xref: /onnv-gate/usr/src/uts/common/io/usb/usba/hubdi.c (revision 12819:b9f8177eb4e2)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
52326Ssl147100  * Common Development and Distribution License (the "License").
62326Ssl147100  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
2212194SRaymond.Chen@Sun.COM  * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
230Sstevel@tonic-gate  */
240Sstevel@tonic-gate 
250Sstevel@tonic-gate /*
260Sstevel@tonic-gate  * USBA: Solaris USB Architecture support for the hub
270Sstevel@tonic-gate  * including root hub
280Sstevel@tonic-gate  * Most of the code for hubd resides in this file and
290Sstevel@tonic-gate  * is shared between the HCD root hub support and hubd
300Sstevel@tonic-gate  */
310Sstevel@tonic-gate #define	USBA_FRAMEWORK
320Sstevel@tonic-gate #include <sys/usb/usba.h>
330Sstevel@tonic-gate #include <sys/usb/usba/usba_devdb.h>
340Sstevel@tonic-gate #include <sys/sunndi.h>
350Sstevel@tonic-gate #include <sys/usb/usba/usba_impl.h>
360Sstevel@tonic-gate #include <sys/usb/usba/usba_types.h>
370Sstevel@tonic-gate #include <sys/usb/usba/hubdi.h>
380Sstevel@tonic-gate #include <sys/usb/usba/hcdi_impl.h>
390Sstevel@tonic-gate #include <sys/usb/hubd/hub.h>
400Sstevel@tonic-gate #include <sys/usb/hubd/hubdvar.h>
410Sstevel@tonic-gate #include <sys/usb/hubd/hubd_impl.h>
420Sstevel@tonic-gate #include <sys/kobj.h>
430Sstevel@tonic-gate #include <sys/kobj_lex.h>
440Sstevel@tonic-gate #include <sys/fs/dv_node.h>
457492SZhigang.Lu@Sun.COM #include <sys/strsun.h>
460Sstevel@tonic-gate 
470Sstevel@tonic-gate /*
4810783SVincent.Wang@Sun.COM  * External functions
4910783SVincent.Wang@Sun.COM  */
5010783SVincent.Wang@Sun.COM extern boolean_t consconfig_console_is_ready(void);
5110783SVincent.Wang@Sun.COM 
5210783SVincent.Wang@Sun.COM /*
530Sstevel@tonic-gate  * Prototypes for static functions
540Sstevel@tonic-gate  */
550Sstevel@tonic-gate static	int	usba_hubdi_bus_ctl(
560Sstevel@tonic-gate 			dev_info_t		*dip,
570Sstevel@tonic-gate 			dev_info_t		*rdip,
580Sstevel@tonic-gate 			ddi_ctl_enum_t		op,
590Sstevel@tonic-gate 			void			*arg,
600Sstevel@tonic-gate 			void			*result);
610Sstevel@tonic-gate 
620Sstevel@tonic-gate static int	usba_hubdi_map_fault(
630Sstevel@tonic-gate 			dev_info_t		*dip,
640Sstevel@tonic-gate 			dev_info_t		*rdip,
650Sstevel@tonic-gate 			struct hat		*hat,
660Sstevel@tonic-gate 			struct seg		*seg,
670Sstevel@tonic-gate 			caddr_t 		addr,
680Sstevel@tonic-gate 			struct devpage		*dp,
690Sstevel@tonic-gate 			pfn_t			pfn,
700Sstevel@tonic-gate 			uint_t			prot,
710Sstevel@tonic-gate 			uint_t			lock);
720Sstevel@tonic-gate 
730Sstevel@tonic-gate static int hubd_busop_get_eventcookie(dev_info_t *dip,
740Sstevel@tonic-gate 			dev_info_t *rdip,
750Sstevel@tonic-gate 			char *eventname,
760Sstevel@tonic-gate 			ddi_eventcookie_t *cookie);
770Sstevel@tonic-gate static int hubd_busop_add_eventcall(dev_info_t *dip,
780Sstevel@tonic-gate 			dev_info_t *rdip,
790Sstevel@tonic-gate 			ddi_eventcookie_t cookie,
800Sstevel@tonic-gate 			void (*callback)(dev_info_t *dip,
810Sstevel@tonic-gate 				ddi_eventcookie_t cookie, void *arg,
820Sstevel@tonic-gate 				void *bus_impldata),
830Sstevel@tonic-gate 			void *arg, ddi_callback_id_t *cb_id);
840Sstevel@tonic-gate static int hubd_busop_remove_eventcall(dev_info_t *dip,
850Sstevel@tonic-gate 			ddi_callback_id_t cb_id);
860Sstevel@tonic-gate static int hubd_bus_config(dev_info_t *dip,
870Sstevel@tonic-gate 			uint_t flag,
880Sstevel@tonic-gate 			ddi_bus_config_op_t op,
890Sstevel@tonic-gate 			void *arg,
900Sstevel@tonic-gate 			dev_info_t **child);
910Sstevel@tonic-gate static int hubd_bus_unconfig(dev_info_t *dip,
920Sstevel@tonic-gate 			uint_t flag,
930Sstevel@tonic-gate 			ddi_bus_config_op_t op,
940Sstevel@tonic-gate 			void *arg);
950Sstevel@tonic-gate static int hubd_bus_power(dev_info_t *dip, void *impl_arg,
960Sstevel@tonic-gate 			pm_bus_power_op_t op, void *arg, void *result);
970Sstevel@tonic-gate 
980Sstevel@tonic-gate static usb_port_t  hubd_get_port_num(hubd_t *, struct devctl_iocdata *);
990Sstevel@tonic-gate static dev_info_t *hubd_get_child_dip(hubd_t *, usb_port_t);
1000Sstevel@tonic-gate static uint_t hubd_cfgadm_state(hubd_t *, usb_port_t);
1010Sstevel@tonic-gate static int hubd_toggle_port(hubd_t *, usb_port_t);
1020Sstevel@tonic-gate static void hubd_register_cpr_callback(hubd_t *);
1030Sstevel@tonic-gate static void hubd_unregister_cpr_callback(hubd_t *);
1040Sstevel@tonic-gate 
1050Sstevel@tonic-gate /*
1060Sstevel@tonic-gate  * Busops vector for USB HUB's
1070Sstevel@tonic-gate  */
1080Sstevel@tonic-gate struct bus_ops usba_hubdi_busops =	{
1090Sstevel@tonic-gate 	BUSO_REV,
1100Sstevel@tonic-gate 	nullbusmap,			/* bus_map */
1110Sstevel@tonic-gate 	NULL,				/* bus_get_intrspec */
1120Sstevel@tonic-gate 	NULL,				/* bus_add_intrspec */
1130Sstevel@tonic-gate 	NULL,				/* bus_remove_intrspec */
1140Sstevel@tonic-gate 	usba_hubdi_map_fault,		/* bus_map_fault */
1150Sstevel@tonic-gate 	ddi_dma_map,			/* bus_dma_map */
1160Sstevel@tonic-gate 	ddi_dma_allochdl,
1170Sstevel@tonic-gate 	ddi_dma_freehdl,
1180Sstevel@tonic-gate 	ddi_dma_bindhdl,
1190Sstevel@tonic-gate 	ddi_dma_unbindhdl,
1200Sstevel@tonic-gate 	ddi_dma_flush,
1210Sstevel@tonic-gate 	ddi_dma_win,
1220Sstevel@tonic-gate 	ddi_dma_mctl,			/* bus_dma_ctl */
1230Sstevel@tonic-gate 	usba_hubdi_bus_ctl,		/* bus_ctl */
1240Sstevel@tonic-gate 	ddi_bus_prop_op,		/* bus_prop_op */
1250Sstevel@tonic-gate 	hubd_busop_get_eventcookie,
1260Sstevel@tonic-gate 	hubd_busop_add_eventcall,
1270Sstevel@tonic-gate 	hubd_busop_remove_eventcall,
1280Sstevel@tonic-gate 	NULL,				/* bus_post_event */
1290Sstevel@tonic-gate 	NULL,				/* bus_intr_ctl */
1300Sstevel@tonic-gate 	hubd_bus_config,		/* bus_config */
1310Sstevel@tonic-gate 	hubd_bus_unconfig,		/* bus_unconfig */
1320Sstevel@tonic-gate 	NULL,				/* bus_fm_init */
1330Sstevel@tonic-gate 	NULL,				/* bus_fm_fini */
1340Sstevel@tonic-gate 	NULL,				/* bus_fm_access_enter */
1350Sstevel@tonic-gate 	NULL,				/* bus_fm_access_exit */
1360Sstevel@tonic-gate 	hubd_bus_power			/* bus_power */
1370Sstevel@tonic-gate };
1380Sstevel@tonic-gate 
13912194SRaymond.Chen@Sun.COM #define	USB_HUB_INTEL_VID	0x8087
14012194SRaymond.Chen@Sun.COM #define	USB_HUB_INTEL_PID	0x0020
1410Sstevel@tonic-gate 
1420Sstevel@tonic-gate /*
1430Sstevel@tonic-gate  * local variables
1440Sstevel@tonic-gate  */
1450Sstevel@tonic-gate static kmutex_t	usba_hubdi_mutex;	/* protects USBA HUB data structures */
1460Sstevel@tonic-gate 
1470Sstevel@tonic-gate static usba_list_entry_t	usba_hubdi_list;
1480Sstevel@tonic-gate 
1490Sstevel@tonic-gate usb_log_handle_t	hubdi_log_handle;
1500Sstevel@tonic-gate uint_t			hubdi_errlevel = USB_LOG_L4;
1510Sstevel@tonic-gate uint_t			hubdi_errmask = (uint_t)-1;
1527492SZhigang.Lu@Sun.COM uint8_t			hubdi_min_pm_threshold = 5; /* seconds */
15310468SRaymond.Chen@Sun.COM uint8_t			hubdi_reset_delay = 20; /* seconds */
15411987SFei.Feng@Sun.COM extern int modrootloaded;
1550Sstevel@tonic-gate 
1560Sstevel@tonic-gate /*
1570Sstevel@tonic-gate  * initialize private data
1580Sstevel@tonic-gate  */
1590Sstevel@tonic-gate void
usba_hubdi_initialization()1600Sstevel@tonic-gate usba_hubdi_initialization()
1610Sstevel@tonic-gate {
1620Sstevel@tonic-gate 	hubdi_log_handle = usb_alloc_log_hdl(NULL, "hubdi", &hubdi_errlevel,
1634763Slg150142 	    &hubdi_errmask, NULL, 0);
1640Sstevel@tonic-gate 
1650Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle,
1660Sstevel@tonic-gate 	    "usba_hubdi_initialization");
1670Sstevel@tonic-gate 
1680Sstevel@tonic-gate 	mutex_init(&usba_hubdi_mutex, NULL, MUTEX_DRIVER, NULL);
1690Sstevel@tonic-gate 
1700Sstevel@tonic-gate 	usba_init_list(&usba_hubdi_list, NULL, NULL);
1710Sstevel@tonic-gate }
1720Sstevel@tonic-gate 
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate void
usba_hubdi_destroy()1750Sstevel@tonic-gate usba_hubdi_destroy()
1760Sstevel@tonic-gate {
1770Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle,
1780Sstevel@tonic-gate 	    "usba_hubdi_destroy");
1790Sstevel@tonic-gate 
1800Sstevel@tonic-gate 	mutex_destroy(&usba_hubdi_mutex);
1810Sstevel@tonic-gate 	usba_destroy_list(&usba_hubdi_list);
1820Sstevel@tonic-gate 
1830Sstevel@tonic-gate 	usb_free_log_hdl(hubdi_log_handle);
1840Sstevel@tonic-gate }
1850Sstevel@tonic-gate 
1860Sstevel@tonic-gate 
1870Sstevel@tonic-gate /*
1880Sstevel@tonic-gate  * Called by an	HUB to attach an instance of the driver
1890Sstevel@tonic-gate  *	make this instance known to USBA
1900Sstevel@tonic-gate  *	the HUB	should initialize usba_hubdi structure prior
1910Sstevel@tonic-gate  *	to calling this	interface
1920Sstevel@tonic-gate  */
1939430SRaymond.Chen@Sun.COM int
usba_hubdi_register(dev_info_t * dip,uint_t flags)1940Sstevel@tonic-gate usba_hubdi_register(dev_info_t	*dip,
1950Sstevel@tonic-gate 		uint_t		flags)
1960Sstevel@tonic-gate {
1970Sstevel@tonic-gate 	usba_hubdi_t *hubdi = kmem_zalloc(sizeof (usba_hubdi_t), KM_SLEEP);
1980Sstevel@tonic-gate 	usba_device_t *usba_device = usba_get_usba_device(dip);
1990Sstevel@tonic-gate 
2000Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle,
2010Sstevel@tonic-gate 	    "usba_hubdi_register: %s", ddi_node_name(dip));
2020Sstevel@tonic-gate 
2030Sstevel@tonic-gate 	hubdi->hubdi_dip = dip;
2040Sstevel@tonic-gate 	hubdi->hubdi_flags = flags;
2050Sstevel@tonic-gate 
2060Sstevel@tonic-gate 	usba_device->usb_hubdi = hubdi;
2070Sstevel@tonic-gate 
2080Sstevel@tonic-gate 	/*
2090Sstevel@tonic-gate 	 * add this hubdi instance to the list of known hubdi's
2100Sstevel@tonic-gate 	 */
2110Sstevel@tonic-gate 	usba_init_list(&hubdi->hubdi_list, (usb_opaque_t)hubdi,
2120Sstevel@tonic-gate 	    usba_hcdi_get_hcdi(usba_device->usb_root_hub_dip)->
2130Sstevel@tonic-gate 	    hcdi_iblock_cookie);
2140Sstevel@tonic-gate 	mutex_enter(&usba_hubdi_mutex);
2150Sstevel@tonic-gate 	usba_add_to_list(&usba_hubdi_list, &hubdi->hubdi_list);
2160Sstevel@tonic-gate 	mutex_exit(&usba_hubdi_mutex);
2170Sstevel@tonic-gate 
2180Sstevel@tonic-gate 	return (DDI_SUCCESS);
2190Sstevel@tonic-gate }
2200Sstevel@tonic-gate 
2210Sstevel@tonic-gate 
2220Sstevel@tonic-gate /*
2230Sstevel@tonic-gate  * Called by an	HUB to detach an instance of the driver
2240Sstevel@tonic-gate  */
2259430SRaymond.Chen@Sun.COM int
usba_hubdi_unregister(dev_info_t * dip)2260Sstevel@tonic-gate usba_hubdi_unregister(dev_info_t *dip)
2270Sstevel@tonic-gate {
2280Sstevel@tonic-gate 	usba_device_t *usba_device = usba_get_usba_device(dip);
2290Sstevel@tonic-gate 	usba_hubdi_t *hubdi = usba_device->usb_hubdi;
2300Sstevel@tonic-gate 
2310Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle,
2320Sstevel@tonic-gate 	    "usba_hubdi_unregister: %s", ddi_node_name(dip));
2330Sstevel@tonic-gate 
2340Sstevel@tonic-gate 	mutex_enter(&usba_hubdi_mutex);
2350Sstevel@tonic-gate 	(void) usba_rm_from_list(&usba_hubdi_list, &hubdi->hubdi_list);
2360Sstevel@tonic-gate 	mutex_exit(&usba_hubdi_mutex);
2370Sstevel@tonic-gate 
2380Sstevel@tonic-gate 	usba_destroy_list(&hubdi->hubdi_list);
2390Sstevel@tonic-gate 
2400Sstevel@tonic-gate 	kmem_free(hubdi, sizeof (usba_hubdi_t));
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate 	return (DDI_SUCCESS);
2430Sstevel@tonic-gate }
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate 
2460Sstevel@tonic-gate /*
2470Sstevel@tonic-gate  * misc bus routines currently not used
2480Sstevel@tonic-gate  */
2490Sstevel@tonic-gate /*ARGSUSED*/
2500Sstevel@tonic-gate static int
usba_hubdi_map_fault(dev_info_t * dip,dev_info_t * rdip,struct hat * hat,struct seg * seg,caddr_t addr,struct devpage * dp,pfn_t pfn,uint_t prot,uint_t lock)2510Sstevel@tonic-gate usba_hubdi_map_fault(dev_info_t *dip,
2520Sstevel@tonic-gate 	dev_info_t	*rdip,
2530Sstevel@tonic-gate 	struct hat	*hat,
2540Sstevel@tonic-gate 	struct seg	*seg,
2550Sstevel@tonic-gate 	caddr_t 	addr,
2560Sstevel@tonic-gate 	struct devpage	*dp,
2570Sstevel@tonic-gate 	pfn_t		pfn,
2580Sstevel@tonic-gate 	uint_t		prot,
2590Sstevel@tonic-gate 	uint_t		lock)
2600Sstevel@tonic-gate {
2610Sstevel@tonic-gate 	return (DDI_FAILURE);
2620Sstevel@tonic-gate }
2630Sstevel@tonic-gate 
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate /*
2660Sstevel@tonic-gate  * root hub support. the root hub uses the same devi as the HCD
2670Sstevel@tonic-gate  */
2680Sstevel@tonic-gate int
usba_hubdi_bind_root_hub(dev_info_t * dip,uchar_t * root_hub_config_descriptor,size_t config_length,usb_dev_descr_t * root_hub_device_descriptor)2690Sstevel@tonic-gate usba_hubdi_bind_root_hub(dev_info_t *dip,
2700Sstevel@tonic-gate 	uchar_t	*root_hub_config_descriptor,
2710Sstevel@tonic-gate 	size_t config_length,
2720Sstevel@tonic-gate 	usb_dev_descr_t *root_hub_device_descriptor)
2730Sstevel@tonic-gate {
2740Sstevel@tonic-gate 	usba_device_t *usba_device;
2750Sstevel@tonic-gate 	usba_hcdi_t *hcdi = usba_hcdi_get_hcdi(dip);
2760Sstevel@tonic-gate 	hubd_t	*root_hubd;
2770Sstevel@tonic-gate 	usb_pipe_handle_t ph = NULL;
2780Sstevel@tonic-gate 	dev_info_t *child = ddi_get_child(dip);
2790Sstevel@tonic-gate 
2800Sstevel@tonic-gate 	if (ndi_prop_create_boolean(DDI_DEV_T_NONE, dip,
2810Sstevel@tonic-gate 	    "root-hub") != NDI_SUCCESS) {
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate 		return (USB_FAILURE);
2840Sstevel@tonic-gate 	}
2850Sstevel@tonic-gate 
2869094SVincent.Wang@Sun.COM 	usba_add_root_hub(dip);
2879094SVincent.Wang@Sun.COM 
2880Sstevel@tonic-gate 	root_hubd = kmem_zalloc(sizeof (hubd_t), KM_SLEEP);
2890Sstevel@tonic-gate 
2900Sstevel@tonic-gate 	/*
2910Sstevel@tonic-gate 	 * create and initialize a usba_device structure
2920Sstevel@tonic-gate 	 */
2930Sstevel@tonic-gate 	usba_device = usba_alloc_usba_device(dip);
2940Sstevel@tonic-gate 
2950Sstevel@tonic-gate 	mutex_enter(&usba_device->usb_mutex);
2960Sstevel@tonic-gate 	usba_device->usb_hcdi_ops = hcdi->hcdi_ops;
2970Sstevel@tonic-gate 	usba_device->usb_cfg = root_hub_config_descriptor;
2980Sstevel@tonic-gate 	usba_device->usb_cfg_length = config_length;
2990Sstevel@tonic-gate 	usba_device->usb_dev_descr = root_hub_device_descriptor;
3000Sstevel@tonic-gate 	usba_device->usb_port = 1;
3010Sstevel@tonic-gate 	usba_device->usb_addr = ROOT_HUB_ADDR;
3020Sstevel@tonic-gate 	usba_device->usb_root_hubd = root_hubd;
3030Sstevel@tonic-gate 	usba_device->usb_cfg_array = kmem_zalloc(sizeof (uchar_t *),
3044763Slg150142 	    KM_SLEEP);
3050Sstevel@tonic-gate 	usba_device->usb_cfg_array_length = sizeof (uchar_t *);
3060Sstevel@tonic-gate 
3070Sstevel@tonic-gate 	usba_device->usb_cfg_array_len = kmem_zalloc(sizeof (uint16_t),
3084763Slg150142 	    KM_SLEEP);
3090Sstevel@tonic-gate 	usba_device->usb_cfg_array_len_length = sizeof (uint16_t);
3100Sstevel@tonic-gate 
3110Sstevel@tonic-gate 	usba_device->usb_cfg_array[0] = root_hub_config_descriptor;
3120Sstevel@tonic-gate 	usba_device->usb_cfg_array_len[0] =
3134763Slg150142 	    sizeof (root_hub_config_descriptor);
3140Sstevel@tonic-gate 
3150Sstevel@tonic-gate 	usba_device->usb_cfg_str_descr = kmem_zalloc(sizeof (uchar_t *),
3164763Slg150142 	    KM_SLEEP);
3170Sstevel@tonic-gate 	usba_device->usb_n_cfgs = 1;
3180Sstevel@tonic-gate 	usba_device->usb_n_ifs = 1;
3190Sstevel@tonic-gate 	usba_device->usb_dip = dip;
3200Sstevel@tonic-gate 
3210Sstevel@tonic-gate 	usba_device->usb_client_flags = kmem_zalloc(
3220Sstevel@tonic-gate 	    usba_device->usb_n_ifs * USBA_CLIENT_FLAG_SIZE, KM_SLEEP);
3230Sstevel@tonic-gate 
3240Sstevel@tonic-gate 	usba_device->usb_client_attach_list = kmem_zalloc(
3250Sstevel@tonic-gate 	    usba_device->usb_n_ifs *
3260Sstevel@tonic-gate 	    sizeof (*usba_device->usb_client_attach_list), KM_SLEEP);
3270Sstevel@tonic-gate 
3280Sstevel@tonic-gate 	usba_device->usb_client_ev_cb_list = kmem_zalloc(
3290Sstevel@tonic-gate 	    usba_device->usb_n_ifs *
3300Sstevel@tonic-gate 	    sizeof (*usba_device->usb_client_ev_cb_list), KM_SLEEP);
3310Sstevel@tonic-gate 
3320Sstevel@tonic-gate 	/*
3330Sstevel@tonic-gate 	 * The bDeviceProtocol field of root hub device specifies,
3340Sstevel@tonic-gate 	 * whether root hub is a High or Full speed usb device.
3350Sstevel@tonic-gate 	 */
3360Sstevel@tonic-gate 	if (root_hub_device_descriptor->bDeviceProtocol) {
3370Sstevel@tonic-gate 		usba_device->usb_port_status = USBA_HIGH_SPEED_DEV;
3380Sstevel@tonic-gate 	} else {
3390Sstevel@tonic-gate 		usba_device->usb_port_status = USBA_FULL_SPEED_DEV;
3400Sstevel@tonic-gate 	}
3410Sstevel@tonic-gate 
3420Sstevel@tonic-gate 	mutex_exit(&usba_device->usb_mutex);
3430Sstevel@tonic-gate 
3440Sstevel@tonic-gate 	usba_set_usba_device(dip, usba_device);
3450Sstevel@tonic-gate 
3460Sstevel@tonic-gate 	/*
3470Sstevel@tonic-gate 	 * For the root hub the default pipe is not yet open
3480Sstevel@tonic-gate 	 */
3490Sstevel@tonic-gate 	if (usb_pipe_open(dip, NULL, NULL,
3500Sstevel@tonic-gate 	    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph) != USB_SUCCESS) {
3510Sstevel@tonic-gate 		goto fail;
3520Sstevel@tonic-gate 	}
3530Sstevel@tonic-gate 
3540Sstevel@tonic-gate 	/*
3550Sstevel@tonic-gate 	 * kill off all OBP children, they may not be fully
3560Sstevel@tonic-gate 	 * enumerated
3570Sstevel@tonic-gate 	 */
3580Sstevel@tonic-gate 	while (child) {
3590Sstevel@tonic-gate 		dev_info_t *next = ddi_get_next_sibling(child);
3600Sstevel@tonic-gate 		(void) ddi_remove_child(child, 0);
3610Sstevel@tonic-gate 		child = next;
3620Sstevel@tonic-gate 	}
3630Sstevel@tonic-gate 
3640Sstevel@tonic-gate 	/*
3650Sstevel@tonic-gate 	 * "attach" the root hub driver
3660Sstevel@tonic-gate 	 */
3670Sstevel@tonic-gate 	if (usba_hubdi_attach(dip, DDI_ATTACH) != DDI_SUCCESS) {
3680Sstevel@tonic-gate 		goto fail;
3690Sstevel@tonic-gate 	}
3700Sstevel@tonic-gate 
3710Sstevel@tonic-gate 	return (USB_SUCCESS);
3720Sstevel@tonic-gate 
3730Sstevel@tonic-gate fail:
3740Sstevel@tonic-gate 	(void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "root-hub");
3750Sstevel@tonic-gate 
3769094SVincent.Wang@Sun.COM 	usba_rem_root_hub(dip);
3779094SVincent.Wang@Sun.COM 
3780Sstevel@tonic-gate 	if (ph) {
3790Sstevel@tonic-gate 		usb_pipe_close(dip, ph,
3800Sstevel@tonic-gate 		    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
3810Sstevel@tonic-gate 	}
3820Sstevel@tonic-gate 
3830Sstevel@tonic-gate 	kmem_free(usba_device->usb_cfg_array,
3844763Slg150142 	    usba_device->usb_cfg_array_length);
3850Sstevel@tonic-gate 	kmem_free(usba_device->usb_cfg_array_len,
3864763Slg150142 	    usba_device->usb_cfg_array_len_length);
3870Sstevel@tonic-gate 
3880Sstevel@tonic-gate 	kmem_free(usba_device->usb_cfg_str_descr, sizeof (uchar_t *));
3890Sstevel@tonic-gate 
3900Sstevel@tonic-gate 	usba_free_usba_device(usba_device);
3910Sstevel@tonic-gate 
3920Sstevel@tonic-gate 	usba_set_usba_device(dip, NULL);
3930Sstevel@tonic-gate 	if (root_hubd) {
3940Sstevel@tonic-gate 		kmem_free(root_hubd, sizeof (hubd_t));
3950Sstevel@tonic-gate 	}
3960Sstevel@tonic-gate 
3970Sstevel@tonic-gate 	return (USB_FAILURE);
3980Sstevel@tonic-gate }
3990Sstevel@tonic-gate 
4000Sstevel@tonic-gate 
4010Sstevel@tonic-gate int
usba_hubdi_unbind_root_hub(dev_info_t * dip)4020Sstevel@tonic-gate usba_hubdi_unbind_root_hub(dev_info_t *dip)
4030Sstevel@tonic-gate {
4040Sstevel@tonic-gate 	usba_device_t *usba_device;
4050Sstevel@tonic-gate 
4060Sstevel@tonic-gate 	/* was root hub attached? */
4070Sstevel@tonic-gate 	if (!(usba_is_root_hub(dip))) {
4080Sstevel@tonic-gate 
4090Sstevel@tonic-gate 		/* return success anyway */
4100Sstevel@tonic-gate 		return (USB_SUCCESS);
4110Sstevel@tonic-gate 	}
4120Sstevel@tonic-gate 
4130Sstevel@tonic-gate 	/*
4140Sstevel@tonic-gate 	 * usba_hubdi_detach also closes the default pipe
4150Sstevel@tonic-gate 	 * and removes properties so there is no need to
4160Sstevel@tonic-gate 	 * do it here
4170Sstevel@tonic-gate 	 */
4180Sstevel@tonic-gate 	if (usba_hubdi_detach(dip, DDI_DETACH) != DDI_SUCCESS) {
4190Sstevel@tonic-gate 
4200Sstevel@tonic-gate 		if (DEVI_IS_ATTACHING(dip)) {
421978Sfrits 			USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
4220Sstevel@tonic-gate 			    "failure to unbind root hub after attach failure");
4230Sstevel@tonic-gate 		}
4240Sstevel@tonic-gate 
4250Sstevel@tonic-gate 		return (USB_FAILURE);
4260Sstevel@tonic-gate 	}
4270Sstevel@tonic-gate 
4280Sstevel@tonic-gate 	usba_device = usba_get_usba_device(dip);
4290Sstevel@tonic-gate 
4300Sstevel@tonic-gate 	kmem_free(usba_device->usb_root_hubd, sizeof (hubd_t));
4310Sstevel@tonic-gate 
4320Sstevel@tonic-gate 	kmem_free(usba_device->usb_cfg_array,
4334763Slg150142 	    usba_device->usb_cfg_array_length);
4340Sstevel@tonic-gate 	kmem_free(usba_device->usb_cfg_array_len,
4354763Slg150142 	    usba_device->usb_cfg_array_len_length);
4360Sstevel@tonic-gate 
4370Sstevel@tonic-gate 	kmem_free(usba_device->usb_cfg_str_descr, sizeof (uchar_t *));
4380Sstevel@tonic-gate 
4390Sstevel@tonic-gate 	usba_free_usba_device(usba_device);
4400Sstevel@tonic-gate 
4419094SVincent.Wang@Sun.COM 	usba_rem_root_hub(dip);
4429094SVincent.Wang@Sun.COM 
4430Sstevel@tonic-gate 	(void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "root-hub");
4440Sstevel@tonic-gate 
4450Sstevel@tonic-gate 	return (USB_SUCCESS);
4460Sstevel@tonic-gate }
4470Sstevel@tonic-gate 
4480Sstevel@tonic-gate 
4490Sstevel@tonic-gate /*
4500Sstevel@tonic-gate  * Actual Hub Driver support code:
4510Sstevel@tonic-gate  *	shared by root hub and non-root hubs
4520Sstevel@tonic-gate  */
4530Sstevel@tonic-gate #include <sys/usb/usba/usbai_version.h>
4540Sstevel@tonic-gate 
4550Sstevel@tonic-gate /* Debugging support */
4569430SRaymond.Chen@Sun.COM uint_t hubd_errlevel	= USB_LOG_L4;
4579430SRaymond.Chen@Sun.COM uint_t hubd_errmask	= (uint_t)DPRINT_MASK_ALL;
4589430SRaymond.Chen@Sun.COM uint_t hubd_instance_debug = (uint_t)-1;
4590Sstevel@tonic-gate static uint_t hubdi_bus_config_debug = 0;
4600Sstevel@tonic-gate 
4610Sstevel@tonic-gate _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_errlevel))
4620Sstevel@tonic-gate _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_errmask))
4630Sstevel@tonic-gate _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_instance_debug))
4640Sstevel@tonic-gate 
4650Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique", msgb))
4660Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique", dev_info))
4670Sstevel@tonic-gate 
4680Sstevel@tonic-gate 
4690Sstevel@tonic-gate /*
4700Sstevel@tonic-gate  * local variables:
4710Sstevel@tonic-gate  *
4720Sstevel@tonic-gate  * Amount of time to wait between resetting the port and accessing
4730Sstevel@tonic-gate  * the device.	The value is in microseconds.
4740Sstevel@tonic-gate  */
4750Sstevel@tonic-gate static uint_t hubd_device_delay = 1000000;
4760Sstevel@tonic-gate 
4770Sstevel@tonic-gate /*
4780Sstevel@tonic-gate  * enumeration retry
4790Sstevel@tonic-gate  */
4800Sstevel@tonic-gate #define	HUBD_PORT_RETRY 5
4810Sstevel@tonic-gate static uint_t hubd_retry_enumerate = HUBD_PORT_RETRY;
4820Sstevel@tonic-gate 
4830Sstevel@tonic-gate /*
4840Sstevel@tonic-gate  * Stale hotremoved device cleanup delay
4850Sstevel@tonic-gate  */
4860Sstevel@tonic-gate #define	HUBD_STALE_DIP_CLEANUP_DELAY	5000000
4870Sstevel@tonic-gate static uint_t hubd_dip_cleanup_delay = HUBD_STALE_DIP_CLEANUP_DELAY;
4880Sstevel@tonic-gate 
4890Sstevel@tonic-gate /*
4900Sstevel@tonic-gate  * retries for USB suspend and resume
4910Sstevel@tonic-gate  */
4920Sstevel@tonic-gate #define	HUBD_SUS_RES_RETRY	2
4930Sstevel@tonic-gate 
4940Sstevel@tonic-gate void	*hubd_statep;
4950Sstevel@tonic-gate 
4960Sstevel@tonic-gate /*
4970Sstevel@tonic-gate  * prototypes
4980Sstevel@tonic-gate  */
4990Sstevel@tonic-gate static int hubd_cleanup(dev_info_t *dip, hubd_t  *hubd);
5000Sstevel@tonic-gate static int hubd_check_ports(hubd_t  *hubd);
5010Sstevel@tonic-gate 
5020Sstevel@tonic-gate static int  hubd_open_intr_pipe(hubd_t *hubd);
5030Sstevel@tonic-gate static void hubd_start_polling(hubd_t *hubd, int always);
5040Sstevel@tonic-gate static void hubd_stop_polling(hubd_t *hubd);
5050Sstevel@tonic-gate static void hubd_close_intr_pipe(hubd_t *hubd);
5060Sstevel@tonic-gate 
5070Sstevel@tonic-gate static void hubd_read_cb(usb_pipe_handle_t pipe, usb_intr_req_t *req);
5080Sstevel@tonic-gate static void hubd_exception_cb(usb_pipe_handle_t pipe,
5090Sstevel@tonic-gate 						usb_intr_req_t *req);
5100Sstevel@tonic-gate static void hubd_hotplug_thread(void *arg);
5114844Slg150142 static void hubd_reset_thread(void *arg);
5120Sstevel@tonic-gate static int hubd_create_child(dev_info_t *dip,
5130Sstevel@tonic-gate 		hubd_t		*hubd,
5140Sstevel@tonic-gate 		usba_device_t	*usba_device,
5150Sstevel@tonic-gate 		usb_port_status_t port_status,
5160Sstevel@tonic-gate 		usb_port_t	port,
5170Sstevel@tonic-gate 		int		iteration);
5180Sstevel@tonic-gate 
5190Sstevel@tonic-gate static int hubd_delete_child(hubd_t *hubd, usb_port_t port, uint_t flag,
5200Sstevel@tonic-gate 	boolean_t retry);
5210Sstevel@tonic-gate 
5220Sstevel@tonic-gate static int hubd_get_hub_descriptor(hubd_t *hubd);
5230Sstevel@tonic-gate 
5241001Ssl147100 static int hubd_get_hub_status_words(hubd_t *hubd, uint16_t *status);
5251001Ssl147100 
5260Sstevel@tonic-gate static int hubd_reset_port(hubd_t *hubd, usb_port_t port);
5270Sstevel@tonic-gate 
5280Sstevel@tonic-gate static int hubd_get_hub_status(hubd_t *hubd);
5290Sstevel@tonic-gate 
5300Sstevel@tonic-gate static int hubd_handle_port_connect(hubd_t *hubd, usb_port_t port);
5310Sstevel@tonic-gate 
5320Sstevel@tonic-gate static int hubd_disable_port(hubd_t *hubd, usb_port_t port);
5330Sstevel@tonic-gate 
5340Sstevel@tonic-gate static int hubd_enable_port(hubd_t *hubd, usb_port_t port);
5350Sstevel@tonic-gate static int hubd_recover_disabled_port(hubd_t *hubd, usb_port_t port);
5360Sstevel@tonic-gate 
5370Sstevel@tonic-gate static int hubd_determine_port_status(hubd_t *hubd, usb_port_t port,
5380Sstevel@tonic-gate 	uint16_t *status, uint16_t *change, uint_t ack_flag);
5390Sstevel@tonic-gate 
5400Sstevel@tonic-gate static int hubd_enable_all_port_power(hubd_t *hubd);
5410Sstevel@tonic-gate static int hubd_disable_all_port_power(hubd_t *hubd);
5420Sstevel@tonic-gate static int hubd_disable_port_power(hubd_t *hubd, usb_port_t port);
5430Sstevel@tonic-gate static int hubd_enable_port_power(hubd_t *hubd, usb_port_t port);
5440Sstevel@tonic-gate 
5450Sstevel@tonic-gate static void hubd_free_usba_device(hubd_t *hubd, usba_device_t *usba_device);
5460Sstevel@tonic-gate 
5470Sstevel@tonic-gate static int hubd_can_suspend(hubd_t *hubd);
5480Sstevel@tonic-gate static void hubd_restore_device_state(dev_info_t *dip, hubd_t *hubd);
5490Sstevel@tonic-gate static int hubd_setdevaddr(hubd_t *hubd, usb_port_t port);
5500Sstevel@tonic-gate static void hubd_setdevconfig(hubd_t *hubd, usb_port_t port);
5510Sstevel@tonic-gate 
5520Sstevel@tonic-gate static int hubd_register_events(hubd_t *hubd);
5530Sstevel@tonic-gate static void hubd_do_callback(hubd_t *hubd, dev_info_t *dip,
5540Sstevel@tonic-gate 	ddi_eventcookie_t cookie);
5550Sstevel@tonic-gate static void hubd_run_callbacks(hubd_t *hubd, usba_event_t type);
5560Sstevel@tonic-gate static void hubd_post_event(hubd_t *hubd, usb_port_t port, usba_event_t type);
5570Sstevel@tonic-gate static void hubd_create_pm_components(dev_info_t *dip, hubd_t *hubd);
5580Sstevel@tonic-gate 
5590Sstevel@tonic-gate static int hubd_disconnect_event_cb(dev_info_t *dip);
5600Sstevel@tonic-gate static int hubd_reconnect_event_cb(dev_info_t *dip);
5610Sstevel@tonic-gate static int hubd_pre_suspend_event_cb(dev_info_t *dip);
5620Sstevel@tonic-gate static int hubd_post_resume_event_cb(dev_info_t *dip);
5630Sstevel@tonic-gate static int hubd_cpr_suspend(hubd_t *hubd);
5640Sstevel@tonic-gate static void hubd_cpr_resume(dev_info_t *dip);
5650Sstevel@tonic-gate static int hubd_restore_state_cb(dev_info_t *dip);
5664844Slg150142 static int hubd_check_same_device(hubd_t *hubd, usb_port_t port);
5670Sstevel@tonic-gate 
5681001Ssl147100 static int hubd_init_power_budget(hubd_t *hubd);
5691001Ssl147100 
5700Sstevel@tonic-gate static ndi_event_definition_t hubd_ndi_event_defs[] = {
5710Sstevel@tonic-gate 	{USBA_EVENT_TAG_HOT_REMOVAL, DDI_DEVI_REMOVE_EVENT, EPL_KERNEL,
5720Sstevel@tonic-gate 						NDI_EVENT_POST_TO_ALL},
5730Sstevel@tonic-gate 	{USBA_EVENT_TAG_HOT_INSERTION, DDI_DEVI_INSERT_EVENT, EPL_KERNEL,
5740Sstevel@tonic-gate 						NDI_EVENT_POST_TO_ALL},
5750Sstevel@tonic-gate 	{USBA_EVENT_TAG_POST_RESUME, USBA_POST_RESUME_EVENT, EPL_KERNEL,
5760Sstevel@tonic-gate 						NDI_EVENT_POST_TO_ALL},
5770Sstevel@tonic-gate 	{USBA_EVENT_TAG_PRE_SUSPEND, USBA_PRE_SUSPEND_EVENT, EPL_KERNEL,
5780Sstevel@tonic-gate 						NDI_EVENT_POST_TO_ALL}
5790Sstevel@tonic-gate };
5800Sstevel@tonic-gate 
5810Sstevel@tonic-gate #define	HUBD_N_NDI_EVENTS \
5820Sstevel@tonic-gate 	(sizeof (hubd_ndi_event_defs) / sizeof (ndi_event_definition_t))
5830Sstevel@tonic-gate 
5840Sstevel@tonic-gate static ndi_event_set_t hubd_ndi_events = {
5850Sstevel@tonic-gate 	NDI_EVENTS_REV1, HUBD_N_NDI_EVENTS, hubd_ndi_event_defs};
5860Sstevel@tonic-gate 
5870Sstevel@tonic-gate /* events received from parent */
5880Sstevel@tonic-gate static usb_event_t hubd_events = {
5890Sstevel@tonic-gate 	hubd_disconnect_event_cb,
5900Sstevel@tonic-gate 	hubd_reconnect_event_cb,
5910Sstevel@tonic-gate 	hubd_pre_suspend_event_cb,
5920Sstevel@tonic-gate 	hubd_post_resume_event_cb
5930Sstevel@tonic-gate };
5940Sstevel@tonic-gate 
5950Sstevel@tonic-gate 
5960Sstevel@tonic-gate /*
5970Sstevel@tonic-gate  * hubd_get_soft_state() returns the hubd soft state
5989430SRaymond.Chen@Sun.COM  *
5999430SRaymond.Chen@Sun.COM  * WUSB support extends this function to support wire adapter class
6009430SRaymond.Chen@Sun.COM  * devices. The hubd soft state for the wire adapter class device
6019430SRaymond.Chen@Sun.COM  * would be stored in usb_root_hubd field of the usba_device structure,
6029430SRaymond.Chen@Sun.COM  * just as the USB host controller drivers do.
6030Sstevel@tonic-gate  */
6049430SRaymond.Chen@Sun.COM hubd_t *
hubd_get_soft_state(dev_info_t * dip)6050Sstevel@tonic-gate hubd_get_soft_state(dev_info_t *dip)
6060Sstevel@tonic-gate {
6070Sstevel@tonic-gate 	if (dip == NULL) {
6080Sstevel@tonic-gate 
6090Sstevel@tonic-gate 		return (NULL);
6100Sstevel@tonic-gate 	}
6110Sstevel@tonic-gate 
6129430SRaymond.Chen@Sun.COM 	if (usba_is_root_hub(dip) || usba_is_wa(dip)) {
6130Sstevel@tonic-gate 		usba_device_t *usba_device = usba_get_usba_device(dip);
6140Sstevel@tonic-gate 
6150Sstevel@tonic-gate 		return (usba_device->usb_root_hubd);
6160Sstevel@tonic-gate 	} else {
6170Sstevel@tonic-gate 		int instance = ddi_get_instance(dip);
6180Sstevel@tonic-gate 
6190Sstevel@tonic-gate 		return (ddi_get_soft_state(hubd_statep, instance));
6200Sstevel@tonic-gate 	}
6210Sstevel@tonic-gate }
6220Sstevel@tonic-gate 
6230Sstevel@tonic-gate 
6240Sstevel@tonic-gate /*
6250Sstevel@tonic-gate  * PM support functions:
6260Sstevel@tonic-gate  */
6270Sstevel@tonic-gate /*ARGSUSED*/
6280Sstevel@tonic-gate static void
hubd_pm_busy_component(hubd_t * hubd,dev_info_t * dip,int component)6290Sstevel@tonic-gate hubd_pm_busy_component(hubd_t *hubd, dev_info_t *dip, int component)
6300Sstevel@tonic-gate {
6310Sstevel@tonic-gate 	if (hubd->h_hubpm != NULL) {
6320Sstevel@tonic-gate 		hubd->h_hubpm->hubp_busy_pm++;
6330Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
6340Sstevel@tonic-gate 		if (pm_busy_component(dip, 0) != DDI_SUCCESS) {
6350Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
6360Sstevel@tonic-gate 			hubd->h_hubpm->hubp_busy_pm--;
6370Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
6380Sstevel@tonic-gate 		}
6390Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
6400Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
6410Sstevel@tonic-gate 		    "hubd_pm_busy_component: %d", hubd->h_hubpm->hubp_busy_pm);
6420Sstevel@tonic-gate 	}
6430Sstevel@tonic-gate }
6440Sstevel@tonic-gate 
6450Sstevel@tonic-gate 
6460Sstevel@tonic-gate /*ARGSUSED*/
6470Sstevel@tonic-gate static void
hubd_pm_idle_component(hubd_t * hubd,dev_info_t * dip,int component)6480Sstevel@tonic-gate hubd_pm_idle_component(hubd_t *hubd, dev_info_t *dip, int component)
6490Sstevel@tonic-gate {
6500Sstevel@tonic-gate 	if (hubd->h_hubpm != NULL) {
6510Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
6520Sstevel@tonic-gate 		if (pm_idle_component(dip, 0) == DDI_SUCCESS) {
6530Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
6540Sstevel@tonic-gate 			ASSERT(hubd->h_hubpm->hubp_busy_pm > 0);
6550Sstevel@tonic-gate 			hubd->h_hubpm->hubp_busy_pm--;
6560Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
6570Sstevel@tonic-gate 		}
6580Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
6590Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
6600Sstevel@tonic-gate 		    "hubd_pm_idle_component: %d", hubd->h_hubpm->hubp_busy_pm);
6610Sstevel@tonic-gate 	}
6620Sstevel@tonic-gate }
6630Sstevel@tonic-gate 
6640Sstevel@tonic-gate 
6650Sstevel@tonic-gate /*
6660Sstevel@tonic-gate  * track power level changes for children of this instance
6670Sstevel@tonic-gate  */
6680Sstevel@tonic-gate static void
hubd_set_child_pwrlvl(hubd_t * hubd,usb_port_t port,uint8_t power)6690Sstevel@tonic-gate hubd_set_child_pwrlvl(hubd_t *hubd, usb_port_t port, uint8_t power)
6700Sstevel@tonic-gate {
6710Sstevel@tonic-gate 	int	old_power, new_power, pwr;
6720Sstevel@tonic-gate 	usb_port_t	portno;
6730Sstevel@tonic-gate 	hub_power_t	*hubpm;
6740Sstevel@tonic-gate 
6750Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
6760Sstevel@tonic-gate 	    "hubd_set_child_pwrlvl: port=%d power=%d",
6770Sstevel@tonic-gate 	    port, power);
6780Sstevel@tonic-gate 
6790Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
6800Sstevel@tonic-gate 	hubpm = hubd->h_hubpm;
6810Sstevel@tonic-gate 
6820Sstevel@tonic-gate 	old_power = 0;
6830Sstevel@tonic-gate 	for (portno = 1; portno <= hubd->h_hub_descr.bNbrPorts; portno++) {
6840Sstevel@tonic-gate 		old_power += hubpm->hubp_child_pwrstate[portno];
6850Sstevel@tonic-gate 	}
6860Sstevel@tonic-gate 
6870Sstevel@tonic-gate 	/* assign the port power */
6880Sstevel@tonic-gate 	pwr = hubd->h_hubpm->hubp_child_pwrstate[port];
6890Sstevel@tonic-gate 	hubd->h_hubpm->hubp_child_pwrstate[port] = power;
6900Sstevel@tonic-gate 	new_power = old_power - pwr + power;
6910Sstevel@tonic-gate 
6920Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
6930Sstevel@tonic-gate 	    "hubd_set_child_pwrlvl: new_power=%d old_power=%d",
6940Sstevel@tonic-gate 	    new_power, old_power);
6950Sstevel@tonic-gate 
6960Sstevel@tonic-gate 	if ((new_power > 0) && (old_power == 0)) {
6970Sstevel@tonic-gate 		/* we have the first child coming out of low power */
6980Sstevel@tonic-gate 		(void) hubd_pm_busy_component(hubd, hubd->h_dip, 0);
6990Sstevel@tonic-gate 	} else if ((new_power == 0) && (old_power > 0)) {
7000Sstevel@tonic-gate 		/* we have the last child going to low power */
7010Sstevel@tonic-gate 		(void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
7020Sstevel@tonic-gate 	}
7030Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
7040Sstevel@tonic-gate }
7050Sstevel@tonic-gate 
7060Sstevel@tonic-gate 
7070Sstevel@tonic-gate /*
7080Sstevel@tonic-gate  * given a child dip, locate its port number
7090Sstevel@tonic-gate  */
7100Sstevel@tonic-gate static usb_port_t
hubd_child_dip2port(hubd_t * hubd,dev_info_t * dip)7110Sstevel@tonic-gate hubd_child_dip2port(hubd_t *hubd, dev_info_t *dip)
7120Sstevel@tonic-gate {
7130Sstevel@tonic-gate 	usb_port_t	port;
7140Sstevel@tonic-gate 
7150Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
7160Sstevel@tonic-gate 	for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
7170Sstevel@tonic-gate 		if (hubd->h_children_dips[port] == dip) {
7180Sstevel@tonic-gate 
7190Sstevel@tonic-gate 			break;
7200Sstevel@tonic-gate 		}
7210Sstevel@tonic-gate 	}
7220Sstevel@tonic-gate 	ASSERT(port <= hubd->h_hub_descr.bNbrPorts);
7230Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
7240Sstevel@tonic-gate 
7250Sstevel@tonic-gate 	return (port);
7260Sstevel@tonic-gate }
7270Sstevel@tonic-gate 
7280Sstevel@tonic-gate 
7290Sstevel@tonic-gate /*
7300Sstevel@tonic-gate  * if the hub can be put into low power mode, return success
7310Sstevel@tonic-gate  * NOTE: suspend here means going to lower power, not CPR suspend.
7320Sstevel@tonic-gate  */
7330Sstevel@tonic-gate static int
hubd_can_suspend(hubd_t * hubd)7340Sstevel@tonic-gate hubd_can_suspend(hubd_t *hubd)
7350Sstevel@tonic-gate {
7360Sstevel@tonic-gate 	hub_power_t	*hubpm;
7370Sstevel@tonic-gate 	int		total_power = 0;
7380Sstevel@tonic-gate 	usb_port_t	port;
7390Sstevel@tonic-gate 
7400Sstevel@tonic-gate 	hubpm = hubd->h_hubpm;
7410Sstevel@tonic-gate 
7420Sstevel@tonic-gate 	if (DEVI_IS_DETACHING(hubd->h_dip)) {
7430Sstevel@tonic-gate 
7440Sstevel@tonic-gate 		return (USB_SUCCESS);
7450Sstevel@tonic-gate 	}
7460Sstevel@tonic-gate 
7470Sstevel@tonic-gate 	/*
7480Sstevel@tonic-gate 	 * Don't go to lower power if haven't been at full power for enough
7490Sstevel@tonic-gate 	 * time to let hotplug thread kickoff.
7500Sstevel@tonic-gate 	 */
7510Sstevel@tonic-gate 	if (ddi_get_time() < (hubpm->hubp_time_at_full_power +
7520Sstevel@tonic-gate 	    hubpm->hubp_min_pm_threshold)) {
7530Sstevel@tonic-gate 
7540Sstevel@tonic-gate 		return (USB_FAILURE);
7550Sstevel@tonic-gate 	}
7560Sstevel@tonic-gate 
7570Sstevel@tonic-gate 	for (port = 1; (total_power == 0) &&
7580Sstevel@tonic-gate 	    (port <= hubd->h_hub_descr.bNbrPorts); port++) {
7590Sstevel@tonic-gate 		total_power += hubpm->hubp_child_pwrstate[port];
7600Sstevel@tonic-gate 	}
7610Sstevel@tonic-gate 
7620Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
7634763Slg150142 	    "hubd_can_suspend: %d", total_power);
7640Sstevel@tonic-gate 
7650Sstevel@tonic-gate 	return (total_power ? USB_FAILURE : USB_SUCCESS);
7660Sstevel@tonic-gate }
7670Sstevel@tonic-gate 
7680Sstevel@tonic-gate 
7690Sstevel@tonic-gate /*
7700Sstevel@tonic-gate  * resume port depending on current device state
7710Sstevel@tonic-gate  */
7720Sstevel@tonic-gate static int
hubd_resume_port(hubd_t * hubd,usb_port_t port)7730Sstevel@tonic-gate hubd_resume_port(hubd_t *hubd, usb_port_t port)
7740Sstevel@tonic-gate {
7750Sstevel@tonic-gate 	int		rval, retry;
7760Sstevel@tonic-gate 	usb_cr_t	completion_reason;
7770Sstevel@tonic-gate 	usb_cb_flags_t	cb_flags;
7780Sstevel@tonic-gate 	uint16_t	status;
7790Sstevel@tonic-gate 	uint16_t	change;
7800Sstevel@tonic-gate 	int		retval = USB_FAILURE;
7810Sstevel@tonic-gate 
7820Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
7830Sstevel@tonic-gate 
7840Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
7850Sstevel@tonic-gate 	    "hubd_resume_port: port=%d state=0x%x (%s)", port,
7860Sstevel@tonic-gate 	    hubd->h_dev_state, usb_str_dev_state(hubd->h_dev_state));
7870Sstevel@tonic-gate 
7880Sstevel@tonic-gate 	switch (hubd->h_dev_state) {
7890Sstevel@tonic-gate 	case USB_DEV_HUB_CHILD_PWRLVL:
7900Sstevel@tonic-gate 		/*
7910Sstevel@tonic-gate 		 * This could be a bus ctl for a port other than the one
7920Sstevel@tonic-gate 		 * that has a remote wakeup condition. So check.
7930Sstevel@tonic-gate 		 */
7940Sstevel@tonic-gate 		if ((hubd->h_port_state[port] & PORT_STATUS_PSS) == 0) {
7950Sstevel@tonic-gate 			/* the port isn't suspended, so don't resume */
7960Sstevel@tonic-gate 			retval = USB_SUCCESS;
7970Sstevel@tonic-gate 
7980Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
7990Sstevel@tonic-gate 			    "hubd_resume_port: port=%d not suspended", port);
8000Sstevel@tonic-gate 
8010Sstevel@tonic-gate 			break;
8020Sstevel@tonic-gate 		}
8030Sstevel@tonic-gate 		/*
8040Sstevel@tonic-gate 		 * Device has initiated a wakeup.
8050Sstevel@tonic-gate 		 * Issue a ClearFeature(PortSuspend)
8060Sstevel@tonic-gate 		 */
8070Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
8080Sstevel@tonic-gate 		if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
8090Sstevel@tonic-gate 		    hubd->h_default_pipe,
8101001Ssl147100 		    HUB_HANDLE_PORT_FEATURE_TYPE,
8110Sstevel@tonic-gate 		    USB_REQ_CLEAR_FEATURE,
8120Sstevel@tonic-gate 		    CFS_PORT_SUSPEND,
8130Sstevel@tonic-gate 		    port,
8140Sstevel@tonic-gate 		    0, NULL, 0,
8150Sstevel@tonic-gate 		    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
8160Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
8170Sstevel@tonic-gate 			    "ClearFeature(PortSuspend) fails "
8180Sstevel@tonic-gate 			    "rval=%d cr=%d cb=0x%x", rval,
8190Sstevel@tonic-gate 			    completion_reason, cb_flags);
8200Sstevel@tonic-gate 		}
8210Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
8220Sstevel@tonic-gate 
8230Sstevel@tonic-gate 		/* either way ack changes on the port */
8240Sstevel@tonic-gate 		(void) hubd_determine_port_status(hubd, port,
8254763Slg150142 		    &status, &change, PORT_CHANGE_PSSC);
8260Sstevel@tonic-gate 		retval = USB_SUCCESS;
8270Sstevel@tonic-gate 
8280Sstevel@tonic-gate 		break;
8290Sstevel@tonic-gate 	case USB_DEV_HUB_STATE_RECOVER:
8300Sstevel@tonic-gate 		/*
8310Sstevel@tonic-gate 		 * When hubd's connect event callback posts a connect
8320Sstevel@tonic-gate 		 * event to its child, it results in this busctl call
8330Sstevel@tonic-gate 		 * which is valid
8340Sstevel@tonic-gate 		 */
8350Sstevel@tonic-gate 		/* FALLTHRU */
8360Sstevel@tonic-gate 	case USB_DEV_ONLINE:
8372651Ssl147100 		if (((hubd->h_port_state[port] & PORT_STATUS_CCS) == 0) ||
8382651Ssl147100 		    ((hubd->h_port_state[port] & PORT_STATUS_PSS) == 0)) {
8390Sstevel@tonic-gate 			/*
8400Sstevel@tonic-gate 			 * the port isn't suspended, or connected
8410Sstevel@tonic-gate 			 * so don't resume
8420Sstevel@tonic-gate 			 */
8430Sstevel@tonic-gate 			retval = USB_SUCCESS;
8440Sstevel@tonic-gate 
8450Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
8460Sstevel@tonic-gate 			    "hubd_resume_port: port=%d not suspended", port);
8470Sstevel@tonic-gate 
8480Sstevel@tonic-gate 			break;
8490Sstevel@tonic-gate 		}
8500Sstevel@tonic-gate 		/*
8510Sstevel@tonic-gate 		 * prevent kicking off the hotplug thread
8520Sstevel@tonic-gate 		 */
8530Sstevel@tonic-gate 		hubd->h_hotplug_thread++;
8540Sstevel@tonic-gate 		hubd_stop_polling(hubd);
8550Sstevel@tonic-gate 
8560Sstevel@tonic-gate 		/* Now ClearFeature(PortSuspend) */
8570Sstevel@tonic-gate 		for (retry = 0; retry < HUBD_SUS_RES_RETRY; retry++) {
8580Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
8590Sstevel@tonic-gate 			rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
8600Sstevel@tonic-gate 			    hubd->h_default_pipe,
8611001Ssl147100 			    HUB_HANDLE_PORT_FEATURE_TYPE,
8620Sstevel@tonic-gate 			    USB_REQ_CLEAR_FEATURE,
8630Sstevel@tonic-gate 			    CFS_PORT_SUSPEND,
8640Sstevel@tonic-gate 			    port,
8650Sstevel@tonic-gate 			    0, NULL, 0,
8660Sstevel@tonic-gate 			    &completion_reason, &cb_flags, 0);
8670Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
8680Sstevel@tonic-gate 			if (rval != USB_SUCCESS) {
8690Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_PM,
8700Sstevel@tonic-gate 				    hubd->h_log_handle,
8710Sstevel@tonic-gate 				    "ClearFeature(PortSuspend) fails"
8720Sstevel@tonic-gate 				    "rval=%d cr=%d cb=0x%x", rval,
8730Sstevel@tonic-gate 				    completion_reason, cb_flags);
8740Sstevel@tonic-gate 			} else {
8750Sstevel@tonic-gate 				/*
8760Sstevel@tonic-gate 				 * As per spec section 11.9 and 7.1.7.7
8770Sstevel@tonic-gate 				 * hub need to provide at least 20ms of
8780Sstevel@tonic-gate 				 * resume signalling, and s/w provide 10ms of
8790Sstevel@tonic-gate 				 * recovery time before accessing the port.
8800Sstevel@tonic-gate 				 */
8810Sstevel@tonic-gate 				mutex_exit(HUBD_MUTEX(hubd));
8820Sstevel@tonic-gate 				delay(drv_usectohz(40000));
8830Sstevel@tonic-gate 				mutex_enter(HUBD_MUTEX(hubd));
8840Sstevel@tonic-gate 				(void) hubd_determine_port_status(hubd, port,
8850Sstevel@tonic-gate 				    &status, &change, PORT_CHANGE_PSSC);
8860Sstevel@tonic-gate 
8870Sstevel@tonic-gate 				if ((status & PORT_STATUS_PSS) == 0) {
8880Sstevel@tonic-gate 					/* the port did finally resume */
8890Sstevel@tonic-gate 					retval = USB_SUCCESS;
8900Sstevel@tonic-gate 
8910Sstevel@tonic-gate 					break;
8920Sstevel@tonic-gate 				}
8930Sstevel@tonic-gate 			}
8940Sstevel@tonic-gate 		}
8950Sstevel@tonic-gate 
8960Sstevel@tonic-gate 		/* allow hotplug thread again */
8970Sstevel@tonic-gate 		hubd->h_hotplug_thread--;
8980Sstevel@tonic-gate 		hubd_start_polling(hubd, 0);
8990Sstevel@tonic-gate 
9000Sstevel@tonic-gate 		break;
9010Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
9020Sstevel@tonic-gate 		/* Ignore - NO Operation */
9030Sstevel@tonic-gate 		retval = USB_SUCCESS;
9040Sstevel@tonic-gate 
9050Sstevel@tonic-gate 		break;
9060Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
9070Sstevel@tonic-gate 	case USB_DEV_PWRED_DOWN:
9080Sstevel@tonic-gate 	default:
9090Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
9100Sstevel@tonic-gate 		    "Improper state for port Resume");
9110Sstevel@tonic-gate 
9120Sstevel@tonic-gate 		break;
9130Sstevel@tonic-gate 	}
9140Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
9150Sstevel@tonic-gate 
9160Sstevel@tonic-gate 	return (retval);
9170Sstevel@tonic-gate }
9180Sstevel@tonic-gate 
9190Sstevel@tonic-gate 
9200Sstevel@tonic-gate /*
9210Sstevel@tonic-gate  * suspend port depending on device state
9220Sstevel@tonic-gate  */
9230Sstevel@tonic-gate static int
hubd_suspend_port(hubd_t * hubd,usb_port_t port)9240Sstevel@tonic-gate hubd_suspend_port(hubd_t *hubd, usb_port_t port)
9250Sstevel@tonic-gate {
9260Sstevel@tonic-gate 	int		rval, retry;
9270Sstevel@tonic-gate 	int		retval = USB_FAILURE;
9280Sstevel@tonic-gate 	usb_cr_t	completion_reason;
9290Sstevel@tonic-gate 	usb_cb_flags_t	cb_flags;
9300Sstevel@tonic-gate 	uint16_t	status;
9310Sstevel@tonic-gate 	uint16_t	change;
9320Sstevel@tonic-gate 
9330Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
9340Sstevel@tonic-gate 	    "hubd_suspend_port: port=%d", port);
9350Sstevel@tonic-gate 
9360Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
9370Sstevel@tonic-gate 
9380Sstevel@tonic-gate 	switch (hubd->h_dev_state) {
9390Sstevel@tonic-gate 	case USB_DEV_HUB_STATE_RECOVER:
9400Sstevel@tonic-gate 		/*
9410Sstevel@tonic-gate 		 * When hubd's connect event callback posts a connect
9420Sstevel@tonic-gate 		 * event to its child, it results in this busctl call
9430Sstevel@tonic-gate 		 * which is valid
9440Sstevel@tonic-gate 		 */
9450Sstevel@tonic-gate 		/* FALLTHRU */
9460Sstevel@tonic-gate 	case USB_DEV_HUB_CHILD_PWRLVL:
9470Sstevel@tonic-gate 		/*
9480Sstevel@tonic-gate 		 * When one child is resuming, the other could timeout
9490Sstevel@tonic-gate 		 * and go to low power mode, which is valid
9500Sstevel@tonic-gate 		 */
9510Sstevel@tonic-gate 		/* FALLTHRU */
9520Sstevel@tonic-gate 	case USB_DEV_ONLINE:
9530Sstevel@tonic-gate 		hubd->h_hotplug_thread++;
9540Sstevel@tonic-gate 		hubd_stop_polling(hubd);
9550Sstevel@tonic-gate 
9560Sstevel@tonic-gate 		/*
9570Sstevel@tonic-gate 		 * Some devices start an unprovoked resume.  According to spec,
9580Sstevel@tonic-gate 		 * normal resume time for port is 10ms.  Wait for double that
9590Sstevel@tonic-gate 		 * time, then check to be sure port is really suspended.
9600Sstevel@tonic-gate 		 */
9610Sstevel@tonic-gate 		for (retry = 0; retry < HUBD_SUS_RES_RETRY; retry++) {
9620Sstevel@tonic-gate 			/* Now SetFeature(PortSuspend) */
9630Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
9640Sstevel@tonic-gate 			if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
9650Sstevel@tonic-gate 			    hubd->h_default_pipe,
9661001Ssl147100 			    HUB_HANDLE_PORT_FEATURE_TYPE,
9670Sstevel@tonic-gate 			    USB_REQ_SET_FEATURE,
9680Sstevel@tonic-gate 			    CFS_PORT_SUSPEND,
9690Sstevel@tonic-gate 			    port,
9700Sstevel@tonic-gate 			    0, NULL, 0,
9710Sstevel@tonic-gate 			    &completion_reason, &cb_flags, 0)) !=
9720Sstevel@tonic-gate 			    USB_SUCCESS) {
9730Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_PM,
9740Sstevel@tonic-gate 				    hubd->h_log_handle,
9750Sstevel@tonic-gate 				    "SetFeature(PortSuspend) fails"
9760Sstevel@tonic-gate 				    "rval=%d cr=%d cb=0x%x",
9770Sstevel@tonic-gate 				    rval, completion_reason, cb_flags);
9780Sstevel@tonic-gate 			}
9790Sstevel@tonic-gate 
9800Sstevel@tonic-gate 			/*
9810Sstevel@tonic-gate 			 * some devices start an unprovoked resume
9820Sstevel@tonic-gate 			 * wait and check port status after some time
9830Sstevel@tonic-gate 			 */
9840Sstevel@tonic-gate 			delay(drv_usectohz(20000));
9850Sstevel@tonic-gate 
9860Sstevel@tonic-gate 			/* either ways ack changes on the port */
9870Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
9880Sstevel@tonic-gate 			(void) hubd_determine_port_status(hubd, port,
9890Sstevel@tonic-gate 			    &status, &change, PORT_CHANGE_PSSC);
9900Sstevel@tonic-gate 			if (status & PORT_STATUS_PSS) {
9910Sstevel@tonic-gate 				/* the port is indeed suspended */
9920Sstevel@tonic-gate 				retval = USB_SUCCESS;
9930Sstevel@tonic-gate 
9940Sstevel@tonic-gate 				break;
995*12819SVincent.Wang@Sun.COM 			} else {
996*12819SVincent.Wang@Sun.COM 				USB_DPRINTF_L0(DPRINT_MASK_PM,
997*12819SVincent.Wang@Sun.COM 				    hubd->h_log_handle,
998*12819SVincent.Wang@Sun.COM 				    "hubdi: port%d failed to be suspended!",
999*12819SVincent.Wang@Sun.COM 				    port);
10000Sstevel@tonic-gate 			}
10010Sstevel@tonic-gate 		}
10020Sstevel@tonic-gate 
10030Sstevel@tonic-gate 		hubd->h_hotplug_thread--;
10040Sstevel@tonic-gate 		hubd_start_polling(hubd, 0);
10050Sstevel@tonic-gate 
10060Sstevel@tonic-gate 		break;
10070Sstevel@tonic-gate 
10080Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
10090Sstevel@tonic-gate 		/* Ignore - No Operation */
10100Sstevel@tonic-gate 		retval = USB_SUCCESS;
10110Sstevel@tonic-gate 
10120Sstevel@tonic-gate 		break;
10130Sstevel@tonic-gate 
10140Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
10150Sstevel@tonic-gate 	case USB_DEV_PWRED_DOWN:
10160Sstevel@tonic-gate 	default:
10170Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
10180Sstevel@tonic-gate 		    "Improper state for port Suspend");
10190Sstevel@tonic-gate 
10200Sstevel@tonic-gate 		break;
10210Sstevel@tonic-gate 	}
10220Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
10230Sstevel@tonic-gate 
10240Sstevel@tonic-gate 	return (retval);
10250Sstevel@tonic-gate }
10260Sstevel@tonic-gate 
10270Sstevel@tonic-gate 
10280Sstevel@tonic-gate /*
10290Sstevel@tonic-gate  * child post attach/detach notifications
10300Sstevel@tonic-gate  */
10310Sstevel@tonic-gate static void
hubd_post_attach(hubd_t * hubd,usb_port_t port,struct attachspec * as)10320Sstevel@tonic-gate hubd_post_attach(hubd_t *hubd, usb_port_t port, struct attachspec *as)
10330Sstevel@tonic-gate {
10340Sstevel@tonic-gate 	dev_info_t	*dip;
10350Sstevel@tonic-gate 
10360Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
10370Sstevel@tonic-gate 	    "hubd_post_attach: port=%d result=%d",
10380Sstevel@tonic-gate 	    port, as->result);
10390Sstevel@tonic-gate 
10400Sstevel@tonic-gate 	if (as->result == DDI_SUCCESS) {
10410Sstevel@tonic-gate 		/*
10420Sstevel@tonic-gate 		 * Check if the child created wants to be power managed.
10430Sstevel@tonic-gate 		 * If yes, the childs power level gets automatically tracked
10440Sstevel@tonic-gate 		 * by DDI_CTLOPS_POWER busctl.
10450Sstevel@tonic-gate 		 * If no, we set power of the new child by default
10460Sstevel@tonic-gate 		 * to USB_DEV_OS_FULL_PWR. Because we should never suspend.
10470Sstevel@tonic-gate 		 */
10480Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
10490Sstevel@tonic-gate 		dip = hubd->h_children_dips[port];
10500Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
10510Sstevel@tonic-gate 		if (DEVI(dip)->devi_pm_info == NULL) {
10520Sstevel@tonic-gate 			hubd_set_child_pwrlvl(hubd, port, USB_DEV_OS_FULL_PWR);
10530Sstevel@tonic-gate 		}
10540Sstevel@tonic-gate 	}
10550Sstevel@tonic-gate }
10560Sstevel@tonic-gate 
10570Sstevel@tonic-gate 
10580Sstevel@tonic-gate static void
hubd_post_detach(hubd_t * hubd,usb_port_t port,struct detachspec * ds)10590Sstevel@tonic-gate hubd_post_detach(hubd_t *hubd, usb_port_t port, struct detachspec *ds)
10600Sstevel@tonic-gate {
10610Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
10620Sstevel@tonic-gate 	    "hubd_post_detach: port=%d result=%d", port, ds->result);
10630Sstevel@tonic-gate 
10640Sstevel@tonic-gate 	/*
10650Sstevel@tonic-gate 	 * if the device is successfully detached and is the
10660Sstevel@tonic-gate 	 * last device to detach, mark component as idle
10670Sstevel@tonic-gate 	 */
10680Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
10690Sstevel@tonic-gate 	if (ds->result == DDI_SUCCESS) {
10700Sstevel@tonic-gate 		usba_device_t	*usba_device = hubd->h_usba_devices[port];
10711001Ssl147100 		dev_info_t	*pdip = hubd->h_dip;
10720Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
10730Sstevel@tonic-gate 
10741001Ssl147100 		usba_hubdi_incr_power_budget(pdip, usba_device);
10751001Ssl147100 
10760Sstevel@tonic-gate 		/*
10770Sstevel@tonic-gate 		 * We set power of the detached child
10780Sstevel@tonic-gate 		 * to 0, so that we can suspend if all
10790Sstevel@tonic-gate 		 * our children are gone
10800Sstevel@tonic-gate 		 */
10810Sstevel@tonic-gate 		hubd_set_child_pwrlvl(hubd, port, USB_DEV_OS_PWR_OFF);
10820Sstevel@tonic-gate 
10830Sstevel@tonic-gate 		/* check for leaks on detaching */
10840Sstevel@tonic-gate 		if ((usba_device) && (ds->cmd == DDI_DETACH)) {
10850Sstevel@tonic-gate 			usba_check_for_leaks(usba_device);
10860Sstevel@tonic-gate 		}
10870Sstevel@tonic-gate 	} else {
10880Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
10890Sstevel@tonic-gate 	}
10900Sstevel@tonic-gate }
10910Sstevel@tonic-gate 
10920Sstevel@tonic-gate 
10930Sstevel@tonic-gate /*
10940Sstevel@tonic-gate  * hubd_post_power
10950Sstevel@tonic-gate  *	After the child's power entry point has been called
10960Sstevel@tonic-gate  *	we record its power level in our local struct.
10970Sstevel@tonic-gate  *	If the device has powered off, we suspend port
10980Sstevel@tonic-gate  */
10990Sstevel@tonic-gate static int
hubd_post_power(hubd_t * hubd,usb_port_t port,pm_bp_child_pwrchg_t * bpc,int result)11000Sstevel@tonic-gate hubd_post_power(hubd_t *hubd, usb_port_t port, pm_bp_child_pwrchg_t *bpc,
11010Sstevel@tonic-gate     int result)
11020Sstevel@tonic-gate {
11030Sstevel@tonic-gate 	int	retval = USB_SUCCESS;
11040Sstevel@tonic-gate 
11050Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
11060Sstevel@tonic-gate 	    "hubd_post_power: port=%d", port);
11070Sstevel@tonic-gate 
11080Sstevel@tonic-gate 	if (result == DDI_SUCCESS) {
11090Sstevel@tonic-gate 
11100Sstevel@tonic-gate 		/* record this power in our local struct */
11110Sstevel@tonic-gate 		hubd_set_child_pwrlvl(hubd, port, bpc->bpc_nlevel);
11120Sstevel@tonic-gate 
11130Sstevel@tonic-gate 		if (bpc->bpc_nlevel == USB_DEV_OS_PWR_OFF) {
11140Sstevel@tonic-gate 
11150Sstevel@tonic-gate 			/* now suspend the port */
11160Sstevel@tonic-gate 			retval = hubd_suspend_port(hubd, port);
11170Sstevel@tonic-gate 		} else if (bpc->bpc_nlevel == USB_DEV_OS_FULL_PWR) {
11180Sstevel@tonic-gate 
11190Sstevel@tonic-gate 			/* make sure the port is resumed */
11200Sstevel@tonic-gate 			retval = hubd_resume_port(hubd, port);
11210Sstevel@tonic-gate 		}
11220Sstevel@tonic-gate 	} else {
11230Sstevel@tonic-gate 
11240Sstevel@tonic-gate 		/* record old power in our local struct */
11250Sstevel@tonic-gate 		hubd_set_child_pwrlvl(hubd, port, bpc->bpc_olevel);
11260Sstevel@tonic-gate 
11270Sstevel@tonic-gate 		if (bpc->bpc_olevel == USB_DEV_OS_PWR_OFF) {
11280Sstevel@tonic-gate 
11290Sstevel@tonic-gate 			/*
11300Sstevel@tonic-gate 			 * As this device failed to transition from
11310Sstevel@tonic-gate 			 * power off state, suspend the port again
11320Sstevel@tonic-gate 			 */
11330Sstevel@tonic-gate 			retval = hubd_suspend_port(hubd, port);
11340Sstevel@tonic-gate 		}
11350Sstevel@tonic-gate 	}
11360Sstevel@tonic-gate 
11370Sstevel@tonic-gate 	return (retval);
11380Sstevel@tonic-gate }
11390Sstevel@tonic-gate 
11400Sstevel@tonic-gate 
11410Sstevel@tonic-gate /*
11420Sstevel@tonic-gate  * bus ctl notifications are handled here, the rest goes up to root hub/hcd
11430Sstevel@tonic-gate  */
11440Sstevel@tonic-gate static int
usba_hubdi_bus_ctl(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t op,void * arg,void * result)11450Sstevel@tonic-gate usba_hubdi_bus_ctl(dev_info_t *dip,
11460Sstevel@tonic-gate 	dev_info_t	*rdip,
11470Sstevel@tonic-gate 	ddi_ctl_enum_t	op,
11480Sstevel@tonic-gate 	void		*arg,
11490Sstevel@tonic-gate 	void		*result)
11500Sstevel@tonic-gate {
11510Sstevel@tonic-gate 	usba_device_t *hub_usba_device = usba_get_usba_device(rdip);
11520Sstevel@tonic-gate 	dev_info_t *root_hub_dip = hub_usba_device->usb_root_hub_dip;
11530Sstevel@tonic-gate 	struct attachspec *as;
11540Sstevel@tonic-gate 	struct detachspec *ds;
11550Sstevel@tonic-gate 	hubd_t		*hubd;
11560Sstevel@tonic-gate 	usb_port_t	port;
11570Sstevel@tonic-gate 	int		circ, rval;
11580Sstevel@tonic-gate 	int		retval = DDI_FAILURE;
11590Sstevel@tonic-gate 
11600Sstevel@tonic-gate 	hubd = hubd_get_soft_state(dip);
11610Sstevel@tonic-gate 
11620Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
11630Sstevel@tonic-gate 
11640Sstevel@tonic-gate 	/* flag that we are currently running bus_ctl */
11650Sstevel@tonic-gate 	hubd->h_bus_ctls++;
11660Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
11670Sstevel@tonic-gate 
11680Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle,
11690Sstevel@tonic-gate 	    "usba_hubdi_bus_ctl:\n\t"
11700Sstevel@tonic-gate 	    "dip=0x%p, rdip=0x%p, op=0x%x, arg=0x%p",
11716898Sfb209375 	    (void *)dip, (void *)rdip, op, arg);
11720Sstevel@tonic-gate 
11730Sstevel@tonic-gate 	switch (op) {
11740Sstevel@tonic-gate 	case DDI_CTLOPS_ATTACH:
11750Sstevel@tonic-gate 		as = (struct attachspec *)arg;
11760Sstevel@tonic-gate 		port = hubd_child_dip2port(hubd, rdip);
11770Sstevel@tonic-gate 
11780Sstevel@tonic-gate 		/* there is nothing to do at resume time */
11790Sstevel@tonic-gate 		if (as->cmd == DDI_RESUME) {
11800Sstevel@tonic-gate 			break;
11810Sstevel@tonic-gate 		}
11820Sstevel@tonic-gate 
11830Sstevel@tonic-gate 		/* serialize access */
11840Sstevel@tonic-gate 		ndi_devi_enter(hubd->h_dip, &circ);
11850Sstevel@tonic-gate 
11860Sstevel@tonic-gate 		switch (as->when) {
11870Sstevel@tonic-gate 		case DDI_PRE:
11880Sstevel@tonic-gate 			USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
11890Sstevel@tonic-gate 			    "DDI_PRE DDI_CTLOPS_ATTACH: dip=%p, port=%d",
11906898Sfb209375 			    (void *)rdip, port);
11910Sstevel@tonic-gate 
11920Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
11930Sstevel@tonic-gate 			hubd->h_port_state[port] |= HUBD_CHILD_ATTACHING;
11940Sstevel@tonic-gate 
11950Sstevel@tonic-gate 			/* Go busy here.  Matching idle is DDI_POST case. */
11960Sstevel@tonic-gate 			(void) hubd_pm_busy_component(hubd, dip, 0);
11970Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
11980Sstevel@tonic-gate 
11990Sstevel@tonic-gate 			/*
12000Sstevel@tonic-gate 			 * if we suspended the port previously
12010Sstevel@tonic-gate 			 * because child went to low power state, and
12020Sstevel@tonic-gate 			 * someone unloaded the driver, the port would
12030Sstevel@tonic-gate 			 * still be suspended and needs to be resumed
12040Sstevel@tonic-gate 			 */
12050Sstevel@tonic-gate 			rval = hubd_resume_port(hubd, port);
12060Sstevel@tonic-gate 			if (rval == USB_SUCCESS) {
12070Sstevel@tonic-gate 				retval = DDI_SUCCESS;
12080Sstevel@tonic-gate 			}
12090Sstevel@tonic-gate 
12100Sstevel@tonic-gate 			break;
12110Sstevel@tonic-gate 		case DDI_POST:
12120Sstevel@tonic-gate 			USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
12130Sstevel@tonic-gate 			    "DDI_POST DDI_CTLOPS_ATTACH: dip=%p, port=%d",
12146898Sfb209375 			    (void *)rdip, port);
12150Sstevel@tonic-gate 
12160Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
12170Sstevel@tonic-gate 			hubd->h_port_state[port] &= ~HUBD_CHILD_ATTACHING;
12180Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
12190Sstevel@tonic-gate 
12200Sstevel@tonic-gate 			hubd_post_attach(hubd, port, (struct attachspec *)arg);
12210Sstevel@tonic-gate 			retval = DDI_SUCCESS;
12220Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
12230Sstevel@tonic-gate 
12240Sstevel@tonic-gate 			/* Matching idle call for DDI_PRE busy call. */
12250Sstevel@tonic-gate 			(void) hubd_pm_idle_component(hubd, dip, 0);
12260Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
12270Sstevel@tonic-gate 		}
12280Sstevel@tonic-gate 		ndi_devi_exit(hubd->h_dip, circ);
12290Sstevel@tonic-gate 
12300Sstevel@tonic-gate 		break;
12310Sstevel@tonic-gate 	case DDI_CTLOPS_DETACH:
12320Sstevel@tonic-gate 		ds = (struct detachspec *)arg;
12330Sstevel@tonic-gate 		port = hubd_child_dip2port(hubd, rdip);
12340Sstevel@tonic-gate 
12350Sstevel@tonic-gate 		/* there is nothing to do at suspend time */
12360Sstevel@tonic-gate 		if (ds->cmd == DDI_SUSPEND) {
12370Sstevel@tonic-gate 			break;
12380Sstevel@tonic-gate 		}
12390Sstevel@tonic-gate 
12400Sstevel@tonic-gate 		/* serialize access */
12410Sstevel@tonic-gate 		ndi_devi_enter(hubd->h_dip, &circ);
12420Sstevel@tonic-gate 
12430Sstevel@tonic-gate 		switch (ds->when) {
12440Sstevel@tonic-gate 		case DDI_PRE:
12450Sstevel@tonic-gate 			USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
12460Sstevel@tonic-gate 			    "DDI_PRE DDI_CTLOPS_DETACH: dip=%p port=%d",
12476898Sfb209375 			    (void *)rdip, port);
12480Sstevel@tonic-gate 
12490Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
12500Sstevel@tonic-gate 			hubd->h_port_state[port] |= HUBD_CHILD_DETACHING;
12510Sstevel@tonic-gate 
12520Sstevel@tonic-gate 			/* Go busy here.  Matching idle is DDI_POST case. */
12530Sstevel@tonic-gate 			(void) hubd_pm_busy_component(hubd, dip, 0);
12540Sstevel@tonic-gate 
12550Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
12560Sstevel@tonic-gate 			retval = DDI_SUCCESS;
12570Sstevel@tonic-gate 
12580Sstevel@tonic-gate 			break;
12590Sstevel@tonic-gate 		case DDI_POST:
12600Sstevel@tonic-gate 			USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
12610Sstevel@tonic-gate 			    "DDI_POST DDI_CTLOPS_DETACH: dip=%p port=%d",
12626898Sfb209375 			    (void *)rdip, port);
12630Sstevel@tonic-gate 
12640Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
12650Sstevel@tonic-gate 			hubd->h_port_state[port] &= ~HUBD_CHILD_DETACHING;
12660Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
12670Sstevel@tonic-gate 
12680Sstevel@tonic-gate 			/* Matching idle call for DDI_PRE busy call. */
12690Sstevel@tonic-gate 			hubd_post_detach(hubd, port, (struct detachspec *)arg);
12700Sstevel@tonic-gate 			retval = DDI_SUCCESS;
12710Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
12720Sstevel@tonic-gate 			(void) hubd_pm_idle_component(hubd, dip, 0);
12730Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
12740Sstevel@tonic-gate 
12750Sstevel@tonic-gate 			break;
12760Sstevel@tonic-gate 		}
12770Sstevel@tonic-gate 		ndi_devi_exit(hubd->h_dip, circ);
12780Sstevel@tonic-gate 
12790Sstevel@tonic-gate 		break;
12800Sstevel@tonic-gate 	default:
12810Sstevel@tonic-gate 		retval = usba_bus_ctl(root_hub_dip, rdip, op, arg, result);
12820Sstevel@tonic-gate 	}
12830Sstevel@tonic-gate 
12840Sstevel@tonic-gate 	/* decrement bus_ctls count */
12850Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
12860Sstevel@tonic-gate 	hubd->h_bus_ctls--;
12870Sstevel@tonic-gate 	ASSERT(hubd->h_bus_ctls >= 0);
12880Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
12890Sstevel@tonic-gate 
12900Sstevel@tonic-gate 	return (retval);
12910Sstevel@tonic-gate }
12920Sstevel@tonic-gate 
129310783SVincent.Wang@Sun.COM /*
129410783SVincent.Wang@Sun.COM  * hubd_config_one:
129510783SVincent.Wang@Sun.COM  * 	enumerate one child according to 'port'
129610783SVincent.Wang@Sun.COM  */
129710783SVincent.Wang@Sun.COM 
129810783SVincent.Wang@Sun.COM static boolean_t
hubd_config_one(hubd_t * hubd,int port)129910783SVincent.Wang@Sun.COM hubd_config_one(hubd_t *hubd, int port)
130010783SVincent.Wang@Sun.COM {
130110783SVincent.Wang@Sun.COM 	uint16_t	status, change;
130210783SVincent.Wang@Sun.COM 	dev_info_t	*hdip = hubd->h_dip;
130310783SVincent.Wang@Sun.COM 	dev_info_t	*rh_dip = hubd->h_usba_device->usb_root_hub_dip;
130410783SVincent.Wang@Sun.COM 	boolean_t	online_child = B_FALSE, found = B_FALSE;
130510783SVincent.Wang@Sun.COM 	int		prh_circ, rh_circ, circ;
130610783SVincent.Wang@Sun.COM 
130710783SVincent.Wang@Sun.COM 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
130810783SVincent.Wang@Sun.COM 	    "hubd_config_one:  started, hubd_reset_port = 0x%x", port);
130910783SVincent.Wang@Sun.COM 
131010783SVincent.Wang@Sun.COM 	ndi_hold_devi(hdip); /* so we don't race with detach */
131110783SVincent.Wang@Sun.COM 
131210783SVincent.Wang@Sun.COM 	/*
131310783SVincent.Wang@Sun.COM 	 * this ensures one config activity per system at a time.
131410783SVincent.Wang@Sun.COM 	 * we enter the parent PCI node to have this serialization.
131510783SVincent.Wang@Sun.COM 	 * this also excludes ioctls and deathrow thread
131610783SVincent.Wang@Sun.COM 	 */
131710783SVincent.Wang@Sun.COM 	ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
131810783SVincent.Wang@Sun.COM 	ndi_devi_enter(rh_dip, &rh_circ);
131910783SVincent.Wang@Sun.COM 
132010783SVincent.Wang@Sun.COM 	/* exclude other threads */
132110783SVincent.Wang@Sun.COM 	ndi_devi_enter(hdip, &circ);
132210783SVincent.Wang@Sun.COM 	mutex_enter(HUBD_MUTEX(hubd));
132310783SVincent.Wang@Sun.COM 
132411214SVincent.Wang@Sun.COM 	hubd_pm_busy_component(hubd, hubd->h_dip, 0);
132511214SVincent.Wang@Sun.COM 
132610783SVincent.Wang@Sun.COM 	if (!hubd->h_children_dips[port]) {
132710783SVincent.Wang@Sun.COM 
132810783SVincent.Wang@Sun.COM 		(void) hubd_determine_port_status(hubd, port,
132910783SVincent.Wang@Sun.COM 		    &status, &change, HUBD_ACK_ALL_CHANGES);
133010783SVincent.Wang@Sun.COM 
133110783SVincent.Wang@Sun.COM 		if (status & PORT_STATUS_CCS) {
133210783SVincent.Wang@Sun.COM 			online_child |=	(hubd_handle_port_connect(hubd,
133310783SVincent.Wang@Sun.COM 			    port) == USB_SUCCESS);
133410783SVincent.Wang@Sun.COM 			found = online_child;
133510783SVincent.Wang@Sun.COM 		}
133610783SVincent.Wang@Sun.COM 	} else {
133710783SVincent.Wang@Sun.COM 		found = B_TRUE;
133810783SVincent.Wang@Sun.COM 	}
133910783SVincent.Wang@Sun.COM 
134010783SVincent.Wang@Sun.COM 	mutex_exit(HUBD_MUTEX(hubd));
134110783SVincent.Wang@Sun.COM 
134210783SVincent.Wang@Sun.COM 	ndi_devi_exit(hdip, circ);
134310783SVincent.Wang@Sun.COM 	ndi_devi_exit(rh_dip, rh_circ);
134410783SVincent.Wang@Sun.COM 	ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ);
134510783SVincent.Wang@Sun.COM 
134610783SVincent.Wang@Sun.COM 	if (online_child) {
134710783SVincent.Wang@Sun.COM 		USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
134810783SVincent.Wang@Sun.COM 		    "hubd_config_one: onlining child");
134910783SVincent.Wang@Sun.COM 
135010783SVincent.Wang@Sun.COM 		(void) ndi_devi_online(hubd->h_dip, 0);
135110783SVincent.Wang@Sun.COM 	}
135210783SVincent.Wang@Sun.COM 
135310783SVincent.Wang@Sun.COM 	mutex_enter(HUBD_MUTEX(hubd));
135410783SVincent.Wang@Sun.COM 
135510783SVincent.Wang@Sun.COM 	(void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
135610783SVincent.Wang@Sun.COM 
135710783SVincent.Wang@Sun.COM 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
135810783SVincent.Wang@Sun.COM 	    "hubd_config_one: exit");
135910783SVincent.Wang@Sun.COM 
136010783SVincent.Wang@Sun.COM 	mutex_exit(HUBD_MUTEX(hubd));
136110783SVincent.Wang@Sun.COM 
136210783SVincent.Wang@Sun.COM 	ndi_rele_devi(hdip);
136310783SVincent.Wang@Sun.COM 
136410783SVincent.Wang@Sun.COM 	return (found);
136510783SVincent.Wang@Sun.COM }
13660Sstevel@tonic-gate 
13670Sstevel@tonic-gate /*
13680Sstevel@tonic-gate  * bus enumeration entry points
13690Sstevel@tonic-gate  */
13700Sstevel@tonic-gate static int
hubd_bus_config(dev_info_t * dip,uint_t flag,ddi_bus_config_op_t op,void * arg,dev_info_t ** child)13710Sstevel@tonic-gate hubd_bus_config(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
13720Sstevel@tonic-gate     void *arg, dev_info_t **child)
13730Sstevel@tonic-gate {
13740Sstevel@tonic-gate 	hubd_t	*hubd = hubd_get_soft_state(dip);
13750Sstevel@tonic-gate 	int	rval, circ;
137610783SVincent.Wang@Sun.COM 	long port;
13770Sstevel@tonic-gate 
13780Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
13790Sstevel@tonic-gate 	    "hubd_bus_config: op=%d", op);
13800Sstevel@tonic-gate 
13810Sstevel@tonic-gate 	if (hubdi_bus_config_debug) {
13820Sstevel@tonic-gate 		flag |= NDI_DEVI_DEBUG;
13830Sstevel@tonic-gate 	}
13840Sstevel@tonic-gate 
138510783SVincent.Wang@Sun.COM 	if (op == BUS_CONFIG_ONE) {
138610783SVincent.Wang@Sun.COM 		boolean_t found;
13879595SGuoqing.Zhu@Sun.COM 		char cname[80];
13889595SGuoqing.Zhu@Sun.COM 		char *name, *addr;
13899595SGuoqing.Zhu@Sun.COM 
139010783SVincent.Wang@Sun.COM 		USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
139110783SVincent.Wang@Sun.COM 		    "hubd_bus_config: op=%d (BUS_CONFIG_ONE)", op);
139210783SVincent.Wang@Sun.COM 
13939595SGuoqing.Zhu@Sun.COM 		(void) snprintf(cname, 80, "%s", (char *)arg);
13949595SGuoqing.Zhu@Sun.COM 		/* split name into "name@addr" parts */
13959595SGuoqing.Zhu@Sun.COM 		i_ddi_parse_name(cname, &name, &addr, NULL);
139610783SVincent.Wang@Sun.COM 		if (addr && *addr) {
139710783SVincent.Wang@Sun.COM 			(void) ddi_strtol(addr, NULL, 16, &port);
139810783SVincent.Wang@Sun.COM 		} else {
139910783SVincent.Wang@Sun.COM 			return (NDI_FAILURE);
140010783SVincent.Wang@Sun.COM 		}
140110783SVincent.Wang@Sun.COM 
140210783SVincent.Wang@Sun.COM 		found = hubd_config_one(hubd, port);
140310783SVincent.Wang@Sun.COM 
140410783SVincent.Wang@Sun.COM 		if (found == 0) {
14059595SGuoqing.Zhu@Sun.COM 			return (NDI_FAILURE);
14069595SGuoqing.Zhu@Sun.COM 		}
14079595SGuoqing.Zhu@Sun.COM 
14080Sstevel@tonic-gate 	}
14090Sstevel@tonic-gate 	ndi_devi_enter(hubd->h_dip, &circ);
14100Sstevel@tonic-gate 	rval = ndi_busop_bus_config(dip, flag, op, arg, child, 0);
14110Sstevel@tonic-gate 	ndi_devi_exit(hubd->h_dip, circ);
14120Sstevel@tonic-gate 
14130Sstevel@tonic-gate 	return (rval);
14140Sstevel@tonic-gate }
14150Sstevel@tonic-gate 
14160Sstevel@tonic-gate 
14170Sstevel@tonic-gate static int
hubd_bus_unconfig(dev_info_t * dip,uint_t flag,ddi_bus_config_op_t op,void * arg)14180Sstevel@tonic-gate hubd_bus_unconfig(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
14190Sstevel@tonic-gate     void *arg)
14200Sstevel@tonic-gate {
14210Sstevel@tonic-gate 	hubd_t		*hubd = hubd_get_soft_state(dip);
14220Sstevel@tonic-gate 	dev_info_t	*cdip;
14230Sstevel@tonic-gate 	usb_port_t	port;
14240Sstevel@tonic-gate 	int		circ;
14250Sstevel@tonic-gate 	int		rval;
14260Sstevel@tonic-gate 
14270Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
14280Sstevel@tonic-gate 	    "hubd_bus_unconfig: op=%d", op);
14290Sstevel@tonic-gate 
14300Sstevel@tonic-gate 	if (hubdi_bus_config_debug) {
14310Sstevel@tonic-gate 		flag |= NDI_DEVI_DEBUG;
14320Sstevel@tonic-gate 	}
14330Sstevel@tonic-gate 
14340Sstevel@tonic-gate 	if ((op == BUS_UNCONFIG_ALL) && (flag & NDI_AUTODETACH) == 0) {
14350Sstevel@tonic-gate 		flag |= NDI_DEVI_REMOVE;
14360Sstevel@tonic-gate 	}
14370Sstevel@tonic-gate 
14380Sstevel@tonic-gate 	/* serialize access */
14390Sstevel@tonic-gate 	ndi_devi_enter(dip, &circ);
14400Sstevel@tonic-gate 
14410Sstevel@tonic-gate 	rval = ndi_busop_bus_unconfig(dip, flag, op, arg);
14420Sstevel@tonic-gate 
14430Sstevel@tonic-gate 	/* logically zap children's list */
14440Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
14450Sstevel@tonic-gate 	for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
14460Sstevel@tonic-gate 		hubd->h_port_state[port] |= HUBD_CHILD_ZAP;
14470Sstevel@tonic-gate 	}
14480Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
14490Sstevel@tonic-gate 
14500Sstevel@tonic-gate 	/* fill in what's left */
14510Sstevel@tonic-gate 	for (cdip = ddi_get_child(dip); cdip;
14520Sstevel@tonic-gate 	    cdip = ddi_get_next_sibling(cdip)) {
14530Sstevel@tonic-gate 		usba_device_t *usba_device = usba_get_usba_device(cdip);
14540Sstevel@tonic-gate 
14550Sstevel@tonic-gate 		if (usba_device == NULL) {
14560Sstevel@tonic-gate 
14570Sstevel@tonic-gate 			continue;
14580Sstevel@tonic-gate 		}
14590Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
14600Sstevel@tonic-gate 		port = usba_device->usb_port;
14610Sstevel@tonic-gate 		hubd->h_children_dips[port] = cdip;
14620Sstevel@tonic-gate 		hubd->h_port_state[port] &= ~HUBD_CHILD_ZAP;
14630Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
14640Sstevel@tonic-gate 	}
14650Sstevel@tonic-gate 
14660Sstevel@tonic-gate 	/* physically zap the children we didn't find */
14670Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
14680Sstevel@tonic-gate 	for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
14696112Sqz150045 		if (hubd->h_port_state[port] &	HUBD_CHILD_ZAP) {
14700Sstevel@tonic-gate 			/* zap the dip and usba_device structure as well */
14710Sstevel@tonic-gate 			hubd_free_usba_device(hubd, hubd->h_usba_devices[port]);
14720Sstevel@tonic-gate 			hubd->h_children_dips[port] = NULL;
14730Sstevel@tonic-gate 			hubd->h_port_state[port] &= ~HUBD_CHILD_ZAP;
14740Sstevel@tonic-gate 		}
14750Sstevel@tonic-gate 	}
14760Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
14770Sstevel@tonic-gate 
14780Sstevel@tonic-gate 	ndi_devi_exit(dip, circ);
14790Sstevel@tonic-gate 
14800Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
14810Sstevel@tonic-gate 	    "hubd_bus_unconfig: rval=%d", rval);
14820Sstevel@tonic-gate 
14830Sstevel@tonic-gate 	return (rval);
14840Sstevel@tonic-gate }
14850Sstevel@tonic-gate 
14860Sstevel@tonic-gate 
14870Sstevel@tonic-gate /* bus_power entry point */
14880Sstevel@tonic-gate static int
hubd_bus_power(dev_info_t * dip,void * impl_arg,pm_bus_power_op_t op,void * arg,void * result)14890Sstevel@tonic-gate hubd_bus_power(dev_info_t *dip, void *impl_arg, pm_bus_power_op_t op,
14900Sstevel@tonic-gate     void *arg, void *result)
14910Sstevel@tonic-gate {
14920Sstevel@tonic-gate 	hubd_t		*hubd;
14930Sstevel@tonic-gate 	int		rval, pwrup_res;
14940Sstevel@tonic-gate 	usb_port_t	port;
14950Sstevel@tonic-gate 	int		retval = DDI_FAILURE;
14960Sstevel@tonic-gate 	pm_bp_child_pwrchg_t	*bpc;
14970Sstevel@tonic-gate 	pm_bp_nexus_pwrup_t	bpn;
14980Sstevel@tonic-gate 
14990Sstevel@tonic-gate 	hubd = hubd_get_soft_state(dip);
15000Sstevel@tonic-gate 
15010Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubd->h_log_handle,
15020Sstevel@tonic-gate 	    "hubd_bus_power: dip=%p, impl_arg=%p, power_op=%d, arg=%p, "
15036898Sfb209375 	    "result=%d\n", (void *)dip, impl_arg, op, arg, *(int *)result);
15040Sstevel@tonic-gate 
15050Sstevel@tonic-gate 	bpc = (pm_bp_child_pwrchg_t *)arg;
15060Sstevel@tonic-gate 
15070Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
15080Sstevel@tonic-gate 	hubd->h_bus_pwr++;
15090Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
15100Sstevel@tonic-gate 
15110Sstevel@tonic-gate 	switch (op) {
15120Sstevel@tonic-gate 	case BUS_POWER_PRE_NOTIFICATION:
15130Sstevel@tonic-gate 		port = hubd_child_dip2port(hubd, bpc->bpc_dip);
15140Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle,
15150Sstevel@tonic-gate 		    "hubd_bus_power: BUS_POWER_PRE_NOTIFICATION, port=%d",
15160Sstevel@tonic-gate 		    port);
15170Sstevel@tonic-gate 
15180Sstevel@tonic-gate 		/* go to full power if we are powered down */
15190Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
15200Sstevel@tonic-gate 
15210Sstevel@tonic-gate 		/*
15220Sstevel@tonic-gate 		 * If this case completes normally, idle will be in
15230Sstevel@tonic-gate 		 * hubd_bus_power / BUS_POWER_POST_NOTIFICATION
15240Sstevel@tonic-gate 		 */
15250Sstevel@tonic-gate 		hubd_pm_busy_component(hubd, dip, 0);
15260Sstevel@tonic-gate 
15270Sstevel@tonic-gate 		/*
15280Sstevel@tonic-gate 		 * raise power only if we have created the components
15290Sstevel@tonic-gate 		 * and are currently in low power
15300Sstevel@tonic-gate 		 */
15310Sstevel@tonic-gate 		if ((hubd->h_dev_state == USB_DEV_PWRED_DOWN) &&
15320Sstevel@tonic-gate 		    hubd->h_hubpm->hubp_wakeup_enabled) {
15330Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
15340Sstevel@tonic-gate 
15350Sstevel@tonic-gate 			bpn.bpn_comp = 0;
15360Sstevel@tonic-gate 			bpn.bpn_dip = dip;
15370Sstevel@tonic-gate 			bpn.bpn_level = USB_DEV_OS_FULL_PWR;
15380Sstevel@tonic-gate 			bpn.bpn_private = bpc->bpc_private;
15390Sstevel@tonic-gate 
15400Sstevel@tonic-gate 			rval = pm_busop_bus_power(dip, impl_arg,
15410Sstevel@tonic-gate 			    BUS_POWER_NEXUS_PWRUP, (void *)&bpn,
15420Sstevel@tonic-gate 			    (void *)&pwrup_res);
15430Sstevel@tonic-gate 
15440Sstevel@tonic-gate 			if (rval != DDI_SUCCESS || pwrup_res != DDI_SUCCESS) {
15450Sstevel@tonic-gate 				mutex_enter(HUBD_MUTEX(hubd));
15460Sstevel@tonic-gate 				hubd_pm_idle_component(hubd, dip, 0);
15470Sstevel@tonic-gate 				mutex_exit(HUBD_MUTEX(hubd));
15480Sstevel@tonic-gate 
15490Sstevel@tonic-gate 				break;
15500Sstevel@tonic-gate 			}
15510Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
15520Sstevel@tonic-gate 		}
15530Sstevel@tonic-gate 
15540Sstevel@tonic-gate 		/* indicate that child is changing power level */
15550Sstevel@tonic-gate 		hubd->h_port_state[port] |= HUBD_CHILD_PWRLVL_CHNG;
15560Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
15570Sstevel@tonic-gate 
15580Sstevel@tonic-gate 		if ((bpc->bpc_olevel == 0) &&
15590Sstevel@tonic-gate 		    (bpc->bpc_nlevel > bpc->bpc_olevel)) {
15600Sstevel@tonic-gate 			/*
15610Sstevel@tonic-gate 			 * this child is transitioning from power off
15620Sstevel@tonic-gate 			 * to power on state - resume port
15630Sstevel@tonic-gate 			 */
15640Sstevel@tonic-gate 			rval = hubd_resume_port(hubd, port);
15650Sstevel@tonic-gate 			if (rval == USB_SUCCESS) {
15660Sstevel@tonic-gate 				retval = DDI_SUCCESS;
15670Sstevel@tonic-gate 			} else {
15680Sstevel@tonic-gate 				/* reset this flag on failure */
15690Sstevel@tonic-gate 				mutex_enter(HUBD_MUTEX(hubd));
15700Sstevel@tonic-gate 				hubd->h_port_state[port] &=
15710Sstevel@tonic-gate 				    ~HUBD_CHILD_PWRLVL_CHNG;
15720Sstevel@tonic-gate 				hubd_pm_idle_component(hubd, dip, 0);
15730Sstevel@tonic-gate 				mutex_exit(HUBD_MUTEX(hubd));
15740Sstevel@tonic-gate 			}
15750Sstevel@tonic-gate 		} else {
15760Sstevel@tonic-gate 			retval = DDI_SUCCESS;
15770Sstevel@tonic-gate 		}
15780Sstevel@tonic-gate 
15790Sstevel@tonic-gate 		break;
15800Sstevel@tonic-gate 	case BUS_POWER_POST_NOTIFICATION:
15810Sstevel@tonic-gate 		port = hubd_child_dip2port(hubd, bpc->bpc_dip);
15820Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle,
15830Sstevel@tonic-gate 		    "hubd_bus_power: BUS_POWER_POST_NOTIFICATION, port=%d",
15840Sstevel@tonic-gate 		    port);
15850Sstevel@tonic-gate 
15860Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
15870Sstevel@tonic-gate 		hubd->h_port_state[port] &= ~HUBD_CHILD_PWRLVL_CHNG;
15880Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
15890Sstevel@tonic-gate 
15900Sstevel@tonic-gate 		/* record child's pwr and suspend port if required */
15910Sstevel@tonic-gate 		rval = hubd_post_power(hubd, port, bpc, *(int *)result);
15920Sstevel@tonic-gate 		if (rval == USB_SUCCESS) {
15930Sstevel@tonic-gate 
15940Sstevel@tonic-gate 			retval = DDI_SUCCESS;
15950Sstevel@tonic-gate 		}
15960Sstevel@tonic-gate 
15970Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
15980Sstevel@tonic-gate 
15990Sstevel@tonic-gate 		/*
16000Sstevel@tonic-gate 		 * Matching idle for the busy in
16010Sstevel@tonic-gate 		 * hubd_bus_power / BUS_POWER_PRE_NOTIFICATION
16020Sstevel@tonic-gate 		 */
16030Sstevel@tonic-gate 		hubd_pm_idle_component(hubd, dip, 0);
16040Sstevel@tonic-gate 
16050Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
16060Sstevel@tonic-gate 
16070Sstevel@tonic-gate 		break;
16080Sstevel@tonic-gate 	default:
16090Sstevel@tonic-gate 		retval = pm_busop_bus_power(dip, impl_arg, op, arg, result);
16100Sstevel@tonic-gate 
16110Sstevel@tonic-gate 		break;
16120Sstevel@tonic-gate 	}
16130Sstevel@tonic-gate 
16140Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
16150Sstevel@tonic-gate 	hubd->h_bus_pwr--;
16160Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
16170Sstevel@tonic-gate 
16180Sstevel@tonic-gate 	return (retval);
16190Sstevel@tonic-gate }
16200Sstevel@tonic-gate 
16210Sstevel@tonic-gate 
16220Sstevel@tonic-gate /*
16230Sstevel@tonic-gate  * functions to handle power transition for OS levels 0 -> 3
16240Sstevel@tonic-gate  */
16250Sstevel@tonic-gate static int
hubd_pwrlvl0(hubd_t * hubd)16260Sstevel@tonic-gate hubd_pwrlvl0(hubd_t *hubd)
16270Sstevel@tonic-gate {
16280Sstevel@tonic-gate 	hub_power_t	*hubpm;
16290Sstevel@tonic-gate 
16300Sstevel@tonic-gate 	/* We can't power down if hotplug thread is running */
16310Sstevel@tonic-gate 	if (hubd->h_hotplug_thread || hubd->h_hubpm->hubp_busy_pm ||
16320Sstevel@tonic-gate 	    (hubd_can_suspend(hubd) == USB_FAILURE)) {
16330Sstevel@tonic-gate 
16340Sstevel@tonic-gate 		return (USB_FAILURE);
16350Sstevel@tonic-gate 	}
16360Sstevel@tonic-gate 
16370Sstevel@tonic-gate 	switch (hubd->h_dev_state) {
16380Sstevel@tonic-gate 	case USB_DEV_ONLINE:
16390Sstevel@tonic-gate 		hubpm = hubd->h_hubpm;
16400Sstevel@tonic-gate 
16410Sstevel@tonic-gate 		/*
16420Sstevel@tonic-gate 		 * To avoid race with bus_power pre_notify on check over
16430Sstevel@tonic-gate 		 * dev_state, we need to correctly set the dev state
16440Sstevel@tonic-gate 		 * before the mutex is dropped in stop polling.
16450Sstevel@tonic-gate 		 */
16460Sstevel@tonic-gate 		hubd->h_dev_state = USB_DEV_PWRED_DOWN;
16470Sstevel@tonic-gate 		hubpm->hubp_current_power = USB_DEV_OS_PWR_OFF;
16480Sstevel@tonic-gate 
16490Sstevel@tonic-gate 		/*
16500Sstevel@tonic-gate 		 * if we are the root hub, do not stop polling
16510Sstevel@tonic-gate 		 * otherwise, we will never see a resume
16520Sstevel@tonic-gate 		 */
16530Sstevel@tonic-gate 		if (usba_is_root_hub(hubd->h_dip)) {
16540Sstevel@tonic-gate 			/* place holder to implement Global Suspend */
16550Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
16560Sstevel@tonic-gate 			    "Global Suspend: Not Yet Implemented");
16570Sstevel@tonic-gate 		} else {
16580Sstevel@tonic-gate 			hubd_stop_polling(hubd);
16590Sstevel@tonic-gate 		}
16600Sstevel@tonic-gate 
16610Sstevel@tonic-gate 		/* Issue USB D3 command to the device here */
16620Sstevel@tonic-gate 		(void) usb_set_device_pwrlvl3(hubd->h_dip);
16630Sstevel@tonic-gate 
16640Sstevel@tonic-gate 		break;
16650Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
16660Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
16670Sstevel@tonic-gate 	case USB_DEV_PWRED_DOWN:
16680Sstevel@tonic-gate 	default:
16690Sstevel@tonic-gate 
16700Sstevel@tonic-gate 		break;
16710Sstevel@tonic-gate 	}
16720Sstevel@tonic-gate 
16730Sstevel@tonic-gate 	return (USB_SUCCESS);
16740Sstevel@tonic-gate }
16750Sstevel@tonic-gate 
16760Sstevel@tonic-gate 
16770Sstevel@tonic-gate /* ARGSUSED */
16780Sstevel@tonic-gate static int
hubd_pwrlvl1(hubd_t * hubd)16790Sstevel@tonic-gate hubd_pwrlvl1(hubd_t *hubd)
16800Sstevel@tonic-gate {
16810Sstevel@tonic-gate 	/* Issue USB D2 command to the device here */
16820Sstevel@tonic-gate 	(void) usb_set_device_pwrlvl2(hubd->h_dip);
16830Sstevel@tonic-gate 
16840Sstevel@tonic-gate 	return (USB_FAILURE);
16850Sstevel@tonic-gate }
16860Sstevel@tonic-gate 
16870Sstevel@tonic-gate 
16880Sstevel@tonic-gate /* ARGSUSED */
16890Sstevel@tonic-gate static int
hubd_pwrlvl2(hubd_t * hubd)16900Sstevel@tonic-gate hubd_pwrlvl2(hubd_t *hubd)
16910Sstevel@tonic-gate {
16920Sstevel@tonic-gate 	/* Issue USB D1 command to the device here */
16930Sstevel@tonic-gate 	(void) usb_set_device_pwrlvl1(hubd->h_dip);
16940Sstevel@tonic-gate 
16950Sstevel@tonic-gate 	return (USB_FAILURE);
16960Sstevel@tonic-gate }
16970Sstevel@tonic-gate 
16980Sstevel@tonic-gate 
16990Sstevel@tonic-gate static int
hubd_pwrlvl3(hubd_t * hubd)17000Sstevel@tonic-gate hubd_pwrlvl3(hubd_t *hubd)
17010Sstevel@tonic-gate {
17020Sstevel@tonic-gate 	hub_power_t	*hubpm;
17030Sstevel@tonic-gate 	int		rval;
17040Sstevel@tonic-gate 
17050Sstevel@tonic-gate 	USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, "hubd_pwrlvl3");
17060Sstevel@tonic-gate 
17070Sstevel@tonic-gate 	hubpm = hubd->h_hubpm;
17080Sstevel@tonic-gate 	switch (hubd->h_dev_state) {
17090Sstevel@tonic-gate 	case USB_DEV_PWRED_DOWN:
17100Sstevel@tonic-gate 		ASSERT(hubpm->hubp_current_power == USB_DEV_OS_PWR_OFF);
17110Sstevel@tonic-gate 		if (usba_is_root_hub(hubd->h_dip)) {
17120Sstevel@tonic-gate 			/* implement global resume here */
17130Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_PM,
17140Sstevel@tonic-gate 			    hubd->h_log_handle,
17150Sstevel@tonic-gate 			    "Global Resume: Not Yet Implemented");
17160Sstevel@tonic-gate 		}
17170Sstevel@tonic-gate 		/* Issue USB D0 command to the device here */
17180Sstevel@tonic-gate 		rval = usb_set_device_pwrlvl0(hubd->h_dip);
17190Sstevel@tonic-gate 		ASSERT(rval == USB_SUCCESS);
17200Sstevel@tonic-gate 		hubd->h_dev_state = USB_DEV_ONLINE;
17210Sstevel@tonic-gate 		hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR;
17220Sstevel@tonic-gate 		hubpm->hubp_time_at_full_power = ddi_get_time();
17230Sstevel@tonic-gate 		hubd_start_polling(hubd, 0);
17240Sstevel@tonic-gate 
17250Sstevel@tonic-gate 		/* FALLTHRU */
17260Sstevel@tonic-gate 	case USB_DEV_ONLINE:
17270Sstevel@tonic-gate 		/* we are already in full power */
17280Sstevel@tonic-gate 
17290Sstevel@tonic-gate 		/* FALLTHRU */
17300Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
17310Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
17320Sstevel@tonic-gate 		/*
17330Sstevel@tonic-gate 		 * PM framework tries to put you in full power
17340Sstevel@tonic-gate 		 * during system shutdown. If we are disconnected
17350Sstevel@tonic-gate 		 * return success. Also, we should not change state
17360Sstevel@tonic-gate 		 * when we are disconnected or suspended or about to
17370Sstevel@tonic-gate 		 * transition to that state
17380Sstevel@tonic-gate 		 */
17390Sstevel@tonic-gate 
17400Sstevel@tonic-gate 		return (USB_SUCCESS);
17410Sstevel@tonic-gate 	default:
17420Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
17430Sstevel@tonic-gate 		    "hubd_pwrlvl3: Illegal dev_state=%d", hubd->h_dev_state);
17440Sstevel@tonic-gate 
17450Sstevel@tonic-gate 		return (USB_FAILURE);
17460Sstevel@tonic-gate 	}
17470Sstevel@tonic-gate }
17480Sstevel@tonic-gate 
17490Sstevel@tonic-gate 
17500Sstevel@tonic-gate /* power entry point */
17510Sstevel@tonic-gate /* ARGSUSED */
17520Sstevel@tonic-gate int
usba_hubdi_power(dev_info_t * dip,int comp,int level)17530Sstevel@tonic-gate usba_hubdi_power(dev_info_t *dip, int comp, int level)
17540Sstevel@tonic-gate {
17550Sstevel@tonic-gate 	hubd_t		*hubd;
17560Sstevel@tonic-gate 	hub_power_t	*hubpm;
17570Sstevel@tonic-gate 	int		retval;
17580Sstevel@tonic-gate 	int		circ;
17590Sstevel@tonic-gate 
17600Sstevel@tonic-gate 	hubd = hubd_get_soft_state(dip);
17610Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle,
17620Sstevel@tonic-gate 	    "usba_hubdi_power: level=%d", level);
17630Sstevel@tonic-gate 
17640Sstevel@tonic-gate 	ndi_devi_enter(dip, &circ);
17650Sstevel@tonic-gate 
17660Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
17670Sstevel@tonic-gate 	hubpm = hubd->h_hubpm;
17680Sstevel@tonic-gate 
17690Sstevel@tonic-gate 	/* check if we are transitioning to a legal power level */
17700Sstevel@tonic-gate 	if (USB_DEV_PWRSTATE_OK(hubpm->hubp_pwr_states, level)) {
17710Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_HUBDI, hubd->h_log_handle,
17720Sstevel@tonic-gate 		    "usba_hubdi_power: illegal power level=%d "
17730Sstevel@tonic-gate 		    "hubp_pwr_states=0x%x", level, hubpm->hubp_pwr_states);
17740Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
17750Sstevel@tonic-gate 
17760Sstevel@tonic-gate 		ndi_devi_exit(dip, circ);
17770Sstevel@tonic-gate 
17780Sstevel@tonic-gate 		return (DDI_FAILURE);
17790Sstevel@tonic-gate 	}
17800Sstevel@tonic-gate 
17810Sstevel@tonic-gate 	switch (level) {
17820Sstevel@tonic-gate 	case USB_DEV_OS_PWR_OFF:
17830Sstevel@tonic-gate 		retval = hubd_pwrlvl0(hubd);
17840Sstevel@tonic-gate 
17850Sstevel@tonic-gate 		break;
17860Sstevel@tonic-gate 	case USB_DEV_OS_PWR_1:
17870Sstevel@tonic-gate 		retval = hubd_pwrlvl1(hubd);
17880Sstevel@tonic-gate 
17890Sstevel@tonic-gate 		break;
17900Sstevel@tonic-gate 	case USB_DEV_OS_PWR_2:
17910Sstevel@tonic-gate 		retval = hubd_pwrlvl2(hubd);
17920Sstevel@tonic-gate 
17930Sstevel@tonic-gate 		break;
17940Sstevel@tonic-gate 	case USB_DEV_OS_FULL_PWR:
17950Sstevel@tonic-gate 		retval = hubd_pwrlvl3(hubd);
17960Sstevel@tonic-gate 
17970Sstevel@tonic-gate 		break;
17980Sstevel@tonic-gate 	}
17990Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
18000Sstevel@tonic-gate 
18010Sstevel@tonic-gate 	ndi_devi_exit(dip, circ);
18020Sstevel@tonic-gate 
18030Sstevel@tonic-gate 	return ((retval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
18040Sstevel@tonic-gate }
18050Sstevel@tonic-gate 
18060Sstevel@tonic-gate 
18070Sstevel@tonic-gate /* power entry point for the root hub */
18080Sstevel@tonic-gate int
usba_hubdi_root_hub_power(dev_info_t * dip,int comp,int level)18090Sstevel@tonic-gate usba_hubdi_root_hub_power(dev_info_t *dip, int comp, int level)
18100Sstevel@tonic-gate {
18110Sstevel@tonic-gate 	return (usba_hubdi_power(dip, comp, level));
18120Sstevel@tonic-gate }
18130Sstevel@tonic-gate 
18140Sstevel@tonic-gate 
18150Sstevel@tonic-gate /*
18160Sstevel@tonic-gate  * standard driver entry points support code
18170Sstevel@tonic-gate  */
18180Sstevel@tonic-gate int
usba_hubdi_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)18190Sstevel@tonic-gate usba_hubdi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
18200Sstevel@tonic-gate {
18210Sstevel@tonic-gate 	int			instance = ddi_get_instance(dip);
18220Sstevel@tonic-gate 	hubd_t			*hubd = NULL;
18230Sstevel@tonic-gate 	int			i, rval;
18240Sstevel@tonic-gate 	int			minor;
18256112Sqz150045 	uint8_t			ports_count;
18260Sstevel@tonic-gate 	char			*log_name = NULL;
18270Sstevel@tonic-gate 	const char		*root_hub_drvname;
18280Sstevel@tonic-gate 	usb_ep_data_t		*ep_data;
18290Sstevel@tonic-gate 	usba_device_t		*child_ud = NULL;
18300Sstevel@tonic-gate 	usb_dev_descr_t		*usb_dev_descr;
18310Sstevel@tonic-gate 	usb_port_status_t	parent_port_status, child_port_status;
18320Sstevel@tonic-gate 
18330Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubdi_log_handle,
18344763Slg150142 	    "hubd_attach instance %d, cmd=0x%x", instance, cmd);
18350Sstevel@tonic-gate 
18360Sstevel@tonic-gate 	switch (cmd) {
18370Sstevel@tonic-gate 	case DDI_ATTACH:
18380Sstevel@tonic-gate 
18390Sstevel@tonic-gate 		break;
18400Sstevel@tonic-gate 	case DDI_RESUME:
18410Sstevel@tonic-gate 		hubd_cpr_resume(dip);
18420Sstevel@tonic-gate 
18430Sstevel@tonic-gate 		return (DDI_SUCCESS);
18440Sstevel@tonic-gate 	default:
18450Sstevel@tonic-gate 		return (DDI_FAILURE);
18460Sstevel@tonic-gate 	}
18470Sstevel@tonic-gate 
18480Sstevel@tonic-gate 	/*
18490Sstevel@tonic-gate 	 * Allocate softc information.
18500Sstevel@tonic-gate 	 */
18510Sstevel@tonic-gate 	if (usba_is_root_hub(dip)) {
18520Sstevel@tonic-gate 		/* soft state has already been allocated */
18530Sstevel@tonic-gate 		hubd = hubd_get_soft_state(dip);
18540Sstevel@tonic-gate 		minor = HUBD_IS_ROOT_HUB;
18550Sstevel@tonic-gate 
18560Sstevel@tonic-gate 		/* generate readable labels for different root hubs */
18570Sstevel@tonic-gate 		root_hub_drvname = ddi_driver_name(dip);
18580Sstevel@tonic-gate 		if (strcmp(root_hub_drvname, "ehci") == 0) {
18590Sstevel@tonic-gate 			log_name = "eusb";
18600Sstevel@tonic-gate 		} else if (strcmp(root_hub_drvname, "uhci") == 0) {
18610Sstevel@tonic-gate 			log_name = "uusb";
18620Sstevel@tonic-gate 		} else {
18630Sstevel@tonic-gate 			/* std. for ohci */
18640Sstevel@tonic-gate 			log_name = "usb";
18650Sstevel@tonic-gate 		}
18660Sstevel@tonic-gate 	} else {
18670Sstevel@tonic-gate 		rval = ddi_soft_state_zalloc(hubd_statep, instance);
18680Sstevel@tonic-gate 		minor = 0;
18690Sstevel@tonic-gate 
18700Sstevel@tonic-gate 		if (rval != DDI_SUCCESS) {
18710Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
18720Sstevel@tonic-gate 			    "cannot allocate soft state (%d)", instance);
18730Sstevel@tonic-gate 			goto fail;
18740Sstevel@tonic-gate 		}
18750Sstevel@tonic-gate 
18760Sstevel@tonic-gate 		hubd = hubd_get_soft_state(dip);
18770Sstevel@tonic-gate 		if (hubd == NULL) {
18780Sstevel@tonic-gate 			goto fail;
18790Sstevel@tonic-gate 		}
18800Sstevel@tonic-gate 	}
18810Sstevel@tonic-gate 
18820Sstevel@tonic-gate 	hubd->h_log_handle = usb_alloc_log_hdl(dip, log_name, &hubd_errlevel,
18834763Slg150142 	    &hubd_errmask, &hubd_instance_debug, 0);
18840Sstevel@tonic-gate 
18850Sstevel@tonic-gate 	hubd->h_usba_device	= child_ud = usba_get_usba_device(dip);
18860Sstevel@tonic-gate 	hubd->h_dip		= dip;
18870Sstevel@tonic-gate 	hubd->h_instance	= instance;
18880Sstevel@tonic-gate 
18890Sstevel@tonic-gate 	mutex_enter(&child_ud->usb_mutex);
18900Sstevel@tonic-gate 	child_port_status = child_ud->usb_port_status;
18910Sstevel@tonic-gate 	usb_dev_descr = child_ud->usb_dev_descr;
18920Sstevel@tonic-gate 	parent_port_status = (child_ud->usb_hs_hub_usba_dev) ?
18930Sstevel@tonic-gate 	    child_ud->usb_hs_hub_usba_dev->usb_port_status : 0;
18940Sstevel@tonic-gate 	mutex_exit(&child_ud->usb_mutex);
18950Sstevel@tonic-gate 
18960Sstevel@tonic-gate 	if ((child_port_status == USBA_FULL_SPEED_DEV) &&
18970Sstevel@tonic-gate 	    (parent_port_status == USBA_HIGH_SPEED_DEV) &&
18980Sstevel@tonic-gate 	    (usb_dev_descr->bcdUSB == 0x100)) {
18990Sstevel@tonic-gate 		USB_DPRINTF_L0(DPRINT_MASK_ATTA, hubd->h_log_handle,
19000Sstevel@tonic-gate 		    "Use of a USB1.0 hub behind a high speed port may "
19010Sstevel@tonic-gate 		    "cause unexpected failures");
19020Sstevel@tonic-gate 	}
19030Sstevel@tonic-gate 
19040Sstevel@tonic-gate 	hubd->h_pipe_policy.pp_max_async_reqs = 1;
19050Sstevel@tonic-gate 
19060Sstevel@tonic-gate 	/* register with USBA as client driver */
19070Sstevel@tonic-gate 	if (usb_client_attach(dip, USBDRV_VERSION, 0) != USB_SUCCESS) {
19080Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
19090Sstevel@tonic-gate 		    "client attach failed");
19100Sstevel@tonic-gate 
19110Sstevel@tonic-gate 		goto fail;
19120Sstevel@tonic-gate 	}
19130Sstevel@tonic-gate 
19140Sstevel@tonic-gate 	if (usb_get_dev_data(dip, &hubd->h_dev_data,
19150Sstevel@tonic-gate 	    USB_PARSE_LVL_IF, 0) != USB_SUCCESS) {
19160Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
19170Sstevel@tonic-gate 		    "cannot get dev_data");
19180Sstevel@tonic-gate 
19190Sstevel@tonic-gate 		goto fail;
19200Sstevel@tonic-gate 	}
19210Sstevel@tonic-gate 
19220Sstevel@tonic-gate 	if ((ep_data = usb_lookup_ep_data(dip, hubd->h_dev_data,
19230Sstevel@tonic-gate 	    hubd->h_dev_data->dev_curr_if, 0, 0,
19240Sstevel@tonic-gate 	    (uint_t)USB_EP_ATTR_INTR, (uint_t)USB_EP_DIR_IN)) == NULL) {
19250Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
19260Sstevel@tonic-gate 		    "no interrupt IN endpoint found");
19270Sstevel@tonic-gate 
19280Sstevel@tonic-gate 		goto fail;
19290Sstevel@tonic-gate 	}
19300Sstevel@tonic-gate 
19310Sstevel@tonic-gate 	hubd->h_ep1_descr = ep_data->ep_descr;
19320Sstevel@tonic-gate 	hubd->h_default_pipe = hubd->h_dev_data->dev_default_ph;
19330Sstevel@tonic-gate 
19340Sstevel@tonic-gate 	mutex_init(HUBD_MUTEX(hubd), NULL, MUTEX_DRIVER,
19354763Slg150142 	    hubd->h_dev_data->dev_iblock_cookie);
19360Sstevel@tonic-gate 	cv_init(&hubd->h_cv_reset_port, NULL, CV_DRIVER, NULL);
19374844Slg150142 	cv_init(&hubd->h_cv_hotplug_dev, NULL, CV_DRIVER, NULL);
19380Sstevel@tonic-gate 
19390Sstevel@tonic-gate 	hubd->h_init_state |= HUBD_LOCKS_DONE;
19400Sstevel@tonic-gate 
19410Sstevel@tonic-gate 	usb_free_descr_tree(dip, hubd->h_dev_data);
19420Sstevel@tonic-gate 
19430Sstevel@tonic-gate 	/*
19440Sstevel@tonic-gate 	 * register this hub instance with usba
19450Sstevel@tonic-gate 	 */
19460Sstevel@tonic-gate 	rval = usba_hubdi_register(dip, 0);
19470Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
1948978Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
19490Sstevel@tonic-gate 		    "usba_hubdi_register failed");
19500Sstevel@tonic-gate 		goto fail;
19510Sstevel@tonic-gate 	}
19520Sstevel@tonic-gate 
19530Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
19540Sstevel@tonic-gate 	hubd->h_init_state |= HUBD_HUBDI_REGISTERED;
19550Sstevel@tonic-gate 	hubd->h_dev_state = USB_DEV_ONLINE;
19560Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
19570Sstevel@tonic-gate 
19580Sstevel@tonic-gate 	/* now create components to power manage this device */
19590Sstevel@tonic-gate 	hubd_create_pm_components(dip, hubd);
19600Sstevel@tonic-gate 
19610Sstevel@tonic-gate 	/*
19620Sstevel@tonic-gate 	 * Event handling: definition and registration
19630Sstevel@tonic-gate 	 *
19640Sstevel@tonic-gate 	 * first the  definition:
19650Sstevel@tonic-gate 	 * get event handle
19660Sstevel@tonic-gate 	 */
19670Sstevel@tonic-gate 	(void) ndi_event_alloc_hdl(dip, 0, &hubd->h_ndi_event_hdl, NDI_SLEEP);
19680Sstevel@tonic-gate 
19690Sstevel@tonic-gate 	/* bind event set to the handle */
19700Sstevel@tonic-gate 	if (ndi_event_bind_set(hubd->h_ndi_event_hdl, &hubd_ndi_events,
19710Sstevel@tonic-gate 	    NDI_SLEEP)) {
19720Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
19730Sstevel@tonic-gate 		    "binding event set failed");
19740Sstevel@tonic-gate 
19750Sstevel@tonic-gate 		goto fail;
19760Sstevel@tonic-gate 	}
19770Sstevel@tonic-gate 
19780Sstevel@tonic-gate 	/* event registration */
19790Sstevel@tonic-gate 	if (hubd_register_events(hubd) != USB_SUCCESS) {
1980978Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
19810Sstevel@tonic-gate 		    "hubd_register_events failed");
19820Sstevel@tonic-gate 
19830Sstevel@tonic-gate 		goto fail;
19840Sstevel@tonic-gate 	}
19850Sstevel@tonic-gate 
19860Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
19870Sstevel@tonic-gate 	hubd->h_init_state |= HUBD_EVENTS_REGISTERED;
19880Sstevel@tonic-gate 
19891001Ssl147100 	if ((hubd_get_hub_descriptor(hubd)) != USB_SUCCESS) {
19901001Ssl147100 		mutex_exit(HUBD_MUTEX(hubd));
19911001Ssl147100 
19921001Ssl147100 		goto fail;
19931001Ssl147100 	}
19941001Ssl147100 
19951001Ssl147100 	if (ddi_prop_exists(DDI_DEV_T_ANY, dip,
19961001Ssl147100 	    (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
19971001Ssl147100 	    "hub-ignore-power-budget") == 1) {
19981001Ssl147100 		hubd->h_ignore_pwr_budget = B_TRUE;
19991001Ssl147100 	} else {
20001001Ssl147100 		hubd->h_ignore_pwr_budget = B_FALSE;
20011001Ssl147100 
20021001Ssl147100 		/* initialize hub power budget variables */
20031001Ssl147100 		if (hubd_init_power_budget(hubd) != USB_SUCCESS) {
20041001Ssl147100 			USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
20051001Ssl147100 			    "hubd_init_power_budget failed");
20061001Ssl147100 			mutex_exit(HUBD_MUTEX(hubd));
20071001Ssl147100 
20081001Ssl147100 			goto fail;
20091001Ssl147100 		}
20101001Ssl147100 	}
20111001Ssl147100 
20120Sstevel@tonic-gate 	/* initialize and create children */
20130Sstevel@tonic-gate 	if (hubd_check_ports(hubd) != USB_SUCCESS) {
2014978Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
20150Sstevel@tonic-gate 		    "hubd_check_ports failed");
20160Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
20170Sstevel@tonic-gate 
20180Sstevel@tonic-gate 		goto fail;
20190Sstevel@tonic-gate 	}
20200Sstevel@tonic-gate 
20210Sstevel@tonic-gate 	/*
20220Sstevel@tonic-gate 	 * create cfgadm nodes
20230Sstevel@tonic-gate 	 */
20240Sstevel@tonic-gate 	hubd->h_ancestry_str = (char *)kmem_zalloc(HUBD_APID_NAMELEN, KM_SLEEP);
20250Sstevel@tonic-gate 	hubd_get_ancestry_str(hubd);
20260Sstevel@tonic-gate 
20270Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
20280Sstevel@tonic-gate 	    "#ports=0x%x", hubd->h_hub_descr.bNbrPorts);
20290Sstevel@tonic-gate 
20300Sstevel@tonic-gate 	for (i = 1; i <= hubd->h_hub_descr.bNbrPorts; i++) {
20310Sstevel@tonic-gate 		char ap_name[HUBD_APID_NAMELEN];
20320Sstevel@tonic-gate 
20330Sstevel@tonic-gate 		(void) snprintf(ap_name, HUBD_APID_NAMELEN, "%s%d",
20340Sstevel@tonic-gate 		    hubd->h_ancestry_str, i);
20350Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
20360Sstevel@tonic-gate 		    "ap_name=%s", ap_name);
20370Sstevel@tonic-gate 
20380Sstevel@tonic-gate 		if (ddi_create_minor_node(dip, ap_name, S_IFCHR, instance,
20390Sstevel@tonic-gate 		    DDI_NT_USB_ATTACHMENT_POINT, 0) != DDI_SUCCESS) {
2040978Sfrits 			USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
20410Sstevel@tonic-gate 			    "cannot create attachment point node (%d)",
20420Sstevel@tonic-gate 			    instance);
20430Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
20440Sstevel@tonic-gate 
20450Sstevel@tonic-gate 			goto fail;
20460Sstevel@tonic-gate 		}
20470Sstevel@tonic-gate 	}
20486112Sqz150045 
20496112Sqz150045 	ports_count = hubd->h_hub_descr.bNbrPorts;
20500Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
20510Sstevel@tonic-gate 
20520Sstevel@tonic-gate 	/* create minor nodes */
20530Sstevel@tonic-gate 	if (ddi_create_minor_node(dip, "hubd", S_IFCHR,
20540Sstevel@tonic-gate 	    instance | minor, DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
20550Sstevel@tonic-gate 
2056978Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
20570Sstevel@tonic-gate 		    "cannot create devctl minor node (%d)", instance);
20580Sstevel@tonic-gate 
20590Sstevel@tonic-gate 		goto fail;
20600Sstevel@tonic-gate 	}
20610Sstevel@tonic-gate 
20620Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
20630Sstevel@tonic-gate 	hubd->h_init_state |= HUBD_MINOR_NODE_CREATED;
20640Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
20650Sstevel@tonic-gate 
20666112Sqz150045 	if (ndi_prop_update_int(DDI_DEV_T_NONE, dip,
20676112Sqz150045 	    "usb-port-count", ports_count) != DDI_PROP_SUCCESS) {
20686112Sqz150045 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
20699430SRaymond.Chen@Sun.COM 		    "usb-port-count update failed");
20706112Sqz150045 	}
20716112Sqz150045 
20720Sstevel@tonic-gate 	/*
20730Sstevel@tonic-gate 	 * host controller driver has already reported this dev
20740Sstevel@tonic-gate 	 * if we are the root hub
20750Sstevel@tonic-gate 	 */
20760Sstevel@tonic-gate 	if (!usba_is_root_hub(dip)) {
20770Sstevel@tonic-gate 		ddi_report_dev(dip);
20780Sstevel@tonic-gate 	}
20790Sstevel@tonic-gate 
20800Sstevel@tonic-gate 	/* enable deathrow thread */
20810Sstevel@tonic-gate 	hubd->h_cleanup_enabled = B_TRUE;
20820Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
20830Sstevel@tonic-gate 	hubd_pm_idle_component(hubd, dip, 0);
20840Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
20850Sstevel@tonic-gate 
20860Sstevel@tonic-gate 	return (DDI_SUCCESS);
20870Sstevel@tonic-gate 
20880Sstevel@tonic-gate fail:
20890Sstevel@tonic-gate 	{
20900Sstevel@tonic-gate 		char *pathname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
20910Sstevel@tonic-gate 
20923435Slg150142 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
20930Sstevel@tonic-gate 		    "cannot attach %s", ddi_pathname(dip, pathname));
20940Sstevel@tonic-gate 
20950Sstevel@tonic-gate 		kmem_free(pathname, MAXPATHLEN);
20960Sstevel@tonic-gate 	}
20970Sstevel@tonic-gate 
20980Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
20990Sstevel@tonic-gate 	hubd_pm_idle_component(hubd, dip, 0);
21000Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
21010Sstevel@tonic-gate 
21020Sstevel@tonic-gate 	if (hubd) {
21030Sstevel@tonic-gate 		rval = hubd_cleanup(dip, hubd);
21040Sstevel@tonic-gate 		if (rval != USB_SUCCESS) {
2105978Sfrits 			USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
21060Sstevel@tonic-gate 			    "failure to complete cleanup after attach failure");
21070Sstevel@tonic-gate 		}
21080Sstevel@tonic-gate 	}
21090Sstevel@tonic-gate 
21100Sstevel@tonic-gate 	return (DDI_FAILURE);
21110Sstevel@tonic-gate }
21120Sstevel@tonic-gate 
21130Sstevel@tonic-gate 
21140Sstevel@tonic-gate int
usba_hubdi_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)21150Sstevel@tonic-gate usba_hubdi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
21160Sstevel@tonic-gate {
21170Sstevel@tonic-gate 	hubd_t	*hubd = hubd_get_soft_state(dip);
21180Sstevel@tonic-gate 	int	rval;
21190Sstevel@tonic-gate 
21200Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
21210Sstevel@tonic-gate 	    "hubd_detach: cmd=0x%x", cmd);
21220Sstevel@tonic-gate 
21230Sstevel@tonic-gate 	switch (cmd) {
21240Sstevel@tonic-gate 	case DDI_DETACH:
21250Sstevel@tonic-gate 		rval = hubd_cleanup(dip, hubd);
21260Sstevel@tonic-gate 
21270Sstevel@tonic-gate 		return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
21280Sstevel@tonic-gate 	case DDI_SUSPEND:
21290Sstevel@tonic-gate 		rval = hubd_cpr_suspend(hubd);
21300Sstevel@tonic-gate 
21310Sstevel@tonic-gate 		return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
21320Sstevel@tonic-gate 	default:
21330Sstevel@tonic-gate 		return (DDI_FAILURE);
21340Sstevel@tonic-gate 	}
21350Sstevel@tonic-gate }
21360Sstevel@tonic-gate 
21370Sstevel@tonic-gate 
21380Sstevel@tonic-gate /*
21390Sstevel@tonic-gate  * hubd_setdevaddr
21400Sstevel@tonic-gate  *	set the device addrs on this port
21410Sstevel@tonic-gate  */
21420Sstevel@tonic-gate static int
hubd_setdevaddr(hubd_t * hubd,usb_port_t port)21430Sstevel@tonic-gate hubd_setdevaddr(hubd_t *hubd, usb_port_t port)
21440Sstevel@tonic-gate {
21450Sstevel@tonic-gate 	int		rval;
21460Sstevel@tonic-gate 	usb_cr_t	completion_reason;
21470Sstevel@tonic-gate 	usb_cb_flags_t	cb_flags;
21480Sstevel@tonic-gate 	usb_pipe_handle_t ph;
21490Sstevel@tonic-gate 	dev_info_t	*child_dip = NULL;
21500Sstevel@tonic-gate 	uchar_t		address = 0;
21510Sstevel@tonic-gate 	usba_device_t	*usba_device;
21520Sstevel@tonic-gate 	int		retry = 0;
21530Sstevel@tonic-gate 	long		time_delay;
21540Sstevel@tonic-gate 
21550Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
21560Sstevel@tonic-gate 	    "hubd_setdevaddr: port=%d", port);
21570Sstevel@tonic-gate 
21580Sstevel@tonic-gate 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
21590Sstevel@tonic-gate 
21600Sstevel@tonic-gate 	child_dip = hubd->h_children_dips[port];
21610Sstevel@tonic-gate 	address = hubd->h_usba_devices[port]->usb_addr;
21620Sstevel@tonic-gate 	usba_device = hubd->h_usba_devices[port];
21630Sstevel@tonic-gate 
21640Sstevel@tonic-gate 	/* close the default pipe with addr x */
21650Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
21660Sstevel@tonic-gate 	ph = usba_get_dflt_pipe_handle(child_dip);
21670Sstevel@tonic-gate 	usb_pipe_close(child_dip, ph,
21680Sstevel@tonic-gate 	    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
21690Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
21700Sstevel@tonic-gate 
21710Sstevel@tonic-gate 	/*
21720Sstevel@tonic-gate 	 * As this device has been reset, temporarily
21730Sstevel@tonic-gate 	 * assign the default address
21740Sstevel@tonic-gate 	 */
21750Sstevel@tonic-gate 	mutex_enter(&usba_device->usb_mutex);
21760Sstevel@tonic-gate 	address = usba_device->usb_addr;
21770Sstevel@tonic-gate 	usba_device->usb_addr = USBA_DEFAULT_ADDR;
21780Sstevel@tonic-gate 	mutex_exit(&usba_device->usb_mutex);
21790Sstevel@tonic-gate 
21800Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
21810Sstevel@tonic-gate 
21820Sstevel@tonic-gate 	time_delay = drv_usectohz(hubd_device_delay / 20);
21830Sstevel@tonic-gate 	for (retry = 0; retry < hubd_retry_enumerate; retry++) {
21840Sstevel@tonic-gate 
21850Sstevel@tonic-gate 		/* open child's default pipe with USBA_DEFAULT_ADDR */
21860Sstevel@tonic-gate 		if (usb_pipe_open(child_dip, NULL, NULL,
21870Sstevel@tonic-gate 		    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph) !=
21880Sstevel@tonic-gate 		    USB_SUCCESS) {
21890Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
21900Sstevel@tonic-gate 			    "hubd_setdevaddr: Unable to open default pipe");
21910Sstevel@tonic-gate 
21920Sstevel@tonic-gate 			break;
21930Sstevel@tonic-gate 		}
21940Sstevel@tonic-gate 
21950Sstevel@tonic-gate 		/* Set the address of the device */
21960Sstevel@tonic-gate 		if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
21970Sstevel@tonic-gate 		    USB_DEV_REQ_HOST_TO_DEV,
21980Sstevel@tonic-gate 		    USB_REQ_SET_ADDRESS,	/* bRequest */
21990Sstevel@tonic-gate 		    address,			/* wValue */
22000Sstevel@tonic-gate 		    0,				/* wIndex */
22010Sstevel@tonic-gate 		    0,				/* wLength */
22020Sstevel@tonic-gate 		    NULL, 0,
22030Sstevel@tonic-gate 		    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
22040Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
22050Sstevel@tonic-gate 			    "hubd_setdevaddr(%d): rval=%d cr=%d cb_fl=0x%x",
22060Sstevel@tonic-gate 			    retry, rval, completion_reason, cb_flags);
22070Sstevel@tonic-gate 		}
22080Sstevel@tonic-gate 
22090Sstevel@tonic-gate 		usb_pipe_close(child_dip, ph,
22100Sstevel@tonic-gate 		    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
22110Sstevel@tonic-gate 
22120Sstevel@tonic-gate 		if (rval == USB_SUCCESS) {
22130Sstevel@tonic-gate 
22140Sstevel@tonic-gate 			break;
22150Sstevel@tonic-gate 		}
22160Sstevel@tonic-gate 
22170Sstevel@tonic-gate 		delay(time_delay);
22180Sstevel@tonic-gate 	}
22190Sstevel@tonic-gate 
22200Sstevel@tonic-gate 	/* Reset to the old address */
22210Sstevel@tonic-gate 	mutex_enter(&usba_device->usb_mutex);
22220Sstevel@tonic-gate 	usba_device->usb_addr = address;
22230Sstevel@tonic-gate 	mutex_exit(&usba_device->usb_mutex);
22240Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
22250Sstevel@tonic-gate 
22260Sstevel@tonic-gate 	usba_clear_data_toggle(usba_device);
22270Sstevel@tonic-gate 
22280Sstevel@tonic-gate 	return (rval);
22290Sstevel@tonic-gate }
22300Sstevel@tonic-gate 
22310Sstevel@tonic-gate 
22320Sstevel@tonic-gate /*
22330Sstevel@tonic-gate  * hubd_setdevconfig
22340Sstevel@tonic-gate  *	set the device addrs on this port
22350Sstevel@tonic-gate  */
22360Sstevel@tonic-gate static void
hubd_setdevconfig(hubd_t * hubd,usb_port_t port)22370Sstevel@tonic-gate hubd_setdevconfig(hubd_t *hubd, usb_port_t port)
22380Sstevel@tonic-gate {
22390Sstevel@tonic-gate 	int			rval;
22400Sstevel@tonic-gate 	usb_cr_t		completion_reason;
22410Sstevel@tonic-gate 	usb_cb_flags_t		cb_flags;
22420Sstevel@tonic-gate 	usb_pipe_handle_t	ph;
22430Sstevel@tonic-gate 	dev_info_t		*child_dip = NULL;
22440Sstevel@tonic-gate 	usba_device_t		*usba_device = NULL;
22450Sstevel@tonic-gate 	uint16_t		config_value;
22460Sstevel@tonic-gate 
22470Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
22480Sstevel@tonic-gate 	    "hubd_setdevconfig: port=%d", port);
22490Sstevel@tonic-gate 
22500Sstevel@tonic-gate 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
22510Sstevel@tonic-gate 
22520Sstevel@tonic-gate 	child_dip = hubd->h_children_dips[port];
22530Sstevel@tonic-gate 	usba_device = hubd->h_usba_devices[port];
22540Sstevel@tonic-gate 	config_value = hubd->h_usba_devices[port]->usb_cfg_value;
22550Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
22560Sstevel@tonic-gate 
22570Sstevel@tonic-gate 	/* open the default control pipe */
22580Sstevel@tonic-gate 	if ((rval = usb_pipe_open(child_dip, NULL, NULL,
22590Sstevel@tonic-gate 	    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) ==
22600Sstevel@tonic-gate 	    USB_SUCCESS) {
22610Sstevel@tonic-gate 
22620Sstevel@tonic-gate 		/* Set the default configuration of the device */
22630Sstevel@tonic-gate 		if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
22640Sstevel@tonic-gate 		    USB_DEV_REQ_HOST_TO_DEV,
22650Sstevel@tonic-gate 		    USB_REQ_SET_CFG,		/* bRequest */
22660Sstevel@tonic-gate 		    config_value,		/* wValue */
22670Sstevel@tonic-gate 		    0,				/* wIndex */
22680Sstevel@tonic-gate 		    0,				/* wLength */
22690Sstevel@tonic-gate 		    NULL, 0,
22700Sstevel@tonic-gate 		    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
22710Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
22720Sstevel@tonic-gate 			    "hubd_setdevconfig: set device config failed: "
22730Sstevel@tonic-gate 			    "cr=%d cb_fl=0x%x rval=%d",
22740Sstevel@tonic-gate 			    completion_reason, cb_flags, rval);
22750Sstevel@tonic-gate 		}
22760Sstevel@tonic-gate 		/*
22770Sstevel@tonic-gate 		 * After setting the configuration, we make this default
22780Sstevel@tonic-gate 		 * control pipe persistent, so that it gets re-opened
22790Sstevel@tonic-gate 		 * on posting a connect event
22800Sstevel@tonic-gate 		 */
22810Sstevel@tonic-gate 		usba_persistent_pipe_close(usba_device);
22820Sstevel@tonic-gate 	} else {
22830Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
22840Sstevel@tonic-gate 		    "pipe open fails: rval=%d", rval);
22850Sstevel@tonic-gate 	}
22860Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
22870Sstevel@tonic-gate }
22880Sstevel@tonic-gate 
22890Sstevel@tonic-gate 
22900Sstevel@tonic-gate /*ARGSUSED*/
22910Sstevel@tonic-gate static int
hubd_check_disconnected_ports(dev_info_t * dip,void * arg)22920Sstevel@tonic-gate hubd_check_disconnected_ports(dev_info_t *dip, void *arg)
22930Sstevel@tonic-gate {
22940Sstevel@tonic-gate 	int circ;
22950Sstevel@tonic-gate 	usb_port_t port;
22960Sstevel@tonic-gate 	hubd_t *hubd;
22970Sstevel@tonic-gate 	major_t hub_major = ddi_name_to_major("hubd");
22989430SRaymond.Chen@Sun.COM 	major_t hwahc_major = ddi_name_to_major("hwahc");
22999430SRaymond.Chen@Sun.COM 	major_t usbmid_major = ddi_name_to_major("usb_mid");
23000Sstevel@tonic-gate 
23010Sstevel@tonic-gate 	/*
23020Sstevel@tonic-gate 	 * make sure dip is a usb hub, major of root hub is HCD
23030Sstevel@tonic-gate 	 * major
23040Sstevel@tonic-gate 	 */
23050Sstevel@tonic-gate 	if (!usba_is_root_hub(dip)) {
23069430SRaymond.Chen@Sun.COM 		if (ddi_driver_major(dip) == usbmid_major) {
23079430SRaymond.Chen@Sun.COM 			/*
23089430SRaymond.Chen@Sun.COM 			 * need to walk the children since it might be a
23099430SRaymond.Chen@Sun.COM 			 * HWA device
23109430SRaymond.Chen@Sun.COM 			 */
23119430SRaymond.Chen@Sun.COM 
23129430SRaymond.Chen@Sun.COM 			return (DDI_WALK_CONTINUE);
23139430SRaymond.Chen@Sun.COM 		}
23149430SRaymond.Chen@Sun.COM 
23159430SRaymond.Chen@Sun.COM 		/* TODO: DWA device may also need special handling */
23169430SRaymond.Chen@Sun.COM 
23179430SRaymond.Chen@Sun.COM 		if (((ddi_driver_major(dip) != hub_major) &&
23189430SRaymond.Chen@Sun.COM 		    (ddi_driver_major(dip) != hwahc_major)) ||
23191333Scth 		    !i_ddi_devi_attached(dip)) {
23200Sstevel@tonic-gate 
23210Sstevel@tonic-gate 			return (DDI_WALK_PRUNECHILD);
23220Sstevel@tonic-gate 		}
23230Sstevel@tonic-gate 	}
23240Sstevel@tonic-gate 
23250Sstevel@tonic-gate 	hubd = hubd_get_soft_state(dip);
23260Sstevel@tonic-gate 	if (hubd == NULL) {
23270Sstevel@tonic-gate 
23280Sstevel@tonic-gate 		return (DDI_WALK_PRUNECHILD);
23290Sstevel@tonic-gate 	}
23300Sstevel@tonic-gate 
23310Sstevel@tonic-gate 	/* walk child list and remove nodes with flag DEVI_DEVICE_REMOVED */
23320Sstevel@tonic-gate 	ndi_devi_enter(dip, &circ);
23330Sstevel@tonic-gate 
23349430SRaymond.Chen@Sun.COM 	if (ddi_driver_major(dip) != hwahc_major) {
23359430SRaymond.Chen@Sun.COM 		/* for normal usb hub or root hub */
23369430SRaymond.Chen@Sun.COM 		mutex_enter(HUBD_MUTEX(hubd));
23379430SRaymond.Chen@Sun.COM 		for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
23389430SRaymond.Chen@Sun.COM 			dev_info_t *cdip = hubd->h_children_dips[port];
23399430SRaymond.Chen@Sun.COM 
23409430SRaymond.Chen@Sun.COM 			if (cdip == NULL || DEVI_IS_DEVICE_REMOVED(cdip) == 0) {
23419430SRaymond.Chen@Sun.COM 
23429430SRaymond.Chen@Sun.COM 				continue;
23439430SRaymond.Chen@Sun.COM 			}
23449430SRaymond.Chen@Sun.COM 
23459430SRaymond.Chen@Sun.COM 			(void) hubd_delete_child(hubd, port, NDI_DEVI_REMOVE,
23469430SRaymond.Chen@Sun.COM 			    B_TRUE);
23479430SRaymond.Chen@Sun.COM 		}
23489430SRaymond.Chen@Sun.COM 		mutex_exit(HUBD_MUTEX(hubd));
23499430SRaymond.Chen@Sun.COM 	} else {
23509430SRaymond.Chen@Sun.COM 		/* for HWA */
23519430SRaymond.Chen@Sun.COM 		if (hubd->h_cleanup_child != NULL) {
23529430SRaymond.Chen@Sun.COM 			if (hubd->h_cleanup_child(dip) != USB_SUCCESS) {
23539430SRaymond.Chen@Sun.COM 				ndi_devi_exit(dip, circ);
23549430SRaymond.Chen@Sun.COM 
23559430SRaymond.Chen@Sun.COM 				return (DDI_WALK_PRUNECHILD);
23569430SRaymond.Chen@Sun.COM 			}
23579430SRaymond.Chen@Sun.COM 		} else {
23589430SRaymond.Chen@Sun.COM 			ndi_devi_exit(dip, circ);
23599430SRaymond.Chen@Sun.COM 
23609430SRaymond.Chen@Sun.COM 			return (DDI_WALK_PRUNECHILD);
23619430SRaymond.Chen@Sun.COM 		}
23629430SRaymond.Chen@Sun.COM 	}
23639430SRaymond.Chen@Sun.COM 
23640Sstevel@tonic-gate 	ndi_devi_exit(dip, circ);
23650Sstevel@tonic-gate 
23660Sstevel@tonic-gate 	/* skip siblings of root hub */
23670Sstevel@tonic-gate 	if (usba_is_root_hub(dip)) {
23680Sstevel@tonic-gate 
23690Sstevel@tonic-gate 		return (DDI_WALK_PRUNESIB);
23700Sstevel@tonic-gate 	}
23710Sstevel@tonic-gate 
23720Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
23730Sstevel@tonic-gate }
23740Sstevel@tonic-gate 
23750Sstevel@tonic-gate 
23760Sstevel@tonic-gate /*
23770Sstevel@tonic-gate  * this thread will walk all children under the root hub for this
23780Sstevel@tonic-gate  * USB bus instance and attempt to remove them
23790Sstevel@tonic-gate  */
23800Sstevel@tonic-gate static void
hubd_root_hub_cleanup_thread(void * arg)23810Sstevel@tonic-gate hubd_root_hub_cleanup_thread(void *arg)
23820Sstevel@tonic-gate {
23830Sstevel@tonic-gate 	int circ;
23840Sstevel@tonic-gate 	hubd_t *root_hubd = (hubd_t *)arg;
23850Sstevel@tonic-gate 	dev_info_t *rh_dip = root_hubd->h_dip;
23860Sstevel@tonic-gate #ifndef __lock_lint
23870Sstevel@tonic-gate 	callb_cpr_t cprinfo;
23880Sstevel@tonic-gate 
23890Sstevel@tonic-gate 	CALLB_CPR_INIT(&cprinfo, HUBD_MUTEX(root_hubd), callb_generic_cpr,
23900Sstevel@tonic-gate 	    "USB root hub");
23910Sstevel@tonic-gate #endif
23920Sstevel@tonic-gate 
23930Sstevel@tonic-gate 	for (;;) {
23940Sstevel@tonic-gate 		/* don't race with detach */
23950Sstevel@tonic-gate 		ndi_hold_devi(rh_dip);
23960Sstevel@tonic-gate 
23970Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(root_hubd));
23980Sstevel@tonic-gate 		root_hubd->h_cleanup_needed = 0;
23990Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(root_hubd));
24000Sstevel@tonic-gate 
24010Sstevel@tonic-gate 		(void) devfs_clean(rh_dip, NULL, 0);
24020Sstevel@tonic-gate 
24030Sstevel@tonic-gate 		ndi_devi_enter(ddi_get_parent(rh_dip), &circ);
24040Sstevel@tonic-gate 		ddi_walk_devs(rh_dip, hubd_check_disconnected_ports,
24054763Slg150142 		    NULL);
24060Sstevel@tonic-gate #ifdef __lock_lint
24070Sstevel@tonic-gate 		(void) hubd_check_disconnected_ports(rh_dip, NULL);
24080Sstevel@tonic-gate #endif
24090Sstevel@tonic-gate 		ndi_devi_exit(ddi_get_parent(rh_dip), circ);
24100Sstevel@tonic-gate 
24110Sstevel@tonic-gate 		/* quit if we are not enabled anymore */
24120Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(root_hubd));
24130Sstevel@tonic-gate 		if ((root_hubd->h_cleanup_enabled == B_FALSE) ||
24140Sstevel@tonic-gate 		    (root_hubd->h_cleanup_needed == B_FALSE)) {
24150Sstevel@tonic-gate 			root_hubd->h_cleanup_active = B_FALSE;
24160Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(root_hubd));
24170Sstevel@tonic-gate 			ndi_rele_devi(rh_dip);
24180Sstevel@tonic-gate 
24190Sstevel@tonic-gate 			break;
24200Sstevel@tonic-gate 		}
24210Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(root_hubd));
24220Sstevel@tonic-gate 		ndi_rele_devi(rh_dip);
24230Sstevel@tonic-gate 
24240Sstevel@tonic-gate #ifndef __lock_lint
24250Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(root_hubd));
24260Sstevel@tonic-gate 		CALLB_CPR_SAFE_BEGIN(&cprinfo);
24270Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(root_hubd));
24280Sstevel@tonic-gate 
24290Sstevel@tonic-gate 		delay(drv_usectohz(hubd_dip_cleanup_delay));
24300Sstevel@tonic-gate 
24310Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(root_hubd));
24320Sstevel@tonic-gate 		CALLB_CPR_SAFE_END(&cprinfo, HUBD_MUTEX(root_hubd));
24330Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(root_hubd));
24340Sstevel@tonic-gate #endif
24350Sstevel@tonic-gate 	}
24360Sstevel@tonic-gate 
24370Sstevel@tonic-gate #ifndef __lock_lint
24380Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(root_hubd));
24390Sstevel@tonic-gate 	CALLB_CPR_EXIT(&cprinfo);
24400Sstevel@tonic-gate #endif
24410Sstevel@tonic-gate }
24420Sstevel@tonic-gate 
24430Sstevel@tonic-gate 
24449430SRaymond.Chen@Sun.COM void
hubd_schedule_cleanup(dev_info_t * rh_dip)24450Sstevel@tonic-gate hubd_schedule_cleanup(dev_info_t *rh_dip)
24460Sstevel@tonic-gate {
24479430SRaymond.Chen@Sun.COM 	hubd_t	*root_hubd;
24489430SRaymond.Chen@Sun.COM 
24499430SRaymond.Chen@Sun.COM 	/*
24509430SRaymond.Chen@Sun.COM 	 * The usb_root_hub_dip pointer for the child hub of the WUSB
24519430SRaymond.Chen@Sun.COM 	 * wire adapter class device points to the wire adapter, not
24529430SRaymond.Chen@Sun.COM 	 * the root hub. Need to find the real root hub dip so that
24539430SRaymond.Chen@Sun.COM 	 * the cleanup thread only starts from the root hub.
24549430SRaymond.Chen@Sun.COM 	 */
24559430SRaymond.Chen@Sun.COM 	while (!usba_is_root_hub(rh_dip)) {
24569430SRaymond.Chen@Sun.COM 		root_hubd = hubd_get_soft_state(rh_dip);
24579430SRaymond.Chen@Sun.COM 		if (root_hubd != NULL) {
24589430SRaymond.Chen@Sun.COM 			rh_dip = root_hubd->h_usba_device->usb_root_hub_dip;
24599430SRaymond.Chen@Sun.COM 			if (rh_dip == NULL) {
24609430SRaymond.Chen@Sun.COM 				USB_DPRINTF_L2(DPRINT_MASK_ATTA,
24619430SRaymond.Chen@Sun.COM 				    root_hubd->h_log_handle,
24629430SRaymond.Chen@Sun.COM 				    "hubd_schedule_cleanup: null rh dip");
24639430SRaymond.Chen@Sun.COM 
24649430SRaymond.Chen@Sun.COM 				return;
24659430SRaymond.Chen@Sun.COM 			}
24669430SRaymond.Chen@Sun.COM 		} else {
24679430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(DPRINT_MASK_ATTA,
24689430SRaymond.Chen@Sun.COM 			    root_hubd->h_log_handle,
24699430SRaymond.Chen@Sun.COM 			    "hubd_schedule_cleanup: cannot find root hub");
24709430SRaymond.Chen@Sun.COM 
24719430SRaymond.Chen@Sun.COM 			return;
24729430SRaymond.Chen@Sun.COM 		}
24739430SRaymond.Chen@Sun.COM 	}
24749430SRaymond.Chen@Sun.COM 	root_hubd = hubd_get_soft_state(rh_dip);
24750Sstevel@tonic-gate 
24760Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(root_hubd));
24770Sstevel@tonic-gate 	root_hubd->h_cleanup_needed = B_TRUE;
24780Sstevel@tonic-gate 	if (root_hubd->h_cleanup_enabled && !(root_hubd->h_cleanup_active)) {
24790Sstevel@tonic-gate 		root_hubd->h_cleanup_active = B_TRUE;
24800Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(root_hubd));
24810Sstevel@tonic-gate 		(void) thread_create(NULL, 0,
24820Sstevel@tonic-gate 		    hubd_root_hub_cleanup_thread,
24830Sstevel@tonic-gate 		    (void *)root_hubd, 0, &p0, TS_RUN,
24840Sstevel@tonic-gate 		    minclsyspri);
24850Sstevel@tonic-gate 	} else {
24860Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(root_hubd));
24870Sstevel@tonic-gate 	}
24880Sstevel@tonic-gate }
24890Sstevel@tonic-gate 
24900Sstevel@tonic-gate 
24910Sstevel@tonic-gate /*
24920Sstevel@tonic-gate  * hubd_restore_device_state:
24930Sstevel@tonic-gate  *	- set config for the hub
24940Sstevel@tonic-gate  *	- power cycle all the ports
24950Sstevel@tonic-gate  *	- for each port that was connected
24960Sstevel@tonic-gate  *		- reset port
24970Sstevel@tonic-gate  *		- assign addrs to the device on this port
24980Sstevel@tonic-gate  *	- restart polling
24990Sstevel@tonic-gate  *	- reset suspend flag
25000Sstevel@tonic-gate  */
25010Sstevel@tonic-gate static void
hubd_restore_device_state(dev_info_t * dip,hubd_t * hubd)25020Sstevel@tonic-gate hubd_restore_device_state(dev_info_t *dip, hubd_t *hubd)
25030Sstevel@tonic-gate {
25040Sstevel@tonic-gate 	int		rval;
25050Sstevel@tonic-gate 	int		retry;
25060Sstevel@tonic-gate 	uint_t		hub_prev_state;
25070Sstevel@tonic-gate 	usb_port_t	port;
25080Sstevel@tonic-gate 	uint16_t	status;
25090Sstevel@tonic-gate 	uint16_t	change;
25100Sstevel@tonic-gate 	dev_info_t	*ch_dip;
25110Sstevel@tonic-gate 	boolean_t	ehci_root_hub;
25120Sstevel@tonic-gate 
25130Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
25140Sstevel@tonic-gate 	    "hubd_restore_device_state:");
25150Sstevel@tonic-gate 
25160Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
25170Sstevel@tonic-gate 	hub_prev_state = hubd->h_dev_state;
25180Sstevel@tonic-gate 	ASSERT(hub_prev_state != USB_DEV_PWRED_DOWN);
25190Sstevel@tonic-gate 
25200Sstevel@tonic-gate 	/* First bring the device to full power */
25210Sstevel@tonic-gate 	(void) hubd_pm_busy_component(hubd, dip, 0);
25220Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
25230Sstevel@tonic-gate 
25240Sstevel@tonic-gate 	(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
25250Sstevel@tonic-gate 
25260Sstevel@tonic-gate 	if (!usba_is_root_hub(dip) &&
25270Sstevel@tonic-gate 	    (usb_check_same_device(dip, hubd->h_log_handle, USB_LOG_L0,
25280Sstevel@tonic-gate 	    DPRINT_MASK_HOTPLUG,
25290Sstevel@tonic-gate 	    USB_CHK_BASIC|USB_CHK_CFG, NULL) != USB_SUCCESS)) {
25300Sstevel@tonic-gate 
25310Sstevel@tonic-gate 		/* change the device state to disconnected */
25320Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
25330Sstevel@tonic-gate 		hubd->h_dev_state = USB_DEV_DISCONNECTED;
25340Sstevel@tonic-gate 		(void) hubd_pm_idle_component(hubd, dip, 0);
25350Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
25360Sstevel@tonic-gate 
25370Sstevel@tonic-gate 		return;
25380Sstevel@tonic-gate 	}
25390Sstevel@tonic-gate 
25400Sstevel@tonic-gate 	ehci_root_hub = (strcmp(ddi_driver_name(dip), "ehci") == 0);
25410Sstevel@tonic-gate 
25420Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
25430Sstevel@tonic-gate 	/* First turn off all port power */
25440Sstevel@tonic-gate 	rval = hubd_disable_all_port_power(hubd);
25450Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
25460Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
25470Sstevel@tonic-gate 		    "hubd_restore_device_state:"
25480Sstevel@tonic-gate 		    "turning off port power failed");
25490Sstevel@tonic-gate 	}
25500Sstevel@tonic-gate 
25510Sstevel@tonic-gate 	/* Settling time before turning on again */
25520Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
25530Sstevel@tonic-gate 	delay(drv_usectohz(hubd_device_delay / 100));
25540Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
25550Sstevel@tonic-gate 
25560Sstevel@tonic-gate 	/* enable power on all ports so we can see connects */
25570Sstevel@tonic-gate 	if (hubd_enable_all_port_power(hubd) != USB_SUCCESS) {
25580Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
25590Sstevel@tonic-gate 		    "hubd_restore_device_state: turn on port power failed");
25600Sstevel@tonic-gate 
25610Sstevel@tonic-gate 		/* disable whatever was enabled */
25620Sstevel@tonic-gate 		(void) hubd_disable_all_port_power(hubd);
25630Sstevel@tonic-gate 
25640Sstevel@tonic-gate 		(void) hubd_pm_idle_component(hubd, dip, 0);
25650Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
25660Sstevel@tonic-gate 
25670Sstevel@tonic-gate 		return;
25680Sstevel@tonic-gate 	}
25690Sstevel@tonic-gate 
25700Sstevel@tonic-gate 	/*
25710Sstevel@tonic-gate 	 * wait at least 3 frames before accessing devices
25720Sstevel@tonic-gate 	 * (note that delay's minimal time is one clock tick which
25730Sstevel@tonic-gate 	 * is 10ms unless hires_tick has been changed)
25740Sstevel@tonic-gate 	 */
25750Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
25760Sstevel@tonic-gate 	delay(drv_usectohz(10000));
25770Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
25780Sstevel@tonic-gate 
25790Sstevel@tonic-gate 	hubd->h_dev_state = USB_DEV_HUB_STATE_RECOVER;
25800Sstevel@tonic-gate 
25810Sstevel@tonic-gate 	for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
25820Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
25830Sstevel@tonic-gate 		    "hubd_restore_device_state: port=%d", port);
25840Sstevel@tonic-gate 
25850Sstevel@tonic-gate 		/*
25860Sstevel@tonic-gate 		 * the childen_dips list may have dips that have been
25870Sstevel@tonic-gate 		 * already deallocated. we only get a post_detach notification
25880Sstevel@tonic-gate 		 * but not a destroy notification
25890Sstevel@tonic-gate 		 */
25900Sstevel@tonic-gate 		ch_dip = hubd->h_children_dips[port];
25910Sstevel@tonic-gate 		if (ch_dip) {
25920Sstevel@tonic-gate 			/* get port status */
25930Sstevel@tonic-gate 			(void) hubd_determine_port_status(hubd, port,
25940Sstevel@tonic-gate 			    &status, &change, PORT_CHANGE_CSC);
25950Sstevel@tonic-gate 
25960Sstevel@tonic-gate 			/* check if it is truly connected */
25970Sstevel@tonic-gate 			if (status & PORT_STATUS_CCS) {
25980Sstevel@tonic-gate 				/*
25990Sstevel@tonic-gate 				 * Now reset port and assign the device
26000Sstevel@tonic-gate 				 * its original address
26010Sstevel@tonic-gate 				 */
26020Sstevel@tonic-gate 				retry = 0;
26030Sstevel@tonic-gate 				do {
26040Sstevel@tonic-gate 					(void) hubd_reset_port(hubd, port);
26050Sstevel@tonic-gate 
26060Sstevel@tonic-gate 					/* required for ppx */
26070Sstevel@tonic-gate 					(void) hubd_enable_port(hubd, port);
26080Sstevel@tonic-gate 
26090Sstevel@tonic-gate 					if (retry) {
26100Sstevel@tonic-gate 						mutex_exit(HUBD_MUTEX(hubd));
26110Sstevel@tonic-gate 						delay(drv_usectohz(
26124763Slg150142 						    hubd_device_delay/2));
26130Sstevel@tonic-gate 						mutex_enter(HUBD_MUTEX(hubd));
26140Sstevel@tonic-gate 					}
26150Sstevel@tonic-gate 
26160Sstevel@tonic-gate 					rval = hubd_setdevaddr(hubd, port);
26170Sstevel@tonic-gate 					retry++;
26180Sstevel@tonic-gate 				} while ((rval != USB_SUCCESS) &&
26190Sstevel@tonic-gate 				    (retry < hubd_retry_enumerate));
26200Sstevel@tonic-gate 
26210Sstevel@tonic-gate 				hubd_setdevconfig(hubd, port);
26220Sstevel@tonic-gate 
26230Sstevel@tonic-gate 				if (hub_prev_state == USB_DEV_DISCONNECTED) {
26240Sstevel@tonic-gate 					/* post a connect event */
26250Sstevel@tonic-gate 					mutex_exit(HUBD_MUTEX(hubd));
26260Sstevel@tonic-gate 					hubd_post_event(hubd, port,
26270Sstevel@tonic-gate 					    USBA_EVENT_TAG_HOT_INSERTION);
26280Sstevel@tonic-gate 					mutex_enter(HUBD_MUTEX(hubd));
26290Sstevel@tonic-gate 				} else {
26300Sstevel@tonic-gate 					/*
26310Sstevel@tonic-gate 					 * Since we have this device connected
26320Sstevel@tonic-gate 					 * mark it reinserted to prevent
26330Sstevel@tonic-gate 					 * cleanup thread from stepping in.
26340Sstevel@tonic-gate 					 */
2635495Scth 					mutex_exit(HUBD_MUTEX(hubd));
2636495Scth 					mutex_enter(&(DEVI(ch_dip)->devi_lock));
26370Sstevel@tonic-gate 					DEVI_SET_DEVICE_REINSERTED(ch_dip);
2638495Scth 					mutex_exit(&(DEVI(ch_dip)->devi_lock));
26390Sstevel@tonic-gate 
26400Sstevel@tonic-gate 					/*
26410Sstevel@tonic-gate 					 * reopen pipes for children for
26420Sstevel@tonic-gate 					 * their DDI_RESUME
26430Sstevel@tonic-gate 					 */
26440Sstevel@tonic-gate 					rval = usba_persistent_pipe_open(
26450Sstevel@tonic-gate 					    usba_get_usba_device(ch_dip));
26460Sstevel@tonic-gate 					mutex_enter(HUBD_MUTEX(hubd));
26470Sstevel@tonic-gate 					ASSERT(rval == USB_SUCCESS);
26480Sstevel@tonic-gate 				}
26490Sstevel@tonic-gate 			} else {
26500Sstevel@tonic-gate 				/*
26510Sstevel@tonic-gate 				 * Mark this dip for deletion as the device
26520Sstevel@tonic-gate 				 * is not physically present, and schedule
26530Sstevel@tonic-gate 				 * cleanup thread upon post resume
26540Sstevel@tonic-gate 				 */
26550Sstevel@tonic-gate 				mutex_exit(HUBD_MUTEX(hubd));
26560Sstevel@tonic-gate 
26570Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_ATTA,
26580Sstevel@tonic-gate 				    hubd->h_log_handle,
26590Sstevel@tonic-gate 				    "hubd_restore_device_state: "
26600Sstevel@tonic-gate 				    "dip=%p on port=%d marked for cleanup",
26616898Sfb209375 				    (void *)ch_dip, port);
2662495Scth 				mutex_enter(&(DEVI(ch_dip)->devi_lock));
26630Sstevel@tonic-gate 				DEVI_SET_DEVICE_REMOVED(ch_dip);
2664495Scth 				mutex_exit(&(DEVI(ch_dip)->devi_lock));
26650Sstevel@tonic-gate 
26660Sstevel@tonic-gate 				mutex_enter(HUBD_MUTEX(hubd));
26670Sstevel@tonic-gate 			}
26680Sstevel@tonic-gate 		} else if (ehci_root_hub) {
26690Sstevel@tonic-gate 			/* get port status */
26700Sstevel@tonic-gate 			(void) hubd_determine_port_status(hubd, port,
26710Sstevel@tonic-gate 			    &status, &change, PORT_CHANGE_CSC);
26720Sstevel@tonic-gate 
26730Sstevel@tonic-gate 			/* check if it is truly connected */
26740Sstevel@tonic-gate 			if (status & PORT_STATUS_CCS) {
26750Sstevel@tonic-gate 				/*
26760Sstevel@tonic-gate 				 * reset the port to find out if we have
26770Sstevel@tonic-gate 				 * 2.0 device connected or 1.X. A 2.0
26780Sstevel@tonic-gate 				 * device will still be seen as connected,
26790Sstevel@tonic-gate 				 * while a 1.X device will switch over to
26800Sstevel@tonic-gate 				 * the companion controller.
26810Sstevel@tonic-gate 				 */
26820Sstevel@tonic-gate 				(void) hubd_reset_port(hubd, port);
26830Sstevel@tonic-gate 
26840Sstevel@tonic-gate 				(void) hubd_determine_port_status(hubd, port,
26850Sstevel@tonic-gate 				    &status, &change, PORT_CHANGE_CSC);
26860Sstevel@tonic-gate 
26870Sstevel@tonic-gate 				if (status &
26880Sstevel@tonic-gate 				    (PORT_STATUS_CCS | PORT_STATUS_HSDA)) {
26890Sstevel@tonic-gate 					/*
26900Sstevel@tonic-gate 					 * We have a USB 2.0 device
26910Sstevel@tonic-gate 					 * connected. Power cycle this port
26920Sstevel@tonic-gate 					 * so that hotplug thread can
26930Sstevel@tonic-gate 					 * enumerate this device.
26940Sstevel@tonic-gate 					 */
26950Sstevel@tonic-gate 					(void) hubd_toggle_port(hubd, port);
26960Sstevel@tonic-gate 				} else {
26970Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_ATTA,
26980Sstevel@tonic-gate 					    hubd->h_log_handle,
26990Sstevel@tonic-gate 					    "hubd_restore_device_state: "
27000Sstevel@tonic-gate 					    "device on port %d switched over",
27010Sstevel@tonic-gate 					    port);
27020Sstevel@tonic-gate 				}
27030Sstevel@tonic-gate 			}
27040Sstevel@tonic-gate 
27050Sstevel@tonic-gate 		}
27060Sstevel@tonic-gate 	}
27070Sstevel@tonic-gate 
27080Sstevel@tonic-gate 
27090Sstevel@tonic-gate 	/* if the device had remote wakeup earlier, enable it again */
27100Sstevel@tonic-gate 	if (hubd->h_hubpm->hubp_wakeup_enabled) {
27110Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
27120Sstevel@tonic-gate 		(void) usb_handle_remote_wakeup(hubd->h_dip,
27130Sstevel@tonic-gate 		    USB_REMOTE_WAKEUP_ENABLE);
27140Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
27150Sstevel@tonic-gate 	}
27160Sstevel@tonic-gate 
27170Sstevel@tonic-gate 	hubd->h_dev_state = USB_DEV_ONLINE;
27180Sstevel@tonic-gate 	hubd_start_polling(hubd, 0);
27190Sstevel@tonic-gate 	(void) hubd_pm_idle_component(hubd, dip, 0);
27200Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
27210Sstevel@tonic-gate }
27220Sstevel@tonic-gate 
27230Sstevel@tonic-gate 
27240Sstevel@tonic-gate /*
27250Sstevel@tonic-gate  * hubd_cleanup:
27260Sstevel@tonic-gate  *	cleanup hubd and deallocate. this function is called for
27270Sstevel@tonic-gate  *	handling attach failures and detaching including dynamic
27280Sstevel@tonic-gate  *	reconfiguration. If called from attaching, it must clean
27290Sstevel@tonic-gate  *	up the whole thing and return success.
27300Sstevel@tonic-gate  */
27310Sstevel@tonic-gate /*ARGSUSED*/
27320Sstevel@tonic-gate static int
hubd_cleanup(dev_info_t * dip,hubd_t * hubd)27330Sstevel@tonic-gate hubd_cleanup(dev_info_t *dip, hubd_t *hubd)
27340Sstevel@tonic-gate {
27350Sstevel@tonic-gate 	int		circ, rval, old_dev_state;
27360Sstevel@tonic-gate 	hub_power_t	*hubpm;
27370Sstevel@tonic-gate #ifdef DEBUG
27380Sstevel@tonic-gate 	usb_port_t	port;
27390Sstevel@tonic-gate #endif
27400Sstevel@tonic-gate 
27410Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
27420Sstevel@tonic-gate 	    "hubd_cleanup:");
27430Sstevel@tonic-gate 
27440Sstevel@tonic-gate 	if ((hubd->h_init_state & HUBD_LOCKS_DONE) == 0) {
27450Sstevel@tonic-gate 		goto done;
27460Sstevel@tonic-gate 	}
27470Sstevel@tonic-gate 
27480Sstevel@tonic-gate 	/* ensure we are the only one active */
27490Sstevel@tonic-gate 	ndi_devi_enter(dip, &circ);
27500Sstevel@tonic-gate 
27510Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
27520Sstevel@tonic-gate 
27530Sstevel@tonic-gate 	/* Cleanup failure is only allowed if called from detach */
27540Sstevel@tonic-gate 	if (DEVI_IS_DETACHING(dip)) {
27550Sstevel@tonic-gate 		dev_info_t *rh_dip = hubd->h_usba_device->usb_root_hub_dip;
27560Sstevel@tonic-gate 
27570Sstevel@tonic-gate 		/*
27580Sstevel@tonic-gate 		 * We are being called from detach.
27590Sstevel@tonic-gate 		 * Fail immediately if the hotplug thread is running
27600Sstevel@tonic-gate 		 * else set the dev_state to disconnected so that
27610Sstevel@tonic-gate 		 * hotplug thread just exits without doing anything.
27620Sstevel@tonic-gate 		 */
27630Sstevel@tonic-gate 		if (hubd->h_bus_ctls || hubd->h_bus_pwr ||
27640Sstevel@tonic-gate 		    hubd->h_hotplug_thread) {
27650Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
27660Sstevel@tonic-gate 			ndi_devi_exit(dip, circ);
27670Sstevel@tonic-gate 
27680Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
27690Sstevel@tonic-gate 			    "hubd_cleanup: hotplug thread/bus ctl active "
27700Sstevel@tonic-gate 			    "- failing detach");
27710Sstevel@tonic-gate 
27720Sstevel@tonic-gate 			return (USB_FAILURE);
27730Sstevel@tonic-gate 		}
27740Sstevel@tonic-gate 
27750Sstevel@tonic-gate 		/*
27760Sstevel@tonic-gate 		 * if the deathrow thread is still active or about
27770Sstevel@tonic-gate 		 * to become active, fail detach
27780Sstevel@tonic-gate 		 * the roothup can only be detached if nexus drivers
27790Sstevel@tonic-gate 		 * are unloaded or explicitly offlined
27800Sstevel@tonic-gate 		 */
27810Sstevel@tonic-gate 		if (rh_dip == dip) {
27820Sstevel@tonic-gate 			if (hubd->h_cleanup_needed ||
27830Sstevel@tonic-gate 			    hubd->h_cleanup_active) {
27840Sstevel@tonic-gate 				mutex_exit(HUBD_MUTEX(hubd));
27850Sstevel@tonic-gate 				ndi_devi_exit(dip, circ);
27860Sstevel@tonic-gate 
27870Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_ATTA,
27880Sstevel@tonic-gate 				    hubd->h_log_handle,
27890Sstevel@tonic-gate 				    "hubd_cleanup: deathrow still active?"
27900Sstevel@tonic-gate 				    "- failing detach");
27910Sstevel@tonic-gate 
27920Sstevel@tonic-gate 				return (USB_FAILURE);
27930Sstevel@tonic-gate 			}
27940Sstevel@tonic-gate 		}
27950Sstevel@tonic-gate 	}
27960Sstevel@tonic-gate 
27970Sstevel@tonic-gate 	old_dev_state = hubd->h_dev_state;
27980Sstevel@tonic-gate 	hubd->h_dev_state = USB_DEV_DISCONNECTED;
27990Sstevel@tonic-gate 
28000Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
28010Sstevel@tonic-gate 	    "hubd_cleanup: stop polling");
28020Sstevel@tonic-gate 	hubd_close_intr_pipe(hubd);
28030Sstevel@tonic-gate 
28040Sstevel@tonic-gate 	ASSERT((hubd->h_bus_ctls || hubd->h_bus_pwr ||
28050Sstevel@tonic-gate 	    hubd->h_hotplug_thread) == 0);
28060Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
28070Sstevel@tonic-gate 
28080Sstevel@tonic-gate 	/*
28090Sstevel@tonic-gate 	 * deallocate events, if events are still registered
28100Sstevel@tonic-gate 	 * (ie. children still attached) then we have to fail the detach
28110Sstevel@tonic-gate 	 */
28120Sstevel@tonic-gate 	if (hubd->h_ndi_event_hdl) {
28130Sstevel@tonic-gate 
28140Sstevel@tonic-gate 		rval = ndi_event_free_hdl(hubd->h_ndi_event_hdl);
28150Sstevel@tonic-gate 		if (DEVI_IS_ATTACHING(dip)) {
28160Sstevel@tonic-gate 
28170Sstevel@tonic-gate 			/* It must return success if attaching. */
28180Sstevel@tonic-gate 			ASSERT(rval == NDI_SUCCESS);
28190Sstevel@tonic-gate 
28200Sstevel@tonic-gate 		} else if (rval != NDI_SUCCESS) {
28210Sstevel@tonic-gate 
2822978Sfrits 			USB_DPRINTF_L2(DPRINT_MASK_ALL, hubd->h_log_handle,
28230Sstevel@tonic-gate 			    "hubd_cleanup: ndi_event_free_hdl failed");
28240Sstevel@tonic-gate 			ndi_devi_exit(dip, circ);
28250Sstevel@tonic-gate 
28260Sstevel@tonic-gate 			return (USB_FAILURE);
28270Sstevel@tonic-gate 
28280Sstevel@tonic-gate 		}
28290Sstevel@tonic-gate 	}
28300Sstevel@tonic-gate 
28310Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
28320Sstevel@tonic-gate 
28331001Ssl147100 	if (hubd->h_init_state & HUBD_CHILDREN_CREATED) {
28340Sstevel@tonic-gate #ifdef DEBUG
28351001Ssl147100 		for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
28361001Ssl147100 			ASSERT(hubd->h_usba_devices[port] == NULL);
28371001Ssl147100 			ASSERT(hubd->h_children_dips[port] == NULL);
28381001Ssl147100 		}
28390Sstevel@tonic-gate #endif
28401001Ssl147100 		kmem_free(hubd->h_children_dips, hubd->h_cd_list_length);
28411001Ssl147100 		kmem_free(hubd->h_usba_devices, hubd->h_cd_list_length);
28421001Ssl147100 	}
28430Sstevel@tonic-gate 
28440Sstevel@tonic-gate 	/*
28450Sstevel@tonic-gate 	 * Disable the event callbacks first, after this point, event
28460Sstevel@tonic-gate 	 * callbacks will never get called. Note we shouldn't hold
28470Sstevel@tonic-gate 	 * mutex while unregistering events because there may be a
28480Sstevel@tonic-gate 	 * competing event callback thread. Event callbacks are done
28490Sstevel@tonic-gate 	 * with ndi mutex held and this can cause a potential deadlock.
28500Sstevel@tonic-gate 	 * Note that cleanup can't fail after deregistration of events.
28510Sstevel@tonic-gate 	 */
28520Sstevel@tonic-gate 	if (hubd->h_init_state &  HUBD_EVENTS_REGISTERED) {
28530Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
28540Sstevel@tonic-gate 		usb_unregister_event_cbs(dip, &hubd_events);
28550Sstevel@tonic-gate 		hubd_unregister_cpr_callback(hubd);
28560Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
28570Sstevel@tonic-gate 	}
28580Sstevel@tonic-gate 
28590Sstevel@tonic-gate 	/* restore the old dev state so that device can be put into low power */
28600Sstevel@tonic-gate 	hubd->h_dev_state = old_dev_state;
28610Sstevel@tonic-gate 	hubpm = hubd->h_hubpm;
28620Sstevel@tonic-gate 
28630Sstevel@tonic-gate 	if ((hubpm) && (hubd->h_dev_state != USB_DEV_DISCONNECTED)) {
28640Sstevel@tonic-gate 		(void) hubd_pm_busy_component(hubd, dip, 0);
28650Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
28660Sstevel@tonic-gate 		if (hubd->h_hubpm->hubp_wakeup_enabled) {
28670Sstevel@tonic-gate 			/*
28680Sstevel@tonic-gate 			 * Bring the hub to full power before
28690Sstevel@tonic-gate 			 * issuing the disable remote wakeup command
28700Sstevel@tonic-gate 			 */
28710Sstevel@tonic-gate 			(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
28720Sstevel@tonic-gate 
28730Sstevel@tonic-gate 			if ((rval = usb_handle_remote_wakeup(hubd->h_dip,
28740Sstevel@tonic-gate 			    USB_REMOTE_WAKEUP_DISABLE)) != USB_SUCCESS) {
28750Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_PM,
28760Sstevel@tonic-gate 				    hubd->h_log_handle,
28770Sstevel@tonic-gate 				    "hubd_cleanup: disable remote wakeup "
28780Sstevel@tonic-gate 				    "fails=%d", rval);
28790Sstevel@tonic-gate 			}
28800Sstevel@tonic-gate 		}
28810Sstevel@tonic-gate 
28820Sstevel@tonic-gate 		(void) pm_lower_power(hubd->h_dip, 0, USB_DEV_OS_PWR_OFF);
28830Sstevel@tonic-gate 
28840Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
28850Sstevel@tonic-gate 		(void) hubd_pm_idle_component(hubd, dip, 0);
28860Sstevel@tonic-gate 	}
28870Sstevel@tonic-gate 
28880Sstevel@tonic-gate 	if (hubpm) {
28890Sstevel@tonic-gate 		if (hubpm->hubp_child_pwrstate) {
28900Sstevel@tonic-gate 			kmem_free(hubpm->hubp_child_pwrstate,
28910Sstevel@tonic-gate 			    MAX_PORTS + 1);
28920Sstevel@tonic-gate 		}
28930Sstevel@tonic-gate 		kmem_free(hubpm, sizeof (hub_power_t));
28940Sstevel@tonic-gate 	}
28950Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
28960Sstevel@tonic-gate 
28970Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
28980Sstevel@tonic-gate 	    "hubd_cleanup: freeing space");
28990Sstevel@tonic-gate 
29000Sstevel@tonic-gate 	if (hubd->h_init_state & HUBD_HUBDI_REGISTERED) {
29010Sstevel@tonic-gate 		rval = usba_hubdi_unregister(dip);
29020Sstevel@tonic-gate 		ASSERT(rval == USB_SUCCESS);
29030Sstevel@tonic-gate 	}
29040Sstevel@tonic-gate 
29050Sstevel@tonic-gate 	if (hubd->h_init_state & HUBD_LOCKS_DONE) {
29060Sstevel@tonic-gate 		mutex_destroy(HUBD_MUTEX(hubd));
29070Sstevel@tonic-gate 		cv_destroy(&hubd->h_cv_reset_port);
29084844Slg150142 		cv_destroy(&hubd->h_cv_hotplug_dev);
29090Sstevel@tonic-gate 	}
29100Sstevel@tonic-gate 
29110Sstevel@tonic-gate 	ndi_devi_exit(dip, circ);
29120Sstevel@tonic-gate 
29130Sstevel@tonic-gate 	if (hubd->h_init_state & HUBD_MINOR_NODE_CREATED) {
29140Sstevel@tonic-gate 		ddi_remove_minor_node(dip, NULL);
29150Sstevel@tonic-gate 	}
29160Sstevel@tonic-gate 
29170Sstevel@tonic-gate 	if (usba_is_root_hub(dip)) {
29180Sstevel@tonic-gate 		usb_pipe_close(dip, hubd->h_default_pipe,
29190Sstevel@tonic-gate 		    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
29200Sstevel@tonic-gate 	}
29210Sstevel@tonic-gate 
29220Sstevel@tonic-gate done:
29230Sstevel@tonic-gate 	if (hubd->h_ancestry_str) {
29240Sstevel@tonic-gate 		kmem_free(hubd->h_ancestry_str, HUBD_APID_NAMELEN);
29250Sstevel@tonic-gate 	}
29260Sstevel@tonic-gate 
29270Sstevel@tonic-gate 	usb_client_detach(dip, hubd->h_dev_data);
29280Sstevel@tonic-gate 
29290Sstevel@tonic-gate 	usb_free_log_hdl(hubd->h_log_handle);
29300Sstevel@tonic-gate 
29310Sstevel@tonic-gate 	if (!usba_is_root_hub(dip)) {
29320Sstevel@tonic-gate 		ddi_soft_state_free(hubd_statep, ddi_get_instance(dip));
29330Sstevel@tonic-gate 	}
29340Sstevel@tonic-gate 
29350Sstevel@tonic-gate 	ddi_prop_remove_all(dip);
29360Sstevel@tonic-gate 
29370Sstevel@tonic-gate 	return (USB_SUCCESS);
29380Sstevel@tonic-gate }
29390Sstevel@tonic-gate 
29400Sstevel@tonic-gate 
29410Sstevel@tonic-gate /*
29422651Ssl147100  * hubd_determine_port_connection:
29432651Ssl147100  *	Determine which port is in connect status but does not
29442651Ssl147100  *	have connect status change bit set, and mark port change
29452651Ssl147100  *	bit accordingly.
29462651Ssl147100  *	This function is applied during hub attach time.
29472651Ssl147100  */
29482651Ssl147100 static usb_port_mask_t
hubd_determine_port_connection(hubd_t * hubd)29492651Ssl147100 hubd_determine_port_connection(hubd_t	*hubd)
29502651Ssl147100 {
29512651Ssl147100 	usb_port_t	port;
29522651Ssl147100 	usb_hub_descr_t	*hub_descr;
29532651Ssl147100 	uint16_t	status;
29542651Ssl147100 	uint16_t	change;
29552651Ssl147100 	usb_port_mask_t	port_change = 0;
29562651Ssl147100 
29572651Ssl147100 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
29582651Ssl147100 
29592651Ssl147100 	hub_descr = &hubd->h_hub_descr;
29602651Ssl147100 
29612651Ssl147100 	for (port = 1; port <= hub_descr->bNbrPorts; port++) {
29622651Ssl147100 
29632651Ssl147100 		(void) hubd_determine_port_status(hubd, port, &status,
29642651Ssl147100 		    &change, 0);
29652651Ssl147100 
29662651Ssl147100 		/* Check if port is in connect status */
29672651Ssl147100 		if (!(status & PORT_STATUS_CCS)) {
29682651Ssl147100 
29692651Ssl147100 			continue;
29702651Ssl147100 		}
29712651Ssl147100 
29722651Ssl147100 		/*
29732651Ssl147100 		 * Check if port Connect Status Change bit has been set.
29742651Ssl147100 		 * If already set, the connection will be handled by
29752651Ssl147100 		 * intr polling callback, not during attach.
29762651Ssl147100 		 */
29772651Ssl147100 		if (change & PORT_CHANGE_CSC) {
29782651Ssl147100 
29792651Ssl147100 			continue;
29802651Ssl147100 		}
29812651Ssl147100 
29822651Ssl147100 		port_change |= 1 << port;
29832651Ssl147100 	}
29842651Ssl147100 
29852651Ssl147100 	return (port_change);
29862651Ssl147100 }
29872651Ssl147100 
29882651Ssl147100 
29892651Ssl147100 /*
29900Sstevel@tonic-gate  * hubd_check_ports:
29910Sstevel@tonic-gate  *	- get hub descriptor
29920Sstevel@tonic-gate  *	- check initial port status
29930Sstevel@tonic-gate  *	- enable power on all ports
29940Sstevel@tonic-gate  *	- enable polling on ep1
29950Sstevel@tonic-gate  */
29960Sstevel@tonic-gate static int
hubd_check_ports(hubd_t * hubd)29970Sstevel@tonic-gate hubd_check_ports(hubd_t  *hubd)
29980Sstevel@tonic-gate {
29992651Ssl147100 	int			rval;
30002651Ssl147100 	usb_port_mask_t		port_change = 0;
30012651Ssl147100 	hubd_hotplug_arg_t	*arg;
30020Sstevel@tonic-gate 
30030Sstevel@tonic-gate 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
30040Sstevel@tonic-gate 
30050Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
30060Sstevel@tonic-gate 	    "hubd_check_ports: addr=0x%x", usb_get_addr(hubd->h_dip));
30070Sstevel@tonic-gate 
30080Sstevel@tonic-gate 	/*
30090Sstevel@tonic-gate 	 * First turn off all port power
30100Sstevel@tonic-gate 	 */
30110Sstevel@tonic-gate 	if ((rval = hubd_disable_all_port_power(hubd)) != USB_SUCCESS) {
30120Sstevel@tonic-gate 
30130Sstevel@tonic-gate 		/* disable whatever was enabled */
30140Sstevel@tonic-gate 		(void) hubd_disable_all_port_power(hubd);
30150Sstevel@tonic-gate 
30160Sstevel@tonic-gate 		return (rval);
30170Sstevel@tonic-gate 	}
30180Sstevel@tonic-gate 
30190Sstevel@tonic-gate 	/*
30200Sstevel@tonic-gate 	 * do not switch on immediately (instantly on root hub)
30210Sstevel@tonic-gate 	 * and allow time to settle
30220Sstevel@tonic-gate 	 */
30230Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
30240Sstevel@tonic-gate 	delay(drv_usectohz(10000));
30250Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
30260Sstevel@tonic-gate 
30270Sstevel@tonic-gate 	/*
30280Sstevel@tonic-gate 	 * enable power on all ports so we can see connects
30290Sstevel@tonic-gate 	 */
30300Sstevel@tonic-gate 	if ((rval = hubd_enable_all_port_power(hubd)) != USB_SUCCESS) {
30310Sstevel@tonic-gate 		/* disable whatever was enabled */
30320Sstevel@tonic-gate 		(void) hubd_disable_all_port_power(hubd);
30330Sstevel@tonic-gate 
30340Sstevel@tonic-gate 		return (rval);
30350Sstevel@tonic-gate 	}
30360Sstevel@tonic-gate 
30370Sstevel@tonic-gate 	/* wait at least 3 frames before accessing devices */
30380Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
30390Sstevel@tonic-gate 	delay(drv_usectohz(10000));
30400Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
30410Sstevel@tonic-gate 
30420Sstevel@tonic-gate 	/*
30430Sstevel@tonic-gate 	 * allocate arrays for saving the dips of each child per port
30440Sstevel@tonic-gate 	 *
30450Sstevel@tonic-gate 	 * ports go from 1 - n, allocate 1 more entry
30460Sstevel@tonic-gate 	 */
30470Sstevel@tonic-gate 	hubd->h_cd_list_length =
30484763Slg150142 	    (sizeof (dev_info_t **)) * (hubd->h_hub_descr.bNbrPorts + 1);
30490Sstevel@tonic-gate 
30500Sstevel@tonic-gate 	hubd->h_children_dips = (dev_info_t **)kmem_zalloc(
30514763Slg150142 	    hubd->h_cd_list_length, KM_SLEEP);
30520Sstevel@tonic-gate 	hubd->h_usba_devices = (usba_device_t **)kmem_zalloc(
30534763Slg150142 	    hubd->h_cd_list_length, KM_SLEEP);
30540Sstevel@tonic-gate 
30551001Ssl147100 	hubd->h_init_state |= HUBD_CHILDREN_CREATED;
30561001Ssl147100 
30572651Ssl147100 	mutex_exit(HUBD_MUTEX(hubd));
30582651Ssl147100 	arg = (hubd_hotplug_arg_t *)kmem_zalloc(
30592651Ssl147100 	    sizeof (hubd_hotplug_arg_t), KM_SLEEP);
30602651Ssl147100 	mutex_enter(HUBD_MUTEX(hubd));
30612651Ssl147100 
30622651Ssl147100 	if ((rval = hubd_open_intr_pipe(hubd)) != USB_SUCCESS) {
30632651Ssl147100 		kmem_free(arg, sizeof (hubd_hotplug_arg_t));
30642651Ssl147100 
30652651Ssl147100 		return (rval);
30662651Ssl147100 	}
30672651Ssl147100 
30682651Ssl147100 	hubd_start_polling(hubd, 0);
30692651Ssl147100 
30702651Ssl147100 	/*
30712651Ssl147100 	 * Some hub devices, like the embedded hub in the CKS ErgoMagic
30722651Ssl147100 	 * keyboard, may only have connection status bit set, but not
30732651Ssl147100 	 * have connect status change bit set when a device has been
30742651Ssl147100 	 * connected to its downstream port before the hub is enumerated.
30752651Ssl147100 	 * Then when the hub is in enumeration, the devices connected to
30762651Ssl147100 	 * it cannot be detected by the intr pipe and won't be enumerated.
30772651Ssl147100 	 * We need to check such situation here and enumerate the downstream
30782651Ssl147100 	 * devices for such hubs.
30792651Ssl147100 	 */
30802651Ssl147100 	port_change = hubd_determine_port_connection(hubd);
30812651Ssl147100 
30822651Ssl147100 	if (port_change) {
30832651Ssl147100 		hubd_pm_busy_component(hubd, hubd->h_dip, 0);
30842651Ssl147100 
30852651Ssl147100 		arg->hubd = hubd;
30862651Ssl147100 		arg->hotplug_during_attach = B_TRUE;
30872651Ssl147100 		hubd->h_port_change |= port_change;
30882651Ssl147100 
30892651Ssl147100 		USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
30902651Ssl147100 		    "hubd_check_ports: port change=0x%x, need to connect",
30912651Ssl147100 		    hubd->h_port_change);
30922651Ssl147100 
30932651Ssl147100 		if (usb_async_req(hubd->h_dip, hubd_hotplug_thread,
30942651Ssl147100 		    (void *)arg, 0) == USB_SUCCESS) {
30952651Ssl147100 			hubd->h_hotplug_thread++;
30962651Ssl147100 		} else {
30972651Ssl147100 			/* mark this device as idle */
30982651Ssl147100 			hubd_pm_idle_component(hubd, hubd->h_dip, 0);
30992651Ssl147100 			kmem_free(arg, sizeof (hubd_hotplug_arg_t));
31002651Ssl147100 		}
31012651Ssl147100 	} else {
31022651Ssl147100 		kmem_free(arg, sizeof (hubd_hotplug_arg_t));
31030Sstevel@tonic-gate 	}
31040Sstevel@tonic-gate 
31050Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
31060Sstevel@tonic-gate 	    "hubd_check_ports done");
31070Sstevel@tonic-gate 
31082651Ssl147100 	return (USB_SUCCESS);
31090Sstevel@tonic-gate }
31100Sstevel@tonic-gate 
31110Sstevel@tonic-gate 
31120Sstevel@tonic-gate /*
31130Sstevel@tonic-gate  * hubd_get_hub_descriptor:
31140Sstevel@tonic-gate  */
31150Sstevel@tonic-gate static int
hubd_get_hub_descriptor(hubd_t * hubd)31160Sstevel@tonic-gate hubd_get_hub_descriptor(hubd_t *hubd)
31170Sstevel@tonic-gate {
31180Sstevel@tonic-gate 	usb_hub_descr_t	*hub_descr = &hubd->h_hub_descr;
31190Sstevel@tonic-gate 	mblk_t		*data = NULL;
31200Sstevel@tonic-gate 	usb_cr_t	completion_reason;
31210Sstevel@tonic-gate 	usb_cb_flags_t	cb_flags;
31220Sstevel@tonic-gate 	uint16_t	length;
31230Sstevel@tonic-gate 	int		rval;
312412194SRaymond.Chen@Sun.COM 	usb_req_attrs_t attr = 0;
31250Sstevel@tonic-gate 
31260Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
31270Sstevel@tonic-gate 	    "hubd_get_hub_descriptor:");
31280Sstevel@tonic-gate 
312912194SRaymond.Chen@Sun.COM 	if ((hubd->h_dev_data->dev_descr->idVendor == USB_HUB_INTEL_VID) &&
313012194SRaymond.Chen@Sun.COM 	    (hubd->h_dev_data->dev_descr->idProduct == USB_HUB_INTEL_PID)) {
313112194SRaymond.Chen@Sun.COM 		attr = USB_ATTRS_SHORT_XFER_OK;
313212194SRaymond.Chen@Sun.COM 	}
313312194SRaymond.Chen@Sun.COM 
31340Sstevel@tonic-gate 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
31350Sstevel@tonic-gate 	ASSERT(hubd->h_default_pipe != 0);
31360Sstevel@tonic-gate 
31370Sstevel@tonic-gate 	/* get hub descriptor length first by requesting 8 bytes only */
31380Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
31390Sstevel@tonic-gate 
31400Sstevel@tonic-gate 	if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
31410Sstevel@tonic-gate 	    hubd->h_default_pipe,
31421001Ssl147100 	    HUB_CLASS_REQ_TYPE,
31430Sstevel@tonic-gate 	    USB_REQ_GET_DESCR,		/* bRequest */
31440Sstevel@tonic-gate 	    USB_DESCR_TYPE_SETUP_HUB,	/* wValue */
31450Sstevel@tonic-gate 	    0,				/* wIndex */
31460Sstevel@tonic-gate 	    8,				/* wLength */
31470Sstevel@tonic-gate 	    &data, 0,
31480Sstevel@tonic-gate 	    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
31490Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
31500Sstevel@tonic-gate 		    "get hub descriptor failed: cr=%d cb_fl=0x%x rval=%d",
31510Sstevel@tonic-gate 		    completion_reason, cb_flags, rval);
31520Sstevel@tonic-gate 		freemsg(data);
31530Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
31540Sstevel@tonic-gate 
31550Sstevel@tonic-gate 		return (rval);
31560Sstevel@tonic-gate 	}
31570Sstevel@tonic-gate 
31580Sstevel@tonic-gate 	length = *(data->b_rptr);
31590Sstevel@tonic-gate 
31600Sstevel@tonic-gate 	if (length > 8) {
31610Sstevel@tonic-gate 		freemsg(data);
31620Sstevel@tonic-gate 		data = NULL;
31630Sstevel@tonic-gate 
31640Sstevel@tonic-gate 		/* get complete hub descriptor */
316512194SRaymond.Chen@Sun.COM 		rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
31660Sstevel@tonic-gate 		    hubd->h_default_pipe,
31671001Ssl147100 		    HUB_CLASS_REQ_TYPE,
31680Sstevel@tonic-gate 		    USB_REQ_GET_DESCR,		/* bRequest */
31690Sstevel@tonic-gate 		    USB_DESCR_TYPE_SETUP_HUB,	/* wValue */
31700Sstevel@tonic-gate 		    0,				/* wIndex */
31710Sstevel@tonic-gate 		    length,			/* wLength */
317212194SRaymond.Chen@Sun.COM 		    &data, attr,
317312194SRaymond.Chen@Sun.COM 		    &completion_reason, &cb_flags, 0);
317412194SRaymond.Chen@Sun.COM 
317512194SRaymond.Chen@Sun.COM 		/*
317612194SRaymond.Chen@Sun.COM 		 * Hub descriptor data less than 9 bytes is not valid and
317712194SRaymond.Chen@Sun.COM 		 * may cause trouble if we use it. See USB2.0 Tab11-13.
317812194SRaymond.Chen@Sun.COM 		 */
317912194SRaymond.Chen@Sun.COM 		if ((rval != USB_SUCCESS) || (MBLKL(data) <= 8)) {
31800Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
31810Sstevel@tonic-gate 			    "get hub descriptor failed: "
318212194SRaymond.Chen@Sun.COM 			    "cr=%d cb_fl=0x%x rval=%d, len=%ld",
318312194SRaymond.Chen@Sun.COM 			    completion_reason, cb_flags, rval,
318412194SRaymond.Chen@Sun.COM 			    (data)?MBLKL(data):0);
31850Sstevel@tonic-gate 			freemsg(data);
31860Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
31870Sstevel@tonic-gate 
31880Sstevel@tonic-gate 			return (rval);
31890Sstevel@tonic-gate 		}
31900Sstevel@tonic-gate 	}
31910Sstevel@tonic-gate 
31920Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
31930Sstevel@tonic-gate 
31940Sstevel@tonic-gate 	/* parse the hub descriptor */
31950Sstevel@tonic-gate 	/* only 32 ports are supported at present */
31960Sstevel@tonic-gate 	ASSERT(*(data->b_rptr + 2) <= 32);
31970Sstevel@tonic-gate 	if (usb_parse_CV_descr("cccscccccc",
31987492SZhigang.Lu@Sun.COM 	    data->b_rptr, MBLKL(data),
31990Sstevel@tonic-gate 	    (void *)hub_descr, sizeof (usb_hub_descr_t)) == 0) {
32000Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
32010Sstevel@tonic-gate 		    "parsing hub descriptor failed");
32020Sstevel@tonic-gate 
32030Sstevel@tonic-gate 		freemsg(data);
32040Sstevel@tonic-gate 
32050Sstevel@tonic-gate 		return (USB_FAILURE);
32060Sstevel@tonic-gate 	}
32070Sstevel@tonic-gate 
32080Sstevel@tonic-gate 	freemsg(data);
32090Sstevel@tonic-gate 
32100Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
32111001Ssl147100 	    "rval=0x%x bNbrPorts=0x%x wHubChars=0x%x "
32121001Ssl147100 	    "PwrOn2PwrGood=0x%x HubContrCurrent=%dmA", rval,
32130Sstevel@tonic-gate 	    hub_descr->bNbrPorts, hub_descr->wHubCharacteristics,
32141001Ssl147100 	    hub_descr->bPwrOn2PwrGood, hub_descr->bHubContrCurrent);
32150Sstevel@tonic-gate 
32160Sstevel@tonic-gate 	if (hub_descr->bNbrPorts > MAX_PORTS) {
32170Sstevel@tonic-gate 		USB_DPRINTF_L0(DPRINT_MASK_ATTA, hubd->h_log_handle,
32180Sstevel@tonic-gate 		    "Hub driver supports max of %d ports on hub. "
32190Sstevel@tonic-gate 		    "Hence using the first %d port of %d ports available",
32200Sstevel@tonic-gate 		    MAX_PORTS, MAX_PORTS, hub_descr->bNbrPorts);
32210Sstevel@tonic-gate 
32220Sstevel@tonic-gate 		hub_descr->bNbrPorts = MAX_PORTS;
32230Sstevel@tonic-gate 	}
32240Sstevel@tonic-gate 
32250Sstevel@tonic-gate 	return (USB_SUCCESS);
32260Sstevel@tonic-gate }
32270Sstevel@tonic-gate 
32280Sstevel@tonic-gate 
32290Sstevel@tonic-gate /*
32301001Ssl147100  * hubd_get_hub_status_words:
32311001Ssl147100  */
32321001Ssl147100 static int
hubd_get_hub_status_words(hubd_t * hubd,uint16_t * status)32331001Ssl147100 hubd_get_hub_status_words(hubd_t *hubd, uint16_t *status)
32341001Ssl147100 {
32351001Ssl147100 	usb_cr_t	completion_reason;
32361001Ssl147100 	usb_cb_flags_t	cb_flags;
32371001Ssl147100 	mblk_t		*data = NULL;
32381001Ssl147100 
32391001Ssl147100 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
32401001Ssl147100 
32411001Ssl147100 	mutex_exit(HUBD_MUTEX(hubd));
32421001Ssl147100 
32431001Ssl147100 	if (usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe,
32441001Ssl147100 	    HUB_CLASS_REQ_TYPE,
32451001Ssl147100 	    USB_REQ_GET_STATUS,
32461001Ssl147100 	    0,
32471001Ssl147100 	    0,
32481001Ssl147100 	    GET_STATUS_LENGTH,
32491001Ssl147100 	    &data, 0,
32501001Ssl147100 	    &completion_reason, &cb_flags, 0) != USB_SUCCESS) {
32511001Ssl147100 		USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
32521001Ssl147100 		    "get hub status failed: cr=%d cb=0x%x",
32531001Ssl147100 		    completion_reason, cb_flags);
32541001Ssl147100 
32551001Ssl147100 		if (data) {
32561001Ssl147100 			freemsg(data);
32571001Ssl147100 		}
32581001Ssl147100 
32591001Ssl147100 		mutex_enter(HUBD_MUTEX(hubd));
32601001Ssl147100 
32611001Ssl147100 		return (USB_FAILURE);
32621001Ssl147100 	}
32631001Ssl147100 
32641001Ssl147100 	mutex_enter(HUBD_MUTEX(hubd));
32651001Ssl147100 
32661001Ssl147100 	status[0] = (*(data->b_rptr + 1) << 8) | *(data->b_rptr);
32671001Ssl147100 	status[1] = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2);
32681001Ssl147100 
32691001Ssl147100 	USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle,
32701001Ssl147100 	    "hub status=0x%x change=0x%x", status[0], status[1]);
32711001Ssl147100 
32721001Ssl147100 	freemsg(data);
32731001Ssl147100 
32741001Ssl147100 	return (USB_SUCCESS);
32751001Ssl147100 }
32761001Ssl147100 
32771001Ssl147100 
32781001Ssl147100 /*
32790Sstevel@tonic-gate  * hubd_open_intr_pipe:
32800Sstevel@tonic-gate  *	we read all descriptors first for curiosity and then simply
32810Sstevel@tonic-gate  *	open the pipe
32820Sstevel@tonic-gate  */
32830Sstevel@tonic-gate static int
hubd_open_intr_pipe(hubd_t * hubd)32840Sstevel@tonic-gate hubd_open_intr_pipe(hubd_t	*hubd)
32850Sstevel@tonic-gate {
32860Sstevel@tonic-gate 	int			rval;
32870Sstevel@tonic-gate 
32880Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
32890Sstevel@tonic-gate 	    "hubd_open_intr_pipe:");
32900Sstevel@tonic-gate 
32910Sstevel@tonic-gate 	ASSERT(hubd->h_intr_pipe_state == HUBD_INTR_PIPE_IDLE);
32920Sstevel@tonic-gate 
32930Sstevel@tonic-gate 	hubd->h_intr_pipe_state = HUBD_INTR_PIPE_OPENING;
32940Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
32950Sstevel@tonic-gate 
32960Sstevel@tonic-gate 	if ((rval = usb_pipe_open(hubd->h_dip,
32970Sstevel@tonic-gate 	    &hubd->h_ep1_descr, &hubd->h_pipe_policy,
32980Sstevel@tonic-gate 	    0, &hubd->h_ep1_ph)) != USB_SUCCESS) {
3299978Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
33000Sstevel@tonic-gate 		    "open intr pipe failed (%d)", rval);
33010Sstevel@tonic-gate 
33020Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
33030Sstevel@tonic-gate 		hubd->h_intr_pipe_state = HUBD_INTR_PIPE_IDLE;
33040Sstevel@tonic-gate 
33050Sstevel@tonic-gate 		return (rval);
33060Sstevel@tonic-gate 	}
33070Sstevel@tonic-gate 
33080Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
33090Sstevel@tonic-gate 	hubd->h_intr_pipe_state = HUBD_INTR_PIPE_ACTIVE;
33100Sstevel@tonic-gate 
33110Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
33126898Sfb209375 	    "open intr pipe succeeded, ph=0x%p", (void *)hubd->h_ep1_ph);
33130Sstevel@tonic-gate 
33140Sstevel@tonic-gate 	return (USB_SUCCESS);
33150Sstevel@tonic-gate }
33160Sstevel@tonic-gate 
33170Sstevel@tonic-gate 
33180Sstevel@tonic-gate /*
33190Sstevel@tonic-gate  * hubd_start_polling:
33200Sstevel@tonic-gate  *	start or restart the polling
33210Sstevel@tonic-gate  */
33220Sstevel@tonic-gate static void
hubd_start_polling(hubd_t * hubd,int always)33230Sstevel@tonic-gate hubd_start_polling(hubd_t *hubd, int always)
33240Sstevel@tonic-gate {
33250Sstevel@tonic-gate 	usb_intr_req_t	*reqp;
33260Sstevel@tonic-gate 	int			rval;
33270Sstevel@tonic-gate 	usb_pipe_state_t	pipe_state;
33280Sstevel@tonic-gate 
33290Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
33300Sstevel@tonic-gate 	    "start polling: always=%d dev_state=%d pipe_state=%d\n\t"
33310Sstevel@tonic-gate 	    "thread=%d ep1_ph=0x%p",
33320Sstevel@tonic-gate 	    always, hubd->h_dev_state, hubd->h_intr_pipe_state,
33336898Sfb209375 	    hubd->h_hotplug_thread, (void *)hubd->h_ep1_ph);
33340Sstevel@tonic-gate 
33350Sstevel@tonic-gate 	/*
33360Sstevel@tonic-gate 	 * start or restart polling on the intr pipe
33370Sstevel@tonic-gate 	 * only if hotplug thread is not running
33380Sstevel@tonic-gate 	 */
33390Sstevel@tonic-gate 	if ((always == HUBD_ALWAYS_START_POLLING) ||
33400Sstevel@tonic-gate 	    ((hubd->h_dev_state == USB_DEV_ONLINE) &&
33410Sstevel@tonic-gate 	    (hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE) &&
33420Sstevel@tonic-gate 	    (hubd->h_hotplug_thread == 0) && hubd->h_ep1_ph)) {
33430Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
33440Sstevel@tonic-gate 		    "start polling requested");
33450Sstevel@tonic-gate 
33460Sstevel@tonic-gate 		reqp = usb_alloc_intr_req(hubd->h_dip, 0, USB_FLAGS_SLEEP);
33470Sstevel@tonic-gate 
33480Sstevel@tonic-gate 		reqp->intr_client_private = (usb_opaque_t)hubd;
33490Sstevel@tonic-gate 		reqp->intr_attributes = USB_ATTRS_SHORT_XFER_OK |
33504763Slg150142 		    USB_ATTRS_AUTOCLEARING;
33510Sstevel@tonic-gate 		reqp->intr_len = hubd->h_ep1_descr.wMaxPacketSize;
33520Sstevel@tonic-gate 		reqp->intr_cb = hubd_read_cb;
33530Sstevel@tonic-gate 		reqp->intr_exc_cb = hubd_exception_cb;
33540Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
33550Sstevel@tonic-gate 		if ((rval = usb_pipe_intr_xfer(hubd->h_ep1_ph, reqp,
33560Sstevel@tonic-gate 		    USB_FLAGS_SLEEP)) != USB_SUCCESS) {
33570Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
33580Sstevel@tonic-gate 			    "start polling failed, rval=%d", rval);
33590Sstevel@tonic-gate 			usb_free_intr_req(reqp);
33600Sstevel@tonic-gate 		}
33610Sstevel@tonic-gate 
33620Sstevel@tonic-gate 		rval = usb_pipe_get_state(hubd->h_ep1_ph, &pipe_state,
33634763Slg150142 		    USB_FLAGS_SLEEP);
33640Sstevel@tonic-gate 		if (pipe_state != USB_PIPE_STATE_ACTIVE) {
33650Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
33660Sstevel@tonic-gate 			    "intr pipe state=%d, rval=%d", pipe_state, rval);
33670Sstevel@tonic-gate 		}
33680Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
33696898Sfb209375 		    "start polling request 0x%p", (void *)reqp);
33700Sstevel@tonic-gate 
33710Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
33720Sstevel@tonic-gate 	}
33730Sstevel@tonic-gate }
33740Sstevel@tonic-gate 
33750Sstevel@tonic-gate 
33760Sstevel@tonic-gate /*
33770Sstevel@tonic-gate  * hubd_stop_polling
33780Sstevel@tonic-gate  *	stop polling but do not close the pipe
33790Sstevel@tonic-gate  */
33800Sstevel@tonic-gate static void
hubd_stop_polling(hubd_t * hubd)33810Sstevel@tonic-gate hubd_stop_polling(hubd_t *hubd)
33820Sstevel@tonic-gate {
33830Sstevel@tonic-gate 	int			rval;
33840Sstevel@tonic-gate 	usb_pipe_state_t	pipe_state;
33850Sstevel@tonic-gate 
33860Sstevel@tonic-gate 	if (hubd->h_ep1_ph) {
33870Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
33880Sstevel@tonic-gate 		    "hubd_stop_polling:");
33890Sstevel@tonic-gate 		hubd->h_intr_pipe_state = HUBD_INTR_PIPE_STOPPED;
33900Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
33910Sstevel@tonic-gate 
33920Sstevel@tonic-gate 		usb_pipe_stop_intr_polling(hubd->h_ep1_ph, USB_FLAGS_SLEEP);
33930Sstevel@tonic-gate 		rval = usb_pipe_get_state(hubd->h_ep1_ph, &pipe_state,
33944763Slg150142 		    USB_FLAGS_SLEEP);
33950Sstevel@tonic-gate 
33960Sstevel@tonic-gate 		if (pipe_state != USB_PIPE_STATE_IDLE) {
33970Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
33980Sstevel@tonic-gate 			    "intr pipe state=%d, rval=%d", pipe_state, rval);
33990Sstevel@tonic-gate 		}
34000Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
34010Sstevel@tonic-gate 		if (hubd->h_intr_pipe_state == HUBD_INTR_PIPE_STOPPED) {
34020Sstevel@tonic-gate 			hubd->h_intr_pipe_state = HUBD_INTR_PIPE_ACTIVE;
34030Sstevel@tonic-gate 		}
34040Sstevel@tonic-gate 	}
34050Sstevel@tonic-gate }
34060Sstevel@tonic-gate 
34070Sstevel@tonic-gate 
34080Sstevel@tonic-gate /*
34090Sstevel@tonic-gate  * hubd_close_intr_pipe:
34100Sstevel@tonic-gate  *	close the pipe (which also stops the polling
34110Sstevel@tonic-gate  *	and wait for the hotplug thread to exit
34120Sstevel@tonic-gate  */
34130Sstevel@tonic-gate static void
hubd_close_intr_pipe(hubd_t * hubd)34140Sstevel@tonic-gate hubd_close_intr_pipe(hubd_t *hubd)
34150Sstevel@tonic-gate {
34160Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
34170Sstevel@tonic-gate 	    "hubd_close_intr_pipe:");
34180Sstevel@tonic-gate 
34190Sstevel@tonic-gate 	/*
34200Sstevel@tonic-gate 	 * Now that no async operation is outstanding on pipe,
34210Sstevel@tonic-gate 	 * we can change the state to HUBD_INTR_PIPE_CLOSING
34220Sstevel@tonic-gate 	 */
34230Sstevel@tonic-gate 	hubd->h_intr_pipe_state = HUBD_INTR_PIPE_CLOSING;
34240Sstevel@tonic-gate 
34250Sstevel@tonic-gate 	ASSERT(hubd->h_hotplug_thread == 0);
34260Sstevel@tonic-gate 
34270Sstevel@tonic-gate 	if (hubd->h_ep1_ph) {
34280Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
34290Sstevel@tonic-gate 		usb_pipe_close(hubd->h_dip, hubd->h_ep1_ph, USB_FLAGS_SLEEP,
34304763Slg150142 		    NULL, NULL);
34310Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
34320Sstevel@tonic-gate 		hubd->h_ep1_ph = NULL;
34330Sstevel@tonic-gate 	}
34340Sstevel@tonic-gate 
34350Sstevel@tonic-gate 	hubd->h_intr_pipe_state = HUBD_INTR_PIPE_IDLE;
34360Sstevel@tonic-gate }
34370Sstevel@tonic-gate 
34380Sstevel@tonic-gate 
34390Sstevel@tonic-gate /*
34400Sstevel@tonic-gate  * hubd_exception_cb
34410Sstevel@tonic-gate  *	interrupt ep1 exception callback function.
34420Sstevel@tonic-gate  *	this callback executes in taskq thread context and assumes
34430Sstevel@tonic-gate  *	autoclearing
34440Sstevel@tonic-gate  */
34450Sstevel@tonic-gate /*ARGSUSED*/
34460Sstevel@tonic-gate static void
hubd_exception_cb(usb_pipe_handle_t pipe,usb_intr_req_t * reqp)34470Sstevel@tonic-gate hubd_exception_cb(usb_pipe_handle_t pipe, usb_intr_req_t *reqp)
34480Sstevel@tonic-gate {
34490Sstevel@tonic-gate 	hubd_t		*hubd = (hubd_t *)(reqp->intr_client_private);
34500Sstevel@tonic-gate 
34510Sstevel@tonic-gate 	USB_DPRINTF_L2(DPRINT_MASK_CALLBACK, hubd->h_log_handle,
34520Sstevel@tonic-gate 	    "hubd_exception_cb: "
34536898Sfb209375 	    "req=0x%p cr=%d data=0x%p cb_flags=0x%x", (void *)reqp,
34546898Sfb209375 	    reqp->intr_completion_reason, (void *)reqp->intr_data,
34550Sstevel@tonic-gate 	    reqp->intr_cb_flags);
34560Sstevel@tonic-gate 
34570Sstevel@tonic-gate 	ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
34580Sstevel@tonic-gate 
34590Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
34600Sstevel@tonic-gate 	(void) hubd_pm_busy_component(hubd, hubd->h_dip, 0);
34610Sstevel@tonic-gate 
34620Sstevel@tonic-gate 	switch (reqp->intr_completion_reason) {
34630Sstevel@tonic-gate 	case USB_CR_PIPE_RESET:
34640Sstevel@tonic-gate 		/* only restart polling after autoclearing */
34650Sstevel@tonic-gate 		if ((hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE) &&
34660Sstevel@tonic-gate 		    (hubd->h_port_reset_wait == 0)) {
34670Sstevel@tonic-gate 			hubd_start_polling(hubd, 0);
34680Sstevel@tonic-gate 		}
34690Sstevel@tonic-gate 
34700Sstevel@tonic-gate 		break;
34710Sstevel@tonic-gate 	case USB_CR_DEV_NOT_RESP:
34720Sstevel@tonic-gate 	case USB_CR_STOPPED_POLLING:
34730Sstevel@tonic-gate 	case USB_CR_PIPE_CLOSING:
34740Sstevel@tonic-gate 	case USB_CR_UNSPECIFIED_ERR:
34750Sstevel@tonic-gate 		/* never restart polling on these conditions */
34760Sstevel@tonic-gate 	default:
34770Sstevel@tonic-gate 		/* for all others, wait for the autoclearing PIPE_RESET cb */
34780Sstevel@tonic-gate 
34790Sstevel@tonic-gate 		break;
34800Sstevel@tonic-gate 	}
34810Sstevel@tonic-gate 
34820Sstevel@tonic-gate 	usb_free_intr_req(reqp);
34830Sstevel@tonic-gate 	(void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
34840Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
34850Sstevel@tonic-gate }
34860Sstevel@tonic-gate 
34870Sstevel@tonic-gate 
34880Sstevel@tonic-gate /*
34890Sstevel@tonic-gate  * helper function to convert LE bytes to a portmask
34900Sstevel@tonic-gate  */
34910Sstevel@tonic-gate static usb_port_mask_t
hubd_mblk2portmask(mblk_t * data)34920Sstevel@tonic-gate hubd_mblk2portmask(mblk_t *data)
34930Sstevel@tonic-gate {
34947492SZhigang.Lu@Sun.COM 	int len = min(MBLKL(data), sizeof (usb_port_mask_t));
34950Sstevel@tonic-gate 	usb_port_mask_t rval = 0;
34960Sstevel@tonic-gate 	int i;
34970Sstevel@tonic-gate 
34980Sstevel@tonic-gate 	for (i = 0; i < len; i++) {
34990Sstevel@tonic-gate 		rval |= data->b_rptr[i] << (i * 8);
35000Sstevel@tonic-gate 	}
35010Sstevel@tonic-gate 
35020Sstevel@tonic-gate 	return (rval);
35030Sstevel@tonic-gate }
35040Sstevel@tonic-gate 
35050Sstevel@tonic-gate 
35060Sstevel@tonic-gate /*
35070Sstevel@tonic-gate  * hubd_read_cb:
35080Sstevel@tonic-gate  *	interrupt ep1 callback function
35090Sstevel@tonic-gate  *
35100Sstevel@tonic-gate  *	the status indicates just a change on the pipe with no indication
35110Sstevel@tonic-gate  *	of what the change was
35120Sstevel@tonic-gate  *
35130Sstevel@tonic-gate  *	known conditions:
35140Sstevel@tonic-gate  *		- reset port completion
35150Sstevel@tonic-gate  *		- connect
35160Sstevel@tonic-gate  *		- disconnect
35170Sstevel@tonic-gate  *
35180Sstevel@tonic-gate  *	for handling the hotplugging, create a new thread that can do
35190Sstevel@tonic-gate  *	synchronous usba calls
35200Sstevel@tonic-gate  */
35210Sstevel@tonic-gate static void
hubd_read_cb(usb_pipe_handle_t pipe,usb_intr_req_t * reqp)35220Sstevel@tonic-gate hubd_read_cb(usb_pipe_handle_t pipe, usb_intr_req_t *reqp)
35230Sstevel@tonic-gate {
35240Sstevel@tonic-gate 	hubd_t		*hubd = (hubd_t *)(reqp->intr_client_private);
35250Sstevel@tonic-gate 	size_t		length;
35260Sstevel@tonic-gate 	mblk_t		*data = reqp->intr_data;
35272651Ssl147100 	int		mem_flag = 0;
35282651Ssl147100 	hubd_hotplug_arg_t *arg;
35290Sstevel@tonic-gate 
35300Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
35316898Sfb209375 	    "hubd_read_cb: ph=0x%p req=0x%p", (void *)pipe, (void *)reqp);
35320Sstevel@tonic-gate 
35330Sstevel@tonic-gate 	ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
35340Sstevel@tonic-gate 
35350Sstevel@tonic-gate 	/*
35360Sstevel@tonic-gate 	 * At present, we are not handling notification for completion of
35370Sstevel@tonic-gate 	 * asynchronous pipe reset, for which this data ptr could be NULL
35380Sstevel@tonic-gate 	 */
35390Sstevel@tonic-gate 
35400Sstevel@tonic-gate 	if (data == NULL) {
35410Sstevel@tonic-gate 		usb_free_intr_req(reqp);
35420Sstevel@tonic-gate 
35430Sstevel@tonic-gate 		return;
35440Sstevel@tonic-gate 	}
35450Sstevel@tonic-gate 
35462651Ssl147100 	arg = (hubd_hotplug_arg_t *)kmem_zalloc(
35472651Ssl147100 	    sizeof (hubd_hotplug_arg_t), KM_SLEEP);
35482651Ssl147100 	mem_flag = 1;
35492651Ssl147100 
35500Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
35510Sstevel@tonic-gate 
35520Sstevel@tonic-gate 	if ((hubd->h_dev_state == USB_DEV_SUSPENDED) ||
35530Sstevel@tonic-gate 	    (hubd->h_intr_pipe_state != HUBD_INTR_PIPE_ACTIVE)) {
35540Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
35550Sstevel@tonic-gate 		usb_free_intr_req(reqp);
35562651Ssl147100 		kmem_free(arg, sizeof (hubd_hotplug_arg_t));
35570Sstevel@tonic-gate 
35580Sstevel@tonic-gate 		return;
35590Sstevel@tonic-gate 	}
35600Sstevel@tonic-gate 
35610Sstevel@tonic-gate 	ASSERT(hubd->h_ep1_ph == pipe);
35620Sstevel@tonic-gate 
35637492SZhigang.Lu@Sun.COM 	length = MBLKL(data);
35640Sstevel@tonic-gate 
35650Sstevel@tonic-gate 	/*
35660Sstevel@tonic-gate 	 * Only look at the data and startup the hotplug thread if
35670Sstevel@tonic-gate 	 * there actually is data.
35680Sstevel@tonic-gate 	 */
35690Sstevel@tonic-gate 	if (length != 0) {
35700Sstevel@tonic-gate 		usb_port_mask_t port_change = hubd_mblk2portmask(data);
35710Sstevel@tonic-gate 
35720Sstevel@tonic-gate 		/*
35730Sstevel@tonic-gate 		 * if a port change was already reported and we are waiting for
35740Sstevel@tonic-gate 		 * reset port completion then wake up the hotplug thread which
35750Sstevel@tonic-gate 		 * should be waiting on reset port completion
35760Sstevel@tonic-gate 		 *
35770Sstevel@tonic-gate 		 * if there is disconnect event instead of reset completion, let
35780Sstevel@tonic-gate 		 * the hotplug thread figure this out
35790Sstevel@tonic-gate 		 */
35800Sstevel@tonic-gate 
35810Sstevel@tonic-gate 		/* remove the reset wait bits from the status */
35820Sstevel@tonic-gate 		hubd->h_port_change |= port_change &
35834763Slg150142 		    ~hubd->h_port_reset_wait;
35840Sstevel@tonic-gate 
35850Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_CALLBACK, hubd->h_log_handle,
35860Sstevel@tonic-gate 		    "port change=0x%x port_reset_wait=0x%x",
35870Sstevel@tonic-gate 		    hubd->h_port_change, hubd->h_port_reset_wait);
35880Sstevel@tonic-gate 
35890Sstevel@tonic-gate 		/* there should be only one reset bit active at the time */
35900Sstevel@tonic-gate 		if (hubd->h_port_reset_wait & port_change) {
35910Sstevel@tonic-gate 			hubd->h_port_reset_wait = 0;
35920Sstevel@tonic-gate 			cv_signal(&hubd->h_cv_reset_port);
35930Sstevel@tonic-gate 		}
35940Sstevel@tonic-gate 
35950Sstevel@tonic-gate 		/*
35960Sstevel@tonic-gate 		 * kick off the thread only if device is ONLINE and it is not
35970Sstevel@tonic-gate 		 * during attaching or detaching
35980Sstevel@tonic-gate 		 */
35990Sstevel@tonic-gate 		if ((hubd->h_dev_state == USB_DEV_ONLINE) &&
36000Sstevel@tonic-gate 		    (!DEVI_IS_ATTACHING(hubd->h_dip)) &&
36010Sstevel@tonic-gate 		    (!DEVI_IS_DETACHING(hubd->h_dip)) &&
36020Sstevel@tonic-gate 		    (hubd->h_port_change) &&
36030Sstevel@tonic-gate 		    (hubd->h_hotplug_thread == 0)) {
36040Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_CALLBACK, hubd->h_log_handle,
36050Sstevel@tonic-gate 			    "creating hotplug thread: "
36060Sstevel@tonic-gate 			    "dev_state=%d", hubd->h_dev_state);
36070Sstevel@tonic-gate 
36080Sstevel@tonic-gate 			/*
36090Sstevel@tonic-gate 			 * Mark this device as busy. The will be marked idle
36100Sstevel@tonic-gate 			 * if the async req fails or at the exit of  hotplug
36110Sstevel@tonic-gate 			 * thread
36120Sstevel@tonic-gate 			 */
36130Sstevel@tonic-gate 			(void) hubd_pm_busy_component(hubd, hubd->h_dip, 0);
36140Sstevel@tonic-gate 
36152651Ssl147100 			arg->hubd = hubd;
36162651Ssl147100 			arg->hotplug_during_attach = B_FALSE;
36172651Ssl147100 
36180Sstevel@tonic-gate 			if (usb_async_req(hubd->h_dip,
36190Sstevel@tonic-gate 			    hubd_hotplug_thread,
36202651Ssl147100 			    (void *)arg, 0) == USB_SUCCESS) {
36210Sstevel@tonic-gate 				hubd->h_hotplug_thread++;
36222651Ssl147100 				mem_flag = 0;
36230Sstevel@tonic-gate 			} else {
36240Sstevel@tonic-gate 				/* mark this device as idle */
36250Sstevel@tonic-gate 				(void) hubd_pm_idle_component(hubd,
36260Sstevel@tonic-gate 				    hubd->h_dip, 0);
36270Sstevel@tonic-gate 			}
36280Sstevel@tonic-gate 		}
36290Sstevel@tonic-gate 	}
36300Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
36310Sstevel@tonic-gate 
36322651Ssl147100 	if (mem_flag == 1) {
36332651Ssl147100 		kmem_free(arg, sizeof (hubd_hotplug_arg_t));
36342651Ssl147100 	}
36352651Ssl147100 
36360Sstevel@tonic-gate 	usb_free_intr_req(reqp);
36370Sstevel@tonic-gate }
36380Sstevel@tonic-gate 
36390Sstevel@tonic-gate 
36400Sstevel@tonic-gate /*
36410Sstevel@tonic-gate  * hubd_hotplug_thread:
36420Sstevel@tonic-gate  *	handles resetting of port, and creating children
36430Sstevel@tonic-gate  *
36440Sstevel@tonic-gate  *	the ports to check are indicated in h_port_change bit mask
36450Sstevel@tonic-gate  * XXX note that one time poll doesn't work on the root hub
36460Sstevel@tonic-gate  */
36470Sstevel@tonic-gate static void
hubd_hotplug_thread(void * arg)36480Sstevel@tonic-gate hubd_hotplug_thread(void *arg)
36490Sstevel@tonic-gate {
36502651Ssl147100 	hubd_hotplug_arg_t *hd_arg = (hubd_hotplug_arg_t *)arg;
36512651Ssl147100 	hubd_t		*hubd = hd_arg->hubd;
36522651Ssl147100 	boolean_t	attach_flg = hd_arg->hotplug_during_attach;
36530Sstevel@tonic-gate 	usb_port_t	port;
36540Sstevel@tonic-gate 	uint16_t	nports;
36550Sstevel@tonic-gate 	uint16_t	status, change;
36560Sstevel@tonic-gate 	hub_power_t	*hubpm;
36570Sstevel@tonic-gate 	dev_info_t	*hdip = hubd->h_dip;
36580Sstevel@tonic-gate 	dev_info_t	*rh_dip = hubd->h_usba_device->usb_root_hub_dip;
36594763Slg150142 	dev_info_t	*child_dip;
36600Sstevel@tonic-gate 	boolean_t	online_child = B_FALSE;
36610Sstevel@tonic-gate 	boolean_t	offline_child = B_FALSE;
36620Sstevel@tonic-gate 	boolean_t	pwrup_child = B_FALSE;
36634763Slg150142 	int		prh_circ, rh_circ, chld_circ, circ, old_state;
36640Sstevel@tonic-gate 
36650Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
36660Sstevel@tonic-gate 	    "hubd_hotplug_thread:  started");
36670Sstevel@tonic-gate 
366810783SVincent.Wang@Sun.COM 	/*
366910783SVincent.Wang@Sun.COM 	 * Before console is init'd, we temporarily block the hotplug
367010783SVincent.Wang@Sun.COM 	 * threads so that BUS_CONFIG_ONE through hubd_bus_config() can be
367110783SVincent.Wang@Sun.COM 	 * processed quickly. This reduces the time needed for vfs_mountroot()
367211987SFei.Feng@Sun.COM 	 * to mount the root FS from a USB disk. And on SPARC platform,
367311987SFei.Feng@Sun.COM 	 * in order to load 'consconfig' successfully after OBP is gone,
367411987SFei.Feng@Sun.COM 	 * we need to check 'modrootloaded' to make sure root filesystem is
367511987SFei.Feng@Sun.COM 	 * available.
367610783SVincent.Wang@Sun.COM 	 */
367711987SFei.Feng@Sun.COM 	while (!modrootloaded || !consconfig_console_is_ready()) {
367810783SVincent.Wang@Sun.COM 		delay(drv_usectohz(10000));
367910783SVincent.Wang@Sun.COM 	}
368010783SVincent.Wang@Sun.COM 
36812651Ssl147100 	kmem_free(arg, sizeof (hubd_hotplug_arg_t));
36822651Ssl147100 
36830Sstevel@tonic-gate 	/*
36840Sstevel@tonic-gate 	 * if our bus power entry point is active, process the change
36850Sstevel@tonic-gate 	 * on the next notification of interrupt pipe
36860Sstevel@tonic-gate 	 */
36870Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
36880Sstevel@tonic-gate 	if (hubd->h_bus_pwr || (hubd->h_hotplug_thread > 1)) {
36890Sstevel@tonic-gate 		hubd->h_hotplug_thread--;
36900Sstevel@tonic-gate 
36910Sstevel@tonic-gate 		/* mark this device as idle */
36920Sstevel@tonic-gate 		hubd_pm_idle_component(hubd, hubd->h_dip, 0);
36930Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
36940Sstevel@tonic-gate 
36950Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
36960Sstevel@tonic-gate 		    "hubd_hotplug_thread: "
36970Sstevel@tonic-gate 		    "bus_power in progress/hotplugging undesirable - quit");
36980Sstevel@tonic-gate 
36990Sstevel@tonic-gate 		return;
37000Sstevel@tonic-gate 	}
37010Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
37020Sstevel@tonic-gate 
37030Sstevel@tonic-gate 	ndi_hold_devi(hdip); /* so we don't race with detach */
37040Sstevel@tonic-gate 
37050Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
37060Sstevel@tonic-gate 
37070Sstevel@tonic-gate 	/* is this the root hub? */
37080Sstevel@tonic-gate 	if (hdip == rh_dip) {
37090Sstevel@tonic-gate 		if (hubd->h_dev_state == USB_DEV_PWRED_DOWN) {
37100Sstevel@tonic-gate 			hubpm = hubd->h_hubpm;
37110Sstevel@tonic-gate 
37120Sstevel@tonic-gate 			/* mark the root hub as full power */
37130Sstevel@tonic-gate 			hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR;
37140Sstevel@tonic-gate 			hubpm->hubp_time_at_full_power = ddi_get_time();
37150Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
37160Sstevel@tonic-gate 
37170Sstevel@tonic-gate 			USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
37180Sstevel@tonic-gate 			    "hubd_hotplug_thread: call pm_power_has_changed");
37190Sstevel@tonic-gate 
37200Sstevel@tonic-gate 			(void) pm_power_has_changed(hdip, 0,
37214763Slg150142 			    USB_DEV_OS_FULL_PWR);
37220Sstevel@tonic-gate 
37230Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
37240Sstevel@tonic-gate 			hubd->h_dev_state = USB_DEV_ONLINE;
37250Sstevel@tonic-gate 		}
37260Sstevel@tonic-gate 
37270Sstevel@tonic-gate 	} else {
37280Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
37290Sstevel@tonic-gate 		    "hubd_hotplug_thread: not root hub");
37300Sstevel@tonic-gate 	}
37310Sstevel@tonic-gate 
37320Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
37330Sstevel@tonic-gate 
37340Sstevel@tonic-gate 	/*
37350Sstevel@tonic-gate 	 * this ensures one hotplug activity per system at a time.
37360Sstevel@tonic-gate 	 * we enter the parent PCI node to have this serialization.
37370Sstevel@tonic-gate 	 * this also excludes ioctls and deathrow thread
37380Sstevel@tonic-gate 	 * (a bit crude but easier to debug)
37390Sstevel@tonic-gate 	 */
37400Sstevel@tonic-gate 	ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
37410Sstevel@tonic-gate 	ndi_devi_enter(rh_dip, &rh_circ);
37420Sstevel@tonic-gate 
37430Sstevel@tonic-gate 	/* exclude other threads */
37440Sstevel@tonic-gate 	ndi_devi_enter(hdip, &circ);
37450Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
37460Sstevel@tonic-gate 
374711214SVincent.Wang@Sun.COM 	ASSERT(hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE);
374811214SVincent.Wang@Sun.COM 
374911214SVincent.Wang@Sun.COM 	nports = hubd->h_hub_descr.bNbrPorts;
375011214SVincent.Wang@Sun.COM 
375111214SVincent.Wang@Sun.COM 	hubd_stop_polling(hubd);
375211214SVincent.Wang@Sun.COM 
37530Sstevel@tonic-gate 	while ((hubd->h_dev_state == USB_DEV_ONLINE) &&
37540Sstevel@tonic-gate 	    (hubd->h_port_change)) {
37550Sstevel@tonic-gate 		/*
37560Sstevel@tonic-gate 		 * The 0th bit is the hub status change bit.
37570Sstevel@tonic-gate 		 * handle loss of local power here
37580Sstevel@tonic-gate 		 */
37590Sstevel@tonic-gate 		if (hubd->h_port_change & HUB_CHANGE_STATUS) {
37600Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
37610Sstevel@tonic-gate 			    "hubd_hotplug_thread: hub status change!");
37620Sstevel@tonic-gate 
37630Sstevel@tonic-gate 			/*
37640Sstevel@tonic-gate 			 * This should be handled properly.  For now,
37650Sstevel@tonic-gate 			 * mask off the bit.
37660Sstevel@tonic-gate 			 */
37670Sstevel@tonic-gate 			hubd->h_port_change &= ~HUB_CHANGE_STATUS;
37680Sstevel@tonic-gate 
37690Sstevel@tonic-gate 			/*
37700Sstevel@tonic-gate 			 * check and ack hub status
37710Sstevel@tonic-gate 			 * this causes stall conditions
37720Sstevel@tonic-gate 			 * when local power is removed
37730Sstevel@tonic-gate 			 */
37740Sstevel@tonic-gate 			(void) hubd_get_hub_status(hubd);
37750Sstevel@tonic-gate 		}
37760Sstevel@tonic-gate 
37770Sstevel@tonic-gate 		for (port = 1; port <= nports; port++) {
37780Sstevel@tonic-gate 			usb_port_mask_t port_mask;
37790Sstevel@tonic-gate 			boolean_t was_connected;
37800Sstevel@tonic-gate 
37810Sstevel@tonic-gate 			port_mask = 1 << port;
37820Sstevel@tonic-gate 			was_connected =
37834763Slg150142 			    (hubd->h_port_state[port] & PORT_STATUS_CCS) &&
37844763Slg150142 			    (hubd->h_children_dips[port]);
37850Sstevel@tonic-gate 
37860Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
37870Sstevel@tonic-gate 			    "hubd_hotplug_thread: "
37880Sstevel@tonic-gate 			    "port %d mask=0x%x change=0x%x connected=0x%x",
37890Sstevel@tonic-gate 			    port, port_mask, hubd->h_port_change,
37900Sstevel@tonic-gate 			    was_connected);
37910Sstevel@tonic-gate 
37920Sstevel@tonic-gate 			/*
37930Sstevel@tonic-gate 			 * is this a port connection that changed?
37940Sstevel@tonic-gate 			 */
37950Sstevel@tonic-gate 			if ((hubd->h_port_change & port_mask) == 0) {
37960Sstevel@tonic-gate 
37970Sstevel@tonic-gate 				continue;
37980Sstevel@tonic-gate 			}
37990Sstevel@tonic-gate 			hubd->h_port_change &= ~port_mask;
38000Sstevel@tonic-gate 
38010Sstevel@tonic-gate 			/* ack all changes */
38020Sstevel@tonic-gate 			(void) hubd_determine_port_status(hubd, port,
38030Sstevel@tonic-gate 			    &status, &change, HUBD_ACK_ALL_CHANGES);
38040Sstevel@tonic-gate 
38050Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
38060Sstevel@tonic-gate 			    "handle port %d:\n\t"
38070Sstevel@tonic-gate 			    "new status=0x%x change=0x%x was_conn=0x%x ",
38080Sstevel@tonic-gate 			    port, status, change, was_connected);
38090Sstevel@tonic-gate 
38100Sstevel@tonic-gate 			/* Recover a disabled port */
38110Sstevel@tonic-gate 			if (change & PORT_CHANGE_PESC) {
38120Sstevel@tonic-gate 				USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG,
38134763Slg150142 				    hubd->h_log_handle,
38144763Slg150142 				    "port%d Disabled - "
38154763Slg150142 				    "status=0x%x, change=0x%x",
38164763Slg150142 				    port, status, change);
38170Sstevel@tonic-gate 
38180Sstevel@tonic-gate 				/*
38190Sstevel@tonic-gate 				 * if the port was connected and is still
38200Sstevel@tonic-gate 				 * connected, recover the port
38210Sstevel@tonic-gate 				 */
38220Sstevel@tonic-gate 				if (was_connected && (status &
38230Sstevel@tonic-gate 				    PORT_STATUS_CCS)) {
38240Sstevel@tonic-gate 					online_child |=
38250Sstevel@tonic-gate 					    (hubd_recover_disabled_port(hubd,
38260Sstevel@tonic-gate 					    port) == USB_SUCCESS);
38270Sstevel@tonic-gate 				}
38280Sstevel@tonic-gate 			}
38290Sstevel@tonic-gate 
38300Sstevel@tonic-gate 			/*
38310Sstevel@tonic-gate 			 * Now check what changed on the port
38320Sstevel@tonic-gate 			 */
38332651Ssl147100 			if ((change & PORT_CHANGE_CSC) || attach_flg) {
38340Sstevel@tonic-gate 				if ((status & PORT_STATUS_CCS) &&
38350Sstevel@tonic-gate 				    (!was_connected)) {
38360Sstevel@tonic-gate 					/* new device plugged in */
38370Sstevel@tonic-gate 					online_child |=
38380Sstevel@tonic-gate 					    (hubd_handle_port_connect(hubd,
38390Sstevel@tonic-gate 					    port) == USB_SUCCESS);
38400Sstevel@tonic-gate 
38410Sstevel@tonic-gate 				} else if ((status & PORT_STATUS_CCS) &&
38420Sstevel@tonic-gate 				    was_connected) {
38430Sstevel@tonic-gate 					/*
38440Sstevel@tonic-gate 					 * In this case we can never be sure
38450Sstevel@tonic-gate 					 * if the device indeed got hotplugged
38460Sstevel@tonic-gate 					 * or the hub is falsely reporting the
38470Sstevel@tonic-gate 					 * change.
38484763Slg150142 					 */
38494763Slg150142 					child_dip = hubd->h_children_dips[port];
38504763Slg150142 
38514763Slg150142 					mutex_exit(HUBD_MUTEX(hubd));
38524763Slg150142 					/*
38534763Slg150142 					 * this ensures we do not race with
38544763Slg150142 					 * other threads which are detaching
38554763Slg150142 					 * the child driver at the same time.
38564763Slg150142 					 */
38574763Slg150142 					ndi_devi_enter(child_dip, &chld_circ);
38584763Slg150142 					/*
38594763Slg150142 					 * Now check if the driver remains
38604763Slg150142 					 * attached.
38610Sstevel@tonic-gate 					 */
38624763Slg150142 					if (i_ddi_devi_attached(child_dip)) {
38634763Slg150142 						/*
38644763Slg150142 						 * first post a disconnect event
38654763Slg150142 						 * to the child.
38664763Slg150142 						 */
38674763Slg150142 						hubd_post_event(hubd, port,
38684763Slg150142 						    USBA_EVENT_TAG_HOT_REMOVAL);
38694763Slg150142 						mutex_enter(HUBD_MUTEX(hubd));
38704763Slg150142 
38714763Slg150142 						/*
38724763Slg150142 						 * then reset the port and
38734763Slg150142 						 * recover the device
38744763Slg150142 						 */
38754763Slg150142 						online_child |=
38764763Slg150142 						    (hubd_handle_port_connect(
38774763Slg150142 						    hubd, port) == USB_SUCCESS);
38784763Slg150142 
38794763Slg150142 						mutex_exit(HUBD_MUTEX(hubd));
38804763Slg150142 					}
38814763Slg150142 
38824763Slg150142 					ndi_devi_exit(child_dip, chld_circ);
38830Sstevel@tonic-gate 					mutex_enter(HUBD_MUTEX(hubd));
38840Sstevel@tonic-gate 				} else if (was_connected) {
38850Sstevel@tonic-gate 					/* this is a disconnect */
38860Sstevel@tonic-gate 					mutex_exit(HUBD_MUTEX(hubd));
38870Sstevel@tonic-gate 					hubd_post_event(hubd, port,
38880Sstevel@tonic-gate 					    USBA_EVENT_TAG_HOT_REMOVAL);
38890Sstevel@tonic-gate 					mutex_enter(HUBD_MUTEX(hubd));
38900Sstevel@tonic-gate 
38910Sstevel@tonic-gate 					offline_child = B_TRUE;
38920Sstevel@tonic-gate 				}
38930Sstevel@tonic-gate 			}
38940Sstevel@tonic-gate 
38950Sstevel@tonic-gate 			/*
38960Sstevel@tonic-gate 			 * Check if any port is coming out of suspend
38970Sstevel@tonic-gate 			 */
38980Sstevel@tonic-gate 			if (change & PORT_CHANGE_PSSC) {
38990Sstevel@tonic-gate 				/* a resuming device could have disconnected */
39000Sstevel@tonic-gate 				if (was_connected &&
39010Sstevel@tonic-gate 				    hubd->h_children_dips[port]) {
39020Sstevel@tonic-gate 
39030Sstevel@tonic-gate 					/* device on this port resuming */
39040Sstevel@tonic-gate 					dev_info_t *dip;
39050Sstevel@tonic-gate 
39060Sstevel@tonic-gate 					dip = hubd->h_children_dips[port];
39070Sstevel@tonic-gate 
39080Sstevel@tonic-gate 					/*
39090Sstevel@tonic-gate 					 * Don't raise power on detaching child
39100Sstevel@tonic-gate 					 */
39110Sstevel@tonic-gate 					if (!DEVI_IS_DETACHING(dip)) {
39120Sstevel@tonic-gate 						/*
39130Sstevel@tonic-gate 						 * As this child is not
39140Sstevel@tonic-gate 						 * detaching, we set this
39150Sstevel@tonic-gate 						 * flag, causing bus_ctls
39160Sstevel@tonic-gate 						 * to stall detach till
39170Sstevel@tonic-gate 						 * pm_raise_power returns
39180Sstevel@tonic-gate 						 * and flag it for a deferred
39190Sstevel@tonic-gate 						 * raise_power.
39200Sstevel@tonic-gate 						 *
39210Sstevel@tonic-gate 						 * pm_raise_power is deferred
39220Sstevel@tonic-gate 						 * because we need to release
39230Sstevel@tonic-gate 						 * the locks first.
39240Sstevel@tonic-gate 						 */
39250Sstevel@tonic-gate 						hubd->h_port_state[port] |=
39264763Slg150142 						    HUBD_CHILD_RAISE_POWER;
39270Sstevel@tonic-gate 						pwrup_child = B_TRUE;
39280Sstevel@tonic-gate 						mutex_exit(HUBD_MUTEX(hubd));
39290Sstevel@tonic-gate 
39300Sstevel@tonic-gate 						/*
39310Sstevel@tonic-gate 						 * make sure that child
39320Sstevel@tonic-gate 						 * doesn't disappear
39330Sstevel@tonic-gate 						 */
39340Sstevel@tonic-gate 						ndi_hold_devi(dip);
39350Sstevel@tonic-gate 
39360Sstevel@tonic-gate 						mutex_enter(HUBD_MUTEX(hubd));
39370Sstevel@tonic-gate 					}
39380Sstevel@tonic-gate 				}
39390Sstevel@tonic-gate 			}
39402326Ssl147100 
39412326Ssl147100 			/*
39422326Ssl147100 			 * Check if the port is over-current
39432326Ssl147100 			 */
39442326Ssl147100 			if (change & PORT_CHANGE_OCIC) {
39452326Ssl147100 				USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG,
39462326Ssl147100 				    hubd->h_log_handle,
39472326Ssl147100 				    "Port%d in over current condition, "
39482326Ssl147100 				    "please check the attached device to "
39492326Ssl147100 				    "clear the condition. The system will "
39502326Ssl147100 				    "try to recover the port, but if not "
39512326Ssl147100 				    "successful, you need to re-connect "
39522326Ssl147100 				    "the hub or reboot the system to bring "
39532326Ssl147100 				    "the port back to work", port);
39542326Ssl147100 
39552326Ssl147100 				if (!(status & PORT_STATUS_PPS)) {
39562326Ssl147100 					/*
39572326Ssl147100 					 * Try to enable port power, but
39582326Ssl147100 					 * possibly fail. Ignore failure
39592326Ssl147100 					 */
39602326Ssl147100 					(void) hubd_enable_port_power(hubd,
39612326Ssl147100 					    port);
39622326Ssl147100 
39632326Ssl147100 					/*
39642326Ssl147100 					 * Delay some time to avoid
39652326Ssl147100 					 * over-current event to happen
39662326Ssl147100 					 * too frequently in some cases
39672326Ssl147100 					 */
39682326Ssl147100 					mutex_exit(HUBD_MUTEX(hubd));
39692326Ssl147100 					delay(drv_usectohz(500000));
39702326Ssl147100 					mutex_enter(HUBD_MUTEX(hubd));
39712326Ssl147100 				}
39722326Ssl147100 			}
39730Sstevel@tonic-gate 		}
39740Sstevel@tonic-gate 	}
39750Sstevel@tonic-gate 
39760Sstevel@tonic-gate 	/* release locks so we can do a devfs_clean */
39770Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
39780Sstevel@tonic-gate 
39790Sstevel@tonic-gate 	/* delete cached dv_node's but drop locks first */
39800Sstevel@tonic-gate 	ndi_devi_exit(hdip, circ);
39810Sstevel@tonic-gate 	ndi_devi_exit(rh_dip, rh_circ);
39820Sstevel@tonic-gate 	ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ);
39830Sstevel@tonic-gate 
39840Sstevel@tonic-gate 	(void) devfs_clean(rh_dip, NULL, 0);
39850Sstevel@tonic-gate 
39860Sstevel@tonic-gate 	/* now check if any children need onlining */
39870Sstevel@tonic-gate 	if (online_child) {
39880Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
39890Sstevel@tonic-gate 		    "hubd_hotplug_thread: onlining children");
39900Sstevel@tonic-gate 
399110783SVincent.Wang@Sun.COM 		(void) ndi_devi_online(hubd->h_dip, 0);
39920Sstevel@tonic-gate 	}
39930Sstevel@tonic-gate 
39940Sstevel@tonic-gate 	/* now check if any disconnected devices need to be cleaned up */
39950Sstevel@tonic-gate 	if (offline_child) {
39960Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
39970Sstevel@tonic-gate 		    "hubd_hotplug_thread: scheduling cleanup");
39980Sstevel@tonic-gate 
39990Sstevel@tonic-gate 		hubd_schedule_cleanup(hubd->h_usba_device->usb_root_hub_dip);
40000Sstevel@tonic-gate 	}
40010Sstevel@tonic-gate 
40020Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
40030Sstevel@tonic-gate 
40040Sstevel@tonic-gate 	/* now raise power on the children that have woken up */
40050Sstevel@tonic-gate 	if (pwrup_child) {
40060Sstevel@tonic-gate 		old_state = hubd->h_dev_state;
40070Sstevel@tonic-gate 		hubd->h_dev_state = USB_DEV_HUB_CHILD_PWRLVL;
40080Sstevel@tonic-gate 		for (port = 1; port <= nports; port++) {
40090Sstevel@tonic-gate 			if (hubd->h_port_state[port] & HUBD_CHILD_RAISE_POWER) {
40100Sstevel@tonic-gate 				dev_info_t *dip = hubd->h_children_dips[port];
40110Sstevel@tonic-gate 
40120Sstevel@tonic-gate 				mutex_exit(HUBD_MUTEX(hubd));
40130Sstevel@tonic-gate 
40140Sstevel@tonic-gate 				/* Get the device to full power */
40150Sstevel@tonic-gate 				(void) pm_busy_component(dip, 0);
40160Sstevel@tonic-gate 				(void) pm_raise_power(dip, 0,
40170Sstevel@tonic-gate 				    USB_DEV_OS_FULL_PWR);
40180Sstevel@tonic-gate 				(void) pm_idle_component(dip, 0);
40190Sstevel@tonic-gate 
40200Sstevel@tonic-gate 				/* release the hold on the child */
40210Sstevel@tonic-gate 				ndi_rele_devi(dip);
40220Sstevel@tonic-gate 				mutex_enter(HUBD_MUTEX(hubd));
40230Sstevel@tonic-gate 				hubd->h_port_state[port] &=
40240Sstevel@tonic-gate 				    ~HUBD_CHILD_RAISE_POWER;
40250Sstevel@tonic-gate 			}
40260Sstevel@tonic-gate 		}
40270Sstevel@tonic-gate 		/*
40280Sstevel@tonic-gate 		 * make sure that we don't accidentally
40290Sstevel@tonic-gate 		 * over write the disconnect state
40300Sstevel@tonic-gate 		 */
40310Sstevel@tonic-gate 		if (hubd->h_dev_state == USB_DEV_HUB_CHILD_PWRLVL) {
40320Sstevel@tonic-gate 			hubd->h_dev_state = old_state;
40330Sstevel@tonic-gate 		}
40340Sstevel@tonic-gate 	}
40350Sstevel@tonic-gate 
40360Sstevel@tonic-gate 	/*
40370Sstevel@tonic-gate 	 * start polling can immediately kick off read callback
40380Sstevel@tonic-gate 	 * we need to set the h_hotplug_thread to 0 so that
40390Sstevel@tonic-gate 	 * the callback is not dropped
40404844Slg150142 	 *
40414844Slg150142 	 * if there is device during reset, still stop polling to avoid the
40424844Slg150142 	 * read callback interrupting the reset, the polling will be started
40434844Slg150142 	 * in hubd_reset_thread.
40440Sstevel@tonic-gate 	 */
40454844Slg150142 	for (port = 1; port <= MAX_PORTS; port++) {
40464844Slg150142 		if (hubd->h_reset_port[port]) {
40474844Slg150142 
40484844Slg150142 			break;
40494844Slg150142 		}
40504844Slg150142 	}
40514844Slg150142 	if (port > MAX_PORTS) {
40524844Slg150142 		hubd_start_polling(hubd, HUBD_ALWAYS_START_POLLING);
40534844Slg150142 	}
40540Sstevel@tonic-gate 
40550Sstevel@tonic-gate 	/*
40560Sstevel@tonic-gate 	 * Earlier we would set the h_hotplug_thread = 0 before
40570Sstevel@tonic-gate 	 * polling was restarted  so that
40580Sstevel@tonic-gate 	 * if there is any root hub status change interrupt, we can still kick
40590Sstevel@tonic-gate 	 * off the hotplug thread. This was valid when this interrupt was
40600Sstevel@tonic-gate 	 * delivered in hardware, and only ONE interrupt would be delivered.
40610Sstevel@tonic-gate 	 * Now that we poll on the root hub looking for status change in
40620Sstevel@tonic-gate 	 * software, this assignment is no longer required.
40630Sstevel@tonic-gate 	 */
40640Sstevel@tonic-gate 	hubd->h_hotplug_thread--;
40650Sstevel@tonic-gate 
40660Sstevel@tonic-gate 	/* mark this device as idle */
40670Sstevel@tonic-gate 	(void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
40680Sstevel@tonic-gate 
40694844Slg150142 	cv_broadcast(&hubd->h_cv_hotplug_dev);
40704844Slg150142 
40710Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
40720Sstevel@tonic-gate 	    "hubd_hotplug_thread: exit");
40730Sstevel@tonic-gate 
40740Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
40750Sstevel@tonic-gate 
40760Sstevel@tonic-gate 	ndi_rele_devi(hdip);
40770Sstevel@tonic-gate }
40780Sstevel@tonic-gate 
40790Sstevel@tonic-gate 
40800Sstevel@tonic-gate /*
40810Sstevel@tonic-gate  * hubd_handle_port_connect:
40820Sstevel@tonic-gate  *	Transition a port from Disabled to Enabled.  Ensure that the
40830Sstevel@tonic-gate  *	port is in the correct state before attempting to
40840Sstevel@tonic-gate  *	access the device.
40850Sstevel@tonic-gate  */
40860Sstevel@tonic-gate static int
hubd_handle_port_connect(hubd_t * hubd,usb_port_t port)40870Sstevel@tonic-gate hubd_handle_port_connect(hubd_t *hubd, usb_port_t port)
40880Sstevel@tonic-gate {
40890Sstevel@tonic-gate 	int			rval;
40900Sstevel@tonic-gate 	int			retry;
40910Sstevel@tonic-gate 	long			time_delay;
40920Sstevel@tonic-gate 	long			settling_time;
40930Sstevel@tonic-gate 	uint16_t		status;
40940Sstevel@tonic-gate 	uint16_t		change;
40950Sstevel@tonic-gate 	usb_addr_t		hubd_usb_addr;
40960Sstevel@tonic-gate 	usba_device_t		*usba_device;
40970Sstevel@tonic-gate 	usb_port_status_t	port_status = 0;
40980Sstevel@tonic-gate 	usb_port_status_t	hub_port_status = 0;
40990Sstevel@tonic-gate 
41000Sstevel@tonic-gate 	/* Get the hub address and port status */
41010Sstevel@tonic-gate 	usba_device = hubd->h_usba_device;
41020Sstevel@tonic-gate 	mutex_enter(&usba_device->usb_mutex);
41030Sstevel@tonic-gate 	hubd_usb_addr = usba_device->usb_addr;
41040Sstevel@tonic-gate 	hub_port_status = usba_device->usb_port_status;
41050Sstevel@tonic-gate 	mutex_exit(&usba_device->usb_mutex);
41060Sstevel@tonic-gate 
41070Sstevel@tonic-gate 	/*
41080Sstevel@tonic-gate 	 * If a device is connected, transition the
41090Sstevel@tonic-gate 	 * port from Disabled to the Enabled state.
41100Sstevel@tonic-gate 	 * The device will receive downstream packets
41110Sstevel@tonic-gate 	 * in the Enabled state.
41120Sstevel@tonic-gate 	 *
41130Sstevel@tonic-gate 	 * reset port and wait for the hub to report
41140Sstevel@tonic-gate 	 * completion
41150Sstevel@tonic-gate 	 */
41160Sstevel@tonic-gate 	change = status = 0;
41170Sstevel@tonic-gate 
41180Sstevel@tonic-gate 	/*
41190Sstevel@tonic-gate 	 * According to section 9.1.2 of USB 2.0 spec, the host should
41200Sstevel@tonic-gate 	 * wait for atleast 100ms to allow completion of an insertion
41210Sstevel@tonic-gate 	 * process and for power at the device to become stable.
41220Sstevel@tonic-gate 	 * We wait for 200 ms
41230Sstevel@tonic-gate 	 */
41240Sstevel@tonic-gate 	settling_time = drv_usectohz(hubd_device_delay / 5);
41250Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
41260Sstevel@tonic-gate 	delay(settling_time);
41270Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
41280Sstevel@tonic-gate 
41290Sstevel@tonic-gate 	/* calculate 600 ms delay time */
41300Sstevel@tonic-gate 	time_delay = (6 * drv_usectohz(hubd_device_delay)) / 10;
41310Sstevel@tonic-gate 
41320Sstevel@tonic-gate 	for (retry = 0; (hubd->h_dev_state == USB_DEV_ONLINE) &&
41330Sstevel@tonic-gate 	    (retry < hubd_retry_enumerate); retry++) {
41340Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
41350Sstevel@tonic-gate 		    "resetting port%d, retry=%d", port, retry);
41360Sstevel@tonic-gate 
41370Sstevel@tonic-gate 		if ((rval = hubd_reset_port(hubd, port)) != USB_SUCCESS) {
41380Sstevel@tonic-gate 			(void) hubd_determine_port_status(hubd,
41390Sstevel@tonic-gate 			    port, &status, &change, 0);
41400Sstevel@tonic-gate 
41410Sstevel@tonic-gate 			/* continue only if port is still connected */
41420Sstevel@tonic-gate 			if (status & PORT_STATUS_CCS) {
41430Sstevel@tonic-gate 				continue;
41440Sstevel@tonic-gate 			}
41450Sstevel@tonic-gate 
41460Sstevel@tonic-gate 			/* carry on regardless */
41470Sstevel@tonic-gate 		}
41480Sstevel@tonic-gate 
41490Sstevel@tonic-gate 		/*
41500Sstevel@tonic-gate 		 * according to USB 2.0 spec section 11.24.2.7.1.2
41510Sstevel@tonic-gate 		 * at the end of port reset, the hub enables the port.
41520Sstevel@tonic-gate 		 * But for some strange reasons, uhci port remains disabled.
41530Sstevel@tonic-gate 		 * And because the port remains disabled for the settling
41540Sstevel@tonic-gate 		 * time below, the device connected to the port gets wedged
41550Sstevel@tonic-gate 		 * - fails to enumerate (device not responding)
41560Sstevel@tonic-gate 		 * Hence, we enable it here immediately and later again after
41570Sstevel@tonic-gate 		 * the delay
41580Sstevel@tonic-gate 		 */
41590Sstevel@tonic-gate 		(void) hubd_enable_port(hubd, port);
41600Sstevel@tonic-gate 
41610Sstevel@tonic-gate 		/* we skip this delay in the first iteration */
41620Sstevel@tonic-gate 		if (retry) {
41630Sstevel@tonic-gate 			/*
41640Sstevel@tonic-gate 			 * delay for device to signal disconnect/connect so
41650Sstevel@tonic-gate 			 * that hub properly recognizes the speed of the device
41660Sstevel@tonic-gate 			 */
41670Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
41680Sstevel@tonic-gate 			delay(settling_time);
41690Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
41700Sstevel@tonic-gate 
41710Sstevel@tonic-gate 			/*
41720Sstevel@tonic-gate 			 * When a low speed device is connected to any port of
41730Sstevel@tonic-gate 			 * PPX it has to be explicitly enabled
41740Sstevel@tonic-gate 			 * Also, if device intentionally signals
41750Sstevel@tonic-gate 			 * disconnect/connect, it will disable the port.
41760Sstevel@tonic-gate 			 * So enable it again.
41770Sstevel@tonic-gate 			 */
41780Sstevel@tonic-gate 			(void) hubd_enable_port(hubd, port);
41790Sstevel@tonic-gate 		}
41800Sstevel@tonic-gate 
41810Sstevel@tonic-gate 		if ((rval = hubd_determine_port_status(hubd, port, &status,
41820Sstevel@tonic-gate 		    &change, 0)) != USB_SUCCESS) {
41830Sstevel@tonic-gate 
4184978Sfrits 			USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
41850Sstevel@tonic-gate 			    "getting status failed (%d)", rval);
41860Sstevel@tonic-gate 
41870Sstevel@tonic-gate 			(void) hubd_disable_port(hubd, port);
41880Sstevel@tonic-gate 
41890Sstevel@tonic-gate 			continue;
41900Sstevel@tonic-gate 		}
41910Sstevel@tonic-gate 
41920Sstevel@tonic-gate 		if (status & PORT_STATUS_POCI) {
4193978Sfrits 			USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
41940Sstevel@tonic-gate 			    "port %d overcurrent", port);
41950Sstevel@tonic-gate 
41960Sstevel@tonic-gate 			(void) hubd_disable_port(hubd, port);
41970Sstevel@tonic-gate 
41980Sstevel@tonic-gate 			/* ack changes */
41990Sstevel@tonic-gate 			(void) hubd_determine_port_status(hubd,
42000Sstevel@tonic-gate 			    port, &status, &change, PORT_CHANGE_OCIC);
42010Sstevel@tonic-gate 
42020Sstevel@tonic-gate 			continue;
42030Sstevel@tonic-gate 		}
42040Sstevel@tonic-gate 
42050Sstevel@tonic-gate 		/* is status really OK? */
42060Sstevel@tonic-gate 		if ((status & PORT_STATUS_OK) != PORT_STATUS_OK) {
42070Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
42080Sstevel@tonic-gate 			    "port %d status (0x%x) not OK on retry %d",
42090Sstevel@tonic-gate 			    port, status, retry);
42100Sstevel@tonic-gate 
42110Sstevel@tonic-gate 			/* check if we still have the connection */
42120Sstevel@tonic-gate 			if (!(status & PORT_STATUS_CCS)) {
42130Sstevel@tonic-gate 				/* lost connection, set exit condition */
42140Sstevel@tonic-gate 				retry = hubd_retry_enumerate;
42150Sstevel@tonic-gate 
42160Sstevel@tonic-gate 				break;
42170Sstevel@tonic-gate 			}
42180Sstevel@tonic-gate 		} else {
42190Sstevel@tonic-gate 			/*
42200Sstevel@tonic-gate 			 * Determine if the device is high or full
42210Sstevel@tonic-gate 			 * or low speed.
42220Sstevel@tonic-gate 			 */
42230Sstevel@tonic-gate 			if (status & PORT_STATUS_LSDA) {
42240Sstevel@tonic-gate 				port_status = USBA_LOW_SPEED_DEV;
42250Sstevel@tonic-gate 			} else if (status & PORT_STATUS_HSDA) {
42260Sstevel@tonic-gate 				port_status = USBA_HIGH_SPEED_DEV;
42270Sstevel@tonic-gate 			} else {
42280Sstevel@tonic-gate 				port_status = USBA_FULL_SPEED_DEV;
42290Sstevel@tonic-gate 			}
42300Sstevel@tonic-gate 
42310Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
42320Sstevel@tonic-gate 			    "creating child port%d, status=0x%x "
42330Sstevel@tonic-gate 			    "port status=0x%x",
42340Sstevel@tonic-gate 			    port, status, port_status);
42350Sstevel@tonic-gate 
42360Sstevel@tonic-gate 			/*
42370Sstevel@tonic-gate 			 * if the child already exists, set addrs and config
42380Sstevel@tonic-gate 			 * to the device post connect event to the child
42390Sstevel@tonic-gate 			 */
42400Sstevel@tonic-gate 			if (hubd->h_children_dips[port]) {
42410Sstevel@tonic-gate 				/* set addrs to this device */
42420Sstevel@tonic-gate 				rval = hubd_setdevaddr(hubd, port);
42430Sstevel@tonic-gate 
42440Sstevel@tonic-gate 				/*
42450Sstevel@tonic-gate 				 * This delay is important for the CATC hub
42460Sstevel@tonic-gate 				 * to enumerate. But, avoid delay in the first
42470Sstevel@tonic-gate 				 * iteration
42480Sstevel@tonic-gate 				 */
42490Sstevel@tonic-gate 				if (retry) {
42500Sstevel@tonic-gate 					mutex_exit(HUBD_MUTEX(hubd));
42510Sstevel@tonic-gate 					delay(drv_usectohz(
42520Sstevel@tonic-gate 					    hubd_device_delay/100));
42530Sstevel@tonic-gate 					mutex_enter(HUBD_MUTEX(hubd));
42540Sstevel@tonic-gate 				}
42550Sstevel@tonic-gate 
42560Sstevel@tonic-gate 				if (rval == USB_SUCCESS) {
42570Sstevel@tonic-gate 					/*
42584844Slg150142 					 * if the port is resetting, check if
42594844Slg150142 					 * device's descriptors have changed.
42604844Slg150142 					 */
42614844Slg150142 					if ((hubd->h_reset_port[port]) &&
42624844Slg150142 					    (hubd_check_same_device(hubd,
42634844Slg150142 					    port) != USB_SUCCESS)) {
42644844Slg150142 						retry = hubd_retry_enumerate;
42654844Slg150142 
42664844Slg150142 						break;
42674844Slg150142 					}
42684844Slg150142 
42694844Slg150142 					/*
42700Sstevel@tonic-gate 					 * set the default config for
42710Sstevel@tonic-gate 					 * this device
42720Sstevel@tonic-gate 					 */
42730Sstevel@tonic-gate 					hubd_setdevconfig(hubd, port);
42740Sstevel@tonic-gate 
42750Sstevel@tonic-gate 					/*
42764844Slg150142 					 * if we are doing Default reset, do
42774844Slg150142 					 * not post reconnect event since we
42784844Slg150142 					 * don't know where reset function is
42794844Slg150142 					 * called.
42804844Slg150142 					 */
42814844Slg150142 					if (hubd->h_reset_port[port]) {
42824844Slg150142 
42834844Slg150142 						return (USB_SUCCESS);
42844844Slg150142 					}
42854844Slg150142 
42864844Slg150142 					/*
42870Sstevel@tonic-gate 					 * indicate to the child that
42880Sstevel@tonic-gate 					 * it is online again
42890Sstevel@tonic-gate 					 */
42900Sstevel@tonic-gate 					mutex_exit(HUBD_MUTEX(hubd));
42910Sstevel@tonic-gate 					hubd_post_event(hubd, port,
42920Sstevel@tonic-gate 					    USBA_EVENT_TAG_HOT_INSERTION);
42930Sstevel@tonic-gate 					mutex_enter(HUBD_MUTEX(hubd));
42940Sstevel@tonic-gate 
42950Sstevel@tonic-gate 					return (USB_SUCCESS);
42960Sstevel@tonic-gate 				}
42970Sstevel@tonic-gate 			} else {
42980Sstevel@tonic-gate 				/*
42990Sstevel@tonic-gate 				 * We need to release access here
43000Sstevel@tonic-gate 				 * so that busctls on other ports can
43010Sstevel@tonic-gate 				 * continue and don't cause a deadlock
43020Sstevel@tonic-gate 				 * when busctl and removal of prom node
43030Sstevel@tonic-gate 				 * takes concurrently. This also ensures
43040Sstevel@tonic-gate 				 * busctls for attach of successfully
43050Sstevel@tonic-gate 				 * enumerated devices on other ports can
43060Sstevel@tonic-gate 				 * continue concurrently with the process
43070Sstevel@tonic-gate 				 * of enumerating the new devices. This
43080Sstevel@tonic-gate 				 * reduces the overall boot time of the system.
43090Sstevel@tonic-gate 				 */
43100Sstevel@tonic-gate 				rval = hubd_create_child(hubd->h_dip,
43114763Slg150142 				    hubd,
43124763Slg150142 				    hubd->h_usba_device,
43134763Slg150142 				    port_status, port,
43144763Slg150142 				    retry);
43150Sstevel@tonic-gate 				if (rval == USB_SUCCESS) {
43160Sstevel@tonic-gate 					usba_update_hotplug_stats(hubd->h_dip,
43170Sstevel@tonic-gate 					    USBA_TOTAL_HOTPLUG_SUCCESS|
43180Sstevel@tonic-gate 					    USBA_HOTPLUG_SUCCESS);
43190Sstevel@tonic-gate 					hubd->h_total_hotplug_success++;
43200Sstevel@tonic-gate 
43210Sstevel@tonic-gate 					if (retry > 0) {
4322978Sfrits 						USB_DPRINTF_L2(
43230Sstevel@tonic-gate 						    DPRINT_MASK_HOTPLUG,
43240Sstevel@tonic-gate 						    hubd->h_log_handle,
43250Sstevel@tonic-gate 						    "device on port %d "
43260Sstevel@tonic-gate 						    "enumerated after %d %s",
43270Sstevel@tonic-gate 						    port, retry,
43280Sstevel@tonic-gate 						    (retry > 1) ? "retries" :
43290Sstevel@tonic-gate 						    "retry");
43300Sstevel@tonic-gate 
43310Sstevel@tonic-gate 					}
43320Sstevel@tonic-gate 
43330Sstevel@tonic-gate 					return (USB_SUCCESS);
43340Sstevel@tonic-gate 				}
43350Sstevel@tonic-gate 			}
43360Sstevel@tonic-gate 		}
43370Sstevel@tonic-gate 
43380Sstevel@tonic-gate 		/* wait a while until it settles? */
43390Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
43400Sstevel@tonic-gate 		    "disabling port %d again", port);
43410Sstevel@tonic-gate 
43420Sstevel@tonic-gate 		(void) hubd_disable_port(hubd, port);
43430Sstevel@tonic-gate 		if (retry) {
43440Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
43450Sstevel@tonic-gate 			delay(time_delay);
43460Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
43470Sstevel@tonic-gate 		}
43480Sstevel@tonic-gate 
43490Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
43500Sstevel@tonic-gate 		    "retrying on port %d", port);
43510Sstevel@tonic-gate 	}
43520Sstevel@tonic-gate 
43530Sstevel@tonic-gate 	if (retry >= hubd_retry_enumerate) {
43540Sstevel@tonic-gate 		/*
43550Sstevel@tonic-gate 		 * If it is a High Speed Root Hub and connected device
43560Sstevel@tonic-gate 		 * Is a Low/Full Speed, it will be handled by USB 1.1
43570Sstevel@tonic-gate 		 * Host Controller. In this case, USB 2.0 Host Controller
43580Sstevel@tonic-gate 		 * will transfer the ownership of this port to USB 1.1
43590Sstevel@tonic-gate 		 * Host Controller. So don't display any error message on
43600Sstevel@tonic-gate 		 * the console.
43610Sstevel@tonic-gate 		 */
43620Sstevel@tonic-gate 		if ((hubd_usb_addr == ROOT_HUB_ADDR) &&
43630Sstevel@tonic-gate 		    (hub_port_status == USBA_HIGH_SPEED_DEV) &&
43640Sstevel@tonic-gate 		    (port_status != USBA_HIGH_SPEED_DEV)) {
43650Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
43660Sstevel@tonic-gate 			    hubd->h_log_handle,
43670Sstevel@tonic-gate 			    "hubd_handle_port_connect: Low/Full speed "
43680Sstevel@tonic-gate 			    "device is connected to High Speed root hub");
43690Sstevel@tonic-gate 		} else {
43700Sstevel@tonic-gate 			USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG,
43710Sstevel@tonic-gate 			    hubd->h_log_handle,
43720Sstevel@tonic-gate 			    "Connecting device on port %d failed", port);
43730Sstevel@tonic-gate 		}
43740Sstevel@tonic-gate 
43750Sstevel@tonic-gate 		(void) hubd_disable_port(hubd, port);
43760Sstevel@tonic-gate 		usba_update_hotplug_stats(hubd->h_dip,
43770Sstevel@tonic-gate 		    USBA_TOTAL_HOTPLUG_FAILURE|USBA_HOTPLUG_FAILURE);
43780Sstevel@tonic-gate 		hubd->h_total_hotplug_failure++;
43790Sstevel@tonic-gate 
43800Sstevel@tonic-gate 		/*
43810Sstevel@tonic-gate 		 * the port should be automagically
43820Sstevel@tonic-gate 		 * disabled but just in case, we do
43830Sstevel@tonic-gate 		 * it here
43840Sstevel@tonic-gate 		 */
43850Sstevel@tonic-gate 		(void) hubd_disable_port(hubd, port);
43860Sstevel@tonic-gate 
43870Sstevel@tonic-gate 		/* ack all changes because we disabled this port */
43880Sstevel@tonic-gate 		(void) hubd_determine_port_status(hubd,
43890Sstevel@tonic-gate 		    port, &status, &change, HUBD_ACK_ALL_CHANGES);
43900Sstevel@tonic-gate 
43910Sstevel@tonic-gate 	}
43920Sstevel@tonic-gate 
43930Sstevel@tonic-gate 	return (USB_FAILURE);
43940Sstevel@tonic-gate }
43950Sstevel@tonic-gate 
43960Sstevel@tonic-gate 
43970Sstevel@tonic-gate /*
43980Sstevel@tonic-gate  * hubd_get_hub_status:
43990Sstevel@tonic-gate  */
44000Sstevel@tonic-gate static int
hubd_get_hub_status(hubd_t * hubd)44010Sstevel@tonic-gate hubd_get_hub_status(hubd_t *hubd)
44020Sstevel@tonic-gate {
44030Sstevel@tonic-gate 	int		rval;
44040Sstevel@tonic-gate 	usb_cr_t	completion_reason;
44050Sstevel@tonic-gate 	usb_cb_flags_t	cb_flags;
44061001Ssl147100 	uint16_t	stword[2];
44070Sstevel@tonic-gate 	uint16_t	status;
44080Sstevel@tonic-gate 	uint16_t	change;
44090Sstevel@tonic-gate 	usb_cfg_descr_t	cfg_descr;
44100Sstevel@tonic-gate 	size_t		cfg_length;
44110Sstevel@tonic-gate 	uchar_t		*usb_cfg;
44120Sstevel@tonic-gate 	uint8_t		MaxPower;
44132326Ssl147100 	usb_hub_descr_t	*hub_descr;
44142326Ssl147100 	usb_port_t	port;
44152326Ssl147100 
44162326Ssl147100 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
44171001Ssl147100 	    "hubd_get_hub_status:");
44181001Ssl147100 
44191001Ssl147100 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
44201001Ssl147100 
44211001Ssl147100 	if ((hubd_get_hub_status_words(hubd, stword)) != USB_SUCCESS) {
44220Sstevel@tonic-gate 
44230Sstevel@tonic-gate 		return (USB_FAILURE);
44240Sstevel@tonic-gate 	}
44251001Ssl147100 	status = stword[0];
44261001Ssl147100 	change = stword[1];
44270Sstevel@tonic-gate 
44280Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
44290Sstevel@tonic-gate 
44300Sstevel@tonic-gate 	/* Obtain the raw configuration descriptor */
44310Sstevel@tonic-gate 	usb_cfg = usb_get_raw_cfg_data(hubd->h_dip, &cfg_length);
44320Sstevel@tonic-gate 
44330Sstevel@tonic-gate 	/* get configuration descriptor */
44340Sstevel@tonic-gate 	rval = usb_parse_cfg_descr(usb_cfg, cfg_length,
44354763Slg150142 	    &cfg_descr, USB_CFG_DESCR_SIZE);
44360Sstevel@tonic-gate 
44370Sstevel@tonic-gate 	if (rval != USB_CFG_DESCR_SIZE) {
44380Sstevel@tonic-gate 
44392326Ssl147100 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
44400Sstevel@tonic-gate 		    "get hub configuration descriptor failed.");
44410Sstevel@tonic-gate 
44420Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
44430Sstevel@tonic-gate 
44440Sstevel@tonic-gate 		return (USB_FAILURE);
44450Sstevel@tonic-gate 	} else {
44460Sstevel@tonic-gate 		MaxPower = cfg_descr.bMaxPower;
44470Sstevel@tonic-gate 	}
44480Sstevel@tonic-gate 
44490Sstevel@tonic-gate 	/* check if local power status changed. */
44500Sstevel@tonic-gate 	if (change & C_HUB_LOCAL_POWER_STATUS) {
44510Sstevel@tonic-gate 
44520Sstevel@tonic-gate 		/*
44530Sstevel@tonic-gate 		 * local power has been lost, check the maximum
44540Sstevel@tonic-gate 		 * power consumption of current configuration.
44550Sstevel@tonic-gate 		 * see USB2.0 spec Table 11-12.
44560Sstevel@tonic-gate 		 */
44570Sstevel@tonic-gate 		if (status & HUB_LOCAL_POWER_STATUS) {
44580Sstevel@tonic-gate 
44590Sstevel@tonic-gate 			if (MaxPower == 0) {
44600Sstevel@tonic-gate 
44610Sstevel@tonic-gate 				/*
44620Sstevel@tonic-gate 				 * Self-powered only hub. Because it could
44630Sstevel@tonic-gate 				 * not draw any power from USB bus.
44640Sstevel@tonic-gate 				 * It can't work well on this condition.
44650Sstevel@tonic-gate 				 */
44662326Ssl147100 				USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG,
44670Sstevel@tonic-gate 				    hubd->h_log_handle,
44680Sstevel@tonic-gate 				    "local power has been lost, "
44690Sstevel@tonic-gate 				    "please disconnect hub");
44700Sstevel@tonic-gate 			} else {
44710Sstevel@tonic-gate 
44720Sstevel@tonic-gate 				/*
44730Sstevel@tonic-gate 				 * Bus-powered only or self/bus-powered hub.
44740Sstevel@tonic-gate 				 */
44752326Ssl147100 				USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG,
44760Sstevel@tonic-gate 				    hubd->h_log_handle,
44770Sstevel@tonic-gate 				    "local power has been lost,"
44780Sstevel@tonic-gate 				    "the hub could draw %d"
44790Sstevel@tonic-gate 				    " mA power from the USB bus.",
44800Sstevel@tonic-gate 				    2*MaxPower);
44810Sstevel@tonic-gate 			}
44820Sstevel@tonic-gate 
44830Sstevel@tonic-gate 		}
44840Sstevel@tonic-gate 
44852326Ssl147100 		USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
44860Sstevel@tonic-gate 		    "clearing feature C_HUB_LOCAL_POWER ");
44870Sstevel@tonic-gate 
44880Sstevel@tonic-gate 		if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
44890Sstevel@tonic-gate 		    hubd->h_default_pipe,
44902326Ssl147100 		    HUB_HANDLE_HUB_FEATURE_TYPE,
44910Sstevel@tonic-gate 		    USB_REQ_CLEAR_FEATURE,
44920Sstevel@tonic-gate 		    CFS_C_HUB_LOCAL_POWER,
44930Sstevel@tonic-gate 		    0,
44940Sstevel@tonic-gate 		    0,
44950Sstevel@tonic-gate 		    NULL, 0,
44960Sstevel@tonic-gate 		    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
44972326Ssl147100 			USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
44980Sstevel@tonic-gate 			    hubd->h_log_handle,
44990Sstevel@tonic-gate 			    "clear feature C_HUB_LOCAL_POWER "
45000Sstevel@tonic-gate 			    "failed (%d 0x%x %d)",
45010Sstevel@tonic-gate 			    rval, completion_reason, cb_flags);
45020Sstevel@tonic-gate 		}
45030Sstevel@tonic-gate 
45040Sstevel@tonic-gate 	}
45050Sstevel@tonic-gate 
45060Sstevel@tonic-gate 	if (change & C_HUB_OVER_CURRENT) {
45070Sstevel@tonic-gate 
45080Sstevel@tonic-gate 		if (status & HUB_OVER_CURRENT) {
45092326Ssl147100 
45102326Ssl147100 			if (usba_is_root_hub(hubd->h_dip)) {
45112326Ssl147100 				/*
45122326Ssl147100 				 * The root hub should be automatically
45132326Ssl147100 				 * recovered when over-current condition is
45142326Ssl147100 				 * cleared. But there might be exception and
45152326Ssl147100 				 * need user interaction to recover.
45162326Ssl147100 				 */
45172326Ssl147100 				USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG,
45182326Ssl147100 				    hubd->h_log_handle,
45192326Ssl147100 				    "Root hub over current condition, "
45202326Ssl147100 				    "please check your system to clear the "
45212326Ssl147100 				    "condition as soon as possible. And you "
45222326Ssl147100 				    "may need to reboot the system to bring "
45232326Ssl147100 				    "the root hub back to work if it cannot "
45242326Ssl147100 				    "recover automatically");
45252326Ssl147100 			} else {
45262326Ssl147100 				/*
45272326Ssl147100 				 * The driver would try to recover port power
45282326Ssl147100 				 * on over current condition. When the recovery
45292326Ssl147100 				 * fails, the user may still need to offline
45302326Ssl147100 				 * this hub in order to recover.
45312326Ssl147100 				 * The port power is automatically disabled,
45322326Ssl147100 				 * so we won't see disconnects.
45332326Ssl147100 				 */
45342326Ssl147100 				USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG,
45352326Ssl147100 				    hubd->h_log_handle,
45362326Ssl147100 				    "Hub global over current condition, "
45372326Ssl147100 				    "please disconnect the devices connected "
45382326Ssl147100 				    "to the hub to clear the condition. And "
45392326Ssl147100 				    "you may need to re-connect the hub if "
45402326Ssl147100 				    "the ports do not work");
45412326Ssl147100 			}
45422326Ssl147100 		}
45432326Ssl147100 
45442326Ssl147100 		USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
45450Sstevel@tonic-gate 		    "clearing feature C_HUB_OVER_CURRENT");
45460Sstevel@tonic-gate 
45470Sstevel@tonic-gate 		if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
45480Sstevel@tonic-gate 		    hubd->h_default_pipe,
45492326Ssl147100 		    HUB_HANDLE_HUB_FEATURE_TYPE,
45500Sstevel@tonic-gate 		    USB_REQ_CLEAR_FEATURE,
45510Sstevel@tonic-gate 		    CFS_C_HUB_OVER_CURRENT,
45520Sstevel@tonic-gate 		    0,
45530Sstevel@tonic-gate 		    0,
45540Sstevel@tonic-gate 		    NULL, 0,
45550Sstevel@tonic-gate 		    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
45562326Ssl147100 			USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
45570Sstevel@tonic-gate 			    hubd->h_log_handle,
45580Sstevel@tonic-gate 			    "clear feature C_HUB_OVER_CURRENT "
45590Sstevel@tonic-gate 			    "failed (%d 0x%x %d)",
45600Sstevel@tonic-gate 			    rval, completion_reason, cb_flags);
45610Sstevel@tonic-gate 		}
45622326Ssl147100 
45632326Ssl147100 		/*
45642326Ssl147100 		 * Try to recover all port power if they are turned off.
45652326Ssl147100 		 * Don't do this for root hub, but rely on the root hub
45662326Ssl147100 		 * to recover itself.
45672326Ssl147100 		 */
45682326Ssl147100 		if (!usba_is_root_hub(hubd->h_dip)) {
45692326Ssl147100 
45702326Ssl147100 			mutex_enter(HUBD_MUTEX(hubd));
45712326Ssl147100 
45722326Ssl147100 			/*
45732326Ssl147100 			 * Only check the power status of the 1st port
45742326Ssl147100 			 * since all port power status should be the same.
45752326Ssl147100 			 */
45762326Ssl147100 			(void) hubd_determine_port_status(hubd, 1, &status,
45772326Ssl147100 			    &change, 0);
45782326Ssl147100 
45792326Ssl147100 			if (status & PORT_STATUS_PPS) {
45802326Ssl147100 
45812326Ssl147100 				return (USB_SUCCESS);
45822326Ssl147100 			}
45832326Ssl147100 
45842326Ssl147100 			hub_descr = &hubd->h_hub_descr;
45852326Ssl147100 
45862326Ssl147100 			for (port = 1; port <= hub_descr->bNbrPorts;
45872326Ssl147100 			    port++) {
45882326Ssl147100 
45892326Ssl147100 				(void) hubd_enable_port_power(hubd, port);
45902326Ssl147100 			}
45912326Ssl147100 
45922326Ssl147100 			mutex_exit(HUBD_MUTEX(hubd));
45932326Ssl147100 
45942326Ssl147100 			/*
45952326Ssl147100 			 * Delay some time to avoid over-current event
45962326Ssl147100 			 * to happen too frequently in some cases
45972326Ssl147100 			 */
45982326Ssl147100 			delay(drv_usectohz(500000));
45992326Ssl147100 		}
46000Sstevel@tonic-gate 	}
46010Sstevel@tonic-gate 
46020Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
46030Sstevel@tonic-gate 
46040Sstevel@tonic-gate 	return (USB_SUCCESS);
46050Sstevel@tonic-gate }
46060Sstevel@tonic-gate 
46070Sstevel@tonic-gate 
46080Sstevel@tonic-gate /*
46090Sstevel@tonic-gate  * hubd_reset_port:
46100Sstevel@tonic-gate  */
46110Sstevel@tonic-gate static int
hubd_reset_port(hubd_t * hubd,usb_port_t port)46120Sstevel@tonic-gate hubd_reset_port(hubd_t *hubd, usb_port_t port)
46130Sstevel@tonic-gate {
46140Sstevel@tonic-gate 	int	rval;
46150Sstevel@tonic-gate 	usb_cr_t completion_reason;
46160Sstevel@tonic-gate 	usb_cb_flags_t cb_flags;
46170Sstevel@tonic-gate 	usb_port_mask_t port_mask = 1 << port;
46180Sstevel@tonic-gate 	mblk_t	*data;
46190Sstevel@tonic-gate 	uint16_t status;
46200Sstevel@tonic-gate 	uint16_t change;
46210Sstevel@tonic-gate 	int	i;
462211066Srafael.vanoni@sun.com 	clock_t	delta;
46230Sstevel@tonic-gate 
46240Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
46250Sstevel@tonic-gate 	    "hubd_reset_port: port=%d", port);
46260Sstevel@tonic-gate 
46270Sstevel@tonic-gate 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
46280Sstevel@tonic-gate 
46290Sstevel@tonic-gate 	hubd->h_port_reset_wait |= port_mask;
46300Sstevel@tonic-gate 
46310Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
46320Sstevel@tonic-gate 
46330Sstevel@tonic-gate 	if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
46340Sstevel@tonic-gate 	    hubd->h_default_pipe,
46351001Ssl147100 	    HUB_HANDLE_PORT_FEATURE_TYPE,
46360Sstevel@tonic-gate 	    USB_REQ_SET_FEATURE,
46370Sstevel@tonic-gate 	    CFS_PORT_RESET,
46380Sstevel@tonic-gate 	    port,
46390Sstevel@tonic-gate 	    0,
46400Sstevel@tonic-gate 	    NULL, 0,
46410Sstevel@tonic-gate 	    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
4642978Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
46430Sstevel@tonic-gate 		    "reset port%d failed (%d 0x%x %d)",
46440Sstevel@tonic-gate 		    port, completion_reason, cb_flags, rval);
46450Sstevel@tonic-gate 
46460Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
46470Sstevel@tonic-gate 
46480Sstevel@tonic-gate 		return (USB_FAILURE);
46490Sstevel@tonic-gate 	}
46500Sstevel@tonic-gate 
46510Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
46520Sstevel@tonic-gate 
46530Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
46540Sstevel@tonic-gate 	    "waiting on cv for reset completion");
46550Sstevel@tonic-gate 
46560Sstevel@tonic-gate 	/*
46570Sstevel@tonic-gate 	 * wait for port status change event
46580Sstevel@tonic-gate 	 */
465911066Srafael.vanoni@sun.com 	delta = drv_usectohz(hubd_device_delay / 10);
46600Sstevel@tonic-gate 	for (i = 0; i < hubd_retry_enumerate; i++) {
46610Sstevel@tonic-gate 		/*
46620Sstevel@tonic-gate 		 * start polling ep1 for receiving notification on
46630Sstevel@tonic-gate 		 * reset completion
46640Sstevel@tonic-gate 		 */
46650Sstevel@tonic-gate 		hubd_start_polling(hubd, HUBD_ALWAYS_START_POLLING);
46660Sstevel@tonic-gate 
46670Sstevel@tonic-gate 		/*
46680Sstevel@tonic-gate 		 * sleep a max of 100ms for reset completion
46690Sstevel@tonic-gate 		 * notification to be received
46700Sstevel@tonic-gate 		 */
46710Sstevel@tonic-gate 		if (hubd->h_port_reset_wait & port_mask) {
467211066Srafael.vanoni@sun.com 			rval = cv_reltimedwait(&hubd->h_cv_reset_port,
467311066Srafael.vanoni@sun.com 			    &hubd->h_mutex, delta, TR_CLOCK_TICK);
46740Sstevel@tonic-gate 			if ((rval <= 0) &&
46750Sstevel@tonic-gate 			    (hubd->h_port_reset_wait & port_mask)) {
46760Sstevel@tonic-gate 				/* we got woken up because of a timeout */
46770Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_PORT,
46780Sstevel@tonic-gate 				    hubd->h_log_handle,
46790Sstevel@tonic-gate 				    "timeout: reset port=%d failed", port);
46800Sstevel@tonic-gate 
46810Sstevel@tonic-gate 				hubd->h_port_reset_wait &=  ~port_mask;
46820Sstevel@tonic-gate 
46830Sstevel@tonic-gate 				hubd_stop_polling(hubd);
46840Sstevel@tonic-gate 
46850Sstevel@tonic-gate 				return (USB_FAILURE);
46860Sstevel@tonic-gate 			}
46870Sstevel@tonic-gate 		}
46880Sstevel@tonic-gate 
46890Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
46900Sstevel@tonic-gate 		    "reset completion received");
46910Sstevel@tonic-gate 
46920Sstevel@tonic-gate 		hubd_stop_polling(hubd);
46930Sstevel@tonic-gate 
46940Sstevel@tonic-gate 		data = NULL;
46950Sstevel@tonic-gate 
46960Sstevel@tonic-gate 		/* check status to determine whether reset completed */
46970Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
46980Sstevel@tonic-gate 		if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
46990Sstevel@tonic-gate 		    hubd->h_default_pipe,
47001001Ssl147100 		    HUB_GET_PORT_STATUS_TYPE,
47010Sstevel@tonic-gate 		    USB_REQ_GET_STATUS,
47020Sstevel@tonic-gate 		    0,
47030Sstevel@tonic-gate 		    port,
47040Sstevel@tonic-gate 		    GET_STATUS_LENGTH,
47050Sstevel@tonic-gate 		    &data, 0,
47060Sstevel@tonic-gate 		    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
4707978Sfrits 			USB_DPRINTF_L2(DPRINT_MASK_PORT,
47080Sstevel@tonic-gate 			    hubd->h_log_handle,
47090Sstevel@tonic-gate 			    "get status port%d failed (%d 0x%x %d)",
47100Sstevel@tonic-gate 			    port, completion_reason, cb_flags, rval);
47110Sstevel@tonic-gate 
47120Sstevel@tonic-gate 			if (data) {
47130Sstevel@tonic-gate 				freemsg(data);
47140Sstevel@tonic-gate 				data = NULL;
47150Sstevel@tonic-gate 			}
47160Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
47170Sstevel@tonic-gate 
47180Sstevel@tonic-gate 			continue;
47190Sstevel@tonic-gate 		}
47200Sstevel@tonic-gate 
47210Sstevel@tonic-gate 		status = (*(data->b_rptr + 1) << 8) | *(data->b_rptr);
47220Sstevel@tonic-gate 		change = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2);
47230Sstevel@tonic-gate 
47240Sstevel@tonic-gate 		freemsg(data);
47250Sstevel@tonic-gate 
47260Sstevel@tonic-gate 		/* continue only if port is still connected */
47270Sstevel@tonic-gate 		if (!(status & PORT_STATUS_CCS)) {
47280Sstevel@tonic-gate 
47290Sstevel@tonic-gate 			/* lost connection, set exit condition */
47300Sstevel@tonic-gate 			i = hubd_retry_enumerate;
47310Sstevel@tonic-gate 
47320Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
47330Sstevel@tonic-gate 
47340Sstevel@tonic-gate 			break;
47350Sstevel@tonic-gate 		}
47360Sstevel@tonic-gate 
47370Sstevel@tonic-gate 		if (status & PORT_STATUS_PRS) {
47380Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
47390Sstevel@tonic-gate 			    "port%d reset active", port);
47400Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
47410Sstevel@tonic-gate 
47420Sstevel@tonic-gate 			continue;
47430Sstevel@tonic-gate 		} else {
47440Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
47450Sstevel@tonic-gate 			    "port%d reset inactive", port);
47460Sstevel@tonic-gate 		}
47470Sstevel@tonic-gate 
47480Sstevel@tonic-gate 		if (change & PORT_CHANGE_PRSC) {
47490Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
47500Sstevel@tonic-gate 			    "clearing feature CFS_C_PORT_RESET");
47510Sstevel@tonic-gate 
47520Sstevel@tonic-gate 			if (usb_pipe_sync_ctrl_xfer(hubd->h_dip,
47530Sstevel@tonic-gate 			    hubd->h_default_pipe,
47541001Ssl147100 			    HUB_HANDLE_PORT_FEATURE_TYPE,
47550Sstevel@tonic-gate 			    USB_REQ_CLEAR_FEATURE,
47560Sstevel@tonic-gate 			    CFS_C_PORT_RESET,
47570Sstevel@tonic-gate 			    port,
47580Sstevel@tonic-gate 			    0,
47590Sstevel@tonic-gate 			    NULL, 0,
47600Sstevel@tonic-gate 			    &completion_reason, &cb_flags, 0) != USB_SUCCESS) {
47610Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_PORT,
47620Sstevel@tonic-gate 				    hubd->h_log_handle,
47630Sstevel@tonic-gate 				    "clear feature CFS_C_PORT_RESET"
47640Sstevel@tonic-gate 				    " port%d failed (%d 0x%x %d)",
47650Sstevel@tonic-gate 				    port, completion_reason, cb_flags, rval);
47660Sstevel@tonic-gate 			}
47670Sstevel@tonic-gate 		}
47680Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
47690Sstevel@tonic-gate 
47700Sstevel@tonic-gate 		break;
47710Sstevel@tonic-gate 	}
47720Sstevel@tonic-gate 
47730Sstevel@tonic-gate 	if (i >= hubd_retry_enumerate) {
47740Sstevel@tonic-gate 		/* port reset has failed */
47750Sstevel@tonic-gate 		rval = USB_FAILURE;
47760Sstevel@tonic-gate 	}
47770Sstevel@tonic-gate 
47780Sstevel@tonic-gate 	return (rval);
47790Sstevel@tonic-gate }
47800Sstevel@tonic-gate 
47810Sstevel@tonic-gate 
47820Sstevel@tonic-gate /*
47830Sstevel@tonic-gate  * hubd_enable_port:
47840Sstevel@tonic-gate  *	this may fail if the hub as been disconnected
47850Sstevel@tonic-gate  */
47860Sstevel@tonic-gate static int
hubd_enable_port(hubd_t * hubd,usb_port_t port)47870Sstevel@tonic-gate hubd_enable_port(hubd_t *hubd, usb_port_t port)
47880Sstevel@tonic-gate {
47890Sstevel@tonic-gate 	int	rval;
47900Sstevel@tonic-gate 	usb_cr_t completion_reason;
47910Sstevel@tonic-gate 	usb_cb_flags_t cb_flags;
47920Sstevel@tonic-gate 
47930Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
47940Sstevel@tonic-gate 	    "hubd_enable_port: port=%d", port);
47950Sstevel@tonic-gate 
47960Sstevel@tonic-gate 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
47970Sstevel@tonic-gate 
47980Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
47990Sstevel@tonic-gate 
48000Sstevel@tonic-gate 	/* Do not issue a SetFeature(PORT_ENABLE) on external hubs */
48010Sstevel@tonic-gate 	if (!usba_is_root_hub(hubd->h_dip)) {
48020Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
48030Sstevel@tonic-gate 
48040Sstevel@tonic-gate 		return (USB_SUCCESS);
48050Sstevel@tonic-gate 	}
48060Sstevel@tonic-gate 
48070Sstevel@tonic-gate 	if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
48080Sstevel@tonic-gate 	    hubd->h_default_pipe,
48091001Ssl147100 	    HUB_HANDLE_PORT_FEATURE_TYPE,
48100Sstevel@tonic-gate 	    USB_REQ_SET_FEATURE,
48110Sstevel@tonic-gate 	    CFS_PORT_ENABLE,
48120Sstevel@tonic-gate 	    port,
48130Sstevel@tonic-gate 	    0,
48140Sstevel@tonic-gate 	    NULL, 0,
48150Sstevel@tonic-gate 	    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
48160Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
48170Sstevel@tonic-gate 		    "enable port%d failed (%d 0x%x %d)",
48180Sstevel@tonic-gate 		    port, completion_reason, cb_flags, rval);
48190Sstevel@tonic-gate 	}
48200Sstevel@tonic-gate 
48210Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
48220Sstevel@tonic-gate 
48230Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
48240Sstevel@tonic-gate 	    "enabling port done");
48250Sstevel@tonic-gate 
48260Sstevel@tonic-gate 	return (rval);
48270Sstevel@tonic-gate }
48280Sstevel@tonic-gate 
48290Sstevel@tonic-gate 
48300Sstevel@tonic-gate /*
48310Sstevel@tonic-gate  * hubd_disable_port
48320Sstevel@tonic-gate  */
48330Sstevel@tonic-gate static int
hubd_disable_port(hubd_t * hubd,usb_port_t port)48340Sstevel@tonic-gate hubd_disable_port(hubd_t *hubd, usb_port_t port)
48350Sstevel@tonic-gate {
48360Sstevel@tonic-gate 	int	rval;
48370Sstevel@tonic-gate 	usb_cr_t completion_reason;
48380Sstevel@tonic-gate 	usb_cb_flags_t cb_flags;
48390Sstevel@tonic-gate 
48400Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
48410Sstevel@tonic-gate 	    "hubd_disable_port: port=%d", port);
48420Sstevel@tonic-gate 
48430Sstevel@tonic-gate 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
48440Sstevel@tonic-gate 
48450Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
48460Sstevel@tonic-gate 
48470Sstevel@tonic-gate 	if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
48480Sstevel@tonic-gate 	    hubd->h_default_pipe,
48491001Ssl147100 	    HUB_HANDLE_PORT_FEATURE_TYPE,
48500Sstevel@tonic-gate 	    USB_REQ_CLEAR_FEATURE,
48510Sstevel@tonic-gate 	    CFS_PORT_ENABLE,
48520Sstevel@tonic-gate 	    port,
48530Sstevel@tonic-gate 	    0,
48540Sstevel@tonic-gate 	    NULL, 0,
48550Sstevel@tonic-gate 	    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
48560Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
48570Sstevel@tonic-gate 		    "disable port%d failed (%d 0x%x %d)", port,
48580Sstevel@tonic-gate 		    completion_reason, cb_flags, rval);
48590Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
48600Sstevel@tonic-gate 
48610Sstevel@tonic-gate 		return (USB_FAILURE);
48620Sstevel@tonic-gate 	}
48630Sstevel@tonic-gate 
48640Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
48650Sstevel@tonic-gate 	    "clearing feature CFS_C_PORT_ENABLE");
48660Sstevel@tonic-gate 
48670Sstevel@tonic-gate 	if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
48680Sstevel@tonic-gate 	    hubd->h_default_pipe,
48691001Ssl147100 	    HUB_HANDLE_PORT_FEATURE_TYPE,
48700Sstevel@tonic-gate 	    USB_REQ_CLEAR_FEATURE,
48710Sstevel@tonic-gate 	    CFS_C_PORT_ENABLE,
48720Sstevel@tonic-gate 	    port,
48730Sstevel@tonic-gate 	    0,
48740Sstevel@tonic-gate 	    NULL, 0,
48750Sstevel@tonic-gate 	    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
48760Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_PORT,
48770Sstevel@tonic-gate 		    hubd->h_log_handle,
48780Sstevel@tonic-gate 		    "clear feature CFS_C_PORT_ENABLE port%d failed "
48790Sstevel@tonic-gate 		    "(%d 0x%x %d)",
48800Sstevel@tonic-gate 		    port, completion_reason, cb_flags, rval);
48810Sstevel@tonic-gate 
48820Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
48830Sstevel@tonic-gate 
48840Sstevel@tonic-gate 		return (USB_FAILURE);
48850Sstevel@tonic-gate 	}
48860Sstevel@tonic-gate 
48870Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
48880Sstevel@tonic-gate 
48890Sstevel@tonic-gate 	return (USB_SUCCESS);
48900Sstevel@tonic-gate }
48910Sstevel@tonic-gate 
48920Sstevel@tonic-gate 
48930Sstevel@tonic-gate /*
48940Sstevel@tonic-gate  * hubd_determine_port_status:
48950Sstevel@tonic-gate  */
48960Sstevel@tonic-gate static int
hubd_determine_port_status(hubd_t * hubd,usb_port_t port,uint16_t * status,uint16_t * change,uint_t ack_flag)48970Sstevel@tonic-gate hubd_determine_port_status(hubd_t *hubd, usb_port_t port,
48980Sstevel@tonic-gate 		uint16_t *status, uint16_t *change, uint_t ack_flag)
48990Sstevel@tonic-gate {
49000Sstevel@tonic-gate 	int rval;
49010Sstevel@tonic-gate 	mblk_t	*data = NULL;
49020Sstevel@tonic-gate 	usb_cr_t completion_reason;
49030Sstevel@tonic-gate 	usb_cb_flags_t cb_flags;
49040Sstevel@tonic-gate 
49050Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
49060Sstevel@tonic-gate 	    "hubd_determine_port_status: port=%d, state=0x%x ack=0x%x", port,
49070Sstevel@tonic-gate 	    hubd->h_port_state[port], ack_flag);
49080Sstevel@tonic-gate 
49090Sstevel@tonic-gate 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
49100Sstevel@tonic-gate 
49110Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
49120Sstevel@tonic-gate 
49130Sstevel@tonic-gate 	if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
49140Sstevel@tonic-gate 	    hubd->h_default_pipe,
49151001Ssl147100 	    HUB_GET_PORT_STATUS_TYPE,
49160Sstevel@tonic-gate 	    USB_REQ_GET_STATUS,
49170Sstevel@tonic-gate 	    0,
49180Sstevel@tonic-gate 	    port,
49190Sstevel@tonic-gate 	    GET_STATUS_LENGTH,
49200Sstevel@tonic-gate 	    &data, 0,
49210Sstevel@tonic-gate 	    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
49220Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
49230Sstevel@tonic-gate 		    "port=%d get status failed (%d 0x%x %d)",
49240Sstevel@tonic-gate 		    port, completion_reason, cb_flags, rval);
49250Sstevel@tonic-gate 
49260Sstevel@tonic-gate 		if (data) {
49270Sstevel@tonic-gate 			freemsg(data);
49280Sstevel@tonic-gate 		}
49290Sstevel@tonic-gate 
49300Sstevel@tonic-gate 		*status = *change = 0;
49310Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
49320Sstevel@tonic-gate 
49330Sstevel@tonic-gate 		return (rval);
49340Sstevel@tonic-gate 	}
49350Sstevel@tonic-gate 
49360Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
49377492SZhigang.Lu@Sun.COM 	if (MBLKL(data) != GET_STATUS_LENGTH) {
49380Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
49396898Sfb209375 		    "port %d: length incorrect %ld",
49407492SZhigang.Lu@Sun.COM 		    port, MBLKL(data));
49410Sstevel@tonic-gate 		freemsg(data);
49420Sstevel@tonic-gate 		*status = *change = 0;
49430Sstevel@tonic-gate 
49440Sstevel@tonic-gate 		return (rval);
49450Sstevel@tonic-gate 	}
49460Sstevel@tonic-gate 
49470Sstevel@tonic-gate 
49480Sstevel@tonic-gate 	*status = (*(data->b_rptr + 1) << 8) | *(data->b_rptr);
49490Sstevel@tonic-gate 	*change = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2);
49500Sstevel@tonic-gate 
49510Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
49520Sstevel@tonic-gate 	    "port%d status=0x%x, change=0x%x", port, *status, *change);
49530Sstevel@tonic-gate 
49540Sstevel@tonic-gate 	freemsg(data);
49550Sstevel@tonic-gate 
49560Sstevel@tonic-gate 	if (*status & PORT_STATUS_CCS) {
49570Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
49580Sstevel@tonic-gate 		    "port%d connected", port);
49590Sstevel@tonic-gate 
49600Sstevel@tonic-gate 		hubd->h_port_state[port] |= (PORT_STATUS_CCS & ack_flag);
49610Sstevel@tonic-gate 	} else {
49620Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
49630Sstevel@tonic-gate 		    "port%d disconnected", port);
49640Sstevel@tonic-gate 
49650Sstevel@tonic-gate 		hubd->h_port_state[port] &= ~(PORT_STATUS_CCS & ack_flag);
49660Sstevel@tonic-gate 	}
49670Sstevel@tonic-gate 
49680Sstevel@tonic-gate 	if (*status & PORT_STATUS_PES) {
49690Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
49700Sstevel@tonic-gate 		    "port%d enabled", port);
49710Sstevel@tonic-gate 
49720Sstevel@tonic-gate 		hubd->h_port_state[port] |= (PORT_STATUS_PES & ack_flag);
49730Sstevel@tonic-gate 	} else {
49740Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
49750Sstevel@tonic-gate 		    "port%d disabled", port);
49760Sstevel@tonic-gate 
49770Sstevel@tonic-gate 		hubd->h_port_state[port] &= ~(PORT_STATUS_PES & ack_flag);
49780Sstevel@tonic-gate 	}
49790Sstevel@tonic-gate 
49800Sstevel@tonic-gate 	if (*status & PORT_STATUS_PSS) {
49810Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
49820Sstevel@tonic-gate 		    "port%d suspended", port);
49830Sstevel@tonic-gate 
49840Sstevel@tonic-gate 		hubd->h_port_state[port] |= (PORT_STATUS_PSS & ack_flag);
49850Sstevel@tonic-gate 	} else {
49860Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
49870Sstevel@tonic-gate 		    "port%d not suspended", port);
49880Sstevel@tonic-gate 
49890Sstevel@tonic-gate 		hubd->h_port_state[port] &= ~(PORT_STATUS_PSS & ack_flag);
49900Sstevel@tonic-gate 	}
49910Sstevel@tonic-gate 
49920Sstevel@tonic-gate 	if (*change & PORT_CHANGE_PRSC) {
49930Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
49940Sstevel@tonic-gate 		    "port%d reset completed", port);
49950Sstevel@tonic-gate 
49960Sstevel@tonic-gate 		hubd->h_port_state[port] |= (PORT_CHANGE_PRSC & ack_flag);
49970Sstevel@tonic-gate 	} else {
49980Sstevel@tonic-gate 
49990Sstevel@tonic-gate 		hubd->h_port_state[port] &= ~(PORT_CHANGE_PRSC & ack_flag);
50000Sstevel@tonic-gate 	}
50010Sstevel@tonic-gate 
50020Sstevel@tonic-gate 	if (*status & PORT_STATUS_POCI) {
5003978Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
5004978Sfrits 		    "port%d overcurrent!", port);
50050Sstevel@tonic-gate 
50060Sstevel@tonic-gate 		hubd->h_port_state[port] |= (PORT_STATUS_POCI & ack_flag);
50070Sstevel@tonic-gate 	} else {
50080Sstevel@tonic-gate 
50090Sstevel@tonic-gate 		hubd->h_port_state[port] &= ~(PORT_STATUS_POCI & ack_flag);
50100Sstevel@tonic-gate 	}
50110Sstevel@tonic-gate 
50120Sstevel@tonic-gate 	if (*status & PORT_STATUS_PRS) {
50130Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
50140Sstevel@tonic-gate 		    "port%d reset active", port);
50150Sstevel@tonic-gate 
50160Sstevel@tonic-gate 		hubd->h_port_state[port] |= (PORT_STATUS_PRS & ack_flag);
50170Sstevel@tonic-gate 	} else {
50180Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
50190Sstevel@tonic-gate 		    "port%d reset inactive", port);
50200Sstevel@tonic-gate 
50210Sstevel@tonic-gate 		hubd->h_port_state[port] &= ~(PORT_STATUS_PRS & ack_flag);
50220Sstevel@tonic-gate 	}
50230Sstevel@tonic-gate 	if (*status & PORT_STATUS_PPS) {
50240Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
50250Sstevel@tonic-gate 		    "port%d power on", port);
50260Sstevel@tonic-gate 
50270Sstevel@tonic-gate 		hubd->h_port_state[port] |= (PORT_STATUS_PPS & ack_flag);
50280Sstevel@tonic-gate 	} else {
50290Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
50300Sstevel@tonic-gate 		    "port%d power off", port);
50310Sstevel@tonic-gate 
50320Sstevel@tonic-gate 		hubd->h_port_state[port] &= ~(PORT_STATUS_PPS & ack_flag);
50330Sstevel@tonic-gate 	}
50340Sstevel@tonic-gate 	if (*status & PORT_STATUS_LSDA) {
50350Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
50360Sstevel@tonic-gate 		    "port%d low speed", port);
50370Sstevel@tonic-gate 
50380Sstevel@tonic-gate 		hubd->h_port_state[port] |= (PORT_STATUS_LSDA & ack_flag);
50390Sstevel@tonic-gate 	} else {
50400Sstevel@tonic-gate 		hubd->h_port_state[port] &= ~(PORT_STATUS_LSDA & ack_flag);
50410Sstevel@tonic-gate 		if (*status & PORT_STATUS_HSDA) {
50420Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_PORT,
50430Sstevel@tonic-gate 			    hubd->h_log_handle, "port%d "
50440Sstevel@tonic-gate 			    "high speed", port);
50450Sstevel@tonic-gate 
50460Sstevel@tonic-gate 			hubd->h_port_state[port] |=
50474763Slg150142 			    (PORT_STATUS_HSDA & ack_flag);
50480Sstevel@tonic-gate 		} else {
50490Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_PORT,
50500Sstevel@tonic-gate 			    hubd->h_log_handle, "port%d "
50510Sstevel@tonic-gate 			    "full speed", port);
50520Sstevel@tonic-gate 
50530Sstevel@tonic-gate 			hubd->h_port_state[port] &=
50544763Slg150142 			    ~(PORT_STATUS_HSDA & ack_flag);
50550Sstevel@tonic-gate 		}
50560Sstevel@tonic-gate 	}
50570Sstevel@tonic-gate 
50580Sstevel@tonic-gate 	/*
50590Sstevel@tonic-gate 	 * Acknowledge connection, enable, reset status
50600Sstevel@tonic-gate 	 */
50610Sstevel@tonic-gate 	if (ack_flag) {
50620Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
50630Sstevel@tonic-gate 		if (*change & PORT_CHANGE_CSC & ack_flag) {
50640Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
50650Sstevel@tonic-gate 			    "clearing feature CFS_C_PORT_CONNECTION");
50660Sstevel@tonic-gate 			if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
50670Sstevel@tonic-gate 			    hubd->h_default_pipe,
50681001Ssl147100 			    HUB_HANDLE_PORT_FEATURE_TYPE,
50690Sstevel@tonic-gate 			    USB_REQ_CLEAR_FEATURE,
50700Sstevel@tonic-gate 			    CFS_C_PORT_CONNECTION,
50710Sstevel@tonic-gate 			    port,
50720Sstevel@tonic-gate 			    0, NULL, 0,
50730Sstevel@tonic-gate 			    &completion_reason, &cb_flags, 0)) !=
50740Sstevel@tonic-gate 			    USB_SUCCESS) {
50750Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_PORT,
50760Sstevel@tonic-gate 				    hubd->h_log_handle,
50770Sstevel@tonic-gate 				    "clear feature CFS_C_PORT_CONNECTION"
50780Sstevel@tonic-gate 				    " port%d failed (%d 0x%x %d)",
50790Sstevel@tonic-gate 				    port, completion_reason, cb_flags, rval);
50800Sstevel@tonic-gate 			}
50810Sstevel@tonic-gate 		}
50820Sstevel@tonic-gate 		if (*change & PORT_CHANGE_PESC & ack_flag) {
50830Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
50840Sstevel@tonic-gate 			    "clearing feature CFS_C_PORT_ENABLE");
50850Sstevel@tonic-gate 			if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
50860Sstevel@tonic-gate 			    hubd->h_default_pipe,
50871001Ssl147100 			    HUB_HANDLE_PORT_FEATURE_TYPE,
50880Sstevel@tonic-gate 			    USB_REQ_CLEAR_FEATURE,
50890Sstevel@tonic-gate 			    CFS_C_PORT_ENABLE,
50900Sstevel@tonic-gate 			    port,
50910Sstevel@tonic-gate 			    0, NULL, 0,
50920Sstevel@tonic-gate 			    &completion_reason, &cb_flags, 0)) !=
50930Sstevel@tonic-gate 			    USB_SUCCESS) {
50940Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_PORT,
50950Sstevel@tonic-gate 				    hubd->h_log_handle,
50960Sstevel@tonic-gate 				    "clear feature CFS_C_PORT_ENABLE"
50970Sstevel@tonic-gate 				    " port%d failed (%d 0x%x %d)",
50980Sstevel@tonic-gate 				    port, completion_reason, cb_flags, rval);
50990Sstevel@tonic-gate 			}
51000Sstevel@tonic-gate 		}
51010Sstevel@tonic-gate 		if (*change & PORT_CHANGE_PSSC & ack_flag) {
51020Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
51030Sstevel@tonic-gate 			    "clearing feature CFS_C_PORT_SUSPEND");
51040Sstevel@tonic-gate 
51050Sstevel@tonic-gate 			if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
51060Sstevel@tonic-gate 			    hubd->h_default_pipe,
51071001Ssl147100 			    HUB_HANDLE_PORT_FEATURE_TYPE,
51080Sstevel@tonic-gate 			    USB_REQ_CLEAR_FEATURE,
51090Sstevel@tonic-gate 			    CFS_C_PORT_SUSPEND,
51100Sstevel@tonic-gate 			    port,
51110Sstevel@tonic-gate 			    0, NULL, 0,
51120Sstevel@tonic-gate 			    &completion_reason, &cb_flags, 0)) !=
51130Sstevel@tonic-gate 			    USB_SUCCESS) {
51140Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_PORT,
51150Sstevel@tonic-gate 				    hubd->h_log_handle,
51160Sstevel@tonic-gate 				    "clear feature CFS_C_PORT_SUSPEND"
51170Sstevel@tonic-gate 				    " port%d failed (%d 0x%x %d)",
51180Sstevel@tonic-gate 				    port, completion_reason, cb_flags, rval);
51190Sstevel@tonic-gate 			}
51200Sstevel@tonic-gate 		}
51210Sstevel@tonic-gate 		if (*change & PORT_CHANGE_OCIC & ack_flag) {
51220Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
51230Sstevel@tonic-gate 			    "clearing feature CFS_C_PORT_OVER_CURRENT");
51240Sstevel@tonic-gate 
51250Sstevel@tonic-gate 			if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
51260Sstevel@tonic-gate 			    hubd->h_default_pipe,
51271001Ssl147100 			    HUB_HANDLE_PORT_FEATURE_TYPE,
51280Sstevel@tonic-gate 			    USB_REQ_CLEAR_FEATURE,
51290Sstevel@tonic-gate 			    CFS_C_PORT_OVER_CURRENT,
51300Sstevel@tonic-gate 			    port,
51310Sstevel@tonic-gate 			    0, NULL, 0,
51320Sstevel@tonic-gate 			    &completion_reason, &cb_flags, 0)) !=
51330Sstevel@tonic-gate 			    USB_SUCCESS) {
51340Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_PORT,
51350Sstevel@tonic-gate 				    hubd->h_log_handle,
51360Sstevel@tonic-gate 				    "clear feature CFS_C_PORT_OVER_CURRENT"
51370Sstevel@tonic-gate 				    " port%d failed (%d 0x%x %d)",
51380Sstevel@tonic-gate 				    port, completion_reason, cb_flags, rval);
51390Sstevel@tonic-gate 			}
51400Sstevel@tonic-gate 		}
51410Sstevel@tonic-gate 		if (*change & PORT_CHANGE_PRSC & ack_flag) {
51420Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
51430Sstevel@tonic-gate 			    "clearing feature CFS_C_PORT_RESET");
51440Sstevel@tonic-gate 			if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
51450Sstevel@tonic-gate 			    hubd->h_default_pipe,
51461001Ssl147100 			    HUB_HANDLE_PORT_FEATURE_TYPE,
51470Sstevel@tonic-gate 			    USB_REQ_CLEAR_FEATURE,
51480Sstevel@tonic-gate 			    CFS_C_PORT_RESET,
51490Sstevel@tonic-gate 			    port,
51500Sstevel@tonic-gate 			    0, NULL, 0,
51510Sstevel@tonic-gate 			    &completion_reason, &cb_flags, 0)) !=
51520Sstevel@tonic-gate 			    USB_SUCCESS) {
51530Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_PORT,
51540Sstevel@tonic-gate 				    hubd->h_log_handle,
51550Sstevel@tonic-gate 				    "clear feature CFS_C_PORT_RESET"
51560Sstevel@tonic-gate 				    " port%d failed (%d 0x%x %d)",
51570Sstevel@tonic-gate 				    port, completion_reason, cb_flags, rval);
51580Sstevel@tonic-gate 			}
51590Sstevel@tonic-gate 		}
51600Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
51610Sstevel@tonic-gate 	}
51620Sstevel@tonic-gate 
51630Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
51640Sstevel@tonic-gate 	    "new port%d state 0x%x", port, hubd->h_port_state[port]);
51650Sstevel@tonic-gate 
51660Sstevel@tonic-gate 
51670Sstevel@tonic-gate 	return (USB_SUCCESS);
51680Sstevel@tonic-gate }
51690Sstevel@tonic-gate 
51700Sstevel@tonic-gate 
51710Sstevel@tonic-gate /*
51720Sstevel@tonic-gate  * hubd_recover_disabled_port
51730Sstevel@tonic-gate  * if the port got disabled because of an error
51740Sstevel@tonic-gate  * enable it. If hub doesn't suport enable port,
51750Sstevel@tonic-gate  * reset the port to bring the device to life again
51760Sstevel@tonic-gate  */
51770Sstevel@tonic-gate static int
hubd_recover_disabled_port(hubd_t * hubd,usb_port_t port)51780Sstevel@tonic-gate hubd_recover_disabled_port(hubd_t *hubd, usb_port_t port)
51790Sstevel@tonic-gate {
51800Sstevel@tonic-gate 	uint16_t	status;
51810Sstevel@tonic-gate 	uint16_t	change;
51820Sstevel@tonic-gate 	int		rval = USB_FAILURE;
51830Sstevel@tonic-gate 
51840Sstevel@tonic-gate 	/* first try enabling the port */
51850Sstevel@tonic-gate 	(void) hubd_enable_port(hubd, port);
51860Sstevel@tonic-gate 
51870Sstevel@tonic-gate 	/* read the port status */
51880Sstevel@tonic-gate 	(void) hubd_determine_port_status(hubd, port, &status, &change,
51890Sstevel@tonic-gate 	    PORT_CHANGE_PESC);
51900Sstevel@tonic-gate 
51910Sstevel@tonic-gate 	if (status & PORT_STATUS_PES) {
51920Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
51934763Slg150142 		    "Port%d now Enabled", port);
51940Sstevel@tonic-gate 	} else if (status & PORT_STATUS_CCS) {
51950Sstevel@tonic-gate 		/* first post a disconnect event to the child */
51960Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
51970Sstevel@tonic-gate 		hubd_post_event(hubd, port, USBA_EVENT_TAG_HOT_REMOVAL);
51980Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
51990Sstevel@tonic-gate 
52000Sstevel@tonic-gate 		/* then reset the port and recover the device */
52010Sstevel@tonic-gate 		rval = hubd_handle_port_connect(hubd, port);
52020Sstevel@tonic-gate 
52030Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
52044763Slg150142 		    "Port%d now Enabled by force", port);
52050Sstevel@tonic-gate 	}
52060Sstevel@tonic-gate 
52070Sstevel@tonic-gate 	return (rval);
52080Sstevel@tonic-gate }
52090Sstevel@tonic-gate 
52100Sstevel@tonic-gate 
52110Sstevel@tonic-gate /*
52120Sstevel@tonic-gate  * hubd_enable_all_port_power:
52130Sstevel@tonic-gate  */
52140Sstevel@tonic-gate static int
hubd_enable_all_port_power(hubd_t * hubd)52150Sstevel@tonic-gate hubd_enable_all_port_power(hubd_t *hubd)
52160Sstevel@tonic-gate {
52170Sstevel@tonic-gate 	usb_hub_descr_t	*hub_descr;
52180Sstevel@tonic-gate 	int		wait;
52190Sstevel@tonic-gate 	usb_port_t	port;
52200Sstevel@tonic-gate 	uint_t		retry;
52210Sstevel@tonic-gate 	uint16_t	status;
52220Sstevel@tonic-gate 	uint16_t	change;
52230Sstevel@tonic-gate 
52240Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
52250Sstevel@tonic-gate 	    "hubd_enable_all_port_power");
52260Sstevel@tonic-gate 
52270Sstevel@tonic-gate 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
52280Sstevel@tonic-gate 
52290Sstevel@tonic-gate 	hub_descr = &hubd->h_hub_descr;
52300Sstevel@tonic-gate 
52310Sstevel@tonic-gate 	/*
52320Sstevel@tonic-gate 	 * According to section 11.11 of USB, for hubs with no power
52330Sstevel@tonic-gate 	 * switches, bPwrOn2PwrGood is zero. But we wait for some
52340Sstevel@tonic-gate 	 * arbitrary time to enable power to become stable.
52350Sstevel@tonic-gate 	 *
52360Sstevel@tonic-gate 	 * If an hub supports port power switching, we need to wait
52370Sstevel@tonic-gate 	 * at least 20ms before accessing corresponding usb port.
52380Sstevel@tonic-gate 	 */
52390Sstevel@tonic-gate 	if ((hub_descr->wHubCharacteristics &
52400Sstevel@tonic-gate 	    HUB_CHARS_NO_POWER_SWITCHING) || (!hub_descr->bPwrOn2PwrGood)) {
52410Sstevel@tonic-gate 		wait = hubd_device_delay / 10;
52420Sstevel@tonic-gate 	} else {
52430Sstevel@tonic-gate 		wait = max(HUB_DEFAULT_POPG,
52440Sstevel@tonic-gate 		    hub_descr->bPwrOn2PwrGood) * 2 * 1000;
52450Sstevel@tonic-gate 	}
52460Sstevel@tonic-gate 
52470Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
52480Sstevel@tonic-gate 	    "hubd_enable_all_port_power: popg=%d wait=%d",
52490Sstevel@tonic-gate 	    hub_descr->bPwrOn2PwrGood, wait);
52500Sstevel@tonic-gate 
52510Sstevel@tonic-gate 	/*
52520Sstevel@tonic-gate 	 * Enable power per port. we ignore gang power and power mask
52530Sstevel@tonic-gate 	 * and always enable all ports one by one.
52540Sstevel@tonic-gate 	 */
52550Sstevel@tonic-gate 	for (port = 1; port <= hub_descr->bNbrPorts; port++) {
52560Sstevel@tonic-gate 		/*
52570Sstevel@tonic-gate 		 * Transition the port from the Powered Off to the
52580Sstevel@tonic-gate 		 * Disconnected state by supplying power to the port.
52590Sstevel@tonic-gate 		 */
52600Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_PORT,
52610Sstevel@tonic-gate 		    hubd->h_log_handle,
52620Sstevel@tonic-gate 		    "hubd_enable_all_port_power: power port=%d", port);
52630Sstevel@tonic-gate 
52640Sstevel@tonic-gate 		(void) hubd_enable_port_power(hubd, port);
52650Sstevel@tonic-gate 	}
52660Sstevel@tonic-gate 
52670Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
52680Sstevel@tonic-gate 	delay(drv_usectohz(wait));
52690Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
52700Sstevel@tonic-gate 
52710Sstevel@tonic-gate 	/* For retry if any, use some extra delay */
52720Sstevel@tonic-gate 	wait = max(wait, hubd_device_delay / 10);
52730Sstevel@tonic-gate 
52740Sstevel@tonic-gate 	/* Check each port power status for a given usb hub */
52750Sstevel@tonic-gate 	for (port = 1; port <= hub_descr->bNbrPorts; port++) {
52760Sstevel@tonic-gate 
52770Sstevel@tonic-gate 		/* Get port status */
52780Sstevel@tonic-gate 		(void) hubd_determine_port_status(hubd, port,
52790Sstevel@tonic-gate 		    &status, &change, 0);
52800Sstevel@tonic-gate 
52810Sstevel@tonic-gate 		for (retry = 0; ((!(status & PORT_STATUS_PPS)) &&
52820Sstevel@tonic-gate 		    (retry < HUBD_PORT_RETRY)); retry++) {
52830Sstevel@tonic-gate 
52840Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
52850Sstevel@tonic-gate 			    "Retry is in progress %d: port %d status %d",
52860Sstevel@tonic-gate 			    retry, port, status);
52870Sstevel@tonic-gate 
52880Sstevel@tonic-gate 			(void) hubd_enable_port_power(hubd, port);
52890Sstevel@tonic-gate 
52900Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
52910Sstevel@tonic-gate 			delay(drv_usectohz(wait));
52920Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
52930Sstevel@tonic-gate 
52940Sstevel@tonic-gate 			/* Get port status */
52950Sstevel@tonic-gate 			(void) hubd_determine_port_status(hubd, port,
52960Sstevel@tonic-gate 			    &status, &change, 0);
52970Sstevel@tonic-gate 		}
52980Sstevel@tonic-gate 
52990Sstevel@tonic-gate 		/* Print warning message if port has no power */
53000Sstevel@tonic-gate 		if (!(status & PORT_STATUS_PPS)) {
53010Sstevel@tonic-gate 
5302978Sfrits 			USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
53030Sstevel@tonic-gate 			    "hubd_enable_all_port_power: port %d power-on "
53040Sstevel@tonic-gate 			    "failed, port status 0x%x", port, status);
53050Sstevel@tonic-gate 		}
53060Sstevel@tonic-gate 	}
53070Sstevel@tonic-gate 
53080Sstevel@tonic-gate 	return (USB_SUCCESS);
53090Sstevel@tonic-gate }
53100Sstevel@tonic-gate 
53110Sstevel@tonic-gate 
53120Sstevel@tonic-gate /*
53130Sstevel@tonic-gate  * hubd_enable_port_power:
53140Sstevel@tonic-gate  *	enable individual port power
53150Sstevel@tonic-gate  */
53160Sstevel@tonic-gate static int
hubd_enable_port_power(hubd_t * hubd,usb_port_t port)53170Sstevel@tonic-gate hubd_enable_port_power(hubd_t *hubd, usb_port_t port)
53180Sstevel@tonic-gate {
53190Sstevel@tonic-gate 	int		rval;
53200Sstevel@tonic-gate 	usb_cr_t	completion_reason;
53210Sstevel@tonic-gate 	usb_cb_flags_t	cb_flags;
53220Sstevel@tonic-gate 
53230Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
53240Sstevel@tonic-gate 	    "hubd_enable_port_power: port=%d", port);
53250Sstevel@tonic-gate 
53260Sstevel@tonic-gate 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
53270Sstevel@tonic-gate 	ASSERT(hubd->h_default_pipe != 0);
53280Sstevel@tonic-gate 
53290Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
53300Sstevel@tonic-gate 
53310Sstevel@tonic-gate 	if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
53320Sstevel@tonic-gate 	    hubd->h_default_pipe,
53331001Ssl147100 	    HUB_HANDLE_PORT_FEATURE_TYPE,
53340Sstevel@tonic-gate 	    USB_REQ_SET_FEATURE,
53350Sstevel@tonic-gate 	    CFS_PORT_POWER,
53360Sstevel@tonic-gate 	    port,
53370Sstevel@tonic-gate 	    0, NULL, 0,
53380Sstevel@tonic-gate 	    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
53390Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
53400Sstevel@tonic-gate 		    "set port power failed (%d 0x%x %d)",
53410Sstevel@tonic-gate 		    completion_reason, cb_flags, rval);
53420Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
53430Sstevel@tonic-gate 
53440Sstevel@tonic-gate 		return (USB_FAILURE);
53450Sstevel@tonic-gate 	} else {
53460Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
53470Sstevel@tonic-gate 		hubd->h_port_state[port] |= PORT_STATUS_PPS;
53480Sstevel@tonic-gate 
53490Sstevel@tonic-gate 		return (USB_SUCCESS);
53500Sstevel@tonic-gate 	}
53510Sstevel@tonic-gate }
53520Sstevel@tonic-gate 
53530Sstevel@tonic-gate 
53540Sstevel@tonic-gate /*
53550Sstevel@tonic-gate  * hubd_disable_all_port_power:
53560Sstevel@tonic-gate  */
53570Sstevel@tonic-gate static int
hubd_disable_all_port_power(hubd_t * hubd)53580Sstevel@tonic-gate hubd_disable_all_port_power(hubd_t *hubd)
53590Sstevel@tonic-gate {
53600Sstevel@tonic-gate 	usb_port_t port;
53610Sstevel@tonic-gate 
53620Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
53630Sstevel@tonic-gate 	    "hubd_disable_all_port_power");
53640Sstevel@tonic-gate 
53650Sstevel@tonic-gate 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
53660Sstevel@tonic-gate 
53670Sstevel@tonic-gate 	/*
53680Sstevel@tonic-gate 	 * disable power per port, ignore gang power and power mask
53690Sstevel@tonic-gate 	 */
53700Sstevel@tonic-gate 	for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
53710Sstevel@tonic-gate 		(void) hubd_disable_port_power(hubd, port);
53720Sstevel@tonic-gate 	}
53730Sstevel@tonic-gate 
53740Sstevel@tonic-gate 	return (USB_SUCCESS);
53750Sstevel@tonic-gate }
53760Sstevel@tonic-gate 
53770Sstevel@tonic-gate 
53780Sstevel@tonic-gate /*
53790Sstevel@tonic-gate  * hubd_disable_port_power:
53800Sstevel@tonic-gate  *	disable individual port power
53810Sstevel@tonic-gate  */
53820Sstevel@tonic-gate static int
hubd_disable_port_power(hubd_t * hubd,usb_port_t port)53830Sstevel@tonic-gate hubd_disable_port_power(hubd_t *hubd, usb_port_t port)
53840Sstevel@tonic-gate {
53850Sstevel@tonic-gate 	int		rval;
53860Sstevel@tonic-gate 	usb_cr_t	completion_reason;
53870Sstevel@tonic-gate 	usb_cb_flags_t	cb_flags;
53880Sstevel@tonic-gate 
53890Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
53900Sstevel@tonic-gate 	    "hubd_disable_port_power: port=%d", port);
53910Sstevel@tonic-gate 
53920Sstevel@tonic-gate 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
53930Sstevel@tonic-gate 
53940Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
53950Sstevel@tonic-gate 
53960Sstevel@tonic-gate 	if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
53970Sstevel@tonic-gate 	    hubd->h_default_pipe,
53981001Ssl147100 	    HUB_HANDLE_PORT_FEATURE_TYPE,
53990Sstevel@tonic-gate 	    USB_REQ_CLEAR_FEATURE,
54000Sstevel@tonic-gate 	    CFS_PORT_POWER,
54010Sstevel@tonic-gate 	    port,
54020Sstevel@tonic-gate 	    0, NULL, 0,
54030Sstevel@tonic-gate 	    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
54040Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
54050Sstevel@tonic-gate 		    "clearing port%d power failed (%d 0x%x %d)",
54060Sstevel@tonic-gate 		    port, completion_reason, cb_flags, rval);
54070Sstevel@tonic-gate 
54080Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
54090Sstevel@tonic-gate 
54100Sstevel@tonic-gate 		return (USB_FAILURE);
54110Sstevel@tonic-gate 	} else {
54120Sstevel@tonic-gate 
54130Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
54140Sstevel@tonic-gate 		ASSERT(completion_reason == 0);
54150Sstevel@tonic-gate 		hubd->h_port_state[port] &= ~PORT_STATUS_PPS;
54160Sstevel@tonic-gate 
54170Sstevel@tonic-gate 		return (USB_SUCCESS);
54180Sstevel@tonic-gate 	}
54190Sstevel@tonic-gate }
54200Sstevel@tonic-gate 
54210Sstevel@tonic-gate 
54220Sstevel@tonic-gate /*
54230Sstevel@tonic-gate  * Search the database of user preferences and find out the preferred
54240Sstevel@tonic-gate  * configuration for this new device
54250Sstevel@tonic-gate  */
54269430SRaymond.Chen@Sun.COM int
hubd_select_device_configuration(hubd_t * hubd,usb_port_t port,dev_info_t * child_dip,usba_device_t * child_ud)54270Sstevel@tonic-gate hubd_select_device_configuration(hubd_t *hubd, usb_port_t port,
54280Sstevel@tonic-gate 	dev_info_t *child_dip, usba_device_t *child_ud)
54290Sstevel@tonic-gate {
54300Sstevel@tonic-gate 	char		*pathname = NULL;
54310Sstevel@tonic-gate 	char		*tmp_path = NULL;
54320Sstevel@tonic-gate 	int		user_conf;
54330Sstevel@tonic-gate 	int		pathlen;
54340Sstevel@tonic-gate 	usb_dev_descr_t	*usbdev_ptr;
54350Sstevel@tonic-gate 	usba_configrec_t *user_pref;
54360Sstevel@tonic-gate 
54370Sstevel@tonic-gate 	mutex_enter(&child_ud->usb_mutex);
54380Sstevel@tonic-gate 	usbdev_ptr = child_ud->usb_dev_descr;
54390Sstevel@tonic-gate 	mutex_exit(&child_ud->usb_mutex);
54400Sstevel@tonic-gate 
54410Sstevel@tonic-gate 	/* try to get pathname for this device */
54420Sstevel@tonic-gate 	tmp_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
54430Sstevel@tonic-gate 	(void) ddi_pathname(child_dip, tmp_path);
54440Sstevel@tonic-gate 
54450Sstevel@tonic-gate 	pathlen = strlen(tmp_path) + 32;
54460Sstevel@tonic-gate 	pathname = kmem_zalloc(pathlen, KM_SLEEP);
54470Sstevel@tonic-gate 
54480Sstevel@tonic-gate 	/*
54490Sstevel@tonic-gate 	 * We haven't initialized the node and it doesn't have an address
54500Sstevel@tonic-gate 	 * yet. Append port number to the physical pathname
54510Sstevel@tonic-gate 	 */
54520Sstevel@tonic-gate 	(void) sprintf(pathname, "%s@%d", tmp_path, port);
54530Sstevel@tonic-gate 
54540Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
54550Sstevel@tonic-gate 	    "hubd_select_device_configuration: Device=%s\n\t"
54560Sstevel@tonic-gate 	    "Child path=%s",
54570Sstevel@tonic-gate 	    usba_get_mfg_prod_sn_str(child_dip, tmp_path, MAXPATHLEN),
54580Sstevel@tonic-gate 	    pathname);
54590Sstevel@tonic-gate 	kmem_free(tmp_path, MAXPATHLEN);
54600Sstevel@tonic-gate 
54610Sstevel@tonic-gate 
54620Sstevel@tonic-gate 	/* database search for user preferences */
54630Sstevel@tonic-gate 	user_pref = usba_devdb_get_user_preferences(usbdev_ptr->idVendor,
54640Sstevel@tonic-gate 	    usbdev_ptr->idProduct, child_ud->usb_serialno_str, pathname);
54650Sstevel@tonic-gate 
54660Sstevel@tonic-gate 	if (user_pref) {
54670Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
54680Sstevel@tonic-gate 		    "hubd_select_device_configuration: "
54690Sstevel@tonic-gate 		    "usba_devdb_get_user_preferences "
54700Sstevel@tonic-gate 		    "return user_conf=%d\npreferred driver=%s path=%s",
54710Sstevel@tonic-gate 		    user_pref->cfg_index, user_pref->driver,
54720Sstevel@tonic-gate 		    user_pref->pathname);
54730Sstevel@tonic-gate 
54740Sstevel@tonic-gate 		user_conf = user_pref->cfg_index;
54750Sstevel@tonic-gate 
54760Sstevel@tonic-gate 		if (user_pref->driver) {
54770Sstevel@tonic-gate 			mutex_enter(&child_ud->usb_mutex);
54780Sstevel@tonic-gate 			child_ud->usb_preferred_driver = user_pref->driver;
54790Sstevel@tonic-gate 			mutex_exit(&child_ud->usb_mutex);
54800Sstevel@tonic-gate 		}
54810Sstevel@tonic-gate 	} else {
54820Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
54830Sstevel@tonic-gate 		    "hubd_select_device_configuration: No match found");
54840Sstevel@tonic-gate 
54850Sstevel@tonic-gate 		/* select default configuration for this device */
54860Sstevel@tonic-gate 		user_conf = USBA_DEV_CONFIG_INDEX_UNDEFINED;
54870Sstevel@tonic-gate 	}
54880Sstevel@tonic-gate 	kmem_free(pathname, pathlen);
54890Sstevel@tonic-gate 
54900Sstevel@tonic-gate 	/* if the device has just one configuration, set default value */
54910Sstevel@tonic-gate 	if (usbdev_ptr->bNumConfigurations == 1) {
54920Sstevel@tonic-gate 		user_conf = USB_DEV_DEFAULT_CONFIG_INDEX;
54930Sstevel@tonic-gate 	}
54940Sstevel@tonic-gate 
54950Sstevel@tonic-gate 	return (user_conf);
54960Sstevel@tonic-gate }
54970Sstevel@tonic-gate 
54980Sstevel@tonic-gate 
54990Sstevel@tonic-gate /*
55000Sstevel@tonic-gate  * Retrieves config cloud for this configuration
55010Sstevel@tonic-gate  */
55020Sstevel@tonic-gate int
hubd_get_this_config_cloud(hubd_t * hubd,dev_info_t * dip,usba_device_t * child_ud,uint16_t conf_index)55030Sstevel@tonic-gate hubd_get_this_config_cloud(hubd_t *hubd, dev_info_t *dip,
55040Sstevel@tonic-gate 	usba_device_t *child_ud, uint16_t conf_index)
55050Sstevel@tonic-gate {
55060Sstevel@tonic-gate 	usb_cfg_descr_t	*confdescr;
55070Sstevel@tonic-gate 	mblk_t		*pdata = NULL;
55080Sstevel@tonic-gate 	int		rval;
55090Sstevel@tonic-gate 	size_t		size;
55100Sstevel@tonic-gate 	char		*tmpbuf;
55110Sstevel@tonic-gate 	usb_cr_t	completion_reason;
55120Sstevel@tonic-gate 	usb_cb_flags_t	cb_flags;
55130Sstevel@tonic-gate 	usb_pipe_handle_t	def_ph;
55140Sstevel@tonic-gate 
55150Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
55160Sstevel@tonic-gate 	    "hubd_get_this_config_cloud: conf_index=%d", conf_index);
55170Sstevel@tonic-gate 
55180Sstevel@tonic-gate 
55190Sstevel@tonic-gate 	/* alloc temporary space for config descriptor */
55200Sstevel@tonic-gate 	confdescr = (usb_cfg_descr_t *)kmem_zalloc(USB_CFG_DESCR_SIZE,
55210Sstevel@tonic-gate 	    KM_SLEEP);
55220Sstevel@tonic-gate 
55230Sstevel@tonic-gate 	/* alloc temporary space for string descriptor */
55240Sstevel@tonic-gate 	tmpbuf = kmem_zalloc(USB_MAXSTRINGLEN, KM_SLEEP);
55250Sstevel@tonic-gate 
55260Sstevel@tonic-gate 	def_ph = usba_get_dflt_pipe_handle(dip);
55270Sstevel@tonic-gate 
55280Sstevel@tonic-gate 	if ((rval = usb_pipe_sync_ctrl_xfer(dip, def_ph,
55290Sstevel@tonic-gate 	    USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
55300Sstevel@tonic-gate 	    USB_REQ_GET_DESCR,
55310Sstevel@tonic-gate 	    USB_DESCR_TYPE_SETUP_CFG | conf_index,
55320Sstevel@tonic-gate 	    0,
55330Sstevel@tonic-gate 	    USB_CFG_DESCR_SIZE,
55340Sstevel@tonic-gate 	    &pdata,
55350Sstevel@tonic-gate 	    0,
55360Sstevel@tonic-gate 	    &completion_reason,
55370Sstevel@tonic-gate 	    &cb_flags,
55380Sstevel@tonic-gate 	    0)) == USB_SUCCESS) {
55390Sstevel@tonic-gate 
55400Sstevel@tonic-gate 		/* this must be true since we didn't allow data underruns */
55417492SZhigang.Lu@Sun.COM 		if (MBLKL(pdata) != USB_CFG_DESCR_SIZE) {
55421001Ssl147100 			USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
55431001Ssl147100 			    "device returned incorrect configuration "
55441001Ssl147100 			    "descriptor size.");
55451001Ssl147100 
55461001Ssl147100 			rval = USB_FAILURE;
55471001Ssl147100 			goto done;
55481001Ssl147100 		}
55490Sstevel@tonic-gate 
55500Sstevel@tonic-gate 		/*
55510Sstevel@tonic-gate 		 * Parse the configuration descriptor
55520Sstevel@tonic-gate 		 */
55530Sstevel@tonic-gate 		size = usb_parse_cfg_descr(pdata->b_rptr,
55547492SZhigang.Lu@Sun.COM 		    MBLKL(pdata), confdescr,
55550Sstevel@tonic-gate 		    USB_CFG_DESCR_SIZE);
55560Sstevel@tonic-gate 
55570Sstevel@tonic-gate 		/* if parse cfg descr error, it should return failure */
55580Sstevel@tonic-gate 		if (size == USB_PARSE_ERROR) {
55590Sstevel@tonic-gate 
55600Sstevel@tonic-gate 			if (pdata->b_rptr[1] != USB_DESCR_TYPE_CFG) {
5561978Sfrits 				USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
55620Sstevel@tonic-gate 				    hubd->h_log_handle,
55630Sstevel@tonic-gate 				    "device returned incorrect "
55640Sstevel@tonic-gate 				    "configuration descriptor type.");
55650Sstevel@tonic-gate 			}
55660Sstevel@tonic-gate 			rval = USB_FAILURE;
55670Sstevel@tonic-gate 			goto done;
55680Sstevel@tonic-gate 		}
55690Sstevel@tonic-gate 
55700Sstevel@tonic-gate 		if (confdescr->wTotalLength < USB_CFG_DESCR_SIZE) {
5571978Sfrits 			USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
55720Sstevel@tonic-gate 			    hubd->h_log_handle,
55730Sstevel@tonic-gate 			    "device returned incorrect "
55740Sstevel@tonic-gate 			    "configuration descriptor size.");
55750Sstevel@tonic-gate 
55760Sstevel@tonic-gate 			rval = USB_FAILURE;
55770Sstevel@tonic-gate 			goto done;
55780Sstevel@tonic-gate 		}
55790Sstevel@tonic-gate 
55800Sstevel@tonic-gate 		freemsg(pdata);
55810Sstevel@tonic-gate 		pdata = NULL;
55820Sstevel@tonic-gate 
55830Sstevel@tonic-gate 		/* Now fetch the complete config cloud */
55840Sstevel@tonic-gate 		if ((rval = usb_pipe_sync_ctrl_xfer(dip, def_ph,
55850Sstevel@tonic-gate 		    USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
55860Sstevel@tonic-gate 		    USB_REQ_GET_DESCR,
55870Sstevel@tonic-gate 		    USB_DESCR_TYPE_SETUP_CFG | conf_index,
55880Sstevel@tonic-gate 		    0,
55890Sstevel@tonic-gate 		    confdescr->wTotalLength,
55900Sstevel@tonic-gate 		    &pdata,
55910Sstevel@tonic-gate 		    0,
55920Sstevel@tonic-gate 		    &completion_reason,
55930Sstevel@tonic-gate 		    &cb_flags,
55940Sstevel@tonic-gate 		    0)) == USB_SUCCESS) {
55950Sstevel@tonic-gate 
55967492SZhigang.Lu@Sun.COM 			if (MBLKL(pdata) !=
55970Sstevel@tonic-gate 			    confdescr->wTotalLength) {
55980Sstevel@tonic-gate 
5599978Sfrits 				USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
56000Sstevel@tonic-gate 				    hubd->h_log_handle,
56010Sstevel@tonic-gate 				    "device returned incorrect "
56020Sstevel@tonic-gate 				    "configuration descriptor.");
56030Sstevel@tonic-gate 
56040Sstevel@tonic-gate 				rval = USB_FAILURE;
56050Sstevel@tonic-gate 				goto done;
56060Sstevel@tonic-gate 			}
56070Sstevel@tonic-gate 
56080Sstevel@tonic-gate 			/*
56090Sstevel@tonic-gate 			 * copy config descriptor into usba_device
56100Sstevel@tonic-gate 			 */
56110Sstevel@tonic-gate 			mutex_enter(&child_ud->usb_mutex);
56120Sstevel@tonic-gate 			child_ud->usb_cfg_array[conf_index] =
56130Sstevel@tonic-gate 			    kmem_alloc(confdescr->wTotalLength, KM_SLEEP);
56140Sstevel@tonic-gate 			child_ud->usb_cfg_array_len[conf_index] =
56154763Slg150142 			    confdescr->wTotalLength;
56160Sstevel@tonic-gate 			bcopy((caddr_t)pdata->b_rptr,
56170Sstevel@tonic-gate 			    (caddr_t)child_ud->usb_cfg_array[conf_index],
56180Sstevel@tonic-gate 			    confdescr->wTotalLength);
56190Sstevel@tonic-gate 			mutex_exit(&child_ud->usb_mutex);
56200Sstevel@tonic-gate 
56210Sstevel@tonic-gate 			/*
56220Sstevel@tonic-gate 			 * retrieve string descriptor describing this
56230Sstevel@tonic-gate 			 * configuration
56240Sstevel@tonic-gate 			 */
56250Sstevel@tonic-gate 			if (confdescr->iConfiguration) {
56260Sstevel@tonic-gate 
56270Sstevel@tonic-gate 				USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG,
56280Sstevel@tonic-gate 				    hubd->h_log_handle,
56290Sstevel@tonic-gate 				    "Get conf str descr for config_index=%d",
56300Sstevel@tonic-gate 				    conf_index);
56310Sstevel@tonic-gate 
56320Sstevel@tonic-gate 				/*
56330Sstevel@tonic-gate 				 * Now fetch the string descriptor describing
56340Sstevel@tonic-gate 				 * this configuration
56350Sstevel@tonic-gate 				 */
56360Sstevel@tonic-gate 				if ((rval = usb_get_string_descr(dip,
56370Sstevel@tonic-gate 				    USB_LANG_ID, confdescr->iConfiguration,
56380Sstevel@tonic-gate 				    tmpbuf, USB_MAXSTRINGLEN)) ==
56390Sstevel@tonic-gate 				    USB_SUCCESS) {
56400Sstevel@tonic-gate 					size = strlen(tmpbuf);
56410Sstevel@tonic-gate 					if (size > 0) {
56420Sstevel@tonic-gate 						child_ud->usb_cfg_str_descr
56430Sstevel@tonic-gate 						    [conf_index] = (char *)
56440Sstevel@tonic-gate 						    kmem_zalloc(size + 1,
56450Sstevel@tonic-gate 						    KM_SLEEP);
56460Sstevel@tonic-gate 						(void) strcpy(
56470Sstevel@tonic-gate 						    child_ud->usb_cfg_str_descr
56480Sstevel@tonic-gate 						    [conf_index], tmpbuf);
56490Sstevel@tonic-gate 					}
56500Sstevel@tonic-gate 				} else {
56510Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
56520Sstevel@tonic-gate 					    hubd->h_log_handle,
56530Sstevel@tonic-gate 					    "hubd_get_this_config_cloud: "
56540Sstevel@tonic-gate 					    "getting config string (%d) "
56550Sstevel@tonic-gate 					    "failed",
56560Sstevel@tonic-gate 					    confdescr->iConfiguration);
56570Sstevel@tonic-gate 
56580Sstevel@tonic-gate 					/* ignore this error */
56590Sstevel@tonic-gate 					rval = USB_SUCCESS;
56600Sstevel@tonic-gate 				}
56610Sstevel@tonic-gate 			}
56620Sstevel@tonic-gate 		}
56630Sstevel@tonic-gate 	}
56640Sstevel@tonic-gate 
56650Sstevel@tonic-gate done:
56660Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
56670Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
56680Sstevel@tonic-gate 		    "hubd_get_this_config_cloud: "
56690Sstevel@tonic-gate 		    "error in retrieving config descriptor for "
56700Sstevel@tonic-gate 		    "config index=%d rval=%d cr=%d",
56710Sstevel@tonic-gate 		    conf_index, rval, completion_reason);
56720Sstevel@tonic-gate 	}
56730Sstevel@tonic-gate 
56740Sstevel@tonic-gate 	if (pdata) {
56750Sstevel@tonic-gate 		freemsg(pdata);
56760Sstevel@tonic-gate 		pdata = NULL;
56770Sstevel@tonic-gate 	}
56780Sstevel@tonic-gate 
56790Sstevel@tonic-gate 	kmem_free(confdescr, USB_CFG_DESCR_SIZE);
56800Sstevel@tonic-gate 	kmem_free(tmpbuf, USB_MAXSTRINGLEN);
56810Sstevel@tonic-gate 
56820Sstevel@tonic-gate 	return (rval);
56830Sstevel@tonic-gate }
56840Sstevel@tonic-gate 
56850Sstevel@tonic-gate 
56860Sstevel@tonic-gate /*
56870Sstevel@tonic-gate  * Retrieves the entire config cloud for all configurations of the device
56880Sstevel@tonic-gate  */
56890Sstevel@tonic-gate int
hubd_get_all_device_config_cloud(hubd_t * hubd,dev_info_t * dip,usba_device_t * child_ud)56900Sstevel@tonic-gate hubd_get_all_device_config_cloud(hubd_t *hubd, dev_info_t *dip,
56910Sstevel@tonic-gate 	usba_device_t *child_ud)
56920Sstevel@tonic-gate {
56930Sstevel@tonic-gate 	int		rval = USB_SUCCESS;
56940Sstevel@tonic-gate 	int		ncfgs;
56950Sstevel@tonic-gate 	uint16_t	size;
56960Sstevel@tonic-gate 	uint16_t	conf_index;
56970Sstevel@tonic-gate 	uchar_t		**cfg_array;
56980Sstevel@tonic-gate 	uint16_t	*cfg_array_len;
56990Sstevel@tonic-gate 	char		**str_descr;
57000Sstevel@tonic-gate 
57010Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
57020Sstevel@tonic-gate 	    "hubd_get_all_device_config_cloud: Start");
57030Sstevel@tonic-gate 
57040Sstevel@tonic-gate 	/* alloc pointer array for conf. descriptors */
57050Sstevel@tonic-gate 	mutex_enter(&child_ud->usb_mutex);
57060Sstevel@tonic-gate 	ncfgs = child_ud->usb_n_cfgs;
57070Sstevel@tonic-gate 	mutex_exit(&child_ud->usb_mutex);
57080Sstevel@tonic-gate 
57090Sstevel@tonic-gate 	size = sizeof (uchar_t *) * ncfgs;
57100Sstevel@tonic-gate 	cfg_array = kmem_zalloc(size, KM_SLEEP);
57110Sstevel@tonic-gate 	cfg_array_len = kmem_zalloc(ncfgs * sizeof (uint16_t), KM_SLEEP);
57120Sstevel@tonic-gate 	str_descr = kmem_zalloc(size, KM_SLEEP);
57130Sstevel@tonic-gate 
57140Sstevel@tonic-gate 	mutex_enter(&child_ud->usb_mutex);
57150Sstevel@tonic-gate 	child_ud->usb_cfg_array = cfg_array;
57160Sstevel@tonic-gate 	child_ud->usb_cfg_array_len = cfg_array_len;
57170Sstevel@tonic-gate 	child_ud->usb_cfg_array_length = size;
57180Sstevel@tonic-gate 	child_ud->usb_cfg_array_len_length = ncfgs * sizeof (uint16_t);
57190Sstevel@tonic-gate 	child_ud->usb_cfg_str_descr = str_descr;
57200Sstevel@tonic-gate 	mutex_exit(&child_ud->usb_mutex);
57210Sstevel@tonic-gate 
57220Sstevel@tonic-gate 	/* Get configuration descriptor for each configuration */
57230Sstevel@tonic-gate 	for (conf_index = 0; (conf_index < ncfgs) &&
57240Sstevel@tonic-gate 	    (rval == USB_SUCCESS); conf_index++) {
57250Sstevel@tonic-gate 
57260Sstevel@tonic-gate 		rval = hubd_get_this_config_cloud(hubd, dip, child_ud,
57270Sstevel@tonic-gate 		    conf_index);
57280Sstevel@tonic-gate 	}
57290Sstevel@tonic-gate 
57300Sstevel@tonic-gate 	return (rval);
57310Sstevel@tonic-gate }
57320Sstevel@tonic-gate 
57330Sstevel@tonic-gate 
57340Sstevel@tonic-gate /*
57350Sstevel@tonic-gate  * hubd_ready_device:
57360Sstevel@tonic-gate  *	Update the usba_device structure
57370Sstevel@tonic-gate  *	Set the given configuration
57380Sstevel@tonic-gate  *	Prepares the device node for driver to online. If an existing
57390Sstevel@tonic-gate  *	OBP node is found, it will switch to the OBP node.
57400Sstevel@tonic-gate  */
57419430SRaymond.Chen@Sun.COM dev_info_t *
hubd_ready_device(hubd_t * hubd,dev_info_t * child_dip,usba_device_t * child_ud,uint_t config_index)57420Sstevel@tonic-gate hubd_ready_device(hubd_t *hubd, dev_info_t *child_dip, usba_device_t *child_ud,
57431001Ssl147100     uint_t config_index)
57440Sstevel@tonic-gate {
57450Sstevel@tonic-gate 	usb_cr_t	completion_reason;
57460Sstevel@tonic-gate 	usb_cb_flags_t	cb_flags;
57470Sstevel@tonic-gate 	size_t		size;
57480Sstevel@tonic-gate 	usb_cfg_descr_t	config_descriptor;
57490Sstevel@tonic-gate 	usb_pipe_handle_t def_ph;
57500Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph;
57510Sstevel@tonic-gate 
57520Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
57536898Sfb209375 	    "hubd_ready_device: dip=0x%p, user_conf_index=%d",
57546898Sfb209375 	    (void *)child_dip, config_index);
57550Sstevel@tonic-gate 
57560Sstevel@tonic-gate 	size = usb_parse_cfg_descr(
57570Sstevel@tonic-gate 	    child_ud->usb_cfg_array[config_index], USB_CFG_DESCR_SIZE,
57580Sstevel@tonic-gate 	    &config_descriptor, USB_CFG_DESCR_SIZE);
57590Sstevel@tonic-gate 	ASSERT(size == USB_CFG_DESCR_SIZE);
57600Sstevel@tonic-gate 
57610Sstevel@tonic-gate 	def_ph = usba_get_dflt_pipe_handle(child_dip);
57620Sstevel@tonic-gate 
57630Sstevel@tonic-gate 	/* Set the configuration */
57640Sstevel@tonic-gate 	(void) usb_pipe_sync_ctrl_xfer(child_dip, def_ph,
57650Sstevel@tonic-gate 	    USB_DEV_REQ_HOST_TO_DEV,
57660Sstevel@tonic-gate 	    USB_REQ_SET_CFG,	/* bRequest */
57670Sstevel@tonic-gate 	    config_descriptor.bConfigurationValue,	/* wValue */
57680Sstevel@tonic-gate 	    0,				/* wIndex */
57690Sstevel@tonic-gate 	    0,				/* wLength */
57700Sstevel@tonic-gate 	    NULL,
57710Sstevel@tonic-gate 	    0,
57720Sstevel@tonic-gate 	    &completion_reason,
57730Sstevel@tonic-gate 	    &cb_flags,
57740Sstevel@tonic-gate 	    0);
57750Sstevel@tonic-gate 
57760Sstevel@tonic-gate 	mutex_enter(&child_ud->usb_mutex);
57770Sstevel@tonic-gate 	child_ud->usb_active_cfg_ndx	= config_index;
57780Sstevel@tonic-gate 	child_ud->usb_cfg		= child_ud->usb_cfg_array[config_index];
57790Sstevel@tonic-gate 	child_ud->usb_cfg_length	= config_descriptor.wTotalLength;
57800Sstevel@tonic-gate 	child_ud->usb_cfg_value 	= config_descriptor.bConfigurationValue;
57810Sstevel@tonic-gate 	child_ud->usb_n_ifs		= config_descriptor.bNumInterfaces;
57820Sstevel@tonic-gate 	child_ud->usb_dip		= child_dip;
57830Sstevel@tonic-gate 
57840Sstevel@tonic-gate 	child_ud->usb_client_flags	= kmem_zalloc(
57854763Slg150142 	    child_ud->usb_n_ifs * USBA_CLIENT_FLAG_SIZE, KM_SLEEP);
57860Sstevel@tonic-gate 
57870Sstevel@tonic-gate 	child_ud->usb_client_attach_list = kmem_zalloc(
57884763Slg150142 	    child_ud->usb_n_ifs *
57894763Slg150142 	    sizeof (*child_ud->usb_client_attach_list), KM_SLEEP);
57900Sstevel@tonic-gate 
57910Sstevel@tonic-gate 	child_ud->usb_client_ev_cb_list = kmem_zalloc(
57924763Slg150142 	    child_ud->usb_n_ifs *
57934763Slg150142 	    sizeof (*child_ud->usb_client_ev_cb_list), KM_SLEEP);
57940Sstevel@tonic-gate 
57950Sstevel@tonic-gate 	mutex_exit(&child_ud->usb_mutex);
57960Sstevel@tonic-gate 
57970Sstevel@tonic-gate 	/* ready the device node */
57980Sstevel@tonic-gate 	child_dip = usba_ready_device_node(child_dip);
57990Sstevel@tonic-gate 
58000Sstevel@tonic-gate 	/* set owner of default pipe to child dip */
58010Sstevel@tonic-gate 	ph = usba_get_ph_data(def_ph);
58020Sstevel@tonic-gate 	mutex_enter(&ph->p_mutex);
58030Sstevel@tonic-gate 	mutex_enter(&ph->p_ph_impl->usba_ph_mutex);
58040Sstevel@tonic-gate 	ph->p_ph_impl->usba_ph_dip = ph->p_dip = child_dip;
58050Sstevel@tonic-gate 	mutex_exit(&ph->p_ph_impl->usba_ph_mutex);
58060Sstevel@tonic-gate 	mutex_exit(&ph->p_mutex);
58070Sstevel@tonic-gate 
58080Sstevel@tonic-gate 	return (child_dip);
58090Sstevel@tonic-gate }
58100Sstevel@tonic-gate 
58110Sstevel@tonic-gate 
58120Sstevel@tonic-gate /*
58130Sstevel@tonic-gate  * hubd_create_child
58140Sstevel@tonic-gate  *	- create child dip
58150Sstevel@tonic-gate  *	- open default pipe
58160Sstevel@tonic-gate  *	- get device descriptor
58170Sstevel@tonic-gate  *	- set the address
58180Sstevel@tonic-gate  *	- get device string descriptors
58190Sstevel@tonic-gate  *	- get the entire config cloud (all configurations) of the device
58200Sstevel@tonic-gate  *	- set user preferred configuration
58210Sstevel@tonic-gate  *	- close default pipe
58220Sstevel@tonic-gate  *	- load appropriate driver(s)
58230Sstevel@tonic-gate  */
58240Sstevel@tonic-gate static int
hubd_create_child(dev_info_t * dip,hubd_t * hubd,usba_device_t * hubd_ud,usb_port_status_t port_status,usb_port_t port,int iteration)58250Sstevel@tonic-gate hubd_create_child(dev_info_t *dip,
58260Sstevel@tonic-gate 		hubd_t		*hubd,
58270Sstevel@tonic-gate 		usba_device_t	*hubd_ud,
58280Sstevel@tonic-gate 		usb_port_status_t port_status,
58290Sstevel@tonic-gate 		usb_port_t	port,
58300Sstevel@tonic-gate 		int		iteration)
58310Sstevel@tonic-gate {
58320Sstevel@tonic-gate 	dev_info_t		*child_dip = NULL;
58330Sstevel@tonic-gate 	usb_dev_descr_t	usb_dev_descr;
58340Sstevel@tonic-gate 	int			rval;
58350Sstevel@tonic-gate 	usba_device_t		*child_ud = NULL;
58360Sstevel@tonic-gate 	usba_device_t		*parent_ud = NULL;
58370Sstevel@tonic-gate 	usb_pipe_handle_t	ph = NULL; /* default pipe handle */
58380Sstevel@tonic-gate 	mblk_t			*pdata = NULL;
58390Sstevel@tonic-gate 	usb_cr_t		completion_reason;
58401001Ssl147100 	int			user_conf_index;
58411001Ssl147100 	uint_t			config_index;
58420Sstevel@tonic-gate 	usb_cb_flags_t		cb_flags;
58430Sstevel@tonic-gate 	uchar_t			address = 0;
58440Sstevel@tonic-gate 	uint16_t		length;
58450Sstevel@tonic-gate 	size_t			size;
58460Sstevel@tonic-gate 	usb_addr_t		parent_usb_addr;
58470Sstevel@tonic-gate 	usb_port_t		parent_usb_port;
58480Sstevel@tonic-gate 	usba_device_t		*parent_usba_dev;
58490Sstevel@tonic-gate 	usb_port_status_t	parent_port_status;
58500Sstevel@tonic-gate 
58510Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
58520Sstevel@tonic-gate 	    "hubd_create_child: port=%d", port);
58530Sstevel@tonic-gate 
58540Sstevel@tonic-gate 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
58550Sstevel@tonic-gate 	ASSERT(hubd->h_usba_devices[port] == NULL);
58560Sstevel@tonic-gate 
58570Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
58580Sstevel@tonic-gate 
58590Sstevel@tonic-gate 	/*
58600Sstevel@tonic-gate 	 * create a dip which can be used to open the pipe. we set
58610Sstevel@tonic-gate 	 * the name after getting the descriptors from the device
58620Sstevel@tonic-gate 	 */
58630Sstevel@tonic-gate 	rval = usba_create_child_devi(dip,
58644763Slg150142 	    "device",		/* driver name */
58654763Slg150142 	    hubd_ud->usb_hcdi_ops, /* usba_hcdi ops */
58664763Slg150142 	    hubd_ud->usb_root_hub_dip,
58674763Slg150142 	    port_status,		/* low speed device */
58684763Slg150142 	    child_ud,
58694763Slg150142 	    &child_dip);
58700Sstevel@tonic-gate 
58710Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
58720Sstevel@tonic-gate 
5873978Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
58740Sstevel@tonic-gate 		    "usb_create_child_devi failed (%d)", rval);
58750Sstevel@tonic-gate 
58760Sstevel@tonic-gate 		goto fail_cleanup;
58770Sstevel@tonic-gate 	}
58780Sstevel@tonic-gate 
58790Sstevel@tonic-gate 	child_ud = usba_get_usba_device(child_dip);
58800Sstevel@tonic-gate 	ASSERT(child_ud != NULL);
58810Sstevel@tonic-gate 
58820Sstevel@tonic-gate 	parent_ud = hubd->h_usba_device;
58830Sstevel@tonic-gate 	mutex_enter(&parent_ud->usb_mutex);
58840Sstevel@tonic-gate 	parent_port_status = parent_ud->usb_port_status;
58850Sstevel@tonic-gate 
58860Sstevel@tonic-gate 	/*
58870Sstevel@tonic-gate 	 * To support split transactions, update address and port
58880Sstevel@tonic-gate 	 * of high speed hub to which given device is connected.
58890Sstevel@tonic-gate 	 */
58900Sstevel@tonic-gate 	if (parent_port_status == USBA_HIGH_SPEED_DEV) {
58910Sstevel@tonic-gate 		parent_usba_dev = parent_ud;
58920Sstevel@tonic-gate 		parent_usb_addr = parent_ud->usb_addr;
58930Sstevel@tonic-gate 		parent_usb_port = port;
58940Sstevel@tonic-gate 	} else {
58950Sstevel@tonic-gate 		parent_usba_dev = parent_ud->usb_hs_hub_usba_dev;
58960Sstevel@tonic-gate 		parent_usb_addr = parent_ud->usb_hs_hub_addr;
58970Sstevel@tonic-gate 		parent_usb_port = parent_ud->usb_hs_hub_port;
58980Sstevel@tonic-gate 	}
58990Sstevel@tonic-gate 	mutex_exit(&parent_ud->usb_mutex);
59000Sstevel@tonic-gate 
59010Sstevel@tonic-gate 	mutex_enter(&child_ud->usb_mutex);
59020Sstevel@tonic-gate 	address = child_ud->usb_addr;
59030Sstevel@tonic-gate 	child_ud->usb_addr = 0;
59040Sstevel@tonic-gate 	child_ud->usb_dev_descr = kmem_alloc(sizeof (usb_dev_descr_t),
59050Sstevel@tonic-gate 	    KM_SLEEP);
59060Sstevel@tonic-gate 	bzero(&usb_dev_descr, sizeof (usb_dev_descr_t));
59070Sstevel@tonic-gate 	usb_dev_descr.bMaxPacketSize0 =
59080Sstevel@tonic-gate 	    (port_status == USBA_LOW_SPEED_DEV) ? 8 : 64;
59090Sstevel@tonic-gate 	bcopy(&usb_dev_descr, child_ud->usb_dev_descr,
59100Sstevel@tonic-gate 	    sizeof (usb_dev_descr_t));
59110Sstevel@tonic-gate 	child_ud->usb_port = port;
59120Sstevel@tonic-gate 	child_ud->usb_hs_hub_usba_dev = parent_usba_dev;
59130Sstevel@tonic-gate 	child_ud->usb_hs_hub_addr = parent_usb_addr;
59140Sstevel@tonic-gate 	child_ud->usb_hs_hub_port = parent_usb_port;
59150Sstevel@tonic-gate 	mutex_exit(&child_ud->usb_mutex);
59160Sstevel@tonic-gate 
59170Sstevel@tonic-gate 	/* Open the default pipe */
59180Sstevel@tonic-gate 	if ((rval = usb_pipe_open(child_dip, NULL, NULL,
59190Sstevel@tonic-gate 	    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) != USB_SUCCESS) {
5920978Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5921978Sfrits 		    "usb_pipe_open failed (%d)", rval);
59220Sstevel@tonic-gate 
59230Sstevel@tonic-gate 		goto fail_cleanup;
59240Sstevel@tonic-gate 	}
59250Sstevel@tonic-gate 
59260Sstevel@tonic-gate 	/*
59270Sstevel@tonic-gate 	 * get device descriptor
59280Sstevel@tonic-gate 	 */
59290Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
59300Sstevel@tonic-gate 	    "hubd_create_child: get device descriptor: 64 bytes");
59310Sstevel@tonic-gate 
59320Sstevel@tonic-gate 	rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
59330Sstevel@tonic-gate 	    USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
59340Sstevel@tonic-gate 	    USB_REQ_GET_DESCR,			/* bRequest */
59350Sstevel@tonic-gate 	    USB_DESCR_TYPE_SETUP_DEV,		/* wValue */
59360Sstevel@tonic-gate 	    0,					/* wIndex */
59370Sstevel@tonic-gate 	    64,					/* wLength */
59380Sstevel@tonic-gate 	    &pdata, USB_ATTRS_SHORT_XFER_OK,
59390Sstevel@tonic-gate 	    &completion_reason, &cb_flags, 0);
59400Sstevel@tonic-gate 
59410Sstevel@tonic-gate 	if ((rval != USB_SUCCESS) &&
59420Sstevel@tonic-gate 	    (!((completion_reason == USB_CR_DATA_OVERRUN) && pdata))) {
59430Sstevel@tonic-gate 
59440Sstevel@tonic-gate 		/*
59450Sstevel@tonic-gate 		 * rval != USB_SUCCESS AND
59460Sstevel@tonic-gate 		 * completion_reason != USB_CR_DATA_OVERRUN
59470Sstevel@tonic-gate 		 * pdata could be != NULL.
59480Sstevel@tonic-gate 		 * Free pdata now to prevent memory leak.
59490Sstevel@tonic-gate 		 */
59500Sstevel@tonic-gate 		freemsg(pdata);
59510Sstevel@tonic-gate 		pdata = NULL;
59520Sstevel@tonic-gate 
59530Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
59540Sstevel@tonic-gate 		    "hubd_create_child: get device descriptor: 8 bytes");
59550Sstevel@tonic-gate 
59560Sstevel@tonic-gate 		rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
59570Sstevel@tonic-gate 		    USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
59580Sstevel@tonic-gate 		    USB_REQ_GET_DESCR,			/* bRequest */
59590Sstevel@tonic-gate 		    USB_DESCR_TYPE_SETUP_DEV,		/* wValue */
59600Sstevel@tonic-gate 		    0,					/* wIndex */
59610Sstevel@tonic-gate 		    8,					/* wLength */
59620Sstevel@tonic-gate 		    &pdata, USB_ATTRS_NONE,
59630Sstevel@tonic-gate 		    &completion_reason, &cb_flags, 0);
59640Sstevel@tonic-gate 
59650Sstevel@tonic-gate 		if (rval != USB_SUCCESS) {
5966978Sfrits 			USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
59670Sstevel@tonic-gate 			    "getting device descriptor failed (%s 0x%x %d)",
59680Sstevel@tonic-gate 			    usb_str_cr(completion_reason), cb_flags, rval);
59690Sstevel@tonic-gate 			goto fail_cleanup;
59700Sstevel@tonic-gate 		}
59710Sstevel@tonic-gate 	} else {
59720Sstevel@tonic-gate 		ASSERT(completion_reason == USB_CR_OK);
59730Sstevel@tonic-gate 	}
59740Sstevel@tonic-gate 
59750Sstevel@tonic-gate 	ASSERT(pdata != NULL);
59760Sstevel@tonic-gate 
59770Sstevel@tonic-gate 	size = usb_parse_dev_descr(
59784763Slg150142 	    pdata->b_rptr,
59797492SZhigang.Lu@Sun.COM 	    MBLKL(pdata),
59804763Slg150142 	    &usb_dev_descr,
59814763Slg150142 	    sizeof (usb_dev_descr_t));
59820Sstevel@tonic-gate 
59830Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
59840Sstevel@tonic-gate 	    "parsing device descriptor returned %lu", size);
59850Sstevel@tonic-gate 
59860Sstevel@tonic-gate 	length = *(pdata->b_rptr);
59870Sstevel@tonic-gate 	freemsg(pdata);
59880Sstevel@tonic-gate 	pdata = NULL;
59890Sstevel@tonic-gate 	if (size < 8) {
5990978Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
59910Sstevel@tonic-gate 		    "get device descriptor returned %lu bytes", size);
59920Sstevel@tonic-gate 
59930Sstevel@tonic-gate 		goto fail_cleanup;
59940Sstevel@tonic-gate 	}
59950Sstevel@tonic-gate 
59960Sstevel@tonic-gate 	if (length < 8) {
5997978Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
59980Sstevel@tonic-gate 		    "fail enumeration: bLength=%d", length);
59990Sstevel@tonic-gate 
60000Sstevel@tonic-gate 		goto fail_cleanup;
60010Sstevel@tonic-gate 	}
60020Sstevel@tonic-gate 
60030Sstevel@tonic-gate 	/* Set the address of the device */
60040Sstevel@tonic-gate 	if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
60050Sstevel@tonic-gate 	    USB_DEV_REQ_HOST_TO_DEV,
60060Sstevel@tonic-gate 	    USB_REQ_SET_ADDRESS,	/* bRequest */
60070Sstevel@tonic-gate 	    address,			/* wValue */
60080Sstevel@tonic-gate 	    0,				/* wIndex */
60090Sstevel@tonic-gate 	    0,				/* wLength */
60100Sstevel@tonic-gate 	    NULL, 0,
60110Sstevel@tonic-gate 	    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
60120Sstevel@tonic-gate 		char buffer[64];
6013978Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
60140Sstevel@tonic-gate 		    "setting address failed (cr=%s cb_flags=%s rval=%d)",
60150Sstevel@tonic-gate 		    usb_str_cr(completion_reason),
60160Sstevel@tonic-gate 		    usb_str_cb_flags(cb_flags, buffer, sizeof (buffer)),
60170Sstevel@tonic-gate 		    rval);
60180Sstevel@tonic-gate 
60190Sstevel@tonic-gate 		goto fail_cleanup;
60200Sstevel@tonic-gate 	}
60210Sstevel@tonic-gate 
60220Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
60230Sstevel@tonic-gate 	    "set address 0x%x done", address);
60240Sstevel@tonic-gate 
60250Sstevel@tonic-gate 	/* now close the pipe for addr 0 */
60260Sstevel@tonic-gate 	usb_pipe_close(child_dip, ph,
60270Sstevel@tonic-gate 	    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
60280Sstevel@tonic-gate 
60290Sstevel@tonic-gate 	/*
60300Sstevel@tonic-gate 	 * This delay is important for the CATC hub to enumerate
60310Sstevel@tonic-gate 	 * But, avoid delay in the first iteration
60320Sstevel@tonic-gate 	 */
60330Sstevel@tonic-gate 	if (iteration) {
60340Sstevel@tonic-gate 		delay(drv_usectohz(hubd_device_delay/100));
60350Sstevel@tonic-gate 	}
60360Sstevel@tonic-gate 
60370Sstevel@tonic-gate 	/* assign the address in the usba_device structure */
60380Sstevel@tonic-gate 	mutex_enter(&child_ud->usb_mutex);
60390Sstevel@tonic-gate 	child_ud->usb_addr = address;
60400Sstevel@tonic-gate 	child_ud->usb_no_cpr = 0;
60410Sstevel@tonic-gate 	child_ud->usb_port_status = port_status;
60420Sstevel@tonic-gate 	/* save this device descriptor */
60430Sstevel@tonic-gate 	bcopy(&usb_dev_descr, child_ud->usb_dev_descr,
60444763Slg150142 	    sizeof (usb_dev_descr_t));
60450Sstevel@tonic-gate 	child_ud->usb_n_cfgs = usb_dev_descr.bNumConfigurations;
60460Sstevel@tonic-gate 	mutex_exit(&child_ud->usb_mutex);
60470Sstevel@tonic-gate 
60480Sstevel@tonic-gate 	/* re-open the pipe for the device with the new address */
60490Sstevel@tonic-gate 	if ((rval = usb_pipe_open(child_dip, NULL, NULL,
60500Sstevel@tonic-gate 	    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) != USB_SUCCESS) {
6051978Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
60520Sstevel@tonic-gate 		    "usb_pipe_open failed (%d)", rval);
60530Sstevel@tonic-gate 
60540Sstevel@tonic-gate 		goto fail_cleanup;
60550Sstevel@tonic-gate 	}
60560Sstevel@tonic-gate 
60570Sstevel@tonic-gate 	/*
60580Sstevel@tonic-gate 	 * Get full device descriptor only if we have not received full
60590Sstevel@tonic-gate 	 * device descriptor earlier.
60600Sstevel@tonic-gate 	 */
60610Sstevel@tonic-gate 	if (size < length) {
60620Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
60630Sstevel@tonic-gate 		    "hubd_create_child: get full device descriptor: "
60640Sstevel@tonic-gate 		    "%d bytes", length);
60650Sstevel@tonic-gate 
60660Sstevel@tonic-gate 		if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
60670Sstevel@tonic-gate 		    USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
60680Sstevel@tonic-gate 		    USB_REQ_GET_DESCR,			/* bRequest */
60690Sstevel@tonic-gate 		    USB_DESCR_TYPE_SETUP_DEV,		/* wValue */
60700Sstevel@tonic-gate 		    0,					/* wIndex */
60710Sstevel@tonic-gate 		    length,				/* wLength */
60720Sstevel@tonic-gate 		    &pdata, 0,
60730Sstevel@tonic-gate 		    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
60740Sstevel@tonic-gate 			freemsg(pdata);
60750Sstevel@tonic-gate 			pdata = NULL;
60760Sstevel@tonic-gate 
60770Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG,
60780Sstevel@tonic-gate 			    hubd->h_log_handle,
60790Sstevel@tonic-gate 			    "hubd_create_child: get full device descriptor: "
60800Sstevel@tonic-gate 			    "64 bytes");
60810Sstevel@tonic-gate 
60820Sstevel@tonic-gate 			rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
60830Sstevel@tonic-gate 			    USB_DEV_REQ_DEV_TO_HOST |
60840Sstevel@tonic-gate 			    USB_DEV_REQ_TYPE_STANDARD,
60850Sstevel@tonic-gate 			    USB_REQ_GET_DESCR,		/* bRequest */
60860Sstevel@tonic-gate 			    USB_DESCR_TYPE_SETUP_DEV,	/* wValue */
60870Sstevel@tonic-gate 			    0,				/* wIndex */
60880Sstevel@tonic-gate 			    64,				/* wLength */
60890Sstevel@tonic-gate 			    &pdata, USB_ATTRS_SHORT_XFER_OK,
60900Sstevel@tonic-gate 			    &completion_reason, &cb_flags, 0);
60910Sstevel@tonic-gate 
60920Sstevel@tonic-gate 			/* we have to trust the data now */
60930Sstevel@tonic-gate 			if (pdata) {
60940Sstevel@tonic-gate 				int len = *(pdata->b_rptr);
60950Sstevel@tonic-gate 
60967492SZhigang.Lu@Sun.COM 				length = MBLKL(pdata);
60970Sstevel@tonic-gate 				if (length < len) {
60980Sstevel@tonic-gate 
60990Sstevel@tonic-gate 					goto fail_cleanup;
61000Sstevel@tonic-gate 				}
61010Sstevel@tonic-gate 			} else if (rval != USB_SUCCESS) {
6102978Sfrits 				USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
61030Sstevel@tonic-gate 				    hubd->h_log_handle,
61040Sstevel@tonic-gate 				    "getting device descriptor failed "
61050Sstevel@tonic-gate 				    "(%d 0x%x %d)",
61064763Slg150142 				    completion_reason, cb_flags, rval);
61070Sstevel@tonic-gate 
61080Sstevel@tonic-gate 				goto fail_cleanup;
61090Sstevel@tonic-gate 			}
61100Sstevel@tonic-gate 		}
61110Sstevel@tonic-gate 
61120Sstevel@tonic-gate 		size = usb_parse_dev_descr(
61134763Slg150142 		    pdata->b_rptr,
61147492SZhigang.Lu@Sun.COM 		    MBLKL(pdata),
61154763Slg150142 		    &usb_dev_descr,
61164763Slg150142 		    sizeof (usb_dev_descr_t));
61170Sstevel@tonic-gate 
61180Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
61190Sstevel@tonic-gate 		    "parsing device descriptor returned %lu", size);
61200Sstevel@tonic-gate 
61210Sstevel@tonic-gate 		/*
61220Sstevel@tonic-gate 		 * For now, free the data
61230Sstevel@tonic-gate 		 * eventually, each configuration may need to be looked at
61240Sstevel@tonic-gate 		 */
61250Sstevel@tonic-gate 		freemsg(pdata);
61260Sstevel@tonic-gate 		pdata = NULL;
61270Sstevel@tonic-gate 
61280Sstevel@tonic-gate 		if (size != USB_DEV_DESCR_SIZE) {
6129978Sfrits 			USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
61300Sstevel@tonic-gate 			    "fail enumeration: descriptor size=%lu "
61310Sstevel@tonic-gate 			    "expected size=%u", size, USB_DEV_DESCR_SIZE);
61320Sstevel@tonic-gate 
61330Sstevel@tonic-gate 			goto fail_cleanup;
61340Sstevel@tonic-gate 		}
61350Sstevel@tonic-gate 
61360Sstevel@tonic-gate 		/*
61370Sstevel@tonic-gate 		 * save the device descriptor in usba_device since it is needed
61380Sstevel@tonic-gate 		 * later on again
61390Sstevel@tonic-gate 		 */
61400Sstevel@tonic-gate 		mutex_enter(&child_ud->usb_mutex);
61410Sstevel@tonic-gate 		bcopy(&usb_dev_descr, child_ud->usb_dev_descr,
61424763Slg150142 		    sizeof (usb_dev_descr_t));
61430Sstevel@tonic-gate 		child_ud->usb_n_cfgs = usb_dev_descr.bNumConfigurations;
61440Sstevel@tonic-gate 		mutex_exit(&child_ud->usb_mutex);
61450Sstevel@tonic-gate 	}
61460Sstevel@tonic-gate 
61470Sstevel@tonic-gate 	if (usb_dev_descr.bNumConfigurations == 0) {
6148978Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
61490Sstevel@tonic-gate 		    "device descriptor:\n\t"
61500Sstevel@tonic-gate 		    "l=0x%x type=0x%x USB=0x%x class=0x%x subclass=0x%x\n\t"
61510Sstevel@tonic-gate 		    "protocol=0x%x maxpktsize=0x%x "
61520Sstevel@tonic-gate 		    "Vid=0x%x Pid=0x%x rel=0x%x\n\t"
61530Sstevel@tonic-gate 		    "Mfg=0x%x P=0x%x sn=0x%x #config=0x%x",
61540Sstevel@tonic-gate 		    usb_dev_descr.bLength, usb_dev_descr.bDescriptorType,
61550Sstevel@tonic-gate 		    usb_dev_descr.bcdUSB, usb_dev_descr.bDeviceClass,
61560Sstevel@tonic-gate 		    usb_dev_descr.bDeviceSubClass,
61570Sstevel@tonic-gate 		    usb_dev_descr.bDeviceProtocol,
61580Sstevel@tonic-gate 		    usb_dev_descr.bMaxPacketSize0,
61590Sstevel@tonic-gate 		    usb_dev_descr.idVendor,
61600Sstevel@tonic-gate 		    usb_dev_descr.idProduct, usb_dev_descr.bcdDevice,
61610Sstevel@tonic-gate 		    usb_dev_descr.iManufacturer, usb_dev_descr.iProduct,
61620Sstevel@tonic-gate 		    usb_dev_descr.iSerialNumber,
61630Sstevel@tonic-gate 		    usb_dev_descr.bNumConfigurations);
61640Sstevel@tonic-gate 		goto fail_cleanup;
61650Sstevel@tonic-gate 	}
61660Sstevel@tonic-gate 
61670Sstevel@tonic-gate 
61680Sstevel@tonic-gate 	/* get the device string descriptor(s) */
61690Sstevel@tonic-gate 	usba_get_dev_string_descrs(child_dip, child_ud);
61700Sstevel@tonic-gate 
61710Sstevel@tonic-gate 	/* retrieve config cloud for all configurations */
61720Sstevel@tonic-gate 	rval = hubd_get_all_device_config_cloud(hubd, child_dip, child_ud);
61730Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
6174978Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
61750Sstevel@tonic-gate 		    "failed to get configuration descriptor(s)");
61760Sstevel@tonic-gate 
61770Sstevel@tonic-gate 		goto fail_cleanup;
61780Sstevel@tonic-gate 	}
61790Sstevel@tonic-gate 
61800Sstevel@tonic-gate 	/* get the preferred configuration for this device */
61810Sstevel@tonic-gate 	user_conf_index = hubd_select_device_configuration(hubd, port,
61820Sstevel@tonic-gate 	    child_dip, child_ud);
61830Sstevel@tonic-gate 
61840Sstevel@tonic-gate 	/* Check if the user selected configuration index is in range */
61851001Ssl147100 	if ((user_conf_index >= usb_dev_descr.bNumConfigurations) ||
61861001Ssl147100 	    (user_conf_index < 0)) {
6187978Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
61880Sstevel@tonic-gate 		    "Configuration index for device idVendor=%d "
61890Sstevel@tonic-gate 		    "idProduct=%d is=%d, and is out of range[0..%d]",
61900Sstevel@tonic-gate 		    usb_dev_descr.idVendor, usb_dev_descr.idProduct,
61910Sstevel@tonic-gate 		    user_conf_index, usb_dev_descr.bNumConfigurations - 1);
61920Sstevel@tonic-gate 
61930Sstevel@tonic-gate 		/* treat this as user didn't specify configuration */
61940Sstevel@tonic-gate 		user_conf_index = USBA_DEV_CONFIG_INDEX_UNDEFINED;
61950Sstevel@tonic-gate 	}
61960Sstevel@tonic-gate 
61970Sstevel@tonic-gate 
61980Sstevel@tonic-gate 	/*
61990Sstevel@tonic-gate 	 * Warn users of a performance hit if connecting a
62000Sstevel@tonic-gate 	 * High Speed behind a 1.1 hub, which is behind a
62010Sstevel@tonic-gate 	 * 2.0 port.
62020Sstevel@tonic-gate 	 */
62030Sstevel@tonic-gate 	if ((parent_port_status != USBA_HIGH_SPEED_DEV) &&
62040Sstevel@tonic-gate 	    !(usba_is_root_hub(parent_ud->usb_dip)) &&
62050Sstevel@tonic-gate 	    (parent_usb_addr)) {
62060Sstevel@tonic-gate 
62070Sstevel@tonic-gate 		/*
62080Sstevel@tonic-gate 		 * Now that we know the root port is a high speed port
62090Sstevel@tonic-gate 		 * and that the parent port is not a high speed port,
62100Sstevel@tonic-gate 		 * let's find out if the device itself is a high speed
62110Sstevel@tonic-gate 		 * device.  If it is a high speed device,
62120Sstevel@tonic-gate 		 * USB_DESCR_TYPE_SETUP_DEV_QLF should return a value,
62130Sstevel@tonic-gate 		 * otherwise the command will fail.
62140Sstevel@tonic-gate 		 */
62150Sstevel@tonic-gate 		rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
62160Sstevel@tonic-gate 		    USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
62170Sstevel@tonic-gate 		    USB_REQ_GET_DESCR,			/* bRequest */
62180Sstevel@tonic-gate 		    USB_DESCR_TYPE_SETUP_DEV_QLF,	/* wValue */
62190Sstevel@tonic-gate 		    0,					/* wIndex */
62200Sstevel@tonic-gate 		    10,					/* wLength */
62210Sstevel@tonic-gate 		    &pdata, USB_ATTRS_SHORT_XFER_OK,
62220Sstevel@tonic-gate 		    &completion_reason, &cb_flags, 0);
62230Sstevel@tonic-gate 
62240Sstevel@tonic-gate 		if (pdata) {
62250Sstevel@tonic-gate 			freemsg(pdata);
62260Sstevel@tonic-gate 			pdata = NULL;
62270Sstevel@tonic-gate 		}
62280Sstevel@tonic-gate 
62290Sstevel@tonic-gate 		/*
62300Sstevel@tonic-gate 		 * USB_DESCR_TYPE_SETUP_DEV_QLF query was successful
62310Sstevel@tonic-gate 		 * that means this is a high speed device behind a
62320Sstevel@tonic-gate 		 * high speed root hub, but running at full speed
62330Sstevel@tonic-gate 		 * because there is a full speed hub in the middle.
62340Sstevel@tonic-gate 		 */
62350Sstevel@tonic-gate 		if (rval == USB_SUCCESS) {
62364763Slg150142 			USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG,
62374763Slg150142 			    hubd->h_log_handle,
62384763Slg150142 			    "Connecting a high speed device to a "
62394763Slg150142 			    "non high speed hub (port %d) will result "
62406112Sqz150045 			    "in a loss of performance.	Please connect "
62414763Slg150142 			    "the device to a high speed hub to get "
62424763Slg150142 			    "the maximum performance.",
62434763Slg150142 			    port);
62440Sstevel@tonic-gate 		}
62450Sstevel@tonic-gate 	}
62460Sstevel@tonic-gate 
62470Sstevel@tonic-gate 	/*
62480Sstevel@tonic-gate 	 * Now we try to online the device by attaching a driver
62490Sstevel@tonic-gate 	 * The following truth table illustrates the logic:-
62500Sstevel@tonic-gate 	 * Cfgndx	Driver	Action
62510Sstevel@tonic-gate 	 * 0		0	loop all configs for driver with full
62520Sstevel@tonic-gate 	 *			compatible properties.
62530Sstevel@tonic-gate 	 * 0		1	set first configuration,
62540Sstevel@tonic-gate 	 *			compatible prop = drivername.
62550Sstevel@tonic-gate 	 * 1		0	Set config, full compatible prop
62560Sstevel@tonic-gate 	 * 1		1	Set config, compatible prop = drivername.
62570Sstevel@tonic-gate 	 *
62580Sstevel@tonic-gate 	 * Note:
62590Sstevel@tonic-gate 	 *	cfgndx = user_conf_index
62600Sstevel@tonic-gate 	 *	Driver = usb_preferred_driver
62610Sstevel@tonic-gate 	 */
62620Sstevel@tonic-gate 	if (user_conf_index == USBA_DEV_CONFIG_INDEX_UNDEFINED) {
62630Sstevel@tonic-gate 		if (child_ud->usb_preferred_driver) {
62640Sstevel@tonic-gate 			/*
62650Sstevel@tonic-gate 			 * It is the job of the "preferred driver" to put the
62660Sstevel@tonic-gate 			 * device in the desired configuration. Till then
62670Sstevel@tonic-gate 			 * put the device in config index 0.
62680Sstevel@tonic-gate 			 */
62691001Ssl147100 			if ((rval = usba_hubdi_check_power_budget(dip, child_ud,
62701001Ssl147100 			    USB_DEV_DEFAULT_CONFIG_INDEX)) != USB_SUCCESS) {
62711001Ssl147100 
62721001Ssl147100 				goto fail_cleanup;
62731001Ssl147100 			}
62741001Ssl147100 
62750Sstevel@tonic-gate 			child_dip = hubd_ready_device(hubd, child_dip,
62760Sstevel@tonic-gate 			    child_ud, USB_DEV_DEFAULT_CONFIG_INDEX);
62770Sstevel@tonic-gate 
62780Sstevel@tonic-gate 			/*
62790Sstevel@tonic-gate 			 * Assign the dip before onlining to avoid race
62800Sstevel@tonic-gate 			 * with busctl
62810Sstevel@tonic-gate 			 */
62820Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
62830Sstevel@tonic-gate 			hubd->h_children_dips[port] = child_dip;
62840Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
62850Sstevel@tonic-gate 
62860Sstevel@tonic-gate 			(void) usba_bind_driver(child_dip);
62870Sstevel@tonic-gate 		} else {
62880Sstevel@tonic-gate 			/*
62890Sstevel@tonic-gate 			 * loop through all the configurations to see if we
62900Sstevel@tonic-gate 			 * can find a driver for any one config. If not, set
62910Sstevel@tonic-gate 			 * the device in config_index 0
62920Sstevel@tonic-gate 			 */
62930Sstevel@tonic-gate 			rval = USB_FAILURE;
62940Sstevel@tonic-gate 			for (config_index = 0;
62950Sstevel@tonic-gate 			    (config_index < usb_dev_descr.bNumConfigurations) &&
62960Sstevel@tonic-gate 			    (rval != USB_SUCCESS); config_index++) {
62970Sstevel@tonic-gate 
62980Sstevel@tonic-gate 				child_dip = hubd_ready_device(hubd, child_dip,
62990Sstevel@tonic-gate 				    child_ud, config_index);
63000Sstevel@tonic-gate 
63010Sstevel@tonic-gate 				/*
63020Sstevel@tonic-gate 				 * Assign the dip before onlining to avoid race
63030Sstevel@tonic-gate 				 * with busctl
63040Sstevel@tonic-gate 				 */
63050Sstevel@tonic-gate 				mutex_enter(HUBD_MUTEX(hubd));
63060Sstevel@tonic-gate 				hubd->h_children_dips[port] = child_dip;
63070Sstevel@tonic-gate 				mutex_exit(HUBD_MUTEX(hubd));
63080Sstevel@tonic-gate 
63090Sstevel@tonic-gate 				rval = usba_bind_driver(child_dip);
63101001Ssl147100 
63111001Ssl147100 				/*
63121001Ssl147100 				 * Normally power budget should be checked
63131001Ssl147100 				 * before device is configured. A failure in
63141001Ssl147100 				 * power budget checking will stop the device
63151001Ssl147100 				 * from being configured with current
63161001Ssl147100 				 * config_index and may enable the device to
63171001Ssl147100 				 * be configured in another configuration.
63181001Ssl147100 				 * This may break the user experience that a
63191001Ssl147100 				 * device which previously worked in config
63201001Ssl147100 				 * A now works in config B after power budget
63211001Ssl147100 				 * control is enabled. To avoid such situation,
63221001Ssl147100 				 * power budget checking is moved here and will
63231001Ssl147100 				 * fail the child creation directly if config
63241001Ssl147100 				 * A exceeds the power available.
63251001Ssl147100 				 */
63261001Ssl147100 				if (rval == USB_SUCCESS) {
63271001Ssl147100 					if ((usba_hubdi_check_power_budget(dip,
63281001Ssl147100 					    child_ud, config_index)) !=
63291001Ssl147100 					    USB_SUCCESS) {
63301001Ssl147100 
63311001Ssl147100 						goto fail_cleanup;
63321001Ssl147100 					}
63331001Ssl147100 				}
63340Sstevel@tonic-gate 			}
63350Sstevel@tonic-gate 			if (rval != USB_SUCCESS) {
63361001Ssl147100 
63371001Ssl147100 				if ((usba_hubdi_check_power_budget(dip,
63381001Ssl147100 				    child_ud, 0)) != USB_SUCCESS) {
63391001Ssl147100 
63401001Ssl147100 					goto fail_cleanup;
63411001Ssl147100 				}
63421001Ssl147100 
63430Sstevel@tonic-gate 				child_dip = hubd_ready_device(hubd, child_dip,
63440Sstevel@tonic-gate 				    child_ud, 0);
63450Sstevel@tonic-gate 				mutex_enter(HUBD_MUTEX(hubd));
63460Sstevel@tonic-gate 				hubd->h_children_dips[port] = child_dip;
63470Sstevel@tonic-gate 				mutex_exit(HUBD_MUTEX(hubd));
63480Sstevel@tonic-gate 			}
63490Sstevel@tonic-gate 		} /* end else loop all configs */
63500Sstevel@tonic-gate 	} else {
63511001Ssl147100 
63521001Ssl147100 		if ((usba_hubdi_check_power_budget(dip, child_ud,
63531001Ssl147100 		    (uint_t)user_conf_index)) != USB_SUCCESS) {
63541001Ssl147100 
63551001Ssl147100 			goto fail_cleanup;
63561001Ssl147100 		}
63571001Ssl147100 
63580Sstevel@tonic-gate 		child_dip = hubd_ready_device(hubd, child_dip,
63591001Ssl147100 		    child_ud, (uint_t)user_conf_index);
63600Sstevel@tonic-gate 
63610Sstevel@tonic-gate 		/*
63620Sstevel@tonic-gate 		 * Assign the dip before onlining to avoid race
63630Sstevel@tonic-gate 		 * with busctl
63640Sstevel@tonic-gate 		 */
63650Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
63660Sstevel@tonic-gate 		hubd->h_children_dips[port] = child_dip;
63670Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
63680Sstevel@tonic-gate 
63690Sstevel@tonic-gate 		(void) usba_bind_driver(child_dip);
63700Sstevel@tonic-gate 	}
63710Sstevel@tonic-gate 
63721001Ssl147100 	usba_hubdi_decr_power_budget(dip, child_ud);
63731001Ssl147100 
63740Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
63750Sstevel@tonic-gate 	if (hubd->h_usba_devices[port] == NULL) {
63760Sstevel@tonic-gate 		hubd->h_usba_devices[port] = usba_get_usba_device(child_dip);
63770Sstevel@tonic-gate 	} else {
63780Sstevel@tonic-gate 		ASSERT(hubd->h_usba_devices[port] ==
63794763Slg150142 		    usba_get_usba_device(child_dip));
63800Sstevel@tonic-gate 	}
63810Sstevel@tonic-gate 
63820Sstevel@tonic-gate 	return (USB_SUCCESS);
63830Sstevel@tonic-gate 
63840Sstevel@tonic-gate 
63850Sstevel@tonic-gate fail_cleanup:
63860Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
63870Sstevel@tonic-gate 	    "hubd_create_child: fail_cleanup");
63880Sstevel@tonic-gate 
63890Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
63900Sstevel@tonic-gate 	hubd->h_children_dips[port] = NULL;
63910Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
63920Sstevel@tonic-gate 
63930Sstevel@tonic-gate 	if (pdata) {
63940Sstevel@tonic-gate 		freemsg(pdata);
63950Sstevel@tonic-gate 	}
63960Sstevel@tonic-gate 
63970Sstevel@tonic-gate 	if (ph) {
63980Sstevel@tonic-gate 		usb_pipe_close(child_dip, ph,
63990Sstevel@tonic-gate 		    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
64000Sstevel@tonic-gate 	}
64010Sstevel@tonic-gate 
64020Sstevel@tonic-gate 	if (child_dip) {
64030Sstevel@tonic-gate 		int rval = usba_destroy_child_devi(child_dip,
64040Sstevel@tonic-gate 		    NDI_DEVI_REMOVE);
64050Sstevel@tonic-gate 		if (rval != USB_SUCCESS) {
6406978Sfrits 			USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
64070Sstevel@tonic-gate 			    "failure to remove child node");
64080Sstevel@tonic-gate 		}
64090Sstevel@tonic-gate 	}
64100Sstevel@tonic-gate 
64110Sstevel@tonic-gate 	if (child_ud) {
64120Sstevel@tonic-gate 		/* to make sure we free the address */
64130Sstevel@tonic-gate 		mutex_enter(&child_ud->usb_mutex);
64140Sstevel@tonic-gate 		child_ud->usb_addr = address;
64150Sstevel@tonic-gate 		ASSERT(child_ud->usb_ref_count == 0);
64160Sstevel@tonic-gate 		mutex_exit(&child_ud->usb_mutex);
64170Sstevel@tonic-gate 
64180Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
64190Sstevel@tonic-gate 		if (hubd->h_usba_devices[port] == NULL) {
64200Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
64210Sstevel@tonic-gate 			usba_free_usba_device(child_ud);
64220Sstevel@tonic-gate 		} else {
64230Sstevel@tonic-gate 			hubd_free_usba_device(hubd, hubd->h_usba_devices[port]);
64240Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
64250Sstevel@tonic-gate 		}
64260Sstevel@tonic-gate 	}
64270Sstevel@tonic-gate 
64280Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
64290Sstevel@tonic-gate 
64300Sstevel@tonic-gate 	return (USB_FAILURE);
64310Sstevel@tonic-gate }
64320Sstevel@tonic-gate 
64330Sstevel@tonic-gate 
64340Sstevel@tonic-gate /*
64350Sstevel@tonic-gate  * hubd_delete_child:
64360Sstevel@tonic-gate  *	- free usb address
64370Sstevel@tonic-gate  *	- lookup child dips, there may be multiple on this port
64380Sstevel@tonic-gate  *	- offline each child devi
64390Sstevel@tonic-gate  */
64400Sstevel@tonic-gate static int
hubd_delete_child(hubd_t * hubd,usb_port_t port,uint_t flag,boolean_t retry)64410Sstevel@tonic-gate hubd_delete_child(hubd_t *hubd, usb_port_t port, uint_t flag, boolean_t retry)
64420Sstevel@tonic-gate {
64430Sstevel@tonic-gate 	dev_info_t	*child_dip;
64440Sstevel@tonic-gate 	usba_device_t	*usba_device;
64450Sstevel@tonic-gate 	int		rval = USB_SUCCESS;
64460Sstevel@tonic-gate 
64470Sstevel@tonic-gate 	child_dip = hubd->h_children_dips[port];
64481001Ssl147100 	usba_device = hubd->h_usba_devices[port];
64490Sstevel@tonic-gate 
64500Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
64510Sstevel@tonic-gate 	    "hubd_delete_child: port=%d, dip=0x%p usba_device=0x%p",
64526898Sfb209375 	    port, (void *)child_dip, (void *)usba_device);
64530Sstevel@tonic-gate 
64540Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
64550Sstevel@tonic-gate 	if (child_dip) {
64560Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
64570Sstevel@tonic-gate 		    "hubd_delete_child:\n\t"
64580Sstevel@tonic-gate 		    "dip = 0x%p (%s) at port %d",
64596898Sfb209375 		    (void *)child_dip, ddi_node_name(child_dip), port);
64600Sstevel@tonic-gate 
64611001Ssl147100 		if (usba_device) {
64621001Ssl147100 			usba_hubdi_incr_power_budget(hubd->h_dip, usba_device);
64631001Ssl147100 		}
64641001Ssl147100 
64650Sstevel@tonic-gate 		rval = usba_destroy_child_devi(child_dip, flag);
64660Sstevel@tonic-gate 
64679430SRaymond.Chen@Sun.COM 		if ((rval != USB_SUCCESS) && usba_is_hwa(child_dip)) {
64689430SRaymond.Chen@Sun.COM 			/*
64699430SRaymond.Chen@Sun.COM 			 * This is only useful for HWA device node.
64709430SRaymond.Chen@Sun.COM 			 * Since hwahc interface must hold hwarc interface
64719430SRaymond.Chen@Sun.COM 			 * open until hwahc is detached, the first call to
64729430SRaymond.Chen@Sun.COM 			 * ndi_devi_unconfig_one() can only offline hwahc
64739430SRaymond.Chen@Sun.COM 			 * driver but not hwarc driver. Need to make a second
64749430SRaymond.Chen@Sun.COM 			 * call to ndi_devi_unconfig_one() to make the hwarc
64759430SRaymond.Chen@Sun.COM 			 * driver detach.
64769430SRaymond.Chen@Sun.COM 			 */
64779430SRaymond.Chen@Sun.COM 			rval = usba_destroy_child_devi(child_dip, flag);
64789430SRaymond.Chen@Sun.COM 		}
64799430SRaymond.Chen@Sun.COM 
64800Sstevel@tonic-gate 		if ((rval == USB_SUCCESS) && (flag & NDI_DEVI_REMOVE)) {
64810Sstevel@tonic-gate 			/*
64820Sstevel@tonic-gate 			 * if the child was still < DS_INITIALIZED
64830Sstevel@tonic-gate 			 * then our bus_unconfig was not called and
64840Sstevel@tonic-gate 			 * we have to zap the child here
64850Sstevel@tonic-gate 			 */
64860Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
64870Sstevel@tonic-gate 			if (hubd->h_children_dips[port] == child_dip) {
64880Sstevel@tonic-gate 				usba_device_t *ud =
64894763Slg150142 				    hubd->h_usba_devices[port];
64904844Slg150142 					hubd->h_children_dips[port] = NULL;
64910Sstevel@tonic-gate 				if (ud) {
64920Sstevel@tonic-gate 					mutex_exit(HUBD_MUTEX(hubd));
64930Sstevel@tonic-gate 
64940Sstevel@tonic-gate 					mutex_enter(&ud->usb_mutex);
64950Sstevel@tonic-gate 					ud->usb_ref_count = 0;
64960Sstevel@tonic-gate 					mutex_exit(&ud->usb_mutex);
64970Sstevel@tonic-gate 
64980Sstevel@tonic-gate 					usba_free_usba_device(ud);
64990Sstevel@tonic-gate 					mutex_enter(HUBD_MUTEX(hubd));
65000Sstevel@tonic-gate 					hubd->h_usba_devices[port] = NULL;
65010Sstevel@tonic-gate 				}
65020Sstevel@tonic-gate 			}
65030Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
65040Sstevel@tonic-gate 		}
65050Sstevel@tonic-gate 	}
65060Sstevel@tonic-gate 
65070Sstevel@tonic-gate 	if ((rval != USB_SUCCESS) && retry) {
65080Sstevel@tonic-gate 
65090Sstevel@tonic-gate 		hubd_schedule_cleanup(usba_device->usb_root_hub_dip);
65100Sstevel@tonic-gate 	}
65110Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
65120Sstevel@tonic-gate 
65130Sstevel@tonic-gate 	return (rval);
65140Sstevel@tonic-gate }
65150Sstevel@tonic-gate 
65160Sstevel@tonic-gate 
65170Sstevel@tonic-gate /*
65180Sstevel@tonic-gate  * hubd_free_usba_device:
65190Sstevel@tonic-gate  *	free usb device structure unless it is associated with
65200Sstevel@tonic-gate  *	the root hub which is handled differently
65210Sstevel@tonic-gate  */
65220Sstevel@tonic-gate static void
hubd_free_usba_device(hubd_t * hubd,usba_device_t * usba_device)65230Sstevel@tonic-gate hubd_free_usba_device(hubd_t *hubd, usba_device_t *usba_device)
65240Sstevel@tonic-gate {
65250Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
65260Sstevel@tonic-gate 	    "hubd_free_usba_device: hubd=0x%p, usba_device=0x%p",
65276898Sfb209375 	    (void *)hubd, (void *)usba_device);
65280Sstevel@tonic-gate 
65290Sstevel@tonic-gate 	if (usba_device && (usba_device->usb_addr != ROOT_HUB_ADDR)) {
65300Sstevel@tonic-gate 		usb_port_t port = usba_device->usb_port;
65310Sstevel@tonic-gate 		dev_info_t *dip = hubd->h_children_dips[port];
65320Sstevel@tonic-gate 
65330Sstevel@tonic-gate #ifdef DEBUG
65340Sstevel@tonic-gate 		if (dip) {
65350Sstevel@tonic-gate 			ASSERT(i_ddi_node_state(dip) < DS_INITIALIZED);
65360Sstevel@tonic-gate 		}
65370Sstevel@tonic-gate #endif
65380Sstevel@tonic-gate 
65390Sstevel@tonic-gate 		port = usba_device->usb_port;
65400Sstevel@tonic-gate 		hubd->h_usba_devices[port] = NULL;
65410Sstevel@tonic-gate 
65420Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
65430Sstevel@tonic-gate 		usba_free_usba_device(usba_device);
65440Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
65450Sstevel@tonic-gate 	}
65460Sstevel@tonic-gate }
65470Sstevel@tonic-gate 
65480Sstevel@tonic-gate 
65490Sstevel@tonic-gate /*
65500Sstevel@tonic-gate  * event support
65510Sstevel@tonic-gate  *
65520Sstevel@tonic-gate  * busctl event support
65530Sstevel@tonic-gate  */
65540Sstevel@tonic-gate static int
hubd_busop_get_eventcookie(dev_info_t * dip,dev_info_t * rdip,char * eventname,ddi_eventcookie_t * cookie)65550Sstevel@tonic-gate hubd_busop_get_eventcookie(dev_info_t *dip,
65560Sstevel@tonic-gate 	dev_info_t	*rdip,
65570Sstevel@tonic-gate 	char		*eventname,
65580Sstevel@tonic-gate 	ddi_eventcookie_t *cookie)
65590Sstevel@tonic-gate {
65600Sstevel@tonic-gate 	hubd_t	*hubd = (hubd_t *)hubd_get_soft_state(dip);
65610Sstevel@tonic-gate 
65620Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
65630Sstevel@tonic-gate 	    "hubd_busop_get_eventcookie: dip=0x%p, rdip=0x%p, "
65640Sstevel@tonic-gate 	    "event=%s", (void *)dip, (void *)rdip, eventname);
65650Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
65660Sstevel@tonic-gate 	    "(dip=%s%d, rdip=%s%d)",
65670Sstevel@tonic-gate 	    ddi_driver_name(dip), ddi_get_instance(dip),
65680Sstevel@tonic-gate 	    ddi_driver_name(rdip), ddi_get_instance(rdip));
65690Sstevel@tonic-gate 
65700Sstevel@tonic-gate 	/* return event cookie, iblock cookie, and level */
65710Sstevel@tonic-gate 	return (ndi_event_retrieve_cookie(hubd->h_ndi_event_hdl,
65724763Slg150142 	    rdip, eventname, cookie, NDI_EVENT_NOPASS));
65730Sstevel@tonic-gate }
65740Sstevel@tonic-gate 
65750Sstevel@tonic-gate 
65760Sstevel@tonic-gate static int
hubd_busop_add_eventcall(dev_info_t * dip,dev_info_t * rdip,ddi_eventcookie_t cookie,void (* callback)(dev_info_t * dip,ddi_eventcookie_t cookie,void * arg,void * bus_impldata),void * arg,ddi_callback_id_t * cb_id)65770Sstevel@tonic-gate hubd_busop_add_eventcall(dev_info_t *dip,
65780Sstevel@tonic-gate 	dev_info_t	*rdip,
65790Sstevel@tonic-gate 	ddi_eventcookie_t cookie,
65800Sstevel@tonic-gate 	void		(*callback)(dev_info_t *dip,
65810Sstevel@tonic-gate 			ddi_eventcookie_t cookie, void *arg,
65820Sstevel@tonic-gate 			void *bus_impldata),
65830Sstevel@tonic-gate 	void *arg, ddi_callback_id_t *cb_id)
65840Sstevel@tonic-gate {
65850Sstevel@tonic-gate 	hubd_t	*hubd = (hubd_t *)hubd_get_soft_state(dip);
65860Sstevel@tonic-gate 	usb_port_t port = hubd_child_dip2port(hubd, rdip);
65870Sstevel@tonic-gate 
65880Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
65890Sstevel@tonic-gate 	    "hubd_busop_add_eventcall: dip=0x%p, rdip=0x%p "
65900Sstevel@tonic-gate 	    "cookie=0x%p, cb=0x%p, arg=0x%p",
65910Sstevel@tonic-gate 	    (void *)dip, (void *)rdip, (void *)cookie, (void *)callback, arg);
65920Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
65930Sstevel@tonic-gate 	    "(dip=%s%d, rdip=%s%d, event=%s)",
65940Sstevel@tonic-gate 	    ddi_driver_name(dip), ddi_get_instance(dip),
65950Sstevel@tonic-gate 	    ddi_driver_name(rdip), ddi_get_instance(rdip),
65960Sstevel@tonic-gate 	    ndi_event_cookie_to_name(hubd->h_ndi_event_hdl, cookie));
65970Sstevel@tonic-gate 
65980Sstevel@tonic-gate 	/* Set flag on children registering events */
65990Sstevel@tonic-gate 	switch (ndi_event_cookie_to_tag(hubd->h_ndi_event_hdl, cookie)) {
66000Sstevel@tonic-gate 	case USBA_EVENT_TAG_HOT_REMOVAL:
66010Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
66020Sstevel@tonic-gate 		hubd->h_child_events[port] |= HUBD_CHILD_EVENT_DISCONNECT;
66030Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
66040Sstevel@tonic-gate 
66050Sstevel@tonic-gate 		break;
66060Sstevel@tonic-gate 	case USBA_EVENT_TAG_PRE_SUSPEND:
66070Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
66080Sstevel@tonic-gate 		hubd->h_child_events[port] |= HUBD_CHILD_EVENT_PRESUSPEND;
66090Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
66100Sstevel@tonic-gate 
66110Sstevel@tonic-gate 		break;
66120Sstevel@tonic-gate 	default:
66130Sstevel@tonic-gate 
66140Sstevel@tonic-gate 		break;
66150Sstevel@tonic-gate 	}
66160Sstevel@tonic-gate 
66170Sstevel@tonic-gate 	/* add callback to our event set */
66180Sstevel@tonic-gate 	return (ndi_event_add_callback(hubd->h_ndi_event_hdl,
66194763Slg150142 	    rdip, cookie, callback, arg, NDI_SLEEP, cb_id));
66200Sstevel@tonic-gate }
66210Sstevel@tonic-gate 
66220Sstevel@tonic-gate 
66230Sstevel@tonic-gate static int
hubd_busop_remove_eventcall(dev_info_t * dip,ddi_callback_id_t cb_id)66240Sstevel@tonic-gate hubd_busop_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id)
66250Sstevel@tonic-gate {
66260Sstevel@tonic-gate 	hubd_t	*hubd = (hubd_t *)hubd_get_soft_state(dip);
66270Sstevel@tonic-gate 	ndi_event_callbacks_t *id = (ndi_event_callbacks_t *)cb_id;
66280Sstevel@tonic-gate 
66290Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
66300Sstevel@tonic-gate 	    "hubd_busop_remove_eventcall: dip=0x%p, rdip=0x%p "
66316898Sfb209375 	    "cookie=0x%p", (void *)dip, (void *)id->ndi_evtcb_dip,
66326898Sfb209375 	    (void *)id->ndi_evtcb_cookie);
66330Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
66340Sstevel@tonic-gate 	    "(dip=%s%d, rdip=%s%d, event=%s)",
66350Sstevel@tonic-gate 	    ddi_driver_name(dip), ddi_get_instance(dip),
66360Sstevel@tonic-gate 	    ddi_driver_name(id->ndi_evtcb_dip),
66370Sstevel@tonic-gate 	    ddi_get_instance(id->ndi_evtcb_dip),
66380Sstevel@tonic-gate 	    ndi_event_cookie_to_name(hubd->h_ndi_event_hdl,
66390Sstevel@tonic-gate 	    id->ndi_evtcb_cookie));
66400Sstevel@tonic-gate 
66410Sstevel@tonic-gate 	/* remove event registration from our event set */
66420Sstevel@tonic-gate 	return (ndi_event_remove_callback(hubd->h_ndi_event_hdl, cb_id));
66430Sstevel@tonic-gate }
66440Sstevel@tonic-gate 
66450Sstevel@tonic-gate 
66460Sstevel@tonic-gate /*
66470Sstevel@tonic-gate  * event distribution
66480Sstevel@tonic-gate  *
66490Sstevel@tonic-gate  * hubd_do_callback:
66500Sstevel@tonic-gate  *	Post this event to the specified child
66510Sstevel@tonic-gate  */
66520Sstevel@tonic-gate static void
hubd_do_callback(hubd_t * hubd,dev_info_t * cdip,ddi_eventcookie_t cookie)66530Sstevel@tonic-gate hubd_do_callback(hubd_t *hubd, dev_info_t *cdip, ddi_eventcookie_t cookie)
66540Sstevel@tonic-gate {
66550Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
66560Sstevel@tonic-gate 	    "hubd_do_callback");
66570Sstevel@tonic-gate 
66580Sstevel@tonic-gate 	(void) ndi_event_do_callback(hubd->h_ndi_event_hdl, cdip, cookie, NULL);
66590Sstevel@tonic-gate }
66600Sstevel@tonic-gate 
66610Sstevel@tonic-gate 
66620Sstevel@tonic-gate /*
66630Sstevel@tonic-gate  * hubd_run_callbacks:
66640Sstevel@tonic-gate  *	Send this event to all children
66650Sstevel@tonic-gate  */
66660Sstevel@tonic-gate static void
hubd_run_callbacks(hubd_t * hubd,usba_event_t type)66670Sstevel@tonic-gate hubd_run_callbacks(hubd_t *hubd, usba_event_t type)
66680Sstevel@tonic-gate {
66690Sstevel@tonic-gate 	usb_port_t	port;
66700Sstevel@tonic-gate 
66710Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
66720Sstevel@tonic-gate 	    "hubd_run_callbacks");
66730Sstevel@tonic-gate 
66740Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
66750Sstevel@tonic-gate 	for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
66760Sstevel@tonic-gate 		/*
66770Sstevel@tonic-gate 		 * the childen_dips list may have dips that have been
66780Sstevel@tonic-gate 		 * already deallocated. we only get a post_detach notification
66790Sstevel@tonic-gate 		 * but not a destroy notification
66800Sstevel@tonic-gate 		 */
66810Sstevel@tonic-gate 		if (hubd->h_children_dips[port]) {
66820Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
66830Sstevel@tonic-gate 			hubd_post_event(hubd, port, type);
66840Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
66850Sstevel@tonic-gate 		}
66860Sstevel@tonic-gate 	}
66870Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
66880Sstevel@tonic-gate }
66890Sstevel@tonic-gate 
66900Sstevel@tonic-gate 
66910Sstevel@tonic-gate /*
66920Sstevel@tonic-gate  * hubd_post_event
66930Sstevel@tonic-gate  *	post event to a child on the port depending on the type
66940Sstevel@tonic-gate  */
66950Sstevel@tonic-gate static void
hubd_post_event(hubd_t * hubd,usb_port_t port,usba_event_t type)66960Sstevel@tonic-gate hubd_post_event(hubd_t *hubd, usb_port_t port, usba_event_t type)
66970Sstevel@tonic-gate {
66980Sstevel@tonic-gate 	int	rval;
66990Sstevel@tonic-gate 	dev_info_t	*dip;
67000Sstevel@tonic-gate 	usba_device_t	*usba_device;
67010Sstevel@tonic-gate 	ddi_eventcookie_t cookie, rm_cookie, suspend_cookie;
67020Sstevel@tonic-gate 
67030Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
67040Sstevel@tonic-gate 	    "hubd_post_event: port=%d event=%s", port,
67050Sstevel@tonic-gate 	    ndi_event_tag_to_name(hubd->h_ndi_event_hdl, type));
67060Sstevel@tonic-gate 
67070Sstevel@tonic-gate 	cookie = ndi_event_tag_to_cookie(hubd->h_ndi_event_hdl, type);
67080Sstevel@tonic-gate 	rm_cookie = ndi_event_tag_to_cookie(hubd->h_ndi_event_hdl,
67090Sstevel@tonic-gate 	    USBA_EVENT_TAG_HOT_REMOVAL);
67100Sstevel@tonic-gate 	suspend_cookie = ndi_event_tag_to_cookie(hubd->h_ndi_event_hdl,
67110Sstevel@tonic-gate 	    USBA_EVENT_TAG_PRE_SUSPEND);
67120Sstevel@tonic-gate 
67130Sstevel@tonic-gate 	/*
67140Sstevel@tonic-gate 	 * Hotplug daemon may be attaching a driver that may be registering
67150Sstevel@tonic-gate 	 * event callbacks. So it already has got the device tree lock and
67160Sstevel@tonic-gate 	 * event handle mutex. So to prevent a deadlock while posting events,
67170Sstevel@tonic-gate 	 * we grab and release the locks in the same order.
67180Sstevel@tonic-gate 	 */
67190Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
67200Sstevel@tonic-gate 	dip = hubd->h_children_dips[port];
67210Sstevel@tonic-gate 	usba_device = hubd->h_usba_devices[port];
67220Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
67230Sstevel@tonic-gate 
67240Sstevel@tonic-gate 	switch (type) {
67250Sstevel@tonic-gate 	case USBA_EVENT_TAG_HOT_REMOVAL:
67260Sstevel@tonic-gate 		/* Clear the registered event flag */
67270Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
67280Sstevel@tonic-gate 		hubd->h_child_events[port] &= ~HUBD_CHILD_EVENT_DISCONNECT;
67290Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
67300Sstevel@tonic-gate 
67310Sstevel@tonic-gate 		hubd_do_callback(hubd, dip, cookie);
67320Sstevel@tonic-gate 		usba_persistent_pipe_close(usba_device);
67330Sstevel@tonic-gate 
67340Sstevel@tonic-gate 		/*
67350Sstevel@tonic-gate 		 * Mark the dip for deletion only after the driver has
67360Sstevel@tonic-gate 		 * seen the disconnect event to prevent cleanup thread
67370Sstevel@tonic-gate 		 * from stepping in between.
67380Sstevel@tonic-gate 		 */
6739495Scth 		mutex_enter(&(DEVI(dip)->devi_lock));
67400Sstevel@tonic-gate 		DEVI_SET_DEVICE_REMOVED(dip);
6741495Scth 		mutex_exit(&(DEVI(dip)->devi_lock));
67420Sstevel@tonic-gate 
67430Sstevel@tonic-gate 		break;
67440Sstevel@tonic-gate 	case USBA_EVENT_TAG_PRE_SUSPEND:
67450Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
67460Sstevel@tonic-gate 		hubd->h_child_events[port] &= ~HUBD_CHILD_EVENT_PRESUSPEND;
67470Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
67480Sstevel@tonic-gate 
67490Sstevel@tonic-gate 		hubd_do_callback(hubd, dip, cookie);
67500Sstevel@tonic-gate 		/*
67510Sstevel@tonic-gate 		 * persistent pipe close for this event is taken care by the
67520Sstevel@tonic-gate 		 * caller after verfying that all children can suspend
67530Sstevel@tonic-gate 		 */
67540Sstevel@tonic-gate 
67550Sstevel@tonic-gate 		break;
67560Sstevel@tonic-gate 	case USBA_EVENT_TAG_HOT_INSERTION:
67570Sstevel@tonic-gate 		/*
67580Sstevel@tonic-gate 		 * Check if this child has missed the disconnect event before
67590Sstevel@tonic-gate 		 * it registered for event callbacks
67600Sstevel@tonic-gate 		 */
67610Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
67620Sstevel@tonic-gate 		if (hubd->h_child_events[port] & HUBD_CHILD_EVENT_DISCONNECT) {
67630Sstevel@tonic-gate 			/* clear the flag and post disconnect event */
67640Sstevel@tonic-gate 			hubd->h_child_events[port] &=
67650Sstevel@tonic-gate 			    ~HUBD_CHILD_EVENT_DISCONNECT;
67660Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
67670Sstevel@tonic-gate 			hubd_do_callback(hubd, dip, rm_cookie);
67680Sstevel@tonic-gate 			usba_persistent_pipe_close(usba_device);
67690Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
67700Sstevel@tonic-gate 		}
67710Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
67720Sstevel@tonic-gate 
67730Sstevel@tonic-gate 		/*
67740Sstevel@tonic-gate 		 * Mark the dip as reinserted to prevent cleanup thread
67750Sstevel@tonic-gate 		 * from stepping in.
67760Sstevel@tonic-gate 		 */
6777495Scth 		mutex_enter(&(DEVI(dip)->devi_lock));
67780Sstevel@tonic-gate 		DEVI_SET_DEVICE_REINSERTED(dip);
6779495Scth 		mutex_exit(&(DEVI(dip)->devi_lock));
67800Sstevel@tonic-gate 
67810Sstevel@tonic-gate 		rval = usba_persistent_pipe_open(usba_device);
67820Sstevel@tonic-gate 		if (rval != USB_SUCCESS) {
6783978Sfrits 			USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
67840Sstevel@tonic-gate 			    hubd->h_log_handle,
67850Sstevel@tonic-gate 			    "failed to reopen all pipes on reconnect");
67860Sstevel@tonic-gate 		}
67870Sstevel@tonic-gate 
67880Sstevel@tonic-gate 		hubd_do_callback(hubd, dip, cookie);
67890Sstevel@tonic-gate 
67900Sstevel@tonic-gate 		/*
67910Sstevel@tonic-gate 		 * We might see a connect event only if hotplug thread for
67920Sstevel@tonic-gate 		 * disconnect event don't run in time.
67930Sstevel@tonic-gate 		 * Set the flag again, so we don't miss posting a
67940Sstevel@tonic-gate 		 * disconnect event.
67950Sstevel@tonic-gate 		 */
67960Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
67970Sstevel@tonic-gate 		hubd->h_child_events[port] |= HUBD_CHILD_EVENT_DISCONNECT;
67980Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
67990Sstevel@tonic-gate 
68000Sstevel@tonic-gate 		break;
68010Sstevel@tonic-gate 	case USBA_EVENT_TAG_POST_RESUME:
68020Sstevel@tonic-gate 		/*
68030Sstevel@tonic-gate 		 * Check if this child has missed the pre-suspend event before
68040Sstevel@tonic-gate 		 * it registered for event callbacks
68050Sstevel@tonic-gate 		 */
68060Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
68070Sstevel@tonic-gate 		if (hubd->h_child_events[port] & HUBD_CHILD_EVENT_PRESUSPEND) {
68080Sstevel@tonic-gate 			/* clear the flag and post pre_suspend event */
68090Sstevel@tonic-gate 			hubd->h_port_state[port] &=
68100Sstevel@tonic-gate 			    ~HUBD_CHILD_EVENT_PRESUSPEND;
68110Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
68120Sstevel@tonic-gate 			hubd_do_callback(hubd, dip, suspend_cookie);
68130Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
68140Sstevel@tonic-gate 		}
68150Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
68160Sstevel@tonic-gate 
68170Sstevel@tonic-gate 		mutex_enter(&usba_device->usb_mutex);
68180Sstevel@tonic-gate 		usba_device->usb_no_cpr = 0;
68190Sstevel@tonic-gate 		mutex_exit(&usba_device->usb_mutex);
68200Sstevel@tonic-gate 
68210Sstevel@tonic-gate 		/*
68220Sstevel@tonic-gate 		 * Since the pipe has already been opened by hub
68230Sstevel@tonic-gate 		 * at DDI_RESUME time, there is no need for a
68240Sstevel@tonic-gate 		 * persistent pipe open
68250Sstevel@tonic-gate 		 */
68260Sstevel@tonic-gate 		hubd_do_callback(hubd, dip, cookie);
68270Sstevel@tonic-gate 
68280Sstevel@tonic-gate 		/*
68290Sstevel@tonic-gate 		 * Set the flag again, so we don't miss posting a
68300Sstevel@tonic-gate 		 * pre-suspend event. This enforces a tighter
68310Sstevel@tonic-gate 		 * dev_state model.
68320Sstevel@tonic-gate 		 */
68330Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
68340Sstevel@tonic-gate 		hubd->h_child_events[port] |= HUBD_CHILD_EVENT_PRESUSPEND;
68350Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
68360Sstevel@tonic-gate 		break;
68370Sstevel@tonic-gate 	}
68380Sstevel@tonic-gate }
68390Sstevel@tonic-gate 
68400Sstevel@tonic-gate 
68410Sstevel@tonic-gate /*
68420Sstevel@tonic-gate  * handling of events coming from above
68430Sstevel@tonic-gate  */
68440Sstevel@tonic-gate static int
hubd_disconnect_event_cb(dev_info_t * dip)68450Sstevel@tonic-gate hubd_disconnect_event_cb(dev_info_t *dip)
68460Sstevel@tonic-gate {
68470Sstevel@tonic-gate 	hubd_t		*hubd = (hubd_t *)hubd_get_soft_state(dip);
68480Sstevel@tonic-gate 	usb_port_t	port, nports;
68490Sstevel@tonic-gate 	usba_device_t	*usba_dev;
68500Sstevel@tonic-gate 	usba_event_t	tag = USBA_EVENT_TAG_HOT_REMOVAL;
68510Sstevel@tonic-gate 	int		circ;
68520Sstevel@tonic-gate 
68530Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
68540Sstevel@tonic-gate 	    "hubd_disconnect_event_cb: tag=%d", tag);
68550Sstevel@tonic-gate 
68560Sstevel@tonic-gate 	ndi_devi_enter(dip, &circ);
68570Sstevel@tonic-gate 
68580Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
68590Sstevel@tonic-gate 	switch (hubd->h_dev_state) {
68600Sstevel@tonic-gate 	case USB_DEV_ONLINE:
68610Sstevel@tonic-gate 	case USB_DEV_PWRED_DOWN:
68620Sstevel@tonic-gate 		hubd->h_dev_state = USB_DEV_DISCONNECTED;
68630Sstevel@tonic-gate 		/* stop polling on the interrupt pipe */
68640Sstevel@tonic-gate 		hubd_stop_polling(hubd);
68650Sstevel@tonic-gate 
68660Sstevel@tonic-gate 		/* FALLTHROUGH */
68670Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
68680Sstevel@tonic-gate 		/* we remain in this state */
68690Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
68700Sstevel@tonic-gate 		hubd_run_callbacks(hubd, tag);
68710Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
68720Sstevel@tonic-gate 
68730Sstevel@tonic-gate 		/* close all the open pipes of our children */
68740Sstevel@tonic-gate 		nports = hubd->h_hub_descr.bNbrPorts;
68750Sstevel@tonic-gate 		for (port = 1; port <= nports; port++) {
68760Sstevel@tonic-gate 			usba_dev = hubd->h_usba_devices[port];
68770Sstevel@tonic-gate 			if (usba_dev != NULL) {
68780Sstevel@tonic-gate 				mutex_exit(HUBD_MUTEX(hubd));
68790Sstevel@tonic-gate 				usba_persistent_pipe_close(usba_dev);
68800Sstevel@tonic-gate 				mutex_enter(HUBD_MUTEX(hubd));
68810Sstevel@tonic-gate 			}
68820Sstevel@tonic-gate 		}
68830Sstevel@tonic-gate 
68840Sstevel@tonic-gate 		break;
68850Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
68860Sstevel@tonic-gate 		/* avoid passing multiple disconnects to children */
68870Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
68880Sstevel@tonic-gate 		    "hubd_disconnect_event_cb: Already disconnected");
68890Sstevel@tonic-gate 
68900Sstevel@tonic-gate 		break;
68910Sstevel@tonic-gate 	default:
68920Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
68930Sstevel@tonic-gate 		    "hubd_disconnect_event_cb: Illegal devstate=%d",
68940Sstevel@tonic-gate 		    hubd->h_dev_state);
68950Sstevel@tonic-gate 
68960Sstevel@tonic-gate 		break;
68970Sstevel@tonic-gate 	}
68980Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
68990Sstevel@tonic-gate 
69000Sstevel@tonic-gate 	ndi_devi_exit(dip, circ);
69010Sstevel@tonic-gate 
69020Sstevel@tonic-gate 	return (USB_SUCCESS);
69030Sstevel@tonic-gate }
69040Sstevel@tonic-gate 
69050Sstevel@tonic-gate 
69060Sstevel@tonic-gate static int
hubd_reconnect_event_cb(dev_info_t * dip)69070Sstevel@tonic-gate hubd_reconnect_event_cb(dev_info_t *dip)
69080Sstevel@tonic-gate {
69090Sstevel@tonic-gate 	int	rval, circ;
69100Sstevel@tonic-gate 
69110Sstevel@tonic-gate 	ndi_devi_enter(dip, &circ);
69120Sstevel@tonic-gate 	rval = hubd_restore_state_cb(dip);
69130Sstevel@tonic-gate 	ndi_devi_exit(dip, circ);
69140Sstevel@tonic-gate 
69150Sstevel@tonic-gate 	return (rval);
69160Sstevel@tonic-gate }
69170Sstevel@tonic-gate 
69180Sstevel@tonic-gate 
69190Sstevel@tonic-gate /*
69200Sstevel@tonic-gate  * hubd_pre_suspend_event_cb
69210Sstevel@tonic-gate  *	propogate event for binary compatibility of old drivers
69220Sstevel@tonic-gate  */
69230Sstevel@tonic-gate static int
hubd_pre_suspend_event_cb(dev_info_t * dip)69240Sstevel@tonic-gate hubd_pre_suspend_event_cb(dev_info_t *dip)
69250Sstevel@tonic-gate {
69260Sstevel@tonic-gate 	int	circ;
69270Sstevel@tonic-gate 	hubd_t	*hubd = (hubd_t *)hubd_get_soft_state(dip);
69280Sstevel@tonic-gate 
69290Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle,
69300Sstevel@tonic-gate 	    "hubd_pre_suspend_event_cb");
69310Sstevel@tonic-gate 
69320Sstevel@tonic-gate 	/* disable hotplug thread */
69330Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
69340Sstevel@tonic-gate 	hubd->h_hotplug_thread++;
69350Sstevel@tonic-gate 	hubd_stop_polling(hubd);
69360Sstevel@tonic-gate 
69370Sstevel@tonic-gate 	/* keep PM out till we see a cpr resume */
69380Sstevel@tonic-gate 	(void) hubd_pm_busy_component(hubd, hubd->h_dip, 0);
69390Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
69400Sstevel@tonic-gate 
69410Sstevel@tonic-gate 	ndi_devi_enter(dip, &circ);
69420Sstevel@tonic-gate 	hubd_run_callbacks(hubd, USBA_EVENT_TAG_PRE_SUSPEND);
69430Sstevel@tonic-gate 	ndi_devi_exit(dip, circ);
69440Sstevel@tonic-gate 
69450Sstevel@tonic-gate 	return (USB_SUCCESS);
69460Sstevel@tonic-gate }
69470Sstevel@tonic-gate 
69480Sstevel@tonic-gate 
69490Sstevel@tonic-gate /*
69500Sstevel@tonic-gate  * hubd_post_resume_event_cb
69510Sstevel@tonic-gate  *	propogate event for binary compatibility of old drivers
69520Sstevel@tonic-gate  */
69530Sstevel@tonic-gate static int
hubd_post_resume_event_cb(dev_info_t * dip)69540Sstevel@tonic-gate hubd_post_resume_event_cb(dev_info_t *dip)
69550Sstevel@tonic-gate {
69560Sstevel@tonic-gate 	int	circ;
69570Sstevel@tonic-gate 	hubd_t	*hubd = (hubd_t *)hubd_get_soft_state(dip);
69580Sstevel@tonic-gate 
69590Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle,
69600Sstevel@tonic-gate 	    "hubd_post_resume_event_cb");
69610Sstevel@tonic-gate 
69620Sstevel@tonic-gate 	ndi_devi_enter(dip, &circ);
69630Sstevel@tonic-gate 	hubd_run_callbacks(hubd, USBA_EVENT_TAG_POST_RESUME);
69640Sstevel@tonic-gate 	ndi_devi_exit(dip, circ);
69650Sstevel@tonic-gate 
69660Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
69670Sstevel@tonic-gate 
69680Sstevel@tonic-gate 	/* enable PM */
69690Sstevel@tonic-gate 	(void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
69700Sstevel@tonic-gate 
69710Sstevel@tonic-gate 	/* allow hotplug thread */
69720Sstevel@tonic-gate 	hubd->h_hotplug_thread--;
69730Sstevel@tonic-gate 
69740Sstevel@tonic-gate 	/* start polling */
69750Sstevel@tonic-gate 	hubd_start_polling(hubd, 0);
69760Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
69770Sstevel@tonic-gate 
69780Sstevel@tonic-gate 	return (USB_SUCCESS);
69790Sstevel@tonic-gate }
69800Sstevel@tonic-gate 
69810Sstevel@tonic-gate 
69820Sstevel@tonic-gate /*
69830Sstevel@tonic-gate  * hubd_cpr_suspend
69840Sstevel@tonic-gate  *	save the current state of the driver/device
69850Sstevel@tonic-gate  */
69860Sstevel@tonic-gate static int
hubd_cpr_suspend(hubd_t * hubd)69870Sstevel@tonic-gate hubd_cpr_suspend(hubd_t *hubd)
69880Sstevel@tonic-gate {
69890Sstevel@tonic-gate 	usb_port_t	port, nports;
69900Sstevel@tonic-gate 	usba_device_t	*usba_dev;
69910Sstevel@tonic-gate 	uchar_t		no_cpr = 0;
69920Sstevel@tonic-gate 	int		rval = USB_FAILURE;
69930Sstevel@tonic-gate 
69940Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
69950Sstevel@tonic-gate 	    "hubd_cpr_suspend: Begin");
69960Sstevel@tonic-gate 
69970Sstevel@tonic-gate 	/* Make sure device is powered up to save state. */
69980Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
69990Sstevel@tonic-gate 	hubd_pm_busy_component(hubd, hubd->h_dip, 0);
70000Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
70010Sstevel@tonic-gate 
70020Sstevel@tonic-gate 	/* bring the device to full power */
70030Sstevel@tonic-gate 	(void) pm_raise_power(hubd->h_dip, 0, USB_DEV_OS_FULL_PWR);
70040Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
70050Sstevel@tonic-gate 
70060Sstevel@tonic-gate 	switch (hubd->h_dev_state) {
70070Sstevel@tonic-gate 	case USB_DEV_ONLINE:
70080Sstevel@tonic-gate 	case USB_DEV_PWRED_DOWN:
70090Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
70100Sstevel@tonic-gate 		/* find out if all our children have been quiesced */
70110Sstevel@tonic-gate 		nports = hubd->h_hub_descr.bNbrPorts;
70120Sstevel@tonic-gate 		for (port = 1; (no_cpr == 0) && (port <= nports); port++) {
70130Sstevel@tonic-gate 			usba_dev = hubd->h_usba_devices[port];
70140Sstevel@tonic-gate 			if (usba_dev != NULL) {
70150Sstevel@tonic-gate 				mutex_enter(&usba_dev->usb_mutex);
70160Sstevel@tonic-gate 				no_cpr += usba_dev->usb_no_cpr;
70170Sstevel@tonic-gate 				mutex_exit(&usba_dev->usb_mutex);
70180Sstevel@tonic-gate 			}
70190Sstevel@tonic-gate 		}
70200Sstevel@tonic-gate 		if (no_cpr > 0) {
70210Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
70220Sstevel@tonic-gate 			    "Children busy - can't checkpoint");
70230Sstevel@tonic-gate 			/* remain in same state to fail checkpoint */
70240Sstevel@tonic-gate 
70250Sstevel@tonic-gate 			break;
70260Sstevel@tonic-gate 		} else {
70270Sstevel@tonic-gate 			/*
70280Sstevel@tonic-gate 			 * do not suspend if our hotplug thread
70290Sstevel@tonic-gate 			 * or the deathrow thread is active
70300Sstevel@tonic-gate 			 */
70310Sstevel@tonic-gate 			if ((hubd->h_hotplug_thread > 1) ||
70320Sstevel@tonic-gate 			    (hubd->h_cleanup_active == B_TRUE)) {
70330Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
70340Sstevel@tonic-gate 				    hubd->h_log_handle,
70350Sstevel@tonic-gate 				    "hotplug thread active  - can't cpr");
70360Sstevel@tonic-gate 				/* remain in same state to fail checkpoint */
70370Sstevel@tonic-gate 
70380Sstevel@tonic-gate 				break;
70390Sstevel@tonic-gate 			}
70400Sstevel@tonic-gate 
70410Sstevel@tonic-gate 			/* quiesce ourselves now */
70420Sstevel@tonic-gate 			hubd_stop_polling(hubd);
70430Sstevel@tonic-gate 
70440Sstevel@tonic-gate 			/* close all the open pipes of our children */
70450Sstevel@tonic-gate 			for (port = 1; port <= nports; port++) {
70460Sstevel@tonic-gate 				usba_dev = hubd->h_usba_devices[port];
70470Sstevel@tonic-gate 				if (usba_dev != NULL) {
70480Sstevel@tonic-gate 					mutex_exit(HUBD_MUTEX(hubd));
70490Sstevel@tonic-gate 					usba_persistent_pipe_close(usba_dev);
7050*12819SVincent.Wang@Sun.COM 					if (hubd_suspend_port(hubd, port)) {
7051*12819SVincent.Wang@Sun.COM 						USB_DPRINTF_L0(
7052*12819SVincent.Wang@Sun.COM 						    DPRINT_MASK_HOTPLUG,
7053*12819SVincent.Wang@Sun.COM 						    hubd->h_log_handle,
7054*12819SVincent.Wang@Sun.COM 						    "suspending port %d failed",
7055*12819SVincent.Wang@Sun.COM 						    port);
7056*12819SVincent.Wang@Sun.COM 					}
70570Sstevel@tonic-gate 					mutex_enter(HUBD_MUTEX(hubd));
70580Sstevel@tonic-gate 				}
7059*12819SVincent.Wang@Sun.COM 
70600Sstevel@tonic-gate 			}
7061*12819SVincent.Wang@Sun.COM 			hubd->h_dev_state = USB_DEV_SUSPENDED;
70620Sstevel@tonic-gate 
70630Sstevel@tonic-gate 			/*
70640Sstevel@tonic-gate 			 * if we are the root hub, we close our pipes
70650Sstevel@tonic-gate 			 * ourselves.
70660Sstevel@tonic-gate 			 */
70670Sstevel@tonic-gate 			if (usba_is_root_hub(hubd->h_dip)) {
70680Sstevel@tonic-gate 				mutex_exit(HUBD_MUTEX(hubd));
70690Sstevel@tonic-gate 				usba_persistent_pipe_close(
70700Sstevel@tonic-gate 				    usba_get_usba_device(hubd->h_dip));
70710Sstevel@tonic-gate 				mutex_enter(HUBD_MUTEX(hubd));
70720Sstevel@tonic-gate 			}
70730Sstevel@tonic-gate 			rval = USB_SUCCESS;
70740Sstevel@tonic-gate 
70750Sstevel@tonic-gate 			break;
70760Sstevel@tonic-gate 		}
70770Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
70780Sstevel@tonic-gate 	default:
70790Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
70800Sstevel@tonic-gate 		    "hubd_cpr_suspend: Illegal dev state=%d",
70810Sstevel@tonic-gate 		    hubd->h_dev_state);
70820Sstevel@tonic-gate 
70830Sstevel@tonic-gate 		break;
70840Sstevel@tonic-gate 	}
70850Sstevel@tonic-gate 
70860Sstevel@tonic-gate 	hubd_pm_idle_component(hubd, hubd->h_dip, 0);
70870Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
70880Sstevel@tonic-gate 
70890Sstevel@tonic-gate 	return (rval);
70900Sstevel@tonic-gate }
70910Sstevel@tonic-gate 
70920Sstevel@tonic-gate static void
hubd_cpr_resume(dev_info_t * dip)70930Sstevel@tonic-gate hubd_cpr_resume(dev_info_t *dip)
70940Sstevel@tonic-gate {
70950Sstevel@tonic-gate 	int	rval, circ;
70960Sstevel@tonic-gate 
70970Sstevel@tonic-gate 	ndi_devi_enter(dip, &circ);
70980Sstevel@tonic-gate 	/*
70990Sstevel@tonic-gate 	 * if we are the root hub, we open our pipes
71000Sstevel@tonic-gate 	 * ourselves.
71010Sstevel@tonic-gate 	 */
71020Sstevel@tonic-gate 	if (usba_is_root_hub(dip)) {
71030Sstevel@tonic-gate 		rval = usba_persistent_pipe_open(
71040Sstevel@tonic-gate 		    usba_get_usba_device(dip));
71050Sstevel@tonic-gate 		ASSERT(rval == USB_SUCCESS);
71060Sstevel@tonic-gate 	}
71070Sstevel@tonic-gate 	(void) hubd_restore_state_cb(dip);
71080Sstevel@tonic-gate 	ndi_devi_exit(dip, circ);
71090Sstevel@tonic-gate }
71100Sstevel@tonic-gate 
71110Sstevel@tonic-gate 
71120Sstevel@tonic-gate /*
71130Sstevel@tonic-gate  * hubd_restore_state_cb
71140Sstevel@tonic-gate  *	Event callback to restore device state
71150Sstevel@tonic-gate  */
71160Sstevel@tonic-gate static int
hubd_restore_state_cb(dev_info_t * dip)71170Sstevel@tonic-gate hubd_restore_state_cb(dev_info_t *dip)
71180Sstevel@tonic-gate {
71190Sstevel@tonic-gate 	hubd_t	*hubd = (hubd_t *)hubd_get_soft_state(dip);
71200Sstevel@tonic-gate 
71210Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
71220Sstevel@tonic-gate 	    "hubd_restore_state_cb: Begin");
71230Sstevel@tonic-gate 
71240Sstevel@tonic-gate 	/* restore the state of this device */
71250Sstevel@tonic-gate 	hubd_restore_device_state(dip, hubd);
71260Sstevel@tonic-gate 
71270Sstevel@tonic-gate 	return (USB_SUCCESS);
71280Sstevel@tonic-gate }
71290Sstevel@tonic-gate 
71300Sstevel@tonic-gate 
71310Sstevel@tonic-gate /*
71320Sstevel@tonic-gate  * registering for events
71330Sstevel@tonic-gate  */
71340Sstevel@tonic-gate static int
hubd_register_events(hubd_t * hubd)71350Sstevel@tonic-gate hubd_register_events(hubd_t *hubd)
71360Sstevel@tonic-gate {
71370Sstevel@tonic-gate 	int		rval = USB_SUCCESS;
71380Sstevel@tonic-gate 
71390Sstevel@tonic-gate 	if (usba_is_root_hub(hubd->h_dip)) {
71400Sstevel@tonic-gate 		hubd_register_cpr_callback(hubd);
71410Sstevel@tonic-gate 	} else {
71420Sstevel@tonic-gate 		rval = usb_register_event_cbs(hubd->h_dip, &hubd_events, 0);
71430Sstevel@tonic-gate 	}
71440Sstevel@tonic-gate 
71450Sstevel@tonic-gate 	return (rval);
71460Sstevel@tonic-gate }
71470Sstevel@tonic-gate 
71480Sstevel@tonic-gate 
71490Sstevel@tonic-gate /*
71500Sstevel@tonic-gate  * hubd cpr callback related functions
71510Sstevel@tonic-gate  *
71520Sstevel@tonic-gate  * hubd_cpr_post_user_callb:
71530Sstevel@tonic-gate  *	This function is called during checkpoint & resume -
71540Sstevel@tonic-gate  *		1. after user threads are stopped during checkpoint
71550Sstevel@tonic-gate  *		2. after kernel threads are resumed during resume
71560Sstevel@tonic-gate  */
71570Sstevel@tonic-gate /* ARGSUSED */
71580Sstevel@tonic-gate static boolean_t
hubd_cpr_post_user_callb(void * arg,int code)71590Sstevel@tonic-gate hubd_cpr_post_user_callb(void *arg, int code)
71600Sstevel@tonic-gate {
71610Sstevel@tonic-gate 	hubd_cpr_t	*cpr_cb = (hubd_cpr_t *)arg;
71620Sstevel@tonic-gate 	hubd_t		*hubd = cpr_cb->statep;
71630Sstevel@tonic-gate 	int		retry = 0;
71640Sstevel@tonic-gate 
71650Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle,
71660Sstevel@tonic-gate 	    "hubd_cpr_post_user_callb");
71670Sstevel@tonic-gate 
71680Sstevel@tonic-gate 	switch (code) {
71690Sstevel@tonic-gate 	case CB_CODE_CPR_CHKPT:
71700Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_EVENTS, hubd->h_log_handle,
71710Sstevel@tonic-gate 		    "hubd_cpr_post_user_callb: CB_CODE_CPR_CHKPT");
71720Sstevel@tonic-gate 
71730Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
71740Sstevel@tonic-gate 
71750Sstevel@tonic-gate 		/* turn off deathrow thread */
71760Sstevel@tonic-gate 		hubd->h_cleanup_enabled = B_FALSE;
71770Sstevel@tonic-gate 
71780Sstevel@tonic-gate 		/* give up if deathrow thread doesn't exit */
71790Sstevel@tonic-gate 		while ((hubd->h_cleanup_active == B_TRUE) && (retry++ < 3)) {
71800Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
71810Sstevel@tonic-gate 			delay(drv_usectohz(hubd_dip_cleanup_delay));
71820Sstevel@tonic-gate 
71830Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_EVENTS, hubd->h_log_handle,
71840Sstevel@tonic-gate 			    "hubd_cpr_post_user_callb, waiting for "
71850Sstevel@tonic-gate 			    "deathrow thread to exit");
71860Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
71870Sstevel@tonic-gate 		}
71880Sstevel@tonic-gate 
71890Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
71900Sstevel@tonic-gate 
71910Sstevel@tonic-gate 		/* save the state of the device */
71920Sstevel@tonic-gate 		(void) hubd_pre_suspend_event_cb(hubd->h_dip);
71930Sstevel@tonic-gate 
71940Sstevel@tonic-gate 		return (B_TRUE);
71950Sstevel@tonic-gate 	case CB_CODE_CPR_RESUME:
71960Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_EVENTS, hubd->h_log_handle,
71970Sstevel@tonic-gate 		    "hubd_cpr_post_user_callb: CB_CODE_CPR_RESUME");
71980Sstevel@tonic-gate 
71990Sstevel@tonic-gate 		/* restore the state of the device */
72000Sstevel@tonic-gate 		(void) hubd_post_resume_event_cb(hubd->h_dip);
72010Sstevel@tonic-gate 
72020Sstevel@tonic-gate 		/* turn on deathrow thread */
72030Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
72040Sstevel@tonic-gate 		hubd->h_cleanup_enabled = B_TRUE;
72050Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
72060Sstevel@tonic-gate 
72070Sstevel@tonic-gate 		hubd_schedule_cleanup(hubd->h_usba_device->usb_root_hub_dip);
72080Sstevel@tonic-gate 
72090Sstevel@tonic-gate 		return (B_TRUE);
72100Sstevel@tonic-gate 	default:
72110Sstevel@tonic-gate 
72120Sstevel@tonic-gate 		return (B_FALSE);
72130Sstevel@tonic-gate 	}
72140Sstevel@tonic-gate 
72150Sstevel@tonic-gate }
72160Sstevel@tonic-gate 
72170Sstevel@tonic-gate 
72180Sstevel@tonic-gate /* register callback with cpr framework */
72190Sstevel@tonic-gate void
hubd_register_cpr_callback(hubd_t * hubd)72200Sstevel@tonic-gate hubd_register_cpr_callback(hubd_t *hubd)
72210Sstevel@tonic-gate {
72220Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle,
72230Sstevel@tonic-gate 	    "hubd_register_cpr_callback");
72240Sstevel@tonic-gate 
72250Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
72260Sstevel@tonic-gate 	hubd->h_cpr_cb =
72270Sstevel@tonic-gate 	    (hubd_cpr_t *)kmem_zalloc(sizeof (hubd_cpr_t), KM_SLEEP);
72280Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
72290Sstevel@tonic-gate 	mutex_init(&hubd->h_cpr_cb->lockp, NULL, MUTEX_DRIVER,
72300Sstevel@tonic-gate 	    hubd->h_dev_data->dev_iblock_cookie);
72310Sstevel@tonic-gate 	hubd->h_cpr_cb->statep = hubd;
72320Sstevel@tonic-gate 	hubd->h_cpr_cb->cpr.cc_lockp = &hubd->h_cpr_cb->lockp;
72330Sstevel@tonic-gate 	hubd->h_cpr_cb->cpr.cc_id = callb_add(hubd_cpr_post_user_callb,
72340Sstevel@tonic-gate 	    (void *)hubd->h_cpr_cb, CB_CL_CPR_POST_USER, "hubd");
72350Sstevel@tonic-gate }
72360Sstevel@tonic-gate 
72370Sstevel@tonic-gate 
72380Sstevel@tonic-gate /* unregister callback with cpr framework */
72390Sstevel@tonic-gate void
hubd_unregister_cpr_callback(hubd_t * hubd)72400Sstevel@tonic-gate hubd_unregister_cpr_callback(hubd_t *hubd)
72410Sstevel@tonic-gate {
72420Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle,
72430Sstevel@tonic-gate 	    "hubd_unregister_cpr_callback");
72440Sstevel@tonic-gate 
72450Sstevel@tonic-gate 	if (hubd->h_cpr_cb) {
72460Sstevel@tonic-gate 		(void) callb_delete(hubd->h_cpr_cb->cpr.cc_id);
72470Sstevel@tonic-gate 		mutex_destroy(&hubd->h_cpr_cb->lockp);
72480Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
72490Sstevel@tonic-gate 		kmem_free(hubd->h_cpr_cb, sizeof (hubd_cpr_t));
72500Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
72510Sstevel@tonic-gate 	}
72520Sstevel@tonic-gate }
72530Sstevel@tonic-gate 
72540Sstevel@tonic-gate 
72550Sstevel@tonic-gate /*
72560Sstevel@tonic-gate  * Power management
72570Sstevel@tonic-gate  *
72580Sstevel@tonic-gate  * create the pm components required for power management
72590Sstevel@tonic-gate  */
72600Sstevel@tonic-gate static void
hubd_create_pm_components(dev_info_t * dip,hubd_t * hubd)72610Sstevel@tonic-gate hubd_create_pm_components(dev_info_t *dip, hubd_t *hubd)
72620Sstevel@tonic-gate {
72630Sstevel@tonic-gate 	hub_power_t	*hubpm;
72640Sstevel@tonic-gate 
72650Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
72660Sstevel@tonic-gate 	    "hubd_create_pm_components: Begin");
72670Sstevel@tonic-gate 
72680Sstevel@tonic-gate 	/* Allocate the state structure */
72690Sstevel@tonic-gate 	hubpm = kmem_zalloc(sizeof (hub_power_t), KM_SLEEP);
72700Sstevel@tonic-gate 
72710Sstevel@tonic-gate 	hubd->h_hubpm = hubpm;
72720Sstevel@tonic-gate 	hubpm->hubp_hubd = hubd;
72730Sstevel@tonic-gate 	hubpm->hubp_pm_capabilities = 0;
72740Sstevel@tonic-gate 	hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR;
72750Sstevel@tonic-gate 	hubpm->hubp_time_at_full_power = ddi_get_time();
72760Sstevel@tonic-gate 	hubpm->hubp_min_pm_threshold = hubdi_min_pm_threshold;
72770Sstevel@tonic-gate 
72780Sstevel@tonic-gate 	/* alloc memory to save power states of children */
72790Sstevel@tonic-gate 	hubpm->hubp_child_pwrstate = (uint8_t *)
72804763Slg150142 	    kmem_zalloc(MAX_PORTS + 1, KM_SLEEP);
72810Sstevel@tonic-gate 
72820Sstevel@tonic-gate 	/*
72830Sstevel@tonic-gate 	 * if the enable remote wakeup fails
72840Sstevel@tonic-gate 	 * we still want to enable
72850Sstevel@tonic-gate 	 * parent notification so we can PM the children
72860Sstevel@tonic-gate 	 */
72870Sstevel@tonic-gate 	usb_enable_parent_notification(dip);
72880Sstevel@tonic-gate 
72890Sstevel@tonic-gate 	if (usb_handle_remote_wakeup(dip,
72900Sstevel@tonic-gate 	    USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) {
72910Sstevel@tonic-gate 		uint_t		pwr_states;
72920Sstevel@tonic-gate 
72930Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
72940Sstevel@tonic-gate 		    "hubd_create_pm_components: "
72950Sstevel@tonic-gate 		    "Remote Wakeup Enabled");
72960Sstevel@tonic-gate 
72970Sstevel@tonic-gate 		if (usb_create_pm_components(dip, &pwr_states) ==
72980Sstevel@tonic-gate 		    USB_SUCCESS) {
72990Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
73000Sstevel@tonic-gate 			hubpm->hubp_wakeup_enabled = 1;
73010Sstevel@tonic-gate 			hubpm->hubp_pwr_states = (uint8_t)pwr_states;
73020Sstevel@tonic-gate 
73030Sstevel@tonic-gate 			/* we are busy now till end of the attach */
73040Sstevel@tonic-gate 			hubd_pm_busy_component(hubd, dip, 0);
73050Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
73060Sstevel@tonic-gate 
73070Sstevel@tonic-gate 			/* bring the device to full power */
73080Sstevel@tonic-gate 			(void) pm_raise_power(dip, 0,
73090Sstevel@tonic-gate 			    USB_DEV_OS_FULL_PWR);
73100Sstevel@tonic-gate 		}
73110Sstevel@tonic-gate 	}
73120Sstevel@tonic-gate 
73130Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
73140Sstevel@tonic-gate 	    "hubd_create_pm_components: END");
73150Sstevel@tonic-gate }
73160Sstevel@tonic-gate 
73170Sstevel@tonic-gate 
73180Sstevel@tonic-gate /*
73190Sstevel@tonic-gate  * Attachment point management
73200Sstevel@tonic-gate  */
73210Sstevel@tonic-gate /* ARGSUSED */
73220Sstevel@tonic-gate int
usba_hubdi_open(dev_info_t * dip,dev_t * devp,int flags,int otyp,cred_t * credp)73230Sstevel@tonic-gate usba_hubdi_open(dev_info_t *dip, dev_t *devp, int flags, int otyp,
73240Sstevel@tonic-gate 	cred_t *credp)
73250Sstevel@tonic-gate {
73260Sstevel@tonic-gate 	hubd_t *hubd;
73270Sstevel@tonic-gate 
73280Sstevel@tonic-gate 	if (otyp != OTYP_CHR)
73290Sstevel@tonic-gate 		return (EINVAL);
73300Sstevel@tonic-gate 
73310Sstevel@tonic-gate 	hubd = hubd_get_soft_state(dip);
73320Sstevel@tonic-gate 	if (hubd == NULL) {
73330Sstevel@tonic-gate 		return (ENXIO);
73340Sstevel@tonic-gate 	}
73350Sstevel@tonic-gate 
73360Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
73370Sstevel@tonic-gate 	    "hubd_open:");
73380Sstevel@tonic-gate 
73390Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
73400Sstevel@tonic-gate 	if ((flags & FEXCL) && (hubd->h_softstate & HUBD_SS_ISOPEN)) {
73410Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
73420Sstevel@tonic-gate 
73430Sstevel@tonic-gate 		return (EBUSY);
73440Sstevel@tonic-gate 	}
73450Sstevel@tonic-gate 
73460Sstevel@tonic-gate 	hubd->h_softstate |= HUBD_SS_ISOPEN;
73470Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
73480Sstevel@tonic-gate 
73490Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, "opened");
73500Sstevel@tonic-gate 
73510Sstevel@tonic-gate 	return (0);
73520Sstevel@tonic-gate }
73530Sstevel@tonic-gate 
73540Sstevel@tonic-gate 
73550Sstevel@tonic-gate /* ARGSUSED */
73560Sstevel@tonic-gate int
usba_hubdi_close(dev_info_t * dip,dev_t dev,int flag,int otyp,cred_t * credp)73570Sstevel@tonic-gate usba_hubdi_close(dev_info_t *dip, dev_t dev, int flag, int otyp,
73580Sstevel@tonic-gate 	cred_t *credp)
73590Sstevel@tonic-gate {
73600Sstevel@tonic-gate 	hubd_t *hubd;
73610Sstevel@tonic-gate 
73620Sstevel@tonic-gate 	if (otyp != OTYP_CHR) {
73630Sstevel@tonic-gate 		return (EINVAL);
73640Sstevel@tonic-gate 	}
73650Sstevel@tonic-gate 
73660Sstevel@tonic-gate 	hubd = hubd_get_soft_state(dip);
73670Sstevel@tonic-gate 
73680Sstevel@tonic-gate 	if (hubd == NULL) {
73690Sstevel@tonic-gate 		return (ENXIO);
73700Sstevel@tonic-gate 	}
73710Sstevel@tonic-gate 
73720Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, "hubd_close:");
73730Sstevel@tonic-gate 
73740Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
73750Sstevel@tonic-gate 	hubd->h_softstate &= ~HUBD_SS_ISOPEN;
73760Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
73770Sstevel@tonic-gate 
73780Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, "closed");
73790Sstevel@tonic-gate 
73800Sstevel@tonic-gate 	return (0);
73810Sstevel@tonic-gate }
73820Sstevel@tonic-gate 
73830Sstevel@tonic-gate 
73840Sstevel@tonic-gate /*
73850Sstevel@tonic-gate  * hubd_ioctl: cfgadm controls
73860Sstevel@tonic-gate  */
73870Sstevel@tonic-gate /* ARGSUSED */
73880Sstevel@tonic-gate int
usba_hubdi_ioctl(dev_info_t * self,dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)73890Sstevel@tonic-gate usba_hubdi_ioctl(dev_info_t *self, dev_t dev, int cmd, intptr_t arg,
73900Sstevel@tonic-gate 	int mode, cred_t *credp, int *rvalp)
73910Sstevel@tonic-gate {
73920Sstevel@tonic-gate 	int			rv = 0;
73930Sstevel@tonic-gate 	char			*msg;	/* for messages */
73940Sstevel@tonic-gate 	hubd_t			*hubd;
73950Sstevel@tonic-gate 	usb_port_t		port = 0;
73960Sstevel@tonic-gate 	dev_info_t		*child_dip = NULL;
73970Sstevel@tonic-gate 	dev_info_t		*rh_dip;
73980Sstevel@tonic-gate 	devctl_ap_state_t	ap_state;
73990Sstevel@tonic-gate 	struct devctl_iocdata	*dcp = NULL;
74000Sstevel@tonic-gate 	usb_pipe_state_t	prev_pipe_state = 0;
74010Sstevel@tonic-gate 	int			circ, rh_circ, prh_circ;
74020Sstevel@tonic-gate 
74030Sstevel@tonic-gate 	if ((hubd = hubd_get_soft_state(self)) == NULL) {
74040Sstevel@tonic-gate 
74050Sstevel@tonic-gate 		return (ENXIO);
74060Sstevel@tonic-gate 	}
74070Sstevel@tonic-gate 
74080Sstevel@tonic-gate 	rh_dip = hubd->h_usba_device->usb_root_hub_dip;
74090Sstevel@tonic-gate 
74100Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
74110Sstevel@tonic-gate 	    "usba_hubdi_ioctl: "
74120Sstevel@tonic-gate 	    "cmd=%x, arg=%lx, mode=%x, cred=%p, rval=%p dev=0x%lx",
74136898Sfb209375 	    cmd, arg, mode, (void *)credp, (void *)rvalp, dev);
74140Sstevel@tonic-gate 
74150Sstevel@tonic-gate 	/* read devctl ioctl data */
74160Sstevel@tonic-gate 	if ((cmd != DEVCTL_AP_CONTROL) &&
74170Sstevel@tonic-gate 	    (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)) {
74180Sstevel@tonic-gate 
74190Sstevel@tonic-gate 		return (EFAULT);
74200Sstevel@tonic-gate 	}
74210Sstevel@tonic-gate 
74220Sstevel@tonic-gate 	/*
74230Sstevel@tonic-gate 	 * make sure the hub is connected before trying any
74240Sstevel@tonic-gate 	 * of the following operations:
74250Sstevel@tonic-gate 	 * configure, connect, disconnect
74260Sstevel@tonic-gate 	 */
74270Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
74280Sstevel@tonic-gate 
74290Sstevel@tonic-gate 	switch (cmd) {
74300Sstevel@tonic-gate 	case DEVCTL_AP_DISCONNECT:
74310Sstevel@tonic-gate 	case DEVCTL_AP_UNCONFIGURE:
74320Sstevel@tonic-gate 	case DEVCTL_AP_CONFIGURE:
74330Sstevel@tonic-gate 		if (hubd->h_dev_state == USB_DEV_DISCONNECTED) {
74340Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
74350Sstevel@tonic-gate 			    "hubd: already gone");
74360Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
74370Sstevel@tonic-gate 			if (dcp) {
74380Sstevel@tonic-gate 				ndi_dc_freehdl(dcp);
74390Sstevel@tonic-gate 			}
74400Sstevel@tonic-gate 
74410Sstevel@tonic-gate 			return (EIO);
74420Sstevel@tonic-gate 		}
74430Sstevel@tonic-gate 
74440Sstevel@tonic-gate 		/* FALLTHROUGH */
74450Sstevel@tonic-gate 	case DEVCTL_AP_GETSTATE:
74460Sstevel@tonic-gate 		if ((port = hubd_get_port_num(hubd, dcp)) == 0) {
74470Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
74480Sstevel@tonic-gate 			    "hubd: bad port");
74490Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
74500Sstevel@tonic-gate 			if (dcp) {
74510Sstevel@tonic-gate 				ndi_dc_freehdl(dcp);
74520Sstevel@tonic-gate 			}
74530Sstevel@tonic-gate 
74540Sstevel@tonic-gate 			return (EINVAL);
74550Sstevel@tonic-gate 		}
74560Sstevel@tonic-gate 		break;
74570Sstevel@tonic-gate 
74580Sstevel@tonic-gate 	case DEVCTL_AP_CONTROL:
74590Sstevel@tonic-gate 
74600Sstevel@tonic-gate 		break;
74610Sstevel@tonic-gate 	default:
74620Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
74630Sstevel@tonic-gate 		if (dcp) {
74640Sstevel@tonic-gate 			ndi_dc_freehdl(dcp);
74650Sstevel@tonic-gate 		}
74660Sstevel@tonic-gate 
74670Sstevel@tonic-gate 		return (ENOTTY);
74680Sstevel@tonic-gate 	}
74690Sstevel@tonic-gate 
74700Sstevel@tonic-gate 	/* should not happen, just in case */
74710Sstevel@tonic-gate 	if (hubd->h_dev_state == USB_DEV_SUSPENDED) {
74720Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
74730Sstevel@tonic-gate 		if (dcp) {
74740Sstevel@tonic-gate 			ndi_dc_freehdl(dcp);
74750Sstevel@tonic-gate 		}
74760Sstevel@tonic-gate 
74770Sstevel@tonic-gate 		return (EIO);
74780Sstevel@tonic-gate 	}
74790Sstevel@tonic-gate 
74804844Slg150142 	if (hubd->h_reset_port[port]) {
74814844Slg150142 		USB_DPRINTF_L2(DPRINT_MASK_CBOPS, hubd->h_log_handle,
74824844Slg150142 		    "This port is resetting, just return");
74834844Slg150142 		mutex_exit(HUBD_MUTEX(hubd));
74844844Slg150142 		if (dcp) {
74854844Slg150142 			ndi_dc_freehdl(dcp);
74864844Slg150142 		}
74874844Slg150142 
74884844Slg150142 		return (EIO);
74894844Slg150142 	}
74904844Slg150142 
74910Sstevel@tonic-gate 	hubd_pm_busy_component(hubd, hubd->h_dip, 0);
74920Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
74930Sstevel@tonic-gate 
74940Sstevel@tonic-gate 	/* go full power */
74950Sstevel@tonic-gate 	(void) pm_raise_power(hubd->h_dip, 0, USB_DEV_OS_FULL_PWR);
74960Sstevel@tonic-gate 
74970Sstevel@tonic-gate 	ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
74980Sstevel@tonic-gate 	ndi_devi_enter(rh_dip, &rh_circ);
74990Sstevel@tonic-gate 	ndi_devi_enter(hubd->h_dip, &circ);
75000Sstevel@tonic-gate 
75010Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
75020Sstevel@tonic-gate 
75034763Slg150142 	hubd->h_hotplug_thread++;
75044763Slg150142 
75054763Slg150142 	/* stop polling if it was active */
75060Sstevel@tonic-gate 	if (hubd->h_ep1_ph) {
75070Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
75080Sstevel@tonic-gate 		(void) usb_pipe_get_state(hubd->h_ep1_ph, &prev_pipe_state,
75094763Slg150142 		    USB_FLAGS_SLEEP);
75100Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
75110Sstevel@tonic-gate 
75120Sstevel@tonic-gate 		if (prev_pipe_state == USB_PIPE_STATE_ACTIVE) {
75130Sstevel@tonic-gate 			hubd_stop_polling(hubd);
75140Sstevel@tonic-gate 		}
75150Sstevel@tonic-gate 	}
75160Sstevel@tonic-gate 
75170Sstevel@tonic-gate 	switch (cmd) {
75180Sstevel@tonic-gate 	case DEVCTL_AP_DISCONNECT:
75190Sstevel@tonic-gate 		if (hubd_delete_child(hubd, port,
75200Sstevel@tonic-gate 		    NDI_DEVI_REMOVE, B_FALSE) != USB_SUCCESS) {
75210Sstevel@tonic-gate 			rv = EIO;
75220Sstevel@tonic-gate 		}
75230Sstevel@tonic-gate 
75240Sstevel@tonic-gate 		break;
75250Sstevel@tonic-gate 	case DEVCTL_AP_UNCONFIGURE:
75260Sstevel@tonic-gate 		if (hubd_delete_child(hubd, port,
75270Sstevel@tonic-gate 		    NDI_UNCONFIG, B_FALSE) != USB_SUCCESS) {
75280Sstevel@tonic-gate 			rv = EIO;
75290Sstevel@tonic-gate 		}
75300Sstevel@tonic-gate 
75310Sstevel@tonic-gate 		break;
75320Sstevel@tonic-gate 	case DEVCTL_AP_CONFIGURE:
75330Sstevel@tonic-gate 		/* toggle port */
75340Sstevel@tonic-gate 		if (hubd_toggle_port(hubd, port) != USB_SUCCESS) {
75350Sstevel@tonic-gate 			rv = EIO;
75360Sstevel@tonic-gate 
75370Sstevel@tonic-gate 			break;
75380Sstevel@tonic-gate 		}
75390Sstevel@tonic-gate 
75400Sstevel@tonic-gate 		(void) hubd_handle_port_connect(hubd, port);
75410Sstevel@tonic-gate 		child_dip = hubd_get_child_dip(hubd, port);
75420Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
75430Sstevel@tonic-gate 
75440Sstevel@tonic-gate 		ndi_devi_exit(hubd->h_dip, circ);
75450Sstevel@tonic-gate 		ndi_devi_exit(rh_dip, rh_circ);
75460Sstevel@tonic-gate 		ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ);
754711214SVincent.Wang@Sun.COM 		if (child_dip == NULL) {
75480Sstevel@tonic-gate 			rv = EIO;
754911214SVincent.Wang@Sun.COM 		} else {
755011214SVincent.Wang@Sun.COM 			ndi_hold_devi(child_dip);
755111214SVincent.Wang@Sun.COM 			if (ndi_devi_online(child_dip, 0) != NDI_SUCCESS)
755211214SVincent.Wang@Sun.COM 				rv = EIO;
755311214SVincent.Wang@Sun.COM 			ndi_rele_devi(child_dip);
75540Sstevel@tonic-gate 		}
75550Sstevel@tonic-gate 		ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
75560Sstevel@tonic-gate 		ndi_devi_enter(rh_dip, &rh_circ);
75570Sstevel@tonic-gate 		ndi_devi_enter(hubd->h_dip, &circ);
75580Sstevel@tonic-gate 
75590Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
75600Sstevel@tonic-gate 
75610Sstevel@tonic-gate 		break;
75620Sstevel@tonic-gate 	case DEVCTL_AP_GETSTATE:
75630Sstevel@tonic-gate 		switch (hubd_cfgadm_state(hubd, port)) {
75640Sstevel@tonic-gate 		case HUBD_CFGADM_DISCONNECTED:
75650Sstevel@tonic-gate 			/* port previously 'disconnected' by cfgadm */
75660Sstevel@tonic-gate 			ap_state.ap_rstate = AP_RSTATE_DISCONNECTED;
75670Sstevel@tonic-gate 			ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED;
75680Sstevel@tonic-gate 			ap_state.ap_condition = AP_COND_OK;
75690Sstevel@tonic-gate 
75700Sstevel@tonic-gate 			break;
75710Sstevel@tonic-gate 		case HUBD_CFGADM_UNCONFIGURED:
75720Sstevel@tonic-gate 			ap_state.ap_rstate = AP_RSTATE_CONNECTED;
75730Sstevel@tonic-gate 			ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED;
75740Sstevel@tonic-gate 			ap_state.ap_condition = AP_COND_OK;
75750Sstevel@tonic-gate 
75760Sstevel@tonic-gate 			break;
75770Sstevel@tonic-gate 		case HUBD_CFGADM_CONFIGURED:
75780Sstevel@tonic-gate 			ap_state.ap_rstate = AP_RSTATE_CONNECTED;
75790Sstevel@tonic-gate 			ap_state.ap_ostate = AP_OSTATE_CONFIGURED;
75800Sstevel@tonic-gate 			ap_state.ap_condition = AP_COND_OK;
75810Sstevel@tonic-gate 
75820Sstevel@tonic-gate 			break;
75830Sstevel@tonic-gate 		case HUBD_CFGADM_STILL_REFERENCED:
75840Sstevel@tonic-gate 			ap_state.ap_rstate = AP_RSTATE_EMPTY;
75850Sstevel@tonic-gate 			ap_state.ap_ostate = AP_OSTATE_CONFIGURED;
75860Sstevel@tonic-gate 			ap_state.ap_condition = AP_COND_UNUSABLE;
75870Sstevel@tonic-gate 
75880Sstevel@tonic-gate 			break;
75890Sstevel@tonic-gate 		case HUBD_CFGADM_EMPTY:
75900Sstevel@tonic-gate 		default:
75910Sstevel@tonic-gate 			ap_state.ap_rstate = AP_RSTATE_EMPTY;
75920Sstevel@tonic-gate 			ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED;
75930Sstevel@tonic-gate 			ap_state.ap_condition = AP_COND_OK;
75940Sstevel@tonic-gate 
75950Sstevel@tonic-gate 			break;
75960Sstevel@tonic-gate 		}
75970Sstevel@tonic-gate 
75980Sstevel@tonic-gate 		ap_state.ap_last_change = (time_t)-1;
75990Sstevel@tonic-gate 		ap_state.ap_error_code = 0;
76000Sstevel@tonic-gate 		ap_state.ap_in_transition = 0;
76010Sstevel@tonic-gate 
76020Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
76030Sstevel@tonic-gate 		    "DEVCTL_AP_GETSTATE: "
76040Sstevel@tonic-gate 		    "ostate=0x%x, rstate=0x%x, condition=0x%x",
76050Sstevel@tonic-gate 		    ap_state.ap_ostate,
76060Sstevel@tonic-gate 		    ap_state.ap_rstate, ap_state.ap_condition);
76070Sstevel@tonic-gate 
76080Sstevel@tonic-gate 		/* copy the return-AP-state information to the user space */
76090Sstevel@tonic-gate 		if (ndi_dc_return_ap_state(&ap_state, dcp) != NDI_SUCCESS) {
76100Sstevel@tonic-gate 			rv = EFAULT;
76110Sstevel@tonic-gate 		}
76120Sstevel@tonic-gate 
76130Sstevel@tonic-gate 		break;
76140Sstevel@tonic-gate 	case DEVCTL_AP_CONTROL:
76150Sstevel@tonic-gate 	{
76160Sstevel@tonic-gate 		/*
76170Sstevel@tonic-gate 		 * Generic devctl for hardware-specific functionality.
76180Sstevel@tonic-gate 		 * For list of sub-commands see hubd_impl.h
76190Sstevel@tonic-gate 		 */
76200Sstevel@tonic-gate 		hubd_ioctl_data_t	ioc;	/* for 64 byte copies */
76210Sstevel@tonic-gate 
76220Sstevel@tonic-gate 		/* copy user ioctl data in first */
76230Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
76240Sstevel@tonic-gate 		if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
76250Sstevel@tonic-gate 			hubd_ioctl_data_32_t ioc32;
76260Sstevel@tonic-gate 
76270Sstevel@tonic-gate 			if (ddi_copyin((void *)arg, (void *)&ioc32,
76284763Slg150142 			    sizeof (ioc32), mode) != 0) {
76290Sstevel@tonic-gate 				rv = EFAULT;
76300Sstevel@tonic-gate 
76310Sstevel@tonic-gate 				break;
76320Sstevel@tonic-gate 			}
76330Sstevel@tonic-gate 			ioc.cmd		= (uint_t)ioc32.cmd;
76340Sstevel@tonic-gate 			ioc.port	= (uint_t)ioc32.port;
76350Sstevel@tonic-gate 			ioc.get_size	= (uint_t)ioc32.get_size;
76360Sstevel@tonic-gate 			ioc.buf		= (caddr_t)(uintptr_t)ioc32.buf;
76370Sstevel@tonic-gate 			ioc.bufsiz	= (uint_t)ioc32.bufsiz;
76380Sstevel@tonic-gate 			ioc.misc_arg	= (uint_t)ioc32.misc_arg;
76390Sstevel@tonic-gate 		} else
76400Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
76410Sstevel@tonic-gate 		if (ddi_copyin((void *)arg, (void *)&ioc, sizeof (ioc),
76420Sstevel@tonic-gate 		    mode) != 0) {
76430Sstevel@tonic-gate 			rv = EFAULT;
76440Sstevel@tonic-gate 
76450Sstevel@tonic-gate 			break;
76460Sstevel@tonic-gate 		}
76470Sstevel@tonic-gate 
76480Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_CBOPS, hubd->h_log_handle,
76490Sstevel@tonic-gate 		    "DEVCTL_AP_CONTROL: ioc: cmd=0x%x port=%d get_size=%d"
76500Sstevel@tonic-gate 		    "\n\tbuf=0x%p, bufsiz=%d,  misc_arg=%d", ioc.cmd,
76516898Sfb209375 		    ioc.port, ioc.get_size, (void *)ioc.buf, ioc.bufsiz,
76526898Sfb209375 		    ioc.misc_arg);
76530Sstevel@tonic-gate 
76540Sstevel@tonic-gate 		/*
76550Sstevel@tonic-gate 		 * To avoid BE/LE and 32/64 issues, a get_size always
76560Sstevel@tonic-gate 		 * returns a 32-bit number.
76570Sstevel@tonic-gate 		 */
76580Sstevel@tonic-gate 		if (ioc.get_size != 0 && ioc.bufsiz != (sizeof (uint32_t))) {
76590Sstevel@tonic-gate 			rv = EINVAL;
76600Sstevel@tonic-gate 
76610Sstevel@tonic-gate 			break;
76620Sstevel@tonic-gate 		}
76630Sstevel@tonic-gate 
76640Sstevel@tonic-gate 		switch (ioc.cmd) {
76650Sstevel@tonic-gate 		case USB_DESCR_TYPE_DEV:
76660Sstevel@tonic-gate 			msg = "DEVCTL_AP_CONTROL: GET_DEVICE_DESC";
76670Sstevel@tonic-gate 			if (ioc.get_size) {
76680Sstevel@tonic-gate 				/* uint32 so this works 32/64 */
76690Sstevel@tonic-gate 				uint32_t size = sizeof (usb_dev_descr_t);
76700Sstevel@tonic-gate 
76710Sstevel@tonic-gate 				if (ddi_copyout((void *)&size, ioc.buf,
76720Sstevel@tonic-gate 				    ioc.bufsiz, mode) != 0) {
76730Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
76740Sstevel@tonic-gate 					    hubd->h_log_handle,
76750Sstevel@tonic-gate 					    "%s: get_size copyout failed", msg);
76760Sstevel@tonic-gate 					rv = EIO;
76770Sstevel@tonic-gate 
76780Sstevel@tonic-gate 					break;
76790Sstevel@tonic-gate 				}
76800Sstevel@tonic-gate 			} else {	/* send out the actual descr */
76810Sstevel@tonic-gate 				usb_dev_descr_t *dev_descrp;
76820Sstevel@tonic-gate 
76830Sstevel@tonic-gate 				/* check child_dip */
76840Sstevel@tonic-gate 				if ((child_dip = hubd_get_child_dip(hubd,
76850Sstevel@tonic-gate 				    ioc.port)) == NULL) {
76860Sstevel@tonic-gate 					rv = EINVAL;
76870Sstevel@tonic-gate 
76880Sstevel@tonic-gate 					break;
76890Sstevel@tonic-gate 				}
76900Sstevel@tonic-gate 
76910Sstevel@tonic-gate 				dev_descrp = usb_get_dev_descr(child_dip);
76920Sstevel@tonic-gate 				if (ioc.bufsiz != sizeof (*dev_descrp)) {
76930Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
76940Sstevel@tonic-gate 					    hubd->h_log_handle,
76950Sstevel@tonic-gate 					    "%s: bufsize passed (%d) != sizeof "
76960Sstevel@tonic-gate 					    "usba_device_descr_t (%d)", msg,
76970Sstevel@tonic-gate 					    ioc.bufsiz, dev_descrp->bLength);
76980Sstevel@tonic-gate 					rv = EINVAL;
76990Sstevel@tonic-gate 
77000Sstevel@tonic-gate 					break;
77010Sstevel@tonic-gate 				}
77020Sstevel@tonic-gate 
77030Sstevel@tonic-gate 				if (ddi_copyout((void *)dev_descrp,
77040Sstevel@tonic-gate 				    ioc.buf, ioc.bufsiz, mode) != 0) {
77050Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
77060Sstevel@tonic-gate 					    hubd->h_log_handle,
77070Sstevel@tonic-gate 					    "%s: copyout failed.", msg);
77080Sstevel@tonic-gate 					rv = EIO;
77090Sstevel@tonic-gate 
77100Sstevel@tonic-gate 					break;
77110Sstevel@tonic-gate 				}
77120Sstevel@tonic-gate 			}
77130Sstevel@tonic-gate 			break;
77140Sstevel@tonic-gate 		case USB_DESCR_TYPE_STRING:
77150Sstevel@tonic-gate 		{
77160Sstevel@tonic-gate 			char		*str;
77170Sstevel@tonic-gate 			uint32_t	size;
77180Sstevel@tonic-gate 			usba_device_t	*usba_device;
77190Sstevel@tonic-gate 
77200Sstevel@tonic-gate 			msg = "DEVCTL_AP_CONTROL: GET_STRING_DESCR";
77210Sstevel@tonic-gate 			USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
77220Sstevel@tonic-gate 			    "%s: string request: %d", msg, ioc.misc_arg);
77230Sstevel@tonic-gate 
77240Sstevel@tonic-gate 			/* recheck */
77250Sstevel@tonic-gate 			if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) ==
77260Sstevel@tonic-gate 			    NULL) {
77270Sstevel@tonic-gate 				rv = EINVAL;
77280Sstevel@tonic-gate 
77290Sstevel@tonic-gate 				break;
77300Sstevel@tonic-gate 			}
77310Sstevel@tonic-gate 			usba_device = usba_get_usba_device(child_dip);
77320Sstevel@tonic-gate 
77330Sstevel@tonic-gate 			switch (ioc.misc_arg) {
77340Sstevel@tonic-gate 			case HUBD_MFG_STR:
77350Sstevel@tonic-gate 				str = usba_device->usb_mfg_str;
77360Sstevel@tonic-gate 
77370Sstevel@tonic-gate 				break;
77380Sstevel@tonic-gate 			case HUBD_PRODUCT_STR:
77390Sstevel@tonic-gate 				str = usba_device->usb_product_str;
77400Sstevel@tonic-gate 
77410Sstevel@tonic-gate 				break;
77420Sstevel@tonic-gate 			case HUBD_SERIALNO_STR:
77430Sstevel@tonic-gate 				str = usba_device->usb_serialno_str;
77440Sstevel@tonic-gate 
77450Sstevel@tonic-gate 				break;
77460Sstevel@tonic-gate 			case HUBD_CFG_DESCR_STR:
77470Sstevel@tonic-gate 				mutex_enter(&usba_device->usb_mutex);
77480Sstevel@tonic-gate 				str = usba_device->usb_cfg_str_descr[
77494763Slg150142 				    usba_device->usb_active_cfg_ndx];
77500Sstevel@tonic-gate 				mutex_exit(&usba_device->usb_mutex);
77510Sstevel@tonic-gate 
77520Sstevel@tonic-gate 				break;
77530Sstevel@tonic-gate 			default:
77540Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
77550Sstevel@tonic-gate 				    hubd->h_log_handle,
77560Sstevel@tonic-gate 				    "%s: Invalid string request", msg);
77570Sstevel@tonic-gate 				rv = EINVAL;
77580Sstevel@tonic-gate 
77590Sstevel@tonic-gate 				break;
77600Sstevel@tonic-gate 			} /* end of switch */
77610Sstevel@tonic-gate 
77620Sstevel@tonic-gate 			if (rv != 0) {
77630Sstevel@tonic-gate 
77640Sstevel@tonic-gate 				break;
77650Sstevel@tonic-gate 			}
77660Sstevel@tonic-gate 
77670Sstevel@tonic-gate 			size = (str != NULL) ? strlen(str) + 1 : 0;
77680Sstevel@tonic-gate 			if (ioc.get_size) {
77690Sstevel@tonic-gate 				if (ddi_copyout((void *)&size, ioc.buf,
77700Sstevel@tonic-gate 				    ioc.bufsiz, mode) != 0) {
77710Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
77720Sstevel@tonic-gate 					    hubd->h_log_handle,
77730Sstevel@tonic-gate 					    "%s: copyout of size failed.", msg);
77740Sstevel@tonic-gate 					rv = EIO;
77750Sstevel@tonic-gate 
77760Sstevel@tonic-gate 					break;
77770Sstevel@tonic-gate 				}
77780Sstevel@tonic-gate 			} else {
77790Sstevel@tonic-gate 				if (size == 0) {
77800Sstevel@tonic-gate 					USB_DPRINTF_L3(DPRINT_MASK_CBOPS,
77810Sstevel@tonic-gate 					    hubd->h_log_handle,
77820Sstevel@tonic-gate 					    "%s: String is NULL", msg);
77830Sstevel@tonic-gate 					rv = EINVAL;
77840Sstevel@tonic-gate 
77850Sstevel@tonic-gate 					break;
77860Sstevel@tonic-gate 				}
77870Sstevel@tonic-gate 
77880Sstevel@tonic-gate 				if (ioc.bufsiz != size) {
77890Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
77900Sstevel@tonic-gate 					    hubd->h_log_handle,
77910Sstevel@tonic-gate 					    "%s: string buf size wrong", msg);
77920Sstevel@tonic-gate 					rv = EINVAL;
77930Sstevel@tonic-gate 
77940Sstevel@tonic-gate 					break;
77950Sstevel@tonic-gate 				}
77960Sstevel@tonic-gate 
77970Sstevel@tonic-gate 				if (ddi_copyout((void *)str, ioc.buf,
77980Sstevel@tonic-gate 				    ioc.bufsiz, mode) != 0) {
77990Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
78000Sstevel@tonic-gate 					    hubd->h_log_handle,
78010Sstevel@tonic-gate 					    "%s: copyout failed.", msg);
78020Sstevel@tonic-gate 					rv = EIO;
78030Sstevel@tonic-gate 
78040Sstevel@tonic-gate 					break;
78050Sstevel@tonic-gate 				}
78060Sstevel@tonic-gate 			}
78070Sstevel@tonic-gate 			break;
78080Sstevel@tonic-gate 		}
78090Sstevel@tonic-gate 		case HUBD_GET_CFGADM_NAME:
78100Sstevel@tonic-gate 		{
78110Sstevel@tonic-gate 			uint32_t   name_len;
78120Sstevel@tonic-gate 			const char *name;
78130Sstevel@tonic-gate 
78140Sstevel@tonic-gate 			/* recheck */
78150Sstevel@tonic-gate 			if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) ==
78160Sstevel@tonic-gate 			    NULL) {
78170Sstevel@tonic-gate 				rv = EINVAL;
78180Sstevel@tonic-gate 
78190Sstevel@tonic-gate 				break;
78200Sstevel@tonic-gate 			}
78210Sstevel@tonic-gate 			name = ddi_node_name(child_dip);
78220Sstevel@tonic-gate 			if (name == NULL) {
78230Sstevel@tonic-gate 				name = "unsupported";
78240Sstevel@tonic-gate 			}
78250Sstevel@tonic-gate 			name_len = strlen(name) + 1;
78260Sstevel@tonic-gate 
78270Sstevel@tonic-gate 			msg = "DEVCTL_AP_CONTROL: HUBD_GET_CFGADM_NAME";
78280Sstevel@tonic-gate 			USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
78290Sstevel@tonic-gate 			    "%s: name=%s name_len=%d", msg, name, name_len);
78300Sstevel@tonic-gate 
78310Sstevel@tonic-gate 			if (ioc.get_size) {
78320Sstevel@tonic-gate 				if (ddi_copyout((void *)&name_len,
78330Sstevel@tonic-gate 				    ioc.buf, ioc.bufsiz, mode) != 0) {
78340Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
78350Sstevel@tonic-gate 					    hubd->h_log_handle,
78360Sstevel@tonic-gate 					    "%s: copyout of size failed", msg);
78370Sstevel@tonic-gate 					rv = EIO;
78380Sstevel@tonic-gate 
78390Sstevel@tonic-gate 					break;
78400Sstevel@tonic-gate 				}
78410Sstevel@tonic-gate 			} else {
78420Sstevel@tonic-gate 				if (ioc.bufsiz != name_len) {
78430Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
78440Sstevel@tonic-gate 					    hubd->h_log_handle,
78450Sstevel@tonic-gate 					    "%s: string buf length wrong", msg);
78460Sstevel@tonic-gate 					rv = EINVAL;
78470Sstevel@tonic-gate 
78480Sstevel@tonic-gate 					break;
78490Sstevel@tonic-gate 				}
78500Sstevel@tonic-gate 
78510Sstevel@tonic-gate 				if (ddi_copyout((void *)name, ioc.buf,
78520Sstevel@tonic-gate 				    ioc.bufsiz, mode) != 0) {
78530Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
78540Sstevel@tonic-gate 					    hubd->h_log_handle,
78550Sstevel@tonic-gate 					    "%s: copyout failed.", msg);
78560Sstevel@tonic-gate 					rv = EIO;
78570Sstevel@tonic-gate 
78580Sstevel@tonic-gate 					break;
78590Sstevel@tonic-gate 				}
78600Sstevel@tonic-gate 			}
78610Sstevel@tonic-gate 
78620Sstevel@tonic-gate 			break;
78630Sstevel@tonic-gate 		}
78640Sstevel@tonic-gate 
78650Sstevel@tonic-gate 		/*
78660Sstevel@tonic-gate 		 * Return the config index for the currently-configured
78670Sstevel@tonic-gate 		 * configuration.
78680Sstevel@tonic-gate 		 */
78690Sstevel@tonic-gate 		case HUBD_GET_CURRENT_CONFIG:
78700Sstevel@tonic-gate 		{
78710Sstevel@tonic-gate 			uint_t		config_index;
78720Sstevel@tonic-gate 			uint32_t	size = sizeof (config_index);
78730Sstevel@tonic-gate 			usba_device_t	*usba_device;
78740Sstevel@tonic-gate 
78750Sstevel@tonic-gate 			msg = "DEVCTL_AP_CONTROL: GET_CURRENT_CONFIG";
78760Sstevel@tonic-gate 			USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
78770Sstevel@tonic-gate 			    "%s", msg);
78780Sstevel@tonic-gate 
78790Sstevel@tonic-gate 			/*
78800Sstevel@tonic-gate 			 * Return the config index for the configuration
78810Sstevel@tonic-gate 			 * currently in use.
78820Sstevel@tonic-gate 			 * Recheck if child_dip exists
78830Sstevel@tonic-gate 			 */
78840Sstevel@tonic-gate 			if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) ==
78850Sstevel@tonic-gate 			    NULL) {
78860Sstevel@tonic-gate 				rv = EINVAL;
78870Sstevel@tonic-gate 
78880Sstevel@tonic-gate 				break;
78890Sstevel@tonic-gate 			}
78900Sstevel@tonic-gate 
78910Sstevel@tonic-gate 			usba_device = usba_get_usba_device(child_dip);
78920Sstevel@tonic-gate 			mutex_enter(&usba_device->usb_mutex);
78930Sstevel@tonic-gate 			config_index = usba_device->usb_active_cfg_ndx;
78940Sstevel@tonic-gate 			mutex_exit(&usba_device->usb_mutex);
78950Sstevel@tonic-gate 
78960Sstevel@tonic-gate 			if (ioc.get_size) {
78970Sstevel@tonic-gate 				if (ddi_copyout((void *)&size,
78980Sstevel@tonic-gate 				    ioc.buf, ioc.bufsiz, mode) != 0) {
78990Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
79000Sstevel@tonic-gate 					    hubd->h_log_handle,
79010Sstevel@tonic-gate 					    "%s: copyout of size failed.", msg);
79020Sstevel@tonic-gate 					rv = EIO;
79030Sstevel@tonic-gate 
79040Sstevel@tonic-gate 					break;
79050Sstevel@tonic-gate 				}
79060Sstevel@tonic-gate 			} else {
79070Sstevel@tonic-gate 				if (ioc.bufsiz != size) {
79080Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
79090Sstevel@tonic-gate 					    hubd->h_log_handle,
79100Sstevel@tonic-gate 					    "%s: buffer size wrong", msg);
79110Sstevel@tonic-gate 					rv = EINVAL;
79120Sstevel@tonic-gate 
79130Sstevel@tonic-gate 					break;
79140Sstevel@tonic-gate 				}
79150Sstevel@tonic-gate 				if (ddi_copyout((void *)&config_index,
79160Sstevel@tonic-gate 				    ioc.buf, ioc.bufsiz, mode) != 0) {
79170Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
79180Sstevel@tonic-gate 					    hubd->h_log_handle,
79190Sstevel@tonic-gate 					    "%s: copyout failed", msg);
79200Sstevel@tonic-gate 					rv = EIO;
79210Sstevel@tonic-gate 				}
79220Sstevel@tonic-gate 			}
79230Sstevel@tonic-gate 
79240Sstevel@tonic-gate 			break;
79250Sstevel@tonic-gate 		}
79260Sstevel@tonic-gate 		case HUBD_GET_DEVICE_PATH:
79270Sstevel@tonic-gate 		{
79280Sstevel@tonic-gate 			char		*path;
79290Sstevel@tonic-gate 			uint32_t	size;
79300Sstevel@tonic-gate 
79310Sstevel@tonic-gate 			msg = "DEVCTL_AP_CONTROL: GET_DEVICE_PATH";
79320Sstevel@tonic-gate 			USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
79330Sstevel@tonic-gate 			    "%s", msg);
79340Sstevel@tonic-gate 
79350Sstevel@tonic-gate 			/* Recheck if child_dip exists */
79360Sstevel@tonic-gate 			if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) ==
79370Sstevel@tonic-gate 			    NULL) {
79380Sstevel@tonic-gate 				rv = EINVAL;
79390Sstevel@tonic-gate 
79400Sstevel@tonic-gate 				break;
79410Sstevel@tonic-gate 			}
79420Sstevel@tonic-gate 
79430Sstevel@tonic-gate 			/* ddi_pathname doesn't supply /devices, so we do. */
79440Sstevel@tonic-gate 			path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
79450Sstevel@tonic-gate 			(void) strcpy(path, "/devices");
79460Sstevel@tonic-gate 			(void) ddi_pathname(child_dip, path + strlen(path));
79470Sstevel@tonic-gate 			size = strlen(path) + 1;
79480Sstevel@tonic-gate 
79490Sstevel@tonic-gate 			USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
79500Sstevel@tonic-gate 			    "%s: device path=%s  size=%d", msg, path, size);
79510Sstevel@tonic-gate 
79520Sstevel@tonic-gate 			if (ioc.get_size) {
79530Sstevel@tonic-gate 				if (ddi_copyout((void *)&size,
79540Sstevel@tonic-gate 				    ioc.buf, ioc.bufsiz, mode) != 0) {
79550Sstevel@tonic-gate 
79560Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
79570Sstevel@tonic-gate 					    hubd->h_log_handle,
79580Sstevel@tonic-gate 					    "%s: copyout of size failed.", msg);
79590Sstevel@tonic-gate 					rv = EIO;
79600Sstevel@tonic-gate 				}
79610Sstevel@tonic-gate 			} else {
79620Sstevel@tonic-gate 				if (ioc.bufsiz != size) {
79630Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
79640Sstevel@tonic-gate 					    hubd->h_log_handle,
79650Sstevel@tonic-gate 					    "%s: buffer wrong size.", msg);
79660Sstevel@tonic-gate 					rv = EINVAL;
79670Sstevel@tonic-gate 				} else if (ddi_copyout((void *)path,
79680Sstevel@tonic-gate 				    ioc.buf, ioc.bufsiz, mode) != 0) {
79690Sstevel@tonic-gate 					USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
79700Sstevel@tonic-gate 					    hubd->h_log_handle,
79710Sstevel@tonic-gate 					    "%s: copyout failed.", msg);
79720Sstevel@tonic-gate 					rv = EIO;
79730Sstevel@tonic-gate 				}
79740Sstevel@tonic-gate 			}
79750Sstevel@tonic-gate 			kmem_free(path, MAXPATHLEN);
79760Sstevel@tonic-gate 
79770Sstevel@tonic-gate 			break;
79780Sstevel@tonic-gate 		}
79790Sstevel@tonic-gate 		case HUBD_REFRESH_DEVDB:
79800Sstevel@tonic-gate 			msg = "DEVCTL_AP_CONTROL: HUBD_REFRESH_DEVDB";
79810Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_CBOPS, hubd->h_log_handle,
79820Sstevel@tonic-gate 			    "%s", msg);
79830Sstevel@tonic-gate 
79840Sstevel@tonic-gate 			if ((rv = usba_devdb_refresh()) != USB_SUCCESS) {
79850Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
79860Sstevel@tonic-gate 				    hubd->h_log_handle,
79870Sstevel@tonic-gate 				    "%s: Failed: %d", msg, rv);
79880Sstevel@tonic-gate 				rv = EIO;
79890Sstevel@tonic-gate 			}
79900Sstevel@tonic-gate 
79910Sstevel@tonic-gate 			break;
79920Sstevel@tonic-gate 		default:
79930Sstevel@tonic-gate 			rv = ENOTSUP;
79940Sstevel@tonic-gate 		}	/* end switch */
79950Sstevel@tonic-gate 
79960Sstevel@tonic-gate 		break;
79970Sstevel@tonic-gate 	}
79980Sstevel@tonic-gate 
79990Sstevel@tonic-gate 	default:
80000Sstevel@tonic-gate 		rv = ENOTTY;
80010Sstevel@tonic-gate 	}
80020Sstevel@tonic-gate 
80030Sstevel@tonic-gate 	if (dcp) {
80040Sstevel@tonic-gate 		ndi_dc_freehdl(dcp);
80050Sstevel@tonic-gate 	}
80060Sstevel@tonic-gate 
80070Sstevel@tonic-gate 	/* allow hotplug thread now */
80080Sstevel@tonic-gate 	hubd->h_hotplug_thread--;
80090Sstevel@tonic-gate 
80100Sstevel@tonic-gate 	if ((hubd->h_dev_state == USB_DEV_ONLINE) &&
80110Sstevel@tonic-gate 	    hubd->h_ep1_ph && (prev_pipe_state == USB_PIPE_STATE_ACTIVE)) {
80120Sstevel@tonic-gate 		hubd_start_polling(hubd, 0);
80130Sstevel@tonic-gate 	}
80140Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
80150Sstevel@tonic-gate 
80160Sstevel@tonic-gate 	ndi_devi_exit(hubd->h_dip, circ);
80170Sstevel@tonic-gate 	ndi_devi_exit(rh_dip, rh_circ);
80180Sstevel@tonic-gate 	ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ);
80190Sstevel@tonic-gate 
80200Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
80210Sstevel@tonic-gate 	hubd_pm_idle_component(hubd, hubd->h_dip, 0);
80220Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
80230Sstevel@tonic-gate 
80240Sstevel@tonic-gate 	return (rv);
80250Sstevel@tonic-gate }
80260Sstevel@tonic-gate 
80270Sstevel@tonic-gate 
80280Sstevel@tonic-gate /*
80290Sstevel@tonic-gate  * Helper func used only to help construct the names for the attachment point
80300Sstevel@tonic-gate  * minor nodes.  Used only in usba_hubdi_attach.
80310Sstevel@tonic-gate  * Returns whether it found ancestry or not (USB_SUCCESS if yes).
80320Sstevel@tonic-gate  * ports between the root hub and the device represented by dip.
80330Sstevel@tonic-gate  * E.g.,  "2.4.3.1" means this device is
80340Sstevel@tonic-gate  *	plugged into port 1 of a hub that is
80350Sstevel@tonic-gate  *	plugged into port 3 of a hub that is
80360Sstevel@tonic-gate  *	plugged into port 4 of a hub that is
80370Sstevel@tonic-gate  *	plugged into port 2 of the root hub.
80380Sstevel@tonic-gate  * NOTE: Max ap_id path len is HUBD_APID_NAMELEN (32 chars), which is
80390Sstevel@tonic-gate  * more than sufficient (as hubs are a max 6 levels deep, port needs 3
80400Sstevel@tonic-gate  * chars plus NULL each)
80410Sstevel@tonic-gate  */
80429430SRaymond.Chen@Sun.COM void
hubd_get_ancestry_str(hubd_t * hubd)80430Sstevel@tonic-gate hubd_get_ancestry_str(hubd_t *hubd)
80440Sstevel@tonic-gate {
80459430SRaymond.Chen@Sun.COM 	char		ap_name[HUBD_APID_NAMELEN];
80469430SRaymond.Chen@Sun.COM 	dev_info_t	*pdip;
80479430SRaymond.Chen@Sun.COM 	hubd_t		*phubd;
80489430SRaymond.Chen@Sun.COM 	usb_port_t	port;
80490Sstevel@tonic-gate 
80500Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
80516898Sfb209375 	    "hubd_get_ancestry_str: hubd=0x%p", (void *)hubd);
80520Sstevel@tonic-gate 
80539430SRaymond.Chen@Sun.COM 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
80549430SRaymond.Chen@Sun.COM 
80559430SRaymond.Chen@Sun.COM 	/*
80569430SRaymond.Chen@Sun.COM 	 * The function is extended to support wire adapter class
80579430SRaymond.Chen@Sun.COM 	 * devices introduced by WUSB spec. The node name is no
80589430SRaymond.Chen@Sun.COM 	 * longer "hub" only.
80599430SRaymond.Chen@Sun.COM 	 * Generate the ap_id str based on the parent and child
80609430SRaymond.Chen@Sun.COM 	 * relationship instead of retrieving it from the hub
80619430SRaymond.Chen@Sun.COM 	 * device path, which simplifies the algorithm.
80629430SRaymond.Chen@Sun.COM 	 */
80639430SRaymond.Chen@Sun.COM 	if (usba_is_root_hub(hubd->h_dip)) {
80649430SRaymond.Chen@Sun.COM 		hubd->h_ancestry_str[0] = '\0';
80659430SRaymond.Chen@Sun.COM 	} else {
80669430SRaymond.Chen@Sun.COM 		port = hubd->h_usba_device->usb_port;
80679430SRaymond.Chen@Sun.COM 		mutex_exit(HUBD_MUTEX(hubd));
80689430SRaymond.Chen@Sun.COM 
80699430SRaymond.Chen@Sun.COM 		pdip = ddi_get_parent(hubd->h_dip);
80700Sstevel@tonic-gate 		/*
80719430SRaymond.Chen@Sun.COM 		 * The parent of wire adapter device might be usb_mid.
80729430SRaymond.Chen@Sun.COM 		 * Need to look further up for hub device
80730Sstevel@tonic-gate 		 */
80749430SRaymond.Chen@Sun.COM 		if (strcmp(ddi_driver_name(pdip), "usb_mid") == 0) {
80759430SRaymond.Chen@Sun.COM 			pdip = ddi_get_parent(pdip);
80769430SRaymond.Chen@Sun.COM 			ASSERT(pdip != NULL);
80779430SRaymond.Chen@Sun.COM 		}
80789430SRaymond.Chen@Sun.COM 
80799430SRaymond.Chen@Sun.COM 		phubd = hubd_get_soft_state(pdip);
80809430SRaymond.Chen@Sun.COM 
80819430SRaymond.Chen@Sun.COM 		mutex_enter(HUBD_MUTEX(phubd));
80829430SRaymond.Chen@Sun.COM 		(void) snprintf(ap_name, HUBD_APID_NAMELEN, "%s%d",
80839430SRaymond.Chen@Sun.COM 		    phubd->h_ancestry_str, port);
80849430SRaymond.Chen@Sun.COM 		mutex_exit(HUBD_MUTEX(phubd));
80859430SRaymond.Chen@Sun.COM 
80869430SRaymond.Chen@Sun.COM 		mutex_enter(HUBD_MUTEX(hubd));
80879430SRaymond.Chen@Sun.COM 		(void) strcpy(hubd->h_ancestry_str, ap_name);
80880Sstevel@tonic-gate 		(void) strcat(hubd->h_ancestry_str, ".");
80890Sstevel@tonic-gate 	}
80900Sstevel@tonic-gate }
80910Sstevel@tonic-gate 
80920Sstevel@tonic-gate 
80930Sstevel@tonic-gate /* Get which port to operate on.  */
80940Sstevel@tonic-gate static usb_port_t
hubd_get_port_num(hubd_t * hubd,struct devctl_iocdata * dcp)80950Sstevel@tonic-gate hubd_get_port_num(hubd_t *hubd, struct devctl_iocdata *dcp)
80960Sstevel@tonic-gate {
80970Sstevel@tonic-gate 	int32_t port;
80980Sstevel@tonic-gate 
80990Sstevel@tonic-gate 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
81000Sstevel@tonic-gate 
81010Sstevel@tonic-gate 	/* Get which port to operate on.  */
81020Sstevel@tonic-gate 	if (nvlist_lookup_int32(ndi_dc_get_ap_data(dcp), "port", &port) != 0) {
81030Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_CBOPS, hubd->h_log_handle,
81040Sstevel@tonic-gate 		    "hubd_get_port_num: port lookup failed");
81050Sstevel@tonic-gate 		port = 0;
81060Sstevel@tonic-gate 	}
81070Sstevel@tonic-gate 
81080Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_CBOPS,  hubd->h_log_handle,
81096898Sfb209375 	    "hubd_get_port_num: hubd=0x%p, port=%d", (void *)hubd, port);
81100Sstevel@tonic-gate 
81110Sstevel@tonic-gate 	return ((usb_port_t)port);
81120Sstevel@tonic-gate }
81130Sstevel@tonic-gate 
81140Sstevel@tonic-gate 
81150Sstevel@tonic-gate /* check if child still exists */
81160Sstevel@tonic-gate static dev_info_t *
hubd_get_child_dip(hubd_t * hubd,usb_port_t port)81170Sstevel@tonic-gate hubd_get_child_dip(hubd_t *hubd, usb_port_t port)
81180Sstevel@tonic-gate {
81190Sstevel@tonic-gate 	dev_info_t *child_dip = hubd->h_children_dips[port];
81200Sstevel@tonic-gate 
81210Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_CBOPS,  hubd->h_log_handle,
81226898Sfb209375 	    "hubd_get_child_dip: hubd=0x%p, port=%d", (void *)hubd, port);
81230Sstevel@tonic-gate 
81240Sstevel@tonic-gate 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
81250Sstevel@tonic-gate 
81260Sstevel@tonic-gate 	return (child_dip);
81270Sstevel@tonic-gate }
81280Sstevel@tonic-gate 
81290Sstevel@tonic-gate 
81300Sstevel@tonic-gate /*
81310Sstevel@tonic-gate  * hubd_cfgadm_state:
81320Sstevel@tonic-gate  *
81330Sstevel@tonic-gate  *	child_dip list		port_state		cfgadm_state
81340Sstevel@tonic-gate  *	--------------		----------		------------
81350Sstevel@tonic-gate  *	!= NULL			connected		configured or
81360Sstevel@tonic-gate  *							unconfigured
81370Sstevel@tonic-gate  *	!= NULL			not connected		disconnect but
81380Sstevel@tonic-gate  *							busy/still referenced
81390Sstevel@tonic-gate  *	NULL			connected		logically disconnected
81400Sstevel@tonic-gate  *	NULL			not connected		empty
81410Sstevel@tonic-gate  */
81420Sstevel@tonic-gate static uint_t
hubd_cfgadm_state(hubd_t * hubd,usb_port_t port)81430Sstevel@tonic-gate hubd_cfgadm_state(hubd_t *hubd, usb_port_t port)
81440Sstevel@tonic-gate {
81450Sstevel@tonic-gate 	uint_t		state;
81460Sstevel@tonic-gate 	dev_info_t	*child_dip = hubd_get_child_dip(hubd, port);
81470Sstevel@tonic-gate 
81480Sstevel@tonic-gate 	if (child_dip) {
81490Sstevel@tonic-gate 		if (hubd->h_port_state[port] & PORT_STATUS_CCS) {
81500Sstevel@tonic-gate 			/*
81510Sstevel@tonic-gate 			 * connected,  now check if driver exists
81520Sstevel@tonic-gate 			 */
81530Sstevel@tonic-gate 			if (DEVI_IS_DEVICE_OFFLINE(child_dip) ||
81541333Scth 			    !i_ddi_devi_attached(child_dip)) {
81550Sstevel@tonic-gate 				state = HUBD_CFGADM_UNCONFIGURED;
81560Sstevel@tonic-gate 			} else {
81570Sstevel@tonic-gate 				state = HUBD_CFGADM_CONFIGURED;
81580Sstevel@tonic-gate 			}
81590Sstevel@tonic-gate 		} else {
81600Sstevel@tonic-gate 			/*
81610Sstevel@tonic-gate 			 * this means that the dip is around for
81620Sstevel@tonic-gate 			 * a device that is still referenced but
81630Sstevel@tonic-gate 			 * has been yanked out. So the cfgadm info
81640Sstevel@tonic-gate 			 * for this state should be EMPTY (port empty)
81650Sstevel@tonic-gate 			 * and CONFIGURED (dip still valid).
81660Sstevel@tonic-gate 			 */
81670Sstevel@tonic-gate 			state = HUBD_CFGADM_STILL_REFERENCED;
81680Sstevel@tonic-gate 		}
81690Sstevel@tonic-gate 	} else {
81700Sstevel@tonic-gate 		/* connected but no child dip */
81710Sstevel@tonic-gate 		if (hubd->h_port_state[port] & PORT_STATUS_CCS) {
81720Sstevel@tonic-gate 			/* logically disconnected */
81730Sstevel@tonic-gate 			state = HUBD_CFGADM_DISCONNECTED;
81740Sstevel@tonic-gate 		} else {
81750Sstevel@tonic-gate 			/* physically disconnected */
81760Sstevel@tonic-gate 			state = HUBD_CFGADM_EMPTY;
81770Sstevel@tonic-gate 		}
81780Sstevel@tonic-gate 	}
81790Sstevel@tonic-gate 
81800Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_CBOPS,  hubd->h_log_handle,
81810Sstevel@tonic-gate 	    "hubd_cfgadm_state: hubd=0x%p, port=%d state=0x%x",
81826898Sfb209375 	    (void *)hubd, port, state);
81830Sstevel@tonic-gate 
81840Sstevel@tonic-gate 	return (state);
81850Sstevel@tonic-gate }
81860Sstevel@tonic-gate 
81870Sstevel@tonic-gate 
81880Sstevel@tonic-gate /*
81890Sstevel@tonic-gate  * hubd_toggle_port:
81900Sstevel@tonic-gate  */
81910Sstevel@tonic-gate static int
hubd_toggle_port(hubd_t * hubd,usb_port_t port)81920Sstevel@tonic-gate hubd_toggle_port(hubd_t *hubd, usb_port_t port)
81930Sstevel@tonic-gate {
81940Sstevel@tonic-gate 	usb_hub_descr_t	*hub_descr;
81950Sstevel@tonic-gate 	int		wait;
81960Sstevel@tonic-gate 	uint_t		retry;
81970Sstevel@tonic-gate 	uint16_t	status;
81980Sstevel@tonic-gate 	uint16_t	change;
81990Sstevel@tonic-gate 
82000Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_CBOPS,  hubd->h_log_handle,
82016898Sfb209375 	    "hubd_toggle_port: hubd=0x%p, port=%d", (void *)hubd, port);
82020Sstevel@tonic-gate 
82030Sstevel@tonic-gate 	if ((hubd_disable_port_power(hubd, port)) != USB_SUCCESS) {
82040Sstevel@tonic-gate 
82050Sstevel@tonic-gate 		return (USB_FAILURE);
82060Sstevel@tonic-gate 	}
82070Sstevel@tonic-gate 
82080Sstevel@tonic-gate 	/*
82090Sstevel@tonic-gate 	 * see hubd_enable_all_port_power() which
82100Sstevel@tonic-gate 	 * requires longer delay for hubs.
82110Sstevel@tonic-gate 	 */
82120Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
82130Sstevel@tonic-gate 	delay(drv_usectohz(hubd_device_delay / 10));
82140Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
82150Sstevel@tonic-gate 
82160Sstevel@tonic-gate 	hub_descr = &hubd->h_hub_descr;
82170Sstevel@tonic-gate 
82180Sstevel@tonic-gate 	/*
82190Sstevel@tonic-gate 	 * According to section 11.11 of USB, for hubs with no power
82200Sstevel@tonic-gate 	 * switches, bPwrOn2PwrGood is zero. But we wait for some
82210Sstevel@tonic-gate 	 * arbitrary time to enable power to become stable.
82220Sstevel@tonic-gate 	 *
82230Sstevel@tonic-gate 	 * If an hub supports port power swicthing, we need to wait
82240Sstevel@tonic-gate 	 * at least 20ms before accesing corresonding usb port.
82250Sstevel@tonic-gate 	 */
82260Sstevel@tonic-gate 	if ((hub_descr->wHubCharacteristics &
82270Sstevel@tonic-gate 	    HUB_CHARS_NO_POWER_SWITCHING) || (!hub_descr->bPwrOn2PwrGood)) {
82280Sstevel@tonic-gate 		wait = hubd_device_delay / 10;
82290Sstevel@tonic-gate 	} else {
82300Sstevel@tonic-gate 		wait = max(HUB_DEFAULT_POPG,
82310Sstevel@tonic-gate 		    hub_descr->bPwrOn2PwrGood) * 2 * 1000;
82320Sstevel@tonic-gate 	}
82330Sstevel@tonic-gate 
82340Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
82350Sstevel@tonic-gate 	    "hubd_toggle_port: popg=%d wait=%d",
82360Sstevel@tonic-gate 	    hub_descr->bPwrOn2PwrGood, wait);
82370Sstevel@tonic-gate 
82380Sstevel@tonic-gate 	retry = 0;
82390Sstevel@tonic-gate 
82400Sstevel@tonic-gate 	do {
82410Sstevel@tonic-gate 		(void) hubd_enable_port_power(hubd, port);
82420Sstevel@tonic-gate 
82430Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
82440Sstevel@tonic-gate 		delay(drv_usectohz(wait));
82450Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
82460Sstevel@tonic-gate 
82470Sstevel@tonic-gate 		/* Get port status */
82480Sstevel@tonic-gate 		(void) hubd_determine_port_status(hubd, port,
82490Sstevel@tonic-gate 		    &status, &change, 0);
82500Sstevel@tonic-gate 
82510Sstevel@tonic-gate 		/* For retry if any, use some extra delay */
82520Sstevel@tonic-gate 		wait = max(wait, hubd_device_delay / 10);
82530Sstevel@tonic-gate 
82540Sstevel@tonic-gate 		retry++;
82550Sstevel@tonic-gate 
82560Sstevel@tonic-gate 	} while ((!(status & PORT_STATUS_PPS)) && (retry < HUBD_PORT_RETRY));
82570Sstevel@tonic-gate 
82580Sstevel@tonic-gate 	/* Print warning message if port has no power */
82590Sstevel@tonic-gate 	if (!(status & PORT_STATUS_PPS)) {
82600Sstevel@tonic-gate 
8261978Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
82620Sstevel@tonic-gate 		    "hubd_toggle_port: port %d power-on failed, "
82630Sstevel@tonic-gate 		    "port status 0x%x", port, status);
82640Sstevel@tonic-gate 
82650Sstevel@tonic-gate 		return (USB_FAILURE);
82660Sstevel@tonic-gate 	}
82670Sstevel@tonic-gate 
82680Sstevel@tonic-gate 	return (USB_SUCCESS);
82690Sstevel@tonic-gate }
82701001Ssl147100 
82711001Ssl147100 
82721001Ssl147100 /*
82731001Ssl147100  * hubd_init_power_budget:
82741001Ssl147100  *	Init power budget variables in hubd structure. According
82751001Ssl147100  *	to USB spec, the power budget rules are:
82761001Ssl147100  *	1. local-powered hubs including root-hubs can supply
82771001Ssl147100  *	   500mA to each port at maximum
82781001Ssl147100  *	2. two bus-powered hubs are not allowed to concatenate
82791001Ssl147100  *	3. bus-powered hubs can supply 100mA to each port at
82801001Ssl147100  *	   maximum, and the power consumed by all downstream
82811001Ssl147100  *	   ports and the hub itself cannot exceed the max power
82821001Ssl147100  *	   supplied by the upstream port, i.e., 500mA
82831001Ssl147100  *	The routine is only called during hub attach time
82841001Ssl147100  */
82851001Ssl147100 static int
hubd_init_power_budget(hubd_t * hubd)82861001Ssl147100 hubd_init_power_budget(hubd_t *hubd)
82871001Ssl147100 {
82881001Ssl147100 	uint16_t	status = 0;
82891001Ssl147100 	usba_device_t	*hubd_ud = NULL;
82901001Ssl147100 	size_t		size;
82911001Ssl147100 	usb_cfg_descr_t	cfg_descr;
82921001Ssl147100 	dev_info_t	*pdip = NULL;
82931001Ssl147100 	hubd_t		*phubd = NULL;
82941001Ssl147100 
82951001Ssl147100 	if (hubd->h_ignore_pwr_budget) {
82961001Ssl147100 
82971001Ssl147100 		return (USB_SUCCESS);
82981001Ssl147100 	}
82991001Ssl147100 
83001001Ssl147100 	USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
83011001Ssl147100 	    "hubd_init_power_budget:");
83021001Ssl147100 
83031001Ssl147100 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
83041001Ssl147100 	ASSERT(hubd->h_default_pipe != 0);
83051001Ssl147100 	mutex_exit(HUBD_MUTEX(hubd));
83061001Ssl147100 
83071001Ssl147100 	/* get device status */
83081001Ssl147100 	if ((usb_get_status(hubd->h_dip, hubd->h_default_pipe,
83091001Ssl147100 	    HUB_GET_DEVICE_STATUS_TYPE,
83101001Ssl147100 	    0, &status, 0)) != USB_SUCCESS) {
83111001Ssl147100 		mutex_enter(HUBD_MUTEX(hubd));
83121001Ssl147100 
83131001Ssl147100 		return (USB_FAILURE);
83141001Ssl147100 	}
83151001Ssl147100 
83161001Ssl147100 	hubd_ud = usba_get_usba_device(hubd->h_dip);
83171001Ssl147100 
83181001Ssl147100 	size = usb_parse_cfg_descr(hubd_ud->usb_cfg, hubd_ud->usb_cfg_length,
83191001Ssl147100 	    &cfg_descr, USB_CFG_DESCR_SIZE);
83201001Ssl147100 
83211001Ssl147100 	if (size != USB_CFG_DESCR_SIZE) {
83221001Ssl147100 		USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
83231001Ssl147100 		    "get hub configuration descriptor failed");
83241001Ssl147100 		mutex_enter(HUBD_MUTEX(hubd));
83251001Ssl147100 
83261001Ssl147100 		return (USB_FAILURE);
83271001Ssl147100 	}
83281001Ssl147100 
83291001Ssl147100 	mutex_enter(HUBD_MUTEX(hubd));
83301001Ssl147100 
83311001Ssl147100 	hubd->h_local_pwr_capable = (cfg_descr.bmAttributes &
83321001Ssl147100 	    USB_CFG_ATTR_SELFPWR);
83331001Ssl147100 
83341001Ssl147100 	if (hubd->h_local_pwr_capable) {
83351001Ssl147100 		USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle,
83361001Ssl147100 		    "hub is capable of local power");
83371001Ssl147100 	}
83381001Ssl147100 
83391001Ssl147100 	hubd->h_local_pwr_on = (status &
83401001Ssl147100 	    USB_DEV_SLF_PWRD_STATUS) && hubd->h_local_pwr_capable;
83411001Ssl147100 
83421001Ssl147100 	if (hubd->h_local_pwr_on) {
83431001Ssl147100 		USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle,
83441001Ssl147100 		    "hub is local-powered");
83451001Ssl147100 
83461001Ssl147100 		hubd->h_pwr_limit = (USB_PWR_UNIT_LOAD *
83471001Ssl147100 		    USB_HIGH_PWR_VALUE) / USB_CFG_DESCR_PWR_UNIT;
83481001Ssl147100 	} else {
83491001Ssl147100 		hubd->h_pwr_limit = (USB_PWR_UNIT_LOAD *
83501001Ssl147100 		    USB_LOW_PWR_VALUE) / USB_CFG_DESCR_PWR_UNIT;
83511001Ssl147100 
83521001Ssl147100 		hubd->h_pwr_left = (USB_PWR_UNIT_LOAD *
83531001Ssl147100 		    USB_HIGH_PWR_VALUE) / USB_CFG_DESCR_PWR_UNIT;
83541001Ssl147100 
83551001Ssl147100 		ASSERT(!usba_is_root_hub(hubd->h_dip));
83561001Ssl147100 
83571001Ssl147100 		if (!usba_is_root_hub(hubd->h_dip)) {
83581001Ssl147100 			/*
83591001Ssl147100 			 * two bus-powered hubs are not
83601001Ssl147100 			 * allowed to be concatenated
83611001Ssl147100 			 */
83621001Ssl147100 			mutex_exit(HUBD_MUTEX(hubd));
83631001Ssl147100 
83641001Ssl147100 			pdip = ddi_get_parent(hubd->h_dip);
83651001Ssl147100 			phubd = hubd_get_soft_state(pdip);
83661001Ssl147100 			ASSERT(phubd != NULL);
83671001Ssl147100 
83681001Ssl147100 			if (!phubd->h_ignore_pwr_budget) {
83691001Ssl147100 				mutex_enter(HUBD_MUTEX(phubd));
83701001Ssl147100 				if (phubd->h_local_pwr_on == B_FALSE) {
83713435Slg150142 					USB_DPRINTF_L1(DPRINT_MASK_HUB,
83721001Ssl147100 					    hubd->h_log_handle,
83731001Ssl147100 					    "two bus-powered hubs cannot "
83741001Ssl147100 					    "be concatenated");
83751001Ssl147100 
83761001Ssl147100 					mutex_exit(HUBD_MUTEX(phubd));
83771001Ssl147100 					mutex_enter(HUBD_MUTEX(hubd));
83781001Ssl147100 
83791001Ssl147100 					return (USB_FAILURE);
83801001Ssl147100 				}
83811001Ssl147100 				mutex_exit(HUBD_MUTEX(phubd));
83821001Ssl147100 			}
83831001Ssl147100 
83841001Ssl147100 			mutex_enter(HUBD_MUTEX(hubd));
83851001Ssl147100 
83861001Ssl147100 			USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle,
83871001Ssl147100 			    "hub is bus-powered");
83881001Ssl147100 		} else {
83891001Ssl147100 			USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle,
83901001Ssl147100 			    "root-hub must be local-powered");
83911001Ssl147100 		}
83921001Ssl147100 
83931001Ssl147100 		/*
83941001Ssl147100 		 * Subtract the power consumed by the hub itself
83951001Ssl147100 		 * and get the power that can be supplied to
83961001Ssl147100 		 * downstream ports
83971001Ssl147100 		 */
83981001Ssl147100 		hubd->h_pwr_left -=
83991001Ssl147100 		    hubd->h_hub_descr.bHubContrCurrent /
84001001Ssl147100 		    USB_CFG_DESCR_PWR_UNIT;
84011001Ssl147100 		if (hubd->h_pwr_left < 0) {
84021001Ssl147100 			USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
84031001Ssl147100 			    "hubd->h_pwr_left is less than bHubContrCurrent, "
84041001Ssl147100 			    "should fail");
84051001Ssl147100 
84061001Ssl147100 			return (USB_FAILURE);
84071001Ssl147100 		}
84081001Ssl147100 	}
84091001Ssl147100 
84101001Ssl147100 	return (USB_SUCCESS);
84111001Ssl147100 }
84121001Ssl147100 
84131001Ssl147100 
84141001Ssl147100 /*
84151001Ssl147100  * usba_hubdi_check_power_budget:
84161001Ssl147100  *	Check if the hub has enough power budget to allow a
84171001Ssl147100  *	child device to select a configuration of config_index.
84181001Ssl147100  */
84191001Ssl147100 int
usba_hubdi_check_power_budget(dev_info_t * dip,usba_device_t * child_ud,uint_t config_index)84201001Ssl147100 usba_hubdi_check_power_budget(dev_info_t *dip, usba_device_t *child_ud,
84211001Ssl147100 	uint_t config_index)
84221001Ssl147100 {
84231001Ssl147100 	int16_t		pwr_left, pwr_limit, pwr_required;
84241001Ssl147100 	size_t		size;
84251001Ssl147100 	usb_cfg_descr_t cfg_descr;
84261001Ssl147100 	hubd_t		*hubd;
84271001Ssl147100 
84281001Ssl147100 	if ((hubd = hubd_get_soft_state(dip)) == NULL) {
84291001Ssl147100 
84301001Ssl147100 		return (USB_FAILURE);
84311001Ssl147100 	}
84321001Ssl147100 
84331001Ssl147100 	if (hubd->h_ignore_pwr_budget) {
84341001Ssl147100 
84351001Ssl147100 		return (USB_SUCCESS);
84361001Ssl147100 	}
84371001Ssl147100 
84381001Ssl147100 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
84391001Ssl147100 	    "usba_hubdi_check_power_budget: "
84406898Sfb209375 	    "dip=0x%p child_ud=0x%p conf_index=%d", (void *)dip,
84416898Sfb209375 	    (void *)child_ud, config_index);
84421001Ssl147100 
84431001Ssl147100 	mutex_enter(HUBD_MUTEX(hubd));
84441001Ssl147100 	pwr_limit = hubd->h_pwr_limit;
84451001Ssl147100 	if (hubd->h_local_pwr_on == B_FALSE) {
84461001Ssl147100 		pwr_left = hubd->h_pwr_left;
84471001Ssl147100 		pwr_limit = (pwr_limit <= pwr_left) ? pwr_limit : pwr_left;
84481001Ssl147100 	}
84491001Ssl147100 	mutex_exit(HUBD_MUTEX(hubd));
84501001Ssl147100 
84511001Ssl147100 	USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
84521001Ssl147100 	    "usba_hubdi_check_power_budget: "
84531001Ssl147100 	    "available power is %dmA", pwr_limit * USB_CFG_DESCR_PWR_UNIT);
84541001Ssl147100 
84551001Ssl147100 	size = usb_parse_cfg_descr(
84561001Ssl147100 	    child_ud->usb_cfg_array[config_index], USB_CFG_DESCR_SIZE,
84571001Ssl147100 	    &cfg_descr, USB_CFG_DESCR_SIZE);
84581001Ssl147100 
84591001Ssl147100 	if (size != USB_CFG_DESCR_SIZE) {
84601001Ssl147100 		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
84611001Ssl147100 		    "get hub configuration descriptor failed");
84621001Ssl147100 
84631001Ssl147100 		return (USB_FAILURE);
84641001Ssl147100 	}
84651001Ssl147100 
84661001Ssl147100 	pwr_required = cfg_descr.bMaxPower;
84671001Ssl147100 
84681001Ssl147100 	USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
84691001Ssl147100 	    "usba_hubdi_check_power_budget: "
84701001Ssl147100 	    "child bmAttributes=0x%x bMaxPower=%d "
84711001Ssl147100 	    "with config_index=%d", cfg_descr.bmAttributes,
84721001Ssl147100 	    pwr_required, config_index);
84731001Ssl147100 
84741001Ssl147100 	if (pwr_required > pwr_limit) {
84753435Slg150142 		USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
84761001Ssl147100 		    "configuration %d for device %s %s at port %d "
84771001Ssl147100 		    "exceeds power available for this port, please "
84781001Ssl147100 		    "re-insert your device into another hub port which "
84791001Ssl147100 		    "has enough power",
84801001Ssl147100 		    config_index,
84811001Ssl147100 		    child_ud->usb_mfg_str,
84821001Ssl147100 		    child_ud->usb_product_str,
84831001Ssl147100 		    child_ud->usb_port);
84841001Ssl147100 
84851001Ssl147100 		return (USB_FAILURE);
84861001Ssl147100 	}
84871001Ssl147100 
84881001Ssl147100 	return (USB_SUCCESS);
84891001Ssl147100 }
84901001Ssl147100 
84911001Ssl147100 
84921001Ssl147100 /*
84931001Ssl147100  * usba_hubdi_incr_power_budget:
84941001Ssl147100  *	Increase the hub power budget value when a child device
84951001Ssl147100  *	is removed from a bus-powered hub port.
84961001Ssl147100  */
84971001Ssl147100 void
usba_hubdi_incr_power_budget(dev_info_t * dip,usba_device_t * child_ud)84981001Ssl147100 usba_hubdi_incr_power_budget(dev_info_t *dip, usba_device_t *child_ud)
84991001Ssl147100 {
85001001Ssl147100 	uint16_t	pwr_value;
85011001Ssl147100 	hubd_t		*hubd = hubd_get_soft_state(dip);
85021001Ssl147100 
85031001Ssl147100 	ASSERT(hubd != NULL);
85041001Ssl147100 
85051001Ssl147100 	if (hubd->h_ignore_pwr_budget) {
85061001Ssl147100 
85071001Ssl147100 		return;
85081001Ssl147100 	}
85091001Ssl147100 
85101001Ssl147100 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
85111001Ssl147100 	    "usba_hubdi_incr_power_budget: "
85126898Sfb209375 	    "dip=0x%p child_ud=0x%p", (void *)dip, (void *)child_ud);
85131001Ssl147100 
85141001Ssl147100 	mutex_enter(HUBD_MUTEX(hubd));
85151001Ssl147100 	if (hubd->h_local_pwr_on == B_TRUE) {
85161001Ssl147100 		USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
85171001Ssl147100 		    "usba_hubdi_incr_power_budget: "
85181001Ssl147100 		    "hub is local powered");
85191001Ssl147100 		mutex_exit(HUBD_MUTEX(hubd));
85201001Ssl147100 
85211001Ssl147100 		return;
85221001Ssl147100 	}
85231001Ssl147100 	mutex_exit(HUBD_MUTEX(hubd));
85241001Ssl147100 
85251001Ssl147100 	mutex_enter(&child_ud->usb_mutex);
85261001Ssl147100 	if (child_ud->usb_pwr_from_hub == 0) {
85271001Ssl147100 		mutex_exit(&child_ud->usb_mutex);
85281001Ssl147100 
85291001Ssl147100 		return;
85301001Ssl147100 	}
85311001Ssl147100 	pwr_value = child_ud->usb_pwr_from_hub;
85321001Ssl147100 	mutex_exit(&child_ud->usb_mutex);
85331001Ssl147100 
85341001Ssl147100 	mutex_enter(HUBD_MUTEX(hubd));
85351001Ssl147100 	hubd->h_pwr_left += pwr_value;
85361001Ssl147100 
85371001Ssl147100 	USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
85381001Ssl147100 	    "usba_hubdi_incr_power_budget: "
85391001Ssl147100 	    "available power is %dmA, increased by %dmA",
85401001Ssl147100 	    hubd->h_pwr_left * USB_CFG_DESCR_PWR_UNIT,
85411001Ssl147100 	    pwr_value * USB_CFG_DESCR_PWR_UNIT);
85421001Ssl147100 
85431001Ssl147100 	mutex_exit(HUBD_MUTEX(hubd));
85441001Ssl147100 
85451001Ssl147100 	mutex_enter(&child_ud->usb_mutex);
85461001Ssl147100 	child_ud->usb_pwr_from_hub = 0;
85471001Ssl147100 	mutex_exit(&child_ud->usb_mutex);
85481001Ssl147100 }
85491001Ssl147100 
85501001Ssl147100 
85511001Ssl147100 /*
85521001Ssl147100  * usba_hubdi_decr_power_budget:
85531001Ssl147100  *	Decrease the hub power budget value when a child device
85541001Ssl147100  *	is inserted to a bus-powered hub port.
85551001Ssl147100  */
85561001Ssl147100 void
usba_hubdi_decr_power_budget(dev_info_t * dip,usba_device_t * child_ud)85571001Ssl147100 usba_hubdi_decr_power_budget(dev_info_t *dip, usba_device_t *child_ud)
85581001Ssl147100 {
85591001Ssl147100 	uint16_t	pwr_value;
85601001Ssl147100 	size_t		size;
85611001Ssl147100 	usb_cfg_descr_t	cfg_descr;
85621001Ssl147100 	hubd_t		*hubd = hubd_get_soft_state(dip);
85631001Ssl147100 
85641001Ssl147100 	ASSERT(hubd != NULL);
85651001Ssl147100 
85661001Ssl147100 	if (hubd->h_ignore_pwr_budget) {
85671001Ssl147100 
85681001Ssl147100 		return;
85691001Ssl147100 	}
85701001Ssl147100 
85711001Ssl147100 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
85721001Ssl147100 	    "usba_hubdi_decr_power_budget: "
85736898Sfb209375 	    "dip=0x%p child_ud=0x%p", (void *)dip, (void *)child_ud);
85741001Ssl147100 
85751001Ssl147100 	mutex_enter(HUBD_MUTEX(hubd));
85761001Ssl147100 	if (hubd->h_local_pwr_on == B_TRUE) {
85771001Ssl147100 		USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
85781001Ssl147100 		    "usba_hubdi_decr_power_budget: "
85791001Ssl147100 		    "hub is local powered");
85801001Ssl147100 		mutex_exit(HUBD_MUTEX(hubd));
85811001Ssl147100 
85821001Ssl147100 		return;
85831001Ssl147100 	}
85841001Ssl147100 	mutex_exit(HUBD_MUTEX(hubd));
85851001Ssl147100 
85861001Ssl147100 	mutex_enter(&child_ud->usb_mutex);
85871001Ssl147100 	if (child_ud->usb_pwr_from_hub > 0) {
85881001Ssl147100 		mutex_exit(&child_ud->usb_mutex);
85891001Ssl147100 
85901001Ssl147100 		return;
85911001Ssl147100 	}
85921001Ssl147100 	mutex_exit(&child_ud->usb_mutex);
85931001Ssl147100 
85941001Ssl147100 	size = usb_parse_cfg_descr(
85951001Ssl147100 	    child_ud->usb_cfg, child_ud->usb_cfg_length,
85961001Ssl147100 	    &cfg_descr, USB_CFG_DESCR_SIZE);
85971001Ssl147100 	ASSERT(size == USB_CFG_DESCR_SIZE);
85981001Ssl147100 
85991001Ssl147100 	mutex_enter(HUBD_MUTEX(hubd));
86001001Ssl147100 	pwr_value = cfg_descr.bMaxPower;
86011001Ssl147100 	hubd->h_pwr_left -= pwr_value;
86021001Ssl147100 	ASSERT(hubd->h_pwr_left >= 0);
86031001Ssl147100 
86041001Ssl147100 	USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
86051001Ssl147100 	    "usba_hubdi_decr_power_budget: "
86061001Ssl147100 	    "available power is %dmA, decreased by %dmA",
86071001Ssl147100 	    hubd->h_pwr_left * USB_CFG_DESCR_PWR_UNIT,
86081001Ssl147100 	    pwr_value * USB_CFG_DESCR_PWR_UNIT);
86091001Ssl147100 
86101001Ssl147100 	mutex_exit(HUBD_MUTEX(hubd));
86111001Ssl147100 
86121001Ssl147100 	mutex_enter(&child_ud->usb_mutex);
86131001Ssl147100 	child_ud->usb_pwr_from_hub = pwr_value;
86141001Ssl147100 	mutex_exit(&child_ud->usb_mutex);
86151001Ssl147100 }
86164844Slg150142 
86174844Slg150142 /*
86184844Slg150142  * hubd_wait_for_hotplug_exit:
86196112Sqz150045  *	Waiting for the exit of the running hotplug thread or ioctl thread.
86204844Slg150142  */
86214844Slg150142 static int
hubd_wait_for_hotplug_exit(hubd_t * hubd)86224844Slg150142 hubd_wait_for_hotplug_exit(hubd_t *hubd)
86234844Slg150142 {
862411066Srafael.vanoni@sun.com 	clock_t		until = drv_usectohz(1000000);
86254844Slg150142 	int		rval;
86264844Slg150142 
86274844Slg150142 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
86284844Slg150142 
86294844Slg150142 	if (hubd->h_hotplug_thread) {
86304844Slg150142 		USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
86314844Slg150142 		    "waiting for hubd hotplug thread exit");
863211066Srafael.vanoni@sun.com 		rval = cv_reltimedwait(&hubd->h_cv_hotplug_dev,
863311066Srafael.vanoni@sun.com 		    &hubd->h_mutex, until, TR_CLOCK_TICK);
86344844Slg150142 
86354844Slg150142 		if ((rval <= 0) && (hubd->h_hotplug_thread)) {
86364844Slg150142 
86374844Slg150142 			return (USB_FAILURE);
86384844Slg150142 		}
86394844Slg150142 	}
86404844Slg150142 
86414844Slg150142 	return (USB_SUCCESS);
86424844Slg150142 }
86434844Slg150142 
86444844Slg150142 /*
86454844Slg150142  * hubd_reset_thread:
86464844Slg150142  *	handles the "USB_RESET_LVL_REATTACH" reset of usb device.
86474844Slg150142  *
86484844Slg150142  *	- delete the child (force detaching the device and its children)
86494844Slg150142  *	- reset the corresponding parent hub port
86504844Slg150142  *	- create the child (force re-attaching the device and its children)
86514844Slg150142  */
86524844Slg150142 static void
hubd_reset_thread(void * arg)86534844Slg150142 hubd_reset_thread(void *arg)
86544844Slg150142 {
86554844Slg150142 	hubd_reset_arg_t *hd_arg = (hubd_reset_arg_t *)arg;
86564844Slg150142 	hubd_t		*hubd = hd_arg->hubd;
86574844Slg150142 	uint16_t	reset_port = hd_arg->reset_port;
86584844Slg150142 	uint16_t	status, change;
86594844Slg150142 	hub_power_t	*hubpm;
86604844Slg150142 	dev_info_t	*hdip = hubd->h_dip;
86614844Slg150142 	dev_info_t	*rh_dip = hubd->h_usba_device->usb_root_hub_dip;
86624844Slg150142 	dev_info_t	*child_dip;
86634844Slg150142 	boolean_t	online_child = B_FALSE;
86644844Slg150142 	int		prh_circ, rh_circ, circ, devinst;
86654844Slg150142 	char		*devname;
866610468SRaymond.Chen@Sun.COM 	int		i = 0;
866710468SRaymond.Chen@Sun.COM 	int		rval = USB_FAILURE;
86684844Slg150142 
86694844Slg150142 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
86704844Slg150142 	    "hubd_reset_thread:  started, hubd_reset_port = 0x%x", reset_port);
86714844Slg150142 
86724844Slg150142 	kmem_free(arg, sizeof (hubd_reset_arg_t));
86734844Slg150142 
86744844Slg150142 	mutex_enter(HUBD_MUTEX(hubd));
86754844Slg150142 
86764844Slg150142 	child_dip = hubd->h_children_dips[reset_port];
86774844Slg150142 	ASSERT(child_dip != NULL);
86784844Slg150142 
86794844Slg150142 	devname = (char *)ddi_driver_name(child_dip);
86804844Slg150142 	devinst = ddi_get_instance(child_dip);
86814844Slg150142 
86824844Slg150142 	/* if our bus power entry point is active, quit the reset */
86834844Slg150142 	if (hubd->h_bus_pwr) {
86844844Slg150142 		USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
86854844Slg150142 		    "%s%d is under bus power management, cannot be reset. "
86864844Slg150142 		    "Please disconnect and reconnect this device.",
86874844Slg150142 		    devname, devinst);
86884844Slg150142 
86894844Slg150142 		goto Fail;
86904844Slg150142 	}
86914844Slg150142 
86924844Slg150142 	if (hubd_wait_for_hotplug_exit(hubd) == USB_FAILURE) {
86934844Slg150142 		/* we got woken up because of a timeout */
86944844Slg150142 		USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG,
86954844Slg150142 		    hubd->h_log_handle, "Time out when resetting the device"
86964844Slg150142 		    " %s%d. Please disconnect and reconnect this device.",
86974844Slg150142 		    devname, devinst);
86984844Slg150142 
86994844Slg150142 		goto Fail;
87004844Slg150142 	}
87014844Slg150142 
87024844Slg150142 	hubd->h_hotplug_thread++;
87034844Slg150142 
87044844Slg150142 	/* is this the root hub? */
87054844Slg150142 	if ((hdip == rh_dip) &&
87064844Slg150142 	    (hubd->h_dev_state == USB_DEV_PWRED_DOWN)) {
87074844Slg150142 		hubpm = hubd->h_hubpm;
87084844Slg150142 
87094844Slg150142 		/* mark the root hub as full power */
87104844Slg150142 		hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR;
87114844Slg150142 		hubpm->hubp_time_at_full_power = ddi_get_time();
87124844Slg150142 		mutex_exit(HUBD_MUTEX(hubd));
87134844Slg150142 
87144844Slg150142 		USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
87154844Slg150142 		    "hubd_reset_thread: call pm_power_has_changed");
87164844Slg150142 
87174844Slg150142 		(void) pm_power_has_changed(hdip, 0,
87184844Slg150142 		    USB_DEV_OS_FULL_PWR);
87194844Slg150142 
87204844Slg150142 		mutex_enter(HUBD_MUTEX(hubd));
87214844Slg150142 		hubd->h_dev_state = USB_DEV_ONLINE;
87224844Slg150142 	}
87234844Slg150142 
87244844Slg150142 	mutex_exit(HUBD_MUTEX(hubd));
87254844Slg150142 
87264844Slg150142 	/*
87274844Slg150142 	 * this ensures one reset activity per system at a time.
87284844Slg150142 	 * we enter the parent PCI node to have this serialization.
87294844Slg150142 	 * this also excludes ioctls and deathrow thread
87304844Slg150142 	 */
87314844Slg150142 	ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
87324844Slg150142 	ndi_devi_enter(rh_dip, &rh_circ);
87334844Slg150142 
87344844Slg150142 	/* exclude other threads */
87354844Slg150142 	ndi_devi_enter(hdip, &circ);
87364844Slg150142 	mutex_enter(HUBD_MUTEX(hubd));
87374844Slg150142 
87384844Slg150142 	/*
87394844Slg150142 	 * We need to make sure that the child is still online for a hotplug
87404844Slg150142 	 * thread could have inserted which detached the child.
87414844Slg150142 	 */
87424844Slg150142 	if (hubd->h_children_dips[reset_port]) {
87434844Slg150142 		mutex_exit(HUBD_MUTEX(hubd));
87444844Slg150142 		/* First disconnect the device */
87454844Slg150142 		hubd_post_event(hubd, reset_port, USBA_EVENT_TAG_HOT_REMOVAL);
874610468SRaymond.Chen@Sun.COM 
874710468SRaymond.Chen@Sun.COM 		/* delete cached dv_node's but drop locks first */
874810468SRaymond.Chen@Sun.COM 		ndi_devi_exit(hdip, circ);
874910468SRaymond.Chen@Sun.COM 		ndi_devi_exit(rh_dip, rh_circ);
875010468SRaymond.Chen@Sun.COM 		ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ);
875110468SRaymond.Chen@Sun.COM 
875210468SRaymond.Chen@Sun.COM 		(void) devfs_clean(rh_dip, NULL, DV_CLEAN_FORCE);
875310468SRaymond.Chen@Sun.COM 
875410468SRaymond.Chen@Sun.COM 		/*
875510468SRaymond.Chen@Sun.COM 		 * workaround only for storage device. When it's able to force
875610468SRaymond.Chen@Sun.COM 		 * detach a driver, this code can be removed safely.
875710468SRaymond.Chen@Sun.COM 		 *
875810468SRaymond.Chen@Sun.COM 		 * If we're to reset storage device and the device is used, we
875910468SRaymond.Chen@Sun.COM 		 * will wait at most extra 20s for applications to exit and
876010468SRaymond.Chen@Sun.COM 		 * close the device. This is especially useful for HAL-based
876110468SRaymond.Chen@Sun.COM 		 * applications.
876210468SRaymond.Chen@Sun.COM 		 */
876310468SRaymond.Chen@Sun.COM 		if ((strcmp(devname, "scsa2usb") == 0) &&
876410468SRaymond.Chen@Sun.COM 		    DEVI(child_dip)->devi_ref != 0) {
876510468SRaymond.Chen@Sun.COM 			while (i++ < hubdi_reset_delay) {
876610468SRaymond.Chen@Sun.COM 				mutex_enter(HUBD_MUTEX(hubd));
876710468SRaymond.Chen@Sun.COM 				rval = hubd_delete_child(hubd, reset_port,
876810468SRaymond.Chen@Sun.COM 				    NDI_DEVI_REMOVE, B_FALSE);
876910468SRaymond.Chen@Sun.COM 				mutex_exit(HUBD_MUTEX(hubd));
877010468SRaymond.Chen@Sun.COM 				if (rval == USB_SUCCESS)
877110468SRaymond.Chen@Sun.COM 					break;
877210468SRaymond.Chen@Sun.COM 
877310468SRaymond.Chen@Sun.COM 				delay(drv_usectohz(1000000)); /* 1s */
877410468SRaymond.Chen@Sun.COM 			}
877510468SRaymond.Chen@Sun.COM 		}
877610468SRaymond.Chen@Sun.COM 
877710468SRaymond.Chen@Sun.COM 		ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
877810468SRaymond.Chen@Sun.COM 		ndi_devi_enter(rh_dip, &rh_circ);
877910468SRaymond.Chen@Sun.COM 		ndi_devi_enter(hdip, &circ);
878010468SRaymond.Chen@Sun.COM 
87814844Slg150142 		mutex_enter(HUBD_MUTEX(hubd));
87824844Slg150142 
87834844Slg150142 		/* Then force detaching the device */
878410468SRaymond.Chen@Sun.COM 		if ((rval != USB_SUCCESS) && (hubd_delete_child(hubd,
878510468SRaymond.Chen@Sun.COM 		    reset_port, NDI_DEVI_REMOVE, B_FALSE) != USB_SUCCESS)) {
87864844Slg150142 			USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
87874844Slg150142 			    "%s%d cannot be reset due to other applications "
87884844Slg150142 			    "are using it, please first close these "
87894844Slg150142 			    "applications, then disconnect and reconnect"
87904844Slg150142 			    "the device.", devname, devinst);
87914844Slg150142 
87924844Slg150142 			mutex_exit(HUBD_MUTEX(hubd));
87934844Slg150142 			/* post a re-connect event */
87944844Slg150142 			hubd_post_event(hubd, reset_port,
87954844Slg150142 			    USBA_EVENT_TAG_HOT_INSERTION);
87964844Slg150142 			mutex_enter(HUBD_MUTEX(hubd));
87974844Slg150142 		} else {
87984844Slg150142 			(void) hubd_determine_port_status(hubd, reset_port,
87994844Slg150142 			    &status, &change, HUBD_ACK_ALL_CHANGES);
88004844Slg150142 
88014844Slg150142 			/* Reset the parent hubd port and create new child */
88024844Slg150142 			if (status & PORT_STATUS_CCS) {
88034844Slg150142 				online_child |=	(hubd_handle_port_connect(hubd,
88044844Slg150142 				    reset_port) == USB_SUCCESS);
88054844Slg150142 			}
88064844Slg150142 		}
88074844Slg150142 	}
88084844Slg150142 
88094844Slg150142 	/* release locks so we can do a devfs_clean */
88104844Slg150142 	mutex_exit(HUBD_MUTEX(hubd));
88114844Slg150142 
88124844Slg150142 	/* delete cached dv_node's but drop locks first */
88134844Slg150142 	ndi_devi_exit(hdip, circ);
88144844Slg150142 	ndi_devi_exit(rh_dip, rh_circ);
88154844Slg150142 	ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ);
88164844Slg150142 
88174844Slg150142 	(void) devfs_clean(rh_dip, NULL, 0);
88184844Slg150142 
88194844Slg150142 	/* now check if any children need onlining */
88204844Slg150142 	if (online_child) {
88214844Slg150142 		USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
88224844Slg150142 		    "hubd_reset_thread: onlining children");
88234844Slg150142 
88244844Slg150142 		(void) ndi_devi_online(hubd->h_dip, 0);
88254844Slg150142 	}
88264844Slg150142 
88274844Slg150142 	mutex_enter(HUBD_MUTEX(hubd));
88284844Slg150142 
88294844Slg150142 	/* allow hotplug thread now */
88304844Slg150142 	hubd->h_hotplug_thread--;
88314844Slg150142 Fail:
88324844Slg150142 	hubd_start_polling(hubd, 0);
88334844Slg150142 
88344844Slg150142 	/* mark this device as idle */
88354844Slg150142 	(void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
88364844Slg150142 
88374844Slg150142 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
88384844Slg150142 	    "hubd_reset_thread: exit, %d", hubd->h_hotplug_thread);
88394844Slg150142 
88404844Slg150142 	hubd->h_reset_port[reset_port] = B_FALSE;
88414844Slg150142 
88424844Slg150142 	mutex_exit(HUBD_MUTEX(hubd));
88434844Slg150142 
88444844Slg150142 	ndi_rele_devi(hdip);
88454844Slg150142 }
88464844Slg150142 
88474844Slg150142 /*
88484844Slg150142  * hubd_check_same_device:
88496112Sqz150045  *	- open the default pipe of the device.
88506112Sqz150045  *	- compare the old and new descriptors of the device.
88516112Sqz150045  *	- close the default pipe.
88524844Slg150142  */
88534844Slg150142 static int
hubd_check_same_device(hubd_t * hubd,usb_port_t port)88544844Slg150142 hubd_check_same_device(hubd_t *hubd, usb_port_t port)
88554844Slg150142 {
88566112Sqz150045 	dev_info_t		*dip = hubd->h_children_dips[port];
88574844Slg150142 	usb_pipe_handle_t	ph;
88586112Sqz150045 	int			rval = USB_FAILURE;
88594844Slg150142 
88604844Slg150142 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
88614844Slg150142 
88624844Slg150142 	mutex_exit(HUBD_MUTEX(hubd));
88634844Slg150142 	/* Open the default pipe to operate the device */
88644844Slg150142 	if (usb_pipe_open(dip, NULL, NULL,
88654844Slg150142 	    USB_FLAGS_SLEEP| USBA_FLAGS_PRIVILEGED,
88664844Slg150142 	    &ph) == USB_SUCCESS) {
88674844Slg150142 		/*
88684844Slg150142 		 * Check that if the device's descriptors are different
88694844Slg150142 		 * from the values saved before the port reset.
88704844Slg150142 		 */
88714844Slg150142 		rval = usb_check_same_device(dip,
88724844Slg150142 		    hubd->h_log_handle, USB_LOG_L0,
88734844Slg150142 		    DPRINT_MASK_ALL, USB_CHK_ALL, NULL);
88744844Slg150142 
88754844Slg150142 		usb_pipe_close(dip, ph, USB_FLAGS_SLEEP |
88764844Slg150142 		    USBA_FLAGS_PRIVILEGED, NULL, NULL);
88774844Slg150142 	}
88784844Slg150142 	mutex_enter(HUBD_MUTEX(hubd));
88794844Slg150142 
88804844Slg150142 	return (rval);
88814844Slg150142 }
88824844Slg150142 
88834844Slg150142 /*
88844844Slg150142  * usba_hubdi_reset_device
88856112Sqz150045  *	Called by usb_reset_device to handle usb device reset.
88864844Slg150142  */
88874844Slg150142 int
usba_hubdi_reset_device(dev_info_t * dip,usb_dev_reset_lvl_t reset_level)88884844Slg150142 usba_hubdi_reset_device(dev_info_t *dip, usb_dev_reset_lvl_t reset_level)
88894844Slg150142 {
88904844Slg150142 	hubd_t			*hubd;
88914844Slg150142 	usb_port_t		port = 0;
88924844Slg150142 	dev_info_t		*hdip;
88934844Slg150142 	usb_pipe_state_t	prev_pipe_state = 0;
88944844Slg150142 	usba_device_t		*usba_device;
88954844Slg150142 	hubd_reset_arg_t	*arg;
88964844Slg150142 	int			i, ph_open_cnt;
88974844Slg150142 	int			rval = USB_FAILURE;
88984844Slg150142 
88994844Slg150142 	if ((!dip) || usba_is_root_hub(dip)) {
890010468SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
890110468SRaymond.Chen@Sun.COM 		    "usba_hubdi_reset_device: NULL dip or root hub");
89024844Slg150142 
89034844Slg150142 		return (USB_INVALID_ARGS);
89044844Slg150142 	}
89054844Slg150142 
89064844Slg150142 	if (!usb_owns_device(dip)) {
890710468SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
890810468SRaymond.Chen@Sun.COM 		    "usba_hubdi_reset_device: Not owns the device");
89094844Slg150142 
89104844Slg150142 		return (USB_INVALID_PERM);
89114844Slg150142 	}
89124844Slg150142 
89134844Slg150142 	if ((reset_level != USB_RESET_LVL_REATTACH) &&
89144844Slg150142 	    (reset_level != USB_RESET_LVL_DEFAULT)) {
891510468SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
891610468SRaymond.Chen@Sun.COM 		    "usba_hubdi_reset_device: Unknown flags");
89174844Slg150142 
89184844Slg150142 		return (USB_INVALID_ARGS);
89194844Slg150142 	}
89204844Slg150142 
89214844Slg150142 	if ((hdip = ddi_get_parent(dip)) == NULL) {
892210468SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
892310468SRaymond.Chen@Sun.COM 		    "usba_hubdi_reset_device: fail to get parent hub");
89244844Slg150142 
89254844Slg150142 		return (USB_INVALID_ARGS);
89264844Slg150142 	}
89274844Slg150142 
89284844Slg150142 	if ((hubd = hubd_get_soft_state(hdip)) == NULL) {
892910468SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
893010468SRaymond.Chen@Sun.COM 		    "usba_hubdi_reset_device: fail to get hub softstate");
89314844Slg150142 
89324844Slg150142 		return (USB_INVALID_ARGS);
89334844Slg150142 	}
89344844Slg150142 
89354844Slg150142 	mutex_enter(HUBD_MUTEX(hubd));
89364844Slg150142 
89374844Slg150142 	/* make sure the hub is connected before trying any kinds of reset. */
89384844Slg150142 	if ((hubd->h_dev_state == USB_DEV_DISCONNECTED) ||
89394844Slg150142 	    (hubd->h_dev_state == USB_DEV_SUSPENDED)) {
89404844Slg150142 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
89414844Slg150142 		    "usb_reset_device: the state %d of the hub/roothub "
89426898Sfb209375 		    "associated to the device 0x%p is incorrect",
89436898Sfb209375 		    hubd->h_dev_state, (void *)dip);
89444844Slg150142 		mutex_exit(HUBD_MUTEX(hubd));
89454844Slg150142 
89464844Slg150142 		return (USB_INVALID_ARGS);
89474844Slg150142 	}
89484844Slg150142 
89494844Slg150142 	mutex_exit(HUBD_MUTEX(hubd));
89504844Slg150142 
89514844Slg150142 	port = hubd_child_dip2port(hubd, dip);
89524844Slg150142 
89534844Slg150142 	mutex_enter(HUBD_MUTEX(hubd));
89544844Slg150142 
89554844Slg150142 	if (hubd->h_reset_port[port]) {
89564844Slg150142 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
89574844Slg150142 		    "usb_reset_device: the corresponding port is resetting");
89584844Slg150142 		mutex_exit(HUBD_MUTEX(hubd));
89594844Slg150142 
89604844Slg150142 		return (USB_SUCCESS);
89614844Slg150142 	}
89624844Slg150142 
89634844Slg150142 	/*
89644844Slg150142 	 * For Default reset, client drivers should first close all the pipes
89654844Slg150142 	 * except default pipe before calling the function, also should not
89664844Slg150142 	 * call the function during interrupt context.
89674844Slg150142 	 */
89684844Slg150142 	if (reset_level == USB_RESET_LVL_DEFAULT) {
89694844Slg150142 		usba_device = hubd->h_usba_devices[port];
89704844Slg150142 		mutex_exit(HUBD_MUTEX(hubd));
89714844Slg150142 
89724844Slg150142 		if (servicing_interrupt()) {
89734844Slg150142 			USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
89744844Slg150142 			    "usb_reset_device: during interrput context, quit");
89754844Slg150142 
89764844Slg150142 			return (USB_INVALID_CONTEXT);
89774844Slg150142 		}
89784844Slg150142 		/* Check if all the pipes have been closed */
89794844Slg150142 		for (ph_open_cnt = 0, i = 1; i < USBA_N_ENDPOINTS; i++) {
89804844Slg150142 			if (usba_device->usb_ph_list[i].usba_ph_data) {
89814844Slg150142 				ph_open_cnt++;
89824844Slg150142 				break;
89834844Slg150142 			}
89844844Slg150142 		}
89854844Slg150142 		if (ph_open_cnt) {
89864844Slg150142 			USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
89874844Slg150142 			    "usb_reset_device: %d pipes are still open",
89884844Slg150142 			    ph_open_cnt);
89894844Slg150142 
89904844Slg150142 			return (USB_BUSY);
89914844Slg150142 		}
89924844Slg150142 		mutex_enter(HUBD_MUTEX(hubd));
89934844Slg150142 	}
89944844Slg150142 
89954844Slg150142 	/* Don't perform reset while the device is detaching */
89964844Slg150142 	if (hubd->h_port_state[port] & HUBD_CHILD_DETACHING) {
89974844Slg150142 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
89984844Slg150142 		    "usb_reset_device: the device is detaching, "
89994844Slg150142 		    "cannot be reset");
90004844Slg150142 		mutex_exit(HUBD_MUTEX(hubd));
90014844Slg150142 
90024844Slg150142 		return (USB_FAILURE);
90034844Slg150142 	}
90044844Slg150142 
90054844Slg150142 	hubd->h_reset_port[port] = B_TRUE;
90064844Slg150142 	hdip = hubd->h_dip;
90074844Slg150142 	mutex_exit(HUBD_MUTEX(hubd));
90084844Slg150142 
90094844Slg150142 	/* Don't allow hub detached during the reset */
90104844Slg150142 	ndi_hold_devi(hdip);
90114844Slg150142 
90124844Slg150142 	mutex_enter(HUBD_MUTEX(hubd));
90134844Slg150142 	hubd_pm_busy_component(hubd, hdip, 0);
90144844Slg150142 	mutex_exit(HUBD_MUTEX(hubd));
90154844Slg150142 	/* go full power */
90164844Slg150142 	(void) pm_raise_power(hdip, 0, USB_DEV_OS_FULL_PWR);
90174844Slg150142 	mutex_enter(HUBD_MUTEX(hubd));
90184844Slg150142 
90194844Slg150142 	hubd->h_hotplug_thread++;
90204844Slg150142 
90214844Slg150142 	/* stop polling if it was active */
90224844Slg150142 	if (hubd->h_ep1_ph) {
90234844Slg150142 		mutex_exit(HUBD_MUTEX(hubd));
90244844Slg150142 		(void) usb_pipe_get_state(hubd->h_ep1_ph, &prev_pipe_state,
90254844Slg150142 		    USB_FLAGS_SLEEP);
90264844Slg150142 		mutex_enter(HUBD_MUTEX(hubd));
90274844Slg150142 
90284844Slg150142 		if (prev_pipe_state == USB_PIPE_STATE_ACTIVE) {
90294844Slg150142 			hubd_stop_polling(hubd);
90304844Slg150142 		}
90314844Slg150142 	}
90324844Slg150142 
90334844Slg150142 	switch (reset_level) {
90344844Slg150142 	case USB_RESET_LVL_REATTACH:
90354844Slg150142 		mutex_exit(HUBD_MUTEX(hubd));
90364844Slg150142 		arg = (hubd_reset_arg_t *)kmem_zalloc(
90374844Slg150142 		    sizeof (hubd_reset_arg_t), KM_SLEEP);
90384844Slg150142 		arg->hubd = hubd;
90394844Slg150142 		arg->reset_port = port;
90404844Slg150142 		mutex_enter(HUBD_MUTEX(hubd));
90414844Slg150142 
90424844Slg150142 		if ((rval = usb_async_req(hdip, hubd_reset_thread,
90434844Slg150142 		    (void *)arg, 0)) == USB_SUCCESS) {
90444844Slg150142 			hubd->h_hotplug_thread--;
90454844Slg150142 			mutex_exit(HUBD_MUTEX(hubd));
90464844Slg150142 
90474844Slg150142 			return (USB_SUCCESS);
90484844Slg150142 		} else {
90494844Slg150142 			USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
90504844Slg150142 			    "Cannot create reset thread, the device %s%d failed"
90514844Slg150142 			    " to reset", ddi_driver_name(dip),
90524844Slg150142 			    ddi_get_instance(dip));
90534844Slg150142 
90544844Slg150142 			kmem_free(arg, sizeof (hubd_reset_arg_t));
90554844Slg150142 		}
90564844Slg150142 
90574844Slg150142 		break;
90584844Slg150142 	case USB_RESET_LVL_DEFAULT:
90594844Slg150142 		/*
90604844Slg150142 		 * Reset hub port and then recover device's address, set back
90614844Slg150142 		 * device's configuration, hubd_handle_port_connect() will
90624844Slg150142 		 * handle errors happened during this process.
90634844Slg150142 		 */
90644844Slg150142 		if ((rval = hubd_handle_port_connect(hubd, port))
90654844Slg150142 		    == USB_SUCCESS) {
90664844Slg150142 			mutex_exit(HUBD_MUTEX(hubd));
90674844Slg150142 			/* re-open the default pipe */
90684844Slg150142 			rval = usba_persistent_pipe_open(usba_device);
90694844Slg150142 			mutex_enter(HUBD_MUTEX(hubd));
90704844Slg150142 			if (rval != USB_SUCCESS) {
90714844Slg150142 				USB_DPRINTF_L2(DPRINT_MASK_ATTA,
90724844Slg150142 				    hubd->h_log_handle, "failed to reopen "
90734844Slg150142 				    "default pipe after reset, disable hub"
90744844Slg150142 				    "port for %s%d", ddi_driver_name(dip),
90754844Slg150142 				    ddi_get_instance(dip));
90764844Slg150142 				/*
90774844Slg150142 				 * Disable port to set out a hotplug thread
90784844Slg150142 				 * which will handle errors.
90794844Slg150142 				 */
90804844Slg150142 				(void) hubd_disable_port(hubd, port);
90814844Slg150142 			}
90824844Slg150142 		}
90834844Slg150142 
90844844Slg150142 		break;
90854844Slg150142 	default:
90864844Slg150142 
90874844Slg150142 		break;
90884844Slg150142 	}
90894844Slg150142 
90904844Slg150142 	/* allow hotplug thread now */
90914844Slg150142 	hubd->h_hotplug_thread--;
90924844Slg150142 
90934844Slg150142 	if ((hubd->h_dev_state == USB_DEV_ONLINE) && hubd->h_ep1_ph &&
90944844Slg150142 	    (prev_pipe_state == USB_PIPE_STATE_ACTIVE)) {
90954844Slg150142 		hubd_start_polling(hubd, 0);
90964844Slg150142 	}
90974844Slg150142 
90984844Slg150142 	hubd_pm_idle_component(hubd, hdip, 0);
90994844Slg150142 
91004844Slg150142 	/* Clear reset mark for the port. */
91014844Slg150142 	hubd->h_reset_port[port] = B_FALSE;
91024844Slg150142 
91034844Slg150142 	mutex_exit(HUBD_MUTEX(hubd));
91044844Slg150142 
91054844Slg150142 	ndi_rele_devi(hdip);
91064844Slg150142 
91074844Slg150142 	return (rval);
91084844Slg150142 }
9109