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