10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 52326Ssl147100 * Common Development and Distribution License (the "License"). 62326Ssl147100 * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 223435Slg150142 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate 290Sstevel@tonic-gate /* 300Sstevel@tonic-gate * USBA: Solaris USB Architecture support for the hub 310Sstevel@tonic-gate * including root hub 320Sstevel@tonic-gate * Most of the code for hubd resides in this file and 330Sstevel@tonic-gate * is shared between the HCD root hub support and hubd 340Sstevel@tonic-gate */ 350Sstevel@tonic-gate #define USBA_FRAMEWORK 360Sstevel@tonic-gate #include <sys/usb/usba.h> 370Sstevel@tonic-gate #include <sys/usb/usba/usba_devdb.h> 380Sstevel@tonic-gate #include <sys/sunndi.h> 390Sstevel@tonic-gate #include <sys/usb/usba/usba_impl.h> 400Sstevel@tonic-gate #include <sys/usb/usba/usba_types.h> 410Sstevel@tonic-gate #include <sys/usb/usba/hubdi.h> 420Sstevel@tonic-gate #include <sys/usb/usba/hcdi_impl.h> 430Sstevel@tonic-gate #include <sys/usb/hubd/hub.h> 440Sstevel@tonic-gate #include <sys/usb/hubd/hubdvar.h> 450Sstevel@tonic-gate #include <sys/usb/hubd/hubd_impl.h> 460Sstevel@tonic-gate #include <sys/kobj.h> 470Sstevel@tonic-gate #include <sys/kobj_lex.h> 480Sstevel@tonic-gate #include <sys/fs/dv_node.h> 490Sstevel@tonic-gate 500Sstevel@tonic-gate /* 510Sstevel@tonic-gate * Prototypes for static functions 520Sstevel@tonic-gate */ 530Sstevel@tonic-gate static int usba_hubdi_bus_ctl( 540Sstevel@tonic-gate dev_info_t *dip, 550Sstevel@tonic-gate dev_info_t *rdip, 560Sstevel@tonic-gate ddi_ctl_enum_t op, 570Sstevel@tonic-gate void *arg, 580Sstevel@tonic-gate void *result); 590Sstevel@tonic-gate 600Sstevel@tonic-gate static int usba_hubdi_map_fault( 610Sstevel@tonic-gate dev_info_t *dip, 620Sstevel@tonic-gate dev_info_t *rdip, 630Sstevel@tonic-gate struct hat *hat, 640Sstevel@tonic-gate struct seg *seg, 650Sstevel@tonic-gate caddr_t addr, 660Sstevel@tonic-gate struct devpage *dp, 670Sstevel@tonic-gate pfn_t pfn, 680Sstevel@tonic-gate uint_t prot, 690Sstevel@tonic-gate uint_t lock); 700Sstevel@tonic-gate 710Sstevel@tonic-gate static int hubd_busop_get_eventcookie(dev_info_t *dip, 720Sstevel@tonic-gate dev_info_t *rdip, 730Sstevel@tonic-gate char *eventname, 740Sstevel@tonic-gate ddi_eventcookie_t *cookie); 750Sstevel@tonic-gate static int hubd_busop_add_eventcall(dev_info_t *dip, 760Sstevel@tonic-gate dev_info_t *rdip, 770Sstevel@tonic-gate ddi_eventcookie_t cookie, 780Sstevel@tonic-gate void (*callback)(dev_info_t *dip, 790Sstevel@tonic-gate ddi_eventcookie_t cookie, void *arg, 800Sstevel@tonic-gate void *bus_impldata), 810Sstevel@tonic-gate void *arg, ddi_callback_id_t *cb_id); 820Sstevel@tonic-gate static int hubd_busop_remove_eventcall(dev_info_t *dip, 830Sstevel@tonic-gate ddi_callback_id_t cb_id); 840Sstevel@tonic-gate static int hubd_bus_config(dev_info_t *dip, 850Sstevel@tonic-gate uint_t flag, 860Sstevel@tonic-gate ddi_bus_config_op_t op, 870Sstevel@tonic-gate void *arg, 880Sstevel@tonic-gate dev_info_t **child); 890Sstevel@tonic-gate static int hubd_bus_unconfig(dev_info_t *dip, 900Sstevel@tonic-gate uint_t flag, 910Sstevel@tonic-gate ddi_bus_config_op_t op, 920Sstevel@tonic-gate void *arg); 930Sstevel@tonic-gate static int hubd_bus_power(dev_info_t *dip, void *impl_arg, 940Sstevel@tonic-gate pm_bus_power_op_t op, void *arg, void *result); 950Sstevel@tonic-gate 960Sstevel@tonic-gate static void hubd_get_ancestry_str(hubd_t *); 970Sstevel@tonic-gate static usb_port_t hubd_get_port_num(hubd_t *, struct devctl_iocdata *); 980Sstevel@tonic-gate static dev_info_t *hubd_get_child_dip(hubd_t *, usb_port_t); 990Sstevel@tonic-gate static uint_t hubd_cfgadm_state(hubd_t *, usb_port_t); 1000Sstevel@tonic-gate static int hubd_toggle_port(hubd_t *, usb_port_t); 1010Sstevel@tonic-gate static void hubd_register_cpr_callback(hubd_t *); 1020Sstevel@tonic-gate static void hubd_unregister_cpr_callback(hubd_t *); 1030Sstevel@tonic-gate static hubd_t *hubd_get_soft_state(dev_info_t *dip); 1040Sstevel@tonic-gate 1050Sstevel@tonic-gate /* 1060Sstevel@tonic-gate * Busops vector for USB HUB's 1070Sstevel@tonic-gate */ 1080Sstevel@tonic-gate struct bus_ops usba_hubdi_busops = { 1090Sstevel@tonic-gate BUSO_REV, 1100Sstevel@tonic-gate nullbusmap, /* bus_map */ 1110Sstevel@tonic-gate NULL, /* bus_get_intrspec */ 1120Sstevel@tonic-gate NULL, /* bus_add_intrspec */ 1130Sstevel@tonic-gate NULL, /* bus_remove_intrspec */ 1140Sstevel@tonic-gate usba_hubdi_map_fault, /* bus_map_fault */ 1150Sstevel@tonic-gate ddi_dma_map, /* bus_dma_map */ 1160Sstevel@tonic-gate ddi_dma_allochdl, 1170Sstevel@tonic-gate ddi_dma_freehdl, 1180Sstevel@tonic-gate ddi_dma_bindhdl, 1190Sstevel@tonic-gate ddi_dma_unbindhdl, 1200Sstevel@tonic-gate ddi_dma_flush, 1210Sstevel@tonic-gate ddi_dma_win, 1220Sstevel@tonic-gate ddi_dma_mctl, /* bus_dma_ctl */ 1230Sstevel@tonic-gate usba_hubdi_bus_ctl, /* bus_ctl */ 1240Sstevel@tonic-gate ddi_bus_prop_op, /* bus_prop_op */ 1250Sstevel@tonic-gate hubd_busop_get_eventcookie, 1260Sstevel@tonic-gate hubd_busop_add_eventcall, 1270Sstevel@tonic-gate hubd_busop_remove_eventcall, 1280Sstevel@tonic-gate NULL, /* bus_post_event */ 1290Sstevel@tonic-gate NULL, /* bus_intr_ctl */ 1300Sstevel@tonic-gate hubd_bus_config, /* bus_config */ 1310Sstevel@tonic-gate hubd_bus_unconfig, /* bus_unconfig */ 1320Sstevel@tonic-gate NULL, /* bus_fm_init */ 1330Sstevel@tonic-gate NULL, /* bus_fm_fini */ 1340Sstevel@tonic-gate NULL, /* bus_fm_access_enter */ 1350Sstevel@tonic-gate NULL, /* bus_fm_access_exit */ 1360Sstevel@tonic-gate hubd_bus_power /* bus_power */ 1370Sstevel@tonic-gate }; 1380Sstevel@tonic-gate 1390Sstevel@tonic-gate 1400Sstevel@tonic-gate /* 1410Sstevel@tonic-gate * local variables 1420Sstevel@tonic-gate */ 1430Sstevel@tonic-gate static kmutex_t usba_hubdi_mutex; /* protects USBA HUB data structures */ 1440Sstevel@tonic-gate 1450Sstevel@tonic-gate static usba_list_entry_t usba_hubdi_list; 1460Sstevel@tonic-gate 1470Sstevel@tonic-gate usb_log_handle_t hubdi_log_handle; 1480Sstevel@tonic-gate uint_t hubdi_errlevel = USB_LOG_L4; 1490Sstevel@tonic-gate uint_t hubdi_errmask = (uint_t)-1; 1500Sstevel@tonic-gate uint_t hubdi_min_pm_threshold = 5; /* seconds */ 1510Sstevel@tonic-gate 1520Sstevel@tonic-gate /* 1530Sstevel@tonic-gate * initialize private data 1540Sstevel@tonic-gate */ 1550Sstevel@tonic-gate void 1560Sstevel@tonic-gate usba_hubdi_initialization() 1570Sstevel@tonic-gate { 1580Sstevel@tonic-gate hubdi_log_handle = usb_alloc_log_hdl(NULL, "hubdi", &hubdi_errlevel, 159*4763Slg150142 &hubdi_errmask, NULL, 0); 1600Sstevel@tonic-gate 1610Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle, 1620Sstevel@tonic-gate "usba_hubdi_initialization"); 1630Sstevel@tonic-gate 1640Sstevel@tonic-gate mutex_init(&usba_hubdi_mutex, NULL, MUTEX_DRIVER, NULL); 1650Sstevel@tonic-gate 1660Sstevel@tonic-gate usba_init_list(&usba_hubdi_list, NULL, NULL); 1670Sstevel@tonic-gate } 1680Sstevel@tonic-gate 1690Sstevel@tonic-gate 1700Sstevel@tonic-gate void 1710Sstevel@tonic-gate usba_hubdi_destroy() 1720Sstevel@tonic-gate { 1730Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle, 1740Sstevel@tonic-gate "usba_hubdi_destroy"); 1750Sstevel@tonic-gate 1760Sstevel@tonic-gate mutex_destroy(&usba_hubdi_mutex); 1770Sstevel@tonic-gate usba_destroy_list(&usba_hubdi_list); 1780Sstevel@tonic-gate 1790Sstevel@tonic-gate usb_free_log_hdl(hubdi_log_handle); 1800Sstevel@tonic-gate } 1810Sstevel@tonic-gate 1820Sstevel@tonic-gate 1830Sstevel@tonic-gate /* 1840Sstevel@tonic-gate * Called by an HUB to attach an instance of the driver 1850Sstevel@tonic-gate * make this instance known to USBA 1860Sstevel@tonic-gate * the HUB should initialize usba_hubdi structure prior 1870Sstevel@tonic-gate * to calling this interface 1880Sstevel@tonic-gate */ 1890Sstevel@tonic-gate static int 1900Sstevel@tonic-gate usba_hubdi_register(dev_info_t *dip, 1910Sstevel@tonic-gate uint_t flags) 1920Sstevel@tonic-gate { 1930Sstevel@tonic-gate usba_hubdi_t *hubdi = kmem_zalloc(sizeof (usba_hubdi_t), KM_SLEEP); 1940Sstevel@tonic-gate usba_device_t *usba_device = usba_get_usba_device(dip); 1950Sstevel@tonic-gate 1960Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle, 1970Sstevel@tonic-gate "usba_hubdi_register: %s", ddi_node_name(dip)); 1980Sstevel@tonic-gate 1990Sstevel@tonic-gate hubdi->hubdi_dip = dip; 2000Sstevel@tonic-gate hubdi->hubdi_flags = flags; 2010Sstevel@tonic-gate 2020Sstevel@tonic-gate usba_device->usb_hubdi = hubdi; 2030Sstevel@tonic-gate 2040Sstevel@tonic-gate /* 2050Sstevel@tonic-gate * add this hubdi instance to the list of known hubdi's 2060Sstevel@tonic-gate */ 2070Sstevel@tonic-gate usba_init_list(&hubdi->hubdi_list, (usb_opaque_t)hubdi, 2080Sstevel@tonic-gate usba_hcdi_get_hcdi(usba_device->usb_root_hub_dip)-> 2090Sstevel@tonic-gate hcdi_iblock_cookie); 2100Sstevel@tonic-gate mutex_enter(&usba_hubdi_mutex); 2110Sstevel@tonic-gate usba_add_to_list(&usba_hubdi_list, &hubdi->hubdi_list); 2120Sstevel@tonic-gate mutex_exit(&usba_hubdi_mutex); 2130Sstevel@tonic-gate 2140Sstevel@tonic-gate return (DDI_SUCCESS); 2150Sstevel@tonic-gate } 2160Sstevel@tonic-gate 2170Sstevel@tonic-gate 2180Sstevel@tonic-gate /* 2190Sstevel@tonic-gate * Called by an HUB to detach an instance of the driver 2200Sstevel@tonic-gate */ 2210Sstevel@tonic-gate static int 2220Sstevel@tonic-gate usba_hubdi_unregister(dev_info_t *dip) 2230Sstevel@tonic-gate { 2240Sstevel@tonic-gate usba_device_t *usba_device = usba_get_usba_device(dip); 2250Sstevel@tonic-gate usba_hubdi_t *hubdi = usba_device->usb_hubdi; 2260Sstevel@tonic-gate 2270Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle, 2280Sstevel@tonic-gate "usba_hubdi_unregister: %s", ddi_node_name(dip)); 2290Sstevel@tonic-gate 2300Sstevel@tonic-gate mutex_enter(&usba_hubdi_mutex); 2310Sstevel@tonic-gate (void) usba_rm_from_list(&usba_hubdi_list, &hubdi->hubdi_list); 2320Sstevel@tonic-gate mutex_exit(&usba_hubdi_mutex); 2330Sstevel@tonic-gate 2340Sstevel@tonic-gate usba_destroy_list(&hubdi->hubdi_list); 2350Sstevel@tonic-gate 2360Sstevel@tonic-gate kmem_free(hubdi, sizeof (usba_hubdi_t)); 2370Sstevel@tonic-gate 2380Sstevel@tonic-gate return (DDI_SUCCESS); 2390Sstevel@tonic-gate } 2400Sstevel@tonic-gate 2410Sstevel@tonic-gate 2420Sstevel@tonic-gate /* 2430Sstevel@tonic-gate * misc bus routines currently not used 2440Sstevel@tonic-gate */ 2450Sstevel@tonic-gate /*ARGSUSED*/ 2460Sstevel@tonic-gate static int 2470Sstevel@tonic-gate usba_hubdi_map_fault(dev_info_t *dip, 2480Sstevel@tonic-gate dev_info_t *rdip, 2490Sstevel@tonic-gate struct hat *hat, 2500Sstevel@tonic-gate struct seg *seg, 2510Sstevel@tonic-gate caddr_t addr, 2520Sstevel@tonic-gate struct devpage *dp, 2530Sstevel@tonic-gate pfn_t pfn, 2540Sstevel@tonic-gate uint_t prot, 2550Sstevel@tonic-gate uint_t lock) 2560Sstevel@tonic-gate { 2570Sstevel@tonic-gate return (DDI_FAILURE); 2580Sstevel@tonic-gate } 2590Sstevel@tonic-gate 2600Sstevel@tonic-gate 2610Sstevel@tonic-gate /* 2620Sstevel@tonic-gate * root hub support. the root hub uses the same devi as the HCD 2630Sstevel@tonic-gate */ 2640Sstevel@tonic-gate int 2650Sstevel@tonic-gate usba_hubdi_bind_root_hub(dev_info_t *dip, 2660Sstevel@tonic-gate uchar_t *root_hub_config_descriptor, 2670Sstevel@tonic-gate size_t config_length, 2680Sstevel@tonic-gate usb_dev_descr_t *root_hub_device_descriptor) 2690Sstevel@tonic-gate { 2700Sstevel@tonic-gate usba_device_t *usba_device; 2710Sstevel@tonic-gate usba_hcdi_t *hcdi = usba_hcdi_get_hcdi(dip); 2720Sstevel@tonic-gate hubd_t *root_hubd; 2730Sstevel@tonic-gate usb_pipe_handle_t ph = NULL; 2740Sstevel@tonic-gate dev_info_t *child = ddi_get_child(dip); 2750Sstevel@tonic-gate 2760Sstevel@tonic-gate if (ndi_prop_create_boolean(DDI_DEV_T_NONE, dip, 2770Sstevel@tonic-gate "root-hub") != NDI_SUCCESS) { 2780Sstevel@tonic-gate 2790Sstevel@tonic-gate return (USB_FAILURE); 2800Sstevel@tonic-gate } 2810Sstevel@tonic-gate 2820Sstevel@tonic-gate root_hubd = kmem_zalloc(sizeof (hubd_t), KM_SLEEP); 2830Sstevel@tonic-gate 2840Sstevel@tonic-gate /* 2850Sstevel@tonic-gate * create and initialize a usba_device structure 2860Sstevel@tonic-gate */ 2870Sstevel@tonic-gate usba_device = usba_alloc_usba_device(dip); 2880Sstevel@tonic-gate 2890Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex); 2900Sstevel@tonic-gate usba_device->usb_hcdi_ops = hcdi->hcdi_ops; 2910Sstevel@tonic-gate usba_device->usb_cfg = root_hub_config_descriptor; 2920Sstevel@tonic-gate usba_device->usb_cfg_length = config_length; 2930Sstevel@tonic-gate usba_device->usb_dev_descr = root_hub_device_descriptor; 2940Sstevel@tonic-gate usba_device->usb_port = 1; 2950Sstevel@tonic-gate usba_device->usb_addr = ROOT_HUB_ADDR; 2960Sstevel@tonic-gate usba_device->usb_root_hubd = root_hubd; 2970Sstevel@tonic-gate usba_device->usb_cfg_array = kmem_zalloc(sizeof (uchar_t *), 298*4763Slg150142 KM_SLEEP); 2990Sstevel@tonic-gate usba_device->usb_cfg_array_length = sizeof (uchar_t *); 3000Sstevel@tonic-gate 3010Sstevel@tonic-gate usba_device->usb_cfg_array_len = kmem_zalloc(sizeof (uint16_t), 302*4763Slg150142 KM_SLEEP); 3030Sstevel@tonic-gate usba_device->usb_cfg_array_len_length = sizeof (uint16_t); 3040Sstevel@tonic-gate 3050Sstevel@tonic-gate usba_device->usb_cfg_array[0] = root_hub_config_descriptor; 3060Sstevel@tonic-gate usba_device->usb_cfg_array_len[0] = 307*4763Slg150142 sizeof (root_hub_config_descriptor); 3080Sstevel@tonic-gate 3090Sstevel@tonic-gate usba_device->usb_cfg_str_descr = kmem_zalloc(sizeof (uchar_t *), 310*4763Slg150142 KM_SLEEP); 3110Sstevel@tonic-gate usba_device->usb_n_cfgs = 1; 3120Sstevel@tonic-gate usba_device->usb_n_ifs = 1; 3130Sstevel@tonic-gate usba_device->usb_dip = dip; 3140Sstevel@tonic-gate 3150Sstevel@tonic-gate usba_device->usb_client_flags = kmem_zalloc( 3160Sstevel@tonic-gate usba_device->usb_n_ifs * USBA_CLIENT_FLAG_SIZE, KM_SLEEP); 3170Sstevel@tonic-gate 3180Sstevel@tonic-gate usba_device->usb_client_attach_list = kmem_zalloc( 3190Sstevel@tonic-gate usba_device->usb_n_ifs * 3200Sstevel@tonic-gate sizeof (*usba_device->usb_client_attach_list), KM_SLEEP); 3210Sstevel@tonic-gate 3220Sstevel@tonic-gate usba_device->usb_client_ev_cb_list = kmem_zalloc( 3230Sstevel@tonic-gate usba_device->usb_n_ifs * 3240Sstevel@tonic-gate sizeof (*usba_device->usb_client_ev_cb_list), KM_SLEEP); 3250Sstevel@tonic-gate 3260Sstevel@tonic-gate /* 3270Sstevel@tonic-gate * The bDeviceProtocol field of root hub device specifies, 3280Sstevel@tonic-gate * whether root hub is a High or Full speed usb device. 3290Sstevel@tonic-gate */ 3300Sstevel@tonic-gate if (root_hub_device_descriptor->bDeviceProtocol) { 3310Sstevel@tonic-gate usba_device->usb_port_status = USBA_HIGH_SPEED_DEV; 3320Sstevel@tonic-gate } else { 3330Sstevel@tonic-gate usba_device->usb_port_status = USBA_FULL_SPEED_DEV; 3340Sstevel@tonic-gate } 3350Sstevel@tonic-gate 3360Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex); 3370Sstevel@tonic-gate 3380Sstevel@tonic-gate usba_set_usba_device(dip, usba_device); 3390Sstevel@tonic-gate 3400Sstevel@tonic-gate /* 3410Sstevel@tonic-gate * For the root hub the default pipe is not yet open 3420Sstevel@tonic-gate */ 3430Sstevel@tonic-gate if (usb_pipe_open(dip, NULL, NULL, 3440Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph) != USB_SUCCESS) { 3450Sstevel@tonic-gate goto fail; 3460Sstevel@tonic-gate } 3470Sstevel@tonic-gate 3480Sstevel@tonic-gate /* 3490Sstevel@tonic-gate * kill off all OBP children, they may not be fully 3500Sstevel@tonic-gate * enumerated 3510Sstevel@tonic-gate */ 3520Sstevel@tonic-gate while (child) { 3530Sstevel@tonic-gate dev_info_t *next = ddi_get_next_sibling(child); 3540Sstevel@tonic-gate (void) ddi_remove_child(child, 0); 3550Sstevel@tonic-gate child = next; 3560Sstevel@tonic-gate } 3570Sstevel@tonic-gate 3580Sstevel@tonic-gate /* 3590Sstevel@tonic-gate * "attach" the root hub driver 3600Sstevel@tonic-gate */ 3610Sstevel@tonic-gate if (usba_hubdi_attach(dip, DDI_ATTACH) != DDI_SUCCESS) { 3620Sstevel@tonic-gate goto fail; 3630Sstevel@tonic-gate } 3640Sstevel@tonic-gate 3650Sstevel@tonic-gate return (USB_SUCCESS); 3660Sstevel@tonic-gate 3670Sstevel@tonic-gate fail: 3680Sstevel@tonic-gate (void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "root-hub"); 3690Sstevel@tonic-gate 3700Sstevel@tonic-gate if (ph) { 3710Sstevel@tonic-gate usb_pipe_close(dip, ph, 3720Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL); 3730Sstevel@tonic-gate } 3740Sstevel@tonic-gate 3750Sstevel@tonic-gate kmem_free(usba_device->usb_cfg_array, 376*4763Slg150142 usba_device->usb_cfg_array_length); 3770Sstevel@tonic-gate kmem_free(usba_device->usb_cfg_array_len, 378*4763Slg150142 usba_device->usb_cfg_array_len_length); 3790Sstevel@tonic-gate 3800Sstevel@tonic-gate kmem_free(usba_device->usb_cfg_str_descr, sizeof (uchar_t *)); 3810Sstevel@tonic-gate 3820Sstevel@tonic-gate usba_free_usba_device(usba_device); 3830Sstevel@tonic-gate 3840Sstevel@tonic-gate usba_set_usba_device(dip, NULL); 3850Sstevel@tonic-gate if (root_hubd) { 3860Sstevel@tonic-gate kmem_free(root_hubd, sizeof (hubd_t)); 3870Sstevel@tonic-gate } 3880Sstevel@tonic-gate 3890Sstevel@tonic-gate return (USB_FAILURE); 3900Sstevel@tonic-gate } 3910Sstevel@tonic-gate 3920Sstevel@tonic-gate 3930Sstevel@tonic-gate int 3940Sstevel@tonic-gate usba_hubdi_unbind_root_hub(dev_info_t *dip) 3950Sstevel@tonic-gate { 3960Sstevel@tonic-gate usba_device_t *usba_device; 3970Sstevel@tonic-gate 3980Sstevel@tonic-gate /* was root hub attached? */ 3990Sstevel@tonic-gate if (!(usba_is_root_hub(dip))) { 4000Sstevel@tonic-gate 4010Sstevel@tonic-gate /* return success anyway */ 4020Sstevel@tonic-gate return (USB_SUCCESS); 4030Sstevel@tonic-gate } 4040Sstevel@tonic-gate 4050Sstevel@tonic-gate /* 4060Sstevel@tonic-gate * usba_hubdi_detach also closes the default pipe 4070Sstevel@tonic-gate * and removes properties so there is no need to 4080Sstevel@tonic-gate * do it here 4090Sstevel@tonic-gate */ 4100Sstevel@tonic-gate if (usba_hubdi_detach(dip, DDI_DETACH) != DDI_SUCCESS) { 4110Sstevel@tonic-gate 4120Sstevel@tonic-gate if (DEVI_IS_ATTACHING(dip)) { 413978Sfrits USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle, 4140Sstevel@tonic-gate "failure to unbind root hub after attach failure"); 4150Sstevel@tonic-gate } 4160Sstevel@tonic-gate 4170Sstevel@tonic-gate return (USB_FAILURE); 4180Sstevel@tonic-gate } 4190Sstevel@tonic-gate 4200Sstevel@tonic-gate usba_device = usba_get_usba_device(dip); 4210Sstevel@tonic-gate 4220Sstevel@tonic-gate kmem_free(usba_device->usb_root_hubd, sizeof (hubd_t)); 4230Sstevel@tonic-gate 4240Sstevel@tonic-gate kmem_free(usba_device->usb_cfg_array, 425*4763Slg150142 usba_device->usb_cfg_array_length); 4260Sstevel@tonic-gate kmem_free(usba_device->usb_cfg_array_len, 427*4763Slg150142 usba_device->usb_cfg_array_len_length); 4280Sstevel@tonic-gate 4290Sstevel@tonic-gate kmem_free(usba_device->usb_cfg_str_descr, sizeof (uchar_t *)); 4300Sstevel@tonic-gate 4310Sstevel@tonic-gate usba_free_usba_device(usba_device); 4320Sstevel@tonic-gate 4330Sstevel@tonic-gate (void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "root-hub"); 4340Sstevel@tonic-gate 4350Sstevel@tonic-gate return (USB_SUCCESS); 4360Sstevel@tonic-gate } 4370Sstevel@tonic-gate 4380Sstevel@tonic-gate 4390Sstevel@tonic-gate /* 4400Sstevel@tonic-gate * Actual Hub Driver support code: 4410Sstevel@tonic-gate * shared by root hub and non-root hubs 4420Sstevel@tonic-gate */ 4430Sstevel@tonic-gate #include <sys/usb/usba/usbai_version.h> 4440Sstevel@tonic-gate 4450Sstevel@tonic-gate /* Debugging support */ 4460Sstevel@tonic-gate static uint_t hubd_errlevel = USB_LOG_L4; 4470Sstevel@tonic-gate static uint_t hubd_errmask = (uint_t)DPRINT_MASK_ALL; 4480Sstevel@tonic-gate static uint_t hubd_instance_debug = (uint_t)-1; 4490Sstevel@tonic-gate static uint_t hubdi_bus_config_debug = 0; 4500Sstevel@tonic-gate 4510Sstevel@tonic-gate _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_errlevel)) 4520Sstevel@tonic-gate _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_errmask)) 4530Sstevel@tonic-gate _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_instance_debug)) 4540Sstevel@tonic-gate 4550Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique", msgb)) 4560Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique", dev_info)) 4570Sstevel@tonic-gate 4580Sstevel@tonic-gate 4590Sstevel@tonic-gate /* 4600Sstevel@tonic-gate * local variables: 4610Sstevel@tonic-gate * 4620Sstevel@tonic-gate * Amount of time to wait between resetting the port and accessing 4630Sstevel@tonic-gate * the device. The value is in microseconds. 4640Sstevel@tonic-gate */ 4650Sstevel@tonic-gate static uint_t hubd_device_delay = 1000000; 4660Sstevel@tonic-gate 4670Sstevel@tonic-gate /* 4680Sstevel@tonic-gate * enumeration retry 4690Sstevel@tonic-gate */ 4700Sstevel@tonic-gate #define HUBD_PORT_RETRY 5 4710Sstevel@tonic-gate static uint_t hubd_retry_enumerate = HUBD_PORT_RETRY; 4720Sstevel@tonic-gate 4730Sstevel@tonic-gate /* 4740Sstevel@tonic-gate * Stale hotremoved device cleanup delay 4750Sstevel@tonic-gate */ 4760Sstevel@tonic-gate #define HUBD_STALE_DIP_CLEANUP_DELAY 5000000 4770Sstevel@tonic-gate static uint_t hubd_dip_cleanup_delay = HUBD_STALE_DIP_CLEANUP_DELAY; 4780Sstevel@tonic-gate 4790Sstevel@tonic-gate /* 4800Sstevel@tonic-gate * retries for USB suspend and resume 4810Sstevel@tonic-gate */ 4820Sstevel@tonic-gate #define HUBD_SUS_RES_RETRY 2 4830Sstevel@tonic-gate 4840Sstevel@tonic-gate void *hubd_statep; 4850Sstevel@tonic-gate 4860Sstevel@tonic-gate /* 4870Sstevel@tonic-gate * prototypes 4880Sstevel@tonic-gate */ 4890Sstevel@tonic-gate static int hubd_cleanup(dev_info_t *dip, hubd_t *hubd); 4900Sstevel@tonic-gate static int hubd_check_ports(hubd_t *hubd); 4910Sstevel@tonic-gate 4920Sstevel@tonic-gate static void hubd_schedule_cleanup(dev_info_t *); 4930Sstevel@tonic-gate 4940Sstevel@tonic-gate static int hubd_open_intr_pipe(hubd_t *hubd); 4950Sstevel@tonic-gate static void hubd_start_polling(hubd_t *hubd, int always); 4960Sstevel@tonic-gate static void hubd_stop_polling(hubd_t *hubd); 4970Sstevel@tonic-gate static void hubd_close_intr_pipe(hubd_t *hubd); 4980Sstevel@tonic-gate 4990Sstevel@tonic-gate static void hubd_read_cb(usb_pipe_handle_t pipe, usb_intr_req_t *req); 5000Sstevel@tonic-gate static void hubd_exception_cb(usb_pipe_handle_t pipe, 5010Sstevel@tonic-gate usb_intr_req_t *req); 5020Sstevel@tonic-gate static void hubd_hotplug_thread(void *arg); 5030Sstevel@tonic-gate static int hubd_create_child(dev_info_t *dip, 5040Sstevel@tonic-gate hubd_t *hubd, 5050Sstevel@tonic-gate usba_device_t *usba_device, 5060Sstevel@tonic-gate usb_port_status_t port_status, 5070Sstevel@tonic-gate usb_port_t port, 5080Sstevel@tonic-gate int iteration); 5090Sstevel@tonic-gate 5100Sstevel@tonic-gate static int hubd_delete_child(hubd_t *hubd, usb_port_t port, uint_t flag, 5110Sstevel@tonic-gate boolean_t retry); 5120Sstevel@tonic-gate 5130Sstevel@tonic-gate static int hubd_get_hub_descriptor(hubd_t *hubd); 5140Sstevel@tonic-gate 5151001Ssl147100 static int hubd_get_hub_status_words(hubd_t *hubd, uint16_t *status); 5161001Ssl147100 5170Sstevel@tonic-gate static int hubd_reset_port(hubd_t *hubd, usb_port_t port); 5180Sstevel@tonic-gate 5190Sstevel@tonic-gate static int hubd_get_hub_status(hubd_t *hubd); 5200Sstevel@tonic-gate 5210Sstevel@tonic-gate static int hubd_handle_port_connect(hubd_t *hubd, usb_port_t port); 5220Sstevel@tonic-gate 5230Sstevel@tonic-gate static int hubd_disable_port(hubd_t *hubd, usb_port_t port); 5240Sstevel@tonic-gate 5250Sstevel@tonic-gate static int hubd_enable_port(hubd_t *hubd, usb_port_t port); 5260Sstevel@tonic-gate static int hubd_recover_disabled_port(hubd_t *hubd, usb_port_t port); 5270Sstevel@tonic-gate 5280Sstevel@tonic-gate static int hubd_determine_port_status(hubd_t *hubd, usb_port_t port, 5290Sstevel@tonic-gate uint16_t *status, uint16_t *change, uint_t ack_flag); 5300Sstevel@tonic-gate 5310Sstevel@tonic-gate static int hubd_enable_all_port_power(hubd_t *hubd); 5320Sstevel@tonic-gate static int hubd_disable_all_port_power(hubd_t *hubd); 5330Sstevel@tonic-gate static int hubd_disable_port_power(hubd_t *hubd, usb_port_t port); 5340Sstevel@tonic-gate static int hubd_enable_port_power(hubd_t *hubd, usb_port_t port); 5350Sstevel@tonic-gate 5360Sstevel@tonic-gate static void hubd_free_usba_device(hubd_t *hubd, usba_device_t *usba_device); 5370Sstevel@tonic-gate 5380Sstevel@tonic-gate static int hubd_can_suspend(hubd_t *hubd); 5390Sstevel@tonic-gate static void hubd_restore_device_state(dev_info_t *dip, hubd_t *hubd); 5400Sstevel@tonic-gate static int hubd_setdevaddr(hubd_t *hubd, usb_port_t port); 5410Sstevel@tonic-gate static void hubd_setdevconfig(hubd_t *hubd, usb_port_t port); 5420Sstevel@tonic-gate 5430Sstevel@tonic-gate static int hubd_register_events(hubd_t *hubd); 5440Sstevel@tonic-gate static void hubd_do_callback(hubd_t *hubd, dev_info_t *dip, 5450Sstevel@tonic-gate ddi_eventcookie_t cookie); 5460Sstevel@tonic-gate static void hubd_run_callbacks(hubd_t *hubd, usba_event_t type); 5470Sstevel@tonic-gate static void hubd_post_event(hubd_t *hubd, usb_port_t port, usba_event_t type); 5480Sstevel@tonic-gate static void hubd_create_pm_components(dev_info_t *dip, hubd_t *hubd); 5490Sstevel@tonic-gate 5500Sstevel@tonic-gate static int hubd_disconnect_event_cb(dev_info_t *dip); 5510Sstevel@tonic-gate static int hubd_reconnect_event_cb(dev_info_t *dip); 5520Sstevel@tonic-gate static int hubd_pre_suspend_event_cb(dev_info_t *dip); 5530Sstevel@tonic-gate static int hubd_post_resume_event_cb(dev_info_t *dip); 5540Sstevel@tonic-gate static int hubd_cpr_suspend(hubd_t *hubd); 5550Sstevel@tonic-gate static void hubd_cpr_resume(dev_info_t *dip); 5560Sstevel@tonic-gate static int hubd_restore_state_cb(dev_info_t *dip); 5570Sstevel@tonic-gate 5581001Ssl147100 static int hubd_init_power_budget(hubd_t *hubd); 5591001Ssl147100 5600Sstevel@tonic-gate static ndi_event_definition_t hubd_ndi_event_defs[] = { 5610Sstevel@tonic-gate {USBA_EVENT_TAG_HOT_REMOVAL, DDI_DEVI_REMOVE_EVENT, EPL_KERNEL, 5620Sstevel@tonic-gate NDI_EVENT_POST_TO_ALL}, 5630Sstevel@tonic-gate {USBA_EVENT_TAG_HOT_INSERTION, DDI_DEVI_INSERT_EVENT, EPL_KERNEL, 5640Sstevel@tonic-gate NDI_EVENT_POST_TO_ALL}, 5650Sstevel@tonic-gate {USBA_EVENT_TAG_POST_RESUME, USBA_POST_RESUME_EVENT, EPL_KERNEL, 5660Sstevel@tonic-gate NDI_EVENT_POST_TO_ALL}, 5670Sstevel@tonic-gate {USBA_EVENT_TAG_PRE_SUSPEND, USBA_PRE_SUSPEND_EVENT, EPL_KERNEL, 5680Sstevel@tonic-gate NDI_EVENT_POST_TO_ALL} 5690Sstevel@tonic-gate }; 5700Sstevel@tonic-gate 5710Sstevel@tonic-gate #define HUBD_N_NDI_EVENTS \ 5720Sstevel@tonic-gate (sizeof (hubd_ndi_event_defs) / sizeof (ndi_event_definition_t)) 5730Sstevel@tonic-gate 5740Sstevel@tonic-gate static ndi_event_set_t hubd_ndi_events = { 5750Sstevel@tonic-gate NDI_EVENTS_REV1, HUBD_N_NDI_EVENTS, hubd_ndi_event_defs}; 5760Sstevel@tonic-gate 5770Sstevel@tonic-gate /* events received from parent */ 5780Sstevel@tonic-gate static usb_event_t hubd_events = { 5790Sstevel@tonic-gate hubd_disconnect_event_cb, 5800Sstevel@tonic-gate hubd_reconnect_event_cb, 5810Sstevel@tonic-gate hubd_pre_suspend_event_cb, 5820Sstevel@tonic-gate hubd_post_resume_event_cb 5830Sstevel@tonic-gate }; 5840Sstevel@tonic-gate 5850Sstevel@tonic-gate 5860Sstevel@tonic-gate /* 5870Sstevel@tonic-gate * hubd_get_soft_state() returns the hubd soft state 5880Sstevel@tonic-gate */ 5890Sstevel@tonic-gate static hubd_t * 5900Sstevel@tonic-gate hubd_get_soft_state(dev_info_t *dip) 5910Sstevel@tonic-gate { 5920Sstevel@tonic-gate if (dip == NULL) { 5930Sstevel@tonic-gate 5940Sstevel@tonic-gate return (NULL); 5950Sstevel@tonic-gate } 5960Sstevel@tonic-gate 5970Sstevel@tonic-gate if (usba_is_root_hub(dip)) { 5980Sstevel@tonic-gate usba_device_t *usba_device = usba_get_usba_device(dip); 5990Sstevel@tonic-gate 6000Sstevel@tonic-gate return (usba_device->usb_root_hubd); 6010Sstevel@tonic-gate } else { 6020Sstevel@tonic-gate int instance = ddi_get_instance(dip); 6030Sstevel@tonic-gate 6040Sstevel@tonic-gate return (ddi_get_soft_state(hubd_statep, instance)); 6050Sstevel@tonic-gate } 6060Sstevel@tonic-gate } 6070Sstevel@tonic-gate 6080Sstevel@tonic-gate 6090Sstevel@tonic-gate /* 6100Sstevel@tonic-gate * PM support functions: 6110Sstevel@tonic-gate */ 6120Sstevel@tonic-gate /*ARGSUSED*/ 6130Sstevel@tonic-gate static void 6140Sstevel@tonic-gate hubd_pm_busy_component(hubd_t *hubd, dev_info_t *dip, int component) 6150Sstevel@tonic-gate { 6160Sstevel@tonic-gate if (hubd->h_hubpm != NULL) { 6170Sstevel@tonic-gate hubd->h_hubpm->hubp_busy_pm++; 6180Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6190Sstevel@tonic-gate if (pm_busy_component(dip, 0) != DDI_SUCCESS) { 6200Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6210Sstevel@tonic-gate hubd->h_hubpm->hubp_busy_pm--; 6220Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6230Sstevel@tonic-gate } 6240Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6250Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 6260Sstevel@tonic-gate "hubd_pm_busy_component: %d", hubd->h_hubpm->hubp_busy_pm); 6270Sstevel@tonic-gate } 6280Sstevel@tonic-gate } 6290Sstevel@tonic-gate 6300Sstevel@tonic-gate 6310Sstevel@tonic-gate /*ARGSUSED*/ 6320Sstevel@tonic-gate static void 6330Sstevel@tonic-gate hubd_pm_idle_component(hubd_t *hubd, dev_info_t *dip, int component) 6340Sstevel@tonic-gate { 6350Sstevel@tonic-gate if (hubd->h_hubpm != NULL) { 6360Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6370Sstevel@tonic-gate if (pm_idle_component(dip, 0) == DDI_SUCCESS) { 6380Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6390Sstevel@tonic-gate ASSERT(hubd->h_hubpm->hubp_busy_pm > 0); 6400Sstevel@tonic-gate hubd->h_hubpm->hubp_busy_pm--; 6410Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6420Sstevel@tonic-gate } 6430Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6440Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 6450Sstevel@tonic-gate "hubd_pm_idle_component: %d", hubd->h_hubpm->hubp_busy_pm); 6460Sstevel@tonic-gate } 6470Sstevel@tonic-gate } 6480Sstevel@tonic-gate 6490Sstevel@tonic-gate 6500Sstevel@tonic-gate /* 6510Sstevel@tonic-gate * track power level changes for children of this instance 6520Sstevel@tonic-gate */ 6530Sstevel@tonic-gate static void 6540Sstevel@tonic-gate hubd_set_child_pwrlvl(hubd_t *hubd, usb_port_t port, uint8_t power) 6550Sstevel@tonic-gate { 6560Sstevel@tonic-gate int old_power, new_power, pwr; 6570Sstevel@tonic-gate usb_port_t portno; 6580Sstevel@tonic-gate hub_power_t *hubpm; 6590Sstevel@tonic-gate 6600Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 6610Sstevel@tonic-gate "hubd_set_child_pwrlvl: port=%d power=%d", 6620Sstevel@tonic-gate port, power); 6630Sstevel@tonic-gate 6640Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6650Sstevel@tonic-gate hubpm = hubd->h_hubpm; 6660Sstevel@tonic-gate 6670Sstevel@tonic-gate old_power = 0; 6680Sstevel@tonic-gate for (portno = 1; portno <= hubd->h_hub_descr.bNbrPorts; portno++) { 6690Sstevel@tonic-gate old_power += hubpm->hubp_child_pwrstate[portno]; 6700Sstevel@tonic-gate } 6710Sstevel@tonic-gate 6720Sstevel@tonic-gate /* assign the port power */ 6730Sstevel@tonic-gate pwr = hubd->h_hubpm->hubp_child_pwrstate[port]; 6740Sstevel@tonic-gate hubd->h_hubpm->hubp_child_pwrstate[port] = power; 6750Sstevel@tonic-gate new_power = old_power - pwr + power; 6760Sstevel@tonic-gate 6770Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 6780Sstevel@tonic-gate "hubd_set_child_pwrlvl: new_power=%d old_power=%d", 6790Sstevel@tonic-gate new_power, old_power); 6800Sstevel@tonic-gate 6810Sstevel@tonic-gate if ((new_power > 0) && (old_power == 0)) { 6820Sstevel@tonic-gate /* we have the first child coming out of low power */ 6830Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0); 6840Sstevel@tonic-gate } else if ((new_power == 0) && (old_power > 0)) { 6850Sstevel@tonic-gate /* we have the last child going to low power */ 6860Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0); 6870Sstevel@tonic-gate } 6880Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6890Sstevel@tonic-gate } 6900Sstevel@tonic-gate 6910Sstevel@tonic-gate 6920Sstevel@tonic-gate /* 6930Sstevel@tonic-gate * given a child dip, locate its port number 6940Sstevel@tonic-gate */ 6950Sstevel@tonic-gate static usb_port_t 6960Sstevel@tonic-gate hubd_child_dip2port(hubd_t *hubd, dev_info_t *dip) 6970Sstevel@tonic-gate { 6980Sstevel@tonic-gate usb_port_t port; 6990Sstevel@tonic-gate 7000Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 7010Sstevel@tonic-gate for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { 7020Sstevel@tonic-gate if (hubd->h_children_dips[port] == dip) { 7030Sstevel@tonic-gate 7040Sstevel@tonic-gate break; 7050Sstevel@tonic-gate } 7060Sstevel@tonic-gate } 7070Sstevel@tonic-gate ASSERT(port <= hubd->h_hub_descr.bNbrPorts); 7080Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 7090Sstevel@tonic-gate 7100Sstevel@tonic-gate return (port); 7110Sstevel@tonic-gate } 7120Sstevel@tonic-gate 7130Sstevel@tonic-gate 7140Sstevel@tonic-gate /* 7150Sstevel@tonic-gate * if the hub can be put into low power mode, return success 7160Sstevel@tonic-gate * NOTE: suspend here means going to lower power, not CPR suspend. 7170Sstevel@tonic-gate */ 7180Sstevel@tonic-gate static int 7190Sstevel@tonic-gate hubd_can_suspend(hubd_t *hubd) 7200Sstevel@tonic-gate { 7210Sstevel@tonic-gate hub_power_t *hubpm; 7220Sstevel@tonic-gate int total_power = 0; 7230Sstevel@tonic-gate usb_port_t port; 7240Sstevel@tonic-gate 7250Sstevel@tonic-gate hubpm = hubd->h_hubpm; 7260Sstevel@tonic-gate 7270Sstevel@tonic-gate if (DEVI_IS_DETACHING(hubd->h_dip)) { 7280Sstevel@tonic-gate 7290Sstevel@tonic-gate return (USB_SUCCESS); 7300Sstevel@tonic-gate } 7310Sstevel@tonic-gate 7320Sstevel@tonic-gate /* 7330Sstevel@tonic-gate * Don't go to lower power if haven't been at full power for enough 7340Sstevel@tonic-gate * time to let hotplug thread kickoff. 7350Sstevel@tonic-gate */ 7360Sstevel@tonic-gate if (ddi_get_time() < (hubpm->hubp_time_at_full_power + 7370Sstevel@tonic-gate hubpm->hubp_min_pm_threshold)) { 7380Sstevel@tonic-gate 7390Sstevel@tonic-gate return (USB_FAILURE); 7400Sstevel@tonic-gate } 7410Sstevel@tonic-gate 7420Sstevel@tonic-gate for (port = 1; (total_power == 0) && 7430Sstevel@tonic-gate (port <= hubd->h_hub_descr.bNbrPorts); port++) { 7440Sstevel@tonic-gate total_power += hubpm->hubp_child_pwrstate[port]; 7450Sstevel@tonic-gate } 7460Sstevel@tonic-gate 7470Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 748*4763Slg150142 "hubd_can_suspend: %d", total_power); 7490Sstevel@tonic-gate 7500Sstevel@tonic-gate return (total_power ? USB_FAILURE : USB_SUCCESS); 7510Sstevel@tonic-gate } 7520Sstevel@tonic-gate 7530Sstevel@tonic-gate 7540Sstevel@tonic-gate /* 7550Sstevel@tonic-gate * resume port depending on current device state 7560Sstevel@tonic-gate */ 7570Sstevel@tonic-gate static int 7580Sstevel@tonic-gate hubd_resume_port(hubd_t *hubd, usb_port_t port) 7590Sstevel@tonic-gate { 7600Sstevel@tonic-gate int rval, retry; 7610Sstevel@tonic-gate usb_cr_t completion_reason; 7620Sstevel@tonic-gate usb_cb_flags_t cb_flags; 7630Sstevel@tonic-gate uint16_t status; 7640Sstevel@tonic-gate uint16_t change; 7650Sstevel@tonic-gate int retval = USB_FAILURE; 7660Sstevel@tonic-gate 7670Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 7680Sstevel@tonic-gate 7690Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 7700Sstevel@tonic-gate "hubd_resume_port: port=%d state=0x%x (%s)", port, 7710Sstevel@tonic-gate hubd->h_dev_state, usb_str_dev_state(hubd->h_dev_state)); 7720Sstevel@tonic-gate 7730Sstevel@tonic-gate switch (hubd->h_dev_state) { 7740Sstevel@tonic-gate case USB_DEV_HUB_CHILD_PWRLVL: 7750Sstevel@tonic-gate /* 7760Sstevel@tonic-gate * This could be a bus ctl for a port other than the one 7770Sstevel@tonic-gate * that has a remote wakeup condition. So check. 7780Sstevel@tonic-gate */ 7790Sstevel@tonic-gate if ((hubd->h_port_state[port] & PORT_STATUS_PSS) == 0) { 7800Sstevel@tonic-gate /* the port isn't suspended, so don't resume */ 7810Sstevel@tonic-gate retval = USB_SUCCESS; 7820Sstevel@tonic-gate 7830Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 7840Sstevel@tonic-gate "hubd_resume_port: port=%d not suspended", port); 7850Sstevel@tonic-gate 7860Sstevel@tonic-gate break; 7870Sstevel@tonic-gate } 7880Sstevel@tonic-gate /* 7890Sstevel@tonic-gate * Device has initiated a wakeup. 7900Sstevel@tonic-gate * Issue a ClearFeature(PortSuspend) 7910Sstevel@tonic-gate */ 7920Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 7930Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 7940Sstevel@tonic-gate hubd->h_default_pipe, 7951001Ssl147100 HUB_HANDLE_PORT_FEATURE_TYPE, 7960Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 7970Sstevel@tonic-gate CFS_PORT_SUSPEND, 7980Sstevel@tonic-gate port, 7990Sstevel@tonic-gate 0, NULL, 0, 8000Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 8010Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 8020Sstevel@tonic-gate "ClearFeature(PortSuspend) fails " 8030Sstevel@tonic-gate "rval=%d cr=%d cb=0x%x", rval, 8040Sstevel@tonic-gate completion_reason, cb_flags); 8050Sstevel@tonic-gate } 8060Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 8070Sstevel@tonic-gate 8080Sstevel@tonic-gate /* either way ack changes on the port */ 8090Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 810*4763Slg150142 &status, &change, PORT_CHANGE_PSSC); 8110Sstevel@tonic-gate retval = USB_SUCCESS; 8120Sstevel@tonic-gate 8130Sstevel@tonic-gate break; 8140Sstevel@tonic-gate case USB_DEV_HUB_STATE_RECOVER: 8150Sstevel@tonic-gate /* 8160Sstevel@tonic-gate * When hubd's connect event callback posts a connect 8170Sstevel@tonic-gate * event to its child, it results in this busctl call 8180Sstevel@tonic-gate * which is valid 8190Sstevel@tonic-gate */ 8200Sstevel@tonic-gate /* FALLTHRU */ 8210Sstevel@tonic-gate case USB_DEV_ONLINE: 8222651Ssl147100 if (((hubd->h_port_state[port] & PORT_STATUS_CCS) == 0) || 8232651Ssl147100 ((hubd->h_port_state[port] & PORT_STATUS_PSS) == 0)) { 8240Sstevel@tonic-gate /* 8250Sstevel@tonic-gate * the port isn't suspended, or connected 8260Sstevel@tonic-gate * so don't resume 8270Sstevel@tonic-gate */ 8280Sstevel@tonic-gate retval = USB_SUCCESS; 8290Sstevel@tonic-gate 8300Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 8310Sstevel@tonic-gate "hubd_resume_port: port=%d not suspended", port); 8320Sstevel@tonic-gate 8330Sstevel@tonic-gate break; 8340Sstevel@tonic-gate } 8350Sstevel@tonic-gate /* 8360Sstevel@tonic-gate * prevent kicking off the hotplug thread 8370Sstevel@tonic-gate */ 8380Sstevel@tonic-gate hubd->h_hotplug_thread++; 8390Sstevel@tonic-gate hubd_stop_polling(hubd); 8400Sstevel@tonic-gate 8410Sstevel@tonic-gate /* Now ClearFeature(PortSuspend) */ 8420Sstevel@tonic-gate for (retry = 0; retry < HUBD_SUS_RES_RETRY; retry++) { 8430Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 8440Sstevel@tonic-gate rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 8450Sstevel@tonic-gate hubd->h_default_pipe, 8461001Ssl147100 HUB_HANDLE_PORT_FEATURE_TYPE, 8470Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 8480Sstevel@tonic-gate CFS_PORT_SUSPEND, 8490Sstevel@tonic-gate port, 8500Sstevel@tonic-gate 0, NULL, 0, 8510Sstevel@tonic-gate &completion_reason, &cb_flags, 0); 8520Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 8530Sstevel@tonic-gate if (rval != USB_SUCCESS) { 8540Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, 8550Sstevel@tonic-gate hubd->h_log_handle, 8560Sstevel@tonic-gate "ClearFeature(PortSuspend) fails" 8570Sstevel@tonic-gate "rval=%d cr=%d cb=0x%x", rval, 8580Sstevel@tonic-gate completion_reason, cb_flags); 8590Sstevel@tonic-gate } else { 8600Sstevel@tonic-gate /* 8610Sstevel@tonic-gate * As per spec section 11.9 and 7.1.7.7 8620Sstevel@tonic-gate * hub need to provide at least 20ms of 8630Sstevel@tonic-gate * resume signalling, and s/w provide 10ms of 8640Sstevel@tonic-gate * recovery time before accessing the port. 8650Sstevel@tonic-gate */ 8660Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 8670Sstevel@tonic-gate delay(drv_usectohz(40000)); 8680Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 8690Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 8700Sstevel@tonic-gate &status, &change, PORT_CHANGE_PSSC); 8710Sstevel@tonic-gate 8720Sstevel@tonic-gate if ((status & PORT_STATUS_PSS) == 0) { 8730Sstevel@tonic-gate /* the port did finally resume */ 8740Sstevel@tonic-gate retval = USB_SUCCESS; 8750Sstevel@tonic-gate 8760Sstevel@tonic-gate break; 8770Sstevel@tonic-gate } 8780Sstevel@tonic-gate } 8790Sstevel@tonic-gate } 8800Sstevel@tonic-gate 8810Sstevel@tonic-gate /* allow hotplug thread again */ 8820Sstevel@tonic-gate hubd->h_hotplug_thread--; 8830Sstevel@tonic-gate hubd_start_polling(hubd, 0); 8840Sstevel@tonic-gate 8850Sstevel@tonic-gate break; 8860Sstevel@tonic-gate case USB_DEV_DISCONNECTED: 8870Sstevel@tonic-gate /* Ignore - NO Operation */ 8880Sstevel@tonic-gate retval = USB_SUCCESS; 8890Sstevel@tonic-gate 8900Sstevel@tonic-gate break; 8910Sstevel@tonic-gate case USB_DEV_SUSPENDED: 8920Sstevel@tonic-gate case USB_DEV_PWRED_DOWN: 8930Sstevel@tonic-gate default: 8940Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 8950Sstevel@tonic-gate "Improper state for port Resume"); 8960Sstevel@tonic-gate 8970Sstevel@tonic-gate break; 8980Sstevel@tonic-gate } 8990Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 9000Sstevel@tonic-gate 9010Sstevel@tonic-gate return (retval); 9020Sstevel@tonic-gate } 9030Sstevel@tonic-gate 9040Sstevel@tonic-gate 9050Sstevel@tonic-gate /* 9060Sstevel@tonic-gate * suspend port depending on device state 9070Sstevel@tonic-gate */ 9080Sstevel@tonic-gate static int 9090Sstevel@tonic-gate hubd_suspend_port(hubd_t *hubd, usb_port_t port) 9100Sstevel@tonic-gate { 9110Sstevel@tonic-gate int rval, retry; 9120Sstevel@tonic-gate int retval = USB_FAILURE; 9130Sstevel@tonic-gate usb_cr_t completion_reason; 9140Sstevel@tonic-gate usb_cb_flags_t cb_flags; 9150Sstevel@tonic-gate uint16_t status; 9160Sstevel@tonic-gate uint16_t change; 9170Sstevel@tonic-gate 9180Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 9190Sstevel@tonic-gate "hubd_suspend_port: port=%d", port); 9200Sstevel@tonic-gate 9210Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 9220Sstevel@tonic-gate 9230Sstevel@tonic-gate switch (hubd->h_dev_state) { 9240Sstevel@tonic-gate case USB_DEV_HUB_STATE_RECOVER: 9250Sstevel@tonic-gate /* 9260Sstevel@tonic-gate * When hubd's connect event callback posts a connect 9270Sstevel@tonic-gate * event to its child, it results in this busctl call 9280Sstevel@tonic-gate * which is valid 9290Sstevel@tonic-gate */ 9300Sstevel@tonic-gate /* FALLTHRU */ 9310Sstevel@tonic-gate case USB_DEV_HUB_CHILD_PWRLVL: 9320Sstevel@tonic-gate /* 9330Sstevel@tonic-gate * When one child is resuming, the other could timeout 9340Sstevel@tonic-gate * and go to low power mode, which is valid 9350Sstevel@tonic-gate */ 9360Sstevel@tonic-gate /* FALLTHRU */ 9370Sstevel@tonic-gate case USB_DEV_ONLINE: 9380Sstevel@tonic-gate hubd->h_hotplug_thread++; 9390Sstevel@tonic-gate hubd_stop_polling(hubd); 9400Sstevel@tonic-gate 9410Sstevel@tonic-gate /* 9420Sstevel@tonic-gate * Some devices start an unprovoked resume. According to spec, 9430Sstevel@tonic-gate * normal resume time for port is 10ms. Wait for double that 9440Sstevel@tonic-gate * time, then check to be sure port is really suspended. 9450Sstevel@tonic-gate */ 9460Sstevel@tonic-gate for (retry = 0; retry < HUBD_SUS_RES_RETRY; retry++) { 9470Sstevel@tonic-gate /* Now SetFeature(PortSuspend) */ 9480Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 9490Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 9500Sstevel@tonic-gate hubd->h_default_pipe, 9511001Ssl147100 HUB_HANDLE_PORT_FEATURE_TYPE, 9520Sstevel@tonic-gate USB_REQ_SET_FEATURE, 9530Sstevel@tonic-gate CFS_PORT_SUSPEND, 9540Sstevel@tonic-gate port, 9550Sstevel@tonic-gate 0, NULL, 0, 9560Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != 9570Sstevel@tonic-gate USB_SUCCESS) { 9580Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, 9590Sstevel@tonic-gate hubd->h_log_handle, 9600Sstevel@tonic-gate "SetFeature(PortSuspend) fails" 9610Sstevel@tonic-gate "rval=%d cr=%d cb=0x%x", 9620Sstevel@tonic-gate rval, completion_reason, cb_flags); 9630Sstevel@tonic-gate } 9640Sstevel@tonic-gate 9650Sstevel@tonic-gate /* 9660Sstevel@tonic-gate * some devices start an unprovoked resume 9670Sstevel@tonic-gate * wait and check port status after some time 9680Sstevel@tonic-gate */ 9690Sstevel@tonic-gate delay(drv_usectohz(20000)); 9700Sstevel@tonic-gate 9710Sstevel@tonic-gate /* either ways ack changes on the port */ 9720Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 9730Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 9740Sstevel@tonic-gate &status, &change, PORT_CHANGE_PSSC); 9750Sstevel@tonic-gate if (status & PORT_STATUS_PSS) { 9760Sstevel@tonic-gate /* the port is indeed suspended */ 9770Sstevel@tonic-gate retval = USB_SUCCESS; 9780Sstevel@tonic-gate 9790Sstevel@tonic-gate break; 9800Sstevel@tonic-gate } 9810Sstevel@tonic-gate } 9820Sstevel@tonic-gate 9830Sstevel@tonic-gate hubd->h_hotplug_thread--; 9840Sstevel@tonic-gate hubd_start_polling(hubd, 0); 9850Sstevel@tonic-gate 9860Sstevel@tonic-gate break; 9870Sstevel@tonic-gate 9880Sstevel@tonic-gate case USB_DEV_DISCONNECTED: 9890Sstevel@tonic-gate /* Ignore - No Operation */ 9900Sstevel@tonic-gate retval = USB_SUCCESS; 9910Sstevel@tonic-gate 9920Sstevel@tonic-gate break; 9930Sstevel@tonic-gate 9940Sstevel@tonic-gate case USB_DEV_SUSPENDED: 9950Sstevel@tonic-gate case USB_DEV_PWRED_DOWN: 9960Sstevel@tonic-gate default: 9970Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 9980Sstevel@tonic-gate "Improper state for port Suspend"); 9990Sstevel@tonic-gate 10000Sstevel@tonic-gate break; 10010Sstevel@tonic-gate } 10020Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 10030Sstevel@tonic-gate 10040Sstevel@tonic-gate return (retval); 10050Sstevel@tonic-gate } 10060Sstevel@tonic-gate 10070Sstevel@tonic-gate 10080Sstevel@tonic-gate /* 10090Sstevel@tonic-gate * child post attach/detach notifications 10100Sstevel@tonic-gate */ 10110Sstevel@tonic-gate static void 10120Sstevel@tonic-gate hubd_post_attach(hubd_t *hubd, usb_port_t port, struct attachspec *as) 10130Sstevel@tonic-gate { 10140Sstevel@tonic-gate dev_info_t *dip; 10150Sstevel@tonic-gate 10160Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 10170Sstevel@tonic-gate "hubd_post_attach: port=%d result=%d", 10180Sstevel@tonic-gate port, as->result); 10190Sstevel@tonic-gate 10200Sstevel@tonic-gate if (as->result == DDI_SUCCESS) { 10210Sstevel@tonic-gate /* 10220Sstevel@tonic-gate * Check if the child created wants to be power managed. 10230Sstevel@tonic-gate * If yes, the childs power level gets automatically tracked 10240Sstevel@tonic-gate * by DDI_CTLOPS_POWER busctl. 10250Sstevel@tonic-gate * If no, we set power of the new child by default 10260Sstevel@tonic-gate * to USB_DEV_OS_FULL_PWR. Because we should never suspend. 10270Sstevel@tonic-gate */ 10280Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 10290Sstevel@tonic-gate dip = hubd->h_children_dips[port]; 10300Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 10310Sstevel@tonic-gate if (DEVI(dip)->devi_pm_info == NULL) { 10320Sstevel@tonic-gate hubd_set_child_pwrlvl(hubd, port, USB_DEV_OS_FULL_PWR); 10330Sstevel@tonic-gate } 10340Sstevel@tonic-gate } 10350Sstevel@tonic-gate } 10360Sstevel@tonic-gate 10370Sstevel@tonic-gate 10380Sstevel@tonic-gate static void 10390Sstevel@tonic-gate hubd_post_detach(hubd_t *hubd, usb_port_t port, struct detachspec *ds) 10400Sstevel@tonic-gate { 10410Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 10420Sstevel@tonic-gate "hubd_post_detach: port=%d result=%d", port, ds->result); 10430Sstevel@tonic-gate 10440Sstevel@tonic-gate /* 10450Sstevel@tonic-gate * if the device is successfully detached and is the 10460Sstevel@tonic-gate * last device to detach, mark component as idle 10470Sstevel@tonic-gate */ 10480Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 10490Sstevel@tonic-gate if (ds->result == DDI_SUCCESS) { 10500Sstevel@tonic-gate usba_device_t *usba_device = hubd->h_usba_devices[port]; 10511001Ssl147100 dev_info_t *pdip = hubd->h_dip; 10520Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 10530Sstevel@tonic-gate 10541001Ssl147100 usba_hubdi_incr_power_budget(pdip, usba_device); 10551001Ssl147100 10560Sstevel@tonic-gate /* 10570Sstevel@tonic-gate * We set power of the detached child 10580Sstevel@tonic-gate * to 0, so that we can suspend if all 10590Sstevel@tonic-gate * our children are gone 10600Sstevel@tonic-gate */ 10610Sstevel@tonic-gate hubd_set_child_pwrlvl(hubd, port, USB_DEV_OS_PWR_OFF); 10620Sstevel@tonic-gate 10630Sstevel@tonic-gate /* check for leaks on detaching */ 10640Sstevel@tonic-gate if ((usba_device) && (ds->cmd == DDI_DETACH)) { 10650Sstevel@tonic-gate usba_check_for_leaks(usba_device); 10660Sstevel@tonic-gate } 10670Sstevel@tonic-gate } else { 10680Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 10690Sstevel@tonic-gate } 10700Sstevel@tonic-gate } 10710Sstevel@tonic-gate 10720Sstevel@tonic-gate 10730Sstevel@tonic-gate /* 10740Sstevel@tonic-gate * hubd_post_power 10750Sstevel@tonic-gate * After the child's power entry point has been called 10760Sstevel@tonic-gate * we record its power level in our local struct. 10770Sstevel@tonic-gate * If the device has powered off, we suspend port 10780Sstevel@tonic-gate */ 10790Sstevel@tonic-gate static int 10800Sstevel@tonic-gate hubd_post_power(hubd_t *hubd, usb_port_t port, pm_bp_child_pwrchg_t *bpc, 10810Sstevel@tonic-gate int result) 10820Sstevel@tonic-gate { 10830Sstevel@tonic-gate int retval = USB_SUCCESS; 10840Sstevel@tonic-gate 10850Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 10860Sstevel@tonic-gate "hubd_post_power: port=%d", port); 10870Sstevel@tonic-gate 10880Sstevel@tonic-gate if (result == DDI_SUCCESS) { 10890Sstevel@tonic-gate 10900Sstevel@tonic-gate /* record this power in our local struct */ 10910Sstevel@tonic-gate hubd_set_child_pwrlvl(hubd, port, bpc->bpc_nlevel); 10920Sstevel@tonic-gate 10930Sstevel@tonic-gate if (bpc->bpc_nlevel == USB_DEV_OS_PWR_OFF) { 10940Sstevel@tonic-gate 10950Sstevel@tonic-gate /* now suspend the port */ 10960Sstevel@tonic-gate retval = hubd_suspend_port(hubd, port); 10970Sstevel@tonic-gate } else if (bpc->bpc_nlevel == USB_DEV_OS_FULL_PWR) { 10980Sstevel@tonic-gate 10990Sstevel@tonic-gate /* make sure the port is resumed */ 11000Sstevel@tonic-gate retval = hubd_resume_port(hubd, port); 11010Sstevel@tonic-gate } 11020Sstevel@tonic-gate } else { 11030Sstevel@tonic-gate 11040Sstevel@tonic-gate /* record old power in our local struct */ 11050Sstevel@tonic-gate hubd_set_child_pwrlvl(hubd, port, bpc->bpc_olevel); 11060Sstevel@tonic-gate 11070Sstevel@tonic-gate if (bpc->bpc_olevel == USB_DEV_OS_PWR_OFF) { 11080Sstevel@tonic-gate 11090Sstevel@tonic-gate /* 11100Sstevel@tonic-gate * As this device failed to transition from 11110Sstevel@tonic-gate * power off state, suspend the port again 11120Sstevel@tonic-gate */ 11130Sstevel@tonic-gate retval = hubd_suspend_port(hubd, port); 11140Sstevel@tonic-gate } 11150Sstevel@tonic-gate } 11160Sstevel@tonic-gate 11170Sstevel@tonic-gate return (retval); 11180Sstevel@tonic-gate } 11190Sstevel@tonic-gate 11200Sstevel@tonic-gate 11210Sstevel@tonic-gate /* 11220Sstevel@tonic-gate * bus ctl notifications are handled here, the rest goes up to root hub/hcd 11230Sstevel@tonic-gate */ 11240Sstevel@tonic-gate static int 11250Sstevel@tonic-gate usba_hubdi_bus_ctl(dev_info_t *dip, 11260Sstevel@tonic-gate dev_info_t *rdip, 11270Sstevel@tonic-gate ddi_ctl_enum_t op, 11280Sstevel@tonic-gate void *arg, 11290Sstevel@tonic-gate void *result) 11300Sstevel@tonic-gate { 11310Sstevel@tonic-gate usba_device_t *hub_usba_device = usba_get_usba_device(rdip); 11320Sstevel@tonic-gate dev_info_t *root_hub_dip = hub_usba_device->usb_root_hub_dip; 11330Sstevel@tonic-gate struct attachspec *as; 11340Sstevel@tonic-gate struct detachspec *ds; 11350Sstevel@tonic-gate hubd_t *hubd; 11360Sstevel@tonic-gate usb_port_t port; 11370Sstevel@tonic-gate int circ, rval; 11380Sstevel@tonic-gate int retval = DDI_FAILURE; 11390Sstevel@tonic-gate 11400Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 11410Sstevel@tonic-gate 11420Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 11430Sstevel@tonic-gate 11440Sstevel@tonic-gate /* flag that we are currently running bus_ctl */ 11450Sstevel@tonic-gate hubd->h_bus_ctls++; 11460Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 11470Sstevel@tonic-gate 11480Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle, 11490Sstevel@tonic-gate "usba_hubdi_bus_ctl:\n\t" 11500Sstevel@tonic-gate "dip=0x%p, rdip=0x%p, op=0x%x, arg=0x%p", 11510Sstevel@tonic-gate dip, rdip, op, arg); 11520Sstevel@tonic-gate 11530Sstevel@tonic-gate switch (op) { 11540Sstevel@tonic-gate case DDI_CTLOPS_ATTACH: 11550Sstevel@tonic-gate as = (struct attachspec *)arg; 11560Sstevel@tonic-gate port = hubd_child_dip2port(hubd, rdip); 11570Sstevel@tonic-gate 11580Sstevel@tonic-gate /* there is nothing to do at resume time */ 11590Sstevel@tonic-gate if (as->cmd == DDI_RESUME) { 11600Sstevel@tonic-gate break; 11610Sstevel@tonic-gate } 11620Sstevel@tonic-gate 11630Sstevel@tonic-gate /* serialize access */ 11640Sstevel@tonic-gate ndi_devi_enter(hubd->h_dip, &circ); 11650Sstevel@tonic-gate 11660Sstevel@tonic-gate switch (as->when) { 11670Sstevel@tonic-gate case DDI_PRE: 11680Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 11690Sstevel@tonic-gate "DDI_PRE DDI_CTLOPS_ATTACH: dip=%p, port=%d", 11700Sstevel@tonic-gate rdip, port); 11710Sstevel@tonic-gate 11720Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 11730Sstevel@tonic-gate hubd->h_port_state[port] |= HUBD_CHILD_ATTACHING; 11740Sstevel@tonic-gate 11750Sstevel@tonic-gate /* Go busy here. Matching idle is DDI_POST case. */ 11760Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, dip, 0); 11770Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 11780Sstevel@tonic-gate 11790Sstevel@tonic-gate /* 11800Sstevel@tonic-gate * if we suspended the port previously 11810Sstevel@tonic-gate * because child went to low power state, and 11820Sstevel@tonic-gate * someone unloaded the driver, the port would 11830Sstevel@tonic-gate * still be suspended and needs to be resumed 11840Sstevel@tonic-gate */ 11850Sstevel@tonic-gate rval = hubd_resume_port(hubd, port); 11860Sstevel@tonic-gate if (rval == USB_SUCCESS) { 11870Sstevel@tonic-gate retval = DDI_SUCCESS; 11880Sstevel@tonic-gate } 11890Sstevel@tonic-gate 11900Sstevel@tonic-gate break; 11910Sstevel@tonic-gate case DDI_POST: 11920Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 11930Sstevel@tonic-gate "DDI_POST DDI_CTLOPS_ATTACH: dip=%p, port=%d", 11940Sstevel@tonic-gate rdip, port); 11950Sstevel@tonic-gate 11960Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 11970Sstevel@tonic-gate hubd->h_port_state[port] &= ~HUBD_CHILD_ATTACHING; 11980Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 11990Sstevel@tonic-gate 12000Sstevel@tonic-gate hubd_post_attach(hubd, port, (struct attachspec *)arg); 12010Sstevel@tonic-gate retval = DDI_SUCCESS; 12020Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 12030Sstevel@tonic-gate 12040Sstevel@tonic-gate /* Matching idle call for DDI_PRE busy call. */ 12050Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, dip, 0); 12060Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 12070Sstevel@tonic-gate } 12080Sstevel@tonic-gate ndi_devi_exit(hubd->h_dip, circ); 12090Sstevel@tonic-gate 12100Sstevel@tonic-gate break; 12110Sstevel@tonic-gate case DDI_CTLOPS_DETACH: 12120Sstevel@tonic-gate ds = (struct detachspec *)arg; 12130Sstevel@tonic-gate port = hubd_child_dip2port(hubd, rdip); 12140Sstevel@tonic-gate 12150Sstevel@tonic-gate /* there is nothing to do at suspend time */ 12160Sstevel@tonic-gate if (ds->cmd == DDI_SUSPEND) { 12170Sstevel@tonic-gate break; 12180Sstevel@tonic-gate } 12190Sstevel@tonic-gate 12200Sstevel@tonic-gate /* serialize access */ 12210Sstevel@tonic-gate ndi_devi_enter(hubd->h_dip, &circ); 12220Sstevel@tonic-gate 12230Sstevel@tonic-gate switch (ds->when) { 12240Sstevel@tonic-gate case DDI_PRE: 12250Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 12260Sstevel@tonic-gate "DDI_PRE DDI_CTLOPS_DETACH: dip=%p port=%d", 12270Sstevel@tonic-gate rdip, port); 12280Sstevel@tonic-gate 12290Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 12300Sstevel@tonic-gate hubd->h_port_state[port] |= HUBD_CHILD_DETACHING; 12310Sstevel@tonic-gate 12320Sstevel@tonic-gate /* Go busy here. Matching idle is DDI_POST case. */ 12330Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, dip, 0); 12340Sstevel@tonic-gate 12350Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 12360Sstevel@tonic-gate retval = DDI_SUCCESS; 12370Sstevel@tonic-gate 12380Sstevel@tonic-gate break; 12390Sstevel@tonic-gate case DDI_POST: 12400Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 12410Sstevel@tonic-gate "DDI_POST DDI_CTLOPS_DETACH: dip=%p port=%d", 12420Sstevel@tonic-gate rdip, port); 12430Sstevel@tonic-gate 12440Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 12450Sstevel@tonic-gate hubd->h_port_state[port] &= ~HUBD_CHILD_DETACHING; 12460Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 12470Sstevel@tonic-gate 12480Sstevel@tonic-gate /* Matching idle call for DDI_PRE busy call. */ 12490Sstevel@tonic-gate hubd_post_detach(hubd, port, (struct detachspec *)arg); 12500Sstevel@tonic-gate retval = DDI_SUCCESS; 12510Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 12520Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, dip, 0); 12530Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 12540Sstevel@tonic-gate 12550Sstevel@tonic-gate break; 12560Sstevel@tonic-gate } 12570Sstevel@tonic-gate ndi_devi_exit(hubd->h_dip, circ); 12580Sstevel@tonic-gate 12590Sstevel@tonic-gate break; 12600Sstevel@tonic-gate default: 12610Sstevel@tonic-gate retval = usba_bus_ctl(root_hub_dip, rdip, op, arg, result); 12620Sstevel@tonic-gate } 12630Sstevel@tonic-gate 12640Sstevel@tonic-gate /* decrement bus_ctls count */ 12650Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 12660Sstevel@tonic-gate hubd->h_bus_ctls--; 12670Sstevel@tonic-gate ASSERT(hubd->h_bus_ctls >= 0); 12680Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 12690Sstevel@tonic-gate 12700Sstevel@tonic-gate return (retval); 12710Sstevel@tonic-gate } 12720Sstevel@tonic-gate 12730Sstevel@tonic-gate 12740Sstevel@tonic-gate /* 12750Sstevel@tonic-gate * bus enumeration entry points 12760Sstevel@tonic-gate */ 12770Sstevel@tonic-gate static int 12780Sstevel@tonic-gate hubd_bus_config(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op, 12790Sstevel@tonic-gate void *arg, dev_info_t **child) 12800Sstevel@tonic-gate { 12810Sstevel@tonic-gate extern int modrootloaded; 12820Sstevel@tonic-gate 12830Sstevel@tonic-gate hubd_t *hubd = hubd_get_soft_state(dip); 12840Sstevel@tonic-gate int rval, circ; 12850Sstevel@tonic-gate 12860Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 12870Sstevel@tonic-gate "hubd_bus_config: op=%d", op); 12880Sstevel@tonic-gate 12890Sstevel@tonic-gate if (hubdi_bus_config_debug) { 12900Sstevel@tonic-gate flag |= NDI_DEVI_DEBUG; 12910Sstevel@tonic-gate } 12920Sstevel@tonic-gate 12930Sstevel@tonic-gate /* 12940Sstevel@tonic-gate * there must be a smarter way to do this but for 12950Sstevel@tonic-gate * now, a hack for booting USB storage. 12960Sstevel@tonic-gate */ 12970Sstevel@tonic-gate if (!modrootloaded) { 12980Sstevel@tonic-gate delay(drv_usectohz(1000000)); 12990Sstevel@tonic-gate } 13000Sstevel@tonic-gate ndi_devi_enter(hubd->h_dip, &circ); 13010Sstevel@tonic-gate rval = ndi_busop_bus_config(dip, flag, op, arg, child, 0); 13020Sstevel@tonic-gate ndi_devi_exit(hubd->h_dip, circ); 13030Sstevel@tonic-gate 13040Sstevel@tonic-gate return (rval); 13050Sstevel@tonic-gate } 13060Sstevel@tonic-gate 13070Sstevel@tonic-gate 13080Sstevel@tonic-gate static int 13090Sstevel@tonic-gate hubd_bus_unconfig(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op, 13100Sstevel@tonic-gate void *arg) 13110Sstevel@tonic-gate { 13120Sstevel@tonic-gate hubd_t *hubd = hubd_get_soft_state(dip); 13130Sstevel@tonic-gate dev_info_t *cdip; 13140Sstevel@tonic-gate usb_port_t port; 13150Sstevel@tonic-gate int circ; 13160Sstevel@tonic-gate int rval; 13170Sstevel@tonic-gate 13180Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 13190Sstevel@tonic-gate "hubd_bus_unconfig: op=%d", op); 13200Sstevel@tonic-gate 13210Sstevel@tonic-gate if (hubdi_bus_config_debug) { 13220Sstevel@tonic-gate flag |= NDI_DEVI_DEBUG; 13230Sstevel@tonic-gate } 13240Sstevel@tonic-gate 13250Sstevel@tonic-gate if ((op == BUS_UNCONFIG_ALL) && (flag & NDI_AUTODETACH) == 0) { 13260Sstevel@tonic-gate flag |= NDI_DEVI_REMOVE; 13270Sstevel@tonic-gate } 13280Sstevel@tonic-gate 13290Sstevel@tonic-gate /* serialize access */ 13300Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 13310Sstevel@tonic-gate 13320Sstevel@tonic-gate rval = ndi_busop_bus_unconfig(dip, flag, op, arg); 13330Sstevel@tonic-gate 13340Sstevel@tonic-gate /* logically zap children's list */ 13350Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 13360Sstevel@tonic-gate for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { 13370Sstevel@tonic-gate hubd->h_port_state[port] |= HUBD_CHILD_ZAP; 13380Sstevel@tonic-gate } 13390Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 13400Sstevel@tonic-gate 13410Sstevel@tonic-gate /* fill in what's left */ 13420Sstevel@tonic-gate for (cdip = ddi_get_child(dip); cdip; 13430Sstevel@tonic-gate cdip = ddi_get_next_sibling(cdip)) { 13440Sstevel@tonic-gate usba_device_t *usba_device = usba_get_usba_device(cdip); 13450Sstevel@tonic-gate 13460Sstevel@tonic-gate if (usba_device == NULL) { 13470Sstevel@tonic-gate 13480Sstevel@tonic-gate continue; 13490Sstevel@tonic-gate } 13500Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 13510Sstevel@tonic-gate port = usba_device->usb_port; 13520Sstevel@tonic-gate hubd->h_children_dips[port] = cdip; 13530Sstevel@tonic-gate hubd->h_port_state[port] &= ~HUBD_CHILD_ZAP; 13540Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 13550Sstevel@tonic-gate } 13560Sstevel@tonic-gate 13570Sstevel@tonic-gate /* physically zap the children we didn't find */ 13580Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 13590Sstevel@tonic-gate for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { 1360*4763Slg150142 if (hubd->h_port_state[port] & HUBD_CHILD_ZAP) { 13610Sstevel@tonic-gate /* zap the dip and usba_device structure as well */ 13620Sstevel@tonic-gate hubd_free_usba_device(hubd, hubd->h_usba_devices[port]); 13630Sstevel@tonic-gate hubd->h_children_dips[port] = NULL; 13640Sstevel@tonic-gate hubd->h_port_state[port] &= ~HUBD_CHILD_ZAP; 13650Sstevel@tonic-gate } 13660Sstevel@tonic-gate } 13670Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 13680Sstevel@tonic-gate 13690Sstevel@tonic-gate ndi_devi_exit(dip, circ); 13700Sstevel@tonic-gate 13710Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 13720Sstevel@tonic-gate "hubd_bus_unconfig: rval=%d", rval); 13730Sstevel@tonic-gate 13740Sstevel@tonic-gate return (rval); 13750Sstevel@tonic-gate } 13760Sstevel@tonic-gate 13770Sstevel@tonic-gate 13780Sstevel@tonic-gate /* bus_power entry point */ 13790Sstevel@tonic-gate static int 13800Sstevel@tonic-gate hubd_bus_power(dev_info_t *dip, void *impl_arg, pm_bus_power_op_t op, 13810Sstevel@tonic-gate void *arg, void *result) 13820Sstevel@tonic-gate { 13830Sstevel@tonic-gate hubd_t *hubd; 13840Sstevel@tonic-gate int rval, pwrup_res; 13850Sstevel@tonic-gate usb_port_t port; 13860Sstevel@tonic-gate int retval = DDI_FAILURE; 13870Sstevel@tonic-gate pm_bp_child_pwrchg_t *bpc; 13880Sstevel@tonic-gate pm_bp_nexus_pwrup_t bpn; 13890Sstevel@tonic-gate 13900Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 13910Sstevel@tonic-gate 13920Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubd->h_log_handle, 13930Sstevel@tonic-gate "hubd_bus_power: dip=%p, impl_arg=%p, power_op=%d, arg=%p, " 13940Sstevel@tonic-gate "result=%d\n", dip, impl_arg, op, arg, *(int *)result); 13950Sstevel@tonic-gate 13960Sstevel@tonic-gate bpc = (pm_bp_child_pwrchg_t *)arg; 13970Sstevel@tonic-gate 13980Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 13990Sstevel@tonic-gate hubd->h_bus_pwr++; 14000Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 14010Sstevel@tonic-gate 14020Sstevel@tonic-gate switch (op) { 14030Sstevel@tonic-gate case BUS_POWER_PRE_NOTIFICATION: 14040Sstevel@tonic-gate port = hubd_child_dip2port(hubd, bpc->bpc_dip); 14050Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle, 14060Sstevel@tonic-gate "hubd_bus_power: BUS_POWER_PRE_NOTIFICATION, port=%d", 14070Sstevel@tonic-gate port); 14080Sstevel@tonic-gate 14090Sstevel@tonic-gate /* go to full power if we are powered down */ 14100Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 14110Sstevel@tonic-gate 14120Sstevel@tonic-gate /* 14130Sstevel@tonic-gate * If this case completes normally, idle will be in 14140Sstevel@tonic-gate * hubd_bus_power / BUS_POWER_POST_NOTIFICATION 14150Sstevel@tonic-gate */ 14160Sstevel@tonic-gate hubd_pm_busy_component(hubd, dip, 0); 14170Sstevel@tonic-gate 14180Sstevel@tonic-gate /* 14190Sstevel@tonic-gate * raise power only if we have created the components 14200Sstevel@tonic-gate * and are currently in low power 14210Sstevel@tonic-gate */ 14220Sstevel@tonic-gate if ((hubd->h_dev_state == USB_DEV_PWRED_DOWN) && 14230Sstevel@tonic-gate hubd->h_hubpm->hubp_wakeup_enabled) { 14240Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 14250Sstevel@tonic-gate 14260Sstevel@tonic-gate bpn.bpn_comp = 0; 14270Sstevel@tonic-gate bpn.bpn_dip = dip; 14280Sstevel@tonic-gate bpn.bpn_level = USB_DEV_OS_FULL_PWR; 14290Sstevel@tonic-gate bpn.bpn_private = bpc->bpc_private; 14300Sstevel@tonic-gate 14310Sstevel@tonic-gate rval = pm_busop_bus_power(dip, impl_arg, 14320Sstevel@tonic-gate BUS_POWER_NEXUS_PWRUP, (void *)&bpn, 14330Sstevel@tonic-gate (void *)&pwrup_res); 14340Sstevel@tonic-gate 14350Sstevel@tonic-gate if (rval != DDI_SUCCESS || pwrup_res != DDI_SUCCESS) { 14360Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 14370Sstevel@tonic-gate hubd_pm_idle_component(hubd, dip, 0); 14380Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 14390Sstevel@tonic-gate 14400Sstevel@tonic-gate break; 14410Sstevel@tonic-gate } 14420Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 14430Sstevel@tonic-gate } 14440Sstevel@tonic-gate 14450Sstevel@tonic-gate /* indicate that child is changing power level */ 14460Sstevel@tonic-gate hubd->h_port_state[port] |= HUBD_CHILD_PWRLVL_CHNG; 14470Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 14480Sstevel@tonic-gate 14490Sstevel@tonic-gate if ((bpc->bpc_olevel == 0) && 14500Sstevel@tonic-gate (bpc->bpc_nlevel > bpc->bpc_olevel)) { 14510Sstevel@tonic-gate /* 14520Sstevel@tonic-gate * this child is transitioning from power off 14530Sstevel@tonic-gate * to power on state - resume port 14540Sstevel@tonic-gate */ 14550Sstevel@tonic-gate rval = hubd_resume_port(hubd, port); 14560Sstevel@tonic-gate if (rval == USB_SUCCESS) { 14570Sstevel@tonic-gate retval = DDI_SUCCESS; 14580Sstevel@tonic-gate } else { 14590Sstevel@tonic-gate /* reset this flag on failure */ 14600Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 14610Sstevel@tonic-gate hubd->h_port_state[port] &= 14620Sstevel@tonic-gate ~HUBD_CHILD_PWRLVL_CHNG; 14630Sstevel@tonic-gate hubd_pm_idle_component(hubd, dip, 0); 14640Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 14650Sstevel@tonic-gate } 14660Sstevel@tonic-gate } else { 14670Sstevel@tonic-gate retval = DDI_SUCCESS; 14680Sstevel@tonic-gate } 14690Sstevel@tonic-gate 14700Sstevel@tonic-gate break; 14710Sstevel@tonic-gate case BUS_POWER_POST_NOTIFICATION: 14720Sstevel@tonic-gate port = hubd_child_dip2port(hubd, bpc->bpc_dip); 14730Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle, 14740Sstevel@tonic-gate "hubd_bus_power: BUS_POWER_POST_NOTIFICATION, port=%d", 14750Sstevel@tonic-gate port); 14760Sstevel@tonic-gate 14770Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 14780Sstevel@tonic-gate hubd->h_port_state[port] &= ~HUBD_CHILD_PWRLVL_CHNG; 14790Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 14800Sstevel@tonic-gate 14810Sstevel@tonic-gate /* record child's pwr and suspend port if required */ 14820Sstevel@tonic-gate rval = hubd_post_power(hubd, port, bpc, *(int *)result); 14830Sstevel@tonic-gate if (rval == USB_SUCCESS) { 14840Sstevel@tonic-gate 14850Sstevel@tonic-gate retval = DDI_SUCCESS; 14860Sstevel@tonic-gate } 14870Sstevel@tonic-gate 14880Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 14890Sstevel@tonic-gate 14900Sstevel@tonic-gate /* 14910Sstevel@tonic-gate * Matching idle for the busy in 14920Sstevel@tonic-gate * hubd_bus_power / BUS_POWER_PRE_NOTIFICATION 14930Sstevel@tonic-gate */ 14940Sstevel@tonic-gate hubd_pm_idle_component(hubd, dip, 0); 14950Sstevel@tonic-gate 14960Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 14970Sstevel@tonic-gate 14980Sstevel@tonic-gate break; 14990Sstevel@tonic-gate default: 15000Sstevel@tonic-gate retval = pm_busop_bus_power(dip, impl_arg, op, arg, result); 15010Sstevel@tonic-gate 15020Sstevel@tonic-gate break; 15030Sstevel@tonic-gate } 15040Sstevel@tonic-gate 15050Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 15060Sstevel@tonic-gate hubd->h_bus_pwr--; 15070Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 15080Sstevel@tonic-gate 15090Sstevel@tonic-gate return (retval); 15100Sstevel@tonic-gate } 15110Sstevel@tonic-gate 15120Sstevel@tonic-gate 15130Sstevel@tonic-gate /* 15140Sstevel@tonic-gate * functions to handle power transition for OS levels 0 -> 3 15150Sstevel@tonic-gate */ 15160Sstevel@tonic-gate static int 15170Sstevel@tonic-gate hubd_pwrlvl0(hubd_t *hubd) 15180Sstevel@tonic-gate { 15190Sstevel@tonic-gate hub_power_t *hubpm; 15200Sstevel@tonic-gate 15210Sstevel@tonic-gate /* We can't power down if hotplug thread is running */ 15220Sstevel@tonic-gate if (hubd->h_hotplug_thread || hubd->h_hubpm->hubp_busy_pm || 15230Sstevel@tonic-gate (hubd_can_suspend(hubd) == USB_FAILURE)) { 15240Sstevel@tonic-gate 15250Sstevel@tonic-gate return (USB_FAILURE); 15260Sstevel@tonic-gate } 15270Sstevel@tonic-gate 15280Sstevel@tonic-gate switch (hubd->h_dev_state) { 15290Sstevel@tonic-gate case USB_DEV_ONLINE: 15300Sstevel@tonic-gate hubpm = hubd->h_hubpm; 15310Sstevel@tonic-gate 15320Sstevel@tonic-gate /* 15330Sstevel@tonic-gate * To avoid race with bus_power pre_notify on check over 15340Sstevel@tonic-gate * dev_state, we need to correctly set the dev state 15350Sstevel@tonic-gate * before the mutex is dropped in stop polling. 15360Sstevel@tonic-gate */ 15370Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_PWRED_DOWN; 15380Sstevel@tonic-gate hubpm->hubp_current_power = USB_DEV_OS_PWR_OFF; 15390Sstevel@tonic-gate 15400Sstevel@tonic-gate /* 15410Sstevel@tonic-gate * if we are the root hub, do not stop polling 15420Sstevel@tonic-gate * otherwise, we will never see a resume 15430Sstevel@tonic-gate */ 15440Sstevel@tonic-gate if (usba_is_root_hub(hubd->h_dip)) { 15450Sstevel@tonic-gate /* place holder to implement Global Suspend */ 15460Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 15470Sstevel@tonic-gate "Global Suspend: Not Yet Implemented"); 15480Sstevel@tonic-gate } else { 15490Sstevel@tonic-gate hubd_stop_polling(hubd); 15500Sstevel@tonic-gate } 15510Sstevel@tonic-gate 15520Sstevel@tonic-gate /* Issue USB D3 command to the device here */ 15530Sstevel@tonic-gate (void) usb_set_device_pwrlvl3(hubd->h_dip); 15540Sstevel@tonic-gate 15550Sstevel@tonic-gate break; 15560Sstevel@tonic-gate case USB_DEV_DISCONNECTED: 15570Sstevel@tonic-gate case USB_DEV_SUSPENDED: 15580Sstevel@tonic-gate case USB_DEV_PWRED_DOWN: 15590Sstevel@tonic-gate default: 15600Sstevel@tonic-gate 15610Sstevel@tonic-gate break; 15620Sstevel@tonic-gate } 15630Sstevel@tonic-gate 15640Sstevel@tonic-gate return (USB_SUCCESS); 15650Sstevel@tonic-gate } 15660Sstevel@tonic-gate 15670Sstevel@tonic-gate 15680Sstevel@tonic-gate /* ARGSUSED */ 15690Sstevel@tonic-gate static int 15700Sstevel@tonic-gate hubd_pwrlvl1(hubd_t *hubd) 15710Sstevel@tonic-gate { 15720Sstevel@tonic-gate /* Issue USB D2 command to the device here */ 15730Sstevel@tonic-gate (void) usb_set_device_pwrlvl2(hubd->h_dip); 15740Sstevel@tonic-gate 15750Sstevel@tonic-gate return (USB_FAILURE); 15760Sstevel@tonic-gate } 15770Sstevel@tonic-gate 15780Sstevel@tonic-gate 15790Sstevel@tonic-gate /* ARGSUSED */ 15800Sstevel@tonic-gate static int 15810Sstevel@tonic-gate hubd_pwrlvl2(hubd_t *hubd) 15820Sstevel@tonic-gate { 15830Sstevel@tonic-gate /* Issue USB D1 command to the device here */ 15840Sstevel@tonic-gate (void) usb_set_device_pwrlvl1(hubd->h_dip); 15850Sstevel@tonic-gate 15860Sstevel@tonic-gate return (USB_FAILURE); 15870Sstevel@tonic-gate } 15880Sstevel@tonic-gate 15890Sstevel@tonic-gate 15900Sstevel@tonic-gate static int 15910Sstevel@tonic-gate hubd_pwrlvl3(hubd_t *hubd) 15920Sstevel@tonic-gate { 15930Sstevel@tonic-gate hub_power_t *hubpm; 15940Sstevel@tonic-gate int rval; 15950Sstevel@tonic-gate 15960Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, "hubd_pwrlvl3"); 15970Sstevel@tonic-gate 15980Sstevel@tonic-gate hubpm = hubd->h_hubpm; 15990Sstevel@tonic-gate switch (hubd->h_dev_state) { 16000Sstevel@tonic-gate case USB_DEV_PWRED_DOWN: 16010Sstevel@tonic-gate ASSERT(hubpm->hubp_current_power == USB_DEV_OS_PWR_OFF); 16020Sstevel@tonic-gate if (usba_is_root_hub(hubd->h_dip)) { 16030Sstevel@tonic-gate /* implement global resume here */ 16040Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, 16050Sstevel@tonic-gate hubd->h_log_handle, 16060Sstevel@tonic-gate "Global Resume: Not Yet Implemented"); 16070Sstevel@tonic-gate } 16080Sstevel@tonic-gate /* Issue USB D0 command to the device here */ 16090Sstevel@tonic-gate rval = usb_set_device_pwrlvl0(hubd->h_dip); 16100Sstevel@tonic-gate ASSERT(rval == USB_SUCCESS); 16110Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_ONLINE; 16120Sstevel@tonic-gate hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR; 16130Sstevel@tonic-gate hubpm->hubp_time_at_full_power = ddi_get_time(); 16140Sstevel@tonic-gate hubd_start_polling(hubd, 0); 16150Sstevel@tonic-gate 16160Sstevel@tonic-gate /* FALLTHRU */ 16170Sstevel@tonic-gate case USB_DEV_ONLINE: 16180Sstevel@tonic-gate /* we are already in full power */ 16190Sstevel@tonic-gate 16200Sstevel@tonic-gate /* FALLTHRU */ 16210Sstevel@tonic-gate case USB_DEV_DISCONNECTED: 16220Sstevel@tonic-gate case USB_DEV_SUSPENDED: 16230Sstevel@tonic-gate /* 16240Sstevel@tonic-gate * PM framework tries to put you in full power 16250Sstevel@tonic-gate * during system shutdown. If we are disconnected 16260Sstevel@tonic-gate * return success. Also, we should not change state 16270Sstevel@tonic-gate * when we are disconnected or suspended or about to 16280Sstevel@tonic-gate * transition to that state 16290Sstevel@tonic-gate */ 16300Sstevel@tonic-gate 16310Sstevel@tonic-gate return (USB_SUCCESS); 16320Sstevel@tonic-gate default: 16330Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 16340Sstevel@tonic-gate "hubd_pwrlvl3: Illegal dev_state=%d", hubd->h_dev_state); 16350Sstevel@tonic-gate 16360Sstevel@tonic-gate return (USB_FAILURE); 16370Sstevel@tonic-gate } 16380Sstevel@tonic-gate } 16390Sstevel@tonic-gate 16400Sstevel@tonic-gate 16410Sstevel@tonic-gate /* power entry point */ 16420Sstevel@tonic-gate /* ARGSUSED */ 16430Sstevel@tonic-gate int 16440Sstevel@tonic-gate usba_hubdi_power(dev_info_t *dip, int comp, int level) 16450Sstevel@tonic-gate { 16460Sstevel@tonic-gate hubd_t *hubd; 16470Sstevel@tonic-gate hub_power_t *hubpm; 16480Sstevel@tonic-gate int retval; 16490Sstevel@tonic-gate int circ; 16500Sstevel@tonic-gate 16510Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 16520Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle, 16530Sstevel@tonic-gate "usba_hubdi_power: level=%d", level); 16540Sstevel@tonic-gate 16550Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 16560Sstevel@tonic-gate 16570Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 16580Sstevel@tonic-gate hubpm = hubd->h_hubpm; 16590Sstevel@tonic-gate 16600Sstevel@tonic-gate /* check if we are transitioning to a legal power level */ 16610Sstevel@tonic-gate if (USB_DEV_PWRSTATE_OK(hubpm->hubp_pwr_states, level)) { 16620Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HUBDI, hubd->h_log_handle, 16630Sstevel@tonic-gate "usba_hubdi_power: illegal power level=%d " 16640Sstevel@tonic-gate "hubp_pwr_states=0x%x", level, hubpm->hubp_pwr_states); 16650Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 16660Sstevel@tonic-gate 16670Sstevel@tonic-gate ndi_devi_exit(dip, circ); 16680Sstevel@tonic-gate 16690Sstevel@tonic-gate return (DDI_FAILURE); 16700Sstevel@tonic-gate } 16710Sstevel@tonic-gate 16720Sstevel@tonic-gate switch (level) { 16730Sstevel@tonic-gate case USB_DEV_OS_PWR_OFF: 16740Sstevel@tonic-gate retval = hubd_pwrlvl0(hubd); 16750Sstevel@tonic-gate 16760Sstevel@tonic-gate break; 16770Sstevel@tonic-gate case USB_DEV_OS_PWR_1: 16780Sstevel@tonic-gate retval = hubd_pwrlvl1(hubd); 16790Sstevel@tonic-gate 16800Sstevel@tonic-gate break; 16810Sstevel@tonic-gate case USB_DEV_OS_PWR_2: 16820Sstevel@tonic-gate retval = hubd_pwrlvl2(hubd); 16830Sstevel@tonic-gate 16840Sstevel@tonic-gate break; 16850Sstevel@tonic-gate case USB_DEV_OS_FULL_PWR: 16860Sstevel@tonic-gate retval = hubd_pwrlvl3(hubd); 16870Sstevel@tonic-gate 16880Sstevel@tonic-gate break; 16890Sstevel@tonic-gate } 16900Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 16910Sstevel@tonic-gate 16920Sstevel@tonic-gate ndi_devi_exit(dip, circ); 16930Sstevel@tonic-gate 16940Sstevel@tonic-gate return ((retval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE); 16950Sstevel@tonic-gate } 16960Sstevel@tonic-gate 16970Sstevel@tonic-gate 16980Sstevel@tonic-gate /* power entry point for the root hub */ 16990Sstevel@tonic-gate int 17000Sstevel@tonic-gate usba_hubdi_root_hub_power(dev_info_t *dip, int comp, int level) 17010Sstevel@tonic-gate { 17020Sstevel@tonic-gate return (usba_hubdi_power(dip, comp, level)); 17030Sstevel@tonic-gate } 17040Sstevel@tonic-gate 17050Sstevel@tonic-gate 17060Sstevel@tonic-gate /* 17070Sstevel@tonic-gate * standard driver entry points support code 17080Sstevel@tonic-gate */ 17090Sstevel@tonic-gate int 17100Sstevel@tonic-gate usba_hubdi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 17110Sstevel@tonic-gate { 17120Sstevel@tonic-gate int instance = ddi_get_instance(dip); 17130Sstevel@tonic-gate hubd_t *hubd = NULL; 17140Sstevel@tonic-gate int i, rval; 17150Sstevel@tonic-gate int minor; 17160Sstevel@tonic-gate char *log_name = NULL; 17170Sstevel@tonic-gate const char *root_hub_drvname; 17180Sstevel@tonic-gate usb_ep_data_t *ep_data; 17190Sstevel@tonic-gate usba_device_t *child_ud = NULL; 17200Sstevel@tonic-gate usb_dev_descr_t *usb_dev_descr; 17210Sstevel@tonic-gate usb_port_status_t parent_port_status, child_port_status; 17220Sstevel@tonic-gate 17230Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubdi_log_handle, 1724*4763Slg150142 "hubd_attach instance %d, cmd=0x%x", instance, cmd); 17250Sstevel@tonic-gate 17260Sstevel@tonic-gate switch (cmd) { 17270Sstevel@tonic-gate case DDI_ATTACH: 17280Sstevel@tonic-gate 17290Sstevel@tonic-gate break; 17300Sstevel@tonic-gate case DDI_RESUME: 17310Sstevel@tonic-gate hubd_cpr_resume(dip); 17320Sstevel@tonic-gate 17330Sstevel@tonic-gate return (DDI_SUCCESS); 17340Sstevel@tonic-gate default: 17350Sstevel@tonic-gate return (DDI_FAILURE); 17360Sstevel@tonic-gate } 17370Sstevel@tonic-gate 17380Sstevel@tonic-gate /* 17390Sstevel@tonic-gate * Allocate softc information. 17400Sstevel@tonic-gate */ 17410Sstevel@tonic-gate if (usba_is_root_hub(dip)) { 17420Sstevel@tonic-gate /* soft state has already been allocated */ 17430Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 17440Sstevel@tonic-gate minor = HUBD_IS_ROOT_HUB; 17450Sstevel@tonic-gate 17460Sstevel@tonic-gate /* generate readable labels for different root hubs */ 17470Sstevel@tonic-gate root_hub_drvname = ddi_driver_name(dip); 17480Sstevel@tonic-gate if (strcmp(root_hub_drvname, "ehci") == 0) { 17490Sstevel@tonic-gate log_name = "eusb"; 17500Sstevel@tonic-gate } else if (strcmp(root_hub_drvname, "uhci") == 0) { 17510Sstevel@tonic-gate log_name = "uusb"; 17520Sstevel@tonic-gate } else { 17530Sstevel@tonic-gate /* std. for ohci */ 17540Sstevel@tonic-gate log_name = "usb"; 17550Sstevel@tonic-gate } 17560Sstevel@tonic-gate } else { 17570Sstevel@tonic-gate rval = ddi_soft_state_zalloc(hubd_statep, instance); 17580Sstevel@tonic-gate minor = 0; 17590Sstevel@tonic-gate 17600Sstevel@tonic-gate if (rval != DDI_SUCCESS) { 17610Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle, 17620Sstevel@tonic-gate "cannot allocate soft state (%d)", instance); 17630Sstevel@tonic-gate goto fail; 17640Sstevel@tonic-gate } 17650Sstevel@tonic-gate 17660Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 17670Sstevel@tonic-gate if (hubd == NULL) { 17680Sstevel@tonic-gate goto fail; 17690Sstevel@tonic-gate } 17700Sstevel@tonic-gate } 17710Sstevel@tonic-gate 17720Sstevel@tonic-gate hubd->h_log_handle = usb_alloc_log_hdl(dip, log_name, &hubd_errlevel, 1773*4763Slg150142 &hubd_errmask, &hubd_instance_debug, 0); 17740Sstevel@tonic-gate 17750Sstevel@tonic-gate hubd->h_usba_device = child_ud = usba_get_usba_device(dip); 17760Sstevel@tonic-gate hubd->h_dip = dip; 17770Sstevel@tonic-gate hubd->h_instance = instance; 17780Sstevel@tonic-gate 17790Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 17800Sstevel@tonic-gate child_port_status = child_ud->usb_port_status; 17810Sstevel@tonic-gate usb_dev_descr = child_ud->usb_dev_descr; 17820Sstevel@tonic-gate parent_port_status = (child_ud->usb_hs_hub_usba_dev) ? 17830Sstevel@tonic-gate child_ud->usb_hs_hub_usba_dev->usb_port_status : 0; 17840Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 17850Sstevel@tonic-gate 17860Sstevel@tonic-gate if ((child_port_status == USBA_FULL_SPEED_DEV) && 17870Sstevel@tonic-gate (parent_port_status == USBA_HIGH_SPEED_DEV) && 17880Sstevel@tonic-gate (usb_dev_descr->bcdUSB == 0x100)) { 17890Sstevel@tonic-gate USB_DPRINTF_L0(DPRINT_MASK_ATTA, hubd->h_log_handle, 17900Sstevel@tonic-gate "Use of a USB1.0 hub behind a high speed port may " 17910Sstevel@tonic-gate "cause unexpected failures"); 17920Sstevel@tonic-gate } 17930Sstevel@tonic-gate 17940Sstevel@tonic-gate hubd->h_pipe_policy.pp_max_async_reqs = 1; 17950Sstevel@tonic-gate 17960Sstevel@tonic-gate /* register with USBA as client driver */ 17970Sstevel@tonic-gate if (usb_client_attach(dip, USBDRV_VERSION, 0) != USB_SUCCESS) { 17980Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 17990Sstevel@tonic-gate "client attach failed"); 18000Sstevel@tonic-gate 18010Sstevel@tonic-gate goto fail; 18020Sstevel@tonic-gate } 18030Sstevel@tonic-gate 18040Sstevel@tonic-gate if (usb_get_dev_data(dip, &hubd->h_dev_data, 18050Sstevel@tonic-gate USB_PARSE_LVL_IF, 0) != USB_SUCCESS) { 18060Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 18070Sstevel@tonic-gate "cannot get dev_data"); 18080Sstevel@tonic-gate 18090Sstevel@tonic-gate goto fail; 18100Sstevel@tonic-gate } 18110Sstevel@tonic-gate 18120Sstevel@tonic-gate if ((ep_data = usb_lookup_ep_data(dip, hubd->h_dev_data, 18130Sstevel@tonic-gate hubd->h_dev_data->dev_curr_if, 0, 0, 18140Sstevel@tonic-gate (uint_t)USB_EP_ATTR_INTR, (uint_t)USB_EP_DIR_IN)) == NULL) { 18150Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 18160Sstevel@tonic-gate "no interrupt IN endpoint found"); 18170Sstevel@tonic-gate 18180Sstevel@tonic-gate goto fail; 18190Sstevel@tonic-gate } 18200Sstevel@tonic-gate 18210Sstevel@tonic-gate hubd->h_ep1_descr = ep_data->ep_descr; 18220Sstevel@tonic-gate hubd->h_default_pipe = hubd->h_dev_data->dev_default_ph; 18230Sstevel@tonic-gate 18240Sstevel@tonic-gate mutex_init(HUBD_MUTEX(hubd), NULL, MUTEX_DRIVER, 1825*4763Slg150142 hubd->h_dev_data->dev_iblock_cookie); 18260Sstevel@tonic-gate cv_init(&hubd->h_cv_reset_port, NULL, CV_DRIVER, NULL); 18270Sstevel@tonic-gate 18280Sstevel@tonic-gate hubd->h_init_state |= HUBD_LOCKS_DONE; 18290Sstevel@tonic-gate 18300Sstevel@tonic-gate usb_free_descr_tree(dip, hubd->h_dev_data); 18310Sstevel@tonic-gate 18320Sstevel@tonic-gate /* 18330Sstevel@tonic-gate * register this hub instance with usba 18340Sstevel@tonic-gate */ 18350Sstevel@tonic-gate rval = usba_hubdi_register(dip, 0); 18360Sstevel@tonic-gate if (rval != USB_SUCCESS) { 1837978Sfrits USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 18380Sstevel@tonic-gate "usba_hubdi_register failed"); 18390Sstevel@tonic-gate goto fail; 18400Sstevel@tonic-gate } 18410Sstevel@tonic-gate 18420Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 18430Sstevel@tonic-gate hubd->h_init_state |= HUBD_HUBDI_REGISTERED; 18440Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_ONLINE; 18450Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 18460Sstevel@tonic-gate 18470Sstevel@tonic-gate /* now create components to power manage this device */ 18480Sstevel@tonic-gate hubd_create_pm_components(dip, hubd); 18490Sstevel@tonic-gate 18500Sstevel@tonic-gate /* 18510Sstevel@tonic-gate * Event handling: definition and registration 18520Sstevel@tonic-gate * 18530Sstevel@tonic-gate * first the definition: 18540Sstevel@tonic-gate * get event handle 18550Sstevel@tonic-gate */ 18560Sstevel@tonic-gate (void) ndi_event_alloc_hdl(dip, 0, &hubd->h_ndi_event_hdl, NDI_SLEEP); 18570Sstevel@tonic-gate 18580Sstevel@tonic-gate /* bind event set to the handle */ 18590Sstevel@tonic-gate if (ndi_event_bind_set(hubd->h_ndi_event_hdl, &hubd_ndi_events, 18600Sstevel@tonic-gate NDI_SLEEP)) { 18610Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle, 18620Sstevel@tonic-gate "binding event set failed"); 18630Sstevel@tonic-gate 18640Sstevel@tonic-gate goto fail; 18650Sstevel@tonic-gate } 18660Sstevel@tonic-gate 18670Sstevel@tonic-gate /* event registration */ 18680Sstevel@tonic-gate if (hubd_register_events(hubd) != USB_SUCCESS) { 1869978Sfrits USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 18700Sstevel@tonic-gate "hubd_register_events failed"); 18710Sstevel@tonic-gate 18720Sstevel@tonic-gate goto fail; 18730Sstevel@tonic-gate } 18740Sstevel@tonic-gate 18750Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 18760Sstevel@tonic-gate hubd->h_init_state |= HUBD_EVENTS_REGISTERED; 18770Sstevel@tonic-gate 18781001Ssl147100 if ((hubd_get_hub_descriptor(hubd)) != USB_SUCCESS) { 18791001Ssl147100 mutex_exit(HUBD_MUTEX(hubd)); 18801001Ssl147100 18811001Ssl147100 goto fail; 18821001Ssl147100 } 18831001Ssl147100 18841001Ssl147100 if (ddi_prop_exists(DDI_DEV_T_ANY, dip, 18851001Ssl147100 (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM), 18861001Ssl147100 "hub-ignore-power-budget") == 1) { 18871001Ssl147100 hubd->h_ignore_pwr_budget = B_TRUE; 18881001Ssl147100 } else { 18891001Ssl147100 hubd->h_ignore_pwr_budget = B_FALSE; 18901001Ssl147100 18911001Ssl147100 /* initialize hub power budget variables */ 18921001Ssl147100 if (hubd_init_power_budget(hubd) != USB_SUCCESS) { 18931001Ssl147100 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 18941001Ssl147100 "hubd_init_power_budget failed"); 18951001Ssl147100 mutex_exit(HUBD_MUTEX(hubd)); 18961001Ssl147100 18971001Ssl147100 goto fail; 18981001Ssl147100 } 18991001Ssl147100 } 19001001Ssl147100 19010Sstevel@tonic-gate /* initialize and create children */ 19020Sstevel@tonic-gate if (hubd_check_ports(hubd) != USB_SUCCESS) { 1903978Sfrits USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 19040Sstevel@tonic-gate "hubd_check_ports failed"); 19050Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 19060Sstevel@tonic-gate 19070Sstevel@tonic-gate goto fail; 19080Sstevel@tonic-gate } 19090Sstevel@tonic-gate 19100Sstevel@tonic-gate /* 19110Sstevel@tonic-gate * create cfgadm nodes 19120Sstevel@tonic-gate */ 19130Sstevel@tonic-gate hubd->h_ancestry_str = (char *)kmem_zalloc(HUBD_APID_NAMELEN, KM_SLEEP); 19140Sstevel@tonic-gate hubd_get_ancestry_str(hubd); 19150Sstevel@tonic-gate 19160Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 19170Sstevel@tonic-gate "#ports=0x%x", hubd->h_hub_descr.bNbrPorts); 19180Sstevel@tonic-gate 19190Sstevel@tonic-gate for (i = 1; i <= hubd->h_hub_descr.bNbrPorts; i++) { 19200Sstevel@tonic-gate char ap_name[HUBD_APID_NAMELEN]; 19210Sstevel@tonic-gate 19220Sstevel@tonic-gate (void) snprintf(ap_name, HUBD_APID_NAMELEN, "%s%d", 19230Sstevel@tonic-gate hubd->h_ancestry_str, i); 19240Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 19250Sstevel@tonic-gate "ap_name=%s", ap_name); 19260Sstevel@tonic-gate 19270Sstevel@tonic-gate if (ddi_create_minor_node(dip, ap_name, S_IFCHR, instance, 19280Sstevel@tonic-gate DDI_NT_USB_ATTACHMENT_POINT, 0) != DDI_SUCCESS) { 1929978Sfrits USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 19300Sstevel@tonic-gate "cannot create attachment point node (%d)", 19310Sstevel@tonic-gate instance); 19320Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 19330Sstevel@tonic-gate 19340Sstevel@tonic-gate goto fail; 19350Sstevel@tonic-gate } 19360Sstevel@tonic-gate } 19370Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 19380Sstevel@tonic-gate 19390Sstevel@tonic-gate /* create minor nodes */ 19400Sstevel@tonic-gate if (ddi_create_minor_node(dip, "hubd", S_IFCHR, 19410Sstevel@tonic-gate instance | minor, DDI_NT_NEXUS, 0) != DDI_SUCCESS) { 19420Sstevel@tonic-gate 1943978Sfrits USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 19440Sstevel@tonic-gate "cannot create devctl minor node (%d)", instance); 19450Sstevel@tonic-gate 19460Sstevel@tonic-gate goto fail; 19470Sstevel@tonic-gate } 19480Sstevel@tonic-gate 19490Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 19500Sstevel@tonic-gate hubd->h_init_state |= HUBD_MINOR_NODE_CREATED; 19510Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 19520Sstevel@tonic-gate 19530Sstevel@tonic-gate /* 19540Sstevel@tonic-gate * host controller driver has already reported this dev 19550Sstevel@tonic-gate * if we are the root hub 19560Sstevel@tonic-gate */ 19570Sstevel@tonic-gate if (!usba_is_root_hub(dip)) { 19580Sstevel@tonic-gate ddi_report_dev(dip); 19590Sstevel@tonic-gate } 19600Sstevel@tonic-gate 19610Sstevel@tonic-gate /* enable deathrow thread */ 19620Sstevel@tonic-gate hubd->h_cleanup_enabled = B_TRUE; 19630Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 19640Sstevel@tonic-gate hubd_pm_idle_component(hubd, dip, 0); 19650Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 19660Sstevel@tonic-gate 19670Sstevel@tonic-gate return (DDI_SUCCESS); 19680Sstevel@tonic-gate 19690Sstevel@tonic-gate fail: 19700Sstevel@tonic-gate { 19710Sstevel@tonic-gate char *pathname = kmem_alloc(MAXPATHLEN, KM_SLEEP); 19720Sstevel@tonic-gate 19733435Slg150142 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle, 19740Sstevel@tonic-gate "cannot attach %s", ddi_pathname(dip, pathname)); 19750Sstevel@tonic-gate 19760Sstevel@tonic-gate kmem_free(pathname, MAXPATHLEN); 19770Sstevel@tonic-gate } 19780Sstevel@tonic-gate 19790Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 19800Sstevel@tonic-gate hubd_pm_idle_component(hubd, dip, 0); 19810Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 19820Sstevel@tonic-gate 19830Sstevel@tonic-gate if (hubd) { 19840Sstevel@tonic-gate rval = hubd_cleanup(dip, hubd); 19850Sstevel@tonic-gate if (rval != USB_SUCCESS) { 1986978Sfrits USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle, 19870Sstevel@tonic-gate "failure to complete cleanup after attach failure"); 19880Sstevel@tonic-gate } 19890Sstevel@tonic-gate } 19900Sstevel@tonic-gate 19910Sstevel@tonic-gate return (DDI_FAILURE); 19920Sstevel@tonic-gate } 19930Sstevel@tonic-gate 19940Sstevel@tonic-gate 19950Sstevel@tonic-gate int 19960Sstevel@tonic-gate usba_hubdi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 19970Sstevel@tonic-gate { 19980Sstevel@tonic-gate hubd_t *hubd = hubd_get_soft_state(dip); 19990Sstevel@tonic-gate int rval; 20000Sstevel@tonic-gate 20010Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 20020Sstevel@tonic-gate "hubd_detach: cmd=0x%x", cmd); 20030Sstevel@tonic-gate 20040Sstevel@tonic-gate switch (cmd) { 20050Sstevel@tonic-gate case DDI_DETACH: 20060Sstevel@tonic-gate rval = hubd_cleanup(dip, hubd); 20070Sstevel@tonic-gate 20080Sstevel@tonic-gate return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE); 20090Sstevel@tonic-gate case DDI_SUSPEND: 20100Sstevel@tonic-gate rval = hubd_cpr_suspend(hubd); 20110Sstevel@tonic-gate 20120Sstevel@tonic-gate return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE); 20130Sstevel@tonic-gate default: 20140Sstevel@tonic-gate return (DDI_FAILURE); 20150Sstevel@tonic-gate } 20160Sstevel@tonic-gate } 20170Sstevel@tonic-gate 20180Sstevel@tonic-gate 20190Sstevel@tonic-gate /* 20200Sstevel@tonic-gate * hubd_setdevaddr 20210Sstevel@tonic-gate * set the device addrs on this port 20220Sstevel@tonic-gate */ 20230Sstevel@tonic-gate static int 20240Sstevel@tonic-gate hubd_setdevaddr(hubd_t *hubd, usb_port_t port) 20250Sstevel@tonic-gate { 20260Sstevel@tonic-gate int rval; 20270Sstevel@tonic-gate usb_cr_t completion_reason; 20280Sstevel@tonic-gate usb_cb_flags_t cb_flags; 20290Sstevel@tonic-gate usb_pipe_handle_t ph; 20300Sstevel@tonic-gate dev_info_t *child_dip = NULL; 20310Sstevel@tonic-gate uchar_t address = 0; 20320Sstevel@tonic-gate usba_device_t *usba_device; 20330Sstevel@tonic-gate int retry = 0; 20340Sstevel@tonic-gate long time_delay; 20350Sstevel@tonic-gate 20360Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 20370Sstevel@tonic-gate "hubd_setdevaddr: port=%d", port); 20380Sstevel@tonic-gate 20390Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 20400Sstevel@tonic-gate 20410Sstevel@tonic-gate child_dip = hubd->h_children_dips[port]; 20420Sstevel@tonic-gate address = hubd->h_usba_devices[port]->usb_addr; 20430Sstevel@tonic-gate usba_device = hubd->h_usba_devices[port]; 20440Sstevel@tonic-gate 20450Sstevel@tonic-gate /* close the default pipe with addr x */ 20460Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 20470Sstevel@tonic-gate ph = usba_get_dflt_pipe_handle(child_dip); 20480Sstevel@tonic-gate usb_pipe_close(child_dip, ph, 20490Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL); 20500Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 20510Sstevel@tonic-gate 20520Sstevel@tonic-gate /* 20530Sstevel@tonic-gate * As this device has been reset, temporarily 20540Sstevel@tonic-gate * assign the default address 20550Sstevel@tonic-gate */ 20560Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex); 20570Sstevel@tonic-gate address = usba_device->usb_addr; 20580Sstevel@tonic-gate usba_device->usb_addr = USBA_DEFAULT_ADDR; 20590Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex); 20600Sstevel@tonic-gate 20610Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 20620Sstevel@tonic-gate 20630Sstevel@tonic-gate time_delay = drv_usectohz(hubd_device_delay / 20); 20640Sstevel@tonic-gate for (retry = 0; retry < hubd_retry_enumerate; retry++) { 20650Sstevel@tonic-gate 20660Sstevel@tonic-gate /* open child's default pipe with USBA_DEFAULT_ADDR */ 20670Sstevel@tonic-gate if (usb_pipe_open(child_dip, NULL, NULL, 20680Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph) != 20690Sstevel@tonic-gate USB_SUCCESS) { 20700Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 20710Sstevel@tonic-gate "hubd_setdevaddr: Unable to open default pipe"); 20720Sstevel@tonic-gate 20730Sstevel@tonic-gate break; 20740Sstevel@tonic-gate } 20750Sstevel@tonic-gate 20760Sstevel@tonic-gate /* Set the address of the device */ 20770Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 20780Sstevel@tonic-gate USB_DEV_REQ_HOST_TO_DEV, 20790Sstevel@tonic-gate USB_REQ_SET_ADDRESS, /* bRequest */ 20800Sstevel@tonic-gate address, /* wValue */ 20810Sstevel@tonic-gate 0, /* wIndex */ 20820Sstevel@tonic-gate 0, /* wLength */ 20830Sstevel@tonic-gate NULL, 0, 20840Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 20850Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 20860Sstevel@tonic-gate "hubd_setdevaddr(%d): rval=%d cr=%d cb_fl=0x%x", 20870Sstevel@tonic-gate retry, rval, completion_reason, cb_flags); 20880Sstevel@tonic-gate } 20890Sstevel@tonic-gate 20900Sstevel@tonic-gate usb_pipe_close(child_dip, ph, 20910Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL); 20920Sstevel@tonic-gate 20930Sstevel@tonic-gate if (rval == USB_SUCCESS) { 20940Sstevel@tonic-gate 20950Sstevel@tonic-gate break; 20960Sstevel@tonic-gate } 20970Sstevel@tonic-gate 20980Sstevel@tonic-gate delay(time_delay); 20990Sstevel@tonic-gate } 21000Sstevel@tonic-gate 21010Sstevel@tonic-gate /* Reset to the old address */ 21020Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex); 21030Sstevel@tonic-gate usba_device->usb_addr = address; 21040Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex); 21050Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 21060Sstevel@tonic-gate 21070Sstevel@tonic-gate usba_clear_data_toggle(usba_device); 21080Sstevel@tonic-gate 21090Sstevel@tonic-gate return (rval); 21100Sstevel@tonic-gate } 21110Sstevel@tonic-gate 21120Sstevel@tonic-gate 21130Sstevel@tonic-gate /* 21140Sstevel@tonic-gate * hubd_setdevconfig 21150Sstevel@tonic-gate * set the device addrs on this port 21160Sstevel@tonic-gate */ 21170Sstevel@tonic-gate static void 21180Sstevel@tonic-gate hubd_setdevconfig(hubd_t *hubd, usb_port_t port) 21190Sstevel@tonic-gate { 21200Sstevel@tonic-gate int rval; 21210Sstevel@tonic-gate usb_cr_t completion_reason; 21220Sstevel@tonic-gate usb_cb_flags_t cb_flags; 21230Sstevel@tonic-gate usb_pipe_handle_t ph; 21240Sstevel@tonic-gate dev_info_t *child_dip = NULL; 21250Sstevel@tonic-gate usba_device_t *usba_device = NULL; 21260Sstevel@tonic-gate uint16_t config_value; 21270Sstevel@tonic-gate 21280Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 21290Sstevel@tonic-gate "hubd_setdevconfig: port=%d", port); 21300Sstevel@tonic-gate 21310Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 21320Sstevel@tonic-gate 21330Sstevel@tonic-gate child_dip = hubd->h_children_dips[port]; 21340Sstevel@tonic-gate usba_device = hubd->h_usba_devices[port]; 21350Sstevel@tonic-gate config_value = hubd->h_usba_devices[port]->usb_cfg_value; 21360Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 21370Sstevel@tonic-gate 21380Sstevel@tonic-gate /* open the default control pipe */ 21390Sstevel@tonic-gate if ((rval = usb_pipe_open(child_dip, NULL, NULL, 21400Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) == 21410Sstevel@tonic-gate USB_SUCCESS) { 21420Sstevel@tonic-gate 21430Sstevel@tonic-gate /* Set the default configuration of the device */ 21440Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 21450Sstevel@tonic-gate USB_DEV_REQ_HOST_TO_DEV, 21460Sstevel@tonic-gate USB_REQ_SET_CFG, /* bRequest */ 21470Sstevel@tonic-gate config_value, /* wValue */ 21480Sstevel@tonic-gate 0, /* wIndex */ 21490Sstevel@tonic-gate 0, /* wLength */ 21500Sstevel@tonic-gate NULL, 0, 21510Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 21520Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 21530Sstevel@tonic-gate "hubd_setdevconfig: set device config failed: " 21540Sstevel@tonic-gate "cr=%d cb_fl=0x%x rval=%d", 21550Sstevel@tonic-gate completion_reason, cb_flags, rval); 21560Sstevel@tonic-gate } 21570Sstevel@tonic-gate /* 21580Sstevel@tonic-gate * After setting the configuration, we make this default 21590Sstevel@tonic-gate * control pipe persistent, so that it gets re-opened 21600Sstevel@tonic-gate * on posting a connect event 21610Sstevel@tonic-gate */ 21620Sstevel@tonic-gate usba_persistent_pipe_close(usba_device); 21630Sstevel@tonic-gate } else { 21640Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 21650Sstevel@tonic-gate "pipe open fails: rval=%d", rval); 21660Sstevel@tonic-gate } 21670Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 21680Sstevel@tonic-gate } 21690Sstevel@tonic-gate 21700Sstevel@tonic-gate 21710Sstevel@tonic-gate /*ARGSUSED*/ 21720Sstevel@tonic-gate static int 21730Sstevel@tonic-gate hubd_check_disconnected_ports(dev_info_t *dip, void *arg) 21740Sstevel@tonic-gate { 21750Sstevel@tonic-gate int circ; 21760Sstevel@tonic-gate usb_port_t port; 21770Sstevel@tonic-gate hubd_t *hubd; 21780Sstevel@tonic-gate major_t hub_major = ddi_name_to_major("hubd"); 21790Sstevel@tonic-gate 21800Sstevel@tonic-gate /* 21810Sstevel@tonic-gate * make sure dip is a usb hub, major of root hub is HCD 21820Sstevel@tonic-gate * major 21830Sstevel@tonic-gate */ 21840Sstevel@tonic-gate if (!usba_is_root_hub(dip)) { 21850Sstevel@tonic-gate if ((ddi_driver_major(dip) != hub_major) || 21861333Scth !i_ddi_devi_attached(dip)) { 21870Sstevel@tonic-gate 21880Sstevel@tonic-gate return (DDI_WALK_PRUNECHILD); 21890Sstevel@tonic-gate } 21900Sstevel@tonic-gate } 21910Sstevel@tonic-gate 21920Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 21930Sstevel@tonic-gate if (hubd == NULL) { 21940Sstevel@tonic-gate 21950Sstevel@tonic-gate return (DDI_WALK_PRUNECHILD); 21960Sstevel@tonic-gate } 21970Sstevel@tonic-gate 21980Sstevel@tonic-gate /* walk child list and remove nodes with flag DEVI_DEVICE_REMOVED */ 21990Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 22000Sstevel@tonic-gate 22010Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 22020Sstevel@tonic-gate for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { 22030Sstevel@tonic-gate dev_info_t *cdip = hubd->h_children_dips[port]; 22040Sstevel@tonic-gate 22050Sstevel@tonic-gate if (cdip == NULL || DEVI_IS_DEVICE_REMOVED(cdip) == 0) { 22060Sstevel@tonic-gate 22070Sstevel@tonic-gate continue; 22080Sstevel@tonic-gate } 22090Sstevel@tonic-gate 22100Sstevel@tonic-gate (void) hubd_delete_child(hubd, port, NDI_DEVI_REMOVE, B_TRUE); 22110Sstevel@tonic-gate } 22120Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 22130Sstevel@tonic-gate ndi_devi_exit(dip, circ); 22140Sstevel@tonic-gate 22150Sstevel@tonic-gate /* skip siblings of root hub */ 22160Sstevel@tonic-gate if (usba_is_root_hub(dip)) { 22170Sstevel@tonic-gate 22180Sstevel@tonic-gate return (DDI_WALK_PRUNESIB); 22190Sstevel@tonic-gate } 22200Sstevel@tonic-gate 22210Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 22220Sstevel@tonic-gate } 22230Sstevel@tonic-gate 22240Sstevel@tonic-gate 22250Sstevel@tonic-gate /* 22260Sstevel@tonic-gate * this thread will walk all children under the root hub for this 22270Sstevel@tonic-gate * USB bus instance and attempt to remove them 22280Sstevel@tonic-gate */ 22290Sstevel@tonic-gate static void 22300Sstevel@tonic-gate hubd_root_hub_cleanup_thread(void *arg) 22310Sstevel@tonic-gate { 22320Sstevel@tonic-gate int circ; 22330Sstevel@tonic-gate hubd_t *root_hubd = (hubd_t *)arg; 22340Sstevel@tonic-gate dev_info_t *rh_dip = root_hubd->h_dip; 22350Sstevel@tonic-gate #ifndef __lock_lint 22360Sstevel@tonic-gate callb_cpr_t cprinfo; 22370Sstevel@tonic-gate 22380Sstevel@tonic-gate CALLB_CPR_INIT(&cprinfo, HUBD_MUTEX(root_hubd), callb_generic_cpr, 22390Sstevel@tonic-gate "USB root hub"); 22400Sstevel@tonic-gate #endif 22410Sstevel@tonic-gate 22420Sstevel@tonic-gate for (;;) { 22430Sstevel@tonic-gate /* don't race with detach */ 22440Sstevel@tonic-gate ndi_hold_devi(rh_dip); 22450Sstevel@tonic-gate 22460Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(root_hubd)); 22470Sstevel@tonic-gate root_hubd->h_cleanup_needed = 0; 22480Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd)); 22490Sstevel@tonic-gate 22500Sstevel@tonic-gate (void) devfs_clean(rh_dip, NULL, 0); 22510Sstevel@tonic-gate 22520Sstevel@tonic-gate ndi_devi_enter(ddi_get_parent(rh_dip), &circ); 22530Sstevel@tonic-gate ddi_walk_devs(rh_dip, hubd_check_disconnected_ports, 2254*4763Slg150142 NULL); 22550Sstevel@tonic-gate #ifdef __lock_lint 22560Sstevel@tonic-gate (void) hubd_check_disconnected_ports(rh_dip, NULL); 22570Sstevel@tonic-gate #endif 22580Sstevel@tonic-gate ndi_devi_exit(ddi_get_parent(rh_dip), circ); 22590Sstevel@tonic-gate 22600Sstevel@tonic-gate /* quit if we are not enabled anymore */ 22610Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(root_hubd)); 22620Sstevel@tonic-gate if ((root_hubd->h_cleanup_enabled == B_FALSE) || 22630Sstevel@tonic-gate (root_hubd->h_cleanup_needed == B_FALSE)) { 22640Sstevel@tonic-gate root_hubd->h_cleanup_active = B_FALSE; 22650Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd)); 22660Sstevel@tonic-gate ndi_rele_devi(rh_dip); 22670Sstevel@tonic-gate 22680Sstevel@tonic-gate break; 22690Sstevel@tonic-gate } 22700Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd)); 22710Sstevel@tonic-gate ndi_rele_devi(rh_dip); 22720Sstevel@tonic-gate 22730Sstevel@tonic-gate #ifndef __lock_lint 22740Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(root_hubd)); 22750Sstevel@tonic-gate CALLB_CPR_SAFE_BEGIN(&cprinfo); 22760Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd)); 22770Sstevel@tonic-gate 22780Sstevel@tonic-gate delay(drv_usectohz(hubd_dip_cleanup_delay)); 22790Sstevel@tonic-gate 22800Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(root_hubd)); 22810Sstevel@tonic-gate CALLB_CPR_SAFE_END(&cprinfo, HUBD_MUTEX(root_hubd)); 22820Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd)); 22830Sstevel@tonic-gate #endif 22840Sstevel@tonic-gate } 22850Sstevel@tonic-gate 22860Sstevel@tonic-gate #ifndef __lock_lint 22870Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(root_hubd)); 22880Sstevel@tonic-gate CALLB_CPR_EXIT(&cprinfo); 22890Sstevel@tonic-gate #endif 22900Sstevel@tonic-gate } 22910Sstevel@tonic-gate 22920Sstevel@tonic-gate 22930Sstevel@tonic-gate static void 22940Sstevel@tonic-gate hubd_schedule_cleanup(dev_info_t *rh_dip) 22950Sstevel@tonic-gate { 22960Sstevel@tonic-gate hubd_t *root_hubd = (hubd_t *)hubd_get_soft_state(rh_dip); 22970Sstevel@tonic-gate 22980Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(root_hubd)); 22990Sstevel@tonic-gate root_hubd->h_cleanup_needed = B_TRUE; 23000Sstevel@tonic-gate if (root_hubd->h_cleanup_enabled && !(root_hubd->h_cleanup_active)) { 23010Sstevel@tonic-gate root_hubd->h_cleanup_active = B_TRUE; 23020Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd)); 23030Sstevel@tonic-gate (void) thread_create(NULL, 0, 23040Sstevel@tonic-gate hubd_root_hub_cleanup_thread, 23050Sstevel@tonic-gate (void *)root_hubd, 0, &p0, TS_RUN, 23060Sstevel@tonic-gate minclsyspri); 23070Sstevel@tonic-gate } else { 23080Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd)); 23090Sstevel@tonic-gate } 23100Sstevel@tonic-gate } 23110Sstevel@tonic-gate 23120Sstevel@tonic-gate 23130Sstevel@tonic-gate /* 23140Sstevel@tonic-gate * hubd_restore_device_state: 23150Sstevel@tonic-gate * - set config for the hub 23160Sstevel@tonic-gate * - power cycle all the ports 23170Sstevel@tonic-gate * - for each port that was connected 23180Sstevel@tonic-gate * - reset port 23190Sstevel@tonic-gate * - assign addrs to the device on this port 23200Sstevel@tonic-gate * - restart polling 23210Sstevel@tonic-gate * - reset suspend flag 23220Sstevel@tonic-gate */ 23230Sstevel@tonic-gate static void 23240Sstevel@tonic-gate hubd_restore_device_state(dev_info_t *dip, hubd_t *hubd) 23250Sstevel@tonic-gate { 23260Sstevel@tonic-gate int rval; 23270Sstevel@tonic-gate int retry; 23280Sstevel@tonic-gate uint_t hub_prev_state; 23290Sstevel@tonic-gate usb_port_t port; 23300Sstevel@tonic-gate uint16_t status; 23310Sstevel@tonic-gate uint16_t change; 23320Sstevel@tonic-gate dev_info_t *ch_dip; 23330Sstevel@tonic-gate boolean_t ehci_root_hub; 23340Sstevel@tonic-gate 23350Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 23360Sstevel@tonic-gate "hubd_restore_device_state:"); 23370Sstevel@tonic-gate 23380Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 23390Sstevel@tonic-gate hub_prev_state = hubd->h_dev_state; 23400Sstevel@tonic-gate ASSERT(hub_prev_state != USB_DEV_PWRED_DOWN); 23410Sstevel@tonic-gate 23420Sstevel@tonic-gate /* First bring the device to full power */ 23430Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, dip, 0); 23440Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 23450Sstevel@tonic-gate 23460Sstevel@tonic-gate (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); 23470Sstevel@tonic-gate 23480Sstevel@tonic-gate if (!usba_is_root_hub(dip) && 23490Sstevel@tonic-gate (usb_check_same_device(dip, hubd->h_log_handle, USB_LOG_L0, 23500Sstevel@tonic-gate DPRINT_MASK_HOTPLUG, 23510Sstevel@tonic-gate USB_CHK_BASIC|USB_CHK_CFG, NULL) != USB_SUCCESS)) { 23520Sstevel@tonic-gate 23530Sstevel@tonic-gate /* change the device state to disconnected */ 23540Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 23550Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_DISCONNECTED; 23560Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, dip, 0); 23570Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 23580Sstevel@tonic-gate 23590Sstevel@tonic-gate return; 23600Sstevel@tonic-gate } 23610Sstevel@tonic-gate 23620Sstevel@tonic-gate ehci_root_hub = (strcmp(ddi_driver_name(dip), "ehci") == 0); 23630Sstevel@tonic-gate 23640Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 23650Sstevel@tonic-gate /* First turn off all port power */ 23660Sstevel@tonic-gate rval = hubd_disable_all_port_power(hubd); 23670Sstevel@tonic-gate if (rval != USB_SUCCESS) { 23680Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle, 23690Sstevel@tonic-gate "hubd_restore_device_state:" 23700Sstevel@tonic-gate "turning off port power failed"); 23710Sstevel@tonic-gate } 23720Sstevel@tonic-gate 23730Sstevel@tonic-gate /* Settling time before turning on again */ 23740Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 23750Sstevel@tonic-gate delay(drv_usectohz(hubd_device_delay / 100)); 23760Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 23770Sstevel@tonic-gate 23780Sstevel@tonic-gate /* enable power on all ports so we can see connects */ 23790Sstevel@tonic-gate if (hubd_enable_all_port_power(hubd) != USB_SUCCESS) { 23800Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 23810Sstevel@tonic-gate "hubd_restore_device_state: turn on port power failed"); 23820Sstevel@tonic-gate 23830Sstevel@tonic-gate /* disable whatever was enabled */ 23840Sstevel@tonic-gate (void) hubd_disable_all_port_power(hubd); 23850Sstevel@tonic-gate 23860Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, dip, 0); 23870Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 23880Sstevel@tonic-gate 23890Sstevel@tonic-gate return; 23900Sstevel@tonic-gate } 23910Sstevel@tonic-gate 23920Sstevel@tonic-gate /* 23930Sstevel@tonic-gate * wait at least 3 frames before accessing devices 23940Sstevel@tonic-gate * (note that delay's minimal time is one clock tick which 23950Sstevel@tonic-gate * is 10ms unless hires_tick has been changed) 23960Sstevel@tonic-gate */ 23970Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 23980Sstevel@tonic-gate delay(drv_usectohz(10000)); 23990Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 24000Sstevel@tonic-gate 24010Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_HUB_STATE_RECOVER; 24020Sstevel@tonic-gate 24030Sstevel@tonic-gate for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { 24040Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle, 24050Sstevel@tonic-gate "hubd_restore_device_state: port=%d", port); 24060Sstevel@tonic-gate 24070Sstevel@tonic-gate /* 24080Sstevel@tonic-gate * the childen_dips list may have dips that have been 24090Sstevel@tonic-gate * already deallocated. we only get a post_detach notification 24100Sstevel@tonic-gate * but not a destroy notification 24110Sstevel@tonic-gate */ 24120Sstevel@tonic-gate ch_dip = hubd->h_children_dips[port]; 24130Sstevel@tonic-gate if (ch_dip) { 24140Sstevel@tonic-gate /* get port status */ 24150Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 24160Sstevel@tonic-gate &status, &change, PORT_CHANGE_CSC); 24170Sstevel@tonic-gate 24180Sstevel@tonic-gate /* check if it is truly connected */ 24190Sstevel@tonic-gate if (status & PORT_STATUS_CCS) { 24200Sstevel@tonic-gate /* 24210Sstevel@tonic-gate * Now reset port and assign the device 24220Sstevel@tonic-gate * its original address 24230Sstevel@tonic-gate */ 24240Sstevel@tonic-gate retry = 0; 24250Sstevel@tonic-gate do { 24260Sstevel@tonic-gate (void) hubd_reset_port(hubd, port); 24270Sstevel@tonic-gate 24280Sstevel@tonic-gate /* required for ppx */ 24290Sstevel@tonic-gate (void) hubd_enable_port(hubd, port); 24300Sstevel@tonic-gate 24310Sstevel@tonic-gate if (retry) { 24320Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 24330Sstevel@tonic-gate delay(drv_usectohz( 2434*4763Slg150142 hubd_device_delay/2)); 24350Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 24360Sstevel@tonic-gate } 24370Sstevel@tonic-gate 24380Sstevel@tonic-gate rval = hubd_setdevaddr(hubd, port); 24390Sstevel@tonic-gate retry++; 24400Sstevel@tonic-gate } while ((rval != USB_SUCCESS) && 24410Sstevel@tonic-gate (retry < hubd_retry_enumerate)); 24420Sstevel@tonic-gate 24430Sstevel@tonic-gate hubd_setdevconfig(hubd, port); 24440Sstevel@tonic-gate 24450Sstevel@tonic-gate if (hub_prev_state == USB_DEV_DISCONNECTED) { 24460Sstevel@tonic-gate /* post a connect event */ 24470Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 24480Sstevel@tonic-gate hubd_post_event(hubd, port, 24490Sstevel@tonic-gate USBA_EVENT_TAG_HOT_INSERTION); 24500Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 24510Sstevel@tonic-gate } else { 24520Sstevel@tonic-gate /* 24530Sstevel@tonic-gate * Since we have this device connected 24540Sstevel@tonic-gate * mark it reinserted to prevent 24550Sstevel@tonic-gate * cleanup thread from stepping in. 24560Sstevel@tonic-gate */ 2457495Scth mutex_exit(HUBD_MUTEX(hubd)); 2458495Scth mutex_enter(&(DEVI(ch_dip)->devi_lock)); 24590Sstevel@tonic-gate DEVI_SET_DEVICE_REINSERTED(ch_dip); 2460495Scth mutex_exit(&(DEVI(ch_dip)->devi_lock)); 24610Sstevel@tonic-gate 24620Sstevel@tonic-gate /* 24630Sstevel@tonic-gate * reopen pipes for children for 24640Sstevel@tonic-gate * their DDI_RESUME 24650Sstevel@tonic-gate */ 24660Sstevel@tonic-gate rval = usba_persistent_pipe_open( 24670Sstevel@tonic-gate usba_get_usba_device(ch_dip)); 24680Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 24690Sstevel@tonic-gate ASSERT(rval == USB_SUCCESS); 24700Sstevel@tonic-gate } 24710Sstevel@tonic-gate } else { 24720Sstevel@tonic-gate /* 24730Sstevel@tonic-gate * Mark this dip for deletion as the device 24740Sstevel@tonic-gate * is not physically present, and schedule 24750Sstevel@tonic-gate * cleanup thread upon post resume 24760Sstevel@tonic-gate */ 24770Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 24780Sstevel@tonic-gate 24790Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, 24800Sstevel@tonic-gate hubd->h_log_handle, 24810Sstevel@tonic-gate "hubd_restore_device_state: " 24820Sstevel@tonic-gate "dip=%p on port=%d marked for cleanup", 24830Sstevel@tonic-gate ch_dip, port); 2484495Scth mutex_enter(&(DEVI(ch_dip)->devi_lock)); 24850Sstevel@tonic-gate DEVI_SET_DEVICE_REMOVED(ch_dip); 2486495Scth mutex_exit(&(DEVI(ch_dip)->devi_lock)); 24870Sstevel@tonic-gate 24880Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 24890Sstevel@tonic-gate } 24900Sstevel@tonic-gate } else if (ehci_root_hub) { 24910Sstevel@tonic-gate /* get port status */ 24920Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 24930Sstevel@tonic-gate &status, &change, PORT_CHANGE_CSC); 24940Sstevel@tonic-gate 24950Sstevel@tonic-gate /* check if it is truly connected */ 24960Sstevel@tonic-gate if (status & PORT_STATUS_CCS) { 24970Sstevel@tonic-gate /* 24980Sstevel@tonic-gate * reset the port to find out if we have 24990Sstevel@tonic-gate * 2.0 device connected or 1.X. A 2.0 25000Sstevel@tonic-gate * device will still be seen as connected, 25010Sstevel@tonic-gate * while a 1.X device will switch over to 25020Sstevel@tonic-gate * the companion controller. 25030Sstevel@tonic-gate */ 25040Sstevel@tonic-gate (void) hubd_reset_port(hubd, port); 25050Sstevel@tonic-gate 25060Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 25070Sstevel@tonic-gate &status, &change, PORT_CHANGE_CSC); 25080Sstevel@tonic-gate 25090Sstevel@tonic-gate if (status & 25100Sstevel@tonic-gate (PORT_STATUS_CCS | PORT_STATUS_HSDA)) { 25110Sstevel@tonic-gate /* 25120Sstevel@tonic-gate * We have a USB 2.0 device 25130Sstevel@tonic-gate * connected. Power cycle this port 25140Sstevel@tonic-gate * so that hotplug thread can 25150Sstevel@tonic-gate * enumerate this device. 25160Sstevel@tonic-gate */ 25170Sstevel@tonic-gate (void) hubd_toggle_port(hubd, port); 25180Sstevel@tonic-gate } else { 25190Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, 25200Sstevel@tonic-gate hubd->h_log_handle, 25210Sstevel@tonic-gate "hubd_restore_device_state: " 25220Sstevel@tonic-gate "device on port %d switched over", 25230Sstevel@tonic-gate port); 25240Sstevel@tonic-gate } 25250Sstevel@tonic-gate } 25260Sstevel@tonic-gate 25270Sstevel@tonic-gate } 25280Sstevel@tonic-gate } 25290Sstevel@tonic-gate 25300Sstevel@tonic-gate 25310Sstevel@tonic-gate /* if the device had remote wakeup earlier, enable it again */ 25320Sstevel@tonic-gate if (hubd->h_hubpm->hubp_wakeup_enabled) { 25330Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 25340Sstevel@tonic-gate (void) usb_handle_remote_wakeup(hubd->h_dip, 25350Sstevel@tonic-gate USB_REMOTE_WAKEUP_ENABLE); 25360Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 25370Sstevel@tonic-gate } 25380Sstevel@tonic-gate 25390Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_ONLINE; 25400Sstevel@tonic-gate hubd_start_polling(hubd, 0); 25410Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, dip, 0); 25420Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 25430Sstevel@tonic-gate } 25440Sstevel@tonic-gate 25450Sstevel@tonic-gate 25460Sstevel@tonic-gate /* 25470Sstevel@tonic-gate * hubd_cleanup: 25480Sstevel@tonic-gate * cleanup hubd and deallocate. this function is called for 25490Sstevel@tonic-gate * handling attach failures and detaching including dynamic 25500Sstevel@tonic-gate * reconfiguration. If called from attaching, it must clean 25510Sstevel@tonic-gate * up the whole thing and return success. 25520Sstevel@tonic-gate */ 25530Sstevel@tonic-gate /*ARGSUSED*/ 25540Sstevel@tonic-gate static int 25550Sstevel@tonic-gate hubd_cleanup(dev_info_t *dip, hubd_t *hubd) 25560Sstevel@tonic-gate { 25570Sstevel@tonic-gate int circ, rval, old_dev_state; 25580Sstevel@tonic-gate hub_power_t *hubpm; 25590Sstevel@tonic-gate #ifdef DEBUG 25600Sstevel@tonic-gate usb_port_t port; 25610Sstevel@tonic-gate #endif 25620Sstevel@tonic-gate 25630Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 25640Sstevel@tonic-gate "hubd_cleanup:"); 25650Sstevel@tonic-gate 25660Sstevel@tonic-gate if ((hubd->h_init_state & HUBD_LOCKS_DONE) == 0) { 25670Sstevel@tonic-gate goto done; 25680Sstevel@tonic-gate } 25690Sstevel@tonic-gate 25700Sstevel@tonic-gate /* ensure we are the only one active */ 25710Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 25720Sstevel@tonic-gate 25730Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 25740Sstevel@tonic-gate 25750Sstevel@tonic-gate /* Cleanup failure is only allowed if called from detach */ 25760Sstevel@tonic-gate if (DEVI_IS_DETACHING(dip)) { 25770Sstevel@tonic-gate dev_info_t *rh_dip = hubd->h_usba_device->usb_root_hub_dip; 25780Sstevel@tonic-gate 25790Sstevel@tonic-gate /* 25800Sstevel@tonic-gate * We are being called from detach. 25810Sstevel@tonic-gate * Fail immediately if the hotplug thread is running 25820Sstevel@tonic-gate * else set the dev_state to disconnected so that 25830Sstevel@tonic-gate * hotplug thread just exits without doing anything. 25840Sstevel@tonic-gate */ 25850Sstevel@tonic-gate if (hubd->h_bus_ctls || hubd->h_bus_pwr || 25860Sstevel@tonic-gate hubd->h_hotplug_thread) { 25870Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 25880Sstevel@tonic-gate ndi_devi_exit(dip, circ); 25890Sstevel@tonic-gate 25900Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 25910Sstevel@tonic-gate "hubd_cleanup: hotplug thread/bus ctl active " 25920Sstevel@tonic-gate "- failing detach"); 25930Sstevel@tonic-gate 25940Sstevel@tonic-gate return (USB_FAILURE); 25950Sstevel@tonic-gate } 25960Sstevel@tonic-gate 25970Sstevel@tonic-gate /* 25980Sstevel@tonic-gate * if the deathrow thread is still active or about 25990Sstevel@tonic-gate * to become active, fail detach 26000Sstevel@tonic-gate * the roothup can only be detached if nexus drivers 26010Sstevel@tonic-gate * are unloaded or explicitly offlined 26020Sstevel@tonic-gate */ 26030Sstevel@tonic-gate if (rh_dip == dip) { 26040Sstevel@tonic-gate if (hubd->h_cleanup_needed || 26050Sstevel@tonic-gate hubd->h_cleanup_active) { 26060Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 26070Sstevel@tonic-gate ndi_devi_exit(dip, circ); 26080Sstevel@tonic-gate 26090Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, 26100Sstevel@tonic-gate hubd->h_log_handle, 26110Sstevel@tonic-gate "hubd_cleanup: deathrow still active?" 26120Sstevel@tonic-gate "- failing detach"); 26130Sstevel@tonic-gate 26140Sstevel@tonic-gate return (USB_FAILURE); 26150Sstevel@tonic-gate } 26160Sstevel@tonic-gate } 26170Sstevel@tonic-gate } 26180Sstevel@tonic-gate 26190Sstevel@tonic-gate old_dev_state = hubd->h_dev_state; 26200Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_DISCONNECTED; 26210Sstevel@tonic-gate 26220Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 26230Sstevel@tonic-gate "hubd_cleanup: stop polling"); 26240Sstevel@tonic-gate hubd_close_intr_pipe(hubd); 26250Sstevel@tonic-gate 26260Sstevel@tonic-gate ASSERT((hubd->h_bus_ctls || hubd->h_bus_pwr || 26270Sstevel@tonic-gate hubd->h_hotplug_thread) == 0); 26280Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 26290Sstevel@tonic-gate 26300Sstevel@tonic-gate /* 26310Sstevel@tonic-gate * deallocate events, if events are still registered 26320Sstevel@tonic-gate * (ie. children still attached) then we have to fail the detach 26330Sstevel@tonic-gate */ 26340Sstevel@tonic-gate if (hubd->h_ndi_event_hdl) { 26350Sstevel@tonic-gate 26360Sstevel@tonic-gate rval = ndi_event_free_hdl(hubd->h_ndi_event_hdl); 26370Sstevel@tonic-gate if (DEVI_IS_ATTACHING(dip)) { 26380Sstevel@tonic-gate 26390Sstevel@tonic-gate /* It must return success if attaching. */ 26400Sstevel@tonic-gate ASSERT(rval == NDI_SUCCESS); 26410Sstevel@tonic-gate 26420Sstevel@tonic-gate } else if (rval != NDI_SUCCESS) { 26430Sstevel@tonic-gate 2644978Sfrits USB_DPRINTF_L2(DPRINT_MASK_ALL, hubd->h_log_handle, 26450Sstevel@tonic-gate "hubd_cleanup: ndi_event_free_hdl failed"); 26460Sstevel@tonic-gate ndi_devi_exit(dip, circ); 26470Sstevel@tonic-gate 26480Sstevel@tonic-gate return (USB_FAILURE); 26490Sstevel@tonic-gate 26500Sstevel@tonic-gate } 26510Sstevel@tonic-gate } 26520Sstevel@tonic-gate 26530Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 26540Sstevel@tonic-gate 26551001Ssl147100 if (hubd->h_init_state & HUBD_CHILDREN_CREATED) { 26560Sstevel@tonic-gate #ifdef DEBUG 26571001Ssl147100 for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { 26581001Ssl147100 ASSERT(hubd->h_usba_devices[port] == NULL); 26591001Ssl147100 ASSERT(hubd->h_children_dips[port] == NULL); 26601001Ssl147100 } 26610Sstevel@tonic-gate #endif 26621001Ssl147100 kmem_free(hubd->h_children_dips, hubd->h_cd_list_length); 26631001Ssl147100 kmem_free(hubd->h_usba_devices, hubd->h_cd_list_length); 26641001Ssl147100 } 26650Sstevel@tonic-gate 26660Sstevel@tonic-gate /* 26670Sstevel@tonic-gate * Disable the event callbacks first, after this point, event 26680Sstevel@tonic-gate * callbacks will never get called. Note we shouldn't hold 26690Sstevel@tonic-gate * mutex while unregistering events because there may be a 26700Sstevel@tonic-gate * competing event callback thread. Event callbacks are done 26710Sstevel@tonic-gate * with ndi mutex held and this can cause a potential deadlock. 26720Sstevel@tonic-gate * Note that cleanup can't fail after deregistration of events. 26730Sstevel@tonic-gate */ 26740Sstevel@tonic-gate if (hubd->h_init_state & HUBD_EVENTS_REGISTERED) { 26750Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 26760Sstevel@tonic-gate usb_unregister_event_cbs(dip, &hubd_events); 26770Sstevel@tonic-gate hubd_unregister_cpr_callback(hubd); 26780Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 26790Sstevel@tonic-gate } 26800Sstevel@tonic-gate 26810Sstevel@tonic-gate /* restore the old dev state so that device can be put into low power */ 26820Sstevel@tonic-gate hubd->h_dev_state = old_dev_state; 26830Sstevel@tonic-gate hubpm = hubd->h_hubpm; 26840Sstevel@tonic-gate 26850Sstevel@tonic-gate if ((hubpm) && (hubd->h_dev_state != USB_DEV_DISCONNECTED)) { 26860Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, dip, 0); 26870Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 26880Sstevel@tonic-gate if (hubd->h_hubpm->hubp_wakeup_enabled) { 26890Sstevel@tonic-gate /* 26900Sstevel@tonic-gate * Bring the hub to full power before 26910Sstevel@tonic-gate * issuing the disable remote wakeup command 26920Sstevel@tonic-gate */ 26930Sstevel@tonic-gate (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); 26940Sstevel@tonic-gate 26950Sstevel@tonic-gate if ((rval = usb_handle_remote_wakeup(hubd->h_dip, 26960Sstevel@tonic-gate USB_REMOTE_WAKEUP_DISABLE)) != USB_SUCCESS) { 26970Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, 26980Sstevel@tonic-gate hubd->h_log_handle, 26990Sstevel@tonic-gate "hubd_cleanup: disable remote wakeup " 27000Sstevel@tonic-gate "fails=%d", rval); 27010Sstevel@tonic-gate } 27020Sstevel@tonic-gate } 27030Sstevel@tonic-gate 27040Sstevel@tonic-gate (void) pm_lower_power(hubd->h_dip, 0, USB_DEV_OS_PWR_OFF); 27050Sstevel@tonic-gate 27060Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 27070Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, dip, 0); 27080Sstevel@tonic-gate } 27090Sstevel@tonic-gate 27100Sstevel@tonic-gate if (hubpm) { 27110Sstevel@tonic-gate if (hubpm->hubp_child_pwrstate) { 27120Sstevel@tonic-gate kmem_free(hubpm->hubp_child_pwrstate, 27130Sstevel@tonic-gate MAX_PORTS + 1); 27140Sstevel@tonic-gate } 27150Sstevel@tonic-gate kmem_free(hubpm, sizeof (hub_power_t)); 27160Sstevel@tonic-gate } 27170Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 27180Sstevel@tonic-gate 27190Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 27200Sstevel@tonic-gate "hubd_cleanup: freeing space"); 27210Sstevel@tonic-gate 27220Sstevel@tonic-gate if (hubd->h_init_state & HUBD_HUBDI_REGISTERED) { 27230Sstevel@tonic-gate rval = usba_hubdi_unregister(dip); 27240Sstevel@tonic-gate ASSERT(rval == USB_SUCCESS); 27250Sstevel@tonic-gate } 27260Sstevel@tonic-gate 27270Sstevel@tonic-gate if (hubd->h_init_state & HUBD_LOCKS_DONE) { 27280Sstevel@tonic-gate mutex_destroy(HUBD_MUTEX(hubd)); 27290Sstevel@tonic-gate cv_destroy(&hubd->h_cv_reset_port); 27300Sstevel@tonic-gate } 27310Sstevel@tonic-gate 27320Sstevel@tonic-gate ndi_devi_exit(dip, circ); 27330Sstevel@tonic-gate 27340Sstevel@tonic-gate if (hubd->h_init_state & HUBD_MINOR_NODE_CREATED) { 27350Sstevel@tonic-gate ddi_remove_minor_node(dip, NULL); 27360Sstevel@tonic-gate } 27370Sstevel@tonic-gate 27380Sstevel@tonic-gate if (usba_is_root_hub(dip)) { 27390Sstevel@tonic-gate usb_pipe_close(dip, hubd->h_default_pipe, 27400Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL); 27410Sstevel@tonic-gate } 27420Sstevel@tonic-gate 27430Sstevel@tonic-gate done: 27440Sstevel@tonic-gate if (hubd->h_ancestry_str) { 27450Sstevel@tonic-gate kmem_free(hubd->h_ancestry_str, HUBD_APID_NAMELEN); 27460Sstevel@tonic-gate } 27470Sstevel@tonic-gate 27480Sstevel@tonic-gate usb_client_detach(dip, hubd->h_dev_data); 27490Sstevel@tonic-gate 27500Sstevel@tonic-gate usb_free_log_hdl(hubd->h_log_handle); 27510Sstevel@tonic-gate 27520Sstevel@tonic-gate if (!usba_is_root_hub(dip)) { 27530Sstevel@tonic-gate ddi_soft_state_free(hubd_statep, ddi_get_instance(dip)); 27540Sstevel@tonic-gate } 27550Sstevel@tonic-gate 27560Sstevel@tonic-gate ddi_prop_remove_all(dip); 27570Sstevel@tonic-gate 27580Sstevel@tonic-gate return (USB_SUCCESS); 27590Sstevel@tonic-gate } 27600Sstevel@tonic-gate 27610Sstevel@tonic-gate 27620Sstevel@tonic-gate /* 27632651Ssl147100 * hubd_determine_port_connection: 27642651Ssl147100 * Determine which port is in connect status but does not 27652651Ssl147100 * have connect status change bit set, and mark port change 27662651Ssl147100 * bit accordingly. 27672651Ssl147100 * This function is applied during hub attach time. 27682651Ssl147100 */ 27692651Ssl147100 static usb_port_mask_t 27702651Ssl147100 hubd_determine_port_connection(hubd_t *hubd) 27712651Ssl147100 { 27722651Ssl147100 usb_port_t port; 27732651Ssl147100 usb_hub_descr_t *hub_descr; 27742651Ssl147100 uint16_t status; 27752651Ssl147100 uint16_t change; 27762651Ssl147100 usb_port_mask_t port_change = 0; 27772651Ssl147100 27782651Ssl147100 ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 27792651Ssl147100 27802651Ssl147100 hub_descr = &hubd->h_hub_descr; 27812651Ssl147100 27822651Ssl147100 for (port = 1; port <= hub_descr->bNbrPorts; port++) { 27832651Ssl147100 27842651Ssl147100 (void) hubd_determine_port_status(hubd, port, &status, 27852651Ssl147100 &change, 0); 27862651Ssl147100 27872651Ssl147100 /* Check if port is in connect status */ 27882651Ssl147100 if (!(status & PORT_STATUS_CCS)) { 27892651Ssl147100 27902651Ssl147100 continue; 27912651Ssl147100 } 27922651Ssl147100 27932651Ssl147100 /* 27942651Ssl147100 * Check if port Connect Status Change bit has been set. 27952651Ssl147100 * If already set, the connection will be handled by 27962651Ssl147100 * intr polling callback, not during attach. 27972651Ssl147100 */ 27982651Ssl147100 if (change & PORT_CHANGE_CSC) { 27992651Ssl147100 28002651Ssl147100 continue; 28012651Ssl147100 } 28022651Ssl147100 28032651Ssl147100 port_change |= 1 << port; 28042651Ssl147100 } 28052651Ssl147100 28062651Ssl147100 return (port_change); 28072651Ssl147100 } 28082651Ssl147100 28092651Ssl147100 28102651Ssl147100 /* 28110Sstevel@tonic-gate * hubd_check_ports: 28120Sstevel@tonic-gate * - get hub descriptor 28130Sstevel@tonic-gate * - check initial port status 28140Sstevel@tonic-gate * - enable power on all ports 28150Sstevel@tonic-gate * - enable polling on ep1 28160Sstevel@tonic-gate */ 28170Sstevel@tonic-gate static int 28180Sstevel@tonic-gate hubd_check_ports(hubd_t *hubd) 28190Sstevel@tonic-gate { 28202651Ssl147100 int rval; 28212651Ssl147100 usb_port_mask_t port_change = 0; 28222651Ssl147100 hubd_hotplug_arg_t *arg; 28230Sstevel@tonic-gate 28240Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 28250Sstevel@tonic-gate 28260Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 28270Sstevel@tonic-gate "hubd_check_ports: addr=0x%x", usb_get_addr(hubd->h_dip)); 28280Sstevel@tonic-gate 28290Sstevel@tonic-gate /* 28300Sstevel@tonic-gate * First turn off all port power 28310Sstevel@tonic-gate */ 28320Sstevel@tonic-gate if ((rval = hubd_disable_all_port_power(hubd)) != USB_SUCCESS) { 28330Sstevel@tonic-gate 28340Sstevel@tonic-gate /* disable whatever was enabled */ 28350Sstevel@tonic-gate (void) hubd_disable_all_port_power(hubd); 28360Sstevel@tonic-gate 28370Sstevel@tonic-gate return (rval); 28380Sstevel@tonic-gate } 28390Sstevel@tonic-gate 28400Sstevel@tonic-gate /* 28410Sstevel@tonic-gate * do not switch on immediately (instantly on root hub) 28420Sstevel@tonic-gate * and allow time to settle 28430Sstevel@tonic-gate */ 28440Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 28450Sstevel@tonic-gate delay(drv_usectohz(10000)); 28460Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 28470Sstevel@tonic-gate 28480Sstevel@tonic-gate /* 28490Sstevel@tonic-gate * enable power on all ports so we can see connects 28500Sstevel@tonic-gate */ 28510Sstevel@tonic-gate if ((rval = hubd_enable_all_port_power(hubd)) != USB_SUCCESS) { 28520Sstevel@tonic-gate /* disable whatever was enabled */ 28530Sstevel@tonic-gate (void) hubd_disable_all_port_power(hubd); 28540Sstevel@tonic-gate 28550Sstevel@tonic-gate return (rval); 28560Sstevel@tonic-gate } 28570Sstevel@tonic-gate 28580Sstevel@tonic-gate /* wait at least 3 frames before accessing devices */ 28590Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 28600Sstevel@tonic-gate delay(drv_usectohz(10000)); 28610Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 28620Sstevel@tonic-gate 28630Sstevel@tonic-gate /* 28640Sstevel@tonic-gate * allocate arrays for saving the dips of each child per port 28650Sstevel@tonic-gate * 28660Sstevel@tonic-gate * ports go from 1 - n, allocate 1 more entry 28670Sstevel@tonic-gate */ 28680Sstevel@tonic-gate hubd->h_cd_list_length = 2869*4763Slg150142 (sizeof (dev_info_t **)) * (hubd->h_hub_descr.bNbrPorts + 1); 28700Sstevel@tonic-gate 28710Sstevel@tonic-gate hubd->h_children_dips = (dev_info_t **)kmem_zalloc( 2872*4763Slg150142 hubd->h_cd_list_length, KM_SLEEP); 28730Sstevel@tonic-gate hubd->h_usba_devices = (usba_device_t **)kmem_zalloc( 2874*4763Slg150142 hubd->h_cd_list_length, KM_SLEEP); 28750Sstevel@tonic-gate 28761001Ssl147100 hubd->h_init_state |= HUBD_CHILDREN_CREATED; 28771001Ssl147100 28782651Ssl147100 mutex_exit(HUBD_MUTEX(hubd)); 28792651Ssl147100 arg = (hubd_hotplug_arg_t *)kmem_zalloc( 28802651Ssl147100 sizeof (hubd_hotplug_arg_t), KM_SLEEP); 28812651Ssl147100 mutex_enter(HUBD_MUTEX(hubd)); 28822651Ssl147100 28832651Ssl147100 if ((rval = hubd_open_intr_pipe(hubd)) != USB_SUCCESS) { 28842651Ssl147100 kmem_free(arg, sizeof (hubd_hotplug_arg_t)); 28852651Ssl147100 28862651Ssl147100 return (rval); 28872651Ssl147100 } 28882651Ssl147100 28892651Ssl147100 hubd_start_polling(hubd, 0); 28902651Ssl147100 28912651Ssl147100 /* 28922651Ssl147100 * Some hub devices, like the embedded hub in the CKS ErgoMagic 28932651Ssl147100 * keyboard, may only have connection status bit set, but not 28942651Ssl147100 * have connect status change bit set when a device has been 28952651Ssl147100 * connected to its downstream port before the hub is enumerated. 28962651Ssl147100 * Then when the hub is in enumeration, the devices connected to 28972651Ssl147100 * it cannot be detected by the intr pipe and won't be enumerated. 28982651Ssl147100 * We need to check such situation here and enumerate the downstream 28992651Ssl147100 * devices for such hubs. 29002651Ssl147100 */ 29012651Ssl147100 port_change = hubd_determine_port_connection(hubd); 29022651Ssl147100 29032651Ssl147100 if (port_change) { 29042651Ssl147100 hubd_pm_busy_component(hubd, hubd->h_dip, 0); 29052651Ssl147100 29062651Ssl147100 arg->hubd = hubd; 29072651Ssl147100 arg->hotplug_during_attach = B_TRUE; 29082651Ssl147100 hubd->h_port_change |= port_change; 29092651Ssl147100 29102651Ssl147100 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 29112651Ssl147100 "hubd_check_ports: port change=0x%x, need to connect", 29122651Ssl147100 hubd->h_port_change); 29132651Ssl147100 29142651Ssl147100 if (usb_async_req(hubd->h_dip, hubd_hotplug_thread, 29152651Ssl147100 (void *)arg, 0) == USB_SUCCESS) { 29162651Ssl147100 hubd->h_hotplug_thread++; 29172651Ssl147100 } else { 29182651Ssl147100 /* mark this device as idle */ 29192651Ssl147100 hubd_pm_idle_component(hubd, hubd->h_dip, 0); 29202651Ssl147100 kmem_free(arg, sizeof (hubd_hotplug_arg_t)); 29212651Ssl147100 } 29222651Ssl147100 } else { 29232651Ssl147100 kmem_free(arg, sizeof (hubd_hotplug_arg_t)); 29240Sstevel@tonic-gate } 29250Sstevel@tonic-gate 29260Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 29270Sstevel@tonic-gate "hubd_check_ports done"); 29280Sstevel@tonic-gate 29292651Ssl147100 return (USB_SUCCESS); 29300Sstevel@tonic-gate } 29310Sstevel@tonic-gate 29320Sstevel@tonic-gate 29330Sstevel@tonic-gate /* 29340Sstevel@tonic-gate * hubd_get_hub_descriptor: 29350Sstevel@tonic-gate */ 29360Sstevel@tonic-gate static int 29370Sstevel@tonic-gate hubd_get_hub_descriptor(hubd_t *hubd) 29380Sstevel@tonic-gate { 29390Sstevel@tonic-gate usb_hub_descr_t *hub_descr = &hubd->h_hub_descr; 29400Sstevel@tonic-gate mblk_t *data = NULL; 29410Sstevel@tonic-gate usb_cr_t completion_reason; 29420Sstevel@tonic-gate usb_cb_flags_t cb_flags; 29430Sstevel@tonic-gate uint16_t length; 29440Sstevel@tonic-gate int rval; 29450Sstevel@tonic-gate 29460Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 29470Sstevel@tonic-gate "hubd_get_hub_descriptor:"); 29480Sstevel@tonic-gate 29490Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 29500Sstevel@tonic-gate ASSERT(hubd->h_default_pipe != 0); 29510Sstevel@tonic-gate 29520Sstevel@tonic-gate /* get hub descriptor length first by requesting 8 bytes only */ 29530Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 29540Sstevel@tonic-gate 29550Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 29560Sstevel@tonic-gate hubd->h_default_pipe, 29571001Ssl147100 HUB_CLASS_REQ_TYPE, 29580Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */ 29590Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_HUB, /* wValue */ 29600Sstevel@tonic-gate 0, /* wIndex */ 29610Sstevel@tonic-gate 8, /* wLength */ 29620Sstevel@tonic-gate &data, 0, 29630Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 29640Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 29650Sstevel@tonic-gate "get hub descriptor failed: cr=%d cb_fl=0x%x rval=%d", 29660Sstevel@tonic-gate completion_reason, cb_flags, rval); 29670Sstevel@tonic-gate freemsg(data); 29680Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 29690Sstevel@tonic-gate 29700Sstevel@tonic-gate return (rval); 29710Sstevel@tonic-gate } 29720Sstevel@tonic-gate 29730Sstevel@tonic-gate length = *(data->b_rptr); 29740Sstevel@tonic-gate 29750Sstevel@tonic-gate if (length > 8) { 29760Sstevel@tonic-gate freemsg(data); 29770Sstevel@tonic-gate data = NULL; 29780Sstevel@tonic-gate 29790Sstevel@tonic-gate /* get complete hub descriptor */ 29800Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 29810Sstevel@tonic-gate hubd->h_default_pipe, 29821001Ssl147100 HUB_CLASS_REQ_TYPE, 29830Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */ 29840Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_HUB, /* wValue */ 29850Sstevel@tonic-gate 0, /* wIndex */ 29860Sstevel@tonic-gate length, /* wLength */ 29870Sstevel@tonic-gate &data, 0, 29880Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 29890Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 29900Sstevel@tonic-gate "get hub descriptor failed: " 29910Sstevel@tonic-gate "cr=%d cb_fl=0x%x rval=%d", 29920Sstevel@tonic-gate completion_reason, cb_flags, rval); 29930Sstevel@tonic-gate freemsg(data); 29940Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 29950Sstevel@tonic-gate 29960Sstevel@tonic-gate return (rval); 29970Sstevel@tonic-gate } 29980Sstevel@tonic-gate } 29990Sstevel@tonic-gate 30000Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 30010Sstevel@tonic-gate 30020Sstevel@tonic-gate /* parse the hub descriptor */ 30030Sstevel@tonic-gate /* only 32 ports are supported at present */ 30040Sstevel@tonic-gate ASSERT(*(data->b_rptr + 2) <= 32); 30050Sstevel@tonic-gate if (usb_parse_CV_descr("cccscccccc", 30060Sstevel@tonic-gate data->b_rptr, data->b_wptr - data->b_rptr, 30070Sstevel@tonic-gate (void *)hub_descr, sizeof (usb_hub_descr_t)) == 0) { 30080Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 30090Sstevel@tonic-gate "parsing hub descriptor failed"); 30100Sstevel@tonic-gate 30110Sstevel@tonic-gate freemsg(data); 30120Sstevel@tonic-gate 30130Sstevel@tonic-gate return (USB_FAILURE); 30140Sstevel@tonic-gate } 30150Sstevel@tonic-gate 30160Sstevel@tonic-gate freemsg(data); 30170Sstevel@tonic-gate 30180Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 30191001Ssl147100 "rval=0x%x bNbrPorts=0x%x wHubChars=0x%x " 30201001Ssl147100 "PwrOn2PwrGood=0x%x HubContrCurrent=%dmA", rval, 30210Sstevel@tonic-gate hub_descr->bNbrPorts, hub_descr->wHubCharacteristics, 30221001Ssl147100 hub_descr->bPwrOn2PwrGood, hub_descr->bHubContrCurrent); 30230Sstevel@tonic-gate 30240Sstevel@tonic-gate if (hub_descr->bNbrPorts > MAX_PORTS) { 30250Sstevel@tonic-gate USB_DPRINTF_L0(DPRINT_MASK_ATTA, hubd->h_log_handle, 30260Sstevel@tonic-gate "Hub driver supports max of %d ports on hub. " 30270Sstevel@tonic-gate "Hence using the first %d port of %d ports available", 30280Sstevel@tonic-gate MAX_PORTS, MAX_PORTS, hub_descr->bNbrPorts); 30290Sstevel@tonic-gate 30300Sstevel@tonic-gate hub_descr->bNbrPorts = MAX_PORTS; 30310Sstevel@tonic-gate } 30320Sstevel@tonic-gate 30330Sstevel@tonic-gate return (USB_SUCCESS); 30340Sstevel@tonic-gate } 30350Sstevel@tonic-gate 30360Sstevel@tonic-gate 30370Sstevel@tonic-gate /* 30381001Ssl147100 * hubd_get_hub_status_words: 30391001Ssl147100 */ 30401001Ssl147100 static int 30411001Ssl147100 hubd_get_hub_status_words(hubd_t *hubd, uint16_t *status) 30421001Ssl147100 { 30431001Ssl147100 usb_cr_t completion_reason; 30441001Ssl147100 usb_cb_flags_t cb_flags; 30451001Ssl147100 mblk_t *data = NULL; 30461001Ssl147100 30471001Ssl147100 ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 30481001Ssl147100 30491001Ssl147100 mutex_exit(HUBD_MUTEX(hubd)); 30501001Ssl147100 30511001Ssl147100 if (usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe, 30521001Ssl147100 HUB_CLASS_REQ_TYPE, 30531001Ssl147100 USB_REQ_GET_STATUS, 30541001Ssl147100 0, 30551001Ssl147100 0, 30561001Ssl147100 GET_STATUS_LENGTH, 30571001Ssl147100 &data, 0, 30581001Ssl147100 &completion_reason, &cb_flags, 0) != USB_SUCCESS) { 30591001Ssl147100 USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle, 30601001Ssl147100 "get hub status failed: cr=%d cb=0x%x", 30611001Ssl147100 completion_reason, cb_flags); 30621001Ssl147100 30631001Ssl147100 if (data) { 30641001Ssl147100 freemsg(data); 30651001Ssl147100 } 30661001Ssl147100 30671001Ssl147100 mutex_enter(HUBD_MUTEX(hubd)); 30681001Ssl147100 30691001Ssl147100 return (USB_FAILURE); 30701001Ssl147100 } 30711001Ssl147100 30721001Ssl147100 mutex_enter(HUBD_MUTEX(hubd)); 30731001Ssl147100 30741001Ssl147100 status[0] = (*(data->b_rptr + 1) << 8) | *(data->b_rptr); 30751001Ssl147100 status[1] = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2); 30761001Ssl147100 30771001Ssl147100 USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle, 30781001Ssl147100 "hub status=0x%x change=0x%x", status[0], status[1]); 30791001Ssl147100 30801001Ssl147100 freemsg(data); 30811001Ssl147100 30821001Ssl147100 return (USB_SUCCESS); 30831001Ssl147100 } 30841001Ssl147100 30851001Ssl147100 30861001Ssl147100 /* 30870Sstevel@tonic-gate * hubd_open_intr_pipe: 30880Sstevel@tonic-gate * we read all descriptors first for curiosity and then simply 30890Sstevel@tonic-gate * open the pipe 30900Sstevel@tonic-gate */ 30910Sstevel@tonic-gate static int 30920Sstevel@tonic-gate hubd_open_intr_pipe(hubd_t *hubd) 30930Sstevel@tonic-gate { 30940Sstevel@tonic-gate int rval; 30950Sstevel@tonic-gate 30960Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 30970Sstevel@tonic-gate "hubd_open_intr_pipe:"); 30980Sstevel@tonic-gate 30990Sstevel@tonic-gate ASSERT(hubd->h_intr_pipe_state == HUBD_INTR_PIPE_IDLE); 31000Sstevel@tonic-gate 31010Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_OPENING; 31020Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 31030Sstevel@tonic-gate 31040Sstevel@tonic-gate if ((rval = usb_pipe_open(hubd->h_dip, 31050Sstevel@tonic-gate &hubd->h_ep1_descr, &hubd->h_pipe_policy, 31060Sstevel@tonic-gate 0, &hubd->h_ep1_ph)) != USB_SUCCESS) { 3107978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle, 31080Sstevel@tonic-gate "open intr pipe failed (%d)", rval); 31090Sstevel@tonic-gate 31100Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 31110Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_IDLE; 31120Sstevel@tonic-gate 31130Sstevel@tonic-gate return (rval); 31140Sstevel@tonic-gate } 31150Sstevel@tonic-gate 31160Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 31170Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_ACTIVE; 31180Sstevel@tonic-gate 31190Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 31200Sstevel@tonic-gate "open intr pipe succeeded, ph=0x%p", hubd->h_ep1_ph); 31210Sstevel@tonic-gate 31220Sstevel@tonic-gate return (USB_SUCCESS); 31230Sstevel@tonic-gate } 31240Sstevel@tonic-gate 31250Sstevel@tonic-gate 31260Sstevel@tonic-gate /* 31270Sstevel@tonic-gate * hubd_start_polling: 31280Sstevel@tonic-gate * start or restart the polling 31290Sstevel@tonic-gate */ 31300Sstevel@tonic-gate static void 31310Sstevel@tonic-gate hubd_start_polling(hubd_t *hubd, int always) 31320Sstevel@tonic-gate { 31330Sstevel@tonic-gate usb_intr_req_t *reqp; 31340Sstevel@tonic-gate int rval; 31350Sstevel@tonic-gate usb_pipe_state_t pipe_state; 31360Sstevel@tonic-gate 31370Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 31380Sstevel@tonic-gate "start polling: always=%d dev_state=%d pipe_state=%d\n\t" 31390Sstevel@tonic-gate "thread=%d ep1_ph=0x%p", 31400Sstevel@tonic-gate always, hubd->h_dev_state, hubd->h_intr_pipe_state, 31410Sstevel@tonic-gate hubd->h_hotplug_thread, hubd->h_ep1_ph); 31420Sstevel@tonic-gate 31430Sstevel@tonic-gate /* 31440Sstevel@tonic-gate * start or restart polling on the intr pipe 31450Sstevel@tonic-gate * only if hotplug thread is not running 31460Sstevel@tonic-gate */ 31470Sstevel@tonic-gate if ((always == HUBD_ALWAYS_START_POLLING) || 31480Sstevel@tonic-gate ((hubd->h_dev_state == USB_DEV_ONLINE) && 31490Sstevel@tonic-gate (hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE) && 31500Sstevel@tonic-gate (hubd->h_hotplug_thread == 0) && hubd->h_ep1_ph)) { 31510Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 31520Sstevel@tonic-gate "start polling requested"); 31530Sstevel@tonic-gate 31540Sstevel@tonic-gate reqp = usb_alloc_intr_req(hubd->h_dip, 0, USB_FLAGS_SLEEP); 31550Sstevel@tonic-gate 31560Sstevel@tonic-gate reqp->intr_client_private = (usb_opaque_t)hubd; 31570Sstevel@tonic-gate reqp->intr_attributes = USB_ATTRS_SHORT_XFER_OK | 3158*4763Slg150142 USB_ATTRS_AUTOCLEARING; 31590Sstevel@tonic-gate reqp->intr_len = hubd->h_ep1_descr.wMaxPacketSize; 31600Sstevel@tonic-gate reqp->intr_cb = hubd_read_cb; 31610Sstevel@tonic-gate reqp->intr_exc_cb = hubd_exception_cb; 31620Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 31630Sstevel@tonic-gate if ((rval = usb_pipe_intr_xfer(hubd->h_ep1_ph, reqp, 31640Sstevel@tonic-gate USB_FLAGS_SLEEP)) != USB_SUCCESS) { 31650Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle, 31660Sstevel@tonic-gate "start polling failed, rval=%d", rval); 31670Sstevel@tonic-gate usb_free_intr_req(reqp); 31680Sstevel@tonic-gate } 31690Sstevel@tonic-gate 31700Sstevel@tonic-gate rval = usb_pipe_get_state(hubd->h_ep1_ph, &pipe_state, 3171*4763Slg150142 USB_FLAGS_SLEEP); 31720Sstevel@tonic-gate if (pipe_state != USB_PIPE_STATE_ACTIVE) { 31730Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 31740Sstevel@tonic-gate "intr pipe state=%d, rval=%d", pipe_state, rval); 31750Sstevel@tonic-gate } 31760Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 31770Sstevel@tonic-gate "start polling request 0x%p", reqp); 31780Sstevel@tonic-gate 31790Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 31800Sstevel@tonic-gate } 31810Sstevel@tonic-gate } 31820Sstevel@tonic-gate 31830Sstevel@tonic-gate 31840Sstevel@tonic-gate /* 31850Sstevel@tonic-gate * hubd_stop_polling 31860Sstevel@tonic-gate * stop polling but do not close the pipe 31870Sstevel@tonic-gate */ 31880Sstevel@tonic-gate static void 31890Sstevel@tonic-gate hubd_stop_polling(hubd_t *hubd) 31900Sstevel@tonic-gate { 31910Sstevel@tonic-gate int rval; 31920Sstevel@tonic-gate usb_pipe_state_t pipe_state; 31930Sstevel@tonic-gate 31940Sstevel@tonic-gate if (hubd->h_ep1_ph) { 31950Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 31960Sstevel@tonic-gate "hubd_stop_polling:"); 31970Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_STOPPED; 31980Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 31990Sstevel@tonic-gate 32000Sstevel@tonic-gate usb_pipe_stop_intr_polling(hubd->h_ep1_ph, USB_FLAGS_SLEEP); 32010Sstevel@tonic-gate rval = usb_pipe_get_state(hubd->h_ep1_ph, &pipe_state, 3202*4763Slg150142 USB_FLAGS_SLEEP); 32030Sstevel@tonic-gate 32040Sstevel@tonic-gate if (pipe_state != USB_PIPE_STATE_IDLE) { 32050Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 32060Sstevel@tonic-gate "intr pipe state=%d, rval=%d", pipe_state, rval); 32070Sstevel@tonic-gate } 32080Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 32090Sstevel@tonic-gate if (hubd->h_intr_pipe_state == HUBD_INTR_PIPE_STOPPED) { 32100Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_ACTIVE; 32110Sstevel@tonic-gate } 32120Sstevel@tonic-gate } 32130Sstevel@tonic-gate } 32140Sstevel@tonic-gate 32150Sstevel@tonic-gate 32160Sstevel@tonic-gate /* 32170Sstevel@tonic-gate * hubd_close_intr_pipe: 32180Sstevel@tonic-gate * close the pipe (which also stops the polling 32190Sstevel@tonic-gate * and wait for the hotplug thread to exit 32200Sstevel@tonic-gate */ 32210Sstevel@tonic-gate static void 32220Sstevel@tonic-gate hubd_close_intr_pipe(hubd_t *hubd) 32230Sstevel@tonic-gate { 32240Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 32250Sstevel@tonic-gate "hubd_close_intr_pipe:"); 32260Sstevel@tonic-gate 32270Sstevel@tonic-gate /* 32280Sstevel@tonic-gate * Now that no async operation is outstanding on pipe, 32290Sstevel@tonic-gate * we can change the state to HUBD_INTR_PIPE_CLOSING 32300Sstevel@tonic-gate */ 32310Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_CLOSING; 32320Sstevel@tonic-gate 32330Sstevel@tonic-gate ASSERT(hubd->h_hotplug_thread == 0); 32340Sstevel@tonic-gate 32350Sstevel@tonic-gate if (hubd->h_ep1_ph) { 32360Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 32370Sstevel@tonic-gate usb_pipe_close(hubd->h_dip, hubd->h_ep1_ph, USB_FLAGS_SLEEP, 3238*4763Slg150142 NULL, NULL); 32390Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 32400Sstevel@tonic-gate hubd->h_ep1_ph = NULL; 32410Sstevel@tonic-gate } 32420Sstevel@tonic-gate 32430Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_IDLE; 32440Sstevel@tonic-gate } 32450Sstevel@tonic-gate 32460Sstevel@tonic-gate 32470Sstevel@tonic-gate /* 32480Sstevel@tonic-gate * hubd_exception_cb 32490Sstevel@tonic-gate * interrupt ep1 exception callback function. 32500Sstevel@tonic-gate * this callback executes in taskq thread context and assumes 32510Sstevel@tonic-gate * autoclearing 32520Sstevel@tonic-gate */ 32530Sstevel@tonic-gate /*ARGSUSED*/ 32540Sstevel@tonic-gate static void 32550Sstevel@tonic-gate hubd_exception_cb(usb_pipe_handle_t pipe, usb_intr_req_t *reqp) 32560Sstevel@tonic-gate { 32570Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)(reqp->intr_client_private); 32580Sstevel@tonic-gate 32590Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CALLBACK, hubd->h_log_handle, 32600Sstevel@tonic-gate "hubd_exception_cb: " 32610Sstevel@tonic-gate "req=0x%p cr=%d data=0x%p cb_flags=0x%x", reqp, 32620Sstevel@tonic-gate reqp->intr_completion_reason, reqp->intr_data, 32630Sstevel@tonic-gate reqp->intr_cb_flags); 32640Sstevel@tonic-gate 32650Sstevel@tonic-gate ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0); 32660Sstevel@tonic-gate 32670Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 32680Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0); 32690Sstevel@tonic-gate 32700Sstevel@tonic-gate switch (reqp->intr_completion_reason) { 32710Sstevel@tonic-gate case USB_CR_PIPE_RESET: 32720Sstevel@tonic-gate /* only restart polling after autoclearing */ 32730Sstevel@tonic-gate if ((hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE) && 32740Sstevel@tonic-gate (hubd->h_port_reset_wait == 0)) { 32750Sstevel@tonic-gate hubd_start_polling(hubd, 0); 32760Sstevel@tonic-gate } 32770Sstevel@tonic-gate 32780Sstevel@tonic-gate break; 32790Sstevel@tonic-gate case USB_CR_DEV_NOT_RESP: 32800Sstevel@tonic-gate case USB_CR_STOPPED_POLLING: 32810Sstevel@tonic-gate case USB_CR_PIPE_CLOSING: 32820Sstevel@tonic-gate case USB_CR_UNSPECIFIED_ERR: 32830Sstevel@tonic-gate /* never restart polling on these conditions */ 32840Sstevel@tonic-gate default: 32850Sstevel@tonic-gate /* for all others, wait for the autoclearing PIPE_RESET cb */ 32860Sstevel@tonic-gate 32870Sstevel@tonic-gate break; 32880Sstevel@tonic-gate } 32890Sstevel@tonic-gate 32900Sstevel@tonic-gate usb_free_intr_req(reqp); 32910Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0); 32920Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 32930Sstevel@tonic-gate } 32940Sstevel@tonic-gate 32950Sstevel@tonic-gate 32960Sstevel@tonic-gate /* 32970Sstevel@tonic-gate * helper function to convert LE bytes to a portmask 32980Sstevel@tonic-gate */ 32990Sstevel@tonic-gate static usb_port_mask_t 33000Sstevel@tonic-gate hubd_mblk2portmask(mblk_t *data) 33010Sstevel@tonic-gate { 33020Sstevel@tonic-gate int len = min(data->b_wptr - data->b_rptr, sizeof (usb_port_mask_t)); 33030Sstevel@tonic-gate usb_port_mask_t rval = 0; 33040Sstevel@tonic-gate int i; 33050Sstevel@tonic-gate 33060Sstevel@tonic-gate for (i = 0; i < len; i++) { 33070Sstevel@tonic-gate rval |= data->b_rptr[i] << (i * 8); 33080Sstevel@tonic-gate } 33090Sstevel@tonic-gate 33100Sstevel@tonic-gate return (rval); 33110Sstevel@tonic-gate } 33120Sstevel@tonic-gate 33130Sstevel@tonic-gate 33140Sstevel@tonic-gate /* 33150Sstevel@tonic-gate * hubd_read_cb: 33160Sstevel@tonic-gate * interrupt ep1 callback function 33170Sstevel@tonic-gate * 33180Sstevel@tonic-gate * the status indicates just a change on the pipe with no indication 33190Sstevel@tonic-gate * of what the change was 33200Sstevel@tonic-gate * 33210Sstevel@tonic-gate * known conditions: 33220Sstevel@tonic-gate * - reset port completion 33230Sstevel@tonic-gate * - connect 33240Sstevel@tonic-gate * - disconnect 33250Sstevel@tonic-gate * 33260Sstevel@tonic-gate * for handling the hotplugging, create a new thread that can do 33270Sstevel@tonic-gate * synchronous usba calls 33280Sstevel@tonic-gate */ 33290Sstevel@tonic-gate static void 33300Sstevel@tonic-gate hubd_read_cb(usb_pipe_handle_t pipe, usb_intr_req_t *reqp) 33310Sstevel@tonic-gate { 33320Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)(reqp->intr_client_private); 33330Sstevel@tonic-gate size_t length; 33340Sstevel@tonic-gate mblk_t *data = reqp->intr_data; 33352651Ssl147100 int mem_flag = 0; 33362651Ssl147100 hubd_hotplug_arg_t *arg; 33370Sstevel@tonic-gate 33380Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 33390Sstevel@tonic-gate "hubd_read_cb: ph=0x%p req=0x%p", pipe, reqp); 33400Sstevel@tonic-gate 33410Sstevel@tonic-gate ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0); 33420Sstevel@tonic-gate 33430Sstevel@tonic-gate /* 33440Sstevel@tonic-gate * At present, we are not handling notification for completion of 33450Sstevel@tonic-gate * asynchronous pipe reset, for which this data ptr could be NULL 33460Sstevel@tonic-gate */ 33470Sstevel@tonic-gate 33480Sstevel@tonic-gate if (data == NULL) { 33490Sstevel@tonic-gate usb_free_intr_req(reqp); 33500Sstevel@tonic-gate 33510Sstevel@tonic-gate return; 33520Sstevel@tonic-gate } 33530Sstevel@tonic-gate 33542651Ssl147100 arg = (hubd_hotplug_arg_t *)kmem_zalloc( 33552651Ssl147100 sizeof (hubd_hotplug_arg_t), KM_SLEEP); 33562651Ssl147100 mem_flag = 1; 33572651Ssl147100 33580Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 33590Sstevel@tonic-gate 33600Sstevel@tonic-gate if ((hubd->h_dev_state == USB_DEV_SUSPENDED) || 33610Sstevel@tonic-gate (hubd->h_intr_pipe_state != HUBD_INTR_PIPE_ACTIVE)) { 33620Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 33630Sstevel@tonic-gate usb_free_intr_req(reqp); 33642651Ssl147100 kmem_free(arg, sizeof (hubd_hotplug_arg_t)); 33650Sstevel@tonic-gate 33660Sstevel@tonic-gate return; 33670Sstevel@tonic-gate } 33680Sstevel@tonic-gate 33690Sstevel@tonic-gate ASSERT(hubd->h_ep1_ph == pipe); 33700Sstevel@tonic-gate 33710Sstevel@tonic-gate length = data->b_wptr - data->b_rptr; 33720Sstevel@tonic-gate 33730Sstevel@tonic-gate /* 33740Sstevel@tonic-gate * Only look at the data and startup the hotplug thread if 33750Sstevel@tonic-gate * there actually is data. 33760Sstevel@tonic-gate */ 33770Sstevel@tonic-gate if (length != 0) { 33780Sstevel@tonic-gate usb_port_mask_t port_change = hubd_mblk2portmask(data); 33790Sstevel@tonic-gate 33800Sstevel@tonic-gate /* 33810Sstevel@tonic-gate * if a port change was already reported and we are waiting for 33820Sstevel@tonic-gate * reset port completion then wake up the hotplug thread which 33830Sstevel@tonic-gate * should be waiting on reset port completion 33840Sstevel@tonic-gate * 33850Sstevel@tonic-gate * if there is disconnect event instead of reset completion, let 33860Sstevel@tonic-gate * the hotplug thread figure this out 33870Sstevel@tonic-gate */ 33880Sstevel@tonic-gate 33890Sstevel@tonic-gate /* remove the reset wait bits from the status */ 33900Sstevel@tonic-gate hubd->h_port_change |= port_change & 3391*4763Slg150142 ~hubd->h_port_reset_wait; 33920Sstevel@tonic-gate 33930Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_CALLBACK, hubd->h_log_handle, 33940Sstevel@tonic-gate "port change=0x%x port_reset_wait=0x%x", 33950Sstevel@tonic-gate hubd->h_port_change, hubd->h_port_reset_wait); 33960Sstevel@tonic-gate 33970Sstevel@tonic-gate /* there should be only one reset bit active at the time */ 33980Sstevel@tonic-gate if (hubd->h_port_reset_wait & port_change) { 33990Sstevel@tonic-gate hubd->h_port_reset_wait = 0; 34000Sstevel@tonic-gate cv_signal(&hubd->h_cv_reset_port); 34010Sstevel@tonic-gate } 34020Sstevel@tonic-gate 34030Sstevel@tonic-gate /* 34040Sstevel@tonic-gate * kick off the thread only if device is ONLINE and it is not 34050Sstevel@tonic-gate * during attaching or detaching 34060Sstevel@tonic-gate */ 34070Sstevel@tonic-gate if ((hubd->h_dev_state == USB_DEV_ONLINE) && 34080Sstevel@tonic-gate (!DEVI_IS_ATTACHING(hubd->h_dip)) && 34090Sstevel@tonic-gate (!DEVI_IS_DETACHING(hubd->h_dip)) && 34100Sstevel@tonic-gate (hubd->h_port_change) && 34110Sstevel@tonic-gate (hubd->h_hotplug_thread == 0)) { 34120Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_CALLBACK, hubd->h_log_handle, 34130Sstevel@tonic-gate "creating hotplug thread: " 34140Sstevel@tonic-gate "dev_state=%d", hubd->h_dev_state); 34150Sstevel@tonic-gate 34160Sstevel@tonic-gate /* 34170Sstevel@tonic-gate * Mark this device as busy. The will be marked idle 34180Sstevel@tonic-gate * if the async req fails or at the exit of hotplug 34190Sstevel@tonic-gate * thread 34200Sstevel@tonic-gate */ 34210Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0); 34220Sstevel@tonic-gate 34232651Ssl147100 arg->hubd = hubd; 34242651Ssl147100 arg->hotplug_during_attach = B_FALSE; 34252651Ssl147100 34260Sstevel@tonic-gate if (usb_async_req(hubd->h_dip, 34270Sstevel@tonic-gate hubd_hotplug_thread, 34282651Ssl147100 (void *)arg, 0) == USB_SUCCESS) { 34290Sstevel@tonic-gate hubd->h_hotplug_thread++; 34302651Ssl147100 mem_flag = 0; 34310Sstevel@tonic-gate } else { 34320Sstevel@tonic-gate /* mark this device as idle */ 34330Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, 34340Sstevel@tonic-gate hubd->h_dip, 0); 34350Sstevel@tonic-gate } 34360Sstevel@tonic-gate } 34370Sstevel@tonic-gate } 34380Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 34390Sstevel@tonic-gate 34402651Ssl147100 if (mem_flag == 1) { 34412651Ssl147100 kmem_free(arg, sizeof (hubd_hotplug_arg_t)); 34422651Ssl147100 } 34432651Ssl147100 34440Sstevel@tonic-gate usb_free_intr_req(reqp); 34450Sstevel@tonic-gate } 34460Sstevel@tonic-gate 34470Sstevel@tonic-gate 34480Sstevel@tonic-gate /* 34490Sstevel@tonic-gate * hubd_hotplug_thread: 34500Sstevel@tonic-gate * handles resetting of port, and creating children 34510Sstevel@tonic-gate * 34520Sstevel@tonic-gate * the ports to check are indicated in h_port_change bit mask 34530Sstevel@tonic-gate * XXX note that one time poll doesn't work on the root hub 34540Sstevel@tonic-gate */ 34550Sstevel@tonic-gate static void 34560Sstevel@tonic-gate hubd_hotplug_thread(void *arg) 34570Sstevel@tonic-gate { 34582651Ssl147100 hubd_hotplug_arg_t *hd_arg = (hubd_hotplug_arg_t *)arg; 34592651Ssl147100 hubd_t *hubd = hd_arg->hubd; 34602651Ssl147100 boolean_t attach_flg = hd_arg->hotplug_during_attach; 34610Sstevel@tonic-gate usb_port_t port; 34620Sstevel@tonic-gate uint16_t nports; 34630Sstevel@tonic-gate uint16_t status, change; 34640Sstevel@tonic-gate hub_power_t *hubpm; 34650Sstevel@tonic-gate dev_info_t *hdip = hubd->h_dip; 34660Sstevel@tonic-gate dev_info_t *rh_dip = hubd->h_usba_device->usb_root_hub_dip; 3467*4763Slg150142 dev_info_t *child_dip; 34680Sstevel@tonic-gate boolean_t online_child = B_FALSE; 34690Sstevel@tonic-gate boolean_t offline_child = B_FALSE; 34700Sstevel@tonic-gate boolean_t pwrup_child = B_FALSE; 3471*4763Slg150142 int prh_circ, rh_circ, chld_circ, circ, old_state; 34720Sstevel@tonic-gate 34730Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 34740Sstevel@tonic-gate "hubd_hotplug_thread: started"); 34750Sstevel@tonic-gate 34762651Ssl147100 kmem_free(arg, sizeof (hubd_hotplug_arg_t)); 34772651Ssl147100 34780Sstevel@tonic-gate /* 34790Sstevel@tonic-gate * if our bus power entry point is active, process the change 34800Sstevel@tonic-gate * on the next notification of interrupt pipe 34810Sstevel@tonic-gate */ 34820Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 34830Sstevel@tonic-gate if (hubd->h_bus_pwr || (hubd->h_hotplug_thread > 1)) { 34840Sstevel@tonic-gate hubd->h_hotplug_thread--; 34850Sstevel@tonic-gate 34860Sstevel@tonic-gate /* mark this device as idle */ 34870Sstevel@tonic-gate hubd_pm_idle_component(hubd, hubd->h_dip, 0); 34880Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 34890Sstevel@tonic-gate 34900Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 34910Sstevel@tonic-gate "hubd_hotplug_thread: " 34920Sstevel@tonic-gate "bus_power in progress/hotplugging undesirable - quit"); 34930Sstevel@tonic-gate 34940Sstevel@tonic-gate return; 34950Sstevel@tonic-gate } 34960Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 34970Sstevel@tonic-gate 34980Sstevel@tonic-gate ndi_hold_devi(hdip); /* so we don't race with detach */ 34990Sstevel@tonic-gate 35000Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 35010Sstevel@tonic-gate 35020Sstevel@tonic-gate /* is this the root hub? */ 35030Sstevel@tonic-gate if (hdip == rh_dip) { 35040Sstevel@tonic-gate if (hubd->h_dev_state == USB_DEV_PWRED_DOWN) { 35050Sstevel@tonic-gate hubpm = hubd->h_hubpm; 35060Sstevel@tonic-gate 35070Sstevel@tonic-gate /* mark the root hub as full power */ 35080Sstevel@tonic-gate hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR; 35090Sstevel@tonic-gate hubpm->hubp_time_at_full_power = ddi_get_time(); 35100Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 35110Sstevel@tonic-gate 35120Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 35130Sstevel@tonic-gate "hubd_hotplug_thread: call pm_power_has_changed"); 35140Sstevel@tonic-gate 35150Sstevel@tonic-gate (void) pm_power_has_changed(hdip, 0, 3516*4763Slg150142 USB_DEV_OS_FULL_PWR); 35170Sstevel@tonic-gate 35180Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 35190Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_ONLINE; 35200Sstevel@tonic-gate } 35210Sstevel@tonic-gate 35220Sstevel@tonic-gate } else { 35230Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 35240Sstevel@tonic-gate "hubd_hotplug_thread: not root hub"); 35250Sstevel@tonic-gate } 35260Sstevel@tonic-gate 35270Sstevel@tonic-gate ASSERT(hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE); 35280Sstevel@tonic-gate 35290Sstevel@tonic-gate nports = hubd->h_hub_descr.bNbrPorts; 35300Sstevel@tonic-gate 35310Sstevel@tonic-gate hubd_stop_polling(hubd); 35320Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 35330Sstevel@tonic-gate 35340Sstevel@tonic-gate /* 35350Sstevel@tonic-gate * this ensures one hotplug activity per system at a time. 35360Sstevel@tonic-gate * we enter the parent PCI node to have this serialization. 35370Sstevel@tonic-gate * this also excludes ioctls and deathrow thread 35380Sstevel@tonic-gate * (a bit crude but easier to debug) 35390Sstevel@tonic-gate */ 35400Sstevel@tonic-gate ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ); 35410Sstevel@tonic-gate ndi_devi_enter(rh_dip, &rh_circ); 35420Sstevel@tonic-gate 35430Sstevel@tonic-gate /* exclude other threads */ 35440Sstevel@tonic-gate ndi_devi_enter(hdip, &circ); 35450Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 35460Sstevel@tonic-gate 35470Sstevel@tonic-gate while ((hubd->h_dev_state == USB_DEV_ONLINE) && 35480Sstevel@tonic-gate (hubd->h_port_change)) { 35490Sstevel@tonic-gate /* 35500Sstevel@tonic-gate * The 0th bit is the hub status change bit. 35510Sstevel@tonic-gate * handle loss of local power here 35520Sstevel@tonic-gate */ 35530Sstevel@tonic-gate if (hubd->h_port_change & HUB_CHANGE_STATUS) { 35540Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 35550Sstevel@tonic-gate "hubd_hotplug_thread: hub status change!"); 35560Sstevel@tonic-gate 35570Sstevel@tonic-gate /* 35580Sstevel@tonic-gate * This should be handled properly. For now, 35590Sstevel@tonic-gate * mask off the bit. 35600Sstevel@tonic-gate */ 35610Sstevel@tonic-gate hubd->h_port_change &= ~HUB_CHANGE_STATUS; 35620Sstevel@tonic-gate 35630Sstevel@tonic-gate /* 35640Sstevel@tonic-gate * check and ack hub status 35650Sstevel@tonic-gate * this causes stall conditions 35660Sstevel@tonic-gate * when local power is removed 35670Sstevel@tonic-gate */ 35680Sstevel@tonic-gate (void) hubd_get_hub_status(hubd); 35690Sstevel@tonic-gate } 35700Sstevel@tonic-gate 35710Sstevel@tonic-gate for (port = 1; port <= nports; port++) { 35720Sstevel@tonic-gate usb_port_mask_t port_mask; 35730Sstevel@tonic-gate boolean_t was_connected; 35740Sstevel@tonic-gate 35750Sstevel@tonic-gate port_mask = 1 << port; 35760Sstevel@tonic-gate was_connected = 3577*4763Slg150142 (hubd->h_port_state[port] & PORT_STATUS_CCS) && 3578*4763Slg150142 (hubd->h_children_dips[port]); 35790Sstevel@tonic-gate 35800Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 35810Sstevel@tonic-gate "hubd_hotplug_thread: " 35820Sstevel@tonic-gate "port %d mask=0x%x change=0x%x connected=0x%x", 35830Sstevel@tonic-gate port, port_mask, hubd->h_port_change, 35840Sstevel@tonic-gate was_connected); 35850Sstevel@tonic-gate 35860Sstevel@tonic-gate /* 35870Sstevel@tonic-gate * is this a port connection that changed? 35880Sstevel@tonic-gate */ 35890Sstevel@tonic-gate if ((hubd->h_port_change & port_mask) == 0) { 35900Sstevel@tonic-gate 35910Sstevel@tonic-gate continue; 35920Sstevel@tonic-gate } 35930Sstevel@tonic-gate hubd->h_port_change &= ~port_mask; 35940Sstevel@tonic-gate 35950Sstevel@tonic-gate /* ack all changes */ 35960Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 35970Sstevel@tonic-gate &status, &change, HUBD_ACK_ALL_CHANGES); 35980Sstevel@tonic-gate 35990Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 36000Sstevel@tonic-gate "handle port %d:\n\t" 36010Sstevel@tonic-gate "new status=0x%x change=0x%x was_conn=0x%x ", 36020Sstevel@tonic-gate port, status, change, was_connected); 36030Sstevel@tonic-gate 36040Sstevel@tonic-gate /* Recover a disabled port */ 36050Sstevel@tonic-gate if (change & PORT_CHANGE_PESC) { 36060Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, 3607*4763Slg150142 hubd->h_log_handle, 3608*4763Slg150142 "port%d Disabled - " 3609*4763Slg150142 "status=0x%x, change=0x%x", 3610*4763Slg150142 port, status, change); 36110Sstevel@tonic-gate 36120Sstevel@tonic-gate /* 36130Sstevel@tonic-gate * if the port was connected and is still 36140Sstevel@tonic-gate * connected, recover the port 36150Sstevel@tonic-gate */ 36160Sstevel@tonic-gate if (was_connected && (status & 36170Sstevel@tonic-gate PORT_STATUS_CCS)) { 36180Sstevel@tonic-gate online_child |= 36190Sstevel@tonic-gate (hubd_recover_disabled_port(hubd, 36200Sstevel@tonic-gate port) == USB_SUCCESS); 36210Sstevel@tonic-gate } 36220Sstevel@tonic-gate } 36230Sstevel@tonic-gate 36240Sstevel@tonic-gate /* 36250Sstevel@tonic-gate * Now check what changed on the port 36260Sstevel@tonic-gate */ 36272651Ssl147100 if ((change & PORT_CHANGE_CSC) || attach_flg) { 36280Sstevel@tonic-gate if ((status & PORT_STATUS_CCS) && 36290Sstevel@tonic-gate (!was_connected)) { 36300Sstevel@tonic-gate /* new device plugged in */ 36310Sstevel@tonic-gate online_child |= 36320Sstevel@tonic-gate (hubd_handle_port_connect(hubd, 36330Sstevel@tonic-gate port) == USB_SUCCESS); 36340Sstevel@tonic-gate 36350Sstevel@tonic-gate } else if ((status & PORT_STATUS_CCS) && 36360Sstevel@tonic-gate was_connected) { 36370Sstevel@tonic-gate /* 36380Sstevel@tonic-gate * In this case we can never be sure 36390Sstevel@tonic-gate * if the device indeed got hotplugged 36400Sstevel@tonic-gate * or the hub is falsely reporting the 36410Sstevel@tonic-gate * change. 3642*4763Slg150142 */ 3643*4763Slg150142 child_dip = hubd->h_children_dips[port]; 3644*4763Slg150142 3645*4763Slg150142 mutex_exit(HUBD_MUTEX(hubd)); 3646*4763Slg150142 /* 3647*4763Slg150142 * this ensures we do not race with 3648*4763Slg150142 * other threads which are detaching 3649*4763Slg150142 * the child driver at the same time. 3650*4763Slg150142 */ 3651*4763Slg150142 ndi_devi_enter(child_dip, &chld_circ); 3652*4763Slg150142 /* 3653*4763Slg150142 * Now check if the driver remains 3654*4763Slg150142 * attached. 36550Sstevel@tonic-gate */ 3656*4763Slg150142 if (i_ddi_devi_attached(child_dip)) { 3657*4763Slg150142 /* 3658*4763Slg150142 * first post a disconnect event 3659*4763Slg150142 * to the child. 3660*4763Slg150142 */ 3661*4763Slg150142 hubd_post_event(hubd, port, 3662*4763Slg150142 USBA_EVENT_TAG_HOT_REMOVAL); 3663*4763Slg150142 mutex_enter(HUBD_MUTEX(hubd)); 3664*4763Slg150142 3665*4763Slg150142 /* 3666*4763Slg150142 * then reset the port and 3667*4763Slg150142 * recover the device 3668*4763Slg150142 */ 3669*4763Slg150142 online_child |= 3670*4763Slg150142 (hubd_handle_port_connect( 3671*4763Slg150142 hubd, port) == USB_SUCCESS); 3672*4763Slg150142 3673*4763Slg150142 mutex_exit(HUBD_MUTEX(hubd)); 3674*4763Slg150142 } 3675*4763Slg150142 3676*4763Slg150142 ndi_devi_exit(child_dip, chld_circ); 36770Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 36780Sstevel@tonic-gate } else if (was_connected) { 36790Sstevel@tonic-gate /* this is a disconnect */ 36800Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 36810Sstevel@tonic-gate hubd_post_event(hubd, port, 36820Sstevel@tonic-gate USBA_EVENT_TAG_HOT_REMOVAL); 36830Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 36840Sstevel@tonic-gate 36850Sstevel@tonic-gate offline_child = B_TRUE; 36860Sstevel@tonic-gate } 36870Sstevel@tonic-gate } 36880Sstevel@tonic-gate 36890Sstevel@tonic-gate /* 36900Sstevel@tonic-gate * Check if any port is coming out of suspend 36910Sstevel@tonic-gate */ 36920Sstevel@tonic-gate if (change & PORT_CHANGE_PSSC) { 36930Sstevel@tonic-gate /* a resuming device could have disconnected */ 36940Sstevel@tonic-gate if (was_connected && 36950Sstevel@tonic-gate hubd->h_children_dips[port]) { 36960Sstevel@tonic-gate 36970Sstevel@tonic-gate /* device on this port resuming */ 36980Sstevel@tonic-gate dev_info_t *dip; 36990Sstevel@tonic-gate 37000Sstevel@tonic-gate dip = hubd->h_children_dips[port]; 37010Sstevel@tonic-gate 37020Sstevel@tonic-gate /* 37030Sstevel@tonic-gate * Don't raise power on detaching child 37040Sstevel@tonic-gate */ 37050Sstevel@tonic-gate if (!DEVI_IS_DETACHING(dip)) { 37060Sstevel@tonic-gate /* 37070Sstevel@tonic-gate * As this child is not 37080Sstevel@tonic-gate * detaching, we set this 37090Sstevel@tonic-gate * flag, causing bus_ctls 37100Sstevel@tonic-gate * to stall detach till 37110Sstevel@tonic-gate * pm_raise_power returns 37120Sstevel@tonic-gate * and flag it for a deferred 37130Sstevel@tonic-gate * raise_power. 37140Sstevel@tonic-gate * 37150Sstevel@tonic-gate * pm_raise_power is deferred 37160Sstevel@tonic-gate * because we need to release 37170Sstevel@tonic-gate * the locks first. 37180Sstevel@tonic-gate */ 37190Sstevel@tonic-gate hubd->h_port_state[port] |= 3720*4763Slg150142 HUBD_CHILD_RAISE_POWER; 37210Sstevel@tonic-gate pwrup_child = B_TRUE; 37220Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 37230Sstevel@tonic-gate 37240Sstevel@tonic-gate /* 37250Sstevel@tonic-gate * make sure that child 37260Sstevel@tonic-gate * doesn't disappear 37270Sstevel@tonic-gate */ 37280Sstevel@tonic-gate ndi_hold_devi(dip); 37290Sstevel@tonic-gate 37300Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 37310Sstevel@tonic-gate } 37320Sstevel@tonic-gate } 37330Sstevel@tonic-gate } 37342326Ssl147100 37352326Ssl147100 /* 37362326Ssl147100 * Check if the port is over-current 37372326Ssl147100 */ 37382326Ssl147100 if (change & PORT_CHANGE_OCIC) { 37392326Ssl147100 USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, 37402326Ssl147100 hubd->h_log_handle, 37412326Ssl147100 "Port%d in over current condition, " 37422326Ssl147100 "please check the attached device to " 37432326Ssl147100 "clear the condition. The system will " 37442326Ssl147100 "try to recover the port, but if not " 37452326Ssl147100 "successful, you need to re-connect " 37462326Ssl147100 "the hub or reboot the system to bring " 37472326Ssl147100 "the port back to work", port); 37482326Ssl147100 37492326Ssl147100 if (!(status & PORT_STATUS_PPS)) { 37502326Ssl147100 /* 37512326Ssl147100 * Try to enable port power, but 37522326Ssl147100 * possibly fail. Ignore failure 37532326Ssl147100 */ 37542326Ssl147100 (void) hubd_enable_port_power(hubd, 37552326Ssl147100 port); 37562326Ssl147100 37572326Ssl147100 /* 37582326Ssl147100 * Delay some time to avoid 37592326Ssl147100 * over-current event to happen 37602326Ssl147100 * too frequently in some cases 37612326Ssl147100 */ 37622326Ssl147100 mutex_exit(HUBD_MUTEX(hubd)); 37632326Ssl147100 delay(drv_usectohz(500000)); 37642326Ssl147100 mutex_enter(HUBD_MUTEX(hubd)); 37652326Ssl147100 } 37662326Ssl147100 } 37670Sstevel@tonic-gate } 37680Sstevel@tonic-gate } 37690Sstevel@tonic-gate 37700Sstevel@tonic-gate /* release locks so we can do a devfs_clean */ 37710Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 37720Sstevel@tonic-gate 37730Sstevel@tonic-gate /* delete cached dv_node's but drop locks first */ 37740Sstevel@tonic-gate ndi_devi_exit(hdip, circ); 37750Sstevel@tonic-gate ndi_devi_exit(rh_dip, rh_circ); 37760Sstevel@tonic-gate ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ); 37770Sstevel@tonic-gate 37780Sstevel@tonic-gate (void) devfs_clean(rh_dip, NULL, 0); 37790Sstevel@tonic-gate 37800Sstevel@tonic-gate /* now check if any children need onlining */ 37810Sstevel@tonic-gate if (online_child) { 37820Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 37830Sstevel@tonic-gate "hubd_hotplug_thread: onlining children"); 37840Sstevel@tonic-gate 37850Sstevel@tonic-gate (void) ndi_devi_online(hubd->h_dip, 0); 37860Sstevel@tonic-gate } 37870Sstevel@tonic-gate 37880Sstevel@tonic-gate /* now check if any disconnected devices need to be cleaned up */ 37890Sstevel@tonic-gate if (offline_child) { 37900Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 37910Sstevel@tonic-gate "hubd_hotplug_thread: scheduling cleanup"); 37920Sstevel@tonic-gate 37930Sstevel@tonic-gate hubd_schedule_cleanup(hubd->h_usba_device->usb_root_hub_dip); 37940Sstevel@tonic-gate } 37950Sstevel@tonic-gate 37960Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 37970Sstevel@tonic-gate 37980Sstevel@tonic-gate /* now raise power on the children that have woken up */ 37990Sstevel@tonic-gate if (pwrup_child) { 38000Sstevel@tonic-gate old_state = hubd->h_dev_state; 38010Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_HUB_CHILD_PWRLVL; 38020Sstevel@tonic-gate for (port = 1; port <= nports; port++) { 38030Sstevel@tonic-gate if (hubd->h_port_state[port] & HUBD_CHILD_RAISE_POWER) { 38040Sstevel@tonic-gate dev_info_t *dip = hubd->h_children_dips[port]; 38050Sstevel@tonic-gate 38060Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 38070Sstevel@tonic-gate 38080Sstevel@tonic-gate /* Get the device to full power */ 38090Sstevel@tonic-gate (void) pm_busy_component(dip, 0); 38100Sstevel@tonic-gate (void) pm_raise_power(dip, 0, 38110Sstevel@tonic-gate USB_DEV_OS_FULL_PWR); 38120Sstevel@tonic-gate (void) pm_idle_component(dip, 0); 38130Sstevel@tonic-gate 38140Sstevel@tonic-gate /* release the hold on the child */ 38150Sstevel@tonic-gate ndi_rele_devi(dip); 38160Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 38170Sstevel@tonic-gate hubd->h_port_state[port] &= 38180Sstevel@tonic-gate ~HUBD_CHILD_RAISE_POWER; 38190Sstevel@tonic-gate } 38200Sstevel@tonic-gate } 38210Sstevel@tonic-gate /* 38220Sstevel@tonic-gate * make sure that we don't accidentally 38230Sstevel@tonic-gate * over write the disconnect state 38240Sstevel@tonic-gate */ 38250Sstevel@tonic-gate if (hubd->h_dev_state == USB_DEV_HUB_CHILD_PWRLVL) { 38260Sstevel@tonic-gate hubd->h_dev_state = old_state; 38270Sstevel@tonic-gate } 38280Sstevel@tonic-gate } 38290Sstevel@tonic-gate 38300Sstevel@tonic-gate /* 38310Sstevel@tonic-gate * start polling can immediately kick off read callback 38320Sstevel@tonic-gate * we need to set the h_hotplug_thread to 0 so that 38330Sstevel@tonic-gate * the callback is not dropped 38340Sstevel@tonic-gate */ 38350Sstevel@tonic-gate hubd_start_polling(hubd, HUBD_ALWAYS_START_POLLING); 38360Sstevel@tonic-gate 38370Sstevel@tonic-gate /* 38380Sstevel@tonic-gate * Earlier we would set the h_hotplug_thread = 0 before 38390Sstevel@tonic-gate * polling was restarted so that 38400Sstevel@tonic-gate * if there is any root hub status change interrupt, we can still kick 38410Sstevel@tonic-gate * off the hotplug thread. This was valid when this interrupt was 38420Sstevel@tonic-gate * delivered in hardware, and only ONE interrupt would be delivered. 38430Sstevel@tonic-gate * Now that we poll on the root hub looking for status change in 38440Sstevel@tonic-gate * software, this assignment is no longer required. 38450Sstevel@tonic-gate */ 38460Sstevel@tonic-gate hubd->h_hotplug_thread--; 38470Sstevel@tonic-gate 38480Sstevel@tonic-gate /* mark this device as idle */ 38490Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0); 38500Sstevel@tonic-gate 38510Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 38520Sstevel@tonic-gate "hubd_hotplug_thread: exit"); 38530Sstevel@tonic-gate 38540Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 38550Sstevel@tonic-gate 38560Sstevel@tonic-gate ndi_rele_devi(hdip); 38570Sstevel@tonic-gate } 38580Sstevel@tonic-gate 38590Sstevel@tonic-gate 38600Sstevel@tonic-gate /* 38610Sstevel@tonic-gate * hubd_handle_port_connect: 38620Sstevel@tonic-gate * Transition a port from Disabled to Enabled. Ensure that the 38630Sstevel@tonic-gate * port is in the correct state before attempting to 38640Sstevel@tonic-gate * access the device. 38650Sstevel@tonic-gate */ 38660Sstevel@tonic-gate static int 38670Sstevel@tonic-gate hubd_handle_port_connect(hubd_t *hubd, usb_port_t port) 38680Sstevel@tonic-gate { 38690Sstevel@tonic-gate int rval; 38700Sstevel@tonic-gate int retry; 38710Sstevel@tonic-gate long time_delay; 38720Sstevel@tonic-gate long settling_time; 38730Sstevel@tonic-gate uint16_t status; 38740Sstevel@tonic-gate uint16_t change; 38750Sstevel@tonic-gate usb_addr_t hubd_usb_addr; 38760Sstevel@tonic-gate usba_device_t *usba_device; 38770Sstevel@tonic-gate usb_port_status_t port_status = 0; 38780Sstevel@tonic-gate usb_port_status_t hub_port_status = 0; 38790Sstevel@tonic-gate 38800Sstevel@tonic-gate /* Get the hub address and port status */ 38810Sstevel@tonic-gate usba_device = hubd->h_usba_device; 38820Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex); 38830Sstevel@tonic-gate hubd_usb_addr = usba_device->usb_addr; 38840Sstevel@tonic-gate hub_port_status = usba_device->usb_port_status; 38850Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex); 38860Sstevel@tonic-gate 38870Sstevel@tonic-gate /* 38880Sstevel@tonic-gate * If a device is connected, transition the 38890Sstevel@tonic-gate * port from Disabled to the Enabled state. 38900Sstevel@tonic-gate * The device will receive downstream packets 38910Sstevel@tonic-gate * in the Enabled state. 38920Sstevel@tonic-gate * 38930Sstevel@tonic-gate * reset port and wait for the hub to report 38940Sstevel@tonic-gate * completion 38950Sstevel@tonic-gate */ 38960Sstevel@tonic-gate change = status = 0; 38970Sstevel@tonic-gate 38980Sstevel@tonic-gate /* 38990Sstevel@tonic-gate * According to section 9.1.2 of USB 2.0 spec, the host should 39000Sstevel@tonic-gate * wait for atleast 100ms to allow completion of an insertion 39010Sstevel@tonic-gate * process and for power at the device to become stable. 39020Sstevel@tonic-gate * We wait for 200 ms 39030Sstevel@tonic-gate */ 39040Sstevel@tonic-gate settling_time = drv_usectohz(hubd_device_delay / 5); 39050Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 39060Sstevel@tonic-gate delay(settling_time); 39070Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 39080Sstevel@tonic-gate 39090Sstevel@tonic-gate /* calculate 600 ms delay time */ 39100Sstevel@tonic-gate time_delay = (6 * drv_usectohz(hubd_device_delay)) / 10; 39110Sstevel@tonic-gate 39120Sstevel@tonic-gate for (retry = 0; (hubd->h_dev_state == USB_DEV_ONLINE) && 39130Sstevel@tonic-gate (retry < hubd_retry_enumerate); retry++) { 39140Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 39150Sstevel@tonic-gate "resetting port%d, retry=%d", port, retry); 39160Sstevel@tonic-gate 39170Sstevel@tonic-gate if ((rval = hubd_reset_port(hubd, port)) != USB_SUCCESS) { 39180Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, 39190Sstevel@tonic-gate port, &status, &change, 0); 39200Sstevel@tonic-gate 39210Sstevel@tonic-gate /* continue only if port is still connected */ 39220Sstevel@tonic-gate if (status & PORT_STATUS_CCS) { 39230Sstevel@tonic-gate continue; 39240Sstevel@tonic-gate } 39250Sstevel@tonic-gate 39260Sstevel@tonic-gate /* carry on regardless */ 39270Sstevel@tonic-gate } 39280Sstevel@tonic-gate 39290Sstevel@tonic-gate /* 39300Sstevel@tonic-gate * according to USB 2.0 spec section 11.24.2.7.1.2 39310Sstevel@tonic-gate * at the end of port reset, the hub enables the port. 39320Sstevel@tonic-gate * But for some strange reasons, uhci port remains disabled. 39330Sstevel@tonic-gate * And because the port remains disabled for the settling 39340Sstevel@tonic-gate * time below, the device connected to the port gets wedged 39350Sstevel@tonic-gate * - fails to enumerate (device not responding) 39360Sstevel@tonic-gate * Hence, we enable it here immediately and later again after 39370Sstevel@tonic-gate * the delay 39380Sstevel@tonic-gate */ 39390Sstevel@tonic-gate (void) hubd_enable_port(hubd, port); 39400Sstevel@tonic-gate 39410Sstevel@tonic-gate /* we skip this delay in the first iteration */ 39420Sstevel@tonic-gate if (retry) { 39430Sstevel@tonic-gate /* 39440Sstevel@tonic-gate * delay for device to signal disconnect/connect so 39450Sstevel@tonic-gate * that hub properly recognizes the speed of the device 39460Sstevel@tonic-gate */ 39470Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 39480Sstevel@tonic-gate delay(settling_time); 39490Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 39500Sstevel@tonic-gate 39510Sstevel@tonic-gate /* 39520Sstevel@tonic-gate * When a low speed device is connected to any port of 39530Sstevel@tonic-gate * PPX it has to be explicitly enabled 39540Sstevel@tonic-gate * Also, if device intentionally signals 39550Sstevel@tonic-gate * disconnect/connect, it will disable the port. 39560Sstevel@tonic-gate * So enable it again. 39570Sstevel@tonic-gate */ 39580Sstevel@tonic-gate (void) hubd_enable_port(hubd, port); 39590Sstevel@tonic-gate } 39600Sstevel@tonic-gate 39610Sstevel@tonic-gate if ((rval = hubd_determine_port_status(hubd, port, &status, 39620Sstevel@tonic-gate &change, 0)) != USB_SUCCESS) { 39630Sstevel@tonic-gate 3964978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 39650Sstevel@tonic-gate "getting status failed (%d)", rval); 39660Sstevel@tonic-gate 39670Sstevel@tonic-gate (void) hubd_disable_port(hubd, port); 39680Sstevel@tonic-gate 39690Sstevel@tonic-gate continue; 39700Sstevel@tonic-gate } 39710Sstevel@tonic-gate 39720Sstevel@tonic-gate if (status & PORT_STATUS_POCI) { 3973978Sfrits USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 39740Sstevel@tonic-gate "port %d overcurrent", port); 39750Sstevel@tonic-gate 39760Sstevel@tonic-gate (void) hubd_disable_port(hubd, port); 39770Sstevel@tonic-gate 39780Sstevel@tonic-gate /* ack changes */ 39790Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, 39800Sstevel@tonic-gate port, &status, &change, PORT_CHANGE_OCIC); 39810Sstevel@tonic-gate 39820Sstevel@tonic-gate continue; 39830Sstevel@tonic-gate } 39840Sstevel@tonic-gate 39850Sstevel@tonic-gate /* is status really OK? */ 39860Sstevel@tonic-gate if ((status & PORT_STATUS_OK) != PORT_STATUS_OK) { 39870Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 39880Sstevel@tonic-gate "port %d status (0x%x) not OK on retry %d", 39890Sstevel@tonic-gate port, status, retry); 39900Sstevel@tonic-gate 39910Sstevel@tonic-gate /* check if we still have the connection */ 39920Sstevel@tonic-gate if (!(status & PORT_STATUS_CCS)) { 39930Sstevel@tonic-gate /* lost connection, set exit condition */ 39940Sstevel@tonic-gate retry = hubd_retry_enumerate; 39950Sstevel@tonic-gate 39960Sstevel@tonic-gate break; 39970Sstevel@tonic-gate } 39980Sstevel@tonic-gate } else { 39990Sstevel@tonic-gate /* 40000Sstevel@tonic-gate * Determine if the device is high or full 40010Sstevel@tonic-gate * or low speed. 40020Sstevel@tonic-gate */ 40030Sstevel@tonic-gate if (status & PORT_STATUS_LSDA) { 40040Sstevel@tonic-gate port_status = USBA_LOW_SPEED_DEV; 40050Sstevel@tonic-gate } else if (status & PORT_STATUS_HSDA) { 40060Sstevel@tonic-gate port_status = USBA_HIGH_SPEED_DEV; 40070Sstevel@tonic-gate } else { 40080Sstevel@tonic-gate port_status = USBA_FULL_SPEED_DEV; 40090Sstevel@tonic-gate } 40100Sstevel@tonic-gate 40110Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 40120Sstevel@tonic-gate "creating child port%d, status=0x%x " 40130Sstevel@tonic-gate "port status=0x%x", 40140Sstevel@tonic-gate port, status, port_status); 40150Sstevel@tonic-gate 40160Sstevel@tonic-gate /* 40170Sstevel@tonic-gate * if the child already exists, set addrs and config 40180Sstevel@tonic-gate * to the device post connect event to the child 40190Sstevel@tonic-gate */ 40200Sstevel@tonic-gate if (hubd->h_children_dips[port]) { 40210Sstevel@tonic-gate /* set addrs to this device */ 40220Sstevel@tonic-gate rval = hubd_setdevaddr(hubd, port); 40230Sstevel@tonic-gate 40240Sstevel@tonic-gate /* 40250Sstevel@tonic-gate * This delay is important for the CATC hub 40260Sstevel@tonic-gate * to enumerate. But, avoid delay in the first 40270Sstevel@tonic-gate * iteration 40280Sstevel@tonic-gate */ 40290Sstevel@tonic-gate if (retry) { 40300Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 40310Sstevel@tonic-gate delay(drv_usectohz( 40320Sstevel@tonic-gate hubd_device_delay/100)); 40330Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 40340Sstevel@tonic-gate } 40350Sstevel@tonic-gate 40360Sstevel@tonic-gate if (rval == USB_SUCCESS) { 40370Sstevel@tonic-gate /* 40380Sstevel@tonic-gate * set the default config for 40390Sstevel@tonic-gate * this device 40400Sstevel@tonic-gate */ 40410Sstevel@tonic-gate hubd_setdevconfig(hubd, port); 40420Sstevel@tonic-gate 40430Sstevel@tonic-gate /* 40440Sstevel@tonic-gate * indicate to the child that 40450Sstevel@tonic-gate * it is online again 40460Sstevel@tonic-gate */ 40470Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 40480Sstevel@tonic-gate hubd_post_event(hubd, port, 40490Sstevel@tonic-gate USBA_EVENT_TAG_HOT_INSERTION); 40500Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 40510Sstevel@tonic-gate 40520Sstevel@tonic-gate return (USB_SUCCESS); 40530Sstevel@tonic-gate } 40540Sstevel@tonic-gate } else { 40550Sstevel@tonic-gate /* 40560Sstevel@tonic-gate * We need to release access here 40570Sstevel@tonic-gate * so that busctls on other ports can 40580Sstevel@tonic-gate * continue and don't cause a deadlock 40590Sstevel@tonic-gate * when busctl and removal of prom node 40600Sstevel@tonic-gate * takes concurrently. This also ensures 40610Sstevel@tonic-gate * busctls for attach of successfully 40620Sstevel@tonic-gate * enumerated devices on other ports can 40630Sstevel@tonic-gate * continue concurrently with the process 40640Sstevel@tonic-gate * of enumerating the new devices. This 40650Sstevel@tonic-gate * reduces the overall boot time of the system. 40660Sstevel@tonic-gate */ 40670Sstevel@tonic-gate rval = hubd_create_child(hubd->h_dip, 4068*4763Slg150142 hubd, 4069*4763Slg150142 hubd->h_usba_device, 4070*4763Slg150142 port_status, port, 4071*4763Slg150142 retry); 40720Sstevel@tonic-gate if (rval == USB_SUCCESS) { 40730Sstevel@tonic-gate usba_update_hotplug_stats(hubd->h_dip, 40740Sstevel@tonic-gate USBA_TOTAL_HOTPLUG_SUCCESS| 40750Sstevel@tonic-gate USBA_HOTPLUG_SUCCESS); 40760Sstevel@tonic-gate hubd->h_total_hotplug_success++; 40770Sstevel@tonic-gate 40780Sstevel@tonic-gate if (retry > 0) { 4079978Sfrits USB_DPRINTF_L2( 40800Sstevel@tonic-gate DPRINT_MASK_HOTPLUG, 40810Sstevel@tonic-gate hubd->h_log_handle, 40820Sstevel@tonic-gate "device on port %d " 40830Sstevel@tonic-gate "enumerated after %d %s", 40840Sstevel@tonic-gate port, retry, 40850Sstevel@tonic-gate (retry > 1) ? "retries" : 40860Sstevel@tonic-gate "retry"); 40870Sstevel@tonic-gate 40880Sstevel@tonic-gate } 40890Sstevel@tonic-gate 40900Sstevel@tonic-gate return (USB_SUCCESS); 40910Sstevel@tonic-gate } 40920Sstevel@tonic-gate } 40930Sstevel@tonic-gate } 40940Sstevel@tonic-gate 40950Sstevel@tonic-gate /* wait a while until it settles? */ 40960Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 40970Sstevel@tonic-gate "disabling port %d again", port); 40980Sstevel@tonic-gate 40990Sstevel@tonic-gate (void) hubd_disable_port(hubd, port); 41000Sstevel@tonic-gate if (retry) { 41010Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 41020Sstevel@tonic-gate delay(time_delay); 41030Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 41040Sstevel@tonic-gate } 41050Sstevel@tonic-gate 41060Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 41070Sstevel@tonic-gate "retrying on port %d", port); 41080Sstevel@tonic-gate } 41090Sstevel@tonic-gate 41100Sstevel@tonic-gate if (retry >= hubd_retry_enumerate) { 41110Sstevel@tonic-gate /* 41120Sstevel@tonic-gate * If it is a High Speed Root Hub and connected device 41130Sstevel@tonic-gate * Is a Low/Full Speed, it will be handled by USB 1.1 41140Sstevel@tonic-gate * Host Controller. In this case, USB 2.0 Host Controller 41150Sstevel@tonic-gate * will transfer the ownership of this port to USB 1.1 41160Sstevel@tonic-gate * Host Controller. So don't display any error message on 41170Sstevel@tonic-gate * the console. 41180Sstevel@tonic-gate */ 41190Sstevel@tonic-gate if ((hubd_usb_addr == ROOT_HUB_ADDR) && 41200Sstevel@tonic-gate (hub_port_status == USBA_HIGH_SPEED_DEV) && 41210Sstevel@tonic-gate (port_status != USBA_HIGH_SPEED_DEV)) { 41220Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 41230Sstevel@tonic-gate hubd->h_log_handle, 41240Sstevel@tonic-gate "hubd_handle_port_connect: Low/Full speed " 41250Sstevel@tonic-gate "device is connected to High Speed root hub"); 41260Sstevel@tonic-gate } else { 41270Sstevel@tonic-gate USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, 41280Sstevel@tonic-gate hubd->h_log_handle, 41290Sstevel@tonic-gate "Connecting device on port %d failed", port); 41300Sstevel@tonic-gate } 41310Sstevel@tonic-gate 41320Sstevel@tonic-gate (void) hubd_disable_port(hubd, port); 41330Sstevel@tonic-gate usba_update_hotplug_stats(hubd->h_dip, 41340Sstevel@tonic-gate USBA_TOTAL_HOTPLUG_FAILURE|USBA_HOTPLUG_FAILURE); 41350Sstevel@tonic-gate hubd->h_total_hotplug_failure++; 41360Sstevel@tonic-gate 41370Sstevel@tonic-gate /* 41380Sstevel@tonic-gate * the port should be automagically 41390Sstevel@tonic-gate * disabled but just in case, we do 41400Sstevel@tonic-gate * it here 41410Sstevel@tonic-gate */ 41420Sstevel@tonic-gate (void) hubd_disable_port(hubd, port); 41430Sstevel@tonic-gate 41440Sstevel@tonic-gate /* ack all changes because we disabled this port */ 41450Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, 41460Sstevel@tonic-gate port, &status, &change, HUBD_ACK_ALL_CHANGES); 41470Sstevel@tonic-gate 41480Sstevel@tonic-gate } 41490Sstevel@tonic-gate 41500Sstevel@tonic-gate return (USB_FAILURE); 41510Sstevel@tonic-gate } 41520Sstevel@tonic-gate 41530Sstevel@tonic-gate 41540Sstevel@tonic-gate /* 41550Sstevel@tonic-gate * hubd_get_hub_status: 41560Sstevel@tonic-gate */ 41570Sstevel@tonic-gate static int 41580Sstevel@tonic-gate hubd_get_hub_status(hubd_t *hubd) 41590Sstevel@tonic-gate { 41600Sstevel@tonic-gate int rval; 41610Sstevel@tonic-gate usb_cr_t completion_reason; 41620Sstevel@tonic-gate usb_cb_flags_t cb_flags; 41631001Ssl147100 uint16_t stword[2]; 41640Sstevel@tonic-gate uint16_t status; 41650Sstevel@tonic-gate uint16_t change; 41660Sstevel@tonic-gate usb_cfg_descr_t cfg_descr; 41670Sstevel@tonic-gate size_t cfg_length; 41680Sstevel@tonic-gate uchar_t *usb_cfg; 41690Sstevel@tonic-gate uint8_t MaxPower; 41702326Ssl147100 usb_hub_descr_t *hub_descr; 41712326Ssl147100 usb_port_t port; 41722326Ssl147100 41732326Ssl147100 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 41741001Ssl147100 "hubd_get_hub_status:"); 41751001Ssl147100 41761001Ssl147100 ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 41771001Ssl147100 41781001Ssl147100 if ((hubd_get_hub_status_words(hubd, stword)) != USB_SUCCESS) { 41790Sstevel@tonic-gate 41800Sstevel@tonic-gate return (USB_FAILURE); 41810Sstevel@tonic-gate } 41821001Ssl147100 status = stword[0]; 41831001Ssl147100 change = stword[1]; 41840Sstevel@tonic-gate 41850Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 41860Sstevel@tonic-gate 41870Sstevel@tonic-gate /* Obtain the raw configuration descriptor */ 41880Sstevel@tonic-gate usb_cfg = usb_get_raw_cfg_data(hubd->h_dip, &cfg_length); 41890Sstevel@tonic-gate 41900Sstevel@tonic-gate /* get configuration descriptor */ 41910Sstevel@tonic-gate rval = usb_parse_cfg_descr(usb_cfg, cfg_length, 4192*4763Slg150142 &cfg_descr, USB_CFG_DESCR_SIZE); 41930Sstevel@tonic-gate 41940Sstevel@tonic-gate if (rval != USB_CFG_DESCR_SIZE) { 41950Sstevel@tonic-gate 41962326Ssl147100 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 41970Sstevel@tonic-gate "get hub configuration descriptor failed."); 41980Sstevel@tonic-gate 41990Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 42000Sstevel@tonic-gate 42010Sstevel@tonic-gate return (USB_FAILURE); 42020Sstevel@tonic-gate } else { 42030Sstevel@tonic-gate MaxPower = cfg_descr.bMaxPower; 42040Sstevel@tonic-gate } 42050Sstevel@tonic-gate 42060Sstevel@tonic-gate /* check if local power status changed. */ 42070Sstevel@tonic-gate if (change & C_HUB_LOCAL_POWER_STATUS) { 42080Sstevel@tonic-gate 42090Sstevel@tonic-gate /* 42100Sstevel@tonic-gate * local power has been lost, check the maximum 42110Sstevel@tonic-gate * power consumption of current configuration. 42120Sstevel@tonic-gate * see USB2.0 spec Table 11-12. 42130Sstevel@tonic-gate */ 42140Sstevel@tonic-gate if (status & HUB_LOCAL_POWER_STATUS) { 42150Sstevel@tonic-gate 42160Sstevel@tonic-gate if (MaxPower == 0) { 42170Sstevel@tonic-gate 42180Sstevel@tonic-gate /* 42190Sstevel@tonic-gate * Self-powered only hub. Because it could 42200Sstevel@tonic-gate * not draw any power from USB bus. 42210Sstevel@tonic-gate * It can't work well on this condition. 42220Sstevel@tonic-gate */ 42232326Ssl147100 USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, 42240Sstevel@tonic-gate hubd->h_log_handle, 42250Sstevel@tonic-gate "local power has been lost, " 42260Sstevel@tonic-gate "please disconnect hub"); 42270Sstevel@tonic-gate } else { 42280Sstevel@tonic-gate 42290Sstevel@tonic-gate /* 42300Sstevel@tonic-gate * Bus-powered only or self/bus-powered hub. 42310Sstevel@tonic-gate */ 42322326Ssl147100 USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, 42330Sstevel@tonic-gate hubd->h_log_handle, 42340Sstevel@tonic-gate "local power has been lost," 42350Sstevel@tonic-gate "the hub could draw %d" 42360Sstevel@tonic-gate " mA power from the USB bus.", 42370Sstevel@tonic-gate 2*MaxPower); 42380Sstevel@tonic-gate } 42390Sstevel@tonic-gate 42400Sstevel@tonic-gate } 42410Sstevel@tonic-gate 42422326Ssl147100 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 42430Sstevel@tonic-gate "clearing feature C_HUB_LOCAL_POWER "); 42440Sstevel@tonic-gate 42450Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 42460Sstevel@tonic-gate hubd->h_default_pipe, 42472326Ssl147100 HUB_HANDLE_HUB_FEATURE_TYPE, 42480Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 42490Sstevel@tonic-gate CFS_C_HUB_LOCAL_POWER, 42500Sstevel@tonic-gate 0, 42510Sstevel@tonic-gate 0, 42520Sstevel@tonic-gate NULL, 0, 42530Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 42542326Ssl147100 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 42550Sstevel@tonic-gate hubd->h_log_handle, 42560Sstevel@tonic-gate "clear feature C_HUB_LOCAL_POWER " 42570Sstevel@tonic-gate "failed (%d 0x%x %d)", 42580Sstevel@tonic-gate rval, completion_reason, cb_flags); 42590Sstevel@tonic-gate } 42600Sstevel@tonic-gate 42610Sstevel@tonic-gate } 42620Sstevel@tonic-gate 42630Sstevel@tonic-gate if (change & C_HUB_OVER_CURRENT) { 42640Sstevel@tonic-gate 42650Sstevel@tonic-gate if (status & HUB_OVER_CURRENT) { 42662326Ssl147100 42672326Ssl147100 if (usba_is_root_hub(hubd->h_dip)) { 42682326Ssl147100 /* 42692326Ssl147100 * The root hub should be automatically 42702326Ssl147100 * recovered when over-current condition is 42712326Ssl147100 * cleared. But there might be exception and 42722326Ssl147100 * need user interaction to recover. 42732326Ssl147100 */ 42742326Ssl147100 USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, 42752326Ssl147100 hubd->h_log_handle, 42762326Ssl147100 "Root hub over current condition, " 42772326Ssl147100 "please check your system to clear the " 42782326Ssl147100 "condition as soon as possible. And you " 42792326Ssl147100 "may need to reboot the system to bring " 42802326Ssl147100 "the root hub back to work if it cannot " 42812326Ssl147100 "recover automatically"); 42822326Ssl147100 } else { 42832326Ssl147100 /* 42842326Ssl147100 * The driver would try to recover port power 42852326Ssl147100 * on over current condition. When the recovery 42862326Ssl147100 * fails, the user may still need to offline 42872326Ssl147100 * this hub in order to recover. 42882326Ssl147100 * The port power is automatically disabled, 42892326Ssl147100 * so we won't see disconnects. 42902326Ssl147100 */ 42912326Ssl147100 USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, 42922326Ssl147100 hubd->h_log_handle, 42932326Ssl147100 "Hub global over current condition, " 42942326Ssl147100 "please disconnect the devices connected " 42952326Ssl147100 "to the hub to clear the condition. And " 42962326Ssl147100 "you may need to re-connect the hub if " 42972326Ssl147100 "the ports do not work"); 42982326Ssl147100 } 42992326Ssl147100 } 43002326Ssl147100 43012326Ssl147100 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 43020Sstevel@tonic-gate "clearing feature C_HUB_OVER_CURRENT"); 43030Sstevel@tonic-gate 43040Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 43050Sstevel@tonic-gate hubd->h_default_pipe, 43062326Ssl147100 HUB_HANDLE_HUB_FEATURE_TYPE, 43070Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 43080Sstevel@tonic-gate CFS_C_HUB_OVER_CURRENT, 43090Sstevel@tonic-gate 0, 43100Sstevel@tonic-gate 0, 43110Sstevel@tonic-gate NULL, 0, 43120Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 43132326Ssl147100 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 43140Sstevel@tonic-gate hubd->h_log_handle, 43150Sstevel@tonic-gate "clear feature C_HUB_OVER_CURRENT " 43160Sstevel@tonic-gate "failed (%d 0x%x %d)", 43170Sstevel@tonic-gate rval, completion_reason, cb_flags); 43180Sstevel@tonic-gate } 43192326Ssl147100 43202326Ssl147100 /* 43212326Ssl147100 * Try to recover all port power if they are turned off. 43222326Ssl147100 * Don't do this for root hub, but rely on the root hub 43232326Ssl147100 * to recover itself. 43242326Ssl147100 */ 43252326Ssl147100 if (!usba_is_root_hub(hubd->h_dip)) { 43262326Ssl147100 43272326Ssl147100 mutex_enter(HUBD_MUTEX(hubd)); 43282326Ssl147100 43292326Ssl147100 /* 43302326Ssl147100 * Only check the power status of the 1st port 43312326Ssl147100 * since all port power status should be the same. 43322326Ssl147100 */ 43332326Ssl147100 (void) hubd_determine_port_status(hubd, 1, &status, 43342326Ssl147100 &change, 0); 43352326Ssl147100 43362326Ssl147100 if (status & PORT_STATUS_PPS) { 43372326Ssl147100 43382326Ssl147100 return (USB_SUCCESS); 43392326Ssl147100 } 43402326Ssl147100 43412326Ssl147100 hub_descr = &hubd->h_hub_descr; 43422326Ssl147100 43432326Ssl147100 for (port = 1; port <= hub_descr->bNbrPorts; 43442326Ssl147100 port++) { 43452326Ssl147100 43462326Ssl147100 (void) hubd_enable_port_power(hubd, port); 43472326Ssl147100 } 43482326Ssl147100 43492326Ssl147100 mutex_exit(HUBD_MUTEX(hubd)); 43502326Ssl147100 43512326Ssl147100 /* 43522326Ssl147100 * Delay some time to avoid over-current event 43532326Ssl147100 * to happen too frequently in some cases 43542326Ssl147100 */ 43552326Ssl147100 delay(drv_usectohz(500000)); 43562326Ssl147100 } 43570Sstevel@tonic-gate } 43580Sstevel@tonic-gate 43590Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 43600Sstevel@tonic-gate 43610Sstevel@tonic-gate return (USB_SUCCESS); 43620Sstevel@tonic-gate } 43630Sstevel@tonic-gate 43640Sstevel@tonic-gate 43650Sstevel@tonic-gate /* 43660Sstevel@tonic-gate * hubd_reset_port: 43670Sstevel@tonic-gate */ 43680Sstevel@tonic-gate static int 43690Sstevel@tonic-gate hubd_reset_port(hubd_t *hubd, usb_port_t port) 43700Sstevel@tonic-gate { 43710Sstevel@tonic-gate int rval; 43720Sstevel@tonic-gate usb_cr_t completion_reason; 43730Sstevel@tonic-gate usb_cb_flags_t cb_flags; 43740Sstevel@tonic-gate usb_port_mask_t port_mask = 1 << port; 43750Sstevel@tonic-gate mblk_t *data; 43760Sstevel@tonic-gate uint16_t status; 43770Sstevel@tonic-gate uint16_t change; 43780Sstevel@tonic-gate int i; 43790Sstevel@tonic-gate clock_t current_time; 43800Sstevel@tonic-gate 43810Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 43820Sstevel@tonic-gate "hubd_reset_port: port=%d", port); 43830Sstevel@tonic-gate 43840Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 43850Sstevel@tonic-gate 43860Sstevel@tonic-gate hubd->h_port_reset_wait |= port_mask; 43870Sstevel@tonic-gate 43880Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 43890Sstevel@tonic-gate 43900Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 43910Sstevel@tonic-gate hubd->h_default_pipe, 43921001Ssl147100 HUB_HANDLE_PORT_FEATURE_TYPE, 43930Sstevel@tonic-gate USB_REQ_SET_FEATURE, 43940Sstevel@tonic-gate CFS_PORT_RESET, 43950Sstevel@tonic-gate port, 43960Sstevel@tonic-gate 0, 43970Sstevel@tonic-gate NULL, 0, 43980Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 4399978Sfrits USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 44000Sstevel@tonic-gate "reset port%d failed (%d 0x%x %d)", 44010Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 44020Sstevel@tonic-gate 44030Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 44040Sstevel@tonic-gate 44050Sstevel@tonic-gate return (USB_FAILURE); 44060Sstevel@tonic-gate } 44070Sstevel@tonic-gate 44080Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 44090Sstevel@tonic-gate 44100Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 44110Sstevel@tonic-gate "waiting on cv for reset completion"); 44120Sstevel@tonic-gate 44130Sstevel@tonic-gate /* 44140Sstevel@tonic-gate * wait for port status change event 44150Sstevel@tonic-gate */ 44160Sstevel@tonic-gate for (i = 0; i < hubd_retry_enumerate; i++) { 44170Sstevel@tonic-gate /* 44180Sstevel@tonic-gate * start polling ep1 for receiving notification on 44190Sstevel@tonic-gate * reset completion 44200Sstevel@tonic-gate */ 44210Sstevel@tonic-gate hubd_start_polling(hubd, HUBD_ALWAYS_START_POLLING); 44220Sstevel@tonic-gate 44230Sstevel@tonic-gate /* 44240Sstevel@tonic-gate * sleep a max of 100ms for reset completion 44250Sstevel@tonic-gate * notification to be received 44260Sstevel@tonic-gate */ 44270Sstevel@tonic-gate current_time = ddi_get_lbolt(); 44280Sstevel@tonic-gate if (hubd->h_port_reset_wait & port_mask) { 44290Sstevel@tonic-gate rval = cv_timedwait(&hubd->h_cv_reset_port, 4430*4763Slg150142 &hubd->h_mutex, 4431*4763Slg150142 current_time + 4432*4763Slg150142 drv_usectohz(hubd_device_delay / 10)); 44330Sstevel@tonic-gate if ((rval <= 0) && 44340Sstevel@tonic-gate (hubd->h_port_reset_wait & port_mask)) { 44350Sstevel@tonic-gate /* we got woken up because of a timeout */ 44360Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 44370Sstevel@tonic-gate hubd->h_log_handle, 44380Sstevel@tonic-gate "timeout: reset port=%d failed", port); 44390Sstevel@tonic-gate 44400Sstevel@tonic-gate hubd->h_port_reset_wait &= ~port_mask; 44410Sstevel@tonic-gate 44420Sstevel@tonic-gate hubd_stop_polling(hubd); 44430Sstevel@tonic-gate 44440Sstevel@tonic-gate return (USB_FAILURE); 44450Sstevel@tonic-gate } 44460Sstevel@tonic-gate } 44470Sstevel@tonic-gate 44480Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 44490Sstevel@tonic-gate "reset completion received"); 44500Sstevel@tonic-gate 44510Sstevel@tonic-gate hubd_stop_polling(hubd); 44520Sstevel@tonic-gate 44530Sstevel@tonic-gate data = NULL; 44540Sstevel@tonic-gate 44550Sstevel@tonic-gate /* check status to determine whether reset completed */ 44560Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 44570Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 44580Sstevel@tonic-gate hubd->h_default_pipe, 44591001Ssl147100 HUB_GET_PORT_STATUS_TYPE, 44600Sstevel@tonic-gate USB_REQ_GET_STATUS, 44610Sstevel@tonic-gate 0, 44620Sstevel@tonic-gate port, 44630Sstevel@tonic-gate GET_STATUS_LENGTH, 44640Sstevel@tonic-gate &data, 0, 44650Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 4466978Sfrits USB_DPRINTF_L2(DPRINT_MASK_PORT, 44670Sstevel@tonic-gate hubd->h_log_handle, 44680Sstevel@tonic-gate "get status port%d failed (%d 0x%x %d)", 44690Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 44700Sstevel@tonic-gate 44710Sstevel@tonic-gate if (data) { 44720Sstevel@tonic-gate freemsg(data); 44730Sstevel@tonic-gate data = NULL; 44740Sstevel@tonic-gate } 44750Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 44760Sstevel@tonic-gate 44770Sstevel@tonic-gate continue; 44780Sstevel@tonic-gate } 44790Sstevel@tonic-gate 44800Sstevel@tonic-gate status = (*(data->b_rptr + 1) << 8) | *(data->b_rptr); 44810Sstevel@tonic-gate change = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2); 44820Sstevel@tonic-gate 44830Sstevel@tonic-gate freemsg(data); 44840Sstevel@tonic-gate 44850Sstevel@tonic-gate /* continue only if port is still connected */ 44860Sstevel@tonic-gate if (!(status & PORT_STATUS_CCS)) { 44870Sstevel@tonic-gate 44880Sstevel@tonic-gate /* lost connection, set exit condition */ 44890Sstevel@tonic-gate i = hubd_retry_enumerate; 44900Sstevel@tonic-gate 44910Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 44920Sstevel@tonic-gate 44930Sstevel@tonic-gate break; 44940Sstevel@tonic-gate } 44950Sstevel@tonic-gate 44960Sstevel@tonic-gate if (status & PORT_STATUS_PRS) { 44970Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 44980Sstevel@tonic-gate "port%d reset active", port); 44990Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 45000Sstevel@tonic-gate 45010Sstevel@tonic-gate continue; 45020Sstevel@tonic-gate } else { 45030Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 45040Sstevel@tonic-gate "port%d reset inactive", port); 45050Sstevel@tonic-gate } 45060Sstevel@tonic-gate 45070Sstevel@tonic-gate if (change & PORT_CHANGE_PRSC) { 45080Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 45090Sstevel@tonic-gate "clearing feature CFS_C_PORT_RESET"); 45100Sstevel@tonic-gate 45110Sstevel@tonic-gate if (usb_pipe_sync_ctrl_xfer(hubd->h_dip, 45120Sstevel@tonic-gate hubd->h_default_pipe, 45131001Ssl147100 HUB_HANDLE_PORT_FEATURE_TYPE, 45140Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 45150Sstevel@tonic-gate CFS_C_PORT_RESET, 45160Sstevel@tonic-gate port, 45170Sstevel@tonic-gate 0, 45180Sstevel@tonic-gate NULL, 0, 45190Sstevel@tonic-gate &completion_reason, &cb_flags, 0) != USB_SUCCESS) { 45200Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 45210Sstevel@tonic-gate hubd->h_log_handle, 45220Sstevel@tonic-gate "clear feature CFS_C_PORT_RESET" 45230Sstevel@tonic-gate " port%d failed (%d 0x%x %d)", 45240Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 45250Sstevel@tonic-gate } 45260Sstevel@tonic-gate } 45270Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 45280Sstevel@tonic-gate 45290Sstevel@tonic-gate break; 45300Sstevel@tonic-gate } 45310Sstevel@tonic-gate 45320Sstevel@tonic-gate if (i >= hubd_retry_enumerate) { 45330Sstevel@tonic-gate /* port reset has failed */ 45340Sstevel@tonic-gate rval = USB_FAILURE; 45350Sstevel@tonic-gate } 45360Sstevel@tonic-gate 45370Sstevel@tonic-gate return (rval); 45380Sstevel@tonic-gate } 45390Sstevel@tonic-gate 45400Sstevel@tonic-gate 45410Sstevel@tonic-gate /* 45420Sstevel@tonic-gate * hubd_enable_port: 45430Sstevel@tonic-gate * this may fail if the hub as been disconnected 45440Sstevel@tonic-gate */ 45450Sstevel@tonic-gate static int 45460Sstevel@tonic-gate hubd_enable_port(hubd_t *hubd, usb_port_t port) 45470Sstevel@tonic-gate { 45480Sstevel@tonic-gate int rval; 45490Sstevel@tonic-gate usb_cr_t completion_reason; 45500Sstevel@tonic-gate usb_cb_flags_t cb_flags; 45510Sstevel@tonic-gate 45520Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 45530Sstevel@tonic-gate "hubd_enable_port: port=%d", port); 45540Sstevel@tonic-gate 45550Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 45560Sstevel@tonic-gate 45570Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 45580Sstevel@tonic-gate 45590Sstevel@tonic-gate /* Do not issue a SetFeature(PORT_ENABLE) on external hubs */ 45600Sstevel@tonic-gate if (!usba_is_root_hub(hubd->h_dip)) { 45610Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 45620Sstevel@tonic-gate 45630Sstevel@tonic-gate return (USB_SUCCESS); 45640Sstevel@tonic-gate } 45650Sstevel@tonic-gate 45660Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 45670Sstevel@tonic-gate hubd->h_default_pipe, 45681001Ssl147100 HUB_HANDLE_PORT_FEATURE_TYPE, 45690Sstevel@tonic-gate USB_REQ_SET_FEATURE, 45700Sstevel@tonic-gate CFS_PORT_ENABLE, 45710Sstevel@tonic-gate port, 45720Sstevel@tonic-gate 0, 45730Sstevel@tonic-gate NULL, 0, 45740Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 45750Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 45760Sstevel@tonic-gate "enable port%d failed (%d 0x%x %d)", 45770Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 45780Sstevel@tonic-gate } 45790Sstevel@tonic-gate 45800Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 45810Sstevel@tonic-gate 45820Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 45830Sstevel@tonic-gate "enabling port done"); 45840Sstevel@tonic-gate 45850Sstevel@tonic-gate return (rval); 45860Sstevel@tonic-gate } 45870Sstevel@tonic-gate 45880Sstevel@tonic-gate 45890Sstevel@tonic-gate /* 45900Sstevel@tonic-gate * hubd_disable_port 45910Sstevel@tonic-gate */ 45920Sstevel@tonic-gate static int 45930Sstevel@tonic-gate hubd_disable_port(hubd_t *hubd, usb_port_t port) 45940Sstevel@tonic-gate { 45950Sstevel@tonic-gate int rval; 45960Sstevel@tonic-gate usb_cr_t completion_reason; 45970Sstevel@tonic-gate usb_cb_flags_t cb_flags; 45980Sstevel@tonic-gate 45990Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 46000Sstevel@tonic-gate "hubd_disable_port: port=%d", port); 46010Sstevel@tonic-gate 46020Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 46030Sstevel@tonic-gate 46040Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 46050Sstevel@tonic-gate 46060Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 46070Sstevel@tonic-gate hubd->h_default_pipe, 46081001Ssl147100 HUB_HANDLE_PORT_FEATURE_TYPE, 46090Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 46100Sstevel@tonic-gate CFS_PORT_ENABLE, 46110Sstevel@tonic-gate port, 46120Sstevel@tonic-gate 0, 46130Sstevel@tonic-gate NULL, 0, 46140Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 46150Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 46160Sstevel@tonic-gate "disable port%d failed (%d 0x%x %d)", port, 46170Sstevel@tonic-gate completion_reason, cb_flags, rval); 46180Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 46190Sstevel@tonic-gate 46200Sstevel@tonic-gate return (USB_FAILURE); 46210Sstevel@tonic-gate } 46220Sstevel@tonic-gate 46230Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 46240Sstevel@tonic-gate "clearing feature CFS_C_PORT_ENABLE"); 46250Sstevel@tonic-gate 46260Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 46270Sstevel@tonic-gate hubd->h_default_pipe, 46281001Ssl147100 HUB_HANDLE_PORT_FEATURE_TYPE, 46290Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 46300Sstevel@tonic-gate CFS_C_PORT_ENABLE, 46310Sstevel@tonic-gate port, 46320Sstevel@tonic-gate 0, 46330Sstevel@tonic-gate NULL, 0, 46340Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 46350Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 46360Sstevel@tonic-gate hubd->h_log_handle, 46370Sstevel@tonic-gate "clear feature CFS_C_PORT_ENABLE port%d failed " 46380Sstevel@tonic-gate "(%d 0x%x %d)", 46390Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 46400Sstevel@tonic-gate 46410Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 46420Sstevel@tonic-gate 46430Sstevel@tonic-gate return (USB_FAILURE); 46440Sstevel@tonic-gate } 46450Sstevel@tonic-gate 46460Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 46470Sstevel@tonic-gate 46480Sstevel@tonic-gate return (USB_SUCCESS); 46490Sstevel@tonic-gate } 46500Sstevel@tonic-gate 46510Sstevel@tonic-gate 46520Sstevel@tonic-gate /* 46530Sstevel@tonic-gate * hubd_determine_port_status: 46540Sstevel@tonic-gate */ 46550Sstevel@tonic-gate static int 46560Sstevel@tonic-gate hubd_determine_port_status(hubd_t *hubd, usb_port_t port, 46570Sstevel@tonic-gate uint16_t *status, uint16_t *change, uint_t ack_flag) 46580Sstevel@tonic-gate { 46590Sstevel@tonic-gate int rval; 46600Sstevel@tonic-gate mblk_t *data = NULL; 46610Sstevel@tonic-gate usb_cr_t completion_reason; 46620Sstevel@tonic-gate usb_cb_flags_t cb_flags; 46630Sstevel@tonic-gate 46640Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 46650Sstevel@tonic-gate "hubd_determine_port_status: port=%d, state=0x%x ack=0x%x", port, 46660Sstevel@tonic-gate hubd->h_port_state[port], ack_flag); 46670Sstevel@tonic-gate 46680Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 46690Sstevel@tonic-gate 46700Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 46710Sstevel@tonic-gate 46720Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 46730Sstevel@tonic-gate hubd->h_default_pipe, 46741001Ssl147100 HUB_GET_PORT_STATUS_TYPE, 46750Sstevel@tonic-gate USB_REQ_GET_STATUS, 46760Sstevel@tonic-gate 0, 46770Sstevel@tonic-gate port, 46780Sstevel@tonic-gate GET_STATUS_LENGTH, 46790Sstevel@tonic-gate &data, 0, 46800Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 46810Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 46820Sstevel@tonic-gate "port=%d get status failed (%d 0x%x %d)", 46830Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 46840Sstevel@tonic-gate 46850Sstevel@tonic-gate if (data) { 46860Sstevel@tonic-gate freemsg(data); 46870Sstevel@tonic-gate } 46880Sstevel@tonic-gate 46890Sstevel@tonic-gate *status = *change = 0; 46900Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 46910Sstevel@tonic-gate 46920Sstevel@tonic-gate return (rval); 46930Sstevel@tonic-gate } 46940Sstevel@tonic-gate 46950Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 46960Sstevel@tonic-gate if ((data->b_wptr - data->b_rptr) != GET_STATUS_LENGTH) { 46970Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 46980Sstevel@tonic-gate "port %d: length incorrect %d", 46990Sstevel@tonic-gate port, data->b_wptr - data->b_rptr); 47000Sstevel@tonic-gate freemsg(data); 47010Sstevel@tonic-gate *status = *change = 0; 47020Sstevel@tonic-gate 47030Sstevel@tonic-gate return (rval); 47040Sstevel@tonic-gate } 47050Sstevel@tonic-gate 47060Sstevel@tonic-gate 47070Sstevel@tonic-gate *status = (*(data->b_rptr + 1) << 8) | *(data->b_rptr); 47080Sstevel@tonic-gate *change = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2); 47090Sstevel@tonic-gate 47100Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 47110Sstevel@tonic-gate "port%d status=0x%x, change=0x%x", port, *status, *change); 47120Sstevel@tonic-gate 47130Sstevel@tonic-gate freemsg(data); 47140Sstevel@tonic-gate 47150Sstevel@tonic-gate if (*status & PORT_STATUS_CCS) { 47160Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 47170Sstevel@tonic-gate "port%d connected", port); 47180Sstevel@tonic-gate 47190Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_CCS & ack_flag); 47200Sstevel@tonic-gate } else { 47210Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 47220Sstevel@tonic-gate "port%d disconnected", port); 47230Sstevel@tonic-gate 47240Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_CCS & ack_flag); 47250Sstevel@tonic-gate } 47260Sstevel@tonic-gate 47270Sstevel@tonic-gate if (*status & PORT_STATUS_PES) { 47280Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 47290Sstevel@tonic-gate "port%d enabled", port); 47300Sstevel@tonic-gate 47310Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_PES & ack_flag); 47320Sstevel@tonic-gate } else { 47330Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 47340Sstevel@tonic-gate "port%d disabled", port); 47350Sstevel@tonic-gate 47360Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_PES & ack_flag); 47370Sstevel@tonic-gate } 47380Sstevel@tonic-gate 47390Sstevel@tonic-gate if (*status & PORT_STATUS_PSS) { 47400Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 47410Sstevel@tonic-gate "port%d suspended", port); 47420Sstevel@tonic-gate 47430Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_PSS & ack_flag); 47440Sstevel@tonic-gate } else { 47450Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 47460Sstevel@tonic-gate "port%d not suspended", port); 47470Sstevel@tonic-gate 47480Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_PSS & ack_flag); 47490Sstevel@tonic-gate } 47500Sstevel@tonic-gate 47510Sstevel@tonic-gate if (*change & PORT_CHANGE_PRSC) { 47520Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 47530Sstevel@tonic-gate "port%d reset completed", port); 47540Sstevel@tonic-gate 47550Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_CHANGE_PRSC & ack_flag); 47560Sstevel@tonic-gate } else { 47570Sstevel@tonic-gate 47580Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_CHANGE_PRSC & ack_flag); 47590Sstevel@tonic-gate } 47600Sstevel@tonic-gate 47610Sstevel@tonic-gate if (*status & PORT_STATUS_POCI) { 4762978Sfrits USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 4763978Sfrits "port%d overcurrent!", port); 47640Sstevel@tonic-gate 47650Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_POCI & ack_flag); 47660Sstevel@tonic-gate } else { 47670Sstevel@tonic-gate 47680Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_POCI & ack_flag); 47690Sstevel@tonic-gate } 47700Sstevel@tonic-gate 47710Sstevel@tonic-gate if (*status & PORT_STATUS_PRS) { 47720Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 47730Sstevel@tonic-gate "port%d reset active", port); 47740Sstevel@tonic-gate 47750Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_PRS & ack_flag); 47760Sstevel@tonic-gate } else { 47770Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 47780Sstevel@tonic-gate "port%d reset inactive", port); 47790Sstevel@tonic-gate 47800Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_PRS & ack_flag); 47810Sstevel@tonic-gate } 47820Sstevel@tonic-gate if (*status & PORT_STATUS_PPS) { 47830Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 47840Sstevel@tonic-gate "port%d power on", port); 47850Sstevel@tonic-gate 47860Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_PPS & ack_flag); 47870Sstevel@tonic-gate } else { 47880Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 47890Sstevel@tonic-gate "port%d power off", port); 47900Sstevel@tonic-gate 47910Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_PPS & ack_flag); 47920Sstevel@tonic-gate } 47930Sstevel@tonic-gate if (*status & PORT_STATUS_LSDA) { 47940Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 47950Sstevel@tonic-gate "port%d low speed", port); 47960Sstevel@tonic-gate 47970Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_LSDA & ack_flag); 47980Sstevel@tonic-gate } else { 47990Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_LSDA & ack_flag); 48000Sstevel@tonic-gate if (*status & PORT_STATUS_HSDA) { 48010Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, 48020Sstevel@tonic-gate hubd->h_log_handle, "port%d " 48030Sstevel@tonic-gate "high speed", port); 48040Sstevel@tonic-gate 48050Sstevel@tonic-gate hubd->h_port_state[port] |= 4806*4763Slg150142 (PORT_STATUS_HSDA & ack_flag); 48070Sstevel@tonic-gate } else { 48080Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, 48090Sstevel@tonic-gate hubd->h_log_handle, "port%d " 48100Sstevel@tonic-gate "full speed", port); 48110Sstevel@tonic-gate 48120Sstevel@tonic-gate hubd->h_port_state[port] &= 4813*4763Slg150142 ~(PORT_STATUS_HSDA & ack_flag); 48140Sstevel@tonic-gate } 48150Sstevel@tonic-gate } 48160Sstevel@tonic-gate 48170Sstevel@tonic-gate /* 48180Sstevel@tonic-gate * Acknowledge connection, enable, reset status 48190Sstevel@tonic-gate */ 48200Sstevel@tonic-gate if (ack_flag) { 48210Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 48220Sstevel@tonic-gate if (*change & PORT_CHANGE_CSC & ack_flag) { 48230Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 48240Sstevel@tonic-gate "clearing feature CFS_C_PORT_CONNECTION"); 48250Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 48260Sstevel@tonic-gate hubd->h_default_pipe, 48271001Ssl147100 HUB_HANDLE_PORT_FEATURE_TYPE, 48280Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 48290Sstevel@tonic-gate CFS_C_PORT_CONNECTION, 48300Sstevel@tonic-gate port, 48310Sstevel@tonic-gate 0, NULL, 0, 48320Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != 48330Sstevel@tonic-gate USB_SUCCESS) { 48340Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 48350Sstevel@tonic-gate hubd->h_log_handle, 48360Sstevel@tonic-gate "clear feature CFS_C_PORT_CONNECTION" 48370Sstevel@tonic-gate " port%d failed (%d 0x%x %d)", 48380Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 48390Sstevel@tonic-gate } 48400Sstevel@tonic-gate } 48410Sstevel@tonic-gate if (*change & PORT_CHANGE_PESC & ack_flag) { 48420Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 48430Sstevel@tonic-gate "clearing feature CFS_C_PORT_ENABLE"); 48440Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 48450Sstevel@tonic-gate hubd->h_default_pipe, 48461001Ssl147100 HUB_HANDLE_PORT_FEATURE_TYPE, 48470Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 48480Sstevel@tonic-gate CFS_C_PORT_ENABLE, 48490Sstevel@tonic-gate port, 48500Sstevel@tonic-gate 0, NULL, 0, 48510Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != 48520Sstevel@tonic-gate USB_SUCCESS) { 48530Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 48540Sstevel@tonic-gate hubd->h_log_handle, 48550Sstevel@tonic-gate "clear feature CFS_C_PORT_ENABLE" 48560Sstevel@tonic-gate " port%d failed (%d 0x%x %d)", 48570Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 48580Sstevel@tonic-gate } 48590Sstevel@tonic-gate } 48600Sstevel@tonic-gate if (*change & PORT_CHANGE_PSSC & ack_flag) { 48610Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 48620Sstevel@tonic-gate "clearing feature CFS_C_PORT_SUSPEND"); 48630Sstevel@tonic-gate 48640Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 48650Sstevel@tonic-gate hubd->h_default_pipe, 48661001Ssl147100 HUB_HANDLE_PORT_FEATURE_TYPE, 48670Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 48680Sstevel@tonic-gate CFS_C_PORT_SUSPEND, 48690Sstevel@tonic-gate port, 48700Sstevel@tonic-gate 0, NULL, 0, 48710Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != 48720Sstevel@tonic-gate USB_SUCCESS) { 48730Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 48740Sstevel@tonic-gate hubd->h_log_handle, 48750Sstevel@tonic-gate "clear feature CFS_C_PORT_SUSPEND" 48760Sstevel@tonic-gate " port%d failed (%d 0x%x %d)", 48770Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 48780Sstevel@tonic-gate } 48790Sstevel@tonic-gate } 48800Sstevel@tonic-gate if (*change & PORT_CHANGE_OCIC & ack_flag) { 48810Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 48820Sstevel@tonic-gate "clearing feature CFS_C_PORT_OVER_CURRENT"); 48830Sstevel@tonic-gate 48840Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 48850Sstevel@tonic-gate hubd->h_default_pipe, 48861001Ssl147100 HUB_HANDLE_PORT_FEATURE_TYPE, 48870Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 48880Sstevel@tonic-gate CFS_C_PORT_OVER_CURRENT, 48890Sstevel@tonic-gate port, 48900Sstevel@tonic-gate 0, NULL, 0, 48910Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != 48920Sstevel@tonic-gate USB_SUCCESS) { 48930Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 48940Sstevel@tonic-gate hubd->h_log_handle, 48950Sstevel@tonic-gate "clear feature CFS_C_PORT_OVER_CURRENT" 48960Sstevel@tonic-gate " port%d failed (%d 0x%x %d)", 48970Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 48980Sstevel@tonic-gate } 48990Sstevel@tonic-gate } 49000Sstevel@tonic-gate if (*change & PORT_CHANGE_PRSC & ack_flag) { 49010Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 49020Sstevel@tonic-gate "clearing feature CFS_C_PORT_RESET"); 49030Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 49040Sstevel@tonic-gate hubd->h_default_pipe, 49051001Ssl147100 HUB_HANDLE_PORT_FEATURE_TYPE, 49060Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 49070Sstevel@tonic-gate CFS_C_PORT_RESET, 49080Sstevel@tonic-gate port, 49090Sstevel@tonic-gate 0, NULL, 0, 49100Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != 49110Sstevel@tonic-gate USB_SUCCESS) { 49120Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 49130Sstevel@tonic-gate hubd->h_log_handle, 49140Sstevel@tonic-gate "clear feature CFS_C_PORT_RESET" 49150Sstevel@tonic-gate " port%d failed (%d 0x%x %d)", 49160Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 49170Sstevel@tonic-gate } 49180Sstevel@tonic-gate } 49190Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 49200Sstevel@tonic-gate } 49210Sstevel@tonic-gate 49220Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 49230Sstevel@tonic-gate "new port%d state 0x%x", port, hubd->h_port_state[port]); 49240Sstevel@tonic-gate 49250Sstevel@tonic-gate 49260Sstevel@tonic-gate return (USB_SUCCESS); 49270Sstevel@tonic-gate } 49280Sstevel@tonic-gate 49290Sstevel@tonic-gate 49300Sstevel@tonic-gate /* 49310Sstevel@tonic-gate * hubd_recover_disabled_port 49320Sstevel@tonic-gate * if the port got disabled because of an error 49330Sstevel@tonic-gate * enable it. If hub doesn't suport enable port, 49340Sstevel@tonic-gate * reset the port to bring the device to life again 49350Sstevel@tonic-gate */ 49360Sstevel@tonic-gate static int 49370Sstevel@tonic-gate hubd_recover_disabled_port(hubd_t *hubd, usb_port_t port) 49380Sstevel@tonic-gate { 49390Sstevel@tonic-gate uint16_t status; 49400Sstevel@tonic-gate uint16_t change; 49410Sstevel@tonic-gate int rval = USB_FAILURE; 49420Sstevel@tonic-gate 49430Sstevel@tonic-gate /* first try enabling the port */ 49440Sstevel@tonic-gate (void) hubd_enable_port(hubd, port); 49450Sstevel@tonic-gate 49460Sstevel@tonic-gate /* read the port status */ 49470Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, &status, &change, 49480Sstevel@tonic-gate PORT_CHANGE_PESC); 49490Sstevel@tonic-gate 49500Sstevel@tonic-gate if (status & PORT_STATUS_PES) { 49510Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 4952*4763Slg150142 "Port%d now Enabled", port); 49530Sstevel@tonic-gate } else if (status & PORT_STATUS_CCS) { 49540Sstevel@tonic-gate /* first post a disconnect event to the child */ 49550Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 49560Sstevel@tonic-gate hubd_post_event(hubd, port, USBA_EVENT_TAG_HOT_REMOVAL); 49570Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 49580Sstevel@tonic-gate 49590Sstevel@tonic-gate /* then reset the port and recover the device */ 49600Sstevel@tonic-gate rval = hubd_handle_port_connect(hubd, port); 49610Sstevel@tonic-gate 49620Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 4963*4763Slg150142 "Port%d now Enabled by force", port); 49640Sstevel@tonic-gate } 49650Sstevel@tonic-gate 49660Sstevel@tonic-gate return (rval); 49670Sstevel@tonic-gate } 49680Sstevel@tonic-gate 49690Sstevel@tonic-gate 49700Sstevel@tonic-gate /* 49710Sstevel@tonic-gate * hubd_enable_all_port_power: 49720Sstevel@tonic-gate */ 49730Sstevel@tonic-gate static int 49740Sstevel@tonic-gate hubd_enable_all_port_power(hubd_t *hubd) 49750Sstevel@tonic-gate { 49760Sstevel@tonic-gate usb_hub_descr_t *hub_descr; 49770Sstevel@tonic-gate int wait; 49780Sstevel@tonic-gate usb_port_t port; 49790Sstevel@tonic-gate uint_t retry; 49800Sstevel@tonic-gate uint16_t status; 49810Sstevel@tonic-gate uint16_t change; 49820Sstevel@tonic-gate 49830Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 49840Sstevel@tonic-gate "hubd_enable_all_port_power"); 49850Sstevel@tonic-gate 49860Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 49870Sstevel@tonic-gate 49880Sstevel@tonic-gate hub_descr = &hubd->h_hub_descr; 49890Sstevel@tonic-gate 49900Sstevel@tonic-gate /* 49910Sstevel@tonic-gate * According to section 11.11 of USB, for hubs with no power 49920Sstevel@tonic-gate * switches, bPwrOn2PwrGood is zero. But we wait for some 49930Sstevel@tonic-gate * arbitrary time to enable power to become stable. 49940Sstevel@tonic-gate * 49950Sstevel@tonic-gate * If an hub supports port power switching, we need to wait 49960Sstevel@tonic-gate * at least 20ms before accessing corresponding usb port. 49970Sstevel@tonic-gate */ 49980Sstevel@tonic-gate if ((hub_descr->wHubCharacteristics & 49990Sstevel@tonic-gate HUB_CHARS_NO_POWER_SWITCHING) || (!hub_descr->bPwrOn2PwrGood)) { 50000Sstevel@tonic-gate wait = hubd_device_delay / 10; 50010Sstevel@tonic-gate } else { 50020Sstevel@tonic-gate wait = max(HUB_DEFAULT_POPG, 50030Sstevel@tonic-gate hub_descr->bPwrOn2PwrGood) * 2 * 1000; 50040Sstevel@tonic-gate } 50050Sstevel@tonic-gate 50060Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 50070Sstevel@tonic-gate "hubd_enable_all_port_power: popg=%d wait=%d", 50080Sstevel@tonic-gate hub_descr->bPwrOn2PwrGood, wait); 50090Sstevel@tonic-gate 50100Sstevel@tonic-gate /* 50110Sstevel@tonic-gate * Enable power per port. we ignore gang power and power mask 50120Sstevel@tonic-gate * and always enable all ports one by one. 50130Sstevel@tonic-gate */ 50140Sstevel@tonic-gate for (port = 1; port <= hub_descr->bNbrPorts; port++) { 50150Sstevel@tonic-gate /* 50160Sstevel@tonic-gate * Transition the port from the Powered Off to the 50170Sstevel@tonic-gate * Disconnected state by supplying power to the port. 50180Sstevel@tonic-gate */ 50190Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, 50200Sstevel@tonic-gate hubd->h_log_handle, 50210Sstevel@tonic-gate "hubd_enable_all_port_power: power port=%d", port); 50220Sstevel@tonic-gate 50230Sstevel@tonic-gate (void) hubd_enable_port_power(hubd, port); 50240Sstevel@tonic-gate } 50250Sstevel@tonic-gate 50260Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 50270Sstevel@tonic-gate delay(drv_usectohz(wait)); 50280Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 50290Sstevel@tonic-gate 50300Sstevel@tonic-gate /* For retry if any, use some extra delay */ 50310Sstevel@tonic-gate wait = max(wait, hubd_device_delay / 10); 50320Sstevel@tonic-gate 50330Sstevel@tonic-gate /* Check each port power status for a given usb hub */ 50340Sstevel@tonic-gate for (port = 1; port <= hub_descr->bNbrPorts; port++) { 50350Sstevel@tonic-gate 50360Sstevel@tonic-gate /* Get port status */ 50370Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 50380Sstevel@tonic-gate &status, &change, 0); 50390Sstevel@tonic-gate 50400Sstevel@tonic-gate for (retry = 0; ((!(status & PORT_STATUS_PPS)) && 50410Sstevel@tonic-gate (retry < HUBD_PORT_RETRY)); retry++) { 50420Sstevel@tonic-gate 50430Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 50440Sstevel@tonic-gate "Retry is in progress %d: port %d status %d", 50450Sstevel@tonic-gate retry, port, status); 50460Sstevel@tonic-gate 50470Sstevel@tonic-gate (void) hubd_enable_port_power(hubd, port); 50480Sstevel@tonic-gate 50490Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 50500Sstevel@tonic-gate delay(drv_usectohz(wait)); 50510Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 50520Sstevel@tonic-gate 50530Sstevel@tonic-gate /* Get port status */ 50540Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 50550Sstevel@tonic-gate &status, &change, 0); 50560Sstevel@tonic-gate } 50570Sstevel@tonic-gate 50580Sstevel@tonic-gate /* Print warning message if port has no power */ 50590Sstevel@tonic-gate if (!(status & PORT_STATUS_PPS)) { 50600Sstevel@tonic-gate 5061978Sfrits USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 50620Sstevel@tonic-gate "hubd_enable_all_port_power: port %d power-on " 50630Sstevel@tonic-gate "failed, port status 0x%x", port, status); 50640Sstevel@tonic-gate } 50650Sstevel@tonic-gate } 50660Sstevel@tonic-gate 50670Sstevel@tonic-gate return (USB_SUCCESS); 50680Sstevel@tonic-gate } 50690Sstevel@tonic-gate 50700Sstevel@tonic-gate 50710Sstevel@tonic-gate /* 50720Sstevel@tonic-gate * hubd_enable_port_power: 50730Sstevel@tonic-gate * enable individual port power 50740Sstevel@tonic-gate */ 50750Sstevel@tonic-gate static int 50760Sstevel@tonic-gate hubd_enable_port_power(hubd_t *hubd, usb_port_t port) 50770Sstevel@tonic-gate { 50780Sstevel@tonic-gate int rval; 50790Sstevel@tonic-gate usb_cr_t completion_reason; 50800Sstevel@tonic-gate usb_cb_flags_t cb_flags; 50810Sstevel@tonic-gate 50820Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 50830Sstevel@tonic-gate "hubd_enable_port_power: port=%d", port); 50840Sstevel@tonic-gate 50850Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 50860Sstevel@tonic-gate ASSERT(hubd->h_default_pipe != 0); 50870Sstevel@tonic-gate 50880Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 50890Sstevel@tonic-gate 50900Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 50910Sstevel@tonic-gate hubd->h_default_pipe, 50921001Ssl147100 HUB_HANDLE_PORT_FEATURE_TYPE, 50930Sstevel@tonic-gate USB_REQ_SET_FEATURE, 50940Sstevel@tonic-gate CFS_PORT_POWER, 50950Sstevel@tonic-gate port, 50960Sstevel@tonic-gate 0, NULL, 0, 50970Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 50980Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 50990Sstevel@tonic-gate "set port power failed (%d 0x%x %d)", 51000Sstevel@tonic-gate completion_reason, cb_flags, rval); 51010Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 51020Sstevel@tonic-gate 51030Sstevel@tonic-gate return (USB_FAILURE); 51040Sstevel@tonic-gate } else { 51050Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 51060Sstevel@tonic-gate hubd->h_port_state[port] |= PORT_STATUS_PPS; 51070Sstevel@tonic-gate 51080Sstevel@tonic-gate return (USB_SUCCESS); 51090Sstevel@tonic-gate } 51100Sstevel@tonic-gate } 51110Sstevel@tonic-gate 51120Sstevel@tonic-gate 51130Sstevel@tonic-gate /* 51140Sstevel@tonic-gate * hubd_disable_all_port_power: 51150Sstevel@tonic-gate */ 51160Sstevel@tonic-gate static int 51170Sstevel@tonic-gate hubd_disable_all_port_power(hubd_t *hubd) 51180Sstevel@tonic-gate { 51190Sstevel@tonic-gate usb_port_t port; 51200Sstevel@tonic-gate 51210Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 51220Sstevel@tonic-gate "hubd_disable_all_port_power"); 51230Sstevel@tonic-gate 51240Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 51250Sstevel@tonic-gate 51260Sstevel@tonic-gate /* 51270Sstevel@tonic-gate * disable power per port, ignore gang power and power mask 51280Sstevel@tonic-gate */ 51290Sstevel@tonic-gate for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { 51300Sstevel@tonic-gate (void) hubd_disable_port_power(hubd, port); 51310Sstevel@tonic-gate } 51320Sstevel@tonic-gate 51330Sstevel@tonic-gate return (USB_SUCCESS); 51340Sstevel@tonic-gate } 51350Sstevel@tonic-gate 51360Sstevel@tonic-gate 51370Sstevel@tonic-gate /* 51380Sstevel@tonic-gate * hubd_disable_port_power: 51390Sstevel@tonic-gate * disable individual port power 51400Sstevel@tonic-gate */ 51410Sstevel@tonic-gate static int 51420Sstevel@tonic-gate hubd_disable_port_power(hubd_t *hubd, usb_port_t port) 51430Sstevel@tonic-gate { 51440Sstevel@tonic-gate int rval; 51450Sstevel@tonic-gate usb_cr_t completion_reason; 51460Sstevel@tonic-gate usb_cb_flags_t cb_flags; 51470Sstevel@tonic-gate 51480Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 51490Sstevel@tonic-gate "hubd_disable_port_power: port=%d", port); 51500Sstevel@tonic-gate 51510Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 51520Sstevel@tonic-gate 51530Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 51540Sstevel@tonic-gate 51550Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 51560Sstevel@tonic-gate hubd->h_default_pipe, 51571001Ssl147100 HUB_HANDLE_PORT_FEATURE_TYPE, 51580Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 51590Sstevel@tonic-gate CFS_PORT_POWER, 51600Sstevel@tonic-gate port, 51610Sstevel@tonic-gate 0, NULL, 0, 51620Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 51630Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 51640Sstevel@tonic-gate "clearing port%d power failed (%d 0x%x %d)", 51650Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 51660Sstevel@tonic-gate 51670Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 51680Sstevel@tonic-gate 51690Sstevel@tonic-gate return (USB_FAILURE); 51700Sstevel@tonic-gate } else { 51710Sstevel@tonic-gate 51720Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 51730Sstevel@tonic-gate ASSERT(completion_reason == 0); 51740Sstevel@tonic-gate hubd->h_port_state[port] &= ~PORT_STATUS_PPS; 51750Sstevel@tonic-gate 51760Sstevel@tonic-gate return (USB_SUCCESS); 51770Sstevel@tonic-gate } 51780Sstevel@tonic-gate } 51790Sstevel@tonic-gate 51800Sstevel@tonic-gate 51810Sstevel@tonic-gate /* 51820Sstevel@tonic-gate * Search the database of user preferences and find out the preferred 51830Sstevel@tonic-gate * configuration for this new device 51840Sstevel@tonic-gate */ 51850Sstevel@tonic-gate static int 51860Sstevel@tonic-gate hubd_select_device_configuration(hubd_t *hubd, usb_port_t port, 51870Sstevel@tonic-gate dev_info_t *child_dip, usba_device_t *child_ud) 51880Sstevel@tonic-gate { 51890Sstevel@tonic-gate char *pathname = NULL; 51900Sstevel@tonic-gate char *tmp_path = NULL; 51910Sstevel@tonic-gate int user_conf; 51920Sstevel@tonic-gate int pathlen; 51930Sstevel@tonic-gate usb_dev_descr_t *usbdev_ptr; 51940Sstevel@tonic-gate usba_configrec_t *user_pref; 51950Sstevel@tonic-gate 51960Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 51970Sstevel@tonic-gate usbdev_ptr = child_ud->usb_dev_descr; 51980Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 51990Sstevel@tonic-gate 52000Sstevel@tonic-gate /* try to get pathname for this device */ 52010Sstevel@tonic-gate tmp_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 52020Sstevel@tonic-gate (void) ddi_pathname(child_dip, tmp_path); 52030Sstevel@tonic-gate 52040Sstevel@tonic-gate pathlen = strlen(tmp_path) + 32; 52050Sstevel@tonic-gate pathname = kmem_zalloc(pathlen, KM_SLEEP); 52060Sstevel@tonic-gate 52070Sstevel@tonic-gate /* 52080Sstevel@tonic-gate * We haven't initialized the node and it doesn't have an address 52090Sstevel@tonic-gate * yet. Append port number to the physical pathname 52100Sstevel@tonic-gate */ 52110Sstevel@tonic-gate (void) sprintf(pathname, "%s@%d", tmp_path, port); 52120Sstevel@tonic-gate 52130Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 52140Sstevel@tonic-gate "hubd_select_device_configuration: Device=%s\n\t" 52150Sstevel@tonic-gate "Child path=%s", 52160Sstevel@tonic-gate usba_get_mfg_prod_sn_str(child_dip, tmp_path, MAXPATHLEN), 52170Sstevel@tonic-gate pathname); 52180Sstevel@tonic-gate kmem_free(tmp_path, MAXPATHLEN); 52190Sstevel@tonic-gate 52200Sstevel@tonic-gate 52210Sstevel@tonic-gate /* database search for user preferences */ 52220Sstevel@tonic-gate user_pref = usba_devdb_get_user_preferences(usbdev_ptr->idVendor, 52230Sstevel@tonic-gate usbdev_ptr->idProduct, child_ud->usb_serialno_str, pathname); 52240Sstevel@tonic-gate 52250Sstevel@tonic-gate if (user_pref) { 52260Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 52270Sstevel@tonic-gate "hubd_select_device_configuration: " 52280Sstevel@tonic-gate "usba_devdb_get_user_preferences " 52290Sstevel@tonic-gate "return user_conf=%d\npreferred driver=%s path=%s", 52300Sstevel@tonic-gate user_pref->cfg_index, user_pref->driver, 52310Sstevel@tonic-gate user_pref->pathname); 52320Sstevel@tonic-gate 52330Sstevel@tonic-gate user_conf = user_pref->cfg_index; 52340Sstevel@tonic-gate 52350Sstevel@tonic-gate if (user_pref->driver) { 52360Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 52370Sstevel@tonic-gate child_ud->usb_preferred_driver = user_pref->driver; 52380Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 52390Sstevel@tonic-gate } 52400Sstevel@tonic-gate } else { 52410Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 52420Sstevel@tonic-gate "hubd_select_device_configuration: No match found"); 52430Sstevel@tonic-gate 52440Sstevel@tonic-gate /* select default configuration for this device */ 52450Sstevel@tonic-gate user_conf = USBA_DEV_CONFIG_INDEX_UNDEFINED; 52460Sstevel@tonic-gate } 52470Sstevel@tonic-gate kmem_free(pathname, pathlen); 52480Sstevel@tonic-gate 52490Sstevel@tonic-gate /* if the device has just one configuration, set default value */ 52500Sstevel@tonic-gate if (usbdev_ptr->bNumConfigurations == 1) { 52510Sstevel@tonic-gate user_conf = USB_DEV_DEFAULT_CONFIG_INDEX; 52520Sstevel@tonic-gate } 52530Sstevel@tonic-gate 52540Sstevel@tonic-gate return (user_conf); 52550Sstevel@tonic-gate } 52560Sstevel@tonic-gate 52570Sstevel@tonic-gate 52580Sstevel@tonic-gate /* 52590Sstevel@tonic-gate * Retrieves config cloud for this configuration 52600Sstevel@tonic-gate */ 52610Sstevel@tonic-gate int 52620Sstevel@tonic-gate hubd_get_this_config_cloud(hubd_t *hubd, dev_info_t *dip, 52630Sstevel@tonic-gate usba_device_t *child_ud, uint16_t conf_index) 52640Sstevel@tonic-gate { 52650Sstevel@tonic-gate usb_cfg_descr_t *confdescr; 52660Sstevel@tonic-gate mblk_t *pdata = NULL; 52670Sstevel@tonic-gate int rval; 52680Sstevel@tonic-gate size_t size; 52690Sstevel@tonic-gate char *tmpbuf; 52700Sstevel@tonic-gate usb_cr_t completion_reason; 52710Sstevel@tonic-gate usb_cb_flags_t cb_flags; 52720Sstevel@tonic-gate usb_pipe_handle_t def_ph; 52730Sstevel@tonic-gate 52740Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 52750Sstevel@tonic-gate "hubd_get_this_config_cloud: conf_index=%d", conf_index); 52760Sstevel@tonic-gate 52770Sstevel@tonic-gate 52780Sstevel@tonic-gate /* alloc temporary space for config descriptor */ 52790Sstevel@tonic-gate confdescr = (usb_cfg_descr_t *)kmem_zalloc(USB_CFG_DESCR_SIZE, 52800Sstevel@tonic-gate KM_SLEEP); 52810Sstevel@tonic-gate 52820Sstevel@tonic-gate /* alloc temporary space for string descriptor */ 52830Sstevel@tonic-gate tmpbuf = kmem_zalloc(USB_MAXSTRINGLEN, KM_SLEEP); 52840Sstevel@tonic-gate 52850Sstevel@tonic-gate def_ph = usba_get_dflt_pipe_handle(dip); 52860Sstevel@tonic-gate 52870Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(dip, def_ph, 52880Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD, 52890Sstevel@tonic-gate USB_REQ_GET_DESCR, 52900Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_CFG | conf_index, 52910Sstevel@tonic-gate 0, 52920Sstevel@tonic-gate USB_CFG_DESCR_SIZE, 52930Sstevel@tonic-gate &pdata, 52940Sstevel@tonic-gate 0, 52950Sstevel@tonic-gate &completion_reason, 52960Sstevel@tonic-gate &cb_flags, 52970Sstevel@tonic-gate 0)) == USB_SUCCESS) { 52980Sstevel@tonic-gate 52990Sstevel@tonic-gate /* this must be true since we didn't allow data underruns */ 53001001Ssl147100 if ((pdata->b_wptr - pdata->b_rptr) != USB_CFG_DESCR_SIZE) { 53011001Ssl147100 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 53021001Ssl147100 "device returned incorrect configuration " 53031001Ssl147100 "descriptor size."); 53041001Ssl147100 53051001Ssl147100 rval = USB_FAILURE; 53061001Ssl147100 goto done; 53071001Ssl147100 } 53080Sstevel@tonic-gate 53090Sstevel@tonic-gate /* 53100Sstevel@tonic-gate * Parse the configuration descriptor 53110Sstevel@tonic-gate */ 53120Sstevel@tonic-gate size = usb_parse_cfg_descr(pdata->b_rptr, 53130Sstevel@tonic-gate pdata->b_wptr - pdata->b_rptr, confdescr, 53140Sstevel@tonic-gate USB_CFG_DESCR_SIZE); 53150Sstevel@tonic-gate 53160Sstevel@tonic-gate /* if parse cfg descr error, it should return failure */ 53170Sstevel@tonic-gate if (size == USB_PARSE_ERROR) { 53180Sstevel@tonic-gate 53190Sstevel@tonic-gate if (pdata->b_rptr[1] != USB_DESCR_TYPE_CFG) { 5320978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 53210Sstevel@tonic-gate hubd->h_log_handle, 53220Sstevel@tonic-gate "device returned incorrect " 53230Sstevel@tonic-gate "configuration descriptor type."); 53240Sstevel@tonic-gate } 53250Sstevel@tonic-gate rval = USB_FAILURE; 53260Sstevel@tonic-gate goto done; 53270Sstevel@tonic-gate } 53280Sstevel@tonic-gate 53290Sstevel@tonic-gate if (confdescr->wTotalLength < USB_CFG_DESCR_SIZE) { 5330978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 53310Sstevel@tonic-gate hubd->h_log_handle, 53320Sstevel@tonic-gate "device returned incorrect " 53330Sstevel@tonic-gate "configuration descriptor size."); 53340Sstevel@tonic-gate 53350Sstevel@tonic-gate rval = USB_FAILURE; 53360Sstevel@tonic-gate goto done; 53370Sstevel@tonic-gate } 53380Sstevel@tonic-gate 53390Sstevel@tonic-gate freemsg(pdata); 53400Sstevel@tonic-gate pdata = NULL; 53410Sstevel@tonic-gate 53420Sstevel@tonic-gate /* Now fetch the complete config cloud */ 53430Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(dip, def_ph, 53440Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD, 53450Sstevel@tonic-gate USB_REQ_GET_DESCR, 53460Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_CFG | conf_index, 53470Sstevel@tonic-gate 0, 53480Sstevel@tonic-gate confdescr->wTotalLength, 53490Sstevel@tonic-gate &pdata, 53500Sstevel@tonic-gate 0, 53510Sstevel@tonic-gate &completion_reason, 53520Sstevel@tonic-gate &cb_flags, 53530Sstevel@tonic-gate 0)) == USB_SUCCESS) { 53540Sstevel@tonic-gate 53550Sstevel@tonic-gate if ((pdata->b_wptr - pdata->b_rptr) != 53560Sstevel@tonic-gate confdescr->wTotalLength) { 53570Sstevel@tonic-gate 5358978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 53590Sstevel@tonic-gate hubd->h_log_handle, 53600Sstevel@tonic-gate "device returned incorrect " 53610Sstevel@tonic-gate "configuration descriptor."); 53620Sstevel@tonic-gate 53630Sstevel@tonic-gate rval = USB_FAILURE; 53640Sstevel@tonic-gate goto done; 53650Sstevel@tonic-gate } 53660Sstevel@tonic-gate 53670Sstevel@tonic-gate /* 53680Sstevel@tonic-gate * copy config descriptor into usba_device 53690Sstevel@tonic-gate */ 53700Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 53710Sstevel@tonic-gate child_ud->usb_cfg_array[conf_index] = 53720Sstevel@tonic-gate kmem_alloc(confdescr->wTotalLength, KM_SLEEP); 53730Sstevel@tonic-gate child_ud->usb_cfg_array_len[conf_index] = 5374*4763Slg150142 confdescr->wTotalLength; 53750Sstevel@tonic-gate bcopy((caddr_t)pdata->b_rptr, 53760Sstevel@tonic-gate (caddr_t)child_ud->usb_cfg_array[conf_index], 53770Sstevel@tonic-gate confdescr->wTotalLength); 53780Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 53790Sstevel@tonic-gate 53800Sstevel@tonic-gate /* 53810Sstevel@tonic-gate * retrieve string descriptor describing this 53820Sstevel@tonic-gate * configuration 53830Sstevel@tonic-gate */ 53840Sstevel@tonic-gate if (confdescr->iConfiguration) { 53850Sstevel@tonic-gate 53860Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, 53870Sstevel@tonic-gate hubd->h_log_handle, 53880Sstevel@tonic-gate "Get conf str descr for config_index=%d", 53890Sstevel@tonic-gate conf_index); 53900Sstevel@tonic-gate 53910Sstevel@tonic-gate /* 53920Sstevel@tonic-gate * Now fetch the string descriptor describing 53930Sstevel@tonic-gate * this configuration 53940Sstevel@tonic-gate */ 53950Sstevel@tonic-gate if ((rval = usb_get_string_descr(dip, 53960Sstevel@tonic-gate USB_LANG_ID, confdescr->iConfiguration, 53970Sstevel@tonic-gate tmpbuf, USB_MAXSTRINGLEN)) == 53980Sstevel@tonic-gate USB_SUCCESS) { 53990Sstevel@tonic-gate size = strlen(tmpbuf); 54000Sstevel@tonic-gate if (size > 0) { 54010Sstevel@tonic-gate child_ud->usb_cfg_str_descr 54020Sstevel@tonic-gate [conf_index] = (char *) 54030Sstevel@tonic-gate kmem_zalloc(size + 1, 54040Sstevel@tonic-gate KM_SLEEP); 54050Sstevel@tonic-gate (void) strcpy( 54060Sstevel@tonic-gate child_ud->usb_cfg_str_descr 54070Sstevel@tonic-gate [conf_index], tmpbuf); 54080Sstevel@tonic-gate } 54090Sstevel@tonic-gate } else { 54100Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 54110Sstevel@tonic-gate hubd->h_log_handle, 54120Sstevel@tonic-gate "hubd_get_this_config_cloud: " 54130Sstevel@tonic-gate "getting config string (%d) " 54140Sstevel@tonic-gate "failed", 54150Sstevel@tonic-gate confdescr->iConfiguration); 54160Sstevel@tonic-gate 54170Sstevel@tonic-gate /* ignore this error */ 54180Sstevel@tonic-gate rval = USB_SUCCESS; 54190Sstevel@tonic-gate } 54200Sstevel@tonic-gate } 54210Sstevel@tonic-gate } 54220Sstevel@tonic-gate } 54230Sstevel@tonic-gate 54240Sstevel@tonic-gate done: 54250Sstevel@tonic-gate if (rval != USB_SUCCESS) { 54260Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 54270Sstevel@tonic-gate "hubd_get_this_config_cloud: " 54280Sstevel@tonic-gate "error in retrieving config descriptor for " 54290Sstevel@tonic-gate "config index=%d rval=%d cr=%d", 54300Sstevel@tonic-gate conf_index, rval, completion_reason); 54310Sstevel@tonic-gate } 54320Sstevel@tonic-gate 54330Sstevel@tonic-gate if (pdata) { 54340Sstevel@tonic-gate freemsg(pdata); 54350Sstevel@tonic-gate pdata = NULL; 54360Sstevel@tonic-gate } 54370Sstevel@tonic-gate 54380Sstevel@tonic-gate kmem_free(confdescr, USB_CFG_DESCR_SIZE); 54390Sstevel@tonic-gate kmem_free(tmpbuf, USB_MAXSTRINGLEN); 54400Sstevel@tonic-gate 54410Sstevel@tonic-gate return (rval); 54420Sstevel@tonic-gate } 54430Sstevel@tonic-gate 54440Sstevel@tonic-gate 54450Sstevel@tonic-gate /* 54460Sstevel@tonic-gate * Retrieves the entire config cloud for all configurations of the device 54470Sstevel@tonic-gate */ 54480Sstevel@tonic-gate int 54490Sstevel@tonic-gate hubd_get_all_device_config_cloud(hubd_t *hubd, dev_info_t *dip, 54500Sstevel@tonic-gate usba_device_t *child_ud) 54510Sstevel@tonic-gate { 54520Sstevel@tonic-gate int rval = USB_SUCCESS; 54530Sstevel@tonic-gate int ncfgs; 54540Sstevel@tonic-gate uint16_t size; 54550Sstevel@tonic-gate uint16_t conf_index; 54560Sstevel@tonic-gate uchar_t **cfg_array; 54570Sstevel@tonic-gate uint16_t *cfg_array_len; 54580Sstevel@tonic-gate char **str_descr; 54590Sstevel@tonic-gate 54600Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 54610Sstevel@tonic-gate "hubd_get_all_device_config_cloud: Start"); 54620Sstevel@tonic-gate 54630Sstevel@tonic-gate /* alloc pointer array for conf. descriptors */ 54640Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 54650Sstevel@tonic-gate ncfgs = child_ud->usb_n_cfgs; 54660Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 54670Sstevel@tonic-gate 54680Sstevel@tonic-gate size = sizeof (uchar_t *) * ncfgs; 54690Sstevel@tonic-gate cfg_array = kmem_zalloc(size, KM_SLEEP); 54700Sstevel@tonic-gate cfg_array_len = kmem_zalloc(ncfgs * sizeof (uint16_t), KM_SLEEP); 54710Sstevel@tonic-gate str_descr = kmem_zalloc(size, KM_SLEEP); 54720Sstevel@tonic-gate 54730Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 54740Sstevel@tonic-gate child_ud->usb_cfg_array = cfg_array; 54750Sstevel@tonic-gate child_ud->usb_cfg_array_len = cfg_array_len; 54760Sstevel@tonic-gate child_ud->usb_cfg_array_length = size; 54770Sstevel@tonic-gate child_ud->usb_cfg_array_len_length = ncfgs * sizeof (uint16_t); 54780Sstevel@tonic-gate child_ud->usb_cfg_str_descr = str_descr; 54790Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 54800Sstevel@tonic-gate 54810Sstevel@tonic-gate /* Get configuration descriptor for each configuration */ 54820Sstevel@tonic-gate for (conf_index = 0; (conf_index < ncfgs) && 54830Sstevel@tonic-gate (rval == USB_SUCCESS); conf_index++) { 54840Sstevel@tonic-gate 54850Sstevel@tonic-gate rval = hubd_get_this_config_cloud(hubd, dip, child_ud, 54860Sstevel@tonic-gate conf_index); 54870Sstevel@tonic-gate } 54880Sstevel@tonic-gate 54890Sstevel@tonic-gate return (rval); 54900Sstevel@tonic-gate } 54910Sstevel@tonic-gate 54920Sstevel@tonic-gate 54930Sstevel@tonic-gate /* 54940Sstevel@tonic-gate * hubd_ready_device: 54950Sstevel@tonic-gate * Update the usba_device structure 54960Sstevel@tonic-gate * Set the given configuration 54970Sstevel@tonic-gate * Prepares the device node for driver to online. If an existing 54980Sstevel@tonic-gate * OBP node is found, it will switch to the OBP node. 54990Sstevel@tonic-gate */ 55000Sstevel@tonic-gate static dev_info_t * 55010Sstevel@tonic-gate hubd_ready_device(hubd_t *hubd, dev_info_t *child_dip, usba_device_t *child_ud, 55021001Ssl147100 uint_t config_index) 55030Sstevel@tonic-gate { 55040Sstevel@tonic-gate usb_cr_t completion_reason; 55050Sstevel@tonic-gate usb_cb_flags_t cb_flags; 55060Sstevel@tonic-gate size_t size; 55070Sstevel@tonic-gate usb_cfg_descr_t config_descriptor; 55080Sstevel@tonic-gate usb_pipe_handle_t def_ph; 55090Sstevel@tonic-gate usba_pipe_handle_data_t *ph; 55100Sstevel@tonic-gate 55110Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 55120Sstevel@tonic-gate "hubd_ready_device: dip=0x%p, user_conf_index=%d", child_dip, 55130Sstevel@tonic-gate config_index); 55140Sstevel@tonic-gate 55150Sstevel@tonic-gate size = usb_parse_cfg_descr( 55160Sstevel@tonic-gate child_ud->usb_cfg_array[config_index], USB_CFG_DESCR_SIZE, 55170Sstevel@tonic-gate &config_descriptor, USB_CFG_DESCR_SIZE); 55180Sstevel@tonic-gate ASSERT(size == USB_CFG_DESCR_SIZE); 55190Sstevel@tonic-gate 55200Sstevel@tonic-gate def_ph = usba_get_dflt_pipe_handle(child_dip); 55210Sstevel@tonic-gate 55220Sstevel@tonic-gate /* Set the configuration */ 55230Sstevel@tonic-gate (void) usb_pipe_sync_ctrl_xfer(child_dip, def_ph, 55240Sstevel@tonic-gate USB_DEV_REQ_HOST_TO_DEV, 55250Sstevel@tonic-gate USB_REQ_SET_CFG, /* bRequest */ 55260Sstevel@tonic-gate config_descriptor.bConfigurationValue, /* wValue */ 55270Sstevel@tonic-gate 0, /* wIndex */ 55280Sstevel@tonic-gate 0, /* wLength */ 55290Sstevel@tonic-gate NULL, 55300Sstevel@tonic-gate 0, 55310Sstevel@tonic-gate &completion_reason, 55320Sstevel@tonic-gate &cb_flags, 55330Sstevel@tonic-gate 0); 55340Sstevel@tonic-gate 55350Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 55360Sstevel@tonic-gate child_ud->usb_active_cfg_ndx = config_index; 55370Sstevel@tonic-gate child_ud->usb_cfg = child_ud->usb_cfg_array[config_index]; 55380Sstevel@tonic-gate child_ud->usb_cfg_length = config_descriptor.wTotalLength; 55390Sstevel@tonic-gate child_ud->usb_cfg_value = config_descriptor.bConfigurationValue; 55400Sstevel@tonic-gate child_ud->usb_n_ifs = config_descriptor.bNumInterfaces; 55410Sstevel@tonic-gate child_ud->usb_dip = child_dip; 55420Sstevel@tonic-gate 55430Sstevel@tonic-gate child_ud->usb_client_flags = kmem_zalloc( 5544*4763Slg150142 child_ud->usb_n_ifs * USBA_CLIENT_FLAG_SIZE, KM_SLEEP); 55450Sstevel@tonic-gate 55460Sstevel@tonic-gate child_ud->usb_client_attach_list = kmem_zalloc( 5547*4763Slg150142 child_ud->usb_n_ifs * 5548*4763Slg150142 sizeof (*child_ud->usb_client_attach_list), KM_SLEEP); 55490Sstevel@tonic-gate 55500Sstevel@tonic-gate child_ud->usb_client_ev_cb_list = kmem_zalloc( 5551*4763Slg150142 child_ud->usb_n_ifs * 5552*4763Slg150142 sizeof (*child_ud->usb_client_ev_cb_list), KM_SLEEP); 55530Sstevel@tonic-gate 55540Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 55550Sstevel@tonic-gate 55560Sstevel@tonic-gate /* ready the device node */ 55570Sstevel@tonic-gate child_dip = usba_ready_device_node(child_dip); 55580Sstevel@tonic-gate 55590Sstevel@tonic-gate /* set owner of default pipe to child dip */ 55600Sstevel@tonic-gate ph = usba_get_ph_data(def_ph); 55610Sstevel@tonic-gate mutex_enter(&ph->p_mutex); 55620Sstevel@tonic-gate mutex_enter(&ph->p_ph_impl->usba_ph_mutex); 55630Sstevel@tonic-gate ph->p_ph_impl->usba_ph_dip = ph->p_dip = child_dip; 55640Sstevel@tonic-gate mutex_exit(&ph->p_ph_impl->usba_ph_mutex); 55650Sstevel@tonic-gate mutex_exit(&ph->p_mutex); 55660Sstevel@tonic-gate 55670Sstevel@tonic-gate return (child_dip); 55680Sstevel@tonic-gate } 55690Sstevel@tonic-gate 55700Sstevel@tonic-gate 55710Sstevel@tonic-gate /* 55720Sstevel@tonic-gate * hubd_create_child 55730Sstevel@tonic-gate * - create child dip 55740Sstevel@tonic-gate * - open default pipe 55750Sstevel@tonic-gate * - get device descriptor 55760Sstevel@tonic-gate * - set the address 55770Sstevel@tonic-gate * - get device string descriptors 55780Sstevel@tonic-gate * - get the entire config cloud (all configurations) of the device 55790Sstevel@tonic-gate * - set user preferred configuration 55800Sstevel@tonic-gate * - close default pipe 55810Sstevel@tonic-gate * - load appropriate driver(s) 55820Sstevel@tonic-gate */ 55830Sstevel@tonic-gate static int 55840Sstevel@tonic-gate hubd_create_child(dev_info_t *dip, 55850Sstevel@tonic-gate hubd_t *hubd, 55860Sstevel@tonic-gate usba_device_t *hubd_ud, 55870Sstevel@tonic-gate usb_port_status_t port_status, 55880Sstevel@tonic-gate usb_port_t port, 55890Sstevel@tonic-gate int iteration) 55900Sstevel@tonic-gate { 55910Sstevel@tonic-gate dev_info_t *child_dip = NULL; 55920Sstevel@tonic-gate usb_dev_descr_t usb_dev_descr; 55930Sstevel@tonic-gate int rval; 55940Sstevel@tonic-gate usba_device_t *child_ud = NULL; 55950Sstevel@tonic-gate usba_device_t *parent_ud = NULL; 55960Sstevel@tonic-gate usb_pipe_handle_t ph = NULL; /* default pipe handle */ 55970Sstevel@tonic-gate mblk_t *pdata = NULL; 55980Sstevel@tonic-gate usb_cr_t completion_reason; 55991001Ssl147100 int user_conf_index; 56001001Ssl147100 uint_t config_index; 56010Sstevel@tonic-gate usb_cb_flags_t cb_flags; 56020Sstevel@tonic-gate uchar_t address = 0; 56030Sstevel@tonic-gate uint16_t length; 56040Sstevel@tonic-gate size_t size; 56050Sstevel@tonic-gate usb_addr_t parent_usb_addr; 56060Sstevel@tonic-gate usb_port_t parent_usb_port; 56070Sstevel@tonic-gate usba_device_t *parent_usba_dev; 56080Sstevel@tonic-gate usb_port_status_t parent_port_status; 56090Sstevel@tonic-gate 56100Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 56110Sstevel@tonic-gate "hubd_create_child: port=%d", port); 56120Sstevel@tonic-gate 56130Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 56140Sstevel@tonic-gate ASSERT(hubd->h_usba_devices[port] == NULL); 56150Sstevel@tonic-gate 56160Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 56170Sstevel@tonic-gate 56180Sstevel@tonic-gate /* 56190Sstevel@tonic-gate * create a dip which can be used to open the pipe. we set 56200Sstevel@tonic-gate * the name after getting the descriptors from the device 56210Sstevel@tonic-gate */ 56220Sstevel@tonic-gate rval = usba_create_child_devi(dip, 5623*4763Slg150142 "device", /* driver name */ 5624*4763Slg150142 hubd_ud->usb_hcdi_ops, /* usba_hcdi ops */ 5625*4763Slg150142 hubd_ud->usb_root_hub_dip, 5626*4763Slg150142 port_status, /* low speed device */ 5627*4763Slg150142 child_ud, 5628*4763Slg150142 &child_dip); 56290Sstevel@tonic-gate 56300Sstevel@tonic-gate if (rval != USB_SUCCESS) { 56310Sstevel@tonic-gate 5632978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 56330Sstevel@tonic-gate "usb_create_child_devi failed (%d)", rval); 56340Sstevel@tonic-gate 56350Sstevel@tonic-gate goto fail_cleanup; 56360Sstevel@tonic-gate } 56370Sstevel@tonic-gate 56380Sstevel@tonic-gate child_ud = usba_get_usba_device(child_dip); 56390Sstevel@tonic-gate ASSERT(child_ud != NULL); 56400Sstevel@tonic-gate 56410Sstevel@tonic-gate parent_ud = hubd->h_usba_device; 56420Sstevel@tonic-gate mutex_enter(&parent_ud->usb_mutex); 56430Sstevel@tonic-gate parent_port_status = parent_ud->usb_port_status; 56440Sstevel@tonic-gate 56450Sstevel@tonic-gate /* 56460Sstevel@tonic-gate * To support split transactions, update address and port 56470Sstevel@tonic-gate * of high speed hub to which given device is connected. 56480Sstevel@tonic-gate */ 56490Sstevel@tonic-gate if (parent_port_status == USBA_HIGH_SPEED_DEV) { 56500Sstevel@tonic-gate parent_usba_dev = parent_ud; 56510Sstevel@tonic-gate parent_usb_addr = parent_ud->usb_addr; 56520Sstevel@tonic-gate parent_usb_port = port; 56530Sstevel@tonic-gate } else { 56540Sstevel@tonic-gate parent_usba_dev = parent_ud->usb_hs_hub_usba_dev; 56550Sstevel@tonic-gate parent_usb_addr = parent_ud->usb_hs_hub_addr; 56560Sstevel@tonic-gate parent_usb_port = parent_ud->usb_hs_hub_port; 56570Sstevel@tonic-gate } 56580Sstevel@tonic-gate mutex_exit(&parent_ud->usb_mutex); 56590Sstevel@tonic-gate 56600Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 56610Sstevel@tonic-gate address = child_ud->usb_addr; 56620Sstevel@tonic-gate child_ud->usb_addr = 0; 56630Sstevel@tonic-gate child_ud->usb_dev_descr = kmem_alloc(sizeof (usb_dev_descr_t), 56640Sstevel@tonic-gate KM_SLEEP); 56650Sstevel@tonic-gate bzero(&usb_dev_descr, sizeof (usb_dev_descr_t)); 56660Sstevel@tonic-gate usb_dev_descr.bMaxPacketSize0 = 56670Sstevel@tonic-gate (port_status == USBA_LOW_SPEED_DEV) ? 8 : 64; 56680Sstevel@tonic-gate bcopy(&usb_dev_descr, child_ud->usb_dev_descr, 56690Sstevel@tonic-gate sizeof (usb_dev_descr_t)); 56700Sstevel@tonic-gate child_ud->usb_port = port; 56710Sstevel@tonic-gate child_ud->usb_hs_hub_usba_dev = parent_usba_dev; 56720Sstevel@tonic-gate child_ud->usb_hs_hub_addr = parent_usb_addr; 56730Sstevel@tonic-gate child_ud->usb_hs_hub_port = parent_usb_port; 56740Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 56750Sstevel@tonic-gate 56760Sstevel@tonic-gate /* Open the default pipe */ 56770Sstevel@tonic-gate if ((rval = usb_pipe_open(child_dip, NULL, NULL, 56780Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) != USB_SUCCESS) { 5679978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5680978Sfrits "usb_pipe_open failed (%d)", rval); 56810Sstevel@tonic-gate 56820Sstevel@tonic-gate goto fail_cleanup; 56830Sstevel@tonic-gate } 56840Sstevel@tonic-gate 56850Sstevel@tonic-gate /* 56860Sstevel@tonic-gate * get device descriptor 56870Sstevel@tonic-gate */ 56880Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 56890Sstevel@tonic-gate "hubd_create_child: get device descriptor: 64 bytes"); 56900Sstevel@tonic-gate 56910Sstevel@tonic-gate rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 56920Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD, 56930Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */ 56940Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_DEV, /* wValue */ 56950Sstevel@tonic-gate 0, /* wIndex */ 56960Sstevel@tonic-gate 64, /* wLength */ 56970Sstevel@tonic-gate &pdata, USB_ATTRS_SHORT_XFER_OK, 56980Sstevel@tonic-gate &completion_reason, &cb_flags, 0); 56990Sstevel@tonic-gate 57000Sstevel@tonic-gate if ((rval != USB_SUCCESS) && 57010Sstevel@tonic-gate (!((completion_reason == USB_CR_DATA_OVERRUN) && pdata))) { 57020Sstevel@tonic-gate 57030Sstevel@tonic-gate /* 57040Sstevel@tonic-gate * rval != USB_SUCCESS AND 57050Sstevel@tonic-gate * completion_reason != USB_CR_DATA_OVERRUN 57060Sstevel@tonic-gate * pdata could be != NULL. 57070Sstevel@tonic-gate * Free pdata now to prevent memory leak. 57080Sstevel@tonic-gate */ 57090Sstevel@tonic-gate freemsg(pdata); 57100Sstevel@tonic-gate pdata = NULL; 57110Sstevel@tonic-gate 57120Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 57130Sstevel@tonic-gate "hubd_create_child: get device descriptor: 8 bytes"); 57140Sstevel@tonic-gate 57150Sstevel@tonic-gate rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 57160Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD, 57170Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */ 57180Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_DEV, /* wValue */ 57190Sstevel@tonic-gate 0, /* wIndex */ 57200Sstevel@tonic-gate 8, /* wLength */ 57210Sstevel@tonic-gate &pdata, USB_ATTRS_NONE, 57220Sstevel@tonic-gate &completion_reason, &cb_flags, 0); 57230Sstevel@tonic-gate 57240Sstevel@tonic-gate if (rval != USB_SUCCESS) { 5725978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 57260Sstevel@tonic-gate "getting device descriptor failed (%s 0x%x %d)", 57270Sstevel@tonic-gate usb_str_cr(completion_reason), cb_flags, rval); 57280Sstevel@tonic-gate goto fail_cleanup; 57290Sstevel@tonic-gate } 57300Sstevel@tonic-gate } else { 57310Sstevel@tonic-gate ASSERT(completion_reason == USB_CR_OK); 57320Sstevel@tonic-gate } 57330Sstevel@tonic-gate 57340Sstevel@tonic-gate ASSERT(pdata != NULL); 57350Sstevel@tonic-gate 57360Sstevel@tonic-gate size = usb_parse_dev_descr( 5737*4763Slg150142 pdata->b_rptr, 5738*4763Slg150142 pdata->b_wptr - pdata->b_rptr, 5739*4763Slg150142 &usb_dev_descr, 5740*4763Slg150142 sizeof (usb_dev_descr_t)); 57410Sstevel@tonic-gate 57420Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 57430Sstevel@tonic-gate "parsing device descriptor returned %lu", size); 57440Sstevel@tonic-gate 57450Sstevel@tonic-gate length = *(pdata->b_rptr); 57460Sstevel@tonic-gate freemsg(pdata); 57470Sstevel@tonic-gate pdata = NULL; 57480Sstevel@tonic-gate if (size < 8) { 5749978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 57500Sstevel@tonic-gate "get device descriptor returned %lu bytes", size); 57510Sstevel@tonic-gate 57520Sstevel@tonic-gate goto fail_cleanup; 57530Sstevel@tonic-gate } 57540Sstevel@tonic-gate 57550Sstevel@tonic-gate if (length < 8) { 5756978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 57570Sstevel@tonic-gate "fail enumeration: bLength=%d", length); 57580Sstevel@tonic-gate 57590Sstevel@tonic-gate goto fail_cleanup; 57600Sstevel@tonic-gate } 57610Sstevel@tonic-gate 57620Sstevel@tonic-gate /* Set the address of the device */ 57630Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 57640Sstevel@tonic-gate USB_DEV_REQ_HOST_TO_DEV, 57650Sstevel@tonic-gate USB_REQ_SET_ADDRESS, /* bRequest */ 57660Sstevel@tonic-gate address, /* wValue */ 57670Sstevel@tonic-gate 0, /* wIndex */ 57680Sstevel@tonic-gate 0, /* wLength */ 57690Sstevel@tonic-gate NULL, 0, 57700Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 57710Sstevel@tonic-gate char buffer[64]; 5772978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 57730Sstevel@tonic-gate "setting address failed (cr=%s cb_flags=%s rval=%d)", 57740Sstevel@tonic-gate usb_str_cr(completion_reason), 57750Sstevel@tonic-gate usb_str_cb_flags(cb_flags, buffer, sizeof (buffer)), 57760Sstevel@tonic-gate rval); 57770Sstevel@tonic-gate 57780Sstevel@tonic-gate goto fail_cleanup; 57790Sstevel@tonic-gate } 57800Sstevel@tonic-gate 57810Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 57820Sstevel@tonic-gate "set address 0x%x done", address); 57830Sstevel@tonic-gate 57840Sstevel@tonic-gate /* now close the pipe for addr 0 */ 57850Sstevel@tonic-gate usb_pipe_close(child_dip, ph, 57860Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL); 57870Sstevel@tonic-gate 57880Sstevel@tonic-gate /* 57890Sstevel@tonic-gate * This delay is important for the CATC hub to enumerate 57900Sstevel@tonic-gate * But, avoid delay in the first iteration 57910Sstevel@tonic-gate */ 57920Sstevel@tonic-gate if (iteration) { 57930Sstevel@tonic-gate delay(drv_usectohz(hubd_device_delay/100)); 57940Sstevel@tonic-gate } 57950Sstevel@tonic-gate 57960Sstevel@tonic-gate /* assign the address in the usba_device structure */ 57970Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 57980Sstevel@tonic-gate child_ud->usb_addr = address; 57990Sstevel@tonic-gate child_ud->usb_no_cpr = 0; 58000Sstevel@tonic-gate child_ud->usb_port_status = port_status; 58010Sstevel@tonic-gate /* save this device descriptor */ 58020Sstevel@tonic-gate bcopy(&usb_dev_descr, child_ud->usb_dev_descr, 5803*4763Slg150142 sizeof (usb_dev_descr_t)); 58040Sstevel@tonic-gate child_ud->usb_n_cfgs = usb_dev_descr.bNumConfigurations; 58050Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 58060Sstevel@tonic-gate 58070Sstevel@tonic-gate /* re-open the pipe for the device with the new address */ 58080Sstevel@tonic-gate if ((rval = usb_pipe_open(child_dip, NULL, NULL, 58090Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) != USB_SUCCESS) { 5810978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 58110Sstevel@tonic-gate "usb_pipe_open failed (%d)", rval); 58120Sstevel@tonic-gate 58130Sstevel@tonic-gate goto fail_cleanup; 58140Sstevel@tonic-gate } 58150Sstevel@tonic-gate 58160Sstevel@tonic-gate /* 58170Sstevel@tonic-gate * Get full device descriptor only if we have not received full 58180Sstevel@tonic-gate * device descriptor earlier. 58190Sstevel@tonic-gate */ 58200Sstevel@tonic-gate if (size < length) { 58210Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 58220Sstevel@tonic-gate "hubd_create_child: get full device descriptor: " 58230Sstevel@tonic-gate "%d bytes", length); 58240Sstevel@tonic-gate 58250Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 58260Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD, 58270Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */ 58280Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_DEV, /* wValue */ 58290Sstevel@tonic-gate 0, /* wIndex */ 58300Sstevel@tonic-gate length, /* wLength */ 58310Sstevel@tonic-gate &pdata, 0, 58320Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 58330Sstevel@tonic-gate freemsg(pdata); 58340Sstevel@tonic-gate pdata = NULL; 58350Sstevel@tonic-gate 58360Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, 58370Sstevel@tonic-gate hubd->h_log_handle, 58380Sstevel@tonic-gate "hubd_create_child: get full device descriptor: " 58390Sstevel@tonic-gate "64 bytes"); 58400Sstevel@tonic-gate 58410Sstevel@tonic-gate rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 58420Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | 58430Sstevel@tonic-gate USB_DEV_REQ_TYPE_STANDARD, 58440Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */ 58450Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_DEV, /* wValue */ 58460Sstevel@tonic-gate 0, /* wIndex */ 58470Sstevel@tonic-gate 64, /* wLength */ 58480Sstevel@tonic-gate &pdata, USB_ATTRS_SHORT_XFER_OK, 58490Sstevel@tonic-gate &completion_reason, &cb_flags, 0); 58500Sstevel@tonic-gate 58510Sstevel@tonic-gate /* we have to trust the data now */ 58520Sstevel@tonic-gate if (pdata) { 58530Sstevel@tonic-gate int len = *(pdata->b_rptr); 58540Sstevel@tonic-gate 58550Sstevel@tonic-gate length = pdata->b_wptr - pdata->b_rptr; 58560Sstevel@tonic-gate if (length < len) { 58570Sstevel@tonic-gate 58580Sstevel@tonic-gate goto fail_cleanup; 58590Sstevel@tonic-gate } 58600Sstevel@tonic-gate } else if (rval != USB_SUCCESS) { 5861978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 58620Sstevel@tonic-gate hubd->h_log_handle, 58630Sstevel@tonic-gate "getting device descriptor failed " 58640Sstevel@tonic-gate "(%d 0x%x %d)", 5865*4763Slg150142 completion_reason, cb_flags, rval); 58660Sstevel@tonic-gate 58670Sstevel@tonic-gate goto fail_cleanup; 58680Sstevel@tonic-gate } 58690Sstevel@tonic-gate } 58700Sstevel@tonic-gate 58710Sstevel@tonic-gate size = usb_parse_dev_descr( 5872*4763Slg150142 pdata->b_rptr, 5873*4763Slg150142 pdata->b_wptr - pdata->b_rptr, 5874*4763Slg150142 &usb_dev_descr, 5875*4763Slg150142 sizeof (usb_dev_descr_t)); 58760Sstevel@tonic-gate 58770Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 58780Sstevel@tonic-gate "parsing device descriptor returned %lu", size); 58790Sstevel@tonic-gate 58800Sstevel@tonic-gate /* 58810Sstevel@tonic-gate * For now, free the data 58820Sstevel@tonic-gate * eventually, each configuration may need to be looked at 58830Sstevel@tonic-gate */ 58840Sstevel@tonic-gate freemsg(pdata); 58850Sstevel@tonic-gate pdata = NULL; 58860Sstevel@tonic-gate 58870Sstevel@tonic-gate if (size != USB_DEV_DESCR_SIZE) { 5888978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 58890Sstevel@tonic-gate "fail enumeration: descriptor size=%lu " 58900Sstevel@tonic-gate "expected size=%u", size, USB_DEV_DESCR_SIZE); 58910Sstevel@tonic-gate 58920Sstevel@tonic-gate goto fail_cleanup; 58930Sstevel@tonic-gate } 58940Sstevel@tonic-gate 58950Sstevel@tonic-gate /* 58960Sstevel@tonic-gate * save the device descriptor in usba_device since it is needed 58970Sstevel@tonic-gate * later on again 58980Sstevel@tonic-gate */ 58990Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 59000Sstevel@tonic-gate bcopy(&usb_dev_descr, child_ud->usb_dev_descr, 5901*4763Slg150142 sizeof (usb_dev_descr_t)); 59020Sstevel@tonic-gate child_ud->usb_n_cfgs = usb_dev_descr.bNumConfigurations; 59030Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 59040Sstevel@tonic-gate } 59050Sstevel@tonic-gate 59060Sstevel@tonic-gate if (usb_dev_descr.bNumConfigurations == 0) { 5907978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 59080Sstevel@tonic-gate "device descriptor:\n\t" 59090Sstevel@tonic-gate "l=0x%x type=0x%x USB=0x%x class=0x%x subclass=0x%x\n\t" 59100Sstevel@tonic-gate "protocol=0x%x maxpktsize=0x%x " 59110Sstevel@tonic-gate "Vid=0x%x Pid=0x%x rel=0x%x\n\t" 59120Sstevel@tonic-gate "Mfg=0x%x P=0x%x sn=0x%x #config=0x%x", 59130Sstevel@tonic-gate usb_dev_descr.bLength, usb_dev_descr.bDescriptorType, 59140Sstevel@tonic-gate usb_dev_descr.bcdUSB, usb_dev_descr.bDeviceClass, 59150Sstevel@tonic-gate usb_dev_descr.bDeviceSubClass, 59160Sstevel@tonic-gate usb_dev_descr.bDeviceProtocol, 59170Sstevel@tonic-gate usb_dev_descr.bMaxPacketSize0, 59180Sstevel@tonic-gate usb_dev_descr.idVendor, 59190Sstevel@tonic-gate usb_dev_descr.idProduct, usb_dev_descr.bcdDevice, 59200Sstevel@tonic-gate usb_dev_descr.iManufacturer, usb_dev_descr.iProduct, 59210Sstevel@tonic-gate usb_dev_descr.iSerialNumber, 59220Sstevel@tonic-gate usb_dev_descr.bNumConfigurations); 59230Sstevel@tonic-gate goto fail_cleanup; 59240Sstevel@tonic-gate } 59250Sstevel@tonic-gate 59260Sstevel@tonic-gate 59270Sstevel@tonic-gate /* get the device string descriptor(s) */ 59280Sstevel@tonic-gate usba_get_dev_string_descrs(child_dip, child_ud); 59290Sstevel@tonic-gate 59300Sstevel@tonic-gate /* retrieve config cloud for all configurations */ 59310Sstevel@tonic-gate rval = hubd_get_all_device_config_cloud(hubd, child_dip, child_ud); 59320Sstevel@tonic-gate if (rval != USB_SUCCESS) { 5933978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 59340Sstevel@tonic-gate "failed to get configuration descriptor(s)"); 59350Sstevel@tonic-gate 59360Sstevel@tonic-gate goto fail_cleanup; 59370Sstevel@tonic-gate } 59380Sstevel@tonic-gate 59390Sstevel@tonic-gate /* get the preferred configuration for this device */ 59400Sstevel@tonic-gate user_conf_index = hubd_select_device_configuration(hubd, port, 59410Sstevel@tonic-gate child_dip, child_ud); 59420Sstevel@tonic-gate 59430Sstevel@tonic-gate /* Check if the user selected configuration index is in range */ 59441001Ssl147100 if ((user_conf_index >= usb_dev_descr.bNumConfigurations) || 59451001Ssl147100 (user_conf_index < 0)) { 5946978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 59470Sstevel@tonic-gate "Configuration index for device idVendor=%d " 59480Sstevel@tonic-gate "idProduct=%d is=%d, and is out of range[0..%d]", 59490Sstevel@tonic-gate usb_dev_descr.idVendor, usb_dev_descr.idProduct, 59500Sstevel@tonic-gate user_conf_index, usb_dev_descr.bNumConfigurations - 1); 59510Sstevel@tonic-gate 59520Sstevel@tonic-gate /* treat this as user didn't specify configuration */ 59530Sstevel@tonic-gate user_conf_index = USBA_DEV_CONFIG_INDEX_UNDEFINED; 59540Sstevel@tonic-gate } 59550Sstevel@tonic-gate 59560Sstevel@tonic-gate 59570Sstevel@tonic-gate /* 59580Sstevel@tonic-gate * Warn users of a performance hit if connecting a 59590Sstevel@tonic-gate * High Speed behind a 1.1 hub, which is behind a 59600Sstevel@tonic-gate * 2.0 port. 59610Sstevel@tonic-gate */ 59620Sstevel@tonic-gate if ((parent_port_status != USBA_HIGH_SPEED_DEV) && 59630Sstevel@tonic-gate !(usba_is_root_hub(parent_ud->usb_dip)) && 59640Sstevel@tonic-gate (parent_usb_addr)) { 59650Sstevel@tonic-gate 59660Sstevel@tonic-gate /* 59670Sstevel@tonic-gate * Now that we know the root port is a high speed port 59680Sstevel@tonic-gate * and that the parent port is not a high speed port, 59690Sstevel@tonic-gate * let's find out if the device itself is a high speed 59700Sstevel@tonic-gate * device. If it is a high speed device, 59710Sstevel@tonic-gate * USB_DESCR_TYPE_SETUP_DEV_QLF should return a value, 59720Sstevel@tonic-gate * otherwise the command will fail. 59730Sstevel@tonic-gate */ 59740Sstevel@tonic-gate rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 59750Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD, 59760Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */ 59770Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_DEV_QLF, /* wValue */ 59780Sstevel@tonic-gate 0, /* wIndex */ 59790Sstevel@tonic-gate 10, /* wLength */ 59800Sstevel@tonic-gate &pdata, USB_ATTRS_SHORT_XFER_OK, 59810Sstevel@tonic-gate &completion_reason, &cb_flags, 0); 59820Sstevel@tonic-gate 59830Sstevel@tonic-gate if (pdata) { 59840Sstevel@tonic-gate freemsg(pdata); 59850Sstevel@tonic-gate pdata = NULL; 59860Sstevel@tonic-gate } 59870Sstevel@tonic-gate 59880Sstevel@tonic-gate /* 59890Sstevel@tonic-gate * USB_DESCR_TYPE_SETUP_DEV_QLF query was successful 59900Sstevel@tonic-gate * that means this is a high speed device behind a 59910Sstevel@tonic-gate * high speed root hub, but running at full speed 59920Sstevel@tonic-gate * because there is a full speed hub in the middle. 59930Sstevel@tonic-gate */ 59940Sstevel@tonic-gate if (rval == USB_SUCCESS) { 5995*4763Slg150142 USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, 5996*4763Slg150142 hubd->h_log_handle, 5997*4763Slg150142 "Connecting a high speed device to a " 5998*4763Slg150142 "non high speed hub (port %d) will result " 5999*4763Slg150142 "in a loss of performance. Please connect " 6000*4763Slg150142 "the device to a high speed hub to get " 6001*4763Slg150142 "the maximum performance.", 6002*4763Slg150142 port); 60030Sstevel@tonic-gate } 60040Sstevel@tonic-gate } 60050Sstevel@tonic-gate 60060Sstevel@tonic-gate /* 60070Sstevel@tonic-gate * Now we try to online the device by attaching a driver 60080Sstevel@tonic-gate * The following truth table illustrates the logic:- 60090Sstevel@tonic-gate * Cfgndx Driver Action 60100Sstevel@tonic-gate * 0 0 loop all configs for driver with full 60110Sstevel@tonic-gate * compatible properties. 60120Sstevel@tonic-gate * 0 1 set first configuration, 60130Sstevel@tonic-gate * compatible prop = drivername. 60140Sstevel@tonic-gate * 1 0 Set config, full compatible prop 60150Sstevel@tonic-gate * 1 1 Set config, compatible prop = drivername. 60160Sstevel@tonic-gate * 60170Sstevel@tonic-gate * Note: 60180Sstevel@tonic-gate * cfgndx = user_conf_index 60190Sstevel@tonic-gate * Driver = usb_preferred_driver 60200Sstevel@tonic-gate */ 60210Sstevel@tonic-gate if (user_conf_index == USBA_DEV_CONFIG_INDEX_UNDEFINED) { 60220Sstevel@tonic-gate if (child_ud->usb_preferred_driver) { 60230Sstevel@tonic-gate /* 60240Sstevel@tonic-gate * It is the job of the "preferred driver" to put the 60250Sstevel@tonic-gate * device in the desired configuration. Till then 60260Sstevel@tonic-gate * put the device in config index 0. 60270Sstevel@tonic-gate */ 60281001Ssl147100 if ((rval = usba_hubdi_check_power_budget(dip, child_ud, 60291001Ssl147100 USB_DEV_DEFAULT_CONFIG_INDEX)) != USB_SUCCESS) { 60301001Ssl147100 60311001Ssl147100 goto fail_cleanup; 60321001Ssl147100 } 60331001Ssl147100 60340Sstevel@tonic-gate child_dip = hubd_ready_device(hubd, child_dip, 60350Sstevel@tonic-gate child_ud, USB_DEV_DEFAULT_CONFIG_INDEX); 60360Sstevel@tonic-gate 60370Sstevel@tonic-gate /* 60380Sstevel@tonic-gate * Assign the dip before onlining to avoid race 60390Sstevel@tonic-gate * with busctl 60400Sstevel@tonic-gate */ 60410Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 60420Sstevel@tonic-gate hubd->h_children_dips[port] = child_dip; 60430Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 60440Sstevel@tonic-gate 60450Sstevel@tonic-gate (void) usba_bind_driver(child_dip); 60460Sstevel@tonic-gate } else { 60470Sstevel@tonic-gate /* 60480Sstevel@tonic-gate * loop through all the configurations to see if we 60490Sstevel@tonic-gate * can find a driver for any one config. If not, set 60500Sstevel@tonic-gate * the device in config_index 0 60510Sstevel@tonic-gate */ 60520Sstevel@tonic-gate rval = USB_FAILURE; 60530Sstevel@tonic-gate for (config_index = 0; 60540Sstevel@tonic-gate (config_index < usb_dev_descr.bNumConfigurations) && 60550Sstevel@tonic-gate (rval != USB_SUCCESS); config_index++) { 60560Sstevel@tonic-gate 60570Sstevel@tonic-gate child_dip = hubd_ready_device(hubd, child_dip, 60580Sstevel@tonic-gate child_ud, config_index); 60590Sstevel@tonic-gate 60600Sstevel@tonic-gate /* 60610Sstevel@tonic-gate * Assign the dip before onlining to avoid race 60620Sstevel@tonic-gate * with busctl 60630Sstevel@tonic-gate */ 60640Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 60650Sstevel@tonic-gate hubd->h_children_dips[port] = child_dip; 60660Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 60670Sstevel@tonic-gate 60680Sstevel@tonic-gate rval = usba_bind_driver(child_dip); 60691001Ssl147100 60701001Ssl147100 /* 60711001Ssl147100 * Normally power budget should be checked 60721001Ssl147100 * before device is configured. A failure in 60731001Ssl147100 * power budget checking will stop the device 60741001Ssl147100 * from being configured with current 60751001Ssl147100 * config_index and may enable the device to 60761001Ssl147100 * be configured in another configuration. 60771001Ssl147100 * This may break the user experience that a 60781001Ssl147100 * device which previously worked in config 60791001Ssl147100 * A now works in config B after power budget 60801001Ssl147100 * control is enabled. To avoid such situation, 60811001Ssl147100 * power budget checking is moved here and will 60821001Ssl147100 * fail the child creation directly if config 60831001Ssl147100 * A exceeds the power available. 60841001Ssl147100 */ 60851001Ssl147100 if (rval == USB_SUCCESS) { 60861001Ssl147100 if ((usba_hubdi_check_power_budget(dip, 60871001Ssl147100 child_ud, config_index)) != 60881001Ssl147100 USB_SUCCESS) { 60891001Ssl147100 60901001Ssl147100 goto fail_cleanup; 60911001Ssl147100 } 60921001Ssl147100 } 60930Sstevel@tonic-gate } 60940Sstevel@tonic-gate if (rval != USB_SUCCESS) { 60951001Ssl147100 60961001Ssl147100 if ((usba_hubdi_check_power_budget(dip, 60971001Ssl147100 child_ud, 0)) != USB_SUCCESS) { 60981001Ssl147100 60991001Ssl147100 goto fail_cleanup; 61001001Ssl147100 } 61011001Ssl147100 61020Sstevel@tonic-gate child_dip = hubd_ready_device(hubd, child_dip, 61030Sstevel@tonic-gate child_ud, 0); 61040Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 61050Sstevel@tonic-gate hubd->h_children_dips[port] = child_dip; 61060Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 61070Sstevel@tonic-gate } 61080Sstevel@tonic-gate } /* end else loop all configs */ 61090Sstevel@tonic-gate } else { 61101001Ssl147100 61111001Ssl147100 if ((usba_hubdi_check_power_budget(dip, child_ud, 61121001Ssl147100 (uint_t)user_conf_index)) != USB_SUCCESS) { 61131001Ssl147100 61141001Ssl147100 goto fail_cleanup; 61151001Ssl147100 } 61161001Ssl147100 61170Sstevel@tonic-gate child_dip = hubd_ready_device(hubd, child_dip, 61181001Ssl147100 child_ud, (uint_t)user_conf_index); 61190Sstevel@tonic-gate 61200Sstevel@tonic-gate /* 61210Sstevel@tonic-gate * Assign the dip before onlining to avoid race 61220Sstevel@tonic-gate * with busctl 61230Sstevel@tonic-gate */ 61240Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 61250Sstevel@tonic-gate hubd->h_children_dips[port] = child_dip; 61260Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 61270Sstevel@tonic-gate 61280Sstevel@tonic-gate (void) usba_bind_driver(child_dip); 61290Sstevel@tonic-gate } 61300Sstevel@tonic-gate 61311001Ssl147100 usba_hubdi_decr_power_budget(dip, child_ud); 61321001Ssl147100 61330Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 61340Sstevel@tonic-gate if (hubd->h_usba_devices[port] == NULL) { 61350Sstevel@tonic-gate hubd->h_usba_devices[port] = usba_get_usba_device(child_dip); 61360Sstevel@tonic-gate } else { 61370Sstevel@tonic-gate ASSERT(hubd->h_usba_devices[port] == 6138*4763Slg150142 usba_get_usba_device(child_dip)); 61390Sstevel@tonic-gate } 61400Sstevel@tonic-gate 61410Sstevel@tonic-gate return (USB_SUCCESS); 61420Sstevel@tonic-gate 61430Sstevel@tonic-gate 61440Sstevel@tonic-gate fail_cleanup: 61450Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 61460Sstevel@tonic-gate "hubd_create_child: fail_cleanup"); 61470Sstevel@tonic-gate 61480Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 61490Sstevel@tonic-gate hubd->h_children_dips[port] = NULL; 61500Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 61510Sstevel@tonic-gate 61520Sstevel@tonic-gate if (pdata) { 61530Sstevel@tonic-gate freemsg(pdata); 61540Sstevel@tonic-gate } 61550Sstevel@tonic-gate 61560Sstevel@tonic-gate if (ph) { 61570Sstevel@tonic-gate usb_pipe_close(child_dip, ph, 61580Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL); 61590Sstevel@tonic-gate } 61600Sstevel@tonic-gate 61610Sstevel@tonic-gate if (child_dip) { 61620Sstevel@tonic-gate int rval = usba_destroy_child_devi(child_dip, 61630Sstevel@tonic-gate NDI_DEVI_REMOVE); 61640Sstevel@tonic-gate if (rval != USB_SUCCESS) { 6165978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 61660Sstevel@tonic-gate "failure to remove child node"); 61670Sstevel@tonic-gate } 61680Sstevel@tonic-gate } 61690Sstevel@tonic-gate 61700Sstevel@tonic-gate if (child_ud) { 61710Sstevel@tonic-gate /* to make sure we free the address */ 61720Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 61730Sstevel@tonic-gate child_ud->usb_addr = address; 61740Sstevel@tonic-gate ASSERT(child_ud->usb_ref_count == 0); 61750Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 61760Sstevel@tonic-gate 61770Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 61780Sstevel@tonic-gate if (hubd->h_usba_devices[port] == NULL) { 61790Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 61800Sstevel@tonic-gate usba_free_usba_device(child_ud); 61810Sstevel@tonic-gate } else { 61820Sstevel@tonic-gate hubd_free_usba_device(hubd, hubd->h_usba_devices[port]); 61830Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 61840Sstevel@tonic-gate } 61850Sstevel@tonic-gate } 61860Sstevel@tonic-gate 61870Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 61880Sstevel@tonic-gate 61890Sstevel@tonic-gate return (USB_FAILURE); 61900Sstevel@tonic-gate } 61910Sstevel@tonic-gate 61920Sstevel@tonic-gate 61930Sstevel@tonic-gate /* 61940Sstevel@tonic-gate * hubd_delete_child: 61950Sstevel@tonic-gate * - free usb address 61960Sstevel@tonic-gate * - lookup child dips, there may be multiple on this port 61970Sstevel@tonic-gate * - offline each child devi 61980Sstevel@tonic-gate */ 61990Sstevel@tonic-gate static int 62000Sstevel@tonic-gate hubd_delete_child(hubd_t *hubd, usb_port_t port, uint_t flag, boolean_t retry) 62010Sstevel@tonic-gate { 62020Sstevel@tonic-gate dev_info_t *child_dip; 62030Sstevel@tonic-gate usba_device_t *usba_device; 62040Sstevel@tonic-gate int rval = USB_SUCCESS; 62050Sstevel@tonic-gate 62060Sstevel@tonic-gate child_dip = hubd->h_children_dips[port]; 62071001Ssl147100 usba_device = hubd->h_usba_devices[port]; 62080Sstevel@tonic-gate 62090Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 62100Sstevel@tonic-gate "hubd_delete_child: port=%d, dip=0x%p usba_device=0x%p", 62111001Ssl147100 port, child_dip, usba_device); 62120Sstevel@tonic-gate 62130Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 62140Sstevel@tonic-gate if (child_dip) { 62150Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 62160Sstevel@tonic-gate "hubd_delete_child:\n\t" 62170Sstevel@tonic-gate "dip = 0x%p (%s) at port %d", 62180Sstevel@tonic-gate child_dip, ddi_node_name(child_dip), port); 62190Sstevel@tonic-gate 62201001Ssl147100 if (usba_device) { 62211001Ssl147100 usba_hubdi_incr_power_budget(hubd->h_dip, usba_device); 62221001Ssl147100 } 62231001Ssl147100 62240Sstevel@tonic-gate rval = usba_destroy_child_devi(child_dip, flag); 62250Sstevel@tonic-gate 62260Sstevel@tonic-gate if ((rval == USB_SUCCESS) && (flag & NDI_DEVI_REMOVE)) { 62270Sstevel@tonic-gate /* 62280Sstevel@tonic-gate * if the child was still < DS_INITIALIZED 62290Sstevel@tonic-gate * then our bus_unconfig was not called and 62300Sstevel@tonic-gate * we have to zap the child here 62310Sstevel@tonic-gate */ 62320Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 62330Sstevel@tonic-gate if (hubd->h_children_dips[port] == child_dip) { 62340Sstevel@tonic-gate usba_device_t *ud = 6235*4763Slg150142 hubd->h_usba_devices[port]; 62360Sstevel@tonic-gate hubd->h_children_dips[port] = NULL; 62370Sstevel@tonic-gate if (ud) { 62380Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 62390Sstevel@tonic-gate 62400Sstevel@tonic-gate mutex_enter(&ud->usb_mutex); 62410Sstevel@tonic-gate ud->usb_ref_count = 0; 62420Sstevel@tonic-gate mutex_exit(&ud->usb_mutex); 62430Sstevel@tonic-gate 62440Sstevel@tonic-gate usba_free_usba_device(ud); 62450Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 62460Sstevel@tonic-gate hubd->h_usba_devices[port] = NULL; 62470Sstevel@tonic-gate } 62480Sstevel@tonic-gate } 62490Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 62500Sstevel@tonic-gate } 62510Sstevel@tonic-gate } 62520Sstevel@tonic-gate 62530Sstevel@tonic-gate if ((rval != USB_SUCCESS) && retry) { 62540Sstevel@tonic-gate 62550Sstevel@tonic-gate hubd_schedule_cleanup(usba_device->usb_root_hub_dip); 62560Sstevel@tonic-gate } 62570Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 62580Sstevel@tonic-gate 62590Sstevel@tonic-gate return (rval); 62600Sstevel@tonic-gate } 62610Sstevel@tonic-gate 62620Sstevel@tonic-gate 62630Sstevel@tonic-gate /* 62640Sstevel@tonic-gate * hubd_free_usba_device: 62650Sstevel@tonic-gate * free usb device structure unless it is associated with 62660Sstevel@tonic-gate * the root hub which is handled differently 62670Sstevel@tonic-gate */ 62680Sstevel@tonic-gate static void 62690Sstevel@tonic-gate hubd_free_usba_device(hubd_t *hubd, usba_device_t *usba_device) 62700Sstevel@tonic-gate { 62710Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 62720Sstevel@tonic-gate "hubd_free_usba_device: hubd=0x%p, usba_device=0x%p", 62730Sstevel@tonic-gate hubd, usba_device); 62740Sstevel@tonic-gate 62750Sstevel@tonic-gate if (usba_device && (usba_device->usb_addr != ROOT_HUB_ADDR)) { 62760Sstevel@tonic-gate usb_port_t port = usba_device->usb_port; 62770Sstevel@tonic-gate dev_info_t *dip = hubd->h_children_dips[port]; 62780Sstevel@tonic-gate 62790Sstevel@tonic-gate #ifdef DEBUG 62800Sstevel@tonic-gate if (dip) { 62810Sstevel@tonic-gate ASSERT(i_ddi_node_state(dip) < DS_INITIALIZED); 62820Sstevel@tonic-gate } 62830Sstevel@tonic-gate #endif 62840Sstevel@tonic-gate 62850Sstevel@tonic-gate port = usba_device->usb_port; 62860Sstevel@tonic-gate hubd->h_usba_devices[port] = NULL; 62870Sstevel@tonic-gate 62880Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 62890Sstevel@tonic-gate usba_free_usba_device(usba_device); 62900Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 62910Sstevel@tonic-gate } 62920Sstevel@tonic-gate } 62930Sstevel@tonic-gate 62940Sstevel@tonic-gate 62950Sstevel@tonic-gate /* 62960Sstevel@tonic-gate * event support 62970Sstevel@tonic-gate * 62980Sstevel@tonic-gate * busctl event support 62990Sstevel@tonic-gate */ 63000Sstevel@tonic-gate static int 63010Sstevel@tonic-gate hubd_busop_get_eventcookie(dev_info_t *dip, 63020Sstevel@tonic-gate dev_info_t *rdip, 63030Sstevel@tonic-gate char *eventname, 63040Sstevel@tonic-gate ddi_eventcookie_t *cookie) 63050Sstevel@tonic-gate { 63060Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip); 63070Sstevel@tonic-gate 63080Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 63090Sstevel@tonic-gate "hubd_busop_get_eventcookie: dip=0x%p, rdip=0x%p, " 63100Sstevel@tonic-gate "event=%s", (void *)dip, (void *)rdip, eventname); 63110Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 63120Sstevel@tonic-gate "(dip=%s%d, rdip=%s%d)", 63130Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), 63140Sstevel@tonic-gate ddi_driver_name(rdip), ddi_get_instance(rdip)); 63150Sstevel@tonic-gate 63160Sstevel@tonic-gate /* return event cookie, iblock cookie, and level */ 63170Sstevel@tonic-gate return (ndi_event_retrieve_cookie(hubd->h_ndi_event_hdl, 6318*4763Slg150142 rdip, eventname, cookie, NDI_EVENT_NOPASS)); 63190Sstevel@tonic-gate } 63200Sstevel@tonic-gate 63210Sstevel@tonic-gate 63220Sstevel@tonic-gate static int 63230Sstevel@tonic-gate hubd_busop_add_eventcall(dev_info_t *dip, 63240Sstevel@tonic-gate dev_info_t *rdip, 63250Sstevel@tonic-gate ddi_eventcookie_t cookie, 63260Sstevel@tonic-gate void (*callback)(dev_info_t *dip, 63270Sstevel@tonic-gate ddi_eventcookie_t cookie, void *arg, 63280Sstevel@tonic-gate void *bus_impldata), 63290Sstevel@tonic-gate void *arg, ddi_callback_id_t *cb_id) 63300Sstevel@tonic-gate { 63310Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip); 63320Sstevel@tonic-gate usb_port_t port = hubd_child_dip2port(hubd, rdip); 63330Sstevel@tonic-gate 63340Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 63350Sstevel@tonic-gate "hubd_busop_add_eventcall: dip=0x%p, rdip=0x%p " 63360Sstevel@tonic-gate "cookie=0x%p, cb=0x%p, arg=0x%p", 63370Sstevel@tonic-gate (void *)dip, (void *)rdip, (void *)cookie, (void *)callback, arg); 63380Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 63390Sstevel@tonic-gate "(dip=%s%d, rdip=%s%d, event=%s)", 63400Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), 63410Sstevel@tonic-gate ddi_driver_name(rdip), ddi_get_instance(rdip), 63420Sstevel@tonic-gate ndi_event_cookie_to_name(hubd->h_ndi_event_hdl, cookie)); 63430Sstevel@tonic-gate 63440Sstevel@tonic-gate /* Set flag on children registering events */ 63450Sstevel@tonic-gate switch (ndi_event_cookie_to_tag(hubd->h_ndi_event_hdl, cookie)) { 63460Sstevel@tonic-gate case USBA_EVENT_TAG_HOT_REMOVAL: 63470Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 63480Sstevel@tonic-gate hubd->h_child_events[port] |= HUBD_CHILD_EVENT_DISCONNECT; 63490Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 63500Sstevel@tonic-gate 63510Sstevel@tonic-gate break; 63520Sstevel@tonic-gate case USBA_EVENT_TAG_PRE_SUSPEND: 63530Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 63540Sstevel@tonic-gate hubd->h_child_events[port] |= HUBD_CHILD_EVENT_PRESUSPEND; 63550Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 63560Sstevel@tonic-gate 63570Sstevel@tonic-gate break; 63580Sstevel@tonic-gate default: 63590Sstevel@tonic-gate 63600Sstevel@tonic-gate break; 63610Sstevel@tonic-gate } 63620Sstevel@tonic-gate 63630Sstevel@tonic-gate /* add callback to our event set */ 63640Sstevel@tonic-gate return (ndi_event_add_callback(hubd->h_ndi_event_hdl, 6365*4763Slg150142 rdip, cookie, callback, arg, NDI_SLEEP, cb_id)); 63660Sstevel@tonic-gate } 63670Sstevel@tonic-gate 63680Sstevel@tonic-gate 63690Sstevel@tonic-gate static int 63700Sstevel@tonic-gate hubd_busop_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id) 63710Sstevel@tonic-gate { 63720Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip); 63730Sstevel@tonic-gate ndi_event_callbacks_t *id = (ndi_event_callbacks_t *)cb_id; 63740Sstevel@tonic-gate 63750Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 63760Sstevel@tonic-gate "hubd_busop_remove_eventcall: dip=0x%p, rdip=0x%p " 63770Sstevel@tonic-gate "cookie=0x%p", (void *)dip, id->ndi_evtcb_dip, 63780Sstevel@tonic-gate id->ndi_evtcb_cookie); 63790Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 63800Sstevel@tonic-gate "(dip=%s%d, rdip=%s%d, event=%s)", 63810Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), 63820Sstevel@tonic-gate ddi_driver_name(id->ndi_evtcb_dip), 63830Sstevel@tonic-gate ddi_get_instance(id->ndi_evtcb_dip), 63840Sstevel@tonic-gate ndi_event_cookie_to_name(hubd->h_ndi_event_hdl, 63850Sstevel@tonic-gate id->ndi_evtcb_cookie)); 63860Sstevel@tonic-gate 63870Sstevel@tonic-gate /* remove event registration from our event set */ 63880Sstevel@tonic-gate return (ndi_event_remove_callback(hubd->h_ndi_event_hdl, cb_id)); 63890Sstevel@tonic-gate } 63900Sstevel@tonic-gate 63910Sstevel@tonic-gate 63920Sstevel@tonic-gate /* 63930Sstevel@tonic-gate * event distribution 63940Sstevel@tonic-gate * 63950Sstevel@tonic-gate * hubd_do_callback: 63960Sstevel@tonic-gate * Post this event to the specified child 63970Sstevel@tonic-gate */ 63980Sstevel@tonic-gate static void 63990Sstevel@tonic-gate hubd_do_callback(hubd_t *hubd, dev_info_t *cdip, ddi_eventcookie_t cookie) 64000Sstevel@tonic-gate { 64010Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 64020Sstevel@tonic-gate "hubd_do_callback"); 64030Sstevel@tonic-gate 64040Sstevel@tonic-gate (void) ndi_event_do_callback(hubd->h_ndi_event_hdl, cdip, cookie, NULL); 64050Sstevel@tonic-gate } 64060Sstevel@tonic-gate 64070Sstevel@tonic-gate 64080Sstevel@tonic-gate /* 64090Sstevel@tonic-gate * hubd_run_callbacks: 64100Sstevel@tonic-gate * Send this event to all children 64110Sstevel@tonic-gate */ 64120Sstevel@tonic-gate static void 64130Sstevel@tonic-gate hubd_run_callbacks(hubd_t *hubd, usba_event_t type) 64140Sstevel@tonic-gate { 64150Sstevel@tonic-gate usb_port_t port; 64160Sstevel@tonic-gate 64170Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 64180Sstevel@tonic-gate "hubd_run_callbacks"); 64190Sstevel@tonic-gate 64200Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 64210Sstevel@tonic-gate for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { 64220Sstevel@tonic-gate /* 64230Sstevel@tonic-gate * the childen_dips list may have dips that have been 64240Sstevel@tonic-gate * already deallocated. we only get a post_detach notification 64250Sstevel@tonic-gate * but not a destroy notification 64260Sstevel@tonic-gate */ 64270Sstevel@tonic-gate if (hubd->h_children_dips[port]) { 64280Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 64290Sstevel@tonic-gate hubd_post_event(hubd, port, type); 64300Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 64310Sstevel@tonic-gate } 64320Sstevel@tonic-gate } 64330Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 64340Sstevel@tonic-gate } 64350Sstevel@tonic-gate 64360Sstevel@tonic-gate 64370Sstevel@tonic-gate /* 64380Sstevel@tonic-gate * hubd_post_event 64390Sstevel@tonic-gate * post event to a child on the port depending on the type 64400Sstevel@tonic-gate */ 64410Sstevel@tonic-gate static void 64420Sstevel@tonic-gate hubd_post_event(hubd_t *hubd, usb_port_t port, usba_event_t type) 64430Sstevel@tonic-gate { 64440Sstevel@tonic-gate int rval; 64450Sstevel@tonic-gate dev_info_t *dip; 64460Sstevel@tonic-gate usba_device_t *usba_device; 64470Sstevel@tonic-gate ddi_eventcookie_t cookie, rm_cookie, suspend_cookie; 64480Sstevel@tonic-gate 64490Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 64500Sstevel@tonic-gate "hubd_post_event: port=%d event=%s", port, 64510Sstevel@tonic-gate ndi_event_tag_to_name(hubd->h_ndi_event_hdl, type)); 64520Sstevel@tonic-gate 64530Sstevel@tonic-gate cookie = ndi_event_tag_to_cookie(hubd->h_ndi_event_hdl, type); 64540Sstevel@tonic-gate rm_cookie = ndi_event_tag_to_cookie(hubd->h_ndi_event_hdl, 64550Sstevel@tonic-gate USBA_EVENT_TAG_HOT_REMOVAL); 64560Sstevel@tonic-gate suspend_cookie = ndi_event_tag_to_cookie(hubd->h_ndi_event_hdl, 64570Sstevel@tonic-gate USBA_EVENT_TAG_PRE_SUSPEND); 64580Sstevel@tonic-gate 64590Sstevel@tonic-gate /* 64600Sstevel@tonic-gate * Hotplug daemon may be attaching a driver that may be registering 64610Sstevel@tonic-gate * event callbacks. So it already has got the device tree lock and 64620Sstevel@tonic-gate * event handle mutex. So to prevent a deadlock while posting events, 64630Sstevel@tonic-gate * we grab and release the locks in the same order. 64640Sstevel@tonic-gate */ 64650Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 64660Sstevel@tonic-gate dip = hubd->h_children_dips[port]; 64670Sstevel@tonic-gate usba_device = hubd->h_usba_devices[port]; 64680Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 64690Sstevel@tonic-gate 64700Sstevel@tonic-gate switch (type) { 64710Sstevel@tonic-gate case USBA_EVENT_TAG_HOT_REMOVAL: 64720Sstevel@tonic-gate /* Clear the registered event flag */ 64730Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 64740Sstevel@tonic-gate hubd->h_child_events[port] &= ~HUBD_CHILD_EVENT_DISCONNECT; 64750Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 64760Sstevel@tonic-gate 64770Sstevel@tonic-gate hubd_do_callback(hubd, dip, cookie); 64780Sstevel@tonic-gate usba_persistent_pipe_close(usba_device); 64790Sstevel@tonic-gate 64800Sstevel@tonic-gate /* 64810Sstevel@tonic-gate * Mark the dip for deletion only after the driver has 64820Sstevel@tonic-gate * seen the disconnect event to prevent cleanup thread 64830Sstevel@tonic-gate * from stepping in between. 64840Sstevel@tonic-gate */ 6485495Scth mutex_enter(&(DEVI(dip)->devi_lock)); 64860Sstevel@tonic-gate DEVI_SET_DEVICE_REMOVED(dip); 6487495Scth mutex_exit(&(DEVI(dip)->devi_lock)); 64880Sstevel@tonic-gate 64890Sstevel@tonic-gate break; 64900Sstevel@tonic-gate case USBA_EVENT_TAG_PRE_SUSPEND: 64910Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 64920Sstevel@tonic-gate hubd->h_child_events[port] &= ~HUBD_CHILD_EVENT_PRESUSPEND; 64930Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 64940Sstevel@tonic-gate 64950Sstevel@tonic-gate hubd_do_callback(hubd, dip, cookie); 64960Sstevel@tonic-gate /* 64970Sstevel@tonic-gate * persistent pipe close for this event is taken care by the 64980Sstevel@tonic-gate * caller after verfying that all children can suspend 64990Sstevel@tonic-gate */ 65000Sstevel@tonic-gate 65010Sstevel@tonic-gate break; 65020Sstevel@tonic-gate case USBA_EVENT_TAG_HOT_INSERTION: 65030Sstevel@tonic-gate /* 65040Sstevel@tonic-gate * Check if this child has missed the disconnect event before 65050Sstevel@tonic-gate * it registered for event callbacks 65060Sstevel@tonic-gate */ 65070Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 65080Sstevel@tonic-gate if (hubd->h_child_events[port] & HUBD_CHILD_EVENT_DISCONNECT) { 65090Sstevel@tonic-gate /* clear the flag and post disconnect event */ 65100Sstevel@tonic-gate hubd->h_child_events[port] &= 65110Sstevel@tonic-gate ~HUBD_CHILD_EVENT_DISCONNECT; 65120Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 65130Sstevel@tonic-gate hubd_do_callback(hubd, dip, rm_cookie); 65140Sstevel@tonic-gate usba_persistent_pipe_close(usba_device); 65150Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 65160Sstevel@tonic-gate } 65170Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 65180Sstevel@tonic-gate 65190Sstevel@tonic-gate /* 65200Sstevel@tonic-gate * Mark the dip as reinserted to prevent cleanup thread 65210Sstevel@tonic-gate * from stepping in. 65220Sstevel@tonic-gate */ 6523495Scth mutex_enter(&(DEVI(dip)->devi_lock)); 65240Sstevel@tonic-gate DEVI_SET_DEVICE_REINSERTED(dip); 6525495Scth mutex_exit(&(DEVI(dip)->devi_lock)); 65260Sstevel@tonic-gate 65270Sstevel@tonic-gate rval = usba_persistent_pipe_open(usba_device); 65280Sstevel@tonic-gate if (rval != USB_SUCCESS) { 6529978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 65300Sstevel@tonic-gate hubd->h_log_handle, 65310Sstevel@tonic-gate "failed to reopen all pipes on reconnect"); 65320Sstevel@tonic-gate } 65330Sstevel@tonic-gate 65340Sstevel@tonic-gate hubd_do_callback(hubd, dip, cookie); 65350Sstevel@tonic-gate 65360Sstevel@tonic-gate /* 65370Sstevel@tonic-gate * We might see a connect event only if hotplug thread for 65380Sstevel@tonic-gate * disconnect event don't run in time. 65390Sstevel@tonic-gate * Set the flag again, so we don't miss posting a 65400Sstevel@tonic-gate * disconnect event. 65410Sstevel@tonic-gate */ 65420Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 65430Sstevel@tonic-gate hubd->h_child_events[port] |= HUBD_CHILD_EVENT_DISCONNECT; 65440Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 65450Sstevel@tonic-gate 65460Sstevel@tonic-gate break; 65470Sstevel@tonic-gate case USBA_EVENT_TAG_POST_RESUME: 65480Sstevel@tonic-gate /* 65490Sstevel@tonic-gate * Check if this child has missed the pre-suspend event before 65500Sstevel@tonic-gate * it registered for event callbacks 65510Sstevel@tonic-gate */ 65520Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 65530Sstevel@tonic-gate if (hubd->h_child_events[port] & HUBD_CHILD_EVENT_PRESUSPEND) { 65540Sstevel@tonic-gate /* clear the flag and post pre_suspend event */ 65550Sstevel@tonic-gate hubd->h_port_state[port] &= 65560Sstevel@tonic-gate ~HUBD_CHILD_EVENT_PRESUSPEND; 65570Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 65580Sstevel@tonic-gate hubd_do_callback(hubd, dip, suspend_cookie); 65590Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 65600Sstevel@tonic-gate } 65610Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 65620Sstevel@tonic-gate 65630Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex); 65640Sstevel@tonic-gate usba_device->usb_no_cpr = 0; 65650Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex); 65660Sstevel@tonic-gate 65670Sstevel@tonic-gate /* 65680Sstevel@tonic-gate * Since the pipe has already been opened by hub 65690Sstevel@tonic-gate * at DDI_RESUME time, there is no need for a 65700Sstevel@tonic-gate * persistent pipe open 65710Sstevel@tonic-gate */ 65720Sstevel@tonic-gate hubd_do_callback(hubd, dip, cookie); 65730Sstevel@tonic-gate 65740Sstevel@tonic-gate /* 65750Sstevel@tonic-gate * Set the flag again, so we don't miss posting a 65760Sstevel@tonic-gate * pre-suspend event. This enforces a tighter 65770Sstevel@tonic-gate * dev_state model. 65780Sstevel@tonic-gate */ 65790Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 65800Sstevel@tonic-gate hubd->h_child_events[port] |= HUBD_CHILD_EVENT_PRESUSPEND; 65810Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 65820Sstevel@tonic-gate break; 65830Sstevel@tonic-gate } 65840Sstevel@tonic-gate } 65850Sstevel@tonic-gate 65860Sstevel@tonic-gate 65870Sstevel@tonic-gate /* 65880Sstevel@tonic-gate * handling of events coming from above 65890Sstevel@tonic-gate */ 65900Sstevel@tonic-gate static int 65910Sstevel@tonic-gate hubd_disconnect_event_cb(dev_info_t *dip) 65920Sstevel@tonic-gate { 65930Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip); 65940Sstevel@tonic-gate usb_port_t port, nports; 65950Sstevel@tonic-gate usba_device_t *usba_dev; 65960Sstevel@tonic-gate usba_event_t tag = USBA_EVENT_TAG_HOT_REMOVAL; 65970Sstevel@tonic-gate int circ; 65980Sstevel@tonic-gate 65990Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 66000Sstevel@tonic-gate "hubd_disconnect_event_cb: tag=%d", tag); 66010Sstevel@tonic-gate 66020Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 66030Sstevel@tonic-gate 66040Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 66050Sstevel@tonic-gate switch (hubd->h_dev_state) { 66060Sstevel@tonic-gate case USB_DEV_ONLINE: 66070Sstevel@tonic-gate case USB_DEV_PWRED_DOWN: 66080Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_DISCONNECTED; 66090Sstevel@tonic-gate /* stop polling on the interrupt pipe */ 66100Sstevel@tonic-gate hubd_stop_polling(hubd); 66110Sstevel@tonic-gate 66120Sstevel@tonic-gate /* FALLTHROUGH */ 66130Sstevel@tonic-gate case USB_DEV_SUSPENDED: 66140Sstevel@tonic-gate /* we remain in this state */ 66150Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 66160Sstevel@tonic-gate hubd_run_callbacks(hubd, tag); 66170Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 66180Sstevel@tonic-gate 66190Sstevel@tonic-gate /* close all the open pipes of our children */ 66200Sstevel@tonic-gate nports = hubd->h_hub_descr.bNbrPorts; 66210Sstevel@tonic-gate for (port = 1; port <= nports; port++) { 66220Sstevel@tonic-gate usba_dev = hubd->h_usba_devices[port]; 66230Sstevel@tonic-gate if (usba_dev != NULL) { 66240Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 66250Sstevel@tonic-gate usba_persistent_pipe_close(usba_dev); 66260Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 66270Sstevel@tonic-gate } 66280Sstevel@tonic-gate } 66290Sstevel@tonic-gate 66300Sstevel@tonic-gate break; 66310Sstevel@tonic-gate case USB_DEV_DISCONNECTED: 66320Sstevel@tonic-gate /* avoid passing multiple disconnects to children */ 66330Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 66340Sstevel@tonic-gate "hubd_disconnect_event_cb: Already disconnected"); 66350Sstevel@tonic-gate 66360Sstevel@tonic-gate break; 66370Sstevel@tonic-gate default: 66380Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 66390Sstevel@tonic-gate "hubd_disconnect_event_cb: Illegal devstate=%d", 66400Sstevel@tonic-gate hubd->h_dev_state); 66410Sstevel@tonic-gate 66420Sstevel@tonic-gate break; 66430Sstevel@tonic-gate } 66440Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 66450Sstevel@tonic-gate 66460Sstevel@tonic-gate ndi_devi_exit(dip, circ); 66470Sstevel@tonic-gate 66480Sstevel@tonic-gate return (USB_SUCCESS); 66490Sstevel@tonic-gate } 66500Sstevel@tonic-gate 66510Sstevel@tonic-gate 66520Sstevel@tonic-gate static int 66530Sstevel@tonic-gate hubd_reconnect_event_cb(dev_info_t *dip) 66540Sstevel@tonic-gate { 66550Sstevel@tonic-gate int rval, circ; 66560Sstevel@tonic-gate 66570Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 66580Sstevel@tonic-gate rval = hubd_restore_state_cb(dip); 66590Sstevel@tonic-gate ndi_devi_exit(dip, circ); 66600Sstevel@tonic-gate 66610Sstevel@tonic-gate return (rval); 66620Sstevel@tonic-gate } 66630Sstevel@tonic-gate 66640Sstevel@tonic-gate 66650Sstevel@tonic-gate /* 66660Sstevel@tonic-gate * hubd_pre_suspend_event_cb 66670Sstevel@tonic-gate * propogate event for binary compatibility of old drivers 66680Sstevel@tonic-gate */ 66690Sstevel@tonic-gate static int 66700Sstevel@tonic-gate hubd_pre_suspend_event_cb(dev_info_t *dip) 66710Sstevel@tonic-gate { 66720Sstevel@tonic-gate int circ; 66730Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip); 66740Sstevel@tonic-gate 66750Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle, 66760Sstevel@tonic-gate "hubd_pre_suspend_event_cb"); 66770Sstevel@tonic-gate 66780Sstevel@tonic-gate /* disable hotplug thread */ 66790Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 66800Sstevel@tonic-gate hubd->h_hotplug_thread++; 66810Sstevel@tonic-gate hubd_stop_polling(hubd); 66820Sstevel@tonic-gate 66830Sstevel@tonic-gate /* keep PM out till we see a cpr resume */ 66840Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0); 66850Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 66860Sstevel@tonic-gate 66870Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 66880Sstevel@tonic-gate hubd_run_callbacks(hubd, USBA_EVENT_TAG_PRE_SUSPEND); 66890Sstevel@tonic-gate ndi_devi_exit(dip, circ); 66900Sstevel@tonic-gate 66910Sstevel@tonic-gate return (USB_SUCCESS); 66920Sstevel@tonic-gate } 66930Sstevel@tonic-gate 66940Sstevel@tonic-gate 66950Sstevel@tonic-gate /* 66960Sstevel@tonic-gate * hubd_post_resume_event_cb 66970Sstevel@tonic-gate * propogate event for binary compatibility of old drivers 66980Sstevel@tonic-gate */ 66990Sstevel@tonic-gate static int 67000Sstevel@tonic-gate hubd_post_resume_event_cb(dev_info_t *dip) 67010Sstevel@tonic-gate { 67020Sstevel@tonic-gate int circ; 67030Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip); 67040Sstevel@tonic-gate 67050Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle, 67060Sstevel@tonic-gate "hubd_post_resume_event_cb"); 67070Sstevel@tonic-gate 67080Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 67090Sstevel@tonic-gate hubd_run_callbacks(hubd, USBA_EVENT_TAG_POST_RESUME); 67100Sstevel@tonic-gate ndi_devi_exit(dip, circ); 67110Sstevel@tonic-gate 67120Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 67130Sstevel@tonic-gate 67140Sstevel@tonic-gate /* enable PM */ 67150Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0); 67160Sstevel@tonic-gate 67170Sstevel@tonic-gate /* allow hotplug thread */ 67180Sstevel@tonic-gate hubd->h_hotplug_thread--; 67190Sstevel@tonic-gate 67200Sstevel@tonic-gate /* start polling */ 67210Sstevel@tonic-gate hubd_start_polling(hubd, 0); 67220Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 67230Sstevel@tonic-gate 67240Sstevel@tonic-gate return (USB_SUCCESS); 67250Sstevel@tonic-gate } 67260Sstevel@tonic-gate 67270Sstevel@tonic-gate 67280Sstevel@tonic-gate /* 67290Sstevel@tonic-gate * hubd_cpr_suspend 67300Sstevel@tonic-gate * save the current state of the driver/device 67310Sstevel@tonic-gate */ 67320Sstevel@tonic-gate static int 67330Sstevel@tonic-gate hubd_cpr_suspend(hubd_t *hubd) 67340Sstevel@tonic-gate { 67350Sstevel@tonic-gate usb_port_t port, nports; 67360Sstevel@tonic-gate usba_device_t *usba_dev; 67370Sstevel@tonic-gate uchar_t no_cpr = 0; 67380Sstevel@tonic-gate int rval = USB_FAILURE; 67390Sstevel@tonic-gate 67400Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 67410Sstevel@tonic-gate "hubd_cpr_suspend: Begin"); 67420Sstevel@tonic-gate 67430Sstevel@tonic-gate /* Make sure device is powered up to save state. */ 67440Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 67450Sstevel@tonic-gate hubd_pm_busy_component(hubd, hubd->h_dip, 0); 67460Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 67470Sstevel@tonic-gate 67480Sstevel@tonic-gate /* bring the device to full power */ 67490Sstevel@tonic-gate (void) pm_raise_power(hubd->h_dip, 0, USB_DEV_OS_FULL_PWR); 67500Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 67510Sstevel@tonic-gate 67520Sstevel@tonic-gate switch (hubd->h_dev_state) { 67530Sstevel@tonic-gate case USB_DEV_ONLINE: 67540Sstevel@tonic-gate case USB_DEV_PWRED_DOWN: 67550Sstevel@tonic-gate case USB_DEV_DISCONNECTED: 67560Sstevel@tonic-gate /* find out if all our children have been quiesced */ 67570Sstevel@tonic-gate nports = hubd->h_hub_descr.bNbrPorts; 67580Sstevel@tonic-gate for (port = 1; (no_cpr == 0) && (port <= nports); port++) { 67590Sstevel@tonic-gate usba_dev = hubd->h_usba_devices[port]; 67600Sstevel@tonic-gate if (usba_dev != NULL) { 67610Sstevel@tonic-gate mutex_enter(&usba_dev->usb_mutex); 67620Sstevel@tonic-gate no_cpr += usba_dev->usb_no_cpr; 67630Sstevel@tonic-gate mutex_exit(&usba_dev->usb_mutex); 67640Sstevel@tonic-gate } 67650Sstevel@tonic-gate } 67660Sstevel@tonic-gate if (no_cpr > 0) { 67670Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 67680Sstevel@tonic-gate "Children busy - can't checkpoint"); 67690Sstevel@tonic-gate /* remain in same state to fail checkpoint */ 67700Sstevel@tonic-gate 67710Sstevel@tonic-gate break; 67720Sstevel@tonic-gate } else { 67730Sstevel@tonic-gate /* 67740Sstevel@tonic-gate * do not suspend if our hotplug thread 67750Sstevel@tonic-gate * or the deathrow thread is active 67760Sstevel@tonic-gate */ 67770Sstevel@tonic-gate if ((hubd->h_hotplug_thread > 1) || 67780Sstevel@tonic-gate (hubd->h_cleanup_active == B_TRUE)) { 67790Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 67800Sstevel@tonic-gate hubd->h_log_handle, 67810Sstevel@tonic-gate "hotplug thread active - can't cpr"); 67820Sstevel@tonic-gate /* remain in same state to fail checkpoint */ 67830Sstevel@tonic-gate 67840Sstevel@tonic-gate break; 67850Sstevel@tonic-gate } 67860Sstevel@tonic-gate 67870Sstevel@tonic-gate /* quiesce ourselves now */ 67880Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_SUSPENDED; 67890Sstevel@tonic-gate hubd_stop_polling(hubd); 67900Sstevel@tonic-gate 67910Sstevel@tonic-gate /* close all the open pipes of our children */ 67920Sstevel@tonic-gate for (port = 1; port <= nports; port++) { 67930Sstevel@tonic-gate usba_dev = hubd->h_usba_devices[port]; 67940Sstevel@tonic-gate if (usba_dev != NULL) { 67950Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 67960Sstevel@tonic-gate usba_persistent_pipe_close(usba_dev); 67970Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 67980Sstevel@tonic-gate } 67990Sstevel@tonic-gate } 68000Sstevel@tonic-gate /* 68010Sstevel@tonic-gate * turn off power to all the ports so that we 68020Sstevel@tonic-gate * don't see any spurious activity 68030Sstevel@tonic-gate */ 68040Sstevel@tonic-gate (void) hubd_disable_all_port_power(hubd); 68050Sstevel@tonic-gate 68060Sstevel@tonic-gate /* 68070Sstevel@tonic-gate * if we are the root hub, we close our pipes 68080Sstevel@tonic-gate * ourselves. 68090Sstevel@tonic-gate */ 68100Sstevel@tonic-gate if (usba_is_root_hub(hubd->h_dip)) { 68110Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 68120Sstevel@tonic-gate usba_persistent_pipe_close( 68130Sstevel@tonic-gate usba_get_usba_device(hubd->h_dip)); 68140Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 68150Sstevel@tonic-gate } 68160Sstevel@tonic-gate rval = USB_SUCCESS; 68170Sstevel@tonic-gate 68180Sstevel@tonic-gate break; 68190Sstevel@tonic-gate } 68200Sstevel@tonic-gate case USB_DEV_SUSPENDED: 68210Sstevel@tonic-gate default: 68220Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 68230Sstevel@tonic-gate "hubd_cpr_suspend: Illegal dev state=%d", 68240Sstevel@tonic-gate hubd->h_dev_state); 68250Sstevel@tonic-gate 68260Sstevel@tonic-gate break; 68270Sstevel@tonic-gate } 68280Sstevel@tonic-gate 68290Sstevel@tonic-gate hubd_pm_idle_component(hubd, hubd->h_dip, 0); 68300Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 68310Sstevel@tonic-gate 68320Sstevel@tonic-gate return (rval); 68330Sstevel@tonic-gate } 68340Sstevel@tonic-gate 68350Sstevel@tonic-gate static void 68360Sstevel@tonic-gate hubd_cpr_resume(dev_info_t *dip) 68370Sstevel@tonic-gate { 68380Sstevel@tonic-gate int rval, circ; 68390Sstevel@tonic-gate 68400Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 68410Sstevel@tonic-gate /* 68420Sstevel@tonic-gate * if we are the root hub, we open our pipes 68430Sstevel@tonic-gate * ourselves. 68440Sstevel@tonic-gate */ 68450Sstevel@tonic-gate if (usba_is_root_hub(dip)) { 68460Sstevel@tonic-gate rval = usba_persistent_pipe_open( 68470Sstevel@tonic-gate usba_get_usba_device(dip)); 68480Sstevel@tonic-gate ASSERT(rval == USB_SUCCESS); 68490Sstevel@tonic-gate } 68500Sstevel@tonic-gate (void) hubd_restore_state_cb(dip); 68510Sstevel@tonic-gate ndi_devi_exit(dip, circ); 68520Sstevel@tonic-gate } 68530Sstevel@tonic-gate 68540Sstevel@tonic-gate 68550Sstevel@tonic-gate /* 68560Sstevel@tonic-gate * hubd_restore_state_cb 68570Sstevel@tonic-gate * Event callback to restore device state 68580Sstevel@tonic-gate */ 68590Sstevel@tonic-gate static int 68600Sstevel@tonic-gate hubd_restore_state_cb(dev_info_t *dip) 68610Sstevel@tonic-gate { 68620Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip); 68630Sstevel@tonic-gate 68640Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 68650Sstevel@tonic-gate "hubd_restore_state_cb: Begin"); 68660Sstevel@tonic-gate 68670Sstevel@tonic-gate /* restore the state of this device */ 68680Sstevel@tonic-gate hubd_restore_device_state(dip, hubd); 68690Sstevel@tonic-gate 68700Sstevel@tonic-gate return (USB_SUCCESS); 68710Sstevel@tonic-gate } 68720Sstevel@tonic-gate 68730Sstevel@tonic-gate 68740Sstevel@tonic-gate /* 68750Sstevel@tonic-gate * registering for events 68760Sstevel@tonic-gate */ 68770Sstevel@tonic-gate static int 68780Sstevel@tonic-gate hubd_register_events(hubd_t *hubd) 68790Sstevel@tonic-gate { 68800Sstevel@tonic-gate int rval = USB_SUCCESS; 68810Sstevel@tonic-gate 68820Sstevel@tonic-gate if (usba_is_root_hub(hubd->h_dip)) { 68830Sstevel@tonic-gate hubd_register_cpr_callback(hubd); 68840Sstevel@tonic-gate } else { 68850Sstevel@tonic-gate rval = usb_register_event_cbs(hubd->h_dip, &hubd_events, 0); 68860Sstevel@tonic-gate } 68870Sstevel@tonic-gate 68880Sstevel@tonic-gate return (rval); 68890Sstevel@tonic-gate } 68900Sstevel@tonic-gate 68910Sstevel@tonic-gate 68920Sstevel@tonic-gate /* 68930Sstevel@tonic-gate * hubd cpr callback related functions 68940Sstevel@tonic-gate * 68950Sstevel@tonic-gate * hubd_cpr_post_user_callb: 68960Sstevel@tonic-gate * This function is called during checkpoint & resume - 68970Sstevel@tonic-gate * 1. after user threads are stopped during checkpoint 68980Sstevel@tonic-gate * 2. after kernel threads are resumed during resume 68990Sstevel@tonic-gate */ 69000Sstevel@tonic-gate /* ARGSUSED */ 69010Sstevel@tonic-gate static boolean_t 69020Sstevel@tonic-gate hubd_cpr_post_user_callb(void *arg, int code) 69030Sstevel@tonic-gate { 69040Sstevel@tonic-gate hubd_cpr_t *cpr_cb = (hubd_cpr_t *)arg; 69050Sstevel@tonic-gate hubd_t *hubd = cpr_cb->statep; 69060Sstevel@tonic-gate int retry = 0; 69070Sstevel@tonic-gate 69080Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle, 69090Sstevel@tonic-gate "hubd_cpr_post_user_callb"); 69100Sstevel@tonic-gate 69110Sstevel@tonic-gate switch (code) { 69120Sstevel@tonic-gate case CB_CODE_CPR_CHKPT: 69130Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_EVENTS, hubd->h_log_handle, 69140Sstevel@tonic-gate "hubd_cpr_post_user_callb: CB_CODE_CPR_CHKPT"); 69150Sstevel@tonic-gate 69160Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 69170Sstevel@tonic-gate 69180Sstevel@tonic-gate /* turn off deathrow thread */ 69190Sstevel@tonic-gate hubd->h_cleanup_enabled = B_FALSE; 69200Sstevel@tonic-gate 69210Sstevel@tonic-gate /* give up if deathrow thread doesn't exit */ 69220Sstevel@tonic-gate while ((hubd->h_cleanup_active == B_TRUE) && (retry++ < 3)) { 69230Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 69240Sstevel@tonic-gate delay(drv_usectohz(hubd_dip_cleanup_delay)); 69250Sstevel@tonic-gate 69260Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_EVENTS, hubd->h_log_handle, 69270Sstevel@tonic-gate "hubd_cpr_post_user_callb, waiting for " 69280Sstevel@tonic-gate "deathrow thread to exit"); 69290Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 69300Sstevel@tonic-gate } 69310Sstevel@tonic-gate 69320Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 69330Sstevel@tonic-gate 69340Sstevel@tonic-gate /* save the state of the device */ 69350Sstevel@tonic-gate (void) hubd_pre_suspend_event_cb(hubd->h_dip); 69360Sstevel@tonic-gate 69370Sstevel@tonic-gate return (B_TRUE); 69380Sstevel@tonic-gate case CB_CODE_CPR_RESUME: 69390Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_EVENTS, hubd->h_log_handle, 69400Sstevel@tonic-gate "hubd_cpr_post_user_callb: CB_CODE_CPR_RESUME"); 69410Sstevel@tonic-gate 69420Sstevel@tonic-gate /* restore the state of the device */ 69430Sstevel@tonic-gate (void) hubd_post_resume_event_cb(hubd->h_dip); 69440Sstevel@tonic-gate 69450Sstevel@tonic-gate /* turn on deathrow thread */ 69460Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 69470Sstevel@tonic-gate hubd->h_cleanup_enabled = B_TRUE; 69480Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 69490Sstevel@tonic-gate 69500Sstevel@tonic-gate hubd_schedule_cleanup(hubd->h_usba_device->usb_root_hub_dip); 69510Sstevel@tonic-gate 69520Sstevel@tonic-gate return (B_TRUE); 69530Sstevel@tonic-gate default: 69540Sstevel@tonic-gate 69550Sstevel@tonic-gate return (B_FALSE); 69560Sstevel@tonic-gate } 69570Sstevel@tonic-gate 69580Sstevel@tonic-gate } 69590Sstevel@tonic-gate 69600Sstevel@tonic-gate 69610Sstevel@tonic-gate /* register callback with cpr framework */ 69620Sstevel@tonic-gate void 69630Sstevel@tonic-gate hubd_register_cpr_callback(hubd_t *hubd) 69640Sstevel@tonic-gate { 69650Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle, 69660Sstevel@tonic-gate "hubd_register_cpr_callback"); 69670Sstevel@tonic-gate 69680Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 69690Sstevel@tonic-gate hubd->h_cpr_cb = 69700Sstevel@tonic-gate (hubd_cpr_t *)kmem_zalloc(sizeof (hubd_cpr_t), KM_SLEEP); 69710Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 69720Sstevel@tonic-gate mutex_init(&hubd->h_cpr_cb->lockp, NULL, MUTEX_DRIVER, 69730Sstevel@tonic-gate hubd->h_dev_data->dev_iblock_cookie); 69740Sstevel@tonic-gate hubd->h_cpr_cb->statep = hubd; 69750Sstevel@tonic-gate hubd->h_cpr_cb->cpr.cc_lockp = &hubd->h_cpr_cb->lockp; 69760Sstevel@tonic-gate hubd->h_cpr_cb->cpr.cc_id = callb_add(hubd_cpr_post_user_callb, 69770Sstevel@tonic-gate (void *)hubd->h_cpr_cb, CB_CL_CPR_POST_USER, "hubd"); 69780Sstevel@tonic-gate } 69790Sstevel@tonic-gate 69800Sstevel@tonic-gate 69810Sstevel@tonic-gate /* unregister callback with cpr framework */ 69820Sstevel@tonic-gate void 69830Sstevel@tonic-gate hubd_unregister_cpr_callback(hubd_t *hubd) 69840Sstevel@tonic-gate { 69850Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle, 69860Sstevel@tonic-gate "hubd_unregister_cpr_callback"); 69870Sstevel@tonic-gate 69880Sstevel@tonic-gate if (hubd->h_cpr_cb) { 69890Sstevel@tonic-gate (void) callb_delete(hubd->h_cpr_cb->cpr.cc_id); 69900Sstevel@tonic-gate mutex_destroy(&hubd->h_cpr_cb->lockp); 69910Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 69920Sstevel@tonic-gate kmem_free(hubd->h_cpr_cb, sizeof (hubd_cpr_t)); 69930Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 69940Sstevel@tonic-gate } 69950Sstevel@tonic-gate } 69960Sstevel@tonic-gate 69970Sstevel@tonic-gate 69980Sstevel@tonic-gate /* 69990Sstevel@tonic-gate * Power management 70000Sstevel@tonic-gate * 70010Sstevel@tonic-gate * create the pm components required for power management 70020Sstevel@tonic-gate */ 70030Sstevel@tonic-gate static void 70040Sstevel@tonic-gate hubd_create_pm_components(dev_info_t *dip, hubd_t *hubd) 70050Sstevel@tonic-gate { 70060Sstevel@tonic-gate hub_power_t *hubpm; 70070Sstevel@tonic-gate 70080Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 70090Sstevel@tonic-gate "hubd_create_pm_components: Begin"); 70100Sstevel@tonic-gate 70110Sstevel@tonic-gate /* Allocate the state structure */ 70120Sstevel@tonic-gate hubpm = kmem_zalloc(sizeof (hub_power_t), KM_SLEEP); 70130Sstevel@tonic-gate 70140Sstevel@tonic-gate hubd->h_hubpm = hubpm; 70150Sstevel@tonic-gate hubpm->hubp_hubd = hubd; 70160Sstevel@tonic-gate hubpm->hubp_pm_capabilities = 0; 70170Sstevel@tonic-gate hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR; 70180Sstevel@tonic-gate hubpm->hubp_time_at_full_power = ddi_get_time(); 70190Sstevel@tonic-gate hubpm->hubp_min_pm_threshold = hubdi_min_pm_threshold; 70200Sstevel@tonic-gate 70210Sstevel@tonic-gate /* alloc memory to save power states of children */ 70220Sstevel@tonic-gate hubpm->hubp_child_pwrstate = (uint8_t *) 7023*4763Slg150142 kmem_zalloc(MAX_PORTS + 1, KM_SLEEP); 70240Sstevel@tonic-gate 70250Sstevel@tonic-gate /* 70260Sstevel@tonic-gate * if the enable remote wakeup fails 70270Sstevel@tonic-gate * we still want to enable 70280Sstevel@tonic-gate * parent notification so we can PM the children 70290Sstevel@tonic-gate */ 70300Sstevel@tonic-gate usb_enable_parent_notification(dip); 70310Sstevel@tonic-gate 70320Sstevel@tonic-gate if (usb_handle_remote_wakeup(dip, 70330Sstevel@tonic-gate USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) { 70340Sstevel@tonic-gate uint_t pwr_states; 70350Sstevel@tonic-gate 70360Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 70370Sstevel@tonic-gate "hubd_create_pm_components: " 70380Sstevel@tonic-gate "Remote Wakeup Enabled"); 70390Sstevel@tonic-gate 70400Sstevel@tonic-gate if (usb_create_pm_components(dip, &pwr_states) == 70410Sstevel@tonic-gate USB_SUCCESS) { 70420Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 70430Sstevel@tonic-gate hubpm->hubp_wakeup_enabled = 1; 70440Sstevel@tonic-gate hubpm->hubp_pwr_states = (uint8_t)pwr_states; 70450Sstevel@tonic-gate 70460Sstevel@tonic-gate /* we are busy now till end of the attach */ 70470Sstevel@tonic-gate hubd_pm_busy_component(hubd, dip, 0); 70480Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 70490Sstevel@tonic-gate 70500Sstevel@tonic-gate /* bring the device to full power */ 70510Sstevel@tonic-gate (void) pm_raise_power(dip, 0, 70520Sstevel@tonic-gate USB_DEV_OS_FULL_PWR); 70530Sstevel@tonic-gate } 70540Sstevel@tonic-gate } 70550Sstevel@tonic-gate 70560Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 70570Sstevel@tonic-gate "hubd_create_pm_components: END"); 70580Sstevel@tonic-gate } 70590Sstevel@tonic-gate 70600Sstevel@tonic-gate 70610Sstevel@tonic-gate /* 70620Sstevel@tonic-gate * Attachment point management 70630Sstevel@tonic-gate */ 70640Sstevel@tonic-gate /* ARGSUSED */ 70650Sstevel@tonic-gate int 70660Sstevel@tonic-gate usba_hubdi_open(dev_info_t *dip, dev_t *devp, int flags, int otyp, 70670Sstevel@tonic-gate cred_t *credp) 70680Sstevel@tonic-gate { 70690Sstevel@tonic-gate hubd_t *hubd; 70700Sstevel@tonic-gate 70710Sstevel@tonic-gate if (otyp != OTYP_CHR) 70720Sstevel@tonic-gate return (EINVAL); 70730Sstevel@tonic-gate 70740Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 70750Sstevel@tonic-gate if (hubd == NULL) { 70760Sstevel@tonic-gate return (ENXIO); 70770Sstevel@tonic-gate } 70780Sstevel@tonic-gate 70790Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 70800Sstevel@tonic-gate "hubd_open:"); 70810Sstevel@tonic-gate 70820Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 70830Sstevel@tonic-gate if ((flags & FEXCL) && (hubd->h_softstate & HUBD_SS_ISOPEN)) { 70840Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 70850Sstevel@tonic-gate 70860Sstevel@tonic-gate return (EBUSY); 70870Sstevel@tonic-gate } 70880Sstevel@tonic-gate 70890Sstevel@tonic-gate hubd->h_softstate |= HUBD_SS_ISOPEN; 70900Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 70910Sstevel@tonic-gate 70920Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, "opened"); 70930Sstevel@tonic-gate 70940Sstevel@tonic-gate return (0); 70950Sstevel@tonic-gate } 70960Sstevel@tonic-gate 70970Sstevel@tonic-gate 70980Sstevel@tonic-gate /* ARGSUSED */ 70990Sstevel@tonic-gate int 71000Sstevel@tonic-gate usba_hubdi_close(dev_info_t *dip, dev_t dev, int flag, int otyp, 71010Sstevel@tonic-gate cred_t *credp) 71020Sstevel@tonic-gate { 71030Sstevel@tonic-gate hubd_t *hubd; 71040Sstevel@tonic-gate 71050Sstevel@tonic-gate if (otyp != OTYP_CHR) { 71060Sstevel@tonic-gate return (EINVAL); 71070Sstevel@tonic-gate } 71080Sstevel@tonic-gate 71090Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 71100Sstevel@tonic-gate 71110Sstevel@tonic-gate if (hubd == NULL) { 71120Sstevel@tonic-gate return (ENXIO); 71130Sstevel@tonic-gate } 71140Sstevel@tonic-gate 71150Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, "hubd_close:"); 71160Sstevel@tonic-gate 71170Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 71180Sstevel@tonic-gate hubd->h_softstate &= ~HUBD_SS_ISOPEN; 71190Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 71200Sstevel@tonic-gate 71210Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, "closed"); 71220Sstevel@tonic-gate 71230Sstevel@tonic-gate return (0); 71240Sstevel@tonic-gate } 71250Sstevel@tonic-gate 71260Sstevel@tonic-gate 71270Sstevel@tonic-gate /* 71280Sstevel@tonic-gate * hubd_ioctl: cfgadm controls 71290Sstevel@tonic-gate */ 71300Sstevel@tonic-gate /* ARGSUSED */ 71310Sstevel@tonic-gate int 71320Sstevel@tonic-gate usba_hubdi_ioctl(dev_info_t *self, dev_t dev, int cmd, intptr_t arg, 71330Sstevel@tonic-gate int mode, cred_t *credp, int *rvalp) 71340Sstevel@tonic-gate { 71350Sstevel@tonic-gate int rv = 0; 71360Sstevel@tonic-gate char *msg; /* for messages */ 71370Sstevel@tonic-gate hubd_t *hubd; 71380Sstevel@tonic-gate usb_port_t port = 0; 71390Sstevel@tonic-gate dev_info_t *child_dip = NULL; 71400Sstevel@tonic-gate dev_info_t *rh_dip; 71410Sstevel@tonic-gate devctl_ap_state_t ap_state; 71420Sstevel@tonic-gate struct devctl_iocdata *dcp = NULL; 71430Sstevel@tonic-gate usb_pipe_state_t prev_pipe_state = 0; 71440Sstevel@tonic-gate int circ, rh_circ, prh_circ; 71450Sstevel@tonic-gate 71460Sstevel@tonic-gate if ((hubd = hubd_get_soft_state(self)) == NULL) { 71470Sstevel@tonic-gate 71480Sstevel@tonic-gate return (ENXIO); 71490Sstevel@tonic-gate } 71500Sstevel@tonic-gate 71510Sstevel@tonic-gate rh_dip = hubd->h_usba_device->usb_root_hub_dip; 71520Sstevel@tonic-gate 71530Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 71540Sstevel@tonic-gate "usba_hubdi_ioctl: " 71550Sstevel@tonic-gate "cmd=%x, arg=%lx, mode=%x, cred=%p, rval=%p dev=0x%lx", 71560Sstevel@tonic-gate cmd, arg, mode, credp, rvalp, dev); 71570Sstevel@tonic-gate 71580Sstevel@tonic-gate /* read devctl ioctl data */ 71590Sstevel@tonic-gate if ((cmd != DEVCTL_AP_CONTROL) && 71600Sstevel@tonic-gate (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)) { 71610Sstevel@tonic-gate 71620Sstevel@tonic-gate return (EFAULT); 71630Sstevel@tonic-gate } 71640Sstevel@tonic-gate 71650Sstevel@tonic-gate /* 71660Sstevel@tonic-gate * make sure the hub is connected before trying any 71670Sstevel@tonic-gate * of the following operations: 71680Sstevel@tonic-gate * configure, connect, disconnect 71690Sstevel@tonic-gate */ 71700Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 71710Sstevel@tonic-gate 71720Sstevel@tonic-gate switch (cmd) { 71730Sstevel@tonic-gate case DEVCTL_AP_DISCONNECT: 71740Sstevel@tonic-gate case DEVCTL_AP_UNCONFIGURE: 71750Sstevel@tonic-gate case DEVCTL_AP_CONFIGURE: 71760Sstevel@tonic-gate if (hubd->h_dev_state == USB_DEV_DISCONNECTED) { 71770Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 71780Sstevel@tonic-gate "hubd: already gone"); 71790Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 71800Sstevel@tonic-gate if (dcp) { 71810Sstevel@tonic-gate ndi_dc_freehdl(dcp); 71820Sstevel@tonic-gate } 71830Sstevel@tonic-gate 71840Sstevel@tonic-gate return (EIO); 71850Sstevel@tonic-gate } 71860Sstevel@tonic-gate 71870Sstevel@tonic-gate /* FALLTHROUGH */ 71880Sstevel@tonic-gate case DEVCTL_AP_GETSTATE: 71890Sstevel@tonic-gate if ((port = hubd_get_port_num(hubd, dcp)) == 0) { 71900Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 71910Sstevel@tonic-gate "hubd: bad port"); 71920Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 71930Sstevel@tonic-gate if (dcp) { 71940Sstevel@tonic-gate ndi_dc_freehdl(dcp); 71950Sstevel@tonic-gate } 71960Sstevel@tonic-gate 71970Sstevel@tonic-gate return (EINVAL); 71980Sstevel@tonic-gate } 71990Sstevel@tonic-gate break; 72000Sstevel@tonic-gate 72010Sstevel@tonic-gate case DEVCTL_AP_CONTROL: 72020Sstevel@tonic-gate 72030Sstevel@tonic-gate break; 72040Sstevel@tonic-gate default: 72050Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 72060Sstevel@tonic-gate if (dcp) { 72070Sstevel@tonic-gate ndi_dc_freehdl(dcp); 72080Sstevel@tonic-gate } 72090Sstevel@tonic-gate 72100Sstevel@tonic-gate return (ENOTTY); 72110Sstevel@tonic-gate } 72120Sstevel@tonic-gate 72130Sstevel@tonic-gate /* should not happen, just in case */ 72140Sstevel@tonic-gate if (hubd->h_dev_state == USB_DEV_SUSPENDED) { 72150Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 72160Sstevel@tonic-gate if (dcp) { 72170Sstevel@tonic-gate ndi_dc_freehdl(dcp); 72180Sstevel@tonic-gate } 72190Sstevel@tonic-gate 72200Sstevel@tonic-gate return (EIO); 72210Sstevel@tonic-gate } 72220Sstevel@tonic-gate 72230Sstevel@tonic-gate hubd_pm_busy_component(hubd, hubd->h_dip, 0); 72240Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 72250Sstevel@tonic-gate 72260Sstevel@tonic-gate /* go full power */ 72270Sstevel@tonic-gate (void) pm_raise_power(hubd->h_dip, 0, USB_DEV_OS_FULL_PWR); 72280Sstevel@tonic-gate 72290Sstevel@tonic-gate ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ); 72300Sstevel@tonic-gate ndi_devi_enter(rh_dip, &rh_circ); 72310Sstevel@tonic-gate ndi_devi_enter(hubd->h_dip, &circ); 72320Sstevel@tonic-gate 72330Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 72340Sstevel@tonic-gate 7235*4763Slg150142 hubd->h_hotplug_thread++; 7236*4763Slg150142 7237*4763Slg150142 /* stop polling if it was active */ 72380Sstevel@tonic-gate if (hubd->h_ep1_ph) { 72390Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 72400Sstevel@tonic-gate (void) usb_pipe_get_state(hubd->h_ep1_ph, &prev_pipe_state, 7241*4763Slg150142 USB_FLAGS_SLEEP); 72420Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 72430Sstevel@tonic-gate 72440Sstevel@tonic-gate if (prev_pipe_state == USB_PIPE_STATE_ACTIVE) { 72450Sstevel@tonic-gate hubd_stop_polling(hubd); 72460Sstevel@tonic-gate } 72470Sstevel@tonic-gate } 72480Sstevel@tonic-gate 72490Sstevel@tonic-gate switch (cmd) { 72500Sstevel@tonic-gate case DEVCTL_AP_DISCONNECT: 72510Sstevel@tonic-gate if (hubd_delete_child(hubd, port, 72520Sstevel@tonic-gate NDI_DEVI_REMOVE, B_FALSE) != USB_SUCCESS) { 72530Sstevel@tonic-gate rv = EIO; 72540Sstevel@tonic-gate } 72550Sstevel@tonic-gate 72560Sstevel@tonic-gate break; 72570Sstevel@tonic-gate case DEVCTL_AP_UNCONFIGURE: 72580Sstevel@tonic-gate if (hubd_delete_child(hubd, port, 72590Sstevel@tonic-gate NDI_UNCONFIG, B_FALSE) != USB_SUCCESS) { 72600Sstevel@tonic-gate rv = EIO; 72610Sstevel@tonic-gate } 72620Sstevel@tonic-gate 72630Sstevel@tonic-gate break; 72640Sstevel@tonic-gate case DEVCTL_AP_CONFIGURE: 72650Sstevel@tonic-gate /* toggle port */ 72660Sstevel@tonic-gate if (hubd_toggle_port(hubd, port) != USB_SUCCESS) { 72670Sstevel@tonic-gate rv = EIO; 72680Sstevel@tonic-gate 72690Sstevel@tonic-gate break; 72700Sstevel@tonic-gate } 72710Sstevel@tonic-gate 72720Sstevel@tonic-gate (void) hubd_handle_port_connect(hubd, port); 72730Sstevel@tonic-gate child_dip = hubd_get_child_dip(hubd, port); 72740Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 72750Sstevel@tonic-gate 72760Sstevel@tonic-gate ndi_devi_exit(hubd->h_dip, circ); 72770Sstevel@tonic-gate ndi_devi_exit(rh_dip, rh_circ); 72780Sstevel@tonic-gate ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ); 72790Sstevel@tonic-gate if ((child_dip == NULL) || 72800Sstevel@tonic-gate (ndi_devi_online(child_dip, 0) != NDI_SUCCESS)) { 72810Sstevel@tonic-gate rv = EIO; 72820Sstevel@tonic-gate } 72830Sstevel@tonic-gate ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ); 72840Sstevel@tonic-gate ndi_devi_enter(rh_dip, &rh_circ); 72850Sstevel@tonic-gate ndi_devi_enter(hubd->h_dip, &circ); 72860Sstevel@tonic-gate 72870Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 72880Sstevel@tonic-gate 72890Sstevel@tonic-gate break; 72900Sstevel@tonic-gate case DEVCTL_AP_GETSTATE: 72910Sstevel@tonic-gate switch (hubd_cfgadm_state(hubd, port)) { 72920Sstevel@tonic-gate case HUBD_CFGADM_DISCONNECTED: 72930Sstevel@tonic-gate /* port previously 'disconnected' by cfgadm */ 72940Sstevel@tonic-gate ap_state.ap_rstate = AP_RSTATE_DISCONNECTED; 72950Sstevel@tonic-gate ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED; 72960Sstevel@tonic-gate ap_state.ap_condition = AP_COND_OK; 72970Sstevel@tonic-gate 72980Sstevel@tonic-gate break; 72990Sstevel@tonic-gate case HUBD_CFGADM_UNCONFIGURED: 73000Sstevel@tonic-gate ap_state.ap_rstate = AP_RSTATE_CONNECTED; 73010Sstevel@tonic-gate ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED; 73020Sstevel@tonic-gate ap_state.ap_condition = AP_COND_OK; 73030Sstevel@tonic-gate 73040Sstevel@tonic-gate break; 73050Sstevel@tonic-gate case HUBD_CFGADM_CONFIGURED: 73060Sstevel@tonic-gate ap_state.ap_rstate = AP_RSTATE_CONNECTED; 73070Sstevel@tonic-gate ap_state.ap_ostate = AP_OSTATE_CONFIGURED; 73080Sstevel@tonic-gate ap_state.ap_condition = AP_COND_OK; 73090Sstevel@tonic-gate 73100Sstevel@tonic-gate break; 73110Sstevel@tonic-gate case HUBD_CFGADM_STILL_REFERENCED: 73120Sstevel@tonic-gate ap_state.ap_rstate = AP_RSTATE_EMPTY; 73130Sstevel@tonic-gate ap_state.ap_ostate = AP_OSTATE_CONFIGURED; 73140Sstevel@tonic-gate ap_state.ap_condition = AP_COND_UNUSABLE; 73150Sstevel@tonic-gate 73160Sstevel@tonic-gate break; 73170Sstevel@tonic-gate case HUBD_CFGADM_EMPTY: 73180Sstevel@tonic-gate default: 73190Sstevel@tonic-gate ap_state.ap_rstate = AP_RSTATE_EMPTY; 73200Sstevel@tonic-gate ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED; 73210Sstevel@tonic-gate ap_state.ap_condition = AP_COND_OK; 73220Sstevel@tonic-gate 73230Sstevel@tonic-gate break; 73240Sstevel@tonic-gate } 73250Sstevel@tonic-gate 73260Sstevel@tonic-gate ap_state.ap_last_change = (time_t)-1; 73270Sstevel@tonic-gate ap_state.ap_error_code = 0; 73280Sstevel@tonic-gate ap_state.ap_in_transition = 0; 73290Sstevel@tonic-gate 73300Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 73310Sstevel@tonic-gate "DEVCTL_AP_GETSTATE: " 73320Sstevel@tonic-gate "ostate=0x%x, rstate=0x%x, condition=0x%x", 73330Sstevel@tonic-gate ap_state.ap_ostate, 73340Sstevel@tonic-gate ap_state.ap_rstate, ap_state.ap_condition); 73350Sstevel@tonic-gate 73360Sstevel@tonic-gate /* copy the return-AP-state information to the user space */ 73370Sstevel@tonic-gate if (ndi_dc_return_ap_state(&ap_state, dcp) != NDI_SUCCESS) { 73380Sstevel@tonic-gate rv = EFAULT; 73390Sstevel@tonic-gate } 73400Sstevel@tonic-gate 73410Sstevel@tonic-gate break; 73420Sstevel@tonic-gate case DEVCTL_AP_CONTROL: 73430Sstevel@tonic-gate { 73440Sstevel@tonic-gate /* 73450Sstevel@tonic-gate * Generic devctl for hardware-specific functionality. 73460Sstevel@tonic-gate * For list of sub-commands see hubd_impl.h 73470Sstevel@tonic-gate */ 73480Sstevel@tonic-gate hubd_ioctl_data_t ioc; /* for 64 byte copies */ 73490Sstevel@tonic-gate 73500Sstevel@tonic-gate /* copy user ioctl data in first */ 73510Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL 73520Sstevel@tonic-gate if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) { 73530Sstevel@tonic-gate hubd_ioctl_data_32_t ioc32; 73540Sstevel@tonic-gate 73550Sstevel@tonic-gate if (ddi_copyin((void *)arg, (void *)&ioc32, 7356*4763Slg150142 sizeof (ioc32), mode) != 0) { 73570Sstevel@tonic-gate rv = EFAULT; 73580Sstevel@tonic-gate 73590Sstevel@tonic-gate break; 73600Sstevel@tonic-gate } 73610Sstevel@tonic-gate ioc.cmd = (uint_t)ioc32.cmd; 73620Sstevel@tonic-gate ioc.port = (uint_t)ioc32.port; 73630Sstevel@tonic-gate ioc.get_size = (uint_t)ioc32.get_size; 73640Sstevel@tonic-gate ioc.buf = (caddr_t)(uintptr_t)ioc32.buf; 73650Sstevel@tonic-gate ioc.bufsiz = (uint_t)ioc32.bufsiz; 73660Sstevel@tonic-gate ioc.misc_arg = (uint_t)ioc32.misc_arg; 73670Sstevel@tonic-gate } else 73680Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */ 73690Sstevel@tonic-gate if (ddi_copyin((void *)arg, (void *)&ioc, sizeof (ioc), 73700Sstevel@tonic-gate mode) != 0) { 73710Sstevel@tonic-gate rv = EFAULT; 73720Sstevel@tonic-gate 73730Sstevel@tonic-gate break; 73740Sstevel@tonic-gate } 73750Sstevel@tonic-gate 73760Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_CBOPS, hubd->h_log_handle, 73770Sstevel@tonic-gate "DEVCTL_AP_CONTROL: ioc: cmd=0x%x port=%d get_size=%d" 73780Sstevel@tonic-gate "\n\tbuf=0x%p, bufsiz=%d, misc_arg=%d", ioc.cmd, 73790Sstevel@tonic-gate ioc.port, ioc.get_size, ioc.buf, ioc.bufsiz, ioc.misc_arg); 73800Sstevel@tonic-gate 73810Sstevel@tonic-gate /* 73820Sstevel@tonic-gate * To avoid BE/LE and 32/64 issues, a get_size always 73830Sstevel@tonic-gate * returns a 32-bit number. 73840Sstevel@tonic-gate */ 73850Sstevel@tonic-gate if (ioc.get_size != 0 && ioc.bufsiz != (sizeof (uint32_t))) { 73860Sstevel@tonic-gate rv = EINVAL; 73870Sstevel@tonic-gate 73880Sstevel@tonic-gate break; 73890Sstevel@tonic-gate } 73900Sstevel@tonic-gate 73910Sstevel@tonic-gate switch (ioc.cmd) { 73920Sstevel@tonic-gate case USB_DESCR_TYPE_DEV: 73930Sstevel@tonic-gate msg = "DEVCTL_AP_CONTROL: GET_DEVICE_DESC"; 73940Sstevel@tonic-gate if (ioc.get_size) { 73950Sstevel@tonic-gate /* uint32 so this works 32/64 */ 73960Sstevel@tonic-gate uint32_t size = sizeof (usb_dev_descr_t); 73970Sstevel@tonic-gate 73980Sstevel@tonic-gate if (ddi_copyout((void *)&size, ioc.buf, 73990Sstevel@tonic-gate ioc.bufsiz, mode) != 0) { 74000Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 74010Sstevel@tonic-gate hubd->h_log_handle, 74020Sstevel@tonic-gate "%s: get_size copyout failed", msg); 74030Sstevel@tonic-gate rv = EIO; 74040Sstevel@tonic-gate 74050Sstevel@tonic-gate break; 74060Sstevel@tonic-gate } 74070Sstevel@tonic-gate } else { /* send out the actual descr */ 74080Sstevel@tonic-gate usb_dev_descr_t *dev_descrp; 74090Sstevel@tonic-gate 74100Sstevel@tonic-gate /* check child_dip */ 74110Sstevel@tonic-gate if ((child_dip = hubd_get_child_dip(hubd, 74120Sstevel@tonic-gate ioc.port)) == NULL) { 74130Sstevel@tonic-gate rv = EINVAL; 74140Sstevel@tonic-gate 74150Sstevel@tonic-gate break; 74160Sstevel@tonic-gate } 74170Sstevel@tonic-gate 74180Sstevel@tonic-gate dev_descrp = usb_get_dev_descr(child_dip); 74190Sstevel@tonic-gate if (ioc.bufsiz != sizeof (*dev_descrp)) { 74200Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 74210Sstevel@tonic-gate hubd->h_log_handle, 74220Sstevel@tonic-gate "%s: bufsize passed (%d) != sizeof " 74230Sstevel@tonic-gate "usba_device_descr_t (%d)", msg, 74240Sstevel@tonic-gate ioc.bufsiz, dev_descrp->bLength); 74250Sstevel@tonic-gate rv = EINVAL; 74260Sstevel@tonic-gate 74270Sstevel@tonic-gate break; 74280Sstevel@tonic-gate } 74290Sstevel@tonic-gate 74300Sstevel@tonic-gate if (ddi_copyout((void *)dev_descrp, 74310Sstevel@tonic-gate ioc.buf, ioc.bufsiz, mode) != 0) { 74320Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 74330Sstevel@tonic-gate hubd->h_log_handle, 74340Sstevel@tonic-gate "%s: copyout failed.", msg); 74350Sstevel@tonic-gate rv = EIO; 74360Sstevel@tonic-gate 74370Sstevel@tonic-gate break; 74380Sstevel@tonic-gate } 74390Sstevel@tonic-gate } 74400Sstevel@tonic-gate break; 74410Sstevel@tonic-gate case USB_DESCR_TYPE_STRING: 74420Sstevel@tonic-gate { 74430Sstevel@tonic-gate char *str; 74440Sstevel@tonic-gate uint32_t size; 74450Sstevel@tonic-gate usba_device_t *usba_device; 74460Sstevel@tonic-gate 74470Sstevel@tonic-gate msg = "DEVCTL_AP_CONTROL: GET_STRING_DESCR"; 74480Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 74490Sstevel@tonic-gate "%s: string request: %d", msg, ioc.misc_arg); 74500Sstevel@tonic-gate 74510Sstevel@tonic-gate /* recheck */ 74520Sstevel@tonic-gate if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) == 74530Sstevel@tonic-gate NULL) { 74540Sstevel@tonic-gate rv = EINVAL; 74550Sstevel@tonic-gate 74560Sstevel@tonic-gate break; 74570Sstevel@tonic-gate } 74580Sstevel@tonic-gate usba_device = usba_get_usba_device(child_dip); 74590Sstevel@tonic-gate 74600Sstevel@tonic-gate switch (ioc.misc_arg) { 74610Sstevel@tonic-gate case HUBD_MFG_STR: 74620Sstevel@tonic-gate str = usba_device->usb_mfg_str; 74630Sstevel@tonic-gate 74640Sstevel@tonic-gate break; 74650Sstevel@tonic-gate case HUBD_PRODUCT_STR: 74660Sstevel@tonic-gate str = usba_device->usb_product_str; 74670Sstevel@tonic-gate 74680Sstevel@tonic-gate break; 74690Sstevel@tonic-gate case HUBD_SERIALNO_STR: 74700Sstevel@tonic-gate str = usba_device->usb_serialno_str; 74710Sstevel@tonic-gate 74720Sstevel@tonic-gate break; 74730Sstevel@tonic-gate case HUBD_CFG_DESCR_STR: 74740Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex); 74750Sstevel@tonic-gate str = usba_device->usb_cfg_str_descr[ 7476*4763Slg150142 usba_device->usb_active_cfg_ndx]; 74770Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex); 74780Sstevel@tonic-gate 74790Sstevel@tonic-gate break; 74800Sstevel@tonic-gate default: 74810Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 74820Sstevel@tonic-gate hubd->h_log_handle, 74830Sstevel@tonic-gate "%s: Invalid string request", msg); 74840Sstevel@tonic-gate rv = EINVAL; 74850Sstevel@tonic-gate 74860Sstevel@tonic-gate break; 74870Sstevel@tonic-gate } /* end of switch */ 74880Sstevel@tonic-gate 74890Sstevel@tonic-gate if (rv != 0) { 74900Sstevel@tonic-gate 74910Sstevel@tonic-gate break; 74920Sstevel@tonic-gate } 74930Sstevel@tonic-gate 74940Sstevel@tonic-gate size = (str != NULL) ? strlen(str) + 1 : 0; 74950Sstevel@tonic-gate if (ioc.get_size) { 74960Sstevel@tonic-gate if (ddi_copyout((void *)&size, ioc.buf, 74970Sstevel@tonic-gate ioc.bufsiz, mode) != 0) { 74980Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 74990Sstevel@tonic-gate hubd->h_log_handle, 75000Sstevel@tonic-gate "%s: copyout of size failed.", msg); 75010Sstevel@tonic-gate rv = EIO; 75020Sstevel@tonic-gate 75030Sstevel@tonic-gate break; 75040Sstevel@tonic-gate } 75050Sstevel@tonic-gate } else { 75060Sstevel@tonic-gate if (size == 0) { 75070Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_CBOPS, 75080Sstevel@tonic-gate hubd->h_log_handle, 75090Sstevel@tonic-gate "%s: String is NULL", msg); 75100Sstevel@tonic-gate rv = EINVAL; 75110Sstevel@tonic-gate 75120Sstevel@tonic-gate break; 75130Sstevel@tonic-gate } 75140Sstevel@tonic-gate 75150Sstevel@tonic-gate if (ioc.bufsiz != size) { 75160Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 75170Sstevel@tonic-gate hubd->h_log_handle, 75180Sstevel@tonic-gate "%s: string buf size wrong", msg); 75190Sstevel@tonic-gate rv = EINVAL; 75200Sstevel@tonic-gate 75210Sstevel@tonic-gate break; 75220Sstevel@tonic-gate } 75230Sstevel@tonic-gate 75240Sstevel@tonic-gate if (ddi_copyout((void *)str, ioc.buf, 75250Sstevel@tonic-gate ioc.bufsiz, mode) != 0) { 75260Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 75270Sstevel@tonic-gate hubd->h_log_handle, 75280Sstevel@tonic-gate "%s: copyout failed.", msg); 75290Sstevel@tonic-gate rv = EIO; 75300Sstevel@tonic-gate 75310Sstevel@tonic-gate break; 75320Sstevel@tonic-gate } 75330Sstevel@tonic-gate } 75340Sstevel@tonic-gate break; 75350Sstevel@tonic-gate } 75360Sstevel@tonic-gate case HUBD_GET_CFGADM_NAME: 75370Sstevel@tonic-gate { 75380Sstevel@tonic-gate uint32_t name_len; 75390Sstevel@tonic-gate const char *name; 75400Sstevel@tonic-gate 75410Sstevel@tonic-gate /* recheck */ 75420Sstevel@tonic-gate if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) == 75430Sstevel@tonic-gate NULL) { 75440Sstevel@tonic-gate rv = EINVAL; 75450Sstevel@tonic-gate 75460Sstevel@tonic-gate break; 75470Sstevel@tonic-gate } 75480Sstevel@tonic-gate name = ddi_node_name(child_dip); 75490Sstevel@tonic-gate if (name == NULL) { 75500Sstevel@tonic-gate name = "unsupported"; 75510Sstevel@tonic-gate } 75520Sstevel@tonic-gate name_len = strlen(name) + 1; 75530Sstevel@tonic-gate 75540Sstevel@tonic-gate msg = "DEVCTL_AP_CONTROL: HUBD_GET_CFGADM_NAME"; 75550Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 75560Sstevel@tonic-gate "%s: name=%s name_len=%d", msg, name, name_len); 75570Sstevel@tonic-gate 75580Sstevel@tonic-gate if (ioc.get_size) { 75590Sstevel@tonic-gate if (ddi_copyout((void *)&name_len, 75600Sstevel@tonic-gate ioc.buf, ioc.bufsiz, mode) != 0) { 75610Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 75620Sstevel@tonic-gate hubd->h_log_handle, 75630Sstevel@tonic-gate "%s: copyout of size failed", msg); 75640Sstevel@tonic-gate rv = EIO; 75650Sstevel@tonic-gate 75660Sstevel@tonic-gate break; 75670Sstevel@tonic-gate } 75680Sstevel@tonic-gate } else { 75690Sstevel@tonic-gate if (ioc.bufsiz != name_len) { 75700Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 75710Sstevel@tonic-gate hubd->h_log_handle, 75720Sstevel@tonic-gate "%s: string buf length wrong", msg); 75730Sstevel@tonic-gate rv = EINVAL; 75740Sstevel@tonic-gate 75750Sstevel@tonic-gate break; 75760Sstevel@tonic-gate } 75770Sstevel@tonic-gate 75780Sstevel@tonic-gate if (ddi_copyout((void *)name, ioc.buf, 75790Sstevel@tonic-gate ioc.bufsiz, mode) != 0) { 75800Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 75810Sstevel@tonic-gate hubd->h_log_handle, 75820Sstevel@tonic-gate "%s: copyout failed.", msg); 75830Sstevel@tonic-gate rv = EIO; 75840Sstevel@tonic-gate 75850Sstevel@tonic-gate break; 75860Sstevel@tonic-gate } 75870Sstevel@tonic-gate } 75880Sstevel@tonic-gate 75890Sstevel@tonic-gate break; 75900Sstevel@tonic-gate } 75910Sstevel@tonic-gate 75920Sstevel@tonic-gate /* 75930Sstevel@tonic-gate * Return the config index for the currently-configured 75940Sstevel@tonic-gate * configuration. 75950Sstevel@tonic-gate */ 75960Sstevel@tonic-gate case HUBD_GET_CURRENT_CONFIG: 75970Sstevel@tonic-gate { 75980Sstevel@tonic-gate uint_t config_index; 75990Sstevel@tonic-gate uint32_t size = sizeof (config_index); 76000Sstevel@tonic-gate usba_device_t *usba_device; 76010Sstevel@tonic-gate 76020Sstevel@tonic-gate msg = "DEVCTL_AP_CONTROL: GET_CURRENT_CONFIG"; 76030Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 76040Sstevel@tonic-gate "%s", msg); 76050Sstevel@tonic-gate 76060Sstevel@tonic-gate /* 76070Sstevel@tonic-gate * Return the config index for the configuration 76080Sstevel@tonic-gate * currently in use. 76090Sstevel@tonic-gate * Recheck if child_dip exists 76100Sstevel@tonic-gate */ 76110Sstevel@tonic-gate if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) == 76120Sstevel@tonic-gate NULL) { 76130Sstevel@tonic-gate rv = EINVAL; 76140Sstevel@tonic-gate 76150Sstevel@tonic-gate break; 76160Sstevel@tonic-gate } 76170Sstevel@tonic-gate 76180Sstevel@tonic-gate usba_device = usba_get_usba_device(child_dip); 76190Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex); 76200Sstevel@tonic-gate config_index = usba_device->usb_active_cfg_ndx; 76210Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex); 76220Sstevel@tonic-gate 76230Sstevel@tonic-gate if (ioc.get_size) { 76240Sstevel@tonic-gate if (ddi_copyout((void *)&size, 76250Sstevel@tonic-gate ioc.buf, ioc.bufsiz, mode) != 0) { 76260Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 76270Sstevel@tonic-gate hubd->h_log_handle, 76280Sstevel@tonic-gate "%s: copyout of size failed.", msg); 76290Sstevel@tonic-gate rv = EIO; 76300Sstevel@tonic-gate 76310Sstevel@tonic-gate break; 76320Sstevel@tonic-gate } 76330Sstevel@tonic-gate } else { 76340Sstevel@tonic-gate if (ioc.bufsiz != size) { 76350Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 76360Sstevel@tonic-gate hubd->h_log_handle, 76370Sstevel@tonic-gate "%s: buffer size wrong", msg); 76380Sstevel@tonic-gate rv = EINVAL; 76390Sstevel@tonic-gate 76400Sstevel@tonic-gate break; 76410Sstevel@tonic-gate } 76420Sstevel@tonic-gate if (ddi_copyout((void *)&config_index, 76430Sstevel@tonic-gate ioc.buf, ioc.bufsiz, mode) != 0) { 76440Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 76450Sstevel@tonic-gate hubd->h_log_handle, 76460Sstevel@tonic-gate "%s: copyout failed", msg); 76470Sstevel@tonic-gate rv = EIO; 76480Sstevel@tonic-gate } 76490Sstevel@tonic-gate } 76500Sstevel@tonic-gate 76510Sstevel@tonic-gate break; 76520Sstevel@tonic-gate } 76530Sstevel@tonic-gate case HUBD_GET_DEVICE_PATH: 76540Sstevel@tonic-gate { 76550Sstevel@tonic-gate char *path; 76560Sstevel@tonic-gate uint32_t size; 76570Sstevel@tonic-gate 76580Sstevel@tonic-gate msg = "DEVCTL_AP_CONTROL: GET_DEVICE_PATH"; 76590Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 76600Sstevel@tonic-gate "%s", msg); 76610Sstevel@tonic-gate 76620Sstevel@tonic-gate /* Recheck if child_dip exists */ 76630Sstevel@tonic-gate if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) == 76640Sstevel@tonic-gate NULL) { 76650Sstevel@tonic-gate rv = EINVAL; 76660Sstevel@tonic-gate 76670Sstevel@tonic-gate break; 76680Sstevel@tonic-gate } 76690Sstevel@tonic-gate 76700Sstevel@tonic-gate /* ddi_pathname doesn't supply /devices, so we do. */ 76710Sstevel@tonic-gate path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 76720Sstevel@tonic-gate (void) strcpy(path, "/devices"); 76730Sstevel@tonic-gate (void) ddi_pathname(child_dip, path + strlen(path)); 76740Sstevel@tonic-gate size = strlen(path) + 1; 76750Sstevel@tonic-gate 76760Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 76770Sstevel@tonic-gate "%s: device path=%s size=%d", msg, path, size); 76780Sstevel@tonic-gate 76790Sstevel@tonic-gate if (ioc.get_size) { 76800Sstevel@tonic-gate if (ddi_copyout((void *)&size, 76810Sstevel@tonic-gate ioc.buf, ioc.bufsiz, mode) != 0) { 76820Sstevel@tonic-gate 76830Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 76840Sstevel@tonic-gate hubd->h_log_handle, 76850Sstevel@tonic-gate "%s: copyout of size failed.", msg); 76860Sstevel@tonic-gate rv = EIO; 76870Sstevel@tonic-gate } 76880Sstevel@tonic-gate } else { 76890Sstevel@tonic-gate if (ioc.bufsiz != size) { 76900Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 76910Sstevel@tonic-gate hubd->h_log_handle, 76920Sstevel@tonic-gate "%s: buffer wrong size.", msg); 76930Sstevel@tonic-gate rv = EINVAL; 76940Sstevel@tonic-gate } else if (ddi_copyout((void *)path, 76950Sstevel@tonic-gate ioc.buf, ioc.bufsiz, mode) != 0) { 76960Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 76970Sstevel@tonic-gate hubd->h_log_handle, 76980Sstevel@tonic-gate "%s: copyout failed.", msg); 76990Sstevel@tonic-gate rv = EIO; 77000Sstevel@tonic-gate } 77010Sstevel@tonic-gate } 77020Sstevel@tonic-gate kmem_free(path, MAXPATHLEN); 77030Sstevel@tonic-gate 77040Sstevel@tonic-gate break; 77050Sstevel@tonic-gate } 77060Sstevel@tonic-gate case HUBD_REFRESH_DEVDB: 77070Sstevel@tonic-gate msg = "DEVCTL_AP_CONTROL: HUBD_REFRESH_DEVDB"; 77080Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_CBOPS, hubd->h_log_handle, 77090Sstevel@tonic-gate "%s", msg); 77100Sstevel@tonic-gate 77110Sstevel@tonic-gate if ((rv = usba_devdb_refresh()) != USB_SUCCESS) { 77120Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 77130Sstevel@tonic-gate hubd->h_log_handle, 77140Sstevel@tonic-gate "%s: Failed: %d", msg, rv); 77150Sstevel@tonic-gate rv = EIO; 77160Sstevel@tonic-gate } 77170Sstevel@tonic-gate 77180Sstevel@tonic-gate break; 77190Sstevel@tonic-gate default: 77200Sstevel@tonic-gate rv = ENOTSUP; 77210Sstevel@tonic-gate } /* end switch */ 77220Sstevel@tonic-gate 77230Sstevel@tonic-gate break; 77240Sstevel@tonic-gate } 77250Sstevel@tonic-gate 77260Sstevel@tonic-gate default: 77270Sstevel@tonic-gate rv = ENOTTY; 77280Sstevel@tonic-gate } 77290Sstevel@tonic-gate 77300Sstevel@tonic-gate if (dcp) { 77310Sstevel@tonic-gate ndi_dc_freehdl(dcp); 77320Sstevel@tonic-gate } 77330Sstevel@tonic-gate 77340Sstevel@tonic-gate /* allow hotplug thread now */ 77350Sstevel@tonic-gate hubd->h_hotplug_thread--; 77360Sstevel@tonic-gate 77370Sstevel@tonic-gate if ((hubd->h_dev_state == USB_DEV_ONLINE) && 77380Sstevel@tonic-gate hubd->h_ep1_ph && (prev_pipe_state == USB_PIPE_STATE_ACTIVE)) { 77390Sstevel@tonic-gate hubd_start_polling(hubd, 0); 77400Sstevel@tonic-gate } 77410Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 77420Sstevel@tonic-gate 77430Sstevel@tonic-gate ndi_devi_exit(hubd->h_dip, circ); 77440Sstevel@tonic-gate ndi_devi_exit(rh_dip, rh_circ); 77450Sstevel@tonic-gate ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ); 77460Sstevel@tonic-gate 77470Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 77480Sstevel@tonic-gate hubd_pm_idle_component(hubd, hubd->h_dip, 0); 77490Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 77500Sstevel@tonic-gate 77510Sstevel@tonic-gate return (rv); 77520Sstevel@tonic-gate } 77530Sstevel@tonic-gate 77540Sstevel@tonic-gate 77550Sstevel@tonic-gate /* 77560Sstevel@tonic-gate * Helper func used only to help construct the names for the attachment point 77570Sstevel@tonic-gate * minor nodes. Used only in usba_hubdi_attach. 77580Sstevel@tonic-gate * Returns whether it found ancestry or not (USB_SUCCESS if yes). 77590Sstevel@tonic-gate * ports between the root hub and the device represented by dip. 77600Sstevel@tonic-gate * E.g., "2.4.3.1" means this device is 77610Sstevel@tonic-gate * plugged into port 1 of a hub that is 77620Sstevel@tonic-gate * plugged into port 3 of a hub that is 77630Sstevel@tonic-gate * plugged into port 4 of a hub that is 77640Sstevel@tonic-gate * plugged into port 2 of the root hub. 77650Sstevel@tonic-gate * NOTE: Max ap_id path len is HUBD_APID_NAMELEN (32 chars), which is 77660Sstevel@tonic-gate * more than sufficient (as hubs are a max 6 levels deep, port needs 3 77670Sstevel@tonic-gate * chars plus NULL each) 77680Sstevel@tonic-gate */ 77690Sstevel@tonic-gate static void 77700Sstevel@tonic-gate hubd_get_ancestry_str(hubd_t *hubd) 77710Sstevel@tonic-gate { 77720Sstevel@tonic-gate char dev_path[MAXPATHLEN]; 77730Sstevel@tonic-gate char *port_num_pos; 77740Sstevel@tonic-gate char port_list[HUBD_APID_NAMELEN]; 77750Sstevel@tonic-gate char *port_list_end = port_list; 77760Sstevel@tonic-gate 77770Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 77780Sstevel@tonic-gate "hubd_get_ancestry_str: hubd=0x%p", hubd); 77790Sstevel@tonic-gate 77800Sstevel@tonic-gate dev_path[0] = '\0'; 77810Sstevel@tonic-gate (void) ddi_pathname(hubd->h_dip, dev_path); 77820Sstevel@tonic-gate port_num_pos = dev_path; 77830Sstevel@tonic-gate 77840Sstevel@tonic-gate port_list[0] = NULL; 77850Sstevel@tonic-gate while ((port_num_pos = (char *)strstr(port_num_pos, "hub@")) != NULL) { 77860Sstevel@tonic-gate /* 77870Sstevel@tonic-gate * Found a non-root hub between the root hub port and device. 77880Sstevel@tonic-gate * Get the number of the port this hub is plugged into, 77890Sstevel@tonic-gate * and append it to the ancestry string. 77900Sstevel@tonic-gate */ 77910Sstevel@tonic-gate if (port_list_end != port_list) { /* have list already */ 77920Sstevel@tonic-gate (void) strcat(port_list_end, "."); 77930Sstevel@tonic-gate port_list_end++; 77940Sstevel@tonic-gate } 77950Sstevel@tonic-gate 77960Sstevel@tonic-gate while (!isdigit(*port_num_pos)) { 77970Sstevel@tonic-gate if (*port_num_pos++ == '\0') { 77980Sstevel@tonic-gate 77990Sstevel@tonic-gate break; 78000Sstevel@tonic-gate } 78010Sstevel@tonic-gate } 78020Sstevel@tonic-gate 78030Sstevel@tonic-gate while (isdigit(*port_num_pos)) { 78040Sstevel@tonic-gate *port_list_end++ = *port_num_pos++; 78050Sstevel@tonic-gate ASSERT(port_list_end < 78060Sstevel@tonic-gate (port_list + sizeof (port_list))); 78070Sstevel@tonic-gate ASSERT(port_num_pos < (dev_path + sizeof (dev_path))); 78080Sstevel@tonic-gate } 78090Sstevel@tonic-gate *port_list_end = '\0'; 78100Sstevel@tonic-gate } 78110Sstevel@tonic-gate 78120Sstevel@tonic-gate if (port_list_end != port_list) { 78130Sstevel@tonic-gate (void) strcpy(hubd->h_ancestry_str, port_list); 78140Sstevel@tonic-gate (void) strcat(hubd->h_ancestry_str, "."); 78150Sstevel@tonic-gate } 78160Sstevel@tonic-gate } 78170Sstevel@tonic-gate 78180Sstevel@tonic-gate 78190Sstevel@tonic-gate /* Get which port to operate on. */ 78200Sstevel@tonic-gate static usb_port_t 78210Sstevel@tonic-gate hubd_get_port_num(hubd_t *hubd, struct devctl_iocdata *dcp) 78220Sstevel@tonic-gate { 78230Sstevel@tonic-gate int32_t port; 78240Sstevel@tonic-gate 78250Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 78260Sstevel@tonic-gate 78270Sstevel@tonic-gate /* Get which port to operate on. */ 78280Sstevel@tonic-gate if (nvlist_lookup_int32(ndi_dc_get_ap_data(dcp), "port", &port) != 0) { 78290Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, hubd->h_log_handle, 78300Sstevel@tonic-gate "hubd_get_port_num: port lookup failed"); 78310Sstevel@tonic-gate port = 0; 78320Sstevel@tonic-gate } 78330Sstevel@tonic-gate 78340Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 78350Sstevel@tonic-gate "hubd_get_port_num: hubd=0x%p, port=%d", hubd, port); 78360Sstevel@tonic-gate 78370Sstevel@tonic-gate return ((usb_port_t)port); 78380Sstevel@tonic-gate } 78390Sstevel@tonic-gate 78400Sstevel@tonic-gate 78410Sstevel@tonic-gate /* check if child still exists */ 78420Sstevel@tonic-gate static dev_info_t * 78430Sstevel@tonic-gate hubd_get_child_dip(hubd_t *hubd, usb_port_t port) 78440Sstevel@tonic-gate { 78450Sstevel@tonic-gate dev_info_t *child_dip = hubd->h_children_dips[port]; 78460Sstevel@tonic-gate 78470Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 78480Sstevel@tonic-gate "hubd_get_child_dip: hubd=0x%p, port=%d", hubd, port); 78490Sstevel@tonic-gate 78500Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 78510Sstevel@tonic-gate 78520Sstevel@tonic-gate return (child_dip); 78530Sstevel@tonic-gate } 78540Sstevel@tonic-gate 78550Sstevel@tonic-gate 78560Sstevel@tonic-gate /* 78570Sstevel@tonic-gate * hubd_cfgadm_state: 78580Sstevel@tonic-gate * 78590Sstevel@tonic-gate * child_dip list port_state cfgadm_state 78600Sstevel@tonic-gate * -------------- ---------- ------------ 78610Sstevel@tonic-gate * != NULL connected configured or 78620Sstevel@tonic-gate * unconfigured 78630Sstevel@tonic-gate * != NULL not connected disconnect but 78640Sstevel@tonic-gate * busy/still referenced 78650Sstevel@tonic-gate * NULL connected logically disconnected 78660Sstevel@tonic-gate * NULL not connected empty 78670Sstevel@tonic-gate */ 78680Sstevel@tonic-gate static uint_t 78690Sstevel@tonic-gate hubd_cfgadm_state(hubd_t *hubd, usb_port_t port) 78700Sstevel@tonic-gate { 78710Sstevel@tonic-gate uint_t state; 78720Sstevel@tonic-gate dev_info_t *child_dip = hubd_get_child_dip(hubd, port); 78730Sstevel@tonic-gate 78740Sstevel@tonic-gate if (child_dip) { 78750Sstevel@tonic-gate if (hubd->h_port_state[port] & PORT_STATUS_CCS) { 78760Sstevel@tonic-gate /* 78770Sstevel@tonic-gate * connected, now check if driver exists 78780Sstevel@tonic-gate */ 78790Sstevel@tonic-gate if (DEVI_IS_DEVICE_OFFLINE(child_dip) || 78801333Scth !i_ddi_devi_attached(child_dip)) { 78810Sstevel@tonic-gate state = HUBD_CFGADM_UNCONFIGURED; 78820Sstevel@tonic-gate } else { 78830Sstevel@tonic-gate state = HUBD_CFGADM_CONFIGURED; 78840Sstevel@tonic-gate } 78850Sstevel@tonic-gate } else { 78860Sstevel@tonic-gate /* 78870Sstevel@tonic-gate * this means that the dip is around for 78880Sstevel@tonic-gate * a device that is still referenced but 78890Sstevel@tonic-gate * has been yanked out. So the cfgadm info 78900Sstevel@tonic-gate * for this state should be EMPTY (port empty) 78910Sstevel@tonic-gate * and CONFIGURED (dip still valid). 78920Sstevel@tonic-gate */ 78930Sstevel@tonic-gate state = HUBD_CFGADM_STILL_REFERENCED; 78940Sstevel@tonic-gate } 78950Sstevel@tonic-gate } else { 78960Sstevel@tonic-gate /* connected but no child dip */ 78970Sstevel@tonic-gate if (hubd->h_port_state[port] & PORT_STATUS_CCS) { 78980Sstevel@tonic-gate /* logically disconnected */ 78990Sstevel@tonic-gate state = HUBD_CFGADM_DISCONNECTED; 79000Sstevel@tonic-gate } else { 79010Sstevel@tonic-gate /* physically disconnected */ 79020Sstevel@tonic-gate state = HUBD_CFGADM_EMPTY; 79030Sstevel@tonic-gate } 79040Sstevel@tonic-gate } 79050Sstevel@tonic-gate 79060Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 79070Sstevel@tonic-gate "hubd_cfgadm_state: hubd=0x%p, port=%d state=0x%x", 79080Sstevel@tonic-gate hubd, port, state); 79090Sstevel@tonic-gate 79100Sstevel@tonic-gate return (state); 79110Sstevel@tonic-gate } 79120Sstevel@tonic-gate 79130Sstevel@tonic-gate 79140Sstevel@tonic-gate /* 79150Sstevel@tonic-gate * hubd_toggle_port: 79160Sstevel@tonic-gate */ 79170Sstevel@tonic-gate static int 79180Sstevel@tonic-gate hubd_toggle_port(hubd_t *hubd, usb_port_t port) 79190Sstevel@tonic-gate { 79200Sstevel@tonic-gate usb_hub_descr_t *hub_descr; 79210Sstevel@tonic-gate int wait; 79220Sstevel@tonic-gate uint_t retry; 79230Sstevel@tonic-gate uint16_t status; 79240Sstevel@tonic-gate uint16_t change; 79250Sstevel@tonic-gate 79260Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 79270Sstevel@tonic-gate "hubd_toggle_port: hubd=0x%p, port=%d", hubd, port); 79280Sstevel@tonic-gate 79290Sstevel@tonic-gate if ((hubd_disable_port_power(hubd, port)) != USB_SUCCESS) { 79300Sstevel@tonic-gate 79310Sstevel@tonic-gate return (USB_FAILURE); 79320Sstevel@tonic-gate } 79330Sstevel@tonic-gate 79340Sstevel@tonic-gate /* 79350Sstevel@tonic-gate * see hubd_enable_all_port_power() which 79360Sstevel@tonic-gate * requires longer delay for hubs. 79370Sstevel@tonic-gate */ 79380Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 79390Sstevel@tonic-gate delay(drv_usectohz(hubd_device_delay / 10)); 79400Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 79410Sstevel@tonic-gate 79420Sstevel@tonic-gate hub_descr = &hubd->h_hub_descr; 79430Sstevel@tonic-gate 79440Sstevel@tonic-gate /* 79450Sstevel@tonic-gate * According to section 11.11 of USB, for hubs with no power 79460Sstevel@tonic-gate * switches, bPwrOn2PwrGood is zero. But we wait for some 79470Sstevel@tonic-gate * arbitrary time to enable power to become stable. 79480Sstevel@tonic-gate * 79490Sstevel@tonic-gate * If an hub supports port power swicthing, we need to wait 79500Sstevel@tonic-gate * at least 20ms before accesing corresonding usb port. 79510Sstevel@tonic-gate */ 79520Sstevel@tonic-gate if ((hub_descr->wHubCharacteristics & 79530Sstevel@tonic-gate HUB_CHARS_NO_POWER_SWITCHING) || (!hub_descr->bPwrOn2PwrGood)) { 79540Sstevel@tonic-gate wait = hubd_device_delay / 10; 79550Sstevel@tonic-gate } else { 79560Sstevel@tonic-gate wait = max(HUB_DEFAULT_POPG, 79570Sstevel@tonic-gate hub_descr->bPwrOn2PwrGood) * 2 * 1000; 79580Sstevel@tonic-gate } 79590Sstevel@tonic-gate 79600Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 79610Sstevel@tonic-gate "hubd_toggle_port: popg=%d wait=%d", 79620Sstevel@tonic-gate hub_descr->bPwrOn2PwrGood, wait); 79630Sstevel@tonic-gate 79640Sstevel@tonic-gate retry = 0; 79650Sstevel@tonic-gate 79660Sstevel@tonic-gate do { 79670Sstevel@tonic-gate (void) hubd_enable_port_power(hubd, port); 79680Sstevel@tonic-gate 79690Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 79700Sstevel@tonic-gate delay(drv_usectohz(wait)); 79710Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 79720Sstevel@tonic-gate 79730Sstevel@tonic-gate /* Get port status */ 79740Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 79750Sstevel@tonic-gate &status, &change, 0); 79760Sstevel@tonic-gate 79770Sstevel@tonic-gate /* For retry if any, use some extra delay */ 79780Sstevel@tonic-gate wait = max(wait, hubd_device_delay / 10); 79790Sstevel@tonic-gate 79800Sstevel@tonic-gate retry++; 79810Sstevel@tonic-gate 79820Sstevel@tonic-gate } while ((!(status & PORT_STATUS_PPS)) && (retry < HUBD_PORT_RETRY)); 79830Sstevel@tonic-gate 79840Sstevel@tonic-gate /* Print warning message if port has no power */ 79850Sstevel@tonic-gate if (!(status & PORT_STATUS_PPS)) { 79860Sstevel@tonic-gate 7987978Sfrits USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 79880Sstevel@tonic-gate "hubd_toggle_port: port %d power-on failed, " 79890Sstevel@tonic-gate "port status 0x%x", port, status); 79900Sstevel@tonic-gate 79910Sstevel@tonic-gate return (USB_FAILURE); 79920Sstevel@tonic-gate } 79930Sstevel@tonic-gate 79940Sstevel@tonic-gate return (USB_SUCCESS); 79950Sstevel@tonic-gate } 79961001Ssl147100 79971001Ssl147100 79981001Ssl147100 /* 79991001Ssl147100 * hubd_init_power_budget: 80001001Ssl147100 * Init power budget variables in hubd structure. According 80011001Ssl147100 * to USB spec, the power budget rules are: 80021001Ssl147100 * 1. local-powered hubs including root-hubs can supply 80031001Ssl147100 * 500mA to each port at maximum 80041001Ssl147100 * 2. two bus-powered hubs are not allowed to concatenate 80051001Ssl147100 * 3. bus-powered hubs can supply 100mA to each port at 80061001Ssl147100 * maximum, and the power consumed by all downstream 80071001Ssl147100 * ports and the hub itself cannot exceed the max power 80081001Ssl147100 * supplied by the upstream port, i.e., 500mA 80091001Ssl147100 * The routine is only called during hub attach time 80101001Ssl147100 */ 80111001Ssl147100 static int 80121001Ssl147100 hubd_init_power_budget(hubd_t *hubd) 80131001Ssl147100 { 80141001Ssl147100 uint16_t status = 0; 80151001Ssl147100 usba_device_t *hubd_ud = NULL; 80161001Ssl147100 size_t size; 80171001Ssl147100 usb_cfg_descr_t cfg_descr; 80181001Ssl147100 dev_info_t *pdip = NULL; 80191001Ssl147100 hubd_t *phubd = NULL; 80201001Ssl147100 80211001Ssl147100 if (hubd->h_ignore_pwr_budget) { 80221001Ssl147100 80231001Ssl147100 return (USB_SUCCESS); 80241001Ssl147100 } 80251001Ssl147100 80261001Ssl147100 USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 80271001Ssl147100 "hubd_init_power_budget:"); 80281001Ssl147100 80291001Ssl147100 ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 80301001Ssl147100 ASSERT(hubd->h_default_pipe != 0); 80311001Ssl147100 mutex_exit(HUBD_MUTEX(hubd)); 80321001Ssl147100 80331001Ssl147100 /* get device status */ 80341001Ssl147100 if ((usb_get_status(hubd->h_dip, hubd->h_default_pipe, 80351001Ssl147100 HUB_GET_DEVICE_STATUS_TYPE, 80361001Ssl147100 0, &status, 0)) != USB_SUCCESS) { 80371001Ssl147100 mutex_enter(HUBD_MUTEX(hubd)); 80381001Ssl147100 80391001Ssl147100 return (USB_FAILURE); 80401001Ssl147100 } 80411001Ssl147100 80421001Ssl147100 hubd_ud = usba_get_usba_device(hubd->h_dip); 80431001Ssl147100 80441001Ssl147100 size = usb_parse_cfg_descr(hubd_ud->usb_cfg, hubd_ud->usb_cfg_length, 80451001Ssl147100 &cfg_descr, USB_CFG_DESCR_SIZE); 80461001Ssl147100 80471001Ssl147100 if (size != USB_CFG_DESCR_SIZE) { 80481001Ssl147100 USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle, 80491001Ssl147100 "get hub configuration descriptor failed"); 80501001Ssl147100 mutex_enter(HUBD_MUTEX(hubd)); 80511001Ssl147100 80521001Ssl147100 return (USB_FAILURE); 80531001Ssl147100 } 80541001Ssl147100 80551001Ssl147100 mutex_enter(HUBD_MUTEX(hubd)); 80561001Ssl147100 80571001Ssl147100 hubd->h_local_pwr_capable = (cfg_descr.bmAttributes & 80581001Ssl147100 USB_CFG_ATTR_SELFPWR); 80591001Ssl147100 80601001Ssl147100 if (hubd->h_local_pwr_capable) { 80611001Ssl147100 USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle, 80621001Ssl147100 "hub is capable of local power"); 80631001Ssl147100 } 80641001Ssl147100 80651001Ssl147100 hubd->h_local_pwr_on = (status & 80661001Ssl147100 USB_DEV_SLF_PWRD_STATUS) && hubd->h_local_pwr_capable; 80671001Ssl147100 80681001Ssl147100 if (hubd->h_local_pwr_on) { 80691001Ssl147100 USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle, 80701001Ssl147100 "hub is local-powered"); 80711001Ssl147100 80721001Ssl147100 hubd->h_pwr_limit = (USB_PWR_UNIT_LOAD * 80731001Ssl147100 USB_HIGH_PWR_VALUE) / USB_CFG_DESCR_PWR_UNIT; 80741001Ssl147100 } else { 80751001Ssl147100 hubd->h_pwr_limit = (USB_PWR_UNIT_LOAD * 80761001Ssl147100 USB_LOW_PWR_VALUE) / USB_CFG_DESCR_PWR_UNIT; 80771001Ssl147100 80781001Ssl147100 hubd->h_pwr_left = (USB_PWR_UNIT_LOAD * 80791001Ssl147100 USB_HIGH_PWR_VALUE) / USB_CFG_DESCR_PWR_UNIT; 80801001Ssl147100 80811001Ssl147100 ASSERT(!usba_is_root_hub(hubd->h_dip)); 80821001Ssl147100 80831001Ssl147100 if (!usba_is_root_hub(hubd->h_dip)) { 80841001Ssl147100 /* 80851001Ssl147100 * two bus-powered hubs are not 80861001Ssl147100 * allowed to be concatenated 80871001Ssl147100 */ 80881001Ssl147100 mutex_exit(HUBD_MUTEX(hubd)); 80891001Ssl147100 80901001Ssl147100 pdip = ddi_get_parent(hubd->h_dip); 80911001Ssl147100 phubd = hubd_get_soft_state(pdip); 80921001Ssl147100 ASSERT(phubd != NULL); 80931001Ssl147100 80941001Ssl147100 if (!phubd->h_ignore_pwr_budget) { 80951001Ssl147100 mutex_enter(HUBD_MUTEX(phubd)); 80961001Ssl147100 if (phubd->h_local_pwr_on == B_FALSE) { 80973435Slg150142 USB_DPRINTF_L1(DPRINT_MASK_HUB, 80981001Ssl147100 hubd->h_log_handle, 80991001Ssl147100 "two bus-powered hubs cannot " 81001001Ssl147100 "be concatenated"); 81011001Ssl147100 81021001Ssl147100 mutex_exit(HUBD_MUTEX(phubd)); 81031001Ssl147100 mutex_enter(HUBD_MUTEX(hubd)); 81041001Ssl147100 81051001Ssl147100 return (USB_FAILURE); 81061001Ssl147100 } 81071001Ssl147100 mutex_exit(HUBD_MUTEX(phubd)); 81081001Ssl147100 } 81091001Ssl147100 81101001Ssl147100 mutex_enter(HUBD_MUTEX(hubd)); 81111001Ssl147100 81121001Ssl147100 USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle, 81131001Ssl147100 "hub is bus-powered"); 81141001Ssl147100 } else { 81151001Ssl147100 USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle, 81161001Ssl147100 "root-hub must be local-powered"); 81171001Ssl147100 } 81181001Ssl147100 81191001Ssl147100 /* 81201001Ssl147100 * Subtract the power consumed by the hub itself 81211001Ssl147100 * and get the power that can be supplied to 81221001Ssl147100 * downstream ports 81231001Ssl147100 */ 81241001Ssl147100 hubd->h_pwr_left -= 81251001Ssl147100 hubd->h_hub_descr.bHubContrCurrent / 81261001Ssl147100 USB_CFG_DESCR_PWR_UNIT; 81271001Ssl147100 if (hubd->h_pwr_left < 0) { 81281001Ssl147100 USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle, 81291001Ssl147100 "hubd->h_pwr_left is less than bHubContrCurrent, " 81301001Ssl147100 "should fail"); 81311001Ssl147100 81321001Ssl147100 return (USB_FAILURE); 81331001Ssl147100 } 81341001Ssl147100 } 81351001Ssl147100 81361001Ssl147100 return (USB_SUCCESS); 81371001Ssl147100 } 81381001Ssl147100 81391001Ssl147100 81401001Ssl147100 /* 81411001Ssl147100 * usba_hubdi_check_power_budget: 81421001Ssl147100 * Check if the hub has enough power budget to allow a 81431001Ssl147100 * child device to select a configuration of config_index. 81441001Ssl147100 */ 81451001Ssl147100 int 81461001Ssl147100 usba_hubdi_check_power_budget(dev_info_t *dip, usba_device_t *child_ud, 81471001Ssl147100 uint_t config_index) 81481001Ssl147100 { 81491001Ssl147100 int16_t pwr_left, pwr_limit, pwr_required; 81501001Ssl147100 size_t size; 81511001Ssl147100 usb_cfg_descr_t cfg_descr; 81521001Ssl147100 hubd_t *hubd; 81531001Ssl147100 81541001Ssl147100 if ((hubd = hubd_get_soft_state(dip)) == NULL) { 81551001Ssl147100 81561001Ssl147100 return (USB_FAILURE); 81571001Ssl147100 } 81581001Ssl147100 81591001Ssl147100 if (hubd->h_ignore_pwr_budget) { 81601001Ssl147100 81611001Ssl147100 return (USB_SUCCESS); 81621001Ssl147100 } 81631001Ssl147100 81641001Ssl147100 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 81651001Ssl147100 "usba_hubdi_check_power_budget: " 81661001Ssl147100 "dip=0x%p child_ud=0x%p conf_index=%d", dip, 81671001Ssl147100 child_ud, config_index); 81681001Ssl147100 81691001Ssl147100 mutex_enter(HUBD_MUTEX(hubd)); 81701001Ssl147100 pwr_limit = hubd->h_pwr_limit; 81711001Ssl147100 if (hubd->h_local_pwr_on == B_FALSE) { 81721001Ssl147100 pwr_left = hubd->h_pwr_left; 81731001Ssl147100 pwr_limit = (pwr_limit <= pwr_left) ? pwr_limit : pwr_left; 81741001Ssl147100 } 81751001Ssl147100 mutex_exit(HUBD_MUTEX(hubd)); 81761001Ssl147100 81771001Ssl147100 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 81781001Ssl147100 "usba_hubdi_check_power_budget: " 81791001Ssl147100 "available power is %dmA", pwr_limit * USB_CFG_DESCR_PWR_UNIT); 81801001Ssl147100 81811001Ssl147100 size = usb_parse_cfg_descr( 81821001Ssl147100 child_ud->usb_cfg_array[config_index], USB_CFG_DESCR_SIZE, 81831001Ssl147100 &cfg_descr, USB_CFG_DESCR_SIZE); 81841001Ssl147100 81851001Ssl147100 if (size != USB_CFG_DESCR_SIZE) { 81861001Ssl147100 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 81871001Ssl147100 "get hub configuration descriptor failed"); 81881001Ssl147100 81891001Ssl147100 return (USB_FAILURE); 81901001Ssl147100 } 81911001Ssl147100 81921001Ssl147100 pwr_required = cfg_descr.bMaxPower; 81931001Ssl147100 81941001Ssl147100 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 81951001Ssl147100 "usba_hubdi_check_power_budget: " 81961001Ssl147100 "child bmAttributes=0x%x bMaxPower=%d " 81971001Ssl147100 "with config_index=%d", cfg_descr.bmAttributes, 81981001Ssl147100 pwr_required, config_index); 81991001Ssl147100 82001001Ssl147100 if (pwr_required > pwr_limit) { 82013435Slg150142 USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 82021001Ssl147100 "configuration %d for device %s %s at port %d " 82031001Ssl147100 "exceeds power available for this port, please " 82041001Ssl147100 "re-insert your device into another hub port which " 82051001Ssl147100 "has enough power", 82061001Ssl147100 config_index, 82071001Ssl147100 child_ud->usb_mfg_str, 82081001Ssl147100 child_ud->usb_product_str, 82091001Ssl147100 child_ud->usb_port); 82101001Ssl147100 82111001Ssl147100 return (USB_FAILURE); 82121001Ssl147100 } 82131001Ssl147100 82141001Ssl147100 return (USB_SUCCESS); 82151001Ssl147100 } 82161001Ssl147100 82171001Ssl147100 82181001Ssl147100 /* 82191001Ssl147100 * usba_hubdi_incr_power_budget: 82201001Ssl147100 * Increase the hub power budget value when a child device 82211001Ssl147100 * is removed from a bus-powered hub port. 82221001Ssl147100 */ 82231001Ssl147100 void 82241001Ssl147100 usba_hubdi_incr_power_budget(dev_info_t *dip, usba_device_t *child_ud) 82251001Ssl147100 { 82261001Ssl147100 uint16_t pwr_value; 82271001Ssl147100 hubd_t *hubd = hubd_get_soft_state(dip); 82281001Ssl147100 82291001Ssl147100 ASSERT(hubd != NULL); 82301001Ssl147100 82311001Ssl147100 if (hubd->h_ignore_pwr_budget) { 82321001Ssl147100 82331001Ssl147100 return; 82341001Ssl147100 } 82351001Ssl147100 82361001Ssl147100 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 82371001Ssl147100 "usba_hubdi_incr_power_budget: " 82381001Ssl147100 "dip=0x%p child_ud=0x%p", dip, child_ud); 82391001Ssl147100 82401001Ssl147100 mutex_enter(HUBD_MUTEX(hubd)); 82411001Ssl147100 if (hubd->h_local_pwr_on == B_TRUE) { 82421001Ssl147100 USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle, 82431001Ssl147100 "usba_hubdi_incr_power_budget: " 82441001Ssl147100 "hub is local powered"); 82451001Ssl147100 mutex_exit(HUBD_MUTEX(hubd)); 82461001Ssl147100 82471001Ssl147100 return; 82481001Ssl147100 } 82491001Ssl147100 mutex_exit(HUBD_MUTEX(hubd)); 82501001Ssl147100 82511001Ssl147100 mutex_enter(&child_ud->usb_mutex); 82521001Ssl147100 if (child_ud->usb_pwr_from_hub == 0) { 82531001Ssl147100 mutex_exit(&child_ud->usb_mutex); 82541001Ssl147100 82551001Ssl147100 return; 82561001Ssl147100 } 82571001Ssl147100 pwr_value = child_ud->usb_pwr_from_hub; 82581001Ssl147100 mutex_exit(&child_ud->usb_mutex); 82591001Ssl147100 82601001Ssl147100 mutex_enter(HUBD_MUTEX(hubd)); 82611001Ssl147100 hubd->h_pwr_left += pwr_value; 82621001Ssl147100 82631001Ssl147100 USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle, 82641001Ssl147100 "usba_hubdi_incr_power_budget: " 82651001Ssl147100 "available power is %dmA, increased by %dmA", 82661001Ssl147100 hubd->h_pwr_left * USB_CFG_DESCR_PWR_UNIT, 82671001Ssl147100 pwr_value * USB_CFG_DESCR_PWR_UNIT); 82681001Ssl147100 82691001Ssl147100 mutex_exit(HUBD_MUTEX(hubd)); 82701001Ssl147100 82711001Ssl147100 mutex_enter(&child_ud->usb_mutex); 82721001Ssl147100 child_ud->usb_pwr_from_hub = 0; 82731001Ssl147100 mutex_exit(&child_ud->usb_mutex); 82741001Ssl147100 } 82751001Ssl147100 82761001Ssl147100 82771001Ssl147100 /* 82781001Ssl147100 * usba_hubdi_decr_power_budget: 82791001Ssl147100 * Decrease the hub power budget value when a child device 82801001Ssl147100 * is inserted to a bus-powered hub port. 82811001Ssl147100 */ 82821001Ssl147100 void 82831001Ssl147100 usba_hubdi_decr_power_budget(dev_info_t *dip, usba_device_t *child_ud) 82841001Ssl147100 { 82851001Ssl147100 uint16_t pwr_value; 82861001Ssl147100 size_t size; 82871001Ssl147100 usb_cfg_descr_t cfg_descr; 82881001Ssl147100 hubd_t *hubd = hubd_get_soft_state(dip); 82891001Ssl147100 82901001Ssl147100 ASSERT(hubd != NULL); 82911001Ssl147100 82921001Ssl147100 if (hubd->h_ignore_pwr_budget) { 82931001Ssl147100 82941001Ssl147100 return; 82951001Ssl147100 } 82961001Ssl147100 82971001Ssl147100 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 82981001Ssl147100 "usba_hubdi_decr_power_budget: " 82991001Ssl147100 "dip=0x%p child_ud=0x%p", dip, child_ud); 83001001Ssl147100 83011001Ssl147100 mutex_enter(HUBD_MUTEX(hubd)); 83021001Ssl147100 if (hubd->h_local_pwr_on == B_TRUE) { 83031001Ssl147100 USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle, 83041001Ssl147100 "usba_hubdi_decr_power_budget: " 83051001Ssl147100 "hub is local powered"); 83061001Ssl147100 mutex_exit(HUBD_MUTEX(hubd)); 83071001Ssl147100 83081001Ssl147100 return; 83091001Ssl147100 } 83101001Ssl147100 mutex_exit(HUBD_MUTEX(hubd)); 83111001Ssl147100 83121001Ssl147100 mutex_enter(&child_ud->usb_mutex); 83131001Ssl147100 if (child_ud->usb_pwr_from_hub > 0) { 83141001Ssl147100 mutex_exit(&child_ud->usb_mutex); 83151001Ssl147100 83161001Ssl147100 return; 83171001Ssl147100 } 83181001Ssl147100 mutex_exit(&child_ud->usb_mutex); 83191001Ssl147100 83201001Ssl147100 size = usb_parse_cfg_descr( 83211001Ssl147100 child_ud->usb_cfg, child_ud->usb_cfg_length, 83221001Ssl147100 &cfg_descr, USB_CFG_DESCR_SIZE); 83231001Ssl147100 ASSERT(size == USB_CFG_DESCR_SIZE); 83241001Ssl147100 83251001Ssl147100 mutex_enter(HUBD_MUTEX(hubd)); 83261001Ssl147100 pwr_value = cfg_descr.bMaxPower; 83271001Ssl147100 hubd->h_pwr_left -= pwr_value; 83281001Ssl147100 ASSERT(hubd->h_pwr_left >= 0); 83291001Ssl147100 83301001Ssl147100 USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle, 83311001Ssl147100 "usba_hubdi_decr_power_budget: " 83321001Ssl147100 "available power is %dmA, decreased by %dmA", 83331001Ssl147100 hubd->h_pwr_left * USB_CFG_DESCR_PWR_UNIT, 83341001Ssl147100 pwr_value * USB_CFG_DESCR_PWR_UNIT); 83351001Ssl147100 83361001Ssl147100 mutex_exit(HUBD_MUTEX(hubd)); 83371001Ssl147100 83381001Ssl147100 mutex_enter(&child_ud->usb_mutex); 83391001Ssl147100 child_ud->usb_pwr_from_hub = pwr_value; 83401001Ssl147100 mutex_exit(&child_ud->usb_mutex); 83411001Ssl147100 } 8342