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