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