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
Hub(Object * parent,int8 hubAddress,uint8 hubPort,usb_device_descriptor & desc,int8 deviceAddress,usb_speed speed,bool isRootHub,void * controllerCookie)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
~Hub()103 Hub::~Hub()
104 {
105 delete fInterruptPipe;
106 }
107
108
109 status_t
Changed(change_item ** changeList,bool added)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
UpdatePortStatus(uint8 index)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
ResetPort(uint8 index)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
DisablePort(uint8 index)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
Explore(change_item ** changeList)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
InterruptCallback(void * cookie,status_t status,void * data,size_t actualLength)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
GetDescriptor(uint8 descriptorType,uint8 index,uint16 languageID,void * data,size_t dataLength,size_t * actualLength)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
ReportDevice(usb_support_descriptor * supportDescriptors,uint32 supportDescriptorCount,const usb_notify_hooks * hooks,usb_driver_cookie ** cookies,bool added,bool recursive)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
BuildDeviceName(char * string,uint32 * index,size_t bufferSize,Device * device)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
_DebouncePort(uint8 index)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