1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 27 /* 28 * Universal Serial BUS Host Controller Driver (UHCI) 29 * 30 * The UHCI driver is a driver which interfaces to the Universal 31 * Serial Bus Architecture (USBA) and the Host Controller (HC). The interface to 32 * the Host Controller is defined by the Universal Host Controller Interface. 33 * This file contains the code for root hub related functions. 34 */ 35 #include <sys/usb/hcd/uhci/uhcid.h> 36 #include <sys/usb/hcd/uhci/uhci.h> 37 #include <sys/usb/hcd/uhci/uhcihub.h> 38 39 /* 40 * Function Prototypes 41 */ 42 static int uhci_handle_set_clear_port_feature( 43 uhci_state_t *uhcip, 44 uchar_t bRequest, 45 uint16_t wValue, 46 usb_port_t port); 47 static void uhci_handle_port_power( 48 uhci_state_t *uhcip, 49 usb_port_t port, 50 uint_t on); 51 static void uhci_handle_port_suspend( 52 uhci_state_t *uhcip, 53 usb_port_t port, 54 uint_t on); 55 static void uhci_handle_port_enable_disable( 56 uhci_state_t *uhcip, 57 usb_port_t port, 58 uint_t on); 59 static void uhci_handle_port_reset( 60 uhci_state_t *uhcip, 61 usb_port_t port); 62 static void uhci_handle_complete_port_reset( 63 uhci_state_t *uhcip, 64 usb_port_t port); 65 static void uhci_handle_clear_port_connection( 66 uhci_state_t *uhcip, 67 usb_port_t port); 68 static void uhci_handle_get_port_status( 69 uhci_state_t *uhcip, 70 usb_ctrl_req_t *req, 71 usb_port_t port); 72 static void uhci_handle_get_hub_descriptor( 73 uhci_state_t *uhcip, 74 usb_ctrl_req_t *req); 75 static void uhci_handle_get_hub_status( 76 uhci_state_t *uhcip, 77 usb_ctrl_req_t *req); 78 static void uhci_handle_get_device_status( 79 uhci_state_t *uhcip, 80 usb_ctrl_req_t *req); 81 static uint_t uhci_get_port_status( 82 uhci_state_t *uhcip, 83 usb_port_t port); 84 static void uhci_rh_hcdi_callback( 85 uhci_state_t *uhcip, 86 usba_pipe_handle_data_t *ph, 87 usb_opaque_t req, 88 usb_cr_t cr); 89 90 /* 91 * root hub device descriptor 92 */ 93 static usb_dev_descr_t uhci_rh_dev_descr = { 94 0x12, /* Length */ 95 1, /* Type */ 96 0x110, /* BCD - v1.1 */ 97 9, /* Class */ 98 0, /* Sub class */ 99 0, /* Protocol */ 100 8, /* Max pkt size */ 101 0, /* Vendor */ 102 0, /* Product id */ 103 0, /* Device release */ 104 0, /* Manufacturer */ 105 0, /* Product */ 106 0, /* Sn */ 107 1 /* No of configs */ 108 }; 109 110 /* 111 * root hub config descriptor 112 */ 113 static uchar_t uhci_rh_config_descr[] = { 114 /* config descriptor */ 115 0x09, /* bLength */ 116 0x02, /* bDescriptorType, Configuration */ 117 0x19, 0x00, /* wTotalLength */ 118 0x01, /* bNumInterfaces */ 119 0x01, /* bConfigurationValue */ 120 0x00, /* iConfiguration */ 121 0x40, /* bmAttributes */ 122 0x00, /* MaxPower */ 123 124 /* interface descriptor */ 125 0x09, /* bLength */ 126 0x04, /* bDescriptorType, Interface */ 127 0x00, /* bInterfaceNumber */ 128 0x00, /* bAlternateSetting */ 129 0x01, /* bNumEndpoints */ 130 0x09, /* bInterfaceClass */ 131 0x01, /* bInterfaceSubClass */ 132 0x00, /* bInterfaceProtocol */ 133 0x00, /* iInterface */ 134 135 /* endpoint descriptor */ 136 0x07, /* bLength */ 137 0x05, /* bDescriptorType, Endpoint */ 138 0x81, /* bEndpointAddress */ 139 0x03, /* bmAttributes */ 140 0x01, 0x00, /* wMaxPacketSize, 1 + (OHCI_MAX_RH_PORTS / 8) */ 141 0x20 /* bInterval */ 142 }; 143 144 145 /* 146 * uhci_init_root_hub: 147 * Initialize the root hub 148 */ 149 int 150 uhci_init_root_hub(uhci_state_t *uhcip) 151 { 152 int i, length; 153 usb_hub_descr_t *root_hub_descr = &uhcip->uhci_root_hub.rh_descr; 154 155 USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl, 156 "uhci_init_root_hub:"); 157 158 uhcip->uhci_root_hub.rh_num_ports = MAX_RH_PORTS; 159 160 /* 161 * Build the hub descriptor 162 */ 163 root_hub_descr->bDescriptorType = ROOT_HUB_DESCRIPTOR_TYPE; 164 root_hub_descr->bNbrPorts = MAX_RH_PORTS; 165 166 length = root_hub_descr->bNbrPorts / 8; 167 if (length) { 168 root_hub_descr->bDescLength = 7 + (2 * (length + 1)); 169 } else { 170 root_hub_descr->bDescLength = ROOT_HUB_DESCRIPTOR_LENGTH; 171 } 172 173 /* Determine the Power Switching Mode */ 174 root_hub_descr->bPwrOn2PwrGood = 10; /* arbitrary number */ 175 root_hub_descr->wHubCharacteristics = 176 HUB_CHARS_NO_POWER_SWITCHING|HUB_CHARS_NO_OVER_CURRENT; 177 178 /* Indicate if the device is removable */ 179 root_hub_descr->DeviceRemovable = 0x0; 180 181 /* Fill in the port power control mask */ 182 root_hub_descr->PortPwrCtrlMask = 0xff; 183 184 for (i = 0; i < uhcip->uhci_root_hub.rh_num_ports; i++) { 185 uhcip->uhci_root_hub.rh_port_state[i] = DISCONNECTED; 186 uhcip->uhci_root_hub.rh_port_status[i] = 0; 187 uhcip->uhci_root_hub.rh_port_changes[i] = 0; 188 } 189 190 /* Finally load the root hub driver */ 191 return (usba_hubdi_bind_root_hub(uhcip->uhci_dip, uhci_rh_config_descr, 192 sizeof (uhci_rh_config_descr), &uhci_rh_dev_descr)); 193 } 194 195 196 /* 197 * uhci_handle_root_hub_request: 198 * Intercept a root hub request. 199 * Handle the root hub request through the registers 200 */ 201 int 202 uhci_handle_root_hub_request( 203 uhci_state_t *uhcip, 204 usba_pipe_handle_data_t *pipe_handle, 205 usb_ctrl_req_t *req) 206 { 207 int error = USB_SUCCESS; 208 uint16_t port = req->ctrl_wIndex - 1; 209 usb_cr_t completion_reason; 210 211 USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl, 212 "uhci_handle_root_hub_request: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%p", 213 req->ctrl_bmRequestType, req->ctrl_bRequest, req->ctrl_wValue, 214 req->ctrl_wIndex, req->ctrl_wLength, (void *)req->ctrl_data); 215 216 ASSERT(mutex_owned(&uhcip->uhci_int_mutex)); 217 218 switch (req->ctrl_bmRequestType) { 219 case HUB_GET_DEVICE_STATUS_TYPE: 220 uhci_handle_get_device_status(uhcip, req); 221 222 break; 223 case HUB_HANDLE_PORT_FEATURE_TYPE: 224 error = uhci_handle_set_clear_port_feature(uhcip, 225 req->ctrl_bRequest, req->ctrl_wValue, port); 226 227 break; 228 case HUB_GET_PORT_STATUS_TYPE: 229 uhci_handle_get_port_status(uhcip, req, port); 230 231 break; 232 case HUB_CLASS_REQ_TYPE: 233 switch (req->ctrl_bRequest) { 234 case USB_REQ_GET_DESCR: 235 uhci_handle_get_hub_descriptor(uhcip, req); 236 237 break; 238 case USB_REQ_GET_STATUS: 239 uhci_handle_get_hub_status(uhcip, req); 240 241 break; 242 default: 243 USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl, 244 "uhci_handle_root_hub_request: Unsupported " 245 "request 0x%x", req->ctrl_bmRequestType); 246 247 break; 248 } 249 250 break; 251 default: 252 USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl, 253 "uhci_handle_root_hub_request: Unsupported request 0x%x", 254 req->ctrl_bmRequestType); 255 256 break; 257 } 258 259 completion_reason = (error != USB_SUCCESS) ? 260 USB_CR_NOT_SUPPORTED : USB_CR_OK; 261 262 USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl, 263 "uhci_handle_root_hub_request: error = %d", error); 264 265 uhci_rh_hcdi_callback(uhcip, pipe_handle, (usb_opaque_t)req, 266 completion_reason); 267 268 return (USB_SUCCESS); 269 } 270 271 272 /* 273 * uhci_handle_set_clear_port_feature: 274 */ 275 static int 276 uhci_handle_set_clear_port_feature( 277 uhci_state_t *uhcip, 278 uchar_t bRequest, 279 uint16_t wValue, 280 usb_port_t port) 281 { 282 int error = USB_SUCCESS; 283 284 USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl, 285 "uhci_handle_set_clear_port_feature: 0x%x 0x%x 0x%x", 286 bRequest, wValue, port); 287 288 switch (bRequest) { 289 case USB_REQ_SET_FEATURE: 290 switch (wValue) { 291 case CFS_PORT_ENABLE: 292 uhci_handle_port_enable_disable(uhcip, 293 port, UHCI_ENABLE_PORT); 294 break; 295 case CFS_PORT_SUSPEND: 296 uhci_handle_port_suspend(uhcip, port, 1); 297 298 break; 299 case CFS_PORT_RESET: 300 uhci_handle_port_reset(uhcip, port); 301 302 break; 303 case CFS_PORT_POWER: 304 uhci_handle_port_power(uhcip, port, 305 UHCI_ENABLE_PORT_PWR); 306 break; 307 308 default: 309 USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl, 310 "uhci_handle_set_clear_port_feature: " 311 "Unsupported request 0x%x 0x%x", bRequest, wValue); 312 error = USB_FAILURE; 313 314 break; 315 } 316 317 break; 318 case USB_REQ_CLEAR_FEATURE: 319 switch (wValue) { 320 case CFS_PORT_ENABLE: 321 uhci_handle_port_enable_disable(uhcip, 322 port, UHCI_DISABLE_PORT); 323 324 break; 325 case CFS_C_PORT_ENABLE: 326 uhci_handle_port_enable_disable(uhcip, 327 port, UHCI_CLEAR_ENDIS_BIT); 328 329 break; 330 case CFS_PORT_SUSPEND: 331 uhci_handle_port_suspend(uhcip, port, 0); 332 333 break; 334 case CFS_C_PORT_RESET: 335 uhci_handle_complete_port_reset(uhcip, port); 336 337 break; 338 case CFS_PORT_POWER: 339 uhci_handle_port_power(uhcip, port, 340 UHCI_DISABLE_PORT_PWR); 341 342 break; 343 case CFS_C_PORT_CONNECTION: 344 uhci_handle_clear_port_connection(uhcip, port); 345 346 break; 347 default: 348 USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl, 349 "uhci_handle_set_clear_port_feature: " 350 "Unsupported request 0x%x 0x%x", bRequest, wValue); 351 error = USB_FAILURE; 352 353 break; 354 } 355 356 break; 357 default: 358 USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl, 359 "uhci_handle_set_clear_port_feature: " 360 "Unsupported request 0x%x 0x%x", bRequest, wValue); 361 error = USB_FAILURE; 362 } 363 364 365 return (error); 366 } 367 368 369 /* 370 * uhci_handle_port_suspend: 371 */ 372 static void 373 uhci_handle_port_suspend( 374 uhci_state_t *uhcip, 375 usb_port_t port, 376 uint_t on) 377 { 378 uint_t port_status = Get_OpReg16(PORTSC[port]); 379 380 USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl, 381 "uhci_handle_port_suspend: port=%d on=%d", 382 port, on); 383 384 if (on) { 385 /* See if the port suspend is already on */ 386 if (!(port_status & HCR_PORT_SUSPEND)) { 387 /* suspend the port */ 388 Set_OpReg16(PORTSC[port], 389 (port_status | HCR_PORT_SUSPEND)); 390 } 391 } else { 392 /* See if the port suspend is already off */ 393 if ((port_status & HCR_PORT_SUSPEND)) { 394 /* resume the port */ 395 Set_OpReg16(PORTSC[port], 396 (port_status & ~HCR_PORT_SUSPEND)); 397 } 398 } 399 } 400 401 402 /* 403 * uhci_handle_port_power: 404 * Turn on a root hub port. NOTE: Driver does not have any control 405 * over the power status. 406 */ 407 /* ARGSUSED */ 408 static void 409 uhci_handle_port_power( 410 uhci_state_t *uhcip, 411 usb_port_t port, 412 uint_t on) 413 { 414 USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl, 415 "uhci_handle_port_power: nothing to do"); 416 } 417 418 419 /* 420 * uhci_handle_port_enable_disable: 421 * Handle port enable request. 422 */ 423 static void 424 uhci_handle_port_enable_disable( 425 uhci_state_t *uhcip, 426 usb_port_t port, 427 uint_t action) 428 { 429 uint_t port_status = Get_OpReg16(PORTSC[port]); 430 431 USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl, 432 "uhci_handle_port_enable: port = 0x%x, status = 0x%x", 433 port, port_status); 434 435 if (action == UHCI_ENABLE_PORT) { 436 /* See if the port enable is already on */ 437 if (!(port_status & HCR_PORT_ENABLE)) { 438 /* Enable the port */ 439 Set_OpReg16(PORTSC[port], 440 (port_status | HCR_PORT_ENABLE)); 441 } 442 } else if (action == UHCI_DISABLE_PORT) { 443 /* See if the port enable is already off */ 444 if ((port_status & HCR_PORT_ENABLE)) { 445 /* Disable the port */ 446 Set_OpReg16(PORTSC[port], 447 (port_status & ~HCR_PORT_ENABLE)); 448 } 449 } else { 450 /* Clear the Enable/Disable change bit */ 451 Set_OpReg16(PORTSC[port], (port_status | HCR_PORT_ENDIS_CHG)); 452 453 /* Update software port_changes register */ 454 uhcip->uhci_root_hub.rh_port_changes[port] &= ~PORT_CHANGE_PESC; 455 } 456 } 457 458 459 /* 460 * uhci_root_hub_reset_occurred: 461 * Inform the upper layer that reset has occured on the port. 462 * This is required because the upper layer is expecting an 463 * event immediately after doing a reset. In case of OHCI 464 * the HC gets an interrupt for the change in the root hub 465 * status, but in case of UHCI we don't. So, we send an 466 * event to the upper layer as soon as we complete the reset 467 * as long as the root hub pipe is polling. 468 */ 469 void 470 uhci_root_hub_reset_occurred( 471 uhci_state_t *uhcip, 472 uint16_t port) 473 { 474 usb_intr_req_t *intr_reqp = uhcip->uhci_root_hub.rh_curr_intr_reqp; 475 476 USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl, 477 "uhci_root_hub_reset_occurred: intr_reqp = 0x%p data = 0x%p", 478 (void *)intr_reqp, (void *)intr_reqp->intr_data); 479 480 *intr_reqp->intr_data->b_wptr++ = (1 << (port+1)); 481 482 uhci_rh_hcdi_callback(uhcip, uhcip->uhci_root_hub.rh_intr_pipe_handle, 483 (usb_opaque_t)intr_reqp, USB_CR_OK); 484 } 485 486 487 /* 488 * uhci_handle_port_reset: 489 * Perform a port reset. 490 */ 491 static void 492 uhci_handle_port_reset( 493 uhci_state_t *uhcip, 494 usb_port_t port) 495 { 496 uint_t port_status = Get_OpReg16(PORTSC[port]); 497 498 USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl, 499 "uhci_handle_port_reset: port = 0x%x, status = 0x%x", 500 port, port_status); 501 502 if (!(port_status & HCR_PORT_CCS)) { 503 USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl, 504 "port_status & HCR_PORT_CCS == 0: " 505 "port = 0x%x, status = 0x%x", port, port_status); 506 } 507 508 Set_OpReg16(PORTSC[port], (port_status| HCR_PORT_RESET)); 509 510 drv_usecwait(UHCI_RESET_DELAY); 511 512 Set_OpReg16(PORTSC[port], (port_status & ~HCR_PORT_RESET)); 513 514 drv_usecwait(UHCI_RESET_DELAY/100); 515 516 Set_OpReg16(PORTSC[port], (port_status| HCR_PORT_ENABLE)); 517 518 /* 519 * The next function is only called if the interrupt pipe 520 * is polling and the USBA is ready to receive the 521 * data. If not, we could panic. 522 */ 523 if (uhcip->uhci_root_hub.rh_pipe_state != UHCI_PIPE_STATE_ACTIVE) { 524 /* make a note that we need to send status back */ 525 uhcip->uhci_root_hub.rh_status = port + 1; 526 } else { 527 uhci_root_hub_reset_occurred(uhcip, port); 528 } 529 } 530 531 532 /* 533 * uhci_handle_complete_port_reset: 534 * Perform a port reset change. 535 */ 536 static void 537 uhci_handle_complete_port_reset( 538 uhci_state_t *uhcip, 539 usb_port_t port) 540 { 541 uint_t port_status = Get_OpReg16(PORTSC[port]); 542 543 USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl, 544 "uhci_handle_complete_port_reset: port = 0x%x status = 0x%x", 545 port, port_status); 546 547 if (!(port_status & HCR_PORT_CCS)) { 548 USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl, 549 "port_status & HCR_PORT_CCS == 0: " 550 "port = 0x%x, status = 0x%x", port, port_status); 551 } 552 553 Set_OpReg16(PORTSC[port], (port_status & (~ HCR_PORT_RESET))); 554 555 /* Update software port_changes register */ 556 uhcip->uhci_root_hub.rh_port_changes[port] &= ~PORT_CHANGE_PRSC; 557 } 558 559 560 /* 561 * uhci_handle_clear_port_connection: 562 * Perform a clear port connection. 563 */ 564 static void 565 uhci_handle_clear_port_connection( 566 uhci_state_t *uhcip, 567 usb_port_t port) 568 { 569 uint_t port_status = Get_OpReg16(PORTSC[port]); 570 571 USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl, 572 "uhci_handle_clear_port_connection: port = 0x%x status = 0x%x", 573 port, port_status); 574 575 /* Clear CSC bit */ 576 Set_OpReg16(PORTSC[port], port_status | HCR_PORT_CSC); 577 578 /* Update software port_changes register */ 579 uhcip->uhci_root_hub.rh_port_changes[port] &= ~PORT_CHANGE_CSC; 580 } 581 582 583 /* 584 * uhci_handle_get_port_status: 585 * Handle a get port status request. 586 */ 587 static void 588 uhci_handle_get_port_status( 589 uhci_state_t *uhcip, 590 usb_ctrl_req_t *req, 591 usb_port_t port) 592 { 593 uint_t new_port_status; 594 uint_t old_port_status = 595 uhcip->uhci_root_hub.rh_port_status[port]; 596 uint_t old_port_changes = 597 uhcip->uhci_root_hub.rh_port_changes[port]; 598 uint_t change_status; 599 usb_ctrl_req_t *ctrl_reqp = (usb_ctrl_req_t *)req; 600 uint16_t wLength = req->ctrl_wLength; 601 602 ASSERT(wLength == 4); 603 ASSERT(ctrl_reqp->ctrl_data != NULL); 604 605 /* Read the current port status and return it */ 606 new_port_status = uhci_get_port_status(uhcip, port); 607 change_status = (old_port_status ^ new_port_status) & 0xff; 608 change_status |= old_port_changes; 609 610 USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl, 611 "uhci_handle_get_port_status:\n\t" 612 "port%d: old status = 0x%x new status = 0x%x change = 0x%x", 613 port, old_port_status, new_port_status, change_status); 614 615 *ctrl_reqp->ctrl_data->b_wptr++ = (uchar_t)new_port_status; 616 *ctrl_reqp->ctrl_data->b_wptr++ = (uchar_t)(new_port_status >> 8); 617 *ctrl_reqp->ctrl_data->b_wptr++ = (uchar_t)change_status; 618 *ctrl_reqp->ctrl_data->b_wptr++ = (uchar_t)(change_status >> 8); 619 620 /* Update the status */ 621 uhcip->uhci_root_hub.rh_port_status[port] = new_port_status; 622 uhcip->uhci_root_hub.rh_port_changes[port] = change_status; 623 } 624 625 626 /* 627 * uhci_handle_get_hub_descriptor: 628 */ 629 static void 630 uhci_handle_get_hub_descriptor( 631 uhci_state_t *uhcip, 632 usb_ctrl_req_t *req) 633 { 634 uchar_t raw_descr[ROOT_HUB_DESCRIPTOR_LENGTH]; 635 usb_hub_descr_t *root_hub_descr = &uhcip->uhci_root_hub.rh_descr; 636 637 USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl, 638 "uhci_handle_get_hub_descriptor: wLength = 0x%x", 639 req->ctrl_wLength); 640 641 ASSERT(req->ctrl_wLength != 0); 642 ASSERT(req->ctrl_data != NULL); 643 644 bzero(&raw_descr, ROOT_HUB_DESCRIPTOR_LENGTH); 645 646 raw_descr[0] = root_hub_descr->bDescLength; 647 raw_descr[1] = root_hub_descr->bDescriptorType; 648 raw_descr[2] = root_hub_descr->bNbrPorts; 649 raw_descr[3] = root_hub_descr->wHubCharacteristics & 0x00ff; 650 raw_descr[4] = (root_hub_descr->wHubCharacteristics & 0xff00) >> 8; 651 raw_descr[5] = root_hub_descr->bPwrOn2PwrGood; 652 raw_descr[6] = root_hub_descr->bHubContrCurrent; 653 raw_descr[7] = root_hub_descr->DeviceRemovable; 654 raw_descr[8] = root_hub_descr->PortPwrCtrlMask; 655 656 bcopy(raw_descr, req->ctrl_data->b_wptr, req->ctrl_wLength); 657 req->ctrl_data->b_wptr += req->ctrl_wLength; 658 } 659 660 661 /* 662 * uhci_handle_get_hub_status: 663 */ 664 static void 665 uhci_handle_get_hub_status( 666 uhci_state_t *uhcip, 667 usb_ctrl_req_t *req) 668 { 669 670 USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl, 671 "uhci_handle_get_hub_status: wLength = 0x%x", 672 req->ctrl_wLength); 673 ASSERT(req->ctrl_wLength != 0); 674 ASSERT(req->ctrl_data != NULL); 675 676 /* 677 * A good status is always sent because there is no way that 678 * the driver can get to know about the status change of the 679 * over current or power failure of the root hub from the HC. 680 */ 681 bzero(req->ctrl_data->b_wptr, req->ctrl_wLength); 682 req->ctrl_data->b_wptr += req->ctrl_wLength; 683 } 684 685 686 /* 687 * uhci_handle_get_device_status: 688 */ 689 static void 690 uhci_handle_get_device_status( 691 uhci_state_t *uhcip, 692 usb_ctrl_req_t *req) 693 { 694 uint16_t dev_status; 695 696 USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl, 697 "uhci_handle_get_device_status: wLength = 0x%x", 698 req->ctrl_wLength); 699 700 ASSERT(req->ctrl_wLength != 0); 701 ASSERT(req->ctrl_data != NULL); 702 703 /* 704 * UHCI doesn't have device status information. 705 * Simply return what is desired for the request. 706 */ 707 dev_status = USB_DEV_SLF_PWRD_STATUS; 708 709 *req->ctrl_data->b_wptr++ = (uchar_t)dev_status; 710 *req->ctrl_data->b_wptr++ = (uchar_t)(dev_status >> 8); 711 } 712 713 714 /* 715 * uhci_handle_root_hub_status_change: 716 * This function is called every 256 ms from the time out handler. 717 * It checks for the status change of the root hub and its ports. 718 */ 719 void 720 uhci_handle_root_hub_status_change(void *arg) 721 { 722 usb_port_t port; 723 uint_t old_port_status; 724 uint_t new_port_status; 725 ushort_t port_status; 726 uint_t change_status; 727 uchar_t all_ports_status = 0; 728 uhci_state_t *uhcip = (uhci_state_t *)arg; 729 usb_intr_req_t *curr_intr_reqp; 730 731 mutex_enter(&uhcip->uhci_int_mutex); 732 733 /* reset the timeout id */ 734 uhcip->uhci_timeout_id = 0; 735 736 /* Get the current interrupt request pointer */ 737 curr_intr_reqp = uhcip->uhci_root_hub.rh_curr_intr_reqp; 738 739 /* Check each port */ 740 for (port = 0; port < uhcip->uhci_root_hub.rh_num_ports; port++) { 741 new_port_status = uhci_get_port_status(uhcip, port); 742 old_port_status = uhcip->uhci_root_hub.rh_port_status[port]; 743 744 change_status = (old_port_status ^ new_port_status) & 0xff; 745 change_status |= uhcip->uhci_root_hub.rh_port_changes[port]; 746 747 /* See if a device was attached/detached */ 748 if (change_status & PORT_STATUS_CCS) { 749 all_ports_status |= 1 << (port + 1); 750 } 751 752 port_status = Get_OpReg16(PORTSC[port]); 753 Set_OpReg16(PORTSC[port], port_status | HCR_PORT_ENDIS_CHG); 754 755 uhcip->uhci_root_hub.rh_port_status[port] = new_port_status; 756 uhcip->uhci_root_hub.rh_port_changes[port] = change_status; 757 758 USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl, 759 "port %d old status 0x%x new status 0x%x change 0x%x\n\t" 760 "all_ports_status = 0x%x", port, old_port_status, 761 new_port_status, change_status, all_ports_status); 762 } 763 764 if (uhcip->uhci_root_hub.rh_intr_pipe_handle && 765 all_ports_status && curr_intr_reqp && 766 (uhcip->uhci_root_hub.rh_pipe_state == UHCI_PIPE_STATE_ACTIVE)) { 767 768 ASSERT(curr_intr_reqp->intr_data != NULL); 769 770 *curr_intr_reqp->intr_data->b_wptr++ = all_ports_status; 771 772 uhci_rh_hcdi_callback(uhcip, 773 uhcip->uhci_root_hub.rh_intr_pipe_handle, 774 (usb_opaque_t)curr_intr_reqp, USB_CR_OK); 775 } 776 777 if (uhcip->uhci_root_hub.rh_pipe_state == UHCI_PIPE_STATE_ACTIVE) { 778 /* 779 * If needed, allocate new interrupt request. Also 780 * start the timer for the root hub interrupt polling. 781 */ 782 if (uhci_root_hub_allocate_intr_pipe_resource(uhcip, 0) != 783 USB_SUCCESS) { 784 785 /* Do interrupt pipe cleanup */ 786 uhci_root_hub_intr_pipe_cleanup(uhcip, 787 USB_CR_NO_RESOURCES); 788 } 789 } 790 791 mutex_exit(&uhcip->uhci_int_mutex); 792 } 793 794 795 static uint_t 796 uhci_get_port_status( 797 uhci_state_t *uhcip, 798 usb_port_t port) 799 { 800 uint_t new_port_status = PORT_STATUS_PPS; 801 ushort_t port_status = Get_OpReg16(PORTSC[port]); 802 803 if (port_status & HCR_PORT_CCS) { 804 new_port_status |= PORT_STATUS_CCS; 805 } 806 807 if (port_status & HCR_PORT_LSDA) { 808 new_port_status |= PORT_STATUS_LSDA; 809 } 810 811 if (port_status & HCR_PORT_ENABLE) { 812 new_port_status |= PORT_STATUS_PES; 813 } 814 815 if (port_status & HCR_PORT_SUSPEND) { 816 new_port_status |= PORT_STATUS_PSS; 817 } 818 819 if (port_status & HCR_PORT_RESET) { 820 new_port_status |= PORT_STATUS_PRS; 821 } 822 823 return (new_port_status); 824 } 825 826 827 /* 828 * uhci_root_hub_allocate_intr_pipe_resource: 829 * Allocate interrupt requests and initialize them. 830 */ 831 int 832 uhci_root_hub_allocate_intr_pipe_resource( 833 uhci_state_t *uhcip, 834 usb_flags_t flags) 835 { 836 usb_intr_req_t *curr_intr_reqp; 837 usba_pipe_handle_data_t *ph; 838 839 USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl, 840 "uhci_root_hub_allocate_intr_pipe_resource:"); 841 842 ASSERT(mutex_owned(&uhcip->uhci_int_mutex)); 843 844 /* Get the interrupt pipe handle */ 845 ph = uhcip->uhci_root_hub.rh_intr_pipe_handle; 846 847 /* Get the current interrupt request pointer */ 848 curr_intr_reqp = uhcip->uhci_root_hub.rh_curr_intr_reqp; 849 850 /* 851 * If current interrupt request pointer is null, 852 * allocate new interrupt request. 853 */ 854 if (curr_intr_reqp == NULL) { 855 ASSERT(uhcip->uhci_root_hub.rh_client_intr_req); 856 857 if ((curr_intr_reqp = usba_hcdi_dup_intr_req(ph->p_dip, 858 uhcip->uhci_root_hub.rh_client_intr_req, 859 uhcip->uhci_root_hub.rh_client_intr_req->intr_len, 860 flags)) == NULL) { 861 USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl, 862 "uhci_root_hub_allocate_intr_pipe_resource:" 863 "Interrupt request structure allocation failed"); 864 865 return (USB_NO_RESOURCES); 866 } 867 868 uhcip->uhci_root_hub.rh_curr_intr_reqp = curr_intr_reqp; 869 870 mutex_enter(&ph->p_mutex); 871 ph->p_req_count++; 872 mutex_exit(&ph->p_mutex); 873 } 874 875 if (uhcip->uhci_timeout_id == 0) { 876 uhcip->uhci_timeout_id = timeout( 877 uhci_handle_root_hub_status_change, 878 (void *)uhcip, UHCI_256_MS); 879 uhcip->uhci_root_hub.rh_pipe_state = 880 UHCI_PIPE_STATE_ACTIVE; 881 } 882 883 return (USB_SUCCESS); 884 } 885 886 887 /* 888 * uhci_root_hub_intr_pipe_cleanup: 889 * Deallocate all interrupt requests and do callback 890 * the original client interrupt request. 891 */ 892 void 893 uhci_root_hub_intr_pipe_cleanup(uhci_state_t *uhcip, usb_cr_t cr) 894 { 895 usb_intr_req_t *curr_intr_reqp; 896 usb_opaque_t client_intr_reqp; 897 usba_pipe_handle_data_t *ph; 898 timeout_id_t timer_id; 899 900 USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl, 901 "uhci_root_hub_intr_pipe_cleanup:"); 902 903 ASSERT(mutex_owned(&uhcip->uhci_int_mutex)); 904 905 /* Get the interrupt pipe handle */ 906 ph = uhcip->uhci_root_hub.rh_intr_pipe_handle; 907 908 /* Get the interrupt timerid */ 909 timer_id = uhcip->uhci_timeout_id; 910 911 /* Stop the root hub interrupt timer */ 912 if (timer_id) { 913 914 /* Reset the timer id to zero */ 915 uhcip->uhci_timeout_id = 0; 916 uhcip->uhci_root_hub.rh_pipe_state = 917 UHCI_PIPE_STATE_IDLE; 918 919 mutex_exit(&uhcip->uhci_int_mutex); 920 (void) untimeout(timer_id); 921 mutex_enter(&uhcip->uhci_int_mutex); 922 } 923 924 /* Reset the current interrupt request pointer */ 925 curr_intr_reqp = uhcip->uhci_root_hub.rh_curr_intr_reqp; 926 927 /* Deallocate uncompleted interrupt request */ 928 if (curr_intr_reqp) { 929 uhcip->uhci_root_hub.rh_curr_intr_reqp = NULL; 930 usb_free_intr_req(curr_intr_reqp); 931 932 mutex_enter(&ph->p_mutex); 933 ph->p_req_count--; 934 mutex_exit(&ph->p_mutex); 935 } 936 937 client_intr_reqp = (usb_opaque_t) 938 uhcip->uhci_root_hub.rh_client_intr_req; 939 940 /* Callback for original client interrupt request */ 941 if (client_intr_reqp) { 942 uhcip->uhci_root_hub.rh_client_intr_req = NULL; 943 uhci_rh_hcdi_callback(uhcip, ph, 944 (usb_opaque_t)client_intr_reqp, cr); 945 } 946 } 947 948 949 /* 950 * uhci_rh_hcdi_callback: 951 * Convenience wrapper around usba_hcdi_cb() for the root hub. 952 */ 953 static void 954 uhci_rh_hcdi_callback( 955 uhci_state_t *uhcip, 956 usba_pipe_handle_data_t *ph, 957 usb_opaque_t req, 958 usb_cr_t cr) 959 { 960 USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl, 961 "uhci_rh_hcdi_callback: ph=0x%p cr=0x%x req=0x%p", 962 (void *)ph, cr, (void *)req); 963 964 ASSERT(mutex_owned(&uhcip->uhci_int_mutex)); 965 966 switch (UHCI_XFER_TYPE(&ph->p_ep)) { 967 case USB_EP_ATTR_CONTROL: 968 969 break; 970 case USB_EP_ATTR_INTR: 971 if ((usb_intr_req_t *)req == 972 uhcip->uhci_root_hub.rh_curr_intr_reqp) { 973 uhcip->uhci_root_hub.rh_curr_intr_reqp = NULL; 974 975 break; 976 } else if ((usb_intr_req_t *)req == 977 uhcip->uhci_root_hub.rh_client_intr_req) { 978 uhcip->uhci_root_hub.rh_client_intr_req = NULL; 979 980 break; 981 } 982 /*FALLTHRU*/ 983 default: 984 ASSERT(req); 985 break; 986 } 987 988 mutex_exit(&uhcip->uhci_int_mutex); 989 usba_hcdi_cb(ph, req, cr); 990 mutex_enter(&uhcip->uhci_int_mutex); 991 } 992