17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5fef1e07eSsl * Common Development and Distribution License (the "License"). 6fef1e07eSsl * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22f5b8369cSRaymond Chen * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. 23cd21e7c5SGarrett D'Amore * Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved. 24e5815e7aSJosef 'Jeff' Sipek * Copyright 2014 Nexenta Systems, Inc. All rights reserved. 250d2006e4SRobert Mustacchi * Copyright 2019, Joyent, Inc. 267c478bd9Sstevel@tonic-gate */ 277c478bd9Sstevel@tonic-gate 287c478bd9Sstevel@tonic-gate /* 297c478bd9Sstevel@tonic-gate * USBA: Solaris USB Architecture support for the hub 307c478bd9Sstevel@tonic-gate * including root hub 317c478bd9Sstevel@tonic-gate * Most of the code for hubd resides in this file and 327c478bd9Sstevel@tonic-gate * is shared between the HCD root hub support and hubd 337c478bd9Sstevel@tonic-gate */ 347c478bd9Sstevel@tonic-gate #define USBA_FRAMEWORK 357c478bd9Sstevel@tonic-gate #include <sys/usb/usba.h> 367c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usba_devdb.h> 377c478bd9Sstevel@tonic-gate #include <sys/sunndi.h> 387c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usba_impl.h> 397c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usba_types.h> 407c478bd9Sstevel@tonic-gate #include <sys/usb/usba/hubdi.h> 417c478bd9Sstevel@tonic-gate #include <sys/usb/usba/hcdi_impl.h> 427c478bd9Sstevel@tonic-gate #include <sys/usb/hubd/hub.h> 437c478bd9Sstevel@tonic-gate #include <sys/usb/hubd/hubdvar.h> 447c478bd9Sstevel@tonic-gate #include <sys/usb/hubd/hubd_impl.h> 457c478bd9Sstevel@tonic-gate #include <sys/kobj.h> 467c478bd9Sstevel@tonic-gate #include <sys/kobj_lex.h> 477c478bd9Sstevel@tonic-gate #include <sys/fs/dv_node.h> 48d29f5a71Szhigang lu - Sun Microsystems - Beijing China #include <sys/strsun.h> 497c478bd9Sstevel@tonic-gate 5040482326SVincent Wang /* 5140482326SVincent Wang * External functions 5240482326SVincent Wang */ 5340482326SVincent Wang extern boolean_t consconfig_console_is_ready(void); 5440482326SVincent Wang 557c478bd9Sstevel@tonic-gate /* 567c478bd9Sstevel@tonic-gate * Prototypes for static functions 577c478bd9Sstevel@tonic-gate */ 58*d96925c4SRichard Lowe static int usba_hubdi_bus_ctl(dev_info_t *dip, 59*d96925c4SRichard Lowe dev_info_t *rdip, 60*d96925c4SRichard Lowe ddi_ctl_enum_t op, 61*d96925c4SRichard Lowe void *arg, 62*d96925c4SRichard Lowe void *result); 63*d96925c4SRichard Lowe 64*d96925c4SRichard Lowe static int usba_hubdi_map_fault(dev_info_t *dip, 65*d96925c4SRichard Lowe dev_info_t *rdip, 66*d96925c4SRichard Lowe struct hat *hat, 67*d96925c4SRichard Lowe struct seg *seg, 68*d96925c4SRichard Lowe caddr_t addr, 69*d96925c4SRichard Lowe struct devpage *dp, 70*d96925c4SRichard Lowe pfn_t pfn, 71*d96925c4SRichard Lowe uint_t prot, 72*d96925c4SRichard Lowe uint_t lock); 737c478bd9Sstevel@tonic-gate 747c478bd9Sstevel@tonic-gate static int hubd_busop_get_eventcookie(dev_info_t *dip, 75*d96925c4SRichard Lowe dev_info_t *rdip, 76*d96925c4SRichard Lowe char *eventname, 77*d96925c4SRichard Lowe ddi_eventcookie_t *cookie); 787c478bd9Sstevel@tonic-gate static int hubd_busop_add_eventcall(dev_info_t *dip, 79*d96925c4SRichard Lowe dev_info_t *rdip, 80*d96925c4SRichard Lowe ddi_eventcookie_t cookie, 81*d96925c4SRichard Lowe void (*callback)(dev_info_t *dip, ddi_eventcookie_t cookie, void *arg, 82*d96925c4SRichard Lowe void *bus_impldata), 83*d96925c4SRichard Lowe void *arg, ddi_callback_id_t *cb_id); 847c478bd9Sstevel@tonic-gate static int hubd_busop_remove_eventcall(dev_info_t *dip, 85*d96925c4SRichard Lowe ddi_callback_id_t cb_id); 867c478bd9Sstevel@tonic-gate static int hubd_bus_config(dev_info_t *dip, 87*d96925c4SRichard Lowe uint_t flag, 88*d96925c4SRichard Lowe ddi_bus_config_op_t op, 89*d96925c4SRichard Lowe void *arg, 90*d96925c4SRichard Lowe dev_info_t **child); 917c478bd9Sstevel@tonic-gate static int hubd_bus_unconfig(dev_info_t *dip, 92*d96925c4SRichard Lowe uint_t flag, 93*d96925c4SRichard Lowe ddi_bus_config_op_t op, 94*d96925c4SRichard Lowe void *arg); 957c478bd9Sstevel@tonic-gate static int hubd_bus_power(dev_info_t *dip, void *impl_arg, 96*d96925c4SRichard Lowe pm_bus_power_op_t op, void *arg, void *result); 977c478bd9Sstevel@tonic-gate 987c478bd9Sstevel@tonic-gate static usb_port_t hubd_get_port_num(hubd_t *, struct devctl_iocdata *); 997c478bd9Sstevel@tonic-gate static dev_info_t *hubd_get_child_dip(hubd_t *, usb_port_t); 1007c478bd9Sstevel@tonic-gate static uint_t hubd_cfgadm_state(hubd_t *, usb_port_t); 1017c478bd9Sstevel@tonic-gate static int hubd_toggle_port(hubd_t *, usb_port_t); 1027c478bd9Sstevel@tonic-gate static void hubd_register_cpr_callback(hubd_t *); 1037c478bd9Sstevel@tonic-gate static void hubd_unregister_cpr_callback(hubd_t *); 1047c478bd9Sstevel@tonic-gate 1057c478bd9Sstevel@tonic-gate /* 1067c478bd9Sstevel@tonic-gate * Busops vector for USB HUB's 1077c478bd9Sstevel@tonic-gate */ 1087c478bd9Sstevel@tonic-gate struct bus_ops usba_hubdi_busops = { 1097c478bd9Sstevel@tonic-gate BUSO_REV, 1107c478bd9Sstevel@tonic-gate nullbusmap, /* bus_map */ 1117c478bd9Sstevel@tonic-gate NULL, /* bus_get_intrspec */ 1127c478bd9Sstevel@tonic-gate NULL, /* bus_add_intrspec */ 1137c478bd9Sstevel@tonic-gate NULL, /* bus_remove_intrspec */ 1147c478bd9Sstevel@tonic-gate usba_hubdi_map_fault, /* bus_map_fault */ 115cd21e7c5SGarrett D'Amore NULL, /* bus_dma_map */ 1167c478bd9Sstevel@tonic-gate ddi_dma_allochdl, 1177c478bd9Sstevel@tonic-gate ddi_dma_freehdl, 1187c478bd9Sstevel@tonic-gate ddi_dma_bindhdl, 1197c478bd9Sstevel@tonic-gate ddi_dma_unbindhdl, 1207c478bd9Sstevel@tonic-gate ddi_dma_flush, 1217c478bd9Sstevel@tonic-gate ddi_dma_win, 1227c478bd9Sstevel@tonic-gate ddi_dma_mctl, /* bus_dma_ctl */ 1237c478bd9Sstevel@tonic-gate usba_hubdi_bus_ctl, /* bus_ctl */ 1247c478bd9Sstevel@tonic-gate ddi_bus_prop_op, /* bus_prop_op */ 1257c478bd9Sstevel@tonic-gate hubd_busop_get_eventcookie, 1267c478bd9Sstevel@tonic-gate hubd_busop_add_eventcall, 1277c478bd9Sstevel@tonic-gate hubd_busop_remove_eventcall, 1287c478bd9Sstevel@tonic-gate NULL, /* bus_post_event */ 1297c478bd9Sstevel@tonic-gate NULL, /* bus_intr_ctl */ 1307c478bd9Sstevel@tonic-gate hubd_bus_config, /* bus_config */ 1317c478bd9Sstevel@tonic-gate hubd_bus_unconfig, /* bus_unconfig */ 1327c478bd9Sstevel@tonic-gate NULL, /* bus_fm_init */ 1337c478bd9Sstevel@tonic-gate NULL, /* bus_fm_fini */ 1347c478bd9Sstevel@tonic-gate NULL, /* bus_fm_access_enter */ 1357c478bd9Sstevel@tonic-gate NULL, /* bus_fm_access_exit */ 1367c478bd9Sstevel@tonic-gate hubd_bus_power /* bus_power */ 1377c478bd9Sstevel@tonic-gate }; 1387c478bd9Sstevel@tonic-gate 139f5b8369cSRaymond Chen #define USB_HUB_INTEL_VID 0x8087 140f5b8369cSRaymond Chen #define USB_HUB_INTEL_PID 0x0020 1417c478bd9Sstevel@tonic-gate 1427c478bd9Sstevel@tonic-gate /* 1437c478bd9Sstevel@tonic-gate * local variables 1447c478bd9Sstevel@tonic-gate */ 1457c478bd9Sstevel@tonic-gate static kmutex_t usba_hubdi_mutex; /* protects USBA HUB data structures */ 1467c478bd9Sstevel@tonic-gate 1477c478bd9Sstevel@tonic-gate static usba_list_entry_t usba_hubdi_list; 1487c478bd9Sstevel@tonic-gate 1497c478bd9Sstevel@tonic-gate usb_log_handle_t hubdi_log_handle; 1507c478bd9Sstevel@tonic-gate uint_t hubdi_errlevel = USB_LOG_L4; 1517c478bd9Sstevel@tonic-gate uint_t hubdi_errmask = (uint_t)-1; 152d29f5a71Szhigang lu - Sun Microsystems - Beijing China uint8_t hubdi_min_pm_threshold = 5; /* seconds */ 153aa041649SRaymond Chen uint8_t hubdi_reset_delay = 20; /* seconds */ 1548e1b7aa1Sfei feng - Sun Microsystems - Beijing China extern int modrootloaded; 1557c478bd9Sstevel@tonic-gate 1567c478bd9Sstevel@tonic-gate /* 1577c478bd9Sstevel@tonic-gate * initialize private data 1587c478bd9Sstevel@tonic-gate */ 1597c478bd9Sstevel@tonic-gate void 1607c478bd9Sstevel@tonic-gate usba_hubdi_initialization() 1617c478bd9Sstevel@tonic-gate { 1627c478bd9Sstevel@tonic-gate hubdi_log_handle = usb_alloc_log_hdl(NULL, "hubdi", &hubdi_errlevel, 163c0f24e5bSlg &hubdi_errmask, NULL, 0); 1647c478bd9Sstevel@tonic-gate 1657c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle, 1667c478bd9Sstevel@tonic-gate "usba_hubdi_initialization"); 1677c478bd9Sstevel@tonic-gate 1687c478bd9Sstevel@tonic-gate mutex_init(&usba_hubdi_mutex, NULL, MUTEX_DRIVER, NULL); 1697c478bd9Sstevel@tonic-gate 1707c478bd9Sstevel@tonic-gate usba_init_list(&usba_hubdi_list, NULL, NULL); 1717c478bd9Sstevel@tonic-gate } 1727c478bd9Sstevel@tonic-gate 1737c478bd9Sstevel@tonic-gate 1747c478bd9Sstevel@tonic-gate void 1757c478bd9Sstevel@tonic-gate usba_hubdi_destroy() 1767c478bd9Sstevel@tonic-gate { 1777c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle, 1787c478bd9Sstevel@tonic-gate "usba_hubdi_destroy"); 1797c478bd9Sstevel@tonic-gate 1807c478bd9Sstevel@tonic-gate mutex_destroy(&usba_hubdi_mutex); 1817c478bd9Sstevel@tonic-gate usba_destroy_list(&usba_hubdi_list); 1827c478bd9Sstevel@tonic-gate 1837c478bd9Sstevel@tonic-gate usb_free_log_hdl(hubdi_log_handle); 1847c478bd9Sstevel@tonic-gate } 1857c478bd9Sstevel@tonic-gate 1867c478bd9Sstevel@tonic-gate 1877c478bd9Sstevel@tonic-gate /* 1887c478bd9Sstevel@tonic-gate * Called by an HUB to attach an instance of the driver 1897c478bd9Sstevel@tonic-gate * make this instance known to USBA 1907c478bd9Sstevel@tonic-gate * the HUB should initialize usba_hubdi structure prior 1917c478bd9Sstevel@tonic-gate * to calling this interface 1927c478bd9Sstevel@tonic-gate */ 193ff0e937bSRaymond Chen int 194993e3fafSRobert Mustacchi usba_hubdi_register(dev_info_t *dip, uint_t flags) 1957c478bd9Sstevel@tonic-gate { 1967c478bd9Sstevel@tonic-gate usba_hubdi_t *hubdi = kmem_zalloc(sizeof (usba_hubdi_t), KM_SLEEP); 1977c478bd9Sstevel@tonic-gate usba_device_t *usba_device = usba_get_usba_device(dip); 1987c478bd9Sstevel@tonic-gate 1997c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle, 2007c478bd9Sstevel@tonic-gate "usba_hubdi_register: %s", ddi_node_name(dip)); 2017c478bd9Sstevel@tonic-gate 2027c478bd9Sstevel@tonic-gate hubdi->hubdi_dip = dip; 2037c478bd9Sstevel@tonic-gate hubdi->hubdi_flags = flags; 2047c478bd9Sstevel@tonic-gate 2057c478bd9Sstevel@tonic-gate usba_device->usb_hubdi = hubdi; 2067c478bd9Sstevel@tonic-gate 2077c478bd9Sstevel@tonic-gate /* 2087c478bd9Sstevel@tonic-gate * add this hubdi instance to the list of known hubdi's 2097c478bd9Sstevel@tonic-gate */ 2107c478bd9Sstevel@tonic-gate usba_init_list(&hubdi->hubdi_list, (usb_opaque_t)hubdi, 2117c478bd9Sstevel@tonic-gate usba_hcdi_get_hcdi(usba_device->usb_root_hub_dip)-> 2127c478bd9Sstevel@tonic-gate hcdi_iblock_cookie); 2137c478bd9Sstevel@tonic-gate mutex_enter(&usba_hubdi_mutex); 2147c478bd9Sstevel@tonic-gate usba_add_to_list(&usba_hubdi_list, &hubdi->hubdi_list); 2157c478bd9Sstevel@tonic-gate mutex_exit(&usba_hubdi_mutex); 2167c478bd9Sstevel@tonic-gate 2177c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 2187c478bd9Sstevel@tonic-gate } 2197c478bd9Sstevel@tonic-gate 2207c478bd9Sstevel@tonic-gate 2217c478bd9Sstevel@tonic-gate /* 2227c478bd9Sstevel@tonic-gate * Called by an HUB to detach an instance of the driver 2237c478bd9Sstevel@tonic-gate */ 224ff0e937bSRaymond Chen int 2257c478bd9Sstevel@tonic-gate usba_hubdi_unregister(dev_info_t *dip) 2267c478bd9Sstevel@tonic-gate { 2277c478bd9Sstevel@tonic-gate usba_device_t *usba_device = usba_get_usba_device(dip); 2287c478bd9Sstevel@tonic-gate usba_hubdi_t *hubdi = usba_device->usb_hubdi; 2297c478bd9Sstevel@tonic-gate 2307c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle, 2317c478bd9Sstevel@tonic-gate "usba_hubdi_unregister: %s", ddi_node_name(dip)); 2327c478bd9Sstevel@tonic-gate 2337c478bd9Sstevel@tonic-gate mutex_enter(&usba_hubdi_mutex); 2347c478bd9Sstevel@tonic-gate (void) usba_rm_from_list(&usba_hubdi_list, &hubdi->hubdi_list); 2357c478bd9Sstevel@tonic-gate mutex_exit(&usba_hubdi_mutex); 2367c478bd9Sstevel@tonic-gate 2377c478bd9Sstevel@tonic-gate usba_destroy_list(&hubdi->hubdi_list); 2387c478bd9Sstevel@tonic-gate 2397c478bd9Sstevel@tonic-gate kmem_free(hubdi, sizeof (usba_hubdi_t)); 2407c478bd9Sstevel@tonic-gate 2417c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 2427c478bd9Sstevel@tonic-gate } 2437c478bd9Sstevel@tonic-gate 2447c478bd9Sstevel@tonic-gate 2457c478bd9Sstevel@tonic-gate /* 2467c478bd9Sstevel@tonic-gate * misc bus routines currently not used 2477c478bd9Sstevel@tonic-gate */ 2487c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 2497c478bd9Sstevel@tonic-gate static int 2507c478bd9Sstevel@tonic-gate usba_hubdi_map_fault(dev_info_t *dip, 251*d96925c4SRichard Lowe dev_info_t *rdip, 252*d96925c4SRichard Lowe struct hat *hat, 253*d96925c4SRichard Lowe struct seg *seg, 254*d96925c4SRichard Lowe caddr_t addr, 255*d96925c4SRichard Lowe struct devpage *dp, 256*d96925c4SRichard Lowe pfn_t pfn, 257*d96925c4SRichard Lowe uint_t prot, 258*d96925c4SRichard Lowe uint_t lock) 2597c478bd9Sstevel@tonic-gate { 2607c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 2617c478bd9Sstevel@tonic-gate } 2627c478bd9Sstevel@tonic-gate 2637c478bd9Sstevel@tonic-gate 2647c478bd9Sstevel@tonic-gate /* 2657c478bd9Sstevel@tonic-gate * root hub support. the root hub uses the same devi as the HCD 2667c478bd9Sstevel@tonic-gate */ 2677c478bd9Sstevel@tonic-gate int 2687c478bd9Sstevel@tonic-gate usba_hubdi_bind_root_hub(dev_info_t *dip, 269*d96925c4SRichard Lowe uchar_t *root_hub_config_descriptor, 270*d96925c4SRichard Lowe size_t config_length, 271*d96925c4SRichard Lowe usb_dev_descr_t *root_hub_device_descriptor) 2727c478bd9Sstevel@tonic-gate { 2737c478bd9Sstevel@tonic-gate usba_device_t *usba_device; 2747c478bd9Sstevel@tonic-gate usba_hcdi_t *hcdi = usba_hcdi_get_hcdi(dip); 2757c478bd9Sstevel@tonic-gate hubd_t *root_hubd; 2767c478bd9Sstevel@tonic-gate usb_pipe_handle_t ph = NULL; 2777c478bd9Sstevel@tonic-gate dev_info_t *child = ddi_get_child(dip); 2787c478bd9Sstevel@tonic-gate 2797c478bd9Sstevel@tonic-gate if (ndi_prop_create_boolean(DDI_DEV_T_NONE, dip, 2807c478bd9Sstevel@tonic-gate "root-hub") != NDI_SUCCESS) { 2817c478bd9Sstevel@tonic-gate 2827c478bd9Sstevel@tonic-gate return (USB_FAILURE); 2837c478bd9Sstevel@tonic-gate } 2847c478bd9Sstevel@tonic-gate 285de6f998eSrui wang - Sun Microsystems - Beijing China usba_add_root_hub(dip); 286de6f998eSrui wang - Sun Microsystems - Beijing China 2877c478bd9Sstevel@tonic-gate root_hubd = kmem_zalloc(sizeof (hubd_t), KM_SLEEP); 2887c478bd9Sstevel@tonic-gate 2897c478bd9Sstevel@tonic-gate /* 2907c478bd9Sstevel@tonic-gate * create and initialize a usba_device structure 2917c478bd9Sstevel@tonic-gate */ 2927c478bd9Sstevel@tonic-gate usba_device = usba_alloc_usba_device(dip); 2937c478bd9Sstevel@tonic-gate 2947c478bd9Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex); 2957c478bd9Sstevel@tonic-gate usba_device->usb_hcdi_ops = hcdi->hcdi_ops; 2967c478bd9Sstevel@tonic-gate usba_device->usb_cfg = root_hub_config_descriptor; 2977c478bd9Sstevel@tonic-gate usba_device->usb_cfg_length = config_length; 2987c478bd9Sstevel@tonic-gate usba_device->usb_dev_descr = root_hub_device_descriptor; 2997c478bd9Sstevel@tonic-gate usba_device->usb_port = 1; 3007c478bd9Sstevel@tonic-gate usba_device->usb_addr = ROOT_HUB_ADDR; 3017c478bd9Sstevel@tonic-gate usba_device->usb_root_hubd = root_hubd; 3027c478bd9Sstevel@tonic-gate usba_device->usb_cfg_array = kmem_zalloc(sizeof (uchar_t *), 303c0f24e5bSlg KM_SLEEP); 3047c478bd9Sstevel@tonic-gate usba_device->usb_cfg_array_length = sizeof (uchar_t *); 3057c478bd9Sstevel@tonic-gate 3067c478bd9Sstevel@tonic-gate usba_device->usb_cfg_array_len = kmem_zalloc(sizeof (uint16_t), 307c0f24e5bSlg KM_SLEEP); 3087c478bd9Sstevel@tonic-gate usba_device->usb_cfg_array_len_length = sizeof (uint16_t); 3097c478bd9Sstevel@tonic-gate 3107c478bd9Sstevel@tonic-gate usba_device->usb_cfg_array[0] = root_hub_config_descriptor; 3117c478bd9Sstevel@tonic-gate usba_device->usb_cfg_array_len[0] = 312c0f24e5bSlg sizeof (root_hub_config_descriptor); 3137c478bd9Sstevel@tonic-gate 3147c478bd9Sstevel@tonic-gate usba_device->usb_cfg_str_descr = kmem_zalloc(sizeof (uchar_t *), 315c0f24e5bSlg KM_SLEEP); 3167c478bd9Sstevel@tonic-gate usba_device->usb_n_cfgs = 1; 3177c478bd9Sstevel@tonic-gate usba_device->usb_n_ifs = 1; 3187c478bd9Sstevel@tonic-gate usba_device->usb_dip = dip; 3197c478bd9Sstevel@tonic-gate 3207c478bd9Sstevel@tonic-gate usba_device->usb_client_flags = kmem_zalloc( 3217c478bd9Sstevel@tonic-gate usba_device->usb_n_ifs * USBA_CLIENT_FLAG_SIZE, KM_SLEEP); 3227c478bd9Sstevel@tonic-gate 3237c478bd9Sstevel@tonic-gate usba_device->usb_client_attach_list = kmem_zalloc( 3247c478bd9Sstevel@tonic-gate usba_device->usb_n_ifs * 3257c478bd9Sstevel@tonic-gate sizeof (*usba_device->usb_client_attach_list), KM_SLEEP); 3267c478bd9Sstevel@tonic-gate 3277c478bd9Sstevel@tonic-gate usba_device->usb_client_ev_cb_list = kmem_zalloc( 3287c478bd9Sstevel@tonic-gate usba_device->usb_n_ifs * 3297c478bd9Sstevel@tonic-gate sizeof (*usba_device->usb_client_ev_cb_list), KM_SLEEP); 3307c478bd9Sstevel@tonic-gate 3317c478bd9Sstevel@tonic-gate /* 3327c478bd9Sstevel@tonic-gate * The bDeviceProtocol field of root hub device specifies, 333993e3fafSRobert Mustacchi * whether root hub is a Super, High, or Full speed usb device. 3347c478bd9Sstevel@tonic-gate */ 335993e3fafSRobert Mustacchi if (root_hub_device_descriptor->bDeviceProtocol >= 0x3) { 336993e3fafSRobert Mustacchi usba_device->usb_port_status = USBA_SUPER_SPEED_DEV; 337993e3fafSRobert Mustacchi } else if (root_hub_device_descriptor->bDeviceProtocol > 0) { 3387c478bd9Sstevel@tonic-gate usba_device->usb_port_status = USBA_HIGH_SPEED_DEV; 3397c478bd9Sstevel@tonic-gate } else { 3407c478bd9Sstevel@tonic-gate usba_device->usb_port_status = USBA_FULL_SPEED_DEV; 3417c478bd9Sstevel@tonic-gate } 3427c478bd9Sstevel@tonic-gate 3437c478bd9Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex); 3447c478bd9Sstevel@tonic-gate 3457c478bd9Sstevel@tonic-gate usba_set_usba_device(dip, usba_device); 3467c478bd9Sstevel@tonic-gate 3477c478bd9Sstevel@tonic-gate /* 3487c478bd9Sstevel@tonic-gate * For the root hub the default pipe is not yet open 3497c478bd9Sstevel@tonic-gate */ 3507c478bd9Sstevel@tonic-gate if (usb_pipe_open(dip, NULL, NULL, 3517c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph) != USB_SUCCESS) { 3527c478bd9Sstevel@tonic-gate goto fail; 3537c478bd9Sstevel@tonic-gate } 3547c478bd9Sstevel@tonic-gate 3557c478bd9Sstevel@tonic-gate /* 3567c478bd9Sstevel@tonic-gate * kill off all OBP children, they may not be fully 3577c478bd9Sstevel@tonic-gate * enumerated 3587c478bd9Sstevel@tonic-gate */ 3597c478bd9Sstevel@tonic-gate while (child) { 3607c478bd9Sstevel@tonic-gate dev_info_t *next = ddi_get_next_sibling(child); 3617c478bd9Sstevel@tonic-gate (void) ddi_remove_child(child, 0); 3627c478bd9Sstevel@tonic-gate child = next; 3637c478bd9Sstevel@tonic-gate } 3647c478bd9Sstevel@tonic-gate 3657c478bd9Sstevel@tonic-gate /* 3667c478bd9Sstevel@tonic-gate * "attach" the root hub driver 3677c478bd9Sstevel@tonic-gate */ 3687c478bd9Sstevel@tonic-gate if (usba_hubdi_attach(dip, DDI_ATTACH) != DDI_SUCCESS) { 3697c478bd9Sstevel@tonic-gate goto fail; 3707c478bd9Sstevel@tonic-gate } 3717c478bd9Sstevel@tonic-gate 3727c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 3737c478bd9Sstevel@tonic-gate 3747c478bd9Sstevel@tonic-gate fail: 3757c478bd9Sstevel@tonic-gate if (ph) { 3767c478bd9Sstevel@tonic-gate usb_pipe_close(dip, ph, 3777c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL); 3787c478bd9Sstevel@tonic-gate } 3797c478bd9Sstevel@tonic-gate 3807c478bd9Sstevel@tonic-gate kmem_free(usba_device->usb_cfg_array, 381c0f24e5bSlg usba_device->usb_cfg_array_length); 3827c478bd9Sstevel@tonic-gate kmem_free(usba_device->usb_cfg_array_len, 383c0f24e5bSlg usba_device->usb_cfg_array_len_length); 3847c478bd9Sstevel@tonic-gate 3857c478bd9Sstevel@tonic-gate kmem_free(usba_device->usb_cfg_str_descr, sizeof (uchar_t *)); 3867c478bd9Sstevel@tonic-gate 3877c478bd9Sstevel@tonic-gate usba_free_usba_device(usba_device); 3887c478bd9Sstevel@tonic-gate 3897c478bd9Sstevel@tonic-gate usba_set_usba_device(dip, NULL); 390993e3fafSRobert Mustacchi 3917c478bd9Sstevel@tonic-gate if (root_hubd) { 3927c478bd9Sstevel@tonic-gate kmem_free(root_hubd, sizeof (hubd_t)); 3937c478bd9Sstevel@tonic-gate } 3947c478bd9Sstevel@tonic-gate 395993e3fafSRobert Mustacchi (void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "root-hub"); 396993e3fafSRobert Mustacchi 397993e3fafSRobert Mustacchi usba_rem_root_hub(dip); 398993e3fafSRobert Mustacchi 3997c478bd9Sstevel@tonic-gate return (USB_FAILURE); 4007c478bd9Sstevel@tonic-gate } 4017c478bd9Sstevel@tonic-gate 4027c478bd9Sstevel@tonic-gate 4037c478bd9Sstevel@tonic-gate int 4047c478bd9Sstevel@tonic-gate usba_hubdi_unbind_root_hub(dev_info_t *dip) 4057c478bd9Sstevel@tonic-gate { 4067c478bd9Sstevel@tonic-gate usba_device_t *usba_device; 4077c478bd9Sstevel@tonic-gate 4087c478bd9Sstevel@tonic-gate /* was root hub attached? */ 4097c478bd9Sstevel@tonic-gate if (!(usba_is_root_hub(dip))) { 4107c478bd9Sstevel@tonic-gate 4117c478bd9Sstevel@tonic-gate /* return success anyway */ 4127c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 4137c478bd9Sstevel@tonic-gate } 4147c478bd9Sstevel@tonic-gate 4157c478bd9Sstevel@tonic-gate /* 4167c478bd9Sstevel@tonic-gate * usba_hubdi_detach also closes the default pipe 4177c478bd9Sstevel@tonic-gate * and removes properties so there is no need to 4187c478bd9Sstevel@tonic-gate * do it here 4197c478bd9Sstevel@tonic-gate */ 4207c478bd9Sstevel@tonic-gate if (usba_hubdi_detach(dip, DDI_DETACH) != DDI_SUCCESS) { 4217c478bd9Sstevel@tonic-gate 4227c478bd9Sstevel@tonic-gate if (DEVI_IS_ATTACHING(dip)) { 423d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle, 4247c478bd9Sstevel@tonic-gate "failure to unbind root hub after attach failure"); 4257c478bd9Sstevel@tonic-gate } 4267c478bd9Sstevel@tonic-gate 4277c478bd9Sstevel@tonic-gate return (USB_FAILURE); 4287c478bd9Sstevel@tonic-gate } 4297c478bd9Sstevel@tonic-gate 4307c478bd9Sstevel@tonic-gate usba_device = usba_get_usba_device(dip); 4317c478bd9Sstevel@tonic-gate 4327c478bd9Sstevel@tonic-gate kmem_free(usba_device->usb_root_hubd, sizeof (hubd_t)); 4337c478bd9Sstevel@tonic-gate 4347c478bd9Sstevel@tonic-gate kmem_free(usba_device->usb_cfg_array, 435c0f24e5bSlg usba_device->usb_cfg_array_length); 4367c478bd9Sstevel@tonic-gate kmem_free(usba_device->usb_cfg_array_len, 437c0f24e5bSlg usba_device->usb_cfg_array_len_length); 4387c478bd9Sstevel@tonic-gate 4397c478bd9Sstevel@tonic-gate kmem_free(usba_device->usb_cfg_str_descr, sizeof (uchar_t *)); 4407c478bd9Sstevel@tonic-gate 4417c478bd9Sstevel@tonic-gate usba_free_usba_device(usba_device); 4427c478bd9Sstevel@tonic-gate 443de6f998eSrui wang - Sun Microsystems - Beijing China usba_rem_root_hub(dip); 444de6f998eSrui wang - Sun Microsystems - Beijing China 4457c478bd9Sstevel@tonic-gate (void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "root-hub"); 4467c478bd9Sstevel@tonic-gate 4477c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 4487c478bd9Sstevel@tonic-gate } 4497c478bd9Sstevel@tonic-gate 4507c478bd9Sstevel@tonic-gate 4517c478bd9Sstevel@tonic-gate /* 4527c478bd9Sstevel@tonic-gate * Actual Hub Driver support code: 4537c478bd9Sstevel@tonic-gate * shared by root hub and non-root hubs 4547c478bd9Sstevel@tonic-gate */ 4557c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usbai_version.h> 4567c478bd9Sstevel@tonic-gate 4577c478bd9Sstevel@tonic-gate /* Debugging support */ 458ff0e937bSRaymond Chen uint_t hubd_errlevel = USB_LOG_L4; 459ff0e937bSRaymond Chen uint_t hubd_errmask = (uint_t)DPRINT_MASK_ALL; 460ff0e937bSRaymond Chen uint_t hubd_instance_debug = (uint_t)-1; 4617c478bd9Sstevel@tonic-gate static uint_t hubdi_bus_config_debug = 0; 4627c478bd9Sstevel@tonic-gate 4637c478bd9Sstevel@tonic-gate _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_errlevel)) 4647c478bd9Sstevel@tonic-gate _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_errmask)) 4657c478bd9Sstevel@tonic-gate _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_instance_debug)) 4667c478bd9Sstevel@tonic-gate 4677c478bd9Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique", msgb)) 4687c478bd9Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique", dev_info)) 4697c478bd9Sstevel@tonic-gate 4707c478bd9Sstevel@tonic-gate 4717c478bd9Sstevel@tonic-gate /* 4727c478bd9Sstevel@tonic-gate * local variables: 4737c478bd9Sstevel@tonic-gate * 4747c478bd9Sstevel@tonic-gate * Amount of time to wait between resetting the port and accessing 4757c478bd9Sstevel@tonic-gate * the device. The value is in microseconds. 4767c478bd9Sstevel@tonic-gate */ 4777c478bd9Sstevel@tonic-gate static uint_t hubd_device_delay = 1000000; 4787c478bd9Sstevel@tonic-gate 4797c478bd9Sstevel@tonic-gate /* 4807c478bd9Sstevel@tonic-gate * enumeration retry 4817c478bd9Sstevel@tonic-gate */ 4827c478bd9Sstevel@tonic-gate #define HUBD_PORT_RETRY 5 4837c478bd9Sstevel@tonic-gate static uint_t hubd_retry_enumerate = HUBD_PORT_RETRY; 4847c478bd9Sstevel@tonic-gate 4857c478bd9Sstevel@tonic-gate /* 4867c478bd9Sstevel@tonic-gate * Stale hotremoved device cleanup delay 4877c478bd9Sstevel@tonic-gate */ 4887c478bd9Sstevel@tonic-gate #define HUBD_STALE_DIP_CLEANUP_DELAY 5000000 4897c478bd9Sstevel@tonic-gate static uint_t hubd_dip_cleanup_delay = HUBD_STALE_DIP_CLEANUP_DELAY; 4907c478bd9Sstevel@tonic-gate 4917c478bd9Sstevel@tonic-gate /* 4927c478bd9Sstevel@tonic-gate * retries for USB suspend and resume 4937c478bd9Sstevel@tonic-gate */ 4947c478bd9Sstevel@tonic-gate #define HUBD_SUS_RES_RETRY 2 4957c478bd9Sstevel@tonic-gate 4967c478bd9Sstevel@tonic-gate void *hubd_statep; 4977c478bd9Sstevel@tonic-gate 4987c478bd9Sstevel@tonic-gate /* 4997c478bd9Sstevel@tonic-gate * prototypes 5007c478bd9Sstevel@tonic-gate */ 5017c478bd9Sstevel@tonic-gate static int hubd_cleanup(dev_info_t *dip, hubd_t *hubd); 5027c478bd9Sstevel@tonic-gate static int hubd_check_ports(hubd_t *hubd); 5037c478bd9Sstevel@tonic-gate 5047c478bd9Sstevel@tonic-gate static int hubd_open_intr_pipe(hubd_t *hubd); 5057c478bd9Sstevel@tonic-gate static void hubd_start_polling(hubd_t *hubd, int always); 5067c478bd9Sstevel@tonic-gate static void hubd_stop_polling(hubd_t *hubd); 5077c478bd9Sstevel@tonic-gate static void hubd_close_intr_pipe(hubd_t *hubd); 5087c478bd9Sstevel@tonic-gate 5097c478bd9Sstevel@tonic-gate static void hubd_read_cb(usb_pipe_handle_t pipe, usb_intr_req_t *req); 5107c478bd9Sstevel@tonic-gate static void hubd_exception_cb(usb_pipe_handle_t pipe, 5117c478bd9Sstevel@tonic-gate usb_intr_req_t *req); 5127c478bd9Sstevel@tonic-gate static void hubd_hotplug_thread(void *arg); 513ffcd51f3Slg static void hubd_reset_thread(void *arg); 5147c478bd9Sstevel@tonic-gate static int hubd_create_child(dev_info_t *dip, 5157c478bd9Sstevel@tonic-gate hubd_t *hubd, 5167c478bd9Sstevel@tonic-gate usba_device_t *usba_device, 5177c478bd9Sstevel@tonic-gate usb_port_status_t port_status, 5187c478bd9Sstevel@tonic-gate usb_port_t port, 5197c478bd9Sstevel@tonic-gate int iteration); 5207c478bd9Sstevel@tonic-gate 5217c478bd9Sstevel@tonic-gate static int hubd_delete_child(hubd_t *hubd, usb_port_t port, uint_t flag, 5227c478bd9Sstevel@tonic-gate boolean_t retry); 5237c478bd9Sstevel@tonic-gate 5247c478bd9Sstevel@tonic-gate static int hubd_get_hub_descriptor(hubd_t *hubd); 5257c478bd9Sstevel@tonic-gate 526993e3fafSRobert Mustacchi static int hubd_set_hub_depth(hubd_t *hubd); 527993e3fafSRobert Mustacchi 52835f36846Ssl static int hubd_get_hub_status_words(hubd_t *hubd, uint16_t *status); 52935f36846Ssl 5307c478bd9Sstevel@tonic-gate static int hubd_reset_port(hubd_t *hubd, usb_port_t port); 5317c478bd9Sstevel@tonic-gate 5327c478bd9Sstevel@tonic-gate static int hubd_get_hub_status(hubd_t *hubd); 5337c478bd9Sstevel@tonic-gate 5347c478bd9Sstevel@tonic-gate static int hubd_handle_port_connect(hubd_t *hubd, usb_port_t port); 5357c478bd9Sstevel@tonic-gate 5367c478bd9Sstevel@tonic-gate static int hubd_disable_port(hubd_t *hubd, usb_port_t port); 5377c478bd9Sstevel@tonic-gate 5387c478bd9Sstevel@tonic-gate static int hubd_enable_port(hubd_t *hubd, usb_port_t port); 5397c478bd9Sstevel@tonic-gate static int hubd_recover_disabled_port(hubd_t *hubd, usb_port_t port); 5407c478bd9Sstevel@tonic-gate 5417c478bd9Sstevel@tonic-gate static int hubd_determine_port_status(hubd_t *hubd, usb_port_t port, 542993e3fafSRobert Mustacchi uint16_t *status, uint16_t *change, usb_port_status_t *speed, 543993e3fafSRobert Mustacchi uint_t ack_flag); 5447c478bd9Sstevel@tonic-gate 5457c478bd9Sstevel@tonic-gate static int hubd_enable_all_port_power(hubd_t *hubd); 5467c478bd9Sstevel@tonic-gate static int hubd_disable_all_port_power(hubd_t *hubd); 5477c478bd9Sstevel@tonic-gate static int hubd_disable_port_power(hubd_t *hubd, usb_port_t port); 5487c478bd9Sstevel@tonic-gate static int hubd_enable_port_power(hubd_t *hubd, usb_port_t port); 5497c478bd9Sstevel@tonic-gate 5507c478bd9Sstevel@tonic-gate static void hubd_free_usba_device(hubd_t *hubd, usba_device_t *usba_device); 5517c478bd9Sstevel@tonic-gate 5527c478bd9Sstevel@tonic-gate static int hubd_can_suspend(hubd_t *hubd); 5537c478bd9Sstevel@tonic-gate static void hubd_restore_device_state(dev_info_t *dip, hubd_t *hubd); 5547c478bd9Sstevel@tonic-gate static int hubd_setdevaddr(hubd_t *hubd, usb_port_t port); 5557c478bd9Sstevel@tonic-gate static void hubd_setdevconfig(hubd_t *hubd, usb_port_t port); 5567c478bd9Sstevel@tonic-gate 5577c478bd9Sstevel@tonic-gate static int hubd_register_events(hubd_t *hubd); 5587c478bd9Sstevel@tonic-gate static void hubd_do_callback(hubd_t *hubd, dev_info_t *dip, 5597c478bd9Sstevel@tonic-gate ddi_eventcookie_t cookie); 5607c478bd9Sstevel@tonic-gate static void hubd_run_callbacks(hubd_t *hubd, usba_event_t type); 5617c478bd9Sstevel@tonic-gate static void hubd_post_event(hubd_t *hubd, usb_port_t port, usba_event_t type); 5627c478bd9Sstevel@tonic-gate static void hubd_create_pm_components(dev_info_t *dip, hubd_t *hubd); 5637c478bd9Sstevel@tonic-gate 5647c478bd9Sstevel@tonic-gate static int hubd_disconnect_event_cb(dev_info_t *dip); 5657c478bd9Sstevel@tonic-gate static int hubd_reconnect_event_cb(dev_info_t *dip); 5667c478bd9Sstevel@tonic-gate static int hubd_pre_suspend_event_cb(dev_info_t *dip); 5677c478bd9Sstevel@tonic-gate static int hubd_post_resume_event_cb(dev_info_t *dip); 5687c478bd9Sstevel@tonic-gate static int hubd_cpr_suspend(hubd_t *hubd); 5697c478bd9Sstevel@tonic-gate static void hubd_cpr_resume(dev_info_t *dip); 5707c478bd9Sstevel@tonic-gate static int hubd_restore_state_cb(dev_info_t *dip); 571ffcd51f3Slg static int hubd_check_same_device(hubd_t *hubd, usb_port_t port); 5727c478bd9Sstevel@tonic-gate 57335f36846Ssl static int hubd_init_power_budget(hubd_t *hubd); 57435f36846Ssl 5757c478bd9Sstevel@tonic-gate static ndi_event_definition_t hubd_ndi_event_defs[] = { 5767c478bd9Sstevel@tonic-gate {USBA_EVENT_TAG_HOT_REMOVAL, DDI_DEVI_REMOVE_EVENT, EPL_KERNEL, 5777c478bd9Sstevel@tonic-gate NDI_EVENT_POST_TO_ALL}, 5787c478bd9Sstevel@tonic-gate {USBA_EVENT_TAG_HOT_INSERTION, DDI_DEVI_INSERT_EVENT, EPL_KERNEL, 5797c478bd9Sstevel@tonic-gate NDI_EVENT_POST_TO_ALL}, 5807c478bd9Sstevel@tonic-gate {USBA_EVENT_TAG_POST_RESUME, USBA_POST_RESUME_EVENT, EPL_KERNEL, 5817c478bd9Sstevel@tonic-gate NDI_EVENT_POST_TO_ALL}, 5827c478bd9Sstevel@tonic-gate {USBA_EVENT_TAG_PRE_SUSPEND, USBA_PRE_SUSPEND_EVENT, EPL_KERNEL, 5837c478bd9Sstevel@tonic-gate NDI_EVENT_POST_TO_ALL} 5847c478bd9Sstevel@tonic-gate }; 5857c478bd9Sstevel@tonic-gate 5867c478bd9Sstevel@tonic-gate #define HUBD_N_NDI_EVENTS \ 5877c478bd9Sstevel@tonic-gate (sizeof (hubd_ndi_event_defs) / sizeof (ndi_event_definition_t)) 5887c478bd9Sstevel@tonic-gate 5897c478bd9Sstevel@tonic-gate static ndi_event_set_t hubd_ndi_events = { 5907c478bd9Sstevel@tonic-gate NDI_EVENTS_REV1, HUBD_N_NDI_EVENTS, hubd_ndi_event_defs}; 5917c478bd9Sstevel@tonic-gate 5927c478bd9Sstevel@tonic-gate /* events received from parent */ 5937c478bd9Sstevel@tonic-gate static usb_event_t hubd_events = { 5947c478bd9Sstevel@tonic-gate hubd_disconnect_event_cb, 5957c478bd9Sstevel@tonic-gate hubd_reconnect_event_cb, 5967c478bd9Sstevel@tonic-gate hubd_pre_suspend_event_cb, 5977c478bd9Sstevel@tonic-gate hubd_post_resume_event_cb 5987c478bd9Sstevel@tonic-gate }; 5997c478bd9Sstevel@tonic-gate 6007c478bd9Sstevel@tonic-gate 6017c478bd9Sstevel@tonic-gate /* 6027c478bd9Sstevel@tonic-gate * hubd_get_soft_state() returns the hubd soft state 6037c478bd9Sstevel@tonic-gate */ 604ff0e937bSRaymond Chen hubd_t * 6057c478bd9Sstevel@tonic-gate hubd_get_soft_state(dev_info_t *dip) 6067c478bd9Sstevel@tonic-gate { 6077c478bd9Sstevel@tonic-gate if (dip == NULL) { 6087c478bd9Sstevel@tonic-gate return (NULL); 6097c478bd9Sstevel@tonic-gate } 6107c478bd9Sstevel@tonic-gate 611e2c88f0cSGarrett D'Amore if (usba_is_root_hub(dip)) { 6127c478bd9Sstevel@tonic-gate usba_device_t *usba_device = usba_get_usba_device(dip); 6137c478bd9Sstevel@tonic-gate 6147c478bd9Sstevel@tonic-gate return (usba_device->usb_root_hubd); 6157c478bd9Sstevel@tonic-gate } else { 6167c478bd9Sstevel@tonic-gate int instance = ddi_get_instance(dip); 6177c478bd9Sstevel@tonic-gate 6187c478bd9Sstevel@tonic-gate return (ddi_get_soft_state(hubd_statep, instance)); 6197c478bd9Sstevel@tonic-gate } 6207c478bd9Sstevel@tonic-gate } 6217c478bd9Sstevel@tonic-gate 6227c478bd9Sstevel@tonic-gate 6237c478bd9Sstevel@tonic-gate /* 6247c478bd9Sstevel@tonic-gate * PM support functions: 6257c478bd9Sstevel@tonic-gate */ 6267c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 6277c478bd9Sstevel@tonic-gate static void 6287c478bd9Sstevel@tonic-gate hubd_pm_busy_component(hubd_t *hubd, dev_info_t *dip, int component) 6297c478bd9Sstevel@tonic-gate { 6307c478bd9Sstevel@tonic-gate if (hubd->h_hubpm != NULL) { 6317c478bd9Sstevel@tonic-gate hubd->h_hubpm->hubp_busy_pm++; 6327c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6337c478bd9Sstevel@tonic-gate if (pm_busy_component(dip, 0) != DDI_SUCCESS) { 6347c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6357c478bd9Sstevel@tonic-gate hubd->h_hubpm->hubp_busy_pm--; 6367c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6377c478bd9Sstevel@tonic-gate } 6387c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6397c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 6407c478bd9Sstevel@tonic-gate "hubd_pm_busy_component: %d", hubd->h_hubpm->hubp_busy_pm); 6417c478bd9Sstevel@tonic-gate } 6427c478bd9Sstevel@tonic-gate } 6437c478bd9Sstevel@tonic-gate 6447c478bd9Sstevel@tonic-gate 6457c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 6467c478bd9Sstevel@tonic-gate static void 6477c478bd9Sstevel@tonic-gate hubd_pm_idle_component(hubd_t *hubd, dev_info_t *dip, int component) 6487c478bd9Sstevel@tonic-gate { 6497c478bd9Sstevel@tonic-gate if (hubd->h_hubpm != NULL) { 6507c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6517c478bd9Sstevel@tonic-gate if (pm_idle_component(dip, 0) == DDI_SUCCESS) { 6527c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6537c478bd9Sstevel@tonic-gate ASSERT(hubd->h_hubpm->hubp_busy_pm > 0); 6547c478bd9Sstevel@tonic-gate hubd->h_hubpm->hubp_busy_pm--; 6557c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6567c478bd9Sstevel@tonic-gate } 6577c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6587c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 6597c478bd9Sstevel@tonic-gate "hubd_pm_idle_component: %d", hubd->h_hubpm->hubp_busy_pm); 6607c478bd9Sstevel@tonic-gate } 6617c478bd9Sstevel@tonic-gate } 6627c478bd9Sstevel@tonic-gate 6637c478bd9Sstevel@tonic-gate 6647c478bd9Sstevel@tonic-gate /* 6657c478bd9Sstevel@tonic-gate * track power level changes for children of this instance 6667c478bd9Sstevel@tonic-gate */ 6677c478bd9Sstevel@tonic-gate static void 6687c478bd9Sstevel@tonic-gate hubd_set_child_pwrlvl(hubd_t *hubd, usb_port_t port, uint8_t power) 6697c478bd9Sstevel@tonic-gate { 6707c478bd9Sstevel@tonic-gate int old_power, new_power, pwr; 6717c478bd9Sstevel@tonic-gate usb_port_t portno; 6727c478bd9Sstevel@tonic-gate hub_power_t *hubpm; 6737c478bd9Sstevel@tonic-gate 6747c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 6757c478bd9Sstevel@tonic-gate "hubd_set_child_pwrlvl: port=%d power=%d", 6767c478bd9Sstevel@tonic-gate port, power); 6777c478bd9Sstevel@tonic-gate 6787c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6797c478bd9Sstevel@tonic-gate hubpm = hubd->h_hubpm; 6807c478bd9Sstevel@tonic-gate 6817c478bd9Sstevel@tonic-gate old_power = 0; 682993e3fafSRobert Mustacchi for (portno = 1; portno <= hubd->h_nports; portno++) { 6837c478bd9Sstevel@tonic-gate old_power += hubpm->hubp_child_pwrstate[portno]; 6847c478bd9Sstevel@tonic-gate } 6857c478bd9Sstevel@tonic-gate 6867c478bd9Sstevel@tonic-gate /* assign the port power */ 6877c478bd9Sstevel@tonic-gate pwr = hubd->h_hubpm->hubp_child_pwrstate[port]; 6887c478bd9Sstevel@tonic-gate hubd->h_hubpm->hubp_child_pwrstate[port] = power; 6897c478bd9Sstevel@tonic-gate new_power = old_power - pwr + power; 6907c478bd9Sstevel@tonic-gate 6917c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 6927c478bd9Sstevel@tonic-gate "hubd_set_child_pwrlvl: new_power=%d old_power=%d", 6937c478bd9Sstevel@tonic-gate new_power, old_power); 6947c478bd9Sstevel@tonic-gate 6957c478bd9Sstevel@tonic-gate if ((new_power > 0) && (old_power == 0)) { 6967c478bd9Sstevel@tonic-gate /* we have the first child coming out of low power */ 6977c478bd9Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0); 6987c478bd9Sstevel@tonic-gate } else if ((new_power == 0) && (old_power > 0)) { 6997c478bd9Sstevel@tonic-gate /* we have the last child going to low power */ 7007c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0); 7017c478bd9Sstevel@tonic-gate } 7027c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 7037c478bd9Sstevel@tonic-gate } 7047c478bd9Sstevel@tonic-gate 7057c478bd9Sstevel@tonic-gate 7067c478bd9Sstevel@tonic-gate /* 7077c478bd9Sstevel@tonic-gate * given a child dip, locate its port number 7087c478bd9Sstevel@tonic-gate */ 7097c478bd9Sstevel@tonic-gate static usb_port_t 7107c478bd9Sstevel@tonic-gate hubd_child_dip2port(hubd_t *hubd, dev_info_t *dip) 7117c478bd9Sstevel@tonic-gate { 7127c478bd9Sstevel@tonic-gate usb_port_t port; 7137c478bd9Sstevel@tonic-gate 7147c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 715993e3fafSRobert Mustacchi for (port = 1; port <= hubd->h_nports; port++) { 7167c478bd9Sstevel@tonic-gate if (hubd->h_children_dips[port] == dip) { 7177c478bd9Sstevel@tonic-gate 7187c478bd9Sstevel@tonic-gate break; 7197c478bd9Sstevel@tonic-gate } 7207c478bd9Sstevel@tonic-gate } 721993e3fafSRobert Mustacchi ASSERT(port <= hubd->h_nports); 7227c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 7237c478bd9Sstevel@tonic-gate 7247c478bd9Sstevel@tonic-gate return (port); 7257c478bd9Sstevel@tonic-gate } 7267c478bd9Sstevel@tonic-gate 7277c478bd9Sstevel@tonic-gate 7287c478bd9Sstevel@tonic-gate /* 7297c478bd9Sstevel@tonic-gate * if the hub can be put into low power mode, return success 7307c478bd9Sstevel@tonic-gate * NOTE: suspend here means going to lower power, not CPR suspend. 7317c478bd9Sstevel@tonic-gate */ 7327c478bd9Sstevel@tonic-gate static int 7337c478bd9Sstevel@tonic-gate hubd_can_suspend(hubd_t *hubd) 7347c478bd9Sstevel@tonic-gate { 7357c478bd9Sstevel@tonic-gate hub_power_t *hubpm; 7367c478bd9Sstevel@tonic-gate int total_power = 0; 7377c478bd9Sstevel@tonic-gate usb_port_t port; 7387c478bd9Sstevel@tonic-gate 7397c478bd9Sstevel@tonic-gate hubpm = hubd->h_hubpm; 7407c478bd9Sstevel@tonic-gate 7417c478bd9Sstevel@tonic-gate if (DEVI_IS_DETACHING(hubd->h_dip)) { 7427c478bd9Sstevel@tonic-gate 7437c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 7447c478bd9Sstevel@tonic-gate } 7457c478bd9Sstevel@tonic-gate 7467c478bd9Sstevel@tonic-gate /* 7477c478bd9Sstevel@tonic-gate * Don't go to lower power if haven't been at full power for enough 7487c478bd9Sstevel@tonic-gate * time to let hotplug thread kickoff. 7497c478bd9Sstevel@tonic-gate */ 750e5815e7aSJosef 'Jeff' Sipek if (gethrtime() < (hubpm->hubp_time_at_full_power + 7517c478bd9Sstevel@tonic-gate hubpm->hubp_min_pm_threshold)) { 7527c478bd9Sstevel@tonic-gate 7537c478bd9Sstevel@tonic-gate return (USB_FAILURE); 7547c478bd9Sstevel@tonic-gate } 7557c478bd9Sstevel@tonic-gate 7567c478bd9Sstevel@tonic-gate for (port = 1; (total_power == 0) && 757993e3fafSRobert Mustacchi (port <= hubd->h_nports); port++) { 7587c478bd9Sstevel@tonic-gate total_power += hubpm->hubp_child_pwrstate[port]; 7597c478bd9Sstevel@tonic-gate } 7607c478bd9Sstevel@tonic-gate 7617c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 762c0f24e5bSlg "hubd_can_suspend: %d", total_power); 7637c478bd9Sstevel@tonic-gate 7647c478bd9Sstevel@tonic-gate return (total_power ? USB_FAILURE : USB_SUCCESS); 7657c478bd9Sstevel@tonic-gate } 7667c478bd9Sstevel@tonic-gate 7677c478bd9Sstevel@tonic-gate 7687c478bd9Sstevel@tonic-gate /* 7697c478bd9Sstevel@tonic-gate * resume port depending on current device state 7707c478bd9Sstevel@tonic-gate */ 7717c478bd9Sstevel@tonic-gate static int 7727c478bd9Sstevel@tonic-gate hubd_resume_port(hubd_t *hubd, usb_port_t port) 7737c478bd9Sstevel@tonic-gate { 7747c478bd9Sstevel@tonic-gate int rval, retry; 7757c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 7767c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 7777c478bd9Sstevel@tonic-gate uint16_t status; 7787c478bd9Sstevel@tonic-gate uint16_t change; 7797c478bd9Sstevel@tonic-gate int retval = USB_FAILURE; 7807c478bd9Sstevel@tonic-gate 7817c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 7827c478bd9Sstevel@tonic-gate 7837c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 7847c478bd9Sstevel@tonic-gate "hubd_resume_port: port=%d state=0x%x (%s)", port, 7857c478bd9Sstevel@tonic-gate hubd->h_dev_state, usb_str_dev_state(hubd->h_dev_state)); 7867c478bd9Sstevel@tonic-gate 7877c478bd9Sstevel@tonic-gate switch (hubd->h_dev_state) { 7887c478bd9Sstevel@tonic-gate case USB_DEV_HUB_CHILD_PWRLVL: 7897c478bd9Sstevel@tonic-gate /* 7907c478bd9Sstevel@tonic-gate * This could be a bus ctl for a port other than the one 7917c478bd9Sstevel@tonic-gate * that has a remote wakeup condition. So check. 7927c478bd9Sstevel@tonic-gate */ 7937c478bd9Sstevel@tonic-gate if ((hubd->h_port_state[port] & PORT_STATUS_PSS) == 0) { 7947c478bd9Sstevel@tonic-gate /* the port isn't suspended, so don't resume */ 7957c478bd9Sstevel@tonic-gate retval = USB_SUCCESS; 7967c478bd9Sstevel@tonic-gate 7977c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 7987c478bd9Sstevel@tonic-gate "hubd_resume_port: port=%d not suspended", port); 7997c478bd9Sstevel@tonic-gate 8007c478bd9Sstevel@tonic-gate break; 8017c478bd9Sstevel@tonic-gate } 8027c478bd9Sstevel@tonic-gate /* 8037c478bd9Sstevel@tonic-gate * Device has initiated a wakeup. 8047c478bd9Sstevel@tonic-gate * Issue a ClearFeature(PortSuspend) 8057c478bd9Sstevel@tonic-gate */ 8067c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 8077c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 8087c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 80935f36846Ssl HUB_HANDLE_PORT_FEATURE_TYPE, 8107c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 8117c478bd9Sstevel@tonic-gate CFS_PORT_SUSPEND, 8127c478bd9Sstevel@tonic-gate port, 8137c478bd9Sstevel@tonic-gate 0, NULL, 0, 8147c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 8157c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 8167c478bd9Sstevel@tonic-gate "ClearFeature(PortSuspend) fails " 8177c478bd9Sstevel@tonic-gate "rval=%d cr=%d cb=0x%x", rval, 8187c478bd9Sstevel@tonic-gate completion_reason, cb_flags); 8197c478bd9Sstevel@tonic-gate } 8207c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 8217c478bd9Sstevel@tonic-gate 8227c478bd9Sstevel@tonic-gate /* either way ack changes on the port */ 8237c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 824993e3fafSRobert Mustacchi &status, &change, NULL, PORT_CHANGE_PSSC); 8257c478bd9Sstevel@tonic-gate retval = USB_SUCCESS; 8267c478bd9Sstevel@tonic-gate 8277c478bd9Sstevel@tonic-gate break; 8287c478bd9Sstevel@tonic-gate case USB_DEV_HUB_STATE_RECOVER: 8297c478bd9Sstevel@tonic-gate /* 8307c478bd9Sstevel@tonic-gate * When hubd's connect event callback posts a connect 8317c478bd9Sstevel@tonic-gate * event to its child, it results in this busctl call 8327c478bd9Sstevel@tonic-gate * which is valid 8337c478bd9Sstevel@tonic-gate */ 8347c478bd9Sstevel@tonic-gate /* FALLTHRU */ 8357c478bd9Sstevel@tonic-gate case USB_DEV_ONLINE: 8366c7181fcSsl if (((hubd->h_port_state[port] & PORT_STATUS_CCS) == 0) || 8376c7181fcSsl ((hubd->h_port_state[port] & PORT_STATUS_PSS) == 0)) { 8387c478bd9Sstevel@tonic-gate /* 8397c478bd9Sstevel@tonic-gate * the port isn't suspended, or connected 8407c478bd9Sstevel@tonic-gate * so don't resume 8417c478bd9Sstevel@tonic-gate */ 8427c478bd9Sstevel@tonic-gate retval = USB_SUCCESS; 8437c478bd9Sstevel@tonic-gate 8447c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 8457c478bd9Sstevel@tonic-gate "hubd_resume_port: port=%d not suspended", port); 8467c478bd9Sstevel@tonic-gate 8477c478bd9Sstevel@tonic-gate break; 8487c478bd9Sstevel@tonic-gate } 8497c478bd9Sstevel@tonic-gate /* 8507c478bd9Sstevel@tonic-gate * prevent kicking off the hotplug thread 8517c478bd9Sstevel@tonic-gate */ 8527c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread++; 8537c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd); 8547c478bd9Sstevel@tonic-gate 8557c478bd9Sstevel@tonic-gate /* Now ClearFeature(PortSuspend) */ 8567c478bd9Sstevel@tonic-gate for (retry = 0; retry < HUBD_SUS_RES_RETRY; retry++) { 8577c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 8587c478bd9Sstevel@tonic-gate rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 8597c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 86035f36846Ssl HUB_HANDLE_PORT_FEATURE_TYPE, 8617c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 8627c478bd9Sstevel@tonic-gate CFS_PORT_SUSPEND, 8637c478bd9Sstevel@tonic-gate port, 8647c478bd9Sstevel@tonic-gate 0, NULL, 0, 8657c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0); 8667c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 8677c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 8687c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, 8697c478bd9Sstevel@tonic-gate hubd->h_log_handle, 8707c478bd9Sstevel@tonic-gate "ClearFeature(PortSuspend) fails" 8717c478bd9Sstevel@tonic-gate "rval=%d cr=%d cb=0x%x", rval, 8727c478bd9Sstevel@tonic-gate completion_reason, cb_flags); 8737c478bd9Sstevel@tonic-gate } else { 8747c478bd9Sstevel@tonic-gate /* 8757c478bd9Sstevel@tonic-gate * As per spec section 11.9 and 7.1.7.7 8767c478bd9Sstevel@tonic-gate * hub need to provide at least 20ms of 8777c478bd9Sstevel@tonic-gate * resume signalling, and s/w provide 10ms of 8787c478bd9Sstevel@tonic-gate * recovery time before accessing the port. 8797c478bd9Sstevel@tonic-gate */ 8807c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 8817c478bd9Sstevel@tonic-gate delay(drv_usectohz(40000)); 8827c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 8837c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 884993e3fafSRobert Mustacchi &status, &change, NULL, PORT_CHANGE_PSSC); 8857c478bd9Sstevel@tonic-gate 8867c478bd9Sstevel@tonic-gate if ((status & PORT_STATUS_PSS) == 0) { 8877c478bd9Sstevel@tonic-gate /* the port did finally resume */ 8887c478bd9Sstevel@tonic-gate retval = USB_SUCCESS; 8897c478bd9Sstevel@tonic-gate 8907c478bd9Sstevel@tonic-gate break; 8917c478bd9Sstevel@tonic-gate } 8927c478bd9Sstevel@tonic-gate } 8937c478bd9Sstevel@tonic-gate } 8947c478bd9Sstevel@tonic-gate 8957c478bd9Sstevel@tonic-gate /* allow hotplug thread again */ 8967c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread--; 8977c478bd9Sstevel@tonic-gate hubd_start_polling(hubd, 0); 8987c478bd9Sstevel@tonic-gate 8997c478bd9Sstevel@tonic-gate break; 9007c478bd9Sstevel@tonic-gate case USB_DEV_DISCONNECTED: 9017c478bd9Sstevel@tonic-gate /* Ignore - NO Operation */ 9027c478bd9Sstevel@tonic-gate retval = USB_SUCCESS; 9037c478bd9Sstevel@tonic-gate 9047c478bd9Sstevel@tonic-gate break; 9057c478bd9Sstevel@tonic-gate case USB_DEV_SUSPENDED: 9067c478bd9Sstevel@tonic-gate case USB_DEV_PWRED_DOWN: 9077c478bd9Sstevel@tonic-gate default: 9087c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 9097c478bd9Sstevel@tonic-gate "Improper state for port Resume"); 9107c478bd9Sstevel@tonic-gate 9117c478bd9Sstevel@tonic-gate break; 9127c478bd9Sstevel@tonic-gate } 9137c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 9147c478bd9Sstevel@tonic-gate 9157c478bd9Sstevel@tonic-gate return (retval); 9167c478bd9Sstevel@tonic-gate } 9177c478bd9Sstevel@tonic-gate 9187c478bd9Sstevel@tonic-gate 9197c478bd9Sstevel@tonic-gate /* 9207c478bd9Sstevel@tonic-gate * suspend port depending on device state 9217c478bd9Sstevel@tonic-gate */ 9227c478bd9Sstevel@tonic-gate static int 9237c478bd9Sstevel@tonic-gate hubd_suspend_port(hubd_t *hubd, usb_port_t port) 9247c478bd9Sstevel@tonic-gate { 9257c478bd9Sstevel@tonic-gate int rval, retry; 9267c478bd9Sstevel@tonic-gate int retval = USB_FAILURE; 9277c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 9287c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 9297c478bd9Sstevel@tonic-gate uint16_t status; 9307c478bd9Sstevel@tonic-gate uint16_t change; 9317c478bd9Sstevel@tonic-gate 9327c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 9337c478bd9Sstevel@tonic-gate "hubd_suspend_port: port=%d", port); 9347c478bd9Sstevel@tonic-gate 9357c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 9367c478bd9Sstevel@tonic-gate 9377c478bd9Sstevel@tonic-gate switch (hubd->h_dev_state) { 9387c478bd9Sstevel@tonic-gate case USB_DEV_HUB_STATE_RECOVER: 9397c478bd9Sstevel@tonic-gate /* 9407c478bd9Sstevel@tonic-gate * When hubd's connect event callback posts a connect 9417c478bd9Sstevel@tonic-gate * event to its child, it results in this busctl call 9427c478bd9Sstevel@tonic-gate * which is valid 9437c478bd9Sstevel@tonic-gate */ 9447c478bd9Sstevel@tonic-gate /* FALLTHRU */ 9457c478bd9Sstevel@tonic-gate case USB_DEV_HUB_CHILD_PWRLVL: 9467c478bd9Sstevel@tonic-gate /* 9477c478bd9Sstevel@tonic-gate * When one child is resuming, the other could timeout 9487c478bd9Sstevel@tonic-gate * and go to low power mode, which is valid 9497c478bd9Sstevel@tonic-gate */ 9507c478bd9Sstevel@tonic-gate /* FALLTHRU */ 9517c478bd9Sstevel@tonic-gate case USB_DEV_ONLINE: 9527c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread++; 9537c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd); 9547c478bd9Sstevel@tonic-gate 9557c478bd9Sstevel@tonic-gate /* 9567c478bd9Sstevel@tonic-gate * Some devices start an unprovoked resume. According to spec, 9577c478bd9Sstevel@tonic-gate * normal resume time for port is 10ms. Wait for double that 9587c478bd9Sstevel@tonic-gate * time, then check to be sure port is really suspended. 9597c478bd9Sstevel@tonic-gate */ 9607c478bd9Sstevel@tonic-gate for (retry = 0; retry < HUBD_SUS_RES_RETRY; retry++) { 9617c478bd9Sstevel@tonic-gate /* Now SetFeature(PortSuspend) */ 9627c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 9637c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 9647c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 96535f36846Ssl HUB_HANDLE_PORT_FEATURE_TYPE, 9667c478bd9Sstevel@tonic-gate USB_REQ_SET_FEATURE, 9677c478bd9Sstevel@tonic-gate CFS_PORT_SUSPEND, 9687c478bd9Sstevel@tonic-gate port, 9697c478bd9Sstevel@tonic-gate 0, NULL, 0, 9707c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != 9717c478bd9Sstevel@tonic-gate USB_SUCCESS) { 9727c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, 9737c478bd9Sstevel@tonic-gate hubd->h_log_handle, 9747c478bd9Sstevel@tonic-gate "SetFeature(PortSuspend) fails" 9757c478bd9Sstevel@tonic-gate "rval=%d cr=%d cb=0x%x", 9767c478bd9Sstevel@tonic-gate rval, completion_reason, cb_flags); 9777c478bd9Sstevel@tonic-gate } 9787c478bd9Sstevel@tonic-gate 9797c478bd9Sstevel@tonic-gate /* 9807c478bd9Sstevel@tonic-gate * some devices start an unprovoked resume 9817c478bd9Sstevel@tonic-gate * wait and check port status after some time 9827c478bd9Sstevel@tonic-gate */ 9837c478bd9Sstevel@tonic-gate delay(drv_usectohz(20000)); 9847c478bd9Sstevel@tonic-gate 9857c478bd9Sstevel@tonic-gate /* either ways ack changes on the port */ 9867c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 9877c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 988993e3fafSRobert Mustacchi &status, &change, NULL, PORT_CHANGE_PSSC); 9897c478bd9Sstevel@tonic-gate if (status & PORT_STATUS_PSS) { 9907c478bd9Sstevel@tonic-gate /* the port is indeed suspended */ 9917c478bd9Sstevel@tonic-gate retval = USB_SUCCESS; 9927c478bd9Sstevel@tonic-gate 9937c478bd9Sstevel@tonic-gate break; 9946f6c7d2bSVincent Wang } else { 9956f6c7d2bSVincent Wang USB_DPRINTF_L0(DPRINT_MASK_PM, 9966f6c7d2bSVincent Wang hubd->h_log_handle, 9976f6c7d2bSVincent Wang "hubdi: port%d failed to be suspended!", 9986f6c7d2bSVincent Wang port); 9997c478bd9Sstevel@tonic-gate } 10007c478bd9Sstevel@tonic-gate } 10017c478bd9Sstevel@tonic-gate 10027c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread--; 10037c478bd9Sstevel@tonic-gate hubd_start_polling(hubd, 0); 10047c478bd9Sstevel@tonic-gate 10057c478bd9Sstevel@tonic-gate break; 10067c478bd9Sstevel@tonic-gate 10077c478bd9Sstevel@tonic-gate case USB_DEV_DISCONNECTED: 10087c478bd9Sstevel@tonic-gate /* Ignore - No Operation */ 10097c478bd9Sstevel@tonic-gate retval = USB_SUCCESS; 10107c478bd9Sstevel@tonic-gate 10117c478bd9Sstevel@tonic-gate break; 10127c478bd9Sstevel@tonic-gate 10137c478bd9Sstevel@tonic-gate case USB_DEV_SUSPENDED: 10147c478bd9Sstevel@tonic-gate case USB_DEV_PWRED_DOWN: 10157c478bd9Sstevel@tonic-gate default: 10167c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 10177c478bd9Sstevel@tonic-gate "Improper state for port Suspend"); 10187c478bd9Sstevel@tonic-gate 10197c478bd9Sstevel@tonic-gate break; 10207c478bd9Sstevel@tonic-gate } 10217c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 10227c478bd9Sstevel@tonic-gate 10237c478bd9Sstevel@tonic-gate return (retval); 10247c478bd9Sstevel@tonic-gate } 10257c478bd9Sstevel@tonic-gate 10267c478bd9Sstevel@tonic-gate 10277c478bd9Sstevel@tonic-gate /* 10287c478bd9Sstevel@tonic-gate * child post attach/detach notifications 10297c478bd9Sstevel@tonic-gate */ 10307c478bd9Sstevel@tonic-gate static void 10317c478bd9Sstevel@tonic-gate hubd_post_attach(hubd_t *hubd, usb_port_t port, struct attachspec *as) 10327c478bd9Sstevel@tonic-gate { 10337c478bd9Sstevel@tonic-gate dev_info_t *dip; 10347c478bd9Sstevel@tonic-gate 10357c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 10367c478bd9Sstevel@tonic-gate "hubd_post_attach: port=%d result=%d", 10377c478bd9Sstevel@tonic-gate port, as->result); 10387c478bd9Sstevel@tonic-gate 10397c478bd9Sstevel@tonic-gate if (as->result == DDI_SUCCESS) { 10407c478bd9Sstevel@tonic-gate /* 10417c478bd9Sstevel@tonic-gate * Check if the child created wants to be power managed. 10427c478bd9Sstevel@tonic-gate * If yes, the childs power level gets automatically tracked 10437c478bd9Sstevel@tonic-gate * by DDI_CTLOPS_POWER busctl. 10447c478bd9Sstevel@tonic-gate * If no, we set power of the new child by default 10457c478bd9Sstevel@tonic-gate * to USB_DEV_OS_FULL_PWR. Because we should never suspend. 10467c478bd9Sstevel@tonic-gate */ 10477c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 10487c478bd9Sstevel@tonic-gate dip = hubd->h_children_dips[port]; 10497c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 10507c478bd9Sstevel@tonic-gate if (DEVI(dip)->devi_pm_info == NULL) { 10517c478bd9Sstevel@tonic-gate hubd_set_child_pwrlvl(hubd, port, USB_DEV_OS_FULL_PWR); 10527c478bd9Sstevel@tonic-gate } 10537c478bd9Sstevel@tonic-gate } 10547c478bd9Sstevel@tonic-gate } 10557c478bd9Sstevel@tonic-gate 10567c478bd9Sstevel@tonic-gate 10577c478bd9Sstevel@tonic-gate static void 10587c478bd9Sstevel@tonic-gate hubd_post_detach(hubd_t *hubd, usb_port_t port, struct detachspec *ds) 10597c478bd9Sstevel@tonic-gate { 10607c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 10617c478bd9Sstevel@tonic-gate "hubd_post_detach: port=%d result=%d", port, ds->result); 10627c478bd9Sstevel@tonic-gate 10637c478bd9Sstevel@tonic-gate /* 10647c478bd9Sstevel@tonic-gate * if the device is successfully detached and is the 10657c478bd9Sstevel@tonic-gate * last device to detach, mark component as idle 10667c478bd9Sstevel@tonic-gate */ 10677c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 10687c478bd9Sstevel@tonic-gate if (ds->result == DDI_SUCCESS) { 10697c478bd9Sstevel@tonic-gate usba_device_t *usba_device = hubd->h_usba_devices[port]; 107035f36846Ssl dev_info_t *pdip = hubd->h_dip; 10717c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 10727c478bd9Sstevel@tonic-gate 107335f36846Ssl usba_hubdi_incr_power_budget(pdip, usba_device); 107435f36846Ssl 10757c478bd9Sstevel@tonic-gate /* 10767c478bd9Sstevel@tonic-gate * We set power of the detached child 10777c478bd9Sstevel@tonic-gate * to 0, so that we can suspend if all 10787c478bd9Sstevel@tonic-gate * our children are gone 10797c478bd9Sstevel@tonic-gate */ 10807c478bd9Sstevel@tonic-gate hubd_set_child_pwrlvl(hubd, port, USB_DEV_OS_PWR_OFF); 10817c478bd9Sstevel@tonic-gate 10827c478bd9Sstevel@tonic-gate /* check for leaks on detaching */ 10837c478bd9Sstevel@tonic-gate if ((usba_device) && (ds->cmd == DDI_DETACH)) { 10847c478bd9Sstevel@tonic-gate usba_check_for_leaks(usba_device); 10857c478bd9Sstevel@tonic-gate } 10867c478bd9Sstevel@tonic-gate } else { 10877c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 10887c478bd9Sstevel@tonic-gate } 10897c478bd9Sstevel@tonic-gate } 10907c478bd9Sstevel@tonic-gate 10917c478bd9Sstevel@tonic-gate 10927c478bd9Sstevel@tonic-gate /* 10937c478bd9Sstevel@tonic-gate * hubd_post_power 10947c478bd9Sstevel@tonic-gate * After the child's power entry point has been called 10957c478bd9Sstevel@tonic-gate * we record its power level in our local struct. 10967c478bd9Sstevel@tonic-gate * If the device has powered off, we suspend port 10977c478bd9Sstevel@tonic-gate */ 10987c478bd9Sstevel@tonic-gate static int 10997c478bd9Sstevel@tonic-gate hubd_post_power(hubd_t *hubd, usb_port_t port, pm_bp_child_pwrchg_t *bpc, 11007c478bd9Sstevel@tonic-gate int result) 11017c478bd9Sstevel@tonic-gate { 11027c478bd9Sstevel@tonic-gate int retval = USB_SUCCESS; 11037c478bd9Sstevel@tonic-gate 11047c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 11057c478bd9Sstevel@tonic-gate "hubd_post_power: port=%d", port); 11067c478bd9Sstevel@tonic-gate 11077c478bd9Sstevel@tonic-gate if (result == DDI_SUCCESS) { 11087c478bd9Sstevel@tonic-gate 11097c478bd9Sstevel@tonic-gate /* record this power in our local struct */ 11107c478bd9Sstevel@tonic-gate hubd_set_child_pwrlvl(hubd, port, bpc->bpc_nlevel); 11117c478bd9Sstevel@tonic-gate 11127c478bd9Sstevel@tonic-gate if (bpc->bpc_nlevel == USB_DEV_OS_PWR_OFF) { 11137c478bd9Sstevel@tonic-gate 11147c478bd9Sstevel@tonic-gate /* now suspend the port */ 11157c478bd9Sstevel@tonic-gate retval = hubd_suspend_port(hubd, port); 11167c478bd9Sstevel@tonic-gate } else if (bpc->bpc_nlevel == USB_DEV_OS_FULL_PWR) { 11177c478bd9Sstevel@tonic-gate 11187c478bd9Sstevel@tonic-gate /* make sure the port is resumed */ 11197c478bd9Sstevel@tonic-gate retval = hubd_resume_port(hubd, port); 11207c478bd9Sstevel@tonic-gate } 11217c478bd9Sstevel@tonic-gate } else { 11227c478bd9Sstevel@tonic-gate 11237c478bd9Sstevel@tonic-gate /* record old power in our local struct */ 11247c478bd9Sstevel@tonic-gate hubd_set_child_pwrlvl(hubd, port, bpc->bpc_olevel); 11257c478bd9Sstevel@tonic-gate 11267c478bd9Sstevel@tonic-gate if (bpc->bpc_olevel == USB_DEV_OS_PWR_OFF) { 11277c478bd9Sstevel@tonic-gate 11287c478bd9Sstevel@tonic-gate /* 11297c478bd9Sstevel@tonic-gate * As this device failed to transition from 11307c478bd9Sstevel@tonic-gate * power off state, suspend the port again 11317c478bd9Sstevel@tonic-gate */ 11327c478bd9Sstevel@tonic-gate retval = hubd_suspend_port(hubd, port); 11337c478bd9Sstevel@tonic-gate } 11347c478bd9Sstevel@tonic-gate } 11357c478bd9Sstevel@tonic-gate 11367c478bd9Sstevel@tonic-gate return (retval); 11377c478bd9Sstevel@tonic-gate } 11387c478bd9Sstevel@tonic-gate 11397c478bd9Sstevel@tonic-gate 11407c478bd9Sstevel@tonic-gate /* 11417c478bd9Sstevel@tonic-gate * bus ctl notifications are handled here, the rest goes up to root hub/hcd 11427c478bd9Sstevel@tonic-gate */ 11437c478bd9Sstevel@tonic-gate static int 11447c478bd9Sstevel@tonic-gate usba_hubdi_bus_ctl(dev_info_t *dip, 1145*d96925c4SRichard Lowe dev_info_t *rdip, 1146*d96925c4SRichard Lowe ddi_ctl_enum_t op, 1147*d96925c4SRichard Lowe void *arg, 1148*d96925c4SRichard Lowe void *result) 11497c478bd9Sstevel@tonic-gate { 11507c478bd9Sstevel@tonic-gate usba_device_t *hub_usba_device = usba_get_usba_device(rdip); 11517c478bd9Sstevel@tonic-gate dev_info_t *root_hub_dip = hub_usba_device->usb_root_hub_dip; 11527c478bd9Sstevel@tonic-gate struct attachspec *as; 11537c478bd9Sstevel@tonic-gate struct detachspec *ds; 11547c478bd9Sstevel@tonic-gate hubd_t *hubd; 11557c478bd9Sstevel@tonic-gate usb_port_t port; 11567c478bd9Sstevel@tonic-gate int circ, rval; 11577c478bd9Sstevel@tonic-gate int retval = DDI_FAILURE; 11587c478bd9Sstevel@tonic-gate 11597c478bd9Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 11607c478bd9Sstevel@tonic-gate 11617c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 11627c478bd9Sstevel@tonic-gate 11637c478bd9Sstevel@tonic-gate /* flag that we are currently running bus_ctl */ 11647c478bd9Sstevel@tonic-gate hubd->h_bus_ctls++; 11657c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 11667c478bd9Sstevel@tonic-gate 11677c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle, 11687c478bd9Sstevel@tonic-gate "usba_hubdi_bus_ctl:\n\t" 11697c478bd9Sstevel@tonic-gate "dip=0x%p, rdip=0x%p, op=0x%x, arg=0x%p", 1170112116d8Sfb (void *)dip, (void *)rdip, op, arg); 11717c478bd9Sstevel@tonic-gate 11727c478bd9Sstevel@tonic-gate switch (op) { 11737c478bd9Sstevel@tonic-gate case DDI_CTLOPS_ATTACH: 11747c478bd9Sstevel@tonic-gate as = (struct attachspec *)arg; 11757c478bd9Sstevel@tonic-gate port = hubd_child_dip2port(hubd, rdip); 11767c478bd9Sstevel@tonic-gate 11777c478bd9Sstevel@tonic-gate /* there is nothing to do at resume time */ 11787c478bd9Sstevel@tonic-gate if (as->cmd == DDI_RESUME) { 11797c478bd9Sstevel@tonic-gate break; 11807c478bd9Sstevel@tonic-gate } 11817c478bd9Sstevel@tonic-gate 11827c478bd9Sstevel@tonic-gate /* serialize access */ 11837c478bd9Sstevel@tonic-gate ndi_devi_enter(hubd->h_dip, &circ); 11847c478bd9Sstevel@tonic-gate 11857c478bd9Sstevel@tonic-gate switch (as->when) { 11867c478bd9Sstevel@tonic-gate case DDI_PRE: 11877c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 11887c478bd9Sstevel@tonic-gate "DDI_PRE DDI_CTLOPS_ATTACH: dip=%p, port=%d", 1189112116d8Sfb (void *)rdip, port); 11907c478bd9Sstevel@tonic-gate 11917c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 11927c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= HUBD_CHILD_ATTACHING; 11937c478bd9Sstevel@tonic-gate 11947c478bd9Sstevel@tonic-gate /* Go busy here. Matching idle is DDI_POST case. */ 11957c478bd9Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, dip, 0); 11967c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 11977c478bd9Sstevel@tonic-gate 11987c478bd9Sstevel@tonic-gate /* 11997c478bd9Sstevel@tonic-gate * if we suspended the port previously 12007c478bd9Sstevel@tonic-gate * because child went to low power state, and 12017c478bd9Sstevel@tonic-gate * someone unloaded the driver, the port would 12027c478bd9Sstevel@tonic-gate * still be suspended and needs to be resumed 12037c478bd9Sstevel@tonic-gate */ 12047c478bd9Sstevel@tonic-gate rval = hubd_resume_port(hubd, port); 12057c478bd9Sstevel@tonic-gate if (rval == USB_SUCCESS) { 12067c478bd9Sstevel@tonic-gate retval = DDI_SUCCESS; 12077c478bd9Sstevel@tonic-gate } 12087c478bd9Sstevel@tonic-gate 12097c478bd9Sstevel@tonic-gate break; 12107c478bd9Sstevel@tonic-gate case DDI_POST: 12117c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 12127c478bd9Sstevel@tonic-gate "DDI_POST DDI_CTLOPS_ATTACH: dip=%p, port=%d", 1213112116d8Sfb (void *)rdip, port); 12147c478bd9Sstevel@tonic-gate 12157c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 12167c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~HUBD_CHILD_ATTACHING; 12177c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 12187c478bd9Sstevel@tonic-gate 12197c478bd9Sstevel@tonic-gate hubd_post_attach(hubd, port, (struct attachspec *)arg); 12207c478bd9Sstevel@tonic-gate retval = DDI_SUCCESS; 12217c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 12227c478bd9Sstevel@tonic-gate 12237c478bd9Sstevel@tonic-gate /* Matching idle call for DDI_PRE busy call. */ 12247c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, dip, 0); 12257c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 12267c478bd9Sstevel@tonic-gate } 12277c478bd9Sstevel@tonic-gate ndi_devi_exit(hubd->h_dip, circ); 12287c478bd9Sstevel@tonic-gate 12297c478bd9Sstevel@tonic-gate break; 12307c478bd9Sstevel@tonic-gate case DDI_CTLOPS_DETACH: 12317c478bd9Sstevel@tonic-gate ds = (struct detachspec *)arg; 12327c478bd9Sstevel@tonic-gate port = hubd_child_dip2port(hubd, rdip); 12337c478bd9Sstevel@tonic-gate 12347c478bd9Sstevel@tonic-gate /* there is nothing to do at suspend time */ 12357c478bd9Sstevel@tonic-gate if (ds->cmd == DDI_SUSPEND) { 12367c478bd9Sstevel@tonic-gate break; 12377c478bd9Sstevel@tonic-gate } 12387c478bd9Sstevel@tonic-gate 12397c478bd9Sstevel@tonic-gate /* serialize access */ 12407c478bd9Sstevel@tonic-gate ndi_devi_enter(hubd->h_dip, &circ); 12417c478bd9Sstevel@tonic-gate 12427c478bd9Sstevel@tonic-gate switch (ds->when) { 12437c478bd9Sstevel@tonic-gate case DDI_PRE: 12447c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 12457c478bd9Sstevel@tonic-gate "DDI_PRE DDI_CTLOPS_DETACH: dip=%p port=%d", 1246112116d8Sfb (void *)rdip, port); 12477c478bd9Sstevel@tonic-gate 12487c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 12497c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= HUBD_CHILD_DETACHING; 12507c478bd9Sstevel@tonic-gate 12517c478bd9Sstevel@tonic-gate /* Go busy here. Matching idle is DDI_POST case. */ 12527c478bd9Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, dip, 0); 12537c478bd9Sstevel@tonic-gate 12547c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 12557c478bd9Sstevel@tonic-gate retval = DDI_SUCCESS; 12567c478bd9Sstevel@tonic-gate 12577c478bd9Sstevel@tonic-gate break; 12587c478bd9Sstevel@tonic-gate case DDI_POST: 12597c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 12607c478bd9Sstevel@tonic-gate "DDI_POST DDI_CTLOPS_DETACH: dip=%p port=%d", 1261112116d8Sfb (void *)rdip, port); 12627c478bd9Sstevel@tonic-gate 12637c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 12647c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~HUBD_CHILD_DETACHING; 12657c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 12667c478bd9Sstevel@tonic-gate 12677c478bd9Sstevel@tonic-gate /* Matching idle call for DDI_PRE busy call. */ 12687c478bd9Sstevel@tonic-gate hubd_post_detach(hubd, port, (struct detachspec *)arg); 12697c478bd9Sstevel@tonic-gate retval = DDI_SUCCESS; 12707c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 12717c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, dip, 0); 12727c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 12737c478bd9Sstevel@tonic-gate 12747c478bd9Sstevel@tonic-gate break; 12757c478bd9Sstevel@tonic-gate } 12767c478bd9Sstevel@tonic-gate ndi_devi_exit(hubd->h_dip, circ); 12777c478bd9Sstevel@tonic-gate 12787c478bd9Sstevel@tonic-gate break; 12797c478bd9Sstevel@tonic-gate default: 12807c478bd9Sstevel@tonic-gate retval = usba_bus_ctl(root_hub_dip, rdip, op, arg, result); 12817c478bd9Sstevel@tonic-gate } 12827c478bd9Sstevel@tonic-gate 12837c478bd9Sstevel@tonic-gate /* decrement bus_ctls count */ 12847c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 12857c478bd9Sstevel@tonic-gate hubd->h_bus_ctls--; 12867c478bd9Sstevel@tonic-gate ASSERT(hubd->h_bus_ctls >= 0); 12877c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 12887c478bd9Sstevel@tonic-gate 12897c478bd9Sstevel@tonic-gate return (retval); 12907c478bd9Sstevel@tonic-gate } 12917c478bd9Sstevel@tonic-gate 129240482326SVincent Wang /* 129340482326SVincent Wang * hubd_config_one: 1294*d96925c4SRichard Lowe * enumerate one child according to 'port' 129540482326SVincent Wang */ 129640482326SVincent Wang 129740482326SVincent Wang static boolean_t 129840482326SVincent Wang hubd_config_one(hubd_t *hubd, int port) 129940482326SVincent Wang { 130040482326SVincent Wang dev_info_t *hdip = hubd->h_dip; 130140482326SVincent Wang dev_info_t *rh_dip = hubd->h_usba_device->usb_root_hub_dip; 130240482326SVincent Wang boolean_t online_child = B_FALSE, found = B_FALSE; 130340482326SVincent Wang int prh_circ, rh_circ, circ; 130440482326SVincent Wang 130540482326SVincent Wang USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 130640482326SVincent Wang "hubd_config_one: started, hubd_reset_port = 0x%x", port); 130740482326SVincent Wang 130840482326SVincent Wang ndi_hold_devi(hdip); /* so we don't race with detach */ 130940482326SVincent Wang 131040482326SVincent Wang /* 131140482326SVincent Wang * this ensures one config activity per system at a time. 131240482326SVincent Wang * we enter the parent PCI node to have this serialization. 131340482326SVincent Wang * this also excludes ioctls and deathrow thread 131440482326SVincent Wang */ 131540482326SVincent Wang ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ); 131640482326SVincent Wang ndi_devi_enter(rh_dip, &rh_circ); 131740482326SVincent Wang 131840482326SVincent Wang /* exclude other threads */ 131940482326SVincent Wang ndi_devi_enter(hdip, &circ); 132040482326SVincent Wang mutex_enter(HUBD_MUTEX(hubd)); 132140482326SVincent Wang 1322e4c316c4SVincent Wang hubd_pm_busy_component(hubd, hubd->h_dip, 0); 1323e4c316c4SVincent Wang 132440482326SVincent Wang if (!hubd->h_children_dips[port]) { 1325993e3fafSRobert Mustacchi uint16_t status, change; 132640482326SVincent Wang 132740482326SVincent Wang (void) hubd_determine_port_status(hubd, port, 1328993e3fafSRobert Mustacchi &status, &change, NULL, HUBD_ACK_ALL_CHANGES); 132940482326SVincent Wang 133040482326SVincent Wang if (status & PORT_STATUS_CCS) { 133140482326SVincent Wang online_child |= (hubd_handle_port_connect(hubd, 133240482326SVincent Wang port) == USB_SUCCESS); 133340482326SVincent Wang found = online_child; 133440482326SVincent Wang } 133540482326SVincent Wang } else { 133640482326SVincent Wang found = B_TRUE; 133740482326SVincent Wang } 133840482326SVincent Wang 133940482326SVincent Wang mutex_exit(HUBD_MUTEX(hubd)); 134040482326SVincent Wang 134140482326SVincent Wang ndi_devi_exit(hdip, circ); 134240482326SVincent Wang ndi_devi_exit(rh_dip, rh_circ); 134340482326SVincent Wang ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ); 134440482326SVincent Wang 134540482326SVincent Wang if (online_child) { 134640482326SVincent Wang USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 134740482326SVincent Wang "hubd_config_one: onlining child"); 134840482326SVincent Wang 134940482326SVincent Wang (void) ndi_devi_online(hubd->h_dip, 0); 135040482326SVincent Wang } 135140482326SVincent Wang 135240482326SVincent Wang mutex_enter(HUBD_MUTEX(hubd)); 135340482326SVincent Wang 135440482326SVincent Wang (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0); 135540482326SVincent Wang 135640482326SVincent Wang USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 135740482326SVincent Wang "hubd_config_one: exit"); 135840482326SVincent Wang 135940482326SVincent Wang mutex_exit(HUBD_MUTEX(hubd)); 136040482326SVincent Wang 136140482326SVincent Wang ndi_rele_devi(hdip); 136240482326SVincent Wang 136340482326SVincent Wang return (found); 136440482326SVincent Wang } 13657c478bd9Sstevel@tonic-gate 13667c478bd9Sstevel@tonic-gate /* 13677c478bd9Sstevel@tonic-gate * bus enumeration entry points 13687c478bd9Sstevel@tonic-gate */ 13697c478bd9Sstevel@tonic-gate static int 13707c478bd9Sstevel@tonic-gate hubd_bus_config(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op, 13717c478bd9Sstevel@tonic-gate void *arg, dev_info_t **child) 13727c478bd9Sstevel@tonic-gate { 13737c478bd9Sstevel@tonic-gate hubd_t *hubd = hubd_get_soft_state(dip); 13747c478bd9Sstevel@tonic-gate int rval, circ; 137540482326SVincent Wang long port; 13767c478bd9Sstevel@tonic-gate 13777c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 13787c478bd9Sstevel@tonic-gate "hubd_bus_config: op=%d", op); 13797c478bd9Sstevel@tonic-gate 13807c478bd9Sstevel@tonic-gate if (hubdi_bus_config_debug) { 13817c478bd9Sstevel@tonic-gate flag |= NDI_DEVI_DEBUG; 13827c478bd9Sstevel@tonic-gate } 13837c478bd9Sstevel@tonic-gate 138440482326SVincent Wang if (op == BUS_CONFIG_ONE) { 138540482326SVincent Wang boolean_t found; 1386d724deadSguoqing zhu - Sun Microsystems - Beijing China char cname[80]; 1387d724deadSguoqing zhu - Sun Microsystems - Beijing China char *name, *addr; 1388d724deadSguoqing zhu - Sun Microsystems - Beijing China 1389d724deadSguoqing zhu - Sun Microsystems - Beijing China USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 1390d724deadSguoqing zhu - Sun Microsystems - Beijing China "hubd_bus_config: op=%d (BUS_CONFIG_ONE)", op); 1391d724deadSguoqing zhu - Sun Microsystems - Beijing China 139240482326SVincent Wang (void) snprintf(cname, 80, "%s", (char *)arg); 139340482326SVincent Wang /* split name into "name@addr" parts */ 139440482326SVincent Wang i_ddi_parse_name(cname, &name, &addr, NULL); 139540482326SVincent Wang if (addr && *addr) { 139640482326SVincent Wang (void) ddi_strtol(addr, NULL, 16, &port); 139740482326SVincent Wang } else { 139840482326SVincent Wang return (NDI_FAILURE); 139940482326SVincent Wang } 1400d724deadSguoqing zhu - Sun Microsystems - Beijing China 140140482326SVincent Wang found = hubd_config_one(hubd, port); 14025321cfb7Spengcheng chen - Sun Microsystems - Beijing China 1403d724deadSguoqing zhu - Sun Microsystems - Beijing China if (found == 0) { 1404d724deadSguoqing zhu - Sun Microsystems - Beijing China return (NDI_FAILURE); 1405d724deadSguoqing zhu - Sun Microsystems - Beijing China } 1406d724deadSguoqing zhu - Sun Microsystems - Beijing China 14077c478bd9Sstevel@tonic-gate } 14087c478bd9Sstevel@tonic-gate ndi_devi_enter(hubd->h_dip, &circ); 14097c478bd9Sstevel@tonic-gate rval = ndi_busop_bus_config(dip, flag, op, arg, child, 0); 14107c478bd9Sstevel@tonic-gate ndi_devi_exit(hubd->h_dip, circ); 14117c478bd9Sstevel@tonic-gate 14127c478bd9Sstevel@tonic-gate return (rval); 14137c478bd9Sstevel@tonic-gate } 14147c478bd9Sstevel@tonic-gate 14157c478bd9Sstevel@tonic-gate 14167c478bd9Sstevel@tonic-gate static int 14177c478bd9Sstevel@tonic-gate hubd_bus_unconfig(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op, 14187c478bd9Sstevel@tonic-gate void *arg) 14197c478bd9Sstevel@tonic-gate { 14207c478bd9Sstevel@tonic-gate hubd_t *hubd = hubd_get_soft_state(dip); 14217c478bd9Sstevel@tonic-gate dev_info_t *cdip; 14227c478bd9Sstevel@tonic-gate usb_port_t port; 14237c478bd9Sstevel@tonic-gate int circ; 14247c478bd9Sstevel@tonic-gate int rval; 14257c478bd9Sstevel@tonic-gate 14267c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 14277c478bd9Sstevel@tonic-gate "hubd_bus_unconfig: op=%d", op); 14287c478bd9Sstevel@tonic-gate 14297c478bd9Sstevel@tonic-gate if (hubdi_bus_config_debug) { 14307c478bd9Sstevel@tonic-gate flag |= NDI_DEVI_DEBUG; 14317c478bd9Sstevel@tonic-gate } 14327c478bd9Sstevel@tonic-gate 14337c478bd9Sstevel@tonic-gate if ((op == BUS_UNCONFIG_ALL) && (flag & NDI_AUTODETACH) == 0) { 14347c478bd9Sstevel@tonic-gate flag |= NDI_DEVI_REMOVE; 14357c478bd9Sstevel@tonic-gate } 14367c478bd9Sstevel@tonic-gate 14377c478bd9Sstevel@tonic-gate /* serialize access */ 14387c478bd9Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 14397c478bd9Sstevel@tonic-gate 14407c478bd9Sstevel@tonic-gate rval = ndi_busop_bus_unconfig(dip, flag, op, arg); 14417c478bd9Sstevel@tonic-gate 14427c478bd9Sstevel@tonic-gate /* logically zap children's list */ 14437c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 1444993e3fafSRobert Mustacchi for (port = 1; port <= hubd->h_nports; port++) { 14457c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= HUBD_CHILD_ZAP; 14467c478bd9Sstevel@tonic-gate } 14477c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 14487c478bd9Sstevel@tonic-gate 14497c478bd9Sstevel@tonic-gate /* fill in what's left */ 14507c478bd9Sstevel@tonic-gate for (cdip = ddi_get_child(dip); cdip; 14517c478bd9Sstevel@tonic-gate cdip = ddi_get_next_sibling(cdip)) { 14527c478bd9Sstevel@tonic-gate usba_device_t *usba_device = usba_get_usba_device(cdip); 14537c478bd9Sstevel@tonic-gate 14547c478bd9Sstevel@tonic-gate if (usba_device == NULL) { 14557c478bd9Sstevel@tonic-gate 14567c478bd9Sstevel@tonic-gate continue; 14577c478bd9Sstevel@tonic-gate } 14587c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 14597c478bd9Sstevel@tonic-gate port = usba_device->usb_port; 14607c478bd9Sstevel@tonic-gate hubd->h_children_dips[port] = cdip; 14617c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~HUBD_CHILD_ZAP; 14627c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 14637c478bd9Sstevel@tonic-gate } 14647c478bd9Sstevel@tonic-gate 14657c478bd9Sstevel@tonic-gate /* physically zap the children we didn't find */ 14667c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 1467993e3fafSRobert Mustacchi for (port = 1; port <= hubd->h_nports; port++) { 1468112cd14aSqz if (hubd->h_port_state[port] & HUBD_CHILD_ZAP) { 14697c478bd9Sstevel@tonic-gate /* zap the dip and usba_device structure as well */ 14707c478bd9Sstevel@tonic-gate hubd_free_usba_device(hubd, hubd->h_usba_devices[port]); 14717c478bd9Sstevel@tonic-gate hubd->h_children_dips[port] = NULL; 14727c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~HUBD_CHILD_ZAP; 14737c478bd9Sstevel@tonic-gate } 14747c478bd9Sstevel@tonic-gate } 14757c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 14767c478bd9Sstevel@tonic-gate 14777c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 14787c478bd9Sstevel@tonic-gate 14797c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 14807c478bd9Sstevel@tonic-gate "hubd_bus_unconfig: rval=%d", rval); 14817c478bd9Sstevel@tonic-gate 14827c478bd9Sstevel@tonic-gate return (rval); 14837c478bd9Sstevel@tonic-gate } 14847c478bd9Sstevel@tonic-gate 14857c478bd9Sstevel@tonic-gate 14867c478bd9Sstevel@tonic-gate /* bus_power entry point */ 14877c478bd9Sstevel@tonic-gate static int 14887c478bd9Sstevel@tonic-gate hubd_bus_power(dev_info_t *dip, void *impl_arg, pm_bus_power_op_t op, 14897c478bd9Sstevel@tonic-gate void *arg, void *result) 14907c478bd9Sstevel@tonic-gate { 14917c478bd9Sstevel@tonic-gate hubd_t *hubd; 14927c478bd9Sstevel@tonic-gate int rval, pwrup_res; 14937c478bd9Sstevel@tonic-gate usb_port_t port; 14947c478bd9Sstevel@tonic-gate int retval = DDI_FAILURE; 14957c478bd9Sstevel@tonic-gate pm_bp_child_pwrchg_t *bpc; 14967c478bd9Sstevel@tonic-gate pm_bp_nexus_pwrup_t bpn; 14977c478bd9Sstevel@tonic-gate 14987c478bd9Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 14997c478bd9Sstevel@tonic-gate 15007c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubd->h_log_handle, 15017c478bd9Sstevel@tonic-gate "hubd_bus_power: dip=%p, impl_arg=%p, power_op=%d, arg=%p, " 1502112116d8Sfb "result=%d\n", (void *)dip, impl_arg, op, arg, *(int *)result); 15037c478bd9Sstevel@tonic-gate 15047c478bd9Sstevel@tonic-gate bpc = (pm_bp_child_pwrchg_t *)arg; 15057c478bd9Sstevel@tonic-gate 15067c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 15077c478bd9Sstevel@tonic-gate hubd->h_bus_pwr++; 15087c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 15097c478bd9Sstevel@tonic-gate 15107c478bd9Sstevel@tonic-gate switch (op) { 15117c478bd9Sstevel@tonic-gate case BUS_POWER_PRE_NOTIFICATION: 15127c478bd9Sstevel@tonic-gate port = hubd_child_dip2port(hubd, bpc->bpc_dip); 15137c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle, 15147c478bd9Sstevel@tonic-gate "hubd_bus_power: BUS_POWER_PRE_NOTIFICATION, port=%d", 15157c478bd9Sstevel@tonic-gate port); 15167c478bd9Sstevel@tonic-gate 15177c478bd9Sstevel@tonic-gate /* go to full power if we are powered down */ 15187c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 15197c478bd9Sstevel@tonic-gate 15207c478bd9Sstevel@tonic-gate /* 15217c478bd9Sstevel@tonic-gate * If this case completes normally, idle will be in 15227c478bd9Sstevel@tonic-gate * hubd_bus_power / BUS_POWER_POST_NOTIFICATION 15237c478bd9Sstevel@tonic-gate */ 15247c478bd9Sstevel@tonic-gate hubd_pm_busy_component(hubd, dip, 0); 15257c478bd9Sstevel@tonic-gate 15267c478bd9Sstevel@tonic-gate /* 15277c478bd9Sstevel@tonic-gate * raise power only if we have created the components 15287c478bd9Sstevel@tonic-gate * and are currently in low power 15297c478bd9Sstevel@tonic-gate */ 15307c478bd9Sstevel@tonic-gate if ((hubd->h_dev_state == USB_DEV_PWRED_DOWN) && 15317c478bd9Sstevel@tonic-gate hubd->h_hubpm->hubp_wakeup_enabled) { 15327c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 15337c478bd9Sstevel@tonic-gate 15347c478bd9Sstevel@tonic-gate bpn.bpn_comp = 0; 15357c478bd9Sstevel@tonic-gate bpn.bpn_dip = dip; 15367c478bd9Sstevel@tonic-gate bpn.bpn_level = USB_DEV_OS_FULL_PWR; 15377c478bd9Sstevel@tonic-gate bpn.bpn_private = bpc->bpc_private; 15387c478bd9Sstevel@tonic-gate 15397c478bd9Sstevel@tonic-gate rval = pm_busop_bus_power(dip, impl_arg, 15407c478bd9Sstevel@tonic-gate BUS_POWER_NEXUS_PWRUP, (void *)&bpn, 15417c478bd9Sstevel@tonic-gate (void *)&pwrup_res); 15427c478bd9Sstevel@tonic-gate 15437c478bd9Sstevel@tonic-gate if (rval != DDI_SUCCESS || pwrup_res != DDI_SUCCESS) { 15447c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 15457c478bd9Sstevel@tonic-gate hubd_pm_idle_component(hubd, dip, 0); 15467c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 15477c478bd9Sstevel@tonic-gate 15487c478bd9Sstevel@tonic-gate break; 15497c478bd9Sstevel@tonic-gate } 15507c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 15517c478bd9Sstevel@tonic-gate } 15527c478bd9Sstevel@tonic-gate 15537c478bd9Sstevel@tonic-gate /* indicate that child is changing power level */ 15547c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= HUBD_CHILD_PWRLVL_CHNG; 15557c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 15567c478bd9Sstevel@tonic-gate 15577c478bd9Sstevel@tonic-gate if ((bpc->bpc_olevel == 0) && 15587c478bd9Sstevel@tonic-gate (bpc->bpc_nlevel > bpc->bpc_olevel)) { 15597c478bd9Sstevel@tonic-gate /* 15607c478bd9Sstevel@tonic-gate * this child is transitioning from power off 15617c478bd9Sstevel@tonic-gate * to power on state - resume port 15627c478bd9Sstevel@tonic-gate */ 15637c478bd9Sstevel@tonic-gate rval = hubd_resume_port(hubd, port); 15647c478bd9Sstevel@tonic-gate if (rval == USB_SUCCESS) { 15657c478bd9Sstevel@tonic-gate retval = DDI_SUCCESS; 15667c478bd9Sstevel@tonic-gate } else { 15677c478bd9Sstevel@tonic-gate /* reset this flag on failure */ 15687c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 15697c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= 15707c478bd9Sstevel@tonic-gate ~HUBD_CHILD_PWRLVL_CHNG; 15717c478bd9Sstevel@tonic-gate hubd_pm_idle_component(hubd, dip, 0); 15727c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 15737c478bd9Sstevel@tonic-gate } 15747c478bd9Sstevel@tonic-gate } else { 15757c478bd9Sstevel@tonic-gate retval = DDI_SUCCESS; 15767c478bd9Sstevel@tonic-gate } 15777c478bd9Sstevel@tonic-gate 15787c478bd9Sstevel@tonic-gate break; 15797c478bd9Sstevel@tonic-gate case BUS_POWER_POST_NOTIFICATION: 15807c478bd9Sstevel@tonic-gate port = hubd_child_dip2port(hubd, bpc->bpc_dip); 15817c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle, 15827c478bd9Sstevel@tonic-gate "hubd_bus_power: BUS_POWER_POST_NOTIFICATION, port=%d", 15837c478bd9Sstevel@tonic-gate port); 15847c478bd9Sstevel@tonic-gate 15857c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 15867c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~HUBD_CHILD_PWRLVL_CHNG; 15877c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 15887c478bd9Sstevel@tonic-gate 15897c478bd9Sstevel@tonic-gate /* record child's pwr and suspend port if required */ 15907c478bd9Sstevel@tonic-gate rval = hubd_post_power(hubd, port, bpc, *(int *)result); 15917c478bd9Sstevel@tonic-gate if (rval == USB_SUCCESS) { 15927c478bd9Sstevel@tonic-gate 15937c478bd9Sstevel@tonic-gate retval = DDI_SUCCESS; 15947c478bd9Sstevel@tonic-gate } 15957c478bd9Sstevel@tonic-gate 15967c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 15977c478bd9Sstevel@tonic-gate 15987c478bd9Sstevel@tonic-gate /* 15997c478bd9Sstevel@tonic-gate * Matching idle for the busy in 16007c478bd9Sstevel@tonic-gate * hubd_bus_power / BUS_POWER_PRE_NOTIFICATION 16017c478bd9Sstevel@tonic-gate */ 16027c478bd9Sstevel@tonic-gate hubd_pm_idle_component(hubd, dip, 0); 16037c478bd9Sstevel@tonic-gate 16047c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 16057c478bd9Sstevel@tonic-gate 16067c478bd9Sstevel@tonic-gate break; 16077c478bd9Sstevel@tonic-gate default: 16087c478bd9Sstevel@tonic-gate retval = pm_busop_bus_power(dip, impl_arg, op, arg, result); 16097c478bd9Sstevel@tonic-gate 16107c478bd9Sstevel@tonic-gate break; 16117c478bd9Sstevel@tonic-gate } 16127c478bd9Sstevel@tonic-gate 16137c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 16147c478bd9Sstevel@tonic-gate hubd->h_bus_pwr--; 16157c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 16167c478bd9Sstevel@tonic-gate 16177c478bd9Sstevel@tonic-gate return (retval); 16187c478bd9Sstevel@tonic-gate } 16197c478bd9Sstevel@tonic-gate 16207c478bd9Sstevel@tonic-gate 16217c478bd9Sstevel@tonic-gate /* 16227c478bd9Sstevel@tonic-gate * functions to handle power transition for OS levels 0 -> 3 16237c478bd9Sstevel@tonic-gate */ 16247c478bd9Sstevel@tonic-gate static int 16257c478bd9Sstevel@tonic-gate hubd_pwrlvl0(hubd_t *hubd) 16267c478bd9Sstevel@tonic-gate { 16277c478bd9Sstevel@tonic-gate hub_power_t *hubpm; 16287c478bd9Sstevel@tonic-gate 16297c478bd9Sstevel@tonic-gate /* We can't power down if hotplug thread is running */ 16307c478bd9Sstevel@tonic-gate if (hubd->h_hotplug_thread || hubd->h_hubpm->hubp_busy_pm || 16317c478bd9Sstevel@tonic-gate (hubd_can_suspend(hubd) == USB_FAILURE)) { 16327c478bd9Sstevel@tonic-gate 16337c478bd9Sstevel@tonic-gate return (USB_FAILURE); 16347c478bd9Sstevel@tonic-gate } 16357c478bd9Sstevel@tonic-gate 16367c478bd9Sstevel@tonic-gate switch (hubd->h_dev_state) { 16377c478bd9Sstevel@tonic-gate case USB_DEV_ONLINE: 16387c478bd9Sstevel@tonic-gate hubpm = hubd->h_hubpm; 16397c478bd9Sstevel@tonic-gate 16407c478bd9Sstevel@tonic-gate /* 16417c478bd9Sstevel@tonic-gate * To avoid race with bus_power pre_notify on check over 16427c478bd9Sstevel@tonic-gate * dev_state, we need to correctly set the dev state 16437c478bd9Sstevel@tonic-gate * before the mutex is dropped in stop polling. 16447c478bd9Sstevel@tonic-gate */ 16457c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_PWRED_DOWN; 16467c478bd9Sstevel@tonic-gate hubpm->hubp_current_power = USB_DEV_OS_PWR_OFF; 16477c478bd9Sstevel@tonic-gate 16487c478bd9Sstevel@tonic-gate /* 16497c478bd9Sstevel@tonic-gate * if we are the root hub, do not stop polling 16507c478bd9Sstevel@tonic-gate * otherwise, we will never see a resume 16517c478bd9Sstevel@tonic-gate */ 16527c478bd9Sstevel@tonic-gate if (usba_is_root_hub(hubd->h_dip)) { 16537c478bd9Sstevel@tonic-gate /* place holder to implement Global Suspend */ 16547c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 16557c478bd9Sstevel@tonic-gate "Global Suspend: Not Yet Implemented"); 16567c478bd9Sstevel@tonic-gate } else { 16577c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd); 16587c478bd9Sstevel@tonic-gate } 16597c478bd9Sstevel@tonic-gate 16607c478bd9Sstevel@tonic-gate /* Issue USB D3 command to the device here */ 16617c478bd9Sstevel@tonic-gate (void) usb_set_device_pwrlvl3(hubd->h_dip); 16627c478bd9Sstevel@tonic-gate 16637c478bd9Sstevel@tonic-gate break; 16647c478bd9Sstevel@tonic-gate case USB_DEV_DISCONNECTED: 16657c478bd9Sstevel@tonic-gate case USB_DEV_SUSPENDED: 16667c478bd9Sstevel@tonic-gate case USB_DEV_PWRED_DOWN: 16677c478bd9Sstevel@tonic-gate default: 16687c478bd9Sstevel@tonic-gate 16697c478bd9Sstevel@tonic-gate break; 16707c478bd9Sstevel@tonic-gate } 16717c478bd9Sstevel@tonic-gate 16727c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 16737c478bd9Sstevel@tonic-gate } 16747c478bd9Sstevel@tonic-gate 16757c478bd9Sstevel@tonic-gate 16767c478bd9Sstevel@tonic-gate /* ARGSUSED */ 16777c478bd9Sstevel@tonic-gate static int 16787c478bd9Sstevel@tonic-gate hubd_pwrlvl1(hubd_t *hubd) 16797c478bd9Sstevel@tonic-gate { 16807c478bd9Sstevel@tonic-gate /* Issue USB D2 command to the device here */ 16817c478bd9Sstevel@tonic-gate (void) usb_set_device_pwrlvl2(hubd->h_dip); 16827c478bd9Sstevel@tonic-gate 16837c478bd9Sstevel@tonic-gate return (USB_FAILURE); 16847c478bd9Sstevel@tonic-gate } 16857c478bd9Sstevel@tonic-gate 16867c478bd9Sstevel@tonic-gate 16877c478bd9Sstevel@tonic-gate /* ARGSUSED */ 16887c478bd9Sstevel@tonic-gate static int 16897c478bd9Sstevel@tonic-gate hubd_pwrlvl2(hubd_t *hubd) 16907c478bd9Sstevel@tonic-gate { 16917c478bd9Sstevel@tonic-gate /* Issue USB D1 command to the device here */ 16927c478bd9Sstevel@tonic-gate (void) usb_set_device_pwrlvl1(hubd->h_dip); 16937c478bd9Sstevel@tonic-gate 16947c478bd9Sstevel@tonic-gate return (USB_FAILURE); 16957c478bd9Sstevel@tonic-gate } 16967c478bd9Sstevel@tonic-gate 16977c478bd9Sstevel@tonic-gate 16987c478bd9Sstevel@tonic-gate static int 16997c478bd9Sstevel@tonic-gate hubd_pwrlvl3(hubd_t *hubd) 17007c478bd9Sstevel@tonic-gate { 17017c478bd9Sstevel@tonic-gate hub_power_t *hubpm; 17027c478bd9Sstevel@tonic-gate int rval; 17037c478bd9Sstevel@tonic-gate 17047c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, "hubd_pwrlvl3"); 17057c478bd9Sstevel@tonic-gate 17067c478bd9Sstevel@tonic-gate hubpm = hubd->h_hubpm; 17077c478bd9Sstevel@tonic-gate switch (hubd->h_dev_state) { 17087c478bd9Sstevel@tonic-gate case USB_DEV_PWRED_DOWN: 17097c478bd9Sstevel@tonic-gate ASSERT(hubpm->hubp_current_power == USB_DEV_OS_PWR_OFF); 17107c478bd9Sstevel@tonic-gate if (usba_is_root_hub(hubd->h_dip)) { 17117c478bd9Sstevel@tonic-gate /* implement global resume here */ 17127c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, 17137c478bd9Sstevel@tonic-gate hubd->h_log_handle, 17147c478bd9Sstevel@tonic-gate "Global Resume: Not Yet Implemented"); 17157c478bd9Sstevel@tonic-gate } 17167c478bd9Sstevel@tonic-gate /* Issue USB D0 command to the device here */ 17177c478bd9Sstevel@tonic-gate rval = usb_set_device_pwrlvl0(hubd->h_dip); 17187c478bd9Sstevel@tonic-gate ASSERT(rval == USB_SUCCESS); 17197c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_ONLINE; 17207c478bd9Sstevel@tonic-gate hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR; 1721e5815e7aSJosef 'Jeff' Sipek hubpm->hubp_time_at_full_power = gethrtime(); 17227c478bd9Sstevel@tonic-gate hubd_start_polling(hubd, 0); 17237c478bd9Sstevel@tonic-gate 17247c478bd9Sstevel@tonic-gate /* FALLTHRU */ 17257c478bd9Sstevel@tonic-gate case USB_DEV_ONLINE: 17267c478bd9Sstevel@tonic-gate /* we are already in full power */ 17277c478bd9Sstevel@tonic-gate 17287c478bd9Sstevel@tonic-gate /* FALLTHRU */ 17297c478bd9Sstevel@tonic-gate case USB_DEV_DISCONNECTED: 17307c478bd9Sstevel@tonic-gate case USB_DEV_SUSPENDED: 17317c478bd9Sstevel@tonic-gate /* 17327c478bd9Sstevel@tonic-gate * PM framework tries to put you in full power 17337c478bd9Sstevel@tonic-gate * during system shutdown. If we are disconnected 17347c478bd9Sstevel@tonic-gate * return success. Also, we should not change state 17357c478bd9Sstevel@tonic-gate * when we are disconnected or suspended or about to 17367c478bd9Sstevel@tonic-gate * transition to that state 17377c478bd9Sstevel@tonic-gate */ 17387c478bd9Sstevel@tonic-gate 17397c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 17407c478bd9Sstevel@tonic-gate default: 17417c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 17427c478bd9Sstevel@tonic-gate "hubd_pwrlvl3: Illegal dev_state=%d", hubd->h_dev_state); 17437c478bd9Sstevel@tonic-gate 17447c478bd9Sstevel@tonic-gate return (USB_FAILURE); 17457c478bd9Sstevel@tonic-gate } 17467c478bd9Sstevel@tonic-gate } 17477c478bd9Sstevel@tonic-gate 17487c478bd9Sstevel@tonic-gate 17497c478bd9Sstevel@tonic-gate /* power entry point */ 17507c478bd9Sstevel@tonic-gate /* ARGSUSED */ 17517c478bd9Sstevel@tonic-gate int 17527c478bd9Sstevel@tonic-gate usba_hubdi_power(dev_info_t *dip, int comp, int level) 17537c478bd9Sstevel@tonic-gate { 17547c478bd9Sstevel@tonic-gate hubd_t *hubd; 17557c478bd9Sstevel@tonic-gate hub_power_t *hubpm; 17567c478bd9Sstevel@tonic-gate int retval; 17577c478bd9Sstevel@tonic-gate int circ; 17587c478bd9Sstevel@tonic-gate 17597c478bd9Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 17607c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle, 17617c478bd9Sstevel@tonic-gate "usba_hubdi_power: level=%d", level); 17627c478bd9Sstevel@tonic-gate 17637c478bd9Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 17647c478bd9Sstevel@tonic-gate 17657c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 17667c478bd9Sstevel@tonic-gate hubpm = hubd->h_hubpm; 17677c478bd9Sstevel@tonic-gate 17687c478bd9Sstevel@tonic-gate /* check if we are transitioning to a legal power level */ 17697c478bd9Sstevel@tonic-gate if (USB_DEV_PWRSTATE_OK(hubpm->hubp_pwr_states, level)) { 17707c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HUBDI, hubd->h_log_handle, 17717c478bd9Sstevel@tonic-gate "usba_hubdi_power: illegal power level=%d " 17727c478bd9Sstevel@tonic-gate "hubp_pwr_states=0x%x", level, hubpm->hubp_pwr_states); 17737c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 17747c478bd9Sstevel@tonic-gate 17757c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 17767c478bd9Sstevel@tonic-gate 17777c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 17787c478bd9Sstevel@tonic-gate } 17797c478bd9Sstevel@tonic-gate 17807c478bd9Sstevel@tonic-gate switch (level) { 17817c478bd9Sstevel@tonic-gate case USB_DEV_OS_PWR_OFF: 17827c478bd9Sstevel@tonic-gate retval = hubd_pwrlvl0(hubd); 17837c478bd9Sstevel@tonic-gate 17847c478bd9Sstevel@tonic-gate break; 17857c478bd9Sstevel@tonic-gate case USB_DEV_OS_PWR_1: 17867c478bd9Sstevel@tonic-gate retval = hubd_pwrlvl1(hubd); 17877c478bd9Sstevel@tonic-gate 17887c478bd9Sstevel@tonic-gate break; 17897c478bd9Sstevel@tonic-gate case USB_DEV_OS_PWR_2: 17907c478bd9Sstevel@tonic-gate retval = hubd_pwrlvl2(hubd); 17917c478bd9Sstevel@tonic-gate 17927c478bd9Sstevel@tonic-gate break; 17937c478bd9Sstevel@tonic-gate case USB_DEV_OS_FULL_PWR: 17947c478bd9Sstevel@tonic-gate retval = hubd_pwrlvl3(hubd); 17957c478bd9Sstevel@tonic-gate 17960d2006e4SRobert Mustacchi break; 17970d2006e4SRobert Mustacchi default: 17980d2006e4SRobert Mustacchi retval = USB_FAILURE; 17990d2006e4SRobert Mustacchi 18007c478bd9Sstevel@tonic-gate break; 18017c478bd9Sstevel@tonic-gate } 18027c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 18037c478bd9Sstevel@tonic-gate 18047c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 18057c478bd9Sstevel@tonic-gate 18067c478bd9Sstevel@tonic-gate return ((retval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE); 18077c478bd9Sstevel@tonic-gate } 18087c478bd9Sstevel@tonic-gate 18097c478bd9Sstevel@tonic-gate 18107c478bd9Sstevel@tonic-gate /* power entry point for the root hub */ 18117c478bd9Sstevel@tonic-gate int 18127c478bd9Sstevel@tonic-gate usba_hubdi_root_hub_power(dev_info_t *dip, int comp, int level) 18137c478bd9Sstevel@tonic-gate { 18147c478bd9Sstevel@tonic-gate return (usba_hubdi_power(dip, comp, level)); 18157c478bd9Sstevel@tonic-gate } 18167c478bd9Sstevel@tonic-gate 18177c478bd9Sstevel@tonic-gate 18187c478bd9Sstevel@tonic-gate /* 18197c478bd9Sstevel@tonic-gate * standard driver entry points support code 18207c478bd9Sstevel@tonic-gate */ 18217c478bd9Sstevel@tonic-gate int 18227c478bd9Sstevel@tonic-gate usba_hubdi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 18237c478bd9Sstevel@tonic-gate { 18247c478bd9Sstevel@tonic-gate int instance = ddi_get_instance(dip); 18257c478bd9Sstevel@tonic-gate hubd_t *hubd = NULL; 18267c478bd9Sstevel@tonic-gate int i, rval; 18277c478bd9Sstevel@tonic-gate int minor; 1828112cd14aSqz uint8_t ports_count; 18297c478bd9Sstevel@tonic-gate char *log_name = NULL; 18307c478bd9Sstevel@tonic-gate const char *root_hub_drvname; 18317c478bd9Sstevel@tonic-gate usb_ep_data_t *ep_data; 18327c478bd9Sstevel@tonic-gate usba_device_t *child_ud = NULL; 18337c478bd9Sstevel@tonic-gate usb_dev_descr_t *usb_dev_descr; 18347c478bd9Sstevel@tonic-gate usb_port_status_t parent_port_status, child_port_status; 18357c478bd9Sstevel@tonic-gate 18367c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubdi_log_handle, 1837c0f24e5bSlg "hubd_attach instance %d, cmd=0x%x", instance, cmd); 18387c478bd9Sstevel@tonic-gate 18397c478bd9Sstevel@tonic-gate switch (cmd) { 18407c478bd9Sstevel@tonic-gate case DDI_ATTACH: 18417c478bd9Sstevel@tonic-gate 18427c478bd9Sstevel@tonic-gate break; 18437c478bd9Sstevel@tonic-gate case DDI_RESUME: 18447c478bd9Sstevel@tonic-gate hubd_cpr_resume(dip); 18457c478bd9Sstevel@tonic-gate 18467c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 18477c478bd9Sstevel@tonic-gate default: 18487c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 18497c478bd9Sstevel@tonic-gate } 18507c478bd9Sstevel@tonic-gate 18517c478bd9Sstevel@tonic-gate /* 18527c478bd9Sstevel@tonic-gate * Allocate softc information. 18537c478bd9Sstevel@tonic-gate */ 18547c478bd9Sstevel@tonic-gate if (usba_is_root_hub(dip)) { 18557c478bd9Sstevel@tonic-gate /* soft state has already been allocated */ 18567c478bd9Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 18577c478bd9Sstevel@tonic-gate minor = HUBD_IS_ROOT_HUB; 18587c478bd9Sstevel@tonic-gate 18597c478bd9Sstevel@tonic-gate /* generate readable labels for different root hubs */ 18607c478bd9Sstevel@tonic-gate root_hub_drvname = ddi_driver_name(dip); 1861993e3fafSRobert Mustacchi if (strcmp(root_hub_drvname, "xhci") == 0) { 1862993e3fafSRobert Mustacchi log_name = "xusb"; 1863993e3fafSRobert Mustacchi } else if (strcmp(root_hub_drvname, "ehci") == 0) { 18647c478bd9Sstevel@tonic-gate log_name = "eusb"; 18657c478bd9Sstevel@tonic-gate } else if (strcmp(root_hub_drvname, "uhci") == 0) { 18667c478bd9Sstevel@tonic-gate log_name = "uusb"; 18677c478bd9Sstevel@tonic-gate } else { 18687c478bd9Sstevel@tonic-gate /* std. for ohci */ 18697c478bd9Sstevel@tonic-gate log_name = "usb"; 18707c478bd9Sstevel@tonic-gate } 18717c478bd9Sstevel@tonic-gate } else { 18727c478bd9Sstevel@tonic-gate rval = ddi_soft_state_zalloc(hubd_statep, instance); 18737c478bd9Sstevel@tonic-gate minor = 0; 18747c478bd9Sstevel@tonic-gate 18757c478bd9Sstevel@tonic-gate if (rval != DDI_SUCCESS) { 18767c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle, 18777c478bd9Sstevel@tonic-gate "cannot allocate soft state (%d)", instance); 18787c478bd9Sstevel@tonic-gate goto fail; 18797c478bd9Sstevel@tonic-gate } 18807c478bd9Sstevel@tonic-gate 18817c478bd9Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 18827c478bd9Sstevel@tonic-gate if (hubd == NULL) { 18837c478bd9Sstevel@tonic-gate goto fail; 18847c478bd9Sstevel@tonic-gate } 18857c478bd9Sstevel@tonic-gate } 18867c478bd9Sstevel@tonic-gate 18877c478bd9Sstevel@tonic-gate hubd->h_log_handle = usb_alloc_log_hdl(dip, log_name, &hubd_errlevel, 1888c0f24e5bSlg &hubd_errmask, &hubd_instance_debug, 0); 18897c478bd9Sstevel@tonic-gate 18907c478bd9Sstevel@tonic-gate hubd->h_usba_device = child_ud = usba_get_usba_device(dip); 18917c478bd9Sstevel@tonic-gate hubd->h_dip = dip; 18927c478bd9Sstevel@tonic-gate hubd->h_instance = instance; 18937c478bd9Sstevel@tonic-gate 18947c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 18957c478bd9Sstevel@tonic-gate child_port_status = child_ud->usb_port_status; 18967c478bd9Sstevel@tonic-gate usb_dev_descr = child_ud->usb_dev_descr; 18977c478bd9Sstevel@tonic-gate parent_port_status = (child_ud->usb_hs_hub_usba_dev) ? 18987c478bd9Sstevel@tonic-gate child_ud->usb_hs_hub_usba_dev->usb_port_status : 0; 18997c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 19007c478bd9Sstevel@tonic-gate 19017c478bd9Sstevel@tonic-gate if ((child_port_status == USBA_FULL_SPEED_DEV) && 1902993e3fafSRobert Mustacchi (parent_port_status >= USBA_HIGH_SPEED_DEV) && 19037c478bd9Sstevel@tonic-gate (usb_dev_descr->bcdUSB == 0x100)) { 19047c478bd9Sstevel@tonic-gate USB_DPRINTF_L0(DPRINT_MASK_ATTA, hubd->h_log_handle, 1905993e3fafSRobert Mustacchi "Use of a USB1.0 hub behind a higher speed port may " 19067c478bd9Sstevel@tonic-gate "cause unexpected failures"); 19077c478bd9Sstevel@tonic-gate } 19087c478bd9Sstevel@tonic-gate 19097c478bd9Sstevel@tonic-gate hubd->h_pipe_policy.pp_max_async_reqs = 1; 19107c478bd9Sstevel@tonic-gate 19117c478bd9Sstevel@tonic-gate /* register with USBA as client driver */ 19127c478bd9Sstevel@tonic-gate if (usb_client_attach(dip, USBDRV_VERSION, 0) != USB_SUCCESS) { 19137c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 19147c478bd9Sstevel@tonic-gate "client attach failed"); 19157c478bd9Sstevel@tonic-gate 19167c478bd9Sstevel@tonic-gate goto fail; 19177c478bd9Sstevel@tonic-gate } 19187c478bd9Sstevel@tonic-gate 19197c478bd9Sstevel@tonic-gate if (usb_get_dev_data(dip, &hubd->h_dev_data, 19207c478bd9Sstevel@tonic-gate USB_PARSE_LVL_IF, 0) != USB_SUCCESS) { 19217c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 19227c478bd9Sstevel@tonic-gate "cannot get dev_data"); 19237c478bd9Sstevel@tonic-gate 19247c478bd9Sstevel@tonic-gate goto fail; 19257c478bd9Sstevel@tonic-gate } 19267c478bd9Sstevel@tonic-gate 19277c478bd9Sstevel@tonic-gate if ((ep_data = usb_lookup_ep_data(dip, hubd->h_dev_data, 19287c478bd9Sstevel@tonic-gate hubd->h_dev_data->dev_curr_if, 0, 0, 19297c478bd9Sstevel@tonic-gate (uint_t)USB_EP_ATTR_INTR, (uint_t)USB_EP_DIR_IN)) == NULL) { 19307c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 19317c478bd9Sstevel@tonic-gate "no interrupt IN endpoint found"); 19327c478bd9Sstevel@tonic-gate 19337c478bd9Sstevel@tonic-gate goto fail; 19347c478bd9Sstevel@tonic-gate } 19357c478bd9Sstevel@tonic-gate 1936993e3fafSRobert Mustacchi if (usb_ep_xdescr_fill(USB_EP_XDESCR_CURRENT_VERSION, dip, ep_data, 1937993e3fafSRobert Mustacchi &hubd->h_ep1_xdescr) != USB_SUCCESS) { 1938993e3fafSRobert Mustacchi goto fail; 1939993e3fafSRobert Mustacchi } 1940993e3fafSRobert Mustacchi 19417c478bd9Sstevel@tonic-gate hubd->h_default_pipe = hubd->h_dev_data->dev_default_ph; 19427c478bd9Sstevel@tonic-gate 19437c478bd9Sstevel@tonic-gate mutex_init(HUBD_MUTEX(hubd), NULL, MUTEX_DRIVER, 1944c0f24e5bSlg hubd->h_dev_data->dev_iblock_cookie); 19457c478bd9Sstevel@tonic-gate cv_init(&hubd->h_cv_reset_port, NULL, CV_DRIVER, NULL); 1946ffcd51f3Slg cv_init(&hubd->h_cv_hotplug_dev, NULL, CV_DRIVER, NULL); 19477c478bd9Sstevel@tonic-gate 19487c478bd9Sstevel@tonic-gate hubd->h_init_state |= HUBD_LOCKS_DONE; 19497c478bd9Sstevel@tonic-gate 19507c478bd9Sstevel@tonic-gate usb_free_descr_tree(dip, hubd->h_dev_data); 19517c478bd9Sstevel@tonic-gate 19527c478bd9Sstevel@tonic-gate /* 19537c478bd9Sstevel@tonic-gate * register this hub instance with usba 19547c478bd9Sstevel@tonic-gate */ 19557c478bd9Sstevel@tonic-gate rval = usba_hubdi_register(dip, 0); 19567c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 1957d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 19587c478bd9Sstevel@tonic-gate "usba_hubdi_register failed"); 19597c478bd9Sstevel@tonic-gate goto fail; 19607c478bd9Sstevel@tonic-gate } 19617c478bd9Sstevel@tonic-gate 19627c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 19637c478bd9Sstevel@tonic-gate hubd->h_init_state |= HUBD_HUBDI_REGISTERED; 19647c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_ONLINE; 19657c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 19667c478bd9Sstevel@tonic-gate 19677c478bd9Sstevel@tonic-gate /* now create components to power manage this device */ 19687c478bd9Sstevel@tonic-gate hubd_create_pm_components(dip, hubd); 19697c478bd9Sstevel@tonic-gate 19707c478bd9Sstevel@tonic-gate /* 19717c478bd9Sstevel@tonic-gate * Event handling: definition and registration 19727c478bd9Sstevel@tonic-gate * 19737c478bd9Sstevel@tonic-gate * first the definition: 19747c478bd9Sstevel@tonic-gate * get event handle 19757c478bd9Sstevel@tonic-gate */ 19767c478bd9Sstevel@tonic-gate (void) ndi_event_alloc_hdl(dip, 0, &hubd->h_ndi_event_hdl, NDI_SLEEP); 19777c478bd9Sstevel@tonic-gate 19787c478bd9Sstevel@tonic-gate /* bind event set to the handle */ 19797c478bd9Sstevel@tonic-gate if (ndi_event_bind_set(hubd->h_ndi_event_hdl, &hubd_ndi_events, 19807c478bd9Sstevel@tonic-gate NDI_SLEEP)) { 19817c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle, 19827c478bd9Sstevel@tonic-gate "binding event set failed"); 19837c478bd9Sstevel@tonic-gate 19847c478bd9Sstevel@tonic-gate goto fail; 19857c478bd9Sstevel@tonic-gate } 19867c478bd9Sstevel@tonic-gate 19877c478bd9Sstevel@tonic-gate /* event registration */ 19887c478bd9Sstevel@tonic-gate if (hubd_register_events(hubd) != USB_SUCCESS) { 1989d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 19907c478bd9Sstevel@tonic-gate "hubd_register_events failed"); 19917c478bd9Sstevel@tonic-gate 19927c478bd9Sstevel@tonic-gate goto fail; 19937c478bd9Sstevel@tonic-gate } 19947c478bd9Sstevel@tonic-gate 19957c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 19967c478bd9Sstevel@tonic-gate hubd->h_init_state |= HUBD_EVENTS_REGISTERED; 19977c478bd9Sstevel@tonic-gate 1998993e3fafSRobert Mustacchi if (hubd_get_hub_descriptor(hubd) != USB_SUCCESS) { 199935f36846Ssl mutex_exit(HUBD_MUTEX(hubd)); 200035f36846Ssl 200135f36846Ssl goto fail; 200235f36846Ssl } 200335f36846Ssl 2004993e3fafSRobert Mustacchi /* 2005993e3fafSRobert Mustacchi * Now that we have our descriptors, we need to give the host controller 2006993e3fafSRobert Mustacchi * a chance to properly configure the device if needed (this is required 2007993e3fafSRobert Mustacchi * for xHCI). 2008993e3fafSRobert Mustacchi * 2009993e3fafSRobert Mustacchi * Note, if anyone ever adds support for using the Multi TT mode for USB 2010993e3fafSRobert Mustacchi * 2 High speed Hubs, the xhci driver will need to be updated and that 2011993e3fafSRobert Mustacchi * will need to be done before this is called. 2012993e3fafSRobert Mustacchi */ 2013993e3fafSRobert Mustacchi if (hubd->h_usba_device->usb_hcdi_ops->usba_hcdi_hub_update != NULL && 2014993e3fafSRobert Mustacchi !usba_is_root_hub(dip)) { 2015993e3fafSRobert Mustacchi int ret; 2016993e3fafSRobert Mustacchi uint8_t chars; 2017993e3fafSRobert Mustacchi usba_device_t *ud = hubd->h_usba_device; 2018993e3fafSRobert Mustacchi 2019993e3fafSRobert Mustacchi chars = (hubd->h_hub_chars & HUB_CHARS_TT_THINK_TIME) >> 2020993e3fafSRobert Mustacchi HUB_CHARS_TT_SHIFT; 2021993e3fafSRobert Mustacchi ret = ud->usb_hcdi_ops->usba_hcdi_hub_update(ud, 2022993e3fafSRobert Mustacchi hubd->h_nports, chars); 2023993e3fafSRobert Mustacchi if (ret != USB_SUCCESS) { 2024993e3fafSRobert Mustacchi mutex_exit(HUBD_MUTEX(hubd)); 2025993e3fafSRobert Mustacchi goto fail; 2026993e3fafSRobert Mustacchi } 2027993e3fafSRobert Mustacchi } 2028993e3fafSRobert Mustacchi 2029993e3fafSRobert Mustacchi if (hubd_set_hub_depth(hubd) != USB_SUCCESS) { 2030993e3fafSRobert Mustacchi 2031993e3fafSRobert Mustacchi goto fail; 2032993e3fafSRobert Mustacchi } 2033993e3fafSRobert Mustacchi 203435f36846Ssl if (ddi_prop_exists(DDI_DEV_T_ANY, dip, 203535f36846Ssl (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM), 203635f36846Ssl "hub-ignore-power-budget") == 1) { 203735f36846Ssl hubd->h_ignore_pwr_budget = B_TRUE; 203835f36846Ssl } else { 203935f36846Ssl hubd->h_ignore_pwr_budget = B_FALSE; 204035f36846Ssl 204135f36846Ssl /* initialize hub power budget variables */ 204235f36846Ssl if (hubd_init_power_budget(hubd) != USB_SUCCESS) { 204335f36846Ssl USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 204435f36846Ssl "hubd_init_power_budget failed"); 204535f36846Ssl mutex_exit(HUBD_MUTEX(hubd)); 204635f36846Ssl 204735f36846Ssl goto fail; 204835f36846Ssl } 204935f36846Ssl } 205035f36846Ssl 20517c478bd9Sstevel@tonic-gate /* initialize and create children */ 20527c478bd9Sstevel@tonic-gate if (hubd_check_ports(hubd) != USB_SUCCESS) { 2053d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 20547c478bd9Sstevel@tonic-gate "hubd_check_ports failed"); 20557c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 20567c478bd9Sstevel@tonic-gate 20577c478bd9Sstevel@tonic-gate goto fail; 20587c478bd9Sstevel@tonic-gate } 20597c478bd9Sstevel@tonic-gate 20607c478bd9Sstevel@tonic-gate /* 20617c478bd9Sstevel@tonic-gate * create cfgadm nodes 20627c478bd9Sstevel@tonic-gate */ 20637c478bd9Sstevel@tonic-gate hubd->h_ancestry_str = (char *)kmem_zalloc(HUBD_APID_NAMELEN, KM_SLEEP); 20647c478bd9Sstevel@tonic-gate hubd_get_ancestry_str(hubd); 20657c478bd9Sstevel@tonic-gate 20667c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 2067993e3fafSRobert Mustacchi "#ports=0x%x", hubd->h_nports); 20687c478bd9Sstevel@tonic-gate 2069993e3fafSRobert Mustacchi for (i = 1; i <= hubd->h_nports; i++) { 20707c478bd9Sstevel@tonic-gate char ap_name[HUBD_APID_NAMELEN]; 20717c478bd9Sstevel@tonic-gate 20727c478bd9Sstevel@tonic-gate (void) snprintf(ap_name, HUBD_APID_NAMELEN, "%s%d", 20737c478bd9Sstevel@tonic-gate hubd->h_ancestry_str, i); 20747c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 20757c478bd9Sstevel@tonic-gate "ap_name=%s", ap_name); 20767c478bd9Sstevel@tonic-gate 20777c478bd9Sstevel@tonic-gate if (ddi_create_minor_node(dip, ap_name, S_IFCHR, instance, 20787c478bd9Sstevel@tonic-gate DDI_NT_USB_ATTACHMENT_POINT, 0) != DDI_SUCCESS) { 2079d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 20807c478bd9Sstevel@tonic-gate "cannot create attachment point node (%d)", 20817c478bd9Sstevel@tonic-gate instance); 20827c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 20837c478bd9Sstevel@tonic-gate 20847c478bd9Sstevel@tonic-gate goto fail; 20857c478bd9Sstevel@tonic-gate } 20867c478bd9Sstevel@tonic-gate } 2087112cd14aSqz 2088993e3fafSRobert Mustacchi ports_count = hubd->h_nports; 20897c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 20907c478bd9Sstevel@tonic-gate 20917c478bd9Sstevel@tonic-gate /* create minor nodes */ 20927c478bd9Sstevel@tonic-gate if (ddi_create_minor_node(dip, "hubd", S_IFCHR, 20937c478bd9Sstevel@tonic-gate instance | minor, DDI_NT_NEXUS, 0) != DDI_SUCCESS) { 20947c478bd9Sstevel@tonic-gate 2095d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 20967c478bd9Sstevel@tonic-gate "cannot create devctl minor node (%d)", instance); 20977c478bd9Sstevel@tonic-gate 20987c478bd9Sstevel@tonic-gate goto fail; 20997c478bd9Sstevel@tonic-gate } 21007c478bd9Sstevel@tonic-gate 21017c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 21027c478bd9Sstevel@tonic-gate hubd->h_init_state |= HUBD_MINOR_NODE_CREATED; 21037c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 21047c478bd9Sstevel@tonic-gate 2105112cd14aSqz if (ndi_prop_update_int(DDI_DEV_T_NONE, dip, 2106112cd14aSqz "usb-port-count", ports_count) != DDI_PROP_SUCCESS) { 2107112cd14aSqz USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 2108ff0e937bSRaymond Chen "usb-port-count update failed"); 2109112cd14aSqz } 2110112cd14aSqz 21117c478bd9Sstevel@tonic-gate /* 21127c478bd9Sstevel@tonic-gate * host controller driver has already reported this dev 21137c478bd9Sstevel@tonic-gate * if we are the root hub 21147c478bd9Sstevel@tonic-gate */ 21157c478bd9Sstevel@tonic-gate if (!usba_is_root_hub(dip)) { 21167c478bd9Sstevel@tonic-gate ddi_report_dev(dip); 21177c478bd9Sstevel@tonic-gate } 21187c478bd9Sstevel@tonic-gate 21197c478bd9Sstevel@tonic-gate /* enable deathrow thread */ 21207c478bd9Sstevel@tonic-gate hubd->h_cleanup_enabled = B_TRUE; 21217c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 21227c478bd9Sstevel@tonic-gate hubd_pm_idle_component(hubd, dip, 0); 21237c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 21247c478bd9Sstevel@tonic-gate 21257c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 21267c478bd9Sstevel@tonic-gate 21277c478bd9Sstevel@tonic-gate fail: 21287c478bd9Sstevel@tonic-gate { 21297c478bd9Sstevel@tonic-gate char *pathname = kmem_alloc(MAXPATHLEN, KM_SLEEP); 21307c478bd9Sstevel@tonic-gate 21318668df41Slg USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle, 21327c478bd9Sstevel@tonic-gate "cannot attach %s", ddi_pathname(dip, pathname)); 21337c478bd9Sstevel@tonic-gate 21347c478bd9Sstevel@tonic-gate kmem_free(pathname, MAXPATHLEN); 21357c478bd9Sstevel@tonic-gate } 21367c478bd9Sstevel@tonic-gate 21370d2006e4SRobert Mustacchi if (hubd != NULL) { 21380d2006e4SRobert Mustacchi mutex_enter(HUBD_MUTEX(hubd)); 21390d2006e4SRobert Mustacchi hubd_pm_idle_component(hubd, dip, 0); 21400d2006e4SRobert Mustacchi mutex_exit(HUBD_MUTEX(hubd)); 21417c478bd9Sstevel@tonic-gate 21427c478bd9Sstevel@tonic-gate rval = hubd_cleanup(dip, hubd); 21437c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 2144d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle, 21457c478bd9Sstevel@tonic-gate "failure to complete cleanup after attach failure"); 21467c478bd9Sstevel@tonic-gate } 21477c478bd9Sstevel@tonic-gate } 21487c478bd9Sstevel@tonic-gate 21497c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 21507c478bd9Sstevel@tonic-gate } 21517c478bd9Sstevel@tonic-gate 21527c478bd9Sstevel@tonic-gate 21537c478bd9Sstevel@tonic-gate int 21547c478bd9Sstevel@tonic-gate usba_hubdi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 21557c478bd9Sstevel@tonic-gate { 21567c478bd9Sstevel@tonic-gate hubd_t *hubd = hubd_get_soft_state(dip); 21577c478bd9Sstevel@tonic-gate int rval; 21587c478bd9Sstevel@tonic-gate 21597c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 21607c478bd9Sstevel@tonic-gate "hubd_detach: cmd=0x%x", cmd); 21617c478bd9Sstevel@tonic-gate 21627c478bd9Sstevel@tonic-gate switch (cmd) { 21637c478bd9Sstevel@tonic-gate case DDI_DETACH: 21647c478bd9Sstevel@tonic-gate rval = hubd_cleanup(dip, hubd); 21657c478bd9Sstevel@tonic-gate 21667c478bd9Sstevel@tonic-gate return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE); 21677c478bd9Sstevel@tonic-gate case DDI_SUSPEND: 21687c478bd9Sstevel@tonic-gate rval = hubd_cpr_suspend(hubd); 21697c478bd9Sstevel@tonic-gate 21707c478bd9Sstevel@tonic-gate return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE); 21717c478bd9Sstevel@tonic-gate default: 21727c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 21737c478bd9Sstevel@tonic-gate } 21747c478bd9Sstevel@tonic-gate } 21757c478bd9Sstevel@tonic-gate 21767c478bd9Sstevel@tonic-gate 21777c478bd9Sstevel@tonic-gate /* 21787c478bd9Sstevel@tonic-gate * hubd_setdevaddr 21797c478bd9Sstevel@tonic-gate * set the device addrs on this port 21807c478bd9Sstevel@tonic-gate */ 21817c478bd9Sstevel@tonic-gate static int 21827c478bd9Sstevel@tonic-gate hubd_setdevaddr(hubd_t *hubd, usb_port_t port) 21837c478bd9Sstevel@tonic-gate { 21840d2006e4SRobert Mustacchi int rval = USB_FAILURE; 21857c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 21867c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 21877c478bd9Sstevel@tonic-gate usb_pipe_handle_t ph; 21887c478bd9Sstevel@tonic-gate dev_info_t *child_dip = NULL; 21897c478bd9Sstevel@tonic-gate uchar_t address = 0; 21907c478bd9Sstevel@tonic-gate usba_device_t *usba_device; 21917c478bd9Sstevel@tonic-gate int retry = 0; 21927c478bd9Sstevel@tonic-gate long time_delay; 21937c478bd9Sstevel@tonic-gate 21947c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 21957c478bd9Sstevel@tonic-gate "hubd_setdevaddr: port=%d", port); 21967c478bd9Sstevel@tonic-gate 21977c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 21987c478bd9Sstevel@tonic-gate 21997c478bd9Sstevel@tonic-gate child_dip = hubd->h_children_dips[port]; 22007c478bd9Sstevel@tonic-gate address = hubd->h_usba_devices[port]->usb_addr; 22017c478bd9Sstevel@tonic-gate usba_device = hubd->h_usba_devices[port]; 22027c478bd9Sstevel@tonic-gate 22037c478bd9Sstevel@tonic-gate /* close the default pipe with addr x */ 22047c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 22057c478bd9Sstevel@tonic-gate ph = usba_get_dflt_pipe_handle(child_dip); 22067c478bd9Sstevel@tonic-gate usb_pipe_close(child_dip, ph, 22077c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL); 22087c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 22097c478bd9Sstevel@tonic-gate 2210993e3fafSRobert Mustacchi /* 2211993e3fafSRobert Mustacchi * If the host controller is in charge of addressing, have it do that 2212993e3fafSRobert Mustacchi * now and skip everything else. 2213993e3fafSRobert Mustacchi */ 2214993e3fafSRobert Mustacchi if (usba_device->usb_hcdi_ops->usba_hcdi_device_address != NULL) { 2215993e3fafSRobert Mustacchi mutex_exit(HUBD_MUTEX(hubd)); 2216993e3fafSRobert Mustacchi rval = usba_device->usb_hcdi_ops->usba_hcdi_device_address( 2217993e3fafSRobert Mustacchi usba_device); 2218993e3fafSRobert Mustacchi mutex_enter(HUBD_MUTEX(hubd)); 2219993e3fafSRobert Mustacchi 2220993e3fafSRobert Mustacchi usba_clear_data_toggle(usba_device); 2221993e3fafSRobert Mustacchi return (rval); 2222993e3fafSRobert Mustacchi } 2223993e3fafSRobert Mustacchi 22247c478bd9Sstevel@tonic-gate /* 22257c478bd9Sstevel@tonic-gate * As this device has been reset, temporarily 22267c478bd9Sstevel@tonic-gate * assign the default address 22277c478bd9Sstevel@tonic-gate */ 22287c478bd9Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex); 22297c478bd9Sstevel@tonic-gate address = usba_device->usb_addr; 22307c478bd9Sstevel@tonic-gate usba_device->usb_addr = USBA_DEFAULT_ADDR; 22317c478bd9Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex); 22327c478bd9Sstevel@tonic-gate 22337c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 22347c478bd9Sstevel@tonic-gate 22357c478bd9Sstevel@tonic-gate time_delay = drv_usectohz(hubd_device_delay / 20); 22367c478bd9Sstevel@tonic-gate for (retry = 0; retry < hubd_retry_enumerate; retry++) { 22377c478bd9Sstevel@tonic-gate 22387c478bd9Sstevel@tonic-gate /* open child's default pipe with USBA_DEFAULT_ADDR */ 22390d2006e4SRobert Mustacchi if ((rval = usb_pipe_open(child_dip, NULL, NULL, 22400d2006e4SRobert Mustacchi USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) != 22417c478bd9Sstevel@tonic-gate USB_SUCCESS) { 22427c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 22437c478bd9Sstevel@tonic-gate "hubd_setdevaddr: Unable to open default pipe"); 22447c478bd9Sstevel@tonic-gate 22457c478bd9Sstevel@tonic-gate break; 22467c478bd9Sstevel@tonic-gate } 22477c478bd9Sstevel@tonic-gate 22487c478bd9Sstevel@tonic-gate /* Set the address of the device */ 22497c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 22507c478bd9Sstevel@tonic-gate USB_DEV_REQ_HOST_TO_DEV, 22517c478bd9Sstevel@tonic-gate USB_REQ_SET_ADDRESS, /* bRequest */ 22527c478bd9Sstevel@tonic-gate address, /* wValue */ 22537c478bd9Sstevel@tonic-gate 0, /* wIndex */ 22547c478bd9Sstevel@tonic-gate 0, /* wLength */ 22557c478bd9Sstevel@tonic-gate NULL, 0, 22567c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 22577c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 22587c478bd9Sstevel@tonic-gate "hubd_setdevaddr(%d): rval=%d cr=%d cb_fl=0x%x", 22597c478bd9Sstevel@tonic-gate retry, rval, completion_reason, cb_flags); 22607c478bd9Sstevel@tonic-gate } 22617c478bd9Sstevel@tonic-gate 22627c478bd9Sstevel@tonic-gate usb_pipe_close(child_dip, ph, 22637c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL); 22647c478bd9Sstevel@tonic-gate 22657c478bd9Sstevel@tonic-gate if (rval == USB_SUCCESS) { 22667c478bd9Sstevel@tonic-gate 22677c478bd9Sstevel@tonic-gate break; 22687c478bd9Sstevel@tonic-gate } 22697c478bd9Sstevel@tonic-gate 22707c478bd9Sstevel@tonic-gate delay(time_delay); 22717c478bd9Sstevel@tonic-gate } 22727c478bd9Sstevel@tonic-gate 22737c478bd9Sstevel@tonic-gate /* Reset to the old address */ 22747c478bd9Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex); 22757c478bd9Sstevel@tonic-gate usba_device->usb_addr = address; 22767c478bd9Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex); 22777c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 22787c478bd9Sstevel@tonic-gate 22797c478bd9Sstevel@tonic-gate usba_clear_data_toggle(usba_device); 22807c478bd9Sstevel@tonic-gate 22817c478bd9Sstevel@tonic-gate return (rval); 22827c478bd9Sstevel@tonic-gate } 22837c478bd9Sstevel@tonic-gate 22847c478bd9Sstevel@tonic-gate 22857c478bd9Sstevel@tonic-gate /* 22867c478bd9Sstevel@tonic-gate * hubd_setdevconfig 22877c478bd9Sstevel@tonic-gate * set the device addrs on this port 22887c478bd9Sstevel@tonic-gate */ 22897c478bd9Sstevel@tonic-gate static void 22907c478bd9Sstevel@tonic-gate hubd_setdevconfig(hubd_t *hubd, usb_port_t port) 22917c478bd9Sstevel@tonic-gate { 22927c478bd9Sstevel@tonic-gate int rval; 22937c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 22947c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 22957c478bd9Sstevel@tonic-gate usb_pipe_handle_t ph; 22967c478bd9Sstevel@tonic-gate dev_info_t *child_dip = NULL; 22977c478bd9Sstevel@tonic-gate usba_device_t *usba_device = NULL; 22987c478bd9Sstevel@tonic-gate uint16_t config_value; 22997c478bd9Sstevel@tonic-gate 23007c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 23017c478bd9Sstevel@tonic-gate "hubd_setdevconfig: port=%d", port); 23027c478bd9Sstevel@tonic-gate 23037c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 23047c478bd9Sstevel@tonic-gate 23057c478bd9Sstevel@tonic-gate child_dip = hubd->h_children_dips[port]; 23067c478bd9Sstevel@tonic-gate usba_device = hubd->h_usba_devices[port]; 23077c478bd9Sstevel@tonic-gate config_value = hubd->h_usba_devices[port]->usb_cfg_value; 23087c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 23097c478bd9Sstevel@tonic-gate 23107c478bd9Sstevel@tonic-gate /* open the default control pipe */ 23117c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_open(child_dip, NULL, NULL, 23127c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) == 23137c478bd9Sstevel@tonic-gate USB_SUCCESS) { 23147c478bd9Sstevel@tonic-gate 23157c478bd9Sstevel@tonic-gate /* Set the default configuration of the device */ 23167c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 23177c478bd9Sstevel@tonic-gate USB_DEV_REQ_HOST_TO_DEV, 23187c478bd9Sstevel@tonic-gate USB_REQ_SET_CFG, /* bRequest */ 23197c478bd9Sstevel@tonic-gate config_value, /* wValue */ 23207c478bd9Sstevel@tonic-gate 0, /* wIndex */ 23217c478bd9Sstevel@tonic-gate 0, /* wLength */ 23227c478bd9Sstevel@tonic-gate NULL, 0, 23237c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 23247c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 23257c478bd9Sstevel@tonic-gate "hubd_setdevconfig: set device config failed: " 23267c478bd9Sstevel@tonic-gate "cr=%d cb_fl=0x%x rval=%d", 23277c478bd9Sstevel@tonic-gate completion_reason, cb_flags, rval); 23287c478bd9Sstevel@tonic-gate } 23297c478bd9Sstevel@tonic-gate /* 23307c478bd9Sstevel@tonic-gate * After setting the configuration, we make this default 23317c478bd9Sstevel@tonic-gate * control pipe persistent, so that it gets re-opened 23327c478bd9Sstevel@tonic-gate * on posting a connect event 23337c478bd9Sstevel@tonic-gate */ 23347c478bd9Sstevel@tonic-gate usba_persistent_pipe_close(usba_device); 23357c478bd9Sstevel@tonic-gate } else { 23367c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 23377c478bd9Sstevel@tonic-gate "pipe open fails: rval=%d", rval); 23387c478bd9Sstevel@tonic-gate } 23397c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 23407c478bd9Sstevel@tonic-gate } 23417c478bd9Sstevel@tonic-gate 23427c478bd9Sstevel@tonic-gate 23437c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 23447c478bd9Sstevel@tonic-gate static int 23457c478bd9Sstevel@tonic-gate hubd_check_disconnected_ports(dev_info_t *dip, void *arg) 23467c478bd9Sstevel@tonic-gate { 23477c478bd9Sstevel@tonic-gate int circ; 23487c478bd9Sstevel@tonic-gate usb_port_t port; 23497c478bd9Sstevel@tonic-gate hubd_t *hubd; 23507c478bd9Sstevel@tonic-gate major_t hub_major = ddi_name_to_major("hubd"); 2351ff0e937bSRaymond Chen major_t hwahc_major = ddi_name_to_major("hwahc"); 2352ff0e937bSRaymond Chen major_t usbmid_major = ddi_name_to_major("usb_mid"); 23537c478bd9Sstevel@tonic-gate 23547c478bd9Sstevel@tonic-gate /* 23557c478bd9Sstevel@tonic-gate * make sure dip is a usb hub, major of root hub is HCD 23567c478bd9Sstevel@tonic-gate * major 23577c478bd9Sstevel@tonic-gate */ 23587c478bd9Sstevel@tonic-gate if (!usba_is_root_hub(dip)) { 2359ff0e937bSRaymond Chen if (ddi_driver_major(dip) == usbmid_major) { 2360ff0e937bSRaymond Chen /* 2361ff0e937bSRaymond Chen * need to walk the children since it might be a 2362ff0e937bSRaymond Chen * HWA device 2363ff0e937bSRaymond Chen */ 2364ff0e937bSRaymond Chen 2365ff0e937bSRaymond Chen return (DDI_WALK_CONTINUE); 2366ff0e937bSRaymond Chen } 2367ff0e937bSRaymond Chen 2368ff0e937bSRaymond Chen /* TODO: DWA device may also need special handling */ 2369ff0e937bSRaymond Chen 2370ff0e937bSRaymond Chen if (((ddi_driver_major(dip) != hub_major) && 2371ff0e937bSRaymond Chen (ddi_driver_major(dip) != hwahc_major)) || 2372737d277aScth !i_ddi_devi_attached(dip)) { 23737c478bd9Sstevel@tonic-gate 23747c478bd9Sstevel@tonic-gate return (DDI_WALK_PRUNECHILD); 23757c478bd9Sstevel@tonic-gate } 23767c478bd9Sstevel@tonic-gate } 23777c478bd9Sstevel@tonic-gate 23787c478bd9Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 23797c478bd9Sstevel@tonic-gate if (hubd == NULL) { 23807c478bd9Sstevel@tonic-gate 23817c478bd9Sstevel@tonic-gate return (DDI_WALK_PRUNECHILD); 23827c478bd9Sstevel@tonic-gate } 23837c478bd9Sstevel@tonic-gate 23847c478bd9Sstevel@tonic-gate /* walk child list and remove nodes with flag DEVI_DEVICE_REMOVED */ 23857c478bd9Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 23867c478bd9Sstevel@tonic-gate 2387ff0e937bSRaymond Chen if (ddi_driver_major(dip) != hwahc_major) { 2388ff0e937bSRaymond Chen /* for normal usb hub or root hub */ 2389ff0e937bSRaymond Chen mutex_enter(HUBD_MUTEX(hubd)); 2390993e3fafSRobert Mustacchi for (port = 1; port <= hubd->h_nports; port++) { 2391ff0e937bSRaymond Chen dev_info_t *cdip = hubd->h_children_dips[port]; 23927c478bd9Sstevel@tonic-gate 2393ff0e937bSRaymond Chen if (cdip == NULL || DEVI_IS_DEVICE_REMOVED(cdip) == 0) { 23947c478bd9Sstevel@tonic-gate 2395ff0e937bSRaymond Chen continue; 2396ff0e937bSRaymond Chen } 2397ff0e937bSRaymond Chen 2398ff0e937bSRaymond Chen (void) hubd_delete_child(hubd, port, NDI_DEVI_REMOVE, 2399ff0e937bSRaymond Chen B_TRUE); 24007c478bd9Sstevel@tonic-gate } 2401ff0e937bSRaymond Chen mutex_exit(HUBD_MUTEX(hubd)); 2402ff0e937bSRaymond Chen } else { 2403ff0e937bSRaymond Chen /* for HWA */ 2404ff0e937bSRaymond Chen if (hubd->h_cleanup_child != NULL) { 2405ff0e937bSRaymond Chen if (hubd->h_cleanup_child(dip) != USB_SUCCESS) { 2406ff0e937bSRaymond Chen ndi_devi_exit(dip, circ); 2407ff0e937bSRaymond Chen 2408ff0e937bSRaymond Chen return (DDI_WALK_PRUNECHILD); 2409ff0e937bSRaymond Chen } 2410ff0e937bSRaymond Chen } else { 2411ff0e937bSRaymond Chen ndi_devi_exit(dip, circ); 24127c478bd9Sstevel@tonic-gate 2413ff0e937bSRaymond Chen return (DDI_WALK_PRUNECHILD); 2414ff0e937bSRaymond Chen } 24157c478bd9Sstevel@tonic-gate } 2416ff0e937bSRaymond Chen 24177c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 24187c478bd9Sstevel@tonic-gate 24197c478bd9Sstevel@tonic-gate /* skip siblings of root hub */ 24207c478bd9Sstevel@tonic-gate if (usba_is_root_hub(dip)) { 24217c478bd9Sstevel@tonic-gate 24227c478bd9Sstevel@tonic-gate return (DDI_WALK_PRUNESIB); 24237c478bd9Sstevel@tonic-gate } 24247c478bd9Sstevel@tonic-gate 24257c478bd9Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 24267c478bd9Sstevel@tonic-gate } 24277c478bd9Sstevel@tonic-gate 24287c478bd9Sstevel@tonic-gate 24297c478bd9Sstevel@tonic-gate /* 24307c478bd9Sstevel@tonic-gate * this thread will walk all children under the root hub for this 24317c478bd9Sstevel@tonic-gate * USB bus instance and attempt to remove them 24327c478bd9Sstevel@tonic-gate */ 24337c478bd9Sstevel@tonic-gate static void 24347c478bd9Sstevel@tonic-gate hubd_root_hub_cleanup_thread(void *arg) 24357c478bd9Sstevel@tonic-gate { 24367c478bd9Sstevel@tonic-gate int circ; 24377c478bd9Sstevel@tonic-gate hubd_t *root_hubd = (hubd_t *)arg; 24387c478bd9Sstevel@tonic-gate dev_info_t *rh_dip = root_hubd->h_dip; 24397c478bd9Sstevel@tonic-gate #ifndef __lock_lint 24407c478bd9Sstevel@tonic-gate callb_cpr_t cprinfo; 24417c478bd9Sstevel@tonic-gate 24427c478bd9Sstevel@tonic-gate CALLB_CPR_INIT(&cprinfo, HUBD_MUTEX(root_hubd), callb_generic_cpr, 24437c478bd9Sstevel@tonic-gate "USB root hub"); 24447c478bd9Sstevel@tonic-gate #endif 24457c478bd9Sstevel@tonic-gate 24467c478bd9Sstevel@tonic-gate for (;;) { 24477c478bd9Sstevel@tonic-gate /* don't race with detach */ 24487c478bd9Sstevel@tonic-gate ndi_hold_devi(rh_dip); 24497c478bd9Sstevel@tonic-gate 24507c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(root_hubd)); 24517c478bd9Sstevel@tonic-gate root_hubd->h_cleanup_needed = 0; 24527c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd)); 24537c478bd9Sstevel@tonic-gate 24547c478bd9Sstevel@tonic-gate (void) devfs_clean(rh_dip, NULL, 0); 24557c478bd9Sstevel@tonic-gate 24567c478bd9Sstevel@tonic-gate ndi_devi_enter(ddi_get_parent(rh_dip), &circ); 24577c478bd9Sstevel@tonic-gate ddi_walk_devs(rh_dip, hubd_check_disconnected_ports, 2458c0f24e5bSlg NULL); 24597c478bd9Sstevel@tonic-gate #ifdef __lock_lint 24607c478bd9Sstevel@tonic-gate (void) hubd_check_disconnected_ports(rh_dip, NULL); 24617c478bd9Sstevel@tonic-gate #endif 24627c478bd9Sstevel@tonic-gate ndi_devi_exit(ddi_get_parent(rh_dip), circ); 24637c478bd9Sstevel@tonic-gate 24647c478bd9Sstevel@tonic-gate /* quit if we are not enabled anymore */ 24657c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(root_hubd)); 24667c478bd9Sstevel@tonic-gate if ((root_hubd->h_cleanup_enabled == B_FALSE) || 24677c478bd9Sstevel@tonic-gate (root_hubd->h_cleanup_needed == B_FALSE)) { 24687c478bd9Sstevel@tonic-gate root_hubd->h_cleanup_active = B_FALSE; 24697c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd)); 24707c478bd9Sstevel@tonic-gate ndi_rele_devi(rh_dip); 24717c478bd9Sstevel@tonic-gate 24727c478bd9Sstevel@tonic-gate break; 24737c478bd9Sstevel@tonic-gate } 24747c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd)); 24757c478bd9Sstevel@tonic-gate ndi_rele_devi(rh_dip); 24767c478bd9Sstevel@tonic-gate 24777c478bd9Sstevel@tonic-gate #ifndef __lock_lint 24787c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(root_hubd)); 24797c478bd9Sstevel@tonic-gate CALLB_CPR_SAFE_BEGIN(&cprinfo); 24807c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd)); 24817c478bd9Sstevel@tonic-gate 24827c478bd9Sstevel@tonic-gate delay(drv_usectohz(hubd_dip_cleanup_delay)); 24837c478bd9Sstevel@tonic-gate 24847c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(root_hubd)); 24857c478bd9Sstevel@tonic-gate CALLB_CPR_SAFE_END(&cprinfo, HUBD_MUTEX(root_hubd)); 24867c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd)); 24877c478bd9Sstevel@tonic-gate #endif 24887c478bd9Sstevel@tonic-gate } 24897c478bd9Sstevel@tonic-gate 24907c478bd9Sstevel@tonic-gate #ifndef __lock_lint 24917c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(root_hubd)); 24927c478bd9Sstevel@tonic-gate CALLB_CPR_EXIT(&cprinfo); 24937c478bd9Sstevel@tonic-gate #endif 24947c478bd9Sstevel@tonic-gate } 24957c478bd9Sstevel@tonic-gate 24967c478bd9Sstevel@tonic-gate 2497ff0e937bSRaymond Chen void 24987c478bd9Sstevel@tonic-gate hubd_schedule_cleanup(dev_info_t *rh_dip) 24997c478bd9Sstevel@tonic-gate { 2500ff0e937bSRaymond Chen hubd_t *root_hubd; 2501ff0e937bSRaymond Chen 2502ff0e937bSRaymond Chen /* 2503ff0e937bSRaymond Chen * The usb_root_hub_dip pointer for the child hub of the WUSB 2504ff0e937bSRaymond Chen * wire adapter class device points to the wire adapter, not 2505ff0e937bSRaymond Chen * the root hub. Need to find the real root hub dip so that 2506ff0e937bSRaymond Chen * the cleanup thread only starts from the root hub. 2507ff0e937bSRaymond Chen */ 2508ff0e937bSRaymond Chen while (!usba_is_root_hub(rh_dip)) { 2509ff0e937bSRaymond Chen root_hubd = hubd_get_soft_state(rh_dip); 2510ff0e937bSRaymond Chen if (root_hubd != NULL) { 2511ff0e937bSRaymond Chen rh_dip = root_hubd->h_usba_device->usb_root_hub_dip; 2512ff0e937bSRaymond Chen if (rh_dip == NULL) { 2513ff0e937bSRaymond Chen USB_DPRINTF_L2(DPRINT_MASK_ATTA, 2514ff0e937bSRaymond Chen root_hubd->h_log_handle, 2515ff0e937bSRaymond Chen "hubd_schedule_cleanup: null rh dip"); 2516ff0e937bSRaymond Chen 2517ff0e937bSRaymond Chen return; 2518ff0e937bSRaymond Chen } 2519ff0e937bSRaymond Chen } else { 2520ff0e937bSRaymond Chen USB_DPRINTF_L2(DPRINT_MASK_ATTA, 2521ff0e937bSRaymond Chen root_hubd->h_log_handle, 2522ff0e937bSRaymond Chen "hubd_schedule_cleanup: cannot find root hub"); 2523ff0e937bSRaymond Chen 2524ff0e937bSRaymond Chen return; 2525ff0e937bSRaymond Chen } 2526ff0e937bSRaymond Chen } 2527ff0e937bSRaymond Chen root_hubd = hubd_get_soft_state(rh_dip); 25287c478bd9Sstevel@tonic-gate 25297c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(root_hubd)); 25307c478bd9Sstevel@tonic-gate root_hubd->h_cleanup_needed = B_TRUE; 25317c478bd9Sstevel@tonic-gate if (root_hubd->h_cleanup_enabled && !(root_hubd->h_cleanup_active)) { 25327c478bd9Sstevel@tonic-gate root_hubd->h_cleanup_active = B_TRUE; 25337c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd)); 25347c478bd9Sstevel@tonic-gate (void) thread_create(NULL, 0, 25357c478bd9Sstevel@tonic-gate hubd_root_hub_cleanup_thread, 25367c478bd9Sstevel@tonic-gate (void *)root_hubd, 0, &p0, TS_RUN, 25377c478bd9Sstevel@tonic-gate minclsyspri); 25387c478bd9Sstevel@tonic-gate } else { 25397c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd)); 25407c478bd9Sstevel@tonic-gate } 25417c478bd9Sstevel@tonic-gate } 25427c478bd9Sstevel@tonic-gate 25437c478bd9Sstevel@tonic-gate 25447c478bd9Sstevel@tonic-gate /* 25457c478bd9Sstevel@tonic-gate * hubd_restore_device_state: 25467c478bd9Sstevel@tonic-gate * - set config for the hub 25477c478bd9Sstevel@tonic-gate * - power cycle all the ports 25487c478bd9Sstevel@tonic-gate * - for each port that was connected 25497c478bd9Sstevel@tonic-gate * - reset port 25507c478bd9Sstevel@tonic-gate * - assign addrs to the device on this port 25517c478bd9Sstevel@tonic-gate * - restart polling 25527c478bd9Sstevel@tonic-gate * - reset suspend flag 25537c478bd9Sstevel@tonic-gate */ 25547c478bd9Sstevel@tonic-gate static void 25557c478bd9Sstevel@tonic-gate hubd_restore_device_state(dev_info_t *dip, hubd_t *hubd) 25567c478bd9Sstevel@tonic-gate { 25577c478bd9Sstevel@tonic-gate int rval; 25587c478bd9Sstevel@tonic-gate int retry; 25597c478bd9Sstevel@tonic-gate uint_t hub_prev_state; 25607c478bd9Sstevel@tonic-gate usb_port_t port; 25617c478bd9Sstevel@tonic-gate uint16_t status; 25627c478bd9Sstevel@tonic-gate uint16_t change; 25637c478bd9Sstevel@tonic-gate dev_info_t *ch_dip; 25647c478bd9Sstevel@tonic-gate boolean_t ehci_root_hub; 25657c478bd9Sstevel@tonic-gate 25667c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 25677c478bd9Sstevel@tonic-gate "hubd_restore_device_state:"); 25687c478bd9Sstevel@tonic-gate 25697c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 25707c478bd9Sstevel@tonic-gate hub_prev_state = hubd->h_dev_state; 25717c478bd9Sstevel@tonic-gate ASSERT(hub_prev_state != USB_DEV_PWRED_DOWN); 25727c478bd9Sstevel@tonic-gate 25737c478bd9Sstevel@tonic-gate /* First bring the device to full power */ 25747c478bd9Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, dip, 0); 25757c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 25767c478bd9Sstevel@tonic-gate 25777c478bd9Sstevel@tonic-gate (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); 25787c478bd9Sstevel@tonic-gate 25797c478bd9Sstevel@tonic-gate if (!usba_is_root_hub(dip) && 25807c478bd9Sstevel@tonic-gate (usb_check_same_device(dip, hubd->h_log_handle, USB_LOG_L0, 25817c478bd9Sstevel@tonic-gate DPRINT_MASK_HOTPLUG, 25827c478bd9Sstevel@tonic-gate USB_CHK_BASIC|USB_CHK_CFG, NULL) != USB_SUCCESS)) { 25837c478bd9Sstevel@tonic-gate 25847c478bd9Sstevel@tonic-gate /* change the device state to disconnected */ 25857c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 25867c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_DISCONNECTED; 25877c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, dip, 0); 25887c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 25897c478bd9Sstevel@tonic-gate 25907c478bd9Sstevel@tonic-gate return; 25917c478bd9Sstevel@tonic-gate } 25927c478bd9Sstevel@tonic-gate 25937c478bd9Sstevel@tonic-gate ehci_root_hub = (strcmp(ddi_driver_name(dip), "ehci") == 0); 25947c478bd9Sstevel@tonic-gate 25957c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 25967c478bd9Sstevel@tonic-gate /* First turn off all port power */ 25977c478bd9Sstevel@tonic-gate rval = hubd_disable_all_port_power(hubd); 25987c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 25997c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle, 26007c478bd9Sstevel@tonic-gate "hubd_restore_device_state:" 26017c478bd9Sstevel@tonic-gate "turning off port power failed"); 26027c478bd9Sstevel@tonic-gate } 26037c478bd9Sstevel@tonic-gate 26047c478bd9Sstevel@tonic-gate /* Settling time before turning on again */ 26057c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 26067c478bd9Sstevel@tonic-gate delay(drv_usectohz(hubd_device_delay / 100)); 26077c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 26087c478bd9Sstevel@tonic-gate 26097c478bd9Sstevel@tonic-gate /* enable power on all ports so we can see connects */ 26107c478bd9Sstevel@tonic-gate if (hubd_enable_all_port_power(hubd) != USB_SUCCESS) { 26117c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 26127c478bd9Sstevel@tonic-gate "hubd_restore_device_state: turn on port power failed"); 26137c478bd9Sstevel@tonic-gate 26147c478bd9Sstevel@tonic-gate /* disable whatever was enabled */ 26157c478bd9Sstevel@tonic-gate (void) hubd_disable_all_port_power(hubd); 26167c478bd9Sstevel@tonic-gate 26177c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, dip, 0); 26187c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 26197c478bd9Sstevel@tonic-gate 26207c478bd9Sstevel@tonic-gate return; 26217c478bd9Sstevel@tonic-gate } 26227c478bd9Sstevel@tonic-gate 26237c478bd9Sstevel@tonic-gate /* 26247c478bd9Sstevel@tonic-gate * wait at least 3 frames before accessing devices 2625*d96925c4SRichard Lowe * (note that delay's minimal time is one clock tick). 26267c478bd9Sstevel@tonic-gate */ 26277c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 26287c478bd9Sstevel@tonic-gate delay(drv_usectohz(10000)); 26297c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 26307c478bd9Sstevel@tonic-gate 26317c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_HUB_STATE_RECOVER; 26327c478bd9Sstevel@tonic-gate 2633993e3fafSRobert Mustacchi for (port = 1; port <= hubd->h_nports; port++) { 26347c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle, 26357c478bd9Sstevel@tonic-gate "hubd_restore_device_state: port=%d", port); 26367c478bd9Sstevel@tonic-gate 26377c478bd9Sstevel@tonic-gate /* 26387c478bd9Sstevel@tonic-gate * the childen_dips list may have dips that have been 26397c478bd9Sstevel@tonic-gate * already deallocated. we only get a post_detach notification 26407c478bd9Sstevel@tonic-gate * but not a destroy notification 26417c478bd9Sstevel@tonic-gate */ 26427c478bd9Sstevel@tonic-gate ch_dip = hubd->h_children_dips[port]; 26437c478bd9Sstevel@tonic-gate if (ch_dip) { 26447c478bd9Sstevel@tonic-gate /* get port status */ 26457c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 2646993e3fafSRobert Mustacchi &status, &change, NULL, PORT_CHANGE_CSC); 26477c478bd9Sstevel@tonic-gate 26487c478bd9Sstevel@tonic-gate /* check if it is truly connected */ 26497c478bd9Sstevel@tonic-gate if (status & PORT_STATUS_CCS) { 26507c478bd9Sstevel@tonic-gate /* 26517c478bd9Sstevel@tonic-gate * Now reset port and assign the device 26527c478bd9Sstevel@tonic-gate * its original address 26537c478bd9Sstevel@tonic-gate */ 26547c478bd9Sstevel@tonic-gate retry = 0; 26557c478bd9Sstevel@tonic-gate do { 26567c478bd9Sstevel@tonic-gate (void) hubd_reset_port(hubd, port); 26577c478bd9Sstevel@tonic-gate 26587c478bd9Sstevel@tonic-gate /* required for ppx */ 26597c478bd9Sstevel@tonic-gate (void) hubd_enable_port(hubd, port); 26607c478bd9Sstevel@tonic-gate 26617c478bd9Sstevel@tonic-gate if (retry) { 26627c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 26637c478bd9Sstevel@tonic-gate delay(drv_usectohz( 2664c0f24e5bSlg hubd_device_delay/2)); 26657c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 26667c478bd9Sstevel@tonic-gate } 26677c478bd9Sstevel@tonic-gate 26687c478bd9Sstevel@tonic-gate rval = hubd_setdevaddr(hubd, port); 26697c478bd9Sstevel@tonic-gate retry++; 26707c478bd9Sstevel@tonic-gate } while ((rval != USB_SUCCESS) && 26717c478bd9Sstevel@tonic-gate (retry < hubd_retry_enumerate)); 26727c478bd9Sstevel@tonic-gate 26737c478bd9Sstevel@tonic-gate hubd_setdevconfig(hubd, port); 26747c478bd9Sstevel@tonic-gate 26757c478bd9Sstevel@tonic-gate if (hub_prev_state == USB_DEV_DISCONNECTED) { 26767c478bd9Sstevel@tonic-gate /* post a connect event */ 26777c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 26787c478bd9Sstevel@tonic-gate hubd_post_event(hubd, port, 26797c478bd9Sstevel@tonic-gate USBA_EVENT_TAG_HOT_INSERTION); 26807c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 26817c478bd9Sstevel@tonic-gate } else { 26827c478bd9Sstevel@tonic-gate /* 26837c478bd9Sstevel@tonic-gate * Since we have this device connected 26847c478bd9Sstevel@tonic-gate * mark it reinserted to prevent 26857c478bd9Sstevel@tonic-gate * cleanup thread from stepping in. 26867c478bd9Sstevel@tonic-gate */ 26877c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 268816747f41Scth mutex_enter(&(DEVI(ch_dip)->devi_lock)); 268916747f41Scth DEVI_SET_DEVICE_REINSERTED(ch_dip); 269016747f41Scth mutex_exit(&(DEVI(ch_dip)->devi_lock)); 26917c478bd9Sstevel@tonic-gate 26927c478bd9Sstevel@tonic-gate /* 26937c478bd9Sstevel@tonic-gate * reopen pipes for children for 26947c478bd9Sstevel@tonic-gate * their DDI_RESUME 26957c478bd9Sstevel@tonic-gate */ 26967c478bd9Sstevel@tonic-gate rval = usba_persistent_pipe_open( 26977c478bd9Sstevel@tonic-gate usba_get_usba_device(ch_dip)); 26987c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 26997c478bd9Sstevel@tonic-gate ASSERT(rval == USB_SUCCESS); 27007c478bd9Sstevel@tonic-gate } 27017c478bd9Sstevel@tonic-gate } else { 27027c478bd9Sstevel@tonic-gate /* 27037c478bd9Sstevel@tonic-gate * Mark this dip for deletion as the device 27047c478bd9Sstevel@tonic-gate * is not physically present, and schedule 27057c478bd9Sstevel@tonic-gate * cleanup thread upon post resume 27067c478bd9Sstevel@tonic-gate */ 27077c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 27087c478bd9Sstevel@tonic-gate 27097c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, 27107c478bd9Sstevel@tonic-gate hubd->h_log_handle, 27117c478bd9Sstevel@tonic-gate "hubd_restore_device_state: " 27127c478bd9Sstevel@tonic-gate "dip=%p on port=%d marked for cleanup", 2713112116d8Sfb (void *)ch_dip, port); 271416747f41Scth mutex_enter(&(DEVI(ch_dip)->devi_lock)); 27157c478bd9Sstevel@tonic-gate DEVI_SET_DEVICE_REMOVED(ch_dip); 271616747f41Scth mutex_exit(&(DEVI(ch_dip)->devi_lock)); 27177c478bd9Sstevel@tonic-gate 27187c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 27197c478bd9Sstevel@tonic-gate } 27207c478bd9Sstevel@tonic-gate } else if (ehci_root_hub) { 27217c478bd9Sstevel@tonic-gate /* get port status */ 27227c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 2723993e3fafSRobert Mustacchi &status, &change, NULL, PORT_CHANGE_CSC); 27247c478bd9Sstevel@tonic-gate 27257c478bd9Sstevel@tonic-gate /* check if it is truly connected */ 27267c478bd9Sstevel@tonic-gate if (status & PORT_STATUS_CCS) { 27277c478bd9Sstevel@tonic-gate /* 27287c478bd9Sstevel@tonic-gate * reset the port to find out if we have 27297c478bd9Sstevel@tonic-gate * 2.0 device connected or 1.X. A 2.0 27307c478bd9Sstevel@tonic-gate * device will still be seen as connected, 27317c478bd9Sstevel@tonic-gate * while a 1.X device will switch over to 27327c478bd9Sstevel@tonic-gate * the companion controller. 27337c478bd9Sstevel@tonic-gate */ 27347c478bd9Sstevel@tonic-gate (void) hubd_reset_port(hubd, port); 27357c478bd9Sstevel@tonic-gate 27367c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 2737993e3fafSRobert Mustacchi &status, &change, NULL, PORT_CHANGE_CSC); 27387c478bd9Sstevel@tonic-gate 27397c478bd9Sstevel@tonic-gate if (status & 27407c478bd9Sstevel@tonic-gate (PORT_STATUS_CCS | PORT_STATUS_HSDA)) { 27417c478bd9Sstevel@tonic-gate /* 27427c478bd9Sstevel@tonic-gate * We have a USB 2.0 device 27437c478bd9Sstevel@tonic-gate * connected. Power cycle this port 27447c478bd9Sstevel@tonic-gate * so that hotplug thread can 27457c478bd9Sstevel@tonic-gate * enumerate this device. 27467c478bd9Sstevel@tonic-gate */ 27477c478bd9Sstevel@tonic-gate (void) hubd_toggle_port(hubd, port); 27487c478bd9Sstevel@tonic-gate } else { 27497c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, 27507c478bd9Sstevel@tonic-gate hubd->h_log_handle, 27517c478bd9Sstevel@tonic-gate "hubd_restore_device_state: " 27527c478bd9Sstevel@tonic-gate "device on port %d switched over", 27537c478bd9Sstevel@tonic-gate port); 27547c478bd9Sstevel@tonic-gate } 27557c478bd9Sstevel@tonic-gate } 27567c478bd9Sstevel@tonic-gate 27577c478bd9Sstevel@tonic-gate } 27587c478bd9Sstevel@tonic-gate } 27597c478bd9Sstevel@tonic-gate 27607c478bd9Sstevel@tonic-gate 27617c478bd9Sstevel@tonic-gate /* if the device had remote wakeup earlier, enable it again */ 27627c478bd9Sstevel@tonic-gate if (hubd->h_hubpm->hubp_wakeup_enabled) { 27637c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 27647c478bd9Sstevel@tonic-gate (void) usb_handle_remote_wakeup(hubd->h_dip, 27657c478bd9Sstevel@tonic-gate USB_REMOTE_WAKEUP_ENABLE); 27667c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 27677c478bd9Sstevel@tonic-gate } 27687c478bd9Sstevel@tonic-gate 27697c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_ONLINE; 27707c478bd9Sstevel@tonic-gate hubd_start_polling(hubd, 0); 27717c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, dip, 0); 27727c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 27737c478bd9Sstevel@tonic-gate } 27747c478bd9Sstevel@tonic-gate 27757c478bd9Sstevel@tonic-gate 27767c478bd9Sstevel@tonic-gate /* 27777c478bd9Sstevel@tonic-gate * hubd_cleanup: 27787c478bd9Sstevel@tonic-gate * cleanup hubd and deallocate. this function is called for 27797c478bd9Sstevel@tonic-gate * handling attach failures and detaching including dynamic 27807c478bd9Sstevel@tonic-gate * reconfiguration. If called from attaching, it must clean 27817c478bd9Sstevel@tonic-gate * up the whole thing and return success. 27827c478bd9Sstevel@tonic-gate */ 27837c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 27847c478bd9Sstevel@tonic-gate static int 27857c478bd9Sstevel@tonic-gate hubd_cleanup(dev_info_t *dip, hubd_t *hubd) 27867c478bd9Sstevel@tonic-gate { 27877c478bd9Sstevel@tonic-gate int circ, rval, old_dev_state; 27887c478bd9Sstevel@tonic-gate hub_power_t *hubpm; 27897c478bd9Sstevel@tonic-gate #ifdef DEBUG 27907c478bd9Sstevel@tonic-gate usb_port_t port; 27917c478bd9Sstevel@tonic-gate #endif 27927c478bd9Sstevel@tonic-gate 27937c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 27947c478bd9Sstevel@tonic-gate "hubd_cleanup:"); 27957c478bd9Sstevel@tonic-gate 27967c478bd9Sstevel@tonic-gate if ((hubd->h_init_state & HUBD_LOCKS_DONE) == 0) { 27977c478bd9Sstevel@tonic-gate goto done; 27987c478bd9Sstevel@tonic-gate } 27997c478bd9Sstevel@tonic-gate 28007c478bd9Sstevel@tonic-gate /* ensure we are the only one active */ 28017c478bd9Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 28027c478bd9Sstevel@tonic-gate 28037c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 28047c478bd9Sstevel@tonic-gate 28057c478bd9Sstevel@tonic-gate /* Cleanup failure is only allowed if called from detach */ 28067c478bd9Sstevel@tonic-gate if (DEVI_IS_DETACHING(dip)) { 28077c478bd9Sstevel@tonic-gate dev_info_t *rh_dip = hubd->h_usba_device->usb_root_hub_dip; 28087c478bd9Sstevel@tonic-gate 28097c478bd9Sstevel@tonic-gate /* 28107c478bd9Sstevel@tonic-gate * We are being called from detach. 28117c478bd9Sstevel@tonic-gate * Fail immediately if the hotplug thread is running 28127c478bd9Sstevel@tonic-gate * else set the dev_state to disconnected so that 28137c478bd9Sstevel@tonic-gate * hotplug thread just exits without doing anything. 28147c478bd9Sstevel@tonic-gate */ 28157c478bd9Sstevel@tonic-gate if (hubd->h_bus_ctls || hubd->h_bus_pwr || 28167c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread) { 28177c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 28187c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 28197c478bd9Sstevel@tonic-gate 28207c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 28217c478bd9Sstevel@tonic-gate "hubd_cleanup: hotplug thread/bus ctl active " 28227c478bd9Sstevel@tonic-gate "- failing detach"); 28237c478bd9Sstevel@tonic-gate 28247c478bd9Sstevel@tonic-gate return (USB_FAILURE); 28257c478bd9Sstevel@tonic-gate } 28267c478bd9Sstevel@tonic-gate 28277c478bd9Sstevel@tonic-gate /* 28287c478bd9Sstevel@tonic-gate * if the deathrow thread is still active or about 28297c478bd9Sstevel@tonic-gate * to become active, fail detach 28307c478bd9Sstevel@tonic-gate * the roothup can only be detached if nexus drivers 28317c478bd9Sstevel@tonic-gate * are unloaded or explicitly offlined 28327c478bd9Sstevel@tonic-gate */ 28337c478bd9Sstevel@tonic-gate if (rh_dip == dip) { 28347c478bd9Sstevel@tonic-gate if (hubd->h_cleanup_needed || 28357c478bd9Sstevel@tonic-gate hubd->h_cleanup_active) { 28367c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 28377c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 28387c478bd9Sstevel@tonic-gate 28397c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, 28407c478bd9Sstevel@tonic-gate hubd->h_log_handle, 28417c478bd9Sstevel@tonic-gate "hubd_cleanup: deathrow still active?" 28427c478bd9Sstevel@tonic-gate "- failing detach"); 28437c478bd9Sstevel@tonic-gate 28447c478bd9Sstevel@tonic-gate return (USB_FAILURE); 28457c478bd9Sstevel@tonic-gate } 28467c478bd9Sstevel@tonic-gate } 28477c478bd9Sstevel@tonic-gate } 28487c478bd9Sstevel@tonic-gate 28497c478bd9Sstevel@tonic-gate old_dev_state = hubd->h_dev_state; 28507c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_DISCONNECTED; 28517c478bd9Sstevel@tonic-gate 28527c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 28537c478bd9Sstevel@tonic-gate "hubd_cleanup: stop polling"); 28547c478bd9Sstevel@tonic-gate hubd_close_intr_pipe(hubd); 28557c478bd9Sstevel@tonic-gate 28567c478bd9Sstevel@tonic-gate ASSERT((hubd->h_bus_ctls || hubd->h_bus_pwr || 28577c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread) == 0); 28587c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 28597c478bd9Sstevel@tonic-gate 28607c478bd9Sstevel@tonic-gate /* 28617c478bd9Sstevel@tonic-gate * deallocate events, if events are still registered 28627c478bd9Sstevel@tonic-gate * (ie. children still attached) then we have to fail the detach 28637c478bd9Sstevel@tonic-gate */ 28647c478bd9Sstevel@tonic-gate if (hubd->h_ndi_event_hdl) { 28657c478bd9Sstevel@tonic-gate 28667c478bd9Sstevel@tonic-gate rval = ndi_event_free_hdl(hubd->h_ndi_event_hdl); 28677c478bd9Sstevel@tonic-gate if (DEVI_IS_ATTACHING(dip)) { 28687c478bd9Sstevel@tonic-gate 28697c478bd9Sstevel@tonic-gate /* It must return success if attaching. */ 28707c478bd9Sstevel@tonic-gate ASSERT(rval == NDI_SUCCESS); 28717c478bd9Sstevel@tonic-gate 28727c478bd9Sstevel@tonic-gate } else if (rval != NDI_SUCCESS) { 28737c478bd9Sstevel@tonic-gate 2874d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_ALL, hubd->h_log_handle, 28757c478bd9Sstevel@tonic-gate "hubd_cleanup: ndi_event_free_hdl failed"); 28767c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 28777c478bd9Sstevel@tonic-gate 28787c478bd9Sstevel@tonic-gate return (USB_FAILURE); 28797c478bd9Sstevel@tonic-gate 28807c478bd9Sstevel@tonic-gate } 28817c478bd9Sstevel@tonic-gate } 28827c478bd9Sstevel@tonic-gate 28837c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 28847c478bd9Sstevel@tonic-gate 288535f36846Ssl if (hubd->h_init_state & HUBD_CHILDREN_CREATED) { 28867c478bd9Sstevel@tonic-gate #ifdef DEBUG 2887993e3fafSRobert Mustacchi for (port = 1; port <= hubd->h_nports; port++) { 288835f36846Ssl ASSERT(hubd->h_usba_devices[port] == NULL); 288935f36846Ssl ASSERT(hubd->h_children_dips[port] == NULL); 289035f36846Ssl } 28917c478bd9Sstevel@tonic-gate #endif 289235f36846Ssl kmem_free(hubd->h_children_dips, hubd->h_cd_list_length); 289335f36846Ssl kmem_free(hubd->h_usba_devices, hubd->h_cd_list_length); 289435f36846Ssl } 28957c478bd9Sstevel@tonic-gate 28967c478bd9Sstevel@tonic-gate /* 28977c478bd9Sstevel@tonic-gate * Disable the event callbacks first, after this point, event 28987c478bd9Sstevel@tonic-gate * callbacks will never get called. Note we shouldn't hold 28997c478bd9Sstevel@tonic-gate * mutex while unregistering events because there may be a 29007c478bd9Sstevel@tonic-gate * competing event callback thread. Event callbacks are done 29017c478bd9Sstevel@tonic-gate * with ndi mutex held and this can cause a potential deadlock. 29027c478bd9Sstevel@tonic-gate * Note that cleanup can't fail after deregistration of events. 29037c478bd9Sstevel@tonic-gate */ 29047c478bd9Sstevel@tonic-gate if (hubd->h_init_state & HUBD_EVENTS_REGISTERED) { 29057c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 29067c478bd9Sstevel@tonic-gate usb_unregister_event_cbs(dip, &hubd_events); 29077c478bd9Sstevel@tonic-gate hubd_unregister_cpr_callback(hubd); 29087c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 29097c478bd9Sstevel@tonic-gate } 29107c478bd9Sstevel@tonic-gate 29117c478bd9Sstevel@tonic-gate /* restore the old dev state so that device can be put into low power */ 29127c478bd9Sstevel@tonic-gate hubd->h_dev_state = old_dev_state; 29137c478bd9Sstevel@tonic-gate hubpm = hubd->h_hubpm; 29147c478bd9Sstevel@tonic-gate 29157c478bd9Sstevel@tonic-gate if ((hubpm) && (hubd->h_dev_state != USB_DEV_DISCONNECTED)) { 29167c478bd9Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, dip, 0); 29177c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 29187c478bd9Sstevel@tonic-gate if (hubd->h_hubpm->hubp_wakeup_enabled) { 29197c478bd9Sstevel@tonic-gate /* 29207c478bd9Sstevel@tonic-gate * Bring the hub to full power before 29217c478bd9Sstevel@tonic-gate * issuing the disable remote wakeup command 29227c478bd9Sstevel@tonic-gate */ 29237c478bd9Sstevel@tonic-gate (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); 29247c478bd9Sstevel@tonic-gate 29257c478bd9Sstevel@tonic-gate if ((rval = usb_handle_remote_wakeup(hubd->h_dip, 29267c478bd9Sstevel@tonic-gate USB_REMOTE_WAKEUP_DISABLE)) != USB_SUCCESS) { 29277c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, 29287c478bd9Sstevel@tonic-gate hubd->h_log_handle, 29297c478bd9Sstevel@tonic-gate "hubd_cleanup: disable remote wakeup " 29307c478bd9Sstevel@tonic-gate "fails=%d", rval); 29317c478bd9Sstevel@tonic-gate } 29327c478bd9Sstevel@tonic-gate } 29337c478bd9Sstevel@tonic-gate 29347c478bd9Sstevel@tonic-gate (void) pm_lower_power(hubd->h_dip, 0, USB_DEV_OS_PWR_OFF); 29357c478bd9Sstevel@tonic-gate 29367c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 29377c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, dip, 0); 29387c478bd9Sstevel@tonic-gate } 29397c478bd9Sstevel@tonic-gate 29407c478bd9Sstevel@tonic-gate if (hubpm) { 29417c478bd9Sstevel@tonic-gate if (hubpm->hubp_child_pwrstate) { 29427c478bd9Sstevel@tonic-gate kmem_free(hubpm->hubp_child_pwrstate, 29437c478bd9Sstevel@tonic-gate MAX_PORTS + 1); 29447c478bd9Sstevel@tonic-gate } 29457c478bd9Sstevel@tonic-gate kmem_free(hubpm, sizeof (hub_power_t)); 29467c478bd9Sstevel@tonic-gate } 29477c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 29487c478bd9Sstevel@tonic-gate 29497c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 29507c478bd9Sstevel@tonic-gate "hubd_cleanup: freeing space"); 29517c478bd9Sstevel@tonic-gate 29527c478bd9Sstevel@tonic-gate if (hubd->h_init_state & HUBD_HUBDI_REGISTERED) { 29537c478bd9Sstevel@tonic-gate rval = usba_hubdi_unregister(dip); 29547c478bd9Sstevel@tonic-gate ASSERT(rval == USB_SUCCESS); 29557c478bd9Sstevel@tonic-gate } 29567c478bd9Sstevel@tonic-gate 29577c478bd9Sstevel@tonic-gate if (hubd->h_init_state & HUBD_LOCKS_DONE) { 29587c478bd9Sstevel@tonic-gate mutex_destroy(HUBD_MUTEX(hubd)); 29597c478bd9Sstevel@tonic-gate cv_destroy(&hubd->h_cv_reset_port); 2960ffcd51f3Slg cv_destroy(&hubd->h_cv_hotplug_dev); 29617c478bd9Sstevel@tonic-gate } 29627c478bd9Sstevel@tonic-gate 29637c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 29647c478bd9Sstevel@tonic-gate 29657c478bd9Sstevel@tonic-gate if (hubd->h_init_state & HUBD_MINOR_NODE_CREATED) { 29667c478bd9Sstevel@tonic-gate ddi_remove_minor_node(dip, NULL); 29677c478bd9Sstevel@tonic-gate } 29687c478bd9Sstevel@tonic-gate 29697c478bd9Sstevel@tonic-gate if (usba_is_root_hub(dip)) { 29707c478bd9Sstevel@tonic-gate usb_pipe_close(dip, hubd->h_default_pipe, 29717c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL); 29727c478bd9Sstevel@tonic-gate } 29737c478bd9Sstevel@tonic-gate 29747c478bd9Sstevel@tonic-gate done: 29757c478bd9Sstevel@tonic-gate if (hubd->h_ancestry_str) { 29767c478bd9Sstevel@tonic-gate kmem_free(hubd->h_ancestry_str, HUBD_APID_NAMELEN); 29777c478bd9Sstevel@tonic-gate } 29787c478bd9Sstevel@tonic-gate 29797c478bd9Sstevel@tonic-gate usb_client_detach(dip, hubd->h_dev_data); 29807c478bd9Sstevel@tonic-gate 29817c478bd9Sstevel@tonic-gate usb_free_log_hdl(hubd->h_log_handle); 29827c478bd9Sstevel@tonic-gate 29837c478bd9Sstevel@tonic-gate if (!usba_is_root_hub(dip)) { 29847c478bd9Sstevel@tonic-gate ddi_soft_state_free(hubd_statep, ddi_get_instance(dip)); 29857c478bd9Sstevel@tonic-gate } 29867c478bd9Sstevel@tonic-gate 29877c478bd9Sstevel@tonic-gate ddi_prop_remove_all(dip); 29887c478bd9Sstevel@tonic-gate 29897c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 29907c478bd9Sstevel@tonic-gate } 29917c478bd9Sstevel@tonic-gate 29927c478bd9Sstevel@tonic-gate 29936c7181fcSsl /* 29946c7181fcSsl * hubd_determine_port_connection: 29956c7181fcSsl * Determine which port is in connect status but does not 29966c7181fcSsl * have connect status change bit set, and mark port change 29976c7181fcSsl * bit accordingly. 29986c7181fcSsl * This function is applied during hub attach time. 29996c7181fcSsl */ 30006c7181fcSsl static usb_port_mask_t 30016c7181fcSsl hubd_determine_port_connection(hubd_t *hubd) 30026c7181fcSsl { 30036c7181fcSsl usb_port_t port; 30046c7181fcSsl uint16_t status; 30056c7181fcSsl uint16_t change; 30066c7181fcSsl usb_port_mask_t port_change = 0; 30076c7181fcSsl 30086c7181fcSsl ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 30096c7181fcSsl 3010993e3fafSRobert Mustacchi for (port = 1; port <= hubd->h_nports; port++) { 30116c7181fcSsl 30126c7181fcSsl (void) hubd_determine_port_status(hubd, port, &status, 3013993e3fafSRobert Mustacchi &change, NULL, 0); 30146c7181fcSsl 30156c7181fcSsl /* Check if port is in connect status */ 30166c7181fcSsl if (!(status & PORT_STATUS_CCS)) { 30176c7181fcSsl 30186c7181fcSsl continue; 30196c7181fcSsl } 30206c7181fcSsl 30216c7181fcSsl /* 30226c7181fcSsl * Check if port Connect Status Change bit has been set. 30236c7181fcSsl * If already set, the connection will be handled by 30246c7181fcSsl * intr polling callback, not during attach. 30256c7181fcSsl */ 30266c7181fcSsl if (change & PORT_CHANGE_CSC) { 30276c7181fcSsl 30286c7181fcSsl continue; 30296c7181fcSsl } 30306c7181fcSsl 30316c7181fcSsl port_change |= 1 << port; 30326c7181fcSsl } 30336c7181fcSsl 30346c7181fcSsl return (port_change); 30356c7181fcSsl } 30366c7181fcSsl 30376c7181fcSsl 30387c478bd9Sstevel@tonic-gate /* 30397c478bd9Sstevel@tonic-gate * hubd_check_ports: 30407c478bd9Sstevel@tonic-gate * - get hub descriptor 30417c478bd9Sstevel@tonic-gate * - check initial port status 30427c478bd9Sstevel@tonic-gate * - enable power on all ports 30437c478bd9Sstevel@tonic-gate * - enable polling on ep1 30447c478bd9Sstevel@tonic-gate */ 30457c478bd9Sstevel@tonic-gate static int 30467c478bd9Sstevel@tonic-gate hubd_check_ports(hubd_t *hubd) 30477c478bd9Sstevel@tonic-gate { 30486c7181fcSsl int rval; 30496c7181fcSsl usb_port_mask_t port_change = 0; 30506c7181fcSsl hubd_hotplug_arg_t *arg; 30517c478bd9Sstevel@tonic-gate 30527c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 30537c478bd9Sstevel@tonic-gate 30547c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 30557c478bd9Sstevel@tonic-gate "hubd_check_ports: addr=0x%x", usb_get_addr(hubd->h_dip)); 30567c478bd9Sstevel@tonic-gate 30577c478bd9Sstevel@tonic-gate /* 30587c478bd9Sstevel@tonic-gate * First turn off all port power 30597c478bd9Sstevel@tonic-gate */ 30607c478bd9Sstevel@tonic-gate if ((rval = hubd_disable_all_port_power(hubd)) != USB_SUCCESS) { 30617c478bd9Sstevel@tonic-gate 30627c478bd9Sstevel@tonic-gate /* disable whatever was enabled */ 30637c478bd9Sstevel@tonic-gate (void) hubd_disable_all_port_power(hubd); 30647c478bd9Sstevel@tonic-gate 30657c478bd9Sstevel@tonic-gate return (rval); 30667c478bd9Sstevel@tonic-gate } 30677c478bd9Sstevel@tonic-gate 30687c478bd9Sstevel@tonic-gate /* 30697c478bd9Sstevel@tonic-gate * do not switch on immediately (instantly on root hub) 30707c478bd9Sstevel@tonic-gate * and allow time to settle 30717c478bd9Sstevel@tonic-gate */ 30727c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 30737c478bd9Sstevel@tonic-gate delay(drv_usectohz(10000)); 30747c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 30757c478bd9Sstevel@tonic-gate 30767c478bd9Sstevel@tonic-gate /* 30777c478bd9Sstevel@tonic-gate * enable power on all ports so we can see connects 30787c478bd9Sstevel@tonic-gate */ 30797c478bd9Sstevel@tonic-gate if ((rval = hubd_enable_all_port_power(hubd)) != USB_SUCCESS) { 30807c478bd9Sstevel@tonic-gate /* disable whatever was enabled */ 30817c478bd9Sstevel@tonic-gate (void) hubd_disable_all_port_power(hubd); 30827c478bd9Sstevel@tonic-gate 30837c478bd9Sstevel@tonic-gate return (rval); 30847c478bd9Sstevel@tonic-gate } 30857c478bd9Sstevel@tonic-gate 30867c478bd9Sstevel@tonic-gate /* wait at least 3 frames before accessing devices */ 30877c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 30887c478bd9Sstevel@tonic-gate delay(drv_usectohz(10000)); 30897c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 30907c478bd9Sstevel@tonic-gate 30917c478bd9Sstevel@tonic-gate /* 30927c478bd9Sstevel@tonic-gate * allocate arrays for saving the dips of each child per port 30937c478bd9Sstevel@tonic-gate * 30947c478bd9Sstevel@tonic-gate * ports go from 1 - n, allocate 1 more entry 30957c478bd9Sstevel@tonic-gate */ 30967c478bd9Sstevel@tonic-gate hubd->h_cd_list_length = 3097993e3fafSRobert Mustacchi (sizeof (dev_info_t **)) * (hubd->h_nports + 1); 30987c478bd9Sstevel@tonic-gate 30997c478bd9Sstevel@tonic-gate hubd->h_children_dips = (dev_info_t **)kmem_zalloc( 3100c0f24e5bSlg hubd->h_cd_list_length, KM_SLEEP); 31017c478bd9Sstevel@tonic-gate hubd->h_usba_devices = (usba_device_t **)kmem_zalloc( 3102c0f24e5bSlg hubd->h_cd_list_length, KM_SLEEP); 31037c478bd9Sstevel@tonic-gate 310435f36846Ssl hubd->h_init_state |= HUBD_CHILDREN_CREATED; 310535f36846Ssl 31066c7181fcSsl mutex_exit(HUBD_MUTEX(hubd)); 31076c7181fcSsl arg = (hubd_hotplug_arg_t *)kmem_zalloc( 31086c7181fcSsl sizeof (hubd_hotplug_arg_t), KM_SLEEP); 31096c7181fcSsl mutex_enter(HUBD_MUTEX(hubd)); 31106c7181fcSsl 31116c7181fcSsl if ((rval = hubd_open_intr_pipe(hubd)) != USB_SUCCESS) { 31126c7181fcSsl kmem_free(arg, sizeof (hubd_hotplug_arg_t)); 31136c7181fcSsl 31146c7181fcSsl return (rval); 31156c7181fcSsl } 31166c7181fcSsl 31176c7181fcSsl hubd_start_polling(hubd, 0); 31186c7181fcSsl 31196c7181fcSsl /* 31206c7181fcSsl * Some hub devices, like the embedded hub in the CKS ErgoMagic 31216c7181fcSsl * keyboard, may only have connection status bit set, but not 31226c7181fcSsl * have connect status change bit set when a device has been 31236c7181fcSsl * connected to its downstream port before the hub is enumerated. 31246c7181fcSsl * Then when the hub is in enumeration, the devices connected to 31256c7181fcSsl * it cannot be detected by the intr pipe and won't be enumerated. 31266c7181fcSsl * We need to check such situation here and enumerate the downstream 31276c7181fcSsl * devices for such hubs. 31286c7181fcSsl */ 31296c7181fcSsl port_change = hubd_determine_port_connection(hubd); 31306c7181fcSsl 3131993e3fafSRobert Mustacchi if (port_change != 0 || hubd->h_port_change != 0) { 31326c7181fcSsl hubd_pm_busy_component(hubd, hubd->h_dip, 0); 31336c7181fcSsl 31346c7181fcSsl arg->hubd = hubd; 31356c7181fcSsl arg->hotplug_during_attach = B_TRUE; 31366c7181fcSsl hubd->h_port_change |= port_change; 31376c7181fcSsl 31386c7181fcSsl USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 31396c7181fcSsl "hubd_check_ports: port change=0x%x, need to connect", 31406c7181fcSsl hubd->h_port_change); 31416c7181fcSsl 31426c7181fcSsl if (usb_async_req(hubd->h_dip, hubd_hotplug_thread, 31436c7181fcSsl (void *)arg, 0) == USB_SUCCESS) { 31446c7181fcSsl hubd->h_hotplug_thread++; 31456c7181fcSsl } else { 31466c7181fcSsl /* mark this device as idle */ 31476c7181fcSsl hubd_pm_idle_component(hubd, hubd->h_dip, 0); 31486c7181fcSsl kmem_free(arg, sizeof (hubd_hotplug_arg_t)); 31496c7181fcSsl } 31506c7181fcSsl } else { 31516c7181fcSsl kmem_free(arg, sizeof (hubd_hotplug_arg_t)); 31527c478bd9Sstevel@tonic-gate } 31537c478bd9Sstevel@tonic-gate 31547c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 31557c478bd9Sstevel@tonic-gate "hubd_check_ports done"); 31567c478bd9Sstevel@tonic-gate 31576c7181fcSsl return (USB_SUCCESS); 31587c478bd9Sstevel@tonic-gate } 31597c478bd9Sstevel@tonic-gate 31607c478bd9Sstevel@tonic-gate 31617c478bd9Sstevel@tonic-gate /* 31627c478bd9Sstevel@tonic-gate * hubd_get_hub_descriptor: 31637c478bd9Sstevel@tonic-gate */ 31647c478bd9Sstevel@tonic-gate static int 31657c478bd9Sstevel@tonic-gate hubd_get_hub_descriptor(hubd_t *hubd) 31667c478bd9Sstevel@tonic-gate { 31677c478bd9Sstevel@tonic-gate mblk_t *data = NULL; 31687c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 31697c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 3170993e3fafSRobert Mustacchi uint16_t length, wValue; 31717c478bd9Sstevel@tonic-gate int rval; 3172f5b8369cSRaymond Chen usb_req_attrs_t attr = 0; 31737c478bd9Sstevel@tonic-gate 31747c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 31757c478bd9Sstevel@tonic-gate "hubd_get_hub_descriptor:"); 31767c478bd9Sstevel@tonic-gate 3177f5b8369cSRaymond Chen if ((hubd->h_dev_data->dev_descr->idVendor == USB_HUB_INTEL_VID) && 3178f5b8369cSRaymond Chen (hubd->h_dev_data->dev_descr->idProduct == USB_HUB_INTEL_PID)) { 3179f5b8369cSRaymond Chen attr = USB_ATTRS_SHORT_XFER_OK; 3180f5b8369cSRaymond Chen } 3181f5b8369cSRaymond Chen 31827c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 31837c478bd9Sstevel@tonic-gate ASSERT(hubd->h_default_pipe != 0); 31847c478bd9Sstevel@tonic-gate 31857c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 31867c478bd9Sstevel@tonic-gate 3187993e3fafSRobert Mustacchi /* 3188993e3fafSRobert Mustacchi * The contents of wValue change depending on whether this is a USB 2 or 3189993e3fafSRobert Mustacchi * USB 3 device. SuperSpeed Hubs have different descriptors and you 3190993e3fafSRobert Mustacchi * cannot ask them for the traditional USB 2 descriptor. 3191993e3fafSRobert Mustacchi */ 3192993e3fafSRobert Mustacchi if (hubd->h_usba_device->usb_port_status >= USBA_SUPER_SPEED_DEV) { 3193993e3fafSRobert Mustacchi wValue = USB_DESCR_TYPE_SS_HUB << 8 | HUBD_DEFAULT_DESC_INDEX; 3194993e3fafSRobert Mustacchi } else { 3195993e3fafSRobert Mustacchi wValue = USB_DESCR_TYPE_HUB << 8 | HUBD_DEFAULT_DESC_INDEX; 3196993e3fafSRobert Mustacchi } 3197993e3fafSRobert Mustacchi 3198993e3fafSRobert Mustacchi /* 3199993e3fafSRobert Mustacchi * The hub descriptor length varies in various versions of USB. For 3200993e3fafSRobert Mustacchi * example, in USB 2 it's at least 9 bytes long. To start with, we 3201993e3fafSRobert Mustacchi * always get the first 8 bytes so we can figure out how long it 3202993e3fafSRobert Mustacchi * actually is. 3203993e3fafSRobert Mustacchi */ 32047c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 32057c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 320635f36846Ssl HUB_CLASS_REQ_TYPE, 32077c478bd9Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */ 3208993e3fafSRobert Mustacchi wValue, /* wValue */ 32097c478bd9Sstevel@tonic-gate 0, /* wIndex */ 32107c478bd9Sstevel@tonic-gate 8, /* wLength */ 32117c478bd9Sstevel@tonic-gate &data, 0, 32127c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 32137c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 32147c478bd9Sstevel@tonic-gate "get hub descriptor failed: cr=%d cb_fl=0x%x rval=%d", 32157c478bd9Sstevel@tonic-gate completion_reason, cb_flags, rval); 32167c478bd9Sstevel@tonic-gate freemsg(data); 32177c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 32187c478bd9Sstevel@tonic-gate 32197c478bd9Sstevel@tonic-gate return (rval); 32207c478bd9Sstevel@tonic-gate } 32217c478bd9Sstevel@tonic-gate 32227c478bd9Sstevel@tonic-gate length = *(data->b_rptr); 32237c478bd9Sstevel@tonic-gate 32247c478bd9Sstevel@tonic-gate if (length > 8) { 32257c478bd9Sstevel@tonic-gate freemsg(data); 32267c478bd9Sstevel@tonic-gate data = NULL; 32277c478bd9Sstevel@tonic-gate 32287c478bd9Sstevel@tonic-gate /* get complete hub descriptor */ 3229f5b8369cSRaymond Chen rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 32307c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 323135f36846Ssl HUB_CLASS_REQ_TYPE, 32327c478bd9Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */ 3233993e3fafSRobert Mustacchi wValue, /* wValue */ 32347c478bd9Sstevel@tonic-gate 0, /* wIndex */ 32357c478bd9Sstevel@tonic-gate length, /* wLength */ 3236f5b8369cSRaymond Chen &data, attr, 3237f5b8369cSRaymond Chen &completion_reason, &cb_flags, 0); 3238f5b8369cSRaymond Chen 3239f5b8369cSRaymond Chen /* 3240f5b8369cSRaymond Chen * Hub descriptor data less than 9 bytes is not valid and 3241f5b8369cSRaymond Chen * may cause trouble if we use it. See USB2.0 Tab11-13. 3242f5b8369cSRaymond Chen */ 3243f5b8369cSRaymond Chen if ((rval != USB_SUCCESS) || (MBLKL(data) <= 8)) { 32447c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 32457c478bd9Sstevel@tonic-gate "get hub descriptor failed: " 3246f5b8369cSRaymond Chen "cr=%d cb_fl=0x%x rval=%d, len=%ld", 3247f5b8369cSRaymond Chen completion_reason, cb_flags, rval, 3248f5b8369cSRaymond Chen (data)?MBLKL(data):0); 32497c478bd9Sstevel@tonic-gate freemsg(data); 32507c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 32517c478bd9Sstevel@tonic-gate 32527c478bd9Sstevel@tonic-gate return (rval); 32537c478bd9Sstevel@tonic-gate } 32547c478bd9Sstevel@tonic-gate } 32557c478bd9Sstevel@tonic-gate 32567c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 32577c478bd9Sstevel@tonic-gate 3258993e3fafSRobert Mustacchi /* 3259993e3fafSRobert Mustacchi * Parse the hub descriptor. Note that the format of the descriptor 3260993e3fafSRobert Mustacchi * itself depends on the USB version. We handle the different ones and 3261993e3fafSRobert Mustacchi * transform it into a single uniform view. 3262993e3fafSRobert Mustacchi */ 32637c478bd9Sstevel@tonic-gate 3264993e3fafSRobert Mustacchi ASSERT(*(data->b_rptr + 2) <= (MAX_PORTS + 1)); 3265993e3fafSRobert Mustacchi if (hubd->h_usba_device->usb_port_status >= USBA_SUPER_SPEED_DEV) { 3266993e3fafSRobert Mustacchi usb_ss_hub_descr_t hub_descr; 3267993e3fafSRobert Mustacchi char *desc = "cccscccs"; 3268993e3fafSRobert Mustacchi ASSERT(*(data->b_rptr + 1) == ROOT_HUB_SS_DESCRIPTOR_TYPE); 32697c478bd9Sstevel@tonic-gate 3270993e3fafSRobert Mustacchi /* 3271993e3fafSRobert Mustacchi * Note many hubs may support less than the 255 devices that the 3272993e3fafSRobert Mustacchi * USB specification allows for. In those cases, we'll simply 3273993e3fafSRobert Mustacchi * read less and it should be okay. 3274993e3fafSRobert Mustacchi */ 3275993e3fafSRobert Mustacchi if (usb_parse_CV_descr(desc, data->b_rptr, MBLKL(data), 3276993e3fafSRobert Mustacchi (void *)&hub_descr, sizeof (hub_descr)) == 0) { 3277993e3fafSRobert Mustacchi USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 3278993e3fafSRobert Mustacchi "parsing hub descriptor failed"); 3279993e3fafSRobert Mustacchi freemsg(data); 3280993e3fafSRobert Mustacchi return (USB_FAILURE); 3281993e3fafSRobert Mustacchi } 3282993e3fafSRobert Mustacchi 3283993e3fafSRobert Mustacchi hubd->h_nports = hub_descr.bNbrPorts; 3284993e3fafSRobert Mustacchi hubd->h_hub_chars = hub_descr.wHubCharacteristics; 3285993e3fafSRobert Mustacchi hubd->h_power_good = hub_descr.bPwrOn2PwrGood; 3286993e3fafSRobert Mustacchi hubd->h_current = hub_descr.bHubContrCurrent; 3287993e3fafSRobert Mustacchi } else { 3288993e3fafSRobert Mustacchi usb_hub_descr_t hub_descr; 3289993e3fafSRobert Mustacchi if (usb_parse_CV_descr("cccscccccc", 3290993e3fafSRobert Mustacchi data->b_rptr, MBLKL(data), 3291993e3fafSRobert Mustacchi (void *)&hub_descr, sizeof (usb_hub_descr_t)) == 0) { 3292993e3fafSRobert Mustacchi USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 3293993e3fafSRobert Mustacchi "parsing hub descriptor failed"); 3294993e3fafSRobert Mustacchi freemsg(data); 3295993e3fafSRobert Mustacchi return (USB_FAILURE); 3296993e3fafSRobert Mustacchi } 3297993e3fafSRobert Mustacchi 3298993e3fafSRobert Mustacchi hubd->h_nports = hub_descr.bNbrPorts; 3299993e3fafSRobert Mustacchi hubd->h_hub_chars = hub_descr.wHubCharacteristics; 3300993e3fafSRobert Mustacchi hubd->h_power_good = hub_descr.bPwrOn2PwrGood; 3301993e3fafSRobert Mustacchi hubd->h_current = hub_descr.bHubContrCurrent; 33027c478bd9Sstevel@tonic-gate } 33037c478bd9Sstevel@tonic-gate 33047c478bd9Sstevel@tonic-gate freemsg(data); 33057c478bd9Sstevel@tonic-gate 33067c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 330735f36846Ssl "rval=0x%x bNbrPorts=0x%x wHubChars=0x%x " 330835f36846Ssl "PwrOn2PwrGood=0x%x HubContrCurrent=%dmA", rval, 3309993e3fafSRobert Mustacchi hubd->h_nports, hubd->h_hub_chars, 3310993e3fafSRobert Mustacchi hubd->h_power_good, hubd->h_current); 33117c478bd9Sstevel@tonic-gate 3312993e3fafSRobert Mustacchi if (hubd->h_nports > MAX_PORTS) { 33137c478bd9Sstevel@tonic-gate USB_DPRINTF_L0(DPRINT_MASK_ATTA, hubd->h_log_handle, 33147c478bd9Sstevel@tonic-gate "Hub driver supports max of %d ports on hub. " 33157c478bd9Sstevel@tonic-gate "Hence using the first %d port of %d ports available", 3316993e3fafSRobert Mustacchi MAX_PORTS, MAX_PORTS, hubd->h_nports); 33177c478bd9Sstevel@tonic-gate 3318993e3fafSRobert Mustacchi hubd->h_nports = MAX_PORTS; 33197c478bd9Sstevel@tonic-gate } 33207c478bd9Sstevel@tonic-gate 33217c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 33227c478bd9Sstevel@tonic-gate } 33237c478bd9Sstevel@tonic-gate 3324993e3fafSRobert Mustacchi static int 3325993e3fafSRobert Mustacchi hubd_set_hub_depth(hubd_t *hubd) 3326993e3fafSRobert Mustacchi { 3327993e3fafSRobert Mustacchi int rval; 3328993e3fafSRobert Mustacchi usb_cr_t completion_reason; 3329993e3fafSRobert Mustacchi usb_cb_flags_t cb_flags; 3330*d96925c4SRichard Lowe usba_device_t *ud; 3331*d96925c4SRichard Lowe uint16_t depth; 3332993e3fafSRobert Mustacchi 3333993e3fafSRobert Mustacchi /* 3334993e3fafSRobert Mustacchi * We only need to set the hub depth devices for hubs that are at least 3335993e3fafSRobert Mustacchi * SuperSpeed devices. This didn't exist for USB 2.0 and older hubs. 3336993e3fafSRobert Mustacchi * There's also no need to call this on the root hub. 3337993e3fafSRobert Mustacchi */ 3338993e3fafSRobert Mustacchi if (hubd->h_usba_device->usb_port_status < USBA_SUPER_SPEED_DEV || 3339993e3fafSRobert Mustacchi usba_is_root_hub(hubd->h_dip)) 3340993e3fafSRobert Mustacchi return (USB_SUCCESS); 3341993e3fafSRobert Mustacchi 3342993e3fafSRobert Mustacchi depth = 0; 3343993e3fafSRobert Mustacchi ud = hubd->h_usba_device; 3344993e3fafSRobert Mustacchi while (ud->usb_parent_hub != NULL) { 3345993e3fafSRobert Mustacchi depth++; 3346993e3fafSRobert Mustacchi ud = ud->usb_parent_hub; 3347993e3fafSRobert Mustacchi } 3348993e3fafSRobert Mustacchi ASSERT(depth > 0); 3349993e3fafSRobert Mustacchi 3350993e3fafSRobert Mustacchi if (depth > HUBD_SS_MAX_DEPTH) { 3351993e3fafSRobert Mustacchi const char *mfg, *prod; 3352993e3fafSRobert Mustacchi 3353993e3fafSRobert Mustacchi ud = hubd->h_usba_device; 3354993e3fafSRobert Mustacchi prod = ud->usb_product_str; 3355993e3fafSRobert Mustacchi if (prod == NULL) 3356993e3fafSRobert Mustacchi prod = "Unknown Device"; 3357993e3fafSRobert Mustacchi mfg = ud->usb_mfg_str; 3358993e3fafSRobert Mustacchi if (mfg == NULL) 3359993e3fafSRobert Mustacchi mfg = "Unknown Manufacturer"; 3360993e3fafSRobert Mustacchi cmn_err(CE_WARN, "Unable to attach USB 3.x hub %s %s. A " 3361993e3fafSRobert Mustacchi "maximum of %d hubs may be cascaded", mfg, prod, 3362993e3fafSRobert Mustacchi HUBD_SS_MAX_DEPTH); 3363993e3fafSRobert Mustacchi return (USB_FAILURE); 3364993e3fafSRobert Mustacchi } 3365993e3fafSRobert Mustacchi 3366993e3fafSRobert Mustacchi /* 3367993e3fafSRobert Mustacchi * When making the HUB_REQ_SET_HUB_DEPTH request, a hub connected to a 3368993e3fafSRobert Mustacchi * root port is considered to have a hub depth of zero whereas we 3369993e3fafSRobert Mustacchi * consider having a hub depth of one above. 3370993e3fafSRobert Mustacchi */ 3371993e3fafSRobert Mustacchi depth--; 3372993e3fafSRobert Mustacchi 3373993e3fafSRobert Mustacchi if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 3374993e3fafSRobert Mustacchi hubd->h_default_pipe, 3375993e3fafSRobert Mustacchi HUB_SET_HUB_DEPTH_TYPE, 3376993e3fafSRobert Mustacchi HUB_REQ_SET_HUB_DEPTH, /* bRequest */ 3377993e3fafSRobert Mustacchi depth, /* wValue */ 3378993e3fafSRobert Mustacchi 0, /* wIndex */ 3379993e3fafSRobert Mustacchi 0, /* wLength */ 3380993e3fafSRobert Mustacchi NULL, 0, 3381993e3fafSRobert Mustacchi &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 3382993e3fafSRobert Mustacchi USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle, 3383993e3fafSRobert Mustacchi "get set hub depth failed: cr=%d cb=0x%x", 3384993e3fafSRobert Mustacchi completion_reason, cb_flags); 3385993e3fafSRobert Mustacchi } 3386993e3fafSRobert Mustacchi 3387993e3fafSRobert Mustacchi return (rval); 3388993e3fafSRobert Mustacchi } 33897c478bd9Sstevel@tonic-gate 339035f36846Ssl /* 339135f36846Ssl * hubd_get_hub_status_words: 339235f36846Ssl */ 339335f36846Ssl static int 339435f36846Ssl hubd_get_hub_status_words(hubd_t *hubd, uint16_t *status) 339535f36846Ssl { 339635f36846Ssl usb_cr_t completion_reason; 339735f36846Ssl usb_cb_flags_t cb_flags; 339835f36846Ssl mblk_t *data = NULL; 339935f36846Ssl 340035f36846Ssl ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 340135f36846Ssl 340235f36846Ssl mutex_exit(HUBD_MUTEX(hubd)); 340335f36846Ssl 340435f36846Ssl if (usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe, 340535f36846Ssl HUB_CLASS_REQ_TYPE, 340635f36846Ssl USB_REQ_GET_STATUS, 340735f36846Ssl 0, 340835f36846Ssl 0, 340935f36846Ssl GET_STATUS_LENGTH, 341035f36846Ssl &data, 0, 341135f36846Ssl &completion_reason, &cb_flags, 0) != USB_SUCCESS) { 341235f36846Ssl USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle, 341335f36846Ssl "get hub status failed: cr=%d cb=0x%x", 341435f36846Ssl completion_reason, cb_flags); 341535f36846Ssl 341635f36846Ssl if (data) { 341735f36846Ssl freemsg(data); 341835f36846Ssl } 341935f36846Ssl 342035f36846Ssl mutex_enter(HUBD_MUTEX(hubd)); 342135f36846Ssl 342235f36846Ssl return (USB_FAILURE); 342335f36846Ssl } 342435f36846Ssl 342535f36846Ssl mutex_enter(HUBD_MUTEX(hubd)); 342635f36846Ssl 342735f36846Ssl status[0] = (*(data->b_rptr + 1) << 8) | *(data->b_rptr); 342835f36846Ssl status[1] = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2); 342935f36846Ssl 343035f36846Ssl USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle, 343135f36846Ssl "hub status=0x%x change=0x%x", status[0], status[1]); 343235f36846Ssl 343335f36846Ssl freemsg(data); 343435f36846Ssl 343535f36846Ssl return (USB_SUCCESS); 343635f36846Ssl } 343735f36846Ssl 343835f36846Ssl 34397c478bd9Sstevel@tonic-gate /* 34407c478bd9Sstevel@tonic-gate * hubd_open_intr_pipe: 34417c478bd9Sstevel@tonic-gate * we read all descriptors first for curiosity and then simply 34427c478bd9Sstevel@tonic-gate * open the pipe 34437c478bd9Sstevel@tonic-gate */ 34447c478bd9Sstevel@tonic-gate static int 34457c478bd9Sstevel@tonic-gate hubd_open_intr_pipe(hubd_t *hubd) 34467c478bd9Sstevel@tonic-gate { 34477c478bd9Sstevel@tonic-gate int rval; 34487c478bd9Sstevel@tonic-gate 34497c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 34507c478bd9Sstevel@tonic-gate "hubd_open_intr_pipe:"); 34517c478bd9Sstevel@tonic-gate 34527c478bd9Sstevel@tonic-gate ASSERT(hubd->h_intr_pipe_state == HUBD_INTR_PIPE_IDLE); 34537c478bd9Sstevel@tonic-gate 34547c478bd9Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_OPENING; 34557c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 34567c478bd9Sstevel@tonic-gate 3457993e3fafSRobert Mustacchi if ((rval = usb_pipe_xopen(hubd->h_dip, 3458993e3fafSRobert Mustacchi &hubd->h_ep1_xdescr, &hubd->h_pipe_policy, 34597c478bd9Sstevel@tonic-gate 0, &hubd->h_ep1_ph)) != USB_SUCCESS) { 3460d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle, 34617c478bd9Sstevel@tonic-gate "open intr pipe failed (%d)", rval); 34627c478bd9Sstevel@tonic-gate 34637c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 34647c478bd9Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_IDLE; 34657c478bd9Sstevel@tonic-gate 34667c478bd9Sstevel@tonic-gate return (rval); 34677c478bd9Sstevel@tonic-gate } 34687c478bd9Sstevel@tonic-gate 34697c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 34707c478bd9Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_ACTIVE; 34717c478bd9Sstevel@tonic-gate 34727c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 3473112116d8Sfb "open intr pipe succeeded, ph=0x%p", (void *)hubd->h_ep1_ph); 34747c478bd9Sstevel@tonic-gate 34757c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 34767c478bd9Sstevel@tonic-gate } 34777c478bd9Sstevel@tonic-gate 34787c478bd9Sstevel@tonic-gate 34797c478bd9Sstevel@tonic-gate /* 34807c478bd9Sstevel@tonic-gate * hubd_start_polling: 34817c478bd9Sstevel@tonic-gate * start or restart the polling 34827c478bd9Sstevel@tonic-gate */ 34837c478bd9Sstevel@tonic-gate static void 34847c478bd9Sstevel@tonic-gate hubd_start_polling(hubd_t *hubd, int always) 34857c478bd9Sstevel@tonic-gate { 34867c478bd9Sstevel@tonic-gate usb_intr_req_t *reqp; 34877c478bd9Sstevel@tonic-gate int rval; 34887c478bd9Sstevel@tonic-gate usb_pipe_state_t pipe_state; 34897c478bd9Sstevel@tonic-gate 34907c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 34917c478bd9Sstevel@tonic-gate "start polling: always=%d dev_state=%d pipe_state=%d\n\t" 34927c478bd9Sstevel@tonic-gate "thread=%d ep1_ph=0x%p", 34937c478bd9Sstevel@tonic-gate always, hubd->h_dev_state, hubd->h_intr_pipe_state, 3494112116d8Sfb hubd->h_hotplug_thread, (void *)hubd->h_ep1_ph); 34957c478bd9Sstevel@tonic-gate 34967c478bd9Sstevel@tonic-gate /* 34977c478bd9Sstevel@tonic-gate * start or restart polling on the intr pipe 34987c478bd9Sstevel@tonic-gate * only if hotplug thread is not running 34997c478bd9Sstevel@tonic-gate */ 35007c478bd9Sstevel@tonic-gate if ((always == HUBD_ALWAYS_START_POLLING) || 35017c478bd9Sstevel@tonic-gate ((hubd->h_dev_state == USB_DEV_ONLINE) && 35027c478bd9Sstevel@tonic-gate (hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE) && 35037c478bd9Sstevel@tonic-gate (hubd->h_hotplug_thread == 0) && hubd->h_ep1_ph)) { 35047c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 35057c478bd9Sstevel@tonic-gate "start polling requested"); 35067c478bd9Sstevel@tonic-gate 35077c478bd9Sstevel@tonic-gate reqp = usb_alloc_intr_req(hubd->h_dip, 0, USB_FLAGS_SLEEP); 35087c478bd9Sstevel@tonic-gate 35097c478bd9Sstevel@tonic-gate reqp->intr_client_private = (usb_opaque_t)hubd; 35107c478bd9Sstevel@tonic-gate reqp->intr_attributes = USB_ATTRS_SHORT_XFER_OK | 3511c0f24e5bSlg USB_ATTRS_AUTOCLEARING; 3512993e3fafSRobert Mustacchi reqp->intr_len = hubd->h_ep1_xdescr.uex_ep.wMaxPacketSize; 35137c478bd9Sstevel@tonic-gate reqp->intr_cb = hubd_read_cb; 35147c478bd9Sstevel@tonic-gate reqp->intr_exc_cb = hubd_exception_cb; 35157c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 35167c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_intr_xfer(hubd->h_ep1_ph, reqp, 35177c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP)) != USB_SUCCESS) { 35187c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle, 35197c478bd9Sstevel@tonic-gate "start polling failed, rval=%d", rval); 35207c478bd9Sstevel@tonic-gate usb_free_intr_req(reqp); 35217c478bd9Sstevel@tonic-gate } 35227c478bd9Sstevel@tonic-gate 35237c478bd9Sstevel@tonic-gate rval = usb_pipe_get_state(hubd->h_ep1_ph, &pipe_state, 3524c0f24e5bSlg USB_FLAGS_SLEEP); 35257c478bd9Sstevel@tonic-gate if (pipe_state != USB_PIPE_STATE_ACTIVE) { 35267c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 35277c478bd9Sstevel@tonic-gate "intr pipe state=%d, rval=%d", pipe_state, rval); 35287c478bd9Sstevel@tonic-gate } 35297c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 3530112116d8Sfb "start polling request 0x%p", (void *)reqp); 35317c478bd9Sstevel@tonic-gate 35327c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 35337c478bd9Sstevel@tonic-gate } 35347c478bd9Sstevel@tonic-gate } 35357c478bd9Sstevel@tonic-gate 35367c478bd9Sstevel@tonic-gate 35377c478bd9Sstevel@tonic-gate /* 35387c478bd9Sstevel@tonic-gate * hubd_stop_polling 35397c478bd9Sstevel@tonic-gate * stop polling but do not close the pipe 35407c478bd9Sstevel@tonic-gate */ 35417c478bd9Sstevel@tonic-gate static void 35427c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd_t *hubd) 35437c478bd9Sstevel@tonic-gate { 35447c478bd9Sstevel@tonic-gate int rval; 35457c478bd9Sstevel@tonic-gate usb_pipe_state_t pipe_state; 35467c478bd9Sstevel@tonic-gate 35477c478bd9Sstevel@tonic-gate if (hubd->h_ep1_ph) { 35487c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 35497c478bd9Sstevel@tonic-gate "hubd_stop_polling:"); 35507c478bd9Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_STOPPED; 35517c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 35527c478bd9Sstevel@tonic-gate 35537c478bd9Sstevel@tonic-gate usb_pipe_stop_intr_polling(hubd->h_ep1_ph, USB_FLAGS_SLEEP); 35547c478bd9Sstevel@tonic-gate rval = usb_pipe_get_state(hubd->h_ep1_ph, &pipe_state, 3555c0f24e5bSlg USB_FLAGS_SLEEP); 35567c478bd9Sstevel@tonic-gate 35577c478bd9Sstevel@tonic-gate if (pipe_state != USB_PIPE_STATE_IDLE) { 35587c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 35597c478bd9Sstevel@tonic-gate "intr pipe state=%d, rval=%d", pipe_state, rval); 35607c478bd9Sstevel@tonic-gate } 35617c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 35627c478bd9Sstevel@tonic-gate if (hubd->h_intr_pipe_state == HUBD_INTR_PIPE_STOPPED) { 35637c478bd9Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_ACTIVE; 35647c478bd9Sstevel@tonic-gate } 35657c478bd9Sstevel@tonic-gate } 35667c478bd9Sstevel@tonic-gate } 35677c478bd9Sstevel@tonic-gate 35687c478bd9Sstevel@tonic-gate 35697c478bd9Sstevel@tonic-gate /* 35707c478bd9Sstevel@tonic-gate * hubd_close_intr_pipe: 35717c478bd9Sstevel@tonic-gate * close the pipe (which also stops the polling 35727c478bd9Sstevel@tonic-gate * and wait for the hotplug thread to exit 35737c478bd9Sstevel@tonic-gate */ 35747c478bd9Sstevel@tonic-gate static void 35757c478bd9Sstevel@tonic-gate hubd_close_intr_pipe(hubd_t *hubd) 35767c478bd9Sstevel@tonic-gate { 35777c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 35787c478bd9Sstevel@tonic-gate "hubd_close_intr_pipe:"); 35797c478bd9Sstevel@tonic-gate 35807c478bd9Sstevel@tonic-gate /* 35817c478bd9Sstevel@tonic-gate * Now that no async operation is outstanding on pipe, 35827c478bd9Sstevel@tonic-gate * we can change the state to HUBD_INTR_PIPE_CLOSING 35837c478bd9Sstevel@tonic-gate */ 35847c478bd9Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_CLOSING; 35857c478bd9Sstevel@tonic-gate 35867c478bd9Sstevel@tonic-gate ASSERT(hubd->h_hotplug_thread == 0); 35877c478bd9Sstevel@tonic-gate 35887c478bd9Sstevel@tonic-gate if (hubd->h_ep1_ph) { 35897c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 35907c478bd9Sstevel@tonic-gate usb_pipe_close(hubd->h_dip, hubd->h_ep1_ph, USB_FLAGS_SLEEP, 3591c0f24e5bSlg NULL, NULL); 35927c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 35937c478bd9Sstevel@tonic-gate hubd->h_ep1_ph = NULL; 35947c478bd9Sstevel@tonic-gate } 35957c478bd9Sstevel@tonic-gate 35967c478bd9Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_IDLE; 35977c478bd9Sstevel@tonic-gate } 35987c478bd9Sstevel@tonic-gate 35997c478bd9Sstevel@tonic-gate 36007c478bd9Sstevel@tonic-gate /* 36017c478bd9Sstevel@tonic-gate * hubd_exception_cb 36027c478bd9Sstevel@tonic-gate * interrupt ep1 exception callback function. 36037c478bd9Sstevel@tonic-gate * this callback executes in taskq thread context and assumes 36047c478bd9Sstevel@tonic-gate * autoclearing 36057c478bd9Sstevel@tonic-gate */ 36067c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 36077c478bd9Sstevel@tonic-gate static void 36087c478bd9Sstevel@tonic-gate hubd_exception_cb(usb_pipe_handle_t pipe, usb_intr_req_t *reqp) 36097c478bd9Sstevel@tonic-gate { 36107c478bd9Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)(reqp->intr_client_private); 36117c478bd9Sstevel@tonic-gate 36127c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CALLBACK, hubd->h_log_handle, 36137c478bd9Sstevel@tonic-gate "hubd_exception_cb: " 3614112116d8Sfb "req=0x%p cr=%d data=0x%p cb_flags=0x%x", (void *)reqp, 3615112116d8Sfb reqp->intr_completion_reason, (void *)reqp->intr_data, 36167c478bd9Sstevel@tonic-gate reqp->intr_cb_flags); 36177c478bd9Sstevel@tonic-gate 36187c478bd9Sstevel@tonic-gate ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0); 36197c478bd9Sstevel@tonic-gate 36207c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 36217c478bd9Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0); 36227c478bd9Sstevel@tonic-gate 36237c478bd9Sstevel@tonic-gate switch (reqp->intr_completion_reason) { 36247c478bd9Sstevel@tonic-gate case USB_CR_PIPE_RESET: 36257c478bd9Sstevel@tonic-gate /* only restart polling after autoclearing */ 36267c478bd9Sstevel@tonic-gate if ((hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE) && 36277c478bd9Sstevel@tonic-gate (hubd->h_port_reset_wait == 0)) { 36287c478bd9Sstevel@tonic-gate hubd_start_polling(hubd, 0); 36297c478bd9Sstevel@tonic-gate } 36307c478bd9Sstevel@tonic-gate 36317c478bd9Sstevel@tonic-gate break; 36327c478bd9Sstevel@tonic-gate case USB_CR_DEV_NOT_RESP: 36337c478bd9Sstevel@tonic-gate case USB_CR_STOPPED_POLLING: 36347c478bd9Sstevel@tonic-gate case USB_CR_PIPE_CLOSING: 36357c478bd9Sstevel@tonic-gate case USB_CR_UNSPECIFIED_ERR: 36367c478bd9Sstevel@tonic-gate /* never restart polling on these conditions */ 36377c478bd9Sstevel@tonic-gate default: 36387c478bd9Sstevel@tonic-gate /* for all others, wait for the autoclearing PIPE_RESET cb */ 36397c478bd9Sstevel@tonic-gate 36407c478bd9Sstevel@tonic-gate break; 36417c478bd9Sstevel@tonic-gate } 36427c478bd9Sstevel@tonic-gate 36437c478bd9Sstevel@tonic-gate usb_free_intr_req(reqp); 36447c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0); 36457c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 36467c478bd9Sstevel@tonic-gate } 36477c478bd9Sstevel@tonic-gate 36487c478bd9Sstevel@tonic-gate 36497c478bd9Sstevel@tonic-gate /* 36507c478bd9Sstevel@tonic-gate * helper function to convert LE bytes to a portmask 36517c478bd9Sstevel@tonic-gate */ 36527c478bd9Sstevel@tonic-gate static usb_port_mask_t 36537c478bd9Sstevel@tonic-gate hubd_mblk2portmask(mblk_t *data) 36547c478bd9Sstevel@tonic-gate { 3655d29f5a71Szhigang lu - Sun Microsystems - Beijing China int len = min(MBLKL(data), sizeof (usb_port_mask_t)); 36567c478bd9Sstevel@tonic-gate usb_port_mask_t rval = 0; 36577c478bd9Sstevel@tonic-gate int i; 36587c478bd9Sstevel@tonic-gate 36597c478bd9Sstevel@tonic-gate for (i = 0; i < len; i++) { 36607c478bd9Sstevel@tonic-gate rval |= data->b_rptr[i] << (i * 8); 36617c478bd9Sstevel@tonic-gate } 36627c478bd9Sstevel@tonic-gate 36637c478bd9Sstevel@tonic-gate return (rval); 36647c478bd9Sstevel@tonic-gate } 36657c478bd9Sstevel@tonic-gate 36667c478bd9Sstevel@tonic-gate 36677c478bd9Sstevel@tonic-gate /* 36687c478bd9Sstevel@tonic-gate * hubd_read_cb: 36697c478bd9Sstevel@tonic-gate * interrupt ep1 callback function 36707c478bd9Sstevel@tonic-gate * 36717c478bd9Sstevel@tonic-gate * the status indicates just a change on the pipe with no indication 36727c478bd9Sstevel@tonic-gate * of what the change was 36737c478bd9Sstevel@tonic-gate * 36747c478bd9Sstevel@tonic-gate * known conditions: 36757c478bd9Sstevel@tonic-gate * - reset port completion 36767c478bd9Sstevel@tonic-gate * - connect 36777c478bd9Sstevel@tonic-gate * - disconnect 36787c478bd9Sstevel@tonic-gate * 36797c478bd9Sstevel@tonic-gate * for handling the hotplugging, create a new thread that can do 36807c478bd9Sstevel@tonic-gate * synchronous usba calls 36817c478bd9Sstevel@tonic-gate */ 36827c478bd9Sstevel@tonic-gate static void 36837c478bd9Sstevel@tonic-gate hubd_read_cb(usb_pipe_handle_t pipe, usb_intr_req_t *reqp) 36847c478bd9Sstevel@tonic-gate { 36857c478bd9Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)(reqp->intr_client_private); 36867c478bd9Sstevel@tonic-gate size_t length; 36877c478bd9Sstevel@tonic-gate mblk_t *data = reqp->intr_data; 36886c7181fcSsl int mem_flag = 0; 36896c7181fcSsl hubd_hotplug_arg_t *arg; 36907c478bd9Sstevel@tonic-gate 36917c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 3692112116d8Sfb "hubd_read_cb: ph=0x%p req=0x%p", (void *)pipe, (void *)reqp); 36937c478bd9Sstevel@tonic-gate 36947c478bd9Sstevel@tonic-gate ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0); 36957c478bd9Sstevel@tonic-gate 36967c478bd9Sstevel@tonic-gate /* 36977c478bd9Sstevel@tonic-gate * At present, we are not handling notification for completion of 36987c478bd9Sstevel@tonic-gate * asynchronous pipe reset, for which this data ptr could be NULL 36997c478bd9Sstevel@tonic-gate */ 37007c478bd9Sstevel@tonic-gate 37017c478bd9Sstevel@tonic-gate if (data == NULL) { 37027c478bd9Sstevel@tonic-gate usb_free_intr_req(reqp); 37037c478bd9Sstevel@tonic-gate 37047c478bd9Sstevel@tonic-gate return; 37057c478bd9Sstevel@tonic-gate } 37067c478bd9Sstevel@tonic-gate 37076c7181fcSsl arg = (hubd_hotplug_arg_t *)kmem_zalloc( 37086c7181fcSsl sizeof (hubd_hotplug_arg_t), KM_SLEEP); 37096c7181fcSsl mem_flag = 1; 37106c7181fcSsl 37117c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 37127c478bd9Sstevel@tonic-gate 37137c478bd9Sstevel@tonic-gate if ((hubd->h_dev_state == USB_DEV_SUSPENDED) || 37147c478bd9Sstevel@tonic-gate (hubd->h_intr_pipe_state != HUBD_INTR_PIPE_ACTIVE)) { 37157c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 37167c478bd9Sstevel@tonic-gate usb_free_intr_req(reqp); 37176c7181fcSsl kmem_free(arg, sizeof (hubd_hotplug_arg_t)); 37187c478bd9Sstevel@tonic-gate 37197c478bd9Sstevel@tonic-gate return; 37207c478bd9Sstevel@tonic-gate } 37217c478bd9Sstevel@tonic-gate 37227c478bd9Sstevel@tonic-gate ASSERT(hubd->h_ep1_ph == pipe); 37237c478bd9Sstevel@tonic-gate 3724d29f5a71Szhigang lu - Sun Microsystems - Beijing China length = MBLKL(data); 37257c478bd9Sstevel@tonic-gate 37267c478bd9Sstevel@tonic-gate /* 37277c478bd9Sstevel@tonic-gate * Only look at the data and startup the hotplug thread if 37287c478bd9Sstevel@tonic-gate * there actually is data. 37297c478bd9Sstevel@tonic-gate */ 37307c478bd9Sstevel@tonic-gate if (length != 0) { 37317c478bd9Sstevel@tonic-gate usb_port_mask_t port_change = hubd_mblk2portmask(data); 37327c478bd9Sstevel@tonic-gate 37337c478bd9Sstevel@tonic-gate /* 37347c478bd9Sstevel@tonic-gate * if a port change was already reported and we are waiting for 37357c478bd9Sstevel@tonic-gate * reset port completion then wake up the hotplug thread which 37367c478bd9Sstevel@tonic-gate * should be waiting on reset port completion 37377c478bd9Sstevel@tonic-gate * 37387c478bd9Sstevel@tonic-gate * if there is disconnect event instead of reset completion, let 37397c478bd9Sstevel@tonic-gate * the hotplug thread figure this out 37407c478bd9Sstevel@tonic-gate */ 37417c478bd9Sstevel@tonic-gate 37427c478bd9Sstevel@tonic-gate /* remove the reset wait bits from the status */ 37437c478bd9Sstevel@tonic-gate hubd->h_port_change |= port_change & 3744c0f24e5bSlg ~hubd->h_port_reset_wait; 37457c478bd9Sstevel@tonic-gate 37467c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_CALLBACK, hubd->h_log_handle, 37477c478bd9Sstevel@tonic-gate "port change=0x%x port_reset_wait=0x%x", 37487c478bd9Sstevel@tonic-gate hubd->h_port_change, hubd->h_port_reset_wait); 37497c478bd9Sstevel@tonic-gate 37507c478bd9Sstevel@tonic-gate /* there should be only one reset bit active at the time */ 37517c478bd9Sstevel@tonic-gate if (hubd->h_port_reset_wait & port_change) { 37527c478bd9Sstevel@tonic-gate hubd->h_port_reset_wait = 0; 37537c478bd9Sstevel@tonic-gate cv_signal(&hubd->h_cv_reset_port); 37547c478bd9Sstevel@tonic-gate } 37557c478bd9Sstevel@tonic-gate 37567c478bd9Sstevel@tonic-gate /* 37577c478bd9Sstevel@tonic-gate * kick off the thread only if device is ONLINE and it is not 37587c478bd9Sstevel@tonic-gate * during attaching or detaching 37597c478bd9Sstevel@tonic-gate */ 37607c478bd9Sstevel@tonic-gate if ((hubd->h_dev_state == USB_DEV_ONLINE) && 37617c478bd9Sstevel@tonic-gate (!DEVI_IS_ATTACHING(hubd->h_dip)) && 37627c478bd9Sstevel@tonic-gate (!DEVI_IS_DETACHING(hubd->h_dip)) && 37637c478bd9Sstevel@tonic-gate (hubd->h_port_change) && 37647c478bd9Sstevel@tonic-gate (hubd->h_hotplug_thread == 0)) { 37657c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_CALLBACK, hubd->h_log_handle, 37667c478bd9Sstevel@tonic-gate "creating hotplug thread: " 37677c478bd9Sstevel@tonic-gate "dev_state=%d", hubd->h_dev_state); 37687c478bd9Sstevel@tonic-gate 37697c478bd9Sstevel@tonic-gate /* 37707c478bd9Sstevel@tonic-gate * Mark this device as busy. The will be marked idle 37717c478bd9Sstevel@tonic-gate * if the async req fails or at the exit of hotplug 37727c478bd9Sstevel@tonic-gate * thread 37737c478bd9Sstevel@tonic-gate */ 37747c478bd9Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0); 37757c478bd9Sstevel@tonic-gate 37766c7181fcSsl arg->hubd = hubd; 37776c7181fcSsl arg->hotplug_during_attach = B_FALSE; 37786c7181fcSsl 37797c478bd9Sstevel@tonic-gate if (usb_async_req(hubd->h_dip, 37807c478bd9Sstevel@tonic-gate hubd_hotplug_thread, 37816c7181fcSsl (void *)arg, 0) == USB_SUCCESS) { 37827c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread++; 37836c7181fcSsl mem_flag = 0; 37847c478bd9Sstevel@tonic-gate } else { 37857c478bd9Sstevel@tonic-gate /* mark this device as idle */ 37867c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, 37877c478bd9Sstevel@tonic-gate hubd->h_dip, 0); 37887c478bd9Sstevel@tonic-gate } 37897c478bd9Sstevel@tonic-gate } 37907c478bd9Sstevel@tonic-gate } 37917c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 37927c478bd9Sstevel@tonic-gate 37936c7181fcSsl if (mem_flag == 1) { 37946c7181fcSsl kmem_free(arg, sizeof (hubd_hotplug_arg_t)); 37956c7181fcSsl } 37966c7181fcSsl 37977c478bd9Sstevel@tonic-gate usb_free_intr_req(reqp); 37987c478bd9Sstevel@tonic-gate } 37997c478bd9Sstevel@tonic-gate 38007c478bd9Sstevel@tonic-gate 38017c478bd9Sstevel@tonic-gate /* 38027c478bd9Sstevel@tonic-gate * hubd_hotplug_thread: 38037c478bd9Sstevel@tonic-gate * handles resetting of port, and creating children 38047c478bd9Sstevel@tonic-gate * 38057c478bd9Sstevel@tonic-gate * the ports to check are indicated in h_port_change bit mask 38067c478bd9Sstevel@tonic-gate * XXX note that one time poll doesn't work on the root hub 38077c478bd9Sstevel@tonic-gate */ 38087c478bd9Sstevel@tonic-gate static void 38097c478bd9Sstevel@tonic-gate hubd_hotplug_thread(void *arg) 38107c478bd9Sstevel@tonic-gate { 38116c7181fcSsl hubd_hotplug_arg_t *hd_arg = (hubd_hotplug_arg_t *)arg; 38126c7181fcSsl hubd_t *hubd = hd_arg->hubd; 38136c7181fcSsl boolean_t attach_flg = hd_arg->hotplug_during_attach; 38147c478bd9Sstevel@tonic-gate usb_port_t port; 38157c478bd9Sstevel@tonic-gate uint16_t nports; 38167c478bd9Sstevel@tonic-gate uint16_t status, change; 38177c478bd9Sstevel@tonic-gate hub_power_t *hubpm; 38187c478bd9Sstevel@tonic-gate dev_info_t *hdip = hubd->h_dip; 38197c478bd9Sstevel@tonic-gate dev_info_t *rh_dip = hubd->h_usba_device->usb_root_hub_dip; 3820c0f24e5bSlg dev_info_t *child_dip; 38217c478bd9Sstevel@tonic-gate boolean_t online_child = B_FALSE; 38227c478bd9Sstevel@tonic-gate boolean_t offline_child = B_FALSE; 38237c478bd9Sstevel@tonic-gate boolean_t pwrup_child = B_FALSE; 3824c0f24e5bSlg int prh_circ, rh_circ, chld_circ, circ, old_state; 38257c478bd9Sstevel@tonic-gate 38267c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 38277c478bd9Sstevel@tonic-gate "hubd_hotplug_thread: started"); 38287c478bd9Sstevel@tonic-gate 382940482326SVincent Wang /* 383040482326SVincent Wang * Before console is init'd, we temporarily block the hotplug 383140482326SVincent Wang * threads so that BUS_CONFIG_ONE through hubd_bus_config() can be 383240482326SVincent Wang * processed quickly. This reduces the time needed for vfs_mountroot() 38338e1b7aa1Sfei feng - Sun Microsystems - Beijing China * to mount the root FS from a USB disk. And on SPARC platform, 38348e1b7aa1Sfei feng - Sun Microsystems - Beijing China * in order to load 'consconfig' successfully after OBP is gone, 38358e1b7aa1Sfei feng - Sun Microsystems - Beijing China * we need to check 'modrootloaded' to make sure root filesystem is 38368e1b7aa1Sfei feng - Sun Microsystems - Beijing China * available. 383740482326SVincent Wang */ 38388e1b7aa1Sfei feng - Sun Microsystems - Beijing China while (!modrootloaded || !consconfig_console_is_ready()) { 383940482326SVincent Wang delay(drv_usectohz(10000)); 384040482326SVincent Wang } 384140482326SVincent Wang 38426c7181fcSsl kmem_free(arg, sizeof (hubd_hotplug_arg_t)); 38436c7181fcSsl 38447c478bd9Sstevel@tonic-gate /* 38457c478bd9Sstevel@tonic-gate * if our bus power entry point is active, process the change 38467c478bd9Sstevel@tonic-gate * on the next notification of interrupt pipe 38477c478bd9Sstevel@tonic-gate */ 38487c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 38497c478bd9Sstevel@tonic-gate if (hubd->h_bus_pwr || (hubd->h_hotplug_thread > 1)) { 38507c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread--; 38517c478bd9Sstevel@tonic-gate 38527c478bd9Sstevel@tonic-gate /* mark this device as idle */ 38537c478bd9Sstevel@tonic-gate hubd_pm_idle_component(hubd, hubd->h_dip, 0); 38547c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 38557c478bd9Sstevel@tonic-gate 38567c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 38577c478bd9Sstevel@tonic-gate "hubd_hotplug_thread: " 38587c478bd9Sstevel@tonic-gate "bus_power in progress/hotplugging undesirable - quit"); 38597c478bd9Sstevel@tonic-gate 38607c478bd9Sstevel@tonic-gate return; 38617c478bd9Sstevel@tonic-gate } 38627c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 38637c478bd9Sstevel@tonic-gate 38647c478bd9Sstevel@tonic-gate ndi_hold_devi(hdip); /* so we don't race with detach */ 38657c478bd9Sstevel@tonic-gate 38667c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 38677c478bd9Sstevel@tonic-gate 38687c478bd9Sstevel@tonic-gate /* is this the root hub? */ 38697c478bd9Sstevel@tonic-gate if (hdip == rh_dip) { 38707c478bd9Sstevel@tonic-gate if (hubd->h_dev_state == USB_DEV_PWRED_DOWN) { 38717c478bd9Sstevel@tonic-gate hubpm = hubd->h_hubpm; 38727c478bd9Sstevel@tonic-gate 38737c478bd9Sstevel@tonic-gate /* mark the root hub as full power */ 38747c478bd9Sstevel@tonic-gate hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR; 3875e5815e7aSJosef 'Jeff' Sipek hubpm->hubp_time_at_full_power = gethrtime(); 38767c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 38777c478bd9Sstevel@tonic-gate 38787c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 38797c478bd9Sstevel@tonic-gate "hubd_hotplug_thread: call pm_power_has_changed"); 38807c478bd9Sstevel@tonic-gate 38817c478bd9Sstevel@tonic-gate (void) pm_power_has_changed(hdip, 0, 3882c0f24e5bSlg USB_DEV_OS_FULL_PWR); 38837c478bd9Sstevel@tonic-gate 38847c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 38857c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_ONLINE; 38867c478bd9Sstevel@tonic-gate } 38877c478bd9Sstevel@tonic-gate 38887c478bd9Sstevel@tonic-gate } else { 38897c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 38907c478bd9Sstevel@tonic-gate "hubd_hotplug_thread: not root hub"); 38917c478bd9Sstevel@tonic-gate } 38927c478bd9Sstevel@tonic-gate 38937c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 38947c478bd9Sstevel@tonic-gate 38957c478bd9Sstevel@tonic-gate /* 38967c478bd9Sstevel@tonic-gate * this ensures one hotplug activity per system at a time. 38977c478bd9Sstevel@tonic-gate * we enter the parent PCI node to have this serialization. 38987c478bd9Sstevel@tonic-gate * this also excludes ioctls and deathrow thread 38997c478bd9Sstevel@tonic-gate * (a bit crude but easier to debug) 39007c478bd9Sstevel@tonic-gate */ 39017c478bd9Sstevel@tonic-gate ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ); 39027c478bd9Sstevel@tonic-gate ndi_devi_enter(rh_dip, &rh_circ); 39037c478bd9Sstevel@tonic-gate 39047c478bd9Sstevel@tonic-gate /* exclude other threads */ 39057c478bd9Sstevel@tonic-gate ndi_devi_enter(hdip, &circ); 39067c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 39077c478bd9Sstevel@tonic-gate 3908e4c316c4SVincent Wang ASSERT(hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE); 3909e4c316c4SVincent Wang 3910993e3fafSRobert Mustacchi nports = hubd->h_nports; 3911e4c316c4SVincent Wang 3912e4c316c4SVincent Wang hubd_stop_polling(hubd); 3913e4c316c4SVincent Wang 39147c478bd9Sstevel@tonic-gate while ((hubd->h_dev_state == USB_DEV_ONLINE) && 39157c478bd9Sstevel@tonic-gate (hubd->h_port_change)) { 39167c478bd9Sstevel@tonic-gate /* 39177c478bd9Sstevel@tonic-gate * The 0th bit is the hub status change bit. 39187c478bd9Sstevel@tonic-gate * handle loss of local power here 39197c478bd9Sstevel@tonic-gate */ 39207c478bd9Sstevel@tonic-gate if (hubd->h_port_change & HUB_CHANGE_STATUS) { 39217c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 39227c478bd9Sstevel@tonic-gate "hubd_hotplug_thread: hub status change!"); 39237c478bd9Sstevel@tonic-gate 39247c478bd9Sstevel@tonic-gate /* 39257c478bd9Sstevel@tonic-gate * This should be handled properly. For now, 39267c478bd9Sstevel@tonic-gate * mask off the bit. 39277c478bd9Sstevel@tonic-gate */ 39287c478bd9Sstevel@tonic-gate hubd->h_port_change &= ~HUB_CHANGE_STATUS; 39297c478bd9Sstevel@tonic-gate 39307c478bd9Sstevel@tonic-gate /* 39317c478bd9Sstevel@tonic-gate * check and ack hub status 39327c478bd9Sstevel@tonic-gate * this causes stall conditions 39337c478bd9Sstevel@tonic-gate * when local power is removed 39347c478bd9Sstevel@tonic-gate */ 39357c478bd9Sstevel@tonic-gate (void) hubd_get_hub_status(hubd); 39367c478bd9Sstevel@tonic-gate } 39377c478bd9Sstevel@tonic-gate 39387c478bd9Sstevel@tonic-gate for (port = 1; port <= nports; port++) { 39397c478bd9Sstevel@tonic-gate usb_port_mask_t port_mask; 39407c478bd9Sstevel@tonic-gate boolean_t was_connected; 39417c478bd9Sstevel@tonic-gate 39427c478bd9Sstevel@tonic-gate port_mask = 1 << port; 39437c478bd9Sstevel@tonic-gate was_connected = 3944c0f24e5bSlg (hubd->h_port_state[port] & PORT_STATUS_CCS) && 3945c0f24e5bSlg (hubd->h_children_dips[port]); 39467c478bd9Sstevel@tonic-gate 39477c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 39487c478bd9Sstevel@tonic-gate "hubd_hotplug_thread: " 39497c478bd9Sstevel@tonic-gate "port %d mask=0x%x change=0x%x connected=0x%x", 39507c478bd9Sstevel@tonic-gate port, port_mask, hubd->h_port_change, 39517c478bd9Sstevel@tonic-gate was_connected); 39527c478bd9Sstevel@tonic-gate 39537c478bd9Sstevel@tonic-gate /* 39547c478bd9Sstevel@tonic-gate * is this a port connection that changed? 39557c478bd9Sstevel@tonic-gate */ 39567c478bd9Sstevel@tonic-gate if ((hubd->h_port_change & port_mask) == 0) { 39577c478bd9Sstevel@tonic-gate 39587c478bd9Sstevel@tonic-gate continue; 39597c478bd9Sstevel@tonic-gate } 39607c478bd9Sstevel@tonic-gate hubd->h_port_change &= ~port_mask; 39617c478bd9Sstevel@tonic-gate 39627c478bd9Sstevel@tonic-gate /* ack all changes */ 39637c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 3964993e3fafSRobert Mustacchi &status, &change, NULL, HUBD_ACK_ALL_CHANGES); 39657c478bd9Sstevel@tonic-gate 39667c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 39677c478bd9Sstevel@tonic-gate "handle port %d:\n\t" 39687c478bd9Sstevel@tonic-gate "new status=0x%x change=0x%x was_conn=0x%x ", 39697c478bd9Sstevel@tonic-gate port, status, change, was_connected); 39707c478bd9Sstevel@tonic-gate 39717c478bd9Sstevel@tonic-gate /* Recover a disabled port */ 39727c478bd9Sstevel@tonic-gate if (change & PORT_CHANGE_PESC) { 39737c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, 3974c0f24e5bSlg hubd->h_log_handle, 3975c0f24e5bSlg "port%d Disabled - " 3976c0f24e5bSlg "status=0x%x, change=0x%x", 3977c0f24e5bSlg port, status, change); 39787c478bd9Sstevel@tonic-gate 39797c478bd9Sstevel@tonic-gate /* 39807c478bd9Sstevel@tonic-gate * if the port was connected and is still 39817c478bd9Sstevel@tonic-gate * connected, recover the port 39827c478bd9Sstevel@tonic-gate */ 39837c478bd9Sstevel@tonic-gate if (was_connected && (status & 39847c478bd9Sstevel@tonic-gate PORT_STATUS_CCS)) { 39857c478bd9Sstevel@tonic-gate online_child |= 39867c478bd9Sstevel@tonic-gate (hubd_recover_disabled_port(hubd, 39877c478bd9Sstevel@tonic-gate port) == USB_SUCCESS); 39887c478bd9Sstevel@tonic-gate } 39897c478bd9Sstevel@tonic-gate } 39907c478bd9Sstevel@tonic-gate 39917c478bd9Sstevel@tonic-gate /* 39927c478bd9Sstevel@tonic-gate * Now check what changed on the port 39937c478bd9Sstevel@tonic-gate */ 39946c7181fcSsl if ((change & PORT_CHANGE_CSC) || attach_flg) { 39957c478bd9Sstevel@tonic-gate if ((status & PORT_STATUS_CCS) && 39967c478bd9Sstevel@tonic-gate (!was_connected)) { 39977c478bd9Sstevel@tonic-gate /* new device plugged in */ 39987c478bd9Sstevel@tonic-gate online_child |= 39997c478bd9Sstevel@tonic-gate (hubd_handle_port_connect(hubd, 40007c478bd9Sstevel@tonic-gate port) == USB_SUCCESS); 40017c478bd9Sstevel@tonic-gate 40027c478bd9Sstevel@tonic-gate } else if ((status & PORT_STATUS_CCS) && 40037c478bd9Sstevel@tonic-gate was_connected) { 40047c478bd9Sstevel@tonic-gate /* 40057c478bd9Sstevel@tonic-gate * In this case we can never be sure 40067c478bd9Sstevel@tonic-gate * if the device indeed got hotplugged 40077c478bd9Sstevel@tonic-gate * or the hub is falsely reporting the 40087c478bd9Sstevel@tonic-gate * change. 40097c478bd9Sstevel@tonic-gate */ 4010c0f24e5bSlg child_dip = hubd->h_children_dips[port]; 40117c478bd9Sstevel@tonic-gate 4012c0f24e5bSlg mutex_exit(HUBD_MUTEX(hubd)); 40137c478bd9Sstevel@tonic-gate /* 4014c0f24e5bSlg * this ensures we do not race with 4015c0f24e5bSlg * other threads which are detaching 4016c0f24e5bSlg * the child driver at the same time. 40177c478bd9Sstevel@tonic-gate */ 4018c0f24e5bSlg ndi_devi_enter(child_dip, &chld_circ); 4019c0f24e5bSlg /* 4020c0f24e5bSlg * Now check if the driver remains 4021c0f24e5bSlg * attached. 4022c0f24e5bSlg */ 4023c0f24e5bSlg if (i_ddi_devi_attached(child_dip)) { 4024c0f24e5bSlg /* 4025c0f24e5bSlg * first post a disconnect event 4026c0f24e5bSlg * to the child. 4027c0f24e5bSlg */ 4028c0f24e5bSlg hubd_post_event(hubd, port, 4029c0f24e5bSlg USBA_EVENT_TAG_HOT_REMOVAL); 4030c0f24e5bSlg mutex_enter(HUBD_MUTEX(hubd)); 4031c0f24e5bSlg 4032c0f24e5bSlg /* 4033c0f24e5bSlg * then reset the port and 4034c0f24e5bSlg * recover the device 4035c0f24e5bSlg */ 4036c0f24e5bSlg online_child |= 4037c0f24e5bSlg (hubd_handle_port_connect( 4038c0f24e5bSlg hubd, port) == USB_SUCCESS); 4039c0f24e5bSlg 4040c0f24e5bSlg mutex_exit(HUBD_MUTEX(hubd)); 4041c0f24e5bSlg } 4042c0f24e5bSlg 4043c0f24e5bSlg ndi_devi_exit(child_dip, chld_circ); 4044c0f24e5bSlg mutex_enter(HUBD_MUTEX(hubd)); 40457c478bd9Sstevel@tonic-gate } else if (was_connected) { 40467c478bd9Sstevel@tonic-gate /* this is a disconnect */ 40477c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 40487c478bd9Sstevel@tonic-gate hubd_post_event(hubd, port, 40497c478bd9Sstevel@tonic-gate USBA_EVENT_TAG_HOT_REMOVAL); 40507c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 40517c478bd9Sstevel@tonic-gate 40527c478bd9Sstevel@tonic-gate offline_child = B_TRUE; 40537c478bd9Sstevel@tonic-gate } 40547c478bd9Sstevel@tonic-gate } 40557c478bd9Sstevel@tonic-gate 40567c478bd9Sstevel@tonic-gate /* 40577c478bd9Sstevel@tonic-gate * Check if any port is coming out of suspend 40587c478bd9Sstevel@tonic-gate */ 40597c478bd9Sstevel@tonic-gate if (change & PORT_CHANGE_PSSC) { 40607c478bd9Sstevel@tonic-gate /* a resuming device could have disconnected */ 40617c478bd9Sstevel@tonic-gate if (was_connected && 40627c478bd9Sstevel@tonic-gate hubd->h_children_dips[port]) { 40637c478bd9Sstevel@tonic-gate 40647c478bd9Sstevel@tonic-gate /* device on this port resuming */ 40657c478bd9Sstevel@tonic-gate dev_info_t *dip; 40667c478bd9Sstevel@tonic-gate 40677c478bd9Sstevel@tonic-gate dip = hubd->h_children_dips[port]; 40687c478bd9Sstevel@tonic-gate 40697c478bd9Sstevel@tonic-gate /* 40707c478bd9Sstevel@tonic-gate * Don't raise power on detaching child 40717c478bd9Sstevel@tonic-gate */ 40727c478bd9Sstevel@tonic-gate if (!DEVI_IS_DETACHING(dip)) { 40737c478bd9Sstevel@tonic-gate /* 40747c478bd9Sstevel@tonic-gate * As this child is not 40757c478bd9Sstevel@tonic-gate * detaching, we set this 40767c478bd9Sstevel@tonic-gate * flag, causing bus_ctls 40777c478bd9Sstevel@tonic-gate * to stall detach till 40787c478bd9Sstevel@tonic-gate * pm_raise_power returns 40797c478bd9Sstevel@tonic-gate * and flag it for a deferred 40807c478bd9Sstevel@tonic-gate * raise_power. 40817c478bd9Sstevel@tonic-gate * 40827c478bd9Sstevel@tonic-gate * pm_raise_power is deferred 40837c478bd9Sstevel@tonic-gate * because we need to release 40847c478bd9Sstevel@tonic-gate * the locks first. 40857c478bd9Sstevel@tonic-gate */ 40867c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= 4087c0f24e5bSlg HUBD_CHILD_RAISE_POWER; 40887c478bd9Sstevel@tonic-gate pwrup_child = B_TRUE; 40897c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 40907c478bd9Sstevel@tonic-gate 40917c478bd9Sstevel@tonic-gate /* 40927c478bd9Sstevel@tonic-gate * make sure that child 40937c478bd9Sstevel@tonic-gate * doesn't disappear 40947c478bd9Sstevel@tonic-gate */ 40957c478bd9Sstevel@tonic-gate ndi_hold_devi(dip); 40967c478bd9Sstevel@tonic-gate 40977c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 40987c478bd9Sstevel@tonic-gate } 40997c478bd9Sstevel@tonic-gate } 41007c478bd9Sstevel@tonic-gate } 4101fef1e07eSsl 4102fef1e07eSsl /* 4103fef1e07eSsl * Check if the port is over-current 4104fef1e07eSsl */ 4105fef1e07eSsl if (change & PORT_CHANGE_OCIC) { 4106fef1e07eSsl USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, 4107fef1e07eSsl hubd->h_log_handle, 4108fef1e07eSsl "Port%d in over current condition, " 4109fef1e07eSsl "please check the attached device to " 4110fef1e07eSsl "clear the condition. The system will " 4111fef1e07eSsl "try to recover the port, but if not " 4112fef1e07eSsl "successful, you need to re-connect " 4113fef1e07eSsl "the hub or reboot the system to bring " 4114fef1e07eSsl "the port back to work", port); 4115fef1e07eSsl 4116fef1e07eSsl if (!(status & PORT_STATUS_PPS)) { 4117fef1e07eSsl /* 4118fef1e07eSsl * Try to enable port power, but 4119fef1e07eSsl * possibly fail. Ignore failure 4120fef1e07eSsl */ 4121fef1e07eSsl (void) hubd_enable_port_power(hubd, 4122fef1e07eSsl port); 4123fef1e07eSsl 4124fef1e07eSsl /* 4125fef1e07eSsl * Delay some time to avoid 4126fef1e07eSsl * over-current event to happen 4127fef1e07eSsl * too frequently in some cases 4128fef1e07eSsl */ 4129fef1e07eSsl mutex_exit(HUBD_MUTEX(hubd)); 4130fef1e07eSsl delay(drv_usectohz(500000)); 4131fef1e07eSsl mutex_enter(HUBD_MUTEX(hubd)); 4132fef1e07eSsl } 4133fef1e07eSsl } 41347c478bd9Sstevel@tonic-gate } 41357c478bd9Sstevel@tonic-gate } 41367c478bd9Sstevel@tonic-gate 41377c478bd9Sstevel@tonic-gate /* release locks so we can do a devfs_clean */ 41387c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 41397c478bd9Sstevel@tonic-gate 41407c478bd9Sstevel@tonic-gate /* delete cached dv_node's but drop locks first */ 41417c478bd9Sstevel@tonic-gate ndi_devi_exit(hdip, circ); 41427c478bd9Sstevel@tonic-gate ndi_devi_exit(rh_dip, rh_circ); 41437c478bd9Sstevel@tonic-gate ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ); 41447c478bd9Sstevel@tonic-gate 41457c478bd9Sstevel@tonic-gate (void) devfs_clean(rh_dip, NULL, 0); 41467c478bd9Sstevel@tonic-gate 41477c478bd9Sstevel@tonic-gate /* now check if any children need onlining */ 41487c478bd9Sstevel@tonic-gate if (online_child) { 41497c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 41507c478bd9Sstevel@tonic-gate "hubd_hotplug_thread: onlining children"); 41517c478bd9Sstevel@tonic-gate 415240482326SVincent Wang (void) ndi_devi_online(hubd->h_dip, 0); 41537c478bd9Sstevel@tonic-gate } 41547c478bd9Sstevel@tonic-gate 41557c478bd9Sstevel@tonic-gate /* now check if any disconnected devices need to be cleaned up */ 41567c478bd9Sstevel@tonic-gate if (offline_child) { 41577c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 41587c478bd9Sstevel@tonic-gate "hubd_hotplug_thread: scheduling cleanup"); 41597c478bd9Sstevel@tonic-gate 41607c478bd9Sstevel@tonic-gate hubd_schedule_cleanup(hubd->h_usba_device->usb_root_hub_dip); 41617c478bd9Sstevel@tonic-gate } 41627c478bd9Sstevel@tonic-gate 41637c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 41647c478bd9Sstevel@tonic-gate 41657c478bd9Sstevel@tonic-gate /* now raise power on the children that have woken up */ 41667c478bd9Sstevel@tonic-gate if (pwrup_child) { 41677c478bd9Sstevel@tonic-gate old_state = hubd->h_dev_state; 41687c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_HUB_CHILD_PWRLVL; 41697c478bd9Sstevel@tonic-gate for (port = 1; port <= nports; port++) { 41707c478bd9Sstevel@tonic-gate if (hubd->h_port_state[port] & HUBD_CHILD_RAISE_POWER) { 41717c478bd9Sstevel@tonic-gate dev_info_t *dip = hubd->h_children_dips[port]; 41727c478bd9Sstevel@tonic-gate 41737c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 41747c478bd9Sstevel@tonic-gate 41757c478bd9Sstevel@tonic-gate /* Get the device to full power */ 41767c478bd9Sstevel@tonic-gate (void) pm_busy_component(dip, 0); 41777c478bd9Sstevel@tonic-gate (void) pm_raise_power(dip, 0, 41787c478bd9Sstevel@tonic-gate USB_DEV_OS_FULL_PWR); 41797c478bd9Sstevel@tonic-gate (void) pm_idle_component(dip, 0); 41807c478bd9Sstevel@tonic-gate 41817c478bd9Sstevel@tonic-gate /* release the hold on the child */ 41827c478bd9Sstevel@tonic-gate ndi_rele_devi(dip); 41837c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 41847c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= 41857c478bd9Sstevel@tonic-gate ~HUBD_CHILD_RAISE_POWER; 41867c478bd9Sstevel@tonic-gate } 41877c478bd9Sstevel@tonic-gate } 41887c478bd9Sstevel@tonic-gate /* 41897c478bd9Sstevel@tonic-gate * make sure that we don't accidentally 41907c478bd9Sstevel@tonic-gate * over write the disconnect state 41917c478bd9Sstevel@tonic-gate */ 41927c478bd9Sstevel@tonic-gate if (hubd->h_dev_state == USB_DEV_HUB_CHILD_PWRLVL) { 41937c478bd9Sstevel@tonic-gate hubd->h_dev_state = old_state; 41947c478bd9Sstevel@tonic-gate } 41957c478bd9Sstevel@tonic-gate } 41967c478bd9Sstevel@tonic-gate 41977c478bd9Sstevel@tonic-gate /* 41987c478bd9Sstevel@tonic-gate * start polling can immediately kick off read callback 41997c478bd9Sstevel@tonic-gate * we need to set the h_hotplug_thread to 0 so that 42007c478bd9Sstevel@tonic-gate * the callback is not dropped 4201ffcd51f3Slg * 4202ffcd51f3Slg * if there is device during reset, still stop polling to avoid the 4203ffcd51f3Slg * read callback interrupting the reset, the polling will be started 4204ffcd51f3Slg * in hubd_reset_thread. 42057c478bd9Sstevel@tonic-gate */ 4206ffcd51f3Slg for (port = 1; port <= MAX_PORTS; port++) { 4207ffcd51f3Slg if (hubd->h_reset_port[port]) { 4208ffcd51f3Slg 4209ffcd51f3Slg break; 4210ffcd51f3Slg } 4211ffcd51f3Slg } 4212ffcd51f3Slg if (port > MAX_PORTS) { 4213ffcd51f3Slg hubd_start_polling(hubd, HUBD_ALWAYS_START_POLLING); 4214ffcd51f3Slg } 42157c478bd9Sstevel@tonic-gate 42167c478bd9Sstevel@tonic-gate /* 42177c478bd9Sstevel@tonic-gate * Earlier we would set the h_hotplug_thread = 0 before 42187c478bd9Sstevel@tonic-gate * polling was restarted so that 42197c478bd9Sstevel@tonic-gate * if there is any root hub status change interrupt, we can still kick 42207c478bd9Sstevel@tonic-gate * off the hotplug thread. This was valid when this interrupt was 42217c478bd9Sstevel@tonic-gate * delivered in hardware, and only ONE interrupt would be delivered. 42227c478bd9Sstevel@tonic-gate * Now that we poll on the root hub looking for status change in 42237c478bd9Sstevel@tonic-gate * software, this assignment is no longer required. 42247c478bd9Sstevel@tonic-gate */ 42257c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread--; 42267c478bd9Sstevel@tonic-gate 42277c478bd9Sstevel@tonic-gate /* mark this device as idle */ 42287c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0); 42297c478bd9Sstevel@tonic-gate 4230ffcd51f3Slg cv_broadcast(&hubd->h_cv_hotplug_dev); 4231ffcd51f3Slg 42327c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 42337c478bd9Sstevel@tonic-gate "hubd_hotplug_thread: exit"); 42347c478bd9Sstevel@tonic-gate 42357c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 42367c478bd9Sstevel@tonic-gate 42377c478bd9Sstevel@tonic-gate ndi_rele_devi(hdip); 42387c478bd9Sstevel@tonic-gate } 42397c478bd9Sstevel@tonic-gate 42407c478bd9Sstevel@tonic-gate 42417c478bd9Sstevel@tonic-gate /* 42427c478bd9Sstevel@tonic-gate * hubd_handle_port_connect: 42437c478bd9Sstevel@tonic-gate * Transition a port from Disabled to Enabled. Ensure that the 42447c478bd9Sstevel@tonic-gate * port is in the correct state before attempting to 42457c478bd9Sstevel@tonic-gate * access the device. 42467c478bd9Sstevel@tonic-gate */ 42477c478bd9Sstevel@tonic-gate static int 42487c478bd9Sstevel@tonic-gate hubd_handle_port_connect(hubd_t *hubd, usb_port_t port) 42497c478bd9Sstevel@tonic-gate { 42507c478bd9Sstevel@tonic-gate int rval; 42517c478bd9Sstevel@tonic-gate int retry; 42527c478bd9Sstevel@tonic-gate long time_delay; 42537c478bd9Sstevel@tonic-gate long settling_time; 42547c478bd9Sstevel@tonic-gate uint16_t status; 42557c478bd9Sstevel@tonic-gate uint16_t change; 4256993e3fafSRobert Mustacchi usb_port_status_t speed; 42577c478bd9Sstevel@tonic-gate usb_addr_t hubd_usb_addr; 42587c478bd9Sstevel@tonic-gate usba_device_t *usba_device; 42597c478bd9Sstevel@tonic-gate usb_port_status_t port_status = 0; 42607c478bd9Sstevel@tonic-gate usb_port_status_t hub_port_status = 0; 42617c478bd9Sstevel@tonic-gate 42627c478bd9Sstevel@tonic-gate /* Get the hub address and port status */ 42637c478bd9Sstevel@tonic-gate usba_device = hubd->h_usba_device; 42647c478bd9Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex); 42657c478bd9Sstevel@tonic-gate hubd_usb_addr = usba_device->usb_addr; 42667c478bd9Sstevel@tonic-gate hub_port_status = usba_device->usb_port_status; 42677c478bd9Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex); 42687c478bd9Sstevel@tonic-gate 42697c478bd9Sstevel@tonic-gate /* 42707c478bd9Sstevel@tonic-gate * If a device is connected, transition the 42717c478bd9Sstevel@tonic-gate * port from Disabled to the Enabled state. 42727c478bd9Sstevel@tonic-gate * The device will receive downstream packets 42737c478bd9Sstevel@tonic-gate * in the Enabled state. 42747c478bd9Sstevel@tonic-gate * 42757c478bd9Sstevel@tonic-gate * reset port and wait for the hub to report 42767c478bd9Sstevel@tonic-gate * completion 42777c478bd9Sstevel@tonic-gate */ 42787c478bd9Sstevel@tonic-gate change = status = 0; 42797c478bd9Sstevel@tonic-gate 42807c478bd9Sstevel@tonic-gate /* 42817c478bd9Sstevel@tonic-gate * According to section 9.1.2 of USB 2.0 spec, the host should 42827c478bd9Sstevel@tonic-gate * wait for atleast 100ms to allow completion of an insertion 42837c478bd9Sstevel@tonic-gate * process and for power at the device to become stable. 42847c478bd9Sstevel@tonic-gate * We wait for 200 ms 42857c478bd9Sstevel@tonic-gate */ 42867c478bd9Sstevel@tonic-gate settling_time = drv_usectohz(hubd_device_delay / 5); 42877c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 42887c478bd9Sstevel@tonic-gate delay(settling_time); 42897c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 42907c478bd9Sstevel@tonic-gate 42917c478bd9Sstevel@tonic-gate /* calculate 600 ms delay time */ 42927c478bd9Sstevel@tonic-gate time_delay = (6 * drv_usectohz(hubd_device_delay)) / 10; 42937c478bd9Sstevel@tonic-gate 42947c478bd9Sstevel@tonic-gate for (retry = 0; (hubd->h_dev_state == USB_DEV_ONLINE) && 42957c478bd9Sstevel@tonic-gate (retry < hubd_retry_enumerate); retry++) { 42967c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 42977c478bd9Sstevel@tonic-gate "resetting port%d, retry=%d", port, retry); 42987c478bd9Sstevel@tonic-gate 42997c478bd9Sstevel@tonic-gate if ((rval = hubd_reset_port(hubd, port)) != USB_SUCCESS) { 43007c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, 4301993e3fafSRobert Mustacchi port, &status, &change, &speed, 0); 43027c478bd9Sstevel@tonic-gate 43037c478bd9Sstevel@tonic-gate /* continue only if port is still connected */ 43047c478bd9Sstevel@tonic-gate if (status & PORT_STATUS_CCS) { 43057c478bd9Sstevel@tonic-gate continue; 43067c478bd9Sstevel@tonic-gate } 43077c478bd9Sstevel@tonic-gate 43087c478bd9Sstevel@tonic-gate /* carry on regardless */ 43097c478bd9Sstevel@tonic-gate } 43107c478bd9Sstevel@tonic-gate 43117c478bd9Sstevel@tonic-gate /* 43127c478bd9Sstevel@tonic-gate * according to USB 2.0 spec section 11.24.2.7.1.2 43137c478bd9Sstevel@tonic-gate * at the end of port reset, the hub enables the port. 43147c478bd9Sstevel@tonic-gate * But for some strange reasons, uhci port remains disabled. 43157c478bd9Sstevel@tonic-gate * And because the port remains disabled for the settling 43167c478bd9Sstevel@tonic-gate * time below, the device connected to the port gets wedged 43177c478bd9Sstevel@tonic-gate * - fails to enumerate (device not responding) 43187c478bd9Sstevel@tonic-gate * Hence, we enable it here immediately and later again after 43197c478bd9Sstevel@tonic-gate * the delay 43207c478bd9Sstevel@tonic-gate */ 43217c478bd9Sstevel@tonic-gate (void) hubd_enable_port(hubd, port); 43227c478bd9Sstevel@tonic-gate 43237c478bd9Sstevel@tonic-gate /* we skip this delay in the first iteration */ 43247c478bd9Sstevel@tonic-gate if (retry) { 43257c478bd9Sstevel@tonic-gate /* 43267c478bd9Sstevel@tonic-gate * delay for device to signal disconnect/connect so 43277c478bd9Sstevel@tonic-gate * that hub properly recognizes the speed of the device 43287c478bd9Sstevel@tonic-gate */ 43297c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 43307c478bd9Sstevel@tonic-gate delay(settling_time); 43317c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 43327c478bd9Sstevel@tonic-gate 43337c478bd9Sstevel@tonic-gate /* 43347c478bd9Sstevel@tonic-gate * When a low speed device is connected to any port of 43357c478bd9Sstevel@tonic-gate * PPX it has to be explicitly enabled 43367c478bd9Sstevel@tonic-gate * Also, if device intentionally signals 43377c478bd9Sstevel@tonic-gate * disconnect/connect, it will disable the port. 43387c478bd9Sstevel@tonic-gate * So enable it again. 43397c478bd9Sstevel@tonic-gate */ 43407c478bd9Sstevel@tonic-gate (void) hubd_enable_port(hubd, port); 43417c478bd9Sstevel@tonic-gate } 43427c478bd9Sstevel@tonic-gate 43437c478bd9Sstevel@tonic-gate if ((rval = hubd_determine_port_status(hubd, port, &status, 4344993e3fafSRobert Mustacchi &change, &speed, 0)) != USB_SUCCESS) { 43457c478bd9Sstevel@tonic-gate 4346d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 43477c478bd9Sstevel@tonic-gate "getting status failed (%d)", rval); 43487c478bd9Sstevel@tonic-gate 43497c478bd9Sstevel@tonic-gate (void) hubd_disable_port(hubd, port); 43507c478bd9Sstevel@tonic-gate 43517c478bd9Sstevel@tonic-gate continue; 43527c478bd9Sstevel@tonic-gate } 43537c478bd9Sstevel@tonic-gate 43547c478bd9Sstevel@tonic-gate if (status & PORT_STATUS_POCI) { 4355d291d9f2Sfrits USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 43567c478bd9Sstevel@tonic-gate "port %d overcurrent", port); 43577c478bd9Sstevel@tonic-gate 43587c478bd9Sstevel@tonic-gate (void) hubd_disable_port(hubd, port); 43597c478bd9Sstevel@tonic-gate 43607c478bd9Sstevel@tonic-gate /* ack changes */ 43617c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, 4362993e3fafSRobert Mustacchi port, &status, &change, &speed, PORT_CHANGE_OCIC); 43637c478bd9Sstevel@tonic-gate 43647c478bd9Sstevel@tonic-gate continue; 43657c478bd9Sstevel@tonic-gate } 43667c478bd9Sstevel@tonic-gate 43677c478bd9Sstevel@tonic-gate /* is status really OK? */ 43687c478bd9Sstevel@tonic-gate if ((status & PORT_STATUS_OK) != PORT_STATUS_OK) { 43697c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 43707c478bd9Sstevel@tonic-gate "port %d status (0x%x) not OK on retry %d", 43717c478bd9Sstevel@tonic-gate port, status, retry); 43727c478bd9Sstevel@tonic-gate 43737c478bd9Sstevel@tonic-gate /* check if we still have the connection */ 43747c478bd9Sstevel@tonic-gate if (!(status & PORT_STATUS_CCS)) { 43757c478bd9Sstevel@tonic-gate /* lost connection, set exit condition */ 43767c478bd9Sstevel@tonic-gate retry = hubd_retry_enumerate; 43777c478bd9Sstevel@tonic-gate 43787c478bd9Sstevel@tonic-gate break; 43797c478bd9Sstevel@tonic-gate } 43807c478bd9Sstevel@tonic-gate } else { 4381993e3fafSRobert Mustacchi port_status = speed; 43827c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 43837c478bd9Sstevel@tonic-gate "creating child port%d, status=0x%x " 43847c478bd9Sstevel@tonic-gate "port status=0x%x", 43857c478bd9Sstevel@tonic-gate port, status, port_status); 43867c478bd9Sstevel@tonic-gate 43877c478bd9Sstevel@tonic-gate /* 43887c478bd9Sstevel@tonic-gate * if the child already exists, set addrs and config 43897c478bd9Sstevel@tonic-gate * to the device post connect event to the child 43907c478bd9Sstevel@tonic-gate */ 43917c478bd9Sstevel@tonic-gate if (hubd->h_children_dips[port]) { 43927c478bd9Sstevel@tonic-gate /* set addrs to this device */ 43937c478bd9Sstevel@tonic-gate rval = hubd_setdevaddr(hubd, port); 43947c478bd9Sstevel@tonic-gate 43957c478bd9Sstevel@tonic-gate /* 43967c478bd9Sstevel@tonic-gate * This delay is important for the CATC hub 43977c478bd9Sstevel@tonic-gate * to enumerate. But, avoid delay in the first 43987c478bd9Sstevel@tonic-gate * iteration 43997c478bd9Sstevel@tonic-gate */ 44007c478bd9Sstevel@tonic-gate if (retry) { 44017c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 44027c478bd9Sstevel@tonic-gate delay(drv_usectohz( 44037c478bd9Sstevel@tonic-gate hubd_device_delay/100)); 44047c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 44057c478bd9Sstevel@tonic-gate } 44067c478bd9Sstevel@tonic-gate 44077c478bd9Sstevel@tonic-gate if (rval == USB_SUCCESS) { 4408ffcd51f3Slg /* 4409ffcd51f3Slg * if the port is resetting, check if 4410ffcd51f3Slg * device's descriptors have changed. 4411ffcd51f3Slg */ 4412ffcd51f3Slg if ((hubd->h_reset_port[port]) && 4413ffcd51f3Slg (hubd_check_same_device(hubd, 4414ffcd51f3Slg port) != USB_SUCCESS)) { 4415ffcd51f3Slg retry = hubd_retry_enumerate; 4416ffcd51f3Slg 4417ffcd51f3Slg break; 4418ffcd51f3Slg } 4419ffcd51f3Slg 44207c478bd9Sstevel@tonic-gate /* 44217c478bd9Sstevel@tonic-gate * set the default config for 44227c478bd9Sstevel@tonic-gate * this device 44237c478bd9Sstevel@tonic-gate */ 44247c478bd9Sstevel@tonic-gate hubd_setdevconfig(hubd, port); 44257c478bd9Sstevel@tonic-gate 4426ffcd51f3Slg /* 4427ffcd51f3Slg * if we are doing Default reset, do 4428ffcd51f3Slg * not post reconnect event since we 4429ffcd51f3Slg * don't know where reset function is 4430ffcd51f3Slg * called. 4431ffcd51f3Slg */ 4432ffcd51f3Slg if (hubd->h_reset_port[port]) { 4433ffcd51f3Slg 4434ffcd51f3Slg return (USB_SUCCESS); 4435ffcd51f3Slg } 4436ffcd51f3Slg 44377c478bd9Sstevel@tonic-gate /* 44387c478bd9Sstevel@tonic-gate * indicate to the child that 44397c478bd9Sstevel@tonic-gate * it is online again 44407c478bd9Sstevel@tonic-gate */ 44417c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 44427c478bd9Sstevel@tonic-gate hubd_post_event(hubd, port, 44437c478bd9Sstevel@tonic-gate USBA_EVENT_TAG_HOT_INSERTION); 44447c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 44457c478bd9Sstevel@tonic-gate 44467c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 44477c478bd9Sstevel@tonic-gate } 44487c478bd9Sstevel@tonic-gate } else { 44497c478bd9Sstevel@tonic-gate /* 44507c478bd9Sstevel@tonic-gate * We need to release access here 44517c478bd9Sstevel@tonic-gate * so that busctls on other ports can 44527c478bd9Sstevel@tonic-gate * continue and don't cause a deadlock 44537c478bd9Sstevel@tonic-gate * when busctl and removal of prom node 44547c478bd9Sstevel@tonic-gate * takes concurrently. This also ensures 44557c478bd9Sstevel@tonic-gate * busctls for attach of successfully 44567c478bd9Sstevel@tonic-gate * enumerated devices on other ports can 44577c478bd9Sstevel@tonic-gate * continue concurrently with the process 44587c478bd9Sstevel@tonic-gate * of enumerating the new devices. This 44597c478bd9Sstevel@tonic-gate * reduces the overall boot time of the system. 44607c478bd9Sstevel@tonic-gate */ 44617c478bd9Sstevel@tonic-gate rval = hubd_create_child(hubd->h_dip, 4462c0f24e5bSlg hubd, 4463c0f24e5bSlg hubd->h_usba_device, 4464c0f24e5bSlg port_status, port, 4465c0f24e5bSlg retry); 44667c478bd9Sstevel@tonic-gate if (rval == USB_SUCCESS) { 44677c478bd9Sstevel@tonic-gate usba_update_hotplug_stats(hubd->h_dip, 44687c478bd9Sstevel@tonic-gate USBA_TOTAL_HOTPLUG_SUCCESS| 44697c478bd9Sstevel@tonic-gate USBA_HOTPLUG_SUCCESS); 44707c478bd9Sstevel@tonic-gate hubd->h_total_hotplug_success++; 44717c478bd9Sstevel@tonic-gate 44727c478bd9Sstevel@tonic-gate if (retry > 0) { 4473d291d9f2Sfrits USB_DPRINTF_L2( 44747c478bd9Sstevel@tonic-gate DPRINT_MASK_HOTPLUG, 44757c478bd9Sstevel@tonic-gate hubd->h_log_handle, 44767c478bd9Sstevel@tonic-gate "device on port %d " 44777c478bd9Sstevel@tonic-gate "enumerated after %d %s", 44787c478bd9Sstevel@tonic-gate port, retry, 44797c478bd9Sstevel@tonic-gate (retry > 1) ? "retries" : 44807c478bd9Sstevel@tonic-gate "retry"); 44817c478bd9Sstevel@tonic-gate 44827c478bd9Sstevel@tonic-gate } 44837c478bd9Sstevel@tonic-gate 44847c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 44857c478bd9Sstevel@tonic-gate } 44867c478bd9Sstevel@tonic-gate } 44877c478bd9Sstevel@tonic-gate } 44887c478bd9Sstevel@tonic-gate 44897c478bd9Sstevel@tonic-gate /* wait a while until it settles? */ 44907c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 44917c478bd9Sstevel@tonic-gate "disabling port %d again", port); 44927c478bd9Sstevel@tonic-gate 44937c478bd9Sstevel@tonic-gate (void) hubd_disable_port(hubd, port); 44947c478bd9Sstevel@tonic-gate if (retry) { 44957c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 44967c478bd9Sstevel@tonic-gate delay(time_delay); 44977c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 44987c478bd9Sstevel@tonic-gate } 44997c478bd9Sstevel@tonic-gate 45007c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 45017c478bd9Sstevel@tonic-gate "retrying on port %d", port); 45027c478bd9Sstevel@tonic-gate } 45037c478bd9Sstevel@tonic-gate 45047c478bd9Sstevel@tonic-gate if (retry >= hubd_retry_enumerate) { 45057c478bd9Sstevel@tonic-gate /* 45067c478bd9Sstevel@tonic-gate * If it is a High Speed Root Hub and connected device 45077c478bd9Sstevel@tonic-gate * Is a Low/Full Speed, it will be handled by USB 1.1 45087c478bd9Sstevel@tonic-gate * Host Controller. In this case, USB 2.0 Host Controller 45097c478bd9Sstevel@tonic-gate * will transfer the ownership of this port to USB 1.1 45107c478bd9Sstevel@tonic-gate * Host Controller. So don't display any error message on 4511993e3fafSRobert Mustacchi * the console. Note, this isn't the case for USB 3.x. 45127c478bd9Sstevel@tonic-gate */ 45137c478bd9Sstevel@tonic-gate if ((hubd_usb_addr == ROOT_HUB_ADDR) && 45147c478bd9Sstevel@tonic-gate (hub_port_status == USBA_HIGH_SPEED_DEV) && 45157c478bd9Sstevel@tonic-gate (port_status != USBA_HIGH_SPEED_DEV)) { 45167c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 45177c478bd9Sstevel@tonic-gate hubd->h_log_handle, 45187c478bd9Sstevel@tonic-gate "hubd_handle_port_connect: Low/Full speed " 45197c478bd9Sstevel@tonic-gate "device is connected to High Speed root hub"); 45207c478bd9Sstevel@tonic-gate } else { 45217c478bd9Sstevel@tonic-gate USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, 45227c478bd9Sstevel@tonic-gate hubd->h_log_handle, 45237c478bd9Sstevel@tonic-gate "Connecting device on port %d failed", port); 45247c478bd9Sstevel@tonic-gate } 45257c478bd9Sstevel@tonic-gate 45267c478bd9Sstevel@tonic-gate (void) hubd_disable_port(hubd, port); 45277c478bd9Sstevel@tonic-gate usba_update_hotplug_stats(hubd->h_dip, 45287c478bd9Sstevel@tonic-gate USBA_TOTAL_HOTPLUG_FAILURE|USBA_HOTPLUG_FAILURE); 45297c478bd9Sstevel@tonic-gate hubd->h_total_hotplug_failure++; 45307c478bd9Sstevel@tonic-gate 45317c478bd9Sstevel@tonic-gate /* 45327c478bd9Sstevel@tonic-gate * the port should be automagically 45337c478bd9Sstevel@tonic-gate * disabled but just in case, we do 45347c478bd9Sstevel@tonic-gate * it here 45357c478bd9Sstevel@tonic-gate */ 45367c478bd9Sstevel@tonic-gate (void) hubd_disable_port(hubd, port); 45377c478bd9Sstevel@tonic-gate 45387c478bd9Sstevel@tonic-gate /* ack all changes because we disabled this port */ 45397c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, 4540993e3fafSRobert Mustacchi port, &status, &change, NULL, HUBD_ACK_ALL_CHANGES); 45417c478bd9Sstevel@tonic-gate 45427c478bd9Sstevel@tonic-gate } 45437c478bd9Sstevel@tonic-gate 45447c478bd9Sstevel@tonic-gate return (USB_FAILURE); 45457c478bd9Sstevel@tonic-gate } 45467c478bd9Sstevel@tonic-gate 45477c478bd9Sstevel@tonic-gate 45487c478bd9Sstevel@tonic-gate /* 45497c478bd9Sstevel@tonic-gate * hubd_get_hub_status: 45507c478bd9Sstevel@tonic-gate */ 45517c478bd9Sstevel@tonic-gate static int 45527c478bd9Sstevel@tonic-gate hubd_get_hub_status(hubd_t *hubd) 45537c478bd9Sstevel@tonic-gate { 45547c478bd9Sstevel@tonic-gate int rval; 45557c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 45567c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 455735f36846Ssl uint16_t stword[2]; 45587c478bd9Sstevel@tonic-gate uint16_t status; 45597c478bd9Sstevel@tonic-gate uint16_t change; 45607c478bd9Sstevel@tonic-gate usb_cfg_descr_t cfg_descr; 45617c478bd9Sstevel@tonic-gate size_t cfg_length; 45627c478bd9Sstevel@tonic-gate uchar_t *usb_cfg; 45637c478bd9Sstevel@tonic-gate uint8_t MaxPower; 4564fef1e07eSsl usb_port_t port; 45657c478bd9Sstevel@tonic-gate 4566fef1e07eSsl USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 456735f36846Ssl "hubd_get_hub_status:"); 45687c478bd9Sstevel@tonic-gate 456935f36846Ssl ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 45707c478bd9Sstevel@tonic-gate 457135f36846Ssl if ((hubd_get_hub_status_words(hubd, stword)) != USB_SUCCESS) { 45727c478bd9Sstevel@tonic-gate 45737c478bd9Sstevel@tonic-gate return (USB_FAILURE); 45747c478bd9Sstevel@tonic-gate } 457535f36846Ssl status = stword[0]; 457635f36846Ssl change = stword[1]; 45777c478bd9Sstevel@tonic-gate 45787c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 45797c478bd9Sstevel@tonic-gate 45807c478bd9Sstevel@tonic-gate /* Obtain the raw configuration descriptor */ 45817c478bd9Sstevel@tonic-gate usb_cfg = usb_get_raw_cfg_data(hubd->h_dip, &cfg_length); 45827c478bd9Sstevel@tonic-gate 45837c478bd9Sstevel@tonic-gate /* get configuration descriptor */ 45847c478bd9Sstevel@tonic-gate rval = usb_parse_cfg_descr(usb_cfg, cfg_length, 4585c0f24e5bSlg &cfg_descr, USB_CFG_DESCR_SIZE); 45867c478bd9Sstevel@tonic-gate 45877c478bd9Sstevel@tonic-gate if (rval != USB_CFG_DESCR_SIZE) { 45887c478bd9Sstevel@tonic-gate 4589fef1e07eSsl USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 45907c478bd9Sstevel@tonic-gate "get hub configuration descriptor failed."); 45917c478bd9Sstevel@tonic-gate 45927c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 45937c478bd9Sstevel@tonic-gate 45947c478bd9Sstevel@tonic-gate return (USB_FAILURE); 45957c478bd9Sstevel@tonic-gate } else { 45967c478bd9Sstevel@tonic-gate MaxPower = cfg_descr.bMaxPower; 45977c478bd9Sstevel@tonic-gate } 45987c478bd9Sstevel@tonic-gate 45997c478bd9Sstevel@tonic-gate /* check if local power status changed. */ 46007c478bd9Sstevel@tonic-gate if (change & C_HUB_LOCAL_POWER_STATUS) { 46017c478bd9Sstevel@tonic-gate 46027c478bd9Sstevel@tonic-gate /* 46037c478bd9Sstevel@tonic-gate * local power has been lost, check the maximum 46047c478bd9Sstevel@tonic-gate * power consumption of current configuration. 46057c478bd9Sstevel@tonic-gate * see USB2.0 spec Table 11-12. 46067c478bd9Sstevel@tonic-gate */ 46077c478bd9Sstevel@tonic-gate if (status & HUB_LOCAL_POWER_STATUS) { 46087c478bd9Sstevel@tonic-gate 46097c478bd9Sstevel@tonic-gate if (MaxPower == 0) { 46107c478bd9Sstevel@tonic-gate 46117c478bd9Sstevel@tonic-gate /* 46127c478bd9Sstevel@tonic-gate * Self-powered only hub. Because it could 46137c478bd9Sstevel@tonic-gate * not draw any power from USB bus. 46147c478bd9Sstevel@tonic-gate * It can't work well on this condition. 46157c478bd9Sstevel@tonic-gate */ 4616fef1e07eSsl USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, 46177c478bd9Sstevel@tonic-gate hubd->h_log_handle, 46187c478bd9Sstevel@tonic-gate "local power has been lost, " 46197c478bd9Sstevel@tonic-gate "please disconnect hub"); 46207c478bd9Sstevel@tonic-gate } else { 46217c478bd9Sstevel@tonic-gate 46227c478bd9Sstevel@tonic-gate /* 46237c478bd9Sstevel@tonic-gate * Bus-powered only or self/bus-powered hub. 46247c478bd9Sstevel@tonic-gate */ 4625fef1e07eSsl USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, 46267c478bd9Sstevel@tonic-gate hubd->h_log_handle, 46277c478bd9Sstevel@tonic-gate "local power has been lost," 46287c478bd9Sstevel@tonic-gate "the hub could draw %d" 46297c478bd9Sstevel@tonic-gate " mA power from the USB bus.", 46307c478bd9Sstevel@tonic-gate 2*MaxPower); 46317c478bd9Sstevel@tonic-gate } 46327c478bd9Sstevel@tonic-gate 46337c478bd9Sstevel@tonic-gate } 46347c478bd9Sstevel@tonic-gate 4635fef1e07eSsl USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 46367c478bd9Sstevel@tonic-gate "clearing feature C_HUB_LOCAL_POWER "); 46377c478bd9Sstevel@tonic-gate 46387c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 46397c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 4640fef1e07eSsl HUB_HANDLE_HUB_FEATURE_TYPE, 46417c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 46427c478bd9Sstevel@tonic-gate CFS_C_HUB_LOCAL_POWER, 46437c478bd9Sstevel@tonic-gate 0, 46447c478bd9Sstevel@tonic-gate 0, 46457c478bd9Sstevel@tonic-gate NULL, 0, 46467c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 4647fef1e07eSsl USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 46487c478bd9Sstevel@tonic-gate hubd->h_log_handle, 46497c478bd9Sstevel@tonic-gate "clear feature C_HUB_LOCAL_POWER " 46507c478bd9Sstevel@tonic-gate "failed (%d 0x%x %d)", 46517c478bd9Sstevel@tonic-gate rval, completion_reason, cb_flags); 46527c478bd9Sstevel@tonic-gate } 46537c478bd9Sstevel@tonic-gate 46547c478bd9Sstevel@tonic-gate } 46557c478bd9Sstevel@tonic-gate 46567c478bd9Sstevel@tonic-gate if (change & C_HUB_OVER_CURRENT) { 46577c478bd9Sstevel@tonic-gate 46587c478bd9Sstevel@tonic-gate if (status & HUB_OVER_CURRENT) { 4659fef1e07eSsl 4660fef1e07eSsl if (usba_is_root_hub(hubd->h_dip)) { 4661fef1e07eSsl /* 4662fef1e07eSsl * The root hub should be automatically 4663fef1e07eSsl * recovered when over-current condition is 4664fef1e07eSsl * cleared. But there might be exception and 4665fef1e07eSsl * need user interaction to recover. 4666fef1e07eSsl */ 4667fef1e07eSsl USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, 4668fef1e07eSsl hubd->h_log_handle, 4669fef1e07eSsl "Root hub over current condition, " 4670fef1e07eSsl "please check your system to clear the " 4671fef1e07eSsl "condition as soon as possible. And you " 4672fef1e07eSsl "may need to reboot the system to bring " 4673fef1e07eSsl "the root hub back to work if it cannot " 4674fef1e07eSsl "recover automatically"); 4675fef1e07eSsl } else { 4676fef1e07eSsl /* 4677fef1e07eSsl * The driver would try to recover port power 4678fef1e07eSsl * on over current condition. When the recovery 4679fef1e07eSsl * fails, the user may still need to offline 4680fef1e07eSsl * this hub in order to recover. 4681fef1e07eSsl * The port power is automatically disabled, 4682fef1e07eSsl * so we won't see disconnects. 4683fef1e07eSsl */ 4684fef1e07eSsl USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, 4685fef1e07eSsl hubd->h_log_handle, 4686fef1e07eSsl "Hub global over current condition, " 4687fef1e07eSsl "please disconnect the devices connected " 4688fef1e07eSsl "to the hub to clear the condition. And " 4689fef1e07eSsl "you may need to re-connect the hub if " 4690fef1e07eSsl "the ports do not work"); 4691fef1e07eSsl } 46927c478bd9Sstevel@tonic-gate } 46937c478bd9Sstevel@tonic-gate 4694fef1e07eSsl USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 46957c478bd9Sstevel@tonic-gate "clearing feature C_HUB_OVER_CURRENT"); 46967c478bd9Sstevel@tonic-gate 46977c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 46987c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 4699fef1e07eSsl HUB_HANDLE_HUB_FEATURE_TYPE, 47007c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 47017c478bd9Sstevel@tonic-gate CFS_C_HUB_OVER_CURRENT, 47027c478bd9Sstevel@tonic-gate 0, 47037c478bd9Sstevel@tonic-gate 0, 47047c478bd9Sstevel@tonic-gate NULL, 0, 47057c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 4706fef1e07eSsl USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 47077c478bd9Sstevel@tonic-gate hubd->h_log_handle, 47087c478bd9Sstevel@tonic-gate "clear feature C_HUB_OVER_CURRENT " 47097c478bd9Sstevel@tonic-gate "failed (%d 0x%x %d)", 47107c478bd9Sstevel@tonic-gate rval, completion_reason, cb_flags); 47117c478bd9Sstevel@tonic-gate } 4712fef1e07eSsl 4713fef1e07eSsl /* 4714fef1e07eSsl * Try to recover all port power if they are turned off. 4715fef1e07eSsl * Don't do this for root hub, but rely on the root hub 4716fef1e07eSsl * to recover itself. 4717fef1e07eSsl */ 4718fef1e07eSsl if (!usba_is_root_hub(hubd->h_dip)) { 4719fef1e07eSsl 4720fef1e07eSsl mutex_enter(HUBD_MUTEX(hubd)); 4721fef1e07eSsl 4722fef1e07eSsl /* 4723fef1e07eSsl * Only check the power status of the 1st port 4724fef1e07eSsl * since all port power status should be the same. 4725fef1e07eSsl */ 4726fef1e07eSsl (void) hubd_determine_port_status(hubd, 1, &status, 4727993e3fafSRobert Mustacchi &change, NULL, 0); 4728fef1e07eSsl 4729fef1e07eSsl if (status & PORT_STATUS_PPS) { 4730fef1e07eSsl return (USB_SUCCESS); 4731fef1e07eSsl } 4732fef1e07eSsl 4733993e3fafSRobert Mustacchi for (port = 1; port <= hubd->h_nports; port++) { 4734fef1e07eSsl (void) hubd_enable_port_power(hubd, port); 4735fef1e07eSsl } 4736fef1e07eSsl 4737fef1e07eSsl mutex_exit(HUBD_MUTEX(hubd)); 4738fef1e07eSsl 4739fef1e07eSsl /* 4740fef1e07eSsl * Delay some time to avoid over-current event 4741fef1e07eSsl * to happen too frequently in some cases 4742fef1e07eSsl */ 4743fef1e07eSsl delay(drv_usectohz(500000)); 4744fef1e07eSsl } 47457c478bd9Sstevel@tonic-gate } 47467c478bd9Sstevel@tonic-gate 47477c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 47487c478bd9Sstevel@tonic-gate 47497c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 47507c478bd9Sstevel@tonic-gate } 47517c478bd9Sstevel@tonic-gate 4752993e3fafSRobert Mustacchi /* 4753993e3fafSRobert Mustacchi * Convert a series of USB status requests from USB 2 and USB 3 into a single 4754993e3fafSRobert Mustacchi * uniform type. We separate out the speed into its own value from both USB 2 4755993e3fafSRobert Mustacchi * and USB 3 and from there we transform the status to look like a USB 2 one. 4756993e3fafSRobert Mustacchi */ 4757993e3fafSRobert Mustacchi static void 4758993e3fafSRobert Mustacchi hubd_status_uniform(hubd_t *hubd, usb_port_t port, uint16_t *status, 4759993e3fafSRobert Mustacchi usb_port_status_t *speed) 4760993e3fafSRobert Mustacchi { 4761993e3fafSRobert Mustacchi uint16_t os = *status; 4762993e3fafSRobert Mustacchi 4763993e3fafSRobert Mustacchi hubd->h_port_raw[port] = os; 4764993e3fafSRobert Mustacchi 4765993e3fafSRobert Mustacchi if (hubd->h_usba_device->usb_port_status >= USBA_SUPER_SPEED_DEV) { 4766993e3fafSRobert Mustacchi /* 4767993e3fafSRobert Mustacchi * USB 3 devices are always at super speed when plugged into a 4768993e3fafSRobert Mustacchi * super speed hub. However, this is only true if we're talking 4769993e3fafSRobert Mustacchi * about actual hubs. This doesn't hold for the root hub, which 4770993e3fafSRobert Mustacchi * can support USB 3.x, USB 2.x, and USB 1.x devices operating 4771993e3fafSRobert Mustacchi * at different speeds. To handle this, the USB 3 HCD driver 4772993e3fafSRobert Mustacchi * (xhci) uses some of the extra status bits to stash the 4773993e3fafSRobert Mustacchi * current device's detected speed. 4774993e3fafSRobert Mustacchi */ 4775993e3fafSRobert Mustacchi if (usba_is_root_hub(hubd->h_dip)) { 4776993e3fafSRobert Mustacchi if (speed != NULL) { 4777993e3fafSRobert Mustacchi *speed = (os & PORT_STATUS_SPMASK_SS) >> 4778993e3fafSRobert Mustacchi PORT_STATUS_SPSHIFT_SS; 4779993e3fafSRobert Mustacchi } 4780993e3fafSRobert Mustacchi } else { 4781993e3fafSRobert Mustacchi if (speed != NULL) 4782993e3fafSRobert Mustacchi *speed = USBA_SUPER_SPEED_DEV; 4783993e3fafSRobert Mustacchi } 4784993e3fafSRobert Mustacchi 4785993e3fafSRobert Mustacchi if (os & PORT_STATUS_PPS_SS) { 4786993e3fafSRobert Mustacchi os &= ~PORT_STATUS_PPS_SS; 4787993e3fafSRobert Mustacchi os |= PORT_STATUS_PPS; 4788993e3fafSRobert Mustacchi *status = os; 4789993e3fafSRobert Mustacchi } 4790993e3fafSRobert Mustacchi } else { 4791993e3fafSRobert Mustacchi /* 4792993e3fafSRobert Mustacchi * For USB 2, the only thing we need to do is transform the 4793993e3fafSRobert Mustacchi * speed. 4794993e3fafSRobert Mustacchi */ 4795993e3fafSRobert Mustacchi if (speed == NULL) 4796993e3fafSRobert Mustacchi return; 4797993e3fafSRobert Mustacchi 4798993e3fafSRobert Mustacchi if (os & PORT_STATUS_HSDA) 4799993e3fafSRobert Mustacchi *speed = USBA_HIGH_SPEED_DEV; 4800993e3fafSRobert Mustacchi else if (os & PORT_STATUS_LSDA) 4801993e3fafSRobert Mustacchi *speed = USBA_LOW_SPEED_DEV; 4802993e3fafSRobert Mustacchi else 4803993e3fafSRobert Mustacchi *speed = USBA_FULL_SPEED_DEV; 4804993e3fafSRobert Mustacchi } 4805993e3fafSRobert Mustacchi } 4806993e3fafSRobert Mustacchi 48077c478bd9Sstevel@tonic-gate 48087c478bd9Sstevel@tonic-gate /* 4809993e3fafSRobert Mustacchi * Attempt to reset a port. This feels a bit more complicated than it should be 4810993e3fafSRobert Mustacchi * in part due to how HCD, change status notifications, and the hotplug thread 4811993e3fafSRobert Mustacchi * might interact. Basically we try to block port changes by using the 4812993e3fafSRobert Mustacchi * h_port_reset_wait which says we should get signalled rather than kicking off 4813993e3fafSRobert Mustacchi * the hotplug thread. We'll give this a shot for about 100ms at best. 48147c478bd9Sstevel@tonic-gate */ 48157c478bd9Sstevel@tonic-gate static int 48167c478bd9Sstevel@tonic-gate hubd_reset_port(hubd_t *hubd, usb_port_t port) 48177c478bd9Sstevel@tonic-gate { 48187c478bd9Sstevel@tonic-gate int rval; 48197c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 48207c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 48217c478bd9Sstevel@tonic-gate usb_port_mask_t port_mask = 1 << port; 48227c478bd9Sstevel@tonic-gate mblk_t *data; 48237c478bd9Sstevel@tonic-gate uint16_t status; 48247c478bd9Sstevel@tonic-gate uint16_t change; 4825d3d50737SRafael Vanoni clock_t delta; 4826993e3fafSRobert Mustacchi boolean_t first; 48277c478bd9Sstevel@tonic-gate 48287c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 48297c478bd9Sstevel@tonic-gate "hubd_reset_port: port=%d", port); 48307c478bd9Sstevel@tonic-gate 48317c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 48327c478bd9Sstevel@tonic-gate 48337c478bd9Sstevel@tonic-gate hubd->h_port_reset_wait |= port_mask; 48347c478bd9Sstevel@tonic-gate 48357c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 48367c478bd9Sstevel@tonic-gate 48377c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 48387c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 483935f36846Ssl HUB_HANDLE_PORT_FEATURE_TYPE, 48407c478bd9Sstevel@tonic-gate USB_REQ_SET_FEATURE, 48417c478bd9Sstevel@tonic-gate CFS_PORT_RESET, 48427c478bd9Sstevel@tonic-gate port, 48437c478bd9Sstevel@tonic-gate 0, 48447c478bd9Sstevel@tonic-gate NULL, 0, 48457c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 4846d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 48477c478bd9Sstevel@tonic-gate "reset port%d failed (%d 0x%x %d)", 48487c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 48497c478bd9Sstevel@tonic-gate 48507c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 48517c478bd9Sstevel@tonic-gate 48527c478bd9Sstevel@tonic-gate return (USB_FAILURE); 48537c478bd9Sstevel@tonic-gate } 48547c478bd9Sstevel@tonic-gate 48557c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 48567c478bd9Sstevel@tonic-gate 48577c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 48587c478bd9Sstevel@tonic-gate "waiting on cv for reset completion"); 48597c478bd9Sstevel@tonic-gate 48607c478bd9Sstevel@tonic-gate /* 48617c478bd9Sstevel@tonic-gate * wait for port status change event 48627c478bd9Sstevel@tonic-gate */ 4863d3d50737SRafael Vanoni delta = drv_usectohz(hubd_device_delay / 10); 48647c478bd9Sstevel@tonic-gate 4865993e3fafSRobert Mustacchi first = B_TRUE; 4866993e3fafSRobert Mustacchi for (;;) { 4867993e3fafSRobert Mustacchi if (delta < 0) { 4868993e3fafSRobert Mustacchi rval = USB_FAILURE; 4869993e3fafSRobert Mustacchi break; 4870993e3fafSRobert Mustacchi } 48717c478bd9Sstevel@tonic-gate 4872993e3fafSRobert Mustacchi if (first == B_FALSE) 4873993e3fafSRobert Mustacchi hubd->h_port_reset_wait |= port_mask; 4874993e3fafSRobert Mustacchi else 4875993e3fafSRobert Mustacchi first = B_FALSE; 48767c478bd9Sstevel@tonic-gate 4877993e3fafSRobert Mustacchi hubd_start_polling(hubd, HUBD_ALWAYS_START_POLLING); 48787c478bd9Sstevel@tonic-gate 4879993e3fafSRobert Mustacchi /* 4880993e3fafSRobert Mustacchi * Regardless of the status, we always check to see if the port 4881993e3fafSRobert Mustacchi * has been reset. 4882993e3fafSRobert Mustacchi */ 4883993e3fafSRobert Mustacchi delta = cv_reltimedwait(&hubd->h_cv_reset_port, 4884993e3fafSRobert Mustacchi &hubd->h_mutex, delta, TR_CLOCK_TICK); 4885993e3fafSRobert Mustacchi if (delta < 0) 4886993e3fafSRobert Mustacchi hubd->h_port_reset_wait &= ~port_mask; 48877c478bd9Sstevel@tonic-gate 48887c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd); 48897c478bd9Sstevel@tonic-gate 48907c478bd9Sstevel@tonic-gate data = NULL; 48917c478bd9Sstevel@tonic-gate 48927c478bd9Sstevel@tonic-gate /* check status to determine whether reset completed */ 48937c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 48947c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 48957c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 489635f36846Ssl HUB_GET_PORT_STATUS_TYPE, 48977c478bd9Sstevel@tonic-gate USB_REQ_GET_STATUS, 48987c478bd9Sstevel@tonic-gate 0, 48997c478bd9Sstevel@tonic-gate port, 49007c478bd9Sstevel@tonic-gate GET_STATUS_LENGTH, 49017c478bd9Sstevel@tonic-gate &data, 0, 49027c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 4903d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_PORT, 49047c478bd9Sstevel@tonic-gate hubd->h_log_handle, 49057c478bd9Sstevel@tonic-gate "get status port%d failed (%d 0x%x %d)", 49067c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 49077c478bd9Sstevel@tonic-gate 49087c478bd9Sstevel@tonic-gate if (data) { 49097c478bd9Sstevel@tonic-gate freemsg(data); 49107c478bd9Sstevel@tonic-gate data = NULL; 49117c478bd9Sstevel@tonic-gate } 49127c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 49137c478bd9Sstevel@tonic-gate 49147c478bd9Sstevel@tonic-gate continue; 49157c478bd9Sstevel@tonic-gate } 49167c478bd9Sstevel@tonic-gate 49177c478bd9Sstevel@tonic-gate status = (*(data->b_rptr + 1) << 8) | *(data->b_rptr); 49187c478bd9Sstevel@tonic-gate change = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2); 49197c478bd9Sstevel@tonic-gate 49207c478bd9Sstevel@tonic-gate freemsg(data); 49217c478bd9Sstevel@tonic-gate 4922993e3fafSRobert Mustacchi hubd_status_uniform(hubd, port, &status, NULL); 4923993e3fafSRobert Mustacchi 49247c478bd9Sstevel@tonic-gate /* continue only if port is still connected */ 49257c478bd9Sstevel@tonic-gate if (!(status & PORT_STATUS_CCS)) { 49267c478bd9Sstevel@tonic-gate 49277c478bd9Sstevel@tonic-gate /* lost connection, set exit condition */ 4928993e3fafSRobert Mustacchi delta = -1; 49297c478bd9Sstevel@tonic-gate 49307c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 49317c478bd9Sstevel@tonic-gate 49327c478bd9Sstevel@tonic-gate break; 49337c478bd9Sstevel@tonic-gate } 49347c478bd9Sstevel@tonic-gate 49357c478bd9Sstevel@tonic-gate if (status & PORT_STATUS_PRS) { 49367c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 49377c478bd9Sstevel@tonic-gate "port%d reset active", port); 49387c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 49397c478bd9Sstevel@tonic-gate 49407c478bd9Sstevel@tonic-gate continue; 49417c478bd9Sstevel@tonic-gate } else { 49427c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 49437c478bd9Sstevel@tonic-gate "port%d reset inactive", port); 49447c478bd9Sstevel@tonic-gate } 49457c478bd9Sstevel@tonic-gate 49467c478bd9Sstevel@tonic-gate if (change & PORT_CHANGE_PRSC) { 49477c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 49487c478bd9Sstevel@tonic-gate "clearing feature CFS_C_PORT_RESET"); 49497c478bd9Sstevel@tonic-gate 49507c478bd9Sstevel@tonic-gate if (usb_pipe_sync_ctrl_xfer(hubd->h_dip, 49517c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 495235f36846Ssl HUB_HANDLE_PORT_FEATURE_TYPE, 49537c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 49547c478bd9Sstevel@tonic-gate CFS_C_PORT_RESET, 49557c478bd9Sstevel@tonic-gate port, 49567c478bd9Sstevel@tonic-gate 0, 49577c478bd9Sstevel@tonic-gate NULL, 0, 49587c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0) != USB_SUCCESS) { 49597c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 49607c478bd9Sstevel@tonic-gate hubd->h_log_handle, 49617c478bd9Sstevel@tonic-gate "clear feature CFS_C_PORT_RESET" 49627c478bd9Sstevel@tonic-gate " port%d failed (%d 0x%x %d)", 49637c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 49647c478bd9Sstevel@tonic-gate } 49657c478bd9Sstevel@tonic-gate } 4966993e3fafSRobert Mustacchi 4967993e3fafSRobert Mustacchi /* 4968993e3fafSRobert Mustacchi * In addition to a normal reset, a warm reset may have 4969993e3fafSRobert Mustacchi * happened. Acknowledge that as well. 4970993e3fafSRobert Mustacchi */ 4971993e3fafSRobert Mustacchi if (change & PORT_CHANGE_BHPR) { 4972993e3fafSRobert Mustacchi USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 4973993e3fafSRobert Mustacchi "clearing feature CFS_C_BH_PORT_RESET"); 4974993e3fafSRobert Mustacchi 4975993e3fafSRobert Mustacchi if (usb_pipe_sync_ctrl_xfer(hubd->h_dip, 4976993e3fafSRobert Mustacchi hubd->h_default_pipe, 4977993e3fafSRobert Mustacchi HUB_HANDLE_PORT_FEATURE_TYPE, 4978993e3fafSRobert Mustacchi USB_REQ_CLEAR_FEATURE, 4979993e3fafSRobert Mustacchi CFS_C_BH_PORT_RESET, 4980993e3fafSRobert Mustacchi port, 4981993e3fafSRobert Mustacchi 0, 4982993e3fafSRobert Mustacchi NULL, 0, 4983993e3fafSRobert Mustacchi &completion_reason, &cb_flags, 0) != USB_SUCCESS) { 4984993e3fafSRobert Mustacchi USB_DPRINTF_L2(DPRINT_MASK_PORT, 4985993e3fafSRobert Mustacchi hubd->h_log_handle, 4986993e3fafSRobert Mustacchi "clear feature CFS_C_BH_PORT_RESET" 4987993e3fafSRobert Mustacchi " port%d failed (%d 0x%x %d)", 4988993e3fafSRobert Mustacchi port, completion_reason, cb_flags, rval); 4989993e3fafSRobert Mustacchi } 4990993e3fafSRobert Mustacchi } 4991993e3fafSRobert Mustacchi 4992993e3fafSRobert Mustacchi rval = USB_SUCCESS; 49937c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 49947c478bd9Sstevel@tonic-gate 49957c478bd9Sstevel@tonic-gate break; 49967c478bd9Sstevel@tonic-gate } 49977c478bd9Sstevel@tonic-gate 49987c478bd9Sstevel@tonic-gate return (rval); 49997c478bd9Sstevel@tonic-gate } 50007c478bd9Sstevel@tonic-gate 50017c478bd9Sstevel@tonic-gate 50027c478bd9Sstevel@tonic-gate /* 50037c478bd9Sstevel@tonic-gate * hubd_enable_port: 50047c478bd9Sstevel@tonic-gate * this may fail if the hub as been disconnected 50057c478bd9Sstevel@tonic-gate */ 50067c478bd9Sstevel@tonic-gate static int 50077c478bd9Sstevel@tonic-gate hubd_enable_port(hubd_t *hubd, usb_port_t port) 50087c478bd9Sstevel@tonic-gate { 50097c478bd9Sstevel@tonic-gate int rval; 50107c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 50117c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 50127c478bd9Sstevel@tonic-gate 50137c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 50147c478bd9Sstevel@tonic-gate "hubd_enable_port: port=%d", port); 50157c478bd9Sstevel@tonic-gate 50167c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 50177c478bd9Sstevel@tonic-gate 50187c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 50197c478bd9Sstevel@tonic-gate 50207c478bd9Sstevel@tonic-gate /* Do not issue a SetFeature(PORT_ENABLE) on external hubs */ 50217c478bd9Sstevel@tonic-gate if (!usba_is_root_hub(hubd->h_dip)) { 50227c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 50237c478bd9Sstevel@tonic-gate 50247c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 50257c478bd9Sstevel@tonic-gate } 50267c478bd9Sstevel@tonic-gate 50277c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 50287c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 502935f36846Ssl HUB_HANDLE_PORT_FEATURE_TYPE, 50307c478bd9Sstevel@tonic-gate USB_REQ_SET_FEATURE, 50317c478bd9Sstevel@tonic-gate CFS_PORT_ENABLE, 50327c478bd9Sstevel@tonic-gate port, 50337c478bd9Sstevel@tonic-gate 0, 50347c478bd9Sstevel@tonic-gate NULL, 0, 50357c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 50367c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 50377c478bd9Sstevel@tonic-gate "enable port%d failed (%d 0x%x %d)", 50387c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 50397c478bd9Sstevel@tonic-gate } 50407c478bd9Sstevel@tonic-gate 50417c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 50427c478bd9Sstevel@tonic-gate 50437c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 50447c478bd9Sstevel@tonic-gate "enabling port done"); 50457c478bd9Sstevel@tonic-gate 50467c478bd9Sstevel@tonic-gate return (rval); 50477c478bd9Sstevel@tonic-gate } 50487c478bd9Sstevel@tonic-gate 50497c478bd9Sstevel@tonic-gate 50507c478bd9Sstevel@tonic-gate /* 50517c478bd9Sstevel@tonic-gate * hubd_disable_port 50527c478bd9Sstevel@tonic-gate */ 50537c478bd9Sstevel@tonic-gate static int 50547c478bd9Sstevel@tonic-gate hubd_disable_port(hubd_t *hubd, usb_port_t port) 50557c478bd9Sstevel@tonic-gate { 50567c478bd9Sstevel@tonic-gate int rval; 50577c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 50587c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 50597c478bd9Sstevel@tonic-gate 50607c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 50617c478bd9Sstevel@tonic-gate "hubd_disable_port: port=%d", port); 50627c478bd9Sstevel@tonic-gate 50637c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 50647c478bd9Sstevel@tonic-gate 50657c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 50667c478bd9Sstevel@tonic-gate 50677c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 50687c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 506935f36846Ssl HUB_HANDLE_PORT_FEATURE_TYPE, 50707c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 50717c478bd9Sstevel@tonic-gate CFS_PORT_ENABLE, 50727c478bd9Sstevel@tonic-gate port, 50737c478bd9Sstevel@tonic-gate 0, 50747c478bd9Sstevel@tonic-gate NULL, 0, 50757c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 50767c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 50777c478bd9Sstevel@tonic-gate "disable port%d failed (%d 0x%x %d)", port, 50787c478bd9Sstevel@tonic-gate completion_reason, cb_flags, rval); 50797c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 50807c478bd9Sstevel@tonic-gate 50817c478bd9Sstevel@tonic-gate return (USB_FAILURE); 50827c478bd9Sstevel@tonic-gate } 50837c478bd9Sstevel@tonic-gate 50847c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 50857c478bd9Sstevel@tonic-gate "clearing feature CFS_C_PORT_ENABLE"); 50867c478bd9Sstevel@tonic-gate 50877c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 50887c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 508935f36846Ssl HUB_HANDLE_PORT_FEATURE_TYPE, 50907c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 50917c478bd9Sstevel@tonic-gate CFS_C_PORT_ENABLE, 50927c478bd9Sstevel@tonic-gate port, 50937c478bd9Sstevel@tonic-gate 0, 50947c478bd9Sstevel@tonic-gate NULL, 0, 50957c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 50967c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 50977c478bd9Sstevel@tonic-gate hubd->h_log_handle, 50987c478bd9Sstevel@tonic-gate "clear feature CFS_C_PORT_ENABLE port%d failed " 50997c478bd9Sstevel@tonic-gate "(%d 0x%x %d)", 51007c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 51017c478bd9Sstevel@tonic-gate 51027c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 51037c478bd9Sstevel@tonic-gate 51047c478bd9Sstevel@tonic-gate return (USB_FAILURE); 51057c478bd9Sstevel@tonic-gate } 51067c478bd9Sstevel@tonic-gate 51077c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 51087c478bd9Sstevel@tonic-gate 51097c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 51107c478bd9Sstevel@tonic-gate } 51117c478bd9Sstevel@tonic-gate 51127c478bd9Sstevel@tonic-gate 51137c478bd9Sstevel@tonic-gate /* 51147c478bd9Sstevel@tonic-gate * hubd_determine_port_status: 51157c478bd9Sstevel@tonic-gate */ 51167c478bd9Sstevel@tonic-gate static int 5117993e3fafSRobert Mustacchi hubd_determine_port_status(hubd_t *hubd, usb_port_t port, uint16_t *status, 5118993e3fafSRobert Mustacchi uint16_t *change, usb_port_status_t *speed, uint_t ack_flag) 51197c478bd9Sstevel@tonic-gate { 51207c478bd9Sstevel@tonic-gate int rval; 51217c478bd9Sstevel@tonic-gate mblk_t *data = NULL; 51227c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 51237c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 5124993e3fafSRobert Mustacchi uint16_t st, ch; 5125993e3fafSRobert Mustacchi usb_port_status_t sp; 5126993e3fafSRobert Mustacchi 5127993e3fafSRobert Mustacchi if (status == NULL) 5128993e3fafSRobert Mustacchi status = &st; 5129993e3fafSRobert Mustacchi if (change == NULL) 5130993e3fafSRobert Mustacchi change = &ch; 5131993e3fafSRobert Mustacchi if (speed == NULL) 5132993e3fafSRobert Mustacchi speed = &sp; 5133993e3fafSRobert Mustacchi 5134993e3fafSRobert Mustacchi *status = *change = 0; 5135993e3fafSRobert Mustacchi *speed = 0; 51367c478bd9Sstevel@tonic-gate 51377c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 51387c478bd9Sstevel@tonic-gate "hubd_determine_port_status: port=%d, state=0x%x ack=0x%x", port, 51397c478bd9Sstevel@tonic-gate hubd->h_port_state[port], ack_flag); 51407c478bd9Sstevel@tonic-gate 51417c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 51427c478bd9Sstevel@tonic-gate 51437c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 51447c478bd9Sstevel@tonic-gate 51457c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 51467c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 514735f36846Ssl HUB_GET_PORT_STATUS_TYPE, 51487c478bd9Sstevel@tonic-gate USB_REQ_GET_STATUS, 51497c478bd9Sstevel@tonic-gate 0, 51507c478bd9Sstevel@tonic-gate port, 51517c478bd9Sstevel@tonic-gate GET_STATUS_LENGTH, 51527c478bd9Sstevel@tonic-gate &data, 0, 51537c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 51547c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 51557c478bd9Sstevel@tonic-gate "port=%d get status failed (%d 0x%x %d)", 51567c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 51577c478bd9Sstevel@tonic-gate 51587c478bd9Sstevel@tonic-gate if (data) { 51597c478bd9Sstevel@tonic-gate freemsg(data); 51607c478bd9Sstevel@tonic-gate } 51617c478bd9Sstevel@tonic-gate 51627c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 51637c478bd9Sstevel@tonic-gate 51647c478bd9Sstevel@tonic-gate return (rval); 51657c478bd9Sstevel@tonic-gate } 51667c478bd9Sstevel@tonic-gate 51677c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 5168d29f5a71Szhigang lu - Sun Microsystems - Beijing China if (MBLKL(data) != GET_STATUS_LENGTH) { 51697c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 5170112116d8Sfb "port %d: length incorrect %ld", 5171d29f5a71Szhigang lu - Sun Microsystems - Beijing China port, MBLKL(data)); 51727c478bd9Sstevel@tonic-gate freemsg(data); 51737c478bd9Sstevel@tonic-gate 51747c478bd9Sstevel@tonic-gate return (rval); 51757c478bd9Sstevel@tonic-gate } 51767c478bd9Sstevel@tonic-gate 51777c478bd9Sstevel@tonic-gate 51787c478bd9Sstevel@tonic-gate *status = (*(data->b_rptr + 1) << 8) | *(data->b_rptr); 51797c478bd9Sstevel@tonic-gate *change = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2); 5180993e3fafSRobert Mustacchi hubd_status_uniform(hubd, port, status, speed); 51817c478bd9Sstevel@tonic-gate 51827c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 51837c478bd9Sstevel@tonic-gate "port%d status=0x%x, change=0x%x", port, *status, *change); 51847c478bd9Sstevel@tonic-gate 51857c478bd9Sstevel@tonic-gate freemsg(data); 51867c478bd9Sstevel@tonic-gate 51877c478bd9Sstevel@tonic-gate if (*status & PORT_STATUS_CCS) { 51887c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 51897c478bd9Sstevel@tonic-gate "port%d connected", port); 51907c478bd9Sstevel@tonic-gate 51917c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_CCS & ack_flag); 51927c478bd9Sstevel@tonic-gate } else { 51937c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 51947c478bd9Sstevel@tonic-gate "port%d disconnected", port); 51957c478bd9Sstevel@tonic-gate 51967c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_CCS & ack_flag); 51977c478bd9Sstevel@tonic-gate } 51987c478bd9Sstevel@tonic-gate 51997c478bd9Sstevel@tonic-gate if (*status & PORT_STATUS_PES) { 52007c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 52017c478bd9Sstevel@tonic-gate "port%d enabled", port); 52027c478bd9Sstevel@tonic-gate 52037c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_PES & ack_flag); 52047c478bd9Sstevel@tonic-gate } else { 52057c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 52067c478bd9Sstevel@tonic-gate "port%d disabled", port); 52077c478bd9Sstevel@tonic-gate 52087c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_PES & ack_flag); 52097c478bd9Sstevel@tonic-gate } 52107c478bd9Sstevel@tonic-gate 52117c478bd9Sstevel@tonic-gate if (*status & PORT_STATUS_PSS) { 52127c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 52137c478bd9Sstevel@tonic-gate "port%d suspended", port); 52147c478bd9Sstevel@tonic-gate 52157c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_PSS & ack_flag); 52167c478bd9Sstevel@tonic-gate } else { 52177c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 52187c478bd9Sstevel@tonic-gate "port%d not suspended", port); 52197c478bd9Sstevel@tonic-gate 52207c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_PSS & ack_flag); 52217c478bd9Sstevel@tonic-gate } 52227c478bd9Sstevel@tonic-gate 52237c478bd9Sstevel@tonic-gate if (*change & PORT_CHANGE_PRSC) { 52247c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 52257c478bd9Sstevel@tonic-gate "port%d reset completed", port); 52267c478bd9Sstevel@tonic-gate 52277c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_CHANGE_PRSC & ack_flag); 52287c478bd9Sstevel@tonic-gate } else { 52297c478bd9Sstevel@tonic-gate 52307c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_CHANGE_PRSC & ack_flag); 52317c478bd9Sstevel@tonic-gate } 52327c478bd9Sstevel@tonic-gate 52337c478bd9Sstevel@tonic-gate if (*status & PORT_STATUS_POCI) { 5234d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 5235d291d9f2Sfrits "port%d overcurrent!", port); 52367c478bd9Sstevel@tonic-gate 52377c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_POCI & ack_flag); 52387c478bd9Sstevel@tonic-gate } else { 52397c478bd9Sstevel@tonic-gate 52407c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_POCI & ack_flag); 52417c478bd9Sstevel@tonic-gate } 52427c478bd9Sstevel@tonic-gate 52437c478bd9Sstevel@tonic-gate if (*status & PORT_STATUS_PRS) { 52447c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 52457c478bd9Sstevel@tonic-gate "port%d reset active", port); 52467c478bd9Sstevel@tonic-gate 52477c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_PRS & ack_flag); 52487c478bd9Sstevel@tonic-gate } else { 52497c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 52507c478bd9Sstevel@tonic-gate "port%d reset inactive", port); 52517c478bd9Sstevel@tonic-gate 52527c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_PRS & ack_flag); 52537c478bd9Sstevel@tonic-gate } 52547c478bd9Sstevel@tonic-gate if (*status & PORT_STATUS_PPS) { 52557c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 52567c478bd9Sstevel@tonic-gate "port%d power on", port); 52577c478bd9Sstevel@tonic-gate 52587c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_PPS & ack_flag); 52597c478bd9Sstevel@tonic-gate } else { 52607c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 52617c478bd9Sstevel@tonic-gate "port%d power off", port); 52627c478bd9Sstevel@tonic-gate 52637c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_PPS & ack_flag); 52647c478bd9Sstevel@tonic-gate } 52657c478bd9Sstevel@tonic-gate 52667c478bd9Sstevel@tonic-gate /* 52677c478bd9Sstevel@tonic-gate * Acknowledge connection, enable, reset status 52687c478bd9Sstevel@tonic-gate */ 52697c478bd9Sstevel@tonic-gate if (ack_flag) { 52707c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 52717c478bd9Sstevel@tonic-gate if (*change & PORT_CHANGE_CSC & ack_flag) { 52727c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 52737c478bd9Sstevel@tonic-gate "clearing feature CFS_C_PORT_CONNECTION"); 52747c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 52757c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 527635f36846Ssl HUB_HANDLE_PORT_FEATURE_TYPE, 52777c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 52787c478bd9Sstevel@tonic-gate CFS_C_PORT_CONNECTION, 52797c478bd9Sstevel@tonic-gate port, 52807c478bd9Sstevel@tonic-gate 0, NULL, 0, 52817c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != 52827c478bd9Sstevel@tonic-gate USB_SUCCESS) { 52837c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 52847c478bd9Sstevel@tonic-gate hubd->h_log_handle, 52857c478bd9Sstevel@tonic-gate "clear feature CFS_C_PORT_CONNECTION" 52867c478bd9Sstevel@tonic-gate " port%d failed (%d 0x%x %d)", 52877c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 52887c478bd9Sstevel@tonic-gate } 52897c478bd9Sstevel@tonic-gate } 52907c478bd9Sstevel@tonic-gate if (*change & PORT_CHANGE_PESC & ack_flag) { 52917c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 52927c478bd9Sstevel@tonic-gate "clearing feature CFS_C_PORT_ENABLE"); 52937c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 52947c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 529535f36846Ssl HUB_HANDLE_PORT_FEATURE_TYPE, 52967c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 52977c478bd9Sstevel@tonic-gate CFS_C_PORT_ENABLE, 52987c478bd9Sstevel@tonic-gate port, 52997c478bd9Sstevel@tonic-gate 0, NULL, 0, 53007c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != 53017c478bd9Sstevel@tonic-gate USB_SUCCESS) { 53027c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 53037c478bd9Sstevel@tonic-gate hubd->h_log_handle, 53047c478bd9Sstevel@tonic-gate "clear feature CFS_C_PORT_ENABLE" 53057c478bd9Sstevel@tonic-gate " port%d failed (%d 0x%x %d)", 53067c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 53077c478bd9Sstevel@tonic-gate } 53087c478bd9Sstevel@tonic-gate } 53097c478bd9Sstevel@tonic-gate if (*change & PORT_CHANGE_PSSC & ack_flag) { 53107c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 53117c478bd9Sstevel@tonic-gate "clearing feature CFS_C_PORT_SUSPEND"); 53127c478bd9Sstevel@tonic-gate 53137c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 53147c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 531535f36846Ssl HUB_HANDLE_PORT_FEATURE_TYPE, 53167c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 53177c478bd9Sstevel@tonic-gate CFS_C_PORT_SUSPEND, 53187c478bd9Sstevel@tonic-gate port, 53197c478bd9Sstevel@tonic-gate 0, NULL, 0, 53207c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != 53217c478bd9Sstevel@tonic-gate USB_SUCCESS) { 53227c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 53237c478bd9Sstevel@tonic-gate hubd->h_log_handle, 53247c478bd9Sstevel@tonic-gate "clear feature CFS_C_PORT_SUSPEND" 53257c478bd9Sstevel@tonic-gate " port%d failed (%d 0x%x %d)", 53267c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 53277c478bd9Sstevel@tonic-gate } 53287c478bd9Sstevel@tonic-gate } 53297c478bd9Sstevel@tonic-gate if (*change & PORT_CHANGE_OCIC & ack_flag) { 53307c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 53317c478bd9Sstevel@tonic-gate "clearing feature CFS_C_PORT_OVER_CURRENT"); 53327c478bd9Sstevel@tonic-gate 53337c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 53347c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 533535f36846Ssl HUB_HANDLE_PORT_FEATURE_TYPE, 53367c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 53377c478bd9Sstevel@tonic-gate CFS_C_PORT_OVER_CURRENT, 53387c478bd9Sstevel@tonic-gate port, 53397c478bd9Sstevel@tonic-gate 0, NULL, 0, 53407c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != 53417c478bd9Sstevel@tonic-gate USB_SUCCESS) { 53427c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 53437c478bd9Sstevel@tonic-gate hubd->h_log_handle, 53447c478bd9Sstevel@tonic-gate "clear feature CFS_C_PORT_OVER_CURRENT" 53457c478bd9Sstevel@tonic-gate " port%d failed (%d 0x%x %d)", 53467c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 53477c478bd9Sstevel@tonic-gate } 53487c478bd9Sstevel@tonic-gate } 53497c478bd9Sstevel@tonic-gate if (*change & PORT_CHANGE_PRSC & ack_flag) { 53507c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 53517c478bd9Sstevel@tonic-gate "clearing feature CFS_C_PORT_RESET"); 53527c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 53537c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 535435f36846Ssl HUB_HANDLE_PORT_FEATURE_TYPE, 53557c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 53567c478bd9Sstevel@tonic-gate CFS_C_PORT_RESET, 53577c478bd9Sstevel@tonic-gate port, 53587c478bd9Sstevel@tonic-gate 0, NULL, 0, 53597c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != 53607c478bd9Sstevel@tonic-gate USB_SUCCESS) { 53617c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 53627c478bd9Sstevel@tonic-gate hubd->h_log_handle, 53637c478bd9Sstevel@tonic-gate "clear feature CFS_C_PORT_RESET" 53647c478bd9Sstevel@tonic-gate " port%d failed (%d 0x%x %d)", 53657c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 53667c478bd9Sstevel@tonic-gate } 53677c478bd9Sstevel@tonic-gate } 5368993e3fafSRobert Mustacchi if (*change & PORT_CHANGE_BHPR & ack_flag) { 5369993e3fafSRobert Mustacchi USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 5370993e3fafSRobert Mustacchi "clearing feature CFS_C_BH_PORT_RESET"); 5371993e3fafSRobert Mustacchi if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 5372993e3fafSRobert Mustacchi hubd->h_default_pipe, 5373993e3fafSRobert Mustacchi HUB_HANDLE_PORT_FEATURE_TYPE, 5374993e3fafSRobert Mustacchi USB_REQ_CLEAR_FEATURE, 5375993e3fafSRobert Mustacchi CFS_C_BH_PORT_RESET, 5376993e3fafSRobert Mustacchi port, 5377993e3fafSRobert Mustacchi 0, NULL, 0, 5378993e3fafSRobert Mustacchi &completion_reason, &cb_flags, 0)) != 5379993e3fafSRobert Mustacchi USB_SUCCESS) { 5380993e3fafSRobert Mustacchi USB_DPRINTF_L2(DPRINT_MASK_PORT, 5381993e3fafSRobert Mustacchi hubd->h_log_handle, 5382993e3fafSRobert Mustacchi "clear feature CFS_C_BH_PORT_RESET" 5383993e3fafSRobert Mustacchi " port%d failed (%d 0x%x %d)", 5384993e3fafSRobert Mustacchi port, completion_reason, cb_flags, rval); 5385993e3fafSRobert Mustacchi } 5386993e3fafSRobert Mustacchi } 5387993e3fafSRobert Mustacchi if (*change & PORT_CHANGE_PLSC & ack_flag) { 5388993e3fafSRobert Mustacchi USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 5389993e3fafSRobert Mustacchi "clearing feature CFS_C_PORT_LINK_STATE"); 5390993e3fafSRobert Mustacchi if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 5391993e3fafSRobert Mustacchi hubd->h_default_pipe, 5392993e3fafSRobert Mustacchi HUB_HANDLE_PORT_FEATURE_TYPE, 5393993e3fafSRobert Mustacchi USB_REQ_CLEAR_FEATURE, 5394993e3fafSRobert Mustacchi CFS_C_PORT_LINK_STATE, 5395993e3fafSRobert Mustacchi port, 5396993e3fafSRobert Mustacchi 0, NULL, 0, 5397993e3fafSRobert Mustacchi &completion_reason, &cb_flags, 0)) != 5398993e3fafSRobert Mustacchi USB_SUCCESS) { 5399993e3fafSRobert Mustacchi USB_DPRINTF_L2(DPRINT_MASK_PORT, 5400993e3fafSRobert Mustacchi hubd->h_log_handle, 5401993e3fafSRobert Mustacchi "clear feature CFS_C_PORT_LINK_STATE" 5402993e3fafSRobert Mustacchi " port%d failed (%d 0x%x %d)", 5403993e3fafSRobert Mustacchi port, completion_reason, cb_flags, rval); 5404993e3fafSRobert Mustacchi } 5405993e3fafSRobert Mustacchi } 5406993e3fafSRobert Mustacchi if (*change & PORT_CHANGE_PCE & ack_flag) { 5407993e3fafSRobert Mustacchi USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 5408993e3fafSRobert Mustacchi "clearing feature CFS_C_PORT_CONFIG_ERROR"); 5409993e3fafSRobert Mustacchi if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 5410993e3fafSRobert Mustacchi hubd->h_default_pipe, 5411993e3fafSRobert Mustacchi HUB_HANDLE_PORT_FEATURE_TYPE, 5412993e3fafSRobert Mustacchi USB_REQ_CLEAR_FEATURE, 5413993e3fafSRobert Mustacchi CFS_C_PORT_CONFIG_ERROR, 5414993e3fafSRobert Mustacchi port, 5415993e3fafSRobert Mustacchi 0, NULL, 0, 5416993e3fafSRobert Mustacchi &completion_reason, &cb_flags, 0)) != 5417993e3fafSRobert Mustacchi USB_SUCCESS) { 5418993e3fafSRobert Mustacchi USB_DPRINTF_L2(DPRINT_MASK_PORT, 5419993e3fafSRobert Mustacchi hubd->h_log_handle, 5420993e3fafSRobert Mustacchi "clear feature CFS_C_PORT_CONFIG_ERROR" 5421993e3fafSRobert Mustacchi " port%d failed (%d 0x%x %d)", 5422993e3fafSRobert Mustacchi port, completion_reason, cb_flags, rval); 5423993e3fafSRobert Mustacchi } 5424993e3fafSRobert Mustacchi } 54257c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 54267c478bd9Sstevel@tonic-gate } 54277c478bd9Sstevel@tonic-gate 54287c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 54297c478bd9Sstevel@tonic-gate "new port%d state 0x%x", port, hubd->h_port_state[port]); 54307c478bd9Sstevel@tonic-gate 54317c478bd9Sstevel@tonic-gate 54327c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 54337c478bd9Sstevel@tonic-gate } 54347c478bd9Sstevel@tonic-gate 54357c478bd9Sstevel@tonic-gate 54367c478bd9Sstevel@tonic-gate /* 54377c478bd9Sstevel@tonic-gate * hubd_recover_disabled_port 54387c478bd9Sstevel@tonic-gate * if the port got disabled because of an error 54397c478bd9Sstevel@tonic-gate * enable it. If hub doesn't suport enable port, 54407c478bd9Sstevel@tonic-gate * reset the port to bring the device to life again 54417c478bd9Sstevel@tonic-gate */ 54427c478bd9Sstevel@tonic-gate static int 54437c478bd9Sstevel@tonic-gate hubd_recover_disabled_port(hubd_t *hubd, usb_port_t port) 54447c478bd9Sstevel@tonic-gate { 54457c478bd9Sstevel@tonic-gate uint16_t status; 54467c478bd9Sstevel@tonic-gate uint16_t change; 54477c478bd9Sstevel@tonic-gate int rval = USB_FAILURE; 54487c478bd9Sstevel@tonic-gate 54497c478bd9Sstevel@tonic-gate /* first try enabling the port */ 54507c478bd9Sstevel@tonic-gate (void) hubd_enable_port(hubd, port); 54517c478bd9Sstevel@tonic-gate 54527c478bd9Sstevel@tonic-gate /* read the port status */ 5453993e3fafSRobert Mustacchi (void) hubd_determine_port_status(hubd, port, &status, &change, NULL, 54547c478bd9Sstevel@tonic-gate PORT_CHANGE_PESC); 54557c478bd9Sstevel@tonic-gate 54567c478bd9Sstevel@tonic-gate if (status & PORT_STATUS_PES) { 54577c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5458c0f24e5bSlg "Port%d now Enabled", port); 54597c478bd9Sstevel@tonic-gate } else if (status & PORT_STATUS_CCS) { 54607c478bd9Sstevel@tonic-gate /* first post a disconnect event to the child */ 54617c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 54627c478bd9Sstevel@tonic-gate hubd_post_event(hubd, port, USBA_EVENT_TAG_HOT_REMOVAL); 54637c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 54647c478bd9Sstevel@tonic-gate 54657c478bd9Sstevel@tonic-gate /* then reset the port and recover the device */ 54667c478bd9Sstevel@tonic-gate rval = hubd_handle_port_connect(hubd, port); 54677c478bd9Sstevel@tonic-gate 54687c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5469c0f24e5bSlg "Port%d now Enabled by force", port); 54707c478bd9Sstevel@tonic-gate } 54717c478bd9Sstevel@tonic-gate 54727c478bd9Sstevel@tonic-gate return (rval); 54737c478bd9Sstevel@tonic-gate } 54747c478bd9Sstevel@tonic-gate 54757c478bd9Sstevel@tonic-gate 54767c478bd9Sstevel@tonic-gate /* 54777c478bd9Sstevel@tonic-gate * hubd_enable_all_port_power: 54787c478bd9Sstevel@tonic-gate */ 54797c478bd9Sstevel@tonic-gate static int 54807c478bd9Sstevel@tonic-gate hubd_enable_all_port_power(hubd_t *hubd) 54817c478bd9Sstevel@tonic-gate { 54827c478bd9Sstevel@tonic-gate int wait; 54837c478bd9Sstevel@tonic-gate usb_port_t port; 54847c478bd9Sstevel@tonic-gate uint_t retry; 54857c478bd9Sstevel@tonic-gate uint16_t status; 54867c478bd9Sstevel@tonic-gate uint16_t change; 54877c478bd9Sstevel@tonic-gate 54887c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 54897c478bd9Sstevel@tonic-gate "hubd_enable_all_port_power"); 54907c478bd9Sstevel@tonic-gate 54917c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 54927c478bd9Sstevel@tonic-gate 54937c478bd9Sstevel@tonic-gate /* 54947c478bd9Sstevel@tonic-gate * According to section 11.11 of USB, for hubs with no power 54957c478bd9Sstevel@tonic-gate * switches, bPwrOn2PwrGood is zero. But we wait for some 54967c478bd9Sstevel@tonic-gate * arbitrary time to enable power to become stable. 54977c478bd9Sstevel@tonic-gate * 54987c478bd9Sstevel@tonic-gate * If an hub supports port power switching, we need to wait 5499993e3fafSRobert Mustacchi * at least 20ms before accessing corresponding usb port. Note, 5500993e3fafSRobert Mustacchi * this member is stored in the h_power_good member. 55017c478bd9Sstevel@tonic-gate */ 5502993e3fafSRobert Mustacchi if ((hubd->h_hub_chars & HUB_CHARS_NO_POWER_SWITCHING) || 5503993e3fafSRobert Mustacchi (hubd->h_power_good == 0)) { 55047c478bd9Sstevel@tonic-gate wait = hubd_device_delay / 10; 55057c478bd9Sstevel@tonic-gate } else { 55067c478bd9Sstevel@tonic-gate wait = max(HUB_DEFAULT_POPG, 5507993e3fafSRobert Mustacchi hubd->h_power_good) * 2 * 1000; 55087c478bd9Sstevel@tonic-gate } 55097c478bd9Sstevel@tonic-gate 55107c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 55117c478bd9Sstevel@tonic-gate "hubd_enable_all_port_power: popg=%d wait=%d", 5512993e3fafSRobert Mustacchi hubd->h_power_good, wait); 55137c478bd9Sstevel@tonic-gate 55147c478bd9Sstevel@tonic-gate /* 55157c478bd9Sstevel@tonic-gate * Enable power per port. we ignore gang power and power mask 55167c478bd9Sstevel@tonic-gate * and always enable all ports one by one. 55177c478bd9Sstevel@tonic-gate */ 5518993e3fafSRobert Mustacchi for (port = 1; port <= hubd->h_nports; port++) { 55197c478bd9Sstevel@tonic-gate /* 55207c478bd9Sstevel@tonic-gate * Transition the port from the Powered Off to the 55217c478bd9Sstevel@tonic-gate * Disconnected state by supplying power to the port. 55227c478bd9Sstevel@tonic-gate */ 55237c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, 55247c478bd9Sstevel@tonic-gate hubd->h_log_handle, 55257c478bd9Sstevel@tonic-gate "hubd_enable_all_port_power: power port=%d", port); 55267c478bd9Sstevel@tonic-gate 55277c478bd9Sstevel@tonic-gate (void) hubd_enable_port_power(hubd, port); 55287c478bd9Sstevel@tonic-gate } 55297c478bd9Sstevel@tonic-gate 55307c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 55317c478bd9Sstevel@tonic-gate delay(drv_usectohz(wait)); 55327c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 55337c478bd9Sstevel@tonic-gate 55347c478bd9Sstevel@tonic-gate /* For retry if any, use some extra delay */ 55357c478bd9Sstevel@tonic-gate wait = max(wait, hubd_device_delay / 10); 55367c478bd9Sstevel@tonic-gate 55377c478bd9Sstevel@tonic-gate /* Check each port power status for a given usb hub */ 5538993e3fafSRobert Mustacchi for (port = 1; port <= hubd->h_nports; port++) { 55397c478bd9Sstevel@tonic-gate 55407c478bd9Sstevel@tonic-gate /* Get port status */ 55417c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 5542993e3fafSRobert Mustacchi &status, &change, NULL, 0); 55437c478bd9Sstevel@tonic-gate 55447c478bd9Sstevel@tonic-gate for (retry = 0; ((!(status & PORT_STATUS_PPS)) && 55457c478bd9Sstevel@tonic-gate (retry < HUBD_PORT_RETRY)); retry++) { 55467c478bd9Sstevel@tonic-gate 55477c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 55487c478bd9Sstevel@tonic-gate "Retry is in progress %d: port %d status %d", 55497c478bd9Sstevel@tonic-gate retry, port, status); 55507c478bd9Sstevel@tonic-gate 55517c478bd9Sstevel@tonic-gate (void) hubd_enable_port_power(hubd, port); 55527c478bd9Sstevel@tonic-gate 55537c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 55547c478bd9Sstevel@tonic-gate delay(drv_usectohz(wait)); 55557c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 55567c478bd9Sstevel@tonic-gate 55577c478bd9Sstevel@tonic-gate /* Get port status */ 55587c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 5559993e3fafSRobert Mustacchi &status, &change, NULL, 0); 55607c478bd9Sstevel@tonic-gate } 55617c478bd9Sstevel@tonic-gate 55627c478bd9Sstevel@tonic-gate /* Print warning message if port has no power */ 55637c478bd9Sstevel@tonic-gate if (!(status & PORT_STATUS_PPS)) { 55647c478bd9Sstevel@tonic-gate 5565d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 55667c478bd9Sstevel@tonic-gate "hubd_enable_all_port_power: port %d power-on " 55677c478bd9Sstevel@tonic-gate "failed, port status 0x%x", port, status); 55687c478bd9Sstevel@tonic-gate } 55697c478bd9Sstevel@tonic-gate } 55707c478bd9Sstevel@tonic-gate 55717c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 55727c478bd9Sstevel@tonic-gate } 55737c478bd9Sstevel@tonic-gate 55747c478bd9Sstevel@tonic-gate 55757c478bd9Sstevel@tonic-gate /* 55767c478bd9Sstevel@tonic-gate * hubd_enable_port_power: 55777c478bd9Sstevel@tonic-gate * enable individual port power 55787c478bd9Sstevel@tonic-gate */ 55797c478bd9Sstevel@tonic-gate static int 55807c478bd9Sstevel@tonic-gate hubd_enable_port_power(hubd_t *hubd, usb_port_t port) 55817c478bd9Sstevel@tonic-gate { 55827c478bd9Sstevel@tonic-gate int rval; 55837c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 55847c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 55857c478bd9Sstevel@tonic-gate 55867c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 55877c478bd9Sstevel@tonic-gate "hubd_enable_port_power: port=%d", port); 55887c478bd9Sstevel@tonic-gate 55897c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 55907c478bd9Sstevel@tonic-gate ASSERT(hubd->h_default_pipe != 0); 55917c478bd9Sstevel@tonic-gate 55927c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 55937c478bd9Sstevel@tonic-gate 55947c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 55957c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 559635f36846Ssl HUB_HANDLE_PORT_FEATURE_TYPE, 55977c478bd9Sstevel@tonic-gate USB_REQ_SET_FEATURE, 55987c478bd9Sstevel@tonic-gate CFS_PORT_POWER, 55997c478bd9Sstevel@tonic-gate port, 56007c478bd9Sstevel@tonic-gate 0, NULL, 0, 56017c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 56027c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 56037c478bd9Sstevel@tonic-gate "set port power failed (%d 0x%x %d)", 56047c478bd9Sstevel@tonic-gate completion_reason, cb_flags, rval); 56057c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 56067c478bd9Sstevel@tonic-gate 56077c478bd9Sstevel@tonic-gate return (USB_FAILURE); 56087c478bd9Sstevel@tonic-gate } else { 56097c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 56107c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= PORT_STATUS_PPS; 56117c478bd9Sstevel@tonic-gate 56127c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 56137c478bd9Sstevel@tonic-gate } 56147c478bd9Sstevel@tonic-gate } 56157c478bd9Sstevel@tonic-gate 56167c478bd9Sstevel@tonic-gate 56177c478bd9Sstevel@tonic-gate /* 56187c478bd9Sstevel@tonic-gate * hubd_disable_all_port_power: 56197c478bd9Sstevel@tonic-gate */ 56207c478bd9Sstevel@tonic-gate static int 56217c478bd9Sstevel@tonic-gate hubd_disable_all_port_power(hubd_t *hubd) 56227c478bd9Sstevel@tonic-gate { 56237c478bd9Sstevel@tonic-gate usb_port_t port; 56247c478bd9Sstevel@tonic-gate 56257c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 56267c478bd9Sstevel@tonic-gate "hubd_disable_all_port_power"); 56277c478bd9Sstevel@tonic-gate 56287c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 56297c478bd9Sstevel@tonic-gate 56307c478bd9Sstevel@tonic-gate /* 56317c478bd9Sstevel@tonic-gate * disable power per port, ignore gang power and power mask 56327c478bd9Sstevel@tonic-gate */ 5633993e3fafSRobert Mustacchi for (port = 1; port <= hubd->h_nports; port++) { 56347c478bd9Sstevel@tonic-gate (void) hubd_disable_port_power(hubd, port); 56357c478bd9Sstevel@tonic-gate } 56367c478bd9Sstevel@tonic-gate 56377c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 56387c478bd9Sstevel@tonic-gate } 56397c478bd9Sstevel@tonic-gate 56407c478bd9Sstevel@tonic-gate 56417c478bd9Sstevel@tonic-gate /* 56427c478bd9Sstevel@tonic-gate * hubd_disable_port_power: 56437c478bd9Sstevel@tonic-gate * disable individual port power 56447c478bd9Sstevel@tonic-gate */ 56457c478bd9Sstevel@tonic-gate static int 56467c478bd9Sstevel@tonic-gate hubd_disable_port_power(hubd_t *hubd, usb_port_t port) 56477c478bd9Sstevel@tonic-gate { 56487c478bd9Sstevel@tonic-gate int rval; 56497c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 56507c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 56517c478bd9Sstevel@tonic-gate 56527c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 56537c478bd9Sstevel@tonic-gate "hubd_disable_port_power: port=%d", port); 56547c478bd9Sstevel@tonic-gate 56557c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 56567c478bd9Sstevel@tonic-gate 56577c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 56587c478bd9Sstevel@tonic-gate 56597c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 56607c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 566135f36846Ssl HUB_HANDLE_PORT_FEATURE_TYPE, 56627c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 56637c478bd9Sstevel@tonic-gate CFS_PORT_POWER, 56647c478bd9Sstevel@tonic-gate port, 56657c478bd9Sstevel@tonic-gate 0, NULL, 0, 56667c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 56677c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 56687c478bd9Sstevel@tonic-gate "clearing port%d power failed (%d 0x%x %d)", 56697c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 56707c478bd9Sstevel@tonic-gate 56717c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 56727c478bd9Sstevel@tonic-gate 56737c478bd9Sstevel@tonic-gate return (USB_FAILURE); 56747c478bd9Sstevel@tonic-gate } else { 56757c478bd9Sstevel@tonic-gate 56767c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 56777c478bd9Sstevel@tonic-gate ASSERT(completion_reason == 0); 56787c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~PORT_STATUS_PPS; 56797c478bd9Sstevel@tonic-gate 56807c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 56817c478bd9Sstevel@tonic-gate } 56827c478bd9Sstevel@tonic-gate } 56837c478bd9Sstevel@tonic-gate 56847c478bd9Sstevel@tonic-gate 56857c478bd9Sstevel@tonic-gate /* 56867c478bd9Sstevel@tonic-gate * Search the database of user preferences and find out the preferred 56877c478bd9Sstevel@tonic-gate * configuration for this new device 56887c478bd9Sstevel@tonic-gate */ 5689ff0e937bSRaymond Chen int 56907c478bd9Sstevel@tonic-gate hubd_select_device_configuration(hubd_t *hubd, usb_port_t port, 5691993e3fafSRobert Mustacchi dev_info_t *child_dip, usba_device_t *child_ud) 56927c478bd9Sstevel@tonic-gate { 56937c478bd9Sstevel@tonic-gate char *pathname = NULL; 56947c478bd9Sstevel@tonic-gate char *tmp_path = NULL; 56957c478bd9Sstevel@tonic-gate int user_conf; 56967c478bd9Sstevel@tonic-gate int pathlen; 56977c478bd9Sstevel@tonic-gate usb_dev_descr_t *usbdev_ptr; 56987c478bd9Sstevel@tonic-gate usba_configrec_t *user_pref; 56997c478bd9Sstevel@tonic-gate 57007c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 57017c478bd9Sstevel@tonic-gate usbdev_ptr = child_ud->usb_dev_descr; 57027c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 57037c478bd9Sstevel@tonic-gate 57047c478bd9Sstevel@tonic-gate /* try to get pathname for this device */ 57057c478bd9Sstevel@tonic-gate tmp_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 57067c478bd9Sstevel@tonic-gate (void) ddi_pathname(child_dip, tmp_path); 57077c478bd9Sstevel@tonic-gate 57087c478bd9Sstevel@tonic-gate pathlen = strlen(tmp_path) + 32; 57097c478bd9Sstevel@tonic-gate pathname = kmem_zalloc(pathlen, KM_SLEEP); 57107c478bd9Sstevel@tonic-gate 57117c478bd9Sstevel@tonic-gate /* 57127c478bd9Sstevel@tonic-gate * We haven't initialized the node and it doesn't have an address 57137c478bd9Sstevel@tonic-gate * yet. Append port number to the physical pathname 57147c478bd9Sstevel@tonic-gate */ 57157c478bd9Sstevel@tonic-gate (void) sprintf(pathname, "%s@%d", tmp_path, port); 57167c478bd9Sstevel@tonic-gate 57177c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 57187c478bd9Sstevel@tonic-gate "hubd_select_device_configuration: Device=%s\n\t" 57197c478bd9Sstevel@tonic-gate "Child path=%s", 57207c478bd9Sstevel@tonic-gate usba_get_mfg_prod_sn_str(child_dip, tmp_path, MAXPATHLEN), 57217c478bd9Sstevel@tonic-gate pathname); 57227c478bd9Sstevel@tonic-gate kmem_free(tmp_path, MAXPATHLEN); 57237c478bd9Sstevel@tonic-gate 57247c478bd9Sstevel@tonic-gate 57257c478bd9Sstevel@tonic-gate /* database search for user preferences */ 57267c478bd9Sstevel@tonic-gate user_pref = usba_devdb_get_user_preferences(usbdev_ptr->idVendor, 57277c478bd9Sstevel@tonic-gate usbdev_ptr->idProduct, child_ud->usb_serialno_str, pathname); 57287c478bd9Sstevel@tonic-gate 57297c478bd9Sstevel@tonic-gate if (user_pref) { 57307c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 57317c478bd9Sstevel@tonic-gate "hubd_select_device_configuration: " 57327c478bd9Sstevel@tonic-gate "usba_devdb_get_user_preferences " 57337c478bd9Sstevel@tonic-gate "return user_conf=%d\npreferred driver=%s path=%s", 57347c478bd9Sstevel@tonic-gate user_pref->cfg_index, user_pref->driver, 57357c478bd9Sstevel@tonic-gate user_pref->pathname); 57367c478bd9Sstevel@tonic-gate 57377c478bd9Sstevel@tonic-gate user_conf = user_pref->cfg_index; 57387c478bd9Sstevel@tonic-gate 57397c478bd9Sstevel@tonic-gate if (user_pref->driver) { 57407c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 57417c478bd9Sstevel@tonic-gate child_ud->usb_preferred_driver = user_pref->driver; 57427c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 57437c478bd9Sstevel@tonic-gate } 57447c478bd9Sstevel@tonic-gate } else { 57457c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 57467c478bd9Sstevel@tonic-gate "hubd_select_device_configuration: No match found"); 57477c478bd9Sstevel@tonic-gate 57487c478bd9Sstevel@tonic-gate /* select default configuration for this device */ 57497c478bd9Sstevel@tonic-gate user_conf = USBA_DEV_CONFIG_INDEX_UNDEFINED; 57507c478bd9Sstevel@tonic-gate } 57517c478bd9Sstevel@tonic-gate kmem_free(pathname, pathlen); 57527c478bd9Sstevel@tonic-gate 57537c478bd9Sstevel@tonic-gate /* if the device has just one configuration, set default value */ 57547c478bd9Sstevel@tonic-gate if (usbdev_ptr->bNumConfigurations == 1) { 57557c478bd9Sstevel@tonic-gate user_conf = USB_DEV_DEFAULT_CONFIG_INDEX; 57567c478bd9Sstevel@tonic-gate } 57577c478bd9Sstevel@tonic-gate 57587c478bd9Sstevel@tonic-gate return (user_conf); 57597c478bd9Sstevel@tonic-gate } 57607c478bd9Sstevel@tonic-gate 57617c478bd9Sstevel@tonic-gate 57627c478bd9Sstevel@tonic-gate /* 57637c478bd9Sstevel@tonic-gate * Retrieves config cloud for this configuration 57647c478bd9Sstevel@tonic-gate */ 57657c478bd9Sstevel@tonic-gate int 57667c478bd9Sstevel@tonic-gate hubd_get_this_config_cloud(hubd_t *hubd, dev_info_t *dip, 5767993e3fafSRobert Mustacchi usba_device_t *child_ud, uint16_t conf_index) 57687c478bd9Sstevel@tonic-gate { 57697c478bd9Sstevel@tonic-gate usb_cfg_descr_t *confdescr; 57707c478bd9Sstevel@tonic-gate mblk_t *pdata = NULL; 57717c478bd9Sstevel@tonic-gate int rval; 57727c478bd9Sstevel@tonic-gate size_t size; 57737c478bd9Sstevel@tonic-gate char *tmpbuf; 57747c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 57757c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 57767c478bd9Sstevel@tonic-gate usb_pipe_handle_t def_ph; 57777c478bd9Sstevel@tonic-gate 57787c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 57797c478bd9Sstevel@tonic-gate "hubd_get_this_config_cloud: conf_index=%d", conf_index); 57807c478bd9Sstevel@tonic-gate 57817c478bd9Sstevel@tonic-gate 57827c478bd9Sstevel@tonic-gate /* alloc temporary space for config descriptor */ 57837c478bd9Sstevel@tonic-gate confdescr = (usb_cfg_descr_t *)kmem_zalloc(USB_CFG_DESCR_SIZE, 57847c478bd9Sstevel@tonic-gate KM_SLEEP); 57857c478bd9Sstevel@tonic-gate 57867c478bd9Sstevel@tonic-gate /* alloc temporary space for string descriptor */ 57877c478bd9Sstevel@tonic-gate tmpbuf = kmem_zalloc(USB_MAXSTRINGLEN, KM_SLEEP); 57887c478bd9Sstevel@tonic-gate 57897c478bd9Sstevel@tonic-gate def_ph = usba_get_dflt_pipe_handle(dip); 57907c478bd9Sstevel@tonic-gate 57917c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(dip, def_ph, 57927c478bd9Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD, 57937c478bd9Sstevel@tonic-gate USB_REQ_GET_DESCR, 57947c478bd9Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_CFG | conf_index, 57957c478bd9Sstevel@tonic-gate 0, 57967c478bd9Sstevel@tonic-gate USB_CFG_DESCR_SIZE, 57977c478bd9Sstevel@tonic-gate &pdata, 57987c478bd9Sstevel@tonic-gate 0, 57997c478bd9Sstevel@tonic-gate &completion_reason, 58007c478bd9Sstevel@tonic-gate &cb_flags, 58017c478bd9Sstevel@tonic-gate 0)) == USB_SUCCESS) { 58027c478bd9Sstevel@tonic-gate 58037c478bd9Sstevel@tonic-gate /* this must be true since we didn't allow data underruns */ 5804d29f5a71Szhigang lu - Sun Microsystems - Beijing China if (MBLKL(pdata) != USB_CFG_DESCR_SIZE) { 580535f36846Ssl USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 580635f36846Ssl "device returned incorrect configuration " 580735f36846Ssl "descriptor size."); 580835f36846Ssl 580935f36846Ssl rval = USB_FAILURE; 581035f36846Ssl goto done; 581135f36846Ssl } 58127c478bd9Sstevel@tonic-gate 58137c478bd9Sstevel@tonic-gate /* 58147c478bd9Sstevel@tonic-gate * Parse the configuration descriptor 58157c478bd9Sstevel@tonic-gate */ 58167c478bd9Sstevel@tonic-gate size = usb_parse_cfg_descr(pdata->b_rptr, 5817d29f5a71Szhigang lu - Sun Microsystems - Beijing China MBLKL(pdata), confdescr, 58187c478bd9Sstevel@tonic-gate USB_CFG_DESCR_SIZE); 58197c478bd9Sstevel@tonic-gate 58207c478bd9Sstevel@tonic-gate /* if parse cfg descr error, it should return failure */ 58217c478bd9Sstevel@tonic-gate if (size == USB_PARSE_ERROR) { 58227c478bd9Sstevel@tonic-gate 58237c478bd9Sstevel@tonic-gate if (pdata->b_rptr[1] != USB_DESCR_TYPE_CFG) { 5824d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 58257c478bd9Sstevel@tonic-gate hubd->h_log_handle, 58267c478bd9Sstevel@tonic-gate "device returned incorrect " 58277c478bd9Sstevel@tonic-gate "configuration descriptor type."); 58287c478bd9Sstevel@tonic-gate } 58297c478bd9Sstevel@tonic-gate rval = USB_FAILURE; 58307c478bd9Sstevel@tonic-gate goto done; 58317c478bd9Sstevel@tonic-gate } 58327c478bd9Sstevel@tonic-gate 58337c478bd9Sstevel@tonic-gate if (confdescr->wTotalLength < USB_CFG_DESCR_SIZE) { 5834d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 58357c478bd9Sstevel@tonic-gate hubd->h_log_handle, 58367c478bd9Sstevel@tonic-gate "device returned incorrect " 58377c478bd9Sstevel@tonic-gate "configuration descriptor size."); 58387c478bd9Sstevel@tonic-gate 58397c478bd9Sstevel@tonic-gate rval = USB_FAILURE; 58407c478bd9Sstevel@tonic-gate goto done; 58417c478bd9Sstevel@tonic-gate } 58427c478bd9Sstevel@tonic-gate 58437c478bd9Sstevel@tonic-gate freemsg(pdata); 58447c478bd9Sstevel@tonic-gate pdata = NULL; 58457c478bd9Sstevel@tonic-gate 58467c478bd9Sstevel@tonic-gate /* Now fetch the complete config cloud */ 58477c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(dip, def_ph, 58487c478bd9Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD, 58497c478bd9Sstevel@tonic-gate USB_REQ_GET_DESCR, 58507c478bd9Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_CFG | conf_index, 58517c478bd9Sstevel@tonic-gate 0, 58527c478bd9Sstevel@tonic-gate confdescr->wTotalLength, 58537c478bd9Sstevel@tonic-gate &pdata, 58547c478bd9Sstevel@tonic-gate 0, 58557c478bd9Sstevel@tonic-gate &completion_reason, 58567c478bd9Sstevel@tonic-gate &cb_flags, 58577c478bd9Sstevel@tonic-gate 0)) == USB_SUCCESS) { 58587c478bd9Sstevel@tonic-gate 5859d29f5a71Szhigang lu - Sun Microsystems - Beijing China if (MBLKL(pdata) != 58607c478bd9Sstevel@tonic-gate confdescr->wTotalLength) { 58617c478bd9Sstevel@tonic-gate 5862d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 58637c478bd9Sstevel@tonic-gate hubd->h_log_handle, 58647c478bd9Sstevel@tonic-gate "device returned incorrect " 58657c478bd9Sstevel@tonic-gate "configuration descriptor."); 58667c478bd9Sstevel@tonic-gate 58677c478bd9Sstevel@tonic-gate rval = USB_FAILURE; 58687c478bd9Sstevel@tonic-gate goto done; 58697c478bd9Sstevel@tonic-gate } 58707c478bd9Sstevel@tonic-gate 58717c478bd9Sstevel@tonic-gate /* 58727c478bd9Sstevel@tonic-gate * copy config descriptor into usba_device 58737c478bd9Sstevel@tonic-gate */ 58747c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 58757c478bd9Sstevel@tonic-gate child_ud->usb_cfg_array[conf_index] = 58767c478bd9Sstevel@tonic-gate kmem_alloc(confdescr->wTotalLength, KM_SLEEP); 58777c478bd9Sstevel@tonic-gate child_ud->usb_cfg_array_len[conf_index] = 5878c0f24e5bSlg confdescr->wTotalLength; 58797c478bd9Sstevel@tonic-gate bcopy((caddr_t)pdata->b_rptr, 58807c478bd9Sstevel@tonic-gate (caddr_t)child_ud->usb_cfg_array[conf_index], 58817c478bd9Sstevel@tonic-gate confdescr->wTotalLength); 58827c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 58837c478bd9Sstevel@tonic-gate 58847c478bd9Sstevel@tonic-gate /* 58857c478bd9Sstevel@tonic-gate * retrieve string descriptor describing this 58867c478bd9Sstevel@tonic-gate * configuration 58877c478bd9Sstevel@tonic-gate */ 58887c478bd9Sstevel@tonic-gate if (confdescr->iConfiguration) { 58897c478bd9Sstevel@tonic-gate 58907c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, 58917c478bd9Sstevel@tonic-gate hubd->h_log_handle, 58927c478bd9Sstevel@tonic-gate "Get conf str descr for config_index=%d", 58937c478bd9Sstevel@tonic-gate conf_index); 58947c478bd9Sstevel@tonic-gate 58957c478bd9Sstevel@tonic-gate /* 58967c478bd9Sstevel@tonic-gate * Now fetch the string descriptor describing 58977c478bd9Sstevel@tonic-gate * this configuration 58987c478bd9Sstevel@tonic-gate */ 58997c478bd9Sstevel@tonic-gate if ((rval = usb_get_string_descr(dip, 59007c478bd9Sstevel@tonic-gate USB_LANG_ID, confdescr->iConfiguration, 59017c478bd9Sstevel@tonic-gate tmpbuf, USB_MAXSTRINGLEN)) == 59027c478bd9Sstevel@tonic-gate USB_SUCCESS) { 59037c478bd9Sstevel@tonic-gate size = strlen(tmpbuf); 59047c478bd9Sstevel@tonic-gate if (size > 0) { 59057c478bd9Sstevel@tonic-gate child_ud->usb_cfg_str_descr 59067c478bd9Sstevel@tonic-gate [conf_index] = (char *) 59077c478bd9Sstevel@tonic-gate kmem_zalloc(size + 1, 59087c478bd9Sstevel@tonic-gate KM_SLEEP); 59097c478bd9Sstevel@tonic-gate (void) strcpy( 59107c478bd9Sstevel@tonic-gate child_ud->usb_cfg_str_descr 59117c478bd9Sstevel@tonic-gate [conf_index], tmpbuf); 59127c478bd9Sstevel@tonic-gate } 59137c478bd9Sstevel@tonic-gate } else { 59147c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 59157c478bd9Sstevel@tonic-gate hubd->h_log_handle, 59167c478bd9Sstevel@tonic-gate "hubd_get_this_config_cloud: " 59177c478bd9Sstevel@tonic-gate "getting config string (%d) " 59187c478bd9Sstevel@tonic-gate "failed", 59197c478bd9Sstevel@tonic-gate confdescr->iConfiguration); 59207c478bd9Sstevel@tonic-gate 59217c478bd9Sstevel@tonic-gate /* ignore this error */ 59227c478bd9Sstevel@tonic-gate rval = USB_SUCCESS; 59237c478bd9Sstevel@tonic-gate } 59247c478bd9Sstevel@tonic-gate } 59257c478bd9Sstevel@tonic-gate } 59267c478bd9Sstevel@tonic-gate } 59277c478bd9Sstevel@tonic-gate 59287c478bd9Sstevel@tonic-gate done: 59297c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 59307c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 59317c478bd9Sstevel@tonic-gate "hubd_get_this_config_cloud: " 59327c478bd9Sstevel@tonic-gate "error in retrieving config descriptor for " 59337c478bd9Sstevel@tonic-gate "config index=%d rval=%d cr=%d", 59347c478bd9Sstevel@tonic-gate conf_index, rval, completion_reason); 59357c478bd9Sstevel@tonic-gate } 59367c478bd9Sstevel@tonic-gate 59377c478bd9Sstevel@tonic-gate if (pdata) { 59387c478bd9Sstevel@tonic-gate freemsg(pdata); 59397c478bd9Sstevel@tonic-gate pdata = NULL; 59407c478bd9Sstevel@tonic-gate } 59417c478bd9Sstevel@tonic-gate 59427c478bd9Sstevel@tonic-gate kmem_free(confdescr, USB_CFG_DESCR_SIZE); 59437c478bd9Sstevel@tonic-gate kmem_free(tmpbuf, USB_MAXSTRINGLEN); 59447c478bd9Sstevel@tonic-gate 59457c478bd9Sstevel@tonic-gate return (rval); 59467c478bd9Sstevel@tonic-gate } 59477c478bd9Sstevel@tonic-gate 59487c478bd9Sstevel@tonic-gate 59497c478bd9Sstevel@tonic-gate /* 59507c478bd9Sstevel@tonic-gate * Retrieves the entire config cloud for all configurations of the device 59517c478bd9Sstevel@tonic-gate */ 59527c478bd9Sstevel@tonic-gate int 59537c478bd9Sstevel@tonic-gate hubd_get_all_device_config_cloud(hubd_t *hubd, dev_info_t *dip, 5954993e3fafSRobert Mustacchi usba_device_t *child_ud) 59557c478bd9Sstevel@tonic-gate { 59567c478bd9Sstevel@tonic-gate int rval = USB_SUCCESS; 59577c478bd9Sstevel@tonic-gate int ncfgs; 59587c478bd9Sstevel@tonic-gate uint16_t size; 59597c478bd9Sstevel@tonic-gate uint16_t conf_index; 59607c478bd9Sstevel@tonic-gate uchar_t **cfg_array; 59617c478bd9Sstevel@tonic-gate uint16_t *cfg_array_len; 59627c478bd9Sstevel@tonic-gate char **str_descr; 59637c478bd9Sstevel@tonic-gate 59647c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 59657c478bd9Sstevel@tonic-gate "hubd_get_all_device_config_cloud: Start"); 59667c478bd9Sstevel@tonic-gate 59677c478bd9Sstevel@tonic-gate /* alloc pointer array for conf. descriptors */ 59687c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 59697c478bd9Sstevel@tonic-gate ncfgs = child_ud->usb_n_cfgs; 59707c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 59717c478bd9Sstevel@tonic-gate 59727c478bd9Sstevel@tonic-gate size = sizeof (uchar_t *) * ncfgs; 59737c478bd9Sstevel@tonic-gate cfg_array = kmem_zalloc(size, KM_SLEEP); 59747c478bd9Sstevel@tonic-gate cfg_array_len = kmem_zalloc(ncfgs * sizeof (uint16_t), KM_SLEEP); 59757c478bd9Sstevel@tonic-gate str_descr = kmem_zalloc(size, KM_SLEEP); 59767c478bd9Sstevel@tonic-gate 59777c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 59787c478bd9Sstevel@tonic-gate child_ud->usb_cfg_array = cfg_array; 59797c478bd9Sstevel@tonic-gate child_ud->usb_cfg_array_len = cfg_array_len; 59807c478bd9Sstevel@tonic-gate child_ud->usb_cfg_array_length = size; 59817c478bd9Sstevel@tonic-gate child_ud->usb_cfg_array_len_length = ncfgs * sizeof (uint16_t); 59827c478bd9Sstevel@tonic-gate child_ud->usb_cfg_str_descr = str_descr; 59837c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 59847c478bd9Sstevel@tonic-gate 59857c478bd9Sstevel@tonic-gate /* Get configuration descriptor for each configuration */ 59867c478bd9Sstevel@tonic-gate for (conf_index = 0; (conf_index < ncfgs) && 59877c478bd9Sstevel@tonic-gate (rval == USB_SUCCESS); conf_index++) { 59887c478bd9Sstevel@tonic-gate 59897c478bd9Sstevel@tonic-gate rval = hubd_get_this_config_cloud(hubd, dip, child_ud, 59907c478bd9Sstevel@tonic-gate conf_index); 59917c478bd9Sstevel@tonic-gate } 59927c478bd9Sstevel@tonic-gate 59937c478bd9Sstevel@tonic-gate return (rval); 59947c478bd9Sstevel@tonic-gate } 59957c478bd9Sstevel@tonic-gate 59967c478bd9Sstevel@tonic-gate 59977c478bd9Sstevel@tonic-gate /* 59987c478bd9Sstevel@tonic-gate * hubd_ready_device: 59997c478bd9Sstevel@tonic-gate * Update the usba_device structure 60007c478bd9Sstevel@tonic-gate * Set the given configuration 60017c478bd9Sstevel@tonic-gate * Prepares the device node for driver to online. If an existing 60027c478bd9Sstevel@tonic-gate * OBP node is found, it will switch to the OBP node. 60037c478bd9Sstevel@tonic-gate */ 6004ff0e937bSRaymond Chen dev_info_t * 60057c478bd9Sstevel@tonic-gate hubd_ready_device(hubd_t *hubd, dev_info_t *child_dip, usba_device_t *child_ud, 600635f36846Ssl uint_t config_index) 60077c478bd9Sstevel@tonic-gate { 60087c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 60097c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 60107c478bd9Sstevel@tonic-gate size_t size; 60117c478bd9Sstevel@tonic-gate usb_cfg_descr_t config_descriptor; 60127c478bd9Sstevel@tonic-gate usb_pipe_handle_t def_ph; 60137c478bd9Sstevel@tonic-gate usba_pipe_handle_data_t *ph; 60147c478bd9Sstevel@tonic-gate 60157c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 6016112116d8Sfb "hubd_ready_device: dip=0x%p, user_conf_index=%d", 6017112116d8Sfb (void *)child_dip, config_index); 60187c478bd9Sstevel@tonic-gate 60197c478bd9Sstevel@tonic-gate size = usb_parse_cfg_descr( 60207c478bd9Sstevel@tonic-gate child_ud->usb_cfg_array[config_index], USB_CFG_DESCR_SIZE, 60217c478bd9Sstevel@tonic-gate &config_descriptor, USB_CFG_DESCR_SIZE); 60227c478bd9Sstevel@tonic-gate ASSERT(size == USB_CFG_DESCR_SIZE); 60237c478bd9Sstevel@tonic-gate 60247c478bd9Sstevel@tonic-gate def_ph = usba_get_dflt_pipe_handle(child_dip); 60257c478bd9Sstevel@tonic-gate 60267c478bd9Sstevel@tonic-gate /* Set the configuration */ 60277c478bd9Sstevel@tonic-gate (void) usb_pipe_sync_ctrl_xfer(child_dip, def_ph, 60287c478bd9Sstevel@tonic-gate USB_DEV_REQ_HOST_TO_DEV, 60297c478bd9Sstevel@tonic-gate USB_REQ_SET_CFG, /* bRequest */ 60307c478bd9Sstevel@tonic-gate config_descriptor.bConfigurationValue, /* wValue */ 60317c478bd9Sstevel@tonic-gate 0, /* wIndex */ 60327c478bd9Sstevel@tonic-gate 0, /* wLength */ 60337c478bd9Sstevel@tonic-gate NULL, 60347c478bd9Sstevel@tonic-gate 0, 60357c478bd9Sstevel@tonic-gate &completion_reason, 60367c478bd9Sstevel@tonic-gate &cb_flags, 60377c478bd9Sstevel@tonic-gate 0); 60387c478bd9Sstevel@tonic-gate 60397c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 60407c478bd9Sstevel@tonic-gate child_ud->usb_active_cfg_ndx = config_index; 60417c478bd9Sstevel@tonic-gate child_ud->usb_cfg = child_ud->usb_cfg_array[config_index]; 60427c478bd9Sstevel@tonic-gate child_ud->usb_cfg_length = config_descriptor.wTotalLength; 6043*d96925c4SRichard Lowe child_ud->usb_cfg_value = config_descriptor.bConfigurationValue; 60447c478bd9Sstevel@tonic-gate child_ud->usb_n_ifs = config_descriptor.bNumInterfaces; 60457c478bd9Sstevel@tonic-gate child_ud->usb_dip = child_dip; 60467c478bd9Sstevel@tonic-gate 60477c478bd9Sstevel@tonic-gate child_ud->usb_client_flags = kmem_zalloc( 6048c0f24e5bSlg child_ud->usb_n_ifs * USBA_CLIENT_FLAG_SIZE, KM_SLEEP); 60497c478bd9Sstevel@tonic-gate 60507c478bd9Sstevel@tonic-gate child_ud->usb_client_attach_list = kmem_zalloc( 6051c0f24e5bSlg child_ud->usb_n_ifs * 6052c0f24e5bSlg sizeof (*child_ud->usb_client_attach_list), KM_SLEEP); 60537c478bd9Sstevel@tonic-gate 60547c478bd9Sstevel@tonic-gate child_ud->usb_client_ev_cb_list = kmem_zalloc( 6055c0f24e5bSlg child_ud->usb_n_ifs * 6056c0f24e5bSlg sizeof (*child_ud->usb_client_ev_cb_list), KM_SLEEP); 60577c478bd9Sstevel@tonic-gate 60587c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 60597c478bd9Sstevel@tonic-gate 60607c478bd9Sstevel@tonic-gate /* ready the device node */ 60617c478bd9Sstevel@tonic-gate child_dip = usba_ready_device_node(child_dip); 60627c478bd9Sstevel@tonic-gate 60637c478bd9Sstevel@tonic-gate /* set owner of default pipe to child dip */ 60647c478bd9Sstevel@tonic-gate ph = usba_get_ph_data(def_ph); 60657c478bd9Sstevel@tonic-gate mutex_enter(&ph->p_mutex); 60667c478bd9Sstevel@tonic-gate mutex_enter(&ph->p_ph_impl->usba_ph_mutex); 60677c478bd9Sstevel@tonic-gate ph->p_ph_impl->usba_ph_dip = ph->p_dip = child_dip; 60687c478bd9Sstevel@tonic-gate mutex_exit(&ph->p_ph_impl->usba_ph_mutex); 60697c478bd9Sstevel@tonic-gate mutex_exit(&ph->p_mutex); 60707c478bd9Sstevel@tonic-gate 60717c478bd9Sstevel@tonic-gate return (child_dip); 60727c478bd9Sstevel@tonic-gate } 60737c478bd9Sstevel@tonic-gate 60747c478bd9Sstevel@tonic-gate /* 60757c478bd9Sstevel@tonic-gate * hubd_create_child 60767c478bd9Sstevel@tonic-gate * - create child dip 60777c478bd9Sstevel@tonic-gate * - open default pipe 60787c478bd9Sstevel@tonic-gate * - get device descriptor 60797c478bd9Sstevel@tonic-gate * - set the address 60807c478bd9Sstevel@tonic-gate * - get device string descriptors 60817c478bd9Sstevel@tonic-gate * - get the entire config cloud (all configurations) of the device 60827c478bd9Sstevel@tonic-gate * - set user preferred configuration 60837c478bd9Sstevel@tonic-gate * - close default pipe 60847c478bd9Sstevel@tonic-gate * - load appropriate driver(s) 60857c478bd9Sstevel@tonic-gate */ 60867c478bd9Sstevel@tonic-gate static int 60877c478bd9Sstevel@tonic-gate hubd_create_child(dev_info_t *dip, 6088*d96925c4SRichard Lowe hubd_t *hubd, 6089*d96925c4SRichard Lowe usba_device_t *hubd_ud, 6090*d96925c4SRichard Lowe usb_port_status_t port_status, 6091*d96925c4SRichard Lowe usb_port_t port, 6092*d96925c4SRichard Lowe int iteration) 60937c478bd9Sstevel@tonic-gate { 60947c478bd9Sstevel@tonic-gate dev_info_t *child_dip = NULL; 60957c478bd9Sstevel@tonic-gate usb_dev_descr_t usb_dev_descr; 60967c478bd9Sstevel@tonic-gate int rval; 60977c478bd9Sstevel@tonic-gate usba_device_t *child_ud = NULL; 60987c478bd9Sstevel@tonic-gate usba_device_t *parent_ud = NULL; 60997c478bd9Sstevel@tonic-gate usb_pipe_handle_t ph = NULL; /* default pipe handle */ 61007c478bd9Sstevel@tonic-gate mblk_t *pdata = NULL; 61017c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 610235f36846Ssl int user_conf_index; 610335f36846Ssl uint_t config_index; 61047c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 61057c478bd9Sstevel@tonic-gate uchar_t address = 0; 61067c478bd9Sstevel@tonic-gate uint16_t length; 61077c478bd9Sstevel@tonic-gate size_t size; 61087c478bd9Sstevel@tonic-gate usb_addr_t parent_usb_addr; 61097c478bd9Sstevel@tonic-gate usb_port_t parent_usb_port; 61107c478bd9Sstevel@tonic-gate usba_device_t *parent_usba_dev; 61117c478bd9Sstevel@tonic-gate usb_port_status_t parent_port_status; 6112993e3fafSRobert Mustacchi boolean_t hcd_called = B_FALSE; 61137c478bd9Sstevel@tonic-gate 61147c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 61157c478bd9Sstevel@tonic-gate "hubd_create_child: port=%d", port); 61167c478bd9Sstevel@tonic-gate 61177c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 61187c478bd9Sstevel@tonic-gate ASSERT(hubd->h_usba_devices[port] == NULL); 61197c478bd9Sstevel@tonic-gate 61207c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 61217c478bd9Sstevel@tonic-gate 61227c478bd9Sstevel@tonic-gate /* 61237c478bd9Sstevel@tonic-gate * create a dip which can be used to open the pipe. we set 61247c478bd9Sstevel@tonic-gate * the name after getting the descriptors from the device 61257c478bd9Sstevel@tonic-gate */ 61267c478bd9Sstevel@tonic-gate rval = usba_create_child_devi(dip, 6127c0f24e5bSlg "device", /* driver name */ 6128c0f24e5bSlg hubd_ud->usb_hcdi_ops, /* usba_hcdi ops */ 6129c0f24e5bSlg hubd_ud->usb_root_hub_dip, 6130c0f24e5bSlg port_status, /* low speed device */ 6131c0f24e5bSlg child_ud, 6132c0f24e5bSlg &child_dip); 61337c478bd9Sstevel@tonic-gate 61347c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 61357c478bd9Sstevel@tonic-gate 6136d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 61377c478bd9Sstevel@tonic-gate "usb_create_child_devi failed (%d)", rval); 61387c478bd9Sstevel@tonic-gate 61397c478bd9Sstevel@tonic-gate goto fail_cleanup; 61407c478bd9Sstevel@tonic-gate } 61417c478bd9Sstevel@tonic-gate 61427c478bd9Sstevel@tonic-gate child_ud = usba_get_usba_device(child_dip); 61437c478bd9Sstevel@tonic-gate ASSERT(child_ud != NULL); 61447c478bd9Sstevel@tonic-gate 61457c478bd9Sstevel@tonic-gate parent_ud = hubd->h_usba_device; 61467c478bd9Sstevel@tonic-gate mutex_enter(&parent_ud->usb_mutex); 61477c478bd9Sstevel@tonic-gate parent_port_status = parent_ud->usb_port_status; 61487c478bd9Sstevel@tonic-gate 61497c478bd9Sstevel@tonic-gate /* 6150993e3fafSRobert Mustacchi * To support split transactions, update address and port of high speed 6151993e3fafSRobert Mustacchi * hub to which given device is connected. Note, split transactions 6152993e3fafSRobert Mustacchi * only exist for high speed devices. 61537c478bd9Sstevel@tonic-gate */ 61547c478bd9Sstevel@tonic-gate if (parent_port_status == USBA_HIGH_SPEED_DEV) { 61557c478bd9Sstevel@tonic-gate parent_usba_dev = parent_ud; 61567c478bd9Sstevel@tonic-gate parent_usb_addr = parent_ud->usb_addr; 61577c478bd9Sstevel@tonic-gate parent_usb_port = port; 61587c478bd9Sstevel@tonic-gate } else { 61597c478bd9Sstevel@tonic-gate parent_usba_dev = parent_ud->usb_hs_hub_usba_dev; 61607c478bd9Sstevel@tonic-gate parent_usb_addr = parent_ud->usb_hs_hub_addr; 61617c478bd9Sstevel@tonic-gate parent_usb_port = parent_ud->usb_hs_hub_port; 61627c478bd9Sstevel@tonic-gate } 61637c478bd9Sstevel@tonic-gate mutex_exit(&parent_ud->usb_mutex); 61647c478bd9Sstevel@tonic-gate 61657c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 61667c478bd9Sstevel@tonic-gate address = child_ud->usb_addr; 61677c478bd9Sstevel@tonic-gate child_ud->usb_addr = 0; 61687c478bd9Sstevel@tonic-gate child_ud->usb_dev_descr = kmem_alloc(sizeof (usb_dev_descr_t), 61697c478bd9Sstevel@tonic-gate KM_SLEEP); 61707c478bd9Sstevel@tonic-gate bzero(&usb_dev_descr, sizeof (usb_dev_descr_t)); 6171993e3fafSRobert Mustacchi 6172993e3fafSRobert Mustacchi switch (port_status) { 6173993e3fafSRobert Mustacchi case USBA_SUPER_SPEED_DEV: 6174993e3fafSRobert Mustacchi usb_dev_descr.bMaxPacketSize0 = 9; 6175993e3fafSRobert Mustacchi break; 6176993e3fafSRobert Mustacchi case USBA_LOW_SPEED_DEV: 6177993e3fafSRobert Mustacchi usb_dev_descr.bMaxPacketSize0 = 8; 6178993e3fafSRobert Mustacchi break; 6179993e3fafSRobert Mustacchi default: 6180993e3fafSRobert Mustacchi usb_dev_descr.bMaxPacketSize0 = 64; 6181993e3fafSRobert Mustacchi break; 6182993e3fafSRobert Mustacchi } 61837c478bd9Sstevel@tonic-gate bcopy(&usb_dev_descr, child_ud->usb_dev_descr, 61847c478bd9Sstevel@tonic-gate sizeof (usb_dev_descr_t)); 61857c478bd9Sstevel@tonic-gate child_ud->usb_port = port; 6186993e3fafSRobert Mustacchi 6187993e3fafSRobert Mustacchi /* 6188993e3fafSRobert Mustacchi * The parent hub always keeps track of the hub this device is connected 6189993e3fafSRobert Mustacchi * to; however, the hs_hub_* variables are only keeping track of the 6190993e3fafSRobert Mustacchi * closest high speed hub. Unfortunately, we need both. 6191993e3fafSRobert Mustacchi */ 6192993e3fafSRobert Mustacchi child_ud->usb_parent_hub = parent_ud; 61937c478bd9Sstevel@tonic-gate child_ud->usb_hs_hub_usba_dev = parent_usba_dev; 61947c478bd9Sstevel@tonic-gate child_ud->usb_hs_hub_addr = parent_usb_addr; 61957c478bd9Sstevel@tonic-gate child_ud->usb_hs_hub_port = parent_usb_port; 61967c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 61977c478bd9Sstevel@tonic-gate 6198993e3fafSRobert Mustacchi /* 6199993e3fafSRobert Mustacchi * Before we open up the default pipe, give the HCD a chance to do 6200993e3fafSRobert Mustacchi * something here. 6201993e3fafSRobert Mustacchi */ 6202993e3fafSRobert Mustacchi if (child_ud->usb_hcdi_ops->usba_hcdi_device_init != NULL) { 6203993e3fafSRobert Mustacchi int rval; 6204993e3fafSRobert Mustacchi void *priv = NULL; 6205993e3fafSRobert Mustacchi 6206993e3fafSRobert Mustacchi rval = child_ud->usb_hcdi_ops->usba_hcdi_device_init(child_ud, 6207993e3fafSRobert Mustacchi port, &priv); 6208993e3fafSRobert Mustacchi if (rval != USB_SUCCESS) { 6209993e3fafSRobert Mustacchi USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 6210993e3fafSRobert Mustacchi "HCD usba_hcdi_Device_init failed (%d)", rval); 6211993e3fafSRobert Mustacchi goto fail_cleanup; 6212993e3fafSRobert Mustacchi } 6213993e3fafSRobert Mustacchi 6214993e3fafSRobert Mustacchi child_ud->usb_hcd_private = priv; 6215993e3fafSRobert Mustacchi hcd_called = B_TRUE; 6216993e3fafSRobert Mustacchi } 6217993e3fafSRobert Mustacchi 6218993e3fafSRobert Mustacchi 6219993e3fafSRobert Mustacchi 62207c478bd9Sstevel@tonic-gate /* Open the default pipe */ 62217c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_open(child_dip, NULL, NULL, 62227c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) != USB_SUCCESS) { 6223d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 6224d291d9f2Sfrits "usb_pipe_open failed (%d)", rval); 62257c478bd9Sstevel@tonic-gate 62267c478bd9Sstevel@tonic-gate goto fail_cleanup; 62277c478bd9Sstevel@tonic-gate } 62287c478bd9Sstevel@tonic-gate 62297c478bd9Sstevel@tonic-gate /* 62307c478bd9Sstevel@tonic-gate * get device descriptor 62317c478bd9Sstevel@tonic-gate */ 62327c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 62337c478bd9Sstevel@tonic-gate "hubd_create_child: get device descriptor: 64 bytes"); 62347c478bd9Sstevel@tonic-gate 62357c478bd9Sstevel@tonic-gate rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 62367c478bd9Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD, 62377c478bd9Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */ 62387c478bd9Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_DEV, /* wValue */ 62397c478bd9Sstevel@tonic-gate 0, /* wIndex */ 62407c478bd9Sstevel@tonic-gate 64, /* wLength */ 62417c478bd9Sstevel@tonic-gate &pdata, USB_ATTRS_SHORT_XFER_OK, 62427c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0); 62437c478bd9Sstevel@tonic-gate 6244993e3fafSRobert Mustacchi /* 6245993e3fafSRobert Mustacchi * If this is a full speed device, we cannot assume that its default 6246993e3fafSRobert Mustacchi * packet size is 64 bytes, it may be 8 bytes. 6247993e3fafSRobert Mustacchi */ 6248993e3fafSRobert Mustacchi 62497c478bd9Sstevel@tonic-gate if ((rval != USB_SUCCESS) && 62507c478bd9Sstevel@tonic-gate (!((completion_reason == USB_CR_DATA_OVERRUN) && pdata))) { 62517c478bd9Sstevel@tonic-gate 62527c478bd9Sstevel@tonic-gate /* 62537c478bd9Sstevel@tonic-gate * rval != USB_SUCCESS AND 62547c478bd9Sstevel@tonic-gate * completion_reason != USB_CR_DATA_OVERRUN 62557c478bd9Sstevel@tonic-gate * pdata could be != NULL. 62567c478bd9Sstevel@tonic-gate * Free pdata now to prevent memory leak. 62577c478bd9Sstevel@tonic-gate */ 62587c478bd9Sstevel@tonic-gate freemsg(pdata); 62597c478bd9Sstevel@tonic-gate pdata = NULL; 62607c478bd9Sstevel@tonic-gate 62617c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 62627c478bd9Sstevel@tonic-gate "hubd_create_child: get device descriptor: 8 bytes"); 62637c478bd9Sstevel@tonic-gate 62647c478bd9Sstevel@tonic-gate rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 62657c478bd9Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD, 62667c478bd9Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */ 62677c478bd9Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_DEV, /* wValue */ 62687c478bd9Sstevel@tonic-gate 0, /* wIndex */ 62697c478bd9Sstevel@tonic-gate 8, /* wLength */ 62707c478bd9Sstevel@tonic-gate &pdata, USB_ATTRS_NONE, 62717c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0); 62727c478bd9Sstevel@tonic-gate 62737c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 6274d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 62757c478bd9Sstevel@tonic-gate "getting device descriptor failed (%s 0x%x %d)", 62767c478bd9Sstevel@tonic-gate usb_str_cr(completion_reason), cb_flags, rval); 62777c478bd9Sstevel@tonic-gate goto fail_cleanup; 62787c478bd9Sstevel@tonic-gate } 62797c478bd9Sstevel@tonic-gate } else { 62807c478bd9Sstevel@tonic-gate ASSERT(completion_reason == USB_CR_OK); 62817c478bd9Sstevel@tonic-gate } 62827c478bd9Sstevel@tonic-gate 62837c478bd9Sstevel@tonic-gate ASSERT(pdata != NULL); 62847c478bd9Sstevel@tonic-gate 62857c478bd9Sstevel@tonic-gate size = usb_parse_dev_descr( 6286c0f24e5bSlg pdata->b_rptr, 6287d29f5a71Szhigang lu - Sun Microsystems - Beijing China MBLKL(pdata), 6288c0f24e5bSlg &usb_dev_descr, 6289c0f24e5bSlg sizeof (usb_dev_descr_t)); 62907c478bd9Sstevel@tonic-gate 62917c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 62927c478bd9Sstevel@tonic-gate "parsing device descriptor returned %lu", size); 62937c478bd9Sstevel@tonic-gate 62947c478bd9Sstevel@tonic-gate length = *(pdata->b_rptr); 62957c478bd9Sstevel@tonic-gate freemsg(pdata); 62967c478bd9Sstevel@tonic-gate pdata = NULL; 62977c478bd9Sstevel@tonic-gate if (size < 8) { 6298d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 62997c478bd9Sstevel@tonic-gate "get device descriptor returned %lu bytes", size); 63007c478bd9Sstevel@tonic-gate 63017c478bd9Sstevel@tonic-gate goto fail_cleanup; 63027c478bd9Sstevel@tonic-gate } 63037c478bd9Sstevel@tonic-gate 63047c478bd9Sstevel@tonic-gate if (length < 8) { 6305d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 63067c478bd9Sstevel@tonic-gate "fail enumeration: bLength=%d", length); 63077c478bd9Sstevel@tonic-gate 63087c478bd9Sstevel@tonic-gate goto fail_cleanup; 63097c478bd9Sstevel@tonic-gate } 63107c478bd9Sstevel@tonic-gate 6311993e3fafSRobert Mustacchi if (child_ud->usb_hcdi_ops->usba_hcdi_device_address != NULL) { 6312993e3fafSRobert Mustacchi rval = child_ud->usb_hcdi_ops->usba_hcdi_device_address( 6313993e3fafSRobert Mustacchi child_ud); 6314993e3fafSRobert Mustacchi if (rval != USB_SUCCESS) 6315993e3fafSRobert Mustacchi goto fail_cleanup; 6316993e3fafSRobert Mustacchi } else { 6317993e3fafSRobert Mustacchi /* Set the address of the device */ 6318993e3fafSRobert Mustacchi if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 6319993e3fafSRobert Mustacchi USB_DEV_REQ_HOST_TO_DEV, 6320993e3fafSRobert Mustacchi USB_REQ_SET_ADDRESS, /* bRequest */ 6321993e3fafSRobert Mustacchi address, /* wValue */ 6322993e3fafSRobert Mustacchi 0, /* wIndex */ 6323993e3fafSRobert Mustacchi 0, /* wLength */ 6324993e3fafSRobert Mustacchi NULL, 0, 6325993e3fafSRobert Mustacchi &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 6326993e3fafSRobert Mustacchi char buffer[64]; 6327993e3fafSRobert Mustacchi USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 6328993e3fafSRobert Mustacchi "setting address failed (cr=%s cb_flags=%s " 6329993e3fafSRobert Mustacchi "rval=%d)", usb_str_cr(completion_reason), 6330993e3fafSRobert Mustacchi usb_str_cb_flags(cb_flags, buffer, sizeof (buffer)), 6331993e3fafSRobert Mustacchi rval); 63327c478bd9Sstevel@tonic-gate 6333993e3fafSRobert Mustacchi goto fail_cleanup; 6334993e3fafSRobert Mustacchi } 63357c478bd9Sstevel@tonic-gate } 63367c478bd9Sstevel@tonic-gate 63377c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 63387c478bd9Sstevel@tonic-gate "set address 0x%x done", address); 63397c478bd9Sstevel@tonic-gate 63407c478bd9Sstevel@tonic-gate /* now close the pipe for addr 0 */ 63417c478bd9Sstevel@tonic-gate usb_pipe_close(child_dip, ph, 63427c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL); 63437c478bd9Sstevel@tonic-gate 63447c478bd9Sstevel@tonic-gate /* 63457c478bd9Sstevel@tonic-gate * This delay is important for the CATC hub to enumerate 63467c478bd9Sstevel@tonic-gate * But, avoid delay in the first iteration 63477c478bd9Sstevel@tonic-gate */ 63487c478bd9Sstevel@tonic-gate if (iteration) { 63497c478bd9Sstevel@tonic-gate delay(drv_usectohz(hubd_device_delay/100)); 63507c478bd9Sstevel@tonic-gate } 63517c478bd9Sstevel@tonic-gate 63527c478bd9Sstevel@tonic-gate /* assign the address in the usba_device structure */ 63537c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 63547c478bd9Sstevel@tonic-gate child_ud->usb_addr = address; 63557c478bd9Sstevel@tonic-gate child_ud->usb_no_cpr = 0; 63567c478bd9Sstevel@tonic-gate child_ud->usb_port_status = port_status; 63577c478bd9Sstevel@tonic-gate /* save this device descriptor */ 63587c478bd9Sstevel@tonic-gate bcopy(&usb_dev_descr, child_ud->usb_dev_descr, 6359c0f24e5bSlg sizeof (usb_dev_descr_t)); 63607c478bd9Sstevel@tonic-gate child_ud->usb_n_cfgs = usb_dev_descr.bNumConfigurations; 63617c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 63627c478bd9Sstevel@tonic-gate 63637c478bd9Sstevel@tonic-gate /* re-open the pipe for the device with the new address */ 63647c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_open(child_dip, NULL, NULL, 63657c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) != USB_SUCCESS) { 6366d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 63677c478bd9Sstevel@tonic-gate "usb_pipe_open failed (%d)", rval); 63687c478bd9Sstevel@tonic-gate 63697c478bd9Sstevel@tonic-gate goto fail_cleanup; 63707c478bd9Sstevel@tonic-gate } 63717c478bd9Sstevel@tonic-gate 63727c478bd9Sstevel@tonic-gate /* 63737c478bd9Sstevel@tonic-gate * Get full device descriptor only if we have not received full 63747c478bd9Sstevel@tonic-gate * device descriptor earlier. 63757c478bd9Sstevel@tonic-gate */ 63767c478bd9Sstevel@tonic-gate if (size < length) { 63777c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 63787c478bd9Sstevel@tonic-gate "hubd_create_child: get full device descriptor: " 63797c478bd9Sstevel@tonic-gate "%d bytes", length); 63807c478bd9Sstevel@tonic-gate 63817c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 63827c478bd9Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD, 63837c478bd9Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */ 63847c478bd9Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_DEV, /* wValue */ 63857c478bd9Sstevel@tonic-gate 0, /* wIndex */ 63867c478bd9Sstevel@tonic-gate length, /* wLength */ 63877c478bd9Sstevel@tonic-gate &pdata, 0, 63887c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 63897c478bd9Sstevel@tonic-gate freemsg(pdata); 63907c478bd9Sstevel@tonic-gate pdata = NULL; 63917c478bd9Sstevel@tonic-gate 63927c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, 63937c478bd9Sstevel@tonic-gate hubd->h_log_handle, 63947c478bd9Sstevel@tonic-gate "hubd_create_child: get full device descriptor: " 63957c478bd9Sstevel@tonic-gate "64 bytes"); 63967c478bd9Sstevel@tonic-gate 63977c478bd9Sstevel@tonic-gate rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 63987c478bd9Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | 63997c478bd9Sstevel@tonic-gate USB_DEV_REQ_TYPE_STANDARD, 64007c478bd9Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */ 64017c478bd9Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_DEV, /* wValue */ 64027c478bd9Sstevel@tonic-gate 0, /* wIndex */ 64037c478bd9Sstevel@tonic-gate 64, /* wLength */ 64047c478bd9Sstevel@tonic-gate &pdata, USB_ATTRS_SHORT_XFER_OK, 64057c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0); 64067c478bd9Sstevel@tonic-gate 64077c478bd9Sstevel@tonic-gate /* we have to trust the data now */ 64087c478bd9Sstevel@tonic-gate if (pdata) { 64097c478bd9Sstevel@tonic-gate int len = *(pdata->b_rptr); 64107c478bd9Sstevel@tonic-gate 6411d29f5a71Szhigang lu - Sun Microsystems - Beijing China length = MBLKL(pdata); 64127c478bd9Sstevel@tonic-gate if (length < len) { 64137c478bd9Sstevel@tonic-gate 64147c478bd9Sstevel@tonic-gate goto fail_cleanup; 64157c478bd9Sstevel@tonic-gate } 64167c478bd9Sstevel@tonic-gate } else if (rval != USB_SUCCESS) { 6417d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 64187c478bd9Sstevel@tonic-gate hubd->h_log_handle, 64197c478bd9Sstevel@tonic-gate "getting device descriptor failed " 64207c478bd9Sstevel@tonic-gate "(%d 0x%x %d)", 6421c0f24e5bSlg completion_reason, cb_flags, rval); 64227c478bd9Sstevel@tonic-gate 64237c478bd9Sstevel@tonic-gate goto fail_cleanup; 64247c478bd9Sstevel@tonic-gate } 64257c478bd9Sstevel@tonic-gate } 64267c478bd9Sstevel@tonic-gate 64277c478bd9Sstevel@tonic-gate size = usb_parse_dev_descr( 6428c0f24e5bSlg pdata->b_rptr, 6429d29f5a71Szhigang lu - Sun Microsystems - Beijing China MBLKL(pdata), 6430c0f24e5bSlg &usb_dev_descr, 6431c0f24e5bSlg sizeof (usb_dev_descr_t)); 64327c478bd9Sstevel@tonic-gate 64337c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 64347c478bd9Sstevel@tonic-gate "parsing device descriptor returned %lu", size); 64357c478bd9Sstevel@tonic-gate 64367c478bd9Sstevel@tonic-gate /* 64377c478bd9Sstevel@tonic-gate * For now, free the data 64387c478bd9Sstevel@tonic-gate * eventually, each configuration may need to be looked at 64397c478bd9Sstevel@tonic-gate */ 64407c478bd9Sstevel@tonic-gate freemsg(pdata); 64417c478bd9Sstevel@tonic-gate pdata = NULL; 64427c478bd9Sstevel@tonic-gate 64437c478bd9Sstevel@tonic-gate if (size != USB_DEV_DESCR_SIZE) { 6444d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 64457c478bd9Sstevel@tonic-gate "fail enumeration: descriptor size=%lu " 64467c478bd9Sstevel@tonic-gate "expected size=%u", size, USB_DEV_DESCR_SIZE); 64477c478bd9Sstevel@tonic-gate 64487c478bd9Sstevel@tonic-gate goto fail_cleanup; 64497c478bd9Sstevel@tonic-gate } 64507c478bd9Sstevel@tonic-gate 64517c478bd9Sstevel@tonic-gate /* 64527c478bd9Sstevel@tonic-gate * save the device descriptor in usba_device since it is needed 64537c478bd9Sstevel@tonic-gate * later on again 64547c478bd9Sstevel@tonic-gate */ 64557c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 64567c478bd9Sstevel@tonic-gate bcopy(&usb_dev_descr, child_ud->usb_dev_descr, 6457c0f24e5bSlg sizeof (usb_dev_descr_t)); 64587c478bd9Sstevel@tonic-gate child_ud->usb_n_cfgs = usb_dev_descr.bNumConfigurations; 64597c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 64607c478bd9Sstevel@tonic-gate } 64617c478bd9Sstevel@tonic-gate 64627c478bd9Sstevel@tonic-gate if (usb_dev_descr.bNumConfigurations == 0) { 6463d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 64647c478bd9Sstevel@tonic-gate "device descriptor:\n\t" 64657c478bd9Sstevel@tonic-gate "l=0x%x type=0x%x USB=0x%x class=0x%x subclass=0x%x\n\t" 64667c478bd9Sstevel@tonic-gate "protocol=0x%x maxpktsize=0x%x " 64677c478bd9Sstevel@tonic-gate "Vid=0x%x Pid=0x%x rel=0x%x\n\t" 64687c478bd9Sstevel@tonic-gate "Mfg=0x%x P=0x%x sn=0x%x #config=0x%x", 64697c478bd9Sstevel@tonic-gate usb_dev_descr.bLength, usb_dev_descr.bDescriptorType, 64707c478bd9Sstevel@tonic-gate usb_dev_descr.bcdUSB, usb_dev_descr.bDeviceClass, 64717c478bd9Sstevel@tonic-gate usb_dev_descr.bDeviceSubClass, 64727c478bd9Sstevel@tonic-gate usb_dev_descr.bDeviceProtocol, 64737c478bd9Sstevel@tonic-gate usb_dev_descr.bMaxPacketSize0, 64747c478bd9Sstevel@tonic-gate usb_dev_descr.idVendor, 64757c478bd9Sstevel@tonic-gate usb_dev_descr.idProduct, usb_dev_descr.bcdDevice, 64767c478bd9Sstevel@tonic-gate usb_dev_descr.iManufacturer, usb_dev_descr.iProduct, 64777c478bd9Sstevel@tonic-gate usb_dev_descr.iSerialNumber, 64787c478bd9Sstevel@tonic-gate usb_dev_descr.bNumConfigurations); 64797c478bd9Sstevel@tonic-gate goto fail_cleanup; 64807c478bd9Sstevel@tonic-gate } 64817c478bd9Sstevel@tonic-gate 64820d2006e4SRobert Mustacchi /* Read the BOS data */ 64830d2006e4SRobert Mustacchi usba_get_binary_object_store(child_dip, child_ud); 64847c478bd9Sstevel@tonic-gate 64857c478bd9Sstevel@tonic-gate /* get the device string descriptor(s) */ 64867c478bd9Sstevel@tonic-gate usba_get_dev_string_descrs(child_dip, child_ud); 64877c478bd9Sstevel@tonic-gate 64887c478bd9Sstevel@tonic-gate /* retrieve config cloud for all configurations */ 64897c478bd9Sstevel@tonic-gate rval = hubd_get_all_device_config_cloud(hubd, child_dip, child_ud); 64907c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 6491d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 64927c478bd9Sstevel@tonic-gate "failed to get configuration descriptor(s)"); 64937c478bd9Sstevel@tonic-gate 64947c478bd9Sstevel@tonic-gate goto fail_cleanup; 64957c478bd9Sstevel@tonic-gate } 64967c478bd9Sstevel@tonic-gate 64977c478bd9Sstevel@tonic-gate /* get the preferred configuration for this device */ 64987c478bd9Sstevel@tonic-gate user_conf_index = hubd_select_device_configuration(hubd, port, 64997c478bd9Sstevel@tonic-gate child_dip, child_ud); 65007c478bd9Sstevel@tonic-gate 65017c478bd9Sstevel@tonic-gate /* Check if the user selected configuration index is in range */ 650235f36846Ssl if ((user_conf_index >= usb_dev_descr.bNumConfigurations) || 650335f36846Ssl (user_conf_index < 0)) { 6504d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 65057c478bd9Sstevel@tonic-gate "Configuration index for device idVendor=%d " 65067c478bd9Sstevel@tonic-gate "idProduct=%d is=%d, and is out of range[0..%d]", 65077c478bd9Sstevel@tonic-gate usb_dev_descr.idVendor, usb_dev_descr.idProduct, 65087c478bd9Sstevel@tonic-gate user_conf_index, usb_dev_descr.bNumConfigurations - 1); 65097c478bd9Sstevel@tonic-gate 65107c478bd9Sstevel@tonic-gate /* treat this as user didn't specify configuration */ 65117c478bd9Sstevel@tonic-gate user_conf_index = USBA_DEV_CONFIG_INDEX_UNDEFINED; 65127c478bd9Sstevel@tonic-gate } 65137c478bd9Sstevel@tonic-gate 65147c478bd9Sstevel@tonic-gate 65157c478bd9Sstevel@tonic-gate /* 65167c478bd9Sstevel@tonic-gate * Warn users of a performance hit if connecting a 65177c478bd9Sstevel@tonic-gate * High Speed behind a 1.1 hub, which is behind a 6518993e3fafSRobert Mustacchi * 2.0 port. Don't worry about this for USB 3.x for now. 65197c478bd9Sstevel@tonic-gate */ 65207c478bd9Sstevel@tonic-gate if ((parent_port_status != USBA_HIGH_SPEED_DEV) && 65217c478bd9Sstevel@tonic-gate !(usba_is_root_hub(parent_ud->usb_dip)) && 65227c478bd9Sstevel@tonic-gate (parent_usb_addr)) { 65237c478bd9Sstevel@tonic-gate 65247c478bd9Sstevel@tonic-gate /* 65257c478bd9Sstevel@tonic-gate * Now that we know the root port is a high speed port 65267c478bd9Sstevel@tonic-gate * and that the parent port is not a high speed port, 65277c478bd9Sstevel@tonic-gate * let's find out if the device itself is a high speed 65287c478bd9Sstevel@tonic-gate * device. If it is a high speed device, 65297c478bd9Sstevel@tonic-gate * USB_DESCR_TYPE_SETUP_DEV_QLF should return a value, 65307c478bd9Sstevel@tonic-gate * otherwise the command will fail. 65317c478bd9Sstevel@tonic-gate */ 65327c478bd9Sstevel@tonic-gate rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 65337c478bd9Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD, 65347c478bd9Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */ 65357c478bd9Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_DEV_QLF, /* wValue */ 65367c478bd9Sstevel@tonic-gate 0, /* wIndex */ 65377c478bd9Sstevel@tonic-gate 10, /* wLength */ 65387c478bd9Sstevel@tonic-gate &pdata, USB_ATTRS_SHORT_XFER_OK, 65397c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0); 65407c478bd9Sstevel@tonic-gate 65417c478bd9Sstevel@tonic-gate if (pdata) { 65427c478bd9Sstevel@tonic-gate freemsg(pdata); 65437c478bd9Sstevel@tonic-gate pdata = NULL; 65447c478bd9Sstevel@tonic-gate } 65457c478bd9Sstevel@tonic-gate 65467c478bd9Sstevel@tonic-gate /* 65477c478bd9Sstevel@tonic-gate * USB_DESCR_TYPE_SETUP_DEV_QLF query was successful 65487c478bd9Sstevel@tonic-gate * that means this is a high speed device behind a 65497c478bd9Sstevel@tonic-gate * high speed root hub, but running at full speed 65507c478bd9Sstevel@tonic-gate * because there is a full speed hub in the middle. 65517c478bd9Sstevel@tonic-gate */ 65527c478bd9Sstevel@tonic-gate if (rval == USB_SUCCESS) { 6553c0f24e5bSlg USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, 6554c0f24e5bSlg hubd->h_log_handle, 6555c0f24e5bSlg "Connecting a high speed device to a " 6556c0f24e5bSlg "non high speed hub (port %d) will result " 6557112cd14aSqz "in a loss of performance. Please connect " 6558c0f24e5bSlg "the device to a high speed hub to get " 6559c0f24e5bSlg "the maximum performance.", 6560c0f24e5bSlg port); 65617c478bd9Sstevel@tonic-gate } 65627c478bd9Sstevel@tonic-gate } 65637c478bd9Sstevel@tonic-gate 65647c478bd9Sstevel@tonic-gate /* 65657c478bd9Sstevel@tonic-gate * Now we try to online the device by attaching a driver 65667c478bd9Sstevel@tonic-gate * The following truth table illustrates the logic:- 65677c478bd9Sstevel@tonic-gate * Cfgndx Driver Action 65687c478bd9Sstevel@tonic-gate * 0 0 loop all configs for driver with full 65697c478bd9Sstevel@tonic-gate * compatible properties. 65707c478bd9Sstevel@tonic-gate * 0 1 set first configuration, 65717c478bd9Sstevel@tonic-gate * compatible prop = drivername. 65727c478bd9Sstevel@tonic-gate * 1 0 Set config, full compatible prop 65737c478bd9Sstevel@tonic-gate * 1 1 Set config, compatible prop = drivername. 65747c478bd9Sstevel@tonic-gate * 65757c478bd9Sstevel@tonic-gate * Note: 65767c478bd9Sstevel@tonic-gate * cfgndx = user_conf_index 65777c478bd9Sstevel@tonic-gate * Driver = usb_preferred_driver 65787c478bd9Sstevel@tonic-gate */ 65797c478bd9Sstevel@tonic-gate if (user_conf_index == USBA_DEV_CONFIG_INDEX_UNDEFINED) { 65807c478bd9Sstevel@tonic-gate if (child_ud->usb_preferred_driver) { 65817c478bd9Sstevel@tonic-gate /* 65827c478bd9Sstevel@tonic-gate * It is the job of the "preferred driver" to put the 65837c478bd9Sstevel@tonic-gate * device in the desired configuration. Till then 65847c478bd9Sstevel@tonic-gate * put the device in config index 0. 65857c478bd9Sstevel@tonic-gate */ 658635f36846Ssl if ((rval = usba_hubdi_check_power_budget(dip, child_ud, 658735f36846Ssl USB_DEV_DEFAULT_CONFIG_INDEX)) != USB_SUCCESS) { 658835f36846Ssl 658935f36846Ssl goto fail_cleanup; 659035f36846Ssl } 659135f36846Ssl 65927c478bd9Sstevel@tonic-gate child_dip = hubd_ready_device(hubd, child_dip, 65937c478bd9Sstevel@tonic-gate child_ud, USB_DEV_DEFAULT_CONFIG_INDEX); 65947c478bd9Sstevel@tonic-gate 65957c478bd9Sstevel@tonic-gate /* 65967c478bd9Sstevel@tonic-gate * Assign the dip before onlining to avoid race 65977c478bd9Sstevel@tonic-gate * with busctl 65987c478bd9Sstevel@tonic-gate */ 65997c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 66007c478bd9Sstevel@tonic-gate hubd->h_children_dips[port] = child_dip; 66017c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 66027c478bd9Sstevel@tonic-gate 66037c478bd9Sstevel@tonic-gate (void) usba_bind_driver(child_dip); 66047c478bd9Sstevel@tonic-gate } else { 66057c478bd9Sstevel@tonic-gate /* 66067c478bd9Sstevel@tonic-gate * loop through all the configurations to see if we 66077c478bd9Sstevel@tonic-gate * can find a driver for any one config. If not, set 66087c478bd9Sstevel@tonic-gate * the device in config_index 0 66097c478bd9Sstevel@tonic-gate */ 66107c478bd9Sstevel@tonic-gate rval = USB_FAILURE; 66117c478bd9Sstevel@tonic-gate for (config_index = 0; 66127c478bd9Sstevel@tonic-gate (config_index < usb_dev_descr.bNumConfigurations) && 66137c478bd9Sstevel@tonic-gate (rval != USB_SUCCESS); config_index++) { 66147c478bd9Sstevel@tonic-gate 66157c478bd9Sstevel@tonic-gate child_dip = hubd_ready_device(hubd, child_dip, 66167c478bd9Sstevel@tonic-gate child_ud, config_index); 66177c478bd9Sstevel@tonic-gate 66187c478bd9Sstevel@tonic-gate /* 66197c478bd9Sstevel@tonic-gate * Assign the dip before onlining to avoid race 66207c478bd9Sstevel@tonic-gate * with busctl 66217c478bd9Sstevel@tonic-gate */ 66227c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 66237c478bd9Sstevel@tonic-gate hubd->h_children_dips[port] = child_dip; 66247c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 66257c478bd9Sstevel@tonic-gate 66267c478bd9Sstevel@tonic-gate rval = usba_bind_driver(child_dip); 662735f36846Ssl 662835f36846Ssl /* 662935f36846Ssl * Normally power budget should be checked 663035f36846Ssl * before device is configured. A failure in 663135f36846Ssl * power budget checking will stop the device 663235f36846Ssl * from being configured with current 663335f36846Ssl * config_index and may enable the device to 663435f36846Ssl * be configured in another configuration. 663535f36846Ssl * This may break the user experience that a 663635f36846Ssl * device which previously worked in config 663735f36846Ssl * A now works in config B after power budget 663835f36846Ssl * control is enabled. To avoid such situation, 663935f36846Ssl * power budget checking is moved here and will 664035f36846Ssl * fail the child creation directly if config 664135f36846Ssl * A exceeds the power available. 664235f36846Ssl */ 664335f36846Ssl if (rval == USB_SUCCESS) { 664435f36846Ssl if ((usba_hubdi_check_power_budget(dip, 664535f36846Ssl child_ud, config_index)) != 664635f36846Ssl USB_SUCCESS) { 664735f36846Ssl 664835f36846Ssl goto fail_cleanup; 664935f36846Ssl } 665035f36846Ssl } 66517c478bd9Sstevel@tonic-gate } 66527c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 665335f36846Ssl 665435f36846Ssl if ((usba_hubdi_check_power_budget(dip, 665535f36846Ssl child_ud, 0)) != USB_SUCCESS) { 665635f36846Ssl 665735f36846Ssl goto fail_cleanup; 665835f36846Ssl } 665935f36846Ssl 66607c478bd9Sstevel@tonic-gate child_dip = hubd_ready_device(hubd, child_dip, 66617c478bd9Sstevel@tonic-gate child_ud, 0); 66627c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 66637c478bd9Sstevel@tonic-gate hubd->h_children_dips[port] = child_dip; 66647c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 66657c478bd9Sstevel@tonic-gate } 66667c478bd9Sstevel@tonic-gate } /* end else loop all configs */ 66677c478bd9Sstevel@tonic-gate } else { 666835f36846Ssl 666935f36846Ssl if ((usba_hubdi_check_power_budget(dip, child_ud, 667035f36846Ssl (uint_t)user_conf_index)) != USB_SUCCESS) { 667135f36846Ssl 667235f36846Ssl goto fail_cleanup; 667335f36846Ssl } 667435f36846Ssl 66757c478bd9Sstevel@tonic-gate child_dip = hubd_ready_device(hubd, child_dip, 667635f36846Ssl child_ud, (uint_t)user_conf_index); 66777c478bd9Sstevel@tonic-gate 66787c478bd9Sstevel@tonic-gate /* 66797c478bd9Sstevel@tonic-gate * Assign the dip before onlining to avoid race 66807c478bd9Sstevel@tonic-gate * with busctl 66817c478bd9Sstevel@tonic-gate */ 66827c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 66837c478bd9Sstevel@tonic-gate hubd->h_children_dips[port] = child_dip; 66847c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 66857c478bd9Sstevel@tonic-gate 66867c478bd9Sstevel@tonic-gate (void) usba_bind_driver(child_dip); 66877c478bd9Sstevel@tonic-gate } 66887c478bd9Sstevel@tonic-gate 668935f36846Ssl usba_hubdi_decr_power_budget(dip, child_ud); 669035f36846Ssl 66917c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 66927c478bd9Sstevel@tonic-gate if (hubd->h_usba_devices[port] == NULL) { 66937c478bd9Sstevel@tonic-gate hubd->h_usba_devices[port] = usba_get_usba_device(child_dip); 66947c478bd9Sstevel@tonic-gate } else { 66957c478bd9Sstevel@tonic-gate ASSERT(hubd->h_usba_devices[port] == 6696c0f24e5bSlg usba_get_usba_device(child_dip)); 66977c478bd9Sstevel@tonic-gate } 66987c478bd9Sstevel@tonic-gate 66997c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 67007c478bd9Sstevel@tonic-gate 67017c478bd9Sstevel@tonic-gate 67027c478bd9Sstevel@tonic-gate fail_cleanup: 67037c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 67047c478bd9Sstevel@tonic-gate "hubd_create_child: fail_cleanup"); 67057c478bd9Sstevel@tonic-gate 67067c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 67077c478bd9Sstevel@tonic-gate hubd->h_children_dips[port] = NULL; 67087c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 67097c478bd9Sstevel@tonic-gate 67107c478bd9Sstevel@tonic-gate if (pdata) { 67117c478bd9Sstevel@tonic-gate freemsg(pdata); 67127c478bd9Sstevel@tonic-gate } 67137c478bd9Sstevel@tonic-gate 67147c478bd9Sstevel@tonic-gate if (ph) { 67157c478bd9Sstevel@tonic-gate usb_pipe_close(child_dip, ph, 67167c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL); 67177c478bd9Sstevel@tonic-gate } 67187c478bd9Sstevel@tonic-gate 6719993e3fafSRobert Mustacchi if (child_ud != NULL && hcd_called == B_TRUE && 6720993e3fafSRobert Mustacchi child_ud->usb_hcdi_ops->usba_hcdi_device_fini != NULL) { 6721993e3fafSRobert Mustacchi child_ud->usb_hcdi_ops->usba_hcdi_device_fini(child_ud, 6722993e3fafSRobert Mustacchi child_ud->usb_hcd_private); 6723993e3fafSRobert Mustacchi child_ud->usb_hcd_private = NULL; 6724993e3fafSRobert Mustacchi } 6725993e3fafSRobert Mustacchi 6726993e3fafSRobert Mustacchi 67277c478bd9Sstevel@tonic-gate if (child_dip) { 67287c478bd9Sstevel@tonic-gate int rval = usba_destroy_child_devi(child_dip, 67297c478bd9Sstevel@tonic-gate NDI_DEVI_REMOVE); 67307c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 6731d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 67327c478bd9Sstevel@tonic-gate "failure to remove child node"); 67337c478bd9Sstevel@tonic-gate } 67347c478bd9Sstevel@tonic-gate } 67357c478bd9Sstevel@tonic-gate 67367c478bd9Sstevel@tonic-gate if (child_ud) { 67377c478bd9Sstevel@tonic-gate /* to make sure we free the address */ 67387c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 67397c478bd9Sstevel@tonic-gate child_ud->usb_addr = address; 67407c478bd9Sstevel@tonic-gate ASSERT(child_ud->usb_ref_count == 0); 67417c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 67427c478bd9Sstevel@tonic-gate 67437c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 67447c478bd9Sstevel@tonic-gate if (hubd->h_usba_devices[port] == NULL) { 67457c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 67467c478bd9Sstevel@tonic-gate usba_free_usba_device(child_ud); 67477c478bd9Sstevel@tonic-gate } else { 67487c478bd9Sstevel@tonic-gate hubd_free_usba_device(hubd, hubd->h_usba_devices[port]); 67497c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 67507c478bd9Sstevel@tonic-gate } 67517c478bd9Sstevel@tonic-gate } 67527c478bd9Sstevel@tonic-gate 67537c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 67547c478bd9Sstevel@tonic-gate 67557c478bd9Sstevel@tonic-gate return (USB_FAILURE); 67567c478bd9Sstevel@tonic-gate } 67577c478bd9Sstevel@tonic-gate 67587c478bd9Sstevel@tonic-gate 67597c478bd9Sstevel@tonic-gate /* 67607c478bd9Sstevel@tonic-gate * hubd_delete_child: 67617c478bd9Sstevel@tonic-gate * - free usb address 67627c478bd9Sstevel@tonic-gate * - lookup child dips, there may be multiple on this port 67637c478bd9Sstevel@tonic-gate * - offline each child devi 67647c478bd9Sstevel@tonic-gate */ 67657c478bd9Sstevel@tonic-gate static int 67667c478bd9Sstevel@tonic-gate hubd_delete_child(hubd_t *hubd, usb_port_t port, uint_t flag, boolean_t retry) 67677c478bd9Sstevel@tonic-gate { 67687c478bd9Sstevel@tonic-gate dev_info_t *child_dip; 67697c478bd9Sstevel@tonic-gate usba_device_t *usba_device; 67707c478bd9Sstevel@tonic-gate int rval = USB_SUCCESS; 67717c478bd9Sstevel@tonic-gate 67727c478bd9Sstevel@tonic-gate child_dip = hubd->h_children_dips[port]; 677335f36846Ssl usba_device = hubd->h_usba_devices[port]; 67747c478bd9Sstevel@tonic-gate 67757c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 67767c478bd9Sstevel@tonic-gate "hubd_delete_child: port=%d, dip=0x%p usba_device=0x%p", 6777112116d8Sfb port, (void *)child_dip, (void *)usba_device); 67787c478bd9Sstevel@tonic-gate 67797c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 67807c478bd9Sstevel@tonic-gate if (child_dip) { 67817c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 67827c478bd9Sstevel@tonic-gate "hubd_delete_child:\n\t" 67837c478bd9Sstevel@tonic-gate "dip = 0x%p (%s) at port %d", 6784112116d8Sfb (void *)child_dip, ddi_node_name(child_dip), port); 67857c478bd9Sstevel@tonic-gate 678635f36846Ssl if (usba_device) { 678735f36846Ssl usba_hubdi_incr_power_budget(hubd->h_dip, usba_device); 678835f36846Ssl } 678935f36846Ssl 6790993e3fafSRobert Mustacchi 67917c478bd9Sstevel@tonic-gate rval = usba_destroy_child_devi(child_dip, flag); 67927c478bd9Sstevel@tonic-gate 67937c478bd9Sstevel@tonic-gate if ((rval == USB_SUCCESS) && (flag & NDI_DEVI_REMOVE)) { 67947c478bd9Sstevel@tonic-gate /* 67957c478bd9Sstevel@tonic-gate * if the child was still < DS_INITIALIZED 67967c478bd9Sstevel@tonic-gate * then our bus_unconfig was not called and 67977c478bd9Sstevel@tonic-gate * we have to zap the child here 67987c478bd9Sstevel@tonic-gate */ 67997c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 68007c478bd9Sstevel@tonic-gate if (hubd->h_children_dips[port] == child_dip) { 68017c478bd9Sstevel@tonic-gate usba_device_t *ud = 6802c0f24e5bSlg hubd->h_usba_devices[port]; 680315c07adcSJohn Levon hubd->h_children_dips[port] = NULL; 68047c478bd9Sstevel@tonic-gate if (ud) { 68057c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 68067c478bd9Sstevel@tonic-gate 68077c478bd9Sstevel@tonic-gate mutex_enter(&ud->usb_mutex); 68087c478bd9Sstevel@tonic-gate ud->usb_ref_count = 0; 68097c478bd9Sstevel@tonic-gate mutex_exit(&ud->usb_mutex); 68107c478bd9Sstevel@tonic-gate 68117c478bd9Sstevel@tonic-gate usba_free_usba_device(ud); 68127c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 68137c478bd9Sstevel@tonic-gate hubd->h_usba_devices[port] = NULL; 68147c478bd9Sstevel@tonic-gate } 68157c478bd9Sstevel@tonic-gate } 68167c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 68177c478bd9Sstevel@tonic-gate } 68187c478bd9Sstevel@tonic-gate } 68197c478bd9Sstevel@tonic-gate 68207c478bd9Sstevel@tonic-gate if ((rval != USB_SUCCESS) && retry) { 68217c478bd9Sstevel@tonic-gate 68227c478bd9Sstevel@tonic-gate hubd_schedule_cleanup(usba_device->usb_root_hub_dip); 68237c478bd9Sstevel@tonic-gate } 68247c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 68257c478bd9Sstevel@tonic-gate 68267c478bd9Sstevel@tonic-gate return (rval); 68277c478bd9Sstevel@tonic-gate } 68287c478bd9Sstevel@tonic-gate 68297c478bd9Sstevel@tonic-gate 68307c478bd9Sstevel@tonic-gate /* 68317c478bd9Sstevel@tonic-gate * hubd_free_usba_device: 68327c478bd9Sstevel@tonic-gate * free usb device structure unless it is associated with 68337c478bd9Sstevel@tonic-gate * the root hub which is handled differently 68347c478bd9Sstevel@tonic-gate */ 68357c478bd9Sstevel@tonic-gate static void 68367c478bd9Sstevel@tonic-gate hubd_free_usba_device(hubd_t *hubd, usba_device_t *usba_device) 68377c478bd9Sstevel@tonic-gate { 68387c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 68397c478bd9Sstevel@tonic-gate "hubd_free_usba_device: hubd=0x%p, usba_device=0x%p", 6840112116d8Sfb (void *)hubd, (void *)usba_device); 68417c478bd9Sstevel@tonic-gate 68427c478bd9Sstevel@tonic-gate if (usba_device && (usba_device->usb_addr != ROOT_HUB_ADDR)) { 68437c478bd9Sstevel@tonic-gate usb_port_t port = usba_device->usb_port; 68447c478bd9Sstevel@tonic-gate dev_info_t *dip = hubd->h_children_dips[port]; 68457c478bd9Sstevel@tonic-gate 68467c478bd9Sstevel@tonic-gate #ifdef DEBUG 68477c478bd9Sstevel@tonic-gate if (dip) { 68487c478bd9Sstevel@tonic-gate ASSERT(i_ddi_node_state(dip) < DS_INITIALIZED); 68497c478bd9Sstevel@tonic-gate } 68507c478bd9Sstevel@tonic-gate #endif 68517c478bd9Sstevel@tonic-gate port = usba_device->usb_port; 68527c478bd9Sstevel@tonic-gate hubd->h_usba_devices[port] = NULL; 68537c478bd9Sstevel@tonic-gate 68547c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 68557c478bd9Sstevel@tonic-gate usba_free_usba_device(usba_device); 68567c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 68577c478bd9Sstevel@tonic-gate } 68587c478bd9Sstevel@tonic-gate } 68597c478bd9Sstevel@tonic-gate 68607c478bd9Sstevel@tonic-gate 68617c478bd9Sstevel@tonic-gate /* 68627c478bd9Sstevel@tonic-gate * event support 68637c478bd9Sstevel@tonic-gate * 68647c478bd9Sstevel@tonic-gate * busctl event support 68657c478bd9Sstevel@tonic-gate */ 68667c478bd9Sstevel@tonic-gate static int 68677c478bd9Sstevel@tonic-gate hubd_busop_get_eventcookie(dev_info_t *dip, 6868*d96925c4SRichard Lowe dev_info_t *rdip, 6869*d96925c4SRichard Lowe char *eventname, 6870*d96925c4SRichard Lowe ddi_eventcookie_t *cookie) 68717c478bd9Sstevel@tonic-gate { 68727c478bd9Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip); 68737c478bd9Sstevel@tonic-gate 68747c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 68757c478bd9Sstevel@tonic-gate "hubd_busop_get_eventcookie: dip=0x%p, rdip=0x%p, " 68767c478bd9Sstevel@tonic-gate "event=%s", (void *)dip, (void *)rdip, eventname); 68777c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 68787c478bd9Sstevel@tonic-gate "(dip=%s%d, rdip=%s%d)", 68797c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), 68807c478bd9Sstevel@tonic-gate ddi_driver_name(rdip), ddi_get_instance(rdip)); 68817c478bd9Sstevel@tonic-gate 68827c478bd9Sstevel@tonic-gate /* return event cookie, iblock cookie, and level */ 68837c478bd9Sstevel@tonic-gate return (ndi_event_retrieve_cookie(hubd->h_ndi_event_hdl, 6884c0f24e5bSlg rdip, eventname, cookie, NDI_EVENT_NOPASS)); 68857c478bd9Sstevel@tonic-gate } 68867c478bd9Sstevel@tonic-gate 68877c478bd9Sstevel@tonic-gate 68887c478bd9Sstevel@tonic-gate static int 68897c478bd9Sstevel@tonic-gate hubd_busop_add_eventcall(dev_info_t *dip, 6890*d96925c4SRichard Lowe dev_info_t *rdip, 6891*d96925c4SRichard Lowe ddi_eventcookie_t cookie, 6892*d96925c4SRichard Lowe void (*callback)(dev_info_t *dip, ddi_eventcookie_t cookie, void *arg, 6893*d96925c4SRichard Lowe void *bus_impldata), 6894*d96925c4SRichard Lowe void *arg, ddi_callback_id_t *cb_id) 68957c478bd9Sstevel@tonic-gate { 68967c478bd9Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip); 68977c478bd9Sstevel@tonic-gate usb_port_t port = hubd_child_dip2port(hubd, rdip); 68987c478bd9Sstevel@tonic-gate 68997c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 69007c478bd9Sstevel@tonic-gate "hubd_busop_add_eventcall: dip=0x%p, rdip=0x%p " 69017c478bd9Sstevel@tonic-gate "cookie=0x%p, cb=0x%p, arg=0x%p", 69027c478bd9Sstevel@tonic-gate (void *)dip, (void *)rdip, (void *)cookie, (void *)callback, arg); 69037c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 69047c478bd9Sstevel@tonic-gate "(dip=%s%d, rdip=%s%d, event=%s)", 69057c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), 69067c478bd9Sstevel@tonic-gate ddi_driver_name(rdip), ddi_get_instance(rdip), 69077c478bd9Sstevel@tonic-gate ndi_event_cookie_to_name(hubd->h_ndi_event_hdl, cookie)); 69087c478bd9Sstevel@tonic-gate 69097c478bd9Sstevel@tonic-gate /* Set flag on children registering events */ 69107c478bd9Sstevel@tonic-gate switch (ndi_event_cookie_to_tag(hubd->h_ndi_event_hdl, cookie)) { 69117c478bd9Sstevel@tonic-gate case USBA_EVENT_TAG_HOT_REMOVAL: 69127c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 69137c478bd9Sstevel@tonic-gate hubd->h_child_events[port] |= HUBD_CHILD_EVENT_DISCONNECT; 69147c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 69157c478bd9Sstevel@tonic-gate 69167c478bd9Sstevel@tonic-gate break; 69177c478bd9Sstevel@tonic-gate case USBA_EVENT_TAG_PRE_SUSPEND: 69187c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 69197c478bd9Sstevel@tonic-gate hubd->h_child_events[port] |= HUBD_CHILD_EVENT_PRESUSPEND; 69207c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 69217c478bd9Sstevel@tonic-gate 69227c478bd9Sstevel@tonic-gate break; 69237c478bd9Sstevel@tonic-gate default: 69247c478bd9Sstevel@tonic-gate 69257c478bd9Sstevel@tonic-gate break; 69267c478bd9Sstevel@tonic-gate } 69277c478bd9Sstevel@tonic-gate 69287c478bd9Sstevel@tonic-gate /* add callback to our event set */ 69297c478bd9Sstevel@tonic-gate return (ndi_event_add_callback(hubd->h_ndi_event_hdl, 6930c0f24e5bSlg rdip, cookie, callback, arg, NDI_SLEEP, cb_id)); 69317c478bd9Sstevel@tonic-gate } 69327c478bd9Sstevel@tonic-gate 69337c478bd9Sstevel@tonic-gate 69347c478bd9Sstevel@tonic-gate static int 69357c478bd9Sstevel@tonic-gate hubd_busop_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id) 69367c478bd9Sstevel@tonic-gate { 69377c478bd9Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip); 69387c478bd9Sstevel@tonic-gate ndi_event_callbacks_t *id = (ndi_event_callbacks_t *)cb_id; 69397c478bd9Sstevel@tonic-gate 69407c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 69417c478bd9Sstevel@tonic-gate "hubd_busop_remove_eventcall: dip=0x%p, rdip=0x%p " 6942112116d8Sfb "cookie=0x%p", (void *)dip, (void *)id->ndi_evtcb_dip, 6943112116d8Sfb (void *)id->ndi_evtcb_cookie); 69447c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 69457c478bd9Sstevel@tonic-gate "(dip=%s%d, rdip=%s%d, event=%s)", 69467c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), 69477c478bd9Sstevel@tonic-gate ddi_driver_name(id->ndi_evtcb_dip), 69487c478bd9Sstevel@tonic-gate ddi_get_instance(id->ndi_evtcb_dip), 69497c478bd9Sstevel@tonic-gate ndi_event_cookie_to_name(hubd->h_ndi_event_hdl, 69507c478bd9Sstevel@tonic-gate id->ndi_evtcb_cookie)); 69517c478bd9Sstevel@tonic-gate 69527c478bd9Sstevel@tonic-gate /* remove event registration from our event set */ 69537c478bd9Sstevel@tonic-gate return (ndi_event_remove_callback(hubd->h_ndi_event_hdl, cb_id)); 69547c478bd9Sstevel@tonic-gate } 69557c478bd9Sstevel@tonic-gate 69567c478bd9Sstevel@tonic-gate 69577c478bd9Sstevel@tonic-gate /* 69587c478bd9Sstevel@tonic-gate * event distribution 69597c478bd9Sstevel@tonic-gate * 69607c478bd9Sstevel@tonic-gate * hubd_do_callback: 69617c478bd9Sstevel@tonic-gate * Post this event to the specified child 69627c478bd9Sstevel@tonic-gate */ 69637c478bd9Sstevel@tonic-gate static void 69647c478bd9Sstevel@tonic-gate hubd_do_callback(hubd_t *hubd, dev_info_t *cdip, ddi_eventcookie_t cookie) 69657c478bd9Sstevel@tonic-gate { 69667c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 69677c478bd9Sstevel@tonic-gate "hubd_do_callback"); 69687c478bd9Sstevel@tonic-gate 69697c478bd9Sstevel@tonic-gate (void) ndi_event_do_callback(hubd->h_ndi_event_hdl, cdip, cookie, NULL); 69707c478bd9Sstevel@tonic-gate } 69717c478bd9Sstevel@tonic-gate 69727c478bd9Sstevel@tonic-gate 69737c478bd9Sstevel@tonic-gate /* 69747c478bd9Sstevel@tonic-gate * hubd_run_callbacks: 69757c478bd9Sstevel@tonic-gate * Send this event to all children 69767c478bd9Sstevel@tonic-gate */ 69777c478bd9Sstevel@tonic-gate static void 69787c478bd9Sstevel@tonic-gate hubd_run_callbacks(hubd_t *hubd, usba_event_t type) 69797c478bd9Sstevel@tonic-gate { 69807c478bd9Sstevel@tonic-gate usb_port_t port; 69817c478bd9Sstevel@tonic-gate 69827c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 69837c478bd9Sstevel@tonic-gate "hubd_run_callbacks"); 69847c478bd9Sstevel@tonic-gate 69857c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6986993e3fafSRobert Mustacchi for (port = 1; port <= hubd->h_nports; port++) { 69877c478bd9Sstevel@tonic-gate /* 69887c478bd9Sstevel@tonic-gate * the childen_dips list may have dips that have been 69897c478bd9Sstevel@tonic-gate * already deallocated. we only get a post_detach notification 69907c478bd9Sstevel@tonic-gate * but not a destroy notification 69917c478bd9Sstevel@tonic-gate */ 69927c478bd9Sstevel@tonic-gate if (hubd->h_children_dips[port]) { 69937c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 69947c478bd9Sstevel@tonic-gate hubd_post_event(hubd, port, type); 69957c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 69967c478bd9Sstevel@tonic-gate } 69977c478bd9Sstevel@tonic-gate } 69987c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 69997c478bd9Sstevel@tonic-gate } 70007c478bd9Sstevel@tonic-gate 70017c478bd9Sstevel@tonic-gate 70027c478bd9Sstevel@tonic-gate /* 70037c478bd9Sstevel@tonic-gate * hubd_post_event 70047c478bd9Sstevel@tonic-gate * post event to a child on the port depending on the type 70057c478bd9Sstevel@tonic-gate */ 70067c478bd9Sstevel@tonic-gate static void 70077c478bd9Sstevel@tonic-gate hubd_post_event(hubd_t *hubd, usb_port_t port, usba_event_t type) 70087c478bd9Sstevel@tonic-gate { 70097c478bd9Sstevel@tonic-gate int rval; 70107c478bd9Sstevel@tonic-gate dev_info_t *dip; 70117c478bd9Sstevel@tonic-gate usba_device_t *usba_device; 70127c478bd9Sstevel@tonic-gate ddi_eventcookie_t cookie, rm_cookie, suspend_cookie; 70137c478bd9Sstevel@tonic-gate 70147c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 70157c478bd9Sstevel@tonic-gate "hubd_post_event: port=%d event=%s", port, 70167c478bd9Sstevel@tonic-gate ndi_event_tag_to_name(hubd->h_ndi_event_hdl, type)); 70177c478bd9Sstevel@tonic-gate 70187c478bd9Sstevel@tonic-gate cookie = ndi_event_tag_to_cookie(hubd->h_ndi_event_hdl, type); 70197c478bd9Sstevel@tonic-gate rm_cookie = ndi_event_tag_to_cookie(hubd->h_ndi_event_hdl, 70207c478bd9Sstevel@tonic-gate USBA_EVENT_TAG_HOT_REMOVAL); 70217c478bd9Sstevel@tonic-gate suspend_cookie = ndi_event_tag_to_cookie(hubd->h_ndi_event_hdl, 70227c478bd9Sstevel@tonic-gate USBA_EVENT_TAG_PRE_SUSPEND); 70237c478bd9Sstevel@tonic-gate 70247c478bd9Sstevel@tonic-gate /* 70257c478bd9Sstevel@tonic-gate * Hotplug daemon may be attaching a driver that may be registering 70267c478bd9Sstevel@tonic-gate * event callbacks. So it already has got the device tree lock and 70277c478bd9Sstevel@tonic-gate * event handle mutex. So to prevent a deadlock while posting events, 70287c478bd9Sstevel@tonic-gate * we grab and release the locks in the same order. 70297c478bd9Sstevel@tonic-gate */ 70307c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 70317c478bd9Sstevel@tonic-gate dip = hubd->h_children_dips[port]; 70327c478bd9Sstevel@tonic-gate usba_device = hubd->h_usba_devices[port]; 70337c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 70347c478bd9Sstevel@tonic-gate 70357c478bd9Sstevel@tonic-gate switch (type) { 70367c478bd9Sstevel@tonic-gate case USBA_EVENT_TAG_HOT_REMOVAL: 70377c478bd9Sstevel@tonic-gate /* Clear the registered event flag */ 70387c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 70397c478bd9Sstevel@tonic-gate hubd->h_child_events[port] &= ~HUBD_CHILD_EVENT_DISCONNECT; 70407c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 70417c478bd9Sstevel@tonic-gate 70427c478bd9Sstevel@tonic-gate hubd_do_callback(hubd, dip, cookie); 70437c478bd9Sstevel@tonic-gate usba_persistent_pipe_close(usba_device); 70447c478bd9Sstevel@tonic-gate 70457c478bd9Sstevel@tonic-gate /* 70467c478bd9Sstevel@tonic-gate * Mark the dip for deletion only after the driver has 70477c478bd9Sstevel@tonic-gate * seen the disconnect event to prevent cleanup thread 70487c478bd9Sstevel@tonic-gate * from stepping in between. 70497c478bd9Sstevel@tonic-gate */ 705016747f41Scth mutex_enter(&(DEVI(dip)->devi_lock)); 70517c478bd9Sstevel@tonic-gate DEVI_SET_DEVICE_REMOVED(dip); 705216747f41Scth mutex_exit(&(DEVI(dip)->devi_lock)); 70537c478bd9Sstevel@tonic-gate 70547c478bd9Sstevel@tonic-gate break; 70557c478bd9Sstevel@tonic-gate case USBA_EVENT_TAG_PRE_SUSPEND: 70567c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 70577c478bd9Sstevel@tonic-gate hubd->h_child_events[port] &= ~HUBD_CHILD_EVENT_PRESUSPEND; 70587c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 70597c478bd9Sstevel@tonic-gate 70607c478bd9Sstevel@tonic-gate hubd_do_callback(hubd, dip, cookie); 70617c478bd9Sstevel@tonic-gate /* 70627c478bd9Sstevel@tonic-gate * persistent pipe close for this event is taken care by the 70637c478bd9Sstevel@tonic-gate * caller after verfying that all children can suspend 70647c478bd9Sstevel@tonic-gate */ 70657c478bd9Sstevel@tonic-gate 70667c478bd9Sstevel@tonic-gate break; 70677c478bd9Sstevel@tonic-gate case USBA_EVENT_TAG_HOT_INSERTION: 70687c478bd9Sstevel@tonic-gate /* 70697c478bd9Sstevel@tonic-gate * Check if this child has missed the disconnect event before 70707c478bd9Sstevel@tonic-gate * it registered for event callbacks 70717c478bd9Sstevel@tonic-gate */ 70727c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 70737c478bd9Sstevel@tonic-gate if (hubd->h_child_events[port] & HUBD_CHILD_EVENT_DISCONNECT) { 70747c478bd9Sstevel@tonic-gate /* clear the flag and post disconnect event */ 70757c478bd9Sstevel@tonic-gate hubd->h_child_events[port] &= 70767c478bd9Sstevel@tonic-gate ~HUBD_CHILD_EVENT_DISCONNECT; 70777c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 70787c478bd9Sstevel@tonic-gate hubd_do_callback(hubd, dip, rm_cookie); 70797c478bd9Sstevel@tonic-gate usba_persistent_pipe_close(usba_device); 70807c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 70817c478bd9Sstevel@tonic-gate } 70827c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 70837c478bd9Sstevel@tonic-gate 70847c478bd9Sstevel@tonic-gate /* 70857c478bd9Sstevel@tonic-gate * Mark the dip as reinserted to prevent cleanup thread 70867c478bd9Sstevel@tonic-gate * from stepping in. 70877c478bd9Sstevel@tonic-gate */ 708816747f41Scth mutex_enter(&(DEVI(dip)->devi_lock)); 70897c478bd9Sstevel@tonic-gate DEVI_SET_DEVICE_REINSERTED(dip); 709016747f41Scth mutex_exit(&(DEVI(dip)->devi_lock)); 70917c478bd9Sstevel@tonic-gate 70927c478bd9Sstevel@tonic-gate rval = usba_persistent_pipe_open(usba_device); 70937c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 7094d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 70957c478bd9Sstevel@tonic-gate hubd->h_log_handle, 70967c478bd9Sstevel@tonic-gate "failed to reopen all pipes on reconnect"); 70977c478bd9Sstevel@tonic-gate } 70987c478bd9Sstevel@tonic-gate 70997c478bd9Sstevel@tonic-gate hubd_do_callback(hubd, dip, cookie); 71007c478bd9Sstevel@tonic-gate 71017c478bd9Sstevel@tonic-gate /* 71027c478bd9Sstevel@tonic-gate * We might see a connect event only if hotplug thread for 71037c478bd9Sstevel@tonic-gate * disconnect event don't run in time. 71047c478bd9Sstevel@tonic-gate * Set the flag again, so we don't miss posting a 71057c478bd9Sstevel@tonic-gate * disconnect event. 71067c478bd9Sstevel@tonic-gate */ 71077c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 71087c478bd9Sstevel@tonic-gate hubd->h_child_events[port] |= HUBD_CHILD_EVENT_DISCONNECT; 71097c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 71107c478bd9Sstevel@tonic-gate 71117c478bd9Sstevel@tonic-gate break; 71127c478bd9Sstevel@tonic-gate case USBA_EVENT_TAG_POST_RESUME: 71137c478bd9Sstevel@tonic-gate /* 71147c478bd9Sstevel@tonic-gate * Check if this child has missed the pre-suspend event before 71157c478bd9Sstevel@tonic-gate * it registered for event callbacks 71167c478bd9Sstevel@tonic-gate */ 71177c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 71187c478bd9Sstevel@tonic-gate if (hubd->h_child_events[port] & HUBD_CHILD_EVENT_PRESUSPEND) { 71197c478bd9Sstevel@tonic-gate /* clear the flag and post pre_suspend event */ 71207c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= 71217c478bd9Sstevel@tonic-gate ~HUBD_CHILD_EVENT_PRESUSPEND; 71227c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 71237c478bd9Sstevel@tonic-gate hubd_do_callback(hubd, dip, suspend_cookie); 71247c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 71257c478bd9Sstevel@tonic-gate } 71267c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 71277c478bd9Sstevel@tonic-gate 71287c478bd9Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex); 71297c478bd9Sstevel@tonic-gate usba_device->usb_no_cpr = 0; 71307c478bd9Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex); 71317c478bd9Sstevel@tonic-gate 71327c478bd9Sstevel@tonic-gate /* 71337c478bd9Sstevel@tonic-gate * Since the pipe has already been opened by hub 71347c478bd9Sstevel@tonic-gate * at DDI_RESUME time, there is no need for a 71357c478bd9Sstevel@tonic-gate * persistent pipe open 71367c478bd9Sstevel@tonic-gate */ 71377c478bd9Sstevel@tonic-gate hubd_do_callback(hubd, dip, cookie); 71387c478bd9Sstevel@tonic-gate 71397c478bd9Sstevel@tonic-gate /* 71407c478bd9Sstevel@tonic-gate * Set the flag again, so we don't miss posting a 71417c478bd9Sstevel@tonic-gate * pre-suspend event. This enforces a tighter 71427c478bd9Sstevel@tonic-gate * dev_state model. 71437c478bd9Sstevel@tonic-gate */ 71447c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 71457c478bd9Sstevel@tonic-gate hubd->h_child_events[port] |= HUBD_CHILD_EVENT_PRESUSPEND; 71467c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 71477c478bd9Sstevel@tonic-gate break; 71487c478bd9Sstevel@tonic-gate } 71497c478bd9Sstevel@tonic-gate } 71507c478bd9Sstevel@tonic-gate 71517c478bd9Sstevel@tonic-gate 71527c478bd9Sstevel@tonic-gate /* 71537c478bd9Sstevel@tonic-gate * handling of events coming from above 71547c478bd9Sstevel@tonic-gate */ 71557c478bd9Sstevel@tonic-gate static int 71567c478bd9Sstevel@tonic-gate hubd_disconnect_event_cb(dev_info_t *dip) 71577c478bd9Sstevel@tonic-gate { 71587c478bd9Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip); 71597c478bd9Sstevel@tonic-gate usb_port_t port, nports; 71607c478bd9Sstevel@tonic-gate usba_device_t *usba_dev; 71617c478bd9Sstevel@tonic-gate usba_event_t tag = USBA_EVENT_TAG_HOT_REMOVAL; 71627c478bd9Sstevel@tonic-gate int circ; 71637c478bd9Sstevel@tonic-gate 71647c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 71657c478bd9Sstevel@tonic-gate "hubd_disconnect_event_cb: tag=%d", tag); 71667c478bd9Sstevel@tonic-gate 71677c478bd9Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 71687c478bd9Sstevel@tonic-gate 71697c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 71707c478bd9Sstevel@tonic-gate switch (hubd->h_dev_state) { 71717c478bd9Sstevel@tonic-gate case USB_DEV_ONLINE: 71727c478bd9Sstevel@tonic-gate case USB_DEV_PWRED_DOWN: 71737c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_DISCONNECTED; 71747c478bd9Sstevel@tonic-gate /* stop polling on the interrupt pipe */ 71757c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd); 71767c478bd9Sstevel@tonic-gate 71777c478bd9Sstevel@tonic-gate /* FALLTHROUGH */ 71787c478bd9Sstevel@tonic-gate case USB_DEV_SUSPENDED: 71797c478bd9Sstevel@tonic-gate /* we remain in this state */ 71807c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 71817c478bd9Sstevel@tonic-gate hubd_run_callbacks(hubd, tag); 71827c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 71837c478bd9Sstevel@tonic-gate 71847c478bd9Sstevel@tonic-gate /* close all the open pipes of our children */ 7185993e3fafSRobert Mustacchi nports = hubd->h_nports; 71867c478bd9Sstevel@tonic-gate for (port = 1; port <= nports; port++) { 71877c478bd9Sstevel@tonic-gate usba_dev = hubd->h_usba_devices[port]; 71887c478bd9Sstevel@tonic-gate if (usba_dev != NULL) { 71897c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 71907c478bd9Sstevel@tonic-gate usba_persistent_pipe_close(usba_dev); 71917c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 71927c478bd9Sstevel@tonic-gate } 71937c478bd9Sstevel@tonic-gate } 71947c478bd9Sstevel@tonic-gate 71957c478bd9Sstevel@tonic-gate break; 71967c478bd9Sstevel@tonic-gate case USB_DEV_DISCONNECTED: 71977c478bd9Sstevel@tonic-gate /* avoid passing multiple disconnects to children */ 71987c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 71997c478bd9Sstevel@tonic-gate "hubd_disconnect_event_cb: Already disconnected"); 72007c478bd9Sstevel@tonic-gate 72017c478bd9Sstevel@tonic-gate break; 72027c478bd9Sstevel@tonic-gate default: 72037c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 72047c478bd9Sstevel@tonic-gate "hubd_disconnect_event_cb: Illegal devstate=%d", 72057c478bd9Sstevel@tonic-gate hubd->h_dev_state); 72067c478bd9Sstevel@tonic-gate 72077c478bd9Sstevel@tonic-gate break; 72087c478bd9Sstevel@tonic-gate } 72097c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 72107c478bd9Sstevel@tonic-gate 72117c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 72127c478bd9Sstevel@tonic-gate 72137c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 72147c478bd9Sstevel@tonic-gate } 72157c478bd9Sstevel@tonic-gate 72167c478bd9Sstevel@tonic-gate 72177c478bd9Sstevel@tonic-gate static int 72187c478bd9Sstevel@tonic-gate hubd_reconnect_event_cb(dev_info_t *dip) 72197c478bd9Sstevel@tonic-gate { 72207c478bd9Sstevel@tonic-gate int rval, circ; 72217c478bd9Sstevel@tonic-gate 72227c478bd9Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 72237c478bd9Sstevel@tonic-gate rval = hubd_restore_state_cb(dip); 72247c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 72257c478bd9Sstevel@tonic-gate 72267c478bd9Sstevel@tonic-gate return (rval); 72277c478bd9Sstevel@tonic-gate } 72287c478bd9Sstevel@tonic-gate 72297c478bd9Sstevel@tonic-gate 72307c478bd9Sstevel@tonic-gate /* 72317c478bd9Sstevel@tonic-gate * hubd_pre_suspend_event_cb 72327c478bd9Sstevel@tonic-gate * propogate event for binary compatibility of old drivers 72337c478bd9Sstevel@tonic-gate */ 72347c478bd9Sstevel@tonic-gate static int 72357c478bd9Sstevel@tonic-gate hubd_pre_suspend_event_cb(dev_info_t *dip) 72367c478bd9Sstevel@tonic-gate { 72377c478bd9Sstevel@tonic-gate int circ; 72387c478bd9Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip); 72397c478bd9Sstevel@tonic-gate 72407c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle, 72417c478bd9Sstevel@tonic-gate "hubd_pre_suspend_event_cb"); 72427c478bd9Sstevel@tonic-gate 72437c478bd9Sstevel@tonic-gate /* disable hotplug thread */ 72447c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 72457c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread++; 72467c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd); 72477c478bd9Sstevel@tonic-gate 72487c478bd9Sstevel@tonic-gate /* keep PM out till we see a cpr resume */ 72497c478bd9Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0); 72507c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 72517c478bd9Sstevel@tonic-gate 72527c478bd9Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 72537c478bd9Sstevel@tonic-gate hubd_run_callbacks(hubd, USBA_EVENT_TAG_PRE_SUSPEND); 72547c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 72557c478bd9Sstevel@tonic-gate 72567c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 72577c478bd9Sstevel@tonic-gate } 72587c478bd9Sstevel@tonic-gate 72597c478bd9Sstevel@tonic-gate 72607c478bd9Sstevel@tonic-gate /* 72617c478bd9Sstevel@tonic-gate * hubd_post_resume_event_cb 72627c478bd9Sstevel@tonic-gate * propogate event for binary compatibility of old drivers 72637c478bd9Sstevel@tonic-gate */ 72647c478bd9Sstevel@tonic-gate static int 72657c478bd9Sstevel@tonic-gate hubd_post_resume_event_cb(dev_info_t *dip) 72667c478bd9Sstevel@tonic-gate { 72677c478bd9Sstevel@tonic-gate int circ; 72687c478bd9Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip); 72697c478bd9Sstevel@tonic-gate 72707c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle, 72717c478bd9Sstevel@tonic-gate "hubd_post_resume_event_cb"); 72727c478bd9Sstevel@tonic-gate 72737c478bd9Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 72747c478bd9Sstevel@tonic-gate hubd_run_callbacks(hubd, USBA_EVENT_TAG_POST_RESUME); 72757c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 72767c478bd9Sstevel@tonic-gate 72777c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 72787c478bd9Sstevel@tonic-gate 72797c478bd9Sstevel@tonic-gate /* enable PM */ 72807c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0); 72817c478bd9Sstevel@tonic-gate 72827c478bd9Sstevel@tonic-gate /* allow hotplug thread */ 72837c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread--; 72847c478bd9Sstevel@tonic-gate 72857c478bd9Sstevel@tonic-gate /* start polling */ 72867c478bd9Sstevel@tonic-gate hubd_start_polling(hubd, 0); 72877c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 72887c478bd9Sstevel@tonic-gate 72897c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 72907c478bd9Sstevel@tonic-gate } 72917c478bd9Sstevel@tonic-gate 72927c478bd9Sstevel@tonic-gate 72937c478bd9Sstevel@tonic-gate /* 72947c478bd9Sstevel@tonic-gate * hubd_cpr_suspend 72957c478bd9Sstevel@tonic-gate * save the current state of the driver/device 72967c478bd9Sstevel@tonic-gate */ 72977c478bd9Sstevel@tonic-gate static int 72987c478bd9Sstevel@tonic-gate hubd_cpr_suspend(hubd_t *hubd) 72997c478bd9Sstevel@tonic-gate { 73007c478bd9Sstevel@tonic-gate usb_port_t port, nports; 73017c478bd9Sstevel@tonic-gate usba_device_t *usba_dev; 73027c478bd9Sstevel@tonic-gate uchar_t no_cpr = 0; 73037c478bd9Sstevel@tonic-gate int rval = USB_FAILURE; 73047c478bd9Sstevel@tonic-gate 73057c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 73067c478bd9Sstevel@tonic-gate "hubd_cpr_suspend: Begin"); 73077c478bd9Sstevel@tonic-gate 73087c478bd9Sstevel@tonic-gate /* Make sure device is powered up to save state. */ 73097c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 73107c478bd9Sstevel@tonic-gate hubd_pm_busy_component(hubd, hubd->h_dip, 0); 73117c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 73127c478bd9Sstevel@tonic-gate 73137c478bd9Sstevel@tonic-gate /* bring the device to full power */ 73147c478bd9Sstevel@tonic-gate (void) pm_raise_power(hubd->h_dip, 0, USB_DEV_OS_FULL_PWR); 73157c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 73167c478bd9Sstevel@tonic-gate 73177c478bd9Sstevel@tonic-gate switch (hubd->h_dev_state) { 73187c478bd9Sstevel@tonic-gate case USB_DEV_ONLINE: 73197c478bd9Sstevel@tonic-gate case USB_DEV_PWRED_DOWN: 73207c478bd9Sstevel@tonic-gate case USB_DEV_DISCONNECTED: 73217c478bd9Sstevel@tonic-gate /* find out if all our children have been quiesced */ 7322993e3fafSRobert Mustacchi nports = hubd->h_nports; 73237c478bd9Sstevel@tonic-gate for (port = 1; (no_cpr == 0) && (port <= nports); port++) { 73247c478bd9Sstevel@tonic-gate usba_dev = hubd->h_usba_devices[port]; 73257c478bd9Sstevel@tonic-gate if (usba_dev != NULL) { 73267c478bd9Sstevel@tonic-gate mutex_enter(&usba_dev->usb_mutex); 73277c478bd9Sstevel@tonic-gate no_cpr += usba_dev->usb_no_cpr; 73287c478bd9Sstevel@tonic-gate mutex_exit(&usba_dev->usb_mutex); 73297c478bd9Sstevel@tonic-gate } 73307c478bd9Sstevel@tonic-gate } 73317c478bd9Sstevel@tonic-gate if (no_cpr > 0) { 73327c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 73337c478bd9Sstevel@tonic-gate "Children busy - can't checkpoint"); 73347c478bd9Sstevel@tonic-gate /* remain in same state to fail checkpoint */ 73357c478bd9Sstevel@tonic-gate 73367c478bd9Sstevel@tonic-gate break; 73377c478bd9Sstevel@tonic-gate } else { 73387c478bd9Sstevel@tonic-gate /* 73397c478bd9Sstevel@tonic-gate * do not suspend if our hotplug thread 73407c478bd9Sstevel@tonic-gate * or the deathrow thread is active 73417c478bd9Sstevel@tonic-gate */ 73427c478bd9Sstevel@tonic-gate if ((hubd->h_hotplug_thread > 1) || 73437c478bd9Sstevel@tonic-gate (hubd->h_cleanup_active == B_TRUE)) { 73447c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 73457c478bd9Sstevel@tonic-gate hubd->h_log_handle, 73467c478bd9Sstevel@tonic-gate "hotplug thread active - can't cpr"); 73477c478bd9Sstevel@tonic-gate /* remain in same state to fail checkpoint */ 73487c478bd9Sstevel@tonic-gate 73497c478bd9Sstevel@tonic-gate break; 73507c478bd9Sstevel@tonic-gate } 73517c478bd9Sstevel@tonic-gate 73527c478bd9Sstevel@tonic-gate /* quiesce ourselves now */ 73537c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd); 73547c478bd9Sstevel@tonic-gate 73557c478bd9Sstevel@tonic-gate /* close all the open pipes of our children */ 73567c478bd9Sstevel@tonic-gate for (port = 1; port <= nports; port++) { 73577c478bd9Sstevel@tonic-gate usba_dev = hubd->h_usba_devices[port]; 73587c478bd9Sstevel@tonic-gate if (usba_dev != NULL) { 73597c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 73607c478bd9Sstevel@tonic-gate usba_persistent_pipe_close(usba_dev); 73616f6c7d2bSVincent Wang if (hubd_suspend_port(hubd, port)) { 73626f6c7d2bSVincent Wang USB_DPRINTF_L0( 73636f6c7d2bSVincent Wang DPRINT_MASK_HOTPLUG, 73646f6c7d2bSVincent Wang hubd->h_log_handle, 73656f6c7d2bSVincent Wang "suspending port %d failed", 73666f6c7d2bSVincent Wang port); 73676f6c7d2bSVincent Wang } 73687c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 73697c478bd9Sstevel@tonic-gate } 73706f6c7d2bSVincent Wang 73717c478bd9Sstevel@tonic-gate } 73726f6c7d2bSVincent Wang hubd->h_dev_state = USB_DEV_SUSPENDED; 73737c478bd9Sstevel@tonic-gate 73747c478bd9Sstevel@tonic-gate /* 73757c478bd9Sstevel@tonic-gate * if we are the root hub, we close our pipes 73767c478bd9Sstevel@tonic-gate * ourselves. 73777c478bd9Sstevel@tonic-gate */ 73787c478bd9Sstevel@tonic-gate if (usba_is_root_hub(hubd->h_dip)) { 73797c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 73807c478bd9Sstevel@tonic-gate usba_persistent_pipe_close( 73817c478bd9Sstevel@tonic-gate usba_get_usba_device(hubd->h_dip)); 73827c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 73837c478bd9Sstevel@tonic-gate } 73847c478bd9Sstevel@tonic-gate rval = USB_SUCCESS; 73857c478bd9Sstevel@tonic-gate 73867c478bd9Sstevel@tonic-gate break; 73877c478bd9Sstevel@tonic-gate } 73887c478bd9Sstevel@tonic-gate case USB_DEV_SUSPENDED: 73897c478bd9Sstevel@tonic-gate default: 73907c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 73917c478bd9Sstevel@tonic-gate "hubd_cpr_suspend: Illegal dev state=%d", 73927c478bd9Sstevel@tonic-gate hubd->h_dev_state); 73937c478bd9Sstevel@tonic-gate 73947c478bd9Sstevel@tonic-gate break; 73957c478bd9Sstevel@tonic-gate } 73967c478bd9Sstevel@tonic-gate 73977c478bd9Sstevel@tonic-gate hubd_pm_idle_component(hubd, hubd->h_dip, 0); 73987c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 73997c478bd9Sstevel@tonic-gate 74007c478bd9Sstevel@tonic-gate return (rval); 74017c478bd9Sstevel@tonic-gate } 74027c478bd9Sstevel@tonic-gate 74037c478bd9Sstevel@tonic-gate static void 74047c478bd9Sstevel@tonic-gate hubd_cpr_resume(dev_info_t *dip) 74057c478bd9Sstevel@tonic-gate { 74067c478bd9Sstevel@tonic-gate int rval, circ; 74077c478bd9Sstevel@tonic-gate 74087c478bd9Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 74097c478bd9Sstevel@tonic-gate /* 74107c478bd9Sstevel@tonic-gate * if we are the root hub, we open our pipes 74117c478bd9Sstevel@tonic-gate * ourselves. 74127c478bd9Sstevel@tonic-gate */ 74137c478bd9Sstevel@tonic-gate if (usba_is_root_hub(dip)) { 74147c478bd9Sstevel@tonic-gate rval = usba_persistent_pipe_open( 74157c478bd9Sstevel@tonic-gate usba_get_usba_device(dip)); 74167c478bd9Sstevel@tonic-gate ASSERT(rval == USB_SUCCESS); 74177c478bd9Sstevel@tonic-gate } 74187c478bd9Sstevel@tonic-gate (void) hubd_restore_state_cb(dip); 74197c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 74207c478bd9Sstevel@tonic-gate } 74217c478bd9Sstevel@tonic-gate 74227c478bd9Sstevel@tonic-gate 74237c478bd9Sstevel@tonic-gate /* 74247c478bd9Sstevel@tonic-gate * hubd_restore_state_cb 74257c478bd9Sstevel@tonic-gate * Event callback to restore device state 74267c478bd9Sstevel@tonic-gate */ 74277c478bd9Sstevel@tonic-gate static int 74287c478bd9Sstevel@tonic-gate hubd_restore_state_cb(dev_info_t *dip) 74297c478bd9Sstevel@tonic-gate { 74307c478bd9Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip); 74317c478bd9Sstevel@tonic-gate 74327c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 74337c478bd9Sstevel@tonic-gate "hubd_restore_state_cb: Begin"); 74347c478bd9Sstevel@tonic-gate 74357c478bd9Sstevel@tonic-gate /* restore the state of this device */ 74367c478bd9Sstevel@tonic-gate hubd_restore_device_state(dip, hubd); 74377c478bd9Sstevel@tonic-gate 74387c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 74397c478bd9Sstevel@tonic-gate } 74407c478bd9Sstevel@tonic-gate 74417c478bd9Sstevel@tonic-gate 74427c478bd9Sstevel@tonic-gate /* 74437c478bd9Sstevel@tonic-gate * registering for events 74447c478bd9Sstevel@tonic-gate */ 74457c478bd9Sstevel@tonic-gate static int 74467c478bd9Sstevel@tonic-gate hubd_register_events(hubd_t *hubd) 74477c478bd9Sstevel@tonic-gate { 74487c478bd9Sstevel@tonic-gate int rval = USB_SUCCESS; 74497c478bd9Sstevel@tonic-gate 74507c478bd9Sstevel@tonic-gate if (usba_is_root_hub(hubd->h_dip)) { 74517c478bd9Sstevel@tonic-gate hubd_register_cpr_callback(hubd); 74527c478bd9Sstevel@tonic-gate } else { 74537c478bd9Sstevel@tonic-gate rval = usb_register_event_cbs(hubd->h_dip, &hubd_events, 0); 74547c478bd9Sstevel@tonic-gate } 74557c478bd9Sstevel@tonic-gate 74567c478bd9Sstevel@tonic-gate return (rval); 74577c478bd9Sstevel@tonic-gate } 74587c478bd9Sstevel@tonic-gate 74597c478bd9Sstevel@tonic-gate 74607c478bd9Sstevel@tonic-gate /* 74617c478bd9Sstevel@tonic-gate * hubd cpr callback related functions 74627c478bd9Sstevel@tonic-gate * 74637c478bd9Sstevel@tonic-gate * hubd_cpr_post_user_callb: 74647c478bd9Sstevel@tonic-gate * This function is called during checkpoint & resume - 74657c478bd9Sstevel@tonic-gate * 1. after user threads are stopped during checkpoint 74667c478bd9Sstevel@tonic-gate * 2. after kernel threads are resumed during resume 74677c478bd9Sstevel@tonic-gate */ 74687c478bd9Sstevel@tonic-gate /* ARGSUSED */ 74697c478bd9Sstevel@tonic-gate static boolean_t 74707c478bd9Sstevel@tonic-gate hubd_cpr_post_user_callb(void *arg, int code) 74717c478bd9Sstevel@tonic-gate { 74727c478bd9Sstevel@tonic-gate hubd_cpr_t *cpr_cb = (hubd_cpr_t *)arg; 74737c478bd9Sstevel@tonic-gate hubd_t *hubd = cpr_cb->statep; 74747c478bd9Sstevel@tonic-gate int retry = 0; 74757c478bd9Sstevel@tonic-gate 74767c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle, 74777c478bd9Sstevel@tonic-gate "hubd_cpr_post_user_callb"); 74787c478bd9Sstevel@tonic-gate 74797c478bd9Sstevel@tonic-gate switch (code) { 74807c478bd9Sstevel@tonic-gate case CB_CODE_CPR_CHKPT: 74817c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_EVENTS, hubd->h_log_handle, 74827c478bd9Sstevel@tonic-gate "hubd_cpr_post_user_callb: CB_CODE_CPR_CHKPT"); 74837c478bd9Sstevel@tonic-gate 74847c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 74857c478bd9Sstevel@tonic-gate 74867c478bd9Sstevel@tonic-gate /* turn off deathrow thread */ 74877c478bd9Sstevel@tonic-gate hubd->h_cleanup_enabled = B_FALSE; 74887c478bd9Sstevel@tonic-gate 74897c478bd9Sstevel@tonic-gate /* give up if deathrow thread doesn't exit */ 74907c478bd9Sstevel@tonic-gate while ((hubd->h_cleanup_active == B_TRUE) && (retry++ < 3)) { 74917c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 74927c478bd9Sstevel@tonic-gate delay(drv_usectohz(hubd_dip_cleanup_delay)); 74937c478bd9Sstevel@tonic-gate 74947c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_EVENTS, hubd->h_log_handle, 74957c478bd9Sstevel@tonic-gate "hubd_cpr_post_user_callb, waiting for " 74967c478bd9Sstevel@tonic-gate "deathrow thread to exit"); 74977c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 74987c478bd9Sstevel@tonic-gate } 74997c478bd9Sstevel@tonic-gate 75007c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 75017c478bd9Sstevel@tonic-gate 75027c478bd9Sstevel@tonic-gate /* save the state of the device */ 75037c478bd9Sstevel@tonic-gate (void) hubd_pre_suspend_event_cb(hubd->h_dip); 75047c478bd9Sstevel@tonic-gate 75057c478bd9Sstevel@tonic-gate return (B_TRUE); 75067c478bd9Sstevel@tonic-gate case CB_CODE_CPR_RESUME: 75077c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_EVENTS, hubd->h_log_handle, 75087c478bd9Sstevel@tonic-gate "hubd_cpr_post_user_callb: CB_CODE_CPR_RESUME"); 75097c478bd9Sstevel@tonic-gate 75107c478bd9Sstevel@tonic-gate /* restore the state of the device */ 75117c478bd9Sstevel@tonic-gate (void) hubd_post_resume_event_cb(hubd->h_dip); 75127c478bd9Sstevel@tonic-gate 75137c478bd9Sstevel@tonic-gate /* turn on deathrow thread */ 75147c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 75157c478bd9Sstevel@tonic-gate hubd->h_cleanup_enabled = B_TRUE; 75167c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 75177c478bd9Sstevel@tonic-gate 75187c478bd9Sstevel@tonic-gate hubd_schedule_cleanup(hubd->h_usba_device->usb_root_hub_dip); 75197c478bd9Sstevel@tonic-gate 75207c478bd9Sstevel@tonic-gate return (B_TRUE); 75217c478bd9Sstevel@tonic-gate default: 75227c478bd9Sstevel@tonic-gate 75237c478bd9Sstevel@tonic-gate return (B_FALSE); 75247c478bd9Sstevel@tonic-gate } 75257c478bd9Sstevel@tonic-gate 75267c478bd9Sstevel@tonic-gate } 75277c478bd9Sstevel@tonic-gate 75287c478bd9Sstevel@tonic-gate 75297c478bd9Sstevel@tonic-gate /* register callback with cpr framework */ 75307c478bd9Sstevel@tonic-gate void 75317c478bd9Sstevel@tonic-gate hubd_register_cpr_callback(hubd_t *hubd) 75327c478bd9Sstevel@tonic-gate { 75337c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle, 75347c478bd9Sstevel@tonic-gate "hubd_register_cpr_callback"); 75357c478bd9Sstevel@tonic-gate 75367c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 75377c478bd9Sstevel@tonic-gate hubd->h_cpr_cb = 75387c478bd9Sstevel@tonic-gate (hubd_cpr_t *)kmem_zalloc(sizeof (hubd_cpr_t), KM_SLEEP); 75397c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 75407c478bd9Sstevel@tonic-gate mutex_init(&hubd->h_cpr_cb->lockp, NULL, MUTEX_DRIVER, 75417c478bd9Sstevel@tonic-gate hubd->h_dev_data->dev_iblock_cookie); 75427c478bd9Sstevel@tonic-gate hubd->h_cpr_cb->statep = hubd; 75437c478bd9Sstevel@tonic-gate hubd->h_cpr_cb->cpr.cc_lockp = &hubd->h_cpr_cb->lockp; 75447c478bd9Sstevel@tonic-gate hubd->h_cpr_cb->cpr.cc_id = callb_add(hubd_cpr_post_user_callb, 75457c478bd9Sstevel@tonic-gate (void *)hubd->h_cpr_cb, CB_CL_CPR_POST_USER, "hubd"); 75467c478bd9Sstevel@tonic-gate } 75477c478bd9Sstevel@tonic-gate 75487c478bd9Sstevel@tonic-gate 75497c478bd9Sstevel@tonic-gate /* unregister callback with cpr framework */ 75507c478bd9Sstevel@tonic-gate void 75517c478bd9Sstevel@tonic-gate hubd_unregister_cpr_callback(hubd_t *hubd) 75527c478bd9Sstevel@tonic-gate { 75537c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle, 75547c478bd9Sstevel@tonic-gate "hubd_unregister_cpr_callback"); 75557c478bd9Sstevel@tonic-gate 75567c478bd9Sstevel@tonic-gate if (hubd->h_cpr_cb) { 75577c478bd9Sstevel@tonic-gate (void) callb_delete(hubd->h_cpr_cb->cpr.cc_id); 75587c478bd9Sstevel@tonic-gate mutex_destroy(&hubd->h_cpr_cb->lockp); 75597c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 75607c478bd9Sstevel@tonic-gate kmem_free(hubd->h_cpr_cb, sizeof (hubd_cpr_t)); 75617c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 75627c478bd9Sstevel@tonic-gate } 75637c478bd9Sstevel@tonic-gate } 75647c478bd9Sstevel@tonic-gate 75657c478bd9Sstevel@tonic-gate 75667c478bd9Sstevel@tonic-gate /* 75677c478bd9Sstevel@tonic-gate * Power management 75687c478bd9Sstevel@tonic-gate * 75697c478bd9Sstevel@tonic-gate * create the pm components required for power management 75707c478bd9Sstevel@tonic-gate */ 75717c478bd9Sstevel@tonic-gate static void 75727c478bd9Sstevel@tonic-gate hubd_create_pm_components(dev_info_t *dip, hubd_t *hubd) 75737c478bd9Sstevel@tonic-gate { 75747c478bd9Sstevel@tonic-gate hub_power_t *hubpm; 75757c478bd9Sstevel@tonic-gate 75767c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 75777c478bd9Sstevel@tonic-gate "hubd_create_pm_components: Begin"); 75787c478bd9Sstevel@tonic-gate 75797c478bd9Sstevel@tonic-gate /* Allocate the state structure */ 75807c478bd9Sstevel@tonic-gate hubpm = kmem_zalloc(sizeof (hub_power_t), KM_SLEEP); 75817c478bd9Sstevel@tonic-gate 75827c478bd9Sstevel@tonic-gate hubd->h_hubpm = hubpm; 75837c478bd9Sstevel@tonic-gate hubpm->hubp_hubd = hubd; 75847c478bd9Sstevel@tonic-gate hubpm->hubp_pm_capabilities = 0; 75857c478bd9Sstevel@tonic-gate hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR; 7586e5815e7aSJosef 'Jeff' Sipek hubpm->hubp_time_at_full_power = gethrtime(); 7587e5815e7aSJosef 'Jeff' Sipek hubpm->hubp_min_pm_threshold = hubdi_min_pm_threshold * NANOSEC; 75887c478bd9Sstevel@tonic-gate 75897c478bd9Sstevel@tonic-gate /* alloc memory to save power states of children */ 75907c478bd9Sstevel@tonic-gate hubpm->hubp_child_pwrstate = (uint8_t *) 7591c0f24e5bSlg kmem_zalloc(MAX_PORTS + 1, KM_SLEEP); 75927c478bd9Sstevel@tonic-gate 75937c478bd9Sstevel@tonic-gate /* 75947c478bd9Sstevel@tonic-gate * if the enable remote wakeup fails 75957c478bd9Sstevel@tonic-gate * we still want to enable 75967c478bd9Sstevel@tonic-gate * parent notification so we can PM the children 75977c478bd9Sstevel@tonic-gate */ 75987c478bd9Sstevel@tonic-gate usb_enable_parent_notification(dip); 75997c478bd9Sstevel@tonic-gate 76007c478bd9Sstevel@tonic-gate if (usb_handle_remote_wakeup(dip, 76017c478bd9Sstevel@tonic-gate USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) { 76027c478bd9Sstevel@tonic-gate uint_t pwr_states; 76037c478bd9Sstevel@tonic-gate 76047c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 76057c478bd9Sstevel@tonic-gate "hubd_create_pm_components: " 76067c478bd9Sstevel@tonic-gate "Remote Wakeup Enabled"); 76077c478bd9Sstevel@tonic-gate 76087c478bd9Sstevel@tonic-gate if (usb_create_pm_components(dip, &pwr_states) == 76097c478bd9Sstevel@tonic-gate USB_SUCCESS) { 76107c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 76117c478bd9Sstevel@tonic-gate hubpm->hubp_wakeup_enabled = 1; 76127c478bd9Sstevel@tonic-gate hubpm->hubp_pwr_states = (uint8_t)pwr_states; 76137c478bd9Sstevel@tonic-gate 76147c478bd9Sstevel@tonic-gate /* we are busy now till end of the attach */ 76157c478bd9Sstevel@tonic-gate hubd_pm_busy_component(hubd, dip, 0); 76167c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 76177c478bd9Sstevel@tonic-gate 76187c478bd9Sstevel@tonic-gate /* bring the device to full power */ 76197c478bd9Sstevel@tonic-gate (void) pm_raise_power(dip, 0, 76207c478bd9Sstevel@tonic-gate USB_DEV_OS_FULL_PWR); 76217c478bd9Sstevel@tonic-gate } 76227c478bd9Sstevel@tonic-gate } 76237c478bd9Sstevel@tonic-gate 76247c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 76257c478bd9Sstevel@tonic-gate "hubd_create_pm_components: END"); 76267c478bd9Sstevel@tonic-gate } 76277c478bd9Sstevel@tonic-gate 76287c478bd9Sstevel@tonic-gate 76297c478bd9Sstevel@tonic-gate /* 76307c478bd9Sstevel@tonic-gate * Attachment point management 76317c478bd9Sstevel@tonic-gate */ 76327c478bd9Sstevel@tonic-gate /* ARGSUSED */ 76337c478bd9Sstevel@tonic-gate int 76347c478bd9Sstevel@tonic-gate usba_hubdi_open(dev_info_t *dip, dev_t *devp, int flags, int otyp, 7635993e3fafSRobert Mustacchi cred_t *credp) 76367c478bd9Sstevel@tonic-gate { 76377c478bd9Sstevel@tonic-gate hubd_t *hubd; 76387c478bd9Sstevel@tonic-gate 76397c478bd9Sstevel@tonic-gate if (otyp != OTYP_CHR) 76407c478bd9Sstevel@tonic-gate return (EINVAL); 76417c478bd9Sstevel@tonic-gate 76427c478bd9Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 76437c478bd9Sstevel@tonic-gate if (hubd == NULL) { 76447c478bd9Sstevel@tonic-gate return (ENXIO); 76457c478bd9Sstevel@tonic-gate } 76467c478bd9Sstevel@tonic-gate 76477c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 76487c478bd9Sstevel@tonic-gate "hubd_open:"); 76497c478bd9Sstevel@tonic-gate 76507c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 76517c478bd9Sstevel@tonic-gate if ((flags & FEXCL) && (hubd->h_softstate & HUBD_SS_ISOPEN)) { 76527c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 76537c478bd9Sstevel@tonic-gate 76547c478bd9Sstevel@tonic-gate return (EBUSY); 76557c478bd9Sstevel@tonic-gate } 76567c478bd9Sstevel@tonic-gate 76577c478bd9Sstevel@tonic-gate hubd->h_softstate |= HUBD_SS_ISOPEN; 76587c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 76597c478bd9Sstevel@tonic-gate 76607c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, "opened"); 76617c478bd9Sstevel@tonic-gate 76627c478bd9Sstevel@tonic-gate return (0); 76637c478bd9Sstevel@tonic-gate } 76647c478bd9Sstevel@tonic-gate 76657c478bd9Sstevel@tonic-gate 76667c478bd9Sstevel@tonic-gate /* ARGSUSED */ 76677c478bd9Sstevel@tonic-gate int 76687c478bd9Sstevel@tonic-gate usba_hubdi_close(dev_info_t *dip, dev_t dev, int flag, int otyp, 7669*d96925c4SRichard Lowe cred_t *credp) 76707c478bd9Sstevel@tonic-gate { 76717c478bd9Sstevel@tonic-gate hubd_t *hubd; 76727c478bd9Sstevel@tonic-gate 76737c478bd9Sstevel@tonic-gate if (otyp != OTYP_CHR) { 76747c478bd9Sstevel@tonic-gate return (EINVAL); 76757c478bd9Sstevel@tonic-gate } 76767c478bd9Sstevel@tonic-gate 76777c478bd9Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 76787c478bd9Sstevel@tonic-gate 76797c478bd9Sstevel@tonic-gate if (hubd == NULL) { 76807c478bd9Sstevel@tonic-gate return (ENXIO); 76817c478bd9Sstevel@tonic-gate } 76827c478bd9Sstevel@tonic-gate 76837c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, "hubd_close:"); 76847c478bd9Sstevel@tonic-gate 76857c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 76867c478bd9Sstevel@tonic-gate hubd->h_softstate &= ~HUBD_SS_ISOPEN; 76877c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 76887c478bd9Sstevel@tonic-gate 76897c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, "closed"); 76907c478bd9Sstevel@tonic-gate 76917c478bd9Sstevel@tonic-gate return (0); 76927c478bd9Sstevel@tonic-gate } 76937c478bd9Sstevel@tonic-gate 76947c478bd9Sstevel@tonic-gate 76957c478bd9Sstevel@tonic-gate /* 76967c478bd9Sstevel@tonic-gate * hubd_ioctl: cfgadm controls 76977c478bd9Sstevel@tonic-gate */ 76987c478bd9Sstevel@tonic-gate /* ARGSUSED */ 76997c478bd9Sstevel@tonic-gate int 77007c478bd9Sstevel@tonic-gate usba_hubdi_ioctl(dev_info_t *self, dev_t dev, int cmd, intptr_t arg, 7701993e3fafSRobert Mustacchi int mode, cred_t *credp, int *rvalp) 77027c478bd9Sstevel@tonic-gate { 77037c478bd9Sstevel@tonic-gate int rv = 0; 77047c478bd9Sstevel@tonic-gate char *msg; /* for messages */ 77057c478bd9Sstevel@tonic-gate hubd_t *hubd; 77067c478bd9Sstevel@tonic-gate usb_port_t port = 0; 77077c478bd9Sstevel@tonic-gate dev_info_t *child_dip = NULL; 77087c478bd9Sstevel@tonic-gate dev_info_t *rh_dip; 77097c478bd9Sstevel@tonic-gate devctl_ap_state_t ap_state; 77107c478bd9Sstevel@tonic-gate struct devctl_iocdata *dcp = NULL; 77117c478bd9Sstevel@tonic-gate usb_pipe_state_t prev_pipe_state = 0; 77127c478bd9Sstevel@tonic-gate int circ, rh_circ, prh_circ; 77137c478bd9Sstevel@tonic-gate 77147c478bd9Sstevel@tonic-gate if ((hubd = hubd_get_soft_state(self)) == NULL) { 77157c478bd9Sstevel@tonic-gate 77167c478bd9Sstevel@tonic-gate return (ENXIO); 77177c478bd9Sstevel@tonic-gate } 77187c478bd9Sstevel@tonic-gate 77197c478bd9Sstevel@tonic-gate rh_dip = hubd->h_usba_device->usb_root_hub_dip; 77207c478bd9Sstevel@tonic-gate 77217c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 77227c478bd9Sstevel@tonic-gate "usba_hubdi_ioctl: " 77237c478bd9Sstevel@tonic-gate "cmd=%x, arg=%lx, mode=%x, cred=%p, rval=%p dev=0x%lx", 7724112116d8Sfb cmd, arg, mode, (void *)credp, (void *)rvalp, dev); 77257c478bd9Sstevel@tonic-gate 77267c478bd9Sstevel@tonic-gate /* read devctl ioctl data */ 77277c478bd9Sstevel@tonic-gate if ((cmd != DEVCTL_AP_CONTROL) && 77287c478bd9Sstevel@tonic-gate (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)) { 77297c478bd9Sstevel@tonic-gate 77307c478bd9Sstevel@tonic-gate return (EFAULT); 77317c478bd9Sstevel@tonic-gate } 77327c478bd9Sstevel@tonic-gate 77337c478bd9Sstevel@tonic-gate /* 77347c478bd9Sstevel@tonic-gate * make sure the hub is connected before trying any 77357c478bd9Sstevel@tonic-gate * of the following operations: 77367c478bd9Sstevel@tonic-gate * configure, connect, disconnect 77377c478bd9Sstevel@tonic-gate */ 77387c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 77397c478bd9Sstevel@tonic-gate 77407c478bd9Sstevel@tonic-gate switch (cmd) { 77417c478bd9Sstevel@tonic-gate case DEVCTL_AP_DISCONNECT: 77427c478bd9Sstevel@tonic-gate case DEVCTL_AP_UNCONFIGURE: 77437c478bd9Sstevel@tonic-gate case DEVCTL_AP_CONFIGURE: 77447c478bd9Sstevel@tonic-gate if (hubd->h_dev_state == USB_DEV_DISCONNECTED) { 77457c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 77467c478bd9Sstevel@tonic-gate "hubd: already gone"); 77477c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 77487c478bd9Sstevel@tonic-gate if (dcp) { 77497c478bd9Sstevel@tonic-gate ndi_dc_freehdl(dcp); 77507c478bd9Sstevel@tonic-gate } 77517c478bd9Sstevel@tonic-gate 77527c478bd9Sstevel@tonic-gate return (EIO); 77537c478bd9Sstevel@tonic-gate } 77547c478bd9Sstevel@tonic-gate 77557c478bd9Sstevel@tonic-gate /* FALLTHROUGH */ 77567c478bd9Sstevel@tonic-gate case DEVCTL_AP_GETSTATE: 77577c478bd9Sstevel@tonic-gate if ((port = hubd_get_port_num(hubd, dcp)) == 0) { 77587c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 77597c478bd9Sstevel@tonic-gate "hubd: bad port"); 77607c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 77617c478bd9Sstevel@tonic-gate if (dcp) { 77627c478bd9Sstevel@tonic-gate ndi_dc_freehdl(dcp); 77637c478bd9Sstevel@tonic-gate } 77647c478bd9Sstevel@tonic-gate 77657c478bd9Sstevel@tonic-gate return (EINVAL); 77667c478bd9Sstevel@tonic-gate } 77677c478bd9Sstevel@tonic-gate break; 77687c478bd9Sstevel@tonic-gate 77697c478bd9Sstevel@tonic-gate case DEVCTL_AP_CONTROL: 77707c478bd9Sstevel@tonic-gate 77717c478bd9Sstevel@tonic-gate break; 77727c478bd9Sstevel@tonic-gate default: 77737c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 77747c478bd9Sstevel@tonic-gate if (dcp) { 77757c478bd9Sstevel@tonic-gate ndi_dc_freehdl(dcp); 77767c478bd9Sstevel@tonic-gate } 77777c478bd9Sstevel@tonic-gate 77787c478bd9Sstevel@tonic-gate return (ENOTTY); 77797c478bd9Sstevel@tonic-gate } 77807c478bd9Sstevel@tonic-gate 77817c478bd9Sstevel@tonic-gate /* should not happen, just in case */ 77827c478bd9Sstevel@tonic-gate if (hubd->h_dev_state == USB_DEV_SUSPENDED) { 77837c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 77847c478bd9Sstevel@tonic-gate if (dcp) { 77857c478bd9Sstevel@tonic-gate ndi_dc_freehdl(dcp); 77867c478bd9Sstevel@tonic-gate } 77877c478bd9Sstevel@tonic-gate 77887c478bd9Sstevel@tonic-gate return (EIO); 77897c478bd9Sstevel@tonic-gate } 77907c478bd9Sstevel@tonic-gate 7791ffcd51f3Slg if (hubd->h_reset_port[port]) { 7792ffcd51f3Slg USB_DPRINTF_L2(DPRINT_MASK_CBOPS, hubd->h_log_handle, 7793ffcd51f3Slg "This port is resetting, just return"); 7794ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 7795ffcd51f3Slg if (dcp) { 7796ffcd51f3Slg ndi_dc_freehdl(dcp); 7797ffcd51f3Slg } 7798ffcd51f3Slg 7799ffcd51f3Slg return (EIO); 7800ffcd51f3Slg } 7801ffcd51f3Slg 78027c478bd9Sstevel@tonic-gate hubd_pm_busy_component(hubd, hubd->h_dip, 0); 78037c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 78047c478bd9Sstevel@tonic-gate 78057c478bd9Sstevel@tonic-gate /* go full power */ 78067c478bd9Sstevel@tonic-gate (void) pm_raise_power(hubd->h_dip, 0, USB_DEV_OS_FULL_PWR); 78077c478bd9Sstevel@tonic-gate 78087c478bd9Sstevel@tonic-gate ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ); 78097c478bd9Sstevel@tonic-gate ndi_devi_enter(rh_dip, &rh_circ); 78107c478bd9Sstevel@tonic-gate ndi_devi_enter(hubd->h_dip, &circ); 78117c478bd9Sstevel@tonic-gate 78127c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 78137c478bd9Sstevel@tonic-gate 7814c0f24e5bSlg hubd->h_hotplug_thread++; 7815c0f24e5bSlg 7816c0f24e5bSlg /* stop polling if it was active */ 78177c478bd9Sstevel@tonic-gate if (hubd->h_ep1_ph) { 78187c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 78197c478bd9Sstevel@tonic-gate (void) usb_pipe_get_state(hubd->h_ep1_ph, &prev_pipe_state, 7820c0f24e5bSlg USB_FLAGS_SLEEP); 78217c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 78227c478bd9Sstevel@tonic-gate 78237c478bd9Sstevel@tonic-gate if (prev_pipe_state == USB_PIPE_STATE_ACTIVE) { 78247c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd); 78257c478bd9Sstevel@tonic-gate } 78267c478bd9Sstevel@tonic-gate } 78277c478bd9Sstevel@tonic-gate 78287c478bd9Sstevel@tonic-gate switch (cmd) { 78297c478bd9Sstevel@tonic-gate case DEVCTL_AP_DISCONNECT: 78307c478bd9Sstevel@tonic-gate if (hubd_delete_child(hubd, port, 78317c478bd9Sstevel@tonic-gate NDI_DEVI_REMOVE, B_FALSE) != USB_SUCCESS) { 78327c478bd9Sstevel@tonic-gate rv = EIO; 78337c478bd9Sstevel@tonic-gate } 78347c478bd9Sstevel@tonic-gate 78357c478bd9Sstevel@tonic-gate break; 78367c478bd9Sstevel@tonic-gate case DEVCTL_AP_UNCONFIGURE: 78377c478bd9Sstevel@tonic-gate if (hubd_delete_child(hubd, port, 78387c478bd9Sstevel@tonic-gate NDI_UNCONFIG, B_FALSE) != USB_SUCCESS) { 78397c478bd9Sstevel@tonic-gate rv = EIO; 78407c478bd9Sstevel@tonic-gate } 78417c478bd9Sstevel@tonic-gate 78427c478bd9Sstevel@tonic-gate break; 78437c478bd9Sstevel@tonic-gate case DEVCTL_AP_CONFIGURE: 78447c478bd9Sstevel@tonic-gate /* toggle port */ 78457c478bd9Sstevel@tonic-gate if (hubd_toggle_port(hubd, port) != USB_SUCCESS) { 78467c478bd9Sstevel@tonic-gate rv = EIO; 78477c478bd9Sstevel@tonic-gate 78487c478bd9Sstevel@tonic-gate break; 78497c478bd9Sstevel@tonic-gate } 78507c478bd9Sstevel@tonic-gate 78517c478bd9Sstevel@tonic-gate (void) hubd_handle_port_connect(hubd, port); 78527c478bd9Sstevel@tonic-gate child_dip = hubd_get_child_dip(hubd, port); 78537c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 78547c478bd9Sstevel@tonic-gate 78557c478bd9Sstevel@tonic-gate ndi_devi_exit(hubd->h_dip, circ); 78567c478bd9Sstevel@tonic-gate ndi_devi_exit(rh_dip, rh_circ); 78577c478bd9Sstevel@tonic-gate ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ); 7858e4c316c4SVincent Wang if (child_dip == NULL) { 78597c478bd9Sstevel@tonic-gate rv = EIO; 7860e4c316c4SVincent Wang } else { 7861e4c316c4SVincent Wang ndi_hold_devi(child_dip); 7862e4c316c4SVincent Wang if (ndi_devi_online(child_dip, 0) != NDI_SUCCESS) 7863e4c316c4SVincent Wang rv = EIO; 7864e4c316c4SVincent Wang ndi_rele_devi(child_dip); 78657c478bd9Sstevel@tonic-gate } 78667c478bd9Sstevel@tonic-gate ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ); 78677c478bd9Sstevel@tonic-gate ndi_devi_enter(rh_dip, &rh_circ); 78687c478bd9Sstevel@tonic-gate ndi_devi_enter(hubd->h_dip, &circ); 78697c478bd9Sstevel@tonic-gate 78707c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 78717c478bd9Sstevel@tonic-gate 78727c478bd9Sstevel@tonic-gate break; 78737c478bd9Sstevel@tonic-gate case DEVCTL_AP_GETSTATE: 78747c478bd9Sstevel@tonic-gate switch (hubd_cfgadm_state(hubd, port)) { 78757c478bd9Sstevel@tonic-gate case HUBD_CFGADM_DISCONNECTED: 78767c478bd9Sstevel@tonic-gate /* port previously 'disconnected' by cfgadm */ 78777c478bd9Sstevel@tonic-gate ap_state.ap_rstate = AP_RSTATE_DISCONNECTED; 78787c478bd9Sstevel@tonic-gate ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED; 78797c478bd9Sstevel@tonic-gate ap_state.ap_condition = AP_COND_OK; 78807c478bd9Sstevel@tonic-gate 78817c478bd9Sstevel@tonic-gate break; 78827c478bd9Sstevel@tonic-gate case HUBD_CFGADM_UNCONFIGURED: 78837c478bd9Sstevel@tonic-gate ap_state.ap_rstate = AP_RSTATE_CONNECTED; 78847c478bd9Sstevel@tonic-gate ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED; 78857c478bd9Sstevel@tonic-gate ap_state.ap_condition = AP_COND_OK; 78867c478bd9Sstevel@tonic-gate 78877c478bd9Sstevel@tonic-gate break; 78887c478bd9Sstevel@tonic-gate case HUBD_CFGADM_CONFIGURED: 78897c478bd9Sstevel@tonic-gate ap_state.ap_rstate = AP_RSTATE_CONNECTED; 78907c478bd9Sstevel@tonic-gate ap_state.ap_ostate = AP_OSTATE_CONFIGURED; 78917c478bd9Sstevel@tonic-gate ap_state.ap_condition = AP_COND_OK; 78927c478bd9Sstevel@tonic-gate 78937c478bd9Sstevel@tonic-gate break; 78947c478bd9Sstevel@tonic-gate case HUBD_CFGADM_STILL_REFERENCED: 78957c478bd9Sstevel@tonic-gate ap_state.ap_rstate = AP_RSTATE_EMPTY; 78967c478bd9Sstevel@tonic-gate ap_state.ap_ostate = AP_OSTATE_CONFIGURED; 78977c478bd9Sstevel@tonic-gate ap_state.ap_condition = AP_COND_UNUSABLE; 78987c478bd9Sstevel@tonic-gate 78997c478bd9Sstevel@tonic-gate break; 79007c478bd9Sstevel@tonic-gate case HUBD_CFGADM_EMPTY: 79017c478bd9Sstevel@tonic-gate default: 79027c478bd9Sstevel@tonic-gate ap_state.ap_rstate = AP_RSTATE_EMPTY; 79037c478bd9Sstevel@tonic-gate ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED; 79047c478bd9Sstevel@tonic-gate ap_state.ap_condition = AP_COND_OK; 79057c478bd9Sstevel@tonic-gate 79067c478bd9Sstevel@tonic-gate break; 79077c478bd9Sstevel@tonic-gate } 79087c478bd9Sstevel@tonic-gate 79097c478bd9Sstevel@tonic-gate ap_state.ap_last_change = (time_t)-1; 79107c478bd9Sstevel@tonic-gate ap_state.ap_error_code = 0; 79117c478bd9Sstevel@tonic-gate ap_state.ap_in_transition = 0; 79127c478bd9Sstevel@tonic-gate 79137c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 79147c478bd9Sstevel@tonic-gate "DEVCTL_AP_GETSTATE: " 79157c478bd9Sstevel@tonic-gate "ostate=0x%x, rstate=0x%x, condition=0x%x", 79167c478bd9Sstevel@tonic-gate ap_state.ap_ostate, 79177c478bd9Sstevel@tonic-gate ap_state.ap_rstate, ap_state.ap_condition); 79187c478bd9Sstevel@tonic-gate 79197c478bd9Sstevel@tonic-gate /* copy the return-AP-state information to the user space */ 79207c478bd9Sstevel@tonic-gate if (ndi_dc_return_ap_state(&ap_state, dcp) != NDI_SUCCESS) { 79217c478bd9Sstevel@tonic-gate rv = EFAULT; 79227c478bd9Sstevel@tonic-gate } 79237c478bd9Sstevel@tonic-gate 79247c478bd9Sstevel@tonic-gate break; 79257c478bd9Sstevel@tonic-gate case DEVCTL_AP_CONTROL: 79267c478bd9Sstevel@tonic-gate { 79277c478bd9Sstevel@tonic-gate /* 79287c478bd9Sstevel@tonic-gate * Generic devctl for hardware-specific functionality. 79297c478bd9Sstevel@tonic-gate * For list of sub-commands see hubd_impl.h 79307c478bd9Sstevel@tonic-gate */ 79317c478bd9Sstevel@tonic-gate hubd_ioctl_data_t ioc; /* for 64 byte copies */ 79327c478bd9Sstevel@tonic-gate 79337c478bd9Sstevel@tonic-gate /* copy user ioctl data in first */ 79347c478bd9Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL 79357c478bd9Sstevel@tonic-gate if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) { 79367c478bd9Sstevel@tonic-gate hubd_ioctl_data_32_t ioc32; 79377c478bd9Sstevel@tonic-gate 79387c478bd9Sstevel@tonic-gate if (ddi_copyin((void *)arg, (void *)&ioc32, 7939c0f24e5bSlg sizeof (ioc32), mode) != 0) { 79407c478bd9Sstevel@tonic-gate rv = EFAULT; 79417c478bd9Sstevel@tonic-gate 79427c478bd9Sstevel@tonic-gate break; 79437c478bd9Sstevel@tonic-gate } 79447c478bd9Sstevel@tonic-gate ioc.cmd = (uint_t)ioc32.cmd; 79457c478bd9Sstevel@tonic-gate ioc.port = (uint_t)ioc32.port; 79467c478bd9Sstevel@tonic-gate ioc.get_size = (uint_t)ioc32.get_size; 79477c478bd9Sstevel@tonic-gate ioc.buf = (caddr_t)(uintptr_t)ioc32.buf; 79487c478bd9Sstevel@tonic-gate ioc.bufsiz = (uint_t)ioc32.bufsiz; 79497c478bd9Sstevel@tonic-gate ioc.misc_arg = (uint_t)ioc32.misc_arg; 79507c478bd9Sstevel@tonic-gate } else 79517c478bd9Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */ 79527c478bd9Sstevel@tonic-gate if (ddi_copyin((void *)arg, (void *)&ioc, sizeof (ioc), 79537c478bd9Sstevel@tonic-gate mode) != 0) { 79547c478bd9Sstevel@tonic-gate rv = EFAULT; 79557c478bd9Sstevel@tonic-gate 79567c478bd9Sstevel@tonic-gate break; 79577c478bd9Sstevel@tonic-gate } 79587c478bd9Sstevel@tonic-gate 79597c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_CBOPS, hubd->h_log_handle, 79607c478bd9Sstevel@tonic-gate "DEVCTL_AP_CONTROL: ioc: cmd=0x%x port=%d get_size=%d" 79617c478bd9Sstevel@tonic-gate "\n\tbuf=0x%p, bufsiz=%d, misc_arg=%d", ioc.cmd, 7962112116d8Sfb ioc.port, ioc.get_size, (void *)ioc.buf, ioc.bufsiz, 7963112116d8Sfb ioc.misc_arg); 79647c478bd9Sstevel@tonic-gate 79657c478bd9Sstevel@tonic-gate /* 79667c478bd9Sstevel@tonic-gate * To avoid BE/LE and 32/64 issues, a get_size always 79677c478bd9Sstevel@tonic-gate * returns a 32-bit number. 79687c478bd9Sstevel@tonic-gate */ 79697c478bd9Sstevel@tonic-gate if (ioc.get_size != 0 && ioc.bufsiz != (sizeof (uint32_t))) { 79707c478bd9Sstevel@tonic-gate rv = EINVAL; 79717c478bd9Sstevel@tonic-gate 79727c478bd9Sstevel@tonic-gate break; 79737c478bd9Sstevel@tonic-gate } 79747c478bd9Sstevel@tonic-gate 79757c478bd9Sstevel@tonic-gate switch (ioc.cmd) { 79767c478bd9Sstevel@tonic-gate case USB_DESCR_TYPE_DEV: 79777c478bd9Sstevel@tonic-gate msg = "DEVCTL_AP_CONTROL: GET_DEVICE_DESC"; 79787c478bd9Sstevel@tonic-gate if (ioc.get_size) { 79797c478bd9Sstevel@tonic-gate /* uint32 so this works 32/64 */ 79807c478bd9Sstevel@tonic-gate uint32_t size = sizeof (usb_dev_descr_t); 79817c478bd9Sstevel@tonic-gate 79827c478bd9Sstevel@tonic-gate if (ddi_copyout((void *)&size, ioc.buf, 79837c478bd9Sstevel@tonic-gate ioc.bufsiz, mode) != 0) { 79847c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 79857c478bd9Sstevel@tonic-gate hubd->h_log_handle, 79867c478bd9Sstevel@tonic-gate "%s: get_size copyout failed", msg); 79877c478bd9Sstevel@tonic-gate rv = EIO; 79887c478bd9Sstevel@tonic-gate 79897c478bd9Sstevel@tonic-gate break; 79907c478bd9Sstevel@tonic-gate } 79917c478bd9Sstevel@tonic-gate } else { /* send out the actual descr */ 79927c478bd9Sstevel@tonic-gate usb_dev_descr_t *dev_descrp; 79937c478bd9Sstevel@tonic-gate 79947c478bd9Sstevel@tonic-gate /* check child_dip */ 79957c478bd9Sstevel@tonic-gate if ((child_dip = hubd_get_child_dip(hubd, 79967c478bd9Sstevel@tonic-gate ioc.port)) == NULL) { 79977c478bd9Sstevel@tonic-gate rv = EINVAL; 79987c478bd9Sstevel@tonic-gate 79997c478bd9Sstevel@tonic-gate break; 80007c478bd9Sstevel@tonic-gate } 80017c478bd9Sstevel@tonic-gate 80027c478bd9Sstevel@tonic-gate dev_descrp = usb_get_dev_descr(child_dip); 80037c478bd9Sstevel@tonic-gate if (ioc.bufsiz != sizeof (*dev_descrp)) { 80047c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 80057c478bd9Sstevel@tonic-gate hubd->h_log_handle, 80067c478bd9Sstevel@tonic-gate "%s: bufsize passed (%d) != sizeof " 80077c478bd9Sstevel@tonic-gate "usba_device_descr_t (%d)", msg, 80087c478bd9Sstevel@tonic-gate ioc.bufsiz, dev_descrp->bLength); 80097c478bd9Sstevel@tonic-gate rv = EINVAL; 80107c478bd9Sstevel@tonic-gate 80117c478bd9Sstevel@tonic-gate break; 80127c478bd9Sstevel@tonic-gate } 80137c478bd9Sstevel@tonic-gate 80147c478bd9Sstevel@tonic-gate if (ddi_copyout((void *)dev_descrp, 80157c478bd9Sstevel@tonic-gate ioc.buf, ioc.bufsiz, mode) != 0) { 80167c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 80177c478bd9Sstevel@tonic-gate hubd->h_log_handle, 80187c478bd9Sstevel@tonic-gate "%s: copyout failed.", msg); 80197c478bd9Sstevel@tonic-gate rv = EIO; 80207c478bd9Sstevel@tonic-gate 80217c478bd9Sstevel@tonic-gate break; 80227c478bd9Sstevel@tonic-gate } 80237c478bd9Sstevel@tonic-gate } 80247c478bd9Sstevel@tonic-gate break; 80257c478bd9Sstevel@tonic-gate case USB_DESCR_TYPE_STRING: 80267c478bd9Sstevel@tonic-gate { 80277c478bd9Sstevel@tonic-gate char *str; 80287c478bd9Sstevel@tonic-gate uint32_t size; 80297c478bd9Sstevel@tonic-gate usba_device_t *usba_device; 80307c478bd9Sstevel@tonic-gate 80317c478bd9Sstevel@tonic-gate msg = "DEVCTL_AP_CONTROL: GET_STRING_DESCR"; 80327c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 80337c478bd9Sstevel@tonic-gate "%s: string request: %d", msg, ioc.misc_arg); 80347c478bd9Sstevel@tonic-gate 80357c478bd9Sstevel@tonic-gate /* recheck */ 80367c478bd9Sstevel@tonic-gate if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) == 80377c478bd9Sstevel@tonic-gate NULL) { 80387c478bd9Sstevel@tonic-gate rv = EINVAL; 80397c478bd9Sstevel@tonic-gate 80407c478bd9Sstevel@tonic-gate break; 80417c478bd9Sstevel@tonic-gate } 80427c478bd9Sstevel@tonic-gate usba_device = usba_get_usba_device(child_dip); 80437c478bd9Sstevel@tonic-gate 80447c478bd9Sstevel@tonic-gate switch (ioc.misc_arg) { 80457c478bd9Sstevel@tonic-gate case HUBD_MFG_STR: 80467c478bd9Sstevel@tonic-gate str = usba_device->usb_mfg_str; 80477c478bd9Sstevel@tonic-gate 80487c478bd9Sstevel@tonic-gate break; 80497c478bd9Sstevel@tonic-gate case HUBD_PRODUCT_STR: 80507c478bd9Sstevel@tonic-gate str = usba_device->usb_product_str; 80517c478bd9Sstevel@tonic-gate 80527c478bd9Sstevel@tonic-gate break; 80537c478bd9Sstevel@tonic-gate case HUBD_SERIALNO_STR: 80547c478bd9Sstevel@tonic-gate str = usba_device->usb_serialno_str; 80557c478bd9Sstevel@tonic-gate 80567c478bd9Sstevel@tonic-gate break; 80577c478bd9Sstevel@tonic-gate case HUBD_CFG_DESCR_STR: 80587c478bd9Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex); 80597c478bd9Sstevel@tonic-gate str = usba_device->usb_cfg_str_descr[ 8060c0f24e5bSlg usba_device->usb_active_cfg_ndx]; 80617c478bd9Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex); 80627c478bd9Sstevel@tonic-gate 80637c478bd9Sstevel@tonic-gate break; 80647c478bd9Sstevel@tonic-gate default: 80657c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 80667c478bd9Sstevel@tonic-gate hubd->h_log_handle, 80677c478bd9Sstevel@tonic-gate "%s: Invalid string request", msg); 80687c478bd9Sstevel@tonic-gate rv = EINVAL; 80697c478bd9Sstevel@tonic-gate 80707c478bd9Sstevel@tonic-gate break; 80717c478bd9Sstevel@tonic-gate } /* end of switch */ 80727c478bd9Sstevel@tonic-gate 80737c478bd9Sstevel@tonic-gate if (rv != 0) { 80747c478bd9Sstevel@tonic-gate 80757c478bd9Sstevel@tonic-gate break; 80767c478bd9Sstevel@tonic-gate } 80777c478bd9Sstevel@tonic-gate 80787c478bd9Sstevel@tonic-gate size = (str != NULL) ? strlen(str) + 1 : 0; 80797c478bd9Sstevel@tonic-gate if (ioc.get_size) { 80807c478bd9Sstevel@tonic-gate if (ddi_copyout((void *)&size, ioc.buf, 80817c478bd9Sstevel@tonic-gate ioc.bufsiz, mode) != 0) { 80827c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 80837c478bd9Sstevel@tonic-gate hubd->h_log_handle, 80847c478bd9Sstevel@tonic-gate "%s: copyout of size failed.", msg); 80857c478bd9Sstevel@tonic-gate rv = EIO; 80867c478bd9Sstevel@tonic-gate 80877c478bd9Sstevel@tonic-gate break; 80887c478bd9Sstevel@tonic-gate } 80897c478bd9Sstevel@tonic-gate } else { 80907c478bd9Sstevel@tonic-gate if (size == 0) { 80917c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_CBOPS, 80927c478bd9Sstevel@tonic-gate hubd->h_log_handle, 80937c478bd9Sstevel@tonic-gate "%s: String is NULL", msg); 80947c478bd9Sstevel@tonic-gate rv = EINVAL; 80957c478bd9Sstevel@tonic-gate 80967c478bd9Sstevel@tonic-gate break; 80977c478bd9Sstevel@tonic-gate } 80987c478bd9Sstevel@tonic-gate 80997c478bd9Sstevel@tonic-gate if (ioc.bufsiz != size) { 81007c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 81017c478bd9Sstevel@tonic-gate hubd->h_log_handle, 81027c478bd9Sstevel@tonic-gate "%s: string buf size wrong", msg); 81037c478bd9Sstevel@tonic-gate rv = EINVAL; 81047c478bd9Sstevel@tonic-gate 81057c478bd9Sstevel@tonic-gate break; 81067c478bd9Sstevel@tonic-gate } 81077c478bd9Sstevel@tonic-gate 81087c478bd9Sstevel@tonic-gate if (ddi_copyout((void *)str, ioc.buf, 81097c478bd9Sstevel@tonic-gate ioc.bufsiz, mode) != 0) { 81107c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 81117c478bd9Sstevel@tonic-gate hubd->h_log_handle, 81127c478bd9Sstevel@tonic-gate "%s: copyout failed.", msg); 81137c478bd9Sstevel@tonic-gate rv = EIO; 81147c478bd9Sstevel@tonic-gate 81157c478bd9Sstevel@tonic-gate break; 81167c478bd9Sstevel@tonic-gate } 81177c478bd9Sstevel@tonic-gate } 81187c478bd9Sstevel@tonic-gate break; 81197c478bd9Sstevel@tonic-gate } 81207c478bd9Sstevel@tonic-gate case HUBD_GET_CFGADM_NAME: 81217c478bd9Sstevel@tonic-gate { 81227c478bd9Sstevel@tonic-gate uint32_t name_len; 81237c478bd9Sstevel@tonic-gate const char *name; 81247c478bd9Sstevel@tonic-gate 81257c478bd9Sstevel@tonic-gate /* recheck */ 81267c478bd9Sstevel@tonic-gate if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) == 81277c478bd9Sstevel@tonic-gate NULL) { 81287c478bd9Sstevel@tonic-gate rv = EINVAL; 81297c478bd9Sstevel@tonic-gate 81307c478bd9Sstevel@tonic-gate break; 81317c478bd9Sstevel@tonic-gate } 81327c478bd9Sstevel@tonic-gate name = ddi_node_name(child_dip); 81337c478bd9Sstevel@tonic-gate if (name == NULL) { 81347c478bd9Sstevel@tonic-gate name = "unsupported"; 81357c478bd9Sstevel@tonic-gate } 81367c478bd9Sstevel@tonic-gate name_len = strlen(name) + 1; 81377c478bd9Sstevel@tonic-gate 81387c478bd9Sstevel@tonic-gate msg = "DEVCTL_AP_CONTROL: HUBD_GET_CFGADM_NAME"; 81397c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 81407c478bd9Sstevel@tonic-gate "%s: name=%s name_len=%d", msg, name, name_len); 81417c478bd9Sstevel@tonic-gate 81427c478bd9Sstevel@tonic-gate if (ioc.get_size) { 81437c478bd9Sstevel@tonic-gate if (ddi_copyout((void *)&name_len, 81447c478bd9Sstevel@tonic-gate ioc.buf, ioc.bufsiz, mode) != 0) { 81457c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 81467c478bd9Sstevel@tonic-gate hubd->h_log_handle, 81477c478bd9Sstevel@tonic-gate "%s: copyout of size failed", msg); 81487c478bd9Sstevel@tonic-gate rv = EIO; 81497c478bd9Sstevel@tonic-gate 81507c478bd9Sstevel@tonic-gate break; 81517c478bd9Sstevel@tonic-gate } 81527c478bd9Sstevel@tonic-gate } else { 81537c478bd9Sstevel@tonic-gate if (ioc.bufsiz != name_len) { 81547c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 81557c478bd9Sstevel@tonic-gate hubd->h_log_handle, 81567c478bd9Sstevel@tonic-gate "%s: string buf length wrong", msg); 81577c478bd9Sstevel@tonic-gate rv = EINVAL; 81587c478bd9Sstevel@tonic-gate 81597c478bd9Sstevel@tonic-gate break; 81607c478bd9Sstevel@tonic-gate } 81617c478bd9Sstevel@tonic-gate 81627c478bd9Sstevel@tonic-gate if (ddi_copyout((void *)name, ioc.buf, 81637c478bd9Sstevel@tonic-gate ioc.bufsiz, mode) != 0) { 81647c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 81657c478bd9Sstevel@tonic-gate hubd->h_log_handle, 81667c478bd9Sstevel@tonic-gate "%s: copyout failed.", msg); 81677c478bd9Sstevel@tonic-gate rv = EIO; 81687c478bd9Sstevel@tonic-gate 81697c478bd9Sstevel@tonic-gate break; 81707c478bd9Sstevel@tonic-gate } 81717c478bd9Sstevel@tonic-gate } 81727c478bd9Sstevel@tonic-gate 81737c478bd9Sstevel@tonic-gate break; 81747c478bd9Sstevel@tonic-gate } 81757c478bd9Sstevel@tonic-gate 81767c478bd9Sstevel@tonic-gate /* 81777c478bd9Sstevel@tonic-gate * Return the config index for the currently-configured 81787c478bd9Sstevel@tonic-gate * configuration. 81797c478bd9Sstevel@tonic-gate */ 81807c478bd9Sstevel@tonic-gate case HUBD_GET_CURRENT_CONFIG: 81817c478bd9Sstevel@tonic-gate { 81827c478bd9Sstevel@tonic-gate uint_t config_index; 81837c478bd9Sstevel@tonic-gate uint32_t size = sizeof (config_index); 81847c478bd9Sstevel@tonic-gate usba_device_t *usba_device; 81857c478bd9Sstevel@tonic-gate 81867c478bd9Sstevel@tonic-gate msg = "DEVCTL_AP_CONTROL: GET_CURRENT_CONFIG"; 81877c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 81887c478bd9Sstevel@tonic-gate "%s", msg); 81897c478bd9Sstevel@tonic-gate 81907c478bd9Sstevel@tonic-gate /* 81917c478bd9Sstevel@tonic-gate * Return the config index for the configuration 81927c478bd9Sstevel@tonic-gate * currently in use. 81937c478bd9Sstevel@tonic-gate * Recheck if child_dip exists 81947c478bd9Sstevel@tonic-gate */ 81957c478bd9Sstevel@tonic-gate if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) == 81967c478bd9Sstevel@tonic-gate NULL) { 81977c478bd9Sstevel@tonic-gate rv = EINVAL; 81987c478bd9Sstevel@tonic-gate 81997c478bd9Sstevel@tonic-gate break; 82007c478bd9Sstevel@tonic-gate } 82017c478bd9Sstevel@tonic-gate 82027c478bd9Sstevel@tonic-gate usba_device = usba_get_usba_device(child_dip); 82037c478bd9Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex); 82047c478bd9Sstevel@tonic-gate config_index = usba_device->usb_active_cfg_ndx; 82057c478bd9Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex); 82067c478bd9Sstevel@tonic-gate 82077c478bd9Sstevel@tonic-gate if (ioc.get_size) { 82087c478bd9Sstevel@tonic-gate if (ddi_copyout((void *)&size, 82097c478bd9Sstevel@tonic-gate ioc.buf, ioc.bufsiz, mode) != 0) { 82107c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 82117c478bd9Sstevel@tonic-gate hubd->h_log_handle, 82127c478bd9Sstevel@tonic-gate "%s: copyout of size failed.", msg); 82137c478bd9Sstevel@tonic-gate rv = EIO; 82147c478bd9Sstevel@tonic-gate 82157c478bd9Sstevel@tonic-gate break; 82167c478bd9Sstevel@tonic-gate } 82177c478bd9Sstevel@tonic-gate } else { 82187c478bd9Sstevel@tonic-gate if (ioc.bufsiz != size) { 82197c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 82207c478bd9Sstevel@tonic-gate hubd->h_log_handle, 82217c478bd9Sstevel@tonic-gate "%s: buffer size wrong", msg); 82227c478bd9Sstevel@tonic-gate rv = EINVAL; 82237c478bd9Sstevel@tonic-gate 82247c478bd9Sstevel@tonic-gate break; 82257c478bd9Sstevel@tonic-gate } 82267c478bd9Sstevel@tonic-gate if (ddi_copyout((void *)&config_index, 82277c478bd9Sstevel@tonic-gate ioc.buf, ioc.bufsiz, mode) != 0) { 82287c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 82297c478bd9Sstevel@tonic-gate hubd->h_log_handle, 82307c478bd9Sstevel@tonic-gate "%s: copyout failed", msg); 82317c478bd9Sstevel@tonic-gate rv = EIO; 82327c478bd9Sstevel@tonic-gate } 82337c478bd9Sstevel@tonic-gate } 82347c478bd9Sstevel@tonic-gate 82357c478bd9Sstevel@tonic-gate break; 82367c478bd9Sstevel@tonic-gate } 82377c478bd9Sstevel@tonic-gate case HUBD_GET_DEVICE_PATH: 82387c478bd9Sstevel@tonic-gate { 82397c478bd9Sstevel@tonic-gate char *path; 82407c478bd9Sstevel@tonic-gate uint32_t size; 82417c478bd9Sstevel@tonic-gate 82427c478bd9Sstevel@tonic-gate msg = "DEVCTL_AP_CONTROL: GET_DEVICE_PATH"; 82437c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 82447c478bd9Sstevel@tonic-gate "%s", msg); 82457c478bd9Sstevel@tonic-gate 82467c478bd9Sstevel@tonic-gate /* Recheck if child_dip exists */ 82477c478bd9Sstevel@tonic-gate if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) == 82487c478bd9Sstevel@tonic-gate NULL) { 82497c478bd9Sstevel@tonic-gate rv = EINVAL; 82507c478bd9Sstevel@tonic-gate 82517c478bd9Sstevel@tonic-gate break; 82527c478bd9Sstevel@tonic-gate } 82537c478bd9Sstevel@tonic-gate 82547c478bd9Sstevel@tonic-gate /* ddi_pathname doesn't supply /devices, so we do. */ 82557c478bd9Sstevel@tonic-gate path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 82567c478bd9Sstevel@tonic-gate (void) strcpy(path, "/devices"); 82577c478bd9Sstevel@tonic-gate (void) ddi_pathname(child_dip, path + strlen(path)); 82587c478bd9Sstevel@tonic-gate size = strlen(path) + 1; 82597c478bd9Sstevel@tonic-gate 82607c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 82617c478bd9Sstevel@tonic-gate "%s: device path=%s size=%d", msg, path, size); 82627c478bd9Sstevel@tonic-gate 82637c478bd9Sstevel@tonic-gate if (ioc.get_size) { 82647c478bd9Sstevel@tonic-gate if (ddi_copyout((void *)&size, 82657c478bd9Sstevel@tonic-gate ioc.buf, ioc.bufsiz, mode) != 0) { 82667c478bd9Sstevel@tonic-gate 82677c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 82687c478bd9Sstevel@tonic-gate hubd->h_log_handle, 82697c478bd9Sstevel@tonic-gate "%s: copyout of size failed.", msg); 82707c478bd9Sstevel@tonic-gate rv = EIO; 82717c478bd9Sstevel@tonic-gate } 82727c478bd9Sstevel@tonic-gate } else { 82737c478bd9Sstevel@tonic-gate if (ioc.bufsiz != size) { 82747c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 82757c478bd9Sstevel@tonic-gate hubd->h_log_handle, 82767c478bd9Sstevel@tonic-gate "%s: buffer wrong size.", msg); 82777c478bd9Sstevel@tonic-gate rv = EINVAL; 82787c478bd9Sstevel@tonic-gate } else if (ddi_copyout((void *)path, 82797c478bd9Sstevel@tonic-gate ioc.buf, ioc.bufsiz, mode) != 0) { 82807c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 82817c478bd9Sstevel@tonic-gate hubd->h_log_handle, 82827c478bd9Sstevel@tonic-gate "%s: copyout failed.", msg); 82837c478bd9Sstevel@tonic-gate rv = EIO; 82847c478bd9Sstevel@tonic-gate } 82857c478bd9Sstevel@tonic-gate } 82867c478bd9Sstevel@tonic-gate kmem_free(path, MAXPATHLEN); 82877c478bd9Sstevel@tonic-gate 82887c478bd9Sstevel@tonic-gate break; 82897c478bd9Sstevel@tonic-gate } 82907c478bd9Sstevel@tonic-gate case HUBD_REFRESH_DEVDB: 82917c478bd9Sstevel@tonic-gate msg = "DEVCTL_AP_CONTROL: HUBD_REFRESH_DEVDB"; 82927c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_CBOPS, hubd->h_log_handle, 82937c478bd9Sstevel@tonic-gate "%s", msg); 82947c478bd9Sstevel@tonic-gate 82957c478bd9Sstevel@tonic-gate if ((rv = usba_devdb_refresh()) != USB_SUCCESS) { 82967c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 82977c478bd9Sstevel@tonic-gate hubd->h_log_handle, 82987c478bd9Sstevel@tonic-gate "%s: Failed: %d", msg, rv); 82997c478bd9Sstevel@tonic-gate rv = EIO; 83007c478bd9Sstevel@tonic-gate } 83017c478bd9Sstevel@tonic-gate 83027c478bd9Sstevel@tonic-gate break; 83037c478bd9Sstevel@tonic-gate default: 83047c478bd9Sstevel@tonic-gate rv = ENOTSUP; 83057c478bd9Sstevel@tonic-gate } /* end switch */ 83067c478bd9Sstevel@tonic-gate 83077c478bd9Sstevel@tonic-gate break; 83087c478bd9Sstevel@tonic-gate } 83097c478bd9Sstevel@tonic-gate 83107c478bd9Sstevel@tonic-gate default: 83117c478bd9Sstevel@tonic-gate rv = ENOTTY; 83127c478bd9Sstevel@tonic-gate } 83137c478bd9Sstevel@tonic-gate 83147c478bd9Sstevel@tonic-gate if (dcp) { 83157c478bd9Sstevel@tonic-gate ndi_dc_freehdl(dcp); 83167c478bd9Sstevel@tonic-gate } 83177c478bd9Sstevel@tonic-gate 83187c478bd9Sstevel@tonic-gate /* allow hotplug thread now */ 83197c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread--; 83207c478bd9Sstevel@tonic-gate 83217c478bd9Sstevel@tonic-gate if ((hubd->h_dev_state == USB_DEV_ONLINE) && 83227c478bd9Sstevel@tonic-gate hubd->h_ep1_ph && (prev_pipe_state == USB_PIPE_STATE_ACTIVE)) { 83237c478bd9Sstevel@tonic-gate hubd_start_polling(hubd, 0); 83247c478bd9Sstevel@tonic-gate } 83257c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 83267c478bd9Sstevel@tonic-gate 83277c478bd9Sstevel@tonic-gate ndi_devi_exit(hubd->h_dip, circ); 83287c478bd9Sstevel@tonic-gate ndi_devi_exit(rh_dip, rh_circ); 83297c478bd9Sstevel@tonic-gate ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ); 83307c478bd9Sstevel@tonic-gate 83317c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 83327c478bd9Sstevel@tonic-gate hubd_pm_idle_component(hubd, hubd->h_dip, 0); 83337c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 83347c478bd9Sstevel@tonic-gate 83357c478bd9Sstevel@tonic-gate return (rv); 83367c478bd9Sstevel@tonic-gate } 83377c478bd9Sstevel@tonic-gate 83387c478bd9Sstevel@tonic-gate 83397c478bd9Sstevel@tonic-gate /* 83407c478bd9Sstevel@tonic-gate * Helper func used only to help construct the names for the attachment point 83417c478bd9Sstevel@tonic-gate * minor nodes. Used only in usba_hubdi_attach. 83427c478bd9Sstevel@tonic-gate * Returns whether it found ancestry or not (USB_SUCCESS if yes). 83437c478bd9Sstevel@tonic-gate * ports between the root hub and the device represented by dip. 83447c478bd9Sstevel@tonic-gate * E.g., "2.4.3.1" means this device is 83457c478bd9Sstevel@tonic-gate * plugged into port 1 of a hub that is 83467c478bd9Sstevel@tonic-gate * plugged into port 3 of a hub that is 83477c478bd9Sstevel@tonic-gate * plugged into port 4 of a hub that is 83487c478bd9Sstevel@tonic-gate * plugged into port 2 of the root hub. 83497c478bd9Sstevel@tonic-gate * NOTE: Max ap_id path len is HUBD_APID_NAMELEN (32 chars), which is 83507c478bd9Sstevel@tonic-gate * more than sufficient (as hubs are a max 6 levels deep, port needs 3 83517c478bd9Sstevel@tonic-gate * chars plus NULL each) 83527c478bd9Sstevel@tonic-gate */ 8353ff0e937bSRaymond Chen void 83547c478bd9Sstevel@tonic-gate hubd_get_ancestry_str(hubd_t *hubd) 83557c478bd9Sstevel@tonic-gate { 8356ff0e937bSRaymond Chen char ap_name[HUBD_APID_NAMELEN]; 8357ff0e937bSRaymond Chen dev_info_t *pdip; 8358ff0e937bSRaymond Chen hubd_t *phubd; 8359ff0e937bSRaymond Chen usb_port_t port; 83607c478bd9Sstevel@tonic-gate 83617c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 8362112116d8Sfb "hubd_get_ancestry_str: hubd=0x%p", (void *)hubd); 83637c478bd9Sstevel@tonic-gate 8364ff0e937bSRaymond Chen ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 8365ff0e937bSRaymond Chen 8366ff0e937bSRaymond Chen /* 8367ff0e937bSRaymond Chen * The function is extended to support wire adapter class 8368ff0e937bSRaymond Chen * devices introduced by WUSB spec. The node name is no 8369ff0e937bSRaymond Chen * longer "hub" only. 8370ff0e937bSRaymond Chen * Generate the ap_id str based on the parent and child 8371ff0e937bSRaymond Chen * relationship instead of retrieving it from the hub 8372ff0e937bSRaymond Chen * device path, which simplifies the algorithm. 8373ff0e937bSRaymond Chen */ 8374ff0e937bSRaymond Chen if (usba_is_root_hub(hubd->h_dip)) { 8375ff0e937bSRaymond Chen hubd->h_ancestry_str[0] = '\0'; 8376ff0e937bSRaymond Chen } else { 8377ff0e937bSRaymond Chen port = hubd->h_usba_device->usb_port; 8378ff0e937bSRaymond Chen mutex_exit(HUBD_MUTEX(hubd)); 83797c478bd9Sstevel@tonic-gate 8380ff0e937bSRaymond Chen pdip = ddi_get_parent(hubd->h_dip); 83817c478bd9Sstevel@tonic-gate /* 8382ff0e937bSRaymond Chen * The parent of wire adapter device might be usb_mid. 8383ff0e937bSRaymond Chen * Need to look further up for hub device 83847c478bd9Sstevel@tonic-gate */ 8385ff0e937bSRaymond Chen if (strcmp(ddi_driver_name(pdip), "usb_mid") == 0) { 8386ff0e937bSRaymond Chen pdip = ddi_get_parent(pdip); 8387ff0e937bSRaymond Chen ASSERT(pdip != NULL); 83887c478bd9Sstevel@tonic-gate } 83897c478bd9Sstevel@tonic-gate 8390ff0e937bSRaymond Chen phubd = hubd_get_soft_state(pdip); 83917c478bd9Sstevel@tonic-gate 8392ff0e937bSRaymond Chen mutex_enter(HUBD_MUTEX(phubd)); 8393ff0e937bSRaymond Chen (void) snprintf(ap_name, HUBD_APID_NAMELEN, "%s%d", 8394ff0e937bSRaymond Chen phubd->h_ancestry_str, port); 8395ff0e937bSRaymond Chen mutex_exit(HUBD_MUTEX(phubd)); 83967c478bd9Sstevel@tonic-gate 8397ff0e937bSRaymond Chen mutex_enter(HUBD_MUTEX(hubd)); 8398ff0e937bSRaymond Chen (void) strcpy(hubd->h_ancestry_str, ap_name); 83997c478bd9Sstevel@tonic-gate (void) strcat(hubd->h_ancestry_str, "."); 84007c478bd9Sstevel@tonic-gate } 84017c478bd9Sstevel@tonic-gate } 84027c478bd9Sstevel@tonic-gate 84037c478bd9Sstevel@tonic-gate 84047c478bd9Sstevel@tonic-gate /* Get which port to operate on. */ 84057c478bd9Sstevel@tonic-gate static usb_port_t 84067c478bd9Sstevel@tonic-gate hubd_get_port_num(hubd_t *hubd, struct devctl_iocdata *dcp) 84077c478bd9Sstevel@tonic-gate { 84087c478bd9Sstevel@tonic-gate int32_t port; 84097c478bd9Sstevel@tonic-gate 84107c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 84117c478bd9Sstevel@tonic-gate 84127c478bd9Sstevel@tonic-gate /* Get which port to operate on. */ 84137c478bd9Sstevel@tonic-gate if (nvlist_lookup_int32(ndi_dc_get_ap_data(dcp), "port", &port) != 0) { 84147c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, hubd->h_log_handle, 84157c478bd9Sstevel@tonic-gate "hubd_get_port_num: port lookup failed"); 84167c478bd9Sstevel@tonic-gate port = 0; 84177c478bd9Sstevel@tonic-gate } 84187c478bd9Sstevel@tonic-gate 84197c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 8420112116d8Sfb "hubd_get_port_num: hubd=0x%p, port=%d", (void *)hubd, port); 84217c478bd9Sstevel@tonic-gate 84227c478bd9Sstevel@tonic-gate return ((usb_port_t)port); 84237c478bd9Sstevel@tonic-gate } 84247c478bd9Sstevel@tonic-gate 84257c478bd9Sstevel@tonic-gate 84267c478bd9Sstevel@tonic-gate /* check if child still exists */ 84277c478bd9Sstevel@tonic-gate static dev_info_t * 84287c478bd9Sstevel@tonic-gate hubd_get_child_dip(hubd_t *hubd, usb_port_t port) 84297c478bd9Sstevel@tonic-gate { 84307c478bd9Sstevel@tonic-gate dev_info_t *child_dip = hubd->h_children_dips[port]; 84317c478bd9Sstevel@tonic-gate 84327c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 8433112116d8Sfb "hubd_get_child_dip: hubd=0x%p, port=%d", (void *)hubd, port); 84347c478bd9Sstevel@tonic-gate 84357c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 84367c478bd9Sstevel@tonic-gate 84377c478bd9Sstevel@tonic-gate return (child_dip); 84387c478bd9Sstevel@tonic-gate } 84397c478bd9Sstevel@tonic-gate 84407c478bd9Sstevel@tonic-gate 84417c478bd9Sstevel@tonic-gate /* 84427c478bd9Sstevel@tonic-gate * hubd_cfgadm_state: 84437c478bd9Sstevel@tonic-gate * 84447c478bd9Sstevel@tonic-gate * child_dip list port_state cfgadm_state 84457c478bd9Sstevel@tonic-gate * -------------- ---------- ------------ 84467c478bd9Sstevel@tonic-gate * != NULL connected configured or 84477c478bd9Sstevel@tonic-gate * unconfigured 84487c478bd9Sstevel@tonic-gate * != NULL not connected disconnect but 84497c478bd9Sstevel@tonic-gate * busy/still referenced 84507c478bd9Sstevel@tonic-gate * NULL connected logically disconnected 84517c478bd9Sstevel@tonic-gate * NULL not connected empty 84527c478bd9Sstevel@tonic-gate */ 84537c478bd9Sstevel@tonic-gate static uint_t 84547c478bd9Sstevel@tonic-gate hubd_cfgadm_state(hubd_t *hubd, usb_port_t port) 84557c478bd9Sstevel@tonic-gate { 84567c478bd9Sstevel@tonic-gate uint_t state; 84577c478bd9Sstevel@tonic-gate dev_info_t *child_dip = hubd_get_child_dip(hubd, port); 84587c478bd9Sstevel@tonic-gate 84597c478bd9Sstevel@tonic-gate if (child_dip) { 84607c478bd9Sstevel@tonic-gate if (hubd->h_port_state[port] & PORT_STATUS_CCS) { 84617c478bd9Sstevel@tonic-gate /* 84627c478bd9Sstevel@tonic-gate * connected, now check if driver exists 84637c478bd9Sstevel@tonic-gate */ 84647c478bd9Sstevel@tonic-gate if (DEVI_IS_DEVICE_OFFLINE(child_dip) || 8465737d277aScth !i_ddi_devi_attached(child_dip)) { 84667c478bd9Sstevel@tonic-gate state = HUBD_CFGADM_UNCONFIGURED; 84677c478bd9Sstevel@tonic-gate } else { 84687c478bd9Sstevel@tonic-gate state = HUBD_CFGADM_CONFIGURED; 84697c478bd9Sstevel@tonic-gate } 84707c478bd9Sstevel@tonic-gate } else { 84717c478bd9Sstevel@tonic-gate /* 84727c478bd9Sstevel@tonic-gate * this means that the dip is around for 84737c478bd9Sstevel@tonic-gate * a device that is still referenced but 84747c478bd9Sstevel@tonic-gate * has been yanked out. So the cfgadm info 84757c478bd9Sstevel@tonic-gate * for this state should be EMPTY (port empty) 84767c478bd9Sstevel@tonic-gate * and CONFIGURED (dip still valid). 84777c478bd9Sstevel@tonic-gate */ 84787c478bd9Sstevel@tonic-gate state = HUBD_CFGADM_STILL_REFERENCED; 84797c478bd9Sstevel@tonic-gate } 84807c478bd9Sstevel@tonic-gate } else { 84817c478bd9Sstevel@tonic-gate /* connected but no child dip */ 84827c478bd9Sstevel@tonic-gate if (hubd->h_port_state[port] & PORT_STATUS_CCS) { 84837c478bd9Sstevel@tonic-gate /* logically disconnected */ 84847c478bd9Sstevel@tonic-gate state = HUBD_CFGADM_DISCONNECTED; 84857c478bd9Sstevel@tonic-gate } else { 84867c478bd9Sstevel@tonic-gate /* physically disconnected */ 84877c478bd9Sstevel@tonic-gate state = HUBD_CFGADM_EMPTY; 84887c478bd9Sstevel@tonic-gate } 84897c478bd9Sstevel@tonic-gate } 84907c478bd9Sstevel@tonic-gate 84917c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 84927c478bd9Sstevel@tonic-gate "hubd_cfgadm_state: hubd=0x%p, port=%d state=0x%x", 8493112116d8Sfb (void *)hubd, port, state); 84947c478bd9Sstevel@tonic-gate 84957c478bd9Sstevel@tonic-gate return (state); 84967c478bd9Sstevel@tonic-gate } 84977c478bd9Sstevel@tonic-gate 84987c478bd9Sstevel@tonic-gate 84997c478bd9Sstevel@tonic-gate /* 85007c478bd9Sstevel@tonic-gate * hubd_toggle_port: 85017c478bd9Sstevel@tonic-gate */ 85027c478bd9Sstevel@tonic-gate static int 85037c478bd9Sstevel@tonic-gate hubd_toggle_port(hubd_t *hubd, usb_port_t port) 85047c478bd9Sstevel@tonic-gate { 85057c478bd9Sstevel@tonic-gate int wait; 85067c478bd9Sstevel@tonic-gate uint_t retry; 85077c478bd9Sstevel@tonic-gate uint16_t status; 85087c478bd9Sstevel@tonic-gate uint16_t change; 85097c478bd9Sstevel@tonic-gate 85107c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 8511112116d8Sfb "hubd_toggle_port: hubd=0x%p, port=%d", (void *)hubd, port); 85127c478bd9Sstevel@tonic-gate 85137c478bd9Sstevel@tonic-gate if ((hubd_disable_port_power(hubd, port)) != USB_SUCCESS) { 85147c478bd9Sstevel@tonic-gate 85157c478bd9Sstevel@tonic-gate return (USB_FAILURE); 85167c478bd9Sstevel@tonic-gate } 85177c478bd9Sstevel@tonic-gate 85187c478bd9Sstevel@tonic-gate /* 85197c478bd9Sstevel@tonic-gate * see hubd_enable_all_port_power() which 85207c478bd9Sstevel@tonic-gate * requires longer delay for hubs. 85217c478bd9Sstevel@tonic-gate */ 85227c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 85237c478bd9Sstevel@tonic-gate delay(drv_usectohz(hubd_device_delay / 10)); 85247c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 85257c478bd9Sstevel@tonic-gate 85267c478bd9Sstevel@tonic-gate /* 85277c478bd9Sstevel@tonic-gate * According to section 11.11 of USB, for hubs with no power 85287c478bd9Sstevel@tonic-gate * switches, bPwrOn2PwrGood is zero. But we wait for some 85297c478bd9Sstevel@tonic-gate * arbitrary time to enable power to become stable. 85307c478bd9Sstevel@tonic-gate * 85317c478bd9Sstevel@tonic-gate * If an hub supports port power swicthing, we need to wait 8532993e3fafSRobert Mustacchi * at least 20ms before accesing corresonding usb port. Note 8533993e3fafSRobert Mustacchi * this member is stored in the h_power_good member. 85347c478bd9Sstevel@tonic-gate */ 8535993e3fafSRobert Mustacchi if ((hubd->h_hub_chars & HUB_CHARS_NO_POWER_SWITCHING) || 8536993e3fafSRobert Mustacchi (hubd->h_power_good == 0)) { 85377c478bd9Sstevel@tonic-gate wait = hubd_device_delay / 10; 85387c478bd9Sstevel@tonic-gate } else { 85397c478bd9Sstevel@tonic-gate wait = max(HUB_DEFAULT_POPG, 8540993e3fafSRobert Mustacchi hubd->h_power_good) * 2 * 1000; 85417c478bd9Sstevel@tonic-gate } 85427c478bd9Sstevel@tonic-gate 85437c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 85447c478bd9Sstevel@tonic-gate "hubd_toggle_port: popg=%d wait=%d", 8545993e3fafSRobert Mustacchi hubd->h_power_good, wait); 85467c478bd9Sstevel@tonic-gate 85477c478bd9Sstevel@tonic-gate retry = 0; 85487c478bd9Sstevel@tonic-gate 85497c478bd9Sstevel@tonic-gate do { 85507c478bd9Sstevel@tonic-gate (void) hubd_enable_port_power(hubd, port); 85517c478bd9Sstevel@tonic-gate 85527c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 85537c478bd9Sstevel@tonic-gate delay(drv_usectohz(wait)); 85547c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 85557c478bd9Sstevel@tonic-gate 85567c478bd9Sstevel@tonic-gate /* Get port status */ 85577c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 8558993e3fafSRobert Mustacchi &status, &change, NULL, 0); 85597c478bd9Sstevel@tonic-gate 85607c478bd9Sstevel@tonic-gate /* For retry if any, use some extra delay */ 85617c478bd9Sstevel@tonic-gate wait = max(wait, hubd_device_delay / 10); 85627c478bd9Sstevel@tonic-gate 85637c478bd9Sstevel@tonic-gate retry++; 85647c478bd9Sstevel@tonic-gate 85657c478bd9Sstevel@tonic-gate } while ((!(status & PORT_STATUS_PPS)) && (retry < HUBD_PORT_RETRY)); 85667c478bd9Sstevel@tonic-gate 85677c478bd9Sstevel@tonic-gate /* Print warning message if port has no power */ 85687c478bd9Sstevel@tonic-gate if (!(status & PORT_STATUS_PPS)) { 85697c478bd9Sstevel@tonic-gate 8570d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 85717c478bd9Sstevel@tonic-gate "hubd_toggle_port: port %d power-on failed, " 85727c478bd9Sstevel@tonic-gate "port status 0x%x", port, status); 85737c478bd9Sstevel@tonic-gate 85747c478bd9Sstevel@tonic-gate return (USB_FAILURE); 85757c478bd9Sstevel@tonic-gate } 85767c478bd9Sstevel@tonic-gate 85777c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 85787c478bd9Sstevel@tonic-gate } 857935f36846Ssl 858035f36846Ssl 858135f36846Ssl /* 858235f36846Ssl * hubd_init_power_budget: 858335f36846Ssl * Init power budget variables in hubd structure. According 858435f36846Ssl * to USB spec, the power budget rules are: 858535f36846Ssl * 1. local-powered hubs including root-hubs can supply 858635f36846Ssl * 500mA to each port at maximum 858735f36846Ssl * 2. two bus-powered hubs are not allowed to concatenate 858835f36846Ssl * 3. bus-powered hubs can supply 100mA to each port at 858935f36846Ssl * maximum, and the power consumed by all downstream 859035f36846Ssl * ports and the hub itself cannot exceed the max power 859135f36846Ssl * supplied by the upstream port, i.e., 500mA 859235f36846Ssl * The routine is only called during hub attach time 859335f36846Ssl */ 859435f36846Ssl static int 859535f36846Ssl hubd_init_power_budget(hubd_t *hubd) 859635f36846Ssl { 859735f36846Ssl uint16_t status = 0; 859835f36846Ssl usba_device_t *hubd_ud = NULL; 859935f36846Ssl size_t size; 860035f36846Ssl usb_cfg_descr_t cfg_descr; 860135f36846Ssl dev_info_t *pdip = NULL; 860235f36846Ssl hubd_t *phubd = NULL; 860335f36846Ssl 860435f36846Ssl if (hubd->h_ignore_pwr_budget) { 860535f36846Ssl 860635f36846Ssl return (USB_SUCCESS); 860735f36846Ssl } 860835f36846Ssl 860935f36846Ssl USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 861035f36846Ssl "hubd_init_power_budget:"); 861135f36846Ssl 861235f36846Ssl ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 861335f36846Ssl ASSERT(hubd->h_default_pipe != 0); 861435f36846Ssl mutex_exit(HUBD_MUTEX(hubd)); 861535f36846Ssl 861635f36846Ssl /* get device status */ 861735f36846Ssl if ((usb_get_status(hubd->h_dip, hubd->h_default_pipe, 861835f36846Ssl HUB_GET_DEVICE_STATUS_TYPE, 861935f36846Ssl 0, &status, 0)) != USB_SUCCESS) { 862035f36846Ssl mutex_enter(HUBD_MUTEX(hubd)); 862135f36846Ssl 862235f36846Ssl return (USB_FAILURE); 862335f36846Ssl } 862435f36846Ssl 862535f36846Ssl hubd_ud = usba_get_usba_device(hubd->h_dip); 862635f36846Ssl 862735f36846Ssl size = usb_parse_cfg_descr(hubd_ud->usb_cfg, hubd_ud->usb_cfg_length, 862835f36846Ssl &cfg_descr, USB_CFG_DESCR_SIZE); 862935f36846Ssl 863035f36846Ssl if (size != USB_CFG_DESCR_SIZE) { 863135f36846Ssl USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle, 863235f36846Ssl "get hub configuration descriptor failed"); 863335f36846Ssl mutex_enter(HUBD_MUTEX(hubd)); 863435f36846Ssl 863535f36846Ssl return (USB_FAILURE); 863635f36846Ssl } 863735f36846Ssl 863835f36846Ssl mutex_enter(HUBD_MUTEX(hubd)); 863935f36846Ssl 864035f36846Ssl hubd->h_local_pwr_capable = (cfg_descr.bmAttributes & 864135f36846Ssl USB_CFG_ATTR_SELFPWR); 864235f36846Ssl 864335f36846Ssl if (hubd->h_local_pwr_capable) { 864435f36846Ssl USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle, 864535f36846Ssl "hub is capable of local power"); 864635f36846Ssl } 864735f36846Ssl 864835f36846Ssl hubd->h_local_pwr_on = (status & 864935f36846Ssl USB_DEV_SLF_PWRD_STATUS) && hubd->h_local_pwr_capable; 865035f36846Ssl 865135f36846Ssl if (hubd->h_local_pwr_on) { 865235f36846Ssl USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle, 865335f36846Ssl "hub is local-powered"); 865435f36846Ssl 865535f36846Ssl hubd->h_pwr_limit = (USB_PWR_UNIT_LOAD * 865635f36846Ssl USB_HIGH_PWR_VALUE) / USB_CFG_DESCR_PWR_UNIT; 865735f36846Ssl } else { 865835f36846Ssl hubd->h_pwr_limit = (USB_PWR_UNIT_LOAD * 865935f36846Ssl USB_LOW_PWR_VALUE) / USB_CFG_DESCR_PWR_UNIT; 866035f36846Ssl 866135f36846Ssl hubd->h_pwr_left = (USB_PWR_UNIT_LOAD * 866235f36846Ssl USB_HIGH_PWR_VALUE) / USB_CFG_DESCR_PWR_UNIT; 866335f36846Ssl 866435f36846Ssl ASSERT(!usba_is_root_hub(hubd->h_dip)); 866535f36846Ssl 866635f36846Ssl if (!usba_is_root_hub(hubd->h_dip)) { 866735f36846Ssl /* 866835f36846Ssl * two bus-powered hubs are not 866935f36846Ssl * allowed to be concatenated 867035f36846Ssl */ 867135f36846Ssl mutex_exit(HUBD_MUTEX(hubd)); 867235f36846Ssl 867335f36846Ssl pdip = ddi_get_parent(hubd->h_dip); 867435f36846Ssl phubd = hubd_get_soft_state(pdip); 867535f36846Ssl ASSERT(phubd != NULL); 867635f36846Ssl 867735f36846Ssl if (!phubd->h_ignore_pwr_budget) { 867835f36846Ssl mutex_enter(HUBD_MUTEX(phubd)); 867935f36846Ssl if (phubd->h_local_pwr_on == B_FALSE) { 86808668df41Slg USB_DPRINTF_L1(DPRINT_MASK_HUB, 868135f36846Ssl hubd->h_log_handle, 868235f36846Ssl "two bus-powered hubs cannot " 868335f36846Ssl "be concatenated"); 868435f36846Ssl 868535f36846Ssl mutex_exit(HUBD_MUTEX(phubd)); 868635f36846Ssl mutex_enter(HUBD_MUTEX(hubd)); 868735f36846Ssl 868835f36846Ssl return (USB_FAILURE); 868935f36846Ssl } 869035f36846Ssl mutex_exit(HUBD_MUTEX(phubd)); 869135f36846Ssl } 869235f36846Ssl 869335f36846Ssl mutex_enter(HUBD_MUTEX(hubd)); 869435f36846Ssl 869535f36846Ssl USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle, 869635f36846Ssl "hub is bus-powered"); 869735f36846Ssl } else { 869835f36846Ssl USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle, 869935f36846Ssl "root-hub must be local-powered"); 870035f36846Ssl } 870135f36846Ssl 870235f36846Ssl /* 870335f36846Ssl * Subtract the power consumed by the hub itself 870435f36846Ssl * and get the power that can be supplied to 870535f36846Ssl * downstream ports 870635f36846Ssl */ 8707993e3fafSRobert Mustacchi hubd->h_pwr_left -= hubd->h_current / USB_CFG_DESCR_PWR_UNIT; 870835f36846Ssl if (hubd->h_pwr_left < 0) { 870935f36846Ssl USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle, 871035f36846Ssl "hubd->h_pwr_left is less than bHubContrCurrent, " 871135f36846Ssl "should fail"); 871235f36846Ssl 871335f36846Ssl return (USB_FAILURE); 871435f36846Ssl } 871535f36846Ssl } 871635f36846Ssl 871735f36846Ssl return (USB_SUCCESS); 871835f36846Ssl } 871935f36846Ssl 872035f36846Ssl 872135f36846Ssl /* 872235f36846Ssl * usba_hubdi_check_power_budget: 872335f36846Ssl * Check if the hub has enough power budget to allow a 872435f36846Ssl * child device to select a configuration of config_index. 872535f36846Ssl */ 872635f36846Ssl int 872735f36846Ssl usba_hubdi_check_power_budget(dev_info_t *dip, usba_device_t *child_ud, 8728993e3fafSRobert Mustacchi uint_t config_index) 872935f36846Ssl { 873035f36846Ssl int16_t pwr_left, pwr_limit, pwr_required; 873135f36846Ssl size_t size; 873235f36846Ssl usb_cfg_descr_t cfg_descr; 873335f36846Ssl hubd_t *hubd; 873435f36846Ssl 873535f36846Ssl if ((hubd = hubd_get_soft_state(dip)) == NULL) { 873635f36846Ssl 873735f36846Ssl return (USB_FAILURE); 873835f36846Ssl } 873935f36846Ssl 874035f36846Ssl if (hubd->h_ignore_pwr_budget) { 874135f36846Ssl 874235f36846Ssl return (USB_SUCCESS); 874335f36846Ssl } 874435f36846Ssl 874535f36846Ssl USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 874635f36846Ssl "usba_hubdi_check_power_budget: " 8747112116d8Sfb "dip=0x%p child_ud=0x%p conf_index=%d", (void *)dip, 8748112116d8Sfb (void *)child_ud, config_index); 874935f36846Ssl 875035f36846Ssl mutex_enter(HUBD_MUTEX(hubd)); 875135f36846Ssl pwr_limit = hubd->h_pwr_limit; 875235f36846Ssl if (hubd->h_local_pwr_on == B_FALSE) { 875335f36846Ssl pwr_left = hubd->h_pwr_left; 875435f36846Ssl pwr_limit = (pwr_limit <= pwr_left) ? pwr_limit : pwr_left; 875535f36846Ssl } 875635f36846Ssl mutex_exit(HUBD_MUTEX(hubd)); 875735f36846Ssl 875835f36846Ssl USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 875935f36846Ssl "usba_hubdi_check_power_budget: " 876035f36846Ssl "available power is %dmA", pwr_limit * USB_CFG_DESCR_PWR_UNIT); 876135f36846Ssl 876235f36846Ssl size = usb_parse_cfg_descr( 876335f36846Ssl child_ud->usb_cfg_array[config_index], USB_CFG_DESCR_SIZE, 876435f36846Ssl &cfg_descr, USB_CFG_DESCR_SIZE); 876535f36846Ssl 876635f36846Ssl if (size != USB_CFG_DESCR_SIZE) { 876735f36846Ssl USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 876835f36846Ssl "get hub configuration descriptor failed"); 876935f36846Ssl 877035f36846Ssl return (USB_FAILURE); 877135f36846Ssl } 877235f36846Ssl 877335f36846Ssl pwr_required = cfg_descr.bMaxPower; 877435f36846Ssl 877535f36846Ssl USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 877635f36846Ssl "usba_hubdi_check_power_budget: " 877735f36846Ssl "child bmAttributes=0x%x bMaxPower=%d " 877835f36846Ssl "with config_index=%d", cfg_descr.bmAttributes, 877935f36846Ssl pwr_required, config_index); 878035f36846Ssl 878135f36846Ssl if (pwr_required > pwr_limit) { 87828668df41Slg USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 878335f36846Ssl "configuration %d for device %s %s at port %d " 878435f36846Ssl "exceeds power available for this port, please " 878535f36846Ssl "re-insert your device into another hub port which " 878635f36846Ssl "has enough power", 878735f36846Ssl config_index, 878835f36846Ssl child_ud->usb_mfg_str, 878935f36846Ssl child_ud->usb_product_str, 879035f36846Ssl child_ud->usb_port); 879135f36846Ssl 879235f36846Ssl return (USB_FAILURE); 879335f36846Ssl } 879435f36846Ssl 879535f36846Ssl return (USB_SUCCESS); 879635f36846Ssl } 879735f36846Ssl 879835f36846Ssl 879935f36846Ssl /* 880035f36846Ssl * usba_hubdi_incr_power_budget: 880135f36846Ssl * Increase the hub power budget value when a child device 880235f36846Ssl * is removed from a bus-powered hub port. 880335f36846Ssl */ 880435f36846Ssl void 880535f36846Ssl usba_hubdi_incr_power_budget(dev_info_t *dip, usba_device_t *child_ud) 880635f36846Ssl { 880735f36846Ssl uint16_t pwr_value; 880835f36846Ssl hubd_t *hubd = hubd_get_soft_state(dip); 880935f36846Ssl 881035f36846Ssl ASSERT(hubd != NULL); 881135f36846Ssl 881235f36846Ssl if (hubd->h_ignore_pwr_budget) { 881335f36846Ssl 881435f36846Ssl return; 881535f36846Ssl } 881635f36846Ssl 881735f36846Ssl USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 881835f36846Ssl "usba_hubdi_incr_power_budget: " 8819112116d8Sfb "dip=0x%p child_ud=0x%p", (void *)dip, (void *)child_ud); 882035f36846Ssl 882135f36846Ssl mutex_enter(HUBD_MUTEX(hubd)); 882235f36846Ssl if (hubd->h_local_pwr_on == B_TRUE) { 882335f36846Ssl USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle, 882435f36846Ssl "usba_hubdi_incr_power_budget: " 882535f36846Ssl "hub is local powered"); 882635f36846Ssl mutex_exit(HUBD_MUTEX(hubd)); 882735f36846Ssl 882835f36846Ssl return; 882935f36846Ssl } 883035f36846Ssl mutex_exit(HUBD_MUTEX(hubd)); 883135f36846Ssl 883235f36846Ssl mutex_enter(&child_ud->usb_mutex); 883335f36846Ssl if (child_ud->usb_pwr_from_hub == 0) { 883435f36846Ssl mutex_exit(&child_ud->usb_mutex); 883535f36846Ssl 883635f36846Ssl return; 883735f36846Ssl } 883835f36846Ssl pwr_value = child_ud->usb_pwr_from_hub; 883935f36846Ssl mutex_exit(&child_ud->usb_mutex); 884035f36846Ssl 884135f36846Ssl mutex_enter(HUBD_MUTEX(hubd)); 884235f36846Ssl hubd->h_pwr_left += pwr_value; 884335f36846Ssl 884435f36846Ssl USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle, 884535f36846Ssl "usba_hubdi_incr_power_budget: " 884635f36846Ssl "available power is %dmA, increased by %dmA", 884735f36846Ssl hubd->h_pwr_left * USB_CFG_DESCR_PWR_UNIT, 884835f36846Ssl pwr_value * USB_CFG_DESCR_PWR_UNIT); 884935f36846Ssl 885035f36846Ssl mutex_exit(HUBD_MUTEX(hubd)); 885135f36846Ssl 885235f36846Ssl mutex_enter(&child_ud->usb_mutex); 885335f36846Ssl child_ud->usb_pwr_from_hub = 0; 885435f36846Ssl mutex_exit(&child_ud->usb_mutex); 885535f36846Ssl } 885635f36846Ssl 885735f36846Ssl 885835f36846Ssl /* 885935f36846Ssl * usba_hubdi_decr_power_budget: 886035f36846Ssl * Decrease the hub power budget value when a child device 886135f36846Ssl * is inserted to a bus-powered hub port. 886235f36846Ssl */ 886335f36846Ssl void 886435f36846Ssl usba_hubdi_decr_power_budget(dev_info_t *dip, usba_device_t *child_ud) 886535f36846Ssl { 886635f36846Ssl uint16_t pwr_value; 886735f36846Ssl size_t size; 886835f36846Ssl usb_cfg_descr_t cfg_descr; 886935f36846Ssl hubd_t *hubd = hubd_get_soft_state(dip); 887035f36846Ssl 887135f36846Ssl ASSERT(hubd != NULL); 887235f36846Ssl 887335f36846Ssl if (hubd->h_ignore_pwr_budget) { 887435f36846Ssl 887535f36846Ssl return; 887635f36846Ssl } 887735f36846Ssl 887835f36846Ssl USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 887935f36846Ssl "usba_hubdi_decr_power_budget: " 8880112116d8Sfb "dip=0x%p child_ud=0x%p", (void *)dip, (void *)child_ud); 888135f36846Ssl 888235f36846Ssl mutex_enter(HUBD_MUTEX(hubd)); 888335f36846Ssl if (hubd->h_local_pwr_on == B_TRUE) { 888435f36846Ssl USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle, 888535f36846Ssl "usba_hubdi_decr_power_budget: " 888635f36846Ssl "hub is local powered"); 888735f36846Ssl mutex_exit(HUBD_MUTEX(hubd)); 888835f36846Ssl 888935f36846Ssl return; 889035f36846Ssl } 889135f36846Ssl mutex_exit(HUBD_MUTEX(hubd)); 889235f36846Ssl 889335f36846Ssl mutex_enter(&child_ud->usb_mutex); 889435f36846Ssl if (child_ud->usb_pwr_from_hub > 0) { 889535f36846Ssl mutex_exit(&child_ud->usb_mutex); 889635f36846Ssl 889735f36846Ssl return; 889835f36846Ssl } 889935f36846Ssl mutex_exit(&child_ud->usb_mutex); 890035f36846Ssl 890135f36846Ssl size = usb_parse_cfg_descr( 890235f36846Ssl child_ud->usb_cfg, child_ud->usb_cfg_length, 890335f36846Ssl &cfg_descr, USB_CFG_DESCR_SIZE); 890435f36846Ssl ASSERT(size == USB_CFG_DESCR_SIZE); 890535f36846Ssl 890635f36846Ssl mutex_enter(HUBD_MUTEX(hubd)); 890735f36846Ssl pwr_value = cfg_descr.bMaxPower; 890835f36846Ssl hubd->h_pwr_left -= pwr_value; 890935f36846Ssl ASSERT(hubd->h_pwr_left >= 0); 891035f36846Ssl 891135f36846Ssl USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle, 891235f36846Ssl "usba_hubdi_decr_power_budget: " 891335f36846Ssl "available power is %dmA, decreased by %dmA", 891435f36846Ssl hubd->h_pwr_left * USB_CFG_DESCR_PWR_UNIT, 891535f36846Ssl pwr_value * USB_CFG_DESCR_PWR_UNIT); 891635f36846Ssl 891735f36846Ssl mutex_exit(HUBD_MUTEX(hubd)); 891835f36846Ssl 891935f36846Ssl mutex_enter(&child_ud->usb_mutex); 892035f36846Ssl child_ud->usb_pwr_from_hub = pwr_value; 892135f36846Ssl mutex_exit(&child_ud->usb_mutex); 892235f36846Ssl } 8923ffcd51f3Slg 8924ffcd51f3Slg /* 8925ffcd51f3Slg * hubd_wait_for_hotplug_exit: 8926112cd14aSqz * Waiting for the exit of the running hotplug thread or ioctl thread. 8927ffcd51f3Slg */ 8928ffcd51f3Slg static int 8929ffcd51f3Slg hubd_wait_for_hotplug_exit(hubd_t *hubd) 8930ffcd51f3Slg { 8931d3d50737SRafael Vanoni clock_t until = drv_usectohz(1000000); 8932ffcd51f3Slg int rval; 8933ffcd51f3Slg 8934ffcd51f3Slg ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 8935ffcd51f3Slg 8936ffcd51f3Slg if (hubd->h_hotplug_thread) { 8937ffcd51f3Slg USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 8938ffcd51f3Slg "waiting for hubd hotplug thread exit"); 8939d3d50737SRafael Vanoni rval = cv_reltimedwait(&hubd->h_cv_hotplug_dev, 8940d3d50737SRafael Vanoni &hubd->h_mutex, until, TR_CLOCK_TICK); 8941ffcd51f3Slg 8942ffcd51f3Slg if ((rval <= 0) && (hubd->h_hotplug_thread)) { 8943ffcd51f3Slg 8944ffcd51f3Slg return (USB_FAILURE); 8945ffcd51f3Slg } 8946ffcd51f3Slg } 8947ffcd51f3Slg 8948ffcd51f3Slg return (USB_SUCCESS); 8949ffcd51f3Slg } 8950ffcd51f3Slg 8951ffcd51f3Slg /* 8952ffcd51f3Slg * hubd_reset_thread: 8953ffcd51f3Slg * handles the "USB_RESET_LVL_REATTACH" reset of usb device. 8954ffcd51f3Slg * 8955ffcd51f3Slg * - delete the child (force detaching the device and its children) 8956ffcd51f3Slg * - reset the corresponding parent hub port 8957ffcd51f3Slg * - create the child (force re-attaching the device and its children) 8958ffcd51f3Slg */ 8959ffcd51f3Slg static void 8960ffcd51f3Slg hubd_reset_thread(void *arg) 8961ffcd51f3Slg { 8962ffcd51f3Slg hubd_reset_arg_t *hd_arg = (hubd_reset_arg_t *)arg; 8963ffcd51f3Slg hubd_t *hubd = hd_arg->hubd; 8964ffcd51f3Slg uint16_t reset_port = hd_arg->reset_port; 8965ffcd51f3Slg uint16_t status, change; 8966ffcd51f3Slg hub_power_t *hubpm; 8967ffcd51f3Slg dev_info_t *hdip = hubd->h_dip; 8968ffcd51f3Slg dev_info_t *rh_dip = hubd->h_usba_device->usb_root_hub_dip; 8969ffcd51f3Slg dev_info_t *child_dip; 8970ffcd51f3Slg boolean_t online_child = B_FALSE; 8971ffcd51f3Slg int prh_circ, rh_circ, circ, devinst; 8972ffcd51f3Slg char *devname; 8973aa041649SRaymond Chen int i = 0; 8974aa041649SRaymond Chen int rval = USB_FAILURE; 8975ffcd51f3Slg 8976ffcd51f3Slg USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 8977ffcd51f3Slg "hubd_reset_thread: started, hubd_reset_port = 0x%x", reset_port); 8978ffcd51f3Slg 8979ffcd51f3Slg kmem_free(arg, sizeof (hubd_reset_arg_t)); 8980ffcd51f3Slg 8981ffcd51f3Slg mutex_enter(HUBD_MUTEX(hubd)); 8982ffcd51f3Slg 8983ffcd51f3Slg child_dip = hubd->h_children_dips[reset_port]; 8984ffcd51f3Slg ASSERT(child_dip != NULL); 8985ffcd51f3Slg 8986ffcd51f3Slg devname = (char *)ddi_driver_name(child_dip); 8987ffcd51f3Slg devinst = ddi_get_instance(child_dip); 8988ffcd51f3Slg 8989ffcd51f3Slg /* if our bus power entry point is active, quit the reset */ 8990ffcd51f3Slg if (hubd->h_bus_pwr) { 8991ffcd51f3Slg USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 8992ffcd51f3Slg "%s%d is under bus power management, cannot be reset. " 8993ffcd51f3Slg "Please disconnect and reconnect this device.", 8994ffcd51f3Slg devname, devinst); 8995ffcd51f3Slg 8996ffcd51f3Slg goto Fail; 8997ffcd51f3Slg } 8998ffcd51f3Slg 8999ffcd51f3Slg if (hubd_wait_for_hotplug_exit(hubd) == USB_FAILURE) { 9000ffcd51f3Slg /* we got woken up because of a timeout */ 9001ffcd51f3Slg USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, 9002ffcd51f3Slg hubd->h_log_handle, "Time out when resetting the device" 9003ffcd51f3Slg " %s%d. Please disconnect and reconnect this device.", 9004ffcd51f3Slg devname, devinst); 9005ffcd51f3Slg 9006ffcd51f3Slg goto Fail; 9007ffcd51f3Slg } 9008ffcd51f3Slg 9009ffcd51f3Slg hubd->h_hotplug_thread++; 9010ffcd51f3Slg 9011ffcd51f3Slg /* is this the root hub? */ 9012ffcd51f3Slg if ((hdip == rh_dip) && 9013ffcd51f3Slg (hubd->h_dev_state == USB_DEV_PWRED_DOWN)) { 9014ffcd51f3Slg hubpm = hubd->h_hubpm; 9015ffcd51f3Slg 9016ffcd51f3Slg /* mark the root hub as full power */ 9017ffcd51f3Slg hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR; 9018e5815e7aSJosef 'Jeff' Sipek hubpm->hubp_time_at_full_power = gethrtime(); 9019ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 9020ffcd51f3Slg 9021ffcd51f3Slg USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 9022ffcd51f3Slg "hubd_reset_thread: call pm_power_has_changed"); 9023ffcd51f3Slg 9024ffcd51f3Slg (void) pm_power_has_changed(hdip, 0, 9025ffcd51f3Slg USB_DEV_OS_FULL_PWR); 9026ffcd51f3Slg 9027ffcd51f3Slg mutex_enter(HUBD_MUTEX(hubd)); 9028ffcd51f3Slg hubd->h_dev_state = USB_DEV_ONLINE; 9029ffcd51f3Slg } 9030ffcd51f3Slg 9031ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 9032ffcd51f3Slg 9033ffcd51f3Slg /* 9034ffcd51f3Slg * this ensures one reset activity per system at a time. 9035ffcd51f3Slg * we enter the parent PCI node to have this serialization. 9036ffcd51f3Slg * this also excludes ioctls and deathrow thread 9037ffcd51f3Slg */ 9038ffcd51f3Slg ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ); 9039ffcd51f3Slg ndi_devi_enter(rh_dip, &rh_circ); 9040ffcd51f3Slg 9041ffcd51f3Slg /* exclude other threads */ 9042ffcd51f3Slg ndi_devi_enter(hdip, &circ); 9043ffcd51f3Slg mutex_enter(HUBD_MUTEX(hubd)); 9044ffcd51f3Slg 9045ffcd51f3Slg /* 9046ffcd51f3Slg * We need to make sure that the child is still online for a hotplug 9047ffcd51f3Slg * thread could have inserted which detached the child. 9048ffcd51f3Slg */ 9049ffcd51f3Slg if (hubd->h_children_dips[reset_port]) { 9050ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 9051ffcd51f3Slg /* First disconnect the device */ 9052ffcd51f3Slg hubd_post_event(hubd, reset_port, USBA_EVENT_TAG_HOT_REMOVAL); 9053aa041649SRaymond Chen 9054aa041649SRaymond Chen /* delete cached dv_node's but drop locks first */ 9055aa041649SRaymond Chen ndi_devi_exit(hdip, circ); 9056aa041649SRaymond Chen ndi_devi_exit(rh_dip, rh_circ); 9057aa041649SRaymond Chen ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ); 9058aa041649SRaymond Chen 9059aa041649SRaymond Chen (void) devfs_clean(rh_dip, NULL, DV_CLEAN_FORCE); 9060aa041649SRaymond Chen 9061aa041649SRaymond Chen /* 9062aa041649SRaymond Chen * workaround only for storage device. When it's able to force 9063aa041649SRaymond Chen * detach a driver, this code can be removed safely. 9064aa041649SRaymond Chen * 9065aa041649SRaymond Chen * If we're to reset storage device and the device is used, we 9066aa041649SRaymond Chen * will wait at most extra 20s for applications to exit and 9067aa041649SRaymond Chen * close the device. This is especially useful for HAL-based 9068aa041649SRaymond Chen * applications. 9069aa041649SRaymond Chen */ 9070aa041649SRaymond Chen if ((strcmp(devname, "scsa2usb") == 0) && 9071aa041649SRaymond Chen DEVI(child_dip)->devi_ref != 0) { 9072aa041649SRaymond Chen while (i++ < hubdi_reset_delay) { 9073aa041649SRaymond Chen mutex_enter(HUBD_MUTEX(hubd)); 9074aa041649SRaymond Chen rval = hubd_delete_child(hubd, reset_port, 9075aa041649SRaymond Chen NDI_DEVI_REMOVE, B_FALSE); 9076aa041649SRaymond Chen mutex_exit(HUBD_MUTEX(hubd)); 9077aa041649SRaymond Chen if (rval == USB_SUCCESS) 9078aa041649SRaymond Chen break; 9079aa041649SRaymond Chen 9080aa041649SRaymond Chen delay(drv_usectohz(1000000)); /* 1s */ 9081aa041649SRaymond Chen } 9082aa041649SRaymond Chen } 9083aa041649SRaymond Chen 9084aa041649SRaymond Chen ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ); 9085aa041649SRaymond Chen ndi_devi_enter(rh_dip, &rh_circ); 9086aa041649SRaymond Chen ndi_devi_enter(hdip, &circ); 9087aa041649SRaymond Chen 9088ffcd51f3Slg mutex_enter(HUBD_MUTEX(hubd)); 9089ffcd51f3Slg 9090ffcd51f3Slg /* Then force detaching the device */ 9091aa041649SRaymond Chen if ((rval != USB_SUCCESS) && (hubd_delete_child(hubd, 9092aa041649SRaymond Chen reset_port, NDI_DEVI_REMOVE, B_FALSE) != USB_SUCCESS)) { 9093ffcd51f3Slg USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 9094ffcd51f3Slg "%s%d cannot be reset due to other applications " 9095ffcd51f3Slg "are using it, please first close these " 9096ffcd51f3Slg "applications, then disconnect and reconnect" 9097ffcd51f3Slg "the device.", devname, devinst); 9098ffcd51f3Slg 9099ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 9100ffcd51f3Slg /* post a re-connect event */ 9101ffcd51f3Slg hubd_post_event(hubd, reset_port, 9102ffcd51f3Slg USBA_EVENT_TAG_HOT_INSERTION); 9103ffcd51f3Slg mutex_enter(HUBD_MUTEX(hubd)); 9104ffcd51f3Slg } else { 9105ffcd51f3Slg (void) hubd_determine_port_status(hubd, reset_port, 9106993e3fafSRobert Mustacchi &status, &change, NULL, HUBD_ACK_ALL_CHANGES); 9107ffcd51f3Slg 9108ffcd51f3Slg /* Reset the parent hubd port and create new child */ 9109ffcd51f3Slg if (status & PORT_STATUS_CCS) { 9110ffcd51f3Slg online_child |= (hubd_handle_port_connect(hubd, 9111ffcd51f3Slg reset_port) == USB_SUCCESS); 9112ffcd51f3Slg } 9113ffcd51f3Slg } 9114ffcd51f3Slg } 9115ffcd51f3Slg 9116ffcd51f3Slg /* release locks so we can do a devfs_clean */ 9117ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 9118ffcd51f3Slg 9119ffcd51f3Slg /* delete cached dv_node's but drop locks first */ 9120ffcd51f3Slg ndi_devi_exit(hdip, circ); 9121ffcd51f3Slg ndi_devi_exit(rh_dip, rh_circ); 9122ffcd51f3Slg ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ); 9123ffcd51f3Slg 9124ffcd51f3Slg (void) devfs_clean(rh_dip, NULL, 0); 9125ffcd51f3Slg 9126ffcd51f3Slg /* now check if any children need onlining */ 9127ffcd51f3Slg if (online_child) { 9128ffcd51f3Slg USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 9129ffcd51f3Slg "hubd_reset_thread: onlining children"); 9130ffcd51f3Slg 9131ffcd51f3Slg (void) ndi_devi_online(hubd->h_dip, 0); 9132ffcd51f3Slg } 9133ffcd51f3Slg 9134ffcd51f3Slg mutex_enter(HUBD_MUTEX(hubd)); 9135ffcd51f3Slg 9136ffcd51f3Slg /* allow hotplug thread now */ 9137ffcd51f3Slg hubd->h_hotplug_thread--; 9138ffcd51f3Slg Fail: 9139ffcd51f3Slg hubd_start_polling(hubd, 0); 9140ffcd51f3Slg 9141ffcd51f3Slg /* mark this device as idle */ 9142ffcd51f3Slg (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0); 9143ffcd51f3Slg 9144ffcd51f3Slg USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 9145ffcd51f3Slg "hubd_reset_thread: exit, %d", hubd->h_hotplug_thread); 9146ffcd51f3Slg 9147ffcd51f3Slg hubd->h_reset_port[reset_port] = B_FALSE; 9148ffcd51f3Slg 9149ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 9150ffcd51f3Slg 9151ffcd51f3Slg ndi_rele_devi(hdip); 9152ffcd51f3Slg } 9153ffcd51f3Slg 9154ffcd51f3Slg /* 9155ffcd51f3Slg * hubd_check_same_device: 9156112cd14aSqz * - open the default pipe of the device. 9157112cd14aSqz * - compare the old and new descriptors of the device. 9158112cd14aSqz * - close the default pipe. 9159ffcd51f3Slg */ 9160ffcd51f3Slg static int 9161ffcd51f3Slg hubd_check_same_device(hubd_t *hubd, usb_port_t port) 9162ffcd51f3Slg { 9163112cd14aSqz dev_info_t *dip = hubd->h_children_dips[port]; 9164ffcd51f3Slg usb_pipe_handle_t ph; 9165112cd14aSqz int rval = USB_FAILURE; 9166ffcd51f3Slg 9167ffcd51f3Slg ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 9168ffcd51f3Slg 9169ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 9170ffcd51f3Slg /* Open the default pipe to operate the device */ 9171ffcd51f3Slg if (usb_pipe_open(dip, NULL, NULL, 9172ffcd51f3Slg USB_FLAGS_SLEEP| USBA_FLAGS_PRIVILEGED, 9173ffcd51f3Slg &ph) == USB_SUCCESS) { 9174ffcd51f3Slg /* 9175ffcd51f3Slg * Check that if the device's descriptors are different 9176ffcd51f3Slg * from the values saved before the port reset. 9177ffcd51f3Slg */ 9178ffcd51f3Slg rval = usb_check_same_device(dip, 9179ffcd51f3Slg hubd->h_log_handle, USB_LOG_L0, 9180ffcd51f3Slg DPRINT_MASK_ALL, USB_CHK_ALL, NULL); 9181ffcd51f3Slg 9182ffcd51f3Slg usb_pipe_close(dip, ph, USB_FLAGS_SLEEP | 9183ffcd51f3Slg USBA_FLAGS_PRIVILEGED, NULL, NULL); 9184ffcd51f3Slg } 9185ffcd51f3Slg mutex_enter(HUBD_MUTEX(hubd)); 9186ffcd51f3Slg 9187ffcd51f3Slg return (rval); 9188ffcd51f3Slg } 9189ffcd51f3Slg 9190ffcd51f3Slg /* 9191ffcd51f3Slg * usba_hubdi_reset_device 9192112cd14aSqz * Called by usb_reset_device to handle usb device reset. 9193ffcd51f3Slg */ 9194ffcd51f3Slg int 9195ffcd51f3Slg usba_hubdi_reset_device(dev_info_t *dip, usb_dev_reset_lvl_t reset_level) 9196ffcd51f3Slg { 9197ffcd51f3Slg hubd_t *hubd; 9198ffcd51f3Slg usb_port_t port = 0; 9199ffcd51f3Slg dev_info_t *hdip; 9200ffcd51f3Slg usb_pipe_state_t prev_pipe_state = 0; 92010d2006e4SRobert Mustacchi usba_device_t *usba_device = NULL; 9202ffcd51f3Slg hubd_reset_arg_t *arg; 9203ffcd51f3Slg int i, ph_open_cnt; 9204ffcd51f3Slg int rval = USB_FAILURE; 9205ffcd51f3Slg 9206ffcd51f3Slg if ((!dip) || usba_is_root_hub(dip)) { 9207aa041649SRaymond Chen USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle, 9208aa041649SRaymond Chen "usba_hubdi_reset_device: NULL dip or root hub"); 9209ffcd51f3Slg 9210ffcd51f3Slg return (USB_INVALID_ARGS); 9211ffcd51f3Slg } 9212ffcd51f3Slg 9213ffcd51f3Slg if (!usb_owns_device(dip)) { 9214aa041649SRaymond Chen USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle, 9215aa041649SRaymond Chen "usba_hubdi_reset_device: Not owns the device"); 9216ffcd51f3Slg 9217ffcd51f3Slg return (USB_INVALID_PERM); 9218ffcd51f3Slg } 9219ffcd51f3Slg 9220ffcd51f3Slg if ((reset_level != USB_RESET_LVL_REATTACH) && 9221ffcd51f3Slg (reset_level != USB_RESET_LVL_DEFAULT)) { 9222aa041649SRaymond Chen USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle, 9223aa041649SRaymond Chen "usba_hubdi_reset_device: Unknown flags"); 9224ffcd51f3Slg 9225ffcd51f3Slg return (USB_INVALID_ARGS); 9226ffcd51f3Slg } 9227ffcd51f3Slg 9228ffcd51f3Slg if ((hdip = ddi_get_parent(dip)) == NULL) { 9229aa041649SRaymond Chen USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle, 9230aa041649SRaymond Chen "usba_hubdi_reset_device: fail to get parent hub"); 9231ffcd51f3Slg 9232ffcd51f3Slg return (USB_INVALID_ARGS); 9233ffcd51f3Slg } 9234ffcd51f3Slg 9235ffcd51f3Slg if ((hubd = hubd_get_soft_state(hdip)) == NULL) { 9236aa041649SRaymond Chen USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle, 9237aa041649SRaymond Chen "usba_hubdi_reset_device: fail to get hub softstate"); 9238ffcd51f3Slg 9239ffcd51f3Slg return (USB_INVALID_ARGS); 9240ffcd51f3Slg } 9241ffcd51f3Slg 9242ffcd51f3Slg mutex_enter(HUBD_MUTEX(hubd)); 9243ffcd51f3Slg 9244ffcd51f3Slg /* make sure the hub is connected before trying any kinds of reset. */ 9245ffcd51f3Slg if ((hubd->h_dev_state == USB_DEV_DISCONNECTED) || 9246ffcd51f3Slg (hubd->h_dev_state == USB_DEV_SUSPENDED)) { 9247ffcd51f3Slg USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 9248ffcd51f3Slg "usb_reset_device: the state %d of the hub/roothub " 9249112116d8Sfb "associated to the device 0x%p is incorrect", 9250112116d8Sfb hubd->h_dev_state, (void *)dip); 9251ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 9252ffcd51f3Slg 9253ffcd51f3Slg return (USB_INVALID_ARGS); 9254ffcd51f3Slg } 9255ffcd51f3Slg 9256ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 9257ffcd51f3Slg 9258ffcd51f3Slg port = hubd_child_dip2port(hubd, dip); 9259ffcd51f3Slg 9260ffcd51f3Slg mutex_enter(HUBD_MUTEX(hubd)); 9261ffcd51f3Slg 9262ffcd51f3Slg if (hubd->h_reset_port[port]) { 9263ffcd51f3Slg USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 9264ffcd51f3Slg "usb_reset_device: the corresponding port is resetting"); 9265ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 9266ffcd51f3Slg 9267ffcd51f3Slg return (USB_SUCCESS); 9268ffcd51f3Slg } 9269ffcd51f3Slg 9270ffcd51f3Slg /* 9271ffcd51f3Slg * For Default reset, client drivers should first close all the pipes 9272ffcd51f3Slg * except default pipe before calling the function, also should not 9273ffcd51f3Slg * call the function during interrupt context. 9274ffcd51f3Slg */ 9275ffcd51f3Slg if (reset_level == USB_RESET_LVL_DEFAULT) { 9276ffcd51f3Slg usba_device = hubd->h_usba_devices[port]; 9277ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 9278ffcd51f3Slg 9279ffcd51f3Slg if (servicing_interrupt()) { 9280ffcd51f3Slg USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 9281ffcd51f3Slg "usb_reset_device: during interrput context, quit"); 9282ffcd51f3Slg 9283ffcd51f3Slg return (USB_INVALID_CONTEXT); 9284ffcd51f3Slg } 9285ffcd51f3Slg /* Check if all the pipes have been closed */ 9286ffcd51f3Slg for (ph_open_cnt = 0, i = 1; i < USBA_N_ENDPOINTS; i++) { 9287ffcd51f3Slg if (usba_device->usb_ph_list[i].usba_ph_data) { 9288ffcd51f3Slg ph_open_cnt++; 9289ffcd51f3Slg break; 9290ffcd51f3Slg } 9291ffcd51f3Slg } 9292ffcd51f3Slg if (ph_open_cnt) { 9293ffcd51f3Slg USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 9294ffcd51f3Slg "usb_reset_device: %d pipes are still open", 9295ffcd51f3Slg ph_open_cnt); 9296ffcd51f3Slg 9297ffcd51f3Slg return (USB_BUSY); 9298ffcd51f3Slg } 9299ffcd51f3Slg mutex_enter(HUBD_MUTEX(hubd)); 9300ffcd51f3Slg } 9301ffcd51f3Slg 9302ffcd51f3Slg /* Don't perform reset while the device is detaching */ 9303ffcd51f3Slg if (hubd->h_port_state[port] & HUBD_CHILD_DETACHING) { 9304ffcd51f3Slg USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 9305ffcd51f3Slg "usb_reset_device: the device is detaching, " 9306ffcd51f3Slg "cannot be reset"); 9307ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 9308ffcd51f3Slg 9309ffcd51f3Slg return (USB_FAILURE); 9310ffcd51f3Slg } 9311ffcd51f3Slg 9312ffcd51f3Slg hubd->h_reset_port[port] = B_TRUE; 9313ffcd51f3Slg hdip = hubd->h_dip; 9314ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 9315ffcd51f3Slg 9316ffcd51f3Slg /* Don't allow hub detached during the reset */ 9317ffcd51f3Slg ndi_hold_devi(hdip); 9318ffcd51f3Slg 9319ffcd51f3Slg mutex_enter(HUBD_MUTEX(hubd)); 9320ffcd51f3Slg hubd_pm_busy_component(hubd, hdip, 0); 9321ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 9322ffcd51f3Slg /* go full power */ 9323ffcd51f3Slg (void) pm_raise_power(hdip, 0, USB_DEV_OS_FULL_PWR); 9324ffcd51f3Slg mutex_enter(HUBD_MUTEX(hubd)); 9325ffcd51f3Slg 9326ffcd51f3Slg hubd->h_hotplug_thread++; 9327ffcd51f3Slg 9328ffcd51f3Slg /* stop polling if it was active */ 9329ffcd51f3Slg if (hubd->h_ep1_ph) { 9330ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 9331ffcd51f3Slg (void) usb_pipe_get_state(hubd->h_ep1_ph, &prev_pipe_state, 9332ffcd51f3Slg USB_FLAGS_SLEEP); 9333ffcd51f3Slg mutex_enter(HUBD_MUTEX(hubd)); 9334ffcd51f3Slg 9335ffcd51f3Slg if (prev_pipe_state == USB_PIPE_STATE_ACTIVE) { 9336ffcd51f3Slg hubd_stop_polling(hubd); 9337ffcd51f3Slg } 9338ffcd51f3Slg } 9339ffcd51f3Slg 9340ffcd51f3Slg switch (reset_level) { 9341ffcd51f3Slg case USB_RESET_LVL_REATTACH: 9342ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 9343ffcd51f3Slg arg = (hubd_reset_arg_t *)kmem_zalloc( 9344ffcd51f3Slg sizeof (hubd_reset_arg_t), KM_SLEEP); 9345ffcd51f3Slg arg->hubd = hubd; 9346ffcd51f3Slg arg->reset_port = port; 9347ffcd51f3Slg mutex_enter(HUBD_MUTEX(hubd)); 9348ffcd51f3Slg 9349ffcd51f3Slg if ((rval = usb_async_req(hdip, hubd_reset_thread, 9350ffcd51f3Slg (void *)arg, 0)) == USB_SUCCESS) { 9351ffcd51f3Slg hubd->h_hotplug_thread--; 9352ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 9353ffcd51f3Slg 9354ffcd51f3Slg return (USB_SUCCESS); 9355ffcd51f3Slg } else { 9356ffcd51f3Slg USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 9357ffcd51f3Slg "Cannot create reset thread, the device %s%d failed" 9358ffcd51f3Slg " to reset", ddi_driver_name(dip), 9359ffcd51f3Slg ddi_get_instance(dip)); 9360ffcd51f3Slg 9361ffcd51f3Slg kmem_free(arg, sizeof (hubd_reset_arg_t)); 9362ffcd51f3Slg } 9363ffcd51f3Slg 9364ffcd51f3Slg break; 9365ffcd51f3Slg case USB_RESET_LVL_DEFAULT: 9366ffcd51f3Slg /* 9367ffcd51f3Slg * Reset hub port and then recover device's address, set back 9368ffcd51f3Slg * device's configuration, hubd_handle_port_connect() will 9369ffcd51f3Slg * handle errors happened during this process. 9370ffcd51f3Slg */ 9371ffcd51f3Slg if ((rval = hubd_handle_port_connect(hubd, port)) 9372ffcd51f3Slg == USB_SUCCESS) { 9373ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 9374ffcd51f3Slg /* re-open the default pipe */ 93750d2006e4SRobert Mustacchi ASSERT3P(usba_device, !=, NULL); 9376ffcd51f3Slg rval = usba_persistent_pipe_open(usba_device); 9377ffcd51f3Slg mutex_enter(HUBD_MUTEX(hubd)); 9378ffcd51f3Slg if (rval != USB_SUCCESS) { 9379ffcd51f3Slg USB_DPRINTF_L2(DPRINT_MASK_ATTA, 9380ffcd51f3Slg hubd->h_log_handle, "failed to reopen " 9381ffcd51f3Slg "default pipe after reset, disable hub" 9382ffcd51f3Slg "port for %s%d", ddi_driver_name(dip), 9383ffcd51f3Slg ddi_get_instance(dip)); 9384ffcd51f3Slg /* 9385ffcd51f3Slg * Disable port to set out a hotplug thread 9386ffcd51f3Slg * which will handle errors. 9387ffcd51f3Slg */ 9388ffcd51f3Slg (void) hubd_disable_port(hubd, port); 9389ffcd51f3Slg } 9390ffcd51f3Slg } 9391ffcd51f3Slg 9392ffcd51f3Slg break; 9393ffcd51f3Slg default: 9394ffcd51f3Slg 9395ffcd51f3Slg break; 9396ffcd51f3Slg } 9397ffcd51f3Slg 9398ffcd51f3Slg /* allow hotplug thread now */ 9399ffcd51f3Slg hubd->h_hotplug_thread--; 9400ffcd51f3Slg 9401ffcd51f3Slg if ((hubd->h_dev_state == USB_DEV_ONLINE) && hubd->h_ep1_ph && 9402ffcd51f3Slg (prev_pipe_state == USB_PIPE_STATE_ACTIVE)) { 9403ffcd51f3Slg hubd_start_polling(hubd, 0); 9404ffcd51f3Slg } 9405ffcd51f3Slg 9406ffcd51f3Slg hubd_pm_idle_component(hubd, hdip, 0); 9407ffcd51f3Slg 9408ffcd51f3Slg /* Clear reset mark for the port. */ 9409ffcd51f3Slg hubd->h_reset_port[port] = B_FALSE; 9410ffcd51f3Slg 9411ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 9412ffcd51f3Slg 9413ffcd51f3Slg ndi_rele_devi(hdip); 9414ffcd51f3Slg 9415ffcd51f3Slg return (rval); 9416ffcd51f3Slg } 9417