1*7c478bd9Sstevel@tonic-gate /* 2*7c478bd9Sstevel@tonic-gate * CDDL HEADER START 3*7c478bd9Sstevel@tonic-gate * 4*7c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*7c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*7c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*7c478bd9Sstevel@tonic-gate * with the License. 8*7c478bd9Sstevel@tonic-gate * 9*7c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*7c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*7c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 12*7c478bd9Sstevel@tonic-gate * and limitations under the License. 13*7c478bd9Sstevel@tonic-gate * 14*7c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*7c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*7c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*7c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*7c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*7c478bd9Sstevel@tonic-gate * 20*7c478bd9Sstevel@tonic-gate * CDDL HEADER END 21*7c478bd9Sstevel@tonic-gate */ 22*7c478bd9Sstevel@tonic-gate /* 23*7c478bd9Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24*7c478bd9Sstevel@tonic-gate * Use is subject to license terms. 25*7c478bd9Sstevel@tonic-gate */ 26*7c478bd9Sstevel@tonic-gate 27*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*7c478bd9Sstevel@tonic-gate 29*7c478bd9Sstevel@tonic-gate 30*7c478bd9Sstevel@tonic-gate /* 31*7c478bd9Sstevel@tonic-gate * USBA: Solaris USB Architecture support for the hub 32*7c478bd9Sstevel@tonic-gate * including root hub 33*7c478bd9Sstevel@tonic-gate * Most of the code for hubd resides in this file and 34*7c478bd9Sstevel@tonic-gate * is shared between the HCD root hub support and hubd 35*7c478bd9Sstevel@tonic-gate */ 36*7c478bd9Sstevel@tonic-gate #define USBA_FRAMEWORK 37*7c478bd9Sstevel@tonic-gate #include <sys/usb/usba.h> 38*7c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usba_devdb.h> 39*7c478bd9Sstevel@tonic-gate #include <sys/sunndi.h> 40*7c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usba_impl.h> 41*7c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usba_types.h> 42*7c478bd9Sstevel@tonic-gate #include <sys/usb/usba/hubdi.h> 43*7c478bd9Sstevel@tonic-gate #include <sys/usb/usba/hcdi_impl.h> 44*7c478bd9Sstevel@tonic-gate #include <sys/usb/hubd/hub.h> 45*7c478bd9Sstevel@tonic-gate #include <sys/usb/hubd/hubdvar.h> 46*7c478bd9Sstevel@tonic-gate #include <sys/usb/hubd/hubd_impl.h> 47*7c478bd9Sstevel@tonic-gate #include <sys/kobj.h> 48*7c478bd9Sstevel@tonic-gate #include <sys/kobj_lex.h> 49*7c478bd9Sstevel@tonic-gate #include <sys/fs/dv_node.h> 50*7c478bd9Sstevel@tonic-gate 51*7c478bd9Sstevel@tonic-gate /* 52*7c478bd9Sstevel@tonic-gate * Prototypes for static functions 53*7c478bd9Sstevel@tonic-gate */ 54*7c478bd9Sstevel@tonic-gate static int usba_hubdi_bus_ctl( 55*7c478bd9Sstevel@tonic-gate dev_info_t *dip, 56*7c478bd9Sstevel@tonic-gate dev_info_t *rdip, 57*7c478bd9Sstevel@tonic-gate ddi_ctl_enum_t op, 58*7c478bd9Sstevel@tonic-gate void *arg, 59*7c478bd9Sstevel@tonic-gate void *result); 60*7c478bd9Sstevel@tonic-gate 61*7c478bd9Sstevel@tonic-gate static int usba_hubdi_map_fault( 62*7c478bd9Sstevel@tonic-gate dev_info_t *dip, 63*7c478bd9Sstevel@tonic-gate dev_info_t *rdip, 64*7c478bd9Sstevel@tonic-gate struct hat *hat, 65*7c478bd9Sstevel@tonic-gate struct seg *seg, 66*7c478bd9Sstevel@tonic-gate caddr_t addr, 67*7c478bd9Sstevel@tonic-gate struct devpage *dp, 68*7c478bd9Sstevel@tonic-gate pfn_t pfn, 69*7c478bd9Sstevel@tonic-gate uint_t prot, 70*7c478bd9Sstevel@tonic-gate uint_t lock); 71*7c478bd9Sstevel@tonic-gate 72*7c478bd9Sstevel@tonic-gate static int hubd_busop_get_eventcookie(dev_info_t *dip, 73*7c478bd9Sstevel@tonic-gate dev_info_t *rdip, 74*7c478bd9Sstevel@tonic-gate char *eventname, 75*7c478bd9Sstevel@tonic-gate ddi_eventcookie_t *cookie); 76*7c478bd9Sstevel@tonic-gate static int hubd_busop_add_eventcall(dev_info_t *dip, 77*7c478bd9Sstevel@tonic-gate dev_info_t *rdip, 78*7c478bd9Sstevel@tonic-gate ddi_eventcookie_t cookie, 79*7c478bd9Sstevel@tonic-gate void (*callback)(dev_info_t *dip, 80*7c478bd9Sstevel@tonic-gate ddi_eventcookie_t cookie, void *arg, 81*7c478bd9Sstevel@tonic-gate void *bus_impldata), 82*7c478bd9Sstevel@tonic-gate void *arg, ddi_callback_id_t *cb_id); 83*7c478bd9Sstevel@tonic-gate static int hubd_busop_remove_eventcall(dev_info_t *dip, 84*7c478bd9Sstevel@tonic-gate ddi_callback_id_t cb_id); 85*7c478bd9Sstevel@tonic-gate static int hubd_bus_config(dev_info_t *dip, 86*7c478bd9Sstevel@tonic-gate uint_t flag, 87*7c478bd9Sstevel@tonic-gate ddi_bus_config_op_t op, 88*7c478bd9Sstevel@tonic-gate void *arg, 89*7c478bd9Sstevel@tonic-gate dev_info_t **child); 90*7c478bd9Sstevel@tonic-gate static int hubd_bus_unconfig(dev_info_t *dip, 91*7c478bd9Sstevel@tonic-gate uint_t flag, 92*7c478bd9Sstevel@tonic-gate ddi_bus_config_op_t op, 93*7c478bd9Sstevel@tonic-gate void *arg); 94*7c478bd9Sstevel@tonic-gate static int hubd_bus_power(dev_info_t *dip, void *impl_arg, 95*7c478bd9Sstevel@tonic-gate pm_bus_power_op_t op, void *arg, void *result); 96*7c478bd9Sstevel@tonic-gate 97*7c478bd9Sstevel@tonic-gate static void hubd_get_ancestry_str(hubd_t *); 98*7c478bd9Sstevel@tonic-gate static usb_port_t hubd_get_port_num(hubd_t *, struct devctl_iocdata *); 99*7c478bd9Sstevel@tonic-gate static dev_info_t *hubd_get_child_dip(hubd_t *, usb_port_t); 100*7c478bd9Sstevel@tonic-gate static uint_t hubd_cfgadm_state(hubd_t *, usb_port_t); 101*7c478bd9Sstevel@tonic-gate static int hubd_toggle_port(hubd_t *, usb_port_t); 102*7c478bd9Sstevel@tonic-gate static void hubd_register_cpr_callback(hubd_t *); 103*7c478bd9Sstevel@tonic-gate static void hubd_unregister_cpr_callback(hubd_t *); 104*7c478bd9Sstevel@tonic-gate static hubd_t *hubd_get_soft_state(dev_info_t *dip); 105*7c478bd9Sstevel@tonic-gate 106*7c478bd9Sstevel@tonic-gate /* 107*7c478bd9Sstevel@tonic-gate * Busops vector for USB HUB's 108*7c478bd9Sstevel@tonic-gate */ 109*7c478bd9Sstevel@tonic-gate struct bus_ops usba_hubdi_busops = { 110*7c478bd9Sstevel@tonic-gate BUSO_REV, 111*7c478bd9Sstevel@tonic-gate nullbusmap, /* bus_map */ 112*7c478bd9Sstevel@tonic-gate NULL, /* bus_get_intrspec */ 113*7c478bd9Sstevel@tonic-gate NULL, /* bus_add_intrspec */ 114*7c478bd9Sstevel@tonic-gate NULL, /* bus_remove_intrspec */ 115*7c478bd9Sstevel@tonic-gate usba_hubdi_map_fault, /* bus_map_fault */ 116*7c478bd9Sstevel@tonic-gate ddi_dma_map, /* bus_dma_map */ 117*7c478bd9Sstevel@tonic-gate ddi_dma_allochdl, 118*7c478bd9Sstevel@tonic-gate ddi_dma_freehdl, 119*7c478bd9Sstevel@tonic-gate ddi_dma_bindhdl, 120*7c478bd9Sstevel@tonic-gate ddi_dma_unbindhdl, 121*7c478bd9Sstevel@tonic-gate ddi_dma_flush, 122*7c478bd9Sstevel@tonic-gate ddi_dma_win, 123*7c478bd9Sstevel@tonic-gate ddi_dma_mctl, /* bus_dma_ctl */ 124*7c478bd9Sstevel@tonic-gate usba_hubdi_bus_ctl, /* bus_ctl */ 125*7c478bd9Sstevel@tonic-gate ddi_bus_prop_op, /* bus_prop_op */ 126*7c478bd9Sstevel@tonic-gate hubd_busop_get_eventcookie, 127*7c478bd9Sstevel@tonic-gate hubd_busop_add_eventcall, 128*7c478bd9Sstevel@tonic-gate hubd_busop_remove_eventcall, 129*7c478bd9Sstevel@tonic-gate NULL, /* bus_post_event */ 130*7c478bd9Sstevel@tonic-gate NULL, /* bus_intr_ctl */ 131*7c478bd9Sstevel@tonic-gate hubd_bus_config, /* bus_config */ 132*7c478bd9Sstevel@tonic-gate hubd_bus_unconfig, /* bus_unconfig */ 133*7c478bd9Sstevel@tonic-gate NULL, /* bus_fm_init */ 134*7c478bd9Sstevel@tonic-gate NULL, /* bus_fm_fini */ 135*7c478bd9Sstevel@tonic-gate NULL, /* bus_fm_access_enter */ 136*7c478bd9Sstevel@tonic-gate NULL, /* bus_fm_access_exit */ 137*7c478bd9Sstevel@tonic-gate hubd_bus_power /* bus_power */ 138*7c478bd9Sstevel@tonic-gate }; 139*7c478bd9Sstevel@tonic-gate 140*7c478bd9Sstevel@tonic-gate 141*7c478bd9Sstevel@tonic-gate /* 142*7c478bd9Sstevel@tonic-gate * local variables 143*7c478bd9Sstevel@tonic-gate */ 144*7c478bd9Sstevel@tonic-gate static kmutex_t usba_hubdi_mutex; /* protects USBA HUB data structures */ 145*7c478bd9Sstevel@tonic-gate 146*7c478bd9Sstevel@tonic-gate static usba_list_entry_t usba_hubdi_list; 147*7c478bd9Sstevel@tonic-gate 148*7c478bd9Sstevel@tonic-gate usb_log_handle_t hubdi_log_handle; 149*7c478bd9Sstevel@tonic-gate uint_t hubdi_errlevel = USB_LOG_L4; 150*7c478bd9Sstevel@tonic-gate uint_t hubdi_errmask = (uint_t)-1; 151*7c478bd9Sstevel@tonic-gate uint_t hubdi_min_pm_threshold = 5; /* seconds */ 152*7c478bd9Sstevel@tonic-gate 153*7c478bd9Sstevel@tonic-gate /* 154*7c478bd9Sstevel@tonic-gate * initialize private data 155*7c478bd9Sstevel@tonic-gate */ 156*7c478bd9Sstevel@tonic-gate void 157*7c478bd9Sstevel@tonic-gate usba_hubdi_initialization() 158*7c478bd9Sstevel@tonic-gate { 159*7c478bd9Sstevel@tonic-gate hubdi_log_handle = usb_alloc_log_hdl(NULL, "hubdi", &hubdi_errlevel, 160*7c478bd9Sstevel@tonic-gate &hubdi_errmask, NULL, 0); 161*7c478bd9Sstevel@tonic-gate 162*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle, 163*7c478bd9Sstevel@tonic-gate "usba_hubdi_initialization"); 164*7c478bd9Sstevel@tonic-gate 165*7c478bd9Sstevel@tonic-gate mutex_init(&usba_hubdi_mutex, NULL, MUTEX_DRIVER, NULL); 166*7c478bd9Sstevel@tonic-gate 167*7c478bd9Sstevel@tonic-gate usba_init_list(&usba_hubdi_list, NULL, NULL); 168*7c478bd9Sstevel@tonic-gate } 169*7c478bd9Sstevel@tonic-gate 170*7c478bd9Sstevel@tonic-gate 171*7c478bd9Sstevel@tonic-gate void 172*7c478bd9Sstevel@tonic-gate usba_hubdi_destroy() 173*7c478bd9Sstevel@tonic-gate { 174*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle, 175*7c478bd9Sstevel@tonic-gate "usba_hubdi_destroy"); 176*7c478bd9Sstevel@tonic-gate 177*7c478bd9Sstevel@tonic-gate mutex_destroy(&usba_hubdi_mutex); 178*7c478bd9Sstevel@tonic-gate usba_destroy_list(&usba_hubdi_list); 179*7c478bd9Sstevel@tonic-gate 180*7c478bd9Sstevel@tonic-gate usb_free_log_hdl(hubdi_log_handle); 181*7c478bd9Sstevel@tonic-gate } 182*7c478bd9Sstevel@tonic-gate 183*7c478bd9Sstevel@tonic-gate 184*7c478bd9Sstevel@tonic-gate /* 185*7c478bd9Sstevel@tonic-gate * Called by an HUB to attach an instance of the driver 186*7c478bd9Sstevel@tonic-gate * make this instance known to USBA 187*7c478bd9Sstevel@tonic-gate * the HUB should initialize usba_hubdi structure prior 188*7c478bd9Sstevel@tonic-gate * to calling this interface 189*7c478bd9Sstevel@tonic-gate */ 190*7c478bd9Sstevel@tonic-gate static int 191*7c478bd9Sstevel@tonic-gate usba_hubdi_register(dev_info_t *dip, 192*7c478bd9Sstevel@tonic-gate uint_t flags) 193*7c478bd9Sstevel@tonic-gate { 194*7c478bd9Sstevel@tonic-gate usba_hubdi_t *hubdi = kmem_zalloc(sizeof (usba_hubdi_t), KM_SLEEP); 195*7c478bd9Sstevel@tonic-gate usba_device_t *usba_device = usba_get_usba_device(dip); 196*7c478bd9Sstevel@tonic-gate 197*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle, 198*7c478bd9Sstevel@tonic-gate "usba_hubdi_register: %s", ddi_node_name(dip)); 199*7c478bd9Sstevel@tonic-gate 200*7c478bd9Sstevel@tonic-gate hubdi->hubdi_dip = dip; 201*7c478bd9Sstevel@tonic-gate hubdi->hubdi_flags = flags; 202*7c478bd9Sstevel@tonic-gate 203*7c478bd9Sstevel@tonic-gate usba_device->usb_hubdi = hubdi; 204*7c478bd9Sstevel@tonic-gate 205*7c478bd9Sstevel@tonic-gate /* 206*7c478bd9Sstevel@tonic-gate * add this hubdi instance to the list of known hubdi's 207*7c478bd9Sstevel@tonic-gate */ 208*7c478bd9Sstevel@tonic-gate usba_init_list(&hubdi->hubdi_list, (usb_opaque_t)hubdi, 209*7c478bd9Sstevel@tonic-gate usba_hcdi_get_hcdi(usba_device->usb_root_hub_dip)-> 210*7c478bd9Sstevel@tonic-gate hcdi_iblock_cookie); 211*7c478bd9Sstevel@tonic-gate mutex_enter(&usba_hubdi_mutex); 212*7c478bd9Sstevel@tonic-gate usba_add_to_list(&usba_hubdi_list, &hubdi->hubdi_list); 213*7c478bd9Sstevel@tonic-gate mutex_exit(&usba_hubdi_mutex); 214*7c478bd9Sstevel@tonic-gate 215*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 216*7c478bd9Sstevel@tonic-gate } 217*7c478bd9Sstevel@tonic-gate 218*7c478bd9Sstevel@tonic-gate 219*7c478bd9Sstevel@tonic-gate /* 220*7c478bd9Sstevel@tonic-gate * Called by an HUB to detach an instance of the driver 221*7c478bd9Sstevel@tonic-gate */ 222*7c478bd9Sstevel@tonic-gate static int 223*7c478bd9Sstevel@tonic-gate usba_hubdi_unregister(dev_info_t *dip) 224*7c478bd9Sstevel@tonic-gate { 225*7c478bd9Sstevel@tonic-gate usba_device_t *usba_device = usba_get_usba_device(dip); 226*7c478bd9Sstevel@tonic-gate usba_hubdi_t *hubdi = usba_device->usb_hubdi; 227*7c478bd9Sstevel@tonic-gate 228*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle, 229*7c478bd9Sstevel@tonic-gate "usba_hubdi_unregister: %s", ddi_node_name(dip)); 230*7c478bd9Sstevel@tonic-gate 231*7c478bd9Sstevel@tonic-gate mutex_enter(&usba_hubdi_mutex); 232*7c478bd9Sstevel@tonic-gate (void) usba_rm_from_list(&usba_hubdi_list, &hubdi->hubdi_list); 233*7c478bd9Sstevel@tonic-gate mutex_exit(&usba_hubdi_mutex); 234*7c478bd9Sstevel@tonic-gate 235*7c478bd9Sstevel@tonic-gate usba_destroy_list(&hubdi->hubdi_list); 236*7c478bd9Sstevel@tonic-gate 237*7c478bd9Sstevel@tonic-gate kmem_free(hubdi, sizeof (usba_hubdi_t)); 238*7c478bd9Sstevel@tonic-gate 239*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 240*7c478bd9Sstevel@tonic-gate } 241*7c478bd9Sstevel@tonic-gate 242*7c478bd9Sstevel@tonic-gate 243*7c478bd9Sstevel@tonic-gate /* 244*7c478bd9Sstevel@tonic-gate * misc bus routines currently not used 245*7c478bd9Sstevel@tonic-gate */ 246*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 247*7c478bd9Sstevel@tonic-gate static int 248*7c478bd9Sstevel@tonic-gate usba_hubdi_map_fault(dev_info_t *dip, 249*7c478bd9Sstevel@tonic-gate dev_info_t *rdip, 250*7c478bd9Sstevel@tonic-gate struct hat *hat, 251*7c478bd9Sstevel@tonic-gate struct seg *seg, 252*7c478bd9Sstevel@tonic-gate caddr_t addr, 253*7c478bd9Sstevel@tonic-gate struct devpage *dp, 254*7c478bd9Sstevel@tonic-gate pfn_t pfn, 255*7c478bd9Sstevel@tonic-gate uint_t prot, 256*7c478bd9Sstevel@tonic-gate uint_t lock) 257*7c478bd9Sstevel@tonic-gate { 258*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 259*7c478bd9Sstevel@tonic-gate } 260*7c478bd9Sstevel@tonic-gate 261*7c478bd9Sstevel@tonic-gate 262*7c478bd9Sstevel@tonic-gate /* 263*7c478bd9Sstevel@tonic-gate * root hub support. the root hub uses the same devi as the HCD 264*7c478bd9Sstevel@tonic-gate */ 265*7c478bd9Sstevel@tonic-gate int 266*7c478bd9Sstevel@tonic-gate usba_hubdi_bind_root_hub(dev_info_t *dip, 267*7c478bd9Sstevel@tonic-gate uchar_t *root_hub_config_descriptor, 268*7c478bd9Sstevel@tonic-gate size_t config_length, 269*7c478bd9Sstevel@tonic-gate usb_dev_descr_t *root_hub_device_descriptor) 270*7c478bd9Sstevel@tonic-gate { 271*7c478bd9Sstevel@tonic-gate usba_device_t *usba_device; 272*7c478bd9Sstevel@tonic-gate usba_hcdi_t *hcdi = usba_hcdi_get_hcdi(dip); 273*7c478bd9Sstevel@tonic-gate hubd_t *root_hubd; 274*7c478bd9Sstevel@tonic-gate usb_pipe_handle_t ph = NULL; 275*7c478bd9Sstevel@tonic-gate dev_info_t *child = ddi_get_child(dip); 276*7c478bd9Sstevel@tonic-gate 277*7c478bd9Sstevel@tonic-gate if (ndi_prop_create_boolean(DDI_DEV_T_NONE, dip, 278*7c478bd9Sstevel@tonic-gate "root-hub") != NDI_SUCCESS) { 279*7c478bd9Sstevel@tonic-gate 280*7c478bd9Sstevel@tonic-gate return (USB_FAILURE); 281*7c478bd9Sstevel@tonic-gate } 282*7c478bd9Sstevel@tonic-gate 283*7c478bd9Sstevel@tonic-gate root_hubd = kmem_zalloc(sizeof (hubd_t), KM_SLEEP); 284*7c478bd9Sstevel@tonic-gate 285*7c478bd9Sstevel@tonic-gate /* 286*7c478bd9Sstevel@tonic-gate * create and initialize a usba_device structure 287*7c478bd9Sstevel@tonic-gate */ 288*7c478bd9Sstevel@tonic-gate usba_device = usba_alloc_usba_device(dip); 289*7c478bd9Sstevel@tonic-gate 290*7c478bd9Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex); 291*7c478bd9Sstevel@tonic-gate usba_device->usb_hcdi_ops = hcdi->hcdi_ops; 292*7c478bd9Sstevel@tonic-gate usba_device->usb_cfg = root_hub_config_descriptor; 293*7c478bd9Sstevel@tonic-gate usba_device->usb_cfg_length = config_length; 294*7c478bd9Sstevel@tonic-gate usba_device->usb_dev_descr = root_hub_device_descriptor; 295*7c478bd9Sstevel@tonic-gate usba_device->usb_port = 1; 296*7c478bd9Sstevel@tonic-gate usba_device->usb_addr = ROOT_HUB_ADDR; 297*7c478bd9Sstevel@tonic-gate usba_device->usb_root_hubd = root_hubd; 298*7c478bd9Sstevel@tonic-gate usba_device->usb_cfg_array = kmem_zalloc(sizeof (uchar_t *), 299*7c478bd9Sstevel@tonic-gate KM_SLEEP); 300*7c478bd9Sstevel@tonic-gate usba_device->usb_cfg_array_length = sizeof (uchar_t *); 301*7c478bd9Sstevel@tonic-gate 302*7c478bd9Sstevel@tonic-gate usba_device->usb_cfg_array_len = kmem_zalloc(sizeof (uint16_t), 303*7c478bd9Sstevel@tonic-gate KM_SLEEP); 304*7c478bd9Sstevel@tonic-gate usba_device->usb_cfg_array_len_length = sizeof (uint16_t); 305*7c478bd9Sstevel@tonic-gate 306*7c478bd9Sstevel@tonic-gate usba_device->usb_cfg_array[0] = root_hub_config_descriptor; 307*7c478bd9Sstevel@tonic-gate usba_device->usb_cfg_array_len[0] = 308*7c478bd9Sstevel@tonic-gate sizeof (root_hub_config_descriptor); 309*7c478bd9Sstevel@tonic-gate 310*7c478bd9Sstevel@tonic-gate usba_device->usb_cfg_str_descr = kmem_zalloc(sizeof (uchar_t *), 311*7c478bd9Sstevel@tonic-gate KM_SLEEP); 312*7c478bd9Sstevel@tonic-gate usba_device->usb_n_cfgs = 1; 313*7c478bd9Sstevel@tonic-gate usba_device->usb_n_ifs = 1; 314*7c478bd9Sstevel@tonic-gate usba_device->usb_dip = dip; 315*7c478bd9Sstevel@tonic-gate 316*7c478bd9Sstevel@tonic-gate usba_device->usb_client_flags = kmem_zalloc( 317*7c478bd9Sstevel@tonic-gate usba_device->usb_n_ifs * USBA_CLIENT_FLAG_SIZE, KM_SLEEP); 318*7c478bd9Sstevel@tonic-gate 319*7c478bd9Sstevel@tonic-gate usba_device->usb_client_attach_list = kmem_zalloc( 320*7c478bd9Sstevel@tonic-gate usba_device->usb_n_ifs * 321*7c478bd9Sstevel@tonic-gate sizeof (*usba_device->usb_client_attach_list), KM_SLEEP); 322*7c478bd9Sstevel@tonic-gate 323*7c478bd9Sstevel@tonic-gate usba_device->usb_client_ev_cb_list = kmem_zalloc( 324*7c478bd9Sstevel@tonic-gate usba_device->usb_n_ifs * 325*7c478bd9Sstevel@tonic-gate sizeof (*usba_device->usb_client_ev_cb_list), KM_SLEEP); 326*7c478bd9Sstevel@tonic-gate 327*7c478bd9Sstevel@tonic-gate /* 328*7c478bd9Sstevel@tonic-gate * The bDeviceProtocol field of root hub device specifies, 329*7c478bd9Sstevel@tonic-gate * whether root hub is a High or Full speed usb device. 330*7c478bd9Sstevel@tonic-gate */ 331*7c478bd9Sstevel@tonic-gate if (root_hub_device_descriptor->bDeviceProtocol) { 332*7c478bd9Sstevel@tonic-gate usba_device->usb_port_status = USBA_HIGH_SPEED_DEV; 333*7c478bd9Sstevel@tonic-gate } else { 334*7c478bd9Sstevel@tonic-gate usba_device->usb_port_status = USBA_FULL_SPEED_DEV; 335*7c478bd9Sstevel@tonic-gate } 336*7c478bd9Sstevel@tonic-gate 337*7c478bd9Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex); 338*7c478bd9Sstevel@tonic-gate 339*7c478bd9Sstevel@tonic-gate usba_set_usba_device(dip, usba_device); 340*7c478bd9Sstevel@tonic-gate 341*7c478bd9Sstevel@tonic-gate /* 342*7c478bd9Sstevel@tonic-gate * For the root hub the default pipe is not yet open 343*7c478bd9Sstevel@tonic-gate */ 344*7c478bd9Sstevel@tonic-gate if (usb_pipe_open(dip, NULL, NULL, 345*7c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph) != USB_SUCCESS) { 346*7c478bd9Sstevel@tonic-gate goto fail; 347*7c478bd9Sstevel@tonic-gate } 348*7c478bd9Sstevel@tonic-gate 349*7c478bd9Sstevel@tonic-gate /* 350*7c478bd9Sstevel@tonic-gate * kill off all OBP children, they may not be fully 351*7c478bd9Sstevel@tonic-gate * enumerated 352*7c478bd9Sstevel@tonic-gate */ 353*7c478bd9Sstevel@tonic-gate while (child) { 354*7c478bd9Sstevel@tonic-gate dev_info_t *next = ddi_get_next_sibling(child); 355*7c478bd9Sstevel@tonic-gate (void) ddi_remove_child(child, 0); 356*7c478bd9Sstevel@tonic-gate child = next; 357*7c478bd9Sstevel@tonic-gate } 358*7c478bd9Sstevel@tonic-gate 359*7c478bd9Sstevel@tonic-gate /* 360*7c478bd9Sstevel@tonic-gate * "attach" the root hub driver 361*7c478bd9Sstevel@tonic-gate */ 362*7c478bd9Sstevel@tonic-gate if (usba_hubdi_attach(dip, DDI_ATTACH) != DDI_SUCCESS) { 363*7c478bd9Sstevel@tonic-gate goto fail; 364*7c478bd9Sstevel@tonic-gate } 365*7c478bd9Sstevel@tonic-gate 366*7c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 367*7c478bd9Sstevel@tonic-gate 368*7c478bd9Sstevel@tonic-gate fail: 369*7c478bd9Sstevel@tonic-gate (void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "root-hub"); 370*7c478bd9Sstevel@tonic-gate 371*7c478bd9Sstevel@tonic-gate if (ph) { 372*7c478bd9Sstevel@tonic-gate usb_pipe_close(dip, ph, 373*7c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL); 374*7c478bd9Sstevel@tonic-gate } 375*7c478bd9Sstevel@tonic-gate 376*7c478bd9Sstevel@tonic-gate kmem_free(usba_device->usb_cfg_array, 377*7c478bd9Sstevel@tonic-gate usba_device->usb_cfg_array_length); 378*7c478bd9Sstevel@tonic-gate kmem_free(usba_device->usb_cfg_array_len, 379*7c478bd9Sstevel@tonic-gate usba_device->usb_cfg_array_len_length); 380*7c478bd9Sstevel@tonic-gate 381*7c478bd9Sstevel@tonic-gate kmem_free(usba_device->usb_cfg_str_descr, sizeof (uchar_t *)); 382*7c478bd9Sstevel@tonic-gate 383*7c478bd9Sstevel@tonic-gate usba_free_usba_device(usba_device); 384*7c478bd9Sstevel@tonic-gate 385*7c478bd9Sstevel@tonic-gate usba_set_usba_device(dip, NULL); 386*7c478bd9Sstevel@tonic-gate if (root_hubd) { 387*7c478bd9Sstevel@tonic-gate kmem_free(root_hubd, sizeof (hubd_t)); 388*7c478bd9Sstevel@tonic-gate } 389*7c478bd9Sstevel@tonic-gate 390*7c478bd9Sstevel@tonic-gate return (USB_FAILURE); 391*7c478bd9Sstevel@tonic-gate } 392*7c478bd9Sstevel@tonic-gate 393*7c478bd9Sstevel@tonic-gate 394*7c478bd9Sstevel@tonic-gate int 395*7c478bd9Sstevel@tonic-gate usba_hubdi_unbind_root_hub(dev_info_t *dip) 396*7c478bd9Sstevel@tonic-gate { 397*7c478bd9Sstevel@tonic-gate usba_device_t *usba_device; 398*7c478bd9Sstevel@tonic-gate 399*7c478bd9Sstevel@tonic-gate /* was root hub attached? */ 400*7c478bd9Sstevel@tonic-gate if (!(usba_is_root_hub(dip))) { 401*7c478bd9Sstevel@tonic-gate 402*7c478bd9Sstevel@tonic-gate /* return success anyway */ 403*7c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 404*7c478bd9Sstevel@tonic-gate } 405*7c478bd9Sstevel@tonic-gate 406*7c478bd9Sstevel@tonic-gate /* 407*7c478bd9Sstevel@tonic-gate * usba_hubdi_detach also closes the default pipe 408*7c478bd9Sstevel@tonic-gate * and removes properties so there is no need to 409*7c478bd9Sstevel@tonic-gate * do it here 410*7c478bd9Sstevel@tonic-gate */ 411*7c478bd9Sstevel@tonic-gate if (usba_hubdi_detach(dip, DDI_DETACH) != DDI_SUCCESS) { 412*7c478bd9Sstevel@tonic-gate 413*7c478bd9Sstevel@tonic-gate if (DEVI_IS_ATTACHING(dip)) { 414*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_ATTA, hubdi_log_handle, 415*7c478bd9Sstevel@tonic-gate "failure to unbind root hub after attach failure"); 416*7c478bd9Sstevel@tonic-gate } 417*7c478bd9Sstevel@tonic-gate 418*7c478bd9Sstevel@tonic-gate return (USB_FAILURE); 419*7c478bd9Sstevel@tonic-gate } 420*7c478bd9Sstevel@tonic-gate 421*7c478bd9Sstevel@tonic-gate usba_device = usba_get_usba_device(dip); 422*7c478bd9Sstevel@tonic-gate 423*7c478bd9Sstevel@tonic-gate kmem_free(usba_device->usb_root_hubd, sizeof (hubd_t)); 424*7c478bd9Sstevel@tonic-gate 425*7c478bd9Sstevel@tonic-gate kmem_free(usba_device->usb_cfg_array, 426*7c478bd9Sstevel@tonic-gate usba_device->usb_cfg_array_length); 427*7c478bd9Sstevel@tonic-gate kmem_free(usba_device->usb_cfg_array_len, 428*7c478bd9Sstevel@tonic-gate usba_device->usb_cfg_array_len_length); 429*7c478bd9Sstevel@tonic-gate 430*7c478bd9Sstevel@tonic-gate kmem_free(usba_device->usb_cfg_str_descr, sizeof (uchar_t *)); 431*7c478bd9Sstevel@tonic-gate 432*7c478bd9Sstevel@tonic-gate usba_free_usba_device(usba_device); 433*7c478bd9Sstevel@tonic-gate 434*7c478bd9Sstevel@tonic-gate (void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "root-hub"); 435*7c478bd9Sstevel@tonic-gate 436*7c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 437*7c478bd9Sstevel@tonic-gate } 438*7c478bd9Sstevel@tonic-gate 439*7c478bd9Sstevel@tonic-gate 440*7c478bd9Sstevel@tonic-gate /* 441*7c478bd9Sstevel@tonic-gate * Actual Hub Driver support code: 442*7c478bd9Sstevel@tonic-gate * shared by root hub and non-root hubs 443*7c478bd9Sstevel@tonic-gate */ 444*7c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usbai_version.h> 445*7c478bd9Sstevel@tonic-gate 446*7c478bd9Sstevel@tonic-gate /* Debugging support */ 447*7c478bd9Sstevel@tonic-gate static uint_t hubd_errlevel = USB_LOG_L4; 448*7c478bd9Sstevel@tonic-gate static uint_t hubd_errmask = (uint_t)DPRINT_MASK_ALL; 449*7c478bd9Sstevel@tonic-gate static uint_t hubd_instance_debug = (uint_t)-1; 450*7c478bd9Sstevel@tonic-gate static uint_t hubdi_bus_config_debug = 0; 451*7c478bd9Sstevel@tonic-gate 452*7c478bd9Sstevel@tonic-gate _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_errlevel)) 453*7c478bd9Sstevel@tonic-gate _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_errmask)) 454*7c478bd9Sstevel@tonic-gate _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_instance_debug)) 455*7c478bd9Sstevel@tonic-gate 456*7c478bd9Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique", msgb)) 457*7c478bd9Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique", dev_info)) 458*7c478bd9Sstevel@tonic-gate 459*7c478bd9Sstevel@tonic-gate 460*7c478bd9Sstevel@tonic-gate /* 461*7c478bd9Sstevel@tonic-gate * local variables: 462*7c478bd9Sstevel@tonic-gate * 463*7c478bd9Sstevel@tonic-gate * Amount of time to wait between resetting the port and accessing 464*7c478bd9Sstevel@tonic-gate * the device. The value is in microseconds. 465*7c478bd9Sstevel@tonic-gate */ 466*7c478bd9Sstevel@tonic-gate static uint_t hubd_device_delay = 1000000; 467*7c478bd9Sstevel@tonic-gate 468*7c478bd9Sstevel@tonic-gate /* 469*7c478bd9Sstevel@tonic-gate * enumeration retry 470*7c478bd9Sstevel@tonic-gate */ 471*7c478bd9Sstevel@tonic-gate #define HUBD_PORT_RETRY 5 472*7c478bd9Sstevel@tonic-gate static uint_t hubd_retry_enumerate = HUBD_PORT_RETRY; 473*7c478bd9Sstevel@tonic-gate 474*7c478bd9Sstevel@tonic-gate /* 475*7c478bd9Sstevel@tonic-gate * Stale hotremoved device cleanup delay 476*7c478bd9Sstevel@tonic-gate */ 477*7c478bd9Sstevel@tonic-gate #define HUBD_STALE_DIP_CLEANUP_DELAY 5000000 478*7c478bd9Sstevel@tonic-gate static uint_t hubd_dip_cleanup_delay = HUBD_STALE_DIP_CLEANUP_DELAY; 479*7c478bd9Sstevel@tonic-gate 480*7c478bd9Sstevel@tonic-gate /* 481*7c478bd9Sstevel@tonic-gate * retries for USB suspend and resume 482*7c478bd9Sstevel@tonic-gate */ 483*7c478bd9Sstevel@tonic-gate #define HUBD_SUS_RES_RETRY 2 484*7c478bd9Sstevel@tonic-gate 485*7c478bd9Sstevel@tonic-gate void *hubd_statep; 486*7c478bd9Sstevel@tonic-gate 487*7c478bd9Sstevel@tonic-gate /* 488*7c478bd9Sstevel@tonic-gate * prototypes 489*7c478bd9Sstevel@tonic-gate */ 490*7c478bd9Sstevel@tonic-gate static int hubd_cleanup(dev_info_t *dip, hubd_t *hubd); 491*7c478bd9Sstevel@tonic-gate static int hubd_check_ports(hubd_t *hubd); 492*7c478bd9Sstevel@tonic-gate 493*7c478bd9Sstevel@tonic-gate static void hubd_schedule_cleanup(dev_info_t *); 494*7c478bd9Sstevel@tonic-gate 495*7c478bd9Sstevel@tonic-gate static int hubd_open_intr_pipe(hubd_t *hubd); 496*7c478bd9Sstevel@tonic-gate static void hubd_start_polling(hubd_t *hubd, int always); 497*7c478bd9Sstevel@tonic-gate static void hubd_stop_polling(hubd_t *hubd); 498*7c478bd9Sstevel@tonic-gate static void hubd_close_intr_pipe(hubd_t *hubd); 499*7c478bd9Sstevel@tonic-gate 500*7c478bd9Sstevel@tonic-gate static void hubd_read_cb(usb_pipe_handle_t pipe, usb_intr_req_t *req); 501*7c478bd9Sstevel@tonic-gate static void hubd_exception_cb(usb_pipe_handle_t pipe, 502*7c478bd9Sstevel@tonic-gate usb_intr_req_t *req); 503*7c478bd9Sstevel@tonic-gate static void hubd_hotplug_thread(void *arg); 504*7c478bd9Sstevel@tonic-gate static int hubd_create_child(dev_info_t *dip, 505*7c478bd9Sstevel@tonic-gate hubd_t *hubd, 506*7c478bd9Sstevel@tonic-gate usba_device_t *usba_device, 507*7c478bd9Sstevel@tonic-gate usb_port_status_t port_status, 508*7c478bd9Sstevel@tonic-gate usb_port_t port, 509*7c478bd9Sstevel@tonic-gate int iteration); 510*7c478bd9Sstevel@tonic-gate 511*7c478bd9Sstevel@tonic-gate static int hubd_delete_child(hubd_t *hubd, usb_port_t port, uint_t flag, 512*7c478bd9Sstevel@tonic-gate boolean_t retry); 513*7c478bd9Sstevel@tonic-gate 514*7c478bd9Sstevel@tonic-gate static int hubd_get_hub_descriptor(hubd_t *hubd); 515*7c478bd9Sstevel@tonic-gate 516*7c478bd9Sstevel@tonic-gate static int hubd_reset_port(hubd_t *hubd, usb_port_t port); 517*7c478bd9Sstevel@tonic-gate 518*7c478bd9Sstevel@tonic-gate static int hubd_get_hub_status(hubd_t *hubd); 519*7c478bd9Sstevel@tonic-gate 520*7c478bd9Sstevel@tonic-gate static int hubd_handle_port_connect(hubd_t *hubd, usb_port_t port); 521*7c478bd9Sstevel@tonic-gate 522*7c478bd9Sstevel@tonic-gate static int hubd_disable_port(hubd_t *hubd, usb_port_t port); 523*7c478bd9Sstevel@tonic-gate 524*7c478bd9Sstevel@tonic-gate static int hubd_enable_port(hubd_t *hubd, usb_port_t port); 525*7c478bd9Sstevel@tonic-gate static int hubd_recover_disabled_port(hubd_t *hubd, usb_port_t port); 526*7c478bd9Sstevel@tonic-gate 527*7c478bd9Sstevel@tonic-gate static int hubd_determine_port_status(hubd_t *hubd, usb_port_t port, 528*7c478bd9Sstevel@tonic-gate uint16_t *status, uint16_t *change, uint_t ack_flag); 529*7c478bd9Sstevel@tonic-gate 530*7c478bd9Sstevel@tonic-gate static int hubd_enable_all_port_power(hubd_t *hubd); 531*7c478bd9Sstevel@tonic-gate static int hubd_disable_all_port_power(hubd_t *hubd); 532*7c478bd9Sstevel@tonic-gate static int hubd_disable_port_power(hubd_t *hubd, usb_port_t port); 533*7c478bd9Sstevel@tonic-gate static int hubd_enable_port_power(hubd_t *hubd, usb_port_t port); 534*7c478bd9Sstevel@tonic-gate 535*7c478bd9Sstevel@tonic-gate static void hubd_free_usba_device(hubd_t *hubd, usba_device_t *usba_device); 536*7c478bd9Sstevel@tonic-gate 537*7c478bd9Sstevel@tonic-gate static int hubd_can_suspend(hubd_t *hubd); 538*7c478bd9Sstevel@tonic-gate static void hubd_restore_device_state(dev_info_t *dip, hubd_t *hubd); 539*7c478bd9Sstevel@tonic-gate static int hubd_setdevaddr(hubd_t *hubd, usb_port_t port); 540*7c478bd9Sstevel@tonic-gate static void hubd_setdevconfig(hubd_t *hubd, usb_port_t port); 541*7c478bd9Sstevel@tonic-gate 542*7c478bd9Sstevel@tonic-gate static int hubd_register_events(hubd_t *hubd); 543*7c478bd9Sstevel@tonic-gate static void hubd_do_callback(hubd_t *hubd, dev_info_t *dip, 544*7c478bd9Sstevel@tonic-gate ddi_eventcookie_t cookie); 545*7c478bd9Sstevel@tonic-gate static void hubd_run_callbacks(hubd_t *hubd, usba_event_t type); 546*7c478bd9Sstevel@tonic-gate static void hubd_post_event(hubd_t *hubd, usb_port_t port, usba_event_t type); 547*7c478bd9Sstevel@tonic-gate static void hubd_create_pm_components(dev_info_t *dip, hubd_t *hubd); 548*7c478bd9Sstevel@tonic-gate 549*7c478bd9Sstevel@tonic-gate static int hubd_disconnect_event_cb(dev_info_t *dip); 550*7c478bd9Sstevel@tonic-gate static int hubd_reconnect_event_cb(dev_info_t *dip); 551*7c478bd9Sstevel@tonic-gate static int hubd_pre_suspend_event_cb(dev_info_t *dip); 552*7c478bd9Sstevel@tonic-gate static int hubd_post_resume_event_cb(dev_info_t *dip); 553*7c478bd9Sstevel@tonic-gate static int hubd_cpr_suspend(hubd_t *hubd); 554*7c478bd9Sstevel@tonic-gate static void hubd_cpr_resume(dev_info_t *dip); 555*7c478bd9Sstevel@tonic-gate static int hubd_restore_state_cb(dev_info_t *dip); 556*7c478bd9Sstevel@tonic-gate 557*7c478bd9Sstevel@tonic-gate static ndi_event_definition_t hubd_ndi_event_defs[] = { 558*7c478bd9Sstevel@tonic-gate {USBA_EVENT_TAG_HOT_REMOVAL, DDI_DEVI_REMOVE_EVENT, EPL_KERNEL, 559*7c478bd9Sstevel@tonic-gate NDI_EVENT_POST_TO_ALL}, 560*7c478bd9Sstevel@tonic-gate {USBA_EVENT_TAG_HOT_INSERTION, DDI_DEVI_INSERT_EVENT, EPL_KERNEL, 561*7c478bd9Sstevel@tonic-gate NDI_EVENT_POST_TO_ALL}, 562*7c478bd9Sstevel@tonic-gate {USBA_EVENT_TAG_POST_RESUME, USBA_POST_RESUME_EVENT, EPL_KERNEL, 563*7c478bd9Sstevel@tonic-gate NDI_EVENT_POST_TO_ALL}, 564*7c478bd9Sstevel@tonic-gate {USBA_EVENT_TAG_PRE_SUSPEND, USBA_PRE_SUSPEND_EVENT, EPL_KERNEL, 565*7c478bd9Sstevel@tonic-gate NDI_EVENT_POST_TO_ALL} 566*7c478bd9Sstevel@tonic-gate }; 567*7c478bd9Sstevel@tonic-gate 568*7c478bd9Sstevel@tonic-gate #define HUBD_N_NDI_EVENTS \ 569*7c478bd9Sstevel@tonic-gate (sizeof (hubd_ndi_event_defs) / sizeof (ndi_event_definition_t)) 570*7c478bd9Sstevel@tonic-gate 571*7c478bd9Sstevel@tonic-gate static ndi_event_set_t hubd_ndi_events = { 572*7c478bd9Sstevel@tonic-gate NDI_EVENTS_REV1, HUBD_N_NDI_EVENTS, hubd_ndi_event_defs}; 573*7c478bd9Sstevel@tonic-gate 574*7c478bd9Sstevel@tonic-gate /* events received from parent */ 575*7c478bd9Sstevel@tonic-gate static usb_event_t hubd_events = { 576*7c478bd9Sstevel@tonic-gate hubd_disconnect_event_cb, 577*7c478bd9Sstevel@tonic-gate hubd_reconnect_event_cb, 578*7c478bd9Sstevel@tonic-gate hubd_pre_suspend_event_cb, 579*7c478bd9Sstevel@tonic-gate hubd_post_resume_event_cb 580*7c478bd9Sstevel@tonic-gate }; 581*7c478bd9Sstevel@tonic-gate 582*7c478bd9Sstevel@tonic-gate 583*7c478bd9Sstevel@tonic-gate /* 584*7c478bd9Sstevel@tonic-gate * hubd_get_soft_state() returns the hubd soft state 585*7c478bd9Sstevel@tonic-gate */ 586*7c478bd9Sstevel@tonic-gate static hubd_t * 587*7c478bd9Sstevel@tonic-gate hubd_get_soft_state(dev_info_t *dip) 588*7c478bd9Sstevel@tonic-gate { 589*7c478bd9Sstevel@tonic-gate if (dip == NULL) { 590*7c478bd9Sstevel@tonic-gate 591*7c478bd9Sstevel@tonic-gate return (NULL); 592*7c478bd9Sstevel@tonic-gate } 593*7c478bd9Sstevel@tonic-gate 594*7c478bd9Sstevel@tonic-gate if (usba_is_root_hub(dip)) { 595*7c478bd9Sstevel@tonic-gate usba_device_t *usba_device = usba_get_usba_device(dip); 596*7c478bd9Sstevel@tonic-gate 597*7c478bd9Sstevel@tonic-gate return (usba_device->usb_root_hubd); 598*7c478bd9Sstevel@tonic-gate } else { 599*7c478bd9Sstevel@tonic-gate int instance = ddi_get_instance(dip); 600*7c478bd9Sstevel@tonic-gate 601*7c478bd9Sstevel@tonic-gate return (ddi_get_soft_state(hubd_statep, instance)); 602*7c478bd9Sstevel@tonic-gate } 603*7c478bd9Sstevel@tonic-gate } 604*7c478bd9Sstevel@tonic-gate 605*7c478bd9Sstevel@tonic-gate 606*7c478bd9Sstevel@tonic-gate /* 607*7c478bd9Sstevel@tonic-gate * PM support functions: 608*7c478bd9Sstevel@tonic-gate */ 609*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 610*7c478bd9Sstevel@tonic-gate static void 611*7c478bd9Sstevel@tonic-gate hubd_pm_busy_component(hubd_t *hubd, dev_info_t *dip, int component) 612*7c478bd9Sstevel@tonic-gate { 613*7c478bd9Sstevel@tonic-gate if (hubd->h_hubpm != NULL) { 614*7c478bd9Sstevel@tonic-gate hubd->h_hubpm->hubp_busy_pm++; 615*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 616*7c478bd9Sstevel@tonic-gate if (pm_busy_component(dip, 0) != DDI_SUCCESS) { 617*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 618*7c478bd9Sstevel@tonic-gate hubd->h_hubpm->hubp_busy_pm--; 619*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 620*7c478bd9Sstevel@tonic-gate } 621*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 622*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 623*7c478bd9Sstevel@tonic-gate "hubd_pm_busy_component: %d", hubd->h_hubpm->hubp_busy_pm); 624*7c478bd9Sstevel@tonic-gate } 625*7c478bd9Sstevel@tonic-gate } 626*7c478bd9Sstevel@tonic-gate 627*7c478bd9Sstevel@tonic-gate 628*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 629*7c478bd9Sstevel@tonic-gate static void 630*7c478bd9Sstevel@tonic-gate hubd_pm_idle_component(hubd_t *hubd, dev_info_t *dip, int component) 631*7c478bd9Sstevel@tonic-gate { 632*7c478bd9Sstevel@tonic-gate if (hubd->h_hubpm != NULL) { 633*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 634*7c478bd9Sstevel@tonic-gate if (pm_idle_component(dip, 0) == DDI_SUCCESS) { 635*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 636*7c478bd9Sstevel@tonic-gate ASSERT(hubd->h_hubpm->hubp_busy_pm > 0); 637*7c478bd9Sstevel@tonic-gate hubd->h_hubpm->hubp_busy_pm--; 638*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 639*7c478bd9Sstevel@tonic-gate } 640*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 641*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 642*7c478bd9Sstevel@tonic-gate "hubd_pm_idle_component: %d", hubd->h_hubpm->hubp_busy_pm); 643*7c478bd9Sstevel@tonic-gate } 644*7c478bd9Sstevel@tonic-gate } 645*7c478bd9Sstevel@tonic-gate 646*7c478bd9Sstevel@tonic-gate 647*7c478bd9Sstevel@tonic-gate /* 648*7c478bd9Sstevel@tonic-gate * track power level changes for children of this instance 649*7c478bd9Sstevel@tonic-gate */ 650*7c478bd9Sstevel@tonic-gate static void 651*7c478bd9Sstevel@tonic-gate hubd_set_child_pwrlvl(hubd_t *hubd, usb_port_t port, uint8_t power) 652*7c478bd9Sstevel@tonic-gate { 653*7c478bd9Sstevel@tonic-gate int old_power, new_power, pwr; 654*7c478bd9Sstevel@tonic-gate usb_port_t portno; 655*7c478bd9Sstevel@tonic-gate hub_power_t *hubpm; 656*7c478bd9Sstevel@tonic-gate 657*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 658*7c478bd9Sstevel@tonic-gate "hubd_set_child_pwrlvl: port=%d power=%d", 659*7c478bd9Sstevel@tonic-gate port, power); 660*7c478bd9Sstevel@tonic-gate 661*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 662*7c478bd9Sstevel@tonic-gate hubpm = hubd->h_hubpm; 663*7c478bd9Sstevel@tonic-gate 664*7c478bd9Sstevel@tonic-gate old_power = 0; 665*7c478bd9Sstevel@tonic-gate for (portno = 1; portno <= hubd->h_hub_descr.bNbrPorts; portno++) { 666*7c478bd9Sstevel@tonic-gate old_power += hubpm->hubp_child_pwrstate[portno]; 667*7c478bd9Sstevel@tonic-gate } 668*7c478bd9Sstevel@tonic-gate 669*7c478bd9Sstevel@tonic-gate /* assign the port power */ 670*7c478bd9Sstevel@tonic-gate pwr = hubd->h_hubpm->hubp_child_pwrstate[port]; 671*7c478bd9Sstevel@tonic-gate hubd->h_hubpm->hubp_child_pwrstate[port] = power; 672*7c478bd9Sstevel@tonic-gate new_power = old_power - pwr + power; 673*7c478bd9Sstevel@tonic-gate 674*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 675*7c478bd9Sstevel@tonic-gate "hubd_set_child_pwrlvl: new_power=%d old_power=%d", 676*7c478bd9Sstevel@tonic-gate new_power, old_power); 677*7c478bd9Sstevel@tonic-gate 678*7c478bd9Sstevel@tonic-gate if ((new_power > 0) && (old_power == 0)) { 679*7c478bd9Sstevel@tonic-gate /* we have the first child coming out of low power */ 680*7c478bd9Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0); 681*7c478bd9Sstevel@tonic-gate } else if ((new_power == 0) && (old_power > 0)) { 682*7c478bd9Sstevel@tonic-gate /* we have the last child going to low power */ 683*7c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0); 684*7c478bd9Sstevel@tonic-gate } 685*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 686*7c478bd9Sstevel@tonic-gate } 687*7c478bd9Sstevel@tonic-gate 688*7c478bd9Sstevel@tonic-gate 689*7c478bd9Sstevel@tonic-gate /* 690*7c478bd9Sstevel@tonic-gate * given a child dip, locate its port number 691*7c478bd9Sstevel@tonic-gate */ 692*7c478bd9Sstevel@tonic-gate static usb_port_t 693*7c478bd9Sstevel@tonic-gate hubd_child_dip2port(hubd_t *hubd, dev_info_t *dip) 694*7c478bd9Sstevel@tonic-gate { 695*7c478bd9Sstevel@tonic-gate usb_port_t port; 696*7c478bd9Sstevel@tonic-gate 697*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 698*7c478bd9Sstevel@tonic-gate for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { 699*7c478bd9Sstevel@tonic-gate if (hubd->h_children_dips[port] == dip) { 700*7c478bd9Sstevel@tonic-gate 701*7c478bd9Sstevel@tonic-gate break; 702*7c478bd9Sstevel@tonic-gate } 703*7c478bd9Sstevel@tonic-gate } 704*7c478bd9Sstevel@tonic-gate ASSERT(port <= hubd->h_hub_descr.bNbrPorts); 705*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 706*7c478bd9Sstevel@tonic-gate 707*7c478bd9Sstevel@tonic-gate return (port); 708*7c478bd9Sstevel@tonic-gate } 709*7c478bd9Sstevel@tonic-gate 710*7c478bd9Sstevel@tonic-gate 711*7c478bd9Sstevel@tonic-gate /* 712*7c478bd9Sstevel@tonic-gate * if the hub can be put into low power mode, return success 713*7c478bd9Sstevel@tonic-gate * NOTE: suspend here means going to lower power, not CPR suspend. 714*7c478bd9Sstevel@tonic-gate */ 715*7c478bd9Sstevel@tonic-gate static int 716*7c478bd9Sstevel@tonic-gate hubd_can_suspend(hubd_t *hubd) 717*7c478bd9Sstevel@tonic-gate { 718*7c478bd9Sstevel@tonic-gate hub_power_t *hubpm; 719*7c478bd9Sstevel@tonic-gate int total_power = 0; 720*7c478bd9Sstevel@tonic-gate usb_port_t port; 721*7c478bd9Sstevel@tonic-gate 722*7c478bd9Sstevel@tonic-gate hubpm = hubd->h_hubpm; 723*7c478bd9Sstevel@tonic-gate 724*7c478bd9Sstevel@tonic-gate if (DEVI_IS_DETACHING(hubd->h_dip)) { 725*7c478bd9Sstevel@tonic-gate 726*7c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 727*7c478bd9Sstevel@tonic-gate } 728*7c478bd9Sstevel@tonic-gate 729*7c478bd9Sstevel@tonic-gate /* 730*7c478bd9Sstevel@tonic-gate * Don't go to lower power if haven't been at full power for enough 731*7c478bd9Sstevel@tonic-gate * time to let hotplug thread kickoff. 732*7c478bd9Sstevel@tonic-gate */ 733*7c478bd9Sstevel@tonic-gate if (ddi_get_time() < (hubpm->hubp_time_at_full_power + 734*7c478bd9Sstevel@tonic-gate hubpm->hubp_min_pm_threshold)) { 735*7c478bd9Sstevel@tonic-gate 736*7c478bd9Sstevel@tonic-gate return (USB_FAILURE); 737*7c478bd9Sstevel@tonic-gate } 738*7c478bd9Sstevel@tonic-gate 739*7c478bd9Sstevel@tonic-gate for (port = 1; (total_power == 0) && 740*7c478bd9Sstevel@tonic-gate (port <= hubd->h_hub_descr.bNbrPorts); port++) { 741*7c478bd9Sstevel@tonic-gate total_power += hubpm->hubp_child_pwrstate[port]; 742*7c478bd9Sstevel@tonic-gate } 743*7c478bd9Sstevel@tonic-gate 744*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 745*7c478bd9Sstevel@tonic-gate "hubd_can_suspend: %d", total_power); 746*7c478bd9Sstevel@tonic-gate 747*7c478bd9Sstevel@tonic-gate return (total_power ? USB_FAILURE : USB_SUCCESS); 748*7c478bd9Sstevel@tonic-gate } 749*7c478bd9Sstevel@tonic-gate 750*7c478bd9Sstevel@tonic-gate 751*7c478bd9Sstevel@tonic-gate /* 752*7c478bd9Sstevel@tonic-gate * resume port depending on current device state 753*7c478bd9Sstevel@tonic-gate */ 754*7c478bd9Sstevel@tonic-gate static int 755*7c478bd9Sstevel@tonic-gate hubd_resume_port(hubd_t *hubd, usb_port_t port) 756*7c478bd9Sstevel@tonic-gate { 757*7c478bd9Sstevel@tonic-gate int rval, retry; 758*7c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 759*7c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 760*7c478bd9Sstevel@tonic-gate uint16_t status; 761*7c478bd9Sstevel@tonic-gate uint16_t change; 762*7c478bd9Sstevel@tonic-gate int retval = USB_FAILURE; 763*7c478bd9Sstevel@tonic-gate 764*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 765*7c478bd9Sstevel@tonic-gate 766*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 767*7c478bd9Sstevel@tonic-gate "hubd_resume_port: port=%d state=0x%x (%s)", port, 768*7c478bd9Sstevel@tonic-gate hubd->h_dev_state, usb_str_dev_state(hubd->h_dev_state)); 769*7c478bd9Sstevel@tonic-gate 770*7c478bd9Sstevel@tonic-gate switch (hubd->h_dev_state) { 771*7c478bd9Sstevel@tonic-gate case USB_DEV_HUB_CHILD_PWRLVL: 772*7c478bd9Sstevel@tonic-gate /* 773*7c478bd9Sstevel@tonic-gate * This could be a bus ctl for a port other than the one 774*7c478bd9Sstevel@tonic-gate * that has a remote wakeup condition. So check. 775*7c478bd9Sstevel@tonic-gate */ 776*7c478bd9Sstevel@tonic-gate if ((hubd->h_port_state[port] & PORT_STATUS_PSS) == 0) { 777*7c478bd9Sstevel@tonic-gate /* the port isn't suspended, so don't resume */ 778*7c478bd9Sstevel@tonic-gate retval = USB_SUCCESS; 779*7c478bd9Sstevel@tonic-gate 780*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 781*7c478bd9Sstevel@tonic-gate "hubd_resume_port: port=%d not suspended", port); 782*7c478bd9Sstevel@tonic-gate 783*7c478bd9Sstevel@tonic-gate break; 784*7c478bd9Sstevel@tonic-gate } 785*7c478bd9Sstevel@tonic-gate /* 786*7c478bd9Sstevel@tonic-gate * Device has initiated a wakeup. 787*7c478bd9Sstevel@tonic-gate * Issue a ClearFeature(PortSuspend) 788*7c478bd9Sstevel@tonic-gate */ 789*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 790*7c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 791*7c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 792*7c478bd9Sstevel@tonic-gate HANDLE_PORT_FEATURE, 793*7c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 794*7c478bd9Sstevel@tonic-gate CFS_PORT_SUSPEND, 795*7c478bd9Sstevel@tonic-gate port, 796*7c478bd9Sstevel@tonic-gate 0, NULL, 0, 797*7c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 798*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 799*7c478bd9Sstevel@tonic-gate "ClearFeature(PortSuspend) fails " 800*7c478bd9Sstevel@tonic-gate "rval=%d cr=%d cb=0x%x", rval, 801*7c478bd9Sstevel@tonic-gate completion_reason, cb_flags); 802*7c478bd9Sstevel@tonic-gate } 803*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 804*7c478bd9Sstevel@tonic-gate 805*7c478bd9Sstevel@tonic-gate /* either way ack changes on the port */ 806*7c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 807*7c478bd9Sstevel@tonic-gate &status, &change, PORT_CHANGE_PSSC); 808*7c478bd9Sstevel@tonic-gate retval = USB_SUCCESS; 809*7c478bd9Sstevel@tonic-gate 810*7c478bd9Sstevel@tonic-gate break; 811*7c478bd9Sstevel@tonic-gate case USB_DEV_HUB_STATE_RECOVER: 812*7c478bd9Sstevel@tonic-gate /* 813*7c478bd9Sstevel@tonic-gate * When hubd's connect event callback posts a connect 814*7c478bd9Sstevel@tonic-gate * event to its child, it results in this busctl call 815*7c478bd9Sstevel@tonic-gate * which is valid 816*7c478bd9Sstevel@tonic-gate */ 817*7c478bd9Sstevel@tonic-gate /* FALLTHRU */ 818*7c478bd9Sstevel@tonic-gate case USB_DEV_ONLINE: 819*7c478bd9Sstevel@tonic-gate if ((hubd->h_port_state[port] & 820*7c478bd9Sstevel@tonic-gate (PORT_STATUS_PSS | PORT_STATUS_CCS)) == 0) { 821*7c478bd9Sstevel@tonic-gate /* 822*7c478bd9Sstevel@tonic-gate * the port isn't suspended, or connected 823*7c478bd9Sstevel@tonic-gate * so don't resume 824*7c478bd9Sstevel@tonic-gate */ 825*7c478bd9Sstevel@tonic-gate retval = USB_SUCCESS; 826*7c478bd9Sstevel@tonic-gate 827*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 828*7c478bd9Sstevel@tonic-gate "hubd_resume_port: port=%d not suspended", port); 829*7c478bd9Sstevel@tonic-gate 830*7c478bd9Sstevel@tonic-gate break; 831*7c478bd9Sstevel@tonic-gate } 832*7c478bd9Sstevel@tonic-gate /* 833*7c478bd9Sstevel@tonic-gate * prevent kicking off the hotplug thread 834*7c478bd9Sstevel@tonic-gate */ 835*7c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread++; 836*7c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd); 837*7c478bd9Sstevel@tonic-gate 838*7c478bd9Sstevel@tonic-gate /* Now ClearFeature(PortSuspend) */ 839*7c478bd9Sstevel@tonic-gate for (retry = 0; retry < HUBD_SUS_RES_RETRY; retry++) { 840*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 841*7c478bd9Sstevel@tonic-gate rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 842*7c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 843*7c478bd9Sstevel@tonic-gate HANDLE_PORT_FEATURE, 844*7c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 845*7c478bd9Sstevel@tonic-gate CFS_PORT_SUSPEND, 846*7c478bd9Sstevel@tonic-gate port, 847*7c478bd9Sstevel@tonic-gate 0, NULL, 0, 848*7c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0); 849*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 850*7c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 851*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, 852*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 853*7c478bd9Sstevel@tonic-gate "ClearFeature(PortSuspend) fails" 854*7c478bd9Sstevel@tonic-gate "rval=%d cr=%d cb=0x%x", rval, 855*7c478bd9Sstevel@tonic-gate completion_reason, cb_flags); 856*7c478bd9Sstevel@tonic-gate } else { 857*7c478bd9Sstevel@tonic-gate /* 858*7c478bd9Sstevel@tonic-gate * As per spec section 11.9 and 7.1.7.7 859*7c478bd9Sstevel@tonic-gate * hub need to provide at least 20ms of 860*7c478bd9Sstevel@tonic-gate * resume signalling, and s/w provide 10ms of 861*7c478bd9Sstevel@tonic-gate * recovery time before accessing the port. 862*7c478bd9Sstevel@tonic-gate */ 863*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 864*7c478bd9Sstevel@tonic-gate delay(drv_usectohz(40000)); 865*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 866*7c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 867*7c478bd9Sstevel@tonic-gate &status, &change, PORT_CHANGE_PSSC); 868*7c478bd9Sstevel@tonic-gate 869*7c478bd9Sstevel@tonic-gate if ((status & PORT_STATUS_PSS) == 0) { 870*7c478bd9Sstevel@tonic-gate /* the port did finally resume */ 871*7c478bd9Sstevel@tonic-gate retval = USB_SUCCESS; 872*7c478bd9Sstevel@tonic-gate 873*7c478bd9Sstevel@tonic-gate break; 874*7c478bd9Sstevel@tonic-gate } 875*7c478bd9Sstevel@tonic-gate } 876*7c478bd9Sstevel@tonic-gate } 877*7c478bd9Sstevel@tonic-gate 878*7c478bd9Sstevel@tonic-gate /* allow hotplug thread again */ 879*7c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread--; 880*7c478bd9Sstevel@tonic-gate hubd_start_polling(hubd, 0); 881*7c478bd9Sstevel@tonic-gate 882*7c478bd9Sstevel@tonic-gate break; 883*7c478bd9Sstevel@tonic-gate case USB_DEV_DISCONNECTED: 884*7c478bd9Sstevel@tonic-gate /* Ignore - NO Operation */ 885*7c478bd9Sstevel@tonic-gate retval = USB_SUCCESS; 886*7c478bd9Sstevel@tonic-gate 887*7c478bd9Sstevel@tonic-gate break; 888*7c478bd9Sstevel@tonic-gate case USB_DEV_SUSPENDED: 889*7c478bd9Sstevel@tonic-gate case USB_DEV_PWRED_DOWN: 890*7c478bd9Sstevel@tonic-gate default: 891*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 892*7c478bd9Sstevel@tonic-gate "Improper state for port Resume"); 893*7c478bd9Sstevel@tonic-gate 894*7c478bd9Sstevel@tonic-gate break; 895*7c478bd9Sstevel@tonic-gate } 896*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 897*7c478bd9Sstevel@tonic-gate 898*7c478bd9Sstevel@tonic-gate return (retval); 899*7c478bd9Sstevel@tonic-gate } 900*7c478bd9Sstevel@tonic-gate 901*7c478bd9Sstevel@tonic-gate 902*7c478bd9Sstevel@tonic-gate /* 903*7c478bd9Sstevel@tonic-gate * suspend port depending on device state 904*7c478bd9Sstevel@tonic-gate */ 905*7c478bd9Sstevel@tonic-gate static int 906*7c478bd9Sstevel@tonic-gate hubd_suspend_port(hubd_t *hubd, usb_port_t port) 907*7c478bd9Sstevel@tonic-gate { 908*7c478bd9Sstevel@tonic-gate int rval, retry; 909*7c478bd9Sstevel@tonic-gate int retval = USB_FAILURE; 910*7c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 911*7c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 912*7c478bd9Sstevel@tonic-gate uint16_t status; 913*7c478bd9Sstevel@tonic-gate uint16_t change; 914*7c478bd9Sstevel@tonic-gate 915*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 916*7c478bd9Sstevel@tonic-gate "hubd_suspend_port: port=%d", port); 917*7c478bd9Sstevel@tonic-gate 918*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 919*7c478bd9Sstevel@tonic-gate 920*7c478bd9Sstevel@tonic-gate switch (hubd->h_dev_state) { 921*7c478bd9Sstevel@tonic-gate case USB_DEV_HUB_STATE_RECOVER: 922*7c478bd9Sstevel@tonic-gate /* 923*7c478bd9Sstevel@tonic-gate * When hubd's connect event callback posts a connect 924*7c478bd9Sstevel@tonic-gate * event to its child, it results in this busctl call 925*7c478bd9Sstevel@tonic-gate * which is valid 926*7c478bd9Sstevel@tonic-gate */ 927*7c478bd9Sstevel@tonic-gate /* FALLTHRU */ 928*7c478bd9Sstevel@tonic-gate case USB_DEV_HUB_CHILD_PWRLVL: 929*7c478bd9Sstevel@tonic-gate /* 930*7c478bd9Sstevel@tonic-gate * When one child is resuming, the other could timeout 931*7c478bd9Sstevel@tonic-gate * and go to low power mode, which is valid 932*7c478bd9Sstevel@tonic-gate */ 933*7c478bd9Sstevel@tonic-gate /* FALLTHRU */ 934*7c478bd9Sstevel@tonic-gate case USB_DEV_ONLINE: 935*7c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread++; 936*7c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd); 937*7c478bd9Sstevel@tonic-gate 938*7c478bd9Sstevel@tonic-gate /* 939*7c478bd9Sstevel@tonic-gate * Some devices start an unprovoked resume. According to spec, 940*7c478bd9Sstevel@tonic-gate * normal resume time for port is 10ms. Wait for double that 941*7c478bd9Sstevel@tonic-gate * time, then check to be sure port is really suspended. 942*7c478bd9Sstevel@tonic-gate */ 943*7c478bd9Sstevel@tonic-gate for (retry = 0; retry < HUBD_SUS_RES_RETRY; retry++) { 944*7c478bd9Sstevel@tonic-gate /* Now SetFeature(PortSuspend) */ 945*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 946*7c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 947*7c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 948*7c478bd9Sstevel@tonic-gate HANDLE_PORT_FEATURE, 949*7c478bd9Sstevel@tonic-gate USB_REQ_SET_FEATURE, 950*7c478bd9Sstevel@tonic-gate CFS_PORT_SUSPEND, 951*7c478bd9Sstevel@tonic-gate port, 952*7c478bd9Sstevel@tonic-gate 0, NULL, 0, 953*7c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != 954*7c478bd9Sstevel@tonic-gate USB_SUCCESS) { 955*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, 956*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 957*7c478bd9Sstevel@tonic-gate "SetFeature(PortSuspend) fails" 958*7c478bd9Sstevel@tonic-gate "rval=%d cr=%d cb=0x%x", 959*7c478bd9Sstevel@tonic-gate rval, completion_reason, cb_flags); 960*7c478bd9Sstevel@tonic-gate } 961*7c478bd9Sstevel@tonic-gate 962*7c478bd9Sstevel@tonic-gate /* 963*7c478bd9Sstevel@tonic-gate * some devices start an unprovoked resume 964*7c478bd9Sstevel@tonic-gate * wait and check port status after some time 965*7c478bd9Sstevel@tonic-gate */ 966*7c478bd9Sstevel@tonic-gate delay(drv_usectohz(20000)); 967*7c478bd9Sstevel@tonic-gate 968*7c478bd9Sstevel@tonic-gate /* either ways ack changes on the port */ 969*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 970*7c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 971*7c478bd9Sstevel@tonic-gate &status, &change, PORT_CHANGE_PSSC); 972*7c478bd9Sstevel@tonic-gate if (status & PORT_STATUS_PSS) { 973*7c478bd9Sstevel@tonic-gate /* the port is indeed suspended */ 974*7c478bd9Sstevel@tonic-gate retval = USB_SUCCESS; 975*7c478bd9Sstevel@tonic-gate 976*7c478bd9Sstevel@tonic-gate break; 977*7c478bd9Sstevel@tonic-gate } 978*7c478bd9Sstevel@tonic-gate } 979*7c478bd9Sstevel@tonic-gate 980*7c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread--; 981*7c478bd9Sstevel@tonic-gate hubd_start_polling(hubd, 0); 982*7c478bd9Sstevel@tonic-gate 983*7c478bd9Sstevel@tonic-gate break; 984*7c478bd9Sstevel@tonic-gate 985*7c478bd9Sstevel@tonic-gate case USB_DEV_DISCONNECTED: 986*7c478bd9Sstevel@tonic-gate /* Ignore - No Operation */ 987*7c478bd9Sstevel@tonic-gate retval = USB_SUCCESS; 988*7c478bd9Sstevel@tonic-gate 989*7c478bd9Sstevel@tonic-gate break; 990*7c478bd9Sstevel@tonic-gate 991*7c478bd9Sstevel@tonic-gate case USB_DEV_SUSPENDED: 992*7c478bd9Sstevel@tonic-gate case USB_DEV_PWRED_DOWN: 993*7c478bd9Sstevel@tonic-gate default: 994*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 995*7c478bd9Sstevel@tonic-gate "Improper state for port Suspend"); 996*7c478bd9Sstevel@tonic-gate 997*7c478bd9Sstevel@tonic-gate break; 998*7c478bd9Sstevel@tonic-gate } 999*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1000*7c478bd9Sstevel@tonic-gate 1001*7c478bd9Sstevel@tonic-gate return (retval); 1002*7c478bd9Sstevel@tonic-gate } 1003*7c478bd9Sstevel@tonic-gate 1004*7c478bd9Sstevel@tonic-gate 1005*7c478bd9Sstevel@tonic-gate /* 1006*7c478bd9Sstevel@tonic-gate * child post attach/detach notifications 1007*7c478bd9Sstevel@tonic-gate */ 1008*7c478bd9Sstevel@tonic-gate static void 1009*7c478bd9Sstevel@tonic-gate hubd_post_attach(hubd_t *hubd, usb_port_t port, struct attachspec *as) 1010*7c478bd9Sstevel@tonic-gate { 1011*7c478bd9Sstevel@tonic-gate dev_info_t *dip; 1012*7c478bd9Sstevel@tonic-gate 1013*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 1014*7c478bd9Sstevel@tonic-gate "hubd_post_attach: port=%d result=%d", 1015*7c478bd9Sstevel@tonic-gate port, as->result); 1016*7c478bd9Sstevel@tonic-gate 1017*7c478bd9Sstevel@tonic-gate if (as->result == DDI_SUCCESS) { 1018*7c478bd9Sstevel@tonic-gate /* 1019*7c478bd9Sstevel@tonic-gate * Check if the child created wants to be power managed. 1020*7c478bd9Sstevel@tonic-gate * If yes, the childs power level gets automatically tracked 1021*7c478bd9Sstevel@tonic-gate * by DDI_CTLOPS_POWER busctl. 1022*7c478bd9Sstevel@tonic-gate * If no, we set power of the new child by default 1023*7c478bd9Sstevel@tonic-gate * to USB_DEV_OS_FULL_PWR. Because we should never suspend. 1024*7c478bd9Sstevel@tonic-gate */ 1025*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 1026*7c478bd9Sstevel@tonic-gate dip = hubd->h_children_dips[port]; 1027*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1028*7c478bd9Sstevel@tonic-gate if (DEVI(dip)->devi_pm_info == NULL) { 1029*7c478bd9Sstevel@tonic-gate hubd_set_child_pwrlvl(hubd, port, USB_DEV_OS_FULL_PWR); 1030*7c478bd9Sstevel@tonic-gate } 1031*7c478bd9Sstevel@tonic-gate } 1032*7c478bd9Sstevel@tonic-gate } 1033*7c478bd9Sstevel@tonic-gate 1034*7c478bd9Sstevel@tonic-gate 1035*7c478bd9Sstevel@tonic-gate static void 1036*7c478bd9Sstevel@tonic-gate hubd_post_detach(hubd_t *hubd, usb_port_t port, struct detachspec *ds) 1037*7c478bd9Sstevel@tonic-gate { 1038*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 1039*7c478bd9Sstevel@tonic-gate "hubd_post_detach: port=%d result=%d", port, ds->result); 1040*7c478bd9Sstevel@tonic-gate 1041*7c478bd9Sstevel@tonic-gate /* 1042*7c478bd9Sstevel@tonic-gate * if the device is successfully detached and is the 1043*7c478bd9Sstevel@tonic-gate * last device to detach, mark component as idle 1044*7c478bd9Sstevel@tonic-gate */ 1045*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 1046*7c478bd9Sstevel@tonic-gate if (ds->result == DDI_SUCCESS) { 1047*7c478bd9Sstevel@tonic-gate usba_device_t *usba_device = hubd->h_usba_devices[port]; 1048*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1049*7c478bd9Sstevel@tonic-gate 1050*7c478bd9Sstevel@tonic-gate /* 1051*7c478bd9Sstevel@tonic-gate * We set power of the detached child 1052*7c478bd9Sstevel@tonic-gate * to 0, so that we can suspend if all 1053*7c478bd9Sstevel@tonic-gate * our children are gone 1054*7c478bd9Sstevel@tonic-gate */ 1055*7c478bd9Sstevel@tonic-gate hubd_set_child_pwrlvl(hubd, port, USB_DEV_OS_PWR_OFF); 1056*7c478bd9Sstevel@tonic-gate 1057*7c478bd9Sstevel@tonic-gate /* check for leaks on detaching */ 1058*7c478bd9Sstevel@tonic-gate if ((usba_device) && (ds->cmd == DDI_DETACH)) { 1059*7c478bd9Sstevel@tonic-gate usba_check_for_leaks(usba_device); 1060*7c478bd9Sstevel@tonic-gate } 1061*7c478bd9Sstevel@tonic-gate } else { 1062*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1063*7c478bd9Sstevel@tonic-gate } 1064*7c478bd9Sstevel@tonic-gate } 1065*7c478bd9Sstevel@tonic-gate 1066*7c478bd9Sstevel@tonic-gate 1067*7c478bd9Sstevel@tonic-gate /* 1068*7c478bd9Sstevel@tonic-gate * hubd_post_power 1069*7c478bd9Sstevel@tonic-gate * After the child's power entry point has been called 1070*7c478bd9Sstevel@tonic-gate * we record its power level in our local struct. 1071*7c478bd9Sstevel@tonic-gate * If the device has powered off, we suspend port 1072*7c478bd9Sstevel@tonic-gate */ 1073*7c478bd9Sstevel@tonic-gate static int 1074*7c478bd9Sstevel@tonic-gate hubd_post_power(hubd_t *hubd, usb_port_t port, pm_bp_child_pwrchg_t *bpc, 1075*7c478bd9Sstevel@tonic-gate int result) 1076*7c478bd9Sstevel@tonic-gate { 1077*7c478bd9Sstevel@tonic-gate int retval = USB_SUCCESS; 1078*7c478bd9Sstevel@tonic-gate 1079*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 1080*7c478bd9Sstevel@tonic-gate "hubd_post_power: port=%d", port); 1081*7c478bd9Sstevel@tonic-gate 1082*7c478bd9Sstevel@tonic-gate if (result == DDI_SUCCESS) { 1083*7c478bd9Sstevel@tonic-gate 1084*7c478bd9Sstevel@tonic-gate /* record this power in our local struct */ 1085*7c478bd9Sstevel@tonic-gate hubd_set_child_pwrlvl(hubd, port, bpc->bpc_nlevel); 1086*7c478bd9Sstevel@tonic-gate 1087*7c478bd9Sstevel@tonic-gate if (bpc->bpc_nlevel == USB_DEV_OS_PWR_OFF) { 1088*7c478bd9Sstevel@tonic-gate 1089*7c478bd9Sstevel@tonic-gate /* now suspend the port */ 1090*7c478bd9Sstevel@tonic-gate retval = hubd_suspend_port(hubd, port); 1091*7c478bd9Sstevel@tonic-gate } else if (bpc->bpc_nlevel == USB_DEV_OS_FULL_PWR) { 1092*7c478bd9Sstevel@tonic-gate 1093*7c478bd9Sstevel@tonic-gate /* make sure the port is resumed */ 1094*7c478bd9Sstevel@tonic-gate retval = hubd_resume_port(hubd, port); 1095*7c478bd9Sstevel@tonic-gate } 1096*7c478bd9Sstevel@tonic-gate } else { 1097*7c478bd9Sstevel@tonic-gate 1098*7c478bd9Sstevel@tonic-gate /* record old power in our local struct */ 1099*7c478bd9Sstevel@tonic-gate hubd_set_child_pwrlvl(hubd, port, bpc->bpc_olevel); 1100*7c478bd9Sstevel@tonic-gate 1101*7c478bd9Sstevel@tonic-gate if (bpc->bpc_olevel == USB_DEV_OS_PWR_OFF) { 1102*7c478bd9Sstevel@tonic-gate 1103*7c478bd9Sstevel@tonic-gate /* 1104*7c478bd9Sstevel@tonic-gate * As this device failed to transition from 1105*7c478bd9Sstevel@tonic-gate * power off state, suspend the port again 1106*7c478bd9Sstevel@tonic-gate */ 1107*7c478bd9Sstevel@tonic-gate retval = hubd_suspend_port(hubd, port); 1108*7c478bd9Sstevel@tonic-gate } 1109*7c478bd9Sstevel@tonic-gate } 1110*7c478bd9Sstevel@tonic-gate 1111*7c478bd9Sstevel@tonic-gate return (retval); 1112*7c478bd9Sstevel@tonic-gate } 1113*7c478bd9Sstevel@tonic-gate 1114*7c478bd9Sstevel@tonic-gate 1115*7c478bd9Sstevel@tonic-gate /* 1116*7c478bd9Sstevel@tonic-gate * bus ctl notifications are handled here, the rest goes up to root hub/hcd 1117*7c478bd9Sstevel@tonic-gate */ 1118*7c478bd9Sstevel@tonic-gate static int 1119*7c478bd9Sstevel@tonic-gate usba_hubdi_bus_ctl(dev_info_t *dip, 1120*7c478bd9Sstevel@tonic-gate dev_info_t *rdip, 1121*7c478bd9Sstevel@tonic-gate ddi_ctl_enum_t op, 1122*7c478bd9Sstevel@tonic-gate void *arg, 1123*7c478bd9Sstevel@tonic-gate void *result) 1124*7c478bd9Sstevel@tonic-gate { 1125*7c478bd9Sstevel@tonic-gate usba_device_t *hub_usba_device = usba_get_usba_device(rdip); 1126*7c478bd9Sstevel@tonic-gate dev_info_t *root_hub_dip = hub_usba_device->usb_root_hub_dip; 1127*7c478bd9Sstevel@tonic-gate struct attachspec *as; 1128*7c478bd9Sstevel@tonic-gate struct detachspec *ds; 1129*7c478bd9Sstevel@tonic-gate hubd_t *hubd; 1130*7c478bd9Sstevel@tonic-gate usb_port_t port; 1131*7c478bd9Sstevel@tonic-gate int circ, rval; 1132*7c478bd9Sstevel@tonic-gate int retval = DDI_FAILURE; 1133*7c478bd9Sstevel@tonic-gate 1134*7c478bd9Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 1135*7c478bd9Sstevel@tonic-gate 1136*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 1137*7c478bd9Sstevel@tonic-gate 1138*7c478bd9Sstevel@tonic-gate /* flag that we are currently running bus_ctl */ 1139*7c478bd9Sstevel@tonic-gate hubd->h_bus_ctls++; 1140*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1141*7c478bd9Sstevel@tonic-gate 1142*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle, 1143*7c478bd9Sstevel@tonic-gate "usba_hubdi_bus_ctl:\n\t" 1144*7c478bd9Sstevel@tonic-gate "dip=0x%p, rdip=0x%p, op=0x%x, arg=0x%p", 1145*7c478bd9Sstevel@tonic-gate dip, rdip, op, arg); 1146*7c478bd9Sstevel@tonic-gate 1147*7c478bd9Sstevel@tonic-gate switch (op) { 1148*7c478bd9Sstevel@tonic-gate case DDI_CTLOPS_ATTACH: 1149*7c478bd9Sstevel@tonic-gate as = (struct attachspec *)arg; 1150*7c478bd9Sstevel@tonic-gate port = hubd_child_dip2port(hubd, rdip); 1151*7c478bd9Sstevel@tonic-gate 1152*7c478bd9Sstevel@tonic-gate /* there is nothing to do at resume time */ 1153*7c478bd9Sstevel@tonic-gate if (as->cmd == DDI_RESUME) { 1154*7c478bd9Sstevel@tonic-gate break; 1155*7c478bd9Sstevel@tonic-gate } 1156*7c478bd9Sstevel@tonic-gate 1157*7c478bd9Sstevel@tonic-gate /* serialize access */ 1158*7c478bd9Sstevel@tonic-gate ndi_devi_enter(hubd->h_dip, &circ); 1159*7c478bd9Sstevel@tonic-gate 1160*7c478bd9Sstevel@tonic-gate switch (as->when) { 1161*7c478bd9Sstevel@tonic-gate case DDI_PRE: 1162*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 1163*7c478bd9Sstevel@tonic-gate "DDI_PRE DDI_CTLOPS_ATTACH: dip=%p, port=%d", 1164*7c478bd9Sstevel@tonic-gate rdip, port); 1165*7c478bd9Sstevel@tonic-gate 1166*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 1167*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= HUBD_CHILD_ATTACHING; 1168*7c478bd9Sstevel@tonic-gate 1169*7c478bd9Sstevel@tonic-gate /* Go busy here. Matching idle is DDI_POST case. */ 1170*7c478bd9Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, dip, 0); 1171*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1172*7c478bd9Sstevel@tonic-gate 1173*7c478bd9Sstevel@tonic-gate /* 1174*7c478bd9Sstevel@tonic-gate * if we suspended the port previously 1175*7c478bd9Sstevel@tonic-gate * because child went to low power state, and 1176*7c478bd9Sstevel@tonic-gate * someone unloaded the driver, the port would 1177*7c478bd9Sstevel@tonic-gate * still be suspended and needs to be resumed 1178*7c478bd9Sstevel@tonic-gate */ 1179*7c478bd9Sstevel@tonic-gate rval = hubd_resume_port(hubd, port); 1180*7c478bd9Sstevel@tonic-gate if (rval == USB_SUCCESS) { 1181*7c478bd9Sstevel@tonic-gate retval = DDI_SUCCESS; 1182*7c478bd9Sstevel@tonic-gate } 1183*7c478bd9Sstevel@tonic-gate 1184*7c478bd9Sstevel@tonic-gate break; 1185*7c478bd9Sstevel@tonic-gate case DDI_POST: 1186*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 1187*7c478bd9Sstevel@tonic-gate "DDI_POST DDI_CTLOPS_ATTACH: dip=%p, port=%d", 1188*7c478bd9Sstevel@tonic-gate rdip, port); 1189*7c478bd9Sstevel@tonic-gate 1190*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 1191*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~HUBD_CHILD_ATTACHING; 1192*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1193*7c478bd9Sstevel@tonic-gate 1194*7c478bd9Sstevel@tonic-gate hubd_post_attach(hubd, port, (struct attachspec *)arg); 1195*7c478bd9Sstevel@tonic-gate retval = DDI_SUCCESS; 1196*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 1197*7c478bd9Sstevel@tonic-gate 1198*7c478bd9Sstevel@tonic-gate /* Matching idle call for DDI_PRE busy call. */ 1199*7c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, dip, 0); 1200*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1201*7c478bd9Sstevel@tonic-gate } 1202*7c478bd9Sstevel@tonic-gate ndi_devi_exit(hubd->h_dip, circ); 1203*7c478bd9Sstevel@tonic-gate 1204*7c478bd9Sstevel@tonic-gate break; 1205*7c478bd9Sstevel@tonic-gate case DDI_CTLOPS_DETACH: 1206*7c478bd9Sstevel@tonic-gate ds = (struct detachspec *)arg; 1207*7c478bd9Sstevel@tonic-gate port = hubd_child_dip2port(hubd, rdip); 1208*7c478bd9Sstevel@tonic-gate 1209*7c478bd9Sstevel@tonic-gate /* there is nothing to do at suspend time */ 1210*7c478bd9Sstevel@tonic-gate if (ds->cmd == DDI_SUSPEND) { 1211*7c478bd9Sstevel@tonic-gate break; 1212*7c478bd9Sstevel@tonic-gate } 1213*7c478bd9Sstevel@tonic-gate 1214*7c478bd9Sstevel@tonic-gate /* serialize access */ 1215*7c478bd9Sstevel@tonic-gate ndi_devi_enter(hubd->h_dip, &circ); 1216*7c478bd9Sstevel@tonic-gate 1217*7c478bd9Sstevel@tonic-gate switch (ds->when) { 1218*7c478bd9Sstevel@tonic-gate case DDI_PRE: 1219*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 1220*7c478bd9Sstevel@tonic-gate "DDI_PRE DDI_CTLOPS_DETACH: dip=%p port=%d", 1221*7c478bd9Sstevel@tonic-gate rdip, port); 1222*7c478bd9Sstevel@tonic-gate 1223*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 1224*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= HUBD_CHILD_DETACHING; 1225*7c478bd9Sstevel@tonic-gate 1226*7c478bd9Sstevel@tonic-gate /* Go busy here. Matching idle is DDI_POST case. */ 1227*7c478bd9Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, dip, 0); 1228*7c478bd9Sstevel@tonic-gate 1229*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1230*7c478bd9Sstevel@tonic-gate retval = DDI_SUCCESS; 1231*7c478bd9Sstevel@tonic-gate 1232*7c478bd9Sstevel@tonic-gate break; 1233*7c478bd9Sstevel@tonic-gate case DDI_POST: 1234*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 1235*7c478bd9Sstevel@tonic-gate "DDI_POST DDI_CTLOPS_DETACH: dip=%p port=%d", 1236*7c478bd9Sstevel@tonic-gate rdip, port); 1237*7c478bd9Sstevel@tonic-gate 1238*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 1239*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~HUBD_CHILD_DETACHING; 1240*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1241*7c478bd9Sstevel@tonic-gate 1242*7c478bd9Sstevel@tonic-gate /* Matching idle call for DDI_PRE busy call. */ 1243*7c478bd9Sstevel@tonic-gate hubd_post_detach(hubd, port, (struct detachspec *)arg); 1244*7c478bd9Sstevel@tonic-gate retval = DDI_SUCCESS; 1245*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 1246*7c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, dip, 0); 1247*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1248*7c478bd9Sstevel@tonic-gate 1249*7c478bd9Sstevel@tonic-gate break; 1250*7c478bd9Sstevel@tonic-gate } 1251*7c478bd9Sstevel@tonic-gate ndi_devi_exit(hubd->h_dip, circ); 1252*7c478bd9Sstevel@tonic-gate 1253*7c478bd9Sstevel@tonic-gate break; 1254*7c478bd9Sstevel@tonic-gate default: 1255*7c478bd9Sstevel@tonic-gate retval = usba_bus_ctl(root_hub_dip, rdip, op, arg, result); 1256*7c478bd9Sstevel@tonic-gate } 1257*7c478bd9Sstevel@tonic-gate 1258*7c478bd9Sstevel@tonic-gate /* decrement bus_ctls count */ 1259*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 1260*7c478bd9Sstevel@tonic-gate hubd->h_bus_ctls--; 1261*7c478bd9Sstevel@tonic-gate ASSERT(hubd->h_bus_ctls >= 0); 1262*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1263*7c478bd9Sstevel@tonic-gate 1264*7c478bd9Sstevel@tonic-gate return (retval); 1265*7c478bd9Sstevel@tonic-gate } 1266*7c478bd9Sstevel@tonic-gate 1267*7c478bd9Sstevel@tonic-gate 1268*7c478bd9Sstevel@tonic-gate /* 1269*7c478bd9Sstevel@tonic-gate * bus enumeration entry points 1270*7c478bd9Sstevel@tonic-gate */ 1271*7c478bd9Sstevel@tonic-gate static int 1272*7c478bd9Sstevel@tonic-gate hubd_bus_config(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op, 1273*7c478bd9Sstevel@tonic-gate void *arg, dev_info_t **child) 1274*7c478bd9Sstevel@tonic-gate { 1275*7c478bd9Sstevel@tonic-gate extern int modrootloaded; 1276*7c478bd9Sstevel@tonic-gate 1277*7c478bd9Sstevel@tonic-gate hubd_t *hubd = hubd_get_soft_state(dip); 1278*7c478bd9Sstevel@tonic-gate int rval, circ; 1279*7c478bd9Sstevel@tonic-gate 1280*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 1281*7c478bd9Sstevel@tonic-gate "hubd_bus_config: op=%d", op); 1282*7c478bd9Sstevel@tonic-gate 1283*7c478bd9Sstevel@tonic-gate if (hubdi_bus_config_debug) { 1284*7c478bd9Sstevel@tonic-gate flag |= NDI_DEVI_DEBUG; 1285*7c478bd9Sstevel@tonic-gate } 1286*7c478bd9Sstevel@tonic-gate 1287*7c478bd9Sstevel@tonic-gate /* 1288*7c478bd9Sstevel@tonic-gate * there must be a smarter way to do this but for 1289*7c478bd9Sstevel@tonic-gate * now, a hack for booting USB storage. 1290*7c478bd9Sstevel@tonic-gate */ 1291*7c478bd9Sstevel@tonic-gate if (!modrootloaded) { 1292*7c478bd9Sstevel@tonic-gate delay(drv_usectohz(1000000)); 1293*7c478bd9Sstevel@tonic-gate } 1294*7c478bd9Sstevel@tonic-gate ndi_devi_enter(hubd->h_dip, &circ); 1295*7c478bd9Sstevel@tonic-gate rval = ndi_busop_bus_config(dip, flag, op, arg, child, 0); 1296*7c478bd9Sstevel@tonic-gate ndi_devi_exit(hubd->h_dip, circ); 1297*7c478bd9Sstevel@tonic-gate 1298*7c478bd9Sstevel@tonic-gate return (rval); 1299*7c478bd9Sstevel@tonic-gate } 1300*7c478bd9Sstevel@tonic-gate 1301*7c478bd9Sstevel@tonic-gate 1302*7c478bd9Sstevel@tonic-gate static int 1303*7c478bd9Sstevel@tonic-gate hubd_bus_unconfig(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op, 1304*7c478bd9Sstevel@tonic-gate void *arg) 1305*7c478bd9Sstevel@tonic-gate { 1306*7c478bd9Sstevel@tonic-gate hubd_t *hubd = hubd_get_soft_state(dip); 1307*7c478bd9Sstevel@tonic-gate dev_info_t *cdip; 1308*7c478bd9Sstevel@tonic-gate usb_port_t port; 1309*7c478bd9Sstevel@tonic-gate int circ; 1310*7c478bd9Sstevel@tonic-gate int rval; 1311*7c478bd9Sstevel@tonic-gate 1312*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 1313*7c478bd9Sstevel@tonic-gate "hubd_bus_unconfig: op=%d", op); 1314*7c478bd9Sstevel@tonic-gate 1315*7c478bd9Sstevel@tonic-gate if (hubdi_bus_config_debug) { 1316*7c478bd9Sstevel@tonic-gate flag |= NDI_DEVI_DEBUG; 1317*7c478bd9Sstevel@tonic-gate } 1318*7c478bd9Sstevel@tonic-gate 1319*7c478bd9Sstevel@tonic-gate if ((op == BUS_UNCONFIG_ALL) && (flag & NDI_AUTODETACH) == 0) { 1320*7c478bd9Sstevel@tonic-gate flag |= NDI_DEVI_REMOVE; 1321*7c478bd9Sstevel@tonic-gate } 1322*7c478bd9Sstevel@tonic-gate 1323*7c478bd9Sstevel@tonic-gate /* serialize access */ 1324*7c478bd9Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 1325*7c478bd9Sstevel@tonic-gate 1326*7c478bd9Sstevel@tonic-gate rval = ndi_busop_bus_unconfig(dip, flag, op, arg); 1327*7c478bd9Sstevel@tonic-gate 1328*7c478bd9Sstevel@tonic-gate /* logically zap children's list */ 1329*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 1330*7c478bd9Sstevel@tonic-gate for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { 1331*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= HUBD_CHILD_ZAP; 1332*7c478bd9Sstevel@tonic-gate } 1333*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1334*7c478bd9Sstevel@tonic-gate 1335*7c478bd9Sstevel@tonic-gate /* fill in what's left */ 1336*7c478bd9Sstevel@tonic-gate for (cdip = ddi_get_child(dip); cdip; 1337*7c478bd9Sstevel@tonic-gate cdip = ddi_get_next_sibling(cdip)) { 1338*7c478bd9Sstevel@tonic-gate usba_device_t *usba_device = usba_get_usba_device(cdip); 1339*7c478bd9Sstevel@tonic-gate 1340*7c478bd9Sstevel@tonic-gate if (usba_device == NULL) { 1341*7c478bd9Sstevel@tonic-gate 1342*7c478bd9Sstevel@tonic-gate continue; 1343*7c478bd9Sstevel@tonic-gate } 1344*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 1345*7c478bd9Sstevel@tonic-gate port = usba_device->usb_port; 1346*7c478bd9Sstevel@tonic-gate hubd->h_children_dips[port] = cdip; 1347*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~HUBD_CHILD_ZAP; 1348*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1349*7c478bd9Sstevel@tonic-gate } 1350*7c478bd9Sstevel@tonic-gate 1351*7c478bd9Sstevel@tonic-gate /* physically zap the children we didn't find */ 1352*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 1353*7c478bd9Sstevel@tonic-gate for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { 1354*7c478bd9Sstevel@tonic-gate if (hubd->h_port_state[port] & HUBD_CHILD_ZAP) { 1355*7c478bd9Sstevel@tonic-gate /* zap the dip and usba_device structure as well */ 1356*7c478bd9Sstevel@tonic-gate hubd_free_usba_device(hubd, hubd->h_usba_devices[port]); 1357*7c478bd9Sstevel@tonic-gate hubd->h_children_dips[port] = NULL; 1358*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~HUBD_CHILD_ZAP; 1359*7c478bd9Sstevel@tonic-gate } 1360*7c478bd9Sstevel@tonic-gate } 1361*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1362*7c478bd9Sstevel@tonic-gate 1363*7c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 1364*7c478bd9Sstevel@tonic-gate 1365*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 1366*7c478bd9Sstevel@tonic-gate "hubd_bus_unconfig: rval=%d", rval); 1367*7c478bd9Sstevel@tonic-gate 1368*7c478bd9Sstevel@tonic-gate return (rval); 1369*7c478bd9Sstevel@tonic-gate } 1370*7c478bd9Sstevel@tonic-gate 1371*7c478bd9Sstevel@tonic-gate 1372*7c478bd9Sstevel@tonic-gate /* bus_power entry point */ 1373*7c478bd9Sstevel@tonic-gate static int 1374*7c478bd9Sstevel@tonic-gate hubd_bus_power(dev_info_t *dip, void *impl_arg, pm_bus_power_op_t op, 1375*7c478bd9Sstevel@tonic-gate void *arg, void *result) 1376*7c478bd9Sstevel@tonic-gate { 1377*7c478bd9Sstevel@tonic-gate hubd_t *hubd; 1378*7c478bd9Sstevel@tonic-gate int rval, pwrup_res; 1379*7c478bd9Sstevel@tonic-gate usb_port_t port; 1380*7c478bd9Sstevel@tonic-gate int retval = DDI_FAILURE; 1381*7c478bd9Sstevel@tonic-gate pm_bp_child_pwrchg_t *bpc; 1382*7c478bd9Sstevel@tonic-gate pm_bp_nexus_pwrup_t bpn; 1383*7c478bd9Sstevel@tonic-gate 1384*7c478bd9Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 1385*7c478bd9Sstevel@tonic-gate 1386*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubd->h_log_handle, 1387*7c478bd9Sstevel@tonic-gate "hubd_bus_power: dip=%p, impl_arg=%p, power_op=%d, arg=%p, " 1388*7c478bd9Sstevel@tonic-gate "result=%d\n", dip, impl_arg, op, arg, *(int *)result); 1389*7c478bd9Sstevel@tonic-gate 1390*7c478bd9Sstevel@tonic-gate bpc = (pm_bp_child_pwrchg_t *)arg; 1391*7c478bd9Sstevel@tonic-gate 1392*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 1393*7c478bd9Sstevel@tonic-gate hubd->h_bus_pwr++; 1394*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1395*7c478bd9Sstevel@tonic-gate 1396*7c478bd9Sstevel@tonic-gate switch (op) { 1397*7c478bd9Sstevel@tonic-gate case BUS_POWER_PRE_NOTIFICATION: 1398*7c478bd9Sstevel@tonic-gate port = hubd_child_dip2port(hubd, bpc->bpc_dip); 1399*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle, 1400*7c478bd9Sstevel@tonic-gate "hubd_bus_power: BUS_POWER_PRE_NOTIFICATION, port=%d", 1401*7c478bd9Sstevel@tonic-gate port); 1402*7c478bd9Sstevel@tonic-gate 1403*7c478bd9Sstevel@tonic-gate /* go to full power if we are powered down */ 1404*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 1405*7c478bd9Sstevel@tonic-gate 1406*7c478bd9Sstevel@tonic-gate /* 1407*7c478bd9Sstevel@tonic-gate * If this case completes normally, idle will be in 1408*7c478bd9Sstevel@tonic-gate * hubd_bus_power / BUS_POWER_POST_NOTIFICATION 1409*7c478bd9Sstevel@tonic-gate */ 1410*7c478bd9Sstevel@tonic-gate hubd_pm_busy_component(hubd, dip, 0); 1411*7c478bd9Sstevel@tonic-gate 1412*7c478bd9Sstevel@tonic-gate /* 1413*7c478bd9Sstevel@tonic-gate * raise power only if we have created the components 1414*7c478bd9Sstevel@tonic-gate * and are currently in low power 1415*7c478bd9Sstevel@tonic-gate */ 1416*7c478bd9Sstevel@tonic-gate if ((hubd->h_dev_state == USB_DEV_PWRED_DOWN) && 1417*7c478bd9Sstevel@tonic-gate hubd->h_hubpm->hubp_wakeup_enabled) { 1418*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1419*7c478bd9Sstevel@tonic-gate 1420*7c478bd9Sstevel@tonic-gate bpn.bpn_comp = 0; 1421*7c478bd9Sstevel@tonic-gate bpn.bpn_dip = dip; 1422*7c478bd9Sstevel@tonic-gate bpn.bpn_level = USB_DEV_OS_FULL_PWR; 1423*7c478bd9Sstevel@tonic-gate bpn.bpn_private = bpc->bpc_private; 1424*7c478bd9Sstevel@tonic-gate 1425*7c478bd9Sstevel@tonic-gate rval = pm_busop_bus_power(dip, impl_arg, 1426*7c478bd9Sstevel@tonic-gate BUS_POWER_NEXUS_PWRUP, (void *)&bpn, 1427*7c478bd9Sstevel@tonic-gate (void *)&pwrup_res); 1428*7c478bd9Sstevel@tonic-gate 1429*7c478bd9Sstevel@tonic-gate if (rval != DDI_SUCCESS || pwrup_res != DDI_SUCCESS) { 1430*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 1431*7c478bd9Sstevel@tonic-gate hubd_pm_idle_component(hubd, dip, 0); 1432*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1433*7c478bd9Sstevel@tonic-gate 1434*7c478bd9Sstevel@tonic-gate break; 1435*7c478bd9Sstevel@tonic-gate } 1436*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 1437*7c478bd9Sstevel@tonic-gate } 1438*7c478bd9Sstevel@tonic-gate 1439*7c478bd9Sstevel@tonic-gate /* indicate that child is changing power level */ 1440*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= HUBD_CHILD_PWRLVL_CHNG; 1441*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1442*7c478bd9Sstevel@tonic-gate 1443*7c478bd9Sstevel@tonic-gate if ((bpc->bpc_olevel == 0) && 1444*7c478bd9Sstevel@tonic-gate (bpc->bpc_nlevel > bpc->bpc_olevel)) { 1445*7c478bd9Sstevel@tonic-gate /* 1446*7c478bd9Sstevel@tonic-gate * this child is transitioning from power off 1447*7c478bd9Sstevel@tonic-gate * to power on state - resume port 1448*7c478bd9Sstevel@tonic-gate */ 1449*7c478bd9Sstevel@tonic-gate rval = hubd_resume_port(hubd, port); 1450*7c478bd9Sstevel@tonic-gate if (rval == USB_SUCCESS) { 1451*7c478bd9Sstevel@tonic-gate retval = DDI_SUCCESS; 1452*7c478bd9Sstevel@tonic-gate } else { 1453*7c478bd9Sstevel@tonic-gate /* reset this flag on failure */ 1454*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 1455*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= 1456*7c478bd9Sstevel@tonic-gate ~HUBD_CHILD_PWRLVL_CHNG; 1457*7c478bd9Sstevel@tonic-gate hubd_pm_idle_component(hubd, dip, 0); 1458*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1459*7c478bd9Sstevel@tonic-gate } 1460*7c478bd9Sstevel@tonic-gate } else { 1461*7c478bd9Sstevel@tonic-gate retval = DDI_SUCCESS; 1462*7c478bd9Sstevel@tonic-gate } 1463*7c478bd9Sstevel@tonic-gate 1464*7c478bd9Sstevel@tonic-gate break; 1465*7c478bd9Sstevel@tonic-gate case BUS_POWER_POST_NOTIFICATION: 1466*7c478bd9Sstevel@tonic-gate port = hubd_child_dip2port(hubd, bpc->bpc_dip); 1467*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle, 1468*7c478bd9Sstevel@tonic-gate "hubd_bus_power: BUS_POWER_POST_NOTIFICATION, port=%d", 1469*7c478bd9Sstevel@tonic-gate port); 1470*7c478bd9Sstevel@tonic-gate 1471*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 1472*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~HUBD_CHILD_PWRLVL_CHNG; 1473*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1474*7c478bd9Sstevel@tonic-gate 1475*7c478bd9Sstevel@tonic-gate /* record child's pwr and suspend port if required */ 1476*7c478bd9Sstevel@tonic-gate rval = hubd_post_power(hubd, port, bpc, *(int *)result); 1477*7c478bd9Sstevel@tonic-gate if (rval == USB_SUCCESS) { 1478*7c478bd9Sstevel@tonic-gate 1479*7c478bd9Sstevel@tonic-gate retval = DDI_SUCCESS; 1480*7c478bd9Sstevel@tonic-gate } 1481*7c478bd9Sstevel@tonic-gate 1482*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 1483*7c478bd9Sstevel@tonic-gate 1484*7c478bd9Sstevel@tonic-gate /* 1485*7c478bd9Sstevel@tonic-gate * Matching idle for the busy in 1486*7c478bd9Sstevel@tonic-gate * hubd_bus_power / BUS_POWER_PRE_NOTIFICATION 1487*7c478bd9Sstevel@tonic-gate */ 1488*7c478bd9Sstevel@tonic-gate hubd_pm_idle_component(hubd, dip, 0); 1489*7c478bd9Sstevel@tonic-gate 1490*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1491*7c478bd9Sstevel@tonic-gate 1492*7c478bd9Sstevel@tonic-gate break; 1493*7c478bd9Sstevel@tonic-gate default: 1494*7c478bd9Sstevel@tonic-gate retval = pm_busop_bus_power(dip, impl_arg, op, arg, result); 1495*7c478bd9Sstevel@tonic-gate 1496*7c478bd9Sstevel@tonic-gate break; 1497*7c478bd9Sstevel@tonic-gate } 1498*7c478bd9Sstevel@tonic-gate 1499*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 1500*7c478bd9Sstevel@tonic-gate hubd->h_bus_pwr--; 1501*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1502*7c478bd9Sstevel@tonic-gate 1503*7c478bd9Sstevel@tonic-gate return (retval); 1504*7c478bd9Sstevel@tonic-gate } 1505*7c478bd9Sstevel@tonic-gate 1506*7c478bd9Sstevel@tonic-gate 1507*7c478bd9Sstevel@tonic-gate /* 1508*7c478bd9Sstevel@tonic-gate * functions to handle power transition for OS levels 0 -> 3 1509*7c478bd9Sstevel@tonic-gate */ 1510*7c478bd9Sstevel@tonic-gate static int 1511*7c478bd9Sstevel@tonic-gate hubd_pwrlvl0(hubd_t *hubd) 1512*7c478bd9Sstevel@tonic-gate { 1513*7c478bd9Sstevel@tonic-gate hub_power_t *hubpm; 1514*7c478bd9Sstevel@tonic-gate 1515*7c478bd9Sstevel@tonic-gate /* We can't power down if hotplug thread is running */ 1516*7c478bd9Sstevel@tonic-gate if (hubd->h_hotplug_thread || hubd->h_hubpm->hubp_busy_pm || 1517*7c478bd9Sstevel@tonic-gate (hubd_can_suspend(hubd) == USB_FAILURE)) { 1518*7c478bd9Sstevel@tonic-gate 1519*7c478bd9Sstevel@tonic-gate return (USB_FAILURE); 1520*7c478bd9Sstevel@tonic-gate } 1521*7c478bd9Sstevel@tonic-gate 1522*7c478bd9Sstevel@tonic-gate switch (hubd->h_dev_state) { 1523*7c478bd9Sstevel@tonic-gate case USB_DEV_ONLINE: 1524*7c478bd9Sstevel@tonic-gate hubpm = hubd->h_hubpm; 1525*7c478bd9Sstevel@tonic-gate 1526*7c478bd9Sstevel@tonic-gate /* 1527*7c478bd9Sstevel@tonic-gate * To avoid race with bus_power pre_notify on check over 1528*7c478bd9Sstevel@tonic-gate * dev_state, we need to correctly set the dev state 1529*7c478bd9Sstevel@tonic-gate * before the mutex is dropped in stop polling. 1530*7c478bd9Sstevel@tonic-gate */ 1531*7c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_PWRED_DOWN; 1532*7c478bd9Sstevel@tonic-gate hubpm->hubp_current_power = USB_DEV_OS_PWR_OFF; 1533*7c478bd9Sstevel@tonic-gate 1534*7c478bd9Sstevel@tonic-gate /* 1535*7c478bd9Sstevel@tonic-gate * if we are the root hub, do not stop polling 1536*7c478bd9Sstevel@tonic-gate * otherwise, we will never see a resume 1537*7c478bd9Sstevel@tonic-gate */ 1538*7c478bd9Sstevel@tonic-gate if (usba_is_root_hub(hubd->h_dip)) { 1539*7c478bd9Sstevel@tonic-gate /* place holder to implement Global Suspend */ 1540*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 1541*7c478bd9Sstevel@tonic-gate "Global Suspend: Not Yet Implemented"); 1542*7c478bd9Sstevel@tonic-gate } else { 1543*7c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd); 1544*7c478bd9Sstevel@tonic-gate } 1545*7c478bd9Sstevel@tonic-gate 1546*7c478bd9Sstevel@tonic-gate /* Issue USB D3 command to the device here */ 1547*7c478bd9Sstevel@tonic-gate (void) usb_set_device_pwrlvl3(hubd->h_dip); 1548*7c478bd9Sstevel@tonic-gate 1549*7c478bd9Sstevel@tonic-gate break; 1550*7c478bd9Sstevel@tonic-gate case USB_DEV_DISCONNECTED: 1551*7c478bd9Sstevel@tonic-gate case USB_DEV_SUSPENDED: 1552*7c478bd9Sstevel@tonic-gate case USB_DEV_PWRED_DOWN: 1553*7c478bd9Sstevel@tonic-gate default: 1554*7c478bd9Sstevel@tonic-gate 1555*7c478bd9Sstevel@tonic-gate break; 1556*7c478bd9Sstevel@tonic-gate } 1557*7c478bd9Sstevel@tonic-gate 1558*7c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 1559*7c478bd9Sstevel@tonic-gate } 1560*7c478bd9Sstevel@tonic-gate 1561*7c478bd9Sstevel@tonic-gate 1562*7c478bd9Sstevel@tonic-gate /* ARGSUSED */ 1563*7c478bd9Sstevel@tonic-gate static int 1564*7c478bd9Sstevel@tonic-gate hubd_pwrlvl1(hubd_t *hubd) 1565*7c478bd9Sstevel@tonic-gate { 1566*7c478bd9Sstevel@tonic-gate /* Issue USB D2 command to the device here */ 1567*7c478bd9Sstevel@tonic-gate (void) usb_set_device_pwrlvl2(hubd->h_dip); 1568*7c478bd9Sstevel@tonic-gate 1569*7c478bd9Sstevel@tonic-gate return (USB_FAILURE); 1570*7c478bd9Sstevel@tonic-gate } 1571*7c478bd9Sstevel@tonic-gate 1572*7c478bd9Sstevel@tonic-gate 1573*7c478bd9Sstevel@tonic-gate /* ARGSUSED */ 1574*7c478bd9Sstevel@tonic-gate static int 1575*7c478bd9Sstevel@tonic-gate hubd_pwrlvl2(hubd_t *hubd) 1576*7c478bd9Sstevel@tonic-gate { 1577*7c478bd9Sstevel@tonic-gate /* Issue USB D1 command to the device here */ 1578*7c478bd9Sstevel@tonic-gate (void) usb_set_device_pwrlvl1(hubd->h_dip); 1579*7c478bd9Sstevel@tonic-gate 1580*7c478bd9Sstevel@tonic-gate return (USB_FAILURE); 1581*7c478bd9Sstevel@tonic-gate } 1582*7c478bd9Sstevel@tonic-gate 1583*7c478bd9Sstevel@tonic-gate 1584*7c478bd9Sstevel@tonic-gate static int 1585*7c478bd9Sstevel@tonic-gate hubd_pwrlvl3(hubd_t *hubd) 1586*7c478bd9Sstevel@tonic-gate { 1587*7c478bd9Sstevel@tonic-gate hub_power_t *hubpm; 1588*7c478bd9Sstevel@tonic-gate int rval; 1589*7c478bd9Sstevel@tonic-gate 1590*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, "hubd_pwrlvl3"); 1591*7c478bd9Sstevel@tonic-gate 1592*7c478bd9Sstevel@tonic-gate hubpm = hubd->h_hubpm; 1593*7c478bd9Sstevel@tonic-gate switch (hubd->h_dev_state) { 1594*7c478bd9Sstevel@tonic-gate case USB_DEV_PWRED_DOWN: 1595*7c478bd9Sstevel@tonic-gate ASSERT(hubpm->hubp_current_power == USB_DEV_OS_PWR_OFF); 1596*7c478bd9Sstevel@tonic-gate if (usba_is_root_hub(hubd->h_dip)) { 1597*7c478bd9Sstevel@tonic-gate /* implement global resume here */ 1598*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, 1599*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 1600*7c478bd9Sstevel@tonic-gate "Global Resume: Not Yet Implemented"); 1601*7c478bd9Sstevel@tonic-gate } 1602*7c478bd9Sstevel@tonic-gate /* Issue USB D0 command to the device here */ 1603*7c478bd9Sstevel@tonic-gate rval = usb_set_device_pwrlvl0(hubd->h_dip); 1604*7c478bd9Sstevel@tonic-gate ASSERT(rval == USB_SUCCESS); 1605*7c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_ONLINE; 1606*7c478bd9Sstevel@tonic-gate hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR; 1607*7c478bd9Sstevel@tonic-gate hubpm->hubp_time_at_full_power = ddi_get_time(); 1608*7c478bd9Sstevel@tonic-gate hubd_start_polling(hubd, 0); 1609*7c478bd9Sstevel@tonic-gate 1610*7c478bd9Sstevel@tonic-gate /* FALLTHRU */ 1611*7c478bd9Sstevel@tonic-gate case USB_DEV_ONLINE: 1612*7c478bd9Sstevel@tonic-gate /* we are already in full power */ 1613*7c478bd9Sstevel@tonic-gate 1614*7c478bd9Sstevel@tonic-gate /* FALLTHRU */ 1615*7c478bd9Sstevel@tonic-gate case USB_DEV_DISCONNECTED: 1616*7c478bd9Sstevel@tonic-gate case USB_DEV_SUSPENDED: 1617*7c478bd9Sstevel@tonic-gate /* 1618*7c478bd9Sstevel@tonic-gate * PM framework tries to put you in full power 1619*7c478bd9Sstevel@tonic-gate * during system shutdown. If we are disconnected 1620*7c478bd9Sstevel@tonic-gate * return success. Also, we should not change state 1621*7c478bd9Sstevel@tonic-gate * when we are disconnected or suspended or about to 1622*7c478bd9Sstevel@tonic-gate * transition to that state 1623*7c478bd9Sstevel@tonic-gate */ 1624*7c478bd9Sstevel@tonic-gate 1625*7c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 1626*7c478bd9Sstevel@tonic-gate default: 1627*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 1628*7c478bd9Sstevel@tonic-gate "hubd_pwrlvl3: Illegal dev_state=%d", hubd->h_dev_state); 1629*7c478bd9Sstevel@tonic-gate 1630*7c478bd9Sstevel@tonic-gate return (USB_FAILURE); 1631*7c478bd9Sstevel@tonic-gate } 1632*7c478bd9Sstevel@tonic-gate } 1633*7c478bd9Sstevel@tonic-gate 1634*7c478bd9Sstevel@tonic-gate 1635*7c478bd9Sstevel@tonic-gate /* power entry point */ 1636*7c478bd9Sstevel@tonic-gate /* ARGSUSED */ 1637*7c478bd9Sstevel@tonic-gate int 1638*7c478bd9Sstevel@tonic-gate usba_hubdi_power(dev_info_t *dip, int comp, int level) 1639*7c478bd9Sstevel@tonic-gate { 1640*7c478bd9Sstevel@tonic-gate hubd_t *hubd; 1641*7c478bd9Sstevel@tonic-gate hub_power_t *hubpm; 1642*7c478bd9Sstevel@tonic-gate int retval; 1643*7c478bd9Sstevel@tonic-gate int circ; 1644*7c478bd9Sstevel@tonic-gate 1645*7c478bd9Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 1646*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle, 1647*7c478bd9Sstevel@tonic-gate "usba_hubdi_power: level=%d", level); 1648*7c478bd9Sstevel@tonic-gate 1649*7c478bd9Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 1650*7c478bd9Sstevel@tonic-gate 1651*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 1652*7c478bd9Sstevel@tonic-gate hubpm = hubd->h_hubpm; 1653*7c478bd9Sstevel@tonic-gate 1654*7c478bd9Sstevel@tonic-gate /* check if we are transitioning to a legal power level */ 1655*7c478bd9Sstevel@tonic-gate if (USB_DEV_PWRSTATE_OK(hubpm->hubp_pwr_states, level)) { 1656*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HUBDI, hubd->h_log_handle, 1657*7c478bd9Sstevel@tonic-gate "usba_hubdi_power: illegal power level=%d " 1658*7c478bd9Sstevel@tonic-gate "hubp_pwr_states=0x%x", level, hubpm->hubp_pwr_states); 1659*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1660*7c478bd9Sstevel@tonic-gate 1661*7c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 1662*7c478bd9Sstevel@tonic-gate 1663*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 1664*7c478bd9Sstevel@tonic-gate } 1665*7c478bd9Sstevel@tonic-gate 1666*7c478bd9Sstevel@tonic-gate switch (level) { 1667*7c478bd9Sstevel@tonic-gate case USB_DEV_OS_PWR_OFF: 1668*7c478bd9Sstevel@tonic-gate retval = hubd_pwrlvl0(hubd); 1669*7c478bd9Sstevel@tonic-gate 1670*7c478bd9Sstevel@tonic-gate break; 1671*7c478bd9Sstevel@tonic-gate case USB_DEV_OS_PWR_1: 1672*7c478bd9Sstevel@tonic-gate retval = hubd_pwrlvl1(hubd); 1673*7c478bd9Sstevel@tonic-gate 1674*7c478bd9Sstevel@tonic-gate break; 1675*7c478bd9Sstevel@tonic-gate case USB_DEV_OS_PWR_2: 1676*7c478bd9Sstevel@tonic-gate retval = hubd_pwrlvl2(hubd); 1677*7c478bd9Sstevel@tonic-gate 1678*7c478bd9Sstevel@tonic-gate break; 1679*7c478bd9Sstevel@tonic-gate case USB_DEV_OS_FULL_PWR: 1680*7c478bd9Sstevel@tonic-gate retval = hubd_pwrlvl3(hubd); 1681*7c478bd9Sstevel@tonic-gate 1682*7c478bd9Sstevel@tonic-gate break; 1683*7c478bd9Sstevel@tonic-gate } 1684*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1685*7c478bd9Sstevel@tonic-gate 1686*7c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 1687*7c478bd9Sstevel@tonic-gate 1688*7c478bd9Sstevel@tonic-gate return ((retval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE); 1689*7c478bd9Sstevel@tonic-gate } 1690*7c478bd9Sstevel@tonic-gate 1691*7c478bd9Sstevel@tonic-gate 1692*7c478bd9Sstevel@tonic-gate /* power entry point for the root hub */ 1693*7c478bd9Sstevel@tonic-gate int 1694*7c478bd9Sstevel@tonic-gate usba_hubdi_root_hub_power(dev_info_t *dip, int comp, int level) 1695*7c478bd9Sstevel@tonic-gate { 1696*7c478bd9Sstevel@tonic-gate return (usba_hubdi_power(dip, comp, level)); 1697*7c478bd9Sstevel@tonic-gate } 1698*7c478bd9Sstevel@tonic-gate 1699*7c478bd9Sstevel@tonic-gate 1700*7c478bd9Sstevel@tonic-gate /* 1701*7c478bd9Sstevel@tonic-gate * standard driver entry points support code 1702*7c478bd9Sstevel@tonic-gate */ 1703*7c478bd9Sstevel@tonic-gate int 1704*7c478bd9Sstevel@tonic-gate usba_hubdi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 1705*7c478bd9Sstevel@tonic-gate { 1706*7c478bd9Sstevel@tonic-gate int instance = ddi_get_instance(dip); 1707*7c478bd9Sstevel@tonic-gate hubd_t *hubd = NULL; 1708*7c478bd9Sstevel@tonic-gate int i, rval; 1709*7c478bd9Sstevel@tonic-gate int minor; 1710*7c478bd9Sstevel@tonic-gate char *log_name = NULL; 1711*7c478bd9Sstevel@tonic-gate const char *root_hub_drvname; 1712*7c478bd9Sstevel@tonic-gate usb_ep_data_t *ep_data; 1713*7c478bd9Sstevel@tonic-gate usba_device_t *child_ud = NULL; 1714*7c478bd9Sstevel@tonic-gate usb_dev_descr_t *usb_dev_descr; 1715*7c478bd9Sstevel@tonic-gate usb_port_status_t parent_port_status, child_port_status; 1716*7c478bd9Sstevel@tonic-gate 1717*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubdi_log_handle, 1718*7c478bd9Sstevel@tonic-gate "hubd_attach instance %d, cmd=0x%x", instance, cmd); 1719*7c478bd9Sstevel@tonic-gate 1720*7c478bd9Sstevel@tonic-gate switch (cmd) { 1721*7c478bd9Sstevel@tonic-gate case DDI_ATTACH: 1722*7c478bd9Sstevel@tonic-gate 1723*7c478bd9Sstevel@tonic-gate break; 1724*7c478bd9Sstevel@tonic-gate case DDI_RESUME: 1725*7c478bd9Sstevel@tonic-gate hubd_cpr_resume(dip); 1726*7c478bd9Sstevel@tonic-gate 1727*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 1728*7c478bd9Sstevel@tonic-gate default: 1729*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 1730*7c478bd9Sstevel@tonic-gate } 1731*7c478bd9Sstevel@tonic-gate 1732*7c478bd9Sstevel@tonic-gate /* 1733*7c478bd9Sstevel@tonic-gate * Allocate softc information. 1734*7c478bd9Sstevel@tonic-gate */ 1735*7c478bd9Sstevel@tonic-gate if (usba_is_root_hub(dip)) { 1736*7c478bd9Sstevel@tonic-gate /* soft state has already been allocated */ 1737*7c478bd9Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 1738*7c478bd9Sstevel@tonic-gate minor = HUBD_IS_ROOT_HUB; 1739*7c478bd9Sstevel@tonic-gate 1740*7c478bd9Sstevel@tonic-gate /* generate readable labels for different root hubs */ 1741*7c478bd9Sstevel@tonic-gate root_hub_drvname = ddi_driver_name(dip); 1742*7c478bd9Sstevel@tonic-gate if (strcmp(root_hub_drvname, "ehci") == 0) { 1743*7c478bd9Sstevel@tonic-gate log_name = "eusb"; 1744*7c478bd9Sstevel@tonic-gate } else if (strcmp(root_hub_drvname, "uhci") == 0) { 1745*7c478bd9Sstevel@tonic-gate log_name = "uusb"; 1746*7c478bd9Sstevel@tonic-gate } else { 1747*7c478bd9Sstevel@tonic-gate /* std. for ohci */ 1748*7c478bd9Sstevel@tonic-gate log_name = "usb"; 1749*7c478bd9Sstevel@tonic-gate } 1750*7c478bd9Sstevel@tonic-gate } else { 1751*7c478bd9Sstevel@tonic-gate rval = ddi_soft_state_zalloc(hubd_statep, instance); 1752*7c478bd9Sstevel@tonic-gate minor = 0; 1753*7c478bd9Sstevel@tonic-gate 1754*7c478bd9Sstevel@tonic-gate if (rval != DDI_SUCCESS) { 1755*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle, 1756*7c478bd9Sstevel@tonic-gate "cannot allocate soft state (%d)", instance); 1757*7c478bd9Sstevel@tonic-gate goto fail; 1758*7c478bd9Sstevel@tonic-gate } 1759*7c478bd9Sstevel@tonic-gate 1760*7c478bd9Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 1761*7c478bd9Sstevel@tonic-gate if (hubd == NULL) { 1762*7c478bd9Sstevel@tonic-gate goto fail; 1763*7c478bd9Sstevel@tonic-gate } 1764*7c478bd9Sstevel@tonic-gate } 1765*7c478bd9Sstevel@tonic-gate 1766*7c478bd9Sstevel@tonic-gate hubd->h_log_handle = usb_alloc_log_hdl(dip, log_name, &hubd_errlevel, 1767*7c478bd9Sstevel@tonic-gate &hubd_errmask, &hubd_instance_debug, 0); 1768*7c478bd9Sstevel@tonic-gate 1769*7c478bd9Sstevel@tonic-gate hubd->h_usba_device = child_ud = usba_get_usba_device(dip); 1770*7c478bd9Sstevel@tonic-gate hubd->h_dip = dip; 1771*7c478bd9Sstevel@tonic-gate hubd->h_instance = instance; 1772*7c478bd9Sstevel@tonic-gate 1773*7c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 1774*7c478bd9Sstevel@tonic-gate child_port_status = child_ud->usb_port_status; 1775*7c478bd9Sstevel@tonic-gate usb_dev_descr = child_ud->usb_dev_descr; 1776*7c478bd9Sstevel@tonic-gate parent_port_status = (child_ud->usb_hs_hub_usba_dev) ? 1777*7c478bd9Sstevel@tonic-gate child_ud->usb_hs_hub_usba_dev->usb_port_status : 0; 1778*7c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 1779*7c478bd9Sstevel@tonic-gate 1780*7c478bd9Sstevel@tonic-gate if ((child_port_status == USBA_FULL_SPEED_DEV) && 1781*7c478bd9Sstevel@tonic-gate (parent_port_status == USBA_HIGH_SPEED_DEV) && 1782*7c478bd9Sstevel@tonic-gate (usb_dev_descr->bcdUSB == 0x100)) { 1783*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L0(DPRINT_MASK_ATTA, hubd->h_log_handle, 1784*7c478bd9Sstevel@tonic-gate "Use of a USB1.0 hub behind a high speed port may " 1785*7c478bd9Sstevel@tonic-gate "cause unexpected failures"); 1786*7c478bd9Sstevel@tonic-gate } 1787*7c478bd9Sstevel@tonic-gate 1788*7c478bd9Sstevel@tonic-gate hubd->h_pipe_policy.pp_max_async_reqs = 1; 1789*7c478bd9Sstevel@tonic-gate 1790*7c478bd9Sstevel@tonic-gate /* register with USBA as client driver */ 1791*7c478bd9Sstevel@tonic-gate if (usb_client_attach(dip, USBDRV_VERSION, 0) != USB_SUCCESS) { 1792*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 1793*7c478bd9Sstevel@tonic-gate "client attach failed"); 1794*7c478bd9Sstevel@tonic-gate 1795*7c478bd9Sstevel@tonic-gate goto fail; 1796*7c478bd9Sstevel@tonic-gate } 1797*7c478bd9Sstevel@tonic-gate 1798*7c478bd9Sstevel@tonic-gate if (usb_get_dev_data(dip, &hubd->h_dev_data, 1799*7c478bd9Sstevel@tonic-gate USB_PARSE_LVL_IF, 0) != USB_SUCCESS) { 1800*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 1801*7c478bd9Sstevel@tonic-gate "cannot get dev_data"); 1802*7c478bd9Sstevel@tonic-gate 1803*7c478bd9Sstevel@tonic-gate goto fail; 1804*7c478bd9Sstevel@tonic-gate } 1805*7c478bd9Sstevel@tonic-gate 1806*7c478bd9Sstevel@tonic-gate if ((ep_data = usb_lookup_ep_data(dip, hubd->h_dev_data, 1807*7c478bd9Sstevel@tonic-gate hubd->h_dev_data->dev_curr_if, 0, 0, 1808*7c478bd9Sstevel@tonic-gate (uint_t)USB_EP_ATTR_INTR, (uint_t)USB_EP_DIR_IN)) == NULL) { 1809*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 1810*7c478bd9Sstevel@tonic-gate "no interrupt IN endpoint found"); 1811*7c478bd9Sstevel@tonic-gate 1812*7c478bd9Sstevel@tonic-gate goto fail; 1813*7c478bd9Sstevel@tonic-gate } 1814*7c478bd9Sstevel@tonic-gate 1815*7c478bd9Sstevel@tonic-gate hubd->h_ep1_descr = ep_data->ep_descr; 1816*7c478bd9Sstevel@tonic-gate hubd->h_default_pipe = hubd->h_dev_data->dev_default_ph; 1817*7c478bd9Sstevel@tonic-gate 1818*7c478bd9Sstevel@tonic-gate mutex_init(HUBD_MUTEX(hubd), NULL, MUTEX_DRIVER, 1819*7c478bd9Sstevel@tonic-gate hubd->h_dev_data->dev_iblock_cookie); 1820*7c478bd9Sstevel@tonic-gate cv_init(&hubd->h_cv_reset_port, NULL, CV_DRIVER, NULL); 1821*7c478bd9Sstevel@tonic-gate 1822*7c478bd9Sstevel@tonic-gate hubd->h_init_state |= HUBD_LOCKS_DONE; 1823*7c478bd9Sstevel@tonic-gate 1824*7c478bd9Sstevel@tonic-gate usb_free_descr_tree(dip, hubd->h_dev_data); 1825*7c478bd9Sstevel@tonic-gate 1826*7c478bd9Sstevel@tonic-gate /* 1827*7c478bd9Sstevel@tonic-gate * register this hub instance with usba 1828*7c478bd9Sstevel@tonic-gate */ 1829*7c478bd9Sstevel@tonic-gate rval = usba_hubdi_register(dip, 0); 1830*7c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 1831*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_ATTA, hubd->h_log_handle, 1832*7c478bd9Sstevel@tonic-gate "usba_hubdi_register failed"); 1833*7c478bd9Sstevel@tonic-gate goto fail; 1834*7c478bd9Sstevel@tonic-gate } 1835*7c478bd9Sstevel@tonic-gate 1836*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 1837*7c478bd9Sstevel@tonic-gate hubd->h_init_state |= HUBD_HUBDI_REGISTERED; 1838*7c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_ONLINE; 1839*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1840*7c478bd9Sstevel@tonic-gate 1841*7c478bd9Sstevel@tonic-gate /* now create components to power manage this device */ 1842*7c478bd9Sstevel@tonic-gate hubd_create_pm_components(dip, hubd); 1843*7c478bd9Sstevel@tonic-gate 1844*7c478bd9Sstevel@tonic-gate /* 1845*7c478bd9Sstevel@tonic-gate * Event handling: definition and registration 1846*7c478bd9Sstevel@tonic-gate * 1847*7c478bd9Sstevel@tonic-gate * first the definition: 1848*7c478bd9Sstevel@tonic-gate * get event handle 1849*7c478bd9Sstevel@tonic-gate */ 1850*7c478bd9Sstevel@tonic-gate (void) ndi_event_alloc_hdl(dip, 0, &hubd->h_ndi_event_hdl, NDI_SLEEP); 1851*7c478bd9Sstevel@tonic-gate 1852*7c478bd9Sstevel@tonic-gate /* bind event set to the handle */ 1853*7c478bd9Sstevel@tonic-gate if (ndi_event_bind_set(hubd->h_ndi_event_hdl, &hubd_ndi_events, 1854*7c478bd9Sstevel@tonic-gate NDI_SLEEP)) { 1855*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle, 1856*7c478bd9Sstevel@tonic-gate "binding event set failed"); 1857*7c478bd9Sstevel@tonic-gate 1858*7c478bd9Sstevel@tonic-gate goto fail; 1859*7c478bd9Sstevel@tonic-gate } 1860*7c478bd9Sstevel@tonic-gate 1861*7c478bd9Sstevel@tonic-gate /* event registration */ 1862*7c478bd9Sstevel@tonic-gate if (hubd_register_events(hubd) != USB_SUCCESS) { 1863*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_ATTA, hubd->h_log_handle, 1864*7c478bd9Sstevel@tonic-gate "hubd_register_events failed"); 1865*7c478bd9Sstevel@tonic-gate 1866*7c478bd9Sstevel@tonic-gate goto fail; 1867*7c478bd9Sstevel@tonic-gate } 1868*7c478bd9Sstevel@tonic-gate 1869*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 1870*7c478bd9Sstevel@tonic-gate hubd->h_init_state |= HUBD_EVENTS_REGISTERED; 1871*7c478bd9Sstevel@tonic-gate 1872*7c478bd9Sstevel@tonic-gate /* initialize and create children */ 1873*7c478bd9Sstevel@tonic-gate if (hubd_check_ports(hubd) != USB_SUCCESS) { 1874*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_ATTA, hubd->h_log_handle, 1875*7c478bd9Sstevel@tonic-gate "hubd_check_ports failed"); 1876*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1877*7c478bd9Sstevel@tonic-gate 1878*7c478bd9Sstevel@tonic-gate goto fail; 1879*7c478bd9Sstevel@tonic-gate } 1880*7c478bd9Sstevel@tonic-gate 1881*7c478bd9Sstevel@tonic-gate /* 1882*7c478bd9Sstevel@tonic-gate * create cfgadm nodes 1883*7c478bd9Sstevel@tonic-gate */ 1884*7c478bd9Sstevel@tonic-gate hubd->h_ancestry_str = (char *)kmem_zalloc(HUBD_APID_NAMELEN, KM_SLEEP); 1885*7c478bd9Sstevel@tonic-gate hubd_get_ancestry_str(hubd); 1886*7c478bd9Sstevel@tonic-gate 1887*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 1888*7c478bd9Sstevel@tonic-gate "#ports=0x%x", hubd->h_hub_descr.bNbrPorts); 1889*7c478bd9Sstevel@tonic-gate 1890*7c478bd9Sstevel@tonic-gate for (i = 1; i <= hubd->h_hub_descr.bNbrPorts; i++) { 1891*7c478bd9Sstevel@tonic-gate char ap_name[HUBD_APID_NAMELEN]; 1892*7c478bd9Sstevel@tonic-gate 1893*7c478bd9Sstevel@tonic-gate (void) snprintf(ap_name, HUBD_APID_NAMELEN, "%s%d", 1894*7c478bd9Sstevel@tonic-gate hubd->h_ancestry_str, i); 1895*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 1896*7c478bd9Sstevel@tonic-gate "ap_name=%s", ap_name); 1897*7c478bd9Sstevel@tonic-gate 1898*7c478bd9Sstevel@tonic-gate if (ddi_create_minor_node(dip, ap_name, S_IFCHR, instance, 1899*7c478bd9Sstevel@tonic-gate DDI_NT_USB_ATTACHMENT_POINT, 0) != DDI_SUCCESS) { 1900*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_ATTA, hubd->h_log_handle, 1901*7c478bd9Sstevel@tonic-gate "cannot create attachment point node (%d)", 1902*7c478bd9Sstevel@tonic-gate instance); 1903*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1904*7c478bd9Sstevel@tonic-gate 1905*7c478bd9Sstevel@tonic-gate goto fail; 1906*7c478bd9Sstevel@tonic-gate } 1907*7c478bd9Sstevel@tonic-gate } 1908*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1909*7c478bd9Sstevel@tonic-gate 1910*7c478bd9Sstevel@tonic-gate /* create minor nodes */ 1911*7c478bd9Sstevel@tonic-gate if (ddi_create_minor_node(dip, "hubd", S_IFCHR, 1912*7c478bd9Sstevel@tonic-gate instance | minor, DDI_NT_NEXUS, 0) != DDI_SUCCESS) { 1913*7c478bd9Sstevel@tonic-gate 1914*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_ATTA, hubd->h_log_handle, 1915*7c478bd9Sstevel@tonic-gate "cannot create devctl minor node (%d)", instance); 1916*7c478bd9Sstevel@tonic-gate 1917*7c478bd9Sstevel@tonic-gate goto fail; 1918*7c478bd9Sstevel@tonic-gate } 1919*7c478bd9Sstevel@tonic-gate 1920*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 1921*7c478bd9Sstevel@tonic-gate hubd->h_init_state |= HUBD_MINOR_NODE_CREATED; 1922*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1923*7c478bd9Sstevel@tonic-gate 1924*7c478bd9Sstevel@tonic-gate /* 1925*7c478bd9Sstevel@tonic-gate * host controller driver has already reported this dev 1926*7c478bd9Sstevel@tonic-gate * if we are the root hub 1927*7c478bd9Sstevel@tonic-gate */ 1928*7c478bd9Sstevel@tonic-gate if (!usba_is_root_hub(dip)) { 1929*7c478bd9Sstevel@tonic-gate ddi_report_dev(dip); 1930*7c478bd9Sstevel@tonic-gate } 1931*7c478bd9Sstevel@tonic-gate 1932*7c478bd9Sstevel@tonic-gate /* enable deathrow thread */ 1933*7c478bd9Sstevel@tonic-gate hubd->h_cleanup_enabled = B_TRUE; 1934*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 1935*7c478bd9Sstevel@tonic-gate hubd_pm_idle_component(hubd, dip, 0); 1936*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1937*7c478bd9Sstevel@tonic-gate 1938*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 1939*7c478bd9Sstevel@tonic-gate 1940*7c478bd9Sstevel@tonic-gate fail: 1941*7c478bd9Sstevel@tonic-gate { 1942*7c478bd9Sstevel@tonic-gate char *pathname = kmem_alloc(MAXPATHLEN, KM_SLEEP); 1943*7c478bd9Sstevel@tonic-gate 1944*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L0(DPRINT_MASK_ATTA, hubdi_log_handle, 1945*7c478bd9Sstevel@tonic-gate "cannot attach %s", ddi_pathname(dip, pathname)); 1946*7c478bd9Sstevel@tonic-gate 1947*7c478bd9Sstevel@tonic-gate kmem_free(pathname, MAXPATHLEN); 1948*7c478bd9Sstevel@tonic-gate } 1949*7c478bd9Sstevel@tonic-gate 1950*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 1951*7c478bd9Sstevel@tonic-gate hubd_pm_idle_component(hubd, dip, 0); 1952*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 1953*7c478bd9Sstevel@tonic-gate 1954*7c478bd9Sstevel@tonic-gate if (hubd) { 1955*7c478bd9Sstevel@tonic-gate rval = hubd_cleanup(dip, hubd); 1956*7c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 1957*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_ATTA, hubdi_log_handle, 1958*7c478bd9Sstevel@tonic-gate "failure to complete cleanup after attach failure"); 1959*7c478bd9Sstevel@tonic-gate } 1960*7c478bd9Sstevel@tonic-gate } 1961*7c478bd9Sstevel@tonic-gate 1962*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 1963*7c478bd9Sstevel@tonic-gate } 1964*7c478bd9Sstevel@tonic-gate 1965*7c478bd9Sstevel@tonic-gate 1966*7c478bd9Sstevel@tonic-gate int 1967*7c478bd9Sstevel@tonic-gate usba_hubdi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 1968*7c478bd9Sstevel@tonic-gate { 1969*7c478bd9Sstevel@tonic-gate hubd_t *hubd = hubd_get_soft_state(dip); 1970*7c478bd9Sstevel@tonic-gate int rval; 1971*7c478bd9Sstevel@tonic-gate 1972*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 1973*7c478bd9Sstevel@tonic-gate "hubd_detach: cmd=0x%x", cmd); 1974*7c478bd9Sstevel@tonic-gate 1975*7c478bd9Sstevel@tonic-gate switch (cmd) { 1976*7c478bd9Sstevel@tonic-gate case DDI_DETACH: 1977*7c478bd9Sstevel@tonic-gate rval = hubd_cleanup(dip, hubd); 1978*7c478bd9Sstevel@tonic-gate 1979*7c478bd9Sstevel@tonic-gate return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE); 1980*7c478bd9Sstevel@tonic-gate case DDI_SUSPEND: 1981*7c478bd9Sstevel@tonic-gate rval = hubd_cpr_suspend(hubd); 1982*7c478bd9Sstevel@tonic-gate 1983*7c478bd9Sstevel@tonic-gate return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE); 1984*7c478bd9Sstevel@tonic-gate default: 1985*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 1986*7c478bd9Sstevel@tonic-gate } 1987*7c478bd9Sstevel@tonic-gate } 1988*7c478bd9Sstevel@tonic-gate 1989*7c478bd9Sstevel@tonic-gate 1990*7c478bd9Sstevel@tonic-gate /* 1991*7c478bd9Sstevel@tonic-gate * hubd_setdevaddr 1992*7c478bd9Sstevel@tonic-gate * set the device addrs on this port 1993*7c478bd9Sstevel@tonic-gate */ 1994*7c478bd9Sstevel@tonic-gate static int 1995*7c478bd9Sstevel@tonic-gate hubd_setdevaddr(hubd_t *hubd, usb_port_t port) 1996*7c478bd9Sstevel@tonic-gate { 1997*7c478bd9Sstevel@tonic-gate int rval; 1998*7c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 1999*7c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 2000*7c478bd9Sstevel@tonic-gate usb_pipe_handle_t ph; 2001*7c478bd9Sstevel@tonic-gate dev_info_t *child_dip = NULL; 2002*7c478bd9Sstevel@tonic-gate uchar_t address = 0; 2003*7c478bd9Sstevel@tonic-gate usba_device_t *usba_device; 2004*7c478bd9Sstevel@tonic-gate int retry = 0; 2005*7c478bd9Sstevel@tonic-gate long time_delay; 2006*7c478bd9Sstevel@tonic-gate 2007*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 2008*7c478bd9Sstevel@tonic-gate "hubd_setdevaddr: port=%d", port); 2009*7c478bd9Sstevel@tonic-gate 2010*7c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 2011*7c478bd9Sstevel@tonic-gate 2012*7c478bd9Sstevel@tonic-gate child_dip = hubd->h_children_dips[port]; 2013*7c478bd9Sstevel@tonic-gate address = hubd->h_usba_devices[port]->usb_addr; 2014*7c478bd9Sstevel@tonic-gate usba_device = hubd->h_usba_devices[port]; 2015*7c478bd9Sstevel@tonic-gate 2016*7c478bd9Sstevel@tonic-gate /* close the default pipe with addr x */ 2017*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 2018*7c478bd9Sstevel@tonic-gate ph = usba_get_dflt_pipe_handle(child_dip); 2019*7c478bd9Sstevel@tonic-gate usb_pipe_close(child_dip, ph, 2020*7c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL); 2021*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 2022*7c478bd9Sstevel@tonic-gate 2023*7c478bd9Sstevel@tonic-gate /* 2024*7c478bd9Sstevel@tonic-gate * As this device has been reset, temporarily 2025*7c478bd9Sstevel@tonic-gate * assign the default address 2026*7c478bd9Sstevel@tonic-gate */ 2027*7c478bd9Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex); 2028*7c478bd9Sstevel@tonic-gate address = usba_device->usb_addr; 2029*7c478bd9Sstevel@tonic-gate usba_device->usb_addr = USBA_DEFAULT_ADDR; 2030*7c478bd9Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex); 2031*7c478bd9Sstevel@tonic-gate 2032*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 2033*7c478bd9Sstevel@tonic-gate 2034*7c478bd9Sstevel@tonic-gate time_delay = drv_usectohz(hubd_device_delay / 20); 2035*7c478bd9Sstevel@tonic-gate for (retry = 0; retry < hubd_retry_enumerate; retry++) { 2036*7c478bd9Sstevel@tonic-gate 2037*7c478bd9Sstevel@tonic-gate /* open child's default pipe with USBA_DEFAULT_ADDR */ 2038*7c478bd9Sstevel@tonic-gate if (usb_pipe_open(child_dip, NULL, NULL, 2039*7c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph) != 2040*7c478bd9Sstevel@tonic-gate USB_SUCCESS) { 2041*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 2042*7c478bd9Sstevel@tonic-gate "hubd_setdevaddr: Unable to open default pipe"); 2043*7c478bd9Sstevel@tonic-gate 2044*7c478bd9Sstevel@tonic-gate break; 2045*7c478bd9Sstevel@tonic-gate } 2046*7c478bd9Sstevel@tonic-gate 2047*7c478bd9Sstevel@tonic-gate /* Set the address of the device */ 2048*7c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 2049*7c478bd9Sstevel@tonic-gate USB_DEV_REQ_HOST_TO_DEV, 2050*7c478bd9Sstevel@tonic-gate USB_REQ_SET_ADDRESS, /* bRequest */ 2051*7c478bd9Sstevel@tonic-gate address, /* wValue */ 2052*7c478bd9Sstevel@tonic-gate 0, /* wIndex */ 2053*7c478bd9Sstevel@tonic-gate 0, /* wLength */ 2054*7c478bd9Sstevel@tonic-gate NULL, 0, 2055*7c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 2056*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 2057*7c478bd9Sstevel@tonic-gate "hubd_setdevaddr(%d): rval=%d cr=%d cb_fl=0x%x", 2058*7c478bd9Sstevel@tonic-gate retry, rval, completion_reason, cb_flags); 2059*7c478bd9Sstevel@tonic-gate } 2060*7c478bd9Sstevel@tonic-gate 2061*7c478bd9Sstevel@tonic-gate usb_pipe_close(child_dip, ph, 2062*7c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL); 2063*7c478bd9Sstevel@tonic-gate 2064*7c478bd9Sstevel@tonic-gate if (rval == USB_SUCCESS) { 2065*7c478bd9Sstevel@tonic-gate 2066*7c478bd9Sstevel@tonic-gate break; 2067*7c478bd9Sstevel@tonic-gate } 2068*7c478bd9Sstevel@tonic-gate 2069*7c478bd9Sstevel@tonic-gate delay(time_delay); 2070*7c478bd9Sstevel@tonic-gate } 2071*7c478bd9Sstevel@tonic-gate 2072*7c478bd9Sstevel@tonic-gate /* Reset to the old address */ 2073*7c478bd9Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex); 2074*7c478bd9Sstevel@tonic-gate usba_device->usb_addr = address; 2075*7c478bd9Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex); 2076*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 2077*7c478bd9Sstevel@tonic-gate 2078*7c478bd9Sstevel@tonic-gate usba_clear_data_toggle(usba_device); 2079*7c478bd9Sstevel@tonic-gate 2080*7c478bd9Sstevel@tonic-gate return (rval); 2081*7c478bd9Sstevel@tonic-gate } 2082*7c478bd9Sstevel@tonic-gate 2083*7c478bd9Sstevel@tonic-gate 2084*7c478bd9Sstevel@tonic-gate /* 2085*7c478bd9Sstevel@tonic-gate * hubd_setdevconfig 2086*7c478bd9Sstevel@tonic-gate * set the device addrs on this port 2087*7c478bd9Sstevel@tonic-gate */ 2088*7c478bd9Sstevel@tonic-gate static void 2089*7c478bd9Sstevel@tonic-gate hubd_setdevconfig(hubd_t *hubd, usb_port_t port) 2090*7c478bd9Sstevel@tonic-gate { 2091*7c478bd9Sstevel@tonic-gate int rval; 2092*7c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 2093*7c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 2094*7c478bd9Sstevel@tonic-gate usb_pipe_handle_t ph; 2095*7c478bd9Sstevel@tonic-gate dev_info_t *child_dip = NULL; 2096*7c478bd9Sstevel@tonic-gate usba_device_t *usba_device = NULL; 2097*7c478bd9Sstevel@tonic-gate uint16_t config_value; 2098*7c478bd9Sstevel@tonic-gate 2099*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 2100*7c478bd9Sstevel@tonic-gate "hubd_setdevconfig: port=%d", port); 2101*7c478bd9Sstevel@tonic-gate 2102*7c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 2103*7c478bd9Sstevel@tonic-gate 2104*7c478bd9Sstevel@tonic-gate child_dip = hubd->h_children_dips[port]; 2105*7c478bd9Sstevel@tonic-gate usba_device = hubd->h_usba_devices[port]; 2106*7c478bd9Sstevel@tonic-gate config_value = hubd->h_usba_devices[port]->usb_cfg_value; 2107*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 2108*7c478bd9Sstevel@tonic-gate 2109*7c478bd9Sstevel@tonic-gate /* open the default control pipe */ 2110*7c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_open(child_dip, NULL, NULL, 2111*7c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) == 2112*7c478bd9Sstevel@tonic-gate USB_SUCCESS) { 2113*7c478bd9Sstevel@tonic-gate 2114*7c478bd9Sstevel@tonic-gate /* Set the default configuration of the device */ 2115*7c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 2116*7c478bd9Sstevel@tonic-gate USB_DEV_REQ_HOST_TO_DEV, 2117*7c478bd9Sstevel@tonic-gate USB_REQ_SET_CFG, /* bRequest */ 2118*7c478bd9Sstevel@tonic-gate config_value, /* wValue */ 2119*7c478bd9Sstevel@tonic-gate 0, /* wIndex */ 2120*7c478bd9Sstevel@tonic-gate 0, /* wLength */ 2121*7c478bd9Sstevel@tonic-gate NULL, 0, 2122*7c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 2123*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 2124*7c478bd9Sstevel@tonic-gate "hubd_setdevconfig: set device config failed: " 2125*7c478bd9Sstevel@tonic-gate "cr=%d cb_fl=0x%x rval=%d", 2126*7c478bd9Sstevel@tonic-gate completion_reason, cb_flags, rval); 2127*7c478bd9Sstevel@tonic-gate } 2128*7c478bd9Sstevel@tonic-gate /* 2129*7c478bd9Sstevel@tonic-gate * After setting the configuration, we make this default 2130*7c478bd9Sstevel@tonic-gate * control pipe persistent, so that it gets re-opened 2131*7c478bd9Sstevel@tonic-gate * on posting a connect event 2132*7c478bd9Sstevel@tonic-gate */ 2133*7c478bd9Sstevel@tonic-gate usba_persistent_pipe_close(usba_device); 2134*7c478bd9Sstevel@tonic-gate } else { 2135*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 2136*7c478bd9Sstevel@tonic-gate "pipe open fails: rval=%d", rval); 2137*7c478bd9Sstevel@tonic-gate } 2138*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 2139*7c478bd9Sstevel@tonic-gate } 2140*7c478bd9Sstevel@tonic-gate 2141*7c478bd9Sstevel@tonic-gate 2142*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 2143*7c478bd9Sstevel@tonic-gate static int 2144*7c478bd9Sstevel@tonic-gate hubd_check_disconnected_ports(dev_info_t *dip, void *arg) 2145*7c478bd9Sstevel@tonic-gate { 2146*7c478bd9Sstevel@tonic-gate int circ; 2147*7c478bd9Sstevel@tonic-gate usb_port_t port; 2148*7c478bd9Sstevel@tonic-gate hubd_t *hubd; 2149*7c478bd9Sstevel@tonic-gate major_t hub_major = ddi_name_to_major("hubd"); 2150*7c478bd9Sstevel@tonic-gate 2151*7c478bd9Sstevel@tonic-gate /* 2152*7c478bd9Sstevel@tonic-gate * make sure dip is a usb hub, major of root hub is HCD 2153*7c478bd9Sstevel@tonic-gate * major 2154*7c478bd9Sstevel@tonic-gate */ 2155*7c478bd9Sstevel@tonic-gate if (!usba_is_root_hub(dip)) { 2156*7c478bd9Sstevel@tonic-gate if ((ddi_driver_major(dip) != hub_major) || 2157*7c478bd9Sstevel@tonic-gate (i_ddi_node_state(dip) < DS_ATTACHED)) { 2158*7c478bd9Sstevel@tonic-gate 2159*7c478bd9Sstevel@tonic-gate return (DDI_WALK_PRUNECHILD); 2160*7c478bd9Sstevel@tonic-gate } 2161*7c478bd9Sstevel@tonic-gate } 2162*7c478bd9Sstevel@tonic-gate 2163*7c478bd9Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 2164*7c478bd9Sstevel@tonic-gate if (hubd == NULL) { 2165*7c478bd9Sstevel@tonic-gate 2166*7c478bd9Sstevel@tonic-gate return (DDI_WALK_PRUNECHILD); 2167*7c478bd9Sstevel@tonic-gate } 2168*7c478bd9Sstevel@tonic-gate 2169*7c478bd9Sstevel@tonic-gate /* walk child list and remove nodes with flag DEVI_DEVICE_REMOVED */ 2170*7c478bd9Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 2171*7c478bd9Sstevel@tonic-gate 2172*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 2173*7c478bd9Sstevel@tonic-gate for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { 2174*7c478bd9Sstevel@tonic-gate dev_info_t *cdip = hubd->h_children_dips[port]; 2175*7c478bd9Sstevel@tonic-gate 2176*7c478bd9Sstevel@tonic-gate if (cdip == NULL || DEVI_IS_DEVICE_REMOVED(cdip) == 0) { 2177*7c478bd9Sstevel@tonic-gate 2178*7c478bd9Sstevel@tonic-gate continue; 2179*7c478bd9Sstevel@tonic-gate } 2180*7c478bd9Sstevel@tonic-gate 2181*7c478bd9Sstevel@tonic-gate (void) hubd_delete_child(hubd, port, NDI_DEVI_REMOVE, B_TRUE); 2182*7c478bd9Sstevel@tonic-gate } 2183*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 2184*7c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 2185*7c478bd9Sstevel@tonic-gate 2186*7c478bd9Sstevel@tonic-gate /* skip siblings of root hub */ 2187*7c478bd9Sstevel@tonic-gate if (usba_is_root_hub(dip)) { 2188*7c478bd9Sstevel@tonic-gate 2189*7c478bd9Sstevel@tonic-gate return (DDI_WALK_PRUNESIB); 2190*7c478bd9Sstevel@tonic-gate } 2191*7c478bd9Sstevel@tonic-gate 2192*7c478bd9Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 2193*7c478bd9Sstevel@tonic-gate } 2194*7c478bd9Sstevel@tonic-gate 2195*7c478bd9Sstevel@tonic-gate 2196*7c478bd9Sstevel@tonic-gate /* 2197*7c478bd9Sstevel@tonic-gate * this thread will walk all children under the root hub for this 2198*7c478bd9Sstevel@tonic-gate * USB bus instance and attempt to remove them 2199*7c478bd9Sstevel@tonic-gate */ 2200*7c478bd9Sstevel@tonic-gate static void 2201*7c478bd9Sstevel@tonic-gate hubd_root_hub_cleanup_thread(void *arg) 2202*7c478bd9Sstevel@tonic-gate { 2203*7c478bd9Sstevel@tonic-gate int circ; 2204*7c478bd9Sstevel@tonic-gate hubd_t *root_hubd = (hubd_t *)arg; 2205*7c478bd9Sstevel@tonic-gate dev_info_t *rh_dip = root_hubd->h_dip; 2206*7c478bd9Sstevel@tonic-gate #ifndef __lock_lint 2207*7c478bd9Sstevel@tonic-gate callb_cpr_t cprinfo; 2208*7c478bd9Sstevel@tonic-gate 2209*7c478bd9Sstevel@tonic-gate CALLB_CPR_INIT(&cprinfo, HUBD_MUTEX(root_hubd), callb_generic_cpr, 2210*7c478bd9Sstevel@tonic-gate "USB root hub"); 2211*7c478bd9Sstevel@tonic-gate #endif 2212*7c478bd9Sstevel@tonic-gate 2213*7c478bd9Sstevel@tonic-gate for (;;) { 2214*7c478bd9Sstevel@tonic-gate /* don't race with detach */ 2215*7c478bd9Sstevel@tonic-gate ndi_hold_devi(rh_dip); 2216*7c478bd9Sstevel@tonic-gate 2217*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(root_hubd)); 2218*7c478bd9Sstevel@tonic-gate root_hubd->h_cleanup_needed = 0; 2219*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd)); 2220*7c478bd9Sstevel@tonic-gate 2221*7c478bd9Sstevel@tonic-gate (void) devfs_clean(rh_dip, NULL, 0); 2222*7c478bd9Sstevel@tonic-gate 2223*7c478bd9Sstevel@tonic-gate ndi_devi_enter(ddi_get_parent(rh_dip), &circ); 2224*7c478bd9Sstevel@tonic-gate ddi_walk_devs(rh_dip, hubd_check_disconnected_ports, 2225*7c478bd9Sstevel@tonic-gate NULL); 2226*7c478bd9Sstevel@tonic-gate #ifdef __lock_lint 2227*7c478bd9Sstevel@tonic-gate (void) hubd_check_disconnected_ports(rh_dip, NULL); 2228*7c478bd9Sstevel@tonic-gate #endif 2229*7c478bd9Sstevel@tonic-gate ndi_devi_exit(ddi_get_parent(rh_dip), circ); 2230*7c478bd9Sstevel@tonic-gate 2231*7c478bd9Sstevel@tonic-gate /* quit if we are not enabled anymore */ 2232*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(root_hubd)); 2233*7c478bd9Sstevel@tonic-gate if ((root_hubd->h_cleanup_enabled == B_FALSE) || 2234*7c478bd9Sstevel@tonic-gate (root_hubd->h_cleanup_needed == B_FALSE)) { 2235*7c478bd9Sstevel@tonic-gate root_hubd->h_cleanup_active = B_FALSE; 2236*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd)); 2237*7c478bd9Sstevel@tonic-gate ndi_rele_devi(rh_dip); 2238*7c478bd9Sstevel@tonic-gate 2239*7c478bd9Sstevel@tonic-gate break; 2240*7c478bd9Sstevel@tonic-gate } 2241*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd)); 2242*7c478bd9Sstevel@tonic-gate ndi_rele_devi(rh_dip); 2243*7c478bd9Sstevel@tonic-gate 2244*7c478bd9Sstevel@tonic-gate #ifndef __lock_lint 2245*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(root_hubd)); 2246*7c478bd9Sstevel@tonic-gate CALLB_CPR_SAFE_BEGIN(&cprinfo); 2247*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd)); 2248*7c478bd9Sstevel@tonic-gate 2249*7c478bd9Sstevel@tonic-gate delay(drv_usectohz(hubd_dip_cleanup_delay)); 2250*7c478bd9Sstevel@tonic-gate 2251*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(root_hubd)); 2252*7c478bd9Sstevel@tonic-gate CALLB_CPR_SAFE_END(&cprinfo, HUBD_MUTEX(root_hubd)); 2253*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd)); 2254*7c478bd9Sstevel@tonic-gate #endif 2255*7c478bd9Sstevel@tonic-gate } 2256*7c478bd9Sstevel@tonic-gate 2257*7c478bd9Sstevel@tonic-gate #ifndef __lock_lint 2258*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(root_hubd)); 2259*7c478bd9Sstevel@tonic-gate CALLB_CPR_EXIT(&cprinfo); 2260*7c478bd9Sstevel@tonic-gate #endif 2261*7c478bd9Sstevel@tonic-gate } 2262*7c478bd9Sstevel@tonic-gate 2263*7c478bd9Sstevel@tonic-gate 2264*7c478bd9Sstevel@tonic-gate static void 2265*7c478bd9Sstevel@tonic-gate hubd_schedule_cleanup(dev_info_t *rh_dip) 2266*7c478bd9Sstevel@tonic-gate { 2267*7c478bd9Sstevel@tonic-gate hubd_t *root_hubd = (hubd_t *)hubd_get_soft_state(rh_dip); 2268*7c478bd9Sstevel@tonic-gate 2269*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(root_hubd)); 2270*7c478bd9Sstevel@tonic-gate root_hubd->h_cleanup_needed = B_TRUE; 2271*7c478bd9Sstevel@tonic-gate if (root_hubd->h_cleanup_enabled && !(root_hubd->h_cleanup_active)) { 2272*7c478bd9Sstevel@tonic-gate root_hubd->h_cleanup_active = B_TRUE; 2273*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd)); 2274*7c478bd9Sstevel@tonic-gate (void) thread_create(NULL, 0, 2275*7c478bd9Sstevel@tonic-gate hubd_root_hub_cleanup_thread, 2276*7c478bd9Sstevel@tonic-gate (void *)root_hubd, 0, &p0, TS_RUN, 2277*7c478bd9Sstevel@tonic-gate minclsyspri); 2278*7c478bd9Sstevel@tonic-gate } else { 2279*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd)); 2280*7c478bd9Sstevel@tonic-gate } 2281*7c478bd9Sstevel@tonic-gate } 2282*7c478bd9Sstevel@tonic-gate 2283*7c478bd9Sstevel@tonic-gate 2284*7c478bd9Sstevel@tonic-gate /* 2285*7c478bd9Sstevel@tonic-gate * hubd_restore_device_state: 2286*7c478bd9Sstevel@tonic-gate * - set config for the hub 2287*7c478bd9Sstevel@tonic-gate * - power cycle all the ports 2288*7c478bd9Sstevel@tonic-gate * - for each port that was connected 2289*7c478bd9Sstevel@tonic-gate * - reset port 2290*7c478bd9Sstevel@tonic-gate * - assign addrs to the device on this port 2291*7c478bd9Sstevel@tonic-gate * - restart polling 2292*7c478bd9Sstevel@tonic-gate * - reset suspend flag 2293*7c478bd9Sstevel@tonic-gate */ 2294*7c478bd9Sstevel@tonic-gate static void 2295*7c478bd9Sstevel@tonic-gate hubd_restore_device_state(dev_info_t *dip, hubd_t *hubd) 2296*7c478bd9Sstevel@tonic-gate { 2297*7c478bd9Sstevel@tonic-gate int rval; 2298*7c478bd9Sstevel@tonic-gate int retry; 2299*7c478bd9Sstevel@tonic-gate uint_t hub_prev_state; 2300*7c478bd9Sstevel@tonic-gate usb_port_t port; 2301*7c478bd9Sstevel@tonic-gate uint16_t status; 2302*7c478bd9Sstevel@tonic-gate uint16_t change; 2303*7c478bd9Sstevel@tonic-gate dev_info_t *ch_dip; 2304*7c478bd9Sstevel@tonic-gate boolean_t ehci_root_hub; 2305*7c478bd9Sstevel@tonic-gate 2306*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 2307*7c478bd9Sstevel@tonic-gate "hubd_restore_device_state:"); 2308*7c478bd9Sstevel@tonic-gate 2309*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 2310*7c478bd9Sstevel@tonic-gate hub_prev_state = hubd->h_dev_state; 2311*7c478bd9Sstevel@tonic-gate ASSERT(hub_prev_state != USB_DEV_PWRED_DOWN); 2312*7c478bd9Sstevel@tonic-gate 2313*7c478bd9Sstevel@tonic-gate /* First bring the device to full power */ 2314*7c478bd9Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, dip, 0); 2315*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 2316*7c478bd9Sstevel@tonic-gate 2317*7c478bd9Sstevel@tonic-gate (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); 2318*7c478bd9Sstevel@tonic-gate 2319*7c478bd9Sstevel@tonic-gate if (!usba_is_root_hub(dip) && 2320*7c478bd9Sstevel@tonic-gate (usb_check_same_device(dip, hubd->h_log_handle, USB_LOG_L0, 2321*7c478bd9Sstevel@tonic-gate DPRINT_MASK_HOTPLUG, 2322*7c478bd9Sstevel@tonic-gate USB_CHK_BASIC|USB_CHK_CFG, NULL) != USB_SUCCESS)) { 2323*7c478bd9Sstevel@tonic-gate 2324*7c478bd9Sstevel@tonic-gate /* change the device state to disconnected */ 2325*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 2326*7c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_DISCONNECTED; 2327*7c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, dip, 0); 2328*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 2329*7c478bd9Sstevel@tonic-gate 2330*7c478bd9Sstevel@tonic-gate return; 2331*7c478bd9Sstevel@tonic-gate } 2332*7c478bd9Sstevel@tonic-gate 2333*7c478bd9Sstevel@tonic-gate ehci_root_hub = (strcmp(ddi_driver_name(dip), "ehci") == 0); 2334*7c478bd9Sstevel@tonic-gate 2335*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 2336*7c478bd9Sstevel@tonic-gate /* First turn off all port power */ 2337*7c478bd9Sstevel@tonic-gate rval = hubd_disable_all_port_power(hubd); 2338*7c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 2339*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle, 2340*7c478bd9Sstevel@tonic-gate "hubd_restore_device_state:" 2341*7c478bd9Sstevel@tonic-gate "turning off port power failed"); 2342*7c478bd9Sstevel@tonic-gate } 2343*7c478bd9Sstevel@tonic-gate 2344*7c478bd9Sstevel@tonic-gate /* Settling time before turning on again */ 2345*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 2346*7c478bd9Sstevel@tonic-gate delay(drv_usectohz(hubd_device_delay / 100)); 2347*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 2348*7c478bd9Sstevel@tonic-gate 2349*7c478bd9Sstevel@tonic-gate /* enable power on all ports so we can see connects */ 2350*7c478bd9Sstevel@tonic-gate if (hubd_enable_all_port_power(hubd) != USB_SUCCESS) { 2351*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 2352*7c478bd9Sstevel@tonic-gate "hubd_restore_device_state: turn on port power failed"); 2353*7c478bd9Sstevel@tonic-gate 2354*7c478bd9Sstevel@tonic-gate /* disable whatever was enabled */ 2355*7c478bd9Sstevel@tonic-gate (void) hubd_disable_all_port_power(hubd); 2356*7c478bd9Sstevel@tonic-gate 2357*7c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, dip, 0); 2358*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 2359*7c478bd9Sstevel@tonic-gate 2360*7c478bd9Sstevel@tonic-gate return; 2361*7c478bd9Sstevel@tonic-gate } 2362*7c478bd9Sstevel@tonic-gate 2363*7c478bd9Sstevel@tonic-gate /* 2364*7c478bd9Sstevel@tonic-gate * wait at least 3 frames before accessing devices 2365*7c478bd9Sstevel@tonic-gate * (note that delay's minimal time is one clock tick which 2366*7c478bd9Sstevel@tonic-gate * is 10ms unless hires_tick has been changed) 2367*7c478bd9Sstevel@tonic-gate */ 2368*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 2369*7c478bd9Sstevel@tonic-gate delay(drv_usectohz(10000)); 2370*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 2371*7c478bd9Sstevel@tonic-gate 2372*7c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_HUB_STATE_RECOVER; 2373*7c478bd9Sstevel@tonic-gate 2374*7c478bd9Sstevel@tonic-gate for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { 2375*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle, 2376*7c478bd9Sstevel@tonic-gate "hubd_restore_device_state: port=%d", port); 2377*7c478bd9Sstevel@tonic-gate 2378*7c478bd9Sstevel@tonic-gate /* 2379*7c478bd9Sstevel@tonic-gate * the childen_dips list may have dips that have been 2380*7c478bd9Sstevel@tonic-gate * already deallocated. we only get a post_detach notification 2381*7c478bd9Sstevel@tonic-gate * but not a destroy notification 2382*7c478bd9Sstevel@tonic-gate */ 2383*7c478bd9Sstevel@tonic-gate ch_dip = hubd->h_children_dips[port]; 2384*7c478bd9Sstevel@tonic-gate if (ch_dip) { 2385*7c478bd9Sstevel@tonic-gate /* get port status */ 2386*7c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 2387*7c478bd9Sstevel@tonic-gate &status, &change, PORT_CHANGE_CSC); 2388*7c478bd9Sstevel@tonic-gate 2389*7c478bd9Sstevel@tonic-gate /* check if it is truly connected */ 2390*7c478bd9Sstevel@tonic-gate if (status & PORT_STATUS_CCS) { 2391*7c478bd9Sstevel@tonic-gate /* 2392*7c478bd9Sstevel@tonic-gate * Now reset port and assign the device 2393*7c478bd9Sstevel@tonic-gate * its original address 2394*7c478bd9Sstevel@tonic-gate */ 2395*7c478bd9Sstevel@tonic-gate retry = 0; 2396*7c478bd9Sstevel@tonic-gate do { 2397*7c478bd9Sstevel@tonic-gate (void) hubd_reset_port(hubd, port); 2398*7c478bd9Sstevel@tonic-gate 2399*7c478bd9Sstevel@tonic-gate /* required for ppx */ 2400*7c478bd9Sstevel@tonic-gate (void) hubd_enable_port(hubd, port); 2401*7c478bd9Sstevel@tonic-gate 2402*7c478bd9Sstevel@tonic-gate if (retry) { 2403*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 2404*7c478bd9Sstevel@tonic-gate delay(drv_usectohz( 2405*7c478bd9Sstevel@tonic-gate hubd_device_delay/2)); 2406*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 2407*7c478bd9Sstevel@tonic-gate } 2408*7c478bd9Sstevel@tonic-gate 2409*7c478bd9Sstevel@tonic-gate rval = hubd_setdevaddr(hubd, port); 2410*7c478bd9Sstevel@tonic-gate retry++; 2411*7c478bd9Sstevel@tonic-gate } while ((rval != USB_SUCCESS) && 2412*7c478bd9Sstevel@tonic-gate (retry < hubd_retry_enumerate)); 2413*7c478bd9Sstevel@tonic-gate 2414*7c478bd9Sstevel@tonic-gate hubd_setdevconfig(hubd, port); 2415*7c478bd9Sstevel@tonic-gate 2416*7c478bd9Sstevel@tonic-gate if (hub_prev_state == USB_DEV_DISCONNECTED) { 2417*7c478bd9Sstevel@tonic-gate /* post a connect event */ 2418*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 2419*7c478bd9Sstevel@tonic-gate hubd_post_event(hubd, port, 2420*7c478bd9Sstevel@tonic-gate USBA_EVENT_TAG_HOT_INSERTION); 2421*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 2422*7c478bd9Sstevel@tonic-gate } else { 2423*7c478bd9Sstevel@tonic-gate /* 2424*7c478bd9Sstevel@tonic-gate * Since we have this device connected 2425*7c478bd9Sstevel@tonic-gate * mark it reinserted to prevent 2426*7c478bd9Sstevel@tonic-gate * cleanup thread from stepping in. 2427*7c478bd9Sstevel@tonic-gate */ 2428*7c478bd9Sstevel@tonic-gate DEVI_SET_DEVICE_REINSERTED(ch_dip); 2429*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 2430*7c478bd9Sstevel@tonic-gate 2431*7c478bd9Sstevel@tonic-gate /* 2432*7c478bd9Sstevel@tonic-gate * reopen pipes for children for 2433*7c478bd9Sstevel@tonic-gate * their DDI_RESUME 2434*7c478bd9Sstevel@tonic-gate */ 2435*7c478bd9Sstevel@tonic-gate rval = usba_persistent_pipe_open( 2436*7c478bd9Sstevel@tonic-gate usba_get_usba_device(ch_dip)); 2437*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 2438*7c478bd9Sstevel@tonic-gate ASSERT(rval == USB_SUCCESS); 2439*7c478bd9Sstevel@tonic-gate } 2440*7c478bd9Sstevel@tonic-gate } else { 2441*7c478bd9Sstevel@tonic-gate /* 2442*7c478bd9Sstevel@tonic-gate * Mark this dip for deletion as the device 2443*7c478bd9Sstevel@tonic-gate * is not physically present, and schedule 2444*7c478bd9Sstevel@tonic-gate * cleanup thread upon post resume 2445*7c478bd9Sstevel@tonic-gate */ 2446*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 2447*7c478bd9Sstevel@tonic-gate 2448*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, 2449*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 2450*7c478bd9Sstevel@tonic-gate "hubd_restore_device_state: " 2451*7c478bd9Sstevel@tonic-gate "dip=%p on port=%d marked for cleanup", 2452*7c478bd9Sstevel@tonic-gate ch_dip, port); 2453*7c478bd9Sstevel@tonic-gate DEVI_SET_DEVICE_REMOVED(ch_dip); 2454*7c478bd9Sstevel@tonic-gate 2455*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 2456*7c478bd9Sstevel@tonic-gate } 2457*7c478bd9Sstevel@tonic-gate } else if (ehci_root_hub) { 2458*7c478bd9Sstevel@tonic-gate /* get port status */ 2459*7c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 2460*7c478bd9Sstevel@tonic-gate &status, &change, PORT_CHANGE_CSC); 2461*7c478bd9Sstevel@tonic-gate 2462*7c478bd9Sstevel@tonic-gate /* check if it is truly connected */ 2463*7c478bd9Sstevel@tonic-gate if (status & PORT_STATUS_CCS) { 2464*7c478bd9Sstevel@tonic-gate /* 2465*7c478bd9Sstevel@tonic-gate * reset the port to find out if we have 2466*7c478bd9Sstevel@tonic-gate * 2.0 device connected or 1.X. A 2.0 2467*7c478bd9Sstevel@tonic-gate * device will still be seen as connected, 2468*7c478bd9Sstevel@tonic-gate * while a 1.X device will switch over to 2469*7c478bd9Sstevel@tonic-gate * the companion controller. 2470*7c478bd9Sstevel@tonic-gate */ 2471*7c478bd9Sstevel@tonic-gate (void) hubd_reset_port(hubd, port); 2472*7c478bd9Sstevel@tonic-gate 2473*7c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 2474*7c478bd9Sstevel@tonic-gate &status, &change, PORT_CHANGE_CSC); 2475*7c478bd9Sstevel@tonic-gate 2476*7c478bd9Sstevel@tonic-gate if (status & 2477*7c478bd9Sstevel@tonic-gate (PORT_STATUS_CCS | PORT_STATUS_HSDA)) { 2478*7c478bd9Sstevel@tonic-gate /* 2479*7c478bd9Sstevel@tonic-gate * We have a USB 2.0 device 2480*7c478bd9Sstevel@tonic-gate * connected. Power cycle this port 2481*7c478bd9Sstevel@tonic-gate * so that hotplug thread can 2482*7c478bd9Sstevel@tonic-gate * enumerate this device. 2483*7c478bd9Sstevel@tonic-gate */ 2484*7c478bd9Sstevel@tonic-gate (void) hubd_toggle_port(hubd, port); 2485*7c478bd9Sstevel@tonic-gate } else { 2486*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, 2487*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 2488*7c478bd9Sstevel@tonic-gate "hubd_restore_device_state: " 2489*7c478bd9Sstevel@tonic-gate "device on port %d switched over", 2490*7c478bd9Sstevel@tonic-gate port); 2491*7c478bd9Sstevel@tonic-gate } 2492*7c478bd9Sstevel@tonic-gate } 2493*7c478bd9Sstevel@tonic-gate 2494*7c478bd9Sstevel@tonic-gate } 2495*7c478bd9Sstevel@tonic-gate } 2496*7c478bd9Sstevel@tonic-gate 2497*7c478bd9Sstevel@tonic-gate 2498*7c478bd9Sstevel@tonic-gate /* if the device had remote wakeup earlier, enable it again */ 2499*7c478bd9Sstevel@tonic-gate if (hubd->h_hubpm->hubp_wakeup_enabled) { 2500*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 2501*7c478bd9Sstevel@tonic-gate (void) usb_handle_remote_wakeup(hubd->h_dip, 2502*7c478bd9Sstevel@tonic-gate USB_REMOTE_WAKEUP_ENABLE); 2503*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 2504*7c478bd9Sstevel@tonic-gate } 2505*7c478bd9Sstevel@tonic-gate 2506*7c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_ONLINE; 2507*7c478bd9Sstevel@tonic-gate hubd_start_polling(hubd, 0); 2508*7c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, dip, 0); 2509*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 2510*7c478bd9Sstevel@tonic-gate } 2511*7c478bd9Sstevel@tonic-gate 2512*7c478bd9Sstevel@tonic-gate 2513*7c478bd9Sstevel@tonic-gate /* 2514*7c478bd9Sstevel@tonic-gate * hubd_cleanup: 2515*7c478bd9Sstevel@tonic-gate * cleanup hubd and deallocate. this function is called for 2516*7c478bd9Sstevel@tonic-gate * handling attach failures and detaching including dynamic 2517*7c478bd9Sstevel@tonic-gate * reconfiguration. If called from attaching, it must clean 2518*7c478bd9Sstevel@tonic-gate * up the whole thing and return success. 2519*7c478bd9Sstevel@tonic-gate */ 2520*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 2521*7c478bd9Sstevel@tonic-gate static int 2522*7c478bd9Sstevel@tonic-gate hubd_cleanup(dev_info_t *dip, hubd_t *hubd) 2523*7c478bd9Sstevel@tonic-gate { 2524*7c478bd9Sstevel@tonic-gate int circ, rval, old_dev_state; 2525*7c478bd9Sstevel@tonic-gate hub_power_t *hubpm; 2526*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 2527*7c478bd9Sstevel@tonic-gate usb_port_t port; 2528*7c478bd9Sstevel@tonic-gate #endif 2529*7c478bd9Sstevel@tonic-gate 2530*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 2531*7c478bd9Sstevel@tonic-gate "hubd_cleanup:"); 2532*7c478bd9Sstevel@tonic-gate 2533*7c478bd9Sstevel@tonic-gate if ((hubd->h_init_state & HUBD_LOCKS_DONE) == 0) { 2534*7c478bd9Sstevel@tonic-gate goto done; 2535*7c478bd9Sstevel@tonic-gate } 2536*7c478bd9Sstevel@tonic-gate 2537*7c478bd9Sstevel@tonic-gate /* ensure we are the only one active */ 2538*7c478bd9Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 2539*7c478bd9Sstevel@tonic-gate 2540*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 2541*7c478bd9Sstevel@tonic-gate 2542*7c478bd9Sstevel@tonic-gate /* Cleanup failure is only allowed if called from detach */ 2543*7c478bd9Sstevel@tonic-gate if (DEVI_IS_DETACHING(dip)) { 2544*7c478bd9Sstevel@tonic-gate dev_info_t *rh_dip = hubd->h_usba_device->usb_root_hub_dip; 2545*7c478bd9Sstevel@tonic-gate 2546*7c478bd9Sstevel@tonic-gate /* 2547*7c478bd9Sstevel@tonic-gate * We are being called from detach. 2548*7c478bd9Sstevel@tonic-gate * Fail immediately if the hotplug thread is running 2549*7c478bd9Sstevel@tonic-gate * else set the dev_state to disconnected so that 2550*7c478bd9Sstevel@tonic-gate * hotplug thread just exits without doing anything. 2551*7c478bd9Sstevel@tonic-gate */ 2552*7c478bd9Sstevel@tonic-gate if (hubd->h_bus_ctls || hubd->h_bus_pwr || 2553*7c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread) { 2554*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 2555*7c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 2556*7c478bd9Sstevel@tonic-gate 2557*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 2558*7c478bd9Sstevel@tonic-gate "hubd_cleanup: hotplug thread/bus ctl active " 2559*7c478bd9Sstevel@tonic-gate "- failing detach"); 2560*7c478bd9Sstevel@tonic-gate 2561*7c478bd9Sstevel@tonic-gate return (USB_FAILURE); 2562*7c478bd9Sstevel@tonic-gate } 2563*7c478bd9Sstevel@tonic-gate 2564*7c478bd9Sstevel@tonic-gate /* 2565*7c478bd9Sstevel@tonic-gate * if the deathrow thread is still active or about 2566*7c478bd9Sstevel@tonic-gate * to become active, fail detach 2567*7c478bd9Sstevel@tonic-gate * the roothup can only be detached if nexus drivers 2568*7c478bd9Sstevel@tonic-gate * are unloaded or explicitly offlined 2569*7c478bd9Sstevel@tonic-gate */ 2570*7c478bd9Sstevel@tonic-gate if (rh_dip == dip) { 2571*7c478bd9Sstevel@tonic-gate if (hubd->h_cleanup_needed || 2572*7c478bd9Sstevel@tonic-gate hubd->h_cleanup_active) { 2573*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 2574*7c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 2575*7c478bd9Sstevel@tonic-gate 2576*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, 2577*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 2578*7c478bd9Sstevel@tonic-gate "hubd_cleanup: deathrow still active?" 2579*7c478bd9Sstevel@tonic-gate "- failing detach"); 2580*7c478bd9Sstevel@tonic-gate 2581*7c478bd9Sstevel@tonic-gate return (USB_FAILURE); 2582*7c478bd9Sstevel@tonic-gate } 2583*7c478bd9Sstevel@tonic-gate } 2584*7c478bd9Sstevel@tonic-gate } 2585*7c478bd9Sstevel@tonic-gate 2586*7c478bd9Sstevel@tonic-gate old_dev_state = hubd->h_dev_state; 2587*7c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_DISCONNECTED; 2588*7c478bd9Sstevel@tonic-gate 2589*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 2590*7c478bd9Sstevel@tonic-gate "hubd_cleanup: stop polling"); 2591*7c478bd9Sstevel@tonic-gate hubd_close_intr_pipe(hubd); 2592*7c478bd9Sstevel@tonic-gate 2593*7c478bd9Sstevel@tonic-gate ASSERT((hubd->h_bus_ctls || hubd->h_bus_pwr || 2594*7c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread) == 0); 2595*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 2596*7c478bd9Sstevel@tonic-gate 2597*7c478bd9Sstevel@tonic-gate /* 2598*7c478bd9Sstevel@tonic-gate * deallocate events, if events are still registered 2599*7c478bd9Sstevel@tonic-gate * (ie. children still attached) then we have to fail the detach 2600*7c478bd9Sstevel@tonic-gate */ 2601*7c478bd9Sstevel@tonic-gate if (hubd->h_ndi_event_hdl) { 2602*7c478bd9Sstevel@tonic-gate 2603*7c478bd9Sstevel@tonic-gate rval = ndi_event_free_hdl(hubd->h_ndi_event_hdl); 2604*7c478bd9Sstevel@tonic-gate if (DEVI_IS_ATTACHING(dip)) { 2605*7c478bd9Sstevel@tonic-gate 2606*7c478bd9Sstevel@tonic-gate /* It must return success if attaching. */ 2607*7c478bd9Sstevel@tonic-gate ASSERT(rval == NDI_SUCCESS); 2608*7c478bd9Sstevel@tonic-gate 2609*7c478bd9Sstevel@tonic-gate } else if (rval != NDI_SUCCESS) { 2610*7c478bd9Sstevel@tonic-gate 2611*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_ALL, hubd->h_log_handle, 2612*7c478bd9Sstevel@tonic-gate "hubd_cleanup: ndi_event_free_hdl failed"); 2613*7c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 2614*7c478bd9Sstevel@tonic-gate 2615*7c478bd9Sstevel@tonic-gate return (USB_FAILURE); 2616*7c478bd9Sstevel@tonic-gate 2617*7c478bd9Sstevel@tonic-gate } 2618*7c478bd9Sstevel@tonic-gate } 2619*7c478bd9Sstevel@tonic-gate 2620*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 2621*7c478bd9Sstevel@tonic-gate 2622*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 2623*7c478bd9Sstevel@tonic-gate for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { 2624*7c478bd9Sstevel@tonic-gate ASSERT(hubd->h_usba_devices[port] == NULL); 2625*7c478bd9Sstevel@tonic-gate ASSERT(hubd->h_children_dips[port] == NULL); 2626*7c478bd9Sstevel@tonic-gate } 2627*7c478bd9Sstevel@tonic-gate #endif 2628*7c478bd9Sstevel@tonic-gate kmem_free(hubd->h_children_dips, hubd->h_cd_list_length); 2629*7c478bd9Sstevel@tonic-gate kmem_free(hubd->h_usba_devices, hubd->h_cd_list_length); 2630*7c478bd9Sstevel@tonic-gate 2631*7c478bd9Sstevel@tonic-gate /* 2632*7c478bd9Sstevel@tonic-gate * Disable the event callbacks first, after this point, event 2633*7c478bd9Sstevel@tonic-gate * callbacks will never get called. Note we shouldn't hold 2634*7c478bd9Sstevel@tonic-gate * mutex while unregistering events because there may be a 2635*7c478bd9Sstevel@tonic-gate * competing event callback thread. Event callbacks are done 2636*7c478bd9Sstevel@tonic-gate * with ndi mutex held and this can cause a potential deadlock. 2637*7c478bd9Sstevel@tonic-gate * Note that cleanup can't fail after deregistration of events. 2638*7c478bd9Sstevel@tonic-gate */ 2639*7c478bd9Sstevel@tonic-gate if (hubd->h_init_state & HUBD_EVENTS_REGISTERED) { 2640*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 2641*7c478bd9Sstevel@tonic-gate usb_unregister_event_cbs(dip, &hubd_events); 2642*7c478bd9Sstevel@tonic-gate hubd_unregister_cpr_callback(hubd); 2643*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 2644*7c478bd9Sstevel@tonic-gate } 2645*7c478bd9Sstevel@tonic-gate 2646*7c478bd9Sstevel@tonic-gate /* restore the old dev state so that device can be put into low power */ 2647*7c478bd9Sstevel@tonic-gate hubd->h_dev_state = old_dev_state; 2648*7c478bd9Sstevel@tonic-gate hubpm = hubd->h_hubpm; 2649*7c478bd9Sstevel@tonic-gate 2650*7c478bd9Sstevel@tonic-gate if ((hubpm) && (hubd->h_dev_state != USB_DEV_DISCONNECTED)) { 2651*7c478bd9Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, dip, 0); 2652*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 2653*7c478bd9Sstevel@tonic-gate if (hubd->h_hubpm->hubp_wakeup_enabled) { 2654*7c478bd9Sstevel@tonic-gate /* 2655*7c478bd9Sstevel@tonic-gate * Bring the hub to full power before 2656*7c478bd9Sstevel@tonic-gate * issuing the disable remote wakeup command 2657*7c478bd9Sstevel@tonic-gate */ 2658*7c478bd9Sstevel@tonic-gate (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); 2659*7c478bd9Sstevel@tonic-gate 2660*7c478bd9Sstevel@tonic-gate if ((rval = usb_handle_remote_wakeup(hubd->h_dip, 2661*7c478bd9Sstevel@tonic-gate USB_REMOTE_WAKEUP_DISABLE)) != USB_SUCCESS) { 2662*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, 2663*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 2664*7c478bd9Sstevel@tonic-gate "hubd_cleanup: disable remote wakeup " 2665*7c478bd9Sstevel@tonic-gate "fails=%d", rval); 2666*7c478bd9Sstevel@tonic-gate } 2667*7c478bd9Sstevel@tonic-gate } 2668*7c478bd9Sstevel@tonic-gate 2669*7c478bd9Sstevel@tonic-gate (void) pm_lower_power(hubd->h_dip, 0, USB_DEV_OS_PWR_OFF); 2670*7c478bd9Sstevel@tonic-gate 2671*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 2672*7c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, dip, 0); 2673*7c478bd9Sstevel@tonic-gate } 2674*7c478bd9Sstevel@tonic-gate 2675*7c478bd9Sstevel@tonic-gate if (hubpm) { 2676*7c478bd9Sstevel@tonic-gate if (hubpm->hubp_child_pwrstate) { 2677*7c478bd9Sstevel@tonic-gate kmem_free(hubpm->hubp_child_pwrstate, 2678*7c478bd9Sstevel@tonic-gate MAX_PORTS + 1); 2679*7c478bd9Sstevel@tonic-gate } 2680*7c478bd9Sstevel@tonic-gate kmem_free(hubpm, sizeof (hub_power_t)); 2681*7c478bd9Sstevel@tonic-gate } 2682*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 2683*7c478bd9Sstevel@tonic-gate 2684*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 2685*7c478bd9Sstevel@tonic-gate "hubd_cleanup: freeing space"); 2686*7c478bd9Sstevel@tonic-gate 2687*7c478bd9Sstevel@tonic-gate if (hubd->h_init_state & HUBD_HUBDI_REGISTERED) { 2688*7c478bd9Sstevel@tonic-gate rval = usba_hubdi_unregister(dip); 2689*7c478bd9Sstevel@tonic-gate ASSERT(rval == USB_SUCCESS); 2690*7c478bd9Sstevel@tonic-gate } 2691*7c478bd9Sstevel@tonic-gate 2692*7c478bd9Sstevel@tonic-gate if (hubd->h_init_state & HUBD_LOCKS_DONE) { 2693*7c478bd9Sstevel@tonic-gate mutex_destroy(HUBD_MUTEX(hubd)); 2694*7c478bd9Sstevel@tonic-gate cv_destroy(&hubd->h_cv_reset_port); 2695*7c478bd9Sstevel@tonic-gate } 2696*7c478bd9Sstevel@tonic-gate 2697*7c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 2698*7c478bd9Sstevel@tonic-gate 2699*7c478bd9Sstevel@tonic-gate if (hubd->h_init_state & HUBD_MINOR_NODE_CREATED) { 2700*7c478bd9Sstevel@tonic-gate ddi_remove_minor_node(dip, NULL); 2701*7c478bd9Sstevel@tonic-gate } 2702*7c478bd9Sstevel@tonic-gate 2703*7c478bd9Sstevel@tonic-gate if (usba_is_root_hub(dip)) { 2704*7c478bd9Sstevel@tonic-gate usb_pipe_close(dip, hubd->h_default_pipe, 2705*7c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL); 2706*7c478bd9Sstevel@tonic-gate } 2707*7c478bd9Sstevel@tonic-gate 2708*7c478bd9Sstevel@tonic-gate done: 2709*7c478bd9Sstevel@tonic-gate if (hubd->h_ancestry_str) { 2710*7c478bd9Sstevel@tonic-gate kmem_free(hubd->h_ancestry_str, HUBD_APID_NAMELEN); 2711*7c478bd9Sstevel@tonic-gate } 2712*7c478bd9Sstevel@tonic-gate 2713*7c478bd9Sstevel@tonic-gate usb_client_detach(dip, hubd->h_dev_data); 2714*7c478bd9Sstevel@tonic-gate 2715*7c478bd9Sstevel@tonic-gate usb_free_log_hdl(hubd->h_log_handle); 2716*7c478bd9Sstevel@tonic-gate 2717*7c478bd9Sstevel@tonic-gate if (!usba_is_root_hub(dip)) { 2718*7c478bd9Sstevel@tonic-gate ddi_soft_state_free(hubd_statep, ddi_get_instance(dip)); 2719*7c478bd9Sstevel@tonic-gate } 2720*7c478bd9Sstevel@tonic-gate 2721*7c478bd9Sstevel@tonic-gate ddi_prop_remove_all(dip); 2722*7c478bd9Sstevel@tonic-gate 2723*7c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 2724*7c478bd9Sstevel@tonic-gate } 2725*7c478bd9Sstevel@tonic-gate 2726*7c478bd9Sstevel@tonic-gate 2727*7c478bd9Sstevel@tonic-gate /* 2728*7c478bd9Sstevel@tonic-gate * hubd_check_ports: 2729*7c478bd9Sstevel@tonic-gate * - get hub descriptor 2730*7c478bd9Sstevel@tonic-gate * - check initial port status 2731*7c478bd9Sstevel@tonic-gate * - enable power on all ports 2732*7c478bd9Sstevel@tonic-gate * - enable polling on ep1 2733*7c478bd9Sstevel@tonic-gate */ 2734*7c478bd9Sstevel@tonic-gate static int 2735*7c478bd9Sstevel@tonic-gate hubd_check_ports(hubd_t *hubd) 2736*7c478bd9Sstevel@tonic-gate { 2737*7c478bd9Sstevel@tonic-gate int rval; 2738*7c478bd9Sstevel@tonic-gate 2739*7c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 2740*7c478bd9Sstevel@tonic-gate 2741*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 2742*7c478bd9Sstevel@tonic-gate "hubd_check_ports: addr=0x%x", usb_get_addr(hubd->h_dip)); 2743*7c478bd9Sstevel@tonic-gate 2744*7c478bd9Sstevel@tonic-gate if ((rval = hubd_get_hub_descriptor(hubd)) != USB_SUCCESS) { 2745*7c478bd9Sstevel@tonic-gate 2746*7c478bd9Sstevel@tonic-gate return (rval); 2747*7c478bd9Sstevel@tonic-gate } 2748*7c478bd9Sstevel@tonic-gate 2749*7c478bd9Sstevel@tonic-gate /* 2750*7c478bd9Sstevel@tonic-gate * First turn off all port power 2751*7c478bd9Sstevel@tonic-gate */ 2752*7c478bd9Sstevel@tonic-gate if ((rval = hubd_disable_all_port_power(hubd)) != USB_SUCCESS) { 2753*7c478bd9Sstevel@tonic-gate 2754*7c478bd9Sstevel@tonic-gate /* disable whatever was enabled */ 2755*7c478bd9Sstevel@tonic-gate (void) hubd_disable_all_port_power(hubd); 2756*7c478bd9Sstevel@tonic-gate 2757*7c478bd9Sstevel@tonic-gate return (rval); 2758*7c478bd9Sstevel@tonic-gate } 2759*7c478bd9Sstevel@tonic-gate 2760*7c478bd9Sstevel@tonic-gate /* 2761*7c478bd9Sstevel@tonic-gate * do not switch on immediately (instantly on root hub) 2762*7c478bd9Sstevel@tonic-gate * and allow time to settle 2763*7c478bd9Sstevel@tonic-gate */ 2764*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 2765*7c478bd9Sstevel@tonic-gate delay(drv_usectohz(10000)); 2766*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 2767*7c478bd9Sstevel@tonic-gate 2768*7c478bd9Sstevel@tonic-gate /* 2769*7c478bd9Sstevel@tonic-gate * enable power on all ports so we can see connects 2770*7c478bd9Sstevel@tonic-gate */ 2771*7c478bd9Sstevel@tonic-gate if ((rval = hubd_enable_all_port_power(hubd)) != USB_SUCCESS) { 2772*7c478bd9Sstevel@tonic-gate /* disable whatever was enabled */ 2773*7c478bd9Sstevel@tonic-gate (void) hubd_disable_all_port_power(hubd); 2774*7c478bd9Sstevel@tonic-gate 2775*7c478bd9Sstevel@tonic-gate return (rval); 2776*7c478bd9Sstevel@tonic-gate } 2777*7c478bd9Sstevel@tonic-gate 2778*7c478bd9Sstevel@tonic-gate /* wait at least 3 frames before accessing devices */ 2779*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 2780*7c478bd9Sstevel@tonic-gate delay(drv_usectohz(10000)); 2781*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 2782*7c478bd9Sstevel@tonic-gate 2783*7c478bd9Sstevel@tonic-gate /* 2784*7c478bd9Sstevel@tonic-gate * allocate arrays for saving the dips of each child per port 2785*7c478bd9Sstevel@tonic-gate * 2786*7c478bd9Sstevel@tonic-gate * ports go from 1 - n, allocate 1 more entry 2787*7c478bd9Sstevel@tonic-gate */ 2788*7c478bd9Sstevel@tonic-gate hubd->h_cd_list_length = 2789*7c478bd9Sstevel@tonic-gate (sizeof (dev_info_t **)) * (hubd->h_hub_descr.bNbrPorts + 1); 2790*7c478bd9Sstevel@tonic-gate 2791*7c478bd9Sstevel@tonic-gate hubd->h_children_dips = (dev_info_t **)kmem_zalloc( 2792*7c478bd9Sstevel@tonic-gate hubd->h_cd_list_length, KM_SLEEP); 2793*7c478bd9Sstevel@tonic-gate hubd->h_usba_devices = (usba_device_t **)kmem_zalloc( 2794*7c478bd9Sstevel@tonic-gate hubd->h_cd_list_length, KM_SLEEP); 2795*7c478bd9Sstevel@tonic-gate 2796*7c478bd9Sstevel@tonic-gate if ((rval = hubd_open_intr_pipe(hubd)) == USB_SUCCESS) { 2797*7c478bd9Sstevel@tonic-gate hubd_start_polling(hubd, 0); 2798*7c478bd9Sstevel@tonic-gate } 2799*7c478bd9Sstevel@tonic-gate 2800*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 2801*7c478bd9Sstevel@tonic-gate "hubd_check_ports done"); 2802*7c478bd9Sstevel@tonic-gate 2803*7c478bd9Sstevel@tonic-gate return (rval); 2804*7c478bd9Sstevel@tonic-gate } 2805*7c478bd9Sstevel@tonic-gate 2806*7c478bd9Sstevel@tonic-gate 2807*7c478bd9Sstevel@tonic-gate /* 2808*7c478bd9Sstevel@tonic-gate * hubd_get_hub_descriptor: 2809*7c478bd9Sstevel@tonic-gate */ 2810*7c478bd9Sstevel@tonic-gate static int 2811*7c478bd9Sstevel@tonic-gate hubd_get_hub_descriptor(hubd_t *hubd) 2812*7c478bd9Sstevel@tonic-gate { 2813*7c478bd9Sstevel@tonic-gate usb_hub_descr_t *hub_descr = &hubd->h_hub_descr; 2814*7c478bd9Sstevel@tonic-gate mblk_t *data = NULL; 2815*7c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 2816*7c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 2817*7c478bd9Sstevel@tonic-gate uint16_t length; 2818*7c478bd9Sstevel@tonic-gate int rval; 2819*7c478bd9Sstevel@tonic-gate 2820*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 2821*7c478bd9Sstevel@tonic-gate "hubd_get_hub_descriptor:"); 2822*7c478bd9Sstevel@tonic-gate 2823*7c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 2824*7c478bd9Sstevel@tonic-gate ASSERT(hubd->h_default_pipe != 0); 2825*7c478bd9Sstevel@tonic-gate 2826*7c478bd9Sstevel@tonic-gate /* get hub descriptor length first by requesting 8 bytes only */ 2827*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 2828*7c478bd9Sstevel@tonic-gate 2829*7c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 2830*7c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 2831*7c478bd9Sstevel@tonic-gate HUB_CLASS_REQ, 2832*7c478bd9Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */ 2833*7c478bd9Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_HUB, /* wValue */ 2834*7c478bd9Sstevel@tonic-gate 0, /* wIndex */ 2835*7c478bd9Sstevel@tonic-gate 8, /* wLength */ 2836*7c478bd9Sstevel@tonic-gate &data, 0, 2837*7c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 2838*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 2839*7c478bd9Sstevel@tonic-gate "get hub descriptor failed: cr=%d cb_fl=0x%x rval=%d", 2840*7c478bd9Sstevel@tonic-gate completion_reason, cb_flags, rval); 2841*7c478bd9Sstevel@tonic-gate freemsg(data); 2842*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 2843*7c478bd9Sstevel@tonic-gate 2844*7c478bd9Sstevel@tonic-gate return (rval); 2845*7c478bd9Sstevel@tonic-gate } 2846*7c478bd9Sstevel@tonic-gate 2847*7c478bd9Sstevel@tonic-gate length = *(data->b_rptr); 2848*7c478bd9Sstevel@tonic-gate 2849*7c478bd9Sstevel@tonic-gate if (length > 8) { 2850*7c478bd9Sstevel@tonic-gate freemsg(data); 2851*7c478bd9Sstevel@tonic-gate data = NULL; 2852*7c478bd9Sstevel@tonic-gate 2853*7c478bd9Sstevel@tonic-gate /* get complete hub descriptor */ 2854*7c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 2855*7c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 2856*7c478bd9Sstevel@tonic-gate HUB_CLASS_REQ, 2857*7c478bd9Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */ 2858*7c478bd9Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_HUB, /* wValue */ 2859*7c478bd9Sstevel@tonic-gate 0, /* wIndex */ 2860*7c478bd9Sstevel@tonic-gate length, /* wLength */ 2861*7c478bd9Sstevel@tonic-gate &data, 0, 2862*7c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 2863*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 2864*7c478bd9Sstevel@tonic-gate "get hub descriptor failed: " 2865*7c478bd9Sstevel@tonic-gate "cr=%d cb_fl=0x%x rval=%d", 2866*7c478bd9Sstevel@tonic-gate completion_reason, cb_flags, rval); 2867*7c478bd9Sstevel@tonic-gate freemsg(data); 2868*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 2869*7c478bd9Sstevel@tonic-gate 2870*7c478bd9Sstevel@tonic-gate return (rval); 2871*7c478bd9Sstevel@tonic-gate } 2872*7c478bd9Sstevel@tonic-gate } 2873*7c478bd9Sstevel@tonic-gate 2874*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 2875*7c478bd9Sstevel@tonic-gate 2876*7c478bd9Sstevel@tonic-gate /* parse the hub descriptor */ 2877*7c478bd9Sstevel@tonic-gate /* only 32 ports are supported at present */ 2878*7c478bd9Sstevel@tonic-gate ASSERT(*(data->b_rptr + 2) <= 32); 2879*7c478bd9Sstevel@tonic-gate if (usb_parse_CV_descr("cccscccccc", 2880*7c478bd9Sstevel@tonic-gate data->b_rptr, data->b_wptr - data->b_rptr, 2881*7c478bd9Sstevel@tonic-gate (void *)hub_descr, sizeof (usb_hub_descr_t)) == 0) { 2882*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 2883*7c478bd9Sstevel@tonic-gate "parsing hub descriptor failed"); 2884*7c478bd9Sstevel@tonic-gate 2885*7c478bd9Sstevel@tonic-gate freemsg(data); 2886*7c478bd9Sstevel@tonic-gate 2887*7c478bd9Sstevel@tonic-gate return (USB_FAILURE); 2888*7c478bd9Sstevel@tonic-gate } 2889*7c478bd9Sstevel@tonic-gate 2890*7c478bd9Sstevel@tonic-gate freemsg(data); 2891*7c478bd9Sstevel@tonic-gate 2892*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 2893*7c478bd9Sstevel@tonic-gate "rval = 0x%x bNbrPorts = 0x%x wHubChars = 0x%x " 2894*7c478bd9Sstevel@tonic-gate "PwrOn2PwrGood = 0x%x", rval, 2895*7c478bd9Sstevel@tonic-gate hub_descr->bNbrPorts, hub_descr->wHubCharacteristics, 2896*7c478bd9Sstevel@tonic-gate hub_descr->bPwrOn2PwrGood); 2897*7c478bd9Sstevel@tonic-gate 2898*7c478bd9Sstevel@tonic-gate if (hub_descr->bNbrPorts > MAX_PORTS) { 2899*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L0(DPRINT_MASK_ATTA, hubd->h_log_handle, 2900*7c478bd9Sstevel@tonic-gate "Hub driver supports max of %d ports on hub. " 2901*7c478bd9Sstevel@tonic-gate "Hence using the first %d port of %d ports available", 2902*7c478bd9Sstevel@tonic-gate MAX_PORTS, MAX_PORTS, hub_descr->bNbrPorts); 2903*7c478bd9Sstevel@tonic-gate 2904*7c478bd9Sstevel@tonic-gate hub_descr->bNbrPorts = MAX_PORTS; 2905*7c478bd9Sstevel@tonic-gate } 2906*7c478bd9Sstevel@tonic-gate 2907*7c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 2908*7c478bd9Sstevel@tonic-gate } 2909*7c478bd9Sstevel@tonic-gate 2910*7c478bd9Sstevel@tonic-gate 2911*7c478bd9Sstevel@tonic-gate /* 2912*7c478bd9Sstevel@tonic-gate * hubd_open_intr_pipe: 2913*7c478bd9Sstevel@tonic-gate * we read all descriptors first for curiosity and then simply 2914*7c478bd9Sstevel@tonic-gate * open the pipe 2915*7c478bd9Sstevel@tonic-gate */ 2916*7c478bd9Sstevel@tonic-gate static int 2917*7c478bd9Sstevel@tonic-gate hubd_open_intr_pipe(hubd_t *hubd) 2918*7c478bd9Sstevel@tonic-gate { 2919*7c478bd9Sstevel@tonic-gate int rval; 2920*7c478bd9Sstevel@tonic-gate 2921*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 2922*7c478bd9Sstevel@tonic-gate "hubd_open_intr_pipe:"); 2923*7c478bd9Sstevel@tonic-gate 2924*7c478bd9Sstevel@tonic-gate ASSERT(hubd->h_intr_pipe_state == HUBD_INTR_PIPE_IDLE); 2925*7c478bd9Sstevel@tonic-gate 2926*7c478bd9Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_OPENING; 2927*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 2928*7c478bd9Sstevel@tonic-gate 2929*7c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_open(hubd->h_dip, 2930*7c478bd9Sstevel@tonic-gate &hubd->h_ep1_descr, &hubd->h_pipe_policy, 2931*7c478bd9Sstevel@tonic-gate 0, &hubd->h_ep1_ph)) != USB_SUCCESS) { 2932*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_HUB, hubd->h_log_handle, 2933*7c478bd9Sstevel@tonic-gate "open intr pipe failed (%d)", rval); 2934*7c478bd9Sstevel@tonic-gate 2935*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 2936*7c478bd9Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_IDLE; 2937*7c478bd9Sstevel@tonic-gate 2938*7c478bd9Sstevel@tonic-gate return (rval); 2939*7c478bd9Sstevel@tonic-gate } 2940*7c478bd9Sstevel@tonic-gate 2941*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 2942*7c478bd9Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_ACTIVE; 2943*7c478bd9Sstevel@tonic-gate 2944*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 2945*7c478bd9Sstevel@tonic-gate "open intr pipe succeeded, ph=0x%p", hubd->h_ep1_ph); 2946*7c478bd9Sstevel@tonic-gate 2947*7c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 2948*7c478bd9Sstevel@tonic-gate } 2949*7c478bd9Sstevel@tonic-gate 2950*7c478bd9Sstevel@tonic-gate 2951*7c478bd9Sstevel@tonic-gate /* 2952*7c478bd9Sstevel@tonic-gate * hubd_start_polling: 2953*7c478bd9Sstevel@tonic-gate * start or restart the polling 2954*7c478bd9Sstevel@tonic-gate */ 2955*7c478bd9Sstevel@tonic-gate static void 2956*7c478bd9Sstevel@tonic-gate hubd_start_polling(hubd_t *hubd, int always) 2957*7c478bd9Sstevel@tonic-gate { 2958*7c478bd9Sstevel@tonic-gate usb_intr_req_t *reqp; 2959*7c478bd9Sstevel@tonic-gate int rval; 2960*7c478bd9Sstevel@tonic-gate usb_pipe_state_t pipe_state; 2961*7c478bd9Sstevel@tonic-gate 2962*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 2963*7c478bd9Sstevel@tonic-gate "start polling: always=%d dev_state=%d pipe_state=%d\n\t" 2964*7c478bd9Sstevel@tonic-gate "thread=%d ep1_ph=0x%p", 2965*7c478bd9Sstevel@tonic-gate always, hubd->h_dev_state, hubd->h_intr_pipe_state, 2966*7c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread, hubd->h_ep1_ph); 2967*7c478bd9Sstevel@tonic-gate 2968*7c478bd9Sstevel@tonic-gate /* 2969*7c478bd9Sstevel@tonic-gate * start or restart polling on the intr pipe 2970*7c478bd9Sstevel@tonic-gate * only if hotplug thread is not running 2971*7c478bd9Sstevel@tonic-gate */ 2972*7c478bd9Sstevel@tonic-gate if ((always == HUBD_ALWAYS_START_POLLING) || 2973*7c478bd9Sstevel@tonic-gate ((hubd->h_dev_state == USB_DEV_ONLINE) && 2974*7c478bd9Sstevel@tonic-gate (hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE) && 2975*7c478bd9Sstevel@tonic-gate (hubd->h_hotplug_thread == 0) && hubd->h_ep1_ph)) { 2976*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 2977*7c478bd9Sstevel@tonic-gate "start polling requested"); 2978*7c478bd9Sstevel@tonic-gate 2979*7c478bd9Sstevel@tonic-gate reqp = usb_alloc_intr_req(hubd->h_dip, 0, USB_FLAGS_SLEEP); 2980*7c478bd9Sstevel@tonic-gate 2981*7c478bd9Sstevel@tonic-gate reqp->intr_client_private = (usb_opaque_t)hubd; 2982*7c478bd9Sstevel@tonic-gate reqp->intr_attributes = USB_ATTRS_SHORT_XFER_OK | 2983*7c478bd9Sstevel@tonic-gate USB_ATTRS_AUTOCLEARING; 2984*7c478bd9Sstevel@tonic-gate reqp->intr_len = hubd->h_ep1_descr.wMaxPacketSize; 2985*7c478bd9Sstevel@tonic-gate reqp->intr_cb = hubd_read_cb; 2986*7c478bd9Sstevel@tonic-gate reqp->intr_exc_cb = hubd_exception_cb; 2987*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 2988*7c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_intr_xfer(hubd->h_ep1_ph, reqp, 2989*7c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP)) != USB_SUCCESS) { 2990*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle, 2991*7c478bd9Sstevel@tonic-gate "start polling failed, rval=%d", rval); 2992*7c478bd9Sstevel@tonic-gate usb_free_intr_req(reqp); 2993*7c478bd9Sstevel@tonic-gate } 2994*7c478bd9Sstevel@tonic-gate 2995*7c478bd9Sstevel@tonic-gate rval = usb_pipe_get_state(hubd->h_ep1_ph, &pipe_state, 2996*7c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP); 2997*7c478bd9Sstevel@tonic-gate if (pipe_state != USB_PIPE_STATE_ACTIVE) { 2998*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 2999*7c478bd9Sstevel@tonic-gate "intr pipe state=%d, rval=%d", pipe_state, rval); 3000*7c478bd9Sstevel@tonic-gate } 3001*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 3002*7c478bd9Sstevel@tonic-gate "start polling request 0x%p", reqp); 3003*7c478bd9Sstevel@tonic-gate 3004*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 3005*7c478bd9Sstevel@tonic-gate } 3006*7c478bd9Sstevel@tonic-gate } 3007*7c478bd9Sstevel@tonic-gate 3008*7c478bd9Sstevel@tonic-gate 3009*7c478bd9Sstevel@tonic-gate /* 3010*7c478bd9Sstevel@tonic-gate * hubd_stop_polling 3011*7c478bd9Sstevel@tonic-gate * stop polling but do not close the pipe 3012*7c478bd9Sstevel@tonic-gate */ 3013*7c478bd9Sstevel@tonic-gate static void 3014*7c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd_t *hubd) 3015*7c478bd9Sstevel@tonic-gate { 3016*7c478bd9Sstevel@tonic-gate int rval; 3017*7c478bd9Sstevel@tonic-gate usb_pipe_state_t pipe_state; 3018*7c478bd9Sstevel@tonic-gate 3019*7c478bd9Sstevel@tonic-gate if (hubd->h_ep1_ph) { 3020*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 3021*7c478bd9Sstevel@tonic-gate "hubd_stop_polling:"); 3022*7c478bd9Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_STOPPED; 3023*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 3024*7c478bd9Sstevel@tonic-gate 3025*7c478bd9Sstevel@tonic-gate usb_pipe_stop_intr_polling(hubd->h_ep1_ph, USB_FLAGS_SLEEP); 3026*7c478bd9Sstevel@tonic-gate rval = usb_pipe_get_state(hubd->h_ep1_ph, &pipe_state, 3027*7c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP); 3028*7c478bd9Sstevel@tonic-gate 3029*7c478bd9Sstevel@tonic-gate if (pipe_state != USB_PIPE_STATE_IDLE) { 3030*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 3031*7c478bd9Sstevel@tonic-gate "intr pipe state=%d, rval=%d", pipe_state, rval); 3032*7c478bd9Sstevel@tonic-gate } 3033*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 3034*7c478bd9Sstevel@tonic-gate if (hubd->h_intr_pipe_state == HUBD_INTR_PIPE_STOPPED) { 3035*7c478bd9Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_ACTIVE; 3036*7c478bd9Sstevel@tonic-gate } 3037*7c478bd9Sstevel@tonic-gate } 3038*7c478bd9Sstevel@tonic-gate } 3039*7c478bd9Sstevel@tonic-gate 3040*7c478bd9Sstevel@tonic-gate 3041*7c478bd9Sstevel@tonic-gate /* 3042*7c478bd9Sstevel@tonic-gate * hubd_close_intr_pipe: 3043*7c478bd9Sstevel@tonic-gate * close the pipe (which also stops the polling 3044*7c478bd9Sstevel@tonic-gate * and wait for the hotplug thread to exit 3045*7c478bd9Sstevel@tonic-gate */ 3046*7c478bd9Sstevel@tonic-gate static void 3047*7c478bd9Sstevel@tonic-gate hubd_close_intr_pipe(hubd_t *hubd) 3048*7c478bd9Sstevel@tonic-gate { 3049*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 3050*7c478bd9Sstevel@tonic-gate "hubd_close_intr_pipe:"); 3051*7c478bd9Sstevel@tonic-gate 3052*7c478bd9Sstevel@tonic-gate /* 3053*7c478bd9Sstevel@tonic-gate * Now that no async operation is outstanding on pipe, 3054*7c478bd9Sstevel@tonic-gate * we can change the state to HUBD_INTR_PIPE_CLOSING 3055*7c478bd9Sstevel@tonic-gate */ 3056*7c478bd9Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_CLOSING; 3057*7c478bd9Sstevel@tonic-gate 3058*7c478bd9Sstevel@tonic-gate ASSERT(hubd->h_hotplug_thread == 0); 3059*7c478bd9Sstevel@tonic-gate 3060*7c478bd9Sstevel@tonic-gate if (hubd->h_ep1_ph) { 3061*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 3062*7c478bd9Sstevel@tonic-gate usb_pipe_close(hubd->h_dip, hubd->h_ep1_ph, USB_FLAGS_SLEEP, 3063*7c478bd9Sstevel@tonic-gate NULL, NULL); 3064*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 3065*7c478bd9Sstevel@tonic-gate hubd->h_ep1_ph = NULL; 3066*7c478bd9Sstevel@tonic-gate } 3067*7c478bd9Sstevel@tonic-gate 3068*7c478bd9Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_IDLE; 3069*7c478bd9Sstevel@tonic-gate } 3070*7c478bd9Sstevel@tonic-gate 3071*7c478bd9Sstevel@tonic-gate 3072*7c478bd9Sstevel@tonic-gate /* 3073*7c478bd9Sstevel@tonic-gate * hubd_exception_cb 3074*7c478bd9Sstevel@tonic-gate * interrupt ep1 exception callback function. 3075*7c478bd9Sstevel@tonic-gate * this callback executes in taskq thread context and assumes 3076*7c478bd9Sstevel@tonic-gate * autoclearing 3077*7c478bd9Sstevel@tonic-gate */ 3078*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 3079*7c478bd9Sstevel@tonic-gate static void 3080*7c478bd9Sstevel@tonic-gate hubd_exception_cb(usb_pipe_handle_t pipe, usb_intr_req_t *reqp) 3081*7c478bd9Sstevel@tonic-gate { 3082*7c478bd9Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)(reqp->intr_client_private); 3083*7c478bd9Sstevel@tonic-gate 3084*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CALLBACK, hubd->h_log_handle, 3085*7c478bd9Sstevel@tonic-gate "hubd_exception_cb: " 3086*7c478bd9Sstevel@tonic-gate "req=0x%p cr=%d data=0x%p cb_flags=0x%x", reqp, 3087*7c478bd9Sstevel@tonic-gate reqp->intr_completion_reason, reqp->intr_data, 3088*7c478bd9Sstevel@tonic-gate reqp->intr_cb_flags); 3089*7c478bd9Sstevel@tonic-gate 3090*7c478bd9Sstevel@tonic-gate ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0); 3091*7c478bd9Sstevel@tonic-gate 3092*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 3093*7c478bd9Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0); 3094*7c478bd9Sstevel@tonic-gate 3095*7c478bd9Sstevel@tonic-gate switch (reqp->intr_completion_reason) { 3096*7c478bd9Sstevel@tonic-gate case USB_CR_PIPE_RESET: 3097*7c478bd9Sstevel@tonic-gate /* only restart polling after autoclearing */ 3098*7c478bd9Sstevel@tonic-gate if ((hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE) && 3099*7c478bd9Sstevel@tonic-gate (hubd->h_port_reset_wait == 0)) { 3100*7c478bd9Sstevel@tonic-gate hubd_start_polling(hubd, 0); 3101*7c478bd9Sstevel@tonic-gate } 3102*7c478bd9Sstevel@tonic-gate 3103*7c478bd9Sstevel@tonic-gate break; 3104*7c478bd9Sstevel@tonic-gate case USB_CR_DEV_NOT_RESP: 3105*7c478bd9Sstevel@tonic-gate case USB_CR_STOPPED_POLLING: 3106*7c478bd9Sstevel@tonic-gate case USB_CR_PIPE_CLOSING: 3107*7c478bd9Sstevel@tonic-gate case USB_CR_UNSPECIFIED_ERR: 3108*7c478bd9Sstevel@tonic-gate /* never restart polling on these conditions */ 3109*7c478bd9Sstevel@tonic-gate default: 3110*7c478bd9Sstevel@tonic-gate /* for all others, wait for the autoclearing PIPE_RESET cb */ 3111*7c478bd9Sstevel@tonic-gate 3112*7c478bd9Sstevel@tonic-gate break; 3113*7c478bd9Sstevel@tonic-gate } 3114*7c478bd9Sstevel@tonic-gate 3115*7c478bd9Sstevel@tonic-gate usb_free_intr_req(reqp); 3116*7c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0); 3117*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 3118*7c478bd9Sstevel@tonic-gate } 3119*7c478bd9Sstevel@tonic-gate 3120*7c478bd9Sstevel@tonic-gate 3121*7c478bd9Sstevel@tonic-gate /* 3122*7c478bd9Sstevel@tonic-gate * helper function to convert LE bytes to a portmask 3123*7c478bd9Sstevel@tonic-gate */ 3124*7c478bd9Sstevel@tonic-gate static usb_port_mask_t 3125*7c478bd9Sstevel@tonic-gate hubd_mblk2portmask(mblk_t *data) 3126*7c478bd9Sstevel@tonic-gate { 3127*7c478bd9Sstevel@tonic-gate int len = min(data->b_wptr - data->b_rptr, sizeof (usb_port_mask_t)); 3128*7c478bd9Sstevel@tonic-gate usb_port_mask_t rval = 0; 3129*7c478bd9Sstevel@tonic-gate int i; 3130*7c478bd9Sstevel@tonic-gate 3131*7c478bd9Sstevel@tonic-gate for (i = 0; i < len; i++) { 3132*7c478bd9Sstevel@tonic-gate rval |= data->b_rptr[i] << (i * 8); 3133*7c478bd9Sstevel@tonic-gate } 3134*7c478bd9Sstevel@tonic-gate 3135*7c478bd9Sstevel@tonic-gate return (rval); 3136*7c478bd9Sstevel@tonic-gate } 3137*7c478bd9Sstevel@tonic-gate 3138*7c478bd9Sstevel@tonic-gate 3139*7c478bd9Sstevel@tonic-gate /* 3140*7c478bd9Sstevel@tonic-gate * hubd_read_cb: 3141*7c478bd9Sstevel@tonic-gate * interrupt ep1 callback function 3142*7c478bd9Sstevel@tonic-gate * 3143*7c478bd9Sstevel@tonic-gate * the status indicates just a change on the pipe with no indication 3144*7c478bd9Sstevel@tonic-gate * of what the change was 3145*7c478bd9Sstevel@tonic-gate * 3146*7c478bd9Sstevel@tonic-gate * known conditions: 3147*7c478bd9Sstevel@tonic-gate * - reset port completion 3148*7c478bd9Sstevel@tonic-gate * - connect 3149*7c478bd9Sstevel@tonic-gate * - disconnect 3150*7c478bd9Sstevel@tonic-gate * 3151*7c478bd9Sstevel@tonic-gate * for handling the hotplugging, create a new thread that can do 3152*7c478bd9Sstevel@tonic-gate * synchronous usba calls 3153*7c478bd9Sstevel@tonic-gate */ 3154*7c478bd9Sstevel@tonic-gate static void 3155*7c478bd9Sstevel@tonic-gate hubd_read_cb(usb_pipe_handle_t pipe, usb_intr_req_t *reqp) 3156*7c478bd9Sstevel@tonic-gate { 3157*7c478bd9Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)(reqp->intr_client_private); 3158*7c478bd9Sstevel@tonic-gate size_t length; 3159*7c478bd9Sstevel@tonic-gate mblk_t *data = reqp->intr_data; 3160*7c478bd9Sstevel@tonic-gate 3161*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 3162*7c478bd9Sstevel@tonic-gate "hubd_read_cb: ph=0x%p req=0x%p", pipe, reqp); 3163*7c478bd9Sstevel@tonic-gate 3164*7c478bd9Sstevel@tonic-gate ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0); 3165*7c478bd9Sstevel@tonic-gate 3166*7c478bd9Sstevel@tonic-gate /* 3167*7c478bd9Sstevel@tonic-gate * At present, we are not handling notification for completion of 3168*7c478bd9Sstevel@tonic-gate * asynchronous pipe reset, for which this data ptr could be NULL 3169*7c478bd9Sstevel@tonic-gate */ 3170*7c478bd9Sstevel@tonic-gate 3171*7c478bd9Sstevel@tonic-gate if (data == NULL) { 3172*7c478bd9Sstevel@tonic-gate usb_free_intr_req(reqp); 3173*7c478bd9Sstevel@tonic-gate 3174*7c478bd9Sstevel@tonic-gate return; 3175*7c478bd9Sstevel@tonic-gate } 3176*7c478bd9Sstevel@tonic-gate 3177*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 3178*7c478bd9Sstevel@tonic-gate 3179*7c478bd9Sstevel@tonic-gate if ((hubd->h_dev_state == USB_DEV_SUSPENDED) || 3180*7c478bd9Sstevel@tonic-gate (hubd->h_intr_pipe_state != HUBD_INTR_PIPE_ACTIVE)) { 3181*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 3182*7c478bd9Sstevel@tonic-gate usb_free_intr_req(reqp); 3183*7c478bd9Sstevel@tonic-gate 3184*7c478bd9Sstevel@tonic-gate return; 3185*7c478bd9Sstevel@tonic-gate } 3186*7c478bd9Sstevel@tonic-gate 3187*7c478bd9Sstevel@tonic-gate ASSERT(hubd->h_ep1_ph == pipe); 3188*7c478bd9Sstevel@tonic-gate 3189*7c478bd9Sstevel@tonic-gate length = data->b_wptr - data->b_rptr; 3190*7c478bd9Sstevel@tonic-gate 3191*7c478bd9Sstevel@tonic-gate /* 3192*7c478bd9Sstevel@tonic-gate * Only look at the data and startup the hotplug thread if 3193*7c478bd9Sstevel@tonic-gate * there actually is data. 3194*7c478bd9Sstevel@tonic-gate */ 3195*7c478bd9Sstevel@tonic-gate if (length != 0) { 3196*7c478bd9Sstevel@tonic-gate usb_port_mask_t port_change = hubd_mblk2portmask(data); 3197*7c478bd9Sstevel@tonic-gate 3198*7c478bd9Sstevel@tonic-gate /* 3199*7c478bd9Sstevel@tonic-gate * if a port change was already reported and we are waiting for 3200*7c478bd9Sstevel@tonic-gate * reset port completion then wake up the hotplug thread which 3201*7c478bd9Sstevel@tonic-gate * should be waiting on reset port completion 3202*7c478bd9Sstevel@tonic-gate * 3203*7c478bd9Sstevel@tonic-gate * if there is disconnect event instead of reset completion, let 3204*7c478bd9Sstevel@tonic-gate * the hotplug thread figure this out 3205*7c478bd9Sstevel@tonic-gate */ 3206*7c478bd9Sstevel@tonic-gate 3207*7c478bd9Sstevel@tonic-gate /* remove the reset wait bits from the status */ 3208*7c478bd9Sstevel@tonic-gate hubd->h_port_change |= port_change & 3209*7c478bd9Sstevel@tonic-gate ~hubd->h_port_reset_wait; 3210*7c478bd9Sstevel@tonic-gate 3211*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_CALLBACK, hubd->h_log_handle, 3212*7c478bd9Sstevel@tonic-gate "port change=0x%x port_reset_wait=0x%x", 3213*7c478bd9Sstevel@tonic-gate hubd->h_port_change, hubd->h_port_reset_wait); 3214*7c478bd9Sstevel@tonic-gate 3215*7c478bd9Sstevel@tonic-gate /* there should be only one reset bit active at the time */ 3216*7c478bd9Sstevel@tonic-gate if (hubd->h_port_reset_wait & port_change) { 3217*7c478bd9Sstevel@tonic-gate hubd->h_port_reset_wait = 0; 3218*7c478bd9Sstevel@tonic-gate cv_signal(&hubd->h_cv_reset_port); 3219*7c478bd9Sstevel@tonic-gate } 3220*7c478bd9Sstevel@tonic-gate 3221*7c478bd9Sstevel@tonic-gate /* 3222*7c478bd9Sstevel@tonic-gate * kick off the thread only if device is ONLINE and it is not 3223*7c478bd9Sstevel@tonic-gate * during attaching or detaching 3224*7c478bd9Sstevel@tonic-gate */ 3225*7c478bd9Sstevel@tonic-gate if ((hubd->h_dev_state == USB_DEV_ONLINE) && 3226*7c478bd9Sstevel@tonic-gate (!DEVI_IS_ATTACHING(hubd->h_dip)) && 3227*7c478bd9Sstevel@tonic-gate (!DEVI_IS_DETACHING(hubd->h_dip)) && 3228*7c478bd9Sstevel@tonic-gate (hubd->h_port_change) && 3229*7c478bd9Sstevel@tonic-gate (hubd->h_hotplug_thread == 0)) { 3230*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_CALLBACK, hubd->h_log_handle, 3231*7c478bd9Sstevel@tonic-gate "creating hotplug thread: " 3232*7c478bd9Sstevel@tonic-gate "dev_state=%d", hubd->h_dev_state); 3233*7c478bd9Sstevel@tonic-gate 3234*7c478bd9Sstevel@tonic-gate /* 3235*7c478bd9Sstevel@tonic-gate * Mark this device as busy. The will be marked idle 3236*7c478bd9Sstevel@tonic-gate * if the async req fails or at the exit of hotplug 3237*7c478bd9Sstevel@tonic-gate * thread 3238*7c478bd9Sstevel@tonic-gate */ 3239*7c478bd9Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0); 3240*7c478bd9Sstevel@tonic-gate 3241*7c478bd9Sstevel@tonic-gate if (usb_async_req(hubd->h_dip, 3242*7c478bd9Sstevel@tonic-gate hubd_hotplug_thread, 3243*7c478bd9Sstevel@tonic-gate (void *)hubd, 0) == USB_SUCCESS) { 3244*7c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread++; 3245*7c478bd9Sstevel@tonic-gate } else { 3246*7c478bd9Sstevel@tonic-gate /* mark this device as idle */ 3247*7c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, 3248*7c478bd9Sstevel@tonic-gate hubd->h_dip, 0); 3249*7c478bd9Sstevel@tonic-gate } 3250*7c478bd9Sstevel@tonic-gate } 3251*7c478bd9Sstevel@tonic-gate } 3252*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 3253*7c478bd9Sstevel@tonic-gate 3254*7c478bd9Sstevel@tonic-gate usb_free_intr_req(reqp); 3255*7c478bd9Sstevel@tonic-gate } 3256*7c478bd9Sstevel@tonic-gate 3257*7c478bd9Sstevel@tonic-gate 3258*7c478bd9Sstevel@tonic-gate /* 3259*7c478bd9Sstevel@tonic-gate * hubd_hotplug_thread: 3260*7c478bd9Sstevel@tonic-gate * handles resetting of port, and creating children 3261*7c478bd9Sstevel@tonic-gate * 3262*7c478bd9Sstevel@tonic-gate * the ports to check are indicated in h_port_change bit mask 3263*7c478bd9Sstevel@tonic-gate * XXX note that one time poll doesn't work on the root hub 3264*7c478bd9Sstevel@tonic-gate */ 3265*7c478bd9Sstevel@tonic-gate static void 3266*7c478bd9Sstevel@tonic-gate hubd_hotplug_thread(void *arg) 3267*7c478bd9Sstevel@tonic-gate { 3268*7c478bd9Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)arg; 3269*7c478bd9Sstevel@tonic-gate usb_port_t port; 3270*7c478bd9Sstevel@tonic-gate uint16_t nports; 3271*7c478bd9Sstevel@tonic-gate uint16_t status, change; 3272*7c478bd9Sstevel@tonic-gate hub_power_t *hubpm; 3273*7c478bd9Sstevel@tonic-gate dev_info_t *hdip = hubd->h_dip; 3274*7c478bd9Sstevel@tonic-gate dev_info_t *rh_dip = hubd->h_usba_device->usb_root_hub_dip; 3275*7c478bd9Sstevel@tonic-gate boolean_t online_child = B_FALSE; 3276*7c478bd9Sstevel@tonic-gate boolean_t offline_child = B_FALSE; 3277*7c478bd9Sstevel@tonic-gate boolean_t pwrup_child = B_FALSE; 3278*7c478bd9Sstevel@tonic-gate int prh_circ, rh_circ, circ, old_state; 3279*7c478bd9Sstevel@tonic-gate 3280*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 3281*7c478bd9Sstevel@tonic-gate "hubd_hotplug_thread: started"); 3282*7c478bd9Sstevel@tonic-gate 3283*7c478bd9Sstevel@tonic-gate /* 3284*7c478bd9Sstevel@tonic-gate * if our bus power entry point is active, process the change 3285*7c478bd9Sstevel@tonic-gate * on the next notification of interrupt pipe 3286*7c478bd9Sstevel@tonic-gate */ 3287*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 3288*7c478bd9Sstevel@tonic-gate if (hubd->h_bus_pwr || (hubd->h_hotplug_thread > 1)) { 3289*7c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread--; 3290*7c478bd9Sstevel@tonic-gate 3291*7c478bd9Sstevel@tonic-gate /* mark this device as idle */ 3292*7c478bd9Sstevel@tonic-gate hubd_pm_idle_component(hubd, hubd->h_dip, 0); 3293*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 3294*7c478bd9Sstevel@tonic-gate 3295*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 3296*7c478bd9Sstevel@tonic-gate "hubd_hotplug_thread: " 3297*7c478bd9Sstevel@tonic-gate "bus_power in progress/hotplugging undesirable - quit"); 3298*7c478bd9Sstevel@tonic-gate 3299*7c478bd9Sstevel@tonic-gate return; 3300*7c478bd9Sstevel@tonic-gate } 3301*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 3302*7c478bd9Sstevel@tonic-gate 3303*7c478bd9Sstevel@tonic-gate ndi_hold_devi(hdip); /* so we don't race with detach */ 3304*7c478bd9Sstevel@tonic-gate 3305*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 3306*7c478bd9Sstevel@tonic-gate 3307*7c478bd9Sstevel@tonic-gate /* is this the root hub? */ 3308*7c478bd9Sstevel@tonic-gate if (hdip == rh_dip) { 3309*7c478bd9Sstevel@tonic-gate if (hubd->h_dev_state == USB_DEV_PWRED_DOWN) { 3310*7c478bd9Sstevel@tonic-gate hubpm = hubd->h_hubpm; 3311*7c478bd9Sstevel@tonic-gate 3312*7c478bd9Sstevel@tonic-gate /* mark the root hub as full power */ 3313*7c478bd9Sstevel@tonic-gate hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR; 3314*7c478bd9Sstevel@tonic-gate hubpm->hubp_time_at_full_power = ddi_get_time(); 3315*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 3316*7c478bd9Sstevel@tonic-gate 3317*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 3318*7c478bd9Sstevel@tonic-gate "hubd_hotplug_thread: call pm_power_has_changed"); 3319*7c478bd9Sstevel@tonic-gate 3320*7c478bd9Sstevel@tonic-gate (void) pm_power_has_changed(hdip, 0, 3321*7c478bd9Sstevel@tonic-gate USB_DEV_OS_FULL_PWR); 3322*7c478bd9Sstevel@tonic-gate 3323*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 3324*7c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_ONLINE; 3325*7c478bd9Sstevel@tonic-gate } 3326*7c478bd9Sstevel@tonic-gate 3327*7c478bd9Sstevel@tonic-gate } else { 3328*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 3329*7c478bd9Sstevel@tonic-gate "hubd_hotplug_thread: not root hub"); 3330*7c478bd9Sstevel@tonic-gate } 3331*7c478bd9Sstevel@tonic-gate 3332*7c478bd9Sstevel@tonic-gate ASSERT(hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE); 3333*7c478bd9Sstevel@tonic-gate 3334*7c478bd9Sstevel@tonic-gate nports = hubd->h_hub_descr.bNbrPorts; 3335*7c478bd9Sstevel@tonic-gate 3336*7c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd); 3337*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 3338*7c478bd9Sstevel@tonic-gate 3339*7c478bd9Sstevel@tonic-gate /* 3340*7c478bd9Sstevel@tonic-gate * this ensures one hotplug activity per system at a time. 3341*7c478bd9Sstevel@tonic-gate * we enter the parent PCI node to have this serialization. 3342*7c478bd9Sstevel@tonic-gate * this also excludes ioctls and deathrow thread 3343*7c478bd9Sstevel@tonic-gate * (a bit crude but easier to debug) 3344*7c478bd9Sstevel@tonic-gate */ 3345*7c478bd9Sstevel@tonic-gate ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ); 3346*7c478bd9Sstevel@tonic-gate ndi_devi_enter(rh_dip, &rh_circ); 3347*7c478bd9Sstevel@tonic-gate 3348*7c478bd9Sstevel@tonic-gate /* exclude other threads */ 3349*7c478bd9Sstevel@tonic-gate ndi_devi_enter(hdip, &circ); 3350*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 3351*7c478bd9Sstevel@tonic-gate 3352*7c478bd9Sstevel@tonic-gate while ((hubd->h_dev_state == USB_DEV_ONLINE) && 3353*7c478bd9Sstevel@tonic-gate (hubd->h_port_change)) { 3354*7c478bd9Sstevel@tonic-gate /* 3355*7c478bd9Sstevel@tonic-gate * The 0th bit is the hub status change bit. 3356*7c478bd9Sstevel@tonic-gate * handle loss of local power here 3357*7c478bd9Sstevel@tonic-gate */ 3358*7c478bd9Sstevel@tonic-gate if (hubd->h_port_change & HUB_CHANGE_STATUS) { 3359*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 3360*7c478bd9Sstevel@tonic-gate "hubd_hotplug_thread: hub status change!"); 3361*7c478bd9Sstevel@tonic-gate 3362*7c478bd9Sstevel@tonic-gate /* 3363*7c478bd9Sstevel@tonic-gate * This should be handled properly. For now, 3364*7c478bd9Sstevel@tonic-gate * mask off the bit. 3365*7c478bd9Sstevel@tonic-gate */ 3366*7c478bd9Sstevel@tonic-gate hubd->h_port_change &= ~HUB_CHANGE_STATUS; 3367*7c478bd9Sstevel@tonic-gate 3368*7c478bd9Sstevel@tonic-gate /* 3369*7c478bd9Sstevel@tonic-gate * check and ack hub status 3370*7c478bd9Sstevel@tonic-gate * this causes stall conditions 3371*7c478bd9Sstevel@tonic-gate * when local power is removed 3372*7c478bd9Sstevel@tonic-gate */ 3373*7c478bd9Sstevel@tonic-gate (void) hubd_get_hub_status(hubd); 3374*7c478bd9Sstevel@tonic-gate } 3375*7c478bd9Sstevel@tonic-gate 3376*7c478bd9Sstevel@tonic-gate for (port = 1; port <= nports; port++) { 3377*7c478bd9Sstevel@tonic-gate usb_port_mask_t port_mask; 3378*7c478bd9Sstevel@tonic-gate boolean_t was_connected; 3379*7c478bd9Sstevel@tonic-gate 3380*7c478bd9Sstevel@tonic-gate port_mask = 1 << port; 3381*7c478bd9Sstevel@tonic-gate was_connected = 3382*7c478bd9Sstevel@tonic-gate (hubd->h_port_state[port] & PORT_STATUS_CCS) && 3383*7c478bd9Sstevel@tonic-gate (hubd->h_children_dips[port]); 3384*7c478bd9Sstevel@tonic-gate 3385*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 3386*7c478bd9Sstevel@tonic-gate "hubd_hotplug_thread: " 3387*7c478bd9Sstevel@tonic-gate "port %d mask=0x%x change=0x%x connected=0x%x", 3388*7c478bd9Sstevel@tonic-gate port, port_mask, hubd->h_port_change, 3389*7c478bd9Sstevel@tonic-gate was_connected); 3390*7c478bd9Sstevel@tonic-gate 3391*7c478bd9Sstevel@tonic-gate /* 3392*7c478bd9Sstevel@tonic-gate * is this a port connection that changed? 3393*7c478bd9Sstevel@tonic-gate */ 3394*7c478bd9Sstevel@tonic-gate if ((hubd->h_port_change & port_mask) == 0) { 3395*7c478bd9Sstevel@tonic-gate 3396*7c478bd9Sstevel@tonic-gate continue; 3397*7c478bd9Sstevel@tonic-gate } 3398*7c478bd9Sstevel@tonic-gate hubd->h_port_change &= ~port_mask; 3399*7c478bd9Sstevel@tonic-gate 3400*7c478bd9Sstevel@tonic-gate /* ack all changes */ 3401*7c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 3402*7c478bd9Sstevel@tonic-gate &status, &change, HUBD_ACK_ALL_CHANGES); 3403*7c478bd9Sstevel@tonic-gate 3404*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 3405*7c478bd9Sstevel@tonic-gate "handle port %d:\n\t" 3406*7c478bd9Sstevel@tonic-gate "new status=0x%x change=0x%x was_conn=0x%x ", 3407*7c478bd9Sstevel@tonic-gate port, status, change, was_connected); 3408*7c478bd9Sstevel@tonic-gate 3409*7c478bd9Sstevel@tonic-gate /* Recover a disabled port */ 3410*7c478bd9Sstevel@tonic-gate if (change & PORT_CHANGE_PESC) { 3411*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, 3412*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 3413*7c478bd9Sstevel@tonic-gate "port%d Disabled - " 3414*7c478bd9Sstevel@tonic-gate "status=0x%x, change=0x%x", 3415*7c478bd9Sstevel@tonic-gate port, status, change); 3416*7c478bd9Sstevel@tonic-gate 3417*7c478bd9Sstevel@tonic-gate /* 3418*7c478bd9Sstevel@tonic-gate * if the port was connected and is still 3419*7c478bd9Sstevel@tonic-gate * connected, recover the port 3420*7c478bd9Sstevel@tonic-gate */ 3421*7c478bd9Sstevel@tonic-gate if (was_connected && (status & 3422*7c478bd9Sstevel@tonic-gate PORT_STATUS_CCS)) { 3423*7c478bd9Sstevel@tonic-gate online_child |= 3424*7c478bd9Sstevel@tonic-gate (hubd_recover_disabled_port(hubd, 3425*7c478bd9Sstevel@tonic-gate port) == USB_SUCCESS); 3426*7c478bd9Sstevel@tonic-gate } 3427*7c478bd9Sstevel@tonic-gate } 3428*7c478bd9Sstevel@tonic-gate 3429*7c478bd9Sstevel@tonic-gate /* 3430*7c478bd9Sstevel@tonic-gate * Now check what changed on the port 3431*7c478bd9Sstevel@tonic-gate */ 3432*7c478bd9Sstevel@tonic-gate if (change & PORT_CHANGE_CSC) { 3433*7c478bd9Sstevel@tonic-gate if ((status & PORT_STATUS_CCS) && 3434*7c478bd9Sstevel@tonic-gate (!was_connected)) { 3435*7c478bd9Sstevel@tonic-gate /* new device plugged in */ 3436*7c478bd9Sstevel@tonic-gate online_child |= 3437*7c478bd9Sstevel@tonic-gate (hubd_handle_port_connect(hubd, 3438*7c478bd9Sstevel@tonic-gate port) == USB_SUCCESS); 3439*7c478bd9Sstevel@tonic-gate 3440*7c478bd9Sstevel@tonic-gate } else if ((status & PORT_STATUS_CCS) && 3441*7c478bd9Sstevel@tonic-gate was_connected) { 3442*7c478bd9Sstevel@tonic-gate /* 3443*7c478bd9Sstevel@tonic-gate * In this case we can never be sure 3444*7c478bd9Sstevel@tonic-gate * if the device indeed got hotplugged 3445*7c478bd9Sstevel@tonic-gate * or the hub is falsely reporting the 3446*7c478bd9Sstevel@tonic-gate * change. 3447*7c478bd9Sstevel@tonic-gate * first post a disconnect event 3448*7c478bd9Sstevel@tonic-gate * to the child 3449*7c478bd9Sstevel@tonic-gate */ 3450*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 3451*7c478bd9Sstevel@tonic-gate hubd_post_event(hubd, port, 3452*7c478bd9Sstevel@tonic-gate USBA_EVENT_TAG_HOT_REMOVAL); 3453*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 3454*7c478bd9Sstevel@tonic-gate 3455*7c478bd9Sstevel@tonic-gate /* 3456*7c478bd9Sstevel@tonic-gate * then reset the port and recover 3457*7c478bd9Sstevel@tonic-gate * the device 3458*7c478bd9Sstevel@tonic-gate */ 3459*7c478bd9Sstevel@tonic-gate online_child |= 3460*7c478bd9Sstevel@tonic-gate (hubd_handle_port_connect(hubd, 3461*7c478bd9Sstevel@tonic-gate port) == USB_SUCCESS); 3462*7c478bd9Sstevel@tonic-gate } else if (was_connected) { 3463*7c478bd9Sstevel@tonic-gate /* this is a disconnect */ 3464*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 3465*7c478bd9Sstevel@tonic-gate hubd_post_event(hubd, port, 3466*7c478bd9Sstevel@tonic-gate USBA_EVENT_TAG_HOT_REMOVAL); 3467*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 3468*7c478bd9Sstevel@tonic-gate 3469*7c478bd9Sstevel@tonic-gate offline_child = B_TRUE; 3470*7c478bd9Sstevel@tonic-gate } 3471*7c478bd9Sstevel@tonic-gate } 3472*7c478bd9Sstevel@tonic-gate 3473*7c478bd9Sstevel@tonic-gate /* 3474*7c478bd9Sstevel@tonic-gate * Check if any port is coming out of suspend 3475*7c478bd9Sstevel@tonic-gate */ 3476*7c478bd9Sstevel@tonic-gate if (change & PORT_CHANGE_PSSC) { 3477*7c478bd9Sstevel@tonic-gate /* a resuming device could have disconnected */ 3478*7c478bd9Sstevel@tonic-gate if (was_connected && 3479*7c478bd9Sstevel@tonic-gate hubd->h_children_dips[port]) { 3480*7c478bd9Sstevel@tonic-gate 3481*7c478bd9Sstevel@tonic-gate /* device on this port resuming */ 3482*7c478bd9Sstevel@tonic-gate dev_info_t *dip; 3483*7c478bd9Sstevel@tonic-gate 3484*7c478bd9Sstevel@tonic-gate dip = hubd->h_children_dips[port]; 3485*7c478bd9Sstevel@tonic-gate 3486*7c478bd9Sstevel@tonic-gate /* 3487*7c478bd9Sstevel@tonic-gate * Don't raise power on detaching child 3488*7c478bd9Sstevel@tonic-gate */ 3489*7c478bd9Sstevel@tonic-gate if (!DEVI_IS_DETACHING(dip)) { 3490*7c478bd9Sstevel@tonic-gate /* 3491*7c478bd9Sstevel@tonic-gate * As this child is not 3492*7c478bd9Sstevel@tonic-gate * detaching, we set this 3493*7c478bd9Sstevel@tonic-gate * flag, causing bus_ctls 3494*7c478bd9Sstevel@tonic-gate * to stall detach till 3495*7c478bd9Sstevel@tonic-gate * pm_raise_power returns 3496*7c478bd9Sstevel@tonic-gate * and flag it for a deferred 3497*7c478bd9Sstevel@tonic-gate * raise_power. 3498*7c478bd9Sstevel@tonic-gate * 3499*7c478bd9Sstevel@tonic-gate * pm_raise_power is deferred 3500*7c478bd9Sstevel@tonic-gate * because we need to release 3501*7c478bd9Sstevel@tonic-gate * the locks first. 3502*7c478bd9Sstevel@tonic-gate */ 3503*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= 3504*7c478bd9Sstevel@tonic-gate HUBD_CHILD_RAISE_POWER; 3505*7c478bd9Sstevel@tonic-gate pwrup_child = B_TRUE; 3506*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 3507*7c478bd9Sstevel@tonic-gate 3508*7c478bd9Sstevel@tonic-gate /* 3509*7c478bd9Sstevel@tonic-gate * make sure that child 3510*7c478bd9Sstevel@tonic-gate * doesn't disappear 3511*7c478bd9Sstevel@tonic-gate */ 3512*7c478bd9Sstevel@tonic-gate ndi_hold_devi(dip); 3513*7c478bd9Sstevel@tonic-gate 3514*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 3515*7c478bd9Sstevel@tonic-gate } 3516*7c478bd9Sstevel@tonic-gate } 3517*7c478bd9Sstevel@tonic-gate } 3518*7c478bd9Sstevel@tonic-gate } 3519*7c478bd9Sstevel@tonic-gate } 3520*7c478bd9Sstevel@tonic-gate 3521*7c478bd9Sstevel@tonic-gate /* release locks so we can do a devfs_clean */ 3522*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 3523*7c478bd9Sstevel@tonic-gate 3524*7c478bd9Sstevel@tonic-gate /* delete cached dv_node's but drop locks first */ 3525*7c478bd9Sstevel@tonic-gate ndi_devi_exit(hdip, circ); 3526*7c478bd9Sstevel@tonic-gate ndi_devi_exit(rh_dip, rh_circ); 3527*7c478bd9Sstevel@tonic-gate ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ); 3528*7c478bd9Sstevel@tonic-gate 3529*7c478bd9Sstevel@tonic-gate (void) devfs_clean(rh_dip, NULL, 0); 3530*7c478bd9Sstevel@tonic-gate 3531*7c478bd9Sstevel@tonic-gate /* now check if any children need onlining */ 3532*7c478bd9Sstevel@tonic-gate if (online_child) { 3533*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 3534*7c478bd9Sstevel@tonic-gate "hubd_hotplug_thread: onlining children"); 3535*7c478bd9Sstevel@tonic-gate 3536*7c478bd9Sstevel@tonic-gate (void) ndi_devi_online(hubd->h_dip, 0); 3537*7c478bd9Sstevel@tonic-gate } 3538*7c478bd9Sstevel@tonic-gate 3539*7c478bd9Sstevel@tonic-gate /* now check if any disconnected devices need to be cleaned up */ 3540*7c478bd9Sstevel@tonic-gate if (offline_child) { 3541*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 3542*7c478bd9Sstevel@tonic-gate "hubd_hotplug_thread: scheduling cleanup"); 3543*7c478bd9Sstevel@tonic-gate 3544*7c478bd9Sstevel@tonic-gate hubd_schedule_cleanup(hubd->h_usba_device->usb_root_hub_dip); 3545*7c478bd9Sstevel@tonic-gate } 3546*7c478bd9Sstevel@tonic-gate 3547*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 3548*7c478bd9Sstevel@tonic-gate 3549*7c478bd9Sstevel@tonic-gate /* now raise power on the children that have woken up */ 3550*7c478bd9Sstevel@tonic-gate if (pwrup_child) { 3551*7c478bd9Sstevel@tonic-gate old_state = hubd->h_dev_state; 3552*7c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_HUB_CHILD_PWRLVL; 3553*7c478bd9Sstevel@tonic-gate for (port = 1; port <= nports; port++) { 3554*7c478bd9Sstevel@tonic-gate if (hubd->h_port_state[port] & HUBD_CHILD_RAISE_POWER) { 3555*7c478bd9Sstevel@tonic-gate dev_info_t *dip = hubd->h_children_dips[port]; 3556*7c478bd9Sstevel@tonic-gate 3557*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 3558*7c478bd9Sstevel@tonic-gate 3559*7c478bd9Sstevel@tonic-gate /* Get the device to full power */ 3560*7c478bd9Sstevel@tonic-gate (void) pm_busy_component(dip, 0); 3561*7c478bd9Sstevel@tonic-gate (void) pm_raise_power(dip, 0, 3562*7c478bd9Sstevel@tonic-gate USB_DEV_OS_FULL_PWR); 3563*7c478bd9Sstevel@tonic-gate (void) pm_idle_component(dip, 0); 3564*7c478bd9Sstevel@tonic-gate 3565*7c478bd9Sstevel@tonic-gate /* release the hold on the child */ 3566*7c478bd9Sstevel@tonic-gate ndi_rele_devi(dip); 3567*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 3568*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= 3569*7c478bd9Sstevel@tonic-gate ~HUBD_CHILD_RAISE_POWER; 3570*7c478bd9Sstevel@tonic-gate } 3571*7c478bd9Sstevel@tonic-gate } 3572*7c478bd9Sstevel@tonic-gate /* 3573*7c478bd9Sstevel@tonic-gate * make sure that we don't accidentally 3574*7c478bd9Sstevel@tonic-gate * over write the disconnect state 3575*7c478bd9Sstevel@tonic-gate */ 3576*7c478bd9Sstevel@tonic-gate if (hubd->h_dev_state == USB_DEV_HUB_CHILD_PWRLVL) { 3577*7c478bd9Sstevel@tonic-gate hubd->h_dev_state = old_state; 3578*7c478bd9Sstevel@tonic-gate } 3579*7c478bd9Sstevel@tonic-gate } 3580*7c478bd9Sstevel@tonic-gate 3581*7c478bd9Sstevel@tonic-gate /* 3582*7c478bd9Sstevel@tonic-gate * start polling can immediately kick off read callback 3583*7c478bd9Sstevel@tonic-gate * we need to set the h_hotplug_thread to 0 so that 3584*7c478bd9Sstevel@tonic-gate * the callback is not dropped 3585*7c478bd9Sstevel@tonic-gate */ 3586*7c478bd9Sstevel@tonic-gate hubd_start_polling(hubd, HUBD_ALWAYS_START_POLLING); 3587*7c478bd9Sstevel@tonic-gate 3588*7c478bd9Sstevel@tonic-gate /* 3589*7c478bd9Sstevel@tonic-gate * Earlier we would set the h_hotplug_thread = 0 before 3590*7c478bd9Sstevel@tonic-gate * polling was restarted so that 3591*7c478bd9Sstevel@tonic-gate * if there is any root hub status change interrupt, we can still kick 3592*7c478bd9Sstevel@tonic-gate * off the hotplug thread. This was valid when this interrupt was 3593*7c478bd9Sstevel@tonic-gate * delivered in hardware, and only ONE interrupt would be delivered. 3594*7c478bd9Sstevel@tonic-gate * Now that we poll on the root hub looking for status change in 3595*7c478bd9Sstevel@tonic-gate * software, this assignment is no longer required. 3596*7c478bd9Sstevel@tonic-gate */ 3597*7c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread--; 3598*7c478bd9Sstevel@tonic-gate 3599*7c478bd9Sstevel@tonic-gate /* mark this device as idle */ 3600*7c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0); 3601*7c478bd9Sstevel@tonic-gate 3602*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 3603*7c478bd9Sstevel@tonic-gate "hubd_hotplug_thread: exit"); 3604*7c478bd9Sstevel@tonic-gate 3605*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 3606*7c478bd9Sstevel@tonic-gate 3607*7c478bd9Sstevel@tonic-gate ndi_rele_devi(hdip); 3608*7c478bd9Sstevel@tonic-gate } 3609*7c478bd9Sstevel@tonic-gate 3610*7c478bd9Sstevel@tonic-gate 3611*7c478bd9Sstevel@tonic-gate /* 3612*7c478bd9Sstevel@tonic-gate * hubd_handle_port_connect: 3613*7c478bd9Sstevel@tonic-gate * Transition a port from Disabled to Enabled. Ensure that the 3614*7c478bd9Sstevel@tonic-gate * port is in the correct state before attempting to 3615*7c478bd9Sstevel@tonic-gate * access the device. 3616*7c478bd9Sstevel@tonic-gate */ 3617*7c478bd9Sstevel@tonic-gate static int 3618*7c478bd9Sstevel@tonic-gate hubd_handle_port_connect(hubd_t *hubd, usb_port_t port) 3619*7c478bd9Sstevel@tonic-gate { 3620*7c478bd9Sstevel@tonic-gate int rval; 3621*7c478bd9Sstevel@tonic-gate int retry; 3622*7c478bd9Sstevel@tonic-gate long time_delay; 3623*7c478bd9Sstevel@tonic-gate long settling_time; 3624*7c478bd9Sstevel@tonic-gate uint16_t status; 3625*7c478bd9Sstevel@tonic-gate uint16_t change; 3626*7c478bd9Sstevel@tonic-gate usb_addr_t hubd_usb_addr; 3627*7c478bd9Sstevel@tonic-gate usba_device_t *usba_device; 3628*7c478bd9Sstevel@tonic-gate usb_port_status_t port_status = 0; 3629*7c478bd9Sstevel@tonic-gate usb_port_status_t hub_port_status = 0; 3630*7c478bd9Sstevel@tonic-gate 3631*7c478bd9Sstevel@tonic-gate /* Get the hub address and port status */ 3632*7c478bd9Sstevel@tonic-gate usba_device = hubd->h_usba_device; 3633*7c478bd9Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex); 3634*7c478bd9Sstevel@tonic-gate hubd_usb_addr = usba_device->usb_addr; 3635*7c478bd9Sstevel@tonic-gate hub_port_status = usba_device->usb_port_status; 3636*7c478bd9Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex); 3637*7c478bd9Sstevel@tonic-gate 3638*7c478bd9Sstevel@tonic-gate /* 3639*7c478bd9Sstevel@tonic-gate * If a device is connected, transition the 3640*7c478bd9Sstevel@tonic-gate * port from Disabled to the Enabled state. 3641*7c478bd9Sstevel@tonic-gate * The device will receive downstream packets 3642*7c478bd9Sstevel@tonic-gate * in the Enabled state. 3643*7c478bd9Sstevel@tonic-gate * 3644*7c478bd9Sstevel@tonic-gate * reset port and wait for the hub to report 3645*7c478bd9Sstevel@tonic-gate * completion 3646*7c478bd9Sstevel@tonic-gate */ 3647*7c478bd9Sstevel@tonic-gate change = status = 0; 3648*7c478bd9Sstevel@tonic-gate 3649*7c478bd9Sstevel@tonic-gate /* 3650*7c478bd9Sstevel@tonic-gate * According to section 9.1.2 of USB 2.0 spec, the host should 3651*7c478bd9Sstevel@tonic-gate * wait for atleast 100ms to allow completion of an insertion 3652*7c478bd9Sstevel@tonic-gate * process and for power at the device to become stable. 3653*7c478bd9Sstevel@tonic-gate * We wait for 200 ms 3654*7c478bd9Sstevel@tonic-gate */ 3655*7c478bd9Sstevel@tonic-gate settling_time = drv_usectohz(hubd_device_delay / 5); 3656*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 3657*7c478bd9Sstevel@tonic-gate delay(settling_time); 3658*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 3659*7c478bd9Sstevel@tonic-gate 3660*7c478bd9Sstevel@tonic-gate /* calculate 600 ms delay time */ 3661*7c478bd9Sstevel@tonic-gate time_delay = (6 * drv_usectohz(hubd_device_delay)) / 10; 3662*7c478bd9Sstevel@tonic-gate 3663*7c478bd9Sstevel@tonic-gate for (retry = 0; (hubd->h_dev_state == USB_DEV_ONLINE) && 3664*7c478bd9Sstevel@tonic-gate (retry < hubd_retry_enumerate); retry++) { 3665*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 3666*7c478bd9Sstevel@tonic-gate "resetting port%d, retry=%d", port, retry); 3667*7c478bd9Sstevel@tonic-gate 3668*7c478bd9Sstevel@tonic-gate if ((rval = hubd_reset_port(hubd, port)) != USB_SUCCESS) { 3669*7c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, 3670*7c478bd9Sstevel@tonic-gate port, &status, &change, 0); 3671*7c478bd9Sstevel@tonic-gate 3672*7c478bd9Sstevel@tonic-gate /* continue only if port is still connected */ 3673*7c478bd9Sstevel@tonic-gate if (status & PORT_STATUS_CCS) { 3674*7c478bd9Sstevel@tonic-gate continue; 3675*7c478bd9Sstevel@tonic-gate } 3676*7c478bd9Sstevel@tonic-gate 3677*7c478bd9Sstevel@tonic-gate /* carry on regardless */ 3678*7c478bd9Sstevel@tonic-gate } 3679*7c478bd9Sstevel@tonic-gate 3680*7c478bd9Sstevel@tonic-gate /* 3681*7c478bd9Sstevel@tonic-gate * according to USB 2.0 spec section 11.24.2.7.1.2 3682*7c478bd9Sstevel@tonic-gate * at the end of port reset, the hub enables the port. 3683*7c478bd9Sstevel@tonic-gate * But for some strange reasons, uhci port remains disabled. 3684*7c478bd9Sstevel@tonic-gate * And because the port remains disabled for the settling 3685*7c478bd9Sstevel@tonic-gate * time below, the device connected to the port gets wedged 3686*7c478bd9Sstevel@tonic-gate * - fails to enumerate (device not responding) 3687*7c478bd9Sstevel@tonic-gate * Hence, we enable it here immediately and later again after 3688*7c478bd9Sstevel@tonic-gate * the delay 3689*7c478bd9Sstevel@tonic-gate */ 3690*7c478bd9Sstevel@tonic-gate (void) hubd_enable_port(hubd, port); 3691*7c478bd9Sstevel@tonic-gate 3692*7c478bd9Sstevel@tonic-gate /* we skip this delay in the first iteration */ 3693*7c478bd9Sstevel@tonic-gate if (retry) { 3694*7c478bd9Sstevel@tonic-gate /* 3695*7c478bd9Sstevel@tonic-gate * delay for device to signal disconnect/connect so 3696*7c478bd9Sstevel@tonic-gate * that hub properly recognizes the speed of the device 3697*7c478bd9Sstevel@tonic-gate */ 3698*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 3699*7c478bd9Sstevel@tonic-gate delay(settling_time); 3700*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 3701*7c478bd9Sstevel@tonic-gate 3702*7c478bd9Sstevel@tonic-gate /* 3703*7c478bd9Sstevel@tonic-gate * When a low speed device is connected to any port of 3704*7c478bd9Sstevel@tonic-gate * PPX it has to be explicitly enabled 3705*7c478bd9Sstevel@tonic-gate * Also, if device intentionally signals 3706*7c478bd9Sstevel@tonic-gate * disconnect/connect, it will disable the port. 3707*7c478bd9Sstevel@tonic-gate * So enable it again. 3708*7c478bd9Sstevel@tonic-gate */ 3709*7c478bd9Sstevel@tonic-gate (void) hubd_enable_port(hubd, port); 3710*7c478bd9Sstevel@tonic-gate } 3711*7c478bd9Sstevel@tonic-gate 3712*7c478bd9Sstevel@tonic-gate if ((rval = hubd_determine_port_status(hubd, port, &status, 3713*7c478bd9Sstevel@tonic-gate &change, 0)) != USB_SUCCESS) { 3714*7c478bd9Sstevel@tonic-gate 3715*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 3716*7c478bd9Sstevel@tonic-gate "getting status failed (%d)", rval); 3717*7c478bd9Sstevel@tonic-gate 3718*7c478bd9Sstevel@tonic-gate (void) hubd_disable_port(hubd, port); 3719*7c478bd9Sstevel@tonic-gate 3720*7c478bd9Sstevel@tonic-gate continue; 3721*7c478bd9Sstevel@tonic-gate } 3722*7c478bd9Sstevel@tonic-gate 3723*7c478bd9Sstevel@tonic-gate if (status & PORT_STATUS_POCI) { 3724*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 3725*7c478bd9Sstevel@tonic-gate "port %d overcurrent", port); 3726*7c478bd9Sstevel@tonic-gate 3727*7c478bd9Sstevel@tonic-gate (void) hubd_disable_port(hubd, port); 3728*7c478bd9Sstevel@tonic-gate 3729*7c478bd9Sstevel@tonic-gate /* ack changes */ 3730*7c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, 3731*7c478bd9Sstevel@tonic-gate port, &status, &change, PORT_CHANGE_OCIC); 3732*7c478bd9Sstevel@tonic-gate 3733*7c478bd9Sstevel@tonic-gate continue; 3734*7c478bd9Sstevel@tonic-gate } 3735*7c478bd9Sstevel@tonic-gate 3736*7c478bd9Sstevel@tonic-gate /* is status really OK? */ 3737*7c478bd9Sstevel@tonic-gate if ((status & PORT_STATUS_OK) != PORT_STATUS_OK) { 3738*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 3739*7c478bd9Sstevel@tonic-gate "port %d status (0x%x) not OK on retry %d", 3740*7c478bd9Sstevel@tonic-gate port, status, retry); 3741*7c478bd9Sstevel@tonic-gate 3742*7c478bd9Sstevel@tonic-gate /* check if we still have the connection */ 3743*7c478bd9Sstevel@tonic-gate if (!(status & PORT_STATUS_CCS)) { 3744*7c478bd9Sstevel@tonic-gate /* lost connection, set exit condition */ 3745*7c478bd9Sstevel@tonic-gate retry = hubd_retry_enumerate; 3746*7c478bd9Sstevel@tonic-gate 3747*7c478bd9Sstevel@tonic-gate break; 3748*7c478bd9Sstevel@tonic-gate } 3749*7c478bd9Sstevel@tonic-gate } else { 3750*7c478bd9Sstevel@tonic-gate /* 3751*7c478bd9Sstevel@tonic-gate * Determine if the device is high or full 3752*7c478bd9Sstevel@tonic-gate * or low speed. 3753*7c478bd9Sstevel@tonic-gate */ 3754*7c478bd9Sstevel@tonic-gate if (status & PORT_STATUS_LSDA) { 3755*7c478bd9Sstevel@tonic-gate port_status = USBA_LOW_SPEED_DEV; 3756*7c478bd9Sstevel@tonic-gate } else if (status & PORT_STATUS_HSDA) { 3757*7c478bd9Sstevel@tonic-gate port_status = USBA_HIGH_SPEED_DEV; 3758*7c478bd9Sstevel@tonic-gate } else { 3759*7c478bd9Sstevel@tonic-gate port_status = USBA_FULL_SPEED_DEV; 3760*7c478bd9Sstevel@tonic-gate } 3761*7c478bd9Sstevel@tonic-gate 3762*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 3763*7c478bd9Sstevel@tonic-gate "creating child port%d, status=0x%x " 3764*7c478bd9Sstevel@tonic-gate "port status=0x%x", 3765*7c478bd9Sstevel@tonic-gate port, status, port_status); 3766*7c478bd9Sstevel@tonic-gate 3767*7c478bd9Sstevel@tonic-gate /* 3768*7c478bd9Sstevel@tonic-gate * if the child already exists, set addrs and config 3769*7c478bd9Sstevel@tonic-gate * to the device post connect event to the child 3770*7c478bd9Sstevel@tonic-gate */ 3771*7c478bd9Sstevel@tonic-gate if (hubd->h_children_dips[port]) { 3772*7c478bd9Sstevel@tonic-gate /* set addrs to this device */ 3773*7c478bd9Sstevel@tonic-gate rval = hubd_setdevaddr(hubd, port); 3774*7c478bd9Sstevel@tonic-gate 3775*7c478bd9Sstevel@tonic-gate /* 3776*7c478bd9Sstevel@tonic-gate * This delay is important for the CATC hub 3777*7c478bd9Sstevel@tonic-gate * to enumerate. But, avoid delay in the first 3778*7c478bd9Sstevel@tonic-gate * iteration 3779*7c478bd9Sstevel@tonic-gate */ 3780*7c478bd9Sstevel@tonic-gate if (retry) { 3781*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 3782*7c478bd9Sstevel@tonic-gate delay(drv_usectohz( 3783*7c478bd9Sstevel@tonic-gate hubd_device_delay/100)); 3784*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 3785*7c478bd9Sstevel@tonic-gate } 3786*7c478bd9Sstevel@tonic-gate 3787*7c478bd9Sstevel@tonic-gate if (rval == USB_SUCCESS) { 3788*7c478bd9Sstevel@tonic-gate /* 3789*7c478bd9Sstevel@tonic-gate * set the default config for 3790*7c478bd9Sstevel@tonic-gate * this device 3791*7c478bd9Sstevel@tonic-gate */ 3792*7c478bd9Sstevel@tonic-gate hubd_setdevconfig(hubd, port); 3793*7c478bd9Sstevel@tonic-gate 3794*7c478bd9Sstevel@tonic-gate /* 3795*7c478bd9Sstevel@tonic-gate * indicate to the child that 3796*7c478bd9Sstevel@tonic-gate * it is online again 3797*7c478bd9Sstevel@tonic-gate */ 3798*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 3799*7c478bd9Sstevel@tonic-gate hubd_post_event(hubd, port, 3800*7c478bd9Sstevel@tonic-gate USBA_EVENT_TAG_HOT_INSERTION); 3801*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 3802*7c478bd9Sstevel@tonic-gate 3803*7c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 3804*7c478bd9Sstevel@tonic-gate } 3805*7c478bd9Sstevel@tonic-gate } else { 3806*7c478bd9Sstevel@tonic-gate /* 3807*7c478bd9Sstevel@tonic-gate * We need to release access here 3808*7c478bd9Sstevel@tonic-gate * so that busctls on other ports can 3809*7c478bd9Sstevel@tonic-gate * continue and don't cause a deadlock 3810*7c478bd9Sstevel@tonic-gate * when busctl and removal of prom node 3811*7c478bd9Sstevel@tonic-gate * takes concurrently. This also ensures 3812*7c478bd9Sstevel@tonic-gate * busctls for attach of successfully 3813*7c478bd9Sstevel@tonic-gate * enumerated devices on other ports can 3814*7c478bd9Sstevel@tonic-gate * continue concurrently with the process 3815*7c478bd9Sstevel@tonic-gate * of enumerating the new devices. This 3816*7c478bd9Sstevel@tonic-gate * reduces the overall boot time of the system. 3817*7c478bd9Sstevel@tonic-gate */ 3818*7c478bd9Sstevel@tonic-gate rval = hubd_create_child(hubd->h_dip, 3819*7c478bd9Sstevel@tonic-gate hubd, 3820*7c478bd9Sstevel@tonic-gate hubd->h_usba_device, 3821*7c478bd9Sstevel@tonic-gate port_status, port, 3822*7c478bd9Sstevel@tonic-gate retry); 3823*7c478bd9Sstevel@tonic-gate if (rval == USB_SUCCESS) { 3824*7c478bd9Sstevel@tonic-gate usba_update_hotplug_stats(hubd->h_dip, 3825*7c478bd9Sstevel@tonic-gate USBA_TOTAL_HOTPLUG_SUCCESS| 3826*7c478bd9Sstevel@tonic-gate USBA_HOTPLUG_SUCCESS); 3827*7c478bd9Sstevel@tonic-gate hubd->h_total_hotplug_success++; 3828*7c478bd9Sstevel@tonic-gate 3829*7c478bd9Sstevel@tonic-gate if (retry > 0) { 3830*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1( 3831*7c478bd9Sstevel@tonic-gate DPRINT_MASK_HOTPLUG, 3832*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 3833*7c478bd9Sstevel@tonic-gate "device on port %d " 3834*7c478bd9Sstevel@tonic-gate "enumerated after %d %s", 3835*7c478bd9Sstevel@tonic-gate port, retry, 3836*7c478bd9Sstevel@tonic-gate (retry > 1) ? "retries" : 3837*7c478bd9Sstevel@tonic-gate "retry"); 3838*7c478bd9Sstevel@tonic-gate 3839*7c478bd9Sstevel@tonic-gate } 3840*7c478bd9Sstevel@tonic-gate 3841*7c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 3842*7c478bd9Sstevel@tonic-gate } 3843*7c478bd9Sstevel@tonic-gate } 3844*7c478bd9Sstevel@tonic-gate } 3845*7c478bd9Sstevel@tonic-gate 3846*7c478bd9Sstevel@tonic-gate /* wait a while until it settles? */ 3847*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 3848*7c478bd9Sstevel@tonic-gate "disabling port %d again", port); 3849*7c478bd9Sstevel@tonic-gate 3850*7c478bd9Sstevel@tonic-gate (void) hubd_disable_port(hubd, port); 3851*7c478bd9Sstevel@tonic-gate if (retry) { 3852*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 3853*7c478bd9Sstevel@tonic-gate delay(time_delay); 3854*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 3855*7c478bd9Sstevel@tonic-gate } 3856*7c478bd9Sstevel@tonic-gate 3857*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 3858*7c478bd9Sstevel@tonic-gate "retrying on port %d", port); 3859*7c478bd9Sstevel@tonic-gate } 3860*7c478bd9Sstevel@tonic-gate 3861*7c478bd9Sstevel@tonic-gate if (retry >= hubd_retry_enumerate) { 3862*7c478bd9Sstevel@tonic-gate /* 3863*7c478bd9Sstevel@tonic-gate * If it is a High Speed Root Hub and connected device 3864*7c478bd9Sstevel@tonic-gate * Is a Low/Full Speed, it will be handled by USB 1.1 3865*7c478bd9Sstevel@tonic-gate * Host Controller. In this case, USB 2.0 Host Controller 3866*7c478bd9Sstevel@tonic-gate * will transfer the ownership of this port to USB 1.1 3867*7c478bd9Sstevel@tonic-gate * Host Controller. So don't display any error message on 3868*7c478bd9Sstevel@tonic-gate * the console. 3869*7c478bd9Sstevel@tonic-gate */ 3870*7c478bd9Sstevel@tonic-gate if ((hubd_usb_addr == ROOT_HUB_ADDR) && 3871*7c478bd9Sstevel@tonic-gate (hub_port_status == USBA_HIGH_SPEED_DEV) && 3872*7c478bd9Sstevel@tonic-gate (port_status != USBA_HIGH_SPEED_DEV)) { 3873*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 3874*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 3875*7c478bd9Sstevel@tonic-gate "hubd_handle_port_connect: Low/Full speed " 3876*7c478bd9Sstevel@tonic-gate "device is connected to High Speed root hub"); 3877*7c478bd9Sstevel@tonic-gate } else { 3878*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, 3879*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 3880*7c478bd9Sstevel@tonic-gate "Connecting device on port %d failed", port); 3881*7c478bd9Sstevel@tonic-gate } 3882*7c478bd9Sstevel@tonic-gate 3883*7c478bd9Sstevel@tonic-gate (void) hubd_disable_port(hubd, port); 3884*7c478bd9Sstevel@tonic-gate usba_update_hotplug_stats(hubd->h_dip, 3885*7c478bd9Sstevel@tonic-gate USBA_TOTAL_HOTPLUG_FAILURE|USBA_HOTPLUG_FAILURE); 3886*7c478bd9Sstevel@tonic-gate hubd->h_total_hotplug_failure++; 3887*7c478bd9Sstevel@tonic-gate 3888*7c478bd9Sstevel@tonic-gate /* 3889*7c478bd9Sstevel@tonic-gate * the port should be automagically 3890*7c478bd9Sstevel@tonic-gate * disabled but just in case, we do 3891*7c478bd9Sstevel@tonic-gate * it here 3892*7c478bd9Sstevel@tonic-gate */ 3893*7c478bd9Sstevel@tonic-gate (void) hubd_disable_port(hubd, port); 3894*7c478bd9Sstevel@tonic-gate 3895*7c478bd9Sstevel@tonic-gate /* ack all changes because we disabled this port */ 3896*7c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, 3897*7c478bd9Sstevel@tonic-gate port, &status, &change, HUBD_ACK_ALL_CHANGES); 3898*7c478bd9Sstevel@tonic-gate 3899*7c478bd9Sstevel@tonic-gate } 3900*7c478bd9Sstevel@tonic-gate 3901*7c478bd9Sstevel@tonic-gate return (USB_FAILURE); 3902*7c478bd9Sstevel@tonic-gate } 3903*7c478bd9Sstevel@tonic-gate 3904*7c478bd9Sstevel@tonic-gate 3905*7c478bd9Sstevel@tonic-gate /* 3906*7c478bd9Sstevel@tonic-gate * hubd_get_hub_status: 3907*7c478bd9Sstevel@tonic-gate */ 3908*7c478bd9Sstevel@tonic-gate static int 3909*7c478bd9Sstevel@tonic-gate hubd_get_hub_status(hubd_t *hubd) 3910*7c478bd9Sstevel@tonic-gate { 3911*7c478bd9Sstevel@tonic-gate int rval; 3912*7c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 3913*7c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 3914*7c478bd9Sstevel@tonic-gate mblk_t *data = NULL; 3915*7c478bd9Sstevel@tonic-gate uint16_t status; 3916*7c478bd9Sstevel@tonic-gate uint16_t change; 3917*7c478bd9Sstevel@tonic-gate usb_cfg_descr_t cfg_descr; 3918*7c478bd9Sstevel@tonic-gate size_t cfg_length; 3919*7c478bd9Sstevel@tonic-gate uchar_t *usb_cfg; 3920*7c478bd9Sstevel@tonic-gate uint8_t MaxPower; 3921*7c478bd9Sstevel@tonic-gate 3922*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 3923*7c478bd9Sstevel@tonic-gate if (usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe, 3924*7c478bd9Sstevel@tonic-gate HUB_CLASS_REQ, 3925*7c478bd9Sstevel@tonic-gate USB_REQ_GET_STATUS, 3926*7c478bd9Sstevel@tonic-gate 0, 3927*7c478bd9Sstevel@tonic-gate 0, 3928*7c478bd9Sstevel@tonic-gate GET_STATUS_LENGTH, 3929*7c478bd9Sstevel@tonic-gate &data, 0, 3930*7c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0) != USB_SUCCESS) { 3931*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 3932*7c478bd9Sstevel@tonic-gate "get hub status failed: cr=%d cb=0x%x", 3933*7c478bd9Sstevel@tonic-gate completion_reason, cb_flags); 3934*7c478bd9Sstevel@tonic-gate 3935*7c478bd9Sstevel@tonic-gate if (data) { 3936*7c478bd9Sstevel@tonic-gate freemsg(data); 3937*7c478bd9Sstevel@tonic-gate } 3938*7c478bd9Sstevel@tonic-gate 3939*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 3940*7c478bd9Sstevel@tonic-gate 3941*7c478bd9Sstevel@tonic-gate return (USB_FAILURE); 3942*7c478bd9Sstevel@tonic-gate } 3943*7c478bd9Sstevel@tonic-gate 3944*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 3945*7c478bd9Sstevel@tonic-gate 3946*7c478bd9Sstevel@tonic-gate status = (*(data->b_rptr + 1) << 8) | *(data->b_rptr); 3947*7c478bd9Sstevel@tonic-gate change = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2); 3948*7c478bd9Sstevel@tonic-gate 3949*7c478bd9Sstevel@tonic-gate if (status || change) { 3950*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 3951*7c478bd9Sstevel@tonic-gate "hub status = 0x%x change = 0x%x", status, change); 3952*7c478bd9Sstevel@tonic-gate } 3953*7c478bd9Sstevel@tonic-gate 3954*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 3955*7c478bd9Sstevel@tonic-gate freemsg(data); 3956*7c478bd9Sstevel@tonic-gate 3957*7c478bd9Sstevel@tonic-gate /* Obtain the raw configuration descriptor */ 3958*7c478bd9Sstevel@tonic-gate usb_cfg = usb_get_raw_cfg_data(hubd->h_dip, &cfg_length); 3959*7c478bd9Sstevel@tonic-gate 3960*7c478bd9Sstevel@tonic-gate /* get configuration descriptor */ 3961*7c478bd9Sstevel@tonic-gate rval = usb_parse_cfg_descr(usb_cfg, cfg_length, 3962*7c478bd9Sstevel@tonic-gate &cfg_descr, USB_CFG_DESCR_SIZE); 3963*7c478bd9Sstevel@tonic-gate 3964*7c478bd9Sstevel@tonic-gate if (rval != USB_CFG_DESCR_SIZE) { 3965*7c478bd9Sstevel@tonic-gate 3966*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 3967*7c478bd9Sstevel@tonic-gate "get hub configuration descriptor failed."); 3968*7c478bd9Sstevel@tonic-gate 3969*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 3970*7c478bd9Sstevel@tonic-gate 3971*7c478bd9Sstevel@tonic-gate return (USB_FAILURE); 3972*7c478bd9Sstevel@tonic-gate } else { 3973*7c478bd9Sstevel@tonic-gate MaxPower = cfg_descr.bMaxPower; 3974*7c478bd9Sstevel@tonic-gate } 3975*7c478bd9Sstevel@tonic-gate 3976*7c478bd9Sstevel@tonic-gate /* check if local power status changed. */ 3977*7c478bd9Sstevel@tonic-gate if (change & C_HUB_LOCAL_POWER_STATUS) { 3978*7c478bd9Sstevel@tonic-gate 3979*7c478bd9Sstevel@tonic-gate /* 3980*7c478bd9Sstevel@tonic-gate * local power has been lost, check the maximum 3981*7c478bd9Sstevel@tonic-gate * power consumption of current configuration. 3982*7c478bd9Sstevel@tonic-gate * see USB2.0 spec Table 11-12. 3983*7c478bd9Sstevel@tonic-gate */ 3984*7c478bd9Sstevel@tonic-gate if (status & HUB_LOCAL_POWER_STATUS) { 3985*7c478bd9Sstevel@tonic-gate 3986*7c478bd9Sstevel@tonic-gate if (MaxPower == 0) { 3987*7c478bd9Sstevel@tonic-gate 3988*7c478bd9Sstevel@tonic-gate /* 3989*7c478bd9Sstevel@tonic-gate * Self-powered only hub. Because it could 3990*7c478bd9Sstevel@tonic-gate * not draw any power from USB bus. 3991*7c478bd9Sstevel@tonic-gate * It can't work well on this condition. 3992*7c478bd9Sstevel@tonic-gate */ 3993*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_PORT, 3994*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 3995*7c478bd9Sstevel@tonic-gate "local power has been lost, " 3996*7c478bd9Sstevel@tonic-gate "please disconnect hub"); 3997*7c478bd9Sstevel@tonic-gate } else { 3998*7c478bd9Sstevel@tonic-gate 3999*7c478bd9Sstevel@tonic-gate /* 4000*7c478bd9Sstevel@tonic-gate * Bus-powered only or self/bus-powered hub. 4001*7c478bd9Sstevel@tonic-gate */ 4002*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_PORT, 4003*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 4004*7c478bd9Sstevel@tonic-gate "local power has been lost," 4005*7c478bd9Sstevel@tonic-gate "the hub could draw %d" 4006*7c478bd9Sstevel@tonic-gate " mA power from the USB bus.", 4007*7c478bd9Sstevel@tonic-gate 2*MaxPower); 4008*7c478bd9Sstevel@tonic-gate } 4009*7c478bd9Sstevel@tonic-gate 4010*7c478bd9Sstevel@tonic-gate } 4011*7c478bd9Sstevel@tonic-gate 4012*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 4013*7c478bd9Sstevel@tonic-gate "clearing feature C_HUB_LOCAL_POWER "); 4014*7c478bd9Sstevel@tonic-gate 4015*7c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 4016*7c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 4017*7c478bd9Sstevel@tonic-gate USB_DEV_REQ_TYPE_CLASS, 4018*7c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 4019*7c478bd9Sstevel@tonic-gate CFS_C_HUB_LOCAL_POWER, 4020*7c478bd9Sstevel@tonic-gate 0, 4021*7c478bd9Sstevel@tonic-gate 0, 4022*7c478bd9Sstevel@tonic-gate NULL, 0, 4023*7c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 4024*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 4025*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 4026*7c478bd9Sstevel@tonic-gate "clear feature C_HUB_LOCAL_POWER " 4027*7c478bd9Sstevel@tonic-gate "failed (%d 0x%x %d)", 4028*7c478bd9Sstevel@tonic-gate rval, completion_reason, cb_flags); 4029*7c478bd9Sstevel@tonic-gate } 4030*7c478bd9Sstevel@tonic-gate 4031*7c478bd9Sstevel@tonic-gate } 4032*7c478bd9Sstevel@tonic-gate 4033*7c478bd9Sstevel@tonic-gate if (change & C_HUB_OVER_CURRENT) { 4034*7c478bd9Sstevel@tonic-gate 4035*7c478bd9Sstevel@tonic-gate if (status & HUB_OVER_CURRENT) { 4036*7c478bd9Sstevel@tonic-gate /* 4037*7c478bd9Sstevel@tonic-gate * the user must offline this hub in order to recover. 4038*7c478bd9Sstevel@tonic-gate * the port power is automatically disabled, so we 4039*7c478bd9Sstevel@tonic-gate * won't see disconnects. 4040*7c478bd9Sstevel@tonic-gate */ 4041*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_PORT, hubd->h_log_handle, 4042*7c478bd9Sstevel@tonic-gate "global over current condition, " 4043*7c478bd9Sstevel@tonic-gate "please disconnect hub"); 4044*7c478bd9Sstevel@tonic-gate } 4045*7c478bd9Sstevel@tonic-gate 4046*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 4047*7c478bd9Sstevel@tonic-gate "clearing feature C_HUB_OVER_CURRENT"); 4048*7c478bd9Sstevel@tonic-gate 4049*7c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 4050*7c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 4051*7c478bd9Sstevel@tonic-gate USB_DEV_REQ_TYPE_CLASS, 4052*7c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 4053*7c478bd9Sstevel@tonic-gate CFS_C_HUB_OVER_CURRENT, 4054*7c478bd9Sstevel@tonic-gate 0, 4055*7c478bd9Sstevel@tonic-gate 0, 4056*7c478bd9Sstevel@tonic-gate NULL, 0, 4057*7c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 4058*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 4059*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 4060*7c478bd9Sstevel@tonic-gate "clear feature C_HUB_OVER_CURRENT " 4061*7c478bd9Sstevel@tonic-gate "failed (%d 0x%x %d)", 4062*7c478bd9Sstevel@tonic-gate rval, completion_reason, cb_flags); 4063*7c478bd9Sstevel@tonic-gate } 4064*7c478bd9Sstevel@tonic-gate } 4065*7c478bd9Sstevel@tonic-gate 4066*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 4067*7c478bd9Sstevel@tonic-gate 4068*7c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 4069*7c478bd9Sstevel@tonic-gate } 4070*7c478bd9Sstevel@tonic-gate 4071*7c478bd9Sstevel@tonic-gate 4072*7c478bd9Sstevel@tonic-gate /* 4073*7c478bd9Sstevel@tonic-gate * hubd_reset_port: 4074*7c478bd9Sstevel@tonic-gate */ 4075*7c478bd9Sstevel@tonic-gate static int 4076*7c478bd9Sstevel@tonic-gate hubd_reset_port(hubd_t *hubd, usb_port_t port) 4077*7c478bd9Sstevel@tonic-gate { 4078*7c478bd9Sstevel@tonic-gate int rval; 4079*7c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 4080*7c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 4081*7c478bd9Sstevel@tonic-gate usb_port_mask_t port_mask = 1 << port; 4082*7c478bd9Sstevel@tonic-gate mblk_t *data; 4083*7c478bd9Sstevel@tonic-gate uint16_t status; 4084*7c478bd9Sstevel@tonic-gate uint16_t change; 4085*7c478bd9Sstevel@tonic-gate int i; 4086*7c478bd9Sstevel@tonic-gate clock_t current_time; 4087*7c478bd9Sstevel@tonic-gate 4088*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 4089*7c478bd9Sstevel@tonic-gate "hubd_reset_port: port=%d", port); 4090*7c478bd9Sstevel@tonic-gate 4091*7c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 4092*7c478bd9Sstevel@tonic-gate 4093*7c478bd9Sstevel@tonic-gate hubd->h_port_reset_wait |= port_mask; 4094*7c478bd9Sstevel@tonic-gate 4095*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 4096*7c478bd9Sstevel@tonic-gate 4097*7c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 4098*7c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 4099*7c478bd9Sstevel@tonic-gate HANDLE_PORT_FEATURE, 4100*7c478bd9Sstevel@tonic-gate USB_REQ_SET_FEATURE, 4101*7c478bd9Sstevel@tonic-gate CFS_PORT_RESET, 4102*7c478bd9Sstevel@tonic-gate port, 4103*7c478bd9Sstevel@tonic-gate 0, 4104*7c478bd9Sstevel@tonic-gate NULL, 0, 4105*7c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 4106*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_PORT, hubd->h_log_handle, 4107*7c478bd9Sstevel@tonic-gate "reset port%d failed (%d 0x%x %d)", 4108*7c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 4109*7c478bd9Sstevel@tonic-gate 4110*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 4111*7c478bd9Sstevel@tonic-gate 4112*7c478bd9Sstevel@tonic-gate return (USB_FAILURE); 4113*7c478bd9Sstevel@tonic-gate } 4114*7c478bd9Sstevel@tonic-gate 4115*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 4116*7c478bd9Sstevel@tonic-gate 4117*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 4118*7c478bd9Sstevel@tonic-gate "waiting on cv for reset completion"); 4119*7c478bd9Sstevel@tonic-gate 4120*7c478bd9Sstevel@tonic-gate /* 4121*7c478bd9Sstevel@tonic-gate * wait for port status change event 4122*7c478bd9Sstevel@tonic-gate */ 4123*7c478bd9Sstevel@tonic-gate for (i = 0; i < hubd_retry_enumerate; i++) { 4124*7c478bd9Sstevel@tonic-gate /* 4125*7c478bd9Sstevel@tonic-gate * start polling ep1 for receiving notification on 4126*7c478bd9Sstevel@tonic-gate * reset completion 4127*7c478bd9Sstevel@tonic-gate */ 4128*7c478bd9Sstevel@tonic-gate hubd_start_polling(hubd, HUBD_ALWAYS_START_POLLING); 4129*7c478bd9Sstevel@tonic-gate 4130*7c478bd9Sstevel@tonic-gate /* 4131*7c478bd9Sstevel@tonic-gate * sleep a max of 100ms for reset completion 4132*7c478bd9Sstevel@tonic-gate * notification to be received 4133*7c478bd9Sstevel@tonic-gate */ 4134*7c478bd9Sstevel@tonic-gate current_time = ddi_get_lbolt(); 4135*7c478bd9Sstevel@tonic-gate if (hubd->h_port_reset_wait & port_mask) { 4136*7c478bd9Sstevel@tonic-gate rval = cv_timedwait(&hubd->h_cv_reset_port, 4137*7c478bd9Sstevel@tonic-gate &hubd->h_mutex, 4138*7c478bd9Sstevel@tonic-gate current_time + 4139*7c478bd9Sstevel@tonic-gate drv_usectohz(hubd_device_delay / 10)); 4140*7c478bd9Sstevel@tonic-gate if ((rval <= 0) && 4141*7c478bd9Sstevel@tonic-gate (hubd->h_port_reset_wait & port_mask)) { 4142*7c478bd9Sstevel@tonic-gate /* we got woken up because of a timeout */ 4143*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 4144*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 4145*7c478bd9Sstevel@tonic-gate "timeout: reset port=%d failed", port); 4146*7c478bd9Sstevel@tonic-gate 4147*7c478bd9Sstevel@tonic-gate hubd->h_port_reset_wait &= ~port_mask; 4148*7c478bd9Sstevel@tonic-gate 4149*7c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd); 4150*7c478bd9Sstevel@tonic-gate 4151*7c478bd9Sstevel@tonic-gate return (USB_FAILURE); 4152*7c478bd9Sstevel@tonic-gate } 4153*7c478bd9Sstevel@tonic-gate } 4154*7c478bd9Sstevel@tonic-gate 4155*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 4156*7c478bd9Sstevel@tonic-gate "reset completion received"); 4157*7c478bd9Sstevel@tonic-gate 4158*7c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd); 4159*7c478bd9Sstevel@tonic-gate 4160*7c478bd9Sstevel@tonic-gate data = NULL; 4161*7c478bd9Sstevel@tonic-gate 4162*7c478bd9Sstevel@tonic-gate /* check status to determine whether reset completed */ 4163*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 4164*7c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 4165*7c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 4166*7c478bd9Sstevel@tonic-gate GET_PORT_STATUS, 4167*7c478bd9Sstevel@tonic-gate USB_REQ_GET_STATUS, 4168*7c478bd9Sstevel@tonic-gate 0, 4169*7c478bd9Sstevel@tonic-gate port, 4170*7c478bd9Sstevel@tonic-gate GET_STATUS_LENGTH, 4171*7c478bd9Sstevel@tonic-gate &data, 0, 4172*7c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 4173*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_PORT, 4174*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 4175*7c478bd9Sstevel@tonic-gate "get status port%d failed (%d 0x%x %d)", 4176*7c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 4177*7c478bd9Sstevel@tonic-gate 4178*7c478bd9Sstevel@tonic-gate if (data) { 4179*7c478bd9Sstevel@tonic-gate freemsg(data); 4180*7c478bd9Sstevel@tonic-gate data = NULL; 4181*7c478bd9Sstevel@tonic-gate } 4182*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 4183*7c478bd9Sstevel@tonic-gate 4184*7c478bd9Sstevel@tonic-gate continue; 4185*7c478bd9Sstevel@tonic-gate } 4186*7c478bd9Sstevel@tonic-gate 4187*7c478bd9Sstevel@tonic-gate status = (*(data->b_rptr + 1) << 8) | *(data->b_rptr); 4188*7c478bd9Sstevel@tonic-gate change = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2); 4189*7c478bd9Sstevel@tonic-gate 4190*7c478bd9Sstevel@tonic-gate freemsg(data); 4191*7c478bd9Sstevel@tonic-gate 4192*7c478bd9Sstevel@tonic-gate /* continue only if port is still connected */ 4193*7c478bd9Sstevel@tonic-gate if (!(status & PORT_STATUS_CCS)) { 4194*7c478bd9Sstevel@tonic-gate 4195*7c478bd9Sstevel@tonic-gate /* lost connection, set exit condition */ 4196*7c478bd9Sstevel@tonic-gate i = hubd_retry_enumerate; 4197*7c478bd9Sstevel@tonic-gate 4198*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 4199*7c478bd9Sstevel@tonic-gate 4200*7c478bd9Sstevel@tonic-gate break; 4201*7c478bd9Sstevel@tonic-gate } 4202*7c478bd9Sstevel@tonic-gate 4203*7c478bd9Sstevel@tonic-gate if (status & PORT_STATUS_PRS) { 4204*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 4205*7c478bd9Sstevel@tonic-gate "port%d reset active", port); 4206*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 4207*7c478bd9Sstevel@tonic-gate 4208*7c478bd9Sstevel@tonic-gate continue; 4209*7c478bd9Sstevel@tonic-gate } else { 4210*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 4211*7c478bd9Sstevel@tonic-gate "port%d reset inactive", port); 4212*7c478bd9Sstevel@tonic-gate } 4213*7c478bd9Sstevel@tonic-gate 4214*7c478bd9Sstevel@tonic-gate if (change & PORT_CHANGE_PRSC) { 4215*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 4216*7c478bd9Sstevel@tonic-gate "clearing feature CFS_C_PORT_RESET"); 4217*7c478bd9Sstevel@tonic-gate 4218*7c478bd9Sstevel@tonic-gate if (usb_pipe_sync_ctrl_xfer(hubd->h_dip, 4219*7c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 4220*7c478bd9Sstevel@tonic-gate HANDLE_PORT_FEATURE, 4221*7c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 4222*7c478bd9Sstevel@tonic-gate CFS_C_PORT_RESET, 4223*7c478bd9Sstevel@tonic-gate port, 4224*7c478bd9Sstevel@tonic-gate 0, 4225*7c478bd9Sstevel@tonic-gate NULL, 0, 4226*7c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0) != USB_SUCCESS) { 4227*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 4228*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 4229*7c478bd9Sstevel@tonic-gate "clear feature CFS_C_PORT_RESET" 4230*7c478bd9Sstevel@tonic-gate " port%d failed (%d 0x%x %d)", 4231*7c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 4232*7c478bd9Sstevel@tonic-gate } 4233*7c478bd9Sstevel@tonic-gate } 4234*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 4235*7c478bd9Sstevel@tonic-gate 4236*7c478bd9Sstevel@tonic-gate break; 4237*7c478bd9Sstevel@tonic-gate } 4238*7c478bd9Sstevel@tonic-gate 4239*7c478bd9Sstevel@tonic-gate if (i >= hubd_retry_enumerate) { 4240*7c478bd9Sstevel@tonic-gate /* port reset has failed */ 4241*7c478bd9Sstevel@tonic-gate rval = USB_FAILURE; 4242*7c478bd9Sstevel@tonic-gate } 4243*7c478bd9Sstevel@tonic-gate 4244*7c478bd9Sstevel@tonic-gate return (rval); 4245*7c478bd9Sstevel@tonic-gate } 4246*7c478bd9Sstevel@tonic-gate 4247*7c478bd9Sstevel@tonic-gate 4248*7c478bd9Sstevel@tonic-gate /* 4249*7c478bd9Sstevel@tonic-gate * hubd_enable_port: 4250*7c478bd9Sstevel@tonic-gate * this may fail if the hub as been disconnected 4251*7c478bd9Sstevel@tonic-gate */ 4252*7c478bd9Sstevel@tonic-gate static int 4253*7c478bd9Sstevel@tonic-gate hubd_enable_port(hubd_t *hubd, usb_port_t port) 4254*7c478bd9Sstevel@tonic-gate { 4255*7c478bd9Sstevel@tonic-gate int rval; 4256*7c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 4257*7c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 4258*7c478bd9Sstevel@tonic-gate 4259*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 4260*7c478bd9Sstevel@tonic-gate "hubd_enable_port: port=%d", port); 4261*7c478bd9Sstevel@tonic-gate 4262*7c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 4263*7c478bd9Sstevel@tonic-gate 4264*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 4265*7c478bd9Sstevel@tonic-gate 4266*7c478bd9Sstevel@tonic-gate /* Do not issue a SetFeature(PORT_ENABLE) on external hubs */ 4267*7c478bd9Sstevel@tonic-gate if (!usba_is_root_hub(hubd->h_dip)) { 4268*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 4269*7c478bd9Sstevel@tonic-gate 4270*7c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 4271*7c478bd9Sstevel@tonic-gate } 4272*7c478bd9Sstevel@tonic-gate 4273*7c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 4274*7c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 4275*7c478bd9Sstevel@tonic-gate HANDLE_PORT_FEATURE, 4276*7c478bd9Sstevel@tonic-gate USB_REQ_SET_FEATURE, 4277*7c478bd9Sstevel@tonic-gate CFS_PORT_ENABLE, 4278*7c478bd9Sstevel@tonic-gate port, 4279*7c478bd9Sstevel@tonic-gate 0, 4280*7c478bd9Sstevel@tonic-gate NULL, 0, 4281*7c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 4282*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 4283*7c478bd9Sstevel@tonic-gate "enable port%d failed (%d 0x%x %d)", 4284*7c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 4285*7c478bd9Sstevel@tonic-gate } 4286*7c478bd9Sstevel@tonic-gate 4287*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 4288*7c478bd9Sstevel@tonic-gate 4289*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 4290*7c478bd9Sstevel@tonic-gate "enabling port done"); 4291*7c478bd9Sstevel@tonic-gate 4292*7c478bd9Sstevel@tonic-gate return (rval); 4293*7c478bd9Sstevel@tonic-gate } 4294*7c478bd9Sstevel@tonic-gate 4295*7c478bd9Sstevel@tonic-gate 4296*7c478bd9Sstevel@tonic-gate /* 4297*7c478bd9Sstevel@tonic-gate * hubd_disable_port 4298*7c478bd9Sstevel@tonic-gate */ 4299*7c478bd9Sstevel@tonic-gate static int 4300*7c478bd9Sstevel@tonic-gate hubd_disable_port(hubd_t *hubd, usb_port_t port) 4301*7c478bd9Sstevel@tonic-gate { 4302*7c478bd9Sstevel@tonic-gate int rval; 4303*7c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 4304*7c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 4305*7c478bd9Sstevel@tonic-gate 4306*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 4307*7c478bd9Sstevel@tonic-gate "hubd_disable_port: port=%d", port); 4308*7c478bd9Sstevel@tonic-gate 4309*7c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 4310*7c478bd9Sstevel@tonic-gate 4311*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 4312*7c478bd9Sstevel@tonic-gate 4313*7c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 4314*7c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 4315*7c478bd9Sstevel@tonic-gate HANDLE_PORT_FEATURE, 4316*7c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 4317*7c478bd9Sstevel@tonic-gate CFS_PORT_ENABLE, 4318*7c478bd9Sstevel@tonic-gate port, 4319*7c478bd9Sstevel@tonic-gate 0, 4320*7c478bd9Sstevel@tonic-gate NULL, 0, 4321*7c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 4322*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 4323*7c478bd9Sstevel@tonic-gate "disable port%d failed (%d 0x%x %d)", port, 4324*7c478bd9Sstevel@tonic-gate completion_reason, cb_flags, rval); 4325*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 4326*7c478bd9Sstevel@tonic-gate 4327*7c478bd9Sstevel@tonic-gate return (USB_FAILURE); 4328*7c478bd9Sstevel@tonic-gate } 4329*7c478bd9Sstevel@tonic-gate 4330*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 4331*7c478bd9Sstevel@tonic-gate "clearing feature CFS_C_PORT_ENABLE"); 4332*7c478bd9Sstevel@tonic-gate 4333*7c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 4334*7c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 4335*7c478bd9Sstevel@tonic-gate HANDLE_PORT_FEATURE, 4336*7c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 4337*7c478bd9Sstevel@tonic-gate CFS_C_PORT_ENABLE, 4338*7c478bd9Sstevel@tonic-gate port, 4339*7c478bd9Sstevel@tonic-gate 0, 4340*7c478bd9Sstevel@tonic-gate NULL, 0, 4341*7c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 4342*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 4343*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 4344*7c478bd9Sstevel@tonic-gate "clear feature CFS_C_PORT_ENABLE port%d failed " 4345*7c478bd9Sstevel@tonic-gate "(%d 0x%x %d)", 4346*7c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 4347*7c478bd9Sstevel@tonic-gate 4348*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 4349*7c478bd9Sstevel@tonic-gate 4350*7c478bd9Sstevel@tonic-gate return (USB_FAILURE); 4351*7c478bd9Sstevel@tonic-gate } 4352*7c478bd9Sstevel@tonic-gate 4353*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 4354*7c478bd9Sstevel@tonic-gate 4355*7c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 4356*7c478bd9Sstevel@tonic-gate } 4357*7c478bd9Sstevel@tonic-gate 4358*7c478bd9Sstevel@tonic-gate 4359*7c478bd9Sstevel@tonic-gate /* 4360*7c478bd9Sstevel@tonic-gate * hubd_determine_port_status: 4361*7c478bd9Sstevel@tonic-gate */ 4362*7c478bd9Sstevel@tonic-gate static int 4363*7c478bd9Sstevel@tonic-gate hubd_determine_port_status(hubd_t *hubd, usb_port_t port, 4364*7c478bd9Sstevel@tonic-gate uint16_t *status, uint16_t *change, uint_t ack_flag) 4365*7c478bd9Sstevel@tonic-gate { 4366*7c478bd9Sstevel@tonic-gate int rval; 4367*7c478bd9Sstevel@tonic-gate mblk_t *data = NULL; 4368*7c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 4369*7c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 4370*7c478bd9Sstevel@tonic-gate 4371*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 4372*7c478bd9Sstevel@tonic-gate "hubd_determine_port_status: port=%d, state=0x%x ack=0x%x", port, 4373*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port], ack_flag); 4374*7c478bd9Sstevel@tonic-gate 4375*7c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 4376*7c478bd9Sstevel@tonic-gate 4377*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 4378*7c478bd9Sstevel@tonic-gate 4379*7c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 4380*7c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 4381*7c478bd9Sstevel@tonic-gate GET_PORT_STATUS, 4382*7c478bd9Sstevel@tonic-gate USB_REQ_GET_STATUS, 4383*7c478bd9Sstevel@tonic-gate 0, 4384*7c478bd9Sstevel@tonic-gate port, 4385*7c478bd9Sstevel@tonic-gate GET_STATUS_LENGTH, 4386*7c478bd9Sstevel@tonic-gate &data, 0, 4387*7c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 4388*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 4389*7c478bd9Sstevel@tonic-gate "port=%d get status failed (%d 0x%x %d)", 4390*7c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 4391*7c478bd9Sstevel@tonic-gate 4392*7c478bd9Sstevel@tonic-gate if (data) { 4393*7c478bd9Sstevel@tonic-gate freemsg(data); 4394*7c478bd9Sstevel@tonic-gate } 4395*7c478bd9Sstevel@tonic-gate 4396*7c478bd9Sstevel@tonic-gate *status = *change = 0; 4397*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 4398*7c478bd9Sstevel@tonic-gate 4399*7c478bd9Sstevel@tonic-gate return (rval); 4400*7c478bd9Sstevel@tonic-gate } 4401*7c478bd9Sstevel@tonic-gate 4402*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 4403*7c478bd9Sstevel@tonic-gate if ((data->b_wptr - data->b_rptr) != GET_STATUS_LENGTH) { 4404*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 4405*7c478bd9Sstevel@tonic-gate "port %d: length incorrect %d", 4406*7c478bd9Sstevel@tonic-gate port, data->b_wptr - data->b_rptr); 4407*7c478bd9Sstevel@tonic-gate freemsg(data); 4408*7c478bd9Sstevel@tonic-gate *status = *change = 0; 4409*7c478bd9Sstevel@tonic-gate 4410*7c478bd9Sstevel@tonic-gate return (rval); 4411*7c478bd9Sstevel@tonic-gate } 4412*7c478bd9Sstevel@tonic-gate 4413*7c478bd9Sstevel@tonic-gate 4414*7c478bd9Sstevel@tonic-gate *status = (*(data->b_rptr + 1) << 8) | *(data->b_rptr); 4415*7c478bd9Sstevel@tonic-gate *change = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2); 4416*7c478bd9Sstevel@tonic-gate 4417*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 4418*7c478bd9Sstevel@tonic-gate "port%d status=0x%x, change=0x%x", port, *status, *change); 4419*7c478bd9Sstevel@tonic-gate 4420*7c478bd9Sstevel@tonic-gate freemsg(data); 4421*7c478bd9Sstevel@tonic-gate 4422*7c478bd9Sstevel@tonic-gate if (*status & PORT_STATUS_CCS) { 4423*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 4424*7c478bd9Sstevel@tonic-gate "port%d connected", port); 4425*7c478bd9Sstevel@tonic-gate 4426*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_CCS & ack_flag); 4427*7c478bd9Sstevel@tonic-gate } else { 4428*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 4429*7c478bd9Sstevel@tonic-gate "port%d disconnected", port); 4430*7c478bd9Sstevel@tonic-gate 4431*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_CCS & ack_flag); 4432*7c478bd9Sstevel@tonic-gate } 4433*7c478bd9Sstevel@tonic-gate 4434*7c478bd9Sstevel@tonic-gate if (*status & PORT_STATUS_PES) { 4435*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 4436*7c478bd9Sstevel@tonic-gate "port%d enabled", port); 4437*7c478bd9Sstevel@tonic-gate 4438*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_PES & ack_flag); 4439*7c478bd9Sstevel@tonic-gate } else { 4440*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 4441*7c478bd9Sstevel@tonic-gate "port%d disabled", port); 4442*7c478bd9Sstevel@tonic-gate 4443*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_PES & ack_flag); 4444*7c478bd9Sstevel@tonic-gate } 4445*7c478bd9Sstevel@tonic-gate 4446*7c478bd9Sstevel@tonic-gate if (*status & PORT_STATUS_PSS) { 4447*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 4448*7c478bd9Sstevel@tonic-gate "port%d suspended", port); 4449*7c478bd9Sstevel@tonic-gate 4450*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_PSS & ack_flag); 4451*7c478bd9Sstevel@tonic-gate } else { 4452*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 4453*7c478bd9Sstevel@tonic-gate "port%d not suspended", port); 4454*7c478bd9Sstevel@tonic-gate 4455*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_PSS & ack_flag); 4456*7c478bd9Sstevel@tonic-gate } 4457*7c478bd9Sstevel@tonic-gate 4458*7c478bd9Sstevel@tonic-gate if (*change & PORT_CHANGE_PRSC) { 4459*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 4460*7c478bd9Sstevel@tonic-gate "port%d reset completed", port); 4461*7c478bd9Sstevel@tonic-gate 4462*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_CHANGE_PRSC & ack_flag); 4463*7c478bd9Sstevel@tonic-gate } else { 4464*7c478bd9Sstevel@tonic-gate 4465*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_CHANGE_PRSC & ack_flag); 4466*7c478bd9Sstevel@tonic-gate } 4467*7c478bd9Sstevel@tonic-gate 4468*7c478bd9Sstevel@tonic-gate if (*status & PORT_STATUS_POCI) { 4469*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_PORT, hubd->h_log_handle, 4470*7c478bd9Sstevel@tonic-gate "port%d over current!", port); 4471*7c478bd9Sstevel@tonic-gate 4472*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_POCI & ack_flag); 4473*7c478bd9Sstevel@tonic-gate } else { 4474*7c478bd9Sstevel@tonic-gate 4475*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_POCI & ack_flag); 4476*7c478bd9Sstevel@tonic-gate } 4477*7c478bd9Sstevel@tonic-gate 4478*7c478bd9Sstevel@tonic-gate if (*status & PORT_STATUS_PRS) { 4479*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 4480*7c478bd9Sstevel@tonic-gate "port%d reset active", port); 4481*7c478bd9Sstevel@tonic-gate 4482*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_PRS & ack_flag); 4483*7c478bd9Sstevel@tonic-gate } else { 4484*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 4485*7c478bd9Sstevel@tonic-gate "port%d reset inactive", port); 4486*7c478bd9Sstevel@tonic-gate 4487*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_PRS & ack_flag); 4488*7c478bd9Sstevel@tonic-gate } 4489*7c478bd9Sstevel@tonic-gate if (*status & PORT_STATUS_PPS) { 4490*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 4491*7c478bd9Sstevel@tonic-gate "port%d power on", port); 4492*7c478bd9Sstevel@tonic-gate 4493*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_PPS & ack_flag); 4494*7c478bd9Sstevel@tonic-gate } else { 4495*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 4496*7c478bd9Sstevel@tonic-gate "port%d power off", port); 4497*7c478bd9Sstevel@tonic-gate 4498*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_PPS & ack_flag); 4499*7c478bd9Sstevel@tonic-gate } 4500*7c478bd9Sstevel@tonic-gate if (*status & PORT_STATUS_LSDA) { 4501*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 4502*7c478bd9Sstevel@tonic-gate "port%d low speed", port); 4503*7c478bd9Sstevel@tonic-gate 4504*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_LSDA & ack_flag); 4505*7c478bd9Sstevel@tonic-gate } else { 4506*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_LSDA & ack_flag); 4507*7c478bd9Sstevel@tonic-gate if (*status & PORT_STATUS_HSDA) { 4508*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, 4509*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, "port%d " 4510*7c478bd9Sstevel@tonic-gate "high speed", port); 4511*7c478bd9Sstevel@tonic-gate 4512*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= 4513*7c478bd9Sstevel@tonic-gate (PORT_STATUS_HSDA & ack_flag); 4514*7c478bd9Sstevel@tonic-gate } else { 4515*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, 4516*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, "port%d " 4517*7c478bd9Sstevel@tonic-gate "full speed", port); 4518*7c478bd9Sstevel@tonic-gate 4519*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= 4520*7c478bd9Sstevel@tonic-gate ~(PORT_STATUS_HSDA & ack_flag); 4521*7c478bd9Sstevel@tonic-gate } 4522*7c478bd9Sstevel@tonic-gate } 4523*7c478bd9Sstevel@tonic-gate 4524*7c478bd9Sstevel@tonic-gate /* 4525*7c478bd9Sstevel@tonic-gate * Acknowledge connection, enable, reset status 4526*7c478bd9Sstevel@tonic-gate */ 4527*7c478bd9Sstevel@tonic-gate if (ack_flag) { 4528*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 4529*7c478bd9Sstevel@tonic-gate if (*change & PORT_CHANGE_CSC & ack_flag) { 4530*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 4531*7c478bd9Sstevel@tonic-gate "clearing feature CFS_C_PORT_CONNECTION"); 4532*7c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 4533*7c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 4534*7c478bd9Sstevel@tonic-gate HANDLE_PORT_FEATURE, 4535*7c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 4536*7c478bd9Sstevel@tonic-gate CFS_C_PORT_CONNECTION, 4537*7c478bd9Sstevel@tonic-gate port, 4538*7c478bd9Sstevel@tonic-gate 0, NULL, 0, 4539*7c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != 4540*7c478bd9Sstevel@tonic-gate USB_SUCCESS) { 4541*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 4542*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 4543*7c478bd9Sstevel@tonic-gate "clear feature CFS_C_PORT_CONNECTION" 4544*7c478bd9Sstevel@tonic-gate " port%d failed (%d 0x%x %d)", 4545*7c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 4546*7c478bd9Sstevel@tonic-gate } 4547*7c478bd9Sstevel@tonic-gate } 4548*7c478bd9Sstevel@tonic-gate if (*change & PORT_CHANGE_PESC & ack_flag) { 4549*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 4550*7c478bd9Sstevel@tonic-gate "clearing feature CFS_C_PORT_ENABLE"); 4551*7c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 4552*7c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 4553*7c478bd9Sstevel@tonic-gate HANDLE_PORT_FEATURE, 4554*7c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 4555*7c478bd9Sstevel@tonic-gate CFS_C_PORT_ENABLE, 4556*7c478bd9Sstevel@tonic-gate port, 4557*7c478bd9Sstevel@tonic-gate 0, NULL, 0, 4558*7c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != 4559*7c478bd9Sstevel@tonic-gate USB_SUCCESS) { 4560*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 4561*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 4562*7c478bd9Sstevel@tonic-gate "clear feature CFS_C_PORT_ENABLE" 4563*7c478bd9Sstevel@tonic-gate " port%d failed (%d 0x%x %d)", 4564*7c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 4565*7c478bd9Sstevel@tonic-gate } 4566*7c478bd9Sstevel@tonic-gate } 4567*7c478bd9Sstevel@tonic-gate if (*change & PORT_CHANGE_PSSC & ack_flag) { 4568*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 4569*7c478bd9Sstevel@tonic-gate "clearing feature CFS_C_PORT_SUSPEND"); 4570*7c478bd9Sstevel@tonic-gate 4571*7c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 4572*7c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 4573*7c478bd9Sstevel@tonic-gate HANDLE_PORT_FEATURE, 4574*7c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 4575*7c478bd9Sstevel@tonic-gate CFS_C_PORT_SUSPEND, 4576*7c478bd9Sstevel@tonic-gate port, 4577*7c478bd9Sstevel@tonic-gate 0, NULL, 0, 4578*7c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != 4579*7c478bd9Sstevel@tonic-gate USB_SUCCESS) { 4580*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 4581*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 4582*7c478bd9Sstevel@tonic-gate "clear feature CFS_C_PORT_SUSPEND" 4583*7c478bd9Sstevel@tonic-gate " port%d failed (%d 0x%x %d)", 4584*7c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 4585*7c478bd9Sstevel@tonic-gate } 4586*7c478bd9Sstevel@tonic-gate } 4587*7c478bd9Sstevel@tonic-gate if (*change & PORT_CHANGE_OCIC & ack_flag) { 4588*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 4589*7c478bd9Sstevel@tonic-gate "clearing feature CFS_C_PORT_OVER_CURRENT"); 4590*7c478bd9Sstevel@tonic-gate 4591*7c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 4592*7c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 4593*7c478bd9Sstevel@tonic-gate HANDLE_PORT_FEATURE, 4594*7c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 4595*7c478bd9Sstevel@tonic-gate CFS_C_PORT_OVER_CURRENT, 4596*7c478bd9Sstevel@tonic-gate port, 4597*7c478bd9Sstevel@tonic-gate 0, NULL, 0, 4598*7c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != 4599*7c478bd9Sstevel@tonic-gate USB_SUCCESS) { 4600*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 4601*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 4602*7c478bd9Sstevel@tonic-gate "clear feature CFS_C_PORT_OVER_CURRENT" 4603*7c478bd9Sstevel@tonic-gate " port%d failed (%d 0x%x %d)", 4604*7c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 4605*7c478bd9Sstevel@tonic-gate } 4606*7c478bd9Sstevel@tonic-gate } 4607*7c478bd9Sstevel@tonic-gate if (*change & PORT_CHANGE_PRSC & ack_flag) { 4608*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 4609*7c478bd9Sstevel@tonic-gate "clearing feature CFS_C_PORT_RESET"); 4610*7c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 4611*7c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 4612*7c478bd9Sstevel@tonic-gate HANDLE_PORT_FEATURE, 4613*7c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 4614*7c478bd9Sstevel@tonic-gate CFS_C_PORT_RESET, 4615*7c478bd9Sstevel@tonic-gate port, 4616*7c478bd9Sstevel@tonic-gate 0, NULL, 0, 4617*7c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != 4618*7c478bd9Sstevel@tonic-gate USB_SUCCESS) { 4619*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 4620*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 4621*7c478bd9Sstevel@tonic-gate "clear feature CFS_C_PORT_RESET" 4622*7c478bd9Sstevel@tonic-gate " port%d failed (%d 0x%x %d)", 4623*7c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 4624*7c478bd9Sstevel@tonic-gate } 4625*7c478bd9Sstevel@tonic-gate } 4626*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 4627*7c478bd9Sstevel@tonic-gate } 4628*7c478bd9Sstevel@tonic-gate 4629*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 4630*7c478bd9Sstevel@tonic-gate "new port%d state 0x%x", port, hubd->h_port_state[port]); 4631*7c478bd9Sstevel@tonic-gate 4632*7c478bd9Sstevel@tonic-gate 4633*7c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 4634*7c478bd9Sstevel@tonic-gate } 4635*7c478bd9Sstevel@tonic-gate 4636*7c478bd9Sstevel@tonic-gate 4637*7c478bd9Sstevel@tonic-gate /* 4638*7c478bd9Sstevel@tonic-gate * hubd_recover_disabled_port 4639*7c478bd9Sstevel@tonic-gate * if the port got disabled because of an error 4640*7c478bd9Sstevel@tonic-gate * enable it. If hub doesn't suport enable port, 4641*7c478bd9Sstevel@tonic-gate * reset the port to bring the device to life again 4642*7c478bd9Sstevel@tonic-gate */ 4643*7c478bd9Sstevel@tonic-gate static int 4644*7c478bd9Sstevel@tonic-gate hubd_recover_disabled_port(hubd_t *hubd, usb_port_t port) 4645*7c478bd9Sstevel@tonic-gate { 4646*7c478bd9Sstevel@tonic-gate uint16_t status; 4647*7c478bd9Sstevel@tonic-gate uint16_t change; 4648*7c478bd9Sstevel@tonic-gate int rval = USB_FAILURE; 4649*7c478bd9Sstevel@tonic-gate 4650*7c478bd9Sstevel@tonic-gate /* first try enabling the port */ 4651*7c478bd9Sstevel@tonic-gate (void) hubd_enable_port(hubd, port); 4652*7c478bd9Sstevel@tonic-gate 4653*7c478bd9Sstevel@tonic-gate /* read the port status */ 4654*7c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, &status, &change, 4655*7c478bd9Sstevel@tonic-gate PORT_CHANGE_PESC); 4656*7c478bd9Sstevel@tonic-gate 4657*7c478bd9Sstevel@tonic-gate if (status & PORT_STATUS_PES) { 4658*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 4659*7c478bd9Sstevel@tonic-gate "Port%d now Enabled", port); 4660*7c478bd9Sstevel@tonic-gate } else if (status & PORT_STATUS_CCS) { 4661*7c478bd9Sstevel@tonic-gate /* first post a disconnect event to the child */ 4662*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 4663*7c478bd9Sstevel@tonic-gate hubd_post_event(hubd, port, USBA_EVENT_TAG_HOT_REMOVAL); 4664*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 4665*7c478bd9Sstevel@tonic-gate 4666*7c478bd9Sstevel@tonic-gate /* then reset the port and recover the device */ 4667*7c478bd9Sstevel@tonic-gate rval = hubd_handle_port_connect(hubd, port); 4668*7c478bd9Sstevel@tonic-gate 4669*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 4670*7c478bd9Sstevel@tonic-gate "Port%d now Enabled by force", port); 4671*7c478bd9Sstevel@tonic-gate } 4672*7c478bd9Sstevel@tonic-gate 4673*7c478bd9Sstevel@tonic-gate return (rval); 4674*7c478bd9Sstevel@tonic-gate } 4675*7c478bd9Sstevel@tonic-gate 4676*7c478bd9Sstevel@tonic-gate 4677*7c478bd9Sstevel@tonic-gate /* 4678*7c478bd9Sstevel@tonic-gate * hubd_enable_all_port_power: 4679*7c478bd9Sstevel@tonic-gate */ 4680*7c478bd9Sstevel@tonic-gate static int 4681*7c478bd9Sstevel@tonic-gate hubd_enable_all_port_power(hubd_t *hubd) 4682*7c478bd9Sstevel@tonic-gate { 4683*7c478bd9Sstevel@tonic-gate usb_hub_descr_t *hub_descr; 4684*7c478bd9Sstevel@tonic-gate int wait; 4685*7c478bd9Sstevel@tonic-gate usb_port_t port; 4686*7c478bd9Sstevel@tonic-gate uint_t retry; 4687*7c478bd9Sstevel@tonic-gate uint16_t status; 4688*7c478bd9Sstevel@tonic-gate uint16_t change; 4689*7c478bd9Sstevel@tonic-gate 4690*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 4691*7c478bd9Sstevel@tonic-gate "hubd_enable_all_port_power"); 4692*7c478bd9Sstevel@tonic-gate 4693*7c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 4694*7c478bd9Sstevel@tonic-gate 4695*7c478bd9Sstevel@tonic-gate hub_descr = &hubd->h_hub_descr; 4696*7c478bd9Sstevel@tonic-gate 4697*7c478bd9Sstevel@tonic-gate /* 4698*7c478bd9Sstevel@tonic-gate * According to section 11.11 of USB, for hubs with no power 4699*7c478bd9Sstevel@tonic-gate * switches, bPwrOn2PwrGood is zero. But we wait for some 4700*7c478bd9Sstevel@tonic-gate * arbitrary time to enable power to become stable. 4701*7c478bd9Sstevel@tonic-gate * 4702*7c478bd9Sstevel@tonic-gate * If an hub supports port power switching, we need to wait 4703*7c478bd9Sstevel@tonic-gate * at least 20ms before accessing corresponding usb port. 4704*7c478bd9Sstevel@tonic-gate */ 4705*7c478bd9Sstevel@tonic-gate if ((hub_descr->wHubCharacteristics & 4706*7c478bd9Sstevel@tonic-gate HUB_CHARS_NO_POWER_SWITCHING) || (!hub_descr->bPwrOn2PwrGood)) { 4707*7c478bd9Sstevel@tonic-gate wait = hubd_device_delay / 10; 4708*7c478bd9Sstevel@tonic-gate } else { 4709*7c478bd9Sstevel@tonic-gate wait = max(HUB_DEFAULT_POPG, 4710*7c478bd9Sstevel@tonic-gate hub_descr->bPwrOn2PwrGood) * 2 * 1000; 4711*7c478bd9Sstevel@tonic-gate } 4712*7c478bd9Sstevel@tonic-gate 4713*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 4714*7c478bd9Sstevel@tonic-gate "hubd_enable_all_port_power: popg=%d wait=%d", 4715*7c478bd9Sstevel@tonic-gate hub_descr->bPwrOn2PwrGood, wait); 4716*7c478bd9Sstevel@tonic-gate 4717*7c478bd9Sstevel@tonic-gate /* 4718*7c478bd9Sstevel@tonic-gate * Enable power per port. we ignore gang power and power mask 4719*7c478bd9Sstevel@tonic-gate * and always enable all ports one by one. 4720*7c478bd9Sstevel@tonic-gate */ 4721*7c478bd9Sstevel@tonic-gate for (port = 1; port <= hub_descr->bNbrPorts; port++) { 4722*7c478bd9Sstevel@tonic-gate /* 4723*7c478bd9Sstevel@tonic-gate * Transition the port from the Powered Off to the 4724*7c478bd9Sstevel@tonic-gate * Disconnected state by supplying power to the port. 4725*7c478bd9Sstevel@tonic-gate */ 4726*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, 4727*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 4728*7c478bd9Sstevel@tonic-gate "hubd_enable_all_port_power: power port=%d", port); 4729*7c478bd9Sstevel@tonic-gate 4730*7c478bd9Sstevel@tonic-gate (void) hubd_enable_port_power(hubd, port); 4731*7c478bd9Sstevel@tonic-gate } 4732*7c478bd9Sstevel@tonic-gate 4733*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 4734*7c478bd9Sstevel@tonic-gate delay(drv_usectohz(wait)); 4735*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 4736*7c478bd9Sstevel@tonic-gate 4737*7c478bd9Sstevel@tonic-gate /* For retry if any, use some extra delay */ 4738*7c478bd9Sstevel@tonic-gate wait = max(wait, hubd_device_delay / 10); 4739*7c478bd9Sstevel@tonic-gate 4740*7c478bd9Sstevel@tonic-gate /* Check each port power status for a given usb hub */ 4741*7c478bd9Sstevel@tonic-gate for (port = 1; port <= hub_descr->bNbrPorts; port++) { 4742*7c478bd9Sstevel@tonic-gate 4743*7c478bd9Sstevel@tonic-gate /* Get port status */ 4744*7c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 4745*7c478bd9Sstevel@tonic-gate &status, &change, 0); 4746*7c478bd9Sstevel@tonic-gate 4747*7c478bd9Sstevel@tonic-gate for (retry = 0; ((!(status & PORT_STATUS_PPS)) && 4748*7c478bd9Sstevel@tonic-gate (retry < HUBD_PORT_RETRY)); retry++) { 4749*7c478bd9Sstevel@tonic-gate 4750*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 4751*7c478bd9Sstevel@tonic-gate "Retry is in progress %d: port %d status %d", 4752*7c478bd9Sstevel@tonic-gate retry, port, status); 4753*7c478bd9Sstevel@tonic-gate 4754*7c478bd9Sstevel@tonic-gate (void) hubd_enable_port_power(hubd, port); 4755*7c478bd9Sstevel@tonic-gate 4756*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 4757*7c478bd9Sstevel@tonic-gate delay(drv_usectohz(wait)); 4758*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 4759*7c478bd9Sstevel@tonic-gate 4760*7c478bd9Sstevel@tonic-gate /* Get port status */ 4761*7c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 4762*7c478bd9Sstevel@tonic-gate &status, &change, 0); 4763*7c478bd9Sstevel@tonic-gate } 4764*7c478bd9Sstevel@tonic-gate 4765*7c478bd9Sstevel@tonic-gate /* Print warning message if port has no power */ 4766*7c478bd9Sstevel@tonic-gate if (!(status & PORT_STATUS_PPS)) { 4767*7c478bd9Sstevel@tonic-gate 4768*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_PORT, hubd->h_log_handle, 4769*7c478bd9Sstevel@tonic-gate "hubd_enable_all_port_power: port %d power-on " 4770*7c478bd9Sstevel@tonic-gate "failed, port status 0x%x", port, status); 4771*7c478bd9Sstevel@tonic-gate } 4772*7c478bd9Sstevel@tonic-gate } 4773*7c478bd9Sstevel@tonic-gate 4774*7c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 4775*7c478bd9Sstevel@tonic-gate } 4776*7c478bd9Sstevel@tonic-gate 4777*7c478bd9Sstevel@tonic-gate 4778*7c478bd9Sstevel@tonic-gate /* 4779*7c478bd9Sstevel@tonic-gate * hubd_enable_port_power: 4780*7c478bd9Sstevel@tonic-gate * enable individual port power 4781*7c478bd9Sstevel@tonic-gate */ 4782*7c478bd9Sstevel@tonic-gate static int 4783*7c478bd9Sstevel@tonic-gate hubd_enable_port_power(hubd_t *hubd, usb_port_t port) 4784*7c478bd9Sstevel@tonic-gate { 4785*7c478bd9Sstevel@tonic-gate int rval; 4786*7c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 4787*7c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 4788*7c478bd9Sstevel@tonic-gate 4789*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 4790*7c478bd9Sstevel@tonic-gate "hubd_enable_port_power: port=%d", port); 4791*7c478bd9Sstevel@tonic-gate 4792*7c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 4793*7c478bd9Sstevel@tonic-gate ASSERT(hubd->h_default_pipe != 0); 4794*7c478bd9Sstevel@tonic-gate 4795*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 4796*7c478bd9Sstevel@tonic-gate 4797*7c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 4798*7c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 4799*7c478bd9Sstevel@tonic-gate HANDLE_PORT_FEATURE, 4800*7c478bd9Sstevel@tonic-gate USB_REQ_SET_FEATURE, 4801*7c478bd9Sstevel@tonic-gate CFS_PORT_POWER, 4802*7c478bd9Sstevel@tonic-gate port, 4803*7c478bd9Sstevel@tonic-gate 0, NULL, 0, 4804*7c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 4805*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 4806*7c478bd9Sstevel@tonic-gate "set port power failed (%d 0x%x %d)", 4807*7c478bd9Sstevel@tonic-gate completion_reason, cb_flags, rval); 4808*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 4809*7c478bd9Sstevel@tonic-gate 4810*7c478bd9Sstevel@tonic-gate return (USB_FAILURE); 4811*7c478bd9Sstevel@tonic-gate } else { 4812*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 4813*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= PORT_STATUS_PPS; 4814*7c478bd9Sstevel@tonic-gate 4815*7c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 4816*7c478bd9Sstevel@tonic-gate } 4817*7c478bd9Sstevel@tonic-gate } 4818*7c478bd9Sstevel@tonic-gate 4819*7c478bd9Sstevel@tonic-gate 4820*7c478bd9Sstevel@tonic-gate /* 4821*7c478bd9Sstevel@tonic-gate * hubd_disable_all_port_power: 4822*7c478bd9Sstevel@tonic-gate */ 4823*7c478bd9Sstevel@tonic-gate static int 4824*7c478bd9Sstevel@tonic-gate hubd_disable_all_port_power(hubd_t *hubd) 4825*7c478bd9Sstevel@tonic-gate { 4826*7c478bd9Sstevel@tonic-gate usb_port_t port; 4827*7c478bd9Sstevel@tonic-gate 4828*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 4829*7c478bd9Sstevel@tonic-gate "hubd_disable_all_port_power"); 4830*7c478bd9Sstevel@tonic-gate 4831*7c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 4832*7c478bd9Sstevel@tonic-gate 4833*7c478bd9Sstevel@tonic-gate /* 4834*7c478bd9Sstevel@tonic-gate * disable power per port, ignore gang power and power mask 4835*7c478bd9Sstevel@tonic-gate */ 4836*7c478bd9Sstevel@tonic-gate for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { 4837*7c478bd9Sstevel@tonic-gate (void) hubd_disable_port_power(hubd, port); 4838*7c478bd9Sstevel@tonic-gate } 4839*7c478bd9Sstevel@tonic-gate 4840*7c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 4841*7c478bd9Sstevel@tonic-gate } 4842*7c478bd9Sstevel@tonic-gate 4843*7c478bd9Sstevel@tonic-gate 4844*7c478bd9Sstevel@tonic-gate /* 4845*7c478bd9Sstevel@tonic-gate * hubd_disable_port_power: 4846*7c478bd9Sstevel@tonic-gate * disable individual port power 4847*7c478bd9Sstevel@tonic-gate */ 4848*7c478bd9Sstevel@tonic-gate static int 4849*7c478bd9Sstevel@tonic-gate hubd_disable_port_power(hubd_t *hubd, usb_port_t port) 4850*7c478bd9Sstevel@tonic-gate { 4851*7c478bd9Sstevel@tonic-gate int rval; 4852*7c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 4853*7c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 4854*7c478bd9Sstevel@tonic-gate 4855*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 4856*7c478bd9Sstevel@tonic-gate "hubd_disable_port_power: port=%d", port); 4857*7c478bd9Sstevel@tonic-gate 4858*7c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 4859*7c478bd9Sstevel@tonic-gate 4860*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 4861*7c478bd9Sstevel@tonic-gate 4862*7c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 4863*7c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 4864*7c478bd9Sstevel@tonic-gate HANDLE_PORT_FEATURE, 4865*7c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 4866*7c478bd9Sstevel@tonic-gate CFS_PORT_POWER, 4867*7c478bd9Sstevel@tonic-gate port, 4868*7c478bd9Sstevel@tonic-gate 0, NULL, 0, 4869*7c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 4870*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 4871*7c478bd9Sstevel@tonic-gate "clearing port%d power failed (%d 0x%x %d)", 4872*7c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 4873*7c478bd9Sstevel@tonic-gate 4874*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 4875*7c478bd9Sstevel@tonic-gate 4876*7c478bd9Sstevel@tonic-gate return (USB_FAILURE); 4877*7c478bd9Sstevel@tonic-gate } else { 4878*7c478bd9Sstevel@tonic-gate 4879*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 4880*7c478bd9Sstevel@tonic-gate ASSERT(completion_reason == 0); 4881*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~PORT_STATUS_PPS; 4882*7c478bd9Sstevel@tonic-gate 4883*7c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 4884*7c478bd9Sstevel@tonic-gate } 4885*7c478bd9Sstevel@tonic-gate } 4886*7c478bd9Sstevel@tonic-gate 4887*7c478bd9Sstevel@tonic-gate 4888*7c478bd9Sstevel@tonic-gate /* 4889*7c478bd9Sstevel@tonic-gate * Search the database of user preferences and find out the preferred 4890*7c478bd9Sstevel@tonic-gate * configuration for this new device 4891*7c478bd9Sstevel@tonic-gate */ 4892*7c478bd9Sstevel@tonic-gate static int 4893*7c478bd9Sstevel@tonic-gate hubd_select_device_configuration(hubd_t *hubd, usb_port_t port, 4894*7c478bd9Sstevel@tonic-gate dev_info_t *child_dip, usba_device_t *child_ud) 4895*7c478bd9Sstevel@tonic-gate { 4896*7c478bd9Sstevel@tonic-gate char *pathname = NULL; 4897*7c478bd9Sstevel@tonic-gate char *tmp_path = NULL; 4898*7c478bd9Sstevel@tonic-gate int user_conf; 4899*7c478bd9Sstevel@tonic-gate int pathlen; 4900*7c478bd9Sstevel@tonic-gate usb_dev_descr_t *usbdev_ptr; 4901*7c478bd9Sstevel@tonic-gate usba_configrec_t *user_pref; 4902*7c478bd9Sstevel@tonic-gate 4903*7c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 4904*7c478bd9Sstevel@tonic-gate usbdev_ptr = child_ud->usb_dev_descr; 4905*7c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 4906*7c478bd9Sstevel@tonic-gate 4907*7c478bd9Sstevel@tonic-gate /* try to get pathname for this device */ 4908*7c478bd9Sstevel@tonic-gate tmp_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 4909*7c478bd9Sstevel@tonic-gate (void) ddi_pathname(child_dip, tmp_path); 4910*7c478bd9Sstevel@tonic-gate 4911*7c478bd9Sstevel@tonic-gate pathlen = strlen(tmp_path) + 32; 4912*7c478bd9Sstevel@tonic-gate pathname = kmem_zalloc(pathlen, KM_SLEEP); 4913*7c478bd9Sstevel@tonic-gate 4914*7c478bd9Sstevel@tonic-gate /* 4915*7c478bd9Sstevel@tonic-gate * We haven't initialized the node and it doesn't have an address 4916*7c478bd9Sstevel@tonic-gate * yet. Append port number to the physical pathname 4917*7c478bd9Sstevel@tonic-gate */ 4918*7c478bd9Sstevel@tonic-gate (void) sprintf(pathname, "%s@%d", tmp_path, port); 4919*7c478bd9Sstevel@tonic-gate 4920*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 4921*7c478bd9Sstevel@tonic-gate "hubd_select_device_configuration: Device=%s\n\t" 4922*7c478bd9Sstevel@tonic-gate "Child path=%s", 4923*7c478bd9Sstevel@tonic-gate usba_get_mfg_prod_sn_str(child_dip, tmp_path, MAXPATHLEN), 4924*7c478bd9Sstevel@tonic-gate pathname); 4925*7c478bd9Sstevel@tonic-gate kmem_free(tmp_path, MAXPATHLEN); 4926*7c478bd9Sstevel@tonic-gate 4927*7c478bd9Sstevel@tonic-gate 4928*7c478bd9Sstevel@tonic-gate /* database search for user preferences */ 4929*7c478bd9Sstevel@tonic-gate user_pref = usba_devdb_get_user_preferences(usbdev_ptr->idVendor, 4930*7c478bd9Sstevel@tonic-gate usbdev_ptr->idProduct, child_ud->usb_serialno_str, pathname); 4931*7c478bd9Sstevel@tonic-gate 4932*7c478bd9Sstevel@tonic-gate if (user_pref) { 4933*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 4934*7c478bd9Sstevel@tonic-gate "hubd_select_device_configuration: " 4935*7c478bd9Sstevel@tonic-gate "usba_devdb_get_user_preferences " 4936*7c478bd9Sstevel@tonic-gate "return user_conf=%d\npreferred driver=%s path=%s", 4937*7c478bd9Sstevel@tonic-gate user_pref->cfg_index, user_pref->driver, 4938*7c478bd9Sstevel@tonic-gate user_pref->pathname); 4939*7c478bd9Sstevel@tonic-gate 4940*7c478bd9Sstevel@tonic-gate user_conf = user_pref->cfg_index; 4941*7c478bd9Sstevel@tonic-gate 4942*7c478bd9Sstevel@tonic-gate if (user_pref->driver) { 4943*7c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 4944*7c478bd9Sstevel@tonic-gate child_ud->usb_preferred_driver = user_pref->driver; 4945*7c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 4946*7c478bd9Sstevel@tonic-gate } 4947*7c478bd9Sstevel@tonic-gate } else { 4948*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 4949*7c478bd9Sstevel@tonic-gate "hubd_select_device_configuration: No match found"); 4950*7c478bd9Sstevel@tonic-gate 4951*7c478bd9Sstevel@tonic-gate /* select default configuration for this device */ 4952*7c478bd9Sstevel@tonic-gate user_conf = USBA_DEV_CONFIG_INDEX_UNDEFINED; 4953*7c478bd9Sstevel@tonic-gate } 4954*7c478bd9Sstevel@tonic-gate kmem_free(pathname, pathlen); 4955*7c478bd9Sstevel@tonic-gate 4956*7c478bd9Sstevel@tonic-gate /* if the device has just one configuration, set default value */ 4957*7c478bd9Sstevel@tonic-gate if (usbdev_ptr->bNumConfigurations == 1) { 4958*7c478bd9Sstevel@tonic-gate user_conf = USB_DEV_DEFAULT_CONFIG_INDEX; 4959*7c478bd9Sstevel@tonic-gate } 4960*7c478bd9Sstevel@tonic-gate 4961*7c478bd9Sstevel@tonic-gate return (user_conf); 4962*7c478bd9Sstevel@tonic-gate } 4963*7c478bd9Sstevel@tonic-gate 4964*7c478bd9Sstevel@tonic-gate 4965*7c478bd9Sstevel@tonic-gate /* 4966*7c478bd9Sstevel@tonic-gate * Retrieves config cloud for this configuration 4967*7c478bd9Sstevel@tonic-gate */ 4968*7c478bd9Sstevel@tonic-gate int 4969*7c478bd9Sstevel@tonic-gate hubd_get_this_config_cloud(hubd_t *hubd, dev_info_t *dip, 4970*7c478bd9Sstevel@tonic-gate usba_device_t *child_ud, uint16_t conf_index) 4971*7c478bd9Sstevel@tonic-gate { 4972*7c478bd9Sstevel@tonic-gate usb_cfg_descr_t *confdescr; 4973*7c478bd9Sstevel@tonic-gate mblk_t *pdata = NULL; 4974*7c478bd9Sstevel@tonic-gate int rval; 4975*7c478bd9Sstevel@tonic-gate size_t size; 4976*7c478bd9Sstevel@tonic-gate char *tmpbuf; 4977*7c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 4978*7c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 4979*7c478bd9Sstevel@tonic-gate usb_pipe_handle_t def_ph; 4980*7c478bd9Sstevel@tonic-gate 4981*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 4982*7c478bd9Sstevel@tonic-gate "hubd_get_this_config_cloud: conf_index=%d", conf_index); 4983*7c478bd9Sstevel@tonic-gate 4984*7c478bd9Sstevel@tonic-gate 4985*7c478bd9Sstevel@tonic-gate /* alloc temporary space for config descriptor */ 4986*7c478bd9Sstevel@tonic-gate confdescr = (usb_cfg_descr_t *)kmem_zalloc(USB_CFG_DESCR_SIZE, 4987*7c478bd9Sstevel@tonic-gate KM_SLEEP); 4988*7c478bd9Sstevel@tonic-gate 4989*7c478bd9Sstevel@tonic-gate /* alloc temporary space for string descriptor */ 4990*7c478bd9Sstevel@tonic-gate tmpbuf = kmem_zalloc(USB_MAXSTRINGLEN, KM_SLEEP); 4991*7c478bd9Sstevel@tonic-gate 4992*7c478bd9Sstevel@tonic-gate def_ph = usba_get_dflt_pipe_handle(dip); 4993*7c478bd9Sstevel@tonic-gate 4994*7c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(dip, def_ph, 4995*7c478bd9Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD, 4996*7c478bd9Sstevel@tonic-gate USB_REQ_GET_DESCR, 4997*7c478bd9Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_CFG | conf_index, 4998*7c478bd9Sstevel@tonic-gate 0, 4999*7c478bd9Sstevel@tonic-gate USB_CFG_DESCR_SIZE, 5000*7c478bd9Sstevel@tonic-gate &pdata, 5001*7c478bd9Sstevel@tonic-gate 0, 5002*7c478bd9Sstevel@tonic-gate &completion_reason, 5003*7c478bd9Sstevel@tonic-gate &cb_flags, 5004*7c478bd9Sstevel@tonic-gate 0)) == USB_SUCCESS) { 5005*7c478bd9Sstevel@tonic-gate 5006*7c478bd9Sstevel@tonic-gate /* this must be true since we didn't allow data underruns */ 5007*7c478bd9Sstevel@tonic-gate ASSERT((pdata->b_wptr - pdata->b_rptr) == USB_CFG_DESCR_SIZE); 5008*7c478bd9Sstevel@tonic-gate 5009*7c478bd9Sstevel@tonic-gate /* 5010*7c478bd9Sstevel@tonic-gate * Parse the configuration descriptor 5011*7c478bd9Sstevel@tonic-gate */ 5012*7c478bd9Sstevel@tonic-gate size = usb_parse_cfg_descr(pdata->b_rptr, 5013*7c478bd9Sstevel@tonic-gate pdata->b_wptr - pdata->b_rptr, confdescr, 5014*7c478bd9Sstevel@tonic-gate USB_CFG_DESCR_SIZE); 5015*7c478bd9Sstevel@tonic-gate 5016*7c478bd9Sstevel@tonic-gate /* if parse cfg descr error, it should return failure */ 5017*7c478bd9Sstevel@tonic-gate if (size == USB_PARSE_ERROR) { 5018*7c478bd9Sstevel@tonic-gate 5019*7c478bd9Sstevel@tonic-gate if (pdata->b_rptr[1] != USB_DESCR_TYPE_CFG) { 5020*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, 5021*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 5022*7c478bd9Sstevel@tonic-gate "device returned incorrect " 5023*7c478bd9Sstevel@tonic-gate "configuration descriptor type."); 5024*7c478bd9Sstevel@tonic-gate } 5025*7c478bd9Sstevel@tonic-gate rval = USB_FAILURE; 5026*7c478bd9Sstevel@tonic-gate goto done; 5027*7c478bd9Sstevel@tonic-gate } 5028*7c478bd9Sstevel@tonic-gate 5029*7c478bd9Sstevel@tonic-gate if (confdescr->wTotalLength < USB_CFG_DESCR_SIZE) { 5030*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, 5031*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 5032*7c478bd9Sstevel@tonic-gate "device returned incorrect " 5033*7c478bd9Sstevel@tonic-gate "configuration descriptor size."); 5034*7c478bd9Sstevel@tonic-gate 5035*7c478bd9Sstevel@tonic-gate rval = USB_FAILURE; 5036*7c478bd9Sstevel@tonic-gate goto done; 5037*7c478bd9Sstevel@tonic-gate } 5038*7c478bd9Sstevel@tonic-gate 5039*7c478bd9Sstevel@tonic-gate freemsg(pdata); 5040*7c478bd9Sstevel@tonic-gate pdata = NULL; 5041*7c478bd9Sstevel@tonic-gate 5042*7c478bd9Sstevel@tonic-gate /* Now fetch the complete config cloud */ 5043*7c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(dip, def_ph, 5044*7c478bd9Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD, 5045*7c478bd9Sstevel@tonic-gate USB_REQ_GET_DESCR, 5046*7c478bd9Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_CFG | conf_index, 5047*7c478bd9Sstevel@tonic-gate 0, 5048*7c478bd9Sstevel@tonic-gate confdescr->wTotalLength, 5049*7c478bd9Sstevel@tonic-gate &pdata, 5050*7c478bd9Sstevel@tonic-gate 0, 5051*7c478bd9Sstevel@tonic-gate &completion_reason, 5052*7c478bd9Sstevel@tonic-gate &cb_flags, 5053*7c478bd9Sstevel@tonic-gate 0)) == USB_SUCCESS) { 5054*7c478bd9Sstevel@tonic-gate 5055*7c478bd9Sstevel@tonic-gate if ((pdata->b_wptr - pdata->b_rptr) != 5056*7c478bd9Sstevel@tonic-gate confdescr->wTotalLength) { 5057*7c478bd9Sstevel@tonic-gate 5058*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, 5059*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 5060*7c478bd9Sstevel@tonic-gate "device returned incorrect " 5061*7c478bd9Sstevel@tonic-gate "configuration descriptor."); 5062*7c478bd9Sstevel@tonic-gate 5063*7c478bd9Sstevel@tonic-gate rval = USB_FAILURE; 5064*7c478bd9Sstevel@tonic-gate goto done; 5065*7c478bd9Sstevel@tonic-gate } 5066*7c478bd9Sstevel@tonic-gate 5067*7c478bd9Sstevel@tonic-gate /* 5068*7c478bd9Sstevel@tonic-gate * copy config descriptor into usba_device 5069*7c478bd9Sstevel@tonic-gate */ 5070*7c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 5071*7c478bd9Sstevel@tonic-gate child_ud->usb_cfg_array[conf_index] = 5072*7c478bd9Sstevel@tonic-gate kmem_alloc(confdescr->wTotalLength, KM_SLEEP); 5073*7c478bd9Sstevel@tonic-gate child_ud->usb_cfg_array_len[conf_index] = 5074*7c478bd9Sstevel@tonic-gate confdescr->wTotalLength; 5075*7c478bd9Sstevel@tonic-gate bcopy((caddr_t)pdata->b_rptr, 5076*7c478bd9Sstevel@tonic-gate (caddr_t)child_ud->usb_cfg_array[conf_index], 5077*7c478bd9Sstevel@tonic-gate confdescr->wTotalLength); 5078*7c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 5079*7c478bd9Sstevel@tonic-gate 5080*7c478bd9Sstevel@tonic-gate /* 5081*7c478bd9Sstevel@tonic-gate * retrieve string descriptor describing this 5082*7c478bd9Sstevel@tonic-gate * configuration 5083*7c478bd9Sstevel@tonic-gate */ 5084*7c478bd9Sstevel@tonic-gate if (confdescr->iConfiguration) { 5085*7c478bd9Sstevel@tonic-gate 5086*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, 5087*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 5088*7c478bd9Sstevel@tonic-gate "Get conf str descr for config_index=%d", 5089*7c478bd9Sstevel@tonic-gate conf_index); 5090*7c478bd9Sstevel@tonic-gate 5091*7c478bd9Sstevel@tonic-gate /* 5092*7c478bd9Sstevel@tonic-gate * Now fetch the string descriptor describing 5093*7c478bd9Sstevel@tonic-gate * this configuration 5094*7c478bd9Sstevel@tonic-gate */ 5095*7c478bd9Sstevel@tonic-gate if ((rval = usb_get_string_descr(dip, 5096*7c478bd9Sstevel@tonic-gate USB_LANG_ID, confdescr->iConfiguration, 5097*7c478bd9Sstevel@tonic-gate tmpbuf, USB_MAXSTRINGLEN)) == 5098*7c478bd9Sstevel@tonic-gate USB_SUCCESS) { 5099*7c478bd9Sstevel@tonic-gate size = strlen(tmpbuf); 5100*7c478bd9Sstevel@tonic-gate if (size > 0) { 5101*7c478bd9Sstevel@tonic-gate child_ud->usb_cfg_str_descr 5102*7c478bd9Sstevel@tonic-gate [conf_index] = (char *) 5103*7c478bd9Sstevel@tonic-gate kmem_zalloc(size + 1, 5104*7c478bd9Sstevel@tonic-gate KM_SLEEP); 5105*7c478bd9Sstevel@tonic-gate (void) strcpy( 5106*7c478bd9Sstevel@tonic-gate child_ud->usb_cfg_str_descr 5107*7c478bd9Sstevel@tonic-gate [conf_index], tmpbuf); 5108*7c478bd9Sstevel@tonic-gate } 5109*7c478bd9Sstevel@tonic-gate } else { 5110*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 5111*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 5112*7c478bd9Sstevel@tonic-gate "hubd_get_this_config_cloud: " 5113*7c478bd9Sstevel@tonic-gate "getting config string (%d) " 5114*7c478bd9Sstevel@tonic-gate "failed", 5115*7c478bd9Sstevel@tonic-gate confdescr->iConfiguration); 5116*7c478bd9Sstevel@tonic-gate 5117*7c478bd9Sstevel@tonic-gate /* ignore this error */ 5118*7c478bd9Sstevel@tonic-gate rval = USB_SUCCESS; 5119*7c478bd9Sstevel@tonic-gate } 5120*7c478bd9Sstevel@tonic-gate } 5121*7c478bd9Sstevel@tonic-gate } 5122*7c478bd9Sstevel@tonic-gate } 5123*7c478bd9Sstevel@tonic-gate 5124*7c478bd9Sstevel@tonic-gate done: 5125*7c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 5126*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5127*7c478bd9Sstevel@tonic-gate "hubd_get_this_config_cloud: " 5128*7c478bd9Sstevel@tonic-gate "error in retrieving config descriptor for " 5129*7c478bd9Sstevel@tonic-gate "config index=%d rval=%d cr=%d", 5130*7c478bd9Sstevel@tonic-gate conf_index, rval, completion_reason); 5131*7c478bd9Sstevel@tonic-gate } 5132*7c478bd9Sstevel@tonic-gate 5133*7c478bd9Sstevel@tonic-gate if (pdata) { 5134*7c478bd9Sstevel@tonic-gate freemsg(pdata); 5135*7c478bd9Sstevel@tonic-gate pdata = NULL; 5136*7c478bd9Sstevel@tonic-gate } 5137*7c478bd9Sstevel@tonic-gate 5138*7c478bd9Sstevel@tonic-gate kmem_free(confdescr, USB_CFG_DESCR_SIZE); 5139*7c478bd9Sstevel@tonic-gate kmem_free(tmpbuf, USB_MAXSTRINGLEN); 5140*7c478bd9Sstevel@tonic-gate 5141*7c478bd9Sstevel@tonic-gate return (rval); 5142*7c478bd9Sstevel@tonic-gate } 5143*7c478bd9Sstevel@tonic-gate 5144*7c478bd9Sstevel@tonic-gate 5145*7c478bd9Sstevel@tonic-gate /* 5146*7c478bd9Sstevel@tonic-gate * Retrieves the entire config cloud for all configurations of the device 5147*7c478bd9Sstevel@tonic-gate */ 5148*7c478bd9Sstevel@tonic-gate int 5149*7c478bd9Sstevel@tonic-gate hubd_get_all_device_config_cloud(hubd_t *hubd, dev_info_t *dip, 5150*7c478bd9Sstevel@tonic-gate usba_device_t *child_ud) 5151*7c478bd9Sstevel@tonic-gate { 5152*7c478bd9Sstevel@tonic-gate int rval = USB_SUCCESS; 5153*7c478bd9Sstevel@tonic-gate int ncfgs; 5154*7c478bd9Sstevel@tonic-gate uint16_t size; 5155*7c478bd9Sstevel@tonic-gate uint16_t conf_index; 5156*7c478bd9Sstevel@tonic-gate uchar_t **cfg_array; 5157*7c478bd9Sstevel@tonic-gate uint16_t *cfg_array_len; 5158*7c478bd9Sstevel@tonic-gate char **str_descr; 5159*7c478bd9Sstevel@tonic-gate 5160*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5161*7c478bd9Sstevel@tonic-gate "hubd_get_all_device_config_cloud: Start"); 5162*7c478bd9Sstevel@tonic-gate 5163*7c478bd9Sstevel@tonic-gate /* alloc pointer array for conf. descriptors */ 5164*7c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 5165*7c478bd9Sstevel@tonic-gate ncfgs = child_ud->usb_n_cfgs; 5166*7c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 5167*7c478bd9Sstevel@tonic-gate 5168*7c478bd9Sstevel@tonic-gate size = sizeof (uchar_t *) * ncfgs; 5169*7c478bd9Sstevel@tonic-gate cfg_array = kmem_zalloc(size, KM_SLEEP); 5170*7c478bd9Sstevel@tonic-gate cfg_array_len = kmem_zalloc(ncfgs * sizeof (uint16_t), KM_SLEEP); 5171*7c478bd9Sstevel@tonic-gate str_descr = kmem_zalloc(size, KM_SLEEP); 5172*7c478bd9Sstevel@tonic-gate 5173*7c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 5174*7c478bd9Sstevel@tonic-gate child_ud->usb_cfg_array = cfg_array; 5175*7c478bd9Sstevel@tonic-gate child_ud->usb_cfg_array_len = cfg_array_len; 5176*7c478bd9Sstevel@tonic-gate child_ud->usb_cfg_array_length = size; 5177*7c478bd9Sstevel@tonic-gate child_ud->usb_cfg_array_len_length = ncfgs * sizeof (uint16_t); 5178*7c478bd9Sstevel@tonic-gate child_ud->usb_cfg_str_descr = str_descr; 5179*7c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 5180*7c478bd9Sstevel@tonic-gate 5181*7c478bd9Sstevel@tonic-gate /* Get configuration descriptor for each configuration */ 5182*7c478bd9Sstevel@tonic-gate for (conf_index = 0; (conf_index < ncfgs) && 5183*7c478bd9Sstevel@tonic-gate (rval == USB_SUCCESS); conf_index++) { 5184*7c478bd9Sstevel@tonic-gate 5185*7c478bd9Sstevel@tonic-gate rval = hubd_get_this_config_cloud(hubd, dip, child_ud, 5186*7c478bd9Sstevel@tonic-gate conf_index); 5187*7c478bd9Sstevel@tonic-gate } 5188*7c478bd9Sstevel@tonic-gate 5189*7c478bd9Sstevel@tonic-gate return (rval); 5190*7c478bd9Sstevel@tonic-gate } 5191*7c478bd9Sstevel@tonic-gate 5192*7c478bd9Sstevel@tonic-gate 5193*7c478bd9Sstevel@tonic-gate /* 5194*7c478bd9Sstevel@tonic-gate * hubd_ready_device: 5195*7c478bd9Sstevel@tonic-gate * Update the usba_device structure 5196*7c478bd9Sstevel@tonic-gate * Set the given configuration 5197*7c478bd9Sstevel@tonic-gate * Prepares the device node for driver to online. If an existing 5198*7c478bd9Sstevel@tonic-gate * OBP node is found, it will switch to the OBP node. 5199*7c478bd9Sstevel@tonic-gate */ 5200*7c478bd9Sstevel@tonic-gate static dev_info_t * 5201*7c478bd9Sstevel@tonic-gate hubd_ready_device(hubd_t *hubd, dev_info_t *child_dip, usba_device_t *child_ud, 5202*7c478bd9Sstevel@tonic-gate int config_index) 5203*7c478bd9Sstevel@tonic-gate { 5204*7c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 5205*7c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 5206*7c478bd9Sstevel@tonic-gate size_t size; 5207*7c478bd9Sstevel@tonic-gate usb_cfg_descr_t config_descriptor; 5208*7c478bd9Sstevel@tonic-gate usb_pipe_handle_t def_ph; 5209*7c478bd9Sstevel@tonic-gate usba_pipe_handle_data_t *ph; 5210*7c478bd9Sstevel@tonic-gate 5211*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5212*7c478bd9Sstevel@tonic-gate "hubd_ready_device: dip=0x%p, user_conf_index=%d", child_dip, 5213*7c478bd9Sstevel@tonic-gate config_index); 5214*7c478bd9Sstevel@tonic-gate 5215*7c478bd9Sstevel@tonic-gate ASSERT(config_index >= 0); 5216*7c478bd9Sstevel@tonic-gate 5217*7c478bd9Sstevel@tonic-gate size = usb_parse_cfg_descr( 5218*7c478bd9Sstevel@tonic-gate child_ud->usb_cfg_array[config_index], USB_CFG_DESCR_SIZE, 5219*7c478bd9Sstevel@tonic-gate &config_descriptor, USB_CFG_DESCR_SIZE); 5220*7c478bd9Sstevel@tonic-gate ASSERT(size == USB_CFG_DESCR_SIZE); 5221*7c478bd9Sstevel@tonic-gate 5222*7c478bd9Sstevel@tonic-gate def_ph = usba_get_dflt_pipe_handle(child_dip); 5223*7c478bd9Sstevel@tonic-gate 5224*7c478bd9Sstevel@tonic-gate /* Set the configuration */ 5225*7c478bd9Sstevel@tonic-gate (void) usb_pipe_sync_ctrl_xfer(child_dip, def_ph, 5226*7c478bd9Sstevel@tonic-gate USB_DEV_REQ_HOST_TO_DEV, 5227*7c478bd9Sstevel@tonic-gate USB_REQ_SET_CFG, /* bRequest */ 5228*7c478bd9Sstevel@tonic-gate config_descriptor.bConfigurationValue, /* wValue */ 5229*7c478bd9Sstevel@tonic-gate 0, /* wIndex */ 5230*7c478bd9Sstevel@tonic-gate 0, /* wLength */ 5231*7c478bd9Sstevel@tonic-gate NULL, 5232*7c478bd9Sstevel@tonic-gate 0, 5233*7c478bd9Sstevel@tonic-gate &completion_reason, 5234*7c478bd9Sstevel@tonic-gate &cb_flags, 5235*7c478bd9Sstevel@tonic-gate 0); 5236*7c478bd9Sstevel@tonic-gate 5237*7c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 5238*7c478bd9Sstevel@tonic-gate child_ud->usb_active_cfg_ndx = config_index; 5239*7c478bd9Sstevel@tonic-gate child_ud->usb_cfg = child_ud->usb_cfg_array[config_index]; 5240*7c478bd9Sstevel@tonic-gate child_ud->usb_cfg_length = config_descriptor.wTotalLength; 5241*7c478bd9Sstevel@tonic-gate child_ud->usb_cfg_value = config_descriptor.bConfigurationValue; 5242*7c478bd9Sstevel@tonic-gate child_ud->usb_n_ifs = config_descriptor.bNumInterfaces; 5243*7c478bd9Sstevel@tonic-gate child_ud->usb_dip = child_dip; 5244*7c478bd9Sstevel@tonic-gate 5245*7c478bd9Sstevel@tonic-gate child_ud->usb_client_flags = kmem_zalloc( 5246*7c478bd9Sstevel@tonic-gate child_ud->usb_n_ifs * USBA_CLIENT_FLAG_SIZE, KM_SLEEP); 5247*7c478bd9Sstevel@tonic-gate 5248*7c478bd9Sstevel@tonic-gate child_ud->usb_client_attach_list = kmem_zalloc( 5249*7c478bd9Sstevel@tonic-gate child_ud->usb_n_ifs * 5250*7c478bd9Sstevel@tonic-gate sizeof (*child_ud->usb_client_attach_list), KM_SLEEP); 5251*7c478bd9Sstevel@tonic-gate 5252*7c478bd9Sstevel@tonic-gate child_ud->usb_client_ev_cb_list = kmem_zalloc( 5253*7c478bd9Sstevel@tonic-gate child_ud->usb_n_ifs * 5254*7c478bd9Sstevel@tonic-gate sizeof (*child_ud->usb_client_ev_cb_list), KM_SLEEP); 5255*7c478bd9Sstevel@tonic-gate 5256*7c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 5257*7c478bd9Sstevel@tonic-gate 5258*7c478bd9Sstevel@tonic-gate /* ready the device node */ 5259*7c478bd9Sstevel@tonic-gate child_dip = usba_ready_device_node(child_dip); 5260*7c478bd9Sstevel@tonic-gate 5261*7c478bd9Sstevel@tonic-gate /* set owner of default pipe to child dip */ 5262*7c478bd9Sstevel@tonic-gate ph = usba_get_ph_data(def_ph); 5263*7c478bd9Sstevel@tonic-gate mutex_enter(&ph->p_mutex); 5264*7c478bd9Sstevel@tonic-gate mutex_enter(&ph->p_ph_impl->usba_ph_mutex); 5265*7c478bd9Sstevel@tonic-gate ph->p_ph_impl->usba_ph_dip = ph->p_dip = child_dip; 5266*7c478bd9Sstevel@tonic-gate mutex_exit(&ph->p_ph_impl->usba_ph_mutex); 5267*7c478bd9Sstevel@tonic-gate mutex_exit(&ph->p_mutex); 5268*7c478bd9Sstevel@tonic-gate 5269*7c478bd9Sstevel@tonic-gate return (child_dip); 5270*7c478bd9Sstevel@tonic-gate } 5271*7c478bd9Sstevel@tonic-gate 5272*7c478bd9Sstevel@tonic-gate 5273*7c478bd9Sstevel@tonic-gate /* 5274*7c478bd9Sstevel@tonic-gate * hubd_create_child 5275*7c478bd9Sstevel@tonic-gate * - create child dip 5276*7c478bd9Sstevel@tonic-gate * - open default pipe 5277*7c478bd9Sstevel@tonic-gate * - get device descriptor 5278*7c478bd9Sstevel@tonic-gate * - set the address 5279*7c478bd9Sstevel@tonic-gate * - get device string descriptors 5280*7c478bd9Sstevel@tonic-gate * - get the entire config cloud (all configurations) of the device 5281*7c478bd9Sstevel@tonic-gate * - set user preferred configuration 5282*7c478bd9Sstevel@tonic-gate * - close default pipe 5283*7c478bd9Sstevel@tonic-gate * - load appropriate driver(s) 5284*7c478bd9Sstevel@tonic-gate */ 5285*7c478bd9Sstevel@tonic-gate static int 5286*7c478bd9Sstevel@tonic-gate hubd_create_child(dev_info_t *dip, 5287*7c478bd9Sstevel@tonic-gate hubd_t *hubd, 5288*7c478bd9Sstevel@tonic-gate usba_device_t *hubd_ud, 5289*7c478bd9Sstevel@tonic-gate usb_port_status_t port_status, 5290*7c478bd9Sstevel@tonic-gate usb_port_t port, 5291*7c478bd9Sstevel@tonic-gate int iteration) 5292*7c478bd9Sstevel@tonic-gate { 5293*7c478bd9Sstevel@tonic-gate dev_info_t *child_dip = NULL; 5294*7c478bd9Sstevel@tonic-gate usb_dev_descr_t usb_dev_descr; 5295*7c478bd9Sstevel@tonic-gate int rval; 5296*7c478bd9Sstevel@tonic-gate usba_device_t *child_ud = NULL; 5297*7c478bd9Sstevel@tonic-gate usba_device_t *parent_ud = NULL; 5298*7c478bd9Sstevel@tonic-gate usb_pipe_handle_t ph = NULL; /* default pipe handle */ 5299*7c478bd9Sstevel@tonic-gate mblk_t *pdata = NULL; 5300*7c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 5301*7c478bd9Sstevel@tonic-gate int user_conf_index, config_index; 5302*7c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 5303*7c478bd9Sstevel@tonic-gate uchar_t address = 0; 5304*7c478bd9Sstevel@tonic-gate uint16_t length; 5305*7c478bd9Sstevel@tonic-gate size_t size; 5306*7c478bd9Sstevel@tonic-gate usb_addr_t parent_usb_addr; 5307*7c478bd9Sstevel@tonic-gate usb_port_t parent_usb_port; 5308*7c478bd9Sstevel@tonic-gate usba_device_t *parent_usba_dev; 5309*7c478bd9Sstevel@tonic-gate usb_port_status_t parent_port_status; 5310*7c478bd9Sstevel@tonic-gate 5311*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5312*7c478bd9Sstevel@tonic-gate "hubd_create_child: port=%d", port); 5313*7c478bd9Sstevel@tonic-gate 5314*7c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 5315*7c478bd9Sstevel@tonic-gate ASSERT(hubd->h_usba_devices[port] == NULL); 5316*7c478bd9Sstevel@tonic-gate 5317*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 5318*7c478bd9Sstevel@tonic-gate 5319*7c478bd9Sstevel@tonic-gate /* 5320*7c478bd9Sstevel@tonic-gate * create a dip which can be used to open the pipe. we set 5321*7c478bd9Sstevel@tonic-gate * the name after getting the descriptors from the device 5322*7c478bd9Sstevel@tonic-gate */ 5323*7c478bd9Sstevel@tonic-gate rval = usba_create_child_devi(dip, 5324*7c478bd9Sstevel@tonic-gate "device", /* driver name */ 5325*7c478bd9Sstevel@tonic-gate hubd_ud->usb_hcdi_ops, /* usba_hcdi ops */ 5326*7c478bd9Sstevel@tonic-gate hubd_ud->usb_root_hub_dip, 5327*7c478bd9Sstevel@tonic-gate port_status, /* low speed device */ 5328*7c478bd9Sstevel@tonic-gate child_ud, 5329*7c478bd9Sstevel@tonic-gate &child_dip); 5330*7c478bd9Sstevel@tonic-gate 5331*7c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 5332*7c478bd9Sstevel@tonic-gate 5333*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5334*7c478bd9Sstevel@tonic-gate "usb_create_child_devi failed (%d)", rval); 5335*7c478bd9Sstevel@tonic-gate 5336*7c478bd9Sstevel@tonic-gate goto fail_cleanup; 5337*7c478bd9Sstevel@tonic-gate } 5338*7c478bd9Sstevel@tonic-gate 5339*7c478bd9Sstevel@tonic-gate child_ud = usba_get_usba_device(child_dip); 5340*7c478bd9Sstevel@tonic-gate ASSERT(child_ud != NULL); 5341*7c478bd9Sstevel@tonic-gate 5342*7c478bd9Sstevel@tonic-gate parent_ud = hubd->h_usba_device; 5343*7c478bd9Sstevel@tonic-gate mutex_enter(&parent_ud->usb_mutex); 5344*7c478bd9Sstevel@tonic-gate parent_port_status = parent_ud->usb_port_status; 5345*7c478bd9Sstevel@tonic-gate 5346*7c478bd9Sstevel@tonic-gate /* 5347*7c478bd9Sstevel@tonic-gate * To support split transactions, update address and port 5348*7c478bd9Sstevel@tonic-gate * of high speed hub to which given device is connected. 5349*7c478bd9Sstevel@tonic-gate */ 5350*7c478bd9Sstevel@tonic-gate if (parent_port_status == USBA_HIGH_SPEED_DEV) { 5351*7c478bd9Sstevel@tonic-gate parent_usba_dev = parent_ud; 5352*7c478bd9Sstevel@tonic-gate parent_usb_addr = parent_ud->usb_addr; 5353*7c478bd9Sstevel@tonic-gate parent_usb_port = port; 5354*7c478bd9Sstevel@tonic-gate } else { 5355*7c478bd9Sstevel@tonic-gate parent_usba_dev = parent_ud->usb_hs_hub_usba_dev; 5356*7c478bd9Sstevel@tonic-gate parent_usb_addr = parent_ud->usb_hs_hub_addr; 5357*7c478bd9Sstevel@tonic-gate parent_usb_port = parent_ud->usb_hs_hub_port; 5358*7c478bd9Sstevel@tonic-gate } 5359*7c478bd9Sstevel@tonic-gate mutex_exit(&parent_ud->usb_mutex); 5360*7c478bd9Sstevel@tonic-gate 5361*7c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 5362*7c478bd9Sstevel@tonic-gate address = child_ud->usb_addr; 5363*7c478bd9Sstevel@tonic-gate child_ud->usb_addr = 0; 5364*7c478bd9Sstevel@tonic-gate child_ud->usb_dev_descr = kmem_alloc(sizeof (usb_dev_descr_t), 5365*7c478bd9Sstevel@tonic-gate KM_SLEEP); 5366*7c478bd9Sstevel@tonic-gate bzero(&usb_dev_descr, sizeof (usb_dev_descr_t)); 5367*7c478bd9Sstevel@tonic-gate usb_dev_descr.bMaxPacketSize0 = 5368*7c478bd9Sstevel@tonic-gate (port_status == USBA_LOW_SPEED_DEV) ? 8 : 64; 5369*7c478bd9Sstevel@tonic-gate bcopy(&usb_dev_descr, child_ud->usb_dev_descr, 5370*7c478bd9Sstevel@tonic-gate sizeof (usb_dev_descr_t)); 5371*7c478bd9Sstevel@tonic-gate child_ud->usb_port = port; 5372*7c478bd9Sstevel@tonic-gate child_ud->usb_hs_hub_usba_dev = parent_usba_dev; 5373*7c478bd9Sstevel@tonic-gate child_ud->usb_hs_hub_addr = parent_usb_addr; 5374*7c478bd9Sstevel@tonic-gate child_ud->usb_hs_hub_port = parent_usb_port; 5375*7c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 5376*7c478bd9Sstevel@tonic-gate 5377*7c478bd9Sstevel@tonic-gate /* Open the default pipe */ 5378*7c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_open(child_dip, NULL, NULL, 5379*7c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) != USB_SUCCESS) { 5380*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5381*7c478bd9Sstevel@tonic-gate "usb_pipe_open failed (%d)", rval); 5382*7c478bd9Sstevel@tonic-gate 5383*7c478bd9Sstevel@tonic-gate goto fail_cleanup; 5384*7c478bd9Sstevel@tonic-gate } 5385*7c478bd9Sstevel@tonic-gate 5386*7c478bd9Sstevel@tonic-gate /* 5387*7c478bd9Sstevel@tonic-gate * get device descriptor 5388*7c478bd9Sstevel@tonic-gate */ 5389*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5390*7c478bd9Sstevel@tonic-gate "hubd_create_child: get device descriptor: 64 bytes"); 5391*7c478bd9Sstevel@tonic-gate 5392*7c478bd9Sstevel@tonic-gate rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 5393*7c478bd9Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD, 5394*7c478bd9Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */ 5395*7c478bd9Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_DEV, /* wValue */ 5396*7c478bd9Sstevel@tonic-gate 0, /* wIndex */ 5397*7c478bd9Sstevel@tonic-gate 64, /* wLength */ 5398*7c478bd9Sstevel@tonic-gate &pdata, USB_ATTRS_SHORT_XFER_OK, 5399*7c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0); 5400*7c478bd9Sstevel@tonic-gate 5401*7c478bd9Sstevel@tonic-gate if ((rval != USB_SUCCESS) && 5402*7c478bd9Sstevel@tonic-gate (!((completion_reason == USB_CR_DATA_OVERRUN) && pdata))) { 5403*7c478bd9Sstevel@tonic-gate 5404*7c478bd9Sstevel@tonic-gate /* 5405*7c478bd9Sstevel@tonic-gate * rval != USB_SUCCESS AND 5406*7c478bd9Sstevel@tonic-gate * completion_reason != USB_CR_DATA_OVERRUN 5407*7c478bd9Sstevel@tonic-gate * pdata could be != NULL. 5408*7c478bd9Sstevel@tonic-gate * Free pdata now to prevent memory leak. 5409*7c478bd9Sstevel@tonic-gate */ 5410*7c478bd9Sstevel@tonic-gate freemsg(pdata); 5411*7c478bd9Sstevel@tonic-gate pdata = NULL; 5412*7c478bd9Sstevel@tonic-gate 5413*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5414*7c478bd9Sstevel@tonic-gate "hubd_create_child: get device descriptor: 8 bytes"); 5415*7c478bd9Sstevel@tonic-gate 5416*7c478bd9Sstevel@tonic-gate rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 5417*7c478bd9Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD, 5418*7c478bd9Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */ 5419*7c478bd9Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_DEV, /* wValue */ 5420*7c478bd9Sstevel@tonic-gate 0, /* wIndex */ 5421*7c478bd9Sstevel@tonic-gate 8, /* wLength */ 5422*7c478bd9Sstevel@tonic-gate &pdata, USB_ATTRS_NONE, 5423*7c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0); 5424*7c478bd9Sstevel@tonic-gate 5425*7c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 5426*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5427*7c478bd9Sstevel@tonic-gate "getting device descriptor failed (%s 0x%x %d)", 5428*7c478bd9Sstevel@tonic-gate usb_str_cr(completion_reason), cb_flags, rval); 5429*7c478bd9Sstevel@tonic-gate goto fail_cleanup; 5430*7c478bd9Sstevel@tonic-gate } 5431*7c478bd9Sstevel@tonic-gate } else { 5432*7c478bd9Sstevel@tonic-gate ASSERT(completion_reason == USB_CR_OK); 5433*7c478bd9Sstevel@tonic-gate } 5434*7c478bd9Sstevel@tonic-gate 5435*7c478bd9Sstevel@tonic-gate ASSERT(pdata != NULL); 5436*7c478bd9Sstevel@tonic-gate 5437*7c478bd9Sstevel@tonic-gate size = usb_parse_dev_descr( 5438*7c478bd9Sstevel@tonic-gate pdata->b_rptr, 5439*7c478bd9Sstevel@tonic-gate pdata->b_wptr - pdata->b_rptr, 5440*7c478bd9Sstevel@tonic-gate &usb_dev_descr, 5441*7c478bd9Sstevel@tonic-gate sizeof (usb_dev_descr_t)); 5442*7c478bd9Sstevel@tonic-gate 5443*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5444*7c478bd9Sstevel@tonic-gate "parsing device descriptor returned %lu", size); 5445*7c478bd9Sstevel@tonic-gate 5446*7c478bd9Sstevel@tonic-gate length = *(pdata->b_rptr); 5447*7c478bd9Sstevel@tonic-gate freemsg(pdata); 5448*7c478bd9Sstevel@tonic-gate pdata = NULL; 5449*7c478bd9Sstevel@tonic-gate if (size < 8) { 5450*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5451*7c478bd9Sstevel@tonic-gate "get device descriptor returned %lu bytes", size); 5452*7c478bd9Sstevel@tonic-gate 5453*7c478bd9Sstevel@tonic-gate goto fail_cleanup; 5454*7c478bd9Sstevel@tonic-gate } 5455*7c478bd9Sstevel@tonic-gate 5456*7c478bd9Sstevel@tonic-gate if (length < 8) { 5457*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5458*7c478bd9Sstevel@tonic-gate "fail enumeration: bLength=%d", length); 5459*7c478bd9Sstevel@tonic-gate 5460*7c478bd9Sstevel@tonic-gate goto fail_cleanup; 5461*7c478bd9Sstevel@tonic-gate } 5462*7c478bd9Sstevel@tonic-gate 5463*7c478bd9Sstevel@tonic-gate /* Set the address of the device */ 5464*7c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 5465*7c478bd9Sstevel@tonic-gate USB_DEV_REQ_HOST_TO_DEV, 5466*7c478bd9Sstevel@tonic-gate USB_REQ_SET_ADDRESS, /* bRequest */ 5467*7c478bd9Sstevel@tonic-gate address, /* wValue */ 5468*7c478bd9Sstevel@tonic-gate 0, /* wIndex */ 5469*7c478bd9Sstevel@tonic-gate 0, /* wLength */ 5470*7c478bd9Sstevel@tonic-gate NULL, 0, 5471*7c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 5472*7c478bd9Sstevel@tonic-gate char buffer[64]; 5473*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5474*7c478bd9Sstevel@tonic-gate "setting address failed (cr=%s cb_flags=%s rval=%d)", 5475*7c478bd9Sstevel@tonic-gate usb_str_cr(completion_reason), 5476*7c478bd9Sstevel@tonic-gate usb_str_cb_flags(cb_flags, buffer, sizeof (buffer)), 5477*7c478bd9Sstevel@tonic-gate rval); 5478*7c478bd9Sstevel@tonic-gate 5479*7c478bd9Sstevel@tonic-gate goto fail_cleanup; 5480*7c478bd9Sstevel@tonic-gate } 5481*7c478bd9Sstevel@tonic-gate 5482*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5483*7c478bd9Sstevel@tonic-gate "set address 0x%x done", address); 5484*7c478bd9Sstevel@tonic-gate 5485*7c478bd9Sstevel@tonic-gate /* now close the pipe for addr 0 */ 5486*7c478bd9Sstevel@tonic-gate usb_pipe_close(child_dip, ph, 5487*7c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL); 5488*7c478bd9Sstevel@tonic-gate 5489*7c478bd9Sstevel@tonic-gate /* 5490*7c478bd9Sstevel@tonic-gate * This delay is important for the CATC hub to enumerate 5491*7c478bd9Sstevel@tonic-gate * But, avoid delay in the first iteration 5492*7c478bd9Sstevel@tonic-gate */ 5493*7c478bd9Sstevel@tonic-gate if (iteration) { 5494*7c478bd9Sstevel@tonic-gate delay(drv_usectohz(hubd_device_delay/100)); 5495*7c478bd9Sstevel@tonic-gate } 5496*7c478bd9Sstevel@tonic-gate 5497*7c478bd9Sstevel@tonic-gate /* assign the address in the usba_device structure */ 5498*7c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 5499*7c478bd9Sstevel@tonic-gate child_ud->usb_addr = address; 5500*7c478bd9Sstevel@tonic-gate child_ud->usb_no_cpr = 0; 5501*7c478bd9Sstevel@tonic-gate child_ud->usb_port_status = port_status; 5502*7c478bd9Sstevel@tonic-gate /* save this device descriptor */ 5503*7c478bd9Sstevel@tonic-gate bcopy(&usb_dev_descr, child_ud->usb_dev_descr, 5504*7c478bd9Sstevel@tonic-gate sizeof (usb_dev_descr_t)); 5505*7c478bd9Sstevel@tonic-gate child_ud->usb_n_cfgs = usb_dev_descr.bNumConfigurations; 5506*7c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 5507*7c478bd9Sstevel@tonic-gate 5508*7c478bd9Sstevel@tonic-gate /* re-open the pipe for the device with the new address */ 5509*7c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_open(child_dip, NULL, NULL, 5510*7c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) != USB_SUCCESS) { 5511*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5512*7c478bd9Sstevel@tonic-gate "usb_pipe_open failed (%d)", rval); 5513*7c478bd9Sstevel@tonic-gate 5514*7c478bd9Sstevel@tonic-gate goto fail_cleanup; 5515*7c478bd9Sstevel@tonic-gate } 5516*7c478bd9Sstevel@tonic-gate 5517*7c478bd9Sstevel@tonic-gate /* 5518*7c478bd9Sstevel@tonic-gate * Get full device descriptor only if we have not received full 5519*7c478bd9Sstevel@tonic-gate * device descriptor earlier. 5520*7c478bd9Sstevel@tonic-gate */ 5521*7c478bd9Sstevel@tonic-gate if (size < length) { 5522*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5523*7c478bd9Sstevel@tonic-gate "hubd_create_child: get full device descriptor: " 5524*7c478bd9Sstevel@tonic-gate "%d bytes", length); 5525*7c478bd9Sstevel@tonic-gate 5526*7c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 5527*7c478bd9Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD, 5528*7c478bd9Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */ 5529*7c478bd9Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_DEV, /* wValue */ 5530*7c478bd9Sstevel@tonic-gate 0, /* wIndex */ 5531*7c478bd9Sstevel@tonic-gate length, /* wLength */ 5532*7c478bd9Sstevel@tonic-gate &pdata, 0, 5533*7c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 5534*7c478bd9Sstevel@tonic-gate freemsg(pdata); 5535*7c478bd9Sstevel@tonic-gate pdata = NULL; 5536*7c478bd9Sstevel@tonic-gate 5537*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, 5538*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 5539*7c478bd9Sstevel@tonic-gate "hubd_create_child: get full device descriptor: " 5540*7c478bd9Sstevel@tonic-gate "64 bytes"); 5541*7c478bd9Sstevel@tonic-gate 5542*7c478bd9Sstevel@tonic-gate rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 5543*7c478bd9Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | 5544*7c478bd9Sstevel@tonic-gate USB_DEV_REQ_TYPE_STANDARD, 5545*7c478bd9Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */ 5546*7c478bd9Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_DEV, /* wValue */ 5547*7c478bd9Sstevel@tonic-gate 0, /* wIndex */ 5548*7c478bd9Sstevel@tonic-gate 64, /* wLength */ 5549*7c478bd9Sstevel@tonic-gate &pdata, USB_ATTRS_SHORT_XFER_OK, 5550*7c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0); 5551*7c478bd9Sstevel@tonic-gate 5552*7c478bd9Sstevel@tonic-gate /* we have to trust the data now */ 5553*7c478bd9Sstevel@tonic-gate if (pdata) { 5554*7c478bd9Sstevel@tonic-gate int len = *(pdata->b_rptr); 5555*7c478bd9Sstevel@tonic-gate 5556*7c478bd9Sstevel@tonic-gate length = pdata->b_wptr - pdata->b_rptr; 5557*7c478bd9Sstevel@tonic-gate if (length < len) { 5558*7c478bd9Sstevel@tonic-gate 5559*7c478bd9Sstevel@tonic-gate goto fail_cleanup; 5560*7c478bd9Sstevel@tonic-gate } 5561*7c478bd9Sstevel@tonic-gate } else if (rval != USB_SUCCESS) { 5562*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, 5563*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 5564*7c478bd9Sstevel@tonic-gate "getting device descriptor failed " 5565*7c478bd9Sstevel@tonic-gate "(%d 0x%x %d)", 5566*7c478bd9Sstevel@tonic-gate completion_reason, cb_flags, rval); 5567*7c478bd9Sstevel@tonic-gate 5568*7c478bd9Sstevel@tonic-gate goto fail_cleanup; 5569*7c478bd9Sstevel@tonic-gate } 5570*7c478bd9Sstevel@tonic-gate } 5571*7c478bd9Sstevel@tonic-gate 5572*7c478bd9Sstevel@tonic-gate size = usb_parse_dev_descr( 5573*7c478bd9Sstevel@tonic-gate pdata->b_rptr, 5574*7c478bd9Sstevel@tonic-gate pdata->b_wptr - pdata->b_rptr, 5575*7c478bd9Sstevel@tonic-gate &usb_dev_descr, 5576*7c478bd9Sstevel@tonic-gate sizeof (usb_dev_descr_t)); 5577*7c478bd9Sstevel@tonic-gate 5578*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5579*7c478bd9Sstevel@tonic-gate "parsing device descriptor returned %lu", size); 5580*7c478bd9Sstevel@tonic-gate 5581*7c478bd9Sstevel@tonic-gate /* 5582*7c478bd9Sstevel@tonic-gate * For now, free the data 5583*7c478bd9Sstevel@tonic-gate * eventually, each configuration may need to be looked at 5584*7c478bd9Sstevel@tonic-gate */ 5585*7c478bd9Sstevel@tonic-gate freemsg(pdata); 5586*7c478bd9Sstevel@tonic-gate pdata = NULL; 5587*7c478bd9Sstevel@tonic-gate 5588*7c478bd9Sstevel@tonic-gate if (size != USB_DEV_DESCR_SIZE) { 5589*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5590*7c478bd9Sstevel@tonic-gate "fail enumeration: descriptor size=%lu " 5591*7c478bd9Sstevel@tonic-gate "expected size=%u", size, USB_DEV_DESCR_SIZE); 5592*7c478bd9Sstevel@tonic-gate 5593*7c478bd9Sstevel@tonic-gate goto fail_cleanup; 5594*7c478bd9Sstevel@tonic-gate } 5595*7c478bd9Sstevel@tonic-gate 5596*7c478bd9Sstevel@tonic-gate /* 5597*7c478bd9Sstevel@tonic-gate * save the device descriptor in usba_device since it is needed 5598*7c478bd9Sstevel@tonic-gate * later on again 5599*7c478bd9Sstevel@tonic-gate */ 5600*7c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 5601*7c478bd9Sstevel@tonic-gate bcopy(&usb_dev_descr, child_ud->usb_dev_descr, 5602*7c478bd9Sstevel@tonic-gate sizeof (usb_dev_descr_t)); 5603*7c478bd9Sstevel@tonic-gate child_ud->usb_n_cfgs = usb_dev_descr.bNumConfigurations; 5604*7c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 5605*7c478bd9Sstevel@tonic-gate } 5606*7c478bd9Sstevel@tonic-gate 5607*7c478bd9Sstevel@tonic-gate if (usb_dev_descr.bNumConfigurations == 0) { 5608*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5609*7c478bd9Sstevel@tonic-gate "device descriptor:\n\t" 5610*7c478bd9Sstevel@tonic-gate "l=0x%x type=0x%x USB=0x%x class=0x%x subclass=0x%x\n\t" 5611*7c478bd9Sstevel@tonic-gate "protocol=0x%x maxpktsize=0x%x " 5612*7c478bd9Sstevel@tonic-gate "Vid=0x%x Pid=0x%x rel=0x%x\n\t" 5613*7c478bd9Sstevel@tonic-gate "Mfg=0x%x P=0x%x sn=0x%x #config=0x%x", 5614*7c478bd9Sstevel@tonic-gate usb_dev_descr.bLength, usb_dev_descr.bDescriptorType, 5615*7c478bd9Sstevel@tonic-gate usb_dev_descr.bcdUSB, usb_dev_descr.bDeviceClass, 5616*7c478bd9Sstevel@tonic-gate usb_dev_descr.bDeviceSubClass, 5617*7c478bd9Sstevel@tonic-gate usb_dev_descr.bDeviceProtocol, 5618*7c478bd9Sstevel@tonic-gate usb_dev_descr.bMaxPacketSize0, 5619*7c478bd9Sstevel@tonic-gate usb_dev_descr.idVendor, 5620*7c478bd9Sstevel@tonic-gate usb_dev_descr.idProduct, usb_dev_descr.bcdDevice, 5621*7c478bd9Sstevel@tonic-gate usb_dev_descr.iManufacturer, usb_dev_descr.iProduct, 5622*7c478bd9Sstevel@tonic-gate usb_dev_descr.iSerialNumber, 5623*7c478bd9Sstevel@tonic-gate usb_dev_descr.bNumConfigurations); 5624*7c478bd9Sstevel@tonic-gate goto fail_cleanup; 5625*7c478bd9Sstevel@tonic-gate } 5626*7c478bd9Sstevel@tonic-gate 5627*7c478bd9Sstevel@tonic-gate 5628*7c478bd9Sstevel@tonic-gate /* get the device string descriptor(s) */ 5629*7c478bd9Sstevel@tonic-gate usba_get_dev_string_descrs(child_dip, child_ud); 5630*7c478bd9Sstevel@tonic-gate 5631*7c478bd9Sstevel@tonic-gate /* retrieve config cloud for all configurations */ 5632*7c478bd9Sstevel@tonic-gate rval = hubd_get_all_device_config_cloud(hubd, child_dip, child_ud); 5633*7c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 5634*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5635*7c478bd9Sstevel@tonic-gate "failed to get configuration descriptor(s)"); 5636*7c478bd9Sstevel@tonic-gate 5637*7c478bd9Sstevel@tonic-gate goto fail_cleanup; 5638*7c478bd9Sstevel@tonic-gate } 5639*7c478bd9Sstevel@tonic-gate 5640*7c478bd9Sstevel@tonic-gate /* get the preferred configuration for this device */ 5641*7c478bd9Sstevel@tonic-gate user_conf_index = hubd_select_device_configuration(hubd, port, 5642*7c478bd9Sstevel@tonic-gate child_dip, child_ud); 5643*7c478bd9Sstevel@tonic-gate 5644*7c478bd9Sstevel@tonic-gate /* Check if the user selected configuration index is in range */ 5645*7c478bd9Sstevel@tonic-gate if (user_conf_index >= usb_dev_descr.bNumConfigurations) { 5646*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5647*7c478bd9Sstevel@tonic-gate "Configuration index for device idVendor=%d " 5648*7c478bd9Sstevel@tonic-gate "idProduct=%d is=%d, and is out of range[0..%d]", 5649*7c478bd9Sstevel@tonic-gate usb_dev_descr.idVendor, usb_dev_descr.idProduct, 5650*7c478bd9Sstevel@tonic-gate user_conf_index, usb_dev_descr.bNumConfigurations - 1); 5651*7c478bd9Sstevel@tonic-gate 5652*7c478bd9Sstevel@tonic-gate /* treat this as user didn't specify configuration */ 5653*7c478bd9Sstevel@tonic-gate user_conf_index = USBA_DEV_CONFIG_INDEX_UNDEFINED; 5654*7c478bd9Sstevel@tonic-gate } 5655*7c478bd9Sstevel@tonic-gate 5656*7c478bd9Sstevel@tonic-gate 5657*7c478bd9Sstevel@tonic-gate /* 5658*7c478bd9Sstevel@tonic-gate * Warn users of a performance hit if connecting a 5659*7c478bd9Sstevel@tonic-gate * High Speed behind a 1.1 hub, which is behind a 5660*7c478bd9Sstevel@tonic-gate * 2.0 port. 5661*7c478bd9Sstevel@tonic-gate */ 5662*7c478bd9Sstevel@tonic-gate if ((parent_port_status != USBA_HIGH_SPEED_DEV) && 5663*7c478bd9Sstevel@tonic-gate !(usba_is_root_hub(parent_ud->usb_dip)) && 5664*7c478bd9Sstevel@tonic-gate (parent_usb_addr)) { 5665*7c478bd9Sstevel@tonic-gate 5666*7c478bd9Sstevel@tonic-gate /* 5667*7c478bd9Sstevel@tonic-gate * Now that we know the root port is a high speed port 5668*7c478bd9Sstevel@tonic-gate * and that the parent port is not a high speed port, 5669*7c478bd9Sstevel@tonic-gate * let's find out if the device itself is a high speed 5670*7c478bd9Sstevel@tonic-gate * device. If it is a high speed device, 5671*7c478bd9Sstevel@tonic-gate * USB_DESCR_TYPE_SETUP_DEV_QLF should return a value, 5672*7c478bd9Sstevel@tonic-gate * otherwise the command will fail. 5673*7c478bd9Sstevel@tonic-gate */ 5674*7c478bd9Sstevel@tonic-gate rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 5675*7c478bd9Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD, 5676*7c478bd9Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */ 5677*7c478bd9Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_DEV_QLF, /* wValue */ 5678*7c478bd9Sstevel@tonic-gate 0, /* wIndex */ 5679*7c478bd9Sstevel@tonic-gate 10, /* wLength */ 5680*7c478bd9Sstevel@tonic-gate &pdata, USB_ATTRS_SHORT_XFER_OK, 5681*7c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0); 5682*7c478bd9Sstevel@tonic-gate 5683*7c478bd9Sstevel@tonic-gate if (pdata) { 5684*7c478bd9Sstevel@tonic-gate freemsg(pdata); 5685*7c478bd9Sstevel@tonic-gate pdata = NULL; 5686*7c478bd9Sstevel@tonic-gate } 5687*7c478bd9Sstevel@tonic-gate 5688*7c478bd9Sstevel@tonic-gate /* 5689*7c478bd9Sstevel@tonic-gate * USB_DESCR_TYPE_SETUP_DEV_QLF query was successful 5690*7c478bd9Sstevel@tonic-gate * that means this is a high speed device behind a 5691*7c478bd9Sstevel@tonic-gate * high speed root hub, but running at full speed 5692*7c478bd9Sstevel@tonic-gate * because there is a full speed hub in the middle. 5693*7c478bd9Sstevel@tonic-gate */ 5694*7c478bd9Sstevel@tonic-gate if (rval == USB_SUCCESS) { 5695*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, 5696*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 5697*7c478bd9Sstevel@tonic-gate "Connecting a high speed device to a " 5698*7c478bd9Sstevel@tonic-gate "non high speed hub (port %d) will result " 5699*7c478bd9Sstevel@tonic-gate "in a loss of performance. Please connect " 5700*7c478bd9Sstevel@tonic-gate "the device to a high speed hub to get " 5701*7c478bd9Sstevel@tonic-gate "the maximum performance.", 5702*7c478bd9Sstevel@tonic-gate port); 5703*7c478bd9Sstevel@tonic-gate } 5704*7c478bd9Sstevel@tonic-gate } 5705*7c478bd9Sstevel@tonic-gate 5706*7c478bd9Sstevel@tonic-gate /* 5707*7c478bd9Sstevel@tonic-gate * Now we try to online the device by attaching a driver 5708*7c478bd9Sstevel@tonic-gate * The following truth table illustrates the logic:- 5709*7c478bd9Sstevel@tonic-gate * Cfgndx Driver Action 5710*7c478bd9Sstevel@tonic-gate * 0 0 loop all configs for driver with full 5711*7c478bd9Sstevel@tonic-gate * compatible properties. 5712*7c478bd9Sstevel@tonic-gate * 0 1 set first configuration, 5713*7c478bd9Sstevel@tonic-gate * compatible prop = drivername. 5714*7c478bd9Sstevel@tonic-gate * 1 0 Set config, full compatible prop 5715*7c478bd9Sstevel@tonic-gate * 1 1 Set config, compatible prop = drivername. 5716*7c478bd9Sstevel@tonic-gate * 5717*7c478bd9Sstevel@tonic-gate * Note: 5718*7c478bd9Sstevel@tonic-gate * cfgndx = user_conf_index 5719*7c478bd9Sstevel@tonic-gate * Driver = usb_preferred_driver 5720*7c478bd9Sstevel@tonic-gate */ 5721*7c478bd9Sstevel@tonic-gate if (user_conf_index == USBA_DEV_CONFIG_INDEX_UNDEFINED) { 5722*7c478bd9Sstevel@tonic-gate if (child_ud->usb_preferred_driver) { 5723*7c478bd9Sstevel@tonic-gate /* 5724*7c478bd9Sstevel@tonic-gate * It is the job of the "preferred driver" to put the 5725*7c478bd9Sstevel@tonic-gate * device in the desired configuration. Till then 5726*7c478bd9Sstevel@tonic-gate * put the device in config index 0. 5727*7c478bd9Sstevel@tonic-gate */ 5728*7c478bd9Sstevel@tonic-gate child_dip = hubd_ready_device(hubd, child_dip, 5729*7c478bd9Sstevel@tonic-gate child_ud, USB_DEV_DEFAULT_CONFIG_INDEX); 5730*7c478bd9Sstevel@tonic-gate 5731*7c478bd9Sstevel@tonic-gate /* 5732*7c478bd9Sstevel@tonic-gate * Assign the dip before onlining to avoid race 5733*7c478bd9Sstevel@tonic-gate * with busctl 5734*7c478bd9Sstevel@tonic-gate */ 5735*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 5736*7c478bd9Sstevel@tonic-gate hubd->h_children_dips[port] = child_dip; 5737*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 5738*7c478bd9Sstevel@tonic-gate 5739*7c478bd9Sstevel@tonic-gate (void) usba_bind_driver(child_dip); 5740*7c478bd9Sstevel@tonic-gate } else { 5741*7c478bd9Sstevel@tonic-gate /* 5742*7c478bd9Sstevel@tonic-gate * loop through all the configurations to see if we 5743*7c478bd9Sstevel@tonic-gate * can find a driver for any one config. If not, set 5744*7c478bd9Sstevel@tonic-gate * the device in config_index 0 5745*7c478bd9Sstevel@tonic-gate */ 5746*7c478bd9Sstevel@tonic-gate rval = USB_FAILURE; 5747*7c478bd9Sstevel@tonic-gate for (config_index = 0; 5748*7c478bd9Sstevel@tonic-gate (config_index < usb_dev_descr.bNumConfigurations) && 5749*7c478bd9Sstevel@tonic-gate (rval != USB_SUCCESS); config_index++) { 5750*7c478bd9Sstevel@tonic-gate 5751*7c478bd9Sstevel@tonic-gate child_dip = hubd_ready_device(hubd, child_dip, 5752*7c478bd9Sstevel@tonic-gate child_ud, config_index); 5753*7c478bd9Sstevel@tonic-gate 5754*7c478bd9Sstevel@tonic-gate /* 5755*7c478bd9Sstevel@tonic-gate * Assign the dip before onlining to avoid race 5756*7c478bd9Sstevel@tonic-gate * with busctl 5757*7c478bd9Sstevel@tonic-gate */ 5758*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 5759*7c478bd9Sstevel@tonic-gate hubd->h_children_dips[port] = child_dip; 5760*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 5761*7c478bd9Sstevel@tonic-gate 5762*7c478bd9Sstevel@tonic-gate rval = usba_bind_driver(child_dip); 5763*7c478bd9Sstevel@tonic-gate } 5764*7c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 5765*7c478bd9Sstevel@tonic-gate child_dip = hubd_ready_device(hubd, child_dip, 5766*7c478bd9Sstevel@tonic-gate child_ud, 0); 5767*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 5768*7c478bd9Sstevel@tonic-gate hubd->h_children_dips[port] = child_dip; 5769*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 5770*7c478bd9Sstevel@tonic-gate } 5771*7c478bd9Sstevel@tonic-gate } /* end else loop all configs */ 5772*7c478bd9Sstevel@tonic-gate } else { 5773*7c478bd9Sstevel@tonic-gate child_dip = hubd_ready_device(hubd, child_dip, 5774*7c478bd9Sstevel@tonic-gate child_ud, user_conf_index); 5775*7c478bd9Sstevel@tonic-gate 5776*7c478bd9Sstevel@tonic-gate /* 5777*7c478bd9Sstevel@tonic-gate * Assign the dip before onlining to avoid race 5778*7c478bd9Sstevel@tonic-gate * with busctl 5779*7c478bd9Sstevel@tonic-gate */ 5780*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 5781*7c478bd9Sstevel@tonic-gate hubd->h_children_dips[port] = child_dip; 5782*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 5783*7c478bd9Sstevel@tonic-gate 5784*7c478bd9Sstevel@tonic-gate (void) usba_bind_driver(child_dip); 5785*7c478bd9Sstevel@tonic-gate } 5786*7c478bd9Sstevel@tonic-gate 5787*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 5788*7c478bd9Sstevel@tonic-gate if (hubd->h_usba_devices[port] == NULL) { 5789*7c478bd9Sstevel@tonic-gate hubd->h_usba_devices[port] = usba_get_usba_device(child_dip); 5790*7c478bd9Sstevel@tonic-gate } else { 5791*7c478bd9Sstevel@tonic-gate ASSERT(hubd->h_usba_devices[port] == 5792*7c478bd9Sstevel@tonic-gate usba_get_usba_device(child_dip)); 5793*7c478bd9Sstevel@tonic-gate } 5794*7c478bd9Sstevel@tonic-gate 5795*7c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 5796*7c478bd9Sstevel@tonic-gate 5797*7c478bd9Sstevel@tonic-gate 5798*7c478bd9Sstevel@tonic-gate fail_cleanup: 5799*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5800*7c478bd9Sstevel@tonic-gate "hubd_create_child: fail_cleanup"); 5801*7c478bd9Sstevel@tonic-gate 5802*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 5803*7c478bd9Sstevel@tonic-gate hubd->h_children_dips[port] = NULL; 5804*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 5805*7c478bd9Sstevel@tonic-gate 5806*7c478bd9Sstevel@tonic-gate if (pdata) { 5807*7c478bd9Sstevel@tonic-gate freemsg(pdata); 5808*7c478bd9Sstevel@tonic-gate } 5809*7c478bd9Sstevel@tonic-gate 5810*7c478bd9Sstevel@tonic-gate if (ph) { 5811*7c478bd9Sstevel@tonic-gate usb_pipe_close(child_dip, ph, 5812*7c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL); 5813*7c478bd9Sstevel@tonic-gate } 5814*7c478bd9Sstevel@tonic-gate 5815*7c478bd9Sstevel@tonic-gate if (child_dip) { 5816*7c478bd9Sstevel@tonic-gate int rval = usba_destroy_child_devi(child_dip, 5817*7c478bd9Sstevel@tonic-gate NDI_DEVI_REMOVE); 5818*7c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 5819*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5820*7c478bd9Sstevel@tonic-gate "failure to remove child node"); 5821*7c478bd9Sstevel@tonic-gate } 5822*7c478bd9Sstevel@tonic-gate } 5823*7c478bd9Sstevel@tonic-gate 5824*7c478bd9Sstevel@tonic-gate if (child_ud) { 5825*7c478bd9Sstevel@tonic-gate /* to make sure we free the address */ 5826*7c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 5827*7c478bd9Sstevel@tonic-gate child_ud->usb_addr = address; 5828*7c478bd9Sstevel@tonic-gate ASSERT(child_ud->usb_ref_count == 0); 5829*7c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 5830*7c478bd9Sstevel@tonic-gate 5831*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 5832*7c478bd9Sstevel@tonic-gate if (hubd->h_usba_devices[port] == NULL) { 5833*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 5834*7c478bd9Sstevel@tonic-gate usba_free_usba_device(child_ud); 5835*7c478bd9Sstevel@tonic-gate } else { 5836*7c478bd9Sstevel@tonic-gate hubd_free_usba_device(hubd, hubd->h_usba_devices[port]); 5837*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 5838*7c478bd9Sstevel@tonic-gate } 5839*7c478bd9Sstevel@tonic-gate } 5840*7c478bd9Sstevel@tonic-gate 5841*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 5842*7c478bd9Sstevel@tonic-gate 5843*7c478bd9Sstevel@tonic-gate return (USB_FAILURE); 5844*7c478bd9Sstevel@tonic-gate } 5845*7c478bd9Sstevel@tonic-gate 5846*7c478bd9Sstevel@tonic-gate 5847*7c478bd9Sstevel@tonic-gate /* 5848*7c478bd9Sstevel@tonic-gate * hubd_delete_child: 5849*7c478bd9Sstevel@tonic-gate * - free usb address 5850*7c478bd9Sstevel@tonic-gate * - lookup child dips, there may be multiple on this port 5851*7c478bd9Sstevel@tonic-gate * - offline each child devi 5852*7c478bd9Sstevel@tonic-gate */ 5853*7c478bd9Sstevel@tonic-gate static int 5854*7c478bd9Sstevel@tonic-gate hubd_delete_child(hubd_t *hubd, usb_port_t port, uint_t flag, boolean_t retry) 5855*7c478bd9Sstevel@tonic-gate { 5856*7c478bd9Sstevel@tonic-gate dev_info_t *child_dip; 5857*7c478bd9Sstevel@tonic-gate usba_device_t *usba_device; 5858*7c478bd9Sstevel@tonic-gate int rval = USB_SUCCESS; 5859*7c478bd9Sstevel@tonic-gate 5860*7c478bd9Sstevel@tonic-gate child_dip = hubd->h_children_dips[port]; 5861*7c478bd9Sstevel@tonic-gate 5862*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5863*7c478bd9Sstevel@tonic-gate "hubd_delete_child: port=%d, dip=0x%p usba_device=0x%p", 5864*7c478bd9Sstevel@tonic-gate port, child_dip); 5865*7c478bd9Sstevel@tonic-gate 5866*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 5867*7c478bd9Sstevel@tonic-gate if (child_dip) { 5868*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5869*7c478bd9Sstevel@tonic-gate "hubd_delete_child:\n\t" 5870*7c478bd9Sstevel@tonic-gate "dip = 0x%p (%s) at port %d", 5871*7c478bd9Sstevel@tonic-gate child_dip, ddi_node_name(child_dip), port); 5872*7c478bd9Sstevel@tonic-gate 5873*7c478bd9Sstevel@tonic-gate rval = usba_destroy_child_devi(child_dip, flag); 5874*7c478bd9Sstevel@tonic-gate 5875*7c478bd9Sstevel@tonic-gate if ((rval == USB_SUCCESS) && (flag & NDI_DEVI_REMOVE)) { 5876*7c478bd9Sstevel@tonic-gate /* 5877*7c478bd9Sstevel@tonic-gate * if the child was still < DS_INITIALIZED 5878*7c478bd9Sstevel@tonic-gate * then our bus_unconfig was not called and 5879*7c478bd9Sstevel@tonic-gate * we have to zap the child here 5880*7c478bd9Sstevel@tonic-gate */ 5881*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 5882*7c478bd9Sstevel@tonic-gate if (hubd->h_children_dips[port] == child_dip) { 5883*7c478bd9Sstevel@tonic-gate usba_device_t *ud = 5884*7c478bd9Sstevel@tonic-gate hubd->h_usba_devices[port]; 5885*7c478bd9Sstevel@tonic-gate hubd->h_children_dips[port] = NULL; 5886*7c478bd9Sstevel@tonic-gate if (ud) { 5887*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 5888*7c478bd9Sstevel@tonic-gate 5889*7c478bd9Sstevel@tonic-gate mutex_enter(&ud->usb_mutex); 5890*7c478bd9Sstevel@tonic-gate ud->usb_ref_count = 0; 5891*7c478bd9Sstevel@tonic-gate mutex_exit(&ud->usb_mutex); 5892*7c478bd9Sstevel@tonic-gate 5893*7c478bd9Sstevel@tonic-gate usba_free_usba_device(ud); 5894*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 5895*7c478bd9Sstevel@tonic-gate hubd->h_usba_devices[port] = NULL; 5896*7c478bd9Sstevel@tonic-gate } 5897*7c478bd9Sstevel@tonic-gate } 5898*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 5899*7c478bd9Sstevel@tonic-gate } 5900*7c478bd9Sstevel@tonic-gate } 5901*7c478bd9Sstevel@tonic-gate 5902*7c478bd9Sstevel@tonic-gate if ((rval != USB_SUCCESS) && retry) { 5903*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 5904*7c478bd9Sstevel@tonic-gate usba_device = hubd->h_usba_devices[port]; 5905*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 5906*7c478bd9Sstevel@tonic-gate 5907*7c478bd9Sstevel@tonic-gate hubd_schedule_cleanup(usba_device->usb_root_hub_dip); 5908*7c478bd9Sstevel@tonic-gate } 5909*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 5910*7c478bd9Sstevel@tonic-gate 5911*7c478bd9Sstevel@tonic-gate return (rval); 5912*7c478bd9Sstevel@tonic-gate } 5913*7c478bd9Sstevel@tonic-gate 5914*7c478bd9Sstevel@tonic-gate 5915*7c478bd9Sstevel@tonic-gate /* 5916*7c478bd9Sstevel@tonic-gate * hubd_free_usba_device: 5917*7c478bd9Sstevel@tonic-gate * free usb device structure unless it is associated with 5918*7c478bd9Sstevel@tonic-gate * the root hub which is handled differently 5919*7c478bd9Sstevel@tonic-gate */ 5920*7c478bd9Sstevel@tonic-gate static void 5921*7c478bd9Sstevel@tonic-gate hubd_free_usba_device(hubd_t *hubd, usba_device_t *usba_device) 5922*7c478bd9Sstevel@tonic-gate { 5923*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5924*7c478bd9Sstevel@tonic-gate "hubd_free_usba_device: hubd=0x%p, usba_device=0x%p", 5925*7c478bd9Sstevel@tonic-gate hubd, usba_device); 5926*7c478bd9Sstevel@tonic-gate 5927*7c478bd9Sstevel@tonic-gate if (usba_device && (usba_device->usb_addr != ROOT_HUB_ADDR)) { 5928*7c478bd9Sstevel@tonic-gate usb_port_t port = usba_device->usb_port; 5929*7c478bd9Sstevel@tonic-gate dev_info_t *dip = hubd->h_children_dips[port]; 5930*7c478bd9Sstevel@tonic-gate 5931*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 5932*7c478bd9Sstevel@tonic-gate if (dip) { 5933*7c478bd9Sstevel@tonic-gate ASSERT(i_ddi_node_state(dip) < DS_INITIALIZED); 5934*7c478bd9Sstevel@tonic-gate } 5935*7c478bd9Sstevel@tonic-gate #endif 5936*7c478bd9Sstevel@tonic-gate 5937*7c478bd9Sstevel@tonic-gate port = usba_device->usb_port; 5938*7c478bd9Sstevel@tonic-gate hubd->h_usba_devices[port] = NULL; 5939*7c478bd9Sstevel@tonic-gate 5940*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 5941*7c478bd9Sstevel@tonic-gate usba_free_usba_device(usba_device); 5942*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 5943*7c478bd9Sstevel@tonic-gate } 5944*7c478bd9Sstevel@tonic-gate } 5945*7c478bd9Sstevel@tonic-gate 5946*7c478bd9Sstevel@tonic-gate 5947*7c478bd9Sstevel@tonic-gate /* 5948*7c478bd9Sstevel@tonic-gate * event support 5949*7c478bd9Sstevel@tonic-gate * 5950*7c478bd9Sstevel@tonic-gate * busctl event support 5951*7c478bd9Sstevel@tonic-gate */ 5952*7c478bd9Sstevel@tonic-gate static int 5953*7c478bd9Sstevel@tonic-gate hubd_busop_get_eventcookie(dev_info_t *dip, 5954*7c478bd9Sstevel@tonic-gate dev_info_t *rdip, 5955*7c478bd9Sstevel@tonic-gate char *eventname, 5956*7c478bd9Sstevel@tonic-gate ddi_eventcookie_t *cookie) 5957*7c478bd9Sstevel@tonic-gate { 5958*7c478bd9Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip); 5959*7c478bd9Sstevel@tonic-gate 5960*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5961*7c478bd9Sstevel@tonic-gate "hubd_busop_get_eventcookie: dip=0x%p, rdip=0x%p, " 5962*7c478bd9Sstevel@tonic-gate "event=%s", (void *)dip, (void *)rdip, eventname); 5963*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5964*7c478bd9Sstevel@tonic-gate "(dip=%s%d, rdip=%s%d)", 5965*7c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), 5966*7c478bd9Sstevel@tonic-gate ddi_driver_name(rdip), ddi_get_instance(rdip)); 5967*7c478bd9Sstevel@tonic-gate 5968*7c478bd9Sstevel@tonic-gate /* return event cookie, iblock cookie, and level */ 5969*7c478bd9Sstevel@tonic-gate return (ndi_event_retrieve_cookie(hubd->h_ndi_event_hdl, 5970*7c478bd9Sstevel@tonic-gate rdip, eventname, cookie, NDI_EVENT_NOPASS)); 5971*7c478bd9Sstevel@tonic-gate } 5972*7c478bd9Sstevel@tonic-gate 5973*7c478bd9Sstevel@tonic-gate 5974*7c478bd9Sstevel@tonic-gate static int 5975*7c478bd9Sstevel@tonic-gate hubd_busop_add_eventcall(dev_info_t *dip, 5976*7c478bd9Sstevel@tonic-gate dev_info_t *rdip, 5977*7c478bd9Sstevel@tonic-gate ddi_eventcookie_t cookie, 5978*7c478bd9Sstevel@tonic-gate void (*callback)(dev_info_t *dip, 5979*7c478bd9Sstevel@tonic-gate ddi_eventcookie_t cookie, void *arg, 5980*7c478bd9Sstevel@tonic-gate void *bus_impldata), 5981*7c478bd9Sstevel@tonic-gate void *arg, ddi_callback_id_t *cb_id) 5982*7c478bd9Sstevel@tonic-gate { 5983*7c478bd9Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip); 5984*7c478bd9Sstevel@tonic-gate usb_port_t port = hubd_child_dip2port(hubd, rdip); 5985*7c478bd9Sstevel@tonic-gate 5986*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5987*7c478bd9Sstevel@tonic-gate "hubd_busop_add_eventcall: dip=0x%p, rdip=0x%p " 5988*7c478bd9Sstevel@tonic-gate "cookie=0x%p, cb=0x%p, arg=0x%p", 5989*7c478bd9Sstevel@tonic-gate (void *)dip, (void *)rdip, (void *)cookie, (void *)callback, arg); 5990*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5991*7c478bd9Sstevel@tonic-gate "(dip=%s%d, rdip=%s%d, event=%s)", 5992*7c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), 5993*7c478bd9Sstevel@tonic-gate ddi_driver_name(rdip), ddi_get_instance(rdip), 5994*7c478bd9Sstevel@tonic-gate ndi_event_cookie_to_name(hubd->h_ndi_event_hdl, cookie)); 5995*7c478bd9Sstevel@tonic-gate 5996*7c478bd9Sstevel@tonic-gate /* Set flag on children registering events */ 5997*7c478bd9Sstevel@tonic-gate switch (ndi_event_cookie_to_tag(hubd->h_ndi_event_hdl, cookie)) { 5998*7c478bd9Sstevel@tonic-gate case USBA_EVENT_TAG_HOT_REMOVAL: 5999*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6000*7c478bd9Sstevel@tonic-gate hubd->h_child_events[port] |= HUBD_CHILD_EVENT_DISCONNECT; 6001*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6002*7c478bd9Sstevel@tonic-gate 6003*7c478bd9Sstevel@tonic-gate break; 6004*7c478bd9Sstevel@tonic-gate case USBA_EVENT_TAG_PRE_SUSPEND: 6005*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6006*7c478bd9Sstevel@tonic-gate hubd->h_child_events[port] |= HUBD_CHILD_EVENT_PRESUSPEND; 6007*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6008*7c478bd9Sstevel@tonic-gate 6009*7c478bd9Sstevel@tonic-gate break; 6010*7c478bd9Sstevel@tonic-gate default: 6011*7c478bd9Sstevel@tonic-gate 6012*7c478bd9Sstevel@tonic-gate break; 6013*7c478bd9Sstevel@tonic-gate } 6014*7c478bd9Sstevel@tonic-gate 6015*7c478bd9Sstevel@tonic-gate /* add callback to our event set */ 6016*7c478bd9Sstevel@tonic-gate return (ndi_event_add_callback(hubd->h_ndi_event_hdl, 6017*7c478bd9Sstevel@tonic-gate rdip, cookie, callback, arg, NDI_SLEEP, cb_id)); 6018*7c478bd9Sstevel@tonic-gate } 6019*7c478bd9Sstevel@tonic-gate 6020*7c478bd9Sstevel@tonic-gate 6021*7c478bd9Sstevel@tonic-gate static int 6022*7c478bd9Sstevel@tonic-gate hubd_busop_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id) 6023*7c478bd9Sstevel@tonic-gate { 6024*7c478bd9Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip); 6025*7c478bd9Sstevel@tonic-gate ndi_event_callbacks_t *id = (ndi_event_callbacks_t *)cb_id; 6026*7c478bd9Sstevel@tonic-gate 6027*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 6028*7c478bd9Sstevel@tonic-gate "hubd_busop_remove_eventcall: dip=0x%p, rdip=0x%p " 6029*7c478bd9Sstevel@tonic-gate "cookie=0x%p", (void *)dip, id->ndi_evtcb_dip, 6030*7c478bd9Sstevel@tonic-gate id->ndi_evtcb_cookie); 6031*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 6032*7c478bd9Sstevel@tonic-gate "(dip=%s%d, rdip=%s%d, event=%s)", 6033*7c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), 6034*7c478bd9Sstevel@tonic-gate ddi_driver_name(id->ndi_evtcb_dip), 6035*7c478bd9Sstevel@tonic-gate ddi_get_instance(id->ndi_evtcb_dip), 6036*7c478bd9Sstevel@tonic-gate ndi_event_cookie_to_name(hubd->h_ndi_event_hdl, 6037*7c478bd9Sstevel@tonic-gate id->ndi_evtcb_cookie)); 6038*7c478bd9Sstevel@tonic-gate 6039*7c478bd9Sstevel@tonic-gate /* remove event registration from our event set */ 6040*7c478bd9Sstevel@tonic-gate return (ndi_event_remove_callback(hubd->h_ndi_event_hdl, cb_id)); 6041*7c478bd9Sstevel@tonic-gate } 6042*7c478bd9Sstevel@tonic-gate 6043*7c478bd9Sstevel@tonic-gate 6044*7c478bd9Sstevel@tonic-gate /* 6045*7c478bd9Sstevel@tonic-gate * event distribution 6046*7c478bd9Sstevel@tonic-gate * 6047*7c478bd9Sstevel@tonic-gate * hubd_do_callback: 6048*7c478bd9Sstevel@tonic-gate * Post this event to the specified child 6049*7c478bd9Sstevel@tonic-gate */ 6050*7c478bd9Sstevel@tonic-gate static void 6051*7c478bd9Sstevel@tonic-gate hubd_do_callback(hubd_t *hubd, dev_info_t *cdip, ddi_eventcookie_t cookie) 6052*7c478bd9Sstevel@tonic-gate { 6053*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 6054*7c478bd9Sstevel@tonic-gate "hubd_do_callback"); 6055*7c478bd9Sstevel@tonic-gate 6056*7c478bd9Sstevel@tonic-gate (void) ndi_event_do_callback(hubd->h_ndi_event_hdl, cdip, cookie, NULL); 6057*7c478bd9Sstevel@tonic-gate } 6058*7c478bd9Sstevel@tonic-gate 6059*7c478bd9Sstevel@tonic-gate 6060*7c478bd9Sstevel@tonic-gate /* 6061*7c478bd9Sstevel@tonic-gate * hubd_run_callbacks: 6062*7c478bd9Sstevel@tonic-gate * Send this event to all children 6063*7c478bd9Sstevel@tonic-gate */ 6064*7c478bd9Sstevel@tonic-gate static void 6065*7c478bd9Sstevel@tonic-gate hubd_run_callbacks(hubd_t *hubd, usba_event_t type) 6066*7c478bd9Sstevel@tonic-gate { 6067*7c478bd9Sstevel@tonic-gate usb_port_t port; 6068*7c478bd9Sstevel@tonic-gate 6069*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 6070*7c478bd9Sstevel@tonic-gate "hubd_run_callbacks"); 6071*7c478bd9Sstevel@tonic-gate 6072*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6073*7c478bd9Sstevel@tonic-gate for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { 6074*7c478bd9Sstevel@tonic-gate /* 6075*7c478bd9Sstevel@tonic-gate * the childen_dips list may have dips that have been 6076*7c478bd9Sstevel@tonic-gate * already deallocated. we only get a post_detach notification 6077*7c478bd9Sstevel@tonic-gate * but not a destroy notification 6078*7c478bd9Sstevel@tonic-gate */ 6079*7c478bd9Sstevel@tonic-gate if (hubd->h_children_dips[port]) { 6080*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6081*7c478bd9Sstevel@tonic-gate hubd_post_event(hubd, port, type); 6082*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6083*7c478bd9Sstevel@tonic-gate } 6084*7c478bd9Sstevel@tonic-gate } 6085*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6086*7c478bd9Sstevel@tonic-gate } 6087*7c478bd9Sstevel@tonic-gate 6088*7c478bd9Sstevel@tonic-gate 6089*7c478bd9Sstevel@tonic-gate /* 6090*7c478bd9Sstevel@tonic-gate * hubd_post_event 6091*7c478bd9Sstevel@tonic-gate * post event to a child on the port depending on the type 6092*7c478bd9Sstevel@tonic-gate */ 6093*7c478bd9Sstevel@tonic-gate static void 6094*7c478bd9Sstevel@tonic-gate hubd_post_event(hubd_t *hubd, usb_port_t port, usba_event_t type) 6095*7c478bd9Sstevel@tonic-gate { 6096*7c478bd9Sstevel@tonic-gate int rval; 6097*7c478bd9Sstevel@tonic-gate dev_info_t *dip; 6098*7c478bd9Sstevel@tonic-gate usba_device_t *usba_device; 6099*7c478bd9Sstevel@tonic-gate ddi_eventcookie_t cookie, rm_cookie, suspend_cookie; 6100*7c478bd9Sstevel@tonic-gate 6101*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 6102*7c478bd9Sstevel@tonic-gate "hubd_post_event: port=%d event=%s", port, 6103*7c478bd9Sstevel@tonic-gate ndi_event_tag_to_name(hubd->h_ndi_event_hdl, type)); 6104*7c478bd9Sstevel@tonic-gate 6105*7c478bd9Sstevel@tonic-gate cookie = ndi_event_tag_to_cookie(hubd->h_ndi_event_hdl, type); 6106*7c478bd9Sstevel@tonic-gate rm_cookie = ndi_event_tag_to_cookie(hubd->h_ndi_event_hdl, 6107*7c478bd9Sstevel@tonic-gate USBA_EVENT_TAG_HOT_REMOVAL); 6108*7c478bd9Sstevel@tonic-gate suspend_cookie = ndi_event_tag_to_cookie(hubd->h_ndi_event_hdl, 6109*7c478bd9Sstevel@tonic-gate USBA_EVENT_TAG_PRE_SUSPEND); 6110*7c478bd9Sstevel@tonic-gate 6111*7c478bd9Sstevel@tonic-gate /* 6112*7c478bd9Sstevel@tonic-gate * Hotplug daemon may be attaching a driver that may be registering 6113*7c478bd9Sstevel@tonic-gate * event callbacks. So it already has got the device tree lock and 6114*7c478bd9Sstevel@tonic-gate * event handle mutex. So to prevent a deadlock while posting events, 6115*7c478bd9Sstevel@tonic-gate * we grab and release the locks in the same order. 6116*7c478bd9Sstevel@tonic-gate */ 6117*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6118*7c478bd9Sstevel@tonic-gate dip = hubd->h_children_dips[port]; 6119*7c478bd9Sstevel@tonic-gate usba_device = hubd->h_usba_devices[port]; 6120*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6121*7c478bd9Sstevel@tonic-gate 6122*7c478bd9Sstevel@tonic-gate switch (type) { 6123*7c478bd9Sstevel@tonic-gate case USBA_EVENT_TAG_HOT_REMOVAL: 6124*7c478bd9Sstevel@tonic-gate /* Clear the registered event flag */ 6125*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6126*7c478bd9Sstevel@tonic-gate hubd->h_child_events[port] &= ~HUBD_CHILD_EVENT_DISCONNECT; 6127*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6128*7c478bd9Sstevel@tonic-gate 6129*7c478bd9Sstevel@tonic-gate hubd_do_callback(hubd, dip, cookie); 6130*7c478bd9Sstevel@tonic-gate usba_persistent_pipe_close(usba_device); 6131*7c478bd9Sstevel@tonic-gate 6132*7c478bd9Sstevel@tonic-gate /* 6133*7c478bd9Sstevel@tonic-gate * Mark the dip for deletion only after the driver has 6134*7c478bd9Sstevel@tonic-gate * seen the disconnect event to prevent cleanup thread 6135*7c478bd9Sstevel@tonic-gate * from stepping in between. 6136*7c478bd9Sstevel@tonic-gate */ 6137*7c478bd9Sstevel@tonic-gate DEVI_SET_DEVICE_REMOVED(dip); 6138*7c478bd9Sstevel@tonic-gate 6139*7c478bd9Sstevel@tonic-gate break; 6140*7c478bd9Sstevel@tonic-gate case USBA_EVENT_TAG_PRE_SUSPEND: 6141*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6142*7c478bd9Sstevel@tonic-gate hubd->h_child_events[port] &= ~HUBD_CHILD_EVENT_PRESUSPEND; 6143*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6144*7c478bd9Sstevel@tonic-gate 6145*7c478bd9Sstevel@tonic-gate hubd_do_callback(hubd, dip, cookie); 6146*7c478bd9Sstevel@tonic-gate /* 6147*7c478bd9Sstevel@tonic-gate * persistent pipe close for this event is taken care by the 6148*7c478bd9Sstevel@tonic-gate * caller after verfying that all children can suspend 6149*7c478bd9Sstevel@tonic-gate */ 6150*7c478bd9Sstevel@tonic-gate 6151*7c478bd9Sstevel@tonic-gate break; 6152*7c478bd9Sstevel@tonic-gate case USBA_EVENT_TAG_HOT_INSERTION: 6153*7c478bd9Sstevel@tonic-gate /* 6154*7c478bd9Sstevel@tonic-gate * Check if this child has missed the disconnect event before 6155*7c478bd9Sstevel@tonic-gate * it registered for event callbacks 6156*7c478bd9Sstevel@tonic-gate */ 6157*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6158*7c478bd9Sstevel@tonic-gate if (hubd->h_child_events[port] & HUBD_CHILD_EVENT_DISCONNECT) { 6159*7c478bd9Sstevel@tonic-gate /* clear the flag and post disconnect event */ 6160*7c478bd9Sstevel@tonic-gate hubd->h_child_events[port] &= 6161*7c478bd9Sstevel@tonic-gate ~HUBD_CHILD_EVENT_DISCONNECT; 6162*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6163*7c478bd9Sstevel@tonic-gate hubd_do_callback(hubd, dip, rm_cookie); 6164*7c478bd9Sstevel@tonic-gate usba_persistent_pipe_close(usba_device); 6165*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6166*7c478bd9Sstevel@tonic-gate } 6167*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6168*7c478bd9Sstevel@tonic-gate 6169*7c478bd9Sstevel@tonic-gate /* 6170*7c478bd9Sstevel@tonic-gate * Mark the dip as reinserted to prevent cleanup thread 6171*7c478bd9Sstevel@tonic-gate * from stepping in. 6172*7c478bd9Sstevel@tonic-gate */ 6173*7c478bd9Sstevel@tonic-gate DEVI_SET_DEVICE_REINSERTED(dip); 6174*7c478bd9Sstevel@tonic-gate 6175*7c478bd9Sstevel@tonic-gate rval = usba_persistent_pipe_open(usba_device); 6176*7c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 6177*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, 6178*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 6179*7c478bd9Sstevel@tonic-gate "failed to reopen all pipes on reconnect"); 6180*7c478bd9Sstevel@tonic-gate } 6181*7c478bd9Sstevel@tonic-gate 6182*7c478bd9Sstevel@tonic-gate hubd_do_callback(hubd, dip, cookie); 6183*7c478bd9Sstevel@tonic-gate 6184*7c478bd9Sstevel@tonic-gate /* 6185*7c478bd9Sstevel@tonic-gate * We might see a connect event only if hotplug thread for 6186*7c478bd9Sstevel@tonic-gate * disconnect event don't run in time. 6187*7c478bd9Sstevel@tonic-gate * Set the flag again, so we don't miss posting a 6188*7c478bd9Sstevel@tonic-gate * disconnect event. 6189*7c478bd9Sstevel@tonic-gate */ 6190*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6191*7c478bd9Sstevel@tonic-gate hubd->h_child_events[port] |= HUBD_CHILD_EVENT_DISCONNECT; 6192*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6193*7c478bd9Sstevel@tonic-gate 6194*7c478bd9Sstevel@tonic-gate break; 6195*7c478bd9Sstevel@tonic-gate case USBA_EVENT_TAG_POST_RESUME: 6196*7c478bd9Sstevel@tonic-gate /* 6197*7c478bd9Sstevel@tonic-gate * Check if this child has missed the pre-suspend event before 6198*7c478bd9Sstevel@tonic-gate * it registered for event callbacks 6199*7c478bd9Sstevel@tonic-gate */ 6200*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6201*7c478bd9Sstevel@tonic-gate if (hubd->h_child_events[port] & HUBD_CHILD_EVENT_PRESUSPEND) { 6202*7c478bd9Sstevel@tonic-gate /* clear the flag and post pre_suspend event */ 6203*7c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= 6204*7c478bd9Sstevel@tonic-gate ~HUBD_CHILD_EVENT_PRESUSPEND; 6205*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6206*7c478bd9Sstevel@tonic-gate hubd_do_callback(hubd, dip, suspend_cookie); 6207*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6208*7c478bd9Sstevel@tonic-gate } 6209*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6210*7c478bd9Sstevel@tonic-gate 6211*7c478bd9Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex); 6212*7c478bd9Sstevel@tonic-gate usba_device->usb_no_cpr = 0; 6213*7c478bd9Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex); 6214*7c478bd9Sstevel@tonic-gate 6215*7c478bd9Sstevel@tonic-gate /* 6216*7c478bd9Sstevel@tonic-gate * Since the pipe has already been opened by hub 6217*7c478bd9Sstevel@tonic-gate * at DDI_RESUME time, there is no need for a 6218*7c478bd9Sstevel@tonic-gate * persistent pipe open 6219*7c478bd9Sstevel@tonic-gate */ 6220*7c478bd9Sstevel@tonic-gate hubd_do_callback(hubd, dip, cookie); 6221*7c478bd9Sstevel@tonic-gate 6222*7c478bd9Sstevel@tonic-gate /* 6223*7c478bd9Sstevel@tonic-gate * Set the flag again, so we don't miss posting a 6224*7c478bd9Sstevel@tonic-gate * pre-suspend event. This enforces a tighter 6225*7c478bd9Sstevel@tonic-gate * dev_state model. 6226*7c478bd9Sstevel@tonic-gate */ 6227*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6228*7c478bd9Sstevel@tonic-gate hubd->h_child_events[port] |= HUBD_CHILD_EVENT_PRESUSPEND; 6229*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6230*7c478bd9Sstevel@tonic-gate break; 6231*7c478bd9Sstevel@tonic-gate } 6232*7c478bd9Sstevel@tonic-gate } 6233*7c478bd9Sstevel@tonic-gate 6234*7c478bd9Sstevel@tonic-gate 6235*7c478bd9Sstevel@tonic-gate /* 6236*7c478bd9Sstevel@tonic-gate * handling of events coming from above 6237*7c478bd9Sstevel@tonic-gate */ 6238*7c478bd9Sstevel@tonic-gate static int 6239*7c478bd9Sstevel@tonic-gate hubd_disconnect_event_cb(dev_info_t *dip) 6240*7c478bd9Sstevel@tonic-gate { 6241*7c478bd9Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip); 6242*7c478bd9Sstevel@tonic-gate usb_port_t port, nports; 6243*7c478bd9Sstevel@tonic-gate usba_device_t *usba_dev; 6244*7c478bd9Sstevel@tonic-gate usba_event_t tag = USBA_EVENT_TAG_HOT_REMOVAL; 6245*7c478bd9Sstevel@tonic-gate int circ; 6246*7c478bd9Sstevel@tonic-gate 6247*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 6248*7c478bd9Sstevel@tonic-gate "hubd_disconnect_event_cb: tag=%d", tag); 6249*7c478bd9Sstevel@tonic-gate 6250*7c478bd9Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 6251*7c478bd9Sstevel@tonic-gate 6252*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6253*7c478bd9Sstevel@tonic-gate switch (hubd->h_dev_state) { 6254*7c478bd9Sstevel@tonic-gate case USB_DEV_ONLINE: 6255*7c478bd9Sstevel@tonic-gate case USB_DEV_PWRED_DOWN: 6256*7c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_DISCONNECTED; 6257*7c478bd9Sstevel@tonic-gate /* stop polling on the interrupt pipe */ 6258*7c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd); 6259*7c478bd9Sstevel@tonic-gate 6260*7c478bd9Sstevel@tonic-gate /* FALLTHROUGH */ 6261*7c478bd9Sstevel@tonic-gate case USB_DEV_SUSPENDED: 6262*7c478bd9Sstevel@tonic-gate /* we remain in this state */ 6263*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6264*7c478bd9Sstevel@tonic-gate hubd_run_callbacks(hubd, tag); 6265*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6266*7c478bd9Sstevel@tonic-gate 6267*7c478bd9Sstevel@tonic-gate /* close all the open pipes of our children */ 6268*7c478bd9Sstevel@tonic-gate nports = hubd->h_hub_descr.bNbrPorts; 6269*7c478bd9Sstevel@tonic-gate for (port = 1; port <= nports; port++) { 6270*7c478bd9Sstevel@tonic-gate usba_dev = hubd->h_usba_devices[port]; 6271*7c478bd9Sstevel@tonic-gate if (usba_dev != NULL) { 6272*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6273*7c478bd9Sstevel@tonic-gate usba_persistent_pipe_close(usba_dev); 6274*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6275*7c478bd9Sstevel@tonic-gate } 6276*7c478bd9Sstevel@tonic-gate } 6277*7c478bd9Sstevel@tonic-gate 6278*7c478bd9Sstevel@tonic-gate break; 6279*7c478bd9Sstevel@tonic-gate case USB_DEV_DISCONNECTED: 6280*7c478bd9Sstevel@tonic-gate /* avoid passing multiple disconnects to children */ 6281*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 6282*7c478bd9Sstevel@tonic-gate "hubd_disconnect_event_cb: Already disconnected"); 6283*7c478bd9Sstevel@tonic-gate 6284*7c478bd9Sstevel@tonic-gate break; 6285*7c478bd9Sstevel@tonic-gate default: 6286*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 6287*7c478bd9Sstevel@tonic-gate "hubd_disconnect_event_cb: Illegal devstate=%d", 6288*7c478bd9Sstevel@tonic-gate hubd->h_dev_state); 6289*7c478bd9Sstevel@tonic-gate 6290*7c478bd9Sstevel@tonic-gate break; 6291*7c478bd9Sstevel@tonic-gate } 6292*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6293*7c478bd9Sstevel@tonic-gate 6294*7c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 6295*7c478bd9Sstevel@tonic-gate 6296*7c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 6297*7c478bd9Sstevel@tonic-gate } 6298*7c478bd9Sstevel@tonic-gate 6299*7c478bd9Sstevel@tonic-gate 6300*7c478bd9Sstevel@tonic-gate static int 6301*7c478bd9Sstevel@tonic-gate hubd_reconnect_event_cb(dev_info_t *dip) 6302*7c478bd9Sstevel@tonic-gate { 6303*7c478bd9Sstevel@tonic-gate int rval, circ; 6304*7c478bd9Sstevel@tonic-gate 6305*7c478bd9Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 6306*7c478bd9Sstevel@tonic-gate rval = hubd_restore_state_cb(dip); 6307*7c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 6308*7c478bd9Sstevel@tonic-gate 6309*7c478bd9Sstevel@tonic-gate return (rval); 6310*7c478bd9Sstevel@tonic-gate } 6311*7c478bd9Sstevel@tonic-gate 6312*7c478bd9Sstevel@tonic-gate 6313*7c478bd9Sstevel@tonic-gate /* 6314*7c478bd9Sstevel@tonic-gate * hubd_pre_suspend_event_cb 6315*7c478bd9Sstevel@tonic-gate * propogate event for binary compatibility of old drivers 6316*7c478bd9Sstevel@tonic-gate */ 6317*7c478bd9Sstevel@tonic-gate static int 6318*7c478bd9Sstevel@tonic-gate hubd_pre_suspend_event_cb(dev_info_t *dip) 6319*7c478bd9Sstevel@tonic-gate { 6320*7c478bd9Sstevel@tonic-gate int circ; 6321*7c478bd9Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip); 6322*7c478bd9Sstevel@tonic-gate 6323*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle, 6324*7c478bd9Sstevel@tonic-gate "hubd_pre_suspend_event_cb"); 6325*7c478bd9Sstevel@tonic-gate 6326*7c478bd9Sstevel@tonic-gate /* disable hotplug thread */ 6327*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6328*7c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread++; 6329*7c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd); 6330*7c478bd9Sstevel@tonic-gate 6331*7c478bd9Sstevel@tonic-gate /* keep PM out till we see a cpr resume */ 6332*7c478bd9Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0); 6333*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6334*7c478bd9Sstevel@tonic-gate 6335*7c478bd9Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 6336*7c478bd9Sstevel@tonic-gate hubd_run_callbacks(hubd, USBA_EVENT_TAG_PRE_SUSPEND); 6337*7c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 6338*7c478bd9Sstevel@tonic-gate 6339*7c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 6340*7c478bd9Sstevel@tonic-gate } 6341*7c478bd9Sstevel@tonic-gate 6342*7c478bd9Sstevel@tonic-gate 6343*7c478bd9Sstevel@tonic-gate /* 6344*7c478bd9Sstevel@tonic-gate * hubd_post_resume_event_cb 6345*7c478bd9Sstevel@tonic-gate * propogate event for binary compatibility of old drivers 6346*7c478bd9Sstevel@tonic-gate */ 6347*7c478bd9Sstevel@tonic-gate static int 6348*7c478bd9Sstevel@tonic-gate hubd_post_resume_event_cb(dev_info_t *dip) 6349*7c478bd9Sstevel@tonic-gate { 6350*7c478bd9Sstevel@tonic-gate int circ; 6351*7c478bd9Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip); 6352*7c478bd9Sstevel@tonic-gate 6353*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle, 6354*7c478bd9Sstevel@tonic-gate "hubd_post_resume_event_cb"); 6355*7c478bd9Sstevel@tonic-gate 6356*7c478bd9Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 6357*7c478bd9Sstevel@tonic-gate hubd_run_callbacks(hubd, USBA_EVENT_TAG_POST_RESUME); 6358*7c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 6359*7c478bd9Sstevel@tonic-gate 6360*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6361*7c478bd9Sstevel@tonic-gate 6362*7c478bd9Sstevel@tonic-gate /* enable PM */ 6363*7c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0); 6364*7c478bd9Sstevel@tonic-gate 6365*7c478bd9Sstevel@tonic-gate /* allow hotplug thread */ 6366*7c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread--; 6367*7c478bd9Sstevel@tonic-gate 6368*7c478bd9Sstevel@tonic-gate /* start polling */ 6369*7c478bd9Sstevel@tonic-gate hubd_start_polling(hubd, 0); 6370*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6371*7c478bd9Sstevel@tonic-gate 6372*7c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 6373*7c478bd9Sstevel@tonic-gate } 6374*7c478bd9Sstevel@tonic-gate 6375*7c478bd9Sstevel@tonic-gate 6376*7c478bd9Sstevel@tonic-gate /* 6377*7c478bd9Sstevel@tonic-gate * hubd_cpr_suspend 6378*7c478bd9Sstevel@tonic-gate * save the current state of the driver/device 6379*7c478bd9Sstevel@tonic-gate */ 6380*7c478bd9Sstevel@tonic-gate static int 6381*7c478bd9Sstevel@tonic-gate hubd_cpr_suspend(hubd_t *hubd) 6382*7c478bd9Sstevel@tonic-gate { 6383*7c478bd9Sstevel@tonic-gate usb_port_t port, nports; 6384*7c478bd9Sstevel@tonic-gate usba_device_t *usba_dev; 6385*7c478bd9Sstevel@tonic-gate uchar_t no_cpr = 0; 6386*7c478bd9Sstevel@tonic-gate int rval = USB_FAILURE; 6387*7c478bd9Sstevel@tonic-gate 6388*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 6389*7c478bd9Sstevel@tonic-gate "hubd_cpr_suspend: Begin"); 6390*7c478bd9Sstevel@tonic-gate 6391*7c478bd9Sstevel@tonic-gate /* Make sure device is powered up to save state. */ 6392*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6393*7c478bd9Sstevel@tonic-gate hubd_pm_busy_component(hubd, hubd->h_dip, 0); 6394*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6395*7c478bd9Sstevel@tonic-gate 6396*7c478bd9Sstevel@tonic-gate /* bring the device to full power */ 6397*7c478bd9Sstevel@tonic-gate (void) pm_raise_power(hubd->h_dip, 0, USB_DEV_OS_FULL_PWR); 6398*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6399*7c478bd9Sstevel@tonic-gate 6400*7c478bd9Sstevel@tonic-gate switch (hubd->h_dev_state) { 6401*7c478bd9Sstevel@tonic-gate case USB_DEV_ONLINE: 6402*7c478bd9Sstevel@tonic-gate case USB_DEV_PWRED_DOWN: 6403*7c478bd9Sstevel@tonic-gate case USB_DEV_DISCONNECTED: 6404*7c478bd9Sstevel@tonic-gate /* find out if all our children have been quiesced */ 6405*7c478bd9Sstevel@tonic-gate nports = hubd->h_hub_descr.bNbrPorts; 6406*7c478bd9Sstevel@tonic-gate for (port = 1; (no_cpr == 0) && (port <= nports); port++) { 6407*7c478bd9Sstevel@tonic-gate usba_dev = hubd->h_usba_devices[port]; 6408*7c478bd9Sstevel@tonic-gate if (usba_dev != NULL) { 6409*7c478bd9Sstevel@tonic-gate mutex_enter(&usba_dev->usb_mutex); 6410*7c478bd9Sstevel@tonic-gate no_cpr += usba_dev->usb_no_cpr; 6411*7c478bd9Sstevel@tonic-gate mutex_exit(&usba_dev->usb_mutex); 6412*7c478bd9Sstevel@tonic-gate } 6413*7c478bd9Sstevel@tonic-gate } 6414*7c478bd9Sstevel@tonic-gate if (no_cpr > 0) { 6415*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 6416*7c478bd9Sstevel@tonic-gate "Children busy - can't checkpoint"); 6417*7c478bd9Sstevel@tonic-gate /* remain in same state to fail checkpoint */ 6418*7c478bd9Sstevel@tonic-gate 6419*7c478bd9Sstevel@tonic-gate break; 6420*7c478bd9Sstevel@tonic-gate } else { 6421*7c478bd9Sstevel@tonic-gate /* 6422*7c478bd9Sstevel@tonic-gate * do not suspend if our hotplug thread 6423*7c478bd9Sstevel@tonic-gate * or the deathrow thread is active 6424*7c478bd9Sstevel@tonic-gate */ 6425*7c478bd9Sstevel@tonic-gate if ((hubd->h_hotplug_thread > 1) || 6426*7c478bd9Sstevel@tonic-gate (hubd->h_cleanup_active == B_TRUE)) { 6427*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 6428*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 6429*7c478bd9Sstevel@tonic-gate "hotplug thread active - can't cpr"); 6430*7c478bd9Sstevel@tonic-gate /* remain in same state to fail checkpoint */ 6431*7c478bd9Sstevel@tonic-gate 6432*7c478bd9Sstevel@tonic-gate break; 6433*7c478bd9Sstevel@tonic-gate } 6434*7c478bd9Sstevel@tonic-gate 6435*7c478bd9Sstevel@tonic-gate /* quiesce ourselves now */ 6436*7c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_SUSPENDED; 6437*7c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd); 6438*7c478bd9Sstevel@tonic-gate 6439*7c478bd9Sstevel@tonic-gate /* close all the open pipes of our children */ 6440*7c478bd9Sstevel@tonic-gate for (port = 1; port <= nports; port++) { 6441*7c478bd9Sstevel@tonic-gate usba_dev = hubd->h_usba_devices[port]; 6442*7c478bd9Sstevel@tonic-gate if (usba_dev != NULL) { 6443*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6444*7c478bd9Sstevel@tonic-gate usba_persistent_pipe_close(usba_dev); 6445*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6446*7c478bd9Sstevel@tonic-gate } 6447*7c478bd9Sstevel@tonic-gate } 6448*7c478bd9Sstevel@tonic-gate /* 6449*7c478bd9Sstevel@tonic-gate * turn off power to all the ports so that we 6450*7c478bd9Sstevel@tonic-gate * don't see any spurious activity 6451*7c478bd9Sstevel@tonic-gate */ 6452*7c478bd9Sstevel@tonic-gate (void) hubd_disable_all_port_power(hubd); 6453*7c478bd9Sstevel@tonic-gate 6454*7c478bd9Sstevel@tonic-gate /* 6455*7c478bd9Sstevel@tonic-gate * if we are the root hub, we close our pipes 6456*7c478bd9Sstevel@tonic-gate * ourselves. 6457*7c478bd9Sstevel@tonic-gate */ 6458*7c478bd9Sstevel@tonic-gate if (usba_is_root_hub(hubd->h_dip)) { 6459*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6460*7c478bd9Sstevel@tonic-gate usba_persistent_pipe_close( 6461*7c478bd9Sstevel@tonic-gate usba_get_usba_device(hubd->h_dip)); 6462*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6463*7c478bd9Sstevel@tonic-gate } 6464*7c478bd9Sstevel@tonic-gate rval = USB_SUCCESS; 6465*7c478bd9Sstevel@tonic-gate 6466*7c478bd9Sstevel@tonic-gate break; 6467*7c478bd9Sstevel@tonic-gate } 6468*7c478bd9Sstevel@tonic-gate case USB_DEV_SUSPENDED: 6469*7c478bd9Sstevel@tonic-gate default: 6470*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 6471*7c478bd9Sstevel@tonic-gate "hubd_cpr_suspend: Illegal dev state=%d", 6472*7c478bd9Sstevel@tonic-gate hubd->h_dev_state); 6473*7c478bd9Sstevel@tonic-gate 6474*7c478bd9Sstevel@tonic-gate break; 6475*7c478bd9Sstevel@tonic-gate } 6476*7c478bd9Sstevel@tonic-gate 6477*7c478bd9Sstevel@tonic-gate hubd_pm_idle_component(hubd, hubd->h_dip, 0); 6478*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6479*7c478bd9Sstevel@tonic-gate 6480*7c478bd9Sstevel@tonic-gate return (rval); 6481*7c478bd9Sstevel@tonic-gate } 6482*7c478bd9Sstevel@tonic-gate 6483*7c478bd9Sstevel@tonic-gate static void 6484*7c478bd9Sstevel@tonic-gate hubd_cpr_resume(dev_info_t *dip) 6485*7c478bd9Sstevel@tonic-gate { 6486*7c478bd9Sstevel@tonic-gate int rval, circ; 6487*7c478bd9Sstevel@tonic-gate 6488*7c478bd9Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 6489*7c478bd9Sstevel@tonic-gate /* 6490*7c478bd9Sstevel@tonic-gate * if we are the root hub, we open our pipes 6491*7c478bd9Sstevel@tonic-gate * ourselves. 6492*7c478bd9Sstevel@tonic-gate */ 6493*7c478bd9Sstevel@tonic-gate if (usba_is_root_hub(dip)) { 6494*7c478bd9Sstevel@tonic-gate rval = usba_persistent_pipe_open( 6495*7c478bd9Sstevel@tonic-gate usba_get_usba_device(dip)); 6496*7c478bd9Sstevel@tonic-gate ASSERT(rval == USB_SUCCESS); 6497*7c478bd9Sstevel@tonic-gate } 6498*7c478bd9Sstevel@tonic-gate (void) hubd_restore_state_cb(dip); 6499*7c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 6500*7c478bd9Sstevel@tonic-gate } 6501*7c478bd9Sstevel@tonic-gate 6502*7c478bd9Sstevel@tonic-gate 6503*7c478bd9Sstevel@tonic-gate /* 6504*7c478bd9Sstevel@tonic-gate * hubd_restore_state_cb 6505*7c478bd9Sstevel@tonic-gate * Event callback to restore device state 6506*7c478bd9Sstevel@tonic-gate */ 6507*7c478bd9Sstevel@tonic-gate static int 6508*7c478bd9Sstevel@tonic-gate hubd_restore_state_cb(dev_info_t *dip) 6509*7c478bd9Sstevel@tonic-gate { 6510*7c478bd9Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip); 6511*7c478bd9Sstevel@tonic-gate 6512*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 6513*7c478bd9Sstevel@tonic-gate "hubd_restore_state_cb: Begin"); 6514*7c478bd9Sstevel@tonic-gate 6515*7c478bd9Sstevel@tonic-gate /* restore the state of this device */ 6516*7c478bd9Sstevel@tonic-gate hubd_restore_device_state(dip, hubd); 6517*7c478bd9Sstevel@tonic-gate 6518*7c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 6519*7c478bd9Sstevel@tonic-gate } 6520*7c478bd9Sstevel@tonic-gate 6521*7c478bd9Sstevel@tonic-gate 6522*7c478bd9Sstevel@tonic-gate /* 6523*7c478bd9Sstevel@tonic-gate * registering for events 6524*7c478bd9Sstevel@tonic-gate */ 6525*7c478bd9Sstevel@tonic-gate static int 6526*7c478bd9Sstevel@tonic-gate hubd_register_events(hubd_t *hubd) 6527*7c478bd9Sstevel@tonic-gate { 6528*7c478bd9Sstevel@tonic-gate int rval = USB_SUCCESS; 6529*7c478bd9Sstevel@tonic-gate 6530*7c478bd9Sstevel@tonic-gate if (usba_is_root_hub(hubd->h_dip)) { 6531*7c478bd9Sstevel@tonic-gate hubd_register_cpr_callback(hubd); 6532*7c478bd9Sstevel@tonic-gate } else { 6533*7c478bd9Sstevel@tonic-gate rval = usb_register_event_cbs(hubd->h_dip, &hubd_events, 0); 6534*7c478bd9Sstevel@tonic-gate } 6535*7c478bd9Sstevel@tonic-gate 6536*7c478bd9Sstevel@tonic-gate return (rval); 6537*7c478bd9Sstevel@tonic-gate } 6538*7c478bd9Sstevel@tonic-gate 6539*7c478bd9Sstevel@tonic-gate 6540*7c478bd9Sstevel@tonic-gate /* 6541*7c478bd9Sstevel@tonic-gate * hubd cpr callback related functions 6542*7c478bd9Sstevel@tonic-gate * 6543*7c478bd9Sstevel@tonic-gate * hubd_cpr_post_user_callb: 6544*7c478bd9Sstevel@tonic-gate * This function is called during checkpoint & resume - 6545*7c478bd9Sstevel@tonic-gate * 1. after user threads are stopped during checkpoint 6546*7c478bd9Sstevel@tonic-gate * 2. after kernel threads are resumed during resume 6547*7c478bd9Sstevel@tonic-gate */ 6548*7c478bd9Sstevel@tonic-gate /* ARGSUSED */ 6549*7c478bd9Sstevel@tonic-gate static boolean_t 6550*7c478bd9Sstevel@tonic-gate hubd_cpr_post_user_callb(void *arg, int code) 6551*7c478bd9Sstevel@tonic-gate { 6552*7c478bd9Sstevel@tonic-gate hubd_cpr_t *cpr_cb = (hubd_cpr_t *)arg; 6553*7c478bd9Sstevel@tonic-gate hubd_t *hubd = cpr_cb->statep; 6554*7c478bd9Sstevel@tonic-gate int retry = 0; 6555*7c478bd9Sstevel@tonic-gate 6556*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle, 6557*7c478bd9Sstevel@tonic-gate "hubd_cpr_post_user_callb"); 6558*7c478bd9Sstevel@tonic-gate 6559*7c478bd9Sstevel@tonic-gate switch (code) { 6560*7c478bd9Sstevel@tonic-gate case CB_CODE_CPR_CHKPT: 6561*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_EVENTS, hubd->h_log_handle, 6562*7c478bd9Sstevel@tonic-gate "hubd_cpr_post_user_callb: CB_CODE_CPR_CHKPT"); 6563*7c478bd9Sstevel@tonic-gate 6564*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6565*7c478bd9Sstevel@tonic-gate 6566*7c478bd9Sstevel@tonic-gate /* turn off deathrow thread */ 6567*7c478bd9Sstevel@tonic-gate hubd->h_cleanup_enabled = B_FALSE; 6568*7c478bd9Sstevel@tonic-gate 6569*7c478bd9Sstevel@tonic-gate /* give up if deathrow thread doesn't exit */ 6570*7c478bd9Sstevel@tonic-gate while ((hubd->h_cleanup_active == B_TRUE) && (retry++ < 3)) { 6571*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6572*7c478bd9Sstevel@tonic-gate delay(drv_usectohz(hubd_dip_cleanup_delay)); 6573*7c478bd9Sstevel@tonic-gate 6574*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_EVENTS, hubd->h_log_handle, 6575*7c478bd9Sstevel@tonic-gate "hubd_cpr_post_user_callb, waiting for " 6576*7c478bd9Sstevel@tonic-gate "deathrow thread to exit"); 6577*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6578*7c478bd9Sstevel@tonic-gate } 6579*7c478bd9Sstevel@tonic-gate 6580*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6581*7c478bd9Sstevel@tonic-gate 6582*7c478bd9Sstevel@tonic-gate /* save the state of the device */ 6583*7c478bd9Sstevel@tonic-gate (void) hubd_pre_suspend_event_cb(hubd->h_dip); 6584*7c478bd9Sstevel@tonic-gate 6585*7c478bd9Sstevel@tonic-gate return (B_TRUE); 6586*7c478bd9Sstevel@tonic-gate case CB_CODE_CPR_RESUME: 6587*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_EVENTS, hubd->h_log_handle, 6588*7c478bd9Sstevel@tonic-gate "hubd_cpr_post_user_callb: CB_CODE_CPR_RESUME"); 6589*7c478bd9Sstevel@tonic-gate 6590*7c478bd9Sstevel@tonic-gate /* restore the state of the device */ 6591*7c478bd9Sstevel@tonic-gate (void) hubd_post_resume_event_cb(hubd->h_dip); 6592*7c478bd9Sstevel@tonic-gate 6593*7c478bd9Sstevel@tonic-gate /* turn on deathrow thread */ 6594*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6595*7c478bd9Sstevel@tonic-gate hubd->h_cleanup_enabled = B_TRUE; 6596*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6597*7c478bd9Sstevel@tonic-gate 6598*7c478bd9Sstevel@tonic-gate hubd_schedule_cleanup(hubd->h_usba_device->usb_root_hub_dip); 6599*7c478bd9Sstevel@tonic-gate 6600*7c478bd9Sstevel@tonic-gate return (B_TRUE); 6601*7c478bd9Sstevel@tonic-gate default: 6602*7c478bd9Sstevel@tonic-gate 6603*7c478bd9Sstevel@tonic-gate return (B_FALSE); 6604*7c478bd9Sstevel@tonic-gate } 6605*7c478bd9Sstevel@tonic-gate 6606*7c478bd9Sstevel@tonic-gate } 6607*7c478bd9Sstevel@tonic-gate 6608*7c478bd9Sstevel@tonic-gate 6609*7c478bd9Sstevel@tonic-gate /* register callback with cpr framework */ 6610*7c478bd9Sstevel@tonic-gate void 6611*7c478bd9Sstevel@tonic-gate hubd_register_cpr_callback(hubd_t *hubd) 6612*7c478bd9Sstevel@tonic-gate { 6613*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle, 6614*7c478bd9Sstevel@tonic-gate "hubd_register_cpr_callback"); 6615*7c478bd9Sstevel@tonic-gate 6616*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6617*7c478bd9Sstevel@tonic-gate hubd->h_cpr_cb = 6618*7c478bd9Sstevel@tonic-gate (hubd_cpr_t *)kmem_zalloc(sizeof (hubd_cpr_t), KM_SLEEP); 6619*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6620*7c478bd9Sstevel@tonic-gate mutex_init(&hubd->h_cpr_cb->lockp, NULL, MUTEX_DRIVER, 6621*7c478bd9Sstevel@tonic-gate hubd->h_dev_data->dev_iblock_cookie); 6622*7c478bd9Sstevel@tonic-gate hubd->h_cpr_cb->statep = hubd; 6623*7c478bd9Sstevel@tonic-gate hubd->h_cpr_cb->cpr.cc_lockp = &hubd->h_cpr_cb->lockp; 6624*7c478bd9Sstevel@tonic-gate hubd->h_cpr_cb->cpr.cc_id = callb_add(hubd_cpr_post_user_callb, 6625*7c478bd9Sstevel@tonic-gate (void *)hubd->h_cpr_cb, CB_CL_CPR_POST_USER, "hubd"); 6626*7c478bd9Sstevel@tonic-gate } 6627*7c478bd9Sstevel@tonic-gate 6628*7c478bd9Sstevel@tonic-gate 6629*7c478bd9Sstevel@tonic-gate /* unregister callback with cpr framework */ 6630*7c478bd9Sstevel@tonic-gate void 6631*7c478bd9Sstevel@tonic-gate hubd_unregister_cpr_callback(hubd_t *hubd) 6632*7c478bd9Sstevel@tonic-gate { 6633*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle, 6634*7c478bd9Sstevel@tonic-gate "hubd_unregister_cpr_callback"); 6635*7c478bd9Sstevel@tonic-gate 6636*7c478bd9Sstevel@tonic-gate if (hubd->h_cpr_cb) { 6637*7c478bd9Sstevel@tonic-gate (void) callb_delete(hubd->h_cpr_cb->cpr.cc_id); 6638*7c478bd9Sstevel@tonic-gate mutex_destroy(&hubd->h_cpr_cb->lockp); 6639*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6640*7c478bd9Sstevel@tonic-gate kmem_free(hubd->h_cpr_cb, sizeof (hubd_cpr_t)); 6641*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6642*7c478bd9Sstevel@tonic-gate } 6643*7c478bd9Sstevel@tonic-gate } 6644*7c478bd9Sstevel@tonic-gate 6645*7c478bd9Sstevel@tonic-gate 6646*7c478bd9Sstevel@tonic-gate /* 6647*7c478bd9Sstevel@tonic-gate * Power management 6648*7c478bd9Sstevel@tonic-gate * 6649*7c478bd9Sstevel@tonic-gate * create the pm components required for power management 6650*7c478bd9Sstevel@tonic-gate */ 6651*7c478bd9Sstevel@tonic-gate static void 6652*7c478bd9Sstevel@tonic-gate hubd_create_pm_components(dev_info_t *dip, hubd_t *hubd) 6653*7c478bd9Sstevel@tonic-gate { 6654*7c478bd9Sstevel@tonic-gate hub_power_t *hubpm; 6655*7c478bd9Sstevel@tonic-gate 6656*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 6657*7c478bd9Sstevel@tonic-gate "hubd_create_pm_components: Begin"); 6658*7c478bd9Sstevel@tonic-gate 6659*7c478bd9Sstevel@tonic-gate /* Allocate the state structure */ 6660*7c478bd9Sstevel@tonic-gate hubpm = kmem_zalloc(sizeof (hub_power_t), KM_SLEEP); 6661*7c478bd9Sstevel@tonic-gate 6662*7c478bd9Sstevel@tonic-gate hubd->h_hubpm = hubpm; 6663*7c478bd9Sstevel@tonic-gate hubpm->hubp_hubd = hubd; 6664*7c478bd9Sstevel@tonic-gate hubpm->hubp_pm_capabilities = 0; 6665*7c478bd9Sstevel@tonic-gate hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR; 6666*7c478bd9Sstevel@tonic-gate hubpm->hubp_time_at_full_power = ddi_get_time(); 6667*7c478bd9Sstevel@tonic-gate hubpm->hubp_min_pm_threshold = hubdi_min_pm_threshold; 6668*7c478bd9Sstevel@tonic-gate 6669*7c478bd9Sstevel@tonic-gate /* alloc memory to save power states of children */ 6670*7c478bd9Sstevel@tonic-gate hubpm->hubp_child_pwrstate = (uint8_t *) 6671*7c478bd9Sstevel@tonic-gate kmem_zalloc(MAX_PORTS + 1, KM_SLEEP); 6672*7c478bd9Sstevel@tonic-gate 6673*7c478bd9Sstevel@tonic-gate /* 6674*7c478bd9Sstevel@tonic-gate * if the enable remote wakeup fails 6675*7c478bd9Sstevel@tonic-gate * we still want to enable 6676*7c478bd9Sstevel@tonic-gate * parent notification so we can PM the children 6677*7c478bd9Sstevel@tonic-gate */ 6678*7c478bd9Sstevel@tonic-gate usb_enable_parent_notification(dip); 6679*7c478bd9Sstevel@tonic-gate 6680*7c478bd9Sstevel@tonic-gate if (usb_handle_remote_wakeup(dip, 6681*7c478bd9Sstevel@tonic-gate USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) { 6682*7c478bd9Sstevel@tonic-gate uint_t pwr_states; 6683*7c478bd9Sstevel@tonic-gate 6684*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 6685*7c478bd9Sstevel@tonic-gate "hubd_create_pm_components: " 6686*7c478bd9Sstevel@tonic-gate "Remote Wakeup Enabled"); 6687*7c478bd9Sstevel@tonic-gate 6688*7c478bd9Sstevel@tonic-gate if (usb_create_pm_components(dip, &pwr_states) == 6689*7c478bd9Sstevel@tonic-gate USB_SUCCESS) { 6690*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6691*7c478bd9Sstevel@tonic-gate hubpm->hubp_wakeup_enabled = 1; 6692*7c478bd9Sstevel@tonic-gate hubpm->hubp_pwr_states = (uint8_t)pwr_states; 6693*7c478bd9Sstevel@tonic-gate 6694*7c478bd9Sstevel@tonic-gate /* we are busy now till end of the attach */ 6695*7c478bd9Sstevel@tonic-gate hubd_pm_busy_component(hubd, dip, 0); 6696*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6697*7c478bd9Sstevel@tonic-gate 6698*7c478bd9Sstevel@tonic-gate /* bring the device to full power */ 6699*7c478bd9Sstevel@tonic-gate (void) pm_raise_power(dip, 0, 6700*7c478bd9Sstevel@tonic-gate USB_DEV_OS_FULL_PWR); 6701*7c478bd9Sstevel@tonic-gate } 6702*7c478bd9Sstevel@tonic-gate } 6703*7c478bd9Sstevel@tonic-gate 6704*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 6705*7c478bd9Sstevel@tonic-gate "hubd_create_pm_components: END"); 6706*7c478bd9Sstevel@tonic-gate } 6707*7c478bd9Sstevel@tonic-gate 6708*7c478bd9Sstevel@tonic-gate 6709*7c478bd9Sstevel@tonic-gate /* 6710*7c478bd9Sstevel@tonic-gate * Attachment point management 6711*7c478bd9Sstevel@tonic-gate */ 6712*7c478bd9Sstevel@tonic-gate /* ARGSUSED */ 6713*7c478bd9Sstevel@tonic-gate int 6714*7c478bd9Sstevel@tonic-gate usba_hubdi_open(dev_info_t *dip, dev_t *devp, int flags, int otyp, 6715*7c478bd9Sstevel@tonic-gate cred_t *credp) 6716*7c478bd9Sstevel@tonic-gate { 6717*7c478bd9Sstevel@tonic-gate hubd_t *hubd; 6718*7c478bd9Sstevel@tonic-gate 6719*7c478bd9Sstevel@tonic-gate if (otyp != OTYP_CHR) 6720*7c478bd9Sstevel@tonic-gate return (EINVAL); 6721*7c478bd9Sstevel@tonic-gate 6722*7c478bd9Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 6723*7c478bd9Sstevel@tonic-gate if (hubd == NULL) { 6724*7c478bd9Sstevel@tonic-gate return (ENXIO); 6725*7c478bd9Sstevel@tonic-gate } 6726*7c478bd9Sstevel@tonic-gate 6727*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 6728*7c478bd9Sstevel@tonic-gate "hubd_open:"); 6729*7c478bd9Sstevel@tonic-gate 6730*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6731*7c478bd9Sstevel@tonic-gate if ((flags & FEXCL) && (hubd->h_softstate & HUBD_SS_ISOPEN)) { 6732*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6733*7c478bd9Sstevel@tonic-gate 6734*7c478bd9Sstevel@tonic-gate return (EBUSY); 6735*7c478bd9Sstevel@tonic-gate } 6736*7c478bd9Sstevel@tonic-gate 6737*7c478bd9Sstevel@tonic-gate hubd->h_softstate |= HUBD_SS_ISOPEN; 6738*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6739*7c478bd9Sstevel@tonic-gate 6740*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, "opened"); 6741*7c478bd9Sstevel@tonic-gate 6742*7c478bd9Sstevel@tonic-gate return (0); 6743*7c478bd9Sstevel@tonic-gate } 6744*7c478bd9Sstevel@tonic-gate 6745*7c478bd9Sstevel@tonic-gate 6746*7c478bd9Sstevel@tonic-gate /* ARGSUSED */ 6747*7c478bd9Sstevel@tonic-gate int 6748*7c478bd9Sstevel@tonic-gate usba_hubdi_close(dev_info_t *dip, dev_t dev, int flag, int otyp, 6749*7c478bd9Sstevel@tonic-gate cred_t *credp) 6750*7c478bd9Sstevel@tonic-gate { 6751*7c478bd9Sstevel@tonic-gate hubd_t *hubd; 6752*7c478bd9Sstevel@tonic-gate 6753*7c478bd9Sstevel@tonic-gate if (otyp != OTYP_CHR) { 6754*7c478bd9Sstevel@tonic-gate return (EINVAL); 6755*7c478bd9Sstevel@tonic-gate } 6756*7c478bd9Sstevel@tonic-gate 6757*7c478bd9Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 6758*7c478bd9Sstevel@tonic-gate 6759*7c478bd9Sstevel@tonic-gate if (hubd == NULL) { 6760*7c478bd9Sstevel@tonic-gate return (ENXIO); 6761*7c478bd9Sstevel@tonic-gate } 6762*7c478bd9Sstevel@tonic-gate 6763*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, "hubd_close:"); 6764*7c478bd9Sstevel@tonic-gate 6765*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6766*7c478bd9Sstevel@tonic-gate hubd->h_softstate &= ~HUBD_SS_ISOPEN; 6767*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6768*7c478bd9Sstevel@tonic-gate 6769*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, "closed"); 6770*7c478bd9Sstevel@tonic-gate 6771*7c478bd9Sstevel@tonic-gate return (0); 6772*7c478bd9Sstevel@tonic-gate } 6773*7c478bd9Sstevel@tonic-gate 6774*7c478bd9Sstevel@tonic-gate 6775*7c478bd9Sstevel@tonic-gate /* 6776*7c478bd9Sstevel@tonic-gate * hubd_ioctl: cfgadm controls 6777*7c478bd9Sstevel@tonic-gate */ 6778*7c478bd9Sstevel@tonic-gate /* ARGSUSED */ 6779*7c478bd9Sstevel@tonic-gate int 6780*7c478bd9Sstevel@tonic-gate usba_hubdi_ioctl(dev_info_t *self, dev_t dev, int cmd, intptr_t arg, 6781*7c478bd9Sstevel@tonic-gate int mode, cred_t *credp, int *rvalp) 6782*7c478bd9Sstevel@tonic-gate { 6783*7c478bd9Sstevel@tonic-gate int rv = 0; 6784*7c478bd9Sstevel@tonic-gate char *msg; /* for messages */ 6785*7c478bd9Sstevel@tonic-gate hubd_t *hubd; 6786*7c478bd9Sstevel@tonic-gate usb_port_t port = 0; 6787*7c478bd9Sstevel@tonic-gate dev_info_t *child_dip = NULL; 6788*7c478bd9Sstevel@tonic-gate dev_info_t *rh_dip; 6789*7c478bd9Sstevel@tonic-gate devctl_ap_state_t ap_state; 6790*7c478bd9Sstevel@tonic-gate struct devctl_iocdata *dcp = NULL; 6791*7c478bd9Sstevel@tonic-gate usb_pipe_state_t prev_pipe_state = 0; 6792*7c478bd9Sstevel@tonic-gate int circ, rh_circ, prh_circ; 6793*7c478bd9Sstevel@tonic-gate 6794*7c478bd9Sstevel@tonic-gate if ((hubd = hubd_get_soft_state(self)) == NULL) { 6795*7c478bd9Sstevel@tonic-gate 6796*7c478bd9Sstevel@tonic-gate return (ENXIO); 6797*7c478bd9Sstevel@tonic-gate } 6798*7c478bd9Sstevel@tonic-gate 6799*7c478bd9Sstevel@tonic-gate rh_dip = hubd->h_usba_device->usb_root_hub_dip; 6800*7c478bd9Sstevel@tonic-gate 6801*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 6802*7c478bd9Sstevel@tonic-gate "usba_hubdi_ioctl: " 6803*7c478bd9Sstevel@tonic-gate "cmd=%x, arg=%lx, mode=%x, cred=%p, rval=%p dev=0x%lx", 6804*7c478bd9Sstevel@tonic-gate cmd, arg, mode, credp, rvalp, dev); 6805*7c478bd9Sstevel@tonic-gate 6806*7c478bd9Sstevel@tonic-gate /* read devctl ioctl data */ 6807*7c478bd9Sstevel@tonic-gate if ((cmd != DEVCTL_AP_CONTROL) && 6808*7c478bd9Sstevel@tonic-gate (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)) { 6809*7c478bd9Sstevel@tonic-gate 6810*7c478bd9Sstevel@tonic-gate return (EFAULT); 6811*7c478bd9Sstevel@tonic-gate } 6812*7c478bd9Sstevel@tonic-gate 6813*7c478bd9Sstevel@tonic-gate /* 6814*7c478bd9Sstevel@tonic-gate * make sure the hub is connected before trying any 6815*7c478bd9Sstevel@tonic-gate * of the following operations: 6816*7c478bd9Sstevel@tonic-gate * configure, connect, disconnect 6817*7c478bd9Sstevel@tonic-gate */ 6818*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6819*7c478bd9Sstevel@tonic-gate 6820*7c478bd9Sstevel@tonic-gate switch (cmd) { 6821*7c478bd9Sstevel@tonic-gate case DEVCTL_AP_DISCONNECT: 6822*7c478bd9Sstevel@tonic-gate case DEVCTL_AP_UNCONFIGURE: 6823*7c478bd9Sstevel@tonic-gate case DEVCTL_AP_CONFIGURE: 6824*7c478bd9Sstevel@tonic-gate if (hubd->h_dev_state == USB_DEV_DISCONNECTED) { 6825*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 6826*7c478bd9Sstevel@tonic-gate "hubd: already gone"); 6827*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6828*7c478bd9Sstevel@tonic-gate if (dcp) { 6829*7c478bd9Sstevel@tonic-gate ndi_dc_freehdl(dcp); 6830*7c478bd9Sstevel@tonic-gate } 6831*7c478bd9Sstevel@tonic-gate 6832*7c478bd9Sstevel@tonic-gate return (EIO); 6833*7c478bd9Sstevel@tonic-gate } 6834*7c478bd9Sstevel@tonic-gate 6835*7c478bd9Sstevel@tonic-gate /* FALLTHROUGH */ 6836*7c478bd9Sstevel@tonic-gate case DEVCTL_AP_GETSTATE: 6837*7c478bd9Sstevel@tonic-gate if ((port = hubd_get_port_num(hubd, dcp)) == 0) { 6838*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 6839*7c478bd9Sstevel@tonic-gate "hubd: bad port"); 6840*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6841*7c478bd9Sstevel@tonic-gate if (dcp) { 6842*7c478bd9Sstevel@tonic-gate ndi_dc_freehdl(dcp); 6843*7c478bd9Sstevel@tonic-gate } 6844*7c478bd9Sstevel@tonic-gate 6845*7c478bd9Sstevel@tonic-gate return (EINVAL); 6846*7c478bd9Sstevel@tonic-gate } 6847*7c478bd9Sstevel@tonic-gate break; 6848*7c478bd9Sstevel@tonic-gate 6849*7c478bd9Sstevel@tonic-gate case DEVCTL_AP_CONTROL: 6850*7c478bd9Sstevel@tonic-gate 6851*7c478bd9Sstevel@tonic-gate break; 6852*7c478bd9Sstevel@tonic-gate default: 6853*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6854*7c478bd9Sstevel@tonic-gate if (dcp) { 6855*7c478bd9Sstevel@tonic-gate ndi_dc_freehdl(dcp); 6856*7c478bd9Sstevel@tonic-gate } 6857*7c478bd9Sstevel@tonic-gate 6858*7c478bd9Sstevel@tonic-gate return (ENOTTY); 6859*7c478bd9Sstevel@tonic-gate } 6860*7c478bd9Sstevel@tonic-gate 6861*7c478bd9Sstevel@tonic-gate /* should not happen, just in case */ 6862*7c478bd9Sstevel@tonic-gate if (hubd->h_dev_state == USB_DEV_SUSPENDED) { 6863*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6864*7c478bd9Sstevel@tonic-gate if (dcp) { 6865*7c478bd9Sstevel@tonic-gate ndi_dc_freehdl(dcp); 6866*7c478bd9Sstevel@tonic-gate } 6867*7c478bd9Sstevel@tonic-gate 6868*7c478bd9Sstevel@tonic-gate return (EIO); 6869*7c478bd9Sstevel@tonic-gate } 6870*7c478bd9Sstevel@tonic-gate 6871*7c478bd9Sstevel@tonic-gate hubd_pm_busy_component(hubd, hubd->h_dip, 0); 6872*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6873*7c478bd9Sstevel@tonic-gate 6874*7c478bd9Sstevel@tonic-gate /* go full power */ 6875*7c478bd9Sstevel@tonic-gate (void) pm_raise_power(hubd->h_dip, 0, USB_DEV_OS_FULL_PWR); 6876*7c478bd9Sstevel@tonic-gate 6877*7c478bd9Sstevel@tonic-gate ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ); 6878*7c478bd9Sstevel@tonic-gate ndi_devi_enter(rh_dip, &rh_circ); 6879*7c478bd9Sstevel@tonic-gate ndi_devi_enter(hubd->h_dip, &circ); 6880*7c478bd9Sstevel@tonic-gate 6881*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6882*7c478bd9Sstevel@tonic-gate 6883*7c478bd9Sstevel@tonic-gate /* stop polling iff it was active */ 6884*7c478bd9Sstevel@tonic-gate if (hubd->h_ep1_ph) { 6885*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6886*7c478bd9Sstevel@tonic-gate (void) usb_pipe_get_state(hubd->h_ep1_ph, &prev_pipe_state, 6887*7c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP); 6888*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6889*7c478bd9Sstevel@tonic-gate 6890*7c478bd9Sstevel@tonic-gate if (prev_pipe_state == USB_PIPE_STATE_ACTIVE) { 6891*7c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd); 6892*7c478bd9Sstevel@tonic-gate } 6893*7c478bd9Sstevel@tonic-gate } 6894*7c478bd9Sstevel@tonic-gate 6895*7c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread++; 6896*7c478bd9Sstevel@tonic-gate 6897*7c478bd9Sstevel@tonic-gate switch (cmd) { 6898*7c478bd9Sstevel@tonic-gate case DEVCTL_AP_DISCONNECT: 6899*7c478bd9Sstevel@tonic-gate if (hubd_delete_child(hubd, port, 6900*7c478bd9Sstevel@tonic-gate NDI_DEVI_REMOVE, B_FALSE) != USB_SUCCESS) { 6901*7c478bd9Sstevel@tonic-gate rv = EIO; 6902*7c478bd9Sstevel@tonic-gate } 6903*7c478bd9Sstevel@tonic-gate 6904*7c478bd9Sstevel@tonic-gate break; 6905*7c478bd9Sstevel@tonic-gate case DEVCTL_AP_UNCONFIGURE: 6906*7c478bd9Sstevel@tonic-gate if (hubd_delete_child(hubd, port, 6907*7c478bd9Sstevel@tonic-gate NDI_UNCONFIG, B_FALSE) != USB_SUCCESS) { 6908*7c478bd9Sstevel@tonic-gate rv = EIO; 6909*7c478bd9Sstevel@tonic-gate } 6910*7c478bd9Sstevel@tonic-gate 6911*7c478bd9Sstevel@tonic-gate break; 6912*7c478bd9Sstevel@tonic-gate case DEVCTL_AP_CONFIGURE: 6913*7c478bd9Sstevel@tonic-gate /* toggle port */ 6914*7c478bd9Sstevel@tonic-gate if (hubd_toggle_port(hubd, port) != USB_SUCCESS) { 6915*7c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0); 6916*7c478bd9Sstevel@tonic-gate rv = EIO; 6917*7c478bd9Sstevel@tonic-gate 6918*7c478bd9Sstevel@tonic-gate break; 6919*7c478bd9Sstevel@tonic-gate } 6920*7c478bd9Sstevel@tonic-gate 6921*7c478bd9Sstevel@tonic-gate (void) hubd_handle_port_connect(hubd, port); 6922*7c478bd9Sstevel@tonic-gate child_dip = hubd_get_child_dip(hubd, port); 6923*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6924*7c478bd9Sstevel@tonic-gate 6925*7c478bd9Sstevel@tonic-gate ndi_devi_exit(hubd->h_dip, circ); 6926*7c478bd9Sstevel@tonic-gate ndi_devi_exit(rh_dip, rh_circ); 6927*7c478bd9Sstevel@tonic-gate ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ); 6928*7c478bd9Sstevel@tonic-gate if ((child_dip == NULL) || 6929*7c478bd9Sstevel@tonic-gate (ndi_devi_online(child_dip, 0) != NDI_SUCCESS)) { 6930*7c478bd9Sstevel@tonic-gate rv = EIO; 6931*7c478bd9Sstevel@tonic-gate } 6932*7c478bd9Sstevel@tonic-gate ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ); 6933*7c478bd9Sstevel@tonic-gate ndi_devi_enter(rh_dip, &rh_circ); 6934*7c478bd9Sstevel@tonic-gate ndi_devi_enter(hubd->h_dip, &circ); 6935*7c478bd9Sstevel@tonic-gate 6936*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6937*7c478bd9Sstevel@tonic-gate 6938*7c478bd9Sstevel@tonic-gate break; 6939*7c478bd9Sstevel@tonic-gate case DEVCTL_AP_GETSTATE: 6940*7c478bd9Sstevel@tonic-gate switch (hubd_cfgadm_state(hubd, port)) { 6941*7c478bd9Sstevel@tonic-gate case HUBD_CFGADM_DISCONNECTED: 6942*7c478bd9Sstevel@tonic-gate /* port previously 'disconnected' by cfgadm */ 6943*7c478bd9Sstevel@tonic-gate ap_state.ap_rstate = AP_RSTATE_DISCONNECTED; 6944*7c478bd9Sstevel@tonic-gate ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED; 6945*7c478bd9Sstevel@tonic-gate ap_state.ap_condition = AP_COND_OK; 6946*7c478bd9Sstevel@tonic-gate 6947*7c478bd9Sstevel@tonic-gate break; 6948*7c478bd9Sstevel@tonic-gate case HUBD_CFGADM_UNCONFIGURED: 6949*7c478bd9Sstevel@tonic-gate ap_state.ap_rstate = AP_RSTATE_CONNECTED; 6950*7c478bd9Sstevel@tonic-gate ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED; 6951*7c478bd9Sstevel@tonic-gate ap_state.ap_condition = AP_COND_OK; 6952*7c478bd9Sstevel@tonic-gate 6953*7c478bd9Sstevel@tonic-gate break; 6954*7c478bd9Sstevel@tonic-gate case HUBD_CFGADM_CONFIGURED: 6955*7c478bd9Sstevel@tonic-gate ap_state.ap_rstate = AP_RSTATE_CONNECTED; 6956*7c478bd9Sstevel@tonic-gate ap_state.ap_ostate = AP_OSTATE_CONFIGURED; 6957*7c478bd9Sstevel@tonic-gate ap_state.ap_condition = AP_COND_OK; 6958*7c478bd9Sstevel@tonic-gate 6959*7c478bd9Sstevel@tonic-gate break; 6960*7c478bd9Sstevel@tonic-gate case HUBD_CFGADM_STILL_REFERENCED: 6961*7c478bd9Sstevel@tonic-gate ap_state.ap_rstate = AP_RSTATE_EMPTY; 6962*7c478bd9Sstevel@tonic-gate ap_state.ap_ostate = AP_OSTATE_CONFIGURED; 6963*7c478bd9Sstevel@tonic-gate ap_state.ap_condition = AP_COND_UNUSABLE; 6964*7c478bd9Sstevel@tonic-gate 6965*7c478bd9Sstevel@tonic-gate break; 6966*7c478bd9Sstevel@tonic-gate case HUBD_CFGADM_EMPTY: 6967*7c478bd9Sstevel@tonic-gate default: 6968*7c478bd9Sstevel@tonic-gate ap_state.ap_rstate = AP_RSTATE_EMPTY; 6969*7c478bd9Sstevel@tonic-gate ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED; 6970*7c478bd9Sstevel@tonic-gate ap_state.ap_condition = AP_COND_OK; 6971*7c478bd9Sstevel@tonic-gate 6972*7c478bd9Sstevel@tonic-gate break; 6973*7c478bd9Sstevel@tonic-gate } 6974*7c478bd9Sstevel@tonic-gate 6975*7c478bd9Sstevel@tonic-gate ap_state.ap_last_change = (time_t)-1; 6976*7c478bd9Sstevel@tonic-gate ap_state.ap_error_code = 0; 6977*7c478bd9Sstevel@tonic-gate ap_state.ap_in_transition = 0; 6978*7c478bd9Sstevel@tonic-gate 6979*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 6980*7c478bd9Sstevel@tonic-gate "DEVCTL_AP_GETSTATE: " 6981*7c478bd9Sstevel@tonic-gate "ostate=0x%x, rstate=0x%x, condition=0x%x", 6982*7c478bd9Sstevel@tonic-gate ap_state.ap_ostate, 6983*7c478bd9Sstevel@tonic-gate ap_state.ap_rstate, ap_state.ap_condition); 6984*7c478bd9Sstevel@tonic-gate 6985*7c478bd9Sstevel@tonic-gate /* copy the return-AP-state information to the user space */ 6986*7c478bd9Sstevel@tonic-gate if (ndi_dc_return_ap_state(&ap_state, dcp) != NDI_SUCCESS) { 6987*7c478bd9Sstevel@tonic-gate rv = EFAULT; 6988*7c478bd9Sstevel@tonic-gate } 6989*7c478bd9Sstevel@tonic-gate 6990*7c478bd9Sstevel@tonic-gate break; 6991*7c478bd9Sstevel@tonic-gate case DEVCTL_AP_CONTROL: 6992*7c478bd9Sstevel@tonic-gate { 6993*7c478bd9Sstevel@tonic-gate /* 6994*7c478bd9Sstevel@tonic-gate * Generic devctl for hardware-specific functionality. 6995*7c478bd9Sstevel@tonic-gate * For list of sub-commands see hubd_impl.h 6996*7c478bd9Sstevel@tonic-gate */ 6997*7c478bd9Sstevel@tonic-gate hubd_ioctl_data_t ioc; /* for 64 byte copies */ 6998*7c478bd9Sstevel@tonic-gate 6999*7c478bd9Sstevel@tonic-gate /* copy user ioctl data in first */ 7000*7c478bd9Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL 7001*7c478bd9Sstevel@tonic-gate if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) { 7002*7c478bd9Sstevel@tonic-gate hubd_ioctl_data_32_t ioc32; 7003*7c478bd9Sstevel@tonic-gate 7004*7c478bd9Sstevel@tonic-gate if (ddi_copyin((void *)arg, (void *)&ioc32, 7005*7c478bd9Sstevel@tonic-gate sizeof (ioc32), mode) != 0) { 7006*7c478bd9Sstevel@tonic-gate rv = EFAULT; 7007*7c478bd9Sstevel@tonic-gate 7008*7c478bd9Sstevel@tonic-gate break; 7009*7c478bd9Sstevel@tonic-gate } 7010*7c478bd9Sstevel@tonic-gate ioc.cmd = (uint_t)ioc32.cmd; 7011*7c478bd9Sstevel@tonic-gate ioc.port = (uint_t)ioc32.port; 7012*7c478bd9Sstevel@tonic-gate ioc.get_size = (uint_t)ioc32.get_size; 7013*7c478bd9Sstevel@tonic-gate ioc.buf = (caddr_t)(uintptr_t)ioc32.buf; 7014*7c478bd9Sstevel@tonic-gate ioc.bufsiz = (uint_t)ioc32.bufsiz; 7015*7c478bd9Sstevel@tonic-gate ioc.misc_arg = (uint_t)ioc32.misc_arg; 7016*7c478bd9Sstevel@tonic-gate } else 7017*7c478bd9Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */ 7018*7c478bd9Sstevel@tonic-gate if (ddi_copyin((void *)arg, (void *)&ioc, sizeof (ioc), 7019*7c478bd9Sstevel@tonic-gate mode) != 0) { 7020*7c478bd9Sstevel@tonic-gate rv = EFAULT; 7021*7c478bd9Sstevel@tonic-gate 7022*7c478bd9Sstevel@tonic-gate break; 7023*7c478bd9Sstevel@tonic-gate } 7024*7c478bd9Sstevel@tonic-gate 7025*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_CBOPS, hubd->h_log_handle, 7026*7c478bd9Sstevel@tonic-gate "DEVCTL_AP_CONTROL: ioc: cmd=0x%x port=%d get_size=%d" 7027*7c478bd9Sstevel@tonic-gate "\n\tbuf=0x%p, bufsiz=%d, misc_arg=%d", ioc.cmd, 7028*7c478bd9Sstevel@tonic-gate ioc.port, ioc.get_size, ioc.buf, ioc.bufsiz, ioc.misc_arg); 7029*7c478bd9Sstevel@tonic-gate 7030*7c478bd9Sstevel@tonic-gate /* 7031*7c478bd9Sstevel@tonic-gate * To avoid BE/LE and 32/64 issues, a get_size always 7032*7c478bd9Sstevel@tonic-gate * returns a 32-bit number. 7033*7c478bd9Sstevel@tonic-gate */ 7034*7c478bd9Sstevel@tonic-gate if (ioc.get_size != 0 && ioc.bufsiz != (sizeof (uint32_t))) { 7035*7c478bd9Sstevel@tonic-gate rv = EINVAL; 7036*7c478bd9Sstevel@tonic-gate 7037*7c478bd9Sstevel@tonic-gate break; 7038*7c478bd9Sstevel@tonic-gate } 7039*7c478bd9Sstevel@tonic-gate 7040*7c478bd9Sstevel@tonic-gate switch (ioc.cmd) { 7041*7c478bd9Sstevel@tonic-gate case USB_DESCR_TYPE_DEV: 7042*7c478bd9Sstevel@tonic-gate msg = "DEVCTL_AP_CONTROL: GET_DEVICE_DESC"; 7043*7c478bd9Sstevel@tonic-gate if (ioc.get_size) { 7044*7c478bd9Sstevel@tonic-gate /* uint32 so this works 32/64 */ 7045*7c478bd9Sstevel@tonic-gate uint32_t size = sizeof (usb_dev_descr_t); 7046*7c478bd9Sstevel@tonic-gate 7047*7c478bd9Sstevel@tonic-gate if (ddi_copyout((void *)&size, ioc.buf, 7048*7c478bd9Sstevel@tonic-gate ioc.bufsiz, mode) != 0) { 7049*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 7050*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 7051*7c478bd9Sstevel@tonic-gate "%s: get_size copyout failed", msg); 7052*7c478bd9Sstevel@tonic-gate rv = EIO; 7053*7c478bd9Sstevel@tonic-gate 7054*7c478bd9Sstevel@tonic-gate break; 7055*7c478bd9Sstevel@tonic-gate } 7056*7c478bd9Sstevel@tonic-gate } else { /* send out the actual descr */ 7057*7c478bd9Sstevel@tonic-gate usb_dev_descr_t *dev_descrp; 7058*7c478bd9Sstevel@tonic-gate 7059*7c478bd9Sstevel@tonic-gate /* check child_dip */ 7060*7c478bd9Sstevel@tonic-gate if ((child_dip = hubd_get_child_dip(hubd, 7061*7c478bd9Sstevel@tonic-gate ioc.port)) == NULL) { 7062*7c478bd9Sstevel@tonic-gate rv = EINVAL; 7063*7c478bd9Sstevel@tonic-gate 7064*7c478bd9Sstevel@tonic-gate break; 7065*7c478bd9Sstevel@tonic-gate } 7066*7c478bd9Sstevel@tonic-gate 7067*7c478bd9Sstevel@tonic-gate dev_descrp = usb_get_dev_descr(child_dip); 7068*7c478bd9Sstevel@tonic-gate if (ioc.bufsiz != sizeof (*dev_descrp)) { 7069*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 7070*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 7071*7c478bd9Sstevel@tonic-gate "%s: bufsize passed (%d) != sizeof " 7072*7c478bd9Sstevel@tonic-gate "usba_device_descr_t (%d)", msg, 7073*7c478bd9Sstevel@tonic-gate ioc.bufsiz, dev_descrp->bLength); 7074*7c478bd9Sstevel@tonic-gate rv = EINVAL; 7075*7c478bd9Sstevel@tonic-gate 7076*7c478bd9Sstevel@tonic-gate break; 7077*7c478bd9Sstevel@tonic-gate } 7078*7c478bd9Sstevel@tonic-gate 7079*7c478bd9Sstevel@tonic-gate if (ddi_copyout((void *)dev_descrp, 7080*7c478bd9Sstevel@tonic-gate ioc.buf, ioc.bufsiz, mode) != 0) { 7081*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 7082*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 7083*7c478bd9Sstevel@tonic-gate "%s: copyout failed.", msg); 7084*7c478bd9Sstevel@tonic-gate rv = EIO; 7085*7c478bd9Sstevel@tonic-gate 7086*7c478bd9Sstevel@tonic-gate break; 7087*7c478bd9Sstevel@tonic-gate } 7088*7c478bd9Sstevel@tonic-gate } 7089*7c478bd9Sstevel@tonic-gate break; 7090*7c478bd9Sstevel@tonic-gate case USB_DESCR_TYPE_STRING: 7091*7c478bd9Sstevel@tonic-gate { 7092*7c478bd9Sstevel@tonic-gate char *str; 7093*7c478bd9Sstevel@tonic-gate uint32_t size; 7094*7c478bd9Sstevel@tonic-gate usba_device_t *usba_device; 7095*7c478bd9Sstevel@tonic-gate 7096*7c478bd9Sstevel@tonic-gate msg = "DEVCTL_AP_CONTROL: GET_STRING_DESCR"; 7097*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 7098*7c478bd9Sstevel@tonic-gate "%s: string request: %d", msg, ioc.misc_arg); 7099*7c478bd9Sstevel@tonic-gate 7100*7c478bd9Sstevel@tonic-gate /* recheck */ 7101*7c478bd9Sstevel@tonic-gate if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) == 7102*7c478bd9Sstevel@tonic-gate NULL) { 7103*7c478bd9Sstevel@tonic-gate rv = EINVAL; 7104*7c478bd9Sstevel@tonic-gate 7105*7c478bd9Sstevel@tonic-gate break; 7106*7c478bd9Sstevel@tonic-gate } 7107*7c478bd9Sstevel@tonic-gate usba_device = usba_get_usba_device(child_dip); 7108*7c478bd9Sstevel@tonic-gate 7109*7c478bd9Sstevel@tonic-gate switch (ioc.misc_arg) { 7110*7c478bd9Sstevel@tonic-gate case HUBD_MFG_STR: 7111*7c478bd9Sstevel@tonic-gate str = usba_device->usb_mfg_str; 7112*7c478bd9Sstevel@tonic-gate 7113*7c478bd9Sstevel@tonic-gate break; 7114*7c478bd9Sstevel@tonic-gate case HUBD_PRODUCT_STR: 7115*7c478bd9Sstevel@tonic-gate str = usba_device->usb_product_str; 7116*7c478bd9Sstevel@tonic-gate 7117*7c478bd9Sstevel@tonic-gate break; 7118*7c478bd9Sstevel@tonic-gate case HUBD_SERIALNO_STR: 7119*7c478bd9Sstevel@tonic-gate str = usba_device->usb_serialno_str; 7120*7c478bd9Sstevel@tonic-gate 7121*7c478bd9Sstevel@tonic-gate break; 7122*7c478bd9Sstevel@tonic-gate case HUBD_CFG_DESCR_STR: 7123*7c478bd9Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex); 7124*7c478bd9Sstevel@tonic-gate str = usba_device->usb_cfg_str_descr[ 7125*7c478bd9Sstevel@tonic-gate usba_device->usb_active_cfg_ndx]; 7126*7c478bd9Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex); 7127*7c478bd9Sstevel@tonic-gate 7128*7c478bd9Sstevel@tonic-gate break; 7129*7c478bd9Sstevel@tonic-gate default: 7130*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 7131*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 7132*7c478bd9Sstevel@tonic-gate "%s: Invalid string request", msg); 7133*7c478bd9Sstevel@tonic-gate rv = EINVAL; 7134*7c478bd9Sstevel@tonic-gate 7135*7c478bd9Sstevel@tonic-gate break; 7136*7c478bd9Sstevel@tonic-gate } /* end of switch */ 7137*7c478bd9Sstevel@tonic-gate 7138*7c478bd9Sstevel@tonic-gate if (rv != 0) { 7139*7c478bd9Sstevel@tonic-gate 7140*7c478bd9Sstevel@tonic-gate break; 7141*7c478bd9Sstevel@tonic-gate } 7142*7c478bd9Sstevel@tonic-gate 7143*7c478bd9Sstevel@tonic-gate size = (str != NULL) ? strlen(str) + 1 : 0; 7144*7c478bd9Sstevel@tonic-gate if (ioc.get_size) { 7145*7c478bd9Sstevel@tonic-gate if (ddi_copyout((void *)&size, ioc.buf, 7146*7c478bd9Sstevel@tonic-gate ioc.bufsiz, mode) != 0) { 7147*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 7148*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 7149*7c478bd9Sstevel@tonic-gate "%s: copyout of size failed.", msg); 7150*7c478bd9Sstevel@tonic-gate rv = EIO; 7151*7c478bd9Sstevel@tonic-gate 7152*7c478bd9Sstevel@tonic-gate break; 7153*7c478bd9Sstevel@tonic-gate } 7154*7c478bd9Sstevel@tonic-gate } else { 7155*7c478bd9Sstevel@tonic-gate if (size == 0) { 7156*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_CBOPS, 7157*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 7158*7c478bd9Sstevel@tonic-gate "%s: String is NULL", msg); 7159*7c478bd9Sstevel@tonic-gate rv = EINVAL; 7160*7c478bd9Sstevel@tonic-gate 7161*7c478bd9Sstevel@tonic-gate break; 7162*7c478bd9Sstevel@tonic-gate } 7163*7c478bd9Sstevel@tonic-gate 7164*7c478bd9Sstevel@tonic-gate if (ioc.bufsiz != size) { 7165*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 7166*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 7167*7c478bd9Sstevel@tonic-gate "%s: string buf size wrong", msg); 7168*7c478bd9Sstevel@tonic-gate rv = EINVAL; 7169*7c478bd9Sstevel@tonic-gate 7170*7c478bd9Sstevel@tonic-gate break; 7171*7c478bd9Sstevel@tonic-gate } 7172*7c478bd9Sstevel@tonic-gate 7173*7c478bd9Sstevel@tonic-gate if (ddi_copyout((void *)str, ioc.buf, 7174*7c478bd9Sstevel@tonic-gate ioc.bufsiz, mode) != 0) { 7175*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 7176*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 7177*7c478bd9Sstevel@tonic-gate "%s: copyout failed.", msg); 7178*7c478bd9Sstevel@tonic-gate rv = EIO; 7179*7c478bd9Sstevel@tonic-gate 7180*7c478bd9Sstevel@tonic-gate break; 7181*7c478bd9Sstevel@tonic-gate } 7182*7c478bd9Sstevel@tonic-gate } 7183*7c478bd9Sstevel@tonic-gate break; 7184*7c478bd9Sstevel@tonic-gate } 7185*7c478bd9Sstevel@tonic-gate case HUBD_GET_CFGADM_NAME: 7186*7c478bd9Sstevel@tonic-gate { 7187*7c478bd9Sstevel@tonic-gate uint32_t name_len; 7188*7c478bd9Sstevel@tonic-gate const char *name; 7189*7c478bd9Sstevel@tonic-gate 7190*7c478bd9Sstevel@tonic-gate /* recheck */ 7191*7c478bd9Sstevel@tonic-gate if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) == 7192*7c478bd9Sstevel@tonic-gate NULL) { 7193*7c478bd9Sstevel@tonic-gate rv = EINVAL; 7194*7c478bd9Sstevel@tonic-gate 7195*7c478bd9Sstevel@tonic-gate break; 7196*7c478bd9Sstevel@tonic-gate } 7197*7c478bd9Sstevel@tonic-gate name = ddi_node_name(child_dip); 7198*7c478bd9Sstevel@tonic-gate if (name == NULL) { 7199*7c478bd9Sstevel@tonic-gate name = "unsupported"; 7200*7c478bd9Sstevel@tonic-gate } 7201*7c478bd9Sstevel@tonic-gate name_len = strlen(name) + 1; 7202*7c478bd9Sstevel@tonic-gate 7203*7c478bd9Sstevel@tonic-gate msg = "DEVCTL_AP_CONTROL: HUBD_GET_CFGADM_NAME"; 7204*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 7205*7c478bd9Sstevel@tonic-gate "%s: name=%s name_len=%d", msg, name, name_len); 7206*7c478bd9Sstevel@tonic-gate 7207*7c478bd9Sstevel@tonic-gate if (ioc.get_size) { 7208*7c478bd9Sstevel@tonic-gate if (ddi_copyout((void *)&name_len, 7209*7c478bd9Sstevel@tonic-gate ioc.buf, ioc.bufsiz, mode) != 0) { 7210*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 7211*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 7212*7c478bd9Sstevel@tonic-gate "%s: copyout of size failed", msg); 7213*7c478bd9Sstevel@tonic-gate rv = EIO; 7214*7c478bd9Sstevel@tonic-gate 7215*7c478bd9Sstevel@tonic-gate break; 7216*7c478bd9Sstevel@tonic-gate } 7217*7c478bd9Sstevel@tonic-gate } else { 7218*7c478bd9Sstevel@tonic-gate if (ioc.bufsiz != name_len) { 7219*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 7220*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 7221*7c478bd9Sstevel@tonic-gate "%s: string buf length wrong", msg); 7222*7c478bd9Sstevel@tonic-gate rv = EINVAL; 7223*7c478bd9Sstevel@tonic-gate 7224*7c478bd9Sstevel@tonic-gate break; 7225*7c478bd9Sstevel@tonic-gate } 7226*7c478bd9Sstevel@tonic-gate 7227*7c478bd9Sstevel@tonic-gate if (ddi_copyout((void *)name, ioc.buf, 7228*7c478bd9Sstevel@tonic-gate ioc.bufsiz, mode) != 0) { 7229*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 7230*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 7231*7c478bd9Sstevel@tonic-gate "%s: copyout failed.", msg); 7232*7c478bd9Sstevel@tonic-gate rv = EIO; 7233*7c478bd9Sstevel@tonic-gate 7234*7c478bd9Sstevel@tonic-gate break; 7235*7c478bd9Sstevel@tonic-gate } 7236*7c478bd9Sstevel@tonic-gate } 7237*7c478bd9Sstevel@tonic-gate 7238*7c478bd9Sstevel@tonic-gate break; 7239*7c478bd9Sstevel@tonic-gate } 7240*7c478bd9Sstevel@tonic-gate 7241*7c478bd9Sstevel@tonic-gate /* 7242*7c478bd9Sstevel@tonic-gate * Return the config index for the currently-configured 7243*7c478bd9Sstevel@tonic-gate * configuration. 7244*7c478bd9Sstevel@tonic-gate */ 7245*7c478bd9Sstevel@tonic-gate case HUBD_GET_CURRENT_CONFIG: 7246*7c478bd9Sstevel@tonic-gate { 7247*7c478bd9Sstevel@tonic-gate uint_t config_index; 7248*7c478bd9Sstevel@tonic-gate uint32_t size = sizeof (config_index); 7249*7c478bd9Sstevel@tonic-gate usba_device_t *usba_device; 7250*7c478bd9Sstevel@tonic-gate 7251*7c478bd9Sstevel@tonic-gate msg = "DEVCTL_AP_CONTROL: GET_CURRENT_CONFIG"; 7252*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 7253*7c478bd9Sstevel@tonic-gate "%s", msg); 7254*7c478bd9Sstevel@tonic-gate 7255*7c478bd9Sstevel@tonic-gate /* 7256*7c478bd9Sstevel@tonic-gate * Return the config index for the configuration 7257*7c478bd9Sstevel@tonic-gate * currently in use. 7258*7c478bd9Sstevel@tonic-gate * Recheck if child_dip exists 7259*7c478bd9Sstevel@tonic-gate */ 7260*7c478bd9Sstevel@tonic-gate if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) == 7261*7c478bd9Sstevel@tonic-gate NULL) { 7262*7c478bd9Sstevel@tonic-gate rv = EINVAL; 7263*7c478bd9Sstevel@tonic-gate 7264*7c478bd9Sstevel@tonic-gate break; 7265*7c478bd9Sstevel@tonic-gate } 7266*7c478bd9Sstevel@tonic-gate 7267*7c478bd9Sstevel@tonic-gate usba_device = usba_get_usba_device(child_dip); 7268*7c478bd9Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex); 7269*7c478bd9Sstevel@tonic-gate config_index = usba_device->usb_active_cfg_ndx; 7270*7c478bd9Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex); 7271*7c478bd9Sstevel@tonic-gate 7272*7c478bd9Sstevel@tonic-gate if (ioc.get_size) { 7273*7c478bd9Sstevel@tonic-gate if (ddi_copyout((void *)&size, 7274*7c478bd9Sstevel@tonic-gate ioc.buf, ioc.bufsiz, mode) != 0) { 7275*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 7276*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 7277*7c478bd9Sstevel@tonic-gate "%s: copyout of size failed.", msg); 7278*7c478bd9Sstevel@tonic-gate rv = EIO; 7279*7c478bd9Sstevel@tonic-gate 7280*7c478bd9Sstevel@tonic-gate break; 7281*7c478bd9Sstevel@tonic-gate } 7282*7c478bd9Sstevel@tonic-gate } else { 7283*7c478bd9Sstevel@tonic-gate if (ioc.bufsiz != size) { 7284*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 7285*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 7286*7c478bd9Sstevel@tonic-gate "%s: buffer size wrong", msg); 7287*7c478bd9Sstevel@tonic-gate rv = EINVAL; 7288*7c478bd9Sstevel@tonic-gate 7289*7c478bd9Sstevel@tonic-gate break; 7290*7c478bd9Sstevel@tonic-gate } 7291*7c478bd9Sstevel@tonic-gate if (ddi_copyout((void *)&config_index, 7292*7c478bd9Sstevel@tonic-gate ioc.buf, ioc.bufsiz, mode) != 0) { 7293*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 7294*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 7295*7c478bd9Sstevel@tonic-gate "%s: copyout failed", msg); 7296*7c478bd9Sstevel@tonic-gate rv = EIO; 7297*7c478bd9Sstevel@tonic-gate } 7298*7c478bd9Sstevel@tonic-gate } 7299*7c478bd9Sstevel@tonic-gate 7300*7c478bd9Sstevel@tonic-gate break; 7301*7c478bd9Sstevel@tonic-gate } 7302*7c478bd9Sstevel@tonic-gate case HUBD_GET_DEVICE_PATH: 7303*7c478bd9Sstevel@tonic-gate { 7304*7c478bd9Sstevel@tonic-gate char *path; 7305*7c478bd9Sstevel@tonic-gate uint32_t size; 7306*7c478bd9Sstevel@tonic-gate 7307*7c478bd9Sstevel@tonic-gate msg = "DEVCTL_AP_CONTROL: GET_DEVICE_PATH"; 7308*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 7309*7c478bd9Sstevel@tonic-gate "%s", msg); 7310*7c478bd9Sstevel@tonic-gate 7311*7c478bd9Sstevel@tonic-gate /* Recheck if child_dip exists */ 7312*7c478bd9Sstevel@tonic-gate if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) == 7313*7c478bd9Sstevel@tonic-gate NULL) { 7314*7c478bd9Sstevel@tonic-gate rv = EINVAL; 7315*7c478bd9Sstevel@tonic-gate 7316*7c478bd9Sstevel@tonic-gate break; 7317*7c478bd9Sstevel@tonic-gate } 7318*7c478bd9Sstevel@tonic-gate 7319*7c478bd9Sstevel@tonic-gate /* ddi_pathname doesn't supply /devices, so we do. */ 7320*7c478bd9Sstevel@tonic-gate path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 7321*7c478bd9Sstevel@tonic-gate (void) strcpy(path, "/devices"); 7322*7c478bd9Sstevel@tonic-gate (void) ddi_pathname(child_dip, path + strlen(path)); 7323*7c478bd9Sstevel@tonic-gate size = strlen(path) + 1; 7324*7c478bd9Sstevel@tonic-gate 7325*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 7326*7c478bd9Sstevel@tonic-gate "%s: device path=%s size=%d", msg, path, size); 7327*7c478bd9Sstevel@tonic-gate 7328*7c478bd9Sstevel@tonic-gate if (ioc.get_size) { 7329*7c478bd9Sstevel@tonic-gate if (ddi_copyout((void *)&size, 7330*7c478bd9Sstevel@tonic-gate ioc.buf, ioc.bufsiz, mode) != 0) { 7331*7c478bd9Sstevel@tonic-gate 7332*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 7333*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 7334*7c478bd9Sstevel@tonic-gate "%s: copyout of size failed.", msg); 7335*7c478bd9Sstevel@tonic-gate rv = EIO; 7336*7c478bd9Sstevel@tonic-gate } 7337*7c478bd9Sstevel@tonic-gate } else { 7338*7c478bd9Sstevel@tonic-gate if (ioc.bufsiz != size) { 7339*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 7340*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 7341*7c478bd9Sstevel@tonic-gate "%s: buffer wrong size.", msg); 7342*7c478bd9Sstevel@tonic-gate rv = EINVAL; 7343*7c478bd9Sstevel@tonic-gate } else if (ddi_copyout((void *)path, 7344*7c478bd9Sstevel@tonic-gate ioc.buf, ioc.bufsiz, mode) != 0) { 7345*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 7346*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 7347*7c478bd9Sstevel@tonic-gate "%s: copyout failed.", msg); 7348*7c478bd9Sstevel@tonic-gate rv = EIO; 7349*7c478bd9Sstevel@tonic-gate } 7350*7c478bd9Sstevel@tonic-gate } 7351*7c478bd9Sstevel@tonic-gate kmem_free(path, MAXPATHLEN); 7352*7c478bd9Sstevel@tonic-gate 7353*7c478bd9Sstevel@tonic-gate break; 7354*7c478bd9Sstevel@tonic-gate } 7355*7c478bd9Sstevel@tonic-gate case HUBD_REFRESH_DEVDB: 7356*7c478bd9Sstevel@tonic-gate msg = "DEVCTL_AP_CONTROL: HUBD_REFRESH_DEVDB"; 7357*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_CBOPS, hubd->h_log_handle, 7358*7c478bd9Sstevel@tonic-gate "%s", msg); 7359*7c478bd9Sstevel@tonic-gate 7360*7c478bd9Sstevel@tonic-gate if ((rv = usba_devdb_refresh()) != USB_SUCCESS) { 7361*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 7362*7c478bd9Sstevel@tonic-gate hubd->h_log_handle, 7363*7c478bd9Sstevel@tonic-gate "%s: Failed: %d", msg, rv); 7364*7c478bd9Sstevel@tonic-gate rv = EIO; 7365*7c478bd9Sstevel@tonic-gate } 7366*7c478bd9Sstevel@tonic-gate 7367*7c478bd9Sstevel@tonic-gate break; 7368*7c478bd9Sstevel@tonic-gate default: 7369*7c478bd9Sstevel@tonic-gate rv = ENOTSUP; 7370*7c478bd9Sstevel@tonic-gate } /* end switch */ 7371*7c478bd9Sstevel@tonic-gate 7372*7c478bd9Sstevel@tonic-gate break; 7373*7c478bd9Sstevel@tonic-gate } 7374*7c478bd9Sstevel@tonic-gate 7375*7c478bd9Sstevel@tonic-gate default: 7376*7c478bd9Sstevel@tonic-gate rv = ENOTTY; 7377*7c478bd9Sstevel@tonic-gate } 7378*7c478bd9Sstevel@tonic-gate 7379*7c478bd9Sstevel@tonic-gate if (dcp) { 7380*7c478bd9Sstevel@tonic-gate ndi_dc_freehdl(dcp); 7381*7c478bd9Sstevel@tonic-gate } 7382*7c478bd9Sstevel@tonic-gate 7383*7c478bd9Sstevel@tonic-gate /* allow hotplug thread now */ 7384*7c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread--; 7385*7c478bd9Sstevel@tonic-gate 7386*7c478bd9Sstevel@tonic-gate if ((hubd->h_dev_state == USB_DEV_ONLINE) && 7387*7c478bd9Sstevel@tonic-gate hubd->h_ep1_ph && (prev_pipe_state == USB_PIPE_STATE_ACTIVE)) { 7388*7c478bd9Sstevel@tonic-gate hubd_start_polling(hubd, 0); 7389*7c478bd9Sstevel@tonic-gate } 7390*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 7391*7c478bd9Sstevel@tonic-gate 7392*7c478bd9Sstevel@tonic-gate ndi_devi_exit(hubd->h_dip, circ); 7393*7c478bd9Sstevel@tonic-gate ndi_devi_exit(rh_dip, rh_circ); 7394*7c478bd9Sstevel@tonic-gate ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ); 7395*7c478bd9Sstevel@tonic-gate 7396*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 7397*7c478bd9Sstevel@tonic-gate hubd_pm_idle_component(hubd, hubd->h_dip, 0); 7398*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 7399*7c478bd9Sstevel@tonic-gate 7400*7c478bd9Sstevel@tonic-gate return (rv); 7401*7c478bd9Sstevel@tonic-gate } 7402*7c478bd9Sstevel@tonic-gate 7403*7c478bd9Sstevel@tonic-gate 7404*7c478bd9Sstevel@tonic-gate /* 7405*7c478bd9Sstevel@tonic-gate * Helper func used only to help construct the names for the attachment point 7406*7c478bd9Sstevel@tonic-gate * minor nodes. Used only in usba_hubdi_attach. 7407*7c478bd9Sstevel@tonic-gate * Returns whether it found ancestry or not (USB_SUCCESS if yes). 7408*7c478bd9Sstevel@tonic-gate * ports between the root hub and the device represented by dip. 7409*7c478bd9Sstevel@tonic-gate * E.g., "2.4.3.1" means this device is 7410*7c478bd9Sstevel@tonic-gate * plugged into port 1 of a hub that is 7411*7c478bd9Sstevel@tonic-gate * plugged into port 3 of a hub that is 7412*7c478bd9Sstevel@tonic-gate * plugged into port 4 of a hub that is 7413*7c478bd9Sstevel@tonic-gate * plugged into port 2 of the root hub. 7414*7c478bd9Sstevel@tonic-gate * NOTE: Max ap_id path len is HUBD_APID_NAMELEN (32 chars), which is 7415*7c478bd9Sstevel@tonic-gate * more than sufficient (as hubs are a max 6 levels deep, port needs 3 7416*7c478bd9Sstevel@tonic-gate * chars plus NULL each) 7417*7c478bd9Sstevel@tonic-gate */ 7418*7c478bd9Sstevel@tonic-gate static void 7419*7c478bd9Sstevel@tonic-gate hubd_get_ancestry_str(hubd_t *hubd) 7420*7c478bd9Sstevel@tonic-gate { 7421*7c478bd9Sstevel@tonic-gate char dev_path[MAXPATHLEN]; 7422*7c478bd9Sstevel@tonic-gate char *port_num_pos; 7423*7c478bd9Sstevel@tonic-gate char port_list[HUBD_APID_NAMELEN]; 7424*7c478bd9Sstevel@tonic-gate char *port_list_end = port_list; 7425*7c478bd9Sstevel@tonic-gate 7426*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 7427*7c478bd9Sstevel@tonic-gate "hubd_get_ancestry_str: hubd=0x%p", hubd); 7428*7c478bd9Sstevel@tonic-gate 7429*7c478bd9Sstevel@tonic-gate dev_path[0] = '\0'; 7430*7c478bd9Sstevel@tonic-gate (void) ddi_pathname(hubd->h_dip, dev_path); 7431*7c478bd9Sstevel@tonic-gate port_num_pos = dev_path; 7432*7c478bd9Sstevel@tonic-gate 7433*7c478bd9Sstevel@tonic-gate port_list[0] = NULL; 7434*7c478bd9Sstevel@tonic-gate while ((port_num_pos = (char *)strstr(port_num_pos, "hub@")) != NULL) { 7435*7c478bd9Sstevel@tonic-gate /* 7436*7c478bd9Sstevel@tonic-gate * Found a non-root hub between the root hub port and device. 7437*7c478bd9Sstevel@tonic-gate * Get the number of the port this hub is plugged into, 7438*7c478bd9Sstevel@tonic-gate * and append it to the ancestry string. 7439*7c478bd9Sstevel@tonic-gate */ 7440*7c478bd9Sstevel@tonic-gate if (port_list_end != port_list) { /* have list already */ 7441*7c478bd9Sstevel@tonic-gate (void) strcat(port_list_end, "."); 7442*7c478bd9Sstevel@tonic-gate port_list_end++; 7443*7c478bd9Sstevel@tonic-gate } 7444*7c478bd9Sstevel@tonic-gate 7445*7c478bd9Sstevel@tonic-gate while (!isdigit(*port_num_pos)) { 7446*7c478bd9Sstevel@tonic-gate if (*port_num_pos++ == '\0') { 7447*7c478bd9Sstevel@tonic-gate 7448*7c478bd9Sstevel@tonic-gate break; 7449*7c478bd9Sstevel@tonic-gate } 7450*7c478bd9Sstevel@tonic-gate } 7451*7c478bd9Sstevel@tonic-gate 7452*7c478bd9Sstevel@tonic-gate while (isdigit(*port_num_pos)) { 7453*7c478bd9Sstevel@tonic-gate *port_list_end++ = *port_num_pos++; 7454*7c478bd9Sstevel@tonic-gate ASSERT(port_list_end < 7455*7c478bd9Sstevel@tonic-gate (port_list + sizeof (port_list))); 7456*7c478bd9Sstevel@tonic-gate ASSERT(port_num_pos < (dev_path + sizeof (dev_path))); 7457*7c478bd9Sstevel@tonic-gate } 7458*7c478bd9Sstevel@tonic-gate *port_list_end = '\0'; 7459*7c478bd9Sstevel@tonic-gate } 7460*7c478bd9Sstevel@tonic-gate 7461*7c478bd9Sstevel@tonic-gate if (port_list_end != port_list) { 7462*7c478bd9Sstevel@tonic-gate (void) strcpy(hubd->h_ancestry_str, port_list); 7463*7c478bd9Sstevel@tonic-gate (void) strcat(hubd->h_ancestry_str, "."); 7464*7c478bd9Sstevel@tonic-gate } 7465*7c478bd9Sstevel@tonic-gate } 7466*7c478bd9Sstevel@tonic-gate 7467*7c478bd9Sstevel@tonic-gate 7468*7c478bd9Sstevel@tonic-gate /* Get which port to operate on. */ 7469*7c478bd9Sstevel@tonic-gate static usb_port_t 7470*7c478bd9Sstevel@tonic-gate hubd_get_port_num(hubd_t *hubd, struct devctl_iocdata *dcp) 7471*7c478bd9Sstevel@tonic-gate { 7472*7c478bd9Sstevel@tonic-gate int32_t port; 7473*7c478bd9Sstevel@tonic-gate 7474*7c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 7475*7c478bd9Sstevel@tonic-gate 7476*7c478bd9Sstevel@tonic-gate /* Get which port to operate on. */ 7477*7c478bd9Sstevel@tonic-gate if (nvlist_lookup_int32(ndi_dc_get_ap_data(dcp), "port", &port) != 0) { 7478*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, hubd->h_log_handle, 7479*7c478bd9Sstevel@tonic-gate "hubd_get_port_num: port lookup failed"); 7480*7c478bd9Sstevel@tonic-gate port = 0; 7481*7c478bd9Sstevel@tonic-gate } 7482*7c478bd9Sstevel@tonic-gate 7483*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 7484*7c478bd9Sstevel@tonic-gate "hubd_get_port_num: hubd=0x%p, port=%d", hubd, port); 7485*7c478bd9Sstevel@tonic-gate 7486*7c478bd9Sstevel@tonic-gate return ((usb_port_t)port); 7487*7c478bd9Sstevel@tonic-gate } 7488*7c478bd9Sstevel@tonic-gate 7489*7c478bd9Sstevel@tonic-gate 7490*7c478bd9Sstevel@tonic-gate /* check if child still exists */ 7491*7c478bd9Sstevel@tonic-gate static dev_info_t * 7492*7c478bd9Sstevel@tonic-gate hubd_get_child_dip(hubd_t *hubd, usb_port_t port) 7493*7c478bd9Sstevel@tonic-gate { 7494*7c478bd9Sstevel@tonic-gate dev_info_t *child_dip = hubd->h_children_dips[port]; 7495*7c478bd9Sstevel@tonic-gate 7496*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 7497*7c478bd9Sstevel@tonic-gate "hubd_get_child_dip: hubd=0x%p, port=%d", hubd, port); 7498*7c478bd9Sstevel@tonic-gate 7499*7c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 7500*7c478bd9Sstevel@tonic-gate 7501*7c478bd9Sstevel@tonic-gate return (child_dip); 7502*7c478bd9Sstevel@tonic-gate } 7503*7c478bd9Sstevel@tonic-gate 7504*7c478bd9Sstevel@tonic-gate 7505*7c478bd9Sstevel@tonic-gate /* 7506*7c478bd9Sstevel@tonic-gate * hubd_cfgadm_state: 7507*7c478bd9Sstevel@tonic-gate * 7508*7c478bd9Sstevel@tonic-gate * child_dip list port_state cfgadm_state 7509*7c478bd9Sstevel@tonic-gate * -------------- ---------- ------------ 7510*7c478bd9Sstevel@tonic-gate * != NULL connected configured or 7511*7c478bd9Sstevel@tonic-gate * unconfigured 7512*7c478bd9Sstevel@tonic-gate * != NULL not connected disconnect but 7513*7c478bd9Sstevel@tonic-gate * busy/still referenced 7514*7c478bd9Sstevel@tonic-gate * NULL connected logically disconnected 7515*7c478bd9Sstevel@tonic-gate * NULL not connected empty 7516*7c478bd9Sstevel@tonic-gate */ 7517*7c478bd9Sstevel@tonic-gate static uint_t 7518*7c478bd9Sstevel@tonic-gate hubd_cfgadm_state(hubd_t *hubd, usb_port_t port) 7519*7c478bd9Sstevel@tonic-gate { 7520*7c478bd9Sstevel@tonic-gate uint_t state; 7521*7c478bd9Sstevel@tonic-gate dev_info_t *child_dip = hubd_get_child_dip(hubd, port); 7522*7c478bd9Sstevel@tonic-gate 7523*7c478bd9Sstevel@tonic-gate if (child_dip) { 7524*7c478bd9Sstevel@tonic-gate if (hubd->h_port_state[port] & PORT_STATUS_CCS) { 7525*7c478bd9Sstevel@tonic-gate /* 7526*7c478bd9Sstevel@tonic-gate * connected, now check if driver exists 7527*7c478bd9Sstevel@tonic-gate */ 7528*7c478bd9Sstevel@tonic-gate if (DEVI_IS_DEVICE_OFFLINE(child_dip) || 7529*7c478bd9Sstevel@tonic-gate (i_ddi_node_state(child_dip) < DS_ATTACHED)) { 7530*7c478bd9Sstevel@tonic-gate state = HUBD_CFGADM_UNCONFIGURED; 7531*7c478bd9Sstevel@tonic-gate } else { 7532*7c478bd9Sstevel@tonic-gate state = HUBD_CFGADM_CONFIGURED; 7533*7c478bd9Sstevel@tonic-gate } 7534*7c478bd9Sstevel@tonic-gate } else { 7535*7c478bd9Sstevel@tonic-gate /* 7536*7c478bd9Sstevel@tonic-gate * this means that the dip is around for 7537*7c478bd9Sstevel@tonic-gate * a device that is still referenced but 7538*7c478bd9Sstevel@tonic-gate * has been yanked out. So the cfgadm info 7539*7c478bd9Sstevel@tonic-gate * for this state should be EMPTY (port empty) 7540*7c478bd9Sstevel@tonic-gate * and CONFIGURED (dip still valid). 7541*7c478bd9Sstevel@tonic-gate */ 7542*7c478bd9Sstevel@tonic-gate state = HUBD_CFGADM_STILL_REFERENCED; 7543*7c478bd9Sstevel@tonic-gate } 7544*7c478bd9Sstevel@tonic-gate } else { 7545*7c478bd9Sstevel@tonic-gate /* connected but no child dip */ 7546*7c478bd9Sstevel@tonic-gate if (hubd->h_port_state[port] & PORT_STATUS_CCS) { 7547*7c478bd9Sstevel@tonic-gate /* logically disconnected */ 7548*7c478bd9Sstevel@tonic-gate state = HUBD_CFGADM_DISCONNECTED; 7549*7c478bd9Sstevel@tonic-gate } else { 7550*7c478bd9Sstevel@tonic-gate /* physically disconnected */ 7551*7c478bd9Sstevel@tonic-gate state = HUBD_CFGADM_EMPTY; 7552*7c478bd9Sstevel@tonic-gate } 7553*7c478bd9Sstevel@tonic-gate } 7554*7c478bd9Sstevel@tonic-gate 7555*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 7556*7c478bd9Sstevel@tonic-gate "hubd_cfgadm_state: hubd=0x%p, port=%d state=0x%x", 7557*7c478bd9Sstevel@tonic-gate hubd, port, state); 7558*7c478bd9Sstevel@tonic-gate 7559*7c478bd9Sstevel@tonic-gate return (state); 7560*7c478bd9Sstevel@tonic-gate } 7561*7c478bd9Sstevel@tonic-gate 7562*7c478bd9Sstevel@tonic-gate 7563*7c478bd9Sstevel@tonic-gate /* 7564*7c478bd9Sstevel@tonic-gate * hubd_toggle_port: 7565*7c478bd9Sstevel@tonic-gate */ 7566*7c478bd9Sstevel@tonic-gate static int 7567*7c478bd9Sstevel@tonic-gate hubd_toggle_port(hubd_t *hubd, usb_port_t port) 7568*7c478bd9Sstevel@tonic-gate { 7569*7c478bd9Sstevel@tonic-gate usb_hub_descr_t *hub_descr; 7570*7c478bd9Sstevel@tonic-gate int wait; 7571*7c478bd9Sstevel@tonic-gate uint_t retry; 7572*7c478bd9Sstevel@tonic-gate uint16_t status; 7573*7c478bd9Sstevel@tonic-gate uint16_t change; 7574*7c478bd9Sstevel@tonic-gate 7575*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 7576*7c478bd9Sstevel@tonic-gate "hubd_toggle_port: hubd=0x%p, port=%d", hubd, port); 7577*7c478bd9Sstevel@tonic-gate 7578*7c478bd9Sstevel@tonic-gate if ((hubd_disable_port_power(hubd, port)) != USB_SUCCESS) { 7579*7c478bd9Sstevel@tonic-gate 7580*7c478bd9Sstevel@tonic-gate return (USB_FAILURE); 7581*7c478bd9Sstevel@tonic-gate } 7582*7c478bd9Sstevel@tonic-gate 7583*7c478bd9Sstevel@tonic-gate /* 7584*7c478bd9Sstevel@tonic-gate * see hubd_enable_all_port_power() which 7585*7c478bd9Sstevel@tonic-gate * requires longer delay for hubs. 7586*7c478bd9Sstevel@tonic-gate */ 7587*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 7588*7c478bd9Sstevel@tonic-gate delay(drv_usectohz(hubd_device_delay / 10)); 7589*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 7590*7c478bd9Sstevel@tonic-gate 7591*7c478bd9Sstevel@tonic-gate hub_descr = &hubd->h_hub_descr; 7592*7c478bd9Sstevel@tonic-gate 7593*7c478bd9Sstevel@tonic-gate /* 7594*7c478bd9Sstevel@tonic-gate * According to section 11.11 of USB, for hubs with no power 7595*7c478bd9Sstevel@tonic-gate * switches, bPwrOn2PwrGood is zero. But we wait for some 7596*7c478bd9Sstevel@tonic-gate * arbitrary time to enable power to become stable. 7597*7c478bd9Sstevel@tonic-gate * 7598*7c478bd9Sstevel@tonic-gate * If an hub supports port power swicthing, we need to wait 7599*7c478bd9Sstevel@tonic-gate * at least 20ms before accesing corresonding usb port. 7600*7c478bd9Sstevel@tonic-gate */ 7601*7c478bd9Sstevel@tonic-gate if ((hub_descr->wHubCharacteristics & 7602*7c478bd9Sstevel@tonic-gate HUB_CHARS_NO_POWER_SWITCHING) || (!hub_descr->bPwrOn2PwrGood)) { 7603*7c478bd9Sstevel@tonic-gate wait = hubd_device_delay / 10; 7604*7c478bd9Sstevel@tonic-gate } else { 7605*7c478bd9Sstevel@tonic-gate wait = max(HUB_DEFAULT_POPG, 7606*7c478bd9Sstevel@tonic-gate hub_descr->bPwrOn2PwrGood) * 2 * 1000; 7607*7c478bd9Sstevel@tonic-gate } 7608*7c478bd9Sstevel@tonic-gate 7609*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 7610*7c478bd9Sstevel@tonic-gate "hubd_toggle_port: popg=%d wait=%d", 7611*7c478bd9Sstevel@tonic-gate hub_descr->bPwrOn2PwrGood, wait); 7612*7c478bd9Sstevel@tonic-gate 7613*7c478bd9Sstevel@tonic-gate retry = 0; 7614*7c478bd9Sstevel@tonic-gate 7615*7c478bd9Sstevel@tonic-gate do { 7616*7c478bd9Sstevel@tonic-gate (void) hubd_enable_port_power(hubd, port); 7617*7c478bd9Sstevel@tonic-gate 7618*7c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 7619*7c478bd9Sstevel@tonic-gate delay(drv_usectohz(wait)); 7620*7c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 7621*7c478bd9Sstevel@tonic-gate 7622*7c478bd9Sstevel@tonic-gate /* Get port status */ 7623*7c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 7624*7c478bd9Sstevel@tonic-gate &status, &change, 0); 7625*7c478bd9Sstevel@tonic-gate 7626*7c478bd9Sstevel@tonic-gate /* For retry if any, use some extra delay */ 7627*7c478bd9Sstevel@tonic-gate wait = max(wait, hubd_device_delay / 10); 7628*7c478bd9Sstevel@tonic-gate 7629*7c478bd9Sstevel@tonic-gate retry++; 7630*7c478bd9Sstevel@tonic-gate 7631*7c478bd9Sstevel@tonic-gate } while ((!(status & PORT_STATUS_PPS)) && (retry < HUBD_PORT_RETRY)); 7632*7c478bd9Sstevel@tonic-gate 7633*7c478bd9Sstevel@tonic-gate /* Print warning message if port has no power */ 7634*7c478bd9Sstevel@tonic-gate if (!(status & PORT_STATUS_PPS)) { 7635*7c478bd9Sstevel@tonic-gate 7636*7c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_PORT, hubd->h_log_handle, 7637*7c478bd9Sstevel@tonic-gate "hubd_toggle_port: port %d power-on failed, " 7638*7c478bd9Sstevel@tonic-gate "port status 0x%x", port, status); 7639*7c478bd9Sstevel@tonic-gate 7640*7c478bd9Sstevel@tonic-gate return (USB_FAILURE); 7641*7c478bd9Sstevel@tonic-gate } 7642*7c478bd9Sstevel@tonic-gate 7643*7c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 7644*7c478bd9Sstevel@tonic-gate } 7645