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