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 (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright 2012 Garrett D'Amore <garrett@damore.org>.  All rights reserved.
  24  */
  25 
  26 /*
  27  * USBA: Solaris USB Architecture support for the hub
  28  * including root hub
  29  * Most of the code for hubd resides in this file and
  30  * is shared between the HCD root hub support and hubd
  31  */
  32 #define USBA_FRAMEWORK
  33 #include <sys/usb/usba.h>
  34 #include <sys/usb/usba/usba_devdb.h>
  35 #include <sys/sunndi.h>
  36 #include <sys/usb/usba/usba_impl.h>
  37 #include <sys/usb/usba/usba_types.h>
  38 #include <sys/usb/usba/hubdi.h>
  39 #include <sys/usb/usba/hcdi_impl.h>
  40 #include <sys/usb/hubd/hub.h>
  41 #include <sys/usb/hubd/hubdvar.h>
  42 #include <sys/usb/hubd/hubd_impl.h>
  43 #include <sys/kobj.h>
  44 #include <sys/kobj_lex.h>
  45 #include <sys/fs/dv_node.h>
  46 #include <sys/strsun.h>
  47 
  48 /*
  49  * External functions
  50  */
  51 extern boolean_t consconfig_console_is_ready(void);
  52 
  53 /*
  54  * Prototypes for static functions
  55  */
  56 static  int     usba_hubdi_bus_ctl(
  57                         dev_info_t              *dip,
  58                         dev_info_t              *rdip,
  59                         ddi_ctl_enum_t          op,
  60                         void                    *arg,
  61                         void                    *result);
  62 
  63 static int      usba_hubdi_map_fault(
  64                         dev_info_t              *dip,
  65                         dev_info_t              *rdip,
  66                         struct hat              *hat,
  67                         struct seg              *seg,
  68                         caddr_t                 addr,
  69                         struct devpage          *dp,
  70                         pfn_t                   pfn,
  71                         uint_t                  prot,
  72                         uint_t                  lock);
  73 
  74 static int hubd_busop_get_eventcookie(dev_info_t *dip,
  75                         dev_info_t *rdip,
  76                         char *eventname,
  77                         ddi_eventcookie_t *cookie);
  78 static int hubd_busop_add_eventcall(dev_info_t *dip,
  79                         dev_info_t *rdip,
  80                         ddi_eventcookie_t cookie,
  81                         void (*callback)(dev_info_t *dip,
  82                                 ddi_eventcookie_t cookie, void *arg,
  83                                 void *bus_impldata),
  84                         void *arg, ddi_callback_id_t *cb_id);
  85 static int hubd_busop_remove_eventcall(dev_info_t *dip,
  86                         ddi_callback_id_t cb_id);
  87 static int hubd_bus_config(dev_info_t *dip,
  88                         uint_t flag,
  89                         ddi_bus_config_op_t op,
  90                         void *arg,
  91                         dev_info_t **child);
  92 static int hubd_bus_unconfig(dev_info_t *dip,
  93                         uint_t flag,
  94                         ddi_bus_config_op_t op,
  95                         void *arg);
  96 static int hubd_bus_power(dev_info_t *dip, void *impl_arg,
  97                         pm_bus_power_op_t op, void *arg, void *result);
  98 
  99 static usb_port_t  hubd_get_port_num(hubd_t *, struct devctl_iocdata *);
 100 static dev_info_t *hubd_get_child_dip(hubd_t *, usb_port_t);
 101 static uint_t hubd_cfgadm_state(hubd_t *, usb_port_t);
 102 static int hubd_toggle_port(hubd_t *, usb_port_t);
 103 static void hubd_register_cpr_callback(hubd_t *);
 104 static void hubd_unregister_cpr_callback(hubd_t *);
 105 
 106 /*
 107  * Busops vector for USB HUB's
 108  */
 109 struct bus_ops usba_hubdi_busops =      {
 110         BUSO_REV,
 111         nullbusmap,                     /* bus_map */
 112         NULL,                           /* bus_get_intrspec */
 113         NULL,                           /* bus_add_intrspec */
 114         NULL,                           /* bus_remove_intrspec */
 115         usba_hubdi_map_fault,           /* bus_map_fault */
 116         NULL,                           /* bus_dma_map */
 117         ddi_dma_allochdl,
 118         ddi_dma_freehdl,
 119         ddi_dma_bindhdl,
 120         ddi_dma_unbindhdl,
 121         ddi_dma_flush,
 122         ddi_dma_win,
 123         ddi_dma_mctl,                   /* bus_dma_ctl */
 124         usba_hubdi_bus_ctl,             /* bus_ctl */
 125         ddi_bus_prop_op,                /* bus_prop_op */
 126         hubd_busop_get_eventcookie,
 127         hubd_busop_add_eventcall,
 128         hubd_busop_remove_eventcall,
 129         NULL,                           /* bus_post_event */
 130         NULL,                           /* bus_intr_ctl */
 131         hubd_bus_config,                /* bus_config */
 132         hubd_bus_unconfig,              /* bus_unconfig */
 133         NULL,                           /* bus_fm_init */
 134         NULL,                           /* bus_fm_fini */
 135         NULL,                           /* bus_fm_access_enter */
 136         NULL,                           /* bus_fm_access_exit */
 137         hubd_bus_power                  /* bus_power */
 138 };
 139 
 140 #define USB_HUB_INTEL_VID       0x8087
 141 #define USB_HUB_INTEL_PID       0x0020
 142 
 143 /*
 144  * local variables
 145  */
 146 static kmutex_t usba_hubdi_mutex;       /* protects USBA HUB data structures */
 147 
 148 static usba_list_entry_t        usba_hubdi_list;
 149 
 150 usb_log_handle_t        hubdi_log_handle;
 151 uint_t                  hubdi_errlevel = USB_LOG_L4;
 152 uint_t                  hubdi_errmask = (uint_t)-1;
 153 uint8_t                 hubdi_min_pm_threshold = 5; /* seconds */
 154 uint8_t                 hubdi_reset_delay = 20; /* seconds */
 155 extern int modrootloaded;
 156 
 157 /*
 158  * initialize private data
 159  */
 160 void
 161 usba_hubdi_initialization()
 162 {
 163         hubdi_log_handle = usb_alloc_log_hdl(NULL, "hubdi", &hubdi_errlevel,
 164             &hubdi_errmask, NULL, 0);
 165 
 166         USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle,
 167             "usba_hubdi_initialization");
 168 
 169         mutex_init(&usba_hubdi_mutex, NULL, MUTEX_DRIVER, NULL);
 170 
 171         usba_init_list(&usba_hubdi_list, NULL, NULL);
 172 }
 173 
 174 
 175 void
 176 usba_hubdi_destroy()
 177 {
 178         USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle,
 179             "usba_hubdi_destroy");
 180 
 181         mutex_destroy(&usba_hubdi_mutex);
 182         usba_destroy_list(&usba_hubdi_list);
 183 
 184         usb_free_log_hdl(hubdi_log_handle);
 185 }
 186 
 187 
 188 /*
 189  * Called by an HUB to attach an instance of the driver
 190  *      make this instance known to USBA
 191  *      the HUB should initialize usba_hubdi structure prior
 192  *      to calling this interface
 193  */
 194 int
 195 usba_hubdi_register(dev_info_t  *dip,
 196                 uint_t          flags)
 197 {
 198         usba_hubdi_t *hubdi = kmem_zalloc(sizeof (usba_hubdi_t), KM_SLEEP);
 199         usba_device_t *usba_device = usba_get_usba_device(dip);
 200 
 201         USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle,
 202             "usba_hubdi_register: %s", ddi_node_name(dip));
 203 
 204         hubdi->hubdi_dip = dip;
 205         hubdi->hubdi_flags = flags;
 206 
 207         usba_device->usb_hubdi = hubdi;
 208 
 209         /*
 210          * add this hubdi instance to the list of known hubdi's
 211          */
 212         usba_init_list(&hubdi->hubdi_list, (usb_opaque_t)hubdi,
 213             usba_hcdi_get_hcdi(usba_device->usb_root_hub_dip)->
 214             hcdi_iblock_cookie);
 215         mutex_enter(&usba_hubdi_mutex);
 216         usba_add_to_list(&usba_hubdi_list, &hubdi->hubdi_list);
 217         mutex_exit(&usba_hubdi_mutex);
 218 
 219         return (DDI_SUCCESS);
 220 }
 221 
 222 
 223 /*
 224  * Called by an HUB to detach an instance of the driver
 225  */
 226 int
 227 usba_hubdi_unregister(dev_info_t *dip)
 228 {
 229         usba_device_t *usba_device = usba_get_usba_device(dip);
 230         usba_hubdi_t *hubdi = usba_device->usb_hubdi;
 231 
 232         USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle,
 233             "usba_hubdi_unregister: %s", ddi_node_name(dip));
 234 
 235         mutex_enter(&usba_hubdi_mutex);
 236         (void) usba_rm_from_list(&usba_hubdi_list, &hubdi->hubdi_list);
 237         mutex_exit(&usba_hubdi_mutex);
 238 
 239         usba_destroy_list(&hubdi->hubdi_list);
 240 
 241         kmem_free(hubdi, sizeof (usba_hubdi_t));
 242 
 243         return (DDI_SUCCESS);
 244 }
 245 
 246 
 247 /*
 248  * misc bus routines currently not used
 249  */
 250 /*ARGSUSED*/
 251 static int
 252 usba_hubdi_map_fault(dev_info_t *dip,
 253         dev_info_t      *rdip,
 254         struct hat      *hat,
 255         struct seg      *seg,
 256         caddr_t         addr,
 257         struct devpage  *dp,
 258         pfn_t           pfn,
 259         uint_t          prot,
 260         uint_t          lock)
 261 {
 262         return (DDI_FAILURE);
 263 }
 264 
 265 
 266 /*
 267  * root hub support. the root hub uses the same devi as the HCD
 268  */
 269 int
 270 usba_hubdi_bind_root_hub(dev_info_t *dip,
 271         uchar_t *root_hub_config_descriptor,
 272         size_t config_length,
 273         usb_dev_descr_t *root_hub_device_descriptor)
 274 {
 275         usba_device_t *usba_device;
 276         usba_hcdi_t *hcdi = usba_hcdi_get_hcdi(dip);
 277         hubd_t  *root_hubd;
 278         usb_pipe_handle_t ph = NULL;
 279         dev_info_t *child = ddi_get_child(dip);
 280 
 281         if (ndi_prop_create_boolean(DDI_DEV_T_NONE, dip,
 282             "root-hub") != NDI_SUCCESS) {
 283 
 284                 return (USB_FAILURE);
 285         }
 286 
 287         usba_add_root_hub(dip);
 288 
 289         root_hubd = kmem_zalloc(sizeof (hubd_t), KM_SLEEP);
 290 
 291         /*
 292          * create and initialize a usba_device structure
 293          */
 294         usba_device = usba_alloc_usba_device(dip);
 295 
 296         mutex_enter(&usba_device->usb_mutex);
 297         usba_device->usb_hcdi_ops = hcdi->hcdi_ops;
 298         usba_device->usb_cfg = root_hub_config_descriptor;
 299         usba_device->usb_cfg_length = config_length;
 300         usba_device->usb_dev_descr = root_hub_device_descriptor;
 301         usba_device->usb_port = 1;
 302         usba_device->usb_addr = ROOT_HUB_ADDR;
 303         usba_device->usb_root_hubd = root_hubd;
 304         usba_device->usb_cfg_array = kmem_zalloc(sizeof (uchar_t *),
 305             KM_SLEEP);
 306         usba_device->usb_cfg_array_length = sizeof (uchar_t *);
 307 
 308         usba_device->usb_cfg_array_len = kmem_zalloc(sizeof (uint16_t),
 309             KM_SLEEP);
 310         usba_device->usb_cfg_array_len_length = sizeof (uint16_t);
 311 
 312         usba_device->usb_cfg_array[0] = root_hub_config_descriptor;
 313         usba_device->usb_cfg_array_len[0] =
 314             sizeof (root_hub_config_descriptor);
 315 
 316         usba_device->usb_cfg_str_descr = kmem_zalloc(sizeof (uchar_t *),
 317             KM_SLEEP);
 318         usba_device->usb_n_cfgs = 1;
 319         usba_device->usb_n_ifs = 1;
 320         usba_device->usb_dip = dip;
 321 
 322         usba_device->usb_client_flags = kmem_zalloc(
 323             usba_device->usb_n_ifs * USBA_CLIENT_FLAG_SIZE, KM_SLEEP);
 324 
 325         usba_device->usb_client_attach_list = kmem_zalloc(
 326             usba_device->usb_n_ifs *
 327             sizeof (*usba_device->usb_client_attach_list), KM_SLEEP);
 328 
 329         usba_device->usb_client_ev_cb_list = kmem_zalloc(
 330             usba_device->usb_n_ifs *
 331             sizeof (*usba_device->usb_client_ev_cb_list), KM_SLEEP);
 332 
 333         /*
 334          * The bDeviceProtocol field of root hub device specifies,
 335          * whether root hub is a High or Full speed usb device.
 336          */
 337         if (root_hub_device_descriptor->bDeviceProtocol) {
 338                 usba_device->usb_port_status = USBA_HIGH_SPEED_DEV;
 339         } else {
 340                 usba_device->usb_port_status = USBA_FULL_SPEED_DEV;
 341         }
 342 
 343         mutex_exit(&usba_device->usb_mutex);
 344 
 345         usba_set_usba_device(dip, usba_device);
 346 
 347         /*
 348          * For the root hub the default pipe is not yet open
 349          */
 350         if (usb_pipe_open(dip, NULL, NULL,
 351             USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph) != USB_SUCCESS) {
 352                 goto fail;
 353         }
 354 
 355         /*
 356          * kill off all OBP children, they may not be fully
 357          * enumerated
 358          */
 359         while (child) {
 360                 dev_info_t *next = ddi_get_next_sibling(child);
 361                 (void) ddi_remove_child(child, 0);
 362                 child = next;
 363         }
 364 
 365         /*
 366          * "attach" the root hub driver
 367          */
 368         if (usba_hubdi_attach(dip, DDI_ATTACH) != DDI_SUCCESS) {
 369                 goto fail;
 370         }
 371 
 372         return (USB_SUCCESS);
 373 
 374 fail:
 375         (void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "root-hub");
 376 
 377         usba_rem_root_hub(dip);
 378 
 379         if (ph) {
 380                 usb_pipe_close(dip, ph,
 381                     USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
 382         }
 383 
 384         kmem_free(usba_device->usb_cfg_array,
 385             usba_device->usb_cfg_array_length);
 386         kmem_free(usba_device->usb_cfg_array_len,
 387             usba_device->usb_cfg_array_len_length);
 388 
 389         kmem_free(usba_device->usb_cfg_str_descr, sizeof (uchar_t *));
 390 
 391         usba_free_usba_device(usba_device);
 392 
 393         usba_set_usba_device(dip, NULL);
 394         if (root_hubd) {
 395                 kmem_free(root_hubd, sizeof (hubd_t));
 396         }
 397 
 398         return (USB_FAILURE);
 399 }
 400 
 401 
 402 int
 403 usba_hubdi_unbind_root_hub(dev_info_t *dip)
 404 {
 405         usba_device_t *usba_device;
 406 
 407         /* was root hub attached? */
 408         if (!(usba_is_root_hub(dip))) {
 409 
 410                 /* return success anyway */
 411                 return (USB_SUCCESS);
 412         }
 413 
 414         /*
 415          * usba_hubdi_detach also closes the default pipe
 416          * and removes properties so there is no need to
 417          * do it here
 418          */
 419         if (usba_hubdi_detach(dip, DDI_DETACH) != DDI_SUCCESS) {
 420 
 421                 if (DEVI_IS_ATTACHING(dip)) {
 422                         USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
 423                             "failure to unbind root hub after attach failure");
 424                 }
 425 
 426                 return (USB_FAILURE);
 427         }
 428 
 429         usba_device = usba_get_usba_device(dip);
 430 
 431         kmem_free(usba_device->usb_root_hubd, sizeof (hubd_t));
 432 
 433         kmem_free(usba_device->usb_cfg_array,
 434             usba_device->usb_cfg_array_length);
 435         kmem_free(usba_device->usb_cfg_array_len,
 436             usba_device->usb_cfg_array_len_length);
 437 
 438         kmem_free(usba_device->usb_cfg_str_descr, sizeof (uchar_t *));
 439 
 440         usba_free_usba_device(usba_device);
 441 
 442         usba_rem_root_hub(dip);
 443 
 444         (void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "root-hub");
 445 
 446         return (USB_SUCCESS);
 447 }
 448 
 449 
 450 /*
 451  * Actual Hub Driver support code:
 452  *      shared by root hub and non-root hubs
 453  */
 454 #include <sys/usb/usba/usbai_version.h>
 455 
 456 /* Debugging support */
 457 uint_t hubd_errlevel    = USB_LOG_L4;
 458 uint_t hubd_errmask     = (uint_t)DPRINT_MASK_ALL;
 459 uint_t hubd_instance_debug = (uint_t)-1;
 460 static uint_t hubdi_bus_config_debug = 0;
 461 
 462 _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_errlevel))
 463 _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_errmask))
 464 _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_instance_debug))
 465 
 466 _NOTE(SCHEME_PROTECTS_DATA("unique", msgb))
 467 _NOTE(SCHEME_PROTECTS_DATA("unique", dev_info))
 468 
 469 
 470 /*
 471  * local variables:
 472  *
 473  * Amount of time to wait between resetting the port and accessing
 474  * the device.  The value is in microseconds.
 475  */
 476 static uint_t hubd_device_delay = 1000000;
 477 
 478 /*
 479  * enumeration retry
 480  */
 481 #define HUBD_PORT_RETRY 5
 482 static uint_t hubd_retry_enumerate = HUBD_PORT_RETRY;
 483 
 484 /*
 485  * Stale hotremoved device cleanup delay
 486  */
 487 #define HUBD_STALE_DIP_CLEANUP_DELAY    5000000
 488 static uint_t hubd_dip_cleanup_delay = HUBD_STALE_DIP_CLEANUP_DELAY;
 489 
 490 /*
 491  * retries for USB suspend and resume
 492  */
 493 #define HUBD_SUS_RES_RETRY      2
 494 
 495 void    *hubd_statep;
 496 
 497 /*
 498  * prototypes
 499  */
 500 static int hubd_cleanup(dev_info_t *dip, hubd_t  *hubd);
 501 static int hubd_check_ports(hubd_t  *hubd);
 502 
 503 static int  hubd_open_intr_pipe(hubd_t *hubd);
 504 static void hubd_start_polling(hubd_t *hubd, int always);
 505 static void hubd_stop_polling(hubd_t *hubd);
 506 static void hubd_close_intr_pipe(hubd_t *hubd);
 507 
 508 static void hubd_read_cb(usb_pipe_handle_t pipe, usb_intr_req_t *req);
 509 static void hubd_exception_cb(usb_pipe_handle_t pipe,
 510                                                 usb_intr_req_t *req);
 511 static void hubd_hotplug_thread(void *arg);
 512 static void hubd_reset_thread(void *arg);
 513 static int hubd_create_child(dev_info_t *dip,
 514                 hubd_t          *hubd,
 515                 usba_device_t   *usba_device,
 516                 usb_port_status_t port_status,
 517                 usb_port_t      port,
 518                 int             iteration);
 519 
 520 static int hubd_delete_child(hubd_t *hubd, usb_port_t port, uint_t flag,
 521         boolean_t retry);
 522 
 523 static int hubd_get_hub_descriptor(hubd_t *hubd);
 524 
 525 static int hubd_get_hub_status_words(hubd_t *hubd, uint16_t *status);
 526 
 527 static int hubd_reset_port(hubd_t *hubd, usb_port_t port);
 528 
 529 static int hubd_get_hub_status(hubd_t *hubd);
 530 
 531 static int hubd_handle_port_connect(hubd_t *hubd, usb_port_t port);
 532 
 533 static int hubd_disable_port(hubd_t *hubd, usb_port_t port);
 534 
 535 static int hubd_enable_port(hubd_t *hubd, usb_port_t port);
 536 static int hubd_recover_disabled_port(hubd_t *hubd, usb_port_t port);
 537 
 538 static int hubd_determine_port_status(hubd_t *hubd, usb_port_t port,
 539         uint16_t *status, uint16_t *change, uint_t ack_flag);
 540 
 541 static int hubd_enable_all_port_power(hubd_t *hubd);
 542 static int hubd_disable_all_port_power(hubd_t *hubd);
 543 static int hubd_disable_port_power(hubd_t *hubd, usb_port_t port);
 544 static int hubd_enable_port_power(hubd_t *hubd, usb_port_t port);
 545 
 546 static void hubd_free_usba_device(hubd_t *hubd, usba_device_t *usba_device);
 547 
 548 static int hubd_can_suspend(hubd_t *hubd);
 549 static void hubd_restore_device_state(dev_info_t *dip, hubd_t *hubd);
 550 static int hubd_setdevaddr(hubd_t *hubd, usb_port_t port);
 551 static void hubd_setdevconfig(hubd_t *hubd, usb_port_t port);
 552 
 553 static int hubd_register_events(hubd_t *hubd);
 554 static void hubd_do_callback(hubd_t *hubd, dev_info_t *dip,
 555         ddi_eventcookie_t cookie);
 556 static void hubd_run_callbacks(hubd_t *hubd, usba_event_t type);
 557 static void hubd_post_event(hubd_t *hubd, usb_port_t port, usba_event_t type);
 558 static void hubd_create_pm_components(dev_info_t *dip, hubd_t *hubd);
 559 
 560 static int hubd_disconnect_event_cb(dev_info_t *dip);
 561 static int hubd_reconnect_event_cb(dev_info_t *dip);
 562 static int hubd_pre_suspend_event_cb(dev_info_t *dip);
 563 static int hubd_post_resume_event_cb(dev_info_t *dip);
 564 static int hubd_cpr_suspend(hubd_t *hubd);
 565 static void hubd_cpr_resume(dev_info_t *dip);
 566 static int hubd_restore_state_cb(dev_info_t *dip);
 567 static int hubd_check_same_device(hubd_t *hubd, usb_port_t port);
 568 
 569 static int hubd_init_power_budget(hubd_t *hubd);
 570 
 571 static ndi_event_definition_t hubd_ndi_event_defs[] = {
 572         {USBA_EVENT_TAG_HOT_REMOVAL, DDI_DEVI_REMOVE_EVENT, EPL_KERNEL,
 573                                                 NDI_EVENT_POST_TO_ALL},
 574         {USBA_EVENT_TAG_HOT_INSERTION, DDI_DEVI_INSERT_EVENT, EPL_KERNEL,
 575                                                 NDI_EVENT_POST_TO_ALL},
 576         {USBA_EVENT_TAG_POST_RESUME, USBA_POST_RESUME_EVENT, EPL_KERNEL,
 577                                                 NDI_EVENT_POST_TO_ALL},
 578         {USBA_EVENT_TAG_PRE_SUSPEND, USBA_PRE_SUSPEND_EVENT, EPL_KERNEL,
 579                                                 NDI_EVENT_POST_TO_ALL}
 580 };
 581 
 582 #define HUBD_N_NDI_EVENTS \
 583         (sizeof (hubd_ndi_event_defs) / sizeof (ndi_event_definition_t))
 584 
 585 static ndi_event_set_t hubd_ndi_events = {
 586         NDI_EVENTS_REV1, HUBD_N_NDI_EVENTS, hubd_ndi_event_defs};
 587 
 588 /* events received from parent */
 589 static usb_event_t hubd_events = {
 590         hubd_disconnect_event_cb,
 591         hubd_reconnect_event_cb,
 592         hubd_pre_suspend_event_cb,
 593         hubd_post_resume_event_cb
 594 };
 595 
 596 
 597 /*
 598  * hubd_get_soft_state() returns the hubd soft state
 599  *
 600  * WUSB support extends this function to support wire adapter class
 601  * devices. The hubd soft state for the wire adapter class device
 602  * would be stored in usb_root_hubd field of the usba_device structure,
 603  * just as the USB host controller drivers do.
 604  */
 605 hubd_t *
 606 hubd_get_soft_state(dev_info_t *dip)
 607 {
 608         if (dip == NULL) {
 609 
 610                 return (NULL);
 611         }
 612 
 613         if (usba_is_root_hub(dip) || usba_is_wa(dip)) {
 614                 usba_device_t *usba_device = usba_get_usba_device(dip);
 615 
 616                 return (usba_device->usb_root_hubd);
 617         } else {
 618                 int instance = ddi_get_instance(dip);
 619 
 620                 return (ddi_get_soft_state(hubd_statep, instance));
 621         }
 622 }
 623 
 624 
 625 /*
 626  * PM support functions:
 627  */
 628 /*ARGSUSED*/
 629 static void
 630 hubd_pm_busy_component(hubd_t *hubd, dev_info_t *dip, int component)
 631 {
 632         if (hubd->h_hubpm != NULL) {
 633                 hubd->h_hubpm->hubp_busy_pm++;
 634                 mutex_exit(HUBD_MUTEX(hubd));
 635                 if (pm_busy_component(dip, 0) != DDI_SUCCESS) {
 636                         mutex_enter(HUBD_MUTEX(hubd));
 637                         hubd->h_hubpm->hubp_busy_pm--;
 638                         mutex_exit(HUBD_MUTEX(hubd));
 639                 }
 640                 mutex_enter(HUBD_MUTEX(hubd));
 641                 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
 642                     "hubd_pm_busy_component: %d", hubd->h_hubpm->hubp_busy_pm);
 643         }
 644 }
 645 
 646 
 647 /*ARGSUSED*/
 648 static void
 649 hubd_pm_idle_component(hubd_t *hubd, dev_info_t *dip, int component)
 650 {
 651         if (hubd->h_hubpm != NULL) {
 652                 mutex_exit(HUBD_MUTEX(hubd));
 653                 if (pm_idle_component(dip, 0) == DDI_SUCCESS) {
 654                         mutex_enter(HUBD_MUTEX(hubd));
 655                         ASSERT(hubd->h_hubpm->hubp_busy_pm > 0);
 656                         hubd->h_hubpm->hubp_busy_pm--;
 657                         mutex_exit(HUBD_MUTEX(hubd));
 658                 }
 659                 mutex_enter(HUBD_MUTEX(hubd));
 660                 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
 661                     "hubd_pm_idle_component: %d", hubd->h_hubpm->hubp_busy_pm);
 662         }
 663 }
 664 
 665 
 666 /*
 667  * track power level changes for children of this instance
 668  */
 669 static void
 670 hubd_set_child_pwrlvl(hubd_t *hubd, usb_port_t port, uint8_t power)
 671 {
 672         int     old_power, new_power, pwr;
 673         usb_port_t      portno;
 674         hub_power_t     *hubpm;
 675 
 676         USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
 677             "hubd_set_child_pwrlvl: port=%d power=%d",
 678             port, power);
 679 
 680         mutex_enter(HUBD_MUTEX(hubd));
 681         hubpm = hubd->h_hubpm;
 682 
 683         old_power = 0;
 684         for (portno = 1; portno <= hubd->h_hub_descr.bNbrPorts; portno++) {
 685                 old_power += hubpm->hubp_child_pwrstate[portno];
 686         }
 687 
 688         /* assign the port power */
 689         pwr = hubd->h_hubpm->hubp_child_pwrstate[port];
 690         hubd->h_hubpm->hubp_child_pwrstate[port] = power;
 691         new_power = old_power - pwr + power;
 692 
 693         USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
 694             "hubd_set_child_pwrlvl: new_power=%d old_power=%d",
 695             new_power, old_power);
 696 
 697         if ((new_power > 0) && (old_power == 0)) {
 698                 /* we have the first child coming out of low power */
 699                 (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0);
 700         } else if ((new_power == 0) && (old_power > 0)) {
 701                 /* we have the last child going to low power */
 702                 (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
 703         }
 704         mutex_exit(HUBD_MUTEX(hubd));
 705 }
 706 
 707 
 708 /*
 709  * given a child dip, locate its port number
 710  */
 711 static usb_port_t
 712 hubd_child_dip2port(hubd_t *hubd, dev_info_t *dip)
 713 {
 714         usb_port_t      port;
 715 
 716         mutex_enter(HUBD_MUTEX(hubd));
 717         for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
 718                 if (hubd->h_children_dips[port] == dip) {
 719 
 720                         break;
 721                 }
 722         }
 723         ASSERT(port <= hubd->h_hub_descr.bNbrPorts);
 724         mutex_exit(HUBD_MUTEX(hubd));
 725 
 726         return (port);
 727 }
 728 
 729 
 730 /*
 731  * if the hub can be put into low power mode, return success
 732  * NOTE: suspend here means going to lower power, not CPR suspend.
 733  */
 734 static int
 735 hubd_can_suspend(hubd_t *hubd)
 736 {
 737         hub_power_t     *hubpm;
 738         int             total_power = 0;
 739         usb_port_t      port;
 740 
 741         hubpm = hubd->h_hubpm;
 742 
 743         if (DEVI_IS_DETACHING(hubd->h_dip)) {
 744 
 745                 return (USB_SUCCESS);
 746         }
 747 
 748         /*
 749          * Don't go to lower power if haven't been at full power for enough
 750          * time to let hotplug thread kickoff.
 751          */
 752         if (ddi_get_time() < (hubpm->hubp_time_at_full_power +
 753             hubpm->hubp_min_pm_threshold)) {
 754 
 755                 return (USB_FAILURE);
 756         }
 757 
 758         for (port = 1; (total_power == 0) &&
 759             (port <= hubd->h_hub_descr.bNbrPorts); port++) {
 760                 total_power += hubpm->hubp_child_pwrstate[port];
 761         }
 762 
 763         USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
 764             "hubd_can_suspend: %d", total_power);
 765 
 766         return (total_power ? USB_FAILURE : USB_SUCCESS);
 767 }
 768 
 769 
 770 /*
 771  * resume port depending on current device state
 772  */
 773 static int
 774 hubd_resume_port(hubd_t *hubd, usb_port_t port)
 775 {
 776         int             rval, retry;
 777         usb_cr_t        completion_reason;
 778         usb_cb_flags_t  cb_flags;
 779         uint16_t        status;
 780         uint16_t        change;
 781         int             retval = USB_FAILURE;
 782 
 783         mutex_enter(HUBD_MUTEX(hubd));
 784 
 785         USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
 786             "hubd_resume_port: port=%d state=0x%x (%s)", port,
 787             hubd->h_dev_state, usb_str_dev_state(hubd->h_dev_state));
 788 
 789         switch (hubd->h_dev_state) {
 790         case USB_DEV_HUB_CHILD_PWRLVL:
 791                 /*
 792                  * This could be a bus ctl for a port other than the one
 793                  * that has a remote wakeup condition. So check.
 794                  */
 795                 if ((hubd->h_port_state[port] & PORT_STATUS_PSS) == 0) {
 796                         /* the port isn't suspended, so don't resume */
 797                         retval = USB_SUCCESS;
 798 
 799                         USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
 800                             "hubd_resume_port: port=%d not suspended", port);
 801 
 802                         break;
 803                 }
 804                 /*
 805                  * Device has initiated a wakeup.
 806                  * Issue a ClearFeature(PortSuspend)
 807                  */
 808                 mutex_exit(HUBD_MUTEX(hubd));
 809                 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
 810                     hubd->h_default_pipe,
 811                     HUB_HANDLE_PORT_FEATURE_TYPE,
 812                     USB_REQ_CLEAR_FEATURE,
 813                     CFS_PORT_SUSPEND,
 814                     port,
 815                     0, NULL, 0,
 816                     &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
 817                         USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
 818                             "ClearFeature(PortSuspend) fails "
 819                             "rval=%d cr=%d cb=0x%x", rval,
 820                             completion_reason, cb_flags);
 821                 }
 822                 mutex_enter(HUBD_MUTEX(hubd));
 823 
 824                 /* either way ack changes on the port */
 825                 (void) hubd_determine_port_status(hubd, port,
 826                     &status, &change, PORT_CHANGE_PSSC);
 827                 retval = USB_SUCCESS;
 828 
 829                 break;
 830         case USB_DEV_HUB_STATE_RECOVER:
 831                 /*
 832                  * When hubd's connect event callback posts a connect
 833                  * event to its child, it results in this busctl call
 834                  * which is valid
 835                  */
 836                 /* FALLTHRU */
 837         case USB_DEV_ONLINE:
 838                 if (((hubd->h_port_state[port] & PORT_STATUS_CCS) == 0) ||
 839                     ((hubd->h_port_state[port] & PORT_STATUS_PSS) == 0)) {
 840                         /*
 841                          * the port isn't suspended, or connected
 842                          * so don't resume
 843                          */
 844                         retval = USB_SUCCESS;
 845 
 846                         USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
 847                             "hubd_resume_port: port=%d not suspended", port);
 848 
 849                         break;
 850                 }
 851                 /*
 852                  * prevent kicking off the hotplug thread
 853                  */
 854                 hubd->h_hotplug_thread++;
 855                 hubd_stop_polling(hubd);
 856 
 857                 /* Now ClearFeature(PortSuspend) */
 858                 for (retry = 0; retry < HUBD_SUS_RES_RETRY; retry++) {
 859                         mutex_exit(HUBD_MUTEX(hubd));
 860                         rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
 861                             hubd->h_default_pipe,
 862                             HUB_HANDLE_PORT_FEATURE_TYPE,
 863                             USB_REQ_CLEAR_FEATURE,
 864                             CFS_PORT_SUSPEND,
 865                             port,
 866                             0, NULL, 0,
 867                             &completion_reason, &cb_flags, 0);
 868                         mutex_enter(HUBD_MUTEX(hubd));
 869                         if (rval != USB_SUCCESS) {
 870                                 USB_DPRINTF_L2(DPRINT_MASK_PM,
 871                                     hubd->h_log_handle,
 872                                     "ClearFeature(PortSuspend) fails"
 873                                     "rval=%d cr=%d cb=0x%x", rval,
 874                                     completion_reason, cb_flags);
 875                         } else {
 876                                 /*
 877                                  * As per spec section 11.9 and 7.1.7.7
 878                                  * hub need to provide at least 20ms of
 879                                  * resume signalling, and s/w provide 10ms of
 880                                  * recovery time before accessing the port.
 881                                  */
 882                                 mutex_exit(HUBD_MUTEX(hubd));
 883                                 delay(drv_usectohz(40000));
 884                                 mutex_enter(HUBD_MUTEX(hubd));
 885                                 (void) hubd_determine_port_status(hubd, port,
 886                                     &status, &change, PORT_CHANGE_PSSC);
 887 
 888                                 if ((status & PORT_STATUS_PSS) == 0) {
 889                                         /* the port did finally resume */
 890                                         retval = USB_SUCCESS;
 891 
 892                                         break;
 893                                 }
 894                         }
 895                 }
 896 
 897                 /* allow hotplug thread again */
 898                 hubd->h_hotplug_thread--;
 899                 hubd_start_polling(hubd, 0);
 900 
 901                 break;
 902         case USB_DEV_DISCONNECTED:
 903                 /* Ignore - NO Operation */
 904                 retval = USB_SUCCESS;
 905 
 906                 break;
 907         case USB_DEV_SUSPENDED:
 908         case USB_DEV_PWRED_DOWN:
 909         default:
 910                 USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
 911                     "Improper state for port Resume");
 912 
 913                 break;
 914         }
 915         mutex_exit(HUBD_MUTEX(hubd));
 916 
 917         return (retval);
 918 }
 919 
 920 
 921 /*
 922  * suspend port depending on device state
 923  */
 924 static int
 925 hubd_suspend_port(hubd_t *hubd, usb_port_t port)
 926 {
 927         int             rval, retry;
 928         int             retval = USB_FAILURE;
 929         usb_cr_t        completion_reason;
 930         usb_cb_flags_t  cb_flags;
 931         uint16_t        status;
 932         uint16_t        change;
 933 
 934         USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
 935             "hubd_suspend_port: port=%d", port);
 936 
 937         mutex_enter(HUBD_MUTEX(hubd));
 938 
 939         switch (hubd->h_dev_state) {
 940         case USB_DEV_HUB_STATE_RECOVER:
 941                 /*
 942                  * When hubd's connect event callback posts a connect
 943                  * event to its child, it results in this busctl call
 944                  * which is valid
 945                  */
 946                 /* FALLTHRU */
 947         case USB_DEV_HUB_CHILD_PWRLVL:
 948                 /*
 949                  * When one child is resuming, the other could timeout
 950                  * and go to low power mode, which is valid
 951                  */
 952                 /* FALLTHRU */
 953         case USB_DEV_ONLINE:
 954                 hubd->h_hotplug_thread++;
 955                 hubd_stop_polling(hubd);
 956 
 957                 /*
 958                  * Some devices start an unprovoked resume.  According to spec,
 959                  * normal resume time for port is 10ms.  Wait for double that
 960                  * time, then check to be sure port is really suspended.
 961                  */
 962                 for (retry = 0; retry < HUBD_SUS_RES_RETRY; retry++) {
 963                         /* Now SetFeature(PortSuspend) */
 964                         mutex_exit(HUBD_MUTEX(hubd));
 965                         if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
 966                             hubd->h_default_pipe,
 967                             HUB_HANDLE_PORT_FEATURE_TYPE,
 968                             USB_REQ_SET_FEATURE,
 969                             CFS_PORT_SUSPEND,
 970                             port,
 971                             0, NULL, 0,
 972                             &completion_reason, &cb_flags, 0)) !=
 973                             USB_SUCCESS) {
 974                                 USB_DPRINTF_L2(DPRINT_MASK_PM,
 975                                     hubd->h_log_handle,
 976                                     "SetFeature(PortSuspend) fails"
 977                                     "rval=%d cr=%d cb=0x%x",
 978                                     rval, completion_reason, cb_flags);
 979                         }
 980 
 981                         /*
 982                          * some devices start an unprovoked resume
 983                          * wait and check port status after some time
 984                          */
 985                         delay(drv_usectohz(20000));
 986 
 987                         /* either ways ack changes on the port */
 988                         mutex_enter(HUBD_MUTEX(hubd));
 989                         (void) hubd_determine_port_status(hubd, port,
 990                             &status, &change, PORT_CHANGE_PSSC);
 991                         if (status & PORT_STATUS_PSS) {
 992                                 /* the port is indeed suspended */
 993                                 retval = USB_SUCCESS;
 994 
 995                                 break;
 996                         } else {
 997                                 USB_DPRINTF_L0(DPRINT_MASK_PM,
 998                                     hubd->h_log_handle,
 999                                     "hubdi: port%d failed to be suspended!",
1000                                     port);
1001                         }
1002                 }
1003 
1004                 hubd->h_hotplug_thread--;
1005                 hubd_start_polling(hubd, 0);
1006 
1007                 break;
1008 
1009         case USB_DEV_DISCONNECTED:
1010                 /* Ignore - No Operation */
1011                 retval = USB_SUCCESS;
1012 
1013                 break;
1014 
1015         case USB_DEV_SUSPENDED:
1016         case USB_DEV_PWRED_DOWN:
1017         default:
1018                 USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
1019                     "Improper state for port Suspend");
1020 
1021                 break;
1022         }
1023         mutex_exit(HUBD_MUTEX(hubd));
1024 
1025         return (retval);
1026 }
1027 
1028 
1029 /*
1030  * child post attach/detach notifications
1031  */
1032 static void
1033 hubd_post_attach(hubd_t *hubd, usb_port_t port, struct attachspec *as)
1034 {
1035         dev_info_t      *dip;
1036 
1037         USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
1038             "hubd_post_attach: port=%d result=%d",
1039             port, as->result);
1040 
1041         if (as->result == DDI_SUCCESS) {
1042                 /*
1043                  * Check if the child created wants to be power managed.
1044                  * If yes, the childs power level gets automatically tracked
1045                  * by DDI_CTLOPS_POWER busctl.
1046                  * If no, we set power of the new child by default
1047                  * to USB_DEV_OS_FULL_PWR. Because we should never suspend.
1048                  */
1049                 mutex_enter(HUBD_MUTEX(hubd));
1050                 dip = hubd->h_children_dips[port];
1051                 mutex_exit(HUBD_MUTEX(hubd));
1052                 if (DEVI(dip)->devi_pm_info == NULL) {
1053                         hubd_set_child_pwrlvl(hubd, port, USB_DEV_OS_FULL_PWR);
1054                 }
1055         }
1056 }
1057 
1058 
1059 static void
1060 hubd_post_detach(hubd_t *hubd, usb_port_t port, struct detachspec *ds)
1061 {
1062         USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
1063             "hubd_post_detach: port=%d result=%d", port, ds->result);
1064 
1065         /*
1066          * if the device is successfully detached and is the
1067          * last device to detach, mark component as idle
1068          */
1069         mutex_enter(HUBD_MUTEX(hubd));
1070         if (ds->result == DDI_SUCCESS) {
1071                 usba_device_t   *usba_device = hubd->h_usba_devices[port];
1072                 dev_info_t      *pdip = hubd->h_dip;
1073                 mutex_exit(HUBD_MUTEX(hubd));
1074 
1075                 usba_hubdi_incr_power_budget(pdip, usba_device);
1076 
1077                 /*
1078                  * We set power of the detached child
1079                  * to 0, so that we can suspend if all
1080                  * our children are gone
1081                  */
1082                 hubd_set_child_pwrlvl(hubd, port, USB_DEV_OS_PWR_OFF);
1083 
1084                 /* check for leaks on detaching */
1085                 if ((usba_device) && (ds->cmd == DDI_DETACH)) {
1086                         usba_check_for_leaks(usba_device);
1087                 }
1088         } else {
1089                 mutex_exit(HUBD_MUTEX(hubd));
1090         }
1091 }
1092 
1093 
1094 /*
1095  * hubd_post_power
1096  *      After the child's power entry point has been called
1097  *      we record its power level in our local struct.
1098  *      If the device has powered off, we suspend port
1099  */
1100 static int
1101 hubd_post_power(hubd_t *hubd, usb_port_t port, pm_bp_child_pwrchg_t *bpc,
1102     int result)
1103 {
1104         int     retval = USB_SUCCESS;
1105 
1106         USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
1107             "hubd_post_power: port=%d", port);
1108 
1109         if (result == DDI_SUCCESS) {
1110 
1111                 /* record this power in our local struct */
1112                 hubd_set_child_pwrlvl(hubd, port, bpc->bpc_nlevel);
1113 
1114                 if (bpc->bpc_nlevel == USB_DEV_OS_PWR_OFF) {
1115 
1116                         /* now suspend the port */
1117                         retval = hubd_suspend_port(hubd, port);
1118                 } else if (bpc->bpc_nlevel == USB_DEV_OS_FULL_PWR) {
1119 
1120                         /* make sure the port is resumed */
1121                         retval = hubd_resume_port(hubd, port);
1122                 }
1123         } else {
1124 
1125                 /* record old power in our local struct */
1126                 hubd_set_child_pwrlvl(hubd, port, bpc->bpc_olevel);
1127 
1128                 if (bpc->bpc_olevel == USB_DEV_OS_PWR_OFF) {
1129 
1130                         /*
1131                          * As this device failed to transition from
1132                          * power off state, suspend the port again
1133                          */
1134                         retval = hubd_suspend_port(hubd, port);
1135                 }
1136         }
1137 
1138         return (retval);
1139 }
1140 
1141 
1142 /*
1143  * bus ctl notifications are handled here, the rest goes up to root hub/hcd
1144  */
1145 static int
1146 usba_hubdi_bus_ctl(dev_info_t *dip,
1147         dev_info_t      *rdip,
1148         ddi_ctl_enum_t  op,
1149         void            *arg,
1150         void            *result)
1151 {
1152         usba_device_t *hub_usba_device = usba_get_usba_device(rdip);
1153         dev_info_t *root_hub_dip = hub_usba_device->usb_root_hub_dip;
1154         struct attachspec *as;
1155         struct detachspec *ds;
1156         hubd_t          *hubd;
1157         usb_port_t      port;
1158         int             circ, rval;
1159         int             retval = DDI_FAILURE;
1160 
1161         hubd = hubd_get_soft_state(dip);
1162 
1163         mutex_enter(HUBD_MUTEX(hubd));
1164 
1165         /* flag that we are currently running bus_ctl */
1166         hubd->h_bus_ctls++;
1167         mutex_exit(HUBD_MUTEX(hubd));
1168 
1169         USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle,
1170             "usba_hubdi_bus_ctl:\n\t"
1171             "dip=0x%p, rdip=0x%p, op=0x%x, arg=0x%p",
1172             (void *)dip, (void *)rdip, op, arg);
1173 
1174         switch (op) {
1175         case DDI_CTLOPS_ATTACH:
1176                 as = (struct attachspec *)arg;
1177                 port = hubd_child_dip2port(hubd, rdip);
1178 
1179                 /* there is nothing to do at resume time */
1180                 if (as->cmd == DDI_RESUME) {
1181                         break;
1182                 }
1183 
1184                 /* serialize access */
1185                 ndi_devi_enter(hubd->h_dip, &circ);
1186 
1187                 switch (as->when) {
1188                 case DDI_PRE:
1189                         USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
1190                             "DDI_PRE DDI_CTLOPS_ATTACH: dip=%p, port=%d",
1191                             (void *)rdip, port);
1192 
1193                         mutex_enter(HUBD_MUTEX(hubd));
1194                         hubd->h_port_state[port] |= HUBD_CHILD_ATTACHING;
1195 
1196                         /* Go busy here.  Matching idle is DDI_POST case. */
1197                         (void) hubd_pm_busy_component(hubd, dip, 0);
1198                         mutex_exit(HUBD_MUTEX(hubd));
1199 
1200                         /*
1201                          * if we suspended the port previously
1202                          * because child went to low power state, and
1203                          * someone unloaded the driver, the port would
1204                          * still be suspended and needs to be resumed
1205                          */
1206                         rval = hubd_resume_port(hubd, port);
1207                         if (rval == USB_SUCCESS) {
1208                                 retval = DDI_SUCCESS;
1209                         }
1210 
1211                         break;
1212                 case DDI_POST:
1213                         USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
1214                             "DDI_POST DDI_CTLOPS_ATTACH: dip=%p, port=%d",
1215                             (void *)rdip, port);
1216 
1217                         mutex_enter(HUBD_MUTEX(hubd));
1218                         hubd->h_port_state[port] &= ~HUBD_CHILD_ATTACHING;
1219                         mutex_exit(HUBD_MUTEX(hubd));
1220 
1221                         hubd_post_attach(hubd, port, (struct attachspec *)arg);
1222                         retval = DDI_SUCCESS;
1223                         mutex_enter(HUBD_MUTEX(hubd));
1224 
1225                         /* Matching idle call for DDI_PRE busy call. */
1226                         (void) hubd_pm_idle_component(hubd, dip, 0);
1227                         mutex_exit(HUBD_MUTEX(hubd));
1228                 }
1229                 ndi_devi_exit(hubd->h_dip, circ);
1230 
1231                 break;
1232         case DDI_CTLOPS_DETACH:
1233                 ds = (struct detachspec *)arg;
1234                 port = hubd_child_dip2port(hubd, rdip);
1235 
1236                 /* there is nothing to do at suspend time */
1237                 if (ds->cmd == DDI_SUSPEND) {
1238                         break;
1239                 }
1240 
1241                 /* serialize access */
1242                 ndi_devi_enter(hubd->h_dip, &circ);
1243 
1244                 switch (ds->when) {
1245                 case DDI_PRE:
1246                         USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
1247                             "DDI_PRE DDI_CTLOPS_DETACH: dip=%p port=%d",
1248                             (void *)rdip, port);
1249 
1250                         mutex_enter(HUBD_MUTEX(hubd));
1251                         hubd->h_port_state[port] |= HUBD_CHILD_DETACHING;
1252 
1253                         /* Go busy here.  Matching idle is DDI_POST case. */
1254                         (void) hubd_pm_busy_component(hubd, dip, 0);
1255 
1256                         mutex_exit(HUBD_MUTEX(hubd));
1257                         retval = DDI_SUCCESS;
1258 
1259                         break;
1260                 case DDI_POST:
1261                         USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
1262                             "DDI_POST DDI_CTLOPS_DETACH: dip=%p port=%d",
1263                             (void *)rdip, port);
1264 
1265                         mutex_enter(HUBD_MUTEX(hubd));
1266                         hubd->h_port_state[port] &= ~HUBD_CHILD_DETACHING;
1267                         mutex_exit(HUBD_MUTEX(hubd));
1268 
1269                         /* Matching idle call for DDI_PRE busy call. */
1270                         hubd_post_detach(hubd, port, (struct detachspec *)arg);
1271                         retval = DDI_SUCCESS;
1272                         mutex_enter(HUBD_MUTEX(hubd));
1273                         (void) hubd_pm_idle_component(hubd, dip, 0);
1274                         mutex_exit(HUBD_MUTEX(hubd));
1275 
1276                         break;
1277                 }
1278                 ndi_devi_exit(hubd->h_dip, circ);
1279 
1280                 break;
1281         default:
1282                 retval = usba_bus_ctl(root_hub_dip, rdip, op, arg, result);
1283         }
1284 
1285         /* decrement bus_ctls count */
1286         mutex_enter(HUBD_MUTEX(hubd));
1287         hubd->h_bus_ctls--;
1288         ASSERT(hubd->h_bus_ctls >= 0);
1289         mutex_exit(HUBD_MUTEX(hubd));
1290 
1291         return (retval);
1292 }
1293 
1294 /*
1295  * hubd_config_one:
1296  *      enumerate one child according to 'port'
1297  */
1298 
1299 static boolean_t
1300 hubd_config_one(hubd_t *hubd, int port)
1301 {
1302         uint16_t        status, change;
1303         dev_info_t      *hdip = hubd->h_dip;
1304         dev_info_t      *rh_dip = hubd->h_usba_device->usb_root_hub_dip;
1305         boolean_t       online_child = B_FALSE, found = B_FALSE;
1306         int             prh_circ, rh_circ, circ;
1307 
1308         USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
1309             "hubd_config_one:  started, hubd_reset_port = 0x%x", port);
1310 
1311         ndi_hold_devi(hdip); /* so we don't race with detach */
1312 
1313         /*
1314          * this ensures one config activity per system at a time.
1315          * we enter the parent PCI node to have this serialization.
1316          * this also excludes ioctls and deathrow thread
1317          */
1318         ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
1319         ndi_devi_enter(rh_dip, &rh_circ);
1320 
1321         /* exclude other threads */
1322         ndi_devi_enter(hdip, &circ);
1323         mutex_enter(HUBD_MUTEX(hubd));
1324 
1325         hubd_pm_busy_component(hubd, hubd->h_dip, 0);
1326 
1327         if (!hubd->h_children_dips[port]) {
1328 
1329                 (void) hubd_determine_port_status(hubd, port,
1330                     &status, &change, HUBD_ACK_ALL_CHANGES);
1331 
1332                 if (status & PORT_STATUS_CCS) {
1333                         online_child |= (hubd_handle_port_connect(hubd,
1334                             port) == USB_SUCCESS);
1335                         found = online_child;
1336                 }
1337         } else {
1338                 found = B_TRUE;
1339         }
1340 
1341         mutex_exit(HUBD_MUTEX(hubd));
1342 
1343         ndi_devi_exit(hdip, circ);
1344         ndi_devi_exit(rh_dip, rh_circ);
1345         ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ);
1346 
1347         if (online_child) {
1348                 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
1349                     "hubd_config_one: onlining child");
1350 
1351                 (void) ndi_devi_online(hubd->h_dip, 0);
1352         }
1353 
1354         mutex_enter(HUBD_MUTEX(hubd));
1355 
1356         (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
1357 
1358         USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
1359             "hubd_config_one: exit");
1360 
1361         mutex_exit(HUBD_MUTEX(hubd));
1362 
1363         ndi_rele_devi(hdip);
1364 
1365         return (found);
1366 }
1367 
1368 /*
1369  * bus enumeration entry points
1370  */
1371 static int
1372 hubd_bus_config(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
1373     void *arg, dev_info_t **child)
1374 {
1375         hubd_t  *hubd = hubd_get_soft_state(dip);
1376         int     rval, circ;
1377         long port;
1378 
1379         USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
1380             "hubd_bus_config: op=%d", op);
1381 
1382         if (hubdi_bus_config_debug) {
1383                 flag |= NDI_DEVI_DEBUG;
1384         }
1385 
1386         if (op == BUS_CONFIG_ONE) {
1387                 boolean_t found;
1388                 char cname[80];
1389                 char *name, *addr;
1390 
1391                 USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
1392                     "hubd_bus_config: op=%d (BUS_CONFIG_ONE)", op);
1393 
1394                 (void) snprintf(cname, 80, "%s", (char *)arg);
1395                 /* split name into "name@addr" parts */
1396                 i_ddi_parse_name(cname, &name, &addr, NULL);
1397                 if (addr && *addr) {
1398                         (void) ddi_strtol(addr, NULL, 16, &port);
1399                 } else {
1400                         return (NDI_FAILURE);
1401                 }
1402 
1403                 found = hubd_config_one(hubd, port);
1404 
1405                 if (found == 0) {
1406                         return (NDI_FAILURE);
1407                 }
1408 
1409         }
1410         ndi_devi_enter(hubd->h_dip, &circ);
1411         rval = ndi_busop_bus_config(dip, flag, op, arg, child, 0);
1412         ndi_devi_exit(hubd->h_dip, circ);
1413 
1414         return (rval);
1415 }
1416 
1417 
1418 static int
1419 hubd_bus_unconfig(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
1420     void *arg)
1421 {
1422         hubd_t          *hubd = hubd_get_soft_state(dip);
1423         dev_info_t      *cdip;
1424         usb_port_t      port;
1425         int             circ;
1426         int             rval;
1427 
1428         USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
1429             "hubd_bus_unconfig: op=%d", op);
1430 
1431         if (hubdi_bus_config_debug) {
1432                 flag |= NDI_DEVI_DEBUG;
1433         }
1434 
1435         if ((op == BUS_UNCONFIG_ALL) && (flag & NDI_AUTODETACH) == 0) {
1436                 flag |= NDI_DEVI_REMOVE;
1437         }
1438 
1439         /* serialize access */
1440         ndi_devi_enter(dip, &circ);
1441 
1442         rval = ndi_busop_bus_unconfig(dip, flag, op, arg);
1443 
1444         /* logically zap children's list */
1445         mutex_enter(HUBD_MUTEX(hubd));
1446         for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
1447                 hubd->h_port_state[port] |= HUBD_CHILD_ZAP;
1448         }
1449         mutex_exit(HUBD_MUTEX(hubd));
1450 
1451         /* fill in what's left */
1452         for (cdip = ddi_get_child(dip); cdip;
1453             cdip = ddi_get_next_sibling(cdip)) {
1454                 usba_device_t *usba_device = usba_get_usba_device(cdip);
1455 
1456                 if (usba_device == NULL) {
1457 
1458                         continue;
1459                 }
1460                 mutex_enter(HUBD_MUTEX(hubd));
1461                 port = usba_device->usb_port;
1462                 hubd->h_children_dips[port] = cdip;
1463                 hubd->h_port_state[port] &= ~HUBD_CHILD_ZAP;
1464                 mutex_exit(HUBD_MUTEX(hubd));
1465         }
1466 
1467         /* physically zap the children we didn't find */
1468         mutex_enter(HUBD_MUTEX(hubd));
1469         for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
1470                 if (hubd->h_port_state[port] &   HUBD_CHILD_ZAP) {
1471                         /* zap the dip and usba_device structure as well */
1472                         hubd_free_usba_device(hubd, hubd->h_usba_devices[port]);
1473                         hubd->h_children_dips[port] = NULL;
1474                         hubd->h_port_state[port] &= ~HUBD_CHILD_ZAP;
1475                 }
1476         }
1477         mutex_exit(HUBD_MUTEX(hubd));
1478 
1479         ndi_devi_exit(dip, circ);
1480 
1481         USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
1482             "hubd_bus_unconfig: rval=%d", rval);
1483 
1484         return (rval);
1485 }
1486 
1487 
1488 /* bus_power entry point */
1489 static int
1490 hubd_bus_power(dev_info_t *dip, void *impl_arg, pm_bus_power_op_t op,
1491     void *arg, void *result)
1492 {
1493         hubd_t          *hubd;
1494         int             rval, pwrup_res;
1495         usb_port_t      port;
1496         int             retval = DDI_FAILURE;
1497         pm_bp_child_pwrchg_t    *bpc;
1498         pm_bp_nexus_pwrup_t     bpn;
1499 
1500         hubd = hubd_get_soft_state(dip);
1501 
1502         USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubd->h_log_handle,
1503             "hubd_bus_power: dip=%p, impl_arg=%p, power_op=%d, arg=%p, "
1504             "result=%d\n", (void *)dip, impl_arg, op, arg, *(int *)result);
1505 
1506         bpc = (pm_bp_child_pwrchg_t *)arg;
1507 
1508         mutex_enter(HUBD_MUTEX(hubd));
1509         hubd->h_bus_pwr++;
1510         mutex_exit(HUBD_MUTEX(hubd));
1511 
1512         switch (op) {
1513         case BUS_POWER_PRE_NOTIFICATION:
1514                 port = hubd_child_dip2port(hubd, bpc->bpc_dip);
1515                 USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle,
1516                     "hubd_bus_power: BUS_POWER_PRE_NOTIFICATION, port=%d",
1517                     port);
1518 
1519                 /* go to full power if we are powered down */
1520                 mutex_enter(HUBD_MUTEX(hubd));
1521 
1522                 /*
1523                  * If this case completes normally, idle will be in
1524                  * hubd_bus_power / BUS_POWER_POST_NOTIFICATION
1525                  */
1526                 hubd_pm_busy_component(hubd, dip, 0);
1527 
1528                 /*
1529                  * raise power only if we have created the components
1530                  * and are currently in low power
1531                  */
1532                 if ((hubd->h_dev_state == USB_DEV_PWRED_DOWN) &&
1533                     hubd->h_hubpm->hubp_wakeup_enabled) {
1534                         mutex_exit(HUBD_MUTEX(hubd));
1535 
1536                         bpn.bpn_comp = 0;
1537                         bpn.bpn_dip = dip;
1538                         bpn.bpn_level = USB_DEV_OS_FULL_PWR;
1539                         bpn.bpn_private = bpc->bpc_private;
1540 
1541                         rval = pm_busop_bus_power(dip, impl_arg,
1542                             BUS_POWER_NEXUS_PWRUP, (void *)&bpn,
1543                             (void *)&pwrup_res);
1544 
1545                         if (rval != DDI_SUCCESS || pwrup_res != DDI_SUCCESS) {
1546                                 mutex_enter(HUBD_MUTEX(hubd));
1547                                 hubd_pm_idle_component(hubd, dip, 0);
1548                                 mutex_exit(HUBD_MUTEX(hubd));
1549 
1550                                 break;
1551                         }
1552                         mutex_enter(HUBD_MUTEX(hubd));
1553                 }
1554 
1555                 /* indicate that child is changing power level */
1556                 hubd->h_port_state[port] |= HUBD_CHILD_PWRLVL_CHNG;
1557                 mutex_exit(HUBD_MUTEX(hubd));
1558 
1559                 if ((bpc->bpc_olevel == 0) &&
1560                     (bpc->bpc_nlevel > bpc->bpc_olevel)) {
1561                         /*
1562                          * this child is transitioning from power off
1563                          * to power on state - resume port
1564                          */
1565                         rval = hubd_resume_port(hubd, port);
1566                         if (rval == USB_SUCCESS) {
1567                                 retval = DDI_SUCCESS;
1568                         } else {
1569                                 /* reset this flag on failure */
1570                                 mutex_enter(HUBD_MUTEX(hubd));
1571                                 hubd->h_port_state[port] &=
1572                                     ~HUBD_CHILD_PWRLVL_CHNG;
1573                                 hubd_pm_idle_component(hubd, dip, 0);
1574                                 mutex_exit(HUBD_MUTEX(hubd));
1575                         }
1576                 } else {
1577                         retval = DDI_SUCCESS;
1578                 }
1579 
1580                 break;
1581         case BUS_POWER_POST_NOTIFICATION:
1582                 port = hubd_child_dip2port(hubd, bpc->bpc_dip);
1583                 USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle,
1584                     "hubd_bus_power: BUS_POWER_POST_NOTIFICATION, port=%d",
1585                     port);
1586 
1587                 mutex_enter(HUBD_MUTEX(hubd));
1588                 hubd->h_port_state[port] &= ~HUBD_CHILD_PWRLVL_CHNG;
1589                 mutex_exit(HUBD_MUTEX(hubd));
1590 
1591                 /* record child's pwr and suspend port if required */
1592                 rval = hubd_post_power(hubd, port, bpc, *(int *)result);
1593                 if (rval == USB_SUCCESS) {
1594 
1595                         retval = DDI_SUCCESS;
1596                 }
1597 
1598                 mutex_enter(HUBD_MUTEX(hubd));
1599 
1600                 /*
1601                  * Matching idle for the busy in
1602                  * hubd_bus_power / BUS_POWER_PRE_NOTIFICATION
1603                  */
1604                 hubd_pm_idle_component(hubd, dip, 0);
1605 
1606                 mutex_exit(HUBD_MUTEX(hubd));
1607 
1608                 break;
1609         default:
1610                 retval = pm_busop_bus_power(dip, impl_arg, op, arg, result);
1611 
1612                 break;
1613         }
1614 
1615         mutex_enter(HUBD_MUTEX(hubd));
1616         hubd->h_bus_pwr--;
1617         mutex_exit(HUBD_MUTEX(hubd));
1618 
1619         return (retval);
1620 }
1621 
1622 
1623 /*
1624  * functions to handle power transition for OS levels 0 -> 3
1625  */
1626 static int
1627 hubd_pwrlvl0(hubd_t *hubd)
1628 {
1629         hub_power_t     *hubpm;
1630 
1631         /* We can't power down if hotplug thread is running */
1632         if (hubd->h_hotplug_thread || hubd->h_hubpm->hubp_busy_pm ||
1633             (hubd_can_suspend(hubd) == USB_FAILURE)) {
1634 
1635                 return (USB_FAILURE);
1636         }
1637 
1638         switch (hubd->h_dev_state) {
1639         case USB_DEV_ONLINE:
1640                 hubpm = hubd->h_hubpm;
1641 
1642                 /*
1643                  * To avoid race with bus_power pre_notify on check over
1644                  * dev_state, we need to correctly set the dev state
1645                  * before the mutex is dropped in stop polling.
1646                  */
1647                 hubd->h_dev_state = USB_DEV_PWRED_DOWN;
1648                 hubpm->hubp_current_power = USB_DEV_OS_PWR_OFF;
1649 
1650                 /*
1651                  * if we are the root hub, do not stop polling
1652                  * otherwise, we will never see a resume
1653                  */
1654                 if (usba_is_root_hub(hubd->h_dip)) {
1655                         /* place holder to implement Global Suspend */
1656                         USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
1657                             "Global Suspend: Not Yet Implemented");
1658                 } else {
1659                         hubd_stop_polling(hubd);
1660                 }
1661 
1662                 /* Issue USB D3 command to the device here */
1663                 (void) usb_set_device_pwrlvl3(hubd->h_dip);
1664 
1665                 break;
1666         case USB_DEV_DISCONNECTED:
1667         case USB_DEV_SUSPENDED:
1668         case USB_DEV_PWRED_DOWN:
1669         default:
1670 
1671                 break;
1672         }
1673 
1674         return (USB_SUCCESS);
1675 }
1676 
1677 
1678 /* ARGSUSED */
1679 static int
1680 hubd_pwrlvl1(hubd_t *hubd)
1681 {
1682         /* Issue USB D2 command to the device here */
1683         (void) usb_set_device_pwrlvl2(hubd->h_dip);
1684 
1685         return (USB_FAILURE);
1686 }
1687 
1688 
1689 /* ARGSUSED */
1690 static int
1691 hubd_pwrlvl2(hubd_t *hubd)
1692 {
1693         /* Issue USB D1 command to the device here */
1694         (void) usb_set_device_pwrlvl1(hubd->h_dip);
1695 
1696         return (USB_FAILURE);
1697 }
1698 
1699 
1700 static int
1701 hubd_pwrlvl3(hubd_t *hubd)
1702 {
1703         hub_power_t     *hubpm;
1704         int             rval;
1705 
1706         USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, "hubd_pwrlvl3");
1707 
1708         hubpm = hubd->h_hubpm;
1709         switch (hubd->h_dev_state) {
1710         case USB_DEV_PWRED_DOWN:
1711                 ASSERT(hubpm->hubp_current_power == USB_DEV_OS_PWR_OFF);
1712                 if (usba_is_root_hub(hubd->h_dip)) {
1713                         /* implement global resume here */
1714                         USB_DPRINTF_L2(DPRINT_MASK_PM,
1715                             hubd->h_log_handle,
1716                             "Global Resume: Not Yet Implemented");
1717                 }
1718                 /* Issue USB D0 command to the device here */
1719                 rval = usb_set_device_pwrlvl0(hubd->h_dip);
1720                 ASSERT(rval == USB_SUCCESS);
1721                 hubd->h_dev_state = USB_DEV_ONLINE;
1722                 hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR;
1723                 hubpm->hubp_time_at_full_power = ddi_get_time();
1724                 hubd_start_polling(hubd, 0);
1725 
1726                 /* FALLTHRU */
1727         case USB_DEV_ONLINE:
1728                 /* we are already in full power */
1729 
1730                 /* FALLTHRU */
1731         case USB_DEV_DISCONNECTED:
1732         case USB_DEV_SUSPENDED:
1733                 /*
1734                  * PM framework tries to put you in full power
1735                  * during system shutdown. If we are disconnected
1736                  * return success. Also, we should not change state
1737                  * when we are disconnected or suspended or about to
1738                  * transition to that state
1739                  */
1740 
1741                 return (USB_SUCCESS);
1742         default:
1743                 USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
1744                     "hubd_pwrlvl3: Illegal dev_state=%d", hubd->h_dev_state);
1745 
1746                 return (USB_FAILURE);
1747         }
1748 }
1749 
1750 
1751 /* power entry point */
1752 /* ARGSUSED */
1753 int
1754 usba_hubdi_power(dev_info_t *dip, int comp, int level)
1755 {
1756         hubd_t          *hubd;
1757         hub_power_t     *hubpm;
1758         int             retval;
1759         int             circ;
1760 
1761         hubd = hubd_get_soft_state(dip);
1762         USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle,
1763             "usba_hubdi_power: level=%d", level);
1764 
1765         ndi_devi_enter(dip, &circ);
1766 
1767         mutex_enter(HUBD_MUTEX(hubd));
1768         hubpm = hubd->h_hubpm;
1769 
1770         /* check if we are transitioning to a legal power level */
1771         if (USB_DEV_PWRSTATE_OK(hubpm->hubp_pwr_states, level)) {
1772                 USB_DPRINTF_L2(DPRINT_MASK_HUBDI, hubd->h_log_handle,
1773                     "usba_hubdi_power: illegal power level=%d "
1774                     "hubp_pwr_states=0x%x", level, hubpm->hubp_pwr_states);
1775                 mutex_exit(HUBD_MUTEX(hubd));
1776 
1777                 ndi_devi_exit(dip, circ);
1778 
1779                 return (DDI_FAILURE);
1780         }
1781 
1782         switch (level) {
1783         case USB_DEV_OS_PWR_OFF:
1784                 retval = hubd_pwrlvl0(hubd);
1785 
1786                 break;
1787         case USB_DEV_OS_PWR_1:
1788                 retval = hubd_pwrlvl1(hubd);
1789 
1790                 break;
1791         case USB_DEV_OS_PWR_2:
1792                 retval = hubd_pwrlvl2(hubd);
1793 
1794                 break;
1795         case USB_DEV_OS_FULL_PWR:
1796                 retval = hubd_pwrlvl3(hubd);
1797 
1798                 break;
1799         }
1800         mutex_exit(HUBD_MUTEX(hubd));
1801 
1802         ndi_devi_exit(dip, circ);
1803 
1804         return ((retval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
1805 }
1806 
1807 
1808 /* power entry point for the root hub */
1809 int
1810 usba_hubdi_root_hub_power(dev_info_t *dip, int comp, int level)
1811 {
1812         return (usba_hubdi_power(dip, comp, level));
1813 }
1814 
1815 
1816 /*
1817  * standard driver entry points support code
1818  */
1819 int
1820 usba_hubdi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1821 {
1822         int                     instance = ddi_get_instance(dip);
1823         hubd_t                  *hubd = NULL;
1824         int                     i, rval;
1825         int                     minor;
1826         uint8_t                 ports_count;
1827         char                    *log_name = NULL;
1828         const char              *root_hub_drvname;
1829         usb_ep_data_t           *ep_data;
1830         usba_device_t           *child_ud = NULL;
1831         usb_dev_descr_t         *usb_dev_descr;
1832         usb_port_status_t       parent_port_status, child_port_status;
1833 
1834         USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubdi_log_handle,
1835             "hubd_attach instance %d, cmd=0x%x", instance, cmd);
1836 
1837         switch (cmd) {
1838         case DDI_ATTACH:
1839 
1840                 break;
1841         case DDI_RESUME:
1842                 hubd_cpr_resume(dip);
1843 
1844                 return (DDI_SUCCESS);
1845         default:
1846                 return (DDI_FAILURE);
1847         }
1848 
1849         /*
1850          * Allocate softc information.
1851          */
1852         if (usba_is_root_hub(dip)) {
1853                 /* soft state has already been allocated */
1854                 hubd = hubd_get_soft_state(dip);
1855                 minor = HUBD_IS_ROOT_HUB;
1856 
1857                 /* generate readable labels for different root hubs */
1858                 root_hub_drvname = ddi_driver_name(dip);
1859                 if (strcmp(root_hub_drvname, "ehci") == 0) {
1860                         log_name = "eusb";
1861                 } else if (strcmp(root_hub_drvname, "uhci") == 0) {
1862                         log_name = "uusb";
1863                 } else {
1864                         /* std. for ohci */
1865                         log_name = "usb";
1866                 }
1867         } else {
1868                 rval = ddi_soft_state_zalloc(hubd_statep, instance);
1869                 minor = 0;
1870 
1871                 if (rval != DDI_SUCCESS) {
1872                         USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
1873                             "cannot allocate soft state (%d)", instance);
1874                         goto fail;
1875                 }
1876 
1877                 hubd = hubd_get_soft_state(dip);
1878                 if (hubd == NULL) {
1879                         goto fail;
1880                 }
1881         }
1882 
1883         hubd->h_log_handle = usb_alloc_log_hdl(dip, log_name, &hubd_errlevel,
1884             &hubd_errmask, &hubd_instance_debug, 0);
1885 
1886         hubd->h_usba_device  = child_ud = usba_get_usba_device(dip);
1887         hubd->h_dip          = dip;
1888         hubd->h_instance     = instance;
1889 
1890         mutex_enter(&child_ud->usb_mutex);
1891         child_port_status = child_ud->usb_port_status;
1892         usb_dev_descr = child_ud->usb_dev_descr;
1893         parent_port_status = (child_ud->usb_hs_hub_usba_dev) ?
1894             child_ud->usb_hs_hub_usba_dev->usb_port_status : 0;
1895         mutex_exit(&child_ud->usb_mutex);
1896 
1897         if ((child_port_status == USBA_FULL_SPEED_DEV) &&
1898             (parent_port_status == USBA_HIGH_SPEED_DEV) &&
1899             (usb_dev_descr->bcdUSB == 0x100)) {
1900                 USB_DPRINTF_L0(DPRINT_MASK_ATTA, hubd->h_log_handle,
1901                     "Use of a USB1.0 hub behind a high speed port may "
1902                     "cause unexpected failures");
1903         }
1904 
1905         hubd->h_pipe_policy.pp_max_async_reqs = 1;
1906 
1907         /* register with USBA as client driver */
1908         if (usb_client_attach(dip, USBDRV_VERSION, 0) != USB_SUCCESS) {
1909                 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
1910                     "client attach failed");
1911 
1912                 goto fail;
1913         }
1914 
1915         if (usb_get_dev_data(dip, &hubd->h_dev_data,
1916             USB_PARSE_LVL_IF, 0) != USB_SUCCESS) {
1917                 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
1918                     "cannot get dev_data");
1919 
1920                 goto fail;
1921         }
1922 
1923         if ((ep_data = usb_lookup_ep_data(dip, hubd->h_dev_data,
1924             hubd->h_dev_data->dev_curr_if, 0, 0,
1925             (uint_t)USB_EP_ATTR_INTR, (uint_t)USB_EP_DIR_IN)) == NULL) {
1926                 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
1927                     "no interrupt IN endpoint found");
1928 
1929                 goto fail;
1930         }
1931 
1932         hubd->h_ep1_descr = ep_data->ep_descr;
1933         hubd->h_default_pipe = hubd->h_dev_data->dev_default_ph;
1934 
1935         mutex_init(HUBD_MUTEX(hubd), NULL, MUTEX_DRIVER,
1936             hubd->h_dev_data->dev_iblock_cookie);
1937         cv_init(&hubd->h_cv_reset_port, NULL, CV_DRIVER, NULL);
1938         cv_init(&hubd->h_cv_hotplug_dev, NULL, CV_DRIVER, NULL);
1939 
1940         hubd->h_init_state |= HUBD_LOCKS_DONE;
1941 
1942         usb_free_descr_tree(dip, hubd->h_dev_data);
1943 
1944         /*
1945          * register this hub instance with usba
1946          */
1947         rval = usba_hubdi_register(dip, 0);
1948         if (rval != USB_SUCCESS) {
1949                 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
1950                     "usba_hubdi_register failed");
1951                 goto fail;
1952         }
1953 
1954         mutex_enter(HUBD_MUTEX(hubd));
1955         hubd->h_init_state |= HUBD_HUBDI_REGISTERED;
1956         hubd->h_dev_state = USB_DEV_ONLINE;
1957         mutex_exit(HUBD_MUTEX(hubd));
1958 
1959         /* now create components to power manage this device */
1960         hubd_create_pm_components(dip, hubd);
1961 
1962         /*
1963          * Event handling: definition and registration
1964          *
1965          * first the  definition:
1966          * get event handle
1967          */
1968         (void) ndi_event_alloc_hdl(dip, 0, &hubd->h_ndi_event_hdl, NDI_SLEEP);
1969 
1970         /* bind event set to the handle */
1971         if (ndi_event_bind_set(hubd->h_ndi_event_hdl, &hubd_ndi_events,
1972             NDI_SLEEP)) {
1973                 USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
1974                     "binding event set failed");
1975 
1976                 goto fail;
1977         }
1978 
1979         /* event registration */
1980         if (hubd_register_events(hubd) != USB_SUCCESS) {
1981                 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
1982                     "hubd_register_events failed");
1983 
1984                 goto fail;
1985         }
1986 
1987         mutex_enter(HUBD_MUTEX(hubd));
1988         hubd->h_init_state |= HUBD_EVENTS_REGISTERED;
1989 
1990         if ((hubd_get_hub_descriptor(hubd)) != USB_SUCCESS) {
1991                 mutex_exit(HUBD_MUTEX(hubd));
1992 
1993                 goto fail;
1994         }
1995 
1996         if (ddi_prop_exists(DDI_DEV_T_ANY, dip,
1997             (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
1998             "hub-ignore-power-budget") == 1) {
1999                 hubd->h_ignore_pwr_budget = B_TRUE;
2000         } else {
2001                 hubd->h_ignore_pwr_budget = B_FALSE;
2002 
2003                 /* initialize hub power budget variables */
2004                 if (hubd_init_power_budget(hubd) != USB_SUCCESS) {
2005                         USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
2006                             "hubd_init_power_budget failed");
2007                         mutex_exit(HUBD_MUTEX(hubd));
2008 
2009                         goto fail;
2010                 }
2011         }
2012 
2013         /* initialize and create children */
2014         if (hubd_check_ports(hubd) != USB_SUCCESS) {
2015                 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
2016                     "hubd_check_ports failed");
2017                 mutex_exit(HUBD_MUTEX(hubd));
2018 
2019                 goto fail;
2020         }
2021 
2022         /*
2023          * create cfgadm nodes
2024          */
2025         hubd->h_ancestry_str = (char *)kmem_zalloc(HUBD_APID_NAMELEN, KM_SLEEP);
2026         hubd_get_ancestry_str(hubd);
2027 
2028         USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
2029             "#ports=0x%x", hubd->h_hub_descr.bNbrPorts);
2030 
2031         for (i = 1; i <= hubd->h_hub_descr.bNbrPorts; i++) {
2032                 char ap_name[HUBD_APID_NAMELEN];
2033 
2034                 (void) snprintf(ap_name, HUBD_APID_NAMELEN, "%s%d",
2035                     hubd->h_ancestry_str, i);
2036                 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
2037                     "ap_name=%s", ap_name);
2038 
2039                 if (ddi_create_minor_node(dip, ap_name, S_IFCHR, instance,
2040                     DDI_NT_USB_ATTACHMENT_POINT, 0) != DDI_SUCCESS) {
2041                         USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
2042                             "cannot create attachment point node (%d)",
2043                             instance);
2044                         mutex_exit(HUBD_MUTEX(hubd));
2045 
2046                         goto fail;
2047                 }
2048         }
2049 
2050         ports_count = hubd->h_hub_descr.bNbrPorts;
2051         mutex_exit(HUBD_MUTEX(hubd));
2052 
2053         /* create minor nodes */
2054         if (ddi_create_minor_node(dip, "hubd", S_IFCHR,
2055             instance | minor, DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
2056 
2057                 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
2058                     "cannot create devctl minor node (%d)", instance);
2059 
2060                 goto fail;
2061         }
2062 
2063         mutex_enter(HUBD_MUTEX(hubd));
2064         hubd->h_init_state |= HUBD_MINOR_NODE_CREATED;
2065         mutex_exit(HUBD_MUTEX(hubd));
2066 
2067         if (ndi_prop_update_int(DDI_DEV_T_NONE, dip,
2068             "usb-port-count", ports_count) != DDI_PROP_SUCCESS) {
2069                 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
2070                     "usb-port-count update failed");
2071         }
2072 
2073         /*
2074          * host controller driver has already reported this dev
2075          * if we are the root hub
2076          */
2077         if (!usba_is_root_hub(dip)) {
2078                 ddi_report_dev(dip);
2079         }
2080 
2081         /* enable deathrow thread */
2082         hubd->h_cleanup_enabled = B_TRUE;
2083         mutex_enter(HUBD_MUTEX(hubd));
2084         hubd_pm_idle_component(hubd, dip, 0);
2085         mutex_exit(HUBD_MUTEX(hubd));
2086 
2087         return (DDI_SUCCESS);
2088 
2089 fail:
2090         {
2091                 char *pathname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
2092 
2093                 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
2094                     "cannot attach %s", ddi_pathname(dip, pathname));
2095 
2096                 kmem_free(pathname, MAXPATHLEN);
2097         }
2098 
2099         mutex_enter(HUBD_MUTEX(hubd));
2100         hubd_pm_idle_component(hubd, dip, 0);
2101         mutex_exit(HUBD_MUTEX(hubd));
2102 
2103         if (hubd) {
2104                 rval = hubd_cleanup(dip, hubd);
2105                 if (rval != USB_SUCCESS) {
2106                         USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
2107                             "failure to complete cleanup after attach failure");
2108                 }
2109         }
2110 
2111         return (DDI_FAILURE);
2112 }
2113 
2114 
2115 int
2116 usba_hubdi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
2117 {
2118         hubd_t  *hubd = hubd_get_soft_state(dip);
2119         int     rval;
2120 
2121         USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
2122             "hubd_detach: cmd=0x%x", cmd);
2123 
2124         switch (cmd) {
2125         case DDI_DETACH:
2126                 rval = hubd_cleanup(dip, hubd);
2127 
2128                 return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
2129         case DDI_SUSPEND:
2130                 rval = hubd_cpr_suspend(hubd);
2131 
2132                 return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
2133         default:
2134                 return (DDI_FAILURE);
2135         }
2136 }
2137 
2138 
2139 /*
2140  * hubd_setdevaddr
2141  *      set the device addrs on this port
2142  */
2143 static int
2144 hubd_setdevaddr(hubd_t *hubd, usb_port_t port)
2145 {
2146         int             rval;
2147         usb_cr_t        completion_reason;
2148         usb_cb_flags_t  cb_flags;
2149         usb_pipe_handle_t ph;
2150         dev_info_t      *child_dip = NULL;
2151         uchar_t         address = 0;
2152         usba_device_t   *usba_device;
2153         int             retry = 0;
2154         long            time_delay;
2155 
2156         USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
2157             "hubd_setdevaddr: port=%d", port);
2158 
2159         ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
2160 
2161         child_dip = hubd->h_children_dips[port];
2162         address = hubd->h_usba_devices[port]->usb_addr;
2163         usba_device = hubd->h_usba_devices[port];
2164 
2165         /* close the default pipe with addr x */
2166         mutex_exit(HUBD_MUTEX(hubd));
2167         ph = usba_get_dflt_pipe_handle(child_dip);
2168         usb_pipe_close(child_dip, ph,
2169             USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
2170         mutex_enter(HUBD_MUTEX(hubd));
2171 
2172         /*
2173          * As this device has been reset, temporarily
2174          * assign the default address
2175          */
2176         mutex_enter(&usba_device->usb_mutex);
2177         address = usba_device->usb_addr;
2178         usba_device->usb_addr = USBA_DEFAULT_ADDR;
2179         mutex_exit(&usba_device->usb_mutex);
2180 
2181         mutex_exit(HUBD_MUTEX(hubd));
2182 
2183         time_delay = drv_usectohz(hubd_device_delay / 20);
2184         for (retry = 0; retry < hubd_retry_enumerate; retry++) {
2185 
2186                 /* open child's default pipe with USBA_DEFAULT_ADDR */
2187                 if (usb_pipe_open(child_dip, NULL, NULL,
2188                     USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph) !=
2189                     USB_SUCCESS) {
2190                         USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
2191                             "hubd_setdevaddr: Unable to open default pipe");
2192 
2193                         break;
2194                 }
2195 
2196                 /* Set the address of the device */
2197                 if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
2198                     USB_DEV_REQ_HOST_TO_DEV,
2199                     USB_REQ_SET_ADDRESS,        /* bRequest */
2200                     address,                    /* wValue */
2201                     0,                          /* wIndex */
2202                     0,                          /* wLength */
2203                     NULL, 0,
2204                     &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
2205                         USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
2206                             "hubd_setdevaddr(%d): rval=%d cr=%d cb_fl=0x%x",
2207                             retry, rval, completion_reason, cb_flags);
2208                 }
2209 
2210                 usb_pipe_close(child_dip, ph,
2211                     USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
2212 
2213                 if (rval == USB_SUCCESS) {
2214 
2215                         break;
2216                 }
2217 
2218                 delay(time_delay);
2219         }
2220 
2221         /* Reset to the old address */
2222         mutex_enter(&usba_device->usb_mutex);
2223         usba_device->usb_addr = address;
2224         mutex_exit(&usba_device->usb_mutex);
2225         mutex_enter(HUBD_MUTEX(hubd));
2226 
2227         usba_clear_data_toggle(usba_device);
2228 
2229         return (rval);
2230 }
2231 
2232 
2233 /*
2234  * hubd_setdevconfig
2235  *      set the device addrs on this port
2236  */
2237 static void
2238 hubd_setdevconfig(hubd_t *hubd, usb_port_t port)
2239 {
2240         int                     rval;
2241         usb_cr_t                completion_reason;
2242         usb_cb_flags_t          cb_flags;
2243         usb_pipe_handle_t       ph;
2244         dev_info_t              *child_dip = NULL;
2245         usba_device_t           *usba_device = NULL;
2246         uint16_t                config_value;
2247 
2248         USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
2249             "hubd_setdevconfig: port=%d", port);
2250 
2251         ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
2252 
2253         child_dip = hubd->h_children_dips[port];
2254         usba_device = hubd->h_usba_devices[port];
2255         config_value = hubd->h_usba_devices[port]->usb_cfg_value;
2256         mutex_exit(HUBD_MUTEX(hubd));
2257 
2258         /* open the default control pipe */
2259         if ((rval = usb_pipe_open(child_dip, NULL, NULL,
2260             USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) ==
2261             USB_SUCCESS) {
2262 
2263                 /* Set the default configuration of the device */
2264                 if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
2265                     USB_DEV_REQ_HOST_TO_DEV,
2266                     USB_REQ_SET_CFG,            /* bRequest */
2267                     config_value,               /* wValue */
2268                     0,                          /* wIndex */
2269                     0,                          /* wLength */
2270                     NULL, 0,
2271                     &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
2272                         USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
2273                             "hubd_setdevconfig: set device config failed: "
2274                             "cr=%d cb_fl=0x%x rval=%d",
2275                             completion_reason, cb_flags, rval);
2276                 }
2277                 /*
2278                  * After setting the configuration, we make this default
2279                  * control pipe persistent, so that it gets re-opened
2280                  * on posting a connect event
2281                  */
2282                 usba_persistent_pipe_close(usba_device);
2283         } else {
2284                 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
2285                     "pipe open fails: rval=%d", rval);
2286         }
2287         mutex_enter(HUBD_MUTEX(hubd));
2288 }
2289 
2290 
2291 /*ARGSUSED*/
2292 static int
2293 hubd_check_disconnected_ports(dev_info_t *dip, void *arg)
2294 {
2295         int circ;
2296         usb_port_t port;
2297         hubd_t *hubd;
2298         major_t hub_major = ddi_name_to_major("hubd");
2299         major_t hwahc_major = ddi_name_to_major("hwahc");
2300         major_t usbmid_major = ddi_name_to_major("usb_mid");
2301 
2302         /*
2303          * make sure dip is a usb hub, major of root hub is HCD
2304          * major
2305          */
2306         if (!usba_is_root_hub(dip)) {
2307                 if (ddi_driver_major(dip) == usbmid_major) {
2308                         /*
2309                          * need to walk the children since it might be a
2310                          * HWA device
2311                          */
2312 
2313                         return (DDI_WALK_CONTINUE);
2314                 }
2315 
2316                 /* TODO: DWA device may also need special handling */
2317 
2318                 if (((ddi_driver_major(dip) != hub_major) &&
2319                     (ddi_driver_major(dip) != hwahc_major)) ||
2320                     !i_ddi_devi_attached(dip)) {
2321 
2322                         return (DDI_WALK_PRUNECHILD);
2323                 }
2324         }
2325 
2326         hubd = hubd_get_soft_state(dip);
2327         if (hubd == NULL) {
2328 
2329                 return (DDI_WALK_PRUNECHILD);
2330         }
2331 
2332         /* walk child list and remove nodes with flag DEVI_DEVICE_REMOVED */
2333         ndi_devi_enter(dip, &circ);
2334 
2335         if (ddi_driver_major(dip) != hwahc_major) {
2336                 /* for normal usb hub or root hub */
2337                 mutex_enter(HUBD_MUTEX(hubd));
2338                 for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
2339                         dev_info_t *cdip = hubd->h_children_dips[port];
2340 
2341                         if (cdip == NULL || DEVI_IS_DEVICE_REMOVED(cdip) == 0) {
2342 
2343                                 continue;
2344                         }
2345 
2346                         (void) hubd_delete_child(hubd, port, NDI_DEVI_REMOVE,
2347                             B_TRUE);
2348                 }
2349                 mutex_exit(HUBD_MUTEX(hubd));
2350         } else {
2351                 /* for HWA */
2352                 if (hubd->h_cleanup_child != NULL) {
2353                         if (hubd->h_cleanup_child(dip) != USB_SUCCESS) {
2354                                 ndi_devi_exit(dip, circ);
2355 
2356                                 return (DDI_WALK_PRUNECHILD);
2357                         }
2358                 } else {
2359                         ndi_devi_exit(dip, circ);
2360 
2361                         return (DDI_WALK_PRUNECHILD);
2362                 }
2363         }
2364 
2365         ndi_devi_exit(dip, circ);
2366 
2367         /* skip siblings of root hub */
2368         if (usba_is_root_hub(dip)) {
2369 
2370                 return (DDI_WALK_PRUNESIB);
2371         }
2372 
2373         return (DDI_WALK_CONTINUE);
2374 }
2375 
2376 
2377 /*
2378  * this thread will walk all children under the root hub for this
2379  * USB bus instance and attempt to remove them
2380  */
2381 static void
2382 hubd_root_hub_cleanup_thread(void *arg)
2383 {
2384         int circ;
2385         hubd_t *root_hubd = (hubd_t *)arg;
2386         dev_info_t *rh_dip = root_hubd->h_dip;
2387 #ifndef __lock_lint
2388         callb_cpr_t cprinfo;
2389 
2390         CALLB_CPR_INIT(&cprinfo, HUBD_MUTEX(root_hubd), callb_generic_cpr,
2391             "USB root hub");
2392 #endif
2393 
2394         for (;;) {
2395                 /* don't race with detach */
2396                 ndi_hold_devi(rh_dip);
2397 
2398                 mutex_enter(HUBD_MUTEX(root_hubd));
2399                 root_hubd->h_cleanup_needed = 0;
2400                 mutex_exit(HUBD_MUTEX(root_hubd));
2401 
2402                 (void) devfs_clean(rh_dip, NULL, 0);
2403 
2404                 ndi_devi_enter(ddi_get_parent(rh_dip), &circ);
2405                 ddi_walk_devs(rh_dip, hubd_check_disconnected_ports,
2406                     NULL);
2407 #ifdef __lock_lint
2408                 (void) hubd_check_disconnected_ports(rh_dip, NULL);
2409 #endif
2410                 ndi_devi_exit(ddi_get_parent(rh_dip), circ);
2411 
2412                 /* quit if we are not enabled anymore */
2413                 mutex_enter(HUBD_MUTEX(root_hubd));
2414                 if ((root_hubd->h_cleanup_enabled == B_FALSE) ||
2415                     (root_hubd->h_cleanup_needed == B_FALSE)) {
2416                         root_hubd->h_cleanup_active = B_FALSE;
2417                         mutex_exit(HUBD_MUTEX(root_hubd));
2418                         ndi_rele_devi(rh_dip);
2419 
2420                         break;
2421                 }
2422                 mutex_exit(HUBD_MUTEX(root_hubd));
2423                 ndi_rele_devi(rh_dip);
2424 
2425 #ifndef __lock_lint
2426                 mutex_enter(HUBD_MUTEX(root_hubd));
2427                 CALLB_CPR_SAFE_BEGIN(&cprinfo);
2428                 mutex_exit(HUBD_MUTEX(root_hubd));
2429 
2430                 delay(drv_usectohz(hubd_dip_cleanup_delay));
2431 
2432                 mutex_enter(HUBD_MUTEX(root_hubd));
2433                 CALLB_CPR_SAFE_END(&cprinfo, HUBD_MUTEX(root_hubd));
2434                 mutex_exit(HUBD_MUTEX(root_hubd));
2435 #endif
2436         }
2437 
2438 #ifndef __lock_lint
2439         mutex_enter(HUBD_MUTEX(root_hubd));
2440         CALLB_CPR_EXIT(&cprinfo);
2441 #endif
2442 }
2443 
2444 
2445 void
2446 hubd_schedule_cleanup(dev_info_t *rh_dip)
2447 {
2448         hubd_t  *root_hubd;
2449 
2450         /*
2451          * The usb_root_hub_dip pointer for the child hub of the WUSB
2452          * wire adapter class device points to the wire adapter, not
2453          * the root hub. Need to find the real root hub dip so that
2454          * the cleanup thread only starts from the root hub.
2455          */
2456         while (!usba_is_root_hub(rh_dip)) {
2457                 root_hubd = hubd_get_soft_state(rh_dip);
2458                 if (root_hubd != NULL) {
2459                         rh_dip = root_hubd->h_usba_device->usb_root_hub_dip;
2460                         if (rh_dip == NULL) {
2461                                 USB_DPRINTF_L2(DPRINT_MASK_ATTA,
2462                                     root_hubd->h_log_handle,
2463                                     "hubd_schedule_cleanup: null rh dip");
2464 
2465                                 return;
2466                         }
2467                 } else {
2468                         USB_DPRINTF_L2(DPRINT_MASK_ATTA,
2469                             root_hubd->h_log_handle,
2470                             "hubd_schedule_cleanup: cannot find root hub");
2471 
2472                         return;
2473                 }
2474         }
2475         root_hubd = hubd_get_soft_state(rh_dip);
2476 
2477         mutex_enter(HUBD_MUTEX(root_hubd));
2478         root_hubd->h_cleanup_needed = B_TRUE;
2479         if (root_hubd->h_cleanup_enabled && !(root_hubd->h_cleanup_active)) {
2480                 root_hubd->h_cleanup_active = B_TRUE;
2481                 mutex_exit(HUBD_MUTEX(root_hubd));
2482                 (void) thread_create(NULL, 0,
2483                     hubd_root_hub_cleanup_thread,
2484                     (void *)root_hubd, 0, &p0, TS_RUN,
2485                     minclsyspri);
2486         } else {
2487                 mutex_exit(HUBD_MUTEX(root_hubd));
2488         }
2489 }
2490 
2491 
2492 /*
2493  * hubd_restore_device_state:
2494  *      - set config for the hub
2495  *      - power cycle all the ports
2496  *      - for each port that was connected
2497  *              - reset port
2498  *              - assign addrs to the device on this port
2499  *      - restart polling
2500  *      - reset suspend flag
2501  */
2502 static void
2503 hubd_restore_device_state(dev_info_t *dip, hubd_t *hubd)
2504 {
2505         int             rval;
2506         int             retry;
2507         uint_t          hub_prev_state;
2508         usb_port_t      port;
2509         uint16_t        status;
2510         uint16_t        change;
2511         dev_info_t      *ch_dip;
2512         boolean_t       ehci_root_hub;
2513 
2514         USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
2515             "hubd_restore_device_state:");
2516 
2517         mutex_enter(HUBD_MUTEX(hubd));
2518         hub_prev_state = hubd->h_dev_state;
2519         ASSERT(hub_prev_state != USB_DEV_PWRED_DOWN);
2520 
2521         /* First bring the device to full power */
2522         (void) hubd_pm_busy_component(hubd, dip, 0);
2523         mutex_exit(HUBD_MUTEX(hubd));
2524 
2525         (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
2526 
2527         if (!usba_is_root_hub(dip) &&
2528             (usb_check_same_device(dip, hubd->h_log_handle, USB_LOG_L0,
2529             DPRINT_MASK_HOTPLUG,
2530             USB_CHK_BASIC|USB_CHK_CFG, NULL) != USB_SUCCESS)) {
2531 
2532                 /* change the device state to disconnected */
2533                 mutex_enter(HUBD_MUTEX(hubd));
2534                 hubd->h_dev_state = USB_DEV_DISCONNECTED;
2535                 (void) hubd_pm_idle_component(hubd, dip, 0);
2536                 mutex_exit(HUBD_MUTEX(hubd));
2537 
2538                 return;
2539         }
2540 
2541         ehci_root_hub = (strcmp(ddi_driver_name(dip), "ehci") == 0);
2542 
2543         mutex_enter(HUBD_MUTEX(hubd));
2544         /* First turn off all port power */
2545         rval = hubd_disable_all_port_power(hubd);
2546         if (rval != USB_SUCCESS) {
2547                 USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
2548                     "hubd_restore_device_state:"
2549                     "turning off port power failed");
2550         }
2551 
2552         /* Settling time before turning on again */
2553         mutex_exit(HUBD_MUTEX(hubd));
2554         delay(drv_usectohz(hubd_device_delay / 100));
2555         mutex_enter(HUBD_MUTEX(hubd));
2556 
2557         /* enable power on all ports so we can see connects */
2558         if (hubd_enable_all_port_power(hubd) != USB_SUCCESS) {
2559                 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
2560                     "hubd_restore_device_state: turn on port power failed");
2561 
2562                 /* disable whatever was enabled */
2563                 (void) hubd_disable_all_port_power(hubd);
2564 
2565                 (void) hubd_pm_idle_component(hubd, dip, 0);
2566                 mutex_exit(HUBD_MUTEX(hubd));
2567 
2568                 return;
2569         }
2570 
2571         /*
2572          * wait at least 3 frames before accessing devices
2573          * (note that delay's minimal time is one clock tick which
2574          * is 10ms unless hires_tick has been changed)
2575          */
2576         mutex_exit(HUBD_MUTEX(hubd));
2577         delay(drv_usectohz(10000));
2578         mutex_enter(HUBD_MUTEX(hubd));
2579 
2580         hubd->h_dev_state = USB_DEV_HUB_STATE_RECOVER;
2581 
2582         for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
2583                 USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
2584                     "hubd_restore_device_state: port=%d", port);
2585 
2586                 /*
2587                  * the childen_dips list may have dips that have been
2588                  * already deallocated. we only get a post_detach notification
2589                  * but not a destroy notification
2590                  */
2591                 ch_dip = hubd->h_children_dips[port];
2592                 if (ch_dip) {
2593                         /* get port status */
2594                         (void) hubd_determine_port_status(hubd, port,
2595                             &status, &change, PORT_CHANGE_CSC);
2596 
2597                         /* check if it is truly connected */
2598                         if (status & PORT_STATUS_CCS) {
2599                                 /*
2600                                  * Now reset port and assign the device
2601                                  * its original address
2602                                  */
2603                                 retry = 0;
2604                                 do {
2605                                         (void) hubd_reset_port(hubd, port);
2606 
2607                                         /* required for ppx */
2608                                         (void) hubd_enable_port(hubd, port);
2609 
2610                                         if (retry) {
2611                                                 mutex_exit(HUBD_MUTEX(hubd));
2612                                                 delay(drv_usectohz(
2613                                                     hubd_device_delay/2));
2614                                                 mutex_enter(HUBD_MUTEX(hubd));
2615                                         }
2616 
2617                                         rval = hubd_setdevaddr(hubd, port);
2618                                         retry++;
2619                                 } while ((rval != USB_SUCCESS) &&
2620                                     (retry < hubd_retry_enumerate));
2621 
2622                                 hubd_setdevconfig(hubd, port);
2623 
2624                                 if (hub_prev_state == USB_DEV_DISCONNECTED) {
2625                                         /* post a connect event */
2626                                         mutex_exit(HUBD_MUTEX(hubd));
2627                                         hubd_post_event(hubd, port,
2628                                             USBA_EVENT_TAG_HOT_INSERTION);
2629                                         mutex_enter(HUBD_MUTEX(hubd));
2630                                 } else {
2631                                         /*
2632                                          * Since we have this device connected
2633                                          * mark it reinserted to prevent
2634                                          * cleanup thread from stepping in.
2635                                          */
2636                                         mutex_exit(HUBD_MUTEX(hubd));
2637                                         mutex_enter(&(DEVI(ch_dip)->devi_lock));
2638                                         DEVI_SET_DEVICE_REINSERTED(ch_dip);
2639                                         mutex_exit(&(DEVI(ch_dip)->devi_lock));
2640 
2641                                         /*
2642                                          * reopen pipes for children for
2643                                          * their DDI_RESUME
2644                                          */
2645                                         rval = usba_persistent_pipe_open(
2646                                             usba_get_usba_device(ch_dip));
2647                                         mutex_enter(HUBD_MUTEX(hubd));
2648                                         ASSERT(rval == USB_SUCCESS);
2649                                 }
2650                         } else {
2651                                 /*
2652                                  * Mark this dip for deletion as the device
2653                                  * is not physically present, and schedule
2654                                  * cleanup thread upon post resume
2655                                  */
2656                                 mutex_exit(HUBD_MUTEX(hubd));
2657 
2658                                 USB_DPRINTF_L2(DPRINT_MASK_ATTA,
2659                                     hubd->h_log_handle,
2660                                     "hubd_restore_device_state: "
2661                                     "dip=%p on port=%d marked for cleanup",
2662                                     (void *)ch_dip, port);
2663                                 mutex_enter(&(DEVI(ch_dip)->devi_lock));
2664                                 DEVI_SET_DEVICE_REMOVED(ch_dip);
2665                                 mutex_exit(&(DEVI(ch_dip)->devi_lock));
2666 
2667                                 mutex_enter(HUBD_MUTEX(hubd));
2668                         }
2669                 } else if (ehci_root_hub) {
2670                         /* get port status */
2671                         (void) hubd_determine_port_status(hubd, port,
2672                             &status, &change, PORT_CHANGE_CSC);
2673 
2674                         /* check if it is truly connected */
2675                         if (status & PORT_STATUS_CCS) {
2676                                 /*
2677                                  * reset the port to find out if we have
2678                                  * 2.0 device connected or 1.X. A 2.0
2679                                  * device will still be seen as connected,
2680                                  * while a 1.X device will switch over to
2681                                  * the companion controller.
2682                                  */
2683                                 (void) hubd_reset_port(hubd, port);
2684 
2685                                 (void) hubd_determine_port_status(hubd, port,
2686                                     &status, &change, PORT_CHANGE_CSC);
2687 
2688                                 if (status &
2689                                     (PORT_STATUS_CCS | PORT_STATUS_HSDA)) {
2690                                         /*
2691                                          * We have a USB 2.0 device
2692                                          * connected. Power cycle this port
2693                                          * so that hotplug thread can
2694                                          * enumerate this device.
2695                                          */
2696                                         (void) hubd_toggle_port(hubd, port);
2697                                 } else {
2698                                         USB_DPRINTF_L2(DPRINT_MASK_ATTA,
2699                                             hubd->h_log_handle,
2700                                             "hubd_restore_device_state: "
2701                                             "device on port %d switched over",
2702                                             port);
2703                                 }
2704                         }
2705 
2706                 }
2707         }
2708 
2709 
2710         /* if the device had remote wakeup earlier, enable it again */
2711         if (hubd->h_hubpm->hubp_wakeup_enabled) {
2712                 mutex_exit(HUBD_MUTEX(hubd));
2713                 (void) usb_handle_remote_wakeup(hubd->h_dip,
2714                     USB_REMOTE_WAKEUP_ENABLE);
2715                 mutex_enter(HUBD_MUTEX(hubd));
2716         }
2717 
2718         hubd->h_dev_state = USB_DEV_ONLINE;
2719         hubd_start_polling(hubd, 0);
2720         (void) hubd_pm_idle_component(hubd, dip, 0);
2721         mutex_exit(HUBD_MUTEX(hubd));
2722 }
2723 
2724 
2725 /*
2726  * hubd_cleanup:
2727  *      cleanup hubd and deallocate. this function is called for
2728  *      handling attach failures and detaching including dynamic
2729  *      reconfiguration. If called from attaching, it must clean
2730  *      up the whole thing and return success.
2731  */
2732 /*ARGSUSED*/
2733 static int
2734 hubd_cleanup(dev_info_t *dip, hubd_t *hubd)
2735 {
2736         int             circ, rval, old_dev_state;
2737         hub_power_t     *hubpm;
2738 #ifdef DEBUG
2739         usb_port_t      port;
2740 #endif
2741 
2742         USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
2743             "hubd_cleanup:");
2744 
2745         if ((hubd->h_init_state & HUBD_LOCKS_DONE) == 0) {
2746                 goto done;
2747         }
2748 
2749         /* ensure we are the only one active */
2750         ndi_devi_enter(dip, &circ);
2751 
2752         mutex_enter(HUBD_MUTEX(hubd));
2753 
2754         /* Cleanup failure is only allowed if called from detach */
2755         if (DEVI_IS_DETACHING(dip)) {
2756                 dev_info_t *rh_dip = hubd->h_usba_device->usb_root_hub_dip;
2757 
2758                 /*
2759                  * We are being called from detach.
2760                  * Fail immediately if the hotplug thread is running
2761                  * else set the dev_state to disconnected so that
2762                  * hotplug thread just exits without doing anything.
2763                  */
2764                 if (hubd->h_bus_ctls || hubd->h_bus_pwr ||
2765                     hubd->h_hotplug_thread) {
2766                         mutex_exit(HUBD_MUTEX(hubd));
2767                         ndi_devi_exit(dip, circ);
2768 
2769                         USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
2770                             "hubd_cleanup: hotplug thread/bus ctl active "
2771                             "- failing detach");
2772 
2773                         return (USB_FAILURE);
2774                 }
2775 
2776                 /*
2777                  * if the deathrow thread is still active or about
2778                  * to become active, fail detach
2779                  * the roothup can only be detached if nexus drivers
2780                  * are unloaded or explicitly offlined
2781                  */
2782                 if (rh_dip == dip) {
2783                         if (hubd->h_cleanup_needed ||
2784                             hubd->h_cleanup_active) {
2785                                 mutex_exit(HUBD_MUTEX(hubd));
2786                                 ndi_devi_exit(dip, circ);
2787 
2788                                 USB_DPRINTF_L2(DPRINT_MASK_ATTA,
2789                                     hubd->h_log_handle,
2790                                     "hubd_cleanup: deathrow still active?"
2791                                     "- failing detach");
2792 
2793                                 return (USB_FAILURE);
2794                         }
2795                 }
2796         }
2797 
2798         old_dev_state = hubd->h_dev_state;
2799         hubd->h_dev_state = USB_DEV_DISCONNECTED;
2800 
2801         USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
2802             "hubd_cleanup: stop polling");
2803         hubd_close_intr_pipe(hubd);
2804 
2805         ASSERT((hubd->h_bus_ctls || hubd->h_bus_pwr ||
2806             hubd->h_hotplug_thread) == 0);
2807         mutex_exit(HUBD_MUTEX(hubd));
2808 
2809         /*
2810          * deallocate events, if events are still registered
2811          * (ie. children still attached) then we have to fail the detach
2812          */
2813         if (hubd->h_ndi_event_hdl) {
2814 
2815                 rval = ndi_event_free_hdl(hubd->h_ndi_event_hdl);
2816                 if (DEVI_IS_ATTACHING(dip)) {
2817 
2818                         /* It must return success if attaching. */
2819                         ASSERT(rval == NDI_SUCCESS);
2820 
2821                 } else if (rval != NDI_SUCCESS) {
2822 
2823                         USB_DPRINTF_L2(DPRINT_MASK_ALL, hubd->h_log_handle,
2824                             "hubd_cleanup: ndi_event_free_hdl failed");
2825                         ndi_devi_exit(dip, circ);
2826 
2827                         return (USB_FAILURE);
2828 
2829                 }
2830         }
2831 
2832         mutex_enter(HUBD_MUTEX(hubd));
2833 
2834         if (hubd->h_init_state & HUBD_CHILDREN_CREATED) {
2835 #ifdef DEBUG
2836                 for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
2837                         ASSERT(hubd->h_usba_devices[port] == NULL);
2838                         ASSERT(hubd->h_children_dips[port] == NULL);
2839                 }
2840 #endif
2841                 kmem_free(hubd->h_children_dips, hubd->h_cd_list_length);
2842                 kmem_free(hubd->h_usba_devices, hubd->h_cd_list_length);
2843         }
2844 
2845         /*
2846          * Disable the event callbacks first, after this point, event
2847          * callbacks will never get called. Note we shouldn't hold
2848          * mutex while unregistering events because there may be a
2849          * competing event callback thread. Event callbacks are done
2850          * with ndi mutex held and this can cause a potential deadlock.
2851          * Note that cleanup can't fail after deregistration of events.
2852          */
2853         if (hubd->h_init_state &  HUBD_EVENTS_REGISTERED) {
2854                 mutex_exit(HUBD_MUTEX(hubd));
2855                 usb_unregister_event_cbs(dip, &hubd_events);
2856                 hubd_unregister_cpr_callback(hubd);
2857                 mutex_enter(HUBD_MUTEX(hubd));
2858         }
2859 
2860         /* restore the old dev state so that device can be put into low power */
2861         hubd->h_dev_state = old_dev_state;
2862         hubpm = hubd->h_hubpm;
2863 
2864         if ((hubpm) && (hubd->h_dev_state != USB_DEV_DISCONNECTED)) {
2865                 (void) hubd_pm_busy_component(hubd, dip, 0);
2866                 mutex_exit(HUBD_MUTEX(hubd));
2867                 if (hubd->h_hubpm->hubp_wakeup_enabled) {
2868                         /*
2869                          * Bring the hub to full power before
2870                          * issuing the disable remote wakeup command
2871                          */
2872                         (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
2873 
2874                         if ((rval = usb_handle_remote_wakeup(hubd->h_dip,
2875                             USB_REMOTE_WAKEUP_DISABLE)) != USB_SUCCESS) {
2876                                 USB_DPRINTF_L2(DPRINT_MASK_PM,
2877                                     hubd->h_log_handle,
2878                                     "hubd_cleanup: disable remote wakeup "
2879                                     "fails=%d", rval);
2880                         }
2881                 }
2882 
2883                 (void) pm_lower_power(hubd->h_dip, 0, USB_DEV_OS_PWR_OFF);
2884 
2885                 mutex_enter(HUBD_MUTEX(hubd));
2886                 (void) hubd_pm_idle_component(hubd, dip, 0);
2887         }
2888 
2889         if (hubpm) {
2890                 if (hubpm->hubp_child_pwrstate) {
2891                         kmem_free(hubpm->hubp_child_pwrstate,
2892                             MAX_PORTS + 1);
2893                 }
2894                 kmem_free(hubpm, sizeof (hub_power_t));
2895         }
2896         mutex_exit(HUBD_MUTEX(hubd));
2897 
2898         USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
2899             "hubd_cleanup: freeing space");
2900 
2901         if (hubd->h_init_state & HUBD_HUBDI_REGISTERED) {
2902                 rval = usba_hubdi_unregister(dip);
2903                 ASSERT(rval == USB_SUCCESS);
2904         }
2905 
2906         if (hubd->h_init_state & HUBD_LOCKS_DONE) {
2907                 mutex_destroy(HUBD_MUTEX(hubd));
2908                 cv_destroy(&hubd->h_cv_reset_port);
2909                 cv_destroy(&hubd->h_cv_hotplug_dev);
2910         }
2911 
2912         ndi_devi_exit(dip, circ);
2913 
2914         if (hubd->h_init_state & HUBD_MINOR_NODE_CREATED) {
2915                 ddi_remove_minor_node(dip, NULL);
2916         }
2917 
2918         if (usba_is_root_hub(dip)) {
2919                 usb_pipe_close(dip, hubd->h_default_pipe,
2920                     USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
2921         }
2922 
2923 done:
2924         if (hubd->h_ancestry_str) {
2925                 kmem_free(hubd->h_ancestry_str, HUBD_APID_NAMELEN);
2926         }
2927 
2928         usb_client_detach(dip, hubd->h_dev_data);
2929 
2930         usb_free_log_hdl(hubd->h_log_handle);
2931 
2932         if (!usba_is_root_hub(dip)) {
2933                 ddi_soft_state_free(hubd_statep, ddi_get_instance(dip));
2934         }
2935 
2936         ddi_prop_remove_all(dip);
2937 
2938         return (USB_SUCCESS);
2939 }
2940 
2941 
2942 /*
2943  * hubd_determine_port_connection:
2944  *      Determine which port is in connect status but does not
2945  *      have connect status change bit set, and mark port change
2946  *      bit accordingly.
2947  *      This function is applied during hub attach time.
2948  */
2949 static usb_port_mask_t
2950 hubd_determine_port_connection(hubd_t   *hubd)
2951 {
2952         usb_port_t      port;
2953         usb_hub_descr_t *hub_descr;
2954         uint16_t        status;
2955         uint16_t        change;
2956         usb_port_mask_t port_change = 0;
2957 
2958         ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
2959 
2960         hub_descr = &hubd->h_hub_descr;
2961 
2962         for (port = 1; port <= hub_descr->bNbrPorts; port++) {
2963 
2964                 (void) hubd_determine_port_status(hubd, port, &status,
2965                     &change, 0);
2966 
2967                 /* Check if port is in connect status */
2968                 if (!(status & PORT_STATUS_CCS)) {
2969 
2970                         continue;
2971                 }
2972 
2973                 /*
2974                  * Check if port Connect Status Change bit has been set.
2975                  * If already set, the connection will be handled by
2976                  * intr polling callback, not during attach.
2977                  */
2978                 if (change & PORT_CHANGE_CSC) {
2979 
2980                         continue;
2981                 }
2982 
2983                 port_change |= 1 << port;
2984         }
2985 
2986         return (port_change);
2987 }
2988 
2989 
2990 /*
2991  * hubd_check_ports:
2992  *      - get hub descriptor
2993  *      - check initial port status
2994  *      - enable power on all ports
2995  *      - enable polling on ep1
2996  */
2997 static int
2998 hubd_check_ports(hubd_t  *hubd)
2999 {
3000         int                     rval;
3001         usb_port_mask_t         port_change = 0;
3002         hubd_hotplug_arg_t      *arg;
3003 
3004         ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
3005 
3006         USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
3007             "hubd_check_ports: addr=0x%x", usb_get_addr(hubd->h_dip));
3008 
3009         /*
3010          * First turn off all port power
3011          */
3012         if ((rval = hubd_disable_all_port_power(hubd)) != USB_SUCCESS) {
3013 
3014                 /* disable whatever was enabled */
3015                 (void) hubd_disable_all_port_power(hubd);
3016 
3017                 return (rval);
3018         }
3019 
3020         /*
3021          * do not switch on immediately (instantly on root hub)
3022          * and allow time to settle
3023          */
3024         mutex_exit(HUBD_MUTEX(hubd));
3025         delay(drv_usectohz(10000));
3026         mutex_enter(HUBD_MUTEX(hubd));
3027 
3028         /*
3029          * enable power on all ports so we can see connects
3030          */
3031         if ((rval = hubd_enable_all_port_power(hubd)) != USB_SUCCESS) {
3032                 /* disable whatever was enabled */
3033                 (void) hubd_disable_all_port_power(hubd);
3034 
3035                 return (rval);
3036         }
3037 
3038         /* wait at least 3 frames before accessing devices */
3039         mutex_exit(HUBD_MUTEX(hubd));
3040         delay(drv_usectohz(10000));
3041         mutex_enter(HUBD_MUTEX(hubd));
3042 
3043         /*
3044          * allocate arrays for saving the dips of each child per port
3045          *
3046          * ports go from 1 - n, allocate 1 more entry
3047          */
3048         hubd->h_cd_list_length =
3049             (sizeof (dev_info_t **)) * (hubd->h_hub_descr.bNbrPorts + 1);
3050 
3051         hubd->h_children_dips = (dev_info_t **)kmem_zalloc(
3052             hubd->h_cd_list_length, KM_SLEEP);
3053         hubd->h_usba_devices = (usba_device_t **)kmem_zalloc(
3054             hubd->h_cd_list_length, KM_SLEEP);
3055 
3056         hubd->h_init_state |= HUBD_CHILDREN_CREATED;
3057 
3058         mutex_exit(HUBD_MUTEX(hubd));
3059         arg = (hubd_hotplug_arg_t *)kmem_zalloc(
3060             sizeof (hubd_hotplug_arg_t), KM_SLEEP);
3061         mutex_enter(HUBD_MUTEX(hubd));
3062 
3063         if ((rval = hubd_open_intr_pipe(hubd)) != USB_SUCCESS) {
3064                 kmem_free(arg, sizeof (hubd_hotplug_arg_t));
3065 
3066                 return (rval);
3067         }
3068 
3069         hubd_start_polling(hubd, 0);
3070 
3071         /*
3072          * Some hub devices, like the embedded hub in the CKS ErgoMagic
3073          * keyboard, may only have connection status bit set, but not
3074          * have connect status change bit set when a device has been
3075          * connected to its downstream port before the hub is enumerated.
3076          * Then when the hub is in enumeration, the devices connected to
3077          * it cannot be detected by the intr pipe and won't be enumerated.
3078          * We need to check such situation here and enumerate the downstream
3079          * devices for such hubs.
3080          */
3081         port_change = hubd_determine_port_connection(hubd);
3082 
3083         if (port_change) {
3084                 hubd_pm_busy_component(hubd, hubd->h_dip, 0);
3085 
3086                 arg->hubd = hubd;
3087                 arg->hotplug_during_attach = B_TRUE;
3088                 hubd->h_port_change |= port_change;
3089 
3090                 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
3091                     "hubd_check_ports: port change=0x%x, need to connect",
3092                     hubd->h_port_change);
3093 
3094                 if (usb_async_req(hubd->h_dip, hubd_hotplug_thread,
3095                     (void *)arg, 0) == USB_SUCCESS) {
3096                         hubd->h_hotplug_thread++;
3097                 } else {
3098                         /* mark this device as idle */
3099                         hubd_pm_idle_component(hubd, hubd->h_dip, 0);
3100                         kmem_free(arg, sizeof (hubd_hotplug_arg_t));
3101                 }
3102         } else {
3103                 kmem_free(arg, sizeof (hubd_hotplug_arg_t));
3104         }
3105 
3106         USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
3107             "hubd_check_ports done");
3108 
3109         return (USB_SUCCESS);
3110 }
3111 
3112 
3113 /*
3114  * hubd_get_hub_descriptor:
3115  */
3116 static int
3117 hubd_get_hub_descriptor(hubd_t *hubd)
3118 {
3119         usb_hub_descr_t *hub_descr = &hubd->h_hub_descr;
3120         mblk_t          *data = NULL;
3121         usb_cr_t        completion_reason;
3122         usb_cb_flags_t  cb_flags;
3123         uint16_t        length;
3124         int             rval;
3125         usb_req_attrs_t attr = 0;
3126 
3127         USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
3128             "hubd_get_hub_descriptor:");
3129 
3130         if ((hubd->h_dev_data->dev_descr->idVendor == USB_HUB_INTEL_VID) &&
3131             (hubd->h_dev_data->dev_descr->idProduct == USB_HUB_INTEL_PID)) {
3132                 attr = USB_ATTRS_SHORT_XFER_OK;
3133         }
3134 
3135         ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
3136         ASSERT(hubd->h_default_pipe != 0);
3137 
3138         /* get hub descriptor length first by requesting 8 bytes only */
3139         mutex_exit(HUBD_MUTEX(hubd));
3140 
3141         if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
3142             hubd->h_default_pipe,
3143             HUB_CLASS_REQ_TYPE,
3144             USB_REQ_GET_DESCR,          /* bRequest */
3145             USB_DESCR_TYPE_SETUP_HUB,   /* wValue */
3146             0,                          /* wIndex */
3147             8,                          /* wLength */
3148             &data, 0,
3149             &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
3150                 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
3151                     "get hub descriptor failed: cr=%d cb_fl=0x%x rval=%d",
3152                     completion_reason, cb_flags, rval);
3153                 freemsg(data);
3154                 mutex_enter(HUBD_MUTEX(hubd));
3155 
3156                 return (rval);
3157         }
3158 
3159         length = *(data->b_rptr);
3160 
3161         if (length > 8) {
3162                 freemsg(data);
3163                 data = NULL;
3164 
3165                 /* get complete hub descriptor */
3166                 rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
3167                     hubd->h_default_pipe,
3168                     HUB_CLASS_REQ_TYPE,
3169                     USB_REQ_GET_DESCR,          /* bRequest */
3170                     USB_DESCR_TYPE_SETUP_HUB,   /* wValue */
3171                     0,                          /* wIndex */
3172                     length,                     /* wLength */
3173                     &data, attr,
3174                     &completion_reason, &cb_flags, 0);
3175 
3176                 /*
3177                  * Hub descriptor data less than 9 bytes is not valid and
3178                  * may cause trouble if we use it. See USB2.0 Tab11-13.
3179                  */
3180                 if ((rval != USB_SUCCESS) || (MBLKL(data) <= 8)) {
3181                         USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
3182                             "get hub descriptor failed: "
3183                             "cr=%d cb_fl=0x%x rval=%d, len=%ld",
3184                             completion_reason, cb_flags, rval,
3185                             (data)?MBLKL(data):0);
3186                         freemsg(data);
3187                         mutex_enter(HUBD_MUTEX(hubd));
3188 
3189                         return (rval);
3190                 }
3191         }
3192 
3193         mutex_enter(HUBD_MUTEX(hubd));
3194 
3195         /* parse the hub descriptor */
3196         /* only 32 ports are supported at present */
3197         ASSERT(*(data->b_rptr + 2) <= 32);
3198         if (usb_parse_CV_descr("cccscccccc",
3199             data->b_rptr, MBLKL(data),
3200             (void *)hub_descr, sizeof (usb_hub_descr_t)) == 0) {
3201                 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
3202                     "parsing hub descriptor failed");
3203 
3204                 freemsg(data);
3205 
3206                 return (USB_FAILURE);
3207         }
3208 
3209         freemsg(data);
3210 
3211         USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
3212             "rval=0x%x bNbrPorts=0x%x wHubChars=0x%x "
3213             "PwrOn2PwrGood=0x%x HubContrCurrent=%dmA", rval,
3214             hub_descr->bNbrPorts, hub_descr->wHubCharacteristics,
3215             hub_descr->bPwrOn2PwrGood, hub_descr->bHubContrCurrent);
3216 
3217         if (hub_descr->bNbrPorts > MAX_PORTS) {
3218                 USB_DPRINTF_L0(DPRINT_MASK_ATTA, hubd->h_log_handle,
3219                     "Hub driver supports max of %d ports on hub. "
3220                     "Hence using the first %d port of %d ports available",
3221                     MAX_PORTS, MAX_PORTS, hub_descr->bNbrPorts);
3222 
3223                 hub_descr->bNbrPorts = MAX_PORTS;
3224         }
3225 
3226         return (USB_SUCCESS);
3227 }
3228 
3229 
3230 /*
3231  * hubd_get_hub_status_words:
3232  */
3233 static int
3234 hubd_get_hub_status_words(hubd_t *hubd, uint16_t *status)
3235 {
3236         usb_cr_t        completion_reason;
3237         usb_cb_flags_t  cb_flags;
3238         mblk_t          *data = NULL;
3239 
3240         ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
3241 
3242         mutex_exit(HUBD_MUTEX(hubd));
3243 
3244         if (usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe,
3245             HUB_CLASS_REQ_TYPE,
3246             USB_REQ_GET_STATUS,
3247             0,
3248             0,
3249             GET_STATUS_LENGTH,
3250             &data, 0,
3251             &completion_reason, &cb_flags, 0) != USB_SUCCESS) {
3252                 USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
3253                     "get hub status failed: cr=%d cb=0x%x",
3254                     completion_reason, cb_flags);
3255 
3256                 if (data) {
3257                         freemsg(data);
3258                 }
3259 
3260                 mutex_enter(HUBD_MUTEX(hubd));
3261 
3262                 return (USB_FAILURE);
3263         }
3264 
3265         mutex_enter(HUBD_MUTEX(hubd));
3266 
3267         status[0] = (*(data->b_rptr + 1) << 8) | *(data->b_rptr);
3268         status[1] = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2);
3269 
3270         USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle,
3271             "hub status=0x%x change=0x%x", status[0], status[1]);
3272 
3273         freemsg(data);
3274 
3275         return (USB_SUCCESS);
3276 }
3277 
3278 
3279 /*
3280  * hubd_open_intr_pipe:
3281  *      we read all descriptors first for curiosity and then simply
3282  *      open the pipe
3283  */
3284 static int
3285 hubd_open_intr_pipe(hubd_t      *hubd)
3286 {
3287         int                     rval;
3288 
3289         USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
3290             "hubd_open_intr_pipe:");
3291 
3292         ASSERT(hubd->h_intr_pipe_state == HUBD_INTR_PIPE_IDLE);
3293 
3294         hubd->h_intr_pipe_state = HUBD_INTR_PIPE_OPENING;
3295         mutex_exit(HUBD_MUTEX(hubd));
3296 
3297         if ((rval = usb_pipe_open(hubd->h_dip,
3298             &hubd->h_ep1_descr, &hubd->h_pipe_policy,
3299             0, &hubd->h_ep1_ph)) != USB_SUCCESS) {
3300                 USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
3301                     "open intr pipe failed (%d)", rval);
3302 
3303                 mutex_enter(HUBD_MUTEX(hubd));
3304                 hubd->h_intr_pipe_state = HUBD_INTR_PIPE_IDLE;
3305 
3306                 return (rval);
3307         }
3308 
3309         mutex_enter(HUBD_MUTEX(hubd));
3310         hubd->h_intr_pipe_state = HUBD_INTR_PIPE_ACTIVE;
3311 
3312         USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
3313             "open intr pipe succeeded, ph=0x%p", (void *)hubd->h_ep1_ph);
3314 
3315         return (USB_SUCCESS);
3316 }
3317 
3318 
3319 /*
3320  * hubd_start_polling:
3321  *      start or restart the polling
3322  */
3323 static void
3324 hubd_start_polling(hubd_t *hubd, int always)
3325 {
3326         usb_intr_req_t  *reqp;
3327         int                     rval;
3328         usb_pipe_state_t        pipe_state;
3329 
3330         USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
3331             "start polling: always=%d dev_state=%d pipe_state=%d\n\t"
3332             "thread=%d ep1_ph=0x%p",
3333             always, hubd->h_dev_state, hubd->h_intr_pipe_state,
3334             hubd->h_hotplug_thread, (void *)hubd->h_ep1_ph);
3335 
3336         /*
3337          * start or restart polling on the intr pipe
3338          * only if hotplug thread is not running
3339          */
3340         if ((always == HUBD_ALWAYS_START_POLLING) ||
3341             ((hubd->h_dev_state == USB_DEV_ONLINE) &&
3342             (hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE) &&
3343             (hubd->h_hotplug_thread == 0) && hubd->h_ep1_ph)) {
3344                 USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
3345                     "start polling requested");
3346 
3347                 reqp = usb_alloc_intr_req(hubd->h_dip, 0, USB_FLAGS_SLEEP);
3348 
3349                 reqp->intr_client_private = (usb_opaque_t)hubd;
3350                 reqp->intr_attributes = USB_ATTRS_SHORT_XFER_OK |
3351                     USB_ATTRS_AUTOCLEARING;
3352                 reqp->intr_len = hubd->h_ep1_descr.wMaxPacketSize;
3353                 reqp->intr_cb = hubd_read_cb;
3354                 reqp->intr_exc_cb = hubd_exception_cb;
3355                 mutex_exit(HUBD_MUTEX(hubd));
3356                 if ((rval = usb_pipe_intr_xfer(hubd->h_ep1_ph, reqp,
3357                     USB_FLAGS_SLEEP)) != USB_SUCCESS) {
3358                         USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
3359                             "start polling failed, rval=%d", rval);
3360                         usb_free_intr_req(reqp);
3361                 }
3362 
3363                 rval = usb_pipe_get_state(hubd->h_ep1_ph, &pipe_state,
3364                     USB_FLAGS_SLEEP);
3365                 if (pipe_state != USB_PIPE_STATE_ACTIVE) {
3366                         USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
3367                             "intr pipe state=%d, rval=%d", pipe_state, rval);
3368                 }
3369                 USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
3370                     "start polling request 0x%p", (void *)reqp);
3371 
3372                 mutex_enter(HUBD_MUTEX(hubd));
3373         }
3374 }
3375 
3376 
3377 /*
3378  * hubd_stop_polling
3379  *      stop polling but do not close the pipe
3380  */
3381 static void
3382 hubd_stop_polling(hubd_t *hubd)
3383 {
3384         int                     rval;
3385         usb_pipe_state_t        pipe_state;
3386 
3387         if (hubd->h_ep1_ph) {
3388                 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
3389                     "hubd_stop_polling:");
3390                 hubd->h_intr_pipe_state = HUBD_INTR_PIPE_STOPPED;
3391                 mutex_exit(HUBD_MUTEX(hubd));
3392 
3393                 usb_pipe_stop_intr_polling(hubd->h_ep1_ph, USB_FLAGS_SLEEP);
3394                 rval = usb_pipe_get_state(hubd->h_ep1_ph, &pipe_state,
3395                     USB_FLAGS_SLEEP);
3396 
3397                 if (pipe_state != USB_PIPE_STATE_IDLE) {
3398                         USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
3399                             "intr pipe state=%d, rval=%d", pipe_state, rval);
3400                 }
3401                 mutex_enter(HUBD_MUTEX(hubd));
3402                 if (hubd->h_intr_pipe_state == HUBD_INTR_PIPE_STOPPED) {
3403                         hubd->h_intr_pipe_state = HUBD_INTR_PIPE_ACTIVE;
3404                 }
3405         }
3406 }
3407 
3408 
3409 /*
3410  * hubd_close_intr_pipe:
3411  *      close the pipe (which also stops the polling
3412  *      and wait for the hotplug thread to exit
3413  */
3414 static void
3415 hubd_close_intr_pipe(hubd_t *hubd)
3416 {
3417         USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
3418             "hubd_close_intr_pipe:");
3419 
3420         /*
3421          * Now that no async operation is outstanding on pipe,
3422          * we can change the state to HUBD_INTR_PIPE_CLOSING
3423          */
3424         hubd->h_intr_pipe_state = HUBD_INTR_PIPE_CLOSING;
3425 
3426         ASSERT(hubd->h_hotplug_thread == 0);
3427 
3428         if (hubd->h_ep1_ph) {
3429                 mutex_exit(HUBD_MUTEX(hubd));
3430                 usb_pipe_close(hubd->h_dip, hubd->h_ep1_ph, USB_FLAGS_SLEEP,
3431                     NULL, NULL);
3432                 mutex_enter(HUBD_MUTEX(hubd));
3433                 hubd->h_ep1_ph = NULL;
3434         }
3435 
3436         hubd->h_intr_pipe_state = HUBD_INTR_PIPE_IDLE;
3437 }
3438 
3439 
3440 /*
3441  * hubd_exception_cb
3442  *      interrupt ep1 exception callback function.
3443  *      this callback executes in taskq thread context and assumes
3444  *      autoclearing
3445  */
3446 /*ARGSUSED*/
3447 static void
3448 hubd_exception_cb(usb_pipe_handle_t pipe, usb_intr_req_t *reqp)
3449 {
3450         hubd_t          *hubd = (hubd_t *)(reqp->intr_client_private);
3451 
3452         USB_DPRINTF_L2(DPRINT_MASK_CALLBACK, hubd->h_log_handle,
3453             "hubd_exception_cb: "
3454             "req=0x%p cr=%d data=0x%p cb_flags=0x%x", (void *)reqp,
3455             reqp->intr_completion_reason, (void *)reqp->intr_data,
3456             reqp->intr_cb_flags);
3457 
3458         ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
3459 
3460         mutex_enter(HUBD_MUTEX(hubd));
3461         (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0);
3462 
3463         switch (reqp->intr_completion_reason) {
3464         case USB_CR_PIPE_RESET:
3465                 /* only restart polling after autoclearing */
3466                 if ((hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE) &&
3467                     (hubd->h_port_reset_wait == 0)) {
3468                         hubd_start_polling(hubd, 0);
3469                 }
3470 
3471                 break;
3472         case USB_CR_DEV_NOT_RESP:
3473         case USB_CR_STOPPED_POLLING:
3474         case USB_CR_PIPE_CLOSING:
3475         case USB_CR_UNSPECIFIED_ERR:
3476                 /* never restart polling on these conditions */
3477         default:
3478                 /* for all others, wait for the autoclearing PIPE_RESET cb */
3479 
3480                 break;
3481         }
3482 
3483         usb_free_intr_req(reqp);
3484         (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
3485         mutex_exit(HUBD_MUTEX(hubd));
3486 }
3487 
3488 
3489 /*
3490  * helper function to convert LE bytes to a portmask
3491  */
3492 static usb_port_mask_t
3493 hubd_mblk2portmask(mblk_t *data)
3494 {
3495         int len = min(MBLKL(data), sizeof (usb_port_mask_t));
3496         usb_port_mask_t rval = 0;
3497         int i;
3498 
3499         for (i = 0; i < len; i++) {
3500                 rval |= data->b_rptr[i] << (i * 8);
3501         }
3502 
3503         return (rval);
3504 }
3505 
3506 
3507 /*
3508  * hubd_read_cb:
3509  *      interrupt ep1 callback function
3510  *
3511  *      the status indicates just a change on the pipe with no indication
3512  *      of what the change was
3513  *
3514  *      known conditions:
3515  *              - reset port completion
3516  *              - connect
3517  *              - disconnect
3518  *
3519  *      for handling the hotplugging, create a new thread that can do
3520  *      synchronous usba calls
3521  */
3522 static void
3523 hubd_read_cb(usb_pipe_handle_t pipe, usb_intr_req_t *reqp)
3524 {
3525         hubd_t          *hubd = (hubd_t *)(reqp->intr_client_private);
3526         size_t          length;
3527         mblk_t          *data = reqp->intr_data;
3528         int             mem_flag = 0;
3529         hubd_hotplug_arg_t *arg;
3530 
3531         USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
3532             "hubd_read_cb: ph=0x%p req=0x%p", (void *)pipe, (void *)reqp);
3533 
3534         ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
3535 
3536         /*
3537          * At present, we are not handling notification for completion of
3538          * asynchronous pipe reset, for which this data ptr could be NULL
3539          */
3540 
3541         if (data == NULL) {
3542                 usb_free_intr_req(reqp);
3543 
3544                 return;
3545         }
3546 
3547         arg = (hubd_hotplug_arg_t *)kmem_zalloc(
3548             sizeof (hubd_hotplug_arg_t), KM_SLEEP);
3549         mem_flag = 1;
3550 
3551         mutex_enter(HUBD_MUTEX(hubd));
3552 
3553         if ((hubd->h_dev_state == USB_DEV_SUSPENDED) ||
3554             (hubd->h_intr_pipe_state != HUBD_INTR_PIPE_ACTIVE)) {
3555                 mutex_exit(HUBD_MUTEX(hubd));
3556                 usb_free_intr_req(reqp);
3557                 kmem_free(arg, sizeof (hubd_hotplug_arg_t));
3558 
3559                 return;
3560         }
3561 
3562         ASSERT(hubd->h_ep1_ph == pipe);
3563 
3564         length = MBLKL(data);
3565 
3566         /*
3567          * Only look at the data and startup the hotplug thread if
3568          * there actually is data.
3569          */
3570         if (length != 0) {
3571                 usb_port_mask_t port_change = hubd_mblk2portmask(data);
3572 
3573                 /*
3574                  * if a port change was already reported and we are waiting for
3575                  * reset port completion then wake up the hotplug thread which
3576                  * should be waiting on reset port completion
3577                  *
3578                  * if there is disconnect event instead of reset completion, let
3579                  * the hotplug thread figure this out
3580                  */
3581 
3582                 /* remove the reset wait bits from the status */
3583                 hubd->h_port_change |= port_change &
3584                     ~hubd->h_port_reset_wait;
3585 
3586                 USB_DPRINTF_L3(DPRINT_MASK_CALLBACK, hubd->h_log_handle,
3587                     "port change=0x%x port_reset_wait=0x%x",
3588                     hubd->h_port_change, hubd->h_port_reset_wait);
3589 
3590                 /* there should be only one reset bit active at the time */
3591                 if (hubd->h_port_reset_wait & port_change) {
3592                         hubd->h_port_reset_wait = 0;
3593                         cv_signal(&hubd->h_cv_reset_port);
3594                 }
3595 
3596                 /*
3597                  * kick off the thread only if device is ONLINE and it is not
3598                  * during attaching or detaching
3599                  */
3600                 if ((hubd->h_dev_state == USB_DEV_ONLINE) &&
3601                     (!DEVI_IS_ATTACHING(hubd->h_dip)) &&
3602                     (!DEVI_IS_DETACHING(hubd->h_dip)) &&
3603                     (hubd->h_port_change) &&
3604                     (hubd->h_hotplug_thread == 0)) {
3605                         USB_DPRINTF_L3(DPRINT_MASK_CALLBACK, hubd->h_log_handle,
3606                             "creating hotplug thread: "
3607                             "dev_state=%d", hubd->h_dev_state);
3608 
3609                         /*
3610                          * Mark this device as busy. The will be marked idle
3611                          * if the async req fails or at the exit of  hotplug
3612                          * thread
3613                          */
3614                         (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0);
3615 
3616                         arg->hubd = hubd;
3617                         arg->hotplug_during_attach = B_FALSE;
3618 
3619                         if (usb_async_req(hubd->h_dip,
3620                             hubd_hotplug_thread,
3621                             (void *)arg, 0) == USB_SUCCESS) {
3622                                 hubd->h_hotplug_thread++;
3623                                 mem_flag = 0;
3624                         } else {
3625                                 /* mark this device as idle */
3626                                 (void) hubd_pm_idle_component(hubd,
3627                                     hubd->h_dip, 0);
3628                         }
3629                 }
3630         }
3631         mutex_exit(HUBD_MUTEX(hubd));
3632 
3633         if (mem_flag == 1) {
3634                 kmem_free(arg, sizeof (hubd_hotplug_arg_t));
3635         }
3636 
3637         usb_free_intr_req(reqp);
3638 }
3639 
3640 
3641 /*
3642  * hubd_hotplug_thread:
3643  *      handles resetting of port, and creating children
3644  *
3645  *      the ports to check are indicated in h_port_change bit mask
3646  * XXX note that one time poll doesn't work on the root hub
3647  */
3648 static void
3649 hubd_hotplug_thread(void *arg)
3650 {
3651         hubd_hotplug_arg_t *hd_arg = (hubd_hotplug_arg_t *)arg;
3652         hubd_t          *hubd = hd_arg->hubd;
3653         boolean_t       attach_flg = hd_arg->hotplug_during_attach;
3654         usb_port_t      port;
3655         uint16_t        nports;
3656         uint16_t        status, change;
3657         hub_power_t     *hubpm;
3658         dev_info_t      *hdip = hubd->h_dip;
3659         dev_info_t      *rh_dip = hubd->h_usba_device->usb_root_hub_dip;
3660         dev_info_t      *child_dip;
3661         boolean_t       online_child = B_FALSE;
3662         boolean_t       offline_child = B_FALSE;
3663         boolean_t       pwrup_child = B_FALSE;
3664         int             prh_circ, rh_circ, chld_circ, circ, old_state;
3665 
3666         USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
3667             "hubd_hotplug_thread:  started");
3668 
3669         /*
3670          * Before console is init'd, we temporarily block the hotplug
3671          * threads so that BUS_CONFIG_ONE through hubd_bus_config() can be
3672          * processed quickly. This reduces the time needed for vfs_mountroot()
3673          * to mount the root FS from a USB disk. And on SPARC platform,
3674          * in order to load 'consconfig' successfully after OBP is gone,
3675          * we need to check 'modrootloaded' to make sure root filesystem is
3676          * available.
3677          */
3678         while (!modrootloaded || !consconfig_console_is_ready()) {
3679                 delay(drv_usectohz(10000));
3680         }
3681 
3682         kmem_free(arg, sizeof (hubd_hotplug_arg_t));
3683 
3684         /*
3685          * if our bus power entry point is active, process the change
3686          * on the next notification of interrupt pipe
3687          */
3688         mutex_enter(HUBD_MUTEX(hubd));
3689         if (hubd->h_bus_pwr || (hubd->h_hotplug_thread > 1)) {
3690                 hubd->h_hotplug_thread--;
3691 
3692                 /* mark this device as idle */
3693                 hubd_pm_idle_component(hubd, hubd->h_dip, 0);
3694                 mutex_exit(HUBD_MUTEX(hubd));
3695 
3696                 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
3697                     "hubd_hotplug_thread: "
3698                     "bus_power in progress/hotplugging undesirable - quit");
3699 
3700                 return;
3701         }
3702         mutex_exit(HUBD_MUTEX(hubd));
3703 
3704         ndi_hold_devi(hdip); /* so we don't race with detach */
3705 
3706         mutex_enter(HUBD_MUTEX(hubd));
3707 
3708         /* is this the root hub? */
3709         if (hdip == rh_dip) {
3710                 if (hubd->h_dev_state == USB_DEV_PWRED_DOWN) {
3711                         hubpm = hubd->h_hubpm;
3712 
3713                         /* mark the root hub as full power */
3714                         hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR;
3715                         hubpm->hubp_time_at_full_power = ddi_get_time();
3716                         mutex_exit(HUBD_MUTEX(hubd));
3717 
3718                         USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
3719                             "hubd_hotplug_thread: call pm_power_has_changed");
3720 
3721                         (void) pm_power_has_changed(hdip, 0,
3722                             USB_DEV_OS_FULL_PWR);
3723 
3724                         mutex_enter(HUBD_MUTEX(hubd));
3725                         hubd->h_dev_state = USB_DEV_ONLINE;
3726                 }
3727 
3728         } else {
3729                 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
3730                     "hubd_hotplug_thread: not root hub");
3731         }
3732 
3733         mutex_exit(HUBD_MUTEX(hubd));
3734 
3735         /*
3736          * this ensures one hotplug activity per system at a time.
3737          * we enter the parent PCI node to have this serialization.
3738          * this also excludes ioctls and deathrow thread
3739          * (a bit crude but easier to debug)
3740          */
3741         ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
3742         ndi_devi_enter(rh_dip, &rh_circ);
3743 
3744         /* exclude other threads */
3745         ndi_devi_enter(hdip, &circ);
3746         mutex_enter(HUBD_MUTEX(hubd));
3747 
3748         ASSERT(hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE);
3749 
3750         nports = hubd->h_hub_descr.bNbrPorts;
3751 
3752         hubd_stop_polling(hubd);
3753 
3754         while ((hubd->h_dev_state == USB_DEV_ONLINE) &&
3755             (hubd->h_port_change)) {
3756                 /*
3757                  * The 0th bit is the hub status change bit.
3758                  * handle loss of local power here
3759                  */
3760                 if (hubd->h_port_change & HUB_CHANGE_STATUS) {
3761                         USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
3762                             "hubd_hotplug_thread: hub status change!");
3763 
3764                         /*
3765                          * This should be handled properly.  For now,
3766                          * mask off the bit.
3767                          */
3768                         hubd->h_port_change &= ~HUB_CHANGE_STATUS;
3769 
3770                         /*
3771                          * check and ack hub status
3772                          * this causes stall conditions
3773                          * when local power is removed
3774                          */
3775                         (void) hubd_get_hub_status(hubd);
3776                 }
3777 
3778                 for (port = 1; port <= nports; port++) {
3779                         usb_port_mask_t port_mask;
3780                         boolean_t was_connected;
3781 
3782                         port_mask = 1 << port;
3783                         was_connected =
3784                             (hubd->h_port_state[port] & PORT_STATUS_CCS) &&
3785                             (hubd->h_children_dips[port]);
3786 
3787                         USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
3788                             "hubd_hotplug_thread: "
3789                             "port %d mask=0x%x change=0x%x connected=0x%x",
3790                             port, port_mask, hubd->h_port_change,
3791                             was_connected);
3792 
3793                         /*
3794                          * is this a port connection that changed?
3795                          */
3796                         if ((hubd->h_port_change & port_mask) == 0) {
3797 
3798                                 continue;
3799                         }
3800                         hubd->h_port_change &= ~port_mask;
3801 
3802                         /* ack all changes */
3803                         (void) hubd_determine_port_status(hubd, port,
3804                             &status, &change, HUBD_ACK_ALL_CHANGES);
3805 
3806                         USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
3807                             "handle port %d:\n\t"
3808                             "new status=0x%x change=0x%x was_conn=0x%x ",
3809                             port, status, change, was_connected);
3810 
3811                         /* Recover a disabled port */
3812                         if (change & PORT_CHANGE_PESC) {
3813                                 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG,
3814                                     hubd->h_log_handle,
3815                                     "port%d Disabled - "
3816                                     "status=0x%x, change=0x%x",
3817                                     port, status, change);
3818 
3819                                 /*
3820                                  * if the port was connected and is still
3821                                  * connected, recover the port
3822                                  */
3823                                 if (was_connected && (status &
3824                                     PORT_STATUS_CCS)) {
3825                                         online_child |=
3826                                             (hubd_recover_disabled_port(hubd,
3827                                             port) == USB_SUCCESS);
3828                                 }
3829                         }
3830 
3831                         /*
3832                          * Now check what changed on the port
3833                          */
3834                         if ((change & PORT_CHANGE_CSC) || attach_flg) {
3835                                 if ((status & PORT_STATUS_CCS) &&
3836                                     (!was_connected)) {
3837                                         /* new device plugged in */
3838                                         online_child |=
3839                                             (hubd_handle_port_connect(hubd,
3840                                             port) == USB_SUCCESS);
3841 
3842                                 } else if ((status & PORT_STATUS_CCS) &&
3843                                     was_connected) {
3844                                         /*
3845                                          * In this case we can never be sure
3846                                          * if the device indeed got hotplugged
3847                                          * or the hub is falsely reporting the
3848                                          * change.
3849                                          */
3850                                         child_dip = hubd->h_children_dips[port];
3851 
3852                                         mutex_exit(HUBD_MUTEX(hubd));
3853                                         /*
3854                                          * this ensures we do not race with
3855                                          * other threads which are detaching
3856                                          * the child driver at the same time.
3857                                          */
3858                                         ndi_devi_enter(child_dip, &chld_circ);
3859                                         /*
3860                                          * Now check if the driver remains
3861                                          * attached.
3862                                          */
3863                                         if (i_ddi_devi_attached(child_dip)) {
3864                                                 /*
3865                                                  * first post a disconnect event
3866                                                  * to the child.
3867                                                  */
3868                                                 hubd_post_event(hubd, port,
3869                                                     USBA_EVENT_TAG_HOT_REMOVAL);
3870                                                 mutex_enter(HUBD_MUTEX(hubd));
3871 
3872                                                 /*
3873                                                  * then reset the port and
3874                                                  * recover the device
3875                                                  */
3876                                                 online_child |=
3877                                                     (hubd_handle_port_connect(
3878                                                     hubd, port) == USB_SUCCESS);
3879 
3880                                                 mutex_exit(HUBD_MUTEX(hubd));
3881                                         }
3882 
3883                                         ndi_devi_exit(child_dip, chld_circ);
3884                                         mutex_enter(HUBD_MUTEX(hubd));
3885                                 } else if (was_connected) {
3886                                         /* this is a disconnect */
3887                                         mutex_exit(HUBD_MUTEX(hubd));
3888                                         hubd_post_event(hubd, port,
3889                                             USBA_EVENT_TAG_HOT_REMOVAL);
3890                                         mutex_enter(HUBD_MUTEX(hubd));
3891 
3892                                         offline_child = B_TRUE;
3893                                 }
3894                         }
3895 
3896                         /*
3897                          * Check if any port is coming out of suspend
3898                          */
3899                         if (change & PORT_CHANGE_PSSC) {
3900                                 /* a resuming device could have disconnected */
3901                                 if (was_connected &&
3902                                     hubd->h_children_dips[port]) {
3903 
3904                                         /* device on this port resuming */
3905                                         dev_info_t *dip;
3906 
3907                                         dip = hubd->h_children_dips[port];
3908 
3909                                         /*
3910                                          * Don't raise power on detaching child
3911                                          */
3912                                         if (!DEVI_IS_DETACHING(dip)) {
3913                                                 /*
3914                                                  * As this child is not
3915                                                  * detaching, we set this
3916                                                  * flag, causing bus_ctls
3917                                                  * to stall detach till
3918                                                  * pm_raise_power returns
3919                                                  * and flag it for a deferred
3920                                                  * raise_power.
3921                                                  *
3922                                                  * pm_raise_power is deferred
3923                                                  * because we need to release
3924                                                  * the locks first.
3925                                                  */
3926                                                 hubd->h_port_state[port] |=
3927                                                     HUBD_CHILD_RAISE_POWER;
3928                                                 pwrup_child = B_TRUE;
3929                                                 mutex_exit(HUBD_MUTEX(hubd));
3930 
3931                                                 /*
3932                                                  * make sure that child
3933                                                  * doesn't disappear
3934                                                  */
3935                                                 ndi_hold_devi(dip);
3936 
3937                                                 mutex_enter(HUBD_MUTEX(hubd));
3938                                         }
3939                                 }
3940                         }
3941 
3942                         /*
3943                          * Check if the port is over-current
3944                          */
3945                         if (change & PORT_CHANGE_OCIC) {
3946                                 USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG,
3947                                     hubd->h_log_handle,
3948                                     "Port%d in over current condition, "
3949                                     "please check the attached device to "
3950                                     "clear the condition. The system will "
3951                                     "try to recover the port, but if not "
3952                                     "successful, you need to re-connect "
3953                                     "the hub or reboot the system to bring "
3954                                     "the port back to work", port);
3955 
3956                                 if (!(status & PORT_STATUS_PPS)) {
3957                                         /*
3958                                          * Try to enable port power, but
3959                                          * possibly fail. Ignore failure
3960                                          */
3961                                         (void) hubd_enable_port_power(hubd,
3962                                             port);
3963 
3964                                         /*
3965                                          * Delay some time to avoid
3966                                          * over-current event to happen
3967                                          * too frequently in some cases
3968                                          */
3969                                         mutex_exit(HUBD_MUTEX(hubd));
3970                                         delay(drv_usectohz(500000));
3971                                         mutex_enter(HUBD_MUTEX(hubd));
3972                                 }
3973                         }
3974                 }
3975         }
3976 
3977         /* release locks so we can do a devfs_clean */
3978         mutex_exit(HUBD_MUTEX(hubd));
3979 
3980         /* delete cached dv_node's but drop locks first */
3981         ndi_devi_exit(hdip, circ);
3982         ndi_devi_exit(rh_dip, rh_circ);
3983         ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ);
3984 
3985         (void) devfs_clean(rh_dip, NULL, 0);
3986 
3987         /* now check if any children need onlining */
3988         if (online_child) {
3989                 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
3990                     "hubd_hotplug_thread: onlining children");
3991 
3992                 (void) ndi_devi_online(hubd->h_dip, 0);
3993         }
3994 
3995         /* now check if any disconnected devices need to be cleaned up */
3996         if (offline_child) {
3997                 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
3998                     "hubd_hotplug_thread: scheduling cleanup");
3999 
4000                 hubd_schedule_cleanup(hubd->h_usba_device->usb_root_hub_dip);
4001         }
4002 
4003         mutex_enter(HUBD_MUTEX(hubd));
4004 
4005         /* now raise power on the children that have woken up */
4006         if (pwrup_child) {
4007                 old_state = hubd->h_dev_state;
4008                 hubd->h_dev_state = USB_DEV_HUB_CHILD_PWRLVL;
4009                 for (port = 1; port <= nports; port++) {
4010                         if (hubd->h_port_state[port] & HUBD_CHILD_RAISE_POWER) {
4011                                 dev_info_t *dip = hubd->h_children_dips[port];
4012 
4013                                 mutex_exit(HUBD_MUTEX(hubd));
4014 
4015                                 /* Get the device to full power */
4016                                 (void) pm_busy_component(dip, 0);
4017                                 (void) pm_raise_power(dip, 0,
4018                                     USB_DEV_OS_FULL_PWR);
4019                                 (void) pm_idle_component(dip, 0);
4020 
4021                                 /* release the hold on the child */
4022                                 ndi_rele_devi(dip);
4023                                 mutex_enter(HUBD_MUTEX(hubd));
4024                                 hubd->h_port_state[port] &=
4025                                     ~HUBD_CHILD_RAISE_POWER;
4026                         }
4027                 }
4028                 /*
4029                  * make sure that we don't accidentally
4030                  * over write the disconnect state
4031                  */
4032                 if (hubd->h_dev_state == USB_DEV_HUB_CHILD_PWRLVL) {
4033                         hubd->h_dev_state = old_state;
4034                 }
4035         }
4036 
4037         /*
4038          * start polling can immediately kick off read callback
4039          * we need to set the h_hotplug_thread to 0 so that
4040          * the callback is not dropped
4041          *
4042          * if there is device during reset, still stop polling to avoid the
4043          * read callback interrupting the reset, the polling will be started
4044          * in hubd_reset_thread.
4045          */
4046         for (port = 1; port <= MAX_PORTS; port++) {
4047                 if (hubd->h_reset_port[port]) {
4048 
4049                         break;
4050                 }
4051         }
4052         if (port > MAX_PORTS) {
4053                 hubd_start_polling(hubd, HUBD_ALWAYS_START_POLLING);
4054         }
4055 
4056         /*
4057          * Earlier we would set the h_hotplug_thread = 0 before
4058          * polling was restarted  so that
4059          * if there is any root hub status change interrupt, we can still kick
4060          * off the hotplug thread. This was valid when this interrupt was
4061          * delivered in hardware, and only ONE interrupt would be delivered.
4062          * Now that we poll on the root hub looking for status change in
4063          * software, this assignment is no longer required.
4064          */
4065         hubd->h_hotplug_thread--;
4066 
4067         /* mark this device as idle */
4068         (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
4069 
4070         cv_broadcast(&hubd->h_cv_hotplug_dev);
4071 
4072         USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4073             "hubd_hotplug_thread: exit");
4074 
4075         mutex_exit(HUBD_MUTEX(hubd));
4076 
4077         ndi_rele_devi(hdip);
4078 }
4079 
4080 
4081 /*
4082  * hubd_handle_port_connect:
4083  *      Transition a port from Disabled to Enabled.  Ensure that the
4084  *      port is in the correct state before attempting to
4085  *      access the device.
4086  */
4087 static int
4088 hubd_handle_port_connect(hubd_t *hubd, usb_port_t port)
4089 {
4090         int                     rval;
4091         int                     retry;
4092         long                    time_delay;
4093         long                    settling_time;
4094         uint16_t                status;
4095         uint16_t                change;
4096         usb_addr_t              hubd_usb_addr;
4097         usba_device_t           *usba_device;
4098         usb_port_status_t       port_status = 0;
4099         usb_port_status_t       hub_port_status = 0;
4100 
4101         /* Get the hub address and port status */
4102         usba_device = hubd->h_usba_device;
4103         mutex_enter(&usba_device->usb_mutex);
4104         hubd_usb_addr = usba_device->usb_addr;
4105         hub_port_status = usba_device->usb_port_status;
4106         mutex_exit(&usba_device->usb_mutex);
4107 
4108         /*
4109          * If a device is connected, transition the
4110          * port from Disabled to the Enabled state.
4111          * The device will receive downstream packets
4112          * in the Enabled state.
4113          *
4114          * reset port and wait for the hub to report
4115          * completion
4116          */
4117         change = status = 0;
4118 
4119         /*
4120          * According to section 9.1.2 of USB 2.0 spec, the host should
4121          * wait for atleast 100ms to allow completion of an insertion
4122          * process and for power at the device to become stable.
4123          * We wait for 200 ms
4124          */
4125         settling_time = drv_usectohz(hubd_device_delay / 5);
4126         mutex_exit(HUBD_MUTEX(hubd));
4127         delay(settling_time);
4128         mutex_enter(HUBD_MUTEX(hubd));
4129 
4130         /* calculate 600 ms delay time */
4131         time_delay = (6 * drv_usectohz(hubd_device_delay)) / 10;
4132 
4133         for (retry = 0; (hubd->h_dev_state == USB_DEV_ONLINE) &&
4134             (retry < hubd_retry_enumerate); retry++) {
4135                 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4136                     "resetting port%d, retry=%d", port, retry);
4137 
4138                 if ((rval = hubd_reset_port(hubd, port)) != USB_SUCCESS) {
4139                         (void) hubd_determine_port_status(hubd,
4140                             port, &status, &change, 0);
4141 
4142                         /* continue only if port is still connected */
4143                         if (status & PORT_STATUS_CCS) {
4144                                 continue;
4145                         }
4146 
4147                         /* carry on regardless */
4148                 }
4149 
4150                 /*
4151                  * according to USB 2.0 spec section 11.24.2.7.1.2
4152                  * at the end of port reset, the hub enables the port.
4153                  * But for some strange reasons, uhci port remains disabled.
4154                  * And because the port remains disabled for the settling
4155                  * time below, the device connected to the port gets wedged
4156                  * - fails to enumerate (device not responding)
4157                  * Hence, we enable it here immediately and later again after
4158                  * the delay
4159                  */
4160                 (void) hubd_enable_port(hubd, port);
4161 
4162                 /* we skip this delay in the first iteration */
4163                 if (retry) {
4164                         /*
4165                          * delay for device to signal disconnect/connect so
4166                          * that hub properly recognizes the speed of the device
4167                          */
4168                         mutex_exit(HUBD_MUTEX(hubd));
4169                         delay(settling_time);
4170                         mutex_enter(HUBD_MUTEX(hubd));
4171 
4172                         /*
4173                          * When a low speed device is connected to any port of
4174                          * PPX it has to be explicitly enabled
4175                          * Also, if device intentionally signals
4176                          * disconnect/connect, it will disable the port.
4177                          * So enable it again.
4178                          */
4179                         (void) hubd_enable_port(hubd, port);
4180                 }
4181 
4182                 if ((rval = hubd_determine_port_status(hubd, port, &status,
4183                     &change, 0)) != USB_SUCCESS) {
4184 
4185                         USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4186                             "getting status failed (%d)", rval);
4187 
4188                         (void) hubd_disable_port(hubd, port);
4189 
4190                         continue;
4191                 }
4192 
4193                 if (status & PORT_STATUS_POCI) {
4194                         USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4195                             "port %d overcurrent", port);
4196 
4197                         (void) hubd_disable_port(hubd, port);
4198 
4199                         /* ack changes */
4200                         (void) hubd_determine_port_status(hubd,
4201                             port, &status, &change, PORT_CHANGE_OCIC);
4202 
4203                         continue;
4204                 }
4205 
4206                 /* is status really OK? */
4207                 if ((status & PORT_STATUS_OK) != PORT_STATUS_OK) {
4208                         USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4209                             "port %d status (0x%x) not OK on retry %d",
4210                             port, status, retry);
4211 
4212                         /* check if we still have the connection */
4213                         if (!(status & PORT_STATUS_CCS)) {
4214                                 /* lost connection, set exit condition */
4215                                 retry = hubd_retry_enumerate;
4216 
4217                                 break;
4218                         }
4219                 } else {
4220                         /*
4221                          * Determine if the device is high or full
4222                          * or low speed.
4223                          */
4224                         if (status & PORT_STATUS_LSDA) {
4225                                 port_status = USBA_LOW_SPEED_DEV;
4226                         } else if (status & PORT_STATUS_HSDA) {
4227                                 port_status = USBA_HIGH_SPEED_DEV;
4228                         } else {
4229                                 port_status = USBA_FULL_SPEED_DEV;
4230                         }
4231 
4232                         USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4233                             "creating child port%d, status=0x%x "
4234                             "port status=0x%x",
4235                             port, status, port_status);
4236 
4237                         /*
4238                          * if the child already exists, set addrs and config
4239                          * to the device post connect event to the child
4240                          */
4241                         if (hubd->h_children_dips[port]) {
4242                                 /* set addrs to this device */
4243                                 rval = hubd_setdevaddr(hubd, port);
4244 
4245                                 /*
4246                                  * This delay is important for the CATC hub
4247                                  * to enumerate. But, avoid delay in the first
4248                                  * iteration
4249                                  */
4250                                 if (retry) {
4251                                         mutex_exit(HUBD_MUTEX(hubd));
4252                                         delay(drv_usectohz(
4253                                             hubd_device_delay/100));
4254                                         mutex_enter(HUBD_MUTEX(hubd));
4255                                 }
4256 
4257                                 if (rval == USB_SUCCESS) {
4258                                         /*
4259                                          * if the port is resetting, check if
4260                                          * device's descriptors have changed.
4261                                          */
4262                                         if ((hubd->h_reset_port[port]) &&
4263                                             (hubd_check_same_device(hubd,
4264                                             port) != USB_SUCCESS)) {
4265                                                 retry = hubd_retry_enumerate;
4266 
4267                                                 break;
4268                                         }
4269 
4270                                         /*
4271                                          * set the default config for
4272                                          * this device
4273                                          */
4274                                         hubd_setdevconfig(hubd, port);
4275 
4276                                         /*
4277                                          * if we are doing Default reset, do
4278                                          * not post reconnect event since we
4279                                          * don't know where reset function is
4280                                          * called.
4281                                          */
4282                                         if (hubd->h_reset_port[port]) {
4283 
4284                                                 return (USB_SUCCESS);
4285                                         }
4286 
4287                                         /*
4288                                          * indicate to the child that
4289                                          * it is online again
4290                                          */
4291                                         mutex_exit(HUBD_MUTEX(hubd));
4292                                         hubd_post_event(hubd, port,
4293                                             USBA_EVENT_TAG_HOT_INSERTION);
4294                                         mutex_enter(HUBD_MUTEX(hubd));
4295 
4296                                         return (USB_SUCCESS);
4297                                 }
4298                         } else {
4299                                 /*
4300                                  * We need to release access here
4301                                  * so that busctls on other ports can
4302                                  * continue and don't cause a deadlock
4303                                  * when busctl and removal of prom node
4304                                  * takes concurrently. This also ensures
4305                                  * busctls for attach of successfully
4306                                  * enumerated devices on other ports can
4307                                  * continue concurrently with the process
4308                                  * of enumerating the new devices. This
4309                                  * reduces the overall boot time of the system.
4310                                  */
4311                                 rval = hubd_create_child(hubd->h_dip,
4312                                     hubd,
4313                                     hubd->h_usba_device,
4314                                     port_status, port,
4315                                     retry);
4316                                 if (rval == USB_SUCCESS) {
4317                                         usba_update_hotplug_stats(hubd->h_dip,
4318                                             USBA_TOTAL_HOTPLUG_SUCCESS|
4319                                             USBA_HOTPLUG_SUCCESS);
4320                                         hubd->h_total_hotplug_success++;
4321 
4322                                         if (retry > 0) {
4323                                                 USB_DPRINTF_L2(
4324                                                     DPRINT_MASK_HOTPLUG,
4325                                                     hubd->h_log_handle,
4326                                                     "device on port %d "
4327                                                     "enumerated after %d %s",
4328                                                     port, retry,
4329                                                     (retry > 1) ? "retries" :
4330                                                     "retry");
4331 
4332                                         }
4333 
4334                                         return (USB_SUCCESS);
4335                                 }
4336                         }
4337                 }
4338 
4339                 /* wait a while until it settles? */
4340                 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4341                     "disabling port %d again", port);
4342 
4343                 (void) hubd_disable_port(hubd, port);
4344                 if (retry) {
4345                         mutex_exit(HUBD_MUTEX(hubd));
4346                         delay(time_delay);
4347                         mutex_enter(HUBD_MUTEX(hubd));
4348                 }
4349 
4350                 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4351                     "retrying on port %d", port);
4352         }
4353 
4354         if (retry >= hubd_retry_enumerate) {
4355                 /*
4356                  * If it is a High Speed Root Hub and connected device
4357                  * Is a Low/Full Speed, it will be handled by USB 1.1
4358                  * Host Controller. In this case, USB 2.0 Host Controller
4359                  * will transfer the ownership of this port to USB 1.1
4360                  * Host Controller. So don't display any error message on
4361                  * the console.
4362                  */
4363                 if ((hubd_usb_addr == ROOT_HUB_ADDR) &&
4364                     (hub_port_status == USBA_HIGH_SPEED_DEV) &&
4365                     (port_status != USBA_HIGH_SPEED_DEV)) {
4366                         USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
4367                             hubd->h_log_handle,
4368                             "hubd_handle_port_connect: Low/Full speed "
4369                             "device is connected to High Speed root hub");
4370                 } else {
4371                         USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG,
4372                             hubd->h_log_handle,
4373                             "Connecting device on port %d failed", port);
4374                 }
4375 
4376                 (void) hubd_disable_port(hubd, port);
4377                 usba_update_hotplug_stats(hubd->h_dip,
4378                     USBA_TOTAL_HOTPLUG_FAILURE|USBA_HOTPLUG_FAILURE);
4379                 hubd->h_total_hotplug_failure++;
4380 
4381                 /*
4382                  * the port should be automagically
4383                  * disabled but just in case, we do
4384                  * it here
4385                  */
4386                 (void) hubd_disable_port(hubd, port);
4387 
4388                 /* ack all changes because we disabled this port */
4389                 (void) hubd_determine_port_status(hubd,
4390                     port, &status, &change, HUBD_ACK_ALL_CHANGES);
4391 
4392         }
4393 
4394         return (USB_FAILURE);
4395 }
4396 
4397 
4398 /*
4399  * hubd_get_hub_status:
4400  */
4401 static int
4402 hubd_get_hub_status(hubd_t *hubd)
4403 {
4404         int             rval;
4405         usb_cr_t        completion_reason;
4406         usb_cb_flags_t  cb_flags;
4407         uint16_t        stword[2];
4408         uint16_t        status;
4409         uint16_t        change;
4410         usb_cfg_descr_t cfg_descr;
4411         size_t          cfg_length;
4412         uchar_t         *usb_cfg;
4413         uint8_t         MaxPower;
4414         usb_hub_descr_t *hub_descr;
4415         usb_port_t      port;
4416 
4417         USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4418             "hubd_get_hub_status:");
4419 
4420         ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
4421 
4422         if ((hubd_get_hub_status_words(hubd, stword)) != USB_SUCCESS) {
4423 
4424                 return (USB_FAILURE);
4425         }
4426         status = stword[0];
4427         change = stword[1];
4428 
4429         mutex_exit(HUBD_MUTEX(hubd));
4430 
4431         /* Obtain the raw configuration descriptor */
4432         usb_cfg = usb_get_raw_cfg_data(hubd->h_dip, &cfg_length);
4433 
4434         /* get configuration descriptor */
4435         rval = usb_parse_cfg_descr(usb_cfg, cfg_length,
4436             &cfg_descr, USB_CFG_DESCR_SIZE);
4437 
4438         if (rval != USB_CFG_DESCR_SIZE) {
4439 
4440                 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4441                     "get hub configuration descriptor failed.");
4442 
4443                 mutex_enter(HUBD_MUTEX(hubd));
4444 
4445                 return (USB_FAILURE);
4446         } else {
4447                 MaxPower = cfg_descr.bMaxPower;
4448         }
4449 
4450         /* check if local power status changed. */
4451         if (change & C_HUB_LOCAL_POWER_STATUS) {
4452 
4453                 /*
4454                  * local power has been lost, check the maximum
4455                  * power consumption of current configuration.
4456                  * see USB2.0 spec Table 11-12.
4457                  */
4458                 if (status & HUB_LOCAL_POWER_STATUS) {
4459 
4460                         if (MaxPower == 0) {
4461 
4462                                 /*
4463                                  * Self-powered only hub. Because it could
4464                                  * not draw any power from USB bus.
4465                                  * It can't work well on this condition.
4466                                  */
4467                                 USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG,
4468                                     hubd->h_log_handle,
4469                                     "local power has been lost, "
4470                                     "please disconnect hub");
4471                         } else {
4472 
4473                                 /*
4474                                  * Bus-powered only or self/bus-powered hub.
4475                                  */
4476                                 USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG,
4477                                     hubd->h_log_handle,
4478                                     "local power has been lost,"
4479                                     "the hub could draw %d"
4480                                     " mA power from the USB bus.",
4481                                     2*MaxPower);
4482                         }
4483 
4484                 }
4485 
4486                 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4487                     "clearing feature C_HUB_LOCAL_POWER ");
4488 
4489                 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
4490                     hubd->h_default_pipe,
4491                     HUB_HANDLE_HUB_FEATURE_TYPE,
4492                     USB_REQ_CLEAR_FEATURE,
4493                     CFS_C_HUB_LOCAL_POWER,
4494                     0,
4495                     0,
4496                     NULL, 0,
4497                     &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
4498                         USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
4499                             hubd->h_log_handle,
4500                             "clear feature C_HUB_LOCAL_POWER "
4501                             "failed (%d 0x%x %d)",
4502                             rval, completion_reason, cb_flags);
4503                 }
4504 
4505         }
4506 
4507         if (change & C_HUB_OVER_CURRENT) {
4508 
4509                 if (status & HUB_OVER_CURRENT) {
4510 
4511                         if (usba_is_root_hub(hubd->h_dip)) {
4512                                 /*
4513                                  * The root hub should be automatically
4514                                  * recovered when over-current condition is
4515                                  * cleared. But there might be exception and
4516                                  * need user interaction to recover.
4517                                  */
4518                                 USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG,
4519                                     hubd->h_log_handle,
4520                                     "Root hub over current condition, "
4521                                     "please check your system to clear the "
4522                                     "condition as soon as possible. And you "
4523                                     "may need to reboot the system to bring "
4524                                     "the root hub back to work if it cannot "
4525                                     "recover automatically");
4526                         } else {
4527                                 /*
4528                                  * The driver would try to recover port power
4529                                  * on over current condition. When the recovery
4530                                  * fails, the user may still need to offline
4531                                  * this hub in order to recover.
4532                                  * The port power is automatically disabled,
4533                                  * so we won't see disconnects.
4534                                  */
4535                                 USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG,
4536                                     hubd->h_log_handle,
4537                                     "Hub global over current condition, "
4538                                     "please disconnect the devices connected "
4539                                     "to the hub to clear the condition. And "
4540                                     "you may need to re-connect the hub if "
4541                                     "the ports do not work");
4542                         }
4543                 }
4544 
4545                 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
4546                     "clearing feature C_HUB_OVER_CURRENT");
4547 
4548                 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
4549                     hubd->h_default_pipe,
4550                     HUB_HANDLE_HUB_FEATURE_TYPE,
4551                     USB_REQ_CLEAR_FEATURE,
4552                     CFS_C_HUB_OVER_CURRENT,
4553                     0,
4554                     0,
4555                     NULL, 0,
4556                     &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
4557                         USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
4558                             hubd->h_log_handle,
4559                             "clear feature C_HUB_OVER_CURRENT "
4560                             "failed (%d 0x%x %d)",
4561                             rval, completion_reason, cb_flags);
4562                 }
4563 
4564                 /*
4565                  * Try to recover all port power if they are turned off.
4566                  * Don't do this for root hub, but rely on the root hub
4567                  * to recover itself.
4568                  */
4569                 if (!usba_is_root_hub(hubd->h_dip)) {
4570 
4571                         mutex_enter(HUBD_MUTEX(hubd));
4572 
4573                         /*
4574                          * Only check the power status of the 1st port
4575                          * since all port power status should be the same.
4576                          */
4577                         (void) hubd_determine_port_status(hubd, 1, &status,
4578                             &change, 0);
4579 
4580                         if (status & PORT_STATUS_PPS) {
4581 
4582                                 return (USB_SUCCESS);
4583                         }
4584 
4585                         hub_descr = &hubd->h_hub_descr;
4586 
4587                         for (port = 1; port <= hub_descr->bNbrPorts;
4588                             port++) {
4589 
4590                                 (void) hubd_enable_port_power(hubd, port);
4591                         }
4592 
4593                         mutex_exit(HUBD_MUTEX(hubd));
4594 
4595                         /*
4596                          * Delay some time to avoid over-current event
4597                          * to happen too frequently in some cases
4598                          */
4599                         delay(drv_usectohz(500000));
4600                 }
4601         }
4602 
4603         mutex_enter(HUBD_MUTEX(hubd));
4604 
4605         return (USB_SUCCESS);
4606 }
4607 
4608 
4609 /*
4610  * hubd_reset_port:
4611  */
4612 static int
4613 hubd_reset_port(hubd_t *hubd, usb_port_t port)
4614 {
4615         int     rval;
4616         usb_cr_t completion_reason;
4617         usb_cb_flags_t cb_flags;
4618         usb_port_mask_t port_mask = 1 << port;
4619         mblk_t  *data;
4620         uint16_t status;
4621         uint16_t change;
4622         int     i;
4623         clock_t delta;
4624 
4625         USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
4626             "hubd_reset_port: port=%d", port);
4627 
4628         ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
4629 
4630         hubd->h_port_reset_wait |= port_mask;
4631 
4632         mutex_exit(HUBD_MUTEX(hubd));
4633 
4634         if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
4635             hubd->h_default_pipe,
4636             HUB_HANDLE_PORT_FEATURE_TYPE,
4637             USB_REQ_SET_FEATURE,
4638             CFS_PORT_RESET,
4639             port,
4640             0,
4641             NULL, 0,
4642             &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
4643                 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
4644                     "reset port%d failed (%d 0x%x %d)",
4645                     port, completion_reason, cb_flags, rval);
4646 
4647                 mutex_enter(HUBD_MUTEX(hubd));
4648 
4649                 return (USB_FAILURE);
4650         }
4651 
4652         mutex_enter(HUBD_MUTEX(hubd));
4653 
4654         USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
4655             "waiting on cv for reset completion");
4656 
4657         /*
4658          * wait for port status change event
4659          */
4660         delta = drv_usectohz(hubd_device_delay / 10);
4661         for (i = 0; i < hubd_retry_enumerate; i++) {
4662                 /*
4663                  * start polling ep1 for receiving notification on
4664                  * reset completion
4665                  */
4666                 hubd_start_polling(hubd, HUBD_ALWAYS_START_POLLING);
4667 
4668                 /*
4669                  * sleep a max of 100ms for reset completion
4670                  * notification to be received
4671                  */
4672                 if (hubd->h_port_reset_wait & port_mask) {
4673                         rval = cv_reltimedwait(&hubd->h_cv_reset_port,
4674                             &hubd->h_mutex, delta, TR_CLOCK_TICK);
4675                         if ((rval <= 0) &&
4676                             (hubd->h_port_reset_wait & port_mask)) {
4677                                 /* we got woken up because of a timeout */
4678                                 USB_DPRINTF_L2(DPRINT_MASK_PORT,
4679                                     hubd->h_log_handle,
4680                                     "timeout: reset port=%d failed", port);
4681 
4682                                 hubd->h_port_reset_wait &=  ~port_mask;
4683 
4684                                 hubd_stop_polling(hubd);
4685 
4686                                 return (USB_FAILURE);
4687                         }
4688                 }
4689 
4690                 USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
4691                     "reset completion received");
4692 
4693                 hubd_stop_polling(hubd);
4694 
4695                 data = NULL;
4696 
4697                 /* check status to determine whether reset completed */
4698                 mutex_exit(HUBD_MUTEX(hubd));
4699                 if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
4700                     hubd->h_default_pipe,
4701                     HUB_GET_PORT_STATUS_TYPE,
4702                     USB_REQ_GET_STATUS,
4703                     0,
4704                     port,
4705                     GET_STATUS_LENGTH,
4706                     &data, 0,
4707                     &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
4708                         USB_DPRINTF_L2(DPRINT_MASK_PORT,
4709                             hubd->h_log_handle,
4710                             "get status port%d failed (%d 0x%x %d)",
4711                             port, completion_reason, cb_flags, rval);
4712 
4713                         if (data) {
4714                                 freemsg(data);
4715                                 data = NULL;
4716                         }
4717                         mutex_enter(HUBD_MUTEX(hubd));
4718 
4719                         continue;
4720                 }
4721 
4722                 status = (*(data->b_rptr + 1) << 8) | *(data->b_rptr);
4723                 change = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2);
4724 
4725                 freemsg(data);
4726 
4727                 /* continue only if port is still connected */
4728                 if (!(status & PORT_STATUS_CCS)) {
4729 
4730                         /* lost connection, set exit condition */
4731                         i = hubd_retry_enumerate;
4732 
4733                         mutex_enter(HUBD_MUTEX(hubd));
4734 
4735                         break;
4736                 }
4737 
4738                 if (status & PORT_STATUS_PRS) {
4739                         USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
4740                             "port%d reset active", port);
4741                         mutex_enter(HUBD_MUTEX(hubd));
4742 
4743                         continue;
4744                 } else {
4745                         USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
4746                             "port%d reset inactive", port);
4747                 }
4748 
4749                 if (change & PORT_CHANGE_PRSC) {
4750                         USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
4751                             "clearing feature CFS_C_PORT_RESET");
4752 
4753                         if (usb_pipe_sync_ctrl_xfer(hubd->h_dip,
4754                             hubd->h_default_pipe,
4755                             HUB_HANDLE_PORT_FEATURE_TYPE,
4756                             USB_REQ_CLEAR_FEATURE,
4757                             CFS_C_PORT_RESET,
4758                             port,
4759                             0,
4760                             NULL, 0,
4761                             &completion_reason, &cb_flags, 0) != USB_SUCCESS) {
4762                                 USB_DPRINTF_L2(DPRINT_MASK_PORT,
4763                                     hubd->h_log_handle,
4764                                     "clear feature CFS_C_PORT_RESET"
4765                                     " port%d failed (%d 0x%x %d)",
4766                                     port, completion_reason, cb_flags, rval);
4767                         }
4768                 }
4769                 mutex_enter(HUBD_MUTEX(hubd));
4770 
4771                 break;
4772         }
4773 
4774         if (i >= hubd_retry_enumerate) {
4775                 /* port reset has failed */
4776                 rval = USB_FAILURE;
4777         }
4778 
4779         return (rval);
4780 }
4781 
4782 
4783 /*
4784  * hubd_enable_port:
4785  *      this may fail if the hub as been disconnected
4786  */
4787 static int
4788 hubd_enable_port(hubd_t *hubd, usb_port_t port)
4789 {
4790         int     rval;
4791         usb_cr_t completion_reason;
4792         usb_cb_flags_t cb_flags;
4793 
4794         USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
4795             "hubd_enable_port: port=%d", port);
4796 
4797         ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
4798 
4799         mutex_exit(HUBD_MUTEX(hubd));
4800 
4801         /* Do not issue a SetFeature(PORT_ENABLE) on external hubs */
4802         if (!usba_is_root_hub(hubd->h_dip)) {
4803                 mutex_enter(HUBD_MUTEX(hubd));
4804 
4805                 return (USB_SUCCESS);
4806         }
4807 
4808         if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
4809             hubd->h_default_pipe,
4810             HUB_HANDLE_PORT_FEATURE_TYPE,
4811             USB_REQ_SET_FEATURE,
4812             CFS_PORT_ENABLE,
4813             port,
4814             0,
4815             NULL, 0,
4816             &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
4817                 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
4818                     "enable port%d failed (%d 0x%x %d)",
4819                     port, completion_reason, cb_flags, rval);
4820         }
4821 
4822         mutex_enter(HUBD_MUTEX(hubd));
4823 
4824         USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
4825             "enabling port done");
4826 
4827         return (rval);
4828 }
4829 
4830 
4831 /*
4832  * hubd_disable_port
4833  */
4834 static int
4835 hubd_disable_port(hubd_t *hubd, usb_port_t port)
4836 {
4837         int     rval;
4838         usb_cr_t completion_reason;
4839         usb_cb_flags_t cb_flags;
4840 
4841         USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
4842             "hubd_disable_port: port=%d", port);
4843 
4844         ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
4845 
4846         mutex_exit(HUBD_MUTEX(hubd));
4847 
4848         if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
4849             hubd->h_default_pipe,
4850             HUB_HANDLE_PORT_FEATURE_TYPE,
4851             USB_REQ_CLEAR_FEATURE,
4852             CFS_PORT_ENABLE,
4853             port,
4854             0,
4855             NULL, 0,
4856             &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
4857                 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
4858                     "disable port%d failed (%d 0x%x %d)", port,
4859                     completion_reason, cb_flags, rval);
4860                 mutex_enter(HUBD_MUTEX(hubd));
4861 
4862                 return (USB_FAILURE);
4863         }
4864 
4865         USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
4866             "clearing feature CFS_C_PORT_ENABLE");
4867 
4868         if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
4869             hubd->h_default_pipe,
4870             HUB_HANDLE_PORT_FEATURE_TYPE,
4871             USB_REQ_CLEAR_FEATURE,
4872             CFS_C_PORT_ENABLE,
4873             port,
4874             0,
4875             NULL, 0,
4876             &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
4877                 USB_DPRINTF_L2(DPRINT_MASK_PORT,
4878                     hubd->h_log_handle,
4879                     "clear feature CFS_C_PORT_ENABLE port%d failed "
4880                     "(%d 0x%x %d)",
4881                     port, completion_reason, cb_flags, rval);
4882 
4883                 mutex_enter(HUBD_MUTEX(hubd));
4884 
4885                 return (USB_FAILURE);
4886         }
4887 
4888         mutex_enter(HUBD_MUTEX(hubd));
4889 
4890         return (USB_SUCCESS);
4891 }
4892 
4893 
4894 /*
4895  * hubd_determine_port_status:
4896  */
4897 static int
4898 hubd_determine_port_status(hubd_t *hubd, usb_port_t port,
4899                 uint16_t *status, uint16_t *change, uint_t ack_flag)
4900 {
4901         int rval;
4902         mblk_t  *data = NULL;
4903         usb_cr_t completion_reason;
4904         usb_cb_flags_t cb_flags;
4905 
4906         USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
4907             "hubd_determine_port_status: port=%d, state=0x%x ack=0x%x", port,
4908             hubd->h_port_state[port], ack_flag);
4909 
4910         ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
4911 
4912         mutex_exit(HUBD_MUTEX(hubd));
4913 
4914         if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
4915             hubd->h_default_pipe,
4916             HUB_GET_PORT_STATUS_TYPE,
4917             USB_REQ_GET_STATUS,
4918             0,
4919             port,
4920             GET_STATUS_LENGTH,
4921             &data, 0,
4922             &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
4923                 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
4924                     "port=%d get status failed (%d 0x%x %d)",
4925                     port, completion_reason, cb_flags, rval);
4926 
4927                 if (data) {
4928                         freemsg(data);
4929                 }
4930 
4931                 *status = *change = 0;
4932                 mutex_enter(HUBD_MUTEX(hubd));
4933 
4934                 return (rval);
4935         }
4936 
4937         mutex_enter(HUBD_MUTEX(hubd));
4938         if (MBLKL(data) != GET_STATUS_LENGTH) {
4939                 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
4940                     "port %d: length incorrect %ld",
4941                     port, MBLKL(data));
4942                 freemsg(data);
4943                 *status = *change = 0;
4944 
4945                 return (rval);
4946         }
4947 
4948 
4949         *status = (*(data->b_rptr + 1) << 8) | *(data->b_rptr);
4950         *change = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2);
4951 
4952         USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
4953             "port%d status=0x%x, change=0x%x", port, *status, *change);
4954 
4955         freemsg(data);
4956 
4957         if (*status & PORT_STATUS_CCS) {
4958                 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
4959                     "port%d connected", port);
4960 
4961                 hubd->h_port_state[port] |= (PORT_STATUS_CCS & ack_flag);
4962         } else {
4963                 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
4964                     "port%d disconnected", port);
4965 
4966                 hubd->h_port_state[port] &= ~(PORT_STATUS_CCS & ack_flag);
4967         }
4968 
4969         if (*status & PORT_STATUS_PES) {
4970                 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
4971                     "port%d enabled", port);
4972 
4973                 hubd->h_port_state[port] |= (PORT_STATUS_PES & ack_flag);
4974         } else {
4975                 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
4976                     "port%d disabled", port);
4977 
4978                 hubd->h_port_state[port] &= ~(PORT_STATUS_PES & ack_flag);
4979         }
4980 
4981         if (*status & PORT_STATUS_PSS) {
4982                 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
4983                     "port%d suspended", port);
4984 
4985                 hubd->h_port_state[port] |= (PORT_STATUS_PSS & ack_flag);
4986         } else {
4987                 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
4988                     "port%d not suspended", port);
4989 
4990                 hubd->h_port_state[port] &= ~(PORT_STATUS_PSS & ack_flag);
4991         }
4992 
4993         if (*change & PORT_CHANGE_PRSC) {
4994                 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
4995                     "port%d reset completed", port);
4996 
4997                 hubd->h_port_state[port] |= (PORT_CHANGE_PRSC & ack_flag);
4998         } else {
4999 
5000                 hubd->h_port_state[port] &= ~(PORT_CHANGE_PRSC & ack_flag);
5001         }
5002 
5003         if (*status & PORT_STATUS_POCI) {
5004                 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
5005                     "port%d overcurrent!", port);
5006 
5007                 hubd->h_port_state[port] |= (PORT_STATUS_POCI & ack_flag);
5008         } else {
5009 
5010                 hubd->h_port_state[port] &= ~(PORT_STATUS_POCI & ack_flag);
5011         }
5012 
5013         if (*status & PORT_STATUS_PRS) {
5014                 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5015                     "port%d reset active", port);
5016 
5017                 hubd->h_port_state[port] |= (PORT_STATUS_PRS & ack_flag);
5018         } else {
5019                 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5020                     "port%d reset inactive", port);
5021 
5022                 hubd->h_port_state[port] &= ~(PORT_STATUS_PRS & ack_flag);
5023         }
5024         if (*status & PORT_STATUS_PPS) {
5025                 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5026                     "port%d power on", port);
5027 
5028                 hubd->h_port_state[port] |= (PORT_STATUS_PPS & ack_flag);
5029         } else {
5030                 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5031                     "port%d power off", port);
5032 
5033                 hubd->h_port_state[port] &= ~(PORT_STATUS_PPS & ack_flag);
5034         }
5035         if (*status & PORT_STATUS_LSDA) {
5036                 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5037                     "port%d low speed", port);
5038 
5039                 hubd->h_port_state[port] |= (PORT_STATUS_LSDA & ack_flag);
5040         } else {
5041                 hubd->h_port_state[port] &= ~(PORT_STATUS_LSDA & ack_flag);
5042                 if (*status & PORT_STATUS_HSDA) {
5043                         USB_DPRINTF_L3(DPRINT_MASK_PORT,
5044                             hubd->h_log_handle, "port%d "
5045                             "high speed", port);
5046 
5047                         hubd->h_port_state[port] |=
5048                             (PORT_STATUS_HSDA & ack_flag);
5049                 } else {
5050                         USB_DPRINTF_L3(DPRINT_MASK_PORT,
5051                             hubd->h_log_handle, "port%d "
5052                             "full speed", port);
5053 
5054                         hubd->h_port_state[port] &=
5055                             ~(PORT_STATUS_HSDA & ack_flag);
5056                 }
5057         }
5058 
5059         /*
5060          * Acknowledge connection, enable, reset status
5061          */
5062         if (ack_flag) {
5063                 mutex_exit(HUBD_MUTEX(hubd));
5064                 if (*change & PORT_CHANGE_CSC & ack_flag) {
5065                         USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5066                             "clearing feature CFS_C_PORT_CONNECTION");
5067                         if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5068                             hubd->h_default_pipe,
5069                             HUB_HANDLE_PORT_FEATURE_TYPE,
5070                             USB_REQ_CLEAR_FEATURE,
5071                             CFS_C_PORT_CONNECTION,
5072                             port,
5073                             0, NULL, 0,
5074                             &completion_reason, &cb_flags, 0)) !=
5075                             USB_SUCCESS) {
5076                                 USB_DPRINTF_L2(DPRINT_MASK_PORT,
5077                                     hubd->h_log_handle,
5078                                     "clear feature CFS_C_PORT_CONNECTION"
5079                                     " port%d failed (%d 0x%x %d)",
5080                                     port, completion_reason, cb_flags, rval);
5081                         }
5082                 }
5083                 if (*change & PORT_CHANGE_PESC & ack_flag) {
5084                         USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5085                             "clearing feature CFS_C_PORT_ENABLE");
5086                         if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5087                             hubd->h_default_pipe,
5088                             HUB_HANDLE_PORT_FEATURE_TYPE,
5089                             USB_REQ_CLEAR_FEATURE,
5090                             CFS_C_PORT_ENABLE,
5091                             port,
5092                             0, NULL, 0,
5093                             &completion_reason, &cb_flags, 0)) !=
5094                             USB_SUCCESS) {
5095                                 USB_DPRINTF_L2(DPRINT_MASK_PORT,
5096                                     hubd->h_log_handle,
5097                                     "clear feature CFS_C_PORT_ENABLE"
5098                                     " port%d failed (%d 0x%x %d)",
5099                                     port, completion_reason, cb_flags, rval);
5100                         }
5101                 }
5102                 if (*change & PORT_CHANGE_PSSC & ack_flag) {
5103                         USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5104                             "clearing feature CFS_C_PORT_SUSPEND");
5105 
5106                         if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5107                             hubd->h_default_pipe,
5108                             HUB_HANDLE_PORT_FEATURE_TYPE,
5109                             USB_REQ_CLEAR_FEATURE,
5110                             CFS_C_PORT_SUSPEND,
5111                             port,
5112                             0, NULL, 0,
5113                             &completion_reason, &cb_flags, 0)) !=
5114                             USB_SUCCESS) {
5115                                 USB_DPRINTF_L2(DPRINT_MASK_PORT,
5116                                     hubd->h_log_handle,
5117                                     "clear feature CFS_C_PORT_SUSPEND"
5118                                     " port%d failed (%d 0x%x %d)",
5119                                     port, completion_reason, cb_flags, rval);
5120                         }
5121                 }
5122                 if (*change & PORT_CHANGE_OCIC & ack_flag) {
5123                         USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5124                             "clearing feature CFS_C_PORT_OVER_CURRENT");
5125 
5126                         if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5127                             hubd->h_default_pipe,
5128                             HUB_HANDLE_PORT_FEATURE_TYPE,
5129                             USB_REQ_CLEAR_FEATURE,
5130                             CFS_C_PORT_OVER_CURRENT,
5131                             port,
5132                             0, NULL, 0,
5133                             &completion_reason, &cb_flags, 0)) !=
5134                             USB_SUCCESS) {
5135                                 USB_DPRINTF_L2(DPRINT_MASK_PORT,
5136                                     hubd->h_log_handle,
5137                                     "clear feature CFS_C_PORT_OVER_CURRENT"
5138                                     " port%d failed (%d 0x%x %d)",
5139                                     port, completion_reason, cb_flags, rval);
5140                         }
5141                 }
5142                 if (*change & PORT_CHANGE_PRSC & ack_flag) {
5143                         USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
5144                             "clearing feature CFS_C_PORT_RESET");
5145                         if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5146                             hubd->h_default_pipe,
5147                             HUB_HANDLE_PORT_FEATURE_TYPE,
5148                             USB_REQ_CLEAR_FEATURE,
5149                             CFS_C_PORT_RESET,
5150                             port,
5151                             0, NULL, 0,
5152                             &completion_reason, &cb_flags, 0)) !=
5153                             USB_SUCCESS) {
5154                                 USB_DPRINTF_L2(DPRINT_MASK_PORT,
5155                                     hubd->h_log_handle,
5156                                     "clear feature CFS_C_PORT_RESET"
5157                                     " port%d failed (%d 0x%x %d)",
5158                                     port, completion_reason, cb_flags, rval);
5159                         }
5160                 }
5161                 mutex_enter(HUBD_MUTEX(hubd));
5162         }
5163 
5164         USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
5165             "new port%d state 0x%x", port, hubd->h_port_state[port]);
5166 
5167 
5168         return (USB_SUCCESS);
5169 }
5170 
5171 
5172 /*
5173  * hubd_recover_disabled_port
5174  * if the port got disabled because of an error
5175  * enable it. If hub doesn't suport enable port,
5176  * reset the port to bring the device to life again
5177  */
5178 static int
5179 hubd_recover_disabled_port(hubd_t *hubd, usb_port_t port)
5180 {
5181         uint16_t        status;
5182         uint16_t        change;
5183         int             rval = USB_FAILURE;
5184 
5185         /* first try enabling the port */
5186         (void) hubd_enable_port(hubd, port);
5187 
5188         /* read the port status */
5189         (void) hubd_determine_port_status(hubd, port, &status, &change,
5190             PORT_CHANGE_PESC);
5191 
5192         if (status & PORT_STATUS_PES) {
5193                 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5194                     "Port%d now Enabled", port);
5195         } else if (status & PORT_STATUS_CCS) {
5196                 /* first post a disconnect event to the child */
5197                 mutex_exit(HUBD_MUTEX(hubd));
5198                 hubd_post_event(hubd, port, USBA_EVENT_TAG_HOT_REMOVAL);
5199                 mutex_enter(HUBD_MUTEX(hubd));
5200 
5201                 /* then reset the port and recover the device */
5202                 rval = hubd_handle_port_connect(hubd, port);
5203 
5204                 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5205                     "Port%d now Enabled by force", port);
5206         }
5207 
5208         return (rval);
5209 }
5210 
5211 
5212 /*
5213  * hubd_enable_all_port_power:
5214  */
5215 static int
5216 hubd_enable_all_port_power(hubd_t *hubd)
5217 {
5218         usb_hub_descr_t *hub_descr;
5219         int             wait;
5220         usb_port_t      port;
5221         uint_t          retry;
5222         uint16_t        status;
5223         uint16_t        change;
5224 
5225         USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
5226             "hubd_enable_all_port_power");
5227 
5228         ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
5229 
5230         hub_descr = &hubd->h_hub_descr;
5231 
5232         /*
5233          * According to section 11.11 of USB, for hubs with no power
5234          * switches, bPwrOn2PwrGood is zero. But we wait for some
5235          * arbitrary time to enable power to become stable.
5236          *
5237          * If an hub supports port power switching, we need to wait
5238          * at least 20ms before accessing corresponding usb port.
5239          */
5240         if ((hub_descr->wHubCharacteristics &
5241             HUB_CHARS_NO_POWER_SWITCHING) || (!hub_descr->bPwrOn2PwrGood)) {
5242                 wait = hubd_device_delay / 10;
5243         } else {
5244                 wait = max(HUB_DEFAULT_POPG,
5245                     hub_descr->bPwrOn2PwrGood) * 2 * 1000;
5246         }
5247 
5248         USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
5249             "hubd_enable_all_port_power: popg=%d wait=%d",
5250             hub_descr->bPwrOn2PwrGood, wait);
5251 
5252         /*
5253          * Enable power per port. we ignore gang power and power mask
5254          * and always enable all ports one by one.
5255          */
5256         for (port = 1; port <= hub_descr->bNbrPorts; port++) {
5257                 /*
5258                  * Transition the port from the Powered Off to the
5259                  * Disconnected state by supplying power to the port.
5260                  */
5261                 USB_DPRINTF_L4(DPRINT_MASK_PORT,
5262                     hubd->h_log_handle,
5263                     "hubd_enable_all_port_power: power port=%d", port);
5264 
5265                 (void) hubd_enable_port_power(hubd, port);
5266         }
5267 
5268         mutex_exit(HUBD_MUTEX(hubd));
5269         delay(drv_usectohz(wait));
5270         mutex_enter(HUBD_MUTEX(hubd));
5271 
5272         /* For retry if any, use some extra delay */
5273         wait = max(wait, hubd_device_delay / 10);
5274 
5275         /* Check each port power status for a given usb hub */
5276         for (port = 1; port <= hub_descr->bNbrPorts; port++) {
5277 
5278                 /* Get port status */
5279                 (void) hubd_determine_port_status(hubd, port,
5280                     &status, &change, 0);
5281 
5282                 for (retry = 0; ((!(status & PORT_STATUS_PPS)) &&
5283                     (retry < HUBD_PORT_RETRY)); retry++) {
5284 
5285                         USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
5286                             "Retry is in progress %d: port %d status %d",
5287                             retry, port, status);
5288 
5289                         (void) hubd_enable_port_power(hubd, port);
5290 
5291                         mutex_exit(HUBD_MUTEX(hubd));
5292                         delay(drv_usectohz(wait));
5293                         mutex_enter(HUBD_MUTEX(hubd));
5294 
5295                         /* Get port status */
5296                         (void) hubd_determine_port_status(hubd, port,
5297                             &status, &change, 0);
5298                 }
5299 
5300                 /* Print warning message if port has no power */
5301                 if (!(status & PORT_STATUS_PPS)) {
5302 
5303                         USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
5304                             "hubd_enable_all_port_power: port %d power-on "
5305                             "failed, port status 0x%x", port, status);
5306                 }
5307         }
5308 
5309         return (USB_SUCCESS);
5310 }
5311 
5312 
5313 /*
5314  * hubd_enable_port_power:
5315  *      enable individual port power
5316  */
5317 static int
5318 hubd_enable_port_power(hubd_t *hubd, usb_port_t port)
5319 {
5320         int             rval;
5321         usb_cr_t        completion_reason;
5322         usb_cb_flags_t  cb_flags;
5323 
5324         USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
5325             "hubd_enable_port_power: port=%d", port);
5326 
5327         ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
5328         ASSERT(hubd->h_default_pipe != 0);
5329 
5330         mutex_exit(HUBD_MUTEX(hubd));
5331 
5332         if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5333             hubd->h_default_pipe,
5334             HUB_HANDLE_PORT_FEATURE_TYPE,
5335             USB_REQ_SET_FEATURE,
5336             CFS_PORT_POWER,
5337             port,
5338             0, NULL, 0,
5339             &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
5340                 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
5341                     "set port power failed (%d 0x%x %d)",
5342                     completion_reason, cb_flags, rval);
5343                 mutex_enter(HUBD_MUTEX(hubd));
5344 
5345                 return (USB_FAILURE);
5346         } else {
5347                 mutex_enter(HUBD_MUTEX(hubd));
5348                 hubd->h_port_state[port] |= PORT_STATUS_PPS;
5349 
5350                 return (USB_SUCCESS);
5351         }
5352 }
5353 
5354 
5355 /*
5356  * hubd_disable_all_port_power:
5357  */
5358 static int
5359 hubd_disable_all_port_power(hubd_t *hubd)
5360 {
5361         usb_port_t port;
5362 
5363         USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
5364             "hubd_disable_all_port_power");
5365 
5366         ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
5367 
5368         /*
5369          * disable power per port, ignore gang power and power mask
5370          */
5371         for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
5372                 (void) hubd_disable_port_power(hubd, port);
5373         }
5374 
5375         return (USB_SUCCESS);
5376 }
5377 
5378 
5379 /*
5380  * hubd_disable_port_power:
5381  *      disable individual port power
5382  */
5383 static int
5384 hubd_disable_port_power(hubd_t *hubd, usb_port_t port)
5385 {
5386         int             rval;
5387         usb_cr_t        completion_reason;
5388         usb_cb_flags_t  cb_flags;
5389 
5390         USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
5391             "hubd_disable_port_power: port=%d", port);
5392 
5393         ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
5394 
5395         mutex_exit(HUBD_MUTEX(hubd));
5396 
5397         if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
5398             hubd->h_default_pipe,
5399             HUB_HANDLE_PORT_FEATURE_TYPE,
5400             USB_REQ_CLEAR_FEATURE,
5401             CFS_PORT_POWER,
5402             port,
5403             0, NULL, 0,
5404             &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
5405                 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
5406                     "clearing port%d power failed (%d 0x%x %d)",
5407                     port, completion_reason, cb_flags, rval);
5408 
5409                 mutex_enter(HUBD_MUTEX(hubd));
5410 
5411                 return (USB_FAILURE);
5412         } else {
5413 
5414                 mutex_enter(HUBD_MUTEX(hubd));
5415                 ASSERT(completion_reason == 0);
5416                 hubd->h_port_state[port] &= ~PORT_STATUS_PPS;
5417 
5418                 return (USB_SUCCESS);
5419         }
5420 }
5421 
5422 
5423 /*
5424  * Search the database of user preferences and find out the preferred
5425  * configuration for this new device
5426  */
5427 int
5428 hubd_select_device_configuration(hubd_t *hubd, usb_port_t port,
5429         dev_info_t *child_dip, usba_device_t *child_ud)
5430 {
5431         char            *pathname = NULL;
5432         char            *tmp_path = NULL;
5433         int             user_conf;
5434         int             pathlen;
5435         usb_dev_descr_t *usbdev_ptr;
5436         usba_configrec_t *user_pref;
5437 
5438         mutex_enter(&child_ud->usb_mutex);
5439         usbdev_ptr = child_ud->usb_dev_descr;
5440         mutex_exit(&child_ud->usb_mutex);
5441 
5442         /* try to get pathname for this device */
5443         tmp_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
5444         (void) ddi_pathname(child_dip, tmp_path);
5445 
5446         pathlen = strlen(tmp_path) + 32;
5447         pathname = kmem_zalloc(pathlen, KM_SLEEP);
5448 
5449         /*
5450          * We haven't initialized the node and it doesn't have an address
5451          * yet. Append port number to the physical pathname
5452          */
5453         (void) sprintf(pathname, "%s@%d", tmp_path, port);
5454 
5455         USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5456             "hubd_select_device_configuration: Device=%s\n\t"
5457             "Child path=%s",
5458             usba_get_mfg_prod_sn_str(child_dip, tmp_path, MAXPATHLEN),
5459             pathname);
5460         kmem_free(tmp_path, MAXPATHLEN);
5461 
5462 
5463         /* database search for user preferences */
5464         user_pref = usba_devdb_get_user_preferences(usbdev_ptr->idVendor,
5465             usbdev_ptr->idProduct, child_ud->usb_serialno_str, pathname);
5466 
5467         if (user_pref) {
5468                 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5469                     "hubd_select_device_configuration: "
5470                     "usba_devdb_get_user_preferences "
5471                     "return user_conf=%d\npreferred driver=%s path=%s",
5472                     user_pref->cfg_index, user_pref->driver,
5473                     user_pref->pathname);
5474 
5475                 user_conf = user_pref->cfg_index;
5476 
5477                 if (user_pref->driver) {
5478                         mutex_enter(&child_ud->usb_mutex);
5479                         child_ud->usb_preferred_driver = user_pref->driver;
5480                         mutex_exit(&child_ud->usb_mutex);
5481                 }
5482         } else {
5483                 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5484                     "hubd_select_device_configuration: No match found");
5485 
5486                 /* select default configuration for this device */
5487                 user_conf = USBA_DEV_CONFIG_INDEX_UNDEFINED;
5488         }
5489         kmem_free(pathname, pathlen);
5490 
5491         /* if the device has just one configuration, set default value */
5492         if (usbdev_ptr->bNumConfigurations == 1) {
5493                 user_conf = USB_DEV_DEFAULT_CONFIG_INDEX;
5494         }
5495 
5496         return (user_conf);
5497 }
5498 
5499 
5500 /*
5501  * Retrieves config cloud for this configuration
5502  */
5503 int
5504 hubd_get_this_config_cloud(hubd_t *hubd, dev_info_t *dip,
5505         usba_device_t *child_ud, uint16_t conf_index)
5506 {
5507         usb_cfg_descr_t *confdescr;
5508         mblk_t          *pdata = NULL;
5509         int             rval;
5510         size_t          size;
5511         char            *tmpbuf;
5512         usb_cr_t        completion_reason;
5513         usb_cb_flags_t  cb_flags;
5514         usb_pipe_handle_t       def_ph;
5515 
5516         USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5517             "hubd_get_this_config_cloud: conf_index=%d", conf_index);
5518 
5519 
5520         /* alloc temporary space for config descriptor */
5521         confdescr = (usb_cfg_descr_t *)kmem_zalloc(USB_CFG_DESCR_SIZE,
5522             KM_SLEEP);
5523 
5524         /* alloc temporary space for string descriptor */
5525         tmpbuf = kmem_zalloc(USB_MAXSTRINGLEN, KM_SLEEP);
5526 
5527         def_ph = usba_get_dflt_pipe_handle(dip);
5528 
5529         if ((rval = usb_pipe_sync_ctrl_xfer(dip, def_ph,
5530             USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
5531             USB_REQ_GET_DESCR,
5532             USB_DESCR_TYPE_SETUP_CFG | conf_index,
5533             0,
5534             USB_CFG_DESCR_SIZE,
5535             &pdata,
5536             0,
5537             &completion_reason,
5538             &cb_flags,
5539             0)) == USB_SUCCESS) {
5540 
5541                 /* this must be true since we didn't allow data underruns */
5542                 if (MBLKL(pdata) != USB_CFG_DESCR_SIZE) {
5543                         USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5544                             "device returned incorrect configuration "
5545                             "descriptor size.");
5546 
5547                         rval = USB_FAILURE;
5548                         goto done;
5549                 }
5550 
5551                 /*
5552                  * Parse the configuration descriptor
5553                  */
5554                 size = usb_parse_cfg_descr(pdata->b_rptr,
5555                     MBLKL(pdata), confdescr,
5556                     USB_CFG_DESCR_SIZE);
5557 
5558                 /* if parse cfg descr error, it should return failure */
5559                 if (size == USB_PARSE_ERROR) {
5560 
5561                         if (pdata->b_rptr[1] != USB_DESCR_TYPE_CFG) {
5562                                 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
5563                                     hubd->h_log_handle,
5564                                     "device returned incorrect "
5565                                     "configuration descriptor type.");
5566                         }
5567                         rval = USB_FAILURE;
5568                         goto done;
5569                 }
5570 
5571                 if (confdescr->wTotalLength < USB_CFG_DESCR_SIZE) {
5572                         USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
5573                             hubd->h_log_handle,
5574                             "device returned incorrect "
5575                             "configuration descriptor size.");
5576 
5577                         rval = USB_FAILURE;
5578                         goto done;
5579                 }
5580 
5581                 freemsg(pdata);
5582                 pdata = NULL;
5583 
5584                 /* Now fetch the complete config cloud */
5585                 if ((rval = usb_pipe_sync_ctrl_xfer(dip, def_ph,
5586                     USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
5587                     USB_REQ_GET_DESCR,
5588                     USB_DESCR_TYPE_SETUP_CFG | conf_index,
5589                     0,
5590                     confdescr->wTotalLength,
5591                     &pdata,
5592                     0,
5593                     &completion_reason,
5594                     &cb_flags,
5595                     0)) == USB_SUCCESS) {
5596 
5597                         if (MBLKL(pdata) !=
5598                             confdescr->wTotalLength) {
5599 
5600                                 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
5601                                     hubd->h_log_handle,
5602                                     "device returned incorrect "
5603                                     "configuration descriptor.");
5604 
5605                                 rval = USB_FAILURE;
5606                                 goto done;
5607                         }
5608 
5609                         /*
5610                          * copy config descriptor into usba_device
5611                          */
5612                         mutex_enter(&child_ud->usb_mutex);
5613                         child_ud->usb_cfg_array[conf_index] =
5614                             kmem_alloc(confdescr->wTotalLength, KM_SLEEP);
5615                         child_ud->usb_cfg_array_len[conf_index] =
5616                             confdescr->wTotalLength;
5617                         bcopy((caddr_t)pdata->b_rptr,
5618                             (caddr_t)child_ud->usb_cfg_array[conf_index],
5619                             confdescr->wTotalLength);
5620                         mutex_exit(&child_ud->usb_mutex);
5621 
5622                         /*
5623                          * retrieve string descriptor describing this
5624                          * configuration
5625                          */
5626                         if (confdescr->iConfiguration) {
5627 
5628                                 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG,
5629                                     hubd->h_log_handle,
5630                                     "Get conf str descr for config_index=%d",
5631                                     conf_index);
5632 
5633                                 /*
5634                                  * Now fetch the string descriptor describing
5635                                  * this configuration
5636                                  */
5637                                 if ((rval = usb_get_string_descr(dip,
5638                                     USB_LANG_ID, confdescr->iConfiguration,
5639                                     tmpbuf, USB_MAXSTRINGLEN)) ==
5640                                     USB_SUCCESS) {
5641                                         size = strlen(tmpbuf);
5642                                         if (size > 0) {
5643                                                 child_ud->usb_cfg_str_descr
5644                                                     [conf_index] = (char *)
5645                                                     kmem_zalloc(size + 1,
5646                                                     KM_SLEEP);
5647                                                 (void) strcpy(
5648                                                     child_ud->usb_cfg_str_descr
5649                                                     [conf_index], tmpbuf);
5650                                         }
5651                                 } else {
5652                                         USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
5653                                             hubd->h_log_handle,
5654                                             "hubd_get_this_config_cloud: "
5655                                             "getting config string (%d) "
5656                                             "failed",
5657                                             confdescr->iConfiguration);
5658 
5659                                         /* ignore this error */
5660                                         rval = USB_SUCCESS;
5661                                 }
5662                         }
5663                 }
5664         }
5665 
5666 done:
5667         if (rval != USB_SUCCESS) {
5668                 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5669                     "hubd_get_this_config_cloud: "
5670                     "error in retrieving config descriptor for "
5671                     "config index=%d rval=%d cr=%d",
5672                     conf_index, rval, completion_reason);
5673         }
5674 
5675         if (pdata) {
5676                 freemsg(pdata);
5677                 pdata = NULL;
5678         }
5679 
5680         kmem_free(confdescr, USB_CFG_DESCR_SIZE);
5681         kmem_free(tmpbuf, USB_MAXSTRINGLEN);
5682 
5683         return (rval);
5684 }
5685 
5686 
5687 /*
5688  * Retrieves the entire config cloud for all configurations of the device
5689  */
5690 int
5691 hubd_get_all_device_config_cloud(hubd_t *hubd, dev_info_t *dip,
5692         usba_device_t *child_ud)
5693 {
5694         int             rval = USB_SUCCESS;
5695         int             ncfgs;
5696         uint16_t        size;
5697         uint16_t        conf_index;
5698         uchar_t         **cfg_array;
5699         uint16_t        *cfg_array_len;
5700         char            **str_descr;
5701 
5702         USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5703             "hubd_get_all_device_config_cloud: Start");
5704 
5705         /* alloc pointer array for conf. descriptors */
5706         mutex_enter(&child_ud->usb_mutex);
5707         ncfgs = child_ud->usb_n_cfgs;
5708         mutex_exit(&child_ud->usb_mutex);
5709 
5710         size = sizeof (uchar_t *) * ncfgs;
5711         cfg_array = kmem_zalloc(size, KM_SLEEP);
5712         cfg_array_len = kmem_zalloc(ncfgs * sizeof (uint16_t), KM_SLEEP);
5713         str_descr = kmem_zalloc(size, KM_SLEEP);
5714 
5715         mutex_enter(&child_ud->usb_mutex);
5716         child_ud->usb_cfg_array = cfg_array;
5717         child_ud->usb_cfg_array_len = cfg_array_len;
5718         child_ud->usb_cfg_array_length = size;
5719         child_ud->usb_cfg_array_len_length = ncfgs * sizeof (uint16_t);
5720         child_ud->usb_cfg_str_descr = str_descr;
5721         mutex_exit(&child_ud->usb_mutex);
5722 
5723         /* Get configuration descriptor for each configuration */
5724         for (conf_index = 0; (conf_index < ncfgs) &&
5725             (rval == USB_SUCCESS); conf_index++) {
5726 
5727                 rval = hubd_get_this_config_cloud(hubd, dip, child_ud,
5728                     conf_index);
5729         }
5730 
5731         return (rval);
5732 }
5733 
5734 
5735 /*
5736  * hubd_ready_device:
5737  *      Update the usba_device structure
5738  *      Set the given configuration
5739  *      Prepares the device node for driver to online. If an existing
5740  *      OBP node is found, it will switch to the OBP node.
5741  */
5742 dev_info_t *
5743 hubd_ready_device(hubd_t *hubd, dev_info_t *child_dip, usba_device_t *child_ud,
5744     uint_t config_index)
5745 {
5746         usb_cr_t        completion_reason;
5747         usb_cb_flags_t  cb_flags;
5748         size_t          size;
5749         usb_cfg_descr_t config_descriptor;
5750         usb_pipe_handle_t def_ph;
5751         usba_pipe_handle_data_t *ph;
5752 
5753         USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5754             "hubd_ready_device: dip=0x%p, user_conf_index=%d",
5755             (void *)child_dip, config_index);
5756 
5757         size = usb_parse_cfg_descr(
5758             child_ud->usb_cfg_array[config_index], USB_CFG_DESCR_SIZE,
5759             &config_descriptor, USB_CFG_DESCR_SIZE);
5760         ASSERT(size == USB_CFG_DESCR_SIZE);
5761 
5762         def_ph = usba_get_dflt_pipe_handle(child_dip);
5763 
5764         /* Set the configuration */
5765         (void) usb_pipe_sync_ctrl_xfer(child_dip, def_ph,
5766             USB_DEV_REQ_HOST_TO_DEV,
5767             USB_REQ_SET_CFG,    /* bRequest */
5768             config_descriptor.bConfigurationValue,      /* wValue */
5769             0,                          /* wIndex */
5770             0,                          /* wLength */
5771             NULL,
5772             0,
5773             &completion_reason,
5774             &cb_flags,
5775             0);
5776 
5777         mutex_enter(&child_ud->usb_mutex);
5778         child_ud->usb_active_cfg_ndx = config_index;
5779         child_ud->usb_cfg            = child_ud->usb_cfg_array[config_index];
5780         child_ud->usb_cfg_length     = config_descriptor.wTotalLength;
5781         child_ud->usb_cfg_value      = config_descriptor.bConfigurationValue;
5782         child_ud->usb_n_ifs          = config_descriptor.bNumInterfaces;
5783         child_ud->usb_dip            = child_dip;
5784 
5785         child_ud->usb_client_flags   = kmem_zalloc(
5786             child_ud->usb_n_ifs * USBA_CLIENT_FLAG_SIZE, KM_SLEEP);
5787 
5788         child_ud->usb_client_attach_list = kmem_zalloc(
5789             child_ud->usb_n_ifs *
5790             sizeof (*child_ud->usb_client_attach_list), KM_SLEEP);
5791 
5792         child_ud->usb_client_ev_cb_list = kmem_zalloc(
5793             child_ud->usb_n_ifs *
5794             sizeof (*child_ud->usb_client_ev_cb_list), KM_SLEEP);
5795 
5796         mutex_exit(&child_ud->usb_mutex);
5797 
5798         /* ready the device node */
5799         child_dip = usba_ready_device_node(child_dip);
5800 
5801         /* set owner of default pipe to child dip */
5802         ph = usba_get_ph_data(def_ph);
5803         mutex_enter(&ph->p_mutex);
5804         mutex_enter(&ph->p_ph_impl->usba_ph_mutex);
5805         ph->p_ph_impl->usba_ph_dip = ph->p_dip = child_dip;
5806         mutex_exit(&ph->p_ph_impl->usba_ph_mutex);
5807         mutex_exit(&ph->p_mutex);
5808 
5809         return (child_dip);
5810 }
5811 
5812 
5813 /*
5814  * hubd_create_child
5815  *      - create child dip
5816  *      - open default pipe
5817  *      - get device descriptor
5818  *      - set the address
5819  *      - get device string descriptors
5820  *      - get the entire config cloud (all configurations) of the device
5821  *      - set user preferred configuration
5822  *      - close default pipe
5823  *      - load appropriate driver(s)
5824  */
5825 static int
5826 hubd_create_child(dev_info_t *dip,
5827                 hubd_t          *hubd,
5828                 usba_device_t   *hubd_ud,
5829                 usb_port_status_t port_status,
5830                 usb_port_t      port,
5831                 int             iteration)
5832 {
5833         dev_info_t              *child_dip = NULL;
5834         usb_dev_descr_t usb_dev_descr;
5835         int                     rval;
5836         usba_device_t           *child_ud = NULL;
5837         usba_device_t           *parent_ud = NULL;
5838         usb_pipe_handle_t       ph = NULL; /* default pipe handle */
5839         mblk_t                  *pdata = NULL;
5840         usb_cr_t                completion_reason;
5841         int                     user_conf_index;
5842         uint_t                  config_index;
5843         usb_cb_flags_t          cb_flags;
5844         uchar_t                 address = 0;
5845         uint16_t                length;
5846         size_t                  size;
5847         usb_addr_t              parent_usb_addr;
5848         usb_port_t              parent_usb_port;
5849         usba_device_t           *parent_usba_dev;
5850         usb_port_status_t       parent_port_status;
5851 
5852         USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5853             "hubd_create_child: port=%d", port);
5854 
5855         ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
5856         ASSERT(hubd->h_usba_devices[port] == NULL);
5857 
5858         mutex_exit(HUBD_MUTEX(hubd));
5859 
5860         /*
5861          * create a dip which can be used to open the pipe. we set
5862          * the name after getting the descriptors from the device
5863          */
5864         rval = usba_create_child_devi(dip,
5865             "device",           /* driver name */
5866             hubd_ud->usb_hcdi_ops, /* usba_hcdi ops */
5867             hubd_ud->usb_root_hub_dip,
5868             port_status,                /* low speed device */
5869             child_ud,
5870             &child_dip);
5871 
5872         if (rval != USB_SUCCESS) {
5873 
5874                 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5875                     "usb_create_child_devi failed (%d)", rval);
5876 
5877                 goto fail_cleanup;
5878         }
5879 
5880         child_ud = usba_get_usba_device(child_dip);
5881         ASSERT(child_ud != NULL);
5882 
5883         parent_ud = hubd->h_usba_device;
5884         mutex_enter(&parent_ud->usb_mutex);
5885         parent_port_status = parent_ud->usb_port_status;
5886 
5887         /*
5888          * To support split transactions, update address and port
5889          * of high speed hub to which given device is connected.
5890          */
5891         if (parent_port_status == USBA_HIGH_SPEED_DEV) {
5892                 parent_usba_dev = parent_ud;
5893                 parent_usb_addr = parent_ud->usb_addr;
5894                 parent_usb_port = port;
5895         } else {
5896                 parent_usba_dev = parent_ud->usb_hs_hub_usba_dev;
5897                 parent_usb_addr = parent_ud->usb_hs_hub_addr;
5898                 parent_usb_port = parent_ud->usb_hs_hub_port;
5899         }
5900         mutex_exit(&parent_ud->usb_mutex);
5901 
5902         mutex_enter(&child_ud->usb_mutex);
5903         address = child_ud->usb_addr;
5904         child_ud->usb_addr = 0;
5905         child_ud->usb_dev_descr = kmem_alloc(sizeof (usb_dev_descr_t),
5906             KM_SLEEP);
5907         bzero(&usb_dev_descr, sizeof (usb_dev_descr_t));
5908         usb_dev_descr.bMaxPacketSize0 =
5909             (port_status == USBA_LOW_SPEED_DEV) ? 8 : 64;
5910         bcopy(&usb_dev_descr, child_ud->usb_dev_descr,
5911             sizeof (usb_dev_descr_t));
5912         child_ud->usb_port = port;
5913         child_ud->usb_hs_hub_usba_dev = parent_usba_dev;
5914         child_ud->usb_hs_hub_addr = parent_usb_addr;
5915         child_ud->usb_hs_hub_port = parent_usb_port;
5916         mutex_exit(&child_ud->usb_mutex);
5917 
5918         /* Open the default pipe */
5919         if ((rval = usb_pipe_open(child_dip, NULL, NULL,
5920             USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) != USB_SUCCESS) {
5921                 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5922                     "usb_pipe_open failed (%d)", rval);
5923 
5924                 goto fail_cleanup;
5925         }
5926 
5927         /*
5928          * get device descriptor
5929          */
5930         USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5931             "hubd_create_child: get device descriptor: 64 bytes");
5932 
5933         rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
5934             USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
5935             USB_REQ_GET_DESCR,                  /* bRequest */
5936             USB_DESCR_TYPE_SETUP_DEV,           /* wValue */
5937             0,                                  /* wIndex */
5938             64,                                 /* wLength */
5939             &pdata, USB_ATTRS_SHORT_XFER_OK,
5940             &completion_reason, &cb_flags, 0);
5941 
5942         if ((rval != USB_SUCCESS) &&
5943             (!((completion_reason == USB_CR_DATA_OVERRUN) && pdata))) {
5944 
5945                 /*
5946                  * rval != USB_SUCCESS AND
5947                  * completion_reason != USB_CR_DATA_OVERRUN
5948                  * pdata could be != NULL.
5949                  * Free pdata now to prevent memory leak.
5950                  */
5951                 freemsg(pdata);
5952                 pdata = NULL;
5953 
5954                 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5955                     "hubd_create_child: get device descriptor: 8 bytes");
5956 
5957                 rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
5958                     USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
5959                     USB_REQ_GET_DESCR,                  /* bRequest */
5960                     USB_DESCR_TYPE_SETUP_DEV,           /* wValue */
5961                     0,                                  /* wIndex */
5962                     8,                                  /* wLength */
5963                     &pdata, USB_ATTRS_NONE,
5964                     &completion_reason, &cb_flags, 0);
5965 
5966                 if (rval != USB_SUCCESS) {
5967                         USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5968                             "getting device descriptor failed (%s 0x%x %d)",
5969                             usb_str_cr(completion_reason), cb_flags, rval);
5970                         goto fail_cleanup;
5971                 }
5972         } else {
5973                 ASSERT(completion_reason == USB_CR_OK);
5974         }
5975 
5976         ASSERT(pdata != NULL);
5977 
5978         size = usb_parse_dev_descr(
5979             pdata->b_rptr,
5980             MBLKL(pdata),
5981             &usb_dev_descr,
5982             sizeof (usb_dev_descr_t));
5983 
5984         USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5985             "parsing device descriptor returned %lu", size);
5986 
5987         length = *(pdata->b_rptr);
5988         freemsg(pdata);
5989         pdata = NULL;
5990         if (size < 8) {
5991                 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5992                     "get device descriptor returned %lu bytes", size);
5993 
5994                 goto fail_cleanup;
5995         }
5996 
5997         if (length < 8) {
5998                 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5999                     "fail enumeration: bLength=%d", length);
6000 
6001                 goto fail_cleanup;
6002         }
6003 
6004         /* Set the address of the device */
6005         if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
6006             USB_DEV_REQ_HOST_TO_DEV,
6007             USB_REQ_SET_ADDRESS,        /* bRequest */
6008             address,                    /* wValue */
6009             0,                          /* wIndex */
6010             0,                          /* wLength */
6011             NULL, 0,
6012             &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
6013                 char buffer[64];
6014                 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6015                     "setting address failed (cr=%s cb_flags=%s rval=%d)",
6016                     usb_str_cr(completion_reason),
6017                     usb_str_cb_flags(cb_flags, buffer, sizeof (buffer)),
6018                     rval);
6019 
6020                 goto fail_cleanup;
6021         }
6022 
6023         USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6024             "set address 0x%x done", address);
6025 
6026         /* now close the pipe for addr 0 */
6027         usb_pipe_close(child_dip, ph,
6028             USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
6029 
6030         /*
6031          * This delay is important for the CATC hub to enumerate
6032          * But, avoid delay in the first iteration
6033          */
6034         if (iteration) {
6035                 delay(drv_usectohz(hubd_device_delay/100));
6036         }
6037 
6038         /* assign the address in the usba_device structure */
6039         mutex_enter(&child_ud->usb_mutex);
6040         child_ud->usb_addr = address;
6041         child_ud->usb_no_cpr = 0;
6042         child_ud->usb_port_status = port_status;
6043         /* save this device descriptor */
6044         bcopy(&usb_dev_descr, child_ud->usb_dev_descr,
6045             sizeof (usb_dev_descr_t));
6046         child_ud->usb_n_cfgs = usb_dev_descr.bNumConfigurations;
6047         mutex_exit(&child_ud->usb_mutex);
6048 
6049         /* re-open the pipe for the device with the new address */
6050         if ((rval = usb_pipe_open(child_dip, NULL, NULL,
6051             USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) != USB_SUCCESS) {
6052                 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6053                     "usb_pipe_open failed (%d)", rval);
6054 
6055                 goto fail_cleanup;
6056         }
6057 
6058         /*
6059          * Get full device descriptor only if we have not received full
6060          * device descriptor earlier.
6061          */
6062         if (size < length) {
6063                 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6064                     "hubd_create_child: get full device descriptor: "
6065                     "%d bytes", length);
6066 
6067                 if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
6068                     USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
6069                     USB_REQ_GET_DESCR,                  /* bRequest */
6070                     USB_DESCR_TYPE_SETUP_DEV,           /* wValue */
6071                     0,                                  /* wIndex */
6072                     length,                             /* wLength */
6073                     &pdata, 0,
6074                     &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
6075                         freemsg(pdata);
6076                         pdata = NULL;
6077 
6078                         USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG,
6079                             hubd->h_log_handle,
6080                             "hubd_create_child: get full device descriptor: "
6081                             "64 bytes");
6082 
6083                         rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
6084                             USB_DEV_REQ_DEV_TO_HOST |
6085                             USB_DEV_REQ_TYPE_STANDARD,
6086                             USB_REQ_GET_DESCR,          /* bRequest */
6087                             USB_DESCR_TYPE_SETUP_DEV,   /* wValue */
6088                             0,                          /* wIndex */
6089                             64,                         /* wLength */
6090                             &pdata, USB_ATTRS_SHORT_XFER_OK,
6091                             &completion_reason, &cb_flags, 0);
6092 
6093                         /* we have to trust the data now */
6094                         if (pdata) {
6095                                 int len = *(pdata->b_rptr);
6096 
6097                                 length = MBLKL(pdata);
6098                                 if (length < len) {
6099 
6100                                         goto fail_cleanup;
6101                                 }
6102                         } else if (rval != USB_SUCCESS) {
6103                                 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
6104                                     hubd->h_log_handle,
6105                                     "getting device descriptor failed "
6106                                     "(%d 0x%x %d)",
6107                                     completion_reason, cb_flags, rval);
6108 
6109                                 goto fail_cleanup;
6110                         }
6111                 }
6112 
6113                 size = usb_parse_dev_descr(
6114                     pdata->b_rptr,
6115                     MBLKL(pdata),
6116                     &usb_dev_descr,
6117                     sizeof (usb_dev_descr_t));
6118 
6119                 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6120                     "parsing device descriptor returned %lu", size);
6121 
6122                 /*
6123                  * For now, free the data
6124                  * eventually, each configuration may need to be looked at
6125                  */
6126                 freemsg(pdata);
6127                 pdata = NULL;
6128 
6129                 if (size != USB_DEV_DESCR_SIZE) {
6130                         USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6131                             "fail enumeration: descriptor size=%lu "
6132                             "expected size=%u", size, USB_DEV_DESCR_SIZE);
6133 
6134                         goto fail_cleanup;
6135                 }
6136 
6137                 /*
6138                  * save the device descriptor in usba_device since it is needed
6139                  * later on again
6140                  */
6141                 mutex_enter(&child_ud->usb_mutex);
6142                 bcopy(&usb_dev_descr, child_ud->usb_dev_descr,
6143                     sizeof (usb_dev_descr_t));
6144                 child_ud->usb_n_cfgs = usb_dev_descr.bNumConfigurations;
6145                 mutex_exit(&child_ud->usb_mutex);
6146         }
6147 
6148         if (usb_dev_descr.bNumConfigurations == 0) {
6149                 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6150                     "device descriptor:\n\t"
6151                     "l=0x%x type=0x%x USB=0x%x class=0x%x subclass=0x%x\n\t"
6152                     "protocol=0x%x maxpktsize=0x%x "
6153                     "Vid=0x%x Pid=0x%x rel=0x%x\n\t"
6154                     "Mfg=0x%x P=0x%x sn=0x%x #config=0x%x",
6155                     usb_dev_descr.bLength, usb_dev_descr.bDescriptorType,
6156                     usb_dev_descr.bcdUSB, usb_dev_descr.bDeviceClass,
6157                     usb_dev_descr.bDeviceSubClass,
6158                     usb_dev_descr.bDeviceProtocol,
6159                     usb_dev_descr.bMaxPacketSize0,
6160                     usb_dev_descr.idVendor,
6161                     usb_dev_descr.idProduct, usb_dev_descr.bcdDevice,
6162                     usb_dev_descr.iManufacturer, usb_dev_descr.iProduct,
6163                     usb_dev_descr.iSerialNumber,
6164                     usb_dev_descr.bNumConfigurations);
6165                 goto fail_cleanup;
6166         }
6167 
6168 
6169         /* get the device string descriptor(s) */
6170         usba_get_dev_string_descrs(child_dip, child_ud);
6171 
6172         /* retrieve config cloud for all configurations */
6173         rval = hubd_get_all_device_config_cloud(hubd, child_dip, child_ud);
6174         if (rval != USB_SUCCESS) {
6175                 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6176                     "failed to get configuration descriptor(s)");
6177 
6178                 goto fail_cleanup;
6179         }
6180 
6181         /* get the preferred configuration for this device */
6182         user_conf_index = hubd_select_device_configuration(hubd, port,
6183             child_dip, child_ud);
6184 
6185         /* Check if the user selected configuration index is in range */
6186         if ((user_conf_index >= usb_dev_descr.bNumConfigurations) ||
6187             (user_conf_index < 0)) {
6188                 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6189                     "Configuration index for device idVendor=%d "
6190                     "idProduct=%d is=%d, and is out of range[0..%d]",
6191                     usb_dev_descr.idVendor, usb_dev_descr.idProduct,
6192                     user_conf_index, usb_dev_descr.bNumConfigurations - 1);
6193 
6194                 /* treat this as user didn't specify configuration */
6195                 user_conf_index = USBA_DEV_CONFIG_INDEX_UNDEFINED;
6196         }
6197 
6198 
6199         /*
6200          * Warn users of a performance hit if connecting a
6201          * High Speed behind a 1.1 hub, which is behind a
6202          * 2.0 port.
6203          */
6204         if ((parent_port_status != USBA_HIGH_SPEED_DEV) &&
6205             !(usba_is_root_hub(parent_ud->usb_dip)) &&
6206             (parent_usb_addr)) {
6207 
6208                 /*
6209                  * Now that we know the root port is a high speed port
6210                  * and that the parent port is not a high speed port,
6211                  * let's find out if the device itself is a high speed
6212                  * device.  If it is a high speed device,
6213                  * USB_DESCR_TYPE_SETUP_DEV_QLF should return a value,
6214                  * otherwise the command will fail.
6215                  */
6216                 rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
6217                     USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
6218                     USB_REQ_GET_DESCR,                  /* bRequest */
6219                     USB_DESCR_TYPE_SETUP_DEV_QLF,       /* wValue */
6220                     0,                                  /* wIndex */
6221                     10,                                 /* wLength */
6222                     &pdata, USB_ATTRS_SHORT_XFER_OK,
6223                     &completion_reason, &cb_flags, 0);
6224 
6225                 if (pdata) {
6226                         freemsg(pdata);
6227                         pdata = NULL;
6228                 }
6229 
6230                 /*
6231                  * USB_DESCR_TYPE_SETUP_DEV_QLF query was successful
6232                  * that means this is a high speed device behind a
6233                  * high speed root hub, but running at full speed
6234                  * because there is a full speed hub in the middle.
6235                  */
6236                 if (rval == USB_SUCCESS) {
6237                         USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG,
6238                             hubd->h_log_handle,
6239                             "Connecting a high speed device to a "
6240                             "non high speed hub (port %d) will result "
6241                             "in a loss of performance.  Please connect "
6242                             "the device to a high speed hub to get "
6243                             "the maximum performance.",
6244                             port);
6245                 }
6246         }
6247 
6248         /*
6249          * Now we try to online the device by attaching a driver
6250          * The following truth table illustrates the logic:-
6251          * Cfgndx       Driver  Action
6252          * 0            0       loop all configs for driver with full
6253          *                      compatible properties.
6254          * 0            1       set first configuration,
6255          *                      compatible prop = drivername.
6256          * 1            0       Set config, full compatible prop
6257          * 1            1       Set config, compatible prop = drivername.
6258          *
6259          * Note:
6260          *      cfgndx = user_conf_index
6261          *      Driver = usb_preferred_driver
6262          */
6263         if (user_conf_index == USBA_DEV_CONFIG_INDEX_UNDEFINED) {
6264                 if (child_ud->usb_preferred_driver) {
6265                         /*
6266                          * It is the job of the "preferred driver" to put the
6267                          * device in the desired configuration. Till then
6268                          * put the device in config index 0.
6269                          */
6270                         if ((rval = usba_hubdi_check_power_budget(dip, child_ud,
6271                             USB_DEV_DEFAULT_CONFIG_INDEX)) != USB_SUCCESS) {
6272 
6273                                 goto fail_cleanup;
6274                         }
6275 
6276                         child_dip = hubd_ready_device(hubd, child_dip,
6277                             child_ud, USB_DEV_DEFAULT_CONFIG_INDEX);
6278 
6279                         /*
6280                          * Assign the dip before onlining to avoid race
6281                          * with busctl
6282                          */
6283                         mutex_enter(HUBD_MUTEX(hubd));
6284                         hubd->h_children_dips[port] = child_dip;
6285                         mutex_exit(HUBD_MUTEX(hubd));
6286 
6287                         (void) usba_bind_driver(child_dip);
6288                 } else {
6289                         /*
6290                          * loop through all the configurations to see if we
6291                          * can find a driver for any one config. If not, set
6292                          * the device in config_index 0
6293                          */
6294                         rval = USB_FAILURE;
6295                         for (config_index = 0;
6296                             (config_index < usb_dev_descr.bNumConfigurations) &&
6297                             (rval != USB_SUCCESS); config_index++) {
6298 
6299                                 child_dip = hubd_ready_device(hubd, child_dip,
6300                                     child_ud, config_index);
6301 
6302                                 /*
6303                                  * Assign the dip before onlining to avoid race
6304                                  * with busctl
6305                                  */
6306                                 mutex_enter(HUBD_MUTEX(hubd));
6307                                 hubd->h_children_dips[port] = child_dip;
6308                                 mutex_exit(HUBD_MUTEX(hubd));
6309 
6310                                 rval = usba_bind_driver(child_dip);
6311 
6312                                 /*
6313                                  * Normally power budget should be checked
6314                                  * before device is configured. A failure in
6315                                  * power budget checking will stop the device
6316                                  * from being configured with current
6317                                  * config_index and may enable the device to
6318                                  * be configured in another configuration.
6319                                  * This may break the user experience that a
6320                                  * device which previously worked in config
6321                                  * A now works in config B after power budget
6322                                  * control is enabled. To avoid such situation,
6323                                  * power budget checking is moved here and will
6324                                  * fail the child creation directly if config
6325                                  * A exceeds the power available.
6326                                  */
6327                                 if (rval == USB_SUCCESS) {
6328                                         if ((usba_hubdi_check_power_budget(dip,
6329                                             child_ud, config_index)) !=
6330                                             USB_SUCCESS) {
6331 
6332                                                 goto fail_cleanup;
6333                                         }
6334                                 }
6335                         }
6336                         if (rval != USB_SUCCESS) {
6337 
6338                                 if ((usba_hubdi_check_power_budget(dip,
6339                                     child_ud, 0)) != USB_SUCCESS) {
6340 
6341                                         goto fail_cleanup;
6342                                 }
6343 
6344                                 child_dip = hubd_ready_device(hubd, child_dip,
6345                                     child_ud, 0);
6346                                 mutex_enter(HUBD_MUTEX(hubd));
6347                                 hubd->h_children_dips[port] = child_dip;
6348                                 mutex_exit(HUBD_MUTEX(hubd));
6349                         }
6350                 } /* end else loop all configs */
6351         } else {
6352 
6353                 if ((usba_hubdi_check_power_budget(dip, child_ud,
6354                     (uint_t)user_conf_index)) != USB_SUCCESS) {
6355 
6356                         goto fail_cleanup;
6357                 }
6358 
6359                 child_dip = hubd_ready_device(hubd, child_dip,
6360                     child_ud, (uint_t)user_conf_index);
6361 
6362                 /*
6363                  * Assign the dip before onlining to avoid race
6364                  * with busctl
6365                  */
6366                 mutex_enter(HUBD_MUTEX(hubd));
6367                 hubd->h_children_dips[port] = child_dip;
6368                 mutex_exit(HUBD_MUTEX(hubd));
6369 
6370                 (void) usba_bind_driver(child_dip);
6371         }
6372 
6373         usba_hubdi_decr_power_budget(dip, child_ud);
6374 
6375         mutex_enter(HUBD_MUTEX(hubd));
6376         if (hubd->h_usba_devices[port] == NULL) {
6377                 hubd->h_usba_devices[port] = usba_get_usba_device(child_dip);
6378         } else {
6379                 ASSERT(hubd->h_usba_devices[port] ==
6380                     usba_get_usba_device(child_dip));
6381         }
6382 
6383         return (USB_SUCCESS);
6384 
6385 
6386 fail_cleanup:
6387         USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6388             "hubd_create_child: fail_cleanup");
6389 
6390         mutex_enter(HUBD_MUTEX(hubd));
6391         hubd->h_children_dips[port] = NULL;
6392         mutex_exit(HUBD_MUTEX(hubd));
6393 
6394         if (pdata) {
6395                 freemsg(pdata);
6396         }
6397 
6398         if (ph) {
6399                 usb_pipe_close(child_dip, ph,
6400                     USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
6401         }
6402 
6403         if (child_dip) {
6404                 int rval = usba_destroy_child_devi(child_dip,
6405                     NDI_DEVI_REMOVE);
6406                 if (rval != USB_SUCCESS) {
6407                         USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6408                             "failure to remove child node");
6409                 }
6410         }
6411 
6412         if (child_ud) {
6413                 /* to make sure we free the address */
6414                 mutex_enter(&child_ud->usb_mutex);
6415                 child_ud->usb_addr = address;
6416                 ASSERT(child_ud->usb_ref_count == 0);
6417                 mutex_exit(&child_ud->usb_mutex);
6418 
6419                 mutex_enter(HUBD_MUTEX(hubd));
6420                 if (hubd->h_usba_devices[port] == NULL) {
6421                         mutex_exit(HUBD_MUTEX(hubd));
6422                         usba_free_usba_device(child_ud);
6423                 } else {
6424                         hubd_free_usba_device(hubd, hubd->h_usba_devices[port]);
6425                         mutex_exit(HUBD_MUTEX(hubd));
6426                 }
6427         }
6428 
6429         mutex_enter(HUBD_MUTEX(hubd));
6430 
6431         return (USB_FAILURE);
6432 }
6433 
6434 
6435 /*
6436  * hubd_delete_child:
6437  *      - free usb address
6438  *      - lookup child dips, there may be multiple on this port
6439  *      - offline each child devi
6440  */
6441 static int
6442 hubd_delete_child(hubd_t *hubd, usb_port_t port, uint_t flag, boolean_t retry)
6443 {
6444         dev_info_t      *child_dip;
6445         usba_device_t   *usba_device;
6446         int             rval = USB_SUCCESS;
6447 
6448         child_dip = hubd->h_children_dips[port];
6449         usba_device = hubd->h_usba_devices[port];
6450 
6451         USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6452             "hubd_delete_child: port=%d, dip=0x%p usba_device=0x%p",
6453             port, (void *)child_dip, (void *)usba_device);
6454 
6455         mutex_exit(HUBD_MUTEX(hubd));
6456         if (child_dip) {
6457                 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6458                     "hubd_delete_child:\n\t"
6459                     "dip = 0x%p (%s) at port %d",
6460                     (void *)child_dip, ddi_node_name(child_dip), port);
6461 
6462                 if (usba_device) {
6463                         usba_hubdi_incr_power_budget(hubd->h_dip, usba_device);
6464                 }
6465 
6466                 rval = usba_destroy_child_devi(child_dip, flag);
6467 
6468                 if ((rval != USB_SUCCESS) && usba_is_hwa(child_dip)) {
6469                         /*
6470                          * This is only useful for HWA device node.
6471                          * Since hwahc interface must hold hwarc interface
6472                          * open until hwahc is detached, the first call to
6473                          * ndi_devi_unconfig_one() can only offline hwahc
6474                          * driver but not hwarc driver. Need to make a second
6475                          * call to ndi_devi_unconfig_one() to make the hwarc
6476                          * driver detach.
6477                          */
6478                         rval = usba_destroy_child_devi(child_dip, flag);
6479                 }
6480 
6481                 if ((rval == USB_SUCCESS) && (flag & NDI_DEVI_REMOVE)) {
6482                         /*
6483                          * if the child was still < DS_INITIALIZED
6484                          * then our bus_unconfig was not called and
6485                          * we have to zap the child here
6486                          */
6487                         mutex_enter(HUBD_MUTEX(hubd));
6488                         if (hubd->h_children_dips[port] == child_dip) {
6489                                 usba_device_t *ud =
6490                                     hubd->h_usba_devices[port];
6491                                         hubd->h_children_dips[port] = NULL;
6492                                 if (ud) {
6493                                         mutex_exit(HUBD_MUTEX(hubd));
6494 
6495                                         mutex_enter(&ud->usb_mutex);
6496                                         ud->usb_ref_count = 0;
6497                                         mutex_exit(&ud->usb_mutex);
6498 
6499                                         usba_free_usba_device(ud);
6500                                         mutex_enter(HUBD_MUTEX(hubd));
6501                                         hubd->h_usba_devices[port] = NULL;
6502                                 }
6503                         }
6504                         mutex_exit(HUBD_MUTEX(hubd));
6505                 }
6506         }
6507 
6508         if ((rval != USB_SUCCESS) && retry) {
6509 
6510                 hubd_schedule_cleanup(usba_device->usb_root_hub_dip);
6511         }
6512         mutex_enter(HUBD_MUTEX(hubd));
6513 
6514         return (rval);
6515 }
6516 
6517 
6518 /*
6519  * hubd_free_usba_device:
6520  *      free usb device structure unless it is associated with
6521  *      the root hub which is handled differently
6522  */
6523 static void
6524 hubd_free_usba_device(hubd_t *hubd, usba_device_t *usba_device)
6525 {
6526         USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6527             "hubd_free_usba_device: hubd=0x%p, usba_device=0x%p",
6528             (void *)hubd, (void *)usba_device);
6529 
6530         if (usba_device && (usba_device->usb_addr != ROOT_HUB_ADDR)) {
6531                 usb_port_t port = usba_device->usb_port;
6532                 dev_info_t *dip = hubd->h_children_dips[port];
6533 
6534 #ifdef DEBUG
6535                 if (dip) {
6536                         ASSERT(i_ddi_node_state(dip) < DS_INITIALIZED);
6537                 }
6538 #endif
6539 
6540                 port = usba_device->usb_port;
6541                 hubd->h_usba_devices[port] = NULL;
6542 
6543                 mutex_exit(HUBD_MUTEX(hubd));
6544                 usba_free_usba_device(usba_device);
6545                 mutex_enter(HUBD_MUTEX(hubd));
6546         }
6547 }
6548 
6549 
6550 /*
6551  * event support
6552  *
6553  * busctl event support
6554  */
6555 static int
6556 hubd_busop_get_eventcookie(dev_info_t *dip,
6557         dev_info_t      *rdip,
6558         char            *eventname,
6559         ddi_eventcookie_t *cookie)
6560 {
6561         hubd_t  *hubd = (hubd_t *)hubd_get_soft_state(dip);
6562 
6563         USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6564             "hubd_busop_get_eventcookie: dip=0x%p, rdip=0x%p, "
6565             "event=%s", (void *)dip, (void *)rdip, eventname);
6566         USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6567             "(dip=%s%d, rdip=%s%d)",
6568             ddi_driver_name(dip), ddi_get_instance(dip),
6569             ddi_driver_name(rdip), ddi_get_instance(rdip));
6570 
6571         /* return event cookie, iblock cookie, and level */
6572         return (ndi_event_retrieve_cookie(hubd->h_ndi_event_hdl,
6573             rdip, eventname, cookie, NDI_EVENT_NOPASS));
6574 }
6575 
6576 
6577 static int
6578 hubd_busop_add_eventcall(dev_info_t *dip,
6579         dev_info_t      *rdip,
6580         ddi_eventcookie_t cookie,
6581         void            (*callback)(dev_info_t *dip,
6582                         ddi_eventcookie_t cookie, void *arg,
6583                         void *bus_impldata),
6584         void *arg, ddi_callback_id_t *cb_id)
6585 {
6586         hubd_t  *hubd = (hubd_t *)hubd_get_soft_state(dip);
6587         usb_port_t port = hubd_child_dip2port(hubd, rdip);
6588 
6589         USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6590             "hubd_busop_add_eventcall: dip=0x%p, rdip=0x%p "
6591             "cookie=0x%p, cb=0x%p, arg=0x%p",
6592             (void *)dip, (void *)rdip, (void *)cookie, (void *)callback, arg);
6593         USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6594             "(dip=%s%d, rdip=%s%d, event=%s)",
6595             ddi_driver_name(dip), ddi_get_instance(dip),
6596             ddi_driver_name(rdip), ddi_get_instance(rdip),
6597             ndi_event_cookie_to_name(hubd->h_ndi_event_hdl, cookie));
6598 
6599         /* Set flag on children registering events */
6600         switch (ndi_event_cookie_to_tag(hubd->h_ndi_event_hdl, cookie)) {
6601         case USBA_EVENT_TAG_HOT_REMOVAL:
6602                 mutex_enter(HUBD_MUTEX(hubd));
6603                 hubd->h_child_events[port] |= HUBD_CHILD_EVENT_DISCONNECT;
6604                 mutex_exit(HUBD_MUTEX(hubd));
6605 
6606                 break;
6607         case USBA_EVENT_TAG_PRE_SUSPEND:
6608                 mutex_enter(HUBD_MUTEX(hubd));
6609                 hubd->h_child_events[port] |= HUBD_CHILD_EVENT_PRESUSPEND;
6610                 mutex_exit(HUBD_MUTEX(hubd));
6611 
6612                 break;
6613         default:
6614 
6615                 break;
6616         }
6617 
6618         /* add callback to our event set */
6619         return (ndi_event_add_callback(hubd->h_ndi_event_hdl,
6620             rdip, cookie, callback, arg, NDI_SLEEP, cb_id));
6621 }
6622 
6623 
6624 static int
6625 hubd_busop_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id)
6626 {
6627         hubd_t  *hubd = (hubd_t *)hubd_get_soft_state(dip);
6628         ndi_event_callbacks_t *id = (ndi_event_callbacks_t *)cb_id;
6629 
6630         USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6631             "hubd_busop_remove_eventcall: dip=0x%p, rdip=0x%p "
6632             "cookie=0x%p", (void *)dip, (void *)id->ndi_evtcb_dip,
6633             (void *)id->ndi_evtcb_cookie);
6634         USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6635             "(dip=%s%d, rdip=%s%d, event=%s)",
6636             ddi_driver_name(dip), ddi_get_instance(dip),
6637             ddi_driver_name(id->ndi_evtcb_dip),
6638             ddi_get_instance(id->ndi_evtcb_dip),
6639             ndi_event_cookie_to_name(hubd->h_ndi_event_hdl,
6640             id->ndi_evtcb_cookie));
6641 
6642         /* remove event registration from our event set */
6643         return (ndi_event_remove_callback(hubd->h_ndi_event_hdl, cb_id));
6644 }
6645 
6646 
6647 /*
6648  * event distribution
6649  *
6650  * hubd_do_callback:
6651  *      Post this event to the specified child
6652  */
6653 static void
6654 hubd_do_callback(hubd_t *hubd, dev_info_t *cdip, ddi_eventcookie_t cookie)
6655 {
6656         USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6657             "hubd_do_callback");
6658 
6659         (void) ndi_event_do_callback(hubd->h_ndi_event_hdl, cdip, cookie, NULL);
6660 }
6661 
6662 
6663 /*
6664  * hubd_run_callbacks:
6665  *      Send this event to all children
6666  */
6667 static void
6668 hubd_run_callbacks(hubd_t *hubd, usba_event_t type)
6669 {
6670         usb_port_t      port;
6671 
6672         USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6673             "hubd_run_callbacks");
6674 
6675         mutex_enter(HUBD_MUTEX(hubd));
6676         for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
6677                 /*
6678                  * the childen_dips list may have dips that have been
6679                  * already deallocated. we only get a post_detach notification
6680                  * but not a destroy notification
6681                  */
6682                 if (hubd->h_children_dips[port]) {
6683                         mutex_exit(HUBD_MUTEX(hubd));
6684                         hubd_post_event(hubd, port, type);
6685                         mutex_enter(HUBD_MUTEX(hubd));
6686                 }
6687         }
6688         mutex_exit(HUBD_MUTEX(hubd));
6689 }
6690 
6691 
6692 /*
6693  * hubd_post_event
6694  *      post event to a child on the port depending on the type
6695  */
6696 static void
6697 hubd_post_event(hubd_t *hubd, usb_port_t port, usba_event_t type)
6698 {
6699         int     rval;
6700         dev_info_t      *dip;
6701         usba_device_t   *usba_device;
6702         ddi_eventcookie_t cookie, rm_cookie, suspend_cookie;
6703 
6704         USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6705             "hubd_post_event: port=%d event=%s", port,
6706             ndi_event_tag_to_name(hubd->h_ndi_event_hdl, type));
6707 
6708         cookie = ndi_event_tag_to_cookie(hubd->h_ndi_event_hdl, type);
6709         rm_cookie = ndi_event_tag_to_cookie(hubd->h_ndi_event_hdl,
6710             USBA_EVENT_TAG_HOT_REMOVAL);
6711         suspend_cookie = ndi_event_tag_to_cookie(hubd->h_ndi_event_hdl,
6712             USBA_EVENT_TAG_PRE_SUSPEND);
6713 
6714         /*
6715          * Hotplug daemon may be attaching a driver that may be registering
6716          * event callbacks. So it already has got the device tree lock and
6717          * event handle mutex. So to prevent a deadlock while posting events,
6718          * we grab and release the locks in the same order.
6719          */
6720         mutex_enter(HUBD_MUTEX(hubd));
6721         dip = hubd->h_children_dips[port];
6722         usba_device = hubd->h_usba_devices[port];
6723         mutex_exit(HUBD_MUTEX(hubd));
6724 
6725         switch (type) {
6726         case USBA_EVENT_TAG_HOT_REMOVAL:
6727                 /* Clear the registered event flag */
6728                 mutex_enter(HUBD_MUTEX(hubd));
6729                 hubd->h_child_events[port] &= ~HUBD_CHILD_EVENT_DISCONNECT;
6730                 mutex_exit(HUBD_MUTEX(hubd));
6731 
6732                 hubd_do_callback(hubd, dip, cookie);
6733                 usba_persistent_pipe_close(usba_device);
6734 
6735                 /*
6736                  * Mark the dip for deletion only after the driver has
6737                  * seen the disconnect event to prevent cleanup thread
6738                  * from stepping in between.
6739                  */
6740                 mutex_enter(&(DEVI(dip)->devi_lock));
6741                 DEVI_SET_DEVICE_REMOVED(dip);
6742                 mutex_exit(&(DEVI(dip)->devi_lock));
6743 
6744                 break;
6745         case USBA_EVENT_TAG_PRE_SUSPEND:
6746                 mutex_enter(HUBD_MUTEX(hubd));
6747                 hubd->h_child_events[port] &= ~HUBD_CHILD_EVENT_PRESUSPEND;
6748                 mutex_exit(HUBD_MUTEX(hubd));
6749 
6750                 hubd_do_callback(hubd, dip, cookie);
6751                 /*
6752                  * persistent pipe close for this event is taken care by the
6753                  * caller after verfying that all children can suspend
6754                  */
6755 
6756                 break;
6757         case USBA_EVENT_TAG_HOT_INSERTION:
6758                 /*
6759                  * Check if this child has missed the disconnect event before
6760                  * it registered for event callbacks
6761                  */
6762                 mutex_enter(HUBD_MUTEX(hubd));
6763                 if (hubd->h_child_events[port] & HUBD_CHILD_EVENT_DISCONNECT) {
6764                         /* clear the flag and post disconnect event */
6765                         hubd->h_child_events[port] &=
6766                             ~HUBD_CHILD_EVENT_DISCONNECT;
6767                         mutex_exit(HUBD_MUTEX(hubd));
6768                         hubd_do_callback(hubd, dip, rm_cookie);
6769                         usba_persistent_pipe_close(usba_device);
6770                         mutex_enter(HUBD_MUTEX(hubd));
6771                 }
6772                 mutex_exit(HUBD_MUTEX(hubd));
6773 
6774                 /*
6775                  * Mark the dip as reinserted to prevent cleanup thread
6776                  * from stepping in.
6777                  */
6778                 mutex_enter(&(DEVI(dip)->devi_lock));
6779                 DEVI_SET_DEVICE_REINSERTED(dip);
6780                 mutex_exit(&(DEVI(dip)->devi_lock));
6781 
6782                 rval = usba_persistent_pipe_open(usba_device);
6783                 if (rval != USB_SUCCESS) {
6784                         USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
6785                             hubd->h_log_handle,
6786                             "failed to reopen all pipes on reconnect");
6787                 }
6788 
6789                 hubd_do_callback(hubd, dip, cookie);
6790 
6791                 /*
6792                  * We might see a connect event only if hotplug thread for
6793                  * disconnect event don't run in time.
6794                  * Set the flag again, so we don't miss posting a
6795                  * disconnect event.
6796                  */
6797                 mutex_enter(HUBD_MUTEX(hubd));
6798                 hubd->h_child_events[port] |= HUBD_CHILD_EVENT_DISCONNECT;
6799                 mutex_exit(HUBD_MUTEX(hubd));
6800 
6801                 break;
6802         case USBA_EVENT_TAG_POST_RESUME:
6803                 /*
6804                  * Check if this child has missed the pre-suspend event before
6805                  * it registered for event callbacks
6806                  */
6807                 mutex_enter(HUBD_MUTEX(hubd));
6808                 if (hubd->h_child_events[port] & HUBD_CHILD_EVENT_PRESUSPEND) {
6809                         /* clear the flag and post pre_suspend event */
6810                         hubd->h_port_state[port] &=
6811                             ~HUBD_CHILD_EVENT_PRESUSPEND;
6812                         mutex_exit(HUBD_MUTEX(hubd));
6813                         hubd_do_callback(hubd, dip, suspend_cookie);
6814                         mutex_enter(HUBD_MUTEX(hubd));
6815                 }
6816                 mutex_exit(HUBD_MUTEX(hubd));
6817 
6818                 mutex_enter(&usba_device->usb_mutex);
6819                 usba_device->usb_no_cpr = 0;
6820                 mutex_exit(&usba_device->usb_mutex);
6821 
6822                 /*
6823                  * Since the pipe has already been opened by hub
6824                  * at DDI_RESUME time, there is no need for a
6825                  * persistent pipe open
6826                  */
6827                 hubd_do_callback(hubd, dip, cookie);
6828 
6829                 /*
6830                  * Set the flag again, so we don't miss posting a
6831                  * pre-suspend event. This enforces a tighter
6832                  * dev_state model.
6833                  */
6834                 mutex_enter(HUBD_MUTEX(hubd));
6835                 hubd->h_child_events[port] |= HUBD_CHILD_EVENT_PRESUSPEND;
6836                 mutex_exit(HUBD_MUTEX(hubd));
6837                 break;
6838         }
6839 }
6840 
6841 
6842 /*
6843  * handling of events coming from above
6844  */
6845 static int
6846 hubd_disconnect_event_cb(dev_info_t *dip)
6847 {
6848         hubd_t          *hubd = (hubd_t *)hubd_get_soft_state(dip);
6849         usb_port_t      port, nports;
6850         usba_device_t   *usba_dev;
6851         usba_event_t    tag = USBA_EVENT_TAG_HOT_REMOVAL;
6852         int             circ;
6853 
6854         USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6855             "hubd_disconnect_event_cb: tag=%d", tag);
6856 
6857         ndi_devi_enter(dip, &circ);
6858 
6859         mutex_enter(HUBD_MUTEX(hubd));
6860         switch (hubd->h_dev_state) {
6861         case USB_DEV_ONLINE:
6862         case USB_DEV_PWRED_DOWN:
6863                 hubd->h_dev_state = USB_DEV_DISCONNECTED;
6864                 /* stop polling on the interrupt pipe */
6865                 hubd_stop_polling(hubd);
6866 
6867                 /* FALLTHROUGH */
6868         case USB_DEV_SUSPENDED:
6869                 /* we remain in this state */
6870                 mutex_exit(HUBD_MUTEX(hubd));
6871                 hubd_run_callbacks(hubd, tag);
6872                 mutex_enter(HUBD_MUTEX(hubd));
6873 
6874                 /* close all the open pipes of our children */
6875                 nports = hubd->h_hub_descr.bNbrPorts;
6876                 for (port = 1; port <= nports; port++) {
6877                         usba_dev = hubd->h_usba_devices[port];
6878                         if (usba_dev != NULL) {
6879                                 mutex_exit(HUBD_MUTEX(hubd));
6880                                 usba_persistent_pipe_close(usba_dev);
6881                                 mutex_enter(HUBD_MUTEX(hubd));
6882                         }
6883                 }
6884 
6885                 break;
6886         case USB_DEV_DISCONNECTED:
6887                 /* avoid passing multiple disconnects to children */
6888                 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6889                     "hubd_disconnect_event_cb: Already disconnected");
6890 
6891                 break;
6892         default:
6893                 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6894                     "hubd_disconnect_event_cb: Illegal devstate=%d",
6895                     hubd->h_dev_state);
6896 
6897                 break;
6898         }
6899         mutex_exit(HUBD_MUTEX(hubd));
6900 
6901         ndi_devi_exit(dip, circ);
6902 
6903         return (USB_SUCCESS);
6904 }
6905 
6906 
6907 static int
6908 hubd_reconnect_event_cb(dev_info_t *dip)
6909 {
6910         int     rval, circ;
6911 
6912         ndi_devi_enter(dip, &circ);
6913         rval = hubd_restore_state_cb(dip);
6914         ndi_devi_exit(dip, circ);
6915 
6916         return (rval);
6917 }
6918 
6919 
6920 /*
6921  * hubd_pre_suspend_event_cb
6922  *      propogate event for binary compatibility of old drivers
6923  */
6924 static int
6925 hubd_pre_suspend_event_cb(dev_info_t *dip)
6926 {
6927         int     circ;
6928         hubd_t  *hubd = (hubd_t *)hubd_get_soft_state(dip);
6929 
6930         USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle,
6931             "hubd_pre_suspend_event_cb");
6932 
6933         /* disable hotplug thread */
6934         mutex_enter(HUBD_MUTEX(hubd));
6935         hubd->h_hotplug_thread++;
6936         hubd_stop_polling(hubd);
6937 
6938         /* keep PM out till we see a cpr resume */
6939         (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0);
6940         mutex_exit(HUBD_MUTEX(hubd));
6941 
6942         ndi_devi_enter(dip, &circ);
6943         hubd_run_callbacks(hubd, USBA_EVENT_TAG_PRE_SUSPEND);
6944         ndi_devi_exit(dip, circ);
6945 
6946         return (USB_SUCCESS);
6947 }
6948 
6949 
6950 /*
6951  * hubd_post_resume_event_cb
6952  *      propogate event for binary compatibility of old drivers
6953  */
6954 static int
6955 hubd_post_resume_event_cb(dev_info_t *dip)
6956 {
6957         int     circ;
6958         hubd_t  *hubd = (hubd_t *)hubd_get_soft_state(dip);
6959 
6960         USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle,
6961             "hubd_post_resume_event_cb");
6962 
6963         ndi_devi_enter(dip, &circ);
6964         hubd_run_callbacks(hubd, USBA_EVENT_TAG_POST_RESUME);
6965         ndi_devi_exit(dip, circ);
6966 
6967         mutex_enter(HUBD_MUTEX(hubd));
6968 
6969         /* enable PM */
6970         (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
6971 
6972         /* allow hotplug thread */
6973         hubd->h_hotplug_thread--;
6974 
6975         /* start polling */
6976         hubd_start_polling(hubd, 0);
6977         mutex_exit(HUBD_MUTEX(hubd));
6978 
6979         return (USB_SUCCESS);
6980 }
6981 
6982 
6983 /*
6984  * hubd_cpr_suspend
6985  *      save the current state of the driver/device
6986  */
6987 static int
6988 hubd_cpr_suspend(hubd_t *hubd)
6989 {
6990         usb_port_t      port, nports;
6991         usba_device_t   *usba_dev;
6992         uchar_t         no_cpr = 0;
6993         int             rval = USB_FAILURE;
6994 
6995         USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
6996             "hubd_cpr_suspend: Begin");
6997 
6998         /* Make sure device is powered up to save state. */
6999         mutex_enter(HUBD_MUTEX(hubd));
7000         hubd_pm_busy_component(hubd, hubd->h_dip, 0);
7001         mutex_exit(HUBD_MUTEX(hubd));
7002 
7003         /* bring the device to full power */
7004         (void) pm_raise_power(hubd->h_dip, 0, USB_DEV_OS_FULL_PWR);
7005         mutex_enter(HUBD_MUTEX(hubd));
7006 
7007         switch (hubd->h_dev_state) {
7008         case USB_DEV_ONLINE:
7009         case USB_DEV_PWRED_DOWN:
7010         case USB_DEV_DISCONNECTED:
7011                 /* find out if all our children have been quiesced */
7012                 nports = hubd->h_hub_descr.bNbrPorts;
7013                 for (port = 1; (no_cpr == 0) && (port <= nports); port++) {
7014                         usba_dev = hubd->h_usba_devices[port];
7015                         if (usba_dev != NULL) {
7016                                 mutex_enter(&usba_dev->usb_mutex);
7017                                 no_cpr += usba_dev->usb_no_cpr;
7018                                 mutex_exit(&usba_dev->usb_mutex);
7019                         }
7020                 }
7021                 if (no_cpr > 0) {
7022                         USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
7023                             "Children busy - can't checkpoint");
7024                         /* remain in same state to fail checkpoint */
7025 
7026                         break;
7027                 } else {
7028                         /*
7029                          * do not suspend if our hotplug thread
7030                          * or the deathrow thread is active
7031                          */
7032                         if ((hubd->h_hotplug_thread > 1) ||
7033                             (hubd->h_cleanup_active == B_TRUE)) {
7034                                 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
7035                                     hubd->h_log_handle,
7036                                     "hotplug thread active  - can't cpr");
7037                                 /* remain in same state to fail checkpoint */
7038 
7039                                 break;
7040                         }
7041 
7042                         /* quiesce ourselves now */
7043                         hubd_stop_polling(hubd);
7044 
7045                         /* close all the open pipes of our children */
7046                         for (port = 1; port <= nports; port++) {
7047                                 usba_dev = hubd->h_usba_devices[port];
7048                                 if (usba_dev != NULL) {
7049                                         mutex_exit(HUBD_MUTEX(hubd));
7050                                         usba_persistent_pipe_close(usba_dev);
7051                                         if (hubd_suspend_port(hubd, port)) {
7052                                                 USB_DPRINTF_L0(
7053                                                     DPRINT_MASK_HOTPLUG,
7054                                                     hubd->h_log_handle,
7055                                                     "suspending port %d failed",
7056                                                     port);
7057                                         }
7058                                         mutex_enter(HUBD_MUTEX(hubd));
7059                                 }
7060 
7061                         }
7062                         hubd->h_dev_state = USB_DEV_SUSPENDED;
7063 
7064                         /*
7065                          * if we are the root hub, we close our pipes
7066                          * ourselves.
7067                          */
7068                         if (usba_is_root_hub(hubd->h_dip)) {
7069                                 mutex_exit(HUBD_MUTEX(hubd));
7070                                 usba_persistent_pipe_close(
7071                                     usba_get_usba_device(hubd->h_dip));
7072                                 mutex_enter(HUBD_MUTEX(hubd));
7073                         }
7074                         rval = USB_SUCCESS;
7075 
7076                         break;
7077                 }
7078         case USB_DEV_SUSPENDED:
7079         default:
7080                 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
7081                     "hubd_cpr_suspend: Illegal dev state=%d",
7082                     hubd->h_dev_state);
7083 
7084                 break;
7085         }
7086 
7087         hubd_pm_idle_component(hubd, hubd->h_dip, 0);
7088         mutex_exit(HUBD_MUTEX(hubd));
7089 
7090         return (rval);
7091 }
7092 
7093 static void
7094 hubd_cpr_resume(dev_info_t *dip)
7095 {
7096         int     rval, circ;
7097 
7098         ndi_devi_enter(dip, &circ);
7099         /*
7100          * if we are the root hub, we open our pipes
7101          * ourselves.
7102          */
7103         if (usba_is_root_hub(dip)) {
7104                 rval = usba_persistent_pipe_open(
7105                     usba_get_usba_device(dip));
7106                 ASSERT(rval == USB_SUCCESS);
7107         }
7108         (void) hubd_restore_state_cb(dip);
7109         ndi_devi_exit(dip, circ);
7110 }
7111 
7112 
7113 /*
7114  * hubd_restore_state_cb
7115  *      Event callback to restore device state
7116  */
7117 static int
7118 hubd_restore_state_cb(dev_info_t *dip)
7119 {
7120         hubd_t  *hubd = (hubd_t *)hubd_get_soft_state(dip);
7121 
7122         USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
7123             "hubd_restore_state_cb: Begin");
7124 
7125         /* restore the state of this device */
7126         hubd_restore_device_state(dip, hubd);
7127 
7128         return (USB_SUCCESS);
7129 }
7130 
7131 
7132 /*
7133  * registering for events
7134  */
7135 static int
7136 hubd_register_events(hubd_t *hubd)
7137 {
7138         int             rval = USB_SUCCESS;
7139 
7140         if (usba_is_root_hub(hubd->h_dip)) {
7141                 hubd_register_cpr_callback(hubd);
7142         } else {
7143                 rval = usb_register_event_cbs(hubd->h_dip, &hubd_events, 0);
7144         }
7145 
7146         return (rval);
7147 }
7148 
7149 
7150 /*
7151  * hubd cpr callback related functions
7152  *
7153  * hubd_cpr_post_user_callb:
7154  *      This function is called during checkpoint & resume -
7155  *              1. after user threads are stopped during checkpoint
7156  *              2. after kernel threads are resumed during resume
7157  */
7158 /* ARGSUSED */
7159 static boolean_t
7160 hubd_cpr_post_user_callb(void *arg, int code)
7161 {
7162         hubd_cpr_t      *cpr_cb = (hubd_cpr_t *)arg;
7163         hubd_t          *hubd = cpr_cb->statep;
7164         int             retry = 0;
7165 
7166         USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle,
7167             "hubd_cpr_post_user_callb");
7168 
7169         switch (code) {
7170         case CB_CODE_CPR_CHKPT:
7171                 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, hubd->h_log_handle,
7172                     "hubd_cpr_post_user_callb: CB_CODE_CPR_CHKPT");
7173 
7174                 mutex_enter(HUBD_MUTEX(hubd));
7175 
7176                 /* turn off deathrow thread */
7177                 hubd->h_cleanup_enabled = B_FALSE;
7178 
7179                 /* give up if deathrow thread doesn't exit */
7180                 while ((hubd->h_cleanup_active == B_TRUE) && (retry++ < 3)) {
7181                         mutex_exit(HUBD_MUTEX(hubd));
7182                         delay(drv_usectohz(hubd_dip_cleanup_delay));
7183 
7184                         USB_DPRINTF_L2(DPRINT_MASK_EVENTS, hubd->h_log_handle,
7185                             "hubd_cpr_post_user_callb, waiting for "
7186                             "deathrow thread to exit");
7187                         mutex_enter(HUBD_MUTEX(hubd));
7188                 }
7189 
7190                 mutex_exit(HUBD_MUTEX(hubd));
7191 
7192                 /* save the state of the device */
7193                 (void) hubd_pre_suspend_event_cb(hubd->h_dip);
7194 
7195                 return (B_TRUE);
7196         case CB_CODE_CPR_RESUME:
7197                 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, hubd->h_log_handle,
7198                     "hubd_cpr_post_user_callb: CB_CODE_CPR_RESUME");
7199 
7200                 /* restore the state of the device */
7201                 (void) hubd_post_resume_event_cb(hubd->h_dip);
7202 
7203                 /* turn on deathrow thread */
7204                 mutex_enter(HUBD_MUTEX(hubd));
7205                 hubd->h_cleanup_enabled = B_TRUE;
7206                 mutex_exit(HUBD_MUTEX(hubd));
7207 
7208                 hubd_schedule_cleanup(hubd->h_usba_device->usb_root_hub_dip);
7209 
7210                 return (B_TRUE);
7211         default:
7212 
7213                 return (B_FALSE);
7214         }
7215 
7216 }
7217 
7218 
7219 /* register callback with cpr framework */
7220 void
7221 hubd_register_cpr_callback(hubd_t *hubd)
7222 {
7223         USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle,
7224             "hubd_register_cpr_callback");
7225 
7226         mutex_enter(HUBD_MUTEX(hubd));
7227         hubd->h_cpr_cb =
7228             (hubd_cpr_t *)kmem_zalloc(sizeof (hubd_cpr_t), KM_SLEEP);
7229         mutex_exit(HUBD_MUTEX(hubd));
7230         mutex_init(&hubd->h_cpr_cb->lockp, NULL, MUTEX_DRIVER,
7231             hubd->h_dev_data->dev_iblock_cookie);
7232         hubd->h_cpr_cb->statep = hubd;
7233         hubd->h_cpr_cb->cpr.cc_lockp = &hubd->h_cpr_cb->lockp;
7234         hubd->h_cpr_cb->cpr.cc_id = callb_add(hubd_cpr_post_user_callb,
7235             (void *)hubd->h_cpr_cb, CB_CL_CPR_POST_USER, "hubd");
7236 }
7237 
7238 
7239 /* unregister callback with cpr framework */
7240 void
7241 hubd_unregister_cpr_callback(hubd_t *hubd)
7242 {
7243         USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle,
7244             "hubd_unregister_cpr_callback");
7245 
7246         if (hubd->h_cpr_cb) {
7247                 (void) callb_delete(hubd->h_cpr_cb->cpr.cc_id);
7248                 mutex_destroy(&hubd->h_cpr_cb->lockp);
7249                 mutex_enter(HUBD_MUTEX(hubd));
7250                 kmem_free(hubd->h_cpr_cb, sizeof (hubd_cpr_t));
7251                 mutex_exit(HUBD_MUTEX(hubd));
7252         }
7253 }
7254 
7255 
7256 /*
7257  * Power management
7258  *
7259  * create the pm components required for power management
7260  */
7261 static void
7262 hubd_create_pm_components(dev_info_t *dip, hubd_t *hubd)
7263 {
7264         hub_power_t     *hubpm;
7265 
7266         USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
7267             "hubd_create_pm_components: Begin");
7268 
7269         /* Allocate the state structure */
7270         hubpm = kmem_zalloc(sizeof (hub_power_t), KM_SLEEP);
7271 
7272         hubd->h_hubpm = hubpm;
7273         hubpm->hubp_hubd = hubd;
7274         hubpm->hubp_pm_capabilities = 0;
7275         hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR;
7276         hubpm->hubp_time_at_full_power = ddi_get_time();
7277         hubpm->hubp_min_pm_threshold = hubdi_min_pm_threshold;
7278 
7279         /* alloc memory to save power states of children */
7280         hubpm->hubp_child_pwrstate = (uint8_t *)
7281             kmem_zalloc(MAX_PORTS + 1, KM_SLEEP);
7282 
7283         /*
7284          * if the enable remote wakeup fails
7285          * we still want to enable
7286          * parent notification so we can PM the children
7287          */
7288         usb_enable_parent_notification(dip);
7289 
7290         if (usb_handle_remote_wakeup(dip,
7291             USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) {
7292                 uint_t          pwr_states;
7293 
7294                 USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
7295                     "hubd_create_pm_components: "
7296                     "Remote Wakeup Enabled");
7297 
7298                 if (usb_create_pm_components(dip, &pwr_states) ==
7299                     USB_SUCCESS) {
7300                         mutex_enter(HUBD_MUTEX(hubd));
7301                         hubpm->hubp_wakeup_enabled = 1;
7302                         hubpm->hubp_pwr_states = (uint8_t)pwr_states;
7303 
7304                         /* we are busy now till end of the attach */
7305                         hubd_pm_busy_component(hubd, dip, 0);
7306                         mutex_exit(HUBD_MUTEX(hubd));
7307 
7308                         /* bring the device to full power */
7309                         (void) pm_raise_power(dip, 0,
7310                             USB_DEV_OS_FULL_PWR);
7311                 }
7312         }
7313 
7314         USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
7315             "hubd_create_pm_components: END");
7316 }
7317 
7318 
7319 /*
7320  * Attachment point management
7321  */
7322 /* ARGSUSED */
7323 int
7324 usba_hubdi_open(dev_info_t *dip, dev_t *devp, int flags, int otyp,
7325         cred_t *credp)
7326 {
7327         hubd_t *hubd;
7328 
7329         if (otyp != OTYP_CHR)
7330                 return (EINVAL);
7331 
7332         hubd = hubd_get_soft_state(dip);
7333         if (hubd == NULL) {
7334                 return (ENXIO);
7335         }
7336 
7337         USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
7338             "hubd_open:");
7339 
7340         mutex_enter(HUBD_MUTEX(hubd));
7341         if ((flags & FEXCL) && (hubd->h_softstate & HUBD_SS_ISOPEN)) {
7342                 mutex_exit(HUBD_MUTEX(hubd));
7343 
7344                 return (EBUSY);
7345         }
7346 
7347         hubd->h_softstate |= HUBD_SS_ISOPEN;
7348         mutex_exit(HUBD_MUTEX(hubd));
7349 
7350         USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, "opened");
7351 
7352         return (0);
7353 }
7354 
7355 
7356 /* ARGSUSED */
7357 int
7358 usba_hubdi_close(dev_info_t *dip, dev_t dev, int flag, int otyp,
7359         cred_t *credp)
7360 {
7361         hubd_t *hubd;
7362 
7363         if (otyp != OTYP_CHR) {
7364                 return (EINVAL);
7365         }
7366 
7367         hubd = hubd_get_soft_state(dip);
7368 
7369         if (hubd == NULL) {
7370                 return (ENXIO);
7371         }
7372 
7373         USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, "hubd_close:");
7374 
7375         mutex_enter(HUBD_MUTEX(hubd));
7376         hubd->h_softstate &= ~HUBD_SS_ISOPEN;
7377         mutex_exit(HUBD_MUTEX(hubd));
7378 
7379         USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, "closed");
7380 
7381         return (0);
7382 }
7383 
7384 
7385 /*
7386  * hubd_ioctl: cfgadm controls
7387  */
7388 /* ARGSUSED */
7389 int
7390 usba_hubdi_ioctl(dev_info_t *self, dev_t dev, int cmd, intptr_t arg,
7391         int mode, cred_t *credp, int *rvalp)
7392 {
7393         int                     rv = 0;
7394         char                    *msg;   /* for messages */
7395         hubd_t                  *hubd;
7396         usb_port_t              port = 0;
7397         dev_info_t              *child_dip = NULL;
7398         dev_info_t              *rh_dip;
7399         devctl_ap_state_t       ap_state;
7400         struct devctl_iocdata   *dcp = NULL;
7401         usb_pipe_state_t        prev_pipe_state = 0;
7402         int                     circ, rh_circ, prh_circ;
7403 
7404         if ((hubd = hubd_get_soft_state(self)) == NULL) {
7405 
7406                 return (ENXIO);
7407         }
7408 
7409         rh_dip = hubd->h_usba_device->usb_root_hub_dip;
7410 
7411         USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
7412             "usba_hubdi_ioctl: "
7413             "cmd=%x, arg=%lx, mode=%x, cred=%p, rval=%p dev=0x%lx",
7414             cmd, arg, mode, (void *)credp, (void *)rvalp, dev);
7415 
7416         /* read devctl ioctl data */
7417         if ((cmd != DEVCTL_AP_CONTROL) &&
7418             (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)) {
7419 
7420                 return (EFAULT);
7421         }
7422 
7423         /*
7424          * make sure the hub is connected before trying any
7425          * of the following operations:
7426          * configure, connect, disconnect
7427          */
7428         mutex_enter(HUBD_MUTEX(hubd));
7429 
7430         switch (cmd) {
7431         case DEVCTL_AP_DISCONNECT:
7432         case DEVCTL_AP_UNCONFIGURE:
7433         case DEVCTL_AP_CONFIGURE:
7434                 if (hubd->h_dev_state == USB_DEV_DISCONNECTED) {
7435                         USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
7436                             "hubd: already gone");
7437                         mutex_exit(HUBD_MUTEX(hubd));
7438                         if (dcp) {
7439                                 ndi_dc_freehdl(dcp);
7440                         }
7441 
7442                         return (EIO);
7443                 }
7444 
7445                 /* FALLTHROUGH */
7446         case DEVCTL_AP_GETSTATE:
7447                 if ((port = hubd_get_port_num(hubd, dcp)) == 0) {
7448                         USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
7449                             "hubd: bad port");
7450                         mutex_exit(HUBD_MUTEX(hubd));
7451                         if (dcp) {
7452                                 ndi_dc_freehdl(dcp);
7453                         }
7454 
7455                         return (EINVAL);
7456                 }
7457                 break;
7458 
7459         case DEVCTL_AP_CONTROL:
7460 
7461                 break;
7462         default:
7463                 mutex_exit(HUBD_MUTEX(hubd));
7464                 if (dcp) {
7465                         ndi_dc_freehdl(dcp);
7466                 }
7467 
7468                 return (ENOTTY);
7469         }
7470 
7471         /* should not happen, just in case */
7472         if (hubd->h_dev_state == USB_DEV_SUSPENDED) {
7473                 mutex_exit(HUBD_MUTEX(hubd));
7474                 if (dcp) {
7475                         ndi_dc_freehdl(dcp);
7476                 }
7477 
7478                 return (EIO);
7479         }
7480 
7481         if (hubd->h_reset_port[port]) {
7482                 USB_DPRINTF_L2(DPRINT_MASK_CBOPS, hubd->h_log_handle,
7483                     "This port is resetting, just return");
7484                 mutex_exit(HUBD_MUTEX(hubd));
7485                 if (dcp) {
7486                         ndi_dc_freehdl(dcp);
7487                 }
7488 
7489                 return (EIO);
7490         }
7491 
7492         hubd_pm_busy_component(hubd, hubd->h_dip, 0);
7493         mutex_exit(HUBD_MUTEX(hubd));
7494 
7495         /* go full power */
7496         (void) pm_raise_power(hubd->h_dip, 0, USB_DEV_OS_FULL_PWR);
7497 
7498         ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
7499         ndi_devi_enter(rh_dip, &rh_circ);
7500         ndi_devi_enter(hubd->h_dip, &circ);
7501 
7502         mutex_enter(HUBD_MUTEX(hubd));
7503 
7504         hubd->h_hotplug_thread++;
7505 
7506         /* stop polling if it was active */
7507         if (hubd->h_ep1_ph) {
7508                 mutex_exit(HUBD_MUTEX(hubd));
7509                 (void) usb_pipe_get_state(hubd->h_ep1_ph, &prev_pipe_state,
7510                     USB_FLAGS_SLEEP);
7511                 mutex_enter(HUBD_MUTEX(hubd));
7512 
7513                 if (prev_pipe_state == USB_PIPE_STATE_ACTIVE) {
7514                         hubd_stop_polling(hubd);
7515                 }
7516         }
7517 
7518         switch (cmd) {
7519         case DEVCTL_AP_DISCONNECT:
7520                 if (hubd_delete_child(hubd, port,
7521                     NDI_DEVI_REMOVE, B_FALSE) != USB_SUCCESS) {
7522                         rv = EIO;
7523                 }
7524 
7525                 break;
7526         case DEVCTL_AP_UNCONFIGURE:
7527                 if (hubd_delete_child(hubd, port,
7528                     NDI_UNCONFIG, B_FALSE) != USB_SUCCESS) {
7529                         rv = EIO;
7530                 }
7531 
7532                 break;
7533         case DEVCTL_AP_CONFIGURE:
7534                 /* toggle port */
7535                 if (hubd_toggle_port(hubd, port) != USB_SUCCESS) {
7536                         rv = EIO;
7537 
7538                         break;
7539                 }
7540 
7541                 (void) hubd_handle_port_connect(hubd, port);
7542                 child_dip = hubd_get_child_dip(hubd, port);
7543                 mutex_exit(HUBD_MUTEX(hubd));
7544 
7545                 ndi_devi_exit(hubd->h_dip, circ);
7546                 ndi_devi_exit(rh_dip, rh_circ);
7547                 ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ);
7548                 if (child_dip == NULL) {
7549                         rv = EIO;
7550                 } else {
7551                         ndi_hold_devi(child_dip);
7552                         if (ndi_devi_online(child_dip, 0) != NDI_SUCCESS)
7553                                 rv = EIO;
7554                         ndi_rele_devi(child_dip);
7555                 }
7556                 ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
7557                 ndi_devi_enter(rh_dip, &rh_circ);
7558                 ndi_devi_enter(hubd->h_dip, &circ);
7559 
7560                 mutex_enter(HUBD_MUTEX(hubd));
7561 
7562                 break;
7563         case DEVCTL_AP_GETSTATE:
7564                 switch (hubd_cfgadm_state(hubd, port)) {
7565                 case HUBD_CFGADM_DISCONNECTED:
7566                         /* port previously 'disconnected' by cfgadm */
7567                         ap_state.ap_rstate = AP_RSTATE_DISCONNECTED;
7568                         ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED;
7569                         ap_state.ap_condition = AP_COND_OK;
7570 
7571                         break;
7572                 case HUBD_CFGADM_UNCONFIGURED:
7573                         ap_state.ap_rstate = AP_RSTATE_CONNECTED;
7574                         ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED;
7575                         ap_state.ap_condition = AP_COND_OK;
7576 
7577                         break;
7578                 case HUBD_CFGADM_CONFIGURED:
7579                         ap_state.ap_rstate = AP_RSTATE_CONNECTED;
7580                         ap_state.ap_ostate = AP_OSTATE_CONFIGURED;
7581                         ap_state.ap_condition = AP_COND_OK;
7582 
7583                         break;
7584                 case HUBD_CFGADM_STILL_REFERENCED:
7585                         ap_state.ap_rstate = AP_RSTATE_EMPTY;
7586                         ap_state.ap_ostate = AP_OSTATE_CONFIGURED;
7587                         ap_state.ap_condition = AP_COND_UNUSABLE;
7588 
7589                         break;
7590                 case HUBD_CFGADM_EMPTY:
7591                 default:
7592                         ap_state.ap_rstate = AP_RSTATE_EMPTY;
7593                         ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED;
7594                         ap_state.ap_condition = AP_COND_OK;
7595 
7596                         break;
7597                 }
7598 
7599                 ap_state.ap_last_change = (time_t)-1;
7600                 ap_state.ap_error_code = 0;
7601                 ap_state.ap_in_transition = 0;
7602 
7603                 USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
7604                     "DEVCTL_AP_GETSTATE: "
7605                     "ostate=0x%x, rstate=0x%x, condition=0x%x",
7606                     ap_state.ap_ostate,
7607                     ap_state.ap_rstate, ap_state.ap_condition);
7608 
7609                 /* copy the return-AP-state information to the user space */
7610                 if (ndi_dc_return_ap_state(&ap_state, dcp) != NDI_SUCCESS) {
7611                         rv = EFAULT;
7612                 }
7613 
7614                 break;
7615         case DEVCTL_AP_CONTROL:
7616         {
7617                 /*
7618                  * Generic devctl for hardware-specific functionality.
7619                  * For list of sub-commands see hubd_impl.h
7620                  */
7621                 hubd_ioctl_data_t       ioc;    /* for 64 byte copies */
7622 
7623                 /* copy user ioctl data in first */
7624 #ifdef _MULTI_DATAMODEL
7625                 if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
7626                         hubd_ioctl_data_32_t ioc32;
7627 
7628                         if (ddi_copyin((void *)arg, (void *)&ioc32,
7629                             sizeof (ioc32), mode) != 0) {
7630                                 rv = EFAULT;
7631 
7632                                 break;
7633                         }
7634                         ioc.cmd         = (uint_t)ioc32.cmd;
7635                         ioc.port        = (uint_t)ioc32.port;
7636                         ioc.get_size    = (uint_t)ioc32.get_size;
7637                         ioc.buf         = (caddr_t)(uintptr_t)ioc32.buf;
7638                         ioc.bufsiz      = (uint_t)ioc32.bufsiz;
7639                         ioc.misc_arg    = (uint_t)ioc32.misc_arg;
7640                 } else
7641 #endif /* _MULTI_DATAMODEL */
7642                 if (ddi_copyin((void *)arg, (void *)&ioc, sizeof (ioc),
7643                     mode) != 0) {
7644                         rv = EFAULT;
7645 
7646                         break;
7647                 }
7648 
7649                 USB_DPRINTF_L3(DPRINT_MASK_CBOPS, hubd->h_log_handle,
7650                     "DEVCTL_AP_CONTROL: ioc: cmd=0x%x port=%d get_size=%d"
7651                     "\n\tbuf=0x%p, bufsiz=%d,  misc_arg=%d", ioc.cmd,
7652                     ioc.port, ioc.get_size, (void *)ioc.buf, ioc.bufsiz,
7653                     ioc.misc_arg);
7654 
7655                 /*
7656                  * To avoid BE/LE and 32/64 issues, a get_size always
7657                  * returns a 32-bit number.
7658                  */
7659                 if (ioc.get_size != 0 && ioc.bufsiz != (sizeof (uint32_t))) {
7660                         rv = EINVAL;
7661 
7662                         break;
7663                 }
7664 
7665                 switch (ioc.cmd) {
7666                 case USB_DESCR_TYPE_DEV:
7667                         msg = "DEVCTL_AP_CONTROL: GET_DEVICE_DESC";
7668                         if (ioc.get_size) {
7669                                 /* uint32 so this works 32/64 */
7670                                 uint32_t size = sizeof (usb_dev_descr_t);
7671 
7672                                 if (ddi_copyout((void *)&size, ioc.buf,
7673                                     ioc.bufsiz, mode) != 0) {
7674                                         USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
7675                                             hubd->h_log_handle,
7676                                             "%s: get_size copyout failed", msg);
7677                                         rv = EIO;
7678 
7679                                         break;
7680                                 }
7681                         } else {        /* send out the actual descr */
7682                                 usb_dev_descr_t *dev_descrp;
7683 
7684                                 /* check child_dip */
7685                                 if ((child_dip = hubd_get_child_dip(hubd,
7686                                     ioc.port)) == NULL) {
7687                                         rv = EINVAL;
7688 
7689                                         break;
7690                                 }
7691 
7692                                 dev_descrp = usb_get_dev_descr(child_dip);
7693                                 if (ioc.bufsiz != sizeof (*dev_descrp)) {
7694                                         USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
7695                                             hubd->h_log_handle,
7696                                             "%s: bufsize passed (%d) != sizeof "
7697                                             "usba_device_descr_t (%d)", msg,
7698                                             ioc.bufsiz, dev_descrp->bLength);
7699                                         rv = EINVAL;
7700 
7701                                         break;
7702                                 }
7703 
7704                                 if (ddi_copyout((void *)dev_descrp,
7705                                     ioc.buf, ioc.bufsiz, mode) != 0) {
7706                                         USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
7707                                             hubd->h_log_handle,
7708                                             "%s: copyout failed.", msg);
7709                                         rv = EIO;
7710 
7711                                         break;
7712                                 }
7713                         }
7714                         break;
7715                 case USB_DESCR_TYPE_STRING:
7716                 {
7717                         char            *str;
7718                         uint32_t        size;
7719                         usba_device_t   *usba_device;
7720 
7721                         msg = "DEVCTL_AP_CONTROL: GET_STRING_DESCR";
7722                         USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
7723                             "%s: string request: %d", msg, ioc.misc_arg);
7724 
7725                         /* recheck */
7726                         if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) ==
7727                             NULL) {
7728                                 rv = EINVAL;
7729 
7730                                 break;
7731                         }
7732                         usba_device = usba_get_usba_device(child_dip);
7733 
7734                         switch (ioc.misc_arg) {
7735                         case HUBD_MFG_STR:
7736                                 str = usba_device->usb_mfg_str;
7737 
7738                                 break;
7739                         case HUBD_PRODUCT_STR:
7740                                 str = usba_device->usb_product_str;
7741 
7742                                 break;
7743                         case HUBD_SERIALNO_STR:
7744                                 str = usba_device->usb_serialno_str;
7745 
7746                                 break;
7747                         case HUBD_CFG_DESCR_STR:
7748                                 mutex_enter(&usba_device->usb_mutex);
7749                                 str = usba_device->usb_cfg_str_descr[
7750                                     usba_device->usb_active_cfg_ndx];
7751                                 mutex_exit(&usba_device->usb_mutex);
7752 
7753                                 break;
7754                         default:
7755                                 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
7756                                     hubd->h_log_handle,
7757                                     "%s: Invalid string request", msg);
7758                                 rv = EINVAL;
7759 
7760                                 break;
7761                         } /* end of switch */
7762 
7763                         if (rv != 0) {
7764 
7765                                 break;
7766                         }
7767 
7768                         size = (str != NULL) ? strlen(str) + 1 : 0;
7769                         if (ioc.get_size) {
7770                                 if (ddi_copyout((void *)&size, ioc.buf,
7771                                     ioc.bufsiz, mode) != 0) {
7772                                         USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
7773                                             hubd->h_log_handle,
7774                                             "%s: copyout of size failed.", msg);
7775                                         rv = EIO;
7776 
7777                                         break;
7778                                 }
7779                         } else {
7780                                 if (size == 0) {
7781                                         USB_DPRINTF_L3(DPRINT_MASK_CBOPS,
7782                                             hubd->h_log_handle,
7783                                             "%s: String is NULL", msg);
7784                                         rv = EINVAL;
7785 
7786                                         break;
7787                                 }
7788 
7789                                 if (ioc.bufsiz != size) {
7790                                         USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
7791                                             hubd->h_log_handle,
7792                                             "%s: string buf size wrong", msg);
7793                                         rv = EINVAL;
7794 
7795                                         break;
7796                                 }
7797 
7798                                 if (ddi_copyout((void *)str, ioc.buf,
7799                                     ioc.bufsiz, mode) != 0) {
7800                                         USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
7801                                             hubd->h_log_handle,
7802                                             "%s: copyout failed.", msg);
7803                                         rv = EIO;
7804 
7805                                         break;
7806                                 }
7807                         }
7808                         break;
7809                 }
7810                 case HUBD_GET_CFGADM_NAME:
7811                 {
7812                         uint32_t   name_len;
7813                         const char *name;
7814 
7815                         /* recheck */
7816                         if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) ==
7817                             NULL) {
7818                                 rv = EINVAL;
7819 
7820                                 break;
7821                         }
7822                         name = ddi_node_name(child_dip);
7823                         if (name == NULL) {
7824                                 name = "unsupported";
7825                         }
7826                         name_len = strlen(name) + 1;
7827 
7828                         msg = "DEVCTL_AP_CONTROL: HUBD_GET_CFGADM_NAME";
7829                         USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
7830                             "%s: name=%s name_len=%d", msg, name, name_len);
7831 
7832                         if (ioc.get_size) {
7833                                 if (ddi_copyout((void *)&name_len,
7834                                     ioc.buf, ioc.bufsiz, mode) != 0) {
7835                                         USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
7836                                             hubd->h_log_handle,
7837                                             "%s: copyout of size failed", msg);
7838                                         rv = EIO;
7839 
7840                                         break;
7841                                 }
7842                         } else {
7843                                 if (ioc.bufsiz != name_len) {
7844                                         USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
7845                                             hubd->h_log_handle,
7846                                             "%s: string buf length wrong", msg);
7847                                         rv = EINVAL;
7848 
7849                                         break;
7850                                 }
7851 
7852                                 if (ddi_copyout((void *)name, ioc.buf,
7853                                     ioc.bufsiz, mode) != 0) {
7854                                         USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
7855                                             hubd->h_log_handle,
7856                                             "%s: copyout failed.", msg);
7857                                         rv = EIO;
7858 
7859                                         break;
7860                                 }
7861                         }
7862 
7863                         break;
7864                 }
7865 
7866                 /*
7867                  * Return the config index for the currently-configured
7868                  * configuration.
7869                  */
7870                 case HUBD_GET_CURRENT_CONFIG:
7871                 {
7872                         uint_t          config_index;
7873                         uint32_t        size = sizeof (config_index);
7874                         usba_device_t   *usba_device;
7875 
7876                         msg = "DEVCTL_AP_CONTROL: GET_CURRENT_CONFIG";
7877                         USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
7878                             "%s", msg);
7879 
7880                         /*
7881                          * Return the config index for the configuration
7882                          * currently in use.
7883                          * Recheck if child_dip exists
7884                          */
7885                         if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) ==
7886                             NULL) {
7887                                 rv = EINVAL;
7888 
7889                                 break;
7890                         }
7891 
7892                         usba_device = usba_get_usba_device(child_dip);
7893                         mutex_enter(&usba_device->usb_mutex);
7894                         config_index = usba_device->usb_active_cfg_ndx;
7895                         mutex_exit(&usba_device->usb_mutex);
7896 
7897                         if (ioc.get_size) {
7898                                 if (ddi_copyout((void *)&size,
7899                                     ioc.buf, ioc.bufsiz, mode) != 0) {
7900                                         USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
7901                                             hubd->h_log_handle,
7902                                             "%s: copyout of size failed.", msg);
7903                                         rv = EIO;
7904 
7905                                         break;
7906                                 }
7907                         } else {
7908                                 if (ioc.bufsiz != size) {
7909                                         USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
7910                                             hubd->h_log_handle,
7911                                             "%s: buffer size wrong", msg);
7912                                         rv = EINVAL;
7913 
7914                                         break;
7915                                 }
7916                                 if (ddi_copyout((void *)&config_index,
7917                                     ioc.buf, ioc.bufsiz, mode) != 0) {
7918                                         USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
7919                                             hubd->h_log_handle,
7920                                             "%s: copyout failed", msg);
7921                                         rv = EIO;
7922                                 }
7923                         }
7924 
7925                         break;
7926                 }
7927                 case HUBD_GET_DEVICE_PATH:
7928                 {
7929                         char            *path;
7930                         uint32_t        size;
7931 
7932                         msg = "DEVCTL_AP_CONTROL: GET_DEVICE_PATH";
7933                         USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
7934                             "%s", msg);
7935 
7936                         /* Recheck if child_dip exists */
7937                         if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) ==
7938                             NULL) {
7939                                 rv = EINVAL;
7940 
7941                                 break;
7942                         }
7943 
7944                         /* ddi_pathname doesn't supply /devices, so we do. */
7945                         path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
7946                         (void) strcpy(path, "/devices");
7947                         (void) ddi_pathname(child_dip, path + strlen(path));
7948                         size = strlen(path) + 1;
7949 
7950                         USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
7951                             "%s: device path=%s  size=%d", msg, path, size);
7952 
7953                         if (ioc.get_size) {
7954                                 if (ddi_copyout((void *)&size,
7955                                     ioc.buf, ioc.bufsiz, mode) != 0) {
7956 
7957                                         USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
7958                                             hubd->h_log_handle,
7959                                             "%s: copyout of size failed.", msg);
7960                                         rv = EIO;
7961                                 }
7962                         } else {
7963                                 if (ioc.bufsiz != size) {
7964                                         USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
7965                                             hubd->h_log_handle,
7966                                             "%s: buffer wrong size.", msg);
7967                                         rv = EINVAL;
7968                                 } else if (ddi_copyout((void *)path,
7969                                     ioc.buf, ioc.bufsiz, mode) != 0) {
7970                                         USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
7971                                             hubd->h_log_handle,
7972                                             "%s: copyout failed.", msg);
7973                                         rv = EIO;
7974                                 }
7975                         }
7976                         kmem_free(path, MAXPATHLEN);
7977 
7978                         break;
7979                 }
7980                 case HUBD_REFRESH_DEVDB:
7981                         msg = "DEVCTL_AP_CONTROL: HUBD_REFRESH_DEVDB";
7982                         USB_DPRINTF_L3(DPRINT_MASK_CBOPS, hubd->h_log_handle,
7983                             "%s", msg);
7984 
7985                         if ((rv = usba_devdb_refresh()) != USB_SUCCESS) {
7986                                 USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
7987                                     hubd->h_log_handle,
7988                                     "%s: Failed: %d", msg, rv);
7989                                 rv = EIO;
7990                         }
7991 
7992                         break;
7993                 default:
7994                         rv = ENOTSUP;
7995                 }       /* end switch */
7996 
7997                 break;
7998         }
7999 
8000         default:
8001                 rv = ENOTTY;
8002         }
8003 
8004         if (dcp) {
8005                 ndi_dc_freehdl(dcp);
8006         }
8007 
8008         /* allow hotplug thread now */
8009         hubd->h_hotplug_thread--;
8010 
8011         if ((hubd->h_dev_state == USB_DEV_ONLINE) &&
8012             hubd->h_ep1_ph && (prev_pipe_state == USB_PIPE_STATE_ACTIVE)) {
8013                 hubd_start_polling(hubd, 0);
8014         }
8015         mutex_exit(HUBD_MUTEX(hubd));
8016 
8017         ndi_devi_exit(hubd->h_dip, circ);
8018         ndi_devi_exit(rh_dip, rh_circ);
8019         ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ);
8020 
8021         mutex_enter(HUBD_MUTEX(hubd));
8022         hubd_pm_idle_component(hubd, hubd->h_dip, 0);
8023         mutex_exit(HUBD_MUTEX(hubd));
8024 
8025         return (rv);
8026 }
8027 
8028 
8029 /*
8030  * Helper func used only to help construct the names for the attachment point
8031  * minor nodes.  Used only in usba_hubdi_attach.
8032  * Returns whether it found ancestry or not (USB_SUCCESS if yes).
8033  * ports between the root hub and the device represented by dip.
8034  * E.g.,  "2.4.3.1" means this device is
8035  *      plugged into port 1 of a hub that is
8036  *      plugged into port 3 of a hub that is
8037  *      plugged into port 4 of a hub that is
8038  *      plugged into port 2 of the root hub.
8039  * NOTE: Max ap_id path len is HUBD_APID_NAMELEN (32 chars), which is
8040  * more than sufficient (as hubs are a max 6 levels deep, port needs 3
8041  * chars plus NULL each)
8042  */
8043 void
8044 hubd_get_ancestry_str(hubd_t *hubd)
8045 {
8046         char            ap_name[HUBD_APID_NAMELEN];
8047         dev_info_t      *pdip;
8048         hubd_t          *phubd;
8049         usb_port_t      port;
8050 
8051         USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
8052             "hubd_get_ancestry_str: hubd=0x%p", (void *)hubd);
8053 
8054         ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
8055 
8056         /*
8057          * The function is extended to support wire adapter class
8058          * devices introduced by WUSB spec. The node name is no
8059          * longer "hub" only.
8060          * Generate the ap_id str based on the parent and child
8061          * relationship instead of retrieving it from the hub
8062          * device path, which simplifies the algorithm.
8063          */
8064         if (usba_is_root_hub(hubd->h_dip)) {
8065                 hubd->h_ancestry_str[0] = '\0';
8066         } else {
8067                 port = hubd->h_usba_device->usb_port;
8068                 mutex_exit(HUBD_MUTEX(hubd));
8069 
8070                 pdip = ddi_get_parent(hubd->h_dip);
8071                 /*
8072                  * The parent of wire adapter device might be usb_mid.
8073                  * Need to look further up for hub device
8074                  */
8075                 if (strcmp(ddi_driver_name(pdip), "usb_mid") == 0) {
8076                         pdip = ddi_get_parent(pdip);
8077                         ASSERT(pdip != NULL);
8078                 }
8079 
8080                 phubd = hubd_get_soft_state(pdip);
8081 
8082                 mutex_enter(HUBD_MUTEX(phubd));
8083                 (void) snprintf(ap_name, HUBD_APID_NAMELEN, "%s%d",
8084                     phubd->h_ancestry_str, port);
8085                 mutex_exit(HUBD_MUTEX(phubd));
8086 
8087                 mutex_enter(HUBD_MUTEX(hubd));
8088                 (void) strcpy(hubd->h_ancestry_str, ap_name);
8089                 (void) strcat(hubd->h_ancestry_str, ".");
8090         }
8091 }
8092 
8093 
8094 /* Get which port to operate on.  */
8095 static usb_port_t
8096 hubd_get_port_num(hubd_t *hubd, struct devctl_iocdata *dcp)
8097 {
8098         int32_t port;
8099 
8100         ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
8101 
8102         /* Get which port to operate on.  */
8103         if (nvlist_lookup_int32(ndi_dc_get_ap_data(dcp), "port", &port) != 0) {
8104                 USB_DPRINTF_L2(DPRINT_MASK_CBOPS, hubd->h_log_handle,
8105                     "hubd_get_port_num: port lookup failed");
8106                 port = 0;
8107         }
8108 
8109         USB_DPRINTF_L4(DPRINT_MASK_CBOPS,  hubd->h_log_handle,
8110             "hubd_get_port_num: hubd=0x%p, port=%d", (void *)hubd, port);
8111 
8112         return ((usb_port_t)port);
8113 }
8114 
8115 
8116 /* check if child still exists */
8117 static dev_info_t *
8118 hubd_get_child_dip(hubd_t *hubd, usb_port_t port)
8119 {
8120         dev_info_t *child_dip = hubd->h_children_dips[port];
8121 
8122         USB_DPRINTF_L4(DPRINT_MASK_CBOPS,  hubd->h_log_handle,
8123             "hubd_get_child_dip: hubd=0x%p, port=%d", (void *)hubd, port);
8124 
8125         ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
8126 
8127         return (child_dip);
8128 }
8129 
8130 
8131 /*
8132  * hubd_cfgadm_state:
8133  *
8134  *      child_dip list          port_state              cfgadm_state
8135  *      --------------          ----------              ------------
8136  *      != NULL                 connected               configured or
8137  *                                                      unconfigured
8138  *      != NULL                 not connected           disconnect but
8139  *                                                      busy/still referenced
8140  *      NULL                    connected               logically disconnected
8141  *      NULL                    not connected           empty
8142  */
8143 static uint_t
8144 hubd_cfgadm_state(hubd_t *hubd, usb_port_t port)
8145 {
8146         uint_t          state;
8147         dev_info_t      *child_dip = hubd_get_child_dip(hubd, port);
8148 
8149         if (child_dip) {
8150                 if (hubd->h_port_state[port] & PORT_STATUS_CCS) {
8151                         /*
8152                          * connected,  now check if driver exists
8153                          */
8154                         if (DEVI_IS_DEVICE_OFFLINE(child_dip) ||
8155                             !i_ddi_devi_attached(child_dip)) {
8156                                 state = HUBD_CFGADM_UNCONFIGURED;
8157                         } else {
8158                                 state = HUBD_CFGADM_CONFIGURED;
8159                         }
8160                 } else {
8161                         /*
8162                          * this means that the dip is around for
8163                          * a device that is still referenced but
8164                          * has been yanked out. So the cfgadm info
8165                          * for this state should be EMPTY (port empty)
8166                          * and CONFIGURED (dip still valid).
8167                          */
8168                         state = HUBD_CFGADM_STILL_REFERENCED;
8169                 }
8170         } else {
8171                 /* connected but no child dip */
8172                 if (hubd->h_port_state[port] & PORT_STATUS_CCS) {
8173                         /* logically disconnected */
8174                         state = HUBD_CFGADM_DISCONNECTED;
8175                 } else {
8176                         /* physically disconnected */
8177                         state = HUBD_CFGADM_EMPTY;
8178                 }
8179         }
8180 
8181         USB_DPRINTF_L4(DPRINT_MASK_CBOPS,  hubd->h_log_handle,
8182             "hubd_cfgadm_state: hubd=0x%p, port=%d state=0x%x",
8183             (void *)hubd, port, state);
8184 
8185         return (state);
8186 }
8187 
8188 
8189 /*
8190  * hubd_toggle_port:
8191  */
8192 static int
8193 hubd_toggle_port(hubd_t *hubd, usb_port_t port)
8194 {
8195         usb_hub_descr_t *hub_descr;
8196         int             wait;
8197         uint_t          retry;
8198         uint16_t        status;
8199         uint16_t        change;
8200 
8201         USB_DPRINTF_L4(DPRINT_MASK_CBOPS,  hubd->h_log_handle,
8202             "hubd_toggle_port: hubd=0x%p, port=%d", (void *)hubd, port);
8203 
8204         if ((hubd_disable_port_power(hubd, port)) != USB_SUCCESS) {
8205 
8206                 return (USB_FAILURE);
8207         }
8208 
8209         /*
8210          * see hubd_enable_all_port_power() which
8211          * requires longer delay for hubs.
8212          */
8213         mutex_exit(HUBD_MUTEX(hubd));
8214         delay(drv_usectohz(hubd_device_delay / 10));
8215         mutex_enter(HUBD_MUTEX(hubd));
8216 
8217         hub_descr = &hubd->h_hub_descr;
8218 
8219         /*
8220          * According to section 11.11 of USB, for hubs with no power
8221          * switches, bPwrOn2PwrGood is zero. But we wait for some
8222          * arbitrary time to enable power to become stable.
8223          *
8224          * If an hub supports port power swicthing, we need to wait
8225          * at least 20ms before accesing corresonding usb port.
8226          */
8227         if ((hub_descr->wHubCharacteristics &
8228             HUB_CHARS_NO_POWER_SWITCHING) || (!hub_descr->bPwrOn2PwrGood)) {
8229                 wait = hubd_device_delay / 10;
8230         } else {
8231                 wait = max(HUB_DEFAULT_POPG,
8232                     hub_descr->bPwrOn2PwrGood) * 2 * 1000;
8233         }
8234 
8235         USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
8236             "hubd_toggle_port: popg=%d wait=%d",
8237             hub_descr->bPwrOn2PwrGood, wait);
8238 
8239         retry = 0;
8240 
8241         do {
8242                 (void) hubd_enable_port_power(hubd, port);
8243 
8244                 mutex_exit(HUBD_MUTEX(hubd));
8245                 delay(drv_usectohz(wait));
8246                 mutex_enter(HUBD_MUTEX(hubd));
8247 
8248                 /* Get port status */
8249                 (void) hubd_determine_port_status(hubd, port,
8250                     &status, &change, 0);
8251 
8252                 /* For retry if any, use some extra delay */
8253                 wait = max(wait, hubd_device_delay / 10);
8254 
8255                 retry++;
8256 
8257         } while ((!(status & PORT_STATUS_PPS)) && (retry < HUBD_PORT_RETRY));
8258 
8259         /* Print warning message if port has no power */
8260         if (!(status & PORT_STATUS_PPS)) {
8261 
8262                 USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
8263                     "hubd_toggle_port: port %d power-on failed, "
8264                     "port status 0x%x", port, status);
8265 
8266                 return (USB_FAILURE);
8267         }
8268 
8269         return (USB_SUCCESS);
8270 }
8271 
8272 
8273 /*
8274  * hubd_init_power_budget:
8275  *      Init power budget variables in hubd structure. According
8276  *      to USB spec, the power budget rules are:
8277  *      1. local-powered hubs including root-hubs can supply
8278  *         500mA to each port at maximum
8279  *      2. two bus-powered hubs are not allowed to concatenate
8280  *      3. bus-powered hubs can supply 100mA to each port at
8281  *         maximum, and the power consumed by all downstream
8282  *         ports and the hub itself cannot exceed the max power
8283  *         supplied by the upstream port, i.e., 500mA
8284  *      The routine is only called during hub attach time
8285  */
8286 static int
8287 hubd_init_power_budget(hubd_t *hubd)
8288 {
8289         uint16_t        status = 0;
8290         usba_device_t   *hubd_ud = NULL;
8291         size_t          size;
8292         usb_cfg_descr_t cfg_descr;
8293         dev_info_t      *pdip = NULL;
8294         hubd_t          *phubd = NULL;
8295 
8296         if (hubd->h_ignore_pwr_budget) {
8297 
8298                 return (USB_SUCCESS);
8299         }
8300 
8301         USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
8302             "hubd_init_power_budget:");
8303 
8304         ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
8305         ASSERT(hubd->h_default_pipe != 0);
8306         mutex_exit(HUBD_MUTEX(hubd));
8307 
8308         /* get device status */
8309         if ((usb_get_status(hubd->h_dip, hubd->h_default_pipe,
8310             HUB_GET_DEVICE_STATUS_TYPE,
8311             0, &status, 0)) != USB_SUCCESS) {
8312                 mutex_enter(HUBD_MUTEX(hubd));
8313 
8314                 return (USB_FAILURE);
8315         }
8316 
8317         hubd_ud = usba_get_usba_device(hubd->h_dip);
8318 
8319         size = usb_parse_cfg_descr(hubd_ud->usb_cfg, hubd_ud->usb_cfg_length,
8320             &cfg_descr, USB_CFG_DESCR_SIZE);
8321 
8322         if (size != USB_CFG_DESCR_SIZE) {
8323                 USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
8324                     "get hub configuration descriptor failed");
8325                 mutex_enter(HUBD_MUTEX(hubd));
8326 
8327                 return (USB_FAILURE);
8328         }
8329 
8330         mutex_enter(HUBD_MUTEX(hubd));
8331 
8332         hubd->h_local_pwr_capable = (cfg_descr.bmAttributes &
8333             USB_CFG_ATTR_SELFPWR);
8334 
8335         if (hubd->h_local_pwr_capable) {
8336                 USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle,
8337                     "hub is capable of local power");
8338         }
8339 
8340         hubd->h_local_pwr_on = (status &
8341             USB_DEV_SLF_PWRD_STATUS) && hubd->h_local_pwr_capable;
8342 
8343         if (hubd->h_local_pwr_on) {
8344                 USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle,
8345                     "hub is local-powered");
8346 
8347                 hubd->h_pwr_limit = (USB_PWR_UNIT_LOAD *
8348                     USB_HIGH_PWR_VALUE) / USB_CFG_DESCR_PWR_UNIT;
8349         } else {
8350                 hubd->h_pwr_limit = (USB_PWR_UNIT_LOAD *
8351                     USB_LOW_PWR_VALUE) / USB_CFG_DESCR_PWR_UNIT;
8352 
8353                 hubd->h_pwr_left = (USB_PWR_UNIT_LOAD *
8354                     USB_HIGH_PWR_VALUE) / USB_CFG_DESCR_PWR_UNIT;
8355 
8356                 ASSERT(!usba_is_root_hub(hubd->h_dip));
8357 
8358                 if (!usba_is_root_hub(hubd->h_dip)) {
8359                         /*
8360                          * two bus-powered hubs are not
8361                          * allowed to be concatenated
8362                          */
8363                         mutex_exit(HUBD_MUTEX(hubd));
8364 
8365                         pdip = ddi_get_parent(hubd->h_dip);
8366                         phubd = hubd_get_soft_state(pdip);
8367                         ASSERT(phubd != NULL);
8368 
8369                         if (!phubd->h_ignore_pwr_budget) {
8370                                 mutex_enter(HUBD_MUTEX(phubd));
8371                                 if (phubd->h_local_pwr_on == B_FALSE) {
8372                                         USB_DPRINTF_L1(DPRINT_MASK_HUB,
8373                                             hubd->h_log_handle,
8374                                             "two bus-powered hubs cannot "
8375                                             "be concatenated");
8376 
8377                                         mutex_exit(HUBD_MUTEX(phubd));
8378                                         mutex_enter(HUBD_MUTEX(hubd));
8379 
8380                                         return (USB_FAILURE);
8381                                 }
8382                                 mutex_exit(HUBD_MUTEX(phubd));
8383                         }
8384 
8385                         mutex_enter(HUBD_MUTEX(hubd));
8386 
8387                         USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle,
8388                             "hub is bus-powered");
8389                 } else {
8390                         USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle,
8391                             "root-hub must be local-powered");
8392                 }
8393 
8394                 /*
8395                  * Subtract the power consumed by the hub itself
8396                  * and get the power that can be supplied to
8397                  * downstream ports
8398                  */
8399                 hubd->h_pwr_left -=
8400                     hubd->h_hub_descr.bHubContrCurrent /
8401                     USB_CFG_DESCR_PWR_UNIT;
8402                 if (hubd->h_pwr_left < 0) {
8403                         USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
8404                             "hubd->h_pwr_left is less than bHubContrCurrent, "
8405                             "should fail");
8406 
8407                         return (USB_FAILURE);
8408                 }
8409         }
8410 
8411         return (USB_SUCCESS);
8412 }
8413 
8414 
8415 /*
8416  * usba_hubdi_check_power_budget:
8417  *      Check if the hub has enough power budget to allow a
8418  *      child device to select a configuration of config_index.
8419  */
8420 int
8421 usba_hubdi_check_power_budget(dev_info_t *dip, usba_device_t *child_ud,
8422         uint_t config_index)
8423 {
8424         int16_t         pwr_left, pwr_limit, pwr_required;
8425         size_t          size;
8426         usb_cfg_descr_t cfg_descr;
8427         hubd_t          *hubd;
8428 
8429         if ((hubd = hubd_get_soft_state(dip)) == NULL) {
8430 
8431                 return (USB_FAILURE);
8432         }
8433 
8434         if (hubd->h_ignore_pwr_budget) {
8435 
8436                 return (USB_SUCCESS);
8437         }
8438 
8439         USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
8440             "usba_hubdi_check_power_budget: "
8441             "dip=0x%p child_ud=0x%p conf_index=%d", (void *)dip,
8442             (void *)child_ud, config_index);
8443 
8444         mutex_enter(HUBD_MUTEX(hubd));
8445         pwr_limit = hubd->h_pwr_limit;
8446         if (hubd->h_local_pwr_on == B_FALSE) {
8447                 pwr_left = hubd->h_pwr_left;
8448                 pwr_limit = (pwr_limit <= pwr_left) ? pwr_limit : pwr_left;
8449         }
8450         mutex_exit(HUBD_MUTEX(hubd));
8451 
8452         USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
8453             "usba_hubdi_check_power_budget: "
8454             "available power is %dmA", pwr_limit * USB_CFG_DESCR_PWR_UNIT);
8455 
8456         size = usb_parse_cfg_descr(
8457             child_ud->usb_cfg_array[config_index], USB_CFG_DESCR_SIZE,
8458             &cfg_descr, USB_CFG_DESCR_SIZE);
8459 
8460         if (size != USB_CFG_DESCR_SIZE) {
8461                 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
8462                     "get hub configuration descriptor failed");
8463 
8464                 return (USB_FAILURE);
8465         }
8466 
8467         pwr_required = cfg_descr.bMaxPower;
8468 
8469         USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
8470             "usba_hubdi_check_power_budget: "
8471             "child bmAttributes=0x%x bMaxPower=%d "
8472             "with config_index=%d", cfg_descr.bmAttributes,
8473             pwr_required, config_index);
8474 
8475         if (pwr_required > pwr_limit) {
8476                 USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
8477                     "configuration %d for device %s %s at port %d "
8478                     "exceeds power available for this port, please "
8479                     "re-insert your device into another hub port which "
8480                     "has enough power",
8481                     config_index,
8482                     child_ud->usb_mfg_str,
8483                     child_ud->usb_product_str,
8484                     child_ud->usb_port);
8485 
8486                 return (USB_FAILURE);
8487         }
8488 
8489         return (USB_SUCCESS);
8490 }
8491 
8492 
8493 /*
8494  * usba_hubdi_incr_power_budget:
8495  *      Increase the hub power budget value when a child device
8496  *      is removed from a bus-powered hub port.
8497  */
8498 void
8499 usba_hubdi_incr_power_budget(dev_info_t *dip, usba_device_t *child_ud)
8500 {
8501         uint16_t        pwr_value;
8502         hubd_t          *hubd = hubd_get_soft_state(dip);
8503 
8504         ASSERT(hubd != NULL);
8505 
8506         if (hubd->h_ignore_pwr_budget) {
8507 
8508                 return;
8509         }
8510 
8511         USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
8512             "usba_hubdi_incr_power_budget: "
8513             "dip=0x%p child_ud=0x%p", (void *)dip, (void *)child_ud);
8514 
8515         mutex_enter(HUBD_MUTEX(hubd));
8516         if (hubd->h_local_pwr_on == B_TRUE) {
8517                 USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
8518                     "usba_hubdi_incr_power_budget: "
8519                     "hub is local powered");
8520                 mutex_exit(HUBD_MUTEX(hubd));
8521 
8522                 return;
8523         }
8524         mutex_exit(HUBD_MUTEX(hubd));
8525 
8526         mutex_enter(&child_ud->usb_mutex);
8527         if (child_ud->usb_pwr_from_hub == 0) {
8528                 mutex_exit(&child_ud->usb_mutex);
8529 
8530                 return;
8531         }
8532         pwr_value = child_ud->usb_pwr_from_hub;
8533         mutex_exit(&child_ud->usb_mutex);
8534 
8535         mutex_enter(HUBD_MUTEX(hubd));
8536         hubd->h_pwr_left += pwr_value;
8537 
8538         USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
8539             "usba_hubdi_incr_power_budget: "
8540             "available power is %dmA, increased by %dmA",
8541             hubd->h_pwr_left * USB_CFG_DESCR_PWR_UNIT,
8542             pwr_value * USB_CFG_DESCR_PWR_UNIT);
8543 
8544         mutex_exit(HUBD_MUTEX(hubd));
8545 
8546         mutex_enter(&child_ud->usb_mutex);
8547         child_ud->usb_pwr_from_hub = 0;
8548         mutex_exit(&child_ud->usb_mutex);
8549 }
8550 
8551 
8552 /*
8553  * usba_hubdi_decr_power_budget:
8554  *      Decrease the hub power budget value when a child device
8555  *      is inserted to a bus-powered hub port.
8556  */
8557 void
8558 usba_hubdi_decr_power_budget(dev_info_t *dip, usba_device_t *child_ud)
8559 {
8560         uint16_t        pwr_value;
8561         size_t          size;
8562         usb_cfg_descr_t cfg_descr;
8563         hubd_t          *hubd = hubd_get_soft_state(dip);
8564 
8565         ASSERT(hubd != NULL);
8566 
8567         if (hubd->h_ignore_pwr_budget) {
8568 
8569                 return;
8570         }
8571 
8572         USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
8573             "usba_hubdi_decr_power_budget: "
8574             "dip=0x%p child_ud=0x%p", (void *)dip, (void *)child_ud);
8575 
8576         mutex_enter(HUBD_MUTEX(hubd));
8577         if (hubd->h_local_pwr_on == B_TRUE) {
8578                 USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
8579                     "usba_hubdi_decr_power_budget: "
8580                     "hub is local powered");
8581                 mutex_exit(HUBD_MUTEX(hubd));
8582 
8583                 return;
8584         }
8585         mutex_exit(HUBD_MUTEX(hubd));
8586 
8587         mutex_enter(&child_ud->usb_mutex);
8588         if (child_ud->usb_pwr_from_hub > 0) {
8589                 mutex_exit(&child_ud->usb_mutex);
8590 
8591                 return;
8592         }
8593         mutex_exit(&child_ud->usb_mutex);
8594 
8595         size = usb_parse_cfg_descr(
8596             child_ud->usb_cfg, child_ud->usb_cfg_length,
8597             &cfg_descr, USB_CFG_DESCR_SIZE);
8598         ASSERT(size == USB_CFG_DESCR_SIZE);
8599 
8600         mutex_enter(HUBD_MUTEX(hubd));
8601         pwr_value = cfg_descr.bMaxPower;
8602         hubd->h_pwr_left -= pwr_value;
8603         ASSERT(hubd->h_pwr_left >= 0);
8604 
8605         USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
8606             "usba_hubdi_decr_power_budget: "
8607             "available power is %dmA, decreased by %dmA",
8608             hubd->h_pwr_left * USB_CFG_DESCR_PWR_UNIT,
8609             pwr_value * USB_CFG_DESCR_PWR_UNIT);
8610 
8611         mutex_exit(HUBD_MUTEX(hubd));
8612 
8613         mutex_enter(&child_ud->usb_mutex);
8614         child_ud->usb_pwr_from_hub = pwr_value;
8615         mutex_exit(&child_ud->usb_mutex);
8616 }
8617 
8618 /*
8619  * hubd_wait_for_hotplug_exit:
8620  *      Waiting for the exit of the running hotplug thread or ioctl thread.
8621  */
8622 static int
8623 hubd_wait_for_hotplug_exit(hubd_t *hubd)
8624 {
8625         clock_t         until = drv_usectohz(1000000);
8626         int             rval;
8627 
8628         ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
8629 
8630         if (hubd->h_hotplug_thread) {
8631                 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
8632                     "waiting for hubd hotplug thread exit");
8633                 rval = cv_reltimedwait(&hubd->h_cv_hotplug_dev,
8634                     &hubd->h_mutex, until, TR_CLOCK_TICK);
8635 
8636                 if ((rval <= 0) && (hubd->h_hotplug_thread)) {
8637 
8638                         return (USB_FAILURE);
8639                 }
8640         }
8641 
8642         return (USB_SUCCESS);
8643 }
8644 
8645 /*
8646  * hubd_reset_thread:
8647  *      handles the "USB_RESET_LVL_REATTACH" reset of usb device.
8648  *
8649  *      - delete the child (force detaching the device and its children)
8650  *      - reset the corresponding parent hub port
8651  *      - create the child (force re-attaching the device and its children)
8652  */
8653 static void
8654 hubd_reset_thread(void *arg)
8655 {
8656         hubd_reset_arg_t *hd_arg = (hubd_reset_arg_t *)arg;
8657         hubd_t          *hubd = hd_arg->hubd;
8658         uint16_t        reset_port = hd_arg->reset_port;
8659         uint16_t        status, change;
8660         hub_power_t     *hubpm;
8661         dev_info_t      *hdip = hubd->h_dip;
8662         dev_info_t      *rh_dip = hubd->h_usba_device->usb_root_hub_dip;
8663         dev_info_t      *child_dip;
8664         boolean_t       online_child = B_FALSE;
8665         int             prh_circ, rh_circ, circ, devinst;
8666         char            *devname;
8667         int             i = 0;
8668         int             rval = USB_FAILURE;
8669 
8670         USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
8671             "hubd_reset_thread:  started, hubd_reset_port = 0x%x", reset_port);
8672 
8673         kmem_free(arg, sizeof (hubd_reset_arg_t));
8674 
8675         mutex_enter(HUBD_MUTEX(hubd));
8676 
8677         child_dip = hubd->h_children_dips[reset_port];
8678         ASSERT(child_dip != NULL);
8679 
8680         devname = (char *)ddi_driver_name(child_dip);
8681         devinst = ddi_get_instance(child_dip);
8682 
8683         /* if our bus power entry point is active, quit the reset */
8684         if (hubd->h_bus_pwr) {
8685                 USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
8686                     "%s%d is under bus power management, cannot be reset. "
8687                     "Please disconnect and reconnect this device.",
8688                     devname, devinst);
8689 
8690                 goto Fail;
8691         }
8692 
8693         if (hubd_wait_for_hotplug_exit(hubd) == USB_FAILURE) {
8694                 /* we got woken up because of a timeout */
8695                 USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG,
8696                     hubd->h_log_handle, "Time out when resetting the device"
8697                     " %s%d. Please disconnect and reconnect this device.",
8698                     devname, devinst);
8699 
8700                 goto Fail;
8701         }
8702 
8703         hubd->h_hotplug_thread++;
8704 
8705         /* is this the root hub? */
8706         if ((hdip == rh_dip) &&
8707             (hubd->h_dev_state == USB_DEV_PWRED_DOWN)) {
8708                 hubpm = hubd->h_hubpm;
8709 
8710                 /* mark the root hub as full power */
8711                 hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR;
8712                 hubpm->hubp_time_at_full_power = ddi_get_time();
8713                 mutex_exit(HUBD_MUTEX(hubd));
8714 
8715                 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
8716                     "hubd_reset_thread: call pm_power_has_changed");
8717 
8718                 (void) pm_power_has_changed(hdip, 0,
8719                     USB_DEV_OS_FULL_PWR);
8720 
8721                 mutex_enter(HUBD_MUTEX(hubd));
8722                 hubd->h_dev_state = USB_DEV_ONLINE;
8723         }
8724 
8725         mutex_exit(HUBD_MUTEX(hubd));
8726 
8727         /*
8728          * this ensures one reset activity per system at a time.
8729          * we enter the parent PCI node to have this serialization.
8730          * this also excludes ioctls and deathrow thread
8731          */
8732         ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
8733         ndi_devi_enter(rh_dip, &rh_circ);
8734 
8735         /* exclude other threads */
8736         ndi_devi_enter(hdip, &circ);
8737         mutex_enter(HUBD_MUTEX(hubd));
8738 
8739         /*
8740          * We need to make sure that the child is still online for a hotplug
8741          * thread could have inserted which detached the child.
8742          */
8743         if (hubd->h_children_dips[reset_port]) {
8744                 mutex_exit(HUBD_MUTEX(hubd));
8745                 /* First disconnect the device */
8746                 hubd_post_event(hubd, reset_port, USBA_EVENT_TAG_HOT_REMOVAL);
8747 
8748                 /* delete cached dv_node's but drop locks first */
8749                 ndi_devi_exit(hdip, circ);
8750                 ndi_devi_exit(rh_dip, rh_circ);
8751                 ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ);
8752 
8753                 (void) devfs_clean(rh_dip, NULL, DV_CLEAN_FORCE);
8754 
8755                 /*
8756                  * workaround only for storage device. When it's able to force
8757                  * detach a driver, this code can be removed safely.
8758                  *
8759                  * If we're to reset storage device and the device is used, we
8760                  * will wait at most extra 20s for applications to exit and
8761                  * close the device. This is especially useful for HAL-based
8762                  * applications.
8763                  */
8764                 if ((strcmp(devname, "scsa2usb") == 0) &&
8765                     DEVI(child_dip)->devi_ref != 0) {
8766                         while (i++ < hubdi_reset_delay) {
8767                                 mutex_enter(HUBD_MUTEX(hubd));
8768                                 rval = hubd_delete_child(hubd, reset_port,
8769                                     NDI_DEVI_REMOVE, B_FALSE);
8770                                 mutex_exit(HUBD_MUTEX(hubd));
8771                                 if (rval == USB_SUCCESS)
8772                                         break;
8773 
8774                                 delay(drv_usectohz(1000000)); /* 1s */
8775                         }
8776                 }
8777 
8778                 ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
8779                 ndi_devi_enter(rh_dip, &rh_circ);
8780                 ndi_devi_enter(hdip, &circ);
8781 
8782                 mutex_enter(HUBD_MUTEX(hubd));
8783 
8784                 /* Then force detaching the device */
8785                 if ((rval != USB_SUCCESS) && (hubd_delete_child(hubd,
8786                     reset_port, NDI_DEVI_REMOVE, B_FALSE) != USB_SUCCESS)) {
8787                         USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
8788                             "%s%d cannot be reset due to other applications "
8789                             "are using it, please first close these "
8790                             "applications, then disconnect and reconnect"
8791                             "the device.", devname, devinst);
8792 
8793                         mutex_exit(HUBD_MUTEX(hubd));
8794                         /* post a re-connect event */
8795                         hubd_post_event(hubd, reset_port,
8796                             USBA_EVENT_TAG_HOT_INSERTION);
8797                         mutex_enter(HUBD_MUTEX(hubd));
8798                 } else {
8799                         (void) hubd_determine_port_status(hubd, reset_port,
8800                             &status, &change, HUBD_ACK_ALL_CHANGES);
8801 
8802                         /* Reset the parent hubd port and create new child */
8803                         if (status & PORT_STATUS_CCS) {
8804                                 online_child |= (hubd_handle_port_connect(hubd,
8805                                     reset_port) == USB_SUCCESS);
8806                         }
8807                 }
8808         }
8809 
8810         /* release locks so we can do a devfs_clean */
8811         mutex_exit(HUBD_MUTEX(hubd));
8812 
8813         /* delete cached dv_node's but drop locks first */
8814         ndi_devi_exit(hdip, circ);
8815         ndi_devi_exit(rh_dip, rh_circ);
8816         ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ);
8817 
8818         (void) devfs_clean(rh_dip, NULL, 0);
8819 
8820         /* now check if any children need onlining */
8821         if (online_child) {
8822                 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
8823                     "hubd_reset_thread: onlining children");
8824 
8825                 (void) ndi_devi_online(hubd->h_dip, 0);
8826         }
8827 
8828         mutex_enter(HUBD_MUTEX(hubd));
8829 
8830         /* allow hotplug thread now */
8831         hubd->h_hotplug_thread--;
8832 Fail:
8833         hubd_start_polling(hubd, 0);
8834 
8835         /* mark this device as idle */
8836         (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
8837 
8838         USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
8839             "hubd_reset_thread: exit, %d", hubd->h_hotplug_thread);
8840 
8841         hubd->h_reset_port[reset_port] = B_FALSE;
8842 
8843         mutex_exit(HUBD_MUTEX(hubd));
8844 
8845         ndi_rele_devi(hdip);
8846 }
8847 
8848 /*
8849  * hubd_check_same_device:
8850  *      - open the default pipe of the device.
8851  *      - compare the old and new descriptors of the device.
8852  *      - close the default pipe.
8853  */
8854 static int
8855 hubd_check_same_device(hubd_t *hubd, usb_port_t port)
8856 {
8857         dev_info_t              *dip = hubd->h_children_dips[port];
8858         usb_pipe_handle_t       ph;
8859         int                     rval = USB_FAILURE;
8860 
8861         ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
8862 
8863         mutex_exit(HUBD_MUTEX(hubd));
8864         /* Open the default pipe to operate the device */
8865         if (usb_pipe_open(dip, NULL, NULL,
8866             USB_FLAGS_SLEEP| USBA_FLAGS_PRIVILEGED,
8867             &ph) == USB_SUCCESS) {
8868                 /*
8869                  * Check that if the device's descriptors are different
8870                  * from the values saved before the port reset.
8871                  */
8872                 rval = usb_check_same_device(dip,
8873                     hubd->h_log_handle, USB_LOG_L0,
8874                     DPRINT_MASK_ALL, USB_CHK_ALL, NULL);
8875 
8876                 usb_pipe_close(dip, ph, USB_FLAGS_SLEEP |
8877                     USBA_FLAGS_PRIVILEGED, NULL, NULL);
8878         }
8879         mutex_enter(HUBD_MUTEX(hubd));
8880 
8881         return (rval);
8882 }
8883 
8884 /*
8885  * usba_hubdi_reset_device
8886  *      Called by usb_reset_device to handle usb device reset.
8887  */
8888 int
8889 usba_hubdi_reset_device(dev_info_t *dip, usb_dev_reset_lvl_t reset_level)
8890 {
8891         hubd_t                  *hubd;
8892         usb_port_t              port = 0;
8893         dev_info_t              *hdip;
8894         usb_pipe_state_t        prev_pipe_state = 0;
8895         usba_device_t           *usba_device;
8896         hubd_reset_arg_t        *arg;
8897         int                     i, ph_open_cnt;
8898         int                     rval = USB_FAILURE;
8899 
8900         if ((!dip) || usba_is_root_hub(dip)) {
8901                 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
8902                     "usba_hubdi_reset_device: NULL dip or root hub");
8903 
8904                 return (USB_INVALID_ARGS);
8905         }
8906 
8907         if (!usb_owns_device(dip)) {
8908                 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
8909                     "usba_hubdi_reset_device: Not owns the device");
8910 
8911                 return (USB_INVALID_PERM);
8912         }
8913 
8914         if ((reset_level != USB_RESET_LVL_REATTACH) &&
8915             (reset_level != USB_RESET_LVL_DEFAULT)) {
8916                 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
8917                     "usba_hubdi_reset_device: Unknown flags");
8918 
8919                 return (USB_INVALID_ARGS);
8920         }
8921 
8922         if ((hdip = ddi_get_parent(dip)) == NULL) {
8923                 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
8924                     "usba_hubdi_reset_device: fail to get parent hub");
8925 
8926                 return (USB_INVALID_ARGS);
8927         }
8928 
8929         if ((hubd = hubd_get_soft_state(hdip)) == NULL) {
8930                 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
8931                     "usba_hubdi_reset_device: fail to get hub softstate");
8932 
8933                 return (USB_INVALID_ARGS);
8934         }
8935 
8936         mutex_enter(HUBD_MUTEX(hubd));
8937 
8938         /* make sure the hub is connected before trying any kinds of reset. */
8939         if ((hubd->h_dev_state == USB_DEV_DISCONNECTED) ||
8940             (hubd->h_dev_state == USB_DEV_SUSPENDED)) {
8941                 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
8942                     "usb_reset_device: the state %d of the hub/roothub "
8943                     "associated to the device 0x%p is incorrect",
8944                     hubd->h_dev_state, (void *)dip);
8945                 mutex_exit(HUBD_MUTEX(hubd));
8946 
8947                 return (USB_INVALID_ARGS);
8948         }
8949 
8950         mutex_exit(HUBD_MUTEX(hubd));
8951 
8952         port = hubd_child_dip2port(hubd, dip);
8953 
8954         mutex_enter(HUBD_MUTEX(hubd));
8955 
8956         if (hubd->h_reset_port[port]) {
8957                 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
8958                     "usb_reset_device: the corresponding port is resetting");
8959                 mutex_exit(HUBD_MUTEX(hubd));
8960 
8961                 return (USB_SUCCESS);
8962         }
8963 
8964         /*
8965          * For Default reset, client drivers should first close all the pipes
8966          * except default pipe before calling the function, also should not
8967          * call the function during interrupt context.
8968          */
8969         if (reset_level == USB_RESET_LVL_DEFAULT) {
8970                 usba_device = hubd->h_usba_devices[port];
8971                 mutex_exit(HUBD_MUTEX(hubd));
8972 
8973                 if (servicing_interrupt()) {
8974                         USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
8975                             "usb_reset_device: during interrput context, quit");
8976 
8977                         return (USB_INVALID_CONTEXT);
8978                 }
8979                 /* Check if all the pipes have been closed */
8980                 for (ph_open_cnt = 0, i = 1; i < USBA_N_ENDPOINTS; i++) {
8981                         if (usba_device->usb_ph_list[i].usba_ph_data) {
8982                                 ph_open_cnt++;
8983                                 break;
8984                         }
8985                 }
8986                 if (ph_open_cnt) {
8987                         USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
8988                             "usb_reset_device: %d pipes are still open",
8989                             ph_open_cnt);
8990 
8991                         return (USB_BUSY);
8992                 }
8993                 mutex_enter(HUBD_MUTEX(hubd));
8994         }
8995 
8996         /* Don't perform reset while the device is detaching */
8997         if (hubd->h_port_state[port] & HUBD_CHILD_DETACHING) {
8998                 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
8999                     "usb_reset_device: the device is detaching, "
9000                     "cannot be reset");
9001                 mutex_exit(HUBD_MUTEX(hubd));
9002 
9003                 return (USB_FAILURE);
9004         }
9005 
9006         hubd->h_reset_port[port] = B_TRUE;
9007         hdip = hubd->h_dip;
9008         mutex_exit(HUBD_MUTEX(hubd));
9009 
9010         /* Don't allow hub detached during the reset */
9011         ndi_hold_devi(hdip);
9012 
9013         mutex_enter(HUBD_MUTEX(hubd));
9014         hubd_pm_busy_component(hubd, hdip, 0);
9015         mutex_exit(HUBD_MUTEX(hubd));
9016         /* go full power */
9017         (void) pm_raise_power(hdip, 0, USB_DEV_OS_FULL_PWR);
9018         mutex_enter(HUBD_MUTEX(hubd));
9019 
9020         hubd->h_hotplug_thread++;
9021 
9022         /* stop polling if it was active */
9023         if (hubd->h_ep1_ph) {
9024                 mutex_exit(HUBD_MUTEX(hubd));
9025                 (void) usb_pipe_get_state(hubd->h_ep1_ph, &prev_pipe_state,
9026                     USB_FLAGS_SLEEP);
9027                 mutex_enter(HUBD_MUTEX(hubd));
9028 
9029                 if (prev_pipe_state == USB_PIPE_STATE_ACTIVE) {
9030                         hubd_stop_polling(hubd);
9031                 }
9032         }
9033 
9034         switch (reset_level) {
9035         case USB_RESET_LVL_REATTACH:
9036                 mutex_exit(HUBD_MUTEX(hubd));
9037                 arg = (hubd_reset_arg_t *)kmem_zalloc(
9038                     sizeof (hubd_reset_arg_t), KM_SLEEP);
9039                 arg->hubd = hubd;
9040                 arg->reset_port = port;
9041                 mutex_enter(HUBD_MUTEX(hubd));
9042 
9043                 if ((rval = usb_async_req(hdip, hubd_reset_thread,
9044                     (void *)arg, 0)) == USB_SUCCESS) {
9045                         hubd->h_hotplug_thread--;
9046                         mutex_exit(HUBD_MUTEX(hubd));
9047 
9048                         return (USB_SUCCESS);
9049                 } else {
9050                         USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
9051                             "Cannot create reset thread, the device %s%d failed"
9052                             " to reset", ddi_driver_name(dip),
9053                             ddi_get_instance(dip));
9054 
9055                         kmem_free(arg, sizeof (hubd_reset_arg_t));
9056                 }
9057 
9058                 break;
9059         case USB_RESET_LVL_DEFAULT:
9060                 /*
9061                  * Reset hub port and then recover device's address, set back
9062                  * device's configuration, hubd_handle_port_connect() will
9063                  * handle errors happened during this process.
9064                  */
9065                 if ((rval = hubd_handle_port_connect(hubd, port))
9066                     == USB_SUCCESS) {
9067                         mutex_exit(HUBD_MUTEX(hubd));
9068                         /* re-open the default pipe */
9069                         rval = usba_persistent_pipe_open(usba_device);
9070                         mutex_enter(HUBD_MUTEX(hubd));
9071                         if (rval != USB_SUCCESS) {
9072                                 USB_DPRINTF_L2(DPRINT_MASK_ATTA,
9073                                     hubd->h_log_handle, "failed to reopen "
9074                                     "default pipe after reset, disable hub"
9075                                     "port for %s%d", ddi_driver_name(dip),
9076                                     ddi_get_instance(dip));
9077                                 /*
9078                                  * Disable port to set out a hotplug thread
9079                                  * which will handle errors.
9080                                  */
9081                                 (void) hubd_disable_port(hubd, port);
9082                         }
9083                 }
9084 
9085                 break;
9086         default:
9087 
9088                 break;
9089         }
9090 
9091         /* allow hotplug thread now */
9092         hubd->h_hotplug_thread--;
9093 
9094         if ((hubd->h_dev_state == USB_DEV_ONLINE) && hubd->h_ep1_ph &&
9095             (prev_pipe_state == USB_PIPE_STATE_ACTIVE)) {
9096                 hubd_start_polling(hubd, 0);
9097         }
9098 
9099         hubd_pm_idle_component(hubd, hdip, 0);
9100 
9101         /* Clear reset mark for the port. */
9102         hubd->h_reset_port[port] = B_FALSE;
9103 
9104         mutex_exit(HUBD_MUTEX(hubd));
9105 
9106         ndi_rele_devi(hdip);
9107 
9108         return (rval);
9109 }