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 /* 22de6f998eSrui wang - Sun Microsystems - Beijing China * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate 277c478bd9Sstevel@tonic-gate /* 287c478bd9Sstevel@tonic-gate * USBA: Solaris USB Architecture support for the hub 297c478bd9Sstevel@tonic-gate * including root hub 307c478bd9Sstevel@tonic-gate * Most of the code for hubd resides in this file and 317c478bd9Sstevel@tonic-gate * is shared between the HCD root hub support and hubd 327c478bd9Sstevel@tonic-gate */ 337c478bd9Sstevel@tonic-gate #define USBA_FRAMEWORK 347c478bd9Sstevel@tonic-gate #include <sys/usb/usba.h> 357c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usba_devdb.h> 367c478bd9Sstevel@tonic-gate #include <sys/sunndi.h> 377c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usba_impl.h> 387c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usba_types.h> 397c478bd9Sstevel@tonic-gate #include <sys/usb/usba/hubdi.h> 407c478bd9Sstevel@tonic-gate #include <sys/usb/usba/hcdi_impl.h> 417c478bd9Sstevel@tonic-gate #include <sys/usb/hubd/hub.h> 427c478bd9Sstevel@tonic-gate #include <sys/usb/hubd/hubdvar.h> 437c478bd9Sstevel@tonic-gate #include <sys/usb/hubd/hubd_impl.h> 447c478bd9Sstevel@tonic-gate #include <sys/kobj.h> 457c478bd9Sstevel@tonic-gate #include <sys/kobj_lex.h> 467c478bd9Sstevel@tonic-gate #include <sys/fs/dv_node.h> 47d29f5a71Szhigang lu - Sun Microsystems - Beijing China #include <sys/strsun.h> 487c478bd9Sstevel@tonic-gate 497c478bd9Sstevel@tonic-gate /* 507c478bd9Sstevel@tonic-gate * Prototypes for static functions 517c478bd9Sstevel@tonic-gate */ 527c478bd9Sstevel@tonic-gate static int usba_hubdi_bus_ctl( 537c478bd9Sstevel@tonic-gate dev_info_t *dip, 547c478bd9Sstevel@tonic-gate dev_info_t *rdip, 557c478bd9Sstevel@tonic-gate ddi_ctl_enum_t op, 567c478bd9Sstevel@tonic-gate void *arg, 577c478bd9Sstevel@tonic-gate void *result); 587c478bd9Sstevel@tonic-gate 597c478bd9Sstevel@tonic-gate static int usba_hubdi_map_fault( 607c478bd9Sstevel@tonic-gate dev_info_t *dip, 617c478bd9Sstevel@tonic-gate dev_info_t *rdip, 627c478bd9Sstevel@tonic-gate struct hat *hat, 637c478bd9Sstevel@tonic-gate struct seg *seg, 647c478bd9Sstevel@tonic-gate caddr_t addr, 657c478bd9Sstevel@tonic-gate struct devpage *dp, 667c478bd9Sstevel@tonic-gate pfn_t pfn, 677c478bd9Sstevel@tonic-gate uint_t prot, 687c478bd9Sstevel@tonic-gate uint_t lock); 697c478bd9Sstevel@tonic-gate 707c478bd9Sstevel@tonic-gate static int hubd_busop_get_eventcookie(dev_info_t *dip, 717c478bd9Sstevel@tonic-gate dev_info_t *rdip, 727c478bd9Sstevel@tonic-gate char *eventname, 737c478bd9Sstevel@tonic-gate ddi_eventcookie_t *cookie); 747c478bd9Sstevel@tonic-gate static int hubd_busop_add_eventcall(dev_info_t *dip, 757c478bd9Sstevel@tonic-gate dev_info_t *rdip, 767c478bd9Sstevel@tonic-gate ddi_eventcookie_t cookie, 777c478bd9Sstevel@tonic-gate void (*callback)(dev_info_t *dip, 787c478bd9Sstevel@tonic-gate ddi_eventcookie_t cookie, void *arg, 797c478bd9Sstevel@tonic-gate void *bus_impldata), 807c478bd9Sstevel@tonic-gate void *arg, ddi_callback_id_t *cb_id); 817c478bd9Sstevel@tonic-gate static int hubd_busop_remove_eventcall(dev_info_t *dip, 827c478bd9Sstevel@tonic-gate ddi_callback_id_t cb_id); 837c478bd9Sstevel@tonic-gate static int hubd_bus_config(dev_info_t *dip, 847c478bd9Sstevel@tonic-gate uint_t flag, 857c478bd9Sstevel@tonic-gate ddi_bus_config_op_t op, 867c478bd9Sstevel@tonic-gate void *arg, 877c478bd9Sstevel@tonic-gate dev_info_t **child); 887c478bd9Sstevel@tonic-gate static int hubd_bus_unconfig(dev_info_t *dip, 897c478bd9Sstevel@tonic-gate uint_t flag, 907c478bd9Sstevel@tonic-gate ddi_bus_config_op_t op, 917c478bd9Sstevel@tonic-gate void *arg); 927c478bd9Sstevel@tonic-gate static int hubd_bus_power(dev_info_t *dip, void *impl_arg, 937c478bd9Sstevel@tonic-gate pm_bus_power_op_t op, void *arg, void *result); 947c478bd9Sstevel@tonic-gate 957c478bd9Sstevel@tonic-gate static usb_port_t hubd_get_port_num(hubd_t *, struct devctl_iocdata *); 967c478bd9Sstevel@tonic-gate static dev_info_t *hubd_get_child_dip(hubd_t *, usb_port_t); 977c478bd9Sstevel@tonic-gate static uint_t hubd_cfgadm_state(hubd_t *, usb_port_t); 987c478bd9Sstevel@tonic-gate static int hubd_toggle_port(hubd_t *, usb_port_t); 997c478bd9Sstevel@tonic-gate static void hubd_register_cpr_callback(hubd_t *); 1007c478bd9Sstevel@tonic-gate static void hubd_unregister_cpr_callback(hubd_t *); 1017c478bd9Sstevel@tonic-gate 1027c478bd9Sstevel@tonic-gate /* 1037c478bd9Sstevel@tonic-gate * Busops vector for USB HUB's 1047c478bd9Sstevel@tonic-gate */ 1057c478bd9Sstevel@tonic-gate struct bus_ops usba_hubdi_busops = { 1067c478bd9Sstevel@tonic-gate BUSO_REV, 1077c478bd9Sstevel@tonic-gate nullbusmap, /* bus_map */ 1087c478bd9Sstevel@tonic-gate NULL, /* bus_get_intrspec */ 1097c478bd9Sstevel@tonic-gate NULL, /* bus_add_intrspec */ 1107c478bd9Sstevel@tonic-gate NULL, /* bus_remove_intrspec */ 1117c478bd9Sstevel@tonic-gate usba_hubdi_map_fault, /* bus_map_fault */ 1127c478bd9Sstevel@tonic-gate ddi_dma_map, /* bus_dma_map */ 1137c478bd9Sstevel@tonic-gate ddi_dma_allochdl, 1147c478bd9Sstevel@tonic-gate ddi_dma_freehdl, 1157c478bd9Sstevel@tonic-gate ddi_dma_bindhdl, 1167c478bd9Sstevel@tonic-gate ddi_dma_unbindhdl, 1177c478bd9Sstevel@tonic-gate ddi_dma_flush, 1187c478bd9Sstevel@tonic-gate ddi_dma_win, 1197c478bd9Sstevel@tonic-gate ddi_dma_mctl, /* bus_dma_ctl */ 1207c478bd9Sstevel@tonic-gate usba_hubdi_bus_ctl, /* bus_ctl */ 1217c478bd9Sstevel@tonic-gate ddi_bus_prop_op, /* bus_prop_op */ 1227c478bd9Sstevel@tonic-gate hubd_busop_get_eventcookie, 1237c478bd9Sstevel@tonic-gate hubd_busop_add_eventcall, 1247c478bd9Sstevel@tonic-gate hubd_busop_remove_eventcall, 1257c478bd9Sstevel@tonic-gate NULL, /* bus_post_event */ 1267c478bd9Sstevel@tonic-gate NULL, /* bus_intr_ctl */ 1277c478bd9Sstevel@tonic-gate hubd_bus_config, /* bus_config */ 1287c478bd9Sstevel@tonic-gate hubd_bus_unconfig, /* bus_unconfig */ 1297c478bd9Sstevel@tonic-gate NULL, /* bus_fm_init */ 1307c478bd9Sstevel@tonic-gate NULL, /* bus_fm_fini */ 1317c478bd9Sstevel@tonic-gate NULL, /* bus_fm_access_enter */ 1327c478bd9Sstevel@tonic-gate NULL, /* bus_fm_access_exit */ 1337c478bd9Sstevel@tonic-gate hubd_bus_power /* bus_power */ 1347c478bd9Sstevel@tonic-gate }; 1357c478bd9Sstevel@tonic-gate 1367c478bd9Sstevel@tonic-gate 1377c478bd9Sstevel@tonic-gate /* 1387c478bd9Sstevel@tonic-gate * local variables 1397c478bd9Sstevel@tonic-gate */ 1407c478bd9Sstevel@tonic-gate static kmutex_t usba_hubdi_mutex; /* protects USBA HUB data structures */ 1417c478bd9Sstevel@tonic-gate 1427c478bd9Sstevel@tonic-gate static usba_list_entry_t usba_hubdi_list; 1437c478bd9Sstevel@tonic-gate 1447c478bd9Sstevel@tonic-gate usb_log_handle_t hubdi_log_handle; 1457c478bd9Sstevel@tonic-gate uint_t hubdi_errlevel = USB_LOG_L4; 1467c478bd9Sstevel@tonic-gate uint_t hubdi_errmask = (uint_t)-1; 147d29f5a71Szhigang lu - Sun Microsystems - Beijing China uint8_t hubdi_min_pm_threshold = 5; /* seconds */ 148*aa041649SRaymond Chen uint8_t hubdi_reset_delay = 20; /* seconds */ 149d724deadSguoqing zhu - Sun Microsystems - Beijing China extern int modrootloaded; 150d724deadSguoqing zhu - Sun Microsystems - Beijing China 1517c478bd9Sstevel@tonic-gate 1527c478bd9Sstevel@tonic-gate /* 1537c478bd9Sstevel@tonic-gate * initialize private data 1547c478bd9Sstevel@tonic-gate */ 1557c478bd9Sstevel@tonic-gate void 1567c478bd9Sstevel@tonic-gate usba_hubdi_initialization() 1577c478bd9Sstevel@tonic-gate { 1587c478bd9Sstevel@tonic-gate hubdi_log_handle = usb_alloc_log_hdl(NULL, "hubdi", &hubdi_errlevel, 159c0f24e5bSlg &hubdi_errmask, NULL, 0); 1607c478bd9Sstevel@tonic-gate 1617c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle, 1627c478bd9Sstevel@tonic-gate "usba_hubdi_initialization"); 1637c478bd9Sstevel@tonic-gate 1647c478bd9Sstevel@tonic-gate mutex_init(&usba_hubdi_mutex, NULL, MUTEX_DRIVER, NULL); 1657c478bd9Sstevel@tonic-gate 1667c478bd9Sstevel@tonic-gate usba_init_list(&usba_hubdi_list, NULL, NULL); 1677c478bd9Sstevel@tonic-gate } 1687c478bd9Sstevel@tonic-gate 1697c478bd9Sstevel@tonic-gate 1707c478bd9Sstevel@tonic-gate void 1717c478bd9Sstevel@tonic-gate usba_hubdi_destroy() 1727c478bd9Sstevel@tonic-gate { 1737c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle, 1747c478bd9Sstevel@tonic-gate "usba_hubdi_destroy"); 1757c478bd9Sstevel@tonic-gate 1767c478bd9Sstevel@tonic-gate mutex_destroy(&usba_hubdi_mutex); 1777c478bd9Sstevel@tonic-gate usba_destroy_list(&usba_hubdi_list); 1787c478bd9Sstevel@tonic-gate 1797c478bd9Sstevel@tonic-gate usb_free_log_hdl(hubdi_log_handle); 1807c478bd9Sstevel@tonic-gate } 1817c478bd9Sstevel@tonic-gate 1827c478bd9Sstevel@tonic-gate 1837c478bd9Sstevel@tonic-gate /* 1847c478bd9Sstevel@tonic-gate * Called by an HUB to attach an instance of the driver 1857c478bd9Sstevel@tonic-gate * make this instance known to USBA 1867c478bd9Sstevel@tonic-gate * the HUB should initialize usba_hubdi structure prior 1877c478bd9Sstevel@tonic-gate * to calling this interface 1887c478bd9Sstevel@tonic-gate */ 189ff0e937bSRaymond Chen int 1907c478bd9Sstevel@tonic-gate usba_hubdi_register(dev_info_t *dip, 1917c478bd9Sstevel@tonic-gate uint_t flags) 1927c478bd9Sstevel@tonic-gate { 1937c478bd9Sstevel@tonic-gate usba_hubdi_t *hubdi = kmem_zalloc(sizeof (usba_hubdi_t), KM_SLEEP); 1947c478bd9Sstevel@tonic-gate usba_device_t *usba_device = usba_get_usba_device(dip); 1957c478bd9Sstevel@tonic-gate 1967c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle, 1977c478bd9Sstevel@tonic-gate "usba_hubdi_register: %s", ddi_node_name(dip)); 1987c478bd9Sstevel@tonic-gate 1997c478bd9Sstevel@tonic-gate hubdi->hubdi_dip = dip; 2007c478bd9Sstevel@tonic-gate hubdi->hubdi_flags = flags; 2017c478bd9Sstevel@tonic-gate 2027c478bd9Sstevel@tonic-gate usba_device->usb_hubdi = hubdi; 2037c478bd9Sstevel@tonic-gate 2047c478bd9Sstevel@tonic-gate /* 2057c478bd9Sstevel@tonic-gate * add this hubdi instance to the list of known hubdi's 2067c478bd9Sstevel@tonic-gate */ 2077c478bd9Sstevel@tonic-gate usba_init_list(&hubdi->hubdi_list, (usb_opaque_t)hubdi, 2087c478bd9Sstevel@tonic-gate usba_hcdi_get_hcdi(usba_device->usb_root_hub_dip)-> 2097c478bd9Sstevel@tonic-gate hcdi_iblock_cookie); 2107c478bd9Sstevel@tonic-gate mutex_enter(&usba_hubdi_mutex); 2117c478bd9Sstevel@tonic-gate usba_add_to_list(&usba_hubdi_list, &hubdi->hubdi_list); 2127c478bd9Sstevel@tonic-gate mutex_exit(&usba_hubdi_mutex); 2137c478bd9Sstevel@tonic-gate 2147c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 2157c478bd9Sstevel@tonic-gate } 2167c478bd9Sstevel@tonic-gate 2177c478bd9Sstevel@tonic-gate 2187c478bd9Sstevel@tonic-gate /* 2197c478bd9Sstevel@tonic-gate * Called by an HUB to detach an instance of the driver 2207c478bd9Sstevel@tonic-gate */ 221ff0e937bSRaymond Chen int 2227c478bd9Sstevel@tonic-gate usba_hubdi_unregister(dev_info_t *dip) 2237c478bd9Sstevel@tonic-gate { 2247c478bd9Sstevel@tonic-gate usba_device_t *usba_device = usba_get_usba_device(dip); 2257c478bd9Sstevel@tonic-gate usba_hubdi_t *hubdi = usba_device->usb_hubdi; 2267c478bd9Sstevel@tonic-gate 2277c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle, 2287c478bd9Sstevel@tonic-gate "usba_hubdi_unregister: %s", ddi_node_name(dip)); 2297c478bd9Sstevel@tonic-gate 2307c478bd9Sstevel@tonic-gate mutex_enter(&usba_hubdi_mutex); 2317c478bd9Sstevel@tonic-gate (void) usba_rm_from_list(&usba_hubdi_list, &hubdi->hubdi_list); 2327c478bd9Sstevel@tonic-gate mutex_exit(&usba_hubdi_mutex); 2337c478bd9Sstevel@tonic-gate 2347c478bd9Sstevel@tonic-gate usba_destroy_list(&hubdi->hubdi_list); 2357c478bd9Sstevel@tonic-gate 2367c478bd9Sstevel@tonic-gate kmem_free(hubdi, sizeof (usba_hubdi_t)); 2377c478bd9Sstevel@tonic-gate 2387c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 2397c478bd9Sstevel@tonic-gate } 2407c478bd9Sstevel@tonic-gate 2417c478bd9Sstevel@tonic-gate 2427c478bd9Sstevel@tonic-gate /* 2437c478bd9Sstevel@tonic-gate * misc bus routines currently not used 2447c478bd9Sstevel@tonic-gate */ 2457c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 2467c478bd9Sstevel@tonic-gate static int 2477c478bd9Sstevel@tonic-gate usba_hubdi_map_fault(dev_info_t *dip, 2487c478bd9Sstevel@tonic-gate dev_info_t *rdip, 2497c478bd9Sstevel@tonic-gate struct hat *hat, 2507c478bd9Sstevel@tonic-gate struct seg *seg, 2517c478bd9Sstevel@tonic-gate caddr_t addr, 2527c478bd9Sstevel@tonic-gate struct devpage *dp, 2537c478bd9Sstevel@tonic-gate pfn_t pfn, 2547c478bd9Sstevel@tonic-gate uint_t prot, 2557c478bd9Sstevel@tonic-gate uint_t lock) 2567c478bd9Sstevel@tonic-gate { 2577c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 2587c478bd9Sstevel@tonic-gate } 2597c478bd9Sstevel@tonic-gate 2607c478bd9Sstevel@tonic-gate 2617c478bd9Sstevel@tonic-gate /* 2627c478bd9Sstevel@tonic-gate * root hub support. the root hub uses the same devi as the HCD 2637c478bd9Sstevel@tonic-gate */ 2647c478bd9Sstevel@tonic-gate int 2657c478bd9Sstevel@tonic-gate usba_hubdi_bind_root_hub(dev_info_t *dip, 2667c478bd9Sstevel@tonic-gate uchar_t *root_hub_config_descriptor, 2677c478bd9Sstevel@tonic-gate size_t config_length, 2687c478bd9Sstevel@tonic-gate usb_dev_descr_t *root_hub_device_descriptor) 2697c478bd9Sstevel@tonic-gate { 2707c478bd9Sstevel@tonic-gate usba_device_t *usba_device; 2717c478bd9Sstevel@tonic-gate usba_hcdi_t *hcdi = usba_hcdi_get_hcdi(dip); 2727c478bd9Sstevel@tonic-gate hubd_t *root_hubd; 2737c478bd9Sstevel@tonic-gate usb_pipe_handle_t ph = NULL; 2747c478bd9Sstevel@tonic-gate dev_info_t *child = ddi_get_child(dip); 2757c478bd9Sstevel@tonic-gate 2767c478bd9Sstevel@tonic-gate if (ndi_prop_create_boolean(DDI_DEV_T_NONE, dip, 2777c478bd9Sstevel@tonic-gate "root-hub") != NDI_SUCCESS) { 2787c478bd9Sstevel@tonic-gate 2797c478bd9Sstevel@tonic-gate return (USB_FAILURE); 2807c478bd9Sstevel@tonic-gate } 2817c478bd9Sstevel@tonic-gate 282de6f998eSrui wang - Sun Microsystems - Beijing China usba_add_root_hub(dip); 283de6f998eSrui wang - Sun Microsystems - Beijing China 2847c478bd9Sstevel@tonic-gate root_hubd = kmem_zalloc(sizeof (hubd_t), KM_SLEEP); 2857c478bd9Sstevel@tonic-gate 2867c478bd9Sstevel@tonic-gate /* 2877c478bd9Sstevel@tonic-gate * create and initialize a usba_device structure 2887c478bd9Sstevel@tonic-gate */ 2897c478bd9Sstevel@tonic-gate usba_device = usba_alloc_usba_device(dip); 2907c478bd9Sstevel@tonic-gate 2917c478bd9Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex); 2927c478bd9Sstevel@tonic-gate usba_device->usb_hcdi_ops = hcdi->hcdi_ops; 2937c478bd9Sstevel@tonic-gate usba_device->usb_cfg = root_hub_config_descriptor; 2947c478bd9Sstevel@tonic-gate usba_device->usb_cfg_length = config_length; 2957c478bd9Sstevel@tonic-gate usba_device->usb_dev_descr = root_hub_device_descriptor; 2967c478bd9Sstevel@tonic-gate usba_device->usb_port = 1; 2977c478bd9Sstevel@tonic-gate usba_device->usb_addr = ROOT_HUB_ADDR; 2987c478bd9Sstevel@tonic-gate usba_device->usb_root_hubd = root_hubd; 2997c478bd9Sstevel@tonic-gate usba_device->usb_cfg_array = kmem_zalloc(sizeof (uchar_t *), 300c0f24e5bSlg KM_SLEEP); 3017c478bd9Sstevel@tonic-gate usba_device->usb_cfg_array_length = sizeof (uchar_t *); 3027c478bd9Sstevel@tonic-gate 3037c478bd9Sstevel@tonic-gate usba_device->usb_cfg_array_len = kmem_zalloc(sizeof (uint16_t), 304c0f24e5bSlg KM_SLEEP); 3057c478bd9Sstevel@tonic-gate usba_device->usb_cfg_array_len_length = sizeof (uint16_t); 3067c478bd9Sstevel@tonic-gate 3077c478bd9Sstevel@tonic-gate usba_device->usb_cfg_array[0] = root_hub_config_descriptor; 3087c478bd9Sstevel@tonic-gate usba_device->usb_cfg_array_len[0] = 309c0f24e5bSlg sizeof (root_hub_config_descriptor); 3107c478bd9Sstevel@tonic-gate 3117c478bd9Sstevel@tonic-gate usba_device->usb_cfg_str_descr = kmem_zalloc(sizeof (uchar_t *), 312c0f24e5bSlg KM_SLEEP); 3137c478bd9Sstevel@tonic-gate usba_device->usb_n_cfgs = 1; 3147c478bd9Sstevel@tonic-gate usba_device->usb_n_ifs = 1; 3157c478bd9Sstevel@tonic-gate usba_device->usb_dip = dip; 3167c478bd9Sstevel@tonic-gate 3177c478bd9Sstevel@tonic-gate usba_device->usb_client_flags = kmem_zalloc( 3187c478bd9Sstevel@tonic-gate usba_device->usb_n_ifs * USBA_CLIENT_FLAG_SIZE, KM_SLEEP); 3197c478bd9Sstevel@tonic-gate 3207c478bd9Sstevel@tonic-gate usba_device->usb_client_attach_list = kmem_zalloc( 3217c478bd9Sstevel@tonic-gate usba_device->usb_n_ifs * 3227c478bd9Sstevel@tonic-gate sizeof (*usba_device->usb_client_attach_list), KM_SLEEP); 3237c478bd9Sstevel@tonic-gate 3247c478bd9Sstevel@tonic-gate usba_device->usb_client_ev_cb_list = kmem_zalloc( 3257c478bd9Sstevel@tonic-gate usba_device->usb_n_ifs * 3267c478bd9Sstevel@tonic-gate sizeof (*usba_device->usb_client_ev_cb_list), KM_SLEEP); 3277c478bd9Sstevel@tonic-gate 3287c478bd9Sstevel@tonic-gate /* 3297c478bd9Sstevel@tonic-gate * The bDeviceProtocol field of root hub device specifies, 3307c478bd9Sstevel@tonic-gate * whether root hub is a High or Full speed usb device. 3317c478bd9Sstevel@tonic-gate */ 3327c478bd9Sstevel@tonic-gate if (root_hub_device_descriptor->bDeviceProtocol) { 3337c478bd9Sstevel@tonic-gate usba_device->usb_port_status = USBA_HIGH_SPEED_DEV; 3347c478bd9Sstevel@tonic-gate } else { 3357c478bd9Sstevel@tonic-gate usba_device->usb_port_status = USBA_FULL_SPEED_DEV; 3367c478bd9Sstevel@tonic-gate } 3377c478bd9Sstevel@tonic-gate 3387c478bd9Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex); 3397c478bd9Sstevel@tonic-gate 3407c478bd9Sstevel@tonic-gate usba_set_usba_device(dip, usba_device); 3417c478bd9Sstevel@tonic-gate 3427c478bd9Sstevel@tonic-gate /* 3437c478bd9Sstevel@tonic-gate * For the root hub the default pipe is not yet open 3447c478bd9Sstevel@tonic-gate */ 3457c478bd9Sstevel@tonic-gate if (usb_pipe_open(dip, NULL, NULL, 3467c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph) != USB_SUCCESS) { 3477c478bd9Sstevel@tonic-gate goto fail; 3487c478bd9Sstevel@tonic-gate } 3497c478bd9Sstevel@tonic-gate 3507c478bd9Sstevel@tonic-gate /* 3517c478bd9Sstevel@tonic-gate * kill off all OBP children, they may not be fully 3527c478bd9Sstevel@tonic-gate * enumerated 3537c478bd9Sstevel@tonic-gate */ 3547c478bd9Sstevel@tonic-gate while (child) { 3557c478bd9Sstevel@tonic-gate dev_info_t *next = ddi_get_next_sibling(child); 3567c478bd9Sstevel@tonic-gate (void) ddi_remove_child(child, 0); 3577c478bd9Sstevel@tonic-gate child = next; 3587c478bd9Sstevel@tonic-gate } 3597c478bd9Sstevel@tonic-gate 3607c478bd9Sstevel@tonic-gate /* 3617c478bd9Sstevel@tonic-gate * "attach" the root hub driver 3627c478bd9Sstevel@tonic-gate */ 3637c478bd9Sstevel@tonic-gate if (usba_hubdi_attach(dip, DDI_ATTACH) != DDI_SUCCESS) { 3647c478bd9Sstevel@tonic-gate goto fail; 3657c478bd9Sstevel@tonic-gate } 3667c478bd9Sstevel@tonic-gate 3677c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 3687c478bd9Sstevel@tonic-gate 3697c478bd9Sstevel@tonic-gate fail: 3707c478bd9Sstevel@tonic-gate (void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "root-hub"); 3717c478bd9Sstevel@tonic-gate 372de6f998eSrui wang - Sun Microsystems - Beijing China usba_rem_root_hub(dip); 373de6f998eSrui wang - Sun Microsystems - Beijing China 3747c478bd9Sstevel@tonic-gate if (ph) { 3757c478bd9Sstevel@tonic-gate usb_pipe_close(dip, ph, 3767c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL); 3777c478bd9Sstevel@tonic-gate } 3787c478bd9Sstevel@tonic-gate 3797c478bd9Sstevel@tonic-gate kmem_free(usba_device->usb_cfg_array, 380c0f24e5bSlg usba_device->usb_cfg_array_length); 3817c478bd9Sstevel@tonic-gate kmem_free(usba_device->usb_cfg_array_len, 382c0f24e5bSlg usba_device->usb_cfg_array_len_length); 3837c478bd9Sstevel@tonic-gate 3847c478bd9Sstevel@tonic-gate kmem_free(usba_device->usb_cfg_str_descr, sizeof (uchar_t *)); 3857c478bd9Sstevel@tonic-gate 3867c478bd9Sstevel@tonic-gate usba_free_usba_device(usba_device); 3877c478bd9Sstevel@tonic-gate 3887c478bd9Sstevel@tonic-gate usba_set_usba_device(dip, NULL); 3897c478bd9Sstevel@tonic-gate if (root_hubd) { 3907c478bd9Sstevel@tonic-gate kmem_free(root_hubd, sizeof (hubd_t)); 3917c478bd9Sstevel@tonic-gate } 3927c478bd9Sstevel@tonic-gate 3937c478bd9Sstevel@tonic-gate return (USB_FAILURE); 3947c478bd9Sstevel@tonic-gate } 3957c478bd9Sstevel@tonic-gate 3967c478bd9Sstevel@tonic-gate 3977c478bd9Sstevel@tonic-gate int 3987c478bd9Sstevel@tonic-gate usba_hubdi_unbind_root_hub(dev_info_t *dip) 3997c478bd9Sstevel@tonic-gate { 4007c478bd9Sstevel@tonic-gate usba_device_t *usba_device; 4017c478bd9Sstevel@tonic-gate 4027c478bd9Sstevel@tonic-gate /* was root hub attached? */ 4037c478bd9Sstevel@tonic-gate if (!(usba_is_root_hub(dip))) { 4047c478bd9Sstevel@tonic-gate 4057c478bd9Sstevel@tonic-gate /* return success anyway */ 4067c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 4077c478bd9Sstevel@tonic-gate } 4087c478bd9Sstevel@tonic-gate 4097c478bd9Sstevel@tonic-gate /* 4107c478bd9Sstevel@tonic-gate * usba_hubdi_detach also closes the default pipe 4117c478bd9Sstevel@tonic-gate * and removes properties so there is no need to 4127c478bd9Sstevel@tonic-gate * do it here 4137c478bd9Sstevel@tonic-gate */ 4147c478bd9Sstevel@tonic-gate if (usba_hubdi_detach(dip, DDI_DETACH) != DDI_SUCCESS) { 4157c478bd9Sstevel@tonic-gate 4167c478bd9Sstevel@tonic-gate if (DEVI_IS_ATTACHING(dip)) { 417d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle, 4187c478bd9Sstevel@tonic-gate "failure to unbind root hub after attach failure"); 4197c478bd9Sstevel@tonic-gate } 4207c478bd9Sstevel@tonic-gate 4217c478bd9Sstevel@tonic-gate return (USB_FAILURE); 4227c478bd9Sstevel@tonic-gate } 4237c478bd9Sstevel@tonic-gate 4247c478bd9Sstevel@tonic-gate usba_device = usba_get_usba_device(dip); 4257c478bd9Sstevel@tonic-gate 4267c478bd9Sstevel@tonic-gate kmem_free(usba_device->usb_root_hubd, sizeof (hubd_t)); 4277c478bd9Sstevel@tonic-gate 4287c478bd9Sstevel@tonic-gate kmem_free(usba_device->usb_cfg_array, 429c0f24e5bSlg usba_device->usb_cfg_array_length); 4307c478bd9Sstevel@tonic-gate kmem_free(usba_device->usb_cfg_array_len, 431c0f24e5bSlg usba_device->usb_cfg_array_len_length); 4327c478bd9Sstevel@tonic-gate 4337c478bd9Sstevel@tonic-gate kmem_free(usba_device->usb_cfg_str_descr, sizeof (uchar_t *)); 4347c478bd9Sstevel@tonic-gate 4357c478bd9Sstevel@tonic-gate usba_free_usba_device(usba_device); 4367c478bd9Sstevel@tonic-gate 437de6f998eSrui wang - Sun Microsystems - Beijing China usba_rem_root_hub(dip); 438de6f998eSrui wang - Sun Microsystems - Beijing China 4397c478bd9Sstevel@tonic-gate (void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "root-hub"); 4407c478bd9Sstevel@tonic-gate 4417c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 4427c478bd9Sstevel@tonic-gate } 4437c478bd9Sstevel@tonic-gate 4447c478bd9Sstevel@tonic-gate 4457c478bd9Sstevel@tonic-gate /* 4467c478bd9Sstevel@tonic-gate * Actual Hub Driver support code: 4477c478bd9Sstevel@tonic-gate * shared by root hub and non-root hubs 4487c478bd9Sstevel@tonic-gate */ 4497c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usbai_version.h> 4507c478bd9Sstevel@tonic-gate 4517c478bd9Sstevel@tonic-gate /* Debugging support */ 452ff0e937bSRaymond Chen uint_t hubd_errlevel = USB_LOG_L4; 453ff0e937bSRaymond Chen uint_t hubd_errmask = (uint_t)DPRINT_MASK_ALL; 454ff0e937bSRaymond Chen uint_t hubd_instance_debug = (uint_t)-1; 4557c478bd9Sstevel@tonic-gate static uint_t hubdi_bus_config_debug = 0; 4567c478bd9Sstevel@tonic-gate 4577c478bd9Sstevel@tonic-gate _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_errlevel)) 4587c478bd9Sstevel@tonic-gate _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_errmask)) 4597c478bd9Sstevel@tonic-gate _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_instance_debug)) 4607c478bd9Sstevel@tonic-gate 4617c478bd9Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique", msgb)) 4627c478bd9Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique", dev_info)) 4637c478bd9Sstevel@tonic-gate 4647c478bd9Sstevel@tonic-gate 4657c478bd9Sstevel@tonic-gate /* 4667c478bd9Sstevel@tonic-gate * local variables: 4677c478bd9Sstevel@tonic-gate * 4687c478bd9Sstevel@tonic-gate * Amount of time to wait between resetting the port and accessing 4697c478bd9Sstevel@tonic-gate * the device. The value is in microseconds. 4707c478bd9Sstevel@tonic-gate */ 4717c478bd9Sstevel@tonic-gate static uint_t hubd_device_delay = 1000000; 4727c478bd9Sstevel@tonic-gate 4737c478bd9Sstevel@tonic-gate /* 4747c478bd9Sstevel@tonic-gate * enumeration retry 4757c478bd9Sstevel@tonic-gate */ 4767c478bd9Sstevel@tonic-gate #define HUBD_PORT_RETRY 5 4777c478bd9Sstevel@tonic-gate static uint_t hubd_retry_enumerate = HUBD_PORT_RETRY; 4787c478bd9Sstevel@tonic-gate 4797c478bd9Sstevel@tonic-gate /* 4807c478bd9Sstevel@tonic-gate * Stale hotremoved device cleanup delay 4817c478bd9Sstevel@tonic-gate */ 4827c478bd9Sstevel@tonic-gate #define HUBD_STALE_DIP_CLEANUP_DELAY 5000000 4837c478bd9Sstevel@tonic-gate static uint_t hubd_dip_cleanup_delay = HUBD_STALE_DIP_CLEANUP_DELAY; 4847c478bd9Sstevel@tonic-gate 4857c478bd9Sstevel@tonic-gate /* 4867c478bd9Sstevel@tonic-gate * retries for USB suspend and resume 4877c478bd9Sstevel@tonic-gate */ 4887c478bd9Sstevel@tonic-gate #define HUBD_SUS_RES_RETRY 2 4897c478bd9Sstevel@tonic-gate 4907c478bd9Sstevel@tonic-gate void *hubd_statep; 4917c478bd9Sstevel@tonic-gate 4927c478bd9Sstevel@tonic-gate /* 4937c478bd9Sstevel@tonic-gate * prototypes 4947c478bd9Sstevel@tonic-gate */ 4957c478bd9Sstevel@tonic-gate static int hubd_cleanup(dev_info_t *dip, hubd_t *hubd); 4967c478bd9Sstevel@tonic-gate static int hubd_check_ports(hubd_t *hubd); 4977c478bd9Sstevel@tonic-gate 4987c478bd9Sstevel@tonic-gate static int hubd_open_intr_pipe(hubd_t *hubd); 4997c478bd9Sstevel@tonic-gate static void hubd_start_polling(hubd_t *hubd, int always); 5007c478bd9Sstevel@tonic-gate static void hubd_stop_polling(hubd_t *hubd); 5017c478bd9Sstevel@tonic-gate static void hubd_close_intr_pipe(hubd_t *hubd); 5027c478bd9Sstevel@tonic-gate 5037c478bd9Sstevel@tonic-gate static void hubd_read_cb(usb_pipe_handle_t pipe, usb_intr_req_t *req); 5047c478bd9Sstevel@tonic-gate static void hubd_exception_cb(usb_pipe_handle_t pipe, 5057c478bd9Sstevel@tonic-gate usb_intr_req_t *req); 5067c478bd9Sstevel@tonic-gate static void hubd_hotplug_thread(void *arg); 507ffcd51f3Slg static void hubd_reset_thread(void *arg); 5087c478bd9Sstevel@tonic-gate static int hubd_create_child(dev_info_t *dip, 5097c478bd9Sstevel@tonic-gate hubd_t *hubd, 5107c478bd9Sstevel@tonic-gate usba_device_t *usba_device, 5117c478bd9Sstevel@tonic-gate usb_port_status_t port_status, 5127c478bd9Sstevel@tonic-gate usb_port_t port, 5137c478bd9Sstevel@tonic-gate int iteration); 5147c478bd9Sstevel@tonic-gate 5157c478bd9Sstevel@tonic-gate static int hubd_delete_child(hubd_t *hubd, usb_port_t port, uint_t flag, 5167c478bd9Sstevel@tonic-gate boolean_t retry); 5177c478bd9Sstevel@tonic-gate 5187c478bd9Sstevel@tonic-gate static int hubd_get_hub_descriptor(hubd_t *hubd); 5197c478bd9Sstevel@tonic-gate 52035f36846Ssl static int hubd_get_hub_status_words(hubd_t *hubd, uint16_t *status); 52135f36846Ssl 5227c478bd9Sstevel@tonic-gate static int hubd_reset_port(hubd_t *hubd, usb_port_t port); 5237c478bd9Sstevel@tonic-gate 5247c478bd9Sstevel@tonic-gate static int hubd_get_hub_status(hubd_t *hubd); 5257c478bd9Sstevel@tonic-gate 5267c478bd9Sstevel@tonic-gate static int hubd_handle_port_connect(hubd_t *hubd, usb_port_t port); 5277c478bd9Sstevel@tonic-gate 5287c478bd9Sstevel@tonic-gate static int hubd_disable_port(hubd_t *hubd, usb_port_t port); 5297c478bd9Sstevel@tonic-gate 5307c478bd9Sstevel@tonic-gate static int hubd_enable_port(hubd_t *hubd, usb_port_t port); 5317c478bd9Sstevel@tonic-gate static int hubd_recover_disabled_port(hubd_t *hubd, usb_port_t port); 5327c478bd9Sstevel@tonic-gate 5337c478bd9Sstevel@tonic-gate static int hubd_determine_port_status(hubd_t *hubd, usb_port_t port, 5347c478bd9Sstevel@tonic-gate uint16_t *status, uint16_t *change, uint_t ack_flag); 5357c478bd9Sstevel@tonic-gate 5367c478bd9Sstevel@tonic-gate static int hubd_enable_all_port_power(hubd_t *hubd); 5377c478bd9Sstevel@tonic-gate static int hubd_disable_all_port_power(hubd_t *hubd); 5387c478bd9Sstevel@tonic-gate static int hubd_disable_port_power(hubd_t *hubd, usb_port_t port); 5397c478bd9Sstevel@tonic-gate static int hubd_enable_port_power(hubd_t *hubd, usb_port_t port); 5407c478bd9Sstevel@tonic-gate 5417c478bd9Sstevel@tonic-gate static void hubd_free_usba_device(hubd_t *hubd, usba_device_t *usba_device); 5427c478bd9Sstevel@tonic-gate 5437c478bd9Sstevel@tonic-gate static int hubd_can_suspend(hubd_t *hubd); 5447c478bd9Sstevel@tonic-gate static void hubd_restore_device_state(dev_info_t *dip, hubd_t *hubd); 5457c478bd9Sstevel@tonic-gate static int hubd_setdevaddr(hubd_t *hubd, usb_port_t port); 5467c478bd9Sstevel@tonic-gate static void hubd_setdevconfig(hubd_t *hubd, usb_port_t port); 5477c478bd9Sstevel@tonic-gate 5487c478bd9Sstevel@tonic-gate static int hubd_register_events(hubd_t *hubd); 5497c478bd9Sstevel@tonic-gate static void hubd_do_callback(hubd_t *hubd, dev_info_t *dip, 5507c478bd9Sstevel@tonic-gate ddi_eventcookie_t cookie); 5517c478bd9Sstevel@tonic-gate static void hubd_run_callbacks(hubd_t *hubd, usba_event_t type); 5527c478bd9Sstevel@tonic-gate static void hubd_post_event(hubd_t *hubd, usb_port_t port, usba_event_t type); 5537c478bd9Sstevel@tonic-gate static void hubd_create_pm_components(dev_info_t *dip, hubd_t *hubd); 5547c478bd9Sstevel@tonic-gate 5557c478bd9Sstevel@tonic-gate static int hubd_disconnect_event_cb(dev_info_t *dip); 5567c478bd9Sstevel@tonic-gate static int hubd_reconnect_event_cb(dev_info_t *dip); 5577c478bd9Sstevel@tonic-gate static int hubd_pre_suspend_event_cb(dev_info_t *dip); 5587c478bd9Sstevel@tonic-gate static int hubd_post_resume_event_cb(dev_info_t *dip); 5597c478bd9Sstevel@tonic-gate static int hubd_cpr_suspend(hubd_t *hubd); 5607c478bd9Sstevel@tonic-gate static void hubd_cpr_resume(dev_info_t *dip); 5617c478bd9Sstevel@tonic-gate static int hubd_restore_state_cb(dev_info_t *dip); 562ffcd51f3Slg static int hubd_check_same_device(hubd_t *hubd, usb_port_t port); 5637c478bd9Sstevel@tonic-gate 56435f36846Ssl static int hubd_init_power_budget(hubd_t *hubd); 56535f36846Ssl 5667c478bd9Sstevel@tonic-gate static ndi_event_definition_t hubd_ndi_event_defs[] = { 5677c478bd9Sstevel@tonic-gate {USBA_EVENT_TAG_HOT_REMOVAL, DDI_DEVI_REMOVE_EVENT, EPL_KERNEL, 5687c478bd9Sstevel@tonic-gate NDI_EVENT_POST_TO_ALL}, 5697c478bd9Sstevel@tonic-gate {USBA_EVENT_TAG_HOT_INSERTION, DDI_DEVI_INSERT_EVENT, EPL_KERNEL, 5707c478bd9Sstevel@tonic-gate NDI_EVENT_POST_TO_ALL}, 5717c478bd9Sstevel@tonic-gate {USBA_EVENT_TAG_POST_RESUME, USBA_POST_RESUME_EVENT, EPL_KERNEL, 5727c478bd9Sstevel@tonic-gate NDI_EVENT_POST_TO_ALL}, 5737c478bd9Sstevel@tonic-gate {USBA_EVENT_TAG_PRE_SUSPEND, USBA_PRE_SUSPEND_EVENT, EPL_KERNEL, 5747c478bd9Sstevel@tonic-gate NDI_EVENT_POST_TO_ALL} 5757c478bd9Sstevel@tonic-gate }; 5767c478bd9Sstevel@tonic-gate 5777c478bd9Sstevel@tonic-gate #define HUBD_N_NDI_EVENTS \ 5787c478bd9Sstevel@tonic-gate (sizeof (hubd_ndi_event_defs) / sizeof (ndi_event_definition_t)) 5797c478bd9Sstevel@tonic-gate 5807c478bd9Sstevel@tonic-gate static ndi_event_set_t hubd_ndi_events = { 5817c478bd9Sstevel@tonic-gate NDI_EVENTS_REV1, HUBD_N_NDI_EVENTS, hubd_ndi_event_defs}; 5827c478bd9Sstevel@tonic-gate 5837c478bd9Sstevel@tonic-gate /* events received from parent */ 5847c478bd9Sstevel@tonic-gate static usb_event_t hubd_events = { 5857c478bd9Sstevel@tonic-gate hubd_disconnect_event_cb, 5867c478bd9Sstevel@tonic-gate hubd_reconnect_event_cb, 5877c478bd9Sstevel@tonic-gate hubd_pre_suspend_event_cb, 5887c478bd9Sstevel@tonic-gate hubd_post_resume_event_cb 5897c478bd9Sstevel@tonic-gate }; 5907c478bd9Sstevel@tonic-gate 5917c478bd9Sstevel@tonic-gate 5927c478bd9Sstevel@tonic-gate /* 5937c478bd9Sstevel@tonic-gate * hubd_get_soft_state() returns the hubd soft state 594ff0e937bSRaymond Chen * 595ff0e937bSRaymond Chen * WUSB support extends this function to support wire adapter class 596ff0e937bSRaymond Chen * devices. The hubd soft state for the wire adapter class device 597ff0e937bSRaymond Chen * would be stored in usb_root_hubd field of the usba_device structure, 598ff0e937bSRaymond Chen * just as the USB host controller drivers do. 5997c478bd9Sstevel@tonic-gate */ 600ff0e937bSRaymond Chen hubd_t * 6017c478bd9Sstevel@tonic-gate hubd_get_soft_state(dev_info_t *dip) 6027c478bd9Sstevel@tonic-gate { 6037c478bd9Sstevel@tonic-gate if (dip == NULL) { 6047c478bd9Sstevel@tonic-gate 6057c478bd9Sstevel@tonic-gate return (NULL); 6067c478bd9Sstevel@tonic-gate } 6077c478bd9Sstevel@tonic-gate 608ff0e937bSRaymond Chen if (usba_is_root_hub(dip) || usba_is_wa(dip)) { 6097c478bd9Sstevel@tonic-gate usba_device_t *usba_device = usba_get_usba_device(dip); 6107c478bd9Sstevel@tonic-gate 6117c478bd9Sstevel@tonic-gate return (usba_device->usb_root_hubd); 6127c478bd9Sstevel@tonic-gate } else { 6137c478bd9Sstevel@tonic-gate int instance = ddi_get_instance(dip); 6147c478bd9Sstevel@tonic-gate 6157c478bd9Sstevel@tonic-gate return (ddi_get_soft_state(hubd_statep, instance)); 6167c478bd9Sstevel@tonic-gate } 6177c478bd9Sstevel@tonic-gate } 6187c478bd9Sstevel@tonic-gate 6197c478bd9Sstevel@tonic-gate 6207c478bd9Sstevel@tonic-gate /* 6217c478bd9Sstevel@tonic-gate * PM support functions: 6227c478bd9Sstevel@tonic-gate */ 6237c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 6247c478bd9Sstevel@tonic-gate static void 6257c478bd9Sstevel@tonic-gate hubd_pm_busy_component(hubd_t *hubd, dev_info_t *dip, int component) 6267c478bd9Sstevel@tonic-gate { 6277c478bd9Sstevel@tonic-gate if (hubd->h_hubpm != NULL) { 6287c478bd9Sstevel@tonic-gate hubd->h_hubpm->hubp_busy_pm++; 6297c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6307c478bd9Sstevel@tonic-gate if (pm_busy_component(dip, 0) != DDI_SUCCESS) { 6317c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6327c478bd9Sstevel@tonic-gate hubd->h_hubpm->hubp_busy_pm--; 6337c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6347c478bd9Sstevel@tonic-gate } 6357c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6367c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 6377c478bd9Sstevel@tonic-gate "hubd_pm_busy_component: %d", hubd->h_hubpm->hubp_busy_pm); 6387c478bd9Sstevel@tonic-gate } 6397c478bd9Sstevel@tonic-gate } 6407c478bd9Sstevel@tonic-gate 6417c478bd9Sstevel@tonic-gate 6427c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 6437c478bd9Sstevel@tonic-gate static void 6447c478bd9Sstevel@tonic-gate hubd_pm_idle_component(hubd_t *hubd, dev_info_t *dip, int component) 6457c478bd9Sstevel@tonic-gate { 6467c478bd9Sstevel@tonic-gate if (hubd->h_hubpm != NULL) { 6477c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6487c478bd9Sstevel@tonic-gate if (pm_idle_component(dip, 0) == DDI_SUCCESS) { 6497c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6507c478bd9Sstevel@tonic-gate ASSERT(hubd->h_hubpm->hubp_busy_pm > 0); 6517c478bd9Sstevel@tonic-gate hubd->h_hubpm->hubp_busy_pm--; 6527c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 6537c478bd9Sstevel@tonic-gate } 6547c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6557c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 6567c478bd9Sstevel@tonic-gate "hubd_pm_idle_component: %d", hubd->h_hubpm->hubp_busy_pm); 6577c478bd9Sstevel@tonic-gate } 6587c478bd9Sstevel@tonic-gate } 6597c478bd9Sstevel@tonic-gate 6607c478bd9Sstevel@tonic-gate 6617c478bd9Sstevel@tonic-gate /* 6627c478bd9Sstevel@tonic-gate * track power level changes for children of this instance 6637c478bd9Sstevel@tonic-gate */ 6647c478bd9Sstevel@tonic-gate static void 6657c478bd9Sstevel@tonic-gate hubd_set_child_pwrlvl(hubd_t *hubd, usb_port_t port, uint8_t power) 6667c478bd9Sstevel@tonic-gate { 6677c478bd9Sstevel@tonic-gate int old_power, new_power, pwr; 6687c478bd9Sstevel@tonic-gate usb_port_t portno; 6697c478bd9Sstevel@tonic-gate hub_power_t *hubpm; 6707c478bd9Sstevel@tonic-gate 6717c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 6727c478bd9Sstevel@tonic-gate "hubd_set_child_pwrlvl: port=%d power=%d", 6737c478bd9Sstevel@tonic-gate port, power); 6747c478bd9Sstevel@tonic-gate 6757c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 6767c478bd9Sstevel@tonic-gate hubpm = hubd->h_hubpm; 6777c478bd9Sstevel@tonic-gate 6787c478bd9Sstevel@tonic-gate old_power = 0; 6797c478bd9Sstevel@tonic-gate for (portno = 1; portno <= hubd->h_hub_descr.bNbrPorts; portno++) { 6807c478bd9Sstevel@tonic-gate old_power += hubpm->hubp_child_pwrstate[portno]; 6817c478bd9Sstevel@tonic-gate } 6827c478bd9Sstevel@tonic-gate 6837c478bd9Sstevel@tonic-gate /* assign the port power */ 6847c478bd9Sstevel@tonic-gate pwr = hubd->h_hubpm->hubp_child_pwrstate[port]; 6857c478bd9Sstevel@tonic-gate hubd->h_hubpm->hubp_child_pwrstate[port] = power; 6867c478bd9Sstevel@tonic-gate new_power = old_power - pwr + power; 6877c478bd9Sstevel@tonic-gate 6887c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 6897c478bd9Sstevel@tonic-gate "hubd_set_child_pwrlvl: new_power=%d old_power=%d", 6907c478bd9Sstevel@tonic-gate new_power, old_power); 6917c478bd9Sstevel@tonic-gate 6927c478bd9Sstevel@tonic-gate if ((new_power > 0) && (old_power == 0)) { 6937c478bd9Sstevel@tonic-gate /* we have the first child coming out of low power */ 6947c478bd9Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0); 6957c478bd9Sstevel@tonic-gate } else if ((new_power == 0) && (old_power > 0)) { 6967c478bd9Sstevel@tonic-gate /* we have the last child going to low power */ 6977c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0); 6987c478bd9Sstevel@tonic-gate } 6997c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 7007c478bd9Sstevel@tonic-gate } 7017c478bd9Sstevel@tonic-gate 7027c478bd9Sstevel@tonic-gate 7037c478bd9Sstevel@tonic-gate /* 7047c478bd9Sstevel@tonic-gate * given a child dip, locate its port number 7057c478bd9Sstevel@tonic-gate */ 7067c478bd9Sstevel@tonic-gate static usb_port_t 7077c478bd9Sstevel@tonic-gate hubd_child_dip2port(hubd_t *hubd, dev_info_t *dip) 7087c478bd9Sstevel@tonic-gate { 7097c478bd9Sstevel@tonic-gate usb_port_t port; 7107c478bd9Sstevel@tonic-gate 7117c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 7127c478bd9Sstevel@tonic-gate for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { 7137c478bd9Sstevel@tonic-gate if (hubd->h_children_dips[port] == dip) { 7147c478bd9Sstevel@tonic-gate 7157c478bd9Sstevel@tonic-gate break; 7167c478bd9Sstevel@tonic-gate } 7177c478bd9Sstevel@tonic-gate } 7187c478bd9Sstevel@tonic-gate ASSERT(port <= hubd->h_hub_descr.bNbrPorts); 7197c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 7207c478bd9Sstevel@tonic-gate 7217c478bd9Sstevel@tonic-gate return (port); 7227c478bd9Sstevel@tonic-gate } 7237c478bd9Sstevel@tonic-gate 7247c478bd9Sstevel@tonic-gate 7257c478bd9Sstevel@tonic-gate /* 7267c478bd9Sstevel@tonic-gate * if the hub can be put into low power mode, return success 7277c478bd9Sstevel@tonic-gate * NOTE: suspend here means going to lower power, not CPR suspend. 7287c478bd9Sstevel@tonic-gate */ 7297c478bd9Sstevel@tonic-gate static int 7307c478bd9Sstevel@tonic-gate hubd_can_suspend(hubd_t *hubd) 7317c478bd9Sstevel@tonic-gate { 7327c478bd9Sstevel@tonic-gate hub_power_t *hubpm; 7337c478bd9Sstevel@tonic-gate int total_power = 0; 7347c478bd9Sstevel@tonic-gate usb_port_t port; 7357c478bd9Sstevel@tonic-gate 7367c478bd9Sstevel@tonic-gate hubpm = hubd->h_hubpm; 7377c478bd9Sstevel@tonic-gate 7387c478bd9Sstevel@tonic-gate if (DEVI_IS_DETACHING(hubd->h_dip)) { 7397c478bd9Sstevel@tonic-gate 7407c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 7417c478bd9Sstevel@tonic-gate } 7427c478bd9Sstevel@tonic-gate 7437c478bd9Sstevel@tonic-gate /* 7447c478bd9Sstevel@tonic-gate * Don't go to lower power if haven't been at full power for enough 7457c478bd9Sstevel@tonic-gate * time to let hotplug thread kickoff. 7467c478bd9Sstevel@tonic-gate */ 7477c478bd9Sstevel@tonic-gate if (ddi_get_time() < (hubpm->hubp_time_at_full_power + 7487c478bd9Sstevel@tonic-gate hubpm->hubp_min_pm_threshold)) { 7497c478bd9Sstevel@tonic-gate 7507c478bd9Sstevel@tonic-gate return (USB_FAILURE); 7517c478bd9Sstevel@tonic-gate } 7527c478bd9Sstevel@tonic-gate 7537c478bd9Sstevel@tonic-gate for (port = 1; (total_power == 0) && 7547c478bd9Sstevel@tonic-gate (port <= hubd->h_hub_descr.bNbrPorts); port++) { 7557c478bd9Sstevel@tonic-gate total_power += hubpm->hubp_child_pwrstate[port]; 7567c478bd9Sstevel@tonic-gate } 7577c478bd9Sstevel@tonic-gate 7587c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 759c0f24e5bSlg "hubd_can_suspend: %d", total_power); 7607c478bd9Sstevel@tonic-gate 7617c478bd9Sstevel@tonic-gate return (total_power ? USB_FAILURE : USB_SUCCESS); 7627c478bd9Sstevel@tonic-gate } 7637c478bd9Sstevel@tonic-gate 7647c478bd9Sstevel@tonic-gate 7657c478bd9Sstevel@tonic-gate /* 7667c478bd9Sstevel@tonic-gate * resume port depending on current device state 7677c478bd9Sstevel@tonic-gate */ 7687c478bd9Sstevel@tonic-gate static int 7697c478bd9Sstevel@tonic-gate hubd_resume_port(hubd_t *hubd, usb_port_t port) 7707c478bd9Sstevel@tonic-gate { 7717c478bd9Sstevel@tonic-gate int rval, retry; 7727c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 7737c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 7747c478bd9Sstevel@tonic-gate uint16_t status; 7757c478bd9Sstevel@tonic-gate uint16_t change; 7767c478bd9Sstevel@tonic-gate int retval = USB_FAILURE; 7777c478bd9Sstevel@tonic-gate 7787c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 7797c478bd9Sstevel@tonic-gate 7807c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 7817c478bd9Sstevel@tonic-gate "hubd_resume_port: port=%d state=0x%x (%s)", port, 7827c478bd9Sstevel@tonic-gate hubd->h_dev_state, usb_str_dev_state(hubd->h_dev_state)); 7837c478bd9Sstevel@tonic-gate 7847c478bd9Sstevel@tonic-gate switch (hubd->h_dev_state) { 7857c478bd9Sstevel@tonic-gate case USB_DEV_HUB_CHILD_PWRLVL: 7867c478bd9Sstevel@tonic-gate /* 7877c478bd9Sstevel@tonic-gate * This could be a bus ctl for a port other than the one 7887c478bd9Sstevel@tonic-gate * that has a remote wakeup condition. So check. 7897c478bd9Sstevel@tonic-gate */ 7907c478bd9Sstevel@tonic-gate if ((hubd->h_port_state[port] & PORT_STATUS_PSS) == 0) { 7917c478bd9Sstevel@tonic-gate /* the port isn't suspended, so don't resume */ 7927c478bd9Sstevel@tonic-gate retval = USB_SUCCESS; 7937c478bd9Sstevel@tonic-gate 7947c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 7957c478bd9Sstevel@tonic-gate "hubd_resume_port: port=%d not suspended", port); 7967c478bd9Sstevel@tonic-gate 7977c478bd9Sstevel@tonic-gate break; 7987c478bd9Sstevel@tonic-gate } 7997c478bd9Sstevel@tonic-gate /* 8007c478bd9Sstevel@tonic-gate * Device has initiated a wakeup. 8017c478bd9Sstevel@tonic-gate * Issue a ClearFeature(PortSuspend) 8027c478bd9Sstevel@tonic-gate */ 8037c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 8047c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 8057c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 80635f36846Ssl HUB_HANDLE_PORT_FEATURE_TYPE, 8077c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 8087c478bd9Sstevel@tonic-gate CFS_PORT_SUSPEND, 8097c478bd9Sstevel@tonic-gate port, 8107c478bd9Sstevel@tonic-gate 0, NULL, 0, 8117c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 8127c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 8137c478bd9Sstevel@tonic-gate "ClearFeature(PortSuspend) fails " 8147c478bd9Sstevel@tonic-gate "rval=%d cr=%d cb=0x%x", rval, 8157c478bd9Sstevel@tonic-gate completion_reason, cb_flags); 8167c478bd9Sstevel@tonic-gate } 8177c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 8187c478bd9Sstevel@tonic-gate 8197c478bd9Sstevel@tonic-gate /* either way ack changes on the port */ 8207c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 821c0f24e5bSlg &status, &change, PORT_CHANGE_PSSC); 8227c478bd9Sstevel@tonic-gate retval = USB_SUCCESS; 8237c478bd9Sstevel@tonic-gate 8247c478bd9Sstevel@tonic-gate break; 8257c478bd9Sstevel@tonic-gate case USB_DEV_HUB_STATE_RECOVER: 8267c478bd9Sstevel@tonic-gate /* 8277c478bd9Sstevel@tonic-gate * When hubd's connect event callback posts a connect 8287c478bd9Sstevel@tonic-gate * event to its child, it results in this busctl call 8297c478bd9Sstevel@tonic-gate * which is valid 8307c478bd9Sstevel@tonic-gate */ 8317c478bd9Sstevel@tonic-gate /* FALLTHRU */ 8327c478bd9Sstevel@tonic-gate case USB_DEV_ONLINE: 8336c7181fcSsl if (((hubd->h_port_state[port] & PORT_STATUS_CCS) == 0) || 8346c7181fcSsl ((hubd->h_port_state[port] & PORT_STATUS_PSS) == 0)) { 8357c478bd9Sstevel@tonic-gate /* 8367c478bd9Sstevel@tonic-gate * the port isn't suspended, or connected 8377c478bd9Sstevel@tonic-gate * so don't resume 8387c478bd9Sstevel@tonic-gate */ 8397c478bd9Sstevel@tonic-gate retval = USB_SUCCESS; 8407c478bd9Sstevel@tonic-gate 8417c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 8427c478bd9Sstevel@tonic-gate "hubd_resume_port: port=%d not suspended", port); 8437c478bd9Sstevel@tonic-gate 8447c478bd9Sstevel@tonic-gate break; 8457c478bd9Sstevel@tonic-gate } 8467c478bd9Sstevel@tonic-gate /* 8477c478bd9Sstevel@tonic-gate * prevent kicking off the hotplug thread 8487c478bd9Sstevel@tonic-gate */ 8497c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread++; 8507c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd); 8517c478bd9Sstevel@tonic-gate 8527c478bd9Sstevel@tonic-gate /* Now ClearFeature(PortSuspend) */ 8537c478bd9Sstevel@tonic-gate for (retry = 0; retry < HUBD_SUS_RES_RETRY; retry++) { 8547c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 8557c478bd9Sstevel@tonic-gate rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 8567c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 85735f36846Ssl HUB_HANDLE_PORT_FEATURE_TYPE, 8587c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 8597c478bd9Sstevel@tonic-gate CFS_PORT_SUSPEND, 8607c478bd9Sstevel@tonic-gate port, 8617c478bd9Sstevel@tonic-gate 0, NULL, 0, 8627c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0); 8637c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 8647c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 8657c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, 8667c478bd9Sstevel@tonic-gate hubd->h_log_handle, 8677c478bd9Sstevel@tonic-gate "ClearFeature(PortSuspend) fails" 8687c478bd9Sstevel@tonic-gate "rval=%d cr=%d cb=0x%x", rval, 8697c478bd9Sstevel@tonic-gate completion_reason, cb_flags); 8707c478bd9Sstevel@tonic-gate } else { 8717c478bd9Sstevel@tonic-gate /* 8727c478bd9Sstevel@tonic-gate * As per spec section 11.9 and 7.1.7.7 8737c478bd9Sstevel@tonic-gate * hub need to provide at least 20ms of 8747c478bd9Sstevel@tonic-gate * resume signalling, and s/w provide 10ms of 8757c478bd9Sstevel@tonic-gate * recovery time before accessing the port. 8767c478bd9Sstevel@tonic-gate */ 8777c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 8787c478bd9Sstevel@tonic-gate delay(drv_usectohz(40000)); 8797c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 8807c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 8817c478bd9Sstevel@tonic-gate &status, &change, PORT_CHANGE_PSSC); 8827c478bd9Sstevel@tonic-gate 8837c478bd9Sstevel@tonic-gate if ((status & PORT_STATUS_PSS) == 0) { 8847c478bd9Sstevel@tonic-gate /* the port did finally resume */ 8857c478bd9Sstevel@tonic-gate retval = USB_SUCCESS; 8867c478bd9Sstevel@tonic-gate 8877c478bd9Sstevel@tonic-gate break; 8887c478bd9Sstevel@tonic-gate } 8897c478bd9Sstevel@tonic-gate } 8907c478bd9Sstevel@tonic-gate } 8917c478bd9Sstevel@tonic-gate 8927c478bd9Sstevel@tonic-gate /* allow hotplug thread again */ 8937c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread--; 8947c478bd9Sstevel@tonic-gate hubd_start_polling(hubd, 0); 8957c478bd9Sstevel@tonic-gate 8967c478bd9Sstevel@tonic-gate break; 8977c478bd9Sstevel@tonic-gate case USB_DEV_DISCONNECTED: 8987c478bd9Sstevel@tonic-gate /* Ignore - NO Operation */ 8997c478bd9Sstevel@tonic-gate retval = USB_SUCCESS; 9007c478bd9Sstevel@tonic-gate 9017c478bd9Sstevel@tonic-gate break; 9027c478bd9Sstevel@tonic-gate case USB_DEV_SUSPENDED: 9037c478bd9Sstevel@tonic-gate case USB_DEV_PWRED_DOWN: 9047c478bd9Sstevel@tonic-gate default: 9057c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 9067c478bd9Sstevel@tonic-gate "Improper state for port Resume"); 9077c478bd9Sstevel@tonic-gate 9087c478bd9Sstevel@tonic-gate break; 9097c478bd9Sstevel@tonic-gate } 9107c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 9117c478bd9Sstevel@tonic-gate 9127c478bd9Sstevel@tonic-gate return (retval); 9137c478bd9Sstevel@tonic-gate } 9147c478bd9Sstevel@tonic-gate 9157c478bd9Sstevel@tonic-gate 9167c478bd9Sstevel@tonic-gate /* 9177c478bd9Sstevel@tonic-gate * suspend port depending on device state 9187c478bd9Sstevel@tonic-gate */ 9197c478bd9Sstevel@tonic-gate static int 9207c478bd9Sstevel@tonic-gate hubd_suspend_port(hubd_t *hubd, usb_port_t port) 9217c478bd9Sstevel@tonic-gate { 9227c478bd9Sstevel@tonic-gate int rval, retry; 9237c478bd9Sstevel@tonic-gate int retval = USB_FAILURE; 9247c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 9257c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 9267c478bd9Sstevel@tonic-gate uint16_t status; 9277c478bd9Sstevel@tonic-gate uint16_t change; 9287c478bd9Sstevel@tonic-gate 9297c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 9307c478bd9Sstevel@tonic-gate "hubd_suspend_port: port=%d", port); 9317c478bd9Sstevel@tonic-gate 9327c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 9337c478bd9Sstevel@tonic-gate 9347c478bd9Sstevel@tonic-gate switch (hubd->h_dev_state) { 9357c478bd9Sstevel@tonic-gate case USB_DEV_HUB_STATE_RECOVER: 9367c478bd9Sstevel@tonic-gate /* 9377c478bd9Sstevel@tonic-gate * When hubd's connect event callback posts a connect 9387c478bd9Sstevel@tonic-gate * event to its child, it results in this busctl call 9397c478bd9Sstevel@tonic-gate * which is valid 9407c478bd9Sstevel@tonic-gate */ 9417c478bd9Sstevel@tonic-gate /* FALLTHRU */ 9427c478bd9Sstevel@tonic-gate case USB_DEV_HUB_CHILD_PWRLVL: 9437c478bd9Sstevel@tonic-gate /* 9447c478bd9Sstevel@tonic-gate * When one child is resuming, the other could timeout 9457c478bd9Sstevel@tonic-gate * and go to low power mode, which is valid 9467c478bd9Sstevel@tonic-gate */ 9477c478bd9Sstevel@tonic-gate /* FALLTHRU */ 9487c478bd9Sstevel@tonic-gate case USB_DEV_ONLINE: 9497c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread++; 9507c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd); 9517c478bd9Sstevel@tonic-gate 9527c478bd9Sstevel@tonic-gate /* 9537c478bd9Sstevel@tonic-gate * Some devices start an unprovoked resume. According to spec, 9547c478bd9Sstevel@tonic-gate * normal resume time for port is 10ms. Wait for double that 9557c478bd9Sstevel@tonic-gate * time, then check to be sure port is really suspended. 9567c478bd9Sstevel@tonic-gate */ 9577c478bd9Sstevel@tonic-gate for (retry = 0; retry < HUBD_SUS_RES_RETRY; retry++) { 9587c478bd9Sstevel@tonic-gate /* Now SetFeature(PortSuspend) */ 9597c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 9607c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 9617c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 96235f36846Ssl HUB_HANDLE_PORT_FEATURE_TYPE, 9637c478bd9Sstevel@tonic-gate USB_REQ_SET_FEATURE, 9647c478bd9Sstevel@tonic-gate CFS_PORT_SUSPEND, 9657c478bd9Sstevel@tonic-gate port, 9667c478bd9Sstevel@tonic-gate 0, NULL, 0, 9677c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != 9687c478bd9Sstevel@tonic-gate USB_SUCCESS) { 9697c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, 9707c478bd9Sstevel@tonic-gate hubd->h_log_handle, 9717c478bd9Sstevel@tonic-gate "SetFeature(PortSuspend) fails" 9727c478bd9Sstevel@tonic-gate "rval=%d cr=%d cb=0x%x", 9737c478bd9Sstevel@tonic-gate rval, completion_reason, cb_flags); 9747c478bd9Sstevel@tonic-gate } 9757c478bd9Sstevel@tonic-gate 9767c478bd9Sstevel@tonic-gate /* 9777c478bd9Sstevel@tonic-gate * some devices start an unprovoked resume 9787c478bd9Sstevel@tonic-gate * wait and check port status after some time 9797c478bd9Sstevel@tonic-gate */ 9807c478bd9Sstevel@tonic-gate delay(drv_usectohz(20000)); 9817c478bd9Sstevel@tonic-gate 9827c478bd9Sstevel@tonic-gate /* either ways ack changes on the port */ 9837c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 9847c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 9857c478bd9Sstevel@tonic-gate &status, &change, PORT_CHANGE_PSSC); 9867c478bd9Sstevel@tonic-gate if (status & PORT_STATUS_PSS) { 9877c478bd9Sstevel@tonic-gate /* the port is indeed suspended */ 9887c478bd9Sstevel@tonic-gate retval = USB_SUCCESS; 9897c478bd9Sstevel@tonic-gate 9907c478bd9Sstevel@tonic-gate break; 9917c478bd9Sstevel@tonic-gate } 9927c478bd9Sstevel@tonic-gate } 9937c478bd9Sstevel@tonic-gate 9947c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread--; 9957c478bd9Sstevel@tonic-gate hubd_start_polling(hubd, 0); 9967c478bd9Sstevel@tonic-gate 9977c478bd9Sstevel@tonic-gate break; 9987c478bd9Sstevel@tonic-gate 9997c478bd9Sstevel@tonic-gate case USB_DEV_DISCONNECTED: 10007c478bd9Sstevel@tonic-gate /* Ignore - No Operation */ 10017c478bd9Sstevel@tonic-gate retval = USB_SUCCESS; 10027c478bd9Sstevel@tonic-gate 10037c478bd9Sstevel@tonic-gate break; 10047c478bd9Sstevel@tonic-gate 10057c478bd9Sstevel@tonic-gate case USB_DEV_SUSPENDED: 10067c478bd9Sstevel@tonic-gate case USB_DEV_PWRED_DOWN: 10077c478bd9Sstevel@tonic-gate default: 10087c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 10097c478bd9Sstevel@tonic-gate "Improper state for port Suspend"); 10107c478bd9Sstevel@tonic-gate 10117c478bd9Sstevel@tonic-gate break; 10127c478bd9Sstevel@tonic-gate } 10137c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 10147c478bd9Sstevel@tonic-gate 10157c478bd9Sstevel@tonic-gate return (retval); 10167c478bd9Sstevel@tonic-gate } 10177c478bd9Sstevel@tonic-gate 10187c478bd9Sstevel@tonic-gate 10197c478bd9Sstevel@tonic-gate /* 10207c478bd9Sstevel@tonic-gate * child post attach/detach notifications 10217c478bd9Sstevel@tonic-gate */ 10227c478bd9Sstevel@tonic-gate static void 10237c478bd9Sstevel@tonic-gate hubd_post_attach(hubd_t *hubd, usb_port_t port, struct attachspec *as) 10247c478bd9Sstevel@tonic-gate { 10257c478bd9Sstevel@tonic-gate dev_info_t *dip; 10267c478bd9Sstevel@tonic-gate 10277c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 10287c478bd9Sstevel@tonic-gate "hubd_post_attach: port=%d result=%d", 10297c478bd9Sstevel@tonic-gate port, as->result); 10307c478bd9Sstevel@tonic-gate 10317c478bd9Sstevel@tonic-gate if (as->result == DDI_SUCCESS) { 10327c478bd9Sstevel@tonic-gate /* 10337c478bd9Sstevel@tonic-gate * Check if the child created wants to be power managed. 10347c478bd9Sstevel@tonic-gate * If yes, the childs power level gets automatically tracked 10357c478bd9Sstevel@tonic-gate * by DDI_CTLOPS_POWER busctl. 10367c478bd9Sstevel@tonic-gate * If no, we set power of the new child by default 10377c478bd9Sstevel@tonic-gate * to USB_DEV_OS_FULL_PWR. Because we should never suspend. 10387c478bd9Sstevel@tonic-gate */ 10397c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 10407c478bd9Sstevel@tonic-gate dip = hubd->h_children_dips[port]; 10417c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 10427c478bd9Sstevel@tonic-gate if (DEVI(dip)->devi_pm_info == NULL) { 10437c478bd9Sstevel@tonic-gate hubd_set_child_pwrlvl(hubd, port, USB_DEV_OS_FULL_PWR); 10447c478bd9Sstevel@tonic-gate } 10457c478bd9Sstevel@tonic-gate } 10467c478bd9Sstevel@tonic-gate } 10477c478bd9Sstevel@tonic-gate 10487c478bd9Sstevel@tonic-gate 10497c478bd9Sstevel@tonic-gate static void 10507c478bd9Sstevel@tonic-gate hubd_post_detach(hubd_t *hubd, usb_port_t port, struct detachspec *ds) 10517c478bd9Sstevel@tonic-gate { 10527c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 10537c478bd9Sstevel@tonic-gate "hubd_post_detach: port=%d result=%d", port, ds->result); 10547c478bd9Sstevel@tonic-gate 10557c478bd9Sstevel@tonic-gate /* 10567c478bd9Sstevel@tonic-gate * if the device is successfully detached and is the 10577c478bd9Sstevel@tonic-gate * last device to detach, mark component as idle 10587c478bd9Sstevel@tonic-gate */ 10597c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 10607c478bd9Sstevel@tonic-gate if (ds->result == DDI_SUCCESS) { 10617c478bd9Sstevel@tonic-gate usba_device_t *usba_device = hubd->h_usba_devices[port]; 106235f36846Ssl dev_info_t *pdip = hubd->h_dip; 10637c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 10647c478bd9Sstevel@tonic-gate 106535f36846Ssl usba_hubdi_incr_power_budget(pdip, usba_device); 106635f36846Ssl 10677c478bd9Sstevel@tonic-gate /* 10687c478bd9Sstevel@tonic-gate * We set power of the detached child 10697c478bd9Sstevel@tonic-gate * to 0, so that we can suspend if all 10707c478bd9Sstevel@tonic-gate * our children are gone 10717c478bd9Sstevel@tonic-gate */ 10727c478bd9Sstevel@tonic-gate hubd_set_child_pwrlvl(hubd, port, USB_DEV_OS_PWR_OFF); 10737c478bd9Sstevel@tonic-gate 10747c478bd9Sstevel@tonic-gate /* check for leaks on detaching */ 10757c478bd9Sstevel@tonic-gate if ((usba_device) && (ds->cmd == DDI_DETACH)) { 10767c478bd9Sstevel@tonic-gate usba_check_for_leaks(usba_device); 10777c478bd9Sstevel@tonic-gate } 10787c478bd9Sstevel@tonic-gate } else { 10797c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 10807c478bd9Sstevel@tonic-gate } 10817c478bd9Sstevel@tonic-gate } 10827c478bd9Sstevel@tonic-gate 10837c478bd9Sstevel@tonic-gate 10847c478bd9Sstevel@tonic-gate /* 10857c478bd9Sstevel@tonic-gate * hubd_post_power 10867c478bd9Sstevel@tonic-gate * After the child's power entry point has been called 10877c478bd9Sstevel@tonic-gate * we record its power level in our local struct. 10887c478bd9Sstevel@tonic-gate * If the device has powered off, we suspend port 10897c478bd9Sstevel@tonic-gate */ 10907c478bd9Sstevel@tonic-gate static int 10917c478bd9Sstevel@tonic-gate hubd_post_power(hubd_t *hubd, usb_port_t port, pm_bp_child_pwrchg_t *bpc, 10927c478bd9Sstevel@tonic-gate int result) 10937c478bd9Sstevel@tonic-gate { 10947c478bd9Sstevel@tonic-gate int retval = USB_SUCCESS; 10957c478bd9Sstevel@tonic-gate 10967c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 10977c478bd9Sstevel@tonic-gate "hubd_post_power: port=%d", port); 10987c478bd9Sstevel@tonic-gate 10997c478bd9Sstevel@tonic-gate if (result == DDI_SUCCESS) { 11007c478bd9Sstevel@tonic-gate 11017c478bd9Sstevel@tonic-gate /* record this power in our local struct */ 11027c478bd9Sstevel@tonic-gate hubd_set_child_pwrlvl(hubd, port, bpc->bpc_nlevel); 11037c478bd9Sstevel@tonic-gate 11047c478bd9Sstevel@tonic-gate if (bpc->bpc_nlevel == USB_DEV_OS_PWR_OFF) { 11057c478bd9Sstevel@tonic-gate 11067c478bd9Sstevel@tonic-gate /* now suspend the port */ 11077c478bd9Sstevel@tonic-gate retval = hubd_suspend_port(hubd, port); 11087c478bd9Sstevel@tonic-gate } else if (bpc->bpc_nlevel == USB_DEV_OS_FULL_PWR) { 11097c478bd9Sstevel@tonic-gate 11107c478bd9Sstevel@tonic-gate /* make sure the port is resumed */ 11117c478bd9Sstevel@tonic-gate retval = hubd_resume_port(hubd, port); 11127c478bd9Sstevel@tonic-gate } 11137c478bd9Sstevel@tonic-gate } else { 11147c478bd9Sstevel@tonic-gate 11157c478bd9Sstevel@tonic-gate /* record old power in our local struct */ 11167c478bd9Sstevel@tonic-gate hubd_set_child_pwrlvl(hubd, port, bpc->bpc_olevel); 11177c478bd9Sstevel@tonic-gate 11187c478bd9Sstevel@tonic-gate if (bpc->bpc_olevel == USB_DEV_OS_PWR_OFF) { 11197c478bd9Sstevel@tonic-gate 11207c478bd9Sstevel@tonic-gate /* 11217c478bd9Sstevel@tonic-gate * As this device failed to transition from 11227c478bd9Sstevel@tonic-gate * power off state, suspend the port again 11237c478bd9Sstevel@tonic-gate */ 11247c478bd9Sstevel@tonic-gate retval = hubd_suspend_port(hubd, port); 11257c478bd9Sstevel@tonic-gate } 11267c478bd9Sstevel@tonic-gate } 11277c478bd9Sstevel@tonic-gate 11287c478bd9Sstevel@tonic-gate return (retval); 11297c478bd9Sstevel@tonic-gate } 11307c478bd9Sstevel@tonic-gate 11317c478bd9Sstevel@tonic-gate 11327c478bd9Sstevel@tonic-gate /* 11337c478bd9Sstevel@tonic-gate * bus ctl notifications are handled here, the rest goes up to root hub/hcd 11347c478bd9Sstevel@tonic-gate */ 11357c478bd9Sstevel@tonic-gate static int 11367c478bd9Sstevel@tonic-gate usba_hubdi_bus_ctl(dev_info_t *dip, 11377c478bd9Sstevel@tonic-gate dev_info_t *rdip, 11387c478bd9Sstevel@tonic-gate ddi_ctl_enum_t op, 11397c478bd9Sstevel@tonic-gate void *arg, 11407c478bd9Sstevel@tonic-gate void *result) 11417c478bd9Sstevel@tonic-gate { 11427c478bd9Sstevel@tonic-gate usba_device_t *hub_usba_device = usba_get_usba_device(rdip); 11437c478bd9Sstevel@tonic-gate dev_info_t *root_hub_dip = hub_usba_device->usb_root_hub_dip; 11447c478bd9Sstevel@tonic-gate struct attachspec *as; 11457c478bd9Sstevel@tonic-gate struct detachspec *ds; 11467c478bd9Sstevel@tonic-gate hubd_t *hubd; 11477c478bd9Sstevel@tonic-gate usb_port_t port; 11487c478bd9Sstevel@tonic-gate int circ, rval; 11497c478bd9Sstevel@tonic-gate int retval = DDI_FAILURE; 11507c478bd9Sstevel@tonic-gate 11517c478bd9Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 11527c478bd9Sstevel@tonic-gate 11537c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 11547c478bd9Sstevel@tonic-gate 11557c478bd9Sstevel@tonic-gate /* flag that we are currently running bus_ctl */ 11567c478bd9Sstevel@tonic-gate hubd->h_bus_ctls++; 11577c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 11587c478bd9Sstevel@tonic-gate 11597c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle, 11607c478bd9Sstevel@tonic-gate "usba_hubdi_bus_ctl:\n\t" 11617c478bd9Sstevel@tonic-gate "dip=0x%p, rdip=0x%p, op=0x%x, arg=0x%p", 1162112116d8Sfb (void *)dip, (void *)rdip, op, arg); 11637c478bd9Sstevel@tonic-gate 11647c478bd9Sstevel@tonic-gate switch (op) { 11657c478bd9Sstevel@tonic-gate case DDI_CTLOPS_ATTACH: 11667c478bd9Sstevel@tonic-gate as = (struct attachspec *)arg; 11677c478bd9Sstevel@tonic-gate port = hubd_child_dip2port(hubd, rdip); 11687c478bd9Sstevel@tonic-gate 11697c478bd9Sstevel@tonic-gate /* there is nothing to do at resume time */ 11707c478bd9Sstevel@tonic-gate if (as->cmd == DDI_RESUME) { 11717c478bd9Sstevel@tonic-gate break; 11727c478bd9Sstevel@tonic-gate } 11737c478bd9Sstevel@tonic-gate 11747c478bd9Sstevel@tonic-gate /* serialize access */ 11757c478bd9Sstevel@tonic-gate ndi_devi_enter(hubd->h_dip, &circ); 11767c478bd9Sstevel@tonic-gate 11777c478bd9Sstevel@tonic-gate switch (as->when) { 11787c478bd9Sstevel@tonic-gate case DDI_PRE: 11797c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 11807c478bd9Sstevel@tonic-gate "DDI_PRE DDI_CTLOPS_ATTACH: dip=%p, port=%d", 1181112116d8Sfb (void *)rdip, port); 11827c478bd9Sstevel@tonic-gate 11837c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 11847c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= HUBD_CHILD_ATTACHING; 11857c478bd9Sstevel@tonic-gate 11867c478bd9Sstevel@tonic-gate /* Go busy here. Matching idle is DDI_POST case. */ 11877c478bd9Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, dip, 0); 11887c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 11897c478bd9Sstevel@tonic-gate 11907c478bd9Sstevel@tonic-gate /* 11917c478bd9Sstevel@tonic-gate * if we suspended the port previously 11927c478bd9Sstevel@tonic-gate * because child went to low power state, and 11937c478bd9Sstevel@tonic-gate * someone unloaded the driver, the port would 11947c478bd9Sstevel@tonic-gate * still be suspended and needs to be resumed 11957c478bd9Sstevel@tonic-gate */ 11967c478bd9Sstevel@tonic-gate rval = hubd_resume_port(hubd, port); 11977c478bd9Sstevel@tonic-gate if (rval == USB_SUCCESS) { 11987c478bd9Sstevel@tonic-gate retval = DDI_SUCCESS; 11997c478bd9Sstevel@tonic-gate } 12007c478bd9Sstevel@tonic-gate 12017c478bd9Sstevel@tonic-gate break; 12027c478bd9Sstevel@tonic-gate case DDI_POST: 12037c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 12047c478bd9Sstevel@tonic-gate "DDI_POST DDI_CTLOPS_ATTACH: dip=%p, port=%d", 1205112116d8Sfb (void *)rdip, port); 12067c478bd9Sstevel@tonic-gate 12077c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 12087c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~HUBD_CHILD_ATTACHING; 12097c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 12107c478bd9Sstevel@tonic-gate 12117c478bd9Sstevel@tonic-gate hubd_post_attach(hubd, port, (struct attachspec *)arg); 12127c478bd9Sstevel@tonic-gate retval = DDI_SUCCESS; 12137c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 12147c478bd9Sstevel@tonic-gate 12157c478bd9Sstevel@tonic-gate /* Matching idle call for DDI_PRE busy call. */ 12167c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, dip, 0); 12177c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 12187c478bd9Sstevel@tonic-gate } 12197c478bd9Sstevel@tonic-gate ndi_devi_exit(hubd->h_dip, circ); 12207c478bd9Sstevel@tonic-gate 12217c478bd9Sstevel@tonic-gate break; 12227c478bd9Sstevel@tonic-gate case DDI_CTLOPS_DETACH: 12237c478bd9Sstevel@tonic-gate ds = (struct detachspec *)arg; 12247c478bd9Sstevel@tonic-gate port = hubd_child_dip2port(hubd, rdip); 12257c478bd9Sstevel@tonic-gate 12267c478bd9Sstevel@tonic-gate /* there is nothing to do at suspend time */ 12277c478bd9Sstevel@tonic-gate if (ds->cmd == DDI_SUSPEND) { 12287c478bd9Sstevel@tonic-gate break; 12297c478bd9Sstevel@tonic-gate } 12307c478bd9Sstevel@tonic-gate 12317c478bd9Sstevel@tonic-gate /* serialize access */ 12327c478bd9Sstevel@tonic-gate ndi_devi_enter(hubd->h_dip, &circ); 12337c478bd9Sstevel@tonic-gate 12347c478bd9Sstevel@tonic-gate switch (ds->when) { 12357c478bd9Sstevel@tonic-gate case DDI_PRE: 12367c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 12377c478bd9Sstevel@tonic-gate "DDI_PRE DDI_CTLOPS_DETACH: dip=%p port=%d", 1238112116d8Sfb (void *)rdip, port); 12397c478bd9Sstevel@tonic-gate 12407c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 12417c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= HUBD_CHILD_DETACHING; 12427c478bd9Sstevel@tonic-gate 12437c478bd9Sstevel@tonic-gate /* Go busy here. Matching idle is DDI_POST case. */ 12447c478bd9Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, dip, 0); 12457c478bd9Sstevel@tonic-gate 12467c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 12477c478bd9Sstevel@tonic-gate retval = DDI_SUCCESS; 12487c478bd9Sstevel@tonic-gate 12497c478bd9Sstevel@tonic-gate break; 12507c478bd9Sstevel@tonic-gate case DDI_POST: 12517c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 12527c478bd9Sstevel@tonic-gate "DDI_POST DDI_CTLOPS_DETACH: dip=%p port=%d", 1253112116d8Sfb (void *)rdip, port); 12547c478bd9Sstevel@tonic-gate 12557c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 12567c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~HUBD_CHILD_DETACHING; 12577c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 12587c478bd9Sstevel@tonic-gate 12597c478bd9Sstevel@tonic-gate /* Matching idle call for DDI_PRE busy call. */ 12607c478bd9Sstevel@tonic-gate hubd_post_detach(hubd, port, (struct detachspec *)arg); 12617c478bd9Sstevel@tonic-gate retval = DDI_SUCCESS; 12627c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 12637c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, dip, 0); 12647c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 12657c478bd9Sstevel@tonic-gate 12667c478bd9Sstevel@tonic-gate break; 12677c478bd9Sstevel@tonic-gate } 12687c478bd9Sstevel@tonic-gate ndi_devi_exit(hubd->h_dip, circ); 12697c478bd9Sstevel@tonic-gate 12707c478bd9Sstevel@tonic-gate break; 12717c478bd9Sstevel@tonic-gate default: 12727c478bd9Sstevel@tonic-gate retval = usba_bus_ctl(root_hub_dip, rdip, op, arg, result); 12737c478bd9Sstevel@tonic-gate } 12747c478bd9Sstevel@tonic-gate 12757c478bd9Sstevel@tonic-gate /* decrement bus_ctls count */ 12767c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 12777c478bd9Sstevel@tonic-gate hubd->h_bus_ctls--; 12787c478bd9Sstevel@tonic-gate ASSERT(hubd->h_bus_ctls >= 0); 12797c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 12807c478bd9Sstevel@tonic-gate 12817c478bd9Sstevel@tonic-gate return (retval); 12827c478bd9Sstevel@tonic-gate } 12837c478bd9Sstevel@tonic-gate 12847c478bd9Sstevel@tonic-gate 12857c478bd9Sstevel@tonic-gate /* 12867c478bd9Sstevel@tonic-gate * bus enumeration entry points 12877c478bd9Sstevel@tonic-gate */ 12887c478bd9Sstevel@tonic-gate static int 12897c478bd9Sstevel@tonic-gate hubd_bus_config(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op, 12907c478bd9Sstevel@tonic-gate void *arg, dev_info_t **child) 12917c478bd9Sstevel@tonic-gate { 12927c478bd9Sstevel@tonic-gate hubd_t *hubd = hubd_get_soft_state(dip); 12937c478bd9Sstevel@tonic-gate int rval, circ; 12947c478bd9Sstevel@tonic-gate 12957c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 12967c478bd9Sstevel@tonic-gate "hubd_bus_config: op=%d", op); 12977c478bd9Sstevel@tonic-gate 12987c478bd9Sstevel@tonic-gate if (hubdi_bus_config_debug) { 12997c478bd9Sstevel@tonic-gate flag |= NDI_DEVI_DEBUG; 13007c478bd9Sstevel@tonic-gate } 13017c478bd9Sstevel@tonic-gate 13027c478bd9Sstevel@tonic-gate /* 1303c29cc384Svikram * NOTE: we want to delay the mountroot thread which 1304c29cc384Svikram * exclusively does a BUS_CONFIG_ONE, but not the 1305c29cc384Svikram * USB hotplug threads which do the asynchronous 1306d724deadSguoqing zhu - Sun Microsystems - Beijing China * enumeration exlusively via BUS_CONFIG_ALL. 13077c478bd9Sstevel@tonic-gate */ 1308c29cc384Svikram if (!modrootloaded && op == BUS_CONFIG_ONE) { 1309d724deadSguoqing zhu - Sun Microsystems - Beijing China dev_info_t *cdip; 1310d724deadSguoqing zhu - Sun Microsystems - Beijing China int port, found; 1311d724deadSguoqing zhu - Sun Microsystems - Beijing China char cname[80]; 1312d724deadSguoqing zhu - Sun Microsystems - Beijing China int i; 1313d724deadSguoqing zhu - Sun Microsystems - Beijing China char *name, *addr; 1314d724deadSguoqing zhu - Sun Microsystems - Beijing China 1315d724deadSguoqing zhu - Sun Microsystems - Beijing China (void) snprintf(cname, 80, "%s", (char *)arg); 1316d724deadSguoqing zhu - Sun Microsystems - Beijing China /* split name into "name@addr" parts */ 1317d724deadSguoqing zhu - Sun Microsystems - Beijing China i_ddi_parse_name(cname, &name, &addr, NULL); 1318d724deadSguoqing zhu - Sun Microsystems - Beijing China USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 1319d724deadSguoqing zhu - Sun Microsystems - Beijing China "hubd_bus_config: op=%d (BUS_CONFIG_ONE)", op); 1320d724deadSguoqing zhu - Sun Microsystems - Beijing China 1321d724deadSguoqing zhu - Sun Microsystems - Beijing China found = 0; 1322d724deadSguoqing zhu - Sun Microsystems - Beijing China 1323d724deadSguoqing zhu - Sun Microsystems - Beijing China /* 1324d724deadSguoqing zhu - Sun Microsystems - Beijing China * Wait until the device node on the rootpath has 1325d724deadSguoqing zhu - Sun Microsystems - Beijing China * been enumerated by USB hotplug thread. Current 1326d724deadSguoqing zhu - Sun Microsystems - Beijing China * set 10 seconds timeout because if the device is 1327d724deadSguoqing zhu - Sun Microsystems - Beijing China * behind multiple hubs, hub config time at each 1328d724deadSguoqing zhu - Sun Microsystems - Beijing China * layer needs several hundred milliseconds and 1329d724deadSguoqing zhu - Sun Microsystems - Beijing China * all config time maybe take several seconds. 1330d724deadSguoqing zhu - Sun Microsystems - Beijing China */ 1331d724deadSguoqing zhu - Sun Microsystems - Beijing China 1332d724deadSguoqing zhu - Sun Microsystems - Beijing China for (i = 0; i < 100; i++) { 13335321cfb7Spengcheng chen - Sun Microsystems - Beijing China mutex_enter(HUBD_MUTEX(hubd)); 1334d724deadSguoqing zhu - Sun Microsystems - Beijing China for (port = 1; 1335d724deadSguoqing zhu - Sun Microsystems - Beijing China port <= hubd->h_hub_descr.bNbrPorts; port++) { 1336d724deadSguoqing zhu - Sun Microsystems - Beijing China cdip = hubd->h_children_dips[port]; 1337d724deadSguoqing zhu - Sun Microsystems - Beijing China if (!cdip || i_ddi_node_state(cdip) < 1338d724deadSguoqing zhu - Sun Microsystems - Beijing China DS_INITIALIZED) 1339d724deadSguoqing zhu - Sun Microsystems - Beijing China continue; 1340d724deadSguoqing zhu - Sun Microsystems - Beijing China if (strcmp(name, DEVI(cdip)->devi_node_name)) 1341d724deadSguoqing zhu - Sun Microsystems - Beijing China continue; 1342d724deadSguoqing zhu - Sun Microsystems - Beijing China if (strcmp(addr, DEVI(cdip)->devi_addr)) 1343d724deadSguoqing zhu - Sun Microsystems - Beijing China continue; 1344d724deadSguoqing zhu - Sun Microsystems - Beijing China found = 1; 1345d724deadSguoqing zhu - Sun Microsystems - Beijing China break; 1346d724deadSguoqing zhu - Sun Microsystems - Beijing China } 13475321cfb7Spengcheng chen - Sun Microsystems - Beijing China mutex_exit(HUBD_MUTEX(hubd)); 13485321cfb7Spengcheng chen - Sun Microsystems - Beijing China 1349d724deadSguoqing zhu - Sun Microsystems - Beijing China if (found) 1350d724deadSguoqing zhu - Sun Microsystems - Beijing China break; 1351d724deadSguoqing zhu - Sun Microsystems - Beijing China delay(drv_usectohz(100000)); 1352d724deadSguoqing zhu - Sun Microsystems - Beijing China } 1353d724deadSguoqing zhu - Sun Microsystems - Beijing China if (found == 0) { 1354d724deadSguoqing zhu - Sun Microsystems - Beijing China cmn_err(CE_WARN, 1355d724deadSguoqing zhu - Sun Microsystems - Beijing China "hubd_bus_config: failed for config child %s", 1356d724deadSguoqing zhu - Sun Microsystems - Beijing China (char *)arg); 1357d724deadSguoqing zhu - Sun Microsystems - Beijing China return (NDI_FAILURE); 1358d724deadSguoqing zhu - Sun Microsystems - Beijing China } 1359d724deadSguoqing zhu - Sun Microsystems - Beijing China 13607c478bd9Sstevel@tonic-gate } 13617c478bd9Sstevel@tonic-gate ndi_devi_enter(hubd->h_dip, &circ); 13627c478bd9Sstevel@tonic-gate rval = ndi_busop_bus_config(dip, flag, op, arg, child, 0); 13637c478bd9Sstevel@tonic-gate ndi_devi_exit(hubd->h_dip, circ); 13647c478bd9Sstevel@tonic-gate 13657c478bd9Sstevel@tonic-gate return (rval); 13667c478bd9Sstevel@tonic-gate } 13677c478bd9Sstevel@tonic-gate 13687c478bd9Sstevel@tonic-gate 13697c478bd9Sstevel@tonic-gate static int 13707c478bd9Sstevel@tonic-gate hubd_bus_unconfig(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op, 13717c478bd9Sstevel@tonic-gate void *arg) 13727c478bd9Sstevel@tonic-gate { 13737c478bd9Sstevel@tonic-gate hubd_t *hubd = hubd_get_soft_state(dip); 13747c478bd9Sstevel@tonic-gate dev_info_t *cdip; 13757c478bd9Sstevel@tonic-gate usb_port_t port; 13767c478bd9Sstevel@tonic-gate int circ; 13777c478bd9Sstevel@tonic-gate int rval; 13787c478bd9Sstevel@tonic-gate 13797c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 13807c478bd9Sstevel@tonic-gate "hubd_bus_unconfig: op=%d", op); 13817c478bd9Sstevel@tonic-gate 13827c478bd9Sstevel@tonic-gate if (hubdi_bus_config_debug) { 13837c478bd9Sstevel@tonic-gate flag |= NDI_DEVI_DEBUG; 13847c478bd9Sstevel@tonic-gate } 13857c478bd9Sstevel@tonic-gate 13867c478bd9Sstevel@tonic-gate if ((op == BUS_UNCONFIG_ALL) && (flag & NDI_AUTODETACH) == 0) { 13877c478bd9Sstevel@tonic-gate flag |= NDI_DEVI_REMOVE; 13887c478bd9Sstevel@tonic-gate } 13897c478bd9Sstevel@tonic-gate 13907c478bd9Sstevel@tonic-gate /* serialize access */ 13917c478bd9Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 13927c478bd9Sstevel@tonic-gate 13937c478bd9Sstevel@tonic-gate rval = ndi_busop_bus_unconfig(dip, flag, op, arg); 13947c478bd9Sstevel@tonic-gate 13957c478bd9Sstevel@tonic-gate /* logically zap children's list */ 13967c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 13977c478bd9Sstevel@tonic-gate for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { 13987c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= HUBD_CHILD_ZAP; 13997c478bd9Sstevel@tonic-gate } 14007c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 14017c478bd9Sstevel@tonic-gate 14027c478bd9Sstevel@tonic-gate /* fill in what's left */ 14037c478bd9Sstevel@tonic-gate for (cdip = ddi_get_child(dip); cdip; 14047c478bd9Sstevel@tonic-gate cdip = ddi_get_next_sibling(cdip)) { 14057c478bd9Sstevel@tonic-gate usba_device_t *usba_device = usba_get_usba_device(cdip); 14067c478bd9Sstevel@tonic-gate 14077c478bd9Sstevel@tonic-gate if (usba_device == NULL) { 14087c478bd9Sstevel@tonic-gate 14097c478bd9Sstevel@tonic-gate continue; 14107c478bd9Sstevel@tonic-gate } 14117c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 14127c478bd9Sstevel@tonic-gate port = usba_device->usb_port; 14137c478bd9Sstevel@tonic-gate hubd->h_children_dips[port] = cdip; 14147c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~HUBD_CHILD_ZAP; 14157c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 14167c478bd9Sstevel@tonic-gate } 14177c478bd9Sstevel@tonic-gate 14187c478bd9Sstevel@tonic-gate /* physically zap the children we didn't find */ 14197c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 14207c478bd9Sstevel@tonic-gate for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { 1421112cd14aSqz if (hubd->h_port_state[port] & HUBD_CHILD_ZAP) { 14227c478bd9Sstevel@tonic-gate /* zap the dip and usba_device structure as well */ 14237c478bd9Sstevel@tonic-gate hubd_free_usba_device(hubd, hubd->h_usba_devices[port]); 14247c478bd9Sstevel@tonic-gate hubd->h_children_dips[port] = NULL; 14257c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~HUBD_CHILD_ZAP; 14267c478bd9Sstevel@tonic-gate } 14277c478bd9Sstevel@tonic-gate } 14287c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 14297c478bd9Sstevel@tonic-gate 14307c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 14317c478bd9Sstevel@tonic-gate 14327c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 14337c478bd9Sstevel@tonic-gate "hubd_bus_unconfig: rval=%d", rval); 14347c478bd9Sstevel@tonic-gate 14357c478bd9Sstevel@tonic-gate return (rval); 14367c478bd9Sstevel@tonic-gate } 14377c478bd9Sstevel@tonic-gate 14387c478bd9Sstevel@tonic-gate 14397c478bd9Sstevel@tonic-gate /* bus_power entry point */ 14407c478bd9Sstevel@tonic-gate static int 14417c478bd9Sstevel@tonic-gate hubd_bus_power(dev_info_t *dip, void *impl_arg, pm_bus_power_op_t op, 14427c478bd9Sstevel@tonic-gate void *arg, void *result) 14437c478bd9Sstevel@tonic-gate { 14447c478bd9Sstevel@tonic-gate hubd_t *hubd; 14457c478bd9Sstevel@tonic-gate int rval, pwrup_res; 14467c478bd9Sstevel@tonic-gate usb_port_t port; 14477c478bd9Sstevel@tonic-gate int retval = DDI_FAILURE; 14487c478bd9Sstevel@tonic-gate pm_bp_child_pwrchg_t *bpc; 14497c478bd9Sstevel@tonic-gate pm_bp_nexus_pwrup_t bpn; 14507c478bd9Sstevel@tonic-gate 14517c478bd9Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 14527c478bd9Sstevel@tonic-gate 14537c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubd->h_log_handle, 14547c478bd9Sstevel@tonic-gate "hubd_bus_power: dip=%p, impl_arg=%p, power_op=%d, arg=%p, " 1455112116d8Sfb "result=%d\n", (void *)dip, impl_arg, op, arg, *(int *)result); 14567c478bd9Sstevel@tonic-gate 14577c478bd9Sstevel@tonic-gate bpc = (pm_bp_child_pwrchg_t *)arg; 14587c478bd9Sstevel@tonic-gate 14597c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 14607c478bd9Sstevel@tonic-gate hubd->h_bus_pwr++; 14617c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 14627c478bd9Sstevel@tonic-gate 14637c478bd9Sstevel@tonic-gate switch (op) { 14647c478bd9Sstevel@tonic-gate case BUS_POWER_PRE_NOTIFICATION: 14657c478bd9Sstevel@tonic-gate port = hubd_child_dip2port(hubd, bpc->bpc_dip); 14667c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle, 14677c478bd9Sstevel@tonic-gate "hubd_bus_power: BUS_POWER_PRE_NOTIFICATION, port=%d", 14687c478bd9Sstevel@tonic-gate port); 14697c478bd9Sstevel@tonic-gate 14707c478bd9Sstevel@tonic-gate /* go to full power if we are powered down */ 14717c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 14727c478bd9Sstevel@tonic-gate 14737c478bd9Sstevel@tonic-gate /* 14747c478bd9Sstevel@tonic-gate * If this case completes normally, idle will be in 14757c478bd9Sstevel@tonic-gate * hubd_bus_power / BUS_POWER_POST_NOTIFICATION 14767c478bd9Sstevel@tonic-gate */ 14777c478bd9Sstevel@tonic-gate hubd_pm_busy_component(hubd, dip, 0); 14787c478bd9Sstevel@tonic-gate 14797c478bd9Sstevel@tonic-gate /* 14807c478bd9Sstevel@tonic-gate * raise power only if we have created the components 14817c478bd9Sstevel@tonic-gate * and are currently in low power 14827c478bd9Sstevel@tonic-gate */ 14837c478bd9Sstevel@tonic-gate if ((hubd->h_dev_state == USB_DEV_PWRED_DOWN) && 14847c478bd9Sstevel@tonic-gate hubd->h_hubpm->hubp_wakeup_enabled) { 14857c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 14867c478bd9Sstevel@tonic-gate 14877c478bd9Sstevel@tonic-gate bpn.bpn_comp = 0; 14887c478bd9Sstevel@tonic-gate bpn.bpn_dip = dip; 14897c478bd9Sstevel@tonic-gate bpn.bpn_level = USB_DEV_OS_FULL_PWR; 14907c478bd9Sstevel@tonic-gate bpn.bpn_private = bpc->bpc_private; 14917c478bd9Sstevel@tonic-gate 14927c478bd9Sstevel@tonic-gate rval = pm_busop_bus_power(dip, impl_arg, 14937c478bd9Sstevel@tonic-gate BUS_POWER_NEXUS_PWRUP, (void *)&bpn, 14947c478bd9Sstevel@tonic-gate (void *)&pwrup_res); 14957c478bd9Sstevel@tonic-gate 14967c478bd9Sstevel@tonic-gate if (rval != DDI_SUCCESS || pwrup_res != DDI_SUCCESS) { 14977c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 14987c478bd9Sstevel@tonic-gate hubd_pm_idle_component(hubd, dip, 0); 14997c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 15007c478bd9Sstevel@tonic-gate 15017c478bd9Sstevel@tonic-gate break; 15027c478bd9Sstevel@tonic-gate } 15037c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 15047c478bd9Sstevel@tonic-gate } 15057c478bd9Sstevel@tonic-gate 15067c478bd9Sstevel@tonic-gate /* indicate that child is changing power level */ 15077c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= HUBD_CHILD_PWRLVL_CHNG; 15087c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 15097c478bd9Sstevel@tonic-gate 15107c478bd9Sstevel@tonic-gate if ((bpc->bpc_olevel == 0) && 15117c478bd9Sstevel@tonic-gate (bpc->bpc_nlevel > bpc->bpc_olevel)) { 15127c478bd9Sstevel@tonic-gate /* 15137c478bd9Sstevel@tonic-gate * this child is transitioning from power off 15147c478bd9Sstevel@tonic-gate * to power on state - resume port 15157c478bd9Sstevel@tonic-gate */ 15167c478bd9Sstevel@tonic-gate rval = hubd_resume_port(hubd, port); 15177c478bd9Sstevel@tonic-gate if (rval == USB_SUCCESS) { 15187c478bd9Sstevel@tonic-gate retval = DDI_SUCCESS; 15197c478bd9Sstevel@tonic-gate } else { 15207c478bd9Sstevel@tonic-gate /* reset this flag on failure */ 15217c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 15227c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= 15237c478bd9Sstevel@tonic-gate ~HUBD_CHILD_PWRLVL_CHNG; 15247c478bd9Sstevel@tonic-gate hubd_pm_idle_component(hubd, dip, 0); 15257c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 15267c478bd9Sstevel@tonic-gate } 15277c478bd9Sstevel@tonic-gate } else { 15287c478bd9Sstevel@tonic-gate retval = DDI_SUCCESS; 15297c478bd9Sstevel@tonic-gate } 15307c478bd9Sstevel@tonic-gate 15317c478bd9Sstevel@tonic-gate break; 15327c478bd9Sstevel@tonic-gate case BUS_POWER_POST_NOTIFICATION: 15337c478bd9Sstevel@tonic-gate port = hubd_child_dip2port(hubd, bpc->bpc_dip); 15347c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle, 15357c478bd9Sstevel@tonic-gate "hubd_bus_power: BUS_POWER_POST_NOTIFICATION, port=%d", 15367c478bd9Sstevel@tonic-gate port); 15377c478bd9Sstevel@tonic-gate 15387c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 15397c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~HUBD_CHILD_PWRLVL_CHNG; 15407c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 15417c478bd9Sstevel@tonic-gate 15427c478bd9Sstevel@tonic-gate /* record child's pwr and suspend port if required */ 15437c478bd9Sstevel@tonic-gate rval = hubd_post_power(hubd, port, bpc, *(int *)result); 15447c478bd9Sstevel@tonic-gate if (rval == USB_SUCCESS) { 15457c478bd9Sstevel@tonic-gate 15467c478bd9Sstevel@tonic-gate retval = DDI_SUCCESS; 15477c478bd9Sstevel@tonic-gate } 15487c478bd9Sstevel@tonic-gate 15497c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 15507c478bd9Sstevel@tonic-gate 15517c478bd9Sstevel@tonic-gate /* 15527c478bd9Sstevel@tonic-gate * Matching idle for the busy in 15537c478bd9Sstevel@tonic-gate * hubd_bus_power / BUS_POWER_PRE_NOTIFICATION 15547c478bd9Sstevel@tonic-gate */ 15557c478bd9Sstevel@tonic-gate hubd_pm_idle_component(hubd, dip, 0); 15567c478bd9Sstevel@tonic-gate 15577c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 15587c478bd9Sstevel@tonic-gate 15597c478bd9Sstevel@tonic-gate break; 15607c478bd9Sstevel@tonic-gate default: 15617c478bd9Sstevel@tonic-gate retval = pm_busop_bus_power(dip, impl_arg, op, arg, result); 15627c478bd9Sstevel@tonic-gate 15637c478bd9Sstevel@tonic-gate break; 15647c478bd9Sstevel@tonic-gate } 15657c478bd9Sstevel@tonic-gate 15667c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 15677c478bd9Sstevel@tonic-gate hubd->h_bus_pwr--; 15687c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 15697c478bd9Sstevel@tonic-gate 15707c478bd9Sstevel@tonic-gate return (retval); 15717c478bd9Sstevel@tonic-gate } 15727c478bd9Sstevel@tonic-gate 15737c478bd9Sstevel@tonic-gate 15747c478bd9Sstevel@tonic-gate /* 15757c478bd9Sstevel@tonic-gate * functions to handle power transition for OS levels 0 -> 3 15767c478bd9Sstevel@tonic-gate */ 15777c478bd9Sstevel@tonic-gate static int 15787c478bd9Sstevel@tonic-gate hubd_pwrlvl0(hubd_t *hubd) 15797c478bd9Sstevel@tonic-gate { 15807c478bd9Sstevel@tonic-gate hub_power_t *hubpm; 15817c478bd9Sstevel@tonic-gate 15827c478bd9Sstevel@tonic-gate /* We can't power down if hotplug thread is running */ 15837c478bd9Sstevel@tonic-gate if (hubd->h_hotplug_thread || hubd->h_hubpm->hubp_busy_pm || 15847c478bd9Sstevel@tonic-gate (hubd_can_suspend(hubd) == USB_FAILURE)) { 15857c478bd9Sstevel@tonic-gate 15867c478bd9Sstevel@tonic-gate return (USB_FAILURE); 15877c478bd9Sstevel@tonic-gate } 15887c478bd9Sstevel@tonic-gate 15897c478bd9Sstevel@tonic-gate switch (hubd->h_dev_state) { 15907c478bd9Sstevel@tonic-gate case USB_DEV_ONLINE: 15917c478bd9Sstevel@tonic-gate hubpm = hubd->h_hubpm; 15927c478bd9Sstevel@tonic-gate 15937c478bd9Sstevel@tonic-gate /* 15947c478bd9Sstevel@tonic-gate * To avoid race with bus_power pre_notify on check over 15957c478bd9Sstevel@tonic-gate * dev_state, we need to correctly set the dev state 15967c478bd9Sstevel@tonic-gate * before the mutex is dropped in stop polling. 15977c478bd9Sstevel@tonic-gate */ 15987c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_PWRED_DOWN; 15997c478bd9Sstevel@tonic-gate hubpm->hubp_current_power = USB_DEV_OS_PWR_OFF; 16007c478bd9Sstevel@tonic-gate 16017c478bd9Sstevel@tonic-gate /* 16027c478bd9Sstevel@tonic-gate * if we are the root hub, do not stop polling 16037c478bd9Sstevel@tonic-gate * otherwise, we will never see a resume 16047c478bd9Sstevel@tonic-gate */ 16057c478bd9Sstevel@tonic-gate if (usba_is_root_hub(hubd->h_dip)) { 16067c478bd9Sstevel@tonic-gate /* place holder to implement Global Suspend */ 16077c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 16087c478bd9Sstevel@tonic-gate "Global Suspend: Not Yet Implemented"); 16097c478bd9Sstevel@tonic-gate } else { 16107c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd); 16117c478bd9Sstevel@tonic-gate } 16127c478bd9Sstevel@tonic-gate 16137c478bd9Sstevel@tonic-gate /* Issue USB D3 command to the device here */ 16147c478bd9Sstevel@tonic-gate (void) usb_set_device_pwrlvl3(hubd->h_dip); 16157c478bd9Sstevel@tonic-gate 16167c478bd9Sstevel@tonic-gate break; 16177c478bd9Sstevel@tonic-gate case USB_DEV_DISCONNECTED: 16187c478bd9Sstevel@tonic-gate case USB_DEV_SUSPENDED: 16197c478bd9Sstevel@tonic-gate case USB_DEV_PWRED_DOWN: 16207c478bd9Sstevel@tonic-gate default: 16217c478bd9Sstevel@tonic-gate 16227c478bd9Sstevel@tonic-gate break; 16237c478bd9Sstevel@tonic-gate } 16247c478bd9Sstevel@tonic-gate 16257c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 16267c478bd9Sstevel@tonic-gate } 16277c478bd9Sstevel@tonic-gate 16287c478bd9Sstevel@tonic-gate 16297c478bd9Sstevel@tonic-gate /* ARGSUSED */ 16307c478bd9Sstevel@tonic-gate static int 16317c478bd9Sstevel@tonic-gate hubd_pwrlvl1(hubd_t *hubd) 16327c478bd9Sstevel@tonic-gate { 16337c478bd9Sstevel@tonic-gate /* Issue USB D2 command to the device here */ 16347c478bd9Sstevel@tonic-gate (void) usb_set_device_pwrlvl2(hubd->h_dip); 16357c478bd9Sstevel@tonic-gate 16367c478bd9Sstevel@tonic-gate return (USB_FAILURE); 16377c478bd9Sstevel@tonic-gate } 16387c478bd9Sstevel@tonic-gate 16397c478bd9Sstevel@tonic-gate 16407c478bd9Sstevel@tonic-gate /* ARGSUSED */ 16417c478bd9Sstevel@tonic-gate static int 16427c478bd9Sstevel@tonic-gate hubd_pwrlvl2(hubd_t *hubd) 16437c478bd9Sstevel@tonic-gate { 16447c478bd9Sstevel@tonic-gate /* Issue USB D1 command to the device here */ 16457c478bd9Sstevel@tonic-gate (void) usb_set_device_pwrlvl1(hubd->h_dip); 16467c478bd9Sstevel@tonic-gate 16477c478bd9Sstevel@tonic-gate return (USB_FAILURE); 16487c478bd9Sstevel@tonic-gate } 16497c478bd9Sstevel@tonic-gate 16507c478bd9Sstevel@tonic-gate 16517c478bd9Sstevel@tonic-gate static int 16527c478bd9Sstevel@tonic-gate hubd_pwrlvl3(hubd_t *hubd) 16537c478bd9Sstevel@tonic-gate { 16547c478bd9Sstevel@tonic-gate hub_power_t *hubpm; 16557c478bd9Sstevel@tonic-gate int rval; 16567c478bd9Sstevel@tonic-gate 16577c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, "hubd_pwrlvl3"); 16587c478bd9Sstevel@tonic-gate 16597c478bd9Sstevel@tonic-gate hubpm = hubd->h_hubpm; 16607c478bd9Sstevel@tonic-gate switch (hubd->h_dev_state) { 16617c478bd9Sstevel@tonic-gate case USB_DEV_PWRED_DOWN: 16627c478bd9Sstevel@tonic-gate ASSERT(hubpm->hubp_current_power == USB_DEV_OS_PWR_OFF); 16637c478bd9Sstevel@tonic-gate if (usba_is_root_hub(hubd->h_dip)) { 16647c478bd9Sstevel@tonic-gate /* implement global resume here */ 16657c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, 16667c478bd9Sstevel@tonic-gate hubd->h_log_handle, 16677c478bd9Sstevel@tonic-gate "Global Resume: Not Yet Implemented"); 16687c478bd9Sstevel@tonic-gate } 16697c478bd9Sstevel@tonic-gate /* Issue USB D0 command to the device here */ 16707c478bd9Sstevel@tonic-gate rval = usb_set_device_pwrlvl0(hubd->h_dip); 16717c478bd9Sstevel@tonic-gate ASSERT(rval == USB_SUCCESS); 16727c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_ONLINE; 16737c478bd9Sstevel@tonic-gate hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR; 16747c478bd9Sstevel@tonic-gate hubpm->hubp_time_at_full_power = ddi_get_time(); 16757c478bd9Sstevel@tonic-gate hubd_start_polling(hubd, 0); 16767c478bd9Sstevel@tonic-gate 16777c478bd9Sstevel@tonic-gate /* FALLTHRU */ 16787c478bd9Sstevel@tonic-gate case USB_DEV_ONLINE: 16797c478bd9Sstevel@tonic-gate /* we are already in full power */ 16807c478bd9Sstevel@tonic-gate 16817c478bd9Sstevel@tonic-gate /* FALLTHRU */ 16827c478bd9Sstevel@tonic-gate case USB_DEV_DISCONNECTED: 16837c478bd9Sstevel@tonic-gate case USB_DEV_SUSPENDED: 16847c478bd9Sstevel@tonic-gate /* 16857c478bd9Sstevel@tonic-gate * PM framework tries to put you in full power 16867c478bd9Sstevel@tonic-gate * during system shutdown. If we are disconnected 16877c478bd9Sstevel@tonic-gate * return success. Also, we should not change state 16887c478bd9Sstevel@tonic-gate * when we are disconnected or suspended or about to 16897c478bd9Sstevel@tonic-gate * transition to that state 16907c478bd9Sstevel@tonic-gate */ 16917c478bd9Sstevel@tonic-gate 16927c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 16937c478bd9Sstevel@tonic-gate default: 16947c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 16957c478bd9Sstevel@tonic-gate "hubd_pwrlvl3: Illegal dev_state=%d", hubd->h_dev_state); 16967c478bd9Sstevel@tonic-gate 16977c478bd9Sstevel@tonic-gate return (USB_FAILURE); 16987c478bd9Sstevel@tonic-gate } 16997c478bd9Sstevel@tonic-gate } 17007c478bd9Sstevel@tonic-gate 17017c478bd9Sstevel@tonic-gate 17027c478bd9Sstevel@tonic-gate /* power entry point */ 17037c478bd9Sstevel@tonic-gate /* ARGSUSED */ 17047c478bd9Sstevel@tonic-gate int 17057c478bd9Sstevel@tonic-gate usba_hubdi_power(dev_info_t *dip, int comp, int level) 17067c478bd9Sstevel@tonic-gate { 17077c478bd9Sstevel@tonic-gate hubd_t *hubd; 17087c478bd9Sstevel@tonic-gate hub_power_t *hubpm; 17097c478bd9Sstevel@tonic-gate int retval; 17107c478bd9Sstevel@tonic-gate int circ; 17117c478bd9Sstevel@tonic-gate 17127c478bd9Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 17137c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle, 17147c478bd9Sstevel@tonic-gate "usba_hubdi_power: level=%d", level); 17157c478bd9Sstevel@tonic-gate 17167c478bd9Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 17177c478bd9Sstevel@tonic-gate 17187c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 17197c478bd9Sstevel@tonic-gate hubpm = hubd->h_hubpm; 17207c478bd9Sstevel@tonic-gate 17217c478bd9Sstevel@tonic-gate /* check if we are transitioning to a legal power level */ 17227c478bd9Sstevel@tonic-gate if (USB_DEV_PWRSTATE_OK(hubpm->hubp_pwr_states, level)) { 17237c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HUBDI, hubd->h_log_handle, 17247c478bd9Sstevel@tonic-gate "usba_hubdi_power: illegal power level=%d " 17257c478bd9Sstevel@tonic-gate "hubp_pwr_states=0x%x", level, hubpm->hubp_pwr_states); 17267c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 17277c478bd9Sstevel@tonic-gate 17287c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 17297c478bd9Sstevel@tonic-gate 17307c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 17317c478bd9Sstevel@tonic-gate } 17327c478bd9Sstevel@tonic-gate 17337c478bd9Sstevel@tonic-gate switch (level) { 17347c478bd9Sstevel@tonic-gate case USB_DEV_OS_PWR_OFF: 17357c478bd9Sstevel@tonic-gate retval = hubd_pwrlvl0(hubd); 17367c478bd9Sstevel@tonic-gate 17377c478bd9Sstevel@tonic-gate break; 17387c478bd9Sstevel@tonic-gate case USB_DEV_OS_PWR_1: 17397c478bd9Sstevel@tonic-gate retval = hubd_pwrlvl1(hubd); 17407c478bd9Sstevel@tonic-gate 17417c478bd9Sstevel@tonic-gate break; 17427c478bd9Sstevel@tonic-gate case USB_DEV_OS_PWR_2: 17437c478bd9Sstevel@tonic-gate retval = hubd_pwrlvl2(hubd); 17447c478bd9Sstevel@tonic-gate 17457c478bd9Sstevel@tonic-gate break; 17467c478bd9Sstevel@tonic-gate case USB_DEV_OS_FULL_PWR: 17477c478bd9Sstevel@tonic-gate retval = hubd_pwrlvl3(hubd); 17487c478bd9Sstevel@tonic-gate 17497c478bd9Sstevel@tonic-gate break; 17507c478bd9Sstevel@tonic-gate } 17517c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 17527c478bd9Sstevel@tonic-gate 17537c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 17547c478bd9Sstevel@tonic-gate 17557c478bd9Sstevel@tonic-gate return ((retval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE); 17567c478bd9Sstevel@tonic-gate } 17577c478bd9Sstevel@tonic-gate 17587c478bd9Sstevel@tonic-gate 17597c478bd9Sstevel@tonic-gate /* power entry point for the root hub */ 17607c478bd9Sstevel@tonic-gate int 17617c478bd9Sstevel@tonic-gate usba_hubdi_root_hub_power(dev_info_t *dip, int comp, int level) 17627c478bd9Sstevel@tonic-gate { 17637c478bd9Sstevel@tonic-gate return (usba_hubdi_power(dip, comp, level)); 17647c478bd9Sstevel@tonic-gate } 17657c478bd9Sstevel@tonic-gate 17667c478bd9Sstevel@tonic-gate 17677c478bd9Sstevel@tonic-gate /* 17687c478bd9Sstevel@tonic-gate * standard driver entry points support code 17697c478bd9Sstevel@tonic-gate */ 17707c478bd9Sstevel@tonic-gate int 17717c478bd9Sstevel@tonic-gate usba_hubdi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 17727c478bd9Sstevel@tonic-gate { 17737c478bd9Sstevel@tonic-gate int instance = ddi_get_instance(dip); 17747c478bd9Sstevel@tonic-gate hubd_t *hubd = NULL; 17757c478bd9Sstevel@tonic-gate int i, rval; 17767c478bd9Sstevel@tonic-gate int minor; 1777112cd14aSqz uint8_t ports_count; 17787c478bd9Sstevel@tonic-gate char *log_name = NULL; 17797c478bd9Sstevel@tonic-gate const char *root_hub_drvname; 17807c478bd9Sstevel@tonic-gate usb_ep_data_t *ep_data; 17817c478bd9Sstevel@tonic-gate usba_device_t *child_ud = NULL; 17827c478bd9Sstevel@tonic-gate usb_dev_descr_t *usb_dev_descr; 17837c478bd9Sstevel@tonic-gate usb_port_status_t parent_port_status, child_port_status; 17847c478bd9Sstevel@tonic-gate 17857c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubdi_log_handle, 1786c0f24e5bSlg "hubd_attach instance %d, cmd=0x%x", instance, cmd); 17877c478bd9Sstevel@tonic-gate 17887c478bd9Sstevel@tonic-gate switch (cmd) { 17897c478bd9Sstevel@tonic-gate case DDI_ATTACH: 17907c478bd9Sstevel@tonic-gate 17917c478bd9Sstevel@tonic-gate break; 17927c478bd9Sstevel@tonic-gate case DDI_RESUME: 17937c478bd9Sstevel@tonic-gate hubd_cpr_resume(dip); 17947c478bd9Sstevel@tonic-gate 17957c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 17967c478bd9Sstevel@tonic-gate default: 17977c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 17987c478bd9Sstevel@tonic-gate } 17997c478bd9Sstevel@tonic-gate 18007c478bd9Sstevel@tonic-gate /* 18017c478bd9Sstevel@tonic-gate * Allocate softc information. 18027c478bd9Sstevel@tonic-gate */ 18037c478bd9Sstevel@tonic-gate if (usba_is_root_hub(dip)) { 18047c478bd9Sstevel@tonic-gate /* soft state has already been allocated */ 18057c478bd9Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 18067c478bd9Sstevel@tonic-gate minor = HUBD_IS_ROOT_HUB; 18077c478bd9Sstevel@tonic-gate 18087c478bd9Sstevel@tonic-gate /* generate readable labels for different root hubs */ 18097c478bd9Sstevel@tonic-gate root_hub_drvname = ddi_driver_name(dip); 18107c478bd9Sstevel@tonic-gate if (strcmp(root_hub_drvname, "ehci") == 0) { 18117c478bd9Sstevel@tonic-gate log_name = "eusb"; 18127c478bd9Sstevel@tonic-gate } else if (strcmp(root_hub_drvname, "uhci") == 0) { 18137c478bd9Sstevel@tonic-gate log_name = "uusb"; 18147c478bd9Sstevel@tonic-gate } else { 18157c478bd9Sstevel@tonic-gate /* std. for ohci */ 18167c478bd9Sstevel@tonic-gate log_name = "usb"; 18177c478bd9Sstevel@tonic-gate } 18187c478bd9Sstevel@tonic-gate } else { 18197c478bd9Sstevel@tonic-gate rval = ddi_soft_state_zalloc(hubd_statep, instance); 18207c478bd9Sstevel@tonic-gate minor = 0; 18217c478bd9Sstevel@tonic-gate 18227c478bd9Sstevel@tonic-gate if (rval != DDI_SUCCESS) { 18237c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle, 18247c478bd9Sstevel@tonic-gate "cannot allocate soft state (%d)", instance); 18257c478bd9Sstevel@tonic-gate goto fail; 18267c478bd9Sstevel@tonic-gate } 18277c478bd9Sstevel@tonic-gate 18287c478bd9Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 18297c478bd9Sstevel@tonic-gate if (hubd == NULL) { 18307c478bd9Sstevel@tonic-gate goto fail; 18317c478bd9Sstevel@tonic-gate } 18327c478bd9Sstevel@tonic-gate } 18337c478bd9Sstevel@tonic-gate 18347c478bd9Sstevel@tonic-gate hubd->h_log_handle = usb_alloc_log_hdl(dip, log_name, &hubd_errlevel, 1835c0f24e5bSlg &hubd_errmask, &hubd_instance_debug, 0); 18367c478bd9Sstevel@tonic-gate 18377c478bd9Sstevel@tonic-gate hubd->h_usba_device = child_ud = usba_get_usba_device(dip); 18387c478bd9Sstevel@tonic-gate hubd->h_dip = dip; 18397c478bd9Sstevel@tonic-gate hubd->h_instance = instance; 18407c478bd9Sstevel@tonic-gate 18417c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 18427c478bd9Sstevel@tonic-gate child_port_status = child_ud->usb_port_status; 18437c478bd9Sstevel@tonic-gate usb_dev_descr = child_ud->usb_dev_descr; 18447c478bd9Sstevel@tonic-gate parent_port_status = (child_ud->usb_hs_hub_usba_dev) ? 18457c478bd9Sstevel@tonic-gate child_ud->usb_hs_hub_usba_dev->usb_port_status : 0; 18467c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 18477c478bd9Sstevel@tonic-gate 18487c478bd9Sstevel@tonic-gate if ((child_port_status == USBA_FULL_SPEED_DEV) && 18497c478bd9Sstevel@tonic-gate (parent_port_status == USBA_HIGH_SPEED_DEV) && 18507c478bd9Sstevel@tonic-gate (usb_dev_descr->bcdUSB == 0x100)) { 18517c478bd9Sstevel@tonic-gate USB_DPRINTF_L0(DPRINT_MASK_ATTA, hubd->h_log_handle, 18527c478bd9Sstevel@tonic-gate "Use of a USB1.0 hub behind a high speed port may " 18537c478bd9Sstevel@tonic-gate "cause unexpected failures"); 18547c478bd9Sstevel@tonic-gate } 18557c478bd9Sstevel@tonic-gate 18567c478bd9Sstevel@tonic-gate hubd->h_pipe_policy.pp_max_async_reqs = 1; 18577c478bd9Sstevel@tonic-gate 18587c478bd9Sstevel@tonic-gate /* register with USBA as client driver */ 18597c478bd9Sstevel@tonic-gate if (usb_client_attach(dip, USBDRV_VERSION, 0) != USB_SUCCESS) { 18607c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 18617c478bd9Sstevel@tonic-gate "client attach failed"); 18627c478bd9Sstevel@tonic-gate 18637c478bd9Sstevel@tonic-gate goto fail; 18647c478bd9Sstevel@tonic-gate } 18657c478bd9Sstevel@tonic-gate 18667c478bd9Sstevel@tonic-gate if (usb_get_dev_data(dip, &hubd->h_dev_data, 18677c478bd9Sstevel@tonic-gate USB_PARSE_LVL_IF, 0) != USB_SUCCESS) { 18687c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 18697c478bd9Sstevel@tonic-gate "cannot get dev_data"); 18707c478bd9Sstevel@tonic-gate 18717c478bd9Sstevel@tonic-gate goto fail; 18727c478bd9Sstevel@tonic-gate } 18737c478bd9Sstevel@tonic-gate 18747c478bd9Sstevel@tonic-gate if ((ep_data = usb_lookup_ep_data(dip, hubd->h_dev_data, 18757c478bd9Sstevel@tonic-gate hubd->h_dev_data->dev_curr_if, 0, 0, 18767c478bd9Sstevel@tonic-gate (uint_t)USB_EP_ATTR_INTR, (uint_t)USB_EP_DIR_IN)) == NULL) { 18777c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 18787c478bd9Sstevel@tonic-gate "no interrupt IN endpoint found"); 18797c478bd9Sstevel@tonic-gate 18807c478bd9Sstevel@tonic-gate goto fail; 18817c478bd9Sstevel@tonic-gate } 18827c478bd9Sstevel@tonic-gate 18837c478bd9Sstevel@tonic-gate hubd->h_ep1_descr = ep_data->ep_descr; 18847c478bd9Sstevel@tonic-gate hubd->h_default_pipe = hubd->h_dev_data->dev_default_ph; 18857c478bd9Sstevel@tonic-gate 18867c478bd9Sstevel@tonic-gate mutex_init(HUBD_MUTEX(hubd), NULL, MUTEX_DRIVER, 1887c0f24e5bSlg hubd->h_dev_data->dev_iblock_cookie); 18887c478bd9Sstevel@tonic-gate cv_init(&hubd->h_cv_reset_port, NULL, CV_DRIVER, NULL); 1889ffcd51f3Slg cv_init(&hubd->h_cv_hotplug_dev, NULL, CV_DRIVER, NULL); 18907c478bd9Sstevel@tonic-gate 18917c478bd9Sstevel@tonic-gate hubd->h_init_state |= HUBD_LOCKS_DONE; 18927c478bd9Sstevel@tonic-gate 18937c478bd9Sstevel@tonic-gate usb_free_descr_tree(dip, hubd->h_dev_data); 18947c478bd9Sstevel@tonic-gate 18957c478bd9Sstevel@tonic-gate /* 18967c478bd9Sstevel@tonic-gate * register this hub instance with usba 18977c478bd9Sstevel@tonic-gate */ 18987c478bd9Sstevel@tonic-gate rval = usba_hubdi_register(dip, 0); 18997c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 1900d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 19017c478bd9Sstevel@tonic-gate "usba_hubdi_register failed"); 19027c478bd9Sstevel@tonic-gate goto fail; 19037c478bd9Sstevel@tonic-gate } 19047c478bd9Sstevel@tonic-gate 19057c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 19067c478bd9Sstevel@tonic-gate hubd->h_init_state |= HUBD_HUBDI_REGISTERED; 19077c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_ONLINE; 19087c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 19097c478bd9Sstevel@tonic-gate 19107c478bd9Sstevel@tonic-gate /* now create components to power manage this device */ 19117c478bd9Sstevel@tonic-gate hubd_create_pm_components(dip, hubd); 19127c478bd9Sstevel@tonic-gate 19137c478bd9Sstevel@tonic-gate /* 19147c478bd9Sstevel@tonic-gate * Event handling: definition and registration 19157c478bd9Sstevel@tonic-gate * 19167c478bd9Sstevel@tonic-gate * first the definition: 19177c478bd9Sstevel@tonic-gate * get event handle 19187c478bd9Sstevel@tonic-gate */ 19197c478bd9Sstevel@tonic-gate (void) ndi_event_alloc_hdl(dip, 0, &hubd->h_ndi_event_hdl, NDI_SLEEP); 19207c478bd9Sstevel@tonic-gate 19217c478bd9Sstevel@tonic-gate /* bind event set to the handle */ 19227c478bd9Sstevel@tonic-gate if (ndi_event_bind_set(hubd->h_ndi_event_hdl, &hubd_ndi_events, 19237c478bd9Sstevel@tonic-gate NDI_SLEEP)) { 19247c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle, 19257c478bd9Sstevel@tonic-gate "binding event set failed"); 19267c478bd9Sstevel@tonic-gate 19277c478bd9Sstevel@tonic-gate goto fail; 19287c478bd9Sstevel@tonic-gate } 19297c478bd9Sstevel@tonic-gate 19307c478bd9Sstevel@tonic-gate /* event registration */ 19317c478bd9Sstevel@tonic-gate if (hubd_register_events(hubd) != USB_SUCCESS) { 1932d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 19337c478bd9Sstevel@tonic-gate "hubd_register_events failed"); 19347c478bd9Sstevel@tonic-gate 19357c478bd9Sstevel@tonic-gate goto fail; 19367c478bd9Sstevel@tonic-gate } 19377c478bd9Sstevel@tonic-gate 19387c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 19397c478bd9Sstevel@tonic-gate hubd->h_init_state |= HUBD_EVENTS_REGISTERED; 19407c478bd9Sstevel@tonic-gate 194135f36846Ssl if ((hubd_get_hub_descriptor(hubd)) != USB_SUCCESS) { 194235f36846Ssl mutex_exit(HUBD_MUTEX(hubd)); 194335f36846Ssl 194435f36846Ssl goto fail; 194535f36846Ssl } 194635f36846Ssl 194735f36846Ssl if (ddi_prop_exists(DDI_DEV_T_ANY, dip, 194835f36846Ssl (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM), 194935f36846Ssl "hub-ignore-power-budget") == 1) { 195035f36846Ssl hubd->h_ignore_pwr_budget = B_TRUE; 195135f36846Ssl } else { 195235f36846Ssl hubd->h_ignore_pwr_budget = B_FALSE; 195335f36846Ssl 195435f36846Ssl /* initialize hub power budget variables */ 195535f36846Ssl if (hubd_init_power_budget(hubd) != USB_SUCCESS) { 195635f36846Ssl USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 195735f36846Ssl "hubd_init_power_budget failed"); 195835f36846Ssl mutex_exit(HUBD_MUTEX(hubd)); 195935f36846Ssl 196035f36846Ssl goto fail; 196135f36846Ssl } 196235f36846Ssl } 196335f36846Ssl 19647c478bd9Sstevel@tonic-gate /* initialize and create children */ 19657c478bd9Sstevel@tonic-gate if (hubd_check_ports(hubd) != USB_SUCCESS) { 1966d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 19677c478bd9Sstevel@tonic-gate "hubd_check_ports failed"); 19687c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 19697c478bd9Sstevel@tonic-gate 19707c478bd9Sstevel@tonic-gate goto fail; 19717c478bd9Sstevel@tonic-gate } 19727c478bd9Sstevel@tonic-gate 19737c478bd9Sstevel@tonic-gate /* 19747c478bd9Sstevel@tonic-gate * create cfgadm nodes 19757c478bd9Sstevel@tonic-gate */ 19767c478bd9Sstevel@tonic-gate hubd->h_ancestry_str = (char *)kmem_zalloc(HUBD_APID_NAMELEN, KM_SLEEP); 19777c478bd9Sstevel@tonic-gate hubd_get_ancestry_str(hubd); 19787c478bd9Sstevel@tonic-gate 19797c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 19807c478bd9Sstevel@tonic-gate "#ports=0x%x", hubd->h_hub_descr.bNbrPorts); 19817c478bd9Sstevel@tonic-gate 19827c478bd9Sstevel@tonic-gate for (i = 1; i <= hubd->h_hub_descr.bNbrPorts; i++) { 19837c478bd9Sstevel@tonic-gate char ap_name[HUBD_APID_NAMELEN]; 19847c478bd9Sstevel@tonic-gate 19857c478bd9Sstevel@tonic-gate (void) snprintf(ap_name, HUBD_APID_NAMELEN, "%s%d", 19867c478bd9Sstevel@tonic-gate hubd->h_ancestry_str, i); 19877c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 19887c478bd9Sstevel@tonic-gate "ap_name=%s", ap_name); 19897c478bd9Sstevel@tonic-gate 19907c478bd9Sstevel@tonic-gate if (ddi_create_minor_node(dip, ap_name, S_IFCHR, instance, 19917c478bd9Sstevel@tonic-gate DDI_NT_USB_ATTACHMENT_POINT, 0) != DDI_SUCCESS) { 1992d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 19937c478bd9Sstevel@tonic-gate "cannot create attachment point node (%d)", 19947c478bd9Sstevel@tonic-gate instance); 19957c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 19967c478bd9Sstevel@tonic-gate 19977c478bd9Sstevel@tonic-gate goto fail; 19987c478bd9Sstevel@tonic-gate } 19997c478bd9Sstevel@tonic-gate } 2000112cd14aSqz 2001112cd14aSqz ports_count = hubd->h_hub_descr.bNbrPorts; 20027c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 20037c478bd9Sstevel@tonic-gate 20047c478bd9Sstevel@tonic-gate /* create minor nodes */ 20057c478bd9Sstevel@tonic-gate if (ddi_create_minor_node(dip, "hubd", S_IFCHR, 20067c478bd9Sstevel@tonic-gate instance | minor, DDI_NT_NEXUS, 0) != DDI_SUCCESS) { 20077c478bd9Sstevel@tonic-gate 2008d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 20097c478bd9Sstevel@tonic-gate "cannot create devctl minor node (%d)", instance); 20107c478bd9Sstevel@tonic-gate 20117c478bd9Sstevel@tonic-gate goto fail; 20127c478bd9Sstevel@tonic-gate } 20137c478bd9Sstevel@tonic-gate 20147c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 20157c478bd9Sstevel@tonic-gate hubd->h_init_state |= HUBD_MINOR_NODE_CREATED; 20167c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 20177c478bd9Sstevel@tonic-gate 2018112cd14aSqz if (ndi_prop_update_int(DDI_DEV_T_NONE, dip, 2019112cd14aSqz "usb-port-count", ports_count) != DDI_PROP_SUCCESS) { 2020112cd14aSqz USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 2021ff0e937bSRaymond Chen "usb-port-count update failed"); 2022112cd14aSqz } 2023112cd14aSqz 20247c478bd9Sstevel@tonic-gate /* 20257c478bd9Sstevel@tonic-gate * host controller driver has already reported this dev 20267c478bd9Sstevel@tonic-gate * if we are the root hub 20277c478bd9Sstevel@tonic-gate */ 20287c478bd9Sstevel@tonic-gate if (!usba_is_root_hub(dip)) { 20297c478bd9Sstevel@tonic-gate ddi_report_dev(dip); 20307c478bd9Sstevel@tonic-gate } 20317c478bd9Sstevel@tonic-gate 20327c478bd9Sstevel@tonic-gate /* enable deathrow thread */ 20337c478bd9Sstevel@tonic-gate hubd->h_cleanup_enabled = B_TRUE; 20347c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 20357c478bd9Sstevel@tonic-gate hubd_pm_idle_component(hubd, dip, 0); 20367c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 20377c478bd9Sstevel@tonic-gate 20387c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 20397c478bd9Sstevel@tonic-gate 20407c478bd9Sstevel@tonic-gate fail: 20417c478bd9Sstevel@tonic-gate { 20427c478bd9Sstevel@tonic-gate char *pathname = kmem_alloc(MAXPATHLEN, KM_SLEEP); 20437c478bd9Sstevel@tonic-gate 20448668df41Slg USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle, 20457c478bd9Sstevel@tonic-gate "cannot attach %s", ddi_pathname(dip, pathname)); 20467c478bd9Sstevel@tonic-gate 20477c478bd9Sstevel@tonic-gate kmem_free(pathname, MAXPATHLEN); 20487c478bd9Sstevel@tonic-gate } 20497c478bd9Sstevel@tonic-gate 20507c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 20517c478bd9Sstevel@tonic-gate hubd_pm_idle_component(hubd, dip, 0); 20527c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 20537c478bd9Sstevel@tonic-gate 20547c478bd9Sstevel@tonic-gate if (hubd) { 20557c478bd9Sstevel@tonic-gate rval = hubd_cleanup(dip, hubd); 20567c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 2057d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle, 20587c478bd9Sstevel@tonic-gate "failure to complete cleanup after attach failure"); 20597c478bd9Sstevel@tonic-gate } 20607c478bd9Sstevel@tonic-gate } 20617c478bd9Sstevel@tonic-gate 20627c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 20637c478bd9Sstevel@tonic-gate } 20647c478bd9Sstevel@tonic-gate 20657c478bd9Sstevel@tonic-gate 20667c478bd9Sstevel@tonic-gate int 20677c478bd9Sstevel@tonic-gate usba_hubdi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 20687c478bd9Sstevel@tonic-gate { 20697c478bd9Sstevel@tonic-gate hubd_t *hubd = hubd_get_soft_state(dip); 20707c478bd9Sstevel@tonic-gate int rval; 20717c478bd9Sstevel@tonic-gate 20727c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 20737c478bd9Sstevel@tonic-gate "hubd_detach: cmd=0x%x", cmd); 20747c478bd9Sstevel@tonic-gate 20757c478bd9Sstevel@tonic-gate switch (cmd) { 20767c478bd9Sstevel@tonic-gate case DDI_DETACH: 20777c478bd9Sstevel@tonic-gate rval = hubd_cleanup(dip, hubd); 20787c478bd9Sstevel@tonic-gate 20797c478bd9Sstevel@tonic-gate return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE); 20807c478bd9Sstevel@tonic-gate case DDI_SUSPEND: 20817c478bd9Sstevel@tonic-gate rval = hubd_cpr_suspend(hubd); 20827c478bd9Sstevel@tonic-gate 20837c478bd9Sstevel@tonic-gate return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE); 20847c478bd9Sstevel@tonic-gate default: 20857c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 20867c478bd9Sstevel@tonic-gate } 20877c478bd9Sstevel@tonic-gate } 20887c478bd9Sstevel@tonic-gate 20897c478bd9Sstevel@tonic-gate 20907c478bd9Sstevel@tonic-gate /* 20917c478bd9Sstevel@tonic-gate * hubd_setdevaddr 20927c478bd9Sstevel@tonic-gate * set the device addrs on this port 20937c478bd9Sstevel@tonic-gate */ 20947c478bd9Sstevel@tonic-gate static int 20957c478bd9Sstevel@tonic-gate hubd_setdevaddr(hubd_t *hubd, usb_port_t port) 20967c478bd9Sstevel@tonic-gate { 20977c478bd9Sstevel@tonic-gate int rval; 20987c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 20997c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 21007c478bd9Sstevel@tonic-gate usb_pipe_handle_t ph; 21017c478bd9Sstevel@tonic-gate dev_info_t *child_dip = NULL; 21027c478bd9Sstevel@tonic-gate uchar_t address = 0; 21037c478bd9Sstevel@tonic-gate usba_device_t *usba_device; 21047c478bd9Sstevel@tonic-gate int retry = 0; 21057c478bd9Sstevel@tonic-gate long time_delay; 21067c478bd9Sstevel@tonic-gate 21077c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 21087c478bd9Sstevel@tonic-gate "hubd_setdevaddr: port=%d", port); 21097c478bd9Sstevel@tonic-gate 21107c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 21117c478bd9Sstevel@tonic-gate 21127c478bd9Sstevel@tonic-gate child_dip = hubd->h_children_dips[port]; 21137c478bd9Sstevel@tonic-gate address = hubd->h_usba_devices[port]->usb_addr; 21147c478bd9Sstevel@tonic-gate usba_device = hubd->h_usba_devices[port]; 21157c478bd9Sstevel@tonic-gate 21167c478bd9Sstevel@tonic-gate /* close the default pipe with addr x */ 21177c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 21187c478bd9Sstevel@tonic-gate ph = usba_get_dflt_pipe_handle(child_dip); 21197c478bd9Sstevel@tonic-gate usb_pipe_close(child_dip, ph, 21207c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL); 21217c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 21227c478bd9Sstevel@tonic-gate 21237c478bd9Sstevel@tonic-gate /* 21247c478bd9Sstevel@tonic-gate * As this device has been reset, temporarily 21257c478bd9Sstevel@tonic-gate * assign the default address 21267c478bd9Sstevel@tonic-gate */ 21277c478bd9Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex); 21287c478bd9Sstevel@tonic-gate address = usba_device->usb_addr; 21297c478bd9Sstevel@tonic-gate usba_device->usb_addr = USBA_DEFAULT_ADDR; 21307c478bd9Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex); 21317c478bd9Sstevel@tonic-gate 21327c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 21337c478bd9Sstevel@tonic-gate 21347c478bd9Sstevel@tonic-gate time_delay = drv_usectohz(hubd_device_delay / 20); 21357c478bd9Sstevel@tonic-gate for (retry = 0; retry < hubd_retry_enumerate; retry++) { 21367c478bd9Sstevel@tonic-gate 21377c478bd9Sstevel@tonic-gate /* open child's default pipe with USBA_DEFAULT_ADDR */ 21387c478bd9Sstevel@tonic-gate if (usb_pipe_open(child_dip, NULL, NULL, 21397c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph) != 21407c478bd9Sstevel@tonic-gate USB_SUCCESS) { 21417c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 21427c478bd9Sstevel@tonic-gate "hubd_setdevaddr: Unable to open default pipe"); 21437c478bd9Sstevel@tonic-gate 21447c478bd9Sstevel@tonic-gate break; 21457c478bd9Sstevel@tonic-gate } 21467c478bd9Sstevel@tonic-gate 21477c478bd9Sstevel@tonic-gate /* Set the address of the device */ 21487c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 21497c478bd9Sstevel@tonic-gate USB_DEV_REQ_HOST_TO_DEV, 21507c478bd9Sstevel@tonic-gate USB_REQ_SET_ADDRESS, /* bRequest */ 21517c478bd9Sstevel@tonic-gate address, /* wValue */ 21527c478bd9Sstevel@tonic-gate 0, /* wIndex */ 21537c478bd9Sstevel@tonic-gate 0, /* wLength */ 21547c478bd9Sstevel@tonic-gate NULL, 0, 21557c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 21567c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 21577c478bd9Sstevel@tonic-gate "hubd_setdevaddr(%d): rval=%d cr=%d cb_fl=0x%x", 21587c478bd9Sstevel@tonic-gate retry, rval, completion_reason, cb_flags); 21597c478bd9Sstevel@tonic-gate } 21607c478bd9Sstevel@tonic-gate 21617c478bd9Sstevel@tonic-gate usb_pipe_close(child_dip, ph, 21627c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL); 21637c478bd9Sstevel@tonic-gate 21647c478bd9Sstevel@tonic-gate if (rval == USB_SUCCESS) { 21657c478bd9Sstevel@tonic-gate 21667c478bd9Sstevel@tonic-gate break; 21677c478bd9Sstevel@tonic-gate } 21687c478bd9Sstevel@tonic-gate 21697c478bd9Sstevel@tonic-gate delay(time_delay); 21707c478bd9Sstevel@tonic-gate } 21717c478bd9Sstevel@tonic-gate 21727c478bd9Sstevel@tonic-gate /* Reset to the old address */ 21737c478bd9Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex); 21747c478bd9Sstevel@tonic-gate usba_device->usb_addr = address; 21757c478bd9Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex); 21767c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 21777c478bd9Sstevel@tonic-gate 21787c478bd9Sstevel@tonic-gate usba_clear_data_toggle(usba_device); 21797c478bd9Sstevel@tonic-gate 21807c478bd9Sstevel@tonic-gate return (rval); 21817c478bd9Sstevel@tonic-gate } 21827c478bd9Sstevel@tonic-gate 21837c478bd9Sstevel@tonic-gate 21847c478bd9Sstevel@tonic-gate /* 21857c478bd9Sstevel@tonic-gate * hubd_setdevconfig 21867c478bd9Sstevel@tonic-gate * set the device addrs on this port 21877c478bd9Sstevel@tonic-gate */ 21887c478bd9Sstevel@tonic-gate static void 21897c478bd9Sstevel@tonic-gate hubd_setdevconfig(hubd_t *hubd, usb_port_t port) 21907c478bd9Sstevel@tonic-gate { 21917c478bd9Sstevel@tonic-gate int rval; 21927c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 21937c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 21947c478bd9Sstevel@tonic-gate usb_pipe_handle_t ph; 21957c478bd9Sstevel@tonic-gate dev_info_t *child_dip = NULL; 21967c478bd9Sstevel@tonic-gate usba_device_t *usba_device = NULL; 21977c478bd9Sstevel@tonic-gate uint16_t config_value; 21987c478bd9Sstevel@tonic-gate 21997c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 22007c478bd9Sstevel@tonic-gate "hubd_setdevconfig: port=%d", port); 22017c478bd9Sstevel@tonic-gate 22027c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 22037c478bd9Sstevel@tonic-gate 22047c478bd9Sstevel@tonic-gate child_dip = hubd->h_children_dips[port]; 22057c478bd9Sstevel@tonic-gate usba_device = hubd->h_usba_devices[port]; 22067c478bd9Sstevel@tonic-gate config_value = hubd->h_usba_devices[port]->usb_cfg_value; 22077c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 22087c478bd9Sstevel@tonic-gate 22097c478bd9Sstevel@tonic-gate /* open the default control pipe */ 22107c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_open(child_dip, NULL, NULL, 22117c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) == 22127c478bd9Sstevel@tonic-gate USB_SUCCESS) { 22137c478bd9Sstevel@tonic-gate 22147c478bd9Sstevel@tonic-gate /* Set the default configuration of the device */ 22157c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 22167c478bd9Sstevel@tonic-gate USB_DEV_REQ_HOST_TO_DEV, 22177c478bd9Sstevel@tonic-gate USB_REQ_SET_CFG, /* bRequest */ 22187c478bd9Sstevel@tonic-gate config_value, /* wValue */ 22197c478bd9Sstevel@tonic-gate 0, /* wIndex */ 22207c478bd9Sstevel@tonic-gate 0, /* wLength */ 22217c478bd9Sstevel@tonic-gate NULL, 0, 22227c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 22237c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 22247c478bd9Sstevel@tonic-gate "hubd_setdevconfig: set device config failed: " 22257c478bd9Sstevel@tonic-gate "cr=%d cb_fl=0x%x rval=%d", 22267c478bd9Sstevel@tonic-gate completion_reason, cb_flags, rval); 22277c478bd9Sstevel@tonic-gate } 22287c478bd9Sstevel@tonic-gate /* 22297c478bd9Sstevel@tonic-gate * After setting the configuration, we make this default 22307c478bd9Sstevel@tonic-gate * control pipe persistent, so that it gets re-opened 22317c478bd9Sstevel@tonic-gate * on posting a connect event 22327c478bd9Sstevel@tonic-gate */ 22337c478bd9Sstevel@tonic-gate usba_persistent_pipe_close(usba_device); 22347c478bd9Sstevel@tonic-gate } else { 22357c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 22367c478bd9Sstevel@tonic-gate "pipe open fails: rval=%d", rval); 22377c478bd9Sstevel@tonic-gate } 22387c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 22397c478bd9Sstevel@tonic-gate } 22407c478bd9Sstevel@tonic-gate 22417c478bd9Sstevel@tonic-gate 22427c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 22437c478bd9Sstevel@tonic-gate static int 22447c478bd9Sstevel@tonic-gate hubd_check_disconnected_ports(dev_info_t *dip, void *arg) 22457c478bd9Sstevel@tonic-gate { 22467c478bd9Sstevel@tonic-gate int circ; 22477c478bd9Sstevel@tonic-gate usb_port_t port; 22487c478bd9Sstevel@tonic-gate hubd_t *hubd; 22497c478bd9Sstevel@tonic-gate major_t hub_major = ddi_name_to_major("hubd"); 2250ff0e937bSRaymond Chen major_t hwahc_major = ddi_name_to_major("hwahc"); 2251ff0e937bSRaymond Chen major_t usbmid_major = ddi_name_to_major("usb_mid"); 22527c478bd9Sstevel@tonic-gate 22537c478bd9Sstevel@tonic-gate /* 22547c478bd9Sstevel@tonic-gate * make sure dip is a usb hub, major of root hub is HCD 22557c478bd9Sstevel@tonic-gate * major 22567c478bd9Sstevel@tonic-gate */ 22577c478bd9Sstevel@tonic-gate if (!usba_is_root_hub(dip)) { 2258ff0e937bSRaymond Chen if (ddi_driver_major(dip) == usbmid_major) { 2259ff0e937bSRaymond Chen /* 2260ff0e937bSRaymond Chen * need to walk the children since it might be a 2261ff0e937bSRaymond Chen * HWA device 2262ff0e937bSRaymond Chen */ 2263ff0e937bSRaymond Chen 2264ff0e937bSRaymond Chen return (DDI_WALK_CONTINUE); 2265ff0e937bSRaymond Chen } 2266ff0e937bSRaymond Chen 2267ff0e937bSRaymond Chen /* TODO: DWA device may also need special handling */ 2268ff0e937bSRaymond Chen 2269ff0e937bSRaymond Chen if (((ddi_driver_major(dip) != hub_major) && 2270ff0e937bSRaymond Chen (ddi_driver_major(dip) != hwahc_major)) || 2271737d277aScth !i_ddi_devi_attached(dip)) { 22727c478bd9Sstevel@tonic-gate 22737c478bd9Sstevel@tonic-gate return (DDI_WALK_PRUNECHILD); 22747c478bd9Sstevel@tonic-gate } 22757c478bd9Sstevel@tonic-gate } 22767c478bd9Sstevel@tonic-gate 22777c478bd9Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 22787c478bd9Sstevel@tonic-gate if (hubd == NULL) { 22797c478bd9Sstevel@tonic-gate 22807c478bd9Sstevel@tonic-gate return (DDI_WALK_PRUNECHILD); 22817c478bd9Sstevel@tonic-gate } 22827c478bd9Sstevel@tonic-gate 22837c478bd9Sstevel@tonic-gate /* walk child list and remove nodes with flag DEVI_DEVICE_REMOVED */ 22847c478bd9Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 22857c478bd9Sstevel@tonic-gate 2286ff0e937bSRaymond Chen if (ddi_driver_major(dip) != hwahc_major) { 2287ff0e937bSRaymond Chen /* for normal usb hub or root hub */ 2288ff0e937bSRaymond Chen mutex_enter(HUBD_MUTEX(hubd)); 2289ff0e937bSRaymond Chen for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { 2290ff0e937bSRaymond Chen dev_info_t *cdip = hubd->h_children_dips[port]; 22917c478bd9Sstevel@tonic-gate 2292ff0e937bSRaymond Chen if (cdip == NULL || DEVI_IS_DEVICE_REMOVED(cdip) == 0) { 22937c478bd9Sstevel@tonic-gate 2294ff0e937bSRaymond Chen continue; 2295ff0e937bSRaymond Chen } 2296ff0e937bSRaymond Chen 2297ff0e937bSRaymond Chen (void) hubd_delete_child(hubd, port, NDI_DEVI_REMOVE, 2298ff0e937bSRaymond Chen B_TRUE); 22997c478bd9Sstevel@tonic-gate } 2300ff0e937bSRaymond Chen mutex_exit(HUBD_MUTEX(hubd)); 2301ff0e937bSRaymond Chen } else { 2302ff0e937bSRaymond Chen /* for HWA */ 2303ff0e937bSRaymond Chen if (hubd->h_cleanup_child != NULL) { 2304ff0e937bSRaymond Chen if (hubd->h_cleanup_child(dip) != USB_SUCCESS) { 2305ff0e937bSRaymond Chen ndi_devi_exit(dip, circ); 2306ff0e937bSRaymond Chen 2307ff0e937bSRaymond Chen return (DDI_WALK_PRUNECHILD); 2308ff0e937bSRaymond Chen } 2309ff0e937bSRaymond Chen } else { 2310ff0e937bSRaymond Chen ndi_devi_exit(dip, circ); 23117c478bd9Sstevel@tonic-gate 2312ff0e937bSRaymond Chen return (DDI_WALK_PRUNECHILD); 2313ff0e937bSRaymond Chen } 23147c478bd9Sstevel@tonic-gate } 2315ff0e937bSRaymond Chen 23167c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 23177c478bd9Sstevel@tonic-gate 23187c478bd9Sstevel@tonic-gate /* skip siblings of root hub */ 23197c478bd9Sstevel@tonic-gate if (usba_is_root_hub(dip)) { 23207c478bd9Sstevel@tonic-gate 23217c478bd9Sstevel@tonic-gate return (DDI_WALK_PRUNESIB); 23227c478bd9Sstevel@tonic-gate } 23237c478bd9Sstevel@tonic-gate 23247c478bd9Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 23257c478bd9Sstevel@tonic-gate } 23267c478bd9Sstevel@tonic-gate 23277c478bd9Sstevel@tonic-gate 23287c478bd9Sstevel@tonic-gate /* 23297c478bd9Sstevel@tonic-gate * this thread will walk all children under the root hub for this 23307c478bd9Sstevel@tonic-gate * USB bus instance and attempt to remove them 23317c478bd9Sstevel@tonic-gate */ 23327c478bd9Sstevel@tonic-gate static void 23337c478bd9Sstevel@tonic-gate hubd_root_hub_cleanup_thread(void *arg) 23347c478bd9Sstevel@tonic-gate { 23357c478bd9Sstevel@tonic-gate int circ; 23367c478bd9Sstevel@tonic-gate hubd_t *root_hubd = (hubd_t *)arg; 23377c478bd9Sstevel@tonic-gate dev_info_t *rh_dip = root_hubd->h_dip; 23387c478bd9Sstevel@tonic-gate #ifndef __lock_lint 23397c478bd9Sstevel@tonic-gate callb_cpr_t cprinfo; 23407c478bd9Sstevel@tonic-gate 23417c478bd9Sstevel@tonic-gate CALLB_CPR_INIT(&cprinfo, HUBD_MUTEX(root_hubd), callb_generic_cpr, 23427c478bd9Sstevel@tonic-gate "USB root hub"); 23437c478bd9Sstevel@tonic-gate #endif 23447c478bd9Sstevel@tonic-gate 23457c478bd9Sstevel@tonic-gate for (;;) { 23467c478bd9Sstevel@tonic-gate /* don't race with detach */ 23477c478bd9Sstevel@tonic-gate ndi_hold_devi(rh_dip); 23487c478bd9Sstevel@tonic-gate 23497c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(root_hubd)); 23507c478bd9Sstevel@tonic-gate root_hubd->h_cleanup_needed = 0; 23517c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd)); 23527c478bd9Sstevel@tonic-gate 23537c478bd9Sstevel@tonic-gate (void) devfs_clean(rh_dip, NULL, 0); 23547c478bd9Sstevel@tonic-gate 23557c478bd9Sstevel@tonic-gate ndi_devi_enter(ddi_get_parent(rh_dip), &circ); 23567c478bd9Sstevel@tonic-gate ddi_walk_devs(rh_dip, hubd_check_disconnected_ports, 2357c0f24e5bSlg NULL); 23587c478bd9Sstevel@tonic-gate #ifdef __lock_lint 23597c478bd9Sstevel@tonic-gate (void) hubd_check_disconnected_ports(rh_dip, NULL); 23607c478bd9Sstevel@tonic-gate #endif 23617c478bd9Sstevel@tonic-gate ndi_devi_exit(ddi_get_parent(rh_dip), circ); 23627c478bd9Sstevel@tonic-gate 23637c478bd9Sstevel@tonic-gate /* quit if we are not enabled anymore */ 23647c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(root_hubd)); 23657c478bd9Sstevel@tonic-gate if ((root_hubd->h_cleanup_enabled == B_FALSE) || 23667c478bd9Sstevel@tonic-gate (root_hubd->h_cleanup_needed == B_FALSE)) { 23677c478bd9Sstevel@tonic-gate root_hubd->h_cleanup_active = B_FALSE; 23687c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd)); 23697c478bd9Sstevel@tonic-gate ndi_rele_devi(rh_dip); 23707c478bd9Sstevel@tonic-gate 23717c478bd9Sstevel@tonic-gate break; 23727c478bd9Sstevel@tonic-gate } 23737c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd)); 23747c478bd9Sstevel@tonic-gate ndi_rele_devi(rh_dip); 23757c478bd9Sstevel@tonic-gate 23767c478bd9Sstevel@tonic-gate #ifndef __lock_lint 23777c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(root_hubd)); 23787c478bd9Sstevel@tonic-gate CALLB_CPR_SAFE_BEGIN(&cprinfo); 23797c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd)); 23807c478bd9Sstevel@tonic-gate 23817c478bd9Sstevel@tonic-gate delay(drv_usectohz(hubd_dip_cleanup_delay)); 23827c478bd9Sstevel@tonic-gate 23837c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(root_hubd)); 23847c478bd9Sstevel@tonic-gate CALLB_CPR_SAFE_END(&cprinfo, HUBD_MUTEX(root_hubd)); 23857c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd)); 23867c478bd9Sstevel@tonic-gate #endif 23877c478bd9Sstevel@tonic-gate } 23887c478bd9Sstevel@tonic-gate 23897c478bd9Sstevel@tonic-gate #ifndef __lock_lint 23907c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(root_hubd)); 23917c478bd9Sstevel@tonic-gate CALLB_CPR_EXIT(&cprinfo); 23927c478bd9Sstevel@tonic-gate #endif 23937c478bd9Sstevel@tonic-gate } 23947c478bd9Sstevel@tonic-gate 23957c478bd9Sstevel@tonic-gate 2396ff0e937bSRaymond Chen void 23977c478bd9Sstevel@tonic-gate hubd_schedule_cleanup(dev_info_t *rh_dip) 23987c478bd9Sstevel@tonic-gate { 2399ff0e937bSRaymond Chen hubd_t *root_hubd; 2400ff0e937bSRaymond Chen 2401ff0e937bSRaymond Chen /* 2402ff0e937bSRaymond Chen * The usb_root_hub_dip pointer for the child hub of the WUSB 2403ff0e937bSRaymond Chen * wire adapter class device points to the wire adapter, not 2404ff0e937bSRaymond Chen * the root hub. Need to find the real root hub dip so that 2405ff0e937bSRaymond Chen * the cleanup thread only starts from the root hub. 2406ff0e937bSRaymond Chen */ 2407ff0e937bSRaymond Chen while (!usba_is_root_hub(rh_dip)) { 2408ff0e937bSRaymond Chen root_hubd = hubd_get_soft_state(rh_dip); 2409ff0e937bSRaymond Chen if (root_hubd != NULL) { 2410ff0e937bSRaymond Chen rh_dip = root_hubd->h_usba_device->usb_root_hub_dip; 2411ff0e937bSRaymond Chen if (rh_dip == NULL) { 2412ff0e937bSRaymond Chen USB_DPRINTF_L2(DPRINT_MASK_ATTA, 2413ff0e937bSRaymond Chen root_hubd->h_log_handle, 2414ff0e937bSRaymond Chen "hubd_schedule_cleanup: null rh dip"); 2415ff0e937bSRaymond Chen 2416ff0e937bSRaymond Chen return; 2417ff0e937bSRaymond Chen } 2418ff0e937bSRaymond Chen } else { 2419ff0e937bSRaymond Chen USB_DPRINTF_L2(DPRINT_MASK_ATTA, 2420ff0e937bSRaymond Chen root_hubd->h_log_handle, 2421ff0e937bSRaymond Chen "hubd_schedule_cleanup: cannot find root hub"); 2422ff0e937bSRaymond Chen 2423ff0e937bSRaymond Chen return; 2424ff0e937bSRaymond Chen } 2425ff0e937bSRaymond Chen } 2426ff0e937bSRaymond Chen root_hubd = hubd_get_soft_state(rh_dip); 24277c478bd9Sstevel@tonic-gate 24287c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(root_hubd)); 24297c478bd9Sstevel@tonic-gate root_hubd->h_cleanup_needed = B_TRUE; 24307c478bd9Sstevel@tonic-gate if (root_hubd->h_cleanup_enabled && !(root_hubd->h_cleanup_active)) { 24317c478bd9Sstevel@tonic-gate root_hubd->h_cleanup_active = B_TRUE; 24327c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd)); 24337c478bd9Sstevel@tonic-gate (void) thread_create(NULL, 0, 24347c478bd9Sstevel@tonic-gate hubd_root_hub_cleanup_thread, 24357c478bd9Sstevel@tonic-gate (void *)root_hubd, 0, &p0, TS_RUN, 24367c478bd9Sstevel@tonic-gate minclsyspri); 24377c478bd9Sstevel@tonic-gate } else { 24387c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd)); 24397c478bd9Sstevel@tonic-gate } 24407c478bd9Sstevel@tonic-gate } 24417c478bd9Sstevel@tonic-gate 24427c478bd9Sstevel@tonic-gate 24437c478bd9Sstevel@tonic-gate /* 24447c478bd9Sstevel@tonic-gate * hubd_restore_device_state: 24457c478bd9Sstevel@tonic-gate * - set config for the hub 24467c478bd9Sstevel@tonic-gate * - power cycle all the ports 24477c478bd9Sstevel@tonic-gate * - for each port that was connected 24487c478bd9Sstevel@tonic-gate * - reset port 24497c478bd9Sstevel@tonic-gate * - assign addrs to the device on this port 24507c478bd9Sstevel@tonic-gate * - restart polling 24517c478bd9Sstevel@tonic-gate * - reset suspend flag 24527c478bd9Sstevel@tonic-gate */ 24537c478bd9Sstevel@tonic-gate static void 24547c478bd9Sstevel@tonic-gate hubd_restore_device_state(dev_info_t *dip, hubd_t *hubd) 24557c478bd9Sstevel@tonic-gate { 24567c478bd9Sstevel@tonic-gate int rval; 24577c478bd9Sstevel@tonic-gate int retry; 24587c478bd9Sstevel@tonic-gate uint_t hub_prev_state; 24597c478bd9Sstevel@tonic-gate usb_port_t port; 24607c478bd9Sstevel@tonic-gate uint16_t status; 24617c478bd9Sstevel@tonic-gate uint16_t change; 24627c478bd9Sstevel@tonic-gate dev_info_t *ch_dip; 24637c478bd9Sstevel@tonic-gate boolean_t ehci_root_hub; 24647c478bd9Sstevel@tonic-gate 24657c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 24667c478bd9Sstevel@tonic-gate "hubd_restore_device_state:"); 24677c478bd9Sstevel@tonic-gate 24687c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 24697c478bd9Sstevel@tonic-gate hub_prev_state = hubd->h_dev_state; 24707c478bd9Sstevel@tonic-gate ASSERT(hub_prev_state != USB_DEV_PWRED_DOWN); 24717c478bd9Sstevel@tonic-gate 24727c478bd9Sstevel@tonic-gate /* First bring the device to full power */ 24737c478bd9Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, dip, 0); 24747c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 24757c478bd9Sstevel@tonic-gate 24767c478bd9Sstevel@tonic-gate (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); 24777c478bd9Sstevel@tonic-gate 24787c478bd9Sstevel@tonic-gate if (!usba_is_root_hub(dip) && 24797c478bd9Sstevel@tonic-gate (usb_check_same_device(dip, hubd->h_log_handle, USB_LOG_L0, 24807c478bd9Sstevel@tonic-gate DPRINT_MASK_HOTPLUG, 24817c478bd9Sstevel@tonic-gate USB_CHK_BASIC|USB_CHK_CFG, NULL) != USB_SUCCESS)) { 24827c478bd9Sstevel@tonic-gate 24837c478bd9Sstevel@tonic-gate /* change the device state to disconnected */ 24847c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 24857c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_DISCONNECTED; 24867c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, dip, 0); 24877c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 24887c478bd9Sstevel@tonic-gate 24897c478bd9Sstevel@tonic-gate return; 24907c478bd9Sstevel@tonic-gate } 24917c478bd9Sstevel@tonic-gate 24927c478bd9Sstevel@tonic-gate ehci_root_hub = (strcmp(ddi_driver_name(dip), "ehci") == 0); 24937c478bd9Sstevel@tonic-gate 24947c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 24957c478bd9Sstevel@tonic-gate /* First turn off all port power */ 24967c478bd9Sstevel@tonic-gate rval = hubd_disable_all_port_power(hubd); 24977c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 24987c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle, 24997c478bd9Sstevel@tonic-gate "hubd_restore_device_state:" 25007c478bd9Sstevel@tonic-gate "turning off port power failed"); 25017c478bd9Sstevel@tonic-gate } 25027c478bd9Sstevel@tonic-gate 25037c478bd9Sstevel@tonic-gate /* Settling time before turning on again */ 25047c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 25057c478bd9Sstevel@tonic-gate delay(drv_usectohz(hubd_device_delay / 100)); 25067c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 25077c478bd9Sstevel@tonic-gate 25087c478bd9Sstevel@tonic-gate /* enable power on all ports so we can see connects */ 25097c478bd9Sstevel@tonic-gate if (hubd_enable_all_port_power(hubd) != USB_SUCCESS) { 25107c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 25117c478bd9Sstevel@tonic-gate "hubd_restore_device_state: turn on port power failed"); 25127c478bd9Sstevel@tonic-gate 25137c478bd9Sstevel@tonic-gate /* disable whatever was enabled */ 25147c478bd9Sstevel@tonic-gate (void) hubd_disable_all_port_power(hubd); 25157c478bd9Sstevel@tonic-gate 25167c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, dip, 0); 25177c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 25187c478bd9Sstevel@tonic-gate 25197c478bd9Sstevel@tonic-gate return; 25207c478bd9Sstevel@tonic-gate } 25217c478bd9Sstevel@tonic-gate 25227c478bd9Sstevel@tonic-gate /* 25237c478bd9Sstevel@tonic-gate * wait at least 3 frames before accessing devices 25247c478bd9Sstevel@tonic-gate * (note that delay's minimal time is one clock tick which 25257c478bd9Sstevel@tonic-gate * is 10ms unless hires_tick has been changed) 25267c478bd9Sstevel@tonic-gate */ 25277c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 25287c478bd9Sstevel@tonic-gate delay(drv_usectohz(10000)); 25297c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 25307c478bd9Sstevel@tonic-gate 25317c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_HUB_STATE_RECOVER; 25327c478bd9Sstevel@tonic-gate 25337c478bd9Sstevel@tonic-gate for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { 25347c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle, 25357c478bd9Sstevel@tonic-gate "hubd_restore_device_state: port=%d", port); 25367c478bd9Sstevel@tonic-gate 25377c478bd9Sstevel@tonic-gate /* 25387c478bd9Sstevel@tonic-gate * the childen_dips list may have dips that have been 25397c478bd9Sstevel@tonic-gate * already deallocated. we only get a post_detach notification 25407c478bd9Sstevel@tonic-gate * but not a destroy notification 25417c478bd9Sstevel@tonic-gate */ 25427c478bd9Sstevel@tonic-gate ch_dip = hubd->h_children_dips[port]; 25437c478bd9Sstevel@tonic-gate if (ch_dip) { 25447c478bd9Sstevel@tonic-gate /* get port status */ 25457c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 25467c478bd9Sstevel@tonic-gate &status, &change, PORT_CHANGE_CSC); 25477c478bd9Sstevel@tonic-gate 25487c478bd9Sstevel@tonic-gate /* check if it is truly connected */ 25497c478bd9Sstevel@tonic-gate if (status & PORT_STATUS_CCS) { 25507c478bd9Sstevel@tonic-gate /* 25517c478bd9Sstevel@tonic-gate * Now reset port and assign the device 25527c478bd9Sstevel@tonic-gate * its original address 25537c478bd9Sstevel@tonic-gate */ 25547c478bd9Sstevel@tonic-gate retry = 0; 25557c478bd9Sstevel@tonic-gate do { 25567c478bd9Sstevel@tonic-gate (void) hubd_reset_port(hubd, port); 25577c478bd9Sstevel@tonic-gate 25587c478bd9Sstevel@tonic-gate /* required for ppx */ 25597c478bd9Sstevel@tonic-gate (void) hubd_enable_port(hubd, port); 25607c478bd9Sstevel@tonic-gate 25617c478bd9Sstevel@tonic-gate if (retry) { 25627c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 25637c478bd9Sstevel@tonic-gate delay(drv_usectohz( 2564c0f24e5bSlg hubd_device_delay/2)); 25657c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 25667c478bd9Sstevel@tonic-gate } 25677c478bd9Sstevel@tonic-gate 25687c478bd9Sstevel@tonic-gate rval = hubd_setdevaddr(hubd, port); 25697c478bd9Sstevel@tonic-gate retry++; 25707c478bd9Sstevel@tonic-gate } while ((rval != USB_SUCCESS) && 25717c478bd9Sstevel@tonic-gate (retry < hubd_retry_enumerate)); 25727c478bd9Sstevel@tonic-gate 25737c478bd9Sstevel@tonic-gate hubd_setdevconfig(hubd, port); 25747c478bd9Sstevel@tonic-gate 25757c478bd9Sstevel@tonic-gate if (hub_prev_state == USB_DEV_DISCONNECTED) { 25767c478bd9Sstevel@tonic-gate /* post a connect event */ 25777c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 25787c478bd9Sstevel@tonic-gate hubd_post_event(hubd, port, 25797c478bd9Sstevel@tonic-gate USBA_EVENT_TAG_HOT_INSERTION); 25807c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 25817c478bd9Sstevel@tonic-gate } else { 25827c478bd9Sstevel@tonic-gate /* 25837c478bd9Sstevel@tonic-gate * Since we have this device connected 25847c478bd9Sstevel@tonic-gate * mark it reinserted to prevent 25857c478bd9Sstevel@tonic-gate * cleanup thread from stepping in. 25867c478bd9Sstevel@tonic-gate */ 25877c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 258816747f41Scth mutex_enter(&(DEVI(ch_dip)->devi_lock)); 258916747f41Scth DEVI_SET_DEVICE_REINSERTED(ch_dip); 259016747f41Scth mutex_exit(&(DEVI(ch_dip)->devi_lock)); 25917c478bd9Sstevel@tonic-gate 25927c478bd9Sstevel@tonic-gate /* 25937c478bd9Sstevel@tonic-gate * reopen pipes for children for 25947c478bd9Sstevel@tonic-gate * their DDI_RESUME 25957c478bd9Sstevel@tonic-gate */ 25967c478bd9Sstevel@tonic-gate rval = usba_persistent_pipe_open( 25977c478bd9Sstevel@tonic-gate usba_get_usba_device(ch_dip)); 25987c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 25997c478bd9Sstevel@tonic-gate ASSERT(rval == USB_SUCCESS); 26007c478bd9Sstevel@tonic-gate } 26017c478bd9Sstevel@tonic-gate } else { 26027c478bd9Sstevel@tonic-gate /* 26037c478bd9Sstevel@tonic-gate * Mark this dip for deletion as the device 26047c478bd9Sstevel@tonic-gate * is not physically present, and schedule 26057c478bd9Sstevel@tonic-gate * cleanup thread upon post resume 26067c478bd9Sstevel@tonic-gate */ 26077c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 26087c478bd9Sstevel@tonic-gate 26097c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, 26107c478bd9Sstevel@tonic-gate hubd->h_log_handle, 26117c478bd9Sstevel@tonic-gate "hubd_restore_device_state: " 26127c478bd9Sstevel@tonic-gate "dip=%p on port=%d marked for cleanup", 2613112116d8Sfb (void *)ch_dip, port); 261416747f41Scth mutex_enter(&(DEVI(ch_dip)->devi_lock)); 26157c478bd9Sstevel@tonic-gate DEVI_SET_DEVICE_REMOVED(ch_dip); 261616747f41Scth mutex_exit(&(DEVI(ch_dip)->devi_lock)); 26177c478bd9Sstevel@tonic-gate 26187c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 26197c478bd9Sstevel@tonic-gate } 26207c478bd9Sstevel@tonic-gate } else if (ehci_root_hub) { 26217c478bd9Sstevel@tonic-gate /* get port status */ 26227c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 26237c478bd9Sstevel@tonic-gate &status, &change, PORT_CHANGE_CSC); 26247c478bd9Sstevel@tonic-gate 26257c478bd9Sstevel@tonic-gate /* check if it is truly connected */ 26267c478bd9Sstevel@tonic-gate if (status & PORT_STATUS_CCS) { 26277c478bd9Sstevel@tonic-gate /* 26287c478bd9Sstevel@tonic-gate * reset the port to find out if we have 26297c478bd9Sstevel@tonic-gate * 2.0 device connected or 1.X. A 2.0 26307c478bd9Sstevel@tonic-gate * device will still be seen as connected, 26317c478bd9Sstevel@tonic-gate * while a 1.X device will switch over to 26327c478bd9Sstevel@tonic-gate * the companion controller. 26337c478bd9Sstevel@tonic-gate */ 26347c478bd9Sstevel@tonic-gate (void) hubd_reset_port(hubd, port); 26357c478bd9Sstevel@tonic-gate 26367c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 26377c478bd9Sstevel@tonic-gate &status, &change, PORT_CHANGE_CSC); 26387c478bd9Sstevel@tonic-gate 26397c478bd9Sstevel@tonic-gate if (status & 26407c478bd9Sstevel@tonic-gate (PORT_STATUS_CCS | PORT_STATUS_HSDA)) { 26417c478bd9Sstevel@tonic-gate /* 26427c478bd9Sstevel@tonic-gate * We have a USB 2.0 device 26437c478bd9Sstevel@tonic-gate * connected. Power cycle this port 26447c478bd9Sstevel@tonic-gate * so that hotplug thread can 26457c478bd9Sstevel@tonic-gate * enumerate this device. 26467c478bd9Sstevel@tonic-gate */ 26477c478bd9Sstevel@tonic-gate (void) hubd_toggle_port(hubd, port); 26487c478bd9Sstevel@tonic-gate } else { 26497c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, 26507c478bd9Sstevel@tonic-gate hubd->h_log_handle, 26517c478bd9Sstevel@tonic-gate "hubd_restore_device_state: " 26527c478bd9Sstevel@tonic-gate "device on port %d switched over", 26537c478bd9Sstevel@tonic-gate port); 26547c478bd9Sstevel@tonic-gate } 26557c478bd9Sstevel@tonic-gate } 26567c478bd9Sstevel@tonic-gate 26577c478bd9Sstevel@tonic-gate } 26587c478bd9Sstevel@tonic-gate } 26597c478bd9Sstevel@tonic-gate 26607c478bd9Sstevel@tonic-gate 26617c478bd9Sstevel@tonic-gate /* if the device had remote wakeup earlier, enable it again */ 26627c478bd9Sstevel@tonic-gate if (hubd->h_hubpm->hubp_wakeup_enabled) { 26637c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 26647c478bd9Sstevel@tonic-gate (void) usb_handle_remote_wakeup(hubd->h_dip, 26657c478bd9Sstevel@tonic-gate USB_REMOTE_WAKEUP_ENABLE); 26667c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 26677c478bd9Sstevel@tonic-gate } 26687c478bd9Sstevel@tonic-gate 26697c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_ONLINE; 26707c478bd9Sstevel@tonic-gate hubd_start_polling(hubd, 0); 26717c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, dip, 0); 26727c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 26737c478bd9Sstevel@tonic-gate } 26747c478bd9Sstevel@tonic-gate 26757c478bd9Sstevel@tonic-gate 26767c478bd9Sstevel@tonic-gate /* 26777c478bd9Sstevel@tonic-gate * hubd_cleanup: 26787c478bd9Sstevel@tonic-gate * cleanup hubd and deallocate. this function is called for 26797c478bd9Sstevel@tonic-gate * handling attach failures and detaching including dynamic 26807c478bd9Sstevel@tonic-gate * reconfiguration. If called from attaching, it must clean 26817c478bd9Sstevel@tonic-gate * up the whole thing and return success. 26827c478bd9Sstevel@tonic-gate */ 26837c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 26847c478bd9Sstevel@tonic-gate static int 26857c478bd9Sstevel@tonic-gate hubd_cleanup(dev_info_t *dip, hubd_t *hubd) 26867c478bd9Sstevel@tonic-gate { 26877c478bd9Sstevel@tonic-gate int circ, rval, old_dev_state; 26887c478bd9Sstevel@tonic-gate hub_power_t *hubpm; 26897c478bd9Sstevel@tonic-gate #ifdef DEBUG 26907c478bd9Sstevel@tonic-gate usb_port_t port; 26917c478bd9Sstevel@tonic-gate #endif 26927c478bd9Sstevel@tonic-gate 26937c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 26947c478bd9Sstevel@tonic-gate "hubd_cleanup:"); 26957c478bd9Sstevel@tonic-gate 26967c478bd9Sstevel@tonic-gate if ((hubd->h_init_state & HUBD_LOCKS_DONE) == 0) { 26977c478bd9Sstevel@tonic-gate goto done; 26987c478bd9Sstevel@tonic-gate } 26997c478bd9Sstevel@tonic-gate 27007c478bd9Sstevel@tonic-gate /* ensure we are the only one active */ 27017c478bd9Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 27027c478bd9Sstevel@tonic-gate 27037c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 27047c478bd9Sstevel@tonic-gate 27057c478bd9Sstevel@tonic-gate /* Cleanup failure is only allowed if called from detach */ 27067c478bd9Sstevel@tonic-gate if (DEVI_IS_DETACHING(dip)) { 27077c478bd9Sstevel@tonic-gate dev_info_t *rh_dip = hubd->h_usba_device->usb_root_hub_dip; 27087c478bd9Sstevel@tonic-gate 27097c478bd9Sstevel@tonic-gate /* 27107c478bd9Sstevel@tonic-gate * We are being called from detach. 27117c478bd9Sstevel@tonic-gate * Fail immediately if the hotplug thread is running 27127c478bd9Sstevel@tonic-gate * else set the dev_state to disconnected so that 27137c478bd9Sstevel@tonic-gate * hotplug thread just exits without doing anything. 27147c478bd9Sstevel@tonic-gate */ 27157c478bd9Sstevel@tonic-gate if (hubd->h_bus_ctls || hubd->h_bus_pwr || 27167c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread) { 27177c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 27187c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 27197c478bd9Sstevel@tonic-gate 27207c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 27217c478bd9Sstevel@tonic-gate "hubd_cleanup: hotplug thread/bus ctl active " 27227c478bd9Sstevel@tonic-gate "- failing detach"); 27237c478bd9Sstevel@tonic-gate 27247c478bd9Sstevel@tonic-gate return (USB_FAILURE); 27257c478bd9Sstevel@tonic-gate } 27267c478bd9Sstevel@tonic-gate 27277c478bd9Sstevel@tonic-gate /* 27287c478bd9Sstevel@tonic-gate * if the deathrow thread is still active or about 27297c478bd9Sstevel@tonic-gate * to become active, fail detach 27307c478bd9Sstevel@tonic-gate * the roothup can only be detached if nexus drivers 27317c478bd9Sstevel@tonic-gate * are unloaded or explicitly offlined 27327c478bd9Sstevel@tonic-gate */ 27337c478bd9Sstevel@tonic-gate if (rh_dip == dip) { 27347c478bd9Sstevel@tonic-gate if (hubd->h_cleanup_needed || 27357c478bd9Sstevel@tonic-gate hubd->h_cleanup_active) { 27367c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 27377c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 27387c478bd9Sstevel@tonic-gate 27397c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, 27407c478bd9Sstevel@tonic-gate hubd->h_log_handle, 27417c478bd9Sstevel@tonic-gate "hubd_cleanup: deathrow still active?" 27427c478bd9Sstevel@tonic-gate "- failing detach"); 27437c478bd9Sstevel@tonic-gate 27447c478bd9Sstevel@tonic-gate return (USB_FAILURE); 27457c478bd9Sstevel@tonic-gate } 27467c478bd9Sstevel@tonic-gate } 27477c478bd9Sstevel@tonic-gate } 27487c478bd9Sstevel@tonic-gate 27497c478bd9Sstevel@tonic-gate old_dev_state = hubd->h_dev_state; 27507c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_DISCONNECTED; 27517c478bd9Sstevel@tonic-gate 27527c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 27537c478bd9Sstevel@tonic-gate "hubd_cleanup: stop polling"); 27547c478bd9Sstevel@tonic-gate hubd_close_intr_pipe(hubd); 27557c478bd9Sstevel@tonic-gate 27567c478bd9Sstevel@tonic-gate ASSERT((hubd->h_bus_ctls || hubd->h_bus_pwr || 27577c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread) == 0); 27587c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 27597c478bd9Sstevel@tonic-gate 27607c478bd9Sstevel@tonic-gate /* 27617c478bd9Sstevel@tonic-gate * deallocate events, if events are still registered 27627c478bd9Sstevel@tonic-gate * (ie. children still attached) then we have to fail the detach 27637c478bd9Sstevel@tonic-gate */ 27647c478bd9Sstevel@tonic-gate if (hubd->h_ndi_event_hdl) { 27657c478bd9Sstevel@tonic-gate 27667c478bd9Sstevel@tonic-gate rval = ndi_event_free_hdl(hubd->h_ndi_event_hdl); 27677c478bd9Sstevel@tonic-gate if (DEVI_IS_ATTACHING(dip)) { 27687c478bd9Sstevel@tonic-gate 27697c478bd9Sstevel@tonic-gate /* It must return success if attaching. */ 27707c478bd9Sstevel@tonic-gate ASSERT(rval == NDI_SUCCESS); 27717c478bd9Sstevel@tonic-gate 27727c478bd9Sstevel@tonic-gate } else if (rval != NDI_SUCCESS) { 27737c478bd9Sstevel@tonic-gate 2774d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_ALL, hubd->h_log_handle, 27757c478bd9Sstevel@tonic-gate "hubd_cleanup: ndi_event_free_hdl failed"); 27767c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 27777c478bd9Sstevel@tonic-gate 27787c478bd9Sstevel@tonic-gate return (USB_FAILURE); 27797c478bd9Sstevel@tonic-gate 27807c478bd9Sstevel@tonic-gate } 27817c478bd9Sstevel@tonic-gate } 27827c478bd9Sstevel@tonic-gate 27837c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 27847c478bd9Sstevel@tonic-gate 278535f36846Ssl if (hubd->h_init_state & HUBD_CHILDREN_CREATED) { 27867c478bd9Sstevel@tonic-gate #ifdef DEBUG 278735f36846Ssl for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { 278835f36846Ssl ASSERT(hubd->h_usba_devices[port] == NULL); 278935f36846Ssl ASSERT(hubd->h_children_dips[port] == NULL); 279035f36846Ssl } 27917c478bd9Sstevel@tonic-gate #endif 279235f36846Ssl kmem_free(hubd->h_children_dips, hubd->h_cd_list_length); 279335f36846Ssl kmem_free(hubd->h_usba_devices, hubd->h_cd_list_length); 279435f36846Ssl } 27957c478bd9Sstevel@tonic-gate 27967c478bd9Sstevel@tonic-gate /* 27977c478bd9Sstevel@tonic-gate * Disable the event callbacks first, after this point, event 27987c478bd9Sstevel@tonic-gate * callbacks will never get called. Note we shouldn't hold 27997c478bd9Sstevel@tonic-gate * mutex while unregistering events because there may be a 28007c478bd9Sstevel@tonic-gate * competing event callback thread. Event callbacks are done 28017c478bd9Sstevel@tonic-gate * with ndi mutex held and this can cause a potential deadlock. 28027c478bd9Sstevel@tonic-gate * Note that cleanup can't fail after deregistration of events. 28037c478bd9Sstevel@tonic-gate */ 28047c478bd9Sstevel@tonic-gate if (hubd->h_init_state & HUBD_EVENTS_REGISTERED) { 28057c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 28067c478bd9Sstevel@tonic-gate usb_unregister_event_cbs(dip, &hubd_events); 28077c478bd9Sstevel@tonic-gate hubd_unregister_cpr_callback(hubd); 28087c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 28097c478bd9Sstevel@tonic-gate } 28107c478bd9Sstevel@tonic-gate 28117c478bd9Sstevel@tonic-gate /* restore the old dev state so that device can be put into low power */ 28127c478bd9Sstevel@tonic-gate hubd->h_dev_state = old_dev_state; 28137c478bd9Sstevel@tonic-gate hubpm = hubd->h_hubpm; 28147c478bd9Sstevel@tonic-gate 28157c478bd9Sstevel@tonic-gate if ((hubpm) && (hubd->h_dev_state != USB_DEV_DISCONNECTED)) { 28167c478bd9Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, dip, 0); 28177c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 28187c478bd9Sstevel@tonic-gate if (hubd->h_hubpm->hubp_wakeup_enabled) { 28197c478bd9Sstevel@tonic-gate /* 28207c478bd9Sstevel@tonic-gate * Bring the hub to full power before 28217c478bd9Sstevel@tonic-gate * issuing the disable remote wakeup command 28227c478bd9Sstevel@tonic-gate */ 28237c478bd9Sstevel@tonic-gate (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); 28247c478bd9Sstevel@tonic-gate 28257c478bd9Sstevel@tonic-gate if ((rval = usb_handle_remote_wakeup(hubd->h_dip, 28267c478bd9Sstevel@tonic-gate USB_REMOTE_WAKEUP_DISABLE)) != USB_SUCCESS) { 28277c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, 28287c478bd9Sstevel@tonic-gate hubd->h_log_handle, 28297c478bd9Sstevel@tonic-gate "hubd_cleanup: disable remote wakeup " 28307c478bd9Sstevel@tonic-gate "fails=%d", rval); 28317c478bd9Sstevel@tonic-gate } 28327c478bd9Sstevel@tonic-gate } 28337c478bd9Sstevel@tonic-gate 28347c478bd9Sstevel@tonic-gate (void) pm_lower_power(hubd->h_dip, 0, USB_DEV_OS_PWR_OFF); 28357c478bd9Sstevel@tonic-gate 28367c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 28377c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, dip, 0); 28387c478bd9Sstevel@tonic-gate } 28397c478bd9Sstevel@tonic-gate 28407c478bd9Sstevel@tonic-gate if (hubpm) { 28417c478bd9Sstevel@tonic-gate if (hubpm->hubp_child_pwrstate) { 28427c478bd9Sstevel@tonic-gate kmem_free(hubpm->hubp_child_pwrstate, 28437c478bd9Sstevel@tonic-gate MAX_PORTS + 1); 28447c478bd9Sstevel@tonic-gate } 28457c478bd9Sstevel@tonic-gate kmem_free(hubpm, sizeof (hub_power_t)); 28467c478bd9Sstevel@tonic-gate } 28477c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 28487c478bd9Sstevel@tonic-gate 28497c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 28507c478bd9Sstevel@tonic-gate "hubd_cleanup: freeing space"); 28517c478bd9Sstevel@tonic-gate 28527c478bd9Sstevel@tonic-gate if (hubd->h_init_state & HUBD_HUBDI_REGISTERED) { 28537c478bd9Sstevel@tonic-gate rval = usba_hubdi_unregister(dip); 28547c478bd9Sstevel@tonic-gate ASSERT(rval == USB_SUCCESS); 28557c478bd9Sstevel@tonic-gate } 28567c478bd9Sstevel@tonic-gate 28577c478bd9Sstevel@tonic-gate if (hubd->h_init_state & HUBD_LOCKS_DONE) { 28587c478bd9Sstevel@tonic-gate mutex_destroy(HUBD_MUTEX(hubd)); 28597c478bd9Sstevel@tonic-gate cv_destroy(&hubd->h_cv_reset_port); 2860ffcd51f3Slg cv_destroy(&hubd->h_cv_hotplug_dev); 28617c478bd9Sstevel@tonic-gate } 28627c478bd9Sstevel@tonic-gate 28637c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 28647c478bd9Sstevel@tonic-gate 28657c478bd9Sstevel@tonic-gate if (hubd->h_init_state & HUBD_MINOR_NODE_CREATED) { 28667c478bd9Sstevel@tonic-gate ddi_remove_minor_node(dip, NULL); 28677c478bd9Sstevel@tonic-gate } 28687c478bd9Sstevel@tonic-gate 28697c478bd9Sstevel@tonic-gate if (usba_is_root_hub(dip)) { 28707c478bd9Sstevel@tonic-gate usb_pipe_close(dip, hubd->h_default_pipe, 28717c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL); 28727c478bd9Sstevel@tonic-gate } 28737c478bd9Sstevel@tonic-gate 28747c478bd9Sstevel@tonic-gate done: 28757c478bd9Sstevel@tonic-gate if (hubd->h_ancestry_str) { 28767c478bd9Sstevel@tonic-gate kmem_free(hubd->h_ancestry_str, HUBD_APID_NAMELEN); 28777c478bd9Sstevel@tonic-gate } 28787c478bd9Sstevel@tonic-gate 28797c478bd9Sstevel@tonic-gate usb_client_detach(dip, hubd->h_dev_data); 28807c478bd9Sstevel@tonic-gate 28817c478bd9Sstevel@tonic-gate usb_free_log_hdl(hubd->h_log_handle); 28827c478bd9Sstevel@tonic-gate 28837c478bd9Sstevel@tonic-gate if (!usba_is_root_hub(dip)) { 28847c478bd9Sstevel@tonic-gate ddi_soft_state_free(hubd_statep, ddi_get_instance(dip)); 28857c478bd9Sstevel@tonic-gate } 28867c478bd9Sstevel@tonic-gate 28877c478bd9Sstevel@tonic-gate ddi_prop_remove_all(dip); 28887c478bd9Sstevel@tonic-gate 28897c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 28907c478bd9Sstevel@tonic-gate } 28917c478bd9Sstevel@tonic-gate 28927c478bd9Sstevel@tonic-gate 28936c7181fcSsl /* 28946c7181fcSsl * hubd_determine_port_connection: 28956c7181fcSsl * Determine which port is in connect status but does not 28966c7181fcSsl * have connect status change bit set, and mark port change 28976c7181fcSsl * bit accordingly. 28986c7181fcSsl * This function is applied during hub attach time. 28996c7181fcSsl */ 29006c7181fcSsl static usb_port_mask_t 29016c7181fcSsl hubd_determine_port_connection(hubd_t *hubd) 29026c7181fcSsl { 29036c7181fcSsl usb_port_t port; 29046c7181fcSsl usb_hub_descr_t *hub_descr; 29056c7181fcSsl uint16_t status; 29066c7181fcSsl uint16_t change; 29076c7181fcSsl usb_port_mask_t port_change = 0; 29086c7181fcSsl 29096c7181fcSsl ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 29106c7181fcSsl 29116c7181fcSsl hub_descr = &hubd->h_hub_descr; 29126c7181fcSsl 29136c7181fcSsl for (port = 1; port <= hub_descr->bNbrPorts; port++) { 29146c7181fcSsl 29156c7181fcSsl (void) hubd_determine_port_status(hubd, port, &status, 29166c7181fcSsl &change, 0); 29176c7181fcSsl 29186c7181fcSsl /* Check if port is in connect status */ 29196c7181fcSsl if (!(status & PORT_STATUS_CCS)) { 29206c7181fcSsl 29216c7181fcSsl continue; 29226c7181fcSsl } 29236c7181fcSsl 29246c7181fcSsl /* 29256c7181fcSsl * Check if port Connect Status Change bit has been set. 29266c7181fcSsl * If already set, the connection will be handled by 29276c7181fcSsl * intr polling callback, not during attach. 29286c7181fcSsl */ 29296c7181fcSsl if (change & PORT_CHANGE_CSC) { 29306c7181fcSsl 29316c7181fcSsl continue; 29326c7181fcSsl } 29336c7181fcSsl 29346c7181fcSsl port_change |= 1 << port; 29356c7181fcSsl } 29366c7181fcSsl 29376c7181fcSsl return (port_change); 29386c7181fcSsl } 29396c7181fcSsl 29406c7181fcSsl 29417c478bd9Sstevel@tonic-gate /* 29427c478bd9Sstevel@tonic-gate * hubd_check_ports: 29437c478bd9Sstevel@tonic-gate * - get hub descriptor 29447c478bd9Sstevel@tonic-gate * - check initial port status 29457c478bd9Sstevel@tonic-gate * - enable power on all ports 29467c478bd9Sstevel@tonic-gate * - enable polling on ep1 29477c478bd9Sstevel@tonic-gate */ 29487c478bd9Sstevel@tonic-gate static int 29497c478bd9Sstevel@tonic-gate hubd_check_ports(hubd_t *hubd) 29507c478bd9Sstevel@tonic-gate { 29516c7181fcSsl int rval; 29526c7181fcSsl usb_port_mask_t port_change = 0; 29536c7181fcSsl hubd_hotplug_arg_t *arg; 29547c478bd9Sstevel@tonic-gate 29557c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 29567c478bd9Sstevel@tonic-gate 29577c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 29587c478bd9Sstevel@tonic-gate "hubd_check_ports: addr=0x%x", usb_get_addr(hubd->h_dip)); 29597c478bd9Sstevel@tonic-gate 29607c478bd9Sstevel@tonic-gate /* 29617c478bd9Sstevel@tonic-gate * First turn off all port power 29627c478bd9Sstevel@tonic-gate */ 29637c478bd9Sstevel@tonic-gate if ((rval = hubd_disable_all_port_power(hubd)) != USB_SUCCESS) { 29647c478bd9Sstevel@tonic-gate 29657c478bd9Sstevel@tonic-gate /* disable whatever was enabled */ 29667c478bd9Sstevel@tonic-gate (void) hubd_disable_all_port_power(hubd); 29677c478bd9Sstevel@tonic-gate 29687c478bd9Sstevel@tonic-gate return (rval); 29697c478bd9Sstevel@tonic-gate } 29707c478bd9Sstevel@tonic-gate 29717c478bd9Sstevel@tonic-gate /* 29727c478bd9Sstevel@tonic-gate * do not switch on immediately (instantly on root hub) 29737c478bd9Sstevel@tonic-gate * and allow time to settle 29747c478bd9Sstevel@tonic-gate */ 29757c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 29767c478bd9Sstevel@tonic-gate delay(drv_usectohz(10000)); 29777c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 29787c478bd9Sstevel@tonic-gate 29797c478bd9Sstevel@tonic-gate /* 29807c478bd9Sstevel@tonic-gate * enable power on all ports so we can see connects 29817c478bd9Sstevel@tonic-gate */ 29827c478bd9Sstevel@tonic-gate if ((rval = hubd_enable_all_port_power(hubd)) != USB_SUCCESS) { 29837c478bd9Sstevel@tonic-gate /* disable whatever was enabled */ 29847c478bd9Sstevel@tonic-gate (void) hubd_disable_all_port_power(hubd); 29857c478bd9Sstevel@tonic-gate 29867c478bd9Sstevel@tonic-gate return (rval); 29877c478bd9Sstevel@tonic-gate } 29887c478bd9Sstevel@tonic-gate 29897c478bd9Sstevel@tonic-gate /* wait at least 3 frames before accessing devices */ 29907c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 29917c478bd9Sstevel@tonic-gate delay(drv_usectohz(10000)); 29927c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 29937c478bd9Sstevel@tonic-gate 29947c478bd9Sstevel@tonic-gate /* 29957c478bd9Sstevel@tonic-gate * allocate arrays for saving the dips of each child per port 29967c478bd9Sstevel@tonic-gate * 29977c478bd9Sstevel@tonic-gate * ports go from 1 - n, allocate 1 more entry 29987c478bd9Sstevel@tonic-gate */ 29997c478bd9Sstevel@tonic-gate hubd->h_cd_list_length = 3000c0f24e5bSlg (sizeof (dev_info_t **)) * (hubd->h_hub_descr.bNbrPorts + 1); 30017c478bd9Sstevel@tonic-gate 30027c478bd9Sstevel@tonic-gate hubd->h_children_dips = (dev_info_t **)kmem_zalloc( 3003c0f24e5bSlg hubd->h_cd_list_length, KM_SLEEP); 30047c478bd9Sstevel@tonic-gate hubd->h_usba_devices = (usba_device_t **)kmem_zalloc( 3005c0f24e5bSlg hubd->h_cd_list_length, KM_SLEEP); 30067c478bd9Sstevel@tonic-gate 300735f36846Ssl hubd->h_init_state |= HUBD_CHILDREN_CREATED; 300835f36846Ssl 30096c7181fcSsl mutex_exit(HUBD_MUTEX(hubd)); 30106c7181fcSsl arg = (hubd_hotplug_arg_t *)kmem_zalloc( 30116c7181fcSsl sizeof (hubd_hotplug_arg_t), KM_SLEEP); 30126c7181fcSsl mutex_enter(HUBD_MUTEX(hubd)); 30136c7181fcSsl 30146c7181fcSsl if ((rval = hubd_open_intr_pipe(hubd)) != USB_SUCCESS) { 30156c7181fcSsl kmem_free(arg, sizeof (hubd_hotplug_arg_t)); 30166c7181fcSsl 30176c7181fcSsl return (rval); 30186c7181fcSsl } 30196c7181fcSsl 30206c7181fcSsl hubd_start_polling(hubd, 0); 30216c7181fcSsl 30226c7181fcSsl /* 30236c7181fcSsl * Some hub devices, like the embedded hub in the CKS ErgoMagic 30246c7181fcSsl * keyboard, may only have connection status bit set, but not 30256c7181fcSsl * have connect status change bit set when a device has been 30266c7181fcSsl * connected to its downstream port before the hub is enumerated. 30276c7181fcSsl * Then when the hub is in enumeration, the devices connected to 30286c7181fcSsl * it cannot be detected by the intr pipe and won't be enumerated. 30296c7181fcSsl * We need to check such situation here and enumerate the downstream 30306c7181fcSsl * devices for such hubs. 30316c7181fcSsl */ 30326c7181fcSsl port_change = hubd_determine_port_connection(hubd); 30336c7181fcSsl 30346c7181fcSsl if (port_change) { 30356c7181fcSsl hubd_pm_busy_component(hubd, hubd->h_dip, 0); 30366c7181fcSsl 30376c7181fcSsl arg->hubd = hubd; 30386c7181fcSsl arg->hotplug_during_attach = B_TRUE; 30396c7181fcSsl hubd->h_port_change |= port_change; 30406c7181fcSsl 30416c7181fcSsl USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 30426c7181fcSsl "hubd_check_ports: port change=0x%x, need to connect", 30436c7181fcSsl hubd->h_port_change); 30446c7181fcSsl 30456c7181fcSsl if (usb_async_req(hubd->h_dip, hubd_hotplug_thread, 30466c7181fcSsl (void *)arg, 0) == USB_SUCCESS) { 30476c7181fcSsl hubd->h_hotplug_thread++; 30486c7181fcSsl } else { 30496c7181fcSsl /* mark this device as idle */ 30506c7181fcSsl hubd_pm_idle_component(hubd, hubd->h_dip, 0); 30516c7181fcSsl kmem_free(arg, sizeof (hubd_hotplug_arg_t)); 30526c7181fcSsl } 30536c7181fcSsl } else { 30546c7181fcSsl kmem_free(arg, sizeof (hubd_hotplug_arg_t)); 30557c478bd9Sstevel@tonic-gate } 30567c478bd9Sstevel@tonic-gate 30577c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 30587c478bd9Sstevel@tonic-gate "hubd_check_ports done"); 30597c478bd9Sstevel@tonic-gate 30606c7181fcSsl return (USB_SUCCESS); 30617c478bd9Sstevel@tonic-gate } 30627c478bd9Sstevel@tonic-gate 30637c478bd9Sstevel@tonic-gate 30647c478bd9Sstevel@tonic-gate /* 30657c478bd9Sstevel@tonic-gate * hubd_get_hub_descriptor: 30667c478bd9Sstevel@tonic-gate */ 30677c478bd9Sstevel@tonic-gate static int 30687c478bd9Sstevel@tonic-gate hubd_get_hub_descriptor(hubd_t *hubd) 30697c478bd9Sstevel@tonic-gate { 30707c478bd9Sstevel@tonic-gate usb_hub_descr_t *hub_descr = &hubd->h_hub_descr; 30717c478bd9Sstevel@tonic-gate mblk_t *data = NULL; 30727c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 30737c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 30747c478bd9Sstevel@tonic-gate uint16_t length; 30757c478bd9Sstevel@tonic-gate int rval; 30767c478bd9Sstevel@tonic-gate 30777c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 30787c478bd9Sstevel@tonic-gate "hubd_get_hub_descriptor:"); 30797c478bd9Sstevel@tonic-gate 30807c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 30817c478bd9Sstevel@tonic-gate ASSERT(hubd->h_default_pipe != 0); 30827c478bd9Sstevel@tonic-gate 30837c478bd9Sstevel@tonic-gate /* get hub descriptor length first by requesting 8 bytes only */ 30847c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 30857c478bd9Sstevel@tonic-gate 30867c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 30877c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 308835f36846Ssl HUB_CLASS_REQ_TYPE, 30897c478bd9Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */ 30907c478bd9Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_HUB, /* wValue */ 30917c478bd9Sstevel@tonic-gate 0, /* wIndex */ 30927c478bd9Sstevel@tonic-gate 8, /* wLength */ 30937c478bd9Sstevel@tonic-gate &data, 0, 30947c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 30957c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 30967c478bd9Sstevel@tonic-gate "get hub descriptor failed: cr=%d cb_fl=0x%x rval=%d", 30977c478bd9Sstevel@tonic-gate completion_reason, cb_flags, rval); 30987c478bd9Sstevel@tonic-gate freemsg(data); 30997c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 31007c478bd9Sstevel@tonic-gate 31017c478bd9Sstevel@tonic-gate return (rval); 31027c478bd9Sstevel@tonic-gate } 31037c478bd9Sstevel@tonic-gate 31047c478bd9Sstevel@tonic-gate length = *(data->b_rptr); 31057c478bd9Sstevel@tonic-gate 31067c478bd9Sstevel@tonic-gate if (length > 8) { 31077c478bd9Sstevel@tonic-gate freemsg(data); 31087c478bd9Sstevel@tonic-gate data = NULL; 31097c478bd9Sstevel@tonic-gate 31107c478bd9Sstevel@tonic-gate /* get complete hub descriptor */ 31117c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 31127c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 311335f36846Ssl HUB_CLASS_REQ_TYPE, 31147c478bd9Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */ 31157c478bd9Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_HUB, /* wValue */ 31167c478bd9Sstevel@tonic-gate 0, /* wIndex */ 31177c478bd9Sstevel@tonic-gate length, /* wLength */ 31187c478bd9Sstevel@tonic-gate &data, 0, 31197c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 31207c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 31217c478bd9Sstevel@tonic-gate "get hub descriptor failed: " 31227c478bd9Sstevel@tonic-gate "cr=%d cb_fl=0x%x rval=%d", 31237c478bd9Sstevel@tonic-gate completion_reason, cb_flags, rval); 31247c478bd9Sstevel@tonic-gate freemsg(data); 31257c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 31267c478bd9Sstevel@tonic-gate 31277c478bd9Sstevel@tonic-gate return (rval); 31287c478bd9Sstevel@tonic-gate } 31297c478bd9Sstevel@tonic-gate } 31307c478bd9Sstevel@tonic-gate 31317c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 31327c478bd9Sstevel@tonic-gate 31337c478bd9Sstevel@tonic-gate /* parse the hub descriptor */ 31347c478bd9Sstevel@tonic-gate /* only 32 ports are supported at present */ 31357c478bd9Sstevel@tonic-gate ASSERT(*(data->b_rptr + 2) <= 32); 31367c478bd9Sstevel@tonic-gate if (usb_parse_CV_descr("cccscccccc", 3137d29f5a71Szhigang lu - Sun Microsystems - Beijing China data->b_rptr, MBLKL(data), 31387c478bd9Sstevel@tonic-gate (void *)hub_descr, sizeof (usb_hub_descr_t)) == 0) { 31397c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 31407c478bd9Sstevel@tonic-gate "parsing hub descriptor failed"); 31417c478bd9Sstevel@tonic-gate 31427c478bd9Sstevel@tonic-gate freemsg(data); 31437c478bd9Sstevel@tonic-gate 31447c478bd9Sstevel@tonic-gate return (USB_FAILURE); 31457c478bd9Sstevel@tonic-gate } 31467c478bd9Sstevel@tonic-gate 31477c478bd9Sstevel@tonic-gate freemsg(data); 31487c478bd9Sstevel@tonic-gate 31497c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 315035f36846Ssl "rval=0x%x bNbrPorts=0x%x wHubChars=0x%x " 315135f36846Ssl "PwrOn2PwrGood=0x%x HubContrCurrent=%dmA", rval, 31527c478bd9Sstevel@tonic-gate hub_descr->bNbrPorts, hub_descr->wHubCharacteristics, 315335f36846Ssl hub_descr->bPwrOn2PwrGood, hub_descr->bHubContrCurrent); 31547c478bd9Sstevel@tonic-gate 31557c478bd9Sstevel@tonic-gate if (hub_descr->bNbrPorts > MAX_PORTS) { 31567c478bd9Sstevel@tonic-gate USB_DPRINTF_L0(DPRINT_MASK_ATTA, hubd->h_log_handle, 31577c478bd9Sstevel@tonic-gate "Hub driver supports max of %d ports on hub. " 31587c478bd9Sstevel@tonic-gate "Hence using the first %d port of %d ports available", 31597c478bd9Sstevel@tonic-gate MAX_PORTS, MAX_PORTS, hub_descr->bNbrPorts); 31607c478bd9Sstevel@tonic-gate 31617c478bd9Sstevel@tonic-gate hub_descr->bNbrPorts = MAX_PORTS; 31627c478bd9Sstevel@tonic-gate } 31637c478bd9Sstevel@tonic-gate 31647c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 31657c478bd9Sstevel@tonic-gate } 31667c478bd9Sstevel@tonic-gate 31677c478bd9Sstevel@tonic-gate 316835f36846Ssl /* 316935f36846Ssl * hubd_get_hub_status_words: 317035f36846Ssl */ 317135f36846Ssl static int 317235f36846Ssl hubd_get_hub_status_words(hubd_t *hubd, uint16_t *status) 317335f36846Ssl { 317435f36846Ssl usb_cr_t completion_reason; 317535f36846Ssl usb_cb_flags_t cb_flags; 317635f36846Ssl mblk_t *data = NULL; 317735f36846Ssl 317835f36846Ssl ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 317935f36846Ssl 318035f36846Ssl mutex_exit(HUBD_MUTEX(hubd)); 318135f36846Ssl 318235f36846Ssl if (usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe, 318335f36846Ssl HUB_CLASS_REQ_TYPE, 318435f36846Ssl USB_REQ_GET_STATUS, 318535f36846Ssl 0, 318635f36846Ssl 0, 318735f36846Ssl GET_STATUS_LENGTH, 318835f36846Ssl &data, 0, 318935f36846Ssl &completion_reason, &cb_flags, 0) != USB_SUCCESS) { 319035f36846Ssl USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle, 319135f36846Ssl "get hub status failed: cr=%d cb=0x%x", 319235f36846Ssl completion_reason, cb_flags); 319335f36846Ssl 319435f36846Ssl if (data) { 319535f36846Ssl freemsg(data); 319635f36846Ssl } 319735f36846Ssl 319835f36846Ssl mutex_enter(HUBD_MUTEX(hubd)); 319935f36846Ssl 320035f36846Ssl return (USB_FAILURE); 320135f36846Ssl } 320235f36846Ssl 320335f36846Ssl mutex_enter(HUBD_MUTEX(hubd)); 320435f36846Ssl 320535f36846Ssl status[0] = (*(data->b_rptr + 1) << 8) | *(data->b_rptr); 320635f36846Ssl status[1] = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2); 320735f36846Ssl 320835f36846Ssl USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle, 320935f36846Ssl "hub status=0x%x change=0x%x", status[0], status[1]); 321035f36846Ssl 321135f36846Ssl freemsg(data); 321235f36846Ssl 321335f36846Ssl return (USB_SUCCESS); 321435f36846Ssl } 321535f36846Ssl 321635f36846Ssl 32177c478bd9Sstevel@tonic-gate /* 32187c478bd9Sstevel@tonic-gate * hubd_open_intr_pipe: 32197c478bd9Sstevel@tonic-gate * we read all descriptors first for curiosity and then simply 32207c478bd9Sstevel@tonic-gate * open the pipe 32217c478bd9Sstevel@tonic-gate */ 32227c478bd9Sstevel@tonic-gate static int 32237c478bd9Sstevel@tonic-gate hubd_open_intr_pipe(hubd_t *hubd) 32247c478bd9Sstevel@tonic-gate { 32257c478bd9Sstevel@tonic-gate int rval; 32267c478bd9Sstevel@tonic-gate 32277c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 32287c478bd9Sstevel@tonic-gate "hubd_open_intr_pipe:"); 32297c478bd9Sstevel@tonic-gate 32307c478bd9Sstevel@tonic-gate ASSERT(hubd->h_intr_pipe_state == HUBD_INTR_PIPE_IDLE); 32317c478bd9Sstevel@tonic-gate 32327c478bd9Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_OPENING; 32337c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 32347c478bd9Sstevel@tonic-gate 32357c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_open(hubd->h_dip, 32367c478bd9Sstevel@tonic-gate &hubd->h_ep1_descr, &hubd->h_pipe_policy, 32377c478bd9Sstevel@tonic-gate 0, &hubd->h_ep1_ph)) != USB_SUCCESS) { 3238d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle, 32397c478bd9Sstevel@tonic-gate "open intr pipe failed (%d)", rval); 32407c478bd9Sstevel@tonic-gate 32417c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 32427c478bd9Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_IDLE; 32437c478bd9Sstevel@tonic-gate 32447c478bd9Sstevel@tonic-gate return (rval); 32457c478bd9Sstevel@tonic-gate } 32467c478bd9Sstevel@tonic-gate 32477c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 32487c478bd9Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_ACTIVE; 32497c478bd9Sstevel@tonic-gate 32507c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 3251112116d8Sfb "open intr pipe succeeded, ph=0x%p", (void *)hubd->h_ep1_ph); 32527c478bd9Sstevel@tonic-gate 32537c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 32547c478bd9Sstevel@tonic-gate } 32557c478bd9Sstevel@tonic-gate 32567c478bd9Sstevel@tonic-gate 32577c478bd9Sstevel@tonic-gate /* 32587c478bd9Sstevel@tonic-gate * hubd_start_polling: 32597c478bd9Sstevel@tonic-gate * start or restart the polling 32607c478bd9Sstevel@tonic-gate */ 32617c478bd9Sstevel@tonic-gate static void 32627c478bd9Sstevel@tonic-gate hubd_start_polling(hubd_t *hubd, int always) 32637c478bd9Sstevel@tonic-gate { 32647c478bd9Sstevel@tonic-gate usb_intr_req_t *reqp; 32657c478bd9Sstevel@tonic-gate int rval; 32667c478bd9Sstevel@tonic-gate usb_pipe_state_t pipe_state; 32677c478bd9Sstevel@tonic-gate 32687c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 32697c478bd9Sstevel@tonic-gate "start polling: always=%d dev_state=%d pipe_state=%d\n\t" 32707c478bd9Sstevel@tonic-gate "thread=%d ep1_ph=0x%p", 32717c478bd9Sstevel@tonic-gate always, hubd->h_dev_state, hubd->h_intr_pipe_state, 3272112116d8Sfb hubd->h_hotplug_thread, (void *)hubd->h_ep1_ph); 32737c478bd9Sstevel@tonic-gate 32747c478bd9Sstevel@tonic-gate /* 32757c478bd9Sstevel@tonic-gate * start or restart polling on the intr pipe 32767c478bd9Sstevel@tonic-gate * only if hotplug thread is not running 32777c478bd9Sstevel@tonic-gate */ 32787c478bd9Sstevel@tonic-gate if ((always == HUBD_ALWAYS_START_POLLING) || 32797c478bd9Sstevel@tonic-gate ((hubd->h_dev_state == USB_DEV_ONLINE) && 32807c478bd9Sstevel@tonic-gate (hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE) && 32817c478bd9Sstevel@tonic-gate (hubd->h_hotplug_thread == 0) && hubd->h_ep1_ph)) { 32827c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 32837c478bd9Sstevel@tonic-gate "start polling requested"); 32847c478bd9Sstevel@tonic-gate 32857c478bd9Sstevel@tonic-gate reqp = usb_alloc_intr_req(hubd->h_dip, 0, USB_FLAGS_SLEEP); 32867c478bd9Sstevel@tonic-gate 32877c478bd9Sstevel@tonic-gate reqp->intr_client_private = (usb_opaque_t)hubd; 32887c478bd9Sstevel@tonic-gate reqp->intr_attributes = USB_ATTRS_SHORT_XFER_OK | 3289c0f24e5bSlg USB_ATTRS_AUTOCLEARING; 32907c478bd9Sstevel@tonic-gate reqp->intr_len = hubd->h_ep1_descr.wMaxPacketSize; 32917c478bd9Sstevel@tonic-gate reqp->intr_cb = hubd_read_cb; 32927c478bd9Sstevel@tonic-gate reqp->intr_exc_cb = hubd_exception_cb; 32937c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 32947c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_intr_xfer(hubd->h_ep1_ph, reqp, 32957c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP)) != USB_SUCCESS) { 32967c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle, 32977c478bd9Sstevel@tonic-gate "start polling failed, rval=%d", rval); 32987c478bd9Sstevel@tonic-gate usb_free_intr_req(reqp); 32997c478bd9Sstevel@tonic-gate } 33007c478bd9Sstevel@tonic-gate 33017c478bd9Sstevel@tonic-gate rval = usb_pipe_get_state(hubd->h_ep1_ph, &pipe_state, 3302c0f24e5bSlg USB_FLAGS_SLEEP); 33037c478bd9Sstevel@tonic-gate if (pipe_state != USB_PIPE_STATE_ACTIVE) { 33047c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 33057c478bd9Sstevel@tonic-gate "intr pipe state=%d, rval=%d", pipe_state, rval); 33067c478bd9Sstevel@tonic-gate } 33077c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 3308112116d8Sfb "start polling request 0x%p", (void *)reqp); 33097c478bd9Sstevel@tonic-gate 33107c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 33117c478bd9Sstevel@tonic-gate } 33127c478bd9Sstevel@tonic-gate } 33137c478bd9Sstevel@tonic-gate 33147c478bd9Sstevel@tonic-gate 33157c478bd9Sstevel@tonic-gate /* 33167c478bd9Sstevel@tonic-gate * hubd_stop_polling 33177c478bd9Sstevel@tonic-gate * stop polling but do not close the pipe 33187c478bd9Sstevel@tonic-gate */ 33197c478bd9Sstevel@tonic-gate static void 33207c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd_t *hubd) 33217c478bd9Sstevel@tonic-gate { 33227c478bd9Sstevel@tonic-gate int rval; 33237c478bd9Sstevel@tonic-gate usb_pipe_state_t pipe_state; 33247c478bd9Sstevel@tonic-gate 33257c478bd9Sstevel@tonic-gate if (hubd->h_ep1_ph) { 33267c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 33277c478bd9Sstevel@tonic-gate "hubd_stop_polling:"); 33287c478bd9Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_STOPPED; 33297c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 33307c478bd9Sstevel@tonic-gate 33317c478bd9Sstevel@tonic-gate usb_pipe_stop_intr_polling(hubd->h_ep1_ph, USB_FLAGS_SLEEP); 33327c478bd9Sstevel@tonic-gate rval = usb_pipe_get_state(hubd->h_ep1_ph, &pipe_state, 3333c0f24e5bSlg USB_FLAGS_SLEEP); 33347c478bd9Sstevel@tonic-gate 33357c478bd9Sstevel@tonic-gate if (pipe_state != USB_PIPE_STATE_IDLE) { 33367c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 33377c478bd9Sstevel@tonic-gate "intr pipe state=%d, rval=%d", pipe_state, rval); 33387c478bd9Sstevel@tonic-gate } 33397c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 33407c478bd9Sstevel@tonic-gate if (hubd->h_intr_pipe_state == HUBD_INTR_PIPE_STOPPED) { 33417c478bd9Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_ACTIVE; 33427c478bd9Sstevel@tonic-gate } 33437c478bd9Sstevel@tonic-gate } 33447c478bd9Sstevel@tonic-gate } 33457c478bd9Sstevel@tonic-gate 33467c478bd9Sstevel@tonic-gate 33477c478bd9Sstevel@tonic-gate /* 33487c478bd9Sstevel@tonic-gate * hubd_close_intr_pipe: 33497c478bd9Sstevel@tonic-gate * close the pipe (which also stops the polling 33507c478bd9Sstevel@tonic-gate * and wait for the hotplug thread to exit 33517c478bd9Sstevel@tonic-gate */ 33527c478bd9Sstevel@tonic-gate static void 33537c478bd9Sstevel@tonic-gate hubd_close_intr_pipe(hubd_t *hubd) 33547c478bd9Sstevel@tonic-gate { 33557c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 33567c478bd9Sstevel@tonic-gate "hubd_close_intr_pipe:"); 33577c478bd9Sstevel@tonic-gate 33587c478bd9Sstevel@tonic-gate /* 33597c478bd9Sstevel@tonic-gate * Now that no async operation is outstanding on pipe, 33607c478bd9Sstevel@tonic-gate * we can change the state to HUBD_INTR_PIPE_CLOSING 33617c478bd9Sstevel@tonic-gate */ 33627c478bd9Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_CLOSING; 33637c478bd9Sstevel@tonic-gate 33647c478bd9Sstevel@tonic-gate ASSERT(hubd->h_hotplug_thread == 0); 33657c478bd9Sstevel@tonic-gate 33667c478bd9Sstevel@tonic-gate if (hubd->h_ep1_ph) { 33677c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 33687c478bd9Sstevel@tonic-gate usb_pipe_close(hubd->h_dip, hubd->h_ep1_ph, USB_FLAGS_SLEEP, 3369c0f24e5bSlg NULL, NULL); 33707c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 33717c478bd9Sstevel@tonic-gate hubd->h_ep1_ph = NULL; 33727c478bd9Sstevel@tonic-gate } 33737c478bd9Sstevel@tonic-gate 33747c478bd9Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_IDLE; 33757c478bd9Sstevel@tonic-gate } 33767c478bd9Sstevel@tonic-gate 33777c478bd9Sstevel@tonic-gate 33787c478bd9Sstevel@tonic-gate /* 33797c478bd9Sstevel@tonic-gate * hubd_exception_cb 33807c478bd9Sstevel@tonic-gate * interrupt ep1 exception callback function. 33817c478bd9Sstevel@tonic-gate * this callback executes in taskq thread context and assumes 33827c478bd9Sstevel@tonic-gate * autoclearing 33837c478bd9Sstevel@tonic-gate */ 33847c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 33857c478bd9Sstevel@tonic-gate static void 33867c478bd9Sstevel@tonic-gate hubd_exception_cb(usb_pipe_handle_t pipe, usb_intr_req_t *reqp) 33877c478bd9Sstevel@tonic-gate { 33887c478bd9Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)(reqp->intr_client_private); 33897c478bd9Sstevel@tonic-gate 33907c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CALLBACK, hubd->h_log_handle, 33917c478bd9Sstevel@tonic-gate "hubd_exception_cb: " 3392112116d8Sfb "req=0x%p cr=%d data=0x%p cb_flags=0x%x", (void *)reqp, 3393112116d8Sfb reqp->intr_completion_reason, (void *)reqp->intr_data, 33947c478bd9Sstevel@tonic-gate reqp->intr_cb_flags); 33957c478bd9Sstevel@tonic-gate 33967c478bd9Sstevel@tonic-gate ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0); 33977c478bd9Sstevel@tonic-gate 33987c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 33997c478bd9Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0); 34007c478bd9Sstevel@tonic-gate 34017c478bd9Sstevel@tonic-gate switch (reqp->intr_completion_reason) { 34027c478bd9Sstevel@tonic-gate case USB_CR_PIPE_RESET: 34037c478bd9Sstevel@tonic-gate /* only restart polling after autoclearing */ 34047c478bd9Sstevel@tonic-gate if ((hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE) && 34057c478bd9Sstevel@tonic-gate (hubd->h_port_reset_wait == 0)) { 34067c478bd9Sstevel@tonic-gate hubd_start_polling(hubd, 0); 34077c478bd9Sstevel@tonic-gate } 34087c478bd9Sstevel@tonic-gate 34097c478bd9Sstevel@tonic-gate break; 34107c478bd9Sstevel@tonic-gate case USB_CR_DEV_NOT_RESP: 34117c478bd9Sstevel@tonic-gate case USB_CR_STOPPED_POLLING: 34127c478bd9Sstevel@tonic-gate case USB_CR_PIPE_CLOSING: 34137c478bd9Sstevel@tonic-gate case USB_CR_UNSPECIFIED_ERR: 34147c478bd9Sstevel@tonic-gate /* never restart polling on these conditions */ 34157c478bd9Sstevel@tonic-gate default: 34167c478bd9Sstevel@tonic-gate /* for all others, wait for the autoclearing PIPE_RESET cb */ 34177c478bd9Sstevel@tonic-gate 34187c478bd9Sstevel@tonic-gate break; 34197c478bd9Sstevel@tonic-gate } 34207c478bd9Sstevel@tonic-gate 34217c478bd9Sstevel@tonic-gate usb_free_intr_req(reqp); 34227c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0); 34237c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 34247c478bd9Sstevel@tonic-gate } 34257c478bd9Sstevel@tonic-gate 34267c478bd9Sstevel@tonic-gate 34277c478bd9Sstevel@tonic-gate /* 34287c478bd9Sstevel@tonic-gate * helper function to convert LE bytes to a portmask 34297c478bd9Sstevel@tonic-gate */ 34307c478bd9Sstevel@tonic-gate static usb_port_mask_t 34317c478bd9Sstevel@tonic-gate hubd_mblk2portmask(mblk_t *data) 34327c478bd9Sstevel@tonic-gate { 3433d29f5a71Szhigang lu - Sun Microsystems - Beijing China int len = min(MBLKL(data), sizeof (usb_port_mask_t)); 34347c478bd9Sstevel@tonic-gate usb_port_mask_t rval = 0; 34357c478bd9Sstevel@tonic-gate int i; 34367c478bd9Sstevel@tonic-gate 34377c478bd9Sstevel@tonic-gate for (i = 0; i < len; i++) { 34387c478bd9Sstevel@tonic-gate rval |= data->b_rptr[i] << (i * 8); 34397c478bd9Sstevel@tonic-gate } 34407c478bd9Sstevel@tonic-gate 34417c478bd9Sstevel@tonic-gate return (rval); 34427c478bd9Sstevel@tonic-gate } 34437c478bd9Sstevel@tonic-gate 34447c478bd9Sstevel@tonic-gate 34457c478bd9Sstevel@tonic-gate /* 34467c478bd9Sstevel@tonic-gate * hubd_read_cb: 34477c478bd9Sstevel@tonic-gate * interrupt ep1 callback function 34487c478bd9Sstevel@tonic-gate * 34497c478bd9Sstevel@tonic-gate * the status indicates just a change on the pipe with no indication 34507c478bd9Sstevel@tonic-gate * of what the change was 34517c478bd9Sstevel@tonic-gate * 34527c478bd9Sstevel@tonic-gate * known conditions: 34537c478bd9Sstevel@tonic-gate * - reset port completion 34547c478bd9Sstevel@tonic-gate * - connect 34557c478bd9Sstevel@tonic-gate * - disconnect 34567c478bd9Sstevel@tonic-gate * 34577c478bd9Sstevel@tonic-gate * for handling the hotplugging, create a new thread that can do 34587c478bd9Sstevel@tonic-gate * synchronous usba calls 34597c478bd9Sstevel@tonic-gate */ 34607c478bd9Sstevel@tonic-gate static void 34617c478bd9Sstevel@tonic-gate hubd_read_cb(usb_pipe_handle_t pipe, usb_intr_req_t *reqp) 34627c478bd9Sstevel@tonic-gate { 34637c478bd9Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)(reqp->intr_client_private); 34647c478bd9Sstevel@tonic-gate size_t length; 34657c478bd9Sstevel@tonic-gate mblk_t *data = reqp->intr_data; 34666c7181fcSsl int mem_flag = 0; 34676c7181fcSsl hubd_hotplug_arg_t *arg; 34687c478bd9Sstevel@tonic-gate 34697c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 3470112116d8Sfb "hubd_read_cb: ph=0x%p req=0x%p", (void *)pipe, (void *)reqp); 34717c478bd9Sstevel@tonic-gate 34727c478bd9Sstevel@tonic-gate ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0); 34737c478bd9Sstevel@tonic-gate 34747c478bd9Sstevel@tonic-gate /* 34757c478bd9Sstevel@tonic-gate * At present, we are not handling notification for completion of 34767c478bd9Sstevel@tonic-gate * asynchronous pipe reset, for which this data ptr could be NULL 34777c478bd9Sstevel@tonic-gate */ 34787c478bd9Sstevel@tonic-gate 34797c478bd9Sstevel@tonic-gate if (data == NULL) { 34807c478bd9Sstevel@tonic-gate usb_free_intr_req(reqp); 34817c478bd9Sstevel@tonic-gate 34827c478bd9Sstevel@tonic-gate return; 34837c478bd9Sstevel@tonic-gate } 34847c478bd9Sstevel@tonic-gate 34856c7181fcSsl arg = (hubd_hotplug_arg_t *)kmem_zalloc( 34866c7181fcSsl sizeof (hubd_hotplug_arg_t), KM_SLEEP); 34876c7181fcSsl mem_flag = 1; 34886c7181fcSsl 34897c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 34907c478bd9Sstevel@tonic-gate 34917c478bd9Sstevel@tonic-gate if ((hubd->h_dev_state == USB_DEV_SUSPENDED) || 34927c478bd9Sstevel@tonic-gate (hubd->h_intr_pipe_state != HUBD_INTR_PIPE_ACTIVE)) { 34937c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 34947c478bd9Sstevel@tonic-gate usb_free_intr_req(reqp); 34956c7181fcSsl kmem_free(arg, sizeof (hubd_hotplug_arg_t)); 34967c478bd9Sstevel@tonic-gate 34977c478bd9Sstevel@tonic-gate return; 34987c478bd9Sstevel@tonic-gate } 34997c478bd9Sstevel@tonic-gate 35007c478bd9Sstevel@tonic-gate ASSERT(hubd->h_ep1_ph == pipe); 35017c478bd9Sstevel@tonic-gate 3502d29f5a71Szhigang lu - Sun Microsystems - Beijing China length = MBLKL(data); 35037c478bd9Sstevel@tonic-gate 35047c478bd9Sstevel@tonic-gate /* 35057c478bd9Sstevel@tonic-gate * Only look at the data and startup the hotplug thread if 35067c478bd9Sstevel@tonic-gate * there actually is data. 35077c478bd9Sstevel@tonic-gate */ 35087c478bd9Sstevel@tonic-gate if (length != 0) { 35097c478bd9Sstevel@tonic-gate usb_port_mask_t port_change = hubd_mblk2portmask(data); 35107c478bd9Sstevel@tonic-gate 35117c478bd9Sstevel@tonic-gate /* 35127c478bd9Sstevel@tonic-gate * if a port change was already reported and we are waiting for 35137c478bd9Sstevel@tonic-gate * reset port completion then wake up the hotplug thread which 35147c478bd9Sstevel@tonic-gate * should be waiting on reset port completion 35157c478bd9Sstevel@tonic-gate * 35167c478bd9Sstevel@tonic-gate * if there is disconnect event instead of reset completion, let 35177c478bd9Sstevel@tonic-gate * the hotplug thread figure this out 35187c478bd9Sstevel@tonic-gate */ 35197c478bd9Sstevel@tonic-gate 35207c478bd9Sstevel@tonic-gate /* remove the reset wait bits from the status */ 35217c478bd9Sstevel@tonic-gate hubd->h_port_change |= port_change & 3522c0f24e5bSlg ~hubd->h_port_reset_wait; 35237c478bd9Sstevel@tonic-gate 35247c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_CALLBACK, hubd->h_log_handle, 35257c478bd9Sstevel@tonic-gate "port change=0x%x port_reset_wait=0x%x", 35267c478bd9Sstevel@tonic-gate hubd->h_port_change, hubd->h_port_reset_wait); 35277c478bd9Sstevel@tonic-gate 35287c478bd9Sstevel@tonic-gate /* there should be only one reset bit active at the time */ 35297c478bd9Sstevel@tonic-gate if (hubd->h_port_reset_wait & port_change) { 35307c478bd9Sstevel@tonic-gate hubd->h_port_reset_wait = 0; 35317c478bd9Sstevel@tonic-gate cv_signal(&hubd->h_cv_reset_port); 35327c478bd9Sstevel@tonic-gate } 35337c478bd9Sstevel@tonic-gate 35347c478bd9Sstevel@tonic-gate /* 35357c478bd9Sstevel@tonic-gate * kick off the thread only if device is ONLINE and it is not 35367c478bd9Sstevel@tonic-gate * during attaching or detaching 35377c478bd9Sstevel@tonic-gate */ 35387c478bd9Sstevel@tonic-gate if ((hubd->h_dev_state == USB_DEV_ONLINE) && 35397c478bd9Sstevel@tonic-gate (!DEVI_IS_ATTACHING(hubd->h_dip)) && 35407c478bd9Sstevel@tonic-gate (!DEVI_IS_DETACHING(hubd->h_dip)) && 35417c478bd9Sstevel@tonic-gate (hubd->h_port_change) && 35427c478bd9Sstevel@tonic-gate (hubd->h_hotplug_thread == 0)) { 35437c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_CALLBACK, hubd->h_log_handle, 35447c478bd9Sstevel@tonic-gate "creating hotplug thread: " 35457c478bd9Sstevel@tonic-gate "dev_state=%d", hubd->h_dev_state); 35467c478bd9Sstevel@tonic-gate 35477c478bd9Sstevel@tonic-gate /* 35487c478bd9Sstevel@tonic-gate * Mark this device as busy. The will be marked idle 35497c478bd9Sstevel@tonic-gate * if the async req fails or at the exit of hotplug 35507c478bd9Sstevel@tonic-gate * thread 35517c478bd9Sstevel@tonic-gate */ 35527c478bd9Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0); 35537c478bd9Sstevel@tonic-gate 35546c7181fcSsl arg->hubd = hubd; 35556c7181fcSsl arg->hotplug_during_attach = B_FALSE; 35566c7181fcSsl 35577c478bd9Sstevel@tonic-gate if (usb_async_req(hubd->h_dip, 35587c478bd9Sstevel@tonic-gate hubd_hotplug_thread, 35596c7181fcSsl (void *)arg, 0) == USB_SUCCESS) { 35607c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread++; 35616c7181fcSsl mem_flag = 0; 35627c478bd9Sstevel@tonic-gate } else { 35637c478bd9Sstevel@tonic-gate /* mark this device as idle */ 35647c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, 35657c478bd9Sstevel@tonic-gate hubd->h_dip, 0); 35667c478bd9Sstevel@tonic-gate } 35677c478bd9Sstevel@tonic-gate } 35687c478bd9Sstevel@tonic-gate } 35697c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 35707c478bd9Sstevel@tonic-gate 35716c7181fcSsl if (mem_flag == 1) { 35726c7181fcSsl kmem_free(arg, sizeof (hubd_hotplug_arg_t)); 35736c7181fcSsl } 35746c7181fcSsl 35757c478bd9Sstevel@tonic-gate usb_free_intr_req(reqp); 35767c478bd9Sstevel@tonic-gate } 35777c478bd9Sstevel@tonic-gate 35787c478bd9Sstevel@tonic-gate 35797c478bd9Sstevel@tonic-gate /* 35807c478bd9Sstevel@tonic-gate * hubd_hotplug_thread: 35817c478bd9Sstevel@tonic-gate * handles resetting of port, and creating children 35827c478bd9Sstevel@tonic-gate * 35837c478bd9Sstevel@tonic-gate * the ports to check are indicated in h_port_change bit mask 35847c478bd9Sstevel@tonic-gate * XXX note that one time poll doesn't work on the root hub 35857c478bd9Sstevel@tonic-gate */ 35867c478bd9Sstevel@tonic-gate static void 35877c478bd9Sstevel@tonic-gate hubd_hotplug_thread(void *arg) 35887c478bd9Sstevel@tonic-gate { 35896c7181fcSsl hubd_hotplug_arg_t *hd_arg = (hubd_hotplug_arg_t *)arg; 35906c7181fcSsl hubd_t *hubd = hd_arg->hubd; 35916c7181fcSsl boolean_t attach_flg = hd_arg->hotplug_during_attach; 35927c478bd9Sstevel@tonic-gate usb_port_t port; 35937c478bd9Sstevel@tonic-gate uint16_t nports; 35947c478bd9Sstevel@tonic-gate uint16_t status, change; 35957c478bd9Sstevel@tonic-gate hub_power_t *hubpm; 35967c478bd9Sstevel@tonic-gate dev_info_t *hdip = hubd->h_dip; 35977c478bd9Sstevel@tonic-gate dev_info_t *rh_dip = hubd->h_usba_device->usb_root_hub_dip; 3598c0f24e5bSlg dev_info_t *child_dip; 35997c478bd9Sstevel@tonic-gate boolean_t online_child = B_FALSE; 36007c478bd9Sstevel@tonic-gate boolean_t offline_child = B_FALSE; 36017c478bd9Sstevel@tonic-gate boolean_t pwrup_child = B_FALSE; 3602c0f24e5bSlg int prh_circ, rh_circ, chld_circ, circ, old_state; 36037c478bd9Sstevel@tonic-gate 36047c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 36057c478bd9Sstevel@tonic-gate "hubd_hotplug_thread: started"); 36067c478bd9Sstevel@tonic-gate 36076c7181fcSsl kmem_free(arg, sizeof (hubd_hotplug_arg_t)); 36086c7181fcSsl 36097c478bd9Sstevel@tonic-gate /* 36107c478bd9Sstevel@tonic-gate * if our bus power entry point is active, process the change 36117c478bd9Sstevel@tonic-gate * on the next notification of interrupt pipe 36127c478bd9Sstevel@tonic-gate */ 36137c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 36147c478bd9Sstevel@tonic-gate if (hubd->h_bus_pwr || (hubd->h_hotplug_thread > 1)) { 36157c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread--; 36167c478bd9Sstevel@tonic-gate 36177c478bd9Sstevel@tonic-gate /* mark this device as idle */ 36187c478bd9Sstevel@tonic-gate hubd_pm_idle_component(hubd, hubd->h_dip, 0); 36197c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 36207c478bd9Sstevel@tonic-gate 36217c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 36227c478bd9Sstevel@tonic-gate "hubd_hotplug_thread: " 36237c478bd9Sstevel@tonic-gate "bus_power in progress/hotplugging undesirable - quit"); 36247c478bd9Sstevel@tonic-gate 36257c478bd9Sstevel@tonic-gate return; 36267c478bd9Sstevel@tonic-gate } 36277c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 36287c478bd9Sstevel@tonic-gate 36297c478bd9Sstevel@tonic-gate ndi_hold_devi(hdip); /* so we don't race with detach */ 36307c478bd9Sstevel@tonic-gate 36317c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 36327c478bd9Sstevel@tonic-gate 36337c478bd9Sstevel@tonic-gate /* is this the root hub? */ 36347c478bd9Sstevel@tonic-gate if (hdip == rh_dip) { 36357c478bd9Sstevel@tonic-gate if (hubd->h_dev_state == USB_DEV_PWRED_DOWN) { 36367c478bd9Sstevel@tonic-gate hubpm = hubd->h_hubpm; 36377c478bd9Sstevel@tonic-gate 36387c478bd9Sstevel@tonic-gate /* mark the root hub as full power */ 36397c478bd9Sstevel@tonic-gate hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR; 36407c478bd9Sstevel@tonic-gate hubpm->hubp_time_at_full_power = ddi_get_time(); 36417c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 36427c478bd9Sstevel@tonic-gate 36437c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 36447c478bd9Sstevel@tonic-gate "hubd_hotplug_thread: call pm_power_has_changed"); 36457c478bd9Sstevel@tonic-gate 36467c478bd9Sstevel@tonic-gate (void) pm_power_has_changed(hdip, 0, 3647c0f24e5bSlg USB_DEV_OS_FULL_PWR); 36487c478bd9Sstevel@tonic-gate 36497c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 36507c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_ONLINE; 36517c478bd9Sstevel@tonic-gate } 36527c478bd9Sstevel@tonic-gate 36537c478bd9Sstevel@tonic-gate } else { 36547c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 36557c478bd9Sstevel@tonic-gate "hubd_hotplug_thread: not root hub"); 36567c478bd9Sstevel@tonic-gate } 36577c478bd9Sstevel@tonic-gate 36587c478bd9Sstevel@tonic-gate ASSERT(hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE); 36597c478bd9Sstevel@tonic-gate 36607c478bd9Sstevel@tonic-gate nports = hubd->h_hub_descr.bNbrPorts; 36617c478bd9Sstevel@tonic-gate 36627c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd); 36637c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 36647c478bd9Sstevel@tonic-gate 36657c478bd9Sstevel@tonic-gate /* 36667c478bd9Sstevel@tonic-gate * this ensures one hotplug activity per system at a time. 36677c478bd9Sstevel@tonic-gate * we enter the parent PCI node to have this serialization. 36687c478bd9Sstevel@tonic-gate * this also excludes ioctls and deathrow thread 36697c478bd9Sstevel@tonic-gate * (a bit crude but easier to debug) 36707c478bd9Sstevel@tonic-gate */ 36717c478bd9Sstevel@tonic-gate ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ); 36727c478bd9Sstevel@tonic-gate ndi_devi_enter(rh_dip, &rh_circ); 36737c478bd9Sstevel@tonic-gate 36747c478bd9Sstevel@tonic-gate /* exclude other threads */ 36757c478bd9Sstevel@tonic-gate ndi_devi_enter(hdip, &circ); 36767c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 36777c478bd9Sstevel@tonic-gate 36787c478bd9Sstevel@tonic-gate while ((hubd->h_dev_state == USB_DEV_ONLINE) && 36797c478bd9Sstevel@tonic-gate (hubd->h_port_change)) { 36807c478bd9Sstevel@tonic-gate /* 36817c478bd9Sstevel@tonic-gate * The 0th bit is the hub status change bit. 36827c478bd9Sstevel@tonic-gate * handle loss of local power here 36837c478bd9Sstevel@tonic-gate */ 36847c478bd9Sstevel@tonic-gate if (hubd->h_port_change & HUB_CHANGE_STATUS) { 36857c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 36867c478bd9Sstevel@tonic-gate "hubd_hotplug_thread: hub status change!"); 36877c478bd9Sstevel@tonic-gate 36887c478bd9Sstevel@tonic-gate /* 36897c478bd9Sstevel@tonic-gate * This should be handled properly. For now, 36907c478bd9Sstevel@tonic-gate * mask off the bit. 36917c478bd9Sstevel@tonic-gate */ 36927c478bd9Sstevel@tonic-gate hubd->h_port_change &= ~HUB_CHANGE_STATUS; 36937c478bd9Sstevel@tonic-gate 36947c478bd9Sstevel@tonic-gate /* 36957c478bd9Sstevel@tonic-gate * check and ack hub status 36967c478bd9Sstevel@tonic-gate * this causes stall conditions 36977c478bd9Sstevel@tonic-gate * when local power is removed 36987c478bd9Sstevel@tonic-gate */ 36997c478bd9Sstevel@tonic-gate (void) hubd_get_hub_status(hubd); 37007c478bd9Sstevel@tonic-gate } 37017c478bd9Sstevel@tonic-gate 37027c478bd9Sstevel@tonic-gate for (port = 1; port <= nports; port++) { 37037c478bd9Sstevel@tonic-gate usb_port_mask_t port_mask; 37047c478bd9Sstevel@tonic-gate boolean_t was_connected; 37057c478bd9Sstevel@tonic-gate 37067c478bd9Sstevel@tonic-gate port_mask = 1 << port; 37077c478bd9Sstevel@tonic-gate was_connected = 3708c0f24e5bSlg (hubd->h_port_state[port] & PORT_STATUS_CCS) && 3709c0f24e5bSlg (hubd->h_children_dips[port]); 37107c478bd9Sstevel@tonic-gate 37117c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 37127c478bd9Sstevel@tonic-gate "hubd_hotplug_thread: " 37137c478bd9Sstevel@tonic-gate "port %d mask=0x%x change=0x%x connected=0x%x", 37147c478bd9Sstevel@tonic-gate port, port_mask, hubd->h_port_change, 37157c478bd9Sstevel@tonic-gate was_connected); 37167c478bd9Sstevel@tonic-gate 37177c478bd9Sstevel@tonic-gate /* 37187c478bd9Sstevel@tonic-gate * is this a port connection that changed? 37197c478bd9Sstevel@tonic-gate */ 37207c478bd9Sstevel@tonic-gate if ((hubd->h_port_change & port_mask) == 0) { 37217c478bd9Sstevel@tonic-gate 37227c478bd9Sstevel@tonic-gate continue; 37237c478bd9Sstevel@tonic-gate } 37247c478bd9Sstevel@tonic-gate hubd->h_port_change &= ~port_mask; 37257c478bd9Sstevel@tonic-gate 37267c478bd9Sstevel@tonic-gate /* ack all changes */ 37277c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 37287c478bd9Sstevel@tonic-gate &status, &change, HUBD_ACK_ALL_CHANGES); 37297c478bd9Sstevel@tonic-gate 37307c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 37317c478bd9Sstevel@tonic-gate "handle port %d:\n\t" 37327c478bd9Sstevel@tonic-gate "new status=0x%x change=0x%x was_conn=0x%x ", 37337c478bd9Sstevel@tonic-gate port, status, change, was_connected); 37347c478bd9Sstevel@tonic-gate 37357c478bd9Sstevel@tonic-gate /* Recover a disabled port */ 37367c478bd9Sstevel@tonic-gate if (change & PORT_CHANGE_PESC) { 37377c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, 3738c0f24e5bSlg hubd->h_log_handle, 3739c0f24e5bSlg "port%d Disabled - " 3740c0f24e5bSlg "status=0x%x, change=0x%x", 3741c0f24e5bSlg port, status, change); 37427c478bd9Sstevel@tonic-gate 37437c478bd9Sstevel@tonic-gate /* 37447c478bd9Sstevel@tonic-gate * if the port was connected and is still 37457c478bd9Sstevel@tonic-gate * connected, recover the port 37467c478bd9Sstevel@tonic-gate */ 37477c478bd9Sstevel@tonic-gate if (was_connected && (status & 37487c478bd9Sstevel@tonic-gate PORT_STATUS_CCS)) { 37497c478bd9Sstevel@tonic-gate online_child |= 37507c478bd9Sstevel@tonic-gate (hubd_recover_disabled_port(hubd, 37517c478bd9Sstevel@tonic-gate port) == USB_SUCCESS); 37527c478bd9Sstevel@tonic-gate } 37537c478bd9Sstevel@tonic-gate } 37547c478bd9Sstevel@tonic-gate 37557c478bd9Sstevel@tonic-gate /* 37567c478bd9Sstevel@tonic-gate * Now check what changed on the port 37577c478bd9Sstevel@tonic-gate */ 37586c7181fcSsl if ((change & PORT_CHANGE_CSC) || attach_flg) { 37597c478bd9Sstevel@tonic-gate if ((status & PORT_STATUS_CCS) && 37607c478bd9Sstevel@tonic-gate (!was_connected)) { 37617c478bd9Sstevel@tonic-gate /* new device plugged in */ 37627c478bd9Sstevel@tonic-gate online_child |= 37637c478bd9Sstevel@tonic-gate (hubd_handle_port_connect(hubd, 37647c478bd9Sstevel@tonic-gate port) == USB_SUCCESS); 37657c478bd9Sstevel@tonic-gate 37667c478bd9Sstevel@tonic-gate } else if ((status & PORT_STATUS_CCS) && 37677c478bd9Sstevel@tonic-gate was_connected) { 37687c478bd9Sstevel@tonic-gate /* 37697c478bd9Sstevel@tonic-gate * In this case we can never be sure 37707c478bd9Sstevel@tonic-gate * if the device indeed got hotplugged 37717c478bd9Sstevel@tonic-gate * or the hub is falsely reporting the 37727c478bd9Sstevel@tonic-gate * change. 37737c478bd9Sstevel@tonic-gate */ 3774c0f24e5bSlg child_dip = hubd->h_children_dips[port]; 37757c478bd9Sstevel@tonic-gate 3776c0f24e5bSlg mutex_exit(HUBD_MUTEX(hubd)); 37777c478bd9Sstevel@tonic-gate /* 3778c0f24e5bSlg * this ensures we do not race with 3779c0f24e5bSlg * other threads which are detaching 3780c0f24e5bSlg * the child driver at the same time. 37817c478bd9Sstevel@tonic-gate */ 3782c0f24e5bSlg ndi_devi_enter(child_dip, &chld_circ); 3783c0f24e5bSlg /* 3784c0f24e5bSlg * Now check if the driver remains 3785c0f24e5bSlg * attached. 3786c0f24e5bSlg */ 3787c0f24e5bSlg if (i_ddi_devi_attached(child_dip)) { 3788c0f24e5bSlg /* 3789c0f24e5bSlg * first post a disconnect event 3790c0f24e5bSlg * to the child. 3791c0f24e5bSlg */ 3792c0f24e5bSlg hubd_post_event(hubd, port, 3793c0f24e5bSlg USBA_EVENT_TAG_HOT_REMOVAL); 3794c0f24e5bSlg mutex_enter(HUBD_MUTEX(hubd)); 3795c0f24e5bSlg 3796c0f24e5bSlg /* 3797c0f24e5bSlg * then reset the port and 3798c0f24e5bSlg * recover the device 3799c0f24e5bSlg */ 3800c0f24e5bSlg online_child |= 3801c0f24e5bSlg (hubd_handle_port_connect( 3802c0f24e5bSlg hubd, port) == USB_SUCCESS); 3803c0f24e5bSlg 3804c0f24e5bSlg mutex_exit(HUBD_MUTEX(hubd)); 3805c0f24e5bSlg } 3806c0f24e5bSlg 3807c0f24e5bSlg ndi_devi_exit(child_dip, chld_circ); 3808c0f24e5bSlg mutex_enter(HUBD_MUTEX(hubd)); 38097c478bd9Sstevel@tonic-gate } else if (was_connected) { 38107c478bd9Sstevel@tonic-gate /* this is a disconnect */ 38117c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 38127c478bd9Sstevel@tonic-gate hubd_post_event(hubd, port, 38137c478bd9Sstevel@tonic-gate USBA_EVENT_TAG_HOT_REMOVAL); 38147c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 38157c478bd9Sstevel@tonic-gate 38167c478bd9Sstevel@tonic-gate offline_child = B_TRUE; 38177c478bd9Sstevel@tonic-gate } 38187c478bd9Sstevel@tonic-gate } 38197c478bd9Sstevel@tonic-gate 38207c478bd9Sstevel@tonic-gate /* 38217c478bd9Sstevel@tonic-gate * Check if any port is coming out of suspend 38227c478bd9Sstevel@tonic-gate */ 38237c478bd9Sstevel@tonic-gate if (change & PORT_CHANGE_PSSC) { 38247c478bd9Sstevel@tonic-gate /* a resuming device could have disconnected */ 38257c478bd9Sstevel@tonic-gate if (was_connected && 38267c478bd9Sstevel@tonic-gate hubd->h_children_dips[port]) { 38277c478bd9Sstevel@tonic-gate 38287c478bd9Sstevel@tonic-gate /* device on this port resuming */ 38297c478bd9Sstevel@tonic-gate dev_info_t *dip; 38307c478bd9Sstevel@tonic-gate 38317c478bd9Sstevel@tonic-gate dip = hubd->h_children_dips[port]; 38327c478bd9Sstevel@tonic-gate 38337c478bd9Sstevel@tonic-gate /* 38347c478bd9Sstevel@tonic-gate * Don't raise power on detaching child 38357c478bd9Sstevel@tonic-gate */ 38367c478bd9Sstevel@tonic-gate if (!DEVI_IS_DETACHING(dip)) { 38377c478bd9Sstevel@tonic-gate /* 38387c478bd9Sstevel@tonic-gate * As this child is not 38397c478bd9Sstevel@tonic-gate * detaching, we set this 38407c478bd9Sstevel@tonic-gate * flag, causing bus_ctls 38417c478bd9Sstevel@tonic-gate * to stall detach till 38427c478bd9Sstevel@tonic-gate * pm_raise_power returns 38437c478bd9Sstevel@tonic-gate * and flag it for a deferred 38447c478bd9Sstevel@tonic-gate * raise_power. 38457c478bd9Sstevel@tonic-gate * 38467c478bd9Sstevel@tonic-gate * pm_raise_power is deferred 38477c478bd9Sstevel@tonic-gate * because we need to release 38487c478bd9Sstevel@tonic-gate * the locks first. 38497c478bd9Sstevel@tonic-gate */ 38507c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= 3851c0f24e5bSlg HUBD_CHILD_RAISE_POWER; 38527c478bd9Sstevel@tonic-gate pwrup_child = B_TRUE; 38537c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 38547c478bd9Sstevel@tonic-gate 38557c478bd9Sstevel@tonic-gate /* 38567c478bd9Sstevel@tonic-gate * make sure that child 38577c478bd9Sstevel@tonic-gate * doesn't disappear 38587c478bd9Sstevel@tonic-gate */ 38597c478bd9Sstevel@tonic-gate ndi_hold_devi(dip); 38607c478bd9Sstevel@tonic-gate 38617c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 38627c478bd9Sstevel@tonic-gate } 38637c478bd9Sstevel@tonic-gate } 38647c478bd9Sstevel@tonic-gate } 3865fef1e07eSsl 3866fef1e07eSsl /* 3867fef1e07eSsl * Check if the port is over-current 3868fef1e07eSsl */ 3869fef1e07eSsl if (change & PORT_CHANGE_OCIC) { 3870fef1e07eSsl USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, 3871fef1e07eSsl hubd->h_log_handle, 3872fef1e07eSsl "Port%d in over current condition, " 3873fef1e07eSsl "please check the attached device to " 3874fef1e07eSsl "clear the condition. The system will " 3875fef1e07eSsl "try to recover the port, but if not " 3876fef1e07eSsl "successful, you need to re-connect " 3877fef1e07eSsl "the hub or reboot the system to bring " 3878fef1e07eSsl "the port back to work", port); 3879fef1e07eSsl 3880fef1e07eSsl if (!(status & PORT_STATUS_PPS)) { 3881fef1e07eSsl /* 3882fef1e07eSsl * Try to enable port power, but 3883fef1e07eSsl * possibly fail. Ignore failure 3884fef1e07eSsl */ 3885fef1e07eSsl (void) hubd_enable_port_power(hubd, 3886fef1e07eSsl port); 3887fef1e07eSsl 3888fef1e07eSsl /* 3889fef1e07eSsl * Delay some time to avoid 3890fef1e07eSsl * over-current event to happen 3891fef1e07eSsl * too frequently in some cases 3892fef1e07eSsl */ 3893fef1e07eSsl mutex_exit(HUBD_MUTEX(hubd)); 3894fef1e07eSsl delay(drv_usectohz(500000)); 3895fef1e07eSsl mutex_enter(HUBD_MUTEX(hubd)); 3896fef1e07eSsl } 3897fef1e07eSsl } 38987c478bd9Sstevel@tonic-gate } 38997c478bd9Sstevel@tonic-gate } 39007c478bd9Sstevel@tonic-gate 39017c478bd9Sstevel@tonic-gate /* release locks so we can do a devfs_clean */ 39027c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 39037c478bd9Sstevel@tonic-gate 39047c478bd9Sstevel@tonic-gate /* delete cached dv_node's but drop locks first */ 39057c478bd9Sstevel@tonic-gate ndi_devi_exit(hdip, circ); 39067c478bd9Sstevel@tonic-gate ndi_devi_exit(rh_dip, rh_circ); 39077c478bd9Sstevel@tonic-gate ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ); 39087c478bd9Sstevel@tonic-gate 39097c478bd9Sstevel@tonic-gate (void) devfs_clean(rh_dip, NULL, 0); 39107c478bd9Sstevel@tonic-gate 39117c478bd9Sstevel@tonic-gate /* now check if any children need onlining */ 39127c478bd9Sstevel@tonic-gate if (online_child) { 39137c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 39147c478bd9Sstevel@tonic-gate "hubd_hotplug_thread: onlining children"); 39157c478bd9Sstevel@tonic-gate 3916d724deadSguoqing zhu - Sun Microsystems - Beijing China /* 3917d724deadSguoqing zhu - Sun Microsystems - Beijing China * When mountroot thread is doing BUS_CONFIG_ONE, 3918d724deadSguoqing zhu - Sun Microsystems - Beijing China * don't attach driver on irrelevant nodes, just 3919d724deadSguoqing zhu - Sun Microsystems - Beijing China * configure them to initialized status. Devfs 3920d724deadSguoqing zhu - Sun Microsystems - Beijing China * will induce the attach later. 3921d724deadSguoqing zhu - Sun Microsystems - Beijing China */ 3922d724deadSguoqing zhu - Sun Microsystems - Beijing China if (modrootloaded) { 3923d724deadSguoqing zhu - Sun Microsystems - Beijing China (void) ndi_devi_online(hubd->h_dip, 0); 3924d724deadSguoqing zhu - Sun Microsystems - Beijing China } else { 3925d724deadSguoqing zhu - Sun Microsystems - Beijing China for (port = 1; port <= nports; port++) { 39265321cfb7Spengcheng chen - Sun Microsystems - Beijing China dev_info_t *dip; 39275321cfb7Spengcheng chen - Sun Microsystems - Beijing China 39285321cfb7Spengcheng chen - Sun Microsystems - Beijing China mutex_enter(HUBD_MUTEX(hubd)); 39295321cfb7Spengcheng chen - Sun Microsystems - Beijing China dip = hubd->h_children_dips[port]; 39305321cfb7Spengcheng chen - Sun Microsystems - Beijing China mutex_exit(HUBD_MUTEX(hubd)); 3931d724deadSguoqing zhu - Sun Microsystems - Beijing China if (dip) { 3932d724deadSguoqing zhu - Sun Microsystems - Beijing China int circ, rv; 3933d724deadSguoqing zhu - Sun Microsystems - Beijing China dev_info_t *pdip = ddi_get_parent(dip); 3934d724deadSguoqing zhu - Sun Microsystems - Beijing China ndi_devi_enter(pdip, &circ); 3935d724deadSguoqing zhu - Sun Microsystems - Beijing China rv = i_ndi_config_node(dip, 3936d724deadSguoqing zhu - Sun Microsystems - Beijing China DS_INITIALIZED, 0); 3937d724deadSguoqing zhu - Sun Microsystems - Beijing China 3938d724deadSguoqing zhu - Sun Microsystems - Beijing China if (rv != NDI_SUCCESS) { 3939d724deadSguoqing zhu - Sun Microsystems - Beijing China USB_DPRINTF_L0( 3940d724deadSguoqing zhu - Sun Microsystems - Beijing China DPRINT_MASK_HOTPLUG, 3941d724deadSguoqing zhu - Sun Microsystems - Beijing China hubd->h_log_handle, 3942d724deadSguoqing zhu - Sun Microsystems - Beijing China "hubd_hotplug_thread:" 3943d724deadSguoqing zhu - Sun Microsystems - Beijing China "init node %s@%s failed", 3944d724deadSguoqing zhu - Sun Microsystems - Beijing China DEVI(dip)->devi_node_name, 3945d724deadSguoqing zhu - Sun Microsystems - Beijing China DEVI(dip)->devi_addr); 3946d724deadSguoqing zhu - Sun Microsystems - Beijing China } 3947d724deadSguoqing zhu - Sun Microsystems - Beijing China ndi_devi_exit(pdip, circ); 3948d724deadSguoqing zhu - Sun Microsystems - Beijing China } 3949d724deadSguoqing zhu - Sun Microsystems - Beijing China } 3950d724deadSguoqing zhu - Sun Microsystems - Beijing China } 39517c478bd9Sstevel@tonic-gate } 39527c478bd9Sstevel@tonic-gate 39537c478bd9Sstevel@tonic-gate /* now check if any disconnected devices need to be cleaned up */ 39547c478bd9Sstevel@tonic-gate if (offline_child) { 39557c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 39567c478bd9Sstevel@tonic-gate "hubd_hotplug_thread: scheduling cleanup"); 39577c478bd9Sstevel@tonic-gate 39587c478bd9Sstevel@tonic-gate hubd_schedule_cleanup(hubd->h_usba_device->usb_root_hub_dip); 39597c478bd9Sstevel@tonic-gate } 39607c478bd9Sstevel@tonic-gate 39617c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 39627c478bd9Sstevel@tonic-gate 39637c478bd9Sstevel@tonic-gate /* now raise power on the children that have woken up */ 39647c478bd9Sstevel@tonic-gate if (pwrup_child) { 39657c478bd9Sstevel@tonic-gate old_state = hubd->h_dev_state; 39667c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_HUB_CHILD_PWRLVL; 39677c478bd9Sstevel@tonic-gate for (port = 1; port <= nports; port++) { 39687c478bd9Sstevel@tonic-gate if (hubd->h_port_state[port] & HUBD_CHILD_RAISE_POWER) { 39697c478bd9Sstevel@tonic-gate dev_info_t *dip = hubd->h_children_dips[port]; 39707c478bd9Sstevel@tonic-gate 39717c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 39727c478bd9Sstevel@tonic-gate 39737c478bd9Sstevel@tonic-gate /* Get the device to full power */ 39747c478bd9Sstevel@tonic-gate (void) pm_busy_component(dip, 0); 39757c478bd9Sstevel@tonic-gate (void) pm_raise_power(dip, 0, 39767c478bd9Sstevel@tonic-gate USB_DEV_OS_FULL_PWR); 39777c478bd9Sstevel@tonic-gate (void) pm_idle_component(dip, 0); 39787c478bd9Sstevel@tonic-gate 39797c478bd9Sstevel@tonic-gate /* release the hold on the child */ 39807c478bd9Sstevel@tonic-gate ndi_rele_devi(dip); 39817c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 39827c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= 39837c478bd9Sstevel@tonic-gate ~HUBD_CHILD_RAISE_POWER; 39847c478bd9Sstevel@tonic-gate } 39857c478bd9Sstevel@tonic-gate } 39867c478bd9Sstevel@tonic-gate /* 39877c478bd9Sstevel@tonic-gate * make sure that we don't accidentally 39887c478bd9Sstevel@tonic-gate * over write the disconnect state 39897c478bd9Sstevel@tonic-gate */ 39907c478bd9Sstevel@tonic-gate if (hubd->h_dev_state == USB_DEV_HUB_CHILD_PWRLVL) { 39917c478bd9Sstevel@tonic-gate hubd->h_dev_state = old_state; 39927c478bd9Sstevel@tonic-gate } 39937c478bd9Sstevel@tonic-gate } 39947c478bd9Sstevel@tonic-gate 39957c478bd9Sstevel@tonic-gate /* 39967c478bd9Sstevel@tonic-gate * start polling can immediately kick off read callback 39977c478bd9Sstevel@tonic-gate * we need to set the h_hotplug_thread to 0 so that 39987c478bd9Sstevel@tonic-gate * the callback is not dropped 3999ffcd51f3Slg * 4000ffcd51f3Slg * if there is device during reset, still stop polling to avoid the 4001ffcd51f3Slg * read callback interrupting the reset, the polling will be started 4002ffcd51f3Slg * in hubd_reset_thread. 40037c478bd9Sstevel@tonic-gate */ 4004ffcd51f3Slg for (port = 1; port <= MAX_PORTS; port++) { 4005ffcd51f3Slg if (hubd->h_reset_port[port]) { 4006ffcd51f3Slg 4007ffcd51f3Slg break; 4008ffcd51f3Slg } 4009ffcd51f3Slg } 4010ffcd51f3Slg if (port > MAX_PORTS) { 4011ffcd51f3Slg hubd_start_polling(hubd, HUBD_ALWAYS_START_POLLING); 4012ffcd51f3Slg } 40137c478bd9Sstevel@tonic-gate 40147c478bd9Sstevel@tonic-gate /* 40157c478bd9Sstevel@tonic-gate * Earlier we would set the h_hotplug_thread = 0 before 40167c478bd9Sstevel@tonic-gate * polling was restarted so that 40177c478bd9Sstevel@tonic-gate * if there is any root hub status change interrupt, we can still kick 40187c478bd9Sstevel@tonic-gate * off the hotplug thread. This was valid when this interrupt was 40197c478bd9Sstevel@tonic-gate * delivered in hardware, and only ONE interrupt would be delivered. 40207c478bd9Sstevel@tonic-gate * Now that we poll on the root hub looking for status change in 40217c478bd9Sstevel@tonic-gate * software, this assignment is no longer required. 40227c478bd9Sstevel@tonic-gate */ 40237c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread--; 40247c478bd9Sstevel@tonic-gate 40257c478bd9Sstevel@tonic-gate /* mark this device as idle */ 40267c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0); 40277c478bd9Sstevel@tonic-gate 4028ffcd51f3Slg cv_broadcast(&hubd->h_cv_hotplug_dev); 4029ffcd51f3Slg 40307c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 40317c478bd9Sstevel@tonic-gate "hubd_hotplug_thread: exit"); 40327c478bd9Sstevel@tonic-gate 40337c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 40347c478bd9Sstevel@tonic-gate 40357c478bd9Sstevel@tonic-gate ndi_rele_devi(hdip); 40367c478bd9Sstevel@tonic-gate } 40377c478bd9Sstevel@tonic-gate 40387c478bd9Sstevel@tonic-gate 40397c478bd9Sstevel@tonic-gate /* 40407c478bd9Sstevel@tonic-gate * hubd_handle_port_connect: 40417c478bd9Sstevel@tonic-gate * Transition a port from Disabled to Enabled. Ensure that the 40427c478bd9Sstevel@tonic-gate * port is in the correct state before attempting to 40437c478bd9Sstevel@tonic-gate * access the device. 40447c478bd9Sstevel@tonic-gate */ 40457c478bd9Sstevel@tonic-gate static int 40467c478bd9Sstevel@tonic-gate hubd_handle_port_connect(hubd_t *hubd, usb_port_t port) 40477c478bd9Sstevel@tonic-gate { 40487c478bd9Sstevel@tonic-gate int rval; 40497c478bd9Sstevel@tonic-gate int retry; 40507c478bd9Sstevel@tonic-gate long time_delay; 40517c478bd9Sstevel@tonic-gate long settling_time; 40527c478bd9Sstevel@tonic-gate uint16_t status; 40537c478bd9Sstevel@tonic-gate uint16_t change; 40547c478bd9Sstevel@tonic-gate usb_addr_t hubd_usb_addr; 40557c478bd9Sstevel@tonic-gate usba_device_t *usba_device; 40567c478bd9Sstevel@tonic-gate usb_port_status_t port_status = 0; 40577c478bd9Sstevel@tonic-gate usb_port_status_t hub_port_status = 0; 40587c478bd9Sstevel@tonic-gate 40597c478bd9Sstevel@tonic-gate /* Get the hub address and port status */ 40607c478bd9Sstevel@tonic-gate usba_device = hubd->h_usba_device; 40617c478bd9Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex); 40627c478bd9Sstevel@tonic-gate hubd_usb_addr = usba_device->usb_addr; 40637c478bd9Sstevel@tonic-gate hub_port_status = usba_device->usb_port_status; 40647c478bd9Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex); 40657c478bd9Sstevel@tonic-gate 40667c478bd9Sstevel@tonic-gate /* 40677c478bd9Sstevel@tonic-gate * If a device is connected, transition the 40687c478bd9Sstevel@tonic-gate * port from Disabled to the Enabled state. 40697c478bd9Sstevel@tonic-gate * The device will receive downstream packets 40707c478bd9Sstevel@tonic-gate * in the Enabled state. 40717c478bd9Sstevel@tonic-gate * 40727c478bd9Sstevel@tonic-gate * reset port and wait for the hub to report 40737c478bd9Sstevel@tonic-gate * completion 40747c478bd9Sstevel@tonic-gate */ 40757c478bd9Sstevel@tonic-gate change = status = 0; 40767c478bd9Sstevel@tonic-gate 40777c478bd9Sstevel@tonic-gate /* 40787c478bd9Sstevel@tonic-gate * According to section 9.1.2 of USB 2.0 spec, the host should 40797c478bd9Sstevel@tonic-gate * wait for atleast 100ms to allow completion of an insertion 40807c478bd9Sstevel@tonic-gate * process and for power at the device to become stable. 40817c478bd9Sstevel@tonic-gate * We wait for 200 ms 40827c478bd9Sstevel@tonic-gate */ 40837c478bd9Sstevel@tonic-gate settling_time = drv_usectohz(hubd_device_delay / 5); 40847c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 40857c478bd9Sstevel@tonic-gate delay(settling_time); 40867c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 40877c478bd9Sstevel@tonic-gate 40887c478bd9Sstevel@tonic-gate /* calculate 600 ms delay time */ 40897c478bd9Sstevel@tonic-gate time_delay = (6 * drv_usectohz(hubd_device_delay)) / 10; 40907c478bd9Sstevel@tonic-gate 40917c478bd9Sstevel@tonic-gate for (retry = 0; (hubd->h_dev_state == USB_DEV_ONLINE) && 40927c478bd9Sstevel@tonic-gate (retry < hubd_retry_enumerate); retry++) { 40937c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 40947c478bd9Sstevel@tonic-gate "resetting port%d, retry=%d", port, retry); 40957c478bd9Sstevel@tonic-gate 40967c478bd9Sstevel@tonic-gate if ((rval = hubd_reset_port(hubd, port)) != USB_SUCCESS) { 40977c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, 40987c478bd9Sstevel@tonic-gate port, &status, &change, 0); 40997c478bd9Sstevel@tonic-gate 41007c478bd9Sstevel@tonic-gate /* continue only if port is still connected */ 41017c478bd9Sstevel@tonic-gate if (status & PORT_STATUS_CCS) { 41027c478bd9Sstevel@tonic-gate continue; 41037c478bd9Sstevel@tonic-gate } 41047c478bd9Sstevel@tonic-gate 41057c478bd9Sstevel@tonic-gate /* carry on regardless */ 41067c478bd9Sstevel@tonic-gate } 41077c478bd9Sstevel@tonic-gate 41087c478bd9Sstevel@tonic-gate /* 41097c478bd9Sstevel@tonic-gate * according to USB 2.0 spec section 11.24.2.7.1.2 41107c478bd9Sstevel@tonic-gate * at the end of port reset, the hub enables the port. 41117c478bd9Sstevel@tonic-gate * But for some strange reasons, uhci port remains disabled. 41127c478bd9Sstevel@tonic-gate * And because the port remains disabled for the settling 41137c478bd9Sstevel@tonic-gate * time below, the device connected to the port gets wedged 41147c478bd9Sstevel@tonic-gate * - fails to enumerate (device not responding) 41157c478bd9Sstevel@tonic-gate * Hence, we enable it here immediately and later again after 41167c478bd9Sstevel@tonic-gate * the delay 41177c478bd9Sstevel@tonic-gate */ 41187c478bd9Sstevel@tonic-gate (void) hubd_enable_port(hubd, port); 41197c478bd9Sstevel@tonic-gate 41207c478bd9Sstevel@tonic-gate /* we skip this delay in the first iteration */ 41217c478bd9Sstevel@tonic-gate if (retry) { 41227c478bd9Sstevel@tonic-gate /* 41237c478bd9Sstevel@tonic-gate * delay for device to signal disconnect/connect so 41247c478bd9Sstevel@tonic-gate * that hub properly recognizes the speed of the device 41257c478bd9Sstevel@tonic-gate */ 41267c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 41277c478bd9Sstevel@tonic-gate delay(settling_time); 41287c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 41297c478bd9Sstevel@tonic-gate 41307c478bd9Sstevel@tonic-gate /* 41317c478bd9Sstevel@tonic-gate * When a low speed device is connected to any port of 41327c478bd9Sstevel@tonic-gate * PPX it has to be explicitly enabled 41337c478bd9Sstevel@tonic-gate * Also, if device intentionally signals 41347c478bd9Sstevel@tonic-gate * disconnect/connect, it will disable the port. 41357c478bd9Sstevel@tonic-gate * So enable it again. 41367c478bd9Sstevel@tonic-gate */ 41377c478bd9Sstevel@tonic-gate (void) hubd_enable_port(hubd, port); 41387c478bd9Sstevel@tonic-gate } 41397c478bd9Sstevel@tonic-gate 41407c478bd9Sstevel@tonic-gate if ((rval = hubd_determine_port_status(hubd, port, &status, 41417c478bd9Sstevel@tonic-gate &change, 0)) != USB_SUCCESS) { 41427c478bd9Sstevel@tonic-gate 4143d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 41447c478bd9Sstevel@tonic-gate "getting status failed (%d)", rval); 41457c478bd9Sstevel@tonic-gate 41467c478bd9Sstevel@tonic-gate (void) hubd_disable_port(hubd, port); 41477c478bd9Sstevel@tonic-gate 41487c478bd9Sstevel@tonic-gate continue; 41497c478bd9Sstevel@tonic-gate } 41507c478bd9Sstevel@tonic-gate 41517c478bd9Sstevel@tonic-gate if (status & PORT_STATUS_POCI) { 4152d291d9f2Sfrits USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 41537c478bd9Sstevel@tonic-gate "port %d overcurrent", port); 41547c478bd9Sstevel@tonic-gate 41557c478bd9Sstevel@tonic-gate (void) hubd_disable_port(hubd, port); 41567c478bd9Sstevel@tonic-gate 41577c478bd9Sstevel@tonic-gate /* ack changes */ 41587c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, 41597c478bd9Sstevel@tonic-gate port, &status, &change, PORT_CHANGE_OCIC); 41607c478bd9Sstevel@tonic-gate 41617c478bd9Sstevel@tonic-gate continue; 41627c478bd9Sstevel@tonic-gate } 41637c478bd9Sstevel@tonic-gate 41647c478bd9Sstevel@tonic-gate /* is status really OK? */ 41657c478bd9Sstevel@tonic-gate if ((status & PORT_STATUS_OK) != PORT_STATUS_OK) { 41667c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 41677c478bd9Sstevel@tonic-gate "port %d status (0x%x) not OK on retry %d", 41687c478bd9Sstevel@tonic-gate port, status, retry); 41697c478bd9Sstevel@tonic-gate 41707c478bd9Sstevel@tonic-gate /* check if we still have the connection */ 41717c478bd9Sstevel@tonic-gate if (!(status & PORT_STATUS_CCS)) { 41727c478bd9Sstevel@tonic-gate /* lost connection, set exit condition */ 41737c478bd9Sstevel@tonic-gate retry = hubd_retry_enumerate; 41747c478bd9Sstevel@tonic-gate 41757c478bd9Sstevel@tonic-gate break; 41767c478bd9Sstevel@tonic-gate } 41777c478bd9Sstevel@tonic-gate } else { 41787c478bd9Sstevel@tonic-gate /* 41797c478bd9Sstevel@tonic-gate * Determine if the device is high or full 41807c478bd9Sstevel@tonic-gate * or low speed. 41817c478bd9Sstevel@tonic-gate */ 41827c478bd9Sstevel@tonic-gate if (status & PORT_STATUS_LSDA) { 41837c478bd9Sstevel@tonic-gate port_status = USBA_LOW_SPEED_DEV; 41847c478bd9Sstevel@tonic-gate } else if (status & PORT_STATUS_HSDA) { 41857c478bd9Sstevel@tonic-gate port_status = USBA_HIGH_SPEED_DEV; 41867c478bd9Sstevel@tonic-gate } else { 41877c478bd9Sstevel@tonic-gate port_status = USBA_FULL_SPEED_DEV; 41887c478bd9Sstevel@tonic-gate } 41897c478bd9Sstevel@tonic-gate 41907c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 41917c478bd9Sstevel@tonic-gate "creating child port%d, status=0x%x " 41927c478bd9Sstevel@tonic-gate "port status=0x%x", 41937c478bd9Sstevel@tonic-gate port, status, port_status); 41947c478bd9Sstevel@tonic-gate 41957c478bd9Sstevel@tonic-gate /* 41967c478bd9Sstevel@tonic-gate * if the child already exists, set addrs and config 41977c478bd9Sstevel@tonic-gate * to the device post connect event to the child 41987c478bd9Sstevel@tonic-gate */ 41997c478bd9Sstevel@tonic-gate if (hubd->h_children_dips[port]) { 42007c478bd9Sstevel@tonic-gate /* set addrs to this device */ 42017c478bd9Sstevel@tonic-gate rval = hubd_setdevaddr(hubd, port); 42027c478bd9Sstevel@tonic-gate 42037c478bd9Sstevel@tonic-gate /* 42047c478bd9Sstevel@tonic-gate * This delay is important for the CATC hub 42057c478bd9Sstevel@tonic-gate * to enumerate. But, avoid delay in the first 42067c478bd9Sstevel@tonic-gate * iteration 42077c478bd9Sstevel@tonic-gate */ 42087c478bd9Sstevel@tonic-gate if (retry) { 42097c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 42107c478bd9Sstevel@tonic-gate delay(drv_usectohz( 42117c478bd9Sstevel@tonic-gate hubd_device_delay/100)); 42127c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 42137c478bd9Sstevel@tonic-gate } 42147c478bd9Sstevel@tonic-gate 42157c478bd9Sstevel@tonic-gate if (rval == USB_SUCCESS) { 4216ffcd51f3Slg /* 4217ffcd51f3Slg * if the port is resetting, check if 4218ffcd51f3Slg * device's descriptors have changed. 4219ffcd51f3Slg */ 4220ffcd51f3Slg if ((hubd->h_reset_port[port]) && 4221ffcd51f3Slg (hubd_check_same_device(hubd, 4222ffcd51f3Slg port) != USB_SUCCESS)) { 4223ffcd51f3Slg retry = hubd_retry_enumerate; 4224ffcd51f3Slg 4225ffcd51f3Slg break; 4226ffcd51f3Slg } 4227ffcd51f3Slg 42287c478bd9Sstevel@tonic-gate /* 42297c478bd9Sstevel@tonic-gate * set the default config for 42307c478bd9Sstevel@tonic-gate * this device 42317c478bd9Sstevel@tonic-gate */ 42327c478bd9Sstevel@tonic-gate hubd_setdevconfig(hubd, port); 42337c478bd9Sstevel@tonic-gate 4234ffcd51f3Slg /* 4235ffcd51f3Slg * if we are doing Default reset, do 4236ffcd51f3Slg * not post reconnect event since we 4237ffcd51f3Slg * don't know where reset function is 4238ffcd51f3Slg * called. 4239ffcd51f3Slg */ 4240ffcd51f3Slg if (hubd->h_reset_port[port]) { 4241ffcd51f3Slg 4242ffcd51f3Slg return (USB_SUCCESS); 4243ffcd51f3Slg } 4244ffcd51f3Slg 42457c478bd9Sstevel@tonic-gate /* 42467c478bd9Sstevel@tonic-gate * indicate to the child that 42477c478bd9Sstevel@tonic-gate * it is online again 42487c478bd9Sstevel@tonic-gate */ 42497c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 42507c478bd9Sstevel@tonic-gate hubd_post_event(hubd, port, 42517c478bd9Sstevel@tonic-gate USBA_EVENT_TAG_HOT_INSERTION); 42527c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 42537c478bd9Sstevel@tonic-gate 42547c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 42557c478bd9Sstevel@tonic-gate } 42567c478bd9Sstevel@tonic-gate } else { 42577c478bd9Sstevel@tonic-gate /* 42587c478bd9Sstevel@tonic-gate * We need to release access here 42597c478bd9Sstevel@tonic-gate * so that busctls on other ports can 42607c478bd9Sstevel@tonic-gate * continue and don't cause a deadlock 42617c478bd9Sstevel@tonic-gate * when busctl and removal of prom node 42627c478bd9Sstevel@tonic-gate * takes concurrently. This also ensures 42637c478bd9Sstevel@tonic-gate * busctls for attach of successfully 42647c478bd9Sstevel@tonic-gate * enumerated devices on other ports can 42657c478bd9Sstevel@tonic-gate * continue concurrently with the process 42667c478bd9Sstevel@tonic-gate * of enumerating the new devices. This 42677c478bd9Sstevel@tonic-gate * reduces the overall boot time of the system. 42687c478bd9Sstevel@tonic-gate */ 42697c478bd9Sstevel@tonic-gate rval = hubd_create_child(hubd->h_dip, 4270c0f24e5bSlg hubd, 4271c0f24e5bSlg hubd->h_usba_device, 4272c0f24e5bSlg port_status, port, 4273c0f24e5bSlg retry); 42747c478bd9Sstevel@tonic-gate if (rval == USB_SUCCESS) { 42757c478bd9Sstevel@tonic-gate usba_update_hotplug_stats(hubd->h_dip, 42767c478bd9Sstevel@tonic-gate USBA_TOTAL_HOTPLUG_SUCCESS| 42777c478bd9Sstevel@tonic-gate USBA_HOTPLUG_SUCCESS); 42787c478bd9Sstevel@tonic-gate hubd->h_total_hotplug_success++; 42797c478bd9Sstevel@tonic-gate 42807c478bd9Sstevel@tonic-gate if (retry > 0) { 4281d291d9f2Sfrits USB_DPRINTF_L2( 42827c478bd9Sstevel@tonic-gate DPRINT_MASK_HOTPLUG, 42837c478bd9Sstevel@tonic-gate hubd->h_log_handle, 42847c478bd9Sstevel@tonic-gate "device on port %d " 42857c478bd9Sstevel@tonic-gate "enumerated after %d %s", 42867c478bd9Sstevel@tonic-gate port, retry, 42877c478bd9Sstevel@tonic-gate (retry > 1) ? "retries" : 42887c478bd9Sstevel@tonic-gate "retry"); 42897c478bd9Sstevel@tonic-gate 42907c478bd9Sstevel@tonic-gate } 42917c478bd9Sstevel@tonic-gate 42927c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 42937c478bd9Sstevel@tonic-gate } 42947c478bd9Sstevel@tonic-gate } 42957c478bd9Sstevel@tonic-gate } 42967c478bd9Sstevel@tonic-gate 42977c478bd9Sstevel@tonic-gate /* wait a while until it settles? */ 42987c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 42997c478bd9Sstevel@tonic-gate "disabling port %d again", port); 43007c478bd9Sstevel@tonic-gate 43017c478bd9Sstevel@tonic-gate (void) hubd_disable_port(hubd, port); 43027c478bd9Sstevel@tonic-gate if (retry) { 43037c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 43047c478bd9Sstevel@tonic-gate delay(time_delay); 43057c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 43067c478bd9Sstevel@tonic-gate } 43077c478bd9Sstevel@tonic-gate 43087c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 43097c478bd9Sstevel@tonic-gate "retrying on port %d", port); 43107c478bd9Sstevel@tonic-gate } 43117c478bd9Sstevel@tonic-gate 43127c478bd9Sstevel@tonic-gate if (retry >= hubd_retry_enumerate) { 43137c478bd9Sstevel@tonic-gate /* 43147c478bd9Sstevel@tonic-gate * If it is a High Speed Root Hub and connected device 43157c478bd9Sstevel@tonic-gate * Is a Low/Full Speed, it will be handled by USB 1.1 43167c478bd9Sstevel@tonic-gate * Host Controller. In this case, USB 2.0 Host Controller 43177c478bd9Sstevel@tonic-gate * will transfer the ownership of this port to USB 1.1 43187c478bd9Sstevel@tonic-gate * Host Controller. So don't display any error message on 43197c478bd9Sstevel@tonic-gate * the console. 43207c478bd9Sstevel@tonic-gate */ 43217c478bd9Sstevel@tonic-gate if ((hubd_usb_addr == ROOT_HUB_ADDR) && 43227c478bd9Sstevel@tonic-gate (hub_port_status == USBA_HIGH_SPEED_DEV) && 43237c478bd9Sstevel@tonic-gate (port_status != USBA_HIGH_SPEED_DEV)) { 43247c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 43257c478bd9Sstevel@tonic-gate hubd->h_log_handle, 43267c478bd9Sstevel@tonic-gate "hubd_handle_port_connect: Low/Full speed " 43277c478bd9Sstevel@tonic-gate "device is connected to High Speed root hub"); 43287c478bd9Sstevel@tonic-gate } else { 43297c478bd9Sstevel@tonic-gate USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, 43307c478bd9Sstevel@tonic-gate hubd->h_log_handle, 43317c478bd9Sstevel@tonic-gate "Connecting device on port %d failed", port); 43327c478bd9Sstevel@tonic-gate } 43337c478bd9Sstevel@tonic-gate 43347c478bd9Sstevel@tonic-gate (void) hubd_disable_port(hubd, port); 43357c478bd9Sstevel@tonic-gate usba_update_hotplug_stats(hubd->h_dip, 43367c478bd9Sstevel@tonic-gate USBA_TOTAL_HOTPLUG_FAILURE|USBA_HOTPLUG_FAILURE); 43377c478bd9Sstevel@tonic-gate hubd->h_total_hotplug_failure++; 43387c478bd9Sstevel@tonic-gate 43397c478bd9Sstevel@tonic-gate /* 43407c478bd9Sstevel@tonic-gate * the port should be automagically 43417c478bd9Sstevel@tonic-gate * disabled but just in case, we do 43427c478bd9Sstevel@tonic-gate * it here 43437c478bd9Sstevel@tonic-gate */ 43447c478bd9Sstevel@tonic-gate (void) hubd_disable_port(hubd, port); 43457c478bd9Sstevel@tonic-gate 43467c478bd9Sstevel@tonic-gate /* ack all changes because we disabled this port */ 43477c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, 43487c478bd9Sstevel@tonic-gate port, &status, &change, HUBD_ACK_ALL_CHANGES); 43497c478bd9Sstevel@tonic-gate 43507c478bd9Sstevel@tonic-gate } 43517c478bd9Sstevel@tonic-gate 43527c478bd9Sstevel@tonic-gate return (USB_FAILURE); 43537c478bd9Sstevel@tonic-gate } 43547c478bd9Sstevel@tonic-gate 43557c478bd9Sstevel@tonic-gate 43567c478bd9Sstevel@tonic-gate /* 43577c478bd9Sstevel@tonic-gate * hubd_get_hub_status: 43587c478bd9Sstevel@tonic-gate */ 43597c478bd9Sstevel@tonic-gate static int 43607c478bd9Sstevel@tonic-gate hubd_get_hub_status(hubd_t *hubd) 43617c478bd9Sstevel@tonic-gate { 43627c478bd9Sstevel@tonic-gate int rval; 43637c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 43647c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 436535f36846Ssl uint16_t stword[2]; 43667c478bd9Sstevel@tonic-gate uint16_t status; 43677c478bd9Sstevel@tonic-gate uint16_t change; 43687c478bd9Sstevel@tonic-gate usb_cfg_descr_t cfg_descr; 43697c478bd9Sstevel@tonic-gate size_t cfg_length; 43707c478bd9Sstevel@tonic-gate uchar_t *usb_cfg; 43717c478bd9Sstevel@tonic-gate uint8_t MaxPower; 4372fef1e07eSsl usb_hub_descr_t *hub_descr; 4373fef1e07eSsl usb_port_t port; 43747c478bd9Sstevel@tonic-gate 4375fef1e07eSsl USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 437635f36846Ssl "hubd_get_hub_status:"); 43777c478bd9Sstevel@tonic-gate 437835f36846Ssl ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 43797c478bd9Sstevel@tonic-gate 438035f36846Ssl if ((hubd_get_hub_status_words(hubd, stword)) != USB_SUCCESS) { 43817c478bd9Sstevel@tonic-gate 43827c478bd9Sstevel@tonic-gate return (USB_FAILURE); 43837c478bd9Sstevel@tonic-gate } 438435f36846Ssl status = stword[0]; 438535f36846Ssl change = stword[1]; 43867c478bd9Sstevel@tonic-gate 43877c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 43887c478bd9Sstevel@tonic-gate 43897c478bd9Sstevel@tonic-gate /* Obtain the raw configuration descriptor */ 43907c478bd9Sstevel@tonic-gate usb_cfg = usb_get_raw_cfg_data(hubd->h_dip, &cfg_length); 43917c478bd9Sstevel@tonic-gate 43927c478bd9Sstevel@tonic-gate /* get configuration descriptor */ 43937c478bd9Sstevel@tonic-gate rval = usb_parse_cfg_descr(usb_cfg, cfg_length, 4394c0f24e5bSlg &cfg_descr, USB_CFG_DESCR_SIZE); 43957c478bd9Sstevel@tonic-gate 43967c478bd9Sstevel@tonic-gate if (rval != USB_CFG_DESCR_SIZE) { 43977c478bd9Sstevel@tonic-gate 4398fef1e07eSsl USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 43997c478bd9Sstevel@tonic-gate "get hub configuration descriptor failed."); 44007c478bd9Sstevel@tonic-gate 44017c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 44027c478bd9Sstevel@tonic-gate 44037c478bd9Sstevel@tonic-gate return (USB_FAILURE); 44047c478bd9Sstevel@tonic-gate } else { 44057c478bd9Sstevel@tonic-gate MaxPower = cfg_descr.bMaxPower; 44067c478bd9Sstevel@tonic-gate } 44077c478bd9Sstevel@tonic-gate 44087c478bd9Sstevel@tonic-gate /* check if local power status changed. */ 44097c478bd9Sstevel@tonic-gate if (change & C_HUB_LOCAL_POWER_STATUS) { 44107c478bd9Sstevel@tonic-gate 44117c478bd9Sstevel@tonic-gate /* 44127c478bd9Sstevel@tonic-gate * local power has been lost, check the maximum 44137c478bd9Sstevel@tonic-gate * power consumption of current configuration. 44147c478bd9Sstevel@tonic-gate * see USB2.0 spec Table 11-12. 44157c478bd9Sstevel@tonic-gate */ 44167c478bd9Sstevel@tonic-gate if (status & HUB_LOCAL_POWER_STATUS) { 44177c478bd9Sstevel@tonic-gate 44187c478bd9Sstevel@tonic-gate if (MaxPower == 0) { 44197c478bd9Sstevel@tonic-gate 44207c478bd9Sstevel@tonic-gate /* 44217c478bd9Sstevel@tonic-gate * Self-powered only hub. Because it could 44227c478bd9Sstevel@tonic-gate * not draw any power from USB bus. 44237c478bd9Sstevel@tonic-gate * It can't work well on this condition. 44247c478bd9Sstevel@tonic-gate */ 4425fef1e07eSsl USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, 44267c478bd9Sstevel@tonic-gate hubd->h_log_handle, 44277c478bd9Sstevel@tonic-gate "local power has been lost, " 44287c478bd9Sstevel@tonic-gate "please disconnect hub"); 44297c478bd9Sstevel@tonic-gate } else { 44307c478bd9Sstevel@tonic-gate 44317c478bd9Sstevel@tonic-gate /* 44327c478bd9Sstevel@tonic-gate * Bus-powered only or self/bus-powered hub. 44337c478bd9Sstevel@tonic-gate */ 4434fef1e07eSsl USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, 44357c478bd9Sstevel@tonic-gate hubd->h_log_handle, 44367c478bd9Sstevel@tonic-gate "local power has been lost," 44377c478bd9Sstevel@tonic-gate "the hub could draw %d" 44387c478bd9Sstevel@tonic-gate " mA power from the USB bus.", 44397c478bd9Sstevel@tonic-gate 2*MaxPower); 44407c478bd9Sstevel@tonic-gate } 44417c478bd9Sstevel@tonic-gate 44427c478bd9Sstevel@tonic-gate } 44437c478bd9Sstevel@tonic-gate 4444fef1e07eSsl USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 44457c478bd9Sstevel@tonic-gate "clearing feature C_HUB_LOCAL_POWER "); 44467c478bd9Sstevel@tonic-gate 44477c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 44487c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 4449fef1e07eSsl HUB_HANDLE_HUB_FEATURE_TYPE, 44507c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 44517c478bd9Sstevel@tonic-gate CFS_C_HUB_LOCAL_POWER, 44527c478bd9Sstevel@tonic-gate 0, 44537c478bd9Sstevel@tonic-gate 0, 44547c478bd9Sstevel@tonic-gate NULL, 0, 44557c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 4456fef1e07eSsl USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 44577c478bd9Sstevel@tonic-gate hubd->h_log_handle, 44587c478bd9Sstevel@tonic-gate "clear feature C_HUB_LOCAL_POWER " 44597c478bd9Sstevel@tonic-gate "failed (%d 0x%x %d)", 44607c478bd9Sstevel@tonic-gate rval, completion_reason, cb_flags); 44617c478bd9Sstevel@tonic-gate } 44627c478bd9Sstevel@tonic-gate 44637c478bd9Sstevel@tonic-gate } 44647c478bd9Sstevel@tonic-gate 44657c478bd9Sstevel@tonic-gate if (change & C_HUB_OVER_CURRENT) { 44667c478bd9Sstevel@tonic-gate 44677c478bd9Sstevel@tonic-gate if (status & HUB_OVER_CURRENT) { 4468fef1e07eSsl 4469fef1e07eSsl if (usba_is_root_hub(hubd->h_dip)) { 4470fef1e07eSsl /* 4471fef1e07eSsl * The root hub should be automatically 4472fef1e07eSsl * recovered when over-current condition is 4473fef1e07eSsl * cleared. But there might be exception and 4474fef1e07eSsl * need user interaction to recover. 4475fef1e07eSsl */ 4476fef1e07eSsl USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, 4477fef1e07eSsl hubd->h_log_handle, 4478fef1e07eSsl "Root hub over current condition, " 4479fef1e07eSsl "please check your system to clear the " 4480fef1e07eSsl "condition as soon as possible. And you " 4481fef1e07eSsl "may need to reboot the system to bring " 4482fef1e07eSsl "the root hub back to work if it cannot " 4483fef1e07eSsl "recover automatically"); 4484fef1e07eSsl } else { 4485fef1e07eSsl /* 4486fef1e07eSsl * The driver would try to recover port power 4487fef1e07eSsl * on over current condition. When the recovery 4488fef1e07eSsl * fails, the user may still need to offline 4489fef1e07eSsl * this hub in order to recover. 4490fef1e07eSsl * The port power is automatically disabled, 4491fef1e07eSsl * so we won't see disconnects. 4492fef1e07eSsl */ 4493fef1e07eSsl USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, 4494fef1e07eSsl hubd->h_log_handle, 4495fef1e07eSsl "Hub global over current condition, " 4496fef1e07eSsl "please disconnect the devices connected " 4497fef1e07eSsl "to the hub to clear the condition. And " 4498fef1e07eSsl "you may need to re-connect the hub if " 4499fef1e07eSsl "the ports do not work"); 4500fef1e07eSsl } 45017c478bd9Sstevel@tonic-gate } 45027c478bd9Sstevel@tonic-gate 4503fef1e07eSsl USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 45047c478bd9Sstevel@tonic-gate "clearing feature C_HUB_OVER_CURRENT"); 45057c478bd9Sstevel@tonic-gate 45067c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 45077c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 4508fef1e07eSsl HUB_HANDLE_HUB_FEATURE_TYPE, 45097c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 45107c478bd9Sstevel@tonic-gate CFS_C_HUB_OVER_CURRENT, 45117c478bd9Sstevel@tonic-gate 0, 45127c478bd9Sstevel@tonic-gate 0, 45137c478bd9Sstevel@tonic-gate NULL, 0, 45147c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 4515fef1e07eSsl USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 45167c478bd9Sstevel@tonic-gate hubd->h_log_handle, 45177c478bd9Sstevel@tonic-gate "clear feature C_HUB_OVER_CURRENT " 45187c478bd9Sstevel@tonic-gate "failed (%d 0x%x %d)", 45197c478bd9Sstevel@tonic-gate rval, completion_reason, cb_flags); 45207c478bd9Sstevel@tonic-gate } 4521fef1e07eSsl 4522fef1e07eSsl /* 4523fef1e07eSsl * Try to recover all port power if they are turned off. 4524fef1e07eSsl * Don't do this for root hub, but rely on the root hub 4525fef1e07eSsl * to recover itself. 4526fef1e07eSsl */ 4527fef1e07eSsl if (!usba_is_root_hub(hubd->h_dip)) { 4528fef1e07eSsl 4529fef1e07eSsl mutex_enter(HUBD_MUTEX(hubd)); 4530fef1e07eSsl 4531fef1e07eSsl /* 4532fef1e07eSsl * Only check the power status of the 1st port 4533fef1e07eSsl * since all port power status should be the same. 4534fef1e07eSsl */ 4535fef1e07eSsl (void) hubd_determine_port_status(hubd, 1, &status, 4536fef1e07eSsl &change, 0); 4537fef1e07eSsl 4538fef1e07eSsl if (status & PORT_STATUS_PPS) { 4539fef1e07eSsl 4540fef1e07eSsl return (USB_SUCCESS); 4541fef1e07eSsl } 4542fef1e07eSsl 4543fef1e07eSsl hub_descr = &hubd->h_hub_descr; 4544fef1e07eSsl 4545fef1e07eSsl for (port = 1; port <= hub_descr->bNbrPorts; 4546fef1e07eSsl port++) { 4547fef1e07eSsl 4548fef1e07eSsl (void) hubd_enable_port_power(hubd, port); 4549fef1e07eSsl } 4550fef1e07eSsl 4551fef1e07eSsl mutex_exit(HUBD_MUTEX(hubd)); 4552fef1e07eSsl 4553fef1e07eSsl /* 4554fef1e07eSsl * Delay some time to avoid over-current event 4555fef1e07eSsl * to happen too frequently in some cases 4556fef1e07eSsl */ 4557fef1e07eSsl delay(drv_usectohz(500000)); 4558fef1e07eSsl } 45597c478bd9Sstevel@tonic-gate } 45607c478bd9Sstevel@tonic-gate 45617c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 45627c478bd9Sstevel@tonic-gate 45637c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 45647c478bd9Sstevel@tonic-gate } 45657c478bd9Sstevel@tonic-gate 45667c478bd9Sstevel@tonic-gate 45677c478bd9Sstevel@tonic-gate /* 45687c478bd9Sstevel@tonic-gate * hubd_reset_port: 45697c478bd9Sstevel@tonic-gate */ 45707c478bd9Sstevel@tonic-gate static int 45717c478bd9Sstevel@tonic-gate hubd_reset_port(hubd_t *hubd, usb_port_t port) 45727c478bd9Sstevel@tonic-gate { 45737c478bd9Sstevel@tonic-gate int rval; 45747c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 45757c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 45767c478bd9Sstevel@tonic-gate usb_port_mask_t port_mask = 1 << port; 45777c478bd9Sstevel@tonic-gate mblk_t *data; 45787c478bd9Sstevel@tonic-gate uint16_t status; 45797c478bd9Sstevel@tonic-gate uint16_t change; 45807c478bd9Sstevel@tonic-gate int i; 45817c478bd9Sstevel@tonic-gate clock_t current_time; 45827c478bd9Sstevel@tonic-gate 45837c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 45847c478bd9Sstevel@tonic-gate "hubd_reset_port: port=%d", port); 45857c478bd9Sstevel@tonic-gate 45867c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 45877c478bd9Sstevel@tonic-gate 45887c478bd9Sstevel@tonic-gate hubd->h_port_reset_wait |= port_mask; 45897c478bd9Sstevel@tonic-gate 45907c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 45917c478bd9Sstevel@tonic-gate 45927c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 45937c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 459435f36846Ssl HUB_HANDLE_PORT_FEATURE_TYPE, 45957c478bd9Sstevel@tonic-gate USB_REQ_SET_FEATURE, 45967c478bd9Sstevel@tonic-gate CFS_PORT_RESET, 45977c478bd9Sstevel@tonic-gate port, 45987c478bd9Sstevel@tonic-gate 0, 45997c478bd9Sstevel@tonic-gate NULL, 0, 46007c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 4601d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 46027c478bd9Sstevel@tonic-gate "reset port%d failed (%d 0x%x %d)", 46037c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 46047c478bd9Sstevel@tonic-gate 46057c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 46067c478bd9Sstevel@tonic-gate 46077c478bd9Sstevel@tonic-gate return (USB_FAILURE); 46087c478bd9Sstevel@tonic-gate } 46097c478bd9Sstevel@tonic-gate 46107c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 46117c478bd9Sstevel@tonic-gate 46127c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 46137c478bd9Sstevel@tonic-gate "waiting on cv for reset completion"); 46147c478bd9Sstevel@tonic-gate 46157c478bd9Sstevel@tonic-gate /* 46167c478bd9Sstevel@tonic-gate * wait for port status change event 46177c478bd9Sstevel@tonic-gate */ 46187c478bd9Sstevel@tonic-gate for (i = 0; i < hubd_retry_enumerate; i++) { 46197c478bd9Sstevel@tonic-gate /* 46207c478bd9Sstevel@tonic-gate * start polling ep1 for receiving notification on 46217c478bd9Sstevel@tonic-gate * reset completion 46227c478bd9Sstevel@tonic-gate */ 46237c478bd9Sstevel@tonic-gate hubd_start_polling(hubd, HUBD_ALWAYS_START_POLLING); 46247c478bd9Sstevel@tonic-gate 46257c478bd9Sstevel@tonic-gate /* 46267c478bd9Sstevel@tonic-gate * sleep a max of 100ms for reset completion 46277c478bd9Sstevel@tonic-gate * notification to be received 46287c478bd9Sstevel@tonic-gate */ 46297c478bd9Sstevel@tonic-gate current_time = ddi_get_lbolt(); 46307c478bd9Sstevel@tonic-gate if (hubd->h_port_reset_wait & port_mask) { 46317c478bd9Sstevel@tonic-gate rval = cv_timedwait(&hubd->h_cv_reset_port, 4632c0f24e5bSlg &hubd->h_mutex, 4633c0f24e5bSlg current_time + 4634c0f24e5bSlg drv_usectohz(hubd_device_delay / 10)); 46357c478bd9Sstevel@tonic-gate if ((rval <= 0) && 46367c478bd9Sstevel@tonic-gate (hubd->h_port_reset_wait & port_mask)) { 46377c478bd9Sstevel@tonic-gate /* we got woken up because of a timeout */ 46387c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 46397c478bd9Sstevel@tonic-gate hubd->h_log_handle, 46407c478bd9Sstevel@tonic-gate "timeout: reset port=%d failed", port); 46417c478bd9Sstevel@tonic-gate 46427c478bd9Sstevel@tonic-gate hubd->h_port_reset_wait &= ~port_mask; 46437c478bd9Sstevel@tonic-gate 46447c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd); 46457c478bd9Sstevel@tonic-gate 46467c478bd9Sstevel@tonic-gate return (USB_FAILURE); 46477c478bd9Sstevel@tonic-gate } 46487c478bd9Sstevel@tonic-gate } 46497c478bd9Sstevel@tonic-gate 46507c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 46517c478bd9Sstevel@tonic-gate "reset completion received"); 46527c478bd9Sstevel@tonic-gate 46537c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd); 46547c478bd9Sstevel@tonic-gate 46557c478bd9Sstevel@tonic-gate data = NULL; 46567c478bd9Sstevel@tonic-gate 46577c478bd9Sstevel@tonic-gate /* check status to determine whether reset completed */ 46587c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 46597c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 46607c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 466135f36846Ssl HUB_GET_PORT_STATUS_TYPE, 46627c478bd9Sstevel@tonic-gate USB_REQ_GET_STATUS, 46637c478bd9Sstevel@tonic-gate 0, 46647c478bd9Sstevel@tonic-gate port, 46657c478bd9Sstevel@tonic-gate GET_STATUS_LENGTH, 46667c478bd9Sstevel@tonic-gate &data, 0, 46677c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 4668d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_PORT, 46697c478bd9Sstevel@tonic-gate hubd->h_log_handle, 46707c478bd9Sstevel@tonic-gate "get status port%d failed (%d 0x%x %d)", 46717c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 46727c478bd9Sstevel@tonic-gate 46737c478bd9Sstevel@tonic-gate if (data) { 46747c478bd9Sstevel@tonic-gate freemsg(data); 46757c478bd9Sstevel@tonic-gate data = NULL; 46767c478bd9Sstevel@tonic-gate } 46777c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 46787c478bd9Sstevel@tonic-gate 46797c478bd9Sstevel@tonic-gate continue; 46807c478bd9Sstevel@tonic-gate } 46817c478bd9Sstevel@tonic-gate 46827c478bd9Sstevel@tonic-gate status = (*(data->b_rptr + 1) << 8) | *(data->b_rptr); 46837c478bd9Sstevel@tonic-gate change = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2); 46847c478bd9Sstevel@tonic-gate 46857c478bd9Sstevel@tonic-gate freemsg(data); 46867c478bd9Sstevel@tonic-gate 46877c478bd9Sstevel@tonic-gate /* continue only if port is still connected */ 46887c478bd9Sstevel@tonic-gate if (!(status & PORT_STATUS_CCS)) { 46897c478bd9Sstevel@tonic-gate 46907c478bd9Sstevel@tonic-gate /* lost connection, set exit condition */ 46917c478bd9Sstevel@tonic-gate i = hubd_retry_enumerate; 46927c478bd9Sstevel@tonic-gate 46937c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 46947c478bd9Sstevel@tonic-gate 46957c478bd9Sstevel@tonic-gate break; 46967c478bd9Sstevel@tonic-gate } 46977c478bd9Sstevel@tonic-gate 46987c478bd9Sstevel@tonic-gate if (status & PORT_STATUS_PRS) { 46997c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 47007c478bd9Sstevel@tonic-gate "port%d reset active", port); 47017c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 47027c478bd9Sstevel@tonic-gate 47037c478bd9Sstevel@tonic-gate continue; 47047c478bd9Sstevel@tonic-gate } else { 47057c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 47067c478bd9Sstevel@tonic-gate "port%d reset inactive", port); 47077c478bd9Sstevel@tonic-gate } 47087c478bd9Sstevel@tonic-gate 47097c478bd9Sstevel@tonic-gate if (change & PORT_CHANGE_PRSC) { 47107c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 47117c478bd9Sstevel@tonic-gate "clearing feature CFS_C_PORT_RESET"); 47127c478bd9Sstevel@tonic-gate 47137c478bd9Sstevel@tonic-gate if (usb_pipe_sync_ctrl_xfer(hubd->h_dip, 47147c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 471535f36846Ssl HUB_HANDLE_PORT_FEATURE_TYPE, 47167c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 47177c478bd9Sstevel@tonic-gate CFS_C_PORT_RESET, 47187c478bd9Sstevel@tonic-gate port, 47197c478bd9Sstevel@tonic-gate 0, 47207c478bd9Sstevel@tonic-gate NULL, 0, 47217c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0) != USB_SUCCESS) { 47227c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 47237c478bd9Sstevel@tonic-gate hubd->h_log_handle, 47247c478bd9Sstevel@tonic-gate "clear feature CFS_C_PORT_RESET" 47257c478bd9Sstevel@tonic-gate " port%d failed (%d 0x%x %d)", 47267c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 47277c478bd9Sstevel@tonic-gate } 47287c478bd9Sstevel@tonic-gate } 47297c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 47307c478bd9Sstevel@tonic-gate 47317c478bd9Sstevel@tonic-gate break; 47327c478bd9Sstevel@tonic-gate } 47337c478bd9Sstevel@tonic-gate 47347c478bd9Sstevel@tonic-gate if (i >= hubd_retry_enumerate) { 47357c478bd9Sstevel@tonic-gate /* port reset has failed */ 47367c478bd9Sstevel@tonic-gate rval = USB_FAILURE; 47377c478bd9Sstevel@tonic-gate } 47387c478bd9Sstevel@tonic-gate 47397c478bd9Sstevel@tonic-gate return (rval); 47407c478bd9Sstevel@tonic-gate } 47417c478bd9Sstevel@tonic-gate 47427c478bd9Sstevel@tonic-gate 47437c478bd9Sstevel@tonic-gate /* 47447c478bd9Sstevel@tonic-gate * hubd_enable_port: 47457c478bd9Sstevel@tonic-gate * this may fail if the hub as been disconnected 47467c478bd9Sstevel@tonic-gate */ 47477c478bd9Sstevel@tonic-gate static int 47487c478bd9Sstevel@tonic-gate hubd_enable_port(hubd_t *hubd, usb_port_t port) 47497c478bd9Sstevel@tonic-gate { 47507c478bd9Sstevel@tonic-gate int rval; 47517c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 47527c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 47537c478bd9Sstevel@tonic-gate 47547c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 47557c478bd9Sstevel@tonic-gate "hubd_enable_port: port=%d", port); 47567c478bd9Sstevel@tonic-gate 47577c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 47587c478bd9Sstevel@tonic-gate 47597c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 47607c478bd9Sstevel@tonic-gate 47617c478bd9Sstevel@tonic-gate /* Do not issue a SetFeature(PORT_ENABLE) on external hubs */ 47627c478bd9Sstevel@tonic-gate if (!usba_is_root_hub(hubd->h_dip)) { 47637c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 47647c478bd9Sstevel@tonic-gate 47657c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 47667c478bd9Sstevel@tonic-gate } 47677c478bd9Sstevel@tonic-gate 47687c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 47697c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 477035f36846Ssl HUB_HANDLE_PORT_FEATURE_TYPE, 47717c478bd9Sstevel@tonic-gate USB_REQ_SET_FEATURE, 47727c478bd9Sstevel@tonic-gate CFS_PORT_ENABLE, 47737c478bd9Sstevel@tonic-gate port, 47747c478bd9Sstevel@tonic-gate 0, 47757c478bd9Sstevel@tonic-gate NULL, 0, 47767c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 47777c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 47787c478bd9Sstevel@tonic-gate "enable port%d failed (%d 0x%x %d)", 47797c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 47807c478bd9Sstevel@tonic-gate } 47817c478bd9Sstevel@tonic-gate 47827c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 47837c478bd9Sstevel@tonic-gate 47847c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 47857c478bd9Sstevel@tonic-gate "enabling port done"); 47867c478bd9Sstevel@tonic-gate 47877c478bd9Sstevel@tonic-gate return (rval); 47887c478bd9Sstevel@tonic-gate } 47897c478bd9Sstevel@tonic-gate 47907c478bd9Sstevel@tonic-gate 47917c478bd9Sstevel@tonic-gate /* 47927c478bd9Sstevel@tonic-gate * hubd_disable_port 47937c478bd9Sstevel@tonic-gate */ 47947c478bd9Sstevel@tonic-gate static int 47957c478bd9Sstevel@tonic-gate hubd_disable_port(hubd_t *hubd, usb_port_t port) 47967c478bd9Sstevel@tonic-gate { 47977c478bd9Sstevel@tonic-gate int rval; 47987c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 47997c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 48007c478bd9Sstevel@tonic-gate 48017c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 48027c478bd9Sstevel@tonic-gate "hubd_disable_port: port=%d", port); 48037c478bd9Sstevel@tonic-gate 48047c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 48057c478bd9Sstevel@tonic-gate 48067c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 48077c478bd9Sstevel@tonic-gate 48087c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 48097c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 481035f36846Ssl HUB_HANDLE_PORT_FEATURE_TYPE, 48117c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 48127c478bd9Sstevel@tonic-gate CFS_PORT_ENABLE, 48137c478bd9Sstevel@tonic-gate port, 48147c478bd9Sstevel@tonic-gate 0, 48157c478bd9Sstevel@tonic-gate NULL, 0, 48167c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 48177c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 48187c478bd9Sstevel@tonic-gate "disable port%d failed (%d 0x%x %d)", port, 48197c478bd9Sstevel@tonic-gate completion_reason, cb_flags, rval); 48207c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 48217c478bd9Sstevel@tonic-gate 48227c478bd9Sstevel@tonic-gate return (USB_FAILURE); 48237c478bd9Sstevel@tonic-gate } 48247c478bd9Sstevel@tonic-gate 48257c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 48267c478bd9Sstevel@tonic-gate "clearing feature CFS_C_PORT_ENABLE"); 48277c478bd9Sstevel@tonic-gate 48287c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 48297c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 483035f36846Ssl HUB_HANDLE_PORT_FEATURE_TYPE, 48317c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 48327c478bd9Sstevel@tonic-gate CFS_C_PORT_ENABLE, 48337c478bd9Sstevel@tonic-gate port, 48347c478bd9Sstevel@tonic-gate 0, 48357c478bd9Sstevel@tonic-gate NULL, 0, 48367c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 48377c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 48387c478bd9Sstevel@tonic-gate hubd->h_log_handle, 48397c478bd9Sstevel@tonic-gate "clear feature CFS_C_PORT_ENABLE port%d failed " 48407c478bd9Sstevel@tonic-gate "(%d 0x%x %d)", 48417c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 48427c478bd9Sstevel@tonic-gate 48437c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 48447c478bd9Sstevel@tonic-gate 48457c478bd9Sstevel@tonic-gate return (USB_FAILURE); 48467c478bd9Sstevel@tonic-gate } 48477c478bd9Sstevel@tonic-gate 48487c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 48497c478bd9Sstevel@tonic-gate 48507c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 48517c478bd9Sstevel@tonic-gate } 48527c478bd9Sstevel@tonic-gate 48537c478bd9Sstevel@tonic-gate 48547c478bd9Sstevel@tonic-gate /* 48557c478bd9Sstevel@tonic-gate * hubd_determine_port_status: 48567c478bd9Sstevel@tonic-gate */ 48577c478bd9Sstevel@tonic-gate static int 48587c478bd9Sstevel@tonic-gate hubd_determine_port_status(hubd_t *hubd, usb_port_t port, 48597c478bd9Sstevel@tonic-gate uint16_t *status, uint16_t *change, uint_t ack_flag) 48607c478bd9Sstevel@tonic-gate { 48617c478bd9Sstevel@tonic-gate int rval; 48627c478bd9Sstevel@tonic-gate mblk_t *data = NULL; 48637c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 48647c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 48657c478bd9Sstevel@tonic-gate 48667c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 48677c478bd9Sstevel@tonic-gate "hubd_determine_port_status: port=%d, state=0x%x ack=0x%x", port, 48687c478bd9Sstevel@tonic-gate hubd->h_port_state[port], ack_flag); 48697c478bd9Sstevel@tonic-gate 48707c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 48717c478bd9Sstevel@tonic-gate 48727c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 48737c478bd9Sstevel@tonic-gate 48747c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 48757c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 487635f36846Ssl HUB_GET_PORT_STATUS_TYPE, 48777c478bd9Sstevel@tonic-gate USB_REQ_GET_STATUS, 48787c478bd9Sstevel@tonic-gate 0, 48797c478bd9Sstevel@tonic-gate port, 48807c478bd9Sstevel@tonic-gate GET_STATUS_LENGTH, 48817c478bd9Sstevel@tonic-gate &data, 0, 48827c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 48837c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 48847c478bd9Sstevel@tonic-gate "port=%d get status failed (%d 0x%x %d)", 48857c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 48867c478bd9Sstevel@tonic-gate 48877c478bd9Sstevel@tonic-gate if (data) { 48887c478bd9Sstevel@tonic-gate freemsg(data); 48897c478bd9Sstevel@tonic-gate } 48907c478bd9Sstevel@tonic-gate 48917c478bd9Sstevel@tonic-gate *status = *change = 0; 48927c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 48937c478bd9Sstevel@tonic-gate 48947c478bd9Sstevel@tonic-gate return (rval); 48957c478bd9Sstevel@tonic-gate } 48967c478bd9Sstevel@tonic-gate 48977c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 4898d29f5a71Szhigang lu - Sun Microsystems - Beijing China if (MBLKL(data) != GET_STATUS_LENGTH) { 48997c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 4900112116d8Sfb "port %d: length incorrect %ld", 4901d29f5a71Szhigang lu - Sun Microsystems - Beijing China port, MBLKL(data)); 49027c478bd9Sstevel@tonic-gate freemsg(data); 49037c478bd9Sstevel@tonic-gate *status = *change = 0; 49047c478bd9Sstevel@tonic-gate 49057c478bd9Sstevel@tonic-gate return (rval); 49067c478bd9Sstevel@tonic-gate } 49077c478bd9Sstevel@tonic-gate 49087c478bd9Sstevel@tonic-gate 49097c478bd9Sstevel@tonic-gate *status = (*(data->b_rptr + 1) << 8) | *(data->b_rptr); 49107c478bd9Sstevel@tonic-gate *change = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2); 49117c478bd9Sstevel@tonic-gate 49127c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 49137c478bd9Sstevel@tonic-gate "port%d status=0x%x, change=0x%x", port, *status, *change); 49147c478bd9Sstevel@tonic-gate 49157c478bd9Sstevel@tonic-gate freemsg(data); 49167c478bd9Sstevel@tonic-gate 49177c478bd9Sstevel@tonic-gate if (*status & PORT_STATUS_CCS) { 49187c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 49197c478bd9Sstevel@tonic-gate "port%d connected", port); 49207c478bd9Sstevel@tonic-gate 49217c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_CCS & ack_flag); 49227c478bd9Sstevel@tonic-gate } else { 49237c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 49247c478bd9Sstevel@tonic-gate "port%d disconnected", port); 49257c478bd9Sstevel@tonic-gate 49267c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_CCS & ack_flag); 49277c478bd9Sstevel@tonic-gate } 49287c478bd9Sstevel@tonic-gate 49297c478bd9Sstevel@tonic-gate if (*status & PORT_STATUS_PES) { 49307c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 49317c478bd9Sstevel@tonic-gate "port%d enabled", port); 49327c478bd9Sstevel@tonic-gate 49337c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_PES & ack_flag); 49347c478bd9Sstevel@tonic-gate } else { 49357c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 49367c478bd9Sstevel@tonic-gate "port%d disabled", port); 49377c478bd9Sstevel@tonic-gate 49387c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_PES & ack_flag); 49397c478bd9Sstevel@tonic-gate } 49407c478bd9Sstevel@tonic-gate 49417c478bd9Sstevel@tonic-gate if (*status & PORT_STATUS_PSS) { 49427c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 49437c478bd9Sstevel@tonic-gate "port%d suspended", port); 49447c478bd9Sstevel@tonic-gate 49457c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_PSS & ack_flag); 49467c478bd9Sstevel@tonic-gate } else { 49477c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 49487c478bd9Sstevel@tonic-gate "port%d not suspended", port); 49497c478bd9Sstevel@tonic-gate 49507c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_PSS & ack_flag); 49517c478bd9Sstevel@tonic-gate } 49527c478bd9Sstevel@tonic-gate 49537c478bd9Sstevel@tonic-gate if (*change & PORT_CHANGE_PRSC) { 49547c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 49557c478bd9Sstevel@tonic-gate "port%d reset completed", port); 49567c478bd9Sstevel@tonic-gate 49577c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_CHANGE_PRSC & ack_flag); 49587c478bd9Sstevel@tonic-gate } else { 49597c478bd9Sstevel@tonic-gate 49607c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_CHANGE_PRSC & ack_flag); 49617c478bd9Sstevel@tonic-gate } 49627c478bd9Sstevel@tonic-gate 49637c478bd9Sstevel@tonic-gate if (*status & PORT_STATUS_POCI) { 4964d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 4965d291d9f2Sfrits "port%d overcurrent!", port); 49667c478bd9Sstevel@tonic-gate 49677c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_POCI & ack_flag); 49687c478bd9Sstevel@tonic-gate } else { 49697c478bd9Sstevel@tonic-gate 49707c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_POCI & ack_flag); 49717c478bd9Sstevel@tonic-gate } 49727c478bd9Sstevel@tonic-gate 49737c478bd9Sstevel@tonic-gate if (*status & PORT_STATUS_PRS) { 49747c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 49757c478bd9Sstevel@tonic-gate "port%d reset active", port); 49767c478bd9Sstevel@tonic-gate 49777c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_PRS & ack_flag); 49787c478bd9Sstevel@tonic-gate } else { 49797c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 49807c478bd9Sstevel@tonic-gate "port%d reset inactive", port); 49817c478bd9Sstevel@tonic-gate 49827c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_PRS & ack_flag); 49837c478bd9Sstevel@tonic-gate } 49847c478bd9Sstevel@tonic-gate if (*status & PORT_STATUS_PPS) { 49857c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 49867c478bd9Sstevel@tonic-gate "port%d power on", port); 49877c478bd9Sstevel@tonic-gate 49887c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_PPS & ack_flag); 49897c478bd9Sstevel@tonic-gate } else { 49907c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 49917c478bd9Sstevel@tonic-gate "port%d power off", port); 49927c478bd9Sstevel@tonic-gate 49937c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_PPS & ack_flag); 49947c478bd9Sstevel@tonic-gate } 49957c478bd9Sstevel@tonic-gate if (*status & PORT_STATUS_LSDA) { 49967c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 49977c478bd9Sstevel@tonic-gate "port%d low speed", port); 49987c478bd9Sstevel@tonic-gate 49997c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_LSDA & ack_flag); 50007c478bd9Sstevel@tonic-gate } else { 50017c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_LSDA & ack_flag); 50027c478bd9Sstevel@tonic-gate if (*status & PORT_STATUS_HSDA) { 50037c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, 50047c478bd9Sstevel@tonic-gate hubd->h_log_handle, "port%d " 50057c478bd9Sstevel@tonic-gate "high speed", port); 50067c478bd9Sstevel@tonic-gate 50077c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= 5008c0f24e5bSlg (PORT_STATUS_HSDA & ack_flag); 50097c478bd9Sstevel@tonic-gate } else { 50107c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, 50117c478bd9Sstevel@tonic-gate hubd->h_log_handle, "port%d " 50127c478bd9Sstevel@tonic-gate "full speed", port); 50137c478bd9Sstevel@tonic-gate 50147c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= 5015c0f24e5bSlg ~(PORT_STATUS_HSDA & ack_flag); 50167c478bd9Sstevel@tonic-gate } 50177c478bd9Sstevel@tonic-gate } 50187c478bd9Sstevel@tonic-gate 50197c478bd9Sstevel@tonic-gate /* 50207c478bd9Sstevel@tonic-gate * Acknowledge connection, enable, reset status 50217c478bd9Sstevel@tonic-gate */ 50227c478bd9Sstevel@tonic-gate if (ack_flag) { 50237c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 50247c478bd9Sstevel@tonic-gate if (*change & PORT_CHANGE_CSC & ack_flag) { 50257c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 50267c478bd9Sstevel@tonic-gate "clearing feature CFS_C_PORT_CONNECTION"); 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_CLEAR_FEATURE, 50317c478bd9Sstevel@tonic-gate CFS_C_PORT_CONNECTION, 50327c478bd9Sstevel@tonic-gate port, 50337c478bd9Sstevel@tonic-gate 0, NULL, 0, 50347c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != 50357c478bd9Sstevel@tonic-gate USB_SUCCESS) { 50367c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 50377c478bd9Sstevel@tonic-gate hubd->h_log_handle, 50387c478bd9Sstevel@tonic-gate "clear feature CFS_C_PORT_CONNECTION" 50397c478bd9Sstevel@tonic-gate " port%d failed (%d 0x%x %d)", 50407c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 50417c478bd9Sstevel@tonic-gate } 50427c478bd9Sstevel@tonic-gate } 50437c478bd9Sstevel@tonic-gate if (*change & PORT_CHANGE_PESC & ack_flag) { 50447c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 50457c478bd9Sstevel@tonic-gate "clearing feature CFS_C_PORT_ENABLE"); 50467c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 50477c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 504835f36846Ssl HUB_HANDLE_PORT_FEATURE_TYPE, 50497c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 50507c478bd9Sstevel@tonic-gate CFS_C_PORT_ENABLE, 50517c478bd9Sstevel@tonic-gate port, 50527c478bd9Sstevel@tonic-gate 0, NULL, 0, 50537c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != 50547c478bd9Sstevel@tonic-gate USB_SUCCESS) { 50557c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 50567c478bd9Sstevel@tonic-gate hubd->h_log_handle, 50577c478bd9Sstevel@tonic-gate "clear feature CFS_C_PORT_ENABLE" 50587c478bd9Sstevel@tonic-gate " port%d failed (%d 0x%x %d)", 50597c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 50607c478bd9Sstevel@tonic-gate } 50617c478bd9Sstevel@tonic-gate } 50627c478bd9Sstevel@tonic-gate if (*change & PORT_CHANGE_PSSC & ack_flag) { 50637c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 50647c478bd9Sstevel@tonic-gate "clearing feature CFS_C_PORT_SUSPEND"); 50657c478bd9Sstevel@tonic-gate 50667c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 50677c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 506835f36846Ssl HUB_HANDLE_PORT_FEATURE_TYPE, 50697c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 50707c478bd9Sstevel@tonic-gate CFS_C_PORT_SUSPEND, 50717c478bd9Sstevel@tonic-gate port, 50727c478bd9Sstevel@tonic-gate 0, NULL, 0, 50737c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != 50747c478bd9Sstevel@tonic-gate USB_SUCCESS) { 50757c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 50767c478bd9Sstevel@tonic-gate hubd->h_log_handle, 50777c478bd9Sstevel@tonic-gate "clear feature CFS_C_PORT_SUSPEND" 50787c478bd9Sstevel@tonic-gate " port%d failed (%d 0x%x %d)", 50797c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 50807c478bd9Sstevel@tonic-gate } 50817c478bd9Sstevel@tonic-gate } 50827c478bd9Sstevel@tonic-gate if (*change & PORT_CHANGE_OCIC & ack_flag) { 50837c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 50847c478bd9Sstevel@tonic-gate "clearing feature CFS_C_PORT_OVER_CURRENT"); 50857c478bd9Sstevel@tonic-gate 50867c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 50877c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 508835f36846Ssl HUB_HANDLE_PORT_FEATURE_TYPE, 50897c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 50907c478bd9Sstevel@tonic-gate CFS_C_PORT_OVER_CURRENT, 50917c478bd9Sstevel@tonic-gate port, 50927c478bd9Sstevel@tonic-gate 0, NULL, 0, 50937c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != 50947c478bd9Sstevel@tonic-gate USB_SUCCESS) { 50957c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 50967c478bd9Sstevel@tonic-gate hubd->h_log_handle, 50977c478bd9Sstevel@tonic-gate "clear feature CFS_C_PORT_OVER_CURRENT" 50987c478bd9Sstevel@tonic-gate " port%d failed (%d 0x%x %d)", 50997c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 51007c478bd9Sstevel@tonic-gate } 51017c478bd9Sstevel@tonic-gate } 51027c478bd9Sstevel@tonic-gate if (*change & PORT_CHANGE_PRSC & ack_flag) { 51037c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 51047c478bd9Sstevel@tonic-gate "clearing feature CFS_C_PORT_RESET"); 51057c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 51067c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 510735f36846Ssl HUB_HANDLE_PORT_FEATURE_TYPE, 51087c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 51097c478bd9Sstevel@tonic-gate CFS_C_PORT_RESET, 51107c478bd9Sstevel@tonic-gate port, 51117c478bd9Sstevel@tonic-gate 0, NULL, 0, 51127c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != 51137c478bd9Sstevel@tonic-gate USB_SUCCESS) { 51147c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, 51157c478bd9Sstevel@tonic-gate hubd->h_log_handle, 51167c478bd9Sstevel@tonic-gate "clear feature CFS_C_PORT_RESET" 51177c478bd9Sstevel@tonic-gate " port%d failed (%d 0x%x %d)", 51187c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 51197c478bd9Sstevel@tonic-gate } 51207c478bd9Sstevel@tonic-gate } 51217c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 51227c478bd9Sstevel@tonic-gate } 51237c478bd9Sstevel@tonic-gate 51247c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 51257c478bd9Sstevel@tonic-gate "new port%d state 0x%x", port, hubd->h_port_state[port]); 51267c478bd9Sstevel@tonic-gate 51277c478bd9Sstevel@tonic-gate 51287c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 51297c478bd9Sstevel@tonic-gate } 51307c478bd9Sstevel@tonic-gate 51317c478bd9Sstevel@tonic-gate 51327c478bd9Sstevel@tonic-gate /* 51337c478bd9Sstevel@tonic-gate * hubd_recover_disabled_port 51347c478bd9Sstevel@tonic-gate * if the port got disabled because of an error 51357c478bd9Sstevel@tonic-gate * enable it. If hub doesn't suport enable port, 51367c478bd9Sstevel@tonic-gate * reset the port to bring the device to life again 51377c478bd9Sstevel@tonic-gate */ 51387c478bd9Sstevel@tonic-gate static int 51397c478bd9Sstevel@tonic-gate hubd_recover_disabled_port(hubd_t *hubd, usb_port_t port) 51407c478bd9Sstevel@tonic-gate { 51417c478bd9Sstevel@tonic-gate uint16_t status; 51427c478bd9Sstevel@tonic-gate uint16_t change; 51437c478bd9Sstevel@tonic-gate int rval = USB_FAILURE; 51447c478bd9Sstevel@tonic-gate 51457c478bd9Sstevel@tonic-gate /* first try enabling the port */ 51467c478bd9Sstevel@tonic-gate (void) hubd_enable_port(hubd, port); 51477c478bd9Sstevel@tonic-gate 51487c478bd9Sstevel@tonic-gate /* read the port status */ 51497c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, &status, &change, 51507c478bd9Sstevel@tonic-gate PORT_CHANGE_PESC); 51517c478bd9Sstevel@tonic-gate 51527c478bd9Sstevel@tonic-gate if (status & PORT_STATUS_PES) { 51537c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5154c0f24e5bSlg "Port%d now Enabled", port); 51557c478bd9Sstevel@tonic-gate } else if (status & PORT_STATUS_CCS) { 51567c478bd9Sstevel@tonic-gate /* first post a disconnect event to the child */ 51577c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 51587c478bd9Sstevel@tonic-gate hubd_post_event(hubd, port, USBA_EVENT_TAG_HOT_REMOVAL); 51597c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 51607c478bd9Sstevel@tonic-gate 51617c478bd9Sstevel@tonic-gate /* then reset the port and recover the device */ 51627c478bd9Sstevel@tonic-gate rval = hubd_handle_port_connect(hubd, port); 51637c478bd9Sstevel@tonic-gate 51647c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5165c0f24e5bSlg "Port%d now Enabled by force", port); 51667c478bd9Sstevel@tonic-gate } 51677c478bd9Sstevel@tonic-gate 51687c478bd9Sstevel@tonic-gate return (rval); 51697c478bd9Sstevel@tonic-gate } 51707c478bd9Sstevel@tonic-gate 51717c478bd9Sstevel@tonic-gate 51727c478bd9Sstevel@tonic-gate /* 51737c478bd9Sstevel@tonic-gate * hubd_enable_all_port_power: 51747c478bd9Sstevel@tonic-gate */ 51757c478bd9Sstevel@tonic-gate static int 51767c478bd9Sstevel@tonic-gate hubd_enable_all_port_power(hubd_t *hubd) 51777c478bd9Sstevel@tonic-gate { 51787c478bd9Sstevel@tonic-gate usb_hub_descr_t *hub_descr; 51797c478bd9Sstevel@tonic-gate int wait; 51807c478bd9Sstevel@tonic-gate usb_port_t port; 51817c478bd9Sstevel@tonic-gate uint_t retry; 51827c478bd9Sstevel@tonic-gate uint16_t status; 51837c478bd9Sstevel@tonic-gate uint16_t change; 51847c478bd9Sstevel@tonic-gate 51857c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 51867c478bd9Sstevel@tonic-gate "hubd_enable_all_port_power"); 51877c478bd9Sstevel@tonic-gate 51887c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 51897c478bd9Sstevel@tonic-gate 51907c478bd9Sstevel@tonic-gate hub_descr = &hubd->h_hub_descr; 51917c478bd9Sstevel@tonic-gate 51927c478bd9Sstevel@tonic-gate /* 51937c478bd9Sstevel@tonic-gate * According to section 11.11 of USB, for hubs with no power 51947c478bd9Sstevel@tonic-gate * switches, bPwrOn2PwrGood is zero. But we wait for some 51957c478bd9Sstevel@tonic-gate * arbitrary time to enable power to become stable. 51967c478bd9Sstevel@tonic-gate * 51977c478bd9Sstevel@tonic-gate * If an hub supports port power switching, we need to wait 51987c478bd9Sstevel@tonic-gate * at least 20ms before accessing corresponding usb port. 51997c478bd9Sstevel@tonic-gate */ 52007c478bd9Sstevel@tonic-gate if ((hub_descr->wHubCharacteristics & 52017c478bd9Sstevel@tonic-gate HUB_CHARS_NO_POWER_SWITCHING) || (!hub_descr->bPwrOn2PwrGood)) { 52027c478bd9Sstevel@tonic-gate wait = hubd_device_delay / 10; 52037c478bd9Sstevel@tonic-gate } else { 52047c478bd9Sstevel@tonic-gate wait = max(HUB_DEFAULT_POPG, 52057c478bd9Sstevel@tonic-gate hub_descr->bPwrOn2PwrGood) * 2 * 1000; 52067c478bd9Sstevel@tonic-gate } 52077c478bd9Sstevel@tonic-gate 52087c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 52097c478bd9Sstevel@tonic-gate "hubd_enable_all_port_power: popg=%d wait=%d", 52107c478bd9Sstevel@tonic-gate hub_descr->bPwrOn2PwrGood, wait); 52117c478bd9Sstevel@tonic-gate 52127c478bd9Sstevel@tonic-gate /* 52137c478bd9Sstevel@tonic-gate * Enable power per port. we ignore gang power and power mask 52147c478bd9Sstevel@tonic-gate * and always enable all ports one by one. 52157c478bd9Sstevel@tonic-gate */ 52167c478bd9Sstevel@tonic-gate for (port = 1; port <= hub_descr->bNbrPorts; port++) { 52177c478bd9Sstevel@tonic-gate /* 52187c478bd9Sstevel@tonic-gate * Transition the port from the Powered Off to the 52197c478bd9Sstevel@tonic-gate * Disconnected state by supplying power to the port. 52207c478bd9Sstevel@tonic-gate */ 52217c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, 52227c478bd9Sstevel@tonic-gate hubd->h_log_handle, 52237c478bd9Sstevel@tonic-gate "hubd_enable_all_port_power: power port=%d", port); 52247c478bd9Sstevel@tonic-gate 52257c478bd9Sstevel@tonic-gate (void) hubd_enable_port_power(hubd, port); 52267c478bd9Sstevel@tonic-gate } 52277c478bd9Sstevel@tonic-gate 52287c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 52297c478bd9Sstevel@tonic-gate delay(drv_usectohz(wait)); 52307c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 52317c478bd9Sstevel@tonic-gate 52327c478bd9Sstevel@tonic-gate /* For retry if any, use some extra delay */ 52337c478bd9Sstevel@tonic-gate wait = max(wait, hubd_device_delay / 10); 52347c478bd9Sstevel@tonic-gate 52357c478bd9Sstevel@tonic-gate /* Check each port power status for a given usb hub */ 52367c478bd9Sstevel@tonic-gate for (port = 1; port <= hub_descr->bNbrPorts; port++) { 52377c478bd9Sstevel@tonic-gate 52387c478bd9Sstevel@tonic-gate /* Get port status */ 52397c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 52407c478bd9Sstevel@tonic-gate &status, &change, 0); 52417c478bd9Sstevel@tonic-gate 52427c478bd9Sstevel@tonic-gate for (retry = 0; ((!(status & PORT_STATUS_PPS)) && 52437c478bd9Sstevel@tonic-gate (retry < HUBD_PORT_RETRY)); retry++) { 52447c478bd9Sstevel@tonic-gate 52457c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 52467c478bd9Sstevel@tonic-gate "Retry is in progress %d: port %d status %d", 52477c478bd9Sstevel@tonic-gate retry, port, status); 52487c478bd9Sstevel@tonic-gate 52497c478bd9Sstevel@tonic-gate (void) hubd_enable_port_power(hubd, port); 52507c478bd9Sstevel@tonic-gate 52517c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 52527c478bd9Sstevel@tonic-gate delay(drv_usectohz(wait)); 52537c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 52547c478bd9Sstevel@tonic-gate 52557c478bd9Sstevel@tonic-gate /* Get port status */ 52567c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 52577c478bd9Sstevel@tonic-gate &status, &change, 0); 52587c478bd9Sstevel@tonic-gate } 52597c478bd9Sstevel@tonic-gate 52607c478bd9Sstevel@tonic-gate /* Print warning message if port has no power */ 52617c478bd9Sstevel@tonic-gate if (!(status & PORT_STATUS_PPS)) { 52627c478bd9Sstevel@tonic-gate 5263d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 52647c478bd9Sstevel@tonic-gate "hubd_enable_all_port_power: port %d power-on " 52657c478bd9Sstevel@tonic-gate "failed, port status 0x%x", port, status); 52667c478bd9Sstevel@tonic-gate } 52677c478bd9Sstevel@tonic-gate } 52687c478bd9Sstevel@tonic-gate 52697c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 52707c478bd9Sstevel@tonic-gate } 52717c478bd9Sstevel@tonic-gate 52727c478bd9Sstevel@tonic-gate 52737c478bd9Sstevel@tonic-gate /* 52747c478bd9Sstevel@tonic-gate * hubd_enable_port_power: 52757c478bd9Sstevel@tonic-gate * enable individual port power 52767c478bd9Sstevel@tonic-gate */ 52777c478bd9Sstevel@tonic-gate static int 52787c478bd9Sstevel@tonic-gate hubd_enable_port_power(hubd_t *hubd, usb_port_t port) 52797c478bd9Sstevel@tonic-gate { 52807c478bd9Sstevel@tonic-gate int rval; 52817c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 52827c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 52837c478bd9Sstevel@tonic-gate 52847c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 52857c478bd9Sstevel@tonic-gate "hubd_enable_port_power: port=%d", port); 52867c478bd9Sstevel@tonic-gate 52877c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 52887c478bd9Sstevel@tonic-gate ASSERT(hubd->h_default_pipe != 0); 52897c478bd9Sstevel@tonic-gate 52907c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 52917c478bd9Sstevel@tonic-gate 52927c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 52937c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 529435f36846Ssl HUB_HANDLE_PORT_FEATURE_TYPE, 52957c478bd9Sstevel@tonic-gate USB_REQ_SET_FEATURE, 52967c478bd9Sstevel@tonic-gate CFS_PORT_POWER, 52977c478bd9Sstevel@tonic-gate port, 52987c478bd9Sstevel@tonic-gate 0, NULL, 0, 52997c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 53007c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 53017c478bd9Sstevel@tonic-gate "set port power failed (%d 0x%x %d)", 53027c478bd9Sstevel@tonic-gate completion_reason, cb_flags, rval); 53037c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 53047c478bd9Sstevel@tonic-gate 53057c478bd9Sstevel@tonic-gate return (USB_FAILURE); 53067c478bd9Sstevel@tonic-gate } else { 53077c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 53087c478bd9Sstevel@tonic-gate hubd->h_port_state[port] |= PORT_STATUS_PPS; 53097c478bd9Sstevel@tonic-gate 53107c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 53117c478bd9Sstevel@tonic-gate } 53127c478bd9Sstevel@tonic-gate } 53137c478bd9Sstevel@tonic-gate 53147c478bd9Sstevel@tonic-gate 53157c478bd9Sstevel@tonic-gate /* 53167c478bd9Sstevel@tonic-gate * hubd_disable_all_port_power: 53177c478bd9Sstevel@tonic-gate */ 53187c478bd9Sstevel@tonic-gate static int 53197c478bd9Sstevel@tonic-gate hubd_disable_all_port_power(hubd_t *hubd) 53207c478bd9Sstevel@tonic-gate { 53217c478bd9Sstevel@tonic-gate usb_port_t port; 53227c478bd9Sstevel@tonic-gate 53237c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 53247c478bd9Sstevel@tonic-gate "hubd_disable_all_port_power"); 53257c478bd9Sstevel@tonic-gate 53267c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 53277c478bd9Sstevel@tonic-gate 53287c478bd9Sstevel@tonic-gate /* 53297c478bd9Sstevel@tonic-gate * disable power per port, ignore gang power and power mask 53307c478bd9Sstevel@tonic-gate */ 53317c478bd9Sstevel@tonic-gate for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { 53327c478bd9Sstevel@tonic-gate (void) hubd_disable_port_power(hubd, port); 53337c478bd9Sstevel@tonic-gate } 53347c478bd9Sstevel@tonic-gate 53357c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 53367c478bd9Sstevel@tonic-gate } 53377c478bd9Sstevel@tonic-gate 53387c478bd9Sstevel@tonic-gate 53397c478bd9Sstevel@tonic-gate /* 53407c478bd9Sstevel@tonic-gate * hubd_disable_port_power: 53417c478bd9Sstevel@tonic-gate * disable individual port power 53427c478bd9Sstevel@tonic-gate */ 53437c478bd9Sstevel@tonic-gate static int 53447c478bd9Sstevel@tonic-gate hubd_disable_port_power(hubd_t *hubd, usb_port_t port) 53457c478bd9Sstevel@tonic-gate { 53467c478bd9Sstevel@tonic-gate int rval; 53477c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 53487c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 53497c478bd9Sstevel@tonic-gate 53507c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, 53517c478bd9Sstevel@tonic-gate "hubd_disable_port_power: port=%d", port); 53527c478bd9Sstevel@tonic-gate 53537c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 53547c478bd9Sstevel@tonic-gate 53557c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 53567c478bd9Sstevel@tonic-gate 53577c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, 53587c478bd9Sstevel@tonic-gate hubd->h_default_pipe, 535935f36846Ssl HUB_HANDLE_PORT_FEATURE_TYPE, 53607c478bd9Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE, 53617c478bd9Sstevel@tonic-gate CFS_PORT_POWER, 53627c478bd9Sstevel@tonic-gate port, 53637c478bd9Sstevel@tonic-gate 0, NULL, 0, 53647c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 53657c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 53667c478bd9Sstevel@tonic-gate "clearing port%d power failed (%d 0x%x %d)", 53677c478bd9Sstevel@tonic-gate port, completion_reason, cb_flags, rval); 53687c478bd9Sstevel@tonic-gate 53697c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 53707c478bd9Sstevel@tonic-gate 53717c478bd9Sstevel@tonic-gate return (USB_FAILURE); 53727c478bd9Sstevel@tonic-gate } else { 53737c478bd9Sstevel@tonic-gate 53747c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 53757c478bd9Sstevel@tonic-gate ASSERT(completion_reason == 0); 53767c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= ~PORT_STATUS_PPS; 53777c478bd9Sstevel@tonic-gate 53787c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 53797c478bd9Sstevel@tonic-gate } 53807c478bd9Sstevel@tonic-gate } 53817c478bd9Sstevel@tonic-gate 53827c478bd9Sstevel@tonic-gate 53837c478bd9Sstevel@tonic-gate /* 53847c478bd9Sstevel@tonic-gate * Search the database of user preferences and find out the preferred 53857c478bd9Sstevel@tonic-gate * configuration for this new device 53867c478bd9Sstevel@tonic-gate */ 5387ff0e937bSRaymond Chen int 53887c478bd9Sstevel@tonic-gate hubd_select_device_configuration(hubd_t *hubd, usb_port_t port, 53897c478bd9Sstevel@tonic-gate dev_info_t *child_dip, usba_device_t *child_ud) 53907c478bd9Sstevel@tonic-gate { 53917c478bd9Sstevel@tonic-gate char *pathname = NULL; 53927c478bd9Sstevel@tonic-gate char *tmp_path = NULL; 53937c478bd9Sstevel@tonic-gate int user_conf; 53947c478bd9Sstevel@tonic-gate int pathlen; 53957c478bd9Sstevel@tonic-gate usb_dev_descr_t *usbdev_ptr; 53967c478bd9Sstevel@tonic-gate usba_configrec_t *user_pref; 53977c478bd9Sstevel@tonic-gate 53987c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 53997c478bd9Sstevel@tonic-gate usbdev_ptr = child_ud->usb_dev_descr; 54007c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 54017c478bd9Sstevel@tonic-gate 54027c478bd9Sstevel@tonic-gate /* try to get pathname for this device */ 54037c478bd9Sstevel@tonic-gate tmp_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 54047c478bd9Sstevel@tonic-gate (void) ddi_pathname(child_dip, tmp_path); 54057c478bd9Sstevel@tonic-gate 54067c478bd9Sstevel@tonic-gate pathlen = strlen(tmp_path) + 32; 54077c478bd9Sstevel@tonic-gate pathname = kmem_zalloc(pathlen, KM_SLEEP); 54087c478bd9Sstevel@tonic-gate 54097c478bd9Sstevel@tonic-gate /* 54107c478bd9Sstevel@tonic-gate * We haven't initialized the node and it doesn't have an address 54117c478bd9Sstevel@tonic-gate * yet. Append port number to the physical pathname 54127c478bd9Sstevel@tonic-gate */ 54137c478bd9Sstevel@tonic-gate (void) sprintf(pathname, "%s@%d", tmp_path, port); 54147c478bd9Sstevel@tonic-gate 54157c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 54167c478bd9Sstevel@tonic-gate "hubd_select_device_configuration: Device=%s\n\t" 54177c478bd9Sstevel@tonic-gate "Child path=%s", 54187c478bd9Sstevel@tonic-gate usba_get_mfg_prod_sn_str(child_dip, tmp_path, MAXPATHLEN), 54197c478bd9Sstevel@tonic-gate pathname); 54207c478bd9Sstevel@tonic-gate kmem_free(tmp_path, MAXPATHLEN); 54217c478bd9Sstevel@tonic-gate 54227c478bd9Sstevel@tonic-gate 54237c478bd9Sstevel@tonic-gate /* database search for user preferences */ 54247c478bd9Sstevel@tonic-gate user_pref = usba_devdb_get_user_preferences(usbdev_ptr->idVendor, 54257c478bd9Sstevel@tonic-gate usbdev_ptr->idProduct, child_ud->usb_serialno_str, pathname); 54267c478bd9Sstevel@tonic-gate 54277c478bd9Sstevel@tonic-gate if (user_pref) { 54287c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 54297c478bd9Sstevel@tonic-gate "hubd_select_device_configuration: " 54307c478bd9Sstevel@tonic-gate "usba_devdb_get_user_preferences " 54317c478bd9Sstevel@tonic-gate "return user_conf=%d\npreferred driver=%s path=%s", 54327c478bd9Sstevel@tonic-gate user_pref->cfg_index, user_pref->driver, 54337c478bd9Sstevel@tonic-gate user_pref->pathname); 54347c478bd9Sstevel@tonic-gate 54357c478bd9Sstevel@tonic-gate user_conf = user_pref->cfg_index; 54367c478bd9Sstevel@tonic-gate 54377c478bd9Sstevel@tonic-gate if (user_pref->driver) { 54387c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 54397c478bd9Sstevel@tonic-gate child_ud->usb_preferred_driver = user_pref->driver; 54407c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 54417c478bd9Sstevel@tonic-gate } 54427c478bd9Sstevel@tonic-gate } else { 54437c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 54447c478bd9Sstevel@tonic-gate "hubd_select_device_configuration: No match found"); 54457c478bd9Sstevel@tonic-gate 54467c478bd9Sstevel@tonic-gate /* select default configuration for this device */ 54477c478bd9Sstevel@tonic-gate user_conf = USBA_DEV_CONFIG_INDEX_UNDEFINED; 54487c478bd9Sstevel@tonic-gate } 54497c478bd9Sstevel@tonic-gate kmem_free(pathname, pathlen); 54507c478bd9Sstevel@tonic-gate 54517c478bd9Sstevel@tonic-gate /* if the device has just one configuration, set default value */ 54527c478bd9Sstevel@tonic-gate if (usbdev_ptr->bNumConfigurations == 1) { 54537c478bd9Sstevel@tonic-gate user_conf = USB_DEV_DEFAULT_CONFIG_INDEX; 54547c478bd9Sstevel@tonic-gate } 54557c478bd9Sstevel@tonic-gate 54567c478bd9Sstevel@tonic-gate return (user_conf); 54577c478bd9Sstevel@tonic-gate } 54587c478bd9Sstevel@tonic-gate 54597c478bd9Sstevel@tonic-gate 54607c478bd9Sstevel@tonic-gate /* 54617c478bd9Sstevel@tonic-gate * Retrieves config cloud for this configuration 54627c478bd9Sstevel@tonic-gate */ 54637c478bd9Sstevel@tonic-gate int 54647c478bd9Sstevel@tonic-gate hubd_get_this_config_cloud(hubd_t *hubd, dev_info_t *dip, 54657c478bd9Sstevel@tonic-gate usba_device_t *child_ud, uint16_t conf_index) 54667c478bd9Sstevel@tonic-gate { 54677c478bd9Sstevel@tonic-gate usb_cfg_descr_t *confdescr; 54687c478bd9Sstevel@tonic-gate mblk_t *pdata = NULL; 54697c478bd9Sstevel@tonic-gate int rval; 54707c478bd9Sstevel@tonic-gate size_t size; 54717c478bd9Sstevel@tonic-gate char *tmpbuf; 54727c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 54737c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 54747c478bd9Sstevel@tonic-gate usb_pipe_handle_t def_ph; 54757c478bd9Sstevel@tonic-gate 54767c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 54777c478bd9Sstevel@tonic-gate "hubd_get_this_config_cloud: conf_index=%d", conf_index); 54787c478bd9Sstevel@tonic-gate 54797c478bd9Sstevel@tonic-gate 54807c478bd9Sstevel@tonic-gate /* alloc temporary space for config descriptor */ 54817c478bd9Sstevel@tonic-gate confdescr = (usb_cfg_descr_t *)kmem_zalloc(USB_CFG_DESCR_SIZE, 54827c478bd9Sstevel@tonic-gate KM_SLEEP); 54837c478bd9Sstevel@tonic-gate 54847c478bd9Sstevel@tonic-gate /* alloc temporary space for string descriptor */ 54857c478bd9Sstevel@tonic-gate tmpbuf = kmem_zalloc(USB_MAXSTRINGLEN, KM_SLEEP); 54867c478bd9Sstevel@tonic-gate 54877c478bd9Sstevel@tonic-gate def_ph = usba_get_dflt_pipe_handle(dip); 54887c478bd9Sstevel@tonic-gate 54897c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(dip, def_ph, 54907c478bd9Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD, 54917c478bd9Sstevel@tonic-gate USB_REQ_GET_DESCR, 54927c478bd9Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_CFG | conf_index, 54937c478bd9Sstevel@tonic-gate 0, 54947c478bd9Sstevel@tonic-gate USB_CFG_DESCR_SIZE, 54957c478bd9Sstevel@tonic-gate &pdata, 54967c478bd9Sstevel@tonic-gate 0, 54977c478bd9Sstevel@tonic-gate &completion_reason, 54987c478bd9Sstevel@tonic-gate &cb_flags, 54997c478bd9Sstevel@tonic-gate 0)) == USB_SUCCESS) { 55007c478bd9Sstevel@tonic-gate 55017c478bd9Sstevel@tonic-gate /* this must be true since we didn't allow data underruns */ 5502d29f5a71Szhigang lu - Sun Microsystems - Beijing China if (MBLKL(pdata) != USB_CFG_DESCR_SIZE) { 550335f36846Ssl USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 550435f36846Ssl "device returned incorrect configuration " 550535f36846Ssl "descriptor size."); 550635f36846Ssl 550735f36846Ssl rval = USB_FAILURE; 550835f36846Ssl goto done; 550935f36846Ssl } 55107c478bd9Sstevel@tonic-gate 55117c478bd9Sstevel@tonic-gate /* 55127c478bd9Sstevel@tonic-gate * Parse the configuration descriptor 55137c478bd9Sstevel@tonic-gate */ 55147c478bd9Sstevel@tonic-gate size = usb_parse_cfg_descr(pdata->b_rptr, 5515d29f5a71Szhigang lu - Sun Microsystems - Beijing China MBLKL(pdata), confdescr, 55167c478bd9Sstevel@tonic-gate USB_CFG_DESCR_SIZE); 55177c478bd9Sstevel@tonic-gate 55187c478bd9Sstevel@tonic-gate /* if parse cfg descr error, it should return failure */ 55197c478bd9Sstevel@tonic-gate if (size == USB_PARSE_ERROR) { 55207c478bd9Sstevel@tonic-gate 55217c478bd9Sstevel@tonic-gate if (pdata->b_rptr[1] != USB_DESCR_TYPE_CFG) { 5522d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 55237c478bd9Sstevel@tonic-gate hubd->h_log_handle, 55247c478bd9Sstevel@tonic-gate "device returned incorrect " 55257c478bd9Sstevel@tonic-gate "configuration descriptor type."); 55267c478bd9Sstevel@tonic-gate } 55277c478bd9Sstevel@tonic-gate rval = USB_FAILURE; 55287c478bd9Sstevel@tonic-gate goto done; 55297c478bd9Sstevel@tonic-gate } 55307c478bd9Sstevel@tonic-gate 55317c478bd9Sstevel@tonic-gate if (confdescr->wTotalLength < USB_CFG_DESCR_SIZE) { 5532d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 55337c478bd9Sstevel@tonic-gate hubd->h_log_handle, 55347c478bd9Sstevel@tonic-gate "device returned incorrect " 55357c478bd9Sstevel@tonic-gate "configuration descriptor size."); 55367c478bd9Sstevel@tonic-gate 55377c478bd9Sstevel@tonic-gate rval = USB_FAILURE; 55387c478bd9Sstevel@tonic-gate goto done; 55397c478bd9Sstevel@tonic-gate } 55407c478bd9Sstevel@tonic-gate 55417c478bd9Sstevel@tonic-gate freemsg(pdata); 55427c478bd9Sstevel@tonic-gate pdata = NULL; 55437c478bd9Sstevel@tonic-gate 55447c478bd9Sstevel@tonic-gate /* Now fetch the complete config cloud */ 55457c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(dip, def_ph, 55467c478bd9Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD, 55477c478bd9Sstevel@tonic-gate USB_REQ_GET_DESCR, 55487c478bd9Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_CFG | conf_index, 55497c478bd9Sstevel@tonic-gate 0, 55507c478bd9Sstevel@tonic-gate confdescr->wTotalLength, 55517c478bd9Sstevel@tonic-gate &pdata, 55527c478bd9Sstevel@tonic-gate 0, 55537c478bd9Sstevel@tonic-gate &completion_reason, 55547c478bd9Sstevel@tonic-gate &cb_flags, 55557c478bd9Sstevel@tonic-gate 0)) == USB_SUCCESS) { 55567c478bd9Sstevel@tonic-gate 5557d29f5a71Szhigang lu - Sun Microsystems - Beijing China if (MBLKL(pdata) != 55587c478bd9Sstevel@tonic-gate confdescr->wTotalLength) { 55597c478bd9Sstevel@tonic-gate 5560d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 55617c478bd9Sstevel@tonic-gate hubd->h_log_handle, 55627c478bd9Sstevel@tonic-gate "device returned incorrect " 55637c478bd9Sstevel@tonic-gate "configuration descriptor."); 55647c478bd9Sstevel@tonic-gate 55657c478bd9Sstevel@tonic-gate rval = USB_FAILURE; 55667c478bd9Sstevel@tonic-gate goto done; 55677c478bd9Sstevel@tonic-gate } 55687c478bd9Sstevel@tonic-gate 55697c478bd9Sstevel@tonic-gate /* 55707c478bd9Sstevel@tonic-gate * copy config descriptor into usba_device 55717c478bd9Sstevel@tonic-gate */ 55727c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 55737c478bd9Sstevel@tonic-gate child_ud->usb_cfg_array[conf_index] = 55747c478bd9Sstevel@tonic-gate kmem_alloc(confdescr->wTotalLength, KM_SLEEP); 55757c478bd9Sstevel@tonic-gate child_ud->usb_cfg_array_len[conf_index] = 5576c0f24e5bSlg confdescr->wTotalLength; 55777c478bd9Sstevel@tonic-gate bcopy((caddr_t)pdata->b_rptr, 55787c478bd9Sstevel@tonic-gate (caddr_t)child_ud->usb_cfg_array[conf_index], 55797c478bd9Sstevel@tonic-gate confdescr->wTotalLength); 55807c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 55817c478bd9Sstevel@tonic-gate 55827c478bd9Sstevel@tonic-gate /* 55837c478bd9Sstevel@tonic-gate * retrieve string descriptor describing this 55847c478bd9Sstevel@tonic-gate * configuration 55857c478bd9Sstevel@tonic-gate */ 55867c478bd9Sstevel@tonic-gate if (confdescr->iConfiguration) { 55877c478bd9Sstevel@tonic-gate 55887c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, 55897c478bd9Sstevel@tonic-gate hubd->h_log_handle, 55907c478bd9Sstevel@tonic-gate "Get conf str descr for config_index=%d", 55917c478bd9Sstevel@tonic-gate conf_index); 55927c478bd9Sstevel@tonic-gate 55937c478bd9Sstevel@tonic-gate /* 55947c478bd9Sstevel@tonic-gate * Now fetch the string descriptor describing 55957c478bd9Sstevel@tonic-gate * this configuration 55967c478bd9Sstevel@tonic-gate */ 55977c478bd9Sstevel@tonic-gate if ((rval = usb_get_string_descr(dip, 55987c478bd9Sstevel@tonic-gate USB_LANG_ID, confdescr->iConfiguration, 55997c478bd9Sstevel@tonic-gate tmpbuf, USB_MAXSTRINGLEN)) == 56007c478bd9Sstevel@tonic-gate USB_SUCCESS) { 56017c478bd9Sstevel@tonic-gate size = strlen(tmpbuf); 56027c478bd9Sstevel@tonic-gate if (size > 0) { 56037c478bd9Sstevel@tonic-gate child_ud->usb_cfg_str_descr 56047c478bd9Sstevel@tonic-gate [conf_index] = (char *) 56057c478bd9Sstevel@tonic-gate kmem_zalloc(size + 1, 56067c478bd9Sstevel@tonic-gate KM_SLEEP); 56077c478bd9Sstevel@tonic-gate (void) strcpy( 56087c478bd9Sstevel@tonic-gate child_ud->usb_cfg_str_descr 56097c478bd9Sstevel@tonic-gate [conf_index], tmpbuf); 56107c478bd9Sstevel@tonic-gate } 56117c478bd9Sstevel@tonic-gate } else { 56127c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 56137c478bd9Sstevel@tonic-gate hubd->h_log_handle, 56147c478bd9Sstevel@tonic-gate "hubd_get_this_config_cloud: " 56157c478bd9Sstevel@tonic-gate "getting config string (%d) " 56167c478bd9Sstevel@tonic-gate "failed", 56177c478bd9Sstevel@tonic-gate confdescr->iConfiguration); 56187c478bd9Sstevel@tonic-gate 56197c478bd9Sstevel@tonic-gate /* ignore this error */ 56207c478bd9Sstevel@tonic-gate rval = USB_SUCCESS; 56217c478bd9Sstevel@tonic-gate } 56227c478bd9Sstevel@tonic-gate } 56237c478bd9Sstevel@tonic-gate } 56247c478bd9Sstevel@tonic-gate } 56257c478bd9Sstevel@tonic-gate 56267c478bd9Sstevel@tonic-gate done: 56277c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 56287c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 56297c478bd9Sstevel@tonic-gate "hubd_get_this_config_cloud: " 56307c478bd9Sstevel@tonic-gate "error in retrieving config descriptor for " 56317c478bd9Sstevel@tonic-gate "config index=%d rval=%d cr=%d", 56327c478bd9Sstevel@tonic-gate conf_index, rval, completion_reason); 56337c478bd9Sstevel@tonic-gate } 56347c478bd9Sstevel@tonic-gate 56357c478bd9Sstevel@tonic-gate if (pdata) { 56367c478bd9Sstevel@tonic-gate freemsg(pdata); 56377c478bd9Sstevel@tonic-gate pdata = NULL; 56387c478bd9Sstevel@tonic-gate } 56397c478bd9Sstevel@tonic-gate 56407c478bd9Sstevel@tonic-gate kmem_free(confdescr, USB_CFG_DESCR_SIZE); 56417c478bd9Sstevel@tonic-gate kmem_free(tmpbuf, USB_MAXSTRINGLEN); 56427c478bd9Sstevel@tonic-gate 56437c478bd9Sstevel@tonic-gate return (rval); 56447c478bd9Sstevel@tonic-gate } 56457c478bd9Sstevel@tonic-gate 56467c478bd9Sstevel@tonic-gate 56477c478bd9Sstevel@tonic-gate /* 56487c478bd9Sstevel@tonic-gate * Retrieves the entire config cloud for all configurations of the device 56497c478bd9Sstevel@tonic-gate */ 56507c478bd9Sstevel@tonic-gate int 56517c478bd9Sstevel@tonic-gate hubd_get_all_device_config_cloud(hubd_t *hubd, dev_info_t *dip, 56527c478bd9Sstevel@tonic-gate usba_device_t *child_ud) 56537c478bd9Sstevel@tonic-gate { 56547c478bd9Sstevel@tonic-gate int rval = USB_SUCCESS; 56557c478bd9Sstevel@tonic-gate int ncfgs; 56567c478bd9Sstevel@tonic-gate uint16_t size; 56577c478bd9Sstevel@tonic-gate uint16_t conf_index; 56587c478bd9Sstevel@tonic-gate uchar_t **cfg_array; 56597c478bd9Sstevel@tonic-gate uint16_t *cfg_array_len; 56607c478bd9Sstevel@tonic-gate char **str_descr; 56617c478bd9Sstevel@tonic-gate 56627c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 56637c478bd9Sstevel@tonic-gate "hubd_get_all_device_config_cloud: Start"); 56647c478bd9Sstevel@tonic-gate 56657c478bd9Sstevel@tonic-gate /* alloc pointer array for conf. descriptors */ 56667c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 56677c478bd9Sstevel@tonic-gate ncfgs = child_ud->usb_n_cfgs; 56687c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 56697c478bd9Sstevel@tonic-gate 56707c478bd9Sstevel@tonic-gate size = sizeof (uchar_t *) * ncfgs; 56717c478bd9Sstevel@tonic-gate cfg_array = kmem_zalloc(size, KM_SLEEP); 56727c478bd9Sstevel@tonic-gate cfg_array_len = kmem_zalloc(ncfgs * sizeof (uint16_t), KM_SLEEP); 56737c478bd9Sstevel@tonic-gate str_descr = kmem_zalloc(size, KM_SLEEP); 56747c478bd9Sstevel@tonic-gate 56757c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 56767c478bd9Sstevel@tonic-gate child_ud->usb_cfg_array = cfg_array; 56777c478bd9Sstevel@tonic-gate child_ud->usb_cfg_array_len = cfg_array_len; 56787c478bd9Sstevel@tonic-gate child_ud->usb_cfg_array_length = size; 56797c478bd9Sstevel@tonic-gate child_ud->usb_cfg_array_len_length = ncfgs * sizeof (uint16_t); 56807c478bd9Sstevel@tonic-gate child_ud->usb_cfg_str_descr = str_descr; 56817c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 56827c478bd9Sstevel@tonic-gate 56837c478bd9Sstevel@tonic-gate /* Get configuration descriptor for each configuration */ 56847c478bd9Sstevel@tonic-gate for (conf_index = 0; (conf_index < ncfgs) && 56857c478bd9Sstevel@tonic-gate (rval == USB_SUCCESS); conf_index++) { 56867c478bd9Sstevel@tonic-gate 56877c478bd9Sstevel@tonic-gate rval = hubd_get_this_config_cloud(hubd, dip, child_ud, 56887c478bd9Sstevel@tonic-gate conf_index); 56897c478bd9Sstevel@tonic-gate } 56907c478bd9Sstevel@tonic-gate 56917c478bd9Sstevel@tonic-gate return (rval); 56927c478bd9Sstevel@tonic-gate } 56937c478bd9Sstevel@tonic-gate 56947c478bd9Sstevel@tonic-gate 56957c478bd9Sstevel@tonic-gate /* 56967c478bd9Sstevel@tonic-gate * hubd_ready_device: 56977c478bd9Sstevel@tonic-gate * Update the usba_device structure 56987c478bd9Sstevel@tonic-gate * Set the given configuration 56997c478bd9Sstevel@tonic-gate * Prepares the device node for driver to online. If an existing 57007c478bd9Sstevel@tonic-gate * OBP node is found, it will switch to the OBP node. 57017c478bd9Sstevel@tonic-gate */ 5702ff0e937bSRaymond Chen dev_info_t * 57037c478bd9Sstevel@tonic-gate hubd_ready_device(hubd_t *hubd, dev_info_t *child_dip, usba_device_t *child_ud, 570435f36846Ssl uint_t config_index) 57057c478bd9Sstevel@tonic-gate { 57067c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 57077c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 57087c478bd9Sstevel@tonic-gate size_t size; 57097c478bd9Sstevel@tonic-gate usb_cfg_descr_t config_descriptor; 57107c478bd9Sstevel@tonic-gate usb_pipe_handle_t def_ph; 57117c478bd9Sstevel@tonic-gate usba_pipe_handle_data_t *ph; 57127c478bd9Sstevel@tonic-gate 57137c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5714112116d8Sfb "hubd_ready_device: dip=0x%p, user_conf_index=%d", 5715112116d8Sfb (void *)child_dip, config_index); 57167c478bd9Sstevel@tonic-gate 57177c478bd9Sstevel@tonic-gate size = usb_parse_cfg_descr( 57187c478bd9Sstevel@tonic-gate child_ud->usb_cfg_array[config_index], USB_CFG_DESCR_SIZE, 57197c478bd9Sstevel@tonic-gate &config_descriptor, USB_CFG_DESCR_SIZE); 57207c478bd9Sstevel@tonic-gate ASSERT(size == USB_CFG_DESCR_SIZE); 57217c478bd9Sstevel@tonic-gate 57227c478bd9Sstevel@tonic-gate def_ph = usba_get_dflt_pipe_handle(child_dip); 57237c478bd9Sstevel@tonic-gate 57247c478bd9Sstevel@tonic-gate /* Set the configuration */ 57257c478bd9Sstevel@tonic-gate (void) usb_pipe_sync_ctrl_xfer(child_dip, def_ph, 57267c478bd9Sstevel@tonic-gate USB_DEV_REQ_HOST_TO_DEV, 57277c478bd9Sstevel@tonic-gate USB_REQ_SET_CFG, /* bRequest */ 57287c478bd9Sstevel@tonic-gate config_descriptor.bConfigurationValue, /* wValue */ 57297c478bd9Sstevel@tonic-gate 0, /* wIndex */ 57307c478bd9Sstevel@tonic-gate 0, /* wLength */ 57317c478bd9Sstevel@tonic-gate NULL, 57327c478bd9Sstevel@tonic-gate 0, 57337c478bd9Sstevel@tonic-gate &completion_reason, 57347c478bd9Sstevel@tonic-gate &cb_flags, 57357c478bd9Sstevel@tonic-gate 0); 57367c478bd9Sstevel@tonic-gate 57377c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 57387c478bd9Sstevel@tonic-gate child_ud->usb_active_cfg_ndx = config_index; 57397c478bd9Sstevel@tonic-gate child_ud->usb_cfg = child_ud->usb_cfg_array[config_index]; 57407c478bd9Sstevel@tonic-gate child_ud->usb_cfg_length = config_descriptor.wTotalLength; 57417c478bd9Sstevel@tonic-gate child_ud->usb_cfg_value = config_descriptor.bConfigurationValue; 57427c478bd9Sstevel@tonic-gate child_ud->usb_n_ifs = config_descriptor.bNumInterfaces; 57437c478bd9Sstevel@tonic-gate child_ud->usb_dip = child_dip; 57447c478bd9Sstevel@tonic-gate 57457c478bd9Sstevel@tonic-gate child_ud->usb_client_flags = kmem_zalloc( 5746c0f24e5bSlg child_ud->usb_n_ifs * USBA_CLIENT_FLAG_SIZE, KM_SLEEP); 57477c478bd9Sstevel@tonic-gate 57487c478bd9Sstevel@tonic-gate child_ud->usb_client_attach_list = kmem_zalloc( 5749c0f24e5bSlg child_ud->usb_n_ifs * 5750c0f24e5bSlg sizeof (*child_ud->usb_client_attach_list), KM_SLEEP); 57517c478bd9Sstevel@tonic-gate 57527c478bd9Sstevel@tonic-gate child_ud->usb_client_ev_cb_list = kmem_zalloc( 5753c0f24e5bSlg child_ud->usb_n_ifs * 5754c0f24e5bSlg sizeof (*child_ud->usb_client_ev_cb_list), KM_SLEEP); 57557c478bd9Sstevel@tonic-gate 57567c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 57577c478bd9Sstevel@tonic-gate 57587c478bd9Sstevel@tonic-gate /* ready the device node */ 57597c478bd9Sstevel@tonic-gate child_dip = usba_ready_device_node(child_dip); 57607c478bd9Sstevel@tonic-gate 57617c478bd9Sstevel@tonic-gate /* set owner of default pipe to child dip */ 57627c478bd9Sstevel@tonic-gate ph = usba_get_ph_data(def_ph); 57637c478bd9Sstevel@tonic-gate mutex_enter(&ph->p_mutex); 57647c478bd9Sstevel@tonic-gate mutex_enter(&ph->p_ph_impl->usba_ph_mutex); 57657c478bd9Sstevel@tonic-gate ph->p_ph_impl->usba_ph_dip = ph->p_dip = child_dip; 57667c478bd9Sstevel@tonic-gate mutex_exit(&ph->p_ph_impl->usba_ph_mutex); 57677c478bd9Sstevel@tonic-gate mutex_exit(&ph->p_mutex); 57687c478bd9Sstevel@tonic-gate 57697c478bd9Sstevel@tonic-gate return (child_dip); 57707c478bd9Sstevel@tonic-gate } 57717c478bd9Sstevel@tonic-gate 57727c478bd9Sstevel@tonic-gate 57737c478bd9Sstevel@tonic-gate /* 57747c478bd9Sstevel@tonic-gate * hubd_create_child 57757c478bd9Sstevel@tonic-gate * - create child dip 57767c478bd9Sstevel@tonic-gate * - open default pipe 57777c478bd9Sstevel@tonic-gate * - get device descriptor 57787c478bd9Sstevel@tonic-gate * - set the address 57797c478bd9Sstevel@tonic-gate * - get device string descriptors 57807c478bd9Sstevel@tonic-gate * - get the entire config cloud (all configurations) of the device 57817c478bd9Sstevel@tonic-gate * - set user preferred configuration 57827c478bd9Sstevel@tonic-gate * - close default pipe 57837c478bd9Sstevel@tonic-gate * - load appropriate driver(s) 57847c478bd9Sstevel@tonic-gate */ 57857c478bd9Sstevel@tonic-gate static int 57867c478bd9Sstevel@tonic-gate hubd_create_child(dev_info_t *dip, 57877c478bd9Sstevel@tonic-gate hubd_t *hubd, 57887c478bd9Sstevel@tonic-gate usba_device_t *hubd_ud, 57897c478bd9Sstevel@tonic-gate usb_port_status_t port_status, 57907c478bd9Sstevel@tonic-gate usb_port_t port, 57917c478bd9Sstevel@tonic-gate int iteration) 57927c478bd9Sstevel@tonic-gate { 57937c478bd9Sstevel@tonic-gate dev_info_t *child_dip = NULL; 57947c478bd9Sstevel@tonic-gate usb_dev_descr_t usb_dev_descr; 57957c478bd9Sstevel@tonic-gate int rval; 57967c478bd9Sstevel@tonic-gate usba_device_t *child_ud = NULL; 57977c478bd9Sstevel@tonic-gate usba_device_t *parent_ud = NULL; 57987c478bd9Sstevel@tonic-gate usb_pipe_handle_t ph = NULL; /* default pipe handle */ 57997c478bd9Sstevel@tonic-gate mblk_t *pdata = NULL; 58007c478bd9Sstevel@tonic-gate usb_cr_t completion_reason; 580135f36846Ssl int user_conf_index; 580235f36846Ssl uint_t config_index; 58037c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags; 58047c478bd9Sstevel@tonic-gate uchar_t address = 0; 58057c478bd9Sstevel@tonic-gate uint16_t length; 58067c478bd9Sstevel@tonic-gate size_t size; 58077c478bd9Sstevel@tonic-gate usb_addr_t parent_usb_addr; 58087c478bd9Sstevel@tonic-gate usb_port_t parent_usb_port; 58097c478bd9Sstevel@tonic-gate usba_device_t *parent_usba_dev; 58107c478bd9Sstevel@tonic-gate usb_port_status_t parent_port_status; 58117c478bd9Sstevel@tonic-gate 58127c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 58137c478bd9Sstevel@tonic-gate "hubd_create_child: port=%d", port); 58147c478bd9Sstevel@tonic-gate 58157c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 58167c478bd9Sstevel@tonic-gate ASSERT(hubd->h_usba_devices[port] == NULL); 58177c478bd9Sstevel@tonic-gate 58187c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 58197c478bd9Sstevel@tonic-gate 58207c478bd9Sstevel@tonic-gate /* 58217c478bd9Sstevel@tonic-gate * create a dip which can be used to open the pipe. we set 58227c478bd9Sstevel@tonic-gate * the name after getting the descriptors from the device 58237c478bd9Sstevel@tonic-gate */ 58247c478bd9Sstevel@tonic-gate rval = usba_create_child_devi(dip, 5825c0f24e5bSlg "device", /* driver name */ 5826c0f24e5bSlg hubd_ud->usb_hcdi_ops, /* usba_hcdi ops */ 5827c0f24e5bSlg hubd_ud->usb_root_hub_dip, 5828c0f24e5bSlg port_status, /* low speed device */ 5829c0f24e5bSlg child_ud, 5830c0f24e5bSlg &child_dip); 58317c478bd9Sstevel@tonic-gate 58327c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 58337c478bd9Sstevel@tonic-gate 5834d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 58357c478bd9Sstevel@tonic-gate "usb_create_child_devi failed (%d)", rval); 58367c478bd9Sstevel@tonic-gate 58377c478bd9Sstevel@tonic-gate goto fail_cleanup; 58387c478bd9Sstevel@tonic-gate } 58397c478bd9Sstevel@tonic-gate 58407c478bd9Sstevel@tonic-gate child_ud = usba_get_usba_device(child_dip); 58417c478bd9Sstevel@tonic-gate ASSERT(child_ud != NULL); 58427c478bd9Sstevel@tonic-gate 58437c478bd9Sstevel@tonic-gate parent_ud = hubd->h_usba_device; 58447c478bd9Sstevel@tonic-gate mutex_enter(&parent_ud->usb_mutex); 58457c478bd9Sstevel@tonic-gate parent_port_status = parent_ud->usb_port_status; 58467c478bd9Sstevel@tonic-gate 58477c478bd9Sstevel@tonic-gate /* 58487c478bd9Sstevel@tonic-gate * To support split transactions, update address and port 58497c478bd9Sstevel@tonic-gate * of high speed hub to which given device is connected. 58507c478bd9Sstevel@tonic-gate */ 58517c478bd9Sstevel@tonic-gate if (parent_port_status == USBA_HIGH_SPEED_DEV) { 58527c478bd9Sstevel@tonic-gate parent_usba_dev = parent_ud; 58537c478bd9Sstevel@tonic-gate parent_usb_addr = parent_ud->usb_addr; 58547c478bd9Sstevel@tonic-gate parent_usb_port = port; 58557c478bd9Sstevel@tonic-gate } else { 58567c478bd9Sstevel@tonic-gate parent_usba_dev = parent_ud->usb_hs_hub_usba_dev; 58577c478bd9Sstevel@tonic-gate parent_usb_addr = parent_ud->usb_hs_hub_addr; 58587c478bd9Sstevel@tonic-gate parent_usb_port = parent_ud->usb_hs_hub_port; 58597c478bd9Sstevel@tonic-gate } 58607c478bd9Sstevel@tonic-gate mutex_exit(&parent_ud->usb_mutex); 58617c478bd9Sstevel@tonic-gate 58627c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 58637c478bd9Sstevel@tonic-gate address = child_ud->usb_addr; 58647c478bd9Sstevel@tonic-gate child_ud->usb_addr = 0; 58657c478bd9Sstevel@tonic-gate child_ud->usb_dev_descr = kmem_alloc(sizeof (usb_dev_descr_t), 58667c478bd9Sstevel@tonic-gate KM_SLEEP); 58677c478bd9Sstevel@tonic-gate bzero(&usb_dev_descr, sizeof (usb_dev_descr_t)); 58687c478bd9Sstevel@tonic-gate usb_dev_descr.bMaxPacketSize0 = 58697c478bd9Sstevel@tonic-gate (port_status == USBA_LOW_SPEED_DEV) ? 8 : 64; 58707c478bd9Sstevel@tonic-gate bcopy(&usb_dev_descr, child_ud->usb_dev_descr, 58717c478bd9Sstevel@tonic-gate sizeof (usb_dev_descr_t)); 58727c478bd9Sstevel@tonic-gate child_ud->usb_port = port; 58737c478bd9Sstevel@tonic-gate child_ud->usb_hs_hub_usba_dev = parent_usba_dev; 58747c478bd9Sstevel@tonic-gate child_ud->usb_hs_hub_addr = parent_usb_addr; 58757c478bd9Sstevel@tonic-gate child_ud->usb_hs_hub_port = parent_usb_port; 58767c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 58777c478bd9Sstevel@tonic-gate 58787c478bd9Sstevel@tonic-gate /* Open the default pipe */ 58797c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_open(child_dip, NULL, NULL, 58807c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) != USB_SUCCESS) { 5881d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 5882d291d9f2Sfrits "usb_pipe_open failed (%d)", rval); 58837c478bd9Sstevel@tonic-gate 58847c478bd9Sstevel@tonic-gate goto fail_cleanup; 58857c478bd9Sstevel@tonic-gate } 58867c478bd9Sstevel@tonic-gate 58877c478bd9Sstevel@tonic-gate /* 58887c478bd9Sstevel@tonic-gate * get device descriptor 58897c478bd9Sstevel@tonic-gate */ 58907c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 58917c478bd9Sstevel@tonic-gate "hubd_create_child: get device descriptor: 64 bytes"); 58927c478bd9Sstevel@tonic-gate 58937c478bd9Sstevel@tonic-gate rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 58947c478bd9Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD, 58957c478bd9Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */ 58967c478bd9Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_DEV, /* wValue */ 58977c478bd9Sstevel@tonic-gate 0, /* wIndex */ 58987c478bd9Sstevel@tonic-gate 64, /* wLength */ 58997c478bd9Sstevel@tonic-gate &pdata, USB_ATTRS_SHORT_XFER_OK, 59007c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0); 59017c478bd9Sstevel@tonic-gate 59027c478bd9Sstevel@tonic-gate if ((rval != USB_SUCCESS) && 59037c478bd9Sstevel@tonic-gate (!((completion_reason == USB_CR_DATA_OVERRUN) && pdata))) { 59047c478bd9Sstevel@tonic-gate 59057c478bd9Sstevel@tonic-gate /* 59067c478bd9Sstevel@tonic-gate * rval != USB_SUCCESS AND 59077c478bd9Sstevel@tonic-gate * completion_reason != USB_CR_DATA_OVERRUN 59087c478bd9Sstevel@tonic-gate * pdata could be != NULL. 59097c478bd9Sstevel@tonic-gate * Free pdata now to prevent memory leak. 59107c478bd9Sstevel@tonic-gate */ 59117c478bd9Sstevel@tonic-gate freemsg(pdata); 59127c478bd9Sstevel@tonic-gate pdata = NULL; 59137c478bd9Sstevel@tonic-gate 59147c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 59157c478bd9Sstevel@tonic-gate "hubd_create_child: get device descriptor: 8 bytes"); 59167c478bd9Sstevel@tonic-gate 59177c478bd9Sstevel@tonic-gate rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 59187c478bd9Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD, 59197c478bd9Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */ 59207c478bd9Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_DEV, /* wValue */ 59217c478bd9Sstevel@tonic-gate 0, /* wIndex */ 59227c478bd9Sstevel@tonic-gate 8, /* wLength */ 59237c478bd9Sstevel@tonic-gate &pdata, USB_ATTRS_NONE, 59247c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0); 59257c478bd9Sstevel@tonic-gate 59267c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 5927d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 59287c478bd9Sstevel@tonic-gate "getting device descriptor failed (%s 0x%x %d)", 59297c478bd9Sstevel@tonic-gate usb_str_cr(completion_reason), cb_flags, rval); 59307c478bd9Sstevel@tonic-gate goto fail_cleanup; 59317c478bd9Sstevel@tonic-gate } 59327c478bd9Sstevel@tonic-gate } else { 59337c478bd9Sstevel@tonic-gate ASSERT(completion_reason == USB_CR_OK); 59347c478bd9Sstevel@tonic-gate } 59357c478bd9Sstevel@tonic-gate 59367c478bd9Sstevel@tonic-gate ASSERT(pdata != NULL); 59377c478bd9Sstevel@tonic-gate 59387c478bd9Sstevel@tonic-gate size = usb_parse_dev_descr( 5939c0f24e5bSlg pdata->b_rptr, 5940d29f5a71Szhigang lu - Sun Microsystems - Beijing China MBLKL(pdata), 5941c0f24e5bSlg &usb_dev_descr, 5942c0f24e5bSlg sizeof (usb_dev_descr_t)); 59437c478bd9Sstevel@tonic-gate 59447c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 59457c478bd9Sstevel@tonic-gate "parsing device descriptor returned %lu", size); 59467c478bd9Sstevel@tonic-gate 59477c478bd9Sstevel@tonic-gate length = *(pdata->b_rptr); 59487c478bd9Sstevel@tonic-gate freemsg(pdata); 59497c478bd9Sstevel@tonic-gate pdata = NULL; 59507c478bd9Sstevel@tonic-gate if (size < 8) { 5951d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 59527c478bd9Sstevel@tonic-gate "get device descriptor returned %lu bytes", size); 59537c478bd9Sstevel@tonic-gate 59547c478bd9Sstevel@tonic-gate goto fail_cleanup; 59557c478bd9Sstevel@tonic-gate } 59567c478bd9Sstevel@tonic-gate 59577c478bd9Sstevel@tonic-gate if (length < 8) { 5958d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 59597c478bd9Sstevel@tonic-gate "fail enumeration: bLength=%d", length); 59607c478bd9Sstevel@tonic-gate 59617c478bd9Sstevel@tonic-gate goto fail_cleanup; 59627c478bd9Sstevel@tonic-gate } 59637c478bd9Sstevel@tonic-gate 59647c478bd9Sstevel@tonic-gate /* Set the address of the device */ 59657c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 59667c478bd9Sstevel@tonic-gate USB_DEV_REQ_HOST_TO_DEV, 59677c478bd9Sstevel@tonic-gate USB_REQ_SET_ADDRESS, /* bRequest */ 59687c478bd9Sstevel@tonic-gate address, /* wValue */ 59697c478bd9Sstevel@tonic-gate 0, /* wIndex */ 59707c478bd9Sstevel@tonic-gate 0, /* wLength */ 59717c478bd9Sstevel@tonic-gate NULL, 0, 59727c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 59737c478bd9Sstevel@tonic-gate char buffer[64]; 5974d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 59757c478bd9Sstevel@tonic-gate "setting address failed (cr=%s cb_flags=%s rval=%d)", 59767c478bd9Sstevel@tonic-gate usb_str_cr(completion_reason), 59777c478bd9Sstevel@tonic-gate usb_str_cb_flags(cb_flags, buffer, sizeof (buffer)), 59787c478bd9Sstevel@tonic-gate rval); 59797c478bd9Sstevel@tonic-gate 59807c478bd9Sstevel@tonic-gate goto fail_cleanup; 59817c478bd9Sstevel@tonic-gate } 59827c478bd9Sstevel@tonic-gate 59837c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 59847c478bd9Sstevel@tonic-gate "set address 0x%x done", address); 59857c478bd9Sstevel@tonic-gate 59867c478bd9Sstevel@tonic-gate /* now close the pipe for addr 0 */ 59877c478bd9Sstevel@tonic-gate usb_pipe_close(child_dip, ph, 59887c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL); 59897c478bd9Sstevel@tonic-gate 59907c478bd9Sstevel@tonic-gate /* 59917c478bd9Sstevel@tonic-gate * This delay is important for the CATC hub to enumerate 59927c478bd9Sstevel@tonic-gate * But, avoid delay in the first iteration 59937c478bd9Sstevel@tonic-gate */ 59947c478bd9Sstevel@tonic-gate if (iteration) { 59957c478bd9Sstevel@tonic-gate delay(drv_usectohz(hubd_device_delay/100)); 59967c478bd9Sstevel@tonic-gate } 59977c478bd9Sstevel@tonic-gate 59987c478bd9Sstevel@tonic-gate /* assign the address in the usba_device structure */ 59997c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 60007c478bd9Sstevel@tonic-gate child_ud->usb_addr = address; 60017c478bd9Sstevel@tonic-gate child_ud->usb_no_cpr = 0; 60027c478bd9Sstevel@tonic-gate child_ud->usb_port_status = port_status; 60037c478bd9Sstevel@tonic-gate /* save this device descriptor */ 60047c478bd9Sstevel@tonic-gate bcopy(&usb_dev_descr, child_ud->usb_dev_descr, 6005c0f24e5bSlg sizeof (usb_dev_descr_t)); 60067c478bd9Sstevel@tonic-gate child_ud->usb_n_cfgs = usb_dev_descr.bNumConfigurations; 60077c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 60087c478bd9Sstevel@tonic-gate 60097c478bd9Sstevel@tonic-gate /* re-open the pipe for the device with the new address */ 60107c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_open(child_dip, NULL, NULL, 60117c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) != USB_SUCCESS) { 6012d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 60137c478bd9Sstevel@tonic-gate "usb_pipe_open failed (%d)", rval); 60147c478bd9Sstevel@tonic-gate 60157c478bd9Sstevel@tonic-gate goto fail_cleanup; 60167c478bd9Sstevel@tonic-gate } 60177c478bd9Sstevel@tonic-gate 60187c478bd9Sstevel@tonic-gate /* 60197c478bd9Sstevel@tonic-gate * Get full device descriptor only if we have not received full 60207c478bd9Sstevel@tonic-gate * device descriptor earlier. 60217c478bd9Sstevel@tonic-gate */ 60227c478bd9Sstevel@tonic-gate if (size < length) { 60237c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 60247c478bd9Sstevel@tonic-gate "hubd_create_child: get full device descriptor: " 60257c478bd9Sstevel@tonic-gate "%d bytes", length); 60267c478bd9Sstevel@tonic-gate 60277c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 60287c478bd9Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD, 60297c478bd9Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */ 60307c478bd9Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_DEV, /* wValue */ 60317c478bd9Sstevel@tonic-gate 0, /* wIndex */ 60327c478bd9Sstevel@tonic-gate length, /* wLength */ 60337c478bd9Sstevel@tonic-gate &pdata, 0, 60347c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { 60357c478bd9Sstevel@tonic-gate freemsg(pdata); 60367c478bd9Sstevel@tonic-gate pdata = NULL; 60377c478bd9Sstevel@tonic-gate 60387c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, 60397c478bd9Sstevel@tonic-gate hubd->h_log_handle, 60407c478bd9Sstevel@tonic-gate "hubd_create_child: get full device descriptor: " 60417c478bd9Sstevel@tonic-gate "64 bytes"); 60427c478bd9Sstevel@tonic-gate 60437c478bd9Sstevel@tonic-gate rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 60447c478bd9Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | 60457c478bd9Sstevel@tonic-gate USB_DEV_REQ_TYPE_STANDARD, 60467c478bd9Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */ 60477c478bd9Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_DEV, /* wValue */ 60487c478bd9Sstevel@tonic-gate 0, /* wIndex */ 60497c478bd9Sstevel@tonic-gate 64, /* wLength */ 60507c478bd9Sstevel@tonic-gate &pdata, USB_ATTRS_SHORT_XFER_OK, 60517c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0); 60527c478bd9Sstevel@tonic-gate 60537c478bd9Sstevel@tonic-gate /* we have to trust the data now */ 60547c478bd9Sstevel@tonic-gate if (pdata) { 60557c478bd9Sstevel@tonic-gate int len = *(pdata->b_rptr); 60567c478bd9Sstevel@tonic-gate 6057d29f5a71Szhigang lu - Sun Microsystems - Beijing China length = MBLKL(pdata); 60587c478bd9Sstevel@tonic-gate if (length < len) { 60597c478bd9Sstevel@tonic-gate 60607c478bd9Sstevel@tonic-gate goto fail_cleanup; 60617c478bd9Sstevel@tonic-gate } 60627c478bd9Sstevel@tonic-gate } else if (rval != USB_SUCCESS) { 6063d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 60647c478bd9Sstevel@tonic-gate hubd->h_log_handle, 60657c478bd9Sstevel@tonic-gate "getting device descriptor failed " 60667c478bd9Sstevel@tonic-gate "(%d 0x%x %d)", 6067c0f24e5bSlg completion_reason, cb_flags, rval); 60687c478bd9Sstevel@tonic-gate 60697c478bd9Sstevel@tonic-gate goto fail_cleanup; 60707c478bd9Sstevel@tonic-gate } 60717c478bd9Sstevel@tonic-gate } 60727c478bd9Sstevel@tonic-gate 60737c478bd9Sstevel@tonic-gate size = usb_parse_dev_descr( 6074c0f24e5bSlg pdata->b_rptr, 6075d29f5a71Szhigang lu - Sun Microsystems - Beijing China MBLKL(pdata), 6076c0f24e5bSlg &usb_dev_descr, 6077c0f24e5bSlg sizeof (usb_dev_descr_t)); 60787c478bd9Sstevel@tonic-gate 60797c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 60807c478bd9Sstevel@tonic-gate "parsing device descriptor returned %lu", size); 60817c478bd9Sstevel@tonic-gate 60827c478bd9Sstevel@tonic-gate /* 60837c478bd9Sstevel@tonic-gate * For now, free the data 60847c478bd9Sstevel@tonic-gate * eventually, each configuration may need to be looked at 60857c478bd9Sstevel@tonic-gate */ 60867c478bd9Sstevel@tonic-gate freemsg(pdata); 60877c478bd9Sstevel@tonic-gate pdata = NULL; 60887c478bd9Sstevel@tonic-gate 60897c478bd9Sstevel@tonic-gate if (size != USB_DEV_DESCR_SIZE) { 6090d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 60917c478bd9Sstevel@tonic-gate "fail enumeration: descriptor size=%lu " 60927c478bd9Sstevel@tonic-gate "expected size=%u", size, USB_DEV_DESCR_SIZE); 60937c478bd9Sstevel@tonic-gate 60947c478bd9Sstevel@tonic-gate goto fail_cleanup; 60957c478bd9Sstevel@tonic-gate } 60967c478bd9Sstevel@tonic-gate 60977c478bd9Sstevel@tonic-gate /* 60987c478bd9Sstevel@tonic-gate * save the device descriptor in usba_device since it is needed 60997c478bd9Sstevel@tonic-gate * later on again 61007c478bd9Sstevel@tonic-gate */ 61017c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 61027c478bd9Sstevel@tonic-gate bcopy(&usb_dev_descr, child_ud->usb_dev_descr, 6103c0f24e5bSlg sizeof (usb_dev_descr_t)); 61047c478bd9Sstevel@tonic-gate child_ud->usb_n_cfgs = usb_dev_descr.bNumConfigurations; 61057c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 61067c478bd9Sstevel@tonic-gate } 61077c478bd9Sstevel@tonic-gate 61087c478bd9Sstevel@tonic-gate if (usb_dev_descr.bNumConfigurations == 0) { 6109d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 61107c478bd9Sstevel@tonic-gate "device descriptor:\n\t" 61117c478bd9Sstevel@tonic-gate "l=0x%x type=0x%x USB=0x%x class=0x%x subclass=0x%x\n\t" 61127c478bd9Sstevel@tonic-gate "protocol=0x%x maxpktsize=0x%x " 61137c478bd9Sstevel@tonic-gate "Vid=0x%x Pid=0x%x rel=0x%x\n\t" 61147c478bd9Sstevel@tonic-gate "Mfg=0x%x P=0x%x sn=0x%x #config=0x%x", 61157c478bd9Sstevel@tonic-gate usb_dev_descr.bLength, usb_dev_descr.bDescriptorType, 61167c478bd9Sstevel@tonic-gate usb_dev_descr.bcdUSB, usb_dev_descr.bDeviceClass, 61177c478bd9Sstevel@tonic-gate usb_dev_descr.bDeviceSubClass, 61187c478bd9Sstevel@tonic-gate usb_dev_descr.bDeviceProtocol, 61197c478bd9Sstevel@tonic-gate usb_dev_descr.bMaxPacketSize0, 61207c478bd9Sstevel@tonic-gate usb_dev_descr.idVendor, 61217c478bd9Sstevel@tonic-gate usb_dev_descr.idProduct, usb_dev_descr.bcdDevice, 61227c478bd9Sstevel@tonic-gate usb_dev_descr.iManufacturer, usb_dev_descr.iProduct, 61237c478bd9Sstevel@tonic-gate usb_dev_descr.iSerialNumber, 61247c478bd9Sstevel@tonic-gate usb_dev_descr.bNumConfigurations); 61257c478bd9Sstevel@tonic-gate goto fail_cleanup; 61267c478bd9Sstevel@tonic-gate } 61277c478bd9Sstevel@tonic-gate 61287c478bd9Sstevel@tonic-gate 61297c478bd9Sstevel@tonic-gate /* get the device string descriptor(s) */ 61307c478bd9Sstevel@tonic-gate usba_get_dev_string_descrs(child_dip, child_ud); 61317c478bd9Sstevel@tonic-gate 61327c478bd9Sstevel@tonic-gate /* retrieve config cloud for all configurations */ 61337c478bd9Sstevel@tonic-gate rval = hubd_get_all_device_config_cloud(hubd, child_dip, child_ud); 61347c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 6135d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 61367c478bd9Sstevel@tonic-gate "failed to get configuration descriptor(s)"); 61377c478bd9Sstevel@tonic-gate 61387c478bd9Sstevel@tonic-gate goto fail_cleanup; 61397c478bd9Sstevel@tonic-gate } 61407c478bd9Sstevel@tonic-gate 61417c478bd9Sstevel@tonic-gate /* get the preferred configuration for this device */ 61427c478bd9Sstevel@tonic-gate user_conf_index = hubd_select_device_configuration(hubd, port, 61437c478bd9Sstevel@tonic-gate child_dip, child_ud); 61447c478bd9Sstevel@tonic-gate 61457c478bd9Sstevel@tonic-gate /* Check if the user selected configuration index is in range */ 614635f36846Ssl if ((user_conf_index >= usb_dev_descr.bNumConfigurations) || 614735f36846Ssl (user_conf_index < 0)) { 6148d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 61497c478bd9Sstevel@tonic-gate "Configuration index for device idVendor=%d " 61507c478bd9Sstevel@tonic-gate "idProduct=%d is=%d, and is out of range[0..%d]", 61517c478bd9Sstevel@tonic-gate usb_dev_descr.idVendor, usb_dev_descr.idProduct, 61527c478bd9Sstevel@tonic-gate user_conf_index, usb_dev_descr.bNumConfigurations - 1); 61537c478bd9Sstevel@tonic-gate 61547c478bd9Sstevel@tonic-gate /* treat this as user didn't specify configuration */ 61557c478bd9Sstevel@tonic-gate user_conf_index = USBA_DEV_CONFIG_INDEX_UNDEFINED; 61567c478bd9Sstevel@tonic-gate } 61577c478bd9Sstevel@tonic-gate 61587c478bd9Sstevel@tonic-gate 61597c478bd9Sstevel@tonic-gate /* 61607c478bd9Sstevel@tonic-gate * Warn users of a performance hit if connecting a 61617c478bd9Sstevel@tonic-gate * High Speed behind a 1.1 hub, which is behind a 61627c478bd9Sstevel@tonic-gate * 2.0 port. 61637c478bd9Sstevel@tonic-gate */ 61647c478bd9Sstevel@tonic-gate if ((parent_port_status != USBA_HIGH_SPEED_DEV) && 61657c478bd9Sstevel@tonic-gate !(usba_is_root_hub(parent_ud->usb_dip)) && 61667c478bd9Sstevel@tonic-gate (parent_usb_addr)) { 61677c478bd9Sstevel@tonic-gate 61687c478bd9Sstevel@tonic-gate /* 61697c478bd9Sstevel@tonic-gate * Now that we know the root port is a high speed port 61707c478bd9Sstevel@tonic-gate * and that the parent port is not a high speed port, 61717c478bd9Sstevel@tonic-gate * let's find out if the device itself is a high speed 61727c478bd9Sstevel@tonic-gate * device. If it is a high speed device, 61737c478bd9Sstevel@tonic-gate * USB_DESCR_TYPE_SETUP_DEV_QLF should return a value, 61747c478bd9Sstevel@tonic-gate * otherwise the command will fail. 61757c478bd9Sstevel@tonic-gate */ 61767c478bd9Sstevel@tonic-gate rval = usb_pipe_sync_ctrl_xfer(child_dip, ph, 61777c478bd9Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD, 61787c478bd9Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */ 61797c478bd9Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_DEV_QLF, /* wValue */ 61807c478bd9Sstevel@tonic-gate 0, /* wIndex */ 61817c478bd9Sstevel@tonic-gate 10, /* wLength */ 61827c478bd9Sstevel@tonic-gate &pdata, USB_ATTRS_SHORT_XFER_OK, 61837c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0); 61847c478bd9Sstevel@tonic-gate 61857c478bd9Sstevel@tonic-gate if (pdata) { 61867c478bd9Sstevel@tonic-gate freemsg(pdata); 61877c478bd9Sstevel@tonic-gate pdata = NULL; 61887c478bd9Sstevel@tonic-gate } 61897c478bd9Sstevel@tonic-gate 61907c478bd9Sstevel@tonic-gate /* 61917c478bd9Sstevel@tonic-gate * USB_DESCR_TYPE_SETUP_DEV_QLF query was successful 61927c478bd9Sstevel@tonic-gate * that means this is a high speed device behind a 61937c478bd9Sstevel@tonic-gate * high speed root hub, but running at full speed 61947c478bd9Sstevel@tonic-gate * because there is a full speed hub in the middle. 61957c478bd9Sstevel@tonic-gate */ 61967c478bd9Sstevel@tonic-gate if (rval == USB_SUCCESS) { 6197c0f24e5bSlg USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, 6198c0f24e5bSlg hubd->h_log_handle, 6199c0f24e5bSlg "Connecting a high speed device to a " 6200c0f24e5bSlg "non high speed hub (port %d) will result " 6201112cd14aSqz "in a loss of performance. Please connect " 6202c0f24e5bSlg "the device to a high speed hub to get " 6203c0f24e5bSlg "the maximum performance.", 6204c0f24e5bSlg port); 62057c478bd9Sstevel@tonic-gate } 62067c478bd9Sstevel@tonic-gate } 62077c478bd9Sstevel@tonic-gate 62087c478bd9Sstevel@tonic-gate /* 62097c478bd9Sstevel@tonic-gate * Now we try to online the device by attaching a driver 62107c478bd9Sstevel@tonic-gate * The following truth table illustrates the logic:- 62117c478bd9Sstevel@tonic-gate * Cfgndx Driver Action 62127c478bd9Sstevel@tonic-gate * 0 0 loop all configs for driver with full 62137c478bd9Sstevel@tonic-gate * compatible properties. 62147c478bd9Sstevel@tonic-gate * 0 1 set first configuration, 62157c478bd9Sstevel@tonic-gate * compatible prop = drivername. 62167c478bd9Sstevel@tonic-gate * 1 0 Set config, full compatible prop 62177c478bd9Sstevel@tonic-gate * 1 1 Set config, compatible prop = drivername. 62187c478bd9Sstevel@tonic-gate * 62197c478bd9Sstevel@tonic-gate * Note: 62207c478bd9Sstevel@tonic-gate * cfgndx = user_conf_index 62217c478bd9Sstevel@tonic-gate * Driver = usb_preferred_driver 62227c478bd9Sstevel@tonic-gate */ 62237c478bd9Sstevel@tonic-gate if (user_conf_index == USBA_DEV_CONFIG_INDEX_UNDEFINED) { 62247c478bd9Sstevel@tonic-gate if (child_ud->usb_preferred_driver) { 62257c478bd9Sstevel@tonic-gate /* 62267c478bd9Sstevel@tonic-gate * It is the job of the "preferred driver" to put the 62277c478bd9Sstevel@tonic-gate * device in the desired configuration. Till then 62287c478bd9Sstevel@tonic-gate * put the device in config index 0. 62297c478bd9Sstevel@tonic-gate */ 623035f36846Ssl if ((rval = usba_hubdi_check_power_budget(dip, child_ud, 623135f36846Ssl USB_DEV_DEFAULT_CONFIG_INDEX)) != USB_SUCCESS) { 623235f36846Ssl 623335f36846Ssl goto fail_cleanup; 623435f36846Ssl } 623535f36846Ssl 62367c478bd9Sstevel@tonic-gate child_dip = hubd_ready_device(hubd, child_dip, 62377c478bd9Sstevel@tonic-gate child_ud, USB_DEV_DEFAULT_CONFIG_INDEX); 62387c478bd9Sstevel@tonic-gate 62397c478bd9Sstevel@tonic-gate /* 62407c478bd9Sstevel@tonic-gate * Assign the dip before onlining to avoid race 62417c478bd9Sstevel@tonic-gate * with busctl 62427c478bd9Sstevel@tonic-gate */ 62437c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 62447c478bd9Sstevel@tonic-gate hubd->h_children_dips[port] = child_dip; 62457c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 62467c478bd9Sstevel@tonic-gate 62477c478bd9Sstevel@tonic-gate (void) usba_bind_driver(child_dip); 62487c478bd9Sstevel@tonic-gate } else { 62497c478bd9Sstevel@tonic-gate /* 62507c478bd9Sstevel@tonic-gate * loop through all the configurations to see if we 62517c478bd9Sstevel@tonic-gate * can find a driver for any one config. If not, set 62527c478bd9Sstevel@tonic-gate * the device in config_index 0 62537c478bd9Sstevel@tonic-gate */ 62547c478bd9Sstevel@tonic-gate rval = USB_FAILURE; 62557c478bd9Sstevel@tonic-gate for (config_index = 0; 62567c478bd9Sstevel@tonic-gate (config_index < usb_dev_descr.bNumConfigurations) && 62577c478bd9Sstevel@tonic-gate (rval != USB_SUCCESS); config_index++) { 62587c478bd9Sstevel@tonic-gate 62597c478bd9Sstevel@tonic-gate child_dip = hubd_ready_device(hubd, child_dip, 62607c478bd9Sstevel@tonic-gate child_ud, config_index); 62617c478bd9Sstevel@tonic-gate 62627c478bd9Sstevel@tonic-gate /* 62637c478bd9Sstevel@tonic-gate * Assign the dip before onlining to avoid race 62647c478bd9Sstevel@tonic-gate * with busctl 62657c478bd9Sstevel@tonic-gate */ 62667c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 62677c478bd9Sstevel@tonic-gate hubd->h_children_dips[port] = child_dip; 62687c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 62697c478bd9Sstevel@tonic-gate 62707c478bd9Sstevel@tonic-gate rval = usba_bind_driver(child_dip); 627135f36846Ssl 627235f36846Ssl /* 627335f36846Ssl * Normally power budget should be checked 627435f36846Ssl * before device is configured. A failure in 627535f36846Ssl * power budget checking will stop the device 627635f36846Ssl * from being configured with current 627735f36846Ssl * config_index and may enable the device to 627835f36846Ssl * be configured in another configuration. 627935f36846Ssl * This may break the user experience that a 628035f36846Ssl * device which previously worked in config 628135f36846Ssl * A now works in config B after power budget 628235f36846Ssl * control is enabled. To avoid such situation, 628335f36846Ssl * power budget checking is moved here and will 628435f36846Ssl * fail the child creation directly if config 628535f36846Ssl * A exceeds the power available. 628635f36846Ssl */ 628735f36846Ssl if (rval == USB_SUCCESS) { 628835f36846Ssl if ((usba_hubdi_check_power_budget(dip, 628935f36846Ssl child_ud, config_index)) != 629035f36846Ssl USB_SUCCESS) { 629135f36846Ssl 629235f36846Ssl goto fail_cleanup; 629335f36846Ssl } 629435f36846Ssl } 62957c478bd9Sstevel@tonic-gate } 62967c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 629735f36846Ssl 629835f36846Ssl if ((usba_hubdi_check_power_budget(dip, 629935f36846Ssl child_ud, 0)) != USB_SUCCESS) { 630035f36846Ssl 630135f36846Ssl goto fail_cleanup; 630235f36846Ssl } 630335f36846Ssl 63047c478bd9Sstevel@tonic-gate child_dip = hubd_ready_device(hubd, child_dip, 63057c478bd9Sstevel@tonic-gate child_ud, 0); 63067c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 63077c478bd9Sstevel@tonic-gate hubd->h_children_dips[port] = child_dip; 63087c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 63097c478bd9Sstevel@tonic-gate } 63107c478bd9Sstevel@tonic-gate } /* end else loop all configs */ 63117c478bd9Sstevel@tonic-gate } else { 631235f36846Ssl 631335f36846Ssl if ((usba_hubdi_check_power_budget(dip, child_ud, 631435f36846Ssl (uint_t)user_conf_index)) != USB_SUCCESS) { 631535f36846Ssl 631635f36846Ssl goto fail_cleanup; 631735f36846Ssl } 631835f36846Ssl 63197c478bd9Sstevel@tonic-gate child_dip = hubd_ready_device(hubd, child_dip, 632035f36846Ssl child_ud, (uint_t)user_conf_index); 63217c478bd9Sstevel@tonic-gate 63227c478bd9Sstevel@tonic-gate /* 63237c478bd9Sstevel@tonic-gate * Assign the dip before onlining to avoid race 63247c478bd9Sstevel@tonic-gate * with busctl 63257c478bd9Sstevel@tonic-gate */ 63267c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 63277c478bd9Sstevel@tonic-gate hubd->h_children_dips[port] = child_dip; 63287c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 63297c478bd9Sstevel@tonic-gate 63307c478bd9Sstevel@tonic-gate (void) usba_bind_driver(child_dip); 63317c478bd9Sstevel@tonic-gate } 63327c478bd9Sstevel@tonic-gate 633335f36846Ssl usba_hubdi_decr_power_budget(dip, child_ud); 633435f36846Ssl 63357c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 63367c478bd9Sstevel@tonic-gate if (hubd->h_usba_devices[port] == NULL) { 63377c478bd9Sstevel@tonic-gate hubd->h_usba_devices[port] = usba_get_usba_device(child_dip); 63387c478bd9Sstevel@tonic-gate } else { 63397c478bd9Sstevel@tonic-gate ASSERT(hubd->h_usba_devices[port] == 6340c0f24e5bSlg usba_get_usba_device(child_dip)); 63417c478bd9Sstevel@tonic-gate } 63427c478bd9Sstevel@tonic-gate 63437c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 63447c478bd9Sstevel@tonic-gate 63457c478bd9Sstevel@tonic-gate 63467c478bd9Sstevel@tonic-gate fail_cleanup: 63477c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 63487c478bd9Sstevel@tonic-gate "hubd_create_child: fail_cleanup"); 63497c478bd9Sstevel@tonic-gate 63507c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 63517c478bd9Sstevel@tonic-gate hubd->h_children_dips[port] = NULL; 63527c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 63537c478bd9Sstevel@tonic-gate 63547c478bd9Sstevel@tonic-gate if (pdata) { 63557c478bd9Sstevel@tonic-gate freemsg(pdata); 63567c478bd9Sstevel@tonic-gate } 63577c478bd9Sstevel@tonic-gate 63587c478bd9Sstevel@tonic-gate if (ph) { 63597c478bd9Sstevel@tonic-gate usb_pipe_close(child_dip, ph, 63607c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL); 63617c478bd9Sstevel@tonic-gate } 63627c478bd9Sstevel@tonic-gate 63637c478bd9Sstevel@tonic-gate if (child_dip) { 63647c478bd9Sstevel@tonic-gate int rval = usba_destroy_child_devi(child_dip, 63657c478bd9Sstevel@tonic-gate NDI_DEVI_REMOVE); 63667c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 6367d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 63687c478bd9Sstevel@tonic-gate "failure to remove child node"); 63697c478bd9Sstevel@tonic-gate } 63707c478bd9Sstevel@tonic-gate } 63717c478bd9Sstevel@tonic-gate 63727c478bd9Sstevel@tonic-gate if (child_ud) { 63737c478bd9Sstevel@tonic-gate /* to make sure we free the address */ 63747c478bd9Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex); 63757c478bd9Sstevel@tonic-gate child_ud->usb_addr = address; 63767c478bd9Sstevel@tonic-gate ASSERT(child_ud->usb_ref_count == 0); 63777c478bd9Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex); 63787c478bd9Sstevel@tonic-gate 63797c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 63807c478bd9Sstevel@tonic-gate if (hubd->h_usba_devices[port] == NULL) { 63817c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 63827c478bd9Sstevel@tonic-gate usba_free_usba_device(child_ud); 63837c478bd9Sstevel@tonic-gate } else { 63847c478bd9Sstevel@tonic-gate hubd_free_usba_device(hubd, hubd->h_usba_devices[port]); 63857c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 63867c478bd9Sstevel@tonic-gate } 63877c478bd9Sstevel@tonic-gate } 63887c478bd9Sstevel@tonic-gate 63897c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 63907c478bd9Sstevel@tonic-gate 63917c478bd9Sstevel@tonic-gate return (USB_FAILURE); 63927c478bd9Sstevel@tonic-gate } 63937c478bd9Sstevel@tonic-gate 63947c478bd9Sstevel@tonic-gate 63957c478bd9Sstevel@tonic-gate /* 63967c478bd9Sstevel@tonic-gate * hubd_delete_child: 63977c478bd9Sstevel@tonic-gate * - free usb address 63987c478bd9Sstevel@tonic-gate * - lookup child dips, there may be multiple on this port 63997c478bd9Sstevel@tonic-gate * - offline each child devi 64007c478bd9Sstevel@tonic-gate */ 64017c478bd9Sstevel@tonic-gate static int 64027c478bd9Sstevel@tonic-gate hubd_delete_child(hubd_t *hubd, usb_port_t port, uint_t flag, boolean_t retry) 64037c478bd9Sstevel@tonic-gate { 64047c478bd9Sstevel@tonic-gate dev_info_t *child_dip; 64057c478bd9Sstevel@tonic-gate usba_device_t *usba_device; 64067c478bd9Sstevel@tonic-gate int rval = USB_SUCCESS; 64077c478bd9Sstevel@tonic-gate 64087c478bd9Sstevel@tonic-gate child_dip = hubd->h_children_dips[port]; 640935f36846Ssl usba_device = hubd->h_usba_devices[port]; 64107c478bd9Sstevel@tonic-gate 64117c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 64127c478bd9Sstevel@tonic-gate "hubd_delete_child: port=%d, dip=0x%p usba_device=0x%p", 6413112116d8Sfb port, (void *)child_dip, (void *)usba_device); 64147c478bd9Sstevel@tonic-gate 64157c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 64167c478bd9Sstevel@tonic-gate if (child_dip) { 64177c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 64187c478bd9Sstevel@tonic-gate "hubd_delete_child:\n\t" 64197c478bd9Sstevel@tonic-gate "dip = 0x%p (%s) at port %d", 6420112116d8Sfb (void *)child_dip, ddi_node_name(child_dip), port); 64217c478bd9Sstevel@tonic-gate 642235f36846Ssl if (usba_device) { 642335f36846Ssl usba_hubdi_incr_power_budget(hubd->h_dip, usba_device); 642435f36846Ssl } 642535f36846Ssl 64267c478bd9Sstevel@tonic-gate rval = usba_destroy_child_devi(child_dip, flag); 64277c478bd9Sstevel@tonic-gate 6428ff0e937bSRaymond Chen if ((rval != USB_SUCCESS) && usba_is_hwa(child_dip)) { 6429ff0e937bSRaymond Chen /* 6430ff0e937bSRaymond Chen * This is only useful for HWA device node. 6431ff0e937bSRaymond Chen * Since hwahc interface must hold hwarc interface 6432ff0e937bSRaymond Chen * open until hwahc is detached, the first call to 6433ff0e937bSRaymond Chen * ndi_devi_unconfig_one() can only offline hwahc 6434ff0e937bSRaymond Chen * driver but not hwarc driver. Need to make a second 6435ff0e937bSRaymond Chen * call to ndi_devi_unconfig_one() to make the hwarc 6436ff0e937bSRaymond Chen * driver detach. 6437ff0e937bSRaymond Chen */ 6438ff0e937bSRaymond Chen rval = usba_destroy_child_devi(child_dip, flag); 6439ff0e937bSRaymond Chen } 6440ff0e937bSRaymond Chen 64417c478bd9Sstevel@tonic-gate if ((rval == USB_SUCCESS) && (flag & NDI_DEVI_REMOVE)) { 64427c478bd9Sstevel@tonic-gate /* 64437c478bd9Sstevel@tonic-gate * if the child was still < DS_INITIALIZED 64447c478bd9Sstevel@tonic-gate * then our bus_unconfig was not called and 64457c478bd9Sstevel@tonic-gate * we have to zap the child here 64467c478bd9Sstevel@tonic-gate */ 64477c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 64487c478bd9Sstevel@tonic-gate if (hubd->h_children_dips[port] == child_dip) { 64497c478bd9Sstevel@tonic-gate usba_device_t *ud = 6450c0f24e5bSlg hubd->h_usba_devices[port]; 6451ffcd51f3Slg hubd->h_children_dips[port] = NULL; 64527c478bd9Sstevel@tonic-gate if (ud) { 64537c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 64547c478bd9Sstevel@tonic-gate 64557c478bd9Sstevel@tonic-gate mutex_enter(&ud->usb_mutex); 64567c478bd9Sstevel@tonic-gate ud->usb_ref_count = 0; 64577c478bd9Sstevel@tonic-gate mutex_exit(&ud->usb_mutex); 64587c478bd9Sstevel@tonic-gate 64597c478bd9Sstevel@tonic-gate usba_free_usba_device(ud); 64607c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 64617c478bd9Sstevel@tonic-gate hubd->h_usba_devices[port] = NULL; 64627c478bd9Sstevel@tonic-gate } 64637c478bd9Sstevel@tonic-gate } 64647c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 64657c478bd9Sstevel@tonic-gate } 64667c478bd9Sstevel@tonic-gate } 64677c478bd9Sstevel@tonic-gate 64687c478bd9Sstevel@tonic-gate if ((rval != USB_SUCCESS) && retry) { 64697c478bd9Sstevel@tonic-gate 64707c478bd9Sstevel@tonic-gate hubd_schedule_cleanup(usba_device->usb_root_hub_dip); 64717c478bd9Sstevel@tonic-gate } 64727c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 64737c478bd9Sstevel@tonic-gate 64747c478bd9Sstevel@tonic-gate return (rval); 64757c478bd9Sstevel@tonic-gate } 64767c478bd9Sstevel@tonic-gate 64777c478bd9Sstevel@tonic-gate 64787c478bd9Sstevel@tonic-gate /* 64797c478bd9Sstevel@tonic-gate * hubd_free_usba_device: 64807c478bd9Sstevel@tonic-gate * free usb device structure unless it is associated with 64817c478bd9Sstevel@tonic-gate * the root hub which is handled differently 64827c478bd9Sstevel@tonic-gate */ 64837c478bd9Sstevel@tonic-gate static void 64847c478bd9Sstevel@tonic-gate hubd_free_usba_device(hubd_t *hubd, usba_device_t *usba_device) 64857c478bd9Sstevel@tonic-gate { 64867c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 64877c478bd9Sstevel@tonic-gate "hubd_free_usba_device: hubd=0x%p, usba_device=0x%p", 6488112116d8Sfb (void *)hubd, (void *)usba_device); 64897c478bd9Sstevel@tonic-gate 64907c478bd9Sstevel@tonic-gate if (usba_device && (usba_device->usb_addr != ROOT_HUB_ADDR)) { 64917c478bd9Sstevel@tonic-gate usb_port_t port = usba_device->usb_port; 64927c478bd9Sstevel@tonic-gate dev_info_t *dip = hubd->h_children_dips[port]; 64937c478bd9Sstevel@tonic-gate 64947c478bd9Sstevel@tonic-gate #ifdef DEBUG 64957c478bd9Sstevel@tonic-gate if (dip) { 64967c478bd9Sstevel@tonic-gate ASSERT(i_ddi_node_state(dip) < DS_INITIALIZED); 64977c478bd9Sstevel@tonic-gate } 64987c478bd9Sstevel@tonic-gate #endif 64997c478bd9Sstevel@tonic-gate 65007c478bd9Sstevel@tonic-gate port = usba_device->usb_port; 65017c478bd9Sstevel@tonic-gate hubd->h_usba_devices[port] = NULL; 65027c478bd9Sstevel@tonic-gate 65037c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 65047c478bd9Sstevel@tonic-gate usba_free_usba_device(usba_device); 65057c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 65067c478bd9Sstevel@tonic-gate } 65077c478bd9Sstevel@tonic-gate } 65087c478bd9Sstevel@tonic-gate 65097c478bd9Sstevel@tonic-gate 65107c478bd9Sstevel@tonic-gate /* 65117c478bd9Sstevel@tonic-gate * event support 65127c478bd9Sstevel@tonic-gate * 65137c478bd9Sstevel@tonic-gate * busctl event support 65147c478bd9Sstevel@tonic-gate */ 65157c478bd9Sstevel@tonic-gate static int 65167c478bd9Sstevel@tonic-gate hubd_busop_get_eventcookie(dev_info_t *dip, 65177c478bd9Sstevel@tonic-gate dev_info_t *rdip, 65187c478bd9Sstevel@tonic-gate char *eventname, 65197c478bd9Sstevel@tonic-gate ddi_eventcookie_t *cookie) 65207c478bd9Sstevel@tonic-gate { 65217c478bd9Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip); 65227c478bd9Sstevel@tonic-gate 65237c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 65247c478bd9Sstevel@tonic-gate "hubd_busop_get_eventcookie: dip=0x%p, rdip=0x%p, " 65257c478bd9Sstevel@tonic-gate "event=%s", (void *)dip, (void *)rdip, eventname); 65267c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 65277c478bd9Sstevel@tonic-gate "(dip=%s%d, rdip=%s%d)", 65287c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), 65297c478bd9Sstevel@tonic-gate ddi_driver_name(rdip), ddi_get_instance(rdip)); 65307c478bd9Sstevel@tonic-gate 65317c478bd9Sstevel@tonic-gate /* return event cookie, iblock cookie, and level */ 65327c478bd9Sstevel@tonic-gate return (ndi_event_retrieve_cookie(hubd->h_ndi_event_hdl, 6533c0f24e5bSlg rdip, eventname, cookie, NDI_EVENT_NOPASS)); 65347c478bd9Sstevel@tonic-gate } 65357c478bd9Sstevel@tonic-gate 65367c478bd9Sstevel@tonic-gate 65377c478bd9Sstevel@tonic-gate static int 65387c478bd9Sstevel@tonic-gate hubd_busop_add_eventcall(dev_info_t *dip, 65397c478bd9Sstevel@tonic-gate dev_info_t *rdip, 65407c478bd9Sstevel@tonic-gate ddi_eventcookie_t cookie, 65417c478bd9Sstevel@tonic-gate void (*callback)(dev_info_t *dip, 65427c478bd9Sstevel@tonic-gate ddi_eventcookie_t cookie, void *arg, 65437c478bd9Sstevel@tonic-gate void *bus_impldata), 65447c478bd9Sstevel@tonic-gate void *arg, ddi_callback_id_t *cb_id) 65457c478bd9Sstevel@tonic-gate { 65467c478bd9Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip); 65477c478bd9Sstevel@tonic-gate usb_port_t port = hubd_child_dip2port(hubd, rdip); 65487c478bd9Sstevel@tonic-gate 65497c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 65507c478bd9Sstevel@tonic-gate "hubd_busop_add_eventcall: dip=0x%p, rdip=0x%p " 65517c478bd9Sstevel@tonic-gate "cookie=0x%p, cb=0x%p, arg=0x%p", 65527c478bd9Sstevel@tonic-gate (void *)dip, (void *)rdip, (void *)cookie, (void *)callback, arg); 65537c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 65547c478bd9Sstevel@tonic-gate "(dip=%s%d, rdip=%s%d, event=%s)", 65557c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), 65567c478bd9Sstevel@tonic-gate ddi_driver_name(rdip), ddi_get_instance(rdip), 65577c478bd9Sstevel@tonic-gate ndi_event_cookie_to_name(hubd->h_ndi_event_hdl, cookie)); 65587c478bd9Sstevel@tonic-gate 65597c478bd9Sstevel@tonic-gate /* Set flag on children registering events */ 65607c478bd9Sstevel@tonic-gate switch (ndi_event_cookie_to_tag(hubd->h_ndi_event_hdl, cookie)) { 65617c478bd9Sstevel@tonic-gate case USBA_EVENT_TAG_HOT_REMOVAL: 65627c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 65637c478bd9Sstevel@tonic-gate hubd->h_child_events[port] |= HUBD_CHILD_EVENT_DISCONNECT; 65647c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 65657c478bd9Sstevel@tonic-gate 65667c478bd9Sstevel@tonic-gate break; 65677c478bd9Sstevel@tonic-gate case USBA_EVENT_TAG_PRE_SUSPEND: 65687c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 65697c478bd9Sstevel@tonic-gate hubd->h_child_events[port] |= HUBD_CHILD_EVENT_PRESUSPEND; 65707c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 65717c478bd9Sstevel@tonic-gate 65727c478bd9Sstevel@tonic-gate break; 65737c478bd9Sstevel@tonic-gate default: 65747c478bd9Sstevel@tonic-gate 65757c478bd9Sstevel@tonic-gate break; 65767c478bd9Sstevel@tonic-gate } 65777c478bd9Sstevel@tonic-gate 65787c478bd9Sstevel@tonic-gate /* add callback to our event set */ 65797c478bd9Sstevel@tonic-gate return (ndi_event_add_callback(hubd->h_ndi_event_hdl, 6580c0f24e5bSlg rdip, cookie, callback, arg, NDI_SLEEP, cb_id)); 65817c478bd9Sstevel@tonic-gate } 65827c478bd9Sstevel@tonic-gate 65837c478bd9Sstevel@tonic-gate 65847c478bd9Sstevel@tonic-gate static int 65857c478bd9Sstevel@tonic-gate hubd_busop_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id) 65867c478bd9Sstevel@tonic-gate { 65877c478bd9Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip); 65887c478bd9Sstevel@tonic-gate ndi_event_callbacks_t *id = (ndi_event_callbacks_t *)cb_id; 65897c478bd9Sstevel@tonic-gate 65907c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 65917c478bd9Sstevel@tonic-gate "hubd_busop_remove_eventcall: dip=0x%p, rdip=0x%p " 6592112116d8Sfb "cookie=0x%p", (void *)dip, (void *)id->ndi_evtcb_dip, 6593112116d8Sfb (void *)id->ndi_evtcb_cookie); 65947c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 65957c478bd9Sstevel@tonic-gate "(dip=%s%d, rdip=%s%d, event=%s)", 65967c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), 65977c478bd9Sstevel@tonic-gate ddi_driver_name(id->ndi_evtcb_dip), 65987c478bd9Sstevel@tonic-gate ddi_get_instance(id->ndi_evtcb_dip), 65997c478bd9Sstevel@tonic-gate ndi_event_cookie_to_name(hubd->h_ndi_event_hdl, 66007c478bd9Sstevel@tonic-gate id->ndi_evtcb_cookie)); 66017c478bd9Sstevel@tonic-gate 66027c478bd9Sstevel@tonic-gate /* remove event registration from our event set */ 66037c478bd9Sstevel@tonic-gate return (ndi_event_remove_callback(hubd->h_ndi_event_hdl, cb_id)); 66047c478bd9Sstevel@tonic-gate } 66057c478bd9Sstevel@tonic-gate 66067c478bd9Sstevel@tonic-gate 66077c478bd9Sstevel@tonic-gate /* 66087c478bd9Sstevel@tonic-gate * event distribution 66097c478bd9Sstevel@tonic-gate * 66107c478bd9Sstevel@tonic-gate * hubd_do_callback: 66117c478bd9Sstevel@tonic-gate * Post this event to the specified child 66127c478bd9Sstevel@tonic-gate */ 66137c478bd9Sstevel@tonic-gate static void 66147c478bd9Sstevel@tonic-gate hubd_do_callback(hubd_t *hubd, dev_info_t *cdip, ddi_eventcookie_t cookie) 66157c478bd9Sstevel@tonic-gate { 66167c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 66177c478bd9Sstevel@tonic-gate "hubd_do_callback"); 66187c478bd9Sstevel@tonic-gate 66197c478bd9Sstevel@tonic-gate (void) ndi_event_do_callback(hubd->h_ndi_event_hdl, cdip, cookie, NULL); 66207c478bd9Sstevel@tonic-gate } 66217c478bd9Sstevel@tonic-gate 66227c478bd9Sstevel@tonic-gate 66237c478bd9Sstevel@tonic-gate /* 66247c478bd9Sstevel@tonic-gate * hubd_run_callbacks: 66257c478bd9Sstevel@tonic-gate * Send this event to all children 66267c478bd9Sstevel@tonic-gate */ 66277c478bd9Sstevel@tonic-gate static void 66287c478bd9Sstevel@tonic-gate hubd_run_callbacks(hubd_t *hubd, usba_event_t type) 66297c478bd9Sstevel@tonic-gate { 66307c478bd9Sstevel@tonic-gate usb_port_t port; 66317c478bd9Sstevel@tonic-gate 66327c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 66337c478bd9Sstevel@tonic-gate "hubd_run_callbacks"); 66347c478bd9Sstevel@tonic-gate 66357c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 66367c478bd9Sstevel@tonic-gate for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { 66377c478bd9Sstevel@tonic-gate /* 66387c478bd9Sstevel@tonic-gate * the childen_dips list may have dips that have been 66397c478bd9Sstevel@tonic-gate * already deallocated. we only get a post_detach notification 66407c478bd9Sstevel@tonic-gate * but not a destroy notification 66417c478bd9Sstevel@tonic-gate */ 66427c478bd9Sstevel@tonic-gate if (hubd->h_children_dips[port]) { 66437c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 66447c478bd9Sstevel@tonic-gate hubd_post_event(hubd, port, type); 66457c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 66467c478bd9Sstevel@tonic-gate } 66477c478bd9Sstevel@tonic-gate } 66487c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 66497c478bd9Sstevel@tonic-gate } 66507c478bd9Sstevel@tonic-gate 66517c478bd9Sstevel@tonic-gate 66527c478bd9Sstevel@tonic-gate /* 66537c478bd9Sstevel@tonic-gate * hubd_post_event 66547c478bd9Sstevel@tonic-gate * post event to a child on the port depending on the type 66557c478bd9Sstevel@tonic-gate */ 66567c478bd9Sstevel@tonic-gate static void 66577c478bd9Sstevel@tonic-gate hubd_post_event(hubd_t *hubd, usb_port_t port, usba_event_t type) 66587c478bd9Sstevel@tonic-gate { 66597c478bd9Sstevel@tonic-gate int rval; 66607c478bd9Sstevel@tonic-gate dev_info_t *dip; 66617c478bd9Sstevel@tonic-gate usba_device_t *usba_device; 66627c478bd9Sstevel@tonic-gate ddi_eventcookie_t cookie, rm_cookie, suspend_cookie; 66637c478bd9Sstevel@tonic-gate 66647c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 66657c478bd9Sstevel@tonic-gate "hubd_post_event: port=%d event=%s", port, 66667c478bd9Sstevel@tonic-gate ndi_event_tag_to_name(hubd->h_ndi_event_hdl, type)); 66677c478bd9Sstevel@tonic-gate 66687c478bd9Sstevel@tonic-gate cookie = ndi_event_tag_to_cookie(hubd->h_ndi_event_hdl, type); 66697c478bd9Sstevel@tonic-gate rm_cookie = ndi_event_tag_to_cookie(hubd->h_ndi_event_hdl, 66707c478bd9Sstevel@tonic-gate USBA_EVENT_TAG_HOT_REMOVAL); 66717c478bd9Sstevel@tonic-gate suspend_cookie = ndi_event_tag_to_cookie(hubd->h_ndi_event_hdl, 66727c478bd9Sstevel@tonic-gate USBA_EVENT_TAG_PRE_SUSPEND); 66737c478bd9Sstevel@tonic-gate 66747c478bd9Sstevel@tonic-gate /* 66757c478bd9Sstevel@tonic-gate * Hotplug daemon may be attaching a driver that may be registering 66767c478bd9Sstevel@tonic-gate * event callbacks. So it already has got the device tree lock and 66777c478bd9Sstevel@tonic-gate * event handle mutex. So to prevent a deadlock while posting events, 66787c478bd9Sstevel@tonic-gate * we grab and release the locks in the same order. 66797c478bd9Sstevel@tonic-gate */ 66807c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 66817c478bd9Sstevel@tonic-gate dip = hubd->h_children_dips[port]; 66827c478bd9Sstevel@tonic-gate usba_device = hubd->h_usba_devices[port]; 66837c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 66847c478bd9Sstevel@tonic-gate 66857c478bd9Sstevel@tonic-gate switch (type) { 66867c478bd9Sstevel@tonic-gate case USBA_EVENT_TAG_HOT_REMOVAL: 66877c478bd9Sstevel@tonic-gate /* Clear the registered event flag */ 66887c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 66897c478bd9Sstevel@tonic-gate hubd->h_child_events[port] &= ~HUBD_CHILD_EVENT_DISCONNECT; 66907c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 66917c478bd9Sstevel@tonic-gate 66927c478bd9Sstevel@tonic-gate hubd_do_callback(hubd, dip, cookie); 66937c478bd9Sstevel@tonic-gate usba_persistent_pipe_close(usba_device); 66947c478bd9Sstevel@tonic-gate 66957c478bd9Sstevel@tonic-gate /* 66967c478bd9Sstevel@tonic-gate * Mark the dip for deletion only after the driver has 66977c478bd9Sstevel@tonic-gate * seen the disconnect event to prevent cleanup thread 66987c478bd9Sstevel@tonic-gate * from stepping in between. 66997c478bd9Sstevel@tonic-gate */ 670016747f41Scth mutex_enter(&(DEVI(dip)->devi_lock)); 67017c478bd9Sstevel@tonic-gate DEVI_SET_DEVICE_REMOVED(dip); 670216747f41Scth mutex_exit(&(DEVI(dip)->devi_lock)); 67037c478bd9Sstevel@tonic-gate 67047c478bd9Sstevel@tonic-gate break; 67057c478bd9Sstevel@tonic-gate case USBA_EVENT_TAG_PRE_SUSPEND: 67067c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 67077c478bd9Sstevel@tonic-gate hubd->h_child_events[port] &= ~HUBD_CHILD_EVENT_PRESUSPEND; 67087c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 67097c478bd9Sstevel@tonic-gate 67107c478bd9Sstevel@tonic-gate hubd_do_callback(hubd, dip, cookie); 67117c478bd9Sstevel@tonic-gate /* 67127c478bd9Sstevel@tonic-gate * persistent pipe close for this event is taken care by the 67137c478bd9Sstevel@tonic-gate * caller after verfying that all children can suspend 67147c478bd9Sstevel@tonic-gate */ 67157c478bd9Sstevel@tonic-gate 67167c478bd9Sstevel@tonic-gate break; 67177c478bd9Sstevel@tonic-gate case USBA_EVENT_TAG_HOT_INSERTION: 67187c478bd9Sstevel@tonic-gate /* 67197c478bd9Sstevel@tonic-gate * Check if this child has missed the disconnect event before 67207c478bd9Sstevel@tonic-gate * it registered for event callbacks 67217c478bd9Sstevel@tonic-gate */ 67227c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 67237c478bd9Sstevel@tonic-gate if (hubd->h_child_events[port] & HUBD_CHILD_EVENT_DISCONNECT) { 67247c478bd9Sstevel@tonic-gate /* clear the flag and post disconnect event */ 67257c478bd9Sstevel@tonic-gate hubd->h_child_events[port] &= 67267c478bd9Sstevel@tonic-gate ~HUBD_CHILD_EVENT_DISCONNECT; 67277c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 67287c478bd9Sstevel@tonic-gate hubd_do_callback(hubd, dip, rm_cookie); 67297c478bd9Sstevel@tonic-gate usba_persistent_pipe_close(usba_device); 67307c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 67317c478bd9Sstevel@tonic-gate } 67327c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 67337c478bd9Sstevel@tonic-gate 67347c478bd9Sstevel@tonic-gate /* 67357c478bd9Sstevel@tonic-gate * Mark the dip as reinserted to prevent cleanup thread 67367c478bd9Sstevel@tonic-gate * from stepping in. 67377c478bd9Sstevel@tonic-gate */ 673816747f41Scth mutex_enter(&(DEVI(dip)->devi_lock)); 67397c478bd9Sstevel@tonic-gate DEVI_SET_DEVICE_REINSERTED(dip); 674016747f41Scth mutex_exit(&(DEVI(dip)->devi_lock)); 67417c478bd9Sstevel@tonic-gate 67427c478bd9Sstevel@tonic-gate rval = usba_persistent_pipe_open(usba_device); 67437c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) { 6744d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 67457c478bd9Sstevel@tonic-gate hubd->h_log_handle, 67467c478bd9Sstevel@tonic-gate "failed to reopen all pipes on reconnect"); 67477c478bd9Sstevel@tonic-gate } 67487c478bd9Sstevel@tonic-gate 67497c478bd9Sstevel@tonic-gate hubd_do_callback(hubd, dip, cookie); 67507c478bd9Sstevel@tonic-gate 67517c478bd9Sstevel@tonic-gate /* 67527c478bd9Sstevel@tonic-gate * We might see a connect event only if hotplug thread for 67537c478bd9Sstevel@tonic-gate * disconnect event don't run in time. 67547c478bd9Sstevel@tonic-gate * Set the flag again, so we don't miss posting a 67557c478bd9Sstevel@tonic-gate * disconnect event. 67567c478bd9Sstevel@tonic-gate */ 67577c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 67587c478bd9Sstevel@tonic-gate hubd->h_child_events[port] |= HUBD_CHILD_EVENT_DISCONNECT; 67597c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 67607c478bd9Sstevel@tonic-gate 67617c478bd9Sstevel@tonic-gate break; 67627c478bd9Sstevel@tonic-gate case USBA_EVENT_TAG_POST_RESUME: 67637c478bd9Sstevel@tonic-gate /* 67647c478bd9Sstevel@tonic-gate * Check if this child has missed the pre-suspend event before 67657c478bd9Sstevel@tonic-gate * it registered for event callbacks 67667c478bd9Sstevel@tonic-gate */ 67677c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 67687c478bd9Sstevel@tonic-gate if (hubd->h_child_events[port] & HUBD_CHILD_EVENT_PRESUSPEND) { 67697c478bd9Sstevel@tonic-gate /* clear the flag and post pre_suspend event */ 67707c478bd9Sstevel@tonic-gate hubd->h_port_state[port] &= 67717c478bd9Sstevel@tonic-gate ~HUBD_CHILD_EVENT_PRESUSPEND; 67727c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 67737c478bd9Sstevel@tonic-gate hubd_do_callback(hubd, dip, suspend_cookie); 67747c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 67757c478bd9Sstevel@tonic-gate } 67767c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 67777c478bd9Sstevel@tonic-gate 67787c478bd9Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex); 67797c478bd9Sstevel@tonic-gate usba_device->usb_no_cpr = 0; 67807c478bd9Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex); 67817c478bd9Sstevel@tonic-gate 67827c478bd9Sstevel@tonic-gate /* 67837c478bd9Sstevel@tonic-gate * Since the pipe has already been opened by hub 67847c478bd9Sstevel@tonic-gate * at DDI_RESUME time, there is no need for a 67857c478bd9Sstevel@tonic-gate * persistent pipe open 67867c478bd9Sstevel@tonic-gate */ 67877c478bd9Sstevel@tonic-gate hubd_do_callback(hubd, dip, cookie); 67887c478bd9Sstevel@tonic-gate 67897c478bd9Sstevel@tonic-gate /* 67907c478bd9Sstevel@tonic-gate * Set the flag again, so we don't miss posting a 67917c478bd9Sstevel@tonic-gate * pre-suspend event. This enforces a tighter 67927c478bd9Sstevel@tonic-gate * dev_state model. 67937c478bd9Sstevel@tonic-gate */ 67947c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 67957c478bd9Sstevel@tonic-gate hubd->h_child_events[port] |= HUBD_CHILD_EVENT_PRESUSPEND; 67967c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 67977c478bd9Sstevel@tonic-gate break; 67987c478bd9Sstevel@tonic-gate } 67997c478bd9Sstevel@tonic-gate } 68007c478bd9Sstevel@tonic-gate 68017c478bd9Sstevel@tonic-gate 68027c478bd9Sstevel@tonic-gate /* 68037c478bd9Sstevel@tonic-gate * handling of events coming from above 68047c478bd9Sstevel@tonic-gate */ 68057c478bd9Sstevel@tonic-gate static int 68067c478bd9Sstevel@tonic-gate hubd_disconnect_event_cb(dev_info_t *dip) 68077c478bd9Sstevel@tonic-gate { 68087c478bd9Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip); 68097c478bd9Sstevel@tonic-gate usb_port_t port, nports; 68107c478bd9Sstevel@tonic-gate usba_device_t *usba_dev; 68117c478bd9Sstevel@tonic-gate usba_event_t tag = USBA_EVENT_TAG_HOT_REMOVAL; 68127c478bd9Sstevel@tonic-gate int circ; 68137c478bd9Sstevel@tonic-gate 68147c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 68157c478bd9Sstevel@tonic-gate "hubd_disconnect_event_cb: tag=%d", tag); 68167c478bd9Sstevel@tonic-gate 68177c478bd9Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 68187c478bd9Sstevel@tonic-gate 68197c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 68207c478bd9Sstevel@tonic-gate switch (hubd->h_dev_state) { 68217c478bd9Sstevel@tonic-gate case USB_DEV_ONLINE: 68227c478bd9Sstevel@tonic-gate case USB_DEV_PWRED_DOWN: 68237c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_DISCONNECTED; 68247c478bd9Sstevel@tonic-gate /* stop polling on the interrupt pipe */ 68257c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd); 68267c478bd9Sstevel@tonic-gate 68277c478bd9Sstevel@tonic-gate /* FALLTHROUGH */ 68287c478bd9Sstevel@tonic-gate case USB_DEV_SUSPENDED: 68297c478bd9Sstevel@tonic-gate /* we remain in this state */ 68307c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 68317c478bd9Sstevel@tonic-gate hubd_run_callbacks(hubd, tag); 68327c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 68337c478bd9Sstevel@tonic-gate 68347c478bd9Sstevel@tonic-gate /* close all the open pipes of our children */ 68357c478bd9Sstevel@tonic-gate nports = hubd->h_hub_descr.bNbrPorts; 68367c478bd9Sstevel@tonic-gate for (port = 1; port <= nports; port++) { 68377c478bd9Sstevel@tonic-gate usba_dev = hubd->h_usba_devices[port]; 68387c478bd9Sstevel@tonic-gate if (usba_dev != NULL) { 68397c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 68407c478bd9Sstevel@tonic-gate usba_persistent_pipe_close(usba_dev); 68417c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 68427c478bd9Sstevel@tonic-gate } 68437c478bd9Sstevel@tonic-gate } 68447c478bd9Sstevel@tonic-gate 68457c478bd9Sstevel@tonic-gate break; 68467c478bd9Sstevel@tonic-gate case USB_DEV_DISCONNECTED: 68477c478bd9Sstevel@tonic-gate /* avoid passing multiple disconnects to children */ 68487c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 68497c478bd9Sstevel@tonic-gate "hubd_disconnect_event_cb: Already disconnected"); 68507c478bd9Sstevel@tonic-gate 68517c478bd9Sstevel@tonic-gate break; 68527c478bd9Sstevel@tonic-gate default: 68537c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 68547c478bd9Sstevel@tonic-gate "hubd_disconnect_event_cb: Illegal devstate=%d", 68557c478bd9Sstevel@tonic-gate hubd->h_dev_state); 68567c478bd9Sstevel@tonic-gate 68577c478bd9Sstevel@tonic-gate break; 68587c478bd9Sstevel@tonic-gate } 68597c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 68607c478bd9Sstevel@tonic-gate 68617c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 68627c478bd9Sstevel@tonic-gate 68637c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 68647c478bd9Sstevel@tonic-gate } 68657c478bd9Sstevel@tonic-gate 68667c478bd9Sstevel@tonic-gate 68677c478bd9Sstevel@tonic-gate static int 68687c478bd9Sstevel@tonic-gate hubd_reconnect_event_cb(dev_info_t *dip) 68697c478bd9Sstevel@tonic-gate { 68707c478bd9Sstevel@tonic-gate int rval, circ; 68717c478bd9Sstevel@tonic-gate 68727c478bd9Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 68737c478bd9Sstevel@tonic-gate rval = hubd_restore_state_cb(dip); 68747c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 68757c478bd9Sstevel@tonic-gate 68767c478bd9Sstevel@tonic-gate return (rval); 68777c478bd9Sstevel@tonic-gate } 68787c478bd9Sstevel@tonic-gate 68797c478bd9Sstevel@tonic-gate 68807c478bd9Sstevel@tonic-gate /* 68817c478bd9Sstevel@tonic-gate * hubd_pre_suspend_event_cb 68827c478bd9Sstevel@tonic-gate * propogate event for binary compatibility of old drivers 68837c478bd9Sstevel@tonic-gate */ 68847c478bd9Sstevel@tonic-gate static int 68857c478bd9Sstevel@tonic-gate hubd_pre_suspend_event_cb(dev_info_t *dip) 68867c478bd9Sstevel@tonic-gate { 68877c478bd9Sstevel@tonic-gate int circ; 68887c478bd9Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip); 68897c478bd9Sstevel@tonic-gate 68907c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle, 68917c478bd9Sstevel@tonic-gate "hubd_pre_suspend_event_cb"); 68927c478bd9Sstevel@tonic-gate 68937c478bd9Sstevel@tonic-gate /* disable hotplug thread */ 68947c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 68957c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread++; 68967c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd); 68977c478bd9Sstevel@tonic-gate 68987c478bd9Sstevel@tonic-gate /* keep PM out till we see a cpr resume */ 68997c478bd9Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0); 69007c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 69017c478bd9Sstevel@tonic-gate 69027c478bd9Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 69037c478bd9Sstevel@tonic-gate hubd_run_callbacks(hubd, USBA_EVENT_TAG_PRE_SUSPEND); 69047c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 69057c478bd9Sstevel@tonic-gate 69067c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 69077c478bd9Sstevel@tonic-gate } 69087c478bd9Sstevel@tonic-gate 69097c478bd9Sstevel@tonic-gate 69107c478bd9Sstevel@tonic-gate /* 69117c478bd9Sstevel@tonic-gate * hubd_post_resume_event_cb 69127c478bd9Sstevel@tonic-gate * propogate event for binary compatibility of old drivers 69137c478bd9Sstevel@tonic-gate */ 69147c478bd9Sstevel@tonic-gate static int 69157c478bd9Sstevel@tonic-gate hubd_post_resume_event_cb(dev_info_t *dip) 69167c478bd9Sstevel@tonic-gate { 69177c478bd9Sstevel@tonic-gate int circ; 69187c478bd9Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip); 69197c478bd9Sstevel@tonic-gate 69207c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle, 69217c478bd9Sstevel@tonic-gate "hubd_post_resume_event_cb"); 69227c478bd9Sstevel@tonic-gate 69237c478bd9Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 69247c478bd9Sstevel@tonic-gate hubd_run_callbacks(hubd, USBA_EVENT_TAG_POST_RESUME); 69257c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 69267c478bd9Sstevel@tonic-gate 69277c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 69287c478bd9Sstevel@tonic-gate 69297c478bd9Sstevel@tonic-gate /* enable PM */ 69307c478bd9Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0); 69317c478bd9Sstevel@tonic-gate 69327c478bd9Sstevel@tonic-gate /* allow hotplug thread */ 69337c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread--; 69347c478bd9Sstevel@tonic-gate 69357c478bd9Sstevel@tonic-gate /* start polling */ 69367c478bd9Sstevel@tonic-gate hubd_start_polling(hubd, 0); 69377c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 69387c478bd9Sstevel@tonic-gate 69397c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 69407c478bd9Sstevel@tonic-gate } 69417c478bd9Sstevel@tonic-gate 69427c478bd9Sstevel@tonic-gate 69437c478bd9Sstevel@tonic-gate /* 69447c478bd9Sstevel@tonic-gate * hubd_cpr_suspend 69457c478bd9Sstevel@tonic-gate * save the current state of the driver/device 69467c478bd9Sstevel@tonic-gate */ 69477c478bd9Sstevel@tonic-gate static int 69487c478bd9Sstevel@tonic-gate hubd_cpr_suspend(hubd_t *hubd) 69497c478bd9Sstevel@tonic-gate { 69507c478bd9Sstevel@tonic-gate usb_port_t port, nports; 69517c478bd9Sstevel@tonic-gate usba_device_t *usba_dev; 69527c478bd9Sstevel@tonic-gate uchar_t no_cpr = 0; 69537c478bd9Sstevel@tonic-gate int rval = USB_FAILURE; 69547c478bd9Sstevel@tonic-gate 69557c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 69567c478bd9Sstevel@tonic-gate "hubd_cpr_suspend: Begin"); 69577c478bd9Sstevel@tonic-gate 69587c478bd9Sstevel@tonic-gate /* Make sure device is powered up to save state. */ 69597c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 69607c478bd9Sstevel@tonic-gate hubd_pm_busy_component(hubd, hubd->h_dip, 0); 69617c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 69627c478bd9Sstevel@tonic-gate 69637c478bd9Sstevel@tonic-gate /* bring the device to full power */ 69647c478bd9Sstevel@tonic-gate (void) pm_raise_power(hubd->h_dip, 0, USB_DEV_OS_FULL_PWR); 69657c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 69667c478bd9Sstevel@tonic-gate 69677c478bd9Sstevel@tonic-gate switch (hubd->h_dev_state) { 69687c478bd9Sstevel@tonic-gate case USB_DEV_ONLINE: 69697c478bd9Sstevel@tonic-gate case USB_DEV_PWRED_DOWN: 69707c478bd9Sstevel@tonic-gate case USB_DEV_DISCONNECTED: 69717c478bd9Sstevel@tonic-gate /* find out if all our children have been quiesced */ 69727c478bd9Sstevel@tonic-gate nports = hubd->h_hub_descr.bNbrPorts; 69737c478bd9Sstevel@tonic-gate for (port = 1; (no_cpr == 0) && (port <= nports); port++) { 69747c478bd9Sstevel@tonic-gate usba_dev = hubd->h_usba_devices[port]; 69757c478bd9Sstevel@tonic-gate if (usba_dev != NULL) { 69767c478bd9Sstevel@tonic-gate mutex_enter(&usba_dev->usb_mutex); 69777c478bd9Sstevel@tonic-gate no_cpr += usba_dev->usb_no_cpr; 69787c478bd9Sstevel@tonic-gate mutex_exit(&usba_dev->usb_mutex); 69797c478bd9Sstevel@tonic-gate } 69807c478bd9Sstevel@tonic-gate } 69817c478bd9Sstevel@tonic-gate if (no_cpr > 0) { 69827c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 69837c478bd9Sstevel@tonic-gate "Children busy - can't checkpoint"); 69847c478bd9Sstevel@tonic-gate /* remain in same state to fail checkpoint */ 69857c478bd9Sstevel@tonic-gate 69867c478bd9Sstevel@tonic-gate break; 69877c478bd9Sstevel@tonic-gate } else { 69887c478bd9Sstevel@tonic-gate /* 69897c478bd9Sstevel@tonic-gate * do not suspend if our hotplug thread 69907c478bd9Sstevel@tonic-gate * or the deathrow thread is active 69917c478bd9Sstevel@tonic-gate */ 69927c478bd9Sstevel@tonic-gate if ((hubd->h_hotplug_thread > 1) || 69937c478bd9Sstevel@tonic-gate (hubd->h_cleanup_active == B_TRUE)) { 69947c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, 69957c478bd9Sstevel@tonic-gate hubd->h_log_handle, 69967c478bd9Sstevel@tonic-gate "hotplug thread active - can't cpr"); 69977c478bd9Sstevel@tonic-gate /* remain in same state to fail checkpoint */ 69987c478bd9Sstevel@tonic-gate 69997c478bd9Sstevel@tonic-gate break; 70007c478bd9Sstevel@tonic-gate } 70017c478bd9Sstevel@tonic-gate 70027c478bd9Sstevel@tonic-gate /* quiesce ourselves now */ 70037c478bd9Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_SUSPENDED; 70047c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd); 70057c478bd9Sstevel@tonic-gate 70067c478bd9Sstevel@tonic-gate /* close all the open pipes of our children */ 70077c478bd9Sstevel@tonic-gate for (port = 1; port <= nports; port++) { 70087c478bd9Sstevel@tonic-gate usba_dev = hubd->h_usba_devices[port]; 70097c478bd9Sstevel@tonic-gate if (usba_dev != NULL) { 70107c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 70117c478bd9Sstevel@tonic-gate usba_persistent_pipe_close(usba_dev); 70127c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 70137c478bd9Sstevel@tonic-gate } 70147c478bd9Sstevel@tonic-gate } 70157c478bd9Sstevel@tonic-gate /* 70167c478bd9Sstevel@tonic-gate * turn off power to all the ports so that we 70177c478bd9Sstevel@tonic-gate * don't see any spurious activity 70187c478bd9Sstevel@tonic-gate */ 70197c478bd9Sstevel@tonic-gate (void) hubd_disable_all_port_power(hubd); 70207c478bd9Sstevel@tonic-gate 70217c478bd9Sstevel@tonic-gate /* 70227c478bd9Sstevel@tonic-gate * if we are the root hub, we close our pipes 70237c478bd9Sstevel@tonic-gate * ourselves. 70247c478bd9Sstevel@tonic-gate */ 70257c478bd9Sstevel@tonic-gate if (usba_is_root_hub(hubd->h_dip)) { 70267c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 70277c478bd9Sstevel@tonic-gate usba_persistent_pipe_close( 70287c478bd9Sstevel@tonic-gate usba_get_usba_device(hubd->h_dip)); 70297c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 70307c478bd9Sstevel@tonic-gate } 70317c478bd9Sstevel@tonic-gate rval = USB_SUCCESS; 70327c478bd9Sstevel@tonic-gate 70337c478bd9Sstevel@tonic-gate break; 70347c478bd9Sstevel@tonic-gate } 70357c478bd9Sstevel@tonic-gate case USB_DEV_SUSPENDED: 70367c478bd9Sstevel@tonic-gate default: 70377c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 70387c478bd9Sstevel@tonic-gate "hubd_cpr_suspend: Illegal dev state=%d", 70397c478bd9Sstevel@tonic-gate hubd->h_dev_state); 70407c478bd9Sstevel@tonic-gate 70417c478bd9Sstevel@tonic-gate break; 70427c478bd9Sstevel@tonic-gate } 70437c478bd9Sstevel@tonic-gate 70447c478bd9Sstevel@tonic-gate hubd_pm_idle_component(hubd, hubd->h_dip, 0); 70457c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 70467c478bd9Sstevel@tonic-gate 70477c478bd9Sstevel@tonic-gate return (rval); 70487c478bd9Sstevel@tonic-gate } 70497c478bd9Sstevel@tonic-gate 70507c478bd9Sstevel@tonic-gate static void 70517c478bd9Sstevel@tonic-gate hubd_cpr_resume(dev_info_t *dip) 70527c478bd9Sstevel@tonic-gate { 70537c478bd9Sstevel@tonic-gate int rval, circ; 70547c478bd9Sstevel@tonic-gate 70557c478bd9Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 70567c478bd9Sstevel@tonic-gate /* 70577c478bd9Sstevel@tonic-gate * if we are the root hub, we open our pipes 70587c478bd9Sstevel@tonic-gate * ourselves. 70597c478bd9Sstevel@tonic-gate */ 70607c478bd9Sstevel@tonic-gate if (usba_is_root_hub(dip)) { 70617c478bd9Sstevel@tonic-gate rval = usba_persistent_pipe_open( 70627c478bd9Sstevel@tonic-gate usba_get_usba_device(dip)); 70637c478bd9Sstevel@tonic-gate ASSERT(rval == USB_SUCCESS); 70647c478bd9Sstevel@tonic-gate } 70657c478bd9Sstevel@tonic-gate (void) hubd_restore_state_cb(dip); 70667c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 70677c478bd9Sstevel@tonic-gate } 70687c478bd9Sstevel@tonic-gate 70697c478bd9Sstevel@tonic-gate 70707c478bd9Sstevel@tonic-gate /* 70717c478bd9Sstevel@tonic-gate * hubd_restore_state_cb 70727c478bd9Sstevel@tonic-gate * Event callback to restore device state 70737c478bd9Sstevel@tonic-gate */ 70747c478bd9Sstevel@tonic-gate static int 70757c478bd9Sstevel@tonic-gate hubd_restore_state_cb(dev_info_t *dip) 70767c478bd9Sstevel@tonic-gate { 70777c478bd9Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip); 70787c478bd9Sstevel@tonic-gate 70797c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 70807c478bd9Sstevel@tonic-gate "hubd_restore_state_cb: Begin"); 70817c478bd9Sstevel@tonic-gate 70827c478bd9Sstevel@tonic-gate /* restore the state of this device */ 70837c478bd9Sstevel@tonic-gate hubd_restore_device_state(dip, hubd); 70847c478bd9Sstevel@tonic-gate 70857c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 70867c478bd9Sstevel@tonic-gate } 70877c478bd9Sstevel@tonic-gate 70887c478bd9Sstevel@tonic-gate 70897c478bd9Sstevel@tonic-gate /* 70907c478bd9Sstevel@tonic-gate * registering for events 70917c478bd9Sstevel@tonic-gate */ 70927c478bd9Sstevel@tonic-gate static int 70937c478bd9Sstevel@tonic-gate hubd_register_events(hubd_t *hubd) 70947c478bd9Sstevel@tonic-gate { 70957c478bd9Sstevel@tonic-gate int rval = USB_SUCCESS; 70967c478bd9Sstevel@tonic-gate 70977c478bd9Sstevel@tonic-gate if (usba_is_root_hub(hubd->h_dip)) { 70987c478bd9Sstevel@tonic-gate hubd_register_cpr_callback(hubd); 70997c478bd9Sstevel@tonic-gate } else { 71007c478bd9Sstevel@tonic-gate rval = usb_register_event_cbs(hubd->h_dip, &hubd_events, 0); 71017c478bd9Sstevel@tonic-gate } 71027c478bd9Sstevel@tonic-gate 71037c478bd9Sstevel@tonic-gate return (rval); 71047c478bd9Sstevel@tonic-gate } 71057c478bd9Sstevel@tonic-gate 71067c478bd9Sstevel@tonic-gate 71077c478bd9Sstevel@tonic-gate /* 71087c478bd9Sstevel@tonic-gate * hubd cpr callback related functions 71097c478bd9Sstevel@tonic-gate * 71107c478bd9Sstevel@tonic-gate * hubd_cpr_post_user_callb: 71117c478bd9Sstevel@tonic-gate * This function is called during checkpoint & resume - 71127c478bd9Sstevel@tonic-gate * 1. after user threads are stopped during checkpoint 71137c478bd9Sstevel@tonic-gate * 2. after kernel threads are resumed during resume 71147c478bd9Sstevel@tonic-gate */ 71157c478bd9Sstevel@tonic-gate /* ARGSUSED */ 71167c478bd9Sstevel@tonic-gate static boolean_t 71177c478bd9Sstevel@tonic-gate hubd_cpr_post_user_callb(void *arg, int code) 71187c478bd9Sstevel@tonic-gate { 71197c478bd9Sstevel@tonic-gate hubd_cpr_t *cpr_cb = (hubd_cpr_t *)arg; 71207c478bd9Sstevel@tonic-gate hubd_t *hubd = cpr_cb->statep; 71217c478bd9Sstevel@tonic-gate int retry = 0; 71227c478bd9Sstevel@tonic-gate 71237c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle, 71247c478bd9Sstevel@tonic-gate "hubd_cpr_post_user_callb"); 71257c478bd9Sstevel@tonic-gate 71267c478bd9Sstevel@tonic-gate switch (code) { 71277c478bd9Sstevel@tonic-gate case CB_CODE_CPR_CHKPT: 71287c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_EVENTS, hubd->h_log_handle, 71297c478bd9Sstevel@tonic-gate "hubd_cpr_post_user_callb: CB_CODE_CPR_CHKPT"); 71307c478bd9Sstevel@tonic-gate 71317c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 71327c478bd9Sstevel@tonic-gate 71337c478bd9Sstevel@tonic-gate /* turn off deathrow thread */ 71347c478bd9Sstevel@tonic-gate hubd->h_cleanup_enabled = B_FALSE; 71357c478bd9Sstevel@tonic-gate 71367c478bd9Sstevel@tonic-gate /* give up if deathrow thread doesn't exit */ 71377c478bd9Sstevel@tonic-gate while ((hubd->h_cleanup_active == B_TRUE) && (retry++ < 3)) { 71387c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 71397c478bd9Sstevel@tonic-gate delay(drv_usectohz(hubd_dip_cleanup_delay)); 71407c478bd9Sstevel@tonic-gate 71417c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_EVENTS, hubd->h_log_handle, 71427c478bd9Sstevel@tonic-gate "hubd_cpr_post_user_callb, waiting for " 71437c478bd9Sstevel@tonic-gate "deathrow thread to exit"); 71447c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 71457c478bd9Sstevel@tonic-gate } 71467c478bd9Sstevel@tonic-gate 71477c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 71487c478bd9Sstevel@tonic-gate 71497c478bd9Sstevel@tonic-gate /* save the state of the device */ 71507c478bd9Sstevel@tonic-gate (void) hubd_pre_suspend_event_cb(hubd->h_dip); 71517c478bd9Sstevel@tonic-gate 71527c478bd9Sstevel@tonic-gate return (B_TRUE); 71537c478bd9Sstevel@tonic-gate case CB_CODE_CPR_RESUME: 71547c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_EVENTS, hubd->h_log_handle, 71557c478bd9Sstevel@tonic-gate "hubd_cpr_post_user_callb: CB_CODE_CPR_RESUME"); 71567c478bd9Sstevel@tonic-gate 71577c478bd9Sstevel@tonic-gate /* restore the state of the device */ 71587c478bd9Sstevel@tonic-gate (void) hubd_post_resume_event_cb(hubd->h_dip); 71597c478bd9Sstevel@tonic-gate 71607c478bd9Sstevel@tonic-gate /* turn on deathrow thread */ 71617c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 71627c478bd9Sstevel@tonic-gate hubd->h_cleanup_enabled = B_TRUE; 71637c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 71647c478bd9Sstevel@tonic-gate 71657c478bd9Sstevel@tonic-gate hubd_schedule_cleanup(hubd->h_usba_device->usb_root_hub_dip); 71667c478bd9Sstevel@tonic-gate 71677c478bd9Sstevel@tonic-gate return (B_TRUE); 71687c478bd9Sstevel@tonic-gate default: 71697c478bd9Sstevel@tonic-gate 71707c478bd9Sstevel@tonic-gate return (B_FALSE); 71717c478bd9Sstevel@tonic-gate } 71727c478bd9Sstevel@tonic-gate 71737c478bd9Sstevel@tonic-gate } 71747c478bd9Sstevel@tonic-gate 71757c478bd9Sstevel@tonic-gate 71767c478bd9Sstevel@tonic-gate /* register callback with cpr framework */ 71777c478bd9Sstevel@tonic-gate void 71787c478bd9Sstevel@tonic-gate hubd_register_cpr_callback(hubd_t *hubd) 71797c478bd9Sstevel@tonic-gate { 71807c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle, 71817c478bd9Sstevel@tonic-gate "hubd_register_cpr_callback"); 71827c478bd9Sstevel@tonic-gate 71837c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 71847c478bd9Sstevel@tonic-gate hubd->h_cpr_cb = 71857c478bd9Sstevel@tonic-gate (hubd_cpr_t *)kmem_zalloc(sizeof (hubd_cpr_t), KM_SLEEP); 71867c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 71877c478bd9Sstevel@tonic-gate mutex_init(&hubd->h_cpr_cb->lockp, NULL, MUTEX_DRIVER, 71887c478bd9Sstevel@tonic-gate hubd->h_dev_data->dev_iblock_cookie); 71897c478bd9Sstevel@tonic-gate hubd->h_cpr_cb->statep = hubd; 71907c478bd9Sstevel@tonic-gate hubd->h_cpr_cb->cpr.cc_lockp = &hubd->h_cpr_cb->lockp; 71917c478bd9Sstevel@tonic-gate hubd->h_cpr_cb->cpr.cc_id = callb_add(hubd_cpr_post_user_callb, 71927c478bd9Sstevel@tonic-gate (void *)hubd->h_cpr_cb, CB_CL_CPR_POST_USER, "hubd"); 71937c478bd9Sstevel@tonic-gate } 71947c478bd9Sstevel@tonic-gate 71957c478bd9Sstevel@tonic-gate 71967c478bd9Sstevel@tonic-gate /* unregister callback with cpr framework */ 71977c478bd9Sstevel@tonic-gate void 71987c478bd9Sstevel@tonic-gate hubd_unregister_cpr_callback(hubd_t *hubd) 71997c478bd9Sstevel@tonic-gate { 72007c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle, 72017c478bd9Sstevel@tonic-gate "hubd_unregister_cpr_callback"); 72027c478bd9Sstevel@tonic-gate 72037c478bd9Sstevel@tonic-gate if (hubd->h_cpr_cb) { 72047c478bd9Sstevel@tonic-gate (void) callb_delete(hubd->h_cpr_cb->cpr.cc_id); 72057c478bd9Sstevel@tonic-gate mutex_destroy(&hubd->h_cpr_cb->lockp); 72067c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 72077c478bd9Sstevel@tonic-gate kmem_free(hubd->h_cpr_cb, sizeof (hubd_cpr_t)); 72087c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 72097c478bd9Sstevel@tonic-gate } 72107c478bd9Sstevel@tonic-gate } 72117c478bd9Sstevel@tonic-gate 72127c478bd9Sstevel@tonic-gate 72137c478bd9Sstevel@tonic-gate /* 72147c478bd9Sstevel@tonic-gate * Power management 72157c478bd9Sstevel@tonic-gate * 72167c478bd9Sstevel@tonic-gate * create the pm components required for power management 72177c478bd9Sstevel@tonic-gate */ 72187c478bd9Sstevel@tonic-gate static void 72197c478bd9Sstevel@tonic-gate hubd_create_pm_components(dev_info_t *dip, hubd_t *hubd) 72207c478bd9Sstevel@tonic-gate { 72217c478bd9Sstevel@tonic-gate hub_power_t *hubpm; 72227c478bd9Sstevel@tonic-gate 72237c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 72247c478bd9Sstevel@tonic-gate "hubd_create_pm_components: Begin"); 72257c478bd9Sstevel@tonic-gate 72267c478bd9Sstevel@tonic-gate /* Allocate the state structure */ 72277c478bd9Sstevel@tonic-gate hubpm = kmem_zalloc(sizeof (hub_power_t), KM_SLEEP); 72287c478bd9Sstevel@tonic-gate 72297c478bd9Sstevel@tonic-gate hubd->h_hubpm = hubpm; 72307c478bd9Sstevel@tonic-gate hubpm->hubp_hubd = hubd; 72317c478bd9Sstevel@tonic-gate hubpm->hubp_pm_capabilities = 0; 72327c478bd9Sstevel@tonic-gate hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR; 72337c478bd9Sstevel@tonic-gate hubpm->hubp_time_at_full_power = ddi_get_time(); 72347c478bd9Sstevel@tonic-gate hubpm->hubp_min_pm_threshold = hubdi_min_pm_threshold; 72357c478bd9Sstevel@tonic-gate 72367c478bd9Sstevel@tonic-gate /* alloc memory to save power states of children */ 72377c478bd9Sstevel@tonic-gate hubpm->hubp_child_pwrstate = (uint8_t *) 7238c0f24e5bSlg kmem_zalloc(MAX_PORTS + 1, KM_SLEEP); 72397c478bd9Sstevel@tonic-gate 72407c478bd9Sstevel@tonic-gate /* 72417c478bd9Sstevel@tonic-gate * if the enable remote wakeup fails 72427c478bd9Sstevel@tonic-gate * we still want to enable 72437c478bd9Sstevel@tonic-gate * parent notification so we can PM the children 72447c478bd9Sstevel@tonic-gate */ 72457c478bd9Sstevel@tonic-gate usb_enable_parent_notification(dip); 72467c478bd9Sstevel@tonic-gate 72477c478bd9Sstevel@tonic-gate if (usb_handle_remote_wakeup(dip, 72487c478bd9Sstevel@tonic-gate USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) { 72497c478bd9Sstevel@tonic-gate uint_t pwr_states; 72507c478bd9Sstevel@tonic-gate 72517c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, 72527c478bd9Sstevel@tonic-gate "hubd_create_pm_components: " 72537c478bd9Sstevel@tonic-gate "Remote Wakeup Enabled"); 72547c478bd9Sstevel@tonic-gate 72557c478bd9Sstevel@tonic-gate if (usb_create_pm_components(dip, &pwr_states) == 72567c478bd9Sstevel@tonic-gate USB_SUCCESS) { 72577c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 72587c478bd9Sstevel@tonic-gate hubpm->hubp_wakeup_enabled = 1; 72597c478bd9Sstevel@tonic-gate hubpm->hubp_pwr_states = (uint8_t)pwr_states; 72607c478bd9Sstevel@tonic-gate 72617c478bd9Sstevel@tonic-gate /* we are busy now till end of the attach */ 72627c478bd9Sstevel@tonic-gate hubd_pm_busy_component(hubd, dip, 0); 72637c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 72647c478bd9Sstevel@tonic-gate 72657c478bd9Sstevel@tonic-gate /* bring the device to full power */ 72667c478bd9Sstevel@tonic-gate (void) pm_raise_power(dip, 0, 72677c478bd9Sstevel@tonic-gate USB_DEV_OS_FULL_PWR); 72687c478bd9Sstevel@tonic-gate } 72697c478bd9Sstevel@tonic-gate } 72707c478bd9Sstevel@tonic-gate 72717c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle, 72727c478bd9Sstevel@tonic-gate "hubd_create_pm_components: END"); 72737c478bd9Sstevel@tonic-gate } 72747c478bd9Sstevel@tonic-gate 72757c478bd9Sstevel@tonic-gate 72767c478bd9Sstevel@tonic-gate /* 72777c478bd9Sstevel@tonic-gate * Attachment point management 72787c478bd9Sstevel@tonic-gate */ 72797c478bd9Sstevel@tonic-gate /* ARGSUSED */ 72807c478bd9Sstevel@tonic-gate int 72817c478bd9Sstevel@tonic-gate usba_hubdi_open(dev_info_t *dip, dev_t *devp, int flags, int otyp, 72827c478bd9Sstevel@tonic-gate cred_t *credp) 72837c478bd9Sstevel@tonic-gate { 72847c478bd9Sstevel@tonic-gate hubd_t *hubd; 72857c478bd9Sstevel@tonic-gate 72867c478bd9Sstevel@tonic-gate if (otyp != OTYP_CHR) 72877c478bd9Sstevel@tonic-gate return (EINVAL); 72887c478bd9Sstevel@tonic-gate 72897c478bd9Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 72907c478bd9Sstevel@tonic-gate if (hubd == NULL) { 72917c478bd9Sstevel@tonic-gate return (ENXIO); 72927c478bd9Sstevel@tonic-gate } 72937c478bd9Sstevel@tonic-gate 72947c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 72957c478bd9Sstevel@tonic-gate "hubd_open:"); 72967c478bd9Sstevel@tonic-gate 72977c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 72987c478bd9Sstevel@tonic-gate if ((flags & FEXCL) && (hubd->h_softstate & HUBD_SS_ISOPEN)) { 72997c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 73007c478bd9Sstevel@tonic-gate 73017c478bd9Sstevel@tonic-gate return (EBUSY); 73027c478bd9Sstevel@tonic-gate } 73037c478bd9Sstevel@tonic-gate 73047c478bd9Sstevel@tonic-gate hubd->h_softstate |= HUBD_SS_ISOPEN; 73057c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 73067c478bd9Sstevel@tonic-gate 73077c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, "opened"); 73087c478bd9Sstevel@tonic-gate 73097c478bd9Sstevel@tonic-gate return (0); 73107c478bd9Sstevel@tonic-gate } 73117c478bd9Sstevel@tonic-gate 73127c478bd9Sstevel@tonic-gate 73137c478bd9Sstevel@tonic-gate /* ARGSUSED */ 73147c478bd9Sstevel@tonic-gate int 73157c478bd9Sstevel@tonic-gate usba_hubdi_close(dev_info_t *dip, dev_t dev, int flag, int otyp, 73167c478bd9Sstevel@tonic-gate cred_t *credp) 73177c478bd9Sstevel@tonic-gate { 73187c478bd9Sstevel@tonic-gate hubd_t *hubd; 73197c478bd9Sstevel@tonic-gate 73207c478bd9Sstevel@tonic-gate if (otyp != OTYP_CHR) { 73217c478bd9Sstevel@tonic-gate return (EINVAL); 73227c478bd9Sstevel@tonic-gate } 73237c478bd9Sstevel@tonic-gate 73247c478bd9Sstevel@tonic-gate hubd = hubd_get_soft_state(dip); 73257c478bd9Sstevel@tonic-gate 73267c478bd9Sstevel@tonic-gate if (hubd == NULL) { 73277c478bd9Sstevel@tonic-gate return (ENXIO); 73287c478bd9Sstevel@tonic-gate } 73297c478bd9Sstevel@tonic-gate 73307c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, "hubd_close:"); 73317c478bd9Sstevel@tonic-gate 73327c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 73337c478bd9Sstevel@tonic-gate hubd->h_softstate &= ~HUBD_SS_ISOPEN; 73347c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 73357c478bd9Sstevel@tonic-gate 73367c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, "closed"); 73377c478bd9Sstevel@tonic-gate 73387c478bd9Sstevel@tonic-gate return (0); 73397c478bd9Sstevel@tonic-gate } 73407c478bd9Sstevel@tonic-gate 73417c478bd9Sstevel@tonic-gate 73427c478bd9Sstevel@tonic-gate /* 73437c478bd9Sstevel@tonic-gate * hubd_ioctl: cfgadm controls 73447c478bd9Sstevel@tonic-gate */ 73457c478bd9Sstevel@tonic-gate /* ARGSUSED */ 73467c478bd9Sstevel@tonic-gate int 73477c478bd9Sstevel@tonic-gate usba_hubdi_ioctl(dev_info_t *self, dev_t dev, int cmd, intptr_t arg, 73487c478bd9Sstevel@tonic-gate int mode, cred_t *credp, int *rvalp) 73497c478bd9Sstevel@tonic-gate { 73507c478bd9Sstevel@tonic-gate int rv = 0; 73517c478bd9Sstevel@tonic-gate char *msg; /* for messages */ 73527c478bd9Sstevel@tonic-gate hubd_t *hubd; 73537c478bd9Sstevel@tonic-gate usb_port_t port = 0; 73547c478bd9Sstevel@tonic-gate dev_info_t *child_dip = NULL; 73557c478bd9Sstevel@tonic-gate dev_info_t *rh_dip; 73567c478bd9Sstevel@tonic-gate devctl_ap_state_t ap_state; 73577c478bd9Sstevel@tonic-gate struct devctl_iocdata *dcp = NULL; 73587c478bd9Sstevel@tonic-gate usb_pipe_state_t prev_pipe_state = 0; 73597c478bd9Sstevel@tonic-gate int circ, rh_circ, prh_circ; 73607c478bd9Sstevel@tonic-gate 73617c478bd9Sstevel@tonic-gate if ((hubd = hubd_get_soft_state(self)) == NULL) { 73627c478bd9Sstevel@tonic-gate 73637c478bd9Sstevel@tonic-gate return (ENXIO); 73647c478bd9Sstevel@tonic-gate } 73657c478bd9Sstevel@tonic-gate 73667c478bd9Sstevel@tonic-gate rh_dip = hubd->h_usba_device->usb_root_hub_dip; 73677c478bd9Sstevel@tonic-gate 73687c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 73697c478bd9Sstevel@tonic-gate "usba_hubdi_ioctl: " 73707c478bd9Sstevel@tonic-gate "cmd=%x, arg=%lx, mode=%x, cred=%p, rval=%p dev=0x%lx", 7371112116d8Sfb cmd, arg, mode, (void *)credp, (void *)rvalp, dev); 73727c478bd9Sstevel@tonic-gate 73737c478bd9Sstevel@tonic-gate /* read devctl ioctl data */ 73747c478bd9Sstevel@tonic-gate if ((cmd != DEVCTL_AP_CONTROL) && 73757c478bd9Sstevel@tonic-gate (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)) { 73767c478bd9Sstevel@tonic-gate 73777c478bd9Sstevel@tonic-gate return (EFAULT); 73787c478bd9Sstevel@tonic-gate } 73797c478bd9Sstevel@tonic-gate 73807c478bd9Sstevel@tonic-gate /* 73817c478bd9Sstevel@tonic-gate * make sure the hub is connected before trying any 73827c478bd9Sstevel@tonic-gate * of the following operations: 73837c478bd9Sstevel@tonic-gate * configure, connect, disconnect 73847c478bd9Sstevel@tonic-gate */ 73857c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 73867c478bd9Sstevel@tonic-gate 73877c478bd9Sstevel@tonic-gate switch (cmd) { 73887c478bd9Sstevel@tonic-gate case DEVCTL_AP_DISCONNECT: 73897c478bd9Sstevel@tonic-gate case DEVCTL_AP_UNCONFIGURE: 73907c478bd9Sstevel@tonic-gate case DEVCTL_AP_CONFIGURE: 73917c478bd9Sstevel@tonic-gate if (hubd->h_dev_state == USB_DEV_DISCONNECTED) { 73927c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 73937c478bd9Sstevel@tonic-gate "hubd: already gone"); 73947c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 73957c478bd9Sstevel@tonic-gate if (dcp) { 73967c478bd9Sstevel@tonic-gate ndi_dc_freehdl(dcp); 73977c478bd9Sstevel@tonic-gate } 73987c478bd9Sstevel@tonic-gate 73997c478bd9Sstevel@tonic-gate return (EIO); 74007c478bd9Sstevel@tonic-gate } 74017c478bd9Sstevel@tonic-gate 74027c478bd9Sstevel@tonic-gate /* FALLTHROUGH */ 74037c478bd9Sstevel@tonic-gate case DEVCTL_AP_GETSTATE: 74047c478bd9Sstevel@tonic-gate if ((port = hubd_get_port_num(hubd, dcp)) == 0) { 74057c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 74067c478bd9Sstevel@tonic-gate "hubd: bad port"); 74077c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 74087c478bd9Sstevel@tonic-gate if (dcp) { 74097c478bd9Sstevel@tonic-gate ndi_dc_freehdl(dcp); 74107c478bd9Sstevel@tonic-gate } 74117c478bd9Sstevel@tonic-gate 74127c478bd9Sstevel@tonic-gate return (EINVAL); 74137c478bd9Sstevel@tonic-gate } 74147c478bd9Sstevel@tonic-gate break; 74157c478bd9Sstevel@tonic-gate 74167c478bd9Sstevel@tonic-gate case DEVCTL_AP_CONTROL: 74177c478bd9Sstevel@tonic-gate 74187c478bd9Sstevel@tonic-gate break; 74197c478bd9Sstevel@tonic-gate default: 74207c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 74217c478bd9Sstevel@tonic-gate if (dcp) { 74227c478bd9Sstevel@tonic-gate ndi_dc_freehdl(dcp); 74237c478bd9Sstevel@tonic-gate } 74247c478bd9Sstevel@tonic-gate 74257c478bd9Sstevel@tonic-gate return (ENOTTY); 74267c478bd9Sstevel@tonic-gate } 74277c478bd9Sstevel@tonic-gate 74287c478bd9Sstevel@tonic-gate /* should not happen, just in case */ 74297c478bd9Sstevel@tonic-gate if (hubd->h_dev_state == USB_DEV_SUSPENDED) { 74307c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 74317c478bd9Sstevel@tonic-gate if (dcp) { 74327c478bd9Sstevel@tonic-gate ndi_dc_freehdl(dcp); 74337c478bd9Sstevel@tonic-gate } 74347c478bd9Sstevel@tonic-gate 74357c478bd9Sstevel@tonic-gate return (EIO); 74367c478bd9Sstevel@tonic-gate } 74377c478bd9Sstevel@tonic-gate 7438ffcd51f3Slg if (hubd->h_reset_port[port]) { 7439ffcd51f3Slg USB_DPRINTF_L2(DPRINT_MASK_CBOPS, hubd->h_log_handle, 7440ffcd51f3Slg "This port is resetting, just return"); 7441ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 7442ffcd51f3Slg if (dcp) { 7443ffcd51f3Slg ndi_dc_freehdl(dcp); 7444ffcd51f3Slg } 7445ffcd51f3Slg 7446ffcd51f3Slg return (EIO); 7447ffcd51f3Slg } 7448ffcd51f3Slg 74497c478bd9Sstevel@tonic-gate hubd_pm_busy_component(hubd, hubd->h_dip, 0); 74507c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 74517c478bd9Sstevel@tonic-gate 74527c478bd9Sstevel@tonic-gate /* go full power */ 74537c478bd9Sstevel@tonic-gate (void) pm_raise_power(hubd->h_dip, 0, USB_DEV_OS_FULL_PWR); 74547c478bd9Sstevel@tonic-gate 74557c478bd9Sstevel@tonic-gate ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ); 74567c478bd9Sstevel@tonic-gate ndi_devi_enter(rh_dip, &rh_circ); 74577c478bd9Sstevel@tonic-gate ndi_devi_enter(hubd->h_dip, &circ); 74587c478bd9Sstevel@tonic-gate 74597c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 74607c478bd9Sstevel@tonic-gate 7461c0f24e5bSlg hubd->h_hotplug_thread++; 7462c0f24e5bSlg 7463c0f24e5bSlg /* stop polling if it was active */ 74647c478bd9Sstevel@tonic-gate if (hubd->h_ep1_ph) { 74657c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 74667c478bd9Sstevel@tonic-gate (void) usb_pipe_get_state(hubd->h_ep1_ph, &prev_pipe_state, 7467c0f24e5bSlg USB_FLAGS_SLEEP); 74687c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 74697c478bd9Sstevel@tonic-gate 74707c478bd9Sstevel@tonic-gate if (prev_pipe_state == USB_PIPE_STATE_ACTIVE) { 74717c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd); 74727c478bd9Sstevel@tonic-gate } 74737c478bd9Sstevel@tonic-gate } 74747c478bd9Sstevel@tonic-gate 74757c478bd9Sstevel@tonic-gate switch (cmd) { 74767c478bd9Sstevel@tonic-gate case DEVCTL_AP_DISCONNECT: 74777c478bd9Sstevel@tonic-gate if (hubd_delete_child(hubd, port, 74787c478bd9Sstevel@tonic-gate NDI_DEVI_REMOVE, B_FALSE) != USB_SUCCESS) { 74797c478bd9Sstevel@tonic-gate rv = EIO; 74807c478bd9Sstevel@tonic-gate } 74817c478bd9Sstevel@tonic-gate 74827c478bd9Sstevel@tonic-gate break; 74837c478bd9Sstevel@tonic-gate case DEVCTL_AP_UNCONFIGURE: 74847c478bd9Sstevel@tonic-gate if (hubd_delete_child(hubd, port, 74857c478bd9Sstevel@tonic-gate NDI_UNCONFIG, B_FALSE) != USB_SUCCESS) { 74867c478bd9Sstevel@tonic-gate rv = EIO; 74877c478bd9Sstevel@tonic-gate } 74887c478bd9Sstevel@tonic-gate 74897c478bd9Sstevel@tonic-gate break; 74907c478bd9Sstevel@tonic-gate case DEVCTL_AP_CONFIGURE: 74917c478bd9Sstevel@tonic-gate /* toggle port */ 74927c478bd9Sstevel@tonic-gate if (hubd_toggle_port(hubd, port) != USB_SUCCESS) { 74937c478bd9Sstevel@tonic-gate rv = EIO; 74947c478bd9Sstevel@tonic-gate 74957c478bd9Sstevel@tonic-gate break; 74967c478bd9Sstevel@tonic-gate } 74977c478bd9Sstevel@tonic-gate 74987c478bd9Sstevel@tonic-gate (void) hubd_handle_port_connect(hubd, port); 74997c478bd9Sstevel@tonic-gate child_dip = hubd_get_child_dip(hubd, port); 75007c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 75017c478bd9Sstevel@tonic-gate 75027c478bd9Sstevel@tonic-gate ndi_devi_exit(hubd->h_dip, circ); 75037c478bd9Sstevel@tonic-gate ndi_devi_exit(rh_dip, rh_circ); 75047c478bd9Sstevel@tonic-gate ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ); 75057c478bd9Sstevel@tonic-gate if ((child_dip == NULL) || 75067c478bd9Sstevel@tonic-gate (ndi_devi_online(child_dip, 0) != NDI_SUCCESS)) { 75077c478bd9Sstevel@tonic-gate rv = EIO; 75087c478bd9Sstevel@tonic-gate } 75097c478bd9Sstevel@tonic-gate ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ); 75107c478bd9Sstevel@tonic-gate ndi_devi_enter(rh_dip, &rh_circ); 75117c478bd9Sstevel@tonic-gate ndi_devi_enter(hubd->h_dip, &circ); 75127c478bd9Sstevel@tonic-gate 75137c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 75147c478bd9Sstevel@tonic-gate 75157c478bd9Sstevel@tonic-gate break; 75167c478bd9Sstevel@tonic-gate case DEVCTL_AP_GETSTATE: 75177c478bd9Sstevel@tonic-gate switch (hubd_cfgadm_state(hubd, port)) { 75187c478bd9Sstevel@tonic-gate case HUBD_CFGADM_DISCONNECTED: 75197c478bd9Sstevel@tonic-gate /* port previously 'disconnected' by cfgadm */ 75207c478bd9Sstevel@tonic-gate ap_state.ap_rstate = AP_RSTATE_DISCONNECTED; 75217c478bd9Sstevel@tonic-gate ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED; 75227c478bd9Sstevel@tonic-gate ap_state.ap_condition = AP_COND_OK; 75237c478bd9Sstevel@tonic-gate 75247c478bd9Sstevel@tonic-gate break; 75257c478bd9Sstevel@tonic-gate case HUBD_CFGADM_UNCONFIGURED: 75267c478bd9Sstevel@tonic-gate ap_state.ap_rstate = AP_RSTATE_CONNECTED; 75277c478bd9Sstevel@tonic-gate ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED; 75287c478bd9Sstevel@tonic-gate ap_state.ap_condition = AP_COND_OK; 75297c478bd9Sstevel@tonic-gate 75307c478bd9Sstevel@tonic-gate break; 75317c478bd9Sstevel@tonic-gate case HUBD_CFGADM_CONFIGURED: 75327c478bd9Sstevel@tonic-gate ap_state.ap_rstate = AP_RSTATE_CONNECTED; 75337c478bd9Sstevel@tonic-gate ap_state.ap_ostate = AP_OSTATE_CONFIGURED; 75347c478bd9Sstevel@tonic-gate ap_state.ap_condition = AP_COND_OK; 75357c478bd9Sstevel@tonic-gate 75367c478bd9Sstevel@tonic-gate break; 75377c478bd9Sstevel@tonic-gate case HUBD_CFGADM_STILL_REFERENCED: 75387c478bd9Sstevel@tonic-gate ap_state.ap_rstate = AP_RSTATE_EMPTY; 75397c478bd9Sstevel@tonic-gate ap_state.ap_ostate = AP_OSTATE_CONFIGURED; 75407c478bd9Sstevel@tonic-gate ap_state.ap_condition = AP_COND_UNUSABLE; 75417c478bd9Sstevel@tonic-gate 75427c478bd9Sstevel@tonic-gate break; 75437c478bd9Sstevel@tonic-gate case HUBD_CFGADM_EMPTY: 75447c478bd9Sstevel@tonic-gate default: 75457c478bd9Sstevel@tonic-gate ap_state.ap_rstate = AP_RSTATE_EMPTY; 75467c478bd9Sstevel@tonic-gate ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED; 75477c478bd9Sstevel@tonic-gate ap_state.ap_condition = AP_COND_OK; 75487c478bd9Sstevel@tonic-gate 75497c478bd9Sstevel@tonic-gate break; 75507c478bd9Sstevel@tonic-gate } 75517c478bd9Sstevel@tonic-gate 75527c478bd9Sstevel@tonic-gate ap_state.ap_last_change = (time_t)-1; 75537c478bd9Sstevel@tonic-gate ap_state.ap_error_code = 0; 75547c478bd9Sstevel@tonic-gate ap_state.ap_in_transition = 0; 75557c478bd9Sstevel@tonic-gate 75567c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 75577c478bd9Sstevel@tonic-gate "DEVCTL_AP_GETSTATE: " 75587c478bd9Sstevel@tonic-gate "ostate=0x%x, rstate=0x%x, condition=0x%x", 75597c478bd9Sstevel@tonic-gate ap_state.ap_ostate, 75607c478bd9Sstevel@tonic-gate ap_state.ap_rstate, ap_state.ap_condition); 75617c478bd9Sstevel@tonic-gate 75627c478bd9Sstevel@tonic-gate /* copy the return-AP-state information to the user space */ 75637c478bd9Sstevel@tonic-gate if (ndi_dc_return_ap_state(&ap_state, dcp) != NDI_SUCCESS) { 75647c478bd9Sstevel@tonic-gate rv = EFAULT; 75657c478bd9Sstevel@tonic-gate } 75667c478bd9Sstevel@tonic-gate 75677c478bd9Sstevel@tonic-gate break; 75687c478bd9Sstevel@tonic-gate case DEVCTL_AP_CONTROL: 75697c478bd9Sstevel@tonic-gate { 75707c478bd9Sstevel@tonic-gate /* 75717c478bd9Sstevel@tonic-gate * Generic devctl for hardware-specific functionality. 75727c478bd9Sstevel@tonic-gate * For list of sub-commands see hubd_impl.h 75737c478bd9Sstevel@tonic-gate */ 75747c478bd9Sstevel@tonic-gate hubd_ioctl_data_t ioc; /* for 64 byte copies */ 75757c478bd9Sstevel@tonic-gate 75767c478bd9Sstevel@tonic-gate /* copy user ioctl data in first */ 75777c478bd9Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL 75787c478bd9Sstevel@tonic-gate if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) { 75797c478bd9Sstevel@tonic-gate hubd_ioctl_data_32_t ioc32; 75807c478bd9Sstevel@tonic-gate 75817c478bd9Sstevel@tonic-gate if (ddi_copyin((void *)arg, (void *)&ioc32, 7582c0f24e5bSlg sizeof (ioc32), mode) != 0) { 75837c478bd9Sstevel@tonic-gate rv = EFAULT; 75847c478bd9Sstevel@tonic-gate 75857c478bd9Sstevel@tonic-gate break; 75867c478bd9Sstevel@tonic-gate } 75877c478bd9Sstevel@tonic-gate ioc.cmd = (uint_t)ioc32.cmd; 75887c478bd9Sstevel@tonic-gate ioc.port = (uint_t)ioc32.port; 75897c478bd9Sstevel@tonic-gate ioc.get_size = (uint_t)ioc32.get_size; 75907c478bd9Sstevel@tonic-gate ioc.buf = (caddr_t)(uintptr_t)ioc32.buf; 75917c478bd9Sstevel@tonic-gate ioc.bufsiz = (uint_t)ioc32.bufsiz; 75927c478bd9Sstevel@tonic-gate ioc.misc_arg = (uint_t)ioc32.misc_arg; 75937c478bd9Sstevel@tonic-gate } else 75947c478bd9Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */ 75957c478bd9Sstevel@tonic-gate if (ddi_copyin((void *)arg, (void *)&ioc, sizeof (ioc), 75967c478bd9Sstevel@tonic-gate mode) != 0) { 75977c478bd9Sstevel@tonic-gate rv = EFAULT; 75987c478bd9Sstevel@tonic-gate 75997c478bd9Sstevel@tonic-gate break; 76007c478bd9Sstevel@tonic-gate } 76017c478bd9Sstevel@tonic-gate 76027c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_CBOPS, hubd->h_log_handle, 76037c478bd9Sstevel@tonic-gate "DEVCTL_AP_CONTROL: ioc: cmd=0x%x port=%d get_size=%d" 76047c478bd9Sstevel@tonic-gate "\n\tbuf=0x%p, bufsiz=%d, misc_arg=%d", ioc.cmd, 7605112116d8Sfb ioc.port, ioc.get_size, (void *)ioc.buf, ioc.bufsiz, 7606112116d8Sfb ioc.misc_arg); 76077c478bd9Sstevel@tonic-gate 76087c478bd9Sstevel@tonic-gate /* 76097c478bd9Sstevel@tonic-gate * To avoid BE/LE and 32/64 issues, a get_size always 76107c478bd9Sstevel@tonic-gate * returns a 32-bit number. 76117c478bd9Sstevel@tonic-gate */ 76127c478bd9Sstevel@tonic-gate if (ioc.get_size != 0 && ioc.bufsiz != (sizeof (uint32_t))) { 76137c478bd9Sstevel@tonic-gate rv = EINVAL; 76147c478bd9Sstevel@tonic-gate 76157c478bd9Sstevel@tonic-gate break; 76167c478bd9Sstevel@tonic-gate } 76177c478bd9Sstevel@tonic-gate 76187c478bd9Sstevel@tonic-gate switch (ioc.cmd) { 76197c478bd9Sstevel@tonic-gate case USB_DESCR_TYPE_DEV: 76207c478bd9Sstevel@tonic-gate msg = "DEVCTL_AP_CONTROL: GET_DEVICE_DESC"; 76217c478bd9Sstevel@tonic-gate if (ioc.get_size) { 76227c478bd9Sstevel@tonic-gate /* uint32 so this works 32/64 */ 76237c478bd9Sstevel@tonic-gate uint32_t size = sizeof (usb_dev_descr_t); 76247c478bd9Sstevel@tonic-gate 76257c478bd9Sstevel@tonic-gate if (ddi_copyout((void *)&size, ioc.buf, 76267c478bd9Sstevel@tonic-gate ioc.bufsiz, mode) != 0) { 76277c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 76287c478bd9Sstevel@tonic-gate hubd->h_log_handle, 76297c478bd9Sstevel@tonic-gate "%s: get_size copyout failed", msg); 76307c478bd9Sstevel@tonic-gate rv = EIO; 76317c478bd9Sstevel@tonic-gate 76327c478bd9Sstevel@tonic-gate break; 76337c478bd9Sstevel@tonic-gate } 76347c478bd9Sstevel@tonic-gate } else { /* send out the actual descr */ 76357c478bd9Sstevel@tonic-gate usb_dev_descr_t *dev_descrp; 76367c478bd9Sstevel@tonic-gate 76377c478bd9Sstevel@tonic-gate /* check child_dip */ 76387c478bd9Sstevel@tonic-gate if ((child_dip = hubd_get_child_dip(hubd, 76397c478bd9Sstevel@tonic-gate ioc.port)) == NULL) { 76407c478bd9Sstevel@tonic-gate rv = EINVAL; 76417c478bd9Sstevel@tonic-gate 76427c478bd9Sstevel@tonic-gate break; 76437c478bd9Sstevel@tonic-gate } 76447c478bd9Sstevel@tonic-gate 76457c478bd9Sstevel@tonic-gate dev_descrp = usb_get_dev_descr(child_dip); 76467c478bd9Sstevel@tonic-gate if (ioc.bufsiz != sizeof (*dev_descrp)) { 76477c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 76487c478bd9Sstevel@tonic-gate hubd->h_log_handle, 76497c478bd9Sstevel@tonic-gate "%s: bufsize passed (%d) != sizeof " 76507c478bd9Sstevel@tonic-gate "usba_device_descr_t (%d)", msg, 76517c478bd9Sstevel@tonic-gate ioc.bufsiz, dev_descrp->bLength); 76527c478bd9Sstevel@tonic-gate rv = EINVAL; 76537c478bd9Sstevel@tonic-gate 76547c478bd9Sstevel@tonic-gate break; 76557c478bd9Sstevel@tonic-gate } 76567c478bd9Sstevel@tonic-gate 76577c478bd9Sstevel@tonic-gate if (ddi_copyout((void *)dev_descrp, 76587c478bd9Sstevel@tonic-gate ioc.buf, ioc.bufsiz, mode) != 0) { 76597c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 76607c478bd9Sstevel@tonic-gate hubd->h_log_handle, 76617c478bd9Sstevel@tonic-gate "%s: copyout failed.", msg); 76627c478bd9Sstevel@tonic-gate rv = EIO; 76637c478bd9Sstevel@tonic-gate 76647c478bd9Sstevel@tonic-gate break; 76657c478bd9Sstevel@tonic-gate } 76667c478bd9Sstevel@tonic-gate } 76677c478bd9Sstevel@tonic-gate break; 76687c478bd9Sstevel@tonic-gate case USB_DESCR_TYPE_STRING: 76697c478bd9Sstevel@tonic-gate { 76707c478bd9Sstevel@tonic-gate char *str; 76717c478bd9Sstevel@tonic-gate uint32_t size; 76727c478bd9Sstevel@tonic-gate usba_device_t *usba_device; 76737c478bd9Sstevel@tonic-gate 76747c478bd9Sstevel@tonic-gate msg = "DEVCTL_AP_CONTROL: GET_STRING_DESCR"; 76757c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 76767c478bd9Sstevel@tonic-gate "%s: string request: %d", msg, ioc.misc_arg); 76777c478bd9Sstevel@tonic-gate 76787c478bd9Sstevel@tonic-gate /* recheck */ 76797c478bd9Sstevel@tonic-gate if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) == 76807c478bd9Sstevel@tonic-gate NULL) { 76817c478bd9Sstevel@tonic-gate rv = EINVAL; 76827c478bd9Sstevel@tonic-gate 76837c478bd9Sstevel@tonic-gate break; 76847c478bd9Sstevel@tonic-gate } 76857c478bd9Sstevel@tonic-gate usba_device = usba_get_usba_device(child_dip); 76867c478bd9Sstevel@tonic-gate 76877c478bd9Sstevel@tonic-gate switch (ioc.misc_arg) { 76887c478bd9Sstevel@tonic-gate case HUBD_MFG_STR: 76897c478bd9Sstevel@tonic-gate str = usba_device->usb_mfg_str; 76907c478bd9Sstevel@tonic-gate 76917c478bd9Sstevel@tonic-gate break; 76927c478bd9Sstevel@tonic-gate case HUBD_PRODUCT_STR: 76937c478bd9Sstevel@tonic-gate str = usba_device->usb_product_str; 76947c478bd9Sstevel@tonic-gate 76957c478bd9Sstevel@tonic-gate break; 76967c478bd9Sstevel@tonic-gate case HUBD_SERIALNO_STR: 76977c478bd9Sstevel@tonic-gate str = usba_device->usb_serialno_str; 76987c478bd9Sstevel@tonic-gate 76997c478bd9Sstevel@tonic-gate break; 77007c478bd9Sstevel@tonic-gate case HUBD_CFG_DESCR_STR: 77017c478bd9Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex); 77027c478bd9Sstevel@tonic-gate str = usba_device->usb_cfg_str_descr[ 7703c0f24e5bSlg usba_device->usb_active_cfg_ndx]; 77047c478bd9Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex); 77057c478bd9Sstevel@tonic-gate 77067c478bd9Sstevel@tonic-gate break; 77077c478bd9Sstevel@tonic-gate default: 77087c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 77097c478bd9Sstevel@tonic-gate hubd->h_log_handle, 77107c478bd9Sstevel@tonic-gate "%s: Invalid string request", msg); 77117c478bd9Sstevel@tonic-gate rv = EINVAL; 77127c478bd9Sstevel@tonic-gate 77137c478bd9Sstevel@tonic-gate break; 77147c478bd9Sstevel@tonic-gate } /* end of switch */ 77157c478bd9Sstevel@tonic-gate 77167c478bd9Sstevel@tonic-gate if (rv != 0) { 77177c478bd9Sstevel@tonic-gate 77187c478bd9Sstevel@tonic-gate break; 77197c478bd9Sstevel@tonic-gate } 77207c478bd9Sstevel@tonic-gate 77217c478bd9Sstevel@tonic-gate size = (str != NULL) ? strlen(str) + 1 : 0; 77227c478bd9Sstevel@tonic-gate if (ioc.get_size) { 77237c478bd9Sstevel@tonic-gate if (ddi_copyout((void *)&size, ioc.buf, 77247c478bd9Sstevel@tonic-gate ioc.bufsiz, mode) != 0) { 77257c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 77267c478bd9Sstevel@tonic-gate hubd->h_log_handle, 77277c478bd9Sstevel@tonic-gate "%s: copyout of size failed.", msg); 77287c478bd9Sstevel@tonic-gate rv = EIO; 77297c478bd9Sstevel@tonic-gate 77307c478bd9Sstevel@tonic-gate break; 77317c478bd9Sstevel@tonic-gate } 77327c478bd9Sstevel@tonic-gate } else { 77337c478bd9Sstevel@tonic-gate if (size == 0) { 77347c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_CBOPS, 77357c478bd9Sstevel@tonic-gate hubd->h_log_handle, 77367c478bd9Sstevel@tonic-gate "%s: String is NULL", msg); 77377c478bd9Sstevel@tonic-gate rv = EINVAL; 77387c478bd9Sstevel@tonic-gate 77397c478bd9Sstevel@tonic-gate break; 77407c478bd9Sstevel@tonic-gate } 77417c478bd9Sstevel@tonic-gate 77427c478bd9Sstevel@tonic-gate if (ioc.bufsiz != size) { 77437c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 77447c478bd9Sstevel@tonic-gate hubd->h_log_handle, 77457c478bd9Sstevel@tonic-gate "%s: string buf size wrong", msg); 77467c478bd9Sstevel@tonic-gate rv = EINVAL; 77477c478bd9Sstevel@tonic-gate 77487c478bd9Sstevel@tonic-gate break; 77497c478bd9Sstevel@tonic-gate } 77507c478bd9Sstevel@tonic-gate 77517c478bd9Sstevel@tonic-gate if (ddi_copyout((void *)str, ioc.buf, 77527c478bd9Sstevel@tonic-gate ioc.bufsiz, mode) != 0) { 77537c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 77547c478bd9Sstevel@tonic-gate hubd->h_log_handle, 77557c478bd9Sstevel@tonic-gate "%s: copyout failed.", msg); 77567c478bd9Sstevel@tonic-gate rv = EIO; 77577c478bd9Sstevel@tonic-gate 77587c478bd9Sstevel@tonic-gate break; 77597c478bd9Sstevel@tonic-gate } 77607c478bd9Sstevel@tonic-gate } 77617c478bd9Sstevel@tonic-gate break; 77627c478bd9Sstevel@tonic-gate } 77637c478bd9Sstevel@tonic-gate case HUBD_GET_CFGADM_NAME: 77647c478bd9Sstevel@tonic-gate { 77657c478bd9Sstevel@tonic-gate uint32_t name_len; 77667c478bd9Sstevel@tonic-gate const char *name; 77677c478bd9Sstevel@tonic-gate 77687c478bd9Sstevel@tonic-gate /* recheck */ 77697c478bd9Sstevel@tonic-gate if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) == 77707c478bd9Sstevel@tonic-gate NULL) { 77717c478bd9Sstevel@tonic-gate rv = EINVAL; 77727c478bd9Sstevel@tonic-gate 77737c478bd9Sstevel@tonic-gate break; 77747c478bd9Sstevel@tonic-gate } 77757c478bd9Sstevel@tonic-gate name = ddi_node_name(child_dip); 77767c478bd9Sstevel@tonic-gate if (name == NULL) { 77777c478bd9Sstevel@tonic-gate name = "unsupported"; 77787c478bd9Sstevel@tonic-gate } 77797c478bd9Sstevel@tonic-gate name_len = strlen(name) + 1; 77807c478bd9Sstevel@tonic-gate 77817c478bd9Sstevel@tonic-gate msg = "DEVCTL_AP_CONTROL: HUBD_GET_CFGADM_NAME"; 77827c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 77837c478bd9Sstevel@tonic-gate "%s: name=%s name_len=%d", msg, name, name_len); 77847c478bd9Sstevel@tonic-gate 77857c478bd9Sstevel@tonic-gate if (ioc.get_size) { 77867c478bd9Sstevel@tonic-gate if (ddi_copyout((void *)&name_len, 77877c478bd9Sstevel@tonic-gate ioc.buf, ioc.bufsiz, mode) != 0) { 77887c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 77897c478bd9Sstevel@tonic-gate hubd->h_log_handle, 77907c478bd9Sstevel@tonic-gate "%s: copyout of size failed", msg); 77917c478bd9Sstevel@tonic-gate rv = EIO; 77927c478bd9Sstevel@tonic-gate 77937c478bd9Sstevel@tonic-gate break; 77947c478bd9Sstevel@tonic-gate } 77957c478bd9Sstevel@tonic-gate } else { 77967c478bd9Sstevel@tonic-gate if (ioc.bufsiz != name_len) { 77977c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 77987c478bd9Sstevel@tonic-gate hubd->h_log_handle, 77997c478bd9Sstevel@tonic-gate "%s: string buf length wrong", msg); 78007c478bd9Sstevel@tonic-gate rv = EINVAL; 78017c478bd9Sstevel@tonic-gate 78027c478bd9Sstevel@tonic-gate break; 78037c478bd9Sstevel@tonic-gate } 78047c478bd9Sstevel@tonic-gate 78057c478bd9Sstevel@tonic-gate if (ddi_copyout((void *)name, ioc.buf, 78067c478bd9Sstevel@tonic-gate ioc.bufsiz, mode) != 0) { 78077c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 78087c478bd9Sstevel@tonic-gate hubd->h_log_handle, 78097c478bd9Sstevel@tonic-gate "%s: copyout failed.", msg); 78107c478bd9Sstevel@tonic-gate rv = EIO; 78117c478bd9Sstevel@tonic-gate 78127c478bd9Sstevel@tonic-gate break; 78137c478bd9Sstevel@tonic-gate } 78147c478bd9Sstevel@tonic-gate } 78157c478bd9Sstevel@tonic-gate 78167c478bd9Sstevel@tonic-gate break; 78177c478bd9Sstevel@tonic-gate } 78187c478bd9Sstevel@tonic-gate 78197c478bd9Sstevel@tonic-gate /* 78207c478bd9Sstevel@tonic-gate * Return the config index for the currently-configured 78217c478bd9Sstevel@tonic-gate * configuration. 78227c478bd9Sstevel@tonic-gate */ 78237c478bd9Sstevel@tonic-gate case HUBD_GET_CURRENT_CONFIG: 78247c478bd9Sstevel@tonic-gate { 78257c478bd9Sstevel@tonic-gate uint_t config_index; 78267c478bd9Sstevel@tonic-gate uint32_t size = sizeof (config_index); 78277c478bd9Sstevel@tonic-gate usba_device_t *usba_device; 78287c478bd9Sstevel@tonic-gate 78297c478bd9Sstevel@tonic-gate msg = "DEVCTL_AP_CONTROL: GET_CURRENT_CONFIG"; 78307c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 78317c478bd9Sstevel@tonic-gate "%s", msg); 78327c478bd9Sstevel@tonic-gate 78337c478bd9Sstevel@tonic-gate /* 78347c478bd9Sstevel@tonic-gate * Return the config index for the configuration 78357c478bd9Sstevel@tonic-gate * currently in use. 78367c478bd9Sstevel@tonic-gate * Recheck if child_dip exists 78377c478bd9Sstevel@tonic-gate */ 78387c478bd9Sstevel@tonic-gate if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) == 78397c478bd9Sstevel@tonic-gate NULL) { 78407c478bd9Sstevel@tonic-gate rv = EINVAL; 78417c478bd9Sstevel@tonic-gate 78427c478bd9Sstevel@tonic-gate break; 78437c478bd9Sstevel@tonic-gate } 78447c478bd9Sstevel@tonic-gate 78457c478bd9Sstevel@tonic-gate usba_device = usba_get_usba_device(child_dip); 78467c478bd9Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex); 78477c478bd9Sstevel@tonic-gate config_index = usba_device->usb_active_cfg_ndx; 78487c478bd9Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex); 78497c478bd9Sstevel@tonic-gate 78507c478bd9Sstevel@tonic-gate if (ioc.get_size) { 78517c478bd9Sstevel@tonic-gate if (ddi_copyout((void *)&size, 78527c478bd9Sstevel@tonic-gate ioc.buf, ioc.bufsiz, mode) != 0) { 78537c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 78547c478bd9Sstevel@tonic-gate hubd->h_log_handle, 78557c478bd9Sstevel@tonic-gate "%s: copyout of size failed.", msg); 78567c478bd9Sstevel@tonic-gate rv = EIO; 78577c478bd9Sstevel@tonic-gate 78587c478bd9Sstevel@tonic-gate break; 78597c478bd9Sstevel@tonic-gate } 78607c478bd9Sstevel@tonic-gate } else { 78617c478bd9Sstevel@tonic-gate if (ioc.bufsiz != size) { 78627c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 78637c478bd9Sstevel@tonic-gate hubd->h_log_handle, 78647c478bd9Sstevel@tonic-gate "%s: buffer size wrong", msg); 78657c478bd9Sstevel@tonic-gate rv = EINVAL; 78667c478bd9Sstevel@tonic-gate 78677c478bd9Sstevel@tonic-gate break; 78687c478bd9Sstevel@tonic-gate } 78697c478bd9Sstevel@tonic-gate if (ddi_copyout((void *)&config_index, 78707c478bd9Sstevel@tonic-gate ioc.buf, ioc.bufsiz, mode) != 0) { 78717c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 78727c478bd9Sstevel@tonic-gate hubd->h_log_handle, 78737c478bd9Sstevel@tonic-gate "%s: copyout failed", msg); 78747c478bd9Sstevel@tonic-gate rv = EIO; 78757c478bd9Sstevel@tonic-gate } 78767c478bd9Sstevel@tonic-gate } 78777c478bd9Sstevel@tonic-gate 78787c478bd9Sstevel@tonic-gate break; 78797c478bd9Sstevel@tonic-gate } 78807c478bd9Sstevel@tonic-gate case HUBD_GET_DEVICE_PATH: 78817c478bd9Sstevel@tonic-gate { 78827c478bd9Sstevel@tonic-gate char *path; 78837c478bd9Sstevel@tonic-gate uint32_t size; 78847c478bd9Sstevel@tonic-gate 78857c478bd9Sstevel@tonic-gate msg = "DEVCTL_AP_CONTROL: GET_DEVICE_PATH"; 78867c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 78877c478bd9Sstevel@tonic-gate "%s", msg); 78887c478bd9Sstevel@tonic-gate 78897c478bd9Sstevel@tonic-gate /* Recheck if child_dip exists */ 78907c478bd9Sstevel@tonic-gate if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) == 78917c478bd9Sstevel@tonic-gate NULL) { 78927c478bd9Sstevel@tonic-gate rv = EINVAL; 78937c478bd9Sstevel@tonic-gate 78947c478bd9Sstevel@tonic-gate break; 78957c478bd9Sstevel@tonic-gate } 78967c478bd9Sstevel@tonic-gate 78977c478bd9Sstevel@tonic-gate /* ddi_pathname doesn't supply /devices, so we do. */ 78987c478bd9Sstevel@tonic-gate path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 78997c478bd9Sstevel@tonic-gate (void) strcpy(path, "/devices"); 79007c478bd9Sstevel@tonic-gate (void) ddi_pathname(child_dip, path + strlen(path)); 79017c478bd9Sstevel@tonic-gate size = strlen(path) + 1; 79027c478bd9Sstevel@tonic-gate 79037c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 79047c478bd9Sstevel@tonic-gate "%s: device path=%s size=%d", msg, path, size); 79057c478bd9Sstevel@tonic-gate 79067c478bd9Sstevel@tonic-gate if (ioc.get_size) { 79077c478bd9Sstevel@tonic-gate if (ddi_copyout((void *)&size, 79087c478bd9Sstevel@tonic-gate ioc.buf, ioc.bufsiz, mode) != 0) { 79097c478bd9Sstevel@tonic-gate 79107c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 79117c478bd9Sstevel@tonic-gate hubd->h_log_handle, 79127c478bd9Sstevel@tonic-gate "%s: copyout of size failed.", msg); 79137c478bd9Sstevel@tonic-gate rv = EIO; 79147c478bd9Sstevel@tonic-gate } 79157c478bd9Sstevel@tonic-gate } else { 79167c478bd9Sstevel@tonic-gate if (ioc.bufsiz != size) { 79177c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 79187c478bd9Sstevel@tonic-gate hubd->h_log_handle, 79197c478bd9Sstevel@tonic-gate "%s: buffer wrong size.", msg); 79207c478bd9Sstevel@tonic-gate rv = EINVAL; 79217c478bd9Sstevel@tonic-gate } else if (ddi_copyout((void *)path, 79227c478bd9Sstevel@tonic-gate ioc.buf, ioc.bufsiz, mode) != 0) { 79237c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 79247c478bd9Sstevel@tonic-gate hubd->h_log_handle, 79257c478bd9Sstevel@tonic-gate "%s: copyout failed.", msg); 79267c478bd9Sstevel@tonic-gate rv = EIO; 79277c478bd9Sstevel@tonic-gate } 79287c478bd9Sstevel@tonic-gate } 79297c478bd9Sstevel@tonic-gate kmem_free(path, MAXPATHLEN); 79307c478bd9Sstevel@tonic-gate 79317c478bd9Sstevel@tonic-gate break; 79327c478bd9Sstevel@tonic-gate } 79337c478bd9Sstevel@tonic-gate case HUBD_REFRESH_DEVDB: 79347c478bd9Sstevel@tonic-gate msg = "DEVCTL_AP_CONTROL: HUBD_REFRESH_DEVDB"; 79357c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_CBOPS, hubd->h_log_handle, 79367c478bd9Sstevel@tonic-gate "%s", msg); 79377c478bd9Sstevel@tonic-gate 79387c478bd9Sstevel@tonic-gate if ((rv = usba_devdb_refresh()) != USB_SUCCESS) { 79397c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, 79407c478bd9Sstevel@tonic-gate hubd->h_log_handle, 79417c478bd9Sstevel@tonic-gate "%s: Failed: %d", msg, rv); 79427c478bd9Sstevel@tonic-gate rv = EIO; 79437c478bd9Sstevel@tonic-gate } 79447c478bd9Sstevel@tonic-gate 79457c478bd9Sstevel@tonic-gate break; 79467c478bd9Sstevel@tonic-gate default: 79477c478bd9Sstevel@tonic-gate rv = ENOTSUP; 79487c478bd9Sstevel@tonic-gate } /* end switch */ 79497c478bd9Sstevel@tonic-gate 79507c478bd9Sstevel@tonic-gate break; 79517c478bd9Sstevel@tonic-gate } 79527c478bd9Sstevel@tonic-gate 79537c478bd9Sstevel@tonic-gate default: 79547c478bd9Sstevel@tonic-gate rv = ENOTTY; 79557c478bd9Sstevel@tonic-gate } 79567c478bd9Sstevel@tonic-gate 79577c478bd9Sstevel@tonic-gate if (dcp) { 79587c478bd9Sstevel@tonic-gate ndi_dc_freehdl(dcp); 79597c478bd9Sstevel@tonic-gate } 79607c478bd9Sstevel@tonic-gate 79617c478bd9Sstevel@tonic-gate /* allow hotplug thread now */ 79627c478bd9Sstevel@tonic-gate hubd->h_hotplug_thread--; 79637c478bd9Sstevel@tonic-gate 79647c478bd9Sstevel@tonic-gate if ((hubd->h_dev_state == USB_DEV_ONLINE) && 79657c478bd9Sstevel@tonic-gate hubd->h_ep1_ph && (prev_pipe_state == USB_PIPE_STATE_ACTIVE)) { 79667c478bd9Sstevel@tonic-gate hubd_start_polling(hubd, 0); 79677c478bd9Sstevel@tonic-gate } 79687c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 79697c478bd9Sstevel@tonic-gate 79707c478bd9Sstevel@tonic-gate ndi_devi_exit(hubd->h_dip, circ); 79717c478bd9Sstevel@tonic-gate ndi_devi_exit(rh_dip, rh_circ); 79727c478bd9Sstevel@tonic-gate ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ); 79737c478bd9Sstevel@tonic-gate 79747c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 79757c478bd9Sstevel@tonic-gate hubd_pm_idle_component(hubd, hubd->h_dip, 0); 79767c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 79777c478bd9Sstevel@tonic-gate 79787c478bd9Sstevel@tonic-gate return (rv); 79797c478bd9Sstevel@tonic-gate } 79807c478bd9Sstevel@tonic-gate 79817c478bd9Sstevel@tonic-gate 79827c478bd9Sstevel@tonic-gate /* 79837c478bd9Sstevel@tonic-gate * Helper func used only to help construct the names for the attachment point 79847c478bd9Sstevel@tonic-gate * minor nodes. Used only in usba_hubdi_attach. 79857c478bd9Sstevel@tonic-gate * Returns whether it found ancestry or not (USB_SUCCESS if yes). 79867c478bd9Sstevel@tonic-gate * ports between the root hub and the device represented by dip. 79877c478bd9Sstevel@tonic-gate * E.g., "2.4.3.1" means this device is 79887c478bd9Sstevel@tonic-gate * plugged into port 1 of a hub that is 79897c478bd9Sstevel@tonic-gate * plugged into port 3 of a hub that is 79907c478bd9Sstevel@tonic-gate * plugged into port 4 of a hub that is 79917c478bd9Sstevel@tonic-gate * plugged into port 2 of the root hub. 79927c478bd9Sstevel@tonic-gate * NOTE: Max ap_id path len is HUBD_APID_NAMELEN (32 chars), which is 79937c478bd9Sstevel@tonic-gate * more than sufficient (as hubs are a max 6 levels deep, port needs 3 79947c478bd9Sstevel@tonic-gate * chars plus NULL each) 79957c478bd9Sstevel@tonic-gate */ 7996ff0e937bSRaymond Chen void 79977c478bd9Sstevel@tonic-gate hubd_get_ancestry_str(hubd_t *hubd) 79987c478bd9Sstevel@tonic-gate { 7999ff0e937bSRaymond Chen char ap_name[HUBD_APID_NAMELEN]; 8000ff0e937bSRaymond Chen dev_info_t *pdip; 8001ff0e937bSRaymond Chen hubd_t *phubd; 8002ff0e937bSRaymond Chen usb_port_t port; 80037c478bd9Sstevel@tonic-gate 80047c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 8005112116d8Sfb "hubd_get_ancestry_str: hubd=0x%p", (void *)hubd); 80067c478bd9Sstevel@tonic-gate 8007ff0e937bSRaymond Chen ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 8008ff0e937bSRaymond Chen 8009ff0e937bSRaymond Chen /* 8010ff0e937bSRaymond Chen * The function is extended to support wire adapter class 8011ff0e937bSRaymond Chen * devices introduced by WUSB spec. The node name is no 8012ff0e937bSRaymond Chen * longer "hub" only. 8013ff0e937bSRaymond Chen * Generate the ap_id str based on the parent and child 8014ff0e937bSRaymond Chen * relationship instead of retrieving it from the hub 8015ff0e937bSRaymond Chen * device path, which simplifies the algorithm. 8016ff0e937bSRaymond Chen */ 8017ff0e937bSRaymond Chen if (usba_is_root_hub(hubd->h_dip)) { 8018ff0e937bSRaymond Chen hubd->h_ancestry_str[0] = '\0'; 8019ff0e937bSRaymond Chen } else { 8020ff0e937bSRaymond Chen port = hubd->h_usba_device->usb_port; 8021ff0e937bSRaymond Chen mutex_exit(HUBD_MUTEX(hubd)); 80227c478bd9Sstevel@tonic-gate 8023ff0e937bSRaymond Chen pdip = ddi_get_parent(hubd->h_dip); 80247c478bd9Sstevel@tonic-gate /* 8025ff0e937bSRaymond Chen * The parent of wire adapter device might be usb_mid. 8026ff0e937bSRaymond Chen * Need to look further up for hub device 80277c478bd9Sstevel@tonic-gate */ 8028ff0e937bSRaymond Chen if (strcmp(ddi_driver_name(pdip), "usb_mid") == 0) { 8029ff0e937bSRaymond Chen pdip = ddi_get_parent(pdip); 8030ff0e937bSRaymond Chen ASSERT(pdip != NULL); 80317c478bd9Sstevel@tonic-gate } 80327c478bd9Sstevel@tonic-gate 8033ff0e937bSRaymond Chen phubd = hubd_get_soft_state(pdip); 80347c478bd9Sstevel@tonic-gate 8035ff0e937bSRaymond Chen mutex_enter(HUBD_MUTEX(phubd)); 8036ff0e937bSRaymond Chen (void) snprintf(ap_name, HUBD_APID_NAMELEN, "%s%d", 8037ff0e937bSRaymond Chen phubd->h_ancestry_str, port); 8038ff0e937bSRaymond Chen mutex_exit(HUBD_MUTEX(phubd)); 80397c478bd9Sstevel@tonic-gate 8040ff0e937bSRaymond Chen mutex_enter(HUBD_MUTEX(hubd)); 8041ff0e937bSRaymond Chen (void) strcpy(hubd->h_ancestry_str, ap_name); 80427c478bd9Sstevel@tonic-gate (void) strcat(hubd->h_ancestry_str, "."); 80437c478bd9Sstevel@tonic-gate } 80447c478bd9Sstevel@tonic-gate } 80457c478bd9Sstevel@tonic-gate 80467c478bd9Sstevel@tonic-gate 80477c478bd9Sstevel@tonic-gate /* Get which port to operate on. */ 80487c478bd9Sstevel@tonic-gate static usb_port_t 80497c478bd9Sstevel@tonic-gate hubd_get_port_num(hubd_t *hubd, struct devctl_iocdata *dcp) 80507c478bd9Sstevel@tonic-gate { 80517c478bd9Sstevel@tonic-gate int32_t port; 80527c478bd9Sstevel@tonic-gate 80537c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 80547c478bd9Sstevel@tonic-gate 80557c478bd9Sstevel@tonic-gate /* Get which port to operate on. */ 80567c478bd9Sstevel@tonic-gate if (nvlist_lookup_int32(ndi_dc_get_ap_data(dcp), "port", &port) != 0) { 80577c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, hubd->h_log_handle, 80587c478bd9Sstevel@tonic-gate "hubd_get_port_num: port lookup failed"); 80597c478bd9Sstevel@tonic-gate port = 0; 80607c478bd9Sstevel@tonic-gate } 80617c478bd9Sstevel@tonic-gate 80627c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 8063112116d8Sfb "hubd_get_port_num: hubd=0x%p, port=%d", (void *)hubd, port); 80647c478bd9Sstevel@tonic-gate 80657c478bd9Sstevel@tonic-gate return ((usb_port_t)port); 80667c478bd9Sstevel@tonic-gate } 80677c478bd9Sstevel@tonic-gate 80687c478bd9Sstevel@tonic-gate 80697c478bd9Sstevel@tonic-gate /* check if child still exists */ 80707c478bd9Sstevel@tonic-gate static dev_info_t * 80717c478bd9Sstevel@tonic-gate hubd_get_child_dip(hubd_t *hubd, usb_port_t port) 80727c478bd9Sstevel@tonic-gate { 80737c478bd9Sstevel@tonic-gate dev_info_t *child_dip = hubd->h_children_dips[port]; 80747c478bd9Sstevel@tonic-gate 80757c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 8076112116d8Sfb "hubd_get_child_dip: hubd=0x%p, port=%d", (void *)hubd, port); 80777c478bd9Sstevel@tonic-gate 80787c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 80797c478bd9Sstevel@tonic-gate 80807c478bd9Sstevel@tonic-gate return (child_dip); 80817c478bd9Sstevel@tonic-gate } 80827c478bd9Sstevel@tonic-gate 80837c478bd9Sstevel@tonic-gate 80847c478bd9Sstevel@tonic-gate /* 80857c478bd9Sstevel@tonic-gate * hubd_cfgadm_state: 80867c478bd9Sstevel@tonic-gate * 80877c478bd9Sstevel@tonic-gate * child_dip list port_state cfgadm_state 80887c478bd9Sstevel@tonic-gate * -------------- ---------- ------------ 80897c478bd9Sstevel@tonic-gate * != NULL connected configured or 80907c478bd9Sstevel@tonic-gate * unconfigured 80917c478bd9Sstevel@tonic-gate * != NULL not connected disconnect but 80927c478bd9Sstevel@tonic-gate * busy/still referenced 80937c478bd9Sstevel@tonic-gate * NULL connected logically disconnected 80947c478bd9Sstevel@tonic-gate * NULL not connected empty 80957c478bd9Sstevel@tonic-gate */ 80967c478bd9Sstevel@tonic-gate static uint_t 80977c478bd9Sstevel@tonic-gate hubd_cfgadm_state(hubd_t *hubd, usb_port_t port) 80987c478bd9Sstevel@tonic-gate { 80997c478bd9Sstevel@tonic-gate uint_t state; 81007c478bd9Sstevel@tonic-gate dev_info_t *child_dip = hubd_get_child_dip(hubd, port); 81017c478bd9Sstevel@tonic-gate 81027c478bd9Sstevel@tonic-gate if (child_dip) { 81037c478bd9Sstevel@tonic-gate if (hubd->h_port_state[port] & PORT_STATUS_CCS) { 81047c478bd9Sstevel@tonic-gate /* 81057c478bd9Sstevel@tonic-gate * connected, now check if driver exists 81067c478bd9Sstevel@tonic-gate */ 81077c478bd9Sstevel@tonic-gate if (DEVI_IS_DEVICE_OFFLINE(child_dip) || 8108737d277aScth !i_ddi_devi_attached(child_dip)) { 81097c478bd9Sstevel@tonic-gate state = HUBD_CFGADM_UNCONFIGURED; 81107c478bd9Sstevel@tonic-gate } else { 81117c478bd9Sstevel@tonic-gate state = HUBD_CFGADM_CONFIGURED; 81127c478bd9Sstevel@tonic-gate } 81137c478bd9Sstevel@tonic-gate } else { 81147c478bd9Sstevel@tonic-gate /* 81157c478bd9Sstevel@tonic-gate * this means that the dip is around for 81167c478bd9Sstevel@tonic-gate * a device that is still referenced but 81177c478bd9Sstevel@tonic-gate * has been yanked out. So the cfgadm info 81187c478bd9Sstevel@tonic-gate * for this state should be EMPTY (port empty) 81197c478bd9Sstevel@tonic-gate * and CONFIGURED (dip still valid). 81207c478bd9Sstevel@tonic-gate */ 81217c478bd9Sstevel@tonic-gate state = HUBD_CFGADM_STILL_REFERENCED; 81227c478bd9Sstevel@tonic-gate } 81237c478bd9Sstevel@tonic-gate } else { 81247c478bd9Sstevel@tonic-gate /* connected but no child dip */ 81257c478bd9Sstevel@tonic-gate if (hubd->h_port_state[port] & PORT_STATUS_CCS) { 81267c478bd9Sstevel@tonic-gate /* logically disconnected */ 81277c478bd9Sstevel@tonic-gate state = HUBD_CFGADM_DISCONNECTED; 81287c478bd9Sstevel@tonic-gate } else { 81297c478bd9Sstevel@tonic-gate /* physically disconnected */ 81307c478bd9Sstevel@tonic-gate state = HUBD_CFGADM_EMPTY; 81317c478bd9Sstevel@tonic-gate } 81327c478bd9Sstevel@tonic-gate } 81337c478bd9Sstevel@tonic-gate 81347c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 81357c478bd9Sstevel@tonic-gate "hubd_cfgadm_state: hubd=0x%p, port=%d state=0x%x", 8136112116d8Sfb (void *)hubd, port, state); 81377c478bd9Sstevel@tonic-gate 81387c478bd9Sstevel@tonic-gate return (state); 81397c478bd9Sstevel@tonic-gate } 81407c478bd9Sstevel@tonic-gate 81417c478bd9Sstevel@tonic-gate 81427c478bd9Sstevel@tonic-gate /* 81437c478bd9Sstevel@tonic-gate * hubd_toggle_port: 81447c478bd9Sstevel@tonic-gate */ 81457c478bd9Sstevel@tonic-gate static int 81467c478bd9Sstevel@tonic-gate hubd_toggle_port(hubd_t *hubd, usb_port_t port) 81477c478bd9Sstevel@tonic-gate { 81487c478bd9Sstevel@tonic-gate usb_hub_descr_t *hub_descr; 81497c478bd9Sstevel@tonic-gate int wait; 81507c478bd9Sstevel@tonic-gate uint_t retry; 81517c478bd9Sstevel@tonic-gate uint16_t status; 81527c478bd9Sstevel@tonic-gate uint16_t change; 81537c478bd9Sstevel@tonic-gate 81547c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, 8155112116d8Sfb "hubd_toggle_port: hubd=0x%p, port=%d", (void *)hubd, port); 81567c478bd9Sstevel@tonic-gate 81577c478bd9Sstevel@tonic-gate if ((hubd_disable_port_power(hubd, port)) != USB_SUCCESS) { 81587c478bd9Sstevel@tonic-gate 81597c478bd9Sstevel@tonic-gate return (USB_FAILURE); 81607c478bd9Sstevel@tonic-gate } 81617c478bd9Sstevel@tonic-gate 81627c478bd9Sstevel@tonic-gate /* 81637c478bd9Sstevel@tonic-gate * see hubd_enable_all_port_power() which 81647c478bd9Sstevel@tonic-gate * requires longer delay for hubs. 81657c478bd9Sstevel@tonic-gate */ 81667c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 81677c478bd9Sstevel@tonic-gate delay(drv_usectohz(hubd_device_delay / 10)); 81687c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 81697c478bd9Sstevel@tonic-gate 81707c478bd9Sstevel@tonic-gate hub_descr = &hubd->h_hub_descr; 81717c478bd9Sstevel@tonic-gate 81727c478bd9Sstevel@tonic-gate /* 81737c478bd9Sstevel@tonic-gate * According to section 11.11 of USB, for hubs with no power 81747c478bd9Sstevel@tonic-gate * switches, bPwrOn2PwrGood is zero. But we wait for some 81757c478bd9Sstevel@tonic-gate * arbitrary time to enable power to become stable. 81767c478bd9Sstevel@tonic-gate * 81777c478bd9Sstevel@tonic-gate * If an hub supports port power swicthing, we need to wait 81787c478bd9Sstevel@tonic-gate * at least 20ms before accesing corresonding usb port. 81797c478bd9Sstevel@tonic-gate */ 81807c478bd9Sstevel@tonic-gate if ((hub_descr->wHubCharacteristics & 81817c478bd9Sstevel@tonic-gate HUB_CHARS_NO_POWER_SWITCHING) || (!hub_descr->bPwrOn2PwrGood)) { 81827c478bd9Sstevel@tonic-gate wait = hubd_device_delay / 10; 81837c478bd9Sstevel@tonic-gate } else { 81847c478bd9Sstevel@tonic-gate wait = max(HUB_DEFAULT_POPG, 81857c478bd9Sstevel@tonic-gate hub_descr->bPwrOn2PwrGood) * 2 * 1000; 81867c478bd9Sstevel@tonic-gate } 81877c478bd9Sstevel@tonic-gate 81887c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, 81897c478bd9Sstevel@tonic-gate "hubd_toggle_port: popg=%d wait=%d", 81907c478bd9Sstevel@tonic-gate hub_descr->bPwrOn2PwrGood, wait); 81917c478bd9Sstevel@tonic-gate 81927c478bd9Sstevel@tonic-gate retry = 0; 81937c478bd9Sstevel@tonic-gate 81947c478bd9Sstevel@tonic-gate do { 81957c478bd9Sstevel@tonic-gate (void) hubd_enable_port_power(hubd, port); 81967c478bd9Sstevel@tonic-gate 81977c478bd9Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd)); 81987c478bd9Sstevel@tonic-gate delay(drv_usectohz(wait)); 81997c478bd9Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd)); 82007c478bd9Sstevel@tonic-gate 82017c478bd9Sstevel@tonic-gate /* Get port status */ 82027c478bd9Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, 82037c478bd9Sstevel@tonic-gate &status, &change, 0); 82047c478bd9Sstevel@tonic-gate 82057c478bd9Sstevel@tonic-gate /* For retry if any, use some extra delay */ 82067c478bd9Sstevel@tonic-gate wait = max(wait, hubd_device_delay / 10); 82077c478bd9Sstevel@tonic-gate 82087c478bd9Sstevel@tonic-gate retry++; 82097c478bd9Sstevel@tonic-gate 82107c478bd9Sstevel@tonic-gate } while ((!(status & PORT_STATUS_PPS)) && (retry < HUBD_PORT_RETRY)); 82117c478bd9Sstevel@tonic-gate 82127c478bd9Sstevel@tonic-gate /* Print warning message if port has no power */ 82137c478bd9Sstevel@tonic-gate if (!(status & PORT_STATUS_PPS)) { 82147c478bd9Sstevel@tonic-gate 8215d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, 82167c478bd9Sstevel@tonic-gate "hubd_toggle_port: port %d power-on failed, " 82177c478bd9Sstevel@tonic-gate "port status 0x%x", port, status); 82187c478bd9Sstevel@tonic-gate 82197c478bd9Sstevel@tonic-gate return (USB_FAILURE); 82207c478bd9Sstevel@tonic-gate } 82217c478bd9Sstevel@tonic-gate 82227c478bd9Sstevel@tonic-gate return (USB_SUCCESS); 82237c478bd9Sstevel@tonic-gate } 822435f36846Ssl 822535f36846Ssl 822635f36846Ssl /* 822735f36846Ssl * hubd_init_power_budget: 822835f36846Ssl * Init power budget variables in hubd structure. According 822935f36846Ssl * to USB spec, the power budget rules are: 823035f36846Ssl * 1. local-powered hubs including root-hubs can supply 823135f36846Ssl * 500mA to each port at maximum 823235f36846Ssl * 2. two bus-powered hubs are not allowed to concatenate 823335f36846Ssl * 3. bus-powered hubs can supply 100mA to each port at 823435f36846Ssl * maximum, and the power consumed by all downstream 823535f36846Ssl * ports and the hub itself cannot exceed the max power 823635f36846Ssl * supplied by the upstream port, i.e., 500mA 823735f36846Ssl * The routine is only called during hub attach time 823835f36846Ssl */ 823935f36846Ssl static int 824035f36846Ssl hubd_init_power_budget(hubd_t *hubd) 824135f36846Ssl { 824235f36846Ssl uint16_t status = 0; 824335f36846Ssl usba_device_t *hubd_ud = NULL; 824435f36846Ssl size_t size; 824535f36846Ssl usb_cfg_descr_t cfg_descr; 824635f36846Ssl dev_info_t *pdip = NULL; 824735f36846Ssl hubd_t *phubd = NULL; 824835f36846Ssl 824935f36846Ssl if (hubd->h_ignore_pwr_budget) { 825035f36846Ssl 825135f36846Ssl return (USB_SUCCESS); 825235f36846Ssl } 825335f36846Ssl 825435f36846Ssl USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, 825535f36846Ssl "hubd_init_power_budget:"); 825635f36846Ssl 825735f36846Ssl ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 825835f36846Ssl ASSERT(hubd->h_default_pipe != 0); 825935f36846Ssl mutex_exit(HUBD_MUTEX(hubd)); 826035f36846Ssl 826135f36846Ssl /* get device status */ 826235f36846Ssl if ((usb_get_status(hubd->h_dip, hubd->h_default_pipe, 826335f36846Ssl HUB_GET_DEVICE_STATUS_TYPE, 826435f36846Ssl 0, &status, 0)) != USB_SUCCESS) { 826535f36846Ssl mutex_enter(HUBD_MUTEX(hubd)); 826635f36846Ssl 826735f36846Ssl return (USB_FAILURE); 826835f36846Ssl } 826935f36846Ssl 827035f36846Ssl hubd_ud = usba_get_usba_device(hubd->h_dip); 827135f36846Ssl 827235f36846Ssl size = usb_parse_cfg_descr(hubd_ud->usb_cfg, hubd_ud->usb_cfg_length, 827335f36846Ssl &cfg_descr, USB_CFG_DESCR_SIZE); 827435f36846Ssl 827535f36846Ssl if (size != USB_CFG_DESCR_SIZE) { 827635f36846Ssl USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle, 827735f36846Ssl "get hub configuration descriptor failed"); 827835f36846Ssl mutex_enter(HUBD_MUTEX(hubd)); 827935f36846Ssl 828035f36846Ssl return (USB_FAILURE); 828135f36846Ssl } 828235f36846Ssl 828335f36846Ssl mutex_enter(HUBD_MUTEX(hubd)); 828435f36846Ssl 828535f36846Ssl hubd->h_local_pwr_capable = (cfg_descr.bmAttributes & 828635f36846Ssl USB_CFG_ATTR_SELFPWR); 828735f36846Ssl 828835f36846Ssl if (hubd->h_local_pwr_capable) { 828935f36846Ssl USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle, 829035f36846Ssl "hub is capable of local power"); 829135f36846Ssl } 829235f36846Ssl 829335f36846Ssl hubd->h_local_pwr_on = (status & 829435f36846Ssl USB_DEV_SLF_PWRD_STATUS) && hubd->h_local_pwr_capable; 829535f36846Ssl 829635f36846Ssl if (hubd->h_local_pwr_on) { 829735f36846Ssl USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle, 829835f36846Ssl "hub is local-powered"); 829935f36846Ssl 830035f36846Ssl hubd->h_pwr_limit = (USB_PWR_UNIT_LOAD * 830135f36846Ssl USB_HIGH_PWR_VALUE) / USB_CFG_DESCR_PWR_UNIT; 830235f36846Ssl } else { 830335f36846Ssl hubd->h_pwr_limit = (USB_PWR_UNIT_LOAD * 830435f36846Ssl USB_LOW_PWR_VALUE) / USB_CFG_DESCR_PWR_UNIT; 830535f36846Ssl 830635f36846Ssl hubd->h_pwr_left = (USB_PWR_UNIT_LOAD * 830735f36846Ssl USB_HIGH_PWR_VALUE) / USB_CFG_DESCR_PWR_UNIT; 830835f36846Ssl 830935f36846Ssl ASSERT(!usba_is_root_hub(hubd->h_dip)); 831035f36846Ssl 831135f36846Ssl if (!usba_is_root_hub(hubd->h_dip)) { 831235f36846Ssl /* 831335f36846Ssl * two bus-powered hubs are not 831435f36846Ssl * allowed to be concatenated 831535f36846Ssl */ 831635f36846Ssl mutex_exit(HUBD_MUTEX(hubd)); 831735f36846Ssl 831835f36846Ssl pdip = ddi_get_parent(hubd->h_dip); 831935f36846Ssl phubd = hubd_get_soft_state(pdip); 832035f36846Ssl ASSERT(phubd != NULL); 832135f36846Ssl 832235f36846Ssl if (!phubd->h_ignore_pwr_budget) { 832335f36846Ssl mutex_enter(HUBD_MUTEX(phubd)); 832435f36846Ssl if (phubd->h_local_pwr_on == B_FALSE) { 83258668df41Slg USB_DPRINTF_L1(DPRINT_MASK_HUB, 832635f36846Ssl hubd->h_log_handle, 832735f36846Ssl "two bus-powered hubs cannot " 832835f36846Ssl "be concatenated"); 832935f36846Ssl 833035f36846Ssl mutex_exit(HUBD_MUTEX(phubd)); 833135f36846Ssl mutex_enter(HUBD_MUTEX(hubd)); 833235f36846Ssl 833335f36846Ssl return (USB_FAILURE); 833435f36846Ssl } 833535f36846Ssl mutex_exit(HUBD_MUTEX(phubd)); 833635f36846Ssl } 833735f36846Ssl 833835f36846Ssl mutex_enter(HUBD_MUTEX(hubd)); 833935f36846Ssl 834035f36846Ssl USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle, 834135f36846Ssl "hub is bus-powered"); 834235f36846Ssl } else { 834335f36846Ssl USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle, 834435f36846Ssl "root-hub must be local-powered"); 834535f36846Ssl } 834635f36846Ssl 834735f36846Ssl /* 834835f36846Ssl * Subtract the power consumed by the hub itself 834935f36846Ssl * and get the power that can be supplied to 835035f36846Ssl * downstream ports 835135f36846Ssl */ 835235f36846Ssl hubd->h_pwr_left -= 835335f36846Ssl hubd->h_hub_descr.bHubContrCurrent / 835435f36846Ssl USB_CFG_DESCR_PWR_UNIT; 835535f36846Ssl if (hubd->h_pwr_left < 0) { 835635f36846Ssl USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle, 835735f36846Ssl "hubd->h_pwr_left is less than bHubContrCurrent, " 835835f36846Ssl "should fail"); 835935f36846Ssl 836035f36846Ssl return (USB_FAILURE); 836135f36846Ssl } 836235f36846Ssl } 836335f36846Ssl 836435f36846Ssl return (USB_SUCCESS); 836535f36846Ssl } 836635f36846Ssl 836735f36846Ssl 836835f36846Ssl /* 836935f36846Ssl * usba_hubdi_check_power_budget: 837035f36846Ssl * Check if the hub has enough power budget to allow a 837135f36846Ssl * child device to select a configuration of config_index. 837235f36846Ssl */ 837335f36846Ssl int 837435f36846Ssl usba_hubdi_check_power_budget(dev_info_t *dip, usba_device_t *child_ud, 837535f36846Ssl uint_t config_index) 837635f36846Ssl { 837735f36846Ssl int16_t pwr_left, pwr_limit, pwr_required; 837835f36846Ssl size_t size; 837935f36846Ssl usb_cfg_descr_t cfg_descr; 838035f36846Ssl hubd_t *hubd; 838135f36846Ssl 838235f36846Ssl if ((hubd = hubd_get_soft_state(dip)) == NULL) { 838335f36846Ssl 838435f36846Ssl return (USB_FAILURE); 838535f36846Ssl } 838635f36846Ssl 838735f36846Ssl if (hubd->h_ignore_pwr_budget) { 838835f36846Ssl 838935f36846Ssl return (USB_SUCCESS); 839035f36846Ssl } 839135f36846Ssl 839235f36846Ssl USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 839335f36846Ssl "usba_hubdi_check_power_budget: " 8394112116d8Sfb "dip=0x%p child_ud=0x%p conf_index=%d", (void *)dip, 8395112116d8Sfb (void *)child_ud, config_index); 839635f36846Ssl 839735f36846Ssl mutex_enter(HUBD_MUTEX(hubd)); 839835f36846Ssl pwr_limit = hubd->h_pwr_limit; 839935f36846Ssl if (hubd->h_local_pwr_on == B_FALSE) { 840035f36846Ssl pwr_left = hubd->h_pwr_left; 840135f36846Ssl pwr_limit = (pwr_limit <= pwr_left) ? pwr_limit : pwr_left; 840235f36846Ssl } 840335f36846Ssl mutex_exit(HUBD_MUTEX(hubd)); 840435f36846Ssl 840535f36846Ssl USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 840635f36846Ssl "usba_hubdi_check_power_budget: " 840735f36846Ssl "available power is %dmA", pwr_limit * USB_CFG_DESCR_PWR_UNIT); 840835f36846Ssl 840935f36846Ssl size = usb_parse_cfg_descr( 841035f36846Ssl child_ud->usb_cfg_array[config_index], USB_CFG_DESCR_SIZE, 841135f36846Ssl &cfg_descr, USB_CFG_DESCR_SIZE); 841235f36846Ssl 841335f36846Ssl if (size != USB_CFG_DESCR_SIZE) { 841435f36846Ssl USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 841535f36846Ssl "get hub configuration descriptor failed"); 841635f36846Ssl 841735f36846Ssl return (USB_FAILURE); 841835f36846Ssl } 841935f36846Ssl 842035f36846Ssl pwr_required = cfg_descr.bMaxPower; 842135f36846Ssl 842235f36846Ssl USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 842335f36846Ssl "usba_hubdi_check_power_budget: " 842435f36846Ssl "child bmAttributes=0x%x bMaxPower=%d " 842535f36846Ssl "with config_index=%d", cfg_descr.bmAttributes, 842635f36846Ssl pwr_required, config_index); 842735f36846Ssl 842835f36846Ssl if (pwr_required > pwr_limit) { 84298668df41Slg USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 843035f36846Ssl "configuration %d for device %s %s at port %d " 843135f36846Ssl "exceeds power available for this port, please " 843235f36846Ssl "re-insert your device into another hub port which " 843335f36846Ssl "has enough power", 843435f36846Ssl config_index, 843535f36846Ssl child_ud->usb_mfg_str, 843635f36846Ssl child_ud->usb_product_str, 843735f36846Ssl child_ud->usb_port); 843835f36846Ssl 843935f36846Ssl return (USB_FAILURE); 844035f36846Ssl } 844135f36846Ssl 844235f36846Ssl return (USB_SUCCESS); 844335f36846Ssl } 844435f36846Ssl 844535f36846Ssl 844635f36846Ssl /* 844735f36846Ssl * usba_hubdi_incr_power_budget: 844835f36846Ssl * Increase the hub power budget value when a child device 844935f36846Ssl * is removed from a bus-powered hub port. 845035f36846Ssl */ 845135f36846Ssl void 845235f36846Ssl usba_hubdi_incr_power_budget(dev_info_t *dip, usba_device_t *child_ud) 845335f36846Ssl { 845435f36846Ssl uint16_t pwr_value; 845535f36846Ssl hubd_t *hubd = hubd_get_soft_state(dip); 845635f36846Ssl 845735f36846Ssl ASSERT(hubd != NULL); 845835f36846Ssl 845935f36846Ssl if (hubd->h_ignore_pwr_budget) { 846035f36846Ssl 846135f36846Ssl return; 846235f36846Ssl } 846335f36846Ssl 846435f36846Ssl USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 846535f36846Ssl "usba_hubdi_incr_power_budget: " 8466112116d8Sfb "dip=0x%p child_ud=0x%p", (void *)dip, (void *)child_ud); 846735f36846Ssl 846835f36846Ssl mutex_enter(HUBD_MUTEX(hubd)); 846935f36846Ssl if (hubd->h_local_pwr_on == B_TRUE) { 847035f36846Ssl USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle, 847135f36846Ssl "usba_hubdi_incr_power_budget: " 847235f36846Ssl "hub is local powered"); 847335f36846Ssl mutex_exit(HUBD_MUTEX(hubd)); 847435f36846Ssl 847535f36846Ssl return; 847635f36846Ssl } 847735f36846Ssl mutex_exit(HUBD_MUTEX(hubd)); 847835f36846Ssl 847935f36846Ssl mutex_enter(&child_ud->usb_mutex); 848035f36846Ssl if (child_ud->usb_pwr_from_hub == 0) { 848135f36846Ssl mutex_exit(&child_ud->usb_mutex); 848235f36846Ssl 848335f36846Ssl return; 848435f36846Ssl } 848535f36846Ssl pwr_value = child_ud->usb_pwr_from_hub; 848635f36846Ssl mutex_exit(&child_ud->usb_mutex); 848735f36846Ssl 848835f36846Ssl mutex_enter(HUBD_MUTEX(hubd)); 848935f36846Ssl hubd->h_pwr_left += pwr_value; 849035f36846Ssl 849135f36846Ssl USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle, 849235f36846Ssl "usba_hubdi_incr_power_budget: " 849335f36846Ssl "available power is %dmA, increased by %dmA", 849435f36846Ssl hubd->h_pwr_left * USB_CFG_DESCR_PWR_UNIT, 849535f36846Ssl pwr_value * USB_CFG_DESCR_PWR_UNIT); 849635f36846Ssl 849735f36846Ssl mutex_exit(HUBD_MUTEX(hubd)); 849835f36846Ssl 849935f36846Ssl mutex_enter(&child_ud->usb_mutex); 850035f36846Ssl child_ud->usb_pwr_from_hub = 0; 850135f36846Ssl mutex_exit(&child_ud->usb_mutex); 850235f36846Ssl } 850335f36846Ssl 850435f36846Ssl 850535f36846Ssl /* 850635f36846Ssl * usba_hubdi_decr_power_budget: 850735f36846Ssl * Decrease the hub power budget value when a child device 850835f36846Ssl * is inserted to a bus-powered hub port. 850935f36846Ssl */ 851035f36846Ssl void 851135f36846Ssl usba_hubdi_decr_power_budget(dev_info_t *dip, usba_device_t *child_ud) 851235f36846Ssl { 851335f36846Ssl uint16_t pwr_value; 851435f36846Ssl size_t size; 851535f36846Ssl usb_cfg_descr_t cfg_descr; 851635f36846Ssl hubd_t *hubd = hubd_get_soft_state(dip); 851735f36846Ssl 851835f36846Ssl ASSERT(hubd != NULL); 851935f36846Ssl 852035f36846Ssl if (hubd->h_ignore_pwr_budget) { 852135f36846Ssl 852235f36846Ssl return; 852335f36846Ssl } 852435f36846Ssl 852535f36846Ssl USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, 852635f36846Ssl "usba_hubdi_decr_power_budget: " 8527112116d8Sfb "dip=0x%p child_ud=0x%p", (void *)dip, (void *)child_ud); 852835f36846Ssl 852935f36846Ssl mutex_enter(HUBD_MUTEX(hubd)); 853035f36846Ssl if (hubd->h_local_pwr_on == B_TRUE) { 853135f36846Ssl USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle, 853235f36846Ssl "usba_hubdi_decr_power_budget: " 853335f36846Ssl "hub is local powered"); 853435f36846Ssl mutex_exit(HUBD_MUTEX(hubd)); 853535f36846Ssl 853635f36846Ssl return; 853735f36846Ssl } 853835f36846Ssl mutex_exit(HUBD_MUTEX(hubd)); 853935f36846Ssl 854035f36846Ssl mutex_enter(&child_ud->usb_mutex); 854135f36846Ssl if (child_ud->usb_pwr_from_hub > 0) { 854235f36846Ssl mutex_exit(&child_ud->usb_mutex); 854335f36846Ssl 854435f36846Ssl return; 854535f36846Ssl } 854635f36846Ssl mutex_exit(&child_ud->usb_mutex); 854735f36846Ssl 854835f36846Ssl size = usb_parse_cfg_descr( 854935f36846Ssl child_ud->usb_cfg, child_ud->usb_cfg_length, 855035f36846Ssl &cfg_descr, USB_CFG_DESCR_SIZE); 855135f36846Ssl ASSERT(size == USB_CFG_DESCR_SIZE); 855235f36846Ssl 855335f36846Ssl mutex_enter(HUBD_MUTEX(hubd)); 855435f36846Ssl pwr_value = cfg_descr.bMaxPower; 855535f36846Ssl hubd->h_pwr_left -= pwr_value; 855635f36846Ssl ASSERT(hubd->h_pwr_left >= 0); 855735f36846Ssl 855835f36846Ssl USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle, 855935f36846Ssl "usba_hubdi_decr_power_budget: " 856035f36846Ssl "available power is %dmA, decreased by %dmA", 856135f36846Ssl hubd->h_pwr_left * USB_CFG_DESCR_PWR_UNIT, 856235f36846Ssl pwr_value * USB_CFG_DESCR_PWR_UNIT); 856335f36846Ssl 856435f36846Ssl mutex_exit(HUBD_MUTEX(hubd)); 856535f36846Ssl 856635f36846Ssl mutex_enter(&child_ud->usb_mutex); 856735f36846Ssl child_ud->usb_pwr_from_hub = pwr_value; 856835f36846Ssl mutex_exit(&child_ud->usb_mutex); 856935f36846Ssl } 8570ffcd51f3Slg 8571ffcd51f3Slg /* 8572ffcd51f3Slg * hubd_wait_for_hotplug_exit: 8573112cd14aSqz * Waiting for the exit of the running hotplug thread or ioctl thread. 8574ffcd51f3Slg */ 8575ffcd51f3Slg static int 8576ffcd51f3Slg hubd_wait_for_hotplug_exit(hubd_t *hubd) 8577ffcd51f3Slg { 8578ffcd51f3Slg clock_t until = ddi_get_lbolt() + drv_usectohz(1000000); 8579ffcd51f3Slg int rval; 8580ffcd51f3Slg 8581ffcd51f3Slg ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 8582ffcd51f3Slg 8583ffcd51f3Slg if (hubd->h_hotplug_thread) { 8584ffcd51f3Slg USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 8585ffcd51f3Slg "waiting for hubd hotplug thread exit"); 8586ffcd51f3Slg rval = cv_timedwait(&hubd->h_cv_hotplug_dev, 8587ffcd51f3Slg &hubd->h_mutex, until); 8588ffcd51f3Slg 8589ffcd51f3Slg if ((rval <= 0) && (hubd->h_hotplug_thread)) { 8590ffcd51f3Slg 8591ffcd51f3Slg return (USB_FAILURE); 8592ffcd51f3Slg } 8593ffcd51f3Slg } 8594ffcd51f3Slg 8595ffcd51f3Slg return (USB_SUCCESS); 8596ffcd51f3Slg } 8597ffcd51f3Slg 8598ffcd51f3Slg /* 8599ffcd51f3Slg * hubd_reset_thread: 8600ffcd51f3Slg * handles the "USB_RESET_LVL_REATTACH" reset of usb device. 8601ffcd51f3Slg * 8602ffcd51f3Slg * - delete the child (force detaching the device and its children) 8603ffcd51f3Slg * - reset the corresponding parent hub port 8604ffcd51f3Slg * - create the child (force re-attaching the device and its children) 8605ffcd51f3Slg */ 8606ffcd51f3Slg static void 8607ffcd51f3Slg hubd_reset_thread(void *arg) 8608ffcd51f3Slg { 8609ffcd51f3Slg hubd_reset_arg_t *hd_arg = (hubd_reset_arg_t *)arg; 8610ffcd51f3Slg hubd_t *hubd = hd_arg->hubd; 8611ffcd51f3Slg uint16_t reset_port = hd_arg->reset_port; 8612ffcd51f3Slg uint16_t status, change; 8613ffcd51f3Slg hub_power_t *hubpm; 8614ffcd51f3Slg dev_info_t *hdip = hubd->h_dip; 8615ffcd51f3Slg dev_info_t *rh_dip = hubd->h_usba_device->usb_root_hub_dip; 8616ffcd51f3Slg dev_info_t *child_dip; 8617ffcd51f3Slg boolean_t online_child = B_FALSE; 8618ffcd51f3Slg int prh_circ, rh_circ, circ, devinst; 8619ffcd51f3Slg char *devname; 8620*aa041649SRaymond Chen int i = 0; 8621*aa041649SRaymond Chen int rval = USB_FAILURE; 8622ffcd51f3Slg 8623ffcd51f3Slg USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 8624ffcd51f3Slg "hubd_reset_thread: started, hubd_reset_port = 0x%x", reset_port); 8625ffcd51f3Slg 8626ffcd51f3Slg kmem_free(arg, sizeof (hubd_reset_arg_t)); 8627ffcd51f3Slg 8628ffcd51f3Slg mutex_enter(HUBD_MUTEX(hubd)); 8629ffcd51f3Slg 8630ffcd51f3Slg child_dip = hubd->h_children_dips[reset_port]; 8631ffcd51f3Slg ASSERT(child_dip != NULL); 8632ffcd51f3Slg 8633ffcd51f3Slg devname = (char *)ddi_driver_name(child_dip); 8634ffcd51f3Slg devinst = ddi_get_instance(child_dip); 8635ffcd51f3Slg 8636ffcd51f3Slg /* if our bus power entry point is active, quit the reset */ 8637ffcd51f3Slg if (hubd->h_bus_pwr) { 8638ffcd51f3Slg USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 8639ffcd51f3Slg "%s%d is under bus power management, cannot be reset. " 8640ffcd51f3Slg "Please disconnect and reconnect this device.", 8641ffcd51f3Slg devname, devinst); 8642ffcd51f3Slg 8643ffcd51f3Slg goto Fail; 8644ffcd51f3Slg } 8645ffcd51f3Slg 8646ffcd51f3Slg if (hubd_wait_for_hotplug_exit(hubd) == USB_FAILURE) { 8647ffcd51f3Slg /* we got woken up because of a timeout */ 8648ffcd51f3Slg USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, 8649ffcd51f3Slg hubd->h_log_handle, "Time out when resetting the device" 8650ffcd51f3Slg " %s%d. Please disconnect and reconnect this device.", 8651ffcd51f3Slg devname, devinst); 8652ffcd51f3Slg 8653ffcd51f3Slg goto Fail; 8654ffcd51f3Slg } 8655ffcd51f3Slg 8656ffcd51f3Slg hubd->h_hotplug_thread++; 8657ffcd51f3Slg 8658ffcd51f3Slg /* is this the root hub? */ 8659ffcd51f3Slg if ((hdip == rh_dip) && 8660ffcd51f3Slg (hubd->h_dev_state == USB_DEV_PWRED_DOWN)) { 8661ffcd51f3Slg hubpm = hubd->h_hubpm; 8662ffcd51f3Slg 8663ffcd51f3Slg /* mark the root hub as full power */ 8664ffcd51f3Slg hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR; 8665ffcd51f3Slg hubpm->hubp_time_at_full_power = ddi_get_time(); 8666ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 8667ffcd51f3Slg 8668ffcd51f3Slg USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 8669ffcd51f3Slg "hubd_reset_thread: call pm_power_has_changed"); 8670ffcd51f3Slg 8671ffcd51f3Slg (void) pm_power_has_changed(hdip, 0, 8672ffcd51f3Slg USB_DEV_OS_FULL_PWR); 8673ffcd51f3Slg 8674ffcd51f3Slg mutex_enter(HUBD_MUTEX(hubd)); 8675ffcd51f3Slg hubd->h_dev_state = USB_DEV_ONLINE; 8676ffcd51f3Slg } 8677ffcd51f3Slg 8678ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 8679ffcd51f3Slg 8680ffcd51f3Slg /* 8681ffcd51f3Slg * this ensures one reset activity per system at a time. 8682ffcd51f3Slg * we enter the parent PCI node to have this serialization. 8683ffcd51f3Slg * this also excludes ioctls and deathrow thread 8684ffcd51f3Slg */ 8685ffcd51f3Slg ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ); 8686ffcd51f3Slg ndi_devi_enter(rh_dip, &rh_circ); 8687ffcd51f3Slg 8688ffcd51f3Slg /* exclude other threads */ 8689ffcd51f3Slg ndi_devi_enter(hdip, &circ); 8690ffcd51f3Slg mutex_enter(HUBD_MUTEX(hubd)); 8691ffcd51f3Slg 8692ffcd51f3Slg /* 8693ffcd51f3Slg * We need to make sure that the child is still online for a hotplug 8694ffcd51f3Slg * thread could have inserted which detached the child. 8695ffcd51f3Slg */ 8696ffcd51f3Slg if (hubd->h_children_dips[reset_port]) { 8697ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 8698ffcd51f3Slg /* First disconnect the device */ 8699ffcd51f3Slg hubd_post_event(hubd, reset_port, USBA_EVENT_TAG_HOT_REMOVAL); 8700*aa041649SRaymond Chen 8701*aa041649SRaymond Chen /* delete cached dv_node's but drop locks first */ 8702*aa041649SRaymond Chen ndi_devi_exit(hdip, circ); 8703*aa041649SRaymond Chen ndi_devi_exit(rh_dip, rh_circ); 8704*aa041649SRaymond Chen ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ); 8705*aa041649SRaymond Chen 8706*aa041649SRaymond Chen (void) devfs_clean(rh_dip, NULL, DV_CLEAN_FORCE); 8707*aa041649SRaymond Chen 8708*aa041649SRaymond Chen /* 8709*aa041649SRaymond Chen * workaround only for storage device. When it's able to force 8710*aa041649SRaymond Chen * detach a driver, this code can be removed safely. 8711*aa041649SRaymond Chen * 8712*aa041649SRaymond Chen * If we're to reset storage device and the device is used, we 8713*aa041649SRaymond Chen * will wait at most extra 20s for applications to exit and 8714*aa041649SRaymond Chen * close the device. This is especially useful for HAL-based 8715*aa041649SRaymond Chen * applications. 8716*aa041649SRaymond Chen */ 8717*aa041649SRaymond Chen if ((strcmp(devname, "scsa2usb") == 0) && 8718*aa041649SRaymond Chen DEVI(child_dip)->devi_ref != 0) { 8719*aa041649SRaymond Chen while (i++ < hubdi_reset_delay) { 8720*aa041649SRaymond Chen mutex_enter(HUBD_MUTEX(hubd)); 8721*aa041649SRaymond Chen rval = hubd_delete_child(hubd, reset_port, 8722*aa041649SRaymond Chen NDI_DEVI_REMOVE, B_FALSE); 8723*aa041649SRaymond Chen mutex_exit(HUBD_MUTEX(hubd)); 8724*aa041649SRaymond Chen if (rval == USB_SUCCESS) 8725*aa041649SRaymond Chen break; 8726*aa041649SRaymond Chen 8727*aa041649SRaymond Chen delay(drv_usectohz(1000000)); /* 1s */ 8728*aa041649SRaymond Chen } 8729*aa041649SRaymond Chen } 8730*aa041649SRaymond Chen 8731*aa041649SRaymond Chen ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ); 8732*aa041649SRaymond Chen ndi_devi_enter(rh_dip, &rh_circ); 8733*aa041649SRaymond Chen ndi_devi_enter(hdip, &circ); 8734*aa041649SRaymond Chen 8735ffcd51f3Slg mutex_enter(HUBD_MUTEX(hubd)); 8736ffcd51f3Slg 8737ffcd51f3Slg /* Then force detaching the device */ 8738*aa041649SRaymond Chen if ((rval != USB_SUCCESS) && (hubd_delete_child(hubd, 8739*aa041649SRaymond Chen reset_port, NDI_DEVI_REMOVE, B_FALSE) != USB_SUCCESS)) { 8740ffcd51f3Slg USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 8741ffcd51f3Slg "%s%d cannot be reset due to other applications " 8742ffcd51f3Slg "are using it, please first close these " 8743ffcd51f3Slg "applications, then disconnect and reconnect" 8744ffcd51f3Slg "the device.", devname, devinst); 8745ffcd51f3Slg 8746ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 8747ffcd51f3Slg /* post a re-connect event */ 8748ffcd51f3Slg hubd_post_event(hubd, reset_port, 8749ffcd51f3Slg USBA_EVENT_TAG_HOT_INSERTION); 8750ffcd51f3Slg mutex_enter(HUBD_MUTEX(hubd)); 8751ffcd51f3Slg } else { 8752ffcd51f3Slg (void) hubd_determine_port_status(hubd, reset_port, 8753ffcd51f3Slg &status, &change, HUBD_ACK_ALL_CHANGES); 8754ffcd51f3Slg 8755ffcd51f3Slg /* Reset the parent hubd port and create new child */ 8756ffcd51f3Slg if (status & PORT_STATUS_CCS) { 8757ffcd51f3Slg online_child |= (hubd_handle_port_connect(hubd, 8758ffcd51f3Slg reset_port) == USB_SUCCESS); 8759ffcd51f3Slg } 8760ffcd51f3Slg } 8761ffcd51f3Slg } 8762ffcd51f3Slg 8763ffcd51f3Slg /* release locks so we can do a devfs_clean */ 8764ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 8765ffcd51f3Slg 8766ffcd51f3Slg /* delete cached dv_node's but drop locks first */ 8767ffcd51f3Slg ndi_devi_exit(hdip, circ); 8768ffcd51f3Slg ndi_devi_exit(rh_dip, rh_circ); 8769ffcd51f3Slg ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ); 8770ffcd51f3Slg 8771ffcd51f3Slg (void) devfs_clean(rh_dip, NULL, 0); 8772ffcd51f3Slg 8773ffcd51f3Slg /* now check if any children need onlining */ 8774ffcd51f3Slg if (online_child) { 8775ffcd51f3Slg USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 8776ffcd51f3Slg "hubd_reset_thread: onlining children"); 8777ffcd51f3Slg 8778ffcd51f3Slg (void) ndi_devi_online(hubd->h_dip, 0); 8779ffcd51f3Slg } 8780ffcd51f3Slg 8781ffcd51f3Slg mutex_enter(HUBD_MUTEX(hubd)); 8782ffcd51f3Slg 8783ffcd51f3Slg /* allow hotplug thread now */ 8784ffcd51f3Slg hubd->h_hotplug_thread--; 8785ffcd51f3Slg Fail: 8786ffcd51f3Slg hubd_start_polling(hubd, 0); 8787ffcd51f3Slg 8788ffcd51f3Slg /* mark this device as idle */ 8789ffcd51f3Slg (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0); 8790ffcd51f3Slg 8791ffcd51f3Slg USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, 8792ffcd51f3Slg "hubd_reset_thread: exit, %d", hubd->h_hotplug_thread); 8793ffcd51f3Slg 8794ffcd51f3Slg hubd->h_reset_port[reset_port] = B_FALSE; 8795ffcd51f3Slg 8796ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 8797ffcd51f3Slg 8798ffcd51f3Slg ndi_rele_devi(hdip); 8799ffcd51f3Slg } 8800ffcd51f3Slg 8801ffcd51f3Slg /* 8802ffcd51f3Slg * hubd_check_same_device: 8803112cd14aSqz * - open the default pipe of the device. 8804112cd14aSqz * - compare the old and new descriptors of the device. 8805112cd14aSqz * - close the default pipe. 8806ffcd51f3Slg */ 8807ffcd51f3Slg static int 8808ffcd51f3Slg hubd_check_same_device(hubd_t *hubd, usb_port_t port) 8809ffcd51f3Slg { 8810112cd14aSqz dev_info_t *dip = hubd->h_children_dips[port]; 8811ffcd51f3Slg usb_pipe_handle_t ph; 8812112cd14aSqz int rval = USB_FAILURE; 8813ffcd51f3Slg 8814ffcd51f3Slg ASSERT(mutex_owned(HUBD_MUTEX(hubd))); 8815ffcd51f3Slg 8816ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 8817ffcd51f3Slg /* Open the default pipe to operate the device */ 8818ffcd51f3Slg if (usb_pipe_open(dip, NULL, NULL, 8819ffcd51f3Slg USB_FLAGS_SLEEP| USBA_FLAGS_PRIVILEGED, 8820ffcd51f3Slg &ph) == USB_SUCCESS) { 8821ffcd51f3Slg /* 8822ffcd51f3Slg * Check that if the device's descriptors are different 8823ffcd51f3Slg * from the values saved before the port reset. 8824ffcd51f3Slg */ 8825ffcd51f3Slg rval = usb_check_same_device(dip, 8826ffcd51f3Slg hubd->h_log_handle, USB_LOG_L0, 8827ffcd51f3Slg DPRINT_MASK_ALL, USB_CHK_ALL, NULL); 8828ffcd51f3Slg 8829ffcd51f3Slg usb_pipe_close(dip, ph, USB_FLAGS_SLEEP | 8830ffcd51f3Slg USBA_FLAGS_PRIVILEGED, NULL, NULL); 8831ffcd51f3Slg } 8832ffcd51f3Slg mutex_enter(HUBD_MUTEX(hubd)); 8833ffcd51f3Slg 8834ffcd51f3Slg return (rval); 8835ffcd51f3Slg } 8836ffcd51f3Slg 8837ffcd51f3Slg /* 8838ffcd51f3Slg * usba_hubdi_reset_device 8839112cd14aSqz * Called by usb_reset_device to handle usb device reset. 8840ffcd51f3Slg */ 8841ffcd51f3Slg int 8842ffcd51f3Slg usba_hubdi_reset_device(dev_info_t *dip, usb_dev_reset_lvl_t reset_level) 8843ffcd51f3Slg { 8844ffcd51f3Slg hubd_t *hubd; 8845ffcd51f3Slg usb_port_t port = 0; 8846ffcd51f3Slg dev_info_t *hdip; 8847ffcd51f3Slg usb_pipe_state_t prev_pipe_state = 0; 8848ffcd51f3Slg usba_device_t *usba_device; 8849ffcd51f3Slg hubd_reset_arg_t *arg; 8850ffcd51f3Slg int i, ph_open_cnt; 8851ffcd51f3Slg int rval = USB_FAILURE; 8852ffcd51f3Slg 8853ffcd51f3Slg if ((!dip) || usba_is_root_hub(dip)) { 8854*aa041649SRaymond Chen USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle, 8855*aa041649SRaymond Chen "usba_hubdi_reset_device: NULL dip or root hub"); 8856ffcd51f3Slg 8857ffcd51f3Slg return (USB_INVALID_ARGS); 8858ffcd51f3Slg } 8859ffcd51f3Slg 8860ffcd51f3Slg if (!usb_owns_device(dip)) { 8861*aa041649SRaymond Chen USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle, 8862*aa041649SRaymond Chen "usba_hubdi_reset_device: Not owns the device"); 8863ffcd51f3Slg 8864ffcd51f3Slg return (USB_INVALID_PERM); 8865ffcd51f3Slg } 8866ffcd51f3Slg 8867ffcd51f3Slg if ((reset_level != USB_RESET_LVL_REATTACH) && 8868ffcd51f3Slg (reset_level != USB_RESET_LVL_DEFAULT)) { 8869*aa041649SRaymond Chen USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle, 8870*aa041649SRaymond Chen "usba_hubdi_reset_device: Unknown flags"); 8871ffcd51f3Slg 8872ffcd51f3Slg return (USB_INVALID_ARGS); 8873ffcd51f3Slg } 8874ffcd51f3Slg 8875ffcd51f3Slg if ((hdip = ddi_get_parent(dip)) == NULL) { 8876*aa041649SRaymond Chen USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle, 8877*aa041649SRaymond Chen "usba_hubdi_reset_device: fail to get parent hub"); 8878ffcd51f3Slg 8879ffcd51f3Slg return (USB_INVALID_ARGS); 8880ffcd51f3Slg } 8881ffcd51f3Slg 8882ffcd51f3Slg if ((hubd = hubd_get_soft_state(hdip)) == NULL) { 8883*aa041649SRaymond Chen USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle, 8884*aa041649SRaymond Chen "usba_hubdi_reset_device: fail to get hub softstate"); 8885ffcd51f3Slg 8886ffcd51f3Slg return (USB_INVALID_ARGS); 8887ffcd51f3Slg } 8888ffcd51f3Slg 8889ffcd51f3Slg mutex_enter(HUBD_MUTEX(hubd)); 8890ffcd51f3Slg 8891ffcd51f3Slg /* make sure the hub is connected before trying any kinds of reset. */ 8892ffcd51f3Slg if ((hubd->h_dev_state == USB_DEV_DISCONNECTED) || 8893ffcd51f3Slg (hubd->h_dev_state == USB_DEV_SUSPENDED)) { 8894ffcd51f3Slg USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 8895ffcd51f3Slg "usb_reset_device: the state %d of the hub/roothub " 8896112116d8Sfb "associated to the device 0x%p is incorrect", 8897112116d8Sfb hubd->h_dev_state, (void *)dip); 8898ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 8899ffcd51f3Slg 8900ffcd51f3Slg return (USB_INVALID_ARGS); 8901ffcd51f3Slg } 8902ffcd51f3Slg 8903ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 8904ffcd51f3Slg 8905ffcd51f3Slg port = hubd_child_dip2port(hubd, dip); 8906ffcd51f3Slg 8907ffcd51f3Slg mutex_enter(HUBD_MUTEX(hubd)); 8908ffcd51f3Slg 8909ffcd51f3Slg if (hubd->h_reset_port[port]) { 8910ffcd51f3Slg USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 8911ffcd51f3Slg "usb_reset_device: the corresponding port is resetting"); 8912ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 8913ffcd51f3Slg 8914ffcd51f3Slg return (USB_SUCCESS); 8915ffcd51f3Slg } 8916ffcd51f3Slg 8917ffcd51f3Slg /* 8918ffcd51f3Slg * For Default reset, client drivers should first close all the pipes 8919ffcd51f3Slg * except default pipe before calling the function, also should not 8920ffcd51f3Slg * call the function during interrupt context. 8921ffcd51f3Slg */ 8922ffcd51f3Slg if (reset_level == USB_RESET_LVL_DEFAULT) { 8923ffcd51f3Slg usba_device = hubd->h_usba_devices[port]; 8924ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 8925ffcd51f3Slg 8926ffcd51f3Slg if (servicing_interrupt()) { 8927ffcd51f3Slg USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 8928ffcd51f3Slg "usb_reset_device: during interrput context, quit"); 8929ffcd51f3Slg 8930ffcd51f3Slg return (USB_INVALID_CONTEXT); 8931ffcd51f3Slg } 8932ffcd51f3Slg /* Check if all the pipes have been closed */ 8933ffcd51f3Slg for (ph_open_cnt = 0, i = 1; i < USBA_N_ENDPOINTS; i++) { 8934ffcd51f3Slg if (usba_device->usb_ph_list[i].usba_ph_data) { 8935ffcd51f3Slg ph_open_cnt++; 8936ffcd51f3Slg break; 8937ffcd51f3Slg } 8938ffcd51f3Slg } 8939ffcd51f3Slg if (ph_open_cnt) { 8940ffcd51f3Slg USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 8941ffcd51f3Slg "usb_reset_device: %d pipes are still open", 8942ffcd51f3Slg ph_open_cnt); 8943ffcd51f3Slg 8944ffcd51f3Slg return (USB_BUSY); 8945ffcd51f3Slg } 8946ffcd51f3Slg mutex_enter(HUBD_MUTEX(hubd)); 8947ffcd51f3Slg } 8948ffcd51f3Slg 8949ffcd51f3Slg /* Don't perform reset while the device is detaching */ 8950ffcd51f3Slg if (hubd->h_port_state[port] & HUBD_CHILD_DETACHING) { 8951ffcd51f3Slg USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 8952ffcd51f3Slg "usb_reset_device: the device is detaching, " 8953ffcd51f3Slg "cannot be reset"); 8954ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 8955ffcd51f3Slg 8956ffcd51f3Slg return (USB_FAILURE); 8957ffcd51f3Slg } 8958ffcd51f3Slg 8959ffcd51f3Slg hubd->h_reset_port[port] = B_TRUE; 8960ffcd51f3Slg hdip = hubd->h_dip; 8961ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 8962ffcd51f3Slg 8963ffcd51f3Slg /* Don't allow hub detached during the reset */ 8964ffcd51f3Slg ndi_hold_devi(hdip); 8965ffcd51f3Slg 8966ffcd51f3Slg mutex_enter(HUBD_MUTEX(hubd)); 8967ffcd51f3Slg hubd_pm_busy_component(hubd, hdip, 0); 8968ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 8969ffcd51f3Slg /* go full power */ 8970ffcd51f3Slg (void) pm_raise_power(hdip, 0, USB_DEV_OS_FULL_PWR); 8971ffcd51f3Slg mutex_enter(HUBD_MUTEX(hubd)); 8972ffcd51f3Slg 8973ffcd51f3Slg hubd->h_hotplug_thread++; 8974ffcd51f3Slg 8975ffcd51f3Slg /* stop polling if it was active */ 8976ffcd51f3Slg if (hubd->h_ep1_ph) { 8977ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 8978ffcd51f3Slg (void) usb_pipe_get_state(hubd->h_ep1_ph, &prev_pipe_state, 8979ffcd51f3Slg USB_FLAGS_SLEEP); 8980ffcd51f3Slg mutex_enter(HUBD_MUTEX(hubd)); 8981ffcd51f3Slg 8982ffcd51f3Slg if (prev_pipe_state == USB_PIPE_STATE_ACTIVE) { 8983ffcd51f3Slg hubd_stop_polling(hubd); 8984ffcd51f3Slg } 8985ffcd51f3Slg } 8986ffcd51f3Slg 8987ffcd51f3Slg switch (reset_level) { 8988ffcd51f3Slg case USB_RESET_LVL_REATTACH: 8989ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 8990ffcd51f3Slg arg = (hubd_reset_arg_t *)kmem_zalloc( 8991ffcd51f3Slg sizeof (hubd_reset_arg_t), KM_SLEEP); 8992ffcd51f3Slg arg->hubd = hubd; 8993ffcd51f3Slg arg->reset_port = port; 8994ffcd51f3Slg mutex_enter(HUBD_MUTEX(hubd)); 8995ffcd51f3Slg 8996ffcd51f3Slg if ((rval = usb_async_req(hdip, hubd_reset_thread, 8997ffcd51f3Slg (void *)arg, 0)) == USB_SUCCESS) { 8998ffcd51f3Slg hubd->h_hotplug_thread--; 8999ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 9000ffcd51f3Slg 9001ffcd51f3Slg return (USB_SUCCESS); 9002ffcd51f3Slg } else { 9003ffcd51f3Slg USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, 9004ffcd51f3Slg "Cannot create reset thread, the device %s%d failed" 9005ffcd51f3Slg " to reset", ddi_driver_name(dip), 9006ffcd51f3Slg ddi_get_instance(dip)); 9007ffcd51f3Slg 9008ffcd51f3Slg kmem_free(arg, sizeof (hubd_reset_arg_t)); 9009ffcd51f3Slg } 9010ffcd51f3Slg 9011ffcd51f3Slg break; 9012ffcd51f3Slg case USB_RESET_LVL_DEFAULT: 9013ffcd51f3Slg /* 9014ffcd51f3Slg * Reset hub port and then recover device's address, set back 9015ffcd51f3Slg * device's configuration, hubd_handle_port_connect() will 9016ffcd51f3Slg * handle errors happened during this process. 9017ffcd51f3Slg */ 9018ffcd51f3Slg if ((rval = hubd_handle_port_connect(hubd, port)) 9019ffcd51f3Slg == USB_SUCCESS) { 9020ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 9021ffcd51f3Slg /* re-open the default pipe */ 9022ffcd51f3Slg rval = usba_persistent_pipe_open(usba_device); 9023ffcd51f3Slg mutex_enter(HUBD_MUTEX(hubd)); 9024ffcd51f3Slg if (rval != USB_SUCCESS) { 9025ffcd51f3Slg USB_DPRINTF_L2(DPRINT_MASK_ATTA, 9026ffcd51f3Slg hubd->h_log_handle, "failed to reopen " 9027ffcd51f3Slg "default pipe after reset, disable hub" 9028ffcd51f3Slg "port for %s%d", ddi_driver_name(dip), 9029ffcd51f3Slg ddi_get_instance(dip)); 9030ffcd51f3Slg /* 9031ffcd51f3Slg * Disable port to set out a hotplug thread 9032ffcd51f3Slg * which will handle errors. 9033ffcd51f3Slg */ 9034ffcd51f3Slg (void) hubd_disable_port(hubd, port); 9035ffcd51f3Slg } 9036ffcd51f3Slg } 9037ffcd51f3Slg 9038ffcd51f3Slg break; 9039ffcd51f3Slg default: 9040ffcd51f3Slg 9041ffcd51f3Slg break; 9042ffcd51f3Slg } 9043ffcd51f3Slg 9044ffcd51f3Slg /* allow hotplug thread now */ 9045ffcd51f3Slg hubd->h_hotplug_thread--; 9046ffcd51f3Slg 9047ffcd51f3Slg if ((hubd->h_dev_state == USB_DEV_ONLINE) && hubd->h_ep1_ph && 9048ffcd51f3Slg (prev_pipe_state == USB_PIPE_STATE_ACTIVE)) { 9049ffcd51f3Slg hubd_start_polling(hubd, 0); 9050ffcd51f3Slg } 9051ffcd51f3Slg 9052ffcd51f3Slg hubd_pm_idle_component(hubd, hdip, 0); 9053ffcd51f3Slg 9054ffcd51f3Slg /* Clear reset mark for the port. */ 9055ffcd51f3Slg hubd->h_reset_port[port] = B_FALSE; 9056ffcd51f3Slg 9057ffcd51f3Slg mutex_exit(HUBD_MUTEX(hubd)); 9058ffcd51f3Slg 9059ffcd51f3Slg ndi_rele_devi(hdip); 9060ffcd51f3Slg 9061ffcd51f3Slg return (rval); 9062ffcd51f3Slg } 9063