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