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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 27 /* 28 * USBA: Solaris USB Architecture support for the hub 29 * including root hub 30 * Most of the code for hubd resides in this file and 31 * is shared between the HCD root hub support and hubd 32 */ 33 #define USBA_FRAMEWORK 34 #include <sys/usb/usba.h> 35 #include <sys/usb/usba/usba_devdb.h> 36 #include <sys/sunndi.h> 37 #include <sys/usb/usba/usba_impl.h> 38 #include <sys/usb/usba/usba_types.h> 39 #include <sys/usb/usba/hubdi.h> 40 #include <sys/usb/usba/hcdi_impl.h> 41 #include <sys/usb/hubd/hub.h> 42 #include <sys/usb/hubd/hubdvar.h> 43 #include <sys/usb/hubd/hubd_impl.h> 44 #include <sys/kobj.h> 45 #include <sys/kobj_lex.h> 46 #include <sys/fs/dv_node.h> 47 #include <sys/strsun.h> 48 49 /* 50 * External functions 51 */ 52 extern boolean_t consconfig_console_is_ready(void); 53 54 /* 55 * Prototypes for static functions 56 */ 57 static int usba_hubdi_bus_ctl( 58 dev_info_t *dip, 59 dev_info_t *rdip, 60 ddi_ctl_enum_t op, 61 void *arg, 62 void *result); 63 64 static int usba_hubdi_map_fault( 65 dev_info_t *dip, 66 dev_info_t *rdip, 67 struct hat *hat, 68 struct seg *seg, 69 caddr_t addr, 70 struct devpage *dp, 71 pfn_t pfn, 72 uint_t prot, 73 uint_t lock); 74 75 static int hubd_busop_get_eventcookie(dev_info_t *dip, 76 dev_info_t *rdip, 77 char *eventname, 78 ddi_eventcookie_t *cookie); 79 static int hubd_busop_add_eventcall(dev_info_t *dip, 80 dev_info_t *rdip, 81 ddi_eventcookie_t cookie, 82 void (*callback)(dev_info_t *dip, 83 ddi_eventcookie_t cookie, void *arg, 84 void *bus_impldata), 85 void *arg, ddi_callback_id_t *cb_id); 86 static int hubd_busop_remove_eventcall(dev_info_t *dip, 87 ddi_callback_id_t cb_id); 88 static int hubd_bus_config(dev_info_t *dip, 89 uint_t flag, 90 ddi_bus_config_op_t op, 91 void *arg, 92 dev_info_t **child); 93 static int hubd_bus_unconfig(dev_info_t *dip, 94 uint_t flag, 95 ddi_bus_config_op_t op, 96 void *arg); 97 static int hubd_bus_power(dev_info_t *dip, void *impl_arg, 98 pm_bus_power_op_t op, void *arg, void *result); 99 100 static usb_port_t hubd_get_port_num(hubd_t *, struct devctl_iocdata *); 101 static dev_info_t *hubd_get_child_dip(hubd_t *, usb_port_t); 102 static uint_t hubd_cfgadm_state(hubd_t *, usb_port_t); 103 static int hubd_toggle_port(hubd_t *, usb_port_t); 104 static void hubd_register_cpr_callback(hubd_t *); 105 static void hubd_unregister_cpr_callback(hubd_t *); 106 107 /* 108 * Busops vector for USB HUB's 109 */ 110 struct bus_ops usba_hubdi_busops = { 111 BUSO_REV, 112 nullbusmap, /* bus_map */ 113 NULL, /* bus_get_intrspec */ 114 NULL, /* bus_add_intrspec */ 115 NULL, /* bus_remove_intrspec */ 116 usba_hubdi_map_fault, /* bus_map_fault */ 117 ddi_dma_map, /* bus_dma_map */ 118 ddi_dma_allochdl, 119 ddi_dma_freehdl, 120 ddi_dma_bindhdl, 121 ddi_dma_unbindhdl, 122 ddi_dma_flush, 123 ddi_dma_win, 124 ddi_dma_mctl, /* bus_dma_ctl */ 125 usba_hubdi_bus_ctl, /* bus_ctl */ 126 ddi_bus_prop_op, /* bus_prop_op */ 127 hubd_busop_get_eventcookie, 128 hubd_busop_add_eventcall, 129 hubd_busop_remove_eventcall, 130 NULL, /* bus_post_event */ 131 NULL, /* bus_intr_ctl */ 132 hubd_bus_config, /* bus_config */ 133 hubd_bus_unconfig, /* bus_unconfig */ 134 NULL, /* bus_fm_init */ 135 NULL, /* bus_fm_fini */ 136 NULL, /* bus_fm_access_enter */ 137 NULL, /* bus_fm_access_exit */ 138 hubd_bus_power /* bus_power */ 139 }; 140 141 142 /* 143 * local variables 144 */ 145 static kmutex_t usba_hubdi_mutex; /* protects USBA HUB data structures */ 146 147 static usba_list_entry_t usba_hubdi_list; 148 149 usb_log_handle_t hubdi_log_handle; 150 uint_t hubdi_errlevel = USB_LOG_L4; 151 uint_t hubdi_errmask = (uint_t)-1; 152 uint8_t hubdi_min_pm_threshold = 5; /* seconds */ 153 uint8_t hubdi_reset_delay = 20; /* seconds */ 154 155 /* 156 * initialize private data 157 */ 158 void 159 usba_hubdi_initialization() 160 { 161 hubdi_log_handle = usb_alloc_log_hdl(NULL, "hubdi", &hubdi_errlevel, 162 &hubdi_errmask, NULL, 0); 163 164 USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle, 165 "usba_hubdi_initialization"); 166 167 mutex_init(&usba_hubdi_mutex, NULL, MUTEX_DRIVER, NULL); 168 169 usba_init_list(&usba_hubdi_list, NULL, NULL); 170 } 171 172 173 void 174 usba_hubdi_destroy() 175 { 176 USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle, 177 "usba_hubdi_destroy"); 178 179 mutex_destroy(&usba_hubdi_mutex); 180 usba_destroy_list(&usba_hubdi_list); 181 182 usb_free_log_hdl(hubdi_log_handle); 183 } 184 185 186 /* 187 * Called by an HUB to attach an instance of the driver 188 * make this instance known to USBA 189 * the HUB should initialize usba_hubdi structure prior 190 * to calling this interface 191 */ 192 int 193 usba_hubdi_register(dev_info_t *dip, 194 uint_t flags) 195 { 196 usba_hubdi_t *hubdi = kmem_zalloc(sizeof (usba_hubdi_t), KM_SLEEP); 197 usba_device_t *usba_device = usba_get_usba_device(dip); 198 199 USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle, 200 "usba_hubdi_register: %s", ddi_node_name(dip)); 201 202 hubdi->hubdi_dip = dip; 203 hubdi->hubdi_flags = flags; 204 205 usba_device->usb_hubdi = hubdi; 206 207 /* 208 * add this hubdi instance to the list of known hubdi's 209 */ 210 usba_init_list(&hubdi->hubdi_list, (usb_opaque_t)hubdi, 211 usba_hcdi_get_hcdi(usba_device->usb_root_hub_dip)-> 212 hcdi_iblock_cookie); 213 mutex_enter(&usba_hubdi_mutex); 214 usba_add_to_list(&usba_hubdi_list, &hubdi->hubdi_list); 215 mutex_exit(&usba_hubdi_mutex); 216 217 return (DDI_SUCCESS); 218 } 219 220 221 /* 222 * Called by an HUB to detach an instance of the driver 223 */ 224 int 225 usba_hubdi_unregister(dev_info_t *dip) 226 { 227 usba_device_t *usba_device = usba_get_usba_device(dip); 228 usba_hubdi_t *hubdi = usba_device->usb_hubdi; 229 230 USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle, 231 "usba_hubdi_unregister: %s", ddi_node_name(dip)); 232 233 mutex_enter(&usba_hubdi_mutex); 234 (void) usba_rm_from_list(&usba_hubdi_list, &hubdi->hubdi_list); 235 mutex_exit(&usba_hubdi_mutex); 236 237 usba_destroy_list(&hubdi->hubdi_list); 238 239 kmem_free(hubdi, sizeof (usba_hubdi_t)); 240 241 return (DDI_SUCCESS); 242 } 243 244 245 /* 246 * misc bus routines currently not used 247 */ 248 /*ARGSUSED*/ 249 static int 250 usba_hubdi_map_fault(dev_info_t *dip, 251 dev_info_t *rdip, 252 struct hat *hat, 253 struct seg *seg, 254 caddr_t addr, 255 struct devpage *dp, 256 pfn_t pfn, 257 uint_t prot, 258 uint_t lock) 259 { 260 return (DDI_FAILURE); 261 } 262 263 264 /* 265 * root hub support. the root hub uses the same devi as the HCD 266 */ 267 int 268 usba_hubdi_bind_root_hub(dev_info_t *dip, 269 uchar_t *root_hub_config_descriptor, 270 size_t config_length, 271 usb_dev_descr_t *root_hub_device_descriptor) 272 { 273 usba_device_t *usba_device; 274 usba_hcdi_t *hcdi = usba_hcdi_get_hcdi(dip); 275 hubd_t *root_hubd; 276 usb_pipe_handle_t ph = NULL; 277 dev_info_t *child = ddi_get_child(dip); 278 279 if (ndi_prop_create_boolean(DDI_DEV_T_NONE, dip, 280 "root-hub") != NDI_SUCCESS) { 281 282 return (USB_FAILURE); 283 } 284 285 usba_add_root_hub(dip); 286 287 root_hubd = kmem_zalloc(sizeof (hubd_t), KM_SLEEP); 288 289 /* 290 * create and initialize a usba_device structure 291 */ 292 usba_device = usba_alloc_usba_device(dip); 293 294 mutex_enter(&usba_device->usb_mutex); 295 usba_device->usb_hcdi_ops = hcdi->hcdi_ops; 296 usba_device->usb_cfg = root_hub_config_descriptor; 297 usba_device->usb_cfg_length = config_length; 298 usba_device->usb_dev_descr = root_hub_device_descriptor; 299 usba_device->usb_port = 1; 300 usba_device->usb_addr = ROOT_HUB_ADDR; 301 usba_device->usb_root_hubd = root_hubd; 302 usba_device->usb_cfg_array = kmem_zalloc(sizeof (uchar_t *), 303 KM_SLEEP); 304 usba_device->usb_cfg_array_length = sizeof (uchar_t *); 305 306 usba_device->usb_cfg_array_len = kmem_zalloc(sizeof (uint16_t), 307 KM_SLEEP); 308 usba_device->usb_cfg_array_len_length = sizeof (uint16_t); 309 310 usba_device->usb_cfg_array[0] = root_hub_config_descriptor; 311 usba_device->usb_cfg_array_len[0] = 312 sizeof (root_hub_config_descriptor); 313 314 usba_device->usb_cfg_str_descr = kmem_zalloc(sizeof (uchar_t *), 315 KM_SLEEP); 316 usba_device->usb_n_cfgs = 1; 317 usba_device->usb_n_ifs = 1; 318 usba_device->usb_dip = dip; 319 320 usba_device->usb_client_flags = kmem_zalloc( 321 usba_device->usb_n_ifs * USBA_CLIENT_FLAG_SIZE, KM_SLEEP); 322 323 usba_device->usb_client_attach_list = kmem_zalloc( 324 usba_device->usb_n_ifs * 325 sizeof (*usba_device->usb_client_attach_list), KM_SLEEP); 326 327 usba_device->usb_client_ev_cb_list = kmem_zalloc( 328 usba_device->usb_n_ifs * 329 sizeof (*usba_device->usb_client_ev_cb_list), KM_SLEEP); 330 331 /* 332 * The bDeviceProtocol field of root hub device specifies, 333 * whether root hub is a High or Full speed usb device. 334 */ 335 if (root_hub_device_descriptor->bDeviceProtocol) { 336 usba_device->usb_port_status = USBA_HIGH_SPEED_DEV; 337 } else { 338 usba_device->usb_port_status = USBA_FULL_SPEED_DEV; 339 } 340 341 mutex_exit(&usba_device->usb_mutex); 342 343 usba_set_usba_device(dip, usba_device); 344 345 /* 346 * For the root hub the default pipe is not yet open 347 */ 348 if (usb_pipe_open(dip, NULL, NULL, 349 USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph) != USB_SUCCESS) { 350 goto fail; 351 } 352 353 /* 354 * kill off all OBP children, they may not be fully 355 * enumerated 356 */ 357 while (child) { 358 dev_info_t *next = ddi_get_next_sibling(child); 359 (void) ddi_remove_child(child, 0); 360 child = next; 361 } 362 363 /* 364 * "attach" the root hub driver 365 */ 366 if (usba_hubdi_attach(dip, DDI_ATTACH) != DDI_SUCCESS) { 367 goto fail; 368 } 369 370 return (USB_SUCCESS); 371 372 fail: 373 (void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "root-hub"); 374 375 usba_rem_root_hub(dip); 376 377 if (ph) { 378 usb_pipe_close(dip, ph, 379 USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL); 380 } 381 382 kmem_free(usba_device->usb_cfg_array, 383 usba_device->usb_cfg_array_length); 384 kmem_free(usba_device->usb_cfg_array_len, 385 usba_device->usb_cfg_array_len_length); 386 387 kmem_free(usba_device->usb_cfg_str_descr, sizeof (uchar_t *)); 388 389 usba_free_usba_device(usba_device); 390 391 usba_set_usba_device(dip, NULL); 392 if (root_hubd) { 393 kmem_free(root_hubd, sizeof (hubd_t)); 394 } 395 396 return (USB_FAILURE); 397 } 398 399 400 int 401 usba_hubdi_unbind_root_hub(dev_info_t *dip) 402 { 403 usba_device_t *usba_device; 404 405 /* was root hub attached? */ 406 if (!(usba_is_root_hub(dip))) { 407 408 /* return success anyway */ 409 return (USB_SUCCESS); 410 } 411 412 /* 413 * usba_hubdi_detach also closes the default pipe 414 * and removes properties so there is no need to 415 * do it here 416 */ 417 if (usba_hubdi_detach(dip, DDI_DETACH) != DDI_SUCCESS) { 418 419 if (DEVI_IS_ATTACHING(dip)) { 420 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle, 421 "failure to unbind root hub after attach failure"); 422 } 423 424 return (USB_FAILURE); 425 } 426 427 usba_device = usba_get_usba_device(dip); 428 429 kmem_free(usba_device->usb_root_hubd, sizeof (hubd_t)); 430 431 kmem_free(usba_device->usb_cfg_array, 432 usba_device->usb_cfg_array_length); 433 kmem_free(usba_device->usb_cfg_array_len, 434 usba_device->usb_cfg_array_len_length); 435 436 kmem_free(usba_device->usb_cfg_str_descr, sizeof (uchar_t *)); 437 438 usba_free_usba_device(usba_device); 439 440 usba_rem_root_hub(dip); 441 442 (void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "root-hub"); 443 444 return (USB_SUCCESS); 445 } 446 447 448 /* 449 * Actual Hub Driver support code: 450 * shared by root hub and non-root hubs 451 */ 452 #include <sys/usb/usba/usbai_version.h> 453 454 /* Debugging support */ 455 uint_t hubd_errlevel = USB_LOG_L4; 456 uint_t hubd_errmask = (uint_t)DPRINT_MASK_ALL; 457 uint_t hubd_instance_debug = (uint_t)-1; 458 static uint_t hubdi_bus_config_debug = 0; 459 460 _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_errlevel)) 461 _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_errmask)) 462 _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_instance_debug)) 463 464 _NOTE(SCHEME_PROTECTS_DATA("unique", msgb)) 465 _NOTE(SCHEME_PROTECTS_DATA("unique", dev_info)) 466 467 468 /* 469 * local variables: 470 * 471 * Amount of time to wait between resetting the port and accessing 472 * the device. The value is in microseconds. 473 */ 474 static uint_t hubd_device_delay = 1000000; 475 476 /* 477 * enumeration retry 478 */ 479 #define HUBD_PORT_RETRY 5 480 static uint_t hubd_retry_enumerate = HUBD_PORT_RETRY; 481 482 /* 483 * Stale hotremoved device cleanup delay 484 */ 485 #define HUBD_STALE_DIP_CLEANUP_DELAY 5000000 486 static uint_t hubd_dip_cleanup_delay = HUBD_STALE_DIP_CLEANUP_DELAY; 487 488 /* 489 * retries for USB suspend and resume 490 */ 491 #define HUBD_SUS_RES_RETRY 2 492 493 void *hubd_statep; 494 495 /* 496 * prototypes 497 */ 498 static int hubd_cleanup(dev_info_t *dip, hubd_t *hubd); 499 static int hubd_check_ports(hubd_t *hubd); 500 501 static int hubd_open_intr_pipe(hubd_t *hubd); 502 static void hubd_start_polling(hubd_t *hubd, int always); 503 static void hubd_stop_polling(hubd_t *hubd); 504 static void hubd_close_intr_pipe(hubd_t *hubd); 505 506 static void hubd_read_cb(usb_pipe_handle_t pipe, usb_intr_req_t *req); 507 static void hubd_exception_cb(usb_pipe_handle_t pipe, 508 usb_intr_req_t *req); 509 static void hubd_hotplug_thread(void *arg); 510 static void hubd_reset_thread(void *arg); 511 static int hubd_create_child(dev_info_t *dip, 512 hubd_t *hubd, 513 usba_device_t *usba_device, 514 usb_port_status_t port_status, 515 usb_port_t port, 516 int iteration); 517 518 static int hubd_delete_child(hubd_t *hubd, usb_port_t port, uint_t flag, 519 boolean_t retry); 520 521 static int hubd_get_hub_descriptor(hubd_t *hubd); 522 523 static int hubd_get_hub_status_words(hubd_t *hubd, uint16_t *status); 524 525 static int hubd_reset_port(hubd_t *hubd, usb_port_t port); 526 527 static int hubd_get_hub_status(hubd_t *hubd); 528 529 static int hubd_handle_port_connect(hubd_t *hubd, usb_port_t port); 530 531 static int hubd_disable_port(hubd_t *hubd, usb_port_t port); 532 533 static int hubd_enable_port(hubd_t *hubd, usb_port_t port); 534 static int hubd_recover_disabled_port(hubd_t *hubd, usb_port_t port); 535 536 static int hubd_determine_port_status(hubd_t *hubd, usb_port_t port, 537 uint16_t *status, uint16_t *change, uint_t ack_flag); 538 539 static int hubd_enable_all_port_power(hubd_t *hubd); 540 static int hubd_disable_all_port_power(hubd_t *hubd); 541 static int hubd_disable_port_power(hubd_t *hubd, usb_port_t port); 542 static int hubd_enable_port_power(hubd_t *hubd, usb_port_t port); 543 544 static void hubd_free_usba_device(hubd_t *hubd, usba_device_t *usba_device); 545 546 static int hubd_can_suspend(hubd_t *hubd); 547 static void hubd_restore_device_state(dev_info_t *dip, hubd_t *hubd); 548 static int hubd_setdevaddr(hubd_t *hubd, usb_port_t port); 549 static void hubd_setdevconfig(hubd_t *hubd, usb_port_t port); 550 551 static int hubd_register_events(hubd_t *hubd); 552 static void hubd_do_callback(hubd_t *hubd, dev_info_t *dip, 553 ddi_eventcookie_t cookie); 554 static void hubd_run_callbacks(hubd_t *hubd, usba_event_t type); 555 static void hubd_post_event(hubd_t *hubd, usb_port_t port, usba_event_t type); 556 static void hubd_create_pm_components(dev_info_t *dip, hubd_t *hubd); 557 558 static int hubd_disconnect_event_cb(dev_info_t *dip); 559 static int hubd_reconnect_event_cb(dev_info_t *dip); 560 static int hubd_pre_suspend_event_cb(dev_info_t *dip); 561 static int hubd_post_resume_event_cb(dev_info_t *dip); 562 static int hubd_cpr_suspend(hubd_t *hubd); 563 static void hubd_cpr_resume(dev_info_t *dip); 564 static int hubd_restore_state_cb(dev_info_t *dip); 565 static int hubd_check_same_device(hubd_t *hubd, usb_port_t port); 566 567 static int hubd_init_power_budget(hubd_t *hubd); 568 569 static ndi_event_definition_t hubd_ndi_event_defs[] = { 570 {USBA_EVENT_TAG_HOT_REMOVAL, DDI_DEVI_REMOVE_EVENT, EPL_KERNEL, 571 NDI_EVENT_POST_TO_ALL}, 572 {USBA_EVENT_TAG_HOT_INSERTION, DDI_DEVI_INSERT_EVENT, EPL_KERNEL, 573 NDI_EVENT_POST_TO_ALL}, 574 {USBA_EVENT_TAG_POST_RESUME, USBA_POST_RESUME_EVENT, EPL_KERNEL, 575 NDI_EVENT_POST_TO_ALL}, 576 {USBA_EVENT_TAG_PRE_SUSPEND, USBA_PRE_SUSPEND_EVENT, EPL_KERNEL, 577 NDI_EVENT_POST_TO_ALL} 578 }; 579 580 #define HUBD_N_NDI_EVENTS \ 581 (sizeof (hubd_ndi_event_defs) / sizeof (ndi_event_definition_t)) 582 583 static ndi_event_set_t hubd_ndi_events = { 584 NDI_EVENTS_REV1, HUBD_N_NDI_EVENTS, hubd_ndi_event_defs}; 585 586 /* events received from parent */ 587 static usb_event_t hubd_events = { 588 hubd_disconnect_event_cb, 589 hubd_reconnect_event_cb, 590 hubd_pre_suspend_event_cb, 591 hubd_post_resume_event_cb 592 }; 593 594 595 /* 596 * hubd_get_soft_state() returns the hubd soft state 597 * 598 * WUSB support extends this function to support wire adapter class 599 * devices. The hubd soft state for the wire adapter class device 600 * would be stored in usb_root_hubd field of the usba_device structure, 601 * just as the USB host controller drivers do. 602 */ 603 hubd_t * 604 hubd_get_soft_state(dev_info_t *dip) 605 { 606 if (dip == NULL) { 607 608 return (NULL); 609 } 610 611 if (usba_is_root_hub(dip) || usba_is_wa(dip)) { 612 usba_device_t *usba_device = usba_get_usba_device(dip); 613 614 return (usba_device->usb_root_hubd); 615 } else { 616 int instance = ddi_get_instance(dip); 617 618 return (ddi_get_soft_state(hubd_statep, instance)); 619 } 620 } 621 622 623 /* 624 * PM support functions: 625 */ 626 /*ARGSUSED*/ 627 static void 628 hubd_pm_busy_component(hubd_t *hubd, dev_info_t *dip, int component) 629 { 630 if (hubd->h_hubpm != NULL) { 631 hubd->h_hubpm->hubp_busy_pm++; 632 mutex_exit(HUBD_MUTEX(hubd)); 633 if (pm_busy_component(dip, 0) != DDI_SUCCESS) { 634 mutex_enter(HUBD_MUTEX(hubd)); 635 hubd->h_hubpm->hubp_busy_pm--; 636 mutex_exit(HUBD_MUTEX(hubd)); 637 } 638 mutex_enter(HUBD_MUTEX(hubd)); 639 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 640 "hubd_pm_busy_component: %d", hubd->h_hubpm->hubp_busy_pm); 641 } 642 } 643 644 645 /*ARGSUSED*/ 646 static void 647 hubd_pm_idle_component(hubd_t *hubd, dev_info_t *dip, int component) 648 { 649 if (hubd->h_hubpm != NULL) { 650 mutex_exit(HUBD_MUTEX(hubd)); 651 if (pm_idle_component(dip, 0) == DDI_SUCCESS) { 652 mutex_enter(HUBD_MUTEX(hubd)); 653 ASSERT(hubd->h_hubpm->hubp_busy_pm > 0); 654 hubd->h_hubpm->hubp_busy_pm--; 655 mutex_exit(HUBD_MUTEX(hubd)); 656 } 657 mutex_enter(HUBD_MUTEX(hubd)); 658 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 659 "hubd_pm_idle_component: %d", hubd->h_hubpm->hubp_busy_pm); 660 } 661 } 662 663 664 /* 665 * track power level changes for children of this instance 666 */ 667 static void 668 hubd_set_child_pwrlvl(hubd_t *hubd, usb_port_t port, uint8_t power) 669 { 670 int old_power, new_power, pwr; 671 usb_port_t portno; 672 hub_power_t *hubpm; 673 674 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 675 "hubd_set_child_pwrlvl: port=%d power=%d", 676 port, power); 677 678 mutex_enter(HUBD_MUTEX(hubd)); 679 hubpm = hubd->h_hubpm; 680 681 old_power = 0; 682 for (portno = 1; portno <= hubd->h_hub_descr.bNbrPorts; portno++) { 683 old_power += hubpm->hubp_child_pwrstate[portno]; 684 } 685 686 /* assign the port power */ 687 pwr = hubd->h_hubpm->hubp_child_pwrstate[port]; 688 hubd->h_hubpm->hubp_child_pwrstate[port] = power; 689 new_power = old_power - pwr + power; 690 691 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 692 "hubd_set_child_pwrlvl: new_power=%d old_power=%d", 693 new_power, old_power); 694 695 if ((new_power > 0) && (old_power == 0)) { 696 /* we have the first child coming out of low power */ 697 (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0); 698 } else if ((new_power == 0) && (old_power > 0)) { 699 /* we have the last child going to low power */ 700 (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0); 701 } 702 mutex_exit(HUBD_MUTEX(hubd)); 703 } 704 705 706 /* 707 * given a child dip, locate its port number 708 */ 709 static usb_port_t 710 hubd_child_dip2port(hubd_t *hubd, dev_info_t *dip) 711 { 712 usb_port_t port; 713 714 mutex_enter(HUBD_MUTEX(hubd)); 715 for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { 716 if (hubd->h_children_dips[port] == dip) { 717 718 break; 719 } 720 } 721 ASSERT(port <= hubd->h_hub_descr.bNbrPorts); 722 mutex_exit(HUBD_MUTEX(hubd)); 723 724 return (port); 725 } 726 727 728 /* 729 * if the hub can be put into low power mode, return success 730 * NOTE: suspend here means going to lower power, not CPR suspend. 731 */ 732 static int 733 hubd_can_suspend(hubd_t *hubd) 734 { 735 hub_power_t *hubpm; 736 int total_power = 0; 737 usb_port_t port; 738 739 hubpm = hubd->h_hubpm; 740 741 if (DEVI_IS_DETACHING(hubd->h_dip)) { 742 743 return (USB_SUCCESS); 744 } 745 746 /* 747 * Don't go to lower power if haven't been at full power for enough 748 * time to let hotplug thread kickoff. 749 */ 750 if (ddi_get_time() < (hubpm->hubp_time_at_full_power + 751 hubpm->hubp_min_pm_threshold)) { 752 753 return (USB_FAILURE); 754 } 755 756 for (port = 1; (total_power == 0) && 757 (port <= hubd->h_hub_descr.bNbrPorts); port++) { 758 total_power += hubpm->hubp_child_pwrstate[port]; 759 } 760 761 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 762 "hubd_can_suspend: %d", total_power); 763 764 return (total_power ? USB_FAILURE : USB_SUCCESS); 765 } 766 767 768 /* 769 * resume port depending on current device state 770 */ 771 static int 772 hubd_resume_port(hubd_t *hubd, usb_port_t port) 773 { 774 int rval, retry; 775 usb_cr_t completion_reason; 776 usb_cb_flags_t cb_flags; 777 uint16_t status; 778 uint16_t change; 779 int retval = USB_FAILURE; 780 781 mutex_enter(HUBD_MUTEX(hubd)); 782 783 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 784 "hubd_resume_port: port=%d state=0x%x (%s)", port, 785 hubd->h_dev_state, usb_str_dev_state(hubd->h_dev_state)); 786 787 switch (hubd->h_dev_state) { 788 case USB_DEV_HUB_CHILD_PWRLVL: 789 /* 790 * This could be a bus ctl for a port other than the one 791 * that has a remote wakeup condition. So check. 792 */ 793 if ((hubd->h_port_state[port] & PORT_STATUS_PSS) == 0) { 794 /* the port isn't suspended, so don't resume */ 795 retval = USB_SUCCESS; 796 797 USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 798 "hubd_resume_port: port=%d not suspended", port); 799 800 break; 801 } 802 /* 803 * Device has initiated a wakeup. 804 * Issue a ClearFeature(PortSuspend) 805 */ 806 mutex_exit(HUBD_MUTEX(hubd)); 807 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 808 hubd->h_default_pipe, 809 HUB_HANDLE_PORT_FEATURE_TYPE, 810 USB_REQ_CLEAR_FEATURE, 811 CFS_PORT_SUSPEND, 812 port, 813 0, NULL, 0, 814 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 815 USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 816 "ClearFeature(PortSuspend) fails " 817 "rval=%d cr=%d cb=0x%x", rval, 818 completion_reason, cb_flags); 819 } 820 mutex_enter(HUBD_MUTEX(hubd)); 821 822 /* either way ack changes on the port */ 823 (void) hubd_determine_port_status(hubd, port, 824 &status, &change, PORT_CHANGE_PSSC); 825 retval = USB_SUCCESS; 826 827 break; 828 case USB_DEV_HUB_STATE_RECOVER: 829 /* 830 * When hubd's connect event callback posts a connect 831 * event to its child, it results in this busctl call 832 * which is valid 833 */ 834 /* FALLTHRU */ 835 case USB_DEV_ONLINE: 836 if (((hubd->h_port_state[port] & PORT_STATUS_CCS) == 0) || 837 ((hubd->h_port_state[port] & PORT_STATUS_PSS) == 0)) { 838 /* 839 * the port isn't suspended, or connected 840 * so don't resume 841 */ 842 retval = USB_SUCCESS; 843 844 USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 845 "hubd_resume_port: port=%d not suspended", port); 846 847 break; 848 } 849 /* 850 * prevent kicking off the hotplug thread 851 */ 852 hubd->h_hotplug_thread++; 853 hubd_stop_polling(hubd); 854 855 /* Now ClearFeature(PortSuspend) */ 856 for (retry = 0; retry < HUBD_SUS_RES_RETRY; retry++) { 857 mutex_exit(HUBD_MUTEX(hubd)); 858 rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 859 hubd->h_default_pipe, 860 HUB_HANDLE_PORT_FEATURE_TYPE, 861 USB_REQ_CLEAR_FEATURE, 862 CFS_PORT_SUSPEND, 863 port, 864 0, NULL, 0, 865 &completion_reason, &cb_flags, 0); 866 mutex_enter(HUBD_MUTEX(hubd)); 867 if (rval != USB_SUCCESS) { 868 USB_DPRINTF_L2(DPRINT_MASK_PM, 869 hubd->h_log_handle, 870 "ClearFeature(PortSuspend) fails" 871 "rval=%d cr=%d cb=0x%x", rval, 872 completion_reason, cb_flags); 873 } else { 874 /* 875 * As per spec section 11.9 and 7.1.7.7 876 * hub need to provide at least 20ms of 877 * resume signalling, and s/w provide 10ms of 878 * recovery time before accessing the port. 879 */ 880 mutex_exit(HUBD_MUTEX(hubd)); 881 delay(drv_usectohz(40000)); 882 mutex_enter(HUBD_MUTEX(hubd)); 883 (void) hubd_determine_port_status(hubd, port, 884 &status, &change, PORT_CHANGE_PSSC); 885 886 if ((status & PORT_STATUS_PSS) == 0) { 887 /* the port did finally resume */ 888 retval = USB_SUCCESS; 889 890 break; 891 } 892 } 893 } 894 895 /* allow hotplug thread again */ 896 hubd->h_hotplug_thread--; 897 hubd_start_polling(hubd, 0); 898 899 break; 900 case USB_DEV_DISCONNECTED: 901 /* Ignore - NO Operation */ 902 retval = USB_SUCCESS; 903 904 break; 905 case USB_DEV_SUSPENDED: 906 case USB_DEV_PWRED_DOWN: 907 default: 908 USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 909 "Improper state for port Resume"); 910 911 break; 912 } 913 mutex_exit(HUBD_MUTEX(hubd)); 914 915 return (retval); 916 } 917 918 919 /* 920 * suspend port depending on device state 921 */ 922 static int 923 hubd_suspend_port(hubd_t *hubd, usb_port_t port) 924 { 925 int rval, retry; 926 int retval = USB_FAILURE; 927 usb_cr_t completion_reason; 928 usb_cb_flags_t cb_flags; 929 uint16_t status; 930 uint16_t change; 931 932 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 933 "hubd_suspend_port: port=%d", port); 934 935 mutex_enter(HUBD_MUTEX(hubd)); 936 937 switch (hubd->h_dev_state) { 938 case USB_DEV_HUB_STATE_RECOVER: 939 /* 940 * When hubd's connect event callback posts a connect 941 * event to its child, it results in this busctl call 942 * which is valid 943 */ 944 /* FALLTHRU */ 945 case USB_DEV_HUB_CHILD_PWRLVL: 946 /* 947 * When one child is resuming, the other could timeout 948 * and go to low power mode, which is valid 949 */ 950 /* FALLTHRU */ 951 case USB_DEV_ONLINE: 952 hubd->h_hotplug_thread++; 953 hubd_stop_polling(hubd); 954 955 /* 956 * Some devices start an unprovoked resume. According to spec, 957 * normal resume time for port is 10ms. Wait for double that 958 * time, then check to be sure port is really suspended. 959 */ 960 for (retry = 0; retry < HUBD_SUS_RES_RETRY; retry++) { 961 /* Now SetFeature(PortSuspend) */ 962 mutex_exit(HUBD_MUTEX(hubd)); 963 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 964 hubd->h_default_pipe, 965 HUB_HANDLE_PORT_FEATURE_TYPE, 966 USB_REQ_SET_FEATURE, 967 CFS_PORT_SUSPEND, 968 port, 969 0, NULL, 0, 970 &completion_reason, &cb_flags, 0)) != 971 USB_SUCCESS) { 972 USB_DPRINTF_L2(DPRINT_MASK_PM, 973 hubd->h_log_handle, 974 "SetFeature(PortSuspend) fails" 975 "rval=%d cr=%d cb=0x%x", 976 rval, completion_reason, cb_flags); 977 } 978 979 /* 980 * some devices start an unprovoked resume 981 * wait and check port status after some time 982 */ 983 delay(drv_usectohz(20000)); 984 985 /* either ways ack changes on the port */ 986 mutex_enter(HUBD_MUTEX(hubd)); 987 (void) hubd_determine_port_status(hubd, port, 988 &status, &change, PORT_CHANGE_PSSC); 989 if (status & PORT_STATUS_PSS) { 990 /* the port is indeed suspended */ 991 retval = USB_SUCCESS; 992 993 break; 994 } 995 } 996 997 hubd->h_hotplug_thread--; 998 hubd_start_polling(hubd, 0); 999 1000 break; 1001 1002 case USB_DEV_DISCONNECTED: 1003 /* Ignore - No Operation */ 1004 retval = USB_SUCCESS; 1005 1006 break; 1007 1008 case USB_DEV_SUSPENDED: 1009 case USB_DEV_PWRED_DOWN: 1010 default: 1011 USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 1012 "Improper state for port Suspend"); 1013 1014 break; 1015 } 1016 mutex_exit(HUBD_MUTEX(hubd)); 1017 1018 return (retval); 1019 } 1020 1021 1022 /* 1023 * child post attach/detach notifications 1024 */ 1025 static void 1026 hubd_post_attach(hubd_t *hubd, usb_port_t port, struct attachspec *as) 1027 { 1028 dev_info_t *dip; 1029 1030 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 1031 "hubd_post_attach: port=%d result=%d", 1032 port, as->result); 1033 1034 if (as->result == DDI_SUCCESS) { 1035 /* 1036 * Check if the child created wants to be power managed. 1037 * If yes, the childs power level gets automatically tracked 1038 * by DDI_CTLOPS_POWER busctl. 1039 * If no, we set power of the new child by default 1040 * to USB_DEV_OS_FULL_PWR. Because we should never suspend. 1041 */ 1042 mutex_enter(HUBD_MUTEX(hubd)); 1043 dip = hubd->h_children_dips[port]; 1044 mutex_exit(HUBD_MUTEX(hubd)); 1045 if (DEVI(dip)->devi_pm_info == NULL) { 1046 hubd_set_child_pwrlvl(hubd, port, USB_DEV_OS_FULL_PWR); 1047 } 1048 } 1049 } 1050 1051 1052 static void 1053 hubd_post_detach(hubd_t *hubd, usb_port_t port, struct detachspec *ds) 1054 { 1055 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 1056 "hubd_post_detach: port=%d result=%d", port, ds->result); 1057 1058 /* 1059 * if the device is successfully detached and is the 1060 * last device to detach, mark component as idle 1061 */ 1062 mutex_enter(HUBD_MUTEX(hubd)); 1063 if (ds->result == DDI_SUCCESS) { 1064 usba_device_t *usba_device = hubd->h_usba_devices[port]; 1065 dev_info_t *pdip = hubd->h_dip; 1066 mutex_exit(HUBD_MUTEX(hubd)); 1067 1068 usba_hubdi_incr_power_budget(pdip, usba_device); 1069 1070 /* 1071 * We set power of the detached child 1072 * to 0, so that we can suspend if all 1073 * our children are gone 1074 */ 1075 hubd_set_child_pwrlvl(hubd, port, USB_DEV_OS_PWR_OFF); 1076 1077 /* check for leaks on detaching */ 1078 if ((usba_device) && (ds->cmd == DDI_DETACH)) { 1079 usba_check_for_leaks(usba_device); 1080 } 1081 } else { 1082 mutex_exit(HUBD_MUTEX(hubd)); 1083 } 1084 } 1085 1086 1087 /* 1088 * hubd_post_power 1089 * After the child's power entry point has been called 1090 * we record its power level in our local struct. 1091 * If the device has powered off, we suspend port 1092 */ 1093 static int 1094 hubd_post_power(hubd_t *hubd, usb_port_t port, pm_bp_child_pwrchg_t *bpc, 1095 int result) 1096 { 1097 int retval = USB_SUCCESS; 1098 1099 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 1100 "hubd_post_power: port=%d", port); 1101 1102 if (result == DDI_SUCCESS) { 1103 1104 /* record this power in our local struct */ 1105 hubd_set_child_pwrlvl(hubd, port, bpc->bpc_nlevel); 1106 1107 if (bpc->bpc_nlevel == USB_DEV_OS_PWR_OFF) { 1108 1109 /* now suspend the port */ 1110 retval = hubd_suspend_port(hubd, port); 1111 } else if (bpc->bpc_nlevel == USB_DEV_OS_FULL_PWR) { 1112 1113 /* make sure the port is resumed */ 1114 retval = hubd_resume_port(hubd, port); 1115 } 1116 } else { 1117 1118 /* record old power in our local struct */ 1119 hubd_set_child_pwrlvl(hubd, port, bpc->bpc_olevel); 1120 1121 if (bpc->bpc_olevel == USB_DEV_OS_PWR_OFF) { 1122 1123 /* 1124 * As this device failed to transition from 1125 * power off state, suspend the port again 1126 */ 1127 retval = hubd_suspend_port(hubd, port); 1128 } 1129 } 1130 1131 return (retval); 1132 } 1133 1134 1135 /* 1136 * bus ctl notifications are handled here, the rest goes up to root hub/hcd 1137 */ 1138 static int 1139 usba_hubdi_bus_ctl(dev_info_t *dip, 1140 dev_info_t *rdip, 1141 ddi_ctl_enum_t op, 1142 void *arg, 1143 void *result) 1144 { 1145 usba_device_t *hub_usba_device = usba_get_usba_device(rdip); 1146 dev_info_t *root_hub_dip = hub_usba_device->usb_root_hub_dip; 1147 struct attachspec *as; 1148 struct detachspec *ds; 1149 hubd_t *hubd; 1150 usb_port_t port; 1151 int circ, rval; 1152 int retval = DDI_FAILURE; 1153 1154 hubd = hubd_get_soft_state(dip); 1155 1156 mutex_enter(HUBD_MUTEX(hubd)); 1157 1158 /* flag that we are currently running bus_ctl */ 1159 hubd->h_bus_ctls++; 1160 mutex_exit(HUBD_MUTEX(hubd)); 1161 1162 USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle, 1163 "usba_hubdi_bus_ctl:\n\t" 1164 "dip=0x%p, rdip=0x%p, op=0x%x, arg=0x%p", 1165 (void *)dip, (void *)rdip, op, arg); 1166 1167 switch (op) { 1168 case DDI_CTLOPS_ATTACH: 1169 as = (struct attachspec *)arg; 1170 port = hubd_child_dip2port(hubd, rdip); 1171 1172 /* there is nothing to do at resume time */ 1173 if (as->cmd == DDI_RESUME) { 1174 break; 1175 } 1176 1177 /* serialize access */ 1178 ndi_devi_enter(hubd->h_dip, &circ); 1179 1180 switch (as->when) { 1181 case DDI_PRE: 1182 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 1183 "DDI_PRE DDI_CTLOPS_ATTACH: dip=%p, port=%d", 1184 (void *)rdip, port); 1185 1186 mutex_enter(HUBD_MUTEX(hubd)); 1187 hubd->h_port_state[port] |= HUBD_CHILD_ATTACHING; 1188 1189 /* Go busy here. Matching idle is DDI_POST case. */ 1190 (void) hubd_pm_busy_component(hubd, dip, 0); 1191 mutex_exit(HUBD_MUTEX(hubd)); 1192 1193 /* 1194 * if we suspended the port previously 1195 * because child went to low power state, and 1196 * someone unloaded the driver, the port would 1197 * still be suspended and needs to be resumed 1198 */ 1199 rval = hubd_resume_port(hubd, port); 1200 if (rval == USB_SUCCESS) { 1201 retval = DDI_SUCCESS; 1202 } 1203 1204 break; 1205 case DDI_POST: 1206 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 1207 "DDI_POST DDI_CTLOPS_ATTACH: dip=%p, port=%d", 1208 (void *)rdip, port); 1209 1210 mutex_enter(HUBD_MUTEX(hubd)); 1211 hubd->h_port_state[port] &= ~HUBD_CHILD_ATTACHING; 1212 mutex_exit(HUBD_MUTEX(hubd)); 1213 1214 hubd_post_attach(hubd, port, (struct attachspec *)arg); 1215 retval = DDI_SUCCESS; 1216 mutex_enter(HUBD_MUTEX(hubd)); 1217 1218 /* Matching idle call for DDI_PRE busy call. */ 1219 (void) hubd_pm_idle_component(hubd, dip, 0); 1220 mutex_exit(HUBD_MUTEX(hubd)); 1221 } 1222 ndi_devi_exit(hubd->h_dip, circ); 1223 1224 break; 1225 case DDI_CTLOPS_DETACH: 1226 ds = (struct detachspec *)arg; 1227 port = hubd_child_dip2port(hubd, rdip); 1228 1229 /* there is nothing to do at suspend time */ 1230 if (ds->cmd == DDI_SUSPEND) { 1231 break; 1232 } 1233 1234 /* serialize access */ 1235 ndi_devi_enter(hubd->h_dip, &circ); 1236 1237 switch (ds->when) { 1238 case DDI_PRE: 1239 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 1240 "DDI_PRE DDI_CTLOPS_DETACH: dip=%p port=%d", 1241 (void *)rdip, port); 1242 1243 mutex_enter(HUBD_MUTEX(hubd)); 1244 hubd->h_port_state[port] |= HUBD_CHILD_DETACHING; 1245 1246 /* Go busy here. Matching idle is DDI_POST case. */ 1247 (void) hubd_pm_busy_component(hubd, dip, 0); 1248 1249 mutex_exit(HUBD_MUTEX(hubd)); 1250 retval = DDI_SUCCESS; 1251 1252 break; 1253 case DDI_POST: 1254 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 1255 "DDI_POST DDI_CTLOPS_DETACH: dip=%p port=%d", 1256 (void *)rdip, port); 1257 1258 mutex_enter(HUBD_MUTEX(hubd)); 1259 hubd->h_port_state[port] &= ~HUBD_CHILD_DETACHING; 1260 mutex_exit(HUBD_MUTEX(hubd)); 1261 1262 /* Matching idle call for DDI_PRE busy call. */ 1263 hubd_post_detach(hubd, port, (struct detachspec *)arg); 1264 retval = DDI_SUCCESS; 1265 mutex_enter(HUBD_MUTEX(hubd)); 1266 (void) hubd_pm_idle_component(hubd, dip, 0); 1267 mutex_exit(HUBD_MUTEX(hubd)); 1268 1269 break; 1270 } 1271 ndi_devi_exit(hubd->h_dip, circ); 1272 1273 break; 1274 default: 1275 retval = usba_bus_ctl(root_hub_dip, rdip, op, arg, result); 1276 } 1277 1278 /* decrement bus_ctls count */ 1279 mutex_enter(HUBD_MUTEX(hubd)); 1280 hubd->h_bus_ctls--; 1281 ASSERT(hubd->h_bus_ctls >= 0); 1282 mutex_exit(HUBD_MUTEX(hubd)); 1283 1284 return (retval); 1285 } 1286 1287 /* 1288 * hubd_config_one: 1289 * enumerate one child according to 'port' 1290 */ 1291 1292 static boolean_t 1293 hubd_config_one(hubd_t *hubd, int port) 1294 { 1295 uint16_t status, change; 1296 dev_info_t *hdip = hubd->h_dip; 1297 dev_info_t *rh_dip = hubd->h_usba_device->usb_root_hub_dip; 1298 boolean_t online_child = B_FALSE, found = B_FALSE; 1299 int prh_circ, rh_circ, circ; 1300 1301 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 1302 "hubd_config_one: started, hubd_reset_port = 0x%x", port); 1303 1304 ndi_hold_devi(hdip); /* so we don't race with detach */ 1305 1306 /* 1307 * this ensures one config activity per system at a time. 1308 * we enter the parent PCI node to have this serialization. 1309 * this also excludes ioctls and deathrow thread 1310 */ 1311 ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ); 1312 ndi_devi_enter(rh_dip, &rh_circ); 1313 1314 /* exclude other threads */ 1315 ndi_devi_enter(hdip, &circ); 1316 mutex_enter(HUBD_MUTEX(hubd)); 1317 1318 hubd_pm_busy_component(hubd, hubd->h_dip, 0); 1319 hubd_stop_polling(hubd); 1320 1321 1322 1323 if (!hubd->h_children_dips[port]) { 1324 1325 (void) hubd_determine_port_status(hubd, port, 1326 &status, &change, HUBD_ACK_ALL_CHANGES); 1327 1328 if (status & PORT_STATUS_CCS) { 1329 online_child |= (hubd_handle_port_connect(hubd, 1330 port) == USB_SUCCESS); 1331 found = online_child; 1332 } 1333 } else { 1334 found = B_TRUE; 1335 } 1336 1337 mutex_exit(HUBD_MUTEX(hubd)); 1338 1339 ndi_devi_exit(hdip, circ); 1340 ndi_devi_exit(rh_dip, rh_circ); 1341 ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ); 1342 1343 if (online_child) { 1344 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 1345 "hubd_config_one: onlining child"); 1346 1347 (void) ndi_devi_online(hubd->h_dip, 0); 1348 } 1349 1350 mutex_enter(HUBD_MUTEX(hubd)); 1351 1352 hubd_start_polling(hubd, 0); 1353 (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0); 1354 1355 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 1356 "hubd_config_one: exit"); 1357 1358 mutex_exit(HUBD_MUTEX(hubd)); 1359 1360 ndi_rele_devi(hdip); 1361 1362 return (found); 1363 } 1364 1365 /* 1366 * bus enumeration entry points 1367 */ 1368 static int 1369 hubd_bus_config(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op, 1370 void *arg, dev_info_t **child) 1371 { 1372 hubd_t *hubd = hubd_get_soft_state(dip); 1373 int rval, circ; 1374 long port; 1375 1376 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 1377 "hubd_bus_config: op=%d", op); 1378 1379 if (hubdi_bus_config_debug) { 1380 flag |= NDI_DEVI_DEBUG; 1381 } 1382 1383 if (op == BUS_CONFIG_ONE) { 1384 boolean_t found; 1385 char cname[80]; 1386 char *name, *addr; 1387 1388 USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 1389 "hubd_bus_config: op=%d (BUS_CONFIG_ONE)", op); 1390 1391 (void) snprintf(cname, 80, "%s", (char *)arg); 1392 /* split name into "name@addr" parts */ 1393 i_ddi_parse_name(cname, &name, &addr, NULL); 1394 if (addr && *addr) { 1395 (void) ddi_strtol(addr, NULL, 16, &port); 1396 } else { 1397 return (NDI_FAILURE); 1398 } 1399 1400 found = hubd_config_one(hubd, port); 1401 1402 if (found == 0) { 1403 return (NDI_FAILURE); 1404 } 1405 1406 } 1407 ndi_devi_enter(hubd->h_dip, &circ); 1408 rval = ndi_busop_bus_config(dip, flag, op, arg, child, 0); 1409 ndi_devi_exit(hubd->h_dip, circ); 1410 1411 return (rval); 1412 } 1413 1414 1415 static int 1416 hubd_bus_unconfig(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op, 1417 void *arg) 1418 { 1419 hubd_t *hubd = hubd_get_soft_state(dip); 1420 dev_info_t *cdip; 1421 usb_port_t port; 1422 int circ; 1423 int rval; 1424 1425 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 1426 "hubd_bus_unconfig: op=%d", op); 1427 1428 if (hubdi_bus_config_debug) { 1429 flag |= NDI_DEVI_DEBUG; 1430 } 1431 1432 if ((op == BUS_UNCONFIG_ALL) && (flag & NDI_AUTODETACH) == 0) { 1433 flag |= NDI_DEVI_REMOVE; 1434 } 1435 1436 /* serialize access */ 1437 ndi_devi_enter(dip, &circ); 1438 1439 rval = ndi_busop_bus_unconfig(dip, flag, op, arg); 1440 1441 /* logically zap children's list */ 1442 mutex_enter(HUBD_MUTEX(hubd)); 1443 for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { 1444 hubd->h_port_state[port] |= HUBD_CHILD_ZAP; 1445 } 1446 mutex_exit(HUBD_MUTEX(hubd)); 1447 1448 /* fill in what's left */ 1449 for (cdip = ddi_get_child(dip); cdip; 1450 cdip = ddi_get_next_sibling(cdip)) { 1451 usba_device_t *usba_device = usba_get_usba_device(cdip); 1452 1453 if (usba_device == NULL) { 1454 1455 continue; 1456 } 1457 mutex_enter(HUBD_MUTEX(hubd)); 1458 port = usba_device->usb_port; 1459 hubd->h_children_dips[port] = cdip; 1460 hubd->h_port_state[port] &= ~HUBD_CHILD_ZAP; 1461 mutex_exit(HUBD_MUTEX(hubd)); 1462 } 1463 1464 /* physically zap the children we didn't find */ 1465 mutex_enter(HUBD_MUTEX(hubd)); 1466 for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { 1467 if (hubd->h_port_state[port] & HUBD_CHILD_ZAP) { 1468 /* zap the dip and usba_device structure as well */ 1469 hubd_free_usba_device(hubd, hubd->h_usba_devices[port]); 1470 hubd->h_children_dips[port] = NULL; 1471 hubd->h_port_state[port] &= ~HUBD_CHILD_ZAP; 1472 } 1473 } 1474 mutex_exit(HUBD_MUTEX(hubd)); 1475 1476 ndi_devi_exit(dip, circ); 1477 1478 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 1479 "hubd_bus_unconfig: rval=%d", rval); 1480 1481 return (rval); 1482 } 1483 1484 1485 /* bus_power entry point */ 1486 static int 1487 hubd_bus_power(dev_info_t *dip, void *impl_arg, pm_bus_power_op_t op, 1488 void *arg, void *result) 1489 { 1490 hubd_t *hubd; 1491 int rval, pwrup_res; 1492 usb_port_t port; 1493 int retval = DDI_FAILURE; 1494 pm_bp_child_pwrchg_t *bpc; 1495 pm_bp_nexus_pwrup_t bpn; 1496 1497 hubd = hubd_get_soft_state(dip); 1498 1499 USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubd->h_log_handle, 1500 "hubd_bus_power: dip=%p, impl_arg=%p, power_op=%d, arg=%p, " 1501 "result=%d\n", (void *)dip, impl_arg, op, arg, *(int *)result); 1502 1503 bpc = (pm_bp_child_pwrchg_t *)arg; 1504 1505 mutex_enter(HUBD_MUTEX(hubd)); 1506 hubd->h_bus_pwr++; 1507 mutex_exit(HUBD_MUTEX(hubd)); 1508 1509 switch (op) { 1510 case BUS_POWER_PRE_NOTIFICATION: 1511 port = hubd_child_dip2port(hubd, bpc->bpc_dip); 1512 USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle, 1513 "hubd_bus_power: BUS_POWER_PRE_NOTIFICATION, port=%d", 1514 port); 1515 1516 /* go to full power if we are powered down */ 1517 mutex_enter(HUBD_MUTEX(hubd)); 1518 1519 /* 1520 * If this case completes normally, idle will be in 1521 * hubd_bus_power / BUS_POWER_POST_NOTIFICATION 1522 */ 1523 hubd_pm_busy_component(hubd, dip, 0); 1524 1525 /* 1526 * raise power only if we have created the components 1527 * and are currently in low power 1528 */ 1529 if ((hubd->h_dev_state == USB_DEV_PWRED_DOWN) && 1530 hubd->h_hubpm->hubp_wakeup_enabled) { 1531 mutex_exit(HUBD_MUTEX(hubd)); 1532 1533 bpn.bpn_comp = 0; 1534 bpn.bpn_dip = dip; 1535 bpn.bpn_level = USB_DEV_OS_FULL_PWR; 1536 bpn.bpn_private = bpc->bpc_private; 1537 1538 rval = pm_busop_bus_power(dip, impl_arg, 1539 BUS_POWER_NEXUS_PWRUP, (void *)&bpn, 1540 (void *)&pwrup_res); 1541 1542 if (rval != DDI_SUCCESS || pwrup_res != DDI_SUCCESS) { 1543 mutex_enter(HUBD_MUTEX(hubd)); 1544 hubd_pm_idle_component(hubd, dip, 0); 1545 mutex_exit(HUBD_MUTEX(hubd)); 1546 1547 break; 1548 } 1549 mutex_enter(HUBD_MUTEX(hubd)); 1550 } 1551 1552 /* indicate that child is changing power level */ 1553 hubd->h_port_state[port] |= HUBD_CHILD_PWRLVL_CHNG; 1554 mutex_exit(HUBD_MUTEX(hubd)); 1555 1556 if ((bpc->bpc_olevel == 0) && 1557 (bpc->bpc_nlevel > bpc->bpc_olevel)) { 1558 /* 1559 * this child is transitioning from power off 1560 * to power on state - resume port 1561 */ 1562 rval = hubd_resume_port(hubd, port); 1563 if (rval == USB_SUCCESS) { 1564 retval = DDI_SUCCESS; 1565 } else { 1566 /* reset this flag on failure */ 1567 mutex_enter(HUBD_MUTEX(hubd)); 1568 hubd->h_port_state[port] &= 1569 ~HUBD_CHILD_PWRLVL_CHNG; 1570 hubd_pm_idle_component(hubd, dip, 0); 1571 mutex_exit(HUBD_MUTEX(hubd)); 1572 } 1573 } else { 1574 retval = DDI_SUCCESS; 1575 } 1576 1577 break; 1578 case BUS_POWER_POST_NOTIFICATION: 1579 port = hubd_child_dip2port(hubd, bpc->bpc_dip); 1580 USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle, 1581 "hubd_bus_power: BUS_POWER_POST_NOTIFICATION, port=%d", 1582 port); 1583 1584 mutex_enter(HUBD_MUTEX(hubd)); 1585 hubd->h_port_state[port] &= ~HUBD_CHILD_PWRLVL_CHNG; 1586 mutex_exit(HUBD_MUTEX(hubd)); 1587 1588 /* record child's pwr and suspend port if required */ 1589 rval = hubd_post_power(hubd, port, bpc, *(int *)result); 1590 if (rval == USB_SUCCESS) { 1591 1592 retval = DDI_SUCCESS; 1593 } 1594 1595 mutex_enter(HUBD_MUTEX(hubd)); 1596 1597 /* 1598 * Matching idle for the busy in 1599 * hubd_bus_power / BUS_POWER_PRE_NOTIFICATION 1600 */ 1601 hubd_pm_idle_component(hubd, dip, 0); 1602 1603 mutex_exit(HUBD_MUTEX(hubd)); 1604 1605 break; 1606 default: 1607 retval = pm_busop_bus_power(dip, impl_arg, op, arg, result); 1608 1609 break; 1610 } 1611 1612 mutex_enter(HUBD_MUTEX(hubd)); 1613 hubd->h_bus_pwr--; 1614 mutex_exit(HUBD_MUTEX(hubd)); 1615 1616 return (retval); 1617 } 1618 1619 1620 /* 1621 * functions to handle power transition for OS levels 0 -> 3 1622 */ 1623 static int 1624 hubd_pwrlvl0(hubd_t *hubd) 1625 { 1626 hub_power_t *hubpm; 1627 1628 /* We can't power down if hotplug thread is running */ 1629 if (hubd->h_hotplug_thread || hubd->h_hubpm->hubp_busy_pm || 1630 (hubd_can_suspend(hubd) == USB_FAILURE)) { 1631 1632 return (USB_FAILURE); 1633 } 1634 1635 switch (hubd->h_dev_state) { 1636 case USB_DEV_ONLINE: 1637 hubpm = hubd->h_hubpm; 1638 1639 /* 1640 * To avoid race with bus_power pre_notify on check over 1641 * dev_state, we need to correctly set the dev state 1642 * before the mutex is dropped in stop polling. 1643 */ 1644 hubd->h_dev_state = USB_DEV_PWRED_DOWN; 1645 hubpm->hubp_current_power = USB_DEV_OS_PWR_OFF; 1646 1647 /* 1648 * if we are the root hub, do not stop polling 1649 * otherwise, we will never see a resume 1650 */ 1651 if (usba_is_root_hub(hubd->h_dip)) { 1652 /* place holder to implement Global Suspend */ 1653 USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 1654 "Global Suspend: Not Yet Implemented"); 1655 } else { 1656 hubd_stop_polling(hubd); 1657 } 1658 1659 /* Issue USB D3 command to the device here */ 1660 (void) usb_set_device_pwrlvl3(hubd->h_dip); 1661 1662 break; 1663 case USB_DEV_DISCONNECTED: 1664 case USB_DEV_SUSPENDED: 1665 case USB_DEV_PWRED_DOWN: 1666 default: 1667 1668 break; 1669 } 1670 1671 return (USB_SUCCESS); 1672 } 1673 1674 1675 /* ARGSUSED */ 1676 static int 1677 hubd_pwrlvl1(hubd_t *hubd) 1678 { 1679 /* Issue USB D2 command to the device here */ 1680 (void) usb_set_device_pwrlvl2(hubd->h_dip); 1681 1682 return (USB_FAILURE); 1683 } 1684 1685 1686 /* ARGSUSED */ 1687 static int 1688 hubd_pwrlvl2(hubd_t *hubd) 1689 { 1690 /* Issue USB D1 command to the device here */ 1691 (void) usb_set_device_pwrlvl1(hubd->h_dip); 1692 1693 return (USB_FAILURE); 1694 } 1695 1696 1697 static int 1698 hubd_pwrlvl3(hubd_t *hubd) 1699 { 1700 hub_power_t *hubpm; 1701 int rval; 1702 1703 USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, "hubd_pwrlvl3"); 1704 1705 hubpm = hubd->h_hubpm; 1706 switch (hubd->h_dev_state) { 1707 case USB_DEV_PWRED_DOWN: 1708 ASSERT(hubpm->hubp_current_power == USB_DEV_OS_PWR_OFF); 1709 if (usba_is_root_hub(hubd->h_dip)) { 1710 /* implement global resume here */ 1711 USB_DPRINTF_L2(DPRINT_MASK_PM, 1712 hubd->h_log_handle, 1713 "Global Resume: Not Yet Implemented"); 1714 } 1715 /* Issue USB D0 command to the device here */ 1716 rval = usb_set_device_pwrlvl0(hubd->h_dip); 1717 ASSERT(rval == USB_SUCCESS); 1718 hubd->h_dev_state = USB_DEV_ONLINE; 1719 hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR; 1720 hubpm->hubp_time_at_full_power = ddi_get_time(); 1721 hubd_start_polling(hubd, 0); 1722 1723 /* FALLTHRU */ 1724 case USB_DEV_ONLINE: 1725 /* we are already in full power */ 1726 1727 /* FALLTHRU */ 1728 case USB_DEV_DISCONNECTED: 1729 case USB_DEV_SUSPENDED: 1730 /* 1731 * PM framework tries to put you in full power 1732 * during system shutdown. If we are disconnected 1733 * return success. Also, we should not change state 1734 * when we are disconnected or suspended or about to 1735 * transition to that state 1736 */ 1737 1738 return (USB_SUCCESS); 1739 default: 1740 USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 1741 "hubd_pwrlvl3: Illegal dev_state=%d", hubd->h_dev_state); 1742 1743 return (USB_FAILURE); 1744 } 1745 } 1746 1747 1748 /* power entry point */ 1749 /* ARGSUSED */ 1750 int 1751 usba_hubdi_power(dev_info_t *dip, int comp, int level) 1752 { 1753 hubd_t *hubd; 1754 hub_power_t *hubpm; 1755 int retval; 1756 int circ; 1757 1758 hubd = hubd_get_soft_state(dip); 1759 USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle, 1760 "usba_hubdi_power: level=%d", level); 1761 1762 ndi_devi_enter(dip, &circ); 1763 1764 mutex_enter(HUBD_MUTEX(hubd)); 1765 hubpm = hubd->h_hubpm; 1766 1767 /* check if we are transitioning to a legal power level */ 1768 if (USB_DEV_PWRSTATE_OK(hubpm->hubp_pwr_states, level)) { 1769 USB_DPRINTF_L2(DPRINT_MASK_HUBDI, hubd->h_log_handle, 1770 "usba_hubdi_power: illegal power level=%d " 1771 "hubp_pwr_states=0x%x", level, hubpm->hubp_pwr_states); 1772 mutex_exit(HUBD_MUTEX(hubd)); 1773 1774 ndi_devi_exit(dip, circ); 1775 1776 return (DDI_FAILURE); 1777 } 1778 1779 switch (level) { 1780 case USB_DEV_OS_PWR_OFF: 1781 retval = hubd_pwrlvl0(hubd); 1782 1783 break; 1784 case USB_DEV_OS_PWR_1: 1785 retval = hubd_pwrlvl1(hubd); 1786 1787 break; 1788 case USB_DEV_OS_PWR_2: 1789 retval = hubd_pwrlvl2(hubd); 1790 1791 break; 1792 case USB_DEV_OS_FULL_PWR: 1793 retval = hubd_pwrlvl3(hubd); 1794 1795 break; 1796 } 1797 mutex_exit(HUBD_MUTEX(hubd)); 1798 1799 ndi_devi_exit(dip, circ); 1800 1801 return ((retval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE); 1802 } 1803 1804 1805 /* power entry point for the root hub */ 1806 int 1807 usba_hubdi_root_hub_power(dev_info_t *dip, int comp, int level) 1808 { 1809 return (usba_hubdi_power(dip, comp, level)); 1810 } 1811 1812 1813 /* 1814 * standard driver entry points support code 1815 */ 1816 int 1817 usba_hubdi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 1818 { 1819 int instance = ddi_get_instance(dip); 1820 hubd_t *hubd = NULL; 1821 int i, rval; 1822 int minor; 1823 uint8_t ports_count; 1824 char *log_name = NULL; 1825 const char *root_hub_drvname; 1826 usb_ep_data_t *ep_data; 1827 usba_device_t *child_ud = NULL; 1828 usb_dev_descr_t *usb_dev_descr; 1829 usb_port_status_t parent_port_status, child_port_status; 1830 1831 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubdi_log_handle, 1832 "hubd_attach instance %d, cmd=0x%x", instance, cmd); 1833 1834 switch (cmd) { 1835 case DDI_ATTACH: 1836 1837 break; 1838 case DDI_RESUME: 1839 hubd_cpr_resume(dip); 1840 1841 return (DDI_SUCCESS); 1842 default: 1843 return (DDI_FAILURE); 1844 } 1845 1846 /* 1847 * Allocate softc information. 1848 */ 1849 if (usba_is_root_hub(dip)) { 1850 /* soft state has already been allocated */ 1851 hubd = hubd_get_soft_state(dip); 1852 minor = HUBD_IS_ROOT_HUB; 1853 1854 /* generate readable labels for different root hubs */ 1855 root_hub_drvname = ddi_driver_name(dip); 1856 if (strcmp(root_hub_drvname, "ehci") == 0) { 1857 log_name = "eusb"; 1858 } else if (strcmp(root_hub_drvname, "uhci") == 0) { 1859 log_name = "uusb"; 1860 } else { 1861 /* std. for ohci */ 1862 log_name = "usb"; 1863 } 1864 } else { 1865 rval = ddi_soft_state_zalloc(hubd_statep, instance); 1866 minor = 0; 1867 1868 if (rval != DDI_SUCCESS) { 1869 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle, 1870 "cannot allocate soft state (%d)", instance); 1871 goto fail; 1872 } 1873 1874 hubd = hubd_get_soft_state(dip); 1875 if (hubd == NULL) { 1876 goto fail; 1877 } 1878 } 1879 1880 hubd->h_log_handle = usb_alloc_log_hdl(dip, log_name, &hubd_errlevel, 1881 &hubd_errmask, &hubd_instance_debug, 0); 1882 1883 hubd->h_usba_device = child_ud = usba_get_usba_device(dip); 1884 hubd->h_dip = dip; 1885 hubd->h_instance = instance; 1886 1887 mutex_enter(&child_ud->usb_mutex); 1888 child_port_status = child_ud->usb_port_status; 1889 usb_dev_descr = child_ud->usb_dev_descr; 1890 parent_port_status = (child_ud->usb_hs_hub_usba_dev) ? 1891 child_ud->usb_hs_hub_usba_dev->usb_port_status : 0; 1892 mutex_exit(&child_ud->usb_mutex); 1893 1894 if ((child_port_status == USBA_FULL_SPEED_DEV) && 1895 (parent_port_status == USBA_HIGH_SPEED_DEV) && 1896 (usb_dev_descr->bcdUSB == 0x100)) { 1897 USB_DPRINTF_L0(DPRINT_MASK_ATTA, hubd->h_log_handle, 1898 "Use of a USB1.0 hub behind a high speed port may " 1899 "cause unexpected failures"); 1900 } 1901 1902 hubd->h_pipe_policy.pp_max_async_reqs = 1; 1903 1904 /* register with USBA as client driver */ 1905 if (usb_client_attach(dip, USBDRV_VERSION, 0) != USB_SUCCESS) { 1906 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 1907 "client attach failed"); 1908 1909 goto fail; 1910 } 1911 1912 if (usb_get_dev_data(dip, &hubd->h_dev_data, 1913 USB_PARSE_LVL_IF, 0) != USB_SUCCESS) { 1914 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 1915 "cannot get dev_data"); 1916 1917 goto fail; 1918 } 1919 1920 if ((ep_data = usb_lookup_ep_data(dip, hubd->h_dev_data, 1921 hubd->h_dev_data->dev_curr_if, 0, 0, 1922 (uint_t)USB_EP_ATTR_INTR, (uint_t)USB_EP_DIR_IN)) == NULL) { 1923 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 1924 "no interrupt IN endpoint found"); 1925 1926 goto fail; 1927 } 1928 1929 hubd->h_ep1_descr = ep_data->ep_descr; 1930 hubd->h_default_pipe = hubd->h_dev_data->dev_default_ph; 1931 1932 mutex_init(HUBD_MUTEX(hubd), NULL, MUTEX_DRIVER, 1933 hubd->h_dev_data->dev_iblock_cookie); 1934 cv_init(&hubd->h_cv_reset_port, NULL, CV_DRIVER, NULL); 1935 cv_init(&hubd->h_cv_hotplug_dev, NULL, CV_DRIVER, NULL); 1936 1937 hubd->h_init_state |= HUBD_LOCKS_DONE; 1938 1939 usb_free_descr_tree(dip, hubd->h_dev_data); 1940 1941 /* 1942 * register this hub instance with usba 1943 */ 1944 rval = usba_hubdi_register(dip, 0); 1945 if (rval != USB_SUCCESS) { 1946 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 1947 "usba_hubdi_register failed"); 1948 goto fail; 1949 } 1950 1951 mutex_enter(HUBD_MUTEX(hubd)); 1952 hubd->h_init_state |= HUBD_HUBDI_REGISTERED; 1953 hubd->h_dev_state = USB_DEV_ONLINE; 1954 mutex_exit(HUBD_MUTEX(hubd)); 1955 1956 /* now create components to power manage this device */ 1957 hubd_create_pm_components(dip, hubd); 1958 1959 /* 1960 * Event handling: definition and registration 1961 * 1962 * first the definition: 1963 * get event handle 1964 */ 1965 (void) ndi_event_alloc_hdl(dip, 0, &hubd->h_ndi_event_hdl, NDI_SLEEP); 1966 1967 /* bind event set to the handle */ 1968 if (ndi_event_bind_set(hubd->h_ndi_event_hdl, &hubd_ndi_events, 1969 NDI_SLEEP)) { 1970 USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle, 1971 "binding event set failed"); 1972 1973 goto fail; 1974 } 1975 1976 /* event registration */ 1977 if (hubd_register_events(hubd) != USB_SUCCESS) { 1978 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 1979 "hubd_register_events failed"); 1980 1981 goto fail; 1982 } 1983 1984 mutex_enter(HUBD_MUTEX(hubd)); 1985 hubd->h_init_state |= HUBD_EVENTS_REGISTERED; 1986 1987 if ((hubd_get_hub_descriptor(hubd)) != USB_SUCCESS) { 1988 mutex_exit(HUBD_MUTEX(hubd)); 1989 1990 goto fail; 1991 } 1992 1993 if (ddi_prop_exists(DDI_DEV_T_ANY, dip, 1994 (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM), 1995 "hub-ignore-power-budget") == 1) { 1996 hubd->h_ignore_pwr_budget = B_TRUE; 1997 } else { 1998 hubd->h_ignore_pwr_budget = B_FALSE; 1999 2000 /* initialize hub power budget variables */ 2001 if (hubd_init_power_budget(hubd) != USB_SUCCESS) { 2002 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 2003 "hubd_init_power_budget failed"); 2004 mutex_exit(HUBD_MUTEX(hubd)); 2005 2006 goto fail; 2007 } 2008 } 2009 2010 /* initialize and create children */ 2011 if (hubd_check_ports(hubd) != USB_SUCCESS) { 2012 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 2013 "hubd_check_ports failed"); 2014 mutex_exit(HUBD_MUTEX(hubd)); 2015 2016 goto fail; 2017 } 2018 2019 /* 2020 * create cfgadm nodes 2021 */ 2022 hubd->h_ancestry_str = (char *)kmem_zalloc(HUBD_APID_NAMELEN, KM_SLEEP); 2023 hubd_get_ancestry_str(hubd); 2024 2025 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 2026 "#ports=0x%x", hubd->h_hub_descr.bNbrPorts); 2027 2028 for (i = 1; i <= hubd->h_hub_descr.bNbrPorts; i++) { 2029 char ap_name[HUBD_APID_NAMELEN]; 2030 2031 (void) snprintf(ap_name, HUBD_APID_NAMELEN, "%s%d", 2032 hubd->h_ancestry_str, i); 2033 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 2034 "ap_name=%s", ap_name); 2035 2036 if (ddi_create_minor_node(dip, ap_name, S_IFCHR, instance, 2037 DDI_NT_USB_ATTACHMENT_POINT, 0) != DDI_SUCCESS) { 2038 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 2039 "cannot create attachment point node (%d)", 2040 instance); 2041 mutex_exit(HUBD_MUTEX(hubd)); 2042 2043 goto fail; 2044 } 2045 } 2046 2047 ports_count = hubd->h_hub_descr.bNbrPorts; 2048 mutex_exit(HUBD_MUTEX(hubd)); 2049 2050 /* create minor nodes */ 2051 if (ddi_create_minor_node(dip, "hubd", S_IFCHR, 2052 instance | minor, DDI_NT_NEXUS, 0) != DDI_SUCCESS) { 2053 2054 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 2055 "cannot create devctl minor node (%d)", instance); 2056 2057 goto fail; 2058 } 2059 2060 mutex_enter(HUBD_MUTEX(hubd)); 2061 hubd->h_init_state |= HUBD_MINOR_NODE_CREATED; 2062 mutex_exit(HUBD_MUTEX(hubd)); 2063 2064 if (ndi_prop_update_int(DDI_DEV_T_NONE, dip, 2065 "usb-port-count", ports_count) != DDI_PROP_SUCCESS) { 2066 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 2067 "usb-port-count update failed"); 2068 } 2069 2070 /* 2071 * host controller driver has already reported this dev 2072 * if we are the root hub 2073 */ 2074 if (!usba_is_root_hub(dip)) { 2075 ddi_report_dev(dip); 2076 } 2077 2078 /* enable deathrow thread */ 2079 hubd->h_cleanup_enabled = B_TRUE; 2080 mutex_enter(HUBD_MUTEX(hubd)); 2081 hubd_pm_idle_component(hubd, dip, 0); 2082 mutex_exit(HUBD_MUTEX(hubd)); 2083 2084 return (DDI_SUCCESS); 2085 2086 fail: 2087 { 2088 char *pathname = kmem_alloc(MAXPATHLEN, KM_SLEEP); 2089 2090 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle, 2091 "cannot attach %s", ddi_pathname(dip, pathname)); 2092 2093 kmem_free(pathname, MAXPATHLEN); 2094 } 2095 2096 mutex_enter(HUBD_MUTEX(hubd)); 2097 hubd_pm_idle_component(hubd, dip, 0); 2098 mutex_exit(HUBD_MUTEX(hubd)); 2099 2100 if (hubd) { 2101 rval = hubd_cleanup(dip, hubd); 2102 if (rval != USB_SUCCESS) { 2103 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle, 2104 "failure to complete cleanup after attach failure"); 2105 } 2106 } 2107 2108 return (DDI_FAILURE); 2109 } 2110 2111 2112 int 2113 usba_hubdi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 2114 { 2115 hubd_t *hubd = hubd_get_soft_state(dip); 2116 int rval; 2117 2118 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 2119 "hubd_detach: cmd=0x%x", cmd); 2120 2121 switch (cmd) { 2122 case DDI_DETACH: 2123 rval = hubd_cleanup(dip, hubd); 2124 2125 return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE); 2126 case DDI_SUSPEND: 2127 rval = hubd_cpr_suspend(hubd); 2128 2129 return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE); 2130 default: 2131 return (DDI_FAILURE); 2132 } 2133 } 2134 2135 2136 /* 2137 * hubd_setdevaddr 2138 * set the device addrs on this port 2139 */ 2140 static int 2141 hubd_setdevaddr(hubd_t *hubd, usb_port_t port) 2142 { 2143 int rval; 2144 usb_cr_t completion_reason; 2145 usb_cb_flags_t cb_flags; 2146 usb_pipe_handle_t ph; 2147 dev_info_t *child_dip = NULL; 2148 uchar_t address = 0; 2149 usba_device_t *usba_device; 2150 int retry = 0; 2151 long time_delay; 2152 2153 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 2154 "hubd_setdevaddr: port=%d", port); 2155 2156 ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 2157 2158 child_dip = hubd->h_children_dips[port]; 2159 address = hubd->h_usba_devices[port]->usb_addr; 2160 usba_device = hubd->h_usba_devices[port]; 2161 2162 /* close the default pipe with addr x */ 2163 mutex_exit(HUBD_MUTEX(hubd)); 2164 ph = usba_get_dflt_pipe_handle(child_dip); 2165 usb_pipe_close(child_dip, ph, 2166 USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL); 2167 mutex_enter(HUBD_MUTEX(hubd)); 2168 2169 /* 2170 * As this device has been reset, temporarily 2171 * assign the default address 2172 */ 2173 mutex_enter(&usba_device->usb_mutex); 2174 address = usba_device->usb_addr; 2175 usba_device->usb_addr = USBA_DEFAULT_ADDR; 2176 mutex_exit(&usba_device->usb_mutex); 2177 2178 mutex_exit(HUBD_MUTEX(hubd)); 2179 2180 time_delay = drv_usectohz(hubd_device_delay / 20); 2181 for (retry = 0; retry < hubd_retry_enumerate; retry++) { 2182 2183 /* open child's default pipe with USBA_DEFAULT_ADDR */ 2184 if (usb_pipe_open(child_dip, NULL, NULL, 2185 USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph) != 2186 USB_SUCCESS) { 2187 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 2188 "hubd_setdevaddr: Unable to open default pipe"); 2189 2190 break; 2191 } 2192 2193 /* Set the address of the device */ 2194 if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 2195 USB_DEV_REQ_HOST_TO_DEV, 2196 USB_REQ_SET_ADDRESS, /* bRequest */ 2197 address, /* wValue */ 2198 0, /* wIndex */ 2199 0, /* wLength */ 2200 NULL, 0, 2201 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 2202 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 2203 "hubd_setdevaddr(%d): rval=%d cr=%d cb_fl=0x%x", 2204 retry, rval, completion_reason, cb_flags); 2205 } 2206 2207 usb_pipe_close(child_dip, ph, 2208 USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL); 2209 2210 if (rval == USB_SUCCESS) { 2211 2212 break; 2213 } 2214 2215 delay(time_delay); 2216 } 2217 2218 /* Reset to the old address */ 2219 mutex_enter(&usba_device->usb_mutex); 2220 usba_device->usb_addr = address; 2221 mutex_exit(&usba_device->usb_mutex); 2222 mutex_enter(HUBD_MUTEX(hubd)); 2223 2224 usba_clear_data_toggle(usba_device); 2225 2226 return (rval); 2227 } 2228 2229 2230 /* 2231 * hubd_setdevconfig 2232 * set the device addrs on this port 2233 */ 2234 static void 2235 hubd_setdevconfig(hubd_t *hubd, usb_port_t port) 2236 { 2237 int rval; 2238 usb_cr_t completion_reason; 2239 usb_cb_flags_t cb_flags; 2240 usb_pipe_handle_t ph; 2241 dev_info_t *child_dip = NULL; 2242 usba_device_t *usba_device = NULL; 2243 uint16_t config_value; 2244 2245 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 2246 "hubd_setdevconfig: port=%d", port); 2247 2248 ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 2249 2250 child_dip = hubd->h_children_dips[port]; 2251 usba_device = hubd->h_usba_devices[port]; 2252 config_value = hubd->h_usba_devices[port]->usb_cfg_value; 2253 mutex_exit(HUBD_MUTEX(hubd)); 2254 2255 /* open the default control pipe */ 2256 if ((rval = usb_pipe_open(child_dip, NULL, NULL, 2257 USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) == 2258 USB_SUCCESS) { 2259 2260 /* Set the default configuration of the device */ 2261 if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 2262 USB_DEV_REQ_HOST_TO_DEV, 2263 USB_REQ_SET_CFG, /* bRequest */ 2264 config_value, /* wValue */ 2265 0, /* wIndex */ 2266 0, /* wLength */ 2267 NULL, 0, 2268 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 2269 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 2270 "hubd_setdevconfig: set device config failed: " 2271 "cr=%d cb_fl=0x%x rval=%d", 2272 completion_reason, cb_flags, rval); 2273 } 2274 /* 2275 * After setting the configuration, we make this default 2276 * control pipe persistent, so that it gets re-opened 2277 * on posting a connect event 2278 */ 2279 usba_persistent_pipe_close(usba_device); 2280 } else { 2281 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 2282 "pipe open fails: rval=%d", rval); 2283 } 2284 mutex_enter(HUBD_MUTEX(hubd)); 2285 } 2286 2287 2288 /*ARGSUSED*/ 2289 static int 2290 hubd_check_disconnected_ports(dev_info_t *dip, void *arg) 2291 { 2292 int circ; 2293 usb_port_t port; 2294 hubd_t *hubd; 2295 major_t hub_major = ddi_name_to_major("hubd"); 2296 major_t hwahc_major = ddi_name_to_major("hwahc"); 2297 major_t usbmid_major = ddi_name_to_major("usb_mid"); 2298 2299 /* 2300 * make sure dip is a usb hub, major of root hub is HCD 2301 * major 2302 */ 2303 if (!usba_is_root_hub(dip)) { 2304 if (ddi_driver_major(dip) == usbmid_major) { 2305 /* 2306 * need to walk the children since it might be a 2307 * HWA device 2308 */ 2309 2310 return (DDI_WALK_CONTINUE); 2311 } 2312 2313 /* TODO: DWA device may also need special handling */ 2314 2315 if (((ddi_driver_major(dip) != hub_major) && 2316 (ddi_driver_major(dip) != hwahc_major)) || 2317 !i_ddi_devi_attached(dip)) { 2318 2319 return (DDI_WALK_PRUNECHILD); 2320 } 2321 } 2322 2323 hubd = hubd_get_soft_state(dip); 2324 if (hubd == NULL) { 2325 2326 return (DDI_WALK_PRUNECHILD); 2327 } 2328 2329 /* walk child list and remove nodes with flag DEVI_DEVICE_REMOVED */ 2330 ndi_devi_enter(dip, &circ); 2331 2332 if (ddi_driver_major(dip) != hwahc_major) { 2333 /* for normal usb hub or root hub */ 2334 mutex_enter(HUBD_MUTEX(hubd)); 2335 for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { 2336 dev_info_t *cdip = hubd->h_children_dips[port]; 2337 2338 if (cdip == NULL || DEVI_IS_DEVICE_REMOVED(cdip) == 0) { 2339 2340 continue; 2341 } 2342 2343 (void) hubd_delete_child(hubd, port, NDI_DEVI_REMOVE, 2344 B_TRUE); 2345 } 2346 mutex_exit(HUBD_MUTEX(hubd)); 2347 } else { 2348 /* for HWA */ 2349 if (hubd->h_cleanup_child != NULL) { 2350 if (hubd->h_cleanup_child(dip) != USB_SUCCESS) { 2351 ndi_devi_exit(dip, circ); 2352 2353 return (DDI_WALK_PRUNECHILD); 2354 } 2355 } else { 2356 ndi_devi_exit(dip, circ); 2357 2358 return (DDI_WALK_PRUNECHILD); 2359 } 2360 } 2361 2362 ndi_devi_exit(dip, circ); 2363 2364 /* skip siblings of root hub */ 2365 if (usba_is_root_hub(dip)) { 2366 2367 return (DDI_WALK_PRUNESIB); 2368 } 2369 2370 return (DDI_WALK_CONTINUE); 2371 } 2372 2373 2374 /* 2375 * this thread will walk all children under the root hub for this 2376 * USB bus instance and attempt to remove them 2377 */ 2378 static void 2379 hubd_root_hub_cleanup_thread(void *arg) 2380 { 2381 int circ; 2382 hubd_t *root_hubd = (hubd_t *)arg; 2383 dev_info_t *rh_dip = root_hubd->h_dip; 2384 #ifndef __lock_lint 2385 callb_cpr_t cprinfo; 2386 2387 CALLB_CPR_INIT(&cprinfo, HUBD_MUTEX(root_hubd), callb_generic_cpr, 2388 "USB root hub"); 2389 #endif 2390 2391 for (;;) { 2392 /* don't race with detach */ 2393 ndi_hold_devi(rh_dip); 2394 2395 mutex_enter(HUBD_MUTEX(root_hubd)); 2396 root_hubd->h_cleanup_needed = 0; 2397 mutex_exit(HUBD_MUTEX(root_hubd)); 2398 2399 (void) devfs_clean(rh_dip, NULL, 0); 2400 2401 ndi_devi_enter(ddi_get_parent(rh_dip), &circ); 2402 ddi_walk_devs(rh_dip, hubd_check_disconnected_ports, 2403 NULL); 2404 #ifdef __lock_lint 2405 (void) hubd_check_disconnected_ports(rh_dip, NULL); 2406 #endif 2407 ndi_devi_exit(ddi_get_parent(rh_dip), circ); 2408 2409 /* quit if we are not enabled anymore */ 2410 mutex_enter(HUBD_MUTEX(root_hubd)); 2411 if ((root_hubd->h_cleanup_enabled == B_FALSE) || 2412 (root_hubd->h_cleanup_needed == B_FALSE)) { 2413 root_hubd->h_cleanup_active = B_FALSE; 2414 mutex_exit(HUBD_MUTEX(root_hubd)); 2415 ndi_rele_devi(rh_dip); 2416 2417 break; 2418 } 2419 mutex_exit(HUBD_MUTEX(root_hubd)); 2420 ndi_rele_devi(rh_dip); 2421 2422 #ifndef __lock_lint 2423 mutex_enter(HUBD_MUTEX(root_hubd)); 2424 CALLB_CPR_SAFE_BEGIN(&cprinfo); 2425 mutex_exit(HUBD_MUTEX(root_hubd)); 2426 2427 delay(drv_usectohz(hubd_dip_cleanup_delay)); 2428 2429 mutex_enter(HUBD_MUTEX(root_hubd)); 2430 CALLB_CPR_SAFE_END(&cprinfo, HUBD_MUTEX(root_hubd)); 2431 mutex_exit(HUBD_MUTEX(root_hubd)); 2432 #endif 2433 } 2434 2435 #ifndef __lock_lint 2436 mutex_enter(HUBD_MUTEX(root_hubd)); 2437 CALLB_CPR_EXIT(&cprinfo); 2438 #endif 2439 } 2440 2441 2442 void 2443 hubd_schedule_cleanup(dev_info_t *rh_dip) 2444 { 2445 hubd_t *root_hubd; 2446 2447 /* 2448 * The usb_root_hub_dip pointer for the child hub of the WUSB 2449 * wire adapter class device points to the wire adapter, not 2450 * the root hub. Need to find the real root hub dip so that 2451 * the cleanup thread only starts from the root hub. 2452 */ 2453 while (!usba_is_root_hub(rh_dip)) { 2454 root_hubd = hubd_get_soft_state(rh_dip); 2455 if (root_hubd != NULL) { 2456 rh_dip = root_hubd->h_usba_device->usb_root_hub_dip; 2457 if (rh_dip == NULL) { 2458 USB_DPRINTF_L2(DPRINT_MASK_ATTA, 2459 root_hubd->h_log_handle, 2460 "hubd_schedule_cleanup: null rh dip"); 2461 2462 return; 2463 } 2464 } else { 2465 USB_DPRINTF_L2(DPRINT_MASK_ATTA, 2466 root_hubd->h_log_handle, 2467 "hubd_schedule_cleanup: cannot find root hub"); 2468 2469 return; 2470 } 2471 } 2472 root_hubd = hubd_get_soft_state(rh_dip); 2473 2474 mutex_enter(HUBD_MUTEX(root_hubd)); 2475 root_hubd->h_cleanup_needed = B_TRUE; 2476 if (root_hubd->h_cleanup_enabled && !(root_hubd->h_cleanup_active)) { 2477 root_hubd->h_cleanup_active = B_TRUE; 2478 mutex_exit(HUBD_MUTEX(root_hubd)); 2479 (void) thread_create(NULL, 0, 2480 hubd_root_hub_cleanup_thread, 2481 (void *)root_hubd, 0, &p0, TS_RUN, 2482 minclsyspri); 2483 } else { 2484 mutex_exit(HUBD_MUTEX(root_hubd)); 2485 } 2486 } 2487 2488 2489 /* 2490 * hubd_restore_device_state: 2491 * - set config for the hub 2492 * - power cycle all the ports 2493 * - for each port that was connected 2494 * - reset port 2495 * - assign addrs to the device on this port 2496 * - restart polling 2497 * - reset suspend flag 2498 */ 2499 static void 2500 hubd_restore_device_state(dev_info_t *dip, hubd_t *hubd) 2501 { 2502 int rval; 2503 int retry; 2504 uint_t hub_prev_state; 2505 usb_port_t port; 2506 uint16_t status; 2507 uint16_t change; 2508 dev_info_t *ch_dip; 2509 boolean_t ehci_root_hub; 2510 2511 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 2512 "hubd_restore_device_state:"); 2513 2514 mutex_enter(HUBD_MUTEX(hubd)); 2515 hub_prev_state = hubd->h_dev_state; 2516 ASSERT(hub_prev_state != USB_DEV_PWRED_DOWN); 2517 2518 /* First bring the device to full power */ 2519 (void) hubd_pm_busy_component(hubd, dip, 0); 2520 mutex_exit(HUBD_MUTEX(hubd)); 2521 2522 (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); 2523 2524 if (!usba_is_root_hub(dip) && 2525 (usb_check_same_device(dip, hubd->h_log_handle, USB_LOG_L0, 2526 DPRINT_MASK_HOTPLUG, 2527 USB_CHK_BASIC|USB_CHK_CFG, NULL) != USB_SUCCESS)) { 2528 2529 /* change the device state to disconnected */ 2530 mutex_enter(HUBD_MUTEX(hubd)); 2531 hubd->h_dev_state = USB_DEV_DISCONNECTED; 2532 (void) hubd_pm_idle_component(hubd, dip, 0); 2533 mutex_exit(HUBD_MUTEX(hubd)); 2534 2535 return; 2536 } 2537 2538 ehci_root_hub = (strcmp(ddi_driver_name(dip), "ehci") == 0); 2539 2540 mutex_enter(HUBD_MUTEX(hubd)); 2541 /* First turn off all port power */ 2542 rval = hubd_disable_all_port_power(hubd); 2543 if (rval != USB_SUCCESS) { 2544 USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle, 2545 "hubd_restore_device_state:" 2546 "turning off port power failed"); 2547 } 2548 2549 /* Settling time before turning on again */ 2550 mutex_exit(HUBD_MUTEX(hubd)); 2551 delay(drv_usectohz(hubd_device_delay / 100)); 2552 mutex_enter(HUBD_MUTEX(hubd)); 2553 2554 /* enable power on all ports so we can see connects */ 2555 if (hubd_enable_all_port_power(hubd) != USB_SUCCESS) { 2556 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 2557 "hubd_restore_device_state: turn on port power failed"); 2558 2559 /* disable whatever was enabled */ 2560 (void) hubd_disable_all_port_power(hubd); 2561 2562 (void) hubd_pm_idle_component(hubd, dip, 0); 2563 mutex_exit(HUBD_MUTEX(hubd)); 2564 2565 return; 2566 } 2567 2568 /* 2569 * wait at least 3 frames before accessing devices 2570 * (note that delay's minimal time is one clock tick which 2571 * is 10ms unless hires_tick has been changed) 2572 */ 2573 mutex_exit(HUBD_MUTEX(hubd)); 2574 delay(drv_usectohz(10000)); 2575 mutex_enter(HUBD_MUTEX(hubd)); 2576 2577 hubd->h_dev_state = USB_DEV_HUB_STATE_RECOVER; 2578 2579 for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { 2580 USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle, 2581 "hubd_restore_device_state: port=%d", port); 2582 2583 /* 2584 * the childen_dips list may have dips that have been 2585 * already deallocated. we only get a post_detach notification 2586 * but not a destroy notification 2587 */ 2588 ch_dip = hubd->h_children_dips[port]; 2589 if (ch_dip) { 2590 /* get port status */ 2591 (void) hubd_determine_port_status(hubd, port, 2592 &status, &change, PORT_CHANGE_CSC); 2593 2594 /* check if it is truly connected */ 2595 if (status & PORT_STATUS_CCS) { 2596 /* 2597 * Now reset port and assign the device 2598 * its original address 2599 */ 2600 retry = 0; 2601 do { 2602 (void) hubd_reset_port(hubd, port); 2603 2604 /* required for ppx */ 2605 (void) hubd_enable_port(hubd, port); 2606 2607 if (retry) { 2608 mutex_exit(HUBD_MUTEX(hubd)); 2609 delay(drv_usectohz( 2610 hubd_device_delay/2)); 2611 mutex_enter(HUBD_MUTEX(hubd)); 2612 } 2613 2614 rval = hubd_setdevaddr(hubd, port); 2615 retry++; 2616 } while ((rval != USB_SUCCESS) && 2617 (retry < hubd_retry_enumerate)); 2618 2619 hubd_setdevconfig(hubd, port); 2620 2621 if (hub_prev_state == USB_DEV_DISCONNECTED) { 2622 /* post a connect event */ 2623 mutex_exit(HUBD_MUTEX(hubd)); 2624 hubd_post_event(hubd, port, 2625 USBA_EVENT_TAG_HOT_INSERTION); 2626 mutex_enter(HUBD_MUTEX(hubd)); 2627 } else { 2628 /* 2629 * Since we have this device connected 2630 * mark it reinserted to prevent 2631 * cleanup thread from stepping in. 2632 */ 2633 mutex_exit(HUBD_MUTEX(hubd)); 2634 mutex_enter(&(DEVI(ch_dip)->devi_lock)); 2635 DEVI_SET_DEVICE_REINSERTED(ch_dip); 2636 mutex_exit(&(DEVI(ch_dip)->devi_lock)); 2637 2638 /* 2639 * reopen pipes for children for 2640 * their DDI_RESUME 2641 */ 2642 rval = usba_persistent_pipe_open( 2643 usba_get_usba_device(ch_dip)); 2644 mutex_enter(HUBD_MUTEX(hubd)); 2645 ASSERT(rval == USB_SUCCESS); 2646 } 2647 } else { 2648 /* 2649 * Mark this dip for deletion as the device 2650 * is not physically present, and schedule 2651 * cleanup thread upon post resume 2652 */ 2653 mutex_exit(HUBD_MUTEX(hubd)); 2654 2655 USB_DPRINTF_L2(DPRINT_MASK_ATTA, 2656 hubd->h_log_handle, 2657 "hubd_restore_device_state: " 2658 "dip=%p on port=%d marked for cleanup", 2659 (void *)ch_dip, port); 2660 mutex_enter(&(DEVI(ch_dip)->devi_lock)); 2661 DEVI_SET_DEVICE_REMOVED(ch_dip); 2662 mutex_exit(&(DEVI(ch_dip)->devi_lock)); 2663 2664 mutex_enter(HUBD_MUTEX(hubd)); 2665 } 2666 } else if (ehci_root_hub) { 2667 /* get port status */ 2668 (void) hubd_determine_port_status(hubd, port, 2669 &status, &change, PORT_CHANGE_CSC); 2670 2671 /* check if it is truly connected */ 2672 if (status & PORT_STATUS_CCS) { 2673 /* 2674 * reset the port to find out if we have 2675 * 2.0 device connected or 1.X. A 2.0 2676 * device will still be seen as connected, 2677 * while a 1.X device will switch over to 2678 * the companion controller. 2679 */ 2680 (void) hubd_reset_port(hubd, port); 2681 2682 (void) hubd_determine_port_status(hubd, port, 2683 &status, &change, PORT_CHANGE_CSC); 2684 2685 if (status & 2686 (PORT_STATUS_CCS | PORT_STATUS_HSDA)) { 2687 /* 2688 * We have a USB 2.0 device 2689 * connected. Power cycle this port 2690 * so that hotplug thread can 2691 * enumerate this device. 2692 */ 2693 (void) hubd_toggle_port(hubd, port); 2694 } else { 2695 USB_DPRINTF_L2(DPRINT_MASK_ATTA, 2696 hubd->h_log_handle, 2697 "hubd_restore_device_state: " 2698 "device on port %d switched over", 2699 port); 2700 } 2701 } 2702 2703 } 2704 } 2705 2706 2707 /* if the device had remote wakeup earlier, enable it again */ 2708 if (hubd->h_hubpm->hubp_wakeup_enabled) { 2709 mutex_exit(HUBD_MUTEX(hubd)); 2710 (void) usb_handle_remote_wakeup(hubd->h_dip, 2711 USB_REMOTE_WAKEUP_ENABLE); 2712 mutex_enter(HUBD_MUTEX(hubd)); 2713 } 2714 2715 hubd->h_dev_state = USB_DEV_ONLINE; 2716 hubd_start_polling(hubd, 0); 2717 (void) hubd_pm_idle_component(hubd, dip, 0); 2718 mutex_exit(HUBD_MUTEX(hubd)); 2719 } 2720 2721 2722 /* 2723 * hubd_cleanup: 2724 * cleanup hubd and deallocate. this function is called for 2725 * handling attach failures and detaching including dynamic 2726 * reconfiguration. If called from attaching, it must clean 2727 * up the whole thing and return success. 2728 */ 2729 /*ARGSUSED*/ 2730 static int 2731 hubd_cleanup(dev_info_t *dip, hubd_t *hubd) 2732 { 2733 int circ, rval, old_dev_state; 2734 hub_power_t *hubpm; 2735 #ifdef DEBUG 2736 usb_port_t port; 2737 #endif 2738 2739 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 2740 "hubd_cleanup:"); 2741 2742 if ((hubd->h_init_state & HUBD_LOCKS_DONE) == 0) { 2743 goto done; 2744 } 2745 2746 /* ensure we are the only one active */ 2747 ndi_devi_enter(dip, &circ); 2748 2749 mutex_enter(HUBD_MUTEX(hubd)); 2750 2751 /* Cleanup failure is only allowed if called from detach */ 2752 if (DEVI_IS_DETACHING(dip)) { 2753 dev_info_t *rh_dip = hubd->h_usba_device->usb_root_hub_dip; 2754 2755 /* 2756 * We are being called from detach. 2757 * Fail immediately if the hotplug thread is running 2758 * else set the dev_state to disconnected so that 2759 * hotplug thread just exits without doing anything. 2760 */ 2761 if (hubd->h_bus_ctls || hubd->h_bus_pwr || 2762 hubd->h_hotplug_thread) { 2763 mutex_exit(HUBD_MUTEX(hubd)); 2764 ndi_devi_exit(dip, circ); 2765 2766 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 2767 "hubd_cleanup: hotplug thread/bus ctl active " 2768 "- failing detach"); 2769 2770 return (USB_FAILURE); 2771 } 2772 2773 /* 2774 * if the deathrow thread is still active or about 2775 * to become active, fail detach 2776 * the roothup can only be detached if nexus drivers 2777 * are unloaded or explicitly offlined 2778 */ 2779 if (rh_dip == dip) { 2780 if (hubd->h_cleanup_needed || 2781 hubd->h_cleanup_active) { 2782 mutex_exit(HUBD_MUTEX(hubd)); 2783 ndi_devi_exit(dip, circ); 2784 2785 USB_DPRINTF_L2(DPRINT_MASK_ATTA, 2786 hubd->h_log_handle, 2787 "hubd_cleanup: deathrow still active?" 2788 "- failing detach"); 2789 2790 return (USB_FAILURE); 2791 } 2792 } 2793 } 2794 2795 old_dev_state = hubd->h_dev_state; 2796 hubd->h_dev_state = USB_DEV_DISCONNECTED; 2797 2798 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 2799 "hubd_cleanup: stop polling"); 2800 hubd_close_intr_pipe(hubd); 2801 2802 ASSERT((hubd->h_bus_ctls || hubd->h_bus_pwr || 2803 hubd->h_hotplug_thread) == 0); 2804 mutex_exit(HUBD_MUTEX(hubd)); 2805 2806 /* 2807 * deallocate events, if events are still registered 2808 * (ie. children still attached) then we have to fail the detach 2809 */ 2810 if (hubd->h_ndi_event_hdl) { 2811 2812 rval = ndi_event_free_hdl(hubd->h_ndi_event_hdl); 2813 if (DEVI_IS_ATTACHING(dip)) { 2814 2815 /* It must return success if attaching. */ 2816 ASSERT(rval == NDI_SUCCESS); 2817 2818 } else if (rval != NDI_SUCCESS) { 2819 2820 USB_DPRINTF_L2(DPRINT_MASK_ALL, hubd->h_log_handle, 2821 "hubd_cleanup: ndi_event_free_hdl failed"); 2822 ndi_devi_exit(dip, circ); 2823 2824 return (USB_FAILURE); 2825 2826 } 2827 } 2828 2829 mutex_enter(HUBD_MUTEX(hubd)); 2830 2831 if (hubd->h_init_state & HUBD_CHILDREN_CREATED) { 2832 #ifdef DEBUG 2833 for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { 2834 ASSERT(hubd->h_usba_devices[port] == NULL); 2835 ASSERT(hubd->h_children_dips[port] == NULL); 2836 } 2837 #endif 2838 kmem_free(hubd->h_children_dips, hubd->h_cd_list_length); 2839 kmem_free(hubd->h_usba_devices, hubd->h_cd_list_length); 2840 } 2841 2842 /* 2843 * Disable the event callbacks first, after this point, event 2844 * callbacks will never get called. Note we shouldn't hold 2845 * mutex while unregistering events because there may be a 2846 * competing event callback thread. Event callbacks are done 2847 * with ndi mutex held and this can cause a potential deadlock. 2848 * Note that cleanup can't fail after deregistration of events. 2849 */ 2850 if (hubd->h_init_state & HUBD_EVENTS_REGISTERED) { 2851 mutex_exit(HUBD_MUTEX(hubd)); 2852 usb_unregister_event_cbs(dip, &hubd_events); 2853 hubd_unregister_cpr_callback(hubd); 2854 mutex_enter(HUBD_MUTEX(hubd)); 2855 } 2856 2857 /* restore the old dev state so that device can be put into low power */ 2858 hubd->h_dev_state = old_dev_state; 2859 hubpm = hubd->h_hubpm; 2860 2861 if ((hubpm) && (hubd->h_dev_state != USB_DEV_DISCONNECTED)) { 2862 (void) hubd_pm_busy_component(hubd, dip, 0); 2863 mutex_exit(HUBD_MUTEX(hubd)); 2864 if (hubd->h_hubpm->hubp_wakeup_enabled) { 2865 /* 2866 * Bring the hub to full power before 2867 * issuing the disable remote wakeup command 2868 */ 2869 (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); 2870 2871 if ((rval = usb_handle_remote_wakeup(hubd->h_dip, 2872 USB_REMOTE_WAKEUP_DISABLE)) != USB_SUCCESS) { 2873 USB_DPRINTF_L2(DPRINT_MASK_PM, 2874 hubd->h_log_handle, 2875 "hubd_cleanup: disable remote wakeup " 2876 "fails=%d", rval); 2877 } 2878 } 2879 2880 (void) pm_lower_power(hubd->h_dip, 0, USB_DEV_OS_PWR_OFF); 2881 2882 mutex_enter(HUBD_MUTEX(hubd)); 2883 (void) hubd_pm_idle_component(hubd, dip, 0); 2884 } 2885 2886 if (hubpm) { 2887 if (hubpm->hubp_child_pwrstate) { 2888 kmem_free(hubpm->hubp_child_pwrstate, 2889 MAX_PORTS + 1); 2890 } 2891 kmem_free(hubpm, sizeof (hub_power_t)); 2892 } 2893 mutex_exit(HUBD_MUTEX(hubd)); 2894 2895 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 2896 "hubd_cleanup: freeing space"); 2897 2898 if (hubd->h_init_state & HUBD_HUBDI_REGISTERED) { 2899 rval = usba_hubdi_unregister(dip); 2900 ASSERT(rval == USB_SUCCESS); 2901 } 2902 2903 if (hubd->h_init_state & HUBD_LOCKS_DONE) { 2904 mutex_destroy(HUBD_MUTEX(hubd)); 2905 cv_destroy(&hubd->h_cv_reset_port); 2906 cv_destroy(&hubd->h_cv_hotplug_dev); 2907 } 2908 2909 ndi_devi_exit(dip, circ); 2910 2911 if (hubd->h_init_state & HUBD_MINOR_NODE_CREATED) { 2912 ddi_remove_minor_node(dip, NULL); 2913 } 2914 2915 if (usba_is_root_hub(dip)) { 2916 usb_pipe_close(dip, hubd->h_default_pipe, 2917 USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL); 2918 } 2919 2920 done: 2921 if (hubd->h_ancestry_str) { 2922 kmem_free(hubd->h_ancestry_str, HUBD_APID_NAMELEN); 2923 } 2924 2925 usb_client_detach(dip, hubd->h_dev_data); 2926 2927 usb_free_log_hdl(hubd->h_log_handle); 2928 2929 if (!usba_is_root_hub(dip)) { 2930 ddi_soft_state_free(hubd_statep, ddi_get_instance(dip)); 2931 } 2932 2933 ddi_prop_remove_all(dip); 2934 2935 return (USB_SUCCESS); 2936 } 2937 2938 2939 /* 2940 * hubd_determine_port_connection: 2941 * Determine which port is in connect status but does not 2942 * have connect status change bit set, and mark port change 2943 * bit accordingly. 2944 * This function is applied during hub attach time. 2945 */ 2946 static usb_port_mask_t 2947 hubd_determine_port_connection(hubd_t *hubd) 2948 { 2949 usb_port_t port; 2950 usb_hub_descr_t *hub_descr; 2951 uint16_t status; 2952 uint16_t change; 2953 usb_port_mask_t port_change = 0; 2954 2955 ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 2956 2957 hub_descr = &hubd->h_hub_descr; 2958 2959 for (port = 1; port <= hub_descr->bNbrPorts; port++) { 2960 2961 (void) hubd_determine_port_status(hubd, port, &status, 2962 &change, 0); 2963 2964 /* Check if port is in connect status */ 2965 if (!(status & PORT_STATUS_CCS)) { 2966 2967 continue; 2968 } 2969 2970 /* 2971 * Check if port Connect Status Change bit has been set. 2972 * If already set, the connection will be handled by 2973 * intr polling callback, not during attach. 2974 */ 2975 if (change & PORT_CHANGE_CSC) { 2976 2977 continue; 2978 } 2979 2980 port_change |= 1 << port; 2981 } 2982 2983 return (port_change); 2984 } 2985 2986 2987 /* 2988 * hubd_check_ports: 2989 * - get hub descriptor 2990 * - check initial port status 2991 * - enable power on all ports 2992 * - enable polling on ep1 2993 */ 2994 static int 2995 hubd_check_ports(hubd_t *hubd) 2996 { 2997 int rval; 2998 usb_port_mask_t port_change = 0; 2999 hubd_hotplug_arg_t *arg; 3000 3001 ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 3002 3003 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 3004 "hubd_check_ports: addr=0x%x", usb_get_addr(hubd->h_dip)); 3005 3006 /* 3007 * First turn off all port power 3008 */ 3009 if ((rval = hubd_disable_all_port_power(hubd)) != USB_SUCCESS) { 3010 3011 /* disable whatever was enabled */ 3012 (void) hubd_disable_all_port_power(hubd); 3013 3014 return (rval); 3015 } 3016 3017 /* 3018 * do not switch on immediately (instantly on root hub) 3019 * and allow time to settle 3020 */ 3021 mutex_exit(HUBD_MUTEX(hubd)); 3022 delay(drv_usectohz(10000)); 3023 mutex_enter(HUBD_MUTEX(hubd)); 3024 3025 /* 3026 * enable power on all ports so we can see connects 3027 */ 3028 if ((rval = hubd_enable_all_port_power(hubd)) != USB_SUCCESS) { 3029 /* disable whatever was enabled */ 3030 (void) hubd_disable_all_port_power(hubd); 3031 3032 return (rval); 3033 } 3034 3035 /* wait at least 3 frames before accessing devices */ 3036 mutex_exit(HUBD_MUTEX(hubd)); 3037 delay(drv_usectohz(10000)); 3038 mutex_enter(HUBD_MUTEX(hubd)); 3039 3040 /* 3041 * allocate arrays for saving the dips of each child per port 3042 * 3043 * ports go from 1 - n, allocate 1 more entry 3044 */ 3045 hubd->h_cd_list_length = 3046 (sizeof (dev_info_t **)) * (hubd->h_hub_descr.bNbrPorts + 1); 3047 3048 hubd->h_children_dips = (dev_info_t **)kmem_zalloc( 3049 hubd->h_cd_list_length, KM_SLEEP); 3050 hubd->h_usba_devices = (usba_device_t **)kmem_zalloc( 3051 hubd->h_cd_list_length, KM_SLEEP); 3052 3053 hubd->h_init_state |= HUBD_CHILDREN_CREATED; 3054 3055 mutex_exit(HUBD_MUTEX(hubd)); 3056 arg = (hubd_hotplug_arg_t *)kmem_zalloc( 3057 sizeof (hubd_hotplug_arg_t), KM_SLEEP); 3058 mutex_enter(HUBD_MUTEX(hubd)); 3059 3060 if ((rval = hubd_open_intr_pipe(hubd)) != USB_SUCCESS) { 3061 kmem_free(arg, sizeof (hubd_hotplug_arg_t)); 3062 3063 return (rval); 3064 } 3065 3066 hubd_start_polling(hubd, 0); 3067 3068 /* 3069 * Some hub devices, like the embedded hub in the CKS ErgoMagic 3070 * keyboard, may only have connection status bit set, but not 3071 * have connect status change bit set when a device has been 3072 * connected to its downstream port before the hub is enumerated. 3073 * Then when the hub is in enumeration, the devices connected to 3074 * it cannot be detected by the intr pipe and won't be enumerated. 3075 * We need to check such situation here and enumerate the downstream 3076 * devices for such hubs. 3077 */ 3078 port_change = hubd_determine_port_connection(hubd); 3079 3080 if (port_change) { 3081 hubd_pm_busy_component(hubd, hubd->h_dip, 0); 3082 3083 arg->hubd = hubd; 3084 arg->hotplug_during_attach = B_TRUE; 3085 hubd->h_port_change |= port_change; 3086 3087 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 3088 "hubd_check_ports: port change=0x%x, need to connect", 3089 hubd->h_port_change); 3090 3091 if (usb_async_req(hubd->h_dip, hubd_hotplug_thread, 3092 (void *)arg, 0) == USB_SUCCESS) { 3093 hubd->h_hotplug_thread++; 3094 } else { 3095 /* mark this device as idle */ 3096 hubd_pm_idle_component(hubd, hubd->h_dip, 0); 3097 kmem_free(arg, sizeof (hubd_hotplug_arg_t)); 3098 } 3099 } else { 3100 kmem_free(arg, sizeof (hubd_hotplug_arg_t)); 3101 } 3102 3103 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 3104 "hubd_check_ports done"); 3105 3106 return (USB_SUCCESS); 3107 } 3108 3109 3110 /* 3111 * hubd_get_hub_descriptor: 3112 */ 3113 static int 3114 hubd_get_hub_descriptor(hubd_t *hubd) 3115 { 3116 usb_hub_descr_t *hub_descr = &hubd->h_hub_descr; 3117 mblk_t *data = NULL; 3118 usb_cr_t completion_reason; 3119 usb_cb_flags_t cb_flags; 3120 uint16_t length; 3121 int rval; 3122 3123 USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 3124 "hubd_get_hub_descriptor:"); 3125 3126 ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 3127 ASSERT(hubd->h_default_pipe != 0); 3128 3129 /* get hub descriptor length first by requesting 8 bytes only */ 3130 mutex_exit(HUBD_MUTEX(hubd)); 3131 3132 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 3133 hubd->h_default_pipe, 3134 HUB_CLASS_REQ_TYPE, 3135 USB_REQ_GET_DESCR, /* bRequest */ 3136 USB_DESCR_TYPE_SETUP_HUB, /* wValue */ 3137 0, /* wIndex */ 3138 8, /* wLength */ 3139 &data, 0, 3140 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 3141 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 3142 "get hub descriptor failed: cr=%d cb_fl=0x%x rval=%d", 3143 completion_reason, cb_flags, rval); 3144 freemsg(data); 3145 mutex_enter(HUBD_MUTEX(hubd)); 3146 3147 return (rval); 3148 } 3149 3150 length = *(data->b_rptr); 3151 3152 if (length > 8) { 3153 freemsg(data); 3154 data = NULL; 3155 3156 /* get complete hub descriptor */ 3157 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 3158 hubd->h_default_pipe, 3159 HUB_CLASS_REQ_TYPE, 3160 USB_REQ_GET_DESCR, /* bRequest */ 3161 USB_DESCR_TYPE_SETUP_HUB, /* wValue */ 3162 0, /* wIndex */ 3163 length, /* wLength */ 3164 &data, 0, 3165 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 3166 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 3167 "get hub descriptor failed: " 3168 "cr=%d cb_fl=0x%x rval=%d", 3169 completion_reason, cb_flags, rval); 3170 freemsg(data); 3171 mutex_enter(HUBD_MUTEX(hubd)); 3172 3173 return (rval); 3174 } 3175 } 3176 3177 mutex_enter(HUBD_MUTEX(hubd)); 3178 3179 /* parse the hub descriptor */ 3180 /* only 32 ports are supported at present */ 3181 ASSERT(*(data->b_rptr + 2) <= 32); 3182 if (usb_parse_CV_descr("cccscccccc", 3183 data->b_rptr, MBLKL(data), 3184 (void *)hub_descr, sizeof (usb_hub_descr_t)) == 0) { 3185 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 3186 "parsing hub descriptor failed"); 3187 3188 freemsg(data); 3189 3190 return (USB_FAILURE); 3191 } 3192 3193 freemsg(data); 3194 3195 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 3196 "rval=0x%x bNbrPorts=0x%x wHubChars=0x%x " 3197 "PwrOn2PwrGood=0x%x HubContrCurrent=%dmA", rval, 3198 hub_descr->bNbrPorts, hub_descr->wHubCharacteristics, 3199 hub_descr->bPwrOn2PwrGood, hub_descr->bHubContrCurrent); 3200 3201 if (hub_descr->bNbrPorts > MAX_PORTS) { 3202 USB_DPRINTF_L0(DPRINT_MASK_ATTA, hubd->h_log_handle, 3203 "Hub driver supports max of %d ports on hub. " 3204 "Hence using the first %d port of %d ports available", 3205 MAX_PORTS, MAX_PORTS, hub_descr->bNbrPorts); 3206 3207 hub_descr->bNbrPorts = MAX_PORTS; 3208 } 3209 3210 return (USB_SUCCESS); 3211 } 3212 3213 3214 /* 3215 * hubd_get_hub_status_words: 3216 */ 3217 static int 3218 hubd_get_hub_status_words(hubd_t *hubd, uint16_t *status) 3219 { 3220 usb_cr_t completion_reason; 3221 usb_cb_flags_t cb_flags; 3222 mblk_t *data = NULL; 3223 3224 ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 3225 3226 mutex_exit(HUBD_MUTEX(hubd)); 3227 3228 if (usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe, 3229 HUB_CLASS_REQ_TYPE, 3230 USB_REQ_GET_STATUS, 3231 0, 3232 0, 3233 GET_STATUS_LENGTH, 3234 &data, 0, 3235 &completion_reason, &cb_flags, 0) != USB_SUCCESS) { 3236 USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle, 3237 "get hub status failed: cr=%d cb=0x%x", 3238 completion_reason, cb_flags); 3239 3240 if (data) { 3241 freemsg(data); 3242 } 3243 3244 mutex_enter(HUBD_MUTEX(hubd)); 3245 3246 return (USB_FAILURE); 3247 } 3248 3249 mutex_enter(HUBD_MUTEX(hubd)); 3250 3251 status[0] = (*(data->b_rptr + 1) << 8) | *(data->b_rptr); 3252 status[1] = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2); 3253 3254 USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle, 3255 "hub status=0x%x change=0x%x", status[0], status[1]); 3256 3257 freemsg(data); 3258 3259 return (USB_SUCCESS); 3260 } 3261 3262 3263 /* 3264 * hubd_open_intr_pipe: 3265 * we read all descriptors first for curiosity and then simply 3266 * open the pipe 3267 */ 3268 static int 3269 hubd_open_intr_pipe(hubd_t *hubd) 3270 { 3271 int rval; 3272 3273 USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 3274 "hubd_open_intr_pipe:"); 3275 3276 ASSERT(hubd->h_intr_pipe_state == HUBD_INTR_PIPE_IDLE); 3277 3278 hubd->h_intr_pipe_state = HUBD_INTR_PIPE_OPENING; 3279 mutex_exit(HUBD_MUTEX(hubd)); 3280 3281 if ((rval = usb_pipe_open(hubd->h_dip, 3282 &hubd->h_ep1_descr, &hubd->h_pipe_policy, 3283 0, &hubd->h_ep1_ph)) != USB_SUCCESS) { 3284 USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle, 3285 "open intr pipe failed (%d)", rval); 3286 3287 mutex_enter(HUBD_MUTEX(hubd)); 3288 hubd->h_intr_pipe_state = HUBD_INTR_PIPE_IDLE; 3289 3290 return (rval); 3291 } 3292 3293 mutex_enter(HUBD_MUTEX(hubd)); 3294 hubd->h_intr_pipe_state = HUBD_INTR_PIPE_ACTIVE; 3295 3296 USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 3297 "open intr pipe succeeded, ph=0x%p", (void *)hubd->h_ep1_ph); 3298 3299 return (USB_SUCCESS); 3300 } 3301 3302 3303 /* 3304 * hubd_start_polling: 3305 * start or restart the polling 3306 */ 3307 static void 3308 hubd_start_polling(hubd_t *hubd, int always) 3309 { 3310 usb_intr_req_t *reqp; 3311 int rval; 3312 usb_pipe_state_t pipe_state; 3313 3314 USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 3315 "start polling: always=%d dev_state=%d pipe_state=%d\n\t" 3316 "thread=%d ep1_ph=0x%p", 3317 always, hubd->h_dev_state, hubd->h_intr_pipe_state, 3318 hubd->h_hotplug_thread, (void *)hubd->h_ep1_ph); 3319 3320 /* 3321 * start or restart polling on the intr pipe 3322 * only if hotplug thread is not running 3323 */ 3324 if ((always == HUBD_ALWAYS_START_POLLING) || 3325 ((hubd->h_dev_state == USB_DEV_ONLINE) && 3326 (hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE) && 3327 (hubd->h_hotplug_thread == 0) && hubd->h_ep1_ph)) { 3328 USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 3329 "start polling requested"); 3330 3331 reqp = usb_alloc_intr_req(hubd->h_dip, 0, USB_FLAGS_SLEEP); 3332 3333 reqp->intr_client_private = (usb_opaque_t)hubd; 3334 reqp->intr_attributes = USB_ATTRS_SHORT_XFER_OK | 3335 USB_ATTRS_AUTOCLEARING; 3336 reqp->intr_len = hubd->h_ep1_descr.wMaxPacketSize; 3337 reqp->intr_cb = hubd_read_cb; 3338 reqp->intr_exc_cb = hubd_exception_cb; 3339 mutex_exit(HUBD_MUTEX(hubd)); 3340 if ((rval = usb_pipe_intr_xfer(hubd->h_ep1_ph, reqp, 3341 USB_FLAGS_SLEEP)) != USB_SUCCESS) { 3342 USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle, 3343 "start polling failed, rval=%d", rval); 3344 usb_free_intr_req(reqp); 3345 } 3346 3347 rval = usb_pipe_get_state(hubd->h_ep1_ph, &pipe_state, 3348 USB_FLAGS_SLEEP); 3349 if (pipe_state != USB_PIPE_STATE_ACTIVE) { 3350 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 3351 "intr pipe state=%d, rval=%d", pipe_state, rval); 3352 } 3353 USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 3354 "start polling request 0x%p", (void *)reqp); 3355 3356 mutex_enter(HUBD_MUTEX(hubd)); 3357 } 3358 } 3359 3360 3361 /* 3362 * hubd_stop_polling 3363 * stop polling but do not close the pipe 3364 */ 3365 static void 3366 hubd_stop_polling(hubd_t *hubd) 3367 { 3368 int rval; 3369 usb_pipe_state_t pipe_state; 3370 3371 if (hubd->h_ep1_ph) { 3372 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 3373 "hubd_stop_polling:"); 3374 hubd->h_intr_pipe_state = HUBD_INTR_PIPE_STOPPED; 3375 mutex_exit(HUBD_MUTEX(hubd)); 3376 3377 usb_pipe_stop_intr_polling(hubd->h_ep1_ph, USB_FLAGS_SLEEP); 3378 rval = usb_pipe_get_state(hubd->h_ep1_ph, &pipe_state, 3379 USB_FLAGS_SLEEP); 3380 3381 if (pipe_state != USB_PIPE_STATE_IDLE) { 3382 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 3383 "intr pipe state=%d, rval=%d", pipe_state, rval); 3384 } 3385 mutex_enter(HUBD_MUTEX(hubd)); 3386 if (hubd->h_intr_pipe_state == HUBD_INTR_PIPE_STOPPED) { 3387 hubd->h_intr_pipe_state = HUBD_INTR_PIPE_ACTIVE; 3388 } 3389 } 3390 } 3391 3392 3393 /* 3394 * hubd_close_intr_pipe: 3395 * close the pipe (which also stops the polling 3396 * and wait for the hotplug thread to exit 3397 */ 3398 static void 3399 hubd_close_intr_pipe(hubd_t *hubd) 3400 { 3401 USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 3402 "hubd_close_intr_pipe:"); 3403 3404 /* 3405 * Now that no async operation is outstanding on pipe, 3406 * we can change the state to HUBD_INTR_PIPE_CLOSING 3407 */ 3408 hubd->h_intr_pipe_state = HUBD_INTR_PIPE_CLOSING; 3409 3410 ASSERT(hubd->h_hotplug_thread == 0); 3411 3412 if (hubd->h_ep1_ph) { 3413 mutex_exit(HUBD_MUTEX(hubd)); 3414 usb_pipe_close(hubd->h_dip, hubd->h_ep1_ph, USB_FLAGS_SLEEP, 3415 NULL, NULL); 3416 mutex_enter(HUBD_MUTEX(hubd)); 3417 hubd->h_ep1_ph = NULL; 3418 } 3419 3420 hubd->h_intr_pipe_state = HUBD_INTR_PIPE_IDLE; 3421 } 3422 3423 3424 /* 3425 * hubd_exception_cb 3426 * interrupt ep1 exception callback function. 3427 * this callback executes in taskq thread context and assumes 3428 * autoclearing 3429 */ 3430 /*ARGSUSED*/ 3431 static void 3432 hubd_exception_cb(usb_pipe_handle_t pipe, usb_intr_req_t *reqp) 3433 { 3434 hubd_t *hubd = (hubd_t *)(reqp->intr_client_private); 3435 3436 USB_DPRINTF_L2(DPRINT_MASK_CALLBACK, hubd->h_log_handle, 3437 "hubd_exception_cb: " 3438 "req=0x%p cr=%d data=0x%p cb_flags=0x%x", (void *)reqp, 3439 reqp->intr_completion_reason, (void *)reqp->intr_data, 3440 reqp->intr_cb_flags); 3441 3442 ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0); 3443 3444 mutex_enter(HUBD_MUTEX(hubd)); 3445 (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0); 3446 3447 switch (reqp->intr_completion_reason) { 3448 case USB_CR_PIPE_RESET: 3449 /* only restart polling after autoclearing */ 3450 if ((hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE) && 3451 (hubd->h_port_reset_wait == 0)) { 3452 hubd_start_polling(hubd, 0); 3453 } 3454 3455 break; 3456 case USB_CR_DEV_NOT_RESP: 3457 case USB_CR_STOPPED_POLLING: 3458 case USB_CR_PIPE_CLOSING: 3459 case USB_CR_UNSPECIFIED_ERR: 3460 /* never restart polling on these conditions */ 3461 default: 3462 /* for all others, wait for the autoclearing PIPE_RESET cb */ 3463 3464 break; 3465 } 3466 3467 usb_free_intr_req(reqp); 3468 (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0); 3469 mutex_exit(HUBD_MUTEX(hubd)); 3470 } 3471 3472 3473 /* 3474 * helper function to convert LE bytes to a portmask 3475 */ 3476 static usb_port_mask_t 3477 hubd_mblk2portmask(mblk_t *data) 3478 { 3479 int len = min(MBLKL(data), sizeof (usb_port_mask_t)); 3480 usb_port_mask_t rval = 0; 3481 int i; 3482 3483 for (i = 0; i < len; i++) { 3484 rval |= data->b_rptr[i] << (i * 8); 3485 } 3486 3487 return (rval); 3488 } 3489 3490 3491 /* 3492 * hubd_read_cb: 3493 * interrupt ep1 callback function 3494 * 3495 * the status indicates just a change on the pipe with no indication 3496 * of what the change was 3497 * 3498 * known conditions: 3499 * - reset port completion 3500 * - connect 3501 * - disconnect 3502 * 3503 * for handling the hotplugging, create a new thread that can do 3504 * synchronous usba calls 3505 */ 3506 static void 3507 hubd_read_cb(usb_pipe_handle_t pipe, usb_intr_req_t *reqp) 3508 { 3509 hubd_t *hubd = (hubd_t *)(reqp->intr_client_private); 3510 size_t length; 3511 mblk_t *data = reqp->intr_data; 3512 int mem_flag = 0; 3513 hubd_hotplug_arg_t *arg; 3514 3515 USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 3516 "hubd_read_cb: ph=0x%p req=0x%p", (void *)pipe, (void *)reqp); 3517 3518 ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0); 3519 3520 /* 3521 * At present, we are not handling notification for completion of 3522 * asynchronous pipe reset, for which this data ptr could be NULL 3523 */ 3524 3525 if (data == NULL) { 3526 usb_free_intr_req(reqp); 3527 3528 return; 3529 } 3530 3531 arg = (hubd_hotplug_arg_t *)kmem_zalloc( 3532 sizeof (hubd_hotplug_arg_t), KM_SLEEP); 3533 mem_flag = 1; 3534 3535 mutex_enter(HUBD_MUTEX(hubd)); 3536 3537 if ((hubd->h_dev_state == USB_DEV_SUSPENDED) || 3538 (hubd->h_intr_pipe_state != HUBD_INTR_PIPE_ACTIVE)) { 3539 mutex_exit(HUBD_MUTEX(hubd)); 3540 usb_free_intr_req(reqp); 3541 kmem_free(arg, sizeof (hubd_hotplug_arg_t)); 3542 3543 return; 3544 } 3545 3546 ASSERT(hubd->h_ep1_ph == pipe); 3547 3548 length = MBLKL(data); 3549 3550 /* 3551 * Only look at the data and startup the hotplug thread if 3552 * there actually is data. 3553 */ 3554 if (length != 0) { 3555 usb_port_mask_t port_change = hubd_mblk2portmask(data); 3556 3557 /* 3558 * if a port change was already reported and we are waiting for 3559 * reset port completion then wake up the hotplug thread which 3560 * should be waiting on reset port completion 3561 * 3562 * if there is disconnect event instead of reset completion, let 3563 * the hotplug thread figure this out