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
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  *
227c478bd9Sstevel@tonic-gate  * Copyright 2005 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  * UGEN: USB Generic Driver support code
307c478bd9Sstevel@tonic-gate  *
317c478bd9Sstevel@tonic-gate  * This code provides entry points called by the ugen driver or other
327c478bd9Sstevel@tonic-gate  * drivers that want to export a ugen interface
337c478bd9Sstevel@tonic-gate  *
347c478bd9Sstevel@tonic-gate  * The "Universal Generic Driver"  (UGEN) for USB devices provides interfaces
357c478bd9Sstevel@tonic-gate  * to  talk to	USB  devices.  This is	very  useful for  Point of Sale sale
367c478bd9Sstevel@tonic-gate  * devices and other simple  devices like  USB	scanner, USB palm  pilot.
377c478bd9Sstevel@tonic-gate  * The UGEN provides a system call interface to USB  devices  enabling
387c478bd9Sstevel@tonic-gate  * a USB device vendor to  write an  application for his
397c478bd9Sstevel@tonic-gate  * device instead of  writing a driver. This facilitates the vendor to write
407c478bd9Sstevel@tonic-gate  * device management s/w quickly in userland.
417c478bd9Sstevel@tonic-gate  *
427c478bd9Sstevel@tonic-gate  * UGEN supports read/write/poll entry points. An application can be written
437c478bd9Sstevel@tonic-gate  * using  read/write/aioread/aiowrite/poll  system calls to communicate
447c478bd9Sstevel@tonic-gate  * with the device.
457c478bd9Sstevel@tonic-gate  *
467c478bd9Sstevel@tonic-gate  * XXX Theory of Operations
477c478bd9Sstevel@tonic-gate  */
487c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usbai_version.h>
497c478bd9Sstevel@tonic-gate #include <sys/usb/usba.h>
507c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
517c478bd9Sstevel@tonic-gate 
527c478bd9Sstevel@tonic-gate #include "sys/usb/clients/ugen/usb_ugen.h"
537c478bd9Sstevel@tonic-gate #include "sys/usb/usba/usba_ugen.h"
547c478bd9Sstevel@tonic-gate #include "sys/usb/usba/usba_ugend.h"
557c478bd9Sstevel@tonic-gate 
567c478bd9Sstevel@tonic-gate /* Debugging information */
577c478bd9Sstevel@tonic-gate static uint_t ugen_errmask		= (uint_t)UGEN_PRINT_ALL;
587c478bd9Sstevel@tonic-gate static uint_t ugen_errlevel		= USB_LOG_L4;
597c478bd9Sstevel@tonic-gate static uint_t ugen_instance_debug	= (uint_t)-1;
607c478bd9Sstevel@tonic-gate 
617c478bd9Sstevel@tonic-gate /* default endpoint descriptor */
627c478bd9Sstevel@tonic-gate static usb_ep_descr_t  ugen_default_ep_descr =
637c478bd9Sstevel@tonic-gate 	{7, 5, 0, USB_EP_ATTR_CONTROL, 8, 0};
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate /* tunables */
667c478bd9Sstevel@tonic-gate static int	ugen_busy_loop		= 60;	/* secs */
677c478bd9Sstevel@tonic-gate static int	ugen_ctrl_timeout	= 10;
687c478bd9Sstevel@tonic-gate static int	ugen_bulk_timeout	= 10;
697c478bd9Sstevel@tonic-gate static int	ugen_intr_timeout	= 10;
707c478bd9Sstevel@tonic-gate static int	ugen_enable_pm		= 0;
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate /* local function prototypes */
747c478bd9Sstevel@tonic-gate static int	ugen_cleanup(ugen_state_t *);
757c478bd9Sstevel@tonic-gate static int	ugen_cpr_suspend(ugen_state_t *);
767c478bd9Sstevel@tonic-gate static void	ugen_cpr_resume(ugen_state_t *);
777c478bd9Sstevel@tonic-gate 
787c478bd9Sstevel@tonic-gate static void	ugen_restore_state(ugen_state_t *);
797c478bd9Sstevel@tonic-gate static int	ugen_check_open_flags(ugen_state_t *, dev_t, int);
807c478bd9Sstevel@tonic-gate static int	ugen_strategy(struct buf *);
817c478bd9Sstevel@tonic-gate static void	ugen_minphys(struct buf *);
827c478bd9Sstevel@tonic-gate 
837c478bd9Sstevel@tonic-gate static void	ugen_pm_init(ugen_state_t *);
847c478bd9Sstevel@tonic-gate static void	ugen_pm_destroy(ugen_state_t *);
857c478bd9Sstevel@tonic-gate static void	ugen_pm_busy_component(ugen_state_t *);
867c478bd9Sstevel@tonic-gate static void	ugen_pm_idle_component(ugen_state_t *);
877c478bd9Sstevel@tonic-gate 
887c478bd9Sstevel@tonic-gate /* endpoint xfer and status management */
897c478bd9Sstevel@tonic-gate static int	ugen_epxs_init(ugen_state_t *);
907c478bd9Sstevel@tonic-gate static void	ugen_epxs_destroy(ugen_state_t *);
917c478bd9Sstevel@tonic-gate static int	ugen_epxs_data_init(ugen_state_t *, usb_ep_data_t *,
927c478bd9Sstevel@tonic-gate 					uchar_t, uchar_t, uchar_t, uchar_t);
937c478bd9Sstevel@tonic-gate static void	ugen_epxs_data_destroy(ugen_state_t *, ugen_ep_t *);
947c478bd9Sstevel@tonic-gate static int	ugen_epxs_minor_nodes_create(ugen_state_t *,
957c478bd9Sstevel@tonic-gate 					usb_ep_descr_t *, uchar_t,
967c478bd9Sstevel@tonic-gate 					uchar_t, uchar_t, uchar_t);
977c478bd9Sstevel@tonic-gate static int	ugen_epxs_check_open_nodes(ugen_state_t *);
987c478bd9Sstevel@tonic-gate 
997c478bd9Sstevel@tonic-gate static int	ugen_epx_open(ugen_state_t *, dev_t, int);
1007c478bd9Sstevel@tonic-gate static void	ugen_epx_close(ugen_state_t *, dev_t, int);
1017c478bd9Sstevel@tonic-gate static void	ugen_epx_shutdown(ugen_state_t *);
1027c478bd9Sstevel@tonic-gate 
1037c478bd9Sstevel@tonic-gate static int	ugen_epx_open_pipe(ugen_state_t *, ugen_ep_t *, int);
1047c478bd9Sstevel@tonic-gate static void	ugen_epx_close_pipe(ugen_state_t *, ugen_ep_t *);
1057c478bd9Sstevel@tonic-gate 
1067c478bd9Sstevel@tonic-gate static int	ugen_epx_req(ugen_state_t *, struct buf *);
1077c478bd9Sstevel@tonic-gate static int	ugen_epx_ctrl_req(ugen_state_t *, ugen_ep_t *,
1087c478bd9Sstevel@tonic-gate 					struct buf *, boolean_t *);
1097c478bd9Sstevel@tonic-gate static void	ugen_epx_ctrl_req_cb(usb_pipe_handle_t, usb_ctrl_req_t *);
1107c478bd9Sstevel@tonic-gate static int	ugen_epx_bulk_req(ugen_state_t *, ugen_ep_t *,
1117c478bd9Sstevel@tonic-gate 					struct buf *, boolean_t *);
1127c478bd9Sstevel@tonic-gate static void	ugen_epx_bulk_req_cb(usb_pipe_handle_t, usb_bulk_req_t *);
1137c478bd9Sstevel@tonic-gate static int	ugen_epx_intr_IN_req(ugen_state_t *, ugen_ep_t *,
1147c478bd9Sstevel@tonic-gate 					struct buf *, boolean_t *);
1157c478bd9Sstevel@tonic-gate static int	ugen_epx_intr_IN_start_polling(ugen_state_t *, ugen_ep_t *);
1167c478bd9Sstevel@tonic-gate static void	ugen_epx_intr_IN_stop_polling(ugen_state_t *, ugen_ep_t *);
1177c478bd9Sstevel@tonic-gate static void	ugen_epx_intr_IN_req_cb(usb_pipe_handle_t, usb_intr_req_t *);
1187c478bd9Sstevel@tonic-gate static int	ugen_epx_intr_OUT_req(ugen_state_t *, ugen_ep_t *,
1197c478bd9Sstevel@tonic-gate 					struct buf *, boolean_t *);
1207c478bd9Sstevel@tonic-gate static void	ugen_epx_intr_OUT_req_cb(usb_pipe_handle_t, usb_intr_req_t *);
1217c478bd9Sstevel@tonic-gate 
1227c478bd9Sstevel@tonic-gate static int	ugen_eps_open(ugen_state_t *, dev_t, int);
1237c478bd9Sstevel@tonic-gate static void	ugen_eps_close(ugen_state_t *, dev_t, int);
1247c478bd9Sstevel@tonic-gate static int	ugen_eps_req(ugen_state_t *, struct buf *);
1257c478bd9Sstevel@tonic-gate static void	ugen_update_ep_descr(ugen_state_t *, ugen_ep_t *);
1267c478bd9Sstevel@tonic-gate 
1277c478bd9Sstevel@tonic-gate /* device status management */
1287c478bd9Sstevel@tonic-gate static int	ugen_ds_init(ugen_state_t *);
1297c478bd9Sstevel@tonic-gate static void	ugen_ds_destroy(ugen_state_t *);
1307c478bd9Sstevel@tonic-gate static int	ugen_ds_open(ugen_state_t *, dev_t, int);
1317c478bd9Sstevel@tonic-gate static void	ugen_ds_close(ugen_state_t *, dev_t, int);
1327c478bd9Sstevel@tonic-gate static int	ugen_ds_req(ugen_state_t *, struct buf *);
1337c478bd9Sstevel@tonic-gate static void	ugen_ds_change(ugen_state_t *);
1347c478bd9Sstevel@tonic-gate static int	ugen_ds_minor_nodes_create(ugen_state_t *);
1357c478bd9Sstevel@tonic-gate static void	ugen_ds_poll_wakeup(ugen_state_t *);
1367c478bd9Sstevel@tonic-gate 
1377c478bd9Sstevel@tonic-gate /* utility functions */
1387c478bd9Sstevel@tonic-gate static int	ugen_minor_index_create(ugen_state_t *, ugen_minor_t);
1397c478bd9Sstevel@tonic-gate static ugen_minor_t ugen_devt2minor(ugen_state_t *, dev_t);
1407c478bd9Sstevel@tonic-gate static void	ugen_minor_node_table_create(ugen_state_t *);
1417c478bd9Sstevel@tonic-gate static void	ugen_minor_node_table_destroy(ugen_state_t *);
1427c478bd9Sstevel@tonic-gate static void	ugen_minor_node_table_shrink(ugen_state_t *);
1437c478bd9Sstevel@tonic-gate static int	ugen_cr2lcstat(int);
1447c478bd9Sstevel@tonic-gate static void	ugen_check_mask(uint_t, uint_t *, uint_t *);
145*96603521Sfrits static int	ugen_is_valid_minor_node(ugen_state_t *, dev_t);
1467c478bd9Sstevel@tonic-gate 
1477c478bd9Sstevel@tonic-gate static kmutex_t	ugen_devt_list_mutex;
1487c478bd9Sstevel@tonic-gate static ugen_devt_list_entry_t ugen_devt_list;
1497c478bd9Sstevel@tonic-gate static ugen_devt_cache_entry_t ugen_devt_cache[UGEN_DEVT_CACHE_SIZE];
1507c478bd9Sstevel@tonic-gate static uint_t	ugen_devt_cache_index;
1517c478bd9Sstevel@tonic-gate static void	ugen_store_devt(ugen_state_t *, minor_t);
1527c478bd9Sstevel@tonic-gate static ugen_state_t *ugen_devt2state(dev_t);
1537c478bd9Sstevel@tonic-gate static void	ugen_free_devt(ugen_state_t *);
1547c478bd9Sstevel@tonic-gate 
1557c478bd9Sstevel@tonic-gate /*
1567c478bd9Sstevel@tonic-gate  * usb_ugen entry points
1577c478bd9Sstevel@tonic-gate  *
1587c478bd9Sstevel@tonic-gate  * usb_ugen_get_hdl:
1597c478bd9Sstevel@tonic-gate  *	allocate and initialize handle
1607c478bd9Sstevel@tonic-gate  */
1617c478bd9Sstevel@tonic-gate usb_ugen_hdl_t
1627c478bd9Sstevel@tonic-gate usb_ugen_get_hdl(dev_info_t *dip, usb_ugen_info_t *usb_ugen_info)
1637c478bd9Sstevel@tonic-gate {
1647c478bd9Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*hdl = kmem_zalloc(sizeof (*hdl), KM_SLEEP);
1657c478bd9Sstevel@tonic-gate 	ugen_state_t		*ugenp = kmem_zalloc(sizeof (ugen_state_t),
1667c478bd9Sstevel@tonic-gate 								KM_SLEEP);
1677c478bd9Sstevel@tonic-gate 	uint_t			len, shift, limit;
1687c478bd9Sstevel@tonic-gate 	int			rval;
1697c478bd9Sstevel@tonic-gate 
1707c478bd9Sstevel@tonic-gate 	hdl->hdl_ugenp = ugenp;
1717c478bd9Sstevel@tonic-gate 
1727c478bd9Sstevel@tonic-gate 	/* masks may not overlap */
1737c478bd9Sstevel@tonic-gate 	if (usb_ugen_info->usb_ugen_minor_node_ugen_bits_mask &
1747c478bd9Sstevel@tonic-gate 	    usb_ugen_info->usb_ugen_minor_node_instance_mask) {
1757c478bd9Sstevel@tonic-gate 		usb_ugen_release_hdl((usb_ugen_hdl_t)hdl);
1767c478bd9Sstevel@tonic-gate 
1777c478bd9Sstevel@tonic-gate 		return (NULL);
1787c478bd9Sstevel@tonic-gate 	}
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate 	if ((rval = usb_get_dev_data(dip, &ugenp->ug_dev_data,
1817c478bd9Sstevel@tonic-gate 	    usb_owns_device(dip) ? USB_PARSE_LVL_ALL : USB_PARSE_LVL_IF,
1827c478bd9Sstevel@tonic-gate 	    0)) != USB_SUCCESS) {
1837c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
1847c478bd9Sstevel@tonic-gate 		    "usb_ugen_attach: usb_get_dev_data failed, rval=%d", rval);
1857c478bd9Sstevel@tonic-gate 
1867c478bd9Sstevel@tonic-gate 		return (NULL);
1877c478bd9Sstevel@tonic-gate 	}
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate 	/* Initialize state structure for this instance */
1907c478bd9Sstevel@tonic-gate 	mutex_init(&ugenp->ug_mutex, NULL, MUTEX_DRIVER,
1917c478bd9Sstevel@tonic-gate 				ugenp->ug_dev_data->dev_iblock_cookie);
1927c478bd9Sstevel@tonic-gate 
1937c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
1947c478bd9Sstevel@tonic-gate 	ugenp->ug_dip		= dip;
1957c478bd9Sstevel@tonic-gate 	ugenp->ug_instance	= ddi_get_instance(dip);
1967c478bd9Sstevel@tonic-gate 	ugenp->ug_hdl		= hdl;
1977c478bd9Sstevel@tonic-gate 
1987c478bd9Sstevel@tonic-gate 	/* Allocate a log handle for debug/error messages */
1997c478bd9Sstevel@tonic-gate 	if (strcmp(ddi_driver_name(dip), "ugen") != 0) {
2007c478bd9Sstevel@tonic-gate 		char	*name;
2017c478bd9Sstevel@tonic-gate 
2027c478bd9Sstevel@tonic-gate 		len = strlen(ddi_driver_name(dip)) + sizeof ("_ugen") + 1;
2037c478bd9Sstevel@tonic-gate 		name = kmem_alloc(len, KM_SLEEP);
2047c478bd9Sstevel@tonic-gate 		(void) snprintf(name, len, "%s_ugen", ddi_driver_name(dip));
2057c478bd9Sstevel@tonic-gate 
2067c478bd9Sstevel@tonic-gate 		ugenp->ug_log_hdl = usb_alloc_log_hdl(dip, name, &ugen_errlevel,
2077c478bd9Sstevel@tonic-gate 					&ugen_errmask, &ugen_instance_debug, 0);
2087c478bd9Sstevel@tonic-gate 		hdl->hdl_log_name = name;
2097c478bd9Sstevel@tonic-gate 		hdl->hdl_log_name_length = len;
2107c478bd9Sstevel@tonic-gate 	} else {
2117c478bd9Sstevel@tonic-gate 		ugenp->ug_log_hdl = usb_alloc_log_hdl(dip, "ugen",
2127c478bd9Sstevel@tonic-gate 					&ugen_errlevel,
2137c478bd9Sstevel@tonic-gate 					&ugen_errmask, &ugen_instance_debug, 0);
2147c478bd9Sstevel@tonic-gate 	}
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate 	hdl->hdl_dip = dip;
2177c478bd9Sstevel@tonic-gate 	hdl->hdl_flags = usb_ugen_info->usb_ugen_flags;
2187c478bd9Sstevel@tonic-gate 
2197c478bd9Sstevel@tonic-gate 	ugen_check_mask(usb_ugen_info->usb_ugen_minor_node_ugen_bits_mask,
2207c478bd9Sstevel@tonic-gate 							&shift, &limit);
2217c478bd9Sstevel@tonic-gate 	if (limit == 0) {
2227c478bd9Sstevel@tonic-gate 		usb_ugen_release_hdl((usb_ugen_hdl_t)hdl);
2237c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
2247c478bd9Sstevel@tonic-gate 
2257c478bd9Sstevel@tonic-gate 		return (NULL);
2267c478bd9Sstevel@tonic-gate 	}
2277c478bd9Sstevel@tonic-gate 	hdl->hdl_minor_node_ugen_bits_mask = usb_ugen_info->
2287c478bd9Sstevel@tonic-gate 					usb_ugen_minor_node_ugen_bits_mask;
2297c478bd9Sstevel@tonic-gate 	hdl->hdl_minor_node_ugen_bits_shift = shift;
2307c478bd9Sstevel@tonic-gate 	hdl->hdl_minor_node_ugen_bits_limit = limit;
2317c478bd9Sstevel@tonic-gate 
2327c478bd9Sstevel@tonic-gate 	ugen_check_mask(usb_ugen_info->usb_ugen_minor_node_instance_mask,
2337c478bd9Sstevel@tonic-gate 							&shift, &limit);
2347c478bd9Sstevel@tonic-gate 	if (limit == 0) {
2357c478bd9Sstevel@tonic-gate 		usb_ugen_release_hdl((usb_ugen_hdl_t)hdl);
2367c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
2377c478bd9Sstevel@tonic-gate 
2387c478bd9Sstevel@tonic-gate 		return (NULL);
2397c478bd9Sstevel@tonic-gate 	}
2407c478bd9Sstevel@tonic-gate 
2417c478bd9Sstevel@tonic-gate 	hdl->hdl_minor_node_instance_mask = usb_ugen_info->
2427c478bd9Sstevel@tonic-gate 					usb_ugen_minor_node_instance_mask;
2437c478bd9Sstevel@tonic-gate 	hdl->hdl_minor_node_instance_shift = shift;
2447c478bd9Sstevel@tonic-gate 	hdl->hdl_minor_node_instance_limit = limit;
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
2477c478bd9Sstevel@tonic-gate 	    "usb_ugen_get_hdl: instance shift=%d instance limit=%d",
2487c478bd9Sstevel@tonic-gate 	    hdl->hdl_minor_node_instance_shift,
2497c478bd9Sstevel@tonic-gate 	    hdl->hdl_minor_node_instance_limit);
2507c478bd9Sstevel@tonic-gate 
2517c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
2527c478bd9Sstevel@tonic-gate 	    "usb_ugen_get_hdl: bits shift=%d bits limit=%d",
2537c478bd9Sstevel@tonic-gate 	    hdl->hdl_minor_node_ugen_bits_shift,
2547c478bd9Sstevel@tonic-gate 	    hdl->hdl_minor_node_ugen_bits_limit);
2557c478bd9Sstevel@tonic-gate 
2567c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate 	return ((usb_ugen_hdl_t)hdl);
2597c478bd9Sstevel@tonic-gate }
2607c478bd9Sstevel@tonic-gate 
2617c478bd9Sstevel@tonic-gate 
2627c478bd9Sstevel@tonic-gate /*
2637c478bd9Sstevel@tonic-gate  * usb_ugen_release_hdl:
2647c478bd9Sstevel@tonic-gate  *	deallocate a handle
2657c478bd9Sstevel@tonic-gate  */
2667c478bd9Sstevel@tonic-gate void
2677c478bd9Sstevel@tonic-gate usb_ugen_release_hdl(usb_ugen_hdl_t usb_ugen_hdl)
2687c478bd9Sstevel@tonic-gate {
2697c478bd9Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
2707c478bd9Sstevel@tonic-gate 				(usb_ugen_hdl_impl_t *)usb_ugen_hdl;
2717c478bd9Sstevel@tonic-gate 
2727c478bd9Sstevel@tonic-gate 	if (usb_ugen_hdl_impl) {
2737c478bd9Sstevel@tonic-gate 		ugen_state_t *ugenp = usb_ugen_hdl_impl->hdl_ugenp;
2747c478bd9Sstevel@tonic-gate 
2757c478bd9Sstevel@tonic-gate 		if (ugenp) {
2767c478bd9Sstevel@tonic-gate 			mutex_destroy(&ugenp->ug_mutex);
2777c478bd9Sstevel@tonic-gate 			usb_free_log_hdl(ugenp->ug_log_hdl);
2787c478bd9Sstevel@tonic-gate 			usb_free_dev_data(usb_ugen_hdl_impl->hdl_dip,
2797c478bd9Sstevel@tonic-gate 				ugenp->ug_dev_data);
2807c478bd9Sstevel@tonic-gate 			kmem_free(ugenp, sizeof (*ugenp));
2817c478bd9Sstevel@tonic-gate 		}
2827c478bd9Sstevel@tonic-gate 		if (usb_ugen_hdl_impl->hdl_log_name) {
2837c478bd9Sstevel@tonic-gate 			kmem_free(usb_ugen_hdl_impl->hdl_log_name,
2847c478bd9Sstevel@tonic-gate 				usb_ugen_hdl_impl->hdl_log_name_length);
2857c478bd9Sstevel@tonic-gate 		}
2867c478bd9Sstevel@tonic-gate 		kmem_free(usb_ugen_hdl_impl, sizeof (*usb_ugen_hdl_impl));
2877c478bd9Sstevel@tonic-gate 	}
2887c478bd9Sstevel@tonic-gate }
2897c478bd9Sstevel@tonic-gate 
2907c478bd9Sstevel@tonic-gate 
2917c478bd9Sstevel@tonic-gate /*
2927c478bd9Sstevel@tonic-gate  * usb_ugen_attach()
2937c478bd9Sstevel@tonic-gate  */
2947c478bd9Sstevel@tonic-gate int
2957c478bd9Sstevel@tonic-gate usb_ugen_attach(usb_ugen_hdl_t usb_ugen_hdl, ddi_attach_cmd_t cmd)
2967c478bd9Sstevel@tonic-gate {
2977c478bd9Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
2987c478bd9Sstevel@tonic-gate 				(usb_ugen_hdl_impl_t *)usb_ugen_hdl;
2997c478bd9Sstevel@tonic-gate 	ugen_state_t		*ugenp;
3007c478bd9Sstevel@tonic-gate 	dev_info_t		*dip;
3017c478bd9Sstevel@tonic-gate 
3027c478bd9Sstevel@tonic-gate 	if (usb_ugen_hdl == NULL) {
3037c478bd9Sstevel@tonic-gate 
3047c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
3057c478bd9Sstevel@tonic-gate 	}
3067c478bd9Sstevel@tonic-gate 
3077c478bd9Sstevel@tonic-gate 	ugenp = usb_ugen_hdl_impl->hdl_ugenp;
3087c478bd9Sstevel@tonic-gate 	dip = usb_ugen_hdl_impl->hdl_dip;
3097c478bd9Sstevel@tonic-gate 
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
3127c478bd9Sstevel@tonic-gate 	    "usb_ugen_attach: cmd=%d", cmd);
3137c478bd9Sstevel@tonic-gate 
3147c478bd9Sstevel@tonic-gate 	switch (cmd) {
3157c478bd9Sstevel@tonic-gate 	case DDI_ATTACH:
3167c478bd9Sstevel@tonic-gate 
3177c478bd9Sstevel@tonic-gate 		break;
3187c478bd9Sstevel@tonic-gate 	case DDI_RESUME:
3197c478bd9Sstevel@tonic-gate 		ugen_cpr_resume(ugenp);
3207c478bd9Sstevel@tonic-gate 
3217c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
3227c478bd9Sstevel@tonic-gate 	default:
3237c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, NULL,
3247c478bd9Sstevel@tonic-gate 		    "usb_ugen_attach: unknown command");
3257c478bd9Sstevel@tonic-gate 
3267c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
3277c478bd9Sstevel@tonic-gate 	}
3287c478bd9Sstevel@tonic-gate 
3297c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
3307c478bd9Sstevel@tonic-gate 	ugenp->ug_ser_cookie =
3317c478bd9Sstevel@tonic-gate 	    usb_init_serialization(dip, USB_INIT_SER_CHECK_SAME_THREAD);
3327c478bd9Sstevel@tonic-gate 	ugenp->ug_cleanup_flags |= UGEN_INIT_LOCKS;
3337c478bd9Sstevel@tonic-gate 
3347c478bd9Sstevel@tonic-gate 	/* Get maximum bulk transfer size supported by the HCD */
3357c478bd9Sstevel@tonic-gate 	if (usb_pipe_get_max_bulk_transfer_size(dip,
3367c478bd9Sstevel@tonic-gate 	    &ugenp->ug_max_bulk_xfer_sz) != USB_SUCCESS) {
3377c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
3387c478bd9Sstevel@tonic-gate 		    "usb_ugen_attach: Getting max bulk xfer sz failed");
3397c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
3407c478bd9Sstevel@tonic-gate 
3417c478bd9Sstevel@tonic-gate 		goto fail;
3427c478bd9Sstevel@tonic-gate 	}
3437c478bd9Sstevel@tonic-gate 
3447c478bd9Sstevel@tonic-gate 	/* table for mapping 48 bit minor codes to 9 bit index (for ugen) */
3457c478bd9Sstevel@tonic-gate 	ugen_minor_node_table_create(ugenp);
3467c478bd9Sstevel@tonic-gate 
3477c478bd9Sstevel@tonic-gate 	/* prepare device status node handling */
3487c478bd9Sstevel@tonic-gate 	if (ugen_ds_init(ugenp) != USB_SUCCESS) {
3497c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
3507c478bd9Sstevel@tonic-gate 		    "usb_ugen_attach: preparing dev status failed");
3517c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
3527c478bd9Sstevel@tonic-gate 
3537c478bd9Sstevel@tonic-gate 		goto fail;
3547c478bd9Sstevel@tonic-gate 	}
3557c478bd9Sstevel@tonic-gate 
3567c478bd9Sstevel@tonic-gate 	/* prepare all available xfer and status endpoints nodes */
3577c478bd9Sstevel@tonic-gate 	if (ugen_epxs_init(ugenp) != USB_SUCCESS) {
3587c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
3597c478bd9Sstevel@tonic-gate 		    "usb_ugen_attach: preparing endpoints failed");
3607c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
3617c478bd9Sstevel@tonic-gate 
3627c478bd9Sstevel@tonic-gate 		goto fail;
3637c478bd9Sstevel@tonic-gate 	}
3647c478bd9Sstevel@tonic-gate 
3657c478bd9Sstevel@tonic-gate 	/* reduce table size if not all entries are used */
3667c478bd9Sstevel@tonic-gate 	ugen_minor_node_table_shrink(ugenp);
3677c478bd9Sstevel@tonic-gate 
3687c478bd9Sstevel@tonic-gate 	/* we are ready to go */
3697c478bd9Sstevel@tonic-gate 	ugenp->ug_dev_state = USB_DEV_ONLINE;
3707c478bd9Sstevel@tonic-gate 
3717c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
3727c478bd9Sstevel@tonic-gate 
3737c478bd9Sstevel@tonic-gate 	/* prepare PM */
3747c478bd9Sstevel@tonic-gate 	if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
3757c478bd9Sstevel@tonic-gate 		ugen_pm_init(ugenp);
3767c478bd9Sstevel@tonic-gate 	}
3777c478bd9Sstevel@tonic-gate 
3787c478bd9Sstevel@tonic-gate 	/*
3797c478bd9Sstevel@tonic-gate 	 * if ugen driver, kill all child nodes otherwise set cfg fails
3807c478bd9Sstevel@tonic-gate 	 * if requested
3817c478bd9Sstevel@tonic-gate 	 */
3827c478bd9Sstevel@tonic-gate 	if (usb_owns_device(dip) &&
3837c478bd9Sstevel@tonic-gate 	    (usb_ugen_hdl_impl->hdl_flags & USB_UGEN_REMOVE_CHILDREN)) {
3847c478bd9Sstevel@tonic-gate 		dev_info_t *cdip;
3857c478bd9Sstevel@tonic-gate 
3867c478bd9Sstevel@tonic-gate 		/* save cfgidx so we can restore on detach */
3877c478bd9Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
3887c478bd9Sstevel@tonic-gate 		ugenp->ug_initial_cfgidx = usb_get_current_cfgidx(dip);
3897c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
3907c478bd9Sstevel@tonic-gate 
3917c478bd9Sstevel@tonic-gate 		for (cdip = ddi_get_child(dip); cdip; ) {
3927c478bd9Sstevel@tonic-gate 			dev_info_t *next = ddi_get_next_sibling(cdip);
3937c478bd9Sstevel@tonic-gate 			(void) ddi_remove_child(cdip, 0);
3947c478bd9Sstevel@tonic-gate 			cdip = next;
3957c478bd9Sstevel@tonic-gate 		}
3967c478bd9Sstevel@tonic-gate 	}
3977c478bd9Sstevel@tonic-gate 
3987c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
3997c478bd9Sstevel@tonic-gate fail:
4007c478bd9Sstevel@tonic-gate 	if (ugenp) {
4017c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
4027c478bd9Sstevel@tonic-gate 		    "attach fail");
4037c478bd9Sstevel@tonic-gate 		(void) ugen_cleanup(ugenp);
4047c478bd9Sstevel@tonic-gate 	}
4057c478bd9Sstevel@tonic-gate 
4067c478bd9Sstevel@tonic-gate 	return (DDI_FAILURE);
4077c478bd9Sstevel@tonic-gate }
4087c478bd9Sstevel@tonic-gate 
4097c478bd9Sstevel@tonic-gate 
4107c478bd9Sstevel@tonic-gate /*
4117c478bd9Sstevel@tonic-gate  * usb_ugen_detach()
4127c478bd9Sstevel@tonic-gate  */
4137c478bd9Sstevel@tonic-gate int
4147c478bd9Sstevel@tonic-gate usb_ugen_detach(usb_ugen_hdl_t usb_ugen_hdl, ddi_detach_cmd_t cmd)
4157c478bd9Sstevel@tonic-gate {
4167c478bd9Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
4177c478bd9Sstevel@tonic-gate 				(usb_ugen_hdl_impl_t *)usb_ugen_hdl;
4187c478bd9Sstevel@tonic-gate 	int			rval = USB_FAILURE;
4197c478bd9Sstevel@tonic-gate 
4207c478bd9Sstevel@tonic-gate 	if (usb_ugen_hdl) {
4217c478bd9Sstevel@tonic-gate 		ugen_state_t *ugenp = usb_ugen_hdl_impl->hdl_ugenp;
4227c478bd9Sstevel@tonic-gate 
4237c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
4247c478bd9Sstevel@tonic-gate 		    "usb_ugen_detach cmd %d", cmd);
4257c478bd9Sstevel@tonic-gate 
4267c478bd9Sstevel@tonic-gate 		switch (cmd) {
4277c478bd9Sstevel@tonic-gate 		case DDI_DETACH:
4287c478bd9Sstevel@tonic-gate 			rval = ugen_cleanup(ugenp);
4297c478bd9Sstevel@tonic-gate 
4307c478bd9Sstevel@tonic-gate 			break;
4317c478bd9Sstevel@tonic-gate 		case DDI_SUSPEND:
4327c478bd9Sstevel@tonic-gate 			rval = ugen_cpr_suspend(ugenp);
4337c478bd9Sstevel@tonic-gate 
4347c478bd9Sstevel@tonic-gate 			break;
4357c478bd9Sstevel@tonic-gate 		default:
4367c478bd9Sstevel@tonic-gate 
4377c478bd9Sstevel@tonic-gate 			break;
4387c478bd9Sstevel@tonic-gate 		}
4397c478bd9Sstevel@tonic-gate 	}
4407c478bd9Sstevel@tonic-gate 
4417c478bd9Sstevel@tonic-gate 	return (rval);
4427c478bd9Sstevel@tonic-gate }
4437c478bd9Sstevel@tonic-gate 
4447c478bd9Sstevel@tonic-gate 
4457c478bd9Sstevel@tonic-gate /*
4467c478bd9Sstevel@tonic-gate  * ugen_cleanup()
4477c478bd9Sstevel@tonic-gate  */
4487c478bd9Sstevel@tonic-gate static int
4497c478bd9Sstevel@tonic-gate ugen_cleanup(ugen_state_t *ugenp)
4507c478bd9Sstevel@tonic-gate {
4517c478bd9Sstevel@tonic-gate 	dev_info_t *dip = ugenp->ug_dip;
4527c478bd9Sstevel@tonic-gate 
4537c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl, "ugen_cleanup");
4547c478bd9Sstevel@tonic-gate 
4557c478bd9Sstevel@tonic-gate 	if (ugenp->ug_cleanup_flags & UGEN_INIT_LOCKS) {
4567c478bd9Sstevel@tonic-gate 
4577c478bd9Sstevel@tonic-gate 		/* shutdown all endpoints */
4587c478bd9Sstevel@tonic-gate 		ugen_epx_shutdown(ugenp);
4597c478bd9Sstevel@tonic-gate 
4607c478bd9Sstevel@tonic-gate 		/*
4617c478bd9Sstevel@tonic-gate 		 * At this point, no new activity can be initiated.
4627c478bd9Sstevel@tonic-gate 		 * The driver has disabled hotplug callbacks.
4637c478bd9Sstevel@tonic-gate 		 * The Solaris framework has disabled
4647c478bd9Sstevel@tonic-gate 		 * new opens on a device being detached, and does not
4657c478bd9Sstevel@tonic-gate 		 * allow detaching an open device. PM should power
4667c478bd9Sstevel@tonic-gate 		 * down while we are detaching
4677c478bd9Sstevel@tonic-gate 		 *
4687c478bd9Sstevel@tonic-gate 		 * The following ensures that any other driver
4697c478bd9Sstevel@tonic-gate 		 * activity must have drained (paranoia)
4707c478bd9Sstevel@tonic-gate 		 */
4717c478bd9Sstevel@tonic-gate 		(void) usb_serialize_access(ugenp->ug_ser_cookie,
4727c478bd9Sstevel@tonic-gate 							USB_WAIT, 0);
4737c478bd9Sstevel@tonic-gate 		usb_release_access(ugenp->ug_ser_cookie);
4747c478bd9Sstevel@tonic-gate 
4757c478bd9Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
4767c478bd9Sstevel@tonic-gate 		ASSERT(ugenp->ug_open_count == 0);
4777c478bd9Sstevel@tonic-gate 		ASSERT(ugenp->ug_pending_cmds == 0);
4787c478bd9Sstevel@tonic-gate 
4797c478bd9Sstevel@tonic-gate 		/* dismantle in reverse order */
4807c478bd9Sstevel@tonic-gate 		ugen_pm_destroy(ugenp);
4817c478bd9Sstevel@tonic-gate 		ugen_epxs_destroy(ugenp);
4827c478bd9Sstevel@tonic-gate 		ugen_ds_destroy(ugenp);
4837c478bd9Sstevel@tonic-gate 		ugen_minor_node_table_destroy(ugenp);
4847c478bd9Sstevel@tonic-gate 
4857c478bd9Sstevel@tonic-gate 
4867c478bd9Sstevel@tonic-gate 		/* restore to initial configuration */
4877c478bd9Sstevel@tonic-gate 		if (usb_owns_device(dip) &&
4887c478bd9Sstevel@tonic-gate 		    (ugenp->ug_dev_state != USB_DEV_DISCONNECTED)) {
4897c478bd9Sstevel@tonic-gate 			int idx = ugenp->ug_initial_cfgidx;
4907c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
4917c478bd9Sstevel@tonic-gate 			(void) usb_set_cfg(dip, idx,
4927c478bd9Sstevel@tonic-gate 			    USB_FLAGS_SLEEP, NULL, NULL);
4937c478bd9Sstevel@tonic-gate 		} else {
4947c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
4957c478bd9Sstevel@tonic-gate 		}
4967c478bd9Sstevel@tonic-gate 
4977c478bd9Sstevel@tonic-gate 		usb_fini_serialization(ugenp->ug_ser_cookie);
4987c478bd9Sstevel@tonic-gate 	}
4997c478bd9Sstevel@tonic-gate 
5007c478bd9Sstevel@tonic-gate 	ddi_prop_remove_all(dip);
5017c478bd9Sstevel@tonic-gate 	ddi_remove_minor_node(dip, NULL);
5027c478bd9Sstevel@tonic-gate 
5037c478bd9Sstevel@tonic-gate 	ugen_free_devt(ugenp);
5047c478bd9Sstevel@tonic-gate 
5057c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
5067c478bd9Sstevel@tonic-gate }
5077c478bd9Sstevel@tonic-gate 
5087c478bd9Sstevel@tonic-gate 
5097c478bd9Sstevel@tonic-gate /*
5107c478bd9Sstevel@tonic-gate  * ugen_cpr_suspend
5117c478bd9Sstevel@tonic-gate  */
5127c478bd9Sstevel@tonic-gate static int
5137c478bd9Sstevel@tonic-gate ugen_cpr_suspend(ugen_state_t *ugenp)
5147c478bd9Sstevel@tonic-gate {
5157c478bd9Sstevel@tonic-gate 	int		rval = USB_FAILURE;
5167c478bd9Sstevel@tonic-gate 	int		i;
5177c478bd9Sstevel@tonic-gate 	int		prev_state;
5187c478bd9Sstevel@tonic-gate 
5197c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CPR, ugenp->ug_log_hdl,
5207c478bd9Sstevel@tonic-gate 	    "ugen_cpr_suspend:");
5217c478bd9Sstevel@tonic-gate 
5227c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
5237c478bd9Sstevel@tonic-gate 	switch (ugenp->ug_dev_state) {
5247c478bd9Sstevel@tonic-gate 	case USB_DEV_ONLINE:
5257c478bd9Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
5267c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_CPR, ugenp->ug_log_hdl,
5277c478bd9Sstevel@tonic-gate 		    "ugen_cpr_suspend:");
5287c478bd9Sstevel@tonic-gate 
5297c478bd9Sstevel@tonic-gate 		prev_state = ugenp->ug_dev_state;
5307c478bd9Sstevel@tonic-gate 		ugenp->ug_dev_state = USB_DEV_SUSPENDED;
5317c478bd9Sstevel@tonic-gate 
5327c478bd9Sstevel@tonic-gate 		if (ugenp->ug_open_count) {
5337c478bd9Sstevel@tonic-gate 			/* drain outstanding cmds */
5347c478bd9Sstevel@tonic-gate 			for (i = 0; i < ugen_busy_loop; i++) {
5357c478bd9Sstevel@tonic-gate 				if (ugenp->ug_pending_cmds == 0) {
5367c478bd9Sstevel@tonic-gate 
5377c478bd9Sstevel@tonic-gate 					break;
5387c478bd9Sstevel@tonic-gate 				}
5397c478bd9Sstevel@tonic-gate 				mutex_exit(&ugenp->ug_mutex);
5407c478bd9Sstevel@tonic-gate 				delay(drv_usectohz(100000));
5417c478bd9Sstevel@tonic-gate 				mutex_enter(&ugenp->ug_mutex);
5427c478bd9Sstevel@tonic-gate 			}
5437c478bd9Sstevel@tonic-gate 
5447c478bd9Sstevel@tonic-gate 			/* if still outstanding cmds, fail suspend */
5457c478bd9Sstevel@tonic-gate 			if (ugenp->ug_pending_cmds) {
5467c478bd9Sstevel@tonic-gate 				ugenp->ug_dev_state = prev_state;
5477c478bd9Sstevel@tonic-gate 
5487c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(UGEN_PRINT_CPR,
5497c478bd9Sstevel@tonic-gate 				    ugenp->ug_log_hdl,
5507c478bd9Sstevel@tonic-gate 				    "ugen_cpr_suspend: pending %d",
5517c478bd9Sstevel@tonic-gate 				    ugenp->ug_pending_cmds);
5527c478bd9Sstevel@tonic-gate 
5537c478bd9Sstevel@tonic-gate 				rval =	USB_FAILURE;
5547c478bd9Sstevel@tonic-gate 				break;
5557c478bd9Sstevel@tonic-gate 			}
5567c478bd9Sstevel@tonic-gate 
5577c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
5587c478bd9Sstevel@tonic-gate 			(void) usb_serialize_access(ugenp->ug_ser_cookie,
5597c478bd9Sstevel@tonic-gate 								USB_WAIT, 0);
5607c478bd9Sstevel@tonic-gate 			/* close all pipes */
5617c478bd9Sstevel@tonic-gate 			ugen_epx_shutdown(ugenp);
5627c478bd9Sstevel@tonic-gate 
5637c478bd9Sstevel@tonic-gate 			usb_release_access(ugenp->ug_ser_cookie);
5647c478bd9Sstevel@tonic-gate 
5657c478bd9Sstevel@tonic-gate 			mutex_enter(&ugenp->ug_mutex);
5667c478bd9Sstevel@tonic-gate 		}
5677c478bd9Sstevel@tonic-gate 
5687c478bd9Sstevel@tonic-gate 		/* wakeup devstat reads and polls */
5697c478bd9Sstevel@tonic-gate 		ugen_ds_change(ugenp);
5707c478bd9Sstevel@tonic-gate 		ugen_ds_poll_wakeup(ugenp);
5717c478bd9Sstevel@tonic-gate 
5727c478bd9Sstevel@tonic-gate 		rval = USB_SUCCESS;
5737c478bd9Sstevel@tonic-gate 		break;
5747c478bd9Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
5757c478bd9Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RESUME:
5767c478bd9Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
5777c478bd9Sstevel@tonic-gate 	default:
5787c478bd9Sstevel@tonic-gate 
5797c478bd9Sstevel@tonic-gate 		break;
5807c478bd9Sstevel@tonic-gate 	}
5817c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
5827c478bd9Sstevel@tonic-gate 
5837c478bd9Sstevel@tonic-gate 	return (rval);
5847c478bd9Sstevel@tonic-gate }
5857c478bd9Sstevel@tonic-gate 
5867c478bd9Sstevel@tonic-gate /*
5877c478bd9Sstevel@tonic-gate  * ugen_cpr_resume
5887c478bd9Sstevel@tonic-gate  */
5897c478bd9Sstevel@tonic-gate static void
5907c478bd9Sstevel@tonic-gate ugen_cpr_resume(ugen_state_t *ugenp)
5917c478bd9Sstevel@tonic-gate {
5927c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CPR, ugenp->ug_log_hdl,
5937c478bd9Sstevel@tonic-gate 	    "ugen_cpr_resume:");
5947c478bd9Sstevel@tonic-gate 
5957c478bd9Sstevel@tonic-gate 	ugen_restore_state(ugenp);
5967c478bd9Sstevel@tonic-gate }
5977c478bd9Sstevel@tonic-gate 
5987c478bd9Sstevel@tonic-gate /*
5997c478bd9Sstevel@tonic-gate  * usb_ugen_disconnect_ev_cb:
6007c478bd9Sstevel@tonic-gate  */
6017c478bd9Sstevel@tonic-gate int
6027c478bd9Sstevel@tonic-gate usb_ugen_disconnect_ev_cb(usb_ugen_hdl_t usb_ugen_hdl)
6037c478bd9Sstevel@tonic-gate {
6047c478bd9Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
6057c478bd9Sstevel@tonic-gate 				(usb_ugen_hdl_impl_t *)usb_ugen_hdl;
6067c478bd9Sstevel@tonic-gate 	ugen_state_t		*ugenp;
6077c478bd9Sstevel@tonic-gate 
6087c478bd9Sstevel@tonic-gate 	if (usb_ugen_hdl_impl == NULL) {
6097c478bd9Sstevel@tonic-gate 
6107c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
6117c478bd9Sstevel@tonic-gate 	}
6127c478bd9Sstevel@tonic-gate 
6137c478bd9Sstevel@tonic-gate 	ugenp = usb_ugen_hdl_impl->hdl_ugenp;
6147c478bd9Sstevel@tonic-gate 
6157c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_HOTPLUG, ugenp->ug_log_hdl,
6167c478bd9Sstevel@tonic-gate 	    "usb_ugen_disconnect_ev_cb:");
6177c478bd9Sstevel@tonic-gate 
6187c478bd9Sstevel@tonic-gate 	/* get exclusive access */
6197c478bd9Sstevel@tonic-gate 	(void) usb_serialize_access(ugenp->ug_ser_cookie, USB_WAIT, 0);
6207c478bd9Sstevel@tonic-gate 
6217c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
6227c478bd9Sstevel@tonic-gate 	ugenp->ug_dev_state = USB_DEV_DISCONNECTED;
6237c478bd9Sstevel@tonic-gate 	if (ugenp->ug_open_count) {
6247c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
6257c478bd9Sstevel@tonic-gate 
6267c478bd9Sstevel@tonic-gate 		/* close all pipes */
6277c478bd9Sstevel@tonic-gate 		(void) ugen_epx_shutdown(ugenp);
6287c478bd9Sstevel@tonic-gate 
6297c478bd9Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
6307c478bd9Sstevel@tonic-gate 	}
6317c478bd9Sstevel@tonic-gate 
6327c478bd9Sstevel@tonic-gate 
6337c478bd9Sstevel@tonic-gate 	/* wakeup devstat reads and polls */
6347c478bd9Sstevel@tonic-gate 	ugen_ds_change(ugenp);
6357c478bd9Sstevel@tonic-gate 	ugen_ds_poll_wakeup(ugenp);
6367c478bd9Sstevel@tonic-gate 
6377c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
6387c478bd9Sstevel@tonic-gate 	usb_release_access(ugenp->ug_ser_cookie);
6397c478bd9Sstevel@tonic-gate 
6407c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
6417c478bd9Sstevel@tonic-gate }
6427c478bd9Sstevel@tonic-gate 
6437c478bd9Sstevel@tonic-gate 
6447c478bd9Sstevel@tonic-gate /*
6457c478bd9Sstevel@tonic-gate  * usb_ugen_reconnect_ev_cb:
6467c478bd9Sstevel@tonic-gate  */
6477c478bd9Sstevel@tonic-gate int
6487c478bd9Sstevel@tonic-gate usb_ugen_reconnect_ev_cb(usb_ugen_hdl_t usb_ugen_hdl)
6497c478bd9Sstevel@tonic-gate {
6507c478bd9Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
6517c478bd9Sstevel@tonic-gate 				(usb_ugen_hdl_impl_t *)usb_ugen_hdl;
6527c478bd9Sstevel@tonic-gate 	ugen_state_t		*ugenp = usb_ugen_hdl_impl->hdl_ugenp;
6537c478bd9Sstevel@tonic-gate 
6547c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_HOTPLUG, ugenp->ug_log_hdl,
6557c478bd9Sstevel@tonic-gate 	    "usb_ugen_reconnect_ev_cb:");
6567c478bd9Sstevel@tonic-gate 
6577c478bd9Sstevel@tonic-gate 	ugen_restore_state(ugenp);
6587c478bd9Sstevel@tonic-gate 
6597c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
6607c478bd9Sstevel@tonic-gate }
6617c478bd9Sstevel@tonic-gate 
6627c478bd9Sstevel@tonic-gate 
6637c478bd9Sstevel@tonic-gate /*
6647c478bd9Sstevel@tonic-gate  * ugen_restore_state:
6657c478bd9Sstevel@tonic-gate  *	Check for same device; if a different device is attached, set
6667c478bd9Sstevel@tonic-gate  *	the device status to disconnected.
6677c478bd9Sstevel@tonic-gate  *	If we were open, then set to UNAVAILABLE until all endpoints have
6687c478bd9Sstevel@tonic-gate  *	be closed.
6697c478bd9Sstevel@tonic-gate  */
6707c478bd9Sstevel@tonic-gate static void
6717c478bd9Sstevel@tonic-gate ugen_restore_state(ugen_state_t *ugenp)
6727c478bd9Sstevel@tonic-gate {
6737c478bd9Sstevel@tonic-gate 	dev_info_t *dip = ugenp->ug_dip;
6747c478bd9Sstevel@tonic-gate 
6757c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_HOTPLUG, ugenp->ug_log_hdl,
6767c478bd9Sstevel@tonic-gate 	    "ugen_restore_state");
6777c478bd9Sstevel@tonic-gate 
6787c478bd9Sstevel@tonic-gate 	/* first raise power */
6797c478bd9Sstevel@tonic-gate 	if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
6807c478bd9Sstevel@tonic-gate 		ugen_pm_busy_component(ugenp);
6817c478bd9Sstevel@tonic-gate 		(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
6827c478bd9Sstevel@tonic-gate 	}
6837c478bd9Sstevel@tonic-gate 
6847c478bd9Sstevel@tonic-gate 	/* Check if we are talking to the same device */
6857c478bd9Sstevel@tonic-gate 	if (usb_check_same_device(dip, ugenp->ug_log_hdl,
6867c478bd9Sstevel@tonic-gate 	    USB_LOG_L0, UGEN_PRINT_HOTPLUG, USB_CHK_ALL, NULL) ==
6877c478bd9Sstevel@tonic-gate 	    USB_FAILURE) {
6887c478bd9Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
6897c478bd9Sstevel@tonic-gate 		ugenp->ug_dev_state = USB_DEV_DISCONNECTED;
6907c478bd9Sstevel@tonic-gate 
6917c478bd9Sstevel@tonic-gate 		/* wakeup devstat reads and polls */
6927c478bd9Sstevel@tonic-gate 		ugen_ds_change(ugenp);
6937c478bd9Sstevel@tonic-gate 		ugen_ds_poll_wakeup(ugenp);
6947c478bd9Sstevel@tonic-gate 
6957c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
6967c478bd9Sstevel@tonic-gate 
6977c478bd9Sstevel@tonic-gate 		if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
6987c478bd9Sstevel@tonic-gate 			ugen_pm_idle_component(ugenp);
6997c478bd9Sstevel@tonic-gate 		}
7007c478bd9Sstevel@tonic-gate 
7017c478bd9Sstevel@tonic-gate 		return;
7027c478bd9Sstevel@tonic-gate 	}
7037c478bd9Sstevel@tonic-gate 
7047c478bd9Sstevel@tonic-gate 	/*
7057c478bd9Sstevel@tonic-gate 	 * get exclusive access, we don't want to change state in the
7067c478bd9Sstevel@tonic-gate 	 * middle of some other actions
7077c478bd9Sstevel@tonic-gate 	 */
7087c478bd9Sstevel@tonic-gate 	(void) usb_serialize_access(ugenp->ug_ser_cookie, USB_WAIT, 0);
7097c478bd9Sstevel@tonic-gate 
7107c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
7117c478bd9Sstevel@tonic-gate 	switch (ugenp->ug_dev_state) {
7127c478bd9Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
7137c478bd9Sstevel@tonic-gate 		ugenp->ug_dev_state = (ugenp->ug_open_count == 0) ?
7147c478bd9Sstevel@tonic-gate 		    USB_DEV_ONLINE : USB_UGEN_DEV_UNAVAILABLE_RECONNECT;
7157c478bd9Sstevel@tonic-gate 
7167c478bd9Sstevel@tonic-gate 		break;
7177c478bd9Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
7187c478bd9Sstevel@tonic-gate 		ugenp->ug_dev_state = (ugenp->ug_open_count == 0) ?
7197c478bd9Sstevel@tonic-gate 		    USB_DEV_ONLINE : USB_UGEN_DEV_UNAVAILABLE_RESUME;
7207c478bd9Sstevel@tonic-gate 
7217c478bd9Sstevel@tonic-gate 		break;
7227c478bd9Sstevel@tonic-gate 	}
7237c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_HOTPLUG, ugenp->ug_log_hdl,
7247c478bd9Sstevel@tonic-gate 	    "ugen_restore_state: state=%d, opencount=%d",
7257c478bd9Sstevel@tonic-gate 	    ugenp->ug_dev_state, ugenp->ug_open_count);
7267c478bd9Sstevel@tonic-gate 
7277c478bd9Sstevel@tonic-gate 	/* wakeup devstat reads and polls */
7287c478bd9Sstevel@tonic-gate 	ugen_ds_change(ugenp);
7297c478bd9Sstevel@tonic-gate 	ugen_ds_poll_wakeup(ugenp);
7307c478bd9Sstevel@tonic-gate 
7317c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
7327c478bd9Sstevel@tonic-gate 	usb_release_access(ugenp->ug_ser_cookie);
7337c478bd9Sstevel@tonic-gate 
7347c478bd9Sstevel@tonic-gate 	if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
7357c478bd9Sstevel@tonic-gate 		ugen_pm_idle_component(ugenp);
7367c478bd9Sstevel@tonic-gate 	}
7377c478bd9Sstevel@tonic-gate }
7387c478bd9Sstevel@tonic-gate 
7397c478bd9Sstevel@tonic-gate 
7407c478bd9Sstevel@tonic-gate /*
7417c478bd9Sstevel@tonic-gate  * usb_ugen_open:
7427c478bd9Sstevel@tonic-gate  */
7437c478bd9Sstevel@tonic-gate /* ARGSUSED */
7447c478bd9Sstevel@tonic-gate int
7457c478bd9Sstevel@tonic-gate usb_ugen_open(usb_ugen_hdl_t usb_ugen_hdl, dev_t *devp, int flag, int sflag,
7467c478bd9Sstevel@tonic-gate     cred_t *cr)
7477c478bd9Sstevel@tonic-gate {
7487c478bd9Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
7497c478bd9Sstevel@tonic-gate 				(usb_ugen_hdl_impl_t *)usb_ugen_hdl;
7507c478bd9Sstevel@tonic-gate 	ugen_state_t		*ugenp;
7517c478bd9Sstevel@tonic-gate 	int			rval;
7527c478bd9Sstevel@tonic-gate 	int			minor_node_type;
7537c478bd9Sstevel@tonic-gate 
7547c478bd9Sstevel@tonic-gate 	if (usb_ugen_hdl == NULL) {
7557c478bd9Sstevel@tonic-gate 
7567c478bd9Sstevel@tonic-gate 		return (EINVAL);
7577c478bd9Sstevel@tonic-gate 	}
7587c478bd9Sstevel@tonic-gate 
7597c478bd9Sstevel@tonic-gate 	ugenp = usb_ugen_hdl_impl->hdl_ugenp;
760*96603521Sfrits 
761*96603521Sfrits 	if (ugen_is_valid_minor_node(ugenp, *devp) != USB_SUCCESS) {
762*96603521Sfrits 
763*96603521Sfrits 		return (EINVAL);
764*96603521Sfrits 	}
765*96603521Sfrits 
7667c478bd9Sstevel@tonic-gate 	minor_node_type = UGEN_MINOR_TYPE(ugenp, *devp);
7677c478bd9Sstevel@tonic-gate 
7687c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
7697c478bd9Sstevel@tonic-gate 	    "usb_ugen_open: minor=%u", getminor(*devp));
7707c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
7717c478bd9Sstevel@tonic-gate 	    "cfgval=%" PRIu64 " cfgidx=%" PRIu64 " if=%" PRIu64
7727c478bd9Sstevel@tonic-gate 	    " alt=%" PRIu64 " epidx=%" PRIu64 " type=0x%" PRIx64,
7737c478bd9Sstevel@tonic-gate 	    UGEN_MINOR_CFGVAL(ugenp, *devp), UGEN_MINOR_CFGIDX(ugenp, *devp),
7747c478bd9Sstevel@tonic-gate 	    UGEN_MINOR_IF(ugenp, *devp), UGEN_MINOR_ALT(ugenp, *devp),
7757c478bd9Sstevel@tonic-gate 	    UGEN_MINOR_EPIDX(ugenp, *devp), UGEN_MINOR_TYPE(ugenp, *devp));
7767c478bd9Sstevel@tonic-gate 
7777c478bd9Sstevel@tonic-gate 	/* first check for legal open flags */
7787c478bd9Sstevel@tonic-gate 	if ((rval = ugen_check_open_flags(ugenp, *devp, flag)) != 0) {
7797c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
7807c478bd9Sstevel@tonic-gate 		    "usb_ugen_open: check failed, rval=%d", rval);
7817c478bd9Sstevel@tonic-gate 
7827c478bd9Sstevel@tonic-gate 		return (rval);
7837c478bd9Sstevel@tonic-gate 	}
7847c478bd9Sstevel@tonic-gate 
7857c478bd9Sstevel@tonic-gate 	/* exclude other threads including other opens */
7867c478bd9Sstevel@tonic-gate 	if (usb_serialize_access(ugenp->ug_ser_cookie,
7877c478bd9Sstevel@tonic-gate 	    USB_WAIT_SIG, 0) <= 0) {
7887c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
7897c478bd9Sstevel@tonic-gate 		    "usb_ugen_open: interrupted");
7907c478bd9Sstevel@tonic-gate 
7917c478bd9Sstevel@tonic-gate 		return (EINTR);
7927c478bd9Sstevel@tonic-gate 	}
7937c478bd9Sstevel@tonic-gate 
7947c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
7957c478bd9Sstevel@tonic-gate 
7967c478bd9Sstevel@tonic-gate 	/* always allow open of dev stat node */
7977c478bd9Sstevel@tonic-gate 	if (minor_node_type != UGEN_MINOR_DEV_STAT_NODE) {
7987c478bd9Sstevel@tonic-gate 
7997c478bd9Sstevel@tonic-gate 		/* if we are not online or powered down, fail open */
8007c478bd9Sstevel@tonic-gate 		switch (ugenp->ug_dev_state) {
8017c478bd9Sstevel@tonic-gate 		case USB_DEV_ONLINE:
8027c478bd9Sstevel@tonic-gate 
8037c478bd9Sstevel@tonic-gate 			break;
8047c478bd9Sstevel@tonic-gate 		case USB_DEV_DISCONNECTED:
8057c478bd9Sstevel@tonic-gate 			rval = ENODEV;
8067c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
8077c478bd9Sstevel@tonic-gate 
8087c478bd9Sstevel@tonic-gate 			goto done;
8097c478bd9Sstevel@tonic-gate 		case USB_DEV_SUSPENDED:
8107c478bd9Sstevel@tonic-gate 		case USB_UGEN_DEV_UNAVAILABLE_RESUME:
8117c478bd9Sstevel@tonic-gate 		case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
8127c478bd9Sstevel@tonic-gate 		default:
8137c478bd9Sstevel@tonic-gate 			rval = EBADF;
8147c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
8157c478bd9Sstevel@tonic-gate 
8167c478bd9Sstevel@tonic-gate 			goto done;
8177c478bd9Sstevel@tonic-gate 		}
8187c478bd9Sstevel@tonic-gate 	}
8197c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
8207c478bd9Sstevel@tonic-gate 
8217c478bd9Sstevel@tonic-gate 	/* open node depending on type */
8227c478bd9Sstevel@tonic-gate 	switch (minor_node_type) {
8237c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_EP_XFER_NODE:
8247c478bd9Sstevel@tonic-gate 		if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
8257c478bd9Sstevel@tonic-gate 			ugen_pm_busy_component(ugenp);
8267c478bd9Sstevel@tonic-gate 			(void) pm_raise_power(ugenp->ug_dip, 0,
8277c478bd9Sstevel@tonic-gate 						USB_DEV_OS_FULL_PWR);
8287c478bd9Sstevel@tonic-gate 		}
8297c478bd9Sstevel@tonic-gate 
8307c478bd9Sstevel@tonic-gate 		rval = ugen_epx_open(ugenp, *devp, flag);
8317c478bd9Sstevel@tonic-gate 		if (rval == 0) {
8327c478bd9Sstevel@tonic-gate 			mutex_enter(&ugenp->ug_mutex);
8337c478bd9Sstevel@tonic-gate 			ugenp->ug_open_count++;
8347c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
8357c478bd9Sstevel@tonic-gate 		} else {
8367c478bd9Sstevel@tonic-gate 			if (ugenp->ug_hdl->hdl_flags &
8377c478bd9Sstevel@tonic-gate 			    USB_UGEN_ENABLE_PM) {
8387c478bd9Sstevel@tonic-gate 				ugen_pm_idle_component(ugenp);
8397c478bd9Sstevel@tonic-gate 			}
8407c478bd9Sstevel@tonic-gate 		}
8417c478bd9Sstevel@tonic-gate 
8427c478bd9Sstevel@tonic-gate 		break;
8437c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_EP_STAT_NODE:
8447c478bd9Sstevel@tonic-gate 		rval = ugen_eps_open(ugenp, *devp, flag);
8457c478bd9Sstevel@tonic-gate 		if (rval == 0) {
8467c478bd9Sstevel@tonic-gate 			mutex_enter(&ugenp->ug_mutex);
8477c478bd9Sstevel@tonic-gate 			ugenp->ug_open_count++;
8487c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
8497c478bd9Sstevel@tonic-gate 		}
8507c478bd9Sstevel@tonic-gate 
8517c478bd9Sstevel@tonic-gate 		break;
8527c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_DEV_STAT_NODE:
8537c478bd9Sstevel@tonic-gate 		rval = ugen_ds_open(ugenp, *devp, flag);
8547c478bd9Sstevel@tonic-gate 
8557c478bd9Sstevel@tonic-gate 		break;
8567c478bd9Sstevel@tonic-gate 	default:
8577c478bd9Sstevel@tonic-gate 		rval = EINVAL;
8587c478bd9Sstevel@tonic-gate 
8597c478bd9Sstevel@tonic-gate 		break;
8607c478bd9Sstevel@tonic-gate 	}
8617c478bd9Sstevel@tonic-gate done:
8627c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
8637c478bd9Sstevel@tonic-gate 
8647c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
8657c478bd9Sstevel@tonic-gate 	    "usb_ugen_open: minor=0x%x rval=%d state=%d cnt=%d",
8667c478bd9Sstevel@tonic-gate 	    getminor(*devp), rval, ugenp->ug_dev_state,
8677c478bd9Sstevel@tonic-gate 	    ugenp->ug_open_count);
8687c478bd9Sstevel@tonic-gate 
8697c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
8707c478bd9Sstevel@tonic-gate 
8717c478bd9Sstevel@tonic-gate 	usb_release_access(ugenp->ug_ser_cookie);
8727c478bd9Sstevel@tonic-gate 
8737c478bd9Sstevel@tonic-gate 	return (rval);
8747c478bd9Sstevel@tonic-gate }
8757c478bd9Sstevel@tonic-gate 
8767c478bd9Sstevel@tonic-gate 
8777c478bd9Sstevel@tonic-gate /*
8787c478bd9Sstevel@tonic-gate  * usb_ugen_close()
8797c478bd9Sstevel@tonic-gate  */
8807c478bd9Sstevel@tonic-gate /* ARGSUSED */
8817c478bd9Sstevel@tonic-gate int
8827c478bd9Sstevel@tonic-gate usb_ugen_close(usb_ugen_hdl_t usb_ugen_hdl, dev_t dev, int flag, int otype,
8837c478bd9Sstevel@tonic-gate     cred_t *cr)
8847c478bd9Sstevel@tonic-gate {
8857c478bd9Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
8867c478bd9Sstevel@tonic-gate 				(usb_ugen_hdl_impl_t *)usb_ugen_hdl;
8877c478bd9Sstevel@tonic-gate 	ugen_state_t		*ugenp;
8887c478bd9Sstevel@tonic-gate 	int			minor_node_type;
8897c478bd9Sstevel@tonic-gate 
8907c478bd9Sstevel@tonic-gate 	if (usb_ugen_hdl == NULL) {
8917c478bd9Sstevel@tonic-gate 
8927c478bd9Sstevel@tonic-gate 		return (EINVAL);
8937c478bd9Sstevel@tonic-gate 	}
8947c478bd9Sstevel@tonic-gate 
8957c478bd9Sstevel@tonic-gate 	ugenp = usb_ugen_hdl_impl->hdl_ugenp;
896*96603521Sfrits 	if (ugen_is_valid_minor_node(ugenp, dev) != USB_SUCCESS) {
897*96603521Sfrits 
898*96603521Sfrits 		return (EINVAL);
899*96603521Sfrits 	}
900*96603521Sfrits 
9017c478bd9Sstevel@tonic-gate 	minor_node_type = UGEN_MINOR_TYPE(ugenp, dev);
9027c478bd9Sstevel@tonic-gate 
9037c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
9047c478bd9Sstevel@tonic-gate 	    "usb_ugen_close: minor=0x%x", getminor(dev));
9057c478bd9Sstevel@tonic-gate 
9067c478bd9Sstevel@tonic-gate 	/* exclude other threads, including other opens */
9077c478bd9Sstevel@tonic-gate 	if (usb_serialize_access(ugenp->ug_ser_cookie,
9087c478bd9Sstevel@tonic-gate 	    USB_WAIT_SIG, 0) <= 0) {
9097c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
9107c478bd9Sstevel@tonic-gate 		    "usb_ugen_close: interrupted");
9117c478bd9Sstevel@tonic-gate 
9127c478bd9Sstevel@tonic-gate 		return (EINTR);
9137c478bd9Sstevel@tonic-gate 	}
9147c478bd9Sstevel@tonic-gate 
9157c478bd9Sstevel@tonic-gate 	/* close node depending on type */
9167c478bd9Sstevel@tonic-gate 	switch (minor_node_type) {
9177c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_EP_XFER_NODE:
9187c478bd9Sstevel@tonic-gate 		ugen_epx_close(ugenp, dev, flag);
9197c478bd9Sstevel@tonic-gate 		if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
9207c478bd9Sstevel@tonic-gate 			ugen_pm_idle_component(ugenp);
9217c478bd9Sstevel@tonic-gate 		}
9227c478bd9Sstevel@tonic-gate 
9237c478bd9Sstevel@tonic-gate 		break;
9247c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_EP_STAT_NODE:
9257c478bd9Sstevel@tonic-gate 		ugen_eps_close(ugenp, dev, flag);
9267c478bd9Sstevel@tonic-gate 
9277c478bd9Sstevel@tonic-gate 		break;
9287c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_DEV_STAT_NODE:
9297c478bd9Sstevel@tonic-gate 		ugen_ds_close(ugenp, dev, flag);
9307c478bd9Sstevel@tonic-gate 
9317c478bd9Sstevel@tonic-gate 		break;
9327c478bd9Sstevel@tonic-gate 	default:
9337c478bd9Sstevel@tonic-gate 		usb_release_access(ugenp->ug_ser_cookie);
9347c478bd9Sstevel@tonic-gate 
9357c478bd9Sstevel@tonic-gate 		return (EINVAL);
9367c478bd9Sstevel@tonic-gate 	}
9377c478bd9Sstevel@tonic-gate 
9387c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
9397c478bd9Sstevel@tonic-gate 	if (minor_node_type != UGEN_MINOR_DEV_STAT_NODE) {
9407c478bd9Sstevel@tonic-gate 		ASSERT(ugenp->ug_open_count > 0);
9417c478bd9Sstevel@tonic-gate 		if ((--ugenp->ug_open_count == 0) &&
9427c478bd9Sstevel@tonic-gate 		    ((ugenp->ug_dev_state == USB_UGEN_DEV_UNAVAILABLE_RESUME) ||
9437c478bd9Sstevel@tonic-gate 		    (ugenp->ug_dev_state ==
9447c478bd9Sstevel@tonic-gate 		    USB_UGEN_DEV_UNAVAILABLE_RECONNECT))) {
9457c478bd9Sstevel@tonic-gate 			ugenp->ug_dev_state = USB_DEV_ONLINE;
9467c478bd9Sstevel@tonic-gate 
9477c478bd9Sstevel@tonic-gate 			/* wakeup devstat reads and polls */
9487c478bd9Sstevel@tonic-gate 			ugen_ds_change(ugenp);
9497c478bd9Sstevel@tonic-gate 			ugen_ds_poll_wakeup(ugenp);
9507c478bd9Sstevel@tonic-gate 		}
9517c478bd9Sstevel@tonic-gate 	}
9527c478bd9Sstevel@tonic-gate 
9537c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
9547c478bd9Sstevel@tonic-gate 	    "usb_ugen_close: minor=0x%x state=%d cnt=%d",
9557c478bd9Sstevel@tonic-gate 	    getminor(dev), ugenp->ug_dev_state, ugenp->ug_open_count);
9567c478bd9Sstevel@tonic-gate 
9577c478bd9Sstevel@tonic-gate 	if (ugenp->ug_open_count == 0) {
9587c478bd9Sstevel@tonic-gate 		ASSERT(ugen_epxs_check_open_nodes(ugenp) == USB_FAILURE);
9597c478bd9Sstevel@tonic-gate 	}
9607c478bd9Sstevel@tonic-gate 
9617c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
9627c478bd9Sstevel@tonic-gate 
9637c478bd9Sstevel@tonic-gate 	usb_release_access(ugenp->ug_ser_cookie);
9647c478bd9Sstevel@tonic-gate 
9657c478bd9Sstevel@tonic-gate 	return (0);
9667c478bd9Sstevel@tonic-gate }
9677c478bd9Sstevel@tonic-gate 
9687c478bd9Sstevel@tonic-gate 
9697c478bd9Sstevel@tonic-gate /*
9707c478bd9Sstevel@tonic-gate  * usb_ugen_read/write()
9717c478bd9Sstevel@tonic-gate  */
9727c478bd9Sstevel@tonic-gate /*ARGSUSED*/
9737c478bd9Sstevel@tonic-gate int
9747c478bd9Sstevel@tonic-gate usb_ugen_read(usb_ugen_hdl_t usb_ugen_hdl, dev_t dev, struct uio *uiop,
9757c478bd9Sstevel@tonic-gate     cred_t *credp)
9767c478bd9Sstevel@tonic-gate {
977*96603521Sfrits 	ugen_state_t		*ugenp;
978*96603521Sfrits 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
979*96603521Sfrits 				(usb_ugen_hdl_impl_t *)usb_ugen_hdl;
980*96603521Sfrits 
981*96603521Sfrits 	if (usb_ugen_hdl == NULL) {
982*96603521Sfrits 
983*96603521Sfrits 		return (EINVAL);
984*96603521Sfrits 	}
985*96603521Sfrits 	ugenp = usb_ugen_hdl_impl->hdl_ugenp;
986*96603521Sfrits 
987*96603521Sfrits 	if (ugen_is_valid_minor_node(ugenp, dev) != USB_SUCCESS) {
988*96603521Sfrits 
989*96603521Sfrits 		return (EINVAL);
990*96603521Sfrits 	}
991*96603521Sfrits 
9927c478bd9Sstevel@tonic-gate 	return (physio(ugen_strategy,
9937c478bd9Sstevel@tonic-gate 	    (struct buf *)0, dev, B_READ, ugen_minphys, uiop));
9947c478bd9Sstevel@tonic-gate }
9957c478bd9Sstevel@tonic-gate 
9967c478bd9Sstevel@tonic-gate 
9977c478bd9Sstevel@tonic-gate /*ARGSUSED*/
9987c478bd9Sstevel@tonic-gate int
9997c478bd9Sstevel@tonic-gate usb_ugen_write(usb_ugen_hdl_t usb_ugen_hdl, dev_t dev, struct uio *uiop,
10007c478bd9Sstevel@tonic-gate     cred_t *credp)
10017c478bd9Sstevel@tonic-gate {
1002*96603521Sfrits 	ugen_state_t		*ugenp;
1003*96603521Sfrits 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
1004*96603521Sfrits 				(usb_ugen_hdl_impl_t *)usb_ugen_hdl;
1005*96603521Sfrits 
1006*96603521Sfrits 	if (usb_ugen_hdl == NULL) {
1007*96603521Sfrits 
1008*96603521Sfrits 		return (EINVAL);
1009*96603521Sfrits 	}
1010*96603521Sfrits 	ugenp = usb_ugen_hdl_impl->hdl_ugenp;
1011*96603521Sfrits 
1012*96603521Sfrits 	if (ugen_is_valid_minor_node(ugenp, dev) != USB_SUCCESS) {
1013*96603521Sfrits 
1014*96603521Sfrits 		return (EINVAL);
1015*96603521Sfrits 	}
1016*96603521Sfrits 
10177c478bd9Sstevel@tonic-gate 	return (physio(ugen_strategy,
10187c478bd9Sstevel@tonic-gate 	    (struct buf *)0, dev, B_WRITE, ugen_minphys, uiop));
10197c478bd9Sstevel@tonic-gate }
10207c478bd9Sstevel@tonic-gate 
10217c478bd9Sstevel@tonic-gate 
10227c478bd9Sstevel@tonic-gate /*
10237c478bd9Sstevel@tonic-gate  * usb_ugen_poll
10247c478bd9Sstevel@tonic-gate  */
10257c478bd9Sstevel@tonic-gate int
10267c478bd9Sstevel@tonic-gate usb_ugen_poll(usb_ugen_hdl_t usb_ugen_hdl, dev_t dev, short events,
10277c478bd9Sstevel@tonic-gate     int anyyet,  short *reventsp, struct pollhead **phpp)
10287c478bd9Sstevel@tonic-gate {
10297c478bd9Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
10307c478bd9Sstevel@tonic-gate 				(usb_ugen_hdl_impl_t *)usb_ugen_hdl;
10317c478bd9Sstevel@tonic-gate 	ugen_state_t		*ugenp;
10327c478bd9Sstevel@tonic-gate 	int			minor_node_type;
10337c478bd9Sstevel@tonic-gate 	uint_t			ep_index;
10347c478bd9Sstevel@tonic-gate 	ugen_ep_t		*epp;
10357c478bd9Sstevel@tonic-gate 
10367c478bd9Sstevel@tonic-gate 	if (usb_ugen_hdl == NULL) {
10377c478bd9Sstevel@tonic-gate 
10387c478bd9Sstevel@tonic-gate 		return (EINVAL);
10397c478bd9Sstevel@tonic-gate 	}
10407c478bd9Sstevel@tonic-gate 
10417c478bd9Sstevel@tonic-gate 	ugenp = usb_ugen_hdl_impl->hdl_ugenp;
1042*96603521Sfrits 	if (ugen_is_valid_minor_node(ugenp, dev) != USB_SUCCESS) {
1043*96603521Sfrits 
1044*96603521Sfrits 		return (EINVAL);
1045*96603521Sfrits 	}
10467c478bd9Sstevel@tonic-gate 
10477c478bd9Sstevel@tonic-gate 	minor_node_type = UGEN_MINOR_TYPE(ugenp, dev);
10487c478bd9Sstevel@tonic-gate 	ep_index	= UGEN_MINOR_EPIDX(ugenp, dev);
10497c478bd9Sstevel@tonic-gate 	epp		= &ugenp->ug_ep[ep_index];
10507c478bd9Sstevel@tonic-gate 
10517c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
10527c478bd9Sstevel@tonic-gate 
10537c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_POLL, ugenp->ug_log_hdl,
10547c478bd9Sstevel@tonic-gate 	    "usb_ugen_poll: "
10557c478bd9Sstevel@tonic-gate 	    "dev=0x%lx events=0x%x anyyet=0x%x rev=0x%p type=%d "
10567c478bd9Sstevel@tonic-gate 	    "devstat=0x%x devstate=0x%x",
10577c478bd9Sstevel@tonic-gate 	    dev, events, anyyet, (void *)reventsp, minor_node_type,
10587c478bd9Sstevel@tonic-gate 	    ugenp->ug_ds.dev_stat, ugenp->ug_ds.dev_state);
10597c478bd9Sstevel@tonic-gate 
10607c478bd9Sstevel@tonic-gate 	*reventsp = 0;
10617c478bd9Sstevel@tonic-gate 
10627c478bd9Sstevel@tonic-gate 	if (ugenp->ug_dev_state == USB_DEV_ONLINE) {
10637c478bd9Sstevel@tonic-gate 		switch (minor_node_type) {
10647c478bd9Sstevel@tonic-gate 		case UGEN_MINOR_EP_XFER_NODE:
10657c478bd9Sstevel@tonic-gate 			/* if interrupt IN ep and there is data, set POLLIN */
10667c478bd9Sstevel@tonic-gate 			if ((UGEN_XFER_TYPE(epp) == USB_EP_ATTR_INTR) &&
10677c478bd9Sstevel@tonic-gate 			    (UGEN_XFER_DIR(epp) & USB_EP_DIR_IN)) {
10687c478bd9Sstevel@tonic-gate 
10697c478bd9Sstevel@tonic-gate 				/*
10707c478bd9Sstevel@tonic-gate 				 * if we are not polling, force another
10717c478bd9Sstevel@tonic-gate 				 * read to kick off polling
10727c478bd9Sstevel@tonic-gate 				 */
10737c478bd9Sstevel@tonic-gate 				mutex_enter(&epp->ep_mutex);
10747c478bd9Sstevel@tonic-gate 				if ((epp->ep_data) ||
10757c478bd9Sstevel@tonic-gate 				    ((epp->ep_state &
10767c478bd9Sstevel@tonic-gate 				    UGEN_EP_STATE_INTR_IN_POLLING_ON) == 0)) {
10777c478bd9Sstevel@tonic-gate 					*reventsp |= POLLIN;
10787c478bd9Sstevel@tonic-gate 				} else if (!anyyet) {
10797c478bd9Sstevel@tonic-gate 					*phpp = &epp->ep_pollhead;
10807c478bd9Sstevel@tonic-gate 					epp->ep_state |=
10817c478bd9Sstevel@tonic-gate 					    UGEN_EP_STATE_INTR_IN_POLL_PENDING;
10827c478bd9Sstevel@tonic-gate 				}
10837c478bd9Sstevel@tonic-gate 				mutex_exit(&epp->ep_mutex);
10847c478bd9Sstevel@tonic-gate 			} else {
10857c478bd9Sstevel@tonic-gate 				/* no poll on other ep nodes */
10867c478bd9Sstevel@tonic-gate 				*reventsp |= POLLERR;
10877c478bd9Sstevel@tonic-gate 			}
10887c478bd9Sstevel@tonic-gate 
10897c478bd9Sstevel@tonic-gate 			break;
10907c478bd9Sstevel@tonic-gate 		case UGEN_MINOR_DEV_STAT_NODE:
10917c478bd9Sstevel@tonic-gate 			if (ugenp->ug_ds.dev_stat & UGEN_DEV_STATUS_CHANGED) {
10927c478bd9Sstevel@tonic-gate 				*reventsp |= POLLIN;
10937c478bd9Sstevel@tonic-gate 			} else if (!anyyet) {
10947c478bd9Sstevel@tonic-gate 				*phpp = &ugenp->ug_ds.dev_pollhead;
10957c478bd9Sstevel@tonic-gate 				ugenp->ug_ds.dev_stat |=
10967c478bd9Sstevel@tonic-gate 				    UGEN_DEV_STATUS_POLL_PENDING;
10977c478bd9Sstevel@tonic-gate 			}
10987c478bd9Sstevel@tonic-gate 
10997c478bd9Sstevel@tonic-gate 			break;
11007c478bd9Sstevel@tonic-gate 		case UGEN_MINOR_EP_STAT_NODE:
11017c478bd9Sstevel@tonic-gate 		default:
11027c478bd9Sstevel@tonic-gate 			*reventsp |= POLLERR;
11037c478bd9Sstevel@tonic-gate 
11047c478bd9Sstevel@tonic-gate 			break;
11057c478bd9Sstevel@tonic-gate 		}
11067c478bd9Sstevel@tonic-gate 	} else {
11077c478bd9Sstevel@tonic-gate 		if (ugenp->ug_ds.dev_stat & UGEN_DEV_STATUS_CHANGED) {
11087c478bd9Sstevel@tonic-gate 			*reventsp |= POLLHUP|POLLIN;
11097c478bd9Sstevel@tonic-gate 		} else if (!anyyet) {
11107c478bd9Sstevel@tonic-gate 			*phpp = &ugenp->ug_ds.dev_pollhead;
11117c478bd9Sstevel@tonic-gate 			ugenp->ug_ds.dev_stat |=
11127c478bd9Sstevel@tonic-gate 				    UGEN_DEV_STATUS_POLL_PENDING;
11137c478bd9Sstevel@tonic-gate 		}
11147c478bd9Sstevel@tonic-gate 	}
11157c478bd9Sstevel@tonic-gate 
11167c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
11177c478bd9Sstevel@tonic-gate 
11187c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_POLL, ugenp->ug_log_hdl,
11197c478bd9Sstevel@tonic-gate 	    "usb_ugen_poll end: reventsp=0x%x", *reventsp);
11207c478bd9Sstevel@tonic-gate 
11217c478bd9Sstevel@tonic-gate 	return (0);
11227c478bd9Sstevel@tonic-gate }
11237c478bd9Sstevel@tonic-gate 
11247c478bd9Sstevel@tonic-gate 
11257c478bd9Sstevel@tonic-gate /*
11267c478bd9Sstevel@tonic-gate  * ugen_strategy
11277c478bd9Sstevel@tonic-gate  */
11287c478bd9Sstevel@tonic-gate static int
11297c478bd9Sstevel@tonic-gate ugen_strategy(struct buf *bp)
11307c478bd9Sstevel@tonic-gate {
11317c478bd9Sstevel@tonic-gate 	dev_t		dev = bp->b_edev;
11327c478bd9Sstevel@tonic-gate 	int		rval = 0;
11337c478bd9Sstevel@tonic-gate 	ugen_state_t	*ugenp = ugen_devt2state(dev);
11347c478bd9Sstevel@tonic-gate 	int		minor_node_type = UGEN_MINOR_TYPE(ugenp, dev);
11357c478bd9Sstevel@tonic-gate 
11367c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
11377c478bd9Sstevel@tonic-gate 	    "ugen_strategy: bp=0x%p minor=0x%x", bp, getminor(dev));
11387c478bd9Sstevel@tonic-gate 
1139*96603521Sfrits 	if (ugen_is_valid_minor_node(ugenp, dev) != USB_SUCCESS) {
1140*96603521Sfrits 
1141*96603521Sfrits 		return (EINVAL);
1142*96603521Sfrits 	}
1143*96603521Sfrits 
11447c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
11457c478bd9Sstevel@tonic-gate 	ugenp->ug_pending_cmds++;
11467c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
11477c478bd9Sstevel@tonic-gate 
11487c478bd9Sstevel@tonic-gate 	bp_mapin(bp);
11497c478bd9Sstevel@tonic-gate 
11507c478bd9Sstevel@tonic-gate 	switch (minor_node_type) {
11517c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_EP_XFER_NODE:
11527c478bd9Sstevel@tonic-gate 		rval = ugen_epx_req(ugenp, bp);
11537c478bd9Sstevel@tonic-gate 
11547c478bd9Sstevel@tonic-gate 		break;
11557c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_EP_STAT_NODE:
11567c478bd9Sstevel@tonic-gate 		rval = ugen_eps_req(ugenp, bp);
11577c478bd9Sstevel@tonic-gate 
11587c478bd9Sstevel@tonic-gate 		break;
11597c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_DEV_STAT_NODE:
11607c478bd9Sstevel@tonic-gate 		rval = ugen_ds_req(ugenp, bp);
11617c478bd9Sstevel@tonic-gate 
11627c478bd9Sstevel@tonic-gate 		break;
11637c478bd9Sstevel@tonic-gate 	default:
11647c478bd9Sstevel@tonic-gate 		rval = EINVAL;
11657c478bd9Sstevel@tonic-gate 
11667c478bd9Sstevel@tonic-gate 		break;
11677c478bd9Sstevel@tonic-gate 	}
11687c478bd9Sstevel@tonic-gate 
11697c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
11707c478bd9Sstevel@tonic-gate 	ugenp->ug_pending_cmds--;
11717c478bd9Sstevel@tonic-gate 
11727c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
11737c478bd9Sstevel@tonic-gate 	    "ugen_strategy: "
11747c478bd9Sstevel@tonic-gate 	    "bp=0x%p cnt=%lu resid=%lu err=%d minor=0x%x rval=%d #cmds=%d",
11757c478bd9Sstevel@tonic-gate 	    (void *)bp, bp->b_bcount, bp->b_resid, geterror(bp),
11767c478bd9Sstevel@tonic-gate 	    getminor(dev), rval, ugenp->ug_pending_cmds);
11777c478bd9Sstevel@tonic-gate 
11787c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
11797c478bd9Sstevel@tonic-gate 
11807c478bd9Sstevel@tonic-gate 	if (rval) {
11817c478bd9Sstevel@tonic-gate 		if (geterror(bp) == 0) {
11827c478bd9Sstevel@tonic-gate 			bioerror(bp, rval);
11837c478bd9Sstevel@tonic-gate 		}
11847c478bd9Sstevel@tonic-gate 	}
11857c478bd9Sstevel@tonic-gate 
11867c478bd9Sstevel@tonic-gate 	biodone(bp);
11877c478bd9Sstevel@tonic-gate 
11887c478bd9Sstevel@tonic-gate 	return (0);
11897c478bd9Sstevel@tonic-gate }
11907c478bd9Sstevel@tonic-gate 
11917c478bd9Sstevel@tonic-gate 
11927c478bd9Sstevel@tonic-gate /*
11937c478bd9Sstevel@tonic-gate  * ugen_minphys:
11947c478bd9Sstevel@tonic-gate  */
11957c478bd9Sstevel@tonic-gate static void
11967c478bd9Sstevel@tonic-gate ugen_minphys(struct buf *bp)
11977c478bd9Sstevel@tonic-gate {
11987c478bd9Sstevel@tonic-gate 	dev_t		dev = bp->b_edev;
11997c478bd9Sstevel@tonic-gate 	ugen_state_t	*ugenp = ugen_devt2state(dev);
12007c478bd9Sstevel@tonic-gate 	int		minor_node_type = UGEN_MINOR_TYPE(ugenp, dev);
12017c478bd9Sstevel@tonic-gate 	uint_t		ep_index = UGEN_MINOR_EPIDX(ugenp, dev);
12027c478bd9Sstevel@tonic-gate 	ugen_ep_t	*epp = &ugenp->ug_ep[ep_index];
12037c478bd9Sstevel@tonic-gate 
12047c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
12057c478bd9Sstevel@tonic-gate 	    "ugen_phys: bp=0x%p dev=0x%lx index=%d type=0x%x",
12067c478bd9Sstevel@tonic-gate 	    (void *)bp, dev, ep_index, minor_node_type);
12077c478bd9Sstevel@tonic-gate 
12087c478bd9Sstevel@tonic-gate 	switch (minor_node_type) {
12097c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_EP_XFER_NODE:
12107c478bd9Sstevel@tonic-gate 		switch (UGEN_XFER_TYPE(epp)) {
12117c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_BULK:
12127c478bd9Sstevel@tonic-gate 			if (bp->b_bcount > ugenp->ug_max_bulk_xfer_sz) {
12137c478bd9Sstevel@tonic-gate 				bp->b_bcount = ugenp->ug_max_bulk_xfer_sz;
12147c478bd9Sstevel@tonic-gate 			}
12157c478bd9Sstevel@tonic-gate 
12167c478bd9Sstevel@tonic-gate 			break;
12177c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_INTR:
12187c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_CONTROL:
12197c478bd9Sstevel@tonic-gate 		default:
12207c478bd9Sstevel@tonic-gate 
12217c478bd9Sstevel@tonic-gate 			break;
12227c478bd9Sstevel@tonic-gate 		}
12237c478bd9Sstevel@tonic-gate 		break;
12247c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_EP_STAT_NODE:
12257c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_DEV_STAT_NODE:
12267c478bd9Sstevel@tonic-gate 	default:
12277c478bd9Sstevel@tonic-gate 
12287c478bd9Sstevel@tonic-gate 		break;
12297c478bd9Sstevel@tonic-gate 	}
12307c478bd9Sstevel@tonic-gate }
12317c478bd9Sstevel@tonic-gate 
12327c478bd9Sstevel@tonic-gate 
12337c478bd9Sstevel@tonic-gate /*
12347c478bd9Sstevel@tonic-gate  * check whether flag is appropriate for node type
12357c478bd9Sstevel@tonic-gate  */
12367c478bd9Sstevel@tonic-gate static int
12377c478bd9Sstevel@tonic-gate ugen_check_open_flags(ugen_state_t *ugenp, dev_t dev, int flag)
12387c478bd9Sstevel@tonic-gate {
12397c478bd9Sstevel@tonic-gate 	ugen_ep_t *epp;
12407c478bd9Sstevel@tonic-gate 	int	minor_node_type = UGEN_MINOR_TYPE(ugenp, dev);
12417c478bd9Sstevel@tonic-gate 	int	rval = 0;
12427c478bd9Sstevel@tonic-gate 
12437c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
12447c478bd9Sstevel@tonic-gate 	    "ugen_check_open_flags: "
12457c478bd9Sstevel@tonic-gate 	    "dev=0x%lx, type=0x%x flag=0x%x idx=%" PRIu64,
12467c478bd9Sstevel@tonic-gate 	    dev, minor_node_type, flag, UGEN_MINOR_EPIDX(ugenp, dev));
12477c478bd9Sstevel@tonic-gate 
12487c478bd9Sstevel@tonic-gate 	switch (minor_node_type) {
12497c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_EP_XFER_NODE:
12507c478bd9Sstevel@tonic-gate 		epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
12517c478bd9Sstevel@tonic-gate 		switch (UGEN_XFER_TYPE(epp)) {
12527c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_CONTROL:
12537c478bd9Sstevel@tonic-gate 			/* read and write must be set, ndelay not allowed */
12547c478bd9Sstevel@tonic-gate 			if (((flag & (FREAD | FWRITE)) != (FREAD | FWRITE)) ||
12557c478bd9Sstevel@tonic-gate 			    (flag & (FNDELAY | FNONBLOCK))) {
12567c478bd9Sstevel@tonic-gate 				rval = EACCES;
12577c478bd9Sstevel@tonic-gate 			}
12587c478bd9Sstevel@tonic-gate 
12597c478bd9Sstevel@tonic-gate 			break;
12607c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_BULK:
12617c478bd9Sstevel@tonic-gate 			/* ndelay not allowed */
12627c478bd9Sstevel@tonic-gate 			if (flag & (FNDELAY | FNONBLOCK)) {
12637c478bd9Sstevel@tonic-gate 				rval = EACCES;
12647c478bd9Sstevel@tonic-gate 
12657c478bd9Sstevel@tonic-gate 				break;
12667c478bd9Sstevel@tonic-gate 			}
12677c478bd9Sstevel@tonic-gate 			/*FALLTHRU*/
12687c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_ISOCH:
12697c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_INTR:
12707c478bd9Sstevel@tonic-gate 			/* check flag versus direction */
12717c478bd9Sstevel@tonic-gate 			if ((flag & FWRITE) &&
12727c478bd9Sstevel@tonic-gate 			    (UGEN_XFER_DIR(epp) & USB_EP_DIR_IN)) {
12737c478bd9Sstevel@tonic-gate 				rval = EACCES;
12747c478bd9Sstevel@tonic-gate 			}
12757c478bd9Sstevel@tonic-gate 			if ((flag & FREAD) &&
12767c478bd9Sstevel@tonic-gate 			    ((UGEN_XFER_DIR(epp) & USB_EP_DIR_IN) == 0)) {
12777c478bd9Sstevel@tonic-gate 				rval = EACCES;
12787c478bd9Sstevel@tonic-gate 			}
12797c478bd9Sstevel@tonic-gate 
12807c478bd9Sstevel@tonic-gate 			break;
12817c478bd9Sstevel@tonic-gate 		default:
12827c478bd9Sstevel@tonic-gate 			rval = EINVAL;
12837c478bd9Sstevel@tonic-gate 
12847c478bd9Sstevel@tonic-gate 			break;
12857c478bd9Sstevel@tonic-gate 		}
12867c478bd9Sstevel@tonic-gate 		break;
12877c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_DEV_STAT_NODE:
12887c478bd9Sstevel@tonic-gate 		/* only reads are supported */
12897c478bd9Sstevel@tonic-gate 		if (flag & FWRITE) {
12907c478bd9Sstevel@tonic-gate 			rval = EACCES;
12917c478bd9Sstevel@tonic-gate 		}
12927c478bd9Sstevel@tonic-gate 
12937c478bd9Sstevel@tonic-gate 		break;
12947c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_EP_STAT_NODE:
12957c478bd9Sstevel@tonic-gate 
12967c478bd9Sstevel@tonic-gate 		break;
12977c478bd9Sstevel@tonic-gate 	default:
12987c478bd9Sstevel@tonic-gate 		rval = EINVAL;
12997c478bd9Sstevel@tonic-gate 
13007c478bd9Sstevel@tonic-gate 		break;
13017c478bd9Sstevel@tonic-gate 	}
13027c478bd9Sstevel@tonic-gate 
13037c478bd9Sstevel@tonic-gate 	return (rval);
13047c478bd9Sstevel@tonic-gate }
13057c478bd9Sstevel@tonic-gate 
13067c478bd9Sstevel@tonic-gate 
13077c478bd9Sstevel@tonic-gate /*
13087c478bd9Sstevel@tonic-gate  * endpoint management
13097c478bd9Sstevel@tonic-gate  *
13107c478bd9Sstevel@tonic-gate  * create/initialize all endpoint xfer/stat structures
13117c478bd9Sstevel@tonic-gate  */
13127c478bd9Sstevel@tonic-gate static int
13137c478bd9Sstevel@tonic-gate ugen_epxs_init(ugen_state_t *ugenp)
13147c478bd9Sstevel@tonic-gate {
13157c478bd9Sstevel@tonic-gate 	usb_cfg_data_t	*dev_cfg = ugenp->ug_dev_data->dev_cfg;
13167c478bd9Sstevel@tonic-gate 	uchar_t		cfgidx, cfgval, iface, alt, ep;
13177c478bd9Sstevel@tonic-gate 	usb_if_data_t	*if_data;
13187c478bd9Sstevel@tonic-gate 	usb_alt_if_data_t *alt_if_data;
13197c478bd9Sstevel@tonic-gate 	usb_ep_data_t	*ep_data;
13207c478bd9Sstevel@tonic-gate 
13217c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
13227c478bd9Sstevel@tonic-gate 	    "ugen_epxs_init:");
13237c478bd9Sstevel@tonic-gate 
13247c478bd9Sstevel@tonic-gate 	/* initialize each ep's mutex first */
13257c478bd9Sstevel@tonic-gate 	for (ep = 0; ep < UGEN_N_ENDPOINTS; ep++) {
13267c478bd9Sstevel@tonic-gate 		mutex_init(&ugenp->ug_ep[ep].ep_mutex, NULL, MUTEX_DRIVER,
13277c478bd9Sstevel@tonic-gate 		    ugenp->ug_dev_data->dev_iblock_cookie);
13287c478bd9Sstevel@tonic-gate 	}
13297c478bd9Sstevel@tonic-gate 
13307c478bd9Sstevel@tonic-gate 	/* init default ep as it does not have a descriptor */
13317c478bd9Sstevel@tonic-gate 	if (ugen_epxs_data_init(ugenp, NULL, 0, 0,
13327c478bd9Sstevel@tonic-gate 	    ugenp->ug_dev_data->dev_curr_if, 0) != USB_SUCCESS) {
13337c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
13347c478bd9Sstevel@tonic-gate 		    "creating default endpoint failed");
13357c478bd9Sstevel@tonic-gate 
13367c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
13377c478bd9Sstevel@tonic-gate 	}
13387c478bd9Sstevel@tonic-gate 
13397c478bd9Sstevel@tonic-gate 	/*
13407c478bd9Sstevel@tonic-gate 	 * walk all endpoints of all alternates of all interfaces of
13417c478bd9Sstevel@tonic-gate 	 * all cfs
13427c478bd9Sstevel@tonic-gate 	 */
13437c478bd9Sstevel@tonic-gate 	for (cfgidx = 0; cfgidx < ugenp->ug_dev_data->dev_n_cfg; cfgidx++) {
13447c478bd9Sstevel@tonic-gate 		dev_cfg = &ugenp->ug_dev_data->dev_cfg[cfgidx];
13457c478bd9Sstevel@tonic-gate 		cfgval = dev_cfg->cfg_descr.bConfigurationValue;
13467c478bd9Sstevel@tonic-gate 		for (iface = 0; iface < dev_cfg->cfg_n_if; iface++) {
13477c478bd9Sstevel@tonic-gate 			if_data = &dev_cfg->cfg_if[iface];
13487c478bd9Sstevel@tonic-gate 			for (alt = 0; alt < if_data->if_n_alt; alt++) {
13497c478bd9Sstevel@tonic-gate 				alt_if_data = &if_data->if_alt[alt];
13507c478bd9Sstevel@tonic-gate 				for (ep = 0; ep < alt_if_data->altif_n_ep;
13517c478bd9Sstevel@tonic-gate 				    ep++) {
13527c478bd9Sstevel@tonic-gate 					ep_data = &alt_if_data->altif_ep[ep];
13537c478bd9Sstevel@tonic-gate 					if (ugen_epxs_data_init(ugenp, ep_data,
13547c478bd9Sstevel@tonic-gate 					    cfgval, cfgidx, iface, alt) !=
13557c478bd9Sstevel@tonic-gate 					    USB_SUCCESS) {
13567c478bd9Sstevel@tonic-gate 
13577c478bd9Sstevel@tonic-gate 						return (USB_FAILURE);
13587c478bd9Sstevel@tonic-gate 					}
13597c478bd9Sstevel@tonic-gate 				}
13607c478bd9Sstevel@tonic-gate 			}
13617c478bd9Sstevel@tonic-gate 		}
13627c478bd9Sstevel@tonic-gate 	}
13637c478bd9Sstevel@tonic-gate 
13647c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
13657c478bd9Sstevel@tonic-gate }
13667c478bd9Sstevel@tonic-gate 
13677c478bd9Sstevel@tonic-gate 
13687c478bd9Sstevel@tonic-gate /*
13697c478bd9Sstevel@tonic-gate  * initialize one endpoint structure
13707c478bd9Sstevel@tonic-gate  */
13717c478bd9Sstevel@tonic-gate static int
13727c478bd9Sstevel@tonic-gate ugen_epxs_data_init(ugen_state_t *ugenp, usb_ep_data_t *ep_data,
13737c478bd9Sstevel@tonic-gate 	uchar_t cfgval, uchar_t cfgidx, uchar_t iface, uchar_t alt)
13747c478bd9Sstevel@tonic-gate {
13757c478bd9Sstevel@tonic-gate 	int			ep_index;
13767c478bd9Sstevel@tonic-gate 	ugen_ep_t		*epp;
13777c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*ep_descr;
13787c478bd9Sstevel@tonic-gate 
13797c478bd9Sstevel@tonic-gate 	/* is this the default endpoint */
13807c478bd9Sstevel@tonic-gate 	ep_index = (ep_data == NULL) ? 0 :
13817c478bd9Sstevel@tonic-gate 		    usb_get_ep_index(ep_data->ep_descr.bEndpointAddress);
13827c478bd9Sstevel@tonic-gate 	epp = &ugenp->ug_ep[ep_index];
13837c478bd9Sstevel@tonic-gate 
13847c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
13857c478bd9Sstevel@tonic-gate 	    "ugen_epxs_data_init: "
13867c478bd9Sstevel@tonic-gate 	    "cfgval=%d cfgidx=%d iface=%d alt=%d ep_index=%d",
13877c478bd9Sstevel@tonic-gate 	    cfgval, cfgidx, iface, alt, ep_index);
13887c478bd9Sstevel@tonic-gate 
13897c478bd9Sstevel@tonic-gate 	ep_descr = (ep_data == NULL) ? &ugen_default_ep_descr :
13907c478bd9Sstevel@tonic-gate 						&ep_data->ep_descr;
13917c478bd9Sstevel@tonic-gate 
13927c478bd9Sstevel@tonic-gate 	mutex_init(&epp->ep_mutex, NULL, MUTEX_DRIVER,
13937c478bd9Sstevel@tonic-gate 		    ugenp->ug_dev_data->dev_iblock_cookie);
13947c478bd9Sstevel@tonic-gate 
13957c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
13967c478bd9Sstevel@tonic-gate 
13977c478bd9Sstevel@tonic-gate 	/* initialize if not yet init'ed */
13987c478bd9Sstevel@tonic-gate 	if (epp->ep_state == UGEN_EP_STATE_NONE) {
13997c478bd9Sstevel@tonic-gate 		epp->ep_descr		= *ep_descr;
14007c478bd9Sstevel@tonic-gate 		epp->ep_cfgidx		= cfgidx;
14017c478bd9Sstevel@tonic-gate 		epp->ep_if		= iface;
14027c478bd9Sstevel@tonic-gate 		epp->ep_alt		= alt;
14037c478bd9Sstevel@tonic-gate 		epp->ep_state		= UGEN_EP_STATE_ACTIVE;
14047c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status	= USB_LC_STAT_NOERROR;
14057c478bd9Sstevel@tonic-gate 		epp->ep_pipe_policy.pp_max_async_reqs = 1;
14067c478bd9Sstevel@tonic-gate 
14077c478bd9Sstevel@tonic-gate 		cv_init(&epp->ep_wait_cv, NULL, CV_DRIVER, NULL);
14087c478bd9Sstevel@tonic-gate 		epp->ep_ser_cookie	= usb_init_serialization(
14097c478bd9Sstevel@tonic-gate 						ugenp->ug_dip, 0);
14107c478bd9Sstevel@tonic-gate 	}
14117c478bd9Sstevel@tonic-gate 
14127c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
14137c478bd9Sstevel@tonic-gate 
14147c478bd9Sstevel@tonic-gate 	/* create minor nodes for all alts */
14157c478bd9Sstevel@tonic-gate 
14167c478bd9Sstevel@tonic-gate 	return (ugen_epxs_minor_nodes_create(ugenp, ep_descr,
14177c478bd9Sstevel@tonic-gate 	    cfgval, cfgidx, iface, alt));
14187c478bd9Sstevel@tonic-gate }
14197c478bd9Sstevel@tonic-gate 
14207c478bd9Sstevel@tonic-gate 
14217c478bd9Sstevel@tonic-gate /*
14227c478bd9Sstevel@tonic-gate  * undo all endpoint initializations
14237c478bd9Sstevel@tonic-gate  */
14247c478bd9Sstevel@tonic-gate static void
14257c478bd9Sstevel@tonic-gate ugen_epxs_destroy(ugen_state_t *ugenp)
14267c478bd9Sstevel@tonic-gate {
14277c478bd9Sstevel@tonic-gate 	int	i;
14287c478bd9Sstevel@tonic-gate 
14297c478bd9Sstevel@tonic-gate 	for (i = 0; i < UGEN_N_ENDPOINTS; i++) {
14307c478bd9Sstevel@tonic-gate 		ugen_epxs_data_destroy(ugenp, &ugenp->ug_ep[i]);
14317c478bd9Sstevel@tonic-gate 	}
14327c478bd9Sstevel@tonic-gate }
14337c478bd9Sstevel@tonic-gate 
14347c478bd9Sstevel@tonic-gate 
14357c478bd9Sstevel@tonic-gate static void
14367c478bd9Sstevel@tonic-gate ugen_epxs_data_destroy(ugen_state_t *ugenp, ugen_ep_t *epp)
14377c478bd9Sstevel@tonic-gate {
14387c478bd9Sstevel@tonic-gate 	if (epp) {
14397c478bd9Sstevel@tonic-gate 		ASSERT(epp->ep_ph == NULL);
14407c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
14417c478bd9Sstevel@tonic-gate 		if (epp->ep_state != UGEN_EP_STATE_NONE) {
14427c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
14437c478bd9Sstevel@tonic-gate 			    "ugen_epxs_destroy: addr=0x%x",
14447c478bd9Sstevel@tonic-gate 			    UGEN_XFER_ADDR(epp));
14457c478bd9Sstevel@tonic-gate 			cv_destroy(&epp->ep_wait_cv);
14467c478bd9Sstevel@tonic-gate 		}
14477c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
14487c478bd9Sstevel@tonic-gate 
14497c478bd9Sstevel@tonic-gate 		mutex_destroy(&epp->ep_mutex);
14507c478bd9Sstevel@tonic-gate 		usb_fini_serialization(epp->ep_ser_cookie);
14517c478bd9Sstevel@tonic-gate 	}
14527c478bd9Sstevel@tonic-gate }
14537c478bd9Sstevel@tonic-gate 
14547c478bd9Sstevel@tonic-gate 
14557c478bd9Sstevel@tonic-gate /*
14567c478bd9Sstevel@tonic-gate  * create endpoint status and xfer minor nodes
14577c478bd9Sstevel@tonic-gate  *
14587c478bd9Sstevel@tonic-gate  * The actual minor node needs more than 18 bits. We create a table
14597c478bd9Sstevel@tonic-gate  * and store the full minor node in this table and use the
14607c478bd9Sstevel@tonic-gate  * index in the table as minor node. This allows 256 minor nodes
14617c478bd9Sstevel@tonic-gate  * and 1024 instances
14627c478bd9Sstevel@tonic-gate  */
14637c478bd9Sstevel@tonic-gate static int
14647c478bd9Sstevel@tonic-gate ugen_epxs_minor_nodes_create(ugen_state_t *ugenp, usb_ep_descr_t *ep_descr,
14657c478bd9Sstevel@tonic-gate     uchar_t cfgval, uchar_t cfgidx, uchar_t iface, uchar_t alt)
14667c478bd9Sstevel@tonic-gate {
14677c478bd9Sstevel@tonic-gate 	char		node_name[32], *type;
14687c478bd9Sstevel@tonic-gate 	int		vid = ugenp->ug_dev_data->dev_descr->idVendor;
14697c478bd9Sstevel@tonic-gate 	int		pid = ugenp->ug_dev_data->dev_descr->idProduct;
14707c478bd9Sstevel@tonic-gate 	minor_t		minor;
14717c478bd9Sstevel@tonic-gate 	int		minor_index;
14727c478bd9Sstevel@tonic-gate 	ugen_minor_t	minor_code, minor_code_base;
14737c478bd9Sstevel@tonic-gate 	int		owns_device = (usb_owns_device(ugenp->ug_dip) ?
14747c478bd9Sstevel@tonic-gate 						    UGEN_OWNS_DEVICE : 0);
14757c478bd9Sstevel@tonic-gate 	int		ep_index =
14767c478bd9Sstevel@tonic-gate 			    usb_get_ep_index(ep_descr->bEndpointAddress);
14777c478bd9Sstevel@tonic-gate 	int		ep_addr =
14787c478bd9Sstevel@tonic-gate 			    ep_descr->bEndpointAddress & USB_EP_NUM_MASK;
14797c478bd9Sstevel@tonic-gate 	int		ep_type =
14807c478bd9Sstevel@tonic-gate 			    ep_descr->bmAttributes & USB_EP_ATTR_MASK;
14817c478bd9Sstevel@tonic-gate 	int		ep_dir =
14827c478bd9Sstevel@tonic-gate 			    ep_descr->bEndpointAddress & USB_EP_DIR_IN;
14837c478bd9Sstevel@tonic-gate 
14847c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
14857c478bd9Sstevel@tonic-gate 	    "ugen_epxs_minor_nodes_create: "
14867c478bd9Sstevel@tonic-gate 	    "cfgval=%d cfgidx=%d if=%d alt=%d ep=0x%x",
14877c478bd9Sstevel@tonic-gate 	    cfgval, cfgidx, iface, alt, ep_addr);
14887c478bd9Sstevel@tonic-gate 
14897c478bd9Sstevel@tonic-gate 	if (ugenp->ug_instance >= UGEN_MINOR_INSTANCE_LIMIT(ugenp)) {
14907c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L0(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
14917c478bd9Sstevel@tonic-gate 		    "instance number too high (%d)", ugenp->ug_instance);
14927c478bd9Sstevel@tonic-gate 
14937c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
14947c478bd9Sstevel@tonic-gate 	}
14957c478bd9Sstevel@tonic-gate 
14967c478bd9Sstevel@tonic-gate 	/* create stat and xfer minor node */
14977c478bd9Sstevel@tonic-gate 	minor_code_base =
14987c478bd9Sstevel@tonic-gate 		((ugen_minor_t)cfgval) << UGEN_MINOR_CFGVAL_SHIFT |
14997c478bd9Sstevel@tonic-gate 		((ugen_minor_t)cfgidx) << UGEN_MINOR_CFGIDX_SHIFT |
15007c478bd9Sstevel@tonic-gate 		iface << UGEN_MINOR_IF_SHIFT |
15017c478bd9Sstevel@tonic-gate 		alt << UGEN_MINOR_ALT_SHIFT |
15027c478bd9Sstevel@tonic-gate 		ep_index << UGEN_MINOR_EPIDX_SHIFT | owns_device;
15037c478bd9Sstevel@tonic-gate 	minor_code = minor_code_base | UGEN_MINOR_EP_XFER_NODE;
15047c478bd9Sstevel@tonic-gate 
15057c478bd9Sstevel@tonic-gate 	minor_index = ugen_minor_index_create(ugenp, minor_code);
15067c478bd9Sstevel@tonic-gate 	if (minor_index < 0) {
15077c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L1(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
15087c478bd9Sstevel@tonic-gate 		    "too many minor nodes, "
15097c478bd9Sstevel@tonic-gate 		    "cannot create %d.%d.%d.%x",
15107c478bd9Sstevel@tonic-gate 		    cfgval, iface, alt, ep_addr);
15117c478bd9Sstevel@tonic-gate 		/* carry on regardless */
15127c478bd9Sstevel@tonic-gate 
15137c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
15147c478bd9Sstevel@tonic-gate 	}
15157c478bd9Sstevel@tonic-gate 	minor = (minor_index << UGEN_MINOR_IDX_SHIFT(ugenp)) |
15167c478bd9Sstevel@tonic-gate 		ugenp->ug_instance << UGEN_MINOR_INSTANCE_SHIFT(ugenp);
15177c478bd9Sstevel@tonic-gate 
15187c478bd9Sstevel@tonic-gate 	if (ep_type == USB_EP_ATTR_CONTROL) {
15197c478bd9Sstevel@tonic-gate 		type = "cntrl";
15207c478bd9Sstevel@tonic-gate 	} else {
15217c478bd9Sstevel@tonic-gate 		type = (ep_dir & USB_EP_DIR_IN) ? "in" : "out";
15227c478bd9Sstevel@tonic-gate 	}
15237c478bd9Sstevel@tonic-gate 
15247c478bd9Sstevel@tonic-gate 	/*
15257c478bd9Sstevel@tonic-gate 	 * xfer ep node name:
15267c478bd9Sstevel@tonic-gate 	 * vid.pid.[in|out|cntrl].[<cfg>.][if<iface>.][<alt>.]<ep addr>
15277c478bd9Sstevel@tonic-gate 	 */
15287c478bd9Sstevel@tonic-gate 	if ((ep_addr == 0) && owns_device) {
15297c478bd9Sstevel@tonic-gate 		(void) sprintf(node_name, "%x.%x.%s%d",
15307c478bd9Sstevel@tonic-gate 		    vid, pid, type, ep_addr);
15317c478bd9Sstevel@tonic-gate 	} else if (cfgidx == 0 && alt == 0) {
15327c478bd9Sstevel@tonic-gate 		(void) sprintf(node_name, "%x.%x.if%d%s%d",
15337c478bd9Sstevel@tonic-gate 		    vid, pid, iface, type, ep_addr);
15347c478bd9Sstevel@tonic-gate 	} else if (cfgidx == 0 && alt != 0) {
15357c478bd9Sstevel@tonic-gate 		(void) sprintf(node_name, "%x.%x.if%d.%d%s%d",
15367c478bd9Sstevel@tonic-gate 		    vid, pid, iface, alt, type, ep_addr);
15377c478bd9Sstevel@tonic-gate 	} else if (cfgidx != 0 && alt == 0) {
15387c478bd9Sstevel@tonic-gate 		(void) sprintf(node_name, "%x.%x.cfg%dif%d%s%d",
15397c478bd9Sstevel@tonic-gate 		    vid, pid, cfgval, iface, type, ep_addr);
15407c478bd9Sstevel@tonic-gate 	} else if (cfgidx != 0 && alt != 0) {
15417c478bd9Sstevel@tonic-gate 		(void) sprintf(node_name, "%x.%x.cfg%dif%d.%d%s%d",
15427c478bd9Sstevel@tonic-gate 		    vid, pid, cfgval, iface, alt,
15437c478bd9Sstevel@tonic-gate 		    type, ep_addr);
15447c478bd9Sstevel@tonic-gate 	}
15457c478bd9Sstevel@tonic-gate 
15467c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
15477c478bd9Sstevel@tonic-gate 	    "minor=0x%x index=%d code=0x%" PRIx64 " name=%s",
15487c478bd9Sstevel@tonic-gate 	    minor, minor_index, minor_code, node_name);
15497c478bd9Sstevel@tonic-gate 
15507c478bd9Sstevel@tonic-gate 	ASSERT(minor < L_MAXMIN);
15517c478bd9Sstevel@tonic-gate 
15527c478bd9Sstevel@tonic-gate 	if ((ddi_create_minor_node(ugenp->ug_dip, node_name,
15537c478bd9Sstevel@tonic-gate 	    S_IFCHR, minor, DDI_NT_UGEN, 0)) != DDI_SUCCESS) {
15547c478bd9Sstevel@tonic-gate 
15557c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
15567c478bd9Sstevel@tonic-gate 	}
15577c478bd9Sstevel@tonic-gate 
15587c478bd9Sstevel@tonic-gate 	ugen_store_devt(ugenp, minor);
15597c478bd9Sstevel@tonic-gate 
15607c478bd9Sstevel@tonic-gate 	minor_code = minor_code_base | UGEN_MINOR_EP_STAT_NODE;
15617c478bd9Sstevel@tonic-gate 	minor_index = ugen_minor_index_create(ugenp, minor_code);
15627c478bd9Sstevel@tonic-gate 	if (minor_index < 0) {
15637c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L1(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
15647c478bd9Sstevel@tonic-gate 		    "too many minor nodes, "
15657c478bd9Sstevel@tonic-gate 		    "cannot create %d.%d.%d.%x stat",
15667c478bd9Sstevel@tonic-gate 		    cfgval, iface, alt,
15677c478bd9Sstevel@tonic-gate 		    ep_descr->bEndpointAddress);
15687c478bd9Sstevel@tonic-gate 		/* carry on regardless */
15697c478bd9Sstevel@tonic-gate 
15707c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
15717c478bd9Sstevel@tonic-gate 	}
15727c478bd9Sstevel@tonic-gate 	minor = (minor_index << UGEN_MINOR_IDX_SHIFT(ugenp)) |
15737c478bd9Sstevel@tonic-gate 		ugenp->ug_instance << UGEN_MINOR_INSTANCE_SHIFT(ugenp);
15747c478bd9Sstevel@tonic-gate 
15757c478bd9Sstevel@tonic-gate 	(void) strcat(node_name, "stat");
15767c478bd9Sstevel@tonic-gate 
15777c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
15787c478bd9Sstevel@tonic-gate 	    "minor=0x%x index=%d code=0x%" PRIx64 " name=%s",
15797c478bd9Sstevel@tonic-gate 	    minor, minor_index, minor_code, node_name);
15807c478bd9Sstevel@tonic-gate 
15817c478bd9Sstevel@tonic-gate 	ASSERT(minor < L_MAXMIN);
15827c478bd9Sstevel@tonic-gate 
15837c478bd9Sstevel@tonic-gate 	if ((ddi_create_minor_node(ugenp->ug_dip, node_name,
15847c478bd9Sstevel@tonic-gate 	    S_IFCHR, minor, DDI_NT_UGEN, 0)) != DDI_SUCCESS) {
15857c478bd9Sstevel@tonic-gate 
15867c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
15877c478bd9Sstevel@tonic-gate 	}
15887c478bd9Sstevel@tonic-gate 
15897c478bd9Sstevel@tonic-gate 	ugen_store_devt(ugenp, minor);
15907c478bd9Sstevel@tonic-gate 
15917c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
15927c478bd9Sstevel@tonic-gate }
15937c478bd9Sstevel@tonic-gate 
15947c478bd9Sstevel@tonic-gate 
15957c478bd9Sstevel@tonic-gate /*
15967c478bd9Sstevel@tonic-gate  * close all non-default pipes and drain default pipe
15977c478bd9Sstevel@tonic-gate  */
15987c478bd9Sstevel@tonic-gate static void
15997c478bd9Sstevel@tonic-gate ugen_epx_shutdown(ugen_state_t *ugenp)
16007c478bd9Sstevel@tonic-gate {
16017c478bd9Sstevel@tonic-gate 	int	i;
16027c478bd9Sstevel@tonic-gate 
16037c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
16047c478bd9Sstevel@tonic-gate 	    "ugen_epx_shutdown:");
16057c478bd9Sstevel@tonic-gate 
16067c478bd9Sstevel@tonic-gate 	for (i = 0; i < UGEN_N_ENDPOINTS; i++) {
16077c478bd9Sstevel@tonic-gate 		ugen_ep_t *epp = &ugenp->ug_ep[i];
16087c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
16097c478bd9Sstevel@tonic-gate 		if (epp->ep_state != UGEN_EP_STATE_NONE) {
16107c478bd9Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
16117c478bd9Sstevel@tonic-gate 			(void) usb_serialize_access(epp->ep_ser_cookie,
16127c478bd9Sstevel@tonic-gate 							USB_WAIT, 0);
16137c478bd9Sstevel@tonic-gate 			(void) ugen_epx_close_pipe(ugenp, epp);
16147c478bd9Sstevel@tonic-gate 			usb_release_access(epp->ep_ser_cookie);
16157c478bd9Sstevel@tonic-gate 		} else {
16167c478bd9Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
16177c478bd9Sstevel@tonic-gate 		}
16187c478bd9Sstevel@tonic-gate 	}
16197c478bd9Sstevel@tonic-gate }
16207c478bd9Sstevel@tonic-gate 
16217c478bd9Sstevel@tonic-gate 
16227c478bd9Sstevel@tonic-gate /*
16237c478bd9Sstevel@tonic-gate  * find cfg index corresponding to cfg value
16247c478bd9Sstevel@tonic-gate  */
16257c478bd9Sstevel@tonic-gate static int
16267c478bd9Sstevel@tonic-gate ugen_cfgval2idx(ugen_state_t *ugenp, uint_t cfgval)
16277c478bd9Sstevel@tonic-gate {
16287c478bd9Sstevel@tonic-gate 	usb_cfg_data_t	*dev_cfg = ugenp->ug_dev_data->dev_cfg;
16297c478bd9Sstevel@tonic-gate 	int		cfgidx;
16307c478bd9Sstevel@tonic-gate 
16317c478bd9Sstevel@tonic-gate 	for (cfgidx = 0; cfgidx < ugenp->ug_dev_data->dev_n_cfg; cfgidx++) {
16327c478bd9Sstevel@tonic-gate 		dev_cfg = &ugenp->ug_dev_data->dev_cfg[cfgidx];
16337c478bd9Sstevel@tonic-gate 		if (cfgval == dev_cfg->cfg_descr.bConfigurationValue) {
16347c478bd9Sstevel@tonic-gate 
16357c478bd9Sstevel@tonic-gate 			return (cfgidx);
16367c478bd9Sstevel@tonic-gate 		}
16377c478bd9Sstevel@tonic-gate 	}
16387c478bd9Sstevel@tonic-gate 
16397c478bd9Sstevel@tonic-gate 	ASSERT(cfgidx < ugenp->ug_dev_data->dev_n_cfg);
16407c478bd9Sstevel@tonic-gate 
16417c478bd9Sstevel@tonic-gate 	return (0);
16427c478bd9Sstevel@tonic-gate }
16437c478bd9Sstevel@tonic-gate 
16447c478bd9Sstevel@tonic-gate 
16457c478bd9Sstevel@tonic-gate /*
16467c478bd9Sstevel@tonic-gate  * check if any node is open
16477c478bd9Sstevel@tonic-gate  */
16487c478bd9Sstevel@tonic-gate static int
16497c478bd9Sstevel@tonic-gate ugen_epxs_check_open_nodes(ugen_state_t *ugenp)
16507c478bd9Sstevel@tonic-gate {
16517c478bd9Sstevel@tonic-gate 	int	i;
16527c478bd9Sstevel@tonic-gate 
16537c478bd9Sstevel@tonic-gate 	for (i = 1; i < UGEN_N_ENDPOINTS; i++) {
16547c478bd9Sstevel@tonic-gate 		ugen_ep_t *epp = &ugenp->ug_ep[i];
16557c478bd9Sstevel@tonic-gate 
16567c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
16577c478bd9Sstevel@tonic-gate 
16587c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
16597c478bd9Sstevel@tonic-gate 		    "ugen_epxs_check_open_nodes: epp=%d, ep_state=0x%x",
16607c478bd9Sstevel@tonic-gate 		    i, epp->ep_state);
16617c478bd9Sstevel@tonic-gate 
16627c478bd9Sstevel@tonic-gate 		if (epp->ep_state & UGEN_EP_STATE_XS_OPEN) {
16637c478bd9Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
16647c478bd9Sstevel@tonic-gate 
16657c478bd9Sstevel@tonic-gate 			return (USB_SUCCESS);
16667c478bd9Sstevel@tonic-gate 		}
16677c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
16687c478bd9Sstevel@tonic-gate 	}
16697c478bd9Sstevel@tonic-gate 
16707c478bd9Sstevel@tonic-gate 	return (USB_FAILURE);
16717c478bd9Sstevel@tonic-gate }
16727c478bd9Sstevel@tonic-gate 
16737c478bd9Sstevel@tonic-gate 
16747c478bd9Sstevel@tonic-gate /*
16757c478bd9Sstevel@tonic-gate  * check if we can switch alternate
16767c478bd9Sstevel@tonic-gate  */
16777c478bd9Sstevel@tonic-gate static int
16787c478bd9Sstevel@tonic-gate ugen_epxs_check_alt_switch(ugen_state_t *ugenp, uchar_t iface, uchar_t cfgidx)
16797c478bd9Sstevel@tonic-gate {
16807c478bd9Sstevel@tonic-gate 	int	i;
16817c478bd9Sstevel@tonic-gate 
16827c478bd9Sstevel@tonic-gate 	for (i = 1; i < UGEN_N_ENDPOINTS; i++) {
16837c478bd9Sstevel@tonic-gate 		ugen_ep_t *epp = &ugenp->ug_ep[i];
16847c478bd9Sstevel@tonic-gate 
16857c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
16867c478bd9Sstevel@tonic-gate 
16877c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
16887c478bd9Sstevel@tonic-gate 		    "ugen_epxs_check_alt_switch: epp=%d, ep_state=0x%x",
16897c478bd9Sstevel@tonic-gate 		    i, epp->ep_state);
16907c478bd9Sstevel@tonic-gate 
16917c478bd9Sstevel@tonic-gate 		/*
16927c478bd9Sstevel@tonic-gate 		 * if the endpoint is open and part of this cfg and interface
16937c478bd9Sstevel@tonic-gate 		 * then we cannot switch alternates
16947c478bd9Sstevel@tonic-gate 		 */
16957c478bd9Sstevel@tonic-gate 		if ((epp->ep_state & UGEN_EP_STATE_XS_OPEN) &&
16967c478bd9Sstevel@tonic-gate 		    (epp->ep_cfgidx == cfgidx) &&
16977c478bd9Sstevel@tonic-gate 		    (epp->ep_if == iface)) {
16987c478bd9Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
16997c478bd9Sstevel@tonic-gate 
17007c478bd9Sstevel@tonic-gate 			return (USB_FAILURE);
17017c478bd9Sstevel@tonic-gate 		}
17027c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
17037c478bd9Sstevel@tonic-gate 	}
17047c478bd9Sstevel@tonic-gate 
17057c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
17067c478bd9Sstevel@tonic-gate }
17077c478bd9Sstevel@tonic-gate 
17087c478bd9Sstevel@tonic-gate 
17097c478bd9Sstevel@tonic-gate /*
17107c478bd9Sstevel@tonic-gate  * implicit switch to new cfg and alt
17117c478bd9Sstevel@tonic-gate  * If a crummy device fails usb_get_cfg or usb_get_alt_if, we carry on
17127c478bd9Sstevel@tonic-gate  * regardless so at least the device can be opened.
17137c478bd9Sstevel@tonic-gate  */
17147c478bd9Sstevel@tonic-gate static int
17157c478bd9Sstevel@tonic-gate ugen_epxs_switch_cfg_alt(ugen_state_t *ugenp, ugen_ep_t *epp, dev_t dev)
17167c478bd9Sstevel@tonic-gate {
17177c478bd9Sstevel@tonic-gate 	int	rval = USB_SUCCESS;
17187c478bd9Sstevel@tonic-gate 	uint_t	alt;
17197c478bd9Sstevel@tonic-gate 	uint_t	new_alt = UGEN_MINOR_ALT(ugenp, dev);
17207c478bd9Sstevel@tonic-gate 	uint_t	new_if = UGEN_MINOR_IF(ugenp, dev);
17217c478bd9Sstevel@tonic-gate 	uint_t	cur_if = epp->ep_if;
17227c478bd9Sstevel@tonic-gate 	uint_t	new_cfgidx = UGEN_MINOR_CFGIDX(ugenp, dev);
17237c478bd9Sstevel@tonic-gate 	uint_t	cur_cfgidx;
17247c478bd9Sstevel@tonic-gate 	uint_t	cfgval;
17257c478bd9Sstevel@tonic-gate 	int	switched = 0;
17267c478bd9Sstevel@tonic-gate 
17277c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
17287c478bd9Sstevel@tonic-gate 	    "ugen_epxs_switch_cfg_alt: old cfgidx=%d, if=%d alt=%d",
17297c478bd9Sstevel@tonic-gate 	    epp->ep_cfgidx, epp->ep_if, epp->ep_alt);
17307c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
17317c478bd9Sstevel@tonic-gate 	    "new cfgidx=%d, if=%d alt=%d ep_state=0x%x",
17327c478bd9Sstevel@tonic-gate 	    new_cfgidx, new_if, new_alt, epp->ep_state);
17337c478bd9Sstevel@tonic-gate 
17347c478bd9Sstevel@tonic-gate 	/* no need to switch if there is only 1 cfg, 1 iface and no alts */
17357c478bd9Sstevel@tonic-gate 	if ((new_if == 0) && (new_alt == 0) &&
17367c478bd9Sstevel@tonic-gate 	    (ugenp->ug_dev_data->dev_n_cfg == 1) &&
17377c478bd9Sstevel@tonic-gate 	    (ugenp->ug_dev_data->dev_cfg[0].cfg_n_if == 1) &&
17387c478bd9Sstevel@tonic-gate 	    (ugenp->ug_dev_data->
17397c478bd9Sstevel@tonic-gate 	    dev_cfg[0].cfg_if[new_if].if_n_alt == 1)) {
17407c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
17417c478bd9Sstevel@tonic-gate 		    "no need for switching: n_cfg=%d n_alt=%d",
17427c478bd9Sstevel@tonic-gate 		    ugenp->ug_dev_data->dev_n_cfg,
17437c478bd9Sstevel@tonic-gate 		    ugenp->ug_dev_data->
17447c478bd9Sstevel@tonic-gate 				dev_cfg[0].cfg_if[new_if].if_n_alt);
17457c478bd9Sstevel@tonic-gate 
17467c478bd9Sstevel@tonic-gate 		ASSERT(epp->ep_alt == new_alt);
17477c478bd9Sstevel@tonic-gate 		ASSERT(epp->ep_cfgidx == new_cfgidx);
17487c478bd9Sstevel@tonic-gate 		ASSERT(epp->ep_if == new_if);
17497c478bd9Sstevel@tonic-gate 
17507c478bd9Sstevel@tonic-gate 		return (rval);
17517c478bd9Sstevel@tonic-gate 	}
17527c478bd9Sstevel@tonic-gate 
17537c478bd9Sstevel@tonic-gate 	/* no switch for default endpoint */
17547c478bd9Sstevel@tonic-gate 	if (epp->ep_descr.bEndpointAddress == 0) {
17557c478bd9Sstevel@tonic-gate 
17567c478bd9Sstevel@tonic-gate 		return (rval);
17577c478bd9Sstevel@tonic-gate 	}
17587c478bd9Sstevel@tonic-gate 
17597c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
17607c478bd9Sstevel@tonic-gate 	if ((ugenp->ug_dev_data->dev_n_cfg > 1) &&
17617c478bd9Sstevel@tonic-gate 	    usb_get_cfg(ugenp->ug_dip, &cfgval,
17627c478bd9Sstevel@tonic-gate 	    USB_FLAGS_SLEEP) == USB_SUCCESS) {
17637c478bd9Sstevel@tonic-gate 
17647c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
17657c478bd9Sstevel@tonic-gate 
17667c478bd9Sstevel@tonic-gate 		cur_cfgidx = ugen_cfgval2idx(ugenp, cfgval);
17677c478bd9Sstevel@tonic-gate 
17687c478bd9Sstevel@tonic-gate 		if (new_cfgidx != cur_cfgidx) {
17697c478bd9Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
17707c478bd9Sstevel@tonic-gate 
17717c478bd9Sstevel@tonic-gate 			/*
17727c478bd9Sstevel@tonic-gate 			 * we can't change config if any node
17737c478bd9Sstevel@tonic-gate 			 * is open
17747c478bd9Sstevel@tonic-gate 			 */
17757c478bd9Sstevel@tonic-gate 			if (ugen_epxs_check_open_nodes(ugenp) ==
17767c478bd9Sstevel@tonic-gate 			    USB_SUCCESS) {
17777c478bd9Sstevel@tonic-gate 				mutex_enter(&epp->ep_mutex);
17787c478bd9Sstevel@tonic-gate 
17797c478bd9Sstevel@tonic-gate 				return (USB_BUSY);
17807c478bd9Sstevel@tonic-gate 			}
17817c478bd9Sstevel@tonic-gate 
17827c478bd9Sstevel@tonic-gate 			/*
17837c478bd9Sstevel@tonic-gate 			 * we are going to do this synchronously to
17847c478bd9Sstevel@tonic-gate 			 * keep it simple.
17857c478bd9Sstevel@tonic-gate 			 * This should never hang forever.
17867c478bd9Sstevel@tonic-gate 			 */
17877c478bd9Sstevel@tonic-gate 			if ((rval = usb_set_cfg(ugenp->ug_dip,
17887c478bd9Sstevel@tonic-gate 			    new_cfgidx, USB_FLAGS_SLEEP, NULL,
17897c478bd9Sstevel@tonic-gate 			    NULL)) != USB_SUCCESS) {
17907c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(UGEN_PRINT_XFER,
17917c478bd9Sstevel@tonic-gate 				    ugenp->ug_log_hdl,
17927c478bd9Sstevel@tonic-gate 				    "implicit set cfg (%" PRId64
17937c478bd9Sstevel@tonic-gate 				    ") failed (%d)",
17947c478bd9Sstevel@tonic-gate 				    UGEN_MINOR_CFGIDX(ugenp, dev), rval);
17957c478bd9Sstevel@tonic-gate 				mutex_enter(&epp->ep_mutex);
17967c478bd9Sstevel@tonic-gate 
17977c478bd9Sstevel@tonic-gate 				return (rval);
17987c478bd9Sstevel@tonic-gate 			}
17997c478bd9Sstevel@tonic-gate 			mutex_enter(&epp->ep_mutex);
18007c478bd9Sstevel@tonic-gate 			epp->ep_if = new_if;
18017c478bd9Sstevel@tonic-gate 			switched++;
18027c478bd9Sstevel@tonic-gate 		}
18037c478bd9Sstevel@tonic-gate 		epp->ep_cfgidx = new_cfgidx;
18047c478bd9Sstevel@tonic-gate 
18057c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
18067c478bd9Sstevel@tonic-gate 	}
18077c478bd9Sstevel@tonic-gate 
18087c478bd9Sstevel@tonic-gate 	/*
18097c478bd9Sstevel@tonic-gate 	 * implicitly switch to new alternate if
18107c478bd9Sstevel@tonic-gate 	 * - we have not switched configuration (if we
18117c478bd9Sstevel@tonic-gate 	 *   we switched config, the alternate must be 0)
18127c478bd9Sstevel@tonic-gate 	 * - n_alts is > 1
18137c478bd9Sstevel@tonic-gate 	 * - if the device supports get_alternate iface
18147c478bd9Sstevel@tonic-gate 	 */
18157c478bd9Sstevel@tonic-gate 	if ((switched && (new_alt > 0)) ||
18167c478bd9Sstevel@tonic-gate 	    ((ugenp->ug_dev_data->dev_cfg[new_cfgidx].
18177c478bd9Sstevel@tonic-gate 	    cfg_if[new_if].if_n_alt > 1) &&
18187c478bd9Sstevel@tonic-gate 	    (usb_get_alt_if(ugenp->ug_dip, new_if, &alt,
18197c478bd9Sstevel@tonic-gate 	    USB_FLAGS_SLEEP) == USB_SUCCESS))) {
18207c478bd9Sstevel@tonic-gate 		if (switched || (alt != new_alt)) {
18217c478bd9Sstevel@tonic-gate 			if (ugen_epxs_check_alt_switch(ugenp, cur_if,
18227c478bd9Sstevel@tonic-gate 			    new_cfgidx) != USB_SUCCESS) {
18237c478bd9Sstevel@tonic-gate 				mutex_enter(&epp->ep_mutex);
18247c478bd9Sstevel@tonic-gate 
18257c478bd9Sstevel@tonic-gate 				return (USB_BUSY);
18267c478bd9Sstevel@tonic-gate 			}
18277c478bd9Sstevel@tonic-gate 			if ((rval = usb_set_alt_if(ugenp->ug_dip, new_if,
18287c478bd9Sstevel@tonic-gate 			    new_alt, USB_FLAGS_SLEEP, NULL, NULL)) !=
18297c478bd9Sstevel@tonic-gate 			    USB_SUCCESS) {
18307c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(UGEN_PRINT_XFER,
18317c478bd9Sstevel@tonic-gate 				    ugenp->ug_log_hdl,
18327c478bd9Sstevel@tonic-gate 				    "implicit set new alternate "
18337c478bd9Sstevel@tonic-gate 				    "(%d) failed (%d)", new_alt, rval);
18347c478bd9Sstevel@tonic-gate 				mutex_enter(&epp->ep_mutex);
18357c478bd9Sstevel@tonic-gate 
18367c478bd9Sstevel@tonic-gate 				return (rval);
18377c478bd9Sstevel@tonic-gate 			}
18387c478bd9Sstevel@tonic-gate 		}
18397c478bd9Sstevel@tonic-gate 	}
18407c478bd9Sstevel@tonic-gate 
18417c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
18427c478bd9Sstevel@tonic-gate 	epp->ep_alt = new_alt;
18437c478bd9Sstevel@tonic-gate 	ugen_update_ep_descr(ugenp, epp);
18447c478bd9Sstevel@tonic-gate 
18457c478bd9Sstevel@tonic-gate 	return (rval);
18467c478bd9Sstevel@tonic-gate }
18477c478bd9Sstevel@tonic-gate 
18487c478bd9Sstevel@tonic-gate 
18497c478bd9Sstevel@tonic-gate /*
18507c478bd9Sstevel@tonic-gate  * update endpoint descriptor in ugen_ep structure after
18517c478bd9Sstevel@tonic-gate  * switching configuration or alternate
18527c478bd9Sstevel@tonic-gate  */
18537c478bd9Sstevel@tonic-gate static void
18547c478bd9Sstevel@tonic-gate ugen_update_ep_descr(ugen_state_t *ugenp, ugen_ep_t *epp)
18557c478bd9Sstevel@tonic-gate {
18567c478bd9Sstevel@tonic-gate 	usb_cfg_data_t	*dev_cfg = ugenp->ug_dev_data->dev_cfg;
18577c478bd9Sstevel@tonic-gate 	usb_if_data_t	*if_data;
18587c478bd9Sstevel@tonic-gate 	usb_alt_if_data_t *alt_if_data;
18597c478bd9Sstevel@tonic-gate 	usb_ep_data_t	*ep_data;
18607c478bd9Sstevel@tonic-gate 	int		ep;
18617c478bd9Sstevel@tonic-gate 
18627c478bd9Sstevel@tonic-gate 	dev_cfg = &ugenp->ug_dev_data->dev_cfg[epp->ep_cfgidx];
18637c478bd9Sstevel@tonic-gate 	if_data = &dev_cfg->cfg_if[epp->ep_if];
18647c478bd9Sstevel@tonic-gate 	alt_if_data = &if_data->if_alt[epp->ep_alt];
18657c478bd9Sstevel@tonic-gate 	for (ep = 0; ep < alt_if_data->altif_n_ep; ep++) {
18667c478bd9Sstevel@tonic-gate 		ep_data = &alt_if_data->altif_ep[ep];
18677c478bd9Sstevel@tonic-gate 		if (usb_get_ep_index(ep_data->ep_descr.
18687c478bd9Sstevel@tonic-gate 		    bEndpointAddress) ==
18697c478bd9Sstevel@tonic-gate 		    usb_get_ep_index(epp->ep_descr.
18707c478bd9Sstevel@tonic-gate 		    bEndpointAddress)) {
18717c478bd9Sstevel@tonic-gate 			epp->ep_descr = ep_data->ep_descr;
18727c478bd9Sstevel@tonic-gate 
18737c478bd9Sstevel@tonic-gate 			break;
18747c478bd9Sstevel@tonic-gate 		}
18757c478bd9Sstevel@tonic-gate 	}
18767c478bd9Sstevel@tonic-gate }
18777c478bd9Sstevel@tonic-gate 
18787c478bd9Sstevel@tonic-gate 
18797c478bd9Sstevel@tonic-gate /*
18807c478bd9Sstevel@tonic-gate  * Xfer endpoint management
18817c478bd9Sstevel@tonic-gate  *
18827c478bd9Sstevel@tonic-gate  * open an endpoint for xfers
18837c478bd9Sstevel@tonic-gate  *
18847c478bd9Sstevel@tonic-gate  * Return values: errno
18857c478bd9Sstevel@tonic-gate  */
18867c478bd9Sstevel@tonic-gate static int
18877c478bd9Sstevel@tonic-gate ugen_epx_open(ugen_state_t *ugenp, dev_t dev, int flag)
18887c478bd9Sstevel@tonic-gate {
18897c478bd9Sstevel@tonic-gate 	ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
18907c478bd9Sstevel@tonic-gate 	int	rval;
18917c478bd9Sstevel@tonic-gate 
18927c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
18937c478bd9Sstevel@tonic-gate 
18947c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
18957c478bd9Sstevel@tonic-gate 	    "ugen_epx_open: minor=0x%x flag=0x%x ep_state=0x%x",
18967c478bd9Sstevel@tonic-gate 	    getminor(dev), flag, epp->ep_state);
18977c478bd9Sstevel@tonic-gate 
18987c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_ACTIVE);
18997c478bd9Sstevel@tonic-gate 
19007c478bd9Sstevel@tonic-gate 	/* implicit switch to new cfg & alt */
19017c478bd9Sstevel@tonic-gate 	if ((epp->ep_state & UGEN_EP_STATE_XFER_OPEN) != 0) {
19027c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
19037c478bd9Sstevel@tonic-gate 
19047c478bd9Sstevel@tonic-gate 		return (EBUSY);
19057c478bd9Sstevel@tonic-gate 	}
19067c478bd9Sstevel@tonic-gate 	if ((rval = ugen_epxs_switch_cfg_alt(ugenp, epp, dev)) ==
19077c478bd9Sstevel@tonic-gate 	    USB_SUCCESS) {
19087c478bd9Sstevel@tonic-gate 		rval = ugen_epx_open_pipe(ugenp, epp, flag);
19097c478bd9Sstevel@tonic-gate 	}
19107c478bd9Sstevel@tonic-gate 
19117c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
19127c478bd9Sstevel@tonic-gate 	    "ugen_epx_open: state=0x%x", epp->ep_state);
19137c478bd9Sstevel@tonic-gate 
19147c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_ACTIVE);
19157c478bd9Sstevel@tonic-gate 	epp->ep_done = epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
19167c478bd9Sstevel@tonic-gate 
19177c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
19187c478bd9Sstevel@tonic-gate 
19197c478bd9Sstevel@tonic-gate 	return (usb_rval2errno(rval));
19207c478bd9Sstevel@tonic-gate }
19217c478bd9Sstevel@tonic-gate 
19227c478bd9Sstevel@tonic-gate 
19237c478bd9Sstevel@tonic-gate /*
19247c478bd9Sstevel@tonic-gate  * close an endpoint for xfers
19257c478bd9Sstevel@tonic-gate  */
19267c478bd9Sstevel@tonic-gate static void
19277c478bd9Sstevel@tonic-gate ugen_epx_close(ugen_state_t *ugenp, dev_t dev, int flag)
19287c478bd9Sstevel@tonic-gate {
19297c478bd9Sstevel@tonic-gate 	ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
19307c478bd9Sstevel@tonic-gate 
19317c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
19327c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
19337c478bd9Sstevel@tonic-gate 	    "ugen_epx_close: dev=0x%lx flag=0x%x state=0x%x", dev, flag,
19347c478bd9Sstevel@tonic-gate 	    epp->ep_state);
19357c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
19367c478bd9Sstevel@tonic-gate 
19377c478bd9Sstevel@tonic-gate 	ugen_epx_close_pipe(ugenp, epp);
19387c478bd9Sstevel@tonic-gate 
19397c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
19407c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
19417c478bd9Sstevel@tonic-gate 	    "ugen_epx_close: state=0x%x", epp->ep_state);
19427c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_ACTIVE);
19437c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_bp == NULL);
19447c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_done == 0);
19457c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_data == NULL);
19467c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
19477c478bd9Sstevel@tonic-gate }
19487c478bd9Sstevel@tonic-gate 
19497c478bd9Sstevel@tonic-gate 
19507c478bd9Sstevel@tonic-gate /*
19517c478bd9Sstevel@tonic-gate  * open pipe for this endpoint
19527c478bd9Sstevel@tonic-gate  * If the pipe is an interrupt IN pipe, start polling immediately
19537c478bd9Sstevel@tonic-gate  */
19547c478bd9Sstevel@tonic-gate static int
19557c478bd9Sstevel@tonic-gate ugen_epx_open_pipe(ugen_state_t *ugenp, ugen_ep_t *epp, int flag)
19567c478bd9Sstevel@tonic-gate {
19577c478bd9Sstevel@tonic-gate 	int rval = USB_SUCCESS;
19587c478bd9Sstevel@tonic-gate 
19597c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
19607c478bd9Sstevel@tonic-gate 	    "ugen_epx_open_pipe: epp=0x%p flag=%d state=0x%x",
19617c478bd9Sstevel@tonic-gate 	    epp, flag, epp->ep_state);
19627c478bd9Sstevel@tonic-gate 
19637c478bd9Sstevel@tonic-gate 	epp->ep_state |= UGEN_EP_STATE_XFER_OPEN;
19647c478bd9Sstevel@tonic-gate 	epp->ep_xfer_oflag = flag;
19657c478bd9Sstevel@tonic-gate 
19667c478bd9Sstevel@tonic-gate 	/* if default pipe, just copy the handle */
19677c478bd9Sstevel@tonic-gate 	if ((epp->ep_descr.bEndpointAddress & USB_EP_NUM_MASK) == 0) {
19687c478bd9Sstevel@tonic-gate 		epp->ep_ph = ugenp->ug_dev_data->dev_default_ph;
19697c478bd9Sstevel@tonic-gate 	} else {
19707c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
19717c478bd9Sstevel@tonic-gate 
19727c478bd9Sstevel@tonic-gate 		/* open pipe */
19737c478bd9Sstevel@tonic-gate 		rval = usb_pipe_open(ugenp->ug_dip,
19747c478bd9Sstevel@tonic-gate 		    &epp->ep_descr, &epp->ep_pipe_policy,
19757c478bd9Sstevel@tonic-gate 		    USB_FLAGS_SLEEP, &epp->ep_ph);
19767c478bd9Sstevel@tonic-gate 
19777c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
19787c478bd9Sstevel@tonic-gate 
19797c478bd9Sstevel@tonic-gate 		if (rval == USB_SUCCESS) {
19807c478bd9Sstevel@tonic-gate 			(void) usb_pipe_set_private(epp->ep_ph,
19817c478bd9Sstevel@tonic-gate 						(usb_opaque_t)epp);
19827c478bd9Sstevel@tonic-gate 
19837c478bd9Sstevel@tonic-gate 			/*
19847c478bd9Sstevel@tonic-gate 			 * if interrupt IN pipe, and one xfer mode
19857c478bd9Sstevel@tonic-gate 			 * has not been set, start polling immediately
19867c478bd9Sstevel@tonic-gate 			 */
19877c478bd9Sstevel@tonic-gate 			if ((UGEN_XFER_TYPE(epp) == USB_EP_ATTR_INTR) &&
19887c478bd9Sstevel@tonic-gate 			    (!(epp->ep_one_xfer)) &&
19897c478bd9Sstevel@tonic-gate 			    (UGEN_XFER_DIR(epp) == USB_EP_DIR_IN)) {
19907c478bd9Sstevel@tonic-gate 				if ((rval = ugen_epx_intr_IN_start_polling(
19917c478bd9Sstevel@tonic-gate 				    ugenp, epp)) != USB_SUCCESS) {
19927c478bd9Sstevel@tonic-gate 
19937c478bd9Sstevel@tonic-gate 					mutex_exit(&epp->ep_mutex);
19947c478bd9Sstevel@tonic-gate 					usb_pipe_close(ugenp->ug_dip,
19957c478bd9Sstevel@tonic-gate 					    epp->ep_ph, USB_FLAGS_SLEEP,
19967c478bd9Sstevel@tonic-gate 					    NULL, NULL);
19977c478bd9Sstevel@tonic-gate 					mutex_enter(&epp->ep_mutex);
19987c478bd9Sstevel@tonic-gate 
19997c478bd9Sstevel@tonic-gate 					epp->ep_ph = NULL;
20007c478bd9Sstevel@tonic-gate 				} else {
20017c478bd9Sstevel@tonic-gate 					epp->ep_state |=
20027c478bd9Sstevel@tonic-gate 					    UGEN_EP_STATE_INTR_IN_POLLING_ON;
20037c478bd9Sstevel@tonic-gate 
20047c478bd9Sstevel@tonic-gate 					/* allow for about 1 sec of data */
20057c478bd9Sstevel@tonic-gate 					epp->ep_buf_limit =
20067c478bd9Sstevel@tonic-gate 					    (1000/epp->ep_descr.bInterval) *
20077c478bd9Sstevel@tonic-gate 					    epp->ep_descr.wMaxPacketSize;
20087c478bd9Sstevel@tonic-gate 				}
20097c478bd9Sstevel@tonic-gate 			}
20107c478bd9Sstevel@tonic-gate 		}
20117c478bd9Sstevel@tonic-gate 	}
20127c478bd9Sstevel@tonic-gate 
20137c478bd9Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
20147c478bd9Sstevel@tonic-gate 		epp->ep_state &= ~(UGEN_EP_STATE_XFER_OPEN |
20157c478bd9Sstevel@tonic-gate 		    UGEN_EP_STATE_INTR_IN_POLLING_ON);
20167c478bd9Sstevel@tonic-gate 	}
20177c478bd9Sstevel@tonic-gate 
20187c478bd9Sstevel@tonic-gate 	return (rval);
20197c478bd9Sstevel@tonic-gate }
20207c478bd9Sstevel@tonic-gate 
20217c478bd9Sstevel@tonic-gate 
20227c478bd9Sstevel@tonic-gate /*
20237c478bd9Sstevel@tonic-gate  * close an endpoint pipe
20247c478bd9Sstevel@tonic-gate  */
20257c478bd9Sstevel@tonic-gate static void
20267c478bd9Sstevel@tonic-gate ugen_epx_close_pipe(ugen_state_t *ugenp, ugen_ep_t *epp)
20277c478bd9Sstevel@tonic-gate {
20287c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
20297c478bd9Sstevel@tonic-gate 	    "ugen_epx_close_pipe: epp=0x%p", epp);
20307c478bd9Sstevel@tonic-gate 
20317c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
20327c478bd9Sstevel@tonic-gate 	if (epp->ep_state & UGEN_EP_STATE_XFER_OPEN) {
20337c478bd9Sstevel@tonic-gate 		epp->ep_state &= ~(UGEN_EP_STATE_XFER_OPEN |
20347c478bd9Sstevel@tonic-gate 				UGEN_EP_STATE_INTR_IN_POLLING_IS_STOPPED |
20357c478bd9Sstevel@tonic-gate 				UGEN_EP_STATE_INTR_IN_POLLING_ON);
20367c478bd9Sstevel@tonic-gate 
20377c478bd9Sstevel@tonic-gate 		if (epp->ep_ph == ugenp->ug_dev_data->dev_default_ph) {
20387c478bd9Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
20397c478bd9Sstevel@tonic-gate 
20407c478bd9Sstevel@tonic-gate 			(void) usb_pipe_drain_reqs(ugenp->ug_dip,
20417c478bd9Sstevel@tonic-gate 			    epp->ep_ph, 0, USB_FLAGS_SLEEP,
20427c478bd9Sstevel@tonic-gate 			    NULL, NULL);
20437c478bd9Sstevel@tonic-gate 			mutex_enter(&epp->ep_mutex);
20447c478bd9Sstevel@tonic-gate 		} else {
20457c478bd9Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
20467c478bd9Sstevel@tonic-gate 			usb_pipe_close(ugenp->ug_dip,
20477c478bd9Sstevel@tonic-gate 			    epp->ep_ph, USB_FLAGS_SLEEP, NULL, NULL);
20487c478bd9Sstevel@tonic-gate 
20497c478bd9Sstevel@tonic-gate 			mutex_enter(&epp->ep_mutex);
20507c478bd9Sstevel@tonic-gate 			epp->ep_ph = NULL;
20517c478bd9Sstevel@tonic-gate 		}
20527c478bd9Sstevel@tonic-gate 
20537c478bd9Sstevel@tonic-gate 		freemsg(epp->ep_data);
20547c478bd9Sstevel@tonic-gate 		epp->ep_ph = NULL;
20557c478bd9Sstevel@tonic-gate 		epp->ep_data = NULL;
20567c478bd9Sstevel@tonic-gate 	}
20577c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_ph == NULL);
20587c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_data == NULL);
20597c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
20607c478bd9Sstevel@tonic-gate }
20617c478bd9Sstevel@tonic-gate 
20627c478bd9Sstevel@tonic-gate 
20637c478bd9Sstevel@tonic-gate /*
20647c478bd9Sstevel@tonic-gate  * start endpoint xfer
20657c478bd9Sstevel@tonic-gate  *
20667c478bd9Sstevel@tonic-gate  * We first serialize at endpoint level for only one request at the time
20677c478bd9Sstevel@tonic-gate  *
20687c478bd9Sstevel@tonic-gate  * Return values: errno
20697c478bd9Sstevel@tonic-gate  */
20707c478bd9Sstevel@tonic-gate static int
20717c478bd9Sstevel@tonic-gate ugen_epx_req(ugen_state_t *ugenp, struct buf *bp)
20727c478bd9Sstevel@tonic-gate {
20737c478bd9Sstevel@tonic-gate 	dev_t		dev = bp->b_edev;
20747c478bd9Sstevel@tonic-gate 	ugen_ep_t	*epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
20757c478bd9Sstevel@tonic-gate 	boolean_t	wait = B_FALSE;
20767c478bd9Sstevel@tonic-gate 	int		rval = 0;
20777c478bd9Sstevel@tonic-gate 
20787c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
20797c478bd9Sstevel@tonic-gate 	    "ugen_epx_req: bp=0x%p dev=0x%lx", (void *)bp, dev);
20807c478bd9Sstevel@tonic-gate 
20817c478bd9Sstevel@tonic-gate 	/* single thread per endpoint, one request at the time */
20827c478bd9Sstevel@tonic-gate 	if (usb_serialize_access(epp->ep_ser_cookie, USB_WAIT_SIG, 0) <=
20837c478bd9Sstevel@tonic-gate 	    0) {
20847c478bd9Sstevel@tonic-gate 
20857c478bd9Sstevel@tonic-gate 		return (EINTR);
20867c478bd9Sstevel@tonic-gate 	}
20877c478bd9Sstevel@tonic-gate 
20887c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
20897c478bd9Sstevel@tonic-gate 	switch (ugenp->ug_dev_state) {
20907c478bd9Sstevel@tonic-gate 	case USB_DEV_ONLINE:
20917c478bd9Sstevel@tonic-gate 
20927c478bd9Sstevel@tonic-gate 		break;
20937c478bd9Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
20947c478bd9Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
20957c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
20967c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_DISCONNECTED;
20977c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
20987c478bd9Sstevel@tonic-gate 		rval = ENODEV;
20997c478bd9Sstevel@tonic-gate 
21007c478bd9Sstevel@tonic-gate 		break;
21017c478bd9Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RESUME:
21027c478bd9Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
21037c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
21047c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_SUSPENDED;
21057c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
21067c478bd9Sstevel@tonic-gate 		rval = EBADF;
21077c478bd9Sstevel@tonic-gate 
21087c478bd9Sstevel@tonic-gate 		break;
21097c478bd9Sstevel@tonic-gate 	default:
21107c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
21117c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_HW_ERR;
21127c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
21137c478bd9Sstevel@tonic-gate 		rval = EIO;
21147c478bd9Sstevel@tonic-gate 
21157c478bd9Sstevel@tonic-gate 		break;
21167c478bd9Sstevel@tonic-gate 	}
21177c478bd9Sstevel@tonic-gate 
21187c478bd9Sstevel@tonic-gate #ifndef __lock_lint
21197c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
21207c478bd9Sstevel@tonic-gate 	    "ugen_epx_req: lcmd_status=0x%x", epp->ep_lcmd_status);
21217c478bd9Sstevel@tonic-gate #endif
21227c478bd9Sstevel@tonic-gate 
21237c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
21247c478bd9Sstevel@tonic-gate 
21257c478bd9Sstevel@tonic-gate 	if (rval) {
21267c478bd9Sstevel@tonic-gate 		usb_release_access(epp->ep_ser_cookie);
21277c478bd9Sstevel@tonic-gate 
21287c478bd9Sstevel@tonic-gate 		return (rval);
21297c478bd9Sstevel@tonic-gate 	}
21307c478bd9Sstevel@tonic-gate 
21317c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
21327c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_XS_OPEN);
21337c478bd9Sstevel@tonic-gate 	epp->ep_done = 0;
21347c478bd9Sstevel@tonic-gate 	epp->ep_bp = bp;
21357c478bd9Sstevel@tonic-gate 
21367c478bd9Sstevel@tonic-gate 	switch (epp->ep_descr.bmAttributes & USB_EP_ATTR_MASK) {
21377c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_CONTROL:
21387c478bd9Sstevel@tonic-gate 		rval = ugen_epx_ctrl_req(ugenp, epp, bp, &wait);
21397c478bd9Sstevel@tonic-gate 
21407c478bd9Sstevel@tonic-gate 		break;
21417c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_BULK:
21427c478bd9Sstevel@tonic-gate 		rval = ugen_epx_bulk_req(ugenp, epp, bp, &wait);
21437c478bd9Sstevel@tonic-gate 
21447c478bd9Sstevel@tonic-gate 		break;
21457c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_INTR:
21467c478bd9Sstevel@tonic-gate 		if (bp->b_flags & B_READ) {
21477c478bd9Sstevel@tonic-gate 			rval = ugen_epx_intr_IN_req(ugenp, epp, bp, &wait);
21487c478bd9Sstevel@tonic-gate 		} else {
21497c478bd9Sstevel@tonic-gate 			rval = ugen_epx_intr_OUT_req(ugenp, epp, bp, &wait);
21507c478bd9Sstevel@tonic-gate 		}
21517c478bd9Sstevel@tonic-gate 
21527c478bd9Sstevel@tonic-gate 		break;
21537c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_ISOCH:
21547c478bd9Sstevel@tonic-gate 	default:
21557c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
21567c478bd9Sstevel@tonic-gate 		rval = USB_INVALID_REQUEST;
21577c478bd9Sstevel@tonic-gate 	}
21587c478bd9Sstevel@tonic-gate 
21597c478bd9Sstevel@tonic-gate 	/* if the xfer could not immediately be completed, block here */
21607c478bd9Sstevel@tonic-gate 	if ((rval == USB_SUCCESS) && wait) {
21617c478bd9Sstevel@tonic-gate 		while (!epp->ep_done) {
21627c478bd9Sstevel@tonic-gate 			if ((cv_wait_sig(&epp->ep_wait_cv,
21637c478bd9Sstevel@tonic-gate 			    &epp->ep_mutex) <= 0) && !epp->ep_done) {
21647c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(UGEN_PRINT_XFER,
21657c478bd9Sstevel@tonic-gate 				    ugenp->ug_log_hdl,
21667c478bd9Sstevel@tonic-gate 				    "ugen_epx_req: interrupted ep=0x%" PRIx64,
21677c478bd9Sstevel@tonic-gate 				    UGEN_MINOR_EPIDX(ugenp, dev));
21687c478bd9Sstevel@tonic-gate 
21697c478bd9Sstevel@tonic-gate 				/*
21707c478bd9Sstevel@tonic-gate 				 * blow away the request except for dflt pipe
21717c478bd9Sstevel@tonic-gate 				 * (this is prevented in USBA)
21727c478bd9Sstevel@tonic-gate 				 */
21737c478bd9Sstevel@tonic-gate 				mutex_exit(&epp->ep_mutex);
21747c478bd9Sstevel@tonic-gate 				usb_pipe_reset(ugenp->ug_dip, epp->ep_ph,
21757c478bd9Sstevel@tonic-gate 				    USB_FLAGS_SLEEP, NULL, NULL);
21767c478bd9Sstevel@tonic-gate 				(void) usb_pipe_drain_reqs(ugenp->ug_dip,
21777c478bd9Sstevel@tonic-gate 				    epp->ep_ph, 0,
21787c478bd9Sstevel@tonic-gate 				    USB_FLAGS_SLEEP, NULL, NULL);
21797c478bd9Sstevel@tonic-gate 
21807c478bd9Sstevel@tonic-gate 				mutex_enter(&epp->ep_mutex);
21817c478bd9Sstevel@tonic-gate 
21827c478bd9Sstevel@tonic-gate 				if (geterror(bp) == 0) {
21837c478bd9Sstevel@tonic-gate 					bioerror(bp, EINTR);
21847c478bd9Sstevel@tonic-gate 				}
21857c478bd9Sstevel@tonic-gate 				epp->ep_lcmd_status =
21867c478bd9Sstevel@tonic-gate 				    USB_LC_STAT_INTERRUPTED;
21877c478bd9Sstevel@tonic-gate 
21887c478bd9Sstevel@tonic-gate 				break;
21897c478bd9Sstevel@tonic-gate 			}
21907c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
21917c478bd9Sstevel@tonic-gate 			    "ugen_epx_req: wakeup");
21927c478bd9Sstevel@tonic-gate 		}
21937c478bd9Sstevel@tonic-gate 	}
21947c478bd9Sstevel@tonic-gate 
21957c478bd9Sstevel@tonic-gate 	/* always set lcmd_status if there was a failure */
21967c478bd9Sstevel@tonic-gate 	if ((rval != USB_SUCCESS) &&
21977c478bd9Sstevel@tonic-gate 	    (epp->ep_lcmd_status == USB_LC_STAT_NOERROR)) {
21987c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_UNSPECIFIED_ERR;
21997c478bd9Sstevel@tonic-gate 	}
22007c478bd9Sstevel@tonic-gate 
22017c478bd9Sstevel@tonic-gate 	epp->ep_done = 0;
22027c478bd9Sstevel@tonic-gate 	epp->ep_bp = NULL;
22037c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
22047c478bd9Sstevel@tonic-gate 
22057c478bd9Sstevel@tonic-gate 	usb_release_access(epp->ep_ser_cookie);
22067c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
22077c478bd9Sstevel@tonic-gate 	    "ugen_epx_req: done");
22087c478bd9Sstevel@tonic-gate 
22097c478bd9Sstevel@tonic-gate 	return (usb_rval2errno(rval));
22107c478bd9Sstevel@tonic-gate }
22117c478bd9Sstevel@tonic-gate 
22127c478bd9Sstevel@tonic-gate 
22137c478bd9Sstevel@tonic-gate /*
22147c478bd9Sstevel@tonic-gate  * handle control xfers
22157c478bd9Sstevel@tonic-gate  */
22167c478bd9Sstevel@tonic-gate static int
22177c478bd9Sstevel@tonic-gate ugen_epx_ctrl_req(ugen_state_t *ugenp, ugen_ep_t *epp,
22187c478bd9Sstevel@tonic-gate     struct buf *bp, boolean_t *wait)
22197c478bd9Sstevel@tonic-gate {
22207c478bd9Sstevel@tonic-gate 	usb_ctrl_req_t *reqp = NULL;
22217c478bd9Sstevel@tonic-gate 	uchar_t	*setup = ((uchar_t *)(bp->b_un.b_addr));
22227c478bd9Sstevel@tonic-gate 	int	rval;
22237c478bd9Sstevel@tonic-gate 	ushort_t wLength;
22247c478bd9Sstevel@tonic-gate 
22257c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
22267c478bd9Sstevel@tonic-gate 	    "ugen_epx_ctrl_req: epp=0x%p state=0x%x bp=0x%p",
22277c478bd9Sstevel@tonic-gate 	    epp, epp->ep_state, bp);
22287c478bd9Sstevel@tonic-gate 
22297c478bd9Sstevel@tonic-gate 	/* is this a read following a write with setup data? */
22307c478bd9Sstevel@tonic-gate 	if (bp->b_flags & B_READ) {
22317c478bd9Sstevel@tonic-gate 		if (epp->ep_data) {
22327c478bd9Sstevel@tonic-gate 			int ep_len = epp->ep_data->b_wptr -
22337c478bd9Sstevel@tonic-gate 						epp->ep_data->b_rptr;
22347c478bd9Sstevel@tonic-gate 			int len = min(bp->b_bcount, ep_len);
22357c478bd9Sstevel@tonic-gate 
22367c478bd9Sstevel@tonic-gate 			bcopy(epp->ep_data->b_rptr, bp->b_un.b_addr, len);
22377c478bd9Sstevel@tonic-gate 			epp->ep_data->b_rptr += len;
22387c478bd9Sstevel@tonic-gate 			if ((epp->ep_data->b_wptr - epp->ep_data->b_rptr) ==
22397c478bd9Sstevel@tonic-gate 			    0) {
22407c478bd9Sstevel@tonic-gate 				freemsg(epp->ep_data);
22417c478bd9Sstevel@tonic-gate 				epp->ep_data = NULL;
22427c478bd9Sstevel@tonic-gate 			}
22437c478bd9Sstevel@tonic-gate 			bp->b_resid = bp->b_bcount - len;
22447c478bd9Sstevel@tonic-gate 		} else {
22457c478bd9Sstevel@tonic-gate 			bp->b_resid = bp->b_bcount;
22467c478bd9Sstevel@tonic-gate 		}
22477c478bd9Sstevel@tonic-gate 
22487c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
22497c478bd9Sstevel@tonic-gate 	}
22507c478bd9Sstevel@tonic-gate 
22517c478bd9Sstevel@tonic-gate 	/* discard old data if any */
22527c478bd9Sstevel@tonic-gate 	if (epp->ep_data) {
22537c478bd9Sstevel@tonic-gate 		freemsg(epp->ep_data);
22547c478bd9Sstevel@tonic-gate 		epp->ep_data = NULL;
22557c478bd9Sstevel@tonic-gate 	}
22567c478bd9Sstevel@tonic-gate 
22577c478bd9Sstevel@tonic-gate 	/* allocate and initialize request */
22587c478bd9Sstevel@tonic-gate 	wLength = (setup[7] << 8) | setup[6];
22597c478bd9Sstevel@tonic-gate 	reqp = usb_alloc_ctrl_req(ugenp->ug_dip, wLength, USB_FLAGS_NOSLEEP);
22607c478bd9Sstevel@tonic-gate 	if (reqp == NULL) {
22617c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_NO_RESOURCES;
22627c478bd9Sstevel@tonic-gate 
22637c478bd9Sstevel@tonic-gate 		return (USB_NO_RESOURCES);
22647c478bd9Sstevel@tonic-gate 	}
22657c478bd9Sstevel@tonic-gate 
22667c478bd9Sstevel@tonic-gate 	/* assume an LE data stream */
22677c478bd9Sstevel@tonic-gate 	reqp->ctrl_bmRequestType = setup[0];
22687c478bd9Sstevel@tonic-gate 	reqp->ctrl_bRequest	= setup[1];
22697c478bd9Sstevel@tonic-gate 	reqp->ctrl_wValue	= (setup[3] << 8) | setup[2];
22707c478bd9Sstevel@tonic-gate 	reqp->ctrl_wIndex	= (setup[5] << 8) | setup[4];
22717c478bd9Sstevel@tonic-gate 	reqp->ctrl_wLength	= wLength;
22727c478bd9Sstevel@tonic-gate 	reqp->ctrl_timeout	= ugen_ctrl_timeout;
22737c478bd9Sstevel@tonic-gate 	reqp->ctrl_attributes	= USB_ATTRS_AUTOCLEARING |
22747c478bd9Sstevel@tonic-gate 					USB_ATTRS_SHORT_XFER_OK;
22757c478bd9Sstevel@tonic-gate 	reqp->ctrl_cb		= ugen_epx_ctrl_req_cb;
22767c478bd9Sstevel@tonic-gate 	reqp->ctrl_exc_cb	= ugen_epx_ctrl_req_cb;
22777c478bd9Sstevel@tonic-gate 	reqp->ctrl_client_private = (usb_opaque_t)ugenp;
22787c478bd9Sstevel@tonic-gate 
22797c478bd9Sstevel@tonic-gate 	/*
22807c478bd9Sstevel@tonic-gate 	 * is this a legal request? No accesses to device are
22817c478bd9Sstevel@tonic-gate 	 * allowed if we don't own the device
22827c478bd9Sstevel@tonic-gate 	 */
22837c478bd9Sstevel@tonic-gate 	if (((reqp->ctrl_bmRequestType & USB_DEV_REQ_RCPT_MASK) ==
22847c478bd9Sstevel@tonic-gate 	    USB_DEV_REQ_RCPT_DEV) &&
22857c478bd9Sstevel@tonic-gate 	    (((reqp->ctrl_bmRequestType & USB_DEV_REQ_DIR_MASK) ==
22867c478bd9Sstevel@tonic-gate 	    USB_DEV_REQ_HOST_TO_DEV) &&
22877c478bd9Sstevel@tonic-gate 	    (usb_owns_device(ugenp->ug_dip) == B_FALSE))) {
22887c478bd9Sstevel@tonic-gate 		rval = USB_INVALID_PERM;
22897c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
22907c478bd9Sstevel@tonic-gate 
22917c478bd9Sstevel@tonic-gate 		goto fail;
22927c478bd9Sstevel@tonic-gate 	}
22937c478bd9Sstevel@tonic-gate 
22947c478bd9Sstevel@tonic-gate 	/* filter out set_cfg and set_if standard requests */
22957c478bd9Sstevel@tonic-gate 	if ((reqp->ctrl_bmRequestType & USB_DEV_REQ_TYPE_MASK) ==
22967c478bd9Sstevel@tonic-gate 	    USB_DEV_REQ_TYPE_STANDARD) {
22977c478bd9Sstevel@tonic-gate 		switch (reqp->ctrl_bRequest) {
22987c478bd9Sstevel@tonic-gate 		case USB_REQ_SET_CFG:
22997c478bd9Sstevel@tonic-gate 		case USB_REQ_SET_IF:
23007c478bd9Sstevel@tonic-gate 			rval = USB_INVALID_REQUEST;
23017c478bd9Sstevel@tonic-gate 			epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
23027c478bd9Sstevel@tonic-gate 
23037c478bd9Sstevel@tonic-gate 			goto fail;
23047c478bd9Sstevel@tonic-gate 		default:
23057c478bd9Sstevel@tonic-gate 
23067c478bd9Sstevel@tonic-gate 			break;
23077c478bd9Sstevel@tonic-gate 		}
23087c478bd9Sstevel@tonic-gate 	}
23097c478bd9Sstevel@tonic-gate 
23107c478bd9Sstevel@tonic-gate 	/* is this from host to device? */
23117c478bd9Sstevel@tonic-gate 	if (((reqp->ctrl_bmRequestType & USB_DEV_REQ_DIR_MASK) ==
23127c478bd9Sstevel@tonic-gate 	    USB_DEV_REQ_HOST_TO_DEV) && reqp->ctrl_wLength) {
23137c478bd9Sstevel@tonic-gate 		if (((bp->b_bcount - UGEN_SETUP_PKT_SIZE) - wLength) != 0) {
23147c478bd9Sstevel@tonic-gate 			rval = USB_INVALID_REQUEST;
23157c478bd9Sstevel@tonic-gate 			epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
23167c478bd9Sstevel@tonic-gate 
23177c478bd9Sstevel@tonic-gate 			goto fail;
23187c478bd9Sstevel@tonic-gate 		}
23197c478bd9Sstevel@tonic-gate 		bcopy(bp->b_un.b_addr + UGEN_SETUP_PKT_SIZE,
23207c478bd9Sstevel@tonic-gate 		    reqp->ctrl_data->b_wptr, wLength);
23217c478bd9Sstevel@tonic-gate 		reqp->ctrl_data->b_wptr += wLength;
23227c478bd9Sstevel@tonic-gate 	} else	if ((reqp->ctrl_bmRequestType & USB_DEV_REQ_DIR_MASK) ==
23237c478bd9Sstevel@tonic-gate 	    USB_DEV_REQ_DEV_TO_HOST) {
23247c478bd9Sstevel@tonic-gate 		if (bp->b_bcount != UGEN_SETUP_PKT_SIZE) {
23257c478bd9Sstevel@tonic-gate 			rval = USB_INVALID_REQUEST;
23267c478bd9Sstevel@tonic-gate 			epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
23277c478bd9Sstevel@tonic-gate 
23287c478bd9Sstevel@tonic-gate 			goto fail;
23297c478bd9Sstevel@tonic-gate 		}
23307c478bd9Sstevel@tonic-gate 	}
23317c478bd9Sstevel@tonic-gate 
23327c478bd9Sstevel@tonic-gate 	/* submit the request */
23337c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
23347c478bd9Sstevel@tonic-gate 	rval = usb_pipe_ctrl_xfer(epp->ep_ph, reqp, USB_FLAGS_NOSLEEP);
23357c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
23367c478bd9Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
23377c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status =
23387c478bd9Sstevel@tonic-gate 		    ugen_cr2lcstat(reqp->ctrl_completion_reason);
23397c478bd9Sstevel@tonic-gate 
23407c478bd9Sstevel@tonic-gate 		goto fail;
23417c478bd9Sstevel@tonic-gate 	}
23427c478bd9Sstevel@tonic-gate done:
23437c478bd9Sstevel@tonic-gate 	*wait = B_TRUE;
23447c478bd9Sstevel@tonic-gate 
23457c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
23467c478bd9Sstevel@tonic-gate fail:
23477c478bd9Sstevel@tonic-gate 	*wait = B_FALSE;
23487c478bd9Sstevel@tonic-gate 
23497c478bd9Sstevel@tonic-gate 	usb_free_ctrl_req(reqp);
23507c478bd9Sstevel@tonic-gate 
23517c478bd9Sstevel@tonic-gate 	return (rval);
23527c478bd9Sstevel@tonic-gate }
23537c478bd9Sstevel@tonic-gate 
23547c478bd9Sstevel@tonic-gate 
23557c478bd9Sstevel@tonic-gate /*
23567c478bd9Sstevel@tonic-gate  * callback for control requests, normal and exception completion
23577c478bd9Sstevel@tonic-gate  */
23587c478bd9Sstevel@tonic-gate static void
23597c478bd9Sstevel@tonic-gate ugen_epx_ctrl_req_cb(usb_pipe_handle_t ph, usb_ctrl_req_t *reqp)
23607c478bd9Sstevel@tonic-gate {
23617c478bd9Sstevel@tonic-gate 	ugen_state_t *ugenp = (ugen_state_t *)reqp->ctrl_client_private;
23627c478bd9Sstevel@tonic-gate 	ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
23637c478bd9Sstevel@tonic-gate 
23647c478bd9Sstevel@tonic-gate 	if (epp == NULL) {
23657c478bd9Sstevel@tonic-gate 		epp = &ugenp->ug_ep[0];
23667c478bd9Sstevel@tonic-gate 	}
23677c478bd9Sstevel@tonic-gate 
23687c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
23697c478bd9Sstevel@tonic-gate 
23707c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
23717c478bd9Sstevel@tonic-gate 	    "ugen_epx_ctrl_req_cb:\n\t"
23727c478bd9Sstevel@tonic-gate 	    "epp=0x%p state=0x%x ph=0x%p reqp=0x%p cr=%d cb=0x%x",
23737c478bd9Sstevel@tonic-gate 	    epp, epp->ep_state, ph, reqp, reqp->ctrl_completion_reason,
23747c478bd9Sstevel@tonic-gate 	    reqp->ctrl_cb_flags);
23757c478bd9Sstevel@tonic-gate 
23767c478bd9Sstevel@tonic-gate 	ASSERT((reqp->ctrl_cb_flags & USB_CB_INTR_CONTEXT) == 0);
23777c478bd9Sstevel@tonic-gate 
23787c478bd9Sstevel@tonic-gate 	/* save any data for the next read */
23797c478bd9Sstevel@tonic-gate 	switch (reqp->ctrl_completion_reason) {
23807c478bd9Sstevel@tonic-gate 	case USB_CR_OK:
23817c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
23827c478bd9Sstevel@tonic-gate 
23837c478bd9Sstevel@tonic-gate 		break;
23847c478bd9Sstevel@tonic-gate 	case USB_CR_PIPE_RESET:
23857c478bd9Sstevel@tonic-gate 
23867c478bd9Sstevel@tonic-gate 		break;
23877c478bd9Sstevel@tonic-gate 	default:
23887c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status =
23897c478bd9Sstevel@tonic-gate 		    ugen_cr2lcstat(reqp->ctrl_completion_reason);
23907c478bd9Sstevel@tonic-gate 		if (epp->ep_bp) {
23917c478bd9Sstevel@tonic-gate 			bioerror(epp->ep_bp, EIO);
23927c478bd9Sstevel@tonic-gate 		}
23937c478bd9Sstevel@tonic-gate 
23947c478bd9Sstevel@tonic-gate 		break;
23957c478bd9Sstevel@tonic-gate 	}
23967c478bd9Sstevel@tonic-gate 
23977c478bd9Sstevel@tonic-gate 	if (reqp->ctrl_data) {
23987c478bd9Sstevel@tonic-gate 		ASSERT(epp->ep_data == NULL);
23997c478bd9Sstevel@tonic-gate 		epp->ep_data = reqp->ctrl_data;
24007c478bd9Sstevel@tonic-gate 		reqp->ctrl_data = NULL;
24017c478bd9Sstevel@tonic-gate 	}
24027c478bd9Sstevel@tonic-gate 	epp->ep_done++;
24037c478bd9Sstevel@tonic-gate 	cv_signal(&epp->ep_wait_cv);
24047c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
24057c478bd9Sstevel@tonic-gate 
24067c478bd9Sstevel@tonic-gate 	usb_free_ctrl_req(reqp);
24077c478bd9Sstevel@tonic-gate 
24087c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
24097c478bd9Sstevel@tonic-gate 	    "ugen_epx_ctrl_req_cb: done");
24107c478bd9Sstevel@tonic-gate }
24117c478bd9Sstevel@tonic-gate 
24127c478bd9Sstevel@tonic-gate 
24137c478bd9Sstevel@tonic-gate /*
24147c478bd9Sstevel@tonic-gate  * handle bulk xfers
24157c478bd9Sstevel@tonic-gate  */
24167c478bd9Sstevel@tonic-gate static int
24177c478bd9Sstevel@tonic-gate ugen_epx_bulk_req(ugen_state_t *ugenp, ugen_ep_t *epp,
24187c478bd9Sstevel@tonic-gate     struct buf *bp, boolean_t *wait)
24197c478bd9Sstevel@tonic-gate {
24207c478bd9Sstevel@tonic-gate 	int		rval;
24217c478bd9Sstevel@tonic-gate 	usb_bulk_req_t	*reqp = usb_alloc_bulk_req(ugenp->ug_dip,
24227c478bd9Sstevel@tonic-gate 				bp->b_bcount, USB_FLAGS_NOSLEEP);
24237c478bd9Sstevel@tonic-gate 
24247c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
24257c478bd9Sstevel@tonic-gate 	    "ugen_epx_bulk_req: epp=0x%p state=0x%x bp=0x%p",
24267c478bd9Sstevel@tonic-gate 	    epp, epp->ep_state, bp);
24277c478bd9Sstevel@tonic-gate 
24287c478bd9Sstevel@tonic-gate 	if (reqp == NULL) {
24297c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_NO_RESOURCES;
24307c478bd9Sstevel@tonic-gate 
24317c478bd9Sstevel@tonic-gate 		return (USB_NO_RESOURCES);
24327c478bd9Sstevel@tonic-gate 	}
24337c478bd9Sstevel@tonic-gate 
24347c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_XS_OPEN);
24357c478bd9Sstevel@tonic-gate 
24367c478bd9Sstevel@tonic-gate 	/*
24377c478bd9Sstevel@tonic-gate 	 * the transfer count is limited in minphys with what the HCD can
24387c478bd9Sstevel@tonic-gate 	 * do
24397c478bd9Sstevel@tonic-gate 	 */
24407c478bd9Sstevel@tonic-gate 	reqp->bulk_len		= bp->b_bcount;
24417c478bd9Sstevel@tonic-gate 	reqp->bulk_timeout	= ugen_bulk_timeout;
24427c478bd9Sstevel@tonic-gate 	reqp->bulk_client_private = (usb_opaque_t)ugenp;
24437c478bd9Sstevel@tonic-gate 	reqp->bulk_attributes	= USB_ATTRS_AUTOCLEARING;
24447c478bd9Sstevel@tonic-gate 	reqp->bulk_cb		= ugen_epx_bulk_req_cb;
24457c478bd9Sstevel@tonic-gate 	reqp->bulk_exc_cb	= ugen_epx_bulk_req_cb;
24467c478bd9Sstevel@tonic-gate 
24477c478bd9Sstevel@tonic-gate 	/* copy data into bp for OUT pipes */
24487c478bd9Sstevel@tonic-gate 	if ((UGEN_XFER_DIR(epp) & USB_EP_DIR_IN) == 0) {
24497c478bd9Sstevel@tonic-gate 		bcopy(epp->ep_bp->b_un.b_addr, reqp->bulk_data->b_rptr,
24507c478bd9Sstevel@tonic-gate 								bp->b_bcount);
24517c478bd9Sstevel@tonic-gate 		reqp->bulk_data->b_wptr += bp->b_bcount;
24527c478bd9Sstevel@tonic-gate 	} else {
24537c478bd9Sstevel@tonic-gate 		reqp->bulk_attributes |= USB_ATTRS_SHORT_XFER_OK;
24547c478bd9Sstevel@tonic-gate 	}
24557c478bd9Sstevel@tonic-gate 
24567c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
24577c478bd9Sstevel@tonic-gate 	if ((rval = usb_pipe_bulk_xfer(epp->ep_ph, reqp,
24587c478bd9Sstevel@tonic-gate 	    USB_FLAGS_NOSLEEP)) != USB_SUCCESS) {
24597c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
24607c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status =
24617c478bd9Sstevel@tonic-gate 		    ugen_cr2lcstat(reqp->bulk_completion_reason);
24627c478bd9Sstevel@tonic-gate 		usb_free_bulk_req(reqp);
24637c478bd9Sstevel@tonic-gate 		bioerror(bp, EIO);
24647c478bd9Sstevel@tonic-gate 	} else {
24657c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
24667c478bd9Sstevel@tonic-gate 	}
24677c478bd9Sstevel@tonic-gate 	*wait = (rval == USB_SUCCESS) ? B_TRUE : B_FALSE;
24687c478bd9Sstevel@tonic-gate 
24697c478bd9Sstevel@tonic-gate 	return (rval);
24707c478bd9Sstevel@tonic-gate }
24717c478bd9Sstevel@tonic-gate 
24727c478bd9Sstevel@tonic-gate 
24737c478bd9Sstevel@tonic-gate /*
24747c478bd9Sstevel@tonic-gate  * normal and exception bulk request callback
24757c478bd9Sstevel@tonic-gate  */
24767c478bd9Sstevel@tonic-gate static void
24777c478bd9Sstevel@tonic-gate ugen_epx_bulk_req_cb(usb_pipe_handle_t ph, usb_bulk_req_t *reqp)
24787c478bd9Sstevel@tonic-gate {
24797c478bd9Sstevel@tonic-gate 	ugen_state_t *ugenp = (ugen_state_t *)reqp->bulk_client_private;
24807c478bd9Sstevel@tonic-gate 	ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
24817c478bd9Sstevel@tonic-gate 
24827c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
24837c478bd9Sstevel@tonic-gate 	    "ugen_epx_bulk_req_cb: ph=0x%p reqp=0x%p cr=%d cb=0x%x",
24847c478bd9Sstevel@tonic-gate 	    ph, reqp, reqp->bulk_completion_reason, reqp->bulk_cb_flags);
24857c478bd9Sstevel@tonic-gate 
24867c478bd9Sstevel@tonic-gate 	ASSERT((reqp->bulk_cb_flags & USB_CB_INTR_CONTEXT) == 0);
24877c478bd9Sstevel@tonic-gate 
24887c478bd9Sstevel@tonic-gate 	/* epp might be NULL if we are closing the pipe */
24897c478bd9Sstevel@tonic-gate 	if (epp) {
24907c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
24917c478bd9Sstevel@tonic-gate 		if (epp->ep_bp && reqp->bulk_data) {
24927c478bd9Sstevel@tonic-gate 			int len = min(reqp->bulk_data->b_wptr -
24937c478bd9Sstevel@tonic-gate 					reqp->bulk_data->b_rptr,
24947c478bd9Sstevel@tonic-gate 					epp->ep_bp->b_bcount);
24957c478bd9Sstevel@tonic-gate 			if (UGEN_XFER_DIR(epp) & USB_EP_DIR_IN) {
24967c478bd9Sstevel@tonic-gate 				if (len) {
24977c478bd9Sstevel@tonic-gate 					bcopy(reqp->bulk_data->b_rptr,
24987c478bd9Sstevel@tonic-gate 					    epp->ep_bp->b_un.b_addr, len);
24997c478bd9Sstevel@tonic-gate 					epp->ep_bp->b_resid =
25007c478bd9Sstevel@tonic-gate 					    epp->ep_bp->b_bcount - len;
25017c478bd9Sstevel@tonic-gate 				}
25027c478bd9Sstevel@tonic-gate 			} else {
25037c478bd9Sstevel@tonic-gate 				epp->ep_bp->b_resid =
25047c478bd9Sstevel@tonic-gate 					epp->ep_bp->b_bcount - len;
25057c478bd9Sstevel@tonic-gate 			}
25067c478bd9Sstevel@tonic-gate 		}
25077c478bd9Sstevel@tonic-gate 		switch (reqp->bulk_completion_reason) {
25087c478bd9Sstevel@tonic-gate 		case USB_CR_OK:
25097c478bd9Sstevel@tonic-gate 			epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
25107c478bd9Sstevel@tonic-gate 
25117c478bd9Sstevel@tonic-gate 			break;
25127c478bd9Sstevel@tonic-gate 		case USB_CR_PIPE_RESET:
25137c478bd9Sstevel@tonic-gate 
25147c478bd9Sstevel@tonic-gate 			break;
25157c478bd9Sstevel@tonic-gate 		default:
25167c478bd9Sstevel@tonic-gate 			epp->ep_lcmd_status =
25177c478bd9Sstevel@tonic-gate 			    ugen_cr2lcstat(reqp->bulk_completion_reason);
25187c478bd9Sstevel@tonic-gate 			if (epp->ep_bp) {
25197c478bd9Sstevel@tonic-gate 				bioerror(epp->ep_bp, EIO);
25207c478bd9Sstevel@tonic-gate 			}
25217c478bd9Sstevel@tonic-gate 		}
25227c478bd9Sstevel@tonic-gate 		epp->ep_done++;
25237c478bd9Sstevel@tonic-gate 		cv_signal(&epp->ep_wait_cv);
25247c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
25257c478bd9Sstevel@tonic-gate 	}
25267c478bd9Sstevel@tonic-gate 
25277c478bd9Sstevel@tonic-gate 	usb_free_bulk_req(reqp);
25287c478bd9Sstevel@tonic-gate }
25297c478bd9Sstevel@tonic-gate 
25307c478bd9Sstevel@tonic-gate 
25317c478bd9Sstevel@tonic-gate /*
25327c478bd9Sstevel@tonic-gate  * handle intr IN xfers
25337c478bd9Sstevel@tonic-gate  */
25347c478bd9Sstevel@tonic-gate static int
25357c478bd9Sstevel@tonic-gate ugen_epx_intr_IN_req(ugen_state_t *ugenp, ugen_ep_t *epp,
25367c478bd9Sstevel@tonic-gate     struct buf *bp, boolean_t *wait)
25377c478bd9Sstevel@tonic-gate {
25387c478bd9Sstevel@tonic-gate 	int	len = 0;
25397c478bd9Sstevel@tonic-gate 	int	rval = USB_SUCCESS;
25407c478bd9Sstevel@tonic-gate 
25417c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
25427c478bd9Sstevel@tonic-gate 	    "ugen_epx_intr_IN_req: epp=0x%p state=0x%x bp=0x%p",
25437c478bd9Sstevel@tonic-gate 	    epp, epp->ep_state, bp);
25447c478bd9Sstevel@tonic-gate 
25457c478bd9Sstevel@tonic-gate 	*wait = B_FALSE;
25467c478bd9Sstevel@tonic-gate 
25477c478bd9Sstevel@tonic-gate 	/* can we satisfy this read? */
25487c478bd9Sstevel@tonic-gate 	if (epp->ep_data) {
25497c478bd9Sstevel@tonic-gate 		len = min(epp->ep_data->b_wptr - epp->ep_data->b_rptr,
25507c478bd9Sstevel@tonic-gate 							bp->b_bcount);
25517c478bd9Sstevel@tonic-gate 	}
25527c478bd9Sstevel@tonic-gate 
25537c478bd9Sstevel@tonic-gate 	/*
25547c478bd9Sstevel@tonic-gate 	 * if polling not active, restart, and return failure
25557c478bd9Sstevel@tonic-gate 	 * immediately unless one xfer mode has been requested
25567c478bd9Sstevel@tonic-gate 	 * if there is some data, return a short read
25577c478bd9Sstevel@tonic-gate 	 */
25587c478bd9Sstevel@tonic-gate 	if ((epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_ON) == 0) {
25597c478bd9Sstevel@tonic-gate 		if (len == 0) {
25607c478bd9Sstevel@tonic-gate 			if (!epp->ep_one_xfer) {
25617c478bd9Sstevel@tonic-gate 				rval = USB_FAILURE;
25627c478bd9Sstevel@tonic-gate 				if (epp->ep_lcmd_status ==
25637c478bd9Sstevel@tonic-gate 				    USB_LC_STAT_NOERROR) {
25647c478bd9Sstevel@tonic-gate 					epp->ep_lcmd_status =
25657c478bd9Sstevel@tonic-gate 						USB_LC_STAT_INTR_BUF_FULL;
25667c478bd9Sstevel@tonic-gate 				}
25677c478bd9Sstevel@tonic-gate 			}
25687c478bd9Sstevel@tonic-gate 			if (ugen_epx_intr_IN_start_polling(ugenp,
25697c478bd9Sstevel@tonic-gate 			    epp) != USB_SUCCESS) {
25707c478bd9Sstevel@tonic-gate 				epp->ep_lcmd_status =
25717c478bd9Sstevel@tonic-gate 				    USB_LC_STAT_INTR_POLLING_FAILED;
25727c478bd9Sstevel@tonic-gate 			}
25737c478bd9Sstevel@tonic-gate 			if (epp->ep_one_xfer) {
25747c478bd9Sstevel@tonic-gate 				*wait = B_TRUE;
25757c478bd9Sstevel@tonic-gate 			}
25767c478bd9Sstevel@tonic-gate 			goto done;
25777c478bd9Sstevel@tonic-gate 		} else if (epp->ep_data && (len < bp->b_bcount)) {
25787c478bd9Sstevel@tonic-gate 			bcopy(epp->ep_data->b_rptr, bp->b_un.b_addr, len);
25797c478bd9Sstevel@tonic-gate 			bp->b_resid = bp->b_bcount - len;
25807c478bd9Sstevel@tonic-gate 			epp->ep_data->b_rptr += len;
25817c478bd9Sstevel@tonic-gate 
25827c478bd9Sstevel@tonic-gate 			goto done;
25837c478bd9Sstevel@tonic-gate 		}
25847c478bd9Sstevel@tonic-gate 	}
25857c478bd9Sstevel@tonic-gate 
25867c478bd9Sstevel@tonic-gate 	/*
25877c478bd9Sstevel@tonic-gate 	 * if there is data or FNDELAY, return available data
25887c478bd9Sstevel@tonic-gate 	 */
25897c478bd9Sstevel@tonic-gate 	if ((len >= bp->b_bcount) ||
25907c478bd9Sstevel@tonic-gate 	    (epp->ep_xfer_oflag & (FNDELAY | FNONBLOCK))) {
25917c478bd9Sstevel@tonic-gate 		if (epp->ep_data) {
25927c478bd9Sstevel@tonic-gate 			bcopy(epp->ep_data->b_rptr, bp->b_un.b_addr, len);
25937c478bd9Sstevel@tonic-gate 			epp->ep_data->b_rptr += len;
25947c478bd9Sstevel@tonic-gate 			bp->b_resid = bp->b_bcount - len;
25957c478bd9Sstevel@tonic-gate 		} else {
25967c478bd9Sstevel@tonic-gate 			bp->b_resid = bp->b_bcount;
25977c478bd9Sstevel@tonic-gate 		}
25987c478bd9Sstevel@tonic-gate 	} else {
25997c478bd9Sstevel@tonic-gate 		/* otherwise just wait for data */
26007c478bd9Sstevel@tonic-gate 		*wait = B_TRUE;
26017c478bd9Sstevel@tonic-gate 	}
26027c478bd9Sstevel@tonic-gate 
26037c478bd9Sstevel@tonic-gate done:
26047c478bd9Sstevel@tonic-gate 	if (epp->ep_data && (epp->ep_data->b_rptr == epp->ep_data->b_wptr)) {
26057c478bd9Sstevel@tonic-gate 		freemsg(epp->ep_data);
26067c478bd9Sstevel@tonic-gate 		epp->ep_data = NULL;
26077c478bd9Sstevel@tonic-gate 	}
26087c478bd9Sstevel@tonic-gate 
26097c478bd9Sstevel@tonic-gate 	if (*wait) {
26107c478bd9Sstevel@tonic-gate 		ASSERT(epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_ON);
26117c478bd9Sstevel@tonic-gate 	}
26127c478bd9Sstevel@tonic-gate 
26137c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
26147c478bd9Sstevel@tonic-gate 	    "ugen_epx_intr_IN_req end: rval=%d bcount=%lu len=%d data=0x%p",
26157c478bd9Sstevel@tonic-gate 	    rval, bp->b_bcount, len, epp->ep_data);
26167c478bd9Sstevel@tonic-gate 
26177c478bd9Sstevel@tonic-gate 	return (rval);
26187c478bd9Sstevel@tonic-gate }
26197c478bd9Sstevel@tonic-gate 
26207c478bd9Sstevel@tonic-gate 
26217c478bd9Sstevel@tonic-gate /*
26227c478bd9Sstevel@tonic-gate  * Start polling on interrupt endpoint, synchronously
26237c478bd9Sstevel@tonic-gate  */
26247c478bd9Sstevel@tonic-gate static int
26257c478bd9Sstevel@tonic-gate ugen_epx_intr_IN_start_polling(ugen_state_t *ugenp, ugen_ep_t *epp)
26267c478bd9Sstevel@tonic-gate {
26277c478bd9Sstevel@tonic-gate 	int rval = USB_FAILURE;
26287c478bd9Sstevel@tonic-gate 	usb_intr_req_t	*reqp;
26297c478bd9Sstevel@tonic-gate 	usb_flags_t uflag;
26307c478bd9Sstevel@tonic-gate 
26317c478bd9Sstevel@tonic-gate 	/*
26327c478bd9Sstevel@tonic-gate 	 * if polling is being stopped, we restart polling in the
26337c478bd9Sstevel@tonic-gate 	 * interrrupt callback again
26347c478bd9Sstevel@tonic-gate 	 */
26357c478bd9Sstevel@tonic-gate 	if (epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_IS_STOPPED) {
26367c478bd9Sstevel@tonic-gate 
26377c478bd9Sstevel@tonic-gate 		return (rval);
26387c478bd9Sstevel@tonic-gate 	}
26397c478bd9Sstevel@tonic-gate 	if ((epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_ON) == 0) {
26407c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
26417c478bd9Sstevel@tonic-gate 		    "ugen_epx_intr_IN_start_polling: epp=0x%p state=0x%x",
26427c478bd9Sstevel@tonic-gate 		    epp, epp->ep_state);
26437c478bd9Sstevel@tonic-gate 
26447c478bd9Sstevel@tonic-gate 		epp->ep_state |= UGEN_EP_STATE_INTR_IN_POLLING_ON;
26457c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
26467c478bd9Sstevel@tonic-gate 
26477c478bd9Sstevel@tonic-gate 		reqp = usb_alloc_intr_req(ugenp->ug_dip, 0,
26487c478bd9Sstevel@tonic-gate 						USB_FLAGS_SLEEP);
26497c478bd9Sstevel@tonic-gate 		reqp->intr_client_private = (usb_opaque_t)ugenp;
26507c478bd9Sstevel@tonic-gate 
26517c478bd9Sstevel@tonic-gate 		reqp->intr_attributes	= USB_ATTRS_AUTOCLEARING |
26527c478bd9Sstevel@tonic-gate 						USB_ATTRS_SHORT_XFER_OK;
26537c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
26547c478bd9Sstevel@tonic-gate 		if (epp->ep_one_xfer) {
26557c478bd9Sstevel@tonic-gate 			reqp->intr_attributes |= USB_ATTRS_ONE_XFER;
26567c478bd9Sstevel@tonic-gate 			uflag = USB_FLAGS_NOSLEEP;
26577c478bd9Sstevel@tonic-gate 		} else {
26587c478bd9Sstevel@tonic-gate 			uflag = USB_FLAGS_SLEEP;
26597c478bd9Sstevel@tonic-gate 		}
26607c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
26617c478bd9Sstevel@tonic-gate 
26627c478bd9Sstevel@tonic-gate 		reqp->intr_len		= epp->ep_descr.wMaxPacketSize;
26637c478bd9Sstevel@tonic-gate 		reqp->intr_cb		= ugen_epx_intr_IN_req_cb;
26647c478bd9Sstevel@tonic-gate 		reqp->intr_exc_cb	= ugen_epx_intr_IN_req_cb;
26657c478bd9Sstevel@tonic-gate 
26667c478bd9Sstevel@tonic-gate 
26677c478bd9Sstevel@tonic-gate 		if ((rval = usb_pipe_intr_xfer(epp->ep_ph, reqp,
26687c478bd9Sstevel@tonic-gate 		    uflag)) != USB_SUCCESS) {
26697c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
26707c478bd9Sstevel@tonic-gate 			    "ugen_epx_intr_IN_start_polling: failed %d", rval);
26717c478bd9Sstevel@tonic-gate 			usb_free_intr_req(reqp);
26727c478bd9Sstevel@tonic-gate 		}
26737c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
26747c478bd9Sstevel@tonic-gate 		if (rval != USB_SUCCESS) {
26757c478bd9Sstevel@tonic-gate 			epp->ep_state &= ~UGEN_EP_STATE_INTR_IN_POLLING_ON;
26767c478bd9Sstevel@tonic-gate 		}
26777c478bd9Sstevel@tonic-gate 	} else {
26787c478bd9Sstevel@tonic-gate 		rval = USB_SUCCESS;
26797c478bd9Sstevel@tonic-gate 	}
26807c478bd9Sstevel@tonic-gate 
26817c478bd9Sstevel@tonic-gate 	return (rval);
26827c478bd9Sstevel@tonic-gate }
26837c478bd9Sstevel@tonic-gate 
26847c478bd9Sstevel@tonic-gate 
26857c478bd9Sstevel@tonic-gate /*
26867c478bd9Sstevel@tonic-gate  * stop polling on an interrupt endpoint, asynchronously
26877c478bd9Sstevel@tonic-gate  */
26887c478bd9Sstevel@tonic-gate static void
26897c478bd9Sstevel@tonic-gate ugen_epx_intr_IN_stop_polling(ugen_state_t *ugenp, ugen_ep_t *epp)
26907c478bd9Sstevel@tonic-gate {
26917c478bd9Sstevel@tonic-gate 	if ((epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_ON) &&
26927c478bd9Sstevel@tonic-gate 	    ((epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_IS_STOPPED) == 0)) {
26937c478bd9Sstevel@tonic-gate 
26947c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
26957c478bd9Sstevel@tonic-gate 		    "ugen_epx_intr_IN_stop_polling: epp=0x%p state=0x%x",
26967c478bd9Sstevel@tonic-gate 		    epp, epp->ep_state);
26977c478bd9Sstevel@tonic-gate 
26987c478bd9Sstevel@tonic-gate 		epp->ep_state |= UGEN_EP_STATE_INTR_IN_POLLING_IS_STOPPED;
26997c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
27007c478bd9Sstevel@tonic-gate 		usb_pipe_stop_intr_polling(epp->ep_ph, USB_FLAGS_NOSLEEP);
27017c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
27027c478bd9Sstevel@tonic-gate 	}
27037c478bd9Sstevel@tonic-gate }
27047c478bd9Sstevel@tonic-gate 
27057c478bd9Sstevel@tonic-gate 
27067c478bd9Sstevel@tonic-gate /*
27077c478bd9Sstevel@tonic-gate  * poll management
27087c478bd9Sstevel@tonic-gate  */
27097c478bd9Sstevel@tonic-gate static void
27107c478bd9Sstevel@tonic-gate ugen_epx_intr_IN_poll_wakeup(ugen_state_t *ugenp, ugen_ep_t *epp)
27117c478bd9Sstevel@tonic-gate {
27127c478bd9Sstevel@tonic-gate 	if (epp->ep_state & UGEN_EP_STATE_INTR_IN_POLL_PENDING) {
27137c478bd9Sstevel@tonic-gate 		struct pollhead *phpp = &epp->ep_pollhead;
27147c478bd9Sstevel@tonic-gate 
27157c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
27167c478bd9Sstevel@tonic-gate 		    "ugen_epx_intr_IN_poll_wakeup: state=0x%x", epp->ep_state);
27177c478bd9Sstevel@tonic-gate 
27187c478bd9Sstevel@tonic-gate 		epp->ep_state &= ~UGEN_EP_STATE_INTR_IN_POLL_PENDING;
27197c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
27207c478bd9Sstevel@tonic-gate 		pollwakeup(phpp, POLLIN);
27217c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
27227c478bd9Sstevel@tonic-gate 	}
27237c478bd9Sstevel@tonic-gate }
27247c478bd9Sstevel@tonic-gate 
27257c478bd9Sstevel@tonic-gate 
27267c478bd9Sstevel@tonic-gate /*
27277c478bd9Sstevel@tonic-gate  * callback functions for interrupt IN pipe
27287c478bd9Sstevel@tonic-gate  */
27297c478bd9Sstevel@tonic-gate static void
27307c478bd9Sstevel@tonic-gate ugen_epx_intr_IN_req_cb(usb_pipe_handle_t ph, usb_intr_req_t *reqp)
27317c478bd9Sstevel@tonic-gate {
27327c478bd9Sstevel@tonic-gate 	ugen_state_t *ugenp = (ugen_state_t *)reqp->intr_client_private;
27337c478bd9Sstevel@tonic-gate 	ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
27347c478bd9Sstevel@tonic-gate 
27357c478bd9Sstevel@tonic-gate 	if (epp == NULL) {
27367c478bd9Sstevel@tonic-gate 		/* pipe is closing */
27377c478bd9Sstevel@tonic-gate 
27387c478bd9Sstevel@tonic-gate 		goto done;
27397c478bd9Sstevel@tonic-gate 	}
27407c478bd9Sstevel@tonic-gate 
27417c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
27427c478bd9Sstevel@tonic-gate 
27437c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
27447c478bd9Sstevel@tonic-gate 	    "ugen_epx_intr_IN_req_cb:\n\t"
27457c478bd9Sstevel@tonic-gate 	    "epp=0x%p state=0x%x ph=0x%p reqp=0x%p cr=%d cb=0x%x len=%d",
27467c478bd9Sstevel@tonic-gate 	    epp, epp->ep_state, ph, reqp, reqp->intr_completion_reason,
27477c478bd9Sstevel@tonic-gate 	    reqp->intr_cb_flags,
27487c478bd9Sstevel@tonic-gate 	    (reqp->intr_data == NULL) ? 0 :
27497c478bd9Sstevel@tonic-gate 	    reqp->intr_data->b_wptr - reqp->intr_data->b_rptr);
27507c478bd9Sstevel@tonic-gate 
27517c478bd9Sstevel@tonic-gate 	ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
27527c478bd9Sstevel@tonic-gate 
27537c478bd9Sstevel@tonic-gate 	if (epp->ep_data && reqp->intr_data) {
27547c478bd9Sstevel@tonic-gate 		mblk_t *mp;
27557c478bd9Sstevel@tonic-gate 
27567c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
27577c478bd9Sstevel@tonic-gate 		    "ep%x coalesce data", epp->ep_descr.bEndpointAddress);
27587c478bd9Sstevel@tonic-gate 
27597c478bd9Sstevel@tonic-gate 		/* coalesce the data into one mblk */
27607c478bd9Sstevel@tonic-gate 		epp->ep_data->b_cont = reqp->intr_data;
27617c478bd9Sstevel@tonic-gate 		if ((mp = msgpullup(epp->ep_data, -1)) != NULL) {
27627c478bd9Sstevel@tonic-gate 			reqp->intr_data = NULL;
27637c478bd9Sstevel@tonic-gate 			freemsg(epp->ep_data);
27647c478bd9Sstevel@tonic-gate 			epp->ep_data = mp;
27657c478bd9Sstevel@tonic-gate 		} else {
27667c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
27677c478bd9Sstevel@tonic-gate 			    "msgpullup failed, discard data");
27687c478bd9Sstevel@tonic-gate 			epp->ep_data->b_cont = NULL;
27697c478bd9Sstevel@tonic-gate 		}
27707c478bd9Sstevel@tonic-gate 	} else if (reqp->intr_data) {
27717c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
27727c478bd9Sstevel@tonic-gate 		    "setting ep_data");
27737c478bd9Sstevel@tonic-gate 
27747c478bd9Sstevel@tonic-gate 		epp->ep_data = reqp->intr_data;
27757c478bd9Sstevel@tonic-gate 		reqp->intr_data = NULL;
27767c478bd9Sstevel@tonic-gate 	}
27777c478bd9Sstevel@tonic-gate 
27787c478bd9Sstevel@tonic-gate 	switch (reqp->intr_completion_reason) {
27797c478bd9Sstevel@tonic-gate 	case USB_CR_OK:
27807c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
27817c478bd9Sstevel@tonic-gate 
27827c478bd9Sstevel@tonic-gate 		break;
27837c478bd9Sstevel@tonic-gate 	case USB_CR_PIPE_RESET:
27847c478bd9Sstevel@tonic-gate 	case USB_CR_STOPPED_POLLING:
27857c478bd9Sstevel@tonic-gate 
27867c478bd9Sstevel@tonic-gate 		break;
27877c478bd9Sstevel@tonic-gate 	default:
27887c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status =
27897c478bd9Sstevel@tonic-gate 		    ugen_cr2lcstat(reqp->intr_completion_reason);
27907c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
27917c478bd9Sstevel@tonic-gate 		    "ugen_exp_intr_cb_req: lcmd_status=0x%x",
27927c478bd9Sstevel@tonic-gate 		    epp->ep_lcmd_status);
27937c478bd9Sstevel@tonic-gate 
27947c478bd9Sstevel@tonic-gate 		break;
27957c478bd9Sstevel@tonic-gate 	}
27967c478bd9Sstevel@tonic-gate 
27977c478bd9Sstevel@tonic-gate 	/* any non-zero completion reason stops polling */
27987c478bd9Sstevel@tonic-gate 	if ((reqp->intr_completion_reason) ||
27997c478bd9Sstevel@tonic-gate 	    (epp->ep_one_xfer)) {
28007c478bd9Sstevel@tonic-gate 		epp->ep_state &= ~(UGEN_EP_STATE_INTR_IN_POLLING_ON |
28017c478bd9Sstevel@tonic-gate 				    UGEN_EP_STATE_INTR_IN_POLLING_IS_STOPPED);
28027c478bd9Sstevel@tonic-gate 	}
28037c478bd9Sstevel@tonic-gate 
28047c478bd9Sstevel@tonic-gate 	/* is there a poll pending? should we stop polling? */
28057c478bd9Sstevel@tonic-gate 	if (epp->ep_data) {
28067c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
28077c478bd9Sstevel@tonic-gate 		    "ugen_epx_intr_IN_req_cb: data len=0x%x",
28087c478bd9Sstevel@tonic-gate 		    epp->ep_data->b_wptr - epp->ep_data->b_rptr);
28097c478bd9Sstevel@tonic-gate 
28107c478bd9Sstevel@tonic-gate 		ugen_epx_intr_IN_poll_wakeup(ugenp, epp);
28117c478bd9Sstevel@tonic-gate 
28127c478bd9Sstevel@tonic-gate 		/* if there is no space left, stop polling */
28137c478bd9Sstevel@tonic-gate 		if (epp->ep_data &&
28147c478bd9Sstevel@tonic-gate 		    ((epp->ep_data->b_wptr - epp->ep_data->b_rptr) >=
28157c478bd9Sstevel@tonic-gate 		    epp->ep_buf_limit)) {
28167c478bd9Sstevel@tonic-gate 			ugen_epx_intr_IN_stop_polling(ugenp, epp);
28177c478bd9Sstevel@tonic-gate 		}
28187c478bd9Sstevel@tonic-gate 	}
28197c478bd9Sstevel@tonic-gate 
28207c478bd9Sstevel@tonic-gate 	if (reqp->intr_completion_reason && epp->ep_bp) {
28217c478bd9Sstevel@tonic-gate 		bioerror(epp->ep_bp, EIO);
28227c478bd9Sstevel@tonic-gate 		epp->ep_done++;
28237c478bd9Sstevel@tonic-gate 		cv_signal(&epp->ep_wait_cv);
28247c478bd9Sstevel@tonic-gate 
28257c478bd9Sstevel@tonic-gate 	/* can we satisfy the read now */
28267c478bd9Sstevel@tonic-gate 	} else if (epp->ep_data && epp->ep_bp &&
28277c478bd9Sstevel@tonic-gate 	    (!epp->ep_done || epp->ep_one_xfer)) {
28287c478bd9Sstevel@tonic-gate 		boolean_t wait;
28297c478bd9Sstevel@tonic-gate 
28307c478bd9Sstevel@tonic-gate 		if ((ugen_epx_intr_IN_req(ugenp, epp, epp->ep_bp, &wait) ==
28317c478bd9Sstevel@tonic-gate 		    USB_SUCCESS) && (wait == B_FALSE)) {
28327c478bd9Sstevel@tonic-gate 			epp->ep_done++;
28337c478bd9Sstevel@tonic-gate 			cv_signal(&epp->ep_wait_cv);
28347c478bd9Sstevel@tonic-gate 		}
28357c478bd9Sstevel@tonic-gate 	}
28367c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
28377c478bd9Sstevel@tonic-gate 
28387c478bd9Sstevel@tonic-gate done:
28397c478bd9Sstevel@tonic-gate 	usb_free_intr_req(reqp);
28407c478bd9Sstevel@tonic-gate }
28417c478bd9Sstevel@tonic-gate 
28427c478bd9Sstevel@tonic-gate 
28437c478bd9Sstevel@tonic-gate /*
28447c478bd9Sstevel@tonic-gate  * handle intr OUT xfers
28457c478bd9Sstevel@tonic-gate  */
28467c478bd9Sstevel@tonic-gate static int
28477c478bd9Sstevel@tonic-gate ugen_epx_intr_OUT_req(ugen_state_t *ugenp, ugen_ep_t *epp,
28487c478bd9Sstevel@tonic-gate     struct buf *bp, boolean_t *wait)
28497c478bd9Sstevel@tonic-gate {
28507c478bd9Sstevel@tonic-gate 	int	rval = USB_SUCCESS;
28517c478bd9Sstevel@tonic-gate 	usb_intr_req_t	*reqp;
28527c478bd9Sstevel@tonic-gate 
28537c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
28547c478bd9Sstevel@tonic-gate 	    "ugen_epx_intr_OUT_req: epp=0x%p state=0x%x bp=0x%p",
28557c478bd9Sstevel@tonic-gate 	    epp, epp->ep_state, bp);
28567c478bd9Sstevel@tonic-gate 
28577c478bd9Sstevel@tonic-gate 	reqp = usb_alloc_intr_req(ugenp->ug_dip, bp->b_bcount,
28587c478bd9Sstevel@tonic-gate 					USB_FLAGS_NOSLEEP);
28597c478bd9Sstevel@tonic-gate 	if (reqp == NULL) {
28607c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_NO_RESOURCES;
28617c478bd9Sstevel@tonic-gate 
28627c478bd9Sstevel@tonic-gate 		return (USB_NO_RESOURCES);
28637c478bd9Sstevel@tonic-gate 	}
28647c478bd9Sstevel@tonic-gate 
28657c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_XS_OPEN);
28667c478bd9Sstevel@tonic-gate 
28677c478bd9Sstevel@tonic-gate 	reqp->intr_timeout	= ugen_intr_timeout;
28687c478bd9Sstevel@tonic-gate 	reqp->intr_client_private = (usb_opaque_t)ugenp;
28697c478bd9Sstevel@tonic-gate 	reqp->intr_len		= bp->b_bcount;
28707c478bd9Sstevel@tonic-gate 	reqp->intr_attributes	= USB_ATTRS_AUTOCLEARING;
28717c478bd9Sstevel@tonic-gate 	reqp->intr_cb		= ugen_epx_intr_OUT_req_cb;
28727c478bd9Sstevel@tonic-gate 	reqp->intr_exc_cb	= ugen_epx_intr_OUT_req_cb;
28737c478bd9Sstevel@tonic-gate 
28747c478bd9Sstevel@tonic-gate 	/* copy data from bp */
28757c478bd9Sstevel@tonic-gate 	bcopy(epp->ep_bp->b_un.b_addr, reqp->intr_data->b_rptr,
28767c478bd9Sstevel@tonic-gate 							bp->b_bcount);
28777c478bd9Sstevel@tonic-gate 	reqp->intr_data->b_wptr += bp->b_bcount;
28787c478bd9Sstevel@tonic-gate 
28797c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
28807c478bd9Sstevel@tonic-gate 	if ((rval = usb_pipe_intr_xfer(epp->ep_ph, reqp,
28817c478bd9Sstevel@tonic-gate 	    USB_FLAGS_NOSLEEP)) != USB_SUCCESS) {
28827c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
28837c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status =
28847c478bd9Sstevel@tonic-gate 		    ugen_cr2lcstat(reqp->intr_completion_reason);
28857c478bd9Sstevel@tonic-gate 		usb_free_intr_req(reqp);
28867c478bd9Sstevel@tonic-gate 		bioerror(bp, EIO);
28877c478bd9Sstevel@tonic-gate 	} else {
28887c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
28897c478bd9Sstevel@tonic-gate 	}
28907c478bd9Sstevel@tonic-gate 	*wait = (rval == USB_SUCCESS) ? B_TRUE : B_FALSE;
28917c478bd9Sstevel@tonic-gate 
28927c478bd9Sstevel@tonic-gate 	return (rval);
28937c478bd9Sstevel@tonic-gate }
28947c478bd9Sstevel@tonic-gate 
28957c478bd9Sstevel@tonic-gate 
28967c478bd9Sstevel@tonic-gate /*
28977c478bd9Sstevel@tonic-gate  * callback functions for interrupt OUT pipe
28987c478bd9Sstevel@tonic-gate  */
28997c478bd9Sstevel@tonic-gate static void
29007c478bd9Sstevel@tonic-gate ugen_epx_intr_OUT_req_cb(usb_pipe_handle_t ph, usb_intr_req_t *reqp)
29017c478bd9Sstevel@tonic-gate {
29027c478bd9Sstevel@tonic-gate 	ugen_state_t *ugenp = (ugen_state_t *)reqp->intr_client_private;
29037c478bd9Sstevel@tonic-gate 	ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
29047c478bd9Sstevel@tonic-gate 
29057c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
29067c478bd9Sstevel@tonic-gate 	    "ugen_epx_intr_OUT_req_cb: ph=0x%p reqp=0x%p cr=%d cb=0x%x",
29077c478bd9Sstevel@tonic-gate 	    ph, reqp, reqp->intr_completion_reason, reqp->intr_cb_flags);
29087c478bd9Sstevel@tonic-gate 
29097c478bd9Sstevel@tonic-gate 	ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
29107c478bd9Sstevel@tonic-gate 
29117c478bd9Sstevel@tonic-gate 	/* epp might be NULL if we are closing the pipe */
29127c478bd9Sstevel@tonic-gate 	if (epp) {
29137c478bd9Sstevel@tonic-gate 		int len;
29147c478bd9Sstevel@tonic-gate 
29157c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
29167c478bd9Sstevel@tonic-gate 		if (epp->ep_bp) {
29177c478bd9Sstevel@tonic-gate 			len = min(reqp->intr_data->b_wptr -
29187c478bd9Sstevel@tonic-gate 			    reqp->intr_data->b_rptr, epp->ep_bp->b_bcount);
29197c478bd9Sstevel@tonic-gate 
29207c478bd9Sstevel@tonic-gate 			epp->ep_bp->b_resid = epp->ep_bp->b_bcount - len;
29217c478bd9Sstevel@tonic-gate 
29227c478bd9Sstevel@tonic-gate 			switch (reqp->intr_completion_reason) {
29237c478bd9Sstevel@tonic-gate 			case USB_CR_OK:
29247c478bd9Sstevel@tonic-gate 				epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
29257c478bd9Sstevel@tonic-gate 
29267c478bd9Sstevel@tonic-gate 				break;
29277c478bd9Sstevel@tonic-gate 			case USB_CR_PIPE_RESET:
29287c478bd9Sstevel@tonic-gate 
29297c478bd9Sstevel@tonic-gate 				break;
29307c478bd9Sstevel@tonic-gate 			default:
29317c478bd9Sstevel@tonic-gate 				epp->ep_lcmd_status =
29327c478bd9Sstevel@tonic-gate 				    ugen_cr2lcstat(
29337c478bd9Sstevel@tonic-gate 				    reqp->intr_completion_reason);
29347c478bd9Sstevel@tonic-gate 				bioerror(epp->ep_bp, EIO);
29357c478bd9Sstevel@tonic-gate 			}
29367c478bd9Sstevel@tonic-gate 		}
29377c478bd9Sstevel@tonic-gate 		epp->ep_done++;
29387c478bd9Sstevel@tonic-gate 		cv_signal(&epp->ep_wait_cv);
29397c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
29407c478bd9Sstevel@tonic-gate 	}
29417c478bd9Sstevel@tonic-gate 
29427c478bd9Sstevel@tonic-gate 	usb_free_intr_req(reqp);
29437c478bd9Sstevel@tonic-gate }
29447c478bd9Sstevel@tonic-gate 
29457c478bd9Sstevel@tonic-gate 
29467c478bd9Sstevel@tonic-gate /*
29477c478bd9Sstevel@tonic-gate  * Endpoint status node management
29487c478bd9Sstevel@tonic-gate  *
29497c478bd9Sstevel@tonic-gate  * open/close an endpoint status node.
29507c478bd9Sstevel@tonic-gate  *
29517c478bd9Sstevel@tonic-gate  * Return values: errno
29527c478bd9Sstevel@tonic-gate  */
29537c478bd9Sstevel@tonic-gate static int
29547c478bd9Sstevel@tonic-gate ugen_eps_open(ugen_state_t *ugenp, dev_t dev, int flag)
29557c478bd9Sstevel@tonic-gate {
29567c478bd9Sstevel@tonic-gate 	ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
29577c478bd9Sstevel@tonic-gate 	int rval = EBUSY;
29587c478bd9Sstevel@tonic-gate 
29597c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
29607c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_STAT, ugenp->ug_log_hdl,
29617c478bd9Sstevel@tonic-gate 	    "ugen_eps_open: dev=0x%lx flag=0x%x state=0x%x",
29627c478bd9Sstevel@tonic-gate 	    dev, flag, epp->ep_state);
29637c478bd9Sstevel@tonic-gate 
29647c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_ACTIVE);
29657c478bd9Sstevel@tonic-gate 
29667c478bd9Sstevel@tonic-gate 	/* only one open at the time */
29677c478bd9Sstevel@tonic-gate 	if ((epp->ep_state & UGEN_EP_STATE_STAT_OPEN) == 0) {
29687c478bd9Sstevel@tonic-gate 		epp->ep_state |= UGEN_EP_STATE_STAT_OPEN;
29697c478bd9Sstevel@tonic-gate 		epp->ep_stat_oflag = flag;
29707c478bd9Sstevel@tonic-gate 		rval = 0;
29717c478bd9Sstevel@tonic-gate 	}
29727c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
29737c478bd9Sstevel@tonic-gate 
29747c478bd9Sstevel@tonic-gate 	return (rval);
29757c478bd9Sstevel@tonic-gate }
29767c478bd9Sstevel@tonic-gate 
29777c478bd9Sstevel@tonic-gate 
29787c478bd9Sstevel@tonic-gate /*
29797c478bd9Sstevel@tonic-gate  * close endpoint status
29807c478bd9Sstevel@tonic-gate  */
29817c478bd9Sstevel@tonic-gate static void
29827c478bd9Sstevel@tonic-gate ugen_eps_close(ugen_state_t *ugenp, dev_t dev, int flag)
29837c478bd9Sstevel@tonic-gate {
29847c478bd9Sstevel@tonic-gate 	ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
29857c478bd9Sstevel@tonic-gate 
29867c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
29877c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_STAT, ugenp->ug_log_hdl,
29887c478bd9Sstevel@tonic-gate 	    "ugen_eps_close: dev=0x%lx flag=0x%x state=0x%x",
29897c478bd9Sstevel@tonic-gate 	    dev, flag, epp->ep_state);
29907c478bd9Sstevel@tonic-gate 
29917c478bd9Sstevel@tonic-gate 	epp->ep_state &= ~(UGEN_EP_STATE_STAT_OPEN |
29927c478bd9Sstevel@tonic-gate 			UGEN_EP_STATE_INTR_IN_POLL_PENDING);
29937c478bd9Sstevel@tonic-gate 	epp->ep_one_xfer = B_FALSE;
29947c478bd9Sstevel@tonic-gate 
29957c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_STAT, ugenp->ug_log_hdl,
29967c478bd9Sstevel@tonic-gate 	    "ugen_eps_close: state=0x%x", epp->ep_state);
29977c478bd9Sstevel@tonic-gate 
29987c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_ACTIVE);
29997c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
30007c478bd9Sstevel@tonic-gate }
30017c478bd9Sstevel@tonic-gate 
30027c478bd9Sstevel@tonic-gate 
30037c478bd9Sstevel@tonic-gate /*
30047c478bd9Sstevel@tonic-gate  * return status info
30057c478bd9Sstevel@tonic-gate  *
30067c478bd9Sstevel@tonic-gate  * Return values: errno
30077c478bd9Sstevel@tonic-gate  */
30087c478bd9Sstevel@tonic-gate static int
30097c478bd9Sstevel@tonic-gate ugen_eps_req(ugen_state_t *ugenp, struct buf *bp)
30107c478bd9Sstevel@tonic-gate {
30117c478bd9Sstevel@tonic-gate 	ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, bp->b_edev)];
30127c478bd9Sstevel@tonic-gate 
30137c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
30147c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
30157c478bd9Sstevel@tonic-gate 	    "ugen_eps_req: bp=0x%p lcmd_status=0x%x bcount=%lu",
30167c478bd9Sstevel@tonic-gate 	    bp, epp->ep_lcmd_status, bp->b_bcount);
30177c478bd9Sstevel@tonic-gate 
30187c478bd9Sstevel@tonic-gate 	if (bp->b_flags & B_READ) {
30197c478bd9Sstevel@tonic-gate 		int len = min(sizeof (epp->ep_lcmd_status), bp->b_bcount);
30207c478bd9Sstevel@tonic-gate 		if (len) {
30217c478bd9Sstevel@tonic-gate 			bcopy(&epp->ep_lcmd_status, bp->b_un.b_addr, len);
30227c478bd9Sstevel@tonic-gate 		}
30237c478bd9Sstevel@tonic-gate 		bp->b_resid = bp->b_bcount - len;
30247c478bd9Sstevel@tonic-gate 	} else {
30257c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
30267c478bd9Sstevel@tonic-gate 		    "ugen_eps_req: control=0x%x",
30277c478bd9Sstevel@tonic-gate 		    *((char *)(bp->b_un.b_addr)));
30287c478bd9Sstevel@tonic-gate 
30297c478bd9Sstevel@tonic-gate 		if (epp->ep_state & UGEN_EP_STATE_XFER_OPEN) {
30307c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
30317c478bd9Sstevel@tonic-gate 			    "ugen_eps_req: cannot change one xfer mode if "
30327c478bd9Sstevel@tonic-gate 			    "endpoint is open");
30337c478bd9Sstevel@tonic-gate 
30347c478bd9Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
30357c478bd9Sstevel@tonic-gate 
30367c478bd9Sstevel@tonic-gate 			return (EINVAL);
30377c478bd9Sstevel@tonic-gate 		}
30387c478bd9Sstevel@tonic-gate 
30397c478bd9Sstevel@tonic-gate 		if ((epp->ep_descr.bmAttributes & USB_EP_ATTR_INTR) &&
30407c478bd9Sstevel@tonic-gate 		    (epp->ep_descr.bEndpointAddress & USB_EP_DIR_IN)) {
30417c478bd9Sstevel@tonic-gate 			epp->ep_one_xfer = (*((char *)(bp->b_un.b_addr)) &
30427c478bd9Sstevel@tonic-gate 			    USB_EP_INTR_ONE_XFER) ? B_TRUE : B_FALSE;
30437c478bd9Sstevel@tonic-gate 		} else {
30447c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
30457c478bd9Sstevel@tonic-gate 			    "ugen_eps_req: not an interrupt endpoint");
30467c478bd9Sstevel@tonic-gate 
30477c478bd9Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
30487c478bd9Sstevel@tonic-gate 
30497c478bd9Sstevel@tonic-gate 			return (EINVAL);
30507c478bd9Sstevel@tonic-gate 		}
30517c478bd9Sstevel@tonic-gate 
30527c478bd9Sstevel@tonic-gate 		bp->b_resid = bp->b_bcount - 1;
30537c478bd9Sstevel@tonic-gate 	}
30547c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
30557c478bd9Sstevel@tonic-gate 
30567c478bd9Sstevel@tonic-gate 	return (0);
30577c478bd9Sstevel@tonic-gate }
30587c478bd9Sstevel@tonic-gate 
30597c478bd9Sstevel@tonic-gate 
30607c478bd9Sstevel@tonic-gate /*
30617c478bd9Sstevel@tonic-gate  * device status node management
30627c478bd9Sstevel@tonic-gate  */
30637c478bd9Sstevel@tonic-gate static int
30647c478bd9Sstevel@tonic-gate ugen_ds_init(ugen_state_t *ugenp)
30657c478bd9Sstevel@tonic-gate {
30667c478bd9Sstevel@tonic-gate 	cv_init(&ugenp->ug_ds.dev_wait_cv, NULL, CV_DRIVER, NULL);
30677c478bd9Sstevel@tonic-gate 
30687c478bd9Sstevel@tonic-gate 	/* Create devstat minor node for this instance */
30697c478bd9Sstevel@tonic-gate 	if (ugen_ds_minor_nodes_create(ugenp) != USB_SUCCESS) {
30707c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
30717c478bd9Sstevel@tonic-gate 		    "ugen_create_dev_stat_minor_nodes failed");
30727c478bd9Sstevel@tonic-gate 
30737c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
30747c478bd9Sstevel@tonic-gate 	}
30757c478bd9Sstevel@tonic-gate 
30767c478bd9Sstevel@tonic-gate 
30777c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
30787c478bd9Sstevel@tonic-gate }
30797c478bd9Sstevel@tonic-gate 
30807c478bd9Sstevel@tonic-gate 
30817c478bd9Sstevel@tonic-gate static void
30827c478bd9Sstevel@tonic-gate ugen_ds_destroy(ugen_state_t *ugenp)
30837c478bd9Sstevel@tonic-gate {
30847c478bd9Sstevel@tonic-gate 	cv_destroy(&ugenp->ug_ds.dev_wait_cv);
30857c478bd9Sstevel@tonic-gate }
30867c478bd9Sstevel@tonic-gate 
30877c478bd9Sstevel@tonic-gate 
30887c478bd9Sstevel@tonic-gate /*
30897c478bd9Sstevel@tonic-gate  * open devstat minor node
30907c478bd9Sstevel@tonic-gate  *
30917c478bd9Sstevel@tonic-gate  * Return values: errno
30927c478bd9Sstevel@tonic-gate  */
30937c478bd9Sstevel@tonic-gate static int
30947c478bd9Sstevel@tonic-gate ugen_ds_open(ugen_state_t *ugenp, dev_t dev, int flag)
30957c478bd9Sstevel@tonic-gate {
30967c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
30977c478bd9Sstevel@tonic-gate 	    "ugen_ds_open: dev=0x%lx flag=0x%x", dev, flag);
30987c478bd9Sstevel@tonic-gate 
30997c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
31007c478bd9Sstevel@tonic-gate 	if ((ugenp->ug_ds.dev_stat & UGEN_DEV_STATUS_ACTIVE) == 0) {
31017c478bd9Sstevel@tonic-gate 		/*
31027c478bd9Sstevel@tonic-gate 		 * first read on device node should return status
31037c478bd9Sstevel@tonic-gate 		 */
31047c478bd9Sstevel@tonic-gate 		ugenp->ug_ds.dev_stat |= UGEN_DEV_STATUS_CHANGED |
31057c478bd9Sstevel@tonic-gate 						UGEN_DEV_STATUS_ACTIVE;
31067c478bd9Sstevel@tonic-gate 		ugenp->ug_ds.dev_oflag = flag;
31077c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
31087c478bd9Sstevel@tonic-gate 
31097c478bd9Sstevel@tonic-gate 		return (0);
31107c478bd9Sstevel@tonic-gate 	} else {
31117c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
31127c478bd9Sstevel@tonic-gate 
31137c478bd9Sstevel@tonic-gate 		return (EBUSY);
31147c478bd9Sstevel@tonic-gate 	}
31157c478bd9Sstevel@tonic-gate }
31167c478bd9Sstevel@tonic-gate 
31177c478bd9Sstevel@tonic-gate 
31187c478bd9Sstevel@tonic-gate static void
31197c478bd9Sstevel@tonic-gate ugen_ds_close(ugen_state_t *ugenp, dev_t dev, int flag)
31207c478bd9Sstevel@tonic-gate {
31217c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
31227c478bd9Sstevel@tonic-gate 	    "ugen_ds_close: dev=0x%lx flag=0x%x", dev, flag);
31237c478bd9Sstevel@tonic-gate 
31247c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
31257c478bd9Sstevel@tonic-gate 	ugenp->ug_ds.dev_stat = UGEN_DEV_STATUS_INACTIVE;
31267c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
31277c478bd9Sstevel@tonic-gate }
31287c478bd9Sstevel@tonic-gate 
31297c478bd9Sstevel@tonic-gate 
31307c478bd9Sstevel@tonic-gate /*
31317c478bd9Sstevel@tonic-gate  * request for devstat
31327c478bd9Sstevel@tonic-gate  *
31337c478bd9Sstevel@tonic-gate  * Return values: errno
31347c478bd9Sstevel@tonic-gate  */
31357c478bd9Sstevel@tonic-gate static int
31367c478bd9Sstevel@tonic-gate ugen_ds_req(ugen_state_t *ugenp, struct buf *bp)
31377c478bd9Sstevel@tonic-gate {
31387c478bd9Sstevel@tonic-gate 	int len = min(sizeof (ugenp->ug_ds.dev_state), bp->b_bcount);
31397c478bd9Sstevel@tonic-gate 
31407c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
31417c478bd9Sstevel@tonic-gate 	    "ugen_ds_req: bp=0x%p", bp);
31427c478bd9Sstevel@tonic-gate 
31437c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
31447c478bd9Sstevel@tonic-gate 	if ((ugenp->ug_ds.dev_oflag & (FNDELAY | FNONBLOCK)) == 0) {
31457c478bd9Sstevel@tonic-gate 		while ((ugenp->ug_ds.dev_stat &
31467c478bd9Sstevel@tonic-gate 		    UGEN_DEV_STATUS_CHANGED) == 0) {
31477c478bd9Sstevel@tonic-gate 			if (cv_wait_sig(&ugenp->ug_ds.dev_wait_cv,
31487c478bd9Sstevel@tonic-gate 			    &ugenp->ug_mutex) <= 0) {
31497c478bd9Sstevel@tonic-gate 				mutex_exit(&ugenp->ug_mutex);
31507c478bd9Sstevel@tonic-gate 
31517c478bd9Sstevel@tonic-gate 				return (EINTR);
31527c478bd9Sstevel@tonic-gate 			}
31537c478bd9Sstevel@tonic-gate 		}
31547c478bd9Sstevel@tonic-gate 	} else if ((ugenp->ug_ds.dev_stat & UGEN_DEV_STATUS_CHANGED) ==
31557c478bd9Sstevel@tonic-gate 	    0) {
31567c478bd9Sstevel@tonic-gate 		bp->b_resid = bp->b_bcount;
31577c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
31587c478bd9Sstevel@tonic-gate 
31597c478bd9Sstevel@tonic-gate 		return (0);
31607c478bd9Sstevel@tonic-gate 	}
31617c478bd9Sstevel@tonic-gate 
31627c478bd9Sstevel@tonic-gate 	ugenp->ug_ds.dev_stat &= ~UGEN_DEV_STATUS_CHANGED;
31637c478bd9Sstevel@tonic-gate 	switch (ugenp->ug_dev_state) {
31647c478bd9Sstevel@tonic-gate 	case USB_DEV_ONLINE:
31657c478bd9Sstevel@tonic-gate 		ugenp->ug_ds.dev_state = USB_DEV_STAT_ONLINE;
31667c478bd9Sstevel@tonic-gate 
31677c478bd9Sstevel@tonic-gate 		break;
31687c478bd9Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
31697c478bd9Sstevel@tonic-gate 		ugenp->ug_ds.dev_state = USB_DEV_STAT_DISCONNECTED;
31707c478bd9Sstevel@tonic-gate 
31717c478bd9Sstevel@tonic-gate 		break;
31727c478bd9Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
31737c478bd9Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RESUME:
31747c478bd9Sstevel@tonic-gate 		ugenp->ug_ds.dev_state = USB_DEV_STAT_RESUMED;
31757c478bd9Sstevel@tonic-gate 
31767c478bd9Sstevel@tonic-gate 		break;
31777c478bd9Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
31787c478bd9Sstevel@tonic-gate 	default:
31797c478bd9Sstevel@tonic-gate 		ugenp->ug_ds.dev_state = USB_DEV_STAT_UNAVAILABLE;
31807c478bd9Sstevel@tonic-gate 
31817c478bd9Sstevel@tonic-gate 		break;
31827c478bd9Sstevel@tonic-gate 	}
31837c478bd9Sstevel@tonic-gate 
31847c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
31857c478bd9Sstevel@tonic-gate 	    "ugen_ds_req: dev_state=0x%x dev_stat=0x%x",
31867c478bd9Sstevel@tonic-gate 	    ugenp->ug_dev_state, ugenp->ug_ds.dev_stat);
31877c478bd9Sstevel@tonic-gate 
31887c478bd9Sstevel@tonic-gate 	bcopy(&ugenp->ug_ds.dev_state, bp->b_un.b_addr, len);
31897c478bd9Sstevel@tonic-gate 	bp->b_resid = bp->b_bcount - len;
31907c478bd9Sstevel@tonic-gate 
31917c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
31927c478bd9Sstevel@tonic-gate 
31937c478bd9Sstevel@tonic-gate 	return (0);
31947c478bd9Sstevel@tonic-gate }
31957c478bd9Sstevel@tonic-gate 
31967c478bd9Sstevel@tonic-gate 
31977c478bd9Sstevel@tonic-gate static void
31987c478bd9Sstevel@tonic-gate ugen_ds_change(ugen_state_t *ugenp)
31997c478bd9Sstevel@tonic-gate {
32007c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
32017c478bd9Sstevel@tonic-gate 	    "ugen_ds_change:");
32027c478bd9Sstevel@tonic-gate 
32037c478bd9Sstevel@tonic-gate 	ugenp->ug_ds.dev_stat |= UGEN_DEV_STATUS_CHANGED;
32047c478bd9Sstevel@tonic-gate 	cv_signal(&ugenp->ug_ds.dev_wait_cv);
32057c478bd9Sstevel@tonic-gate }
32067c478bd9Sstevel@tonic-gate 
32077c478bd9Sstevel@tonic-gate 
32087c478bd9Sstevel@tonic-gate /*
32097c478bd9Sstevel@tonic-gate  * poll management
32107c478bd9Sstevel@tonic-gate  */
32117c478bd9Sstevel@tonic-gate static void
32127c478bd9Sstevel@tonic-gate ugen_ds_poll_wakeup(ugen_state_t *ugenp)
32137c478bd9Sstevel@tonic-gate {
32147c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
32157c478bd9Sstevel@tonic-gate 	    "ugen_ds_poll_wakeup:");
32167c478bd9Sstevel@tonic-gate 
32177c478bd9Sstevel@tonic-gate 	if (ugenp->ug_ds.dev_stat & UGEN_DEV_STATUS_POLL_PENDING) {
32187c478bd9Sstevel@tonic-gate 		struct pollhead *phpp = &ugenp->ug_ds.dev_pollhead;
32197c478bd9Sstevel@tonic-gate 		ugenp->ug_ds.dev_stat &= ~UGEN_DEV_STATUS_POLL_PENDING;
32207c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
32217c478bd9Sstevel@tonic-gate 		pollwakeup(phpp, POLLIN);
32227c478bd9Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
32237c478bd9Sstevel@tonic-gate 	}
32247c478bd9Sstevel@tonic-gate }
32257c478bd9Sstevel@tonic-gate 
32267c478bd9Sstevel@tonic-gate 
32277c478bd9Sstevel@tonic-gate /*
32287c478bd9Sstevel@tonic-gate  * minor node management:
32297c478bd9Sstevel@tonic-gate  */
32307c478bd9Sstevel@tonic-gate static int
32317c478bd9Sstevel@tonic-gate ugen_ds_minor_nodes_create(ugen_state_t *ugenp)
32327c478bd9Sstevel@tonic-gate {
32337c478bd9Sstevel@tonic-gate 	char	node_name[32];
32347c478bd9Sstevel@tonic-gate 	int	vid = ugenp->ug_dev_data->dev_descr->idVendor;
32357c478bd9Sstevel@tonic-gate 	int	pid = ugenp->ug_dev_data->dev_descr->idProduct;
32367c478bd9Sstevel@tonic-gate 	minor_t	minor;
32377c478bd9Sstevel@tonic-gate 	int	minor_index;
32387c478bd9Sstevel@tonic-gate 	int	owns_device = (usb_owns_device(ugenp->ug_dip) ?
32397c478bd9Sstevel@tonic-gate 						UGEN_OWNS_DEVICE : 0);
32407c478bd9Sstevel@tonic-gate 
32417c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
32427c478bd9Sstevel@tonic-gate 	    "ugen_ds_minor_nodes_create: idx shift=%d inst shift=%d",
32437c478bd9Sstevel@tonic-gate 	    UGEN_MINOR_IDX_SHIFT(ugenp),
32447c478bd9Sstevel@tonic-gate 	    UGEN_MINOR_INSTANCE_SHIFT(ugenp));
32457c478bd9Sstevel@tonic-gate 
32467c478bd9Sstevel@tonic-gate 	if (ugenp->ug_instance >= UGEN_MINOR_INSTANCE_LIMIT(ugenp)) {
32477c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L0(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
32487c478bd9Sstevel@tonic-gate 		    "instance number too high (%d)", ugenp->ug_instance);
32497c478bd9Sstevel@tonic-gate 
32507c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
32517c478bd9Sstevel@tonic-gate 	}
32527c478bd9Sstevel@tonic-gate 
32537c478bd9Sstevel@tonic-gate 	/* create devstat minor node */
32547c478bd9Sstevel@tonic-gate 	if (owns_device) {
32557c478bd9Sstevel@tonic-gate 		(void) sprintf(node_name, "%x.%x.devstat", vid, pid);
32567c478bd9Sstevel@tonic-gate 	} else {
32577c478bd9Sstevel@tonic-gate 		(void) sprintf(node_name, "%x.%x.if%ddevstat", vid, pid,
32587c478bd9Sstevel@tonic-gate 		    ugenp->ug_dev_data->dev_curr_if);
32597c478bd9Sstevel@tonic-gate 	}
32607c478bd9Sstevel@tonic-gate 
32617c478bd9Sstevel@tonic-gate 	minor_index = ugen_minor_index_create(ugenp,
32627c478bd9Sstevel@tonic-gate 	    (UGEN_MINOR_DEV_STAT_NODE | owns_device) <<
32637c478bd9Sstevel@tonic-gate 	    UGEN_MINOR_IDX_SHIFT(ugenp));
32647c478bd9Sstevel@tonic-gate 
32657c478bd9Sstevel@tonic-gate 	if (minor_index < 0) {
32667c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L0(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
32677c478bd9Sstevel@tonic-gate 		    "too many minor nodes");
32687c478bd9Sstevel@tonic-gate 
32697c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
32707c478bd9Sstevel@tonic-gate 	}
32717c478bd9Sstevel@tonic-gate 	minor = (minor_index << UGEN_MINOR_IDX_SHIFT(ugenp)) |
32727c478bd9Sstevel@tonic-gate 	    ugenp->ug_instance << UGEN_MINOR_INSTANCE_SHIFT(ugenp);
32737c478bd9Sstevel@tonic-gate 
32747c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
32757c478bd9Sstevel@tonic-gate 	    "minor=0x%x minor_index=%d name=%s",
32767c478bd9Sstevel@tonic-gate 	    minor, minor_index, node_name);
32777c478bd9Sstevel@tonic-gate 
32787c478bd9Sstevel@tonic-gate 	ASSERT(minor < L_MAXMIN);
32797c478bd9Sstevel@tonic-gate 
32807c478bd9Sstevel@tonic-gate 	if ((ddi_create_minor_node(ugenp->ug_dip, node_name,
32817c478bd9Sstevel@tonic-gate 	    S_IFCHR, minor, DDI_NT_UGEN, 0)) != DDI_SUCCESS) {
32827c478bd9Sstevel@tonic-gate 
32837c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
32847c478bd9Sstevel@tonic-gate 	}
32857c478bd9Sstevel@tonic-gate 
32867c478bd9Sstevel@tonic-gate 	ugen_store_devt(ugenp, minor);
32877c478bd9Sstevel@tonic-gate 
32887c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
32897c478bd9Sstevel@tonic-gate }
32907c478bd9Sstevel@tonic-gate 
32917c478bd9Sstevel@tonic-gate 
32927c478bd9Sstevel@tonic-gate /*
32937c478bd9Sstevel@tonic-gate  * utility functions:
32947c478bd9Sstevel@tonic-gate  *
32957c478bd9Sstevel@tonic-gate  * conversion from completion reason to  USB_LC_STAT_*
32967c478bd9Sstevel@tonic-gate  */
32977c478bd9Sstevel@tonic-gate static struct ugen_cr2lcstat_entry {
32987c478bd9Sstevel@tonic-gate 	int	cr;
32997c478bd9Sstevel@tonic-gate 	int	lcstat;
33007c478bd9Sstevel@tonic-gate } ugen_cr2lcstat_table[] = {
33017c478bd9Sstevel@tonic-gate 	{ USB_CR_OK,			USB_LC_STAT_NOERROR	},
33027c478bd9Sstevel@tonic-gate 	{ USB_CR_CRC,			USB_LC_STAT_CRC		},
33037c478bd9Sstevel@tonic-gate 	{ USB_CR_BITSTUFFING,		USB_LC_STAT_BITSTUFFING },
33047c478bd9Sstevel@tonic-gate 	{ USB_CR_DATA_TOGGLE_MM,	USB_LC_STAT_DATA_TOGGLE_MM },
33057c478bd9Sstevel@tonic-gate 	{ USB_CR_STALL,			USB_LC_STAT_STALL	},
33067c478bd9Sstevel@tonic-gate 	{ USB_CR_DEV_NOT_RESP,		USB_LC_STAT_DEV_NOT_RESP },
33077c478bd9Sstevel@tonic-gate 	{ USB_CR_PID_CHECKFAILURE,	USB_LC_STAT_PID_CHECKFAILURE },
33087c478bd9Sstevel@tonic-gate 	{ USB_CR_UNEXP_PID,		USB_LC_STAT_UNEXP_PID	},
33097c478bd9Sstevel@tonic-gate 	{ USB_CR_DATA_OVERRUN,		USB_LC_STAT_DATA_OVERRUN },
33107c478bd9Sstevel@tonic-gate 	{ USB_CR_DATA_UNDERRUN,		USB_LC_STAT_DATA_UNDERRUN },
33117c478bd9Sstevel@tonic-gate 	{ USB_CR_BUFFER_OVERRUN,	USB_LC_STAT_BUFFER_OVERRUN },
33127c478bd9Sstevel@tonic-gate 	{ USB_CR_BUFFER_UNDERRUN,	USB_LC_STAT_BUFFER_UNDERRUN },
33137c478bd9Sstevel@tonic-gate 	{ USB_CR_TIMEOUT,		USB_LC_STAT_TIMEOUT	},
33147c478bd9Sstevel@tonic-gate 	{ USB_CR_NOT_ACCESSED,		USB_LC_STAT_NOT_ACCESSED },
33157c478bd9Sstevel@tonic-gate 	{ USB_CR_NO_RESOURCES,		USB_LC_STAT_NO_BANDWIDTH },
33167c478bd9Sstevel@tonic-gate 	{ USB_CR_UNSPECIFIED_ERR,	USB_LC_STAT_UNSPECIFIED_ERR },
33177c478bd9Sstevel@tonic-gate 	{ USB_CR_STOPPED_POLLING,	USB_LC_STAT_HW_ERR	},
33187c478bd9Sstevel@tonic-gate 	{ USB_CR_PIPE_CLOSING,		USB_LC_STAT_UNSPECIFIED_ERR	},
33197c478bd9Sstevel@tonic-gate 	{ USB_CR_PIPE_RESET,		USB_LC_STAT_UNSPECIFIED_ERR	},
33207c478bd9Sstevel@tonic-gate 	{ USB_CR_NOT_SUPPORTED,		USB_LC_STAT_UNSPECIFIED_ERR },
33217c478bd9Sstevel@tonic-gate 	{ USB_CR_FLUSHED,		USB_LC_STAT_UNSPECIFIED_ERR }
33227c478bd9Sstevel@tonic-gate };
33237c478bd9Sstevel@tonic-gate 
33247c478bd9Sstevel@tonic-gate #define	UGEN_CR2LCSTAT_TABLE_SIZE (sizeof (ugen_cr2lcstat_table) / \
33257c478bd9Sstevel@tonic-gate 			sizeof (struct ugen_cr2lcstat_entry))
33267c478bd9Sstevel@tonic-gate static int
33277c478bd9Sstevel@tonic-gate ugen_cr2lcstat(int cr)
33287c478bd9Sstevel@tonic-gate {
33297c478bd9Sstevel@tonic-gate 	int i;
33307c478bd9Sstevel@tonic-gate 
33317c478bd9Sstevel@tonic-gate 	for (i = 0; i < UGEN_CR2LCSTAT_TABLE_SIZE; i++) {
33327c478bd9Sstevel@tonic-gate 		if (ugen_cr2lcstat_table[i].cr == cr) {
33337c478bd9Sstevel@tonic-gate 
33347c478bd9Sstevel@tonic-gate 			return (ugen_cr2lcstat_table[i].lcstat);
33357c478bd9Sstevel@tonic-gate 		}
33367c478bd9Sstevel@tonic-gate 	}
33377c478bd9Sstevel@tonic-gate 
33387c478bd9Sstevel@tonic-gate 	return (USB_LC_STAT_UNSPECIFIED_ERR);
33397c478bd9Sstevel@tonic-gate }
33407c478bd9Sstevel@tonic-gate 
33417c478bd9Sstevel@tonic-gate 
33427c478bd9Sstevel@tonic-gate /*
33437c478bd9Sstevel@tonic-gate  * create and lookup minor index
33447c478bd9Sstevel@tonic-gate  */
33457c478bd9Sstevel@tonic-gate static int
33467c478bd9Sstevel@tonic-gate ugen_minor_index_create(ugen_state_t *ugenp, ugen_minor_t minor)
33477c478bd9Sstevel@tonic-gate {
33487c478bd9Sstevel@tonic-gate 	int i;
33497c478bd9Sstevel@tonic-gate 
33507c478bd9Sstevel@tonic-gate 	/* check if already in the table */
33517c478bd9Sstevel@tonic-gate 	for (i = 1; i < ugenp->ug_minor_node_table_index; i++) {
33527c478bd9Sstevel@tonic-gate 		if (ugenp->ug_minor_node_table[i] == minor) {
33537c478bd9Sstevel@tonic-gate 
33547c478bd9Sstevel@tonic-gate 			return (-1);
33557c478bd9Sstevel@tonic-gate 		}
33567c478bd9Sstevel@tonic-gate 	}
33577c478bd9Sstevel@tonic-gate 	if (ugenp->ug_minor_node_table_index <
33587c478bd9Sstevel@tonic-gate 	    (ugenp->ug_minor_node_table_size/sizeof (ugen_minor_t))) {
33597c478bd9Sstevel@tonic-gate 		ugenp->ug_minor_node_table[ugenp->
33607c478bd9Sstevel@tonic-gate 				ug_minor_node_table_index] = minor;
33617c478bd9Sstevel@tonic-gate 
33627c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
33637c478bd9Sstevel@tonic-gate 		    "ugen_minor_index_create: %d: 0x%lx",
33647c478bd9Sstevel@tonic-gate 		    ugenp->ug_minor_node_table_index,
33657c478bd9Sstevel@tonic-gate 		    minor);
33667c478bd9Sstevel@tonic-gate 
33677c478bd9Sstevel@tonic-gate 		return (ugenp->ug_minor_node_table_index++);
33687c478bd9Sstevel@tonic-gate 	} else {
33697c478bd9Sstevel@tonic-gate 
33707c478bd9Sstevel@tonic-gate 		return (-1);
33717c478bd9Sstevel@tonic-gate 	}
33727c478bd9Sstevel@tonic-gate }
33737c478bd9Sstevel@tonic-gate 
33747c478bd9Sstevel@tonic-gate 
33757c478bd9Sstevel@tonic-gate static ugen_minor_t
33767c478bd9Sstevel@tonic-gate ugen_devt2minor(ugen_state_t *ugenp, dev_t dev)
33777c478bd9Sstevel@tonic-gate {
33787c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
3379*96603521Sfrits 	    "ugen_devt2minor: minorindex=%d, minor=0x%" PRIx64,
33807c478bd9Sstevel@tonic-gate 	    UGEN_MINOR_GET_IDX(ugenp, dev),
33817c478bd9Sstevel@tonic-gate 	    ugenp->ug_minor_node_table[UGEN_MINOR_GET_IDX(ugenp, dev)]);
33827c478bd9Sstevel@tonic-gate 
33837c478bd9Sstevel@tonic-gate 	ASSERT(UGEN_MINOR_GET_IDX(ugenp, dev) <
33847c478bd9Sstevel@tonic-gate 			ugenp->ug_minor_node_table_index);
33857c478bd9Sstevel@tonic-gate 
33867c478bd9Sstevel@tonic-gate 	return (ugenp->ug_minor_node_table[UGEN_MINOR_GET_IDX(ugenp, dev)]);
33877c478bd9Sstevel@tonic-gate }
33887c478bd9Sstevel@tonic-gate 
33897c478bd9Sstevel@tonic-gate 
3390*96603521Sfrits static int
3391*96603521Sfrits ugen_is_valid_minor_node(ugen_state_t *ugenp, dev_t dev)
3392*96603521Sfrits {
3393*96603521Sfrits 	int idx = UGEN_MINOR_GET_IDX(ugenp, dev);
3394*96603521Sfrits 
3395*96603521Sfrits 	if ((idx < ugenp->ug_minor_node_table_index) &&
3396*96603521Sfrits 	    (idx > 0)) {
3397*96603521Sfrits 
3398*96603521Sfrits 		return (USB_SUCCESS);
3399*96603521Sfrits 	}
3400*96603521Sfrits 	USB_DPRINTF_L2(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
3401*96603521Sfrits 	    "ugen_is_valid_minor_node: invalid minorindex=%d", idx);
3402*96603521Sfrits 
3403*96603521Sfrits 	return (USB_FAILURE);
3404*96603521Sfrits }
3405*96603521Sfrits 
3406*96603521Sfrits 
34077c478bd9Sstevel@tonic-gate static void
34087c478bd9Sstevel@tonic-gate ugen_minor_node_table_create(ugen_state_t *ugenp)
34097c478bd9Sstevel@tonic-gate {
34107c478bd9Sstevel@tonic-gate 	size_t	size = sizeof (ugen_minor_t) * UGEN_MINOR_IDX_LIMIT(ugenp);
34117c478bd9Sstevel@tonic-gate 
34127c478bd9Sstevel@tonic-gate 	/* allocate the max table size needed, we reduce later */
34137c478bd9Sstevel@tonic-gate 	ugenp->ug_minor_node_table = kmem_zalloc(size, KM_SLEEP);
34147c478bd9Sstevel@tonic-gate 	ugenp->ug_minor_node_table_size = size;
34157c478bd9Sstevel@tonic-gate 	ugenp->ug_minor_node_table_index = 1;
34167c478bd9Sstevel@tonic-gate }
34177c478bd9Sstevel@tonic-gate 
34187c478bd9Sstevel@tonic-gate 
34197c478bd9Sstevel@tonic-gate static void
34207c478bd9Sstevel@tonic-gate ugen_minor_node_table_shrink(ugen_state_t *ugenp)
34217c478bd9Sstevel@tonic-gate {
34227c478bd9Sstevel@tonic-gate 	/* reduce the table size to save some memory */
34237c478bd9Sstevel@tonic-gate 	if (ugenp->ug_minor_node_table_index < UGEN_MINOR_IDX_LIMIT(ugenp)) {
34247c478bd9Sstevel@tonic-gate 		size_t newsize = sizeof (ugen_minor_t) *
34257c478bd9Sstevel@tonic-gate 				ugenp->ug_minor_node_table_index;
34267c478bd9Sstevel@tonic-gate 		ugen_minor_t *buf = kmem_zalloc(newsize, KM_SLEEP);
34277c478bd9Sstevel@tonic-gate 
34287c478bd9Sstevel@tonic-gate 		bcopy(ugenp->ug_minor_node_table, buf, newsize);
34297c478bd9Sstevel@tonic-gate 		kmem_free(ugenp->ug_minor_node_table,
34307c478bd9Sstevel@tonic-gate 					ugenp->ug_minor_node_table_size);
34317c478bd9Sstevel@tonic-gate 		ugenp->ug_minor_node_table = buf;
34327c478bd9Sstevel@tonic-gate 		ugenp->ug_minor_node_table_size = newsize;
34337c478bd9Sstevel@tonic-gate 	}
34347c478bd9Sstevel@tonic-gate }
34357c478bd9Sstevel@tonic-gate 
34367c478bd9Sstevel@tonic-gate 
34377c478bd9Sstevel@tonic-gate static void
34387c478bd9Sstevel@tonic-gate ugen_minor_node_table_destroy(ugen_state_t *ugenp)
34397c478bd9Sstevel@tonic-gate {
34407c478bd9Sstevel@tonic-gate 	if (ugenp->ug_minor_node_table) {
34417c478bd9Sstevel@tonic-gate 		kmem_free(ugenp->ug_minor_node_table,
34427c478bd9Sstevel@tonic-gate 				ugenp->ug_minor_node_table_size);
34437c478bd9Sstevel@tonic-gate 	}
34447c478bd9Sstevel@tonic-gate }
34457c478bd9Sstevel@tonic-gate 
34467c478bd9Sstevel@tonic-gate 
34477c478bd9Sstevel@tonic-gate static void
34487c478bd9Sstevel@tonic-gate ugen_check_mask(uint_t mask, uint_t *shift, uint_t *limit)
34497c478bd9Sstevel@tonic-gate {
34507c478bd9Sstevel@tonic-gate 	uint_t i, j;
34517c478bd9Sstevel@tonic-gate 
34527c478bd9Sstevel@tonic-gate 	for (i = 0; i < UGEN_MINOR_NODE_SIZE; i++) {
34537c478bd9Sstevel@tonic-gate 		if ((1 << i)  & mask) {
34547c478bd9Sstevel@tonic-gate 
34557c478bd9Sstevel@tonic-gate 			break;
34567c478bd9Sstevel@tonic-gate 		}
34577c478bd9Sstevel@tonic-gate 	}
34587c478bd9Sstevel@tonic-gate 
34597c478bd9Sstevel@tonic-gate 	for (j = i; j < UGEN_MINOR_NODE_SIZE; j++) {
34607c478bd9Sstevel@tonic-gate 		if (((1 << j) & mask) == 0) {
34617c478bd9Sstevel@tonic-gate 
34627c478bd9Sstevel@tonic-gate 			break;
34637c478bd9Sstevel@tonic-gate 		}
34647c478bd9Sstevel@tonic-gate 	}
34657c478bd9Sstevel@tonic-gate 
34667c478bd9Sstevel@tonic-gate 	*limit = (i == j) ? 0 : 1 << (j - i);
34677c478bd9Sstevel@tonic-gate 	*shift = i;
34687c478bd9Sstevel@tonic-gate }
34697c478bd9Sstevel@tonic-gate 
34707c478bd9Sstevel@tonic-gate 
34717c478bd9Sstevel@tonic-gate 
34727c478bd9Sstevel@tonic-gate /*
34737c478bd9Sstevel@tonic-gate  * power management:
34747c478bd9Sstevel@tonic-gate  *
34757c478bd9Sstevel@tonic-gate  * ugen_pm_init:
34767c478bd9Sstevel@tonic-gate  *	Initialize power management and remote wakeup functionality.
34777c478bd9Sstevel@tonic-gate  *	No mutex is necessary in this function as it's called only by attach.
34787c478bd9Sstevel@tonic-gate  */
34797c478bd9Sstevel@tonic-gate static void
34807c478bd9Sstevel@tonic-gate ugen_pm_init(ugen_state_t *ugenp)
34817c478bd9Sstevel@tonic-gate {
34827c478bd9Sstevel@tonic-gate 	dev_info_t	*dip = ugenp->ug_dip;
34837c478bd9Sstevel@tonic-gate 	ugen_power_t	*ugenpm;
34847c478bd9Sstevel@tonic-gate 
34857c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_PM, ugenp->ug_log_hdl,
34867c478bd9Sstevel@tonic-gate 	    "ugen_pm_init:");
34877c478bd9Sstevel@tonic-gate 
34887c478bd9Sstevel@tonic-gate 	/* Allocate the state structure */
34897c478bd9Sstevel@tonic-gate 	ugenpm = kmem_zalloc(sizeof (ugen_power_t), KM_SLEEP);
34907c478bd9Sstevel@tonic-gate 
34917c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
34927c478bd9Sstevel@tonic-gate 	ugenp->ug_pm = ugenpm;
34937c478bd9Sstevel@tonic-gate 	ugenpm->pwr_wakeup_enabled = B_FALSE;
34947c478bd9Sstevel@tonic-gate 	ugenpm->pwr_current = USB_DEV_OS_FULL_PWR;
34957c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
34967c478bd9Sstevel@tonic-gate 
34977c478bd9Sstevel@tonic-gate 	/*
34987c478bd9Sstevel@tonic-gate 	 * If remote wakeup is not available you may not want to do
34997c478bd9Sstevel@tonic-gate 	 * power management.
35007c478bd9Sstevel@tonic-gate 	 */
35017c478bd9Sstevel@tonic-gate 	if (ugen_enable_pm || usb_handle_remote_wakeup(dip,
35027c478bd9Sstevel@tonic-gate 	    USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) {
35037c478bd9Sstevel@tonic-gate 		if (usb_create_pm_components(dip,
35047c478bd9Sstevel@tonic-gate 		    &ugenpm->pwr_states) == USB_SUCCESS) {
35057c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L4(UGEN_PRINT_PM,
35067c478bd9Sstevel@tonic-gate 			    ugenp->ug_log_hdl,
35077c478bd9Sstevel@tonic-gate 			    "ugen_pm_init: "
35087c478bd9Sstevel@tonic-gate 			    "created PM components");
35097c478bd9Sstevel@tonic-gate 
35107c478bd9Sstevel@tonic-gate 			mutex_enter(&ugenp->ug_mutex);
35117c478bd9Sstevel@tonic-gate 			ugenpm->pwr_wakeup_enabled = B_TRUE;
35127c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
35137c478bd9Sstevel@tonic-gate 
35147c478bd9Sstevel@tonic-gate 			if (pm_raise_power(dip, 0,
35157c478bd9Sstevel@tonic-gate 			    USB_DEV_OS_FULL_PWR) != DDI_SUCCESS) {
35167c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(UGEN_PRINT_PM,
35177c478bd9Sstevel@tonic-gate 				    ugenp->ug_log_hdl,
35187c478bd9Sstevel@tonic-gate 				    "ugen_pm_init: "
35197c478bd9Sstevel@tonic-gate 				    "raising power failed");
35207c478bd9Sstevel@tonic-gate 			}
35217c478bd9Sstevel@tonic-gate 		} else {
35227c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(UGEN_PRINT_PM,
35237c478bd9Sstevel@tonic-gate 			    ugenp->ug_log_hdl,
35247c478bd9Sstevel@tonic-gate 			    "ugen_pm_init: "
35257c478bd9Sstevel@tonic-gate 			    "create_pm_comps failed");
35267c478bd9Sstevel@tonic-gate 		}
35277c478bd9Sstevel@tonic-gate 	} else {
35287c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_PM,
35297c478bd9Sstevel@tonic-gate 		    ugenp->ug_log_hdl, "ugen_pm_init: "
35307c478bd9Sstevel@tonic-gate 		    "failure enabling remote wakeup");
35317c478bd9Sstevel@tonic-gate 	}
35327c478bd9Sstevel@tonic-gate 
35337c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_PM, ugenp->ug_log_hdl,
35347c478bd9Sstevel@tonic-gate 	    "ugen_pm_init: end");
35357c478bd9Sstevel@tonic-gate }
35367c478bd9Sstevel@tonic-gate 
35377c478bd9Sstevel@tonic-gate 
35387c478bd9Sstevel@tonic-gate /*
35397c478bd9Sstevel@tonic-gate  * ugen_pm_destroy:
35407c478bd9Sstevel@tonic-gate  *	Shut down and destroy power management and remote wakeup functionality.
35417c478bd9Sstevel@tonic-gate  */
35427c478bd9Sstevel@tonic-gate static void
35437c478bd9Sstevel@tonic-gate ugen_pm_destroy(ugen_state_t *ugenp)
35447c478bd9Sstevel@tonic-gate {
35457c478bd9Sstevel@tonic-gate 	dev_info_t *dip = ugenp->ug_dip;
35467c478bd9Sstevel@tonic-gate 
35477c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_PM, ugenp->ug_log_hdl,
35487c478bd9Sstevel@tonic-gate 	    "ugen_pm_destroy:");
35497c478bd9Sstevel@tonic-gate 
35507c478bd9Sstevel@tonic-gate 	if (ugenp->ug_pm) {
35517c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
35527c478bd9Sstevel@tonic-gate 		ugen_pm_busy_component(ugenp);
35537c478bd9Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
35547c478bd9Sstevel@tonic-gate 
35557c478bd9Sstevel@tonic-gate 		if ((ugenp->ug_pm->pwr_wakeup_enabled) &&
35567c478bd9Sstevel@tonic-gate 		    (ugenp->ug_dev_state != USB_DEV_DISCONNECTED)) {
35577c478bd9Sstevel@tonic-gate 			int rval;
35587c478bd9Sstevel@tonic-gate 
35597c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
35607c478bd9Sstevel@tonic-gate 			(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
35617c478bd9Sstevel@tonic-gate 
35627c478bd9Sstevel@tonic-gate 			if ((rval = usb_handle_remote_wakeup(dip,
35637c478bd9Sstevel@tonic-gate 			    USB_REMOTE_WAKEUP_DISABLE)) != USB_SUCCESS) {
35647c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L4(UGEN_PRINT_PM,
35657c478bd9Sstevel@tonic-gate 				    ugenp->ug_log_hdl, "ugen_pm_destroy: "
35667c478bd9Sstevel@tonic-gate 				    "disabling rmt wakeup: rval=%d", rval);
35677c478bd9Sstevel@tonic-gate 			}
35687c478bd9Sstevel@tonic-gate 			/*
35697c478bd9Sstevel@tonic-gate 			 * Since remote wakeup is disabled now,
35707c478bd9Sstevel@tonic-gate 			 * no one can raise power
35717c478bd9Sstevel@tonic-gate 			 * and get to device once power is lowered here.
35727c478bd9Sstevel@tonic-gate 			 */
35737c478bd9Sstevel@tonic-gate 		} else {
35747c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
35757c478bd9Sstevel@tonic-gate 		}
35767c478bd9Sstevel@tonic-gate 		(void) pm_lower_power(dip, 0, USB_DEV_OS_PWR_OFF);
35777c478bd9Sstevel@tonic-gate 		ugen_pm_idle_component(ugenp);
35787c478bd9Sstevel@tonic-gate 
35797c478bd9Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
35807c478bd9Sstevel@tonic-gate 		kmem_free(ugenp->ug_pm, sizeof (ugen_power_t));
35817c478bd9Sstevel@tonic-gate 		ugenp->ug_pm = NULL;
35827c478bd9Sstevel@tonic-gate 	}
35837c478bd9Sstevel@tonic-gate }
35847c478bd9Sstevel@tonic-gate 
35857c478bd9Sstevel@tonic-gate 
35867c478bd9Sstevel@tonic-gate /*
35877c478bd9Sstevel@tonic-gate  * ugen_power :
35887c478bd9Sstevel@tonic-gate  *	Power entry point, the workhorse behind pm_raise_power, pm_lower_power,
35897c478bd9Sstevel@tonic-gate  *	usb_req_raise_power and usb_req_lower_power.
35907c478bd9Sstevel@tonic-gate  */
35917c478bd9Sstevel@tonic-gate /*ARGSUSED*/
35927c478bd9Sstevel@tonic-gate int
35937c478bd9Sstevel@tonic-gate usb_ugen_power(usb_ugen_hdl_t usb_ugen_hdl, int comp, int level)
35947c478bd9Sstevel@tonic-gate {
35957c478bd9Sstevel@tonic-gate 	ugen_power_t		*pm;
35967c478bd9Sstevel@tonic-gate 	int			rval = USB_FAILURE;
35977c478bd9Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
35987c478bd9Sstevel@tonic-gate 				(usb_ugen_hdl_impl_t *)usb_ugen_hdl;
35997c478bd9Sstevel@tonic-gate 	ugen_state_t		*ugenp;
36007c478bd9Sstevel@tonic-gate 	dev_info_t		*dip;
36017c478bd9Sstevel@tonic-gate 
36027c478bd9Sstevel@tonic-gate 	if (usb_ugen_hdl == NULL) {
36037c478bd9Sstevel@tonic-gate 
36047c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
36057c478bd9Sstevel@tonic-gate 	}
36067c478bd9Sstevel@tonic-gate 
36077c478bd9Sstevel@tonic-gate 	ugenp = usb_ugen_hdl_impl->hdl_ugenp;
36087c478bd9Sstevel@tonic-gate 	dip = ugenp->ug_dip;
36097c478bd9Sstevel@tonic-gate 
36107c478bd9Sstevel@tonic-gate 	if (ugenp->ug_pm == NULL) {
36117c478bd9Sstevel@tonic-gate 
36127c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
36137c478bd9Sstevel@tonic-gate 	}
36147c478bd9Sstevel@tonic-gate 
36157c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_PM, ugenp->ug_log_hdl,
36167c478bd9Sstevel@tonic-gate 	    "usb_ugen_power: level=%d", level);
36177c478bd9Sstevel@tonic-gate 
36187c478bd9Sstevel@tonic-gate 	(void) usb_serialize_access(ugenp->ug_ser_cookie,
36197c478bd9Sstevel@tonic-gate 						USB_WAIT, 0);
36207c478bd9Sstevel@tonic-gate 	/*
36217c478bd9Sstevel@tonic-gate 	 * If we are disconnected/suspended, return success. Note that if we
36227c478bd9Sstevel@tonic-gate 	 * return failure, bringing down the system will hang when
36237c478bd9Sstevel@tonic-gate 	 * PM tries to power up all devices
36247c478bd9Sstevel@tonic-gate 	 */
36257c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
36267c478bd9Sstevel@tonic-gate 	switch (ugenp->ug_dev_state) {
36277c478bd9Sstevel@tonic-gate 	case USB_DEV_ONLINE:
36287c478bd9Sstevel@tonic-gate 
36297c478bd9Sstevel@tonic-gate 		break;
36307c478bd9Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
36317c478bd9Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
36327c478bd9Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RESUME:
36337c478bd9Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
36347c478bd9Sstevel@tonic-gate 	default:
36357c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_PM, ugenp->ug_log_hdl,
36367c478bd9Sstevel@tonic-gate 		    "ugen_power: disconnected/suspended "
36377c478bd9Sstevel@tonic-gate 		    "dev_state=%d", ugenp->ug_dev_state);
36387c478bd9Sstevel@tonic-gate 		rval = USB_SUCCESS;
36397c478bd9Sstevel@tonic-gate 
36407c478bd9Sstevel@tonic-gate 		goto done;
36417c478bd9Sstevel@tonic-gate 	}
36427c478bd9Sstevel@tonic-gate 
36437c478bd9Sstevel@tonic-gate 	pm = ugenp->ug_pm;
36447c478bd9Sstevel@tonic-gate 
36457c478bd9Sstevel@tonic-gate 	/* Check if we are transitioning to a legal power level */
36467c478bd9Sstevel@tonic-gate 	if (USB_DEV_PWRSTATE_OK(pm->pwr_states, level)) {
36477c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_PM, ugenp->ug_log_hdl,
36487c478bd9Sstevel@tonic-gate 		    "ugen_power: illegal power level=%d "
36497c478bd9Sstevel@tonic-gate 		    "pwr_states: 0x%x", level, pm->pwr_states);
36507c478bd9Sstevel@tonic-gate 
36517c478bd9Sstevel@tonic-gate 		goto done;
36527c478bd9Sstevel@tonic-gate 	}
36537c478bd9Sstevel@tonic-gate 
36547c478bd9Sstevel@tonic-gate 	switch (level) {
36557c478bd9Sstevel@tonic-gate 	case USB_DEV_OS_PWR_OFF :
36567c478bd9Sstevel@tonic-gate 		switch (ugenp->ug_dev_state) {
36577c478bd9Sstevel@tonic-gate 		case USB_DEV_ONLINE:
36587c478bd9Sstevel@tonic-gate 			/* Deny the powerdown request if the device is busy */
36597c478bd9Sstevel@tonic-gate 			if (ugenp->ug_pm->pwr_busy != 0) {
36607c478bd9Sstevel@tonic-gate 
36617c478bd9Sstevel@tonic-gate 				break;
36627c478bd9Sstevel@tonic-gate 			}
36637c478bd9Sstevel@tonic-gate 			ASSERT(ugenp->ug_open_count == 0);
36647c478bd9Sstevel@tonic-gate 			ASSERT(ugenp->ug_pending_cmds == 0);
36657c478bd9Sstevel@tonic-gate 			ugenp->ug_pm->pwr_current = USB_DEV_OS_PWR_OFF;
36667c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
36677c478bd9Sstevel@tonic-gate 
36687c478bd9Sstevel@tonic-gate 			/* Issue USB D3 command to the device here */
36697c478bd9Sstevel@tonic-gate 			rval = usb_set_device_pwrlvl3(dip);
36707c478bd9Sstevel@tonic-gate 			mutex_enter(&ugenp->ug_mutex);
36717c478bd9Sstevel@tonic-gate 
36727c478bd9Sstevel@tonic-gate 			break;
36737c478bd9Sstevel@tonic-gate 		default:
36747c478bd9Sstevel@tonic-gate 			rval = USB_SUCCESS;
36757c478bd9Sstevel@tonic-gate 
36767c478bd9Sstevel@tonic-gate 			break;
36777c478bd9Sstevel@tonic-gate 		}
36787c478bd9Sstevel@tonic-gate 		break;
36797c478bd9Sstevel@tonic-gate 	case USB_DEV_OS_FULL_PWR :
36807c478bd9Sstevel@tonic-gate 		/*
36817c478bd9Sstevel@tonic-gate 		 * PM framework tries to put us in full power during system
36827c478bd9Sstevel@tonic-gate 		 * shutdown.
36837c478bd9Sstevel@tonic-gate 		 */
36847c478bd9Sstevel@tonic-gate 		switch (ugenp->ug_dev_state) {
36857c478bd9Sstevel@tonic-gate 		case USB_UGEN_DEV_UNAVAILABLE_RESUME:
36867c478bd9Sstevel@tonic-gate 		case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
36877c478bd9Sstevel@tonic-gate 
36887c478bd9Sstevel@tonic-gate 			break;
36897c478bd9Sstevel@tonic-gate 		default:
36907c478bd9Sstevel@tonic-gate 			ugenp->ug_dev_state = USB_DEV_ONLINE;
36917c478bd9Sstevel@tonic-gate 
36927c478bd9Sstevel@tonic-gate 			/* wakeup devstat reads and polls */
36937c478bd9Sstevel@tonic-gate 			ugen_ds_change(ugenp);
36947c478bd9Sstevel@tonic-gate 			ugen_ds_poll_wakeup(ugenp);
36957c478bd9Sstevel@tonic-gate 
36967c478bd9Sstevel@tonic-gate 			break;
36977c478bd9Sstevel@tonic-gate 		}
36987c478bd9Sstevel@tonic-gate 		ugenp->ug_pm->pwr_current = USB_DEV_OS_FULL_PWR;
36997c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
37007c478bd9Sstevel@tonic-gate 		rval = usb_set_device_pwrlvl0(dip);
37017c478bd9Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
37027c478bd9Sstevel@tonic-gate 
37037c478bd9Sstevel@tonic-gate 		break;
37047c478bd9Sstevel@tonic-gate 	default:
37057c478bd9Sstevel@tonic-gate 		/* Levels 1 and 2 are not supported to keep it simple. */
37067c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_PM, ugenp->ug_log_hdl,
37077c478bd9Sstevel@tonic-gate 		    "ugen_power: power level %d not supported", level);
37087c478bd9Sstevel@tonic-gate 
37097c478bd9Sstevel@tonic-gate 		break;
37107c478bd9Sstevel@tonic-gate 	}
37117c478bd9Sstevel@tonic-gate done:
37127c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
37137c478bd9Sstevel@tonic-gate 	usb_release_access(ugenp->ug_ser_cookie);
37147c478bd9Sstevel@tonic-gate 
37157c478bd9Sstevel@tonic-gate 	return (rval);
37167c478bd9Sstevel@tonic-gate }
37177c478bd9Sstevel@tonic-gate 
37187c478bd9Sstevel@tonic-gate 
37197c478bd9Sstevel@tonic-gate static void
37207c478bd9Sstevel@tonic-gate ugen_pm_busy_component(ugen_state_t *ugen_statep)
37217c478bd9Sstevel@tonic-gate {
37227c478bd9Sstevel@tonic-gate 	ASSERT(!mutex_owned(&ugen_statep->ug_mutex));
37237c478bd9Sstevel@tonic-gate 
37247c478bd9Sstevel@tonic-gate 	if (ugen_statep->ug_pm != NULL) {
37257c478bd9Sstevel@tonic-gate 		mutex_enter(&ugen_statep->ug_mutex);
37267c478bd9Sstevel@tonic-gate 		ugen_statep->ug_pm->pwr_busy++;
37277c478bd9Sstevel@tonic-gate 
37287c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_PM, ugen_statep->ug_log_hdl,
37297c478bd9Sstevel@tonic-gate 		    "ugen_pm_busy_component: %d", ugen_statep->ug_pm->pwr_busy);
37307c478bd9Sstevel@tonic-gate 
37317c478bd9Sstevel@tonic-gate 		mutex_exit(&ugen_statep->ug_mutex);
37327c478bd9Sstevel@tonic-gate 		if (pm_busy_component(ugen_statep->ug_dip, 0) != DDI_SUCCESS) {
37337c478bd9Sstevel@tonic-gate 			mutex_enter(&ugen_statep->ug_mutex);
37347c478bd9Sstevel@tonic-gate 			ugen_statep->ug_pm->pwr_busy--;
37357c478bd9Sstevel@tonic-gate 
37367c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(UGEN_PRINT_PM, ugen_statep->ug_log_hdl,
37377c478bd9Sstevel@tonic-gate 			    "ugen_pm_busy_component failed: %d",
37387c478bd9Sstevel@tonic-gate 			    ugen_statep->ug_pm->pwr_busy);
37397c478bd9Sstevel@tonic-gate 
37407c478bd9Sstevel@tonic-gate 			mutex_exit(&ugen_statep->ug_mutex);
37417c478bd9Sstevel@tonic-gate 		}
37427c478bd9Sstevel@tonic-gate 	}
37437c478bd9Sstevel@tonic-gate }
37447c478bd9Sstevel@tonic-gate 
37457c478bd9Sstevel@tonic-gate 
37467c478bd9Sstevel@tonic-gate static void
37477c478bd9Sstevel@tonic-gate ugen_pm_idle_component(ugen_state_t *ugen_statep)
37487c478bd9Sstevel@tonic-gate {
37497c478bd9Sstevel@tonic-gate 	ASSERT(!mutex_owned(&ugen_statep->ug_mutex));
37507c478bd9Sstevel@tonic-gate 
37517c478bd9Sstevel@tonic-gate 	if (ugen_statep->ug_pm != NULL) {
37527c478bd9Sstevel@tonic-gate 		if (pm_idle_component(ugen_statep->ug_dip, 0) == DDI_SUCCESS) {
37537c478bd9Sstevel@tonic-gate 			mutex_enter(&ugen_statep->ug_mutex);
37547c478bd9Sstevel@tonic-gate 			ASSERT(ugen_statep->ug_pm->pwr_busy > 0);
37557c478bd9Sstevel@tonic-gate 			ugen_statep->ug_pm->pwr_busy--;
37567c478bd9Sstevel@tonic-gate 
37577c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L4(UGEN_PRINT_PM, ugen_statep->ug_log_hdl,
37587c478bd9Sstevel@tonic-gate 			    "ugen_pm_idle_component: %d",
37597c478bd9Sstevel@tonic-gate 			    ugen_statep->ug_pm->pwr_busy);
37607c478bd9Sstevel@tonic-gate 
37617c478bd9Sstevel@tonic-gate 			mutex_exit(&ugen_statep->ug_mutex);
37627c478bd9Sstevel@tonic-gate 		}
37637c478bd9Sstevel@tonic-gate 	}
37647c478bd9Sstevel@tonic-gate }
37657c478bd9Sstevel@tonic-gate 
37667c478bd9Sstevel@tonic-gate 
37677c478bd9Sstevel@tonic-gate /*
37687c478bd9Sstevel@tonic-gate  * devt lookup support
37697c478bd9Sstevel@tonic-gate  *	In ugen_strategy and ugen_minphys, we only have the devt and need
37707c478bd9Sstevel@tonic-gate  *	the ugen_state pointer. Since we don't know instance mask, we can't
37717c478bd9Sstevel@tonic-gate  *	easily derive a softstate pointer. Therefore, we use a list
37727c478bd9Sstevel@tonic-gate  */
37737c478bd9Sstevel@tonic-gate static void
37747c478bd9Sstevel@tonic-gate ugen_store_devt(ugen_state_t *ugenp, minor_t minor)
37757c478bd9Sstevel@tonic-gate {
37767c478bd9Sstevel@tonic-gate 	ugen_devt_list_entry_t *e = kmem_zalloc(
37777c478bd9Sstevel@tonic-gate 				sizeof (ugen_devt_list_entry_t), KM_SLEEP);
37787c478bd9Sstevel@tonic-gate 	ugen_devt_list_entry_t *t;
37797c478bd9Sstevel@tonic-gate 
37807c478bd9Sstevel@tonic-gate 	mutex_enter(&ugen_devt_list_mutex);
37817c478bd9Sstevel@tonic-gate 	e->list_dev = makedevice(ddi_driver_major(ugenp->ug_dip), minor);
37827c478bd9Sstevel@tonic-gate 	e->list_state = ugenp;
37837c478bd9Sstevel@tonic-gate 
37847c478bd9Sstevel@tonic-gate 	t = ugen_devt_list.list_next;
37857c478bd9Sstevel@tonic-gate 
37867c478bd9Sstevel@tonic-gate 	/* check if the entry is already in the list */
37877c478bd9Sstevel@tonic-gate 	while (t) {
37887c478bd9Sstevel@tonic-gate 		ASSERT(t->list_dev != e->list_dev);
37897c478bd9Sstevel@tonic-gate 		t = t->list_next;
37907c478bd9Sstevel@tonic-gate 	}
37917c478bd9Sstevel@tonic-gate 
37927c478bd9Sstevel@tonic-gate 	/* add to the head of the list */
37937c478bd9Sstevel@tonic-gate 	e->list_next = ugen_devt_list.list_next;
37947c478bd9Sstevel@tonic-gate 	if (ugen_devt_list.list_next) {
37957c478bd9Sstevel@tonic-gate 		ugen_devt_list.list_next->list_prev = e;
37967c478bd9Sstevel@tonic-gate 	}
37977c478bd9Sstevel@tonic-gate 	ugen_devt_list.list_next = e;
37987c478bd9Sstevel@tonic-gate 	mutex_exit(&ugen_devt_list_mutex);
37997c478bd9Sstevel@tonic-gate }
38007c478bd9Sstevel@tonic-gate 
38017c478bd9Sstevel@tonic-gate 
38027c478bd9Sstevel@tonic-gate static ugen_state_t *
38037c478bd9Sstevel@tonic-gate ugen_devt2state(dev_t dev)
38047c478bd9Sstevel@tonic-gate {
38057c478bd9Sstevel@tonic-gate 	ugen_devt_list_entry_t *t;
38067c478bd9Sstevel@tonic-gate 	ugen_state_t	*ugenp = NULL;
38077c478bd9Sstevel@tonic-gate 	int		index, count;
38087c478bd9Sstevel@tonic-gate 
38097c478bd9Sstevel@tonic-gate 	mutex_enter(&ugen_devt_list_mutex);
38107c478bd9Sstevel@tonic-gate 
38117c478bd9Sstevel@tonic-gate 	for (index = ugen_devt_cache_index, count = 0;
38127c478bd9Sstevel@tonic-gate 	    count < UGEN_DEVT_CACHE_SIZE; count++) {
38137c478bd9Sstevel@tonic-gate 		if (ugen_devt_cache[index].cache_dev == dev) {
38147c478bd9Sstevel@tonic-gate 			ugen_devt_cache[index].cache_hit++;
38157c478bd9Sstevel@tonic-gate 			ugenp = ugen_devt_cache[index].cache_state;
38167c478bd9Sstevel@tonic-gate 
38177c478bd9Sstevel@tonic-gate 			mutex_exit(&ugen_devt_list_mutex);
38187c478bd9Sstevel@tonic-gate 
38197c478bd9Sstevel@tonic-gate 			return (ugenp);
38207c478bd9Sstevel@tonic-gate 		}
38217c478bd9Sstevel@tonic-gate 		index++;
38227c478bd9Sstevel@tonic-gate 		index %= UGEN_DEVT_CACHE_SIZE;
38237c478bd9Sstevel@tonic-gate 	}
38247c478bd9Sstevel@tonic-gate 
38257c478bd9Sstevel@tonic-gate 	t = ugen_devt_list.list_next;
38267c478bd9Sstevel@tonic-gate 
38277c478bd9Sstevel@tonic-gate 	while (t) {
38287c478bd9Sstevel@tonic-gate 		if (t->list_dev == dev) {
38297c478bd9Sstevel@tonic-gate 			ugenp = t->list_state;
38307c478bd9Sstevel@tonic-gate 			ugen_devt_cache_index++;
38317c478bd9Sstevel@tonic-gate 			ugen_devt_cache_index %= UGEN_DEVT_CACHE_SIZE;
38327c478bd9Sstevel@tonic-gate 			ugen_devt_cache[ugen_devt_cache_index].cache_dev = dev;
38337c478bd9Sstevel@tonic-gate 			ugen_devt_cache[ugen_devt_cache_index].cache_state =
38347c478bd9Sstevel@tonic-gate 									ugenp;
38357c478bd9Sstevel@tonic-gate 			mutex_exit(&ugen_devt_list_mutex);
38367c478bd9Sstevel@tonic-gate 
38377c478bd9Sstevel@tonic-gate 			return (ugenp);
38387c478bd9Sstevel@tonic-gate 		}
38397c478bd9Sstevel@tonic-gate 		t = t->list_next;
38407c478bd9Sstevel@tonic-gate 	}
38417c478bd9Sstevel@tonic-gate 	mutex_exit(&ugen_devt_list_mutex);
38427c478bd9Sstevel@tonic-gate 
38437c478bd9Sstevel@tonic-gate 	return (ugenp);
38447c478bd9Sstevel@tonic-gate }
38457c478bd9Sstevel@tonic-gate 
38467c478bd9Sstevel@tonic-gate 
38477c478bd9Sstevel@tonic-gate static void
38487c478bd9Sstevel@tonic-gate ugen_free_devt(ugen_state_t *ugenp)
38497c478bd9Sstevel@tonic-gate {
38507c478bd9Sstevel@tonic-gate 	ugen_devt_list_entry_t *e, *next, *prev;
38517c478bd9Sstevel@tonic-gate 	major_t		major = ddi_driver_major(ugenp->ug_dip);
38527c478bd9Sstevel@tonic-gate 	int		instance = ddi_get_instance(ugenp->ug_dip);
38537c478bd9Sstevel@tonic-gate 
38547c478bd9Sstevel@tonic-gate 	mutex_enter(&ugen_devt_list_mutex);
38557c478bd9Sstevel@tonic-gate 	prev = &ugen_devt_list;
38567c478bd9Sstevel@tonic-gate 	for (e = prev->list_next; e != 0; e = next) {
38577c478bd9Sstevel@tonic-gate 		int i = (getminor(e->list_dev) &
38587c478bd9Sstevel@tonic-gate 			ugenp->ug_hdl->hdl_minor_node_instance_mask) >>
38597c478bd9Sstevel@tonic-gate 			ugenp->ug_hdl->hdl_minor_node_instance_shift;
38607c478bd9Sstevel@tonic-gate 		int m = getmajor(e->list_dev);
38617c478bd9Sstevel@tonic-gate 
38627c478bd9Sstevel@tonic-gate 		next = e->list_next;
38637c478bd9Sstevel@tonic-gate 
38647c478bd9Sstevel@tonic-gate 		if ((i == instance) && (m == major)) {
38657c478bd9Sstevel@tonic-gate 			prev->list_next = e->list_next;
38667c478bd9Sstevel@tonic-gate 			if (e->list_next) {
38677c478bd9Sstevel@tonic-gate 				e->list_next->list_prev = prev;
38687c478bd9Sstevel@tonic-gate 			}
38697c478bd9Sstevel@tonic-gate 			kmem_free(e, sizeof (ugen_devt_list_entry_t));
38707c478bd9Sstevel@tonic-gate 		} else {
38717c478bd9Sstevel@tonic-gate 			prev = e;
38727c478bd9Sstevel@tonic-gate 		}
38737c478bd9Sstevel@tonic-gate 	}
38747c478bd9Sstevel@tonic-gate 
38757c478bd9Sstevel@tonic-gate 	bzero(ugen_devt_cache, sizeof (ugen_devt_cache));
38767c478bd9Sstevel@tonic-gate 	ugen_devt_cache_index = 0;
38777c478bd9Sstevel@tonic-gate 	mutex_exit(&ugen_devt_list_mutex);
38787c478bd9Sstevel@tonic-gate }
3879