1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 /*
  26  * Copyright 2012 Garrett D'Amore <garrett@damore.org>.  All rights reserved.
  27  */
  28 
  29 /*
  30  * The Data Transfer Interface driver for Host Wire Adapter device
  31  *
  32  * HWA device has two interfaces, one is the data transfer interface,
  33  * another is the radio control interface. This driver (hwahc) is only
  34  * for data transfer interface support, but it depends on the radio
  35  * control interface driver (hwarc) to work. That means the hwarc
  36  * driver must be loaded while the hwahc is working. This is now
  37  * ensured by holding hwarc open until hwahc detaches or powers down.
  38  *
  39  * The data transfer interface has three endpoints besides the default
  40  * control endpoint which is shared between the two interfaces. The
  41  * three endpoints are:
  42  *
  43  * - notification endpoint (intr in type, for asynchronous event
  44  * notifications and transfer status notifications)
  45  *
  46  * - data transfer OUT endpoint (bulk out type, for sending transfer
  47  * requests and transfer data from the host to the HWA device)
  48  *
  49  * - data transfer IN endpoint (bulk in type, for returning transfer
  50  * status and transfer data from the HWA device to the host)
  51  *
  52  * The HWA device is a USB 2.0 device, so it supports the standard USB
  53  * requests defined in chapter 9 of USB 2.0 specification as other USB
  54  * client devices. But its most important functionality is to work as
  55  * a wireless USB host. This means the hwahc driver needs to supply
  56  * host controller functionalities, which include children hotplug
  57  * support and data transfer support to children device endpoints.
  58  *
  59  * So hwahc driver is implemented as a nexus driver and it follows the
  60  * event mechanism in existing USBA framework to support children
  61  * hotplug events.
  62  *
  63  * The hwahc driver works as the root-hub on wireless USB bus. And it
  64  * relays data transfers to/from wireless bus to the USB bus where ehci/
  65  * ohci/uhci works as the root-hub. This makes a bus cascading topology.
  66  *
  67  * The data transfer to/from wireless device endpoints is implemented by
  68  * remote pipe (rpipe) mechanism. The rpipe descriptor on the HWA defines
  69  * the attributes of a wireless USB transfer, such as the transfer type,
  70  * the target device address, the target endpoint address and the max
  71  * packet size. And the transfer requests through data transfer OUT
  72  * endpoint will take a certain rpipe as the transfer target, thus
  73  * fulfills the data transfer across buses. Refer to chapter 8 of WUSB
  74  * 1.0 specification for details of this.
  75  */
  76 
  77 #define USBDRV_MAJOR_VER        2
  78 #define USBDRV_MINOR_VER        0
  79 
  80 #include <sys/usb/hwa/hwahc/hwahc.h>
  81 #include <sys/usb/hwa/hwahc/hwahc_util.h>
  82 #include <sys/usb/usba/wa.h>
  83 #include <sys/usb/usba/wusba.h>
  84 #include <sys/usb/usba/whcdi.h>
  85 #include <sys/usb/usba.h>
  86 #include <sys/usb/usba/usba_impl.h>
  87 #include <sys/usb/usba/usba_devdb.h>      /* for usba_devdb_refresh */
  88 #include <sys/usb/hubd/hubdvar.h>
  89 #include <sys/usb/hubd/hubd_impl.h>       /* for hubd_ioctl_data_t */
  90 #include <sys/strsubr.h>  /* for allocb_wait */
  91 #include <sys/strsun.h>           /* for MBLKL macro */
  92 #include <sys/fs/dv_node.h>       /* for devfs_clean */
  93 #include <sys/uwb/uwbai.h>        /* for uwb ioctls */
  94 #include <sys/random.h>
  95 
  96 void *hwahc_statep;
  97 
  98 /* number of instances */
  99 #define HWAHC_INSTS     1
 100 
 101 /* default value for set number DNTS slots request */
 102 #define HWAHC_DEFAULT_DNTS_INTERVAL     2 /* ms */
 103 #define HWAHC_DEFAULT_DNTS_SLOT_NUM     4
 104 
 105 
 106 /* debug support */
 107 uint_t  hwahc_errmask   = (uint_t)PRINT_MASK_ALL;
 108 uint_t  hwahc_errlevel  = USB_LOG_L4;
 109 uint_t  hwahc_instance_debug = (uint_t)-1;
 110 
 111 /* bus config debug flag */
 112 uint_t  hwahc_bus_config_debug = 0;
 113 uint8_t hwahc_enable_trust_timeout = 1;
 114 
 115 
 116 /*
 117  * Use the default GTK for the whole life of HWA driver.
 118  * Not so compatible with WUSB spec.
 119  */
 120 static uint8_t  dft_gtk[16];
 121 static uint8_t  dft_gtkid[3];
 122 
 123 extern usb_log_handle_t whcdi_log_handle;
 124 
 125 /*
 126  * Function Prototypes
 127  */
 128 /* driver operations (dev_ops) entry points */
 129 static int      hwahc_open(dev_t *, int, int, cred_t *);
 130 static int      hwahc_close(dev_t, int, int, cred_t *);
 131 static int      hwahc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
 132 
 133 static int      hwahc_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
 134 static int      hwahc_attach(dev_info_t *, ddi_attach_cmd_t);
 135 static int      hwahc_detach(dev_info_t *, ddi_detach_cmd_t);
 136 static int      hwahc_power(dev_info_t *, int, int);
 137 
 138 /* bus_ops entry points */
 139 static int      hwahc_bus_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t,
 140                 void *, void *);
 141 static int      hwahc_busop_get_eventcookie(dev_info_t *, dev_info_t *,
 142                 char *, ddi_eventcookie_t *);
 143 static int      hwahc_busop_add_eventcall(
 144                 dev_info_t *, dev_info_t *, ddi_eventcookie_t,
 145                 void (*)(dev_info_t *, ddi_eventcookie_t, void *, void *),
 146                 void *, ddi_callback_id_t *);
 147 static int      hwahc_busop_remove_eventcall(dev_info_t *, ddi_callback_id_t);
 148 static int      hwahc_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t,
 149                 void *, dev_info_t **);
 150 static int      hwahc_bus_unconfig(dev_info_t *, uint_t, ddi_bus_config_op_t,
 151                 void *);
 152 
 153 /* hotplug and power management supporting functions */
 154 static int      hwahc_disconnect_event_cb(dev_info_t *dip);
 155 static int      hwahc_reconnect_event_cb(dev_info_t *dip);
 156 static int      hwahc_pre_suspend_event_cb(dev_info_t *dip);
 157 static int      hwahc_post_resume_event_cb(dev_info_t *dip);
 158 static int      hwahc_cpr_suspend(dev_info_t *);
 159 static int      hwahc_cpr_resume(dev_info_t *);
 160 static void     hwahc_restore_device_state(dev_info_t *, hwahc_state_t *);
 161 static void     hwahc_run_callbacks(hwahc_state_t *, usba_event_t);
 162 static void     hwahc_post_event(hwahc_state_t *, usb_port_t, usba_event_t);
 163 
 164 static int      hwahc_cleanup(dev_info_t *, hwahc_state_t *);
 165 static void     hwahc_create_pm_components(dev_info_t *, hwahc_state_t *);
 166 static void     hwahc_destroy_pm_components(hwahc_state_t *);
 167 static void     hwahc_pm_busy_component(hwahc_state_t *);
 168 static void     hwahc_pm_idle_component(hwahc_state_t *);
 169 static int      hwahc_pwrlvl0(hwahc_state_t *);
 170 static int      hwahc_pwrlvl1(hwahc_state_t *);
 171 static int      hwahc_pwrlvl2(hwahc_state_t *);
 172 static int      hwahc_pwrlvl3(hwahc_state_t *);
 173 static int      hwahc_hc_channel_suspend(hwahc_state_t *);
 174 
 175 /* hardware initialization and deinitialization functions */
 176 static int      hwahc_parse_security_data(wusb_secrt_data_t *,
 177                 usb_cfg_data_t *);
 178 static void     hwahc_print_secrt_data(hwahc_state_t *);
 179 
 180 static int      hwahc_hub_attach(hwahc_state_t *);
 181 static int      hwahc_hub_detach(hwahc_state_t *);
 182 
 183 static int      hwahc_hc_initial_start(hwahc_state_t *);
 184 static int      hwahc_hc_final_stop(hwahc_state_t *);
 185 static int      hwahc_wa_start(hwahc_state_t *);
 186 static void     hwahc_wa_stop(hwahc_state_t *);
 187 static int      hwahc_hc_channel_start(hwahc_state_t *);
 188 static int      hwahc_hc_channel_stop(hwahc_state_t *);
 189 static void     hwahc_hc_data_init(hwahc_state_t *);
 190 static void     hwahc_hc_data_fini(hwahc_state_t *);
 191 
 192 /* ioctl support */
 193 static int      hwahc_cfgadm_ioctl(hwahc_state_t *, int, intptr_t, int,
 194                 cred_t *, int *);
 195 static int      hwahc_wusb_ioctl(hwahc_state_t *, int, intptr_t, int,
 196                 cred_t *, int *);
 197 
 198 /* callbacks registered to USBA */
 199 static void     hwahc_disconnect_dev(dev_info_t *, usb_port_t);
 200 static void     hwahc_reconnect_dev(dev_info_t *, usb_port_t);
 201 static int      hwahc_create_child(dev_info_t *, usb_port_t);
 202 static int      hwahc_destroy_child(dev_info_t *, usb_port_t);
 203 static int      hwahc_cleanup_child(dev_info_t *);
 204 static int      hwahc_delete_child(dev_info_t *, usb_port_t, uint_t, boolean_t);
 205 
 206 /* data transfer and notification handling */
 207 static void     hwahc_intr_cb(usb_pipe_handle_t, struct usb_intr_req *);
 208 static void     hwahc_intr_exc_cb(usb_pipe_handle_t, struct usb_intr_req *);
 209 static void     hwahc_handle_notif(hwahc_state_t *, mblk_t *);
 210 static void     hwahc_handle_xfer_result(hwahc_state_t *, uint8_t);
 211 static void     hwahc_stop_result_thread(hwahc_state_t *);
 212 static void     hwahc_result_thread(void *);
 213 static void     hwahc_handle_dn_notif(hwahc_state_t *, hwa_notif_dn_recvd_t *);
 214 static void     hwahc_notif_thread(void *);
 215 static void     hwahc_handle_dn(hwahc_state_t *, hwa_notif_dn_recvd_t *);
 216 static void     hwahc_drain_notif_queue(hwahc_state_t *);
 217 static void     hwahc_rpipe_xfer_cb(dev_info_t *, usba_pipe_handle_data_t *,
 218                 wusb_wa_trans_wrapper_t *, usb_cr_t);
 219 
 220 static void     hwahc_trust_timeout_handler(void *arg);
 221 static void     hwahc_stop_trust_timer(wusb_dev_info_t *dev);
 222 
 223 static int hwahc_pipe_submit_periodic_req(wusb_wa_data_t *wa_data,
 224         usba_pipe_handle_data_t *ph);
 225 
 226 /* hwa specific requests */
 227 static int      hwahc_set_chid(hwahc_state_t *, uint8_t *);
 228 
 229 /* helper functions */
 230 static usb_port_t hwahc_get_port_num(hwahc_state_t *, struct devctl_iocdata *);
 231 static dev_info_t *hwahc_get_child_dip(hwahc_state_t *, usb_port_t);
 232 
 233 static struct cb_ops hwahc_cb_ops = {
 234         hwahc_open,                     /* Open */
 235         hwahc_close,                    /* Close */
 236         nodev,                          /* Strategy */
 237         nodev,                          /* Print */
 238         nodev,                          /* Dump */
 239         nodev,                          /* Read */
 240         nodev,                          /* Write */
 241         hwahc_ioctl,                    /* Ioctl */
 242         nodev,                          /* Devmap */
 243         nodev,                          /* Mmap */
 244         nodev,                          /* Segmap */
 245         nochpoll,                       /* Poll */
 246         ddi_prop_op,                    /* cb_prop_op */
 247         NULL,                           /* Streamtab */
 248         D_MP                            /* Driver compatibility flag */
 249 };
 250 
 251 static struct bus_ops hwahc_busops = {
 252         BUSO_REV,
 253         nullbusmap,                     /* bus_map */
 254         NULL,                           /* bus_get_intrspec */
 255         NULL,                           /* bus_add_intrspec */
 256         NULL,                           /* bus_remove_intrspec */
 257         NULL,                           /* bus_map_fault */
 258         NULL,                           /* bus_dma_map */
 259         ddi_dma_allochdl,
 260         ddi_dma_freehdl,
 261         ddi_dma_bindhdl,
 262         ddi_dma_unbindhdl,
 263         ddi_dma_flush,
 264         ddi_dma_win,
 265         ddi_dma_mctl,                   /* bus_dma_ctl */
 266         hwahc_bus_ctl,                  /* bus_ctl */
 267         ddi_bus_prop_op,                /* bus_prop_op */
 268         hwahc_busop_get_eventcookie,    /* bus_get_eventcookie */
 269         hwahc_busop_add_eventcall,      /* bus_add_eventcall */
 270         hwahc_busop_remove_eventcall,   /* bus_remove_eventcall */
 271         NULL,                           /* bus_post_event */
 272         NULL,                           /* bus_intr_ctl */
 273         hwahc_bus_config,               /* bus_config */
 274         hwahc_bus_unconfig,             /* bus_unconfig */
 275         NULL,                           /* bus_fm_init */
 276         NULL,                           /* bus_fm_fini */
 277         NULL,                           /* bus_fm_access_enter */
 278         NULL,                           /* bus_fm_access_exit */
 279         NULL,                           /* bus_power */
 280 };
 281 
 282 static struct dev_ops hwahc_ops = {
 283         DEVO_REV,                       /* Devo_rev */
 284         0,                              /* Refcnt */
 285         hwahc_info,                     /* Info */
 286         nulldev,                        /* Identify */
 287         nulldev,                        /* Probe */
 288         hwahc_attach,                   /* Attach */
 289         hwahc_detach,                   /* Detach */
 290         nodev,                          /* Reset */
 291         &hwahc_cb_ops,                      /* Driver operations */
 292         &hwahc_busops,                      /* Bus operations */
 293         hwahc_power,                    /* Power */
 294         ddi_quiesce_not_needed,         /* devo_quiesce */
 295 };
 296 
 297 static struct modldrv hwahc_modldrv =   {
 298         &mod_driverops,
 299         "WUSB hwa-hc driver",
 300         &hwahc_ops
 301 };
 302 
 303 static struct modlinkage modlinkage = {
 304         MODREV_1,
 305         &hwahc_modldrv,
 306         NULL
 307 };
 308 
 309 /* events from parent */
 310 static usb_event_t hwahc_events = {
 311         hwahc_disconnect_event_cb,
 312         hwahc_reconnect_event_cb,
 313         hwahc_pre_suspend_event_cb,
 314         hwahc_post_resume_event_cb
 315 };
 316 
 317 /*
 318  * events support for children
 319  * A map tween USBA_EVENTs and DDI_EVENTs.
 320  */
 321 static ndi_event_definition_t hwahc_ndi_event_defs[] = {
 322         {USBA_EVENT_TAG_HOT_REMOVAL, DDI_DEVI_REMOVE_EVENT, EPL_KERNEL,
 323                                                 NDI_EVENT_POST_TO_ALL},
 324         {USBA_EVENT_TAG_HOT_INSERTION, DDI_DEVI_INSERT_EVENT, EPL_KERNEL,
 325                                                 NDI_EVENT_POST_TO_ALL},
 326         {USBA_EVENT_TAG_POST_RESUME, USBA_POST_RESUME_EVENT, EPL_KERNEL,
 327                                                 NDI_EVENT_POST_TO_ALL},
 328         {USBA_EVENT_TAG_PRE_SUSPEND, USBA_PRE_SUSPEND_EVENT, EPL_KERNEL,
 329                                                 NDI_EVENT_POST_TO_ALL}
 330 };
 331 
 332 #define HWAHC_N_NDI_EVENTS \
 333         (sizeof (hwahc_ndi_event_defs) / sizeof (ndi_event_definition_t))
 334 
 335 static  ndi_event_set_t hwahc_ndi_events = {
 336         NDI_EVENTS_REV1, HWAHC_N_NDI_EVENTS, hwahc_ndi_event_defs};
 337 
 338 /* transfer callbacks */
 339 static wusb_wa_cb_t hwahc_cbs = {
 340         hwahc_pipe_submit_periodic_req,
 341         hwahc_intr_cb,
 342         hwahc_intr_exc_cb,
 343         hwahc_rpipe_xfer_cb
 344 };
 345 
 346 
 347 /*
 348  * Module-wide initialization routine.
 349  */
 350 int
 351 _init(void)
 352 {
 353         int rval;
 354 
 355         if ((rval = ddi_soft_state_init(&hwahc_statep, sizeof (hwahc_state_t),
 356             HWAHC_INSTS)) != 0) {
 357 
 358                 return (rval);
 359         }
 360 
 361         if ((rval = mod_install(&modlinkage)) != 0) {
 362                 ddi_soft_state_fini(&hwahc_statep);
 363         }
 364 
 365         return (rval);
 366 }
 367 
 368 
 369 /*
 370  * Module-wide tear-down routine.
 371  */
 372 int
 373 _fini(void)
 374 {
 375         int rval;
 376 
 377         if ((rval = mod_remove(&modlinkage)) == 0) {
 378                 /* Release per module resources */
 379                 ddi_soft_state_fini(&hwahc_statep);
 380         }
 381 
 382         return (rval);
 383 }
 384 
 385 
 386 int
 387 _info(struct modinfo *modinfop)
 388 {
 389         return (mod_info(&modlinkage, modinfop));
 390 }
 391 
 392 
 393 /*
 394  * hwahc_info:
 395  *      Get minor number, instance number, etc.
 396  */
 397 /*ARGSUSED*/
 398 static int
 399 hwahc_info(dev_info_t *dip, ddi_info_cmd_t infocmd,
 400         void *arg, void **result)
 401 {
 402         hwahc_state_t   *hwahcp;
 403         int error = DDI_FAILURE;
 404         int instance = HWAHC_MINOR_TO_INSTANCE(getminor((dev_t)arg));
 405 
 406         switch (infocmd) {
 407         case DDI_INFO_DEVT2DEVINFO:
 408                 if ((hwahcp = ddi_get_soft_state(hwahc_statep,
 409                     instance)) != NULL) {
 410                         *result = hwahcp->hwahc_dip;
 411                         if (*result != NULL) {
 412                                 error = DDI_SUCCESS;
 413                         }
 414                 } else {
 415                         *result = NULL;
 416                 }
 417                 break;
 418         case DDI_INFO_DEVT2INSTANCE:
 419                 *result = (void *)(uintptr_t)instance;
 420                 error = DDI_SUCCESS;
 421                 break;
 422         default:
 423                 break;
 424         }
 425 
 426         return (error);
 427 }
 428 
 429 
 430 /*
 431  * hwahc_attach:
 432  *      Attach or resume.
 433  *
 434  *      For attach, initialize state and device, including:
 435  *              state variables, locks, device node,
 436  *              resource initialization, event registration,
 437  *              device registration with system
 438  *              power management, hotplugging
 439  *      For resume, restore device and state
 440  */
 441 static int
 442 hwahc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
 443 {
 444         int                             instance = ddi_get_instance(dip);
 445         hwahc_state_t                   *hwahcp = NULL;
 446         usb_client_dev_data_t           *dev_data;
 447         struct usb_cfg_data             *cfg_data;
 448         usba_hcdi_register_args_t       hcdi_args;
 449         int                             rval;
 450         char *pathname;
 451 
 452         USB_DPRINTF_L3(PRINT_MASK_ATTA, NULL, "hwahc_attach: cmd=%d", cmd);
 453 
 454         switch (cmd) {
 455         case DDI_ATTACH:
 456                 break;
 457         case DDI_RESUME:
 458                 (void) hwahc_cpr_resume(dip);
 459 
 460                 return (DDI_SUCCESS);
 461         default:
 462                 USB_DPRINTF_L2(PRINT_MASK_ATTA, NULL,
 463                     "hwahc_attach: failed");
 464 
 465                 return (DDI_FAILURE);
 466         }
 467 
 468         /*
 469          * Allocate soft state information.
 470          */
 471         rval = ddi_soft_state_zalloc(hwahc_statep, instance);
 472         if (rval != DDI_SUCCESS) {
 473                 USB_DPRINTF_L2(PRINT_MASK_ATTA, NULL,
 474                     "hwahc_attach: cannot allocate soft state for instance %d",
 475                     instance);
 476 
 477                 return (USB_FAILURE);
 478         }
 479 
 480         hwahcp = ddi_get_soft_state(hwahc_statep, instance);
 481         if (hwahcp == NULL) {
 482                 USB_DPRINTF_L2(PRINT_MASK_ATTA, NULL,
 483                     "hwahc_attach: get soft state failed for instance %d",
 484                     instance);
 485 
 486                 return (USB_FAILURE);
 487         }
 488 
 489         hwahcp->hwahc_log_handle = usb_alloc_log_hdl(dip, "hwahc",
 490             &hwahc_errlevel, &hwahc_errmask, &hwahc_instance_debug, 0);
 491 
 492         /* initialize hc state */
 493         hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_INIT_STATE;
 494         hwahcp->hwahc_dip = dip;
 495         hwahcp->hwahc_instance = instance;
 496 
 497         /* register with USBA as client driver */
 498         if (usb_client_attach(dip, USBDRV_VERSION, 0) != USB_SUCCESS) {
 499                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
 500                     "hwahc_attach: client attach failed");
 501 
 502                 goto fail;
 503         }
 504 
 505         if (usb_get_dev_data(dip, &dev_data, USB_PARSE_LVL_IF, 0) !=
 506             USB_SUCCESS) {
 507                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
 508                     "hwahc_attach: cannot get dev_data");
 509 
 510                 goto fail;
 511         }
 512 
 513         /* initialize mutex and cv */
 514         mutex_init(&hwahcp->hwahc_mutex, NULL, MUTEX_DRIVER,
 515             dev_data->dev_iblock_cookie);
 516         cv_init(&hwahcp->hwahc_result_thread_cv, NULL, CV_DRIVER, NULL);
 517 
 518         hwahcp->hwahc_flags |= HWAHC_LOCK_INITED;
 519         hwahcp->hwahc_dev_data = dev_data;
 520 
 521         /* initialize data transfer function related structure */
 522         if (wusb_wa_data_init(dip, &hwahcp->hwahc_wa_data, &hwahc_cbs,
 523             dev_data, PRINT_MASK_ATTA,
 524             hwahcp->hwahc_log_handle) != USB_SUCCESS) {
 525                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
 526                     "hwahc_attach: init wa data failed");
 527 
 528                 goto fail;
 529         }
 530 
 531         hwahcp->hwahc_flags |= HWAHC_WA_INITED;
 532         cfg_data = dev_data->dev_curr_cfg;
 533 
 534         /* parse the security descrs from the configuration descr cloud */
 535         if (hwahc_parse_security_data(&hwahcp->hwahc_secrt_data, cfg_data) !=
 536             USB_SUCCESS) {
 537                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
 538                     "hwahc_attach: parse security descrs failed");
 539 
 540                 goto fail;
 541         }
 542 
 543         hwahcp->hwahc_default_pipe = dev_data->dev_default_ph;
 544         hwahcp->hwahc_wa_data.wa_private_data = (void *)hwahcp;
 545         hwahcp->hwahc_wa_data.wa_default_pipe = hwahcp->hwahc_default_pipe;
 546 
 547         usb_free_descr_tree(dip, dev_data);
 548 
 549         hwahcp->hwahc_dev_state = USB_DEV_ONLINE;
 550 
 551         /* now create components to power manage this device */
 552         hwahc_create_pm_components(dip, hwahcp);
 553 
 554         /*
 555          * Event definition and registration
 556          *
 557          * allocate a new NDI event handle as a nexus driver
 558          */
 559         (void) ndi_event_alloc_hdl(dip, 0, &hwahcp->hwahc_ndi_event_hdl,
 560             NDI_SLEEP);
 561 
 562         /*
 563          * bind our NDI events with the event handle,
 564          * i.e. Define the events set we're to support as a nexus driver.
 565          *
 566          * These events will be used by bus_ops functions to register callbacks.
 567          */
 568         if (ndi_event_bind_set(hwahcp->hwahc_ndi_event_hdl, &hwahc_ndi_events,
 569             NDI_SLEEP)) {
 570                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
 571                     "hwahc_attach: binding event set failed");
 572 
 573                 goto fail;
 574         }
 575 
 576 
 577         /*
 578          * Register USB events to USBA(the parent) to get callbacks as a
 579          * child of (root) hub
 580          */
 581         if (usb_register_event_cbs(dip, &hwahc_events, 0) != USB_SUCCESS) {
 582                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
 583                     "hwahc_attach: register_events failed");
 584 
 585                 goto fail;
 586         }
 587 
 588         hwahcp->hwahc_flags |= HWAHC_EVENTS_REGISTERED;
 589 
 590         /* create minor nodes */
 591         if (ddi_create_minor_node(dip, "hwahc", S_IFCHR,
 592             instance << HWAHC_MINOR_INSTANCE_SHIFT,
 593             DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
 594 
 595                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
 596                     "hwahc_attach: cannot create minor node");
 597 
 598                 goto fail;
 599         }
 600 
 601         hwahcp->hwahc_flags |= HWAHC_MINOR_NODE_CREATED;
 602 
 603         hwahcp->hwahc_hcdi_ops = hwahc_alloc_hcdi_ops(hwahcp);
 604 
 605         /* register this hc instance with usba HCD interface */
 606         hcdi_args.usba_hcdi_register_version = HCDI_REGISTER_VERSION;
 607         hcdi_args.usba_hcdi_register_dip = dip;
 608         hcdi_args.usba_hcdi_register_ops = hwahcp->hwahc_hcdi_ops;
 609 
 610         /* use parent dma attr here */
 611         hcdi_args.usba_hcdi_register_dma_attr = usba_get_hc_dma_attr(dip);
 612         hcdi_args.usba_hcdi_register_iblock_cookie = NULL;
 613 
 614         if (usba_hcdi_register(&hcdi_args, 0) != USB_SUCCESS) {
 615                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
 616                     "hwahc_attach: usba_hcdi_register failed");
 617 
 618                 goto fail;
 619         }
 620 
 621         hwahcp->hwahc_flags |= HWAHC_HCDI_REGISTERED;
 622 
 623         /* create hub minor node and register to usba HUBD interface */
 624         if (hwahc_hub_attach(hwahcp) != USB_SUCCESS) {
 625                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
 626                     "hwahc_attach: hub attach failed");
 627 
 628                 goto fail;
 629         }
 630         hwahcp->hwahc_flags |= HWAHC_HUBREG;
 631 
 632         /* intialize WUSB host function related structure */
 633         hwahc_hc_data_init(hwahcp);
 634         hwahcp->hwahc_flags |= HWAHC_HC_INITED;
 635 
 636         /* can be combined with wusb_wa_data_init() */
 637         if (hwahc_wa_start(hwahcp) != USB_SUCCESS) {
 638 
 639                 goto fail;
 640         }
 641 
 642         hwahcp->hwahc_flags |= HWAHC_WA_STARTED;
 643 
 644         /* report this dev */
 645         ddi_report_dev(dip);
 646 
 647         hwahc_pm_idle_component(hwahcp);
 648 
 649         mutex_enter(&(hwahcp->hwahc_mutex));
 650         hwahc_print_secrt_data(hwahcp);
 651         mutex_exit(&(hwahcp->hwahc_mutex));
 652 
 653         if (uwb_dev_online(dip) != USB_SUCCESS) {
 654                 goto fail;
 655         }
 656 
 657         return (DDI_SUCCESS);
 658 
 659 fail:
 660         pathname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
 661 
 662         /* log this message to usba_debug_buf */
 663         USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
 664             "cannot attach %s", ddi_pathname(dip, pathname));
 665 
 666         kmem_free(pathname, MAXPATHLEN);
 667 
 668         if (hwahcp) {
 669                 hwahc_pm_idle_component(hwahcp);
 670 
 671                 rval = hwahc_cleanup(dip, hwahcp);
 672                 if (rval != USB_SUCCESS) {
 673                         USB_DPRINTF_L2(PRINT_MASK_ATTA,
 674                             hwahcp->hwahc_log_handle,
 675                             "failure to complete cleanup after attach failure");
 676                 }
 677         }
 678 
 679         return (DDI_FAILURE);
 680 }
 681 
 682 
 683 /*
 684  * hwahc_detach:
 685  *      detach or suspend driver instance
 686  *
 687  * Note: in detach, only contention threads is from pm and disconnnect.
 688  */
 689 static int
 690 hwahc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
 691 {
 692         int             instance = ddi_get_instance(dip);
 693         hwahc_state_t   *hwahcp = ddi_get_soft_state(hwahc_statep, instance);
 694         int             rval = DDI_FAILURE;
 695 
 696 
 697         USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
 698             "hwahc_detach: cmd = %d", cmd);
 699 
 700         switch (cmd) {
 701         case DDI_DETACH:
 702                 USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
 703                     "offline uwb device for dip: 0x%p", (void *)dip);
 704                 /* offline the hwarc interface */
 705                 (void) uwb_dev_offline(dip);
 706                 if (hwahcp) {
 707                         rval = hwahc_cleanup(dip, hwahcp);
 708                 }
 709 
 710                 break;
 711         case DDI_SUSPEND:
 712                 rval = hwahc_cpr_suspend(dip);
 713 
 714                 break;
 715         default:
 716 
 717                 break;
 718         }
 719 
 720         return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
 721 }
 722 
 723 
 724 /*
 725  * hwahc_cleanup:
 726  *      clean up on attach failure or detach
 727  */
 728 static int
 729 hwahc_cleanup(dev_info_t *dip, hwahc_state_t *hwahcp)
 730 {
 731         USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
 732             "hwahc_cleanup: start");
 733 
 734         if ((hwahcp->hwahc_flags & HWAHC_LOCK_INITED) == 0) {
 735 
 736                 goto done;
 737         }
 738 
 739         /*
 740          * deallocate events, if events are still registered
 741          * (ie. children still attached) then we have to fail the detach
 742          */
 743         if (hwahcp->hwahc_ndi_event_hdl &&
 744             (ndi_event_free_hdl(hwahcp->hwahc_ndi_event_hdl) != NDI_SUCCESS)) {
 745 
 746                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
 747                     "hwahc_cleanup: ndi_event_free_hdl failed");
 748 
 749                 return (USB_FAILURE);
 750         }
 751 
 752         if (hwahcp->hwahc_flags & HWAHC_EVENTS_REGISTERED) {
 753                 /* unregister events */
 754                 usb_unregister_event_cbs(dip, &hwahc_events);
 755         }
 756 
 757         if (hwahcp->hwahc_flags & HWAHC_HCDI_REGISTERED) {
 758                 /* unregister the instance with usba HCD interface */
 759                 usba_hcdi_unregister(hwahcp->hwahc_dip);
 760         }
 761 
 762         mutex_enter(&hwahcp->hwahc_mutex);
 763 
 764         if (hwahcp->hwahc_hw_state != HWAHC_HW_STOPPED) {
 765                 /* stop the hw if it is enabled */
 766                 (void) hwahc_hc_final_stop(hwahcp);
 767         }
 768 
 769         if (hwahcp->hwahc_flags & HWAHC_WA_STARTED) {
 770                 /* can be combined with wusb_wa_data_fini() */
 771                 mutex_exit(&hwahcp->hwahc_mutex);
 772                 hwahc_wa_stop(hwahcp);
 773                 mutex_enter(&hwahcp->hwahc_mutex);
 774         }
 775 
 776         if (hwahcp->hwahc_flags & HWAHC_HC_INITED) {
 777                 /* deinitialize the WUSB host function related structure */
 778                 hwahc_hc_data_fini(hwahcp);
 779         }
 780 
 781         mutex_exit(&hwahcp->hwahc_mutex);
 782 
 783         if (hwahcp->hwahc_pm) {
 784                 /* destroy power management components */
 785                 hwahc_destroy_pm_components(hwahcp);
 786         }
 787 
 788         if (hwahcp->hwahc_flags & HWAHC_HUBREG) {
 789                 /* unregister the instance from usba HUBD interface */
 790                 if (hwahc_hub_detach(hwahcp) != USB_SUCCESS) {
 791 
 792                         return (USB_FAILURE);
 793                 }
 794         }
 795 
 796         if (hwahcp->hwahc_hcdi_ops) {
 797                 usba_free_hcdi_ops(hwahcp->hwahc_hcdi_ops);
 798         }
 799 
 800         mutex_enter(&hwahcp->hwahc_mutex);
 801         if (hwahcp->hwahc_secrt_data.secrt_encry_descr) {
 802                 /* free security descrs */
 803                 kmem_free(hwahcp->hwahc_secrt_data.secrt_encry_descr,
 804                     sizeof (usb_encryption_descr_t) *
 805                     hwahcp->hwahc_secrt_data.secrt_n_encry);
 806         }
 807 
 808         if (hwahcp->hwahc_flags & HWAHC_WA_INITED) {
 809                 /* deinitialize data transfer function related structure */
 810                 wusb_wa_data_fini(&hwahcp->hwahc_wa_data);
 811         }
 812         mutex_exit(&hwahcp->hwahc_mutex);
 813 
 814         if (hwahcp->hwahc_flags & HWAHC_MINOR_NODE_CREATED) {
 815                 /* remove all the minor nodes */
 816                 ddi_remove_minor_node(dip, NULL);
 817         }
 818 
 819         /* destroy mutex and cv */
 820         mutex_destroy(&hwahcp->hwahc_mutex);
 821         cv_destroy(&hwahcp->hwahc_result_thread_cv);
 822 
 823 done:
 824         /* unregister the client driver from usba */
 825         usb_client_detach(dip, hwahcp->hwahc_dev_data);
 826 
 827         USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
 828             "hwahc_cleanup: end");
 829 
 830         usb_free_log_hdl(hwahcp->hwahc_log_handle);
 831 
 832         /* remove all properties created */
 833         ddi_prop_remove_all(dip);
 834 
 835         /* free the soft state information */
 836         ddi_soft_state_free(hwahc_statep, ddi_get_instance(dip));
 837 
 838         return (USB_SUCCESS);
 839 }
 840 
 841 
 842 /*ARGSUSED*/
 843 static int
 844 hwahc_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
 845 {
 846         hwahc_state_t   *hwahcp;
 847 
 848         if ((hwahcp = ddi_get_soft_state(hwahc_statep,
 849             HWAHC_MINOR_TO_INSTANCE(getminor(*devp)))) == NULL) {
 850 
 851                 return (ENXIO);
 852         }
 853 
 854         USB_DPRINTF_L4(PRINT_MASK_OPEN, hwahcp->hwahc_log_handle,
 855             "hwahc_open: start");
 856 
 857         mutex_enter(&hwahcp->hwahc_mutex);
 858         /* exclusive open */
 859         if ((flag & FEXCL) && (hwahcp->hwahc_open_count > 0)) {
 860                 mutex_exit(&hwahcp->hwahc_mutex);
 861 
 862                 return (EBUSY);
 863         }
 864 
 865         if ((hwahcp->hwahc_dev_state == USB_DEV_DISCONNECTED) ||
 866             (hwahcp->hwahc_dev_state == USB_DEV_SUSPENDED)) {
 867                 mutex_exit(&hwahcp->hwahc_mutex);
 868 
 869                 return (EIO);
 870         }
 871 
 872         hwahcp->hwahc_open_count++;
 873 
 874         mutex_exit(&hwahcp->hwahc_mutex);
 875 
 876         /* raise to full power and keep it until close */
 877         hwahc_pm_busy_component(hwahcp);
 878         (void) pm_raise_power(hwahcp->hwahc_dip, 0, USB_DEV_OS_FULL_PWR);
 879 
 880         USB_DPRINTF_L4(PRINT_MASK_OPEN, hwahcp->hwahc_log_handle,
 881             "hwahc_open: end");
 882 
 883         return (0);
 884 }
 885 
 886 
 887 /*ARGSUSED*/
 888 static int
 889 hwahc_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
 890 {
 891         hwahc_state_t   *hwahcp;
 892 
 893         if ((hwahcp = ddi_get_soft_state(hwahc_statep,
 894             HWAHC_MINOR_TO_INSTANCE(getminor(dev)))) == NULL) {
 895 
 896                 return (ENXIO);
 897         }
 898 
 899         USB_DPRINTF_L4(PRINT_MASK_CLOSE, hwahcp->hwahc_log_handle,
 900             "hwahc_close: start");
 901 
 902         mutex_enter(&hwahcp->hwahc_mutex);
 903         if (hwahcp->hwahc_open_count == 0) {
 904                 USB_DPRINTF_L2(PRINT_MASK_CLOSE, hwahcp->hwahc_log_handle,
 905                     "hwahc_close: already closed");
 906                 mutex_exit(&hwahcp->hwahc_mutex);
 907 
 908                 return (EINVAL);
 909         }
 910 
 911         hwahcp->hwahc_open_count--;
 912         mutex_exit(&hwahcp->hwahc_mutex);
 913 
 914         hwahc_pm_idle_component(hwahcp);
 915 
 916         USB_DPRINTF_L4(PRINT_MASK_CLOSE, hwahcp->hwahc_log_handle,
 917             "hwahc_close: end");
 918 
 919         return (0);
 920 }
 921 
 922 /* retrieve port number from devctl data */
 923 static usb_port_t
 924 hwahc_get_port_num(hwahc_state_t *hwahcp, struct devctl_iocdata *dcp)
 925 {
 926         int32_t port;
 927 
 928         ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
 929 
 930         /* Get which port to operate on.  */
 931         if (nvlist_lookup_int32(ndi_dc_get_ap_data(dcp), "port", &port) != 0) {
 932                 USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
 933                     "hwahc_get_port_num: port lookup failed");
 934                 port = 0;
 935         }
 936 
 937         USB_DPRINTF_L4(PRINT_MASK_CBOPS,  hwahcp->hwahc_log_handle,
 938             "hwahc_get_port_num: hwahcp=0x%p, port=%d", (void *)hwahcp,
 939             port);
 940 
 941         return ((usb_port_t)port);
 942 }
 943 
 944 /* return the child dip on a certain port */
 945 static dev_info_t *
 946 hwahc_get_child_dip(hwahc_state_t *hwahcp, usb_port_t port)
 947 {
 948         wusb_hc_data_t          *hc_data;
 949         dev_info_t              *child_dip;
 950 
 951         hc_data = &hwahcp->hwahc_hc_data;
 952 
 953         /* check port range to prevent an illegal number */
 954         if (port > hc_data->hc_num_ports) {
 955                 return (NULL);
 956         }
 957 
 958         mutex_enter(&hc_data->hc_mutex);
 959         child_dip = hc_data->hc_children_dips[port];
 960         mutex_exit(&hc_data->hc_mutex);
 961 
 962         return (child_dip);
 963 }
 964 
 965 /*
 966  * hwahc_cfgadm_state:
 967  *
 968  *      child_dip list          child_state             cfgadm_state
 969  *      --------------          ----------              ------------
 970  *      != NULL                 connected               configured or
 971  *                                                      unconfigured
 972  *      != NULL                 not connected           disconnect but
 973  *                                                      busy/still referenced
 974  *      NULL                    connected               logically disconnected
 975  *      NULL                    not connected           empty
 976  */
 977 static uint_t
 978 hwahc_cfgadm_state(hwahc_state_t *hwahcp, usb_port_t port)
 979 {
 980         uint_t          state;
 981         dev_info_t      *child_dip = hwahc_get_child_dip(hwahcp, port);
 982         wusb_hc_data_t  *hc_data = &hwahcp->hwahc_hc_data;
 983         wusb_dev_info_t *dev_info;
 984 
 985         if (child_dip == NULL) {
 986 
 987                 return (HWAHC_CFGADM_INVALID);
 988         }
 989 
 990         mutex_enter(&hc_data->hc_mutex);
 991         dev_info = hc_data->hc_dev_infos[port];
 992         if (dev_info) {
 993                 if (dev_info->wdev_state == WUSB_STATE_CONFIGURED) {
 994                         if (child_dip &&
 995                             (DEVI_IS_DEVICE_OFFLINE(child_dip) ||
 996                             !i_ddi_devi_attached(child_dip))) {
 997                                 state = HWAHC_CFGADM_UNCONFIGURED;
 998                         } else if (!child_dip) {
 999                                 state = HWAHC_CFGADM_UNCONFIGURED;
1000                         } else {
1001                                 state = HWAHC_CFGADM_CONFIGURED;
1002                         }
1003                 } else if (dev_info->wdev_state == WUSB_STATE_UNCONNTED) {
1004                         if (child_dip) {
1005                                 state = HWAHC_CFGADM_STILL_REFERENCED;
1006                         } else {
1007                                 state = HWAHC_CFGADM_DISCONNECTED;
1008                         }
1009                 } else {
1010                         if (child_dip) {
1011                                 state = HWAHC_CFGADM_STILL_REFERENCED;
1012                         } else {
1013                                 state = HWAHC_CFGADM_UNCONFIGURED;
1014                         }
1015                 }
1016         } else {
1017                 state = HWAHC_CFGADM_EMPTY;
1018         }
1019         mutex_exit(&hc_data->hc_mutex);
1020 
1021         USB_DPRINTF_L4(PRINT_MASK_CBOPS,  hwahcp->hwahc_log_handle,
1022             "hwahc_cfgadm_state: hwahcp=0x%p, port=%d state=0x%x",
1023             (void *) hwahcp, port, state);
1024 
1025         return (state);
1026 }
1027 
1028 /* cfgadm ioctl support, now only implements list function */
1029 /* ARGSUSED */
1030 static int
1031 hwahc_cfgadm_ioctl(hwahc_state_t *hwahcp, int cmd, intptr_t arg,
1032         int mode, cred_t *credp, int *rvalp)
1033 {
1034         dev_info_t              *rh_dip;
1035         dev_info_t              *child_dip;
1036         struct devctl_iocdata   *dcp = NULL;
1037         usb_port_t              port = 0;
1038         devctl_ap_state_t       ap_state;
1039         int                     circ, rh_circ, prh_circ;
1040         int                     rv = 0;
1041         char                    *msg;
1042 
1043         /* read devctl ioctl data */
1044         if ((cmd != DEVCTL_AP_CONTROL) &&
1045             (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)) {
1046 
1047                 return (EFAULT);
1048         }
1049 
1050         mutex_enter(&hwahcp->hwahc_mutex);
1051 
1052         rh_dip = hwahcp->hwahc_hubd->h_usba_device->usb_root_hub_dip;
1053 
1054         switch (cmd) {
1055         case DEVCTL_AP_DISCONNECT:
1056         case DEVCTL_AP_UNCONFIGURE:
1057         case DEVCTL_AP_CONFIGURE:
1058                 if (hwahcp->hwahc_dev_state == USB_DEV_DISCONNECTED) {
1059                         USB_DPRINTF_L2(PRINT_MASK_ATTA,
1060                             hwahcp->hwahc_log_handle,
1061                             "hwahc_cfgadm_ioctl: dev already gone");
1062                         mutex_exit(&hwahcp->hwahc_mutex);
1063                         if (dcp) {
1064                                 ndi_dc_freehdl(dcp);
1065                         }
1066 
1067                         return (EIO);
1068                 }
1069                 /* FALLTHROUGH */
1070         case DEVCTL_AP_GETSTATE:
1071                 if ((port = hwahc_get_port_num(hwahcp, dcp)) == 0) {
1072                         USB_DPRINTF_L2(PRINT_MASK_ATTA,
1073                             hwahcp->hwahc_log_handle,
1074                             "hwahc_cfgadm_ioctl: bad port");
1075                         mutex_exit(&hwahcp->hwahc_mutex);
1076                         if (dcp) {
1077                                 ndi_dc_freehdl(dcp);
1078                         }
1079 
1080                         return (EINVAL);
1081                 }
1082                 break;
1083         case DEVCTL_AP_CONTROL:
1084 
1085                 break;
1086         default:
1087                 mutex_exit(&hwahcp->hwahc_mutex);
1088                 if (dcp) {
1089                         ndi_dc_freehdl(dcp);
1090                 }
1091 
1092                 return (ENOTTY);
1093         }
1094 
1095         /* should not happen, just in case */
1096         if (hwahcp->hwahc_dev_state == USB_DEV_SUSPENDED) {
1097                 mutex_exit(&hwahcp->hwahc_mutex);
1098                 if (dcp) {
1099                         ndi_dc_freehdl(dcp);
1100                 }
1101 
1102                 return (EIO);
1103         }
1104 
1105         mutex_exit(&hwahcp->hwahc_mutex);
1106 
1107         ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
1108         ndi_devi_enter(rh_dip, &rh_circ);
1109         ndi_devi_enter(hwahcp->hwahc_dip, &circ);
1110 
1111         mutex_enter(&hwahcp->hwahc_mutex);
1112 
1113         switch (cmd) {
1114         case DEVCTL_AP_DISCONNECT:
1115                 /* TODO: not supported now */
1116                 rv = EIO;
1117                 break;
1118         case DEVCTL_AP_UNCONFIGURE:
1119                 /* TODO: not supported now */
1120                 rv = EIO;
1121                 break;
1122         case DEVCTL_AP_CONFIGURE:
1123                 /* TODO: not supported now */
1124                 rv = EIO;
1125                 break;
1126         case DEVCTL_AP_GETSTATE:
1127                 switch (hwahc_cfgadm_state(hwahcp, port)) {
1128                 case HWAHC_CFGADM_DISCONNECTED:
1129                         /* port previously 'disconnected' by cfgadm */
1130                         ap_state.ap_rstate = AP_RSTATE_DISCONNECTED;
1131                         ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED;
1132                         ap_state.ap_condition = AP_COND_OK;
1133 
1134                         break;
1135                 case HWAHC_CFGADM_UNCONFIGURED:
1136                         ap_state.ap_rstate = AP_RSTATE_CONNECTED;
1137                         ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED;
1138                         ap_state.ap_condition = AP_COND_OK;
1139 
1140                         break;
1141                 case HWAHC_CFGADM_CONFIGURED:
1142                         ap_state.ap_rstate = AP_RSTATE_CONNECTED;
1143                         ap_state.ap_ostate = AP_OSTATE_CONFIGURED;
1144                         ap_state.ap_condition = AP_COND_OK;
1145 
1146                         break;
1147                 case HWAHC_CFGADM_STILL_REFERENCED:
1148                         ap_state.ap_rstate = AP_RSTATE_EMPTY;
1149                         ap_state.ap_ostate = AP_OSTATE_CONFIGURED;
1150                         ap_state.ap_condition = AP_COND_UNUSABLE;
1151 
1152                         break;
1153                 case HWAHC_CFGADM_EMPTY:
1154                 default:
1155                         ap_state.ap_rstate = AP_RSTATE_EMPTY;
1156                         ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED;
1157                         ap_state.ap_condition = AP_COND_OK;
1158 
1159                         break;
1160                 }
1161 
1162                 ap_state.ap_last_change = (time_t)-1;
1163                 ap_state.ap_error_code = 0;
1164                 ap_state.ap_in_transition = 0;
1165 
1166                 USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
1167                     "DEVCTL_AP_GETSTATE: "
1168                     "ostate=0x%x, rstate=0x%x, condition=0x%x",
1169                     ap_state.ap_ostate,
1170                     ap_state.ap_rstate, ap_state.ap_condition);
1171 
1172                 /* copy the return-AP-state information to the user space */
1173                 if (ndi_dc_return_ap_state(&ap_state, dcp) != NDI_SUCCESS) {
1174                         rv = EFAULT;
1175                 }
1176 
1177                 break;
1178         case DEVCTL_AP_CONTROL:
1179         {
1180                 /*
1181                  * Generic devctl for hardware-specific functionality.
1182                  * For list of sub-commands see hubd_impl.h
1183                  */
1184                 hubd_ioctl_data_t       ioc;    /* for 64 byte copies */
1185 
1186                 /* copy user ioctl data in first */
1187 #ifdef _MULTI_DATAMODEL
1188                 if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
1189                         hubd_ioctl_data_32_t ioc32;
1190 
1191                         if (ddi_copyin((void *)arg, (void *)&ioc32,
1192                             sizeof (ioc32), mode) != 0) {
1193                                 rv = EFAULT;
1194 
1195                                 break;
1196                         }
1197                         ioc.cmd         = (uint_t)ioc32.cmd;
1198                         ioc.port        = (uint_t)ioc32.port;
1199                         ioc.get_size    = (uint_t)ioc32.get_size;
1200                         ioc.buf         = (caddr_t)(uintptr_t)ioc32.buf;
1201                         ioc.bufsiz      = (uint_t)ioc32.bufsiz;
1202                         ioc.misc_arg    = (uint_t)ioc32.misc_arg;
1203                 } else
1204 #endif /* _MULTI_DATAMODEL */
1205                 if (ddi_copyin((void *)arg, (void *)&ioc, sizeof (ioc),
1206                     mode) != 0) {
1207                         rv = EFAULT;
1208 
1209                         break;
1210                 }
1211 
1212                 USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
1213                     "DEVCTL_AP_CONTROL: ioc: cmd=0x%x port=%d get_size=%d"
1214                     "\n\tbuf=0x%p, bufsiz=%d,  misc_arg=%d", ioc.cmd,
1215                     ioc.port, ioc.get_size, (void *) ioc.buf, ioc.bufsiz,
1216                     ioc.misc_arg);
1217 
1218                 /*
1219                  * To avoid BE/LE and 32/64 issues, a get_size always
1220                  * returns a 32-bit number.
1221                  */
1222                 if (ioc.get_size != 0 && ioc.bufsiz != (sizeof (uint32_t))) {
1223                         rv = EINVAL;
1224 
1225                         break;
1226                 }
1227 
1228                 switch (ioc.cmd) {
1229                 case USB_DESCR_TYPE_DEV:
1230                         msg = "DEVCTL_AP_CONTROL: GET_DEVICE_DESC";
1231                         if (ioc.get_size) {
1232                                 /* uint32 so this works 32/64 */
1233                                 uint32_t size = sizeof (usb_dev_descr_t);
1234 
1235                                 if (ddi_copyout((void *)&size, ioc.buf,
1236                                     ioc.bufsiz, mode) != 0) {
1237                                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1238                                             hwahcp->hwahc_log_handle,
1239                                             "%s: get_size copyout failed", msg);
1240                                         rv = EIO;
1241 
1242                                         break;
1243                                 }
1244                         } else {        /* send out the actual descr */
1245                                 usb_dev_descr_t *dev_descrp;
1246 
1247                                 /* check child_dip */
1248                                 if ((child_dip = hwahc_get_child_dip(hwahcp,
1249                                     ioc.port)) == NULL) {
1250                                         rv = EINVAL;
1251 
1252                                         break;
1253                                 }
1254 
1255                                 dev_descrp = usb_get_dev_descr(child_dip);
1256                                 if (ioc.bufsiz != sizeof (*dev_descrp)) {
1257                                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1258                                             hwahcp->hwahc_log_handle,
1259                                             "%s: bufsize passed (%d) != sizeof "
1260                                             "usba_device_descr_t (%d)", msg,
1261                                             ioc.bufsiz, dev_descrp->bLength);
1262                                         rv = EINVAL;
1263 
1264                                         break;
1265                                 }
1266 
1267                                 if (ddi_copyout((void *)dev_descrp,
1268                                     ioc.buf, ioc.bufsiz, mode) != 0) {
1269                                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1270                                             hwahcp->hwahc_log_handle,
1271                                             "%s: copyout failed.", msg);
1272                                         rv = EIO;
1273 
1274                                         break;
1275                                 }
1276                         }
1277                         break;
1278                 case USB_DESCR_TYPE_CFG:
1279                 {
1280                         usba_device_t   *child_ud = NULL;
1281                         uint32_t        idx = ioc.misc_arg;
1282                         uint32_t        cfg_len = 0;
1283 
1284                         if ((child_dip =
1285                             hwahc_get_child_dip(hwahcp, ioc.port)) == NULL) {
1286                                 rv = EINVAL;
1287 
1288                                 break;
1289                         }
1290                         child_ud = usba_get_usba_device(child_dip);
1291                         cfg_len = (uint32_t)child_ud->usb_cfg_array_len[idx];
1292 
1293                         msg = "DEVCTL_AP_CONTROL: GET_CONFIG_DESC";
1294                         if (ioc.get_size) {
1295                                 if (ddi_copyout((void *)&cfg_len, ioc.buf,
1296                                     ioc.bufsiz, mode) != 0) {
1297                                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1298                                             hwahcp->hwahc_log_handle,
1299                                             "%s: get_size copyout failed", msg);
1300                                         rv = EIO;
1301 
1302                                         break;
1303                                 }
1304                         } else {        /* send out the actual descr */
1305                                 uchar_t *cfg_descr =
1306                                     child_ud->usb_cfg_array[idx];
1307 
1308                                 if (ioc.bufsiz != cfg_len) {
1309                                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1310                                             hwahcp->hwahc_log_handle,
1311                                             "%s: bufsize passed (%d) != size "
1312                                             "of cfg_descr (%d)", msg,
1313                                             ioc.bufsiz, cfg_len);
1314                                         rv = EINVAL;
1315 
1316                                         break;
1317                                 }
1318 
1319                                 if (ddi_copyout((void *)cfg_descr,
1320                                     ioc.buf, ioc.bufsiz, mode) != 0) {
1321                                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1322                                             hwahcp->hwahc_log_handle,
1323                                             "%s: copyout failed.", msg);
1324                                         rv = EIO;
1325 
1326                                         break;
1327                                 }
1328                         }
1329                         break;
1330                 }
1331                 case USB_DESCR_TYPE_STRING:
1332                 {
1333                         char            *str;
1334                         uint32_t        size;
1335                         usba_device_t   *usba_device;
1336 
1337                         msg = "DEVCTL_AP_CONTROL: GET_STRING_DESCR";
1338                         USB_DPRINTF_L4(PRINT_MASK_CBOPS,
1339                             hwahcp->hwahc_log_handle,
1340                             "%s: string request: %d", msg, ioc.misc_arg);
1341 
1342                         /* recheck */
1343                         if ((child_dip =
1344                             hwahc_get_child_dip(hwahcp, ioc.port)) == NULL) {
1345                                 rv = EINVAL;
1346 
1347                                 break;
1348                         }
1349                         usba_device = usba_get_usba_device(child_dip);
1350 
1351                         switch (ioc.misc_arg) {
1352                         case HUBD_MFG_STR:
1353                                 str = usba_device->usb_mfg_str;
1354 
1355                                 break;
1356                         case HUBD_PRODUCT_STR:
1357                                 str = usba_device->usb_product_str;
1358 
1359                                 break;
1360                         case HUBD_SERIALNO_STR:
1361                                 str = usba_device->usb_serialno_str;
1362 
1363                                 break;
1364                         case HUBD_CFG_DESCR_STR:
1365                                 mutex_enter(&usba_device->usb_mutex);
1366                                 str = usba_device->usb_cfg_str_descr[
1367                                     usba_device->usb_active_cfg_ndx];
1368                                 mutex_exit(&usba_device->usb_mutex);
1369 
1370                                 break;
1371                         default:
1372                                 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1373                                     hwahcp->hwahc_log_handle,
1374                                     "%s: Invalid string request", msg);
1375                                 rv = EINVAL;
1376 
1377                                 break;
1378                         } /* end of switch */
1379 
1380                         if (rv != 0) {
1381 
1382                                 break;
1383                         }
1384 
1385                         size = (str != NULL) ? strlen(str) + 1 : 0;
1386                         if (ioc.get_size) {
1387                                 if (ddi_copyout((void *)&size, ioc.buf,
1388                                     ioc.bufsiz, mode) != 0) {
1389                                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1390                                             hwahcp->hwahc_log_handle,
1391                                             "%s: copyout of size failed.", msg);
1392                                         rv = EIO;
1393 
1394                                         break;
1395                                 }
1396                         } else {
1397                                 if (size == 0) {
1398                                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1399                                             hwahcp->hwahc_log_handle,
1400                                             "%s: String is NULL", msg);
1401                                         rv = EINVAL;
1402 
1403                                         break;
1404                                 }
1405 
1406                                 if (ioc.bufsiz != size) {
1407                                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1408                                             hwahcp->hwahc_log_handle,
1409                                             "%s: string buf size wrong", msg);
1410                                         rv = EINVAL;
1411 
1412                                         break;
1413                                 }
1414 
1415                                 if (ddi_copyout((void *)str, ioc.buf,
1416                                     ioc.bufsiz, mode) != 0) {
1417                                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1418                                             hwahcp->hwahc_log_handle,
1419                                             "%s: copyout failed.", msg);
1420                                         rv = EIO;
1421 
1422                                         break;
1423                                 }
1424                         }
1425                         break;
1426                 }
1427                 case HUBD_GET_CFGADM_NAME:
1428                 {
1429                         uint32_t   name_len;
1430                         const char *name;
1431 
1432                         /* recheck */
1433                         if ((child_dip =
1434                             hwahc_get_child_dip(hwahcp, ioc.port)) == NULL) {
1435                                 rv = EINVAL;
1436 
1437                                 break;
1438                         }
1439                         name = ddi_node_name(child_dip);
1440                         if (name == NULL) {
1441                                 name = "unsupported";
1442                         }
1443                         name_len = strlen(name) + 1;
1444 
1445                         msg = "DEVCTL_AP_CONTROL: HUBD_GET_CFGADM_NAME";
1446                         USB_DPRINTF_L4(PRINT_MASK_CBOPS,
1447                             hwahcp->hwahc_log_handle,
1448                             "%s: name=%s name_len=%d", msg, name, name_len);
1449 
1450                         if (ioc.get_size) {
1451                                 if (ddi_copyout((void *)&name_len,
1452                                     ioc.buf, ioc.bufsiz, mode) != 0) {
1453                                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1454                                             hwahcp->hwahc_log_handle,
1455                                             "%s: copyout of size failed", msg);
1456                                         rv = EIO;
1457 
1458                                         break;
1459                                 }
1460                         } else {
1461                                 if (ioc.bufsiz != name_len) {
1462                                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1463                                             hwahcp->hwahc_log_handle,
1464                                             "%s: string buf length wrong", msg);
1465                                         rv = EINVAL;
1466 
1467                                         break;
1468                                 }
1469 
1470                                 if (ddi_copyout((void *)name, ioc.buf,
1471                                     ioc.bufsiz, mode) != 0) {
1472                                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1473                                             hwahcp->hwahc_log_handle,
1474                                             "%s: copyout failed.", msg);
1475                                         rv = EIO;
1476 
1477                                         break;
1478                                 }
1479                         }
1480 
1481                         break;
1482                 }
1483 
1484                 /*
1485                  * Return the config index for the currently-configured
1486                  * configuration.
1487                  */
1488                 case HUBD_GET_CURRENT_CONFIG:
1489                 {
1490                         uint_t          config_index;
1491                         uint32_t        size = sizeof (config_index);
1492                         usba_device_t   *usba_device;
1493 
1494                         msg = "DEVCTL_AP_CONTROL: GET_CURRENT_CONFIG";
1495                         USB_DPRINTF_L4(PRINT_MASK_CBOPS,
1496                             hwahcp->hwahc_log_handle, "%s", msg);
1497 
1498                         /*
1499                          * Return the config index for the configuration
1500                          * currently in use.
1501                          * Recheck if child_dip exists
1502                          */
1503                         if ((child_dip =
1504                             hwahc_get_child_dip(hwahcp, ioc.port)) == NULL) {
1505                                 rv = EINVAL;
1506 
1507                                 break;
1508                         }
1509 
1510                         usba_device = usba_get_usba_device(child_dip);
1511                         mutex_enter(&usba_device->usb_mutex);
1512                         config_index = usba_device->usb_active_cfg_ndx;
1513                         mutex_exit(&usba_device->usb_mutex);
1514 
1515                         if (ioc.get_size) {
1516                                 if (ddi_copyout((void *)&size,
1517                                     ioc.buf, ioc.bufsiz, mode) != 0) {
1518                                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1519                                             hwahcp->hwahc_log_handle,
1520                                             "%s: copyout of size failed.", msg);
1521                                         rv = EIO;
1522 
1523                                         break;
1524                                 }
1525                         } else {
1526                                 if (ioc.bufsiz != size) {
1527                                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1528                                             hwahcp->hwahc_log_handle,
1529                                             "%s: buffer size wrong", msg);
1530                                         rv = EINVAL;
1531 
1532                                         break;
1533                                 }
1534                                 if (ddi_copyout((void *)&config_index,
1535                                     ioc.buf, ioc.bufsiz, mode) != 0) {
1536                                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1537                                             hwahcp->hwahc_log_handle,
1538                                             "%s: copyout failed", msg);
1539                                         rv = EIO;
1540                                 }
1541                         }
1542 
1543                         break;
1544                 }
1545                 case HUBD_GET_DEVICE_PATH:
1546                 {
1547                         char            *path;
1548                         uint32_t        size;
1549 
1550                         msg = "DEVCTL_AP_CONTROL: GET_DEVICE_PATH";
1551                         USB_DPRINTF_L4(PRINT_MASK_CBOPS,
1552                             hwahcp->hwahc_log_handle, "%s", msg);
1553 
1554                         /* Recheck if child_dip exists */
1555                         if ((child_dip =
1556                             hwahc_get_child_dip(hwahcp, ioc.port)) == NULL) {
1557                                 rv = EINVAL;
1558 
1559                                 break;
1560                         }
1561 
1562                         /* ddi_pathname doesn't supply /devices, so we do. */
1563                         path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
1564                         (void) strcpy(path, "/devices");
1565                         (void) ddi_pathname(child_dip, path + strlen(path));
1566                         size = strlen(path) + 1;
1567 
1568                         USB_DPRINTF_L4(PRINT_MASK_CBOPS,
1569                             hwahcp->hwahc_log_handle,
1570                             "%s: device path=%s  size=%d", msg, path, size);
1571 
1572                         if (ioc.get_size) {
1573                                 if (ddi_copyout((void *)&size,
1574                                     ioc.buf, ioc.bufsiz, mode) != 0) {
1575 
1576                                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1577                                             hwahcp->hwahc_log_handle,
1578                                             "%s: copyout of size failed.", msg);
1579                                         rv = EIO;
1580                                 }
1581                         } else {
1582                                 if (ioc.bufsiz != size) {
1583                                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1584                                             hwahcp->hwahc_log_handle,
1585                                             "%s: buffer wrong size.", msg);
1586                                         rv = EINVAL;
1587                                 } else if (ddi_copyout((void *)path,
1588                                     ioc.buf, ioc.bufsiz, mode) != 0) {
1589                                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1590                                             hwahcp->hwahc_log_handle,
1591                                             "%s: copyout failed.", msg);
1592                                         rv = EIO;
1593                                 }
1594                         }
1595                         kmem_free(path, MAXPATHLEN);
1596 
1597                         break;
1598                 }
1599                 case HUBD_REFRESH_DEVDB:
1600                         msg = "DEVCTL_AP_CONTROL: HUBD_REFRESH_DEVDB";
1601                         USB_DPRINTF_L4(PRINT_MASK_CBOPS,
1602                             hwahcp->hwahc_log_handle, "%s", msg);
1603 
1604                         if ((rv = usba_devdb_refresh()) != USB_SUCCESS) {
1605                                 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1606                                     hwahcp->hwahc_log_handle,
1607                                     "%s: Failed: %d", msg, rv);
1608                                 rv = EIO;
1609                         }
1610 
1611                         break;
1612                 default:
1613                         rv = ENOTSUP;
1614                 }       /* end switch */
1615 
1616                 break;
1617         }
1618 
1619         default:
1620                 rv = ENOTTY;
1621         }
1622 
1623         if (dcp) {
1624                 ndi_dc_freehdl(dcp);
1625         }
1626 
1627         mutex_exit(&hwahcp->hwahc_mutex);
1628 
1629         ndi_devi_exit(hwahcp->hwahc_dip, circ);
1630         ndi_devi_exit(rh_dip, rh_circ);
1631         ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ);
1632 
1633         return (rv);
1634 }
1635 
1636 /* update CHID for the hc driver, return 0 on success */
1637 static int
1638 hwahc_set_chid(hwahc_state_t *hwahcp, uint8_t *chid)
1639 {
1640         wusb_hc_data_t  *hc_data = &hwahcp->hwahc_hc_data;
1641 
1642         ASSERT(!mutex_owned(&hc_data->hc_mutex));
1643 
1644         /* same as the old CHID, return success */
1645         if (memcmp(chid, hc_data->hc_chid, 16) == 0) {
1646 
1647                 return (0);
1648         }
1649 
1650         /*
1651          * stop hw from working before updating CHID
1652          * this may not be necessary but so far we don't know
1653          * other ways to do it safely
1654          */
1655         if (hwahcp->hwahc_hw_state == HWAHC_HW_STARTED) {
1656                 /* use final_stop to fully stop the hwa */
1657                 if (hwahc_hc_final_stop(hwahcp) != USB_SUCCESS) {
1658 
1659                         return (EIO);
1660                 }
1661 
1662                 mutex_enter(&hc_data->hc_mutex);
1663                 (void) memcpy(hc_data->hc_chid, chid, 16);
1664                 mutex_exit(&hc_data->hc_mutex);
1665 
1666                 /* restart the host */
1667                 if (hwahc_hc_initial_start(hwahcp) != USB_SUCCESS) {
1668 
1669                         return (EIO);
1670                 }
1671 
1672                 return (0);
1673         }
1674 
1675         /* hc is stopped or partially stopped, simply update */
1676         mutex_enter(&hc_data->hc_mutex);
1677         (void) memcpy(hc_data->hc_chid, chid, 16);
1678         mutex_exit(&hc_data->hc_mutex);
1679 
1680         return (0);
1681 }
1682 
1683 /*
1684  * wusbadm ioctl support
1685  */
1686 /* ARGSUSED */
1687 static int
1688 hwahc_wusb_ioctl(hwahc_state_t *hwahcp, int cmd, intptr_t arg,
1689         int mode, cred_t *credp, int *rvalp)
1690 {
1691         int             rv = 0;
1692         wusb_hc_data_t  *hc_data = &hwahcp->hwahc_hc_data;
1693 
1694         if (drv_priv(credp) != 0) {
1695                 USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
1696                     "hwahc_wusb_ioctl: user must have SYS_DEVICE privilege,"
1697                     "cmd=%x", cmd);
1698 
1699                 return (EPERM);
1700         }
1701 
1702         mutex_enter(&hwahcp->hwahc_mutex);
1703 
1704         switch (cmd) {
1705         case WUSB_HC_GET_DSTATE: /* Get device state: wusbadm list */
1706         {
1707                 wusb_hc_get_dstate_t    state;
1708                 usb_port_t              port = 0;
1709 
1710                 if (ddi_copyin((void *)arg, (void *)&state, sizeof (state),
1711                     mode) != 0) {
1712                         rv = EFAULT;
1713 
1714                         break;
1715                 }
1716 
1717                 mutex_enter(&hc_data->hc_mutex);
1718 
1719                 if (wusb_hc_is_dev_connected(hc_data, &state.cdid[0], &port)) {
1720                         state.state = hc_data->hc_dev_infos[port]->wdev_state;
1721                 } else {
1722                         /* cdid not found */
1723                         state.state = WUSB_STATE_UNCONNTED;
1724                 }
1725 
1726                 USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
1727                     "hwahc_wusb_ioctl: hc_data=%p, port = %d, state=%d",
1728                     (void *) hc_data, port, state.state);
1729 
1730                 mutex_exit(&hc_data->hc_mutex);
1731 
1732                 if (state.state == WUSB_STATE_CONFIGURED) {
1733                         /* Get the bind device node name of this child */
1734                         (void) memset(state.nodename, 0, MAX_USB_NODENAME);
1735                         (void) snprintf(state.nodename, MAX_USB_NODENAME, "%s",
1736                             ddi_node_name(hwahc_get_child_dip(hwahcp, port)));
1737 
1738                         USB_DPRINTF_L3(PRINT_MASK_CBOPS,
1739                             hwahcp->hwahc_log_handle,
1740                             "WUSB_HC_GET_DSTATE: nodename %s", state.nodename);
1741                 }
1742 
1743                 if (ddi_copyout((void *)&state, (void *)arg,
1744                     sizeof (state), mode) != 0) {
1745                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1746                             hwahcp->hwahc_log_handle,
1747                             "WUSB_HC_GET_DSTATE: copyout failed");
1748                         rv = EIO;
1749                 }
1750 
1751                 break;
1752         }
1753 
1754         case WUSB_HC_GET_MAC_ADDR: /* Get host MAC addr */
1755         {
1756                 uint8_t         mac_addr[6];
1757 
1758                 bzero(mac_addr, 6);
1759 
1760                 /*
1761                  * get UWB 48-bit mac address
1762                  * Section 8.6.2.2.
1763                  */
1764                 if (uwb_get_mac_addr(hwahcp->hwahc_dip, mac_addr) !=
1765                     USB_SUCCESS) {
1766                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1767                             hwahcp->hwahc_log_handle,
1768                             "WUSB_HC_GET_MAC_ADDR: get mac failed");
1769                         rv = EIO;
1770 
1771                         break;
1772                 }
1773 
1774                 if (ddi_copyout((void *)mac_addr, (void *)arg,
1775                     6, mode) != 0) {
1776                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1777                             hwahcp->hwahc_log_handle,
1778                             "WUSB_HC_GET_MAC_ADDR: copyout failed");
1779                         rv = EIO;
1780                 }
1781 
1782                 break;
1783         }
1784         case WUSB_HC_ADD_CC:
1785         {
1786         /*
1787          * add a new device CC to host's list: wusbadm associate
1788          * Or, the application can pass in a fake CC with only CHID set
1789          * to set the host's CHID.
1790          */
1791                 wusb_hc_cc_list_t       *cc_list;
1792 
1793                 cc_list = kmem_zalloc(sizeof (wusb_hc_cc_list_t), KM_SLEEP);
1794 
1795                 if (ddi_copyin((void *)arg, (void *)&cc_list->cc,
1796                     sizeof (wusb_cc_t), mode) != 0) {
1797                         rv = EFAULT;
1798                         kmem_free(cc_list, sizeof (wusb_hc_cc_list_t));
1799 
1800                         break;
1801                 }
1802 
1803                 /* update CHID only when cc list is empty */
1804                 mutex_enter(&hc_data->hc_mutex);
1805                 if (hc_data->hc_cc_list == NULL) {
1806                         mutex_exit(&hc_data->hc_mutex);
1807 
1808                         if ((rv = hwahc_set_chid(hwahcp,
1809                             cc_list->cc.CHID)) != 0) {
1810                                 kmem_free(cc_list, sizeof (wusb_hc_cc_list_t));
1811 
1812                                 break;
1813                         }
1814 
1815                         mutex_enter(&hc_data->hc_mutex);
1816                 } else {
1817                         /* fail if the CHID in the new CC does not match */
1818                         if (memcmp(cc_list->cc.CHID, hc_data->hc_chid,
1819                             16) != 0) {
1820                                 rv = EINVAL;
1821                                 kmem_free(cc_list, sizeof (wusb_hc_cc_list_t));
1822 
1823                                 mutex_exit(&hc_data->hc_mutex);
1824                                 break;
1825                         }
1826                 }
1827                 cc_list->next = NULL;
1828 
1829                 wusb_hc_add_cc(&hc_data->hc_cc_list, cc_list);
1830                 mutex_exit(&hc_data->hc_mutex);
1831 
1832                 break;
1833         }
1834         case WUSB_HC_REM_CC:
1835         {
1836                 wusb_cc_t       cc;
1837                 usb_port_t      port;
1838 
1839                 if (ddi_copyin((void *)arg, (void *)&cc, sizeof (wusb_cc_t),
1840                     mode) != 0) {
1841                         rv = EFAULT;
1842 
1843                         break;
1844                 }
1845 
1846                 /* check if the CHID in the CC matches */
1847                 if (memcmp(cc.CHID, hc_data->hc_chid, 16) != 0) {
1848                         rv = EINVAL;
1849 
1850                         break;
1851                 }
1852 
1853                 /* if the device is connected, disconnect it first */
1854                 mutex_enter(&hc_data->hc_mutex);
1855                 if (wusb_hc_is_dev_connected(hc_data, cc.CDID, &port)) {
1856                         mutex_exit(&hc_data->hc_mutex);
1857                         mutex_exit(&hwahcp->hwahc_mutex);
1858                         /*
1859                          * clean up host side state, device not
1860                          * really disconnected. But user can safely remove
1861                          * the device now.
1862                          */
1863                         (void) hwahc_destroy_child(hc_data->hc_dip, port);
1864                         mutex_enter(&hwahcp->hwahc_mutex);
1865                         mutex_enter(&hc_data->hc_mutex);
1866                 }
1867 
1868                 wusb_hc_rem_cc(&hc_data->hc_cc_list, &cc);
1869                 mutex_exit(&hc_data->hc_mutex);
1870 
1871                 break;
1872         }
1873         case WUSB_HC_SET_CHANNEL: /* for debug purpose */
1874         {
1875                 uint8_t channel;
1876 
1877                 channel = (uint8_t)arg;
1878 
1879                 if (hwahcp->hwahc_hc_data.hc_channel == channel) {
1880                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1881                             hwahcp->hwahc_log_handle,
1882                             "WUSB_HC_SET_CHANNEL ioctl: same as existing");
1883 
1884                         break;
1885                 }
1886 
1887                 if (hwahcp->hwahc_hw_state != HWAHC_HW_STOPPED) {
1888                         /* beacon is already started, stop it first */
1889                         if (uwb_stop_beacon(hwahcp->hwahc_dip) != USB_SUCCESS) {
1890                                 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1891                                     hwahcp->hwahc_log_handle,
1892                                     "WUSB_HC_SET_CHANNEL ioctl: "
1893                                     "stop beacon failed");
1894                                 rv = EIO;
1895 
1896                                 break;
1897                         }
1898                         /* update channel number */
1899                         hwahcp->hwahc_hc_data.hc_channel = channel;
1900                         /* restart beacon on the new channel */
1901                         if (uwb_start_beacon(hwahcp->hwahc_dip,
1902                             channel) != USB_SUCCESS) {
1903                                 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1904                                     hwahcp->hwahc_log_handle,
1905                                     "WUSB_HC_SET_CHANNEL ioctl: "
1906                                     "restart beacon failed");
1907                                 rv = EIO;
1908                         }
1909 
1910                         break;
1911                 }
1912 
1913                 /* beacon is not started, simply update channel number */
1914                 hwahcp->hwahc_hc_data.hc_channel = channel;
1915 
1916                 break;
1917         }
1918         case WUSB_HC_START:
1919         {
1920                 int     flag;
1921 
1922 
1923                 flag = (int)arg;
1924 
1925                 if (hwahcp->hwahc_hw_state == HWAHC_HW_STARTED) {
1926                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1927                             hwahcp->hwahc_log_handle,
1928                             "WUSB_HC_START ioctl: already started");
1929 
1930                         break;
1931                 }
1932 
1933                 /*
1934                  * now we start hc only when the cc list is not NULL
1935                  * this limitation may be removed if we support
1936                  * numeric association, but CHID needs to be set
1937                  * in advance for the hc to work
1938                  */
1939                 mutex_enter(&hc_data->hc_mutex);
1940                 if (hc_data->hc_cc_list == NULL) {
1941                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1942                             hwahcp->hwahc_log_handle,
1943                             "WUSB_HC_START ioctl: cc list not inited");
1944                         rv = EINVAL;
1945 
1946                         mutex_exit(&hc_data->hc_mutex);
1947 
1948                         break;
1949                 }
1950                 mutex_exit(&hc_data->hc_mutex);
1951 
1952                 /* cannot be both */
1953                 if ((flag & WUSB_HC_INITIAL_START) && (flag &
1954                     WUSB_HC_CHANNEL_START)) {
1955                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1956                             hwahcp->hwahc_log_handle,
1957                             "WUSB_HC_START ioctl: flag cannot coexist");
1958                         rv = EINVAL;
1959 
1960                         break;
1961                 }
1962 
1963                 /*
1964                  * init Mac layer 16-bit dev addr. it is important for
1965                  * authentication. It'd be better to let UWB provide
1966                  * this address.
1967                  */
1968                 mutex_enter(&hc_data->hc_mutex);
1969                 if (hc_data->hc_addr == 0) {
1970                         uint16_t dev_addr = HWAHC_DEV_ADDR_BASE +
1971                             ddi_get_instance(hwahcp->hwahc_dip);
1972 
1973                         mutex_exit(&hc_data->hc_mutex);
1974                         /* set UWB 16-bit dev address */
1975                         if (uwb_set_dev_addr(hwahcp->hwahc_dip,
1976                             dev_addr) != USB_SUCCESS) {
1977                                 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1978                                     hwahcp->hwahc_log_handle,
1979                                     "WUSB_HC_START ioctl: set dev addr failed");
1980                                 rv = EIO;
1981 
1982                                 break;
1983                         }
1984 
1985                         /* verify the dev addr is set correctly */
1986                         if (uwb_get_dev_addr(hwahcp->hwahc_dip,
1987                             &dev_addr) != USB_SUCCESS) {
1988                                 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1989                                     hwahcp->hwahc_log_handle,
1990                                     "WUSB_HC_START ioctl: get dev addr failed");
1991                                 rv = EIO;
1992 
1993                                 break;
1994                         }
1995 
1996                         mutex_enter(&hc_data->hc_mutex);
1997                         hc_data->hc_addr = dev_addr;
1998                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1999                             hwahcp->hwahc_log_handle,
2000                             "host dev addr = 0x%x", dev_addr);
2001                 }
2002                 mutex_exit(&hc_data->hc_mutex);
2003 
2004                 /* start functions of wusb host */
2005                 if ((flag & WUSB_HC_INITIAL_START) &&
2006                     (hwahcp->hwahc_hw_state == HWAHC_HW_STOPPED)) {
2007                         if (hwahc_hc_initial_start(hwahcp) != USB_SUCCESS) {
2008                                 rv = EIO;
2009                         }
2010                 } else if ((flag & WUSB_HC_CHANNEL_START) &&
2011                     (hwahcp->hwahc_hw_state == HWAHC_HW_CH_STOPPED)) {
2012                         if (hwahc_hc_channel_start(hwahcp) != USB_SUCCESS) {
2013                                 rv = EIO;
2014                         }
2015                 } else {
2016                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
2017                             hwahcp->hwahc_log_handle,
2018                             "WUSB_HC_START ioctl: unknown flag (%d) or "
2019                             "state (%d)", flag, hwahcp->hwahc_hw_state);
2020                         rv = EINVAL;
2021                 }
2022 
2023                 break;
2024         }
2025         case WUSB_HC_STOP:
2026         {
2027                 int     flag;
2028 
2029                 flag = (int)arg;
2030 
2031                 /* cannot be both */
2032                 if ((flag & WUSB_HC_FINAL_STOP) && (flag &
2033                     WUSB_HC_CHANNEL_STOP)) {
2034                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
2035                             hwahcp->hwahc_log_handle,
2036                             "WUSB_HC_STOP ioctl: flag cannot coexist");
2037                         rv = EINVAL;
2038 
2039                         break;
2040                 }
2041 
2042                 if (flag & WUSB_HC_FINAL_STOP) {
2043                         if (hwahc_hc_final_stop(hwahcp) != USB_SUCCESS) {
2044                                 rv = EIO;
2045                         }
2046                 } else if (flag & WUSB_HC_CHANNEL_STOP) {
2047                         if (hwahc_hc_channel_stop(hwahcp) != USB_SUCCESS) {
2048                                 rv = EIO;
2049                         }
2050                 } else {
2051                         /* must be one of the STOP flag */
2052                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
2053                             hwahcp->hwahc_log_handle,
2054                             "WUSB_HC_STOP ioctl: invalid flag = %d", flag);
2055                         rv = EINVAL;
2056                 }
2057 
2058                 /* REM_ALL_CC flag is optional */
2059                 if ((rv == 0) && (flag & WUSB_HC_REM_ALL_CC)) {
2060                         mutex_enter(&hc_data->hc_mutex);
2061                         if (hc_data->hc_cc_list) {
2062                                 wusb_hc_free_cc_list(hc_data->hc_cc_list);
2063                                 hc_data->hc_cc_list = NULL;
2064                         }
2065                         mutex_exit(&hc_data->hc_mutex);
2066                 }
2067 
2068                 break;
2069         }
2070         case WUSB_HC_GET_HSTATE:
2071         {
2072                 int     state;
2073 
2074                 if (hwahcp->hwahc_dev_state == USB_DEV_DISCONNECTED) {
2075                         state = WUSB_HC_DISCONNTED;
2076                 } else {
2077                         switch (hwahcp->hwahc_hw_state) {
2078                         case HWAHC_HW_STOPPED:
2079                                 state = WUSB_HC_STOPPED;
2080                                 break;
2081                         case HWAHC_HW_STARTED:
2082                                 state = WUSB_HC_STARTED;
2083                                 break;
2084                         case HWAHC_HW_CH_STOPPED:
2085                                 /*
2086                                  * app can mark the hwa as disabled
2087                                  * for this state
2088                                  */
2089                                 state = WUSB_HC_CH_STOPPED;
2090                                 break;
2091                         }
2092                 }
2093 
2094                 if (ddi_copyout((void *)&state, (void *)arg,
2095                     sizeof (int), mode) != 0) {
2096                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
2097                             hwahcp->hwahc_log_handle,
2098                             "WUSB_HC_GET_HSTATE: copyout failed");
2099                         rv = EIO;
2100                 }
2101 
2102                 break;
2103         }
2104         default:
2105                 USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
2106                     "hwahc_ioctl: unsupported command");
2107 
2108                 rv = ENOTSUP;
2109         }
2110         mutex_exit(&hwahcp->hwahc_mutex);
2111 
2112         return (rv);
2113 }
2114 
2115 static int
2116 hwahc_ioctl(dev_t dev, int cmd, intptr_t arg,
2117         int mode, cred_t *credp, int *rvalp)
2118 {
2119         hwahc_state_t   *hwahcp;
2120         int             rval;
2121 
2122         if ((hwahcp = ddi_get_soft_state(hwahc_statep,
2123             HWAHC_MINOR_TO_INSTANCE(getminor(dev)))) == NULL) {
2124 
2125                 return (ENXIO);
2126         }
2127 
2128         USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
2129             "hwahc_ioctl: cmd=%x, arg=%lx, mode=%x, cred=%p, rval=%p dev=0x%lx",
2130             cmd, arg, mode, (void *) credp, (void *) rvalp, dev);
2131 
2132         if (IS_DEVCTL(cmd)) {
2133                 /* for cfgadm cmd support */
2134                 rval = hwahc_cfgadm_ioctl(hwahcp, cmd, arg, mode, credp, rvalp);
2135         } else {
2136                 /* for wusbadm cmd support */
2137                 rval = hwahc_wusb_ioctl(hwahcp, cmd, arg, mode, credp, rvalp);
2138         }
2139 
2140         return (rval);
2141 }
2142 
2143 /* return the port number corresponding the child dip */
2144 static usb_port_t
2145 hwahc_child_dip2port(hwahc_state_t *hwahcp, dev_info_t *dip)
2146 {
2147         usb_port_t      port;
2148         wusb_hc_data_t  *hc_data = &hwahcp->hwahc_hc_data;
2149 
2150         mutex_enter(&hc_data->hc_mutex);
2151         for (port = 1; port <= hc_data->hc_num_ports; port++) {
2152                 if (hc_data->hc_children_dips[port] == dip) {
2153 
2154                         break;
2155                 }
2156         }
2157         ASSERT(port <= hc_data->hc_num_ports);
2158         mutex_exit(&hc_data->hc_mutex);
2159 
2160         return (port);
2161 }
2162 
2163 /*
2164  * child post attach/detach notification
2165  */
2166 static void
2167 hwahc_post_attach(hwahc_state_t *hwahcp, dev_info_t *rdip,
2168         struct attachspec *as)
2169 {
2170         /* we don't need additional process for post-attach now */
2171         USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2172             "hwahc_post_attach: rdip = 0x%p result = %d", (void *) rdip,
2173             as->result);
2174 }
2175 
2176 static void
2177 hwahc_post_detach(hwahc_state_t *hwahcp, dev_info_t *rdip,
2178         struct detachspec *as)
2179 {
2180         /* we don't need additional process for post-detach now */
2181         USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2182             "hwahc_post_detach: rdip = 0x%p result = %d", (void *) rdip,
2183             as->result);
2184 }
2185 
2186 /*
2187  * bus ctl support.
2188  * To support different operations, such as a PreAttach preparation,
2189  * PostAttach operations. HWA only process the interested operations.
2190  * Other general ones are processed by usba_bus_ctl().
2191  */
2192 static int
2193 hwahc_bus_ctl(dev_info_t *dip, /* dip could be the parent */
2194         dev_info_t      *rdip, /* rdip is the dev node to be operated */
2195         ddi_ctl_enum_t  op,
2196         void            *arg,
2197         void            *result)
2198 {
2199         usba_device_t *usba_device = usba_get_usba_device(rdip);
2200         dev_info_t *hubdip = usba_device->usb_root_hub_dip;
2201         hwahc_state_t *hwahcp;
2202         struct attachspec *as;
2203         struct detachspec *ds;
2204 
2205         if ((hwahcp = ddi_get_soft_state(hwahc_statep,
2206             ddi_get_instance(dip))) == NULL) {
2207 
2208                 return (DDI_FAILURE);
2209         }
2210 
2211         USB_DPRINTF_L2(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2212             "hwahc_bus_ctl:\n\t"
2213             "dip = 0x%p, rdip = 0x%p, op = 0x%x, arg = 0x%p",
2214             (void *) dip, (void *) rdip, op, (void *) arg);
2215 
2216         switch (op) {
2217         case DDI_CTLOPS_ATTACH:
2218                 as = (struct attachspec *)arg;
2219 
2220                 switch (as->when) {
2221                 case DDI_PRE :
2222                         /* nothing to do basically */
2223                         USB_DPRINTF_L2(PRINT_MASK_EVENTS,
2224                             hwahcp->hwahc_log_handle,
2225                             "DDI_PRE DDI_CTLOPS_ATTACH");
2226                         break;
2227                 case DDI_POST :
2228                         hwahc_post_attach(hwahcp, rdip,
2229                             (struct attachspec *)arg);
2230                         break;
2231                 }
2232 
2233                 break;
2234         case DDI_CTLOPS_DETACH:
2235                 ds = (struct detachspec *)arg;
2236 
2237                 switch (ds->when) {
2238                 case DDI_PRE :
2239                         /* nothing to do basically */
2240                         USB_DPRINTF_L2(PRINT_MASK_EVENTS,
2241                             hwahcp->hwahc_log_handle,
2242                             "DDI_PRE DDI_CTLOPS_DETACH");
2243                         break;
2244                 case DDI_POST :
2245                         hwahc_post_detach(hwahcp, rdip,
2246                             (struct detachspec *)arg);
2247                         break;
2248                 }
2249 
2250                 break;
2251         case DDI_CTLOPS_REPORTDEV: /* the workhorse behind ddi_report_dev */
2252         {
2253                 char *name, compat_name[64];
2254 
2255                 if (usb_owns_device(rdip)) {
2256                         (void) snprintf(compat_name,
2257                             sizeof (compat_name),
2258                             "usb%x,%x",
2259                             usba_device->usb_dev_descr->idVendor,
2260                             usba_device->usb_dev_descr->idProduct);
2261                 } else if (usba_owns_ia(rdip)) {
2262                         (void) snprintf(compat_name,
2263                             sizeof (compat_name),
2264                             "usbia%x,%x.config%x.%x",
2265                             usba_device->usb_dev_descr->idVendor,
2266                             usba_device->usb_dev_descr->idProduct,
2267                             usba_device->usb_cfg_value,
2268                             usb_get_if_number(rdip));
2269                 } else {
2270                         (void) snprintf(compat_name,
2271                             sizeof (compat_name),
2272                             "usbif%x,%x.config%x.%x",
2273                             usba_device->usb_dev_descr->idVendor,
2274                             usba_device->usb_dev_descr->idProduct,
2275                             usba_device->usb_cfg_value,
2276                             usb_get_if_number(rdip));
2277                 }
2278 
2279                 cmn_err(CE_CONT,
2280                     "?USB %x.%x %s (%s) operating wirelessly with "
2281                     "HWA device: "
2282                     "%s@%s, %s%d at bus address %d\n",
2283                     (usba_device->usb_dev_descr->bcdUSB & 0xff00) >> 8,
2284                     usba_device->usb_dev_descr->bcdUSB & 0xff,
2285                     (usb_owns_device(rdip) ? "device" :
2286                     ((usba_owns_ia(rdip) ? "interface-association" :
2287                     "interface"))),
2288                     compat_name,
2289                     ddi_node_name(rdip), ddi_get_name_addr(rdip),
2290                     ddi_driver_name(rdip),
2291                     ddi_get_instance(rdip), usba_device->usb_addr);
2292 
2293                 name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
2294                 (void) usba_get_mfg_prod_sn_str(rdip, name, MAXNAMELEN);
2295                 if (name[0] != '\0') {
2296                         cmn_err(CE_CONT, "?\t%s\n", name);
2297                 }
2298                 kmem_free(name, MAXNAMELEN);
2299 
2300                 break;
2301         }
2302         default:
2303                 /* pass to usba to handle */
2304                 return (usba_bus_ctl(hubdip, rdip, op, arg, result));
2305         }
2306 
2307         return (DDI_SUCCESS);
2308 }
2309 
2310 /*
2311  * bus enumeration entry points
2312  *  Configures the named device(BUS_CONFIG_ONE) or all devices under
2313  *  the nexus(BUS_CONFIG_ALL). Drives devinfo state to DS_READY,i.e.device
2314  *  is fully operational.
2315  *
2316  *  This operation is driven from devfs(reading /devices), devctl, libdevinfo;
2317  *  or from within the kernel to attach a boot device or layered underlying
2318  *  driver.
2319  */
2320 static int
2321 hwahc_bus_config(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
2322         void *arg, dev_info_t **child)
2323 {
2324         hwahc_state_t   *hwahcp;
2325         int             rval, circ;
2326 
2327         if ((hwahcp = ddi_get_soft_state(hwahc_statep,
2328             ddi_get_instance(dip))) == NULL) {
2329 
2330                 return (NDI_FAILURE);
2331         }
2332 
2333         USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2334             "hwahc_bus_config: op=%d", op);
2335 
2336         if (hwahc_bus_config_debug) {
2337                 flag |= NDI_DEVI_DEBUG;
2338         }
2339 
2340         ndi_devi_enter(dip, &circ);
2341         rval = ndi_busop_bus_config(dip, flag, op, arg, child, 0);
2342         ndi_devi_exit(dip, circ);
2343 
2344         return (rval);
2345 }
2346 
2347 /*
2348  * Unconfigures the named device or all devices under the nexus. The
2349  * devinfo state is not DS_READY anymore.
2350  * This operations is driven by modunload, devctl or DR branch removal or
2351  * rem_drv(1M).
2352  */
2353 static int
2354 hwahc_bus_unconfig(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
2355         void *arg)
2356 {
2357         hwahc_state_t   *hwahcp;
2358         wusb_hc_data_t  *hc_data;
2359         dev_info_t      *cdip;
2360         usb_port_t      port;
2361         int             rval, circ;
2362 
2363         if ((hwahcp = ddi_get_soft_state(hwahc_statep,
2364             ddi_get_instance(dip))) == NULL) {
2365 
2366                 return (NDI_FAILURE);
2367         }
2368 
2369         USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2370             "hwahc_bus_unconfig: op=%d", op);
2371 
2372         if (hwahc_bus_config_debug) {
2373                 flag |= NDI_DEVI_DEBUG;
2374         }
2375 
2376         if ((op == BUS_UNCONFIG_ALL) && (flag & NDI_AUTODETACH) == 0) {
2377                 flag |= NDI_DEVI_REMOVE;
2378         }
2379 
2380         /* serialize access */
2381         ndi_devi_enter(dip, &circ);
2382 
2383         /* unconfig children, detach them */
2384         rval = ndi_busop_bus_unconfig(dip, flag, op, arg);
2385 
2386         /* logically zap children's list */
2387         hc_data = &hwahcp->hwahc_hc_data;
2388 
2389         mutex_enter(&hc_data->hc_mutex);
2390         for (port = 1; port <= hc_data->hc_num_ports; port++) {
2391                 hc_data->hc_children_state[port] |= WUSB_CHILD_ZAP;
2392         }
2393         mutex_exit(&hc_data->hc_mutex);
2394 
2395         /* fill in what's left */
2396         for (cdip = ddi_get_child(dip); cdip;
2397             cdip = ddi_get_next_sibling(cdip)) {
2398                 usba_device_t *usba_device = usba_get_usba_device(cdip);
2399 
2400                 if (usba_device == NULL) {
2401 
2402                         continue;
2403                 }
2404                 mutex_enter(&hc_data->hc_mutex);
2405                 port = usba_device->usb_port;
2406                 hc_data->hc_children_dips[port] = cdip;
2407                 hc_data->hc_children_state[port] &= ~WUSB_CHILD_ZAP;
2408                 mutex_exit(&hc_data->hc_mutex);
2409         }
2410 
2411         /* physically zap the children we didn't find */
2412         mutex_enter(&hc_data->hc_mutex);
2413         for (port = 1; port <= hc_data->hc_num_ports; port++) {
2414                 if (hc_data->hc_children_state[port] & WUSB_CHILD_ZAP) {
2415                         wusb_dev_info_t         *dev_info;
2416                         wusb_secrt_data_t       *csecrt_data;
2417                         usba_device_t           *child_ud;
2418 
2419                         USB_DPRINTF_L3(PRINT_MASK_EVENTS,
2420                             hwahcp->hwahc_log_handle,
2421                             "hwahc_bus_unconfig: physically zap port %d", port);
2422 
2423                         child_ud = hc_data->hc_usba_devices[port];
2424                         mutex_exit(&hc_data->hc_mutex);
2425                         /* zap the dip and usba_device structure as well */
2426                         usba_free_usba_device(child_ud);
2427                         mutex_enter(&hc_data->hc_mutex);
2428                         hc_data->hc_usba_devices[port] = NULL;
2429 
2430                         /* dip freed in usba_destroy_child_devi */
2431                         hc_data->hc_children_dips[port] = NULL;
2432                         hc_data->hc_children_state[port] &= ~WUSB_CHILD_ZAP;
2433 
2434                         /* free hc_dev_infos[port] */
2435                         dev_info = hc_data->hc_dev_infos[port];
2436                         if (dev_info == NULL) {
2437 
2438                                 continue;
2439                         }
2440 
2441                         /* stop the device's trust timer before deallocate it */
2442                         hwahc_stop_trust_timer(dev_info);
2443 
2444                         if (dev_info->wdev_secrt_data.secrt_encry_descr) {
2445                                 csecrt_data = &dev_info->wdev_secrt_data;
2446                                 kmem_free(csecrt_data->secrt_encry_descr,
2447                                     sizeof (usb_encryption_descr_t) *
2448                                     csecrt_data->secrt_n_encry);
2449                         }
2450                         if (dev_info->wdev_uwb_descr) {
2451                                 kmem_free(dev_info->wdev_uwb_descr,
2452                                     sizeof (usb_uwb_cap_descr_t));
2453                         }
2454                         kmem_free(dev_info, sizeof (wusb_dev_info_t));
2455                         hc_data->hc_dev_infos[port] = NULL;
2456                 }
2457         }
2458         mutex_exit(&hc_data->hc_mutex);
2459 
2460         ndi_devi_exit(dip, circ);
2461 
2462         USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2463             "hwahc_bus_unconfig: rval=%d", rval);
2464 
2465         return (rval);
2466 }
2467 
2468 /*
2469  * busctl event support
2470  *
2471  * Called by ndi_busop_get_eventcookie(). Return a event cookie
2472  * associated with one event name.
2473  * The eventname should be the one we defined in hwahc_ndi_event_defs
2474  */
2475 static int
2476 hwahc_busop_get_eventcookie(dev_info_t *dip,
2477         dev_info_t      *rdip,
2478         char            *eventname,
2479         ddi_eventcookie_t *cookie)
2480 {
2481         hwahc_state_t   *hwahcp;
2482 
2483         if ((hwahcp = ddi_get_soft_state(hwahc_statep,
2484             ddi_get_instance(dip))) == NULL) {
2485 
2486                 return (NDI_FAILURE);
2487         }
2488 
2489         USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2490             "hwahc_busop_get_eventcookie: dip=0x%p, rdip=0x%p, "
2491             "event=%s", (void *)dip, (void *)rdip, eventname);
2492         USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2493             "(dip=%s%d, rdip=%s%d)",
2494             ddi_driver_name(dip), ddi_get_instance(dip),
2495             ddi_driver_name(rdip), ddi_get_instance(rdip));
2496 
2497         /* return event cookie, iblock cookie, and level */
2498         return (ndi_event_retrieve_cookie(hwahcp->hwahc_ndi_event_hdl,
2499             rdip, eventname, cookie, NDI_EVENT_NOPASS));
2500 }
2501 
2502 /*
2503  * Add event handler for a given event cookie
2504  */
2505 static int
2506 hwahc_busop_add_eventcall(dev_info_t *dip,
2507         dev_info_t      *rdip,
2508         ddi_eventcookie_t cookie,
2509         void            (*callback)(dev_info_t *dip,
2510                         ddi_eventcookie_t cookie, void *arg,
2511                         void *bus_impldata),
2512         void *arg, ddi_callback_id_t *cb_id)
2513 {
2514         hwahc_state_t   *hwahcp;
2515         usb_port_t      port;
2516 
2517         if ((hwahcp = ddi_get_soft_state(hwahc_statep,
2518             ddi_get_instance(dip))) == NULL) {
2519 
2520                 return (NDI_FAILURE);
2521         }
2522 
2523         port = hwahc_child_dip2port(hwahcp, rdip);
2524 
2525         mutex_enter(&hwahcp->hwahc_mutex);
2526 
2527         USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2528             "hwahc_busop_add_eventcall: dip=0x%p, rdip=0x%p "
2529             "cookie=0x%p, cb=0x%p, arg=0x%p",
2530             (void *)dip, (void *)rdip, (void *)cookie, (void *)callback, arg);
2531         USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2532             "(dip=%s%d, rdip=%s%d, event=%s)",
2533             ddi_driver_name(dip), ddi_get_instance(dip),
2534             ddi_driver_name(rdip), ddi_get_instance(rdip),
2535             ndi_event_cookie_to_name(hwahcp->hwahc_ndi_event_hdl, cookie));
2536 
2537         /* Set flag on children registering events */
2538         switch (ndi_event_cookie_to_tag(hwahcp->hwahc_ndi_event_hdl, cookie)) {
2539         case USBA_EVENT_TAG_HOT_REMOVAL:
2540                 hwahcp->hwahc_child_events[port] |=
2541                     HWAHC_CHILD_EVENT_DISCONNECT;
2542 
2543                 break;
2544         case USBA_EVENT_TAG_PRE_SUSPEND:
2545                 hwahcp->hwahc_child_events[port] |=
2546                     HWAHC_CHILD_EVENT_PRESUSPEND;
2547 
2548                 break;
2549         default:
2550 
2551                 break;
2552         }
2553 
2554         mutex_exit(&hwahcp->hwahc_mutex);
2555 
2556         /* add callback to our event set */
2557         return (ndi_event_add_callback(hwahcp->hwahc_ndi_event_hdl,
2558             rdip, cookie, callback, arg, NDI_SLEEP, cb_id));
2559 
2560 }
2561 
2562 
2563 /*
2564  * Remove a callback previously added by bus_add_eventcall()
2565  */
2566 static int
2567 hwahc_busop_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id)
2568 {
2569         hwahc_state_t   *hwahcp;
2570         ndi_event_callbacks_t *id = (ndi_event_callbacks_t *)cb_id;
2571 
2572         if ((hwahcp = ddi_get_soft_state(hwahc_statep,
2573             ddi_get_instance(dip))) == NULL) {
2574 
2575                 return (NDI_FAILURE);
2576         }
2577 
2578         USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2579             "hwahc_busop_remove_eventcall: dip=0x%p, rdip=0x%p "
2580             "cookie=0x%p", (void *)dip, (void *) id->ndi_evtcb_dip,
2581             (void *)id->ndi_evtcb_cookie);
2582         USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2583             "(dip=%s%d, rdip=%s%d, event=%s)",
2584             ddi_driver_name(dip), ddi_get_instance(dip),
2585             ddi_driver_name(id->ndi_evtcb_dip),
2586             ddi_get_instance(id->ndi_evtcb_dip),
2587             ndi_event_cookie_to_name(hwahcp->hwahc_ndi_event_hdl,
2588             id->ndi_evtcb_cookie));
2589 
2590         /* remove event registration from our event set */
2591         return (ndi_event_remove_callback(hwahcp->hwahc_ndi_event_hdl, cb_id));
2592 }
2593 
2594 /*
2595  * hwahc_post_event
2596  *      post event to a single child on the port depending on the type, i.e.
2597  *      to invoke the child's registered callback.
2598  */
2599 static void
2600 hwahc_post_event(hwahc_state_t *hwahcp, usb_port_t port, usba_event_t type)
2601 {
2602         int             rval;
2603         dev_info_t      *dip;
2604         usba_device_t   *usba_device;
2605         ddi_eventcookie_t cookie, rm_cookie, suspend_cookie;
2606         wusb_hc_data_t  *hc_data = &hwahcp->hwahc_hc_data;
2607 
2608         USB_DPRINTF_L4(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2609             "hwahc_post_event: port=%d event=%s", port,
2610             ndi_event_tag_to_name(hwahcp->hwahc_ndi_event_hdl, type));
2611 
2612         cookie = ndi_event_tag_to_cookie(hwahcp->hwahc_ndi_event_hdl, type);
2613         rm_cookie = ndi_event_tag_to_cookie(hwahcp->hwahc_ndi_event_hdl,
2614             USBA_EVENT_TAG_HOT_REMOVAL);
2615         suspend_cookie = ndi_event_tag_to_cookie(hwahcp->hwahc_ndi_event_hdl,
2616             USBA_EVENT_TAG_PRE_SUSPEND);
2617 
2618         /*
2619          * Hotplug daemon may be attaching a driver that may be registering
2620          * event callbacks. So it already has got the device tree lock and
2621          * event handle mutex. So to prevent a deadlock while posting events,
2622          * we grab and release the locks in the same order.
2623          */
2624         mutex_enter(&hwahcp->hwahc_mutex);
2625         dip = hwahcp->hwahc_hc_data.hc_children_dips[port];
2626         usba_device = hwahcp->hwahc_hc_data.hc_usba_devices[port];
2627         mutex_exit((&hwahcp->hwahc_mutex));
2628 
2629         switch (type) {
2630         case USBA_EVENT_TAG_HOT_REMOVAL:
2631                 /* stop this device's timer to prevent its further process */
2632                 mutex_enter(&hc_data->hc_mutex);
2633 
2634                 hwahc_stop_trust_timer(hc_data->hc_dev_infos[port]);
2635                 mutex_exit(&hc_data->hc_mutex);
2636 
2637                 /* Clear the registered event flag */
2638                 mutex_enter(&hwahcp->hwahc_mutex);
2639                 hwahcp->hwahc_child_events[port] &=
2640                     ~HWAHC_CHILD_EVENT_DISCONNECT;
2641                 mutex_exit(&hwahcp->hwahc_mutex);
2642 
2643                 (void) ndi_event_do_callback(hwahcp->hwahc_ndi_event_hdl,
2644                     dip, cookie, NULL);
2645                 usba_persistent_pipe_close(usba_device);
2646 
2647                 /*
2648                  * Mark the dip for deletion only after the driver has
2649                  * seen the disconnect event to prevent cleanup thread
2650                  * from stepping in between.
2651                  */
2652 #ifndef __lock_lint
2653                 mutex_enter(&DEVI(dip)->devi_lock);
2654                 DEVI_SET_DEVICE_REMOVED(dip);
2655                 mutex_exit(&DEVI(dip)->devi_lock);
2656 #endif
2657 
2658                 break;
2659         case USBA_EVENT_TAG_PRE_SUSPEND:
2660                 mutex_enter(&hwahcp->hwahc_mutex);
2661                 hwahcp->hwahc_child_events[port] &=
2662                     ~HWAHC_CHILD_EVENT_PRESUSPEND;
2663                 mutex_exit(&hwahcp->hwahc_mutex);
2664 
2665                 (void) ndi_event_do_callback(hwahcp->hwahc_ndi_event_hdl,
2666                     dip, cookie, NULL);
2667                 /*
2668                  * persistent pipe close for this event is taken care by the
2669                  * caller after verfying that all children can suspend
2670                  */
2671 
2672                 break;
2673         case USBA_EVENT_TAG_HOT_INSERTION:
2674                 /*
2675                  * Check if this child has missed the disconnect event before
2676                  * it registered for event callbacks
2677                  */
2678                 mutex_enter(&hwahcp->hwahc_mutex);
2679                 if (hwahcp->hwahc_child_events[port] &
2680                     HWAHC_CHILD_EVENT_DISCONNECT) {
2681                         /* clear the flag and post disconnect event */
2682                         hwahcp->hwahc_child_events[port] &=
2683                             ~HWAHC_CHILD_EVENT_DISCONNECT;
2684                         mutex_exit(&hwahcp->hwahc_mutex);
2685 
2686                         (void) ndi_event_do_callback(
2687                             hwahcp->hwahc_ndi_event_hdl,
2688                             dip, rm_cookie, NULL);
2689                         usba_persistent_pipe_close(usba_device);
2690                         mutex_enter(&hwahcp->hwahc_mutex);
2691                 }
2692                 mutex_exit(&hwahcp->hwahc_mutex);
2693 
2694                 /*
2695                  * Mark the dip as reinserted to prevent cleanup thread
2696                  * from stepping in.
2697                  */
2698 #ifndef __lock_lint
2699                 mutex_enter(&(DEVI(dip)->devi_lock));
2700                 DEVI_SET_DEVICE_REINSERTED(dip);
2701                 mutex_exit(&(DEVI(dip)->devi_lock));
2702 #endif
2703 
2704                 rval = usba_persistent_pipe_open(usba_device);
2705                 if (rval != USB_SUCCESS) {
2706                         USB_DPRINTF_L2(PRINT_MASK_EVENTS,
2707                             hwahcp->hwahc_log_handle,
2708                             "failed to reopen all pipes on reconnect");
2709                 }
2710 
2711                 (void) ndi_event_do_callback(hwahcp->hwahc_ndi_event_hdl,
2712                     dip, cookie, NULL);
2713 
2714                 /*
2715                  * We might see a connect event only if hotplug thread for
2716                  * disconnect event don't run in time.
2717                  * Set the flag again, so we don't miss posting a
2718                  * disconnect event.
2719                  */
2720                 mutex_enter(&hwahcp->hwahc_mutex);
2721                 hwahcp->hwahc_child_events[port] |=
2722                     HWAHC_CHILD_EVENT_DISCONNECT;
2723                 mutex_exit(&hwahcp->hwahc_mutex);
2724 
2725                 break;
2726         case USBA_EVENT_TAG_POST_RESUME:
2727                 /*
2728                  * Check if this child has missed the pre-suspend event before
2729                  * it registered for event callbacks
2730                  */
2731                 mutex_enter(&hwahcp->hwahc_mutex);
2732                 if (hwahcp->hwahc_child_events[port] &
2733                     HWAHC_CHILD_EVENT_PRESUSPEND) {
2734                         /* clear the flag and post pre_suspend event */
2735                         hwahcp->hwahc_child_events[port] &=
2736                             ~HWAHC_CHILD_EVENT_PRESUSPEND;
2737                         mutex_exit(&hwahcp->hwahc_mutex);
2738                         (void) ndi_event_do_callback(
2739                             hwahcp->hwahc_ndi_event_hdl,
2740                             dip, suspend_cookie, NULL);
2741                         mutex_enter(&hwahcp->hwahc_mutex);
2742                 }
2743                 mutex_exit(&hwahcp->hwahc_mutex);
2744 
2745                 mutex_enter(&usba_device->usb_mutex);
2746                 usba_device->usb_no_cpr = 0;
2747                 mutex_exit(&usba_device->usb_mutex);
2748 
2749                 /*
2750                  * Since the pipe has already been opened by whub
2751                  * at DDI_RESUME time, there is no need for a
2752                  * persistent pipe open
2753                  */
2754                 (void) ndi_event_do_callback(hwahcp->hwahc_ndi_event_hdl,
2755                     dip, cookie, NULL);
2756 
2757                 /*
2758                  * Set the flag again, so we don't miss posting a
2759                  * pre-suspend event. This enforces a tighter
2760                  * dev_state model.
2761                  */
2762                 mutex_enter(&hwahcp->hwahc_mutex);
2763                 hwahcp->hwahc_child_events[port] |=
2764                     HWAHC_CHILD_EVENT_PRESUSPEND;
2765                 mutex_exit(&hwahcp->hwahc_mutex);
2766                 break;
2767         }
2768 }
2769 
2770 /*
2771  * hwahc_run_callbacks:
2772  *      Send an event to all children
2773  */
2774 static void
2775 hwahc_run_callbacks(hwahc_state_t *hwahcp, usba_event_t type)
2776 {
2777         usb_port_t      port;
2778         wusb_hc_data_t  *hc_data = &hwahcp->hwahc_hc_data;
2779 
2780         USB_DPRINTF_L4(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2781             "hwahc_run_callbacks:");
2782 
2783         mutex_enter(&hc_data->hc_mutex);
2784         for (port = 1; port <= hc_data->hc_num_ports; port++) {
2785                 if (hc_data->hc_children_dips[port]) {
2786                         mutex_exit(&hc_data->hc_mutex);
2787                         hwahc_post_event(hwahcp, port, type);
2788                         mutex_enter(&hc_data->hc_mutex);
2789                 }
2790         }
2791         mutex_exit(&hc_data->hc_mutex);
2792 }
2793 
2794 /*
2795  * hwahc_disconnect_event_cb:
2796  *      Called when hwa device hotplug-removed.
2797  *              Close pipes
2798  *              Post event to child
2799  *              Set state to DISCONNECTED
2800  */
2801 static int
2802 hwahc_disconnect_event_cb(dev_info_t *dip)
2803 {
2804         int             instance = ddi_get_instance(dip);
2805         hwahc_state_t   *hwahcp;
2806         int             circ;
2807 
2808         if ((hwahcp = ddi_get_soft_state(hwahc_statep, instance)) == NULL) {
2809 
2810                 return (USB_FAILURE);
2811         }
2812 
2813         USB_DPRINTF_L4(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2814             "hwahc_disconnect_event_cb: dip = 0x%p", (void *)dip);
2815 
2816         ndi_devi_enter(dip, &circ);
2817 
2818         mutex_enter(&hwahcp->hwahc_mutex);
2819 
2820         USB_DPRINTF_L4(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2821             "hwahc_disconnect_event_cb: devstate= %d hw-state=%d",
2822             hwahcp->hwahc_dev_state, hwahcp->hwahc_hw_state);
2823         switch (hwahcp->hwahc_dev_state) {
2824         case USB_DEV_ONLINE:
2825         case USB_DEV_PWRED_DOWN:
2826                 hwahcp->hwahc_dev_state = USB_DEV_DISCONNECTED;
2827 
2828                 if (hwahcp->hwahc_hw_state != HWAHC_HW_STOPPED) {
2829                         mutex_exit(&hwahcp->hwahc_mutex);
2830                         wusb_wa_stop_nep(&hwahcp->hwahc_wa_data);
2831                         mutex_enter(&hwahcp->hwahc_mutex);
2832                         hwahc_stop_result_thread(hwahcp);
2833                         hwahc_drain_notif_queue(hwahcp);
2834                 }
2835                 /* FALLTHROUGH */
2836         case USB_DEV_SUSPENDED:
2837                 /* remain in this state */
2838                 mutex_exit(&hwahcp->hwahc_mutex);
2839                 hwahc_run_callbacks(hwahcp, USBA_EVENT_TAG_HOT_REMOVAL);
2840                 mutex_enter(&hwahcp->hwahc_mutex);
2841 
2842                 hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_INIT_STATE;
2843 
2844                 break;
2845         case USB_DEV_DISCONNECTED:
2846                 USB_DPRINTF_L2(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2847                     "hwahc_disconnect_event_cb: already disconnected");
2848 
2849                 break;
2850         default:
2851                 USB_DPRINTF_L2(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2852                     "hwahc_disconnect_event_cb: illegal devstate=%d",
2853                     hwahcp->hwahc_dev_state);
2854 
2855                 break;
2856         }
2857         mutex_exit(&hwahcp->hwahc_mutex);
2858 
2859         ndi_devi_exit(dip, circ);
2860 
2861         return (USB_SUCCESS);
2862 }
2863 
2864 
2865 /*
2866  * hwahc_reconnect_event_cb:
2867  *      Called with device hotplug-inserted
2868  *              Restore state
2869  */
2870 static int
2871 hwahc_reconnect_event_cb(dev_info_t *dip)
2872 {
2873         int             instance = ddi_get_instance(dip);
2874         hwahc_state_t   *hwahcp;
2875         int             circ;
2876 
2877 
2878         if ((hwahcp = ddi_get_soft_state(hwahc_statep, instance)) == NULL) {
2879 
2880                 return (USB_FAILURE);
2881         }
2882 
2883         USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
2884             "hwahc_reconnect_event_cb: dip = 0x%p", (void *)dip);
2885 
2886         ndi_devi_enter(dip, &circ);
2887         hwahc_restore_device_state(dip, hwahcp);
2888         ndi_devi_exit(dip, circ);
2889 
2890         return (USB_SUCCESS);
2891 }
2892 
2893 
2894 /*
2895  * hwahc_pre_suspend_event_cb:
2896  *      Called before HWA device suspend
2897  */
2898 static int
2899 hwahc_pre_suspend_event_cb(dev_info_t *dip)
2900 {
2901         int             instance = ddi_get_instance(dip);
2902         hwahc_state_t   *hwahcp;
2903         int             circ;
2904 
2905         if ((hwahcp = ddi_get_soft_state(hwahc_statep, instance)) == NULL) {
2906 
2907                 return (USB_FAILURE);
2908         }
2909 
2910         USB_DPRINTF_L4(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2911             "hwahc_pre_suspend_event_cb: dip = 0x%p", (void *)dip);
2912 
2913         mutex_enter(&hwahcp->hwahc_mutex);
2914         USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
2915             "hwahc_pre_suspend_event_cb: start, hw state = %d, softstate = %d",
2916             hwahcp->hwahc_hw_state, hwahcp->hwahc_hc_soft_state);
2917         mutex_exit(&hwahcp->hwahc_mutex);
2918 
2919         /* keep PM out till we see a cpr resume */
2920         (void) hwahc_pm_busy_component(hwahcp);
2921         (void) pm_raise_power(hwahcp->hwahc_dip, 0, USB_DEV_OS_FULL_PWR);
2922 
2923         ndi_devi_enter(dip, &circ);
2924         hwahc_run_callbacks(hwahcp, USBA_EVENT_TAG_PRE_SUSPEND);
2925         ndi_devi_exit(dip, circ);
2926 
2927         /*
2928          * rc driver is always suspended first, that fails the hc suspend.
2929          * need to suspend hc before rc is suspended, so move the suspend
2930          * operations here
2931          */
2932         mutex_enter(&hwahcp->hwahc_mutex);
2933         if (hwahcp->hwahc_dev_state != USB_DEV_ONLINE) {
2934                 USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2935                     "hwahc_pre_suspend_event_cb: dev_state = %d",
2936                     hwahcp->hwahc_dev_state);
2937                 mutex_exit(&hwahcp->hwahc_mutex);
2938 
2939                 return (USB_SUCCESS);
2940         }
2941 
2942         if (hwahcp->hwahc_hw_state == HWAHC_HW_STARTED) {
2943                 /*
2944                  * notify children the host is going to stop
2945                  */
2946                 (void) hwahc_hc_channel_suspend(hwahcp);
2947         }
2948 
2949         /* stop the hc from functioning */
2950         if (hwahcp->hwahc_hw_state != HWAHC_HW_STOPPED) {
2951                 mutex_exit(&hwahcp->hwahc_mutex);
2952                 wusb_wa_stop_nep(&hwahcp->hwahc_wa_data);
2953 
2954                 mutex_enter(&hwahcp->hwahc_mutex);
2955                 hwahc_stop_result_thread(hwahcp);
2956                 hwahc_drain_notif_queue(hwahcp);
2957 
2958                 mutex_exit(&hwahcp->hwahc_mutex);
2959                 (void) wusb_wa_disable(&hwahcp->hwahc_wa_data,
2960                     hwahcp->hwahc_default_pipe);
2961                 mutex_enter(&hwahcp->hwahc_mutex);
2962 
2963         }
2964 
2965         USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
2966             "hwahc_pre_suspend_event_cb: end, devstate=%d "
2967             "hwstate=%d softstate = %d",
2968             hwahcp->hwahc_dev_state, hwahcp->hwahc_hw_state,
2969             hwahcp->hwahc_hc_soft_state);
2970 
2971         mutex_exit(&hwahcp->hwahc_mutex);
2972 
2973         return (USB_SUCCESS);
2974 }
2975 
2976 
2977 /*
2978  * hwahc_post_resume_event_cb:
2979  *      Call after HWA device resume
2980  */
2981 static int
2982 hwahc_post_resume_event_cb(dev_info_t *dip)
2983 {
2984         int             instance = ddi_get_instance(dip);
2985         hwahc_state_t   *hwahcp;
2986         int             circ;
2987 
2988         if ((hwahcp = ddi_get_soft_state(hwahc_statep, instance)) == NULL) {
2989 
2990                 return (USB_FAILURE);
2991         }
2992 
2993         USB_DPRINTF_L4(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2994             "hwahc_post_resume_event_cb: dip = 0x%p", (void *)dip);
2995 
2996         mutex_enter(&hwahcp->hwahc_mutex);
2997 
2998         USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
2999             "hwahc_post_resume_event_cb: start, hw state = %d, softstate = %d",
3000             hwahcp->hwahc_hw_state, hwahcp->hwahc_hc_soft_state);
3001         mutex_exit(&hwahcp->hwahc_mutex);
3002 
3003         ndi_devi_enter(dip, &circ);
3004 
3005         /* need to place hc restore here to make sure rc has resumed */
3006         hwahc_restore_device_state(dip, hwahcp);
3007 
3008         hwahc_run_callbacks(hwahcp, USBA_EVENT_TAG_POST_RESUME);
3009 
3010         ndi_devi_exit(dip, circ);
3011 
3012         /* enable PM */
3013         (void) hwahc_pm_idle_component(hwahcp);
3014 
3015         return (USB_SUCCESS);
3016 }
3017 
3018 
3019 /*
3020  * hwahc_restore_device_state:
3021  *      Called during hotplug-reconnect and resume.
3022  *              re-enable power management
3023  *              Verify the device is the same as before the disconnect/suspend.
3024  *              Restore device state
3025  *              Thaw any IO which was frozen.
3026  *              Quiesce device.  (Other routines will activate if thawed IO.)
3027  *              Set device online.
3028  *              Leave device disconnected if there are problems.
3029  */
3030 static void
3031 hwahc_restore_device_state(dev_info_t *dip, hwahc_state_t *hwahcp)
3032 {
3033         int     rval;
3034         int     old_hw_state;
3035 
3036         USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3037             "hwahc_restore_device_state: dip = 0x%p", (void *)dip);
3038 
3039         mutex_enter(&hwahcp->hwahc_mutex);
3040 
3041         ASSERT((hwahcp->hwahc_dev_state == USB_DEV_DISCONNECTED) ||
3042             (hwahcp->hwahc_dev_state == USB_DEV_SUSPENDED));
3043 
3044         /* raise power */
3045         mutex_exit(&hwahcp->hwahc_mutex);
3046         hwahc_pm_busy_component(hwahcp);
3047         (void) pm_raise_power(hwahcp->hwahc_dip, 0, USB_DEV_OS_FULL_PWR);
3048 
3049         /*
3050          * Check if we are talking to the same device
3051          * Some host controllers may see all devices disconnected
3052          * when they just resume. This may be a cause of not
3053          * finding the same device.
3054          *
3055          * Some HWA devices need to download firmware when it is
3056          * powered on. Before the firmware is downloaded, the device
3057          * will look differently.
3058          */
3059         if (usb_check_same_device(dip, hwahcp->hwahc_log_handle,
3060             USB_LOG_L0, PRINT_MASK_ALL,
3061             USB_CHK_BASIC | USB_CHK_SERIAL | USB_CHK_VIDPID, NULL) !=
3062             USB_SUCCESS) {
3063                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3064                     "hwahc_restore_device_state: not the same device");
3065                 /* change the device state from suspended to disconnected */
3066                 mutex_enter(&hwahcp->hwahc_mutex);
3067                 hwahcp->hwahc_dev_state = USB_DEV_DISCONNECTED;
3068                 hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_ERROR_STATE;
3069                 mutex_exit(&hwahcp->hwahc_mutex);
3070                 hwahc_pm_idle_component(hwahcp);
3071 
3072                 return;
3073         }
3074 
3075         USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3076             "hwahc_restore_device_state: Hwahc has been reconnected but"
3077             " data may have been lost");
3078 
3079         mutex_enter(&hwahcp->hwahc_mutex);
3080 
3081         /* reinitialize the hw */
3082         hwahcp->hwahc_dev_state = USB_DEV_ONLINE;
3083         hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_INIT_STATE;
3084 
3085         if (hwahcp->hwahc_hw_state == HWAHC_HW_STOPPED) {
3086                 mutex_exit(&hwahcp->hwahc_mutex);
3087                 /* no need to start hc */
3088                 hwahc_pm_idle_component(hwahcp);
3089                 USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3090                     "hwahc_restore_device_state: stopped hwa");
3091 
3092                 return;
3093         }
3094 
3095 
3096         rval = wusb_hc_set_cluster_id(&hwahcp->hwahc_hc_data,
3097             hwahcp->hwahc_hc_data.hc_cluster_id);
3098 
3099         if (rval != USB_SUCCESS) {
3100                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3101                     "hwahc_restore_device_state: set cluster id fails");
3102 
3103                 goto err;
3104         }
3105 
3106         if (hwahcp->hwahc_hw_state == HWAHC_HW_STARTED) {
3107                 old_hw_state = hwahcp->hwahc_hw_state;
3108                 hwahcp->hwahc_hw_state = HWAHC_HW_CH_STOPPED;
3109                 rval = hwahc_hc_channel_start(hwahcp);
3110                 if (rval != USB_SUCCESS) {
3111                         USB_DPRINTF_L2(PRINT_MASK_ATTA,
3112                             hwahcp->hwahc_log_handle,
3113                             "hwahc_restore_device_state: start hc fails");
3114                         hwahcp->hwahc_hw_state = old_hw_state;
3115 
3116                         goto err;
3117                 }
3118                 hwahcp->hwahc_hw_state = old_hw_state;
3119         }
3120 
3121         rval = wusb_hc_set_num_dnts(&hwahcp->hwahc_hc_data,
3122             HWAHC_DEFAULT_DNTS_INTERVAL, HWAHC_DEFAULT_DNTS_SLOT_NUM);
3123         if (rval != USB_SUCCESS) {
3124                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3125                     "hwahc_restore_device_state: set num dnts fails");
3126 
3127                 goto err;
3128         }
3129 
3130         /* set default GTK */
3131         rval = wusb_hc_set_gtk(&hwahcp->hwahc_hc_data, dft_gtk, dft_gtkid);
3132         if (rval != USB_SUCCESS) {
3133                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3134                     "hwahc_restore_device_state: set gtk fails");
3135 
3136                 goto err;
3137         }
3138 
3139         mutex_exit(&hwahcp->hwahc_mutex);
3140 
3141         rval = wusb_wa_enable(&hwahcp->hwahc_wa_data,
3142             hwahcp->hwahc_default_pipe);
3143         mutex_enter(&hwahcp->hwahc_mutex);
3144         if (rval != USB_SUCCESS) {
3145                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3146                     "hwahc_restore_device_state: enable wa fails");
3147 
3148                 goto err;
3149         }
3150 
3151         /*
3152          * This is a workaround, sometimes the ioctl and reconnect will
3153          * happen at the sametime, so the ioctl will start nep which makes
3154          * the below sart nep fail. Need more work to do to avoid such
3155          * issues
3156          */
3157         (void) wusb_wa_stop_nep(&hwahcp->hwahc_wa_data);
3158 
3159         rval = wusb_wa_start_nep(&hwahcp->hwahc_wa_data, USB_FLAGS_SLEEP);
3160         if (rval != USB_SUCCESS) {
3161                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3162                     "hwahc_restore_device_state: start notifep fails rval =%d",
3163                     rval);
3164                 mutex_exit(&hwahcp->hwahc_mutex);
3165                 (void) wusb_wa_disable(&hwahcp->hwahc_wa_data,
3166                     hwahcp->hwahc_default_pipe);
3167                 mutex_enter(&hwahcp->hwahc_mutex);
3168 
3169                 goto err;
3170         }
3171 
3172         /* Handle transfer results on bulk-in ep */
3173         rval = hwahc_start_result_thread(hwahcp);
3174         if (rval != USB_SUCCESS) {
3175                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3176                     "hwahc_restore_device_state: start result thread fails");
3177                 mutex_exit(&hwahcp->hwahc_mutex);
3178                 wusb_wa_stop_nep(&hwahcp->hwahc_wa_data);
3179                 (void) wusb_wa_disable(&hwahcp->hwahc_wa_data,
3180                     hwahcp->hwahc_default_pipe);
3181                 mutex_enter(&hwahcp->hwahc_mutex);
3182 
3183                 goto err;
3184         }
3185 
3186         /* if the device had remote wakeup earlier, enable it again */
3187         if (hwahcp->hwahc_pm && hwahcp->hwahc_pm->hwahc_wakeup_enabled) {
3188                 mutex_exit(&hwahcp->hwahc_mutex);
3189                 (void) usb_handle_remote_wakeup(hwahcp->hwahc_dip,
3190                     USB_REMOTE_WAKEUP_ENABLE);
3191                 mutex_enter(&hwahcp->hwahc_mutex);
3192         }
3193 
3194         hwahcp->hwahc_hw_state = HWAHC_HW_STARTED;
3195         hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_OPERATIONAL_STATE;
3196         mutex_exit(&hwahcp->hwahc_mutex);
3197         hwahc_pm_idle_component(hwahcp);
3198 
3199         return;
3200 
3201 err:
3202         hwahcp->hwahc_hw_state = HWAHC_HW_STOPPED;
3203         mutex_exit(&hwahcp->hwahc_mutex);
3204         hwahc_pm_idle_component(hwahcp);
3205 }
3206 
3207 
3208 /*
3209  * hwahc_cpr_suspend:
3210  *      Clean up device.
3211  *      Wait for any IO to finish, then close pipes.
3212  *      Quiesce device.
3213  * due to the dependency on hwarc, the actual suspend operations are
3214  * moved to hwahc_pre_suspend_event_cb function.
3215  */
3216 static int
3217 hwahc_cpr_suspend(dev_info_t *dip)
3218 {
3219         int             instance = ddi_get_instance(dip);
3220         hwahc_state_t   *hwahcp = ddi_get_soft_state(hwahc_statep, instance);
3221 
3222         if (hwahcp == NULL) {
3223 
3224                 return (USB_FAILURE);
3225         }
3226 
3227         USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3228             "hwahc_cpr_suspend: start");
3229 
3230         mutex_enter(&hwahcp->hwahc_mutex);
3231 
3232         /* Don't suspend if the device is open. */
3233         if (hwahcp->hwahc_open_count > 0) {
3234                 USB_DPRINTF_L2(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3235                     "hwahc_cpr_suspend: Device is open, cannot suspend");
3236                 mutex_exit(&hwahcp->hwahc_mutex);
3237 
3238                 return (USB_FAILURE);
3239         }
3240 
3241         mutex_exit(&hwahcp->hwahc_mutex);
3242         /* raise power */
3243         hwahc_pm_busy_component(hwahcp);
3244         (void) pm_raise_power(hwahcp->hwahc_dip, 0, USB_DEV_OS_FULL_PWR);
3245 
3246         mutex_enter(&hwahcp->hwahc_mutex);
3247         switch (hwahcp->hwahc_dev_state) {
3248         case USB_DEV_ONLINE:
3249         /* real suspend operations put in pre_suspend function */
3250                 /* FALLTHRU */
3251         case USB_DEV_DISCONNECTED:
3252         case USB_DEV_PWRED_DOWN:
3253                 hwahcp->hwahc_dev_state = USB_DEV_SUSPENDED;
3254                 hwahcp->hwahc_hw_state = HWAHC_HW_CH_SUSPEND;
3255 
3256                 break;
3257         case USB_DEV_SUSPENDED:
3258         default:
3259                 USB_DPRINTF_L2(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3260                     "hwahc_cpr_suspend: illegal dev state=%d",
3261                     hwahcp->hwahc_dev_state);
3262 
3263                 break;
3264         }
3265 
3266         mutex_exit(&hwahcp->hwahc_mutex);
3267         hwahc_pm_idle_component(hwahcp);
3268 
3269         USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3270             "hwahc_cpr_suspend: end");
3271 
3272         return (USB_SUCCESS);
3273 }
3274 
3275 
3276 /*
3277  * hwahc_cpr_resume:
3278  *
3279  *      hwahc_restore_device_state marks success by putting device back online
3280  */
3281 static int
3282 hwahc_cpr_resume(dev_info_t *dip)
3283 {
3284         int             instance = ddi_get_instance(dip);
3285         hwahc_state_t   *hwahcp = ddi_get_soft_state(hwahc_statep, instance);
3286 
3287         if (hwahcp == NULL) {
3288 
3289                 return (USB_FAILURE);
3290         }
3291 
3292         USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3293             "hwahc_cpr_resume: hw state = %d, softstate = %d",
3294             hwahcp->hwahc_hw_state, hwahcp->hwahc_hc_soft_state);
3295 
3296         /*
3297          * rc is always resumed after hc. restoring hc before rc would fail.
3298          * move the restoring operations to hwahc_post_resume_event_cb.
3299          */
3300 
3301         return (USB_SUCCESS);
3302 }
3303 
3304 /*
3305  * hwahc_create_pm_components:
3306  *      Create power managements components
3307  */
3308 static void
3309 hwahc_create_pm_components(dev_info_t *dip, hwahc_state_t *hwahcp)
3310 {
3311         hwahc_power_t   *hwahcpm;
3312         uint_t          pwr_states;
3313 
3314         USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3315             "hwahc_create_pm_components: Begin");
3316 
3317         /* Allocate the state structure */
3318         hwahcpm = kmem_zalloc(sizeof (hwahc_power_t), KM_SLEEP);
3319         hwahcp->hwahc_pm = hwahcpm;
3320         hwahcpm->hwahc_state = hwahcp;
3321         hwahcpm->hwahc_pm_capabilities = 0;
3322         hwahcpm->hwahc_current_power = USB_DEV_OS_FULL_PWR;
3323 
3324         if (usb_create_pm_components(dip, &pwr_states) == USB_SUCCESS) {
3325                 USB_DPRINTF_L3(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3326                     "hwahc_create_pm_components: created PM components");
3327 
3328                 if (usb_handle_remote_wakeup(dip,
3329                     USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) {
3330                         hwahcpm->hwahc_wakeup_enabled = 1;
3331                 }
3332                 hwahcpm->hwahc_pwr_states = (uint8_t)pwr_states;
3333                 /* make device busy till end of attach */
3334                 hwahc_pm_busy_component(hwahcp);
3335                 (void) pm_raise_power(hwahcp->hwahc_dip, 0,
3336                     USB_DEV_OS_FULL_PWR);
3337         } else {
3338                 USB_DPRINTF_L3(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3339                     "hwahc_create_pm_components: failed");
3340         }
3341 
3342         USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3343             "hwahc_create_pm_components: End");
3344 }
3345 
3346 /*
3347  * hwahc_destroy_pm_components:
3348  *      Shut down and destroy power management and remote wakeup functionality
3349  */
3350 static void
3351 hwahc_destroy_pm_components(hwahc_state_t *hwahcp)
3352 {
3353         USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3354             "hwahc_destroy_pm_components: Begin");
3355 
3356         ASSERT(!mutex_owned(&hwahcp->hwahc_mutex));
3357 
3358         mutex_enter(&hwahcp->hwahc_mutex);
3359         if (hwahcp->hwahc_pm && (hwahcp->hwahc_dev_state !=
3360             USB_DEV_DISCONNECTED)) {
3361                 mutex_exit(&hwahcp->hwahc_mutex);
3362                 hwahc_pm_busy_component(hwahcp);
3363                 mutex_enter(&hwahcp->hwahc_mutex);
3364 
3365                 if (hwahcp->hwahc_pm->hwahc_wakeup_enabled) {
3366                         int rval;
3367 
3368                         mutex_exit(&hwahcp->hwahc_mutex);
3369                         (void) pm_raise_power(hwahcp->hwahc_dip, 0,
3370                             USB_DEV_OS_FULL_PWR);
3371 
3372                         if ((rval = usb_handle_remote_wakeup(
3373                             hwahcp->hwahc_dip,
3374                             USB_REMOTE_WAKEUP_DISABLE)) !=
3375                             USB_SUCCESS) {
3376                                 USB_DPRINTF_L3(PRINT_MASK_PM,
3377                                     hwahcp->hwahc_log_handle,
3378                                     "hwahc_destroy_pm_components: "
3379                                     "Error disabling rmt wakeup: rval = %d",
3380                                     rval);
3381                         }
3382                 } else {
3383                         mutex_exit(&hwahcp->hwahc_mutex);
3384                 }
3385 
3386                 /*
3387                  * Since remote wakeup is disabled now,
3388                  * no one can raise power and get to device
3389                  * once power is lowered here.
3390                  */
3391                 (void) pm_lower_power(hwahcp->hwahc_dip, 0, USB_DEV_OS_PWR_OFF);
3392 
3393                 hwahc_pm_idle_component(hwahcp);
3394                 mutex_enter(&hwahcp->hwahc_mutex);
3395         }
3396 
3397         if (hwahcp->hwahc_pm) {
3398                 kmem_free(hwahcp->hwahc_pm, sizeof (hwahc_power_t));
3399                 hwahcp->hwahc_pm = NULL;
3400         }
3401         mutex_exit(&hwahcp->hwahc_mutex);
3402 
3403         USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3404             "hwahc_destroy_pm_components: End");
3405 }
3406 
3407 /* mark component busy */
3408 static void
3409 hwahc_pm_busy_component(hwahc_state_t *hwahcp)
3410 {
3411         ASSERT(!mutex_owned(&hwahcp->hwahc_mutex));
3412 
3413         if (hwahcp->hwahc_pm != NULL) {
3414                 mutex_enter(&hwahcp->hwahc_mutex);
3415                 hwahcp->hwahc_pm->hwahc_pm_busy++;
3416                 USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3417                     "hwahc_pm_busy_component: %d",
3418                     hwahcp->hwahc_pm->hwahc_pm_busy);
3419                 mutex_exit(&hwahcp->hwahc_mutex);
3420 
3421                 if (pm_busy_component(hwahcp->hwahc_dip, 0) !=
3422                     DDI_SUCCESS) {
3423                         mutex_enter(&hwahcp->hwahc_mutex);
3424                         hwahcp->hwahc_pm->hwahc_pm_busy--;
3425                         USB_DPRINTF_L2(PRINT_MASK_PM,
3426                             hwahcp->hwahc_log_handle,
3427                             "hwahc_pm_busy_component failed: %d",
3428                             hwahcp->hwahc_pm->hwahc_pm_busy);
3429                         mutex_exit(&hwahcp->hwahc_mutex);
3430                 }
3431         }
3432 }
3433 
3434 /* mark component idle */
3435 static void
3436 hwahc_pm_idle_component(hwahc_state_t *hwahcp)
3437 {
3438         ASSERT(!mutex_owned(&hwahcp->hwahc_mutex));
3439 
3440         if (hwahcp->hwahc_pm != NULL) {
3441 
3442                 if (pm_idle_component(hwahcp->hwahc_dip, 0) ==
3443                     DDI_SUCCESS) {
3444                         mutex_enter(&hwahcp->hwahc_mutex);
3445                         ASSERT(hwahcp->hwahc_pm->hwahc_pm_busy > 0);
3446                         hwahcp->hwahc_pm->hwahc_pm_busy--;
3447                         USB_DPRINTF_L4(PRINT_MASK_PM,
3448                             hwahcp->hwahc_log_handle,
3449                             "hwahc_pm_idle_component: %d",
3450                             hwahcp->hwahc_pm->hwahc_pm_busy);
3451                         mutex_exit(&hwahcp->hwahc_mutex);
3452                 }
3453         }
3454 }
3455 
3456 /*
3457  * hwahc_power :
3458  *      Power entry point, the workhorse behind pm_raise_power, pm_lower_power,
3459  *      usb_req_raise_power and usb_req_lower_power.
3460  */
3461 /* ARGSUSED */
3462 static int
3463 hwahc_power(dev_info_t *dip, int comp, int level)
3464 {
3465         hwahc_state_t   *hwahcp;
3466         hwahc_power_t   *pm;
3467         int             rval = USB_FAILURE;
3468 
3469         hwahcp = ddi_get_soft_state(hwahc_statep, ddi_get_instance(dip));
3470 
3471         if (hwahcp == NULL) {
3472 
3473                 return (DDI_FAILURE);
3474         }
3475 
3476         USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3477             "hwahc_power: dip = 0x%p", (void *)dip);
3478 
3479         mutex_enter(&hwahcp->hwahc_mutex);
3480 
3481         if (hwahcp->hwahc_pm == NULL) {
3482 
3483                 goto done;
3484         }
3485 
3486         pm = hwahcp->hwahc_pm;
3487 
3488         /* Check if we are transitioning to a legal power level */
3489         if (USB_DEV_PWRSTATE_OK(pm->hwahc_pwr_states, level)) {
3490                 USB_DPRINTF_L2(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3491                     "hwahc_power: illegal power level = %d "
3492                     "pwr_states: %x", level, pm->hwahc_pwr_states);
3493 
3494                 goto done;
3495         }
3496 
3497         switch (level) {
3498         case USB_DEV_OS_PWR_OFF :
3499                 rval = hwahc_pwrlvl0(hwahcp);
3500 
3501                 break;
3502         case USB_DEV_OS_PWR_1:
3503                 rval = hwahc_pwrlvl1(hwahcp);
3504 
3505                 break;
3506         case USB_DEV_OS_PWR_2:
3507                 rval = hwahc_pwrlvl2(hwahcp);
3508 
3509                 break;
3510         case USB_DEV_OS_FULL_PWR :
3511                 rval = hwahc_pwrlvl3(hwahcp);
3512 
3513                 break;
3514         }
3515 done:
3516         mutex_exit(&hwahcp->hwahc_mutex);
3517 
3518         return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
3519 }
3520 
3521 /*
3522  * hwahc_pwrlvl0:
3523  * Functions to handle power transition for OS levels 0 -> 3
3524  *      OS 0 <--> USB D3, no or minimal power
3525  */
3526 static int
3527 hwahc_pwrlvl0(hwahc_state_t *hwahcp)
3528 {
3529         int     rval;
3530 
3531         USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3532             "hwahc_pwrlvl0: %d", hwahcp->hwahc_pm->hwahc_pm_busy);
3533 
3534         switch (hwahcp->hwahc_dev_state) {
3535         case USB_DEV_ONLINE:
3536                 /* Deny the powerdown request if the device is busy */
3537                 if (hwahcp->hwahc_pm->hwahc_pm_busy != 0) {
3538                         USB_DPRINTF_L2(PRINT_MASK_PM,
3539                             hwahcp->hwahc_log_handle,
3540                             "hwahc_pwrlvl0: hwahc_pm is busy");
3541 
3542                         return (USB_FAILURE);
3543                 }
3544                 /*
3545                  * only when final_stop gets called, we allow the system
3546                  * to do PM on us. At this moment, we don't need to do
3547                  * more operations other than those in final_stop.
3548                  */
3549 
3550                 /* Issue USB D3 command to the device here */
3551                 rval = usb_set_device_pwrlvl3(hwahcp->hwahc_dip);
3552                 ASSERT(rval == USB_SUCCESS);
3553 
3554                 hwahcp->hwahc_dev_state = USB_DEV_PWRED_DOWN;
3555 
3556                 hwahcp->hwahc_pm->hwahc_current_power = USB_DEV_OS_PWR_OFF;
3557 
3558                 break;
3559         case USB_DEV_DISCONNECTED:
3560         case USB_DEV_SUSPENDED:
3561         case USB_DEV_PWRED_DOWN:
3562         default:
3563                 break;
3564         }
3565 
3566         return (USB_SUCCESS);
3567 }
3568 
3569 /*
3570  * hwahc_pwrlvl1:
3571  *      Functions to handle power transition to OS levels -> 2
3572  *      OS level 1 <--> D2
3573  */
3574 static int
3575 hwahc_pwrlvl1(hwahc_state_t *hwahcp)
3576 {
3577         int     rval;
3578 
3579         USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3580             "hwahc_pwrlvl1:");
3581 
3582         /* Issue USB D2 command to the device here */
3583         rval = usb_set_device_pwrlvl2(hwahcp->hwahc_dip);
3584         ASSERT(rval == USB_SUCCESS);
3585 
3586         return (USB_FAILURE);
3587 }
3588 
3589 /*
3590  * hwahc_pwrlvl2:
3591  *      Functions to handle power transition to OS levels -> 1
3592  *      OS leve 2 <--> D1
3593  */
3594 static int
3595 hwahc_pwrlvl2(hwahc_state_t *hwahcp)
3596 {
3597         int     rval;
3598 
3599         USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3600             "hwahc_pwrlvl2:");
3601 
3602         /* Issue USB D1 command to the device here */
3603         rval = usb_set_device_pwrlvl1(hwahcp->hwahc_dip);
3604         ASSERT(rval == USB_SUCCESS);
3605 
3606         return (USB_FAILURE);
3607 }
3608 
3609 
3610 /*
3611  * hwahc_pwrlvl3:
3612  *      Functions to handle power transition to OS level -> 0
3613  *      OS level 3 <--> D0 (full power)
3614  */
3615 static int
3616 hwahc_pwrlvl3(hwahc_state_t *hwahcp)
3617 {
3618         USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3619             "hwahc_pwrlvl3: %d", hwahcp->hwahc_pm->hwahc_pm_busy);
3620 
3621         ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
3622 
3623         switch (hwahcp->hwahc_dev_state) {
3624         case USB_DEV_PWRED_DOWN:
3625                 /* Issue USB D0 command to the device here */
3626                 (void) usb_set_device_pwrlvl0(hwahcp->hwahc_dip);
3627 
3628                 /*
3629                  * Due to our current PM policy, it's not possible
3630                  * for hwa to be in USB_DEV_PWRED_DOWN between
3631                  * initial_start and final_stop. If it's PWRED_DOWN,
3632                  * it should not start. We don't need to resume
3633                  * soft or hardware state in this case.
3634                  */
3635                 if (hwahcp->hwahc_hw_state == HWAHC_HW_STOPPED) {
3636                         /* no need to start hc */
3637                         hwahcp->hwahc_dev_state = USB_DEV_ONLINE;
3638                         hwahcp->hwahc_pm->hwahc_current_power =
3639                             USB_DEV_OS_FULL_PWR;
3640 
3641                         return (USB_SUCCESS);
3642                 }
3643 
3644                 hwahcp->hwahc_pm->hwahc_current_power = USB_DEV_OS_FULL_PWR;
3645 
3646                 /* FALLTHRU */
3647         case USB_DEV_ONLINE:
3648                 /* we are already in full power */
3649                 /* FALLTHRU */
3650         case USB_DEV_DISCONNECTED:
3651         case USB_DEV_SUSPENDED:
3652                 /*
3653                  * PM framework tries to put you in full power
3654                  * during system shutdown. If we are disconnected
3655                  * return success. Also, we should not change state
3656                  * when we are disconnected or suspended or about to
3657                  * transition to that state
3658                  */
3659 
3660                 return (USB_SUCCESS);
3661         default:
3662                 USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3663                     "hwahc_pwrlvl3: illegal dev_state=%d",
3664                     hwahcp->hwahc_dev_state);
3665 
3666 
3667                 return (USB_FAILURE);
3668         }
3669 }
3670 
3671 /*
3672  * Host power management: stop channel
3673  *      See Section 4.16.2.1 for details
3674  *      See Section 8.1.0 for HWA suspend/resume
3675  */
3676 static int
3677 hwahc_hc_channel_suspend(hwahc_state_t *hwahcp)
3678 {
3679         int                     rval;
3680 
3681         USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3682             "hwahc_hc_channel_suspend:");
3683 
3684         ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
3685 
3686         /* no need to suspend if host hw was not started */
3687         if (hwahcp->hwahc_hw_state != HWAHC_HW_STARTED) {
3688                 USB_DPRINTF_L3(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3689                     "hwahc_hc_channel_suspend: hw already stopped");
3690 
3691                 return (USB_SUCCESS);
3692         }
3693 
3694         if (hwahcp->hwahc_hw_state == HWAHC_HW_CH_SUSPEND) {
3695                 USB_DPRINTF_L3(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3696                     "hwahc_hc_channel_suspend: already suspended");
3697 
3698                 return (USB_SUCCESS);
3699         }
3700 
3701         mutex_exit(&hwahcp->hwahc_mutex);
3702         /* suspend host, refer to WUSB 1.0 spec 8.5.3.14 */
3703         rval = wusb_hc_stop_ch(&hwahcp->hwahc_hc_data, 10000); /* 10ms */
3704         mutex_enter(&hwahcp->hwahc_mutex);
3705         if (rval != USB_SUCCESS) {
3706                 USB_DPRINTF_L2(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3707                     "hwahc_hc_channel_suspend: wusb channel stop fails");
3708 
3709                 return (rval);
3710         }
3711 
3712         hwahcp->hwahc_hw_state = HWAHC_HW_CH_SUSPEND;
3713 
3714         return (USB_SUCCESS);
3715 }
3716 
3717 /*
3718  * Parse security descriptors, see T.8-43
3719  *      put result in secrt_data
3720  */
3721 static int
3722 hwahc_parse_security_data(wusb_secrt_data_t *secrt_data,
3723         usb_cfg_data_t *cfg_data)
3724 {
3725         int             i, j;
3726         usb_cvs_data_t  *cvs_data;
3727         size_t          count, len;
3728 
3729         if ((secrt_data == NULL) || (cfg_data == NULL)) {
3730                 return (USB_INVALID_ARGS);
3731         }
3732 
3733         for (i = 0; i < cfg_data->cfg_n_cvs; i++) {
3734                 cvs_data = &cfg_data->cfg_cvs[i];
3735                 if (cvs_data == NULL) {
3736                         continue;
3737                 }
3738                 if (cvs_data->cvs_buf[1] == USB_DESCR_TYPE_SECURITY) {
3739                         count = usb_parse_data("ccsc",
3740                             cvs_data->cvs_buf, cvs_data->cvs_buf_len,
3741                             (void *)&secrt_data->secrt_descr,
3742                             (size_t)USB_SECURITY_DESCR_SIZE);
3743                         if (count != USB_SECURITY_DESCR_SIZE) {
3744 
3745                                 return (USB_FAILURE);
3746                         } else {
3747                                 secrt_data->secrt_n_encry =
3748                                     secrt_data->secrt_descr.bNumEncryptionTypes;
3749                                 len = sizeof (usb_encryption_descr_t) *
3750                                     secrt_data->secrt_n_encry;
3751 
3752                                 secrt_data->secrt_encry_descr =
3753                                     (usb_encryption_descr_t *)kmem_alloc(len,
3754                                     KM_SLEEP);
3755 
3756                                 for (j = 0; j < secrt_data->secrt_n_encry;
3757                                     j++) {
3758                                         cvs_data =
3759                                             &cfg_data->cfg_cvs[i + j + 1];
3760                                         if (cvs_data->cvs_buf[1] !=
3761                                             USB_DESCR_TYPE_ENCRYPTION) {
3762                                                 kmem_free(secrt_data->
3763                                                     secrt_encry_descr, len);
3764 
3765                                                 return (USB_FAILURE);
3766                                         }
3767 
3768                                         /* Table 7-34 */
3769                                         count = usb_parse_data("ccccc",
3770                                             cvs_data->cvs_buf,
3771                                             cvs_data->cvs_buf_len,
3772                                             (void *)&secrt_data->
3773                                             secrt_encry_descr[j],
3774                                             USB_ENCRYPTION_DESCR_SIZE);
3775                                         if (count !=
3776                                             USB_ENCRYPTION_DESCR_SIZE) {
3777                                                 kmem_free(secrt_data->
3778                                                     secrt_encry_descr, len);
3779 
3780                                                 return (USB_FAILURE);
3781 
3782                                         }
3783                                 }
3784                                 return (USB_SUCCESS);
3785                         }
3786                 }
3787         }
3788 
3789         return (USB_FAILURE);
3790 }
3791 
3792 /* initialize wusb_hc_data_t structure */
3793 static void
3794 hwahc_hc_data_init(hwahc_state_t *hwahcp)
3795 {
3796         wusb_hc_data_t  *hc_data = &hwahcp->hwahc_hc_data;
3797 
3798         hc_data->hc_dip = hwahcp->hwahc_dip;
3799         hc_data->hc_private_data = (void *)hwahcp;
3800 
3801         (void) memset(hc_data->hc_chid, 0, sizeof (hc_data->hc_chid));
3802 
3803         hc_data->hc_num_mmcies = hwahcp->hwahc_wa_data.wa_descr.bNumMMCIEs;
3804 
3805         ASSERT(hc_data->hc_num_mmcies != 0);
3806 
3807         hc_data->hc_mmcie_list = kmem_zalloc((hc_data->hc_num_mmcies *
3808             sizeof (wusb_ie_header_t *)), KM_SLEEP);
3809 
3810         /* initialize frequently used IE */
3811         hc_data->hc_alive_ie.bIEIdentifier = WUSB_IE_DEV_KEEPALIVE;
3812 
3813         /* register callbacks */
3814         hc_data->disconnect_dev = hwahc_disconnect_dev;
3815         hc_data->reconnect_dev = hwahc_reconnect_dev;
3816         hc_data->create_child = hwahc_create_child;
3817         hc_data->destroy_child = hwahc_destroy_child;
3818 
3819         /* HWA HC operation functions */
3820         hc_data->set_encrypt = hwahc_set_encrypt;
3821         hc_data->set_ptk = hwahc_set_ptk;
3822         hc_data->set_gtk = hwahc_set_gtk;
3823         hc_data->set_device_info = hwahc_set_device_info;
3824         hc_data->set_cluster_id = hwahc_set_cluster_id;
3825         hc_data->set_stream_idx = hwahc_set_stream_idx;
3826         hc_data->set_wusb_mas = hwahc_set_wusb_mas;
3827         hc_data->add_mmc_ie = hwahc_add_mmc_ie;
3828         hc_data->rem_mmc_ie = hwahc_remove_mmc_ie;
3829         hc_data->stop_ch = hwahc_stop_ch;
3830         hc_data->set_num_dnts = hwahc_set_num_dnts;
3831         hc_data->get_time = hwahc_get_time;
3832 
3833         hc_data->hc_num_ports = hwahcp->hwahc_wa_data.wa_descr.bNumPorts;
3834 
3835         hc_data->hc_cd_list_length = (sizeof (dev_info_t **)) *
3836             (hc_data->hc_num_ports + 1);
3837 
3838         hc_data->hc_children_dips = (dev_info_t **)kmem_zalloc(
3839             hc_data->hc_cd_list_length, KM_SLEEP);
3840         hc_data->hc_usba_devices = (usba_device_t **)kmem_zalloc(
3841             hc_data->hc_cd_list_length, KM_SLEEP);
3842         hc_data->hc_dev_infos = (wusb_dev_info_t **)kmem_zalloc(
3843             hc_data->hc_cd_list_length, KM_SLEEP);
3844 
3845         mutex_init(&hc_data->hc_mutex, NULL, MUTEX_DRIVER, NULL);
3846 }
3847 
3848 /* deinitialize wusb_hc_data_t structure */
3849 static void
3850 hwahc_hc_data_fini(hwahc_state_t *hwahcp)
3851 {
3852         int                     i;
3853         wusb_hc_data_t          *hc_data = &hwahcp->hwahc_hc_data;
3854         wusb_ie_header_t        *hdr;
3855 
3856 #ifdef DEBUG
3857         usb_port_t      port;
3858 #endif
3859 
3860         if (hc_data->hc_mmcie_list) {
3861                 /* Free all recorded IEs except statically allocated IEs */
3862                 for (i = 0; i < hc_data->hc_num_mmcies; i++) {
3863                         if (hc_data->hc_mmcie_list[i] != NULL) {
3864                                 hdr = hc_data->hc_mmcie_list[i];
3865                                 if ((hdr->bIEIdentifier !=
3866                                     WUSB_IE_DEV_KEEPALIVE)) {
3867                                         kmem_free(hdr, hdr->bLength);
3868                                 }
3869                                 hc_data->hc_mmcie_list[i] = NULL;
3870                         }
3871                 }
3872 
3873                 kmem_free(hc_data->hc_mmcie_list,
3874                     hc_data->hc_num_mmcies * sizeof (wusb_ie_header_t *));
3875         }
3876 
3877         if (hc_data->hc_cluster_id) {
3878                 wusb_hc_free_cluster_id(hc_data->hc_cluster_id);
3879         }
3880 
3881         if (hc_data->hc_cc_list) {
3882                 wusb_hc_free_cc_list(hc_data->hc_cc_list);
3883         }
3884 
3885 #ifdef DEBUG
3886         for (port = 1; port <= hc_data->hc_num_ports; port++) {
3887                 ASSERT(hc_data->hc_usba_devices[port] == NULL);
3888                 ASSERT(hc_data->hc_children_dips[port] == NULL);
3889                 ASSERT(hc_data->hc_dev_infos[port] == NULL);
3890         }
3891 #endif
3892 
3893         kmem_free(hc_data->hc_children_dips, hc_data->hc_cd_list_length);
3894         kmem_free(hc_data->hc_usba_devices, hc_data->hc_cd_list_length);
3895         kmem_free(hc_data->hc_dev_infos, hc_data->hc_cd_list_length);
3896 
3897         mutex_destroy(&hc_data->hc_mutex);
3898 }
3899 
3900 /* fully start the HWA hw */
3901 static int
3902 hwahc_hc_initial_start(hwahc_state_t *hwahcp)
3903 {
3904         uint8_t stream_idx;
3905         uint8_t mas[WUSB_SET_WUSB_MAS_LEN];
3906         int     rval;
3907         uint8_t cluster_id = 0;
3908 
3909         USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3910             "hwahc_hc_initial_start:");
3911 
3912         ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
3913 
3914         if (hwahcp->hwahc_dev_state != USB_DEV_ONLINE) {
3915                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3916                     "hwahc_hc_initial_start: invalid dev state = %d",
3917                     hwahcp->hwahc_dev_state);
3918 
3919                 return (USB_INVALID_REQUEST);
3920         }
3921 
3922         if (hwahcp->hwahc_hw_state != HWAHC_HW_STOPPED) {
3923                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3924                     "hwahc_hc_initial_start: invalid hw state");
3925 
3926                 return (USB_INVALID_REQUEST);
3927         }
3928 
3929         /*
3930          * start beacon of radio layer
3931          * We're not sure if previouse channel is occupied or not. So, let
3932          * UWB allocates a free channel for this hwa. Then we can start
3933          * beacon.
3934          */
3935         hwahcp->hwahc_hc_data.hc_channel =
3936             uwb_allocate_channel(hwahcp->hwahc_dip);
3937         if (hwahcp->hwahc_hc_data.hc_channel  == 0) {
3938                 USB_DPRINTF_L2(PRINT_MASK_ATTA,
3939                     hwahcp->hwahc_log_handle,
3940                     "wusb_hc_initial_start: channel = %d",
3941                     hwahcp->hwahc_hc_data.hc_channel);
3942                 return (USB_FAILURE);
3943         }
3944 
3945         if ((rval = uwb_start_beacon(hwahcp->hwahc_dip,
3946             hwahcp->hwahc_hc_data.hc_channel)) != USB_SUCCESS) {
3947                 USB_DPRINTF_L2(PRINT_MASK_ATTA,
3948                     hwahcp->hwahc_log_handle,
3949                     "wusb_hc_initial_start: start uwb beacon failed");
3950 
3951                 return (rval);
3952         }
3953 
3954         mutex_exit(&hwahcp->hwahc_mutex);
3955         /* reset wire adapter */
3956         rval = wusb_wa_reset(&hwahcp->hwahc_wa_data,
3957             hwahcp->hwahc_default_pipe);
3958         mutex_enter(&hwahcp->hwahc_mutex);
3959         if (rval != SUCCESS) {
3960                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3961                     "hwahc_hc_initial_start: reset wa fails");
3962 
3963                 goto err;
3964         }
3965 
3966         /* reuse the old cluster id or assign one */
3967         if (hwahcp->hwahc_hc_data.hc_cluster_id) {
3968                 cluster_id = hwahcp->hwahc_hc_data.hc_cluster_id;
3969         } else {
3970                 cluster_id = wusb_hc_get_cluster_id();
3971                 if (cluster_id == 0) {
3972                         USB_DPRINTF_L2(PRINT_MASK_ATTA,
3973                             hwahcp->hwahc_log_handle,
3974                             "hwahc_hc_initial_start: cannot get cluster id");
3975                         rval = USB_NO_RESOURCES;
3976 
3977                         goto err;
3978                 }
3979         }
3980 
3981         mutex_exit(&hwahcp->hwahc_mutex);
3982         /* set cluster id for the wusb channel */
3983         rval = wusb_hc_set_cluster_id(&hwahcp->hwahc_hc_data, cluster_id);
3984         if (rval != USB_SUCCESS) {
3985                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3986                     "hwahc_hc_initial_start: set cluster id %d fails",
3987                     cluster_id);
3988                 mutex_enter(&hwahcp->hwahc_mutex);
3989 
3990                 goto err;
3991         }
3992 
3993         /* UWB should be responsible for assigning stream index */
3994         stream_idx = 1;
3995 
3996         rval = wusb_hc_set_stream_idx(&hwahcp->hwahc_hc_data, stream_idx);
3997         if (rval != USB_SUCCESS) {
3998                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3999                     "hwahc_hc_initial_start: set stream idx %d fails",
4000                     stream_idx);
4001                 mutex_enter(&hwahcp->hwahc_mutex);
4002 
4003                 goto err;
4004         }
4005 
4006         /* set dnts slot */
4007         rval = wusb_hc_set_num_dnts(&hwahcp->hwahc_hc_data,
4008             HWAHC_DEFAULT_DNTS_INTERVAL, HWAHC_DEFAULT_DNTS_SLOT_NUM);
4009 
4010         if (rval != USB_SUCCESS) {
4011                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4012                     "hwahc_hc_initial_start: set num dnts fails");
4013                 mutex_enter(&hwahcp->hwahc_mutex);
4014 
4015                 goto err;
4016         }
4017 
4018         /* set host info IE */
4019         rval = wusb_hc_add_host_info(&hwahcp->hwahc_hc_data, stream_idx);
4020         if (rval != USB_SUCCESS) {
4021                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4022                     "hwahc_hc_initial_start: add hostinfo ie fails");
4023                 mutex_enter(&hwahcp->hwahc_mutex);
4024 
4025                 goto err;
4026         }
4027 
4028         /* reserve MAS slots for the host, need a way to assign */
4029         (void) memset(mas, 0xff, WUSB_SET_WUSB_MAS_LEN);
4030         mas[0] = 0xf0;  /* the first 4 slots are for beacons */
4031         rval = wusb_hc_set_wusb_mas(&hwahcp->hwahc_hc_data, mas);
4032         mutex_enter(&hwahcp->hwahc_mutex);
4033         if (rval != USB_SUCCESS) {
4034                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4035                     "hwahc_hc_initial_start: set wusb mas fails");
4036 
4037                 goto err;
4038         }
4039 
4040         /* record the available MAS slots */
4041         (void) memcpy(hwahcp->hwahc_hc_data.hc_mas, mas, WUSB_SET_WUSB_MAS_LEN);
4042 
4043         /* Set initial GTK/TKID to random values */
4044         (void) random_get_pseudo_bytes(dft_gtk, 16);
4045         (void) random_get_pseudo_bytes(dft_gtkid, 3);
4046 
4047         /* set default GTK, need a way to dynamically compute it */
4048         mutex_exit(&hwahcp->hwahc_mutex);
4049         rval = wusb_hc_set_gtk(&hwahcp->hwahc_hc_data, dft_gtk, dft_gtkid);
4050         if (rval != USB_SUCCESS) {
4051                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4052                     "hwahc_hc_initial_start: set gtk fails");
4053                 mutex_enter(&hwahcp->hwahc_mutex);
4054 
4055                 goto err;
4056         }
4057 
4058         /* enable wire adapter */
4059         rval = wusb_wa_enable(&hwahcp->hwahc_wa_data,
4060             hwahcp->hwahc_default_pipe);
4061         if (rval != USB_SUCCESS) {
4062                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4063                     "hwahc_hc_initial_start: enable wa fails");
4064                 mutex_enter(&hwahcp->hwahc_mutex);
4065 
4066                 goto err;
4067         }
4068 
4069         /* Start Notification endpoint */
4070         rval = wusb_wa_start_nep(&hwahcp->hwahc_wa_data, USB_FLAGS_SLEEP);
4071 
4072         if (rval != USB_SUCCESS) {
4073                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4074                     "hwahc_hc_initial_start: start notification ep fails");
4075                 (void) wusb_wa_disable(&hwahcp->hwahc_wa_data,
4076                     hwahcp->hwahc_default_pipe);
4077 
4078                 mutex_enter(&hwahcp->hwahc_mutex);
4079 
4080                 goto err;
4081         }
4082 
4083         mutex_enter(&hwahcp->hwahc_mutex);
4084 
4085         /*
4086          * Handle transfer results on bulk-in ep
4087          * The bulk-in ep needs to be polled no matter the completion
4088          * notification is received or not to avoid miss result.
4089          */
4090         rval = hwahc_start_result_thread(hwahcp);
4091         if (rval != USB_SUCCESS) {
4092                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4093                     "hwahc_hc_initial_start: start result thread fails, "
4094                     "rval = %d", rval);
4095                 mutex_exit(&hwahcp->hwahc_mutex);
4096                 wusb_wa_stop_nep(&hwahcp->hwahc_wa_data);
4097                 (void) wusb_wa_disable(&hwahcp->hwahc_wa_data,
4098                     hwahcp->hwahc_default_pipe);
4099                 mutex_enter(&hwahcp->hwahc_mutex);
4100 
4101                 goto err;
4102         }
4103         USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4104             "hwahc_hc_initial_start: start result thread success");
4105 
4106         hwahcp->hwahc_hw_state = HWAHC_HW_STARTED;
4107         hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_OPERATIONAL_STATE;
4108 
4109         /* Don't do PM on an active beacon hwa until explicitly stopped */
4110         mutex_exit(&hwahcp->hwahc_mutex);
4111         hwahc_pm_busy_component(hwahcp);
4112         mutex_enter(&hwahcp->hwahc_mutex);
4113 
4114         return (USB_SUCCESS);
4115 
4116 err:
4117         if (cluster_id != 0) {
4118                 wusb_hc_free_cluster_id(cluster_id);
4119         }
4120 
4121         mutex_exit(&hwahcp->hwahc_mutex);
4122         (void) uwb_stop_beacon(hwahcp->hwahc_dip);
4123         mutex_enter(&hwahcp->hwahc_mutex);
4124 
4125         return (rval);
4126 }
4127 
4128 /* entirely stop the HWA from working */
4129 static int
4130 hwahc_hc_final_stop(hwahc_state_t *hwahcp)
4131 {
4132         USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4133             "hwahc_hc_final_stop:");
4134 
4135         ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
4136 
4137         if (hwahcp->hwahc_hw_state == HWAHC_HW_STOPPED) {
4138                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4139                     "hwahc_hc_final_stop: already stopped");
4140 
4141                 return (USB_SUCCESS);
4142         }
4143 
4144         if (hwahcp->hwahc_dev_state == USB_DEV_SUSPENDED) {
4145                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4146                     "hwahc_hc_final_stop: invalid dev state = %d",
4147                     hwahcp->hwahc_dev_state);
4148 
4149                 return (USB_INVALID_REQUEST);
4150         }
4151 
4152         /* might have been powered down before detaching */
4153         mutex_exit(&hwahcp->hwahc_mutex);
4154         (void) pm_raise_power(hwahcp->hwahc_dip, 0, USB_DEV_OS_FULL_PWR);
4155         mutex_enter(&hwahcp->hwahc_mutex);
4156 
4157         if (hwahcp->hwahc_dev_state != USB_DEV_DISCONNECTED) {
4158                 /* notify children the host is going to stop */
4159                 (void) hwahc_hc_channel_suspend(hwahcp);
4160 
4161                 /* release mutex here to avoid deadlock with exc_cb */
4162                 mutex_exit(&hwahcp->hwahc_mutex);
4163 
4164                 /* stop notification endpoint */
4165                 wusb_wa_stop_nep(&hwahcp->hwahc_wa_data);
4166                 mutex_enter(&hwahcp->hwahc_mutex);
4167 
4168                 /* stop bulk-in ept from listening result */
4169                 hwahc_stop_result_thread(hwahcp);
4170 
4171                 /* drain the device notifications */
4172                 hwahc_drain_notif_queue(hwahcp);
4173 
4174                 /* disable wire adapter */
4175                 mutex_exit(&hwahcp->hwahc_mutex);
4176                 (void) wusb_wa_disable(&hwahcp->hwahc_wa_data,
4177                     hwahcp->hwahc_default_pipe);
4178 
4179                 /* stop beaconing. Not necessary to unreserve mas */
4180                 (void) uwb_stop_beacon(hwahcp->hwahc_dip);
4181 
4182                 wusb_hc_rem_host_info(&hwahcp->hwahc_hc_data);
4183 
4184                 /* Manually remove all connected children */
4185                 hwahc_run_callbacks(hwahcp, USBA_EVENT_TAG_HOT_REMOVAL);
4186 
4187                 /* delete all the children */
4188                 (void) hwahc_cleanup_child(hwahcp->hwahc_dip);
4189                 mutex_enter(&hwahcp->hwahc_mutex);
4190         }
4191 
4192         /*
4193          * we make it busy at hwahc_hc_initial_start(). This idle operation
4194          * is to match that busy operation.
4195          * All other busy/idle operations should have been matched.
4196          */
4197         if ((hwahcp->hwahc_hw_state == HWAHC_HW_STARTED) &&
4198             (hwahcp->hwahc_hc_soft_state == HWAHC_CTRL_OPERATIONAL_STATE)) {
4199                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4200                     "hwahc_hc_final_stop: pm_busy=%d",
4201                     hwahcp->hwahc_pm->hwahc_pm_busy);
4202                 mutex_exit(&hwahcp->hwahc_mutex);
4203                 hwahc_pm_idle_component(hwahcp);
4204                 mutex_enter(&hwahcp->hwahc_mutex);
4205         }
4206 
4207         hwahcp->hwahc_hw_state = HWAHC_HW_STOPPED;
4208         if (hwahcp->hwahc_hc_soft_state == HWAHC_CTRL_OPERATIONAL_STATE) {
4209                 hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_INIT_STATE;
4210         }
4211 
4212         return (USB_SUCCESS);
4213 }
4214 
4215 /*
4216  * init WUSB channel, this is only part of the full hw start operations
4217  * including setting wusb channel stream idx, wusb MAS slots reservation
4218  * and adding host info IE
4219  */
4220 static int
4221 hwahc_hc_channel_start(hwahc_state_t *hwahcp)
4222 {
4223         uint8_t                 stream_idx;
4224         uint8_t                 mas[WUSB_SET_WUSB_MAS_LEN];
4225         int                     rval;
4226 
4227         USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4228             "hwahc_hc_channel_start:");
4229 
4230         ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
4231 
4232         if (hwahcp->hwahc_dev_state != USB_DEV_ONLINE) {
4233                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4234                     "hwahc_hc_channel_start: invalid dev_state = %d",
4235                     hwahcp->hwahc_dev_state);
4236 
4237                 return (USB_INVALID_REQUEST);
4238         }
4239 
4240         if (hwahcp->hwahc_hw_state != HWAHC_HW_CH_STOPPED) {
4241                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4242                     "hwahc_hc_channel_start: invalid hw state");
4243 
4244                 return (USB_INVALID_REQUEST);
4245         }
4246 
4247         /* set stream idx */
4248         stream_idx = 1;
4249 
4250         mutex_exit(&hwahcp->hwahc_mutex);
4251         rval = wusb_hc_set_stream_idx(&hwahcp->hwahc_hc_data, stream_idx);
4252         if (rval != USB_SUCCESS) {
4253                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4254                     "hwahc_hc_channel_start: set stream idx %d fails",
4255                     stream_idx);
4256                 mutex_enter(&hwahcp->hwahc_mutex);
4257 
4258                 return (rval);
4259         }
4260 
4261         /* reserve MAS slots for the host. Should be allocated by UWB */
4262         (void) memset(mas, 0xff, WUSB_SET_WUSB_MAS_LEN);
4263         mas[0] = 0xf0;  /* for beacons */
4264         rval = wusb_hc_set_wusb_mas(&hwahcp->hwahc_hc_data, mas);
4265 
4266         if (rval != USB_SUCCESS) {
4267                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4268                     "hwahc_hc_channel_start: set wusb mas fails");
4269                 mutex_enter(&hwahcp->hwahc_mutex);
4270 
4271                 return (rval);
4272         }
4273         (void) memcpy(hwahcp->hwahc_hc_data.hc_mas, mas, WUSB_SET_WUSB_MAS_LEN);
4274 
4275         /* set host info IE */
4276         rval = wusb_hc_add_host_info(&hwahcp->hwahc_hc_data, stream_idx);
4277 
4278         if (rval != USB_SUCCESS) {
4279                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4280                     "hwahc_hc_channel_start: add hostinfo ie fails");
4281                 mutex_enter(&hwahcp->hwahc_mutex);
4282 
4283                 return (rval);
4284         }
4285 
4286         mutex_enter(&hwahcp->hwahc_mutex);
4287         hwahcp->hwahc_hw_state = HWAHC_HW_STARTED;
4288         hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_OPERATIONAL_STATE;
4289 
4290         /* do not PM this device, once we're ready to accept DN */
4291         mutex_exit(&hwahcp->hwahc_mutex);
4292         hwahc_pm_busy_component(hwahcp);
4293         mutex_enter(&hwahcp->hwahc_mutex);
4294 
4295         return (USB_SUCCESS);
4296 }
4297 
4298 /*
4299  * stop WUSB channel, this only stops part of the hw function
4300  * it mainly unreserve the MAS slots and remove the host info IE
4301  */
4302 static int
4303 hwahc_hc_channel_stop(hwahc_state_t *hwahcp)
4304 {
4305         uint8_t                 stream_idx;
4306         uint8_t                 mas[WUSB_SET_WUSB_MAS_LEN];
4307         int                     rval;
4308 
4309         USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4310             "hwahc_hc_channel_stop:");
4311 
4312         ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
4313 
4314         if (hwahcp->hwahc_dev_state != USB_DEV_ONLINE) {
4315                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4316                     "hwahc_hc_channel_stop: invalid dev state %d",
4317                     hwahcp->hwahc_dev_state);
4318 
4319                 return (USB_INVALID_REQUEST);
4320         }
4321 
4322         if (hwahcp->hwahc_hw_state == HWAHC_HW_CH_STOPPED) {
4323                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4324                     "hwahc_hc_channel_stop: already partially stopped");
4325 
4326                 return (USB_SUCCESS);
4327         }
4328 
4329         if (hwahcp->hwahc_hw_state == HWAHC_HW_STOPPED) {
4330                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4331                     "hwahc_hc_channel_stop: already stopped, invalid state");
4332 
4333                 return (USB_INVALID_REQUEST);
4334         }
4335 
4336         /* send host disconect IE so that the children know to disconnect */
4337         mutex_exit(&hwahcp->hwahc_mutex);
4338         rval = wusb_hc_send_host_disconnect(&hwahcp->hwahc_hc_data);
4339         if (rval != USB_SUCCESS) {
4340                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4341                     "hwahc_hc_channel_stop: send host disconnect ie fails");
4342 
4343                 mutex_enter(&hwahcp->hwahc_mutex);
4344 
4345                 return (rval);
4346         }
4347 
4348         /* remove host info IE */
4349         wusb_hc_rem_host_info(&hwahcp->hwahc_hc_data);
4350 
4351         /* unset stream idx */
4352         stream_idx = 0;
4353 
4354         rval = wusb_hc_set_stream_idx(&hwahcp->hwahc_hc_data, stream_idx);
4355         if (rval != USB_SUCCESS) {
4356                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4357                     "hwahc_hc_channel_stop: set stream idx 0 fails");
4358                 mutex_enter(&hwahcp->hwahc_mutex);
4359 
4360                 return (rval);
4361         }
4362 
4363         /* unreserve MAS slots */
4364         (void) memset(mas, 0, WUSB_SET_WUSB_MAS_LEN);
4365         rval = wusb_hc_set_wusb_mas(&hwahcp->hwahc_hc_data, mas);
4366 
4367         if (rval != USB_SUCCESS) {
4368                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4369                     "hwahc_hc_channel_stop: set null wusb mas fails");
4370                 mutex_enter(&hwahcp->hwahc_mutex);
4371 
4372                 return (rval);
4373         }
4374 
4375         mutex_enter(&hwahcp->hwahc_mutex);
4376         (void) memcpy(hwahcp->hwahc_hc_data.hc_mas, mas, WUSB_SET_WUSB_MAS_LEN);
4377 
4378         hwahcp->hwahc_hw_state = HWAHC_HW_CH_STOPPED;
4379 
4380         /* Channel is stopped, can be PM'ed */
4381         mutex_exit(&hwahcp->hwahc_mutex);
4382         hwahc_pm_idle_component(hwahcp);
4383         mutex_enter(&hwahcp->hwahc_mutex);
4384 
4385         return (USB_SUCCESS);
4386 }
4387 
4388 /* initialize data transfer related resources */
4389 static int
4390 hwahc_wa_start(hwahc_state_t *hwahcp)
4391 {
4392         int     rval;
4393 
4394         USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4395             "hwahc_wa_start:");
4396 
4397         /* get all rpipe descrs */
4398         if ((rval = wusb_wa_get_rpipe_descrs(&hwahcp->hwahc_wa_data,
4399             hwahcp->hwahc_default_pipe, PRINT_MASK_ATTA,
4400             hwahcp->hwahc_log_handle)) != USB_SUCCESS) {
4401                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4402                     "hwahc_wa_start: get rpipe descrs fails, rval=%d", rval);
4403 
4404                 return (rval);
4405         }
4406 
4407         /* open all data transfer epts */
4408         if ((rval = wusb_wa_open_pipes(&hwahcp->hwahc_wa_data)) !=
4409             USB_SUCCESS) {
4410                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4411                     "hwahc_wa_start: open pipes fails, rval=%d", rval);
4412                 (void) wusb_wa_disable(&hwahcp->hwahc_wa_data,
4413                     hwahcp->hwahc_default_pipe);
4414 
4415                 return (rval);
4416         }
4417 
4418         /* init notification list */
4419         usba_init_list(&hwahcp->hwahc_dn_notif_queue, NULL,
4420             hwahcp->hwahc_dev_data->dev_iblock_cookie);
4421 
4422         return (USB_SUCCESS);
4423 }
4424 
4425 /* deinitialize data transfer related resources */
4426 static void
4427 hwahc_wa_stop(hwahc_state_t *hwahcp)
4428 {
4429         USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4430             "hwahc_wa_stop:");
4431 
4432         usba_destroy_list(&hwahcp->hwahc_dn_notif_queue);
4433         wusb_wa_close_pipes(&hwahcp->hwahc_wa_data);
4434 }
4435 
4436 /*
4437  * HUBD related initialization
4438  * To mimic standard hub attach process to create a fake "root hub"
4439  * for HWA
4440  */
4441 static int
4442 hwahc_hub_attach(hwahc_state_t *hwahcp)
4443 {
4444         hubd_t          *hubd = NULL;
4445         dev_info_t      *dip = hwahcp->hwahc_dip;
4446         int             instance = ddi_get_instance(dip);
4447         int             i;
4448         int             rval;
4449 
4450         USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4451             "hwahc_hub_attach:");
4452 
4453         if (ndi_prop_create_boolean(DDI_DEV_T_NONE, dip,
4454             "wire-adapter") != NDI_SUCCESS) {
4455 
4456                 return (USB_FAILURE);
4457         }
4458 
4459         /* allocate hubd structure */
4460         hubd = hwahcp->hwahc_hubd = kmem_zalloc(sizeof (hubd_t), KM_SLEEP);
4461 
4462         hubd->h_log_handle = usb_alloc_log_hdl(dip, "husb", &hubd_errlevel,
4463             &hubd_errmask, &hubd_instance_debug, 0);
4464         hubd->h_usba_device = usba_get_usba_device(dip);
4465         hubd->h_usba_device->usb_is_wa = TRUE;
4466         hubd->h_dip = dip;
4467         hubd->h_instance = instance;
4468         hubd->h_ignore_pwr_budget = B_TRUE;
4469         hubd->h_cleanup_child = hwahc_cleanup_child;
4470 
4471         mutex_enter(&hubd->h_usba_device->usb_mutex);
4472         hubd->h_usba_device->usb_root_hubd = hubd;
4473         mutex_exit(&hubd->h_usba_device->usb_mutex);
4474 
4475         if (usb_get_dev_data(dip, &hubd->h_dev_data,
4476             USB_PARSE_LVL_IF, 0) != USB_SUCCESS) {
4477                 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
4478                     "cannot get dev_data");
4479 
4480                 goto fail;
4481         }
4482 
4483         /* init hubd mutex */
4484         mutex_init(HUBD_MUTEX(hubd), NULL, MUTEX_DRIVER,
4485             hubd->h_dev_data->dev_iblock_cookie);
4486 
4487         usb_free_descr_tree(dip, hubd->h_dev_data);
4488 
4489         hubd->h_init_state |= HUBD_LOCKS_DONE;
4490 
4491         /* register the instance to usba HUBDI */
4492         rval = usba_hubdi_register(dip, 0);
4493         if (rval != USB_SUCCESS) {
4494                 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
4495                     "usba_hubdi_register failed");
4496 
4497                 goto fail;
4498         }
4499 
4500         mutex_enter(HUBD_MUTEX(hubd));
4501         hubd->h_init_state |= HUBD_HUBDI_REGISTERED;
4502 
4503         hubd->h_ancestry_str = (char *)kmem_zalloc(HUBD_APID_NAMELEN,
4504             KM_SLEEP);
4505         hubd_get_ancestry_str(hubd);
4506 
4507         /* create cfgadm minor nodes */
4508         for (i = 1; i <= hwahcp->hwahc_wa_data.wa_descr.bNumPorts; i++) {
4509                 char ap_name[HUBD_APID_NAMELEN];
4510 
4511                 (void) snprintf(ap_name, HUBD_APID_NAMELEN, "%s%d",
4512                     hubd->h_ancestry_str, i);
4513                 USB_DPRINTF_L3(DPRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4514                     "ap_name=%s", ap_name);
4515 
4516                 if (ddi_create_minor_node(dip, ap_name, S_IFCHR,
4517                     (instance << HWAHC_MINOR_INSTANCE_SHIFT) | i,
4518                     DDI_NT_USB_ATTACHMENT_POINT, 0) != DDI_SUCCESS) {
4519                         USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
4520                             "cannot create attachment point node (%d)",
4521                             instance);
4522                         mutex_exit(HUBD_MUTEX(hubd));
4523 
4524                         goto fail;
4525                 }
4526         }
4527         i = hwahcp->hwahc_wa_data.wa_descr.bNumPorts;
4528         mutex_exit(HUBD_MUTEX(hubd));
4529 
4530         /* create hubd minor node */
4531         if (ddi_create_minor_node(dip, "hubd", S_IFCHR,
4532             instance << HWAHC_MINOR_INSTANCE_SHIFT,
4533             DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
4534                 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
4535                     "cannot create devctl minor node (%d)", instance);
4536 
4537                 goto fail;
4538         }
4539 
4540         mutex_enter(HUBD_MUTEX(hubd));
4541         hubd->h_init_state |= HUBD_MINOR_NODE_CREATED;
4542         mutex_exit(HUBD_MUTEX(hubd));
4543 
4544         if (ndi_prop_update_int(DDI_DEV_T_NONE, dip,
4545             "usb-port-count", i) != DDI_PROP_SUCCESS) {
4546                 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
4547                     "usb-port-count update failed");
4548         }
4549 
4550         return (USB_SUCCESS);
4551 
4552 fail:
4553         if (hwahc_hub_detach(hwahcp) != USB_SUCCESS) {
4554                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4555                     "fail to cleanup after hub attach failure");
4556         }
4557 
4558         return (USB_FAILURE);
4559 }
4560 
4561 /* HUBD related deinitialization */
4562 static int
4563 hwahc_hub_detach(hwahc_state_t *hwahcp)
4564 {
4565         hubd_t          *hubd = hwahcp->hwahc_hubd;
4566         dev_info_t      *dip = hwahcp->hwahc_dip;
4567 
4568         USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4569             "hwahc_hub_detach:");
4570 
4571         if ((hubd->h_init_state & HUBD_LOCKS_DONE) == 0) {
4572                 goto done;
4573         }
4574 
4575         if (hubd->h_init_state & HUBD_MINOR_NODE_CREATED) {
4576                 /* remove minor nodes */
4577                 ddi_remove_minor_node(dip, NULL);
4578         }
4579 
4580         if (hubd->h_init_state & HUBD_HUBDI_REGISTERED) {
4581                 /* unregister with usba HUBDI */
4582                 (void) usba_hubdi_unregister(dip);
4583         }
4584 
4585         if (hubd->h_init_state & HUBD_LOCKS_DONE) {
4586                 mutex_destroy(HUBD_MUTEX(hubd));
4587         }
4588 
4589         if (hubd->h_ancestry_str) {
4590                 kmem_free(hubd->h_ancestry_str, HUBD_APID_NAMELEN);
4591         }
4592 
4593 done:
4594         if (hubd->h_dev_data) {
4595                 /* unregister client from usba */
4596                 usb_client_detach(dip, hubd->h_dev_data);
4597         }
4598 
4599         usb_free_log_hdl(hubd->h_log_handle);
4600         kmem_free(hubd, sizeof (hubd_t));
4601         ddi_prop_remove_all(dip);
4602 
4603         return (USB_SUCCESS);
4604 }
4605 
4606 /* print security descrs */
4607 static void
4608 hwahc_print_secrt_data(hwahc_state_t *hwahcp)
4609 {
4610         int                     i;
4611         wusb_secrt_data_t       *secrt_data = &hwahcp->hwahc_secrt_data;
4612 
4613         USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4614             "The Host Wire Adapter security descriptor:");
4615         USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4616             "bLength = 0x%x\t\t bDescriptorType = 0x%x",
4617             secrt_data->secrt_descr.bLength,
4618             secrt_data->secrt_descr.bDescriptorType);
4619         USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4620             "wTotalLength = 0x%x\t bNumEncryptionTypes = 0x%x",
4621             secrt_data->secrt_descr.wTotalLength,
4622             secrt_data->secrt_descr.bNumEncryptionTypes);
4623 
4624         for (i = 0; i < secrt_data->secrt_n_encry; i++) {
4625                 USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4626                     "The Host Wire Adapter encryption descriptor %d:", i + 1);
4627                 USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4628                     "bLength = 0x%x\t\t bDescriptorType = 0x%x",
4629                     secrt_data->secrt_encry_descr[i].bLength,
4630                     secrt_data->secrt_encry_descr[i].bDescriptorType);
4631                 USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4632                     "bEncryptionType = 0x%x\t bEncryptionValue = 0x%x",
4633                     secrt_data->secrt_encry_descr[i].bEncryptionType,
4634                     secrt_data->secrt_encry_descr[i].bEncryptionValue);
4635                 USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4636                     "bAuthKeyIndex = 0x%x",
4637                     secrt_data->secrt_encry_descr[i].bAuthKeyIndex);
4638         }
4639 }
4640 
4641 /* drain device notifications */
4642 static void
4643 hwahc_drain_notif_queue(hwahc_state_t *hwahcp)
4644 {
4645         int     i;
4646 
4647         ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
4648 
4649         USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
4650             "hwahc_drain_notif_queue: started");
4651 
4652         if ((hwahcp->hwahc_notif_thread_id == NULL) &&
4653             (usba_list_entry_count(&hwahcp->hwahc_dn_notif_queue) != 0)) {
4654                 /* kick off a notif thread to drain the queue */
4655                 if (usb_async_req(hwahcp->hwahc_dip, hwahc_notif_thread,
4656                     (void *)hwahcp, 0) != USB_SUCCESS) {
4657                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
4658                             hwahcp->hwahc_log_handle,
4659                             "hwahc_drain_notif_queue: no notif thread started");
4660                 } else {
4661                         hwahcp->hwahc_notif_thread_id = (kthread_t *)1;
4662                 }
4663         }
4664 
4665         for (i = 0; i < HWAHC_NOTIF_DRAIN_TIMEOUT; i++) {
4666                 /* loop until the queue is completed or it timeouts */
4667                 if ((hwahcp->hwahc_notif_thread_id == NULL) &&
4668                     (usba_list_entry_count(&hwahcp->hwahc_dn_notif_queue) ==
4669                     0)) {
4670 
4671                         break;
4672                 }
4673                 mutex_exit(&hwahcp->hwahc_mutex);
4674                 delay(drv_usectohz(1000000));
4675                 mutex_enter(&hwahcp->hwahc_mutex);
4676         }
4677 
4678         /* cleanup the queue if not completed */
4679         while (usba_list_entry_count(&hwahcp->hwahc_dn_notif_queue) != 0) {
4680                 hwahc_dn_notif_list_t   *nlist;
4681 
4682                 nlist = (hwahc_dn_notif_list_t *)usba_rm_first_pvt_from_list(
4683                     &hwahcp->hwahc_dn_notif_queue);
4684                 ASSERT(nlist != NULL);
4685                 ASSERT(nlist->dn_notif != NULL);
4686                 usba_destroy_list(&nlist->notif_list);
4687                 kmem_free(nlist->dn_notif, nlist->dn_notif->bLength);
4688                 kmem_free(nlist, sizeof (hwahc_dn_notif_list_t));
4689         }
4690 
4691         USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
4692             "hwahc_drain_notif_queue: ended");
4693 }
4694 
4695 
4696 /* normal callback for notification ept */
4697 static void
4698 hwahc_intr_cb(usb_pipe_handle_t ph, struct usb_intr_req *reqp)
4699 {
4700         dev_info_t              *dip = (USBA_REQ2WRP(reqp))->wr_dip;
4701         hwahc_state_t           *hwahcp;
4702         mblk_t                  *data = reqp->intr_data;
4703 
4704         ASSERT(dip != NULL);
4705         hwahcp = ddi_get_soft_state(hwahc_statep, ddi_get_instance(dip));
4706         ASSERT(hwahcp != NULL);
4707 
4708         USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
4709             "hwahc_intr_cb: ph = 0x%p reqp = 0x%p", (void *)ph,
4710             (void *)reqp);
4711 
4712         ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
4713 
4714         if (data == NULL) {
4715                 usb_free_intr_req(reqp);
4716 
4717                 return;
4718         }
4719 
4720         /* handle the notification */
4721         hwahc_handle_notif(hwahcp, data);
4722 
4723         usb_free_intr_req(reqp);
4724 }
4725 
4726 /*
4727  * See Section 8.3.3.3 for Transfer Notification format and
4728  * Section 8.5.4 for HWA specific notifications.
4729  *      Three kinds of Notifications:
4730  *              - Transfer Completion
4731  *              - DN Received
4732  *              - BPST ADJ
4733  */
4734 /* handle the notification according to notification type */
4735 static void
4736 hwahc_handle_notif(hwahc_state_t *hwahcp, mblk_t *data)
4737 {
4738         int                     len;
4739         uint8_t                 *p;
4740         wa_notif_header_t       *hdr;
4741 
4742         if (data == NULL) {
4743 
4744                 return;
4745         }
4746 
4747         len = MBLKL(data);
4748         p = data->b_rptr;
4749         USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
4750             "hwahc_handle_notif: data len = %d", len);
4751 
4752         /*
4753          * according to WUSB 1.0/8.1.2, multiple notifications might be sent
4754          * at a time, need to parse one by one
4755          */
4756         while (len > 0) {
4757                 if (len < 2) {
4758                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
4759                             hwahcp->hwahc_log_handle,
4760                             "hwahc_handle_notif: short packet len = %d",
4761                             len);
4762 
4763                         break;
4764                 }
4765 
4766                 hdr = (wa_notif_header_t *)p;
4767                 if (len < hdr->bLength) {
4768                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
4769                             hwahcp->hwahc_log_handle,
4770                             "hwahc_handle_notif: length not match, "
4771                             "hdr length = %d, actual length = %d",
4772                             hdr->bLength, len);
4773 
4774                         break;
4775                 }
4776 
4777                 switch (hdr->bNotifyType) {
4778                 case WA_NOTIF_TYPE_TRANSFER:
4779                 {
4780                         uint8_t         ept = p[2];
4781 
4782                         /* deal with transfer completion notification */
4783                         hwahc_handle_xfer_result(hwahcp, ept);
4784 
4785                         break;
4786                 }
4787                 case HWA_NOTIF_TYPE_DN_RECEIVED:
4788                 {
4789                         hwa_notif_dn_recvd_t    *dn_notif;
4790 
4791                         dn_notif = kmem_alloc(hdr->bLength, KM_NOSLEEP);
4792                         (void) memcpy(dn_notif, p, hdr->bLength);
4793 
4794                         /* deal with device notification */
4795                         hwahc_handle_dn_notif(hwahcp, dn_notif);
4796 
4797                         break;
4798                 }
4799                 case HWA_NOTIF_TYPE_BPST_ADJ:
4800                         USB_DPRINTF_L3(PRINT_MASK_CBOPS,
4801                             hwahcp->hwahc_log_handle,
4802                             "hwahc_handle_notif: received BPST adjust "
4803                             "notification, bAdjustment = %d", p[2]);
4804 
4805                         break;
4806                 default:
4807                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
4808                             hwahcp->hwahc_log_handle,
4809                             "hwahc_handle_notif: unknown notification 0x%x",
4810                             hdr->bNotifyType);
4811 
4812                         break;
4813                 }
4814                 p += hdr->bLength;
4815                 len -= hdr->bLength;
4816         }
4817 }
4818 
4819 /*
4820  * start listening on bulk-in ept for transfer result
4821  *
4822  * Dispatches a task to read the BULK IN endpoint to get the result of
4823  * last request. usb_async_req() will have system_taskq to process the tasks.
4824  */
4825 int
4826 hwahc_start_result_thread(hwahc_state_t *hwahcp)
4827 {
4828         wusb_wa_data_t *wa_data;
4829 
4830         USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4831             "hwahc_start_result_thread:");
4832 
4833         ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
4834 
4835         if (hwahcp->hwahc_result_thread_id != 0) {
4836                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4837                     "hwahc_start_result_thread: already started");
4838 
4839                 return (USB_SUCCESS);
4840         }
4841 
4842         wa_data = &hwahcp->hwahc_wa_data;
4843 
4844         mutex_enter(&wa_data->wa_mutex);
4845         if ((wa_data->wa_bulkin_ph != NULL) &&
4846             (wa_data->wa_bulkin_pipe_state != WA_PIPE_STOPPED)) {
4847                 mutex_exit(&wa_data->wa_mutex);
4848 
4849                 return (USB_INVALID_PIPE);
4850         }
4851         mutex_exit(&wa_data->wa_mutex);
4852 
4853         if (wa_data->wa_bulkin_ph == NULL) {
4854                 mutex_exit(&hwahcp->hwahc_mutex);
4855                 if (usb_pipe_open(wa_data->wa_dip, &wa_data->wa_bulkin_ept,
4856                     &wa_data->wa_pipe_policy, USB_FLAGS_SLEEP,
4857                     &wa_data->wa_bulkin_ph) != USB_SUCCESS) {
4858                         USB_DPRINTF_L2(PRINT_MASK_ATTA,
4859                             hwahcp->hwahc_log_handle,
4860                             "hwahc_start_result_thread: open pipe failed");
4861 
4862 
4863                         mutex_enter(&hwahcp->hwahc_mutex);
4864                         return (USB_FAILURE);
4865                 }
4866                 mutex_enter(&hwahcp->hwahc_mutex);
4867 
4868                 mutex_enter(&wa_data->wa_mutex);
4869                 wa_data->wa_bulkin_pipe_state = WA_PIPE_STOPPED;
4870                 mutex_exit(&wa_data->wa_mutex);
4871         }
4872 
4873         /* kick off an asynchronous thread to handle transfer result */
4874         if (usb_async_req(hwahcp->hwahc_dip, hwahc_result_thread,
4875             (void *)hwahcp, 0) != USB_SUCCESS) {
4876                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4877                     "hwahc_start_result_thread: failed to start result thread");
4878 
4879                 return (USB_FAILURE);
4880         }
4881         hwahcp->hwahc_result_thread_id = (kthread_t *)1;
4882 
4883         /* pipe state is active while the result thread is on */
4884         mutex_enter(&wa_data->wa_mutex);
4885         wa_data->wa_bulkin_pipe_state = WA_PIPE_ACTIVE;
4886         mutex_exit(&wa_data->wa_mutex);
4887 
4888         return (USB_SUCCESS);
4889 }
4890 
4891 /* stop the bulk-in ept from listening */
4892 static void
4893 hwahc_stop_result_thread(hwahc_state_t *hwahcp)
4894 {
4895         wusb_wa_data_t *wa_data;
4896 
4897         ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
4898 
4899         USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4900             "hwahc_stop_result_thread:");
4901 
4902         if (hwahcp->hwahc_result_thread_id == 0) {
4903                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4904                     "hwahc_stop_result_thread: already stopped");
4905 
4906                 return;
4907         }
4908 
4909         wa_data = &hwahcp->hwahc_wa_data;
4910         mutex_enter(&wa_data->wa_mutex);
4911         if ((wa_data->wa_bulkin_ph == NULL) ||
4912             (wa_data->wa_bulkin_pipe_state != WA_PIPE_ACTIVE)) {
4913                 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4914                     "hwahc_stop_result_thread: invalid pipe state");
4915 
4916                 mutex_exit(&wa_data->wa_mutex);
4917 
4918                 return;
4919         }
4920         mutex_exit(&wa_data->wa_mutex);
4921 
4922         USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4923             "hwahc_stop_result_thread: reset hwa bulk-in pipe");
4924         mutex_exit(&hwahcp->hwahc_mutex);
4925         usb_pipe_reset(wa_data->wa_dip, wa_data->wa_bulkin_ph,
4926             USB_FLAGS_SLEEP, NULL, NULL);
4927 
4928         /*
4929          * have to close pipe here to fail the bulk-in transfer
4930          * that never timeouts
4931          */
4932         USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4933             "hwahc_stop_result_thread: close hwa bulk-in pipe");
4934         usb_pipe_close(wa_data->wa_dip, wa_data->wa_bulkin_ph,
4935             USB_FLAGS_SLEEP, NULL, NULL);
4936         mutex_enter(&hwahcp->hwahc_mutex);
4937 
4938         mutex_enter(&wa_data->wa_mutex);
4939         wa_data->wa_bulkin_ph = NULL;
4940         wa_data->wa_bulkin_pipe_state = WA_PIPE_STOPPED;
4941         mutex_exit(&wa_data->wa_mutex);
4942 
4943         while (hwahcp->hwahc_result_thread_id != 0) {
4944                 /* wait the result thread to exit */
4945                 cv_wait(&hwahcp->hwahc_result_thread_cv, &hwahcp->hwahc_mutex);
4946         }
4947 }
4948 
4949 /*
4950  * keep listening for transfer result by setting timeout to 0 while the
4951  * bulk-in pipe is active
4952  * the thread would be stopped by closing bulk-in pipe or encountering
4953  * transaction error, eg, hot-removal of hwa device
4954  */
4955 static void
4956 hwahc_result_thread(void *arg)
4957 {
4958         hwahc_state_t   *hwahcp = (hwahc_state_t *)arg;
4959         wusb_wa_data_t  *wa_data = &hwahcp->hwahc_wa_data;
4960         int             rval;
4961         uint8_t         retry = 0;
4962 
4963         mutex_enter(&hwahcp->hwahc_mutex);
4964         ASSERT(hwahcp->hwahc_result_thread_id == (kthread_t *)1);
4965         hwahcp->hwahc_result_thread_id = curthread;
4966         USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4967             "hwahc_result_thread: started, thread_id=0x%p",
4968             (void *)hwahcp->hwahc_result_thread_id);
4969 
4970         /* keep polling the bulk IN endpoint to get the result */
4971         mutex_enter(&wa_data->wa_mutex);
4972         while (wa_data->wa_bulkin_pipe_state == WA_PIPE_ACTIVE) {
4973                 mutex_exit(&wa_data->wa_mutex);
4974                 mutex_exit(&hwahcp->hwahc_mutex);
4975 
4976                 if ((rval = wusb_wa_get_xfer_result(wa_data)) != USB_SUCCESS) {
4977                         retry++;
4978                         USB_DPRINTF_L2(PRINT_MASK_ATTA,
4979                             hwahcp->hwahc_log_handle,
4980                             "hwahc_result_thread: get xfer result failed, "
4981                             "rval = %d, retry = %d", rval, retry);
4982 
4983                         /* retry 3 times upon failure */
4984                         if (retry >= 3) {
4985                                 mutex_enter(&hwahcp->hwahc_mutex);
4986                                 mutex_enter(&wa_data->wa_mutex);
4987 
4988                                 break;
4989                         }
4990                 }
4991 
4992                 mutex_enter(&hwahcp->hwahc_mutex);
4993                 mutex_enter(&wa_data->wa_mutex);
4994         }
4995 
4996         hwahcp->hwahc_result_thread_id = 0;
4997         wa_data->wa_bulkin_pipe_state = WA_PIPE_STOPPED;
4998         mutex_exit(&wa_data->wa_mutex);
4999 
5000         /* signal to the thread requesting stopping if any */
5001         cv_signal(&hwahcp->hwahc_result_thread_cv);
5002 
5003         USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
5004             "hwahc_result_thread: ended");
5005 
5006         mutex_exit(&hwahcp->hwahc_mutex);
5007 }
5008 
5009 /*
5010  * nothing to do here, just check if the ept number in the transfer
5011  * completion notification is valid
5012  * the actual handling of transfer result is performed by the result thread
5013  */
5014 static void
5015 hwahc_handle_xfer_result(hwahc_state_t *hwahcp, uint8_t ept)
5016 {
5017         usb_ep_descr_t  *epdt;
5018 
5019         USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5020             "hwahc_handle_xfer_result: result on ept %d", ept);
5021 
5022         epdt = &hwahcp->hwahc_wa_data.wa_bulkin_ept;
5023 
5024         /* the result should be on the bulk-in ept */
5025         if ((epdt->bEndpointAddress & USB_EP_NUM_MASK) != ept) {
5026                 USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5027                     "hwahc_handle_xfer_result: ept number not match");
5028 
5029                 return;
5030         }
5031 }
5032 
5033 
5034 /*
5035  * Section 8.5.4.2.
5036  *      Copy the DN Notification and add it to the instance's global
5037  *      nofication list. If the worker thread is not started yet, start
5038  *      it.
5039  */
5040 static void
5041 hwahc_handle_dn_notif(hwahc_state_t *hwahcp, hwa_notif_dn_recvd_t *dn_notif)
5042 {
5043         hwahc_dn_notif_list_t   *nlist;
5044 
5045         USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5046             "hwahc_handle_dn_notif: notif = 0x%p", (void *)dn_notif);
5047 
5048         nlist = kmem_zalloc(sizeof (hwahc_dn_notif_list_t), KM_NOSLEEP);
5049 
5050         mutex_enter(&hwahcp->hwahc_mutex);
5051         nlist->dn_notif = dn_notif;
5052 
5053         usba_init_list(&nlist->notif_list, (usb_opaque_t)nlist,
5054             hwahcp->hwahc_dev_data->dev_iblock_cookie);
5055 
5056         /* queue the new notification to the list */
5057         usba_add_to_list(&hwahcp->hwahc_dn_notif_queue, &nlist->notif_list);
5058 
5059         /* handle the notification queue with an asynchronous thread */
5060         if (hwahcp->hwahc_notif_thread_id == 0) {
5061                 if (usb_async_req(hwahcp->hwahc_dip, hwahc_notif_thread,
5062                     (void *)hwahcp, 0) != USB_SUCCESS) {
5063                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
5064                             hwahcp->hwahc_log_handle,
5065                             "hwahc_handle_dn_notif: no notif thread started");
5066                         mutex_exit(&hwahcp->hwahc_mutex);
5067 
5068                         return;
5069                 }
5070                 hwahcp->hwahc_notif_thread_id = (kthread_t *)1;
5071         }
5072 
5073         mutex_exit(&hwahcp->hwahc_mutex);
5074 }
5075 
5076 /* handle the notifications in the notification queue in sequence */
5077 static void
5078 hwahc_notif_thread(void *arg)
5079 {
5080         hwahc_state_t           *hwahcp = (hwahc_state_t *)arg;
5081         hwahc_dn_notif_list_t   *nlist;
5082         hwa_notif_dn_recvd_t    *dn_notif;
5083 
5084         mutex_enter(&hwahcp->hwahc_mutex);
5085         ASSERT(hwahcp->hwahc_notif_thread_id == (kthread_t *)1);
5086         hwahcp->hwahc_notif_thread_id = curthread;
5087 
5088         USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5089             "hwahc_notif_thread: started, thread_id=0x%p",
5090             (void *)hwahcp->hwahc_notif_thread_id);
5091 
5092         while (usba_list_entry_count(&hwahcp->hwahc_dn_notif_queue) != 0) {
5093                 /*
5094                  * first in first out, only one notification will be handled
5095                  * at a time, so it assures no racing in attach or detach
5096                  */
5097                 if ((nlist =
5098                     (hwahc_dn_notif_list_t *)usba_rm_first_pvt_from_list(
5099                     &hwahcp->hwahc_dn_notif_queue)) == NULL) {
5100 
5101                         continue;
5102                 }
5103                 dn_notif = nlist->dn_notif;
5104                 mutex_exit(&hwahcp->hwahc_mutex);
5105                 hwahc_handle_dn(hwahcp, dn_notif);
5106                 usba_destroy_list(&nlist->notif_list);
5107                 kmem_free(nlist, sizeof (hwahc_dn_notif_list_t));
5108                 mutex_enter(&hwahcp->hwahc_mutex);
5109         }
5110 
5111         hwahcp->hwahc_notif_thread_id = 0;
5112 
5113         USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5114             "hwahc_notif_thread: ended");
5115 
5116         mutex_exit(&hwahcp->hwahc_mutex);
5117 }
5118 
5119 /* Set the child device's active bit to 1 */
5120 static void
5121 hwahc_set_device_active(hwahc_state_t *hwahcp, uint8_t devaddr)
5122 {
5123         wusb_dev_info_t *dev_info;
5124         wusb_hc_data_t *hc_data = &hwahcp->hwahc_hc_data;
5125         int i;
5126 
5127         mutex_enter(&hc_data->hc_mutex);
5128         for (i = 1; i <= hc_data->hc_num_ports; i++) {
5129                 dev_info = hc_data->hc_dev_infos[i];
5130                 if ((dev_info != NULL) && (dev_info->wdev_addr == devaddr)) {
5131                         dev_info->wdev_active = 1;
5132                         USB_DPRINTF_L3(DPRINT_MASK_EVENTS,
5133                             hwahcp->hwahc_log_handle,
5134                             "hwahc_set_device_active:device(%p) updated ",
5135                             (void *)dev_info);
5136 
5137                         break;
5138                 }
5139         }
5140         mutex_exit(&hc_data->hc_mutex);
5141 }
5142 
5143 /*
5144  * handle a specific device notification
5145  * assuming the raw data in HWA DN_RECEIVED notification pkt includes
5146  * no more than one dn pkt
5147  */
5148 static void
5149 hwahc_handle_dn(hwahc_state_t *hwahcp, hwa_notif_dn_recvd_t *dn_notif)
5150 {
5151         uint8_t                 *p;
5152         size_t                  len;
5153         uint8_t                 dntype;
5154         int circ;
5155         wusb_hc_data_t          *hc_data = &hwahcp->hwahc_hc_data;
5156 
5157         if (dn_notif->bLength < 4) {
5158                 USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5159                     "hwahc_handle_dn: bLength too short %d", dn_notif->bLength);
5160                 kmem_free(dn_notif, dn_notif->bLength);
5161 
5162                 return;
5163         }
5164 
5165         p = dn_notif->notifdata;
5166         len = dn_notif->bLength - 4;
5167 
5168         /*
5169          * WUSB Errata 06.12 specifies that the raw data in the DN_RECEIVED
5170          * notification must not include the WUSB header, but only the bType
5171          * and Notification specific data
5172          */
5173         if (len == 0) {
5174                 USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5175                     "hwahc_handle_dn: no raw data");
5176                 kmem_free(dn_notif, dn_notif->bLength);
5177 
5178                 return;
5179         }
5180         dntype = *p;
5181 
5182         /* update the device's status bit, no matter what the DN is */
5183         hwahc_set_device_active(hwahcp, dn_notif->bSourceDeviceAddr);
5184 
5185         ndi_devi_enter(hwahcp->hwahc_dip, &circ);
5186         switch (dntype) {
5187         case WUSB_DN_CONNECT:
5188                 /* DN_Connect */
5189                 wusb_hc_handle_dn_connect(
5190                     hc_data, hwahcp->hwahc_default_pipe,
5191                     hwahcp->hwahc_wa_data.wa_ifno, p, len,
5192                     &hwahcp->hwahc_secrt_data);
5193 
5194                 break;
5195         case WUSB_DN_DISCONNECT:
5196                 /* DN_Disconnect */
5197                 wusb_hc_handle_dn_disconnect(
5198                     hc_data, dn_notif->bSourceDeviceAddr,
5199                     p, len);
5200 
5201                 break;
5202         case WUSB_DN_ALIVE:
5203                 /* We only send KeepAlive IE to one device at a comment */
5204                 mutex_enter(&hc_data->hc_mutex);
5205                 if (dn_notif->bSourceDeviceAddr ==
5206                     hc_data->hc_alive_ie.bDeviceAddress[0]) {
5207                         mutex_exit(&hc_data->hc_mutex);
5208                         wusb_hc_rem_ie(hc_data,
5209                             (wusb_ie_header_t *)&hc_data->hc_alive_ie);
5210                         mutex_enter(&hc_data->hc_mutex);
5211                 }
5212                 mutex_exit(&hc_data->hc_mutex);
5213 
5214                 break;
5215         case WUSB_DN_EPRDY:
5216         case WUSB_DN_MASAVAILCHANGED:
5217         case WUSB_DN_REMOTEWAKEUP:
5218         case WUSB_DN_SLEEP:
5219         default:
5220                 USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5221                     "hwahc_handle_dn: dn type 0x%x not supported yet",
5222                     dntype);
5223 
5224                 break;
5225         }
5226 
5227         kmem_free(dn_notif, dn_notif->bLength);
5228         ndi_devi_exit(hwahcp->hwahc_dip, circ);
5229 }
5230 
5231 /* exceptional callback for notification ept */
5232 /* ARGSUSED */
5233 static void
5234 hwahc_intr_exc_cb(usb_pipe_handle_t ph, struct usb_intr_req *reqp)
5235 {
5236         dev_info_t      *dip = (USBA_REQ2WRP(reqp))->wr_dip;
5237         hwahc_state_t   *hwahcp;
5238 
5239         ASSERT(dip != NULL);
5240         hwahcp = ddi_get_soft_state(hwahc_statep, ddi_get_instance(dip));
5241         ASSERT(hwahcp != NULL);
5242 
5243         USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5244             "hwahc_intr_exc_cb: receive intr exception cb, cr=%d",
5245             reqp->intr_completion_reason);
5246 
5247         ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
5248 
5249         mutex_enter(&hwahcp->hwahc_mutex);
5250 
5251         switch (reqp->intr_completion_reason) {
5252         case USB_CR_PIPE_RESET:
5253                 /* only restart nep after autoclearing */
5254                 if (hwahcp->hwahc_dev_state == USB_DEV_ONLINE) {
5255                         hwahcp->hwahc_wa_data.wa_intr_pipe_state =
5256                             WA_PIPE_STOPPED;
5257                         mutex_exit(&hwahcp->hwahc_mutex);
5258                         (void) wusb_wa_start_nep(&hwahcp->hwahc_wa_data,
5259                             USB_FLAGS_NOSLEEP);
5260                         mutex_enter(&hwahcp->hwahc_mutex);
5261                 }
5262 
5263                 break;
5264         case USB_CR_DEV_NOT_RESP:
5265         case USB_CR_STOPPED_POLLING:
5266         case USB_CR_PIPE_CLOSING:
5267         case USB_CR_UNSPECIFIED_ERR:
5268                 /* never restart nep on these conditions */
5269         default:
5270                 /* for all others, wait for the autoclearing PIPE_RESET cb */
5271 
5272                 break;
5273         }
5274 
5275         usb_free_intr_req(reqp);
5276         mutex_exit(&hwahcp->hwahc_mutex);
5277 }
5278 
5279 /*
5280  * callback function called by WA to resubmit a periodic request for
5281  * interrupt polling or isochronous transfer.
5282  */
5283 static int
5284 hwahc_pipe_submit_periodic_req(wusb_wa_data_t *wa_data,
5285         usba_pipe_handle_data_t *ph)
5286 {
5287         hwahc_state_t *hwahcp = wa_data->wa_private_data;
5288         hwahc_pipe_private_t *pp = (hwahc_pipe_private_t *)ph->p_hcd_private;
5289         int rval;
5290 
5291         mutex_enter(&hwahcp->hwahc_mutex);
5292 
5293         USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5294             "hwahc_pipe_submit_periodic_req: hwahcp=0x%p, pp=0x%p,"
5295             " pipe state = %d", (void *)hwahcp, (void *)pp, pp->pp_state);
5296 
5297         if (pp->pp_state != HWAHC_PIPE_STATE_ACTIVE) {
5298                 /* pipe error or pipe closing, don't resubmit any more */
5299                 USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5300                     "hwahc_pipe_submit_periodic_req: pipe not active = %d",
5301                     pp->pp_state);
5302 
5303                 mutex_exit(&hwahcp->hwahc_mutex);
5304 
5305                 return (USB_PIPE_ERROR);
5306         }
5307 
5308         mutex_exit(&hwahcp->hwahc_mutex);
5309 
5310         /* re-submit the original request */
5311         rval = wusb_wa_intr_xfer(wa_data, pp->pp_rp, ph,
5312             (usb_intr_req_t *)pp->pp_client_periodic_in_reqp, 0);
5313 
5314         return (rval);
5315 }
5316 
5317 /* call HCD callback for completion handling */
5318 static void
5319 hwahc_rpipe_xfer_cb(dev_info_t *dip, usba_pipe_handle_data_t *ph,
5320         wusb_wa_trans_wrapper_t *wr, usb_cr_t cr)
5321 {
5322         hwahc_state_t           *hwahcp;
5323         hwahc_pipe_private_t    *pp;
5324         usb_opaque_t            req;
5325         wusb_hc_data_t          *hc_data;
5326 
5327         hwahcp = ddi_get_soft_state(hwahc_statep, ddi_get_instance(dip));
5328         if (hwahcp == NULL) {
5329 
5330                 return;
5331         }
5332 
5333         hc_data = &hwahcp->hwahc_hc_data;
5334 
5335         mutex_enter(&hwahcp->hwahc_mutex);
5336         USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5337             "hwahc_rpipe_xfer_cb: ph = 0x%p, wr = 0x%p cr = 0x%x",
5338             (void *)ph, (void *)wr, cr);
5339 
5340         pp = (hwahc_pipe_private_t *)ph->p_hcd_private;
5341 
5342         mutex_enter(&hc_data->hc_mutex);
5343         pp->pp_wdev->wdev_active = 1; /* this device is active on xfer */
5344         mutex_exit(&hc_data->hc_mutex);
5345 
5346         switch (cr) {
5347         case USB_CR_OK:
5348                 break;
5349         case USB_CR_NOT_SUPPORTED:
5350         case USB_CR_NO_RESOURCES:
5351         case USB_CR_PIPE_RESET:
5352         case USB_CR_STOPPED_POLLING:
5353                 pp->pp_state = HWAHC_PIPE_STATE_IDLE;
5354                 break;
5355         case USB_CR_PIPE_CLOSING:
5356                 break;
5357         default:
5358                 pp->pp_state = HWAHC_PIPE_STATE_ERROR;
5359 
5360                 break;
5361         }
5362 
5363         if (wr && wr->wr_reqp) {
5364                 req = wr->wr_reqp;
5365 
5366                 mutex_enter(&wr->wr_rp->rp_mutex);
5367                 wr->wr_reqp = NULL;
5368                 mutex_exit(&wr->wr_rp->rp_mutex);
5369 
5370         } else { /* periodic pipe cleanup */
5371 
5372                 /* the original request is cleared and returned to client */
5373                 req = pp->pp_client_periodic_in_reqp;
5374                 pp->pp_client_periodic_in_reqp = NULL;
5375         }
5376 
5377         mutex_exit(&hwahcp->hwahc_mutex);
5378 
5379         USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5380             "hwahc_rpipe_xfer_cb: call usba_hcdi_cb for req= 0x%p",
5381             (void *)req);
5382 
5383         usba_hcdi_cb(ph, req, cr);
5384 }
5385 
5386 /* post disconnect event to child on a certain port */
5387 static void
5388 hwahc_disconnect_dev(dev_info_t *dip, usb_port_t port)
5389 {
5390         hwahc_state_t   *hwahcp;
5391         int             circ;
5392         dev_info_t      *child_dip;
5393 
5394         if ((hwahcp = ddi_get_soft_state(hwahc_statep,
5395             ddi_get_instance(dip))) == NULL) {
5396 
5397                 return;
5398         }
5399 
5400         ndi_devi_enter(dip, &circ);
5401         mutex_enter(&hwahcp->hwahc_mutex);
5402 
5403         child_dip = hwahcp->hwahc_hc_data.hc_children_dips[port];
5404         if ((hwahcp->hwahc_dev_state == USB_DEV_ONLINE) && child_dip) {
5405                 mutex_exit(&hwahcp->hwahc_mutex);
5406 
5407                 /* if the child driver remains attached */
5408                 if (i_ddi_devi_attached(child_dip)) {
5409                         hwahc_post_event(hwahcp, port,
5410                             USBA_EVENT_TAG_HOT_REMOVAL);
5411                 }
5412                 mutex_enter(&hwahcp->hwahc_mutex);
5413         }
5414 
5415         mutex_exit(&hwahcp->hwahc_mutex);
5416         ndi_devi_exit(dip, circ);
5417 }
5418 
5419 /* post reconect event to child on a certain port */
5420 static void
5421 hwahc_reconnect_dev(dev_info_t *dip, usb_port_t port)
5422 {
5423         hwahc_state_t   *hwahcp;
5424         int             circ;
5425 
5426         if ((hwahcp = ddi_get_soft_state(hwahc_statep,
5427             ddi_get_instance(dip))) == NULL) {
5428 
5429                 return;
5430         }
5431         ndi_devi_enter(dip, &circ);
5432         mutex_enter(&hwahcp->hwahc_mutex);
5433 
5434         if ((hwahcp->hwahc_dev_state == USB_DEV_ONLINE) &&
5435             (hwahcp->hwahc_hc_data.hc_children_dips[port])) {
5436                 mutex_exit(&hwahcp->hwahc_mutex);
5437                 hwahc_post_event(hwahcp, port, USBA_EVENT_TAG_HOT_INSERTION);
5438                 mutex_enter(&hwahcp->hwahc_mutex);
5439         }
5440 
5441         mutex_exit(&hwahcp->hwahc_mutex);
5442         ndi_devi_exit(dip, circ);
5443 }
5444 
5445 
5446 /*
5447  * Device TrustTimeout timer operations:
5448  * hwahc_start_trust_timer: start the trust timer for a newly connected device
5449  * hwahc_trust_timeout_handler: timer handler
5450  * hwahc_stop_trust_timer: stop a device's trust timer
5451  */
5452 static void
5453 hwahc_start_trust_timer(wusb_dev_info_t *dev)
5454 {
5455         if (hwahc_enable_trust_timeout == 0) {
5456 
5457                 return;
5458         }
5459 
5460         if (dev->wdev_trust_timer == NULL) {
5461                 dev->wdev_trust_timer = timeout(hwahc_trust_timeout_handler,
5462                     (void *)dev, drv_usectohz(WUSB_TRUST_TIMEOUT_US));
5463         }
5464 }
5465 
5466 /* timeout handler for device TrustTimeout. See section 4.14 */
5467 static void
5468 hwahc_trust_timeout_handler(void *arg)
5469 {
5470         wusb_dev_info_t *dev = (wusb_dev_info_t *)arg;
5471         usb_port_t port;
5472         uint16_t   dev_addr;
5473         wusb_hc_data_t *hc_data = dev->wdev_hc;
5474         uint8_t retry = 3;
5475         int rval;
5476 
5477         mutex_enter(&hc_data->hc_mutex);
5478 
5479         dev->wdev_trust_timer = 0;
5480         dev_addr = dev->wdev_addr;
5481 
5482         if (dev->wdev_active == 1) {
5483         /* device is active during the past period. Restart the timer */
5484                 dev->wdev_active = 0; /* expect device DN set it to 1 */
5485         } else {
5486                 /* send a KeepAlive IE to query the device */
5487                 for (retry = 0; retry < 3; retry++) {
5488                         mutex_exit(&hc_data->hc_mutex);
5489                         rval = wusb_hc_send_keepalive_ie(hc_data,
5490                             dev_addr);
5491                         mutex_enter(&hc_data->hc_mutex);
5492 
5493                         if (rval == USB_SUCCESS) {
5494                                 break;
5495                         }
5496                         /* retry 3 times if fail to send KeepAlive IE */
5497                 }
5498 
5499                 if (dev->wdev_active == 0) {
5500                         /* still no activity! Delete this device */
5501                         if (wusb_hc_is_dev_connected(hc_data, dev->wdev_cdid,
5502                             &port)) {
5503                                 mutex_exit(&hc_data->hc_mutex);
5504                                 (void) hwahc_destroy_child(hc_data->hc_dip,
5505                                     port);
5506 
5507                                 /* the device comes to the end of its life */
5508                                 return;
5509                         }
5510                 }
5511         }
5512 
5513         /* active or we received DN during query */
5514         hwahc_start_trust_timer(dev);
5515 
5516         mutex_exit(&hc_data->hc_mutex);
5517 }
5518 
5519 /* stop a child device's trust timeout handler */
5520 void
5521 hwahc_stop_trust_timer(wusb_dev_info_t *dev)
5522 {
5523         timeout_id_t tid;
5524         wusb_hc_data_t *hc_data = dev->wdev_hc;
5525 
5526         ASSERT(mutex_owned(&hc_data->hc_mutex));
5527 
5528         if (hwahc_enable_trust_timeout == 0) {
5529                 return;
5530         }
5531 
5532         tid = dev->wdev_trust_timer;
5533 
5534         dev->wdev_trust_timer = NULL;
5535         mutex_exit(&hc_data->hc_mutex);
5536 
5537         if (tid != NULL) {
5538                 (void) untimeout(tid);
5539         }
5540 
5541         mutex_enter(&hc_data->hc_mutex);
5542 }
5543 
5544 /* configure child device and attach child on a certain port */
5545 static int
5546 hwahc_create_child(dev_info_t *dip, usb_port_t port)
5547 {
5548         hwahc_state_t           *hwahcp;
5549         wusb_hc_data_t          *hc_data;
5550         wusb_dev_info_t         *dev_info;
5551         usb_pipe_handle_t       ph;
5552         int                     rval;
5553         dev_info_t              *child_dip;
5554         usba_device_t           *child_ud = NULL;
5555         mblk_t                  *pdata = NULL;
5556         usb_cr_t                completion_reason;
5557         usb_cb_flags_t          cb_flags;
5558         size_t                  size;
5559         uint8_t                 address;
5560         int                     user_conf_index;
5561         uint_t                  config_index;
5562         int                     prh_circ, rh_circ, circ;
5563         dev_info_t              *rh_dip;
5564         usb_dev_descr_t         usb_dev_descr;
5565 
5566         if ((hwahcp = ddi_get_soft_state(hwahc_statep,
5567             ddi_get_instance(dip))) == NULL) {
5568 
5569                 return (USB_INVALID_ARGS);
5570         }
5571 
5572         rh_dip = hwahcp->hwahc_hubd->h_usba_device->usb_root_hub_dip;
5573         ndi_hold_devi(dip);     /* avoid racing with dev detach */
5574         /* exclude other threads */
5575         ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
5576         ndi_devi_enter(rh_dip, &rh_circ);
5577         ndi_devi_enter(dip, &circ);
5578 
5579         _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*dev_info));
5580 
5581         hc_data = &hwahcp->hwahc_hc_data;
5582         mutex_enter(&hc_data->hc_mutex);
5583         dev_info = hc_data->hc_dev_infos[port];
5584 
5585         /* Created in whcdi.c before authed */
5586         child_dip = hc_data->hc_children_dips[port];
5587 
5588         child_ud = usba_get_usba_device(child_dip);
5589         ph = dev_info->wdev_ph;
5590 
5591         mutex_exit(&hc_data->hc_mutex);
5592         /*
5593          * HWA maintains the address space as a separate bus and
5594          * will not occupy parent's address space
5595          */
5596         address = child_ud->usb_addr;
5597         if (address < 0x80) {
5598                 USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5599                     "hwahc_create_child: reconnecting, address = %d",
5600                     address);
5601 
5602         } else {
5603                 /* SetAddress(0) */
5604                 if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
5605                     USB_DEV_REQ_HOST_TO_DEV,
5606                     USB_REQ_SET_ADDRESS,        /* bRequest */
5607                     0,                          /* wValue */
5608                     0,                          /* wIndex */
5609                     0,                          /* wLength */
5610                     NULL, 0,
5611                     &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
5612                         char buffer[64];
5613                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
5614                             hwahcp->hwahc_log_handle,
5615                             "setting address failed (cr=%s cb_flags=%s "
5616                             "rval=%d)", usb_str_cr(completion_reason),
5617                             usb_str_cb_flags(cb_flags, buffer, sizeof (buffer)),
5618                             rval);
5619 
5620                         goto done;
5621                 }
5622 
5623                 USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5624                     "set address 0 done");
5625 
5626                 usb_pipe_close(child_dip, ph,
5627                     USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
5628 
5629                 child_ud->usb_addr = 0;
5630                 dev_info->wdev_addr = 0;
5631                 dev_info->wdev_ph = NULL;
5632 
5633                 /* need to be called each time dev addr is changed */
5634                 if ((rval = wusb_hc_set_device_info(&hwahcp->hwahc_hc_data,
5635                     port)) != USB_SUCCESS) {
5636                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
5637                             hwahcp->hwahc_log_handle,
5638                             "update device info failed, rval = %d", rval);
5639 
5640                         goto done;
5641                 }
5642 
5643                 /* new ph is stored in usba_device */
5644                 if ((rval = usb_pipe_open(child_dip, NULL, NULL,
5645                     USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) !=
5646                     USB_SUCCESS) {
5647                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
5648                             hwahcp->hwahc_log_handle,
5649                             "usb_pipe_open failed (%d)", rval);
5650 
5651                         goto done;
5652                 }
5653 
5654                 /* provide at least 2ms time for address change, 7.3.1.3 */
5655                 delay(drv_usectohz(2000));
5656 
5657                 /* start normal enumeration process */
5658                 /*
5659                  * wusb bus address has 1:1 relationship with port number
5660                  * and wusb bus address starts from 2, so as to follow
5661                  * the convention that USB bus address 1 is reserved for
5662                  * host controller device. As such, only 126 WUSB devices
5663                  * are supported on a WUSB host
5664                  */
5665                 address = port + 1;
5666                 if (address >= 0x80) {
5667                         USB_DPRINTF_L3(PRINT_MASK_CBOPS,
5668                             hwahcp->hwahc_log_handle,
5669                             "hwahc_create_child: address for port %d exceeds "
5670                             "0x80", port);
5671                         rval = USB_FAILURE;
5672 
5673                         goto done;
5674                 }
5675                 /* Set the address of the device */
5676                 if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
5677                     USB_DEV_REQ_HOST_TO_DEV,
5678                     USB_REQ_SET_ADDRESS,        /* bRequest */
5679                     address,                    /* wValue */
5680                     0,                          /* wIndex */
5681                     0,                          /* wLength */
5682                     NULL, 0,
5683                     &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
5684                         char buffer[64];
5685                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
5686                             hwahcp->hwahc_log_handle,
5687                             "setting address failed (cr=%s cb_flags=%s "
5688                             "rval=%d)", usb_str_cr(completion_reason),
5689                             usb_str_cb_flags(cb_flags, buffer, sizeof (buffer)),
5690                             rval);
5691 
5692                         goto done;
5693                 }
5694 
5695                 USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5696                     "set address 0x%x done", address);
5697 
5698                 usb_pipe_close(child_dip, ph,
5699                     USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
5700 
5701                 child_ud->usb_addr = address;
5702                 dev_info->wdev_addr = address;
5703                 dev_info->wdev_ph = NULL;
5704 
5705                 if ((rval = wusb_hc_set_device_info(&hwahcp->hwahc_hc_data,
5706                     port)) != USB_SUCCESS) {
5707                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
5708                             hwahcp->hwahc_log_handle,
5709                             "update device info failed, rval = %d", rval);
5710 
5711                         goto done;
5712                 }
5713 
5714                 /* new ph is stored in usba_device */
5715                 if ((rval = usb_pipe_open(child_dip, NULL, NULL,
5716                     USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) !=
5717                     USB_SUCCESS) {
5718                         USB_DPRINTF_L2(PRINT_MASK_CBOPS,
5719                             hwahcp->hwahc_log_handle,
5720                             "usb_pipe_open failed (%d)", rval);
5721 
5722                         goto done;
5723                 }
5724 
5725                 /* provide at least 2ms time for address change, 7.3.1.3 */
5726                 delay(drv_usectohz(2000));
5727         }
5728 
5729         /* get device descriptor ignoring device reconnection */
5730         rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
5731             USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
5732             USB_REQ_GET_DESCR,                  /* bRequest */
5733             USB_DESCR_TYPE_SETUP_DEV,           /* wValue */
5734             0,                                  /* wIndex */
5735             512,                                /* wLength */
5736             &pdata, USB_ATTRS_SHORT_XFER_OK,
5737             &completion_reason, &cb_flags, 0);
5738 
5739         if (rval != USB_SUCCESS) {
5740                 if (pdata) {
5741                         freemsg(pdata);
5742                         pdata = NULL;
5743                 }
5744 
5745                 USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5746                     "hwahc_create_child: get device descriptor failed "
5747                     "(%s 0x%x %d)", usb_str_cr(completion_reason),
5748                     cb_flags, rval);
5749 
5750                 goto done;
5751         }
5752 
5753         ASSERT(pdata != NULL);
5754         size = usb_parse_dev_descr(
5755             pdata->b_rptr,
5756             MBLKL(pdata),
5757             &usb_dev_descr,
5758             sizeof (usb_dev_descr_t));
5759         freemsg(pdata);
5760 
5761         if (size < USB_DEV_DESCR_SIZE) {
5762                 USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5763                     "hwahc_create_child: get device descriptor size = %lu "
5764                     "expected size = %u", size, USB_DEV_DESCR_SIZE);
5765                 rval = USB_FAILURE;
5766 
5767                 goto done;
5768         }
5769 
5770         bcopy(&usb_dev_descr, child_ud->usb_dev_descr,
5771             sizeof (usb_dev_descr_t));
5772         child_ud->usb_n_cfgs = usb_dev_descr.bNumConfigurations;
5773 
5774         if (usb_dev_descr.bNumConfigurations == 0) {
5775                 USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5776                     "device descriptor:\n\t"
5777                     "l=0x%x type=0x%x USB=0x%x class=0x%x subclass=0x%x\n\t"
5778                     "protocol=0x%x maxpktsize=0x%x "
5779                     "Vid=0x%x Pid=0x%x rel=0x%x\n\t"
5780                     "Mfg=0x%x P=0x%x sn=0x%x #config=0x%x",
5781                     usb_dev_descr.bLength, usb_dev_descr.bDescriptorType,
5782                     usb_dev_descr.bcdUSB, usb_dev_descr.bDeviceClass,
5783                     usb_dev_descr.bDeviceSubClass,
5784                     usb_dev_descr.bDeviceProtocol,
5785                     usb_dev_descr.bMaxPacketSize0,
5786                     usb_dev_descr.idVendor,
5787                     usb_dev_descr.idProduct, usb_dev_descr.bcdDevice,
5788                     usb_dev_descr.iManufacturer, usb_dev_descr.iProduct,
5789                     usb_dev_descr.iSerialNumber,
5790                     usb_dev_descr.bNumConfigurations);
5791 
5792                 rval = USB_FAILURE;
5793 
5794                 goto done;
5795         }
5796 
5797         /* get the device string descriptor(s) */
5798         usba_get_dev_string_descrs(child_dip, child_ud);
5799 
5800         /* retrieve config cloud for all configurations */
5801         rval = hubd_get_all_device_config_cloud(hwahcp->hwahc_hubd,
5802             child_dip, child_ud);
5803         if (rval != USB_SUCCESS) {
5804                 USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5805                     "failed to get configuration descriptor(s)");
5806 
5807                 goto done;
5808         }
5809 
5810         /* get the preferred configuration for this device */
5811         user_conf_index = hubd_select_device_configuration(hwahcp->hwahc_hubd,
5812             port, child_dip, child_ud);
5813 
5814         /* Check if the user selected configuration index is in range */
5815         if ((user_conf_index >= usb_dev_descr.bNumConfigurations) ||
5816             (user_conf_index < 0)) {
5817                 USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5818                     "Configuration index for device idVendor=%d "
5819                     "idProduct=%d is=%d, and is out of range[0..%d]",
5820                     usb_dev_descr.idVendor, usb_dev_descr.idProduct,
5821                     user_conf_index, usb_dev_descr.bNumConfigurations - 1);
5822 
5823                 /* treat this as user didn't specify configuration */
5824                 user_conf_index = USBA_DEV_CONFIG_INDEX_UNDEFINED;
5825         }
5826 
5827         if (user_conf_index == USBA_DEV_CONFIG_INDEX_UNDEFINED) {
5828                 if (child_ud->usb_preferred_driver) {
5829                         /*
5830                          * It is the job of the "preferred driver" to put the
5831                          * device in the desired configuration. Till then
5832                          * put the device in config index 0.
5833                          */
5834                         /* h_ignore_pwr_budget = TRUE, not care the power */
5835                         if ((rval = usba_hubdi_check_power_budget(dip, child_ud,
5836                             USB_DEV_DEFAULT_CONFIG_INDEX)) != USB_SUCCESS) {
5837 
5838                                 goto done;
5839                         }
5840 
5841                         child_dip = hubd_ready_device(hwahcp->hwahc_hubd,
5842                             child_dip, child_ud, USB_DEV_DEFAULT_CONFIG_INDEX);
5843 
5844                         /*
5845                          * Assign the dip before onlining to avoid race
5846                          * with busctl
5847                          */
5848                         mutex_enter(&hc_data->hc_mutex);
5849                         hc_data->hc_children_dips[port] = child_dip;
5850                         mutex_exit(&hc_data->hc_mutex);
5851 
5852                         (void) usba_bind_driver(child_dip);
5853                 } else {
5854                         /*
5855                          * loop through all the configurations to see if we
5856                          * can find a driver for any one config. If not, set
5857                          * the device in config_index 0
5858                          */
5859                         rval = USB_FAILURE;
5860                         for (config_index = 0;
5861                             (config_index < usb_dev_descr.bNumConfigurations) &&
5862                             (rval != USB_SUCCESS); config_index++) {
5863 
5864                                 child_dip = hubd_ready_device(
5865                                     hwahcp->hwahc_hubd,
5866                                     child_dip, child_ud, config_index);
5867 
5868                                 /*
5869                                  * Assign the dip before onlining to avoid race
5870                                  * with busctl
5871                                  */
5872                                 mutex_enter(&hc_data->hc_mutex);
5873                                 hc_data->hc_children_dips[port] = child_dip;
5874                                 mutex_exit(&hc_data->hc_mutex);
5875 
5876                                 rval = usba_bind_driver(child_dip);
5877 
5878                                 if (rval == USB_SUCCESS) {
5879                                         /* always succeed for WUSB device */
5880                                         if ((usba_hubdi_check_power_budget(dip,
5881                                             child_ud, config_index)) !=
5882                                             USB_SUCCESS) {
5883                                                 rval = USB_FAILURE;
5884 
5885                                                 goto done;
5886                                         }
5887                                 }
5888                         }
5889 
5890                         if (rval != USB_SUCCESS) {
5891                                 if ((usba_hubdi_check_power_budget(dip,
5892                                     child_ud, 0)) != USB_SUCCESS) {
5893 
5894                                         goto done;
5895                                 }
5896 
5897                                 child_dip = hubd_ready_device(
5898                                     hwahcp->hwahc_hubd,
5899                                     child_dip, child_ud, 0);
5900                                 mutex_enter(&hc_data->hc_mutex);
5901                                 hc_data->hc_children_dips[port] = child_dip;
5902                                 mutex_exit(&hc_data->hc_mutex);
5903                         }
5904                 } /* end else loop all configs */
5905         } else {
5906                 if ((usba_hubdi_check_power_budget(dip, child_ud,
5907                     (uint_t)user_conf_index)) != USB_SUCCESS) {
5908                         rval = USB_FAILURE;
5909 
5910                         goto done;
5911                 }
5912 
5913                 child_dip = hubd_ready_device(hwahcp->hwahc_hubd, child_dip,
5914                     child_ud, (uint_t)user_conf_index);
5915 
5916                 /*
5917                  * Assign the dip before onlining to avoid race
5918                  * with busctl
5919                  */
5920                 mutex_enter(&hc_data->hc_mutex);
5921                 hc_data->hc_children_dips[port] = child_dip;
5922                 mutex_exit(&hc_data->hc_mutex);
5923 
5924                 (void) usba_bind_driver(child_dip);
5925 
5926                 rval = USB_SUCCESS;
5927         }
5928 
5929         /* workaround for non response after ctrl write */
5930         usb_pipe_close(child_dip, ph,
5931             USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
5932 
5933         if ((rval = usb_pipe_open(child_dip, NULL, NULL,
5934             USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) !=
5935             USB_SUCCESS) {
5936                 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
5937                     hwahcp->hwahc_log_handle,
5938                     "usb_pipe_open failed (%d)", rval);
5939 
5940                 goto done;
5941         }
5942 
5943 done:
5944         _NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*dev_info));
5945 
5946         ndi_devi_exit(dip, circ);
5947         ndi_devi_exit(rh_dip, rh_circ);
5948         ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ);
5949 
5950         (void) devfs_clean(rh_dip, NULL, 0);
5951 
5952         if (rval == USB_SUCCESS) {
5953                 (void) ndi_devi_online(child_dip, 0);
5954 
5955                 USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5956                     "hwahc_create_child: create timer for child %p",
5957                     (void *)dev_info);
5958 
5959                 mutex_enter(&hc_data->hc_mutex);
5960                 hwahc_start_trust_timer(dev_info);
5961                 mutex_exit(&hc_data->hc_mutex);
5962         }
5963 
5964         ndi_rele_devi(dip);
5965 
5966         return (rval);
5967 }
5968 
5969 /* offline child on a certain port */
5970 static int
5971 hwahc_destroy_child(dev_info_t *dip, usb_port_t port)
5972 {
5973         hwahc_state_t   *hwahcp;
5974 
5975         if ((hwahcp = ddi_get_soft_state(hwahc_statep,
5976             ddi_get_instance(dip))) == NULL) {
5977 
5978                 return (USB_INVALID_ARGS);
5979         }
5980 
5981         hwahc_post_event(hwahcp, port, USBA_EVENT_TAG_HOT_REMOVAL);
5982 
5983         USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5984             "hwahc_destroy_child: scheduling cleanup");
5985 
5986         /* schedule cleanup thread */
5987         hubd_schedule_cleanup(hwahcp->hwahc_hubd->h_usba_device->
5988             usb_root_hub_dip);
5989 
5990         return (USB_SUCCESS);
5991 }
5992 
5993 /*
5994  * called by cleanup thread to offline child and cleanup child resources
5995  * Child's callback functions have been called before calling this routine.
5996  *      dip - hwahc's dip
5997  */
5998 static int
5999 hwahc_cleanup_child(dev_info_t *dip)
6000 {
6001         hwahc_state_t   *hwahcp;
6002         wusb_hc_data_t  *hc_data;
6003         usb_port_t      port;
6004 
6005         if ((hwahcp = ddi_get_soft_state(hwahc_statep,
6006             ddi_get_instance(dip))) == NULL) {
6007 
6008                 return (USB_INVALID_ARGS);
6009         }
6010 
6011         hc_data = &hwahcp->hwahc_hc_data;
6012         mutex_enter(&hc_data->hc_mutex);
6013         for (port = 1; port <= hc_data->hc_num_ports; port++) {
6014                 dev_info_t *cdip = hc_data->hc_children_dips[port];
6015 
6016                 if (cdip == NULL || DEVI_IS_DEVICE_REMOVED(cdip) == 0) {
6017 
6018                         continue;
6019                 }
6020 
6021                 /*
6022                  * child's callback has been called and its dip has been
6023                  * marked REMOVED. Do further cleanup in hwa driver for
6024                  * this child.
6025                  */
6026                 mutex_exit(&hc_data->hc_mutex);
6027                 (void) hwahc_delete_child(dip, port, NDI_DEVI_REMOVE, B_TRUE);
6028                 mutex_enter(&hc_data->hc_mutex);
6029         }
6030         mutex_exit(&hc_data->hc_mutex);
6031 
6032         return (USB_SUCCESS);
6033 }
6034 
6035 /* offline child and cleanup child resources */
6036 static int
6037 hwahc_delete_child(dev_info_t *dip, usb_port_t port, uint_t flag,
6038         boolean_t retry)
6039 {
6040         hwahc_state_t   *hwahcp;
6041         dev_info_t      *child_dip;
6042         usba_device_t   *usba_device;
6043         wusb_hc_data_t  *hc_data;
6044         int             rval;
6045 
6046         if ((hwahcp = ddi_get_soft_state(hwahc_statep,
6047             ddi_get_instance(dip))) == NULL) {
6048 
6049                 return (USB_INVALID_ARGS);
6050         }
6051 
6052         child_dip = hwahc_get_child_dip(hwahcp, port);
6053         if (child_dip == NULL) {
6054 
6055                 return (USB_SUCCESS);
6056         }
6057 
6058         usba_device = usba_get_usba_device(child_dip);
6059         hc_data = &hwahcp->hwahc_hc_data;
6060 
6061         USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
6062             "hwahc_delete_child: port=%d, dip=0x%p usba_device=0x%p",
6063             port, (void *)child_dip, (void *)usba_device);
6064 
6065         if (usba_device) {
6066                 usba_hubdi_incr_power_budget(dip, usba_device);
6067         }
6068 
6069         /* remove this child's dip. If it's <DS_INITIALIZED, free it */
6070         rval = usba_destroy_child_devi(child_dip, flag);
6071 
6072         if ((rval == USB_SUCCESS) && (flag & NDI_DEVI_REMOVE)) {
6073                 /*
6074                  * if the child was still < DS_INITIALIZED
6075                  * then our bus_unconfig was not called and
6076                  * we have to zap the child here
6077                  */
6078                 mutex_enter(&hc_data->hc_mutex);
6079                 if (hc_data->hc_children_dips[port] == child_dip) {
6080                         usba_device_t *ud = hc_data->hc_usba_devices[port];
6081                         wusb_dev_info_t *dev_info = hc_data->hc_dev_infos[port];
6082 
6083                         hc_data->hc_children_dips[port] = NULL;
6084                         if (ud) {
6085                                 mutex_exit(&hc_data->hc_mutex);
6086 
6087                                 mutex_enter(&ud->usb_mutex);
6088                                 ud->usb_ref_count = 0;
6089                                 mutex_exit(&ud->usb_mutex);
6090 
6091                                 usba_free_usba_device(ud);
6092                                 mutex_enter(&hc_data->hc_mutex);
6093                                 hc_data->hc_usba_devices[port] = NULL;
6094                         }
6095 
6096                         /* free the child's wusb_dev_info data */
6097                         if (dev_info) {
6098                                 wusb_secrt_data_t *secrt_data;
6099 
6100                                 if (dev_info->
6101                                     wdev_secrt_data.secrt_encry_descr) {
6102                                         secrt_data = &dev_info->wdev_secrt_data;
6103                                         kmem_free(secrt_data->secrt_encry_descr,
6104                                             sizeof (usb_encryption_descr_t) *
6105                                             secrt_data->secrt_n_encry);
6106                                 }
6107                                 if (dev_info->wdev_uwb_descr) {
6108                                         kmem_free(dev_info->wdev_uwb_descr,
6109                                             sizeof (usb_uwb_cap_descr_t));
6110                                 }
6111                                 kmem_free(dev_info, sizeof (wusb_dev_info_t));
6112                                 hc_data->hc_dev_infos[port] = NULL;
6113                         }
6114                 }
6115                 mutex_exit(&hc_data->hc_mutex);
6116         }
6117 
6118         if ((rval != USB_SUCCESS) && retry) {
6119 
6120                 hubd_schedule_cleanup(usba_device->usb_root_hub_dip);
6121         }
6122 
6123         return (rval);
6124 }
6125 
6126 /*
6127  * Set encryption type for WUSB host, refer to WUSB 1.0/8.5.3.6
6128  * index = port number - 1
6129  */
6130 int
6131 hwahc_set_dev_encrypt(usb_pipe_handle_t ph, uint8_t ifc,
6132         usb_port_t index, wusb_secrt_data_t *secrt_data, uint8_t type)
6133 {
6134         int16_t                 value;
6135         usb_ctrl_setup_t        setup;
6136         usb_cr_t                cr;
6137         usb_cb_flags_t          cb_flags;
6138 
6139         USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
6140             "hwahc_set_dev_encrypt: device index = %d", index);
6141 
6142         if (type == USB_ENC_TYPE_UNSECURE) {
6143                 value = 0;
6144         } else if (type == USB_ENC_TYPE_CCM_1) {
6145                 if (secrt_data == NULL) {
6146 
6147                         return (USB_INVALID_ARGS);
6148                 }
6149 
6150                 value = wusb_get_ccm_encryption_value(secrt_data);
6151                 if (value == -1) {
6152                         USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
6153                             "hwahc_set_dev_encrypt: cannot find ccm "
6154                             "encryption type");
6155 
6156                         return (USB_FAILURE);
6157                 }
6158                 USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
6159                     "hwahc_set_dev_encrypt: ccm encryption value is %d",
6160                     value);
6161         } else {
6162                 USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
6163                     "hwahc_set_dev_encrypt: unsupported encryption type %d",
6164                     type);
6165 
6166                 return (USB_INVALID_ARGS);
6167         }
6168 
6169         setup.bmRequestType = USB_DEV_REQ_HOST_TO_DEV |
6170             USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_RCPT_IF;
6171         setup.bRequest = USB_REQ_SET_ENCRYPTION;
6172         setup.wValue = (uint16_t)value;
6173         setup.wIndex = (index << 8) | ifc;
6174         setup.wLength = 0;
6175         setup.attrs = USB_ATTRS_NONE;
6176 
6177         USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
6178             "bmRequestType=0x%x, bRequest=0x%x, wValue=0x%x, wIndex=0x%x",
6179             setup.bmRequestType, setup.bRequest, setup.wValue, setup.wIndex);
6180 
6181         return (usb_pipe_ctrl_xfer_wait(ph, &setup, NULL,
6182             &cr, &cb_flags, USB_FLAGS_SLEEP));
6183 }