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 /*
22*112116d8Sfb  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate /*
297c478bd9Sstevel@tonic-gate  * USBA: Solaris USB Architecture support
307c478bd9Sstevel@tonic-gate  *
317c478bd9Sstevel@tonic-gate  * This module builds a tree of parsed USB standard descriptors and unparsed
327c478bd9Sstevel@tonic-gate  * Class/Vendor specific (C/V) descriptors.  Routines are grouped into three
337c478bd9Sstevel@tonic-gate  * groups: those which build the tree, those which take it down, and those which
347c478bd9Sstevel@tonic-gate  * dump it.
357c478bd9Sstevel@tonic-gate  *
367c478bd9Sstevel@tonic-gate  * The tree built hangs off of the dev_cfg field of the usb_client_dev_data_t
377c478bd9Sstevel@tonic-gate  * structure returned by usb_get_dev_data().  The tree consists of different
387c478bd9Sstevel@tonic-gate  * kinds of tree nodes (usb_xxx_data_t) each containing a standard USB
397c478bd9Sstevel@tonic-gate  * descriptor (usb_xxx_descr_t) and pointers to arrays of other nodes.
407c478bd9Sstevel@tonic-gate  *
417c478bd9Sstevel@tonic-gate  * Arrays are dynamically sized, as the descriptors coming from the device may
427c478bd9Sstevel@tonic-gate  * lie, but the number of descriptors from the device is a more reliable
437c478bd9Sstevel@tonic-gate  * indicator of configuration.	This makes the code more robust.  After the raw
447c478bd9Sstevel@tonic-gate  * descriptor data has been parsed into a non-sparse tree, the tree is ordered
457c478bd9Sstevel@tonic-gate  * and made sparse with a bin-sort style algorithm.
467c478bd9Sstevel@tonic-gate  *
477c478bd9Sstevel@tonic-gate  * dev_cfg is an array of configuration tree nodes. Each contains space for one
487c478bd9Sstevel@tonic-gate  * parsed standard USB configuration descriptor, a pointer to an array of c/v
497c478bd9Sstevel@tonic-gate  * tree nodes and a pointer to an array of interface tree nodes.
507c478bd9Sstevel@tonic-gate  *
517c478bd9Sstevel@tonic-gate  * Each interface tree node represents a group of interface descriptors, called
527c478bd9Sstevel@tonic-gate  * alternates, with the same interface number.	Thus, each interface tree node
537c478bd9Sstevel@tonic-gate  * has a pointer to an array of alternate-interface tree nodes each containing a
547c478bd9Sstevel@tonic-gate  * standard USB interface descriptor. Alternate-interface tree nodes also
557c478bd9Sstevel@tonic-gate  * contain a pointer to an array of c/v tree nodes and a pointer to an array of
567c478bd9Sstevel@tonic-gate  * endpoint tree nodes.
577c478bd9Sstevel@tonic-gate  *
587c478bd9Sstevel@tonic-gate  * Endpoint tree nodes contain a standard endpoint descriptor, plus a pointer to
597c478bd9Sstevel@tonic-gate  * an array of c/v tree nodes.
607c478bd9Sstevel@tonic-gate  *
617c478bd9Sstevel@tonic-gate  * Each array in the tree contains elements ranging from 0 to the largest key
627c478bd9Sstevel@tonic-gate  * value of it's elements.  Endpoints are a special case.  The direction bit is
637c478bd9Sstevel@tonic-gate  * right shifted over three unused bits before the index is determined, leaving
647c478bd9Sstevel@tonic-gate  * a range of 0..31 instead of a sparsely-populated range of 0..255.
657c478bd9Sstevel@tonic-gate  *
667c478bd9Sstevel@tonic-gate  * The indices of tree elements coincide with their USB key values.  For
677c478bd9Sstevel@tonic-gate  * example, standard USB devices have no configuration 0;  if they have one
687c478bd9Sstevel@tonic-gate  * configuration it is #1.  dev_cfg[0] is zeroed out;  dev_cfg[1] is the root
697c478bd9Sstevel@tonic-gate  * of configuration #1.
707c478bd9Sstevel@tonic-gate  *
717c478bd9Sstevel@tonic-gate  * The idea here is for a driver to be able to parse the tree to easily find a
727c478bd9Sstevel@tonic-gate  * desired descriptor.	For example, the interval of endpoint 2, alternate 3,
737c478bd9Sstevel@tonic-gate  * interface 1, configuration 1 would be:
747c478bd9Sstevel@tonic-gate  *  dv->dev_cfg[1].cfg_if[1].if_alt[3].altif_ep[2].ep_descr.bInterval
757c478bd9Sstevel@tonic-gate  *
767c478bd9Sstevel@tonic-gate  * How the tree is built:
777c478bd9Sstevel@tonic-gate  *
787c478bd9Sstevel@tonic-gate  * usb_build_descr_tree() is responsible for the whole process.
797c478bd9Sstevel@tonic-gate  *
807c478bd9Sstevel@tonic-gate  * Next, usba_build_descr_tree() coordinates parsing this byte stream,
817c478bd9Sstevel@tonic-gate  * descriptor by descriptor.  usba_build_descr_tree() calls the appropriate
827c478bd9Sstevel@tonic-gate  * usba_process_xx_descr() function to interpret and install each descriptor in
837c478bd9Sstevel@tonic-gate  * the tree, based on the descriptor's type.  When done with this phase, a
847c478bd9Sstevel@tonic-gate  * non-sparse tree exists containing tree nodes with descriptors in the order
857c478bd9Sstevel@tonic-gate  * they were found in the raw data.
867c478bd9Sstevel@tonic-gate  *
877c478bd9Sstevel@tonic-gate  * All levels of the tree, except alternates, remain non-sparse.  Alternates are
887c478bd9Sstevel@tonic-gate  * moved, possibly, within their array, so that descriptors are indexed by their
897c478bd9Sstevel@tonic-gate  * alternate ID.
907c478bd9Sstevel@tonic-gate  *
917c478bd9Sstevel@tonic-gate  * The usba_reg_state_t structure maintains state of the tree-building process,
927c478bd9Sstevel@tonic-gate  * helping coordinate all routines involved.
937c478bd9Sstevel@tonic-gate  */
947c478bd9Sstevel@tonic-gate #define	USBA_FRAMEWORK
957c478bd9Sstevel@tonic-gate #include <sys/usb/usba.h>
967c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usba_impl.h>
977c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usba_private.h>
987c478bd9Sstevel@tonic-gate #include <sys/usb/usba/hcdi_impl.h>
997c478bd9Sstevel@tonic-gate #include <sys/usb/hubd/hub.h>
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usbai_register_impl.h>
1027c478bd9Sstevel@tonic-gate 
1037c478bd9Sstevel@tonic-gate /*
1047c478bd9Sstevel@tonic-gate  * Header needed for use by this module only.
1057c478bd9Sstevel@tonic-gate  * However, function may be used in V0.8 drivers so needs to be global.
1067c478bd9Sstevel@tonic-gate  */
1077c478bd9Sstevel@tonic-gate int usb_log_descr_tree(usb_client_dev_data_t *, usb_log_handle_t,
1087c478bd9Sstevel@tonic-gate 				uint_t, uint_t);
1097c478bd9Sstevel@tonic-gate 
1107c478bd9Sstevel@tonic-gate /* Debug stuff */
1117c478bd9Sstevel@tonic-gate usb_log_handle_t	usbai_reg_log_handle;
1124610e4a0Sfrits uint_t			usbai_register_errlevel = USB_LOG_L2;
1134610e4a0Sfrits uint_t			usbai_register_dump_errlevel = USB_LOG_L2;
1144610e4a0Sfrits uint_t			usbai_register_errmask = (uint_t)-1;
1157c478bd9Sstevel@tonic-gate 
1167c478bd9Sstevel@tonic-gate /* Function prototypes */
1177c478bd9Sstevel@tonic-gate static int usba_build_descr_tree(dev_info_t *, usba_device_t *,
1187c478bd9Sstevel@tonic-gate 				usb_client_dev_data_t *);
1197c478bd9Sstevel@tonic-gate static void usba_process_cfg_descr(usba_reg_state_t *);
1207c478bd9Sstevel@tonic-gate static int usba_process_if_descr(usba_reg_state_t *, boolean_t *);
1217c478bd9Sstevel@tonic-gate static int usba_process_ep_descr(usba_reg_state_t *);
1227c478bd9Sstevel@tonic-gate static int usba_process_cv_descr(usba_reg_state_t *);
1237c478bd9Sstevel@tonic-gate static int usba_set_parse_values(dev_info_t *dip, usba_device_t *usba_device,
1247c478bd9Sstevel@tonic-gate     usba_reg_state_t *state);
1257c478bd9Sstevel@tonic-gate static void* usba_kmem_realloc(void *, int, int);
1267c478bd9Sstevel@tonic-gate static void usba_augment_array(void **, uint_t, uint_t);
1277c478bd9Sstevel@tonic-gate static void usba_make_alts_sparse(usb_alt_if_data_t **, uint_t *);
1287c478bd9Sstevel@tonic-gate 
1297c478bd9Sstevel@tonic-gate static void usba_order_tree(usba_reg_state_t *);
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate static void usba_free_if_array(usb_if_data_t *, uint_t);
1327c478bd9Sstevel@tonic-gate static void usba_free_ep_array(usb_ep_data_t *, uint_t);
1337c478bd9Sstevel@tonic-gate static void usba_free_cv_array(usb_cvs_data_t *, uint_t);
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate static int usba_dump_descr_tree(dev_info_t *, usb_client_dev_data_t *,
1367c478bd9Sstevel@tonic-gate 				usb_log_handle_t, uint_t, uint_t);
1377c478bd9Sstevel@tonic-gate static void usba_dump_if(usb_if_data_t *, usb_log_handle_t,
1387c478bd9Sstevel@tonic-gate 				uint_t, uint_t, char *);
1397c478bd9Sstevel@tonic-gate static void usba_dump_ep(uint_t, usb_ep_data_t *, usb_log_handle_t, uint_t,
1407c478bd9Sstevel@tonic-gate 				uint_t, char *);
1417c478bd9Sstevel@tonic-gate static void usba_dump_cv(usb_cvs_data_t *, usb_log_handle_t, uint_t, uint_t,
1427c478bd9Sstevel@tonic-gate 				char *, int);
1437c478bd9Sstevel@tonic-gate static void usba_dump_bin(uint8_t *, int, int, usb_log_handle_t,
1447c478bd9Sstevel@tonic-gate 				uint_t,  uint_t, char *, int);
1457c478bd9Sstevel@tonic-gate 
1467c478bd9Sstevel@tonic-gate /* Framework initialization. */
1477c478bd9Sstevel@tonic-gate void
1487c478bd9Sstevel@tonic-gate usba_usbai_register_initialization()
1497c478bd9Sstevel@tonic-gate {
1507c478bd9Sstevel@tonic-gate 	usbai_reg_log_handle = usb_alloc_log_hdl(NULL, "usbreg",
151*112116d8Sfb 	    &usbai_register_errlevel,
152*112116d8Sfb 	    &usbai_register_errmask, NULL,
153*112116d8Sfb 	    0);
1547c478bd9Sstevel@tonic-gate 
1557c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
156*112116d8Sfb 	    "usba_usbai_register_initialization");
1577c478bd9Sstevel@tonic-gate }
1587c478bd9Sstevel@tonic-gate 
1597c478bd9Sstevel@tonic-gate 
1607c478bd9Sstevel@tonic-gate /* Framework destruction. */
1617c478bd9Sstevel@tonic-gate void
1627c478bd9Sstevel@tonic-gate usba_usbai_register_destroy()
1637c478bd9Sstevel@tonic-gate {
1647c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
1657c478bd9Sstevel@tonic-gate 	    "usba_usbai_register destroy");
1667c478bd9Sstevel@tonic-gate 
1677c478bd9Sstevel@tonic-gate 	usb_free_log_hdl(usbai_reg_log_handle);
1687c478bd9Sstevel@tonic-gate }
1697c478bd9Sstevel@tonic-gate 
1707c478bd9Sstevel@tonic-gate 
1717c478bd9Sstevel@tonic-gate /*
1727c478bd9Sstevel@tonic-gate  * usb_client_attach:
1737c478bd9Sstevel@tonic-gate  *
1747c478bd9Sstevel@tonic-gate  * Arguments:
1757c478bd9Sstevel@tonic-gate  *	dip		- pointer to devinfo node of the client
1767c478bd9Sstevel@tonic-gate  *	version 	- USBA registration version number
1777c478bd9Sstevel@tonic-gate  *	flags		- None used
1787c478bd9Sstevel@tonic-gate  *
1797c478bd9Sstevel@tonic-gate  * Return Values:
1807c478bd9Sstevel@tonic-gate  *	USB_SUCCESS		- attach succeeded
1817c478bd9Sstevel@tonic-gate  *	USB_INVALID_ARGS	- received null dip
1827c478bd9Sstevel@tonic-gate  *	USB_INVALID_VERSION	- version argument is incorrect.
1837c478bd9Sstevel@tonic-gate  *	USB_FAILURE		- other internal failure
1847c478bd9Sstevel@tonic-gate  */
1857c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1867c478bd9Sstevel@tonic-gate int
1877c478bd9Sstevel@tonic-gate usb_client_attach(dev_info_t *dip, uint_t version, usb_flags_t flags)
1887c478bd9Sstevel@tonic-gate {
1897c478bd9Sstevel@tonic-gate 	int rval;
1907c478bd9Sstevel@tonic-gate 	usba_device_t *usba_device;
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate 	if (dip == NULL) {
1937c478bd9Sstevel@tonic-gate 
1947c478bd9Sstevel@tonic-gate 		return (USB_INVALID_ARGS);
1957c478bd9Sstevel@tonic-gate 	}
1967c478bd9Sstevel@tonic-gate 
1977c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
1987c478bd9Sstevel@tonic-gate 	    "usb_client attach:");
1997c478bd9Sstevel@tonic-gate 
2007c478bd9Sstevel@tonic-gate 	usba_device = usba_get_usba_device(dip);
2017c478bd9Sstevel@tonic-gate 
2027c478bd9Sstevel@tonic-gate 	/*
2037c478bd9Sstevel@tonic-gate 	 * Allow exact match for legacy (DDK 0.8/9) drivers, or same major
2047c478bd9Sstevel@tonic-gate 	 * VERSion and smaller or same minor version for non-legacy drivers.
2057c478bd9Sstevel@tonic-gate 	 */
2067c478bd9Sstevel@tonic-gate 	if ((version !=
2077c478bd9Sstevel@tonic-gate 	    USBA_MAKE_VER(USBA_LEG_MAJOR_VER, USBA_LEG_MINOR_VER)) &&
2087c478bd9Sstevel@tonic-gate 	    ((USBA_GET_MAJOR(version) != USBA_MAJOR_VER) ||
2097c478bd9Sstevel@tonic-gate 	    (USBA_GET_MINOR(version) > USBA_MINOR_VER))) {
2107c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L1(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
2117c478bd9Sstevel@tonic-gate 		    "Incorrect USB driver version for %s%d: found: %d.%d, "
2127c478bd9Sstevel@tonic-gate 		    "expecting %d.%d",
2137c478bd9Sstevel@tonic-gate 		    ddi_driver_name(dip), ddi_get_instance(dip),
2147c478bd9Sstevel@tonic-gate 		    USBA_GET_MAJOR(version), USBA_GET_MINOR(version),
2157c478bd9Sstevel@tonic-gate 		    USBA_MAJOR_VER, USBA_MINOR_VER);
2167c478bd9Sstevel@tonic-gate 
2177c478bd9Sstevel@tonic-gate 		return (USB_INVALID_VERSION);
2187c478bd9Sstevel@tonic-gate 	}
2197c478bd9Sstevel@tonic-gate 
2207c478bd9Sstevel@tonic-gate 	if (version == USBA_MAKE_VER(USBA_LEG_MAJOR_VER, USBA_LEG_MINOR_VER)) {
221d291d9f2Sfrits 		USB_DPRINTF_L2(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
2227c478bd9Sstevel@tonic-gate 		    "Accepting legacy USB driver version %d.%d for %s%d",
2237c478bd9Sstevel@tonic-gate 		    USBA_LEG_MAJOR_VER, USBA_LEG_MINOR_VER,
2247c478bd9Sstevel@tonic-gate 		    ddi_driver_name(dip), ddi_get_instance(dip));
2257c478bd9Sstevel@tonic-gate 	}
2267c478bd9Sstevel@tonic-gate 
2277c478bd9Sstevel@tonic-gate 	rval = ndi_prop_update_int(DDI_DEV_T_NONE, dip, "driver-major",
228*112116d8Sfb 	    USBA_GET_MAJOR(version));
2297c478bd9Sstevel@tonic-gate 	if (rval != DDI_PROP_SUCCESS) {
2307c478bd9Sstevel@tonic-gate 
2317c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
2327c478bd9Sstevel@tonic-gate 	}
2337c478bd9Sstevel@tonic-gate 	rval = ndi_prop_update_int(DDI_DEV_T_NONE, dip, "driver-minor",
234*112116d8Sfb 	    USBA_GET_MINOR(version));
2357c478bd9Sstevel@tonic-gate 	if (rval != DDI_PROP_SUCCESS) {
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
2387c478bd9Sstevel@tonic-gate 	}
2397c478bd9Sstevel@tonic-gate 
2407c478bd9Sstevel@tonic-gate 	mutex_enter(&usba_device->usb_mutex);
2417c478bd9Sstevel@tonic-gate 	if (strcmp(ddi_driver_name(dip), "usb_mid") != 0) {
2427c478bd9Sstevel@tonic-gate 		usba_device->usb_client_flags[usba_get_ifno(dip)] |=
243*112116d8Sfb 		    USBA_CLIENT_FLAG_ATTACH;
2447c478bd9Sstevel@tonic-gate 		usba_device->usb_client_attach_list->dip = dip;
2457c478bd9Sstevel@tonic-gate 	}
2467c478bd9Sstevel@tonic-gate 	mutex_exit(&usba_device->usb_mutex);
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
2497c478bd9Sstevel@tonic-gate 	    "usb_client attach: done");
2507c478bd9Sstevel@tonic-gate 
2517c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
2527c478bd9Sstevel@tonic-gate }
2537c478bd9Sstevel@tonic-gate 
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate /*
2567c478bd9Sstevel@tonic-gate  * usb_client_detach:
2577c478bd9Sstevel@tonic-gate  *	free dev_data is reg != NULL, not much else to do
2587c478bd9Sstevel@tonic-gate  *
2597c478bd9Sstevel@tonic-gate  * Arguments:
2607c478bd9Sstevel@tonic-gate  *	dip		- pointer to devinfo node of the client
2617c478bd9Sstevel@tonic-gate  *	reg		- return registration data at this address
2627c478bd9Sstevel@tonic-gate  */
2637c478bd9Sstevel@tonic-gate void
2647c478bd9Sstevel@tonic-gate usb_client_detach(dev_info_t *dip, usb_client_dev_data_t *reg)
2657c478bd9Sstevel@tonic-gate {
2667c478bd9Sstevel@tonic-gate 	usba_device_t *usba_device = usba_get_usba_device(dip);
2677c478bd9Sstevel@tonic-gate 
2687c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
2697c478bd9Sstevel@tonic-gate 	    "usb_client_detach:");
2707c478bd9Sstevel@tonic-gate 
2717c478bd9Sstevel@tonic-gate 	if (dip) {
2727c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
2737c478bd9Sstevel@tonic-gate 		    "Unregistering usb client %s%d: reg=0x%p",
274*112116d8Sfb 		    ddi_driver_name(dip), ddi_get_instance(dip), (void *)reg);
2757c478bd9Sstevel@tonic-gate 
2767c478bd9Sstevel@tonic-gate 		usb_free_dev_data(dip, reg);
2777c478bd9Sstevel@tonic-gate 
2787c478bd9Sstevel@tonic-gate 		mutex_enter(&usba_device->usb_mutex);
2797c478bd9Sstevel@tonic-gate 		if (strcmp(ddi_driver_name(dip), "usb_mid") != 0) {
2807c478bd9Sstevel@tonic-gate 			usba_device->usb_client_flags[usba_get_ifno(dip)] &=
281*112116d8Sfb 			    ~USBA_CLIENT_FLAG_ATTACH;
2827c478bd9Sstevel@tonic-gate 		}
2837c478bd9Sstevel@tonic-gate 		mutex_exit(&usba_device->usb_mutex);
2847c478bd9Sstevel@tonic-gate 	}
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
2877c478bd9Sstevel@tonic-gate 	    "usb_client_detach done");
2887c478bd9Sstevel@tonic-gate }
2897c478bd9Sstevel@tonic-gate 
2907c478bd9Sstevel@tonic-gate 
2917c478bd9Sstevel@tonic-gate /*
2927c478bd9Sstevel@tonic-gate  * usb_register_client (deprecated):
2937c478bd9Sstevel@tonic-gate  *	The client registers with USBA during attach.
2947c478bd9Sstevel@tonic-gate  */
2957c478bd9Sstevel@tonic-gate /*ARGSUSED*/
2967c478bd9Sstevel@tonic-gate int
2977c478bd9Sstevel@tonic-gate usb_register_client(dev_info_t *dip, uint_t version,
2987c478bd9Sstevel@tonic-gate     usb_client_dev_data_t **reg, usb_reg_parse_lvl_t parse_level,
2997c478bd9Sstevel@tonic-gate     usb_flags_t flags)
3007c478bd9Sstevel@tonic-gate {
3017c478bd9Sstevel@tonic-gate 	int rval = usb_client_attach(dip, version, flags);
3027c478bd9Sstevel@tonic-gate 
3037c478bd9Sstevel@tonic-gate 	if (rval == USB_SUCCESS) {
3047c478bd9Sstevel@tonic-gate 		rval = usb_get_dev_data(dip, reg, parse_level, flags);
3057c478bd9Sstevel@tonic-gate 
3067c478bd9Sstevel@tonic-gate 		if (rval != USB_SUCCESS) {
3077c478bd9Sstevel@tonic-gate 			usb_client_detach(dip, NULL);
3087c478bd9Sstevel@tonic-gate 		}
3097c478bd9Sstevel@tonic-gate 	}
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate 	return (rval);
3127c478bd9Sstevel@tonic-gate }
3137c478bd9Sstevel@tonic-gate 
3147c478bd9Sstevel@tonic-gate 
3157c478bd9Sstevel@tonic-gate /*
3167c478bd9Sstevel@tonic-gate  * usb_unregister_client (deprecated):
3177c478bd9Sstevel@tonic-gate  *	Undo the makings of usb_get_dev_data().  Free memory if allocated.
3187c478bd9Sstevel@tonic-gate  *
3197c478bd9Sstevel@tonic-gate  * Arguments:
3207c478bd9Sstevel@tonic-gate  *	dip	- pointer to devinfo node of the client
3217c478bd9Sstevel@tonic-gate  *	reg	- pointer to registration data to be freed
3227c478bd9Sstevel@tonic-gate  */
3237c478bd9Sstevel@tonic-gate void
3247c478bd9Sstevel@tonic-gate usb_unregister_client(dev_info_t *dip, usb_client_dev_data_t *reg)
3257c478bd9Sstevel@tonic-gate {
3267c478bd9Sstevel@tonic-gate 	usb_client_detach(dip, reg);
3277c478bd9Sstevel@tonic-gate }
3287c478bd9Sstevel@tonic-gate 
3297c478bd9Sstevel@tonic-gate 
3307c478bd9Sstevel@tonic-gate /*
3317c478bd9Sstevel@tonic-gate  * usb_get_dev_data:
3327c478bd9Sstevel@tonic-gate  *	On completion, the registration data has been initialized.
3337c478bd9Sstevel@tonic-gate  *	Most data items are straightforward.
3347c478bd9Sstevel@tonic-gate  *	Among the items returned in the data is the tree of
3357c478bd9Sstevel@tonic-gate  *	parsed descriptors, in dev_cfg;	 the number of configurations parsed,
3367c478bd9Sstevel@tonic-gate  *	in dev_n_cfg; a pointer to the current configuration in the tree,
3377c478bd9Sstevel@tonic-gate  *	in dev_curr_cfg; the index of the first valid interface in the
3387c478bd9Sstevel@tonic-gate  *	tree, in dev_curr_if, and a parse level that accurately reflects what
3397c478bd9Sstevel@tonic-gate  *	is in the tree, in dev_parse_level.
3407c478bd9Sstevel@tonic-gate  *
3417c478bd9Sstevel@tonic-gate  *	This routine sets up directly-initialized fields, and calls
3427c478bd9Sstevel@tonic-gate  *	usb_build_descr_tree() to parse the raw descriptors and initialize the
3437c478bd9Sstevel@tonic-gate  *	tree.
3447c478bd9Sstevel@tonic-gate  *
3457c478bd9Sstevel@tonic-gate  *	Parse_level determines the extent to which the tree is built.  It has
3467c478bd9Sstevel@tonic-gate  *	the following values:
3477c478bd9Sstevel@tonic-gate  *
3487c478bd9Sstevel@tonic-gate  *	USB_PARSE_LVL_NONE - Build no tree.  dev_n_cfg will return 0, dev_cfg
3497c478bd9Sstevel@tonic-gate  *			     and dev_curr_cfg will return NULL.
3507c478bd9Sstevel@tonic-gate  *	USB_PARSE_LVL_IF   - Parse configured interface only, if configuration#
3517c478bd9Sstevel@tonic-gate  *			     and interface properties are set (as when different
3527c478bd9Sstevel@tonic-gate  *			     interfaces are viewed by the OS as different device
3537c478bd9Sstevel@tonic-gate  *			     instances). If an OS device instance is set up to
3547c478bd9Sstevel@tonic-gate  *			     represent an entire physical device, this works
3557c478bd9Sstevel@tonic-gate  *			     like USB_PARSE_LVL_ALL.
3567c478bd9Sstevel@tonic-gate  *	USB_PARSE_LVL_CFG  - Parse entire configuration of configured interface
3577c478bd9Sstevel@tonic-gate  *			     only.  This is like USB_PARSE_LVL_IF except entire
3587c478bd9Sstevel@tonic-gate  *			     configuration is returned.
3597c478bd9Sstevel@tonic-gate  *	USB_PARSE_LVL_ALL  - Parse entire device (all configurations), even
3607c478bd9Sstevel@tonic-gate  *			     when driver is bound to a single interface of a
3617c478bd9Sstevel@tonic-gate  *			     single configuration.
3627c478bd9Sstevel@tonic-gate  *
3637c478bd9Sstevel@tonic-gate  *	No tree is built for root hubs, regardless of parse_level.
3647c478bd9Sstevel@tonic-gate  *
3657c478bd9Sstevel@tonic-gate  * Arguments:
3667c478bd9Sstevel@tonic-gate  *	dip		- pointer to devinfo node of the client
3677c478bd9Sstevel@tonic-gate  *	version		- USBA registration version number
3687c478bd9Sstevel@tonic-gate  *	reg		- return registration data at this address
3697c478bd9Sstevel@tonic-gate  *	parse_level	- See above
3707c478bd9Sstevel@tonic-gate  *	flags		- None used
3717c478bd9Sstevel@tonic-gate  *
3727c478bd9Sstevel@tonic-gate  * Return Values:
3737c478bd9Sstevel@tonic-gate  *	USB_SUCCESS		- usb_get_dev_data succeeded
3747c478bd9Sstevel@tonic-gate  *	USB_INVALID_ARGS	- received null dip or reg argument
3757c478bd9Sstevel@tonic-gate  *	USB_INVALID_CONTEXT	- called from callback context
3767c478bd9Sstevel@tonic-gate  *	USB_FAILURE		- bad descriptor info or other internal failure
3777c478bd9Sstevel@tonic-gate  *
3787c478bd9Sstevel@tonic-gate  * Note: The non-standard USB descriptors are returned in RAW format.
3797c478bd9Sstevel@tonic-gate  *	returns initialized registration data.	Most data items are clear.
3807c478bd9Sstevel@tonic-gate  *	Among the items returned is the tree of parsed descriptors in dev_cfg;
3817c478bd9Sstevel@tonic-gate  *	and the number of configurations parsed in dev_n_cfg.
3827c478bd9Sstevel@tonic-gate  *
3837c478bd9Sstevel@tonic-gate  *	The registration data is not shared. each client receives its own
3847c478bd9Sstevel@tonic-gate  *	copy.
3857c478bd9Sstevel@tonic-gate  */
3867c478bd9Sstevel@tonic-gate /*ARGSUSED*/
3877c478bd9Sstevel@tonic-gate int
3887c478bd9Sstevel@tonic-gate usb_get_dev_data(dev_info_t *dip,
3897c478bd9Sstevel@tonic-gate     usb_client_dev_data_t **reg, usb_reg_parse_lvl_t parse_level,
3907c478bd9Sstevel@tonic-gate     usb_flags_t flags)
3917c478bd9Sstevel@tonic-gate {
3927c478bd9Sstevel@tonic-gate 	usb_client_dev_data_t	*usb_reg = NULL;
3937c478bd9Sstevel@tonic-gate 	char			*tmpbuf = NULL;
3947c478bd9Sstevel@tonic-gate 	usba_device_t		*usba_device;
3957c478bd9Sstevel@tonic-gate 	int			rval = USB_SUCCESS;
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate 	if ((dip == NULL) || (reg == NULL)) {
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate 		return (USB_INVALID_ARGS);
4007c478bd9Sstevel@tonic-gate 	}
4017c478bd9Sstevel@tonic-gate 
4027c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
4037c478bd9Sstevel@tonic-gate 	    "usb_get_dev_data: %s%d",
4047c478bd9Sstevel@tonic-gate 	    ddi_driver_name(dip), ddi_get_instance(dip));
4057c478bd9Sstevel@tonic-gate 
4067c478bd9Sstevel@tonic-gate 	*reg = NULL;
4077c478bd9Sstevel@tonic-gate 
4087c478bd9Sstevel@tonic-gate 	/* did the client attach first? */
4097c478bd9Sstevel@tonic-gate 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
4107c478bd9Sstevel@tonic-gate 	    "driver-major", -1) == -1) {
4117c478bd9Sstevel@tonic-gate 
4127c478bd9Sstevel@tonic-gate 		return (USB_INVALID_VERSION);
4137c478bd9Sstevel@tonic-gate 	}
4147c478bd9Sstevel@tonic-gate 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
4157c478bd9Sstevel@tonic-gate 	    "driver-minor", -1) == -1) {
4167c478bd9Sstevel@tonic-gate 
4177c478bd9Sstevel@tonic-gate 		return (USB_INVALID_VERSION);
4187c478bd9Sstevel@tonic-gate 	}
4197c478bd9Sstevel@tonic-gate 
4207c478bd9Sstevel@tonic-gate 	usb_reg = kmem_zalloc(sizeof (usb_client_dev_data_t), KM_SLEEP);
4217c478bd9Sstevel@tonic-gate 	usba_device = usba_get_usba_device(dip);
4227c478bd9Sstevel@tonic-gate 	usb_reg->dev_descr = usba_device->usb_dev_descr;
4237c478bd9Sstevel@tonic-gate 	usb_reg->dev_default_ph = usba_get_dflt_pipe_handle(dip);
4247c478bd9Sstevel@tonic-gate 	if (usb_reg->dev_default_ph == NULL) {
4257c478bd9Sstevel@tonic-gate 		kmem_free(usb_reg, sizeof (usb_client_dev_data_t));
4267c478bd9Sstevel@tonic-gate 
4277c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
4287c478bd9Sstevel@tonic-gate 	}
4297c478bd9Sstevel@tonic-gate 
4307c478bd9Sstevel@tonic-gate 	usb_reg->dev_iblock_cookie = usba_hcdi_get_hcdi(
4317c478bd9Sstevel@tonic-gate 	    usba_device->usb_root_hub_dip)->hcdi_soft_iblock_cookie;
4327c478bd9Sstevel@tonic-gate 
4337c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
434*112116d8Sfb 	    "cookie = 0x%p", (void *)usb_reg->dev_iblock_cookie);
4357c478bd9Sstevel@tonic-gate 
4367c478bd9Sstevel@tonic-gate 	tmpbuf = (char *)kmem_zalloc(USB_MAXSTRINGLEN, KM_SLEEP);
4377c478bd9Sstevel@tonic-gate 
4387c478bd9Sstevel@tonic-gate 	if (usba_device->usb_mfg_str != NULL) {
4397c478bd9Sstevel@tonic-gate 		usb_reg->dev_mfg = kmem_zalloc(
440*112116d8Sfb 		    strlen(usba_device->usb_mfg_str) + 1, KM_SLEEP);
4417c478bd9Sstevel@tonic-gate 		(void) strcpy(usb_reg->dev_mfg, usba_device->usb_mfg_str);
4427c478bd9Sstevel@tonic-gate 	}
4437c478bd9Sstevel@tonic-gate 
4447c478bd9Sstevel@tonic-gate 	if (usba_device->usb_product_str != NULL) {
4457c478bd9Sstevel@tonic-gate 		usb_reg->dev_product = kmem_zalloc(
446*112116d8Sfb 		    strlen(usba_device->usb_product_str) + 1,
447*112116d8Sfb 		    KM_SLEEP);
4487c478bd9Sstevel@tonic-gate 		(void) strcpy(usb_reg->dev_product,
449*112116d8Sfb 		    usba_device->usb_product_str);
4507c478bd9Sstevel@tonic-gate 	}
4517c478bd9Sstevel@tonic-gate 
4527c478bd9Sstevel@tonic-gate 	if (usba_device->usb_serialno_str != NULL) {
4537c478bd9Sstevel@tonic-gate 		usb_reg->dev_serial = kmem_zalloc(
454*112116d8Sfb 		    strlen(usba_device->usb_serialno_str) + 1,
455*112116d8Sfb 		    KM_SLEEP);
4567c478bd9Sstevel@tonic-gate 		(void) strcpy(usb_reg->dev_serial,
457*112116d8Sfb 		    usba_device->usb_serialno_str);
4587c478bd9Sstevel@tonic-gate 	}
4597c478bd9Sstevel@tonic-gate 
4607c478bd9Sstevel@tonic-gate 	if ((usb_reg->dev_parse_level = parse_level) == USB_PARSE_LVL_NONE) {
4617c478bd9Sstevel@tonic-gate 		rval = USB_SUCCESS;
4627c478bd9Sstevel@tonic-gate 
4637c478bd9Sstevel@tonic-gate 	} else if ((rval = usba_build_descr_tree(dip, usba_device, usb_reg)) !=
4647c478bd9Sstevel@tonic-gate 	    USB_SUCCESS) {
4657c478bd9Sstevel@tonic-gate 		usb_unregister_client(dip, usb_reg);
4667c478bd9Sstevel@tonic-gate 		usb_reg = NULL;
4677c478bd9Sstevel@tonic-gate 	} else {
4687c478bd9Sstevel@tonic-gate 
4697c478bd9Sstevel@tonic-gate 		/* Current tree cfg is always zero if only one cfg in tree. */
4707c478bd9Sstevel@tonic-gate 		if (usb_reg->dev_n_cfg == 1) {
4717c478bd9Sstevel@tonic-gate 			usb_reg->dev_curr_cfg = &usb_reg->dev_cfg[0];
4727c478bd9Sstevel@tonic-gate 		} else {
4737c478bd9Sstevel@tonic-gate 			mutex_enter(&usba_device->usb_mutex);
4747c478bd9Sstevel@tonic-gate 			usb_reg->dev_curr_cfg =
4757c478bd9Sstevel@tonic-gate 			    &usb_reg->dev_cfg[usba_device->usb_active_cfg_ndx];
4767c478bd9Sstevel@tonic-gate 			mutex_exit(&usba_device->usb_mutex);
4777c478bd9Sstevel@tonic-gate 			ASSERT(usb_reg->dev_curr_cfg != NULL);
4787c478bd9Sstevel@tonic-gate 			ASSERT(usb_reg->dev_curr_cfg->cfg_descr.bLength ==
4797c478bd9Sstevel@tonic-gate 			    USB_CFG_DESCR_SIZE);
4807c478bd9Sstevel@tonic-gate 		}
4817c478bd9Sstevel@tonic-gate 
4827c478bd9Sstevel@tonic-gate 		/*
4837c478bd9Sstevel@tonic-gate 		 * Keep dev_curr_if at device's single interface only if that
4847c478bd9Sstevel@tonic-gate 		 * particular interface has been explicitly defined by the
4857c478bd9Sstevel@tonic-gate 		 * device.
4867c478bd9Sstevel@tonic-gate 		 */
4877c478bd9Sstevel@tonic-gate 		usb_reg->dev_curr_if = usba_get_ifno(dip);
4887c478bd9Sstevel@tonic-gate #ifdef DEBUG
4897c478bd9Sstevel@tonic-gate 		(void) usb_log_descr_tree(usb_reg, usbai_reg_log_handle,
490*112116d8Sfb 		    usbai_register_dump_errlevel, (uint_t)-1);
4917c478bd9Sstevel@tonic-gate #endif
4927c478bd9Sstevel@tonic-gate 		/*
4937c478bd9Sstevel@tonic-gate 		 * Fail if interface and configuration of dev_curr_if and
4947c478bd9Sstevel@tonic-gate 		 * dev_curr_cfg don't exist or are invalid.  (Shouldn't happen.)
4957c478bd9Sstevel@tonic-gate 		 * These indices must be reliable for tree traversal.
4967c478bd9Sstevel@tonic-gate 		 */
4977c478bd9Sstevel@tonic-gate 		if ((usb_reg->dev_curr_cfg->cfg_n_if <= usb_reg->dev_curr_if) ||
4987c478bd9Sstevel@tonic-gate 		    (usb_reg->dev_curr_cfg->cfg_descr.bLength == 0) ||
4997c478bd9Sstevel@tonic-gate 		    (usb_reg->dev_curr_cfg->cfg_if[usb_reg->dev_curr_if].
5007c478bd9Sstevel@tonic-gate 		    if_n_alt == 0)) {
5017c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_MASK_ALL, usbai_reg_log_handle,
5027c478bd9Sstevel@tonic-gate 			    "usb_get_dev_data: dev_curr_cfg or "
5037c478bd9Sstevel@tonic-gate 			    "dev_curr_if have no descriptors");
5047c478bd9Sstevel@tonic-gate 			usb_unregister_client(dip, usb_reg);
5057c478bd9Sstevel@tonic-gate 			usb_reg = NULL;
5067c478bd9Sstevel@tonic-gate 			rval = USB_FAILURE;
5077c478bd9Sstevel@tonic-gate 		}
5087c478bd9Sstevel@tonic-gate 	}
5097c478bd9Sstevel@tonic-gate 
5107c478bd9Sstevel@tonic-gate 	*reg = usb_reg;
5117c478bd9Sstevel@tonic-gate 	kmem_free(tmpbuf, USB_MAXSTRINGLEN);
5127c478bd9Sstevel@tonic-gate 
5137c478bd9Sstevel@tonic-gate 	if (rval == USB_SUCCESS) {
5147c478bd9Sstevel@tonic-gate 		usb_client_dev_data_list_t *entry = kmem_zalloc(
515*112116d8Sfb 		    sizeof (*entry), KM_SLEEP);
5167c478bd9Sstevel@tonic-gate 		mutex_enter(&usba_device->usb_mutex);
5177c478bd9Sstevel@tonic-gate 
5187c478bd9Sstevel@tonic-gate 		usba_device->usb_client_flags[usba_get_ifno(dip)] |=
519*112116d8Sfb 		    USBA_CLIENT_FLAG_DEV_DATA;
5207c478bd9Sstevel@tonic-gate 
5217c478bd9Sstevel@tonic-gate 		entry->cddl_dip = dip;
5227c478bd9Sstevel@tonic-gate 		entry->cddl_dev_data = usb_reg;
5237c478bd9Sstevel@tonic-gate 		entry->cddl_ifno = usba_get_ifno(dip);
5247c478bd9Sstevel@tonic-gate 
5257c478bd9Sstevel@tonic-gate 		entry->cddl_next =
526*112116d8Sfb 		    usba_device->usb_client_dev_data_list.cddl_next;
5277c478bd9Sstevel@tonic-gate 		if (entry->cddl_next) {
5287c478bd9Sstevel@tonic-gate 			entry->cddl_next->cddl_prev = entry;
5297c478bd9Sstevel@tonic-gate 		}
5307c478bd9Sstevel@tonic-gate 		entry->cddl_prev = &usba_device->usb_client_dev_data_list;
5317c478bd9Sstevel@tonic-gate 		usba_device->usb_client_dev_data_list.cddl_next = entry;
5327c478bd9Sstevel@tonic-gate 
5337c478bd9Sstevel@tonic-gate 		mutex_exit(&usba_device->usb_mutex);
5347c478bd9Sstevel@tonic-gate 	}
5357c478bd9Sstevel@tonic-gate 
5367c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
5377c478bd9Sstevel@tonic-gate 	    "usb_get_dev_data rval=%d", rval);
5387c478bd9Sstevel@tonic-gate 
5397c478bd9Sstevel@tonic-gate 	return (rval);
5407c478bd9Sstevel@tonic-gate }
5417c478bd9Sstevel@tonic-gate 
5427c478bd9Sstevel@tonic-gate 
5437c478bd9Sstevel@tonic-gate /*
5447c478bd9Sstevel@tonic-gate  * usb_free_dev_data
5457c478bd9Sstevel@tonic-gate  *	undoes what usb_get_dev_data does
5467c478bd9Sstevel@tonic-gate  *
5477c478bd9Sstevel@tonic-gate  * Arguments:
5487c478bd9Sstevel@tonic-gate  *	dip		- pointer to devinfo node of the client
5497c478bd9Sstevel@tonic-gate  *	reg		- return registration data at this address
5507c478bd9Sstevel@tonic-gate  */
5517c478bd9Sstevel@tonic-gate void
5527c478bd9Sstevel@tonic-gate usb_free_dev_data(dev_info_t *dip, usb_client_dev_data_t *reg)
5537c478bd9Sstevel@tonic-gate {
5547c478bd9Sstevel@tonic-gate 	if (dip == NULL) {
5557c478bd9Sstevel@tonic-gate 
5567c478bd9Sstevel@tonic-gate 		return;
5577c478bd9Sstevel@tonic-gate 	}
5587c478bd9Sstevel@tonic-gate 
5597c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
5607c478bd9Sstevel@tonic-gate 	    "usb_free_dev_data %s%d: reg=0x%p",
561*112116d8Sfb 	    ddi_driver_name(dip), ddi_get_instance(dip), (void *)reg);
5627c478bd9Sstevel@tonic-gate 
5637c478bd9Sstevel@tonic-gate 	if (reg != NULL) {
5647c478bd9Sstevel@tonic-gate 		usba_device_t *usba_device = usba_get_usba_device(dip);
5657c478bd9Sstevel@tonic-gate 		usb_client_dev_data_list_t *next, *prev, *entry;
5667c478bd9Sstevel@tonic-gate 		int	matches = 0;
5677c478bd9Sstevel@tonic-gate 
5687c478bd9Sstevel@tonic-gate 		if (reg->dev_serial != NULL) {
5697c478bd9Sstevel@tonic-gate 			kmem_free((char *)reg->dev_serial,
5707c478bd9Sstevel@tonic-gate 			    strlen((char *)reg->dev_serial) + 1);
5717c478bd9Sstevel@tonic-gate 		}
5727c478bd9Sstevel@tonic-gate 
5737c478bd9Sstevel@tonic-gate 		if (reg->dev_product != NULL) {
5747c478bd9Sstevel@tonic-gate 			kmem_free((char *)reg->dev_product,
5757c478bd9Sstevel@tonic-gate 			    strlen((char *)reg->dev_product) + 1);
5767c478bd9Sstevel@tonic-gate 		}
5777c478bd9Sstevel@tonic-gate 
5787c478bd9Sstevel@tonic-gate 		if (reg->dev_mfg != NULL) {
5797c478bd9Sstevel@tonic-gate 			kmem_free((char *)reg->dev_mfg,
5807c478bd9Sstevel@tonic-gate 			    strlen((char *)reg->dev_mfg) + 1);
5817c478bd9Sstevel@tonic-gate 		}
5827c478bd9Sstevel@tonic-gate 
5837c478bd9Sstevel@tonic-gate 		/* Free config tree under reg->dev_cfg. */
5847c478bd9Sstevel@tonic-gate 		if (reg->dev_cfg != NULL) {
5857c478bd9Sstevel@tonic-gate 			usb_free_descr_tree(dip, reg);
5867c478bd9Sstevel@tonic-gate 		}
5877c478bd9Sstevel@tonic-gate 
5887c478bd9Sstevel@tonic-gate 		mutex_enter(&usba_device->usb_mutex);
5897c478bd9Sstevel@tonic-gate 		prev = &usba_device->usb_client_dev_data_list;
5907c478bd9Sstevel@tonic-gate 		entry = usba_device->usb_client_dev_data_list.cddl_next;
5917c478bd9Sstevel@tonic-gate 
5927c478bd9Sstevel@tonic-gate 		/* free the entries in usb_client_data_list */
5937c478bd9Sstevel@tonic-gate 		while (entry) {
5947c478bd9Sstevel@tonic-gate 			next = entry->cddl_next;
5957c478bd9Sstevel@tonic-gate 			if ((dip == entry->cddl_dip) &&
5967c478bd9Sstevel@tonic-gate 			    (reg == entry->cddl_dev_data)) {
5977c478bd9Sstevel@tonic-gate 				prev->cddl_next = entry->cddl_next;
5987c478bd9Sstevel@tonic-gate 				if (entry->cddl_next) {
5997c478bd9Sstevel@tonic-gate 					entry->cddl_next->cddl_prev = prev;
6007c478bd9Sstevel@tonic-gate 				}
6017c478bd9Sstevel@tonic-gate 				kmem_free(entry, sizeof (*entry));
6027c478bd9Sstevel@tonic-gate 			} else {
6037c478bd9Sstevel@tonic-gate 				/*
6047c478bd9Sstevel@tonic-gate 				 * any other entries for this interface?
6057c478bd9Sstevel@tonic-gate 				 */
6067c478bd9Sstevel@tonic-gate 				if (usba_get_ifno(dip) == entry->cddl_ifno) {
6077c478bd9Sstevel@tonic-gate 					matches++;
6087c478bd9Sstevel@tonic-gate 				}
6097c478bd9Sstevel@tonic-gate 				prev = entry;
6107c478bd9Sstevel@tonic-gate 			}
6117c478bd9Sstevel@tonic-gate 			entry = next;
6127c478bd9Sstevel@tonic-gate 		}
6137c478bd9Sstevel@tonic-gate 
6147c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_MASK_REGISTER,
6157c478bd9Sstevel@tonic-gate 		    usbai_reg_log_handle,
6167c478bd9Sstevel@tonic-gate 		    "usb_free_dev_data: next=0x%p flags[%d]=0x%x",
617*112116d8Sfb 		    (void *)usba_device->usb_client_dev_data_list.cddl_next,
6187c478bd9Sstevel@tonic-gate 		    usba_get_ifno(dip),
6197c478bd9Sstevel@tonic-gate 		    usba_device->usb_client_flags[usba_get_ifno(dip)]);
6207c478bd9Sstevel@tonic-gate 
6217c478bd9Sstevel@tonic-gate 		if (matches == 0) {
6227c478bd9Sstevel@tonic-gate 			usba_device->
6237c478bd9Sstevel@tonic-gate 			    usb_client_flags[usba_get_ifno(dip)] &=
624*112116d8Sfb 			    ~USBA_CLIENT_FLAG_DEV_DATA;
6257c478bd9Sstevel@tonic-gate 		}
6267c478bd9Sstevel@tonic-gate 		mutex_exit(&usba_device->usb_mutex);
6277c478bd9Sstevel@tonic-gate 
6287c478bd9Sstevel@tonic-gate 		kmem_free(reg, sizeof (usb_client_dev_data_t));
6297c478bd9Sstevel@tonic-gate 	}
6307c478bd9Sstevel@tonic-gate 
6317c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
6327c478bd9Sstevel@tonic-gate 	    "usb_free_dev_data done");
6337c478bd9Sstevel@tonic-gate }
6347c478bd9Sstevel@tonic-gate 
6357c478bd9Sstevel@tonic-gate 
6367c478bd9Sstevel@tonic-gate /*
6377c478bd9Sstevel@tonic-gate  * usba_build_descr_tree:
6387c478bd9Sstevel@tonic-gate  *	This builds the descriptor tree.  See module header comment for tree
6397c478bd9Sstevel@tonic-gate  *	description.
6407c478bd9Sstevel@tonic-gate  *
6417c478bd9Sstevel@tonic-gate  * Arguments:
6427c478bd9Sstevel@tonic-gate  *	dip		- devinfo pointer - cannot be NULL.
6437c478bd9Sstevel@tonic-gate  *	usba_device	- pointer to usba_device structure.
6447c478bd9Sstevel@tonic-gate  *	usb_reg		- pointer to area returned to client describing device.
6457c478bd9Sstevel@tonic-gate  *			  number of configuration (dev_n_cfg) and array of
6467c478bd9Sstevel@tonic-gate  *			  configurations (dev_cfg) are initialized here -
6477c478bd9Sstevel@tonic-gate  *			  dev_parse_level used and may be modified to fit
6487c478bd9Sstevel@tonic-gate  *			  current configuration.
6497c478bd9Sstevel@tonic-gate  * Return values:
6507c478bd9Sstevel@tonic-gate  *	USB_SUCCESS	 - Tree build succeeded
6517c478bd9Sstevel@tonic-gate  *	USB_INVALID_ARGS - dev_parse_level in usb_reg is invalid.
6527c478bd9Sstevel@tonic-gate  *	USB_FAILURE	 - Bad descriptor info or other internal failure
6537c478bd9Sstevel@tonic-gate  */
6547c478bd9Sstevel@tonic-gate static int
6557c478bd9Sstevel@tonic-gate usba_build_descr_tree(dev_info_t *dip, usba_device_t *usba_device,
6567c478bd9Sstevel@tonic-gate     usb_client_dev_data_t *usb_reg)
6577c478bd9Sstevel@tonic-gate {
6587c478bd9Sstevel@tonic-gate 	usba_reg_state_t state;			/* State of tree construction */
6597c478bd9Sstevel@tonic-gate 	int		cfg_len_so_far = 0;	/* Bytes found, this config. */
6607c478bd9Sstevel@tonic-gate 	uint8_t 	*last_byte;	/* Ptr to the end of the cfg cloud. */
6617c478bd9Sstevel@tonic-gate 	uint_t		this_cfg_ndx;		/* Configuration counter. */
6627c478bd9Sstevel@tonic-gate 	uint_t		high_cfg_bound;		/* High config index + 1. */
6637c478bd9Sstevel@tonic-gate 	uint_t		low_cfg_bound;		/* Low config index. */
6647c478bd9Sstevel@tonic-gate 	boolean_t	process_this_if_tree = B_FALSE; /* Save alts, eps, */
6657c478bd9Sstevel@tonic-gate 							/* of this interface. */
6667c478bd9Sstevel@tonic-gate 
6677c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
6687c478bd9Sstevel@tonic-gate 	    "usba_build_descr_tree starting");
6697c478bd9Sstevel@tonic-gate 
6707c478bd9Sstevel@tonic-gate 	bzero(&state, sizeof (usba_reg_state_t));
6717c478bd9Sstevel@tonic-gate 	state.dip = dip;
6727c478bd9Sstevel@tonic-gate 
6737c478bd9Sstevel@tonic-gate 	/*
6747c478bd9Sstevel@tonic-gate 	 * Set config(s) and interface(s) to parse based on parse level.
6757c478bd9Sstevel@tonic-gate 	 * Adjust parse_level according to which configs and interfaces are
6767c478bd9Sstevel@tonic-gate 	 * made available by the device.
6777c478bd9Sstevel@tonic-gate 	 */
6787c478bd9Sstevel@tonic-gate 	state.st_dev_parse_level = usb_reg->dev_parse_level;
6797c478bd9Sstevel@tonic-gate 	if (usba_set_parse_values(dip, usba_device, &state) != USB_SUCCESS) {
6807c478bd9Sstevel@tonic-gate 
6817c478bd9Sstevel@tonic-gate 		return (USB_INVALID_ARGS);
6827c478bd9Sstevel@tonic-gate 	}
6837c478bd9Sstevel@tonic-gate 	usb_reg->dev_parse_level = state.st_dev_parse_level;
6847c478bd9Sstevel@tonic-gate 
6857c478bd9Sstevel@tonic-gate 	/* Preallocate configurations based on parse level. */
6867c478bd9Sstevel@tonic-gate 	if (usb_reg->dev_parse_level == USB_PARSE_LVL_ALL) {
6877c478bd9Sstevel@tonic-gate 		usb_reg->dev_n_cfg = usba_device->usb_n_cfgs;
6887c478bd9Sstevel@tonic-gate 		low_cfg_bound = 0;
6897c478bd9Sstevel@tonic-gate 		high_cfg_bound = usba_device->usb_n_cfgs;
6907c478bd9Sstevel@tonic-gate 	} else {
6917c478bd9Sstevel@tonic-gate 		usb_reg->dev_n_cfg = 1;
6927c478bd9Sstevel@tonic-gate 		mutex_enter(&usba_device->usb_mutex);
6937c478bd9Sstevel@tonic-gate 		low_cfg_bound = usba_device->usb_active_cfg_ndx;
6947c478bd9Sstevel@tonic-gate 		high_cfg_bound = usba_device->usb_active_cfg_ndx + 1;
6957c478bd9Sstevel@tonic-gate 		mutex_exit(&usba_device->usb_mutex);
6967c478bd9Sstevel@tonic-gate 	}
6977c478bd9Sstevel@tonic-gate 	usb_reg->dev_cfg = state.st_dev_cfg = kmem_zalloc(
698*112116d8Sfb 	    (usb_reg->dev_n_cfg * sizeof (usb_cfg_data_t)),
699*112116d8Sfb 	    KM_SLEEP);
7007c478bd9Sstevel@tonic-gate 	/*
7017c478bd9Sstevel@tonic-gate 	 * this_cfg_ndx loops through all configurations presented;
7027c478bd9Sstevel@tonic-gate 	 * state.st_dev_n_cfg limits the cfgs checked to the number desired.
7037c478bd9Sstevel@tonic-gate 	 */
7047c478bd9Sstevel@tonic-gate 	state.st_dev_n_cfg = 0;
7057c478bd9Sstevel@tonic-gate 	for (this_cfg_ndx = low_cfg_bound; this_cfg_ndx < high_cfg_bound;
7067c478bd9Sstevel@tonic-gate 	    this_cfg_ndx++) {
7077c478bd9Sstevel@tonic-gate 
7087c478bd9Sstevel@tonic-gate 		state.st_curr_raw_descr =
709*112116d8Sfb 		    usba_device->usb_cfg_array[this_cfg_ndx];
7107c478bd9Sstevel@tonic-gate 		ASSERT(state.st_curr_raw_descr != NULL);
7117c478bd9Sstevel@tonic-gate 
7127c478bd9Sstevel@tonic-gate 		/* Clear the following for config cloud sanity checking. */
7137c478bd9Sstevel@tonic-gate 		last_byte = NULL;
7147c478bd9Sstevel@tonic-gate 		state.st_curr_cfg = NULL;
7157c478bd9Sstevel@tonic-gate 		state.st_curr_if = NULL;
7167c478bd9Sstevel@tonic-gate 		state.st_curr_alt = NULL;
7177c478bd9Sstevel@tonic-gate 		state.st_curr_ep = NULL;
7187c478bd9Sstevel@tonic-gate 
7197c478bd9Sstevel@tonic-gate 		do {
7207c478bd9Sstevel@tonic-gate 			/* All descr have length and type at offset 0 and 1 */
7217c478bd9Sstevel@tonic-gate 			state.st_curr_raw_descr_len =
7227c478bd9Sstevel@tonic-gate 			    state.st_curr_raw_descr[0];
7237c478bd9Sstevel@tonic-gate 			state.st_curr_raw_descr_type =
7247c478bd9Sstevel@tonic-gate 			    state.st_curr_raw_descr[1];
7257c478bd9Sstevel@tonic-gate 
7267c478bd9Sstevel@tonic-gate 			/* First descr in cloud must be a config descr. */
7277c478bd9Sstevel@tonic-gate 			if ((last_byte == NULL) &&
7287c478bd9Sstevel@tonic-gate 			    (state.st_curr_raw_descr_type !=
7297c478bd9Sstevel@tonic-gate 			    USB_DESCR_TYPE_CFG)) {
7307c478bd9Sstevel@tonic-gate 
7317c478bd9Sstevel@tonic-gate 				return (USB_FAILURE);
7327c478bd9Sstevel@tonic-gate 			}
7337c478bd9Sstevel@tonic-gate 
7347c478bd9Sstevel@tonic-gate 			/*
7357c478bd9Sstevel@tonic-gate 			 * Bomb if we don't find a new cfg descr when expected.
7367c478bd9Sstevel@tonic-gate 			 * cfg_len_so_far = total_cfg_length = 0 1st time thru.
7377c478bd9Sstevel@tonic-gate 			 */
7387c478bd9Sstevel@tonic-gate 			if (cfg_len_so_far > state.st_total_cfg_length) {
7397c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_ALL,
7407c478bd9Sstevel@tonic-gate 				    usbai_reg_log_handle,
7417c478bd9Sstevel@tonic-gate 				    "usba_build_descr_tree: Configuration (%d) "
7427c478bd9Sstevel@tonic-gate 				    "larger than wTotalLength (%d).",
7437c478bd9Sstevel@tonic-gate 				    cfg_len_so_far, state.st_total_cfg_length);
7447c478bd9Sstevel@tonic-gate 
7457c478bd9Sstevel@tonic-gate 				return (USB_FAILURE);
7467c478bd9Sstevel@tonic-gate 			}
7477c478bd9Sstevel@tonic-gate 
7487c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_MASK_REGISTER,
7497c478bd9Sstevel@tonic-gate 			    usbai_reg_log_handle,
7507c478bd9Sstevel@tonic-gate 			    "usba_build_descr_tree: Process type %d descr "
7517c478bd9Sstevel@tonic-gate 			    "(addr=0x%p)", state.st_curr_raw_descr_type,
752*112116d8Sfb 			    (void *)state.st_curr_raw_descr);
7537c478bd9Sstevel@tonic-gate 
7547c478bd9Sstevel@tonic-gate 			switch (state.st_curr_raw_descr_type) {
7557c478bd9Sstevel@tonic-gate 			case USB_DESCR_TYPE_CFG:
7567c478bd9Sstevel@tonic-gate 				cfg_len_so_far = 0;
7577c478bd9Sstevel@tonic-gate 				process_this_if_tree = B_FALSE;
7587c478bd9Sstevel@tonic-gate 
7597c478bd9Sstevel@tonic-gate 				state.st_curr_cfg_str = usba_device->
760*112116d8Sfb 				    usb_cfg_str_descr[this_cfg_ndx];
7617c478bd9Sstevel@tonic-gate 				usba_process_cfg_descr(&state);
7627c478bd9Sstevel@tonic-gate 				state.st_last_processed_descr_type =
763*112116d8Sfb 				    USB_DESCR_TYPE_CFG;
7647c478bd9Sstevel@tonic-gate 				last_byte = state.st_curr_raw_descr +
7657c478bd9Sstevel@tonic-gate 				    (state.st_total_cfg_length *
7667c478bd9Sstevel@tonic-gate 				    sizeof (uchar_t));
7677c478bd9Sstevel@tonic-gate 
7687c478bd9Sstevel@tonic-gate 				break;
7697c478bd9Sstevel@tonic-gate 
7707c478bd9Sstevel@tonic-gate 			case USB_DESCR_TYPE_IF:
7717c478bd9Sstevel@tonic-gate 				/*
7727c478bd9Sstevel@tonic-gate 				 * process_this_if_tree == TRUE means this
7737c478bd9Sstevel@tonic-gate 				 * interface, plus all eps and c/vs in it are
7747c478bd9Sstevel@tonic-gate 				 * to be processed.
7757c478bd9Sstevel@tonic-gate 				 */
7767c478bd9Sstevel@tonic-gate 				if (usba_process_if_descr(&state,
777*112116d8Sfb 				    &process_this_if_tree) != USB_SUCCESS) {
7787c478bd9Sstevel@tonic-gate 
779*112116d8Sfb 					return (USB_FAILURE);
7807c478bd9Sstevel@tonic-gate 				}
7817c478bd9Sstevel@tonic-gate 				state.st_last_processed_descr_type =
782*112116d8Sfb 				    USB_DESCR_TYPE_IF;
7837c478bd9Sstevel@tonic-gate 
7847c478bd9Sstevel@tonic-gate 				break;
7857c478bd9Sstevel@tonic-gate 
7867c478bd9Sstevel@tonic-gate 			case USB_DESCR_TYPE_EP:
7877c478bd9Sstevel@tonic-gate 				/*
7887c478bd9Sstevel@tonic-gate 				 * Skip if endpoints of a specific interface are
7897c478bd9Sstevel@tonic-gate 				 * desired and this endpoint is associated with
7907c478bd9Sstevel@tonic-gate 				 * a different interface.
7917c478bd9Sstevel@tonic-gate 				 */
7927c478bd9Sstevel@tonic-gate 				if (process_this_if_tree) {
7937c478bd9Sstevel@tonic-gate 					if (usba_process_ep_descr(&state) !=
7947c478bd9Sstevel@tonic-gate 					    USB_SUCCESS) {
7957c478bd9Sstevel@tonic-gate 
7967c478bd9Sstevel@tonic-gate 						return (USB_FAILURE);
7977c478bd9Sstevel@tonic-gate 					}
7987c478bd9Sstevel@tonic-gate 					state.st_last_processed_descr_type =
799*112116d8Sfb 					    USB_DESCR_TYPE_EP;
8007c478bd9Sstevel@tonic-gate 				}
8017c478bd9Sstevel@tonic-gate 
8027c478bd9Sstevel@tonic-gate 				break;
8037c478bd9Sstevel@tonic-gate 
8047c478bd9Sstevel@tonic-gate 			case USB_DESCR_TYPE_STRING:
8057c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_MASK_ALL,
8067c478bd9Sstevel@tonic-gate 				    usbai_reg_log_handle,
8077c478bd9Sstevel@tonic-gate 				    "usb_get_dev_data: "
8087c478bd9Sstevel@tonic-gate 				    "Found unexpected str descr at addr 0x%p",
809*112116d8Sfb 				    (void *)state.st_curr_raw_descr);
8107c478bd9Sstevel@tonic-gate 
8117c478bd9Sstevel@tonic-gate 				break;	/* Shouldn't be any here.  Skip. */
8127c478bd9Sstevel@tonic-gate 
8137c478bd9Sstevel@tonic-gate 			default:
8147c478bd9Sstevel@tonic-gate 				/*
8157c478bd9Sstevel@tonic-gate 				 * Treat all other descr as class/vendor
8167c478bd9Sstevel@tonic-gate 				 * specific.  Skip if c/vs of a specific
8177c478bd9Sstevel@tonic-gate 				 * interface are desired and this c/v is
8187c478bd9Sstevel@tonic-gate 				 * associated with a different one.
8197c478bd9Sstevel@tonic-gate 				 */
8207c478bd9Sstevel@tonic-gate 				if (process_this_if_tree == B_TRUE) {
8217c478bd9Sstevel@tonic-gate 					if (usba_process_cv_descr(&state) !=
8227c478bd9Sstevel@tonic-gate 					    USB_SUCCESS) {
8237c478bd9Sstevel@tonic-gate 
8247c478bd9Sstevel@tonic-gate 						return (USB_FAILURE);
8257c478bd9Sstevel@tonic-gate 					}
8267c478bd9Sstevel@tonic-gate 				}
8277c478bd9Sstevel@tonic-gate 			}
8287c478bd9Sstevel@tonic-gate 
8297c478bd9Sstevel@tonic-gate 			state.st_curr_raw_descr += state.st_curr_raw_descr_len;
8307c478bd9Sstevel@tonic-gate 			cfg_len_so_far += state.st_curr_raw_descr_len;
8317c478bd9Sstevel@tonic-gate 
8327c478bd9Sstevel@tonic-gate 		} while (state.st_curr_raw_descr < last_byte);
8337c478bd9Sstevel@tonic-gate 	}
8347c478bd9Sstevel@tonic-gate 
8357c478bd9Sstevel@tonic-gate 	/* Make tree sparse, and put elements in order. */
8367c478bd9Sstevel@tonic-gate 	usba_order_tree(&state);
8377c478bd9Sstevel@tonic-gate 
8387c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
8397c478bd9Sstevel@tonic-gate 	    "usba_build_descr_tree done");
8407c478bd9Sstevel@tonic-gate 
8417c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
8427c478bd9Sstevel@tonic-gate }
8437c478bd9Sstevel@tonic-gate 
8447c478bd9Sstevel@tonic-gate 
8457c478bd9Sstevel@tonic-gate /*
8467c478bd9Sstevel@tonic-gate  * usba_process_cfg_descr:
8477c478bd9Sstevel@tonic-gate  *	Set up a configuration tree node based on a raw config descriptor.
8487c478bd9Sstevel@tonic-gate  *
8497c478bd9Sstevel@tonic-gate  * Arguments:
8507c478bd9Sstevel@tonic-gate  *	state		- Pointer to this module's state structure.
8517c478bd9Sstevel@tonic-gate  *
8527c478bd9Sstevel@tonic-gate  * Returns:
8537c478bd9Sstevel@tonic-gate  *	B_TRUE: the descr processed corresponds to a requested configuration.
8547c478bd9Sstevel@tonic-gate  *	B_FALSE: the descr processed does not correspond to a requested config.
8557c478bd9Sstevel@tonic-gate  */
8567c478bd9Sstevel@tonic-gate static void
8577c478bd9Sstevel@tonic-gate usba_process_cfg_descr(usba_reg_state_t *state)
8587c478bd9Sstevel@tonic-gate {
8597c478bd9Sstevel@tonic-gate 	usb_cfg_data_t *curr_cfg;
8607c478bd9Sstevel@tonic-gate 
8617c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
8627c478bd9Sstevel@tonic-gate 	    "usba_process_cfg_descr starting");
8637c478bd9Sstevel@tonic-gate 
8647c478bd9Sstevel@tonic-gate 	curr_cfg = state->st_curr_cfg =
8657c478bd9Sstevel@tonic-gate 	    &state->st_dev_cfg[state->st_dev_n_cfg++];
8667c478bd9Sstevel@tonic-gate 
8677c478bd9Sstevel@tonic-gate 	/* Parse and store config descriptor proper in the tree. */
8687c478bd9Sstevel@tonic-gate 	(void) usb_parse_data("2cs5c",
8697c478bd9Sstevel@tonic-gate 	    state->st_curr_raw_descr, state->st_curr_raw_descr_len,
8707c478bd9Sstevel@tonic-gate 	    &curr_cfg->cfg_descr,
8717c478bd9Sstevel@tonic-gate 	    sizeof (usb_cfg_descr_t));
8727c478bd9Sstevel@tonic-gate 
8737c478bd9Sstevel@tonic-gate 	state->st_total_cfg_length = curr_cfg->cfg_descr.wTotalLength;
8747c478bd9Sstevel@tonic-gate 
8757c478bd9Sstevel@tonic-gate 	if (state->st_curr_cfg_str != NULL) {
8767c478bd9Sstevel@tonic-gate 		curr_cfg->cfg_strsize = strlen(state->st_curr_cfg_str) + 1;
8777c478bd9Sstevel@tonic-gate 		curr_cfg->cfg_str = kmem_zalloc(curr_cfg->cfg_strsize,
878*112116d8Sfb 		    KM_SLEEP);
8797c478bd9Sstevel@tonic-gate 		(void) strcpy(curr_cfg->cfg_str, state->st_curr_cfg_str);
8807c478bd9Sstevel@tonic-gate 	}
8817c478bd9Sstevel@tonic-gate 
8827c478bd9Sstevel@tonic-gate 	curr_cfg->cfg_n_if = curr_cfg->cfg_descr.bNumInterfaces;
8837c478bd9Sstevel@tonic-gate 	curr_cfg->cfg_if = kmem_zalloc((curr_cfg->cfg_n_if *
884*112116d8Sfb 	    sizeof (usb_if_data_t)), KM_SLEEP);
8857c478bd9Sstevel@tonic-gate 
8867c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
8877c478bd9Sstevel@tonic-gate 	    "usba_process_cfg_descr done");
8887c478bd9Sstevel@tonic-gate }
8897c478bd9Sstevel@tonic-gate 
8907c478bd9Sstevel@tonic-gate 
8917c478bd9Sstevel@tonic-gate /*
8927c478bd9Sstevel@tonic-gate  * usba_process_if_descr:
8937c478bd9Sstevel@tonic-gate  *	This processes a raw interface descriptor, and sets up an analogous
8947c478bd9Sstevel@tonic-gate  *	interface node and child "alternate" nodes (each containing an
8957c478bd9Sstevel@tonic-gate  *	interface descriptor) in the descriptor tree.
8967c478bd9Sstevel@tonic-gate  *
8977c478bd9Sstevel@tonic-gate  *	It groups all descriptors with the same bInterfaceNumber (alternates)
8987c478bd9Sstevel@tonic-gate  *	into an array.	It makes entries in an interface array, each of which
8997c478bd9Sstevel@tonic-gate  *	points to an array of alternates.
9007c478bd9Sstevel@tonic-gate  *
9017c478bd9Sstevel@tonic-gate  * Arguments:
9027c478bd9Sstevel@tonic-gate  *	state		- Pointer to this module's state structure.
9037c478bd9Sstevel@tonic-gate  *	requested_if	- Address into which the following is returned:
9047c478bd9Sstevel@tonic-gate  *	    B_TRUE	- the processed descr is of a requested interface.
9057c478bd9Sstevel@tonic-gate  *	    B_FALSE	- the processed descr if of a non-requested interface.
9067c478bd9Sstevel@tonic-gate  *
9077c478bd9Sstevel@tonic-gate  * Returns:
9087c478bd9Sstevel@tonic-gate  *	USB_SUCCESS:	Descriptor is successfully parsed.
9097c478bd9Sstevel@tonic-gate  *	USB_FAILURE:	Descriptor is inappropriately placed in config cloud.
9107c478bd9Sstevel@tonic-gate  */
9117c478bd9Sstevel@tonic-gate static int
9127c478bd9Sstevel@tonic-gate usba_process_if_descr(usba_reg_state_t *state, boolean_t *requested_if)
9137c478bd9Sstevel@tonic-gate {
9147c478bd9Sstevel@tonic-gate 	char *string;
9157c478bd9Sstevel@tonic-gate 	usb_if_descr_t *new_if_descr;
9167c478bd9Sstevel@tonic-gate 	usba_device_t *usba_device = usba_get_usba_device(state->dip);
9177c478bd9Sstevel@tonic-gate 	int is_root_hub = (usba_device->usb_addr == ROOT_HUB_ADDR);
9187c478bd9Sstevel@tonic-gate 
9197c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
9207c478bd9Sstevel@tonic-gate 	    "usba_process_if_descr starting");
9217c478bd9Sstevel@tonic-gate 
9227c478bd9Sstevel@tonic-gate 	/* No config preceeds this interface. */
9237c478bd9Sstevel@tonic-gate 	if (state->st_curr_cfg == NULL) {
9247c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
9257c478bd9Sstevel@tonic-gate 		    "usba_process_if_descr found interface after no config.");
9267c478bd9Sstevel@tonic-gate 
9277c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
9287c478bd9Sstevel@tonic-gate 	}
9297c478bd9Sstevel@tonic-gate 
9307c478bd9Sstevel@tonic-gate 	new_if_descr = kmem_zalloc(sizeof (usb_if_descr_t), KM_SLEEP);
9317c478bd9Sstevel@tonic-gate 
9327c478bd9Sstevel@tonic-gate 	/* Strictly speaking, unpacking is not necessary.  Could use bcopy. */
9337c478bd9Sstevel@tonic-gate 	(void) usb_parse_data("9c", state->st_curr_raw_descr,
934*112116d8Sfb 	    state->st_curr_raw_descr_len,
935*112116d8Sfb 	    new_if_descr, sizeof (usb_if_descr_t));
9367c478bd9Sstevel@tonic-gate 
937cbab2b26Slg 	/* Check the interface number in case of a malfunction device */
938cbab2b26Slg 	if (new_if_descr->bInterfaceNumber >= state->st_curr_cfg->cfg_n_if) {
939cbab2b26Slg 		USB_DPRINTF_L2(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
940cbab2b26Slg 		    "usba_process_if_descr: bInterfaceNumber=%d is not "
941cbab2b26Slg 		    "a valid one", new_if_descr->bInterfaceNumber);
942cbab2b26Slg 		kmem_free(new_if_descr, sizeof (usb_if_descr_t));
943cbab2b26Slg 
944de577e60Svn 		*requested_if = B_FALSE;
945de577e60Svn 
946de577e60Svn 		return (USB_SUCCESS);
947cbab2b26Slg 	}
948cbab2b26Slg 	*requested_if = B_TRUE;
949cbab2b26Slg 
9507c478bd9Sstevel@tonic-gate 	/* Not a requested interface. */
9517c478bd9Sstevel@tonic-gate 	if ((state->st_if_to_build != new_if_descr->bInterfaceNumber) &&
9527c478bd9Sstevel@tonic-gate 	    (state->st_if_to_build != USBA_ALL)) {
9537c478bd9Sstevel@tonic-gate 		*requested_if = B_FALSE;
9547c478bd9Sstevel@tonic-gate 
9557c478bd9Sstevel@tonic-gate 	} else {
9567c478bd9Sstevel@tonic-gate 		usb_alt_if_data_t *alt_array;
9577c478bd9Sstevel@tonic-gate 		uint_t		alt_index;
9587c478bd9Sstevel@tonic-gate 
9597c478bd9Sstevel@tonic-gate 		/* Point to proper interface node, based on num in descr. */
9607c478bd9Sstevel@tonic-gate 		state->st_curr_if =
9617c478bd9Sstevel@tonic-gate 		    &state->st_curr_cfg->cfg_if[new_if_descr->bInterfaceNumber];
9627c478bd9Sstevel@tonic-gate 
9637c478bd9Sstevel@tonic-gate 		/* Make room for new alternate. */
9647c478bd9Sstevel@tonic-gate 		alt_index = state->st_curr_if->if_n_alt;
9657c478bd9Sstevel@tonic-gate 		alt_array = state->st_curr_if->if_alt;
9667c478bd9Sstevel@tonic-gate 		usba_augment_array((void **)(&alt_array), alt_index,
967*112116d8Sfb 		    sizeof (usb_alt_if_data_t));
9687c478bd9Sstevel@tonic-gate 
9697c478bd9Sstevel@tonic-gate 		/* Ptr to the current alt, may be used to attach a c/v to it. */
9707c478bd9Sstevel@tonic-gate 		state->st_curr_alt = &alt_array[alt_index];
9717c478bd9Sstevel@tonic-gate 
9727c478bd9Sstevel@tonic-gate 		bcopy(new_if_descr, &(alt_array[alt_index++].altif_descr),
9737c478bd9Sstevel@tonic-gate 		    sizeof (usb_if_descr_t));
9747c478bd9Sstevel@tonic-gate 		state->st_curr_if->if_alt = alt_array;
9757c478bd9Sstevel@tonic-gate 		state->st_curr_if->if_n_alt = alt_index;
9767c478bd9Sstevel@tonic-gate 
9777c478bd9Sstevel@tonic-gate 		string = kmem_zalloc(USB_MAXSTRINGLEN, KM_SLEEP);
9787c478bd9Sstevel@tonic-gate 		if (!is_root_hub) {
9797c478bd9Sstevel@tonic-gate 			(void) usb_get_string_descr(state->dip, USB_LANG_ID,
9807c478bd9Sstevel@tonic-gate 			    state->st_curr_alt->altif_descr.iInterface,
9817c478bd9Sstevel@tonic-gate 			    string, USB_MAXSTRINGLEN);
9827c478bd9Sstevel@tonic-gate 		}
9837c478bd9Sstevel@tonic-gate 		if (string[0] == '\0') {
9847c478bd9Sstevel@tonic-gate 			(void) strcpy(string, "<none>");
9857c478bd9Sstevel@tonic-gate 		}
9867c478bd9Sstevel@tonic-gate 		state->st_curr_alt->altif_strsize = strlen(string) + 1;
9877c478bd9Sstevel@tonic-gate 		state->st_curr_alt->altif_str = kmem_zalloc(
9887c478bd9Sstevel@tonic-gate 		    state->st_curr_alt->altif_strsize, KM_SLEEP);
9897c478bd9Sstevel@tonic-gate 		(void) strcpy(state->st_curr_alt->altif_str, string);
9907c478bd9Sstevel@tonic-gate 		kmem_free(string, USB_MAXSTRINGLEN);
9917c478bd9Sstevel@tonic-gate 	}
9927c478bd9Sstevel@tonic-gate 
9937c478bd9Sstevel@tonic-gate 	kmem_free(new_if_descr, sizeof (usb_if_descr_t));
9947c478bd9Sstevel@tonic-gate 
9957c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
9967c478bd9Sstevel@tonic-gate 	    "usba_process_if_descr done");
9977c478bd9Sstevel@tonic-gate 
9987c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
9997c478bd9Sstevel@tonic-gate }
10007c478bd9Sstevel@tonic-gate 
10017c478bd9Sstevel@tonic-gate 
10027c478bd9Sstevel@tonic-gate /*
10037c478bd9Sstevel@tonic-gate  * usba_process_ep_descr:
10047c478bd9Sstevel@tonic-gate  *	This processes a raw endpoint descriptor, and sets up an analogous
10057c478bd9Sstevel@tonic-gate  *	endpoint descriptor node in the descriptor tree.
10067c478bd9Sstevel@tonic-gate  *
10077c478bd9Sstevel@tonic-gate  * Arguments:
10087c478bd9Sstevel@tonic-gate  *	state		- Pointer to this module's state structure.
10097c478bd9Sstevel@tonic-gate  *
10107c478bd9Sstevel@tonic-gate  * Returns:
10117c478bd9Sstevel@tonic-gate  *	USB_SUCCESS:	Descriptor is successfully parsed.
10127c478bd9Sstevel@tonic-gate  *	USB_FAILURE:	Descriptor is inappropriately placed in config cloud.
10137c478bd9Sstevel@tonic-gate  */
10147c478bd9Sstevel@tonic-gate static int
10157c478bd9Sstevel@tonic-gate usba_process_ep_descr(usba_reg_state_t *state)
10167c478bd9Sstevel@tonic-gate {
10177c478bd9Sstevel@tonic-gate 	usb_alt_if_data_t *curr_alt = state->st_curr_alt;
10187c478bd9Sstevel@tonic-gate 
10197c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
10207c478bd9Sstevel@tonic-gate 	    "usba_process_ep_descr starting");
10217c478bd9Sstevel@tonic-gate 
10227c478bd9Sstevel@tonic-gate 	/* No interface preceeds this endpoint. */
10237c478bd9Sstevel@tonic-gate 	if (state->st_curr_alt == NULL) {
10247c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
10257c478bd9Sstevel@tonic-gate 		    "usba_process_ep_descr: no requested alt before endpt.");
10267c478bd9Sstevel@tonic-gate 
10277c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
10287c478bd9Sstevel@tonic-gate 	}
10297c478bd9Sstevel@tonic-gate 
10307c478bd9Sstevel@tonic-gate 	usba_augment_array((void **)(&curr_alt->altif_ep),
1031*112116d8Sfb 	    curr_alt->altif_n_ep, sizeof (usb_ep_data_t));
10327c478bd9Sstevel@tonic-gate 
10337c478bd9Sstevel@tonic-gate 	/* Ptr to the current endpt, may be used to attach a c/v to it. */
10347c478bd9Sstevel@tonic-gate 	state->st_curr_ep = &curr_alt->altif_ep[curr_alt->altif_n_ep++];
10357c478bd9Sstevel@tonic-gate 
10367c478bd9Sstevel@tonic-gate 	(void) usb_parse_data("4csc", state->st_curr_raw_descr,
1037*112116d8Sfb 	    state->st_curr_raw_descr_len,
1038*112116d8Sfb 	    &state->st_curr_ep->ep_descr, sizeof (usb_ep_descr_t));
10397c478bd9Sstevel@tonic-gate 
10407c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
10417c478bd9Sstevel@tonic-gate 	    "usba_process_ep_descr done");
10427c478bd9Sstevel@tonic-gate 
10437c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
10447c478bd9Sstevel@tonic-gate }
10457c478bd9Sstevel@tonic-gate 
10467c478bd9Sstevel@tonic-gate 
10477c478bd9Sstevel@tonic-gate /*
10487c478bd9Sstevel@tonic-gate  * usba_process_cv_descr:
10497c478bd9Sstevel@tonic-gate  *	This processes a raw endpoint descriptor, and sets up an analogous
10507c478bd9Sstevel@tonic-gate  *	endpoint descriptor in the descriptor tree.  C/Vs are associated with
10517c478bd9Sstevel@tonic-gate  *	other descriptors they follow in the raw data.
10527c478bd9Sstevel@tonic-gate  *	last_processed_descr_type indicates the type of descr this c/v follows.
10537c478bd9Sstevel@tonic-gate  *
10547c478bd9Sstevel@tonic-gate  * Arguments:
10557c478bd9Sstevel@tonic-gate  *	state		- Pointer to this module's state structure.
10567c478bd9Sstevel@tonic-gate  *
10577c478bd9Sstevel@tonic-gate  * Returns:
10587c478bd9Sstevel@tonic-gate  *	USB_SUCCESS:	Descriptor is successfully parsed.
10597c478bd9Sstevel@tonic-gate  *	USB_FAILURE:	Descriptor is inappropriately placed in config cloud.
10607c478bd9Sstevel@tonic-gate  */
10617c478bd9Sstevel@tonic-gate static int
10627c478bd9Sstevel@tonic-gate usba_process_cv_descr(usba_reg_state_t *state)
10637c478bd9Sstevel@tonic-gate {
10647c478bd9Sstevel@tonic-gate 	usb_cvs_data_t	*curr_cv_descr;
10657c478bd9Sstevel@tonic-gate 	usb_cvs_data_t	**cvs_ptr = NULL;
10667c478bd9Sstevel@tonic-gate 	uint_t		*n_cvs_ptr;
10677c478bd9Sstevel@tonic-gate 
10687c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
10697c478bd9Sstevel@tonic-gate 	    "usba_process_cv_descr starting.  Processing c/v for descr type %d",
10707c478bd9Sstevel@tonic-gate 	    state->st_last_processed_descr_type);
10717c478bd9Sstevel@tonic-gate 
10727c478bd9Sstevel@tonic-gate 	/*
10737c478bd9Sstevel@tonic-gate 	 * Attach the c/v to a node based on the last descr type processed.
10747c478bd9Sstevel@tonic-gate 	 * Save handles to appropriate c/v node array and count to update.
10757c478bd9Sstevel@tonic-gate 	 */
10767c478bd9Sstevel@tonic-gate 	switch (state->st_last_processed_descr_type) {
10777c478bd9Sstevel@tonic-gate 	case USB_DESCR_TYPE_CFG:
10787c478bd9Sstevel@tonic-gate 		n_cvs_ptr = &state->st_curr_cfg->cfg_n_cvs;
10797c478bd9Sstevel@tonic-gate 		cvs_ptr = &state->st_curr_cfg->cfg_cvs;
10807c478bd9Sstevel@tonic-gate 		break;
10817c478bd9Sstevel@tonic-gate 
10827c478bd9Sstevel@tonic-gate 	case USB_DESCR_TYPE_IF:
10837c478bd9Sstevel@tonic-gate 		n_cvs_ptr = &state->st_curr_alt->altif_n_cvs;
10847c478bd9Sstevel@tonic-gate 		cvs_ptr = &state->st_curr_alt->altif_cvs;
10857c478bd9Sstevel@tonic-gate 		break;
10867c478bd9Sstevel@tonic-gate 
10877c478bd9Sstevel@tonic-gate 	case USB_DESCR_TYPE_EP:
10887c478bd9Sstevel@tonic-gate 		n_cvs_ptr = &state->st_curr_ep->ep_n_cvs;
10897c478bd9Sstevel@tonic-gate 		cvs_ptr = &state->st_curr_ep->ep_cvs;
10907c478bd9Sstevel@tonic-gate 		break;
10917c478bd9Sstevel@tonic-gate 
10927c478bd9Sstevel@tonic-gate 	default:
10937c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_MASK_ALL, usbai_reg_log_handle,
10947c478bd9Sstevel@tonic-gate 		    "usba_process_cv_descr: Type of last descriptor unknown. ");
10957c478bd9Sstevel@tonic-gate 
10967c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
10977c478bd9Sstevel@tonic-gate 	}
10987c478bd9Sstevel@tonic-gate 
10997c478bd9Sstevel@tonic-gate 	usba_augment_array((void **)cvs_ptr, *n_cvs_ptr,
1100*112116d8Sfb 	    sizeof (usb_cvs_data_t));
11017c478bd9Sstevel@tonic-gate 	curr_cv_descr = &(*cvs_ptr)[(*n_cvs_ptr)++];
11027c478bd9Sstevel@tonic-gate 
11037c478bd9Sstevel@tonic-gate 	curr_cv_descr->cvs_buf =
1104*112116d8Sfb 	    kmem_zalloc(state->st_curr_raw_descr_len, KM_SLEEP);
11057c478bd9Sstevel@tonic-gate 	curr_cv_descr->cvs_buf_len = state->st_curr_raw_descr_len;
11067c478bd9Sstevel@tonic-gate 	bcopy(state->st_curr_raw_descr, curr_cv_descr->cvs_buf,
11077c478bd9Sstevel@tonic-gate 	    state->st_curr_raw_descr_len);
11087c478bd9Sstevel@tonic-gate 
11097c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
11107c478bd9Sstevel@tonic-gate 	    "usba_process_cv_descr done");
11117c478bd9Sstevel@tonic-gate 
11127c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
11137c478bd9Sstevel@tonic-gate }
11147c478bd9Sstevel@tonic-gate 
11157c478bd9Sstevel@tonic-gate 
11167c478bd9Sstevel@tonic-gate /*
11177c478bd9Sstevel@tonic-gate  * usba_set_parse_values:
11187c478bd9Sstevel@tonic-gate  *	Based on parse level, set the configuration(s) and interface(s) to build
11197c478bd9Sstevel@tonic-gate  *
11207c478bd9Sstevel@tonic-gate  *	Returned configuration value can be USBA_ALL indicating to build all
11217c478bd9Sstevel@tonic-gate  *	configurations.  Likewise for the returned interface value.
11227c478bd9Sstevel@tonic-gate  *
11237c478bd9Sstevel@tonic-gate  * Arguments:
11247c478bd9Sstevel@tonic-gate  *	dip		- pointer to devinfo of the device
11257c478bd9Sstevel@tonic-gate  *	usba_device	- pointer to usba_device structure of the device
11267c478bd9Sstevel@tonic-gate  *	state		- Pointer to this module's state structure.
11277c478bd9Sstevel@tonic-gate  *			  if no specific config specified, default to all config
11287c478bd9Sstevel@tonic-gate  *			  if no specific interface specified, default to all.
11297c478bd9Sstevel@tonic-gate  *			  if_to_build and config_to_build are modified.
11307c478bd9Sstevel@tonic-gate  *			  dev_parse_level may be modified.
11317c478bd9Sstevel@tonic-gate  *
11327c478bd9Sstevel@tonic-gate  * Returns:
11337c478bd9Sstevel@tonic-gate  *	USB_SUCCESS	- success
11347c478bd9Sstevel@tonic-gate  *	USB_INVALID_ARGS - state->st_dev_parse_level is invalid.
11357c478bd9Sstevel@tonic-gate  */
11367c478bd9Sstevel@tonic-gate static int
11377c478bd9Sstevel@tonic-gate usba_set_parse_values(dev_info_t *dip, usba_device_t *usba_device,
11387c478bd9Sstevel@tonic-gate     usba_reg_state_t *state)
11397c478bd9Sstevel@tonic-gate {
11407c478bd9Sstevel@tonic-gate 	/* Default to *all* in case configuration# prop not set. */
11417c478bd9Sstevel@tonic-gate 	mutex_enter(&usba_device->usb_mutex);
11427c478bd9Sstevel@tonic-gate 	state->st_cfg_to_build = usba_device->usb_active_cfg_ndx;
11437c478bd9Sstevel@tonic-gate 	mutex_exit(&usba_device->usb_mutex);
11447c478bd9Sstevel@tonic-gate 	if (state->st_cfg_to_build == USBA_DEV_CONFIG_INDEX_UNDEFINED) {
11457c478bd9Sstevel@tonic-gate 		state->st_cfg_to_build = USBA_ALL;
11467c478bd9Sstevel@tonic-gate 	}
11477c478bd9Sstevel@tonic-gate 	state->st_if_to_build = usb_get_if_number(dip);
11487c478bd9Sstevel@tonic-gate 
11497c478bd9Sstevel@tonic-gate 	switch (state->st_dev_parse_level) {
11507c478bd9Sstevel@tonic-gate 	case USB_PARSE_LVL_ALL:		/* Parse all configurations */
11517c478bd9Sstevel@tonic-gate 		state->st_cfg_to_build = USBA_ALL;
11527c478bd9Sstevel@tonic-gate 		state->st_if_to_build = USBA_ALL;
11537c478bd9Sstevel@tonic-gate 		break;
11547c478bd9Sstevel@tonic-gate 
11557c478bd9Sstevel@tonic-gate 	case USB_PARSE_LVL_CFG:		/* Parse all interfaces of a */
11567c478bd9Sstevel@tonic-gate 					/* specific configuration. */
11577c478bd9Sstevel@tonic-gate 		state->st_if_to_build = USBA_ALL;
11587c478bd9Sstevel@tonic-gate 		break;
11597c478bd9Sstevel@tonic-gate 
11607c478bd9Sstevel@tonic-gate 	case USB_PARSE_LVL_IF:		/* Parse configured interface only */
11617c478bd9Sstevel@tonic-gate 		if (state->st_if_to_build < 0) {
11627c478bd9Sstevel@tonic-gate 			state->st_if_to_build = USBA_ALL;
11637c478bd9Sstevel@tonic-gate 		}
11647c478bd9Sstevel@tonic-gate 		break;
11657c478bd9Sstevel@tonic-gate 
11667c478bd9Sstevel@tonic-gate 	default:
11677c478bd9Sstevel@tonic-gate 
11687c478bd9Sstevel@tonic-gate 		return (USB_INVALID_ARGS);
11697c478bd9Sstevel@tonic-gate 	}
11707c478bd9Sstevel@tonic-gate 
11717c478bd9Sstevel@tonic-gate 	/*
11727c478bd9Sstevel@tonic-gate 	 * Set parse level to identify this tree properly, regardless of what
11737c478bd9Sstevel@tonic-gate 	 * the caller thought the tree would have.
11747c478bd9Sstevel@tonic-gate 	 */
11757c478bd9Sstevel@tonic-gate 	if ((state->st_if_to_build == USBA_ALL) &&
11767c478bd9Sstevel@tonic-gate 	    (state->st_dev_parse_level == USB_PARSE_LVL_IF)) {
11777c478bd9Sstevel@tonic-gate 		state->st_dev_parse_level = USB_PARSE_LVL_CFG;
11787c478bd9Sstevel@tonic-gate 	}
11797c478bd9Sstevel@tonic-gate 	if ((state->st_cfg_to_build == USBA_ALL) &&
11807c478bd9Sstevel@tonic-gate 	    (state->st_dev_parse_level == USB_PARSE_LVL_CFG)) {
11817c478bd9Sstevel@tonic-gate 		state->st_dev_parse_level = USB_PARSE_LVL_ALL;
11827c478bd9Sstevel@tonic-gate 	}
11837c478bd9Sstevel@tonic-gate 
11847c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
11857c478bd9Sstevel@tonic-gate }
11867c478bd9Sstevel@tonic-gate 
11877c478bd9Sstevel@tonic-gate 
11887c478bd9Sstevel@tonic-gate /*
11897c478bd9Sstevel@tonic-gate  * usba_kmem_realloc:
11907c478bd9Sstevel@tonic-gate  *	Resize dynamic memory.	Copy contents of old area to
11917c478bd9Sstevel@tonic-gate  *	beginning of new area.
11927c478bd9Sstevel@tonic-gate  *
11937c478bd9Sstevel@tonic-gate  * Arguments:
11947c478bd9Sstevel@tonic-gate  *	old_mem		- pointer to old memory area.
11957c478bd9Sstevel@tonic-gate  *	old_size	- size of old memory area.  0 is OK.
11967c478bd9Sstevel@tonic-gate  *	new_size	- size desired.
11977c478bd9Sstevel@tonic-gate  *
11987c478bd9Sstevel@tonic-gate  * Returns:
11997c478bd9Sstevel@tonic-gate  *	pointer to new memory area.
12007c478bd9Sstevel@tonic-gate  */
12017c478bd9Sstevel@tonic-gate static void*
12027c478bd9Sstevel@tonic-gate usba_kmem_realloc(void* old_mem, int old_size, int new_size)
12037c478bd9Sstevel@tonic-gate {
12047c478bd9Sstevel@tonic-gate 	void *new_mem = NULL;
12057c478bd9Sstevel@tonic-gate 
12067c478bd9Sstevel@tonic-gate 	if (new_size > 0) {
12077c478bd9Sstevel@tonic-gate 		new_mem = kmem_zalloc(new_size, KM_SLEEP);
12087c478bd9Sstevel@tonic-gate 		if (old_size > 0) {
12097c478bd9Sstevel@tonic-gate 			bcopy(old_mem, new_mem,
12107c478bd9Sstevel@tonic-gate 			    min(old_size, new_size));
12117c478bd9Sstevel@tonic-gate 		}
12127c478bd9Sstevel@tonic-gate 	}
12137c478bd9Sstevel@tonic-gate 
12147c478bd9Sstevel@tonic-gate 	if (old_size > 0) {
12157c478bd9Sstevel@tonic-gate 		kmem_free(old_mem, old_size);
12167c478bd9Sstevel@tonic-gate 	}
12177c478bd9Sstevel@tonic-gate 
12187c478bd9Sstevel@tonic-gate 	return (new_mem);
12197c478bd9Sstevel@tonic-gate }
12207c478bd9Sstevel@tonic-gate 
12217c478bd9Sstevel@tonic-gate 
12227c478bd9Sstevel@tonic-gate /*
12237c478bd9Sstevel@tonic-gate  * usba_augment_array:
12247c478bd9Sstevel@tonic-gate  *	Add a new element on the end of an array.
12257c478bd9Sstevel@tonic-gate  *
12267c478bd9Sstevel@tonic-gate  * Arguments:
12277c478bd9Sstevel@tonic-gate  *	addr		- ptr to the array address.  Array addr will change.
12287c478bd9Sstevel@tonic-gate  *	n_elements	- array element count.
12297c478bd9Sstevel@tonic-gate  *	element_size	- size of an array element
12307c478bd9Sstevel@tonic-gate  */
12317c478bd9Sstevel@tonic-gate static void
12327c478bd9Sstevel@tonic-gate usba_augment_array(void **addr, uint_t n_elements, uint_t element_size)
12337c478bd9Sstevel@tonic-gate {
12347c478bd9Sstevel@tonic-gate 	*addr = usba_kmem_realloc(*addr, (n_elements * element_size),
1235*112116d8Sfb 	    ((n_elements + 1) * element_size));
12367c478bd9Sstevel@tonic-gate }
12377c478bd9Sstevel@tonic-gate 
12387c478bd9Sstevel@tonic-gate 
12397c478bd9Sstevel@tonic-gate /*
12407c478bd9Sstevel@tonic-gate  * usba_make_alts_sparse:
12417c478bd9Sstevel@tonic-gate  *	Disburse alternate array elements such that they are at the proper array
12427c478bd9Sstevel@tonic-gate  *	indices for which alt they represent.  It is assumed that all key values
12437c478bd9Sstevel@tonic-gate  *	used for ordering the elements are positive.  Original array space may
12447c478bd9Sstevel@tonic-gate  *	be freed and new space allocated.
12457c478bd9Sstevel@tonic-gate  *
12467c478bd9Sstevel@tonic-gate  * Arguments:
12477c478bd9Sstevel@tonic-gate  *	array		- pointer to alternates array; may be modified
12487c478bd9Sstevel@tonic-gate  *	n_elements	- number of elements in the array; may be modified
12497c478bd9Sstevel@tonic-gate  */
12507c478bd9Sstevel@tonic-gate static void
12517c478bd9Sstevel@tonic-gate usba_make_alts_sparse(usb_alt_if_data_t **array, uint_t *n_elements)
12527c478bd9Sstevel@tonic-gate {
12537c478bd9Sstevel@tonic-gate 	uint_t	n_orig_elements = *n_elements;
12547c478bd9Sstevel@tonic-gate 	uint8_t smallest_value;
12557c478bd9Sstevel@tonic-gate 	uint8_t largest_value;
12567c478bd9Sstevel@tonic-gate 	uint8_t curr_value;
12577c478bd9Sstevel@tonic-gate 	uint_t	in_order = 0;
12587c478bd9Sstevel@tonic-gate 	usb_alt_if_data_t *orig_addr = *array; /* Non-sparse array base ptr */
12597c478bd9Sstevel@tonic-gate 	usb_alt_if_data_t *repl_array;	/* Base ptr to sparse array */
12607c478bd9Sstevel@tonic-gate 	uint_t	n_repl_elements;	/* Number elements in the new array */
12617c478bd9Sstevel@tonic-gate 	uint_t	i;
12627c478bd9Sstevel@tonic-gate 
12637c478bd9Sstevel@tonic-gate 	/* Check for a null array. */
12647c478bd9Sstevel@tonic-gate 	if ((array == NULL) || (n_orig_elements == 0)) {
12657c478bd9Sstevel@tonic-gate 
12667c478bd9Sstevel@tonic-gate 		return;
12677c478bd9Sstevel@tonic-gate 	}
12687c478bd9Sstevel@tonic-gate 
12697c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
12707c478bd9Sstevel@tonic-gate 	    "make_sparse: array=0x%p, n_orig_elements=%d",
1271*112116d8Sfb 	    (void *)array, n_orig_elements);
12727c478bd9Sstevel@tonic-gate 
12737c478bd9Sstevel@tonic-gate 	curr_value = orig_addr[0].altif_descr.bAlternateSetting;
12747c478bd9Sstevel@tonic-gate 	smallest_value = largest_value = curr_value;
12757c478bd9Sstevel@tonic-gate 
12767c478bd9Sstevel@tonic-gate 	/* Figure the low-high range of the array. */
12777c478bd9Sstevel@tonic-gate 	for (i = 1; i < n_orig_elements; i++) {
12787c478bd9Sstevel@tonic-gate 		curr_value = orig_addr[i].altif_descr.bAlternateSetting;
12797c478bd9Sstevel@tonic-gate 		if (curr_value < smallest_value) {
12807c478bd9Sstevel@tonic-gate 			smallest_value = curr_value;
12817c478bd9Sstevel@tonic-gate 		} else if (curr_value > largest_value) {
12827c478bd9Sstevel@tonic-gate 			in_order++;
12837c478bd9Sstevel@tonic-gate 			largest_value = curr_value;
12847c478bd9Sstevel@tonic-gate 		}
12857c478bd9Sstevel@tonic-gate 	}
12867c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
1287*112116d8Sfb 	    "make_sparse: largest=%d, smallest=%d, "
1288*112116d8Sfb 	    "order=%d",
1289*112116d8Sfb 	    largest_value, smallest_value, in_order);
12907c478bd9Sstevel@tonic-gate 
12917c478bd9Sstevel@tonic-gate 	n_repl_elements = largest_value + 1;
12927c478bd9Sstevel@tonic-gate 
12937c478bd9Sstevel@tonic-gate 	/*
12947c478bd9Sstevel@tonic-gate 	 * No holes to leave, array starts at zero, and everything is already
12957c478bd9Sstevel@tonic-gate 	 * in order.  Just return original array.
12967c478bd9Sstevel@tonic-gate 	 */
12977c478bd9Sstevel@tonic-gate 	if ((n_repl_elements == n_orig_elements) &&
12987c478bd9Sstevel@tonic-gate 	    ((in_order + 1) == n_orig_elements)) {
12997c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
1300*112116d8Sfb 		    "No holes");
13017c478bd9Sstevel@tonic-gate 
13027c478bd9Sstevel@tonic-gate 		return;
13037c478bd9Sstevel@tonic-gate 	}
13047c478bd9Sstevel@tonic-gate 
13057c478bd9Sstevel@tonic-gate 	/* Allocate zeroed space for the array. */
13067c478bd9Sstevel@tonic-gate 	repl_array = kmem_zalloc(
1307*112116d8Sfb 	    (n_repl_elements * sizeof (usb_alt_if_data_t)), KM_SLEEP);
13087c478bd9Sstevel@tonic-gate 
13097c478bd9Sstevel@tonic-gate 	/* Now fill in the array. */
13107c478bd9Sstevel@tonic-gate 	for (i = 0; i < n_orig_elements; i++) {
13117c478bd9Sstevel@tonic-gate 		curr_value = orig_addr[i].altif_descr.bAlternateSetting;
13127c478bd9Sstevel@tonic-gate 
13137c478bd9Sstevel@tonic-gate 		/* Place in sparse array based on key. */
13147c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
1315*112116d8Sfb 		    "move %lu bytes (key %d) from 0x%p to 0x%p",
1316*112116d8Sfb 		    sizeof (usb_alt_if_data_t), curr_value,
1317*112116d8Sfb 		    (void *)&orig_addr[i], (void *)&repl_array[curr_value]);
13187c478bd9Sstevel@tonic-gate 
13197c478bd9Sstevel@tonic-gate 		bcopy((char *)&orig_addr[i], (char *)&repl_array[curr_value],
13207c478bd9Sstevel@tonic-gate 		    sizeof (usb_alt_if_data_t));
13217c478bd9Sstevel@tonic-gate 	}
13227c478bd9Sstevel@tonic-gate 
13237c478bd9Sstevel@tonic-gate 	kmem_free(*array, sizeof (usb_alt_if_data_t) * n_orig_elements);
13247c478bd9Sstevel@tonic-gate 	*array = repl_array;
13257c478bd9Sstevel@tonic-gate 	*n_elements = n_repl_elements;
13267c478bd9Sstevel@tonic-gate }
13277c478bd9Sstevel@tonic-gate 
13287c478bd9Sstevel@tonic-gate 
13297c478bd9Sstevel@tonic-gate /*
13307c478bd9Sstevel@tonic-gate  * usba_order_tree:
13317c478bd9Sstevel@tonic-gate  *	Take a tree as built by usba_build_descr_tree and make sure the key
13327c478bd9Sstevel@tonic-gate  *	values of all elements match their indeces.  Proper order is implied.
13337c478bd9Sstevel@tonic-gate  *
13347c478bd9Sstevel@tonic-gate  * Arguments:
13357c478bd9Sstevel@tonic-gate  *	state		- Pointer to this module's state structure.
13367c478bd9Sstevel@tonic-gate  */
13377c478bd9Sstevel@tonic-gate static void
13387c478bd9Sstevel@tonic-gate usba_order_tree(usba_reg_state_t *state)
13397c478bd9Sstevel@tonic-gate {
13407c478bd9Sstevel@tonic-gate 	usb_cfg_data_t	*this_cfg;
13417c478bd9Sstevel@tonic-gate 	usb_if_data_t	*this_if;
13427c478bd9Sstevel@tonic-gate 	uint_t		n_cfgs = state->st_dev_n_cfg;
13437c478bd9Sstevel@tonic-gate 	uint_t		cfg;
13447c478bd9Sstevel@tonic-gate 	uint_t		which_if;
13457c478bd9Sstevel@tonic-gate 
13467c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
13477c478bd9Sstevel@tonic-gate 	    "usba_order_tree:");
13487c478bd9Sstevel@tonic-gate 
13497c478bd9Sstevel@tonic-gate 	for (cfg = 0; cfg < n_cfgs; cfg++) {
13507c478bd9Sstevel@tonic-gate 		this_cfg = &state->st_dev_cfg[cfg];
13517c478bd9Sstevel@tonic-gate 
13527c478bd9Sstevel@tonic-gate 		for (which_if = 0; which_if < this_cfg->cfg_n_if; which_if++) {
13537c478bd9Sstevel@tonic-gate 			this_if = this_cfg->cfg_if;
13547c478bd9Sstevel@tonic-gate 			usba_make_alts_sparse(&this_if->if_alt,
1355*112116d8Sfb 			    &this_if->if_n_alt);
13567c478bd9Sstevel@tonic-gate 		}
13577c478bd9Sstevel@tonic-gate 	}
13587c478bd9Sstevel@tonic-gate }
13597c478bd9Sstevel@tonic-gate 
13607c478bd9Sstevel@tonic-gate 
13617c478bd9Sstevel@tonic-gate /*
13627c478bd9Sstevel@tonic-gate  * usb_free_descr_tree:
13637c478bd9Sstevel@tonic-gate  *	Take down the configuration tree.  Called internally and can be called
13647c478bd9Sstevel@tonic-gate  *	from a driver standalone to take the tree down while leaving the rest
13657c478bd9Sstevel@tonic-gate  *	of the registration intact.
13667c478bd9Sstevel@tonic-gate  *
13677c478bd9Sstevel@tonic-gate  * Arguments:
13687c478bd9Sstevel@tonic-gate  *	dip		- pointer to devinfo of the device
13697c478bd9Sstevel@tonic-gate  *	dev_data	- pointer to registration data containing the tree.
13707c478bd9Sstevel@tonic-gate  */
13717c478bd9Sstevel@tonic-gate void
13727c478bd9Sstevel@tonic-gate usb_free_descr_tree(dev_info_t *dip, usb_client_dev_data_t *dev_data)
13737c478bd9Sstevel@tonic-gate {
13747c478bd9Sstevel@tonic-gate 	usb_cfg_data_t *cfg_array;
13757c478bd9Sstevel@tonic-gate 	int n_cfgs;
13767c478bd9Sstevel@tonic-gate 	int cfg;
13777c478bd9Sstevel@tonic-gate 
13787c478bd9Sstevel@tonic-gate 	if ((dip == NULL) || (dev_data == NULL)) {
13797c478bd9Sstevel@tonic-gate 
13807c478bd9Sstevel@tonic-gate 		return;
13817c478bd9Sstevel@tonic-gate 	}
13827c478bd9Sstevel@tonic-gate 	cfg_array = dev_data->dev_cfg;
13837c478bd9Sstevel@tonic-gate 	n_cfgs = dev_data->dev_n_cfg;
13847c478bd9Sstevel@tonic-gate 
13857c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
13867c478bd9Sstevel@tonic-gate 	    "usb_free_descr_tree starting for %s%d",
13877c478bd9Sstevel@tonic-gate 	    ddi_driver_name(dip), ddi_get_instance(dip));
13887c478bd9Sstevel@tonic-gate 
13897c478bd9Sstevel@tonic-gate 	for (cfg = 0; cfg < n_cfgs; cfg++) {
13907c478bd9Sstevel@tonic-gate 		if (cfg_array[cfg].cfg_if) {
13917c478bd9Sstevel@tonic-gate 			usba_free_if_array(cfg_array[cfg].cfg_if,
1392*112116d8Sfb 			    cfg_array[cfg].cfg_n_if);
13937c478bd9Sstevel@tonic-gate 		}
13947c478bd9Sstevel@tonic-gate 		if (cfg_array[cfg].cfg_cvs) {
13957c478bd9Sstevel@tonic-gate 			usba_free_cv_array(cfg_array[cfg].cfg_cvs,
1396*112116d8Sfb 			    cfg_array[cfg].cfg_n_cvs);
13977c478bd9Sstevel@tonic-gate 		}
13987c478bd9Sstevel@tonic-gate 		if (cfg_array[cfg].cfg_str) {
13997c478bd9Sstevel@tonic-gate 			kmem_free(cfg_array[cfg].cfg_str,
1400*112116d8Sfb 			    cfg_array[cfg].cfg_strsize);
14017c478bd9Sstevel@tonic-gate 		}
14027c478bd9Sstevel@tonic-gate 	}
14037c478bd9Sstevel@tonic-gate 
14047c478bd9Sstevel@tonic-gate 	if (cfg_array) {
14057c478bd9Sstevel@tonic-gate 		kmem_free(cfg_array, (sizeof (usb_cfg_data_t) * n_cfgs));
14067c478bd9Sstevel@tonic-gate 	}
14077c478bd9Sstevel@tonic-gate 
14087c478bd9Sstevel@tonic-gate 	dev_data->dev_parse_level = USB_PARSE_LVL_NONE;
14097c478bd9Sstevel@tonic-gate 	dev_data->dev_n_cfg = 0;
14107c478bd9Sstevel@tonic-gate 	dev_data->dev_cfg = NULL;
14117c478bd9Sstevel@tonic-gate 	dev_data->dev_curr_cfg = NULL;
14127c478bd9Sstevel@tonic-gate 
14137c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_REGISTER, usbai_reg_log_handle,
14147c478bd9Sstevel@tonic-gate 	    "usb_free_descr_tree done");
14157c478bd9Sstevel@tonic-gate }
14167c478bd9Sstevel@tonic-gate 
14177c478bd9Sstevel@tonic-gate 
14187c478bd9Sstevel@tonic-gate /*
14197c478bd9Sstevel@tonic-gate  * usba_free_if_array:
14207c478bd9Sstevel@tonic-gate  *	Free a configuration's array of interface nodes and their subtrees of
14217c478bd9Sstevel@tonic-gate  *	interface alternate, endpoint and c/v descriptors.
14227c478bd9Sstevel@tonic-gate  *
14237c478bd9Sstevel@tonic-gate  * Arguments:
14247c478bd9Sstevel@tonic-gate  *	if_array	- pointer to array of interfaces to remove.
14257c478bd9Sstevel@tonic-gate  *	n_ifs		- number of elements in the array to remove.
14267c478bd9Sstevel@tonic-gate  */
14277c478bd9Sstevel@tonic-gate static void
14287c478bd9Sstevel@tonic-gate usba_free_if_array(usb_if_data_t *if_array, uint_t n_ifs)
14297c478bd9Sstevel@tonic-gate {
14307c478bd9Sstevel@tonic-gate 	uint_t which_if;
14317c478bd9Sstevel@tonic-gate 	uint_t which_alt;
14327c478bd9Sstevel@tonic-gate 	uint_t n_alts;
14337c478bd9Sstevel@tonic-gate 	usb_alt_if_data_t *altif;
14347c478bd9Sstevel@tonic-gate 
14357c478bd9Sstevel@tonic-gate 	for (which_if = 0; which_if < n_ifs; which_if++) {
14367c478bd9Sstevel@tonic-gate 		n_alts = if_array[which_if].if_n_alt;
14377c478bd9Sstevel@tonic-gate 
14387c478bd9Sstevel@tonic-gate 		/* Every interface has at least one alternate. */
14397c478bd9Sstevel@tonic-gate 		for (which_alt = 0; which_alt < n_alts; which_alt++) {
14407c478bd9Sstevel@tonic-gate 			altif = &if_array[which_if].if_alt[which_alt];
14417c478bd9Sstevel@tonic-gate 			usba_free_ep_array(altif->altif_ep, altif->altif_n_ep);
14427c478bd9Sstevel@tonic-gate 			usba_free_cv_array(altif->altif_cvs,
1443*112116d8Sfb 			    altif->altif_n_cvs);
14447c478bd9Sstevel@tonic-gate 			kmem_free(altif->altif_str, altif->altif_strsize);
14457c478bd9Sstevel@tonic-gate 		}
14467c478bd9Sstevel@tonic-gate 
14477c478bd9Sstevel@tonic-gate 		kmem_free(if_array[which_if].if_alt,
14487c478bd9Sstevel@tonic-gate 		    (sizeof (usb_alt_if_data_t) * n_alts));
14497c478bd9Sstevel@tonic-gate 	}
14507c478bd9Sstevel@tonic-gate 
14517c478bd9Sstevel@tonic-gate 	/* Free the interface array itself. */
14527c478bd9Sstevel@tonic-gate 	kmem_free(if_array, (sizeof (usb_if_data_t) * n_ifs));
14537c478bd9Sstevel@tonic-gate }
14547c478bd9Sstevel@tonic-gate 
14557c478bd9Sstevel@tonic-gate 
14567c478bd9Sstevel@tonic-gate /*
14577c478bd9Sstevel@tonic-gate  * usba_free_ep_array:
14587c478bd9Sstevel@tonic-gate  *	Free an array of endpoint nodes and their subtrees of c/v descriptors.
14597c478bd9Sstevel@tonic-gate  *
14607c478bd9Sstevel@tonic-gate  * Arguments:
14617c478bd9Sstevel@tonic-gate  *	ep_array	- pointer to array of endpoints to remove.
14627c478bd9Sstevel@tonic-gate  *	n_eps		- number of elements in the array to remove.
14637c478bd9Sstevel@tonic-gate  */
14647c478bd9Sstevel@tonic-gate static void
14657c478bd9Sstevel@tonic-gate usba_free_ep_array(usb_ep_data_t *ep_array, uint_t n_eps)
14667c478bd9Sstevel@tonic-gate {
14677c478bd9Sstevel@tonic-gate 	uint_t ep;
14687c478bd9Sstevel@tonic-gate 
14697c478bd9Sstevel@tonic-gate 	for (ep = 0; ep < n_eps; ep++) {
14707c478bd9Sstevel@tonic-gate 		usba_free_cv_array(ep_array[ep].ep_cvs, ep_array[ep].ep_n_cvs);
14717c478bd9Sstevel@tonic-gate 	}
14727c478bd9Sstevel@tonic-gate 
14737c478bd9Sstevel@tonic-gate 	kmem_free(ep_array, (sizeof (usb_ep_data_t) * n_eps));
14747c478bd9Sstevel@tonic-gate }
14757c478bd9Sstevel@tonic-gate 
14767c478bd9Sstevel@tonic-gate 
14777c478bd9Sstevel@tonic-gate /*
14787c478bd9Sstevel@tonic-gate  * usba_free_cv_array:
14797c478bd9Sstevel@tonic-gate  *	Free an array of class/vendor (c/v) descriptor nodes.
14807c478bd9Sstevel@tonic-gate  *
14817c478bd9Sstevel@tonic-gate  * Arguments:
14827c478bd9Sstevel@tonic-gate  *	cv_array	- pointer to array of c/v nodes to remove.
14837c478bd9Sstevel@tonic-gate  *	n_cvs		- number of elements in the array to remove.
14847c478bd9Sstevel@tonic-gate  */
14857c478bd9Sstevel@tonic-gate static void
14867c478bd9Sstevel@tonic-gate usba_free_cv_array(usb_cvs_data_t *cv_array, uint_t n_cvs)
14877c478bd9Sstevel@tonic-gate {
14887c478bd9Sstevel@tonic-gate 	uint_t cv_node;
14897c478bd9Sstevel@tonic-gate 
14907c478bd9Sstevel@tonic-gate 	/* Free data areas hanging off of each c/v descriptor. */
14917c478bd9Sstevel@tonic-gate 	for (cv_node = 0; cv_node < n_cvs; cv_node++) {
14927c478bd9Sstevel@tonic-gate 		kmem_free(cv_array[cv_node].cvs_buf,
1493*112116d8Sfb 		    cv_array[cv_node].cvs_buf_len);
14947c478bd9Sstevel@tonic-gate 	}
14957c478bd9Sstevel@tonic-gate 
14967c478bd9Sstevel@tonic-gate 	/* Free the array of cv descriptors. */
14977c478bd9Sstevel@tonic-gate 	kmem_free(cv_array, (sizeof (usb_cvs_data_t) * n_cvs));
14987c478bd9Sstevel@tonic-gate }
14997c478bd9Sstevel@tonic-gate 
15007c478bd9Sstevel@tonic-gate 
15017c478bd9Sstevel@tonic-gate /*
15027c478bd9Sstevel@tonic-gate  * usb_log_descr_tree:
15037c478bd9Sstevel@tonic-gate  *	Log to the usba_debug_buf a descriptor tree as returned by
15047c478bd9Sstevel@tonic-gate  *	usbai_register_client.
15057c478bd9Sstevel@tonic-gate  *
15067c478bd9Sstevel@tonic-gate  * Arguments:
15077c478bd9Sstevel@tonic-gate  *	dev_data	- pointer to registration area containing the tree
15087c478bd9Sstevel@tonic-gate  *	log_handle	- pointer to log handle to use for dumping.
15097c478bd9Sstevel@tonic-gate  *	level		- print level, one of USB_LOG_L0 ... USB_LOG_L4
15107c478bd9Sstevel@tonic-gate  *			  Please see usb_log(9F) for details.
15117c478bd9Sstevel@tonic-gate  *	mask		- print mask.  Please see usb_log(9F) for details.
15127c478bd9Sstevel@tonic-gate  *
15137c478bd9Sstevel@tonic-gate  * Returns:
15147c478bd9Sstevel@tonic-gate  *	USB_SUCCESS		- tree successfully dumped
15157c478bd9Sstevel@tonic-gate  *	USB_INVALID_CONTEXT	- called from callback context
15167c478bd9Sstevel@tonic-gate  *	USB_INVALID_ARGS	- bad arguments given
15177c478bd9Sstevel@tonic-gate  */
15187c478bd9Sstevel@tonic-gate int
15197c478bd9Sstevel@tonic-gate usb_log_descr_tree(usb_client_dev_data_t *dev_data,
15207c478bd9Sstevel@tonic-gate     usb_log_handle_t log_handle, uint_t level, uint_t mask)
15217c478bd9Sstevel@tonic-gate {
15227c478bd9Sstevel@tonic-gate 	return (usba_dump_descr_tree(NULL, dev_data, log_handle, level, mask));
15237c478bd9Sstevel@tonic-gate }
15247c478bd9Sstevel@tonic-gate 
15257c478bd9Sstevel@tonic-gate 
15267c478bd9Sstevel@tonic-gate /*
15277c478bd9Sstevel@tonic-gate  * usb_print_descr_tree:
15287c478bd9Sstevel@tonic-gate  *	Print to the screen a descriptor tree as returned by
15297c478bd9Sstevel@tonic-gate  *	usbai_register_client.
15307c478bd9Sstevel@tonic-gate  *
15317c478bd9Sstevel@tonic-gate  * Arguments:
15327c478bd9Sstevel@tonic-gate  *	dip		- pointer to devinfo of the client
15337c478bd9Sstevel@tonic-gate  *	dev_data	- pointer to registration area containing the tree
15347c478bd9Sstevel@tonic-gate  *
15357c478bd9Sstevel@tonic-gate  * Returns:
15367c478bd9Sstevel@tonic-gate  *	USB_SUCCESS		- tree successfully dumped
15377c478bd9Sstevel@tonic-gate  *	USB_INVALID_CONTEXT	- called from callback context
15387c478bd9Sstevel@tonic-gate  *	USB_INVALID_ARGS	- bad arguments given
15397c478bd9Sstevel@tonic-gate  */
15407c478bd9Sstevel@tonic-gate int
15417c478bd9Sstevel@tonic-gate usb_print_descr_tree(dev_info_t *dip, usb_client_dev_data_t *dev_data)
15427c478bd9Sstevel@tonic-gate {
15437c478bd9Sstevel@tonic-gate 	return (usba_dump_descr_tree(dip, dev_data, NULL, 0, 0));
15447c478bd9Sstevel@tonic-gate }
15457c478bd9Sstevel@tonic-gate 
15467c478bd9Sstevel@tonic-gate 
15477c478bd9Sstevel@tonic-gate /*
15487c478bd9Sstevel@tonic-gate  * usba_dump_descr_tree:
15497c478bd9Sstevel@tonic-gate  *	Dump a descriptor tree.
15507c478bd9Sstevel@tonic-gate  *
15517c478bd9Sstevel@tonic-gate  * Arguments:
15527c478bd9Sstevel@tonic-gate  *	dip		- pointer to devinfo of the client.  Used when no
15537c478bd9Sstevel@tonic-gate  *			  log_handle argument given.
15547c478bd9Sstevel@tonic-gate  *	usb_reg		- pointer to registration area containing the tree
15557c478bd9Sstevel@tonic-gate  *	log_handle	- pointer to log handle to use for dumping.  If NULL,
15567c478bd9Sstevel@tonic-gate  *			  use internal log handle, which dumps to screen.
15577c478bd9Sstevel@tonic-gate  *	level		- print level, one of USB_LOG_L0 ... USB_LOG_L4
15587c478bd9Sstevel@tonic-gate  *			  Used only when log_handle provided.
15597c478bd9Sstevel@tonic-gate  *	mask		- print mask, used when log_handle argument provided.
15607c478bd9Sstevel@tonic-gate  *
15617c478bd9Sstevel@tonic-gate  * Returns:
15627c478bd9Sstevel@tonic-gate  *	USB_SUCCESS		- tree successfully dumped
15637c478bd9Sstevel@tonic-gate  *	USB_INVALID_CONTEXT	- called from callback context
15647c478bd9Sstevel@tonic-gate  *	USB_INVALID_ARGS	- bad arguments given
15657c478bd9Sstevel@tonic-gate  */
15667c478bd9Sstevel@tonic-gate static int
15677c478bd9Sstevel@tonic-gate usba_dump_descr_tree(dev_info_t *dip, usb_client_dev_data_t *usb_reg,
15687c478bd9Sstevel@tonic-gate     usb_log_handle_t log_handle, uint_t level, uint_t mask)
15697c478bd9Sstevel@tonic-gate {
15707c478bd9Sstevel@tonic-gate 	usb_log_handle_t dump_handle;
15717c478bd9Sstevel@tonic-gate 	uint_t		dump_level;
15727c478bd9Sstevel@tonic-gate 	uint_t		dump_mask;
15737c478bd9Sstevel@tonic-gate 	int		which_config; /* Counters. */
15747c478bd9Sstevel@tonic-gate 	int		which_if;
15757c478bd9Sstevel@tonic-gate 	int		which_cv;
15767c478bd9Sstevel@tonic-gate 	usb_cfg_data_t	*config; /* ptr to current configuration tree node */
15777c478bd9Sstevel@tonic-gate 	usb_cfg_descr_t *config_descr; /* and its USB descriptor. */
15787c478bd9Sstevel@tonic-gate 	char		*string;
15797c478bd9Sstevel@tonic-gate 	char		*name_string = NULL;
15807c478bd9Sstevel@tonic-gate 	int		name_string_size;
15817c478bd9Sstevel@tonic-gate 
15827c478bd9Sstevel@tonic-gate 	if ((usb_reg == NULL) || ((log_handle == NULL) && (dip == NULL))) {
15837c478bd9Sstevel@tonic-gate 
15847c478bd9Sstevel@tonic-gate 		return (USB_INVALID_ARGS);
15857c478bd9Sstevel@tonic-gate 	}
15867c478bd9Sstevel@tonic-gate 
15877c478bd9Sstevel@tonic-gate 	/*
15887c478bd9Sstevel@tonic-gate 	 * To keep calling this simple, kmem_zalloc with the sleep flag always.
15897c478bd9Sstevel@tonic-gate 	 * This means no interrupt context is allowed.
15907c478bd9Sstevel@tonic-gate 	 */
15917c478bd9Sstevel@tonic-gate 	if (servicing_interrupt()) {
15927c478bd9Sstevel@tonic-gate 
15937c478bd9Sstevel@tonic-gate 		return (USB_INVALID_CONTEXT);
15947c478bd9Sstevel@tonic-gate 	}
15957c478bd9Sstevel@tonic-gate 
15967c478bd9Sstevel@tonic-gate 	string = kmem_zalloc(USB_MAXSTRINGLEN, KM_SLEEP);
15977c478bd9Sstevel@tonic-gate 
15987c478bd9Sstevel@tonic-gate 	if (log_handle != NULL) {
15997c478bd9Sstevel@tonic-gate 		dump_level = level;
16007c478bd9Sstevel@tonic-gate 		dump_mask = mask;
16017c478bd9Sstevel@tonic-gate 		dump_handle = log_handle;
16027c478bd9Sstevel@tonic-gate 	} else {
16037c478bd9Sstevel@tonic-gate 		dump_level = USB_LOG_L1;
16047c478bd9Sstevel@tonic-gate 		dump_mask = DPRINT_MASK_ALL;
16057c478bd9Sstevel@tonic-gate 
16067c478bd9Sstevel@tonic-gate 		/* Build device name string. */
16077c478bd9Sstevel@tonic-gate 		(void) snprintf(string, USB_MAXSTRINGLEN,
1608*112116d8Sfb 		    "Port%d", usb_get_addr(dip));
16097c478bd9Sstevel@tonic-gate 		name_string_size = strlen(string) + 1;
16107c478bd9Sstevel@tonic-gate 		name_string = kmem_zalloc(name_string_size, KM_SLEEP);
16117c478bd9Sstevel@tonic-gate 		(void) strcpy(name_string, string);
16127c478bd9Sstevel@tonic-gate 
16137c478bd9Sstevel@tonic-gate 		/* Allocate a log handle specifying the name string. */
16147c478bd9Sstevel@tonic-gate 		dump_handle = usb_alloc_log_hdl(NULL, name_string,
1615*112116d8Sfb 		    &dump_level, &dump_mask, NULL,
1616*112116d8Sfb 		    USB_FLAGS_SLEEP);
16177c478bd9Sstevel@tonic-gate 	}
16187c478bd9Sstevel@tonic-gate 
16197c478bd9Sstevel@tonic-gate 	(void) usb_log(dump_handle, dump_level, dump_mask,
16207c478bd9Sstevel@tonic-gate 	    "USB descriptor tree for %s %s",
16217c478bd9Sstevel@tonic-gate 	    (usb_reg->dev_mfg != NULL ? usb_reg->dev_mfg : ""),
16227c478bd9Sstevel@tonic-gate 	    (usb_reg->dev_product != NULL ? usb_reg->dev_product : ""));
16237c478bd9Sstevel@tonic-gate 	if (usb_reg->dev_n_cfg == 0) {
16247c478bd9Sstevel@tonic-gate 		(void) usb_log(dump_handle, dump_level, dump_mask,
16257c478bd9Sstevel@tonic-gate 		    "No descriptor tree present");
16267c478bd9Sstevel@tonic-gate 	} else {
16277c478bd9Sstevel@tonic-gate 		(void) usb_log(dump_handle, dump_level, dump_mask,
16287c478bd9Sstevel@tonic-gate 		    "highest configuration found=%d", usb_reg->dev_n_cfg - 1);
16297c478bd9Sstevel@tonic-gate 	}
16307c478bd9Sstevel@tonic-gate 
16317c478bd9Sstevel@tonic-gate 	for (which_config = 0; which_config < usb_reg->dev_n_cfg;
16327c478bd9Sstevel@tonic-gate 	    which_config++) {
16337c478bd9Sstevel@tonic-gate 		config = &usb_reg->dev_cfg[which_config];
16347c478bd9Sstevel@tonic-gate 		config_descr = &config->cfg_descr;
16357c478bd9Sstevel@tonic-gate 		if (config_descr->bLength == 0) {
16367c478bd9Sstevel@tonic-gate 
16377c478bd9Sstevel@tonic-gate 			continue;
16387c478bd9Sstevel@tonic-gate 		}
16397c478bd9Sstevel@tonic-gate 		if (dump_level == USB_LOG_L0) {
16407c478bd9Sstevel@tonic-gate 			(void) usb_log(dump_handle, dump_level, dump_mask, " ");
16417c478bd9Sstevel@tonic-gate 		}
16427c478bd9Sstevel@tonic-gate 		(void) usb_log(dump_handle, dump_level, dump_mask,
1643*112116d8Sfb 		    "Configuration #%d (Addr= 0x%p)", which_config,
1644*112116d8Sfb 		    (void *)config);
16457c478bd9Sstevel@tonic-gate 		(void) usb_log(dump_handle, dump_level, dump_mask,
16467c478bd9Sstevel@tonic-gate 		    "String descr=%s", config->cfg_str);
16477c478bd9Sstevel@tonic-gate 		(void) usb_log(dump_handle, dump_level, dump_mask,
16487c478bd9Sstevel@tonic-gate 		    "config descr: len=%d tp=%d totLen=%d numIf=%d "
16497c478bd9Sstevel@tonic-gate 		    "cfgVal=%d att=0x%x pwr=%d",
16507c478bd9Sstevel@tonic-gate 		    config_descr->bLength, config_descr->bDescriptorType,
16517c478bd9Sstevel@tonic-gate 		    config_descr->wTotalLength, config_descr->bNumInterfaces,
16527c478bd9Sstevel@tonic-gate 		    config_descr->bConfigurationValue,
16537c478bd9Sstevel@tonic-gate 		    config_descr->bmAttributes, config_descr->bMaxPower);
16547c478bd9Sstevel@tonic-gate 		if ((config->cfg_n_if > 0) || (config->cfg_n_cvs > 0)) {
16557c478bd9Sstevel@tonic-gate 			(void) usb_log(dump_handle, dump_level, dump_mask,
16567c478bd9Sstevel@tonic-gate 			    "usb_cfg_data_t shows max if=%d "
16577c478bd9Sstevel@tonic-gate 			    "and %d cv descr(s).",
16587c478bd9Sstevel@tonic-gate 			    config->cfg_n_if - 1, config->cfg_n_cvs);
16597c478bd9Sstevel@tonic-gate 		}
16607c478bd9Sstevel@tonic-gate 
16617c478bd9Sstevel@tonic-gate 		for (which_if = 0; which_if < config->cfg_n_if;
16627c478bd9Sstevel@tonic-gate 		    which_if++) {
16637c478bd9Sstevel@tonic-gate 
16647c478bd9Sstevel@tonic-gate 			if (dump_level == USB_LOG_L0) {
16657c478bd9Sstevel@tonic-gate 				(void) usb_log(dump_handle, dump_level,
1666*112116d8Sfb 				    dump_mask, " ");
16677c478bd9Sstevel@tonic-gate 			}
16687c478bd9Sstevel@tonic-gate 			(void) usb_log(dump_handle, dump_level, dump_mask,
16697c478bd9Sstevel@tonic-gate 			    "	 interface #%d (0x%p)",
1670*112116d8Sfb 			    which_if, (void *)&config->cfg_if[which_if]);
16717c478bd9Sstevel@tonic-gate 			usba_dump_if(&config->cfg_if[which_if],
16727c478bd9Sstevel@tonic-gate 			    dump_handle, dump_level, dump_mask, string);
16737c478bd9Sstevel@tonic-gate 		}
16747c478bd9Sstevel@tonic-gate 
16757c478bd9Sstevel@tonic-gate 		for (which_cv = 0; which_cv < config->cfg_n_cvs; which_cv++) {
16767c478bd9Sstevel@tonic-gate 			(void) usb_log(dump_handle, dump_level, dump_mask,
16777c478bd9Sstevel@tonic-gate 			    "  config cv descriptor %d (Address=0x%p)",
1678*112116d8Sfb 			    which_cv, (void *)&config->cfg_cvs[which_cv]);
16797c478bd9Sstevel@tonic-gate 			usba_dump_cv(&config->cfg_cvs[which_cv],
16807c478bd9Sstevel@tonic-gate 			    dump_handle, dump_level, dump_mask, string, 4);
16817c478bd9Sstevel@tonic-gate 		}
16827c478bd9Sstevel@tonic-gate 	}
16837c478bd9Sstevel@tonic-gate 
16847c478bd9Sstevel@tonic-gate 	(void) usb_log(dump_handle, dump_level, dump_mask,
16857c478bd9Sstevel@tonic-gate 	    "Returning dev_curr_cfg:0x%p, dev_curr_if:%d",
1686*112116d8Sfb 	    (void *)usb_reg->dev_curr_cfg, usb_reg->dev_curr_if);
16877c478bd9Sstevel@tonic-gate 
16887c478bd9Sstevel@tonic-gate 	if (log_handle == NULL) {
16897c478bd9Sstevel@tonic-gate 		usb_free_log_hdl(dump_handle);
16907c478bd9Sstevel@tonic-gate 	}
16917c478bd9Sstevel@tonic-gate 	if (name_string != NULL) {
16927c478bd9Sstevel@tonic-gate 		kmem_free(name_string, name_string_size);
16937c478bd9Sstevel@tonic-gate 	}
16947c478bd9Sstevel@tonic-gate 	kmem_free(string, USB_MAXSTRINGLEN);
16957c478bd9Sstevel@tonic-gate 
16967c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
16977c478bd9Sstevel@tonic-gate }
16987c478bd9Sstevel@tonic-gate 
16997c478bd9Sstevel@tonic-gate 
17007c478bd9Sstevel@tonic-gate /*
17017c478bd9Sstevel@tonic-gate  * usba_dump_if:
17027c478bd9Sstevel@tonic-gate  *	Dump an interface node and its branches.
17037c478bd9Sstevel@tonic-gate  *
17047c478bd9Sstevel@tonic-gate  * Arguments:
17057c478bd9Sstevel@tonic-gate  *	which_if	- interface node to dump
17067c478bd9Sstevel@tonic-gate  *	dump_handle	- write data through this log handle
17077c478bd9Sstevel@tonic-gate  *	dump_level	- level passed to usb_log
17087c478bd9Sstevel@tonic-gate  *	dump_mask	- mask passed to usb_log
17097c478bd9Sstevel@tonic-gate  *	string		- temporary area used for processing
17107c478bd9Sstevel@tonic-gate  *
17117c478bd9Sstevel@tonic-gate  */
17127c478bd9Sstevel@tonic-gate static void
17137c478bd9Sstevel@tonic-gate usba_dump_if(usb_if_data_t *which_if, usb_log_handle_t dump_handle,
17147c478bd9Sstevel@tonic-gate     uint_t dump_level, uint_t dump_mask, char *string)
17157c478bd9Sstevel@tonic-gate {
17167c478bd9Sstevel@tonic-gate 	int		which_alt;	/* Number of alt being dumped */
17177c478bd9Sstevel@tonic-gate 	usb_alt_if_data_t *alt;		/* Pointer to it. */
17187c478bd9Sstevel@tonic-gate 	usb_if_descr_t *if_descr;	/* Pointer to its USB descr. */
17197c478bd9Sstevel@tonic-gate 	int		which_ep;	/* Endpoint counter. */
17207c478bd9Sstevel@tonic-gate 	int		which_cv;	/* C/V descr counter. */
17217c478bd9Sstevel@tonic-gate 
17227c478bd9Sstevel@tonic-gate 	for (which_alt = 0; which_alt < which_if->if_n_alt; which_alt++) {
17237c478bd9Sstevel@tonic-gate 		alt = &which_if->if_alt[which_alt];
17247c478bd9Sstevel@tonic-gate 		if_descr = &alt->altif_descr;
17257c478bd9Sstevel@tonic-gate 
17267c478bd9Sstevel@tonic-gate 		if (if_descr->bLength == 0) {
17277c478bd9Sstevel@tonic-gate 
17287c478bd9Sstevel@tonic-gate 			continue;
17297c478bd9Sstevel@tonic-gate 		}
17307c478bd9Sstevel@tonic-gate 		if (dump_level == USB_LOG_L0) {
17317c478bd9Sstevel@tonic-gate 			(void) usb_log(dump_handle, dump_level, dump_mask, " ");
17327c478bd9Sstevel@tonic-gate 		}
17337c478bd9Sstevel@tonic-gate 		(void) usb_log(dump_handle, dump_level, dump_mask,
1734*112116d8Sfb 		    "\tAlt #%d (0x%p)", which_alt, (void *)alt);
17357c478bd9Sstevel@tonic-gate 		(void) usb_log(dump_handle, dump_level, dump_mask,
17367c478bd9Sstevel@tonic-gate 		    "\tString descr=%s", alt->altif_str);
17377c478bd9Sstevel@tonic-gate 		(void) usb_log(dump_handle, dump_level, dump_mask,
17387c478bd9Sstevel@tonic-gate 		    "\tif descr: len=%d type=%d if=%d alt=%d n_ept=%d "
17397c478bd9Sstevel@tonic-gate 		    "cls=%d sub=%d proto=%d",
17407c478bd9Sstevel@tonic-gate 		    if_descr->bLength,
17417c478bd9Sstevel@tonic-gate 		    if_descr->bDescriptorType, if_descr->bInterfaceNumber,
17427c478bd9Sstevel@tonic-gate 		    if_descr->bAlternateSetting, if_descr->bNumEndpoints,
17437c478bd9Sstevel@tonic-gate 		    if_descr->bInterfaceClass, if_descr->bInterfaceSubClass,
17447c478bd9Sstevel@tonic-gate 		    if_descr->bInterfaceProtocol);
17457c478bd9Sstevel@tonic-gate 
17467c478bd9Sstevel@tonic-gate 		if ((alt->altif_n_ep > 0) || (alt->altif_n_cvs > 0)) {
17477c478bd9Sstevel@tonic-gate 			(void) usb_log(dump_handle, dump_level, dump_mask,
17487c478bd9Sstevel@tonic-gate 			    "\tusb_alt_if_data_t shows max ep=%d "
17497c478bd9Sstevel@tonic-gate 			    "and %d cv descr(s).",
17507c478bd9Sstevel@tonic-gate 			    alt->altif_n_ep - 1, alt->altif_n_cvs);
17517c478bd9Sstevel@tonic-gate 		}
17527c478bd9Sstevel@tonic-gate 
17537c478bd9Sstevel@tonic-gate 		for (which_ep = 0; which_ep < alt->altif_n_ep;
17547c478bd9Sstevel@tonic-gate 		    which_ep++) {
17557c478bd9Sstevel@tonic-gate 			if (alt->altif_ep[which_ep].ep_descr.bLength == 0) {
17567c478bd9Sstevel@tonic-gate 
17577c478bd9Sstevel@tonic-gate 				continue;
17587c478bd9Sstevel@tonic-gate 			}
17597c478bd9Sstevel@tonic-gate 			if (dump_level == USB_LOG_L0) {
17607c478bd9Sstevel@tonic-gate 				(void) usb_log(dump_handle, dump_level,
1761*112116d8Sfb 				    dump_mask, " ");
17627c478bd9Sstevel@tonic-gate 			}
17637c478bd9Sstevel@tonic-gate 			usba_dump_ep(which_ep, &alt->altif_ep[which_ep],
17647c478bd9Sstevel@tonic-gate 			    dump_handle, dump_level, dump_mask, string);
17657c478bd9Sstevel@tonic-gate 		}
17667c478bd9Sstevel@tonic-gate 
17677c478bd9Sstevel@tonic-gate 		for (which_cv = 0; which_cv < alt->altif_n_cvs; which_cv++) {
17687c478bd9Sstevel@tonic-gate 			if (dump_level == USB_LOG_L0) {
17697c478bd9Sstevel@tonic-gate 				(void) usb_log(dump_handle, dump_level,
1770*112116d8Sfb 				    dump_mask, " ");
17717c478bd9Sstevel@tonic-gate 			}
17727c478bd9Sstevel@tonic-gate 			(void) usb_log(dump_handle, dump_level, dump_mask,
17737c478bd9Sstevel@tonic-gate 			    "\talt cv descriptor #%d (0x%p), size=%d",
1774*112116d8Sfb 			    which_cv, (void *)&alt->altif_cvs[which_cv],
17757c478bd9Sstevel@tonic-gate 			    alt->altif_cvs[which_cv].cvs_buf_len);
17767c478bd9Sstevel@tonic-gate 			usba_dump_cv(&alt->altif_cvs[which_cv],
17777c478bd9Sstevel@tonic-gate 			    dump_handle, dump_level, dump_mask, string, 2);
17787c478bd9Sstevel@tonic-gate 		}
17797c478bd9Sstevel@tonic-gate 	}
17807c478bd9Sstevel@tonic-gate }
17817c478bd9Sstevel@tonic-gate 
17827c478bd9Sstevel@tonic-gate 
17837c478bd9Sstevel@tonic-gate /*
17847c478bd9Sstevel@tonic-gate  * usba_dump_ep:
17857c478bd9Sstevel@tonic-gate  *	Dump an endpoint node and its branches.
17867c478bd9Sstevel@tonic-gate  *
17877c478bd9Sstevel@tonic-gate  * Arguments:
17887c478bd9Sstevel@tonic-gate  *	which_ep	- index to display
17897c478bd9Sstevel@tonic-gate  *	ep		- endpoint node to dump
17907c478bd9Sstevel@tonic-gate  *	dump_handle	- write data through this log handle
17917c478bd9Sstevel@tonic-gate  *	dump_level	- level passed to usb_log
17927c478bd9Sstevel@tonic-gate  *	dump_mask	- mask passed to usb_log
17937c478bd9Sstevel@tonic-gate  *	string		- temporary area used for processing
17947c478bd9Sstevel@tonic-gate  *
17957c478bd9Sstevel@tonic-gate  */
17967c478bd9Sstevel@tonic-gate static void
17977c478bd9Sstevel@tonic-gate usba_dump_ep(uint_t which_ep, usb_ep_data_t *ep, usb_log_handle_t dump_handle,
17987c478bd9Sstevel@tonic-gate 		uint_t dump_level, uint_t dump_mask, char *string)
17997c478bd9Sstevel@tonic-gate {
18007c478bd9Sstevel@tonic-gate 	int which_cv;
18017c478bd9Sstevel@tonic-gate 	usb_ep_descr_t *ep_descr = &ep->ep_descr;
18027c478bd9Sstevel@tonic-gate 
18037c478bd9Sstevel@tonic-gate 	(void) usb_log(dump_handle, dump_level, dump_mask,
18047c478bd9Sstevel@tonic-gate 	    "\t    endpoint[%d], epaddr=0x%x (0x%p)", which_ep,
1805*112116d8Sfb 	    ep_descr->bEndpointAddress, (void *)ep);
18067c478bd9Sstevel@tonic-gate 	(void) usb_log(dump_handle, dump_level, dump_mask,
18077c478bd9Sstevel@tonic-gate 	    "\t    len=%d type=%d attr=0x%x pktsize=%d interval=%d",
18087c478bd9Sstevel@tonic-gate 	    ep_descr->bLength, ep_descr->bDescriptorType,
18097c478bd9Sstevel@tonic-gate 	    ep_descr->bmAttributes, ep_descr->wMaxPacketSize,
18107c478bd9Sstevel@tonic-gate 	    ep_descr->bInterval);
18117c478bd9Sstevel@tonic-gate 	if (ep->ep_n_cvs > 0) {
18127c478bd9Sstevel@tonic-gate 		(void) usb_log(dump_handle, dump_level, dump_mask,
18137c478bd9Sstevel@tonic-gate 		    "\t    usb_ep_data_t shows %d cv descr(s)", ep->ep_n_cvs);
18147c478bd9Sstevel@tonic-gate 	}
18157c478bd9Sstevel@tonic-gate 
18167c478bd9Sstevel@tonic-gate 	for (which_cv = 0; which_cv < ep->ep_n_cvs; which_cv++) {
18177c478bd9Sstevel@tonic-gate 		if (dump_level == USB_LOG_L0) {
18187c478bd9Sstevel@tonic-gate 			(void) usb_log(dump_handle, dump_level,
1819*112116d8Sfb 			    dump_mask, " ");
18207c478bd9Sstevel@tonic-gate 		}
18217c478bd9Sstevel@tonic-gate 		(void) usb_log(dump_handle, dump_level, dump_mask,
18227c478bd9Sstevel@tonic-gate 		    "\t    endpoint cv descriptor %d (0x%p), size=%d",
1823*112116d8Sfb 		    which_cv, (void *)&ep->ep_cvs[which_cv],
18247c478bd9Sstevel@tonic-gate 		    ep->ep_cvs[which_cv].cvs_buf_len);
18257c478bd9Sstevel@tonic-gate 		usba_dump_cv(&ep->ep_cvs[which_cv],
18267c478bd9Sstevel@tonic-gate 		    dump_handle, dump_level, dump_mask, string, 3);
18277c478bd9Sstevel@tonic-gate 	}
18287c478bd9Sstevel@tonic-gate }
18297c478bd9Sstevel@tonic-gate 
18307c478bd9Sstevel@tonic-gate 
18317c478bd9Sstevel@tonic-gate /*
18327c478bd9Sstevel@tonic-gate  * usba_dump_cv:
18337c478bd9Sstevel@tonic-gate  *	Dump a raw class or vendor specific descriptor.
18347c478bd9Sstevel@tonic-gate  *
18357c478bd9Sstevel@tonic-gate  * Arguments:
18367c478bd9Sstevel@tonic-gate  *	cv_node		- pointer to the descriptor to dump
18377c478bd9Sstevel@tonic-gate  *	dump_handle	- write data through this log handle
18387c478bd9Sstevel@tonic-gate  *	dump_level	- level passed to usb_log
18397c478bd9Sstevel@tonic-gate  *	dump_mask	- mask passed to usb_log
18407c478bd9Sstevel@tonic-gate  *	string		- temporary area used for processing
18417c478bd9Sstevel@tonic-gate  *	indent		- number of tabs to indent output
18427c478bd9Sstevel@tonic-gate  *
18437c478bd9Sstevel@tonic-gate  */
18447c478bd9Sstevel@tonic-gate static void
18457c478bd9Sstevel@tonic-gate usba_dump_cv(usb_cvs_data_t *cv_node, usb_log_handle_t dump_handle,
18467c478bd9Sstevel@tonic-gate     uint_t dump_level, uint_t dump_mask, char *string, int indent)
18477c478bd9Sstevel@tonic-gate {
18487c478bd9Sstevel@tonic-gate 	if (cv_node) {
18497c478bd9Sstevel@tonic-gate 		usba_dump_bin(cv_node->cvs_buf, cv_node->cvs_buf_len, indent,
1850*112116d8Sfb 		    dump_handle, dump_level, dump_mask, string,
1851*112116d8Sfb 		    USB_MAXSTRINGLEN);
18527c478bd9Sstevel@tonic-gate 	}
18537c478bd9Sstevel@tonic-gate }
18547c478bd9Sstevel@tonic-gate 
18557c478bd9Sstevel@tonic-gate 
18567c478bd9Sstevel@tonic-gate /*
18577c478bd9Sstevel@tonic-gate  * usba_dump_bin:
18587c478bd9Sstevel@tonic-gate  *	Generic byte dump function.
18597c478bd9Sstevel@tonic-gate  *
18607c478bd9Sstevel@tonic-gate  * Arguments:
18617c478bd9Sstevel@tonic-gate  *	data		- pointer to the data to dump
18627c478bd9Sstevel@tonic-gate  *	max_bytes	- amount of data to dump
18637c478bd9Sstevel@tonic-gate  *	indent		- number of indentation levels
18647c478bd9Sstevel@tonic-gate  *	dump_handle	- write data through this log handle
18657c478bd9Sstevel@tonic-gate  *	dump_level	- level passed to usb_log
18667c478bd9Sstevel@tonic-gate  *	dump_mask	- mask passed to usb_log
18677c478bd9Sstevel@tonic-gate  *	buffer		- temporary area used for processing
18687c478bd9Sstevel@tonic-gate  *	bufferlen	- size of the temporary string area
18697c478bd9Sstevel@tonic-gate  *
18707c478bd9Sstevel@tonic-gate  */
18717c478bd9Sstevel@tonic-gate static void
18727c478bd9Sstevel@tonic-gate usba_dump_bin(uint8_t *data, int max_bytes, int indent,
18737c478bd9Sstevel@tonic-gate     usb_log_handle_t dump_handle, uint_t dump_level, uint_t dump_mask,
18747c478bd9Sstevel@tonic-gate     char *buffer, int bufferlen)
18757c478bd9Sstevel@tonic-gate {
18767c478bd9Sstevel@tonic-gate 	int i;
18777c478bd9Sstevel@tonic-gate 	int bufoffset = 0;
18787c478bd9Sstevel@tonic-gate 	int nexthere;
18797c478bd9Sstevel@tonic-gate 
18807c478bd9Sstevel@tonic-gate 	if ((indent * SPACES_PER_INDENT) >
18817c478bd9Sstevel@tonic-gate 	    (bufferlen - (BINDUMP_BYTES_PER_LINE * 3))) {
18827c478bd9Sstevel@tonic-gate 		(void) usb_log(dump_handle, dump_level, dump_mask,
18837c478bd9Sstevel@tonic-gate 		    "Offset to usb_dump_bin must be %d or less.  "
18847c478bd9Sstevel@tonic-gate 		    "Setting to 0.\n",
18857c478bd9Sstevel@tonic-gate 		    (bufferlen - (BINDUMP_BYTES_PER_LINE * 3)));
18867c478bd9Sstevel@tonic-gate 		indent = 0;
18877c478bd9Sstevel@tonic-gate 	}
18887c478bd9Sstevel@tonic-gate 
18897c478bd9Sstevel@tonic-gate 	/* Assume a tab is 2 four-space units. */
18907c478bd9Sstevel@tonic-gate 	for (i = 0; i < indent/2; i++) {
1891*112116d8Sfb 		buffer[bufoffset] = '\t';
1892*112116d8Sfb 		bufoffset++;
18937c478bd9Sstevel@tonic-gate 	}
18947c478bd9Sstevel@tonic-gate 
18957c478bd9Sstevel@tonic-gate 	if (indent % 2) {
18967c478bd9Sstevel@tonic-gate 		(void) strcpy(&buffer[bufoffset], INDENT_SPACE_STR);
18977c478bd9Sstevel@tonic-gate 		bufoffset += SPACES_PER_INDENT;
18987c478bd9Sstevel@tonic-gate 	}
18997c478bd9Sstevel@tonic-gate 
19007c478bd9Sstevel@tonic-gate 	i = 0;			/* Num dumped bytes put on this line. */
19017c478bd9Sstevel@tonic-gate 	nexthere = bufoffset;
19027c478bd9Sstevel@tonic-gate 	while (i < max_bytes) {
19037c478bd9Sstevel@tonic-gate 		(void) sprintf(&buffer[nexthere], "%2x ", *data++);
19047c478bd9Sstevel@tonic-gate 		nexthere += 3;
19057c478bd9Sstevel@tonic-gate 		i++;
19067c478bd9Sstevel@tonic-gate 		if (!(i % BINDUMP_BYTES_PER_LINE)) {
19077c478bd9Sstevel@tonic-gate 			buffer[nexthere] = '\0';
19087c478bd9Sstevel@tonic-gate 			(void) usb_log(dump_handle, dump_level, dump_mask,
19097c478bd9Sstevel@tonic-gate 			    buffer);
19107c478bd9Sstevel@tonic-gate 			nexthere = bufoffset;
19117c478bd9Sstevel@tonic-gate 		}
19127c478bd9Sstevel@tonic-gate 	}
19137c478bd9Sstevel@tonic-gate 
19147c478bd9Sstevel@tonic-gate 	if (nexthere > bufoffset) {
19157c478bd9Sstevel@tonic-gate 		buffer[nexthere] = '\0';
19167c478bd9Sstevel@tonic-gate 		(void) usb_log(dump_handle, dump_level, dump_mask, buffer);
19177c478bd9Sstevel@tonic-gate 	}
19187c478bd9Sstevel@tonic-gate }
1919