xref: /haiku/src/add-ons/kernel/bus_managers/usb/Hub.cpp (revision e6b30aee0fd7a23d6a6baab9f3718945a0cd838a)
1 /*
2  * Copyright 2003-2006, Haiku Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Michael Lotz <mmlr@mlotz.ch>
7  *		Niels S. Reedijk
8  */
9 
10 #include "usb_p.h"
11 #include <stdio.h>
12 
13 
14 Hub::Hub(Object *parent, int8 hubPort, usb_device_descriptor &desc,
15 	int8 deviceAddress, usb_speed speed)
16 	:	Device(parent, hubPort, desc, deviceAddress, speed),
17 		fInterruptPipe(NULL)
18 {
19 	TRACE(("USB Hub %d: creating hub\n", DeviceAddress()));
20 
21 	memset(&fHubDescriptor, 0, sizeof(fHubDescriptor));
22 	for (int32 i = 0; i < 8; i++)
23 		fChildren[i] = NULL;
24 
25 	if (!fInitOK) {
26 		TRACE_ERROR(("USB Hub %d: device failed to initialize\n", DeviceAddress()));
27 		return;
28 	}
29 
30 	// Set to false again for the hub init.
31 	fInitOK = false;
32 
33 	if (fDeviceDescriptor.device_class != 9) {
34 		TRACE_ERROR(("USB Hub %d: wrong class! bailing out\n", DeviceAddress()));
35 		return;
36 	}
37 
38 	TRACE(("USB Hub %d: Getting hub descriptor...\n", DeviceAddress()));
39 	size_t actualLength;
40 	status_t status = GetDescriptor(USB_DESCRIPTOR_HUB, 0, 0,
41 		(void *)&fHubDescriptor, sizeof(usb_hub_descriptor), &actualLength);
42 
43 	// we need at least 8 bytes
44 	if (status < B_OK || actualLength < 8) {
45 		TRACE_ERROR(("USB Hub %d: Error getting hub descriptor\n", DeviceAddress()));
46 		return;
47 	}
48 
49 	TRACE(("USB Hub %d: hub descriptor (%ld bytes):\n", DeviceAddress(), actualLength));
50 	TRACE(("\tlength:..............%d\n", fHubDescriptor.length));
51 	TRACE(("\tdescriptor_type:.....0x%02x\n", fHubDescriptor.descriptor_type));
52 	TRACE(("\tnum_ports:...........%d\n", fHubDescriptor.num_ports));
53 	TRACE(("\tcharacteristics:.....0x%04x\n", fHubDescriptor.characteristics));
54 	TRACE(("\tpower_on_to_power_g:.%d\n", fHubDescriptor.power_on_to_power_good));
55 	TRACE(("\tdevice_removeable:...0x%02x\n", fHubDescriptor.device_removeable));
56 	TRACE(("\tpower_control_mask:..0x%02x\n", fHubDescriptor.power_control_mask));
57 
58 	if (fHubDescriptor.num_ports > 8) {
59 		TRACE(("USB Hub %d: hub supports more ports than we do (%d vs. 8)\n", DeviceAddress(), fHubDescriptor.num_ports));
60 		fHubDescriptor.num_ports = 8;
61 	}
62 
63 	Object *object = GetStack()->GetObject(Configuration()->interface->active->endpoint[0].handle);
64 	if (!object || (object->Type() & USB_OBJECT_INTERRUPT_PIPE) == 0) {
65 		TRACE_ERROR(("USB Hub %d: no interrupt pipe found\n", DeviceAddress()));
66 		return;
67 	}
68 
69 	fInterruptPipe = (InterruptPipe *)object;
70 	fInterruptPipe->QueueInterrupt(fInterruptStatus, sizeof(fInterruptStatus),
71 		InterruptCallback, this);
72 
73 	// Wait some time before powering up the ports
74 	snooze(USB_DELAY_HUB_POWER_UP);
75 
76 	// Enable port power on all ports
77 	for (int32 i = 0; i < fHubDescriptor.num_ports; i++) {
78 		status = DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
79 			USB_REQUEST_SET_FEATURE, PORT_POWER, i + 1, 0, NULL, 0, NULL);
80 
81 		if (status < B_OK)
82 			TRACE_ERROR(("USB Hub %d: power up failed on port %ld\n", DeviceAddress(), i));
83 	}
84 
85 	// Wait for power to stabilize
86 	snooze(fHubDescriptor.power_on_to_power_good * 2000);
87 
88 	fInitOK = true;
89 	TRACE(("USB Hub %d: initialised ok\n", DeviceAddress()));
90 }
91 
92 
93 Hub::~Hub()
94 {
95 	delete fInterruptPipe;
96 }
97 
98 
99 status_t
100 Hub::Changed(change_item **changeList, bool added)
101 {
102 	status_t result = Device::Changed(changeList, added);
103 	if (added || result < B_OK)
104 		return result;
105 
106 	for (int32 i = 0; i < fHubDescriptor.num_ports; i++) {
107 		if (fChildren[i] == NULL)
108 			continue;
109 
110 		fChildren[i]->Changed(changeList, false);
111 		fChildren[i] = NULL;
112 	}
113 
114 	return B_OK;
115 }
116 
117 
118 status_t
119 Hub::UpdatePortStatus(uint8 index)
120 {
121 	// get the current port status
122 	size_t actualLength = 0;
123 	status_t result = DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_IN,
124 		USB_REQUEST_GET_STATUS, 0, index + 1, 4, (void *)&fPortStatus[index],
125 		4, &actualLength);
126 
127 	if (result < B_OK || actualLength < 4) {
128 		TRACE_ERROR(("USB Hub %d: error updating port status\n", DeviceAddress()));
129 		return B_ERROR;
130 	}
131 
132 	return B_OK;
133 }
134 
135 
136 status_t
137 Hub::ResetPort(uint8 index)
138 {
139 	status_t result = DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
140 		USB_REQUEST_SET_FEATURE, PORT_RESET, index + 1, 0, NULL, 0, NULL);
141 
142 	if (result < B_OK)
143 		return result;
144 
145 	for (int32 i = 0; i < 10; i++) {
146 		snooze(USB_DELAY_PORT_RESET);
147 
148 		result = UpdatePortStatus(index);
149 		if (result < B_OK)
150 			return result;
151 
152 		if (fPortStatus[index].change & C_PORT_RESET) {
153 			// reset is done
154 			break;
155 		}
156 	}
157 
158 	if ((fPortStatus[index].change & C_PORT_RESET) == 0) {
159 		TRACE_ERROR(("USB Hub %d: port %d won't reset\n", DeviceAddress(), index));
160 		return B_ERROR;
161 	}
162 
163 	// clear the reset change
164 	result = DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
165 		USB_REQUEST_CLEAR_FEATURE, C_PORT_RESET, index + 1, 0, NULL, 0, NULL);
166 	if (result < B_OK)
167 		return result;
168 
169 	// wait for reset recovery
170 	snooze(USB_DELAY_PORT_RESET_RECOVERY);
171 	TRACE(("USB Hub %d: port %d was reset successfully\n", DeviceAddress(), index));
172 	return B_OK;
173 }
174 
175 
176 void
177 Hub::Explore(change_item **changeList)
178 {
179 	for (int32 i = 0; i < fHubDescriptor.num_ports; i++) {
180 		status_t result = UpdatePortStatus(i);
181 		if (result < B_OK)
182 			continue;
183 
184 #ifdef TRACE_USB
185 		if (fPortStatus[i].change) {
186 			TRACE(("USB Hub %d: port %ld: status: 0x%04x; change: 0x%04x\n", DeviceAddress(), i, fPortStatus[i].status, fPortStatus[i].change));
187 			TRACE(("USB Hub %d: device at port %ld: 0x%08lx\n", DeviceAddress(), i, fChildren[i]));
188 		}
189 #endif
190 
191 		if (fPortStatus[i].change & PORT_STATUS_CONNECTION) {
192 			// clear status change
193 			DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
194 				USB_REQUEST_CLEAR_FEATURE, C_PORT_CONNECTION, i + 1,
195 				0, NULL, 0, NULL);
196 
197 			if (fPortStatus[i].status & PORT_STATUS_CONNECTION) {
198 				// new device attached!
199 				TRACE(("USB Hub %d: new device connected\n", DeviceAddress()));
200 
201 				// wait some time for the device to power up
202 				snooze(USB_DELAY_DEVICE_POWER_UP);
203 
204 				// reset the port, this will also enable it
205 				result = ResetPort(i);
206 				if (result < B_OK)
207 					continue;
208 
209 				result = UpdatePortStatus(i);
210 				if (result < B_OK)
211 					continue;
212 
213 				if ((fPortStatus[i].status & PORT_STATUS_CONNECTION) == 0) {
214 					// device has vanished after reset, ignore
215 					TRACE(("USB Hub %d: device disappeared on reset\n", DeviceAddress()));
216 					continue;
217 				}
218 
219 				if (fChildren[i]) {
220 					TRACE_ERROR(("USB Hub %d: new device on a port that is already in use\n", DeviceAddress()));
221 					fChildren[i]->Changed(changeList, false);
222 					fChildren[i] = NULL;
223 				}
224 
225 				usb_speed speed = USB_SPEED_FULLSPEED;
226 				if (fPortStatus[i].status & PORT_STATUS_LOW_SPEED)
227 					speed = USB_SPEED_LOWSPEED;
228 				if (fPortStatus[i].status & PORT_STATUS_HIGH_SPEED)
229 					speed = USB_SPEED_HIGHSPEED;
230 
231 				Device *newDevice = GetBusManager()->AllocateDevice(this, i, speed);
232 
233 				if (newDevice) {
234 					newDevice->Changed(changeList, true);
235 					fChildren[i] = newDevice;
236 				} else {
237 					// the device failed to setup correctly, disable the port
238 					// so that the device doesn't get in the way of future
239 					// addressing.
240 					DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
241 						USB_REQUEST_CLEAR_FEATURE, PORT_ENABLE, i + 1,
242 						0, NULL, 0, NULL);
243 				}
244 			} else {
245 				// Device removed...
246 				TRACE(("USB Hub %d: device removed\n", DeviceAddress()));
247 				if (fChildren[i]) {
248 					TRACE(("USB Hub %d: removing device 0x%08lx\n", DeviceAddress(), fChildren[i]));
249 					fChildren[i]->Changed(changeList, false);
250 					fChildren[i] = NULL;
251 				}
252 			}
253 		}
254 
255 		// other port changes we do not really handle, report and clear them
256 		if (fPortStatus[i].change & PORT_STATUS_ENABLE) {
257 			TRACE_ERROR(("USB Hub %d: port %ld %sabled\n", DeviceAddress(), i, (fPortStatus[i].status & PORT_STATUS_ENABLE) ? "en" : "dis"));
258 			DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
259 				USB_REQUEST_CLEAR_FEATURE, C_PORT_ENABLE, i + 1,
260 				0, NULL, 0, NULL);
261 		}
262 
263 		if (fPortStatus[i].change & PORT_STATUS_SUSPEND) {
264 			TRACE_ERROR(("USB Hub %d: port %ld is %ssuspended\n", DeviceAddress(), i, (fPortStatus[i].status & PORT_STATUS_SUSPEND) ? "" : "not "));
265 			DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
266 				USB_REQUEST_CLEAR_FEATURE, C_PORT_SUSPEND, i + 1,
267 				0, NULL, 0, NULL);
268 		}
269 
270 		if (fPortStatus[i].change & PORT_STATUS_OVER_CURRENT) {
271 			TRACE_ERROR(("USB Hub %d: port %ld is %sin an over current state\n", DeviceAddress(), i, (fPortStatus[i].status & PORT_STATUS_OVER_CURRENT) ? "" : "not "));
272 			DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
273 				USB_REQUEST_CLEAR_FEATURE, C_PORT_OVER_CURRENT, i + 1,
274 				0, NULL, 0, NULL);
275 		}
276 
277 		if (fPortStatus[i].change & PORT_RESET) {
278 			TRACE_ERROR(("USB Hub %d: port %ld was reset\n", DeviceAddress(), i));
279 			DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
280 				USB_REQUEST_CLEAR_FEATURE, C_PORT_RESET, i + 1,
281 				0, NULL, 0, NULL);
282 		}
283 	}
284 
285 	// explore down the tree if we have hubs connected
286 	for (int32 i = 0; i < fHubDescriptor.num_ports; i++) {
287 		if (!fChildren[i] || (fChildren[i]->Type() & USB_OBJECT_HUB) == 0)
288 			continue;
289 
290 		((Hub *)fChildren[i])->Explore(changeList);
291 	}
292 }
293 
294 
295 void
296 Hub::InterruptCallback(void *cookie, status_t status, void *data,
297 	size_t actualLength)
298 {
299 	TRACE(("USB Hub %d: interrupt callback!\n", ((Hub *)data)->DeviceAddress()));
300 }
301 
302 
303 status_t
304 Hub::GetDescriptor(uint8 descriptorType, uint8 index, uint16 languageID,
305 	void *data, size_t dataLength, size_t *actualLength)
306 {
307 	return DefaultPipe()->SendRequest(
308 		USB_REQTYPE_DEVICE_IN | USB_REQTYPE_CLASS,			// type
309 		USB_REQUEST_GET_DESCRIPTOR,							// request
310 		(descriptorType << 8) | index,						// value
311 		languageID,											// index
312 		dataLength,											// length
313 		data,												// buffer
314 		dataLength,											// buffer length
315 		actualLength);										// actual length
316 }
317 
318 
319 status_t
320 Hub::ReportDevice(usb_support_descriptor *supportDescriptors,
321 	uint32 supportDescriptorCount, const usb_notify_hooks *hooks,
322 	usb_driver_cookie **cookies, bool added, bool recursive)
323 {
324 	TRACE(("USB Hub %d: reporting hub\n", DeviceAddress()));
325 
326 	// Report ourselfs first
327 	status_t result = Device::ReportDevice(supportDescriptors,
328 		supportDescriptorCount, hooks, cookies, added, recursive);
329 
330 	if (!recursive)
331 		return result;
332 
333 	for (int32 i = 0; i < fHubDescriptor.num_ports; i++) {
334 		if (!fChildren[i])
335 			continue;
336 
337 		if (fChildren[i]->ReportDevice(supportDescriptors,
338 				supportDescriptorCount, hooks, cookies, added, true) == B_OK)
339 			result = B_OK;
340 	}
341 
342 	return result;
343 }
344 
345 
346 status_t
347 Hub::BuildDeviceName(char *string, uint32 *index, size_t bufferSize,
348 	Device *device)
349 {
350 	status_t result = Device::BuildDeviceName(string, index, bufferSize, device);
351 	if (result < B_OK) {
352 		// recursion to parent failed, we're at the root(hub)
353 		int32 managerIndex = GetStack()->IndexOfBusManager(GetBusManager());
354 		*index += snprintf(string + *index, bufferSize - *index, "%ld", managerIndex);
355 	}
356 
357 	if (!device) {
358 		// no device was specified - report the hub
359 		*index += snprintf(string + *index, bufferSize - *index, "/hub");
360 	} else {
361 		// find out where the requested device sitts
362 		for (int32 i = 0; i < fHubDescriptor.num_ports; i++) {
363 			if (fChildren[i] == device) {
364 				*index += snprintf(string + *index, bufferSize - *index, "/%ld", i);
365 				break;
366 			}
367 		}
368 	}
369 
370 	return B_OK;
371 }
372