xref: /haiku/src/add-ons/kernel/bus_managers/usb/Hub.cpp (revision 344ded80d400028c8f561b4b876257b94c12db4a)
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 
11 #include "usb_private.h"
12 
13 #include <stdio.h>
14 
15 #include <algorithm>
16 
17 
18 Hub::Hub(Object *parent, int8 hubAddress, uint8 hubPort,
19 	usb_device_descriptor &desc, int8 deviceAddress, usb_speed speed,
20 	bool isRootHub, void *controllerCookie)
21 	:	Device(parent, hubAddress, hubPort, desc, deviceAddress, speed,
22 			isRootHub, controllerCookie),
23 		fInterruptPipe(NULL)
24 {
25 	TRACE("creating hub\n");
26 
27 	memset(&fHubDescriptor, 0, sizeof(fHubDescriptor));
28 	for (int32 i = 0; i < USB_MAX_PORT_COUNT; i++)
29 		fChildren[i] = NULL;
30 
31 	if (!fInitOK) {
32 		TRACE_ERROR("device failed to initialize\n");
33 		return;
34 	}
35 
36 	// Set to false again for the hub init.
37 	fInitOK = false;
38 
39 	if (fDeviceDescriptor.device_class != 9) {
40 		TRACE_ERROR("wrong class! bailing out\n");
41 		return;
42 	}
43 
44 	TRACE("getting hub descriptor...\n");
45 	size_t actualLength;
46 	status_t status = GetDescriptor(USB_DESCRIPTOR_HUB, 0, 0,
47 		(void *)&fHubDescriptor, sizeof(usb_hub_descriptor), &actualLength);
48 
49 	// we need at least 8 bytes
50 	if (status < B_OK || actualLength < 8) {
51 		TRACE_ERROR("error getting hub descriptor\n");
52 		return;
53 	}
54 
55 	TRACE("hub descriptor (%ld bytes):\n", actualLength);
56 	TRACE("\tlength:..............%d\n", fHubDescriptor.length);
57 	TRACE("\tdescriptor_type:.....0x%02x\n", fHubDescriptor.descriptor_type);
58 	TRACE("\tnum_ports:...........%d\n", fHubDescriptor.num_ports);
59 	TRACE("\tcharacteristics:.....0x%04x\n", fHubDescriptor.characteristics);
60 	TRACE("\tpower_on_to_power_g:.%d\n", fHubDescriptor.power_on_to_power_good);
61 	TRACE("\tdevice_removeable:...0x%02x\n", fHubDescriptor.device_removeable);
62 	TRACE("\tpower_control_mask:..0x%02x\n", fHubDescriptor.power_control_mask);
63 
64 	if (fHubDescriptor.num_ports > USB_MAX_PORT_COUNT) {
65 		TRACE_ALWAYS("hub supports more ports than we do (%d vs. %d)\n",
66 			fHubDescriptor.num_ports, USB_MAX_PORT_COUNT);
67 		fHubDescriptor.num_ports = USB_MAX_PORT_COUNT;
68 	}
69 
70 	usb_interface_list *list = Configuration()->interface;
71 	Object *object = GetStack()->GetObject(list->active->endpoint[0].handle);
72 	if (object != NULL && (object->Type() & USB_OBJECT_INTERRUPT_PIPE) != 0) {
73 		fInterruptPipe = (InterruptPipe *)object;
74 		fInterruptPipe->QueueInterrupt(fInterruptStatus,
75 			sizeof(fInterruptStatus), InterruptCallback, this);
76 	} else {
77 		TRACE_ALWAYS("no interrupt pipe found\n");
78 	}
79 	if (object != NULL)
80 		object->ReleaseReference();
81 
82 	// Wait some time before powering up the ports
83 	if (!isRootHub)
84 		snooze(USB_DELAY_HUB_POWER_UP);
85 
86 	// Enable port power on all ports
87 	for (int32 i = 0; i < fHubDescriptor.num_ports; i++) {
88 		status = DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
89 			USB_REQUEST_SET_FEATURE, PORT_POWER, i + 1, 0, NULL, 0, NULL);
90 
91 		if (status < B_OK)
92 			TRACE_ERROR("power up failed on port %" B_PRId32 "\n", i);
93 	}
94 
95 	// Wait for power to stabilize
96 	snooze(fHubDescriptor.power_on_to_power_good * 2000);
97 
98 	fInitOK = true;
99 	TRACE("initialised ok\n");
100 }
101 
102 
103 Hub::~Hub()
104 {
105 	delete fInterruptPipe;
106 }
107 
108 
109 status_t
110 Hub::Changed(change_item **changeList, bool added)
111 {
112 	status_t result = Device::Changed(changeList, added);
113 	if (added || result < B_OK)
114 		return result;
115 
116 	for (int32 i = 0; i < fHubDescriptor.num_ports; i++) {
117 		if (fChildren[i] == NULL)
118 			continue;
119 
120 		fChildren[i]->Changed(changeList, false);
121 		fChildren[i] = NULL;
122 	}
123 
124 	return B_OK;
125 }
126 
127 
128 status_t
129 Hub::UpdatePortStatus(uint8 index)
130 {
131 	// get the current port status
132 	size_t actualLength = 0;
133 	status_t result = DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_IN,
134 		USB_REQUEST_GET_STATUS, 0, index + 1, sizeof(usb_port_status),
135 		(void *)&fPortStatus[index], sizeof(usb_port_status), &actualLength);
136 
137 	if (result < B_OK || actualLength < sizeof(usb_port_status)) {
138 		TRACE_ERROR("error updating port status\n");
139 		return B_ERROR;
140 	}
141 
142 	return B_OK;
143 }
144 
145 
146 status_t
147 Hub::ResetPort(uint8 index)
148 {
149 	status_t result = DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
150 		USB_REQUEST_SET_FEATURE, PORT_RESET, index + 1, 0, NULL, 0, NULL);
151 
152 	if (result < B_OK)
153 		return result;
154 
155 	for (int32 i = 0; i < 10; i++) {
156 		snooze(USB_DELAY_PORT_RESET);
157 
158 		result = UpdatePortStatus(index);
159 		if (result < B_OK)
160 			return result;
161 
162 		if ((fPortStatus[index].change & PORT_STATUS_RESET) != 0
163 			|| (fPortStatus[index].status & PORT_STATUS_RESET) == 0) {
164 			// reset is done
165 			break;
166 		}
167 	}
168 
169 	if ((fPortStatus[index].change & PORT_STATUS_RESET) == 0
170 			&& (fPortStatus[index].status & PORT_STATUS_RESET) != 0) {
171 		TRACE_ERROR("port %d won't reset (%#x, %#x)\n", index,
172 			fPortStatus[index].change, fPortStatus[index].status);
173 		return B_ERROR;
174 	}
175 
176 	// clear the reset change
177 	result = DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
178 		USB_REQUEST_CLEAR_FEATURE, C_PORT_RESET, index + 1, 0, NULL, 0, NULL);
179 	if (result < B_OK)
180 		return result;
181 
182 	// wait for reset recovery
183 	snooze(USB_DELAY_PORT_RESET_RECOVERY);
184 	TRACE("port %d was reset successfully\n", index);
185 	return B_OK;
186 }
187 
188 
189 status_t
190 Hub::DisablePort(uint8 index)
191 {
192 	return DefaultPipe()->SendRequest(USB_REQTYPE_CLASS
193 		| USB_REQTYPE_OTHER_OUT, USB_REQUEST_CLEAR_FEATURE, PORT_ENABLE,
194 		index + 1, 0, NULL, 0, NULL);
195 }
196 
197 
198 
199 void
200 Hub::Explore(change_item **changeList)
201 {
202 	for (int32 i = 0; i < fHubDescriptor.num_ports; i++) {
203 		status_t result = UpdatePortStatus(i);
204 		if (result < B_OK)
205 			continue;
206 
207 #ifdef TRACE_USB
208 		if (fPortStatus[i].change) {
209 			TRACE("port %" B_PRId32 ": status: 0x%04x; change: 0x%04x\n", i,
210 				fPortStatus[i].status, fPortStatus[i].change);
211 			TRACE("device at port %" B_PRId32 ": %p (%" B_PRId32 ")\n", i,
212 				fChildren[i], fChildren[i] != NULL
213 					? fChildren[i]->USBID() : 0);
214 		}
215 #endif
216 
217 		if ((fPortStatus[i].change & PORT_STATUS_CONNECTION)
218 				|| ((fPortStatus[i].status & PORT_STATUS_CONNECTION)
219 					&& fChildren[i] == NULL)) {
220 			// clear status change
221 			DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
222 				USB_REQUEST_CLEAR_FEATURE, C_PORT_CONNECTION, i + 1,
223 				0, NULL, 0, NULL);
224 
225 			if (fPortStatus[i].status & PORT_STATUS_CONNECTION) {
226 				// new device attached!
227 				TRACE_ALWAYS("port %" B_PRId32 ": new device connected\n", i);
228 
229 				int32 retry = 2;
230 				while (retry--) {
231 					// wait for stable device power
232 					result = _DebouncePort(i);
233 					if (result != B_OK) {
234 						TRACE_ERROR("debouncing port %" B_PRId32
235 							" failed: %s\n", i, strerror(result));
236 						break;
237 					}
238 
239 					// reset the port, this will also enable it
240 					result = ResetPort(i);
241 					if (result < B_OK) {
242 						TRACE_ERROR("resetting port %" B_PRId32 " failed\n",
243 							i);
244 						break;
245 					}
246 
247 					result = UpdatePortStatus(i);
248 					if (result < B_OK)
249 						break;
250 
251 					if ((fPortStatus[i].status & PORT_STATUS_CONNECTION) == 0) {
252 						// device has vanished after reset, ignore
253 						TRACE("device disappeared on reset\n");
254 						break;
255 					}
256 
257 					if (fChildren[i] != NULL) {
258 						TRACE_ERROR("new device on a port that is already in "
259 							"use\n");
260 						fChildren[i]->Changed(changeList, false);
261 						fChildren[i] = NULL;
262 					}
263 
264 					// Determine the device speed.
265 					usb_speed speed;
266 
267 					// PORT_STATUS_LOW_SPEED and PORT_STATUS_SS_POWER are the
268 					// same, but PORT_STATUS_POWER will not be set for SS
269 					// devices, hence this somewhat convoluted logic.
270 					if ((fPortStatus[i].status & PORT_STATUS_POWER) != 0) {
271 						if ((fPortStatus[i].status & PORT_STATUS_HIGH_SPEED) != 0)
272 							speed = USB_SPEED_HIGHSPEED;
273 						else if ((fPortStatus[i].status & PORT_STATUS_LOW_SPEED) != 0)
274 							speed = USB_SPEED_LOWSPEED;
275 						else
276 							speed = USB_SPEED_FULLSPEED;
277 					} else {
278 						// This must be a SuperSpeed device, which will
279 						// simply inherit our speed.
280 						speed = Speed();
281 					}
282 					if (speed > Speed())
283 						speed = Speed();
284 
285 					// either let the device inherit our addresses (if we are
286 					// already potentially using a transaction translator) or
287 					// set ourselves as the hub when we might become the
288 					// transaction translator for the device.
289 					int8 hubAddress = HubAddress();
290 					uint8 hubPort = HubPort();
291 					if (Speed() == USB_SPEED_HIGHSPEED || Speed() >= USB_SPEED_SUPERSPEED) {
292 						hubAddress = DeviceAddress();
293 						hubPort = i + 1;
294 					}
295 
296 					Device *newDevice = GetBusManager()->AllocateDevice(this,
297 						hubAddress, hubPort, speed);
298 
299 					if (newDevice) {
300 						newDevice->Changed(changeList, true);
301 						fChildren[i] = newDevice;
302 						break;
303 					} else {
304 						// the device failed to setup correctly, disable the
305 						// port so that the device doesn't get in the way of
306 						// future addressing.
307 						DisablePort(i);
308 					}
309 				}
310 			} else {
311 				// Device removed...
312 				TRACE_ALWAYS("port %" B_PRId32 ": device removed\n", i);
313 				if (fChildren[i] != NULL) {
314 					TRACE("removing device %p\n", fChildren[i]);
315 					fChildren[i]->Changed(changeList, false);
316 					fChildren[i] = NULL;
317 				}
318 			}
319 		}
320 
321 		// other port changes we do not really handle, report and clear them
322 		if (fPortStatus[i].change & PORT_STATUS_ENABLE) {
323 			TRACE_ALWAYS("port %" B_PRId32 " %sabled\n", i,
324 				(fPortStatus[i].status & PORT_STATUS_ENABLE) ? "en" : "dis");
325 			DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
326 				USB_REQUEST_CLEAR_FEATURE, C_PORT_ENABLE, i + 1,
327 				0, NULL, 0, NULL);
328 		}
329 
330 		if (fPortStatus[i].change & PORT_STATUS_SUSPEND) {
331 			TRACE_ALWAYS("port %" B_PRId32 " is %ssuspended\n", i,
332 				(fPortStatus[i].status & PORT_STATUS_SUSPEND) ? "" : "not ");
333 			DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
334 				USB_REQUEST_CLEAR_FEATURE, C_PORT_SUSPEND, i + 1,
335 				0, NULL, 0, NULL);
336 		}
337 
338 		if (fPortStatus[i].change & PORT_STATUS_OVER_CURRENT) {
339 			TRACE_ALWAYS("port %" B_PRId32 " is %sin an over current state\n",
340 				i, (fPortStatus[i].status & PORT_STATUS_OVER_CURRENT) ? "" : "not ");
341 			DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
342 				USB_REQUEST_CLEAR_FEATURE, C_PORT_OVER_CURRENT, i + 1,
343 				0, NULL, 0, NULL);
344 		}
345 
346 		if (fPortStatus[i].change & PORT_STATUS_RESET) {
347 			TRACE_ALWAYS("port %" B_PRId32 " was reset\n", i);
348 			DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
349 				USB_REQUEST_CLEAR_FEATURE, C_PORT_RESET, i + 1,
350 				0, NULL, 0, NULL);
351 		}
352 
353 		if (fPortStatus[i].change & PORT_CHANGE_LINK_STATE) {
354 			TRACE_ALWAYS("port %" B_PRId32 " link state changed\n", i);
355 			DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
356 				USB_REQUEST_CLEAR_FEATURE, C_PORT_LINK_STATE, i + 1,
357 				0, NULL, 0, NULL);
358 		}
359 
360 		if (fPortStatus[i].change & PORT_CHANGE_BH_PORT_RESET) {
361 			TRACE_ALWAYS("port %" B_PRId32 " was warm reset\n", i);
362 			DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
363 				USB_REQUEST_CLEAR_FEATURE, C_PORT_BH_PORT_RESET, i + 1,
364 				0, NULL, 0, NULL);
365 		}
366 
367 	}
368 
369 	// explore down the tree if we have hubs connected
370 	for (int32 i = 0; i < fHubDescriptor.num_ports; i++) {
371 		if (!fChildren[i] || (fChildren[i]->Type() & USB_OBJECT_HUB) == 0)
372 			continue;
373 
374 		((Hub *)fChildren[i])->Explore(changeList);
375 	}
376 }
377 
378 
379 void
380 Hub::InterruptCallback(void *cookie, status_t status, void *data,
381 	size_t actualLength)
382 {
383 	TRACE_STATIC((Hub *)cookie, "interrupt callback!\n");
384 }
385 
386 
387 status_t
388 Hub::GetDescriptor(uint8 descriptorType, uint8 index, uint16 languageID,
389 	void *data, size_t dataLength, size_t *actualLength)
390 {
391 	return DefaultPipe()->SendRequest(
392 		USB_REQTYPE_DEVICE_IN | USB_REQTYPE_CLASS,			// type
393 		USB_REQUEST_GET_DESCRIPTOR,							// request
394 		(descriptorType << 8) | index,						// value
395 		languageID,											// index
396 		dataLength,											// length
397 		data,												// buffer
398 		dataLength,											// buffer length
399 		actualLength);										// actual length
400 }
401 
402 
403 status_t
404 Hub::ReportDevice(usb_support_descriptor *supportDescriptors,
405 	uint32 supportDescriptorCount, const usb_notify_hooks *hooks,
406 	usb_driver_cookie **cookies, bool added, bool recursive)
407 {
408 	status_t result = B_UNSUPPORTED;
409 
410 	if (added) {
411 		// Report hub before children when adding devices
412 		TRACE("reporting hub before children\n");
413 		result = Device::ReportDevice(supportDescriptors,
414 			supportDescriptorCount, hooks, cookies, added, recursive);
415 	}
416 
417 	for (int32 i = 0; recursive && i < fHubDescriptor.num_ports; i++) {
418 		if (!fChildren[i])
419 			continue;
420 
421 		if (fChildren[i]->ReportDevice(supportDescriptors,
422 				supportDescriptorCount, hooks, cookies, added, true) == B_OK)
423 			result = B_OK;
424 	}
425 
426 	if (!added) {
427 		// Report hub after children when removing devices
428 		TRACE("reporting hub after children\n");
429 		if (Device::ReportDevice(supportDescriptors, supportDescriptorCount,
430 				hooks, cookies, added, recursive) == B_OK)
431 			result = B_OK;
432 	}
433 
434 	return result;
435 }
436 
437 
438 status_t
439 Hub::BuildDeviceName(char *string, uint32 *index, size_t bufferSize,
440 	Device *device)
441 {
442 	status_t result = Device::BuildDeviceName(string, index, bufferSize, device);
443 	if (result < B_OK) {
444 		// recursion to parent failed, we're at the root(hub)
445 		if (*index < bufferSize) {
446 			int32 managerIndex = GetStack()->IndexOfBusManager(GetBusManager());
447 			size_t totalBytes = snprintf(string + *index, bufferSize - *index,
448 				"%" B_PRId32, managerIndex);
449 			*index += std::min(totalBytes, (size_t)(bufferSize - *index - 1));
450 		}
451 	}
452 
453 	if (!device) {
454 		// no device was specified - report the hub
455 		if (*index < bufferSize) {
456 			size_t totalBytes = snprintf(string + *index, bufferSize - *index,
457 				"/hub");
458 			*index += std::min(totalBytes, (size_t)(bufferSize - *index - 1));
459 		}
460 	} else {
461 		// find out where the requested device sitts
462 		for (int32 i = 0; i < fHubDescriptor.num_ports; i++) {
463 			if (fChildren[i] == device) {
464 				if (*index < bufferSize) {
465 					size_t totalBytes = snprintf(string + *index,
466 						bufferSize - *index, "/%" B_PRId32, i);
467 					*index += std::min(totalBytes,
468 						(size_t)(bufferSize - *index - 1));
469 				}
470 				break;
471 			}
472 		}
473 	}
474 
475 	return B_OK;
476 }
477 
478 
479 status_t
480 Hub::_DebouncePort(uint8 index)
481 {
482 	uint32 timeout = 0;
483 	uint32 stableTime = 0;
484 	while (timeout < USB_DEBOUNCE_TIMEOUT) {
485 		snooze(USB_DEBOUNCE_CHECK_INTERVAL);
486 		timeout += USB_DEBOUNCE_CHECK_INTERVAL;
487 
488 		status_t result = UpdatePortStatus(index);
489 		if (result != B_OK)
490 			return result;
491 
492 		if ((fPortStatus[index].change & PORT_STATUS_CONNECTION) == 0) {
493 			stableTime += USB_DEBOUNCE_CHECK_INTERVAL;
494 			if (stableTime >= USB_DEBOUNCE_STABLE_TIME)
495 				return B_OK;
496 			continue;
497 		}
498 
499 		// clear the connection change and reset stable time
500 		result = DefaultPipe()->SendRequest(USB_REQTYPE_CLASS
501 			| USB_REQTYPE_OTHER_OUT, USB_REQUEST_CLEAR_FEATURE,
502 			C_PORT_CONNECTION, index + 1, 0, NULL, 0, NULL);
503 		if (result != B_OK)
504 			return result;
505 
506 		TRACE("got connection change during debounce, resetting stable time\n");
507 		stableTime = 0;
508 	}
509 
510 	return B_TIMED_OUT;
511 }
512