xref: /illumos-gate/usr/src/uts/common/io/usb/usba/usba.c (revision 0d2006e4)
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
5cbab2b26Slg  * Common Development and Distribution License (the "License").
6cbab2b26Slg  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22de6f998eSrui wang - Sun Microsystems - Beijing China  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
24e2c88f0cSGarrett D'Amore  *
25e2c88f0cSGarrett D'Amore  * Copyright 2014 Garrett D'Amore <garrett@damore.org>
265c9564e1SJames Blachly  * Copyright 2016 James S. Blachly, MD <james.blachly@gmail.com>
27*0d2006e4SRobert Mustacchi  * Copyright 2019 Joyent, Inc.
287c478bd9Sstevel@tonic-gate  */
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate 
317c478bd9Sstevel@tonic-gate /*
327c478bd9Sstevel@tonic-gate  * USBA: Solaris USB Architecture support
337c478bd9Sstevel@tonic-gate  */
347c478bd9Sstevel@tonic-gate #define	USBA_FRAMEWORK
357c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usba_impl.h>
367c478bd9Sstevel@tonic-gate #include <sys/usb/usba/hcdi_impl.h>
377c478bd9Sstevel@tonic-gate #include <sys/usb/hubd/hub.h>
387c478bd9Sstevel@tonic-gate #include <sys/fs/dv_node.h>
397c478bd9Sstevel@tonic-gate 
407c478bd9Sstevel@tonic-gate /*
417c478bd9Sstevel@tonic-gate  * USBA private variables and tunables
427c478bd9Sstevel@tonic-gate  */
437c478bd9Sstevel@tonic-gate static kmutex_t	usba_mutex;
447c478bd9Sstevel@tonic-gate 
45de6f998eSrui wang - Sun Microsystems - Beijing China /* mutex to protect usba_root_hubs */
46de6f998eSrui wang - Sun Microsystems - Beijing China static kmutex_t usba_hub_mutex;
47de6f998eSrui wang - Sun Microsystems - Beijing China 
48de6f998eSrui wang - Sun Microsystems - Beijing China typedef struct usba_root_hub_ent {
49de6f998eSrui wang - Sun Microsystems - Beijing China 	dev_info_t *dip;
50de6f998eSrui wang - Sun Microsystems - Beijing China 	struct usba_root_hub_ent *next;
51de6f998eSrui wang - Sun Microsystems - Beijing China }usba_root_hub_ent_t;
52de6f998eSrui wang - Sun Microsystems - Beijing China 
53de6f998eSrui wang - Sun Microsystems - Beijing China static usba_root_hub_ent_t *usba_root_hubs = NULL;
54de6f998eSrui wang - Sun Microsystems - Beijing China 
557c478bd9Sstevel@tonic-gate /*
567c478bd9Sstevel@tonic-gate  * ddivs forced binding:
577c478bd9Sstevel@tonic-gate  *
587c478bd9Sstevel@tonic-gate  *    usbc usbc_xhubs usbc_xaddress  node name
597c478bd9Sstevel@tonic-gate  *
607c478bd9Sstevel@tonic-gate  *	0	x	x	class name or "device"
617c478bd9Sstevel@tonic-gate  *
627c478bd9Sstevel@tonic-gate  *	1	0	0	ddivs_usbc
637c478bd9Sstevel@tonic-gate  *	1	0	>1	ddivs_usbc except device
647c478bd9Sstevel@tonic-gate  *				at usbc_xaddress
657c478bd9Sstevel@tonic-gate  *	1	1	0	ddivs_usbc except hubs
667c478bd9Sstevel@tonic-gate  *	1	1	>1	ddivs_usbc except hubs and
677c478bd9Sstevel@tonic-gate  *				device at usbc_xaddress
687c478bd9Sstevel@tonic-gate  */
697c478bd9Sstevel@tonic-gate uint_t usba_ddivs_usbc;
707c478bd9Sstevel@tonic-gate uint_t usba_ddivs_usbc_xhubs;
717c478bd9Sstevel@tonic-gate uint_t usba_ddivs_usbc_xaddress;
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate uint_t usba_ugen_force_binding;
747c478bd9Sstevel@tonic-gate 
757c478bd9Sstevel@tonic-gate /*
767c478bd9Sstevel@tonic-gate  * compatible name handling
777c478bd9Sstevel@tonic-gate  */
78a7df97baSStrony Zhang - Solaris China Team /*
79a7df97baSStrony Zhang - Solaris China Team  * allowing for 15 compat names, plus one force bind name and
80a7df97baSStrony Zhang - Solaris China Team  * one possible specified client driver name
81a7df97baSStrony Zhang - Solaris China Team  */
82a7df97baSStrony Zhang - Solaris China Team #define	USBA_MAX_COMPAT_NAMES		17
837c478bd9Sstevel@tonic-gate #define	USBA_MAX_COMPAT_NAME_LEN	64
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate /* double linked list for usba_devices */
867c478bd9Sstevel@tonic-gate usba_list_entry_t	usba_device_list;
877c478bd9Sstevel@tonic-gate 
887c478bd9Sstevel@tonic-gate _NOTE(MUTEX_PROTECTS_DATA(usba_mutex, usba_device_list))
897c478bd9Sstevel@tonic-gate 
907c478bd9Sstevel@tonic-gate /*
917c478bd9Sstevel@tonic-gate  * modload support
927c478bd9Sstevel@tonic-gate  */
937c478bd9Sstevel@tonic-gate 
94e8ed0869SJohn Beck static struct modlmisc modlmisc	= {
957c478bd9Sstevel@tonic-gate 	&mod_miscops,	/* Type	of module */
96d73ae94eSgc 	"USBA: USB Architecture 2.0 1.66"
977c478bd9Sstevel@tonic-gate };
987c478bd9Sstevel@tonic-gate 
99e8ed0869SJohn Beck static struct modlinkage modlinkage = {
1007c478bd9Sstevel@tonic-gate 	MODREV_1, (void	*)&modlmisc, NULL
1017c478bd9Sstevel@tonic-gate };
1027c478bd9Sstevel@tonic-gate 
1037c478bd9Sstevel@tonic-gate 
1047c478bd9Sstevel@tonic-gate static usb_log_handle_t	usba_log_handle;
1054610e4a0Sfrits uint_t		usba_errlevel = USB_LOG_L4;
1064610e4a0Sfrits uint_t		usba_errmask = (uint_t)-1;
1077c478bd9Sstevel@tonic-gate 
1087c478bd9Sstevel@tonic-gate extern usb_log_handle_t	hubdi_log_handle;
1097c478bd9Sstevel@tonic-gate 
1107c478bd9Sstevel@tonic-gate int
1117c478bd9Sstevel@tonic-gate _init(void)
1127c478bd9Sstevel@tonic-gate {
1133c91d182Slg 	int rval;
1147c478bd9Sstevel@tonic-gate 
1157c478bd9Sstevel@tonic-gate 	/*
1167c478bd9Sstevel@tonic-gate 	 * usbai providing log support needs to be init'ed first
1177c478bd9Sstevel@tonic-gate 	 * and destroyed last
1187c478bd9Sstevel@tonic-gate 	 */
1197c478bd9Sstevel@tonic-gate 	usba_usbai_initialization();
1207c478bd9Sstevel@tonic-gate 	usba_usba_initialization();
1217c478bd9Sstevel@tonic-gate 	usba_usbai_register_initialization();
1227c478bd9Sstevel@tonic-gate 	usba_hcdi_initialization();
1237c478bd9Sstevel@tonic-gate 	usba_hubdi_initialization();
1247c478bd9Sstevel@tonic-gate 	usba_devdb_initialization();
1257c478bd9Sstevel@tonic-gate 
1267c478bd9Sstevel@tonic-gate 	if ((rval = mod_install(&modlinkage)) != 0) {
1277c478bd9Sstevel@tonic-gate 		usba_devdb_destroy();
1287c478bd9Sstevel@tonic-gate 		usba_hubdi_destroy();
1297c478bd9Sstevel@tonic-gate 		usba_hcdi_destroy();
1307c478bd9Sstevel@tonic-gate 		usba_usbai_register_destroy();
1317c478bd9Sstevel@tonic-gate 		usba_usba_destroy();
1327c478bd9Sstevel@tonic-gate 		usba_usbai_destroy();
1337c478bd9Sstevel@tonic-gate 	}
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate 	return (rval);
1367c478bd9Sstevel@tonic-gate }
1377c478bd9Sstevel@tonic-gate 
1387c478bd9Sstevel@tonic-gate int
1397c478bd9Sstevel@tonic-gate _fini()
1407c478bd9Sstevel@tonic-gate {
1417c478bd9Sstevel@tonic-gate 	int rval;
1427c478bd9Sstevel@tonic-gate 
1437c478bd9Sstevel@tonic-gate 	if ((rval = mod_remove(&modlinkage)) == 0) {
1447c478bd9Sstevel@tonic-gate 		usba_devdb_destroy();
1457c478bd9Sstevel@tonic-gate 		usba_hubdi_destroy();
1467c478bd9Sstevel@tonic-gate 		usba_hcdi_destroy();
1477c478bd9Sstevel@tonic-gate 		usba_usbai_register_destroy();
1487c478bd9Sstevel@tonic-gate 		usba_usba_destroy();
1497c478bd9Sstevel@tonic-gate 		usba_usbai_destroy();
1507c478bd9Sstevel@tonic-gate 	}
1517c478bd9Sstevel@tonic-gate 
1527c478bd9Sstevel@tonic-gate 	return (rval);
1537c478bd9Sstevel@tonic-gate }
1547c478bd9Sstevel@tonic-gate 
1557c478bd9Sstevel@tonic-gate int
1567c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
1577c478bd9Sstevel@tonic-gate {
1587c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
1597c478bd9Sstevel@tonic-gate }
1607c478bd9Sstevel@tonic-gate 
161d73ae94eSgc boolean_t
162d73ae94eSgc usba_owns_ia(dev_info_t *dip)
163d73ae94eSgc {
164d73ae94eSgc 	int if_count = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
165d73ae94eSgc 	    "interface-count", 0);
166d73ae94eSgc 
167d73ae94eSgc 	return ((if_count) ? B_TRUE : B_FALSE);
168d73ae94eSgc }
1697c478bd9Sstevel@tonic-gate 
1707c478bd9Sstevel@tonic-gate /*
1717c478bd9Sstevel@tonic-gate  * common bus ctl for hcd, usb_mid, and hubd
1727c478bd9Sstevel@tonic-gate  */
1737c478bd9Sstevel@tonic-gate int
1747c478bd9Sstevel@tonic-gate usba_bus_ctl(dev_info_t	*dip,
1757c478bd9Sstevel@tonic-gate 	dev_info_t		*rdip,
1767c478bd9Sstevel@tonic-gate 	ddi_ctl_enum_t		op,
1777c478bd9Sstevel@tonic-gate 	void			*arg,
1787c478bd9Sstevel@tonic-gate 	void			*result)
1797c478bd9Sstevel@tonic-gate {
1807c478bd9Sstevel@tonic-gate 	dev_info_t		*child_dip = (dev_info_t *)arg;
1817c478bd9Sstevel@tonic-gate 	usba_device_t		*usba_device;
1827c478bd9Sstevel@tonic-gate 	usba_hcdi_t		*usba_hcdi;
1837c478bd9Sstevel@tonic-gate 	usba_hcdi_ops_t		*usba_hcdi_ops;
1847c478bd9Sstevel@tonic-gate 
1857c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_USBA, hubdi_log_handle,
1867c478bd9Sstevel@tonic-gate 	    "usba_bus_ctl: %s%d %s%d op=%d", ddi_node_name(rdip),
1877c478bd9Sstevel@tonic-gate 	    ddi_get_instance(rdip), ddi_node_name(dip),
1887c478bd9Sstevel@tonic-gate 	    ddi_get_instance(dip), op);
1897c478bd9Sstevel@tonic-gate 
1907c478bd9Sstevel@tonic-gate 	switch (op) {
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_REPORTDEV:
1937c478bd9Sstevel@tonic-gate 	{
1947c478bd9Sstevel@tonic-gate 		char *name, compat_name[64], *speed;
1957c478bd9Sstevel@tonic-gate 		usba_device_t	*hub_usba_device;
1967c478bd9Sstevel@tonic-gate 		dev_info_t	*hubdip;
1977c478bd9Sstevel@tonic-gate 
1987c478bd9Sstevel@tonic-gate 		usba_device = usba_get_usba_device(rdip);
1997c478bd9Sstevel@tonic-gate 
2007c478bd9Sstevel@tonic-gate 		/* find the parent hub */
2017c478bd9Sstevel@tonic-gate 		hubdip = ddi_get_parent(rdip);
2027c478bd9Sstevel@tonic-gate 		while ((strcmp(ddi_driver_name(hubdip), "hubd") != 0) &&
2037c478bd9Sstevel@tonic-gate 		    !(usba_is_root_hub(hubdip))) {
2047c478bd9Sstevel@tonic-gate 			hubdip = ddi_get_parent(hubdip);
2057c478bd9Sstevel@tonic-gate 		}
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate 		hub_usba_device = usba_get_usba_device(hubdip);
2087c478bd9Sstevel@tonic-gate 
2097c478bd9Sstevel@tonic-gate 		if (usba_device) {
2107c478bd9Sstevel@tonic-gate 			if (usb_owns_device(rdip)) {
2117c478bd9Sstevel@tonic-gate 				(void) snprintf(compat_name,
2127c478bd9Sstevel@tonic-gate 				    sizeof (compat_name),
2137c478bd9Sstevel@tonic-gate 				    "usb%x,%x",
2147c478bd9Sstevel@tonic-gate 				    usba_device->usb_dev_descr->idVendor,
2157c478bd9Sstevel@tonic-gate 				    usba_device->usb_dev_descr->idProduct);
216d73ae94eSgc 			} else if (usba_owns_ia(rdip)) {
217d73ae94eSgc 				(void) snprintf(compat_name,
218d73ae94eSgc 				    sizeof (compat_name),
219d73ae94eSgc 				    "usbia%x,%x.config%x.%x",
220d73ae94eSgc 				    usba_device->usb_dev_descr->idVendor,
221d73ae94eSgc 				    usba_device->usb_dev_descr->idProduct,
222d73ae94eSgc 				    usba_device->usb_cfg_value,
223d73ae94eSgc 				    usb_get_if_number(rdip));
2247c478bd9Sstevel@tonic-gate 			} else {
2257c478bd9Sstevel@tonic-gate 				(void) snprintf(compat_name,
2267c478bd9Sstevel@tonic-gate 				    sizeof (compat_name),
2277c478bd9Sstevel@tonic-gate 				    "usbif%x,%x.config%x.%x",
2287c478bd9Sstevel@tonic-gate 				    usba_device->usb_dev_descr->idVendor,
2297c478bd9Sstevel@tonic-gate 				    usba_device->usb_dev_descr->idProduct,
2307c478bd9Sstevel@tonic-gate 				    usba_device->usb_cfg_value,
2317c478bd9Sstevel@tonic-gate 				    usb_get_if_number(rdip));
2327c478bd9Sstevel@tonic-gate 			}
2337c478bd9Sstevel@tonic-gate 			switch (usba_device->usb_port_status) {
234993e3fafSRobert Mustacchi 			case USBA_SUPER_SPEED_DEV:
235993e3fafSRobert Mustacchi 				speed = "super speed (USB 3.x)";
236993e3fafSRobert Mustacchi 				break;
2377c478bd9Sstevel@tonic-gate 			case USBA_HIGH_SPEED_DEV:
2387c478bd9Sstevel@tonic-gate 				speed = "hi speed (USB 2.x)";
2397c478bd9Sstevel@tonic-gate 				break;
2407c478bd9Sstevel@tonic-gate 			case USBA_LOW_SPEED_DEV:
2417c478bd9Sstevel@tonic-gate 				speed = "low speed (USB 1.x)";
2427c478bd9Sstevel@tonic-gate 				break;
2437c478bd9Sstevel@tonic-gate 			case USBA_FULL_SPEED_DEV:
2447c478bd9Sstevel@tonic-gate 			default:
2457c478bd9Sstevel@tonic-gate 				speed = "full speed (USB 1.x)";
2467c478bd9Sstevel@tonic-gate 				break;
2477c478bd9Sstevel@tonic-gate 			}
2487c478bd9Sstevel@tonic-gate 
2497c478bd9Sstevel@tonic-gate 			cmn_err(CE_CONT,
2507c478bd9Sstevel@tonic-gate 			    "?USB %x.%x %s (%s) operating at %s on "
2517c478bd9Sstevel@tonic-gate 			    "USB %x.%x %s hub: "
2527c478bd9Sstevel@tonic-gate 			    "%s@%s, %s%d at bus address %d\n",
2537c478bd9Sstevel@tonic-gate 			    (usba_device->usb_dev_descr->bcdUSB & 0xff00) >> 8,
2547c478bd9Sstevel@tonic-gate 			    usba_device->usb_dev_descr->bcdUSB & 0xff,
255d73ae94eSgc 			    (usb_owns_device(rdip) ? "device" :
256d73ae94eSgc 			    ((usba_owns_ia(rdip) ? "interface-association" :
257d73ae94eSgc 			    "interface"))),
2587c478bd9Sstevel@tonic-gate 			    compat_name, speed,
2597c478bd9Sstevel@tonic-gate 			    (hub_usba_device->usb_dev_descr->bcdUSB &
2607c478bd9Sstevel@tonic-gate 			    0xff00) >> 8,
2617c478bd9Sstevel@tonic-gate 			    hub_usba_device->usb_dev_descr->bcdUSB & 0xff,
2627c478bd9Sstevel@tonic-gate 			    usba_is_root_hub(hubdip) ? "root" : "external",
2637c478bd9Sstevel@tonic-gate 			    ddi_node_name(rdip), ddi_get_name_addr(rdip),
2647c478bd9Sstevel@tonic-gate 			    ddi_driver_name(rdip),
2657c478bd9Sstevel@tonic-gate 			    ddi_get_instance(rdip), usba_device->usb_addr);
2667c478bd9Sstevel@tonic-gate 
2677c478bd9Sstevel@tonic-gate 			name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
2687c478bd9Sstevel@tonic-gate 			(void) usba_get_mfg_prod_sn_str(rdip, name, MAXNAMELEN);
2697c478bd9Sstevel@tonic-gate 			if (name[0] != '\0') {
2705c9564e1SJames Blachly 				cmn_err(CE_CONT, "?%s\n", name);
2717c478bd9Sstevel@tonic-gate 			}
2727c478bd9Sstevel@tonic-gate 			kmem_free(name, MAXNAMELEN);
2737c478bd9Sstevel@tonic-gate 
2747c478bd9Sstevel@tonic-gate 		} else { /* harden USBA against this case; if it happens */
2757c478bd9Sstevel@tonic-gate 
2767c478bd9Sstevel@tonic-gate 			cmn_err(CE_CONT,
2777c478bd9Sstevel@tonic-gate 			    "?USB-device: %s@%s, %s%d\n",
2787c478bd9Sstevel@tonic-gate 			    ddi_node_name(rdip), ddi_get_name_addr(rdip),
2797c478bd9Sstevel@tonic-gate 			    ddi_driver_name(rdip), ddi_get_instance(rdip));
2807c478bd9Sstevel@tonic-gate 		}
2817c478bd9Sstevel@tonic-gate 
2827c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
2837c478bd9Sstevel@tonic-gate 	}
2847c478bd9Sstevel@tonic-gate 
2857c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_INITCHILD:
2867c478bd9Sstevel@tonic-gate 	{
2877c478bd9Sstevel@tonic-gate 		int			usb_addr;
2887c478bd9Sstevel@tonic-gate 		uint_t			n;
2897c478bd9Sstevel@tonic-gate 		char			name[32];
2907c478bd9Sstevel@tonic-gate 		int			*data;
2917c478bd9Sstevel@tonic-gate 		int			rval;
2927c478bd9Sstevel@tonic-gate 		int			len = sizeof (usb_addr);
2937c478bd9Sstevel@tonic-gate 
2947c478bd9Sstevel@tonic-gate 		usba_hcdi	= usba_hcdi_get_hcdi(dip);
2957c478bd9Sstevel@tonic-gate 		usba_hcdi_ops	= usba_hcdi->hcdi_ops;
2967c478bd9Sstevel@tonic-gate 		ASSERT(usba_hcdi_ops != NULL);
2977c478bd9Sstevel@tonic-gate 
2987c478bd9Sstevel@tonic-gate 		/*
2997c478bd9Sstevel@tonic-gate 		 * as long as the dip exists, it should have
3007c478bd9Sstevel@tonic-gate 		 * usba_device structure associated with it
3017c478bd9Sstevel@tonic-gate 		 */
3027c478bd9Sstevel@tonic-gate 		usba_device = usba_get_usba_device(child_dip);
3037c478bd9Sstevel@tonic-gate 		if (usba_device == NULL) {
3047c478bd9Sstevel@tonic-gate 
3057c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_USBA, hubdi_log_handle,
3067c478bd9Sstevel@tonic-gate 			    "usba_bus_ctl: DDI_NOT_WELL_FORMED (%s (0x%p))",
3077c478bd9Sstevel@tonic-gate 			    ddi_node_name(child_dip), (void *)child_dip);
3087c478bd9Sstevel@tonic-gate 
3097c478bd9Sstevel@tonic-gate 			return (DDI_NOT_WELL_FORMED);
3107c478bd9Sstevel@tonic-gate 		}
3117c478bd9Sstevel@tonic-gate 
3127c478bd9Sstevel@tonic-gate 		/* the dip should have an address and reg property */
3137c478bd9Sstevel@tonic-gate 		if (ddi_prop_op(DDI_DEV_T_NONE, child_dip, PROP_LEN_AND_VAL_BUF,
3147c478bd9Sstevel@tonic-gate 		    DDI_PROP_DONTPASS |	DDI_PROP_CANSLEEP, "assigned-address",
3157c478bd9Sstevel@tonic-gate 		    (caddr_t)&usb_addr,	&len) != DDI_SUCCESS) {
3167c478bd9Sstevel@tonic-gate 
317d291d9f2Sfrits 			USB_DPRINTF_L2(DPRINT_MASK_USBA, hubdi_log_handle,
3187c478bd9Sstevel@tonic-gate 			    "usba_bus_ctl:\n\t"
3197c478bd9Sstevel@tonic-gate 			    "%s%d %s%d op=%d rdip = 0x%p dip = 0x%p",
3207c478bd9Sstevel@tonic-gate 			    ddi_node_name(rdip), ddi_get_instance(rdip),
3217c478bd9Sstevel@tonic-gate 			    ddi_node_name(dip), ddi_get_instance(dip), op,
322112116d8Sfb 			    (void *)rdip, (void *)dip);
3237c478bd9Sstevel@tonic-gate 
324d291d9f2Sfrits 			USB_DPRINTF_L2(DPRINT_MASK_USBA, hubdi_log_handle,
3257c478bd9Sstevel@tonic-gate 			    "usba_bus_ctl: DDI_NOT_WELL_FORMED (%s (0x%p))",
3267c478bd9Sstevel@tonic-gate 			    ddi_node_name(child_dip), (void *)child_dip);
3277c478bd9Sstevel@tonic-gate 
3287c478bd9Sstevel@tonic-gate 			return (DDI_NOT_WELL_FORMED);
3297c478bd9Sstevel@tonic-gate 		}
3307c478bd9Sstevel@tonic-gate 
3317c478bd9Sstevel@tonic-gate 		if ((rval = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child_dip,
3327c478bd9Sstevel@tonic-gate 		    DDI_PROP_DONTPASS, "reg",
3337c478bd9Sstevel@tonic-gate 		    &data, &n)) != DDI_SUCCESS) {
3347c478bd9Sstevel@tonic-gate 
335d291d9f2Sfrits 			USB_DPRINTF_L2(DPRINT_MASK_USBA, hubdi_log_handle,
3367c478bd9Sstevel@tonic-gate 			    "usba_bus_ctl: %d, DDI_NOT_WELL_FORMED", rval);
3377c478bd9Sstevel@tonic-gate 
3387c478bd9Sstevel@tonic-gate 			return (DDI_NOT_WELL_FORMED);
3397c478bd9Sstevel@tonic-gate 		}
3407c478bd9Sstevel@tonic-gate 
3417c478bd9Sstevel@tonic-gate 
3427c478bd9Sstevel@tonic-gate 		/*
3437c478bd9Sstevel@tonic-gate 		 * if the configuration is 1, the unit address is
3447c478bd9Sstevel@tonic-gate 		 * just the interface number
3457c478bd9Sstevel@tonic-gate 		 */
3467c478bd9Sstevel@tonic-gate 		if ((n == 1) || ((n > 1) && (data[1] == 1))) {
3477c478bd9Sstevel@tonic-gate 			(void) sprintf(name, "%x", data[0]);
3487c478bd9Sstevel@tonic-gate 		} else {
3497c478bd9Sstevel@tonic-gate 			(void) sprintf(name, "%x,%x", data[0], data[1]);
3507c478bd9Sstevel@tonic-gate 		}
3517c478bd9Sstevel@tonic-gate 
3527c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_USBA,
3537c478bd9Sstevel@tonic-gate 		    hubdi_log_handle, "usba_bus_ctl: name = %s", name);
3547c478bd9Sstevel@tonic-gate 
3557c478bd9Sstevel@tonic-gate 		ddi_prop_free(data);
3567c478bd9Sstevel@tonic-gate 		ddi_set_name_addr(child_dip, name);
3577c478bd9Sstevel@tonic-gate 
3587c478bd9Sstevel@tonic-gate 		/*
3597c478bd9Sstevel@tonic-gate 		 * increment the reference count for each child using this
3607c478bd9Sstevel@tonic-gate 		 * usba_device structure
3617c478bd9Sstevel@tonic-gate 		 */
3627c478bd9Sstevel@tonic-gate 		mutex_enter(&usba_device->usb_mutex);
3637c478bd9Sstevel@tonic-gate 		usba_device->usb_ref_count++;
3647c478bd9Sstevel@tonic-gate 
3657c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_USBA, hubdi_log_handle,
3667c478bd9Sstevel@tonic-gate 		    "usba_bus_ctl: init usba_device = 0x%p ref_count = %d",
3677c478bd9Sstevel@tonic-gate 		    (void *)usba_device, usba_device->usb_ref_count);
3687c478bd9Sstevel@tonic-gate 
3697c478bd9Sstevel@tonic-gate 		mutex_exit(&usba_device->usb_mutex);
3707c478bd9Sstevel@tonic-gate 
3717c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
3727c478bd9Sstevel@tonic-gate 	}
3737c478bd9Sstevel@tonic-gate 
3747c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_UNINITCHILD:
3757c478bd9Sstevel@tonic-gate 	{
3767c478bd9Sstevel@tonic-gate 		usba_device = usba_get_usba_device(child_dip);
3777c478bd9Sstevel@tonic-gate 
3787c478bd9Sstevel@tonic-gate 		if (usba_device != NULL) {
3797c478bd9Sstevel@tonic-gate 			/*
3807c478bd9Sstevel@tonic-gate 			 * decrement the reference count for each child
3817c478bd9Sstevel@tonic-gate 			 * using this  usba_device structure
3827c478bd9Sstevel@tonic-gate 			 */
3837c478bd9Sstevel@tonic-gate 			mutex_enter(&usba_device->usb_mutex);
3847c478bd9Sstevel@tonic-gate 			usba_device->usb_ref_count--;
3857c478bd9Sstevel@tonic-gate 
3867c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_USBA, hubdi_log_handle,
3877c478bd9Sstevel@tonic-gate 			    "usba_hcdi_bus_ctl: uninit usba_device=0x%p "
3887c478bd9Sstevel@tonic-gate 			    "ref_count=%d",
389112116d8Sfb 			    (void *)usba_device, usba_device->usb_ref_count);
3907c478bd9Sstevel@tonic-gate 
3917c478bd9Sstevel@tonic-gate 			mutex_exit(&usba_device->usb_mutex);
3927c478bd9Sstevel@tonic-gate 		}
3937c478bd9Sstevel@tonic-gate 		ddi_set_name_addr(child_dip, NULL);
3947c478bd9Sstevel@tonic-gate 
3957c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
3967c478bd9Sstevel@tonic-gate 	}
3977c478bd9Sstevel@tonic-gate 
3987c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_IOMIN:
3997c478bd9Sstevel@tonic-gate 		/* Do nothing */
4007c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
4017c478bd9Sstevel@tonic-gate 
4027c478bd9Sstevel@tonic-gate 	/*
4037c478bd9Sstevel@tonic-gate 	 * These ops correspond	to functions that "shouldn't" be called
4047c478bd9Sstevel@tonic-gate 	 * by a	USB client driver.  So	we whine when we're called.
4057c478bd9Sstevel@tonic-gate 	 */
4067c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_DMAPMAPC:
4077c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_REPORTINT:
4087c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_REGSIZE:
4097c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_NREGS:
4107c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_SIDDEV:
4117c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_SLAVEONLY:
4127c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_AFFINITY:
4137c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_POKE:
4147c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_PEEK:
4157c478bd9Sstevel@tonic-gate 		cmn_err(CE_CONT, "%s%d:	invalid	op (%d)	from %s%d",
416112cd14aSqz 		    ddi_node_name(dip), ddi_get_instance(dip),
417112cd14aSqz 		    op, ddi_node_name(rdip), ddi_get_instance(rdip));
4187c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
4197c478bd9Sstevel@tonic-gate 
4207c478bd9Sstevel@tonic-gate 	/*
4217c478bd9Sstevel@tonic-gate 	 * Everything else (e.g. PTOB/BTOP/BTOPR requests) we pass up
4227c478bd9Sstevel@tonic-gate 	 */
4237c478bd9Sstevel@tonic-gate 	default:
4247c478bd9Sstevel@tonic-gate 		return (ddi_ctlops(dip,	rdip, op, arg, result));
4257c478bd9Sstevel@tonic-gate 	}
4267c478bd9Sstevel@tonic-gate }
4277c478bd9Sstevel@tonic-gate 
4287c478bd9Sstevel@tonic-gate 
4297c478bd9Sstevel@tonic-gate /*
4307c478bd9Sstevel@tonic-gate  * initialize and destroy USBA module
4317c478bd9Sstevel@tonic-gate  */
4327c478bd9Sstevel@tonic-gate void
4337c478bd9Sstevel@tonic-gate usba_usba_initialization()
4347c478bd9Sstevel@tonic-gate {
4357c478bd9Sstevel@tonic-gate 	usba_log_handle = usb_alloc_log_hdl(NULL, "usba", &usba_errlevel,
436112cd14aSqz 	    &usba_errmask, NULL, 0);
4377c478bd9Sstevel@tonic-gate 
4387c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_USBA,
4397c478bd9Sstevel@tonic-gate 	    usba_log_handle, "usba_usba_initialization");
4407c478bd9Sstevel@tonic-gate 
4417c478bd9Sstevel@tonic-gate 	mutex_init(&usba_mutex, NULL, MUTEX_DRIVER, NULL);
442de6f998eSrui wang - Sun Microsystems - Beijing China 	mutex_init(&usba_hub_mutex, NULL, MUTEX_DRIVER, NULL);
4437c478bd9Sstevel@tonic-gate 	usba_init_list(&usba_device_list, NULL, NULL);
4447c478bd9Sstevel@tonic-gate }
4457c478bd9Sstevel@tonic-gate 
4467c478bd9Sstevel@tonic-gate 
4477c478bd9Sstevel@tonic-gate void
4487c478bd9Sstevel@tonic-gate usba_usba_destroy()
4497c478bd9Sstevel@tonic-gate {
4507c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_USBA, usba_log_handle, "usba_usba_destroy");
4517c478bd9Sstevel@tonic-gate 
452de6f998eSrui wang - Sun Microsystems - Beijing China 	mutex_destroy(&usba_hub_mutex);
4537c478bd9Sstevel@tonic-gate 	mutex_destroy(&usba_mutex);
4547c478bd9Sstevel@tonic-gate 	usba_destroy_list(&usba_device_list);
4557c478bd9Sstevel@tonic-gate 
4567c478bd9Sstevel@tonic-gate 	usb_free_log_hdl(usba_log_handle);
4577c478bd9Sstevel@tonic-gate }
4587c478bd9Sstevel@tonic-gate 
4597c478bd9Sstevel@tonic-gate 
4607c478bd9Sstevel@tonic-gate /*
4617c478bd9Sstevel@tonic-gate  * usba_set_usb_address:
4627c478bd9Sstevel@tonic-gate  *	set usb address in usba_device structure
4637c478bd9Sstevel@tonic-gate  */
4647c478bd9Sstevel@tonic-gate int
4657c478bd9Sstevel@tonic-gate usba_set_usb_address(usba_device_t *usba_device)
4667c478bd9Sstevel@tonic-gate {
4677c478bd9Sstevel@tonic-gate 	usb_addr_t address;
4687c478bd9Sstevel@tonic-gate 	uchar_t s = 8;
4697c478bd9Sstevel@tonic-gate 	usba_hcdi_t *hcdi;
4707c478bd9Sstevel@tonic-gate 	char *usb_address_in_use;
4717c478bd9Sstevel@tonic-gate 
4727c478bd9Sstevel@tonic-gate 	mutex_enter(&usba_device->usb_mutex);
4737c478bd9Sstevel@tonic-gate 
4747c478bd9Sstevel@tonic-gate 	hcdi = usba_hcdi_get_hcdi(usba_device->usb_root_hub_dip);
4757c478bd9Sstevel@tonic-gate 
4767c478bd9Sstevel@tonic-gate 	mutex_enter(&hcdi->hcdi_mutex);
4777c478bd9Sstevel@tonic-gate 	usb_address_in_use = hcdi->hcdi_usb_address_in_use;
4787c478bd9Sstevel@tonic-gate 
4797c478bd9Sstevel@tonic-gate 	for (address = ROOT_HUB_ADDR + 1;
4807c478bd9Sstevel@tonic-gate 	    address <= USBA_MAX_ADDRESS; address++) {
4817c478bd9Sstevel@tonic-gate 		if (usb_address_in_use[address/s] & (1 << (address % s))) {
4827c478bd9Sstevel@tonic-gate 			continue;
4837c478bd9Sstevel@tonic-gate 		}
4847c478bd9Sstevel@tonic-gate 		usb_address_in_use[address/s] |= (1 << (address % s));
4857c478bd9Sstevel@tonic-gate 		hcdi->hcdi_device_count++;
4867c478bd9Sstevel@tonic-gate 		HCDI_HOTPLUG_STATS_DATA(hcdi)->hcdi_device_count.value.ui64++;
4877c478bd9Sstevel@tonic-gate 		mutex_exit(&hcdi->hcdi_mutex);
4887c478bd9Sstevel@tonic-gate 
4897c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_USBA, usba_log_handle,
4907c478bd9Sstevel@tonic-gate 		    "usba_set_usb_address: %d", address);
4917c478bd9Sstevel@tonic-gate 
4927c478bd9Sstevel@tonic-gate 		usba_device->usb_addr = address;
4937c478bd9Sstevel@tonic-gate 
4947c478bd9Sstevel@tonic-gate 		mutex_exit(&usba_device->usb_mutex);
4957c478bd9Sstevel@tonic-gate 
4967c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
4977c478bd9Sstevel@tonic-gate 	}
4987c478bd9Sstevel@tonic-gate 
4997c478bd9Sstevel@tonic-gate 	usba_device->usb_addr = 0;
5007c478bd9Sstevel@tonic-gate 
501d291d9f2Sfrits 	USB_DPRINTF_L2(DPRINT_MASK_USBA, usba_log_handle,
5027c478bd9Sstevel@tonic-gate 	    "no usb address available");
5037c478bd9Sstevel@tonic-gate 
5047c478bd9Sstevel@tonic-gate 	mutex_exit(&hcdi->hcdi_mutex);
5057c478bd9Sstevel@tonic-gate 	mutex_exit(&usba_device->usb_mutex);
5067c478bd9Sstevel@tonic-gate 
5077c478bd9Sstevel@tonic-gate 	return (USB_FAILURE);
5087c478bd9Sstevel@tonic-gate }
5097c478bd9Sstevel@tonic-gate 
5107c478bd9Sstevel@tonic-gate 
5117c478bd9Sstevel@tonic-gate /*
5127c478bd9Sstevel@tonic-gate  * usba_unset_usb_address:
5137c478bd9Sstevel@tonic-gate  *	unset usb_address in usba_device structure
5147c478bd9Sstevel@tonic-gate  */
5157c478bd9Sstevel@tonic-gate void
5167c478bd9Sstevel@tonic-gate usba_unset_usb_address(usba_device_t *usba_device)
5177c478bd9Sstevel@tonic-gate {
5187c478bd9Sstevel@tonic-gate 	usb_addr_t address;
5197c478bd9Sstevel@tonic-gate 	usba_hcdi_t *hcdi;
5207c478bd9Sstevel@tonic-gate 	uchar_t s = 8;
5217c478bd9Sstevel@tonic-gate 	char *usb_address_in_use;
5227c478bd9Sstevel@tonic-gate 
5237c478bd9Sstevel@tonic-gate 	mutex_enter(&usba_device->usb_mutex);
5247c478bd9Sstevel@tonic-gate 	address = usba_device->usb_addr;
5257c478bd9Sstevel@tonic-gate 	hcdi = usba_hcdi_get_hcdi(usba_device->usb_root_hub_dip);
5267c478bd9Sstevel@tonic-gate 
5277c478bd9Sstevel@tonic-gate 	if (address > ROOT_HUB_ADDR) {
5287c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_USBA, usba_log_handle,
5297c478bd9Sstevel@tonic-gate 		    "usba_unset_usb_address: address=%d", address);
5307c478bd9Sstevel@tonic-gate 
5317c478bd9Sstevel@tonic-gate 		mutex_enter(&hcdi->hcdi_mutex);
5327c478bd9Sstevel@tonic-gate 		usb_address_in_use = hcdi->hcdi_usb_address_in_use;
5337c478bd9Sstevel@tonic-gate 
5347c478bd9Sstevel@tonic-gate 		ASSERT(usb_address_in_use[address/s] & (1 << (address % s)));
5357c478bd9Sstevel@tonic-gate 
5367c478bd9Sstevel@tonic-gate 		usb_address_in_use[address/s] &= ~(1 << (address % s));
5377c478bd9Sstevel@tonic-gate 
5387c478bd9Sstevel@tonic-gate 		hcdi->hcdi_device_count--;
5397c478bd9Sstevel@tonic-gate 		HCDI_HOTPLUG_STATS_DATA(hcdi)->hcdi_device_count.value.ui64--;
5407c478bd9Sstevel@tonic-gate 
5417c478bd9Sstevel@tonic-gate 		mutex_exit(&hcdi->hcdi_mutex);
5427c478bd9Sstevel@tonic-gate 
5437c478bd9Sstevel@tonic-gate 		usba_device->usb_addr = 0;
5447c478bd9Sstevel@tonic-gate 	}
5457c478bd9Sstevel@tonic-gate 	mutex_exit(&usba_device->usb_mutex);
5467c478bd9Sstevel@tonic-gate }
5477c478bd9Sstevel@tonic-gate 
5487c478bd9Sstevel@tonic-gate 
5497c478bd9Sstevel@tonic-gate struct usba_evdata *
5507c478bd9Sstevel@tonic-gate usba_get_evdata(dev_info_t *dip)
5517c478bd9Sstevel@tonic-gate {
5527c478bd9Sstevel@tonic-gate 	usba_evdata_t *evdata;
5537c478bd9Sstevel@tonic-gate 	usba_device_t *usba_device = usba_get_usba_device(dip);
5547c478bd9Sstevel@tonic-gate 
5557c478bd9Sstevel@tonic-gate 	/* called when dip attaches */
5567c478bd9Sstevel@tonic-gate 	ASSERT(usba_device != NULL);
5577c478bd9Sstevel@tonic-gate 
5587c478bd9Sstevel@tonic-gate 	mutex_enter(&usba_device->usb_mutex);
5597c478bd9Sstevel@tonic-gate 	evdata = usba_device->usb_evdata;
5607c478bd9Sstevel@tonic-gate 	while (evdata) {
5617c478bd9Sstevel@tonic-gate 		if (evdata->ev_dip == dip) {
5627c478bd9Sstevel@tonic-gate 			mutex_exit(&usba_device->usb_mutex);
5637c478bd9Sstevel@tonic-gate 
5647c478bd9Sstevel@tonic-gate 			return (evdata);
5657c478bd9Sstevel@tonic-gate 		}
5667c478bd9Sstevel@tonic-gate 		evdata = evdata->ev_next;
5677c478bd9Sstevel@tonic-gate 	}
5687c478bd9Sstevel@tonic-gate 
5697c478bd9Sstevel@tonic-gate 	evdata = kmem_zalloc(sizeof (usba_evdata_t), KM_SLEEP);
5707c478bd9Sstevel@tonic-gate 	evdata->ev_dip = dip;
5717c478bd9Sstevel@tonic-gate 	evdata->ev_next = usba_device->usb_evdata;
5727c478bd9Sstevel@tonic-gate 	usba_device->usb_evdata = evdata;
5737c478bd9Sstevel@tonic-gate 	mutex_exit(&usba_device->usb_mutex);
5747c478bd9Sstevel@tonic-gate 
5757c478bd9Sstevel@tonic-gate 	return (evdata);
5767c478bd9Sstevel@tonic-gate }
5777c478bd9Sstevel@tonic-gate 
5787c478bd9Sstevel@tonic-gate 
5797c478bd9Sstevel@tonic-gate /*
5807c478bd9Sstevel@tonic-gate  * allocate a usb device structure and link it in the list
5817c478bd9Sstevel@tonic-gate  */
5827c478bd9Sstevel@tonic-gate usba_device_t *
5837c478bd9Sstevel@tonic-gate usba_alloc_usba_device(dev_info_t *root_hub_dip)
5847c478bd9Sstevel@tonic-gate {
5857c478bd9Sstevel@tonic-gate 	usba_device_t	*usba_device;
5867c478bd9Sstevel@tonic-gate 	int		ep_idx;
5877c478bd9Sstevel@tonic-gate 	ddi_iblock_cookie_t iblock_cookie =
5887c478bd9Sstevel@tonic-gate 	    usba_hcdi_get_hcdi(root_hub_dip)->hcdi_iblock_cookie;
5897c478bd9Sstevel@tonic-gate 
5907c478bd9Sstevel@tonic-gate 	/*
5917c478bd9Sstevel@tonic-gate 	 * create a new usba_device structure
5927c478bd9Sstevel@tonic-gate 	 */
5937c478bd9Sstevel@tonic-gate 	usba_device = kmem_zalloc(sizeof (usba_device_t), KM_SLEEP);
5947c478bd9Sstevel@tonic-gate 
5957c478bd9Sstevel@tonic-gate 	/*
5967c478bd9Sstevel@tonic-gate 	 * initialize usba_device
5977c478bd9Sstevel@tonic-gate 	 */
5987c478bd9Sstevel@tonic-gate 	mutex_init(&usba_device->usb_mutex, NULL, MUTEX_DRIVER,
599112cd14aSqz 	    iblock_cookie);
6007c478bd9Sstevel@tonic-gate 
6017c478bd9Sstevel@tonic-gate 	usba_init_list(&usba_device->usb_device_list, (usb_opaque_t)usba_device,
602112cd14aSqz 	    iblock_cookie);
6037c478bd9Sstevel@tonic-gate 	usba_init_list(&usba_device->usb_allocated, (usb_opaque_t)usba_device,
604112cd14aSqz 	    iblock_cookie);
6057c478bd9Sstevel@tonic-gate 	mutex_enter(&usba_device->usb_mutex);
6067c478bd9Sstevel@tonic-gate 	usba_device->usb_root_hub_dip = root_hub_dip;
6077c478bd9Sstevel@tonic-gate 
6087c478bd9Sstevel@tonic-gate 	/*
6097c478bd9Sstevel@tonic-gate 	 * add to list of usba_devices
6107c478bd9Sstevel@tonic-gate 	 */
6117c478bd9Sstevel@tonic-gate 	usba_add_to_list(&usba_device_list, &usba_device->usb_device_list);
6127c478bd9Sstevel@tonic-gate 
6137c478bd9Sstevel@tonic-gate 	/* init mutex in each usba_ph_impl structure */
6147c478bd9Sstevel@tonic-gate 	for (ep_idx = 0; ep_idx < USBA_N_ENDPOINTS; ep_idx++) {
6157c478bd9Sstevel@tonic-gate 		mutex_init(&usba_device->usb_ph_list[ep_idx].usba_ph_mutex,
6167c478bd9Sstevel@tonic-gate 		    NULL, MUTEX_DRIVER, iblock_cookie);
6177c478bd9Sstevel@tonic-gate 	}
6187c478bd9Sstevel@tonic-gate 
6197c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L2(DPRINT_MASK_USBA, usba_log_handle,
6207c478bd9Sstevel@tonic-gate 	    "allocated usba_device 0x%p", (void *)usba_device);
6217c478bd9Sstevel@tonic-gate 
6227c478bd9Sstevel@tonic-gate 	mutex_exit(&usba_device->usb_mutex);
6237c478bd9Sstevel@tonic-gate 
6247c478bd9Sstevel@tonic-gate 	return (usba_device);
6257c478bd9Sstevel@tonic-gate }
6267c478bd9Sstevel@tonic-gate 
6277c478bd9Sstevel@tonic-gate 
6287c478bd9Sstevel@tonic-gate /* free NDI event data associated with usba_device */
6297c478bd9Sstevel@tonic-gate void
6307c478bd9Sstevel@tonic-gate usba_free_evdata(usba_evdata_t *evdata)
6317c478bd9Sstevel@tonic-gate {
6327c478bd9Sstevel@tonic-gate 	usba_evdata_t *next;
6337c478bd9Sstevel@tonic-gate 
6347c478bd9Sstevel@tonic-gate 	while (evdata) {
6357c478bd9Sstevel@tonic-gate 		next = evdata->ev_next;
6367c478bd9Sstevel@tonic-gate 		kmem_free(evdata, sizeof (usba_evdata_t));
6377c478bd9Sstevel@tonic-gate 		evdata = next;
6387c478bd9Sstevel@tonic-gate 	}
6397c478bd9Sstevel@tonic-gate }
6407c478bd9Sstevel@tonic-gate 
6417c478bd9Sstevel@tonic-gate 
6427c478bd9Sstevel@tonic-gate /*
6437c478bd9Sstevel@tonic-gate  * free usb device structure
6447c478bd9Sstevel@tonic-gate  */
6457c478bd9Sstevel@tonic-gate void
6467c478bd9Sstevel@tonic-gate usba_free_usba_device(usba_device_t *usba_device)
6477c478bd9Sstevel@tonic-gate {
6487c478bd9Sstevel@tonic-gate 	int			i, ep_idx;
6497c478bd9Sstevel@tonic-gate 	usb_pipe_handle_t	def_ph;
6507c478bd9Sstevel@tonic-gate 
6517c478bd9Sstevel@tonic-gate 	if (usba_device == NULL) {
6527c478bd9Sstevel@tonic-gate 
6537c478bd9Sstevel@tonic-gate 		return;
6547c478bd9Sstevel@tonic-gate 	}
6557c478bd9Sstevel@tonic-gate 
6567c478bd9Sstevel@tonic-gate 	mutex_enter(&usba_device->usb_mutex);
6577c478bd9Sstevel@tonic-gate 	if (usba_device->usb_ref_count) {
6587c478bd9Sstevel@tonic-gate 		mutex_exit(&usba_device->usb_mutex);
6597c478bd9Sstevel@tonic-gate 
6607c478bd9Sstevel@tonic-gate 		return;
6617c478bd9Sstevel@tonic-gate 	}
6627c478bd9Sstevel@tonic-gate 
6637c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_USBA, usba_log_handle,
6647c478bd9Sstevel@tonic-gate 	    "usba_free_usba_device 0x%p, address=0x%x, ref cnt=%d",
6657c478bd9Sstevel@tonic-gate 	    (void *)usba_device, usba_device->usb_addr,
6667c478bd9Sstevel@tonic-gate 	    usba_device->usb_ref_count);
6677c478bd9Sstevel@tonic-gate 
6687c478bd9Sstevel@tonic-gate 	usba_free_evdata(usba_device->usb_evdata);
6697c478bd9Sstevel@tonic-gate 	mutex_exit(&usba_device->usb_mutex);
6707c478bd9Sstevel@tonic-gate 
6717c478bd9Sstevel@tonic-gate 	def_ph = usba_usbdev_to_dflt_pipe_handle(usba_device);
6727c478bd9Sstevel@tonic-gate 	if (def_ph != NULL) {
6737c478bd9Sstevel@tonic-gate 		usba_pipe_handle_data_t	*ph_data = usba_get_ph_data(def_ph);
6747c478bd9Sstevel@tonic-gate 
6757c478bd9Sstevel@tonic-gate 		if (ph_data) {
6767c478bd9Sstevel@tonic-gate 			usb_pipe_close(ph_data->p_dip, def_ph,
6777c478bd9Sstevel@tonic-gate 			    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED,
6787c478bd9Sstevel@tonic-gate 			    NULL, NULL);
6797c478bd9Sstevel@tonic-gate 		}
6807c478bd9Sstevel@tonic-gate 	}
6817c478bd9Sstevel@tonic-gate 
682993e3fafSRobert Mustacchi 	/*
683993e3fafSRobert Mustacchi 	 * Give the HCD a chance to clean up this child device before we finish
684993e3fafSRobert Mustacchi 	 * tearing things down.
685993e3fafSRobert Mustacchi 	 */
686993e3fafSRobert Mustacchi 	if (usba_device->usb_hcdi_ops->usba_hcdi_device_fini != NULL) {
687993e3fafSRobert Mustacchi 		usba_device->usb_hcdi_ops->usba_hcdi_device_fini(
688993e3fafSRobert Mustacchi 		    usba_device, usba_device->usb_hcd_private);
689993e3fafSRobert Mustacchi 		usba_device->usb_hcd_private = NULL;
690993e3fafSRobert Mustacchi 	}
691993e3fafSRobert Mustacchi 
6927c478bd9Sstevel@tonic-gate 	mutex_enter(&usba_mutex);
6937c478bd9Sstevel@tonic-gate 
6947c478bd9Sstevel@tonic-gate 	/* destroy mutex in each usba_ph_impl structure */
6957c478bd9Sstevel@tonic-gate 	for (ep_idx = 0; ep_idx < USBA_N_ENDPOINTS; ep_idx++) {
6967c478bd9Sstevel@tonic-gate 		mutex_destroy(&usba_device->usb_ph_list[ep_idx].usba_ph_mutex);
6977c478bd9Sstevel@tonic-gate 	}
6987c478bd9Sstevel@tonic-gate 
6997c478bd9Sstevel@tonic-gate 	(void) usba_rm_from_list(&usba_device_list,
700112cd14aSqz 	    &usba_device->usb_device_list);
7017c478bd9Sstevel@tonic-gate 
7027c478bd9Sstevel@tonic-gate 	mutex_exit(&usba_mutex);
7037c478bd9Sstevel@tonic-gate 
7047c478bd9Sstevel@tonic-gate 	usba_destroy_list(&usba_device->usb_device_list);
7057c478bd9Sstevel@tonic-gate 	usba_destroy_list(&usba_device->usb_allocated);
7067c478bd9Sstevel@tonic-gate 
7077c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L2(DPRINT_MASK_USBA, usba_log_handle,
7087c478bd9Sstevel@tonic-gate 	    "deallocating usba_device = 0x%p, address = 0x%x",
7097c478bd9Sstevel@tonic-gate 	    (void *)usba_device, usba_device->usb_addr);
7107c478bd9Sstevel@tonic-gate 
7117c478bd9Sstevel@tonic-gate 	/*
7127c478bd9Sstevel@tonic-gate 	 * ohci allocates descriptors for root hub so we can't
7137c478bd9Sstevel@tonic-gate 	 * deallocate these here
7147c478bd9Sstevel@tonic-gate 	 */
7157c478bd9Sstevel@tonic-gate 
7167c478bd9Sstevel@tonic-gate 	if (usba_device->usb_addr != ROOT_HUB_ADDR) {
7177c478bd9Sstevel@tonic-gate 		if (usba_device->usb_cfg_array) {
7187c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_USBA, usba_log_handle,
7197c478bd9Sstevel@tonic-gate 			    "deallocating usb_config_array: 0x%p",
720112116d8Sfb 			    (void *)usba_device->usb_cfg_array);
7217c478bd9Sstevel@tonic-gate 			mutex_enter(&usba_device->usb_mutex);
7227c478bd9Sstevel@tonic-gate 			for (i = 0;
7237c478bd9Sstevel@tonic-gate 			    i < usba_device->usb_dev_descr->bNumConfigurations;
7247c478bd9Sstevel@tonic-gate 			    i++) {
7257c478bd9Sstevel@tonic-gate 				if (usba_device->usb_cfg_array[i]) {
7267c478bd9Sstevel@tonic-gate 					kmem_free(
7277c478bd9Sstevel@tonic-gate 					    usba_device->usb_cfg_array[i],
7287c478bd9Sstevel@tonic-gate 					    usba_device->usb_cfg_array_len[i]);
7297c478bd9Sstevel@tonic-gate 				}
7307c478bd9Sstevel@tonic-gate 			}
7317c478bd9Sstevel@tonic-gate 
7327c478bd9Sstevel@tonic-gate 			/* free the array pointers */
7337c478bd9Sstevel@tonic-gate 			kmem_free(usba_device->usb_cfg_array,
7347c478bd9Sstevel@tonic-gate 			    usba_device->usb_cfg_array_length);
7357c478bd9Sstevel@tonic-gate 			kmem_free(usba_device->usb_cfg_array_len,
7367c478bd9Sstevel@tonic-gate 			    usba_device->usb_cfg_array_len_length);
7377c478bd9Sstevel@tonic-gate 
7387c478bd9Sstevel@tonic-gate 			mutex_exit(&usba_device->usb_mutex);
7397c478bd9Sstevel@tonic-gate 		}
7407c478bd9Sstevel@tonic-gate 
7417c478bd9Sstevel@tonic-gate 		if (usba_device->usb_cfg_str_descr) {
7427c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_USBA, usba_log_handle,
7437c478bd9Sstevel@tonic-gate 			    "deallocating usb_cfg_str_descr: 0x%p",
744112116d8Sfb 			    (void *)usba_device->usb_cfg_str_descr);
7457c478bd9Sstevel@tonic-gate 			for (i = 0;
7467c478bd9Sstevel@tonic-gate 			    i < usba_device->usb_dev_descr->bNumConfigurations;
7477c478bd9Sstevel@tonic-gate 			    i++) {
7487c478bd9Sstevel@tonic-gate 				if (usba_device->usb_cfg_str_descr[i]) {
7497c478bd9Sstevel@tonic-gate 					kmem_free(
7507c478bd9Sstevel@tonic-gate 					    usba_device->usb_cfg_str_descr[i],
7517c478bd9Sstevel@tonic-gate 					    strlen(usba_device->
752112cd14aSqz 					    usb_cfg_str_descr[i]) + 1);
7537c478bd9Sstevel@tonic-gate 				}
7547c478bd9Sstevel@tonic-gate 			}
7557c478bd9Sstevel@tonic-gate 			/* free the array pointers */
7567c478bd9Sstevel@tonic-gate 			kmem_free(usba_device->usb_cfg_str_descr,
7577c478bd9Sstevel@tonic-gate 			    sizeof (uchar_t *) * usba_device->usb_n_cfgs);
7587c478bd9Sstevel@tonic-gate 		}
7597c478bd9Sstevel@tonic-gate 
7607c478bd9Sstevel@tonic-gate 		if (usba_device->usb_dev_descr) {
7617c478bd9Sstevel@tonic-gate 			kmem_free(usba_device->usb_dev_descr,
762112cd14aSqz 			    sizeof (usb_dev_descr_t));
7637c478bd9Sstevel@tonic-gate 		}
7647c478bd9Sstevel@tonic-gate 
7657c478bd9Sstevel@tonic-gate 		if (usba_device->usb_mfg_str) {
7667c478bd9Sstevel@tonic-gate 			kmem_free(usba_device->usb_mfg_str,
767112cd14aSqz 			    strlen(usba_device->usb_mfg_str) + 1);
7687c478bd9Sstevel@tonic-gate 		}
7697c478bd9Sstevel@tonic-gate 
7707c478bd9Sstevel@tonic-gate 		if (usba_device->usb_product_str) {
7717c478bd9Sstevel@tonic-gate 			kmem_free(usba_device->usb_product_str,
772112cd14aSqz 			    strlen(usba_device->usb_product_str) + 1);
7737c478bd9Sstevel@tonic-gate 		}
7747c478bd9Sstevel@tonic-gate 
7757c478bd9Sstevel@tonic-gate 		if (usba_device->usb_serialno_str) {
7767c478bd9Sstevel@tonic-gate 			kmem_free(usba_device->usb_serialno_str,
777112cd14aSqz 			    strlen(usba_device->usb_serialno_str) + 1);
7787c478bd9Sstevel@tonic-gate 		}
7797c478bd9Sstevel@tonic-gate 
780*0d2006e4SRobert Mustacchi 		usba_free_binary_object_store(usba_device);
781*0d2006e4SRobert Mustacchi 
782e2c88f0cSGarrett D'Amore 		usba_unset_usb_address(usba_device);
7837c478bd9Sstevel@tonic-gate 	}
7847c478bd9Sstevel@tonic-gate 
7857c478bd9Sstevel@tonic-gate #ifndef __lock_lint
7867c478bd9Sstevel@tonic-gate 	ASSERT(usba_device->usb_client_dev_data_list.cddl_next == NULL);
7877c478bd9Sstevel@tonic-gate #endif
7887c478bd9Sstevel@tonic-gate 
7897c478bd9Sstevel@tonic-gate 	if (usba_device->usb_client_flags) {
7907c478bd9Sstevel@tonic-gate #ifndef __lock_lint
7917c478bd9Sstevel@tonic-gate 		int i;
7927c478bd9Sstevel@tonic-gate 
7937c478bd9Sstevel@tonic-gate 		for (i = 0; i < usba_device->usb_n_ifs; i++) {
7947c478bd9Sstevel@tonic-gate 			ASSERT(usba_device->usb_client_flags[i] == 0);
7957c478bd9Sstevel@tonic-gate 		}
7967c478bd9Sstevel@tonic-gate #endif
7977c478bd9Sstevel@tonic-gate 		kmem_free(usba_device->usb_client_flags,
7987c478bd9Sstevel@tonic-gate 		    usba_device->usb_n_ifs * USBA_CLIENT_FLAG_SIZE);
7997c478bd9Sstevel@tonic-gate 	}
8007c478bd9Sstevel@tonic-gate 
8017c478bd9Sstevel@tonic-gate 
8027c478bd9Sstevel@tonic-gate 	if (usba_device->usb_client_attach_list) {
8037c478bd9Sstevel@tonic-gate 		kmem_free(usba_device->usb_client_attach_list,
8047c478bd9Sstevel@tonic-gate 		    usba_device->usb_n_ifs *
8057c478bd9Sstevel@tonic-gate 		    sizeof (*usba_device->usb_client_attach_list));
8067c478bd9Sstevel@tonic-gate 	}
8077c478bd9Sstevel@tonic-gate 	if (usba_device->usb_client_ev_cb_list) {
8087c478bd9Sstevel@tonic-gate 		kmem_free(usba_device->usb_client_ev_cb_list,
8097c478bd9Sstevel@tonic-gate 		    usba_device->usb_n_ifs *
8107c478bd9Sstevel@tonic-gate 		    sizeof (*usba_device->usb_client_ev_cb_list));
8117c478bd9Sstevel@tonic-gate 	}
8127c478bd9Sstevel@tonic-gate 
8137c478bd9Sstevel@tonic-gate 	/*
8147c478bd9Sstevel@tonic-gate 	 * finally ready to destroy the structure
8157c478bd9Sstevel@tonic-gate 	 */
8167c478bd9Sstevel@tonic-gate 	mutex_destroy(&usba_device->usb_mutex);
8177c478bd9Sstevel@tonic-gate 
8187c478bd9Sstevel@tonic-gate 	kmem_free((caddr_t)usba_device, sizeof (usba_device_t));
8197c478bd9Sstevel@tonic-gate }
8207c478bd9Sstevel@tonic-gate 
8217c478bd9Sstevel@tonic-gate 
8227c478bd9Sstevel@tonic-gate /* clear the data toggle for all endpoints on this device */
8237c478bd9Sstevel@tonic-gate void
8247c478bd9Sstevel@tonic-gate usba_clear_data_toggle(usba_device_t *usba_device)
8257c478bd9Sstevel@tonic-gate {
8267c478bd9Sstevel@tonic-gate 	int	i;
8277c478bd9Sstevel@tonic-gate 
8287c478bd9Sstevel@tonic-gate 	if (usba_device != NULL) {
8297c478bd9Sstevel@tonic-gate 		mutex_enter(&usba_device->usb_mutex);
8307c478bd9Sstevel@tonic-gate 		for (i = 0; i < USBA_N_ENDPOINTS; i++) {
8317c478bd9Sstevel@tonic-gate 			usba_device->usb_ph_list[i].usba_ph_flags &=
8327c478bd9Sstevel@tonic-gate 			    ~USBA_PH_DATA_TOGGLE;
8337c478bd9Sstevel@tonic-gate 		}
8347c478bd9Sstevel@tonic-gate 		mutex_exit(&usba_device->usb_mutex);
8357c478bd9Sstevel@tonic-gate 	}
8367c478bd9Sstevel@tonic-gate }
8377c478bd9Sstevel@tonic-gate 
8387c478bd9Sstevel@tonic-gate 
8397c478bd9Sstevel@tonic-gate /*
8407c478bd9Sstevel@tonic-gate  * usba_create_child_devi():
8417c478bd9Sstevel@tonic-gate  *	create a child devinfo node, usba_device, attach properties.
8427c478bd9Sstevel@tonic-gate  *	the usba_device structure is shared between all interfaces
8437c478bd9Sstevel@tonic-gate  */
8447c478bd9Sstevel@tonic-gate int
8457c478bd9Sstevel@tonic-gate usba_create_child_devi(dev_info_t	*dip,
8467c478bd9Sstevel@tonic-gate 		char			*node_name,
8477c478bd9Sstevel@tonic-gate 		usba_hcdi_ops_t		*usba_hcdi_ops,
8487c478bd9Sstevel@tonic-gate 		dev_info_t		*usb_root_hub_dip,
8497c478bd9Sstevel@tonic-gate 		usb_port_status_t	port_status,
8507c478bd9Sstevel@tonic-gate 		usba_device_t		*usba_device,
8517c478bd9Sstevel@tonic-gate 		dev_info_t		**child_dip)
8527c478bd9Sstevel@tonic-gate {
8537c478bd9Sstevel@tonic-gate 	int rval = USB_FAILURE;
8547c478bd9Sstevel@tonic-gate 	int usba_device_allocated = 0;
8557c478bd9Sstevel@tonic-gate 	usb_addr_t	address;
8567c478bd9Sstevel@tonic-gate 
8577c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_USBA, usba_log_handle,
8587c478bd9Sstevel@tonic-gate 	    "usba_create_child_devi: %s usba_device=0x%p "
8597c478bd9Sstevel@tonic-gate 	    "port status=0x%x", node_name,
8607c478bd9Sstevel@tonic-gate 	    (void *)usba_device, port_status);
8617c478bd9Sstevel@tonic-gate 
862fa9e4066Sahrens 	ndi_devi_alloc_sleep(dip, node_name, (pnode_t)DEVI_SID_NODEID,
863112cd14aSqz 	    child_dip);
8647c478bd9Sstevel@tonic-gate 
8657c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_MASK_USBA, usba_log_handle,
866112116d8Sfb 	    "child dip=0x%p", (void *)*child_dip);
8677c478bd9Sstevel@tonic-gate 
8687c478bd9Sstevel@tonic-gate 	if (usba_device == NULL) {
8697c478bd9Sstevel@tonic-gate 
8707c478bd9Sstevel@tonic-gate 		usba_device = usba_alloc_usba_device(usb_root_hub_dip);
8717c478bd9Sstevel@tonic-gate 
8727c478bd9Sstevel@tonic-gate 		/* grab the mutex to keep warlock happy */
8737c478bd9Sstevel@tonic-gate 		mutex_enter(&usba_device->usb_mutex);
8747c478bd9Sstevel@tonic-gate 		usba_device->usb_hcdi_ops	= usba_hcdi_ops;
8757c478bd9Sstevel@tonic-gate 		usba_device->usb_port_status	= port_status;
8767c478bd9Sstevel@tonic-gate 		mutex_exit(&usba_device->usb_mutex);
8777c478bd9Sstevel@tonic-gate 
8787c478bd9Sstevel@tonic-gate 		usba_device_allocated++;
8797c478bd9Sstevel@tonic-gate 	} else {
8807c478bd9Sstevel@tonic-gate 		mutex_enter(&usba_device->usb_mutex);
8817c478bd9Sstevel@tonic-gate 		if (usba_hcdi_ops) {
8827c478bd9Sstevel@tonic-gate 			ASSERT(usba_device->usb_hcdi_ops == usba_hcdi_ops);
8837c478bd9Sstevel@tonic-gate 		}
8847c478bd9Sstevel@tonic-gate 		if (usb_root_hub_dip) {
8857c478bd9Sstevel@tonic-gate 			ASSERT(usba_device->usb_root_hub_dip ==
886112cd14aSqz 			    usb_root_hub_dip);
8877c478bd9Sstevel@tonic-gate 		}
8887c478bd9Sstevel@tonic-gate 
8897c478bd9Sstevel@tonic-gate 		usba_device->usb_port_status	= port_status;
8907c478bd9Sstevel@tonic-gate 
8917c478bd9Sstevel@tonic-gate 		mutex_exit(&usba_device->usb_mutex);
8927c478bd9Sstevel@tonic-gate 	}
8937c478bd9Sstevel@tonic-gate 
8947c478bd9Sstevel@tonic-gate 	if (usba_device->usb_addr == 0) {
8957c478bd9Sstevel@tonic-gate 		if (usba_set_usb_address(usba_device) == USB_FAILURE) {
8967c478bd9Sstevel@tonic-gate 			address = 0;
8977c478bd9Sstevel@tonic-gate 
8987c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_USBA, usba_log_handle,
899112cd14aSqz 			    "cannot set usb address for dip=0x%p",
900112116d8Sfb 			    (void *)*child_dip);
9017c478bd9Sstevel@tonic-gate 
9027c478bd9Sstevel@tonic-gate 			goto fail;
9037c478bd9Sstevel@tonic-gate 		}
9047c478bd9Sstevel@tonic-gate 	}
9057c478bd9Sstevel@tonic-gate 	address = usba_device->usb_addr;
9067c478bd9Sstevel@tonic-gate 
9077c478bd9Sstevel@tonic-gate 	/* attach properties */
9087c478bd9Sstevel@tonic-gate 	rval = ndi_prop_update_int(DDI_DEV_T_NONE, *child_dip,
909112cd14aSqz 	    "assigned-address", address);
9107c478bd9Sstevel@tonic-gate 	if (rval != DDI_PROP_SUCCESS) {
9117c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_USBA, usba_log_handle,
912112cd14aSqz 		    "cannot set usb address property for dip=0x%p",
913112116d8Sfb 		    (void *)*child_dip);
9147c478bd9Sstevel@tonic-gate 		rval = USB_FAILURE;
9157c478bd9Sstevel@tonic-gate 
9167c478bd9Sstevel@tonic-gate 		goto fail;
9177c478bd9Sstevel@tonic-gate 	}
9187c478bd9Sstevel@tonic-gate 
9197c478bd9Sstevel@tonic-gate 	/*
9207c478bd9Sstevel@tonic-gate 	 * store the usba_device point in the dip
9217c478bd9Sstevel@tonic-gate 	 */
9227c478bd9Sstevel@tonic-gate 	usba_set_usba_device(*child_dip, usba_device);
9237c478bd9Sstevel@tonic-gate 
9247c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_USBA, usba_log_handle,
9257c478bd9Sstevel@tonic-gate 	    "usba_create_child_devi: devi=0x%p (%s) ud=0x%p",
926112116d8Sfb 	    (void *)*child_dip, ddi_driver_name(*child_dip),
927112116d8Sfb 	    (void *)usba_device);
9287c478bd9Sstevel@tonic-gate 
9297c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
9307c478bd9Sstevel@tonic-gate 
9317c478bd9Sstevel@tonic-gate fail:
9327c478bd9Sstevel@tonic-gate 	if (*child_dip) {
9337c478bd9Sstevel@tonic-gate 		int rval = usba_destroy_child_devi(*child_dip, NDI_DEVI_REMOVE);
9347c478bd9Sstevel@tonic-gate 		ASSERT(rval == USB_SUCCESS);
9357c478bd9Sstevel@tonic-gate 		*child_dip = NULL;
9367c478bd9Sstevel@tonic-gate 	}
9377c478bd9Sstevel@tonic-gate 
9387c478bd9Sstevel@tonic-gate 	if (usba_device_allocated) {
9397c478bd9Sstevel@tonic-gate 		usba_free_usba_device(usba_device);
9407c478bd9Sstevel@tonic-gate 	} else if (address && usba_device) {
9417c478bd9Sstevel@tonic-gate 		usba_unset_usb_address(usba_device);
9427c478bd9Sstevel@tonic-gate 	}
9437c478bd9Sstevel@tonic-gate 
944d291d9f2Sfrits 	USB_DPRINTF_L2(DPRINT_MASK_USBA, usba_log_handle,
9457c478bd9Sstevel@tonic-gate 	    "usba_create_child_devi failed: rval=%d", rval);
9467c478bd9Sstevel@tonic-gate 
9477c478bd9Sstevel@tonic-gate 	return (rval);
9487c478bd9Sstevel@tonic-gate }
9497c478bd9Sstevel@tonic-gate 
9507c478bd9Sstevel@tonic-gate 
9517c478bd9Sstevel@tonic-gate int
9527c478bd9Sstevel@tonic-gate usba_destroy_child_devi(dev_info_t *dip, uint_t flag)
9537c478bd9Sstevel@tonic-gate {
9547c478bd9Sstevel@tonic-gate 	usba_device_t	*usba_device;
9557c478bd9Sstevel@tonic-gate 	int		rval = NDI_SUCCESS;
9567c478bd9Sstevel@tonic-gate 
9577c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L2(DPRINT_MASK_USBA, usba_log_handle,
9587c478bd9Sstevel@tonic-gate 	    "usba_destroy_child_devi: %s%d (0x%p)",
959112116d8Sfb 	    ddi_driver_name(dip), ddi_get_instance(dip), (void *)dip);
9607c478bd9Sstevel@tonic-gate 
9617c478bd9Sstevel@tonic-gate 	usba_device = usba_get_usba_device(dip);
9627c478bd9Sstevel@tonic-gate 
9637c478bd9Sstevel@tonic-gate 	/*
9647c478bd9Sstevel@tonic-gate 	 * if the child hasn't been bound yet, we can just
9657c478bd9Sstevel@tonic-gate 	 * free the dip
9667c478bd9Sstevel@tonic-gate 	 */
9677c478bd9Sstevel@tonic-gate 	if (i_ddi_node_state(dip) < DS_INITIALIZED) {
9687c478bd9Sstevel@tonic-gate 		/*
9697c478bd9Sstevel@tonic-gate 		 * do not call ndi_devi_free() since it might
9707c478bd9Sstevel@tonic-gate 		 * deadlock
9717c478bd9Sstevel@tonic-gate 		 */
9727c478bd9Sstevel@tonic-gate 		rval = ddi_remove_child(dip, 0);
9737c478bd9Sstevel@tonic-gate 
9747c478bd9Sstevel@tonic-gate 	} else {
9757c478bd9Sstevel@tonic-gate 		char *devnm = kmem_alloc(MAXNAMELEN + 1, KM_SLEEP);
9767c478bd9Sstevel@tonic-gate 		dev_info_t *pdip = ddi_get_parent(dip);
9777c478bd9Sstevel@tonic-gate 
9787c478bd9Sstevel@tonic-gate 		(void) ddi_deviname(dip, devnm);
9797c478bd9Sstevel@tonic-gate 
9807c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_USBA, usba_log_handle,
9817c478bd9Sstevel@tonic-gate 		    "usba_destroy_child_devi:\n\t"
982112116d8Sfb 		    "offlining dip 0x%p usba_device=0x%p (%s)", (void *)dip,
9837c478bd9Sstevel@tonic-gate 		    (void *)usba_device, devnm);
9847c478bd9Sstevel@tonic-gate 
9857c478bd9Sstevel@tonic-gate 		(void) devfs_clean(pdip, NULL, DV_CLEAN_FORCE);
9867c478bd9Sstevel@tonic-gate 		rval =	ndi_devi_unconfig_one(pdip, devnm + 1, NULL,
9877c478bd9Sstevel@tonic-gate 		    flag | NDI_UNCONFIG | NDI_DEVI_OFFLINE);
9887c478bd9Sstevel@tonic-gate 		if (rval != NDI_SUCCESS) {
9897c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_USBA, usba_log_handle,
9907c478bd9Sstevel@tonic-gate 			    " ndi_devi_unconfig_one %s%d failed (%d)",
9917c478bd9Sstevel@tonic-gate 			    ddi_driver_name(dip), ddi_get_instance(dip),
9927c478bd9Sstevel@tonic-gate 			    rval);
9937c478bd9Sstevel@tonic-gate 		}
9947c478bd9Sstevel@tonic-gate 		kmem_free(devnm, MAXNAMELEN + 1);
9957c478bd9Sstevel@tonic-gate 	}
9967c478bd9Sstevel@tonic-gate 
9977c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_USBA, usba_log_handle,
9987c478bd9Sstevel@tonic-gate 	    "usba_destroy_child_devi: rval=%d", rval);
9997c478bd9Sstevel@tonic-gate 
10007c478bd9Sstevel@tonic-gate 	return (rval == NDI_SUCCESS ? USB_SUCCESS : USB_FAILURE);
10017c478bd9Sstevel@tonic-gate }
10027c478bd9Sstevel@tonic-gate 
10037c478bd9Sstevel@tonic-gate 
10047c478bd9Sstevel@tonic-gate /*
10057c478bd9Sstevel@tonic-gate  * list management
10067c478bd9Sstevel@tonic-gate  */
10077c478bd9Sstevel@tonic-gate void
10087c478bd9Sstevel@tonic-gate usba_init_list(usba_list_entry_t *element, usb_opaque_t private,
10097c478bd9Sstevel@tonic-gate 	ddi_iblock_cookie_t	iblock_cookie)
10107c478bd9Sstevel@tonic-gate {
10117c478bd9Sstevel@tonic-gate 	mutex_init(&element->list_mutex, NULL, MUTEX_DRIVER,
1012112cd14aSqz 	    iblock_cookie);
10137c478bd9Sstevel@tonic-gate 	mutex_enter(&element->list_mutex);
10147c478bd9Sstevel@tonic-gate 	element->private = private;
10157c478bd9Sstevel@tonic-gate 	mutex_exit(&element->list_mutex);
10167c478bd9Sstevel@tonic-gate }
10177c478bd9Sstevel@tonic-gate 
10187c478bd9Sstevel@tonic-gate 
10197c478bd9Sstevel@tonic-gate void
10207c478bd9Sstevel@tonic-gate usba_destroy_list(usba_list_entry_t *head)
10217c478bd9Sstevel@tonic-gate {
10227c478bd9Sstevel@tonic-gate 	mutex_enter(&head->list_mutex);
10237c478bd9Sstevel@tonic-gate 	ASSERT(head->next == NULL);
10247c478bd9Sstevel@tonic-gate 	ASSERT(head->prev == NULL);
10257c478bd9Sstevel@tonic-gate 	mutex_exit(&head->list_mutex);
10267c478bd9Sstevel@tonic-gate 
10277c478bd9Sstevel@tonic-gate 	mutex_destroy(&head->list_mutex);
10287c478bd9Sstevel@tonic-gate }
10297c478bd9Sstevel@tonic-gate 
10307c478bd9Sstevel@tonic-gate 
10317c478bd9Sstevel@tonic-gate void
10327c478bd9Sstevel@tonic-gate usba_add_to_list(usba_list_entry_t *head, usba_list_entry_t *element)
10337c478bd9Sstevel@tonic-gate {
10347c478bd9Sstevel@tonic-gate 	usba_list_entry_t *next;
10357c478bd9Sstevel@tonic-gate 	int		remaining;
10367c478bd9Sstevel@tonic-gate 
10377c478bd9Sstevel@tonic-gate 	mutex_enter(&head->list_mutex);
10387c478bd9Sstevel@tonic-gate 	mutex_enter(&element->list_mutex);
10397c478bd9Sstevel@tonic-gate 
10407c478bd9Sstevel@tonic-gate 	remaining = head->count;
10417c478bd9Sstevel@tonic-gate 
10427c478bd9Sstevel@tonic-gate 	/* check if it is not in another list */
10437c478bd9Sstevel@tonic-gate 	ASSERT(element->next == NULL);
10447c478bd9Sstevel@tonic-gate 	ASSERT(element->prev == NULL);
10457c478bd9Sstevel@tonic-gate 
10467c478bd9Sstevel@tonic-gate #ifdef DEBUG
10477c478bd9Sstevel@tonic-gate 	/*
10487c478bd9Sstevel@tonic-gate 	 * only verify the list when not in interrupt context, we
10497c478bd9Sstevel@tonic-gate 	 * have to trust the HCD
10507c478bd9Sstevel@tonic-gate 	 */
10517c478bd9Sstevel@tonic-gate 	if (!servicing_interrupt()) {
10527c478bd9Sstevel@tonic-gate 
10537c478bd9Sstevel@tonic-gate 		/* check if not already in this list */
10547c478bd9Sstevel@tonic-gate 		for (next = head->next; (next != NULL);
10557c478bd9Sstevel@tonic-gate 		    next = next->next) {
10567c478bd9Sstevel@tonic-gate 			if (next == element) {
10577c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L0(DPRINT_MASK_USBA,
10587c478bd9Sstevel@tonic-gate 				    usba_log_handle,
10597c478bd9Sstevel@tonic-gate 				    "Attempt to corrupt USB list at 0x%p",
10607c478bd9Sstevel@tonic-gate 				    (void *)head);
10617c478bd9Sstevel@tonic-gate 				ASSERT(next == element);
10627c478bd9Sstevel@tonic-gate 
10637c478bd9Sstevel@tonic-gate 				goto done;
10647c478bd9Sstevel@tonic-gate 			}
10657c478bd9Sstevel@tonic-gate 			remaining--;
10667c478bd9Sstevel@tonic-gate 
10677c478bd9Sstevel@tonic-gate 			/*
10687c478bd9Sstevel@tonic-gate 			 * Detect incorrect circ links or found
10697c478bd9Sstevel@tonic-gate 			 * unexpected elements.
10707c478bd9Sstevel@tonic-gate 			 */
10717c478bd9Sstevel@tonic-gate 			if ((next->next && (remaining == 0)) ||
10727c478bd9Sstevel@tonic-gate 			    ((next->next == NULL) && remaining)) {
10737c478bd9Sstevel@tonic-gate 				panic("Corrupted USB list at 0x%p",
10747c478bd9Sstevel@tonic-gate 				    (void *)head);
10757c478bd9Sstevel@tonic-gate 				/*NOTREACHED*/
10767c478bd9Sstevel@tonic-gate 			}
10777c478bd9Sstevel@tonic-gate 		}
10787c478bd9Sstevel@tonic-gate 	}
10797c478bd9Sstevel@tonic-gate #endif
10807c478bd9Sstevel@tonic-gate 
10817c478bd9Sstevel@tonic-gate 	if (head->next == NULL) {
10827c478bd9Sstevel@tonic-gate 		head->prev = head->next = element;
10837c478bd9Sstevel@tonic-gate 	} else {
10847c478bd9Sstevel@tonic-gate 		/* add to tail */
10857c478bd9Sstevel@tonic-gate 		head->prev->next = element;
10867c478bd9Sstevel@tonic-gate 		element->prev = head->prev;
10877c478bd9Sstevel@tonic-gate 		head->prev = element;
10887c478bd9Sstevel@tonic-gate 	}
10897c478bd9Sstevel@tonic-gate 
10907c478bd9Sstevel@tonic-gate 	head->count++;
10917c478bd9Sstevel@tonic-gate 
10927c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_USBA, usba_log_handle,
10937c478bd9Sstevel@tonic-gate 	    "usba_add_to_list: head=0x%p element=0x%p count=%d",
1094112116d8Sfb 	    (void *)head, (void *)element, head->count);
10957c478bd9Sstevel@tonic-gate 
10967c478bd9Sstevel@tonic-gate done:
10977c478bd9Sstevel@tonic-gate 	mutex_exit(&head->list_mutex);
10987c478bd9Sstevel@tonic-gate 	mutex_exit(&element->list_mutex);
10997c478bd9Sstevel@tonic-gate }
11007c478bd9Sstevel@tonic-gate 
11017c478bd9Sstevel@tonic-gate 
11027c478bd9Sstevel@tonic-gate int
11037c478bd9Sstevel@tonic-gate usba_rm_from_list(usba_list_entry_t *head, usba_list_entry_t *element)
11047c478bd9Sstevel@tonic-gate {
11057c478bd9Sstevel@tonic-gate 	usba_list_entry_t *e;
11067c478bd9Sstevel@tonic-gate 	int		found = 0;
11077c478bd9Sstevel@tonic-gate 	int		remaining;
11087c478bd9Sstevel@tonic-gate 
11097c478bd9Sstevel@tonic-gate 	/* find the element in the list first */
11107c478bd9Sstevel@tonic-gate 	mutex_enter(&head->list_mutex);
11117c478bd9Sstevel@tonic-gate 
11127c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_USBA, usba_log_handle,
11137c478bd9Sstevel@tonic-gate 	    "usba_rm_from_list: head=0x%p element=0x%p count=%d",
1114112116d8Sfb 	    (void *)head, (void *)element, head->count);
11157c478bd9Sstevel@tonic-gate 
11167c478bd9Sstevel@tonic-gate 	remaining = head->count;
11177c478bd9Sstevel@tonic-gate 	e = head->next;
11187c478bd9Sstevel@tonic-gate 
11197c478bd9Sstevel@tonic-gate 	while (e) {
11207c478bd9Sstevel@tonic-gate 		if (e == element) {
11217c478bd9Sstevel@tonic-gate 			found++;
11227c478bd9Sstevel@tonic-gate 			break;
11237c478bd9Sstevel@tonic-gate 		}
11247c478bd9Sstevel@tonic-gate 		e = e->next;
11257c478bd9Sstevel@tonic-gate 
11267c478bd9Sstevel@tonic-gate 		remaining--;
11277c478bd9Sstevel@tonic-gate 
11287c478bd9Sstevel@tonic-gate 		/* Detect incorrect circ links or found unexpected elements. */
11297c478bd9Sstevel@tonic-gate 		if ((e && (remaining == 0)) ||
11307c478bd9Sstevel@tonic-gate 		    ((e == NULL) && (remaining))) {
11317c478bd9Sstevel@tonic-gate 			panic("Corrupted USB list at 0x%p", (void *)head);
11327c478bd9Sstevel@tonic-gate 			/*NOTREACHED*/
11337c478bd9Sstevel@tonic-gate 		}
11347c478bd9Sstevel@tonic-gate 	}
11357c478bd9Sstevel@tonic-gate 
11367c478bd9Sstevel@tonic-gate 	if (!found) {
11377c478bd9Sstevel@tonic-gate 		mutex_exit(&head->list_mutex);
11387c478bd9Sstevel@tonic-gate 
11397c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
11407c478bd9Sstevel@tonic-gate 	}
11417c478bd9Sstevel@tonic-gate 
11427c478bd9Sstevel@tonic-gate 	/* now remove the element */
11437c478bd9Sstevel@tonic-gate 	mutex_enter(&element->list_mutex);
11447c478bd9Sstevel@tonic-gate 
11457c478bd9Sstevel@tonic-gate 	if (element->next) {
11467c478bd9Sstevel@tonic-gate 		element->next->prev = element->prev;
11477c478bd9Sstevel@tonic-gate 	}
11487c478bd9Sstevel@tonic-gate 	if (element->prev) {
11497c478bd9Sstevel@tonic-gate 		element->prev->next = element->next;
11507c478bd9Sstevel@tonic-gate 	}
11517c478bd9Sstevel@tonic-gate 	if (head->next == element) {
1152