xref: /illumos-gate/usr/src/uts/common/io/usb/usba/hubdi.c (revision d96925c4)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5fef1e07eSsl  * Common Development and Distribution License (the "License").
6fef1e07eSsl  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22f5b8369cSRaymond Chen  * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
23cd21e7c5SGarrett D'Amore  * Copyright 2012 Garrett D'Amore <garrett@damore.org>.  All rights reserved.
24e5815e7aSJosef 'Jeff' Sipek  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
250d2006e4SRobert Mustacchi  * Copyright 2019, Joyent, Inc.
267c478bd9Sstevel@tonic-gate  */
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate /*
297c478bd9Sstevel@tonic-gate  * USBA: Solaris USB Architecture support for the hub
307c478bd9Sstevel@tonic-gate  * including root hub
317c478bd9Sstevel@tonic-gate  * Most of the code for hubd resides in this file and
327c478bd9Sstevel@tonic-gate  * is shared between the HCD root hub support and hubd
337c478bd9Sstevel@tonic-gate  */
347c478bd9Sstevel@tonic-gate #define	USBA_FRAMEWORK
357c478bd9Sstevel@tonic-gate #include <sys/usb/usba.h>
367c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usba_devdb.h>
377c478bd9Sstevel@tonic-gate #include <sys/sunndi.h>
387c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usba_impl.h>
397c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usba_types.h>
407c478bd9Sstevel@tonic-gate #include <sys/usb/usba/hubdi.h>
417c478bd9Sstevel@tonic-gate #include <sys/usb/usba/hcdi_impl.h>
427c478bd9Sstevel@tonic-gate #include <sys/usb/hubd/hub.h>
437c478bd9Sstevel@tonic-gate #include <sys/usb/hubd/hubdvar.h>
447c478bd9Sstevel@tonic-gate #include <sys/usb/hubd/hubd_impl.h>
457c478bd9Sstevel@tonic-gate #include <sys/kobj.h>
467c478bd9Sstevel@tonic-gate #include <sys/kobj_lex.h>
477c478bd9Sstevel@tonic-gate #include <sys/fs/dv_node.h>
48d29f5a71Szhigang lu - Sun Microsystems - Beijing China #include <sys/strsun.h>
497c478bd9Sstevel@tonic-gate 
5040482326SVincent Wang /*
5140482326SVincent Wang  * External functions
5240482326SVincent Wang  */
5340482326SVincent Wang extern boolean_t consconfig_console_is_ready(void);
5440482326SVincent Wang 
557c478bd9Sstevel@tonic-gate /*
567c478bd9Sstevel@tonic-gate  * Prototypes for static functions
577c478bd9Sstevel@tonic-gate  */
58*d96925c4SRichard Lowe static	int	usba_hubdi_bus_ctl(dev_info_t *dip,
59*d96925c4SRichard Lowe     dev_info_t *rdip,
60*d96925c4SRichard Lowe     ddi_ctl_enum_t op,
61*d96925c4SRichard Lowe     void *arg,
62*d96925c4SRichard Lowe     void *result);
63*d96925c4SRichard Lowe 
64*d96925c4SRichard Lowe static int	usba_hubdi_map_fault(dev_info_t *dip,
65*d96925c4SRichard Lowe     dev_info_t *rdip,
66*d96925c4SRichard Lowe     struct hat *hat,
67*d96925c4SRichard Lowe     struct seg *seg,
68*d96925c4SRichard Lowe     caddr_t addr,
69*d96925c4SRichard Lowe     struct devpage *dp,
70*d96925c4SRichard Lowe     pfn_t pfn,
71*d96925c4SRichard Lowe     uint_t prot,
72*d96925c4SRichard Lowe     uint_t lock);
737c478bd9Sstevel@tonic-gate 
747c478bd9Sstevel@tonic-gate static int hubd_busop_get_eventcookie(dev_info_t *dip,
75*d96925c4SRichard Lowe     dev_info_t *rdip,
76*d96925c4SRichard Lowe     char *eventname,
77*d96925c4SRichard Lowe     ddi_eventcookie_t *cookie);
787c478bd9Sstevel@tonic-gate static int hubd_busop_add_eventcall(dev_info_t *dip,
79*d96925c4SRichard Lowe     dev_info_t *rdip,
80*d96925c4SRichard Lowe     ddi_eventcookie_t cookie,
81*d96925c4SRichard Lowe     void (*callback)(dev_info_t *dip, ddi_eventcookie_t cookie, void *arg,
82*d96925c4SRichard Lowe 	void *bus_impldata),
83*d96925c4SRichard Lowe     void *arg, ddi_callback_id_t *cb_id);
847c478bd9Sstevel@tonic-gate static int hubd_busop_remove_eventcall(dev_info_t *dip,
85*d96925c4SRichard Lowe     ddi_callback_id_t cb_id);
867c478bd9Sstevel@tonic-gate static int hubd_bus_config(dev_info_t *dip,
87*d96925c4SRichard Lowe     uint_t flag,
88*d96925c4SRichard Lowe     ddi_bus_config_op_t op,
89*d96925c4SRichard Lowe     void *arg,
90*d96925c4SRichard Lowe     dev_info_t **child);
917c478bd9Sstevel@tonic-gate static int hubd_bus_unconfig(dev_info_t *dip,
92*d96925c4SRichard Lowe     uint_t flag,
93*d96925c4SRichard Lowe     ddi_bus_config_op_t op,
94*d96925c4SRichard Lowe     void *arg);
957c478bd9Sstevel@tonic-gate static int hubd_bus_power(dev_info_t *dip, void *impl_arg,
96*d96925c4SRichard Lowe     pm_bus_power_op_t op, void *arg, void *result);
977c478bd9Sstevel@tonic-gate 
987c478bd9Sstevel@tonic-gate static usb_port_t  hubd_get_port_num(hubd_t *, struct devctl_iocdata *);
997c478bd9Sstevel@tonic-gate static dev_info_t *hubd_get_child_dip(hubd_t *, usb_port_t);
1007c478bd9Sstevel@tonic-gate static uint_t hubd_cfgadm_state(hubd_t *, usb_port_t);
1017c478bd9Sstevel@tonic-gate static int hubd_toggle_port(hubd_t *, usb_port_t);
1027c478bd9Sstevel@tonic-gate static void hubd_register_cpr_callback(hubd_t *);
1037c478bd9Sstevel@tonic-gate static void hubd_unregister_cpr_callback(hubd_t *);
1047c478bd9Sstevel@tonic-gate 
1057c478bd9Sstevel@tonic-gate /*
1067c478bd9Sstevel@tonic-gate  * Busops vector for USB HUB's
1077c478bd9Sstevel@tonic-gate  */
1087c478bd9Sstevel@tonic-gate struct bus_ops usba_hubdi_busops =	{
1097c478bd9Sstevel@tonic-gate 	BUSO_REV,
1107c478bd9Sstevel@tonic-gate 	nullbusmap,			/* bus_map */
1117c478bd9Sstevel@tonic-gate 	NULL,				/* bus_get_intrspec */
1127c478bd9Sstevel@tonic-gate 	NULL,				/* bus_add_intrspec */
1137c478bd9Sstevel@tonic-gate 	NULL,				/* bus_remove_intrspec */
1147c478bd9Sstevel@tonic-gate 	usba_hubdi_map_fault,		/* bus_map_fault */
115cd21e7c5SGarrett D'Amore 	NULL,				/* bus_dma_map */
1167c478bd9Sstevel@tonic-gate 	ddi_dma_allochdl,
1177c478bd9Sstevel@tonic-gate 	ddi_dma_freehdl,
1187c478bd9Sstevel@tonic-gate 	ddi_dma_bindhdl,
1197c478bd9Sstevel@tonic-gate 	ddi_dma_unbindhdl,
1207c478bd9Sstevel@tonic-gate 	ddi_dma_flush,
1217c478bd9Sstevel@tonic-gate 	ddi_dma_win,
1227c478bd9Sstevel@tonic-gate 	ddi_dma_mctl,			/* bus_dma_ctl */
1237c478bd9Sstevel@tonic-gate 	usba_hubdi_bus_ctl,		/* bus_ctl */
1247c478bd9Sstevel@tonic-gate 	ddi_bus_prop_op,		/* bus_prop_op */
1257c478bd9Sstevel@tonic-gate 	hubd_busop_get_eventcookie,
1267c478bd9Sstevel@tonic-gate 	hubd_busop_add_eventcall,
1277c478bd9Sstevel@tonic-gate 	hubd_busop_remove_eventcall,
1287c478bd9Sstevel@tonic-gate 	NULL,				/* bus_post_event */
1297c478bd9Sstevel@tonic-gate 	NULL,				/* bus_intr_ctl */
1307c478bd9Sstevel@tonic-gate 	hubd_bus_config,		/* bus_config */
1317c478bd9Sstevel@tonic-gate 	hubd_bus_unconfig,		/* bus_unconfig */
1327c478bd9Sstevel@tonic-gate 	NULL,				/* bus_fm_init */
1337c478bd9Sstevel@tonic-gate 	NULL,				/* bus_fm_fini */
1347c478bd9Sstevel@tonic-gate 	NULL,				/* bus_fm_access_enter */
1357c478bd9Sstevel@tonic-gate 	NULL,				/* bus_fm_access_exit */
1367c478bd9Sstevel@tonic-gate 	hubd_bus_power			/* bus_power */
1377c478bd9Sstevel@tonic-gate };
1387c478bd9Sstevel@tonic-gate 
139f5b8369cSRaymond Chen #define	USB_HUB_INTEL_VID	0x8087
140f5b8369cSRaymond Chen #define	USB_HUB_INTEL_PID	0x0020
1417c478bd9Sstevel@tonic-gate 
1427c478bd9Sstevel@tonic-gate /*
1437c478bd9Sstevel@tonic-gate  * local variables
1447c478bd9Sstevel@tonic-gate  */
1457c478bd9Sstevel@tonic-gate static kmutex_t	usba_hubdi_mutex;	/* protects USBA HUB data structures */
1467c478bd9Sstevel@tonic-gate 
1477c478bd9Sstevel@tonic-gate static usba_list_entry_t	usba_hubdi_list;
1487c478bd9Sstevel@tonic-gate 
1497c478bd9Sstevel@tonic-gate usb_log_handle_t	hubdi_log_handle;
1507c478bd9Sstevel@tonic-gate uint_t			hubdi_errlevel = USB_LOG_L4;
1517c478bd9Sstevel@tonic-gate uint_t			hubdi_errmask = (uint_t)-1;
152d29f5a71Szhigang lu - Sun Microsystems - Beijing China uint8_t			hubdi_min_pm_threshold = 5; /* seconds */
153aa041649SRaymond Chen uint8_t			hubdi_reset_delay = 20; /* seconds */
1548e1b7aa1Sfei feng - Sun Microsystems - Beijing China extern int modrootloaded;
1557c478bd9Sstevel@tonic-gate 
1567c478bd9Sstevel@tonic-gate /*
1577c478bd9Sstevel@tonic-gate  * initialize private data
1587c478bd9Sstevel@tonic-gate  */
1597c478bd9Sstevel@tonic-gate void
usba_hubdi_initialization()1607c478bd9Sstevel@tonic-gate usba_hubdi_initialization()
1617c478bd9Sstevel@tonic-gate {
1627c478bd9Sstevel@tonic-gate 	hubdi_log_handle = usb_alloc_log_hdl(NULL, "hubdi", &hubdi_errlevel,
163c0f24e5bSlg 	    &hubdi_errmask, NULL, 0);
1647c478bd9Sstevel@tonic-gate 
1657c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle,
1667c478bd9Sstevel@tonic-gate 	    "usba_hubdi_initialization");
1677c478bd9Sstevel@tonic-gate 
1687c478bd9Sstevel@tonic-gate 	mutex_init(&usba_hubdi_mutex, NULL, MUTEX_DRIVER, NULL);
1697c478bd9Sstevel@tonic-gate 
1707c478bd9Sstevel@tonic-gate 	usba_init_list(&usba_hubdi_list, NULL, NULL);
1717c478bd9Sstevel@tonic-gate }
1727c478bd9Sstevel@tonic-gate 
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate void
usba_hubdi_destroy()1757c478bd9Sstevel@tonic-gate usba_hubdi_destroy()
1767c478bd9Sstevel@tonic-gate {
1777c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle,
1787c478bd9Sstevel@tonic-gate 	    "usba_hubdi_destroy");
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate 	mutex_destroy(&usba_hubdi_mutex);
1817c478bd9Sstevel@tonic-gate 	usba_destroy_list(&usba_hubdi_list);
1827c478bd9Sstevel@tonic-gate 
1837c478bd9Sstevel@tonic-gate 	usb_free_log_hdl(hubdi_log_handle);
1847c478bd9Sstevel@tonic-gate }
1857c478bd9Sstevel@tonic-gate 
1867c478bd9Sstevel@tonic-gate 
1877c478bd9Sstevel@tonic-gate /*
1887c478bd9Sstevel@tonic-gate  * Called by an	HUB to attach an instance of the driver
1897c478bd9Sstevel@tonic-gate  *	make this instance known to USBA
1907c478bd9Sstevel@tonic-gate  *	the HUB	should initialize usba_hubdi structure prior
1917c478bd9Sstevel@tonic-gate  *	to calling this	interface
1927c478bd9Sstevel@tonic-gate  */
193ff0e937bSRaymond Chen int
usba_hubdi_register(dev_info_t * dip,uint_t flags)194993e3fafSRobert Mustacchi usba_hubdi_register(dev_info_t	*dip, uint_t flags)
1957c478bd9Sstevel@tonic-gate {
1967c478bd9Sstevel@tonic-gate 	usba_hubdi_t *hubdi = kmem_zalloc(sizeof (usba_hubdi_t), KM_SLEEP);
1977c478bd9Sstevel@tonic-gate 	usba_device_t *usba_device = usba_get_usba_device(dip);
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle,
2007c478bd9Sstevel@tonic-gate 	    "usba_hubdi_register: %s", ddi_node_name(dip));
2017c478bd9Sstevel@tonic-gate 
2027c478bd9Sstevel@tonic-gate 	hubdi->hubdi_dip = dip;
2037c478bd9Sstevel@tonic-gate 	hubdi->hubdi_flags = flags;
2047c478bd9Sstevel@tonic-gate 
2057c478bd9Sstevel@tonic-gate 	usba_device->usb_hubdi = hubdi;
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate 	/*
2087c478bd9Sstevel@tonic-gate 	 * add this hubdi instance to the list of known hubdi's
2097c478bd9Sstevel@tonic-gate 	 */
2107c478bd9Sstevel@tonic-gate 	usba_init_list(&hubdi->hubdi_list, (usb_opaque_t)hubdi,
2117c478bd9Sstevel@tonic-gate 	    usba_hcdi_get_hcdi(usba_device->usb_root_hub_dip)->
2127c478bd9Sstevel@tonic-gate 	    hcdi_iblock_cookie);
2137c478bd9Sstevel@tonic-gate 	mutex_enter(&usba_hubdi_mutex);
2147c478bd9Sstevel@tonic-gate 	usba_add_to_list(&usba_hubdi_list, &hubdi->hubdi_list);
2157c478bd9Sstevel@tonic-gate 	mutex_exit(&usba_hubdi_mutex);
2167c478bd9Sstevel@tonic-gate 
2177c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
2187c478bd9Sstevel@tonic-gate }
2197c478bd9Sstevel@tonic-gate 
2207c478bd9Sstevel@tonic-gate 
2217c478bd9Sstevel@tonic-gate /*
2227c478bd9Sstevel@tonic-gate  * Called by an	HUB to detach an instance of the driver
2237c478bd9Sstevel@tonic-gate  */
224ff0e937bSRaymond Chen int
usba_hubdi_unregister(dev_info_t * dip)2257c478bd9Sstevel@tonic-gate usba_hubdi_unregister(dev_info_t *dip)
2267c478bd9Sstevel@tonic-gate {
2277c478bd9Sstevel@tonic-gate 	usba_device_t *usba_device = usba_get_usba_device(dip);
2287c478bd9Sstevel@tonic-gate 	usba_hubdi_t *hubdi = usba_device->usb_hubdi;
2297c478bd9Sstevel@tonic-gate 
2307c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle,
2317c478bd9Sstevel@tonic-gate 	    "usba_hubdi_unregister: %s", ddi_node_name(dip));
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate 	mutex_enter(&usba_hubdi_mutex);
2347c478bd9Sstevel@tonic-gate 	(void) usba_rm_from_list(&usba_hubdi_list, &hubdi->hubdi_list);
2357c478bd9Sstevel@tonic-gate 	mutex_exit(&usba_hubdi_mutex);
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate 	usba_destroy_list(&hubdi->hubdi_list);
2387c478bd9Sstevel@tonic-gate 
2397c478bd9Sstevel@tonic-gate 	kmem_free(hubdi, sizeof (usba_hubdi_t));
2407c478bd9Sstevel@tonic-gate 
2417c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
2427c478bd9Sstevel@tonic-gate }
2437c478bd9Sstevel@tonic-gate 
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate /*
2467c478bd9Sstevel@tonic-gate  * misc bus routines currently not used
2477c478bd9Sstevel@tonic-gate  */
2487c478bd9Sstevel@tonic-gate /*ARGSUSED*/
2497c478bd9Sstevel@tonic-gate static int
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)2507c478bd9Sstevel@tonic-gate usba_hubdi_map_fault(dev_info_t *dip,
251*d96925c4SRichard Lowe     dev_info_t	*rdip,
252*d96925c4SRichard Lowe     struct hat	*hat,
253*d96925c4SRichard Lowe     struct seg	*seg,
254*d96925c4SRichard Lowe     caddr_t	addr,
255*d96925c4SRichard Lowe     struct devpage	*dp,
256*d96925c4SRichard Lowe     pfn_t		pfn,
257*d96925c4SRichard Lowe     uint_t		prot,
258*d96925c4SRichard Lowe     uint_t		lock)
2597c478bd9Sstevel@tonic-gate {
2607c478bd9Sstevel@tonic-gate 	return (DDI_FAILURE);
2617c478bd9Sstevel@tonic-gate }
2627c478bd9Sstevel@tonic-gate 
2637c478bd9Sstevel@tonic-gate 
2647c478bd9Sstevel@tonic-gate /*
2657c478bd9Sstevel@tonic-gate  * root hub support. the root hub uses the same devi as the HCD
2667c478bd9Sstevel@tonic-gate  */
2677c478bd9Sstevel@tonic-gate int
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)2687c478bd9Sstevel@tonic-gate usba_hubdi_bind_root_hub(dev_info_t *dip,
269*d96925c4SRichard Lowe     uchar_t	*root_hub_config_descriptor,
270*d96925c4SRichard Lowe     size_t config_length,
271*d96925c4SRichard Lowe     usb_dev_descr_t *root_hub_device_descriptor)
2727c478bd9Sstevel@tonic-gate {
2737c478bd9Sstevel@tonic-gate 	usba_device_t *usba_device;
2747c478bd9Sstevel@tonic-gate 	usba_hcdi_t *hcdi = usba_hcdi_get_hcdi(dip);
2757c478bd9Sstevel@tonic-gate 	hubd_t	*root_hubd;
2767c478bd9Sstevel@tonic-gate 	usb_pipe_handle_t ph = NULL;
2777c478bd9Sstevel@tonic-gate 	dev_info_t *child = ddi_get_child(dip);
2787c478bd9Sstevel@tonic-gate 
2797c478bd9Sstevel@tonic-gate 	if (ndi_prop_create_boolean(DDI_DEV_T_NONE, dip,
2807c478bd9Sstevel@tonic-gate 	    "root-hub") != NDI_SUCCESS) {
2817c478bd9Sstevel@tonic-gate 
2827c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
2837c478bd9Sstevel@tonic-gate 	}
2847c478bd9Sstevel@tonic-gate 
285de6f998eSrui wang - Sun Microsystems - Beijing China 	usba_add_root_hub(dip);
286de6f998eSrui wang - Sun Microsystems - Beijing China 
2877c478bd9Sstevel@tonic-gate 	root_hubd = kmem_zalloc(sizeof (hubd_t), KM_SLEEP);
2887c478bd9Sstevel@tonic-gate 
2897c478bd9Sstevel@tonic-gate 	/*
2907c478bd9Sstevel@tonic-gate 	 * create and initialize a usba_device structure
2917c478bd9Sstevel@tonic-gate 	 */
2927c478bd9Sstevel@tonic-gate 	usba_device = usba_alloc_usba_device(dip);
2937c478bd9Sstevel@tonic-gate 
2947c478bd9Sstevel@tonic-gate 	mutex_enter(&usba_device->usb_mutex);
2957c478bd9Sstevel@tonic-gate 	usba_device->usb_hcdi_ops = hcdi->hcdi_ops;
2967c478bd9Sstevel@tonic-gate 	usba_device->usb_cfg = root_hub_config_descriptor;
2977c478bd9Sstevel@tonic-gate 	usba_device->usb_cfg_length = config_length;
2987c478bd9Sstevel@tonic-gate 	usba_device->usb_dev_descr = root_hub_device_descriptor;
2997c478bd9Sstevel@tonic-gate 	usba_device->usb_port = 1;
3007c478bd9Sstevel@tonic-gate 	usba_device->usb_addr = ROOT_HUB_ADDR;
3017c478bd9Sstevel@tonic-gate 	usba_device->usb_root_hubd = root_hubd;
3027c478bd9Sstevel@tonic-gate 	usba_device->usb_cfg_array = kmem_zalloc(sizeof (uchar_t *),
303c0f24e5bSlg 	    KM_SLEEP);
3047c478bd9Sstevel@tonic-gate 	usba_device->usb_cfg_array_length = sizeof (uchar_t *);
3057c478bd9Sstevel@tonic-gate 
3067c478bd9Sstevel@tonic-gate 	usba_device->usb_cfg_array_len = kmem_zalloc(sizeof (uint16_t),
307c0f24e5bSlg 	    KM_SLEEP);
3087c478bd9Sstevel@tonic-gate 	usba_device->usb_cfg_array_len_length = sizeof (uint16_t);
3097c478bd9Sstevel@tonic-gate 
3107c478bd9Sstevel@tonic-gate 	usba_device->usb_cfg_array[0] = root_hub_config_descriptor;
3117c478bd9Sstevel@tonic-gate 	usba_device->usb_cfg_array_len[0] =
312c0f24e5bSlg 	    sizeof (root_hub_config_descriptor);
3137c478bd9Sstevel@tonic-gate 
3147c478bd9Sstevel@tonic-gate 	usba_device->usb_cfg_str_descr = kmem_zalloc(sizeof (uchar_t *),
315c0f24e5bSlg 	    KM_SLEEP);
3167c478bd9Sstevel@tonic-gate 	usba_device->usb_n_cfgs = 1;
3177c478bd9Sstevel@tonic-gate 	usba_device->usb_n_ifs = 1;
3187c478bd9Sstevel@tonic-gate 	usba_device->usb_dip = dip;
3197c478bd9Sstevel@tonic-gate 
3207c478bd9Sstevel@tonic-gate 	usba_device->usb_client_flags = kmem_zalloc(
3217c478bd9Sstevel@tonic-gate 	    usba_device->usb_n_ifs * USBA_CLIENT_FLAG_SIZE, KM_SLEEP);
3227c478bd9Sstevel@tonic-gate 
3237c478bd9Sstevel@tonic-gate 	usba_device->usb_client_attach_list = kmem_zalloc(
3247c478bd9Sstevel@tonic-gate 	    usba_device->usb_n_ifs *
3257c478bd9Sstevel@tonic-gate 	    sizeof (*usba_device->usb_client_attach_list), KM_SLEEP);
3267c478bd9Sstevel@tonic-gate 
3277c478bd9Sstevel@tonic-gate 	usba_device->usb_client_ev_cb_list = kmem_zalloc(
3287c478bd9Sstevel@tonic-gate 	    usba_device->usb_n_ifs *
3297c478bd9Sstevel@tonic-gate 	    sizeof (*usba_device->usb_client_ev_cb_list), KM_SLEEP);
3307c478bd9Sstevel@tonic-gate 
3317c478bd9Sstevel@tonic-gate 	/*
3327c478bd9Sstevel@tonic-gate 	 * The bDeviceProtocol field of root hub device specifies,
333993e3fafSRobert Mustacchi 	 * whether root hub is a Super, High, or Full speed usb device.
3347c478bd9Sstevel@tonic-gate 	 */
335993e3fafSRobert Mustacchi 	if (root_hub_device_descriptor->bDeviceProtocol >= 0x3) {
336993e3fafSRobert Mustacchi 		usba_device->usb_port_status = USBA_SUPER_SPEED_DEV;
337993e3fafSRobert Mustacchi 	} else if (root_hub_device_descriptor->bDeviceProtocol > 0) {
3387c478bd9Sstevel@tonic-gate 		usba_device->usb_port_status = USBA_HIGH_SPEED_DEV;
3397c478bd9Sstevel@tonic-gate 	} else {
3407c478bd9Sstevel@tonic-gate 		usba_device->usb_port_status = USBA_FULL_SPEED_DEV;
3417c478bd9Sstevel@tonic-gate 	}
3427c478bd9Sstevel@tonic-gate 
3437c478bd9Sstevel@tonic-gate 	mutex_exit(&usba_device->usb_mutex);
3447c478bd9Sstevel@tonic-gate 
3457c478bd9Sstevel@tonic-gate 	usba_set_usba_device(dip, usba_device);
3467c478bd9Sstevel@tonic-gate 
3477c478bd9Sstevel@tonic-gate 	/*
3487c478bd9Sstevel@tonic-gate 	 * For the root hub the default pipe is not yet open
3497c478bd9Sstevel@tonic-gate 	 */
3507c478bd9Sstevel@tonic-gate 	if (usb_pipe_open(dip, NULL, NULL,
3517c478bd9Sstevel@tonic-gate 	    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph) != USB_SUCCESS) {
3527c478bd9Sstevel@tonic-gate 		goto fail;
3537c478bd9Sstevel@tonic-gate 	}
3547c478bd9Sstevel@tonic-gate 
3557c478bd9Sstevel@tonic-gate 	/*
3567c478bd9Sstevel@tonic-gate 	 * kill off all OBP children, they may not be fully
3577c478bd9Sstevel@tonic-gate 	 * enumerated
3587c478bd9Sstevel@tonic-gate 	 */
3597c478bd9Sstevel@tonic-gate 	while (child) {
3607c478bd9Sstevel@tonic-gate 		dev_info_t *next = ddi_get_next_sibling(child);
3617c478bd9Sstevel@tonic-gate 		(void) ddi_remove_child(child, 0);
3627c478bd9Sstevel@tonic-gate 		child = next;
3637c478bd9Sstevel@tonic-gate 	}
3647c478bd9Sstevel@tonic-gate 
3657c478bd9Sstevel@tonic-gate 	/*
3667c478bd9Sstevel@tonic-gate 	 * "attach" the root hub driver
3677c478bd9Sstevel@tonic-gate 	 */
3687c478bd9Sstevel@tonic-gate 	if (usba_hubdi_attach(dip, DDI_ATTACH) != DDI_SUCCESS) {
3697c478bd9Sstevel@tonic-gate 		goto fail;
3707c478bd9Sstevel@tonic-gate 	}
3717c478bd9Sstevel@tonic-gate 
3727c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
3737c478bd9Sstevel@tonic-gate 
3747c478bd9Sstevel@tonic-gate fail:
3757c478bd9Sstevel@tonic-gate 	if (ph) {
3767c478bd9Sstevel@tonic-gate 		usb_pipe_close(dip, ph,
3777c478bd9Sstevel@tonic-gate 		    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
3787c478bd9Sstevel@tonic-gate 	}
3797c478bd9Sstevel@tonic-gate 
3807c478bd9Sstevel@tonic-gate 	kmem_free(usba_device->usb_cfg_array,
381c0f24e5bSlg 	    usba_device->usb_cfg_array_length);
3827c478bd9Sstevel@tonic-gate 	kmem_free(usba_device->usb_cfg_array_len,
383c0f24e5bSlg 	    usba_device->usb_cfg_array_len_length);
3847c478bd9Sstevel@tonic-gate 
3857c478bd9Sstevel@tonic-gate 	kmem_free(usba_device->usb_cfg_str_descr, sizeof (uchar_t *));
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate 	usba_free_usba_device(usba_device);
3887c478bd9Sstevel@tonic-gate 
3897c478bd9Sstevel@tonic-gate 	usba_set_usba_device(dip, NULL);
390993e3fafSRobert Mustacchi 
3917c478bd9Sstevel@tonic-gate 	if (root_hubd) {
3927c478bd9Sstevel@tonic-gate 		kmem_free(root_hubd, sizeof (hubd_t));
3937c478bd9Sstevel@tonic-gate 	}
3947c478bd9Sstevel@tonic-gate 
395993e3fafSRobert Mustacchi 	(void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "root-hub");
396993e3fafSRobert Mustacchi 
397993e3fafSRobert Mustacchi 	usba_rem_root_hub(dip);
398993e3fafSRobert Mustacchi 
3997c478bd9Sstevel@tonic-gate 	return (USB_FAILURE);
4007c478bd9Sstevel@tonic-gate }
4017c478bd9Sstevel@tonic-gate 
4027c478bd9Sstevel@tonic-gate 
4037c478bd9Sstevel@tonic-gate int
usba_hubdi_unbind_root_hub(dev_info_t * dip)4047c478bd9Sstevel@tonic-gate usba_hubdi_unbind_root_hub(dev_info_t *dip)
4057c478bd9Sstevel@tonic-gate {
4067c478bd9Sstevel@tonic-gate 	usba_device_t *usba_device;
4077c478bd9Sstevel@tonic-gate 
4087c478bd9Sstevel@tonic-gate 	/* was root hub attached? */
4097c478bd9Sstevel@tonic-gate 	if (!(usba_is_root_hub(dip))) {
4107c478bd9Sstevel@tonic-gate 
4117c478bd9Sstevel@tonic-gate 		/* return success anyway */
4127c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
4137c478bd9Sstevel@tonic-gate 	}
4147c478bd9Sstevel@tonic-gate 
4157c478bd9Sstevel@tonic-gate 	/*
4167c478bd9Sstevel@tonic-gate 	 * usba_hubdi_detach also closes the default pipe
4177c478bd9Sstevel@tonic-gate 	 * and removes properties so there is no need to
4187c478bd9Sstevel@tonic-gate 	 * do it here
4197c478bd9Sstevel@tonic-gate 	 */
4207c478bd9Sstevel@tonic-gate 	if (usba_hubdi_detach(dip, DDI_DETACH) != DDI_SUCCESS) {
4217c478bd9Sstevel@tonic-gate 
4227c478bd9Sstevel@tonic-gate 		if (DEVI_IS_ATTACHING(dip)) {
423d291d9f2Sfrits 			USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
4247c478bd9Sstevel@tonic-gate 			    "failure to unbind root hub after attach failure");
4257c478bd9Sstevel@tonic-gate 		}
4267c478bd9Sstevel@tonic-gate 
4277c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
4287c478bd9Sstevel@tonic-gate 	}
4297c478bd9Sstevel@tonic-gate 
4307c478bd9Sstevel@tonic-gate 	usba_device = usba_get_usba_device(dip);
4317c478bd9Sstevel@tonic-gate 
4327c478bd9Sstevel@tonic-gate 	kmem_free(usba_device->usb_root_hubd, sizeof (hubd_t));
4337c478bd9Sstevel@tonic-gate 
4347c478bd9Sstevel@tonic-gate 	kmem_free(usba_device->usb_cfg_array,
435c0f24e5bSlg 	    usba_device->usb_cfg_array_length);
4367c478bd9Sstevel@tonic-gate 	kmem_free(usba_device->usb_cfg_array_len,
437c0f24e5bSlg 	    usba_device->usb_cfg_array_len_length);
4387c478bd9Sstevel@tonic-gate 
4397c478bd9Sstevel@tonic-gate 	kmem_free(usba_device->usb_cfg_str_descr, sizeof (uchar_t *));
4407c478bd9Sstevel@tonic-gate 
4417c478bd9Sstevel@tonic-gate 	usba_free_usba_device(usba_device);
4427c478bd9Sstevel@tonic-gate 
443de6f998eSrui wang - Sun Microsystems - Beijing China 	usba_rem_root_hub(dip);
444de6f998eSrui wang - Sun Microsystems - Beijing China 
4457c478bd9Sstevel@tonic-gate 	(void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "root-hub");
4467c478bd9Sstevel@tonic-gate 
4477c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
4487c478bd9Sstevel@tonic-gate }
4497c478bd9Sstevel@tonic-gate 
4507c478bd9Sstevel@tonic-gate 
4517c478bd9Sstevel@tonic-gate /*
4527c478bd9Sstevel@tonic-gate  * Actual Hub Driver support code:
4537c478bd9Sstevel@tonic-gate  *	shared by root hub and non-root hubs
4547c478bd9Sstevel@tonic-gate  */
4557c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usbai_version.h>
4567c478bd9Sstevel@tonic-gate 
4577c478bd9Sstevel@tonic-gate /* Debugging support */
458ff0e937bSRaymond Chen uint_t hubd_errlevel	= USB_LOG_L4;
459ff0e937bSRaymond Chen uint_t hubd_errmask	= (uint_t)DPRINT_MASK_ALL;
460ff0e937bSRaymond Chen uint_t hubd_instance_debug = (uint_t)-1;
4617c478bd9Sstevel@tonic-gate static uint_t hubdi_bus_config_debug = 0;
4627c478bd9Sstevel@tonic-gate 
4637c478bd9Sstevel@tonic-gate _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_errlevel))
4647c478bd9Sstevel@tonic-gate _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_errmask))
4657c478bd9Sstevel@tonic-gate _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_instance_debug))
4667c478bd9Sstevel@tonic-gate 
4677c478bd9Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique", msgb))
4687c478bd9Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique", dev_info))
4697c478bd9Sstevel@tonic-gate 
4707c478bd9Sstevel@tonic-gate 
4717c478bd9Sstevel@tonic-gate /*
4727c478bd9Sstevel@tonic-gate  * local variables:
4737c478bd9Sstevel@tonic-gate  *
4747c478bd9Sstevel@tonic-gate  * Amount of time to wait between resetting the port and accessing
4757c478bd9Sstevel@tonic-gate  * the device.	The value is in microseconds.
4767c478bd9Sstevel@tonic-gate  */
4777c478bd9Sstevel@tonic-gate static uint_t hubd_device_delay = 1000000;
4787c478bd9Sstevel@tonic-gate 
4797c478bd9Sstevel@tonic-gate /*
4807c478bd9Sstevel@tonic-gate  * enumeration retry
4817c478bd9Sstevel@tonic-gate  */
4827c478bd9Sstevel@tonic-gate #define	HUBD_PORT_RETRY 5
4837c478bd9Sstevel@tonic-gate static uint_t hubd_retry_enumerate = HUBD_PORT_RETRY;
4847c478bd9Sstevel@tonic-gate 
4857c478bd9Sstevel@tonic-gate /*
4867c478bd9Sstevel@tonic-gate  * Stale hotremoved device cleanup delay
4877c478bd9Sstevel@tonic-gate  */
4887c478bd9Sstevel@tonic-gate #define	HUBD_STALE_DIP_CLEANUP_DELAY	5000000
4897c478bd9Sstevel@tonic-gate static uint_t hubd_dip_cleanup_delay = HUBD_STALE_DIP_CLEANUP_DELAY;
4907c478bd9Sstevel@tonic-gate 
4917c478bd9Sstevel@tonic-gate /*
4927c478bd9Sstevel@tonic-gate  * retries for USB suspend and resume
4937c478bd9Sstevel@tonic-gate  */
4947c478bd9Sstevel@tonic-gate #define	HUBD_SUS_RES_RETRY	2
4957c478bd9Sstevel@tonic-gate 
4967c478bd9Sstevel@tonic-gate void	*hubd_statep;
4977c478bd9Sstevel@tonic-gate 
4987c478bd9Sstevel@tonic-gate /*
4997c478bd9Sstevel@tonic-gate  * prototypes
5007c478bd9Sstevel@tonic-gate  */
5017c478bd9Sstevel@tonic-gate static int hubd_cleanup(dev_info_t *dip, hubd_t  *hubd);
5027c478bd9Sstevel@tonic-gate static int hubd_check_ports(hubd_t  *hubd);
5037c478bd9Sstevel@tonic-gate 
5047c478bd9Sstevel@tonic-gate static int  hubd_open_intr_pipe(hubd_t *hubd);
5057c478bd9Sstevel@tonic-gate static void hubd_start_polling(hubd_t *hubd, int always);
5067c478bd9Sstevel@tonic-gate static void hubd_stop_polling(hubd_t *hubd);
5077c478bd9Sstevel@tonic-gate static void hubd_close_intr_pipe(hubd_t *hubd);
5087c478bd9Sstevel@tonic-gate 
5097c478bd9Sstevel@tonic-gate static void hubd_read_cb(usb_pipe_handle_t pipe, usb_intr_req_t *req);
5107c478bd9Sstevel@tonic-gate static void hubd_exception_cb(usb_pipe_handle_t pipe,
5117c478bd9Sstevel@tonic-gate 						usb_intr_req_t *req);
5127c478bd9Sstevel@tonic-gate static void hubd_hotplug_thread(void *arg);
513ffcd51f3Slg static void hubd_reset_thread(void *arg);
5147c478bd9Sstevel@tonic-gate static int hubd_create_child(dev_info_t *dip,
5157c478bd9Sstevel@tonic-gate 		hubd_t		*hubd,
5167c478bd9Sstevel@tonic-gate 		usba_device_t	*usba_device,
5177c478bd9Sstevel@tonic-gate 		usb_port_status_t port_status,
5187c478bd9Sstevel@tonic-gate 		usb_port_t	port,
5197c478bd9Sstevel@tonic-gate 		int		iteration);
5207c478bd9Sstevel@tonic-gate 
5217c478bd9Sstevel@tonic-gate static int hubd_delete_child(hubd_t *hubd, usb_port_t port, uint_t flag,
5227c478bd9Sstevel@tonic-gate 	boolean_t retry);
5237c478bd9Sstevel@tonic-gate 
5247c478bd9Sstevel@tonic-gate static int hubd_get_hub_descriptor(hubd_t *hubd);
5257c478bd9Sstevel@tonic-gate 
526993e3fafSRobert Mustacchi static int hubd_set_hub_depth(hubd_t *hubd);
527993e3fafSRobert Mustacchi 
52835f36846Ssl static int hubd_get_hub_status_words(hubd_t *hubd, uint16_t *status);
52935f36846Ssl 
5307c478bd9Sstevel@tonic-gate static int hubd_reset_port(hubd_t *hubd, usb_port_t port);
5317c478bd9Sstevel@tonic-gate 
5327c478bd9Sstevel@tonic-gate static int hubd_get_hub_status(hubd_t *hubd);
5337c478bd9Sstevel@tonic-gate 
5347c478bd9Sstevel@tonic-gate static int hubd_handle_port_connect(hubd_t *hubd, usb_port_t port);
5357c478bd9Sstevel@tonic-gate 
5367c478bd9Sstevel@tonic-gate static int hubd_disable_port(hubd_t *hubd, usb_port_t port);
5377c478bd9Sstevel@tonic-gate 
5387c478bd9Sstevel@tonic-gate static int hubd_enable_port(hubd_t *hubd, usb_port_t port);
5397c478bd9Sstevel@tonic-gate static int hubd_recover_disabled_port(hubd_t *hubd, usb_port_t port);
5407c478bd9Sstevel@tonic-gate 
5417c478bd9Sstevel@tonic-gate static int hubd_determine_port_status(hubd_t *hubd, usb_port_t port,
542993e3fafSRobert Mustacchi 	uint16_t *status, uint16_t *change, usb_port_status_t *speed,
543993e3fafSRobert Mustacchi 	uint_t ack_flag);
5447c478bd9Sstevel@tonic-gate 
5457c478bd9Sstevel@tonic-gate static int hubd_enable_all_port_power(hubd_t *hubd);
5467c478bd9Sstevel@tonic-gate static int hubd_disable_all_port_power(hubd_t *hubd);
5477c478bd9Sstevel@tonic-gate static int hubd_disable_port_power(hubd_t *hubd, usb_port_t port);
5487c478bd9Sstevel@tonic-gate static int hubd_enable_port_power(hubd_t *hubd, usb_port_t port);
5497c478bd9Sstevel@tonic-gate 
5507c478bd9Sstevel@tonic-gate static void hubd_free_usba_device(hubd_t *hubd, usba_device_t *usba_device);
5517c478bd9Sstevel@tonic-gate 
5527c478bd9Sstevel@tonic-gate static int hubd_can_suspend(hubd_t *hubd);
5537c478bd9Sstevel@tonic-gate static void hubd_restore_device_state(dev_info_t *dip, hubd_t *hubd);
5547c478bd9Sstevel@tonic-gate static int hubd_setdevaddr(hubd_t *hubd, usb_port_t port);
5557c478bd9Sstevel@tonic-gate static void hubd_setdevconfig(hubd_t *hubd, usb_port_t port);
5567c478bd9Sstevel@tonic-gate 
5577c478bd9Sstevel@tonic-gate static int hubd_register_events(hubd_t *hubd);
5587c478bd9Sstevel@tonic-gate static void hubd_do_callback(hubd_t *hubd, dev_info_t *dip,
5597c478bd9Sstevel@tonic-gate 	ddi_eventcookie_t cookie);
5607c478bd9Sstevel@tonic-gate static void hubd_run_callbacks(hubd_t *hubd, usba_event_t type);
5617c478bd9Sstevel@tonic-gate static void hubd_post_event(hubd_t *hubd, usb_port_t port, usba_event_t type);
5627c478bd9Sstevel@tonic-gate static void hubd_create_pm_components(dev_info_t *dip, hubd_t *hubd);
5637c478bd9Sstevel@tonic-gate 
5647c478bd9Sstevel@tonic-gate static int hubd_disconnect_event_cb(dev_info_t *dip);
5657c478bd9Sstevel@tonic-gate static int hubd_reconnect_event_cb(dev_info_t *dip);
5667c478bd9Sstevel@tonic-gate static int hubd_pre_suspend_event_cb(dev_info_t *dip);
5677c478bd9Sstevel@tonic-gate static int hubd_post_resume_event_cb(dev_info_t *dip);
5687c478bd9Sstevel@tonic-gate static int hubd_cpr_suspend(hubd_t *hubd);
5697c478bd9Sstevel@tonic-gate static void hubd_cpr_resume(dev_info_t *dip);
5707c478bd9Sstevel@tonic-gate static int hubd_restore_state_cb(dev_info_t *dip);
571ffcd51f3Slg static int hubd_check_same_device(hubd_t *hubd, usb_port_t port);
5727c478bd9Sstevel@tonic-gate 
57335f36846Ssl static int hubd_init_power_budget(hubd_t *hubd);
57435f36846Ssl 
5757c478bd9Sstevel@tonic-gate static ndi_event_definition_t hubd_ndi_event_defs[] = {
5767c478bd9Sstevel@tonic-gate 	{USBA_EVENT_TAG_HOT_REMOVAL, DDI_DEVI_REMOVE_EVENT, EPL_KERNEL,
5777c478bd9Sstevel@tonic-gate 						NDI_EVENT_POST_TO_ALL},
5787c478bd9Sstevel@tonic-gate 	{USBA_EVENT_TAG_HOT_INSERTION, DDI_DEVI_INSERT_EVENT, EPL_KERNEL,
5797c478bd9Sstevel@tonic-gate 						NDI_EVENT_POST_TO_ALL},
5807c478bd9Sstevel@tonic-gate 	{USBA_EVENT_TAG_POST_RESUME, USBA_POST_RESUME_EVENT, EPL_KERNEL,
5817c478bd9Sstevel@tonic-gate 						NDI_EVENT_POST_TO_ALL},
5827c478bd9Sstevel@tonic-gate 	{USBA_EVENT_TAG_PRE_SUSPEND, USBA_PRE_SUSPEND_EVENT, EPL_KERNEL,
5837c478bd9Sstevel@tonic-gate 						NDI_EVENT_POST_TO_ALL}
5847c478bd9Sstevel@tonic-gate };
5857c478bd9Sstevel@tonic-gate 
5867c478bd9Sstevel@tonic-gate #define	HUBD_N_NDI_EVENTS \
5877c478bd9Sstevel@tonic-gate 	(sizeof (hubd_ndi_event_defs) / sizeof (ndi_event_definition_t))
5887c478bd9Sstevel@tonic-gate 
5897c478bd9Sstevel@tonic-gate static ndi_event_set_t hubd_ndi_events = {
5907c478bd9Sstevel@tonic-gate 	NDI_EVENTS_REV1, HUBD_N_NDI_EVENTS, hubd_ndi_event_defs};
5917c478bd9Sstevel@tonic-gate 
5927c478bd9Sstevel@tonic-gate /* events received from parent */
5937c478bd9Sstevel@tonic-gate static usb_event_t hubd_events = {
5947c478bd9Sstevel@tonic-gate 	hubd_disconnect_event_cb,
5957c478bd9Sstevel@tonic-gate 	hubd_reconnect_event_cb,
5967c478bd9Sstevel@tonic-gate 	hubd_pre_suspend_event_cb,
5977c478bd9Sstevel@tonic-gate 	hubd_post_resume_event_cb
5987c478bd9Sstevel@tonic-gate };
5997c478bd9Sstevel@tonic-gate 
6007c478bd9Sstevel@tonic-gate 
6017c478bd9Sstevel@tonic-gate /*
6027c478bd9Sstevel@tonic-gate  * hubd_get_soft_state() returns the hubd soft state
6037c478bd9Sstevel@tonic-gate  */
604ff0e937bSRaymond Chen hubd_t *
hubd_get_soft_state(dev_info_t * dip)6057c478bd9Sstevel@tonic-gate hubd_get_soft_state(dev_info_t *dip)
6067c478bd9Sstevel@tonic-gate {
6077c478bd9Sstevel@tonic-gate 	if (dip == NULL) {
6087c478bd9Sstevel@tonic-gate 		return (NULL);
6097c478bd9Sstevel@tonic-gate 	}
6107c478bd9Sstevel@tonic-gate 
611e2c88f0cSGarrett D'Amore 	if (usba_is_root_hub(dip)) {
6127c478bd9Sstevel@tonic-gate 		usba_device_t *usba_device = usba_get_usba_device(dip);
6137c478bd9Sstevel@tonic-gate 
6147c478bd9Sstevel@tonic-gate 		return (usba_device->usb_root_hubd);
6157c478bd9Sstevel@tonic-gate 	} else {
6167c478bd9Sstevel@tonic-gate 		int instance = ddi_get_instance(dip);
6177c478bd9Sstevel@tonic-gate 
6187c478bd9Sstevel@tonic-gate 		return (ddi_get_soft_state(hubd_statep, instance));
6197c478bd9Sstevel@tonic-gate 	}
6207c478bd9Sstevel@tonic-gate }
6217c478bd9Sstevel@tonic-gate 
6227c478bd9Sstevel@tonic-gate 
6237c478bd9Sstevel@tonic-gate /*
6247c478bd9Sstevel@tonic-gate  * PM support functions:
6257c478bd9Sstevel@tonic-gate  */
6267c478bd9Sstevel@tonic-gate /*ARGSUSED*/
6277c478bd9Sstevel@tonic-gate static void
hubd_pm_busy_component(hubd_t * hubd,dev_info_t * dip,int component)6287c478bd9Sstevel@tonic-gate hubd_pm_busy_component(hubd_t *hubd, dev_info_t *dip, int component)
6297c478bd9Sstevel@tonic-gate {
6307c478bd9Sstevel@tonic-gate 	if (hubd->h_hubpm != NULL) {
6317c478bd9Sstevel@tonic-gate 		hubd->h_hubpm->hubp_busy_pm++;
6327c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
6337c478bd9Sstevel@tonic-gate 		if (pm_busy_component(dip, 0) != DDI_SUCCESS) {
6347c478bd9Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
6357c478bd9Sstevel@tonic-gate 			hubd->h_hubpm->hubp_busy_pm--;
6367c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
6377c478bd9Sstevel@tonic-gate 		}
6387c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
6397c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
6407c478bd9Sstevel@tonic-gate 		    "hubd_pm_busy_component: %d", hubd->h_hubpm->hubp_busy_pm);
6417c478bd9Sstevel@tonic-gate 	}
6427c478bd9Sstevel@tonic-gate }
6437c478bd9Sstevel@tonic-gate 
6447c478bd9Sstevel@tonic-gate 
6457c478bd9Sstevel@tonic-gate /*ARGSUSED*/
6467c478bd9Sstevel@tonic-gate static void
hubd_pm_idle_component(hubd_t * hubd,dev_info_t * dip,int component)6477c478bd9Sstevel@tonic-gate hubd_pm_idle_component(hubd_t *hubd, dev_info_t *dip, int component)
6487c478bd9Sstevel@tonic-gate {
6497c478bd9Sstevel@tonic-gate 	if (hubd->h_hubpm != NULL) {
6507c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
6517c478bd9Sstevel@tonic-gate 		if (pm_idle_component(dip, 0) == DDI_SUCCESS) {
6527c478bd9Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
6537c478bd9Sstevel@tonic-gate 			ASSERT(hubd->h_hubpm->hubp_busy_pm > 0);
6547c478bd9Sstevel@tonic-gate 			hubd->h_hubpm->hubp_busy_pm--;
6557c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
6567c478bd9Sstevel@tonic-gate 		}
6577c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
6587c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
6597c478bd9Sstevel@tonic-gate 		    "hubd_pm_idle_component: %d", hubd->h_hubpm->hubp_busy_pm);
6607c478bd9Sstevel@tonic-gate 	}
6617c478bd9Sstevel@tonic-gate }
6627c478bd9Sstevel@tonic-gate 
6637c478bd9Sstevel@tonic-gate 
6647c478bd9Sstevel@tonic-gate /*
6657c478bd9Sstevel@tonic-gate  * track power level changes for children of this instance
6667c478bd9Sstevel@tonic-gate  */
6677c478bd9Sstevel@tonic-gate static void
hubd_set_child_pwrlvl(hubd_t * hubd,usb_port_t port,uint8_t power)6687c478bd9Sstevel@tonic-gate hubd_set_child_pwrlvl(hubd_t *hubd, usb_port_t port, uint8_t power)
6697c478bd9Sstevel@tonic-gate {
6707c478bd9Sstevel@tonic-gate 	int	old_power, new_power, pwr;
6717c478bd9Sstevel@tonic-gate 	usb_port_t	portno;
6727c478bd9Sstevel@tonic-gate 	hub_power_t	*hubpm;
6737c478bd9Sstevel@tonic-gate 
6747c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
6757c478bd9Sstevel@tonic-gate 	    "hubd_set_child_pwrlvl: port=%d power=%d",
6767c478bd9Sstevel@tonic-gate 	    port, power);
6777c478bd9Sstevel@tonic-gate 
6787c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
6797c478bd9Sstevel@tonic-gate 	hubpm = hubd->h_hubpm;
6807c478bd9Sstevel@tonic-gate 
6817c478bd9Sstevel@tonic-gate 	old_power = 0;
682993e3fafSRobert Mustacchi 	for (portno = 1; portno <= hubd->h_nports; portno++) {
6837c478bd9Sstevel@tonic-gate 		old_power += hubpm->hubp_child_pwrstate[portno];
6847c478bd9Sstevel@tonic-gate 	}
6857c478bd9Sstevel@tonic-gate 
6867c478bd9Sstevel@tonic-gate 	/* assign the port power */
6877c478bd9Sstevel@tonic-gate 	pwr = hubd->h_hubpm->hubp_child_pwrstate[port];
6887c478bd9Sstevel@tonic-gate 	hubd->h_hubpm->hubp_child_pwrstate[port] = power;
6897c478bd9Sstevel@tonic-gate 	new_power = old_power - pwr + power;
6907c478bd9Sstevel@tonic-gate 
6917c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
6927c478bd9Sstevel@tonic-gate 	    "hubd_set_child_pwrlvl: new_power=%d old_power=%d",
6937c478bd9Sstevel@tonic-gate 	    new_power, old_power);
6947c478bd9Sstevel@tonic-gate 
6957c478bd9Sstevel@tonic-gate 	if ((new_power > 0) && (old_power == 0)) {
6967c478bd9Sstevel@tonic-gate 		/* we have the first child coming out of low power */
6977c478bd9Sstevel@tonic-gate 		(void) hubd_pm_busy_component(hubd, hubd->h_dip, 0);
6987c478bd9Sstevel@tonic-gate 	} else if ((new_power == 0) && (old_power > 0)) {
6997c478bd9Sstevel@tonic-gate 		/* we have the last child going to low power */
7007c478bd9Sstevel@tonic-gate 		(void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
7017c478bd9Sstevel@tonic-gate 	}
7027c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
7037c478bd9Sstevel@tonic-gate }
7047c478bd9Sstevel@tonic-gate 
7057c478bd9Sstevel@tonic-gate 
7067c478bd9Sstevel@tonic-gate /*
7077c478bd9Sstevel@tonic-gate  * given a child dip, locate its port number
7087c478bd9Sstevel@tonic-gate  */
7097c478bd9Sstevel@tonic-gate static usb_port_t
hubd_child_dip2port(hubd_t * hubd,dev_info_t * dip)7107c478bd9Sstevel@tonic-gate hubd_child_dip2port(hubd_t *hubd, dev_info_t *dip)
7117c478bd9Sstevel@tonic-gate {
7127c478bd9Sstevel@tonic-gate 	usb_port_t	port;
7137c478bd9Sstevel@tonic-gate 
7147c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
715993e3fafSRobert Mustacchi 	for (port = 1; port <= hubd->h_nports; port++) {
7167c478bd9Sstevel@tonic-gate 		if (hubd->h_children_dips[port] == dip) {
7177c478bd9Sstevel@tonic-gate 
7187c478bd9Sstevel@tonic-gate 			break;
7197c478bd9Sstevel@tonic-gate 		}
7207c478bd9Sstevel@tonic-gate 	}
721993e3fafSRobert Mustacchi 	ASSERT(port <= hubd->h_nports);
7227c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
7237c478bd9Sstevel@tonic-gate 
7247c478bd9Sstevel@tonic-gate 	return (port);
7257c478bd9Sstevel@tonic-gate }
7267c478bd9Sstevel@tonic-gate 
7277c478bd9Sstevel@tonic-gate 
7287c478bd9Sstevel@tonic-gate /*
7297c478bd9Sstevel@tonic-gate  * if the hub can be put into low power mode, return success
7307c478bd9Sstevel@tonic-gate  * NOTE: suspend here means going to lower power, not CPR suspend.
7317c478bd9Sstevel@tonic-gate  */
7327c478bd9Sstevel@tonic-gate static int
hubd_can_suspend(hubd_t * hubd)7337c478bd9Sstevel@tonic-gate hubd_can_suspend(hubd_t *hubd)
7347c478bd9Sstevel@tonic-gate {
7357c478bd9Sstevel@tonic-gate 	hub_power_t	*hubpm;
7367c478bd9Sstevel@tonic-gate 	int		total_power = 0;
7377c478bd9Sstevel@tonic-gate 	usb_port_t	port;
7387c478bd9Sstevel@tonic-gate 
7397c478bd9Sstevel@tonic-gate 	hubpm = hubd->h_hubpm;
7407c478bd9Sstevel@tonic-gate 
7417c478bd9Sstevel@tonic-gate 	if (DEVI_IS_DETACHING(hubd->h_dip)) {
7427c478bd9Sstevel@tonic-gate 
7437c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
7447c478bd9Sstevel@tonic-gate 	}
7457c478bd9Sstevel@tonic-gate 
7467c478bd9Sstevel@tonic-gate 	/*
7477c478bd9Sstevel@tonic-gate 	 * Don't go to lower power if haven't been at full power for enough
7487c478bd9Sstevel@tonic-gate 	 * time to let hotplug thread kickoff.
7497c478bd9Sstevel@tonic-gate 	 */
750e5815e7aSJosef 'Jeff' Sipek 	if (gethrtime() < (hubpm->hubp_time_at_full_power +
7517c478bd9Sstevel@tonic-gate 	    hubpm->hubp_min_pm_threshold)) {
7527c478bd9Sstevel@tonic-gate 
7537c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
7547c478bd9Sstevel@tonic-gate 	}
7557c478bd9Sstevel@tonic-gate 
7567c478bd9Sstevel@tonic-gate 	for (port = 1; (total_power == 0) &&
757993e3fafSRobert Mustacchi 	    (port <= hubd->h_nports); port++) {
7587c478bd9Sstevel@tonic-gate 		total_power += hubpm->hubp_child_pwrstate[port];
7597c478bd9Sstevel@tonic-gate 	}
7607c478bd9Sstevel@tonic-gate 
7617c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
762c0f24e5bSlg 	    "hubd_can_suspend: %d", total_power);
7637c478bd9Sstevel@tonic-gate 
7647c478bd9Sstevel@tonic-gate 	return (total_power ? USB_FAILURE : USB_SUCCESS);
7657c478bd9Sstevel@tonic-gate }
7667c478bd9Sstevel@tonic-gate 
7677c478bd9Sstevel@tonic-gate 
7687c478bd9Sstevel@tonic-gate /*
7697c478bd9Sstevel@tonic-gate  * resume port depending on current device state
7707c478bd9Sstevel@tonic-gate  */
7717c478bd9Sstevel@tonic-gate static int
hubd_resume_port(hubd_t * hubd,usb_port_t port)7727c478bd9Sstevel@tonic-gate hubd_resume_port(hubd_t *hubd, usb_port_t port)
7737c478bd9Sstevel@tonic-gate {
7747c478bd9Sstevel@tonic-gate 	int		rval, retry;
7757c478bd9Sstevel@tonic-gate 	usb_cr_t	completion_reason;
7767c478bd9Sstevel@tonic-gate 	usb_cb_flags_t	cb_flags;
7777c478bd9Sstevel@tonic-gate 	uint16_t	status;
7787c478bd9Sstevel@tonic-gate 	uint16_t	change;
7797c478bd9Sstevel@tonic-gate 	int		retval = USB_FAILURE;
7807c478bd9Sstevel@tonic-gate 
7817c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
7827c478bd9Sstevel@tonic-gate 
7837c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
7847c478bd9Sstevel@tonic-gate 	    "hubd_resume_port: port=%d state=0x%x (%s)", port,
7857c478bd9Sstevel@tonic-gate 	    hubd->h_dev_state, usb_str_dev_state(hubd->h_dev_state));
7867c478bd9Sstevel@tonic-gate 
7877c478bd9Sstevel@tonic-gate 	switch (hubd->h_dev_state) {
7887c478bd9Sstevel@tonic-gate 	case USB_DEV_HUB_CHILD_PWRLVL:
7897c478bd9Sstevel@tonic-gate 		/*
7907c478bd9Sstevel@tonic-gate 		 * This could be a bus ctl for a port other than the one
7917c478bd9Sstevel@tonic-gate 		 * that has a remote wakeup condition. So check.
7927c478bd9Sstevel@tonic-gate 		 */
7937c478bd9Sstevel@tonic-gate 		if ((hubd->h_port_state[port] & PORT_STATUS_PSS) == 0) {
7947c478bd9Sstevel@tonic-gate 			/* the port isn't suspended, so don't resume */
7957c478bd9Sstevel@tonic-gate 			retval = USB_SUCCESS;
7967c478bd9Sstevel@tonic-gate 
7977c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
7987c478bd9Sstevel@tonic-gate 			    "hubd_resume_port: port=%d not suspended", port);
7997c478bd9Sstevel@tonic-gate 
8007c478bd9Sstevel@tonic-gate 			break;
8017c478bd9Sstevel@tonic-gate 		}
8027c478bd9Sstevel@tonic-gate 		/*
8037c478bd9Sstevel@tonic-gate 		 * Device has initiated a wakeup.
8047c478bd9Sstevel@tonic-gate 		 * Issue a ClearFeature(PortSuspend)
8057c478bd9Sstevel@tonic-gate 		 */
8067c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
8077c478bd9Sstevel@tonic-gate 		if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
8087c478bd9Sstevel@tonic-gate 		    hubd->h_default_pipe,
80935f36846Ssl 		    HUB_HANDLE_PORT_FEATURE_TYPE,
8107c478bd9Sstevel@tonic-gate 		    USB_REQ_CLEAR_FEATURE,
8117c478bd9Sstevel@tonic-gate 		    CFS_PORT_SUSPEND,
8127c478bd9Sstevel@tonic-gate 		    port,
8137c478bd9Sstevel@tonic-gate 		    0, NULL, 0,
8147c478bd9Sstevel@tonic-gate 		    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
8157c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
8167c478bd9Sstevel@tonic-gate 			    "ClearFeature(PortSuspend) fails "
8177c478bd9Sstevel@tonic-gate 			    "rval=%d cr=%d cb=0x%x", rval,
8187c478bd9Sstevel@tonic-gate 			    completion_reason, cb_flags);
8197c478bd9Sstevel@tonic-gate 		}
8207c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
8217c478bd9Sstevel@tonic-gate 
8227c478bd9Sstevel@tonic-gate 		/* either way ack changes on the port */
8237c478bd9Sstevel@tonic-gate 		(void) hubd_determine_port_status(hubd, port,
824993e3fafSRobert Mustacchi 		    &status, &change, NULL, PORT_CHANGE_PSSC);
8257c478bd9Sstevel@tonic-gate 		retval = USB_SUCCESS;
8267c478bd9Sstevel@tonic-gate 
8277c478bd9Sstevel@tonic-gate 		break;
8287c478bd9Sstevel@tonic-gate 	case USB_DEV_HUB_STATE_RECOVER:
8297c478bd9Sstevel@tonic-gate 		/*
8307c478bd9Sstevel@tonic-gate 		 * When hubd's connect event callback posts a connect
8317c478bd9Sstevel@tonic-gate 		 * event to its child, it results in this busctl call
8327c478bd9Sstevel@tonic-gate 		 * which is valid
8337c478bd9Sstevel@tonic-gate 		 */
8347c478bd9Sstevel@tonic-gate 		/* FALLTHRU */
8357c478bd9Sstevel@tonic-gate 	case USB_DEV_ONLINE:
8366c7181fcSsl 		if (((hubd->h_port_state[port] & PORT_STATUS_CCS) == 0) ||
8376c7181fcSsl 		    ((hubd->h_port_state[port] & PORT_STATUS_PSS) == 0)) {
8387c478bd9Sstevel@tonic-gate 			/*
8397c478bd9Sstevel@tonic-gate 			 * the port isn't suspended, or connected
8407c478bd9Sstevel@tonic-gate 			 * so don't resume
8417c478bd9Sstevel@tonic-gate 			 */
8427c478bd9Sstevel@tonic-gate 			retval = USB_SUCCESS;
8437c478bd9Sstevel@tonic-gate 
8447c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
8457c478bd9Sstevel@tonic-gate 			    "hubd_resume_port: port=%d not suspended", port);
8467c478bd9Sstevel@tonic-gate 
8477c478bd9Sstevel@tonic-gate 			break;
8487c478bd9Sstevel@tonic-gate 		}
8497c478bd9Sstevel@tonic-gate 		/*
8507c478bd9Sstevel@tonic-gate 		 * prevent kicking off the hotplug thread
8517c478bd9Sstevel@tonic-gate 		 */
8527c478bd9Sstevel@tonic-gate 		hubd->h_hotplug_thread++;
8537c478bd9Sstevel@tonic-gate 		hubd_stop_polling(hubd);
8547c478bd9Sstevel@tonic-gate 
8557c478bd9Sstevel@tonic-gate 		/* Now ClearFeature(PortSuspend) */
8567c478bd9Sstevel@tonic-gate 		for (retry = 0; retry < HUBD_SUS_RES_RETRY; retry++) {
8577c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
8587c478bd9Sstevel@tonic-gate 			rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
8597c478bd9Sstevel@tonic-gate 			    hubd->h_default_pipe,
86035f36846Ssl 			    HUB_HANDLE_PORT_FEATURE_TYPE,
8617c478bd9Sstevel@tonic-gate 			    USB_REQ_CLEAR_FEATURE,
8627c478bd9Sstevel@tonic-gate 			    CFS_PORT_SUSPEND,
8637c478bd9Sstevel@tonic-gate 			    port,
8647c478bd9Sstevel@tonic-gate 			    0, NULL, 0,
8657c478bd9Sstevel@tonic-gate 			    &completion_reason, &cb_flags, 0);
8667c478bd9Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
8677c478bd9Sstevel@tonic-gate 			if (rval != USB_SUCCESS) {
8687c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_PM,
8697c478bd9Sstevel@tonic-gate 				    hubd->h_log_handle,
8707c478bd9Sstevel@tonic-gate 				    "ClearFeature(PortSuspend) fails"
8717c478bd9Sstevel@tonic-gate 				    "rval=%d cr=%d cb=0x%x", rval,
8727c478bd9Sstevel@tonic-gate 				    completion_reason, cb_flags);
8737c478bd9Sstevel@tonic-gate 			} else {
8747c478bd9Sstevel@tonic-gate 				/*
8757c478bd9Sstevel@tonic-gate 				 * As per spec section 11.9 and 7.1.7.7
8767c478bd9Sstevel@tonic-gate 				 * hub need to provide at least 20ms of
8777c478bd9Sstevel@tonic-gate 				 * resume signalling, and s/w provide 10ms of
8787c478bd9Sstevel@tonic-gate 				 * recovery time before accessing the port.
8797c478bd9Sstevel@tonic-gate 				 */
8807c478bd9Sstevel@tonic-gate 				mutex_exit(HUBD_MUTEX(hubd));
8817c478bd9Sstevel@tonic-gate 				delay(drv_usectohz(40000));
8827c478bd9Sstevel@tonic-gate 				mutex_enter(HUBD_MUTEX(hubd));
8837c478bd9Sstevel@tonic-gate 				(void) hubd_determine_port_status(hubd, port,
884993e3fafSRobert Mustacchi 				    &status, &change, NULL, PORT_CHANGE_PSSC);
8857c478bd9Sstevel@tonic-gate 
8867c478bd9Sstevel@tonic-gate 				if ((status & PORT_STATUS_PSS) == 0) {
8877c478bd9Sstevel@tonic-gate 					/* the port did finally resume */
8887c478bd9Sstevel@tonic-gate 					retval = USB_SUCCESS;
8897c478bd9Sstevel@tonic-gate 
8907c478bd9Sstevel@tonic-gate 					break;
8917c478bd9Sstevel@tonic-gate 				}
8927c478bd9Sstevel@tonic-gate 			}
8937c478bd9Sstevel@tonic-gate 		}
8947c478bd9Sstevel@tonic-gate 
8957c478bd9Sstevel@tonic-gate 		/* allow hotplug thread again */
8967c478bd9Sstevel@tonic-gate 		hubd->h_hotplug_thread--;
8977c478bd9Sstevel@tonic-gate 		hubd_start_polling(hubd, 0);
8987c478bd9Sstevel@tonic-gate 
8997c478bd9Sstevel@tonic-gate 		break;
9007c478bd9Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
9017c478bd9Sstevel@tonic-gate 		/* Ignore - NO Operation */
9027c478bd9Sstevel@tonic-gate 		retval = USB_SUCCESS;
9037c478bd9Sstevel@tonic-gate 
9047c478bd9Sstevel@tonic-gate 		break;
9057c478bd9Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
9067c478bd9Sstevel@tonic-gate 	case USB_DEV_PWRED_DOWN:
9077c478bd9Sstevel@tonic-gate 	default:
9087c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
9097c478bd9Sstevel@tonic-gate 		    "Improper state for port Resume");
9107c478bd9Sstevel@tonic-gate 
9117c478bd9Sstevel@tonic-gate 		break;
9127c478bd9Sstevel@tonic-gate 	}
9137c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
9147c478bd9Sstevel@tonic-gate 
9157c478bd9Sstevel@tonic-gate 	return (retval);
9167c478bd9Sstevel@tonic-gate }
9177c478bd9Sstevel@tonic-gate 
9187c478bd9Sstevel@tonic-gate 
9197c478bd9Sstevel@tonic-gate /*
9207c478bd9Sstevel@tonic-gate  * suspend port depending on device state
9217c478bd9Sstevel@tonic-gate  */
9227c478bd9Sstevel@tonic-gate static int
hubd_suspend_port(hubd_t * hubd,usb_port_t port)9237c478bd9Sstevel@tonic-gate hubd_suspend_port(hubd_t *hubd, usb_port_t port)
9247c478bd9Sstevel@tonic-gate {
9257c478bd9Sstevel@tonic-gate 	int		rval, retry;
9267c478bd9Sstevel@tonic-gate 	int		retval = USB_FAILURE;
9277c478bd9Sstevel@tonic-gate 	usb_cr_t	completion_reason;
9287c478bd9Sstevel@tonic-gate 	usb_cb_flags_t	cb_flags;
9297c478bd9Sstevel@tonic-gate 	uint16_t	status;
9307c478bd9Sstevel@tonic-gate 	uint16_t	change;
9317c478bd9Sstevel@tonic-gate 
9327c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
9337c478bd9Sstevel@tonic-gate 	    "hubd_suspend_port: port=%d", port);
9347c478bd9Sstevel@tonic-gate 
9357c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
9367c478bd9Sstevel@tonic-gate 
9377c478bd9Sstevel@tonic-gate 	switch (hubd->h_dev_state) {
9387c478bd9Sstevel@tonic-gate 	case USB_DEV_HUB_STATE_RECOVER:
9397c478bd9Sstevel@tonic-gate 		/*
9407c478bd9Sstevel@tonic-gate 		 * When hubd's connect event callback posts a connect
9417c478bd9Sstevel@tonic-gate 		 * event to its child, it results in this busctl call
9427c478bd9Sstevel@tonic-gate 		 * which is valid
9437c478bd9Sstevel@tonic-gate 		 */
9447c478bd9Sstevel@tonic-gate 		/* FALLTHRU */
9457c478bd9Sstevel@tonic-gate 	case USB_DEV_HUB_CHILD_PWRLVL:
9467c478bd9Sstevel@tonic-gate 		/*
9477c478bd9Sstevel@tonic-gate 		 * When one child is resuming, the other could timeout
9487c478bd9Sstevel@tonic-gate 		 * and go to low power mode, which is valid
9497c478bd9Sstevel@tonic-gate 		 */
9507c478bd9Sstevel@tonic-gate 		/* FALLTHRU */
9517c478bd9Sstevel@tonic-gate 	case USB_DEV_ONLINE:
9527c478bd9Sstevel@tonic-gate 		hubd->h_hotplug_thread++;
9537c478bd9Sstevel@tonic-gate 		hubd_stop_polling(hubd);
9547c478bd9Sstevel@tonic-gate 
9557c478bd9Sstevel@tonic-gate 		/*
9567c478bd9Sstevel@tonic-gate 		 * Some devices start an unprovoked resume.  According to spec,
9577c478bd9Sstevel@tonic-gate 		 * normal resume time for port is 10ms.  Wait for double that
9587c478bd9Sstevel@tonic-gate 		 * time, then check to be sure port is really suspended.
9597c478bd9Sstevel@tonic-gate 		 */
9607c478bd9Sstevel@tonic-gate 		for (retry = 0; retry < HUBD_SUS_RES_RETRY; retry++) {
9617c478bd9Sstevel@tonic-gate 			/* Now SetFeature(PortSuspend) */
9627c478bd9Sstevel@tonic-gate 			mutex_exit(HUBD_MUTEX(hubd));
9637c478bd9Sstevel@tonic-gate 			if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
9647c478bd9Sstevel@tonic-gate 			    hubd->h_default_pipe,
96535f36846Ssl 			    HUB_HANDLE_PORT_FEATURE_TYPE,
9667c478bd9Sstevel@tonic-gate 			    USB_REQ_SET_FEATURE,
9677c478bd9Sstevel@tonic-gate 			    CFS_PORT_SUSPEND,
9687c478bd9Sstevel@tonic-gate 			    port,
9697c478bd9Sstevel@tonic-gate 			    0, NULL, 0,
9707c478bd9Sstevel@tonic-gate 			    &completion_reason, &cb_flags, 0)) !=
9717c478bd9Sstevel@tonic-gate 			    USB_SUCCESS) {
9727c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_PM,
9737c478bd9Sstevel@tonic-gate 				    hubd->h_log_handle,
9747c478bd9Sstevel@tonic-gate 				    "SetFeature(PortSuspend) fails"
9757c478bd9Sstevel@tonic-gate 				    "rval=%d cr=%d cb=0x%x",
9767c478bd9Sstevel@tonic-gate 				    rval, completion_reason, cb_flags);
9777c478bd9Sstevel@tonic-gate 			}
9787c478bd9Sstevel@tonic-gate 
9797c478bd9Sstevel@tonic-gate 			/*
9807c478bd9Sstevel@tonic-gate 			 * some devices start an unprovoked resume
9817c478bd9Sstevel@tonic-gate 			 * wait and check port status after some time
9827c478bd9Sstevel@tonic-gate 			 */
9837c478bd9Sstevel@tonic-gate 			delay(drv_usectohz(20000));
9847c478bd9Sstevel@tonic-gate 
9857c478bd9Sstevel@tonic-gate 			/* either ways ack changes on the port */
9867c478bd9Sstevel@tonic-gate 			mutex_enter(HUBD_MUTEX(hubd));
9877c478bd9Sstevel@tonic-gate 			(void) hubd_determine_port_status(hubd, port,
988993e3fafSRobert Mustacchi 			    &status, &change, NULL, PORT_CHANGE_PSSC);
9897c478bd9Sstevel@tonic-gate 			if (status & PORT_STATUS_PSS) {
9907c478bd9Sstevel@tonic-gate 				/* the port is indeed suspended */
9917c478bd9Sstevel@tonic-gate 				retval = USB_SUCCESS;
9927c478bd9Sstevel@tonic-gate 
9937c478bd9Sstevel@tonic-gate 				break;
9946f6c7d2bSVincent Wang 			} else {
9956f6c7d2bSVincent Wang 				USB_DPRINTF_L0(DPRINT_MASK_PM,
9966f6c7d2bSVincent Wang 				    hubd->h_log_handle,
9976f6c7d2bSVincent Wang 				    "hubdi: port%d failed to be suspended!",
9986f6c7d2bSVincent Wang 				    port);
9997c478bd9Sstevel@tonic-gate 			}
10007c478bd9Sstevel@tonic-gate 		}
10017c478bd9Sstevel@tonic-gate 
10027c478bd9Sstevel@tonic-gate 		hubd->h_hotplug_thread--;
10037c478bd9Sstevel@tonic-gate 		hubd_start_polling(hubd, 0);
10047c478bd9Sstevel@tonic-gate 
10057c478bd9Sstevel@tonic-gate 		break;
10067c478bd9Sstevel@tonic-gate 
10077c478bd9Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
10087c478bd9Sstevel@tonic-gate 		/* Ignore - No Operation */
10097c478bd9Sstevel@tonic-gate 		retval = USB_SUCCESS;
10107c478bd9Sstevel@tonic-gate 
10117c478bd9Sstevel@tonic-gate 		break;
10127c478bd9Sstevel@tonic-gate 
10137c478bd9Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
10147c478bd9Sstevel@tonic-gate 	case USB_DEV_PWRED_DOWN:
10157c478bd9Sstevel@tonic-gate 	default:
10167c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
10177c478bd9Sstevel@tonic-gate 		    "Improper state for port Suspend");
10187c478bd9Sstevel@tonic-gate 
10197c478bd9Sstevel@tonic-gate 		break;
10207c478bd9Sstevel@tonic-gate 	}
10217c478bd9Sstevel@tonic-gate 	mutex_exit(HUBD_MUTEX(hubd));
10227c478bd9Sstevel@tonic-gate 
10237c478bd9Sstevel@tonic-gate 	return (retval);
10247c478bd9Sstevel@tonic-gate }
10257c478bd9Sstevel@tonic-gate 
10267c478bd9Sstevel@tonic-gate 
10277c478bd9Sstevel@tonic-gate /*
10287c478bd9Sstevel@tonic-gate  * child post attach/detach notifications
10297c478bd9Sstevel@tonic-gate  */
10307c478bd9Sstevel@tonic-gate static void
hubd_post_attach(hubd_t * hubd,usb_port_t port,struct attachspec * as)10317c478bd9Sstevel@tonic-gate hubd_post_attach(hubd_t *hubd, usb_port_t port, struct attachspec *as)
10327c478bd9Sstevel@tonic-gate {
10337c478bd9Sstevel@tonic-gate 	dev_info_t	*dip;
10347c478bd9Sstevel@tonic-gate 
10357c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
10367c478bd9Sstevel@tonic-gate 	    "hubd_post_attach: port=%d result=%d",
10377c478bd9Sstevel@tonic-gate 	    port, as->result);
10387c478bd9Sstevel@tonic-gate 
10397c478bd9Sstevel@tonic-gate 	if (as->result == DDI_SUCCESS) {
10407c478bd9Sstevel@tonic-gate 		/*
10417c478bd9Sstevel@tonic-gate 		 * Check if the child created wants to be power managed.
10427c478bd9Sstevel@tonic-gate 		 * If yes, the childs power level gets automatically tracked
10437c478bd9Sstevel@tonic-gate 		 * by DDI_CTLOPS_POWER busctl.
10447c478bd9Sstevel@tonic-gate 		 * If no, we set power of the new child by default
10457c478bd9Sstevel@tonic-gate 		 * to USB_DEV_OS_FULL_PWR. Because we should never suspend.
10467c478bd9Sstevel@tonic-gate 		 */
10477c478bd9Sstevel@tonic-gate 		mutex_enter(HUBD_MUTEX(hubd));
10487c478bd9Sstevel@tonic-gate 		dip = hubd->h_children_dips[port];
10497c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
10507c478bd9Sstevel@tonic-gate 		if (DEVI(dip)->devi_pm_info == NULL) {
10517c478bd9Sstevel@tonic-gate 			hubd_set_child_pwrlvl(hubd, port, USB_DEV_OS_FULL_PWR);
10527c478bd9Sstevel@tonic-gate 		}
10537c478bd9Sstevel@tonic-gate 	}
10547c478bd9Sstevel@tonic-gate }
10557c478bd9Sstevel@tonic-gate 
10567c478bd9Sstevel@tonic-gate 
10577c478bd9Sstevel@tonic-gate static void
hubd_post_detach(hubd_t * hubd,usb_port_t port,struct detachspec * ds)10587c478bd9Sstevel@tonic-gate hubd_post_detach(hubd_t *hubd, usb_port_t port, struct detachspec *ds)
10597c478bd9Sstevel@tonic-gate {
10607c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
10617c478bd9Sstevel@tonic-gate 	    "hubd_post_detach: port=%d result=%d", port, ds->result);
10627c478bd9Sstevel@tonic-gate 
10637c478bd9Sstevel@tonic-gate 	/*
10647c478bd9Sstevel@tonic-gate 	 * if the device is successfully detached and is the
10657c478bd9Sstevel@tonic-gate 	 * last device to detach, mark component as idle
10667c478bd9Sstevel@tonic-gate 	 */
10677c478bd9Sstevel@tonic-gate 	mutex_enter(HUBD_MUTEX(hubd));
10687c478bd9Sstevel@tonic-gate 	if (ds->result == DDI_SUCCESS) {
10697c478bd9Sstevel@tonic-gate 		usba_device_t	*usba_device = hubd->h_usba_devices[port];
107035f36846Ssl 		dev_info_t	*pdip = hubd->h_dip;
10717c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
10727c478bd9Sstevel@tonic-gate 
107335f36846Ssl 		usba_hubdi_incr_power_budget(pdip, usba_device);
107435f36846Ssl 
10757c478bd9Sstevel@tonic-gate 		/*
10767c478bd9Sstevel@tonic-gate 		 * We set power of the detached child
10777c478bd9Sstevel@tonic-gate 		 * to 0, so that we can suspend if all
10787c478bd9Sstevel@tonic-gate 		 * our children are gone
10797c478bd9Sstevel@tonic-gate 		 */
10807c478bd9Sstevel@tonic-gate 		hubd_set_child_pwrlvl(hubd, port, USB_DEV_OS_PWR_OFF);
10817c478bd9Sstevel@tonic-gate 
10827c478bd9Sstevel@tonic-gate 		/* check for leaks on detaching */
10837c478bd9Sstevel@tonic-gate 		if ((usba_device) && (ds->cmd == DDI_DETACH)) {
10847c478bd9Sstevel@tonic-gate 			usba_check_for_leaks(usba_device);
10857c478bd9Sstevel@tonic-gate 		}
10867c478bd9Sstevel@tonic-gate 	} else {
10877c478bd9Sstevel@tonic-gate 		mutex_exit(HUBD_MUTEX(hubd));
10887c478bd9Sstevel@tonic-gate 	}
10897c478bd9Sstevel@tonic-gate }
10907c478bd9Sstevel@tonic-gate 
10917c478bd9Sstevel@tonic-gate 
1092</