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
20cbab2b26Slg  */
21cbab2b26Slg /*
22ff0e937bSRaymond Chen  * 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>
26*0d2006e4SRobert Mustacchi  * Copyright 2019, Joyent, Inc.
277c478bd9Sstevel@tonic-gate  */
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate /*
307c478bd9Sstevel@tonic-gate  * USBA: Solaris USB Architecture support
317c478bd9Sstevel@tonic-gate  *
327c478bd9Sstevel@tonic-gate  * This module builds a tree of parsed USB standard descriptors and unparsed
337c478bd9Sstevel@tonic-gate  * Class/Vendor specific (C/V) descriptors.  Routines are grouped into three
347c478bd9Sstevel@tonic-gate  * groups: those which build the tree, those which take it down, and those which
357c478bd9Sstevel@tonic-gate  * dump it.
367c478bd9Sstevel@tonic-gate  *
377c478bd9Sstevel@tonic-gate  * The tree built hangs off of the dev_cfg field of the usb_client_dev_data_t
387c478bd9Sstevel@tonic-gate  * structure returned by usb_get_dev_data().  The tree consists of different
397c478bd9Sstevel@tonic-gate  * kinds of tree nodes (usb_xxx_data_t) each containing a standard USB
407c478bd9Sstevel@tonic-gate  * descriptor (usb_xxx_descr_t) and pointers to arrays of other nodes.
417c478bd9Sstevel@tonic-gate  *
427c478bd9Sstevel@tonic-gate  * Arrays are dynamically sized, as the descriptors coming from the device may
437c478bd9Sstevel@tonic-gate  * lie, but the number of descriptors from the device is a more reliable
447c478bd9Sstevel@tonic-gate  * indicator of configuration.	This makes the code more robust.  After the raw
457c478bd9Sstevel@tonic-gate  * descriptor data has been parsed into a non-sparse tree, the tree is ordered
467c478bd9Sstevel@tonic-gate  * and made sparse with a bin-sort style algorithm.
477c478bd9Sstevel@tonic-gate  *
487c478bd9Sstevel@tonic-gate  * dev_cfg is an array of configuration tree nodes. Each contains space for one
497c478bd9Sstevel@tonic-gate  * parsed standard USB configuration descriptor, a pointer to an array of c/v
507c478bd9Sstevel@tonic-gate  * tree nodes and a pointer to an array of interface tree nodes.
517c478bd9Sstevel@tonic-gate  *
527c478bd9Sstevel@tonic-gate  * Each interface tree node represents a group of interface descriptors, called
537c478bd9Sstevel@tonic-gate  * alternates, with the same interface number.	Thus, each interface tree node
547c478bd9Sstevel@tonic-gate  * has a pointer to an array of alternate-interface tree nodes each containing a
557c478bd9Sstevel@tonic-gate  * standard USB interface descriptor. Alternate-interface tree nodes also
567c478bd9Sstevel@tonic-gate  * contain a pointer to an array of c/v tree nodes and a pointer to an array of
577c478bd9Sstevel@tonic-gate  * endpoint tree nodes.
587c478bd9Sstevel@tonic-gate  *
597c478bd9Sstevel@tonic-gate  * Endpoint tree nodes contain a standard endpoint descriptor, plus a pointer to
607c478bd9Sstevel@tonic-gate  * an array of c/v tree nodes.
617c478bd9Sstevel@tonic-gate  *
627c478bd9Sstevel@tonic-gate  * Each array in the tree contains elements ranging from 0 to the largest key
637c478bd9Sstevel@tonic-gate  * value of it's elements.  Endpoints are a special case.  The direction bit is
647c478bd9Sstevel@tonic-gate  * right shifted over three unused bits before the index is determined, leaving
657c478bd9Sstevel@tonic-gate  * a range of 0..31 instead of a sparsely-populated range of 0..255.
667c478bd9Sstevel@tonic-gate  *
677c478bd9Sstevel@tonic-gate  * The indices of tree elements coincide with their USB key values.  For
687c478bd9Sstevel@tonic-gate  * example, standard USB devices have no configuration 0;  if they have one
697c478bd9Sstevel@tonic-gate  * configuration it is #1.  dev_cfg[0] is zeroed out;  dev_cfg[1] is the root
707c478bd9Sstevel@tonic-gate  * of configuration #1.
717c478bd9Sstevel@tonic-gate  *
727c478bd9Sstevel@tonic-gate  * The idea here is for a driver to be able to parse the tree to easily find a
737c478bd9Sstevel@tonic-gate  * desired descriptor.	For example, the interval of endpoint 2, alternate 3,
747c478bd9Sstevel@tonic-gate  * interface 1, configuration 1 would be:
757c478bd9Sstevel@tonic-gate  *  dv->dev_cfg[1].cfg_if[1].if_alt[3].altif_ep[2].ep_descr.bInterval
767c478bd9Sstevel@tonic-gate  *
777c478bd9Sstevel@tonic-gate  * How the tree is built:
787c478bd9Sstevel@tonic-gate  *
797c478bd9Sstevel@tonic-gate  * usb_build_descr_tree() is responsible for the whole process.
807c478bd9Sstevel@tonic-gate  *
817c478bd9Sstevel@tonic-gate  * Next, usba_build_descr_tree() coordinates parsing this byte stream,
827c478bd9Sstevel@tonic-gate  * descriptor by descriptor.  usba_build_descr_tree() calls the appropriate
837c478bd9Sstevel@tonic-gate  * usba_process_xx_descr() function to interpret and install each descriptor in
847c478bd9Sstevel@tonic-gate  * the tree, based on the descriptor's type.  When done with this phase, a
857c478bd9Sstevel@tonic-gate  * non-sparse tree exists containing tree nodes with descriptors in the order
867c478bd9Sstevel@tonic-gate  * they were found in the raw data.
877c478bd9Sstevel@tonic-gate  *
887c478bd9Sstevel@tonic-gate  * All levels of the tree, except alternates, remain non-sparse.  Alternates are
897c478bd9Sstevel@tonic-gate  * moved, possibly, within their array, so that descriptors are indexed by their
907c478bd9Sstevel@tonic-gate  * alternate ID.
917c478bd9Sstevel@tonic-gate  *
927c478bd9Sstevel@tonic-gate  * The usba_reg_state_t structure maintains state of the tree-building process,
937c478bd9Sstevel@tonic-gate  * helping coordinate all routines involved.
947c478bd9Sstevel@tonic-gate  */
957c478bd9Sstevel@tonic-gate #define	USBA_FRAMEWORK
967c478bd9Sstevel@tonic-gate #include <sys/usb/usba.h>
977c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usba_impl.h>
987c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usba_private.h>
997c478bd9Sstevel@tonic-gate #include <sys/usb/usba/hcdi_impl.h>
1007c478bd9Sstevel@tonic-gate #include <sys/usb/hubd/hub.h>
1017c478bd9Sstevel@tonic-gate 
1027c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usbai_register_impl.h>
1037c478bd9Sstevel@tonic-gate 
1047c478bd9Sstevel@tonic-gate /*
1057c478bd9Sstevel@tonic-gate  * Header needed for use by this module only.
1067c478bd9Sstevel@tonic-gate  * However, function may be used in V0.8 drivers so needs to be global.
1077c478bd9Sstevel@tonic-gate  */
1087c478bd9Sstevel@tonic-gate int usb_log_descr_tree(usb_client_dev_data_t *, usb_log_handle_t,
1097c478bd9Sstevel@tonic-gate 				uint_t, uint_t);
1107c478bd9Sstevel@tonic-gate 
1117c478bd9Sstevel@tonic-gate /* Debug stuff */
1127c478bd9Sstevel@tonic-gate usb_log_handle_t	usbai_reg_log_handle;
1134610e4a0Sfrits uint_t			usbai_register_errlevel = USB_LOG_L2;
1144610e4a0Sfrits uint_t			usbai_register_dump_errlevel = USB_LOG_L2;
1154610e4a0Sfrits uint_t			usbai_register_errmask = (uint_t)-1;
1167c478bd9Sstevel@tonic-gate 
1177c478bd9Sstevel@tonic-gate /* Function prototypes */
1187c478bd9Sstevel@tonic-gate static int usba_build_descr_tree(dev_info_t *, usba_device_t *,
1197c478bd9Sstevel@tonic-gate 				usb_client_dev_data_t *);
1207c478bd9Sstevel@tonic-gate static void usba_process_cfg_descr(usba_reg_state_t *);
1217c478bd9Sstevel@tonic-gate static int usba_process_if_descr(usba_reg_state_t *, boolean_t *);
1227c478bd9Sstevel@tonic-gate static int usba_process_ep_descr(usba_reg_state_t *);
123993e3fafSRobert Mustacchi static int usba_process_ss_ep_comp_descr(usba_reg_state_t *);
1247c478bd9Sstevel@tonic-gate static int usba_process_cv_descr(usba_reg_state_t *);
1257c478bd9Sstevel@tonic-gate static int usba_set_parse_values(dev_info_t *dip, usba_device_t *usba_device,
1267c478bd9Sstevel@tonic-gate     usba_reg_state_t *state);
1277c478bd9Sstevel@tonic-gate static void* usba_kmem_realloc(void *, int, int);
1287c478bd9Sstevel@tonic-gate static void usba_augment_array(void **, uint_t, uint_t);
1297c478bd9Sstevel@tonic-gate static void usba_make_alts_sparse(usb_alt_if_data_t **, uint_t *);
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate static void usba_order_tree(usba_reg_state_t *);
1327c478bd9Sstevel@tonic-gate 
1337c478bd9Sstevel@tonic-gate static void usba_free_if_array(usb_if_data_t *, uint_t);
1347c478bd9Sstevel@tonic-gate static void usba_free_ep_array(usb_ep_data_t *, uint_t);
1357c478bd9Sstevel@tonic-gate static void usba_free_cv_array(usb_cvs_data_t *, uint_t);
1367c478bd9Sstevel@tonic-gate 
1377c478bd9Sstevel@tonic-gate static int usba_dump_descr_tree(dev_info_t *, usb_client_dev_data_t *,
1387c478bd9Sstevel@tonic-gate 				usb_log_handle_t, uint_t, uint_t);
1397c478bd9Sstevel@tonic-gate static void usba_dump_if(usb_if_data_t *, usb_log_handle_t,
1407c478bd9Sstevel@tonic-gate 				uint_t, uint_t, char *);
1417c478bd9Sstevel@tonic-gate static void usba_dump_ep(uint_t, usb_ep_data_t *, usb_log_handle_t, uint_t,
1427c478bd9Sstevel@tonic-gate 				uint_t, char *);
1437c478bd9Sstevel@tonic-gate static void usba_dump_cv(usb_cvs_data_t *, usb_log_handle_t, uint_t, uint_t,
1447c478bd9Sstevel@tonic-gate 				char *, int);
1457c478bd9Sstevel@tonic-gate static void usba_dump_bin(uint8_t *, int, int, usb_log_handle_t,
1467c478bd9Sstevel@tonic-gate 				uint_t,  uint_t, char *, int);
1477c478bd9Sstevel@tonic-gate 
1487c478bd9Sstevel@tonic-gate /* Framework initialization. */
1497c478bd9Sstevel@tonic-gate void
usba_usbai_register_initialization()1507c478bd9Sstevel@tonic-gate usba_usbai_register_initialization()
1517c478bd9Sstevel@tonic-gate {
1527c478bd9Sstevel@tonic-gate 	usbai_reg_log_handle = usb_alloc_log_hdl(NULL, "usbreg",
153112116d8Sfb 	    &usbai_register_errlevel,
154112116d8Sfb 	    &usbai_register_errmask, NULL,
155112116d8Sfb 	    0);
1567c478bd9Sstevel@tonic-gate 
1577c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
158112116d8Sfb 	    "usba_usbai_register_initialization");
1597c478bd9Sstevel@tonic-gate }
1607c478bd9Sstevel@tonic-gate 
1617c478bd9Sstevel@tonic-gate 
1627c478bd9Sstevel@tonic-gate /* Framework destruction. */
1637c478bd9Sstevel@tonic-gate void
usba_usbai_register_destroy()1647c478bd9Sstevel@tonic-gate usba_usbai_register_destroy()
1657c478bd9Sstevel@tonic-gate {
1667c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
1677c478bd9Sstevel@tonic-gate 	    "usba_usbai_register destroy");
1687c478bd9Sstevel@tonic-gate 
1697c478bd9Sstevel@tonic-gate 	usb_free_log_hdl(usbai_reg_log_handle);
1707c478bd9Sstevel@tonic-gate }
1717c478bd9Sstevel@tonic-gate 
1727c478bd9Sstevel@tonic-gate 
1737c478bd9Sstevel@tonic-gate /*
1747c478bd9Sstevel@tonic-gate  * usb_client_attach:
1757c478bd9Sstevel@tonic-gate  *
1767c478bd9Sstevel@tonic-gate  * Arguments:
1777c478bd9Sstevel@tonic-gate  *	dip		- pointer to devinfo node of the client
1787c478bd9Sstevel@tonic-gate  *	version 	- USBA registration version number
1797c478bd9Sstevel@tonic-gate  *	flags		- None used
1807c478bd9Sstevel@tonic-gate  *
1817c478bd9Sstevel@tonic-gate  * Return Values:
1827c478bd9Sstevel@tonic-gate  *	USB_SUCCESS		- attach succeeded
1837c478bd9Sstevel@tonic-gate  *	USB_INVALID_ARGS	- received null dip
1847c478bd9Sstevel@tonic-gate  *	USB_INVALID_VERSION	- version argument is incorrect.
1857c478bd9Sstevel@tonic-gate  *	USB_FAILURE		- other internal failure
1867c478bd9Sstevel@tonic-gate  */
1877c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1887c478bd9Sstevel@tonic-gate int
usb_client_attach(dev_info_t * dip,uint_t version,usb_flags_t flags)1897c478bd9Sstevel@tonic-gate usb_client_attach(dev_info_t *dip, uint_t version, usb_flags_t flags)
1907c478bd9Sstevel@tonic-gate {
1917c478bd9Sstevel@tonic-gate 	int rval;
1927c478bd9Sstevel@tonic-gate 	usba_device_t *usba_device;
1937c478bd9Sstevel@tonic-gate 
1947c478bd9Sstevel@tonic-gate 	if (dip == NULL) {
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate 		return (USB_INVALID_ARGS);
1977c478bd9Sstevel@tonic-gate 	}
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
2007c478bd9Sstevel@tonic-gate 	    "usb_client attach:");
2017c478bd9Sstevel@tonic-gate 
2027c478bd9Sstevel@tonic-gate 	usba_device = usba_get_usba_device(dip);
2037c478bd9Sstevel@tonic-gate 
2047c478bd9Sstevel@tonic-gate 	/*
2057c478bd9Sstevel@tonic-gate 	 * Allow exact match for legacy (DDK 0.8/9) drivers, or same major
2067c478bd9Sstevel@tonic-gate 	 * VERSion and smaller or same minor version for non-legacy drivers.
2077c478bd9Sstevel@tonic-gate 	 */
2087c478bd9Sstevel@tonic-gate 	if ((version !=
2097c478bd9Sstevel@tonic-gate 	    USBA_MAKE_VER(USBA_LEG_MAJOR_VER, USBA_LEG_MINOR_VER)) &&
2107c478bd9Sstevel@tonic-gate 	    ((USBA_GET_MAJOR(version) != USBA_MAJOR_VER) ||
2117c478bd9Sstevel@tonic-gate 	    (USBA_GET_MINOR(version) > USBA_MINOR_VER))) {
2127c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L1(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
2137c478bd9Sstevel@tonic-gate 		    "Incorrect USB driver version for %s%d: found: %d.%d, "
2147c478bd9Sstevel@tonic-gate 		    "expecting %d.%d",
2157c478bd9Sstevel@tonic-gate 		    ddi_driver_name(dip), ddi_get_instance(dip),
2167c478bd9Sstevel@tonic-gate 		    USBA_GET_MAJOR(version), USBA_GET_MINOR(version),
2177c478bd9Sstevel@tonic-gate 		    USBA_MAJOR_VER, USBA_MINOR_VER);
2187c478bd9Sstevel@tonic-gate 
2197c478bd9Sstevel@tonic-gate 		return (USB_INVALID_VERSION);
2207c478bd9Sstevel@tonic-gate 	}
2217c478bd9Sstevel@tonic-gate 
2227c478bd9Sstevel@tonic-gate 	if (version == USBA_MAKE_VER(USBA_LEG_MAJOR_VER, USBA_LEG_MINOR_VER)) {
223d291d9f2Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
2247c478bd9Sstevel@tonic-gate 		    "Accepting legacy USB driver version %d.%d for %s%d",
2257c478bd9Sstevel@tonic-gate 		    USBA_LEG_MAJOR_VER, USBA_LEG_MINOR_VER,
2267c478bd9Sstevel@tonic-gate 		    ddi_driver_name(dip), ddi_get_instance(dip));
2277c478bd9Sstevel@tonic-gate 	}
2287c478bd9Sstevel@tonic-gate 
2297c478bd9Sstevel@tonic-gate 	rval = ndi_prop_update_int(DDI_DEV_T_NONE, dip, "driver-major",
230112116d8Sfb 	    USBA_GET_MAJOR(version));
2317c478bd9Sstevel@tonic-gate 	if (rval != DDI_PROP_SUCCESS) {
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
2347c478bd9Sstevel@tonic-gate 	}
2357c478bd9Sstevel@tonic-gate 	rval = ndi_prop_update_int(DDI_DEV_T_NONE, dip, "driver-minor",
236112116d8Sfb 	    USBA_GET_MINOR(version));
2377c478bd9Sstevel@tonic-gate 	if (rval != DDI_PROP_SUCCESS) {
2387c478bd9Sstevel@tonic-gate 
2397c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
2407c478bd9Sstevel@tonic-gate 	}
2417c478bd9Sstevel@tonic-gate 
2427c478bd9Sstevel@tonic-gate 	mutex_enter(&usba_device->usb_mutex);
2437c478bd9Sstevel@tonic-gate 	if (strcmp(ddi_driver_name(dip), "usb_mid") != 0) {
2447c478bd9Sstevel@tonic-gate 		usba_device->usb_client_flags[usba_get_ifno(dip)] |=
245112116d8Sfb 		    USBA_CLIENT_FLAG_ATTACH;
2467c478bd9Sstevel@tonic-gate 		usba_device->usb_client_attach_list->dip = dip;
2477c478bd9Sstevel@tonic-gate 	}
2487c478bd9Sstevel@tonic-gate 	mutex_exit(&usba_device->usb_mutex);
2497c478bd9Sstevel@tonic-gate 
2507c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
2517c478bd9Sstevel@tonic-gate 	    "usb_client attach: done");
2527c478bd9Sstevel@tonic-gate 
2537c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
2547c478bd9Sstevel@tonic-gate }
2557c478bd9Sstevel@tonic-gate 
2567c478bd9Sstevel@tonic-gate 
2577c478bd9Sstevel@tonic-gate /*
2587c478bd9Sstevel@tonic-gate  * usb_client_detach:
2597c478bd9Sstevel@tonic-gate  *	free dev_data is reg != NULL, not much else to do
2607c478bd9Sstevel@tonic-gate  *
2617c478bd9Sstevel@tonic-gate  * Arguments:
2627c478bd9Sstevel@tonic-gate  *	dip		- pointer to devinfo node of the client
2637c478bd9Sstevel@tonic-gate  *	reg		- return registration data at this address
2647c478bd9Sstevel@tonic-gate  */
2657c478bd9Sstevel@tonic-gate void
usb_client_detach(dev_info_t * dip,usb_client_dev_data_t * reg)2667c478bd9Sstevel@tonic-gate usb_client_detach(dev_info_t *dip, usb_client_dev_data_t *reg)
2677c478bd9Sstevel@tonic-gate {
2687c478bd9Sstevel@tonic-gate 	usba_device_t *usba_device = usba_get_usba_device(dip);
2697c478bd9Sstevel@tonic-gate 
2707c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
2717c478bd9Sstevel@tonic-gate 	    "usb_client_detach:");
2727c478bd9Sstevel@tonic-gate 
2737c478bd9Sstevel@tonic-gate 	if (dip) {
2747c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
2757c478bd9Sstevel@tonic-gate 		    "Unregistering usb client %s%d: reg=0x%p",
276112116d8Sfb 		    ddi_driver_name(dip), ddi_get_instance(dip), (void *)reg);
2777c478bd9Sstevel@tonic-gate 
2787c478bd9Sstevel@tonic-gate 		usb_free_dev_data(dip, reg);
2797c478bd9Sstevel@tonic-gate 
2807c478bd9Sstevel@tonic-gate 		mutex_enter(&usba_device->usb_mutex);
2817c478bd9Sstevel@tonic-gate 		if (strcmp(ddi_driver_name(dip), "usb_mid") != 0) {
2827c478bd9Sstevel@tonic-gate 			usba_device->usb_client_flags[usba_get_ifno(dip)] &=
283112116d8Sfb 			    ~USBA_CLIENT_FLAG_ATTACH;
2847c478bd9Sstevel@tonic-gate 		}
2857c478bd9Sstevel@tonic-gate 		mutex_exit(&usba_device->usb_mutex);
2867c478bd9Sstevel@tonic-gate 	}
2877c478bd9Sstevel@tonic-gate 
2887c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
2897c478bd9Sstevel@tonic-gate 	    "usb_client_detach done");
2907c478bd9Sstevel@tonic-gate }
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate 
2937c478bd9Sstevel@tonic-gate /*
2947c478bd9Sstevel@tonic-gate  * usb_register_client (deprecated):
2957c478bd9Sstevel@tonic-gate  *	The client registers with USBA during attach.
2967c478bd9Sstevel@tonic-gate  */
2977c478bd9Sstevel@tonic-gate /*ARGSUSED*/
2987c478bd9Sstevel@tonic-gate int
usb_register_client(dev_info_t * dip,uint_t version,usb_client_dev_data_t ** reg,usb_reg_parse_lvl_t parse_level,usb_flags_t flags)2997c478bd9Sstevel@tonic-gate usb_register_client(dev_info_t *dip, uint_t version,
3007c478bd9Sstevel@tonic-gate     usb_client_dev_data_t **reg, usb_reg_parse_lvl_t parse_level,
3017c478bd9Sstevel@tonic-gate     usb_flags_t flags)
3027c478bd9Sstevel@tonic-gate {
3037c478bd9Sstevel@tonic-gate 	int rval = usb_client_attach(dip, version, flags);
3047c478bd9Sstevel@tonic-gate 
3057c478bd9Sstevel@tonic-gate 	if (rval == USB_SUCCESS) {
3067c478bd9Sstevel@tonic-gate 		rval = usb_get_dev_data(dip, reg, parse_level, flags);
3077c478bd9Sstevel@tonic-gate 
3087c478bd9Sstevel@tonic-gate 		if (rval != USB_SUCCESS) {
3097c478bd9Sstevel@tonic-gate 			usb_client_detach(dip, NULL);
3107c478bd9Sstevel@tonic-gate 		}
3117c478bd9Sstevel@tonic-gate 	}
3127c478bd9Sstevel@tonic-gate 
3137c478bd9Sstevel@tonic-gate 	return (rval);
3147c478bd9Sstevel@tonic-gate }
3157c478bd9Sstevel@tonic-gate 
3167c478bd9Sstevel@tonic-gate 
3177c478bd9Sstevel@tonic-gate /*
3187c478bd9Sstevel@tonic-gate  * usb_unregister_client (deprecated):
3197c478bd9Sstevel@tonic-gate  *	Undo the makings of usb_get_dev_data().  Free memory if allocated.
3207c478bd9Sstevel@tonic-gate  *
3217c478bd9Sstevel@tonic-gate  * Arguments:
3227c478bd9Sstevel@tonic-gate  *	dip	- pointer to devinfo node of the client
3237c478bd9Sstevel@tonic-gate  *	reg	- pointer to registration data to be freed
3247c478bd9Sstevel@tonic-gate  */
3257c478bd9Sstevel@tonic-gate void
usb_unregister_client(dev_info_t * dip,usb_client_dev_data_t * reg)3267c478bd9Sstevel@tonic-gate usb_unregister_client(dev_info_t *dip, usb_client_dev_data_t *reg)
3277c478bd9Sstevel@tonic-gate {
3287c478bd9Sstevel@tonic-gate 	usb_client_detach(dip, reg);
3297c478bd9Sstevel@tonic-gate }
3307c478bd9Sstevel@tonic-gate 
3317c478bd9Sstevel@tonic-gate 
3327c478bd9Sstevel@tonic-gate /*
3337c478bd9Sstevel@tonic-gate  * usb_get_dev_data:
3347c478bd9Sstevel@tonic-gate  *	On completion, the registration data has been initialized.
3357c478bd9Sstevel@tonic-gate  *	Most data items are straightforward.
3367c478bd9Sstevel@tonic-gate  *	Among the items returned in the data is the tree of
3377c478bd9Sstevel@tonic-gate  *	parsed descriptors, in dev_cfg;	 the number of configurations parsed,
3387c478bd9Sstevel@tonic-gate  *	in dev_n_cfg; a pointer to the current configuration in the tree,
3397c478bd9Sstevel@tonic-gate  *	in dev_curr_cfg; the index of the first valid interface in the
3407c478bd9Sstevel@tonic-gate  *	tree, in dev_curr_if, and a parse level that accurately reflects what
3417c478bd9Sstevel@tonic-gate  *	is in the tree, in dev_parse_level.
3427c478bd9Sstevel@tonic-gate  *
3437c478bd9Sstevel@tonic-gate  *	This routine sets up directly-initialized fields, and calls
3447c478bd9Sstevel@tonic-gate  *	usb_build_descr_tree() to parse the raw descriptors and initialize the
3457c478bd9Sstevel@tonic-gate  *	tree.
3467c478bd9Sstevel@tonic-gate  *
3477c478bd9Sstevel@tonic-gate  *	Parse_level determines the extent to which the tree is built.  It has
3487c478bd9Sstevel@tonic-gate  *	the following values:
3497c478bd9Sstevel@tonic-gate  *
3507c478bd9Sstevel@tonic-gate  *	USB_PARSE_LVL_NONE - Build no tree.  dev_n_cfg will return 0, dev_cfg
3517c478bd9Sstevel@tonic-gate  *			     and dev_curr_cfg will return NULL.
3527c478bd9Sstevel@tonic-gate  *	USB_PARSE_LVL_IF   - Parse configured interface only, if configuration#
3537c478bd9Sstevel@tonic-gate  *			     and interface properties are set (as when different
3547c478bd9Sstevel@tonic-gate  *			     interfaces are viewed by the OS as different device
3557c478bd9Sstevel@tonic-gate  *			     instances). If an OS device instance is set up to
3567c478bd9Sstevel@tonic-gate  *			     represent an entire physical device, this works
3577c478bd9Sstevel@tonic-gate  *			     like USB_PARSE_LVL_ALL.
3587c478bd9Sstevel@tonic-gate  *	USB_PARSE_LVL_CFG  - Parse entire configuration of configured interface
3597c478bd9Sstevel@tonic-gate  *			     only.  This is like USB_PARSE_LVL_IF except entire
3607c478bd9Sstevel@tonic-gate  *			     configuration is returned.
3617c478bd9Sstevel@tonic-gate  *	USB_PARSE_LVL_ALL  - Parse entire device (all configurations), even
3627c478bd9Sstevel@tonic-gate  *			     when driver is bound to a single interface of a
3637c478bd9Sstevel@tonic-gate  *			     single configuration.
3647c478bd9Sstevel@tonic-gate  *
3657c478bd9Sstevel@tonic-gate  *	No tree is built for root hubs, regardless of parse_level.
3667c478bd9Sstevel@tonic-gate  *
3677c478bd9Sstevel@tonic-gate  * Arguments:
3687c478bd9Sstevel@tonic-gate  *	dip		- pointer to devinfo node of the client
3697c478bd9Sstevel@tonic-gate  *	version		- USBA registration version number
3707c478bd9Sstevel@tonic-gate  *	reg		- return registration data at this address
3717c478bd9Sstevel@tonic-gate  *	parse_level	- See above
3727c478bd9Sstevel@tonic-gate  *	flags		- None used
3737c478bd9Sstevel@tonic-gate  *
3747c478bd9Sstevel@tonic-gate  * Return Values:
3757c478bd9Sstevel@tonic-gate  *	USB_SUCCESS		- usb_get_dev_data succeeded
3767c478bd9Sstevel@tonic-gate  *	USB_INVALID_ARGS	- received null dip or reg argument
3777c478bd9Sstevel@tonic-gate  *	USB_INVALID_CONTEXT	- called from callback context
3787c478bd9Sstevel@tonic-gate  *	USB_FAILURE		- bad descriptor info or other internal failure
3797c478bd9Sstevel@tonic-gate  *
3807c478bd9Sstevel@tonic-gate  * Note: The non-standard USB descriptors are returned in RAW format.
3817c478bd9Sstevel@tonic-gate  *	returns initialized registration data.	Most data items are clear.
3827c478bd9Sstevel@tonic-gate  *	Among the items returned is the tree of parsed descriptors in dev_cfg;
3837c478bd9Sstevel@tonic-gate  *	and the number of configurations parsed in dev_n_cfg.
3847c478bd9Sstevel@tonic-gate  *
3857c478bd9Sstevel@tonic-gate  *	The registration data is not shared. each client receives its own
3867c478bd9Sstevel@tonic-gate  *	copy.
3877c478bd9Sstevel@tonic-gate  */
3887c478bd9Sstevel@tonic-gate /*ARGSUSED*/
3897c478bd9Sstevel@tonic-gate int
usb_get_dev_data(dev_info_t * dip,usb_client_dev_data_t ** reg,usb_reg_parse_lvl_t parse_level,usb_flags_t flags)3907c478bd9Sstevel@tonic-gate usb_get_dev_data(dev_info_t *dip,
3917c478bd9Sstevel@tonic-gate     usb_client_dev_data_t **reg, usb_reg_parse_lvl_t parse_level,
3927c478bd9Sstevel@tonic-gate     usb_flags_t flags)
3937c478bd9Sstevel@tonic-gate {
3947c478bd9Sstevel@tonic-gate 	usb_client_dev_data_t	*usb_reg = NULL;
3957c478bd9Sstevel@tonic-gate 	char			*tmpbuf = NULL;
3967c478bd9Sstevel@tonic-gate 	usba_device_t		*usba_device;
3977c478bd9Sstevel@tonic-gate 	int			rval = USB_SUCCESS;
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate 	if ((dip == NULL) || (reg == NULL)) {
4007c478bd9Sstevel@tonic-gate 
4017c478bd9Sstevel@tonic-gate 		return (USB_INVALID_ARGS);
4027c478bd9Sstevel@tonic-gate 	}
4037c478bd9Sstevel@tonic-gate 
4047c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
4057c478bd9Sstevel@tonic-gate 	    "usb_get_dev_data: %s%d",
4067c478bd9Sstevel@tonic-gate 	    ddi_driver_name(dip), ddi_get_instance(dip));
4077c478bd9Sstevel@tonic-gate 
4087c478bd9Sstevel@tonic-gate 	*reg = NULL;
4097c478bd9Sstevel@tonic-gate 
4107c478bd9Sstevel@tonic-gate 	/* did the client attach first? */
4117c478bd9Sstevel@tonic-gate 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
4127c478bd9Sstevel@tonic-gate 	    "driver-major", -1) == -1) {
4137c478bd9Sstevel@tonic-gate 
4147c478bd9Sstevel@tonic-gate 		return (USB_INVALID_VERSION);
4157c478bd9Sstevel@tonic-gate 	}
4167c478bd9Sstevel@tonic-gate 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
4177c478bd9Sstevel@tonic-gate 	    "driver-minor", -1) == -1) {
4187c478bd9Sstevel@tonic-gate 
4197c478bd9Sstevel@tonic-gate 		return (USB_INVALID_VERSION);
4207c478bd9Sstevel@tonic-gate 	}
4217c478bd9Sstevel@tonic-gate 
4227c478bd9Sstevel@tonic-gate 	usb_reg = kmem_zalloc(sizeof (usb_client_dev_data_t), KM_SLEEP);
4237c478bd9Sstevel@tonic-gate 	usba_device = usba_get_usba_device(dip);
4247c478bd9Sstevel@tonic-gate 	usb_reg->dev_descr = usba_device->usb_dev_descr;
4257c478bd9Sstevel@tonic-gate 	usb_reg->dev_default_ph = usba_get_dflt_pipe_handle(dip);
4267c478bd9Sstevel@tonic-gate 	if (usb_reg->dev_default_ph == NULL) {
4277c478bd9Sstevel@tonic-gate 		kmem_free(usb_reg, sizeof (usb_client_dev_data_t));
4287c478bd9Sstevel@tonic-gate 
4297c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
4307c478bd9Sstevel@tonic-gate 	}
4317c478bd9Sstevel@tonic-gate 
4327c478bd9Sstevel@tonic-gate 	usb_reg->dev_iblock_cookie = usba_hcdi_get_hcdi(
4337c478bd9Sstevel@tonic-gate 	    usba_device->usb_root_hub_dip)->hcdi_soft_iblock_cookie;
4347c478bd9Sstevel@tonic-gate 
4357c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
436112116d8Sfb 	    "cookie = 0x%p", (void *)usb_reg->dev_iblock_cookie);
4377c478bd9Sstevel@tonic-gate 
4387c478bd9Sstevel@tonic-gate 	tmpbuf = (char *)kmem_zalloc(USB_MAXSTRINGLEN, KM_SLEEP);
4397c478bd9Sstevel@tonic-gate 
4407c478bd9Sstevel@tonic-gate 	if (usba_device->usb_mfg_str != NULL) {
4417c478bd9Sstevel@tonic-gate 		usb_reg->dev_mfg = kmem_zalloc(
442112116d8Sfb 		    strlen(usba_device->usb_mfg_str) + 1, KM_SLEEP);
4437c478bd9Sstevel@tonic-gate 		(void) strcpy(usb_reg->dev_mfg, usba_device->usb_mfg_str);
4447c478bd9Sstevel@tonic-gate 	}
4457c478bd9Sstevel@tonic-gate 
4467c478bd9Sstevel@tonic-gate 	if (usba_device->usb_product_str != NULL) {
4477c478bd9Sstevel@tonic-gate 		usb_reg->dev_product = kmem_zalloc(
448112116d8Sfb 		    strlen(usba_device->usb_product_str) + 1,
449112116d8Sfb 		    KM_SLEEP);
4507c478bd9Sstevel@tonic-gate 		(void) strcpy(usb_reg->dev_product,
451112116d8Sfb 		    usba_device->usb_product_str);
4527c478bd9Sstevel@tonic-gate 	}
4537c478bd9Sstevel@tonic-gate 
4547c478bd9Sstevel@tonic-gate 	if (usba_device->usb_serialno_str != NULL) {
4557c478bd9Sstevel@tonic-gate 		usb_reg->dev_serial = kmem_zalloc(
456112116d8Sfb 		    strlen(usba_device->usb_serialno_str) + 1,
457112116d8Sfb 		    KM_SLEEP);
4587c478bd9Sstevel@tonic-gate 		(void) strcpy(usb_reg->dev_serial,
459112116d8Sfb 		    usba_device->usb_serialno_str);
4607c478bd9Sstevel@tonic-gate 	}
4617c478bd9Sstevel@tonic-gate 
4627c478bd9Sstevel@tonic-gate 	if ((usb_reg->dev_parse_level = parse_level) == USB_PARSE_LVL_NONE) {
4637c478bd9Sstevel@tonic-gate 		rval = USB_SUCCESS;
4647c478bd9Sstevel@tonic-gate 
4657c478bd9Sstevel@tonic-gate 	} else if ((rval = usba_build_descr_tree(dip, usba_device, usb_reg)) !=
4667c478bd9Sstevel@tonic-gate 	    USB_SUCCESS) {
4677c478bd9Sstevel@tonic-gate 		usb_unregister_client(dip, usb_reg);
4687c478bd9Sstevel@tonic-gate 		usb_reg = NULL;
4697c478bd9Sstevel@tonic-gate 	} else {
4707c478bd9Sstevel@tonic-gate 
4717c478bd9Sstevel@tonic-gate 		/* Current tree cfg is always zero if only one cfg in tree. */
4727c478bd9Sstevel@tonic-gate 		if (usb_reg->dev_n_cfg == 1) {
4737c478bd9Sstevel@tonic-gate 			usb_reg->dev_curr_cfg = &usb_reg->dev_cfg[0];
4747c478bd9Sstevel@tonic-gate 		} else {
4757c478bd9Sstevel@tonic-gate 			mutex_enter(&usba_device->usb_mutex);
4767c478bd9Sstevel@tonic-gate 			usb_reg->dev_curr_cfg =
4777c478bd9Sstevel@tonic-gate 			    &usb_reg->dev_cfg[usba_device->usb_active_cfg_ndx];
4787c478bd9Sstevel@tonic-gate 			mutex_exit(&usba_device->usb_mutex);
4797c478bd9Sstevel@tonic-gate 			ASSERT(usb_reg->dev_curr_cfg != NULL);
4807c478bd9Sstevel@tonic-gate 			ASSERT(usb_reg->dev_curr_cfg->cfg_descr.bLength ==
4817c478bd9Sstevel@tonic-gate 			    USB_CFG_DESCR_SIZE);
4827c478bd9Sstevel@tonic-gate 		}
4837c478bd9Sstevel@tonic-gate 
4847c478bd9Sstevel@tonic-gate 		/*
4857c478bd9Sstevel@tonic-gate 		 * Keep dev_curr_if at device's single interface only if that
4867c478bd9Sstevel@tonic-gate 		 * particular interface has been explicitly defined by the
4877c478bd9Sstevel@tonic-gate 		 * device.
4887c478bd9Sstevel@tonic-gate 		 */
4897c478bd9Sstevel@tonic-gate 		usb_reg->dev_curr_if = usba_get_ifno(dip);
4907c478bd9Sstevel@tonic-gate #ifdef DEBUG
4917c478bd9Sstevel@tonic-gate 		(void) usb_log_descr_tree(usb_reg, usbai_reg_log_handle,
492112116d8Sfb 		    usbai_register_dump_errlevel, (uint_t)-1);
4937c478bd9Sstevel@tonic-gate #endif
4947c478bd9Sstevel@tonic-gate 		/*
4957c478bd9Sstevel@tonic-gate 		 * Fail if interface and configuration of dev_curr_if and
4967c478bd9Sstevel@tonic-gate 		 * dev_curr_cfg don't exist or are invalid.  (Shouldn't happen.)
4977c478bd9Sstevel@tonic-gate 		 * These indices must be reliable for tree traversal.
4987c478bd9Sstevel@tonic-gate 		 */
4997c478bd9Sstevel@tonic-gate 		if ((usb_reg->dev_curr_cfg->cfg_n_if <= usb_reg->dev_curr_if) ||
5007c478bd9Sstevel@tonic-gate 		    (usb_reg->dev_curr_cfg->cfg_descr.bLength == 0) ||
5017c478bd9Sstevel@tonic-gate 		    (usb_reg->dev_curr_cfg->cfg_if[usb_reg->dev_curr_if].
5027c478bd9Sstevel@tonic-gate 		    if_n_alt == 0)) {
5037c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_ALL, usbai_reg_log_handle,
5047c478bd9Sstevel@tonic-gate 			    "usb_get_dev_data: dev_curr_cfg or "
5057c478bd9Sstevel@tonic-gate 			    "dev_curr_if have no descriptors");
5067c478bd9Sstevel@tonic-gate 			usb_unregister_client(dip, usb_reg);
5077c478bd9Sstevel@tonic-gate 			usb_reg = NULL;
5087c478bd9Sstevel@tonic-gate 			rval = USB_FAILURE;
5097c478bd9Sstevel@tonic-gate 		}
5107c478bd9Sstevel@tonic-gate 	}
5117c478bd9Sstevel@tonic-gate 
5127c478bd9Sstevel@tonic-gate 	*reg = usb_reg;
5137c478bd9Sstevel@tonic-gate 	kmem_free(tmpbuf, USB_MAXSTRINGLEN);
5147c478bd9Sstevel@tonic-gate 
5157c478bd9Sstevel@tonic-gate 	if (rval == USB_SUCCESS) {
5167c478bd9Sstevel@tonic-gate 		usb_client_dev_data_list_t *entry = kmem_zalloc(
517112116d8Sfb 		    sizeof (*entry), KM_SLEEP);
5187c478bd9Sstevel@tonic-gate 		mutex_enter(&usba_device->usb_mutex);
5197c478bd9Sstevel@tonic-gate 
5207c478bd9Sstevel@tonic-gate 		usba_device->usb_client_flags[usba_get_ifno(dip)] |=
521112116d8Sfb 		    USBA_CLIENT_FLAG_DEV_DATA;
5227c478bd9Sstevel@tonic-gate 
5237c478bd9Sstevel@tonic-gate 		entry->cddl_dip = dip;
5247c478bd9Sstevel@tonic-gate 		entry->cddl_dev_data = usb_reg;
5257c478bd9Sstevel@tonic-gate 		entry->cddl_ifno = usba_get_ifno(dip);
5267c478bd9Sstevel@tonic-gate 
5277c478bd9Sstevel@tonic-gate 		entry->cddl_next =
528112116d8Sfb 		    usba_device->usb_client_dev_data_list.cddl_next;
5297c478bd9Sstevel@tonic-gate 		if (entry->cddl_next) {
5307c478bd9Sstevel@tonic-gate 			entry->cddl_next->cddl_prev = entry;
5317c478bd9Sstevel@tonic-gate 		}
5327c478bd9Sstevel@tonic-gate 		entry->cddl_prev = &usba_device->usb_client_dev_data_list;
5337c478bd9Sstevel@tonic-gate 		usba_device->usb_client_dev_data_list.cddl_next = entry;
5347c478bd9Sstevel@tonic-gate 
5357c478bd9Sstevel@tonic-gate 		mutex_exit(&usba_device->usb_mutex);
5367c478bd9Sstevel@tonic-gate 	}
5377c478bd9Sstevel@tonic-gate 
5387c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
5397c478bd9Sstevel@tonic-gate 	    "usb_get_dev_data rval=%d", rval);
5407c478bd9Sstevel@tonic-gate 
5417c478bd9Sstevel@tonic-gate 	return (rval);
5427c478bd9Sstevel@tonic-gate }
5437c478bd9Sstevel@tonic-gate 
5447c478bd9Sstevel@tonic-gate 
5457c478bd9Sstevel@tonic-gate /*
5467c478bd9Sstevel@tonic-gate  * usb_free_dev_data
5477c478bd9Sstevel@tonic-gate  *	undoes what usb_get_dev_data does
5487c478bd9Sstevel@tonic-gate  *
5497c478bd9Sstevel@tonic-gate  * Arguments:
5507c478bd9Sstevel@tonic-gate  *	dip		- pointer to devinfo node of the client
5517c478bd9Sstevel@tonic-gate  *	reg		- return registration data at this address
5527c478bd9Sstevel@tonic-gate  */
5537c478bd9Sstevel@tonic-gate void
usb_free_dev_data(dev_info_t * dip,usb_client_dev_data_t * reg)5547c478bd9Sstevel@tonic-gate usb_free_dev_data(dev_info_t *dip, usb_client_dev_data_t *reg)
5557c478bd9Sstevel@tonic-gate {
5567c478bd9Sstevel@tonic-gate 	if (dip == NULL) {
5577c478bd9Sstevel@tonic-gate 
5587c478bd9Sstevel@tonic-gate 		return;
5597c478bd9Sstevel@tonic-gate 	}
5607c478bd9Sstevel@tonic-gate 
5617c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
5627c478bd9Sstevel@tonic-gate 	    "usb_free_dev_data %s%d: reg=0x%p",
563112116d8Sfb 	    ddi_driver_name(dip), ddi_get_instance(dip), (void *)reg);
5647c478bd9Sstevel@tonic-gate 
5657c478bd9Sstevel@tonic-gate 	if (reg != NULL) {
5667c478bd9Sstevel@tonic-gate 		usba_device_t *usba_device = usba_get_usba_device(dip);
5677c478bd9Sstevel@tonic-gate 		usb_client_dev_data_list_t *next, *prev, *entry;
5687c478bd9Sstevel@tonic-gate 		int	matches = 0;
5697c478bd9Sstevel@tonic-gate 
5707c478bd9Sstevel@tonic-gate 		if (reg->dev_serial != NULL) {
5717c478bd9Sstevel@tonic-gate 			kmem_free((char *)reg->dev_serial,
5727c478bd9Sstevel@tonic-gate 			    strlen((char *)reg->dev_serial) + 1);
5737c478bd9Sstevel@tonic-gate 		}
5747c478bd9Sstevel@tonic-gate 
5757c478bd9Sstevel@tonic-gate 		if (reg->dev_product != NULL) {
5767c478bd9Sstevel@tonic-gate 			kmem_free((char *)reg->dev_product,
5777c478bd9Sstevel@tonic-gate 			    strlen((char *)reg->dev_product) + 1);
5787c478bd9Sstevel@tonic-gate 		}
5797c478bd9Sstevel@tonic-gate 
5807c478bd9Sstevel@tonic-gate 		if (reg->dev_mfg != NULL) {
5817c478bd9Sstevel@tonic-gate 			kmem_free((char *)reg->dev_mfg,
5827c478bd9Sstevel@tonic-gate 			    strlen((char *)reg->dev_mfg) + 1);
5837c478bd9Sstevel@tonic-gate 		}
5847c478bd9Sstevel@tonic-gate 
5857c478bd9Sstevel@tonic-gate 		/* Free config tree under reg->dev_cfg. */
5867c478bd9Sstevel@tonic-gate 		if (reg->dev_cfg != NULL) {
5877c478bd9Sstevel@tonic-gate 			usb_free_descr_tree(dip, reg);
5887c478bd9Sstevel@tonic-gate 		}
5897c478bd9Sstevel@tonic-gate 
5907c478bd9Sstevel@tonic-gate 		mutex_enter(&usba_device->usb_mutex);
5917c478bd9Sstevel@tonic-gate 		prev = &usba_device->usb_client_dev_data_list;
5927c478bd9Sstevel@tonic-gate 		entry = usba_device->usb_client_dev_data_list.cddl_next;
5937c478bd9Sstevel@tonic-gate 
5947c478bd9Sstevel@tonic-gate 		/* free the entries in usb_client_data_list */
5957c478bd9Sstevel@tonic-gate 		while (entry) {
5967c478bd9Sstevel@tonic-gate 			next = entry->cddl_next;
5977c478bd9Sstevel@tonic-gate 			if ((dip == entry->cddl_dip) &&
5987c478bd9Sstevel@tonic-gate 			    (reg == entry->cddl_dev_data)) {
5997c478bd9Sstevel@tonic-gate 				prev->cddl_next = entry->cddl_next;
6007c478bd9Sstevel@tonic-gate 				if (entry->cddl_next) {
6017c478bd9Sstevel@tonic-gate 					entry->cddl_next->cddl_prev = prev;
6027c478bd9Sstevel@tonic-gate 				}
6037c478bd9Sstevel@tonic-gate 				kmem_free(entry, sizeof (*entry));
6047c478bd9Sstevel@tonic-gate 			} else {
6057c478bd9Sstevel@tonic-gate 				/*
6067c478bd9Sstevel@tonic-gate 				 * any other entries for this interface?
6077c478bd9Sstevel@tonic-gate 				 */
6087c478bd9Sstevel@tonic-gate 				if (usba_get_ifno(dip) == entry->cddl_ifno) {
6097c478bd9Sstevel@tonic-gate 					matches++;
6107c478bd9Sstevel@tonic-gate 				}
6117c478bd9Sstevel@tonic-gate 				prev = entry;
6127c478bd9Sstevel@tonic-gate 			}
6137c478bd9Sstevel@tonic-gate 			entry = next;
6147c478bd9Sstevel@tonic-gate 		}
6157c478bd9Sstevel@tonic-gate 
6167c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_REGISTER,
6177c478bd9Sstevel@tonic-gate 		    usbai_reg_log_handle,
6187c478bd9Sstevel@tonic-gate 		    "usb_free_dev_data: next=0x%p flags[%d]=0x%x",
619112116d8Sfb 		    (void *)usba_device->usb_client_dev_data_list.cddl_next,
6207c478bd9Sstevel@tonic-gate 		    usba_get_ifno(dip),
6217c478bd9Sstevel@tonic-gate 		    usba_device->usb_client_flags[usba_get_ifno(dip)]);
6227c478bd9Sstevel@tonic-gate 
6237c478bd9Sstevel@tonic-gate 		if (matches == 0) {
6247c478bd9Sstevel@tonic-gate 			usba_device->
6257c478bd9Sstevel@tonic-gate 			    usb_client_flags[usba_get_ifno(dip)] &=
626112116d8Sfb 			    ~USBA_CLIENT_FLAG_DEV_DATA;
6277c478bd9Sstevel@tonic-gate 		}
6287c478bd9Sstevel@tonic-gate 		mutex_exit(&usba_device->usb_mutex);
6297c478bd9Sstevel@tonic-gate 
6307c478bd9Sstevel@tonic-gate 		kmem_free(reg, sizeof (usb_client_dev_data_t));
6317c478bd9Sstevel@tonic-gate 	}
6327c478bd9Sstevel@tonic-gate 
6337c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
6347c478bd9Sstevel@tonic-gate 	    "usb_free_dev_data done");
6357c478bd9Sstevel@tonic-gate }
6367c478bd9Sstevel@tonic-gate 
6377c478bd9Sstevel@tonic-gate /*
6387c478bd9Sstevel@tonic-gate  * usba_build_descr_tree:
6397c478bd9Sstevel@tonic-gate  *	This builds the descriptor tree.  See module header comment for tree
6407c478bd9Sstevel@tonic-gate  *	description.
6417c478bd9Sstevel@tonic-gate  *
6427c478bd9Sstevel@tonic-gate  * Arguments:
6437c478bd9Sstevel@tonic-gate  *	dip		- devinfo pointer - cannot be NULL.
6447c478bd9Sstevel@tonic-gate  *	usba_device	- pointer to usba_device structure.
6457c478bd9Sstevel@tonic-gate  *	usb_reg		- pointer to area returned to client describing device.
6467c478bd9Sstevel@tonic-gate  *			  number of configuration (dev_n_cfg) and array of
6477c478bd9Sstevel@tonic-gate  *			  configurations (dev_cfg) are initialized here -
6487c478bd9Sstevel@tonic-gate  *			  dev_parse_level used and may be modified to fit
6497c478bd9Sstevel@tonic-gate  *			  current configuration.
6507c478bd9Sstevel@tonic-gate  * Return values:
6517c478bd9Sstevel@tonic-gate  *	USB_SUCCESS	 - Tree build succeeded
6527c478bd9Sstevel@tonic-gate  *	USB_INVALID_ARGS - dev_parse_level in usb_reg is invalid.
6537c478bd9Sstevel@tonic-gate  *	USB_FAILURE	 - Bad descriptor info or other internal failure
6547c478bd9Sstevel@tonic-gate  */
6557c478bd9Sstevel@tonic-gate static int
usba_build_descr_tree(dev_info_t * dip,usba_device_t * usba_device,usb_client_dev_data_t * usb_reg)6567c478bd9Sstevel@tonic-gate usba_build_descr_tree(dev_info_t *dip, usba_device_t *usba_device,
6577c478bd9Sstevel@tonic-gate     usb_client_dev_data_t *usb_reg)
6587c478bd9Sstevel@tonic-gate {
6597c478bd9Sstevel@tonic-gate 	usba_reg_state_t state;			/* State of tree construction */
6607c478bd9Sstevel@tonic-gate 	int		cfg_len_so_far = 0;	/* Bytes found, this config. */
6617c478bd9Sstevel@tonic-gate 	uint8_t 	*last_byte;	/* Ptr to the end of the cfg cloud. */
6627c478bd9Sstevel@tonic-gate 	uint_t		this_cfg_ndx;		/* Configuration counter. */
6637c478bd9Sstevel@tonic-gate 	uint_t		high_cfg_bound;		/* High config index + 1. */
6647c478bd9Sstevel@tonic-gate 	uint_t		low_cfg_bound;		/* Low config index. */
6657c478bd9Sstevel@tonic-gate 	boolean_t	process_this_if_tree = B_FALSE; /* Save alts, eps, */
6667c478bd9Sstevel@tonic-gate 							/* of this interface. */
6677c478bd9Sstevel@tonic-gate 
6687c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
6697c478bd9Sstevel@tonic-gate 	    "usba_build_descr_tree starting");
6707c478bd9Sstevel@tonic-gate 
6717c478bd9Sstevel@tonic-gate 	bzero(&state, sizeof (usba_reg_state_t));
6727c478bd9Sstevel@tonic-gate 	state.dip = dip;
6737c478bd9Sstevel@tonic-gate 
6747c478bd9Sstevel@tonic-gate 	/*
6757c478bd9Sstevel@tonic-gate 	 * Set config(s) and interface(s) to parse based on parse level.
6767c478bd9Sstevel@tonic-gate 	 * Adjust parse_level according to which configs and interfaces are
6777c478bd9Sstevel@tonic-gate 	 * made available by the device.
6787c478bd9Sstevel@tonic-gate 	 */
6797c478bd9Sstevel@tonic-gate 	state.st_dev_parse_level = usb_reg->dev_parse_level;
6807c478bd9Sstevel@tonic-gate 	if (usba_set_parse_values(dip, usba_device, &state) != USB_SUCCESS) {
6817c478bd9Sstevel@tonic-gate 
6827c478bd9Sstevel@tonic-gate 		return (USB_INVALID_ARGS);
6837c478bd9Sstevel@tonic-gate 	}
6847c478bd9Sstevel@tonic-gate 	usb_reg->dev_parse_level = state.st_dev_parse_level;
6857c478bd9Sstevel@tonic-gate 
6867c478bd9Sstevel@tonic-gate 	/* Preallocate configurations based on parse level. */
6877c478bd9Sstevel@tonic-gate 	if (usb_reg->dev_parse_level == USB_PARSE_LVL_ALL) {
6887c478bd9Sstevel@tonic-gate 		usb_reg->dev_n_cfg = usba_device->usb_n_cfgs;
6897c478bd9Sstevel@tonic-gate 		low_cfg_bound = 0;
6907c478bd9Sstevel@tonic-gate 		high_cfg_bound = usba_device->usb_n_cfgs;
6917c478bd9Sstevel@tonic-gate 	} else {
6927c478bd9Sstevel@tonic-gate 		usb_reg->dev_n_cfg = 1;
6937c478bd9Sstevel@tonic-gate 		mutex_enter(&usba_device->usb_mutex);
6947c478bd9Sstevel@tonic-gate 		low_cfg_bound = usba_device->usb_active_cfg_ndx;
6957c478bd9Sstevel@tonic-gate 		high_cfg_bound = usba_device->usb_active_cfg_ndx + 1;
6967c478bd9Sstevel@tonic-gate 		mutex_exit(&usba_device->usb_mutex);
6977c478bd9Sstevel@tonic-gate 	}
6987c478bd9Sstevel@tonic-gate 	usb_reg->dev_cfg = state.st_dev_cfg = kmem_zalloc(
699112116d8Sfb 	    (usb_reg->dev_n_cfg * sizeof (usb_cfg_data_t)),
700112116d8Sfb 	    KM_SLEEP);
7017c478bd9Sstevel@tonic-gate 	/*
7027c478bd9Sstevel@tonic-gate 	 * this_cfg_ndx loops through all configurations presented;
7037c478bd9Sstevel@tonic-gate 	 * state.st_dev_n_cfg limits the cfgs checked to the number desired.
7047c478bd9Sstevel@tonic-gate 	 */
7057c478bd9Sstevel@tonic-gate 	state.st_dev_n_cfg = 0;
7067c478bd9Sstevel@tonic-gate 	for (this_cfg_ndx = low_cfg_bound; this_cfg_ndx < high_cfg_bound;
7077c478bd9Sstevel@tonic-gate 	    this_cfg_ndx++) {
7087c478bd9Sstevel@tonic-gate 
7097c478bd9Sstevel@tonic-gate 		state.st_curr_raw_descr =
710112116d8Sfb 		    usba_device->usb_cfg_array[this_cfg_ndx];
7117c478bd9Sstevel@tonic-gate 		ASSERT(state.st_curr_raw_descr != NULL);
7127c478bd9Sstevel@tonic-gate 
7137c478bd9Sstevel@tonic-gate 		/* Clear the following for config cloud sanity checking. */
7147c478bd9Sstevel@tonic-gate 		last_byte = NULL;
7157c478bd9Sstevel@tonic-gate 		state.st_curr_cfg = NULL;
7167c478bd9Sstevel@tonic-gate 		state.st_curr_if = NULL;
7177c478bd9Sstevel@tonic-gate 		state.st_curr_alt = NULL;
7187c478bd9Sstevel@tonic-gate 		state.st_curr_ep = NULL;
7197c478bd9Sstevel@tonic-gate 
7207c478bd9Sstevel@tonic-gate 		do {
7217c478bd9Sstevel@tonic-gate 			/* All descr have length and type at offset 0 and 1 */
7227c478bd9Sstevel@tonic-gate 			state.st_curr_raw_descr_len =
7237c478bd9Sstevel@tonic-gate 			    state.st_curr_raw_descr[0];
7247c478bd9Sstevel@tonic-gate 			state.st_curr_raw_descr_type =
7257c478bd9Sstevel@tonic-gate 			    state.st_curr_raw_descr[1];
7267c478bd9Sstevel@tonic-gate 
7277c478bd9Sstevel@tonic-gate 			/* First descr in cloud must be a config descr. */
7287c478bd9Sstevel@tonic-gate 			if ((last_byte == NULL) &&
7297c478bd9Sstevel@tonic-gate 			    (state.st_curr_raw_descr_type !=
7307c478bd9Sstevel@tonic-gate 			    USB_DESCR_TYPE_CFG)) {
7317c478bd9Sstevel@tonic-gate 
7327c478bd9Sstevel@tonic-gate 				return (USB_FAILURE);
7337c478bd9Sstevel@tonic-gate 			}
7347c478bd9Sstevel@tonic-gate 
7357c478bd9Sstevel@tonic-gate 			/*
7367c478bd9Sstevel@tonic-gate 			 * Bomb if we don't find a new cfg descr when expected.
7377c478bd9Sstevel@tonic-gate 			 * cfg_len_so_far = total_cfg_length = 0 1st time thru.
7387c478bd9Sstevel@tonic-gate 			 */
7397c478bd9Sstevel@tonic-gate 			if (cfg_len_so_far > state.st_total_cfg_length) {
7407c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_ALL,
7417c478bd9Sstevel@tonic-gate 				    usbai_reg_log_handle,
7427c478bd9Sstevel@tonic-gate 				    "usba_build_descr_tree: Configuration (%d) "
7437c478bd9Sstevel@tonic-gate 				    "larger than wTotalLength (%d).",
7447c478bd9Sstevel@tonic-gate 				    cfg_len_so_far, state.st_total_cfg_length);
7457c478bd9Sstevel@tonic-gate 
7467c478bd9Sstevel@tonic-gate 				return (USB_FAILURE);
7477c478bd9Sstevel@tonic-gate 			}
7487c478bd9Sstevel@tonic-gate 
7497c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_REGISTER,
7507c478bd9Sstevel@tonic-gate 			    usbai_reg_log_handle,
7517c478bd9Sstevel@tonic-gate 			    "usba_build_descr_tree: Process type %d descr "
7527c478bd9Sstevel@tonic-gate 			    "(addr=0x%p)", state.st_curr_raw_descr_type,
753112116d8Sfb 			    (void *)state.st_curr_raw_descr);
7547c478bd9Sstevel@tonic-gate 
7557c478bd9Sstevel@tonic-gate 			switch (state.st_curr_raw_descr_type) {
7567c478bd9Sstevel@tonic-gate 			case USB_DESCR_TYPE_CFG:
7577c478bd9Sstevel@tonic-gate 				cfg_len_so_far = 0;
7587c478bd9Sstevel@tonic-gate 				process_this_if_tree = B_FALSE;
7597c478bd9Sstevel@tonic-gate 
7607c478bd9Sstevel@tonic-gate 				state.st_curr_cfg_str = usba_device->
761112116d8Sfb 				    usb_cfg_str_descr[this_cfg_ndx];
7627c478bd9Sstevel@tonic-gate 				usba_process_cfg_descr(&state);
7637c478bd9Sstevel@tonic-gate 				state.st_last_processed_descr_type =
764112116d8Sfb 				    USB_DESCR_TYPE_CFG;
7657c478bd9Sstevel@tonic-gate 				last_byte = state.st_curr_raw_descr +
7667c478bd9Sstevel@tonic-gate 				    (state.st_total_cfg_length *
7677c478bd9Sstevel@tonic-gate 				    sizeof (uchar_t));
7687c478bd9Sstevel@tonic-gate 
7697c478bd9Sstevel@tonic-gate 				break;
7707c478bd9Sstevel@tonic-gate 
7717c478bd9Sstevel@tonic-gate 			case USB_DESCR_TYPE_IF:
7727c478bd9Sstevel@tonic-gate 				/*
7737c478bd9Sstevel@tonic-gate 				 * process_this_if_tree == TRUE means this
7747c478bd9Sstevel@tonic-gate 				 * interface, plus all eps and c/vs in it are
7757c478bd9Sstevel@tonic-gate 				 * to be processed.
7767c478bd9Sstevel@tonic-gate 				 */
7777c478bd9Sstevel@tonic-gate 				if (usba_process_if_descr(&state,
778112116d8Sfb 				    &process_this_if_tree) != USB_SUCCESS) {
7797c478bd9Sstevel@tonic-gate 
780112116d8Sfb 					return (USB_FAILURE);
7817c478bd9Sstevel@tonic-gate 				}
7827c478bd9Sstevel@tonic-gate 				state.st_last_processed_descr_type =
783112116d8Sfb 				    USB_DESCR_TYPE_IF;
7847c478bd9Sstevel@tonic-gate 
7857c478bd9Sstevel@tonic-gate 				break;
7867c478bd9Sstevel@tonic-gate 
7877c478bd9Sstevel@tonic-gate 			case USB_DESCR_TYPE_EP:
7887c478bd9Sstevel@tonic-gate 				/*
7897c478bd9Sstevel@tonic-gate 				 * Skip if endpoints of a specific interface are
7907c478bd9Sstevel@tonic-gate 				 * desired and this endpoint is associated with
7917c478bd9Sstevel@tonic-gate 				 * a different interface.
7927c478bd9Sstevel@tonic-gate 				 */
7937c478bd9Sstevel@tonic-gate 				if (process_this_if_tree) {
7947c478bd9Sstevel@tonic-gate 					if (usba_process_ep_descr(&state) !=
7957c478bd9Sstevel@tonic-gate 					    USB_SUCCESS) {
7967c478bd9Sstevel@tonic-gate 
7977c478bd9Sstevel@tonic-gate 						return (USB_FAILURE);
7987c478bd9Sstevel@tonic-gate 					}
7997c478bd9Sstevel@tonic-gate 					state.st_last_processed_descr_type =
800112116d8Sfb 					    USB_DESCR_TYPE_EP;
8017c478bd9Sstevel@tonic-gate 				}
8027c478bd9Sstevel@tonic-gate 
803ff0e937bSRaymond Chen 				break;
804993e3fafSRobert Mustacchi 
805993e3fafSRobert Mustacchi 			case USB_DESCR_TYPE_SS_EP_COMP:
806993e3fafSRobert Mustacchi 
807993e3fafSRobert Mustacchi 				/*
808993e3fafSRobert Mustacchi 				 * These entries should always follow an
809993e3fafSRobert Mustacchi 				 * endpoint description. If an endpoint
810993e3fafSRobert Mustacchi 				 * description wasn't the last
811993e3fafSRobert Mustacchi 				 * thing that we found, then we shouldn't
812993e3fafSRobert Mustacchi 				 * process this descriptor.
813993e3fafSRobert Mustacchi 				 */
814993e3fafSRobert Mustacchi 				if (state.st_last_processed_descr_type ==
815993e3fafSRobert Mustacchi 				    USB_DESCR_TYPE_EP) {
816993e3fafSRobert Mustacchi 					if (usba_process_ss_ep_comp_descr(
817993e3fafSRobert Mustacchi 					    &state) != USB_SUCCESS) {
818993e3fafSRobert Mustacchi 
819993e3fafSRobert Mustacchi 						return (USB_FAILURE);
820993e3fafSRobert Mustacchi 					}
821993e3fafSRobert Mustacchi 
822993e3fafSRobert Mustacchi 					state.st_last_processed_descr_type =
823993e3fafSRobert Mustacchi 					    USB_DESCR_TYPE_SS_EP_COMP;
824993e3fafSRobert Mustacchi 
825993e3fafSRobert Mustacchi 					break;
826993e3fafSRobert Mustacchi 				}
827993e3fafSRobert Mustacchi 				break;
828993e3fafSRobert Mustacchi 
8297c478bd9Sstevel@tonic-gate 			case USB_DESCR_TYPE_STRING:
8307c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_ALL,
8317c478bd9Sstevel@tonic-gate 				    usbai_reg_log_handle,
8327c478bd9Sstevel@tonic-gate 				    "usb_get_dev_data: "
8337c478bd9Sstevel@tonic-gate 				    "Found unexpected str descr at addr 0x%p",
834112116d8Sfb 				    (void *)state.st_curr_raw_descr);
8357c478bd9Sstevel@tonic-gate 
8367c478bd9Sstevel@tonic-gate 				break;	/* Shouldn't be any here.  Skip. */
8377c478bd9Sstevel@tonic-gate 
8387c478bd9Sstevel@tonic-gate 			default:
8397c478bd9Sstevel@tonic-gate 				/*
8407c478bd9Sstevel@tonic-gate 				 * Treat all other descr as class/vendor
8417c478bd9Sstevel@tonic-gate 				 * specific.  Skip if c/vs of a specific
8427c478bd9Sstevel@tonic-gate 				 * interface are desired and this c/v is
8437c478bd9Sstevel@tonic-gate 				 * associated with a different one.
844ff0e937bSRaymond Chen 				 * Device level c/vs should always be
845ff0e937bSRaymond Chen 				 * processed, e.g., the security descrs
846ff0e937bSRaymond Chen 				 * for the Host Wire Adapter.
8477c478bd9Sstevel@tonic-gate 				 */
848ff0e937bSRaymond Chen 				if ((state.st_last_processed_descr_type ==
849ff0e937bSRaymond Chen 				    USB_DESCR_TYPE_CFG) ||
850ff0e937bSRaymond Chen 				    (process_this_if_tree == B_TRUE)) {
8517c478bd9Sstevel@tonic-gate 					if (usba_process_cv_descr(&state) !=
8527c478bd9Sstevel@tonic-gate 					    USB_SUCCESS) {
8537c478bd9Sstevel@tonic-gate 
8547c478bd9Sstevel@tonic-gate 						return (USB_FAILURE);
8557c478bd9Sstevel@tonic-gate 					}
8567c478bd9Sstevel@tonic-gate 				}
8577c478bd9Sstevel@tonic-gate 			}
8587c478bd9Sstevel@tonic-gate 
8597c478bd9Sstevel@tonic-gate 			state.st_curr_raw_descr += state.st_curr_raw_descr_len;
8607c478bd9Sstevel@tonic-gate 			cfg_len_so_far += state.st_curr_raw_descr_len;
8617c478bd9Sstevel@tonic-gate 
8627c478bd9Sstevel@tonic-gate 		} while (state.st_curr_raw_descr < last_byte);
8637c478bd9Sstevel@tonic-gate 	}
8647c478bd9Sstevel@tonic-gate 
8657c478bd9Sstevel@tonic-gate 	/* Make tree sparse, and put elements in order. */
8667c478bd9Sstevel@tonic-gate 	usba_order_tree(&state);
8677c478bd9Sstevel@tonic-gate 
8687c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
8697c478bd9Sstevel@tonic-gate 	    "usba_build_descr_tree done");
8707c478bd9Sstevel@tonic-gate 
8717c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
8727c478bd9Sstevel@tonic-gate }
8737c478bd9Sstevel@tonic-gate 
8747c478bd9Sstevel@tonic-gate 
8757c478bd9Sstevel@tonic-gate /*
8767c478bd9Sstevel@tonic-gate  * usba_process_cfg_descr:
8777c478bd9Sstevel@tonic-gate  *	Set up a configuration tree node based on a raw config descriptor.
8787c478bd9Sstevel@tonic-gate  *
8797c478bd9Sstevel@tonic-gate  * Arguments:
8807c478bd9Sstevel@tonic-gate  *	state		- Pointer to this module's state structure.
8817c478bd9Sstevel@tonic-gate  *
8827c478bd9Sstevel@tonic-gate  * Returns:
8837c478bd9Sstevel@tonic-gate  *	B_TRUE: the descr processed corresponds to a requested configuration.
8847c478bd9Sstevel@tonic-gate  *	B_FALSE: the descr processed does not correspond to a requested config.
8857c478bd9Sstevel@tonic-gate  */
8867c478bd9Sstevel@tonic-gate static void
usba_process_cfg_descr(usba_reg_state_t * state)8877c478bd9Sstevel@tonic-gate usba_process_cfg_descr(usba_reg_state_t *state)
8887c478bd9Sstevel@tonic-gate {
8897c478bd9Sstevel@tonic-gate 	usb_cfg_data_t *curr_cfg;
8907c478bd9Sstevel@tonic-gate 
8917c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
8927c478bd9Sstevel@tonic-gate 	    "usba_process_cfg_descr starting");
8937c478bd9Sstevel@tonic-gate 
8947c478bd9Sstevel@tonic-gate 	curr_cfg = state->st_curr_cfg =
8957c478bd9Sstevel@tonic-gate 	    &state->st_dev_cfg[state->st_dev_n_cfg++];
8967c478bd9Sstevel@tonic-gate 
8977c478bd9Sstevel@tonic-gate 	/* Parse and store config descriptor proper in the tree. */
8987c478bd9Sstevel@tonic-gate 	(void) usb_parse_data("2cs5c",
8997c478bd9Sstevel@tonic-gate 	    state->st_curr_raw_descr, state->st_curr_raw_descr_len,
9007c478bd9Sstevel@tonic-gate 	    &curr_cfg->cfg_descr,
9017c478bd9Sstevel@tonic-gate 	    sizeof (usb_cfg_descr_t));
9027c478bd9Sstevel@tonic-gate 
9037c478bd9Sstevel@tonic-gate 	state->st_total_cfg_length = curr_cfg->cfg_descr.wTotalLength;
9047c478bd9Sstevel@tonic-gate 
9057c478bd9Sstevel@tonic-gate 	if (state->st_curr_cfg_str != NULL) {
9067c478bd9Sstevel@tonic-gate 		curr_cfg->cfg_strsize = strlen(state->st_curr_cfg_str) + 1;
9077c478bd9Sstevel@tonic-gate 		curr_cfg->cfg_str = kmem_zalloc(curr_cfg->cfg_strsize,
908112116d8Sfb 		    KM_SLEEP);
9097c478bd9Sstevel@tonic-gate 		(void) strcpy(curr_cfg->cfg_str, state->st_curr_cfg_str);
9107c478bd9Sstevel@tonic-gate 	}
9117c478bd9Sstevel@tonic-gate 
9127c478bd9Sstevel@tonic-gate 	curr_cfg->cfg_n_if = curr_cfg->cfg_descr.bNumInterfaces;
9137c478bd9Sstevel@tonic-gate 	curr_cfg->cfg_if = kmem_zalloc((curr_cfg->cfg_n_if *
914112116d8Sfb 	    sizeof (usb_if_data_t)), KM_SLEEP);
9157c478bd9Sstevel@tonic-gate 
9167c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
9177c478bd9Sstevel@tonic-gate 	    "usba_process_cfg_descr done");
9187c478bd9Sstevel@tonic-gate }
9197c478bd9Sstevel@tonic-gate 
9207c478bd9Sstevel@tonic-gate 
9217c478bd9Sstevel@tonic-gate /*
9227c478bd9Sstevel@tonic-gate  * usba_process_if_descr:
9237c478bd9Sstevel@tonic-gate  *	This processes a raw interface descriptor, and sets up an analogous
9247c478bd9Sstevel@tonic-gate  *	interface node and child "alternate" nodes (each containing an
9257c478bd9Sstevel@tonic-gate  *	interface descriptor) in the descriptor tree.
9267c478bd9Sstevel@tonic-gate  *
9277c478bd9Sstevel@tonic-gate  *	It groups all descriptors with the same bInterfaceNumber (alternates)
9287c478bd9Sstevel@tonic-gate  *	into an array.	It makes entries in an interface array, each of which
9297c478bd9Sstevel@tonic-gate  *	points to an array of alternates.
9307c478bd9Sstevel@tonic-gate  *
9317c478bd9Sstevel@tonic-gate  * Arguments:
9327c478bd9Sstevel@tonic-gate  *	state		- Pointer to this module's state structure.
9337c478bd9Sstevel@tonic-gate  *	requested_if	- Address into which the following is returned:
9347c478bd9Sstevel@tonic-gate  *	    B_TRUE	- the processed descr is of a requested interface.
9357c478bd9Sstevel@tonic-gate  *	    B_FALSE	- the processed descr if of a non-requested interface.
9367c478bd9Sstevel@tonic-gate  *
9377c478bd9Sstevel@tonic-gate  * Returns:
9387c478bd9Sstevel@tonic-gate  *	USB_SUCCESS:	Descriptor is successfully parsed.
9397c478bd9Sstevel@tonic-gate  *	USB_FAILURE:	Descriptor is inappropriately placed in config cloud.
9407c478bd9Sstevel@tonic-gate  */
9417c478bd9Sstevel@tonic-gate static int
usba_process_if_descr(usba_reg_state_t * state,boolean_t * requested_if)9427c478bd9Sstevel@tonic-gate usba_process_if_descr(usba_reg_state_t *state, boolean_t *requested_if)
9437c478bd9Sstevel@tonic-gate {
9447c478bd9Sstevel@tonic-gate 	char *string;
9457c478bd9Sstevel@tonic-gate 	usb_if_descr_t *new_if_descr;
9467c478bd9Sstevel@tonic-gate 	usba_device_t *usba_device = usba_get_usba_device(state->dip);
9477c478bd9Sstevel@tonic-gate 	int is_root_hub = (usba_device->usb_addr == ROOT_HUB_ADDR);
9487c478bd9Sstevel@tonic-gate 
9497c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
9507c478bd9Sstevel@tonic-gate 	    "usba_process_if_descr starting");
9517c478bd9Sstevel@tonic-gate 
9527c478bd9Sstevel@tonic-gate 	/* No config preceeds this interface. */
9537c478bd9Sstevel@tonic-gate 	if (state->st_curr_cfg == NULL) {
9547c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
9557c478bd9Sstevel@tonic-gate 		    "usba_process_if_descr found interface after no config.");
9567c478bd9Sstevel@tonic-gate 
9577c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
9587c478bd9Sstevel@tonic-gate 	}
9597c478bd9Sstevel@tonic-gate 
9607c478bd9Sstevel@tonic-gate 	new_if_descr = kmem_zalloc(sizeof (usb_if_descr_t), KM_SLEEP);
9617c478bd9Sstevel@tonic-gate 
9627c478bd9Sstevel@tonic-gate 	/* Strictly speaking, unpacking is not necessary.  Could use bcopy. */
9637c478bd9Sstevel@tonic-gate 	(void) usb_parse_data("9c", state->st_curr_raw_descr,
964112116d8Sfb 	    state->st_curr_raw_descr_len,
965112116d8Sfb 	    new_if_descr, sizeof (usb_if_descr_t));
9667c478bd9Sstevel@tonic-gate 
967cbab2b26Slg 	/* Check the interface number in case of a malfunction device */
968cbab2b26Slg 	if (new_if_descr->bInterfaceNumber >= state->st_curr_cfg->cfg_n_if) {
969cbab2b26Slg 		USB_DPRINTF_L2(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
970cbab2b26Slg 		    "usba_process_if_descr: bInterfaceNumber=%d is not "
971cbab2b26Slg 		    "a valid one", new_if_descr->bInterfaceNumber);
972cbab2b26Slg 		kmem_free(new_if_descr, sizeof (usb_if_descr_t));
973cbab2b26Slg 
974de577e60Svn 		*requested_if = B_FALSE;
975de577e60Svn 
976de577e60Svn 		return (USB_SUCCESS);
977cbab2b26Slg 	}
978cbab2b26Slg 	*requested_if = B_TRUE;
979cbab2b26Slg 
9807c478bd9Sstevel@tonic-gate 	/* Not a requested interface. */
9817c478bd9Sstevel@tonic-gate 	if ((state->st_if_to_build != new_if_descr->bInterfaceNumber) &&
9827c478bd9Sstevel@tonic-gate 	    (state->st_if_to_build != USBA_ALL)) {
9837c478bd9Sstevel@tonic-gate 		*requested_if = B_FALSE;
9847c478bd9Sstevel@tonic-gate 
9857c478bd9Sstevel@tonic-gate 	} else {
9867c478bd9Sstevel@tonic-gate 		usb_alt_if_data_t *alt_array;
9877c478bd9Sstevel@tonic-gate 		uint_t		alt_index;
9887c478bd9Sstevel@tonic-gate 
9897c478bd9Sstevel@tonic-gate 		/* Point to proper interface node, based on num in descr. */
9907c478bd9Sstevel@tonic-gate 		state->st_curr_if =
9917c478bd9Sstevel@tonic-gate 		    &state->st_curr_cfg->cfg_if[new_if_descr->bInterfaceNumber];
9927c478bd9Sstevel@tonic-gate 
9937c478bd9Sstevel@tonic-gate 		/* Make room for new alternate. */
9947c478bd9Sstevel@tonic-gate 		alt_index = state->st_curr_if->if_n_alt;
9957c478bd9Sstevel@tonic-gate 		alt_array = state->st_curr_if->if_alt;
9967c478bd9Sstevel@tonic-gate 		usba_augment_array((void **)(&alt_array), alt_index,
997112116d8Sfb 		    sizeof (usb_alt_if_data_t));
9987c478bd9Sstevel@tonic-gate 
9997c478bd9Sstevel@tonic-gate 		/* Ptr to the current alt, may be used to attach a c/v to it. */
10007c478bd9Sstevel@tonic-gate 		state->st_curr_alt = &alt_array[alt_index];
10017c478bd9Sstevel@tonic-gate 
10027c478bd9Sstevel@tonic-gate 		bcopy(new_if_descr, &(alt_array[alt_index++].altif_descr),
10037c478bd9Sstevel@tonic-gate 		    sizeof (usb_if_descr_t));
10047c478bd9Sstevel@tonic-gate 		state->st_curr_if->if_alt = alt_array;
10057c478bd9Sstevel@tonic-gate 		state->st_curr_if->if_n_alt = alt_index;
10067c478bd9Sstevel@tonic-gate 
10077c478bd9Sstevel@tonic-gate 		string = kmem_zalloc(USB_MAXSTRINGLEN, KM_SLEEP);
10087c478bd9Sstevel@tonic-gate 		if (!is_root_hub) {
10097c478bd9Sstevel@tonic-gate 			(void) usb_get_string_descr(state->dip, USB_LANG_ID,
10107c478bd9Sstevel@tonic-gate 			    state->st_curr_alt->altif_descr.iInterface,
10117c478bd9Sstevel@tonic-gate 			    string, USB_MAXSTRINGLEN);
10127c478bd9Sstevel@tonic-gate 		}
10137c478bd9Sstevel@tonic-gate 		if (string[0] == '\0') {
10147c478bd9Sstevel@tonic-gate 			(void) strcpy(string, "<none>");
10157c478bd9Sstevel@tonic-gate 		}
10167c478bd9Sstevel@tonic-gate 		state->st_curr_alt->altif_strsize = strlen(string) + 1;
10177c478bd9Sstevel@tonic-gate 		state->st_curr_alt->altif_str = kmem_zalloc(
10187c478bd9Sstevel@tonic-gate 		    state->st_curr_alt->altif_strsize, KM_SLEEP);
10197c478bd9Sstevel@tonic-gate 		(void) strcpy(state->st_curr_alt->altif_str, string);
10207c478bd9Sstevel@tonic-gate 		kmem_free(string, USB_MAXSTRINGLEN);
10217c478bd9Sstevel@tonic-gate 	}
10227c478bd9Sstevel@tonic-gate 
10237c478bd9Sstevel@tonic-gate 	kmem_free(new_if_descr, sizeof (usb_if_descr_t));
10247c478bd9Sstevel@tonic-gate 
10257c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
10267c478bd9Sstevel@tonic-gate 	    "usba_process_if_descr done");
10277c478bd9Sstevel@tonic-gate 
10287c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
10297c478bd9Sstevel@tonic-gate }
10307c478bd9Sstevel@tonic-gate 
10317c478bd9Sstevel@tonic-gate 
10327c478bd9Sstevel@tonic-gate /*
10337c478bd9Sstevel@tonic-gate  * usba_process_ep_descr:
10347c478bd9Sstevel@tonic-gate  *	This processes a raw endpoint descriptor, and sets up an analogous
10357c478bd9Sstevel@tonic-gate  *	endpoint descriptor node in the descriptor tree.
10367c478bd9Sstevel@tonic-gate  *
10377c478bd9Sstevel@tonic-gate  * Arguments:
10387c478bd9Sstevel@tonic-gate  *	state		- Pointer to this module's state structure.
10397c478bd9Sstevel@tonic-gate  *
10407c478bd9Sstevel@tonic-gate  * Returns:
10417c478bd9Sstevel@tonic-gate  *	USB_SUCCESS:	Descriptor is successfully parsed.
10427c478bd9Sstevel@tonic-gate  *	USB_FAILURE:	Descriptor is inappropriately placed in config cloud.
10437c478bd9Sstevel@tonic-gate  */
10447c478bd9Sstevel@tonic-gate static int
usba_process_ep_descr(usba_reg_state_t * state)10457c478bd9Sstevel@tonic-gate usba_process_ep_descr(usba_reg_state_t *state)
10467c478bd9Sstevel@tonic-gate {
10477c478bd9Sstevel@tonic-gate 	usb_alt_if_data_t *curr_alt = state->st_curr_alt;
10487c478bd9Sstevel@tonic-gate 
10497c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
10507c478bd9Sstevel@tonic-gate 	    "usba_process_ep_descr starting");
10517c478bd9Sstevel@tonic-gate 
10527c478bd9Sstevel@tonic-gate 	/* No interface preceeds this endpoint. */
10537c478bd9Sstevel@tonic-gate 	if (state->st_curr_alt == NULL) {
10547c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
10557c478bd9Sstevel@tonic-gate 		    "usba_process_ep_descr: no requested alt before endpt.");
10567c478bd9Sstevel@tonic-gate 
10577c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
10587c478bd9Sstevel@tonic-gate 	}
10597c478bd9Sstevel@tonic-gate 
10607c478bd9Sstevel@tonic-gate 	usba_augment_array((void **)(&curr_alt->altif_ep),
1061112116d8Sfb 	    curr_alt->altif_n_ep, sizeof (usb_ep_data_t));
10627c478bd9Sstevel@tonic-gate 
10637c478bd9Sstevel@tonic-gate 	/* Ptr to the current endpt, may be used to attach a c/v to it. */
10647c478bd9Sstevel@tonic-gate 	state->st_curr_ep = &curr_alt->altif_ep[curr_alt->altif_n_ep++];
10657c478bd9Sstevel@tonic-gate 
10667c478bd9Sstevel@tonic-gate 	(void) usb_parse_data("4csc", state->st_curr_raw_descr,
1067112116d8Sfb 	    state->st_curr_raw_descr_len,
1068112116d8Sfb 	    &state->st_curr_ep->ep_descr, sizeof (usb_ep_descr_t));
10697c478bd9Sstevel@tonic-gate 
10707c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
10717c478bd9Sstevel@tonic-gate 	    "usba_process_ep_descr done");
10727c478bd9Sstevel@tonic-gate 
10737c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
10747c478bd9Sstevel@tonic-gate }
10757c478bd9Sstevel@tonic-gate 
1076993e3fafSRobert Mustacchi /*
1077993e3fafSRobert Mustacchi  * usba_process_ss_ep_comp_descr:
1078993e3fafSRobert Mustacchi  * 	This processes a raw endpoint companion descriptor and associates it
1079993e3fafSRobert Mustacchi  * 	inside of an existing endpoint's entry.
1080993e3fafSRobert Mustacchi  *
1081993e3fafSRobert Mustacchi  * Arguments:
1082993e3fafSRobert Mustacchi  *	state		- Pointer to this module's state structure.
1083993e3fafSRobert Mustacchi  *
1084993e3fafSRobert Mustacchi  * Returns:
1085993e3fafSRobert Mustacchi  *	USB_SUCCESS:	Descriptor is successfully parsed.
1086993e3fafSRobert Mustacchi  *	USB_FAILURE:	Descriptor is inappropriately placed in config cloud.
1087993e3fafSRobert Mustacchi  */
1088993e3fafSRobert Mustacchi static int
usba_process_ss_ep_comp_descr(usba_reg_state_t * state)1089993e3fafSRobert Mustacchi usba_process_ss_ep_comp_descr(usba_reg_state_t *state)
1090993e3fafSRobert Mustacchi {
1091993e3fafSRobert Mustacchi 	if (state->st_curr_ep == NULL)
1092993e3fafSRobert Mustacchi 		return (USB_FAILURE);
1093993e3fafSRobert Mustacchi 
1094993e3fafSRobert Mustacchi 	(void) usb_parse_data("4cs", state->st_curr_raw_descr,
1095993e3fafSRobert Mustacchi 	    state->st_curr_raw_descr_len,
1096993e3fafSRobert Mustacchi 	    &state->st_curr_ep->ep_ss_comp,
1097993e3fafSRobert Mustacchi 	    sizeof (usb_ep_ss_comp_descr_t));
1098993e3fafSRobert Mustacchi 	state->st_curr_ep->ep_ss_valid = B_TRUE;
1099