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