xref: /illumos-gate/usr/src/uts/common/io/usb/usba/hubdi.c (revision d291d9f2)
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
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
237c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate /*
317c478bd9Sstevel@tonic-gate  * USBA: Solaris USB Architecture support for the hub
327c478bd9Sstevel@tonic-gate  * including root hub
337c478bd9Sstevel@tonic-gate  * Most of the code for hubd resides in this file and
347c478bd9Sstevel@tonic-gate  * is shared between the HCD root hub support and hubd
357c478bd9Sstevel@tonic-gate  */
367c478bd9Sstevel@tonic-gate #define	USBA_FRAMEWORK
377c478bd9Sstevel@tonic-gate #include <sys/usb/usba.h>
387c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usba_devdb.h>
397c478bd9Sstevel@tonic-gate #include <sys/sunndi.h>
407c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usba_impl.h>
417c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usba_types.h>
427c478bd9Sstevel@tonic-gate #include <sys/usb/usba/hubdi.h>
437c478bd9Sstevel@tonic-gate #include <sys/usb/usba/hcdi_impl.h>
447c478bd9Sstevel@tonic-gate #include <sys/usb/hubd/hub.h>
457c478bd9Sstevel@tonic-gate #include <sys/usb/hubd/hubdvar.h>
467c478bd9Sstevel@tonic-gate #include <sys/usb/hubd/hubd_impl.h>
477c478bd9Sstevel@tonic-gate #include <sys/kobj.h>
487c478bd9Sstevel@tonic-gate #include <sys/kobj_lex.h>
497c478bd9Sstevel@tonic-gate #include <sys/fs/dv_node.h>
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate /*
527c478bd9Sstevel@tonic-gate  * Prototypes for static functions
537c478bd9Sstevel@tonic-gate  */
547c478bd9Sstevel@tonic-gate static	int	usba_hubdi_bus_ctl(
557c478bd9Sstevel@tonic-gate 			dev_info_t		*dip,
567c478bd9Sstevel@tonic-gate 			dev_info_t		*rdip,
577c478bd9Sstevel@tonic-gate 			ddi_ctl_enum_t		op,
587c478bd9Sstevel@tonic-gate 			void			*arg,
597c478bd9Sstevel@tonic-gate 			void			*result);
607c478bd9Sstevel@tonic-gate 
617c478bd9Sstevel@tonic-gate static int	usba_hubdi_map_fault(
627c478bd9Sstevel@tonic-gate 			dev_info_t		*dip,
637c478bd9Sstevel@tonic-gate 			dev_info_t		*rdip,
647c478bd9Sstevel@tonic-gate 			struct hat		*hat,
657c478bd9Sstevel@tonic-gate 			struct seg		*seg,
667c478bd9Sstevel@tonic-gate 			caddr_t 		addr,
677c478bd9Sstevel@tonic-gate 			struct devpage		*dp,
687c478bd9Sstevel@tonic-gate 			pfn_t			pfn,
697c478bd9Sstevel@tonic-gate 			uint_t			prot,
707c478bd9Sstevel@tonic-gate 			uint_t			lock);
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate static int hubd_busop_get_eventcookie(dev_info_t *dip,
737c478bd9Sstevel@tonic-gate 			dev_info_t *rdip,
747c478bd9Sstevel@tonic-gate 			char *eventname,
757c478bd9Sstevel@tonic-gate 			ddi_eventcookie_t *cookie);
767c478bd9Sstevel@tonic-gate static int hubd_busop_add_eventcall(dev_info_t *dip,
777c478bd9Sstevel@tonic-gate 			dev_info_t *rdip,
787c478bd9Sstevel@tonic-gate 			ddi_eventcookie_t cookie,
797c478bd9Sstevel@tonic-gate 			void (*callback)(dev_info_t *dip,
807c478bd9Sstevel@tonic-gate 				ddi_eventcookie_t cookie, void *arg,
817c478bd9Sstevel@tonic-gate 				void *bus_impldata),
827c478bd9Sstevel@tonic-gate 			void *arg, ddi_callback_id_t *cb_id);
837c478bd9Sstevel@tonic-gate static int hubd_busop_remove_eventcall(dev_info_t *dip,
847c478bd9Sstevel@tonic-gate 			ddi_callback_id_t cb_id);
857c478bd9Sstevel@tonic-gate static int hubd_bus_config(dev_info_t *dip,
867c478bd9Sstevel@tonic-gate 			uint_t flag,
877c478bd9Sstevel@tonic-gate 			ddi_bus_config_op_t op,
887c478bd9Sstevel@tonic-gate 			void *arg,
897c478bd9Sstevel@tonic-gate 			dev_info_t **child);
907c478bd9Sstevel@tonic-gate static int hubd_bus_unconfig(dev_info_t *dip,
917c478bd9Sstevel@tonic-gate 			uint_t flag,
927c478bd9Sstevel@tonic-gate 			ddi_bus_config_op_t op,
937c478bd9Sstevel@tonic-gate 			void *arg);
947c478bd9Sstevel@tonic-gate static int hubd_bus_power(dev_info_t *dip, void *impl_arg,
957c478bd9Sstevel@tonic-gate 			pm_bus_power_op_t op, void *arg, void *result);
967c478bd9Sstevel@tonic-gate 
977c478bd9Sstevel@tonic-gate static void hubd_get_ancestry_str(hubd_t *);
987c478bd9Sstevel@tonic-gate static usb_port_t  hubd_get_port_num(hubd_t *, struct devctl_iocdata *);
997c478bd9Sstevel@tonic-gate static dev_info_t *hubd_get_child_dip(hubd_t *, usb_port_t);
1007c478bd9Sstevel@tonic-gate static uint_t hubd_cfgadm_state(hubd_t *, usb_port_t);
1017c478bd9Sstevel@tonic-gate static int hubd_toggle_port(hubd_t *, usb_port_t);
1027c478bd9Sstevel@tonic-gate static void hubd_register_cpr_callback(hubd_t *);
1037c478bd9Sstevel@tonic-gate static void hubd_unregister_cpr_callback(hubd_t *);
1047c478bd9Sstevel@tonic-gate static hubd_t *hubd_get_soft_state(dev_info_t *dip);
1057c478bd9Sstevel@tonic-gate 
1067c478bd9Sstevel@tonic-gate /*
1077c478bd9Sstevel@tonic-gate  * Busops vector for USB HUB's
1087c478bd9Sstevel@tonic-gate  */
1097c478bd9Sstevel@tonic-gate struct bus_ops usba_hubdi_busops =	{
1107c478bd9Sstevel@tonic-gate 	BUSO_REV,
1117c478bd9Sstevel@tonic-gate 	nullbusmap,			/* bus_map */
1127c478bd9Sstevel@tonic-gate 	NULL,				/* bus_get_intrspec */
1137c478bd9Sstevel@tonic-gate 	NULL,				/* bus_add_intrspec */
1147c478bd9Sstevel@tonic-gate 	NULL,				/* bus_remove_intrspec */
1157c478bd9Sstevel@tonic-gate 	usba_hubdi_map_fault,		/* bus_map_fault */
1167c478bd9Sstevel@tonic-gate 	ddi_dma_map,			/* bus_dma_map */
1177c478bd9Sstevel@tonic-gate 	ddi_dma_allochdl,
1187c478bd9Sstevel@tonic-gate 	ddi_dma_freehdl,
1197c478bd9Sstevel@tonic-gate 	ddi_dma_bindhdl,
1207c478bd9Sstevel@tonic-gate 	ddi_dma_unbindhdl,
1217c478bd9Sstevel@tonic-gate 	ddi_dma_flush,
1227c478bd9Sstevel@tonic-gate 	ddi_dma_win,
1237c478bd9Sstevel@tonic-gate 	ddi_dma_mctl,			/* bus_dma_ctl */
1247c478bd9Sstevel@tonic-gate 	usba_hubdi_bus_ctl,		/* bus_ctl */
1257c478bd9Sstevel@tonic-gate 	ddi_bus_prop_op,		/* bus_prop_op */
1267c478bd9Sstevel@tonic-gate 	hubd_busop_get_eventcookie,
1277c478bd9Sstevel@tonic-gate 	hubd_busop_add_eventcall,
1287c478bd9Sstevel@tonic-gate 	hubd_busop_remove_eventcall,
1297c478bd9Sstevel@tonic-gate 	NULL,				/* bus_post_event */
1307c478bd9Sstevel@tonic-gate 	NULL,				/* bus_intr_ctl */
1317c478bd9Sstevel@tonic-gate 	hubd_bus_config,		/* bus_config */
1327c478bd9Sstevel@tonic-gate 	hubd_bus_unconfig,		/* bus_unconfig */
1337c478bd9Sstevel@tonic-gate 	NULL,				/* bus_fm_init */
1347c478bd9Sstevel@tonic-gate 	NULL,				/* bus_fm_fini */
1357c478bd9Sstevel@tonic-gate 	NULL,				/* bus_fm_access_enter */
1367c478bd9Sstevel@tonic-gate 	NULL,				/* bus_fm_access_exit */
1377c478bd9Sstevel@tonic-gate 	hubd_bus_power			/* bus_power */
1387c478bd9Sstevel@tonic-gate };
1397c478bd9Sstevel@tonic-gate 
1407c478bd9Sstevel@tonic-gate 
1417c478bd9Sstevel@tonic-gate /*
1427c478bd9Sstevel@tonic-gate  * local variables
1437c478bd9Sstevel@tonic-gate  */
1447c478bd9Sstevel@tonic-gate static kmutex_t	usba_hubdi_mutex;	/* protects USBA HUB data structures */
1457c478bd9Sstevel@tonic-gate 
1467c478bd9Sstevel@tonic-gate static usba_list_entry_t	usba_hubdi_list;
1477c478bd9Sstevel@tonic-gate 
1487c478bd9Sstevel@tonic-gate usb_log_handle_t	hubdi_log_handle;
1497c478bd9Sstevel@tonic-gate uint_t			hubdi_errlevel = USB_LOG_L4;
1507c478bd9Sstevel@tonic-gate uint_t			hubdi_errmask = (uint_t)-1;
1517c478bd9Sstevel@tonic-gate uint_t			hubdi_min_pm_threshold = 5; /* seconds */
1527c478bd9Sstevel@tonic-gate 
1537c478bd9Sstevel@tonic-gate /*
1547c478bd9Sstevel@tonic-gate  * initialize private data
1557c478bd9Sstevel@tonic-gate  */
1567c478bd9Sstevel@tonic-gate void
1577c478bd9Sstevel@tonic-gate usba_hubdi_initialization()
1587c478bd9Sstevel@tonic-gate {
1597c478bd9Sstevel@tonic-gate 	hubdi_log_handle = usb_alloc_log_hdl(NULL, "hubdi", &hubdi_errlevel,
1607c478bd9Sstevel@tonic-gate 				&hubdi_errmask, NULL, 0);
1617c478bd9Sstevel@tonic-gate 
1627c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle,
1637c478bd9Sstevel@tonic-gate 	    "usba_hubdi_initialization");
1647c478bd9Sstevel@tonic-gate 
1657c478bd9Sstevel@tonic-gate 	mutex_init(&usba_hubdi_mutex, NULL, MUTEX_DRIVER, NULL);
1667c478bd9Sstevel@tonic-gate 
1677c478bd9Sstevel@tonic-gate 	usba_init_list(&usba_hubdi_list, NULL, NULL);
1687c478bd9Sstevel@tonic-gate }
1697c478bd9Sstevel@tonic-gate 
1707c478bd9Sstevel@tonic-gate 
1717c478bd9Sstevel@tonic-gate void
1727c478bd9Sstevel@tonic-gate usba_hubdi_destroy()
1737c478bd9Sstevel@tonic-gate {
1747c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle,
1757c478bd9Sstevel@tonic-gate 	    "usba_hubdi_destroy");
1767c478bd9Sstevel@tonic-gate 
1777c478bd9Sstevel@tonic-gate 	mutex_destroy(&usba_hubdi_mutex);
1787c478bd9Sstevel@tonic-gate 	usba_destroy_list(&usba_hubdi_list);
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate 	usb_free_log_hdl(hubdi_log_handle);
1817c478bd9Sstevel@tonic-gate }
1827c478bd9Sstevel@tonic-gate 
1837c478bd9Sstevel@tonic-gate 
1847c478bd9Sstevel@tonic-gate /*
1857c478bd9Sstevel@tonic-gate  * Called by an	HUB to attach an instance of the driver
1867c478bd9Sstevel@tonic-gate  *	make this instance known to USBA
1877c478bd9Sstevel@tonic-gate  *	the HUB	should initialize usba_hubdi structure prior
1887c478bd9Sstevel@tonic-gate  *	to calling this	interface
1897c478bd9Sstevel@tonic-gate  */
1907c478bd9Sstevel@tonic-gate static int
1917c478bd9Sstevel@tonic-gate usba_hubdi_register(dev_info_t	*dip,
1927c478bd9Sstevel@tonic-gate 		uint_t		flags)
1937c478bd9Sstevel@tonic-gate {
1947c478bd9Sstevel@tonic-gate 	usba_hubdi_t *hubdi = kmem_zalloc(sizeof (usba_hubdi_t), KM_SLEEP);
1957c478bd9Sstevel@tonic-gate 	usba_device_t *usba_device = usba_get_usba_device(dip);
1967c478bd9Sstevel@tonic-gate 
1977c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle,
1987c478bd9Sstevel@tonic-gate 	    "usba_hubdi_register: %s", ddi_node_name(dip));
1997c478bd9Sstevel@tonic-gate 
2007c478bd9Sstevel@tonic-gate 	hubdi->hubdi_dip = dip;
2017c478bd9Sstevel@tonic-gate 	hubdi->hubdi_flags = flags;
2027c478bd9Sstevel@tonic-gate 
2037c478bd9Sstevel@tonic-gate 	usba_device->usb_hubdi = hubdi;
2047c478bd9Sstevel@tonic-gate 
2057c478bd9Sstevel@tonic-gate 	/*
2067c478bd9Sstevel@tonic-gate 	 * add this hubdi instance to the list of known hubdi's
2077c478bd9Sstevel@tonic-gate 	 */
2087c478bd9Sstevel@tonic-gate 	usba_init_list(&hubdi->hubdi_list, (usb_opaque_t)hubdi,
2097c478bd9Sstevel@tonic-gate 	    usba_hcdi_get_hcdi(usba_device->usb_root_hub_dip)->
2107c478bd9Sstevel@tonic-gate 	    hcdi_iblock_cookie);
2117c478bd9Sstevel@tonic-gate 	mutex_enter(&usba_hubdi_mutex);
2127c478bd9Sstevel@tonic-gate 	usba_add_to_list(&usba_hubdi_list, &hubdi->hubdi_list);
2137c478bd9Sstevel@tonic-gate 	mutex_exit(&usba_hubdi_mutex);
2147c478bd9Sstevel@tonic-gate 
2157c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
2167c478bd9Sstevel@tonic-gate }
2177c478bd9Sstevel@tonic-gate 
2187c478bd9Sstevel@tonic-gate 
2197c478bd9Sstevel@tonic-gate /*
2207c478bd9Sstevel@tonic-gate  * Called by an	HUB to detach an instance of the driver
2217c478bd9Sstevel@tonic-gate  */
2227c478bd9Sstevel@tonic-gate static int
2237c478bd9Sstevel@tonic-gate usba_hubdi_unregister(dev_info_t *dip)
2247c478bd9Sstevel@tonic-gate {
2257c478bd9Sstevel@tonic-gate 	usba_device_t *usba_device = usba_get_usba_device(dip);
2267c478bd9Sstevel@tonic-gate 	usba_hubdi_t *hubdi = usba_device->usb_hubdi;
2277c478bd9Sstevel@tonic-gate 
2287c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle,
2297c478bd9Sstevel@tonic-gate 	    "usba_hubdi_unregister: %s", ddi_node_name(dip));
2307c478bd9Sstevel@tonic-gate 
2317c478bd9Sstevel@tonic-gate 	mutex_enter(&usba_hubdi_mutex);
2327c478bd9Sstevel@tonic-gate 	(void) usba_rm_from_list(&usba_hubdi_list, &hubdi->hubdi_list);
2337c478bd9Sstevel@tonic-gate 	mutex_exit(&usba_hubdi_mutex);
2347c478bd9Sstevel@tonic-gate 
2357c478bd9Sstevel@tonic-gate 	usba_destroy_list(&hubdi->hubdi_list);
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate 	kmem_free(hubdi, sizeof (usba_hubdi_t));
2387c478bd9Sstevel@tonic-gate 
2397c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
2407c478bd9Sstevel@tonic-gate }
2417c478bd9Sstevel@tonic-gate 
2427c478bd9Sstevel@tonic-gate 
2437c478bd9Sstevel@tonic-gate /*
2447c478bd9Sstevel@tonic-gate  * misc bus routines currently not used
2457c478bd9Sstevel@tonic-gate  */
2467c478bd9Sstevel@tonic-gate /*ARGSUSED*/
2477c478bd9Sstevel@tonic-gate static int
2487c478bd9Sstevel@tonic-gate usba_hubdi_map_fault(dev_info_t *dip,
2497c478bd9Sstevel@tonic-gate 	dev_info_t	*rdip,
2507c478bd9Sstevel@tonic-gate 	struct hat	*hat,
2517c478bd9Sstevel@tonic-gate 	struct seg	*seg,
2527c478bd9Sstevel@tonic-gate 	caddr_t 	addr,
2537c478bd9Sstevel@tonic-gate 	struct devpage	*dp,
2547c478bd9Sstevel@tonic-gate 	pfn_t		pfn,
2557c478bd9Sstevel@tonic-gate 	uint_t		prot,
2567c478bd9Sstevel@tonic-gate 	uint_t		lock)
2577c478bd9Sstevel@tonic-gate {
2587c478bd9Sstevel@tonic-gate 	return (DDI_FAILURE);
2597c478bd9Sstevel@tonic-gate }
2607c478bd9Sstevel@tonic-gate 
2617c478bd9Sstevel@tonic-gate 
2627c478bd9Sstevel@tonic-gate /*
2637c478bd9Sstevel@tonic-gate  * root hub support. the root hub uses the same devi as the HCD
2647c478bd9Sstevel@tonic-gate  */
2657c478bd9Sstevel@tonic-gate int
2667c478bd9Sstevel@tonic-gate usba_hubdi_bind_root_hub(dev_info_t *dip,
2677c478bd9Sstevel@tonic-gate 	uchar_t	*root_hub_config_descriptor,
2687c478bd9Sstevel@tonic-gate 	size_t config_length,
2697c478bd9Sstevel@tonic-gate 	usb_dev_descr_t *root_hub_device_descriptor)
2707c478bd9Sstevel@tonic-gate {
2717c478bd9Sstevel@tonic-gate 	usba_device_t *usba_device;
2727c478bd9Sstevel@tonic-gate 	usba_hcdi_t *hcdi = usba_hcdi_get_hcdi(dip);
2737c478bd9Sstevel@tonic-gate 	hubd_t	*root_hubd;
2747c478bd9Sstevel@tonic-gate 	usb_pipe_handle_t ph = NULL;
2757c478bd9Sstevel@tonic-gate 	dev_info_t *child = ddi_get_child(dip);
2767c478bd9Sstevel@tonic-gate 
2777c478bd9Sstevel@tonic-gate 	if (ndi_prop_create_boolean(DDI_DEV_T_NONE, dip,
2787c478bd9Sstevel@tonic-gate 	    "root-hub") != NDI_SUCCESS) {
2797c478bd9Sstevel@tonic-gate 
2807c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
2817c478bd9Sstevel@tonic-gate 	}
2827c478bd9Sstevel@tonic-gate 
2837c478bd9Sstevel@tonic-gate 	root_hubd = kmem_zalloc(sizeof (hubd_t), KM_SLEEP);
2847c478bd9Sstevel@tonic-gate 
2857c478bd9Sstevel@tonic-gate 	/*
2867c478bd9Sstevel@tonic-gate 	 * create and initialize a usba_device structure
2877c478bd9Sstevel@tonic-gate 	 */
2887c478bd9Sstevel@tonic-gate 	usba_device = usba_alloc_usba_device(dip);
2897c478bd9Sstevel@tonic-gate 
2907c478bd9Sstevel@tonic-gate 	mutex_enter(&usba_device->usb_mutex);
2917c478bd9Sstevel@tonic-gate 	usba_device->usb_hcdi_ops = hcdi->hcdi_ops;
2927c478bd9Sstevel@tonic-gate 	usba_device->usb_cfg = root_hub_config_descriptor;
2937c478bd9Sstevel@tonic-gate 	usba_device->usb_cfg_length = config_length;
2947c478bd9Sstevel@tonic-gate 	usba_device->usb_dev_descr = root_hub_device_descriptor;
2957c478bd9Sstevel@tonic-gate 	usba_device->usb_port = 1;
2967c478bd9Sstevel@tonic-gate 	usba_device->usb_addr = ROOT_HUB_ADDR;
2977c478bd9Sstevel@tonic-gate 	usba_device->usb_root_hubd = root_hubd;
2987c478bd9Sstevel@tonic-gate 	usba_device->usb_cfg_array = kmem_zalloc(sizeof (uchar_t *),
2997c478bd9Sstevel@tonic-gate 								KM_SLEEP);
3007c478bd9Sstevel@tonic-gate 	usba_device->usb_cfg_array_length = sizeof (uchar_t *);
3017c478bd9Sstevel@tonic-gate 
3027c478bd9Sstevel@tonic-gate 	usba_device->usb_cfg_array_len = kmem_zalloc(sizeof (uint16_t),
3037c478bd9Sstevel@tonic-gate 								KM_SLEEP);
3047c478bd9Sstevel@tonic-gate 	usba_device->usb_cfg_array_len_length = sizeof (uint16_t);
3057c478bd9Sstevel@tonic-gate 
3067c478bd9Sstevel@tonic-gate 	usba_device->usb_cfg_array[0] = root_hub_config_descriptor;
3077c478bd9Sstevel@tonic-gate 	usba_device->usb_cfg_array_len[0] =
3087c478bd9Sstevel@tonic-gate 				sizeof (root_hub_config_descriptor);
3097c478bd9Sstevel@tonic-gate 
3107c478bd9Sstevel@tonic-gate 	usba_device->usb_cfg_str_descr = kmem_zalloc(sizeof (uchar_t *),
3117c478bd9Sstevel@tonic-gate 								KM_SLEEP);
3127c478bd9Sstevel@tonic-gate 	usba_device->usb_n_cfgs = 1;
3137c478bd9Sstevel@tonic-gate 	usba_device->usb_n_ifs = 1;
3147c478bd9Sstevel@tonic-gate 	usba_device->usb_dip = dip;
3157c478bd9Sstevel@tonic-gate 
3167c478bd9Sstevel@tonic-gate 	usba_device->usb_client_flags = kmem_zalloc(
3177c478bd9Sstevel@tonic-gate 	    usba_device->usb_n_ifs * USBA_CLIENT_FLAG_SIZE, KM_SLEEP);
3187c478bd9Sstevel@tonic-gate 
3197c478bd9Sstevel@tonic-gate 	usba_device->usb_client_attach_list = kmem_zalloc(
3207c478bd9Sstevel@tonic-gate 	    usba_device->usb_n_ifs *
3217c478bd9Sstevel@tonic-gate 	    sizeof (*usba_device->usb_client_attach_list), KM_SLEEP);
3227c478bd9Sstevel@tonic-gate 
3237c478bd9Sstevel@tonic-gate 	usba_device->usb_client_ev_cb_list = kmem_zalloc(
3247c478bd9Sstevel@tonic-gate 	    usba_device->usb_n_ifs *
3257c478bd9Sstevel@tonic-gate 	    sizeof (*usba_device->usb_client_ev_cb_list), KM_SLEEP);
3267c478bd9Sstevel@tonic-gate 
3277c478bd9Sstevel@tonic-gate 	/*
3287c478bd9Sstevel@tonic-gate 	 * The bDeviceProtocol field of root hub device specifies,
3297c478bd9Sstevel@tonic-gate 	 * whether root hub is a High or Full speed usb device.
3307c478bd9Sstevel@tonic-gate 	 */
3317c478bd9Sstevel@tonic-gate 	if (root_hub_device_descriptor->bDeviceProtocol) {
3327c478bd9Sstevel@tonic-gate 		usba_device->usb_port_status = USBA_HIGH_SPEED_DEV;
3337c478bd9Sstevel@tonic-gate 	} else {
3347c478bd9Sstevel@tonic-gate 		usba_device->usb_port_status = USBA_FULL_SPEED_DEV;
3357c478bd9Sstevel@tonic-gate 	}
3367c478bd9Sstevel@tonic-gate 
3377c478bd9Sstevel@tonic-gate 	mutex_exit(&usba_device->usb_mutex);
3387c478bd9Sstevel@tonic-gate 
3397c478bd9Sstevel@tonic-gate 	usba_set_usba_device(dip, usba_device);
3407c478bd9Sstevel@tonic-gate 
3417c478bd9Sstevel@tonic-gate 	/*
3427c478bd9Sstevel@tonic-gate 	 * For the root hub the default pipe is not yet open
3437c478bd9Sstevel@tonic-gate 	 */
3447c478bd9Sstevel@tonic-gate 	if (usb_pipe_open(dip, NULL, NULL,
3457c478bd9Sstevel@tonic-gate 	    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph) != USB_SUCCESS) {
3467c478bd9Sstevel@tonic-gate 		goto fail;
3477c478bd9Sstevel@tonic-gate 	}
3487c478bd9Sstevel@tonic-gate 
3497c478bd9Sstevel@tonic-gate 	/*
3507c478bd9Sstevel@tonic-gate 	 * kill off all OBP children, they may not be fully
3517c478bd9Sstevel@tonic-gate 	 * enumerated
3527c478bd9Sstevel@tonic-gate 	 */
3537c478bd9Sstevel@tonic-gate 	while (child) {
3547c478bd9Sstevel@tonic-gate 		dev_info_t *next = ddi_get_next_sibling(child);
3557c478bd9Sstevel@tonic-gate 		(void) ddi_remove_child(child, 0);
3567c478bd9Sstevel@tonic-gate 		child = next;
3577c478bd9Sstevel@tonic-gate 	}
3587c478bd9Sstevel@tonic-gate 
3597c478bd9Sstevel@tonic-gate 	/*
3607c478bd9Sstevel@tonic-gate 	 * "attach" the root hub driver
3617c478bd9Sstevel@tonic-gate 	 */
3627c478bd9Sstevel@tonic-gate 	if (usba_hubdi_attach(dip, DDI_ATTACH) != DDI_SUCCESS) {
3637c478bd9Sstevel@tonic-gate 		goto fail;
3647c478bd9Sstevel@tonic-gate 	}
3657c478bd9Sstevel@tonic-gate 
3667c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
3677c478bd9Sstevel@tonic-gate 
3687c478bd9Sstevel@tonic-gate fail:
3697c478bd9Sstevel@tonic-gate 	(void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "root-hub");
3707c478bd9Sstevel@tonic-gate 
3717c478bd9Sstevel@tonic-gate 	if (ph) {
3727c478bd9Sstevel@tonic-gate 		usb_pipe_close(dip, ph,
3737c478bd9Sstevel@tonic-gate 		    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
3747c478bd9Sstevel@tonic-gate 	}
3757c478bd9Sstevel@tonic-gate 
3767c478bd9Sstevel@tonic-gate 	kmem_free(usba_device->usb_cfg_array,
3777c478bd9Sstevel@tonic-gate 			usba_device->usb_cfg_array_length);
3787c478bd9Sstevel@tonic-gate 	kmem_free(usba_device->usb_cfg_array_len,
3797c478bd9Sstevel@tonic-gate 			usba_device->usb_cfg_array_len_length);
3807c478bd9Sstevel@tonic-gate 
3817c478bd9Sstevel@tonic-gate 	kmem_free(usba_device->usb_cfg_str_descr, sizeof (uchar_t *));
3827c478bd9Sstevel@tonic-gate 
3837c478bd9Sstevel@tonic-gate 	usba_free_usba_device(usba_device);
3847c478bd9Sstevel@tonic-gate 
3857c478bd9Sstevel@tonic-gate 	usba_set_usba_device(dip, NULL);
3867c478bd9Sstevel@tonic-gate 	if (root_hubd) {
3877c478bd9Sstevel@tonic-gate 		kmem_free(root_hubd, sizeof (hubd_t));
3887c478bd9Sstevel@tonic-gate 	}
3897c478bd9Sstevel@tonic-gate 
3907c478bd9Sstevel@tonic-gate 	return (USB_FAILURE);
3917c478bd9Sstevel@tonic-gate }
3927c478bd9Sstevel@tonic-gate 
3937c478bd9Sstevel@tonic-gate 
3947c478bd9Sstevel@tonic-gate int
3957c478bd9Sstevel@tonic-gate usba_hubdi_unbind_root_hub(dev_info_t *dip)
3967c478bd9Sstevel@tonic-gate {
3977c478bd9Sstevel@tonic-gate 	usba_device_t *usba_device;
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate 	/* was root hub attached? */
4007c478bd9Sstevel@tonic-gate 	if (!(usba_is_root_hub(dip))) {
4017c478bd9Sstevel@tonic-gate 
4027c478bd9Sstevel@tonic-gate 		/* return success anyway */
4037c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
4047c478bd9Sstevel@tonic-gate 	}
4057c478bd9Sstevel@tonic-gate 
4067c478bd9Sstevel@tonic-gate 	/*
4077c478bd9Sstevel@tonic-gate 	 * usba_hubdi_detach also closes the default pipe
4087c478bd9Sstevel@tonic-gate 	 * and removes properties so there is no need to
4097c478bd9Sstevel@tonic-gate 	 * do it here
4107c478bd9Sstevel@tonic-gate 	 */
4117c478bd9Sstevel@tonic-gate 	if (usba_hubdi_detach(dip, DDI_DETACH) != DDI_SUCCESS) {
4127c478bd9Sstevel@tonic-gate 
4137c478bd9Sstevel@tonic-gate 		if (DEVI_IS_ATTACHING(dip)) {
414*d291d9f2Sfrits 			USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
4157c478bd9Sstevel@tonic-gate 			    "failure to unbind root hub after attach failure");
4167c478bd9Sstevel@tonic-gate 		}
4177c478bd9Sstevel@tonic-gate 
4187c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
4197c478bd9Sstevel@tonic-gate 	}
4207c478bd9Sstevel@tonic-gate 
4217c478bd9Sstevel@tonic-gate 	usba_device = usba_get_usba_device(dip);
4227c478bd9Sstevel@tonic-gate 
4237c478bd9Sstevel@tonic-gate 	kmem_free(usba_device->usb_root_hubd, sizeof (hubd_t));
4247c478bd9Sstevel@tonic-gate 
4257c478bd9Sstevel@tonic-gate 	kmem_free(usba_device->usb_cfg_array,
4267c478bd9Sstevel@tonic-gate 			usba_device->usb_cfg_array_length);
4277c478bd9Sstevel@tonic-gate 	kmem_free(usba_device->usb_cfg_array_len,
4287c478bd9Sstevel@tonic-gate 			usba_device->usb_cfg_array_len_length);
4297c478bd9Sstevel@tonic-gate 
4307c478bd9Sstevel@tonic-gate 	kmem_free(usba_device->usb_cfg_str_descr, sizeof (uchar_t *));
4317c478bd9Sstevel@tonic-gate 
4327c478bd9Sstevel@tonic-gate 	usba_free_usba_device(usba_device);
4337c478bd9Sstevel@tonic-gate 
4347c478bd9Sstevel@tonic-gate 	(void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "root-hub");
4357c478bd9Sstevel@tonic-gate 
4367c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
4377c478bd9Sstevel@tonic-gate }
4387c478bd9Sstevel@tonic-gate 
4397c478bd9Sstevel@tonic-gate 
4407c478bd9Sstevel@tonic-gate /*
4417c478bd9Sstevel@tonic-gate  * Actual Hub Driver support code:
4427c478bd9Sstevel@tonic-gate  *	shared by root hub and non-root hubs
4437c478bd9Sstevel@tonic-gate  */
4447c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usbai_version.h>
4457c478bd9Sstevel@tonic-gate 
4467c478bd9Sstevel@tonic-gate /* Debugging support */
4477c478bd9Sstevel@tonic-gate static uint_t hubd_errlevel	= USB_LOG_L4;
4487c478bd9Sstevel@tonic-gate static uint_t hubd_errmask	= (uint_t)DPRINT_MASK_ALL;
4497c478bd9Sstevel@tonic-gate static uint_t hubd_instance_debug = (uint_t)-1;
4507c478bd9Sstevel@tonic-gate static uint_t hubdi_bus_config_debug = 0;
4517c478bd9Sstevel@tonic-gate 
4527c478bd9Sstevel@tonic-gate _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_errlevel))
4537c478bd9Sstevel@tonic-gate _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_errmask))
4547c478bd9Sstevel@tonic-gate _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_instance_debug))
4557c478bd9Sstevel@tonic-gate 
4567c478bd9Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique", msgb))
4577c478bd9Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique", dev_info))
4587c478bd9Sstevel@tonic-gate 
4597c478bd9Sstevel@tonic-gate 
4607c478bd9Sstevel@tonic-gate /*
4617c478bd9Sstevel@tonic-gate  * local variables:
4627c478bd9Sstevel@tonic-gate  *
4637c478bd9Sstevel@tonic-gate  * Amount of time to wait between resetting the port and accessing
4647c478bd9Sstevel@tonic-gate  * the device.	The value is in microseconds.
4657c478bd9Sstevel@tonic-gate  */
4667c478bd9Sstevel@tonic-gate static uint_t hubd_device_delay = 1000000;
4677c478bd9Sstevel@tonic-gate 
4687c478bd9Sstevel@tonic-gate /*
4697c478bd9Sstevel@tonic-gate  * enumeration retry
4707c478bd9Sstevel@tonic-gate  */
4717c478bd9Sstevel@tonic-gate #define	HUBD_PORT_RETRY 5
4727c478bd9Sstevel@tonic-gate static uint_t hubd_retry_enumerate = HUBD_PORT_RETRY;
4737c478bd9Sstevel@tonic-gate 
4747c478bd9Sstevel@tonic-gate /*
4757c478bd9Sstevel@tonic-gate  * Stale hotremoved device cleanup delay
4767c478bd9Sstevel@tonic-gate  */
4777c478bd9Sstevel@tonic-gate #define	HUBD_STALE_DIP_CLEANUP_DELAY	5000000
4787c478bd9Sstevel@tonic-gate static uint_t hubd_dip_cleanup_delay = HUBD_STALE_DIP_CLEANUP_DELAY;
4797c478bd9Sstevel@tonic-gate 
4807c478bd9Sstevel@tonic-gate /*
4817c478bd9Sstevel@tonic-gate  * retries for USB suspend and resume
4827c478bd9Sstevel@tonic-gate  */
4837c478bd9Sstevel@tonic-gate #define	HUBD_SUS_RES_RETRY	2
4847c478bd9Sstevel@tonic-gate 
4857c478bd9Sstevel@tonic-gate void	*hubd_statep;
4867c478bd9Sstevel@tonic-gate 
4877c478bd9Sstevel@tonic-gate /*
4887c478bd9Sstevel@tonic-gate  * prototypes
4897c478bd9Sstevel@tonic-gate  */
4907c478bd9Sstevel@tonic-gate static int hubd_cleanup(dev_info_t *dip, hubd_t  *hubd);
4917c478bd9Sstevel@tonic-gate static int hubd_check_ports(hubd_t  *hubd);
4927c478bd9Sstevel@tonic-gate 
4937c478bd9Sstevel@tonic-gate static void hubd_schedule_cleanup(dev_info_t *);
4947c478bd9Sstevel@tonic-gate 
4957c478bd9Sstevel@tonic-gate static int  hubd_open_intr_pipe(hubd_t *hubd);
4967c478bd9Sstevel@tonic-gate static void hubd_start_polling(hubd_t *hubd, int always);
4977c478bd9Sstevel@tonic-gate static void hubd_stop_polling(hubd_t *hubd);
4987c478bd9Sstevel@tonic-gate static void hubd_close_intr_pipe(hubd_t *hubd);
4997c478bd9Sstevel@tonic-gate 
5007c478bd9Sstevel@tonic-gate static void hubd_read_cb(usb_pipe_handle_t pipe, usb_intr_req_t *req);
5017c478bd9Sstevel@tonic-gate static void hubd_exception_cb(usb_pipe_handle_t pipe,
5027c478bd9Sstevel@tonic-gate 						usb_intr_req_t *req);
5037c478bd9Sstevel@tonic-gate static void hubd_hotplug_thread(void *arg);
5047c478bd9Sstevel@tonic-gate static int hubd_create_child(dev_info_t *dip,
5057c478bd9Sstevel@tonic-gate 		hubd_t		*hubd,
5067c478bd9Sstevel@tonic-gate 		usba_device_t	*usba_device,
5077c478bd9Sstevel@tonic-gate 		usb_port_status_t port_status,
5087c478bd9Sstevel@tonic-gate 		usb_port_t	port,
5097c478bd9Sstevel@tonic-gate 		int		iteration);
5107c478bd9Sstevel@tonic-gate 
5117c478bd9Sstevel@tonic-gate static int hubd_delete_child(hubd_t *hubd, usb_port_t port, uint_t flag,
5127c478bd9Sstevel@tonic-gate 	boolean_t retry);
5137c478bd9Sstevel@tonic-gate 
5147c478bd9Sstevel@tonic-gate static int hubd_get_hub_descriptor(hubd_t *hubd);
5157c478bd9Sstevel@tonic-gate 
5167c478bd9Sstevel@tonic-gate static int hubd_reset_port(hubd_t *hubd, usb_port_t port);
5177c478bd9Sstevel@tonic-gate 
5187c478bd9Sstevel@tonic-gate static int hubd_get_hub_status(hubd_t *hubd);
5197c478bd9Sstevel@tonic-gate 
5207c478bd9Sstevel@tonic-gate static int hubd_handle_port_connect(hubd_t *hubd, usb_port_t port);
5217c478bd9Sstevel@tonic-gate 
5227c478bd9Sstevel@tonic-gate static int hubd_disable_port(hubd_t *hubd, usb_port_t port);
5237c478bd9Sstevel@tonic-gate 
5247c478bd9Sstevel@tonic-gate static int hubd_enable_port(hubd_t *hubd, usb_port_t port);
5257c478bd9Sstevel@tonic-gate static int hubd_recover_disabled_port(hubd_t *hubd, usb_port_t port);
5267c478bd9Sstevel@tonic-gate 
5277c478bd9Sstevel@tonic-gate static int hubd_determine_port_status(hubd_t *hubd, usb_port_t port,
5287c478bd9Sstevel@tonic-gate 	uint16_t *status, uint16_t *change, uint_t ack_flag);
5297c478bd9Sstevel@tonic-gate 
5307c478bd9Sstevel@tonic-gate static int hubd_enable_all_port_power(hubd_t *hubd);
5317c478bd9Sstevel@tonic-gate static int hubd_disable_all_port_power(hubd_t *hubd);
5327c478bd9Sstevel@tonic-gate static int hubd_disable_port_power(hubd_t *hubd, usb_port_t port);
5337c478bd9Sstevel@tonic-gate static int hubd_enable_port_power(hubd_t *hubd, usb_port_t port);
5347c478bd9Sstevel@tonic-gate 
5357c478bd9Sstevel@tonic-gate static void hubd_free_usba_device(hubd_t *hubd, usba_device_t *usba_device);
5367c478bd9Sstevel@tonic-gate 
5377c478bd9Sstevel@tonic-gate static int hubd_can_suspend(hubd_t *hubd);
5387c478bd9Sstevel@tonic-gate static void hubd_restore_device_state(dev_info_t *dip, hubd_t *hubd);
5397c478bd9Sstevel@tonic-gate static int hubd_setdevaddr(hubd_t *hubd, usb_port_t port);
5407c478bd9Sstevel@tonic-gate static void hubd_setdevconfig(hubd_t *hubd, usb_port_t port);
5417c478bd9Sstevel@tonic-gate 
5427c478bd9Sstevel@tonic-gate static int hubd_register_events(hubd_t *hubd);
5437c478bd9Sstevel@tonic-gate static void hubd_do_callback(hubd_t *hubd, dev_info_t *dip,
5447c478bd9Sstevel@tonic-gate 	ddi_eventcookie_t cookie);
5457c478bd9Sstevel@tonic-gate static void hubd_run_callbacks(hubd_t *hubd, usba_event_t type);
5467c478bd9Sstevel@tonic-gate static void hubd_post_event(hubd_t *hubd, usb_port_t port, usba_event_t type);
5477c478bd9Sstevel@tonic-gate static void hubd_create_pm_components(dev_info_t *dip, hubd_t *hubd);
5487c478bd9Sstevel@tonic-gate 
5497c478bd9Sstevel@tonic-gate static int hubd_disconnect_event_cb(dev_info_t *dip);
5507c478bd9Sstevel@tonic-gate static int hubd_reconnect_event_cb(dev_info_t *dip);
5517c478bd9Sstevel@tonic-gate static int hubd_pre_suspend_event_cb(dev_info_t *dip);
5527c478bd9Sstevel@tonic-gate static int hubd_post_resume_event_cb(dev_info_t *dip);
5537c478bd9Sstevel@tonic-gate static int hubd_cpr_suspend(hubd_t *hubd);
5547c478bd9Sstevel@tonic-gate static void hubd_cpr_resume(dev_info_t *dip);
5557c478bd9Sstevel@tonic-gate static int hubd_restore_state_cb(dev_info_t *dip);
5567c478bd9Sstevel@tonic-gate 
5577c478bd9Sstevel@tonic-gate static ndi_event_definition_t hubd_ndi_event_defs[] = {
5587c478bd9Sstevel@tonic-gate 	{USBA_EVENT_TAG_HOT_REMOVAL, DDI_DEVI_REMOVE_EVENT, EPL_KERNEL,
5597c478bd9Sstevel@tonic-gate 						NDI_EVENT_POST_TO_ALL},
5607c478bd9Sstevel@tonic-gate 	{USBA_EVENT_TAG_HOT_INSERTION, DDI_DEVI_INSERT_EVENT, EPL_KERNEL,
5617c478bd9Sstevel@tonic-gate 						NDI_EVENT_POST_TO_ALL},
5627c478bd9Sstevel@tonic-gate 	{USBA_EVENT_TAG_POST_RESUME, USBA_POST_RESUME_EVENT, EPL_KERNEL,
5637c478bd9Sstevel@tonic-gate 						NDI_EVENT_POST_TO_ALL},
5647c478bd9Sstevel@tonic-gate 	{USBA_EVENT_TAG_PRE_SUSPEND, USBA_PRE_SUSPEND_EVENT, EPL_KERNEL,
5657c478bd9Sstevel@tonic-gate 						NDI_EVENT_POST_TO_ALL}
5667c478bd9Sstevel@tonic-gate };
5677c478bd9Sstevel@tonic-gate 
5687c478bd9Sstevel@tonic-gate #define	HUBD_N_NDI_EVENTS \
5697c478bd9Sstevel@tonic-gate 	(sizeof (hubd_ndi_event_defs) / sizeof (ndi_event_definition_t))
5707c478bd9Sstevel@tonic-gate 
5717c478bd9Sstevel@tonic-gate static ndi_event_set_t hubd_ndi_events = {
5727c478bd9Sstevel@tonic-gate 	NDI_EVENTS_REV1, HUBD_N_NDI_EVENTS, hubd_ndi_event_defs};
5737c478bd9Sstevel@tonic-gate 
5747c478bd9Sstevel@tonic-gate /* events received from parent */
5757c478bd9Sstevel@tonic-gate static usb_event_t hubd_events = {
5767c478bd9Sstevel@tonic-gate 	hubd_disconnect_event_cb,
5777c478bd9Sstevel@tonic-gate 	hubd_reconnect_event_cb,
5787c478bd9Sstevel@tonic-gate 	hubd_pre_suspend_event_cb,
5797c478bd9Sstevel@tonic-gate 	hubd_post_resume_event_cb
5807c478bd9Sstevel@tonic-gate };
5817c478bd9Sstevel@tonic-gate 
5827c478bd9Sstevel@tonic-gate 
5837c478bd9Sstevel@tonic-gate /*
5847c478bd9Sstevel@tonic-gate  * hubd_get_soft_state() returns the hubd soft state
5857c478bd9Sstevel@tonic-gate  */
5867c478bd9Sstevel@tonic-gate static hubd_t *
5877c478bd9Sstevel@tonic-gate hubd_get_soft_state(dev_info_t *dip)
5887c478bd9Sstevel@tonic-gate {
5897c478bd9Sstevel@tonic-gate 	if (dip == NULL) {
5907c478bd9Sstevel@tonic-gate 
5917c478bd9Sstevel@tonic-gate 		return (NULL);
5927c478bd9Sstevel@tonic-gate 	}
5937c478bd9Sstevel@tonic-gate 
5947c478bd9Sstevel@tonic-gate 	if (usba_is_root_hub(dip)) {
5957c478bd9Sstevel@tonic-gate 		usba_device_t *usba_device = usba_get_usba_device(dip);
5967c478bd9Sstevel@tonic-gate 
5977c478bd9Sstevel@tonic-gate 		return (usba_device->usb_root_hubd);
5987c478bd9Sstevel@tonic-gate 	} else {
5997c478bd9Sstevel@tonic-gate 		int instance = ddi_get_instance(dip);
6007c478bd9Sstevel@tonic-gate 
6017c478bd9Sstevel@tonic-gate 		return (ddi_get_soft_state(hubd_statep, instance));
6027c478bd9Sstevel@tonic-gate 	}
6037c478bd9Sstevel@tonic-gate }
6047c478bd9Sstevel@tonic-gate 
6057c478bd9Sstevel@tonic-gate 
6067c478bd9Sstevel@tonic-gate /*
6077c478bd9Sstevel@tonic-gate  * PM support functions:
6087c478bd9Sstevel@tonic-gate  */
6097c478bd9Sstevel@tonic-gate /*ARGSUSED*/
6107c478bd9Sstevel@tonic-gate static void
6117c478bd9Sstevel@tonic-gate hubd_pm_busy_component(hubd_t *hubd, dev_info_t *dip, int component)
6127c478bd9Sstevel@tonic-gate {
6137c478bd9Sstevel@tonic-gate 	if (hubd->h_hubpm != NULL) {
6147c478bd9Sstevel@tonic-gate 		hubd->h_hubpm->hubp_busy_pm++;
6157c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
6167c478bd9Sstevel@tonic-gate 		if (pm_busy_component(dip, 0) != DDI_SUCCESS) {
6177c478bd9Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
6187c478bd9Sstevel@tonic-gate 			hubd->h_hubpm->hubp_busy_pm--;
6197c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
6207c478bd9Sstevel@tonic-gate 		}
6217c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
6227c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
6237c478bd9Sstevel@tonic-gate 		    "hubd_pm_busy_component: %d", hubd->h_hubpm->hubp_busy_pm);
6247c478bd9Sstevel@tonic-gate 	}
6257c478bd9Sstevel@tonic-gate }
6267c478bd9Sstevel@tonic-gate 
6277c478bd9Sstevel@tonic-gate 
6287c478bd9Sstevel@tonic-gate /*ARGSUSED*/
6297c478bd9Sstevel@tonic-gate static void
6307c478bd9Sstevel@tonic-gate hubd_pm_idle_component(hubd_t *hubd, dev_info_t *dip, int component)
6317c478bd9Sstevel@tonic-gate {
6327c478bd9Sstevel@tonic-gate 	if (hubd->h_hubpm != NULL) {
6337c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
6347c478bd9Sstevel@tonic-gate 		if (pm_idle_component(dip, 0) == DDI_SUCCESS) {
6357c478bd9Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
6367c478bd9Sstevel@tonic-gate 			ASSERT(hubd->h_hubpm->hubp_busy_pm > 0);
6377c478bd9Sstevel@tonic-gate 			hubd->h_hubpm->hubp_busy_pm--;
6387c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
6397c478bd9Sstevel@tonic-gate 		}
6407c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
6417c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
6427c478bd9Sstevel@tonic-gate 		    "hubd_pm_idle_component: %d", hubd->h_hubpm->hubp_busy_pm);
6437c478bd9Sstevel@tonic-gate 	}
6447c478bd9Sstevel@tonic-gate }
6457c478bd9Sstevel@tonic-gate 
6467c478bd9Sstevel@tonic-gate 
6477c478bd9Sstevel@tonic-gate /*
6487c478bd9Sstevel@tonic-gate  * track power level changes for children of this instance
6497c478bd9Sstevel@tonic-gate  */
6507c478bd9Sstevel@tonic-gate static void
6517c478bd9Sstevel@tonic-gate hubd_set_child_pwrlvl(hubd_t *hubd, usb_port_t port, uint8_t power)
6527c478bd9Sstevel@tonic-gate {
6537c478bd9Sstevel@tonic-gate 	int	old_power, new_power, pwr;
6547c478bd9Sstevel@tonic-gate 	usb_port_t	portno;
6557c478bd9Sstevel@tonic-gate 	hub_power_t	*hubpm;
6567c478bd9Sstevel@tonic-gate 
6577c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
6587c478bd9Sstevel@tonic-gate 	    "hubd_set_child_pwrlvl: port=%d power=%d",
6597c478bd9Sstevel@tonic-gate 	    port, power);
6607c478bd9Sstevel@tonic-gate 
6617c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
6627c478bd9Sstevel@tonic-gate 	hubpm = hubd->h_hubpm;
6637c478bd9Sstevel@tonic-gate 
6647c478bd9Sstevel@tonic-gate 	old_power = 0;
6657c478bd9Sstevel@tonic-gate 	for (portno = 1; portno <= hubd->h_hub_descr.bNbrPorts; portno++) {
6667c478bd9Sstevel@tonic-gate 		old_power += hubpm->hubp_child_pwrstate[portno];
6677c478bd9Sstevel@tonic-gate 	}
6687c478bd9Sstevel@tonic-gate 
6697c478bd9Sstevel@tonic-gate 	/* assign the port power */
6707c478bd9Sstevel@tonic-gate 	pwr = hubd->h_hubpm->hubp_child_pwrstate[port];
6717c478bd9Sstevel@tonic-gate 	hubd->h_hubpm->hubp_child_pwrstate[port] = power;
6727c478bd9Sstevel@tonic-gate 	new_power = old_power - pwr + power;
6737c478bd9Sstevel@tonic-gate 
6747c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
6757c478bd9Sstevel@tonic-gate 	    "hubd_set_child_pwrlvl: new_power=%d old_power=%d",
6767c478bd9Sstevel@tonic-gate 	    new_power, old_power);
6777c478bd9Sstevel@tonic-gate 
6787c478bd9Sstevel@tonic-gate 	if ((new_power > 0) && (old_power == 0)) {
6797c478bd9Sstevel@tonic-gate 		/* we have the first child coming out of low power */
6807c478bd9Sstevel@tonic-gate 		(void) hubd_pm_busy_component(hubd, hubd->h_dip, 0);
6817c478bd9Sstevel@tonic-gate 	} else if ((new_power == 0) && (old_power > 0)) {
6827c478bd9Sstevel@tonic-gate 		/* we have the last child going to low power */
6837c478bd9Sstevel@tonic-gate 		(void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
6847c478bd9Sstevel@tonic-gate 	}
6857c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
6867c478bd9Sstevel@tonic-gate }
6877c478bd9Sstevel@tonic-gate 
6887c478bd9Sstevel@tonic-gate 
6897c478bd9Sstevel@tonic-gate /*
6907c478bd9Sstevel@tonic-gate  * given a child dip, locate its port number
6917c478bd9Sstevel@tonic-gate  */
6927c478bd9Sstevel@tonic-gate static usb_port_t
6937c478bd9Sstevel@tonic-gate hubd_child_dip2port(hubd_t *hubd, dev_info_t *dip)
6947c478bd9Sstevel@tonic-gate {
6957c478bd9Sstevel@tonic-gate 	usb_port_t	port;
6967c478bd9Sstevel@tonic-gate 
6977c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
6987c478bd9Sstevel@tonic-gate 	for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
6997c478bd9Sstevel@tonic-gate 		if (hubd->h_children_dips[port] == dip) {
7007c478bd9Sstevel@tonic-gate 
7017c478bd9Sstevel@tonic-gate 			break;
7027c478bd9Sstevel@tonic-gate 		}
7037c478bd9Sstevel@tonic-gate 	}
7047c478bd9Sstevel@tonic-gate 	ASSERT(port <= hubd->h_hub_descr.bNbrPorts);
7057c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
7067c478bd9Sstevel@tonic-gate 
7077c478bd9Sstevel@tonic-gate 	return (port);
7087c478bd9Sstevel@tonic-gate }
7097c478bd9Sstevel@tonic-gate 
7107c478bd9Sstevel@tonic-gate 
7117c478bd9Sstevel@tonic-gate /*
7127c478bd9Sstevel@tonic-gate  * if the hub can be put into low power mode, return success
7137c478bd9Sstevel@tonic-gate  * NOTE: suspend here means going to lower power, not CPR suspend.
7147c478bd9Sstevel@tonic-gate  */
7157c478bd9Sstevel@tonic-gate static int
7167c478bd9Sstevel@tonic-gate hubd_can_suspend(hubd_t *hubd)
7177c478bd9Sstevel@tonic-gate {
7187c478bd9Sstevel@tonic-gate 	hub_power_t	*hubpm;
7197c478bd9Sstevel@tonic-gate 	int		total_power = 0;
7207c478bd9Sstevel@tonic-gate 	usb_port_t	port;
7217c478bd9Sstevel@tonic-gate 
7227c478bd9Sstevel@tonic-gate 	hubpm = hubd->h_hubpm;
7237c478bd9Sstevel@tonic-gate 
7247c478bd9Sstevel@tonic-gate 	if (DEVI_IS_DETACHING(hubd->h_dip)) {
7257c478bd9Sstevel@tonic-gate 
7267c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
7277c478bd9Sstevel@tonic-gate 	}
7287c478bd9Sstevel@tonic-gate 
7297c478bd9Sstevel@tonic-gate 	/*
7307c478bd9Sstevel@tonic-gate 	 * Don't go to lower power if haven't been at full power for enough
7317c478bd9Sstevel@tonic-gate 	 * time to let hotplug thread kickoff.
7327c478bd9Sstevel@tonic-gate 	 */
7337c478bd9Sstevel@tonic-gate 	if (ddi_get_time() < (hubpm->hubp_time_at_full_power +
7347c478bd9Sstevel@tonic-gate 	    hubpm->hubp_min_pm_threshold)) {
7357c478bd9Sstevel@tonic-gate 
7367c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
7377c478bd9Sstevel@tonic-gate 	}
7387c478bd9Sstevel@tonic-gate 
7397c478bd9Sstevel@tonic-gate 	for (port = 1; (total_power == 0) &&
7407c478bd9Sstevel@tonic-gate 	    (port <= hubd->h_hub_descr.bNbrPorts); port++) {
7417c478bd9Sstevel@tonic-gate 		total_power += hubpm->hubp_child_pwrstate[port];
7427c478bd9Sstevel@tonic-gate 	}
7437c478bd9Sstevel@tonic-gate 
7447c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
7457c478bd9Sstevel@tonic-gate 		"hubd_can_suspend: %d", total_power);
7467c478bd9Sstevel@tonic-gate 
7477c478bd9Sstevel@tonic-gate 	return (total_power ? USB_FAILURE : USB_SUCCESS);
7487c478bd9Sstevel@tonic-gate }
7497c478bd9Sstevel@tonic-gate 
7507c478bd9Sstevel@tonic-gate 
7517c478bd9Sstevel@tonic-gate /*
7527c478bd9Sstevel@tonic-gate  * resume port depending on current device state
7537c478bd9Sstevel@tonic-gate  */
7547c478bd9Sstevel@tonic-gate static int
7557c478bd9Sstevel@tonic-gate hubd_resume_port(hubd_t *hubd, usb_port_t port)
7567c478bd9Sstevel@tonic-gate {
7577c478bd9Sstevel@tonic-gate 	int		rval, retry;
7587c478bd9Sstevel@tonic-gate 	usb_cr_t	completion_reason;
7597c478bd9Sstevel@tonic-gate 	usb_cb_flags_t	cb_flags;
7607c478bd9Sstevel@tonic-gate 	uint16_t	status;
7617c478bd9Sstevel@tonic-gate 	uint16_t	change;
7627c478bd9Sstevel@tonic-gate 	int		retval = USB_FAILURE;
7637c478bd9Sstevel@tonic-gate 
7647c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
7657c478bd9Sstevel@tonic-gate 
7667c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
7677c478bd9Sstevel@tonic-gate 	    "hubd_resume_port: port=%d state=0x%x (%s)", port,
7687c478bd9Sstevel@tonic-gate 	    hubd->h_dev_state, usb_str_dev_state(hubd->h_dev_state));
7697c478bd9Sstevel@tonic-gate 
7707c478bd9Sstevel@tonic-gate 	switch (hubd->h_dev_state) {
7717c478bd9Sstevel@tonic-gate 	case USB_DEV_HUB_CHILD_PWRLVL:
7727c478bd9Sstevel@tonic-gate 		/*
7737c478bd9Sstevel@tonic-gate 		 * This could be a bus ctl for a port other than the one
7747c478bd9Sstevel@tonic-gate 		 * that has a remote wakeup condition. So check.
7757c478bd9Sstevel@tonic-gate 		 */
7767c478bd9Sstevel@tonic-gate 		if ((hubd->h_port_state[port] & PORT_STATUS_PSS) == 0) {
7777c478bd9Sstevel@tonic-gate 			/* the port isn't suspended, so don't resume */
7787c478bd9Sstevel@tonic-gate 			retval = USB_SUCCESS;
7797c478bd9Sstevel@tonic-gate 
7807c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
7817c478bd9Sstevel@tonic-gate 			    "hubd_resume_port: port=%d not suspended", port);
7827c478bd9Sstevel@tonic-gate 
7837c478bd9Sstevel@tonic-gate 			break;
7847c478bd9Sstevel@tonic-gate 		}
7857c478bd9Sstevel@tonic-gate 		/*
7867c478bd9Sstevel@tonic-gate 		 * Device has initiated a wakeup.
7877c478bd9Sstevel@tonic-gate 		 * Issue a ClearFeature(PortSuspend)
7887c478bd9Sstevel@tonic-gate 		 */
7897c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
7907c478bd9Sstevel@tonic-gate 		if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
7917c478bd9Sstevel@tonic-gate 		    hubd->h_default_pipe,
7927c478bd9Sstevel@tonic-gate 		    HANDLE_PORT_FEATURE,
7937c478bd9Sstevel@tonic-gate 		    USB_REQ_CLEAR_FEATURE,
7947c478bd9Sstevel@tonic-gate 		    CFS_PORT_SUSPEND,
7957c478bd9Sstevel@tonic-gate 		    port,
7967c478bd9Sstevel@tonic-gate 		    0, NULL, 0,
7977c478bd9Sstevel@tonic-gate 		    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
7987c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
7997c478bd9Sstevel@tonic-gate 			    "ClearFeature(PortSuspend) fails "
8007c478bd9Sstevel@tonic-gate 			    "rval=%d cr=%d cb=0x%x", rval,
8017c478bd9Sstevel@tonic-gate 			    completion_reason, cb_flags);
8027c478bd9Sstevel@tonic-gate 		}
8037c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
8047c478bd9Sstevel@tonic-gate 
8057c478bd9Sstevel@tonic-gate 		/* either way ack changes on the port */
8067c478bd9Sstevel@tonic-gate 		(void) hubd_determine_port_status(hubd, port,
8077c478bd9Sstevel@tonic-gate 			&status, &change, PORT_CHANGE_PSSC);
8087c478bd9Sstevel@tonic-gate 		retval = USB_SUCCESS;
8097c478bd9Sstevel@tonic-gate 
8107c478bd9Sstevel@tonic-gate 		break;
8117c478bd9Sstevel@tonic-gate 	case USB_DEV_HUB_STATE_RECOVER:
8127c478bd9Sstevel@tonic-gate 		/*
8137c478bd9Sstevel@tonic-gate 		 * When hubd's connect event callback posts a connect
8147c478bd9Sstevel@tonic-gate 		 * event to its child, it results in this busctl call
8157c478bd9Sstevel@tonic-gate 		 * which is valid
8167c478bd9Sstevel@tonic-gate 		 */
8177c478bd9Sstevel@tonic-gate 		/* FALLTHRU */
8187c478bd9Sstevel@tonic-gate 	case USB_DEV_ONLINE:
8197c478bd9Sstevel@tonic-gate 		if ((hubd->h_port_state[port] &
8207c478bd9Sstevel@tonic-gate 		    (PORT_STATUS_PSS | PORT_STATUS_CCS)) == 0) {
8217c478bd9Sstevel@tonic-gate 			/*
8227c478bd9Sstevel@tonic-gate 			 * the port isn't suspended, or connected
8237c478bd9Sstevel@tonic-gate 			 * so don't resume
8247c478bd9Sstevel@tonic-gate 			 */
8257c478bd9Sstevel@tonic-gate 			retval = USB_SUCCESS;
8267c478bd9Sstevel@tonic-gate 
8277c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
8287c478bd9Sstevel@tonic-gate 			    "hubd_resume_port: port=%d not suspended", port);
8297c478bd9Sstevel@tonic-gate 
8307c478bd9Sstevel@tonic-gate 			break;
8317c478bd9Sstevel@tonic-gate 		}
8327c478bd9Sstevel@tonic-gate 		/*
8337c478bd9Sstevel@tonic-gate 		 * prevent kicking off the hotplug thread
8347c478bd9Sstevel@tonic-gate 		 */
8357c478bd9Sstevel@tonic-gate 		hubd->h_hotplug_thread++;
8367c478bd9Sstevel@tonic-gate 		hubd_stop_polling(hubd);
8377c478bd9Sstevel@tonic-gate 
8387c478bd9Sstevel@tonic-gate 		/* Now ClearFeature(PortSuspend) */
8397c478bd9Sstevel@tonic-gate 		for (retry = 0; retry < HUBD_SUS_RES_RETRY; retry++) {
8407c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
8417c478bd9Sstevel@tonic-gate 			rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
8427c478bd9Sstevel@tonic-gate 			    hubd->h_default_pipe,
8437c478bd9Sstevel@tonic-gate 			    HANDLE_PORT_FEATURE,
8447c478bd9Sstevel@tonic-gate 			    USB_REQ_CLEAR_FEATURE,
8457c478bd9Sstevel@tonic-gate 			    CFS_PORT_SUSPEND,
8467c478bd9Sstevel@tonic-gate 			    port,
8477c478bd9Sstevel@tonic-gate 			    0, NULL, 0,
8487c478bd9Sstevel@tonic-gate 			    &completion_reason, &cb_flags, 0);
8497c478bd9Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
8507c478bd9Sstevel@tonic-gate 			if (rval != USB_SUCCESS) {
8517c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_PM,
8527c478bd9Sstevel@tonic-gate 				    hubd->h_log_handle,
8537c478bd9Sstevel@tonic-gate 				    "ClearFeature(PortSuspend) fails"
8547c478bd9Sstevel@tonic-gate 				    "rval=%d cr=%d cb=0x%x", rval,
8557c478bd9Sstevel@tonic-gate 				    completion_reason, cb_flags);
8567c478bd9Sstevel@tonic-gate 			} else {
8577c478bd9Sstevel@tonic-gate 				/*
8587c478bd9Sstevel@tonic-gate 				 * As per spec section 11.9 and 7.1.7.7
8597c478bd9Sstevel@tonic-gate 				 * hub need to provide at least 20ms of
8607c478bd9Sstevel@tonic-gate 				 * resume signalling, and s/w provide 10ms of
8617c478bd9Sstevel@tonic-gate 				 * recovery time before accessing the port.
8627c478bd9Sstevel@tonic-gate 				 */
8637c478bd9Sstevel@tonic-gate 				mutex_exit(HUBD_MUTEX(hubd));
8647c478bd9Sstevel@tonic-gate 				delay(drv_usectohz(40000));
8657c478bd9Sstevel@tonic-gate 				mutex_enter(HUBD_MUTEX(hubd));
8667c478bd9Sstevel@tonic-gate 				(void) hubd_determine_port_status(hubd, port,
8677c478bd9Sstevel@tonic-gate 				    &status, &change, PORT_CHANGE_PSSC);
8687c478bd9Sstevel@tonic-gate 
8697c478bd9Sstevel@tonic-gate 				if ((status & PORT_STATUS_PSS) == 0) {
8707c478bd9Sstevel@tonic-gate 					/* the port did finally resume */
8717c478bd9Sstevel@tonic-gate 					retval = USB_SUCCESS;
8727c478bd9Sstevel@tonic-gate 
8737c478bd9Sstevel@tonic-gate 					break;
8747c478bd9Sstevel@tonic-gate 				}
8757c478bd9Sstevel@tonic-gate 			}
8767c478bd9Sstevel@tonic-gate 		}
8777c478bd9Sstevel@tonic-gate 
8787c478bd9Sstevel@tonic-gate 		/* allow hotplug thread again */
8797c478bd9Sstevel@tonic-gate 		hubd->h_hotplug_thread--;
8807c478bd9Sstevel@tonic-gate 		hubd_start_polling(hubd, 0);
8817c478bd9Sstevel@tonic-gate 
8827c478bd9Sstevel@tonic-gate 		break;
8837c478bd9Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
8847c478bd9Sstevel@tonic-gate 		/* Ignore - NO Operation */
8857c478bd9Sstevel@tonic-gate 		retval = USB_SUCCESS;
8867c478bd9Sstevel@tonic-gate 
8877c478bd9Sstevel@tonic-gate 		break;
8887c478bd9Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
8897c478bd9Sstevel@tonic-gate 	case USB_DEV_PWRED_DOWN:
8907c478bd9Sstevel@tonic-gate 	default:
8917c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
8927c478bd9Sstevel@tonic-gate 		    "Improper state for port Resume");
8937c478bd9Sstevel@tonic-gate 
8947c478bd9Sstevel@tonic-gate 		break;
8957c478bd9Sstevel@tonic-gate 	}
8967c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
8977c478bd9Sstevel@tonic-gate 
8987c478bd9Sstevel@tonic-gate 	return (retval);
8997c478bd9Sstevel@tonic-gate }
9007c478bd9Sstevel@tonic-gate 
9017c478bd9Sstevel@tonic-gate 
9027c478bd9Sstevel@tonic-gate /*
9037c478bd9Sstevel@tonic-gate  * suspend port depending on device state
9047c478bd9Sstevel@tonic-gate  */
9057c478bd9Sstevel@tonic-gate static int
9067c478bd9Sstevel@tonic-gate hubd_suspend_port(hubd_t *hubd, usb_port_t port)
9077c478bd9Sstevel@tonic-gate {
9087c478bd9Sstevel@tonic-gate 	int		rval, retry;
9097c478bd9Sstevel@tonic-gate 	int		retval = USB_FAILURE;
9107c478bd9Sstevel@tonic-gate 	usb_cr_t	completion_reason;
9117c478bd9Sstevel@tonic-gate 	usb_cb_flags_t	cb_flags;
9127c478bd9Sstevel@tonic-gate 	uint16_t	status;
9137c478bd9Sstevel@tonic-gate 	uint16_t	change;
9147c478bd9Sstevel@tonic-gate 
9157c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
9167c478bd9Sstevel@tonic-gate 	    "hubd_suspend_port: port=%d", port);
9177c478bd9Sstevel@tonic-gate 
9187c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
9197c478bd9Sstevel@tonic-gate 
9207c478bd9Sstevel@tonic-gate 	switch (hubd->h_dev_state) {
9217c478bd9Sstevel@tonic-gate 	case USB_DEV_HUB_STATE_RECOVER:
9227c478bd9Sstevel@tonic-gate 		/*
9237c478bd9Sstevel@tonic-gate 		 * When hubd's connect event callback posts a connect
9247c478bd9Sstevel@tonic-gate 		 * event to its child, it results in this busctl call
9257c478bd9Sstevel@tonic-gate 		 * which is valid
9267c478bd9Sstevel@tonic-gate 		 */
9277c478bd9Sstevel@tonic-gate 		/* FALLTHRU */
9287c478bd9Sstevel@tonic-gate 	case USB_DEV_HUB_CHILD_PWRLVL:
9297c478bd9Sstevel@tonic-gate 		/*
9307c478bd9Sstevel@tonic-gate 		 * When one child is resuming, the other could timeout
9317c478bd9Sstevel@tonic-gate 		 * and go to low power mode, which is valid
9327c478bd9Sstevel@tonic-gate 		 */
9337c478bd9Sstevel@tonic-gate 		/* FALLTHRU */
9347c478bd9Sstevel@tonic-gate 	case USB_DEV_ONLINE:
9357c478bd9Sstevel@tonic-gate 		hubd->h_hotplug_thread++;
9367c478bd9Sstevel@tonic-gate 		hubd_stop_polling(hubd);
9377c478bd9Sstevel@tonic-gate 
9387c478bd9Sstevel@tonic-gate 		/*
9397c478bd9Sstevel@tonic-gate 		 * Some devices start an unprovoked resume.  According to spec,
9407c478bd9Sstevel@tonic-gate 		 * normal resume time for port is 10ms.  Wait for double that
9417c478bd9Sstevel@tonic-gate 		 * time, then check to be sure port is really suspended.
9427c478bd9Sstevel@tonic-gate 		 */
9437c478bd9Sstevel@tonic-gate 		for (retry = 0; retry < HUBD_SUS_RES_RETRY; retry++) {
9447c478bd9Sstevel@tonic-gate 			/* Now SetFeature(PortSuspend) */
9457c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
9467c478bd9Sstevel@tonic-gate 			if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
9477c478bd9Sstevel@tonic-gate 			    hubd->h_default_pipe,
9487c478bd9Sstevel@tonic-gate 			    HANDLE_PORT_FEATURE,
9497c478bd9Sstevel@tonic-gate 			    USB_REQ_SET_FEATURE,
9507c478bd9Sstevel@tonic-gate 			    CFS_PORT_SUSPEND,
9517c478bd9Sstevel@tonic-gate 			    port,
9527c478bd9Sstevel@tonic-gate 			    0, NULL, 0,
9537c478bd9Sstevel@tonic-gate 			    &completion_reason, &cb_flags, 0)) !=
9547c478bd9Sstevel@tonic-gate 			    USB_SUCCESS) {
9557c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_PM,
9567c478bd9Sstevel@tonic-gate 				    hubd->h_log_handle,
9577c478bd9Sstevel@tonic-gate 				    "SetFeature(PortSuspend) fails"
9587c478bd9Sstevel@tonic-gate 				    "rval=%d cr=%d cb=0x%x",
9597c478bd9Sstevel@tonic-gate 				    rval, completion_reason, cb_flags);
9607c478bd9Sstevel@tonic-gate 			}
9617c478bd9Sstevel@tonic-gate 
9627c478bd9Sstevel@tonic-gate 			/*
9637c478bd9Sstevel@tonic-gate 			 * some devices start an unprovoked resume
9647c478bd9Sstevel@tonic-gate 			 * wait and check port status after some time
9657c478bd9Sstevel@tonic-gate 			 */
9667c478bd9Sstevel@tonic-gate 			delay(drv_usectohz(20000));
9677c478bd9Sstevel@tonic-gate 
9687c478bd9Sstevel@tonic-gate 			/* either ways ack changes on the port */
9697c478bd9Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
9707c478bd9Sstevel@tonic-gate 			(void) hubd_determine_port_status(hubd, port,
9717c478bd9Sstevel@tonic-gate 			    &status, &change, PORT_CHANGE_PSSC);
9727c478bd9Sstevel@tonic-gate 			if (status & PORT_STATUS_PSS) {
9737c478bd9Sstevel@tonic-gate 				/* the port is indeed suspended */
9747c478bd9Sstevel@tonic-gate 				retval = USB_SUCCESS;
9757c478bd9Sstevel@tonic-gate 
9767c478bd9Sstevel@tonic-gate 				break;
9777c478bd9Sstevel@tonic-gate 			}
9787c478bd9Sstevel@tonic-gate 		}
9797c478bd9Sstevel@tonic-gate 
9807c478bd9Sstevel@tonic-gate 		hubd->h_hotplug_thread--;
9817c478bd9Sstevel@tonic-gate 		hubd_start_polling(hubd, 0);
9827c478bd9Sstevel@tonic-gate 
9837c478bd9Sstevel@tonic-gate 		break;
9847c478bd9Sstevel@tonic-gate 
9857c478bd9Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
9867c478bd9Sstevel@tonic-gate 		/* Ignore - No Operation */
9877c478bd9Sstevel@tonic-gate 		retval = USB_SUCCESS;
9887c478bd9Sstevel@tonic-gate 
9897c478bd9Sstevel@tonic-gate 		break;
9907c478bd9Sstevel@tonic-gate 
9917c478bd9Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
9927c478bd9Sstevel@tonic-gate 	case USB_DEV_PWRED_DOWN:
9937c478bd9Sstevel@tonic-gate 	default:
9947c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
9957c478bd9Sstevel@tonic-gate 		    "Improper state for port Suspend");
9967c478bd9Sstevel@tonic-gate 
9977c478bd9Sstevel@tonic-gate 		break;
9987c478bd9Sstevel@tonic-gate 	}
9997c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
10007c478bd9Sstevel@tonic-gate 
10017c478bd9Sstevel@tonic-gate 	return (retval);
10027c478bd9Sstevel@tonic-gate }
10037c478bd9Sstevel@tonic-gate 
10047c478bd9Sstevel@tonic-gate 
10057c478bd9Sstevel@tonic-gate /*
10067c478bd9Sstevel@tonic-gate  * child post attach/detach notifications
10077c478bd9Sstevel@tonic-gate  */
10087c478bd9Sstevel@tonic-gate static void
10097c478bd9Sstevel@tonic-gate hubd_post_attach(hubd_t *hubd, usb_port_t port, struct attachspec *as)
10107c478bd9Sstevel@tonic-gate {
10117c478bd9Sstevel@tonic-gate 	dev_info_t	*dip;
10127c478bd9Sstevel@tonic-gate 
10137c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
10147c478bd9Sstevel@tonic-gate 	    "hubd_post_attach: port=%d result=%d",
10157c478bd9Sstevel@tonic-gate 	    port, as->result);
10167c478bd9Sstevel@tonic-gate 
10177c478bd9Sstevel@tonic-gate 	if (as->result == DDI_SUCCESS) {
10187c478bd9Sstevel@tonic-gate 		/*
10197c478bd9Sstevel@tonic-gate 		 * Check if the child created wants to be power managed.
10207c478bd9Sstevel@tonic-gate 		 * If yes, the childs power level gets automatically tracked
10217c478bd9Sstevel@tonic-gate 		 * by DDI_CTLOPS_POWER busctl.
10227c478bd9Sstevel@tonic-gate 		 * If no, we set power of the new child by default
10237c478bd9Sstevel@tonic-gate 		 * to USB_DEV_OS_FULL_PWR. Because we should never suspend.
10247c478bd9Sstevel@tonic-gate 		 */
10257c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
10267c478bd9Sstevel@tonic-gate 		dip = hubd->h_children_dips[port];
10277c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
10287c478bd9Sstevel@tonic-gate 		if (DEVI(dip)->devi_pm_info == NULL) {
10297c478bd9Sstevel@tonic-gate 			hubd_set_child_pwrlvl(hubd, port, USB_DEV_OS_FULL_PWR);
10307c478bd9Sstevel@tonic-gate 		}
10317c478bd9Sstevel@tonic-gate 	}
10327c478bd9Sstevel@tonic-gate }
10337c478bd9Sstevel@tonic-gate 
10347c478bd9Sstevel@tonic-gate 
10357c478bd9Sstevel@tonic-gate static void
10367c478bd9Sstevel@tonic-gate hubd_post_detach(hubd_t *hubd, usb_port_t port, struct detachspec *ds)
10377c478bd9Sstevel@tonic-gate {
10387c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
10397c478bd9Sstevel@tonic-gate 	    "hubd_post_detach: port=%d result=%d", port, ds->result);
10407c478bd9Sstevel@tonic-gate 
10417c478bd9Sstevel@tonic-gate 	/*
10427c478bd9Sstevel@tonic-gate 	 * if the device is successfully detached and is the
10437c478bd9Sstevel@tonic-gate 	 * last device to detach, mark component as idle
10447c478bd9Sstevel@tonic-gate 	 */
10457c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
10467c478bd9Sstevel@tonic-gate 	if (ds->result == DDI_SUCCESS) {
10477c478bd9Sstevel@tonic-gate 		usba_device_t	*usba_device = hubd->h_usba_devices[port];
10487c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
10497c478bd9Sstevel@tonic-gate 
10507c478bd9Sstevel@tonic-gate 		/*
10517c478bd9Sstevel@tonic-gate 		 * We set power of the detached child
10527c478bd9Sstevel@tonic-gate 		 * to 0, so that we can suspend if all
10537c478bd9Sstevel@tonic-gate 		 * our children are gone
10547c478bd9Sstevel@tonic-gate 		 */
10557c478bd9Sstevel@tonic-gate 		hubd_set_child_pwrlvl(hubd, port, USB_DEV_OS_PWR_OFF);
10567c478bd9Sstevel@tonic-gate 
10577c478bd9Sstevel@tonic-gate 		/* check for leaks on detaching */
10587c478bd9Sstevel@tonic-gate 		if ((usba_device) && (ds->cmd == DDI_DETACH)) {
10597c478bd9Sstevel@tonic-gate 			usba_check_for_leaks(usba_device);
10607c478bd9Sstevel@tonic-gate 		}
10617c478bd9Sstevel@tonic-gate 	} else {
10627c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
10637c478bd9Sstevel@tonic-gate 	}
10647c478bd9Sstevel@tonic-gate }
10657c478bd9Sstevel@tonic-gate 
10667c478bd9Sstevel@tonic-gate 
10677c478bd9Sstevel@tonic-gate /*
10687c478bd9Sstevel@tonic-gate  * hubd_post_power
10697c478bd9Sstevel@tonic-gate  *	After the child's power entry point has been called
10707c478bd9Sstevel@tonic-gate  *	we record its power level in our local struct.
10717c478bd9Sstevel@tonic-gate  *	If the device has powered off, we suspend port
10727c478bd9Sstevel@tonic-gate  */
10737c478bd9Sstevel@tonic-gate static int
10747c478bd9Sstevel@tonic-gate hubd_post_power(hubd_t *hubd, usb_port_t port, pm_bp_child_pwrchg_t *bpc,
10757c478bd9Sstevel@tonic-gate     int result)
10767c478bd9Sstevel@tonic-gate {
10777c478bd9Sstevel@tonic-gate 	int	retval = USB_SUCCESS;
10787c478bd9Sstevel@tonic-gate 
10797c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
10807c478bd9Sstevel@tonic-gate 	    "hubd_post_power: port=%d", port);
10817c478bd9Sstevel@tonic-gate 
10827c478bd9Sstevel@tonic-gate 	if (result == DDI_SUCCESS) {
10837c478bd9Sstevel@tonic-gate 
10847c478bd9Sstevel@tonic-gate 		/* record this power in our local struct */
10857c478bd9Sstevel@tonic-gate 		hubd_set_child_pwrlvl(hubd, port, bpc->bpc_nlevel);
10867c478bd9Sstevel@tonic-gate 
10877c478bd9Sstevel@tonic-gate 		if (bpc->bpc_nlevel == USB_DEV_OS_PWR_OFF) {
10887c478bd9Sstevel@tonic-gate 
10897c478bd9Sstevel@tonic-gate 			/* now suspend the port */
10907c478bd9Sstevel@tonic-gate 			retval = hubd_suspend_port(hubd, port);
10917c478bd9Sstevel@tonic-gate 		} else if (bpc->bpc_nlevel == USB_DEV_OS_FULL_PWR) {
10927c478bd9Sstevel@tonic-gate 
10937c478bd9Sstevel@tonic-gate 			/* make sure the port is resumed */
10947c478bd9Sstevel@tonic-gate 			retval = hubd_resume_port(hubd, port);
10957c478bd9Sstevel@tonic-gate 		}
10967c478bd9Sstevel@tonic-gate 	} else {
10977c478bd9Sstevel@tonic-gate 
10987c478bd9Sstevel@tonic-gate 		/* record old power in our local struct */
10997c478bd9Sstevel@tonic-gate 		hubd_set_child_pwrlvl(hubd, port, bpc->bpc_olevel);
11007c478bd9Sstevel@tonic-gate 
11017c478bd9Sstevel@tonic-gate 		if (bpc->bpc_olevel == USB_DEV_OS_PWR_OFF) {
11027c478bd9Sstevel@tonic-gate 
11037c478bd9Sstevel@tonic-gate 			/*
11047c478bd9Sstevel@tonic-gate 			 * As this device failed to transition from
11057c478bd9Sstevel@tonic-gate 			 * power off state, suspend the port again
11067c478bd9Sstevel@tonic-gate 			 */
11077c478bd9Sstevel@tonic-gate 			retval = hubd_suspend_port(hubd, port);
11087c478bd9Sstevel@tonic-gate 		}
11097c478bd9Sstevel@tonic-gate 	}
11107c478bd9Sstevel@tonic-gate 
11117c478bd9Sstevel@tonic-gate 	return (retval);
11127c478bd9Sstevel@tonic-gate }
11137c478bd9Sstevel@tonic-gate 
11147c478bd9Sstevel@tonic-gate 
11157c478bd9Sstevel@tonic-gate /*
11167c478bd9Sstevel@tonic-gate  * bus ctl notifications are handled here, the rest goes up to root hub/hcd
11177c478bd9Sstevel@tonic-gate  */
11187c478bd9Sstevel@tonic-gate static int
11197c478bd9Sstevel@tonic-gate usba_hubdi_bus_ctl(dev_info_t *dip,
11207c478bd9Sstevel@tonic-gate 	dev_info_t	*rdip,
11217c478bd9Sstevel@tonic-gate 	ddi_ctl_enum_t	op,
11227c478bd9Sstevel@tonic-gate 	void		*arg,
11237c478bd9Sstevel@tonic-gate 	void		*result)
11247c478bd9Sstevel@tonic-gate {
11257c478bd9Sstevel@tonic-gate 	usba_device_t *hub_usba_device = usba_get_usba_device(rdip);
11267c478bd9Sstevel@tonic-gate 	dev_info_t *root_hub_dip = hub_usba_device->usb_root_hub_dip;
11277c478bd9Sstevel@tonic-gate 	struct attachspec *as;
11287c478bd9Sstevel@tonic-gate 	struct detachspec *ds;
11297c478bd9Sstevel@tonic-gate 	hubd_t		*hubd;
11307c478bd9Sstevel@tonic-gate 	usb_port_t	port;
11317c478bd9Sstevel@tonic-gate 	int		circ, rval;
11327c478bd9Sstevel@tonic-gate 	int		retval = DDI_FAILURE;
11337c478bd9Sstevel@tonic-gate 
11347c478bd9Sstevel@tonic-gate 	hubd = hubd_get_soft_state(dip);
11357c478bd9Sstevel@tonic-gate 
11367c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
11377c478bd9Sstevel@tonic-gate 
11387c478bd9Sstevel@tonic-gate 	/* flag that we are currently running bus_ctl */
11397c478bd9Sstevel@tonic-gate 	hubd->h_bus_ctls++;
11407c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
11417c478bd9Sstevel@tonic-gate 
11427c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle,
11437c478bd9Sstevel@tonic-gate 	    "usba_hubdi_bus_ctl:\n\t"
11447c478bd9Sstevel@tonic-gate 	    "dip=0x%p, rdip=0x%p, op=0x%x, arg=0x%p",
11457c478bd9Sstevel@tonic-gate 	    dip, rdip, op, arg);
11467c478bd9Sstevel@tonic-gate 
11477c478bd9Sstevel@tonic-gate 	switch (op) {
11487c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_ATTACH:
11497c478bd9Sstevel@tonic-gate 		as = (struct attachspec *)arg;
11507c478bd9Sstevel@tonic-gate 		port = hubd_child_dip2port(hubd, rdip);
11517c478bd9Sstevel@tonic-gate 
11527c478bd9Sstevel@tonic-gate 		/* there is nothing to do at resume time */
11537c478bd9Sstevel@tonic-gate 		if (as->cmd == DDI_RESUME) {
11547c478bd9Sstevel@tonic-gate 			break;
11557c478bd9Sstevel@tonic-gate 		}
11567c478bd9Sstevel@tonic-gate 
11577c478bd9Sstevel@tonic-gate 		/* serialize access */
11587c478bd9Sstevel@tonic-gate 		ndi_devi_enter(hubd->h_dip, &circ);
11597c478bd9Sstevel@tonic-gate 
11607c478bd9Sstevel@tonic-gate 		switch (as->when) {
11617c478bd9Sstevel@tonic-gate 		case DDI_PRE:
11627c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
11637c478bd9Sstevel@tonic-gate 			    "DDI_PRE DDI_CTLOPS_ATTACH: dip=%p, port=%d",
11647c478bd9Sstevel@tonic-gate 			    rdip, port);
11657c478bd9Sstevel@tonic-gate 
11667c478bd9Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
11677c478bd9Sstevel@tonic-gate 			hubd->h_port_state[port] |= HUBD_CHILD_ATTACHING;
11687c478bd9Sstevel@tonic-gate 
11697c478bd9Sstevel@tonic-gate 			/* Go busy here.  Matching idle is DDI_POST case. */
11707c478bd9Sstevel@tonic-gate 			(void) hubd_pm_busy_component(hubd, dip, 0);
11717c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
11727c478bd9Sstevel@tonic-gate 
11737c478bd9Sstevel@tonic-gate 			/*
11747c478bd9Sstevel@tonic-gate 			 * if we suspended the port previously
11757c478bd9Sstevel@tonic-gate 			 * because child went to low power state, and
11767c478bd9Sstevel@tonic-gate 			 * someone unloaded the driver, the port would
11777c478bd9Sstevel@tonic-gate 			 * still be suspended and needs to be resumed
11787c478bd9Sstevel@tonic-gate 			 */
11797c478bd9Sstevel@tonic-gate 			rval = hubd_resume_port(hubd, port);
11807c478bd9Sstevel@tonic-gate 			if (rval == USB_SUCCESS) {
11817c478bd9Sstevel@tonic-gate 				retval = DDI_SUCCESS;
11827c478bd9Sstevel@tonic-gate 			}
11837c478bd9Sstevel@tonic-gate 
11847c478bd9Sstevel@tonic-gate 			break;
11857c478bd9Sstevel@tonic-gate 		case DDI_POST:
11867c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
11877c478bd9Sstevel@tonic-gate 			    "DDI_POST DDI_CTLOPS_ATTACH: dip=%p, port=%d",
11887c478bd9Sstevel@tonic-gate 			    rdip, port);
11897c478bd9Sstevel@tonic-gate 
11907c478bd9Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
11917c478bd9Sstevel@tonic-gate 			hubd->h_port_state[port] &= ~HUBD_CHILD_ATTACHING;
11927c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
11937c478bd9Sstevel@tonic-gate 
11947c478bd9Sstevel@tonic-gate 			hubd_post_attach(hubd, port, (struct attachspec *)arg);
11957c478bd9Sstevel@tonic-gate 			retval = DDI_SUCCESS;
11967c478bd9Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
11977c478bd9Sstevel@tonic-gate 
11987c478bd9Sstevel@tonic-gate 			/* Matching idle call for DDI_PRE busy call. */
11997c478bd9Sstevel@tonic-gate 			(void) hubd_pm_idle_component(hubd, dip, 0);
12007c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
12017c478bd9Sstevel@tonic-gate 		}
12027c478bd9Sstevel@tonic-gate 		ndi_devi_exit(hubd->h_dip, circ);
12037c478bd9Sstevel@tonic-gate 
12047c478bd9Sstevel@tonic-gate 		break;
12057c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_DETACH:
12067c478bd9Sstevel@tonic-gate 		ds = (struct detachspec *)arg;
12077c478bd9Sstevel@tonic-gate 		port = hubd_child_dip2port(hubd, rdip);
12087c478bd9Sstevel@tonic-gate 
12097c478bd9Sstevel@tonic-gate 		/* there is nothing to do at suspend time */
12107c478bd9Sstevel@tonic-gate 		if (ds->cmd == DDI_SUSPEND) {
12117c478bd9Sstevel@tonic-gate 			break;
12127c478bd9Sstevel@tonic-gate 		}
12137c478bd9Sstevel@tonic-gate 
12147c478bd9Sstevel@tonic-gate 		/* serialize access */
12157c478bd9Sstevel@tonic-gate 		ndi_devi_enter(hubd->h_dip, &circ);
12167c478bd9Sstevel@tonic-gate 
12177c478bd9Sstevel@tonic-gate 		switch (ds->when) {
12187c478bd9Sstevel@tonic-gate 		case DDI_PRE:
12197c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
12207c478bd9Sstevel@tonic-gate 			    "DDI_PRE DDI_CTLOPS_DETACH: dip=%p port=%d",
12217c478bd9Sstevel@tonic-gate 			    rdip, port);
12227c478bd9Sstevel@tonic-gate 
12237c478bd9Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
12247c478bd9Sstevel@tonic-gate 			hubd->h_port_state[port] |= HUBD_CHILD_DETACHING;
12257c478bd9Sstevel@tonic-gate 
12267c478bd9Sstevel@tonic-gate 			/* Go busy here.  Matching idle is DDI_POST case. */
12277c478bd9Sstevel@tonic-gate 			(void) hubd_pm_busy_component(hubd, dip, 0);
12287c478bd9Sstevel@tonic-gate 
12297c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
12307c478bd9Sstevel@tonic-gate 			retval = DDI_SUCCESS;
12317c478bd9Sstevel@tonic-gate 
12327c478bd9Sstevel@tonic-gate 			break;
12337c478bd9Sstevel@tonic-gate 		case DDI_POST:
12347c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
12357c478bd9Sstevel@tonic-gate 			    "DDI_POST DDI_CTLOPS_DETACH: dip=%p port=%d",
12367c478bd9Sstevel@tonic-gate 			    rdip, port);
12377c478bd9Sstevel@tonic-gate 
12387c478bd9Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
12397c478bd9Sstevel@tonic-gate 			hubd->h_port_state[port] &= ~HUBD_CHILD_DETACHING;
12407c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
12417c478bd9Sstevel@tonic-gate 
12427c478bd9Sstevel@tonic-gate 			/* Matching idle call for DDI_PRE busy call. */
12437c478bd9Sstevel@tonic-gate 			hubd_post_detach(hubd, port, (struct detachspec *)arg);
12447c478bd9Sstevel@tonic-gate 			retval = DDI_SUCCESS;
12457c478bd9Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
12467c478bd9Sstevel@tonic-gate 			(void) hubd_pm_idle_component(hubd, dip, 0);
12477c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
12487c478bd9Sstevel@tonic-gate 
12497c478bd9Sstevel@tonic-gate 			break;
12507c478bd9Sstevel@tonic-gate 		}
12517c478bd9Sstevel@tonic-gate 		ndi_devi_exit(hubd->h_dip, circ);
12527c478bd9Sstevel@tonic-gate 
12537c478bd9Sstevel@tonic-gate 		break;
12547c478bd9Sstevel@tonic-gate 	default:
12557c478bd9Sstevel@tonic-gate 		retval = usba_bus_ctl(root_hub_dip, rdip, op, arg, result);
12567c478bd9Sstevel@tonic-gate 	}
12577c478bd9Sstevel@tonic-gate 
12587c478bd9Sstevel@tonic-gate 	/* decrement bus_ctls count */
12597c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
12607c478bd9Sstevel@tonic-gate 	hubd->h_bus_ctls--;
12617c478bd9Sstevel@tonic-gate 	ASSERT(hubd->h_bus_ctls >= 0);
12627c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
12637c478bd9Sstevel@tonic-gate 
12647c478bd9Sstevel@tonic-gate 	return (retval);
12657c478bd9Sstevel@tonic-gate }
12667c478bd9Sstevel@tonic-gate 
12677c478bd9Sstevel@tonic-gate 
12687c478bd9Sstevel@tonic-gate /*
12697c478bd9Sstevel@tonic-gate  * bus enumeration entry points
12707c478bd9Sstevel@tonic-gate  */
12717c478bd9Sstevel@tonic-gate static int
12727c478bd9Sstevel@tonic-gate hubd_bus_config(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
12737c478bd9Sstevel@tonic-gate     void *arg, dev_info_t **child)
12747c478bd9Sstevel@tonic-gate {
12757c478bd9Sstevel@tonic-gate 	extern int modrootloaded;
12767c478bd9Sstevel@tonic-gate 
12777c478bd9Sstevel@tonic-gate 	hubd_t	*hubd = hubd_get_soft_state(dip);
12787c478bd9Sstevel@tonic-gate 	int	rval, circ;
12797c478bd9Sstevel@tonic-gate 
12807c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
12817c478bd9Sstevel@tonic-gate 	    "hubd_bus_config: op=%d", op);
12827c478bd9Sstevel@tonic-gate 
12837c478bd9Sstevel@tonic-gate 	if (hubdi_bus_config_debug) {
12847c478bd9Sstevel@tonic-gate 		flag |= NDI_DEVI_DEBUG;
12857c478bd9Sstevel@tonic-gate 	}
12867c478bd9Sstevel@tonic-gate 
12877c478bd9Sstevel@tonic-gate 	/*
12887c478bd9Sstevel@tonic-gate 	 * there must be a smarter way to do this but for
12897c478bd9Sstevel@tonic-gate 	 * now, a hack for booting USB storage.
12907c478bd9Sstevel@tonic-gate 	 */
12917c478bd9Sstevel@tonic-gate 	if (!modrootloaded) {
12927c478bd9Sstevel@tonic-gate 		delay(drv_usectohz(1000000));
12937c478bd9Sstevel@tonic-gate 	}
12947c478bd9Sstevel@tonic-gate 	ndi_devi_enter(hubd->h_dip, &circ);
12957c478bd9Sstevel@tonic-gate 	rval = ndi_busop_bus_config(dip, flag, op, arg, child, 0);
12967c478bd9Sstevel@tonic-gate 	ndi_devi_exit(hubd->h_dip, circ);
12977c478bd9Sstevel@tonic-gate 
12987c478bd9Sstevel@tonic-gate 	return (rval);
12997c478bd9Sstevel@tonic-gate }
13007c478bd9Sstevel@tonic-gate 
13017c478bd9Sstevel@tonic-gate 
13027c478bd9Sstevel@tonic-gate static int
13037c478bd9Sstevel@tonic-gate hubd_bus_unconfig(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
13047c478bd9Sstevel@tonic-gate     void *arg)
13057c478bd9Sstevel@tonic-gate {
13067c478bd9Sstevel@tonic-gate 	hubd_t		*hubd = hubd_get_soft_state(dip);
13077c478bd9Sstevel@tonic-gate 	dev_info_t	*cdip;
13087c478bd9Sstevel@tonic-gate 	usb_port_t	port;
13097c478bd9Sstevel@tonic-gate 	int		circ;
13107c478bd9Sstevel@tonic-gate 	int		rval;
13117c478bd9Sstevel@tonic-gate 
13127c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
13137c478bd9Sstevel@tonic-gate 	    "hubd_bus_unconfig: op=%d", op);
13147c478bd9Sstevel@tonic-gate 
13157c478bd9Sstevel@tonic-gate 	if (hubdi_bus_config_debug) {
13167c478bd9Sstevel@tonic-gate 		flag |= NDI_DEVI_DEBUG;
13177c478bd9Sstevel@tonic-gate 	}
13187c478bd9Sstevel@tonic-gate 
13197c478bd9Sstevel@tonic-gate 	if ((op == BUS_UNCONFIG_ALL) && (flag & NDI_AUTODETACH) == 0) {
13207c478bd9Sstevel@tonic-gate 		flag |= NDI_DEVI_REMOVE;
13217c478bd9Sstevel@tonic-gate 	}
13227c478bd9Sstevel@tonic-gate 
13237c478bd9Sstevel@tonic-gate 	/* serialize access */
13247c478bd9Sstevel@tonic-gate 	ndi_devi_enter(dip, &circ);
13257c478bd9Sstevel@tonic-gate 
13267c478bd9Sstevel@tonic-gate 	rval = ndi_busop_bus_unconfig(dip, flag, op, arg);
13277c478bd9Sstevel@tonic-gate 
13287c478bd9Sstevel@tonic-gate 	/* logically zap children's list */
13297c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
13307c478bd9Sstevel@tonic-gate 	for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
13317c478bd9Sstevel@tonic-gate 		hubd->h_port_state[port] |= HUBD_CHILD_ZAP;
13327c478bd9Sstevel@tonic-gate 	}
13337c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
13347c478bd9Sstevel@tonic-gate 
13357c478bd9Sstevel@tonic-gate 	/* fill in what's left */
13367c478bd9Sstevel@tonic-gate 	for (cdip = ddi_get_child(dip); cdip;
13377c478bd9Sstevel@tonic-gate 	    cdip = ddi_get_next_sibling(cdip)) {
13387c478bd9Sstevel@tonic-gate 		usba_device_t *usba_device = usba_get_usba_device(cdip);
13397c478bd9Sstevel@tonic-gate 
13407c478bd9Sstevel@tonic-gate 		if (usba_device == NULL) {
13417c478bd9Sstevel@tonic-gate 
13427c478bd9Sstevel@tonic-gate 			continue;
13437c478bd9Sstevel@tonic-gate 		}
13447c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
13457c478bd9Sstevel@tonic-gate 		port = usba_device->usb_port;
13467c478bd9Sstevel@tonic-gate 		hubd->h_children_dips[port] = cdip;
13477c478bd9Sstevel@tonic-gate 		hubd->h_port_state[port] &= ~HUBD_CHILD_ZAP;
13487c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
13497c478bd9Sstevel@tonic-gate 	}
13507c478bd9Sstevel@tonic-gate 
13517c478bd9Sstevel@tonic-gate 	/* physically zap the children we didn't find */
13527c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
13537c478bd9Sstevel@tonic-gate 	for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
13547c478bd9Sstevel@tonic-gate 		if (hubd->h_port_state[port] &  HUBD_CHILD_ZAP) {
13557c478bd9Sstevel@tonic-gate 			/* zap the dip and usba_device structure as well */
13567c478bd9Sstevel@tonic-gate 			hubd_free_usba_device(hubd, hubd->h_usba_devices[port]);
13577c478bd9Sstevel@tonic-gate 			hubd->h_children_dips[port] = NULL;
13587c478bd9Sstevel@tonic-gate 			hubd->h_port_state[port] &= ~HUBD_CHILD_ZAP;
13597c478bd9Sstevel@tonic-gate 		}
13607c478bd9Sstevel@tonic-gate 	}
13617c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
13627c478bd9Sstevel@tonic-gate 
13637c478bd9Sstevel@tonic-gate 	ndi_devi_exit(dip, circ);
13647c478bd9Sstevel@tonic-gate 
13657c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
13667c478bd9Sstevel@tonic-gate 	    "hubd_bus_unconfig: rval=%d", rval);
13677c478bd9Sstevel@tonic-gate 
13687c478bd9Sstevel@tonic-gate 	return (rval);
13697c478bd9Sstevel@tonic-gate }
13707c478bd9Sstevel@tonic-gate 
13717c478bd9Sstevel@tonic-gate 
13727c478bd9Sstevel@tonic-gate /* bus_power entry point */
13737c478bd9Sstevel@tonic-gate static int
13747c478bd9Sstevel@tonic-gate hubd_bus_power(dev_info_t *dip, void *impl_arg, pm_bus_power_op_t op,
13757c478bd9Sstevel@tonic-gate     void *arg, void *result)
13767c478bd9Sstevel@tonic-gate {
13777c478bd9Sstevel@tonic-gate 	hubd_t		*hubd;
13787c478bd9Sstevel@tonic-gate 	int		rval, pwrup_res;
13797c478bd9Sstevel@tonic-gate 	usb_port_t	port;
13807c478bd9Sstevel@tonic-gate 	int		retval = DDI_FAILURE;
13817c478bd9Sstevel@tonic-gate 	pm_bp_child_pwrchg_t	*bpc;
13827c478bd9Sstevel@tonic-gate 	pm_bp_nexus_pwrup_t	bpn;
13837c478bd9Sstevel@tonic-gate 
13847c478bd9Sstevel@tonic-gate 	hubd = hubd_get_soft_state(dip);
13857c478bd9Sstevel@tonic-gate 
13867c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubd->h_log_handle,
13877c478bd9Sstevel@tonic-gate 	    "hubd_bus_power: dip=%p, impl_arg=%p, power_op=%d, arg=%p, "
13887c478bd9Sstevel@tonic-gate 	    "result=%d\n", dip, impl_arg, op, arg, *(int *)result);
13897c478bd9Sstevel@tonic-gate 
13907c478bd9Sstevel@tonic-gate 	bpc = (pm_bp_child_pwrchg_t *)arg;
13917c478bd9Sstevel@tonic-gate 
13927c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
13937c478bd9Sstevel@tonic-gate 	hubd->h_bus_pwr++;
13947c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
13957c478bd9Sstevel@tonic-gate 
13967c478bd9Sstevel@tonic-gate 	switch (op) {
13977c478bd9Sstevel@tonic-gate 	case BUS_POWER_PRE_NOTIFICATION:
13987c478bd9Sstevel@tonic-gate 		port = hubd_child_dip2port(hubd, bpc->bpc_dip);
13997c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle,
14007c478bd9Sstevel@tonic-gate 		    "hubd_bus_power: BUS_POWER_PRE_NOTIFICATION, port=%d",
14017c478bd9Sstevel@tonic-gate 		    port);
14027c478bd9Sstevel@tonic-gate 
14037c478bd9Sstevel@tonic-gate 		/* go to full power if we are powered down */
14047c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
14057c478bd9Sstevel@tonic-gate 
14067c478bd9Sstevel@tonic-gate 		/*
14077c478bd9Sstevel@tonic-gate 		 * If this case completes normally, idle will be in
14087c478bd9Sstevel@tonic-gate 		 * hubd_bus_power / BUS_POWER_POST_NOTIFICATION
14097c478bd9Sstevel@tonic-gate 		 */
14107c478bd9Sstevel@tonic-gate 		hubd_pm_busy_component(hubd, dip, 0);
14117c478bd9Sstevel@tonic-gate 
14127c478bd9Sstevel@tonic-gate 		/*
14137c478bd9Sstevel@tonic-gate 		 * raise power only if we have created the components
14147c478bd9Sstevel@tonic-gate 		 * and are currently in low power
14157c478bd9Sstevel@tonic-gate 		 */
14167c478bd9Sstevel@tonic-gate 		if ((hubd->h_dev_state == USB_DEV_PWRED_DOWN) &&
14177c478bd9Sstevel@tonic-gate 		    hubd->h_hubpm->hubp_wakeup_enabled) {
14187c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
14197c478bd9Sstevel@tonic-gate 
14207c478bd9Sstevel@tonic-gate 			bpn.bpn_comp = 0;
14217c478bd9Sstevel@tonic-gate 			bpn.bpn_dip = dip;
14227c478bd9Sstevel@tonic-gate 			bpn.bpn_level = USB_DEV_OS_FULL_PWR;
14237c478bd9Sstevel@tonic-gate 			bpn.bpn_private = bpc->bpc_private;
14247c478bd9Sstevel@tonic-gate 
14257c478bd9Sstevel@tonic-gate 			rval = pm_busop_bus_power(dip, impl_arg,
14267c478bd9Sstevel@tonic-gate 			    BUS_POWER_NEXUS_PWRUP, (void *)&bpn,
14277c478bd9Sstevel@tonic-gate 			    (void *)&pwrup_res);
14287c478bd9Sstevel@tonic-gate 
14297c478bd9Sstevel@tonic-gate 			if (rval != DDI_SUCCESS || pwrup_res != DDI_SUCCESS) {
14307c478bd9Sstevel@tonic-gate 				mutex_enter(HUBD_MUTEX(hubd));
14317c478bd9Sstevel@tonic-gate 				hubd_pm_idle_component(hubd, dip, 0);
14327c478bd9Sstevel@tonic-gate 				mutex_exit(HUBD_MUTEX(hubd));
14337c478bd9Sstevel@tonic-gate 
14347c478bd9Sstevel@tonic-gate 				break;
14357c478bd9Sstevel@tonic-gate 			}
14367c478bd9Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
14377c478bd9Sstevel@tonic-gate 		}
14387c478bd9Sstevel@tonic-gate 
14397c478bd9Sstevel@tonic-gate 		/* indicate that child is changing power level */
14407c478bd9Sstevel@tonic-gate 		hubd->h_port_state[port] |= HUBD_CHILD_PWRLVL_CHNG;
14417c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
14427c478bd9Sstevel@tonic-gate 
14437c478bd9Sstevel@tonic-gate 		if ((bpc->bpc_olevel == 0) &&
14447c478bd9Sstevel@tonic-gate 		    (bpc->bpc_nlevel > bpc->bpc_olevel)) {
14457c478bd9Sstevel@tonic-gate 			/*
14467c478bd9Sstevel@tonic-gate 			 * this child is transitioning from power off
14477c478bd9Sstevel@tonic-gate 			 * to power on state - resume port
14487c478bd9Sstevel@tonic-gate 			 */
14497c478bd9Sstevel@tonic-gate 			rval = hubd_resume_port(hubd, port);
14507c478bd9Sstevel@tonic-gate 			if (rval == USB_SUCCESS) {
14517c478bd9Sstevel@tonic-gate 				retval = DDI_SUCCESS;
14527c478bd9Sstevel@tonic-gate 			} else {
14537c478bd9Sstevel@tonic-gate 				/* reset this flag on failure */
14547c478bd9Sstevel@tonic-gate 				mutex_enter(HUBD_MUTEX(hubd));
14557c478bd9Sstevel@tonic-gate 				hubd->h_port_state[port] &=
14567c478bd9Sstevel@tonic-gate 				    ~HUBD_CHILD_PWRLVL_CHNG;
14577c478bd9Sstevel@tonic-gate 				hubd_pm_idle_component(hubd, dip, 0);
14587c478bd9Sstevel@tonic-gate 				mutex_exit(HUBD_MUTEX(hubd));
14597c478bd9Sstevel@tonic-gate 			}
14607c478bd9Sstevel@tonic-gate 		} else {
14617c478bd9Sstevel@tonic-gate 			retval = DDI_SUCCESS;
14627c478bd9Sstevel@tonic-gate 		}
14637c478bd9Sstevel@tonic-gate 
14647c478bd9Sstevel@tonic-gate 		break;
14657c478bd9Sstevel@tonic-gate 	case BUS_POWER_POST_NOTIFICATION:
14667c478bd9Sstevel@tonic-gate 		port = hubd_child_dip2port(hubd, bpc->bpc_dip);
14677c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle,
14687c478bd9Sstevel@tonic-gate 		    "hubd_bus_power: BUS_POWER_POST_NOTIFICATION, port=%d",
14697c478bd9Sstevel@tonic-gate 		    port);
14707c478bd9Sstevel@tonic-gate 
14717c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
14727c478bd9Sstevel@tonic-gate 		hubd->h_port_state[port] &= ~HUBD_CHILD_PWRLVL_CHNG;
14737c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
14747c478bd9Sstevel@tonic-gate 
14757c478bd9Sstevel@tonic-gate 		/* record child's pwr and suspend port if required */
14767c478bd9Sstevel@tonic-gate 		rval = hubd_post_power(hubd, port, bpc, *(int *)result);
14777c478bd9Sstevel@tonic-gate 		if (rval == USB_SUCCESS) {
14787c478bd9Sstevel@tonic-gate 
14797c478bd9Sstevel@tonic-gate 			retval = DDI_SUCCESS;
14807c478bd9Sstevel@tonic-gate 		}
14817c478bd9Sstevel@tonic-gate 
14827c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
14837c478bd9Sstevel@tonic-gate 
14847c478bd9Sstevel@tonic-gate 		/*
14857c478bd9Sstevel@tonic-gate 		 * Matching idle for the busy in
14867c478bd9Sstevel@tonic-gate 		 * hubd_bus_power / BUS_POWER_PRE_NOTIFICATION
14877c478bd9Sstevel@tonic-gate 		 */
14887c478bd9Sstevel@tonic-gate 		hubd_pm_idle_component(hubd, dip, 0);
14897c478bd9Sstevel@tonic-gate 
14907c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
14917c478bd9Sstevel@tonic-gate 
14927c478bd9Sstevel@tonic-gate 		break;
14937c478bd9Sstevel@tonic-gate 	default:
14947c478bd9Sstevel@tonic-gate 		retval = pm_busop_bus_power(dip, impl_arg, op, arg, result);
14957c478bd9Sstevel@tonic-gate 
14967c478bd9Sstevel@tonic-gate 		break;
14977c478bd9Sstevel@tonic-gate 	}
14987c478bd9Sstevel@tonic-gate 
14997c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
15007c478bd9Sstevel@tonic-gate 	hubd->h_bus_pwr--;
15017c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
15027c478bd9Sstevel@tonic-gate 
15037c478bd9Sstevel@tonic-gate 	return (retval);
15047c478bd9Sstevel@tonic-gate }
15057c478bd9Sstevel@tonic-gate 
15067c478bd9Sstevel@tonic-gate 
15077c478bd9Sstevel@tonic-gate /*
15087c478bd9Sstevel@tonic-gate  * functions to handle power transition for OS levels 0 -> 3
15097c478bd9Sstevel@tonic-gate  */
15107c478bd9Sstevel@tonic-gate static int
15117c478bd9Sstevel@tonic-gate hubd_pwrlvl0(hubd_t *hubd)
15127c478bd9Sstevel@tonic-gate {
15137c478bd9Sstevel@tonic-gate 	hub_power_t	*hubpm;
15147c478bd9Sstevel@tonic-gate 
15157c478bd9Sstevel@tonic-gate 	/* We can't power down if hotplug thread is running */
15167c478bd9Sstevel@tonic-gate 	if (hubd->h_hotplug_thread || hubd->h_hubpm->hubp_busy_pm ||
15177c478bd9Sstevel@tonic-gate 	    (hubd_can_suspend(hubd) == USB_FAILURE)) {
15187c478bd9Sstevel@tonic-gate 
15197c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
15207c478bd9Sstevel@tonic-gate 	}
15217c478bd9Sstevel@tonic-gate 
15227c478bd9Sstevel@tonic-gate 	switch (hubd->h_dev_state) {
15237c478bd9Sstevel@tonic-gate 	case USB_DEV_ONLINE:
15247c478bd9Sstevel@tonic-gate 		hubpm = hubd->h_hubpm;
15257c478bd9Sstevel@tonic-gate 
15267c478bd9Sstevel@tonic-gate 		/*
15277c478bd9Sstevel@tonic-gate 		 * To avoid race with bus_power pre_notify on check over
15287c478bd9Sstevel@tonic-gate 		 * dev_state, we need to correctly set the dev state
15297c478bd9Sstevel@tonic-gate 		 * before the mutex is dropped in stop polling.
15307c478bd9Sstevel@tonic-gate 		 */
15317c478bd9Sstevel@tonic-gate 		hubd->h_dev_state = USB_DEV_PWRED_DOWN;
15327c478bd9Sstevel@tonic-gate 		hubpm->hubp_current_power = USB_DEV_OS_PWR_OFF;
15337c478bd9Sstevel@tonic-gate 
15347c478bd9Sstevel@tonic-gate 		/*
15357c478bd9Sstevel@tonic-gate 		 * if we are the root hub, do not stop polling
15367c478bd9Sstevel@tonic-gate 		 * otherwise, we will never see a resume
15377c478bd9Sstevel@tonic-gate 		 */
15387c478bd9Sstevel@tonic-gate 		if (usba_is_root_hub(hubd->h_dip)) {
15397c478bd9Sstevel@tonic-gate 			/* place holder to implement Global Suspend */
15407c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
15417c478bd9Sstevel@tonic-gate 			    "Global Suspend: Not Yet Implemented");
15427c478bd9Sstevel@tonic-gate 		} else {
15437c478bd9Sstevel@tonic-gate 			hubd_stop_polling(hubd);
15447c478bd9Sstevel@tonic-gate 		}
15457c478bd9Sstevel@tonic-gate 
15467c478bd9Sstevel@tonic-gate 		/* Issue USB D3 command to the device here */
15477c478bd9Sstevel@tonic-gate 		(void) usb_set_device_pwrlvl3(hubd->h_dip);
15487c478bd9Sstevel@tonic-gate 
15497c478bd9Sstevel@tonic-gate 		break;
15507c478bd9Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
15517c478bd9Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
15527c478bd9Sstevel@tonic-gate 	case USB_DEV_PWRED_DOWN:
15537c478bd9Sstevel@tonic-gate 	default:
15547c478bd9Sstevel@tonic-gate 
15557c478bd9Sstevel@tonic-gate 		break;
15567c478bd9Sstevel@tonic-gate 	}
15577c478bd9Sstevel@tonic-gate 
15587c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
15597c478bd9Sstevel@tonic-gate }
15607c478bd9Sstevel@tonic-gate 
15617c478bd9Sstevel@tonic-gate 
15627c478bd9Sstevel@tonic-gate /* ARGSUSED */
15637c478bd9Sstevel@tonic-gate static int
15647c478bd9Sstevel@tonic-gate hubd_pwrlvl1(hubd_t *hubd)
15657c478bd9Sstevel@tonic-gate {
15667c478bd9Sstevel@tonic-gate 	/* Issue USB D2 command to the device here */
15677c478bd9Sstevel@tonic-gate 	(void) usb_set_device_pwrlvl2(hubd->h_dip);
15687c478bd9Sstevel@tonic-gate 
15697c478bd9Sstevel@tonic-gate 	return (USB_FAILURE);
15707c478bd9Sstevel@tonic-gate }
15717c478bd9Sstevel@tonic-gate 
15727c478bd9Sstevel@tonic-gate 
15737c478bd9Sstevel@tonic-gate /* ARGSUSED */
15747c478bd9Sstevel@tonic-gate static int
15757c478bd9Sstevel@tonic-gate hubd_pwrlvl2(hubd_t *hubd)
15767c478bd9Sstevel@tonic-gate {
15777c478bd9Sstevel@tonic-gate 	/* Issue USB D1 command to the device here */
15787c478bd9Sstevel@tonic-gate 	(void) usb_set_device_pwrlvl1(hubd->h_dip);
15797c478bd9Sstevel@tonic-gate 
15807c478bd9Sstevel@tonic-gate 	return (USB_FAILURE);
15817c478bd9Sstevel@tonic-gate }
15827c478bd9Sstevel@tonic-gate 
15837c478bd9Sstevel@tonic-gate 
15847c478bd9Sstevel@tonic-gate static int
15857c478bd9Sstevel@tonic-gate hubd_pwrlvl3(hubd_t *hubd)
15867c478bd9Sstevel@tonic-gate {
15877c478bd9Sstevel@tonic-gate 	hub_power_t	*hubpm;
15887c478bd9Sstevel@tonic-gate 	int		rval;
15897c478bd9Sstevel@tonic-gate 
15907c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, "hubd_pwrlvl3");
15917c478bd9Sstevel@tonic-gate 
15927c478bd9Sstevel@tonic-gate 	hubpm = hubd->h_hubpm;
15937c478bd9Sstevel@tonic-gate 	switch (hubd->h_dev_state) {
15947c478bd9Sstevel@tonic-gate 	case USB_DEV_PWRED_DOWN:
15957c478bd9Sstevel@tonic-gate 		ASSERT(hubpm->hubp_current_power == USB_DEV_OS_PWR_OFF);
15967c478bd9Sstevel@tonic-gate 		if (usba_is_root_hub(hubd->h_dip)) {
15977c478bd9Sstevel@tonic-gate 			/* implement global resume here */
15987c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_PM,
15997c478bd9Sstevel@tonic-gate 			    hubd->h_log_handle,
16007c478bd9Sstevel@tonic-gate 			    "Global Resume: Not Yet Implemented");
16017c478bd9Sstevel@tonic-gate 		}
16027c478bd9Sstevel@tonic-gate 		/* Issue USB D0 command to the device here */
16037c478bd9Sstevel@tonic-gate 		rval = usb_set_device_pwrlvl0(hubd->h_dip);
16047c478bd9Sstevel@tonic-gate 		ASSERT(rval == USB_SUCCESS);
16057c478bd9Sstevel@tonic-gate 		hubd->h_dev_state = USB_DEV_ONLINE;
16067c478bd9Sstevel@tonic-gate 		hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR;
16077c478bd9Sstevel@tonic-gate 		hubpm->hubp_time_at_full_power = ddi_get_time();
16087c478bd9Sstevel@tonic-gate 		hubd_start_polling(hubd, 0);
16097c478bd9Sstevel@tonic-gate 
16107c478bd9Sstevel@tonic-gate 		/* FALLTHRU */
16117c478bd9Sstevel@tonic-gate 	case USB_DEV_ONLINE:
16127c478bd9Sstevel@tonic-gate 		/* we are already in full power */
16137c478bd9Sstevel@tonic-gate 
16147c478bd9Sstevel@tonic-gate 		/* FALLTHRU */
16157c478bd9Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
16167c478bd9Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
16177c478bd9Sstevel@tonic-gate 		/*
16187c478bd9Sstevel@tonic-gate 		 * PM framework tries to put you in full power
16197c478bd9Sstevel@tonic-gate 		 * during system shutdown. If we are disconnected
16207c478bd9Sstevel@tonic-gate 		 * return success. Also, we should not change state
16217c478bd9Sstevel@tonic-gate 		 * when we are disconnected or suspended or about to
16227c478bd9Sstevel@tonic-gate 		 * transition to that state
16237c478bd9Sstevel@tonic-gate 		 */
16247c478bd9Sstevel@tonic-gate 
16257c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
16267c478bd9Sstevel@tonic-gate 	default:
16277c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
16287c478bd9Sstevel@tonic-gate 		    "hubd_pwrlvl3: Illegal dev_state=%d", hubd->h_dev_state);
16297c478bd9Sstevel@tonic-gate 
16307c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
16317c478bd9Sstevel@tonic-gate 	}
16327c478bd9Sstevel@tonic-gate }
16337c478bd9Sstevel@tonic-gate 
16347c478bd9Sstevel@tonic-gate 
16357c478bd9Sstevel@tonic-gate /* power entry point */
16367c478bd9Sstevel@tonic-gate /* ARGSUSED */
16377c478bd9Sstevel@tonic-gate int
16387c478bd9Sstevel@tonic-gate usba_hubdi_power(dev_info_t *dip, int comp, int level)
16397c478bd9Sstevel@tonic-gate {
16407c478bd9Sstevel@tonic-gate 	hubd_t		*hubd;
16417c478bd9Sstevel@tonic-gate 	hub_power_t	*hubpm;
16427c478bd9Sstevel@tonic-gate 	int		retval;
16437c478bd9Sstevel@tonic-gate 	int		circ;
16447c478bd9Sstevel@tonic-gate 
16457c478bd9Sstevel@tonic-gate 	hubd = hubd_get_soft_state(dip);
16467c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle,
16477c478bd9Sstevel@tonic-gate 	    "usba_hubdi_power: level=%d", level);
16487c478bd9Sstevel@tonic-gate 
16497c478bd9Sstevel@tonic-gate 	ndi_devi_enter(dip, &circ);
16507c478bd9Sstevel@tonic-gate 
16517c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
16527c478bd9Sstevel@tonic-gate 	hubpm = hubd->h_hubpm;
16537c478bd9Sstevel@tonic-gate 
16547c478bd9Sstevel@tonic-gate 	/* check if we are transitioning to a legal power level */
16557c478bd9Sstevel@tonic-gate 	if (USB_DEV_PWRSTATE_OK(hubpm->hubp_pwr_states, level)) {
16567c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_HUBDI, hubd->h_log_handle,
16577c478bd9Sstevel@tonic-gate 		    "usba_hubdi_power: illegal power level=%d "
16587c478bd9Sstevel@tonic-gate 		    "hubp_pwr_states=0x%x", level, hubpm->hubp_pwr_states);
16597c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
16607c478bd9Sstevel@tonic-gate 
16617c478bd9Sstevel@tonic-gate 		ndi_devi_exit(dip, circ);
16627c478bd9Sstevel@tonic-gate 
16637c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
16647c478bd9Sstevel@tonic-gate 	}
16657c478bd9Sstevel@tonic-gate 
16667c478bd9Sstevel@tonic-gate 	switch (level) {
16677c478bd9Sstevel@tonic-gate 	case USB_DEV_OS_PWR_OFF:
16687c478bd9Sstevel@tonic-gate 		retval = hubd_pwrlvl0(hubd);
16697c478bd9Sstevel@tonic-gate 
16707c478bd9Sstevel@tonic-gate 		break;
16717c478bd9Sstevel@tonic-gate 	case USB_DEV_OS_PWR_1:
16727c478bd9Sstevel@tonic-gate 		retval = hubd_pwrlvl1(hubd);
16737c478bd9Sstevel@tonic-gate 
16747c478bd9Sstevel@tonic-gate 		break;
16757c478bd9Sstevel@tonic-gate 	case USB_DEV_OS_PWR_2:
16767c478bd9Sstevel@tonic-gate 		retval = hubd_pwrlvl2(hubd);
16777c478bd9Sstevel@tonic-gate 
16787c478bd9Sstevel@tonic-gate 		break;
16797c478bd9Sstevel@tonic-gate 	case USB_DEV_OS_FULL_PWR:
16807c478bd9Sstevel@tonic-gate 		retval = hubd_pwrlvl3(hubd);
16817c478bd9Sstevel@tonic-gate 
16827c478bd9Sstevel@tonic-gate 		break;
16837c478bd9Sstevel@tonic-gate 	}
16847c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
16857c478bd9Sstevel@tonic-gate 
16867c478bd9Sstevel@tonic-gate 	ndi_devi_exit(dip, circ);
16877c478bd9Sstevel@tonic-gate 
16887c478bd9Sstevel@tonic-gate 	return ((retval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
16897c478bd9Sstevel@tonic-gate }
16907c478bd9Sstevel@tonic-gate 
16917c478bd9Sstevel@tonic-gate 
16927c478bd9Sstevel@tonic-gate /* power entry point for the root hub */
16937c478bd9Sstevel@tonic-gate int
16947c478bd9Sstevel@tonic-gate usba_hubdi_root_hub_power(dev_info_t *dip, int comp, int level)
16957c478bd9Sstevel@tonic-gate {
16967c478bd9Sstevel@tonic-gate 	return (usba_hubdi_power(dip, comp, level));
16977c478bd9Sstevel@tonic-gate }
16987c478bd9Sstevel@tonic-gate 
16997c478bd9Sstevel@tonic-gate 
17007c478bd9Sstevel@tonic-gate /*
17017c478bd9Sstevel@tonic-gate  * standard driver entry points support code
17027c478bd9Sstevel@tonic-gate  */
17037c478bd9Sstevel@tonic-gate int
17047c478bd9Sstevel@tonic-gate usba_hubdi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
17057c478bd9Sstevel@tonic-gate {
17067c478bd9Sstevel@tonic-gate 	int			instance = ddi_get_instance(dip);
17077c478bd9Sstevel@tonic-gate 	hubd_t			*hubd = NULL;
17087c478bd9Sstevel@tonic-gate 	int			i, rval;
17097c478bd9Sstevel@tonic-gate 	int			minor;
17107c478bd9Sstevel@tonic-gate 	char			*log_name = NULL;
17117c478bd9Sstevel@tonic-gate 	const char		*root_hub_drvname;
17127c478bd9Sstevel@tonic-gate 	usb_ep_data_t		*ep_data;
17137c478bd9Sstevel@tonic-gate 	usba_device_t		*child_ud = NULL;
17147c478bd9Sstevel@tonic-gate 	usb_dev_descr_t		*usb_dev_descr;
17157c478bd9Sstevel@tonic-gate 	usb_port_status_t	parent_port_status, child_port_status;
17167c478bd9Sstevel@tonic-gate 
17177c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubdi_log_handle,
17187c478bd9Sstevel@tonic-gate 		"hubd_attach instance %d, cmd=0x%x", instance, cmd);
17197c478bd9Sstevel@tonic-gate 
17207c478bd9Sstevel@tonic-gate 	switch (cmd) {
17217c478bd9Sstevel@tonic-gate 	case DDI_ATTACH:
17227c478bd9Sstevel@tonic-gate 
17237c478bd9Sstevel@tonic-gate 		break;
17247c478bd9Sstevel@tonic-gate 	case DDI_RESUME:
17257c478bd9Sstevel@tonic-gate 		hubd_cpr_resume(dip);
17267c478bd9Sstevel@tonic-gate 
17277c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
17287c478bd9Sstevel@tonic-gate 	default:
17297c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
17307c478bd9Sstevel@tonic-gate 	}
17317c478bd9Sstevel@tonic-gate 
17327c478bd9Sstevel@tonic-gate 	/*
17337c478bd9Sstevel@tonic-gate 	 * Allocate softc information.
17347c478bd9Sstevel@tonic-gate 	 */
17357c478bd9Sstevel@tonic-gate 	if (usba_is_root_hub(dip)) {
17367c478bd9Sstevel@tonic-gate 		/* soft state has already been allocated */
17377c478bd9Sstevel@tonic-gate 		hubd = hubd_get_soft_state(dip);
17387c478bd9Sstevel@tonic-gate 		minor = HUBD_IS_ROOT_HUB;
17397c478bd9Sstevel@tonic-gate 
17407c478bd9Sstevel@tonic-gate 		/* generate readable labels for different root hubs */
17417c478bd9Sstevel@tonic-gate 		root_hub_drvname = ddi_driver_name(dip);
17427c478bd9Sstevel@tonic-gate 		if (strcmp(root_hub_drvname, "ehci") == 0) {
17437c478bd9Sstevel@tonic-gate 			log_name = "eusb";
17447c478bd9Sstevel@tonic-gate 		} else if (strcmp(root_hub_drvname, "uhci") == 0) {
17457c478bd9Sstevel@tonic-gate 			log_name = "uusb";
17467c478bd9Sstevel@tonic-gate 		} else {
17477c478bd9Sstevel@tonic-gate 			/* std. for ohci */
17487c478bd9Sstevel@tonic-gate 			log_name = "usb";
17497c478bd9Sstevel@tonic-gate 		}
17507c478bd9Sstevel@tonic-gate 	} else {
17517c478bd9Sstevel@tonic-gate 		rval = ddi_soft_state_zalloc(hubd_statep, instance);
17527c478bd9Sstevel@tonic-gate 		minor = 0;
17537c478bd9Sstevel@tonic-gate 
17547c478bd9Sstevel@tonic-gate 		if (rval != DDI_SUCCESS) {
17557c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
17567c478bd9Sstevel@tonic-gate 			    "cannot allocate soft state (%d)", instance);
17577c478bd9Sstevel@tonic-gate 			goto fail;
17587c478bd9Sstevel@tonic-gate 		}
17597c478bd9Sstevel@tonic-gate 
17607c478bd9Sstevel@tonic-gate 		hubd = hubd_get_soft_state(dip);
17617c478bd9Sstevel@tonic-gate 		if (hubd == NULL) {
17627c478bd9Sstevel@tonic-gate 			goto fail;
17637c478bd9Sstevel@tonic-gate 		}
17647c478bd9Sstevel@tonic-gate 	}
17657c478bd9Sstevel@tonic-gate 
17667c478bd9Sstevel@tonic-gate 	hubd->h_log_handle = usb_alloc_log_hdl(dip, log_name, &hubd_errlevel,
17677c478bd9Sstevel@tonic-gate 				&hubd_errmask, &hubd_instance_debug, 0);
17687c478bd9Sstevel@tonic-gate 
17697c478bd9Sstevel@tonic-gate 	hubd->h_usba_device	= child_ud = usba_get_usba_device(dip);
17707c478bd9Sstevel@tonic-gate 	hubd->h_dip		= dip;
17717c478bd9Sstevel@tonic-gate 	hubd->h_instance	= instance;
17727c478bd9Sstevel@tonic-gate 
17737c478bd9Sstevel@tonic-gate 	mutex_enter(&child_ud->usb_mutex);
17747c478bd9Sstevel@tonic-gate 	child_port_status = child_ud->usb_port_status;
17757c478bd9Sstevel@tonic-gate 	usb_dev_descr = child_ud->usb_dev_descr;
17767c478bd9Sstevel@tonic-gate 	parent_port_status = (child_ud->usb_hs_hub_usba_dev) ?
17777c478bd9Sstevel@tonic-gate 	    child_ud->usb_hs_hub_usba_dev->usb_port_status : 0;
17787c478bd9Sstevel@tonic-gate 	mutex_exit(&child_ud->usb_mutex);
17797c478bd9Sstevel@tonic-gate 
17807c478bd9Sstevel@tonic-gate 	if ((child_port_status == USBA_FULL_SPEED_DEV) &&
17817c478bd9Sstevel@tonic-gate 	    (parent_port_status == USBA_HIGH_SPEED_DEV) &&
17827c478bd9Sstevel@tonic-gate 	    (usb_dev_descr->bcdUSB == 0x100)) {
17837c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L0(DPRINT_MASK_ATTA, hubd->h_log_handle,
17847c478bd9Sstevel@tonic-gate 		    "Use of a USB1.0 hub behind a high speed port may "
17857c478bd9Sstevel@tonic-gate 		    "cause unexpected failures");
17867c478bd9Sstevel@tonic-gate 	}
17877c478bd9Sstevel@tonic-gate 
17887c478bd9Sstevel@tonic-gate 	hubd->h_pipe_policy.pp_max_async_reqs = 1;
17897c478bd9Sstevel@tonic-gate 
17907c478bd9Sstevel@tonic-gate 	/* register with USBA as client driver */
17917c478bd9Sstevel@tonic-gate 	if (usb_client_attach(dip, USBDRV_VERSION, 0) != USB_SUCCESS) {
17927c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
17937c478bd9Sstevel@tonic-gate 		    "client attach failed");
17947c478bd9Sstevel@tonic-gate 
17957c478bd9Sstevel@tonic-gate 		goto fail;
17967c478bd9Sstevel@tonic-gate 	}
17977c478bd9Sstevel@tonic-gate 
17987c478bd9Sstevel@tonic-gate 	if (usb_get_dev_data(dip, &hubd->h_dev_data,
17997c478bd9Sstevel@tonic-gate 	    USB_PARSE_LVL_IF, 0) != USB_SUCCESS) {
18007c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
18017c478bd9Sstevel@tonic-gate 		    "cannot get dev_data");
18027c478bd9Sstevel@tonic-gate 
18037c478bd9Sstevel@tonic-gate 		goto fail;
18047c478bd9Sstevel@tonic-gate 	}
18057c478bd9Sstevel@tonic-gate 
18067c478bd9Sstevel@tonic-gate 	if ((ep_data = usb_lookup_ep_data(dip, hubd->h_dev_data,
18077c478bd9Sstevel@tonic-gate 	    hubd->h_dev_data->dev_curr_if, 0, 0,
18087c478bd9Sstevel@tonic-gate 	    (uint_t)USB_EP_ATTR_INTR, (uint_t)USB_EP_DIR_IN)) == NULL) {
18097c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
18107c478bd9Sstevel@tonic-gate 		    "no interrupt IN endpoint found");
18117c478bd9Sstevel@tonic-gate 
18127c478bd9Sstevel@tonic-gate 		goto fail;
18137c478bd9Sstevel@tonic-gate 	}
18147c478bd9Sstevel@tonic-gate 
18157c478bd9Sstevel@tonic-gate 	hubd->h_ep1_descr = ep_data->ep_descr;
18167c478bd9Sstevel@tonic-gate 	hubd->h_default_pipe = hubd->h_dev_data->dev_default_ph;
18177c478bd9Sstevel@tonic-gate 
18187c478bd9Sstevel@tonic-gate 	mutex_init(HUBD_MUTEX(hubd), NULL, MUTEX_DRIVER,
18197c478bd9Sstevel@tonic-gate 				hubd->h_dev_data->dev_iblock_cookie);
18207c478bd9Sstevel@tonic-gate 	cv_init(&hubd->h_cv_reset_port, NULL, CV_DRIVER, NULL);
18217c478bd9Sstevel@tonic-gate 
18227c478bd9Sstevel@tonic-gate 	hubd->h_init_state |= HUBD_LOCKS_DONE;
18237c478bd9Sstevel@tonic-gate 
18247c478bd9Sstevel@tonic-gate 	usb_free_descr_tree(dip, hubd->h_dev_data);
18257c478bd9Sstevel@tonic-gate 
18267c478bd9Sstevel@tonic-gate 	/*
18277c478bd9Sstevel@tonic-gate 	 * register this hub instance with usba
18287c478bd9Sstevel@tonic-gate 	 */
18297c478bd9Sstevel@tonic-gate 	rval = usba_hubdi_register(dip, 0);
18307c478bd9Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
1831*d291d9f2Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
18327c478bd9Sstevel@tonic-gate 		    "usba_hubdi_register failed");
18337c478bd9Sstevel@tonic-gate 		goto fail;
18347c478bd9Sstevel@tonic-gate 	}
18357c478bd9Sstevel@tonic-gate 
18367c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
18377c478bd9Sstevel@tonic-gate 	hubd->h_init_state |= HUBD_HUBDI_REGISTERED;
18387c478bd9Sstevel@tonic-gate 	hubd->h_dev_state = USB_DEV_ONLINE;
18397c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
18407c478bd9Sstevel@tonic-gate 
18417c478bd9Sstevel@tonic-gate 	/* now create components to power manage this device */
18427c478bd9Sstevel@tonic-gate 	hubd_create_pm_components(dip, hubd);
18437c478bd9Sstevel@tonic-gate 
18447c478bd9Sstevel@tonic-gate 	/*
18457c478bd9Sstevel@tonic-gate 	 * Event handling: definition and registration
18467c478bd9Sstevel@tonic-gate 	 *
18477c478bd9Sstevel@tonic-gate 	 * first the  definition:
18487c478bd9Sstevel@tonic-gate 	 * get event handle
18497c478bd9Sstevel@tonic-gate 	 */
18507c478bd9Sstevel@tonic-gate 	(void) ndi_event_alloc_hdl(dip, 0, &hubd->h_ndi_event_hdl, NDI_SLEEP);
18517c478bd9Sstevel@tonic-gate 
18527c478bd9Sstevel@tonic-gate 	/* bind event set to the handle */
18537c478bd9Sstevel@tonic-gate 	if (ndi_event_bind_set(hubd->h_ndi_event_hdl, &hubd_ndi_events,
18547c478bd9Sstevel@tonic-gate 	    NDI_SLEEP)) {
18557c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
18567c478bd9Sstevel@tonic-gate 		    "binding event set failed");
18577c478bd9Sstevel@tonic-gate 
18587c478bd9Sstevel@tonic-gate 		goto fail;
18597c478bd9Sstevel@tonic-gate 	}
18607c478bd9Sstevel@tonic-gate 
18617c478bd9Sstevel@tonic-gate 	/* event registration */
18627c478bd9Sstevel@tonic-gate 	if (hubd_register_events(hubd) != USB_SUCCESS) {
1863*d291d9f2Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
18647c478bd9Sstevel@tonic-gate 		    "hubd_register_events failed");
18657c478bd9Sstevel@tonic-gate 
18667c478bd9Sstevel@tonic-gate 		goto fail;
18677c478bd9Sstevel@tonic-gate 	}
18687c478bd9Sstevel@tonic-gate 
18697c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
18707c478bd9Sstevel@tonic-gate 	hubd->h_init_state |= HUBD_EVENTS_REGISTERED;
18717c478bd9Sstevel@tonic-gate 
18727c478bd9Sstevel@tonic-gate 	/* initialize and create children */
18737c478bd9Sstevel@tonic-gate 	if (hubd_check_ports(hubd) != USB_SUCCESS) {
1874*d291d9f2Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
18757c478bd9Sstevel@tonic-gate 		    "hubd_check_ports failed");
18767c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
18777c478bd9Sstevel@tonic-gate 
18787c478bd9Sstevel@tonic-gate 		goto fail;
18797c478bd9Sstevel@tonic-gate 	}
18807c478bd9Sstevel@tonic-gate 
18817c478bd9Sstevel@tonic-gate 	/*
18827c478bd9Sstevel@tonic-gate 	 * create cfgadm nodes
18837c478bd9Sstevel@tonic-gate 	 */
18847c478bd9Sstevel@tonic-gate 	hubd->h_ancestry_str = (char *)kmem_zalloc(HUBD_APID_NAMELEN, KM_SLEEP);
18857c478bd9Sstevel@tonic-gate 	hubd_get_ancestry_str(hubd);
18867c478bd9Sstevel@tonic-gate 
18877c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
18887c478bd9Sstevel@tonic-gate 	    "#ports=0x%x", hubd->h_hub_descr.bNbrPorts);
18897c478bd9Sstevel@tonic-gate 
18907c478bd9Sstevel@tonic-gate 	for (i = 1; i <= hubd->h_hub_descr.bNbrPorts; i++) {
18917c478bd9Sstevel@tonic-gate 		char ap_name[HUBD_APID_NAMELEN];
18927c478bd9Sstevel@tonic-gate 
18937c478bd9Sstevel@tonic-gate 		(void) snprintf(ap_name, HUBD_APID_NAMELEN, "%s%d",
18947c478bd9Sstevel@tonic-gate 		    hubd->h_ancestry_str, i);
18957c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
18967c478bd9Sstevel@tonic-gate 		    "ap_name=%s", ap_name);
18977c478bd9Sstevel@tonic-gate 
18987c478bd9Sstevel@tonic-gate 		if (ddi_create_minor_node(dip, ap_name, S_IFCHR, instance,
18997c478bd9Sstevel@tonic-gate 		    DDI_NT_USB_ATTACHMENT_POINT, 0) != DDI_SUCCESS) {
1900*d291d9f2Sfrits 			USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
19017c478bd9Sstevel@tonic-gate 			    "cannot create attachment point node (%d)",
19027c478bd9Sstevel@tonic-gate 			    instance);
19037c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
19047c478bd9Sstevel@tonic-gate 
19057c478bd9Sstevel@tonic-gate 			goto fail;
19067c478bd9Sstevel@tonic-gate 		}
19077c478bd9Sstevel@tonic-gate 	}
19087c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
19097c478bd9Sstevel@tonic-gate 
19107c478bd9Sstevel@tonic-gate 	/* create minor nodes */
19117c478bd9Sstevel@tonic-gate 	if (ddi_create_minor_node(dip, "hubd", S_IFCHR,
19127c478bd9Sstevel@tonic-gate 	    instance | minor, DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
19137c478bd9Sstevel@tonic-gate 
1914*d291d9f2Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
19157c478bd9Sstevel@tonic-gate 		    "cannot create devctl minor node (%d)", instance);
19167c478bd9Sstevel@tonic-gate 
19177c478bd9Sstevel@tonic-gate 		goto fail;
19187c478bd9Sstevel@tonic-gate 	}
19197c478bd9Sstevel@tonic-gate 
19207c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
19217c478bd9Sstevel@tonic-gate 	hubd->h_init_state |= HUBD_MINOR_NODE_CREATED;
19227c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
19237c478bd9Sstevel@tonic-gate 
19247c478bd9Sstevel@tonic-gate 	/*
19257c478bd9Sstevel@tonic-gate 	 * host controller driver has already reported this dev
19267c478bd9Sstevel@tonic-gate 	 * if we are the root hub
19277c478bd9Sstevel@tonic-gate 	 */
19287c478bd9Sstevel@tonic-gate 	if (!usba_is_root_hub(dip)) {
19297c478bd9Sstevel@tonic-gate 		ddi_report_dev(dip);
19307c478bd9Sstevel@tonic-gate 	}
19317c478bd9Sstevel@tonic-gate 
19327c478bd9Sstevel@tonic-gate 	/* enable deathrow thread */
19337c478bd9Sstevel@tonic-gate 	hubd->h_cleanup_enabled = B_TRUE;
19347c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
19357c478bd9Sstevel@tonic-gate 	hubd_pm_idle_component(hubd, dip, 0);
19367c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
19377c478bd9Sstevel@tonic-gate 
19387c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
19397c478bd9Sstevel@tonic-gate 
19407c478bd9Sstevel@tonic-gate fail:
19417c478bd9Sstevel@tonic-gate 	{
19427c478bd9Sstevel@tonic-gate 		char *pathname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
19437c478bd9Sstevel@tonic-gate 
19447c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L0(DPRINT_MASK_ATTA, hubdi_log_handle,
19457c478bd9Sstevel@tonic-gate 		    "cannot attach %s", ddi_pathname(dip, pathname));
19467c478bd9Sstevel@tonic-gate 
19477c478bd9Sstevel@tonic-gate 		kmem_free(pathname, MAXPATHLEN);
19487c478bd9Sstevel@tonic-gate 	}
19497c478bd9Sstevel@tonic-gate 
19507c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
19517c478bd9Sstevel@tonic-gate 	hubd_pm_idle_component(hubd, dip, 0);
19527c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
19537c478bd9Sstevel@tonic-gate 
19547c478bd9Sstevel@tonic-gate 	if (hubd) {
19557c478bd9Sstevel@tonic-gate 		rval = hubd_cleanup(dip, hubd);
19567c478bd9Sstevel@tonic-gate 		if (rval != USB_SUCCESS) {
1957*d291d9f2Sfrits 			USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
19587c478bd9Sstevel@tonic-gate 			    "failure to complete cleanup after attach failure");
19597c478bd9Sstevel@tonic-gate 		}
19607c478bd9Sstevel@tonic-gate 	}
19617c478bd9Sstevel@tonic-gate 
19627c478bd9Sstevel@tonic-gate 	return (DDI_FAILURE);
19637c478bd9Sstevel@tonic-gate }
19647c478bd9Sstevel@tonic-gate 
19657c478bd9Sstevel@tonic-gate 
19667c478bd9Sstevel@tonic-gate int
19677c478bd9Sstevel@tonic-gate usba_hubdi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
19687c478bd9Sstevel@tonic-gate {
19697c478bd9Sstevel@tonic-gate 	hubd_t	*hubd = hubd_get_soft_state(dip);
19707c478bd9Sstevel@tonic-gate 	int	rval;
19717c478bd9Sstevel@tonic-gate 
19727c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
19737c478bd9Sstevel@tonic-gate 	    "hubd_detach: cmd=0x%x", cmd);
19747c478bd9Sstevel@tonic-gate 
19757c478bd9Sstevel@tonic-gate 	switch (cmd) {
19767c478bd9Sstevel@tonic-gate 	case DDI_DETACH:
19777c478bd9Sstevel@tonic-gate 		rval = hubd_cleanup(dip, hubd);
19787c478bd9Sstevel@tonic-gate 
19797c478bd9Sstevel@tonic-gate 		return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
19807c478bd9Sstevel@tonic-gate 	case DDI_SUSPEND:
19817c478bd9Sstevel@tonic-gate 		rval = hubd_cpr_suspend(hubd);
19827c478bd9Sstevel@tonic-gate 
19837c478bd9Sstevel@tonic-gate 		return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
19847c478bd9Sstevel@tonic-gate 	default:
19857c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
19867c478bd9Sstevel@tonic-gate 	}
19877c478bd9Sstevel@tonic-gate }
19887c478bd9Sstevel@tonic-gate 
19897c478bd9Sstevel@tonic-gate 
19907c478bd9Sstevel@tonic-gate /*
19917c478bd9Sstevel@tonic-gate  * hubd_setdevaddr
19927c478bd9Sstevel@tonic-gate  *	set the device addrs on this port
19937c478bd9Sstevel@tonic-gate  */
19947c478bd9Sstevel@tonic-gate static int
19957c478bd9Sstevel@tonic-gate hubd_setdevaddr(hubd_t *hubd, usb_port_t port)
19967c478bd9Sstevel@tonic-gate {
19977c478bd9Sstevel@tonic-gate 	int		rval;
19987c478bd9Sstevel@tonic-gate 	usb_cr_t	completion_reason;
19997c478bd9Sstevel@tonic-gate 	usb_cb_flags_t	cb_flags;
20007c478bd9Sstevel@tonic-gate 	usb_pipe_handle_t ph;
20017c478bd9Sstevel@tonic-gate 	dev_info_t	*child_dip = NULL;
20027c478bd9Sstevel@tonic-gate 	uchar_t		address = 0;
20037c478bd9Sstevel@tonic-gate 	usba_device_t	*usba_device;
20047c478bd9Sstevel@tonic-gate 	int		retry = 0;
20057c478bd9Sstevel@tonic-gate 	long		time_delay;
20067c478bd9Sstevel@tonic-gate 
20077c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
20087c478bd9Sstevel@tonic-gate 	    "hubd_setdevaddr: port=%d", port);
20097c478bd9Sstevel@tonic-gate 
20107c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
20117c478bd9Sstevel@tonic-gate 
20127c478bd9Sstevel@tonic-gate 	child_dip = hubd->h_children_dips[port];
20137c478bd9Sstevel@tonic-gate 	address = hubd->h_usba_devices[port]->usb_addr;
20147c478bd9Sstevel@tonic-gate 	usba_device = hubd->h_usba_devices[port];
20157c478bd9Sstevel@tonic-gate 
20167c478bd9Sstevel@tonic-gate 	/* close the default pipe with addr x */
20177c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
20187c478bd9Sstevel@tonic-gate 	ph = usba_get_dflt_pipe_handle(child_dip);
20197c478bd9Sstevel@tonic-gate 	usb_pipe_close(child_dip, ph,
20207c478bd9Sstevel@tonic-gate 	    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
20217c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
20227c478bd9Sstevel@tonic-gate 
20237c478bd9Sstevel@tonic-gate 	/*
20247c478bd9Sstevel@tonic-gate 	 * As this device has been reset, temporarily
20257c478bd9Sstevel@tonic-gate 	 * assign the default address
20267c478bd9Sstevel@tonic-gate 	 */
20277c478bd9Sstevel@tonic-gate 	mutex_enter(&usba_device->usb_mutex);
20287c478bd9Sstevel@tonic-gate 	address = usba_device->usb_addr;
20297c478bd9Sstevel@tonic-gate 	usba_device->usb_addr = USBA_DEFAULT_ADDR;
20307c478bd9Sstevel@tonic-gate 	mutex_exit(&usba_device->usb_mutex);
20317c478bd9Sstevel@tonic-gate 
20327c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
20337c478bd9Sstevel@tonic-gate 
20347c478bd9Sstevel@tonic-gate 	time_delay = drv_usectohz(hubd_device_delay / 20);
20357c478bd9Sstevel@tonic-gate 	for (retry = 0; retry < hubd_retry_enumerate; retry++) {
20367c478bd9Sstevel@tonic-gate 
20377c478bd9Sstevel@tonic-gate 		/* open child's default pipe with USBA_DEFAULT_ADDR */
20387c478bd9Sstevel@tonic-gate 		if (usb_pipe_open(child_dip, NULL, NULL,
20397c478bd9Sstevel@tonic-gate 		    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph) !=
20407c478bd9Sstevel@tonic-gate 		    USB_SUCCESS) {
20417c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
20427c478bd9Sstevel@tonic-gate 			    "hubd_setdevaddr: Unable to open default pipe");
20437c478bd9Sstevel@tonic-gate 
20447c478bd9Sstevel@tonic-gate 			break;
20457c478bd9Sstevel@tonic-gate 		}
20467c478bd9Sstevel@tonic-gate 
20477c478bd9Sstevel@tonic-gate 		/* Set the address of the device */
20487c478bd9Sstevel@tonic-gate 		if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
20497c478bd9Sstevel@tonic-gate 		    USB_DEV_REQ_HOST_TO_DEV,
20507c478bd9Sstevel@tonic-gate 		    USB_REQ_SET_ADDRESS,	/* bRequest */
20517c478bd9Sstevel@tonic-gate 		    address,			/* wValue */
20527c478bd9Sstevel@tonic-gate 		    0,				/* wIndex */
20537c478bd9Sstevel@tonic-gate 		    0,				/* wLength */
20547c478bd9Sstevel@tonic-gate 		    NULL, 0,
20557c478bd9Sstevel@tonic-gate 		    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
20567c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
20577c478bd9Sstevel@tonic-gate 			    "hubd_setdevaddr(%d): rval=%d cr=%d cb_fl=0x%x",
20587c478bd9Sstevel@tonic-gate 			    retry, rval, completion_reason, cb_flags);
20597c478bd9Sstevel@tonic-gate 		}
20607c478bd9Sstevel@tonic-gate 
20617c478bd9Sstevel@tonic-gate 		usb_pipe_close(child_dip, ph,
20627c478bd9Sstevel@tonic-gate 		    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
20637c478bd9Sstevel@tonic-gate 
20647c478bd9Sstevel@tonic-gate 		if (rval == USB_SUCCESS) {
20657c478bd9Sstevel@tonic-gate 
20667c478bd9Sstevel@tonic-gate 			break;
20677c478bd9Sstevel@tonic-gate 		}
20687c478bd9Sstevel@tonic-gate 
20697c478bd9Sstevel@tonic-gate 		delay(time_delay);
20707c478bd9Sstevel@tonic-gate 	}
20717c478bd9Sstevel@tonic-gate 
20727c478bd9Sstevel@tonic-gate 	/* Reset to the old address */
20737c478bd9Sstevel@tonic-gate 	mutex_enter(&usba_device->usb_mutex);
20747c478bd9Sstevel@tonic-gate 	usba_device->usb_addr = address;
20757c478bd9Sstevel@tonic-gate 	mutex_exit(&usba_device->usb_mutex);
20767c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
20777c478bd9Sstevel@tonic-gate 
20787c478bd9Sstevel@tonic-gate 	usba_clear_data_toggle(usba_device);
20797c478bd9Sstevel@tonic-gate 
20807c478bd9Sstevel@tonic-gate 	return (rval);
20817c478bd9Sstevel@tonic-gate }
20827c478bd9Sstevel@tonic-gate 
20837c478bd9Sstevel@tonic-gate 
20847c478bd9Sstevel@tonic-gate /*
20857c478bd9Sstevel@tonic-gate  * hubd_setdevconfig
20867c478bd9Sstevel@tonic-gate  *	set the device addrs on this port
20877c478bd9Sstevel@tonic-gate  */
20887c478bd9Sstevel@tonic-gate static void
20897c478bd9Sstevel@tonic-gate hubd_setdevconfig(hubd_t *hubd, usb_port_t port)
20907c478bd9Sstevel@tonic-gate {
20917c478bd9Sstevel@tonic-gate 	int			rval;
20927c478bd9Sstevel@tonic-gate 	usb_cr_t		completion_reason;
20937c478bd9Sstevel@tonic-gate 	usb_cb_flags_t		cb_flags;
20947c478bd9Sstevel@tonic-gate 	usb_pipe_handle_t	ph;
20957c478bd9Sstevel@tonic-gate 	dev_info_t		*child_dip = NULL;
20967c478bd9Sstevel@tonic-gate 	usba_device_t		*usba_device = NULL;
20977c478bd9Sstevel@tonic-gate 	uint16_t		config_value;
20987c478bd9Sstevel@tonic-gate 
20997c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
21007c478bd9Sstevel@tonic-gate 	    "hubd_setdevconfig: port=%d", port);
21017c478bd9Sstevel@tonic-gate 
21027c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
21037c478bd9Sstevel@tonic-gate 
21047c478bd9Sstevel@tonic-gate 	child_dip = hubd->h_children_dips[port];
21057c478bd9Sstevel@tonic-gate 	usba_device = hubd->h_usba_devices[port];
21067c478bd9Sstevel@tonic-gate 	config_value = hubd->h_usba_devices[port]->usb_cfg_value;
21077c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
21087c478bd9Sstevel@tonic-gate 
21097c478bd9Sstevel@tonic-gate 	/* open the default control pipe */
21107c478bd9Sstevel@tonic-gate 	if ((rval = usb_pipe_open(child_dip, NULL, NULL,
21117c478bd9Sstevel@tonic-gate 	    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) ==
21127c478bd9Sstevel@tonic-gate 	    USB_SUCCESS) {
21137c478bd9Sstevel@tonic-gate 
21147c478bd9Sstevel@tonic-gate 		/* Set the default configuration of the device */
21157c478bd9Sstevel@tonic-gate 		if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
21167c478bd9Sstevel@tonic-gate 		    USB_DEV_REQ_HOST_TO_DEV,
21177c478bd9Sstevel@tonic-gate 		    USB_REQ_SET_CFG,		/* bRequest */
21187c478bd9Sstevel@tonic-gate 		    config_value,		/* wValue */
21197c478bd9Sstevel@tonic-gate 		    0,				/* wIndex */
21207c478bd9Sstevel@tonic-gate 		    0,				/* wLength */
21217c478bd9Sstevel@tonic-gate 		    NULL, 0,
21227c478bd9Sstevel@tonic-gate 		    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
21237c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
21247c478bd9Sstevel@tonic-gate 			    "hubd_setdevconfig: set device config failed: "
21257c478bd9Sstevel@tonic-gate 			    "cr=%d cb_fl=0x%x rval=%d",
21267c478bd9Sstevel@tonic-gate 			    completion_reason, cb_flags, rval);
21277c478bd9Sstevel@tonic-gate 		}
21287c478bd9Sstevel@tonic-gate 		/*
21297c478bd9Sstevel@tonic-gate 		 * After setting the configuration, we make this default
21307c478bd9Sstevel@tonic-gate 		 * control pipe persistent, so that it gets re-opened
21317c478bd9Sstevel@tonic-gate 		 * on posting a connect event
21327c478bd9Sstevel@tonic-gate 		 */
21337c478bd9Sstevel@tonic-gate 		usba_persistent_pipe_close(usba_device);
21347c478bd9Sstevel@tonic-gate 	} else {
21357c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
21367c478bd9Sstevel@tonic-gate 		    "pipe open fails: rval=%d", rval);
21377c478bd9Sstevel@tonic-gate 	}
21387c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
21397c478bd9Sstevel@tonic-gate }
21407c478bd9Sstevel@tonic-gate 
21417c478bd9Sstevel@tonic-gate 
21427c478bd9Sstevel@tonic-gate /*ARGSUSED*/
21437c478bd9Sstevel@tonic-gate static int
21447c478bd9Sstevel@tonic-gate hubd_check_disconnected_ports(dev_info_t *dip, void *arg)
21457c478bd9Sstevel@tonic-gate {
21467c478bd9Sstevel@tonic-gate 	int circ;
21477c478bd9Sstevel@tonic-gate 	usb_port_t port;
21487c478bd9Sstevel@tonic-gate 	hubd_t *hubd;
21497c478bd9Sstevel@tonic-gate 	major_t hub_major = ddi_name_to_major("hubd");
21507c478bd9Sstevel@tonic-gate 
21517c478bd9Sstevel@tonic-gate 	/*
21527c478bd9Sstevel@tonic-gate 	 * make sure dip is a usb hub, major of root hub is HCD
21537c478bd9Sstevel@tonic-gate 	 * major
21547c478bd9Sstevel@tonic-gate 	 */
21557c478bd9Sstevel@tonic-gate 	if (!usba_is_root_hub(dip)) {
21567c478bd9Sstevel@tonic-gate 		if ((ddi_driver_major(dip) != hub_major) ||
21577c478bd9Sstevel@tonic-gate 		    (i_ddi_node_state(dip) < DS_ATTACHED)) {
21587c478bd9Sstevel@tonic-gate 
21597c478bd9Sstevel@tonic-gate 			return (DDI_WALK_PRUNECHILD);
21607c478bd9Sstevel@tonic-gate 		}
21617c478bd9Sstevel@tonic-gate 	}
21627c478bd9Sstevel@tonic-gate 
21637c478bd9Sstevel@tonic-gate 	hubd = hubd_get_soft_state(dip);
21647c478bd9Sstevel@tonic-gate 	if (hubd == NULL) {
21657c478bd9Sstevel@tonic-gate 
21667c478bd9Sstevel@tonic-gate 		return (DDI_WALK_PRUNECHILD);
21677c478bd9Sstevel@tonic-gate 	}
21687c478bd9Sstevel@tonic-gate 
21697c478bd9Sstevel@tonic-gate 	/* walk child list and remove nodes with flag DEVI_DEVICE_REMOVED */
21707c478bd9Sstevel@tonic-gate 	ndi_devi_enter(dip, &circ);
21717c478bd9Sstevel@tonic-gate 
21727c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
21737c478bd9Sstevel@tonic-gate 	for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
21747c478bd9Sstevel@tonic-gate 		dev_info_t *cdip = hubd->h_children_dips[port];
21757c478bd9Sstevel@tonic-gate 
21767c478bd9Sstevel@tonic-gate 		if (cdip == NULL || DEVI_IS_DEVICE_REMOVED(cdip) == 0) {
21777c478bd9Sstevel@tonic-gate 
21787c478bd9Sstevel@tonic-gate 			continue;
21797c478bd9Sstevel@tonic-gate 		}
21807c478bd9Sstevel@tonic-gate 
21817c478bd9Sstevel@tonic-gate 		(void) hubd_delete_child(hubd, port, NDI_DEVI_REMOVE, B_TRUE);
21827c478bd9Sstevel@tonic-gate 	}
21837c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
21847c478bd9Sstevel@tonic-gate 	ndi_devi_exit(dip, circ);
21857c478bd9Sstevel@tonic-gate 
21867c478bd9Sstevel@tonic-gate 	/* skip siblings of root hub */
21877c478bd9Sstevel@tonic-gate 	if (usba_is_root_hub(dip)) {
21887c478bd9Sstevel@tonic-gate 
21897c478bd9Sstevel@tonic-gate 		return (DDI_WALK_PRUNESIB);
21907c478bd9Sstevel@tonic-gate 	}
21917c478bd9Sstevel@tonic-gate 
21927c478bd9Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
21937c478bd9Sstevel@tonic-gate }
21947c478bd9Sstevel@tonic-gate 
21957c478bd9Sstevel@tonic-gate 
21967c478bd9Sstevel@tonic-gate /*
21977c478bd9Sstevel@tonic-gate  * this thread will walk all children under the root hub for this
21987c478bd9Sstevel@tonic-gate  * USB bus instance and attempt to remove them
21997c478bd9Sstevel@tonic-gate  */
22007c478bd9Sstevel@tonic-gate static void
22017c478bd9Sstevel@tonic-gate hubd_root_hub_cleanup_thread(void *arg)
22027c478bd9Sstevel@tonic-gate {
22037c478bd9Sstevel@tonic-gate 	int circ;
22047c478bd9Sstevel@tonic-gate 	hubd_t *root_hubd = (hubd_t *)arg;
22057c478bd9Sstevel@tonic-gate 	dev_info_t *rh_dip = root_hubd->h_dip;
22067c478bd9Sstevel@tonic-gate #ifndef __lock_lint
22077c478bd9Sstevel@tonic-gate 	callb_cpr_t cprinfo;
22087c478bd9Sstevel@tonic-gate 
22097c478bd9Sstevel@tonic-gate 	CALLB_CPR_INIT(&cprinfo, HUBD_MUTEX(root_hubd), callb_generic_cpr,
22107c478bd9Sstevel@tonic-gate 	    "USB root hub");
22117c478bd9Sstevel@tonic-gate #endif
22127c478bd9Sstevel@tonic-gate 
22137c478bd9Sstevel@tonic-gate 	for (;;) {
22147c478bd9Sstevel@tonic-gate 		/* don't race with detach */
22157c478bd9Sstevel@tonic-gate 		ndi_hold_devi(rh_dip);
22167c478bd9Sstevel@tonic-gate 
22177c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(root_hubd));
22187c478bd9Sstevel@tonic-gate 		root_hubd->h_cleanup_needed = 0;
22197c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(root_hubd));
22207c478bd9Sstevel@tonic-gate 
22217c478bd9Sstevel@tonic-gate 		(void) devfs_clean(rh_dip, NULL, 0);
22227c478bd9Sstevel@tonic-gate 
22237c478bd9Sstevel@tonic-gate 		ndi_devi_enter(ddi_get_parent(rh_dip), &circ);
22247c478bd9Sstevel@tonic-gate 		ddi_walk_devs(rh_dip, hubd_check_disconnected_ports,
22257c478bd9Sstevel@tonic-gate 							NULL);
22267c478bd9Sstevel@tonic-gate #ifdef __lock_lint
22277c478bd9Sstevel@tonic-gate 		(void) hubd_check_disconnected_ports(rh_dip, NULL);
22287c478bd9Sstevel@tonic-gate #endif
22297c478bd9Sstevel@tonic-gate 		ndi_devi_exit(ddi_get_parent(rh_dip), circ);
22307c478bd9Sstevel@tonic-gate 
22317c478bd9Sstevel@tonic-gate 		/* quit if we are not enabled anymore */
22327c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(root_hubd));
22337c478bd9Sstevel@tonic-gate 		if ((root_hubd->h_cleanup_enabled == B_FALSE) ||
22347c478bd9Sstevel@tonic-gate 		    (root_hubd->h_cleanup_needed == B_FALSE)) {
22357c478bd9Sstevel@tonic-gate 			root_hubd->h_cleanup_active = B_FALSE;
22367c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(root_hubd));
22377c478bd9Sstevel@tonic-gate 			ndi_rele_devi(rh_dip);
22387c478bd9Sstevel@tonic-gate 
22397c478bd9Sstevel@tonic-gate 			break;
22407c478bd9Sstevel@tonic-gate 		}
22417c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(root_hubd));
22427c478bd9Sstevel@tonic-gate 		ndi_rele_devi(rh_dip);
22437c478bd9Sstevel@tonic-gate 
22447c478bd9Sstevel@tonic-gate #ifndef __lock_lint
22457c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(root_hubd));
22467c478bd9Sstevel@tonic-gate 		CALLB_CPR_SAFE_BEGIN(&cprinfo);
22477c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(root_hubd));
22487c478bd9Sstevel@tonic-gate 
22497c478bd9Sstevel@tonic-gate 		delay(drv_usectohz(hubd_dip_cleanup_delay));
22507c478bd9Sstevel@tonic-gate 
22517c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(root_hubd));
22527c478bd9Sstevel@tonic-gate 		CALLB_CPR_SAFE_END(&cprinfo, HUBD_MUTEX(root_hubd));
22537c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(root_hubd));
22547c478bd9Sstevel@tonic-gate #endif
22557c478bd9Sstevel@tonic-gate 	}
22567c478bd9Sstevel@tonic-gate 
22577c478bd9Sstevel@tonic-gate #ifndef __lock_lint
22587c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(root_hubd));
22597c478bd9Sstevel@tonic-gate 	CALLB_CPR_EXIT(&cprinfo);
22607c478bd9Sstevel@tonic-gate #endif
22617c478bd9Sstevel@tonic-gate }
22627c478bd9Sstevel@tonic-gate 
22637c478bd9Sstevel@tonic-gate 
22647c478bd9Sstevel@tonic-gate static void
22657c478bd9Sstevel@tonic-gate hubd_schedule_cleanup(dev_info_t *rh_dip)
22667c478bd9Sstevel@tonic-gate {
22677c478bd9Sstevel@tonic-gate 	hubd_t	*root_hubd = (hubd_t *)hubd_get_soft_state(rh_dip);
22687c478bd9Sstevel@tonic-gate 
22697c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(root_hubd));
22707c478bd9Sstevel@tonic-gate 	root_hubd->h_cleanup_needed = B_TRUE;
22717c478bd9Sstevel@tonic-gate 	if (root_hubd->h_cleanup_enabled && !(root_hubd->h_cleanup_active)) {
22727c478bd9Sstevel@tonic-gate 		root_hubd->h_cleanup_active = B_TRUE;
22737c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(root_hubd));
22747c478bd9Sstevel@tonic-gate 		(void) thread_create(NULL, 0,
22757c478bd9Sstevel@tonic-gate 		    hubd_root_hub_cleanup_thread,
22767c478bd9Sstevel@tonic-gate 		    (void *)root_hubd, 0, &p0, TS_RUN,
22777c478bd9Sstevel@tonic-gate 		    minclsyspri);
22787c478bd9Sstevel@tonic-gate 	} else {
22797c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(root_hubd));
22807c478bd9Sstevel@tonic-gate 	}
22817c478bd9Sstevel@tonic-gate }
22827c478bd9Sstevel@tonic-gate 
22837c478bd9Sstevel@tonic-gate 
22847c478bd9Sstevel@tonic-gate /*
22857c478bd9Sstevel@tonic-gate  * hubd_restore_device_state:
22867c478bd9Sstevel@tonic-gate  *	- set config for the hub
22877c478bd9Sstevel@tonic-gate  *	- power cycle all the ports
22887c478bd9Sstevel@tonic-gate  *	- for each port that was connected
22897c478bd9Sstevel@tonic-gate  *		- reset port
22907c478bd9Sstevel@tonic-gate  *		- assign addrs to the device on this port
22917c478bd9Sstevel@tonic-gate  *	- restart polling
22927c478bd9Sstevel@tonic-gate  *	- reset suspend flag
22937c478bd9Sstevel@tonic-gate  */
22947c478bd9Sstevel@tonic-gate static void
22957c478bd9Sstevel@tonic-gate hubd_restore_device_state(dev_info_t *dip, hubd_t *hubd)
22967c478bd9Sstevel@tonic-gate {
22977c478bd9Sstevel@tonic-gate 	int		rval;
22987c478bd9Sstevel@tonic-gate 	int		retry;
22997c478bd9Sstevel@tonic-gate 	uint_t		hub_prev_state;
23007c478bd9Sstevel@tonic-gate 	usb_port_t	port;
23017c478bd9Sstevel@tonic-gate 	uint16_t	status;
23027c478bd9Sstevel@tonic-gate 	uint16_t	change;
23037c478bd9Sstevel@tonic-gate 	dev_info_t	*ch_dip;
23047c478bd9Sstevel@tonic-gate 	boolean_t	ehci_root_hub;
23057c478bd9Sstevel@tonic-gate 
23067c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
23077c478bd9Sstevel@tonic-gate 	    "hubd_restore_device_state:");
23087c478bd9Sstevel@tonic-gate 
23097c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
23107c478bd9Sstevel@tonic-gate 	hub_prev_state = hubd->h_dev_state;
23117c478bd9Sstevel@tonic-gate 	ASSERT(hub_prev_state != USB_DEV_PWRED_DOWN);
23127c478bd9Sstevel@tonic-gate 
23137c478bd9Sstevel@tonic-gate 	/* First bring the device to full power */
23147c478bd9Sstevel@tonic-gate 	(void) hubd_pm_busy_component(hubd, dip, 0);
23157c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
23167c478bd9Sstevel@tonic-gate 
23177c478bd9Sstevel@tonic-gate 	(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
23187c478bd9Sstevel@tonic-gate 
23197c478bd9Sstevel@tonic-gate 	if (!usba_is_root_hub(dip) &&
23207c478bd9Sstevel@tonic-gate 	    (usb_check_same_device(dip, hubd->h_log_handle, USB_LOG_L0,
23217c478bd9Sstevel@tonic-gate 	    DPRINT_MASK_HOTPLUG,
23227c478bd9Sstevel@tonic-gate 	    USB_CHK_BASIC|USB_CHK_CFG, NULL) != USB_SUCCESS)) {
23237c478bd9Sstevel@tonic-gate 
23247c478bd9Sstevel@tonic-gate 		/* change the device state to disconnected */
23257c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
23267c478bd9Sstevel@tonic-gate 		hubd->h_dev_state = USB_DEV_DISCONNECTED;
23277c478bd9Sstevel@tonic-gate 		(void) hubd_pm_idle_component(hubd, dip, 0);
23287c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
23297c478bd9Sstevel@tonic-gate 
23307c478bd9Sstevel@tonic-gate 		return;
23317c478bd9Sstevel@tonic-gate 	}
23327c478bd9Sstevel@tonic-gate 
23337c478bd9Sstevel@tonic-gate 	ehci_root_hub = (strcmp(ddi_driver_name(dip), "ehci") == 0);
23347c478bd9Sstevel@tonic-gate 
23357c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
23367c478bd9Sstevel@tonic-gate 	/* First turn off all port power */
23377c478bd9Sstevel@tonic-gate 	rval = hubd_disable_all_port_power(hubd);
23387c478bd9Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
23397c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
23407c478bd9Sstevel@tonic-gate 		    "hubd_restore_device_state:"
23417c478bd9Sstevel@tonic-gate 		    "turning off port power failed");
23427c478bd9Sstevel@tonic-gate 	}
23437c478bd9Sstevel@tonic-gate 
23447c478bd9Sstevel@tonic-gate 	/* Settling time before turning on again */
23457c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
23467c478bd9Sstevel@tonic-gate 	delay(drv_usectohz(hubd_device_delay / 100));
23477c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
23487c478bd9Sstevel@tonic-gate 
23497c478bd9Sstevel@tonic-gate 	/* enable power on all ports so we can see connects */
23507c478bd9Sstevel@tonic-gate 	if (hubd_enable_all_port_power(hubd) != USB_SUCCESS) {
23517c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
23527c478bd9Sstevel@tonic-gate 		    "hubd_restore_device_state: turn on port power failed");
23537c478bd9Sstevel@tonic-gate 
23547c478bd9Sstevel@tonic-gate 		/* disable whatever was enabled */
23557c478bd9Sstevel@tonic-gate 		(void) hubd_disable_all_port_power(hubd);
23567c478bd9Sstevel@tonic-gate 
23577c478bd9Sstevel@tonic-gate 		(void) hubd_pm_idle_component(hubd, dip, 0);
23587c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
23597c478bd9Sstevel@tonic-gate 
23607c478bd9Sstevel@tonic-gate 		return;
23617c478bd9Sstevel@tonic-gate 	}
23627c478bd9Sstevel@tonic-gate 
23637c478bd9Sstevel@tonic-gate 	/*
23647c478bd9Sstevel@tonic-gate 	 * wait at least 3 frames before accessing devices
23657c478bd9Sstevel@tonic-gate 	 * (note that delay's minimal time is one clock tick which
23667c478bd9Sstevel@tonic-gate 	 * is 10ms unless hires_tick has been changed)
23677c478bd9Sstevel@tonic-gate 	 */
23687c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
23697c478bd9Sstevel@tonic-gate 	delay(drv_usectohz(10000));
23707c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
23717c478bd9Sstevel@tonic-gate 
23727c478bd9Sstevel@tonic-gate 	hubd->h_dev_state = USB_DEV_HUB_STATE_RECOVER;
23737c478bd9Sstevel@tonic-gate 
23747c478bd9Sstevel@tonic-gate 	for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
23757c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
23767c478bd9Sstevel@tonic-gate 		    "hubd_restore_device_state: port=%d", port);
23777c478bd9Sstevel@tonic-gate 
23787c478bd9Sstevel@tonic-gate 		/*
23797c478bd9Sstevel@tonic-gate 		 * the childen_dips list may have dips that have been
23807c478bd9Sstevel@tonic-gate 		 * already deallocated. we only get a post_detach notification
23817c478bd9Sstevel@tonic-gate 		 * but not a destroy notification
23827c478bd9Sstevel@tonic-gate 		 */
23837c478bd9Sstevel@tonic-gate 		ch_dip = hubd->h_children_dips[port];
23847c478bd9Sstevel@tonic-gate 		if (ch_dip) {
23857c478bd9Sstevel@tonic-gate 			/* get port status */
23867c478bd9Sstevel@tonic-gate 			(void) hubd_determine_port_status(hubd, port,
23877c478bd9Sstevel@tonic-gate 			    &status, &change, PORT_CHANGE_CSC);
23887c478bd9Sstevel@tonic-gate 
23897c478bd9Sstevel@tonic-gate 			/* check if it is truly connected */
23907c478bd9Sstevel@tonic-gate 			if (status & PORT_STATUS_CCS) {
23917c478bd9Sstevel@tonic-gate 				/*
23927c478bd9Sstevel@tonic-gate 				 * Now reset port and assign the device
23937c478bd9Sstevel@tonic-gate 				 * its original address
23947c478bd9Sstevel@tonic-gate 				 */
23957c478bd9Sstevel@tonic-gate 				retry = 0;
23967c478bd9Sstevel@tonic-gate 				do {
23977c478bd9Sstevel@tonic-gate 					(void) hubd_reset_port(hubd, port);
23987c478bd9Sstevel@tonic-gate 
23997c478bd9Sstevel@tonic-gate 					/* required for ppx */
24007c478bd9Sstevel@tonic-gate 					(void) hubd_enable_port(hubd, port);
24017c478bd9Sstevel@tonic-gate 
24027c478bd9Sstevel@tonic-gate 					if (retry) {
24037c478bd9Sstevel@tonic-gate 						mutex_exit(HUBD_MUTEX(hubd));
24047c478bd9Sstevel@tonic-gate 						delay(drv_usectohz(
24057c478bd9Sstevel@tonic-gate 							hubd_device_delay/2));
24067c478bd9Sstevel@tonic-gate 						mutex_enter(HUBD_MUTEX(hubd));
24077c478bd9Sstevel@tonic-gate 					}
24087c478bd9Sstevel@tonic-gate 
24097c478bd9Sstevel@tonic-gate 					rval = hubd_setdevaddr(hubd, port);
24107c478bd9Sstevel@tonic-gate 					retry++;
24117c478bd9Sstevel@tonic-gate 				} while ((rval != USB_SUCCESS) &&
24127c478bd9Sstevel@tonic-gate 				    (retry < hubd_retry_enumerate));
24137c478bd9Sstevel@tonic-gate 
24147c478bd9Sstevel@tonic-gate 				hubd_setdevconfig(hubd, port);
24157c478bd9Sstevel@tonic-gate 
24167c478bd9Sstevel@tonic-gate 				if (hub_prev_state == USB_DEV_DISCONNECTED) {
24177c478bd9Sstevel@tonic-gate 					/* post a connect event */
24187c478bd9Sstevel@tonic-gate 					mutex_exit(HUBD_MUTEX(hubd));
24197c478bd9Sstevel@tonic-gate 					hubd_post_event(hubd, port,
24207c478bd9Sstevel@tonic-gate 					    USBA_EVENT_TAG_HOT_INSERTION);
24217c478bd9Sstevel@tonic-gate 					mutex_enter(HUBD_MUTEX(hubd));
24227c478bd9Sstevel@tonic-gate 				} else {
24237c478bd9Sstevel@tonic-gate 					/*
24247c478bd9Sstevel@tonic-gate 					 * Since we have this device connected
24257c478bd9Sstevel@tonic-gate 					 * mark it reinserted to prevent
24267c478bd9Sstevel@tonic-gate 					 * cleanup thread from stepping in.
24277c478bd9Sstevel@tonic-gate 					 */
24287c478bd9Sstevel@tonic-gate 					mutex_exit(HUBD_MUTEX(hubd));
242916747f41Scth 					mutex_enter(&(DEVI(ch_dip)->devi_lock));
243016747f41Scth 					DEVI_SET_DEVICE_REINSERTED(ch_dip);
243116747f41Scth 					mutex_exit(&(DEVI(ch_dip)->devi_lock));
24327c478bd9Sstevel@tonic-gate 
24337c478bd9Sstevel@tonic-gate 					/*
24347c478bd9Sstevel@tonic-gate 					 * reopen pipes for children for
24357c478bd9Sstevel@tonic-gate 					 * their DDI_RESUME
24367c478bd9Sstevel@tonic-gate 					 */
24377c478bd9Sstevel@tonic-gate 					rval = usba_persistent_pipe_open(
24387c478bd9Sstevel@tonic-gate 					    usba_get_usba_device(ch_dip));
24397c478bd9Sstevel@tonic-gate 					mutex_enter(HUBD_MUTEX(hubd));
24407c478bd9Sstevel@tonic-gate 					ASSERT(rval == USB_SUCCESS);
24417c478bd9Sstevel@tonic-gate 				}
24427c478bd9Sstevel@tonic-gate 			} else {
24437c478bd9Sstevel@tonic-gate 				/*
24447c478bd9Sstevel@tonic-gate 				 * Mark this dip for deletion as the device
24457c478bd9Sstevel@tonic-gate 				 * is not physically present, and schedule
24467c478bd9Sstevel@tonic-gate 				 * cleanup thread upon post resume
24477c478bd9Sstevel@tonic-gate 				 */
24487c478bd9Sstevel@tonic-gate 				mutex_exit(HUBD_MUTEX(hubd));
24497c478bd9Sstevel@tonic-gate 
24507c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_ATTA,
24517c478bd9Sstevel@tonic-gate 				    hubd->h_log_handle,
24527c478bd9Sstevel@tonic-gate 				    "hubd_restore_device_state: "
24537c478bd9Sstevel@tonic-gate 				    "dip=%p on port=%d marked for cleanup",
24547c478bd9Sstevel@tonic-gate 				    ch_dip, port);
245516747f41Scth 				mutex_enter(&(DEVI(ch_dip)->devi_lock));
24567c478bd9Sstevel@tonic-gate 				DEVI_SET_DEVICE_REMOVED(ch_dip);
245716747f41Scth 				mutex_exit(&(DEVI(ch_dip)->devi_lock));
24587c478bd9Sstevel@tonic-gate 
24597c478bd9Sstevel@tonic-gate 				mutex_enter(HUBD_MUTEX(hubd));
24607c478bd9Sstevel@tonic-gate 			}
24617c478bd9Sstevel@tonic-gate 		} else if (ehci_root_hub) {
24627c478bd9Sstevel@tonic-gate 			/* get port status */
24637c478bd9Sstevel@tonic-gate 			(void) hubd_determine_port_status(hubd, port,
24647c478bd9Sstevel@tonic-gate 			    &status, &change, PORT_CHANGE_CSC);
24657c478bd9Sstevel@tonic-gate 
24667c478bd9Sstevel@tonic-gate 			/* check if it is truly connected */
24677c478bd9Sstevel@tonic-gate 			if (status & PORT_STATUS_CCS) {
24687c478bd9Sstevel@tonic-gate 				/*
24697c478bd9Sstevel@tonic-gate 				 * reset the port to find out if we have
24707c478bd9Sstevel@tonic-gate 				 * 2.0 device connected or 1.X. A 2.0
24717c478bd9Sstevel@tonic-gate 				 * device will still be seen as connected,
24727c478bd9Sstevel@tonic-gate 				 * while a 1.X device will switch over to
24737c478bd9Sstevel@tonic-gate 				 * the companion controller.
24747c478bd9Sstevel@tonic-gate 				 */
24757c478bd9Sstevel@tonic-gate 				(void) hubd_reset_port(hubd, port);
24767c478bd9Sstevel@tonic-gate 
24777c478bd9Sstevel@tonic-gate 				(void) hubd_determine_port_status(hubd, port,
24787c478bd9Sstevel@tonic-gate 				    &status, &change, PORT_CHANGE_CSC);
24797c478bd9Sstevel@tonic-gate 
24807c478bd9Sstevel@tonic-gate 				if (status &
24817c478bd9Sstevel@tonic-gate 				    (PORT_STATUS_CCS | PORT_STATUS_HSDA)) {
24827c478bd9Sstevel@tonic-gate 					/*
24837c478bd9Sstevel@tonic-gate 					 * We have a USB 2.0 device
24847c478bd9Sstevel@tonic-gate 					 * connected. Power cycle this port
24857c478bd9Sstevel@tonic-gate 					 * so that hotplug thread can
24867c478bd9Sstevel@tonic-gate 					 * enumerate this device.
24877c478bd9Sstevel@tonic-gate 					 */
24887c478bd9Sstevel@tonic-gate 					(void) hubd_toggle_port(hubd, port);
24897c478bd9Sstevel@tonic-gate 				} else {
24907c478bd9Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_ATTA,
24917c478bd9Sstevel@tonic-gate 					    hubd->h_log_handle,
24927c478bd9Sstevel@tonic-gate 					    "hubd_restore_device_state: "
24937c478bd9Sstevel@tonic-gate 					    "device on port %d switched over",
24947c478bd9Sstevel@tonic-gate 					    port);
24957c478bd9Sstevel@tonic-gate 				}
24967c478bd9Sstevel@tonic-gate 			}
24977c478bd9Sstevel@tonic-gate 
24987c478bd9Sstevel@tonic-gate 		}
24997c478bd9Sstevel@tonic-gate 	}
25007c478bd9Sstevel@tonic-gate 
25017c478bd9Sstevel@tonic-gate 
25027c478bd9Sstevel@tonic-gate 	/* if the device had remote wakeup earlier, enable it again */
25037c478bd9Sstevel@tonic-gate 	if (hubd->h_hubpm->hubp_wakeup_enabled) {
25047c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
25057c478bd9Sstevel@tonic-gate 		(void) usb_handle_remote_wakeup(hubd->h_dip,
25067c478bd9Sstevel@tonic-gate 		    USB_REMOTE_WAKEUP_ENABLE);
25077c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
25087c478bd9Sstevel@tonic-gate 	}
25097c478bd9Sstevel@tonic-gate 
25107c478bd9Sstevel@tonic-gate 	hubd->h_dev_state = USB_DEV_ONLINE;
25117c478bd9Sstevel@tonic-gate 	hubd_start_polling(hubd, 0);
25127c478bd9Sstevel@tonic-gate 	(void) hubd_pm_idle_component(hubd, dip, 0);
25137c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
25147c478bd9Sstevel@tonic-gate }
25157c478bd9Sstevel@tonic-gate 
25167c478bd9Sstevel@tonic-gate 
25177c478bd9Sstevel@tonic-gate /*
25187c478bd9Sstevel@tonic-gate  * hubd_cleanup:
25197c478bd9Sstevel@tonic-gate  *	cleanup hubd and deallocate. this function is called for
25207c478bd9Sstevel@tonic-gate  *	handling attach failures and detaching including dynamic
25217c478bd9Sstevel@tonic-gate  *	reconfiguration. If called from attaching, it must clean
25227c478bd9Sstevel@tonic-gate  *	up the whole thing and return success.
25237c478bd9Sstevel@tonic-gate  */
25247c478bd9Sstevel@tonic-gate /*ARGSUSED*/
25257c478bd9Sstevel@tonic-gate static int
25267c478bd9Sstevel@tonic-gate hubd_cleanup(dev_info_t *dip, hubd_t *hubd)
25277c478bd9Sstevel@tonic-gate {
25287c478bd9Sstevel@tonic-gate 	int		circ, rval, old_dev_state;
25297c478bd9Sstevel@tonic-gate 	hub_power_t	*hubpm;
25307c478bd9Sstevel@tonic-gate #ifdef DEBUG
25317c478bd9Sstevel@tonic-gate 	usb_port_t	port;
25327c478bd9Sstevel@tonic-gate #endif
25337c478bd9Sstevel@tonic-gate 
25347c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
25357c478bd9Sstevel@tonic-gate 	    "hubd_cleanup:");
25367c478bd9Sstevel@tonic-gate 
25377c478bd9Sstevel@tonic-gate 	if ((hubd->h_init_state & HUBD_LOCKS_DONE) == 0) {
25387c478bd9Sstevel@tonic-gate 		goto done;
25397c478bd9Sstevel@tonic-gate 	}
25407c478bd9Sstevel@tonic-gate 
25417c478bd9Sstevel@tonic-gate 	/* ensure we are the only one active */
25427c478bd9Sstevel@tonic-gate 	ndi_devi_enter(dip, &circ);
25437c478bd9Sstevel@tonic-gate 
25447c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
25457c478bd9Sstevel@tonic-gate 
25467c478bd9Sstevel@tonic-gate 	/* Cleanup failure is only allowed if called from detach */
25477c478bd9Sstevel@tonic-gate 	if (DEVI_IS_DETACHING(dip)) {
25487c478bd9Sstevel@tonic-gate 		dev_info_t *rh_dip = hubd->h_usba_device->usb_root_hub_dip;
25497c478bd9Sstevel@tonic-gate 
25507c478bd9Sstevel@tonic-gate 		/*
25517c478bd9Sstevel@tonic-gate 		 * We are being called from detach.
25527c478bd9Sstevel@tonic-gate 		 * Fail immediately if the hotplug thread is running
25537c478bd9Sstevel@tonic-gate 		 * else set the dev_state to disconnected so that
25547c478bd9Sstevel@tonic-gate 		 * hotplug thread just exits without doing anything.
25557c478bd9Sstevel@tonic-gate 		 */
25567c478bd9Sstevel@tonic-gate 		if (hubd->h_bus_ctls || hubd->h_bus_pwr ||
25577c478bd9Sstevel@tonic-gate 		    hubd->h_hotplug_thread) {
25587c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
25597c478bd9Sstevel@tonic-gate 			ndi_devi_exit(dip, circ);
25607c478bd9Sstevel@tonic-gate 
25617c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
25627c478bd9Sstevel@tonic-gate 			    "hubd_cleanup: hotplug thread/bus ctl active "
25637c478bd9Sstevel@tonic-gate 			    "- failing detach");
25647c478bd9Sstevel@tonic-gate 
25657c478bd9Sstevel@tonic-gate 			return (USB_FAILURE);
25667c478bd9Sstevel@tonic-gate 		}
25677c478bd9Sstevel@tonic-gate 
25687c478bd9Sstevel@tonic-gate 		/*
25697c478bd9Sstevel@tonic-gate 		 * if the deathrow thread is still active or about
25707c478bd9Sstevel@tonic-gate 		 * to become active, fail detach
25717c478bd9Sstevel@tonic-gate 		 * the roothup can only be detached if nexus drivers
25727c478bd9Sstevel@tonic-gate 		 * are unloaded or explicitly offlined
25737c478bd9Sstevel@tonic-gate 		 */
25747c478bd9Sstevel@tonic-gate 		if (rh_dip == dip) {
25757c478bd9Sstevel@tonic-gate 			if (hubd->h_cleanup_needed ||
25767c478bd9Sstevel@tonic-gate 			    hubd->h_cleanup_active) {
25777c478bd9Sstevel@tonic-gate 				mutex_exit(HUBD_MUTEX(hubd));
25787c478bd9Sstevel@tonic-gate 				ndi_devi_exit(dip, circ);
25797c478bd9Sstevel@tonic-gate 
25807c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_ATTA,
25817c478bd9Sstevel@tonic-gate 				    hubd->h_log_handle,
25827c478bd9Sstevel@tonic-gate 				    "hubd_cleanup: deathrow still active?"
25837c478bd9Sstevel@tonic-gate 				    "- failing detach");
25847c478bd9Sstevel@tonic-gate 
25857c478bd9Sstevel@tonic-gate 				return (USB_FAILURE);
25867c478bd9Sstevel@tonic-gate 			}
25877c478bd9Sstevel@tonic-gate 		}
25887c478bd9Sstevel@tonic-gate 	}
25897c478bd9Sstevel@tonic-gate 
25907c478bd9Sstevel@tonic-gate 	old_dev_state = hubd->h_dev_state;
25917c478bd9Sstevel@tonic-gate 	hubd->h_dev_state = USB_DEV_DISCONNECTED;
25927c478bd9Sstevel@tonic-gate 
25937c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
25947c478bd9Sstevel@tonic-gate 	    "hubd_cleanup: stop polling");
25957c478bd9Sstevel@tonic-gate 	hubd_close_intr_pipe(hubd);
25967c478bd9Sstevel@tonic-gate 
25977c478bd9Sstevel@tonic-gate 	ASSERT((hubd->h_bus_ctls || hubd->h_bus_pwr ||
25987c478bd9Sstevel@tonic-gate 	    hubd->h_hotplug_thread) == 0);
25997c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
26007c478bd9Sstevel@tonic-gate 
26017c478bd9Sstevel@tonic-gate 	/*
26027c478bd9Sstevel@tonic-gate 	 * deallocate events, if events are still registered
26037c478bd9Sstevel@tonic-gate 	 * (ie. children still attached) then we have to fail the detach
26047c478bd9Sstevel@tonic-gate 	 */
26057c478bd9Sstevel@tonic-gate 	if (hubd->h_ndi_event_hdl) {
26067c478bd9Sstevel@tonic-gate 
26077c478bd9Sstevel@tonic-gate 		rval = ndi_event_free_hdl(hubd->h_ndi_event_hdl);
26087c478bd9Sstevel@tonic-gate 		if (DEVI_IS_ATTACHING(dip)) {
26097c478bd9Sstevel@tonic-gate 
26107c478bd9Sstevel@tonic-gate 			/* It must return success if attaching. */
26117c478bd9Sstevel@tonic-gate 			ASSERT(rval == NDI_SUCCESS);
26127c478bd9Sstevel@tonic-gate 
26137c478bd9Sstevel@tonic-gate 		} else if (rval != NDI_SUCCESS) {
26147c478bd9Sstevel@tonic-gate 
2615*d291d9f2Sfrits 			USB_DPRINTF_L2(DPRINT_MASK_ALL, hubd->h_log_handle,
26167c478bd9Sstevel@tonic-gate 			    "hubd_cleanup: ndi_event_free_hdl failed");
26177c478bd9Sstevel@tonic-gate 			ndi_devi_exit(dip, circ);
26187c478bd9Sstevel@tonic-gate 
26197c478bd9Sstevel@tonic-gate 			return (USB_FAILURE);
26207c478bd9Sstevel@tonic-gate 
26217c478bd9Sstevel@tonic-gate 		}
26227c478bd9Sstevel@tonic-gate 	}
26237c478bd9Sstevel@tonic-gate 
26247c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
26257c478bd9Sstevel@tonic-gate 
26267c478bd9Sstevel@tonic-gate #ifdef DEBUG
26277c478bd9Sstevel@tonic-gate 	for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
26287c478bd9Sstevel@tonic-gate 		ASSERT(hubd->h_usba_devices[port] == NULL);
26297c478bd9Sstevel@tonic-gate 		ASSERT(hubd->h_children_dips[port] == NULL);
26307c478bd9Sstevel@tonic-gate 	}
26317c478bd9Sstevel@tonic-gate #endif
26327c478bd9Sstevel@tonic-gate 	kmem_free(hubd->h_children_dips, hubd->h_cd_list_length);
26337c478bd9Sstevel@tonic-gate 	kmem_free(hubd->h_usba_devices, hubd->h_cd_list_length);
26347c478bd9Sstevel@tonic-gate 
26357c478bd9Sstevel@tonic-gate 	/*
26367c478bd9Sstevel@tonic-gate 	 * Disable the event callbacks first, after this point, event
26377c478bd9Sstevel@tonic-gate 	 * callbacks will never get called. Note we shouldn't hold
26387c478bd9Sstevel@tonic-gate 	 * mutex while unregistering events because there may be a
26397c478bd9Sstevel@tonic-gate 	 * competing event callback thread. Event callbacks are done
26407c478bd9Sstevel@tonic-gate 	 * with ndi mutex held and this can cause a potential deadlock.
26417c478bd9Sstevel@tonic-gate 	 * Note that cleanup can't fail after deregistration of events.
26427c478bd9Sstevel@tonic-gate 	 */
26437c478bd9Sstevel@tonic-gate 	if (hubd->h_init_state &  HUBD_EVENTS_REGISTERED) {
26447c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
26457c478bd9Sstevel@tonic-gate 		usb_unregister_event_cbs(dip, &hubd_events);
26467c478bd9Sstevel@tonic-gate 		hubd_unregister_cpr_callback(hubd);
26477c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
26487c478bd9Sstevel@tonic-gate 	}
26497c478bd9Sstevel@tonic-gate 
26507c478bd9Sstevel@tonic-gate 	/* restore the old dev state so that device can be put into low power */
26517c478bd9Sstevel@tonic-gate 	hubd->h_dev_state = old_dev_state;
26527c478bd9Sstevel@tonic-gate 	hubpm = hubd->h_hubpm;
26537c478bd9Sstevel@tonic-gate 
26547c478bd9Sstevel@tonic-gate 	if ((hubpm) && (hubd->h_dev_state != USB_DEV_DISCONNECTED)) {
26557c478bd9Sstevel@tonic-gate 		(void) hubd_pm_busy_component(hubd, dip, 0);
26567c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
26577c478bd9Sstevel@tonic-gate 		if (hubd->h_hubpm->hubp_wakeup_enabled) {
26587c478bd9Sstevel@tonic-gate 			/*
26597c478bd9Sstevel@tonic-gate 			 * Bring the hub to full power before
26607c478bd9Sstevel@tonic-gate 			 * issuing the disable remote wakeup command
26617c478bd9Sstevel@tonic-gate 			 */
26627c478bd9Sstevel@tonic-gate 			(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
26637c478bd9Sstevel@tonic-gate 
26647c478bd9Sstevel@tonic-gate 			if ((rval = usb_handle_remote_wakeup(hubd->h_dip,
26657c478bd9Sstevel@tonic-gate 			    USB_REMOTE_WAKEUP_DISABLE)) != USB_SUCCESS) {
26667c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_PM,
26677c478bd9Sstevel@tonic-gate 				    hubd->h_log_handle,
26687c478bd9Sstevel@tonic-gate 				    "hubd_cleanup: disable remote wakeup "
26697c478bd9Sstevel@tonic-gate 				    "fails=%d", rval);
26707c478bd9Sstevel@tonic-gate 			}
26717c478bd9Sstevel@tonic-gate 		}
26727c478bd9Sstevel@tonic-gate 
26737c478bd9Sstevel@tonic-gate 		(void) pm_lower_power(hubd->h_dip, 0, USB_DEV_OS_PWR_OFF);
26747c478bd9Sstevel@tonic-gate 
26757c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
26767c478bd9Sstevel@tonic-gate 		(void) hubd_pm_idle_component(hubd, dip, 0);
26777c478bd9Sstevel@tonic-gate 	}
26787c478bd9Sstevel@tonic-gate 
26797c478bd9Sstevel@tonic-gate 	if (hubpm) {
26807c478bd9Sstevel@tonic-gate 		if (hubpm->hubp_child_pwrstate) {
26817c478bd9Sstevel@tonic-gate 			kmem_free(hubpm->hubp_child_pwrstate,
26827c478bd9Sstevel@tonic-gate 			    MAX_PORTS + 1);
26837c478bd9Sstevel@tonic-gate 		}
26847c478bd9Sstevel@tonic-gate 		kmem_free(hubpm, sizeof (hub_power_t));
26857c478bd9Sstevel@tonic-gate 	}
26867c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
26877c478bd9Sstevel@tonic-gate 
26887c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
26897c478bd9Sstevel@tonic-gate 	    "hubd_cleanup: freeing space");
26907c478bd9Sstevel@tonic-gate 
26917c478bd9Sstevel@tonic-gate 	if (hubd->h_init_state & HUBD_HUBDI_REGISTERED) {
26927c478bd9Sstevel@tonic-gate 		rval = usba_hubdi_unregister(dip);
26937c478bd9Sstevel@tonic-gate 		ASSERT(rval == USB_SUCCESS);
26947c478bd9Sstevel@tonic-gate 	}
26957c478bd9Sstevel@tonic-gate 
26967c478bd9Sstevel@tonic-gate 	if (hubd->h_init_state & HUBD_LOCKS_DONE) {
26977c478bd9Sstevel@tonic-gate 		mutex_destroy(HUBD_MUTEX(hubd));
26987c478bd9Sstevel@tonic-gate 		cv_destroy(&hubd->h_cv_reset_port);
26997c478bd9Sstevel@tonic-gate 	}
27007c478bd9Sstevel@tonic-gate 
27017c478bd9Sstevel@tonic-gate 	ndi_devi_exit(dip, circ);
27027c478bd9Sstevel@tonic-gate 
27037c478bd9Sstevel@tonic-gate 	if (hubd->h_init_state & HUBD_MINOR_NODE_CREATED) {
27047c478bd9Sstevel@tonic-gate 		ddi_remove_minor_node(dip, NULL);
27057c478bd9Sstevel@tonic-gate 	}
27067c478bd9Sstevel@tonic-gate 
27077c478bd9Sstevel@tonic-gate 	if (usba_is_root_hub(dip)) {
27087c478bd9Sstevel@tonic-gate 		usb_pipe_close(dip, hubd->h_default_pipe,
27097c478bd9Sstevel@tonic-gate 		    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
27107c478bd9Sstevel@tonic-gate 	}
27117c478bd9Sstevel@tonic-gate 
27127c478bd9Sstevel@tonic-gate done:
27137c478bd9Sstevel@tonic-gate 	if (hubd->h_ancestry_str) {
27147c478bd9Sstevel@tonic-gate 		kmem_free(hubd->h_ancestry_str, HUBD_APID_NAMELEN);
27157c478bd9Sstevel@tonic-gate 	}
27167c478bd9Sstevel@tonic-gate 
27177c478bd9Sstevel@tonic-gate 	usb_client_detach(dip, hubd->h_dev_data);
27187c478bd9Sstevel@tonic-gate 
27197c478bd9Sstevel@tonic-gate 	usb_free_log_hdl(hubd->h_log_handle);
27207c478bd9Sstevel@tonic-gate 
27217c478bd9Sstevel@tonic-gate 	if (!usba_is_root_hub(dip)) {
27227c478bd9Sstevel@tonic-gate 		ddi_soft_state_free(hubd_statep, ddi_get_instance(dip));
27237c478bd9Sstevel@tonic-gate 	}
27247c478bd9Sstevel@tonic-gate 
27257c478bd9Sstevel@tonic-gate 	ddi_prop_remove_all(dip);
27267c478bd9Sstevel@tonic-gate 
27277c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
27287c478bd9Sstevel@tonic-gate }
27297c478bd9Sstevel@tonic-gate 
27307c478bd9Sstevel@tonic-gate 
27317c478bd9Sstevel@tonic-gate /*
27327c478bd9Sstevel@tonic-gate  * hubd_check_ports:
27337c478bd9Sstevel@tonic-gate  *	- get hub descriptor
27347c478bd9Sstevel@tonic-gate  *	- check initial port status
27357c478bd9Sstevel@tonic-gate  *	- enable power on all ports
27367c478bd9Sstevel@tonic-gate  *	- enable polling on ep1
27377c478bd9Sstevel@tonic-gate  */
27387c478bd9Sstevel@tonic-gate static int
27397c478bd9Sstevel@tonic-gate hubd_check_ports(hubd_t  *hubd)
27407c478bd9Sstevel@tonic-gate {
27417c478bd9Sstevel@tonic-gate 	int	rval;
27427c478bd9Sstevel@tonic-gate 
27437c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
27447c478bd9Sstevel@tonic-gate 
27457c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
27467c478bd9Sstevel@tonic-gate 	    "hubd_check_ports: addr=0x%x", usb_get_addr(hubd->h_dip));
27477c478bd9Sstevel@tonic-gate 
27487c478bd9Sstevel@tonic-gate 	if ((rval = hubd_get_hub_descriptor(hubd)) != USB_SUCCESS) {
27497c478bd9Sstevel@tonic-gate 
27507c478bd9Sstevel@tonic-gate 		return (rval);
27517c478bd9Sstevel@tonic-gate 	}
27527c478bd9Sstevel@tonic-gate 
27537c478bd9Sstevel@tonic-gate 	/*
27547c478bd9Sstevel@tonic-gate 	 * First turn off all port power
27557c478bd9Sstevel@tonic-gate 	 */
27567c478bd9Sstevel@tonic-gate 	if ((rval = hubd_disable_all_port_power(hubd)) != USB_SUCCESS) {
27577c478bd9Sstevel@tonic-gate 
27587c478bd9Sstevel@tonic-gate 		/* disable whatever was enabled */
27597c478bd9Sstevel@tonic-gate 		(void) hubd_disable_all_port_power(hubd);
27607c478bd9Sstevel@tonic-gate 
27617c478bd9Sstevel@tonic-gate 		return (rval);
27627c478bd9Sstevel@tonic-gate 	}
27637c478bd9Sstevel@tonic-gate 
27647c478bd9Sstevel@tonic-gate 	/*
27657c478bd9Sstevel@tonic-gate 	 * do not switch on immediately (instantly on root hub)
27667c478bd9Sstevel@tonic-gate 	 * and allow time to settle
27677c478bd9Sstevel@tonic-gate 	 */
27687c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
27697c478bd9Sstevel@tonic-gate 	delay(drv_usectohz(10000));
27707c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
27717c478bd9Sstevel@tonic-gate 
27727c478bd9Sstevel@tonic-gate 	/*
27737c478bd9Sstevel@tonic-gate 	 * enable power on all ports so we can see connects
27747c478bd9Sstevel@tonic-gate 	 */
27757c478bd9Sstevel@tonic-gate 	if ((rval = hubd_enable_all_port_power(hubd)) != USB_SUCCESS) {
27767c478bd9Sstevel@tonic-gate 		/* disable whatever was enabled */
27777c478bd9Sstevel@tonic-gate 		(void) hubd_disable_all_port_power(hubd);
27787c478bd9Sstevel@tonic-gate 
27797c478bd9Sstevel@tonic-gate 		return (rval);
27807c478bd9Sstevel@tonic-gate 	}
27817c478bd9Sstevel@tonic-gate 
27827c478bd9Sstevel@tonic-gate 	/* wait at least 3 frames before accessing devices */
27837c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
27847c478bd9Sstevel@tonic-gate 	delay(drv_usectohz(10000));
27857c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
27867c478bd9Sstevel@tonic-gate 
27877c478bd9Sstevel@tonic-gate 	/*
27887c478bd9Sstevel@tonic-gate 	 * allocate arrays for saving the dips of each child per port
27897c478bd9Sstevel@tonic-gate 	 *
27907c478bd9Sstevel@tonic-gate 	 * ports go from 1 - n, allocate 1 more entry
27917c478bd9Sstevel@tonic-gate 	 */
27927c478bd9Sstevel@tonic-gate 	hubd->h_cd_list_length =
27937c478bd9Sstevel@tonic-gate 		(sizeof (dev_info_t **)) * (hubd->h_hub_descr.bNbrPorts + 1);
27947c478bd9Sstevel@tonic-gate 
27957c478bd9Sstevel@tonic-gate 	hubd->h_children_dips = (dev_info_t **)kmem_zalloc(
27967c478bd9Sstevel@tonic-gate 			hubd->h_cd_list_length, KM_SLEEP);
27977c478bd9Sstevel@tonic-gate 	hubd->h_usba_devices = (usba_device_t **)kmem_zalloc(
27987c478bd9Sstevel@tonic-gate 			hubd->h_cd_list_length, KM_SLEEP);
27997c478bd9Sstevel@tonic-gate 
28007c478bd9Sstevel@tonic-gate 	if ((rval = hubd_open_intr_pipe(hubd)) == USB_SUCCESS) {
28017c478bd9Sstevel@tonic-gate 		hubd_start_polling(hubd, 0);
28027c478bd9Sstevel@tonic-gate 	}
28037c478bd9Sstevel@tonic-gate 
28047c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
28057c478bd9Sstevel@tonic-gate 	    "hubd_check_ports done");
28067c478bd9Sstevel@tonic-gate 
28077c478bd9Sstevel@tonic-gate 	return (rval);
28087c478bd9Sstevel@tonic-gate }
28097c478bd9Sstevel@tonic-gate 
28107c478bd9Sstevel@tonic-gate 
28117c478bd9Sstevel@tonic-gate /*
28127c478bd9Sstevel@tonic-gate  * hubd_get_hub_descriptor:
28137c478bd9Sstevel@tonic-gate  */
28147c478bd9Sstevel@tonic-gate static int
28157c478bd9Sstevel@tonic-gate hubd_get_hub_descriptor(hubd_t *hubd)
28167c478bd9Sstevel@tonic-gate {
28177c478bd9Sstevel@tonic-gate 	usb_hub_descr_t	*hub_descr = &hubd->h_hub_descr;
28187c478bd9Sstevel@tonic-gate 	mblk_t		*data = NULL;
28197c478bd9Sstevel@tonic-gate 	usb_cr_t	completion_reason;
28207c478bd9Sstevel@tonic-gate 	usb_cb_flags_t	cb_flags;
28217c478bd9Sstevel@tonic-gate 	uint16_t	length;
28227c478bd9Sstevel@tonic-gate 	int		rval;
28237c478bd9Sstevel@tonic-gate 
28247c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
28257c478bd9Sstevel@tonic-gate 	    "hubd_get_hub_descriptor:");
28267c478bd9Sstevel@tonic-gate 
28277c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
28287c478bd9Sstevel@tonic-gate 	ASSERT(hubd->h_default_pipe != 0);
28297c478bd9Sstevel@tonic-gate 
28307c478bd9Sstevel@tonic-gate 	/* get hub descriptor length first by requesting 8 bytes only */
28317c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
28327c478bd9Sstevel@tonic-gate 
28337c478bd9Sstevel@tonic-gate 	if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
28347c478bd9Sstevel@tonic-gate 	    hubd->h_default_pipe,
28357c478bd9Sstevel@tonic-gate 	    HUB_CLASS_REQ,
28367c478bd9Sstevel@tonic-gate 	    USB_REQ_GET_DESCR,		/* bRequest */
28377c478bd9Sstevel@tonic-gate 	    USB_DESCR_TYPE_SETUP_HUB,	/* wValue */
28387c478bd9Sstevel@tonic-gate 	    0,				/* wIndex */
28397c478bd9Sstevel@tonic-gate 	    8,				/* wLength */
28407c478bd9Sstevel@tonic-gate 	    &data, 0,
28417c478bd9Sstevel@tonic-gate 	    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
28427c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
28437c478bd9Sstevel@tonic-gate 		    "get hub descriptor failed: cr=%d cb_fl=0x%x rval=%d",
28447c478bd9Sstevel@tonic-gate 		    completion_reason, cb_flags, rval);
28457c478bd9Sstevel@tonic-gate 		freemsg(data);
28467c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
28477c478bd9Sstevel@tonic-gate 
28487c478bd9Sstevel@tonic-gate 		return (rval);
28497c478bd9Sstevel@tonic-gate 	}
28507c478bd9Sstevel@tonic-gate 
28517c478bd9Sstevel@tonic-gate 	length = *(data->b_rptr);
28527c478bd9Sstevel@tonic-gate 
28537c478bd9Sstevel@tonic-gate 	if (length > 8) {
28547c478bd9Sstevel@tonic-gate 		freemsg(data);
28557c478bd9Sstevel@tonic-gate 		data = NULL;
28567c478bd9Sstevel@tonic-gate 
28577c478bd9Sstevel@tonic-gate 		/* get complete hub descriptor */
28587c478bd9Sstevel@tonic-gate 		if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
28597c478bd9Sstevel@tonic-gate 		    hubd->h_default_pipe,
28607c478bd9Sstevel@tonic-gate 		    HUB_CLASS_REQ,
28617c478bd9Sstevel@tonic-gate 		    USB_REQ_GET_DESCR,		/* bRequest */
28627c478bd9Sstevel@tonic-gate 		    USB_DESCR_TYPE_SETUP_HUB,	/* wValue */
28637c478bd9Sstevel@tonic-gate 		    0,				/* wIndex */
28647c478bd9Sstevel@tonic-gate 		    length,			/* wLength */
28657c478bd9Sstevel@tonic-gate 		    &data, 0,
28667c478bd9Sstevel@tonic-gate 		    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
28677c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
28687c478bd9Sstevel@tonic-gate 			    "get hub descriptor failed: "
28697c478bd9Sstevel@tonic-gate 			    "cr=%d cb_fl=0x%x rval=%d",
28707c478bd9Sstevel@tonic-gate 			    completion_reason, cb_flags, rval);
28717c478bd9Sstevel@tonic-gate 			freemsg(data);
28727c478bd9Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
28737c478bd9Sstevel@tonic-gate 
28747c478bd9Sstevel@tonic-gate 			return (rval);
28757c478bd9Sstevel@tonic-gate 		}
28767c478bd9Sstevel@tonic-gate 	}
28777c478bd9Sstevel@tonic-gate 
28787c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
28797c478bd9Sstevel@tonic-gate 
28807c478bd9Sstevel@tonic-gate 	/* parse the hub descriptor */
28817c478bd9Sstevel@tonic-gate 	/* only 32 ports are supported at present */
28827c478bd9Sstevel@tonic-gate 	ASSERT(*(data->b_rptr + 2) <= 32);
28837c478bd9Sstevel@tonic-gate 	if (usb_parse_CV_descr("cccscccccc",
28847c478bd9Sstevel@tonic-gate 	    data->b_rptr, data->b_wptr - data->b_rptr,
28857c478bd9Sstevel@tonic-gate 	    (void *)hub_descr, sizeof (usb_hub_descr_t)) == 0) {
28867c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
28877c478bd9Sstevel@tonic-gate 		    "parsing hub descriptor failed");
28887c478bd9Sstevel@tonic-gate 
28897c478bd9Sstevel@tonic-gate 		freemsg(data);
28907c478bd9Sstevel@tonic-gate 
28917c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
28927c478bd9Sstevel@tonic-gate 	}
28937c478bd9Sstevel@tonic-gate 
28947c478bd9Sstevel@tonic-gate 	freemsg(data);
28957c478bd9Sstevel@tonic-gate 
28967c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
28977c478bd9Sstevel@tonic-gate 	    "rval = 0x%x bNbrPorts = 0x%x wHubChars = 0x%x "
28987c478bd9Sstevel@tonic-gate 	    "PwrOn2PwrGood = 0x%x", rval,
28997c478bd9Sstevel@tonic-gate 	    hub_descr->bNbrPorts, hub_descr->wHubCharacteristics,
29007c478bd9Sstevel@tonic-gate 	    hub_descr->bPwrOn2PwrGood);
29017c478bd9Sstevel@tonic-gate 
29027c478bd9Sstevel@tonic-gate 	if (hub_descr->bNbrPorts > MAX_PORTS) {
29037c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L0(DPRINT_MASK_ATTA, hubd->h_log_handle,
29047c478bd9Sstevel@tonic-gate 		    "Hub driver supports max of %d ports on hub. "
29057c478bd9Sstevel@tonic-gate 		    "Hence using the first %d port of %d ports available",
29067c478bd9Sstevel@tonic-gate 		    MAX_PORTS, MAX_PORTS, hub_descr->bNbrPorts);
29077c478bd9Sstevel@tonic-gate 
29087c478bd9Sstevel@tonic-gate 		hub_descr->bNbrPorts = MAX_PORTS;
29097c478bd9Sstevel@tonic-gate 	}
29107c478bd9Sstevel@tonic-gate 
29117c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
29127c478bd9Sstevel@tonic-gate }
29137c478bd9Sstevel@tonic-gate 
29147c478bd9Sstevel@tonic-gate 
29157c478bd9Sstevel@tonic-gate /*
29167c478bd9Sstevel@tonic-gate  * hubd_open_intr_pipe:
29177c478bd9Sstevel@tonic-gate  *	we read all descriptors first for curiosity and then simply
29187c478bd9Sstevel@tonic-gate  *	open the pipe
29197c478bd9Sstevel@tonic-gate  */
29207c478bd9Sstevel@tonic-gate static int
29217c478bd9Sstevel@tonic-gate hubd_open_intr_pipe(hubd_t	*hubd)
29227c478bd9Sstevel@tonic-gate {
29237c478bd9Sstevel@tonic-gate 	int			rval;
29247c478bd9Sstevel@tonic-gate 
29257c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
29267c478bd9Sstevel@tonic-gate 	    "hubd_open_intr_pipe:");
29277c478bd9Sstevel@tonic-gate 
29287c478bd9Sstevel@tonic-gate 	ASSERT(hubd->h_intr_pipe_state == HUBD_INTR_PIPE_IDLE);
29297c478bd9Sstevel@tonic-gate 
29307c478bd9Sstevel@tonic-gate 	hubd->h_intr_pipe_state = HUBD_INTR_PIPE_OPENING;
29317c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
29327c478bd9Sstevel@tonic-gate 
29337c478bd9Sstevel@tonic-gate 	if ((rval = usb_pipe_open(hubd->h_dip,
29347c478bd9Sstevel@tonic-gate 	    &hubd->h_ep1_descr, &hubd->h_pipe_policy,
29357c478bd9Sstevel@tonic-gate 	    0, &hubd->h_ep1_ph)) != USB_SUCCESS) {
2936*d291d9f2Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
29377c478bd9Sstevel@tonic-gate 		    "open intr pipe failed (%d)", rval);
29387c478bd9Sstevel@tonic-gate 
29397c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
29407c478bd9Sstevel@tonic-gate 		hubd->h_intr_pipe_state = HUBD_INTR_PIPE_IDLE;
29417c478bd9Sstevel@tonic-gate 
29427c478bd9Sstevel@tonic-gate 		return (rval);
29437c478bd9Sstevel@tonic-gate 	}
29447c478bd9Sstevel@tonic-gate 
29457c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
29467c478bd9Sstevel@tonic-gate 	hubd->h_intr_pipe_state = HUBD_INTR_PIPE_ACTIVE;
29477c478bd9Sstevel@tonic-gate 
29487c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
29497c478bd9Sstevel@tonic-gate 	    "open intr pipe succeeded, ph=0x%p", hubd->h_ep1_ph);
29507c478bd9Sstevel@tonic-gate 
29517c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
29527c478bd9Sstevel@tonic-gate }
29537c478bd9Sstevel@tonic-gate 
29547c478bd9Sstevel@tonic-gate 
29557c478bd9Sstevel@tonic-gate /*
29567c478bd9Sstevel@tonic-gate  * hubd_start_polling:
29577c478bd9Sstevel@tonic-gate  *	start or restart the polling
29587c478bd9Sstevel@tonic-gate  */
29597c478bd9Sstevel@tonic-gate static void
29607c478bd9Sstevel@tonic-gate hubd_start_polling(hubd_t *hubd, int always)
29617c478bd9Sstevel@tonic-gate {
29627c478bd9Sstevel@tonic-gate 	usb_intr_req_t	*reqp;
29637c478bd9Sstevel@tonic-gate 	int			rval;
29647c478bd9Sstevel@tonic-gate 	usb_pipe_state_t	pipe_state;
29657c478bd9Sstevel@tonic-gate 
29667c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
29677c478bd9Sstevel@tonic-gate 	    "start polling: always=%d dev_state=%d pipe_state=%d\n\t"
29687c478bd9Sstevel@tonic-gate 	    "thread=%d ep1_ph=0x%p",
29697c478bd9Sstevel@tonic-gate 	    always, hubd->h_dev_state, hubd->h_intr_pipe_state,
29707c478bd9Sstevel@tonic-gate 	    hubd->h_hotplug_thread, hubd->h_ep1_ph);
29717c478bd9Sstevel@tonic-gate 
29727c478bd9Sstevel@tonic-gate 	/*
29737c478bd9Sstevel@tonic-gate 	 * start or restart polling on the intr pipe
29747c478bd9Sstevel@tonic-gate 	 * only if hotplug thread is not running
29757c478bd9Sstevel@tonic-gate 	 */
29767c478bd9Sstevel@tonic-gate 	if ((always == HUBD_ALWAYS_START_POLLING) ||
29777c478bd9Sstevel@tonic-gate 	    ((hubd->h_dev_state == USB_DEV_ONLINE) &&
29787c478bd9Sstevel@tonic-gate 	    (hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE) &&
29797c478bd9Sstevel@tonic-gate 	    (hubd->h_hotplug_thread == 0) && hubd->h_ep1_ph)) {
29807c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
29817c478bd9Sstevel@tonic-gate 		    "start polling requested");
29827c478bd9Sstevel@tonic-gate 
29837c478bd9Sstevel@tonic-gate 		reqp = usb_alloc_intr_req(hubd->h_dip, 0, USB_FLAGS_SLEEP);
29847c478bd9Sstevel@tonic-gate 
29857c478bd9Sstevel@tonic-gate 		reqp->intr_client_private = (usb_opaque_t)hubd;
29867c478bd9Sstevel@tonic-gate 		reqp->intr_attributes = USB_ATTRS_SHORT_XFER_OK |
29877c478bd9Sstevel@tonic-gate 					USB_ATTRS_AUTOCLEARING;
29887c478bd9Sstevel@tonic-gate 		reqp->intr_len = hubd->h_ep1_descr.wMaxPacketSize;
29897c478bd9Sstevel@tonic-gate 		reqp->intr_cb = hubd_read_cb;
29907c478bd9Sstevel@tonic-gate 		reqp->intr_exc_cb = hubd_exception_cb;
29917c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
29927c478bd9Sstevel@tonic-gate 		if ((rval = usb_pipe_intr_xfer(hubd->h_ep1_ph, reqp,
29937c478bd9Sstevel@tonic-gate 		    USB_FLAGS_SLEEP)) != USB_SUCCESS) {
29947c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
29957c478bd9Sstevel@tonic-gate 			    "start polling failed, rval=%d", rval);
29967c478bd9Sstevel@tonic-gate 			usb_free_intr_req(reqp);
29977c478bd9Sstevel@tonic-gate 		}
29987c478bd9Sstevel@tonic-gate 
29997c478bd9Sstevel@tonic-gate 		rval = usb_pipe_get_state(hubd->h_ep1_ph, &pipe_state,
30007c478bd9Sstevel@tonic-gate 			USB_FLAGS_SLEEP);
30017c478bd9Sstevel@tonic-gate 		if (pipe_state != USB_PIPE_STATE_ACTIVE) {
30027c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
30037c478bd9Sstevel@tonic-gate 			    "intr pipe state=%d, rval=%d", pipe_state, rval);
30047c478bd9Sstevel@tonic-gate 		}
30057c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
30067c478bd9Sstevel@tonic-gate 		    "start polling request 0x%p", reqp);
30077c478bd9Sstevel@tonic-gate 
30087c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
30097c478bd9Sstevel@tonic-gate 	}
30107c478bd9Sstevel@tonic-gate }
30117c478bd9Sstevel@tonic-gate 
30127c478bd9Sstevel@tonic-gate 
30137c478bd9Sstevel@tonic-gate /*
30147c478bd9Sstevel@tonic-gate  * hubd_stop_polling
30157c478bd9Sstevel@tonic-gate  *	stop polling but do not close the pipe
30167c478bd9Sstevel@tonic-gate  */
30177c478bd9Sstevel@tonic-gate static void
30187c478bd9Sstevel@tonic-gate hubd_stop_polling(hubd_t *hubd)
30197c478bd9Sstevel@tonic-gate {
30207c478bd9Sstevel@tonic-gate 	int			rval;
30217c478bd9Sstevel@tonic-gate 	usb_pipe_state_t	pipe_state;
30227c478bd9Sstevel@tonic-gate 
30237c478bd9Sstevel@tonic-gate 	if (hubd->h_ep1_ph) {
30247c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
30257c478bd9Sstevel@tonic-gate 		    "hubd_stop_polling:");
30267c478bd9Sstevel@tonic-gate 		hubd->h_intr_pipe_state = HUBD_INTR_PIPE_STOPPED;
30277c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
30287c478bd9Sstevel@tonic-gate 
30297c478bd9Sstevel@tonic-gate 		usb_pipe_stop_intr_polling(hubd->h_ep1_ph, USB_FLAGS_SLEEP);
30307c478bd9Sstevel@tonic-gate 		rval = usb_pipe_get_state(hubd->h_ep1_ph, &pipe_state,
30317c478bd9Sstevel@tonic-gate 			USB_FLAGS_SLEEP);
30327c478bd9Sstevel@tonic-gate 
30337c478bd9Sstevel@tonic-gate 		if (pipe_state != USB_PIPE_STATE_IDLE) {
30347c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
30357c478bd9Sstevel@tonic-gate 			    "intr pipe state=%d, rval=%d", pipe_state, rval);
30367c478bd9Sstevel@tonic-gate 		}
30377c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
30387c478bd9Sstevel@tonic-gate 		if (hubd->h_intr_pipe_state == HUBD_INTR_PIPE_STOPPED) {
30397c478bd9Sstevel@tonic-gate 			hubd->h_intr_pipe_state = HUBD_INTR_PIPE_ACTIVE;
30407c478bd9Sstevel@tonic-gate 		}
30417c478bd9Sstevel@tonic-gate 	}
30427c478bd9Sstevel@tonic-gate }
30437c478bd9Sstevel@tonic-gate 
30447c478bd9Sstevel@tonic-gate 
30457c478bd9Sstevel@tonic-gate /*
30467c478bd9Sstevel@tonic-gate  * hubd_close_intr_pipe:
30477c478bd9Sstevel@tonic-gate  *	close the pipe (which also stops the polling
30487c478bd9Sstevel@tonic-gate  *	and wait for the hotplug thread to exit
30497c478bd9Sstevel@tonic-gate  */
30507c478bd9Sstevel@tonic-gate static void
30517c478bd9Sstevel@tonic-gate hubd_close_intr_pipe(hubd_t *hubd)
30527c478bd9Sstevel@tonic-gate {
30537c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
30547c478bd9Sstevel@tonic-gate 	    "hubd_close_intr_pipe:");
30557c478bd9Sstevel@tonic-gate 
30567c478bd9Sstevel@tonic-gate 	/*
30577c478bd9Sstevel@tonic-gate 	 * Now that no async operation is outstanding on pipe,
30587c478bd9Sstevel@tonic-gate 	 * we can change the state to HUBD_INTR_PIPE_CLOSING
30597c478bd9Sstevel@tonic-gate 	 */
30607c478bd9Sstevel@tonic-gate 	hubd->h_intr_pipe_state = HUBD_INTR_PIPE_CLOSING;
30617c478bd9Sstevel@tonic-gate 
30627c478bd9Sstevel@tonic-gate 	ASSERT(hubd->h_hotplug_thread == 0);
30637c478bd9Sstevel@tonic-gate 
30647c478bd9Sstevel@tonic-gate 	if (hubd->h_ep1_ph) {
30657c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
30667c478bd9Sstevel@tonic-gate 		usb_pipe_close(hubd->h_dip, hubd->h_ep1_ph, USB_FLAGS_SLEEP,
30677c478bd9Sstevel@tonic-gate 							NULL, NULL);
30687c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
30697c478bd9Sstevel@tonic-gate 		hubd->h_ep1_ph = NULL;
30707c478bd9Sstevel@tonic-gate 	}
30717c478bd9Sstevel@tonic-gate 
30727c478bd9Sstevel@tonic-gate 	hubd->h_intr_pipe_state = HUBD_INTR_PIPE_IDLE;
30737c478bd9Sstevel@tonic-gate }
30747c478bd9Sstevel@tonic-gate 
30757c478bd9Sstevel@tonic-gate 
30767c478bd9Sstevel@tonic-gate /*
30777c478bd9Sstevel@tonic-gate  * hubd_exception_cb
30787c478bd9Sstevel@tonic-gate  *	interrupt ep1 exception callback function.
30797c478bd9Sstevel@tonic-gate  *	this callback executes in taskq thread context and assumes
30807c478bd9Sstevel@tonic-gate  *	autoclearing
30817c478bd9Sstevel@tonic-gate  */
30827c478bd9Sstevel@tonic-gate /*ARGSUSED*/
30837c478bd9Sstevel@tonic-gate static void
30847c478bd9Sstevel@tonic-gate hubd_exception_cb(usb_pipe_handle_t pipe, usb_intr_req_t *reqp)
30857c478bd9Sstevel@tonic-gate {
30867c478bd9Sstevel@tonic-gate 	hubd_t		*hubd = (hubd_t *)(reqp->intr_client_private);
30877c478bd9Sstevel@tonic-gate 
30887c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L2(DPRINT_MASK_CALLBACK, hubd->h_log_handle,
30897c478bd9Sstevel@tonic-gate 	    "hubd_exception_cb: "
30907c478bd9Sstevel@tonic-gate 	    "req=0x%p cr=%d data=0x%p cb_flags=0x%x", reqp,
30917c478bd9Sstevel@tonic-gate 	    reqp->intr_completion_reason, reqp->intr_data,
30927c478bd9Sstevel@tonic-gate 	    reqp->intr_cb_flags);
30937c478bd9Sstevel@tonic-gate 
30947c478bd9Sstevel@tonic-gate 	ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
30957c478bd9Sstevel@tonic-gate 
30967c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
30977c478bd9Sstevel@tonic-gate 	(void) hubd_pm_busy_component(hubd, hubd->h_dip, 0);
30987c478bd9Sstevel@tonic-gate 
30997c478bd9Sstevel@tonic-gate 	switch (reqp->intr_completion_reason) {
31007c478bd9Sstevel@tonic-gate 	case USB_CR_PIPE_RESET:
31017c478bd9Sstevel@tonic-gate 		/* only restart polling after autoclearing */
31027c478bd9Sstevel@tonic-gate 		if ((hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE) &&
31037c478bd9Sstevel@tonic-gate 		    (hubd->h_port_reset_wait == 0)) {
31047c478bd9Sstevel@tonic-gate 			hubd_start_polling(hubd, 0);
31057c478bd9Sstevel@tonic-gate 		}
31067c478bd9Sstevel@tonic-gate 
31077c478bd9Sstevel@tonic-gate 		break;
31087c478bd9Sstevel@tonic-gate 	case USB_CR_DEV_NOT_RESP:
31097c478bd9Sstevel@tonic-gate 	case USB_CR_STOPPED_POLLING:
31107c478bd9Sstevel@tonic-gate 	case USB_CR_PIPE_CLOSING:
31117c478bd9Sstevel@tonic-gate 	case USB_CR_UNSPECIFIED_ERR:
31127c478bd9Sstevel@tonic-gate 		/* never restart polling on these conditions */
31137c478bd9Sstevel@tonic-gate 	default:
31147c478bd9Sstevel@tonic-gate 		/* for all others, wait for the autoclearing PIPE_RESET cb */
31157c478bd9Sstevel@tonic-gate 
31167c478bd9Sstevel@tonic-gate 		break;
31177c478bd9Sstevel@tonic-gate 	}
31187c478bd9Sstevel@tonic-gate 
31197c478bd9Sstevel@tonic-gate 	usb_free_intr_req(reqp);
31207c478bd9Sstevel@tonic-gate 	(void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
31217c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
31227c478bd9Sstevel@tonic-gate }
31237c478bd9Sstevel@tonic-gate 
31247c478bd9Sstevel@tonic-gate 
31257c478bd9Sstevel@tonic-gate /*
31267c478bd9Sstevel@tonic-gate  * helper function to convert LE bytes to a portmask
31277c478bd9Sstevel@tonic-gate  */
31287c478bd9Sstevel@tonic-gate static usb_port_mask_t
31297c478bd9Sstevel@tonic-gate hubd_mblk2portmask(mblk_t *data)
31307c478bd9Sstevel@tonic-gate {
31317c478bd9Sstevel@tonic-gate 	int len = min(data->b_wptr - data->b_rptr, sizeof (usb_port_mask_t));
31327c478bd9Sstevel@tonic-gate 	usb_port_mask_t rval = 0;
31337c478bd9Sstevel@tonic-gate 	int i;
31347c478bd9Sstevel@tonic-gate 
31357c478bd9Sstevel@tonic-gate 	for (i = 0; i < len; i++) {
31367c478bd9Sstevel@tonic-gate 		rval |= data->b_rptr[i] << (i * 8);
31377c478bd9Sstevel@tonic-gate 	}
31387c478bd9Sstevel@tonic-gate 
31397c478bd9Sstevel@tonic-gate 	return (rval);
31407c478bd9Sstevel@tonic-gate }
31417c478bd9Sstevel@tonic-gate 
31427c478bd9Sstevel@tonic-gate 
31437c478bd9Sstevel@tonic-gate /*
31447c478bd9Sstevel@tonic-gate  * hubd_read_cb:
31457c478bd9Sstevel@tonic-gate  *	interrupt ep1 callback function
31467c478bd9Sstevel@tonic-gate  *
31477c478bd9Sstevel@tonic-gate  *	the status indicates just a change on the pipe with no indication
31487c478bd9Sstevel@tonic-gate  *	of what the change was
31497c478bd9Sstevel@tonic-gate  *
31507c478bd9Sstevel@tonic-gate  *	known conditions:
31517c478bd9Sstevel@tonic-gate  *		- reset port completion
31527c478bd9Sstevel@tonic-gate  *		- connect
31537c478bd9Sstevel@tonic-gate  *		- disconnect
31547c478bd9Sstevel@tonic-gate  *
31557c478bd9Sstevel@tonic-gate  *	for handling the hotplugging, create a new thread that can do
31567c478bd9Sstevel@tonic-gate  *	synchronous usba calls
31577c478bd9Sstevel@tonic-gate  */
31587c478bd9Sstevel@tonic-gate static void
31597c478bd9Sstevel@tonic-gate hubd_read_cb(usb_pipe_handle_t pipe, usb_intr_req_t *reqp)
31607c478bd9Sstevel@tonic-gate {
31617c478bd9Sstevel@tonic-gate 	hubd_t		*hubd = (hubd_t *)(reqp->intr_client_private);
31627c478bd9Sstevel@tonic-gate 	size_t		length;
31637c478bd9Sstevel@tonic-gate 	mblk_t		*data = reqp->intr_data;
31647c478bd9Sstevel@tonic-gate 
31657c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
31667c478bd9Sstevel@tonic-gate 	    "hubd_read_cb: ph=0x%p req=0x%p", pipe, reqp);
31677c478bd9Sstevel@tonic-gate 
31687c478bd9Sstevel@tonic-gate 	ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
31697c478bd9Sstevel@tonic-gate 
31707c478bd9Sstevel@tonic-gate 	/*
31717c478bd9Sstevel@tonic-gate 	 * At present, we are not handling notification for completion of
31727c478bd9Sstevel@tonic-gate 	 * asynchronous pipe reset, for which this data ptr could be NULL
31737c478bd9Sstevel@tonic-gate 	 */
31747c478bd9Sstevel@tonic-gate 
31757c478bd9Sstevel@tonic-gate 	if (data == NULL) {
31767c478bd9Sstevel@tonic-gate 		usb_free_intr_req(reqp);
31777c478bd9Sstevel@tonic-gate 
31787c478bd9Sstevel@tonic-gate 		return;
31797c478bd9Sstevel@tonic-gate 	}
31807c478bd9Sstevel@tonic-gate 
31817c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
31827c478bd9Sstevel@tonic-gate 
31837c478bd9Sstevel@tonic-gate 	if ((hubd->h_dev_state == USB_DEV_SUSPENDED) ||
31847c478bd9Sstevel@tonic-gate 	    (hubd->h_intr_pipe_state != HUBD_INTR_PIPE_ACTIVE)) {
31857c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
31867c478bd9Sstevel@tonic-gate 		usb_free_intr_req(reqp);
31877c478bd9Sstevel@tonic-gate 
31887c478bd9Sstevel@tonic-gate 		return;
31897c478bd9Sstevel@tonic-gate 	}
31907c478bd9Sstevel@tonic-gate 
31917c478bd9Sstevel@tonic-gate 	ASSERT(hubd->h_ep1_ph == pipe);
31927c478bd9Sstevel@tonic-gate 
31937c478bd9Sstevel@tonic-gate 	length = data->b_wptr - data->b_rptr;
31947c478bd9Sstevel@tonic-gate 
31957c478bd9Sstevel@tonic-gate 	/*
31967c478bd9Sstevel@tonic-gate 	 * Only look at the data and startup the hotplug thread if
31977c478bd9Sstevel@tonic-gate 	 * there actually is data.
31987c478bd9Sstevel@tonic-gate 	 */
31997c478bd9Sstevel@tonic-gate 	if (length != 0) {
32007c478bd9Sstevel@tonic-gate 		usb_port_mask_t port_change = hubd_mblk2portmask(data);
32017c478bd9Sstevel@tonic-gate 
32027c478bd9Sstevel@tonic-gate 		/*
32037c478bd9Sstevel@tonic-gate 		 * if a port change was already reported and we are waiting for
32047c478bd9Sstevel@tonic-gate 		 * reset port completion then wake up the hotplug thread which
32057c478bd9Sstevel@tonic-gate 		 * should be waiting on reset port completion
32067c478bd9Sstevel@tonic-gate 		 *
32077c478bd9Sstevel@tonic-gate 		 * if there is disconnect event instead of reset completion, let
32087c478bd9Sstevel@tonic-gate 		 * the hotplug thread figure this out
32097c478bd9Sstevel@tonic-gate 		 */
32107c478bd9Sstevel@tonic-gate 
32117c478bd9Sstevel@tonic-gate 		/* remove the reset wait bits from the status */
32127c478bd9Sstevel@tonic-gate 		hubd->h_port_change |= port_change &
32137c478bd9Sstevel@tonic-gate 						~hubd->h_port_reset_wait;
32147c478bd9Sstevel@tonic-gate 
32157c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_CALLBACK, hubd->h_log_handle,
32167c478bd9Sstevel@tonic-gate 		    "port change=0x%x port_reset_wait=0x%x",
32177c478bd9Sstevel@tonic-gate 		    hubd->h_port_change, hubd->h_port_reset_wait);
32187c478bd9Sstevel@tonic-gate 
32197c478bd9Sstevel@tonic-gate 		/* there should be only one reset bit active at the time */
32207c478bd9Sstevel@tonic-gate 		if (hubd->h_port_reset_wait & port_change) {
32217c478bd9Sstevel@tonic-gate 			hubd->h_port_reset_wait = 0;
32227c478bd9Sstevel@tonic-gate 			cv_signal(&hubd->h_cv_reset_port);
32237c478bd9Sstevel@tonic-gate 		}
32247c478bd9Sstevel@tonic-gate 
32257c478bd9Sstevel@tonic-gate 		/*
32267c478bd9Sstevel@tonic-gate 		 * kick off the thread only if device is ONLINE and it is not
32277c478bd9Sstevel@tonic-gate 		 * during attaching or detaching
32287c478bd9Sstevel@tonic-gate 		 */
32297c478bd9Sstevel@tonic-gate 		if ((hubd->h_dev_state == USB_DEV_ONLINE) &&
32307c478bd9Sstevel@tonic-gate 		    (!DEVI_IS_ATTACHING(hubd->h_dip)) &&
32317c478bd9Sstevel@tonic-gate 		    (!DEVI_IS_DETACHING(hubd->h_dip)) &&
32327c478bd9Sstevel@tonic-gate 		    (hubd->h_port_change) &&
32337c478bd9Sstevel@tonic-gate 		    (hubd->h_hotplug_thread == 0)) {
32347c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_CALLBACK, hubd->h_log_handle,
32357c478bd9Sstevel@tonic-gate 			    "creating hotplug thread: "
32367c478bd9Sstevel@tonic-gate 			    "dev_state=%d", hubd->h_dev_state);
32377c478bd9Sstevel@tonic-gate 
32387c478bd9Sstevel@tonic-gate 			/*
32397c478bd9Sstevel@tonic-gate 			 * Mark this device as busy. The will be marked idle
32407c478bd9Sstevel@tonic-gate 			 * if the async req fails or at the exit of  hotplug
32417c478bd9Sstevel@tonic-gate 			 * thread
32427c478bd9Sstevel@tonic-gate 			 */
32437c478bd9Sstevel@tonic-gate 			(void) hubd_pm_busy_component(hubd, hubd->h_dip, 0);
32447c478bd9Sstevel@tonic-gate 
32457c478bd9Sstevel@tonic-gate 			if (usb_async_req(hubd->h_dip,
32467c478bd9Sstevel@tonic-gate 			    hubd_hotplug_thread,
32477c478bd9Sstevel@tonic-gate 			    (void *)hubd, 0) == USB_SUCCESS) {
32487c478bd9Sstevel@tonic-gate 				hubd->h_hotplug_thread++;
32497c478bd9Sstevel@tonic-gate 			} else {
32507c478bd9Sstevel@tonic-gate 				/* mark this device as idle */
32517c478bd9Sstevel@tonic-gate 				(void) hubd_pm_idle_component(hubd,
32527c478bd9Sstevel@tonic-gate 				    hubd->h_dip, 0);
32537c478bd9Sstevel@tonic-gate 			}
32547c478bd9Sstevel@tonic-gate 		}
32557c478bd9Sstevel@tonic-gate 	}
32567c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
32577c478bd9Sstevel@tonic-gate 
32587c478bd9Sstevel@tonic-gate 	usb_free_intr_req(reqp);
32597c478bd9Sstevel@tonic-gate }
32607c478bd9Sstevel@tonic-gate 
32617c478bd9Sstevel@tonic-gate 
32627c478bd9Sstevel@tonic-gate /*
32637c478bd9Sstevel@tonic-gate  * hubd_hotplug_thread:
32647c478bd9Sstevel@tonic-gate  *	handles resetting of port, and creating children
32657c478bd9Sstevel@tonic-gate  *
32667c478bd9Sstevel@tonic-gate  *	the ports to check are indicated in h_port_change bit mask
32677c478bd9Sstevel@tonic-gate  * XXX note that one time poll doesn't work on the root hub
32687c478bd9Sstevel@tonic-gate  */
32697c478bd9Sstevel@tonic-gate static void
32707c478bd9Sstevel@tonic-gate hubd_hotplug_thread(void *arg)
32717c478bd9Sstevel@tonic-gate {
32727c478bd9Sstevel@tonic-gate 	hubd_t		*hubd = (hubd_t *)arg;
32737c478bd9Sstevel@tonic-gate 	usb_port_t	port;
32747c478bd9Sstevel@tonic-gate 	uint16_t	nports;
32757c478bd9Sstevel@tonic-gate 	uint16_t	status, change;
32767c478bd9Sstevel@tonic-gate 	hub_power_t	*hubpm;
32777c478bd9Sstevel@tonic-gate 	dev_info_t	*hdip = hubd->h_dip;
32787c478bd9Sstevel@tonic-gate 	dev_info_t	*rh_dip = hubd->h_usba_device->usb_root_hub_dip;
32797c478bd9Sstevel@tonic-gate 	boolean_t	online_child = B_FALSE;
32807c478bd9Sstevel@tonic-gate 	boolean_t	offline_child = B_FALSE;
32817c478bd9Sstevel@tonic-gate 	boolean_t	pwrup_child = B_FALSE;
32827c478bd9Sstevel@tonic-gate 	int		prh_circ, rh_circ, circ, old_state;
32837c478bd9Sstevel@tonic-gate 
32847c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
32857c478bd9Sstevel@tonic-gate 	    "hubd_hotplug_thread:  started");
32867c478bd9Sstevel@tonic-gate 
32877c478bd9Sstevel@tonic-gate 	/*
32887c478bd9Sstevel@tonic-gate 	 * if our bus power entry point is active, process the change
32897c478bd9Sstevel@tonic-gate 	 * on the next notification of interrupt pipe
32907c478bd9Sstevel@tonic-gate 	 */
32917c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
32927c478bd9Sstevel@tonic-gate 	if (hubd->h_bus_pwr || (hubd->h_hotplug_thread > 1)) {
32937c478bd9Sstevel@tonic-gate 		hubd->h_hotplug_thread--;
32947c478bd9Sstevel@tonic-gate 
32957c478bd9Sstevel@tonic-gate 		/* mark this device as idle */
32967c478bd9Sstevel@tonic-gate 		hubd_pm_idle_component(hubd, hubd->h_dip, 0);
32977c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
32987c478bd9Sstevel@tonic-gate 
32997c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
33007c478bd9Sstevel@tonic-gate 		    "hubd_hotplug_thread: "
33017c478bd9Sstevel@tonic-gate 		    "bus_power in progress/hotplugging undesirable - quit");
33027c478bd9Sstevel@tonic-gate 
33037c478bd9Sstevel@tonic-gate 		return;
33047c478bd9Sstevel@tonic-gate 	}
33057c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
33067c478bd9Sstevel@tonic-gate 
33077c478bd9Sstevel@tonic-gate 	ndi_hold_devi(hdip); /* so we don't race with detach */
33087c478bd9Sstevel@tonic-gate 
33097c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
33107c478bd9Sstevel@tonic-gate 
33117c478bd9Sstevel@tonic-gate 	/* is this the root hub? */
33127c478bd9Sstevel@tonic-gate 	if (hdip == rh_dip) {
33137c478bd9Sstevel@tonic-gate 		if (hubd->h_dev_state == USB_DEV_PWRED_DOWN) {
33147c478bd9Sstevel@tonic-gate 			hubpm = hubd->h_hubpm;
33157c478bd9Sstevel@tonic-gate 
33167c478bd9Sstevel@tonic-gate 			/* mark the root hub as full power */
33177c478bd9Sstevel@tonic-gate 			hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR;
33187c478bd9Sstevel@tonic-gate 			hubpm->hubp_time_at_full_power = ddi_get_time();
33197c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
33207c478bd9Sstevel@tonic-gate 
33217c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
33227c478bd9Sstevel@tonic-gate 			    "hubd_hotplug_thread: call pm_power_has_changed");
33237c478bd9Sstevel@tonic-gate 
33247c478bd9Sstevel@tonic-gate 			(void) pm_power_has_changed(hdip, 0,
33257c478bd9Sstevel@tonic-gate 				USB_DEV_OS_FULL_PWR);
33267c478bd9Sstevel@tonic-gate 
33277c478bd9Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
33287c478bd9Sstevel@tonic-gate 			hubd->h_dev_state = USB_DEV_ONLINE;
33297c478bd9Sstevel@tonic-gate 		}
33307c478bd9Sstevel@tonic-gate 
33317c478bd9Sstevel@tonic-gate 	} else {
33327c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
33337c478bd9Sstevel@tonic-gate 		    "hubd_hotplug_thread: not root hub");
33347c478bd9Sstevel@tonic-gate 	}
33357c478bd9Sstevel@tonic-gate 
33367c478bd9Sstevel@tonic-gate 	ASSERT(hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE);
33377c478bd9Sstevel@tonic-gate 
33387c478bd9Sstevel@tonic-gate 	nports = hubd->h_hub_descr.bNbrPorts;
33397c478bd9Sstevel@tonic-gate 
33407c478bd9Sstevel@tonic-gate 	hubd_stop_polling(hubd);
33417c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
33427c478bd9Sstevel@tonic-gate 
33437c478bd9Sstevel@tonic-gate 	/*
33447c478bd9Sstevel@tonic-gate 	 * this ensures one hotplug activity per system at a time.
33457c478bd9Sstevel@tonic-gate 	 * we enter the parent PCI node to have this serialization.
33467c478bd9Sstevel@tonic-gate 	 * this also excludes ioctls and deathrow thread
33477c478bd9Sstevel@tonic-gate 	 * (a bit crude but easier to debug)
33487c478bd9Sstevel@tonic-gate 	 */
33497c478bd9Sstevel@tonic-gate 	ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
33507c478bd9Sstevel@tonic-gate 	ndi_devi_enter(rh_dip, &rh_circ);
33517c478bd9Sstevel@tonic-gate 
33527c478bd9Sstevel@tonic-gate 	/* exclude other threads */
33537c478bd9Sstevel@tonic-gate 	ndi_devi_enter(hdip, &circ);
33547c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
33557c478bd9Sstevel@tonic-gate 
33567c478bd9Sstevel@tonic-gate 	while ((hubd->h_dev_state == USB_DEV_ONLINE) &&
33577c478bd9Sstevel@tonic-gate 	    (hubd->h_port_change)) {
33587c478bd9Sstevel@tonic-gate 		/*
33597c478bd9Sstevel@tonic-gate 		 * The 0th bit is the hub status change bit.
33607c478bd9Sstevel@tonic-gate 		 * handle loss of local power here
33617c478bd9Sstevel@tonic-gate 		 */
33627c478bd9Sstevel@tonic-gate 		if (hubd->h_port_change & HUB_CHANGE_STATUS) {
33637c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
33647c478bd9Sstevel@tonic-gate 			    "hubd_hotplug_thread: hub status change!");
33657c478bd9Sstevel@tonic-gate 
33667c478bd9Sstevel@tonic-gate 			/*
33677c478bd9Sstevel@tonic-gate 			 * This should be handled properly.  For now,
33687c478bd9Sstevel@tonic-gate 			 * mask off the bit.
33697c478bd9Sstevel@tonic-gate 			 */
33707c478bd9Sstevel@tonic-gate 			hubd->h_port_change &= ~HUB_CHANGE_STATUS;
33717c478bd9Sstevel@tonic-gate 
33727c478bd9Sstevel@tonic-gate 			/*
33737c478bd9Sstevel@tonic-gate 			 * check and ack hub status
33747c478bd9Sstevel@tonic-gate 			 * this causes stall conditions
33757c478bd9Sstevel@tonic-gate 			 * when local power is removed
33767c478bd9Sstevel@tonic-gate 			 */
33777c478bd9Sstevel@tonic-gate 			(void) hubd_get_hub_status(hubd);
33787c478bd9Sstevel@tonic-gate 		}
33797c478bd9Sstevel@tonic-gate 
33807c478bd9Sstevel@tonic-gate 		for (port = 1; port <= nports; port++) {
33817c478bd9Sstevel@tonic-gate 			usb_port_mask_t port_mask;
33827c478bd9Sstevel@tonic-gate 			boolean_t was_connected;
33837c478bd9Sstevel@tonic-gate 
33847c478bd9Sstevel@tonic-gate 			port_mask = 1 << port;
33857c478bd9Sstevel@tonic-gate 			was_connected =
33867c478bd9Sstevel@tonic-gate 				(hubd->h_port_state[port] & PORT_STATUS_CCS) &&
33877c478bd9Sstevel@tonic-gate 				(hubd->h_children_dips[port]);
33887c478bd9Sstevel@tonic-gate 
33897c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
33907c478bd9Sstevel@tonic-gate 			    "hubd_hotplug_thread: "
33917c478bd9Sstevel@tonic-gate 			    "port %d mask=0x%x change=0x%x connected=0x%x",
33927c478bd9Sstevel@tonic-gate 			    port, port_mask, hubd->h_port_change,
33937c478bd9Sstevel@tonic-gate 			    was_connected);
33947c478bd9Sstevel@tonic-gate 
33957c478bd9Sstevel@tonic-gate 			/*
33967c478bd9Sstevel@tonic-gate 			 * is this a port connection that changed?
33977c478bd9Sstevel@tonic-gate 			 */
33987c478bd9Sstevel@tonic-gate 			if ((hubd->h_port_change & port_mask) == 0) {
33997c478bd9Sstevel@tonic-gate 
34007c478bd9Sstevel@tonic-gate 				continue;
34017c478bd9Sstevel@tonic-gate 			}
34027c478bd9Sstevel@tonic-gate 			hubd->h_port_change &= ~port_mask;
34037c478bd9Sstevel@tonic-gate 
34047c478bd9Sstevel@tonic-gate 			/* ack all changes */
34057c478bd9Sstevel@tonic-gate 			(void) hubd_determine_port_status(hubd, port,
34067c478bd9Sstevel@tonic-gate 			    &status, &change, HUBD_ACK_ALL_CHANGES);
34077c478bd9Sstevel@tonic-gate 
34087c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
34097c478bd9Sstevel@tonic-gate 			    "handle port %d:\n\t"
34107c478bd9Sstevel@tonic-gate 			    "new status=0x%x change=0x%x was_conn=0x%x ",
34117c478bd9Sstevel@tonic-gate 			    port, status, change, was_connected);
34127c478bd9Sstevel@tonic-gate 
34137c478bd9Sstevel@tonic-gate 			/* Recover a disabled port */
34147c478bd9Sstevel@tonic-gate 			if (change & PORT_CHANGE_PESC) {
34157c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG,
34167c478bd9Sstevel@tonic-gate 					hubd->h_log_handle,
34177c478bd9Sstevel@tonic-gate 					"port%d Disabled - "
34187c478bd9Sstevel@tonic-gate 					"status=0x%x, change=0x%x",
34197c478bd9Sstevel@tonic-gate 					port, status, change);
34207c478bd9Sstevel@tonic-gate 
34217c478bd9Sstevel@tonic-gate 				/*
34227c478bd9Sstevel@tonic-gate 				 * if the port was connected and is still
34237c478bd9Sstevel@tonic-gate 				 * connected, recover the port
34247c478bd9Sstevel@tonic-gate 				 */
34257c478bd9Sstevel@tonic-gate 				if (was_connected && (status &
34267c478bd9Sstevel@tonic-gate 				    PORT_STATUS_CCS)) {
34277c478bd9Sstevel@tonic-gate 					online_child |=
34287c478bd9Sstevel@tonic-gate 					    (hubd_recover_disabled_port(hubd,
34297c478bd9Sstevel@tonic-gate 					    port) == USB_SUCCESS);
34307c478bd9Sstevel@tonic-gate 				}
34317c478bd9Sstevel@tonic-gate 			}
34327c478bd9Sstevel@tonic-gate 
34337c478bd9Sstevel@tonic-gate 			/*
34347c478bd9Sstevel@tonic-gate 			 * Now check what changed on the port
34357c478bd9Sstevel@tonic-gate 			 */
34367c478bd9Sstevel@tonic-gate 			if (change & PORT_CHANGE_CSC) {
34377c478bd9Sstevel@tonic-gate 				if ((status & PORT_STATUS_CCS) &&
34387c478bd9Sstevel@tonic-gate 				    (!was_connected)) {
34397c478bd9Sstevel@tonic-gate 					/* new device plugged in */
34407c478bd9Sstevel@tonic-gate 					online_child |=
34417c478bd9Sstevel@tonic-gate 					    (hubd_handle_port_connect(hubd,
34427c478bd9Sstevel@tonic-gate 					    port) == USB_SUCCESS);
34437c478bd9Sstevel@tonic-gate 
34447c478bd9Sstevel@tonic-gate 				} else if ((status & PORT_STATUS_CCS) &&
34457c478bd9Sstevel@tonic-gate 				    was_connected) {
34467c478bd9Sstevel@tonic-gate 					/*
34477c478bd9Sstevel@tonic-gate 					 * In this case we can never be sure
34487c478bd9Sstevel@tonic-gate 					 * if the device indeed got hotplugged
34497c478bd9Sstevel@tonic-gate 					 * or the hub is falsely reporting the
34507c478bd9Sstevel@tonic-gate 					 * change.
34517c478bd9Sstevel@tonic-gate 					 * first post a disconnect event
34527c478bd9Sstevel@tonic-gate 					 * to the child
34537c478bd9Sstevel@tonic-gate 					 */
34547c478bd9Sstevel@tonic-gate 					mutex_exit(HUBD_MUTEX(hubd));
34557c478bd9Sstevel@tonic-gate 					hubd_post_event(hubd, port,
34567c478bd9Sstevel@tonic-gate 					    USBA_EVENT_TAG_HOT_REMOVAL);
34577c478bd9Sstevel@tonic-gate 					mutex_enter(HUBD_MUTEX(hubd));
34587c478bd9Sstevel@tonic-gate 
34597c478bd9Sstevel@tonic-gate 					/*
34607c478bd9Sstevel@tonic-gate 					 * then reset the port and recover
34617c478bd9Sstevel@tonic-gate 					 * the device
34627c478bd9Sstevel@tonic-gate 					 */
34637c478bd9Sstevel@tonic-gate 					online_child |=
34647c478bd9Sstevel@tonic-gate 					    (hubd_handle_port_connect(hubd,
34657c478bd9Sstevel@tonic-gate 					    port) == USB_SUCCESS);
34667c478bd9Sstevel@tonic-gate 				} else if (was_connected) {
34677c478bd9Sstevel@tonic-gate 					/* this is a disconnect */
34687c478bd9Sstevel@tonic-gate 					mutex_exit(HUBD_MUTEX(hubd));
34697c478bd9Sstevel@tonic-gate 					hubd_post_event(hubd, port,
34707c478bd9Sstevel@tonic-gate 					    USBA_EVENT_TAG_HOT_REMOVAL);
34717c478bd9Sstevel@tonic-gate 					mutex_enter(HUBD_MUTEX(hubd));
34727c478bd9Sstevel@tonic-gate 
34737c478bd9Sstevel@tonic-gate 					offline_child = B_TRUE;
34747c478bd9Sstevel@tonic-gate 				}
34757c478bd9Sstevel@tonic-gate 			}
34767c478bd9Sstevel@tonic-gate 
34777c478bd9Sstevel@tonic-gate 			/*
34787c478bd9Sstevel@tonic-gate 			 * Check if any port is coming out of suspend
34797c478bd9Sstevel@tonic-gate 			 */
34807c478bd9Sstevel@tonic-gate 			if (change & PORT_CHANGE_PSSC) {
34817c478bd9Sstevel@tonic-gate 				/* a resuming device could have disconnected */
34827c478bd9Sstevel@tonic-gate 				if (was_connected &&
34837c478bd9Sstevel@tonic-gate 				    hubd->h_children_dips[port]) {
34847c478bd9Sstevel@tonic-gate 
34857c478bd9Sstevel@tonic-gate 					/* device on this port resuming */
34867c478bd9Sstevel@tonic-gate 					dev_info_t *dip;
34877c478bd9Sstevel@tonic-gate 
34887c478bd9Sstevel@tonic-gate 					dip = hubd->h_children_dips[port];
34897c478bd9Sstevel@tonic-gate 
34907c478bd9Sstevel@tonic-gate 					/*
34917c478bd9Sstevel@tonic-gate 					 * Don't raise power on detaching child
34927c478bd9Sstevel@tonic-gate 					 */
34937c478bd9Sstevel@tonic-gate 					if (!DEVI_IS_DETACHING(dip)) {
34947c478bd9Sstevel@tonic-gate 						/*
34957c478bd9Sstevel@tonic-gate 						 * As this child is not
34967c478bd9Sstevel@tonic-gate 						 * detaching, we set this
34977c478bd9Sstevel@tonic-gate 						 * flag, causing bus_ctls
34987c478bd9Sstevel@tonic-gate 						 * to stall detach till
34997c478bd9Sstevel@tonic-gate 						 * pm_raise_power returns
35007c478bd9Sstevel@tonic-gate 						 * and flag it for a deferred
35017c478bd9Sstevel@tonic-gate 						 * raise_power.
35027c478bd9Sstevel@tonic-gate 						 *
35037c478bd9Sstevel@tonic-gate 						 * pm_raise_power is deferred
35047c478bd9Sstevel@tonic-gate 						 * because we need to release
35057c478bd9Sstevel@tonic-gate 						 * the locks first.
35067c478bd9Sstevel@tonic-gate 						 */
35077c478bd9Sstevel@tonic-gate 						hubd->h_port_state[port] |=
35087c478bd9Sstevel@tonic-gate 							HUBD_CHILD_RAISE_POWER;
35097c478bd9Sstevel@tonic-gate 						pwrup_child = B_TRUE;
35107c478bd9Sstevel@tonic-gate 						mutex_exit(HUBD_MUTEX(hubd));
35117c478bd9Sstevel@tonic-gate 
35127c478bd9Sstevel@tonic-gate 						/*
35137c478bd9Sstevel@tonic-gate 						 * make sure that child
35147c478bd9Sstevel@tonic-gate 						 * doesn't disappear
35157c478bd9Sstevel@tonic-gate 						 */
35167c478bd9Sstevel@tonic-gate 						ndi_hold_devi(dip);
35177c478bd9Sstevel@tonic-gate 
35187c478bd9Sstevel@tonic-gate 						mutex_enter(HUBD_MUTEX(hubd));
35197c478bd9Sstevel@tonic-gate 					}
35207c478bd9Sstevel@tonic-gate 				}
35217c478bd9Sstevel@tonic-gate 			}
35227c478bd9Sstevel@tonic-gate 		}
35237c478bd9Sstevel@tonic-gate 	}
35247c478bd9Sstevel@tonic-gate 
35257c478bd9Sstevel@tonic-gate 	/* release locks so we can do a devfs_clean */
35267c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
35277c478bd9Sstevel@tonic-gate 
35287c478bd9Sstevel@tonic-gate 	/* delete cached dv_node's but drop locks first */
35297c478bd9Sstevel@tonic-gate 	ndi_devi_exit(hdip, circ);
35307c478bd9Sstevel@tonic-gate 	ndi_devi_exit(rh_dip, rh_circ);
35317c478bd9Sstevel@tonic-gate 	ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ);
35327c478bd9Sstevel@tonic-gate 
35337c478bd9Sstevel@tonic-gate 	(void) devfs_clean(rh_dip, NULL, 0);
35347c478bd9Sstevel@tonic-gate 
35357c478bd9Sstevel@tonic-gate 	/* now check if any children need onlining */
35367c478bd9Sstevel@tonic-gate 	if (online_child) {
35377c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
35387c478bd9Sstevel@tonic-gate 		    "hubd_hotplug_thread: onlining children");
35397c478bd9Sstevel@tonic-gate 
35407c478bd9Sstevel@tonic-gate 		(void) ndi_devi_online(hubd->h_dip, 0);
35417c478bd9Sstevel@tonic-gate 	}
35427c478bd9Sstevel@tonic-gate 
35437c478bd9Sstevel@tonic-gate 	/* now check if any disconnected devices need to be cleaned up */
35447c478bd9Sstevel@tonic-gate 	if (offline_child) {
35457c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
35467c478bd9Sstevel@tonic-gate 		    "hubd_hotplug_thread: scheduling cleanup");
35477c478bd9Sstevel@tonic-gate 
35487c478bd9Sstevel@tonic-gate 		hubd_schedule_cleanup(hubd->h_usba_device->usb_root_hub_dip);
35497c478bd9Sstevel@tonic-gate 	}
35507c478bd9Sstevel@tonic-gate 
35517c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
35527c478bd9Sstevel@tonic-gate 
35537c478bd9Sstevel@tonic-gate 	/* now raise power on the children that have woken up */
35547c478bd9Sstevel@tonic-gate 	if (pwrup_child) {
35557c478bd9Sstevel@tonic-gate 		old_state = hubd->h_dev_state;
35567c478bd9Sstevel@tonic-gate 		hubd->h_dev_state = USB_DEV_HUB_CHILD_PWRLVL;
35577c478bd9Sstevel@tonic-gate 		for (port = 1; port <= nports; port++) {
35587c478bd9Sstevel@tonic-gate 			if (hubd->h_port_state[port] & HUBD_CHILD_RAISE_POWER) {
35597c478bd9Sstevel@tonic-gate 				dev_info_t *dip = hubd->h_children_dips[port];
35607c478bd9Sstevel@tonic-gate 
35617c478bd9Sstevel@tonic-gate 				mutex_exit(HUBD_MUTEX(hubd));
35627c478bd9Sstevel@tonic-gate 
35637c478bd9Sstevel@tonic-gate 				/* Get the device to full power */
35647c478bd9Sstevel@tonic-gate 				(void) pm_busy_component(dip, 0);
35657c478bd9Sstevel@tonic-gate 				(void) pm_raise_power(dip, 0,
35667c478bd9Sstevel@tonic-gate 				    USB_DEV_OS_FULL_PWR);
35677c478bd9Sstevel@tonic-gate 				(void) pm_idle_component(dip, 0);
35687c478bd9Sstevel@tonic-gate 
35697c478bd9Sstevel@tonic-gate 				/* release the hold on the child */
35707c478bd9Sstevel@tonic-gate 				ndi_rele_devi(dip);
35717c478bd9Sstevel@tonic-gate 				mutex_enter(HUBD_MUTEX(hubd));
35727c478bd9Sstevel@tonic-gate 				hubd->h_port_state[port] &=
35737c478bd9Sstevel@tonic-gate 				    ~HUBD_CHILD_RAISE_POWER;
35747c478bd9Sstevel@tonic-gate 			}
35757c478bd9Sstevel@tonic-gate 		}
35767c478bd9Sstevel@tonic-gate 		/*
35777c478bd9Sstevel@tonic-gate 		 * make sure that we don't accidentally
35787c478bd9Sstevel@tonic-gate 		 * over write the disconnect state
35797c478bd9Sstevel@tonic-gate 		 */
35807c478bd9Sstevel@tonic-gate 		if (hubd->h_dev_state == USB_DEV_HUB_CHILD_PWRLVL) {
35817c478bd9Sstevel@tonic-gate 			hubd->h_dev_state = old_state;
35827c478bd9Sstevel@tonic-gate 		}
35837c478bd9Sstevel@tonic-gate 	}
35847c478bd9Sstevel@tonic-gate 
35857c478bd9Sstevel@tonic-gate 	/*
35867c478bd9Sstevel@tonic-gate 	 * start polling can immediately kick off read callback
35877c478bd9Sstevel@tonic-gate 	 * we need to set the h_hotplug_thread to 0 so that
35887c478bd9Sstevel@tonic-gate 	 * the callback is not dropped
35897c478bd9Sstevel@tonic-gate 	 */
35907c478bd9Sstevel@tonic-gate 	hubd_start_polling(hubd, HUBD_ALWAYS_START_POLLING);
35917c478bd9Sstevel@tonic-gate 
35927c478bd9Sstevel@tonic-gate 	/*
35937c478bd9Sstevel@tonic-gate 	 * Earlier we would set the h_hotplug_thread = 0 before
35947c478bd9Sstevel@tonic-gate 	 * polling was restarted  so that
35957c478bd9Sstevel@tonic-gate 	 * if there is any root hub status change interrupt, we can still kick
35967c478bd9Sstevel@tonic-gate 	 * off the hotplug thread. This was valid when this interrupt was
35977c478bd9Sstevel@tonic-gate 	 * delivered in hardware, and only ONE interrupt would be delivered.
35987c478bd9Sstevel@tonic-gate 	 * Now that we poll on the root hub looking for status change in
35997c478bd9Sstevel@tonic-gate 	 * software, this assignment is no longer required.
36007c478bd9Sstevel@tonic-gate 	 */
36017c478bd9Sstevel@tonic-gate 	hubd->h_hotplug_thread--;
36027c478bd9Sstevel@tonic-gate 
36037c478bd9Sstevel@tonic-gate 	/* mark this device as idle */
36047c478bd9Sstevel@tonic-gate 	(void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
36057c478bd9Sstevel@tonic-gate 
36067c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
36077c478bd9Sstevel@tonic-gate 	    "hubd_hotplug_thread: exit");
36087c478bd9Sstevel@tonic-gate 
36097c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
36107c478bd9Sstevel@tonic-gate 
36117c478bd9Sstevel@tonic-gate 	ndi_rele_devi(hdip);
36127c478bd9Sstevel@tonic-gate }
36137c478bd9Sstevel@tonic-gate 
36147c478bd9Sstevel@tonic-gate 
36157c478bd9Sstevel@tonic-gate /*
36167c478bd9Sstevel@tonic-gate  * hubd_handle_port_connect:
36177c478bd9Sstevel@tonic-gate  *	Transition a port from Disabled to Enabled.  Ensure that the
36187c478bd9Sstevel@tonic-gate  *	port is in the correct state before attempting to
36197c478bd9Sstevel@tonic-gate  *	access the device.
36207c478bd9Sstevel@tonic-gate  */
36217c478bd9Sstevel@tonic-gate static int
36227c478bd9Sstevel@tonic-gate hubd_handle_port_connect(hubd_t *hubd, usb_port_t port)
36237c478bd9Sstevel@tonic-gate {
36247c478bd9Sstevel@tonic-gate 	int			rval;
36257c478bd9Sstevel@tonic-gate 	int			retry;
36267c478bd9Sstevel@tonic-gate 	long			time_delay;
36277c478bd9Sstevel@tonic-gate 	long			settling_time;
36287c478bd9Sstevel@tonic-gate 	uint16_t		status;
36297c478bd9Sstevel@tonic-gate 	uint16_t		change;
36307c478bd9Sstevel@tonic-gate 	usb_addr_t		hubd_usb_addr;
36317c478bd9Sstevel@tonic-gate 	usba_device_t		*usba_device;
36327c478bd9Sstevel@tonic-gate 	usb_port_status_t	port_status = 0;
36337c478bd9Sstevel@tonic-gate 	usb_port_status_t	hub_port_status = 0;
36347c478bd9Sstevel@tonic-gate 
36357c478bd9Sstevel@tonic-gate 	/* Get the hub address and port status */
36367c478bd9Sstevel@tonic-gate 	usba_device = hubd->h_usba_device;
36377c478bd9Sstevel@tonic-gate 	mutex_enter(&usba_device->usb_mutex);
36387c478bd9Sstevel@tonic-gate 	hubd_usb_addr = usba_device->usb_addr;
36397c478bd9Sstevel@tonic-gate 	hub_port_status = usba_device->usb_port_status;
36407c478bd9Sstevel@tonic-gate 	mutex_exit(&usba_device->usb_mutex);
36417c478bd9Sstevel@tonic-gate 
36427c478bd9Sstevel@tonic-gate 	/*
36437c478bd9Sstevel@tonic-gate 	 * If a device is connected, transition the
36447c478bd9Sstevel@tonic-gate 	 * port from Disabled to the Enabled state.
36457c478bd9Sstevel@tonic-gate 	 * The device will receive downstream packets
36467c478bd9Sstevel@tonic-gate 	 * in the Enabled state.
36477c478bd9Sstevel@tonic-gate 	 *
36487c478bd9Sstevel@tonic-gate 	 * reset port and wait for the hub to report
36497c478bd9Sstevel@tonic-gate 	 * completion
36507c478bd9Sstevel@tonic-gate 	 */
36517c478bd9Sstevel@tonic-gate 	change = status = 0;
36527c478bd9Sstevel@tonic-gate 
36537c478bd9Sstevel@tonic-gate 	/*
36547c478bd9Sstevel@tonic-gate 	 * According to section 9.1.2 of USB 2.0 spec, the host should
36557c478bd9Sstevel@tonic-gate 	 * wait for atleast 100ms to allow completion of an insertion
36567c478bd9Sstevel@tonic-gate 	 * process and for power at the device to become stable.
36577c478bd9Sstevel@tonic-gate 	 * We wait for 200 ms
36587c478bd9Sstevel@tonic-gate 	 */
36597c478bd9Sstevel@tonic-gate 	settling_time = drv_usectohz(hubd_device_delay / 5);
36607c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
36617c478bd9Sstevel@tonic-gate 	delay(settling_time);
36627c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
36637c478bd9Sstevel@tonic-gate 
36647c478bd9Sstevel@tonic-gate 	/* calculate 600 ms delay time */
36657c478bd9Sstevel@tonic-gate 	time_delay = (6 * drv_usectohz(hubd_device_delay)) / 10;
36667c478bd9Sstevel@tonic-gate 
36677c478bd9Sstevel@tonic-gate 	for (retry = 0; (hubd->h_dev_state == USB_DEV_ONLINE) &&
36687c478bd9Sstevel@tonic-gate 	    (retry < hubd_retry_enumerate); retry++) {
36697c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
36707c478bd9Sstevel@tonic-gate 		    "resetting port%d, retry=%d", port, retry);
36717c478bd9Sstevel@tonic-gate 
36727c478bd9Sstevel@tonic-gate 		if ((rval = hubd_reset_port(hubd, port)) != USB_SUCCESS) {
36737c478bd9Sstevel@tonic-gate 			(void) hubd_determine_port_status(hubd,
36747c478bd9Sstevel@tonic-gate 			    port, &status, &change, 0);
36757c478bd9Sstevel@tonic-gate 
36767c478bd9Sstevel@tonic-gate 			/* continue only if port is still connected */
36777c478bd9Sstevel@tonic-gate 			if (status & PORT_STATUS_CCS) {
36787c478bd9Sstevel@tonic-gate 				continue;
36797c478bd9Sstevel@tonic-gate 			}
36807c478bd9Sstevel@tonic-gate 
36817c478bd9Sstevel@tonic-gate 			/* carry on regardless */
36827c478bd9Sstevel@tonic-gate 		}
36837c478bd9Sstevel@tonic-gate 
36847c478bd9Sstevel@tonic-gate 		/*
36857c478bd9Sstevel@tonic-gate 		 * according to USB 2.0 spec section 11.24.2.7.1.2
36867c478bd9Sstevel@tonic-gate 		 * at the end of port reset, the hub enables the port.
36877c478bd9Sstevel@tonic-gate 		 * But for some strange reasons, uhci port remains disabled.
36887c478bd9Sstevel@tonic-gate 		 * And because the port remains disabled for the settling
36897c478bd9Sstevel@tonic-gate 		 * time below, the device connected to the port gets wedged
36907c478bd9Sstevel@tonic-gate 		 * - fails to enumerate (device not responding)
36917c478bd9Sstevel@tonic-gate 		 * Hence, we enable it here immediately and later again after
36927c478bd9Sstevel@tonic-gate 		 * the delay
36937c478bd9Sstevel@tonic-gate 		 */
36947c478bd9Sstevel@tonic-gate 		(void) hubd_enable_port(hubd, port);
36957c478bd9Sstevel@tonic-gate 
36967c478bd9Sstevel@tonic-gate 		/* we skip this delay in the first iteration */
36977c478bd9Sstevel@tonic-gate 		if (retry) {
36987c478bd9Sstevel@tonic-gate 			/*
36997c478bd9Sstevel@tonic-gate 			 * delay for device to signal disconnect/connect so
37007c478bd9Sstevel@tonic-gate 			 * that hub properly recognizes the speed of the device
37017c478bd9Sstevel@tonic-gate 			 */
37027c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
37037c478bd9Sstevel@tonic-gate 			delay(settling_time);
37047c478bd9Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
37057c478bd9Sstevel@tonic-gate 
37067c478bd9Sstevel@tonic-gate 			/*
37077c478bd9Sstevel@tonic-gate 			 * When a low speed device is connected to any port of
37087c478bd9Sstevel@tonic-gate 			 * PPX it has to be explicitly enabled
37097c478bd9Sstevel@tonic-gate 			 * Also, if device intentionally signals
37107c478bd9Sstevel@tonic-gate 			 * disconnect/connect, it will disable the port.
37117c478bd9Sstevel@tonic-gate 			 * So enable it again.
37127c478bd9Sstevel@tonic-gate 			 */
37137c478bd9Sstevel@tonic-gate 			(void) hubd_enable_port(hubd, port);
37147c478bd9Sstevel@tonic-gate 		}
37157c478bd9Sstevel@tonic-gate 
37167c478bd9Sstevel@tonic-gate 		if ((rval = hubd_determine_port_status(hubd, port, &status,
37177c478bd9Sstevel@tonic-gate 		    &change, 0)) != USB_SUCCESS) {
37187c478bd9Sstevel@tonic-gate 
3719*d291d9f2Sfrits 			USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
37207c478bd9Sstevel@tonic-gate 			    "getting status failed (%d)", rval);
37217c478bd9Sstevel@tonic-gate 
37227c478bd9Sstevel@tonic-gate 			(void) hubd_disable_port(hubd, port);
37237c478bd9Sstevel@tonic-gate 
37247c478bd9Sstevel@tonic-gate 			continue;
37257c478bd9Sstevel@tonic-gate 		}
37267c478bd9Sstevel@tonic-gate 
37277c478bd9Sstevel@tonic-gate 		if (status & PORT_STATUS_POCI) {
3728*d291d9f2Sfrits 			USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
37297c478bd9Sstevel@tonic-gate 			    "port %d overcurrent", port);
37307c478bd9Sstevel@tonic-gate 
37317c478bd9Sstevel@tonic-gate 			(void) hubd_disable_port(hubd, port);
37327c478bd9Sstevel@tonic-gate 
37337c478bd9Sstevel@tonic-gate 			/* ack changes */
37347c478bd9Sstevel@tonic-gate 			(void) hubd_determine_port_status(hubd,
37357c478bd9Sstevel@tonic-gate 			    port, &status, &change, PORT_CHANGE_OCIC);
37367c478bd9Sstevel@tonic-gate 
37377c478bd9Sstevel@tonic-gate 			continue;
37387c478bd9Sstevel@tonic-gate 		}
37397c478bd9Sstevel@tonic-gate 
37407c478bd9Sstevel@tonic-gate 		/* is status really OK? */
37417c478bd9Sstevel@tonic-gate 		if ((status & PORT_STATUS_OK) != PORT_STATUS_OK) {
37427c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
37437c478bd9Sstevel@tonic-gate 			    "port %d status (0x%x) not OK on retry %d",
37447c478bd9Sstevel@tonic-gate 			    port, status, retry);
37457c478bd9Sstevel@tonic-gate 
37467c478bd9Sstevel@tonic-gate 			/* check if we still have the connection */
37477c478bd9Sstevel@tonic-gate 			if (!(status & PORT_STATUS_CCS)) {
37487c478bd9Sstevel@tonic-gate 				/* lost connection, set exit condition */
37497c478bd9Sstevel@tonic-gate 				retry = hubd_retry_enumerate;
37507c478bd9Sstevel@tonic-gate 
37517c478bd9Sstevel@tonic-gate 				break;
37527c478bd9Sstevel@tonic-gate 			}
37537c478bd9Sstevel@tonic-gate 		} else {
37547c478bd9Sstevel@tonic-gate 			/*
37557c478bd9Sstevel@tonic-gate 			 * Determine if the device is high or full
37567c478bd9Sstevel@tonic-gate 			 * or low speed.
37577c478bd9Sstevel@tonic-gate 			 */
37587c478bd9Sstevel@tonic-gate 			if (status & PORT_STATUS_LSDA) {
37597c478bd9Sstevel@tonic-gate 				port_status = USBA_LOW_SPEED_DEV;
37607c478bd9Sstevel@tonic-gate 			} else if (status & PORT_STATUS_HSDA) {
37617c478bd9Sstevel@tonic-gate 				port_status = USBA_HIGH_SPEED_DEV;
37627c478bd9Sstevel@tonic-gate 			} else {
37637c478bd9Sstevel@tonic-gate 				port_status = USBA_FULL_SPEED_DEV;
37647c478bd9Sstevel@tonic-gate 			}
37657c478bd9Sstevel@tonic-gate 
37667c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
37677c478bd9Sstevel@tonic-gate 			    "creating child port%d, status=0x%x "
37687c478bd9Sstevel@tonic-gate 			    "port status=0x%x",
37697c478bd9Sstevel@tonic-gate 			    port, status, port_status);
37707c478bd9Sstevel@tonic-gate 
37717c478bd9Sstevel@tonic-gate 			/*
37727c478bd9Sstevel@tonic-gate 			 * if the child already exists, set addrs and config
37737c478bd9Sstevel@tonic-gate 			 * to the device post connect event to the child
37747c478bd9Sstevel@tonic-gate 			 */
37757c478bd9Sstevel@tonic-gate 			if (hubd->h_children_dips[port]) {
37767c478bd9Sstevel@tonic-gate 				/* set addrs to this device */
37777c478bd9Sstevel@tonic-gate 				rval = hubd_setdevaddr(hubd, port);
37787c478bd9Sstevel@tonic-gate 
37797c478bd9Sstevel@tonic-gate 				/*
37807c478bd9Sstevel@tonic-gate 				 * This delay is important for the CATC hub
37817c478bd9Sstevel@tonic-gate 				 * to enumerate. But, avoid delay in the first
37827c478bd9Sstevel@tonic-gate 				 * iteration
37837c478bd9Sstevel@tonic-gate 				 */
37847c478bd9Sstevel@tonic-gate 				if (retry) {
37857c478bd9Sstevel@tonic-gate 					mutex_exit(HUBD_MUTEX(hubd));
37867c478bd9Sstevel@tonic-gate 					delay(drv_usectohz(
37877c478bd9Sstevel@tonic-gate 					    hubd_device_delay/100));
37887c478bd9Sstevel@tonic-gate 					mutex_enter(HUBD_MUTEX(hubd));
37897c478bd9Sstevel@tonic-gate 				}
37907c478bd9Sstevel@tonic-gate 
37917c478bd9Sstevel@tonic-gate 				if (rval == USB_SUCCESS) {
37927c478bd9Sstevel@tonic-gate 					/*
37937c478bd9Sstevel@tonic-gate 					 * set the default config for
37947c478bd9Sstevel@tonic-gate 					 * this device
37957c478bd9Sstevel@tonic-gate 					 */
37967c478bd9Sstevel@tonic-gate 					hubd_setdevconfig(hubd, port);
37977c478bd9Sstevel@tonic-gate 
37987c478bd9Sstevel@tonic-gate 					/*
37997c478bd9Sstevel@tonic-gate 					 * indicate to the child that
38007c478bd9Sstevel@tonic-gate 					 * it is online again
38017c478bd9Sstevel@tonic-gate 					 */
38027c478bd9Sstevel@tonic-gate 					mutex_exit(HUBD_MUTEX(hubd));
38037c478bd9Sstevel@tonic-gate 					hubd_post_event(hubd, port,
38047c478bd9Sstevel@tonic-gate 					    USBA_EVENT_TAG_HOT_INSERTION);
38057c478bd9Sstevel@tonic-gate 					mutex_enter(HUBD_MUTEX(hubd));
38067c478bd9Sstevel@tonic-gate 
38077c478bd9Sstevel@tonic-gate 					return (USB_SUCCESS);
38087c478bd9Sstevel@tonic-gate 				}
38097c478bd9Sstevel@tonic-gate 			} else {
38107c478bd9Sstevel@tonic-gate 				/*
38117c478bd9Sstevel@tonic-gate 				 * We need to release access here
38127c478bd9Sstevel@tonic-gate 				 * so that busctls on other ports can
38137c478bd9Sstevel@tonic-gate 				 * continue and don't cause a deadlock
38147c478bd9Sstevel@tonic-gate 				 * when busctl and removal of prom node
38157c478bd9Sstevel@tonic-gate 				 * takes concurrently. This also ensures
38167c478bd9Sstevel@tonic-gate 				 * busctls for attach of successfully
38177c478bd9Sstevel@tonic-gate 				 * enumerated devices on other ports can
38187c478bd9Sstevel@tonic-gate 				 * continue concurrently with the process
38197c478bd9Sstevel@tonic-gate 				 * of enumerating the new devices. This
38207c478bd9Sstevel@tonic-gate 				 * reduces the overall boot time of the system.
38217c478bd9Sstevel@tonic-gate 				 */
38227c478bd9Sstevel@tonic-gate 				rval = hubd_create_child(hubd->h_dip,
38237c478bd9Sstevel@tonic-gate 							hubd,
38247c478bd9Sstevel@tonic-gate 							hubd->h_usba_device,
38257c478bd9Sstevel@tonic-gate 							port_status, port,
38267c478bd9Sstevel@tonic-gate 							retry);
38277c478bd9Sstevel@tonic-gate 				if (rval == USB_SUCCESS) {
38287c478bd9Sstevel@tonic-gate 					usba_update_hotplug_stats(hubd->h_dip,
38297c478bd9Sstevel@tonic-gate 					    USBA_TOTAL_HOTPLUG_SUCCESS|
38307c478bd9Sstevel@tonic-gate 					    USBA_HOTPLUG_SUCCESS);
38317c478bd9Sstevel@tonic-gate 					hubd->h_total_hotplug_success++;
38327c478bd9Sstevel@tonic-gate 
38337c478bd9Sstevel@tonic-gate 					if (retry > 0) {
3834*d291d9f2Sfrits 						USB_DPRINTF_L2(
38357c478bd9Sstevel@tonic-gate 						    DPRINT_MASK_HOTPLUG,
38367c478bd9Sstevel@tonic-gate 						    hubd->h_log_handle,
38377c478bd9Sstevel@tonic-gate 						    "device on port %d "
38387c478bd9Sstevel@tonic-gate 						    "enumerated after %d %s",
38397c478bd9Sstevel@tonic-gate 						    port, retry,
38407c478bd9Sstevel@tonic-gate 						    (retry > 1) ? "retries" :
38417c478bd9Sstevel@tonic-gate 						    "retry");
38427c478bd9Sstevel@tonic-gate 
38437c478bd9Sstevel@tonic-gate 					}
38447c478bd9Sstevel@tonic-gate 
38457c478bd9Sstevel@tonic-gate 					return (USB_SUCCESS);
38467c478bd9Sstevel@tonic-gate 				}
38477c478bd9Sstevel@tonic-gate 			}
38487c478bd9Sstevel@tonic-gate 		}
38497c478bd9Sstevel@tonic-gate 
38507c478bd9Sstevel@tonic-gate 		/* wait a while until it settles? */
38517c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
38527c478bd9Sstevel@tonic-gate 		    "disabling port %d again", port);
38537c478bd9Sstevel@tonic-gate 
38547c478bd9Sstevel@tonic-gate 		(void) hubd_disable_port(hubd, port);
38557c478bd9Sstevel@tonic-gate 		if (retry) {
38567c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
38577c478bd9Sstevel@tonic-gate 			delay(time_delay);
38587c478bd9Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
38597c478bd9Sstevel@tonic-gate 		}
38607c478bd9Sstevel@tonic-gate 
38617c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
38627c478bd9Sstevel@tonic-gate 		    "retrying on port %d", port);
38637c478bd9Sstevel@tonic-gate 	}
38647c478bd9Sstevel@tonic-gate 
38657c478bd9Sstevel@tonic-gate 	if (retry >= hubd_retry_enumerate) {
38667c478bd9Sstevel@tonic-gate 		/*
38677c478bd9Sstevel@tonic-gate 		 * If it is a High Speed Root Hub and connected device
38687c478bd9Sstevel@tonic-gate 		 * Is a Low/Full Speed, it will be handled by USB 1.1
38697c478bd9Sstevel@tonic-gate 		 * Host Controller. In this case, USB 2.0 Host Controller
38707c478bd9Sstevel@tonic-gate 		 * will transfer the ownership of this port to USB 1.1
38717c478bd9Sstevel@tonic-gate 		 * Host Controller. So don't display any error message on
38727c478bd9Sstevel@tonic-gate 		 * the console.
38737c478bd9Sstevel@tonic-gate 		 */
38747c478bd9Sstevel@tonic-gate 		if ((hubd_usb_addr == ROOT_HUB_ADDR) &&
38757c478bd9Sstevel@tonic-gate 		    (hub_port_status == USBA_HIGH_SPEED_DEV) &&
38767c478bd9Sstevel@tonic-gate 		    (port_status != USBA_HIGH_SPEED_DEV)) {
38777c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
38787c478bd9Sstevel@tonic-gate 			    hubd->h_log_handle,
38797c478bd9Sstevel@tonic-gate 			    "hubd_handle_port_connect: Low/Full speed "
38807c478bd9Sstevel@tonic-gate 			    "device is connected to High Speed root hub");
38817c478bd9Sstevel@tonic-gate 		} else {
38827c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG,
38837c478bd9Sstevel@tonic-gate 			    hubd->h_log_handle,
38847c478bd9Sstevel@tonic-gate 			    "Connecting device on port %d failed", port);
38857c478bd9Sstevel@tonic-gate 		}
38867c478bd9Sstevel@tonic-gate 
38877c478bd9Sstevel@tonic-gate 		(void) hubd_disable_port(hubd, port);
38887c478bd9Sstevel@tonic-gate 		usba_update_hotplug_stats(hubd->h_dip,
38897c478bd9Sstevel@tonic-gate 		    USBA_TOTAL_HOTPLUG_FAILURE|USBA_HOTPLUG_FAILURE);
38907c478bd9Sstevel@tonic-gate 		hubd->h_total_hotplug_failure++;
38917c478bd9Sstevel@tonic-gate 
38927c478bd9Sstevel@tonic-gate 		/*
38937c478bd9Sstevel@tonic-gate 		 * the port should be automagically
38947c478bd9Sstevel@tonic-gate 		 * disabled but just in case, we do
38957c478bd9Sstevel@tonic-gate 		 * it here
38967c478bd9Sstevel@tonic-gate 		 */
38977c478bd9Sstevel@tonic-gate 		(void) hubd_disable_port(hubd, port);
38987c478bd9Sstevel@tonic-gate 
38997c478bd9Sstevel@tonic-gate 		/* ack all changes because we disabled this port */
39007c478bd9Sstevel@tonic-gate 		(void) hubd_determine_port_status(hubd,
39017c478bd9Sstevel@tonic-gate 		    port, &status, &change, HUBD_ACK_ALL_CHANGES);
39027c478bd9Sstevel@tonic-gate 
39037c478bd9Sstevel@tonic-gate 	}
39047c478bd9Sstevel@tonic-gate 
39057c478bd9Sstevel@tonic-gate 	return (USB_FAILURE);
39067c478bd9Sstevel@tonic-gate }
39077c478bd9Sstevel@tonic-gate 
39087c478bd9Sstevel@tonic-gate 
39097c478bd9Sstevel@tonic-gate /*
39107c478bd9Sstevel@tonic-gate  * hubd_get_hub_status:
39117c478bd9Sstevel@tonic-gate  */
39127c478bd9Sstevel@tonic-gate static int
39137c478bd9Sstevel@tonic-gate hubd_get_hub_status(hubd_t *hubd)
39147c478bd9Sstevel@tonic-gate {
39157c478bd9Sstevel@tonic-gate 	int		rval;
39167c478bd9Sstevel@tonic-gate 	usb_cr_t	completion_reason;
39177c478bd9Sstevel@tonic-gate 	usb_cb_flags_t	cb_flags;
39187c478bd9Sstevel@tonic-gate 	mblk_t		*data = NULL;
39197c478bd9Sstevel@tonic-gate 	uint16_t	status;
39207c478bd9Sstevel@tonic-gate 	uint16_t	change;
39217c478bd9Sstevel@tonic-gate 	usb_cfg_descr_t	cfg_descr;
39227c478bd9Sstevel@tonic-gate 	size_t		cfg_length;
39237c478bd9Sstevel@tonic-gate 	uchar_t		*usb_cfg;
39247c478bd9Sstevel@tonic-gate 	uint8_t		MaxPower;
39257c478bd9Sstevel@tonic-gate 
39267c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
39277c478bd9Sstevel@tonic-gate 	if (usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe,
39287c478bd9Sstevel@tonic-gate 	    HUB_CLASS_REQ,
39297c478bd9Sstevel@tonic-gate 	    USB_REQ_GET_STATUS,
39307c478bd9Sstevel@tonic-gate 	    0,
39317c478bd9Sstevel@tonic-gate 	    0,
39327c478bd9Sstevel@tonic-gate 	    GET_STATUS_LENGTH,
39337c478bd9Sstevel@tonic-gate 	    &data, 0,
39347c478bd9Sstevel@tonic-gate 	    &completion_reason, &cb_flags, 0) != USB_SUCCESS) {
39357c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
39367c478bd9Sstevel@tonic-gate 		    "get hub status failed: cr=%d cb=0x%x",
39377c478bd9Sstevel@tonic-gate 		    completion_reason, cb_flags);
39387c478bd9Sstevel@tonic-gate 
39397c478bd9Sstevel@tonic-gate 		if (data) {
39407c478bd9Sstevel@tonic-gate 			freemsg(data);
39417c478bd9Sstevel@tonic-gate 		}
39427c478bd9Sstevel@tonic-gate 
39437c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
39447c478bd9Sstevel@tonic-gate 
39457c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
39467c478bd9Sstevel@tonic-gate 	}
39477c478bd9Sstevel@tonic-gate 
39487c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
39497c478bd9Sstevel@tonic-gate 
39507c478bd9Sstevel@tonic-gate 	status = (*(data->b_rptr + 1) << 8) | *(data->b_rptr);
39517c478bd9Sstevel@tonic-gate 	change = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2);
39527c478bd9Sstevel@tonic-gate 
39537c478bd9Sstevel@tonic-gate 	if (status || change) {
39547c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
39557c478bd9Sstevel@tonic-gate 		    "hub status = 0x%x change = 0x%x", status, change);
39567c478bd9Sstevel@tonic-gate 	}
39577c478bd9Sstevel@tonic-gate 
39587c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
39597c478bd9Sstevel@tonic-gate 	freemsg(data);
39607c478bd9Sstevel@tonic-gate 
39617c478bd9Sstevel@tonic-gate 	/* Obtain the raw configuration descriptor */
39627c478bd9Sstevel@tonic-gate 	usb_cfg = usb_get_raw_cfg_data(hubd->h_dip, &cfg_length);
39637c478bd9Sstevel@tonic-gate 
39647c478bd9Sstevel@tonic-gate 	/* get configuration descriptor */
39657c478bd9Sstevel@tonic-gate 	rval = usb_parse_cfg_descr(usb_cfg, cfg_length,
39667c478bd9Sstevel@tonic-gate 				&cfg_descr, USB_CFG_DESCR_SIZE);
39677c478bd9Sstevel@tonic-gate 
39687c478bd9Sstevel@tonic-gate 	if (rval != USB_CFG_DESCR_SIZE) {
39697c478bd9Sstevel@tonic-gate 
39707c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
39717c478bd9Sstevel@tonic-gate 		    "get hub configuration descriptor failed.");
39727c478bd9Sstevel@tonic-gate 
39737c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
39747c478bd9Sstevel@tonic-gate 
39757c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
39767c478bd9Sstevel@tonic-gate 	} else {
39777c478bd9Sstevel@tonic-gate 		MaxPower = cfg_descr.bMaxPower;
39787c478bd9Sstevel@tonic-gate 	}
39797c478bd9Sstevel@tonic-gate 
39807c478bd9Sstevel@tonic-gate 	/* check if local power status changed. */
39817c478bd9Sstevel@tonic-gate 	if (change & C_HUB_LOCAL_POWER_STATUS) {
39827c478bd9Sstevel@tonic-gate 
39837c478bd9Sstevel@tonic-gate 		/*
39847c478bd9Sstevel@tonic-gate 		 * local power has been lost, check the maximum
39857c478bd9Sstevel@tonic-gate 		 * power consumption of current configuration.
39867c478bd9Sstevel@tonic-gate 		 * see USB2.0 spec Table 11-12.
39877c478bd9Sstevel@tonic-gate 		 */
39887c478bd9Sstevel@tonic-gate 		if (status & HUB_LOCAL_POWER_STATUS) {
39897c478bd9Sstevel@tonic-gate 
39907c478bd9Sstevel@tonic-gate 			if (MaxPower == 0) {
39917c478bd9Sstevel@tonic-gate 
39927c478bd9Sstevel@tonic-gate 				/*
39937c478bd9Sstevel@tonic-gate 				 * Self-powered only hub. Because it could
39947c478bd9Sstevel@tonic-gate 				 * not draw any power from USB bus.
39957c478bd9Sstevel@tonic-gate 				 * It can't work well on this condition.
39967c478bd9Sstevel@tonic-gate 				 */
39977c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L1(DPRINT_MASK_PORT,
39987c478bd9Sstevel@tonic-gate 				    hubd->h_log_handle,
39997c478bd9Sstevel@tonic-gate 				    "local power has been lost, "
40007c478bd9Sstevel@tonic-gate 				    "please disconnect hub");
40017c478bd9Sstevel@tonic-gate 			} else {
40027c478bd9Sstevel@tonic-gate 
40037c478bd9Sstevel@tonic-gate 				/*
40047c478bd9Sstevel@tonic-gate 				 * Bus-powered only or self/bus-powered hub.
40057c478bd9Sstevel@tonic-gate 				 */
40067c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L1(DPRINT_MASK_PORT,
40077c478bd9Sstevel@tonic-gate 				    hubd->h_log_handle,
40087c478bd9Sstevel@tonic-gate 				    "local power has been lost,"
40097c478bd9Sstevel@tonic-gate 				    "the hub could draw %d"
40107c478bd9Sstevel@tonic-gate 				    " mA power from the USB bus.",
40117c478bd9Sstevel@tonic-gate 				    2*MaxPower);
40127c478bd9Sstevel@tonic-gate 			}
40137c478bd9Sstevel@tonic-gate 
40147c478bd9Sstevel@tonic-gate 		}
40157c478bd9Sstevel@tonic-gate 
40167c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
40177c478bd9Sstevel@tonic-gate 		    "clearing feature C_HUB_LOCAL_POWER ");
40187c478bd9Sstevel@tonic-gate 
40197c478bd9Sstevel@tonic-gate 		if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
40207c478bd9Sstevel@tonic-gate 		    hubd->h_default_pipe,
40217c478bd9Sstevel@tonic-gate 		    USB_DEV_REQ_TYPE_CLASS,
40227c478bd9Sstevel@tonic-gate 		    USB_REQ_CLEAR_FEATURE,
40237c478bd9Sstevel@tonic-gate 		    CFS_C_HUB_LOCAL_POWER,
40247c478bd9Sstevel@tonic-gate 		    0,
40257c478bd9Sstevel@tonic-gate 		    0,
40267c478bd9Sstevel@tonic-gate 		    NULL, 0,
40277c478bd9Sstevel@tonic-gate 		    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
40287c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_PORT,
40297c478bd9Sstevel@tonic-gate 			    hubd->h_log_handle,
40307c478bd9Sstevel@tonic-gate 			    "clear feature C_HUB_LOCAL_POWER "
40317c478bd9Sstevel@tonic-gate 			    "failed (%d 0x%x %d)",
40327c478bd9Sstevel@tonic-gate 			    rval, completion_reason, cb_flags);
40337c478bd9Sstevel@tonic-gate 		}
40347c478bd9Sstevel@tonic-gate 
40357c478bd9Sstevel@tonic-gate 	}
40367c478bd9Sstevel@tonic-gate 
40377c478bd9Sstevel@tonic-gate 	if (change & C_HUB_OVER_CURRENT) {
40387c478bd9Sstevel@tonic-gate 
40397c478bd9Sstevel@tonic-gate 		if (status & HUB_OVER_CURRENT) {
40407c478bd9Sstevel@tonic-gate 			/*
40417c478bd9Sstevel@tonic-gate 			 * the user must offline this hub in order to recover.
40427c478bd9Sstevel@tonic-gate 			 * the port power is automatically disabled, so we
40437c478bd9Sstevel@tonic-gate 			 * won't see disconnects.
40447c478bd9Sstevel@tonic-gate 			 */
4045*d291d9f2Sfrits 			USB_DPRINTF_L0(DPRINT_MASK_PORT, hubd->h_log_handle,
40467c478bd9Sstevel@tonic-gate 			    "global over current condition, "
40477c478bd9Sstevel@tonic-gate 			    "please disconnect hub");
40487c478bd9Sstevel@tonic-gate 		}
40497c478bd9Sstevel@tonic-gate 
40507c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
40517c478bd9Sstevel@tonic-gate 		    "clearing feature C_HUB_OVER_CURRENT");
40527c478bd9Sstevel@tonic-gate 
40537c478bd9Sstevel@tonic-gate 		if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
40547c478bd9Sstevel@tonic-gate 		    hubd->h_default_pipe,
40557c478bd9Sstevel@tonic-gate 		    USB_DEV_REQ_TYPE_CLASS,
40567c478bd9Sstevel@tonic-gate 		    USB_REQ_CLEAR_FEATURE,
40577c478bd9Sstevel@tonic-gate 		    CFS_C_HUB_OVER_CURRENT,
40587c478bd9Sstevel@tonic-gate 		    0,
40597c478bd9Sstevel@tonic-gate 		    0,
40607c478bd9Sstevel@tonic-gate 		    NULL, 0,
40617c478bd9Sstevel@tonic-gate 		    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
40627c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_PORT,
40637c478bd9Sstevel@tonic-gate 			    hubd->h_log_handle,
40647c478bd9Sstevel@tonic-gate 			    "clear feature C_HUB_OVER_CURRENT "
40657c478bd9Sstevel@tonic-gate 			    "failed (%d 0x%x %d)",
40667c478bd9Sstevel@tonic-gate 			    rval, completion_reason, cb_flags);
40677c478bd9Sstevel@tonic-gate 		}
40687c478bd9Sstevel@tonic-gate 	}
40697c478bd9Sstevel@tonic-gate 
40707c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
40717c478bd9Sstevel@tonic-gate 
40727c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
40737c478bd9Sstevel@tonic-gate }
40747c478bd9Sstevel@tonic-gate 
40757c478bd9Sstevel@tonic-gate 
40767c478bd9Sstevel@tonic-gate /*
40777c478bd9Sstevel@tonic-gate  * hubd_reset_port:
40787c478bd9Sstevel@tonic-gate  */
40797c478bd9Sstevel@tonic-gate static int
40807c478bd9Sstevel@tonic-gate hubd_reset_port(hubd_t *hubd, usb_port_t port)
40817c478bd9Sstevel@tonic-gate {
40827c478bd9Sstevel@tonic-gate 	int	rval;
40837c478bd9Sstevel@tonic-gate 	usb_cr_t completion_reason;
40847c478bd9Sstevel@tonic-gate 	usb_cb_flags_t cb_flags;
40857c478bd9Sstevel@tonic-gate 	usb_port_mask_t port_mask = 1 << port;
40867c478bd9Sstevel@tonic-gate 	mblk_t	*data;
40877c478bd9Sstevel@tonic-gate 	uint16_t status;
40887c478bd9Sstevel@tonic-gate 	uint16_t change;
40897c478bd9Sstevel@tonic-gate 	int	i;
40907c478bd9Sstevel@tonic-gate 	clock_t	current_time;
40917c478bd9Sstevel@tonic-gate 
40927c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
40937c478bd9Sstevel@tonic-gate 	    "hubd_reset_port: port=%d", port);
40947c478bd9Sstevel@tonic-gate 
40957c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
40967c478bd9Sstevel@tonic-gate 
40977c478bd9Sstevel@tonic-gate 	hubd->h_port_reset_wait |= port_mask;
40987c478bd9Sstevel@tonic-gate 
40997c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
41007c478bd9Sstevel@tonic-gate 
41017c478bd9Sstevel@tonic-gate 	if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
41027c478bd9Sstevel@tonic-gate 	    hubd->h_default_pipe,
41037c478bd9Sstevel@tonic-gate 	    HANDLE_PORT_FEATURE,
41047c478bd9Sstevel@tonic-gate 	    USB_REQ_SET_FEATURE,
41057c478bd9Sstevel@tonic-gate 	    CFS_PORT_RESET,
41067c478bd9Sstevel@tonic-gate 	    port,
41077c478bd9Sstevel@tonic-gate 	    0,
41087c478bd9Sstevel@tonic-gate 	    NULL, 0,
41097c478bd9Sstevel@tonic-gate 	    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
4110*d291d9f2Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
41117c478bd9Sstevel@tonic-gate 		    "reset port%d failed (%d 0x%x %d)",
41127c478bd9Sstevel@tonic-gate 		    port, completion_reason, cb_flags, rval);
41137c478bd9Sstevel@tonic-gate 
41147c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
41157c478bd9Sstevel@tonic-gate 
41167c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
41177c478bd9Sstevel@tonic-gate 	}
41187c478bd9Sstevel@tonic-gate 
41197c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
41207c478bd9Sstevel@tonic-gate 
41217c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
41227c478bd9Sstevel@tonic-gate 	    "waiting on cv for reset completion");
41237c478bd9Sstevel@tonic-gate 
41247c478bd9Sstevel@tonic-gate 	/*
41257c478bd9Sstevel@tonic-gate 	 * wait for port status change event
41267c478bd9Sstevel@tonic-gate 	 */
41277c478bd9Sstevel@tonic-gate 	for (i = 0; i < hubd_retry_enumerate; i++) {
41287c478bd9Sstevel@tonic-gate 		/*
41297c478bd9Sstevel@tonic-gate 		 * start polling ep1 for receiving notification on
41307c478bd9Sstevel@tonic-gate 		 * reset completion
41317c478bd9Sstevel@tonic-gate 		 */
41327c478bd9Sstevel@tonic-gate 		hubd_start_polling(hubd, HUBD_ALWAYS_START_POLLING);
41337c478bd9Sstevel@tonic-gate 
41347c478bd9Sstevel@tonic-gate 		/*
41357c478bd9Sstevel@tonic-gate 		 * sleep a max of 100ms for reset completion
41367c478bd9Sstevel@tonic-gate 		 * notification to be received
41377c478bd9Sstevel@tonic-gate 		 */
41387c478bd9Sstevel@tonic-gate 		current_time = ddi_get_lbolt();
41397c478bd9Sstevel@tonic-gate 		if (hubd->h_port_reset_wait & port_mask) {
41407c478bd9Sstevel@tonic-gate 			rval = cv_timedwait(&hubd->h_cv_reset_port,
41417c478bd9Sstevel@tonic-gate 				&hubd->h_mutex,
41427c478bd9Sstevel@tonic-gate 				current_time +
41437c478bd9Sstevel@tonic-gate 				drv_usectohz(hubd_device_delay / 10));
41447c478bd9Sstevel@tonic-gate 			if ((rval <= 0) &&
41457c478bd9Sstevel@tonic-gate 			    (hubd->h_port_reset_wait & port_mask)) {
41467c478bd9Sstevel@tonic-gate 				/* we got woken up because of a timeout */
41477c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_PORT,
41487c478bd9Sstevel@tonic-gate 				    hubd->h_log_handle,
41497c478bd9Sstevel@tonic-gate 				    "timeout: reset port=%d failed", port);
41507c478bd9Sstevel@tonic-gate 
41517c478bd9Sstevel@tonic-gate 				hubd->h_port_reset_wait &=  ~port_mask;
41527c478bd9Sstevel@tonic-gate 
41537c478bd9Sstevel@tonic-gate 				hubd_stop_polling(hubd);
41547c478bd9Sstevel@tonic-gate 
41557c478bd9Sstevel@tonic-gate 				return (USB_FAILURE);
41567c478bd9Sstevel@tonic-gate 			}
41577c478bd9Sstevel@tonic-gate 		}
41587c478bd9Sstevel@tonic-gate 
41597c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
41607c478bd9Sstevel@tonic-gate 		    "reset completion received");
41617c478bd9Sstevel@tonic-gate 
41627c478bd9Sstevel@tonic-gate 		hubd_stop_polling(hubd);
41637c478bd9Sstevel@tonic-gate 
41647c478bd9Sstevel@tonic-gate 		data = NULL;
41657c478bd9Sstevel@tonic-gate 
41667c478bd9Sstevel@tonic-gate 		/* check status to determine whether reset completed */
41677c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
41687c478bd9Sstevel@tonic-gate 		if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
41697c478bd9Sstevel@tonic-gate 		    hubd->h_default_pipe,
41707c478bd9Sstevel@tonic-gate 		    GET_PORT_STATUS,
41717c478bd9Sstevel@tonic-gate 		    USB_REQ_GET_STATUS,
41727c478bd9Sstevel@tonic-gate 		    0,
41737c478bd9Sstevel@tonic-gate 		    port,
41747c478bd9Sstevel@tonic-gate 		    GET_STATUS_LENGTH,
41757c478bd9Sstevel@tonic-gate 		    &data, 0,
41767c478bd9Sstevel@tonic-gate 		    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
4177*d291d9f2Sfrits 			USB_DPRINTF_L2(DPRINT_MASK_PORT,
41787c478bd9Sstevel@tonic-gate 			    hubd->h_log_handle,
41797c478bd9Sstevel@tonic-gate 			    "get status port%d failed (%d 0x%x %d)",
41807c478bd9Sstevel@tonic-gate 			    port, completion_reason, cb_flags, rval);
41817c478bd9Sstevel@tonic-gate 
41827c478bd9Sstevel@tonic-gate 			if (data) {
41837c478bd9Sstevel@tonic-gate 				freemsg(data);
41847c478bd9Sstevel@tonic-gate 				data = NULL;
41857c478bd9Sstevel@tonic-gate 			}
41867c478bd9Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
41877c478bd9Sstevel@tonic-gate 
41887c478bd9Sstevel@tonic-gate 			continue;
41897c478bd9Sstevel@tonic-gate 		}
41907c478bd9Sstevel@tonic-gate 
41917c478bd9Sstevel@tonic-gate 		status = (*(data->b_rptr + 1) << 8) | *(data->b_rptr);
41927c478bd9Sstevel@tonic-gate 		change = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2);
41937c478bd9Sstevel@tonic-gate 
41947c478bd9Sstevel@tonic-gate 		freemsg(data);
41957c478bd9Sstevel@tonic-gate 
41967c478bd9Sstevel@tonic-gate 		/* continue only if port is still connected */
41977c478bd9Sstevel@tonic-gate 		if (!(status & PORT_STATUS_CCS)) {
41987c478bd9Sstevel@tonic-gate 
41997c478bd9Sstevel@tonic-gate 			/* lost connection, set exit condition */
42007c478bd9Sstevel@tonic-gate 			i = hubd_retry_enumerate;
42017c478bd9Sstevel@tonic-gate 
42027c478bd9Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
42037c478bd9Sstevel@tonic-gate 
42047c478bd9Sstevel@tonic-gate 			break;
42057c478bd9Sstevel@tonic-gate 		}
42067c478bd9Sstevel@tonic-gate 
42077c478bd9Sstevel@tonic-gate 		if (status & PORT_STATUS_PRS) {
42087c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
42097c478bd9Sstevel@tonic-gate 			    "port%d reset active", port);
42107c478bd9Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
42117c478bd9Sstevel@tonic-gate 
42127c478bd9Sstevel@tonic-gate 			continue;
42137c478bd9Sstevel@tonic-gate 		} else {
42147c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
42157c478bd9Sstevel@tonic-gate 			    "port%d reset inactive", port);
42167c478bd9Sstevel@tonic-gate 		}
42177c478bd9Sstevel@tonic-gate 
42187c478bd9Sstevel@tonic-gate 		if (change & PORT_CHANGE_PRSC) {
42197c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
42207c478bd9Sstevel@tonic-gate 			    "clearing feature CFS_C_PORT_RESET");
42217c478bd9Sstevel@tonic-gate 
42227c478bd9Sstevel@tonic-gate 			if (usb_pipe_sync_ctrl_xfer(hubd->h_dip,
42237c478bd9Sstevel@tonic-gate 			    hubd->h_default_pipe,
42247c478bd9Sstevel@tonic-gate 			    HANDLE_PORT_FEATURE,
42257c478bd9Sstevel@tonic-gate 			    USB_REQ_CLEAR_FEATURE,
42267c478bd9Sstevel@tonic-gate 			    CFS_C_PORT_RESET,
42277c478bd9Sstevel@tonic-gate 			    port,
42287c478bd9Sstevel@tonic-gate 			    0,
42297c478bd9Sstevel@tonic-gate 			    NULL, 0,
42307c478bd9Sstevel@tonic-gate 			    &completion_reason, &cb_flags, 0) != USB_SUCCESS) {
42317c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_PORT,
42327c478bd9Sstevel@tonic-gate 				    hubd->h_log_handle,
42337c478bd9Sstevel@tonic-gate 				    "clear feature CFS_C_PORT_RESET"
42347c478bd9Sstevel@tonic-gate 				    " port%d failed (%d 0x%x %d)",
42357c478bd9Sstevel@tonic-gate 				    port, completion_reason, cb_flags, rval);
42367c478bd9Sstevel@tonic-gate 			}
42377c478bd9Sstevel@tonic-gate 		}
42387c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
42397c478bd9Sstevel@tonic-gate 
42407c478bd9Sstevel@tonic-gate 		break;
42417c478bd9Sstevel@tonic-gate 	}
42427c478bd9Sstevel@tonic-gate 
42437c478bd9Sstevel@tonic-gate 	if (i >= hubd_retry_enumerate) {
42447c478bd9Sstevel@tonic-gate 		/* port reset has failed */
42457c478bd9Sstevel@tonic-gate 		rval = USB_FAILURE;
42467c478bd9Sstevel@tonic-gate 	}
42477c478bd9Sstevel@tonic-gate 
42487c478bd9Sstevel@tonic-gate 	return (rval);
42497c478bd9Sstevel@tonic-gate }
42507c478bd9Sstevel@tonic-gate 
42517c478bd9Sstevel@tonic-gate 
42527c478bd9Sstevel@tonic-gate /*
42537c478bd9Sstevel@tonic-gate  * hubd_enable_port:
42547c478bd9Sstevel@tonic-gate  *	this may fail if the hub as been disconnected
42557c478bd9Sstevel@tonic-gate  */
42567c478bd9Sstevel@tonic-gate static int
42577c478bd9Sstevel@tonic-gate hubd_enable_port(hubd_t *hubd, usb_port_t port)
42587c478bd9Sstevel@tonic-gate {
42597c478bd9Sstevel@tonic-gate 	int	rval;
42607c478bd9Sstevel@tonic-gate 	usb_cr_t completion_reason;
42617c478bd9Sstevel@tonic-gate 	usb_cb_flags_t cb_flags;
42627c478bd9Sstevel@tonic-gate 
42637c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
42647c478bd9Sstevel@tonic-gate 	    "hubd_enable_port: port=%d", port);
42657c478bd9Sstevel@tonic-gate 
42667c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
42677c478bd9Sstevel@tonic-gate 
42687c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
42697c478bd9Sstevel@tonic-gate 
42707c478bd9Sstevel@tonic-gate 	/* Do not issue a SetFeature(PORT_ENABLE) on external hubs */
42717c478bd9Sstevel@tonic-gate 	if (!usba_is_root_hub(hubd->h_dip)) {
42727c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
42737c478bd9Sstevel@tonic-gate 
42747c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
42757c478bd9Sstevel@tonic-gate 	}
42767c478bd9Sstevel@tonic-gate 
42777c478bd9Sstevel@tonic-gate 	if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
42787c478bd9Sstevel@tonic-gate 	    hubd->h_default_pipe,
42797c478bd9Sstevel@tonic-gate 	    HANDLE_PORT_FEATURE,
42807c478bd9Sstevel@tonic-gate 	    USB_REQ_SET_FEATURE,
42817c478bd9Sstevel@tonic-gate 	    CFS_PORT_ENABLE,
42827c478bd9Sstevel@tonic-gate 	    port,
42837c478bd9Sstevel@tonic-gate 	    0,
42847c478bd9Sstevel@tonic-gate 	    NULL, 0,
42857c478bd9Sstevel@tonic-gate 	    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
42867c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
42877c478bd9Sstevel@tonic-gate 		    "enable port%d failed (%d 0x%x %d)",
42887c478bd9Sstevel@tonic-gate 		    port, completion_reason, cb_flags, rval);
42897c478bd9Sstevel@tonic-gate 	}
42907c478bd9Sstevel@tonic-gate 
42917c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
42927c478bd9Sstevel@tonic-gate 
42937c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
42947c478bd9Sstevel@tonic-gate 	    "enabling port done");
42957c478bd9Sstevel@tonic-gate 
42967c478bd9Sstevel@tonic-gate 	return (rval);
42977c478bd9Sstevel@tonic-gate }
42987c478bd9Sstevel@tonic-gate 
42997c478bd9Sstevel@tonic-gate 
43007c478bd9Sstevel@tonic-gate /*
43017c478bd9Sstevel@tonic-gate  * hubd_disable_port
43027c478bd9Sstevel@tonic-gate  */
43037c478bd9Sstevel@tonic-gate static int
43047c478bd9Sstevel@tonic-gate hubd_disable_port(hubd_t *hubd, usb_port_t port)
43057c478bd9Sstevel@tonic-gate {
43067c478bd9Sstevel@tonic-gate 	int	rval;
43077c478bd9Sstevel@tonic-gate 	usb_cr_t completion_reason;
43087c478bd9Sstevel@tonic-gate 	usb_cb_flags_t cb_flags;
43097c478bd9Sstevel@tonic-gate 
43107c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
43117c478bd9Sstevel@tonic-gate 	    "hubd_disable_port: port=%d", port);
43127c478bd9Sstevel@tonic-gate 
43137c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
43147c478bd9Sstevel@tonic-gate 
43157c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
43167c478bd9Sstevel@tonic-gate 
43177c478bd9Sstevel@tonic-gate 	if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
43187c478bd9Sstevel@tonic-gate 	    hubd->h_default_pipe,
43197c478bd9Sstevel@tonic-gate 	    HANDLE_PORT_FEATURE,
43207c478bd9Sstevel@tonic-gate 	    USB_REQ_CLEAR_FEATURE,
43217c478bd9Sstevel@tonic-gate 	    CFS_PORT_ENABLE,
43227c478bd9Sstevel@tonic-gate 	    port,
43237c478bd9Sstevel@tonic-gate 	    0,
43247c478bd9Sstevel@tonic-gate 	    NULL, 0,
43257c478bd9Sstevel@tonic-gate 	    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
43267c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
43277c478bd9Sstevel@tonic-gate 		    "disable port%d failed (%d 0x%x %d)", port,
43287c478bd9Sstevel@tonic-gate 		    completion_reason, cb_flags, rval);
43297c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
43307c478bd9Sstevel@tonic-gate 
43317c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
43327c478bd9Sstevel@tonic-gate 	}
43337c478bd9Sstevel@tonic-gate 
43347c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
43357c478bd9Sstevel@tonic-gate 	    "clearing feature CFS_C_PORT_ENABLE");
43367c478bd9Sstevel@tonic-gate 
43377c478bd9Sstevel@tonic-gate 	if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
43387c478bd9Sstevel@tonic-gate 	    hubd->h_default_pipe,
43397c478bd9Sstevel@tonic-gate 	    HANDLE_PORT_FEATURE,
43407c478bd9Sstevel@tonic-gate 	    USB_REQ_CLEAR_FEATURE,
43417c478bd9Sstevel@tonic-gate 	    CFS_C_PORT_ENABLE,
43427c478bd9Sstevel@tonic-gate 	    port,
43437c478bd9Sstevel@tonic-gate 	    0,
43447c478bd9Sstevel@tonic-gate 	    NULL, 0,
43457c478bd9Sstevel@tonic-gate 	    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
43467c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_PORT,
43477c478bd9Sstevel@tonic-gate 		    hubd->h_log_handle,
43487c478bd9Sstevel@tonic-gate 		    "clear feature CFS_C_PORT_ENABLE port%d failed "
43497c478bd9Sstevel@tonic-gate 		    "(%d 0x%x %d)",
43507c478bd9Sstevel@tonic-gate 		    port, completion_reason, cb_flags, rval);
43517c478bd9Sstevel@tonic-gate 
43527c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
43537c478bd9Sstevel@tonic-gate 
43547c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
43557c478bd9Sstevel@tonic-gate 	}
43567c478bd9Sstevel@tonic-gate 
43577c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
43587c478bd9Sstevel@tonic-gate 
43597c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
43607c478bd9Sstevel@tonic-gate }
43617c478bd9Sstevel@tonic-gate 
43627c478bd9Sstevel@tonic-gate 
43637c478bd9Sstevel@tonic-gate /*
43647c478bd9Sstevel@tonic-gate  * hubd_determine_port_status:
43657c478bd9Sstevel@tonic-gate  */
43667c478bd9Sstevel@tonic-gate static int
43677c478bd9Sstevel@tonic-gate hubd_determine_port_status(hubd_t *hubd, usb_port_t port,
43687c478bd9Sstevel@tonic-gate 		uint16_t *status, uint16_t *change, uint_t ack_flag)
43697c478bd9Sstevel@tonic-gate {
43707c478bd9Sstevel@tonic-gate 	int rval;
43717c478bd9Sstevel@tonic-gate 	mblk_t	*data = NULL;
43727c478bd9Sstevel@tonic-gate 	usb_cr_t completion_reason;
43737c478bd9Sstevel@tonic-gate 	usb_cb_flags_t cb_flags;
43747c478bd9Sstevel@tonic-gate 
43757c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
43767c478bd9Sstevel@tonic-gate 	    "hubd_determine_port_status: port=%d, state=0x%x ack=0x%x", port,
43777c478bd9Sstevel@tonic-gate 	    hubd->h_port_state[port], ack_flag);
43787c478bd9Sstevel@tonic-gate 
43797c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
43807c478bd9Sstevel@tonic-gate 
43817c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
43827c478bd9Sstevel@tonic-gate 
43837c478bd9Sstevel@tonic-gate 	if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
43847c478bd9Sstevel@tonic-gate 	    hubd->h_default_pipe,
43857c478bd9Sstevel@tonic-gate 	    GET_PORT_STATUS,
43867c478bd9Sstevel@tonic-gate 	    USB_REQ_GET_STATUS,
43877c478bd9Sstevel@tonic-gate 	    0,
43887c478bd9Sstevel@tonic-gate 	    port,
43897c478bd9Sstevel@tonic-gate 	    GET_STATUS_LENGTH,
43907c478bd9Sstevel@tonic-gate 	    &data, 0,
43917c478bd9Sstevel@tonic-gate 	    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
43927c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
43937c478bd9Sstevel@tonic-gate 		    "port=%d get status failed (%d 0x%x %d)",
43947c478bd9Sstevel@tonic-gate 		    port, completion_reason, cb_flags, rval);
43957c478bd9Sstevel@tonic-gate 
43967c478bd9Sstevel@tonic-gate 		if (data) {
43977c478bd9Sstevel@tonic-gate 			freemsg(data);
43987c478bd9Sstevel@tonic-gate 		}
43997c478bd9Sstevel@tonic-gate 
44007c478bd9Sstevel@tonic-gate 		*status = *change = 0;
44017c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
44027c478bd9Sstevel@tonic-gate 
44037c478bd9Sstevel@tonic-gate 		return (rval);
44047c478bd9Sstevel@tonic-gate 	}
44057c478bd9Sstevel@tonic-gate 
44067c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
44077c478bd9Sstevel@tonic-gate 	if ((data->b_wptr - data->b_rptr) != GET_STATUS_LENGTH) {
44087c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
44097c478bd9Sstevel@tonic-gate 		    "port %d: length incorrect %d",
44107c478bd9Sstevel@tonic-gate 		    port, data->b_wptr - data->b_rptr);
44117c478bd9Sstevel@tonic-gate 		freemsg(data);
44127c478bd9Sstevel@tonic-gate 		*status = *change = 0;
44137c478bd9Sstevel@tonic-gate 
44147c478bd9Sstevel@tonic-gate 		return (rval);
44157c478bd9Sstevel@tonic-gate 	}
44167c478bd9Sstevel@tonic-gate 
44177c478bd9Sstevel@tonic-gate 
44187c478bd9Sstevel@tonic-gate 	*status = (*(data->b_rptr + 1) << 8) | *(data->b_rptr);
44197c478bd9Sstevel@tonic-gate 	*change = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2);
44207c478bd9Sstevel@tonic-gate 
44217c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
44227c478bd9Sstevel@tonic-gate 	    "port%d status=0x%x, change=0x%x", port, *status, *change);
44237c478bd9Sstevel@tonic-gate 
44247c478bd9Sstevel@tonic-gate 	freemsg(data);
44257c478bd9Sstevel@tonic-gate 
44267c478bd9Sstevel@tonic-gate 	if (*status & PORT_STATUS_CCS) {
44277c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
44287c478bd9Sstevel@tonic-gate 		    "port%d connected", port);
44297c478bd9Sstevel@tonic-gate 
44307c478bd9Sstevel@tonic-gate 		hubd->h_port_state[port] |= (PORT_STATUS_CCS & ack_flag);
44317c478bd9Sstevel@tonic-gate 	} else {
44327c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
44337c478bd9Sstevel@tonic-gate 		    "port%d disconnected", port);
44347c478bd9Sstevel@tonic-gate 
44357c478bd9Sstevel@tonic-gate 		hubd->h_port_state[port] &= ~(PORT_STATUS_CCS & ack_flag);
44367c478bd9Sstevel@tonic-gate 	}
44377c478bd9Sstevel@tonic-gate 
44387c478bd9Sstevel@tonic-gate 	if (*status & PORT_STATUS_PES) {
44397c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
44407c478bd9Sstevel@tonic-gate 		    "port%d enabled", port);
44417c478bd9Sstevel@tonic-gate 
44427c478bd9Sstevel@tonic-gate 		hubd->h_port_state[port] |= (PORT_STATUS_PES & ack_flag);
44437c478bd9Sstevel@tonic-gate 	} else {
44447c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
44457c478bd9Sstevel@tonic-gate 		    "port%d disabled", port);
44467c478bd9Sstevel@tonic-gate 
44477c478bd9Sstevel@tonic-gate 		hubd->h_port_state[port] &= ~(PORT_STATUS_PES & ack_flag);
44487c478bd9Sstevel@tonic-gate 	}
44497c478bd9Sstevel@tonic-gate 
44507c478bd9Sstevel@tonic-gate 	if (*status & PORT_STATUS_PSS) {
44517c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
44527c478bd9Sstevel@tonic-gate 		    "port%d suspended", port);
44537c478bd9Sstevel@tonic-gate 
44547c478bd9Sstevel@tonic-gate 		hubd->h_port_state[port] |= (PORT_STATUS_PSS & ack_flag);
44557c478bd9Sstevel@tonic-gate 	} else {
44567c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
44577c478bd9Sstevel@tonic-gate 		    "port%d not suspended", port);
44587c478bd9Sstevel@tonic-gate 
44597c478bd9Sstevel@tonic-gate 		hubd->h_port_state[port] &= ~(PORT_STATUS_PSS & ack_flag);
44607c478bd9Sstevel@tonic-gate 	}
44617c478bd9Sstevel@tonic-gate 
44627c478bd9Sstevel@tonic-gate 	if (*change & PORT_CHANGE_PRSC) {
44637c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
44647c478bd9Sstevel@tonic-gate 		    "port%d reset completed", port);
44657c478bd9Sstevel@tonic-gate 
44667c478bd9Sstevel@tonic-gate 		hubd->h_port_state[port] |= (PORT_CHANGE_PRSC & ack_flag);
44677c478bd9Sstevel@tonic-gate 	} else {
44687c478bd9Sstevel@tonic-gate 
44697c478bd9Sstevel@tonic-gate 		hubd->h_port_state[port] &= ~(PORT_CHANGE_PRSC & ack_flag);
44707c478bd9Sstevel@tonic-gate 	}
44717c478bd9Sstevel@tonic-gate 
44727c478bd9Sstevel@tonic-gate 	if (*status & PORT_STATUS_POCI) {
4473*d291d9f2Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
4474*d291d9f2Sfrits 		    "port%d overcurrent!", port);
44757c478bd9Sstevel@tonic-gate 
44767c478bd9Sstevel@tonic-gate 		hubd->h_port_state[port] |= (PORT_STATUS_POCI & ack_flag);
44777c478bd9Sstevel@tonic-gate 	} else {
44787c478bd9Sstevel@tonic-gate 
44797c478bd9Sstevel@tonic-gate 		hubd->h_port_state[port] &= ~(PORT_STATUS_POCI & ack_flag);
44807c478bd9Sstevel@tonic-gate 	}
44817c478bd9Sstevel@tonic-gate 
44827c478bd9Sstevel@tonic-gate 	if (*status & PORT_STATUS_PRS) {
44837c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
44847c478bd9Sstevel@tonic-gate 		    "port%d reset active", port);
44857c478bd9Sstevel@tonic-gate 
44867c478bd9Sstevel@tonic-gate 		hubd->h_port_state[port] |= (PORT_STATUS_PRS & ack_flag);
44877c478bd9Sstevel@tonic-gate 	} else {
44887c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
44897c478bd9Sstevel@tonic-gate 		    "port%d reset inactive", port);
44907c478bd9Sstevel@tonic-gate 
44917c478bd9Sstevel@tonic-gate 		hubd->h_port_state[port] &= ~(PORT_STATUS_PRS & ack_flag);
44927c478bd9Sstevel@tonic-gate 	}
44937c478bd9Sstevel@tonic-gate 	if (*status & PORT_STATUS_PPS) {
44947c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
44957c478bd9Sstevel@tonic-gate 		    "port%d power on", port);
44967c478bd9Sstevel@tonic-gate 
44977c478bd9Sstevel@tonic-gate 		hubd->h_port_state[port] |= (PORT_STATUS_PPS & ack_flag);
44987c478bd9Sstevel@tonic-gate 	} else {
44997c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
45007c478bd9Sstevel@tonic-gate 		    "port%d power off", port);
45017c478bd9Sstevel@tonic-gate 
45027c478bd9Sstevel@tonic-gate 		hubd->h_port_state[port] &= ~(PORT_STATUS_PPS & ack_flag);
45037c478bd9Sstevel@tonic-gate 	}
45047c478bd9Sstevel@tonic-gate 	if (*status & PORT_STATUS_LSDA) {
45057c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
45067c478bd9Sstevel@tonic-gate 		    "port%d low speed", port);
45077c478bd9Sstevel@tonic-gate 
45087c478bd9Sstevel@tonic-gate 		hubd->h_port_state[port] |= (PORT_STATUS_LSDA & ack_flag);
45097c478bd9Sstevel@tonic-gate 	} else {
45107c478bd9Sstevel@tonic-gate 		hubd->h_port_state[port] &= ~(PORT_STATUS_LSDA & ack_flag);
45117c478bd9Sstevel@tonic-gate 		if (*status & PORT_STATUS_HSDA) {
45127c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_PORT,
45137c478bd9Sstevel@tonic-gate 			    hubd->h_log_handle, "port%d "
45147c478bd9Sstevel@tonic-gate 			    "high speed", port);
45157c478bd9Sstevel@tonic-gate 
45167c478bd9Sstevel@tonic-gate 			hubd->h_port_state[port] |=
45177c478bd9Sstevel@tonic-gate 					(PORT_STATUS_HSDA & ack_flag);
45187c478bd9Sstevel@tonic-gate 		} else {
45197c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_PORT,
45207c478bd9Sstevel@tonic-gate 			    hubd->h_log_handle, "port%d "
45217c478bd9Sstevel@tonic-gate 			    "full speed", port);
45227c478bd9Sstevel@tonic-gate 
45237c478bd9Sstevel@tonic-gate 			hubd->h_port_state[port] &=
45247c478bd9Sstevel@tonic-gate 					~(PORT_STATUS_HSDA & ack_flag);
45257c478bd9Sstevel@tonic-gate 		}
45267c478bd9Sstevel@tonic-gate 	}
45277c478bd9Sstevel@tonic-gate 
45287c478bd9Sstevel@tonic-gate 	/*
45297c478bd9Sstevel@tonic-gate 	 * Acknowledge connection, enable, reset status
45307c478bd9Sstevel@tonic-gate 	 */
45317c478bd9Sstevel@tonic-gate 	if (ack_flag) {
45327c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
45337c478bd9Sstevel@tonic-gate 		if (*change & PORT_CHANGE_CSC & ack_flag) {
45347c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
45357c478bd9Sstevel@tonic-gate 			    "clearing feature CFS_C_PORT_CONNECTION");
45367c478bd9Sstevel@tonic-gate 			if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
45377c478bd9Sstevel@tonic-gate 			    hubd->h_default_pipe,
45387c478bd9Sstevel@tonic-gate 			    HANDLE_PORT_FEATURE,
45397c478bd9Sstevel@tonic-gate 			    USB_REQ_CLEAR_FEATURE,
45407c478bd9Sstevel@tonic-gate 			    CFS_C_PORT_CONNECTION,
45417c478bd9Sstevel@tonic-gate 			    port,
45427c478bd9Sstevel@tonic-gate 			    0, NULL, 0,
45437c478bd9Sstevel@tonic-gate 			    &completion_reason, &cb_flags, 0)) !=
45447c478bd9Sstevel@tonic-gate 			    USB_SUCCESS) {
45457c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_PORT,
45467c478bd9Sstevel@tonic-gate 				    hubd->h_log_handle,
45477c478bd9Sstevel@tonic-gate 				    "clear feature CFS_C_PORT_CONNECTION"
45487c478bd9Sstevel@tonic-gate 				    " port%d failed (%d 0x%x %d)",
45497c478bd9Sstevel@tonic-gate 				    port, completion_reason, cb_flags, rval);
45507c478bd9Sstevel@tonic-gate 			}
45517c478bd9Sstevel@tonic-gate 		}
45527c478bd9Sstevel@tonic-gate 		if (*change & PORT_CHANGE_PESC & ack_flag) {
45537c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
45547c478bd9Sstevel@tonic-gate 			    "clearing feature CFS_C_PORT_ENABLE");
45557c478bd9Sstevel@tonic-gate 			if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
45567c478bd9Sstevel@tonic-gate 			    hubd->h_default_pipe,
45577c478bd9Sstevel@tonic-gate 			    HANDLE_PORT_FEATURE,
45587c478bd9Sstevel@tonic-gate 			    USB_REQ_CLEAR_FEATURE,
45597c478bd9Sstevel@tonic-gate 			    CFS_C_PORT_ENABLE,
45607c478bd9Sstevel@tonic-gate 			    port,
45617c478bd9Sstevel@tonic-gate 			    0, NULL, 0,
45627c478bd9Sstevel@tonic-gate 			    &completion_reason, &cb_flags, 0)) !=
45637c478bd9Sstevel@tonic-gate 			    USB_SUCCESS) {
45647c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_PORT,
45657c478bd9Sstevel@tonic-gate 				    hubd->h_log_handle,
45667c478bd9Sstevel@tonic-gate 				    "clear feature CFS_C_PORT_ENABLE"
45677c478bd9Sstevel@tonic-gate 				    " port%d failed (%d 0x%x %d)",
45687c478bd9Sstevel@tonic-gate 				    port, completion_reason, cb_flags, rval);
45697c478bd9Sstevel@tonic-gate 			}
45707c478bd9Sstevel@tonic-gate 		}
45717c478bd9Sstevel@tonic-gate 		if (*change & PORT_CHANGE_PSSC & ack_flag) {
45727c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
45737c478bd9Sstevel@tonic-gate 			    "clearing feature CFS_C_PORT_SUSPEND");
45747c478bd9Sstevel@tonic-gate 
45757c478bd9Sstevel@tonic-gate 			if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
45767c478bd9Sstevel@tonic-gate 			    hubd->h_default_pipe,
45777c478bd9Sstevel@tonic-gate 			    HANDLE_PORT_FEATURE,
45787c478bd9Sstevel@tonic-gate 			    USB_REQ_CLEAR_FEATURE,
45797c478bd9Sstevel@tonic-gate 			    CFS_C_PORT_SUSPEND,
45807c478bd9Sstevel@tonic-gate 			    port,
45817c478bd9Sstevel@tonic-gate 			    0, NULL, 0,
45827c478bd9Sstevel@tonic-gate 			    &completion_reason, &cb_flags, 0)) !=
45837c478bd9Sstevel@tonic-gate 			    USB_SUCCESS) {
45847c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_PORT,
45857c478bd9Sstevel@tonic-gate 				    hubd->h_log_handle,
45867c478bd9Sstevel@tonic-gate 				    "clear feature CFS_C_PORT_SUSPEND"
45877c478bd9Sstevel@tonic-gate 				    " port%d failed (%d 0x%x %d)",
45887c478bd9Sstevel@tonic-gate 				    port, completion_reason, cb_flags, rval);
45897c478bd9Sstevel@tonic-gate 			}
45907c478bd9Sstevel@tonic-gate 		}
45917c478bd9Sstevel@tonic-gate 		if (*change & PORT_CHANGE_OCIC & ack_flag) {
45927c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
45937c478bd9Sstevel@tonic-gate 			    "clearing feature CFS_C_PORT_OVER_CURRENT");
45947c478bd9Sstevel@tonic-gate 
45957c478bd9Sstevel@tonic-gate 			if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
45967c478bd9Sstevel@tonic-gate 			    hubd->h_default_pipe,
45977c478bd9Sstevel@tonic-gate 			    HANDLE_PORT_FEATURE,
45987c478bd9Sstevel@tonic-gate 			    USB_REQ_CLEAR_FEATURE,
45997c478bd9Sstevel@tonic-gate 			    CFS_C_PORT_OVER_CURRENT,
46007c478bd9Sstevel@tonic-gate 			    port,
46017c478bd9Sstevel@tonic-gate 			    0, NULL, 0,
46027c478bd9Sstevel@tonic-gate 			    &completion_reason, &cb_flags, 0)) !=
46037c478bd9Sstevel@tonic-gate 			    USB_SUCCESS) {
46047c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_PORT,
46057c478bd9Sstevel@tonic-gate 				    hubd->h_log_handle,
46067c478bd9Sstevel@tonic-gate 				    "clear feature CFS_C_PORT_OVER_CURRENT"
46077c478bd9Sstevel@tonic-gate 				    " port%d failed (%d 0x%x %d)",
46087c478bd9Sstevel@tonic-gate 				    port, completion_reason, cb_flags, rval);
46097c478bd9Sstevel@tonic-gate 			}
46107c478bd9Sstevel@tonic-gate 		}
46117c478bd9Sstevel@tonic-gate 		if (*change & PORT_CHANGE_PRSC & ack_flag) {
46127c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
46137c478bd9Sstevel@tonic-gate 			    "clearing feature CFS_C_PORT_RESET");
46147c478bd9Sstevel@tonic-gate 			if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
46157c478bd9Sstevel@tonic-gate 			    hubd->h_default_pipe,
46167c478bd9Sstevel@tonic-gate 			    HANDLE_PORT_FEATURE,
46177c478bd9Sstevel@tonic-gate 			    USB_REQ_CLEAR_FEATURE,
46187c478bd9Sstevel@tonic-gate 			    CFS_C_PORT_RESET,
46197c478bd9Sstevel@tonic-gate 			    port,
46207c478bd9Sstevel@tonic-gate 			    0, NULL, 0,
46217c478bd9Sstevel@tonic-gate 			    &completion_reason, &cb_flags, 0)) !=
46227c478bd9Sstevel@tonic-gate 			    USB_SUCCESS) {
46237c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_PORT,
46247c478bd9Sstevel@tonic-gate 				    hubd->h_log_handle,
46257c478bd9Sstevel@tonic-gate 				    "clear feature CFS_C_PORT_RESET"
46267c478bd9Sstevel@tonic-gate 				    " port%d failed (%d 0x%x %d)",
46277c478bd9Sstevel@tonic-gate 				    port, completion_reason, cb_flags, rval);
46287c478bd9Sstevel@tonic-gate 			}
46297c478bd9Sstevel@tonic-gate 		}
46307c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
46317c478bd9Sstevel@tonic-gate 	}
46327c478bd9Sstevel@tonic-gate 
46337c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
46347c478bd9Sstevel@tonic-gate 	    "new port%d state 0x%x", port, hubd->h_port_state[port]);
46357c478bd9Sstevel@tonic-gate 
46367c478bd9Sstevel@tonic-gate 
46377c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
46387c478bd9Sstevel@tonic-gate }
46397c478bd9Sstevel@tonic-gate 
46407c478bd9Sstevel@tonic-gate 
46417c478bd9Sstevel@tonic-gate /*
46427c478bd9Sstevel@tonic-gate  * hubd_recover_disabled_port
46437c478bd9Sstevel@tonic-gate  * if the port got disabled because of an error
46447c478bd9Sstevel@tonic-gate  * enable it. If hub doesn't suport enable port,
46457c478bd9Sstevel@tonic-gate  * reset the port to bring the device to life again
46467c478bd9Sstevel@tonic-gate  */
46477c478bd9Sstevel@tonic-gate static int
46487c478bd9Sstevel@tonic-gate hubd_recover_disabled_port(hubd_t *hubd, usb_port_t port)
46497c478bd9Sstevel@tonic-gate {
46507c478bd9Sstevel@tonic-gate 	uint16_t	status;
46517c478bd9Sstevel@tonic-gate 	uint16_t	change;
46527c478bd9Sstevel@tonic-gate 	int		rval = USB_FAILURE;
46537c478bd9Sstevel@tonic-gate 
46547c478bd9Sstevel@tonic-gate 	/* first try enabling the port */
46557c478bd9Sstevel@tonic-gate 	(void) hubd_enable_port(hubd, port);
46567c478bd9Sstevel@tonic-gate 
46577c478bd9Sstevel@tonic-gate 	/* read the port status */
46587c478bd9Sstevel@tonic-gate 	(void) hubd_determine_port_status(hubd, port, &status, &change,
46597c478bd9Sstevel@tonic-gate 	    PORT_CHANGE_PESC);
46607c478bd9Sstevel@tonic-gate 
46617c478bd9Sstevel@tonic-gate 	if (status & PORT_STATUS_PES) {
46627c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
46637c478bd9Sstevel@tonic-gate 			"Port%d now Enabled", port);
46647c478bd9Sstevel@tonic-gate 	} else if (status & PORT_STATUS_CCS) {
46657c478bd9Sstevel@tonic-gate 		/* first post a disconnect event to the child */
46667c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
46677c478bd9Sstevel@tonic-gate 		hubd_post_event(hubd, port, USBA_EVENT_TAG_HOT_REMOVAL);
46687c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
46697c478bd9Sstevel@tonic-gate 
46707c478bd9Sstevel@tonic-gate 		/* then reset the port and recover the device */
46717c478bd9Sstevel@tonic-gate 		rval = hubd_handle_port_connect(hubd, port);
46727c478bd9Sstevel@tonic-gate 
46737c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
46747c478bd9Sstevel@tonic-gate 			"Port%d now Enabled by force", port);
46757c478bd9Sstevel@tonic-gate 	}
46767c478bd9Sstevel@tonic-gate 
46777c478bd9Sstevel@tonic-gate 	return (rval);
46787c478bd9Sstevel@tonic-gate }
46797c478bd9Sstevel@tonic-gate 
46807c478bd9Sstevel@tonic-gate 
46817c478bd9Sstevel@tonic-gate /*
46827c478bd9Sstevel@tonic-gate  * hubd_enable_all_port_power:
46837c478bd9Sstevel@tonic-gate  */
46847c478bd9Sstevel@tonic-gate static int
46857c478bd9Sstevel@tonic-gate hubd_enable_all_port_power(hubd_t *hubd)
46867c478bd9Sstevel@tonic-gate {
46877c478bd9Sstevel@tonic-gate 	usb_hub_descr_t	*hub_descr;
46887c478bd9Sstevel@tonic-gate 	int		wait;
46897c478bd9Sstevel@tonic-gate 	usb_port_t	port;
46907c478bd9Sstevel@tonic-gate 	uint_t		retry;
46917c478bd9Sstevel@tonic-gate 	uint16_t	status;
46927c478bd9Sstevel@tonic-gate 	uint16_t	change;
46937c478bd9Sstevel@tonic-gate 
46947c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
46957c478bd9Sstevel@tonic-gate 	    "hubd_enable_all_port_power");
46967c478bd9Sstevel@tonic-gate 
46977c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
46987c478bd9Sstevel@tonic-gate 
46997c478bd9Sstevel@tonic-gate 	hub_descr = &hubd->h_hub_descr;
47007c478bd9Sstevel@tonic-gate 
47017c478bd9Sstevel@tonic-gate 	/*
47027c478bd9Sstevel@tonic-gate 	 * According to section 11.11 of USB, for hubs with no power
47037c478bd9Sstevel@tonic-gate 	 * switches, bPwrOn2PwrGood is zero. But we wait for some
47047c478bd9Sstevel@tonic-gate 	 * arbitrary time to enable power to become stable.
47057c478bd9Sstevel@tonic-gate 	 *
47067c478bd9Sstevel@tonic-gate 	 * If an hub supports port power switching, we need to wait
47077c478bd9Sstevel@tonic-gate 	 * at least 20ms before accessing corresponding usb port.
47087c478bd9Sstevel@tonic-gate 	 */
47097c478bd9Sstevel@tonic-gate 	if ((hub_descr->wHubCharacteristics &
47107c478bd9Sstevel@tonic-gate 	    HUB_CHARS_NO_POWER_SWITCHING) || (!hub_descr->bPwrOn2PwrGood)) {
47117c478bd9Sstevel@tonic-gate 		wait = hubd_device_delay / 10;
47127c478bd9Sstevel@tonic-gate 	} else {
47137c478bd9Sstevel@tonic-gate 		wait = max(HUB_DEFAULT_POPG,
47147c478bd9Sstevel@tonic-gate 		    hub_descr->bPwrOn2PwrGood) * 2 * 1000;
47157c478bd9Sstevel@tonic-gate 	}
47167c478bd9Sstevel@tonic-gate 
47177c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
47187c478bd9Sstevel@tonic-gate 	    "hubd_enable_all_port_power: popg=%d wait=%d",
47197c478bd9Sstevel@tonic-gate 	    hub_descr->bPwrOn2PwrGood, wait);
47207c478bd9Sstevel@tonic-gate 
47217c478bd9Sstevel@tonic-gate 	/*
47227c478bd9Sstevel@tonic-gate 	 * Enable power per port. we ignore gang power and power mask
47237c478bd9Sstevel@tonic-gate 	 * and always enable all ports one by one.
47247c478bd9Sstevel@tonic-gate 	 */
47257c478bd9Sstevel@tonic-gate 	for (port = 1; port <= hub_descr->bNbrPorts; port++) {
47267c478bd9Sstevel@tonic-gate 		/*
47277c478bd9Sstevel@tonic-gate 		 * Transition the port from the Powered Off to the
47287c478bd9Sstevel@tonic-gate 		 * Disconnected state by supplying power to the port.
47297c478bd9Sstevel@tonic-gate 		 */
47307c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_PORT,
47317c478bd9Sstevel@tonic-gate 		    hubd->h_log_handle,
47327c478bd9Sstevel@tonic-gate 		    "hubd_enable_all_port_power: power port=%d", port);
47337c478bd9Sstevel@tonic-gate 
47347c478bd9Sstevel@tonic-gate 		(void) hubd_enable_port_power(hubd, port);
47357c478bd9Sstevel@tonic-gate 	}
47367c478bd9Sstevel@tonic-gate 
47377c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
47387c478bd9Sstevel@tonic-gate 	delay(drv_usectohz(wait));
47397c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
47407c478bd9Sstevel@tonic-gate 
47417c478bd9Sstevel@tonic-gate 	/* For retry if any, use some extra delay */
47427c478bd9Sstevel@tonic-gate 	wait = max(wait, hubd_device_delay / 10);
47437c478bd9Sstevel@tonic-gate 
47447c478bd9Sstevel@tonic-gate 	/* Check each port power status for a given usb hub */
47457c478bd9Sstevel@tonic-gate 	for (port = 1; port <= hub_descr->bNbrPorts; port++) {
47467c478bd9Sstevel@tonic-gate 
47477c478bd9Sstevel@tonic-gate 		/* Get port status */
47487c478bd9Sstevel@tonic-gate 		(void) hubd_determine_port_status(hubd, port,
47497c478bd9Sstevel@tonic-gate 		    &status, &change, 0);
47507c478bd9Sstevel@tonic-gate 
47517c478bd9Sstevel@tonic-gate 		for (retry = 0; ((!(status & PORT_STATUS_PPS)) &&
47527c478bd9Sstevel@tonic-gate 		    (retry < HUBD_PORT_RETRY)); retry++) {
47537c478bd9Sstevel@tonic-gate 
47547c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
47557c478bd9Sstevel@tonic-gate 			    "Retry is in progress %d: port %d status %d",
47567c478bd9Sstevel@tonic-gate 			    retry, port, status);
47577c478bd9Sstevel@tonic-gate 
47587c478bd9Sstevel@tonic-gate 			(void) hubd_enable_port_power(hubd, port);
47597c478bd9Sstevel@tonic-gate 
47607c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
47617c478bd9Sstevel@tonic-gate 			delay(drv_usectohz(wait));
47627c478bd9Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
47637c478bd9Sstevel@tonic-gate 
47647c478bd9Sstevel@tonic-gate 			/* Get port status */
47657c478bd9Sstevel@tonic-gate 			(void) hubd_determine_port_status(hubd, port,
47667c478bd9Sstevel@tonic-gate 			    &status, &change, 0);
47677c478bd9Sstevel@tonic-gate 		}
47687c478bd9Sstevel@tonic-gate 
47697c478bd9Sstevel@tonic-gate 		/* Print warning message if port has no power */
47707c478bd9Sstevel@tonic-gate 		if (!(status & PORT_STATUS_PPS)) {
47717c478bd9Sstevel@tonic-gate 
4772*d291d9f2Sfrits 			USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
47737c478bd9Sstevel@tonic-gate 			    "hubd_enable_all_port_power: port %d power-on "
47747c478bd9Sstevel@tonic-gate 			    "failed, port status 0x%x", port, status);
47757c478bd9Sstevel@tonic-gate 		}
47767c478bd9Sstevel@tonic-gate 	}
47777c478bd9Sstevel@tonic-gate 
47787c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
47797c478bd9Sstevel@tonic-gate }
47807c478bd9Sstevel@tonic-gate 
47817c478bd9Sstevel@tonic-gate 
47827c478bd9Sstevel@tonic-gate /*
47837c478bd9Sstevel@tonic-gate  * hubd_enable_port_power:
47847c478bd9Sstevel@tonic-gate  *	enable individual port power
47857c478bd9Sstevel@tonic-gate  */
47867c478bd9Sstevel@tonic-gate static int
47877c478bd9Sstevel@tonic-gate hubd_enable_port_power(hubd_t *hubd, usb_port_t port)
47887c478bd9Sstevel@tonic-gate {
47897c478bd9Sstevel@tonic-gate 	int		rval;
47907c478bd9Sstevel@tonic-gate 	usb_cr_t	completion_reason;
47917c478bd9Sstevel@tonic-gate 	usb_cb_flags_t	cb_flags;
47927c478bd9Sstevel@tonic-gate 
47937c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
47947c478bd9Sstevel@tonic-gate 	    "hubd_enable_port_power: port=%d", port);
47957c478bd9Sstevel@tonic-gate 
47967c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
47977c478bd9Sstevel@tonic-gate 	ASSERT(hubd->h_default_pipe != 0);
47987c478bd9Sstevel@tonic-gate 
47997c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
48007c478bd9Sstevel@tonic-gate 
48017c478bd9Sstevel@tonic-gate 	if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
48027c478bd9Sstevel@tonic-gate 	    hubd->h_default_pipe,
48037c478bd9Sstevel@tonic-gate 	    HANDLE_PORT_FEATURE,
48047c478bd9Sstevel@tonic-gate 	    USB_REQ_SET_FEATURE,
48057c478bd9Sstevel@tonic-gate 	    CFS_PORT_POWER,
48067c478bd9Sstevel@tonic-gate 	    port,
48077c478bd9Sstevel@tonic-gate 	    0, NULL, 0,
48087c478bd9Sstevel@tonic-gate 	    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
48097c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
48107c478bd9Sstevel@tonic-gate 		    "set port power failed (%d 0x%x %d)",
48117c478bd9Sstevel@tonic-gate 		    completion_reason, cb_flags, rval);
48127c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
48137c478bd9Sstevel@tonic-gate 
48147c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
48157c478bd9Sstevel@tonic-gate 	} else {
48167c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
48177c478bd9Sstevel@tonic-gate 		hubd->h_port_state[port] |= PORT_STATUS_PPS;
48187c478bd9Sstevel@tonic-gate 
48197c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
48207c478bd9Sstevel@tonic-gate 	}
48217c478bd9Sstevel@tonic-gate }
48227c478bd9Sstevel@tonic-gate 
48237c478bd9Sstevel@tonic-gate 
48247c478bd9Sstevel@tonic-gate /*
48257c478bd9Sstevel@tonic-gate  * hubd_disable_all_port_power:
48267c478bd9Sstevel@tonic-gate  */
48277c478bd9Sstevel@tonic-gate static int
48287c478bd9Sstevel@tonic-gate hubd_disable_all_port_power(hubd_t *hubd)
48297c478bd9Sstevel@tonic-gate {
48307c478bd9Sstevel@tonic-gate 	usb_port_t port;
48317c478bd9Sstevel@tonic-gate 
48327c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
48337c478bd9Sstevel@tonic-gate 	    "hubd_disable_all_port_power");
48347c478bd9Sstevel@tonic-gate 
48357c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
48367c478bd9Sstevel@tonic-gate 
48377c478bd9Sstevel@tonic-gate 	/*
48387c478bd9Sstevel@tonic-gate 	 * disable power per port, ignore gang power and power mask
48397c478bd9Sstevel@tonic-gate 	 */
48407c478bd9Sstevel@tonic-gate 	for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
48417c478bd9Sstevel@tonic-gate 		(void) hubd_disable_port_power(hubd, port);
48427c478bd9Sstevel@tonic-gate 	}
48437c478bd9Sstevel@tonic-gate 
48447c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
48457c478bd9Sstevel@tonic-gate }
48467c478bd9Sstevel@tonic-gate 
48477c478bd9Sstevel@tonic-gate 
48487c478bd9Sstevel@tonic-gate /*
48497c478bd9Sstevel@tonic-gate  * hubd_disable_port_power:
48507c478bd9Sstevel@tonic-gate  *	disable individual port power
48517c478bd9Sstevel@tonic-gate  */
48527c478bd9Sstevel@tonic-gate static int
48537c478bd9Sstevel@tonic-gate hubd_disable_port_power(hubd_t *hubd, usb_port_t port)
48547c478bd9Sstevel@tonic-gate {
48557c478bd9Sstevel@tonic-gate 	int		rval;
48567c478bd9Sstevel@tonic-gate 	usb_cr_t	completion_reason;
48577c478bd9Sstevel@tonic-gate 	usb_cb_flags_t	cb_flags;
48587c478bd9Sstevel@tonic-gate 
48597c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
48607c478bd9Sstevel@tonic-gate 	    "hubd_disable_port_power: port=%d", port);
48617c478bd9Sstevel@tonic-gate 
48627c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
48637c478bd9Sstevel@tonic-gate 
48647c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
48657c478bd9Sstevel@tonic-gate 
48667c478bd9Sstevel@tonic-gate 	if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
48677c478bd9Sstevel@tonic-gate 	    hubd->h_default_pipe,
48687c478bd9Sstevel@tonic-gate 	    HANDLE_PORT_FEATURE,
48697c478bd9Sstevel@tonic-gate 	    USB_REQ_CLEAR_FEATURE,
48707c478bd9Sstevel@tonic-gate 	    CFS_PORT_POWER,
48717c478bd9Sstevel@tonic-gate 	    port,
48727c478bd9Sstevel@tonic-gate 	    0, NULL, 0,
48737c478bd9Sstevel@tonic-gate 	    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
48747c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
48757c478bd9Sstevel@tonic-gate 		    "clearing port%d power failed (%d 0x%x %d)",
48767c478bd9Sstevel@tonic-gate 		    port, completion_reason, cb_flags, rval);
48777c478bd9Sstevel@tonic-gate 
48787c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
48797c478bd9Sstevel@tonic-gate 
48807c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
48817c478bd9Sstevel@tonic-gate 	} else {
48827c478bd9Sstevel@tonic-gate 
48837c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
48847c478bd9Sstevel@tonic-gate 		ASSERT(completion_reason == 0);
48857c478bd9Sstevel@tonic-gate 		hubd->h_port_state[port] &= ~PORT_STATUS_PPS;
48867c478bd9Sstevel@tonic-gate 
48877c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
48887c478bd9Sstevel@tonic-gate 	}
48897c478bd9Sstevel@tonic-gate }
48907c478bd9Sstevel@tonic-gate 
48917c478bd9Sstevel@tonic-gate 
48927c478bd9Sstevel@tonic-gate /*
48937c478bd9Sstevel@tonic-gate  * Search the database of user preferences and find out the preferred
48947c478bd9Sstevel@tonic-gate  * configuration for this new device
48957c478bd9Sstevel@tonic-gate  */
48967c478bd9Sstevel@tonic-gate static int
48977c478bd9Sstevel@tonic-gate hubd_select_device_configuration(hubd_t *hubd, usb_port_t port,
48987c478bd9Sstevel@tonic-gate 	dev_info_t *child_dip, usba_device_t *child_ud)
48997c478bd9Sstevel@tonic-gate {
49007c478bd9Sstevel@tonic-gate 	char		*pathname = NULL;
49017c478bd9Sstevel@tonic-gate 	char		*tmp_path = NULL;
49027c478bd9Sstevel@tonic-gate 	int		user_conf;
49037c478bd9Sstevel@tonic-gate 	int		pathlen;
49047c478bd9Sstevel@tonic-gate 	usb_dev_descr_t	*usbdev_ptr;
49057c478bd9Sstevel@tonic-gate 	usba_configrec_t *user_pref;
49067c478bd9Sstevel@tonic-gate 
49077c478bd9Sstevel@tonic-gate 	mutex_enter(&child_ud->usb_mutex);
49087c478bd9Sstevel@tonic-gate 	usbdev_ptr = child_ud->usb_dev_descr;
49097c478bd9Sstevel@tonic-gate 	mutex_exit(&child_ud->usb_mutex);
49107c478bd9Sstevel@tonic-gate 
49117c478bd9Sstevel@tonic-gate 	/* try to get pathname for this device */
49127c478bd9Sstevel@tonic-gate 	tmp_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
49137c478bd9Sstevel@tonic-gate 	(void) ddi_pathname(child_dip, tmp_path);
49147c478bd9Sstevel@tonic-gate 
49157c478bd9Sstevel@tonic-gate 	pathlen = strlen(tmp_path) + 32;
49167c478bd9Sstevel@tonic-gate 	pathname = kmem_zalloc(pathlen, KM_SLEEP);
49177c478bd9Sstevel@tonic-gate 
49187c478bd9Sstevel@tonic-gate 	/*
49197c478bd9Sstevel@tonic-gate 	 * We haven't initialized the node and it doesn't have an address
49207c478bd9Sstevel@tonic-gate 	 * yet. Append port number to the physical pathname
49217c478bd9Sstevel@tonic-gate 	 */
49227c478bd9Sstevel@tonic-gate 	(void) sprintf(pathname, "%s@%d", tmp_path, port);
49237c478bd9Sstevel@tonic-gate 
49247c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
49257c478bd9Sstevel@tonic-gate 	    "hubd_select_device_configuration: Device=%s\n\t"
49267c478bd9Sstevel@tonic-gate 	    "Child path=%s",
49277c478bd9Sstevel@tonic-gate 	    usba_get_mfg_prod_sn_str(child_dip, tmp_path, MAXPATHLEN),
49287c478bd9Sstevel@tonic-gate 	    pathname);
49297c478bd9Sstevel@tonic-gate 	kmem_free(tmp_path, MAXPATHLEN);
49307c478bd9Sstevel@tonic-gate 
49317c478bd9Sstevel@tonic-gate 
49327c478bd9Sstevel@tonic-gate 	/* database search for user preferences */
49337c478bd9Sstevel@tonic-gate 	user_pref = usba_devdb_get_user_preferences(usbdev_ptr->idVendor,
49347c478bd9Sstevel@tonic-gate 	    usbdev_ptr->idProduct, child_ud->usb_serialno_str, pathname);
49357c478bd9Sstevel@tonic-gate 
49367c478bd9Sstevel@tonic-gate 	if (user_pref) {
49377c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
49387c478bd9Sstevel@tonic-gate 		    "hubd_select_device_configuration: "
49397c478bd9Sstevel@tonic-gate 		    "usba_devdb_get_user_preferences "
49407c478bd9Sstevel@tonic-gate 		    "return user_conf=%d\npreferred driver=%s path=%s",
49417c478bd9Sstevel@tonic-gate 		    user_pref->cfg_index, user_pref->driver,
49427c478bd9Sstevel@tonic-gate 		    user_pref->pathname);
49437c478bd9Sstevel@tonic-gate 
49447c478bd9Sstevel@tonic-gate 		user_conf = user_pref->cfg_index;
49457c478bd9Sstevel@tonic-gate 
49467c478bd9Sstevel@tonic-gate 		if (user_pref->driver) {
49477c478bd9Sstevel@tonic-gate 			mutex_enter(&child_ud->usb_mutex);
49487c478bd9Sstevel@tonic-gate 			child_ud->usb_preferred_driver = user_pref->driver;
49497c478bd9Sstevel@tonic-gate 			mutex_exit(&child_ud->usb_mutex);
49507c478bd9Sstevel@tonic-gate 		}
49517c478bd9Sstevel@tonic-gate 	} else {
49527c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
49537c478bd9Sstevel@tonic-gate 		    "hubd_select_device_configuration: No match found");
49547c478bd9Sstevel@tonic-gate 
49557c478bd9Sstevel@tonic-gate 		/* select default configuration for this device */
49567c478bd9Sstevel@tonic-gate 		user_conf = USBA_DEV_CONFIG_INDEX_UNDEFINED;
49577c478bd9Sstevel@tonic-gate 	}
49587c478bd9Sstevel@tonic-gate 	kmem_free(pathname, pathlen);
49597c478bd9Sstevel@tonic-gate 
49607c478bd9Sstevel@tonic-gate 	/* if the device has just one configuration, set default value */
49617c478bd9Sstevel@tonic-gate 	if (usbdev_ptr->bNumConfigurations == 1) {
49627c478bd9Sstevel@tonic-gate 		user_conf = USB_DEV_DEFAULT_CONFIG_INDEX;
49637c478bd9Sstevel@tonic-gate 	}
49647c478bd9Sstevel@tonic-gate 
49657c478bd9Sstevel@tonic-gate 	return (user_conf);
49667c478bd9Sstevel@tonic-gate }
49677c478bd9Sstevel@tonic-gate 
49687c478bd9Sstevel@tonic-gate 
49697c478bd9Sstevel@tonic-gate /*
49707c478bd9Sstevel@tonic-gate  * Retrieves config cloud for this configuration
49717c478bd9Sstevel@tonic-gate  */
49727c478bd9Sstevel@tonic-gate int
49737c478bd9Sstevel@tonic-gate hubd_get_this_config_cloud(hubd_t *hubd, dev_info_t *dip,
49747c478bd9Sstevel@tonic-gate 	usba_device_t *child_ud, uint16_t conf_index)
49757c478bd9Sstevel@tonic-gate {
49767c478bd9Sstevel@tonic-gate 	usb_cfg_descr_t	*confdescr;
49777c478bd9Sstevel@tonic-gate 	mblk_t		*pdata = NULL;
49787c478bd9Sstevel@tonic-gate 	int		rval;
49797c478bd9Sstevel@tonic-gate 	size_t		size;
49807c478bd9Sstevel@tonic-gate 	char		*tmpbuf;
49817c478bd9Sstevel@tonic-gate 	usb_cr_t	completion_reason;
49827c478bd9Sstevel@tonic-gate 	usb_cb_flags_t	cb_flags;
49837c478bd9Sstevel@tonic-gate 	usb_pipe_handle_t	def_ph;
49847c478bd9Sstevel@tonic-gate 
49857c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
49867c478bd9Sstevel@tonic-gate 	    "hubd_get_this_config_cloud: conf_index=%d", conf_index);
49877c478bd9Sstevel@tonic-gate 
49887c478bd9Sstevel@tonic-gate 
49897c478bd9Sstevel@tonic-gate 	/* alloc temporary space for config descriptor */
49907c478bd9Sstevel@tonic-gate 	confdescr = (usb_cfg_descr_t *)kmem_zalloc(USB_CFG_DESCR_SIZE,
49917c478bd9Sstevel@tonic-gate 	    KM_SLEEP);
49927c478bd9Sstevel@tonic-gate 
49937c478bd9Sstevel@tonic-gate 	/* alloc temporary space for string descriptor */
49947c478bd9Sstevel@tonic-gate 	tmpbuf = kmem_zalloc(USB_MAXSTRINGLEN, KM_SLEEP);
49957c478bd9Sstevel@tonic-gate 
49967c478bd9Sstevel@tonic-gate 	def_ph = usba_get_dflt_pipe_handle(dip);
49977c478bd9Sstevel@tonic-gate 
49987c478bd9Sstevel@tonic-gate 	if ((rval = usb_pipe_sync_ctrl_xfer(dip, def_ph,
49997c478bd9Sstevel@tonic-gate 	    USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
50007c478bd9Sstevel@tonic-gate 	    USB_REQ_GET_DESCR,
50017c478bd9Sstevel@tonic-gate 	    USB_DESCR_TYPE_SETUP_CFG | conf_index,
50027c478bd9Sstevel@tonic-gate 	    0,
50037c478bd9Sstevel@tonic-gate 	    USB_CFG_DESCR_SIZE,
50047c478bd9Sstevel@tonic-gate 	    &pdata,
50057c478bd9Sstevel@tonic-gate 	    0,
50067c478bd9Sstevel@tonic-gate 	    &completion_reason,
50077c478bd9Sstevel@tonic-gate 	    &cb_flags,
50087c478bd9Sstevel@tonic-gate 	    0)) == USB_SUCCESS) {
50097c478bd9Sstevel@tonic-gate 
50107c478bd9Sstevel@tonic-gate 		/* this must be true since we didn't allow data underruns */
50117c478bd9Sstevel@tonic-gate 		ASSERT((pdata->b_wptr - pdata->b_rptr) == USB_CFG_DESCR_SIZE);
50127c478bd9Sstevel@tonic-gate 
50137c478bd9Sstevel@tonic-gate 		/*
50147c478bd9Sstevel@tonic-gate 		 * Parse the configuration descriptor
50157c478bd9Sstevel@tonic-gate 		 */
50167c478bd9Sstevel@tonic-gate 		size = usb_parse_cfg_descr(pdata->b_rptr,
50177c478bd9Sstevel@tonic-gate 		    pdata->b_wptr - pdata->b_rptr, confdescr,
50187c478bd9Sstevel@tonic-gate 		    USB_CFG_DESCR_SIZE);
50197c478bd9Sstevel@tonic-gate 
50207c478bd9Sstevel@tonic-gate 		/* if parse cfg descr error, it should return failure */
50217c478bd9Sstevel@tonic-gate 		if (size == USB_PARSE_ERROR) {
50227c478bd9Sstevel@tonic-gate 
50237c478bd9Sstevel@tonic-gate 			if (pdata->b_rptr[1] != USB_DESCR_TYPE_CFG) {
5024*d291d9f2Sfrits 				USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
50257c478bd9Sstevel@tonic-gate 				    hubd->h_log_handle,
50267c478bd9Sstevel@tonic-gate 				    "device returned incorrect "
50277c478bd9Sstevel@tonic-gate 				    "configuration descriptor type.");
50287c478bd9Sstevel@tonic-gate 			}
50297c478bd9Sstevel@tonic-gate 			rval = USB_FAILURE;
50307c478bd9Sstevel@tonic-gate 			goto done;
50317c478bd9Sstevel@tonic-gate 		}
50327c478bd9Sstevel@tonic-gate 
50337c478bd9Sstevel@tonic-gate 		if (confdescr->wTotalLength < USB_CFG_DESCR_SIZE) {
5034*d291d9f2Sfrits 			USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
50357c478bd9Sstevel@tonic-gate 			    hubd->h_log_handle,
50367c478bd9Sstevel@tonic-gate 			    "device returned incorrect "
50377c478bd9Sstevel@tonic-gate 			    "configuration descriptor size.");
50387c478bd9Sstevel@tonic-gate 
50397c478bd9Sstevel@tonic-gate 			rval = USB_FAILURE;
50407c478bd9Sstevel@tonic-gate 			goto done;
50417c478bd9Sstevel@tonic-gate 		}
50427c478bd9Sstevel@tonic-gate 
50437c478bd9Sstevel@tonic-gate 		freemsg(pdata);
50447c478bd9Sstevel@tonic-gate 		pdata = NULL;
50457c478bd9Sstevel@tonic-gate 
50467c478bd9Sstevel@tonic-gate 		/* Now fetch the complete config cloud */
50477c478bd9Sstevel@tonic-gate 		if ((rval = usb_pipe_sync_ctrl_xfer(dip, def_ph,
50487c478bd9Sstevel@tonic-gate 		    USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
50497c478bd9Sstevel@tonic-gate 		    USB_REQ_GET_DESCR,
50507c478bd9Sstevel@tonic-gate 		    USB_DESCR_TYPE_SETUP_CFG | conf_index,
50517c478bd9Sstevel@tonic-gate 		    0,
50527c478bd9Sstevel@tonic-gate 		    confdescr->wTotalLength,
50537c478bd9Sstevel@tonic-gate 		    &pdata,
50547c478bd9Sstevel@tonic-gate 		    0,
50557c478bd9Sstevel@tonic-gate 		    &completion_reason,
50567c478bd9Sstevel@tonic-gate 		    &cb_flags,
50577c478bd9Sstevel@tonic-gate 		    0)) == USB_SUCCESS) {
50587c478bd9Sstevel@tonic-gate 
50597c478bd9Sstevel@tonic-gate 			if ((pdata->b_wptr - pdata->b_rptr) !=
50607c478bd9Sstevel@tonic-gate 			    confdescr->wTotalLength) {
50617c478bd9Sstevel@tonic-gate 
5062*d291d9f2Sfrits 				USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
50637c478bd9Sstevel@tonic-gate 				    hubd->h_log_handle,
50647c478bd9Sstevel@tonic-gate 				    "device returned incorrect "
50657c478bd9Sstevel@tonic-gate 				    "configuration descriptor.");
50667c478bd9Sstevel@tonic-gate 
50677c478bd9Sstevel@tonic-gate 				rval = USB_FAILURE;
50687c478bd9Sstevel@tonic-gate 				goto done;
50697c478bd9Sstevel@tonic-gate 			}
50707c478bd9Sstevel@tonic-gate 
50717c478bd9Sstevel@tonic-gate 			/*
50727c478bd9Sstevel@tonic-gate 			 * copy config descriptor into usba_device
50737c478bd9Sstevel@tonic-gate 			 */
50747c478bd9Sstevel@tonic-gate 			mutex_enter(&child_ud->usb_mutex);
50757c478bd9Sstevel@tonic-gate 			child_ud->usb_cfg_array[conf_index] =
50767c478bd9Sstevel@tonic-gate 			    kmem_alloc(confdescr->wTotalLength, KM_SLEEP);
50777c478bd9Sstevel@tonic-gate 			child_ud->usb_cfg_array_len[conf_index] =
50787c478bd9Sstevel@tonic-gate 					confdescr->wTotalLength;
50797c478bd9Sstevel@tonic-gate 			bcopy((caddr_t)pdata->b_rptr,
50807c478bd9Sstevel@tonic-gate 			    (caddr_t)child_ud->usb_cfg_array[conf_index],
50817c478bd9Sstevel@tonic-gate 			    confdescr->wTotalLength);
50827c478bd9Sstevel@tonic-gate 			mutex_exit(&child_ud->usb_mutex);
50837c478bd9Sstevel@tonic-gate 
50847c478bd9Sstevel@tonic-gate 			/*
50857c478bd9Sstevel@tonic-gate 			 * retrieve string descriptor describing this
50867c478bd9Sstevel@tonic-gate 			 * configuration
50877c478bd9Sstevel@tonic-gate 			 */
50887c478bd9Sstevel@tonic-gate 			if (confdescr->iConfiguration) {
50897c478bd9Sstevel@tonic-gate 
50907c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG,
50917c478bd9Sstevel@tonic-gate 				    hubd->h_log_handle,
50927c478bd9Sstevel@tonic-gate 				    "Get conf str descr for config_index=%d",
50937c478bd9Sstevel@tonic-gate 				    conf_index);
50947c478bd9Sstevel@tonic-gate 
50957c478bd9Sstevel@tonic-gate 				/*
50967c478bd9Sstevel@tonic-gate 				 * Now fetch the string descriptor describing
50977c478bd9Sstevel@tonic-gate 				 * this configuration
50987c478bd9Sstevel@tonic-gate 				 */
50997c478bd9Sstevel@tonic-gate 				if ((rval = usb_get_string_descr(dip,
51007c478bd9Sstevel@tonic-gate 				    USB_LANG_ID, confdescr->iConfiguration,
51017c478bd9Sstevel@tonic-gate 				    tmpbuf, USB_MAXSTRINGLEN)) ==
51027c478bd9Sstevel@tonic-gate 				    USB_SUCCESS) {
51037c478bd9Sstevel@tonic-gate 					size = strlen(tmpbuf);
51047c478bd9Sstevel@tonic-gate 					if (size > 0) {
51057c478bd9Sstevel@tonic-gate 						child_ud->usb_cfg_str_descr
51067c478bd9Sstevel@tonic-gate 						    [conf_index] = (char *)
51077c478bd9Sstevel@tonic-gate 						    kmem_zalloc(size + 1,
51087c478bd9Sstevel@tonic-gate 						    KM_SLEEP);
51097c478bd9Sstevel@tonic-gate 						(void) strcpy(
51107c478bd9Sstevel@tonic-gate 						    child_ud->usb_cfg_str_descr
51117c478bd9Sstevel@tonic-gate 						    [conf_index], tmpbuf);
51127c478bd9Sstevel@tonic-gate 					}
51137c478bd9Sstevel@tonic-gate 				} else {
51147c478bd9Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
51157c478bd9Sstevel@tonic-gate 					    hubd->h_log_handle,
51167c478bd9Sstevel@tonic-gate 					    "hubd_get_this_config_cloud: "
51177c478bd9Sstevel@tonic-gate 					    "getting config string (%d) "
51187c478bd9Sstevel@tonic-gate 					    "failed",
51197c478bd9Sstevel@tonic-gate 					    confdescr->iConfiguration);
51207c478bd9Sstevel@tonic-gate 
51217c478bd9Sstevel@tonic-gate 					/* ignore this error */
51227c478bd9Sstevel@tonic-gate 					rval = USB_SUCCESS;
51237c478bd9Sstevel@tonic-gate 				}
51247c478bd9Sstevel@tonic-gate 			}
51257c478bd9Sstevel@tonic-gate 		}
51267c478bd9Sstevel@tonic-gate 	}
51277c478bd9Sstevel@tonic-gate 
51287c478bd9Sstevel@tonic-gate done:
51297c478bd9Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
51307c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
51317c478bd9Sstevel@tonic-gate 		    "hubd_get_this_config_cloud: "
51327c478bd9Sstevel@tonic-gate 		    "error in retrieving config descriptor for "
51337c478bd9Sstevel@tonic-gate 		    "config index=%d rval=%d cr=%d",
51347c478bd9Sstevel@tonic-gate 		    conf_index, rval, completion_reason);
51357c478bd9Sstevel@tonic-gate 	}
51367c478bd9Sstevel@tonic-gate 
51377c478bd9Sstevel@tonic-gate 	if (pdata) {
51387c478bd9Sstevel@tonic-gate 		freemsg(pdata);
51397c478bd9Sstevel@tonic-gate 		pdata = NULL;
51407c478bd9Sstevel@tonic-gate 	}
51417c478bd9Sstevel@tonic-gate 
51427c478bd9Sstevel@tonic-gate 	kmem_free(confdescr, USB_CFG_DESCR_SIZE);
51437c478bd9Sstevel@tonic-gate 	kmem_free(tmpbuf, USB_MAXSTRINGLEN);
51447c478bd9Sstevel@tonic-gate 
51457c478bd9Sstevel@tonic-gate 	return (rval);
51467c478bd9Sstevel@tonic-gate }
51477c478bd9Sstevel@tonic-gate 
51487c478bd9Sstevel@tonic-gate 
51497c478bd9Sstevel@tonic-gate /*
51507c478bd9Sstevel@tonic-gate  * Retrieves the entire config cloud for all configurations of the device
51517c478bd9Sstevel@tonic-gate  */
51527c478bd9Sstevel@tonic-gate int
51537c478bd9Sstevel@tonic-gate hubd_get_all_device_config_cloud(hubd_t *hubd, dev_info_t *dip,
51547c478bd9Sstevel@tonic-gate 	usba_device_t *child_ud)
51557c478bd9Sstevel@tonic-gate {
51567c478bd9Sstevel@tonic-gate 	int		rval = USB_SUCCESS;
51577c478bd9Sstevel@tonic-gate 	int		ncfgs;
51587c478bd9Sstevel@tonic-gate 	uint16_t	size;
51597c478bd9Sstevel@tonic-gate 	uint16_t	conf_index;
51607c478bd9Sstevel@tonic-gate 	uchar_t		**cfg_array;
51617c478bd9Sstevel@tonic-gate 	uint16_t	*cfg_array_len;
51627c478bd9Sstevel@tonic-gate 	char		**str_descr;
51637c478bd9Sstevel@tonic-gate 
51647c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
51657c478bd9Sstevel@tonic-gate 	    "hubd_get_all_device_config_cloud: Start");
51667c478bd9Sstevel@tonic-gate 
51677c478bd9Sstevel@tonic-gate 	/* alloc pointer array for conf. descriptors */
51687c478bd9Sstevel@tonic-gate 	mutex_enter(&child_ud->usb_mutex);
51697c478bd9Sstevel@tonic-gate 	ncfgs = child_ud->usb_n_cfgs;
51707c478bd9Sstevel@tonic-gate 	mutex_exit(&child_ud->usb_mutex);
51717c478bd9Sstevel@tonic-gate 
51727c478bd9Sstevel@tonic-gate 	size = sizeof (uchar_t *) * ncfgs;
51737c478bd9Sstevel@tonic-gate 	cfg_array = kmem_zalloc(size, KM_SLEEP);
51747c478bd9Sstevel@tonic-gate 	cfg_array_len = kmem_zalloc(ncfgs * sizeof (uint16_t), KM_SLEEP);
51757c478bd9Sstevel@tonic-gate 	str_descr = kmem_zalloc(size, KM_SLEEP);
51767c478bd9Sstevel@tonic-gate 
51777c478bd9Sstevel@tonic-gate 	mutex_enter(&child_ud->usb_mutex);
51787c478bd9Sstevel@tonic-gate 	child_ud->usb_cfg_array = cfg_array;
51797c478bd9Sstevel@tonic-gate 	child_ud->usb_cfg_array_len = cfg_array_len;
51807c478bd9Sstevel@tonic-gate 	child_ud->usb_cfg_array_length = size;
51817c478bd9Sstevel@tonic-gate 	child_ud->usb_cfg_array_len_length = ncfgs * sizeof (uint16_t);
51827c478bd9Sstevel@tonic-gate 	child_ud->usb_cfg_str_descr = str_descr;
51837c478bd9Sstevel@tonic-gate 	mutex_exit(&child_ud->usb_mutex);
51847c478bd9Sstevel@tonic-gate 
51857c478bd9Sstevel@tonic-gate 	/* Get configuration descriptor for each configuration */
51867c478bd9Sstevel@tonic-gate 	for (conf_index = 0; (conf_index < ncfgs) &&
51877c478bd9Sstevel@tonic-gate 	    (rval == USB_SUCCESS); conf_index++) {
51887c478bd9Sstevel@tonic-gate 
51897c478bd9Sstevel@tonic-gate 		rval = hubd_get_this_config_cloud(hubd, dip, child_ud,
51907c478bd9Sstevel@tonic-gate 		    conf_index);
51917c478bd9Sstevel@tonic-gate 	}
51927c478bd9Sstevel@tonic-gate 
51937c478bd9Sstevel@tonic-gate 	return (rval);
51947c478bd9Sstevel@tonic-gate }
51957c478bd9Sstevel@tonic-gate 
51967c478bd9Sstevel@tonic-gate 
51977c478bd9Sstevel@tonic-gate /*
51987c478bd9Sstevel@tonic-gate  * hubd_ready_device:
51997c478bd9Sstevel@tonic-gate  *	Update the usba_device structure
52007c478bd9Sstevel@tonic-gate  *	Set the given configuration
52017c478bd9Sstevel@tonic-gate  *	Prepares the device node for driver to online. If an existing
52027c478bd9Sstevel@tonic-gate  *	OBP node is found, it will switch to the OBP node.
52037c478bd9Sstevel@tonic-gate  */
52047c478bd9Sstevel@tonic-gate static dev_info_t *
52057c478bd9Sstevel@tonic-gate hubd_ready_device(hubd_t *hubd, dev_info_t *child_dip, usba_device_t *child_ud,
52067c478bd9Sstevel@tonic-gate     int config_index)
52077c478bd9Sstevel@tonic-gate {
52087c478bd9Sstevel@tonic-gate 	usb_cr_t	completion_reason;
52097c478bd9Sstevel@tonic-gate 	usb_cb_flags_t	cb_flags;
52107c478bd9Sstevel@tonic-gate 	size_t		size;
52117c478bd9Sstevel@tonic-gate 	usb_cfg_descr_t	config_descriptor;
52127c478bd9Sstevel@tonic-gate 	usb_pipe_handle_t def_ph;
52137c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph;
52147c478bd9Sstevel@tonic-gate 
52157c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
52167c478bd9Sstevel@tonic-gate 	    "hubd_ready_device: dip=0x%p, user_conf_index=%d", child_dip,
52177c478bd9Sstevel@tonic-gate 	    config_index);
52187c478bd9Sstevel@tonic-gate 
52197c478bd9Sstevel@tonic-gate 	ASSERT(config_index >= 0);
52207c478bd9Sstevel@tonic-gate 
52217c478bd9Sstevel@tonic-gate 	size = usb_parse_cfg_descr(
52227c478bd9Sstevel@tonic-gate 	    child_ud->usb_cfg_array[config_index], USB_CFG_DESCR_SIZE,
52237c478bd9Sstevel@tonic-gate 	    &config_descriptor, USB_CFG_DESCR_SIZE);
52247c478bd9Sstevel@tonic-gate 	ASSERT(size == USB_CFG_DESCR_SIZE);
52257c478bd9Sstevel@tonic-gate 
52267c478bd9Sstevel@tonic-gate 	def_ph = usba_get_dflt_pipe_handle(child_dip);
52277c478bd9Sstevel@tonic-gate 
52287c478bd9Sstevel@tonic-gate 	/* Set the configuration */
52297c478bd9Sstevel@tonic-gate 	(void) usb_pipe_sync_ctrl_xfer(child_dip, def_ph,
52307c478bd9Sstevel@tonic-gate 	    USB_DEV_REQ_HOST_TO_DEV,
52317c478bd9Sstevel@tonic-gate 	    USB_REQ_SET_CFG,	/* bRequest */
52327c478bd9Sstevel@tonic-gate 	    config_descriptor.bConfigurationValue,	/* wValue */
52337c478bd9Sstevel@tonic-gate 	    0,				/* wIndex */
52347c478bd9Sstevel@tonic-gate 	    0,				/* wLength */
52357c478bd9Sstevel@tonic-gate 	    NULL,
52367c478bd9Sstevel@tonic-gate 	    0,
52377c478bd9Sstevel@tonic-gate 	    &completion_reason,
52387c478bd9Sstevel@tonic-gate 	    &cb_flags,
52397c478bd9Sstevel@tonic-gate 	    0);
52407c478bd9Sstevel@tonic-gate 
52417c478bd9Sstevel@tonic-gate 	mutex_enter(&child_ud->usb_mutex);
52427c478bd9Sstevel@tonic-gate 	child_ud->usb_active_cfg_ndx	= config_index;
52437c478bd9Sstevel@tonic-gate 	child_ud->usb_cfg		= child_ud->usb_cfg_array[config_index];
52447c478bd9Sstevel@tonic-gate 	child_ud->usb_cfg_length	= config_descriptor.wTotalLength;
52457c478bd9Sstevel@tonic-gate 	child_ud->usb_cfg_value 	= config_descriptor.bConfigurationValue;
52467c478bd9Sstevel@tonic-gate 	child_ud->usb_n_ifs		= config_descriptor.bNumInterfaces;
52477c478bd9Sstevel@tonic-gate 	child_ud->usb_dip		= child_dip;
52487c478bd9Sstevel@tonic-gate 
52497c478bd9Sstevel@tonic-gate 	child_ud->usb_client_flags	= kmem_zalloc(
52507c478bd9Sstevel@tonic-gate 		child_ud->usb_n_ifs * USBA_CLIENT_FLAG_SIZE, KM_SLEEP);
52517c478bd9Sstevel@tonic-gate 
52527c478bd9Sstevel@tonic-gate 	child_ud->usb_client_attach_list = kmem_zalloc(
52537c478bd9Sstevel@tonic-gate 		child_ud->usb_n_ifs *
52547c478bd9Sstevel@tonic-gate 		sizeof (*child_ud->usb_client_attach_list), KM_SLEEP);
52557c478bd9Sstevel@tonic-gate 
52567c478bd9Sstevel@tonic-gate 	child_ud->usb_client_ev_cb_list = kmem_zalloc(
52577c478bd9Sstevel@tonic-gate 		child_ud->usb_n_ifs *
52587c478bd9Sstevel@tonic-gate 		sizeof (*child_ud->usb_client_ev_cb_list), KM_SLEEP);
52597c478bd9Sstevel@tonic-gate 
52607c478bd9Sstevel@tonic-gate 	mutex_exit(&child_ud->usb_mutex);
52617c478bd9Sstevel@tonic-gate 
52627c478bd9Sstevel@tonic-gate 	/* ready the device node */
52637c478bd9Sstevel@tonic-gate 	child_dip = usba_ready_device_node(child_dip);
52647c478bd9Sstevel@tonic-gate 
52657c478bd9Sstevel@tonic-gate 	/* set owner of default pipe to child dip */
52667c478bd9Sstevel@tonic-gate 	ph = usba_get_ph_data(def_ph);
52677c478bd9Sstevel@tonic-gate 	mutex_enter(&ph->p_mutex);
52687c478bd9Sstevel@tonic-gate 	mutex_enter(&ph->p_ph_impl->usba_ph_mutex);
52697c478bd9Sstevel@tonic-gate 	ph->p_ph_impl->usba_ph_dip = ph->p_dip = child_dip;
52707c478bd9Sstevel@tonic-gate 	mutex_exit(&ph->p_ph_impl->usba_ph_mutex);
52717c478bd9Sstevel@tonic-gate 	mutex_exit(&ph->p_mutex);
52727c478bd9Sstevel@tonic-gate 
52737c478bd9Sstevel@tonic-gate 	return (child_dip);
52747c478bd9Sstevel@tonic-gate }
52757c478bd9Sstevel@tonic-gate 
52767c478bd9Sstevel@tonic-gate 
52777c478bd9Sstevel@tonic-gate /*
52787c478bd9Sstevel@tonic-gate  * hubd_create_child
52797c478bd9Sstevel@tonic-gate  *	- create child dip
52807c478bd9Sstevel@tonic-gate  *	- open default pipe
52817c478bd9Sstevel@tonic-gate  *	- get device descriptor
52827c478bd9Sstevel@tonic-gate  *	- set the address
52837c478bd9Sstevel@tonic-gate  *	- get device string descriptors
52847c478bd9Sstevel@tonic-gate  *	- get the entire config cloud (all configurations) of the device
52857c478bd9Sstevel@tonic-gate  *	- set user preferred configuration
52867c478bd9Sstevel@tonic-gate  *	- close default pipe
52877c478bd9Sstevel@tonic-gate  *	- load appropriate driver(s)
52887c478bd9Sstevel@tonic-gate  */
52897c478bd9Sstevel@tonic-gate static int
52907c478bd9Sstevel@tonic-gate hubd_create_child(dev_info_t *dip,
52917c478bd9Sstevel@tonic-gate 		hubd_t		*hubd,
52927c478bd9Sstevel@tonic-gate 		usba_device_t	*hubd_ud,
52937c478bd9Sstevel@tonic-gate 		usb_port_status_t port_status,
52947c478bd9Sstevel@tonic-gate 		usb_port_t	port,
52957c478bd9Sstevel@tonic-gate 		int		iteration)
52967c478bd9Sstevel@tonic-gate {
52977c478bd9Sstevel@tonic-gate 	dev_info_t		*child_dip = NULL;
52987c478bd9Sstevel@tonic-gate 	usb_dev_descr_t	usb_dev_descr;
52997c478bd9Sstevel@tonic-gate 	int			rval;
53007c478bd9Sstevel@tonic-gate 	usba_device_t		*child_ud = NULL;
53017c478bd9Sstevel@tonic-gate 	usba_device_t		*parent_ud = NULL;
53027c478bd9Sstevel@tonic-gate 	usb_pipe_handle_t	ph = NULL; /* default pipe handle */
53037c478bd9Sstevel@tonic-gate 	mblk_t			*pdata = NULL;
53047c478bd9Sstevel@tonic-gate 	usb_cr_t		completion_reason;
53057c478bd9Sstevel@tonic-gate 	int			user_conf_index, config_index;
53067c478bd9Sstevel@tonic-gate 	usb_cb_flags_t		cb_flags;
53077c478bd9Sstevel@tonic-gate 	uchar_t			address = 0;
53087c478bd9Sstevel@tonic-gate 	uint16_t		length;
53097c478bd9Sstevel@tonic-gate 	size_t			size;
53107c478bd9Sstevel@tonic-gate 	usb_addr_t		parent_usb_addr;
53117c478bd9Sstevel@tonic-gate 	usb_port_t		parent_usb_port;
53127c478bd9Sstevel@tonic-gate 	usba_device_t		*parent_usba_dev;
53137c478bd9Sstevel@tonic-gate 	usb_port_status_t	parent_port_status;
53147c478bd9Sstevel@tonic-gate 
53157c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
53167c478bd9Sstevel@tonic-gate 	    "hubd_create_child: port=%d", port);
53177c478bd9Sstevel@tonic-gate 
53187c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
53197c478bd9Sstevel@tonic-gate 	ASSERT(hubd->h_usba_devices[port] == NULL);
53207c478bd9Sstevel@tonic-gate 
53217c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
53227c478bd9Sstevel@tonic-gate 
53237c478bd9Sstevel@tonic-gate 	/*
53247c478bd9Sstevel@tonic-gate 	 * create a dip which can be used to open the pipe. we set
53257c478bd9Sstevel@tonic-gate 	 * the name after getting the descriptors from the device
53267c478bd9Sstevel@tonic-gate 	 */
53277c478bd9Sstevel@tonic-gate 	rval = usba_create_child_devi(dip,
53287c478bd9Sstevel@tonic-gate 			"device",		/* driver name */
53297c478bd9Sstevel@tonic-gate 			hubd_ud->usb_hcdi_ops, /* usba_hcdi ops */
53307c478bd9Sstevel@tonic-gate 			hubd_ud->usb_root_hub_dip,
53317c478bd9Sstevel@tonic-gate 			port_status,		/* low speed device */
53327c478bd9Sstevel@tonic-gate 			child_ud,
53337c478bd9Sstevel@tonic-gate 			&child_dip);
53347c478bd9Sstevel@tonic-gate 
53357c478bd9Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
53367c478bd9Sstevel@tonic-gate 
5337*d291d9f2Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
53387c478bd9Sstevel@tonic-gate 		    "usb_create_child_devi failed (%d)", rval);
53397c478bd9Sstevel@tonic-gate 
53407c478bd9Sstevel@tonic-gate 		goto fail_cleanup;
53417c478bd9Sstevel@tonic-gate 	}
53427c478bd9Sstevel@tonic-gate 
53437c478bd9Sstevel@tonic-gate 	child_ud = usba_get_usba_device(child_dip);
53447c478bd9Sstevel@tonic-gate 	ASSERT(child_ud != NULL);
53457c478bd9Sstevel@tonic-gate 
53467c478bd9Sstevel@tonic-gate 	parent_ud = hubd->h_usba_device;
53477c478bd9Sstevel@tonic-gate 	mutex_enter(&parent_ud->usb_mutex);
53487c478bd9Sstevel@tonic-gate 	parent_port_status = parent_ud->usb_port_status;
53497c478bd9Sstevel@tonic-gate 
53507c478bd9Sstevel@tonic-gate 	/*
53517c478bd9Sstevel@tonic-gate 	 * To support split transactions, update address and port
53527c478bd9Sstevel@tonic-gate 	 * of high speed hub to which given device is connected.
53537c478bd9Sstevel@tonic-gate 	 */
53547c478bd9Sstevel@tonic-gate 	if (parent_port_status == USBA_HIGH_SPEED_DEV) {
53557c478bd9Sstevel@tonic-gate 		parent_usba_dev = parent_ud;
53567c478bd9Sstevel@tonic-gate 		parent_usb_addr = parent_ud->usb_addr;
53577c478bd9Sstevel@tonic-gate 		parent_usb_port = port;
53587c478bd9Sstevel@tonic-gate 	} else {
53597c478bd9Sstevel@tonic-gate 		parent_usba_dev = parent_ud->usb_hs_hub_usba_dev;
53607c478bd9Sstevel@tonic-gate 		parent_usb_addr = parent_ud->usb_hs_hub_addr;
53617c478bd9Sstevel@tonic-gate 		parent_usb_port = parent_ud->usb_hs_hub_port;
53627c478bd9Sstevel@tonic-gate 	}
53637c478bd9Sstevel@tonic-gate 	mutex_exit(&parent_ud->usb_mutex);
53647c478bd9Sstevel@tonic-gate 
53657c478bd9Sstevel@tonic-gate 	mutex_enter(&child_ud->usb_mutex);
53667c478bd9Sstevel@tonic-gate 	address = child_ud->usb_addr;
53677c478bd9Sstevel@tonic-gate 	child_ud->usb_addr = 0;
53687c478bd9Sstevel@tonic-gate 	child_ud->usb_dev_descr = kmem_alloc(sizeof (usb_dev_descr_t),
53697c478bd9Sstevel@tonic-gate 	    KM_SLEEP);
53707c478bd9Sstevel@tonic-gate 	bzero(&usb_dev_descr, sizeof (usb_dev_descr_t));
53717c478bd9Sstevel@tonic-gate 	usb_dev_descr.bMaxPacketSize0 =
53727c478bd9Sstevel@tonic-gate 	    (port_status == USBA_LOW_SPEED_DEV) ? 8 : 64;
53737c478bd9Sstevel@tonic-gate 	bcopy(&usb_dev_descr, child_ud->usb_dev_descr,
53747c478bd9Sstevel@tonic-gate 	    sizeof (usb_dev_descr_t));
53757c478bd9Sstevel@tonic-gate 	child_ud->usb_port = port;
53767c478bd9Sstevel@tonic-gate 	child_ud->usb_hs_hub_usba_dev = parent_usba_dev;
53777c478bd9Sstevel@tonic-gate 	child_ud->usb_hs_hub_addr = parent_usb_addr;
53787c478bd9Sstevel@tonic-gate 	child_ud->usb_hs_hub_port = parent_usb_port;
53797c478bd9Sstevel@tonic-gate 	mutex_exit(&child_ud->usb_mutex);
53807c478bd9Sstevel@tonic-gate 
53817c478bd9Sstevel@tonic-gate 	/* Open the default pipe */
53827c478bd9Sstevel@tonic-gate 	if ((rval = usb_pipe_open(child_dip, NULL, NULL,
53837c478bd9Sstevel@tonic-gate 	    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) != USB_SUCCESS) {
5384*d291d9f2Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5385*d291d9f2Sfrits 		    "usb_pipe_open failed (%d)", rval);
53867c478bd9Sstevel@tonic-gate 
53877c478bd9Sstevel@tonic-gate 		goto fail_cleanup;
53887c478bd9Sstevel@tonic-gate 	}
53897c478bd9Sstevel@tonic-gate 
53907c478bd9Sstevel@tonic-gate 	/*
53917c478bd9Sstevel@tonic-gate 	 * get device descriptor
53927c478bd9Sstevel@tonic-gate 	 */
53937c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
53947c478bd9Sstevel@tonic-gate 	    "hubd_create_child: get device descriptor: 64 bytes");
53957c478bd9Sstevel@tonic-gate 
53967c478bd9Sstevel@tonic-gate 	rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
53977c478bd9Sstevel@tonic-gate 	    USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
53987c478bd9Sstevel@tonic-gate 	    USB_REQ_GET_DESCR,			/* bRequest */
53997c478bd9Sstevel@tonic-gate 	    USB_DESCR_TYPE_SETUP_DEV,		/* wValue */
54007c478bd9Sstevel@tonic-gate 	    0,					/* wIndex */
54017c478bd9Sstevel@tonic-gate 	    64,					/* wLength */
54027c478bd9Sstevel@tonic-gate 	    &pdata, USB_ATTRS_SHORT_XFER_OK,
54037c478bd9Sstevel@tonic-gate 	    &completion_reason, &cb_flags, 0);
54047c478bd9Sstevel@tonic-gate 
54057c478bd9Sstevel@tonic-gate 	if ((rval != USB_SUCCESS) &&
54067c478bd9Sstevel@tonic-gate 	    (!((completion_reason == USB_CR_DATA_OVERRUN) && pdata))) {
54077c478bd9Sstevel@tonic-gate 
54087c478bd9Sstevel@tonic-gate 		/*
54097c478bd9Sstevel@tonic-gate 		 * rval != USB_SUCCESS AND
54107c478bd9Sstevel@tonic-gate 		 * completion_reason != USB_CR_DATA_OVERRUN
54117c478bd9Sstevel@tonic-gate 		 * pdata could be != NULL.
54127c478bd9Sstevel@tonic-gate 		 * Free pdata now to prevent memory leak.
54137c478bd9Sstevel@tonic-gate 		 */
54147c478bd9Sstevel@tonic-gate 		freemsg(pdata);
54157c478bd9Sstevel@tonic-gate 		pdata = NULL;
54167c478bd9Sstevel@tonic-gate 
54177c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
54187c478bd9Sstevel@tonic-gate 		    "hubd_create_child: get device descriptor: 8 bytes");
54197c478bd9Sstevel@tonic-gate 
54207c478bd9Sstevel@tonic-gate 		rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
54217c478bd9Sstevel@tonic-gate 		    USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
54227c478bd9Sstevel@tonic-gate 		    USB_REQ_GET_DESCR,			/* bRequest */
54237c478bd9Sstevel@tonic-gate 		    USB_DESCR_TYPE_SETUP_DEV,		/* wValue */
54247c478bd9Sstevel@tonic-gate 		    0,					/* wIndex */
54257c478bd9Sstevel@tonic-gate 		    8,					/* wLength */
54267c478bd9Sstevel@tonic-gate 		    &pdata, USB_ATTRS_NONE,
54277c478bd9Sstevel@tonic-gate 		    &completion_reason, &cb_flags, 0);
54287c478bd9Sstevel@tonic-gate 
54297c478bd9Sstevel@tonic-gate 		if (rval != USB_SUCCESS) {
5430*d291d9f2Sfrits 			USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
54317c478bd9Sstevel@tonic-gate 			    "getting device descriptor failed (%s 0x%x %d)",
54327c478bd9Sstevel@tonic-gate 			    usb_str_cr(completion_reason), cb_flags, rval);
54337c478bd9Sstevel@tonic-gate 			goto fail_cleanup;
54347c478bd9Sstevel@tonic-gate 		}
54357c478bd9Sstevel@tonic-gate 	} else {
54367c478bd9Sstevel@tonic-gate 		ASSERT(completion_reason == USB_CR_OK);
54377c478bd9Sstevel@tonic-gate 	}
54387c478bd9Sstevel@tonic-gate 
54397c478bd9Sstevel@tonic-gate 	ASSERT(pdata != NULL);
54407c478bd9Sstevel@tonic-gate 
54417c478bd9Sstevel@tonic-gate 	size = usb_parse_dev_descr(
54427c478bd9Sstevel@tonic-gate 			pdata->b_rptr,
54437c478bd9Sstevel@tonic-gate 			pdata->b_wptr - pdata->b_rptr,
54447c478bd9Sstevel@tonic-gate 			&usb_dev_descr,
54457c478bd9Sstevel@tonic-gate 			sizeof (usb_dev_descr_t));
54467c478bd9Sstevel@tonic-gate 
54477c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
54487c478bd9Sstevel@tonic-gate 	    "parsing device descriptor returned %lu", size);
54497c478bd9Sstevel@tonic-gate 
54507c478bd9Sstevel@tonic-gate 	length = *(pdata->b_rptr);
54517c478bd9Sstevel@tonic-gate 	freemsg(pdata);
54527c478bd9Sstevel@tonic-gate 	pdata = NULL;
54537c478bd9Sstevel@tonic-gate 	if (size < 8) {
5454*d291d9f2Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
54557c478bd9Sstevel@tonic-gate 		    "get device descriptor returned %lu bytes", size);
54567c478bd9Sstevel@tonic-gate 
54577c478bd9Sstevel@tonic-gate 		goto fail_cleanup;
54587c478bd9Sstevel@tonic-gate 	}
54597c478bd9Sstevel@tonic-gate 
54607c478bd9Sstevel@tonic-gate 	if (length < 8) {
5461*d291d9f2Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
54627c478bd9Sstevel@tonic-gate 		    "fail enumeration: bLength=%d", length);
54637c478bd9Sstevel@tonic-gate 
54647c478bd9Sstevel@tonic-gate 		goto fail_cleanup;
54657c478bd9Sstevel@tonic-gate 	}
54667c478bd9Sstevel@tonic-gate 
54677c478bd9Sstevel@tonic-gate 	/* Set the address of the device */
54687c478bd9Sstevel@tonic-gate 	if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
54697c478bd9Sstevel@tonic-gate 	    USB_DEV_REQ_HOST_TO_DEV,
54707c478bd9Sstevel@tonic-gate 	    USB_REQ_SET_ADDRESS,	/* bRequest */
54717c478bd9Sstevel@tonic-gate 	    address,			/* wValue */
54727c478bd9Sstevel@tonic-gate 	    0,				/* wIndex */
54737c478bd9Sstevel@tonic-gate 	    0,				/* wLength */
54747c478bd9Sstevel@tonic-gate 	    NULL, 0,
54757c478bd9Sstevel@tonic-gate 	    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
54767c478bd9Sstevel@tonic-gate 		char buffer[64];
5477*d291d9f2Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
54787c478bd9Sstevel@tonic-gate 		    "setting address failed (cr=%s cb_flags=%s rval=%d)",
54797c478bd9Sstevel@tonic-gate 		    usb_str_cr(completion_reason),
54807c478bd9Sstevel@tonic-gate 		    usb_str_cb_flags(cb_flags, buffer, sizeof (buffer)),
54817c478bd9Sstevel@tonic-gate 		    rval);
54827c478bd9Sstevel@tonic-gate 
54837c478bd9Sstevel@tonic-gate 		goto fail_cleanup;
54847c478bd9Sstevel@tonic-gate 	}
54857c478bd9Sstevel@tonic-gate 
54867c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
54877c478bd9Sstevel@tonic-gate 	    "set address 0x%x done", address);
54887c478bd9Sstevel@tonic-gate 
54897c478bd9Sstevel@tonic-gate 	/* now close the pipe for addr 0 */
54907c478bd9Sstevel@tonic-gate 	usb_pipe_close(child_dip, ph,
54917c478bd9Sstevel@tonic-gate 	    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
54927c478bd9Sstevel@tonic-gate 
54937c478bd9Sstevel@tonic-gate 	/*
54947c478bd9Sstevel@tonic-gate 	 * This delay is important for the CATC hub to enumerate
54957c478bd9Sstevel@tonic-gate 	 * But, avoid delay in the first iteration
54967c478bd9Sstevel@tonic-gate 	 */
54977c478bd9Sstevel@tonic-gate 	if (iteration) {
54987c478bd9Sstevel@tonic-gate 		delay(drv_usectohz(hubd_device_delay/100));
54997c478bd9Sstevel@tonic-gate 	}
55007c478bd9Sstevel@tonic-gate 
55017c478bd9Sstevel@tonic-gate 	/* assign the address in the usba_device structure */
55027c478bd9Sstevel@tonic-gate 	mutex_enter(&child_ud->usb_mutex);
55037c478bd9Sstevel@tonic-gate 	child_ud->usb_addr = address;
55047c478bd9Sstevel@tonic-gate 	child_ud->usb_no_cpr = 0;
55057c478bd9Sstevel@tonic-gate 	child_ud->usb_port_status = port_status;
55067c478bd9Sstevel@tonic-gate 	/* save this device descriptor */
55077c478bd9Sstevel@tonic-gate 	bcopy(&usb_dev_descr, child_ud->usb_dev_descr,
55087c478bd9Sstevel@tonic-gate 					sizeof (usb_dev_descr_t));
55097c478bd9Sstevel@tonic-gate 	child_ud->usb_n_cfgs = usb_dev_descr.bNumConfigurations;
55107c478bd9Sstevel@tonic-gate 	mutex_exit(&child_ud->usb_mutex);
55117c478bd9Sstevel@tonic-gate 
55127c478bd9Sstevel@tonic-gate 	/* re-open the pipe for the device with the new address */
55137c478bd9Sstevel@tonic-gate 	if ((rval = usb_pipe_open(child_dip, NULL, NULL,
55147c478bd9Sstevel@tonic-gate 	    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) != USB_SUCCESS) {
5515*d291d9f2Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
55167c478bd9Sstevel@tonic-gate 		    "usb_pipe_open failed (%d)", rval);
55177c478bd9Sstevel@tonic-gate 
55187c478bd9Sstevel@tonic-gate 		goto fail_cleanup;
55197c478bd9Sstevel@tonic-gate 	}
55207c478bd9Sstevel@tonic-gate 
55217c478bd9Sstevel@tonic-gate 	/*
55227c478bd9Sstevel@tonic-gate 	 * Get full device descriptor only if we have not received full
55237c478bd9Sstevel@tonic-gate 	 * device descriptor earlier.
55247c478bd9Sstevel@tonic-gate 	 */
55257c478bd9Sstevel@tonic-gate 	if (size < length) {
55267c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
55277c478bd9Sstevel@tonic-gate 		    "hubd_create_child: get full device descriptor: "
55287c478bd9Sstevel@tonic-gate 		    "%d bytes", length);
55297c478bd9Sstevel@tonic-gate 
55307c478bd9Sstevel@tonic-gate 		if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
55317c478bd9Sstevel@tonic-gate 		    USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
55327c478bd9Sstevel@tonic-gate 		    USB_REQ_GET_DESCR,			/* bRequest */
55337c478bd9Sstevel@tonic-gate 		    USB_DESCR_TYPE_SETUP_DEV,		/* wValue */
55347c478bd9Sstevel@tonic-gate 		    0,					/* wIndex */
55357c478bd9Sstevel@tonic-gate 		    length,				/* wLength */
55367c478bd9Sstevel@tonic-gate 		    &pdata, 0,
55377c478bd9Sstevel@tonic-gate 		    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
55387c478bd9Sstevel@tonic-gate 			freemsg(pdata);
55397c478bd9Sstevel@tonic-gate 			pdata = NULL;
55407c478bd9Sstevel@tonic-gate 
55417c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG,
55427c478bd9Sstevel@tonic-gate 			    hubd->h_log_handle,
55437c478bd9Sstevel@tonic-gate 			    "hubd_create_child: get full device descriptor: "
55447c478bd9Sstevel@tonic-gate 			    "64 bytes");
55457c478bd9Sstevel@tonic-gate 
55467c478bd9Sstevel@tonic-gate 			rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
55477c478bd9Sstevel@tonic-gate 			    USB_DEV_REQ_DEV_TO_HOST |
55487c478bd9Sstevel@tonic-gate 			    USB_DEV_REQ_TYPE_STANDARD,
55497c478bd9Sstevel@tonic-gate 			    USB_REQ_GET_DESCR,		/* bRequest */
55507c478bd9Sstevel@tonic-gate 			    USB_DESCR_TYPE_SETUP_DEV,	/* wValue */
55517c478bd9Sstevel@tonic-gate 			    0,				/* wIndex */
55527c478bd9Sstevel@tonic-gate 			    64,				/* wLength */
55537c478bd9Sstevel@tonic-gate 			    &pdata, USB_ATTRS_SHORT_XFER_OK,
55547c478bd9Sstevel@tonic-gate 			    &completion_reason, &cb_flags, 0);
55557c478bd9Sstevel@tonic-gate 
55567c478bd9Sstevel@tonic-gate 			/* we have to trust the data now */
55577c478bd9Sstevel@tonic-gate 			if (pdata) {
55587c478bd9Sstevel@tonic-gate 				int len = *(pdata->b_rptr);
55597c478bd9Sstevel@tonic-gate 
55607c478bd9Sstevel@tonic-gate 				length = pdata->b_wptr - pdata->b_rptr;
55617c478bd9Sstevel@tonic-gate 				if (length < len) {
55627c478bd9Sstevel@tonic-gate 
55637c478bd9Sstevel@tonic-gate 					goto fail_cleanup;
55647c478bd9Sstevel@tonic-gate 				}
55657c478bd9Sstevel@tonic-gate 			} else if (rval != USB_SUCCESS) {
5566*d291d9f2Sfrits 				USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
55677c478bd9Sstevel@tonic-gate 				    hubd->h_log_handle,
55687c478bd9Sstevel@tonic-gate 				    "getting device descriptor failed "
55697c478bd9Sstevel@tonic-gate 				    "(%d 0x%x %d)",
55707c478bd9Sstevel@tonic-gate 				completion_reason, cb_flags, rval);
55717c478bd9Sstevel@tonic-gate 
55727c478bd9Sstevel@tonic-gate 				goto fail_cleanup;
55737c478bd9Sstevel@tonic-gate 			}
55747c478bd9Sstevel@tonic-gate 		}
55757c478bd9Sstevel@tonic-gate 
55767c478bd9Sstevel@tonic-gate 		size = usb_parse_dev_descr(
55777c478bd9Sstevel@tonic-gate 				pdata->b_rptr,
55787c478bd9Sstevel@tonic-gate 				pdata->b_wptr - pdata->b_rptr,
55797c478bd9Sstevel@tonic-gate 				&usb_dev_descr,
55807c478bd9Sstevel@tonic-gate 				sizeof (usb_dev_descr_t));
55817c478bd9Sstevel@tonic-gate 
55827c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
55837c478bd9Sstevel@tonic-gate 		    "parsing device descriptor returned %lu", size);
55847c478bd9Sstevel@tonic-gate 
55857c478bd9Sstevel@tonic-gate 		/*
55867c478bd9Sstevel@tonic-gate 		 * For now, free the data
55877c478bd9Sstevel@tonic-gate 		 * eventually, each configuration may need to be looked at
55887c478bd9Sstevel@tonic-gate 		 */
55897c478bd9Sstevel@tonic-gate 		freemsg(pdata);
55907c478bd9Sstevel@tonic-gate 		pdata = NULL;
55917c478bd9Sstevel@tonic-gate 
55927c478bd9Sstevel@tonic-gate 		if (size != USB_DEV_DESCR_SIZE) {
5593*d291d9f2Sfrits 			USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
55947c478bd9Sstevel@tonic-gate 			    "fail enumeration: descriptor size=%lu "
55957c478bd9Sstevel@tonic-gate 			    "expected size=%u", size, USB_DEV_DESCR_SIZE);
55967c478bd9Sstevel@tonic-gate 
55977c478bd9Sstevel@tonic-gate 			goto fail_cleanup;
55987c478bd9Sstevel@tonic-gate 		}
55997c478bd9Sstevel@tonic-gate 
56007c478bd9Sstevel@tonic-gate 		/*
56017c478bd9Sstevel@tonic-gate 		 * save the device descriptor in usba_device since it is needed
56027c478bd9Sstevel@tonic-gate 		 * later on again
56037c478bd9Sstevel@tonic-gate 		 */
56047c478bd9Sstevel@tonic-gate 		mutex_enter(&child_ud->usb_mutex);
56057c478bd9Sstevel@tonic-gate 		bcopy(&usb_dev_descr, child_ud->usb_dev_descr,
56067c478bd9Sstevel@tonic-gate 			sizeof (usb_dev_descr_t));
56077c478bd9Sstevel@tonic-gate 		child_ud->usb_n_cfgs = usb_dev_descr.bNumConfigurations;
56087c478bd9Sstevel@tonic-gate 		mutex_exit(&child_ud->usb_mutex);
56097c478bd9Sstevel@tonic-gate 	}
56107c478bd9Sstevel@tonic-gate 
56117c478bd9Sstevel@tonic-gate 	if (usb_dev_descr.bNumConfigurations == 0) {
5612*d291d9f2Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
56137c478bd9Sstevel@tonic-gate 		    "device descriptor:\n\t"
56147c478bd9Sstevel@tonic-gate 		    "l=0x%x type=0x%x USB=0x%x class=0x%x subclass=0x%x\n\t"
56157c478bd9Sstevel@tonic-gate 		    "protocol=0x%x maxpktsize=0x%x "
56167c478bd9Sstevel@tonic-gate 		    "Vid=0x%x Pid=0x%x rel=0x%x\n\t"
56177c478bd9Sstevel@tonic-gate 		    "Mfg=0x%x P=0x%x sn=0x%x #config=0x%x",
56187c478bd9Sstevel@tonic-gate 		    usb_dev_descr.bLength, usb_dev_descr.bDescriptorType,
56197c478bd9Sstevel@tonic-gate 		    usb_dev_descr.bcdUSB, usb_dev_descr.bDeviceClass,
56207c478bd9Sstevel@tonic-gate 		    usb_dev_descr.bDeviceSubClass,
56217c478bd9Sstevel@tonic-gate 		    usb_dev_descr.bDeviceProtocol,
56227c478bd9Sstevel@tonic-gate 		    usb_dev_descr.bMaxPacketSize0,
56237c478bd9Sstevel@tonic-gate 		    usb_dev_descr.idVendor,
56247c478bd9Sstevel@tonic-gate 		    usb_dev_descr.idProduct, usb_dev_descr.bcdDevice,
56257c478bd9Sstevel@tonic-gate 		    usb_dev_descr.iManufacturer, usb_dev_descr.iProduct,
56267c478bd9Sstevel@tonic-gate 		    usb_dev_descr.iSerialNumber,
56277c478bd9Sstevel@tonic-gate 		    usb_dev_descr.bNumConfigurations);
56287c478bd9Sstevel@tonic-gate 		goto fail_cleanup;
56297c478bd9Sstevel@tonic-gate 	}
56307c478bd9Sstevel@tonic-gate 
56317c478bd9Sstevel@tonic-gate 
56327c478bd9Sstevel@tonic-gate 	/* get the device string descriptor(s) */
56337c478bd9Sstevel@tonic-gate 	usba_get_dev_string_descrs(child_dip, child_ud);
56347c478bd9Sstevel@tonic-gate 
56357c478bd9Sstevel@tonic-gate 	/* retrieve config cloud for all configurations */
56367c478bd9Sstevel@tonic-gate 	rval = hubd_get_all_device_config_cloud(hubd, child_dip, child_ud);
56377c478bd9Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
5638*d291d9f2Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
56397c478bd9Sstevel@tonic-gate 		    "failed to get configuration descriptor(s)");
56407c478bd9Sstevel@tonic-gate 
56417c478bd9Sstevel@tonic-gate 		goto fail_cleanup;
56427c478bd9Sstevel@tonic-gate 	}
56437c478bd9Sstevel@tonic-gate 
56447c478bd9Sstevel@tonic-gate 	/* get the preferred configuration for this device */
56457c478bd9Sstevel@tonic-gate 	user_conf_index = hubd_select_device_configuration(hubd, port,
56467c478bd9Sstevel@tonic-gate 	    child_dip, child_ud);
56477c478bd9Sstevel@tonic-gate 
56487c478bd9Sstevel@tonic-gate 	/* Check if the user selected configuration index is in range */
56497c478bd9Sstevel@tonic-gate 	if (user_conf_index >= usb_dev_descr.bNumConfigurations) {
5650*d291d9f2Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
56517c478bd9Sstevel@tonic-gate 		    "Configuration index for device idVendor=%d "
56527c478bd9Sstevel@tonic-gate 		    "idProduct=%d is=%d, and is out of range[0..%d]",
56537c478bd9Sstevel@tonic-gate 		    usb_dev_descr.idVendor, usb_dev_descr.idProduct,
56547c478bd9Sstevel@tonic-gate 		    user_conf_index, usb_dev_descr.bNumConfigurations - 1);
56557c478bd9Sstevel@tonic-gate 
56567c478bd9Sstevel@tonic-gate 		/* treat this as user didn't specify configuration */
56577c478bd9Sstevel@tonic-gate 		user_conf_index = USBA_DEV_CONFIG_INDEX_UNDEFINED;
56587c478bd9Sstevel@tonic-gate 	}
56597c478bd9Sstevel@tonic-gate 
56607c478bd9Sstevel@tonic-gate 
56617c478bd9Sstevel@tonic-gate 	/*
56627c478bd9Sstevel@tonic-gate 	 * Warn users of a performance hit if connecting a
56637c478bd9Sstevel@tonic-gate 	 * High Speed behind a 1.1 hub, which is behind a
56647c478bd9Sstevel@tonic-gate 	 * 2.0 port.
56657c478bd9Sstevel@tonic-gate 	 */
56667c478bd9Sstevel@tonic-gate 	if ((parent_port_status != USBA_HIGH_SPEED_DEV) &&
56677c478bd9Sstevel@tonic-gate 	    !(usba_is_root_hub(parent_ud->usb_dip)) &&
56687c478bd9Sstevel@tonic-gate 	    (parent_usb_addr)) {
56697c478bd9Sstevel@tonic-gate 
56707c478bd9Sstevel@tonic-gate 		/*
56717c478bd9Sstevel@tonic-gate 		 * Now that we know the root port is a high speed port
56727c478bd9Sstevel@tonic-gate 		 * and that the parent port is not a high speed port,
56737c478bd9Sstevel@tonic-gate 		 * let's find out if the device itself is a high speed
56747c478bd9Sstevel@tonic-gate 		 * device.  If it is a high speed device,
56757c478bd9Sstevel@tonic-gate 		 * USB_DESCR_TYPE_SETUP_DEV_QLF should return a value,
56767c478bd9Sstevel@tonic-gate 		 * otherwise the command will fail.
56777c478bd9Sstevel@tonic-gate 		 */
56787c478bd9Sstevel@tonic-gate 		rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
56797c478bd9Sstevel@tonic-gate 		    USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
56807c478bd9Sstevel@tonic-gate 		    USB_REQ_GET_DESCR,			/* bRequest */
56817c478bd9Sstevel@tonic-gate 		    USB_DESCR_TYPE_SETUP_DEV_QLF,	/* wValue */
56827c478bd9Sstevel@tonic-gate 		    0,					/* wIndex */
56837c478bd9Sstevel@tonic-gate 		    10,					/* wLength */
56847c478bd9Sstevel@tonic-gate 		    &pdata, USB_ATTRS_SHORT_XFER_OK,
56857c478bd9Sstevel@tonic-gate 		    &completion_reason, &cb_flags, 0);
56867c478bd9Sstevel@tonic-gate 
56877c478bd9Sstevel@tonic-gate 		if (pdata) {
56887c478bd9Sstevel@tonic-gate 			freemsg(pdata);
56897c478bd9Sstevel@tonic-gate 			pdata = NULL;
56907c478bd9Sstevel@tonic-gate 		}
56917c478bd9Sstevel@tonic-gate 
56927c478bd9Sstevel@tonic-gate 		/*
56937c478bd9Sstevel@tonic-gate 		 * USB_DESCR_TYPE_SETUP_DEV_QLF query was successful
56947c478bd9Sstevel@tonic-gate 		 * that means this is a high speed device behind a
56957c478bd9Sstevel@tonic-gate 		 * high speed root hub, but running at full speed
56967c478bd9Sstevel@tonic-gate 		 * because there is a full speed hub in the middle.
56977c478bd9Sstevel@tonic-gate 		 */
56987c478bd9Sstevel@tonic-gate 		if (rval == USB_SUCCESS) {
56997c478bd9Sstevel@tonic-gate 		    USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG,
57007c478bd9Sstevel@tonic-gate 			hubd->h_log_handle,
57017c478bd9Sstevel@tonic-gate 			"Connecting a high speed device to a "
57027c478bd9Sstevel@tonic-gate 			"non high speed hub (port %d) will result "
57037c478bd9Sstevel@tonic-gate 			"in a loss of performance.  Please connect "
57047c478bd9Sstevel@tonic-gate 			"the device to a high speed hub to get "
57057c478bd9Sstevel@tonic-gate 			"the maximum performance.",
57067c478bd9Sstevel@tonic-gate 			port);
57077c478bd9Sstevel@tonic-gate 		}
57087c478bd9Sstevel@tonic-gate 	}
57097c478bd9Sstevel@tonic-gate 
57107c478bd9Sstevel@tonic-gate 	/*
57117c478bd9Sstevel@tonic-gate 	 * Now we try to online the device by attaching a driver
57127c478bd9Sstevel@tonic-gate 	 * The following truth table illustrates the logic:-
57137c478bd9Sstevel@tonic-gate 	 * Cfgndx	Driver	Action
57147c478bd9Sstevel@tonic-gate 	 * 0		0	loop all configs for driver with full
57157c478bd9Sstevel@tonic-gate 	 *			compatible properties.
57167c478bd9Sstevel@tonic-gate 	 * 0		1	set first configuration,
57177c478bd9Sstevel@tonic-gate 	 *			compatible prop = drivername.
57187c478bd9Sstevel@tonic-gate 	 * 1		0	Set config, full compatible prop
57197c478bd9Sstevel@tonic-gate 	 * 1		1	Set config, compatible prop = drivername.
57207c478bd9Sstevel@tonic-gate 	 *
57217c478bd9Sstevel@tonic-gate 	 * Note:
57227c478bd9Sstevel@tonic-gate 	 *	cfgndx = user_conf_index
57237c478bd9Sstevel@tonic-gate 	 *	Driver = usb_preferred_driver
57247c478bd9Sstevel@tonic-gate 	 */
57257c478bd9Sstevel@tonic-gate 	if (user_conf_index == USBA_DEV_CONFIG_INDEX_UNDEFINED) {
57267c478bd9Sstevel@tonic-gate 		if (child_ud->usb_preferred_driver) {
57277c478bd9Sstevel@tonic-gate 			/*
57287c478bd9Sstevel@tonic-gate 			 * It is the job of the "preferred driver" to put the
57297c478bd9Sstevel@tonic-gate 			 * device in the desired configuration. Till then
57307c478bd9Sstevel@tonic-gate 			 * put the device in config index 0.
57317c478bd9Sstevel@tonic-gate 			 */
57327c478bd9Sstevel@tonic-gate 			child_dip = hubd_ready_device(hubd, child_dip,
57337c478bd9Sstevel@tonic-gate 			    child_ud, USB_DEV_DEFAULT_CONFIG_INDEX);
57347c478bd9Sstevel@tonic-gate 
57357c478bd9Sstevel@tonic-gate 			/*
57367c478bd9Sstevel@tonic-gate 			 * Assign the dip before onlining to avoid race
57377c478bd9Sstevel@tonic-gate 			 * with busctl
57387c478bd9Sstevel@tonic-gate 			 */
57397c478bd9Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
57407c478bd9Sstevel@tonic-gate 			hubd->h_children_dips[port] = child_dip;
57417c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
57427c478bd9Sstevel@tonic-gate 
57437c478bd9Sstevel@tonic-gate 			(void) usba_bind_driver(child_dip);
57447c478bd9Sstevel@tonic-gate 		} else {
57457c478bd9Sstevel@tonic-gate 			/*
57467c478bd9Sstevel@tonic-gate 			 * loop through all the configurations to see if we
57477c478bd9Sstevel@tonic-gate 			 * can find a driver for any one config. If not, set
57487c478bd9Sstevel@tonic-gate 			 * the device in config_index 0
57497c478bd9Sstevel@tonic-gate 			 */
57507c478bd9Sstevel@tonic-gate 			rval = USB_FAILURE;
57517c478bd9Sstevel@tonic-gate 			for (config_index = 0;
57527c478bd9Sstevel@tonic-gate 			    (config_index < usb_dev_descr.bNumConfigurations) &&
57537c478bd9Sstevel@tonic-gate 			    (rval != USB_SUCCESS); config_index++) {
57547c478bd9Sstevel@tonic-gate 
57557c478bd9Sstevel@tonic-gate 				child_dip = hubd_ready_device(hubd, child_dip,
57567c478bd9Sstevel@tonic-gate 				    child_ud, config_index);
57577c478bd9Sstevel@tonic-gate 
57587c478bd9Sstevel@tonic-gate 				/*
57597c478bd9Sstevel@tonic-gate 				 * Assign the dip before onlining to avoid race
57607c478bd9Sstevel@tonic-gate 				 * with busctl
57617c478bd9Sstevel@tonic-gate 				 */
57627c478bd9Sstevel@tonic-gate 				mutex_enter(HUBD_MUTEX(hubd));
57637c478bd9Sstevel@tonic-gate 				hubd->h_children_dips[port] = child_dip;
57647c478bd9Sstevel@tonic-gate 				mutex_exit(HUBD_MUTEX(hubd));
57657c478bd9Sstevel@tonic-gate 
57667c478bd9Sstevel@tonic-gate 				rval = usba_bind_driver(child_dip);
57677c478bd9Sstevel@tonic-gate 			}
57687c478bd9Sstevel@tonic-gate 			if (rval != USB_SUCCESS) {
57697c478bd9Sstevel@tonic-gate 				child_dip = hubd_ready_device(hubd, child_dip,
57707c478bd9Sstevel@tonic-gate 				    child_ud, 0);
57717c478bd9Sstevel@tonic-gate 				mutex_enter(HUBD_MUTEX(hubd));
57727c478bd9Sstevel@tonic-gate 				hubd->h_children_dips[port] = child_dip;
57737c478bd9Sstevel@tonic-gate 				mutex_exit(HUBD_MUTEX(hubd));
57747c478bd9Sstevel@tonic-gate 			}
57757c478bd9Sstevel@tonic-gate 		} /* end else loop all configs */
57767c478bd9Sstevel@tonic-gate 	} else {
57777c478bd9Sstevel@tonic-gate 		child_dip = hubd_ready_device(hubd, child_dip,
57787c478bd9Sstevel@tonic-gate 		    child_ud, user_conf_index);
57797c478bd9Sstevel@tonic-gate 
57807c478bd9Sstevel@tonic-gate 		/*
57817c478bd9Sstevel@tonic-gate 		 * Assign the dip before onlining to avoid race
57827c478bd9Sstevel@tonic-gate 		 * with busctl
57837c478bd9Sstevel@tonic-gate 		 */
57847c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
57857c478bd9Sstevel@tonic-gate 		hubd->h_children_dips[port] = child_dip;
57867c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
57877c478bd9Sstevel@tonic-gate 
57887c478bd9Sstevel@tonic-gate 		(void) usba_bind_driver(child_dip);
57897c478bd9Sstevel@tonic-gate 	}
57907c478bd9Sstevel@tonic-gate 
57917c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
57927c478bd9Sstevel@tonic-gate 	if (hubd->h_usba_devices[port] == NULL) {
57937c478bd9Sstevel@tonic-gate 		hubd->h_usba_devices[port] = usba_get_usba_device(child_dip);
57947c478bd9Sstevel@tonic-gate 	} else {
57957c478bd9Sstevel@tonic-gate 		ASSERT(hubd->h_usba_devices[port] ==
57967c478bd9Sstevel@tonic-gate 				usba_get_usba_device(child_dip));
57977c478bd9Sstevel@tonic-gate 	}
57987c478bd9Sstevel@tonic-gate 
57997c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
58007c478bd9Sstevel@tonic-gate 
58017c478bd9Sstevel@tonic-gate 
58027c478bd9Sstevel@tonic-gate fail_cleanup:
58037c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
58047c478bd9Sstevel@tonic-gate 	    "hubd_create_child: fail_cleanup");
58057c478bd9Sstevel@tonic-gate 
58067c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
58077c478bd9Sstevel@tonic-gate 	hubd->h_children_dips[port] = NULL;
58087c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
58097c478bd9Sstevel@tonic-gate 
58107c478bd9Sstevel@tonic-gate 	if (pdata) {
58117c478bd9Sstevel@tonic-gate 		freemsg(pdata);
58127c478bd9Sstevel@tonic-gate 	}
58137c478bd9Sstevel@tonic-gate 
58147c478bd9Sstevel@tonic-gate 	if (ph) {
58157c478bd9Sstevel@tonic-gate 		usb_pipe_close(child_dip, ph,
58167c478bd9Sstevel@tonic-gate 		    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
58177c478bd9Sstevel@tonic-gate 	}
58187c478bd9Sstevel@tonic-gate 
58197c478bd9Sstevel@tonic-gate 	if (child_dip) {
58207c478bd9Sstevel@tonic-gate 		int rval = usba_destroy_child_devi(child_dip,
58217c478bd9Sstevel@tonic-gate 		    NDI_DEVI_REMOVE);
58227c478bd9Sstevel@tonic-gate 		if (rval != USB_SUCCESS) {
5823*d291d9f2Sfrits 			USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
58247c478bd9Sstevel@tonic-gate 			    "failure to remove child node");
58257c478bd9Sstevel@tonic-gate 		}
58267c478bd9Sstevel@tonic-gate 	}
58277c478bd9Sstevel@tonic-gate 
58287c478bd9Sstevel@tonic-gate 	if (child_ud) {
58297c478bd9Sstevel@tonic-gate 		/* to make sure we free the address */
58307c478bd9Sstevel@tonic-gate 		mutex_enter(&child_ud->usb_mutex);
58317c478bd9Sstevel@tonic-gate 		child_ud->usb_addr = address;
58327c478bd9Sstevel@tonic-gate 		ASSERT(child_ud->usb_ref_count == 0);
58337c478bd9Sstevel@tonic-gate 		mutex_exit(&child_ud->usb_mutex);
58347c478bd9Sstevel@tonic-gate 
58357c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
58367c478bd9Sstevel@tonic-gate 		if (hubd->h_usba_devices[port] == NULL) {
58377c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
58387c478bd9Sstevel@tonic-gate 			usba_free_usba_device(child_ud);
58397c478bd9Sstevel@tonic-gate 		} else {
58407c478bd9Sstevel@tonic-gate 			hubd_free_usba_device(hubd, hubd->h_usba_devices[port]);
58417c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
58427c478bd9Sstevel@tonic-gate 		}
58437c478bd9Sstevel@tonic-gate 	}
58447c478bd9Sstevel@tonic-gate 
58457c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
58467c478bd9Sstevel@tonic-gate 
58477c478bd9Sstevel@tonic-gate 	return (USB_FAILURE);
58487c478bd9Sstevel@tonic-gate }
58497c478bd9Sstevel@tonic-gate 
58507c478bd9Sstevel@tonic-gate 
58517c478bd9Sstevel@tonic-gate /*
58527c478bd9Sstevel@tonic-gate  * hubd_delete_child:
58537c478bd9Sstevel@tonic-gate  *	- free usb address
58547c478bd9Sstevel@tonic-gate  *	- lookup child dips, there may be multiple on this port
58557c478bd9Sstevel@tonic-gate  *	- offline each child devi
58567c478bd9Sstevel@tonic-gate  */
58577c478bd9Sstevel@tonic-gate static int
58587c478bd9Sstevel@tonic-gate hubd_delete_child(hubd_t *hubd, usb_port_t port, uint_t flag, boolean_t retry)
58597c478bd9Sstevel@tonic-gate {
58607c478bd9Sstevel@tonic-gate 	dev_info_t	*child_dip;
58617c478bd9Sstevel@tonic-gate 	usba_device_t	*usba_device;
58627c478bd9Sstevel@tonic-gate 	int		rval = USB_SUCCESS;
58637c478bd9Sstevel@tonic-gate 
58647c478bd9Sstevel@tonic-gate 	child_dip = hubd->h_children_dips[port];
58657c478bd9Sstevel@tonic-gate 
58667c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
58677c478bd9Sstevel@tonic-gate 	    "hubd_delete_child: port=%d, dip=0x%p usba_device=0x%p",
58687c478bd9Sstevel@tonic-gate 	    port, child_dip);
58697c478bd9Sstevel@tonic-gate 
58707c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
58717c478bd9Sstevel@tonic-gate 	if (child_dip) {
58727c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
58737c478bd9Sstevel@tonic-gate 		    "hubd_delete_child:\n\t"
58747c478bd9Sstevel@tonic-gate 		    "dip = 0x%p (%s) at port %d",
58757c478bd9Sstevel@tonic-gate 		    child_dip, ddi_node_name(child_dip), port);
58767c478bd9Sstevel@tonic-gate 
58777c478bd9Sstevel@tonic-gate 		rval = usba_destroy_child_devi(child_dip, flag);
58787c478bd9Sstevel@tonic-gate 
58797c478bd9Sstevel@tonic-gate 		if ((rval == USB_SUCCESS) && (flag & NDI_DEVI_REMOVE)) {
58807c478bd9Sstevel@tonic-gate 			/*
58817c478bd9Sstevel@tonic-gate 			 * if the child was still < DS_INITIALIZED
58827c478bd9Sstevel@tonic-gate 			 * then our bus_unconfig was not called and
58837c478bd9Sstevel@tonic-gate 			 * we have to zap the child here
58847c478bd9Sstevel@tonic-gate 			 */
58857c478bd9Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
58867c478bd9Sstevel@tonic-gate 			if (hubd->h_children_dips[port] == child_dip) {
58877c478bd9Sstevel@tonic-gate 				usba_device_t *ud =
58887c478bd9Sstevel@tonic-gate 						hubd->h_usba_devices[port];
58897c478bd9Sstevel@tonic-gate 				hubd->h_children_dips[port] = NULL;
58907c478bd9Sstevel@tonic-gate 				if (ud) {
58917c478bd9Sstevel@tonic-gate 					mutex_exit(HUBD_MUTEX(hubd));
58927c478bd9Sstevel@tonic-gate 
58937c478bd9Sstevel@tonic-gate 					mutex_enter(&ud->usb_mutex);
58947c478bd9Sstevel@tonic-gate 					ud->usb_ref_count = 0;
58957c478bd9Sstevel@tonic-gate 					mutex_exit(&ud->usb_mutex);
58967c478bd9Sstevel@tonic-gate 
58977c478bd9Sstevel@tonic-gate 					usba_free_usba_device(ud);
58987c478bd9Sstevel@tonic-gate 					mutex_enter(HUBD_MUTEX(hubd));
58997c478bd9Sstevel@tonic-gate 					hubd->h_usba_devices[port] = NULL;
59007c478bd9Sstevel@tonic-gate 				}
59017c478bd9Sstevel@tonic-gate 			}
59027c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
59037c478bd9Sstevel@tonic-gate 		}
59047c478bd9Sstevel@tonic-gate 	}
59057c478bd9Sstevel@tonic-gate 
59067c478bd9Sstevel@tonic-gate 	if ((rval != USB_SUCCESS) && retry) {
59077c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
59087c478bd9Sstevel@tonic-gate 		usba_device = hubd->h_usba_devices[port];
59097c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
59107c478bd9Sstevel@tonic-gate 
59117c478bd9Sstevel@tonic-gate 		hubd_schedule_cleanup(usba_device->usb_root_hub_dip);
59127c478bd9Sstevel@tonic-gate 	}
59137c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
59147c478bd9Sstevel@tonic-gate 
59157c478bd9Sstevel@tonic-gate 	return (rval);
59167c478bd9Sstevel@tonic-gate }
59177c478bd9Sstevel@tonic-gate 
59187c478bd9Sstevel@tonic-gate 
59197c478bd9Sstevel@tonic-gate /*
59207c478bd9Sstevel@tonic-gate  * hubd_free_usba_device:
59217c478bd9Sstevel@tonic-gate  *	free usb device structure unless it is associated with
59227c478bd9Sstevel@tonic-gate  *	the root hub which is handled differently
59237c478bd9Sstevel@tonic-gate  */
59247c478bd9Sstevel@tonic-gate static void
59257c478bd9Sstevel@tonic-gate hubd_free_usba_device(hubd_t *hubd, usba_device_t *usba_device)
59267c478bd9Sstevel@tonic-gate {
59277c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
59287c478bd9Sstevel@tonic-gate 	    "hubd_free_usba_device: hubd=0x%p, usba_device=0x%p",
59297c478bd9Sstevel@tonic-gate 	    hubd, usba_device);
59307c478bd9Sstevel@tonic-gate 
59317c478bd9Sstevel@tonic-gate 	if (usba_device && (usba_device->usb_addr != ROOT_HUB_ADDR)) {
59327c478bd9Sstevel@tonic-gate 		usb_port_t port = usba_device->usb_port;
59337c478bd9Sstevel@tonic-gate 		dev_info_t *dip = hubd->h_children_dips[port];
59347c478bd9Sstevel@tonic-gate 
59357c478bd9Sstevel@tonic-gate #ifdef DEBUG
59367c478bd9Sstevel@tonic-gate 		if (dip) {
59377c478bd9Sstevel@tonic-gate 			ASSERT(i_ddi_node_state(dip) < DS_INITIALIZED);
59387c478bd9Sstevel@tonic-gate 		}
59397c478bd9Sstevel@tonic-gate #endif
59407c478bd9Sstevel@tonic-gate 
59417c478bd9Sstevel@tonic-gate 		port = usba_device->usb_port;
59427c478bd9Sstevel@tonic-gate 		hubd->h_usba_devices[port] = NULL;
59437c478bd9Sstevel@tonic-gate 
59447c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
59457c478bd9Sstevel@tonic-gate 		usba_free_usba_device(usba_device);
59467c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
59477c478bd9Sstevel@tonic-gate 	}
59487c478bd9Sstevel@tonic-gate }
59497c478bd9Sstevel@tonic-gate 
59507c478bd9Sstevel@tonic-gate 
59517c478bd9Sstevel@tonic-gate /*
59527c478bd9Sstevel@tonic-gate  * event support
59537c478bd9Sstevel@tonic-gate  *
59547c478bd9Sstevel@tonic-gate  * busctl event support
59557c478bd9Sstevel@tonic-gate  */
59567c478bd9Sstevel@tonic-gate static int
59577c478bd9Sstevel@tonic-gate hubd_busop_get_eventcookie(dev_info_t *dip,
59587c478bd9Sstevel@tonic-gate 	dev_info_t	*rdip,
59597c478bd9Sstevel@tonic-gate 	char		*eventname,
59607c478bd9Sstevel@tonic-gate 	ddi_eventcookie_t *cookie)
59617c478bd9Sstevel@tonic-gate {
59627c478bd9Sstevel@tonic-gate 	hubd_t	*hubd = (hubd_t *)hubd_get_soft_state(dip);
59637c478bd9Sstevel@tonic-gate 
59647c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
59657c478bd9Sstevel@tonic-gate 	    "hubd_busop_get_eventcookie: dip=0x%p, rdip=0x%p, "
59667c478bd9Sstevel@tonic-gate 	    "event=%s", (void *)dip, (void *)rdip, eventname);
59677c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
59687c478bd9Sstevel@tonic-gate 	    "(dip=%s%d, rdip=%s%d)",
59697c478bd9Sstevel@tonic-gate 	    ddi_driver_name(dip), ddi_get_instance(dip),
59707c478bd9Sstevel@tonic-gate 	    ddi_driver_name(rdip), ddi_get_instance(rdip));
59717c478bd9Sstevel@tonic-gate 
59727c478bd9Sstevel@tonic-gate 	/* return event cookie, iblock cookie, and level */
59737c478bd9Sstevel@tonic-gate 	return (ndi_event_retrieve_cookie(hubd->h_ndi_event_hdl,
59747c478bd9Sstevel@tonic-gate 		rdip, eventname, cookie, NDI_EVENT_NOPASS));
59757c478bd9Sstevel@tonic-gate }
59767c478bd9Sstevel@tonic-gate 
59777c478bd9Sstevel@tonic-gate 
59787c478bd9Sstevel@tonic-gate static int
59797c478bd9Sstevel@tonic-gate hubd_busop_add_eventcall(dev_info_t *dip,
59807c478bd9Sstevel@tonic-gate 	dev_info_t	*rdip,
59817c478bd9Sstevel@tonic-gate 	ddi_eventcookie_t cookie,
59827c478bd9Sstevel@tonic-gate 	void		(*callback)(dev_info_t *dip,
59837c478bd9Sstevel@tonic-gate 			ddi_eventcookie_t cookie, void *arg,
59847c478bd9Sstevel@tonic-gate 			void *bus_impldata),
59857c478bd9Sstevel@tonic-gate 	void *arg, ddi_callback_id_t *cb_id)
59867c478bd9Sstevel@tonic-gate {
59877c478bd9Sstevel@tonic-gate 	hubd_t	*hubd = (hubd_t *)hubd_get_soft_state(dip);
59887c478bd9Sstevel@tonic-gate 	usb_port_t port = hubd_child_dip2port(hubd, rdip);
59897c478bd9Sstevel@tonic-gate 
59907c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
59917c478bd9Sstevel@tonic-gate 	    "hubd_busop_add_eventcall: dip=0x%p, rdip=0x%p "
59927c478bd9Sstevel@tonic-gate 	    "cookie=0x%p, cb=0x%p, arg=0x%p",
59937c478bd9Sstevel@tonic-gate 	    (void *)dip, (void *)rdip, (void *)cookie, (void *)callback, arg);
59947c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
59957c478bd9Sstevel@tonic-gate 	    "(dip=%s%d, rdip=%s%d, event=%s)",
59967c478bd9Sstevel@tonic-gate 	    ddi_driver_name(dip), ddi_get_instance(dip),
59977c478bd9Sstevel@tonic-gate 	    ddi_driver_name(rdip), ddi_get_instance(rdip),
59987c478bd9Sstevel@tonic-gate 	    ndi_event_cookie_to_name(hubd->h_ndi_event_hdl, cookie));
59997c478bd9Sstevel@tonic-gate 
60007c478bd9Sstevel@tonic-gate 	/* Set flag on children registering events */
60017c478bd9Sstevel@tonic-gate 	switch (ndi_event_cookie_to_tag(hubd->h_ndi_event_hdl, cookie)) {
60027c478bd9Sstevel@tonic-gate 	case USBA_EVENT_TAG_HOT_REMOVAL:
60037c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
60047c478bd9Sstevel@tonic-gate 		hubd->h_child_events[port] |= HUBD_CHILD_EVENT_DISCONNECT;
60057c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
60067c478bd9Sstevel@tonic-gate 
60077c478bd9Sstevel@tonic-gate 		break;
60087c478bd9Sstevel@tonic-gate 	case USBA_EVENT_TAG_PRE_SUSPEND:
60097c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
60107c478bd9Sstevel@tonic-gate 		hubd->h_child_events[port] |= HUBD_CHILD_EVENT_PRESUSPEND;
60117c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
60127c478bd9Sstevel@tonic-gate 
60137c478bd9Sstevel@tonic-gate 		break;
60147c478bd9Sstevel@tonic-gate 	default:
60157c478bd9Sstevel@tonic-gate 
60167c478bd9Sstevel@tonic-gate 		break;
60177c478bd9Sstevel@tonic-gate 	}
60187c478bd9Sstevel@tonic-gate 
60197c478bd9Sstevel@tonic-gate 	/* add callback to our event set */
60207c478bd9Sstevel@tonic-gate 	return (ndi_event_add_callback(hubd->h_ndi_event_hdl,
60217c478bd9Sstevel@tonic-gate 		rdip, cookie, callback, arg, NDI_SLEEP, cb_id));
60227c478bd9Sstevel@tonic-gate }
60237c478bd9Sstevel@tonic-gate 
60247c478bd9Sstevel@tonic-gate 
60257c478bd9Sstevel@tonic-gate static int
60267c478bd9Sstevel@tonic-gate hubd_busop_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id)
60277c478bd9Sstevel@tonic-gate {
60287c478bd9Sstevel@tonic-gate 	hubd_t	*hubd = (hubd_t *)hubd_get_soft_state(dip);
60297c478bd9Sstevel@tonic-gate 	ndi_event_callbacks_t *id = (ndi_event_callbacks_t *)cb_id;
60307c478bd9Sstevel@tonic-gate 
60317c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
60327c478bd9Sstevel@tonic-gate 	    "hubd_busop_remove_eventcall: dip=0x%p, rdip=0x%p "
60337c478bd9Sstevel@tonic-gate 	    "cookie=0x%p", (void *)dip, id->ndi_evtcb_dip,
60347c478bd9Sstevel@tonic-gate 	    id->ndi_evtcb_cookie);
60357c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
60367c478bd9Sstevel@tonic-gate 	    "(dip=%s%d, rdip=%s%d, event=%s)",
60377c478bd9Sstevel@tonic-gate 	    ddi_driver_name(dip), ddi_get_instance(dip),
60387c478bd9Sstevel@tonic-gate 	    ddi_driver_name(id->ndi_evtcb_dip),
60397c478bd9Sstevel@tonic-gate 	    ddi_get_instance(id->ndi_evtcb_dip),
60407c478bd9Sstevel@tonic-gate 	    ndi_event_cookie_to_name(hubd->h_ndi_event_hdl,
60417c478bd9Sstevel@tonic-gate 	    id->ndi_evtcb_cookie));
60427c478bd9Sstevel@tonic-gate 
60437c478bd9Sstevel@tonic-gate 	/* remove event registration from our event set */
60447c478bd9Sstevel@tonic-gate 	return (ndi_event_remove_callback(hubd->h_ndi_event_hdl, cb_id));
60457c478bd9Sstevel@tonic-gate }
60467c478bd9Sstevel@tonic-gate 
60477c478bd9Sstevel@tonic-gate 
60487c478bd9Sstevel@tonic-gate /*
60497c478bd9Sstevel@tonic-gate  * event distribution
60507c478bd9Sstevel@tonic-gate  *
60517c478bd9Sstevel@tonic-gate  * hubd_do_callback:
60527c478bd9Sstevel@tonic-gate  *	Post this event to the specified child
60537c478bd9Sstevel@tonic-gate  */
60547c478bd9Sstevel@tonic-gate static void
60557c478bd9Sstevel@tonic-gate hubd_do_callback(hubd_t *hubd, dev_info_t *cdip, ddi_eventcookie_t cookie)
60567c478bd9Sstevel@tonic-gate {
60577c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
60587c478bd9Sstevel@tonic-gate 	    "hubd_do_callback");
60597c478bd9Sstevel@tonic-gate 
60607c478bd9Sstevel@tonic-gate 	(void) ndi_event_do_callback(hubd->h_ndi_event_hdl, cdip, cookie, NULL);
60617c478bd9Sstevel@tonic-gate }
60627c478bd9Sstevel@tonic-gate 
60637c478bd9Sstevel@tonic-gate 
60647c478bd9Sstevel@tonic-gate /*
60657c478bd9Sstevel@tonic-gate  * hubd_run_callbacks:
60667c478bd9Sstevel@tonic-gate  *	Send this event to all children
60677c478bd9Sstevel@tonic-gate  */
60687c478bd9Sstevel@tonic-gate static void
60697c478bd9Sstevel@tonic-gate hubd_run_callbacks(hubd_t *hubd, usba_event_t type)
60707c478bd9Sstevel@tonic-gate {
60717c478bd9Sstevel@tonic-gate 	usb_port_t	port;
60727c478bd9Sstevel@tonic-gate 
60737c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
60747c478bd9Sstevel@tonic-gate 	    "hubd_run_callbacks");
60757c478bd9Sstevel@tonic-gate 
60767c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
60777c478bd9Sstevel@tonic-gate 	for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
60787c478bd9Sstevel@tonic-gate 		/*
60797c478bd9Sstevel@tonic-gate 		 * the childen_dips list may have dips that have been
60807c478bd9Sstevel@tonic-gate 		 * already deallocated. we only get a post_detach notification
60817c478bd9Sstevel@tonic-gate 		 * but not a destroy notification
60827c478bd9Sstevel@tonic-gate 		 */
60837c478bd9Sstevel@tonic-gate 		if (hubd->h_children_dips[port]) {
60847c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
60857c478bd9Sstevel@tonic-gate 			hubd_post_event(hubd, port, type);
60867c478bd9Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
60877c478bd9Sstevel@tonic-gate 		}
60887c478bd9Sstevel@tonic-gate 	}
60897c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
60907c478bd9Sstevel@tonic-gate }
60917c478bd9Sstevel@tonic-gate 
60927c478bd9Sstevel@tonic-gate 
60937c478bd9Sstevel@tonic-gate /*
60947c478bd9Sstevel@tonic-gate  * hubd_post_event
60957c478bd9Sstevel@tonic-gate  *	post event to a child on the port depending on the type
60967c478bd9Sstevel@tonic-gate  */
60977c478bd9Sstevel@tonic-gate static void
60987c478bd9Sstevel@tonic-gate hubd_post_event(hubd_t *hubd, usb_port_t port, usba_event_t type)
60997c478bd9Sstevel@tonic-gate {
61007c478bd9Sstevel@tonic-gate 	int	rval;
61017c478bd9Sstevel@tonic-gate 	dev_info_t	*dip;
61027c478bd9Sstevel@tonic-gate 	usba_device_t	*usba_device;
61037c478bd9Sstevel@tonic-gate 	ddi_eventcookie_t cookie, rm_cookie, suspend_cookie;
61047c478bd9Sstevel@tonic-gate 
61057c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
61067c478bd9Sstevel@tonic-gate 	    "hubd_post_event: port=%d event=%s", port,
61077c478bd9Sstevel@tonic-gate 	    ndi_event_tag_to_name(hubd->h_ndi_event_hdl, type));
61087c478bd9Sstevel@tonic-gate 
61097c478bd9Sstevel@tonic-gate 	cookie = ndi_event_tag_to_cookie(hubd->h_ndi_event_hdl, type);
61107c478bd9Sstevel@tonic-gate 	rm_cookie = ndi_event_tag_to_cookie(hubd->h_ndi_event_hdl,
61117c478bd9Sstevel@tonic-gate 	    USBA_EVENT_TAG_HOT_REMOVAL);
61127c478bd9Sstevel@tonic-gate 	suspend_cookie = ndi_event_tag_to_cookie(hubd->h_ndi_event_hdl,
61137c478bd9Sstevel@tonic-gate 	    USBA_EVENT_TAG_PRE_SUSPEND);
61147c478bd9Sstevel@tonic-gate 
61157c478bd9Sstevel@tonic-gate 	/*
61167c478bd9Sstevel@tonic-gate 	 * Hotplug daemon may be attaching a driver that may be registering
61177c478bd9Sstevel@tonic-gate 	 * event callbacks. So it already has got the device tree lock and
61187c478bd9Sstevel@tonic-gate 	 * event handle mutex. So to prevent a deadlock while posting events,
61197c478bd9Sstevel@tonic-gate 	 * we grab and release the locks in the same order.
61207c478bd9Sstevel@tonic-gate 	 */
61217c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
61227c478bd9Sstevel@tonic-gate 	dip = hubd->h_children_dips[port];
61237c478bd9Sstevel@tonic-gate 	usba_device = hubd->h_usba_devices[port];
61247c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
61257c478bd9Sstevel@tonic-gate 
61267c478bd9Sstevel@tonic-gate 	switch (type) {
61277c478bd9Sstevel@tonic-gate 	case USBA_EVENT_TAG_HOT_REMOVAL:
61287c478bd9Sstevel@tonic-gate 		/* Clear the registered event flag */
61297c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
61307c478bd9Sstevel@tonic-gate 		hubd->h_child_events[port] &= ~HUBD_CHILD_EVENT_DISCONNECT;
61317c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
61327c478bd9Sstevel@tonic-gate 
61337c478bd9Sstevel@tonic-gate 		hubd_do_callback(hubd, dip, cookie);
61347c478bd9Sstevel@tonic-gate 		usba_persistent_pipe_close(usba_device);
61357c478bd9Sstevel@tonic-gate 
61367c478bd9Sstevel@tonic-gate 		/*
61377c478bd9Sstevel@tonic-gate 		 * Mark the dip for deletion only after the driver has
61387c478bd9Sstevel@tonic-gate 		 * seen the disconnect event to prevent cleanup thread
61397c478bd9Sstevel@tonic-gate 		 * from stepping in between.
61407c478bd9Sstevel@tonic-gate 		 */
614116747f41Scth 		mutex_enter(&(DEVI(dip)->devi_lock));
61427c478bd9Sstevel@tonic-gate 		DEVI_SET_DEVICE_REMOVED(dip);
614316747f41Scth 		mutex_exit(&(DEVI(dip)->devi_lock));
61447c478bd9Sstevel@tonic-gate 
61457c478bd9Sstevel@tonic-gate 		break;
61467c478bd9Sstevel@tonic-gate 	case USBA_EVENT_TAG_PRE_SUSPEND:
61477c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
61487c478bd9Sstevel@tonic-gate 		hubd->h_child_events[port] &= ~HUBD_CHILD_EVENT_PRESUSPEND;
61497c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
61507c478bd9Sstevel@tonic-gate 
61517c478bd9Sstevel@tonic-gate 		hubd_do_callback(hubd, dip, cookie);
61527c478bd9Sstevel@tonic-gate 		/*
61537c478bd9Sstevel@tonic-gate 		 * persistent pipe close for this event is taken care by the
61547c478bd9Sstevel@tonic-gate 		 * caller after verfying that all children can suspend
61557c478bd9Sstevel@tonic-gate 		 */
61567c478bd9Sstevel@tonic-gate 
61577c478bd9Sstevel@tonic-gate 		break;
61587c478bd9Sstevel@tonic-gate 	case USBA_EVENT_TAG_HOT_INSERTION:
61597c478bd9Sstevel@tonic-gate 		/*
61607c478bd9Sstevel@tonic-gate 		 * Check if this child has missed the disconnect event before
61617c478bd9Sstevel@tonic-gate 		 * it registered for event callbacks
61627c478bd9Sstevel@tonic-gate 		 */
61637c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
61647c478bd9Sstevel@tonic-gate 		if (hubd->h_child_events[port] & HUBD_CHILD_EVENT_DISCONNECT) {
61657c478bd9Sstevel@tonic-gate 			/* clear the flag and post disconnect event */
61667c478bd9Sstevel@tonic-gate 			hubd->h_child_events[port] &=
61677c478bd9Sstevel@tonic-gate 			    ~HUBD_CHILD_EVENT_DISCONNECT;
61687c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
61697c478bd9Sstevel@tonic-gate 			hubd_do_callback(hubd, dip, rm_cookie);
61707c478bd9Sstevel@tonic-gate 			usba_persistent_pipe_close(usba_device);
61717c478bd9Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
61727c478bd9Sstevel@tonic-gate 		}
61737c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
61747c478bd9Sstevel@tonic-gate 
61757c478bd9Sstevel@tonic-gate 		/*
61767c478bd9Sstevel@tonic-gate 		 * Mark the dip as reinserted to prevent cleanup thread
61777c478bd9Sstevel@tonic-gate 		 * from stepping in.
61787c478bd9Sstevel@tonic-gate 		 */
617916747f41Scth 		mutex_enter(&(DEVI(dip)->devi_lock));
61807c478bd9Sstevel@tonic-gate 		DEVI_SET_DEVICE_REINSERTED(dip);
618116747f41Scth 		mutex_exit(&(DEVI(dip)->devi_lock));
61827c478bd9Sstevel@tonic-gate 
61837c478bd9Sstevel@tonic-gate 		rval = usba_persistent_pipe_open(usba_device);
61847c478bd9Sstevel@tonic-gate 		if (rval != USB_SUCCESS) {
6185*d291d9f2Sfrits 			USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
61867c478bd9Sstevel@tonic-gate 			    hubd->h_log_handle,
61877c478bd9Sstevel@tonic-gate 			    "failed to reopen all pipes on reconnect");
61887c478bd9Sstevel@tonic-gate 		}
61897c478bd9Sstevel@tonic-gate 
61907c478bd9Sstevel@tonic-gate 		hubd_do_callback(hubd, dip, cookie);
61917c478bd9Sstevel@tonic-gate 
61927c478bd9Sstevel@tonic-gate 		/*
61937c478bd9Sstevel@tonic-gate 		 * We might see a connect event only if hotplug thread for
61947c478bd9Sstevel@tonic-gate 		 * disconnect event don't run in time.
61957c478bd9Sstevel@tonic-gate 		 * Set the flag again, so we don't miss posting a
61967c478bd9Sstevel@tonic-gate 		 * disconnect event.
61977c478bd9Sstevel@tonic-gate 		 */
61987c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
61997c478bd9Sstevel@tonic-gate 		hubd->h_child_events[port] |= HUBD_CHILD_EVENT_DISCONNECT;
62007c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
62017c478bd9Sstevel@tonic-gate 
62027c478bd9Sstevel@tonic-gate 		break;
62037c478bd9Sstevel@tonic-gate 	case USBA_EVENT_TAG_POST_RESUME:
62047c478bd9Sstevel@tonic-gate 		/*
62057c478bd9Sstevel@tonic-gate 		 * Check if this child has missed the pre-suspend event before
62067c478bd9Sstevel@tonic-gate 		 * it registered for event callbacks
62077c478bd9Sstevel@tonic-gate 		 */
62087c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
62097c478bd9Sstevel@tonic-gate 		if (hubd->h_child_events[port] & HUBD_CHILD_EVENT_PRESUSPEND) {
62107c478bd9Sstevel@tonic-gate 			/* clear the flag and post pre_suspend event */
62117c478bd9Sstevel@tonic-gate 			hubd->h_port_state[port] &=
62127c478bd9Sstevel@tonic-gate 			    ~HUBD_CHILD_EVENT_PRESUSPEND;
62137c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
62147c478bd9Sstevel@tonic-gate 			hubd_do_callback(hubd, dip, suspend_cookie);
62157c478bd9Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
62167c478bd9Sstevel@tonic-gate 		}
62177c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
62187c478bd9Sstevel@tonic-gate 
62197c478bd9Sstevel@tonic-gate 		mutex_enter(&usba_device->usb_mutex);
62207c478bd9Sstevel@tonic-gate 		usba_device->usb_no_cpr = 0;
62217c478bd9Sstevel@tonic-gate 		mutex_exit(&usba_device->usb_mutex);
62227c478bd9Sstevel@tonic-gate 
62237c478bd9Sstevel@tonic-gate 		/*
62247c478bd9Sstevel@tonic-gate 		 * Since the pipe has already been opened by hub
62257c478bd9Sstevel@tonic-gate 		 * at DDI_RESUME time, there is no need for a
62267c478bd9Sstevel@tonic-gate 		 * persistent pipe open
62277c478bd9Sstevel@tonic-gate 		 */
62287c478bd9Sstevel@tonic-gate 		hubd_do_callback(hubd, dip, cookie);
62297c478bd9Sstevel@tonic-gate 
62307c478bd9Sstevel@tonic-gate 		/*
62317c478bd9Sstevel@tonic-gate 		 * Set the flag again, so we don't miss posting a
62327c478bd9Sstevel@tonic-gate 		 * pre-suspend event. This enforces a tighter
62337c478bd9Sstevel@tonic-gate 		 * dev_state model.
62347c478bd9Sstevel@tonic-gate 		 */
62357c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
62367c478bd9Sstevel@tonic-gate 		hubd->h_child_events[port] |= HUBD_CHILD_EVENT_PRESUSPEND;
62377c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
62387c478bd9Sstevel@tonic-gate 		break;
62397c478bd9Sstevel@tonic-gate 	}
62407c478bd9Sstevel@tonic-gate }
62417c478bd9Sstevel@tonic-gate 
62427c478bd9Sstevel@tonic-gate 
62437c478bd9Sstevel@tonic-gate /*
62447c478bd9Sstevel@tonic-gate  * handling of events coming from above
62457c478bd9Sstevel@tonic-gate  */
62467c478bd9Sstevel@tonic-gate static int
62477c478bd9Sstevel@tonic-gate hubd_disconnect_event_cb(dev_info_t *dip)
62487c478bd9Sstevel@tonic-gate {
62497c478bd9Sstevel@tonic-gate 	hubd_t		*hubd = (hubd_t *)hubd_get_soft_state(dip);
62507c478bd9Sstevel@tonic-gate 	usb_port_t	port, nports;
62517c478bd9Sstevel@tonic-gate 	usba_device_t	*usba_dev;
62527c478bd9Sstevel@tonic-gate 	usba_event_t	tag = USBA_EVENT_TAG_HOT_REMOVAL;
62537c478bd9Sstevel@tonic-gate 	int		circ;
62547c478bd9Sstevel@tonic-gate 
62557c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
62567c478bd9Sstevel@tonic-gate 	    "hubd_disconnect_event_cb: tag=%d", tag);
62577c478bd9Sstevel@tonic-gate 
62587c478bd9Sstevel@tonic-gate 	ndi_devi_enter(dip, &circ);
62597c478bd9Sstevel@tonic-gate 
62607c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
62617c478bd9Sstevel@tonic-gate 	switch (hubd->h_dev_state) {
62627c478bd9Sstevel@tonic-gate 	case USB_DEV_ONLINE:
62637c478bd9Sstevel@tonic-gate 	case USB_DEV_PWRED_DOWN:
62647c478bd9Sstevel@tonic-gate 		hubd->h_dev_state = USB_DEV_DISCONNECTED;
62657c478bd9Sstevel@tonic-gate 		/* stop polling on the interrupt pipe */
62667c478bd9Sstevel@tonic-gate 		hubd_stop_polling(hubd);
62677c478bd9Sstevel@tonic-gate 
62687c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
62697c478bd9Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
62707c478bd9Sstevel@tonic-gate 		/* we remain in this state */
62717c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
62727c478bd9Sstevel@tonic-gate 		hubd_run_callbacks(hubd, tag);
62737c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
62747c478bd9Sstevel@tonic-gate 
62757c478bd9Sstevel@tonic-gate 		/* close all the open pipes of our children */
62767c478bd9Sstevel@tonic-gate 		nports = hubd->h_hub_descr.bNbrPorts;
62777c478bd9Sstevel@tonic-gate 		for (port = 1; port <= nports; port++) {
62787c478bd9Sstevel@tonic-gate 			usba_dev = hubd->h_usba_devices[port];
62797c478bd9Sstevel@tonic-gate 			if (usba_dev != NULL) {
62807c478bd9Sstevel@tonic-gate 				mutex_exit(HUBD_MUTEX(hubd));
62817c478bd9Sstevel@tonic-gate 				usba_persistent_pipe_close(usba_dev);
62827c478bd9Sstevel@tonic-gate 				mutex_enter(HUBD_MUTEX(hubd));
62837c478bd9Sstevel@tonic-gate 			}
62847c478bd9Sstevel@tonic-gate 		}
62857c478bd9Sstevel@tonic-gate 
62867c478bd9Sstevel@tonic-gate 		break;
62877c478bd9Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
62887c478bd9Sstevel@tonic-gate 		/* avoid passing multiple disconnects to children */
62897c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
62907c478bd9Sstevel@tonic-gate 		    "hubd_disconnect_event_cb: Already disconnected");
62917c478bd9Sstevel@tonic-gate 
62927c478bd9Sstevel@tonic-gate 		break;
62937c478bd9Sstevel@tonic-gate 	default:
62947c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
62957c478bd9Sstevel@tonic-gate 		    "hubd_disconnect_event_cb: Illegal devstate=%d",
62967c478bd9Sstevel@tonic-gate 		    hubd->h_dev_state);
62977c478bd9Sstevel@tonic-gate 
62987c478bd9Sstevel@tonic-gate 		break;
62997c478bd9Sstevel@tonic-gate 	}
63007c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
63017c478bd9Sstevel@tonic-gate 
63027c478bd9Sstevel@tonic-gate 	ndi_devi_exit(dip, circ);
63037c478bd9Sstevel@tonic-gate 
63047c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
63057c478bd9Sstevel@tonic-gate }
63067c478bd9Sstevel@tonic-gate 
63077c478bd9Sstevel@tonic-gate 
63087c478bd9Sstevel@tonic-gate static int
63097c478bd9Sstevel@tonic-gate hubd_reconnect_event_cb(dev_info_t *dip)
63107c478bd9Sstevel@tonic-gate {
63117c478bd9Sstevel@tonic-gate 	int	rval, circ;
63127c478bd9Sstevel@tonic-gate 
63137c478bd9Sstevel@tonic-gate 	ndi_devi_enter(dip, &circ);
63147c478bd9Sstevel@tonic-gate 	rval = hubd_restore_state_cb(dip);
63157c478bd9Sstevel@tonic-gate 	ndi_devi_exit(dip, circ);
63167c478bd9Sstevel@tonic-gate 
63177c478bd9Sstevel@tonic-gate 	return (rval);
63187c478bd9Sstevel@tonic-gate }
63197c478bd9Sstevel@tonic-gate 
63207c478bd9Sstevel@tonic-gate 
63217c478bd9Sstevel@tonic-gate /*
63227c478bd9Sstevel@tonic-gate  * hubd_pre_suspend_event_cb
63237c478bd9Sstevel@tonic-gate  *	propogate event for binary compatibility of old drivers
63247c478bd9Sstevel@tonic-gate  */
63257c478bd9Sstevel@tonic-gate static int
63267c478bd9Sstevel@tonic-gate hubd_pre_suspend_event_cb(dev_info_t *dip)
63277c478bd9Sstevel@tonic-gate {
63287c478bd9Sstevel@tonic-gate 	int	circ;
63297c478bd9Sstevel@tonic-gate 	hubd_t	*hubd = (hubd_t *)hubd_get_soft_state(dip);
63307c478bd9Sstevel@tonic-gate 
63317c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle,
63327c478bd9Sstevel@tonic-gate 	    "hubd_pre_suspend_event_cb");
63337c478bd9Sstevel@tonic-gate 
63347c478bd9Sstevel@tonic-gate 	/* disable hotplug thread */
63357c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
63367c478bd9Sstevel@tonic-gate 	hubd->h_hotplug_thread++;
63377c478bd9Sstevel@tonic-gate 	hubd_stop_polling(hubd);
63387c478bd9Sstevel@tonic-gate 
63397c478bd9Sstevel@tonic-gate 	/* keep PM out till we see a cpr resume */
63407c478bd9Sstevel@tonic-gate 	(void) hubd_pm_busy_component(hubd, hubd->h_dip, 0);
63417c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
63427c478bd9Sstevel@tonic-gate 
63437c478bd9Sstevel@tonic-gate 	ndi_devi_enter(dip, &circ);
63447c478bd9Sstevel@tonic-gate 	hubd_run_callbacks(hubd, USBA_EVENT_TAG_PRE_SUSPEND);
63457c478bd9Sstevel@tonic-gate 	ndi_devi_exit(dip, circ);
63467c478bd9Sstevel@tonic-gate 
63477c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
63487c478bd9Sstevel@tonic-gate }
63497c478bd9Sstevel@tonic-gate 
63507c478bd9Sstevel@tonic-gate 
63517c478bd9Sstevel@tonic-gate /*
63527c478bd9Sstevel@tonic-gate  * hubd_post_resume_event_cb
63537c478bd9Sstevel@tonic-gate  *	propogate event for binary compatibility of old drivers
63547c478bd9Sstevel@tonic-gate  */
63557c478bd9Sstevel@tonic-gate static int
63567c478bd9Sstevel@tonic-gate hubd_post_resume_event_cb(dev_info_t *dip)
63577c478bd9Sstevel@tonic-gate {
63587c478bd9Sstevel@tonic-gate 	int	circ;
63597c478bd9Sstevel@tonic-gate 	hubd_t	*hubd = (hubd_t *)hubd_get_soft_state(dip);
63607c478bd9Sstevel@tonic-gate 
63617c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle,
63627c478bd9Sstevel@tonic-gate 	    "hubd_post_resume_event_cb");
63637c478bd9Sstevel@tonic-gate 
63647c478bd9Sstevel@tonic-gate 	ndi_devi_enter(dip, &circ);
63657c478bd9Sstevel@tonic-gate 	hubd_run_callbacks(hubd, USBA_EVENT_TAG_POST_RESUME);
63667c478bd9Sstevel@tonic-gate 	ndi_devi_exit(dip, circ);
63677c478bd9Sstevel@tonic-gate 
63687c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
63697c478bd9Sstevel@tonic-gate 
63707c478bd9Sstevel@tonic-gate 	/* enable PM */
63717c478bd9Sstevel@tonic-gate 	(void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
63727c478bd9Sstevel@tonic-gate 
63737c478bd9Sstevel@tonic-gate 	/* allow hotplug thread */
63747c478bd9Sstevel@tonic-gate 	hubd->h_hotplug_thread--;
63757c478bd9Sstevel@tonic-gate 
63767c478bd9Sstevel@tonic-gate 	/* start polling */
63777c478bd9Sstevel@tonic-gate 	hubd_start_polling(hubd, 0);
63787c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
63797c478bd9Sstevel@tonic-gate 
63807c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
63817c478bd9Sstevel@tonic-gate }
63827c478bd9Sstevel@tonic-gate 
63837c478bd9Sstevel@tonic-gate 
63847c478bd9Sstevel@tonic-gate /*
63857c478bd9Sstevel@tonic-gate  * hubd_cpr_suspend
63867c478bd9Sstevel@tonic-gate  *	save the current state of the driver/device
63877c478bd9Sstevel@tonic-gate  */
63887c478bd9Sstevel@tonic-gate static int
63897c478bd9Sstevel@tonic-gate hubd_cpr_suspend(hubd_t *hubd)
63907c478bd9Sstevel@tonic-gate {
63917c478bd9Sstevel@tonic-gate 	usb_port_t	port, nports;
63927c478bd9Sstevel@tonic-gate 	usba_device_t	*usba_dev;
63937c478bd9Sstevel@tonic-gate 	uchar_t		no_cpr = 0;
63947c478bd9Sstevel@tonic-gate 	int		rval = USB_FAILURE;
63957c478bd9Sstevel@tonic-gate 
63967c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
63977c478bd9Sstevel@tonic-gate 	    "hubd_cpr_suspend: Begin");
63987c478bd9Sstevel@tonic-gate 
63997c478bd9Sstevel@tonic-gate 	/* Make sure device is powered up to save state. */
64007c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
64017c478bd9Sstevel@tonic-gate 	hubd_pm_busy_component(hubd, hubd->h_dip, 0);
64027c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
64037c478bd9Sstevel@tonic-gate 
64047c478bd9Sstevel@tonic-gate 	/* bring the device to full power */
64057c478bd9Sstevel@tonic-gate 	(void) pm_raise_power(hubd->h_dip, 0, USB_DEV_OS_FULL_PWR);
64067c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
64077c478bd9Sstevel@tonic-gate 
64087c478bd9Sstevel@tonic-gate 	switch (hubd->h_dev_state) {
64097c478bd9Sstevel@tonic-gate 	case USB_DEV_ONLINE:
64107c478bd9Sstevel@tonic-gate 	case USB_DEV_PWRED_DOWN:
64117c478bd9Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
64127c478bd9Sstevel@tonic-gate 		/* find out if all our children have been quiesced */
64137c478bd9Sstevel@tonic-gate 		nports = hubd->h_hub_descr.bNbrPorts;
64147c478bd9Sstevel@tonic-gate 		for (port = 1; (no_cpr == 0) && (port <= nports); port++) {
64157c478bd9Sstevel@tonic-gate 			usba_dev = hubd->h_usba_devices[port];
64167c478bd9Sstevel@tonic-gate 			if (usba_dev != NULL) {
64177c478bd9Sstevel@tonic-gate 				mutex_enter(&usba_dev->usb_mutex);
64187c478bd9Sstevel@tonic-gate 				no_cpr += usba_dev->usb_no_cpr;
64197c478bd9Sstevel@tonic-gate 				mutex_exit(&usba_dev->usb_mutex);
64207c478bd9Sstevel@tonic-gate 			}
64217c478bd9Sstevel@tonic-gate 		}
64227c478bd9Sstevel@tonic-gate 		if (no_cpr > 0) {
64237c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
64247c478bd9Sstevel@tonic-gate 			    "Children busy - can't checkpoint");
64257c478bd9Sstevel@tonic-gate 			/* remain in same state to fail checkpoint */
64267c478bd9Sstevel@tonic-gate 
64277c478bd9Sstevel@tonic-gate 			break;
64287c478bd9Sstevel@tonic-gate 		} else {
64297c478bd9Sstevel@tonic-gate 			/*
64307c478bd9Sstevel@tonic-gate 			 * do not suspend if our hotplug thread
64317c478bd9Sstevel@tonic-gate 			 * or the deathrow thread is active
64327c478bd9Sstevel@tonic-gate 			 */
64337c478bd9Sstevel@tonic-gate 			if ((hubd->h_hotplug_thread > 1) ||
64347c478bd9Sstevel@tonic-gate 			    (hubd->h_cleanup_active == B_TRUE)) {
64357c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
64367c478bd9Sstevel@tonic-gate 				    hubd->h_log_handle,
64377c478bd9Sstevel@tonic-gate 				    "hotplug thread active  - can't cpr");
64387c478bd9Sstevel@tonic-gate 				/* remain in same state to fail checkpoint */
64397c478bd9Sstevel@tonic-gate 
64407c478bd9Sstevel@tonic-gate 				break;
64417c478bd9Sstevel@tonic-gate 			}
64427c478bd9Sstevel@tonic-gate 
64437c478bd9Sstevel@tonic-gate 			/* quiesce ourselves now */
64447c478bd9Sstevel@tonic-gate 			hubd->h_dev_state = USB_DEV_SUSPENDED;
64457c478bd9Sstevel@tonic-gate 			hubd_stop_polling(hubd);
64467c478bd9Sstevel@tonic-gate 
64477c478bd9Sstevel@tonic-gate 			/* close all the open pipes of our children */
64487c478bd9Sstevel@tonic-gate 			for (port = 1; port <= nports; port++) {
64497c478bd9Sstevel@tonic-gate 				usba_dev = hubd->h_usba_devices[port];
64507c478bd9Sstevel@tonic-gate 				if (usba_dev != NULL) {
64517c478bd9Sstevel@tonic-gate 					mutex_exit(HUBD_MUTEX(hubd));
64527c478bd9Sstevel@tonic-gate 					usba_persistent_pipe_close(usba_dev);
64537c478bd9Sstevel@tonic-gate 					mutex_enter(HUBD_MUTEX(hubd));
64547c478bd9Sstevel@tonic-gate 				}
64557c478bd9Sstevel@tonic-gate 			}
64567c478bd9Sstevel@tonic-gate 			/*
64577c478bd9Sstevel@tonic-gate 			 * turn off power to all the ports so that we
64587c478bd9Sstevel@tonic-gate 			 * don't see any spurious activity
64597c478bd9Sstevel@tonic-gate 			 */
64607c478bd9Sstevel@tonic-gate 			(void) hubd_disable_all_port_power(hubd);
64617c478bd9Sstevel@tonic-gate 
64627c478bd9Sstevel@tonic-gate 			/*
64637c478bd9Sstevel@tonic-gate 			 * if we are the root hub, we close our pipes
64647c478bd9Sstevel@tonic-gate 			 * ourselves.
64657c478bd9Sstevel@tonic-gate 			 */
64667c478bd9Sstevel@tonic-gate 			if (usba_is_root_hub(hubd->h_dip)) {
64677c478bd9Sstevel@tonic-gate 				mutex_exit(HUBD_MUTEX(hubd));
64687c478bd9Sstevel@tonic-gate 				usba_persistent_pipe_close(
64697c478bd9Sstevel@tonic-gate 				    usba_get_usba_device(hubd->h_dip));
64707c478bd9Sstevel@tonic-gate 				mutex_enter(HUBD_MUTEX(hubd));
64717c478bd9Sstevel@tonic-gate 			}
64727c478bd9Sstevel@tonic-gate 			rval = USB_SUCCESS;
64737c478bd9Sstevel@tonic-gate 
64747c478bd9Sstevel@tonic-gate 			break;
64757c478bd9Sstevel@tonic-gate 		}
64767c478bd9Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
64777c478bd9Sstevel@tonic-gate 	default:
64787c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
64797c478bd9Sstevel@tonic-gate 		    "hubd_cpr_suspend: Illegal dev state=%d",
64807c478bd9Sstevel@tonic-gate 		    hubd->h_dev_state);
64817c478bd9Sstevel@tonic-gate 
64827c478bd9Sstevel@tonic-gate 		break;
64837c478bd9Sstevel@tonic-gate 	}
64847c478bd9Sstevel@tonic-gate 
64857c478bd9Sstevel@tonic-gate 	hubd_pm_idle_component(hubd, hubd->h_dip, 0);
64867c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
64877c478bd9Sstevel@tonic-gate 
64887c478bd9Sstevel@tonic-gate 	return (rval);
64897c478bd9Sstevel@tonic-gate }
64907c478bd9Sstevel@tonic-gate 
64917c478bd9Sstevel@tonic-gate static void
64927c478bd9Sstevel@tonic-gate hubd_cpr_resume(dev_info_t *dip)
64937c478bd9Sstevel@tonic-gate {
64947c478bd9Sstevel@tonic-gate 	int	rval, circ;
64957c478bd9Sstevel@tonic-gate 
64967c478bd9Sstevel@tonic-gate 	ndi_devi_enter(dip, &circ);
64977c478bd9Sstevel@tonic-gate 	/*
64987c478bd9Sstevel@tonic-gate 	 * if we are the root hub, we open our pipes
64997c478bd9Sstevel@tonic-gate 	 * ourselves.
65007c478bd9Sstevel@tonic-gate 	 */
65017c478bd9Sstevel@tonic-gate 	if (usba_is_root_hub(dip)) {
65027c478bd9Sstevel@tonic-gate 		rval = usba_persistent_pipe_open(
65037c478bd9Sstevel@tonic-gate 		    usba_get_usba_device(dip));
65047c478bd9Sstevel@tonic-gate 		ASSERT(rval == USB_SUCCESS);
65057c478bd9Sstevel@tonic-gate 	}
65067c478bd9Sstevel@tonic-gate 	(void) hubd_restore_state_cb(dip);
65077c478bd9Sstevel@tonic-gate 	ndi_devi_exit(dip, circ);
65087c478bd9Sstevel@tonic-gate }
65097c478bd9Sstevel@tonic-gate 
65107c478bd9Sstevel@tonic-gate 
65117c478bd9Sstevel@tonic-gate /*
65127c478bd9Sstevel@tonic-gate  * hubd_restore_state_cb
65137c478bd9Sstevel@tonic-gate  *	Event callback to restore device state
65147c478bd9Sstevel@tonic-gate  */
65157c478bd9Sstevel@tonic-gate static int
65167c478bd9Sstevel@tonic-gate hubd_restore_state_cb(dev_info_t *dip)
65177c478bd9Sstevel@tonic-gate {
65187c478bd9Sstevel@tonic-gate 	hubd_t	*hubd = (hubd_t *)hubd_get_soft_state(dip);
65197c478bd9Sstevel@tonic-gate 
65207c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
65217c478bd9Sstevel@tonic-gate 	    "hubd_restore_state_cb: Begin");
65227c478bd9Sstevel@tonic-gate 
65237c478bd9Sstevel@tonic-gate 	/* restore the state of this device */
65247c478bd9Sstevel@tonic-gate 	hubd_restore_device_state(dip, hubd);
65257c478bd9Sstevel@tonic-gate 
65267c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
65277c478bd9Sstevel@tonic-gate }
65287c478bd9Sstevel@tonic-gate 
65297c478bd9Sstevel@tonic-gate 
65307c478bd9Sstevel@tonic-gate /*
65317c478bd9Sstevel@tonic-gate  * registering for events
65327c478bd9Sstevel@tonic-gate  */
65337c478bd9Sstevel@tonic-gate static int
65347c478bd9Sstevel@tonic-gate hubd_register_events(hubd_t *hubd)
65357c478bd9Sstevel@tonic-gate {
65367c478bd9Sstevel@tonic-gate 	int		rval = USB_SUCCESS;
65377c478bd9Sstevel@tonic-gate 
65387c478bd9Sstevel@tonic-gate 	if (usba_is_root_hub(hubd->h_dip)) {
65397c478bd9Sstevel@tonic-gate 		hubd_register_cpr_callback(hubd);
65407c478bd9Sstevel@tonic-gate 	} else {
65417c478bd9Sstevel@tonic-gate 		rval = usb_register_event_cbs(hubd->h_dip, &hubd_events, 0);
65427c478bd9Sstevel@tonic-gate 	}
65437c478bd9Sstevel@tonic-gate 
65447c478bd9Sstevel@tonic-gate 	return (rval);
65457c478bd9Sstevel@tonic-gate }
65467c478bd9Sstevel@tonic-gate 
65477c478bd9Sstevel@tonic-gate 
65487c478bd9Sstevel@tonic-gate /*
65497c478bd9Sstevel@tonic-gate  * hubd cpr callback related functions
65507c478bd9Sstevel@tonic-gate  *
65517c478bd9Sstevel@tonic-gate  * hubd_cpr_post_user_callb:
65527c478bd9Sstevel@tonic-gate  *	This function is called during checkpoint & resume -
65537c478bd9Sstevel@tonic-gate  *		1. after user threads are stopped during checkpoint
65547c478bd9Sstevel@tonic-gate  *		2. after kernel threads are resumed during resume
65557c478bd9Sstevel@tonic-gate  */
65567c478bd9Sstevel@tonic-gate /* ARGSUSED */
65577c478bd9Sstevel@tonic-gate static boolean_t
65587c478bd9Sstevel@tonic-gate hubd_cpr_post_user_callb(void *arg, int code)
65597c478bd9Sstevel@tonic-gate {
65607c478bd9Sstevel@tonic-gate 	hubd_cpr_t	*cpr_cb = (hubd_cpr_t *)arg;
65617c478bd9Sstevel@tonic-gate 	hubd_t		*hubd = cpr_cb->statep;
65627c478bd9Sstevel@tonic-gate 	int		retry = 0;
65637c478bd9Sstevel@tonic-gate 
65647c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle,
65657c478bd9Sstevel@tonic-gate 	    "hubd_cpr_post_user_callb");
65667c478bd9Sstevel@tonic-gate 
65677c478bd9Sstevel@tonic-gate 	switch (code) {
65687c478bd9Sstevel@tonic-gate 	case CB_CODE_CPR_CHKPT:
65697c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_EVENTS, hubd->h_log_handle,
65707c478bd9Sstevel@tonic-gate 		    "hubd_cpr_post_user_callb: CB_CODE_CPR_CHKPT");
65717c478bd9Sstevel@tonic-gate 
65727c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
65737c478bd9Sstevel@tonic-gate 
65747c478bd9Sstevel@tonic-gate 		/* turn off deathrow thread */
65757c478bd9Sstevel@tonic-gate 		hubd->h_cleanup_enabled = B_FALSE;
65767c478bd9Sstevel@tonic-gate 
65777c478bd9Sstevel@tonic-gate 		/* give up if deathrow thread doesn't exit */
65787c478bd9Sstevel@tonic-gate 		while ((hubd->h_cleanup_active == B_TRUE) && (retry++ < 3)) {
65797c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
65807c478bd9Sstevel@tonic-gate 			delay(drv_usectohz(hubd_dip_cleanup_delay));
65817c478bd9Sstevel@tonic-gate 
65827c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_EVENTS, hubd->h_log_handle,
65837c478bd9Sstevel@tonic-gate 			    "hubd_cpr_post_user_callb, waiting for "
65847c478bd9Sstevel@tonic-gate 			    "deathrow thread to exit");
65857c478bd9Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
65867c478bd9Sstevel@tonic-gate 		}
65877c478bd9Sstevel@tonic-gate 
65887c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
65897c478bd9Sstevel@tonic-gate 
65907c478bd9Sstevel@tonic-gate 		/* save the state of the device */
65917c478bd9Sstevel@tonic-gate 		(void) hubd_pre_suspend_event_cb(hubd->h_dip);
65927c478bd9Sstevel@tonic-gate 
65937c478bd9Sstevel@tonic-gate 		return (B_TRUE);
65947c478bd9Sstevel@tonic-gate 	case CB_CODE_CPR_RESUME:
65957c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_EVENTS, hubd->h_log_handle,
65967c478bd9Sstevel@tonic-gate 		    "hubd_cpr_post_user_callb: CB_CODE_CPR_RESUME");
65977c478bd9Sstevel@tonic-gate 
65987c478bd9Sstevel@tonic-gate 		/* restore the state of the device */
65997c478bd9Sstevel@tonic-gate 		(void) hubd_post_resume_event_cb(hubd->h_dip);
66007c478bd9Sstevel@tonic-gate 
66017c478bd9Sstevel@tonic-gate 		/* turn on deathrow thread */
66027c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
66037c478bd9Sstevel@tonic-gate 		hubd->h_cleanup_enabled = B_TRUE;
66047c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
66057c478bd9Sstevel@tonic-gate 
66067c478bd9Sstevel@tonic-gate 		hubd_schedule_cleanup(hubd->h_usba_device->usb_root_hub_dip);
66077c478bd9Sstevel@tonic-gate 
66087c478bd9Sstevel@tonic-gate 		return (B_TRUE);
66097c478bd9Sstevel@tonic-gate 	default:
66107c478bd9Sstevel@tonic-gate 
66117c478bd9Sstevel@tonic-gate 		return (B_FALSE);
66127c478bd9Sstevel@tonic-gate 	}
66137c478bd9Sstevel@tonic-gate 
66147c478bd9Sstevel@tonic-gate }
66157c478bd9Sstevel@tonic-gate 
66167c478bd9Sstevel@tonic-gate 
66177c478bd9Sstevel@tonic-gate /* register callback with cpr framework */
66187c478bd9Sstevel@tonic-gate void
66197c478bd9Sstevel@tonic-gate hubd_register_cpr_callback(hubd_t *hubd)
66207c478bd9Sstevel@tonic-gate {
66217c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle,
66227c478bd9Sstevel@tonic-gate 	    "hubd_register_cpr_callback");
66237c478bd9Sstevel@tonic-gate 
66247c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
66257c478bd9Sstevel@tonic-gate 	hubd->h_cpr_cb =
66267c478bd9Sstevel@tonic-gate 	    (hubd_cpr_t *)kmem_zalloc(sizeof (hubd_cpr_t), KM_SLEEP);
66277c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
66287c478bd9Sstevel@tonic-gate 	mutex_init(&hubd->h_cpr_cb->lockp, NULL, MUTEX_DRIVER,
66297c478bd9Sstevel@tonic-gate 	    hubd->h_dev_data->dev_iblock_cookie);
66307c478bd9Sstevel@tonic-gate 	hubd->h_cpr_cb->statep = hubd;
66317c478bd9Sstevel@tonic-gate 	hubd->h_cpr_cb->cpr.cc_lockp = &hubd->h_cpr_cb->lockp;
66327c478bd9Sstevel@tonic-gate 	hubd->h_cpr_cb->cpr.cc_id = callb_add(hubd_cpr_post_user_callb,
66337c478bd9Sstevel@tonic-gate 	    (void *)hubd->h_cpr_cb, CB_CL_CPR_POST_USER, "hubd");
66347c478bd9Sstevel@tonic-gate }
66357c478bd9Sstevel@tonic-gate 
66367c478bd9Sstevel@tonic-gate 
66377c478bd9Sstevel@tonic-gate /* unregister callback with cpr framework */
66387c478bd9Sstevel@tonic-gate void
66397c478bd9Sstevel@tonic-gate hubd_unregister_cpr_callback(hubd_t *hubd)
66407c478bd9Sstevel@tonic-gate {
66417c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle,
66427c478bd9Sstevel@tonic-gate 	    "hubd_unregister_cpr_callback");
66437c478bd9Sstevel@tonic-gate 
66447c478bd9Sstevel@tonic-gate 	if (hubd->h_cpr_cb) {
66457c478bd9Sstevel@tonic-gate 		(void) callb_delete(hubd->h_cpr_cb->cpr.cc_id);
66467c478bd9Sstevel@tonic-gate 		mutex_destroy(&hubd->h_cpr_cb->lockp);
66477c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
66487c478bd9Sstevel@tonic-gate 		kmem_free(hubd->h_cpr_cb, sizeof (hubd_cpr_t));
66497c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
66507c478bd9Sstevel@tonic-gate 	}
66517c478bd9Sstevel@tonic-gate }
66527c478bd9Sstevel@tonic-gate 
66537c478bd9Sstevel@tonic-gate 
66547c478bd9Sstevel@tonic-gate /*
66557c478bd9Sstevel@tonic-gate  * Power management
66567c478bd9Sstevel@tonic-gate  *
66577c478bd9Sstevel@tonic-gate  * create the pm components required for power management
66587c478bd9Sstevel@tonic-gate  */
66597c478bd9Sstevel@tonic-gate static void
66607c478bd9Sstevel@tonic-gate hubd_create_pm_components(dev_info_t *dip, hubd_t *hubd)
66617c478bd9Sstevel@tonic-gate {
66627c478bd9Sstevel@tonic-gate 	hub_power_t	*hubpm;
66637c478bd9Sstevel@tonic-gate 
66647c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
66657c478bd9Sstevel@tonic-gate 	    "hubd_create_pm_components: Begin");
66667c478bd9Sstevel@tonic-gate 
66677c478bd9Sstevel@tonic-gate 	/* Allocate the state structure */
66687c478bd9Sstevel@tonic-gate 	hubpm = kmem_zalloc(sizeof (hub_power_t), KM_SLEEP);
66697c478bd9Sstevel@tonic-gate 
66707c478bd9Sstevel@tonic-gate 	hubd->h_hubpm = hubpm;
66717c478bd9Sstevel@tonic-gate 	hubpm->hubp_hubd = hubd;
66727c478bd9Sstevel@tonic-gate 	hubpm->hubp_pm_capabilities = 0;
66737c478bd9Sstevel@tonic-gate 	hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR;
66747c478bd9Sstevel@tonic-gate 	hubpm->hubp_time_at_full_power = ddi_get_time();
66757c478bd9Sstevel@tonic-gate 	hubpm->hubp_min_pm_threshold = hubdi_min_pm_threshold;
66767c478bd9Sstevel@tonic-gate 
66777c478bd9Sstevel@tonic-gate 	/* alloc memory to save power states of children */
66787c478bd9Sstevel@tonic-gate 	hubpm->hubp_child_pwrstate = (uint8_t *)
66797c478bd9Sstevel@tonic-gate 		kmem_zalloc(MAX_PORTS + 1, KM_SLEEP);
66807c478bd9Sstevel@tonic-gate 
66817c478bd9Sstevel@tonic-gate 	/*
66827c478bd9Sstevel@tonic-gate 	 * if the enable remote wakeup fails
66837c478bd9Sstevel@tonic-gate 	 * we still want to enable
66847c478bd9Sstevel@tonic-gate 	 * parent notification so we can PM the children
66857c478bd9Sstevel@tonic-gate 	 */
66867c478bd9Sstevel@tonic-gate 	usb_enable_parent_notification(dip);
66877c478bd9Sstevel@tonic-gate 
66887c478bd9Sstevel@tonic-gate 	if (usb_handle_remote_wakeup(dip,
66897c478bd9Sstevel@tonic-gate 	    USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) {
66907c478bd9Sstevel@tonic-gate 		uint_t		pwr_states;
66917c478bd9Sstevel@tonic-gate 
66927c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
66937c478bd9Sstevel@tonic-gate 		    "hubd_create_pm_components: "
66947c478bd9Sstevel@tonic-gate 		    "Remote Wakeup Enabled");
66957c478bd9Sstevel@tonic-gate 
66967c478bd9Sstevel@tonic-gate 		if (usb_create_pm_components(dip, &pwr_states) ==
66977c478bd9Sstevel@tonic-gate 		    USB_SUCCESS) {
66987c478bd9Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
66997c478bd9Sstevel@tonic-gate 			hubpm->hubp_wakeup_enabled = 1;
67007c478bd9Sstevel@tonic-gate 			hubpm->hubp_pwr_states = (uint8_t)pwr_states;
67017c478bd9Sstevel@tonic-gate 
67027c478bd9Sstevel@tonic-gate 			/* we are busy now till end of the attach */
67037c478bd9Sstevel@tonic-gate 			hubd_pm_busy_component(hubd, dip, 0);
67047c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
67057c478bd9Sstevel@tonic-gate 
67067c478bd9Sstevel@tonic-gate 			/* bring the device to full power */
67077c478bd9Sstevel@tonic-gate 			(void) pm_raise_power(dip, 0,
67087c478bd9Sstevel@tonic-gate 			    USB_DEV_OS_FULL_PWR);
67097c478bd9Sstevel@tonic-gate 		}
67107c478bd9Sstevel@tonic-gate 	}
67117c478bd9Sstevel@tonic-gate 
67127c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
67137c478bd9Sstevel@tonic-gate 	    "hubd_create_pm_components: END");
67147c478bd9Sstevel@tonic-gate }
67157c478bd9Sstevel@tonic-gate 
67167c478bd9Sstevel@tonic-gate 
67177c478bd9Sstevel@tonic-gate /*
67187c478bd9Sstevel@tonic-gate  * Attachment point management
67197c478bd9Sstevel@tonic-gate  */
67207c478bd9Sstevel@tonic-gate /* ARGSUSED */
67217c478bd9Sstevel@tonic-gate int
67227c478bd9Sstevel@tonic-gate usba_hubdi_open(dev_info_t *dip, dev_t *devp, int flags, int otyp,
67237c478bd9Sstevel@tonic-gate 	cred_t *credp)
67247c478bd9Sstevel@tonic-gate {
67257c478bd9Sstevel@tonic-gate 	hubd_t *hubd;
67267c478bd9Sstevel@tonic-gate 
67277c478bd9Sstevel@tonic-gate 	if (otyp != OTYP_CHR)
67287c478bd9Sstevel@tonic-gate 		return (EINVAL);
67297c478bd9Sstevel@tonic-gate 
67307c478bd9Sstevel@tonic-gate 	hubd = hubd_get_soft_state(dip);
67317c478bd9Sstevel@tonic-gate 	if (hubd == NULL) {
67327c478bd9Sstevel@tonic-gate 		return (ENXIO);
67337c478bd9Sstevel@tonic-gate 	}
67347c478bd9Sstevel@tonic-gate 
67357c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
67367c478bd9Sstevel@tonic-gate 	    "hubd_open:");
67377c478bd9Sstevel@tonic-gate 
67387c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
67397c478bd9Sstevel@tonic-gate 	if ((flags & FEXCL) && (hubd->h_softstate & HUBD_SS_ISOPEN)) {
67407c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
67417c478bd9Sstevel@tonic-gate 
67427c478bd9Sstevel@tonic-gate 		return (EBUSY);
67437c478bd9Sstevel@tonic-gate 	}
67447c478bd9Sstevel@tonic-gate 
67457c478bd9Sstevel@tonic-gate 	hubd->h_softstate |= HUBD_SS_ISOPEN;
67467c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
67477c478bd9Sstevel@tonic-gate 
67487c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, "opened");
67497c478bd9Sstevel@tonic-gate 
67507c478bd9Sstevel@tonic-gate 	return (0);
67517c478bd9Sstevel@tonic-gate }
67527c478bd9Sstevel@tonic-gate 
67537c478bd9Sstevel@tonic-gate 
67547c478bd9Sstevel@tonic-gate /* ARGSUSED */
67557c478bd9Sstevel@tonic-gate int
67567c478bd9Sstevel@tonic-gate usba_hubdi_close(dev_info_t *dip, dev_t dev, int flag, int otyp,
67577c478bd9Sstevel@tonic-gate 	cred_t *credp)
67587c478bd9Sstevel@tonic-gate {
67597c478bd9Sstevel@tonic-gate 	hubd_t *hubd;
67607c478bd9Sstevel@tonic-gate 
67617c478bd9Sstevel@tonic-gate 	if (otyp != OTYP_CHR) {
67627c478bd9Sstevel@tonic-gate 		return (EINVAL);
67637c478bd9Sstevel@tonic-gate 	}
67647c478bd9Sstevel@tonic-gate 
67657c478bd9Sstevel@tonic-gate 	hubd = hubd_get_soft_state(dip);
67667c478bd9Sstevel@tonic-gate 
67677c478bd9Sstevel@tonic-gate 	if (hubd == NULL) {
67687c478bd9Sstevel@tonic-gate 		return (ENXIO);
67697c478bd9Sstevel@tonic-gate 	}
67707c478bd9Sstevel@tonic-gate 
67717c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, "hubd_close:");
67727c478bd9Sstevel@tonic-gate 
67737c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
67747c478bd9Sstevel@tonic-gate 	hubd->h_softstate &= ~HUBD_SS_ISOPEN;
67757c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
67767c478bd9Sstevel@tonic-gate 
67777c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, "closed");
67787c478bd9Sstevel@tonic-gate 
67797c478bd9Sstevel@tonic-gate 	return (0);
67807c478bd9Sstevel@tonic-gate }
67817c478bd9Sstevel@tonic-gate 
67827c478bd9Sstevel@tonic-gate 
67837c478bd9Sstevel@tonic-gate /*
67847c478bd9Sstevel@tonic-gate  * hubd_ioctl: cfgadm controls
67857c478bd9Sstevel@tonic-gate  */
67867c478bd9Sstevel@tonic-gate /* ARGSUSED */
67877c478bd9Sstevel@tonic-gate int
67887c478bd9Sstevel@tonic-gate usba_hubdi_ioctl(dev_info_t *self, dev_t dev, int cmd, intptr_t arg,
67897c478bd9Sstevel@tonic-gate 	int mode, cred_t *credp, int *rvalp)
67907c478bd9Sstevel@tonic-gate {
67917c478bd9Sstevel@tonic-gate 	int			rv = 0;
67927c478bd9Sstevel@tonic-gate 	char			*msg;	/* for messages */
67937c478bd9Sstevel@tonic-gate 	hubd_t			*hubd;
67947c478bd9Sstevel@tonic-gate 	usb_port_t		port = 0;
67957c478bd9Sstevel@tonic-gate 	dev_info_t		*child_dip = NULL;
67967c478bd9Sstevel@tonic-gate 	dev_info_t		*rh_dip;
67977c478bd9Sstevel@tonic-gate 	devctl_ap_state_t	ap_state;
67987c478bd9Sstevel@tonic-gate 	struct devctl_iocdata	*dcp = NULL;
67997c478bd9Sstevel@tonic-gate 	usb_pipe_state_t	prev_pipe_state = 0;
68007c478bd9Sstevel@tonic-gate 	int			circ, rh_circ, prh_circ;
68017c478bd9Sstevel@tonic-gate 
68027c478bd9Sstevel@tonic-gate 	if ((hubd = hubd_get_soft_state(self)) == NULL) {
68037c478bd9Sstevel@tonic-gate 
68047c478bd9Sstevel@tonic-gate 		return (ENXIO);
68057c478bd9Sstevel@tonic-gate 	}
68067c478bd9Sstevel@tonic-gate 
68077c478bd9Sstevel@tonic-gate 	rh_dip = hubd->h_usba_device->usb_root_hub_dip;
68087c478bd9Sstevel@tonic-gate 
68097c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
68107c478bd9Sstevel@tonic-gate 	    "usba_hubdi_ioctl: "
68117c478bd9Sstevel@tonic-gate 	    "cmd=%x, arg=%lx, mode=%x, cred=%p, rval=%p dev=0x%lx",
68127c478bd9Sstevel@tonic-gate 	    cmd, arg, mode, credp, rvalp, dev);
68137c478bd9Sstevel@tonic-gate 
68147c478bd9Sstevel@tonic-gate 	/* read devctl ioctl data */
68157c478bd9Sstevel@tonic-gate 	if ((cmd != DEVCTL_AP_CONTROL) &&
68167c478bd9Sstevel@tonic-gate 	    (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)) {
68177c478bd9Sstevel@tonic-gate 
68187c478bd9Sstevel@tonic-gate 		return (EFAULT);
68197c478bd9Sstevel@tonic-gate 	}
68207c478bd9Sstevel@tonic-gate 
68217c478bd9Sstevel@tonic-gate 	/*
68227c478bd9Sstevel@tonic-gate 	 * make sure the hub is connected before trying any
68237c478bd9Sstevel@tonic-gate 	 * of the following operations:
68247c478bd9Sstevel@tonic-gate 	 * configure, connect, disconnect
68257c478bd9Sstevel@tonic-gate 	 */
68267c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
68277c478bd9Sstevel@tonic-gate 
68287c478bd9Sstevel@tonic-gate 	switch (cmd) {
68297c478bd9Sstevel@tonic-gate 	case DEVCTL_AP_DISCONNECT:
68307c478bd9Sstevel@tonic-gate 	case DEVCTL_AP_UNCONFIGURE:
68317c478bd9Sstevel@tonic-gate 	case DEVCTL_AP_CONFIGURE:
68327c478bd9Sstevel@tonic-gate 		if (hubd->h_dev_state == USB_DEV_DISCONNECTED) {
68337c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
68347c478bd9Sstevel@tonic-gate 			    "hubd: already gone");
68357c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
68367c478bd9Sstevel@tonic-gate 			if (dcp) {
68377c478bd9Sstevel@tonic-gate 				ndi_dc_freehdl(dcp);
68387c478bd9Sstevel@tonic-gate 			}
68397c478bd9Sstevel@tonic-gate 
68407c478bd9Sstevel@tonic-gate 			return (EIO);
68417c478bd9Sstevel@tonic-gate 		}
68427c478bd9Sstevel@tonic-gate 
68437c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
68447c478bd9Sstevel@tonic-gate 	case DEVCTL_AP_GETSTATE:
68457c478bd9Sstevel@tonic-gate 		if ((port = hubd_get_port_num(hubd, dcp)) == 0) {
68467c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
68477c478bd9Sstevel@tonic-gate 			    "hubd: bad port");
68487c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
68497c478bd9Sstevel@tonic-gate 			if (dcp) {
68507c478bd9Sstevel@tonic-gate 				ndi_dc_freehdl(dcp);
68517c478bd9Sstevel@tonic-gate 			}
68527c478bd9Sstevel@tonic-gate 
68537c478bd9Sstevel@tonic-gate 			return (EINVAL);
68547c478bd9Sstevel@tonic-gate 		}
68557c478bd9Sstevel@tonic-gate 		break;
68567c478bd9Sstevel@tonic-gate 
68577c478bd9Sstevel@tonic-gate 	case DEVCTL_AP_CONTROL:
68587c478bd9Sstevel@tonic-gate 
68597c478bd9Sstevel@tonic-gate 		break;
68607c478bd9Sstevel@tonic-gate 	default:
68617c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
68627c478bd9Sstevel@tonic-gate 		if (dcp) {
68637c478bd9Sstevel@tonic-gate 			ndi_dc_freehdl(dcp);
68647c478bd9Sstevel@tonic-gate 		}
68657c478bd9Sstevel@tonic-gate 
68667c478bd9Sstevel@tonic-gate 		return (ENOTTY);
68677c478bd9Sstevel@tonic-gate 	}
68687c478bd9Sstevel@tonic-gate 
68697c478bd9Sstevel@tonic-gate 	/* should not happen, just in case */
68707c478bd9Sstevel@tonic-gate 	if (hubd->h_dev_state == USB_DEV_SUSPENDED) {
68717c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
68727c478bd9Sstevel@tonic-gate 		if (dcp) {
68737c478bd9Sstevel@tonic-gate 			ndi_dc_freehdl(dcp);
68747c478bd9Sstevel@tonic-gate 		}
68757c478bd9Sstevel@tonic-gate 
68767c478bd9Sstevel@tonic-gate 		return (EIO);
68777c478bd9Sstevel@tonic-gate 	}
68787c478bd9Sstevel@tonic-gate 
68797c478bd9Sstevel@tonic-gate 	hubd_pm_busy_component(hubd, hubd->h_dip, 0);
68807c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
68817c478bd9Sstevel@tonic-gate 
68827c478bd9Sstevel@tonic-gate 	/* go full power */
68837c478bd9Sstevel@tonic-gate 	(void) pm_raise_power(hubd->h_dip, 0, USB_DEV_OS_FULL_PWR);
68847c478bd9Sstevel@tonic-gate 
68857c478bd9Sstevel@tonic-gate 	ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
68867c478bd9Sstevel@tonic-gate 	ndi_devi_enter(rh_dip, &rh_circ);
68877c478bd9Sstevel@tonic-gate 	ndi_devi_enter(hubd->h_dip, &circ);
68887c478bd9Sstevel@tonic-gate 
68897c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
68907c478bd9Sstevel@tonic-gate 
68917c478bd9Sstevel@tonic-gate 	/* stop polling iff it was active */
68927c478bd9Sstevel@tonic-gate 	if (hubd->h_ep1_ph) {
68937c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
68947c478bd9Sstevel@tonic-gate 		(void) usb_pipe_get_state(hubd->h_ep1_ph, &prev_pipe_state,
68957c478bd9Sstevel@tonic-gate 							    USB_FLAGS_SLEEP);
68967c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
68977c478bd9Sstevel@tonic-gate 
68987c478bd9Sstevel@tonic-gate 		if (prev_pipe_state == USB_PIPE_STATE_ACTIVE) {
68997c478bd9Sstevel@tonic-gate 			hubd_stop_polling(hubd);
69007c478bd9Sstevel@tonic-gate 		}
69017c478bd9Sstevel@tonic-gate 	}
69027c478bd9Sstevel@tonic-gate 
69037c478bd9Sstevel@tonic-gate 	hubd->h_hotplug_thread++;
69047c478bd9Sstevel@tonic-gate 
69057c478bd9Sstevel@tonic-gate 	switch (cmd) {
69067c478bd9Sstevel@tonic-gate 	case DEVCTL_AP_DISCONNECT:
69077c478bd9Sstevel@tonic-gate 		if (hubd_delete_child(hubd, port,
69087c478bd9Sstevel@tonic-gate 		    NDI_DEVI_REMOVE, B_FALSE) != USB_SUCCESS) {
69097c478bd9Sstevel@tonic-gate 			rv = EIO;
69107c478bd9Sstevel@tonic-gate 		}
69117c478bd9Sstevel@tonic-gate 
69127c478bd9Sstevel@tonic-gate 		break;
69137c478bd9Sstevel@tonic-gate 	case DEVCTL_AP_UNCONFIGURE:
69147c478bd9Sstevel@tonic-gate 		if (hubd_delete_child(hubd, port,
69157c478bd9Sstevel@tonic-gate 		    NDI_UNCONFIG, B_FALSE) != USB_SUCCESS) {
69167c478bd9Sstevel@tonic-gate 			rv = EIO;
69177c478bd9Sstevel@tonic-gate 		}
69187c478bd9Sstevel@tonic-gate 
69197c478bd9Sstevel@tonic-gate 		break;
69207c478bd9Sstevel@tonic-gate 	case DEVCTL_AP_CONFIGURE:
69217c478bd9Sstevel@tonic-gate 		/* toggle port */
69227c478bd9Sstevel@tonic-gate 		if (hubd_toggle_port(hubd, port) != USB_SUCCESS) {
69237c478bd9Sstevel@tonic-gate 			(void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
69247c478bd9Sstevel@tonic-gate 			rv = EIO;
69257c478bd9Sstevel@tonic-gate 
69267c478bd9Sstevel@tonic-gate 			break;
69277c478bd9Sstevel@tonic-gate 		}
69287c478bd9Sstevel@tonic-gate 
69297c478bd9Sstevel@tonic-gate 		(void) hubd_handle_port_connect(hubd, port);
69307c478bd9Sstevel@tonic-gate 		child_dip = hubd_get_child_dip(hubd, port);
69317c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
69327c478bd9Sstevel@tonic-gate 
69337c478bd9Sstevel@tonic-gate 		ndi_devi_exit(hubd->h_dip, circ);
69347c478bd9Sstevel@tonic-gate 		ndi_devi_exit(rh_dip, rh_circ);
69357c478bd9Sstevel@tonic-gate 		ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ);
69367c478bd9Sstevel@tonic-gate 		if ((child_dip == NULL) ||
69377c478bd9Sstevel@tonic-gate 		    (ndi_devi_online(child_dip, 0) != NDI_SUCCESS)) {
69387c478bd9Sstevel@tonic-gate 			rv = EIO;
69397c478bd9Sstevel@tonic-gate 		}
69407c478bd9Sstevel@tonic-gate 		ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
69417c478bd9Sstevel@tonic-gate 		ndi_devi_enter(rh_dip, &rh_circ);
69427c478bd9Sstevel@tonic-gate 		ndi_devi_enter(hubd->h_dip, &circ);
69437c478bd9Sstevel@tonic-gate 
69447c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
69457c478bd9Sstevel@tonic-gate 
69467c478bd9Sstevel@tonic-gate 		break;
69477c478bd9Sstevel@tonic-gate 	case DEVCTL_AP_GETSTATE:
69487c478bd9Sstevel@tonic-gate 		switch (hubd_cfgadm_state(hubd, port)) {
69497c478bd9Sstevel@tonic-gate 		case HUBD_CFGADM_DISCONNECTED:
69507c478bd9Sstevel@tonic-gate 			/* port previously 'disconnected' by cfgadm */
69517c478bd9Sstevel@tonic-gate 			ap_state.ap_rstate = AP_RSTATE_DISCONNECTED;
69527c478bd9Sstevel@tonic-gate 			ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED;
69537c478bd9Sstevel@tonic-gate 			ap_state.ap_condition = AP_COND_OK;
69547c478bd9Sstevel@tonic-gate 
69557c478bd9Sstevel@tonic-gate 			break;
69567c478bd9Sstevel@tonic-gate 		case HUBD_CFGADM_UNCONFIGURED:
69577c478bd9Sstevel@tonic-gate 			ap_state.ap_rstate = AP_RSTATE_CONNECTED;
69587c478bd9Sstevel@tonic-gate 			ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED;
69597c478bd9Sstevel@tonic-gate 			ap_state.ap_condition = AP_COND_OK;
69607c478bd9Sstevel@tonic-gate 
69617c478bd9Sstevel@tonic-gate 			break;
69627c478bd9Sstevel@tonic-gate 		case HUBD_CFGADM_CONFIGURED:
69637c478bd9Sstevel@tonic-gate 			ap_state.ap_rstate = AP_RSTATE_CONNECTED;
69647c478bd9Sstevel@tonic-gate 			ap_state.ap_ostate = AP_OSTATE_CONFIGURED;
69657c478bd9Sstevel@tonic-gate 			ap_state.ap_condition = AP_COND_OK;
69667c478bd9Sstevel@tonic-gate 
69677c478bd9Sstevel@tonic-gate 			break;
69687c478bd9Sstevel@tonic-gate 		case HUBD_CFGADM_STILL_REFERENCED:
69697c478bd9Sstevel@tonic-gate 			ap_state.ap_rstate = AP_RSTATE_EMPTY;
69707c478bd9Sstevel@tonic-gate 			ap_state.ap_ostate = AP_OSTATE_CONFIGURED;
69717c478bd9Sstevel@tonic-gate 			ap_state.ap_condition = AP_COND_UNUSABLE;
69727c478bd9Sstevel@tonic-gate 
69737c478bd9Sstevel@tonic-gate 			break;
69747c478bd9Sstevel@tonic-gate 		case HUBD_CFGADM_EMPTY:
69757c478bd9Sstevel@tonic-gate 		default:
69767c478bd9Sstevel@tonic-gate 			ap_state.ap_rstate = AP_RSTATE_EMPTY;
69777c478bd9Sstevel@tonic-gate 			ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED;
69787c478bd9Sstevel@tonic-gate 			ap_state.ap_condition = AP_COND_OK;
69797c478bd9Sstevel@tonic-gate 
69807c478bd9Sstevel@tonic-gate 			break;
69817c478bd9Sstevel@tonic-gate 		}
69827c478bd9Sstevel@tonic-gate 
69837c478bd9Sstevel@tonic-gate 		ap_state.ap_last_change = (time_t)-1;
69847c478bd9Sstevel@tonic-gate 		ap_state.ap_error_code = 0;
69857c478bd9Sstevel@tonic-gate 		ap_state.ap_in_transition = 0;
69867c478bd9Sstevel@tonic-gate 
69877c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
69887c478bd9Sstevel@tonic-gate 		    "DEVCTL_AP_GETSTATE: "
69897c478bd9Sstevel@tonic-gate 		    "ostate=0x%x, rstate=0x%x, condition=0x%x",
69907c478bd9Sstevel@tonic-gate 		    ap_state.ap_ostate,
69917c478bd9Sstevel@tonic-gate 		    ap_state.ap_rstate, ap_state.ap_condition);
69927c478bd9Sstevel@tonic-gate 
69937c478bd9Sstevel@tonic-gate 		/* copy the return-AP-state information to the user space */
69947c478bd9Sstevel@tonic-gate 		if (ndi_dc_return_ap_state(&ap_state, dcp) != NDI_SUCCESS) {
69957c478bd9Sstevel@tonic-gate 			rv = EFAULT;
69967c478bd9Sstevel@tonic-gate 		}
69977c478bd9Sstevel@tonic-gate 
69987c478bd9Sstevel@tonic-gate 		break;
69997c478bd9Sstevel@tonic-gate 	case DEVCTL_AP_CONTROL:
70007c478bd9Sstevel@tonic-gate 	{
70017c478bd9Sstevel@tonic-gate 		/*
70027c478bd9Sstevel@tonic-gate 		 * Generic devctl for hardware-specific functionality.
70037c478bd9Sstevel@tonic-gate 		 * For list of sub-commands see hubd_impl.h
70047c478bd9Sstevel@tonic-gate 		 */
70057c478bd9Sstevel@tonic-gate 		hubd_ioctl_data_t	ioc;	/* for 64 byte copies */
70067c478bd9Sstevel@tonic-gate 
70077c478bd9Sstevel@tonic-gate 		/* copy user ioctl data in first */
70087c478bd9Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
70097c478bd9Sstevel@tonic-gate 		if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
70107c478bd9Sstevel@tonic-gate 			hubd_ioctl_data_32_t ioc32;
70117c478bd9Sstevel@tonic-gate 
70127c478bd9Sstevel@tonic-gate 			if (ddi_copyin((void *)arg, (void *)&ioc32,
70137c478bd9Sstevel@tonic-gate 				sizeof (ioc32), mode) != 0) {
70147c478bd9Sstevel@tonic-gate 				rv = EFAULT;
70157c478bd9Sstevel@tonic-gate 
70167c478bd9Sstevel@tonic-gate 				break;
70177c478bd9Sstevel@tonic-gate 			}
70187c478bd9Sstevel@tonic-gate 			ioc.cmd		= (uint_t)ioc32.cmd;
70197c478bd9Sstevel@tonic-gate 			ioc.port	= (uint_t)ioc32.port;
70207c478bd9Sstevel@tonic-gate 			ioc.get_size	= (uint_t)ioc32.get_size;
70217c478bd9Sstevel@tonic-gate 			ioc.buf		= (caddr_t)(uintptr_t)ioc32.buf;
70227c478bd9Sstevel@tonic-gate 			ioc.bufsiz	= (uint_t)ioc32.bufsiz;
70237c478bd9Sstevel@tonic-gate 			ioc.misc_arg	= (uint_t)ioc32.misc_arg;
70247c478bd9Sstevel@tonic-gate 		} else
70257c478bd9Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
70267c478bd9Sstevel@tonic-gate 		if (ddi_copyin((void *)arg, (void *)&ioc, sizeof (ioc),
70277c478bd9Sstevel@tonic-gate 		    mode) != 0) {
70287c478bd9Sstevel@tonic-gate 			rv = EFAULT;
70297c478bd9Sstevel@tonic-gate 
70307c478bd9Sstevel@tonic-gate 			break;
70317c478bd9Sstevel@tonic-gate 		}
70327c478bd9Sstevel@tonic-gate 
70337c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_CBOPS, hubd->h_log_handle,
70347c478bd9Sstevel@tonic-gate 		    "DEVCTL_AP_CONTROL: ioc: cmd=0x%x port=%d get_size=%d"
70357c478bd9Sstevel@tonic-gate 		    "\n\tbuf=0x%p, bufsiz=%d,  misc_arg=%d", ioc.cmd,
70367c478bd9Sstevel@tonic-gate 		    ioc.port, ioc.get_size, ioc.buf, ioc.bufsiz, ioc.misc_arg);
70377c478bd9Sstevel@tonic-gate 
70387c478bd9Sstevel@tonic-gate 		/*
70397c478bd9Sstevel@tonic-gate 		 * To avoid BE/LE and 32/64 issues, a get_size always
70407c478bd9Sstevel@tonic-gate 		 * returns a 32-bit number.
70417c478bd9Sstevel@tonic-gate 		 */
70427c478bd9Sstevel@tonic-gate 		if (ioc.get_size != 0 && ioc.bufsiz != (sizeof (uint32_t))) {
70437c478bd9Sstevel@tonic-gate 			rv = EINVAL;
70447c478bd9Sstevel@tonic-gate 
70457c478bd9Sstevel@tonic-gate 			break;
70467c478bd9Sstevel@tonic-gate 		}
70477c478bd9Sstevel@tonic-gate 
70487c478bd9Sstevel@tonic-gate 		switch (ioc.cmd) {
70497c478bd9Sstevel@tonic-gate 		case USB_DESCR_TYPE_DEV:
70507c478bd9Sstevel@tonic-gate 			msg = "DEVCTL_AP_CONTROL: GET_DEVICE_DESC";
70517c478bd9Sstevel@tonic-gate 			if (ioc.get_size) {
70527c478bd9Sstevel@tonic-gate 				/* uint32 so this works 32/64 */
70537c478bd9Sstevel@tonic-gate 				uint32_t size = sizeof (usb_dev_descr_t);
70547c478bd9Sstevel@tonic-gate 
70557c478bd9Sstevel@tonic-gate 				if (ddi_copyout((void *)&size, ioc.buf,
70567c478bd9Sstevel@tonic-gate 				    ioc.bufsiz, mode) != 0) {
70577c478bd9Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
70587c478bd9Sstevel@tonic-gate 					    hubd->h_log_handle,
70597c478bd9Sstevel@tonic-gate 					    "%s: get_size copyout failed", msg);
70607c478bd9Sstevel@tonic-gate 					rv = EIO;
70617c478bd9Sstevel@tonic-gate 
70627c478bd9Sstevel@tonic-gate 					break;
70637c478bd9Sstevel@tonic-gate 				}
70647c478bd9Sstevel@tonic-gate 			} else {	/* send out the actual descr */
70657c478bd9Sstevel@tonic-gate 				usb_dev_descr_t *dev_descrp;
70667c478bd9Sstevel@tonic-gate 
70677c478bd9Sstevel@tonic-gate 				/* check child_dip */
70687c478bd9Sstevel@tonic-gate 				if ((child_dip = hubd_get_child_dip(hubd,
70697c478bd9Sstevel@tonic-gate 				    ioc.port)) == NULL) {
70707c478bd9Sstevel@tonic-gate 					rv = EINVAL;
70717c478bd9Sstevel@tonic-gate 
70727c478bd9Sstevel@tonic-gate 					break;
70737c478bd9Sstevel@tonic-gate 				}
70747c478bd9Sstevel@tonic-gate 
70757c478bd9Sstevel@tonic-gate 				dev_descrp = usb_get_dev_descr(child_dip);
70767c478bd9Sstevel@tonic-gate 				if (ioc.bufsiz != sizeof (*dev_descrp)) {
70777c478bd9Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
70787c478bd9Sstevel@tonic-gate 					    hubd->h_log_handle,
70797c478bd9Sstevel@tonic-gate 					    "%s: bufsize passed (%d) != sizeof "
70807c478bd9Sstevel@tonic-gate 					    "usba_device_descr_t (%d)", msg,
70817c478bd9Sstevel@tonic-gate 					    ioc.bufsiz, dev_descrp->bLength);
70827c478bd9Sstevel@tonic-gate 					rv = EINVAL;
70837c478bd9Sstevel@tonic-gate 
70847c478bd9Sstevel@tonic-gate 					break;
70857c478bd9Sstevel@tonic-gate 				}
70867c478bd9Sstevel@tonic-gate 
70877c478bd9Sstevel@tonic-gate 				if (ddi_copyout((void *)dev_descrp,
70887c478bd9Sstevel@tonic-gate 				    ioc.buf, ioc.bufsiz, mode) != 0) {
70897c478bd9Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
70907c478bd9Sstevel@tonic-gate 					    hubd->h_log_handle,
70917c478bd9Sstevel@tonic-gate 					    "%s: copyout failed.", msg);
70927c478bd9Sstevel@tonic-gate 					rv = EIO;
70937c478bd9Sstevel@tonic-gate 
70947c478bd9Sstevel@tonic-gate 					break;
70957c478bd9Sstevel@tonic-gate 				}
70967c478bd9Sstevel@tonic-gate 			}
70977c478bd9Sstevel@tonic-gate 			break;
70987c478bd9Sstevel@tonic-gate 		case USB_DESCR_TYPE_STRING:
70997c478bd9Sstevel@tonic-gate 		{
71007c478bd9Sstevel@tonic-gate 			char		*str;
71017c478bd9Sstevel@tonic-gate 			uint32_t	size;
71027c478bd9Sstevel@tonic-gate 			usba_device_t	*usba_device;
71037c478bd9Sstevel@tonic-gate 
71047c478bd9Sstevel@tonic-gate 			msg = "DEVCTL_AP_CONTROL: GET_STRING_DESCR";
71057c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
71067c478bd9Sstevel@tonic-gate 			    "%s: string request: %d", msg, ioc.misc_arg);
71077c478bd9Sstevel@tonic-gate 
71087c478bd9Sstevel@tonic-gate 			/* recheck */
71097c478bd9Sstevel@tonic-gate 			if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) ==
71107c478bd9Sstevel@tonic-gate 			    NULL) {
71117c478bd9Sstevel@tonic-gate 				rv = EINVAL;
71127c478bd9Sstevel@tonic-gate 
71137c478bd9Sstevel@tonic-gate 				break;
71147c478bd9Sstevel@tonic-gate 			}
71157c478bd9Sstevel@tonic-gate 			usba_device = usba_get_usba_device(child_dip);
71167c478bd9Sstevel@tonic-gate 
71177c478bd9Sstevel@tonic-gate 			switch (ioc.misc_arg) {
71187c478bd9Sstevel@tonic-gate 			case HUBD_MFG_STR:
71197c478bd9Sstevel@tonic-gate 				str = usba_device->usb_mfg_str;
71207c478bd9Sstevel@tonic-gate 
71217c478bd9Sstevel@tonic-gate 				break;
71227c478bd9Sstevel@tonic-gate 			case HUBD_PRODUCT_STR:
71237c478bd9Sstevel@tonic-gate 				str = usba_device->usb_product_str;
71247c478bd9Sstevel@tonic-gate 
71257c478bd9Sstevel@tonic-gate 				break;
71267c478bd9Sstevel@tonic-gate 			case HUBD_SERIALNO_STR:
71277c478bd9Sstevel@tonic-gate 				str = usba_device->usb_serialno_str;
71287c478bd9Sstevel@tonic-gate 
71297c478bd9Sstevel@tonic-gate 				break;
71307c478bd9Sstevel@tonic-gate 			case HUBD_CFG_DESCR_STR:
71317c478bd9Sstevel@tonic-gate 				mutex_enter(&usba_device->usb_mutex);
71327c478bd9Sstevel@tonic-gate 				str = usba_device->usb_cfg_str_descr[
71337c478bd9Sstevel@tonic-gate 					usba_device->usb_active_cfg_ndx];
71347c478bd9Sstevel@tonic-gate 				mutex_exit(&usba_device->usb_mutex);
71357c478bd9Sstevel@tonic-gate 
71367c478bd9Sstevel@tonic-gate 				break;
71377c478bd9Sstevel@tonic-gate 			default:
71387c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
71397c478bd9Sstevel@tonic-gate 				    hubd->h_log_handle,
71407c478bd9Sstevel@tonic-gate 				    "%s: Invalid string request", msg);
71417c478bd9Sstevel@tonic-gate 				rv = EINVAL;
71427c478bd9Sstevel@tonic-gate 
71437c478bd9Sstevel@tonic-gate 				break;
71447c478bd9Sstevel@tonic-gate 			} /* end of switch */
71457c478bd9Sstevel@tonic-gate 
71467c478bd9Sstevel@tonic-gate 			if (rv != 0) {
71477c478bd9Sstevel@tonic-gate 
71487c478bd9Sstevel@tonic-gate 				break;
71497c478bd9Sstevel@tonic-gate 			}
71507c478bd9Sstevel@tonic-gate 
71517c478bd9Sstevel@tonic-gate 			size = (str != NULL) ? strlen(str) + 1 : 0;
71527c478bd9Sstevel@tonic-gate 			if (ioc.get_size) {
71537c478bd9Sstevel@tonic-gate 				if (ddi_copyout((void *)&size, ioc.buf,
71547c478bd9Sstevel@tonic-gate 				    ioc.bufsiz, mode) != 0) {
71557c478bd9Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
71567c478bd9Sstevel@tonic-gate 					    hubd->h_log_handle,
71577c478bd9Sstevel@tonic-gate 					    "%s: copyout of size failed.", msg);
71587c478bd9Sstevel@tonic-gate 					rv = EIO;
71597c478bd9Sstevel@tonic-gate 
71607c478bd9Sstevel@tonic-gate 					break;
71617c478bd9Sstevel@tonic-gate 				}
71627c478bd9Sstevel@tonic-gate 			} else {
71637c478bd9Sstevel@tonic-gate 				if (size == 0) {
71647c478bd9Sstevel@tonic-gate 					USB_DPRINTF_L3(DPRINT_MASK_CBOPS,
71657c478bd9Sstevel@tonic-gate 					    hubd->h_log_handle,
71667c478bd9Sstevel@tonic-gate 					    "%s: String is NULL", msg);
71677c478bd9Sstevel@tonic-gate 					rv = EINVAL;
71687c478bd9Sstevel@tonic-gate 
71697c478bd9Sstevel@tonic-gate 					break;
71707c478bd9Sstevel@tonic-gate 				}
71717c478bd9Sstevel@tonic-gate 
71727c478bd9Sstevel@tonic-gate 				if (ioc.bufsiz != size) {
71737c478bd9Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
71747c478bd9Sstevel@tonic-gate 					    hubd->h_log_handle,
71757c478bd9Sstevel@tonic-gate 					    "%s: string buf size wrong", msg);
71767c478bd9Sstevel@tonic-gate 					rv = EINVAL;
71777c478bd9Sstevel@tonic-gate 
71787c478bd9Sstevel@tonic-gate 					break;
71797c478bd9Sstevel@tonic-gate 				}
71807c478bd9Sstevel@tonic-gate 
71817c478bd9Sstevel@tonic-gate 				if (ddi_copyout((void *)str, ioc.buf,
71827c478bd9Sstevel@tonic-gate 				    ioc.bufsiz, mode) != 0) {
71837c478bd9Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
71847c478bd9Sstevel@tonic-gate 					    hubd->h_log_handle,
71857c478bd9Sstevel@tonic-gate 					    "%s: copyout failed.", msg);
71867c478bd9Sstevel@tonic-gate 					rv = EIO;
71877c478bd9Sstevel@tonic-gate 
71887c478bd9Sstevel@tonic-gate 					break;
71897c478bd9Sstevel@tonic-gate 				}
71907c478bd9Sstevel@tonic-gate 			}
71917c478bd9Sstevel@tonic-gate 			break;
71927c478bd9Sstevel@tonic-gate 		}
71937c478bd9Sstevel@tonic-gate 		case HUBD_GET_CFGADM_NAME:
71947c478bd9Sstevel@tonic-gate 		{
71957c478bd9Sstevel@tonic-gate 			uint32_t   name_len;
71967c478bd9Sstevel@tonic-gate 			const char *name;
71977c478bd9Sstevel@tonic-gate 
71987c478bd9Sstevel@tonic-gate 			/* recheck */
71997c478bd9Sstevel@tonic-gate 			if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) ==
72007c478bd9Sstevel@tonic-gate 			    NULL) {
72017c478bd9Sstevel@tonic-gate 				rv = EINVAL;
72027c478bd9Sstevel@tonic-gate 
72037c478bd9Sstevel@tonic-gate 				break;
72047c478bd9Sstevel@tonic-gate 			}
72057c478bd9Sstevel@tonic-gate 			name = ddi_node_name(child_dip);
72067c478bd9Sstevel@tonic-gate 			if (name == NULL) {
72077c478bd9Sstevel@tonic-gate 				name = "unsupported";
72087c478bd9Sstevel@tonic-gate 			}
72097c478bd9Sstevel@tonic-gate 			name_len = strlen(name) + 1;
72107c478bd9Sstevel@tonic-gate 
72117c478bd9Sstevel@tonic-gate 			msg = "DEVCTL_AP_CONTROL: HUBD_GET_CFGADM_NAME";
72127c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
72137c478bd9Sstevel@tonic-gate 			    "%s: name=%s name_len=%d", msg, name, name_len);
72147c478bd9Sstevel@tonic-gate 
72157c478bd9Sstevel@tonic-gate 			if (ioc.get_size) {
72167c478bd9Sstevel@tonic-gate 				if (ddi_copyout((void *)&name_len,
72177c478bd9Sstevel@tonic-gate 				    ioc.buf, ioc.bufsiz, mode) != 0) {
72187c478bd9Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
72197c478bd9Sstevel@tonic-gate 					    hubd->h_log_handle,
72207c478bd9Sstevel@tonic-gate 					    "%s: copyout of size failed", msg);
72217c478bd9Sstevel@tonic-gate 					rv = EIO;
72227c478bd9Sstevel@tonic-gate 
72237c478bd9Sstevel@tonic-gate 					break;
72247c478bd9Sstevel@tonic-gate 				}
72257c478bd9Sstevel@tonic-gate 			} else {
72267c478bd9Sstevel@tonic-gate 				if (ioc.bufsiz != name_len) {
72277c478bd9Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
72287c478bd9Sstevel@tonic-gate 					    hubd->h_log_handle,
72297c478bd9Sstevel@tonic-gate 					    "%s: string buf length wrong", msg);
72307c478bd9Sstevel@tonic-gate 					rv = EINVAL;
72317c478bd9Sstevel@tonic-gate 
72327c478bd9Sstevel@tonic-gate 					break;
72337c478bd9Sstevel@tonic-gate 				}
72347c478bd9Sstevel@tonic-gate 
72357c478bd9Sstevel@tonic-gate 				if (ddi_copyout((void *)name, ioc.buf,
72367c478bd9Sstevel@tonic-gate 				    ioc.bufsiz, mode) != 0) {
72377c478bd9Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
72387c478bd9Sstevel@tonic-gate 					    hubd->h_log_handle,
72397c478bd9Sstevel@tonic-gate 					    "%s: copyout failed.", msg);
72407c478bd9Sstevel@tonic-gate 					rv = EIO;
72417c478bd9Sstevel@tonic-gate 
72427c478bd9Sstevel@tonic-gate 					break;
72437c478bd9Sstevel@tonic-gate 				}
72447c478bd9Sstevel@tonic-gate 			}
72457c478bd9Sstevel@tonic-gate 
72467c478bd9Sstevel@tonic-gate 			break;
72477c478bd9Sstevel@tonic-gate 		}
72487c478bd9Sstevel@tonic-gate 
72497c478bd9Sstevel@tonic-gate 		/*
72507c478bd9Sstevel@tonic-gate 		 * Return the config index for the currently-configured
72517c478bd9Sstevel@tonic-gate 		 * configuration.
72527c478bd9Sstevel@tonic-gate 		 */
72537c478bd9Sstevel@tonic-gate 		case HUBD_GET_CURRENT_CONFIG:
72547c478bd9Sstevel@tonic-gate 		{
72557c478bd9Sstevel@tonic-gate 			uint_t		config_index;
72567c478bd9Sstevel@tonic-gate 			uint32_t	size = sizeof (config_index);
72577c478bd9Sstevel@tonic-gate 			usba_device_t	*usba_device;
72587c478bd9Sstevel@tonic-gate 
72597c478bd9Sstevel@tonic-gate 			msg = "DEVCTL_AP_CONTROL: GET_CURRENT_CONFIG";
72607c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
72617c478bd9Sstevel@tonic-gate 			    "%s", msg);
72627c478bd9Sstevel@tonic-gate 
72637c478bd9Sstevel@tonic-gate 			/*
72647c478bd9Sstevel@tonic-gate 			 * Return the config index for the configuration
72657c478bd9Sstevel@tonic-gate 			 * currently in use.
72667c478bd9Sstevel@tonic-gate 			 * Recheck if child_dip exists
72677c478bd9Sstevel@tonic-gate 			 */
72687c478bd9Sstevel@tonic-gate 			if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) ==
72697c478bd9Sstevel@tonic-gate 			    NULL) {
72707c478bd9Sstevel@tonic-gate 				rv = EINVAL;
72717c478bd9Sstevel@tonic-gate 
72727c478bd9Sstevel@tonic-gate 				break;
72737c478bd9Sstevel@tonic-gate 			}
72747c478bd9Sstevel@tonic-gate 
72757c478bd9Sstevel@tonic-gate 			usba_device = usba_get_usba_device(child_dip);
72767c478bd9Sstevel@tonic-gate 			mutex_enter(&usba_device->usb_mutex);
72777c478bd9Sstevel@tonic-gate 			config_index = usba_device->usb_active_cfg_ndx;
72787c478bd9Sstevel@tonic-gate 			mutex_exit(&usba_device->usb_mutex);
72797c478bd9Sstevel@tonic-gate 
72807c478bd9Sstevel@tonic-gate 			if (ioc.get_size) {
72817c478bd9Sstevel@tonic-gate 				if (ddi_copyout((void *)&size,
72827c478bd9Sstevel@tonic-gate 				    ioc.buf, ioc.bufsiz, mode) != 0) {
72837c478bd9Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
72847c478bd9Sstevel@tonic-gate 					    hubd->h_log_handle,
72857c478bd9Sstevel@tonic-gate 					    "%s: copyout of size failed.", msg);
72867c478bd9Sstevel@tonic-gate 					rv = EIO;
72877c478bd9Sstevel@tonic-gate 
72887c478bd9Sstevel@tonic-gate 					break;
72897c478bd9Sstevel@tonic-gate 				}
72907c478bd9Sstevel@tonic-gate 			} else {
72917c478bd9Sstevel@tonic-gate 				if (ioc.bufsiz != size) {
72927c478bd9Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
72937c478bd9Sstevel@tonic-gate 					    hubd->h_log_handle,
72947c478bd9Sstevel@tonic-gate 					    "%s: buffer size wrong", msg);
72957c478bd9Sstevel@tonic-gate 					rv = EINVAL;
72967c478bd9Sstevel@tonic-gate 
72977c478bd9Sstevel@tonic-gate 					break;
72987c478bd9Sstevel@tonic-gate 				}
72997c478bd9Sstevel@tonic-gate 				if (ddi_copyout((void *)&config_index,
73007c478bd9Sstevel@tonic-gate 				    ioc.buf, ioc.bufsiz, mode) != 0) {
73017c478bd9Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
73027c478bd9Sstevel@tonic-gate 					    hubd->h_log_handle,
73037c478bd9Sstevel@tonic-gate 					    "%s: copyout failed", msg);
73047c478bd9Sstevel@tonic-gate 					rv = EIO;
73057c478bd9Sstevel@tonic-gate 				}
73067c478bd9Sstevel@tonic-gate 			}
73077c478bd9Sstevel@tonic-gate 
73087c478bd9Sstevel@tonic-gate 			break;
73097c478bd9Sstevel@tonic-gate 		}
73107c478bd9Sstevel@tonic-gate 		case HUBD_GET_DEVICE_PATH:
73117c478bd9Sstevel@tonic-gate 		{
73127c478bd9Sstevel@tonic-gate 			char		*path;
73137c478bd9Sstevel@tonic-gate 			uint32_t	size;
73147c478bd9Sstevel@tonic-gate 
73157c478bd9Sstevel@tonic-gate 			msg = "DEVCTL_AP_CONTROL: GET_DEVICE_PATH";
73167c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
73177c478bd9Sstevel@tonic-gate 			    "%s", msg);
73187c478bd9Sstevel@tonic-gate 
73197c478bd9Sstevel@tonic-gate 			/* Recheck if child_dip exists */
73207c478bd9Sstevel@tonic-gate 			if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) ==
73217c478bd9Sstevel@tonic-gate 			    NULL) {
73227c478bd9Sstevel@tonic-gate 				rv = EINVAL;
73237c478bd9Sstevel@tonic-gate 
73247c478bd9Sstevel@tonic-gate 				break;
73257c478bd9Sstevel@tonic-gate 			}
73267c478bd9Sstevel@tonic-gate 
73277c478bd9Sstevel@tonic-gate 			/* ddi_pathname doesn't supply /devices, so we do. */
73287c478bd9Sstevel@tonic-gate 			path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
73297c478bd9Sstevel@tonic-gate 			(void) strcpy(path, "/devices");
73307c478bd9Sstevel@tonic-gate 			(void) ddi_pathname(child_dip, path + strlen(path));
73317c478bd9Sstevel@tonic-gate 			size = strlen(path) + 1;
73327c478bd9Sstevel@tonic-gate 
73337c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
73347c478bd9Sstevel@tonic-gate 			    "%s: device path=%s  size=%d", msg, path, size);
73357c478bd9Sstevel@tonic-gate 
73367c478bd9Sstevel@tonic-gate 			if (ioc.get_size) {
73377c478bd9Sstevel@tonic-gate 				if (ddi_copyout((void *)&size,
73387c478bd9Sstevel@tonic-gate 				    ioc.buf, ioc.bufsiz, mode) != 0) {
73397c478bd9Sstevel@tonic-gate 
73407c478bd9Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
73417c478bd9Sstevel@tonic-gate 					    hubd->h_log_handle,
73427c478bd9Sstevel@tonic-gate 					    "%s: copyout of size failed.", msg);
73437c478bd9Sstevel@tonic-gate 					rv = EIO;
73447c478bd9Sstevel@tonic-gate 				}
73457c478bd9Sstevel@tonic-gate 			} else {
73467c478bd9Sstevel@tonic-gate 				if (ioc.bufsiz != size) {
73477c478bd9Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
73487c478bd9Sstevel@tonic-gate 					    hubd->h_log_handle,
73497c478bd9Sstevel@tonic-gate 					    "%s: buffer wrong size.", msg);
73507c478bd9Sstevel@tonic-gate 					rv = EINVAL;
73517c478bd9Sstevel@tonic-gate 				} else if (ddi_copyout((void *)path,
73527c478bd9Sstevel@tonic-gate 				    ioc.buf, ioc.bufsiz, mode) != 0) {
73537c478bd9Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
73547c478bd9Sstevel@tonic-gate 					    hubd->h_log_handle,
73557c478bd9Sstevel@tonic-gate 					    "%s: copyout failed.", msg);
73567c478bd9Sstevel@tonic-gate 					rv = EIO;
73577c478bd9Sstevel@tonic-gate 				}
73587c478bd9Sstevel@tonic-gate 			}
73597c478bd9Sstevel@tonic-gate 			kmem_free(path, MAXPATHLEN);
73607c478bd9Sstevel@tonic-gate 
73617c478bd9Sstevel@tonic-gate 			break;
73627c478bd9Sstevel@tonic-gate 		}
73637c478bd9Sstevel@tonic-gate 		case HUBD_REFRESH_DEVDB:
73647c478bd9Sstevel@tonic-gate 			msg = "DEVCTL_AP_CONTROL: HUBD_REFRESH_DEVDB";
73657c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_CBOPS, hubd->h_log_handle,
73667c478bd9Sstevel@tonic-gate 			    "%s", msg);
73677c478bd9Sstevel@tonic-gate 
73687c478bd9Sstevel@tonic-gate 			if ((rv = usba_devdb_refresh()) != USB_SUCCESS) {
73697c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
73707c478bd9Sstevel@tonic-gate 				    hubd->h_log_handle,
73717c478bd9Sstevel@tonic-gate 				    "%s: Failed: %d", msg, rv);
73727c478bd9Sstevel@tonic-gate 				rv = EIO;
73737c478bd9Sstevel@tonic-gate 			}
73747c478bd9Sstevel@tonic-gate 
73757c478bd9Sstevel@tonic-gate 			break;
73767c478bd9Sstevel@tonic-gate 		default:
73777c478bd9Sstevel@tonic-gate 			rv = ENOTSUP;
73787c478bd9Sstevel@tonic-gate 		}	/* end switch */
73797c478bd9Sstevel@tonic-gate 
73807c478bd9Sstevel@tonic-gate 		break;
73817c478bd9Sstevel@tonic-gate 	}
73827c478bd9Sstevel@tonic-gate 
73837c478bd9Sstevel@tonic-gate 	default:
73847c478bd9Sstevel@tonic-gate 		rv = ENOTTY;
73857c478bd9Sstevel@tonic-gate 	}
73867c478bd9Sstevel@tonic-gate 
73877c478bd9Sstevel@tonic-gate 	if (dcp) {
73887c478bd9Sstevel@tonic-gate 		ndi_dc_freehdl(dcp);
73897c478bd9Sstevel@tonic-gate 	}
73907c478bd9Sstevel@tonic-gate 
73917c478bd9Sstevel@tonic-gate 	/* allow hotplug thread now */
73927c478bd9Sstevel@tonic-gate 	hubd->h_hotplug_thread--;
73937c478bd9Sstevel@tonic-gate 
73947c478bd9Sstevel@tonic-gate 	if ((hubd->h_dev_state == USB_DEV_ONLINE) &&
73957c478bd9Sstevel@tonic-gate 	    hubd->h_ep1_ph && (prev_pipe_state == USB_PIPE_STATE_ACTIVE)) {
73967c478bd9Sstevel@tonic-gate 		hubd_start_polling(hubd, 0);
73977c478bd9Sstevel@tonic-gate 	}
73987c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
73997c478bd9Sstevel@tonic-gate 
74007c478bd9Sstevel@tonic-gate 	ndi_devi_exit(hubd->h_dip, circ);
74017c478bd9Sstevel@tonic-gate 	ndi_devi_exit(rh_dip, rh_circ);
74027c478bd9Sstevel@tonic-gate 	ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ);
74037c478bd9Sstevel@tonic-gate 
74047c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
74057c478bd9Sstevel@tonic-gate 	hubd_pm_idle_component(hubd, hubd->h_dip, 0);
74067c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
74077c478bd9Sstevel@tonic-gate 
74087c478bd9Sstevel@tonic-gate 	return (rv);
74097c478bd9Sstevel@tonic-gate }
74107c478bd9Sstevel@tonic-gate 
74117c478bd9Sstevel@tonic-gate 
74127c478bd9Sstevel@tonic-gate /*
74137c478bd9Sstevel@tonic-gate  * Helper func used only to help construct the names for the attachment point
74147c478bd9Sstevel@tonic-gate  * minor nodes.  Used only in usba_hubdi_attach.
74157c478bd9Sstevel@tonic-gate  * Returns whether it found ancestry or not (USB_SUCCESS if yes).
74167c478bd9Sstevel@tonic-gate  * ports between the root hub and the device represented by dip.
74177c478bd9Sstevel@tonic-gate  * E.g.,  "2.4.3.1" means this device is
74187c478bd9Sstevel@tonic-gate  *	plugged into port 1 of a hub that is
74197c478bd9Sstevel@tonic-gate  *	plugged into port 3 of a hub that is
74207c478bd9Sstevel@tonic-gate  *	plugged into port 4 of a hub that is
74217c478bd9Sstevel@tonic-gate  *	plugged into port 2 of the root hub.
74227c478bd9Sstevel@tonic-gate  * NOTE: Max ap_id path len is HUBD_APID_NAMELEN (32 chars), which is
74237c478bd9Sstevel@tonic-gate  * more than sufficient (as hubs are a max 6 levels deep, port needs 3
74247c478bd9Sstevel@tonic-gate  * chars plus NULL each)
74257c478bd9Sstevel@tonic-gate  */
74267c478bd9Sstevel@tonic-gate static void
74277c478bd9Sstevel@tonic-gate hubd_get_ancestry_str(hubd_t *hubd)
74287c478bd9Sstevel@tonic-gate {
74297c478bd9Sstevel@tonic-gate 	char	dev_path[MAXPATHLEN];
74307c478bd9Sstevel@tonic-gate 	char	*port_num_pos;
74317c478bd9Sstevel@tonic-gate 	char	port_list[HUBD_APID_NAMELEN];
74327c478bd9Sstevel@tonic-gate 	char	*port_list_end = port_list;
74337c478bd9Sstevel@tonic-gate 
74347c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
74357c478bd9Sstevel@tonic-gate 	    "hubd_get_ancestry_str: hubd=0x%p", hubd);
74367c478bd9Sstevel@tonic-gate 
74377c478bd9Sstevel@tonic-gate 	dev_path[0] = '\0';
74387c478bd9Sstevel@tonic-gate 	(void) ddi_pathname(hubd->h_dip, dev_path);
74397c478bd9Sstevel@tonic-gate 	port_num_pos = dev_path;
74407c478bd9Sstevel@tonic-gate 
74417c478bd9Sstevel@tonic-gate 	port_list[0] = NULL;
74427c478bd9Sstevel@tonic-gate 	while ((port_num_pos = (char *)strstr(port_num_pos, "hub@")) != NULL) {
74437c478bd9Sstevel@tonic-gate 		/*
74447c478bd9Sstevel@tonic-gate 		 * Found a non-root hub between the root hub port and device.
74457c478bd9Sstevel@tonic-gate 		 * Get the number of the port this hub is plugged into,
74467c478bd9Sstevel@tonic-gate 		 * and append it to the ancestry string.
74477c478bd9Sstevel@tonic-gate 		 */
74487c478bd9Sstevel@tonic-gate 		if (port_list_end != port_list) { /* have list already */
74497c478bd9Sstevel@tonic-gate 			(void) strcat(port_list_end, ".");
74507c478bd9Sstevel@tonic-gate 			port_list_end++;
74517c478bd9Sstevel@tonic-gate 		}
74527c478bd9Sstevel@tonic-gate 
74537c478bd9Sstevel@tonic-gate 		while (!isdigit(*port_num_pos)) {
74547c478bd9Sstevel@tonic-gate 			if (*port_num_pos++ == '\0') {
74557c478bd9Sstevel@tonic-gate 
74567c478bd9Sstevel@tonic-gate 				break;
74577c478bd9Sstevel@tonic-gate 			}
74587c478bd9Sstevel@tonic-gate 		}
74597c478bd9Sstevel@tonic-gate 
74607c478bd9Sstevel@tonic-gate 		while (isdigit(*port_num_pos)) {
74617c478bd9Sstevel@tonic-gate 			*port_list_end++ = *port_num_pos++;
74627c478bd9Sstevel@tonic-gate 			ASSERT(port_list_end <
74637c478bd9Sstevel@tonic-gate 			    (port_list + sizeof (port_list)));
74647c478bd9Sstevel@tonic-gate 			ASSERT(port_num_pos < (dev_path + sizeof (dev_path)));
74657c478bd9Sstevel@tonic-gate 		}
74667c478bd9Sstevel@tonic-gate 		*port_list_end = '\0';
74677c478bd9Sstevel@tonic-gate 	}
74687c478bd9Sstevel@tonic-gate 
74697c478bd9Sstevel@tonic-gate 	if (port_list_end != port_list) {
74707c478bd9Sstevel@tonic-gate 		(void) strcpy(hubd->h_ancestry_str, port_list);
74717c478bd9Sstevel@tonic-gate 		(void) strcat(hubd->h_ancestry_str, ".");
74727c478bd9Sstevel@tonic-gate 	}
74737c478bd9Sstevel@tonic-gate }
74747c478bd9Sstevel@tonic-gate 
74757c478bd9Sstevel@tonic-gate 
74767c478bd9Sstevel@tonic-gate /* Get which port to operate on.  */
74777c478bd9Sstevel@tonic-gate static usb_port_t
74787c478bd9Sstevel@tonic-gate hubd_get_port_num(hubd_t *hubd, struct devctl_iocdata *dcp)
74797c478bd9Sstevel@tonic-gate {
74807c478bd9Sstevel@tonic-gate 	int32_t port;
74817c478bd9Sstevel@tonic-gate 
74827c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
74837c478bd9Sstevel@tonic-gate 
74847c478bd9Sstevel@tonic-gate 	/* Get which port to operate on.  */
74857c478bd9Sstevel@tonic-gate 	if (nvlist_lookup_int32(ndi_dc_get_ap_data(dcp), "port", &port) != 0) {
74867c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_CBOPS, hubd->h_log_handle,
74877c478bd9Sstevel@tonic-gate 		    "hubd_get_port_num: port lookup failed");
74887c478bd9Sstevel@tonic-gate 		port = 0;
74897c478bd9Sstevel@tonic-gate 	}
74907c478bd9Sstevel@tonic-gate 
74917c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_CBOPS,  hubd->h_log_handle,
74927c478bd9Sstevel@tonic-gate 	    "hubd_get_port_num: hubd=0x%p, port=%d", hubd, port);
74937c478bd9Sstevel@tonic-gate 
74947c478bd9Sstevel@tonic-gate 	return ((usb_port_t)port);
74957c478bd9Sstevel@tonic-gate }
74967c478bd9Sstevel@tonic-gate 
74977c478bd9Sstevel@tonic-gate 
74987c478bd9Sstevel@tonic-gate /* check if child still exists */
74997c478bd9Sstevel@tonic-gate static dev_info_t *
75007c478bd9Sstevel@tonic-gate hubd_get_child_dip(hubd_t *hubd, usb_port_t port)
75017c478bd9Sstevel@tonic-gate {
75027c478bd9Sstevel@tonic-gate 	dev_info_t *child_dip = hubd->h_children_dips[port];
75037c478bd9Sstevel@tonic-gate 
75047c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_CBOPS,  hubd->h_log_handle,
75057c478bd9Sstevel@tonic-gate 	    "hubd_get_child_dip: hubd=0x%p, port=%d", hubd, port);
75067c478bd9Sstevel@tonic-gate 
75077c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
75087c478bd9Sstevel@tonic-gate 
75097c478bd9Sstevel@tonic-gate 	return (child_dip);
75107c478bd9Sstevel@tonic-gate }
75117c478bd9Sstevel@tonic-gate 
75127c478bd9Sstevel@tonic-gate 
75137c478bd9Sstevel@tonic-gate /*
75147c478bd9Sstevel@tonic-gate  * hubd_cfgadm_state:
75157c478bd9Sstevel@tonic-gate  *
75167c478bd9Sstevel@tonic-gate  *	child_dip list		port_state		cfgadm_state
75177c478bd9Sstevel@tonic-gate  *	--------------		----------		------------
75187c478bd9Sstevel@tonic-gate  *	!= NULL			connected		configured or
75197c478bd9Sstevel@tonic-gate  *							unconfigured
75207c478bd9Sstevel@tonic-gate  *	!= NULL			not connected		disconnect but
75217c478bd9Sstevel@tonic-gate  *							busy/still referenced
75227c478bd9Sstevel@tonic-gate  *	NULL			connected		logically disconnected
75237c478bd9Sstevel@tonic-gate  *	NULL			not connected		empty
75247c478bd9Sstevel@tonic-gate  */
75257c478bd9Sstevel@tonic-gate static uint_t
75267c478bd9Sstevel@tonic-gate hubd_cfgadm_state(hubd_t *hubd, usb_port_t port)
75277c478bd9Sstevel@tonic-gate {
75287c478bd9Sstevel@tonic-gate 	uint_t		state;
75297c478bd9Sstevel@tonic-gate 	dev_info_t	*child_dip = hubd_get_child_dip(hubd, port);
75307c478bd9Sstevel@tonic-gate 
75317c478bd9Sstevel@tonic-gate 	if (child_dip) {
75327c478bd9Sstevel@tonic-gate 		if (hubd->h_port_state[port] & PORT_STATUS_CCS) {
75337c478bd9Sstevel@tonic-gate 			/*
75347c478bd9Sstevel@tonic-gate 			 * connected,  now check if driver exists
75357c478bd9Sstevel@tonic-gate 			 */
75367c478bd9Sstevel@tonic-gate 			if (DEVI_IS_DEVICE_OFFLINE(child_dip) ||
75377c478bd9Sstevel@tonic-gate 			    (i_ddi_node_state(child_dip) < DS_ATTACHED)) {
75387c478bd9Sstevel@tonic-gate 				state = HUBD_CFGADM_UNCONFIGURED;
75397c478bd9Sstevel@tonic-gate 			} else {
75407c478bd9Sstevel@tonic-gate 				state = HUBD_CFGADM_CONFIGURED;
75417c478bd9Sstevel@tonic-gate 			}
75427c478bd9Sstevel@tonic-gate 		} else {
75437c478bd9Sstevel@tonic-gate 			/*
75447c478bd9Sstevel@tonic-gate 			 * this means that the dip is around for
75457c478bd9Sstevel@tonic-gate 			 * a device that is still referenced but
75467c478bd9Sstevel@tonic-gate 			 * has been yanked out. So the cfgadm info
75477c478bd9Sstevel@tonic-gate 			 * for this state should be EMPTY (port empty)
75487c478bd9Sstevel@tonic-gate 			 * and CONFIGURED (dip still valid).
75497c478bd9Sstevel@tonic-gate 			 */
75507c478bd9Sstevel@tonic-gate 			state = HUBD_CFGADM_STILL_REFERENCED;
75517c478bd9Sstevel@tonic-gate 		}
75527c478bd9Sstevel@tonic-gate 	} else {
75537c478bd9Sstevel@tonic-gate 		/* connected but no child dip */
75547c478bd9Sstevel@tonic-gate 		if (hubd->h_port_state[port] & PORT_STATUS_CCS) {
75557c478bd9Sstevel@tonic-gate 			/* logically disconnected */
75567c478bd9Sstevel@tonic-gate 			state = HUBD_CFGADM_DISCONNECTED;
75577c478bd9Sstevel@tonic-gate 		} else {
75587c478bd9Sstevel@tonic-gate 			/* physically disconnected */
75597c478bd9Sstevel@tonic-gate 			state = HUBD_CFGADM_EMPTY;
75607c478bd9Sstevel@tonic-gate 		}
75617c478bd9Sstevel@tonic-gate 	}
75627c478bd9Sstevel@tonic-gate 
75637c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_CBOPS,  hubd->h_log_handle,
75647c478bd9Sstevel@tonic-gate 	    "hubd_cfgadm_state: hubd=0x%p, port=%d state=0x%x",
75657c478bd9Sstevel@tonic-gate 	    hubd, port, state);
75667c478bd9Sstevel@tonic-gate 
75677c478bd9Sstevel@tonic-gate 	return (state);
75687c478bd9Sstevel@tonic-gate }
75697c478bd9Sstevel@tonic-gate 
75707c478bd9Sstevel@tonic-gate 
75717c478bd9Sstevel@tonic-gate /*
75727c478bd9Sstevel@tonic-gate  * hubd_toggle_port:
75737c478bd9Sstevel@tonic-gate  */
75747c478bd9Sstevel@tonic-gate static int
75757c478bd9Sstevel@tonic-gate hubd_toggle_port(hubd_t *hubd, usb_port_t port)
75767c478bd9Sstevel@tonic-gate {
75777c478bd9Sstevel@tonic-gate 	usb_hub_descr_t	*hub_descr;
75787c478bd9Sstevel@tonic-gate 	int		wait;
75797c478bd9Sstevel@tonic-gate 	uint_t		retry;
75807c478bd9Sstevel@tonic-gate 	uint16_t	status;
75817c478bd9Sstevel@tonic-gate 	uint16_t	change;
75827c478bd9Sstevel@tonic-gate 
75837c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_CBOPS,  hubd->h_log_handle,
75847c478bd9Sstevel@tonic-gate 	    "hubd_toggle_port: hubd=0x%p, port=%d", hubd, port);
75857c478bd9Sstevel@tonic-gate 
75867c478bd9Sstevel@tonic-gate 	if ((hubd_disable_port_power(hubd, port)) != USB_SUCCESS) {
75877c478bd9Sstevel@tonic-gate 
75887c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
75897c478bd9Sstevel@tonic-gate 	}
75907c478bd9Sstevel@tonic-gate 
75917c478bd9Sstevel@tonic-gate 	/*
75927c478bd9Sstevel@tonic-gate 	 * see hubd_enable_all_port_power() which
75937c478bd9Sstevel@tonic-gate 	 * requires longer delay for hubs.
75947c478bd9Sstevel@tonic-gate 	 */
75957c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
75967c478bd9Sstevel@tonic-gate 	delay(drv_usectohz(hubd_device_delay / 10));
75977c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
75987c478bd9Sstevel@tonic-gate 
75997c478bd9Sstevel@tonic-gate 	hub_descr = &hubd->h_hub_descr;
76007c478bd9Sstevel@tonic-gate 
76017c478bd9Sstevel@tonic-gate 	/*
76027c478bd9Sstevel@tonic-gate 	 * According to section 11.11 of USB, for hubs with no power
76037c478bd9Sstevel@tonic-gate 	 * switches, bPwrOn2PwrGood is zero. But we wait for some
76047c478bd9Sstevel@tonic-gate 	 * arbitrary time to enable power to become stable.
76057c478bd9Sstevel@tonic-gate 	 *
76067c478bd9Sstevel@tonic-gate 	 * If an hub supports port power swicthing, we need to wait
76077c478bd9Sstevel@tonic-gate 	 * at least 20ms before accesing corresonding usb port.
76087c478bd9Sstevel@tonic-gate 	 */
76097c478bd9Sstevel@tonic-gate 	if ((hub_descr->wHubCharacteristics &
76107c478bd9Sstevel@tonic-gate 	    HUB_CHARS_NO_POWER_SWITCHING) || (!hub_descr->bPwrOn2PwrGood)) {
76117c478bd9Sstevel@tonic-gate 		wait = hubd_device_delay / 10;
76127c478bd9Sstevel@tonic-gate 	} else {
76137c478bd9Sstevel@tonic-gate 		wait = max(HUB_DEFAULT_POPG,
76147c478bd9Sstevel@tonic-gate 		    hub_descr->bPwrOn2PwrGood) * 2 * 1000;
76157c478bd9Sstevel@tonic-gate 	}
76167c478bd9Sstevel@tonic-gate 
76177c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
76187c478bd9Sstevel@tonic-gate 	    "hubd_toggle_port: popg=%d wait=%d",
76197c478bd9Sstevel@tonic-gate 	    hub_descr->bPwrOn2PwrGood, wait);
76207c478bd9Sstevel@tonic-gate 
76217c478bd9Sstevel@tonic-gate 	retry = 0;
76227c478bd9Sstevel@tonic-gate 
76237c478bd9Sstevel@tonic-gate 	do {
76247c478bd9Sstevel@tonic-gate 		(void) hubd_enable_port_power(hubd, port);
76257c478bd9Sstevel@tonic-gate 
76267c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
76277c478bd9Sstevel@tonic-gate 		delay(drv_usectohz(wait));
76287c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
76297c478bd9Sstevel@tonic-gate 
76307c478bd9Sstevel@tonic-gate 		/* Get port status */
76317c478bd9Sstevel@tonic-gate 		(void) hubd_determine_port_status(hubd, port,
76327c478bd9Sstevel@tonic-gate 		    &status, &change, 0);
76337c478bd9Sstevel@tonic-gate 
76347c478bd9Sstevel@tonic-gate 		/* For retry if any, use some extra delay */
76357c478bd9Sstevel@tonic-gate 		wait = max(wait, hubd_device_delay / 10);
76367c478bd9Sstevel@tonic-gate 
76377c478bd9Sstevel@tonic-gate 		retry++;
76387c478bd9Sstevel@tonic-gate 
76397c478bd9Sstevel@tonic-gate 	} while ((!(status & PORT_STATUS_PPS)) && (retry < HUBD_PORT_RETRY));
76407c478bd9Sstevel@tonic-gate 
76417c478bd9Sstevel@tonic-gate 	/* Print warning message if port has no power */
76427c478bd9Sstevel@tonic-gate 	if (!(status & PORT_STATUS_PPS)) {
76437c478bd9Sstevel@tonic-gate 
7644*d291d9f2Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
76457c478bd9Sstevel@tonic-gate 		    "hubd_toggle_port: port %d power-on failed, "
76467c478bd9Sstevel@tonic-gate 		    "port status 0x%x", port, status);
76477c478bd9Sstevel@tonic-gate 
76487c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
76497c478bd9Sstevel@tonic-gate 	}
76507c478bd9Sstevel@tonic-gate 
76517c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
76527c478bd9Sstevel@tonic-gate }
7653