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
5*0a05e705Slc  * Common Development and Distribution License (the "License").
6*0a05e705Slc  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  *
21*0a05e705Slc  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
227c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
237c478bd9Sstevel@tonic-gate  */
247c478bd9Sstevel@tonic-gate 
257c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*
287c478bd9Sstevel@tonic-gate  * UGEN: USB Generic Driver support code
297c478bd9Sstevel@tonic-gate  *
307c478bd9Sstevel@tonic-gate  * This code provides entry points called by the ugen driver or other
317c478bd9Sstevel@tonic-gate  * drivers that want to export a ugen interface
327c478bd9Sstevel@tonic-gate  *
337c478bd9Sstevel@tonic-gate  * The "Universal Generic Driver"  (UGEN) for USB devices provides interfaces
347c478bd9Sstevel@tonic-gate  * to  talk to	USB  devices.  This is	very  useful for  Point of Sale sale
357c478bd9Sstevel@tonic-gate  * devices and other simple  devices like  USB	scanner, USB palm  pilot.
367c478bd9Sstevel@tonic-gate  * The UGEN provides a system call interface to USB  devices  enabling
377c478bd9Sstevel@tonic-gate  * a USB device vendor to  write an  application for his
387c478bd9Sstevel@tonic-gate  * device instead of  writing a driver. This facilitates the vendor to write
397c478bd9Sstevel@tonic-gate  * device management s/w quickly in userland.
407c478bd9Sstevel@tonic-gate  *
417c478bd9Sstevel@tonic-gate  * UGEN supports read/write/poll entry points. An application can be written
427c478bd9Sstevel@tonic-gate  * using  read/write/aioread/aiowrite/poll  system calls to communicate
437c478bd9Sstevel@tonic-gate  * with the device.
447c478bd9Sstevel@tonic-gate  *
457c478bd9Sstevel@tonic-gate  * XXX Theory of Operations
467c478bd9Sstevel@tonic-gate  */
477c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usbai_version.h>
487c478bd9Sstevel@tonic-gate #include <sys/usb/usba.h>
497c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate #include "sys/usb/clients/ugen/usb_ugen.h"
527c478bd9Sstevel@tonic-gate #include "sys/usb/usba/usba_ugen.h"
537c478bd9Sstevel@tonic-gate #include "sys/usb/usba/usba_ugend.h"
547c478bd9Sstevel@tonic-gate 
557c478bd9Sstevel@tonic-gate /* Debugging information */
564610e4a0Sfrits uint_t	ugen_errmask		= (uint_t)UGEN_PRINT_ALL;
574610e4a0Sfrits uint_t	ugen_errlevel		= USB_LOG_L4;
584610e4a0Sfrits uint_t	ugen_instance_debug	= (uint_t)-1;
597c478bd9Sstevel@tonic-gate 
607c478bd9Sstevel@tonic-gate /* default endpoint descriptor */
617c478bd9Sstevel@tonic-gate static usb_ep_descr_t  ugen_default_ep_descr =
627c478bd9Sstevel@tonic-gate 	{7, 5, 0, USB_EP_ATTR_CONTROL, 8, 0};
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate /* tunables */
654610e4a0Sfrits int	ugen_busy_loop		= 60;	/* secs */
664610e4a0Sfrits int	ugen_ctrl_timeout	= 10;
674610e4a0Sfrits int	ugen_bulk_timeout	= 10;
684610e4a0Sfrits int	ugen_intr_timeout	= 10;
694610e4a0Sfrits int	ugen_enable_pm		= 0;
70*0a05e705Slc int	ugen_isoc_buf_limit	= 1000;	/* ms */
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 *);
121*0a05e705Slc static int	ugen_epx_isoc_IN_req(ugen_state_t *, ugen_ep_t *,
122*0a05e705Slc 					struct buf *, boolean_t *);
123*0a05e705Slc static int	ugen_epx_isoc_IN_start_polling(ugen_state_t *, ugen_ep_t *);
124*0a05e705Slc static void	ugen_epx_isoc_IN_stop_polling(ugen_state_t *, ugen_ep_t *);
125*0a05e705Slc static void	ugen_epx_isoc_IN_req_cb(usb_pipe_handle_t, usb_isoc_req_t *);
126*0a05e705Slc static int	ugen_epx_isoc_OUT_req(ugen_state_t *, ugen_ep_t *,
127*0a05e705Slc 					struct buf *, boolean_t *);
128*0a05e705Slc static void	ugen_epx_isoc_OUT_req_cb(usb_pipe_handle_t, usb_isoc_req_t *);
1297c478bd9Sstevel@tonic-gate 
1307c478bd9Sstevel@tonic-gate static int	ugen_eps_open(ugen_state_t *, dev_t, int);
1317c478bd9Sstevel@tonic-gate static void	ugen_eps_close(ugen_state_t *, dev_t, int);
1327c478bd9Sstevel@tonic-gate static int	ugen_eps_req(ugen_state_t *, struct buf *);
1337c478bd9Sstevel@tonic-gate static void	ugen_update_ep_descr(ugen_state_t *, ugen_ep_t *);
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate /* device status management */
1367c478bd9Sstevel@tonic-gate static int	ugen_ds_init(ugen_state_t *);
1377c478bd9Sstevel@tonic-gate static void	ugen_ds_destroy(ugen_state_t *);
1387c478bd9Sstevel@tonic-gate static int	ugen_ds_open(ugen_state_t *, dev_t, int);
1397c478bd9Sstevel@tonic-gate static void	ugen_ds_close(ugen_state_t *, dev_t, int);
1407c478bd9Sstevel@tonic-gate static int	ugen_ds_req(ugen_state_t *, struct buf *);
1417c478bd9Sstevel@tonic-gate static void	ugen_ds_change(ugen_state_t *);
1427c478bd9Sstevel@tonic-gate static int	ugen_ds_minor_nodes_create(ugen_state_t *);
1437c478bd9Sstevel@tonic-gate static void	ugen_ds_poll_wakeup(ugen_state_t *);
1447c478bd9Sstevel@tonic-gate 
1457c478bd9Sstevel@tonic-gate /* utility functions */
1467c478bd9Sstevel@tonic-gate static int	ugen_minor_index_create(ugen_state_t *, ugen_minor_t);
1477c478bd9Sstevel@tonic-gate static ugen_minor_t ugen_devt2minor(ugen_state_t *, dev_t);
1487c478bd9Sstevel@tonic-gate static void	ugen_minor_node_table_create(ugen_state_t *);
1497c478bd9Sstevel@tonic-gate static void	ugen_minor_node_table_destroy(ugen_state_t *);
1507c478bd9Sstevel@tonic-gate static void	ugen_minor_node_table_shrink(ugen_state_t *);
1517c478bd9Sstevel@tonic-gate static int	ugen_cr2lcstat(int);
1527c478bd9Sstevel@tonic-gate static void	ugen_check_mask(uint_t, uint_t *, uint_t *);
15396603521Sfrits static int	ugen_is_valid_minor_node(ugen_state_t *, dev_t);
1547c478bd9Sstevel@tonic-gate 
1557c478bd9Sstevel@tonic-gate static kmutex_t	ugen_devt_list_mutex;
1567c478bd9Sstevel@tonic-gate static ugen_devt_list_entry_t ugen_devt_list;
1577c478bd9Sstevel@tonic-gate static ugen_devt_cache_entry_t ugen_devt_cache[UGEN_DEVT_CACHE_SIZE];
1587c478bd9Sstevel@tonic-gate static uint_t	ugen_devt_cache_index;
1597c478bd9Sstevel@tonic-gate static void	ugen_store_devt(ugen_state_t *, minor_t);
1607c478bd9Sstevel@tonic-gate static ugen_state_t *ugen_devt2state(dev_t);
1617c478bd9Sstevel@tonic-gate static void	ugen_free_devt(ugen_state_t *);
1627c478bd9Sstevel@tonic-gate 
1637c478bd9Sstevel@tonic-gate /*
1647c478bd9Sstevel@tonic-gate  * usb_ugen entry points
1657c478bd9Sstevel@tonic-gate  *
1667c478bd9Sstevel@tonic-gate  * usb_ugen_get_hdl:
1677c478bd9Sstevel@tonic-gate  *	allocate and initialize handle
1687c478bd9Sstevel@tonic-gate  */
1697c478bd9Sstevel@tonic-gate usb_ugen_hdl_t
1707c478bd9Sstevel@tonic-gate usb_ugen_get_hdl(dev_info_t *dip, usb_ugen_info_t *usb_ugen_info)
1717c478bd9Sstevel@tonic-gate {
1727c478bd9Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*hdl = kmem_zalloc(sizeof (*hdl), KM_SLEEP);
1737c478bd9Sstevel@tonic-gate 	ugen_state_t		*ugenp = kmem_zalloc(sizeof (ugen_state_t),
174*0a05e705Slc 	    KM_SLEEP);
1757c478bd9Sstevel@tonic-gate 	uint_t			len, shift, limit;
1767c478bd9Sstevel@tonic-gate 	int			rval;
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate 	hdl->hdl_ugenp = ugenp;
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate 	/* masks may not overlap */
1817c478bd9Sstevel@tonic-gate 	if (usb_ugen_info->usb_ugen_minor_node_ugen_bits_mask &
1827c478bd9Sstevel@tonic-gate 	    usb_ugen_info->usb_ugen_minor_node_instance_mask) {
1837c478bd9Sstevel@tonic-gate 		usb_ugen_release_hdl((usb_ugen_hdl_t)hdl);
1847c478bd9Sstevel@tonic-gate 
1857c478bd9Sstevel@tonic-gate 		return (NULL);
1867c478bd9Sstevel@tonic-gate 	}
1877c478bd9Sstevel@tonic-gate 
1887c478bd9Sstevel@tonic-gate 	if ((rval = usb_get_dev_data(dip, &ugenp->ug_dev_data,
1897c478bd9Sstevel@tonic-gate 	    usb_owns_device(dip) ? USB_PARSE_LVL_ALL : USB_PARSE_LVL_IF,
1907c478bd9Sstevel@tonic-gate 	    0)) != USB_SUCCESS) {
1917c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
1927c478bd9Sstevel@tonic-gate 		    "usb_ugen_attach: usb_get_dev_data failed, rval=%d", rval);
1937c478bd9Sstevel@tonic-gate 
1947c478bd9Sstevel@tonic-gate 		return (NULL);
1957c478bd9Sstevel@tonic-gate 	}
1967c478bd9Sstevel@tonic-gate 
1977c478bd9Sstevel@tonic-gate 	/* Initialize state structure for this instance */
1987c478bd9Sstevel@tonic-gate 	mutex_init(&ugenp->ug_mutex, NULL, MUTEX_DRIVER,
199*0a05e705Slc 	    ugenp->ug_dev_data->dev_iblock_cookie);
2007c478bd9Sstevel@tonic-gate 
2017c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
2027c478bd9Sstevel@tonic-gate 	ugenp->ug_dip		= dip;
2037c478bd9Sstevel@tonic-gate 	ugenp->ug_instance	= ddi_get_instance(dip);
2047c478bd9Sstevel@tonic-gate 	ugenp->ug_hdl		= hdl;
2057c478bd9Sstevel@tonic-gate 
2067c478bd9Sstevel@tonic-gate 	/* Allocate a log handle for debug/error messages */
2077c478bd9Sstevel@tonic-gate 	if (strcmp(ddi_driver_name(dip), "ugen") != 0) {
2087c478bd9Sstevel@tonic-gate 		char	*name;
2097c478bd9Sstevel@tonic-gate 
2107c478bd9Sstevel@tonic-gate 		len = strlen(ddi_driver_name(dip)) + sizeof ("_ugen") + 1;
2117c478bd9Sstevel@tonic-gate 		name = kmem_alloc(len, KM_SLEEP);
2127c478bd9Sstevel@tonic-gate 		(void) snprintf(name, len, "%s_ugen", ddi_driver_name(dip));
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate 		ugenp->ug_log_hdl = usb_alloc_log_hdl(dip, name, &ugen_errlevel,
215*0a05e705Slc 		    &ugen_errmask, &ugen_instance_debug, 0);
2167c478bd9Sstevel@tonic-gate 		hdl->hdl_log_name = name;
2177c478bd9Sstevel@tonic-gate 		hdl->hdl_log_name_length = len;
2187c478bd9Sstevel@tonic-gate 	} else {
2197c478bd9Sstevel@tonic-gate 		ugenp->ug_log_hdl = usb_alloc_log_hdl(dip, "ugen",
220*0a05e705Slc 		    &ugen_errlevel,
221*0a05e705Slc 		    &ugen_errmask, &ugen_instance_debug, 0);
2227c478bd9Sstevel@tonic-gate 	}
2237c478bd9Sstevel@tonic-gate 
2247c478bd9Sstevel@tonic-gate 	hdl->hdl_dip = dip;
2257c478bd9Sstevel@tonic-gate 	hdl->hdl_flags = usb_ugen_info->usb_ugen_flags;
2267c478bd9Sstevel@tonic-gate 
2277c478bd9Sstevel@tonic-gate 	ugen_check_mask(usb_ugen_info->usb_ugen_minor_node_ugen_bits_mask,
228*0a05e705Slc 	    &shift, &limit);
2297c478bd9Sstevel@tonic-gate 	if (limit == 0) {
2307c478bd9Sstevel@tonic-gate 		usb_ugen_release_hdl((usb_ugen_hdl_t)hdl);
2317c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate 		return (NULL);
2347c478bd9Sstevel@tonic-gate 	}
2357c478bd9Sstevel@tonic-gate 	hdl->hdl_minor_node_ugen_bits_mask = usb_ugen_info->
236*0a05e705Slc 	    usb_ugen_minor_node_ugen_bits_mask;
2377c478bd9Sstevel@tonic-gate 	hdl->hdl_minor_node_ugen_bits_shift = shift;
2387c478bd9Sstevel@tonic-gate 	hdl->hdl_minor_node_ugen_bits_limit = limit;
2397c478bd9Sstevel@tonic-gate 
2407c478bd9Sstevel@tonic-gate 	ugen_check_mask(usb_ugen_info->usb_ugen_minor_node_instance_mask,
241*0a05e705Slc 	    &shift, &limit);
2427c478bd9Sstevel@tonic-gate 	if (limit == 0) {
2437c478bd9Sstevel@tonic-gate 		usb_ugen_release_hdl((usb_ugen_hdl_t)hdl);
2447c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate 		return (NULL);
2477c478bd9Sstevel@tonic-gate 	}
2487c478bd9Sstevel@tonic-gate 
2497c478bd9Sstevel@tonic-gate 	hdl->hdl_minor_node_instance_mask = usb_ugen_info->
250*0a05e705Slc 	    usb_ugen_minor_node_instance_mask;
2517c478bd9Sstevel@tonic-gate 	hdl->hdl_minor_node_instance_shift = shift;
2527c478bd9Sstevel@tonic-gate 	hdl->hdl_minor_node_instance_limit = limit;
2537c478bd9Sstevel@tonic-gate 
2547c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
2557c478bd9Sstevel@tonic-gate 	    "usb_ugen_get_hdl: instance shift=%d instance limit=%d",
2567c478bd9Sstevel@tonic-gate 	    hdl->hdl_minor_node_instance_shift,
2577c478bd9Sstevel@tonic-gate 	    hdl->hdl_minor_node_instance_limit);
2587c478bd9Sstevel@tonic-gate 
2597c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
2607c478bd9Sstevel@tonic-gate 	    "usb_ugen_get_hdl: bits shift=%d bits limit=%d",
2617c478bd9Sstevel@tonic-gate 	    hdl->hdl_minor_node_ugen_bits_shift,
2627c478bd9Sstevel@tonic-gate 	    hdl->hdl_minor_node_ugen_bits_limit);
2637c478bd9Sstevel@tonic-gate 
2647c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
2657c478bd9Sstevel@tonic-gate 
2667c478bd9Sstevel@tonic-gate 	return ((usb_ugen_hdl_t)hdl);
2677c478bd9Sstevel@tonic-gate }
2687c478bd9Sstevel@tonic-gate 
2697c478bd9Sstevel@tonic-gate 
2707c478bd9Sstevel@tonic-gate /*
2717c478bd9Sstevel@tonic-gate  * usb_ugen_release_hdl:
2727c478bd9Sstevel@tonic-gate  *	deallocate a handle
2737c478bd9Sstevel@tonic-gate  */
2747c478bd9Sstevel@tonic-gate void
2757c478bd9Sstevel@tonic-gate usb_ugen_release_hdl(usb_ugen_hdl_t usb_ugen_hdl)
2767c478bd9Sstevel@tonic-gate {
2777c478bd9Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
278*0a05e705Slc 	    (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
2797c478bd9Sstevel@tonic-gate 
2807c478bd9Sstevel@tonic-gate 	if (usb_ugen_hdl_impl) {
2817c478bd9Sstevel@tonic-gate 		ugen_state_t *ugenp = usb_ugen_hdl_impl->hdl_ugenp;
2827c478bd9Sstevel@tonic-gate 
2837c478bd9Sstevel@tonic-gate 		if (ugenp) {
2847c478bd9Sstevel@tonic-gate 			mutex_destroy(&ugenp->ug_mutex);
2857c478bd9Sstevel@tonic-gate 			usb_free_log_hdl(ugenp->ug_log_hdl);
2867c478bd9Sstevel@tonic-gate 			usb_free_dev_data(usb_ugen_hdl_impl->hdl_dip,
287*0a05e705Slc 			    ugenp->ug_dev_data);
2887c478bd9Sstevel@tonic-gate 			kmem_free(ugenp, sizeof (*ugenp));
2897c478bd9Sstevel@tonic-gate 		}
2907c478bd9Sstevel@tonic-gate 		if (usb_ugen_hdl_impl->hdl_log_name) {
2917c478bd9Sstevel@tonic-gate 			kmem_free(usb_ugen_hdl_impl->hdl_log_name,
292*0a05e705Slc 			    usb_ugen_hdl_impl->hdl_log_name_length);
2937c478bd9Sstevel@tonic-gate 		}
2947c478bd9Sstevel@tonic-gate 		kmem_free(usb_ugen_hdl_impl, sizeof (*usb_ugen_hdl_impl));
2957c478bd9Sstevel@tonic-gate 	}
2967c478bd9Sstevel@tonic-gate }
2977c478bd9Sstevel@tonic-gate 
2987c478bd9Sstevel@tonic-gate 
2997c478bd9Sstevel@tonic-gate /*
3007c478bd9Sstevel@tonic-gate  * usb_ugen_attach()
3017c478bd9Sstevel@tonic-gate  */
3027c478bd9Sstevel@tonic-gate int
3037c478bd9Sstevel@tonic-gate usb_ugen_attach(usb_ugen_hdl_t usb_ugen_hdl, ddi_attach_cmd_t cmd)
3047c478bd9Sstevel@tonic-gate {
3057c478bd9Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
306*0a05e705Slc 	    (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
3077c478bd9Sstevel@tonic-gate 	ugen_state_t		*ugenp;
3087c478bd9Sstevel@tonic-gate 	dev_info_t		*dip;
3097c478bd9Sstevel@tonic-gate 
3107c478bd9Sstevel@tonic-gate 	if (usb_ugen_hdl == NULL) {
3117c478bd9Sstevel@tonic-gate 
3127c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
3137c478bd9Sstevel@tonic-gate 	}
3147c478bd9Sstevel@tonic-gate 
3157c478bd9Sstevel@tonic-gate 	ugenp = usb_ugen_hdl_impl->hdl_ugenp;
3167c478bd9Sstevel@tonic-gate 	dip = usb_ugen_hdl_impl->hdl_dip;
3177c478bd9Sstevel@tonic-gate 
3187c478bd9Sstevel@tonic-gate 
3197c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
3207c478bd9Sstevel@tonic-gate 	    "usb_ugen_attach: cmd=%d", cmd);
3217c478bd9Sstevel@tonic-gate 
3227c478bd9Sstevel@tonic-gate 	switch (cmd) {
3237c478bd9Sstevel@tonic-gate 	case DDI_ATTACH:
3247c478bd9Sstevel@tonic-gate 
3257c478bd9Sstevel@tonic-gate 		break;
3267c478bd9Sstevel@tonic-gate 	case DDI_RESUME:
3277c478bd9Sstevel@tonic-gate 		ugen_cpr_resume(ugenp);
3287c478bd9Sstevel@tonic-gate 
3297c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
3307c478bd9Sstevel@tonic-gate 	default:
3317c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, NULL,
3327c478bd9Sstevel@tonic-gate 		    "usb_ugen_attach: unknown command");
3337c478bd9Sstevel@tonic-gate 
3347c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
3357c478bd9Sstevel@tonic-gate 	}
3367c478bd9Sstevel@tonic-gate 
3377c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
3387c478bd9Sstevel@tonic-gate 	ugenp->ug_ser_cookie =
3397c478bd9Sstevel@tonic-gate 	    usb_init_serialization(dip, USB_INIT_SER_CHECK_SAME_THREAD);
3407c478bd9Sstevel@tonic-gate 	ugenp->ug_cleanup_flags |= UGEN_INIT_LOCKS;
3417c478bd9Sstevel@tonic-gate 
3427c478bd9Sstevel@tonic-gate 	/* Get maximum bulk transfer size supported by the HCD */
3437c478bd9Sstevel@tonic-gate 	if (usb_pipe_get_max_bulk_transfer_size(dip,
3447c478bd9Sstevel@tonic-gate 	    &ugenp->ug_max_bulk_xfer_sz) != USB_SUCCESS) {
3457c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
3467c478bd9Sstevel@tonic-gate 		    "usb_ugen_attach: Getting max bulk xfer sz failed");
3477c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
3487c478bd9Sstevel@tonic-gate 
3497c478bd9Sstevel@tonic-gate 		goto fail;
3507c478bd9Sstevel@tonic-gate 	}
3517c478bd9Sstevel@tonic-gate 
3527c478bd9Sstevel@tonic-gate 	/* table for mapping 48 bit minor codes to 9 bit index (for ugen) */
3537c478bd9Sstevel@tonic-gate 	ugen_minor_node_table_create(ugenp);
3547c478bd9Sstevel@tonic-gate 
3557c478bd9Sstevel@tonic-gate 	/* prepare device status node handling */
3567c478bd9Sstevel@tonic-gate 	if (ugen_ds_init(ugenp) != USB_SUCCESS) {
3577c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
3587c478bd9Sstevel@tonic-gate 		    "usb_ugen_attach: preparing dev status failed");
3597c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
3607c478bd9Sstevel@tonic-gate 
3617c478bd9Sstevel@tonic-gate 		goto fail;
3627c478bd9Sstevel@tonic-gate 	}
3637c478bd9Sstevel@tonic-gate 
3647c478bd9Sstevel@tonic-gate 	/* prepare all available xfer and status endpoints nodes */
3657c478bd9Sstevel@tonic-gate 	if (ugen_epxs_init(ugenp) != USB_SUCCESS) {
3667c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
3677c478bd9Sstevel@tonic-gate 		    "usb_ugen_attach: preparing endpoints failed");
3687c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
3697c478bd9Sstevel@tonic-gate 
3707c478bd9Sstevel@tonic-gate 		goto fail;
3717c478bd9Sstevel@tonic-gate 	}
3727c478bd9Sstevel@tonic-gate 
3737c478bd9Sstevel@tonic-gate 	/* reduce table size if not all entries are used */
3747c478bd9Sstevel@tonic-gate 	ugen_minor_node_table_shrink(ugenp);
3757c478bd9Sstevel@tonic-gate 
3767c478bd9Sstevel@tonic-gate 	/* we are ready to go */
3777c478bd9Sstevel@tonic-gate 	ugenp->ug_dev_state = USB_DEV_ONLINE;
3787c478bd9Sstevel@tonic-gate 
3797c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
3807c478bd9Sstevel@tonic-gate 
3817c478bd9Sstevel@tonic-gate 	/* prepare PM */
3827c478bd9Sstevel@tonic-gate 	if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
3837c478bd9Sstevel@tonic-gate 		ugen_pm_init(ugenp);
3847c478bd9Sstevel@tonic-gate 	}
3857c478bd9Sstevel@tonic-gate 
3867c478bd9Sstevel@tonic-gate 	/*
3877c478bd9Sstevel@tonic-gate 	 * if ugen driver, kill all child nodes otherwise set cfg fails
3887c478bd9Sstevel@tonic-gate 	 * if requested
3897c478bd9Sstevel@tonic-gate 	 */
3907c478bd9Sstevel@tonic-gate 	if (usb_owns_device(dip) &&
3917c478bd9Sstevel@tonic-gate 	    (usb_ugen_hdl_impl->hdl_flags & USB_UGEN_REMOVE_CHILDREN)) {
3927c478bd9Sstevel@tonic-gate 		dev_info_t *cdip;
3937c478bd9Sstevel@tonic-gate 
3947c478bd9Sstevel@tonic-gate 		/* save cfgidx so we can restore on detach */
3957c478bd9Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
3967c478bd9Sstevel@tonic-gate 		ugenp->ug_initial_cfgidx = usb_get_current_cfgidx(dip);
3977c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate 		for (cdip = ddi_get_child(dip); cdip; ) {
4007c478bd9Sstevel@tonic-gate 			dev_info_t *next = ddi_get_next_sibling(cdip);
4017c478bd9Sstevel@tonic-gate 			(void) ddi_remove_child(cdip, 0);
4027c478bd9Sstevel@tonic-gate 			cdip = next;
4037c478bd9Sstevel@tonic-gate 		}
4047c478bd9Sstevel@tonic-gate 	}
4057c478bd9Sstevel@tonic-gate 
4067c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
4077c478bd9Sstevel@tonic-gate fail:
4087c478bd9Sstevel@tonic-gate 	if (ugenp) {
4097c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
4107c478bd9Sstevel@tonic-gate 		    "attach fail");
4117c478bd9Sstevel@tonic-gate 		(void) ugen_cleanup(ugenp);
4127c478bd9Sstevel@tonic-gate 	}
4137c478bd9Sstevel@tonic-gate 
4147c478bd9Sstevel@tonic-gate 	return (DDI_FAILURE);
4157c478bd9Sstevel@tonic-gate }
4167c478bd9Sstevel@tonic-gate 
4177c478bd9Sstevel@tonic-gate 
4187c478bd9Sstevel@tonic-gate /*
4197c478bd9Sstevel@tonic-gate  * usb_ugen_detach()
4207c478bd9Sstevel@tonic-gate  */
4217c478bd9Sstevel@tonic-gate int
4227c478bd9Sstevel@tonic-gate usb_ugen_detach(usb_ugen_hdl_t usb_ugen_hdl, ddi_detach_cmd_t cmd)
4237c478bd9Sstevel@tonic-gate {
4247c478bd9Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
425*0a05e705Slc 	    (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
4267c478bd9Sstevel@tonic-gate 	int			rval = USB_FAILURE;
4277c478bd9Sstevel@tonic-gate 
4287c478bd9Sstevel@tonic-gate 	if (usb_ugen_hdl) {
4297c478bd9Sstevel@tonic-gate 		ugen_state_t *ugenp = usb_ugen_hdl_impl->hdl_ugenp;
4307c478bd9Sstevel@tonic-gate 
4317c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
4327c478bd9Sstevel@tonic-gate 		    "usb_ugen_detach cmd %d", cmd);
4337c478bd9Sstevel@tonic-gate 
4347c478bd9Sstevel@tonic-gate 		switch (cmd) {
4357c478bd9Sstevel@tonic-gate 		case DDI_DETACH:
4367c478bd9Sstevel@tonic-gate 			rval = ugen_cleanup(ugenp);
4377c478bd9Sstevel@tonic-gate 
4387c478bd9Sstevel@tonic-gate 			break;
4397c478bd9Sstevel@tonic-gate 		case DDI_SUSPEND:
4407c478bd9Sstevel@tonic-gate 			rval = ugen_cpr_suspend(ugenp);
4417c478bd9Sstevel@tonic-gate 
4427c478bd9Sstevel@tonic-gate 			break;
4437c478bd9Sstevel@tonic-gate 		default:
4447c478bd9Sstevel@tonic-gate 
4457c478bd9Sstevel@tonic-gate 			break;
4467c478bd9Sstevel@tonic-gate 		}
4477c478bd9Sstevel@tonic-gate 	}
4487c478bd9Sstevel@tonic-gate 
4497c478bd9Sstevel@tonic-gate 	return (rval);
4507c478bd9Sstevel@tonic-gate }
4517c478bd9Sstevel@tonic-gate 
4527c478bd9Sstevel@tonic-gate 
4537c478bd9Sstevel@tonic-gate /*
4547c478bd9Sstevel@tonic-gate  * ugen_cleanup()
4557c478bd9Sstevel@tonic-gate  */
4567c478bd9Sstevel@tonic-gate static int
4577c478bd9Sstevel@tonic-gate ugen_cleanup(ugen_state_t *ugenp)
4587c478bd9Sstevel@tonic-gate {
4597c478bd9Sstevel@tonic-gate 	dev_info_t *dip = ugenp->ug_dip;
4607c478bd9Sstevel@tonic-gate 
4617c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl, "ugen_cleanup");
4627c478bd9Sstevel@tonic-gate 
4637c478bd9Sstevel@tonic-gate 	if (ugenp->ug_cleanup_flags & UGEN_INIT_LOCKS) {
4647c478bd9Sstevel@tonic-gate 
4657c478bd9Sstevel@tonic-gate 		/* shutdown all endpoints */
4667c478bd9Sstevel@tonic-gate 		ugen_epx_shutdown(ugenp);
4677c478bd9Sstevel@tonic-gate 
4687c478bd9Sstevel@tonic-gate 		/*
4697c478bd9Sstevel@tonic-gate 		 * At this point, no new activity can be initiated.
4707c478bd9Sstevel@tonic-gate 		 * The driver has disabled hotplug callbacks.
4717c478bd9Sstevel@tonic-gate 		 * The Solaris framework has disabled
4727c478bd9Sstevel@tonic-gate 		 * new opens on a device being detached, and does not
4737c478bd9Sstevel@tonic-gate 		 * allow detaching an open device. PM should power
4747c478bd9Sstevel@tonic-gate 		 * down while we are detaching
4757c478bd9Sstevel@tonic-gate 		 *
4767c478bd9Sstevel@tonic-gate 		 * The following ensures that any other driver
4777c478bd9Sstevel@tonic-gate 		 * activity must have drained (paranoia)
4787c478bd9Sstevel@tonic-gate 		 */
4797c478bd9Sstevel@tonic-gate 		(void) usb_serialize_access(ugenp->ug_ser_cookie,
480*0a05e705Slc 		    USB_WAIT, 0);
4817c478bd9Sstevel@tonic-gate 		usb_release_access(ugenp->ug_ser_cookie);
4827c478bd9Sstevel@tonic-gate 
4837c478bd9Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
4847c478bd9Sstevel@tonic-gate 		ASSERT(ugenp->ug_open_count == 0);
4857c478bd9Sstevel@tonic-gate 		ASSERT(ugenp->ug_pending_cmds == 0);
4867c478bd9Sstevel@tonic-gate 
4877c478bd9Sstevel@tonic-gate 		/* dismantle in reverse order */
4887c478bd9Sstevel@tonic-gate 		ugen_pm_destroy(ugenp);
4897c478bd9Sstevel@tonic-gate 		ugen_epxs_destroy(ugenp);
4907c478bd9Sstevel@tonic-gate 		ugen_ds_destroy(ugenp);
4917c478bd9Sstevel@tonic-gate 		ugen_minor_node_table_destroy(ugenp);
4927c478bd9Sstevel@tonic-gate 
4937c478bd9Sstevel@tonic-gate 
4947c478bd9Sstevel@tonic-gate 		/* restore to initial configuration */
4957c478bd9Sstevel@tonic-gate 		if (usb_owns_device(dip) &&
4967c478bd9Sstevel@tonic-gate 		    (ugenp->ug_dev_state != USB_DEV_DISCONNECTED)) {
4977c478bd9Sstevel@tonic-gate 			int idx = ugenp->ug_initial_cfgidx;
4987c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
4997c478bd9Sstevel@tonic-gate 			(void) usb_set_cfg(dip, idx,
5007c478bd9Sstevel@tonic-gate 			    USB_FLAGS_SLEEP, NULL, NULL);
5017c478bd9Sstevel@tonic-gate 		} else {
5027c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
5037c478bd9Sstevel@tonic-gate 		}
5047c478bd9Sstevel@tonic-gate 
5057c478bd9Sstevel@tonic-gate 		usb_fini_serialization(ugenp->ug_ser_cookie);
5067c478bd9Sstevel@tonic-gate 	}
5077c478bd9Sstevel@tonic-gate 
5087c478bd9Sstevel@tonic-gate 	ddi_prop_remove_all(dip);
5097c478bd9Sstevel@tonic-gate 	ddi_remove_minor_node(dip, NULL);
5107c478bd9Sstevel@tonic-gate 
5117c478bd9Sstevel@tonic-gate 	ugen_free_devt(ugenp);
5127c478bd9Sstevel@tonic-gate 
5137c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
5147c478bd9Sstevel@tonic-gate }
5157c478bd9Sstevel@tonic-gate 
5167c478bd9Sstevel@tonic-gate 
5177c478bd9Sstevel@tonic-gate /*
5187c478bd9Sstevel@tonic-gate  * ugen_cpr_suspend
5197c478bd9Sstevel@tonic-gate  */
5207c478bd9Sstevel@tonic-gate static int
5217c478bd9Sstevel@tonic-gate ugen_cpr_suspend(ugen_state_t *ugenp)
5227c478bd9Sstevel@tonic-gate {
5237c478bd9Sstevel@tonic-gate 	int		rval = USB_FAILURE;
5247c478bd9Sstevel@tonic-gate 	int		i;
5257c478bd9Sstevel@tonic-gate 	int		prev_state;
5267c478bd9Sstevel@tonic-gate 
5277c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CPR, ugenp->ug_log_hdl,
5287c478bd9Sstevel@tonic-gate 	    "ugen_cpr_suspend:");
5297c478bd9Sstevel@tonic-gate 
5307c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
5317c478bd9Sstevel@tonic-gate 	switch (ugenp->ug_dev_state) {
5327c478bd9Sstevel@tonic-gate 	case USB_DEV_ONLINE:
5337c478bd9Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
5347c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_CPR, ugenp->ug_log_hdl,
5357c478bd9Sstevel@tonic-gate 		    "ugen_cpr_suspend:");
5367c478bd9Sstevel@tonic-gate 
5377c478bd9Sstevel@tonic-gate 		prev_state = ugenp->ug_dev_state;
5387c478bd9Sstevel@tonic-gate 		ugenp->ug_dev_state = USB_DEV_SUSPENDED;
5397c478bd9Sstevel@tonic-gate 
5407c478bd9Sstevel@tonic-gate 		if (ugenp->ug_open_count) {
5417c478bd9Sstevel@tonic-gate 			/* drain outstanding cmds */
5427c478bd9Sstevel@tonic-gate 			for (i = 0; i < ugen_busy_loop; i++) {
5437c478bd9Sstevel@tonic-gate 				if (ugenp->ug_pending_cmds == 0) {
5447c478bd9Sstevel@tonic-gate 
5457c478bd9Sstevel@tonic-gate 					break;
5467c478bd9Sstevel@tonic-gate 				}
5477c478bd9Sstevel@tonic-gate 				mutex_exit(&ugenp->ug_mutex);
5487c478bd9Sstevel@tonic-gate 				delay(drv_usectohz(100000));
5497c478bd9Sstevel@tonic-gate 				mutex_enter(&ugenp->ug_mutex);
5507c478bd9Sstevel@tonic-gate 			}
5517c478bd9Sstevel@tonic-gate 
5527c478bd9Sstevel@tonic-gate 			/* if still outstanding cmds, fail suspend */
5537c478bd9Sstevel@tonic-gate 			if (ugenp->ug_pending_cmds) {
5547c478bd9Sstevel@tonic-gate 				ugenp->ug_dev_state = prev_state;
5557c478bd9Sstevel@tonic-gate 
5567c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(UGEN_PRINT_CPR,
5577c478bd9Sstevel@tonic-gate 				    ugenp->ug_log_hdl,
5587c478bd9Sstevel@tonic-gate 				    "ugen_cpr_suspend: pending %d",
5597c478bd9Sstevel@tonic-gate 				    ugenp->ug_pending_cmds);
5607c478bd9Sstevel@tonic-gate 
5617c478bd9Sstevel@tonic-gate 				rval =	USB_FAILURE;
5627c478bd9Sstevel@tonic-gate 				break;
5637c478bd9Sstevel@tonic-gate 			}
5647c478bd9Sstevel@tonic-gate 
5657c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
5667c478bd9Sstevel@tonic-gate 			(void) usb_serialize_access(ugenp->ug_ser_cookie,
567*0a05e705Slc 			    USB_WAIT, 0);
5687c478bd9Sstevel@tonic-gate 			/* close all pipes */
5697c478bd9Sstevel@tonic-gate 			ugen_epx_shutdown(ugenp);
5707c478bd9Sstevel@tonic-gate 
5717c478bd9Sstevel@tonic-gate 			usb_release_access(ugenp->ug_ser_cookie);
5727c478bd9Sstevel@tonic-gate 
5737c478bd9Sstevel@tonic-gate 			mutex_enter(&ugenp->ug_mutex);
5747c478bd9Sstevel@tonic-gate 		}
5757c478bd9Sstevel@tonic-gate 
5767c478bd9Sstevel@tonic-gate 		/* wakeup devstat reads and polls */
5777c478bd9Sstevel@tonic-gate 		ugen_ds_change(ugenp);
5787c478bd9Sstevel@tonic-gate 		ugen_ds_poll_wakeup(ugenp);
5797c478bd9Sstevel@tonic-gate 
5807c478bd9Sstevel@tonic-gate 		rval = USB_SUCCESS;
5817c478bd9Sstevel@tonic-gate 		break;
5827c478bd9Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
5837c478bd9Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RESUME:
5847c478bd9Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
5857c478bd9Sstevel@tonic-gate 	default:
5867c478bd9Sstevel@tonic-gate 
5877c478bd9Sstevel@tonic-gate 		break;
5887c478bd9Sstevel@tonic-gate 	}
5897c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
5907c478bd9Sstevel@tonic-gate 
5917c478bd9Sstevel@tonic-gate 	return (rval);
5927c478bd9Sstevel@tonic-gate }
5937c478bd9Sstevel@tonic-gate 
5947c478bd9Sstevel@tonic-gate /*
5957c478bd9Sstevel@tonic-gate  * ugen_cpr_resume
5967c478bd9Sstevel@tonic-gate  */
5977c478bd9Sstevel@tonic-gate static void
5987c478bd9Sstevel@tonic-gate ugen_cpr_resume(ugen_state_t *ugenp)
5997c478bd9Sstevel@tonic-gate {
6007c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CPR, ugenp->ug_log_hdl,
6017c478bd9Sstevel@tonic-gate 	    "ugen_cpr_resume:");
6027c478bd9Sstevel@tonic-gate 
6037c478bd9Sstevel@tonic-gate 	ugen_restore_state(ugenp);
6047c478bd9Sstevel@tonic-gate }
6057c478bd9Sstevel@tonic-gate 
6067c478bd9Sstevel@tonic-gate /*
6077c478bd9Sstevel@tonic-gate  * usb_ugen_disconnect_ev_cb:
6087c478bd9Sstevel@tonic-gate  */
6097c478bd9Sstevel@tonic-gate int
6107c478bd9Sstevel@tonic-gate usb_ugen_disconnect_ev_cb(usb_ugen_hdl_t usb_ugen_hdl)
6117c478bd9Sstevel@tonic-gate {
6127c478bd9Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
613*0a05e705Slc 	    (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
6147c478bd9Sstevel@tonic-gate 	ugen_state_t		*ugenp;
6157c478bd9Sstevel@tonic-gate 
6167c478bd9Sstevel@tonic-gate 	if (usb_ugen_hdl_impl == NULL) {
6177c478bd9Sstevel@tonic-gate 
6187c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
6197c478bd9Sstevel@tonic-gate 	}
6207c478bd9Sstevel@tonic-gate 
6217c478bd9Sstevel@tonic-gate 	ugenp = usb_ugen_hdl_impl->hdl_ugenp;
6227c478bd9Sstevel@tonic-gate 
6237c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_HOTPLUG, ugenp->ug_log_hdl,
6247c478bd9Sstevel@tonic-gate 	    "usb_ugen_disconnect_ev_cb:");
6257c478bd9Sstevel@tonic-gate 
6267c478bd9Sstevel@tonic-gate 	/* get exclusive access */
6277c478bd9Sstevel@tonic-gate 	(void) usb_serialize_access(ugenp->ug_ser_cookie, USB_WAIT, 0);
6287c478bd9Sstevel@tonic-gate 
6297c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
6307c478bd9Sstevel@tonic-gate 	ugenp->ug_dev_state = USB_DEV_DISCONNECTED;
6317c478bd9Sstevel@tonic-gate 	if (ugenp->ug_open_count) {
6327c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
6337c478bd9Sstevel@tonic-gate 
6347c478bd9Sstevel@tonic-gate 		/* close all pipes */
6357c478bd9Sstevel@tonic-gate 		(void) ugen_epx_shutdown(ugenp);
6367c478bd9Sstevel@tonic-gate 
6377c478bd9Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
6387c478bd9Sstevel@tonic-gate 	}
6397c478bd9Sstevel@tonic-gate 
6407c478bd9Sstevel@tonic-gate 
6417c478bd9Sstevel@tonic-gate 	/* wakeup devstat reads and polls */
6427c478bd9Sstevel@tonic-gate 	ugen_ds_change(ugenp);
6437c478bd9Sstevel@tonic-gate 	ugen_ds_poll_wakeup(ugenp);
6447c478bd9Sstevel@tonic-gate 
6457c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
6467c478bd9Sstevel@tonic-gate 	usb_release_access(ugenp->ug_ser_cookie);
6477c478bd9Sstevel@tonic-gate 
6487c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
6497c478bd9Sstevel@tonic-gate }
6507c478bd9Sstevel@tonic-gate 
6517c478bd9Sstevel@tonic-gate 
6527c478bd9Sstevel@tonic-gate /*
6537c478bd9Sstevel@tonic-gate  * usb_ugen_reconnect_ev_cb:
6547c478bd9Sstevel@tonic-gate  */
6557c478bd9Sstevel@tonic-gate int
6567c478bd9Sstevel@tonic-gate usb_ugen_reconnect_ev_cb(usb_ugen_hdl_t usb_ugen_hdl)
6577c478bd9Sstevel@tonic-gate {
6587c478bd9Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
659*0a05e705Slc 	    (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
6607c478bd9Sstevel@tonic-gate 	ugen_state_t		*ugenp = usb_ugen_hdl_impl->hdl_ugenp;
6617c478bd9Sstevel@tonic-gate 
6627c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_HOTPLUG, ugenp->ug_log_hdl,
6637c478bd9Sstevel@tonic-gate 	    "usb_ugen_reconnect_ev_cb:");
6647c478bd9Sstevel@tonic-gate 
6657c478bd9Sstevel@tonic-gate 	ugen_restore_state(ugenp);
6667c478bd9Sstevel@tonic-gate 
6677c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
6687c478bd9Sstevel@tonic-gate }
6697c478bd9Sstevel@tonic-gate 
6707c478bd9Sstevel@tonic-gate 
6717c478bd9Sstevel@tonic-gate /*
6727c478bd9Sstevel@tonic-gate  * ugen_restore_state:
6737c478bd9Sstevel@tonic-gate  *	Check for same device; if a different device is attached, set
6747c478bd9Sstevel@tonic-gate  *	the device status to disconnected.
6757c478bd9Sstevel@tonic-gate  *	If we were open, then set to UNAVAILABLE until all endpoints have
6767c478bd9Sstevel@tonic-gate  *	be closed.
6777c478bd9Sstevel@tonic-gate  */
6787c478bd9Sstevel@tonic-gate static void
6797c478bd9Sstevel@tonic-gate ugen_restore_state(ugen_state_t *ugenp)
6807c478bd9Sstevel@tonic-gate {
6817c478bd9Sstevel@tonic-gate 	dev_info_t *dip = ugenp->ug_dip;
6827c478bd9Sstevel@tonic-gate 
6837c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_HOTPLUG, ugenp->ug_log_hdl,
6847c478bd9Sstevel@tonic-gate 	    "ugen_restore_state");
6857c478bd9Sstevel@tonic-gate 
6867c478bd9Sstevel@tonic-gate 	/* first raise power */
6877c478bd9Sstevel@tonic-gate 	if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
6887c478bd9Sstevel@tonic-gate 		ugen_pm_busy_component(ugenp);
6897c478bd9Sstevel@tonic-gate 		(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
6907c478bd9Sstevel@tonic-gate 	}
6917c478bd9Sstevel@tonic-gate 
6927c478bd9Sstevel@tonic-gate 	/* Check if we are talking to the same device */
6937c478bd9Sstevel@tonic-gate 	if (usb_check_same_device(dip, ugenp->ug_log_hdl,
6947c478bd9Sstevel@tonic-gate 	    USB_LOG_L0, UGEN_PRINT_HOTPLUG, USB_CHK_ALL, NULL) ==
6957c478bd9Sstevel@tonic-gate 	    USB_FAILURE) {
6967c478bd9Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
6977c478bd9Sstevel@tonic-gate 		ugenp->ug_dev_state = USB_DEV_DISCONNECTED;
6987c478bd9Sstevel@tonic-gate 
6997c478bd9Sstevel@tonic-gate 		/* wakeup devstat reads and polls */
7007c478bd9Sstevel@tonic-gate 		ugen_ds_change(ugenp);
7017c478bd9Sstevel@tonic-gate 		ugen_ds_poll_wakeup(ugenp);
7027c478bd9Sstevel@tonic-gate 
7037c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
7047c478bd9Sstevel@tonic-gate 
7057c478bd9Sstevel@tonic-gate 		if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
7067c478bd9Sstevel@tonic-gate 			ugen_pm_idle_component(ugenp);
7077c478bd9Sstevel@tonic-gate 		}
7087c478bd9Sstevel@tonic-gate 
7097c478bd9Sstevel@tonic-gate 		return;
7107c478bd9Sstevel@tonic-gate 	}
7117c478bd9Sstevel@tonic-gate 
7127c478bd9Sstevel@tonic-gate 	/*
7137c478bd9Sstevel@tonic-gate 	 * get exclusive access, we don't want to change state in the
7147c478bd9Sstevel@tonic-gate 	 * middle of some other actions
7157c478bd9Sstevel@tonic-gate 	 */
7167c478bd9Sstevel@tonic-gate 	(void) usb_serialize_access(ugenp->ug_ser_cookie, USB_WAIT, 0);
7177c478bd9Sstevel@tonic-gate 
7187c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
7197c478bd9Sstevel@tonic-gate 	switch (ugenp->ug_dev_state) {
7207c478bd9Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
7217c478bd9Sstevel@tonic-gate 		ugenp->ug_dev_state = (ugenp->ug_open_count == 0) ?
7227c478bd9Sstevel@tonic-gate 		    USB_DEV_ONLINE : USB_UGEN_DEV_UNAVAILABLE_RECONNECT;
7237c478bd9Sstevel@tonic-gate 
7247c478bd9Sstevel@tonic-gate 		break;
7257c478bd9Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
7267c478bd9Sstevel@tonic-gate 		ugenp->ug_dev_state = (ugenp->ug_open_count == 0) ?
7277c478bd9Sstevel@tonic-gate 		    USB_DEV_ONLINE : USB_UGEN_DEV_UNAVAILABLE_RESUME;
7287c478bd9Sstevel@tonic-gate 
7297c478bd9Sstevel@tonic-gate 		break;
7307c478bd9Sstevel@tonic-gate 	}
7317c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_HOTPLUG, ugenp->ug_log_hdl,
7327c478bd9Sstevel@tonic-gate 	    "ugen_restore_state: state=%d, opencount=%d",
7337c478bd9Sstevel@tonic-gate 	    ugenp->ug_dev_state, ugenp->ug_open_count);
7347c478bd9Sstevel@tonic-gate 
7357c478bd9Sstevel@tonic-gate 	/* wakeup devstat reads and polls */
7367c478bd9Sstevel@tonic-gate 	ugen_ds_change(ugenp);
7377c478bd9Sstevel@tonic-gate 	ugen_ds_poll_wakeup(ugenp);
7387c478bd9Sstevel@tonic-gate 
7397c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
7407c478bd9Sstevel@tonic-gate 	usb_release_access(ugenp->ug_ser_cookie);
7417c478bd9Sstevel@tonic-gate 
7427c478bd9Sstevel@tonic-gate 	if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
7437c478bd9Sstevel@tonic-gate 		ugen_pm_idle_component(ugenp);
7447c478bd9Sstevel@tonic-gate 	}
7457c478bd9Sstevel@tonic-gate }
7467c478bd9Sstevel@tonic-gate 
7477c478bd9Sstevel@tonic-gate 
7487c478bd9Sstevel@tonic-gate /*
7497c478bd9Sstevel@tonic-gate  * usb_ugen_open:
7507c478bd9Sstevel@tonic-gate  */
7517c478bd9Sstevel@tonic-gate /* ARGSUSED */
7527c478bd9Sstevel@tonic-gate int
7537c478bd9Sstevel@tonic-gate usb_ugen_open(usb_ugen_hdl_t usb_ugen_hdl, dev_t *devp, int flag, int sflag,
7547c478bd9Sstevel@tonic-gate     cred_t *cr)
7557c478bd9Sstevel@tonic-gate {
7567c478bd9Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
757*0a05e705Slc 	    (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
7587c478bd9Sstevel@tonic-gate 	ugen_state_t		*ugenp;
7597c478bd9Sstevel@tonic-gate 	int			rval;
7607c478bd9Sstevel@tonic-gate 	int			minor_node_type;
7617c478bd9Sstevel@tonic-gate 
7627c478bd9Sstevel@tonic-gate 	if (usb_ugen_hdl == NULL) {
7637c478bd9Sstevel@tonic-gate 
7647c478bd9Sstevel@tonic-gate 		return (EINVAL);
7657c478bd9Sstevel@tonic-gate 	}
7667c478bd9Sstevel@tonic-gate 
7677c478bd9Sstevel@tonic-gate 	ugenp = usb_ugen_hdl_impl->hdl_ugenp;
76896603521Sfrits 
76996603521Sfrits 	if (ugen_is_valid_minor_node(ugenp, *devp) != USB_SUCCESS) {
77096603521Sfrits 
77196603521Sfrits 		return (EINVAL);
77296603521Sfrits 	}
77396603521Sfrits 
7747c478bd9Sstevel@tonic-gate 	minor_node_type = UGEN_MINOR_TYPE(ugenp, *devp);
7757c478bd9Sstevel@tonic-gate 
7767c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
7777c478bd9Sstevel@tonic-gate 	    "usb_ugen_open: minor=%u", getminor(*devp));
7787c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
7797c478bd9Sstevel@tonic-gate 	    "cfgval=%" PRIu64 " cfgidx=%" PRIu64 " if=%" PRIu64
7807c478bd9Sstevel@tonic-gate 	    " alt=%" PRIu64 " epidx=%" PRIu64 " type=0x%" PRIx64,
7817c478bd9Sstevel@tonic-gate 	    UGEN_MINOR_CFGVAL(ugenp, *devp), UGEN_MINOR_CFGIDX(ugenp, *devp),
7827c478bd9Sstevel@tonic-gate 	    UGEN_MINOR_IF(ugenp, *devp), UGEN_MINOR_ALT(ugenp, *devp),
7837c478bd9Sstevel@tonic-gate 	    UGEN_MINOR_EPIDX(ugenp, *devp), UGEN_MINOR_TYPE(ugenp, *devp));
7847c478bd9Sstevel@tonic-gate 
7857c478bd9Sstevel@tonic-gate 	/* first check for legal open flags */
7867c478bd9Sstevel@tonic-gate 	if ((rval = ugen_check_open_flags(ugenp, *devp, flag)) != 0) {
7877c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
7887c478bd9Sstevel@tonic-gate 		    "usb_ugen_open: check failed, rval=%d", rval);
7897c478bd9Sstevel@tonic-gate 
7907c478bd9Sstevel@tonic-gate 		return (rval);
7917c478bd9Sstevel@tonic-gate 	}
7927c478bd9Sstevel@tonic-gate 
7937c478bd9Sstevel@tonic-gate 	/* exclude other threads including other opens */
7947c478bd9Sstevel@tonic-gate 	if (usb_serialize_access(ugenp->ug_ser_cookie,
7957c478bd9Sstevel@tonic-gate 	    USB_WAIT_SIG, 0) <= 0) {
7967c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
7977c478bd9Sstevel@tonic-gate 		    "usb_ugen_open: interrupted");
7987c478bd9Sstevel@tonic-gate 
7997c478bd9Sstevel@tonic-gate 		return (EINTR);
8007c478bd9Sstevel@tonic-gate 	}
8017c478bd9Sstevel@tonic-gate 
8027c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
8037c478bd9Sstevel@tonic-gate 
8047c478bd9Sstevel@tonic-gate 	/* always allow open of dev stat node */
8057c478bd9Sstevel@tonic-gate 	if (minor_node_type != UGEN_MINOR_DEV_STAT_NODE) {
8067c478bd9Sstevel@tonic-gate 
8077c478bd9Sstevel@tonic-gate 		/* if we are not online or powered down, fail open */
8087c478bd9Sstevel@tonic-gate 		switch (ugenp->ug_dev_state) {
8097c478bd9Sstevel@tonic-gate 		case USB_DEV_ONLINE:
8107c478bd9Sstevel@tonic-gate 
8117c478bd9Sstevel@tonic-gate 			break;
8127c478bd9Sstevel@tonic-gate 		case USB_DEV_DISCONNECTED:
8137c478bd9Sstevel@tonic-gate 			rval = ENODEV;
8147c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
8157c478bd9Sstevel@tonic-gate 
8167c478bd9Sstevel@tonic-gate 			goto done;
8177c478bd9Sstevel@tonic-gate 		case USB_DEV_SUSPENDED:
8187c478bd9Sstevel@tonic-gate 		case USB_UGEN_DEV_UNAVAILABLE_RESUME:
8197c478bd9Sstevel@tonic-gate 		case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
8207c478bd9Sstevel@tonic-gate 		default:
8217c478bd9Sstevel@tonic-gate 			rval = EBADF;
8227c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
8237c478bd9Sstevel@tonic-gate 
8247c478bd9Sstevel@tonic-gate 			goto done;
8257c478bd9Sstevel@tonic-gate 		}
8267c478bd9Sstevel@tonic-gate 	}
8277c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
8287c478bd9Sstevel@tonic-gate 
8297c478bd9Sstevel@tonic-gate 	/* open node depending on type */
8307c478bd9Sstevel@tonic-gate 	switch (minor_node_type) {
8317c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_EP_XFER_NODE:
8327c478bd9Sstevel@tonic-gate 		if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
8337c478bd9Sstevel@tonic-gate 			ugen_pm_busy_component(ugenp);
8347c478bd9Sstevel@tonic-gate 			(void) pm_raise_power(ugenp->ug_dip, 0,
835*0a05e705Slc 			    USB_DEV_OS_FULL_PWR);
8367c478bd9Sstevel@tonic-gate 		}
8377c478bd9Sstevel@tonic-gate 
8387c478bd9Sstevel@tonic-gate 		rval = ugen_epx_open(ugenp, *devp, flag);
8397c478bd9Sstevel@tonic-gate 		if (rval == 0) {
8407c478bd9Sstevel@tonic-gate 			mutex_enter(&ugenp->ug_mutex);
8417c478bd9Sstevel@tonic-gate 			ugenp->ug_open_count++;
8427c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
8437c478bd9Sstevel@tonic-gate 		} else {
8447c478bd9Sstevel@tonic-gate 			if (ugenp->ug_hdl->hdl_flags &
8457c478bd9Sstevel@tonic-gate 			    USB_UGEN_ENABLE_PM) {
8467c478bd9Sstevel@tonic-gate 				ugen_pm_idle_component(ugenp);
8477c478bd9Sstevel@tonic-gate 			}
8487c478bd9Sstevel@tonic-gate 		}
8497c478bd9Sstevel@tonic-gate 
8507c478bd9Sstevel@tonic-gate 		break;
8517c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_EP_STAT_NODE:
8527c478bd9Sstevel@tonic-gate 		rval = ugen_eps_open(ugenp, *devp, flag);
8537c478bd9Sstevel@tonic-gate 		if (rval == 0) {
8547c478bd9Sstevel@tonic-gate 			mutex_enter(&ugenp->ug_mutex);
8557c478bd9Sstevel@tonic-gate 			ugenp->ug_open_count++;
8567c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
8577c478bd9Sstevel@tonic-gate 		}
8587c478bd9Sstevel@tonic-gate 
8597c478bd9Sstevel@tonic-gate 		break;
8607c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_DEV_STAT_NODE:
8617c478bd9Sstevel@tonic-gate 		rval = ugen_ds_open(ugenp, *devp, flag);
8627c478bd9Sstevel@tonic-gate 
8637c478bd9Sstevel@tonic-gate 		break;
8647c478bd9Sstevel@tonic-gate 	default:
8657c478bd9Sstevel@tonic-gate 		rval = EINVAL;
8667c478bd9Sstevel@tonic-gate 
8677c478bd9Sstevel@tonic-gate 		break;
8687c478bd9Sstevel@tonic-gate 	}
8697c478bd9Sstevel@tonic-gate done:
8707c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
8717c478bd9Sstevel@tonic-gate 
8727c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
8737c478bd9Sstevel@tonic-gate 	    "usb_ugen_open: minor=0x%x rval=%d state=%d cnt=%d",
8747c478bd9Sstevel@tonic-gate 	    getminor(*devp), rval, ugenp->ug_dev_state,
8757c478bd9Sstevel@tonic-gate 	    ugenp->ug_open_count);
8767c478bd9Sstevel@tonic-gate 
8777c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
8787c478bd9Sstevel@tonic-gate 
8797c478bd9Sstevel@tonic-gate 	usb_release_access(ugenp->ug_ser_cookie);
8807c478bd9Sstevel@tonic-gate 
8817c478bd9Sstevel@tonic-gate 	return (rval);
8827c478bd9Sstevel@tonic-gate }
8837c478bd9Sstevel@tonic-gate 
8847c478bd9Sstevel@tonic-gate 
8857c478bd9Sstevel@tonic-gate /*
8867c478bd9Sstevel@tonic-gate  * usb_ugen_close()
8877c478bd9Sstevel@tonic-gate  */
8887c478bd9Sstevel@tonic-gate /* ARGSUSED */
8897c478bd9Sstevel@tonic-gate int
8907c478bd9Sstevel@tonic-gate usb_ugen_close(usb_ugen_hdl_t usb_ugen_hdl, dev_t dev, int flag, int otype,
8917c478bd9Sstevel@tonic-gate     cred_t *cr)
8927c478bd9Sstevel@tonic-gate {
8937c478bd9Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
894*0a05e705Slc 	    (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
8957c478bd9Sstevel@tonic-gate 	ugen_state_t		*ugenp;
8967c478bd9Sstevel@tonic-gate 	int			minor_node_type;
8977c478bd9Sstevel@tonic-gate 
8987c478bd9Sstevel@tonic-gate 	if (usb_ugen_hdl == NULL) {
8997c478bd9Sstevel@tonic-gate 
9007c478bd9Sstevel@tonic-gate 		return (EINVAL);
9017c478bd9Sstevel@tonic-gate 	}
9027c478bd9Sstevel@tonic-gate 
9037c478bd9Sstevel@tonic-gate 	ugenp = usb_ugen_hdl_impl->hdl_ugenp;
90496603521Sfrits 	if (ugen_is_valid_minor_node(ugenp, dev) != USB_SUCCESS) {
90596603521Sfrits 
90696603521Sfrits 		return (EINVAL);
90796603521Sfrits 	}
90896603521Sfrits 
9097c478bd9Sstevel@tonic-gate 	minor_node_type = UGEN_MINOR_TYPE(ugenp, dev);
9107c478bd9Sstevel@tonic-gate 
9117c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
9127c478bd9Sstevel@tonic-gate 	    "usb_ugen_close: minor=0x%x", getminor(dev));
9137c478bd9Sstevel@tonic-gate 
9147c478bd9Sstevel@tonic-gate 	/* exclude other threads, including other opens */
9157c478bd9Sstevel@tonic-gate 	if (usb_serialize_access(ugenp->ug_ser_cookie,
9167c478bd9Sstevel@tonic-gate 	    USB_WAIT_SIG, 0) <= 0) {
9177c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
9187c478bd9Sstevel@tonic-gate 		    "usb_ugen_close: interrupted");
9197c478bd9Sstevel@tonic-gate 
9207c478bd9Sstevel@tonic-gate 		return (EINTR);
9217c478bd9Sstevel@tonic-gate 	}
9227c478bd9Sstevel@tonic-gate 
9237c478bd9Sstevel@tonic-gate 	/* close node depending on type */
9247c478bd9Sstevel@tonic-gate 	switch (minor_node_type) {
9257c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_EP_XFER_NODE:
9267c478bd9Sstevel@tonic-gate 		ugen_epx_close(ugenp, dev, flag);
9277c478bd9Sstevel@tonic-gate 		if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
9287c478bd9Sstevel@tonic-gate 			ugen_pm_idle_component(ugenp);
9297c478bd9Sstevel@tonic-gate 		}
9307c478bd9Sstevel@tonic-gate 
9317c478bd9Sstevel@tonic-gate 		break;
9327c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_EP_STAT_NODE:
9337c478bd9Sstevel@tonic-gate 		ugen_eps_close(ugenp, dev, flag);
9347c478bd9Sstevel@tonic-gate 
9357c478bd9Sstevel@tonic-gate 		break;
9367c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_DEV_STAT_NODE:
9377c478bd9Sstevel@tonic-gate 		ugen_ds_close(ugenp, dev, flag);
9387c478bd9Sstevel@tonic-gate 
9397c478bd9Sstevel@tonic-gate 		break;
9407c478bd9Sstevel@tonic-gate 	default:
9417c478bd9Sstevel@tonic-gate 		usb_release_access(ugenp->ug_ser_cookie);
9427c478bd9Sstevel@tonic-gate 
9437c478bd9Sstevel@tonic-gate 		return (EINVAL);
9447c478bd9Sstevel@tonic-gate 	}
9457c478bd9Sstevel@tonic-gate 
9467c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
9477c478bd9Sstevel@tonic-gate 	if (minor_node_type != UGEN_MINOR_DEV_STAT_NODE) {
9487c478bd9Sstevel@tonic-gate 		ASSERT(ugenp->ug_open_count > 0);
9497c478bd9Sstevel@tonic-gate 		if ((--ugenp->ug_open_count == 0) &&
9507c478bd9Sstevel@tonic-gate 		    ((ugenp->ug_dev_state == USB_UGEN_DEV_UNAVAILABLE_RESUME) ||
9517c478bd9Sstevel@tonic-gate 		    (ugenp->ug_dev_state ==
9527c478bd9Sstevel@tonic-gate 		    USB_UGEN_DEV_UNAVAILABLE_RECONNECT))) {
9537c478bd9Sstevel@tonic-gate 			ugenp->ug_dev_state = USB_DEV_ONLINE;
9547c478bd9Sstevel@tonic-gate 
9557c478bd9Sstevel@tonic-gate 			/* wakeup devstat reads and polls */
9567c478bd9Sstevel@tonic-gate 			ugen_ds_change(ugenp);
9577c478bd9Sstevel@tonic-gate 			ugen_ds_poll_wakeup(ugenp);
9587c478bd9Sstevel@tonic-gate 		}
9597c478bd9Sstevel@tonic-gate 	}
9607c478bd9Sstevel@tonic-gate 
9617c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
9627c478bd9Sstevel@tonic-gate 	    "usb_ugen_close: minor=0x%x state=%d cnt=%d",
9637c478bd9Sstevel@tonic-gate 	    getminor(dev), ugenp->ug_dev_state, ugenp->ug_open_count);
9647c478bd9Sstevel@tonic-gate 
9657c478bd9Sstevel@tonic-gate 	if (ugenp->ug_open_count == 0) {
9667c478bd9Sstevel@tonic-gate 		ASSERT(ugen_epxs_check_open_nodes(ugenp) == USB_FAILURE);
9677c478bd9Sstevel@tonic-gate 	}
9687c478bd9Sstevel@tonic-gate 
9697c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
9707c478bd9Sstevel@tonic-gate 
9717c478bd9Sstevel@tonic-gate 	usb_release_access(ugenp->ug_ser_cookie);
9727c478bd9Sstevel@tonic-gate 
9737c478bd9Sstevel@tonic-gate 	return (0);
9747c478bd9Sstevel@tonic-gate }
9757c478bd9Sstevel@tonic-gate 
9767c478bd9Sstevel@tonic-gate 
9777c478bd9Sstevel@tonic-gate /*
9787c478bd9Sstevel@tonic-gate  * usb_ugen_read/write()
9797c478bd9Sstevel@tonic-gate  */
9807c478bd9Sstevel@tonic-gate /*ARGSUSED*/
9817c478bd9Sstevel@tonic-gate int
9827c478bd9Sstevel@tonic-gate usb_ugen_read(usb_ugen_hdl_t usb_ugen_hdl, dev_t dev, struct uio *uiop,
9837c478bd9Sstevel@tonic-gate     cred_t *credp)
9847c478bd9Sstevel@tonic-gate {
98596603521Sfrits 	ugen_state_t		*ugenp;
98696603521Sfrits 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
987*0a05e705Slc 	    (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
98896603521Sfrits 
98996603521Sfrits 	if (usb_ugen_hdl == NULL) {
99096603521Sfrits 
99196603521Sfrits 		return (EINVAL);
99296603521Sfrits 	}
99396603521Sfrits 	ugenp = usb_ugen_hdl_impl->hdl_ugenp;
99496603521Sfrits 
99596603521Sfrits 	if (ugen_is_valid_minor_node(ugenp, dev) != USB_SUCCESS) {
99696603521Sfrits 
99796603521Sfrits 		return (EINVAL);
99896603521Sfrits 	}
99996603521Sfrits 
10007c478bd9Sstevel@tonic-gate 	return (physio(ugen_strategy,
10017c478bd9Sstevel@tonic-gate 	    (struct buf *)0, dev, B_READ, ugen_minphys, uiop));
10027c478bd9Sstevel@tonic-gate }
10037c478bd9Sstevel@tonic-gate 
10047c478bd9Sstevel@tonic-gate 
10057c478bd9Sstevel@tonic-gate /*ARGSUSED*/
10067c478bd9Sstevel@tonic-gate int
10077c478bd9Sstevel@tonic-gate usb_ugen_write(usb_ugen_hdl_t usb_ugen_hdl, dev_t dev, struct uio *uiop,
10087c478bd9Sstevel@tonic-gate     cred_t *credp)
10097c478bd9Sstevel@tonic-gate {
101096603521Sfrits 	ugen_state_t		*ugenp;
101196603521Sfrits 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
1012*0a05e705Slc 	    (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
101396603521Sfrits 
101496603521Sfrits 	if (usb_ugen_hdl == NULL) {
101596603521Sfrits 
101696603521Sfrits 		return (EINVAL);
101796603521Sfrits 	}
101896603521Sfrits 	ugenp = usb_ugen_hdl_impl->hdl_ugenp;
101996603521Sfrits 
102096603521Sfrits 	if (ugen_is_valid_minor_node(ugenp, dev) != USB_SUCCESS) {
102196603521Sfrits 
102296603521Sfrits 		return (EINVAL);
102396603521Sfrits 	}
102496603521Sfrits 
10257c478bd9Sstevel@tonic-gate 	return (physio(ugen_strategy,
10267c478bd9Sstevel@tonic-gate 	    (struct buf *)0, dev, B_WRITE, ugen_minphys, uiop));
10277c478bd9Sstevel@tonic-gate }
10287c478bd9Sstevel@tonic-gate 
10297c478bd9Sstevel@tonic-gate 
10307c478bd9Sstevel@tonic-gate /*
10317c478bd9Sstevel@tonic-gate  * usb_ugen_poll
10327c478bd9Sstevel@tonic-gate  */
10337c478bd9Sstevel@tonic-gate int
10347c478bd9Sstevel@tonic-gate usb_ugen_poll(usb_ugen_hdl_t usb_ugen_hdl, dev_t dev, short events,
10357c478bd9Sstevel@tonic-gate     int anyyet,  short *reventsp, struct pollhead **phpp)
10367c478bd9Sstevel@tonic-gate {
10377c478bd9Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
1038*0a05e705Slc 	    (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
10397c478bd9Sstevel@tonic-gate 	ugen_state_t		*ugenp;
10407c478bd9Sstevel@tonic-gate 	int			minor_node_type;
10417c478bd9Sstevel@tonic-gate 	uint_t			ep_index;
10427c478bd9Sstevel@tonic-gate 	ugen_ep_t		*epp;
10437c478bd9Sstevel@tonic-gate 
10447c478bd9Sstevel@tonic-gate 	if (usb_ugen_hdl == NULL) {
10457c478bd9Sstevel@tonic-gate 
10467c478bd9Sstevel@tonic-gate 		return (EINVAL);
10477c478bd9Sstevel@tonic-gate 	}
10487c478bd9Sstevel@tonic-gate 
10497c478bd9Sstevel@tonic-gate 	ugenp = usb_ugen_hdl_impl->hdl_ugenp;
105096603521Sfrits 	if (ugen_is_valid_minor_node(ugenp, dev) != USB_SUCCESS) {
105196603521Sfrits 
105296603521Sfrits 		return (EINVAL);
105396603521Sfrits 	}
10547c478bd9Sstevel@tonic-gate 
10557c478bd9Sstevel@tonic-gate 	minor_node_type = UGEN_MINOR_TYPE(ugenp, dev);
10567c478bd9Sstevel@tonic-gate 	ep_index	= UGEN_MINOR_EPIDX(ugenp, dev);
10577c478bd9Sstevel@tonic-gate 	epp		= &ugenp->ug_ep[ep_index];
10587c478bd9Sstevel@tonic-gate 
10597c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
10607c478bd9Sstevel@tonic-gate 
10617c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_POLL, ugenp->ug_log_hdl,
10627c478bd9Sstevel@tonic-gate 	    "usb_ugen_poll: "
10637c478bd9Sstevel@tonic-gate 	    "dev=0x%lx events=0x%x anyyet=0x%x rev=0x%p type=%d "
10647c478bd9Sstevel@tonic-gate 	    "devstat=0x%x devstate=0x%x",
10657c478bd9Sstevel@tonic-gate 	    dev, events, anyyet, (void *)reventsp, minor_node_type,
10667c478bd9Sstevel@tonic-gate 	    ugenp->ug_ds.dev_stat, ugenp->ug_ds.dev_state);
10677c478bd9Sstevel@tonic-gate 
10687c478bd9Sstevel@tonic-gate 	*reventsp = 0;
10697c478bd9Sstevel@tonic-gate 
10707c478bd9Sstevel@tonic-gate 	if (ugenp->ug_dev_state == USB_DEV_ONLINE) {
10717c478bd9Sstevel@tonic-gate 		switch (minor_node_type) {
10727c478bd9Sstevel@tonic-gate 		case UGEN_MINOR_EP_XFER_NODE:
10737c478bd9Sstevel@tonic-gate 			/* if interrupt IN ep and there is data, set POLLIN */
10747c478bd9Sstevel@tonic-gate 			if ((UGEN_XFER_TYPE(epp) == USB_EP_ATTR_INTR) &&
10757c478bd9Sstevel@tonic-gate 			    (UGEN_XFER_DIR(epp) & USB_EP_DIR_IN)) {
10767c478bd9Sstevel@tonic-gate 
10777c478bd9Sstevel@tonic-gate 				/*
10787c478bd9Sstevel@tonic-gate 				 * if we are not polling, force another
10797c478bd9Sstevel@tonic-gate 				 * read to kick off polling
10807c478bd9Sstevel@tonic-gate 				 */
10817c478bd9Sstevel@tonic-gate 				mutex_enter(&epp->ep_mutex);
10827c478bd9Sstevel@tonic-gate 				if ((epp->ep_data) ||
10837c478bd9Sstevel@tonic-gate 				    ((epp->ep_state &
10847c478bd9Sstevel@tonic-gate 				    UGEN_EP_STATE_INTR_IN_POLLING_ON) == 0)) {
10857c478bd9Sstevel@tonic-gate 					*reventsp |= POLLIN;
10867c478bd9Sstevel@tonic-gate 				} else if (!anyyet) {
10877c478bd9Sstevel@tonic-gate 					*phpp = &epp->ep_pollhead;
10887c478bd9Sstevel@tonic-gate 					epp->ep_state |=
10897c478bd9Sstevel@tonic-gate 					    UGEN_EP_STATE_INTR_IN_POLL_PENDING;
10907c478bd9Sstevel@tonic-gate 				}
10917c478bd9Sstevel@tonic-gate 				mutex_exit(&epp->ep_mutex);
1092*0a05e705Slc 
1093*0a05e705Slc 			} else if ((UGEN_XFER_TYPE(epp) == USB_EP_ATTR_ISOCH) &&
1094*0a05e705Slc 			    (UGEN_XFER_DIR(epp) & USB_EP_DIR_IN)) {
1095*0a05e705Slc 
1096*0a05e705Slc 				/*
1097*0a05e705Slc 				 * if we are not polling, force another
1098*0a05e705Slc 				 * read to kick off polling
1099*0a05e705Slc 				 */
1100*0a05e705Slc 				mutex_enter(&epp->ep_mutex);
1101*0a05e705Slc 				if ((epp->ep_data) ||
1102*0a05e705Slc 				    ((epp->ep_state &
1103*0a05e705Slc 				    UGEN_EP_STATE_ISOC_IN_POLLING_ON) == 0)) {
1104*0a05e705Slc 					*reventsp |= POLLIN;
1105*0a05e705Slc 				} else if (!anyyet) {
1106*0a05e705Slc 					*phpp = &epp->ep_pollhead;
1107*0a05e705Slc 					epp->ep_state |=
1108*0a05e705Slc 					    UGEN_EP_STATE_ISOC_IN_POLL_PENDING;
1109*0a05e705Slc 				}
1110*0a05e705Slc 				mutex_exit(&epp->ep_mutex);
1111*0a05e705Slc 
11127c478bd9Sstevel@tonic-gate 			} else {
11137c478bd9Sstevel@tonic-gate 				/* no poll on other ep nodes */
11147c478bd9Sstevel@tonic-gate 				*reventsp |= POLLERR;
11157c478bd9Sstevel@tonic-gate 			}
11167c478bd9Sstevel@tonic-gate 
11177c478bd9Sstevel@tonic-gate 			break;
11187c478bd9Sstevel@tonic-gate 		case UGEN_MINOR_DEV_STAT_NODE:
11197c478bd9Sstevel@tonic-gate 			if (ugenp->ug_ds.dev_stat & UGEN_DEV_STATUS_CHANGED) {
11207c478bd9Sstevel@tonic-gate 				*reventsp |= POLLIN;
11217c478bd9Sstevel@tonic-gate 			} else if (!anyyet) {
11227c478bd9Sstevel@tonic-gate 				*phpp = &ugenp->ug_ds.dev_pollhead;
11237c478bd9Sstevel@tonic-gate 				ugenp->ug_ds.dev_stat |=
11247c478bd9Sstevel@tonic-gate 				    UGEN_DEV_STATUS_POLL_PENDING;
11257c478bd9Sstevel@tonic-gate 			}
11267c478bd9Sstevel@tonic-gate 
11277c478bd9Sstevel@tonic-gate 			break;
11287c478bd9Sstevel@tonic-gate 		case UGEN_MINOR_EP_STAT_NODE:
11297c478bd9Sstevel@tonic-gate 		default:
11307c478bd9Sstevel@tonic-gate 			*reventsp |= POLLERR;
11317c478bd9Sstevel@tonic-gate 
11327c478bd9Sstevel@tonic-gate 			break;
11337c478bd9Sstevel@tonic-gate 		}
11347c478bd9Sstevel@tonic-gate 	} else {
11357c478bd9Sstevel@tonic-gate 		if (ugenp->ug_ds.dev_stat & UGEN_DEV_STATUS_CHANGED) {
11367c478bd9Sstevel@tonic-gate 			*reventsp |= POLLHUP|POLLIN;
11377c478bd9Sstevel@tonic-gate 		} else if (!anyyet) {
11387c478bd9Sstevel@tonic-gate 			*phpp = &ugenp->ug_ds.dev_pollhead;
11397c478bd9Sstevel@tonic-gate 			ugenp->ug_ds.dev_stat |=
1140*0a05e705Slc 			    UGEN_DEV_STATUS_POLL_PENDING;
11417c478bd9Sstevel@tonic-gate 		}
11427c478bd9Sstevel@tonic-gate 	}
11437c478bd9Sstevel@tonic-gate 
11447c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
11457c478bd9Sstevel@tonic-gate 
11467c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_POLL, ugenp->ug_log_hdl,
11477c478bd9Sstevel@tonic-gate 	    "usb_ugen_poll end: reventsp=0x%x", *reventsp);
11487c478bd9Sstevel@tonic-gate 
11497c478bd9Sstevel@tonic-gate 	return (0);
11507c478bd9Sstevel@tonic-gate }
11517c478bd9Sstevel@tonic-gate 
11527c478bd9Sstevel@tonic-gate 
11537c478bd9Sstevel@tonic-gate /*
11547c478bd9Sstevel@tonic-gate  * ugen_strategy
11557c478bd9Sstevel@tonic-gate  */
11567c478bd9Sstevel@tonic-gate static int
11577c478bd9Sstevel@tonic-gate ugen_strategy(struct buf *bp)
11587c478bd9Sstevel@tonic-gate {
11597c478bd9Sstevel@tonic-gate 	dev_t		dev = bp->b_edev;
11607c478bd9Sstevel@tonic-gate 	int		rval = 0;
11617c478bd9Sstevel@tonic-gate 	ugen_state_t	*ugenp = ugen_devt2state(dev);
11627c478bd9Sstevel@tonic-gate 	int		minor_node_type = UGEN_MINOR_TYPE(ugenp, dev);
11637c478bd9Sstevel@tonic-gate 
11647c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
11657c478bd9Sstevel@tonic-gate 	    "ugen_strategy: bp=0x%p minor=0x%x", bp, getminor(dev));
11667c478bd9Sstevel@tonic-gate 
116796603521Sfrits 	if (ugen_is_valid_minor_node(ugenp, dev) != USB_SUCCESS) {
116896603521Sfrits 
116996603521Sfrits 		return (EINVAL);
117096603521Sfrits 	}
117196603521Sfrits 
11727c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
11737c478bd9Sstevel@tonic-gate 	ugenp->ug_pending_cmds++;
11747c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
11757c478bd9Sstevel@tonic-gate 
11767c478bd9Sstevel@tonic-gate 	bp_mapin(bp);
11777c478bd9Sstevel@tonic-gate 
11787c478bd9Sstevel@tonic-gate 	switch (minor_node_type) {
11797c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_EP_XFER_NODE:
11807c478bd9Sstevel@tonic-gate 		rval = ugen_epx_req(ugenp, bp);
11817c478bd9Sstevel@tonic-gate 
11827c478bd9Sstevel@tonic-gate 		break;
11837c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_EP_STAT_NODE:
11847c478bd9Sstevel@tonic-gate 		rval = ugen_eps_req(ugenp, bp);
11857c478bd9Sstevel@tonic-gate 
11867c478bd9Sstevel@tonic-gate 		break;
11877c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_DEV_STAT_NODE:
11887c478bd9Sstevel@tonic-gate 		rval = ugen_ds_req(ugenp, bp);
11897c478bd9Sstevel@tonic-gate 
11907c478bd9Sstevel@tonic-gate 		break;
11917c478bd9Sstevel@tonic-gate 	default:
11927c478bd9Sstevel@tonic-gate 		rval = EINVAL;
11937c478bd9Sstevel@tonic-gate 
11947c478bd9Sstevel@tonic-gate 		break;
11957c478bd9Sstevel@tonic-gate 	}
11967c478bd9Sstevel@tonic-gate 
11977c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
11987c478bd9Sstevel@tonic-gate 	ugenp->ug_pending_cmds--;
11997c478bd9Sstevel@tonic-gate 
12007c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
12017c478bd9Sstevel@tonic-gate 	    "ugen_strategy: "
12027c478bd9Sstevel@tonic-gate 	    "bp=0x%p cnt=%lu resid=%lu err=%d minor=0x%x rval=%d #cmds=%d",
12037c478bd9Sstevel@tonic-gate 	    (void *)bp, bp->b_bcount, bp->b_resid, geterror(bp),
12047c478bd9Sstevel@tonic-gate 	    getminor(dev), rval, ugenp->ug_pending_cmds);
12057c478bd9Sstevel@tonic-gate 
12067c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
12077c478bd9Sstevel@tonic-gate 
12087c478bd9Sstevel@tonic-gate 	if (rval) {
12097c478bd9Sstevel@tonic-gate 		if (geterror(bp) == 0) {
12107c478bd9Sstevel@tonic-gate 			bioerror(bp, rval);
12117c478bd9Sstevel@tonic-gate 		}
12127c478bd9Sstevel@tonic-gate 	}
12137c478bd9Sstevel@tonic-gate 
12147c478bd9Sstevel@tonic-gate 	biodone(bp);
12157c478bd9Sstevel@tonic-gate 
12167c478bd9Sstevel@tonic-gate 	return (0);
12177c478bd9Sstevel@tonic-gate }
12187c478bd9Sstevel@tonic-gate 
12197c478bd9Sstevel@tonic-gate 
12207c478bd9Sstevel@tonic-gate /*
12217c478bd9Sstevel@tonic-gate  * ugen_minphys:
12227c478bd9Sstevel@tonic-gate  */
12237c478bd9Sstevel@tonic-gate static void
12247c478bd9Sstevel@tonic-gate ugen_minphys(struct buf *bp)
12257c478bd9Sstevel@tonic-gate {
12267c478bd9Sstevel@tonic-gate 	dev_t		dev = bp->b_edev;
12277c478bd9Sstevel@tonic-gate 	ugen_state_t	*ugenp = ugen_devt2state(dev);
12287c478bd9Sstevel@tonic-gate 	int		minor_node_type = UGEN_MINOR_TYPE(ugenp, dev);
12297c478bd9Sstevel@tonic-gate 	uint_t		ep_index = UGEN_MINOR_EPIDX(ugenp, dev);
12307c478bd9Sstevel@tonic-gate 	ugen_ep_t	*epp = &ugenp->ug_ep[ep_index];
12317c478bd9Sstevel@tonic-gate 
12327c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
12337c478bd9Sstevel@tonic-gate 	    "ugen_phys: bp=0x%p dev=0x%lx index=%d type=0x%x",
12347c478bd9Sstevel@tonic-gate 	    (void *)bp, dev, ep_index, minor_node_type);
12357c478bd9Sstevel@tonic-gate 
12367c478bd9Sstevel@tonic-gate 	switch (minor_node_type) {
12377c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_EP_XFER_NODE:
12387c478bd9Sstevel@tonic-gate 		switch (UGEN_XFER_TYPE(epp)) {
12397c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_BULK:
12407c478bd9Sstevel@tonic-gate 			if (bp->b_bcount > ugenp->ug_max_bulk_xfer_sz) {
12417c478bd9Sstevel@tonic-gate 				bp->b_bcount = ugenp->ug_max_bulk_xfer_sz;
12427c478bd9Sstevel@tonic-gate 			}
12437c478bd9Sstevel@tonic-gate 
12447c478bd9Sstevel@tonic-gate 			break;
12457c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_INTR:
12467c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_CONTROL:
1247*0a05e705Slc 		case USB_EP_ATTR_ISOCH:
12487c478bd9Sstevel@tonic-gate 		default:
12497c478bd9Sstevel@tonic-gate 
12507c478bd9Sstevel@tonic-gate 			break;
12517c478bd9Sstevel@tonic-gate 		}
12527c478bd9Sstevel@tonic-gate 		break;
12537c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_EP_STAT_NODE:
12547c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_DEV_STAT_NODE:
12557c478bd9Sstevel@tonic-gate 	default:
12567c478bd9Sstevel@tonic-gate 
12577c478bd9Sstevel@tonic-gate 		break;
12587c478bd9Sstevel@tonic-gate 	}
12597c478bd9Sstevel@tonic-gate }
12607c478bd9Sstevel@tonic-gate 
1261*0a05e705Slc /*
1262*0a05e705Slc  * Get bmAttributes and bAddress of the endpoint which is going to
1263*0a05e705Slc  * be opened
1264*0a05e705Slc  */
1265*0a05e705Slc static int
1266*0a05e705Slc ugen_get_ep_descr(ugen_state_t *ugenp, dev_t dev, uint8_t *bmAttr,
1267*0a05e705Slc     uint8_t *bAddr)
1268*0a05e705Slc {
1269*0a05e705Slc 	uint_t	alt = UGEN_MINOR_ALT(ugenp, dev);
1270*0a05e705Slc 	uint_t	ifc = UGEN_MINOR_IF(ugenp, dev);
1271*0a05e705Slc 	uint_t	cfgidx = UGEN_MINOR_CFGIDX(ugenp, dev);
1272*0a05e705Slc 	usb_cfg_data_t	*dev_cfg;
1273*0a05e705Slc 	usb_if_data_t	*if_data;
1274*0a05e705Slc 	usb_alt_if_data_t *alt_if_data;
1275*0a05e705Slc 	usb_ep_data_t	*ep_data;
1276*0a05e705Slc 	int ep;
1277*0a05e705Slc 	int epidx = UGEN_MINOR_EPIDX(ugenp, dev);
1278*0a05e705Slc 
1279*0a05e705Slc 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1280*0a05e705Slc 	    "cfg=%d, if=%d, alt=%d, ep=0x%x", cfgidx, ifc,
1281*0a05e705Slc 	    alt, epidx);
1282*0a05e705Slc 
1283*0a05e705Slc 	dev_cfg = &ugenp->ug_dev_data->dev_cfg[cfgidx];
1284*0a05e705Slc 	if_data = &dev_cfg->cfg_if[ifc];
1285*0a05e705Slc 	alt_if_data = &if_data->if_alt[alt];
1286*0a05e705Slc 	for (ep = 0; ep < alt_if_data->altif_n_ep; ep++) {
1287*0a05e705Slc 		ep_data = &alt_if_data->altif_ep[ep];
1288*0a05e705Slc 
1289*0a05e705Slc 		if (usb_get_ep_index(ep_data->ep_descr.
1290*0a05e705Slc 		    bEndpointAddress) == epidx) {
1291*0a05e705Slc 
1292*0a05e705Slc 			*bmAttr = ep_data->ep_descr.bmAttributes;
1293*0a05e705Slc 			*bAddr = ep_data->ep_descr.bEndpointAddress;
1294*0a05e705Slc 
1295*0a05e705Slc 			return (USB_SUCCESS);
1296*0a05e705Slc 		}
1297*0a05e705Slc 	}
1298*0a05e705Slc 
1299*0a05e705Slc 	return (USB_FAILURE);
1300*0a05e705Slc }
13017c478bd9Sstevel@tonic-gate 
13027c478bd9Sstevel@tonic-gate /*
13037c478bd9Sstevel@tonic-gate  * check whether flag is appropriate for node type
13047c478bd9Sstevel@tonic-gate  */
13057c478bd9Sstevel@tonic-gate static int
13067c478bd9Sstevel@tonic-gate ugen_check_open_flags(ugen_state_t *ugenp, dev_t dev, int flag)
13077c478bd9Sstevel@tonic-gate {
13087c478bd9Sstevel@tonic-gate 	ugen_ep_t *epp;
13097c478bd9Sstevel@tonic-gate 	int	minor_node_type = UGEN_MINOR_TYPE(ugenp, dev);
13107c478bd9Sstevel@tonic-gate 	int	rval = 0;
1311*0a05e705Slc 	uint8_t bmAttribute;
1312*0a05e705Slc 	uint8_t bAddress;
13137c478bd9Sstevel@tonic-gate 
13147c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
13157c478bd9Sstevel@tonic-gate 	    "ugen_check_open_flags: "
13167c478bd9Sstevel@tonic-gate 	    "dev=0x%lx, type=0x%x flag=0x%x idx=%" PRIu64,
13177c478bd9Sstevel@tonic-gate 	    dev, minor_node_type, flag, UGEN_MINOR_EPIDX(ugenp, dev));
13187c478bd9Sstevel@tonic-gate 
13197c478bd9Sstevel@tonic-gate 	switch (minor_node_type) {
13207c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_EP_XFER_NODE:
13217c478bd9Sstevel@tonic-gate 		epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
1322*0a05e705Slc 
1323*0a05e705Slc 		/*
1324*0a05e705Slc 		 * Endpoints in two altsetting happen to have the same
1325*0a05e705Slc 		 * bEndpointAddress, but they are different type, e.g,
1326*0a05e705Slc 		 * one is BULK and the other is ISOC. They use the same
1327*0a05e705Slc 		 * slot of ug_ep array. It's OK after switch_alt, because
1328*0a05e705Slc 		 * after alt switch, ep info is updated to the new endpoint.
1329*0a05e705Slc 		 * But it's not right here to use the other EP's info for
1330*0a05e705Slc 		 * checking.
1331*0a05e705Slc 		 */
1332*0a05e705Slc 		if (UGEN_MINOR_EPIDX(ugenp, dev) != 0) {
1333*0a05e705Slc 			if ((rval = ugen_get_ep_descr(ugenp, dev, &bmAttribute,
1334*0a05e705Slc 			    &bAddress)) != USB_SUCCESS) {
1335*0a05e705Slc 				USB_DPRINTF_L2(UGEN_PRINT_XFER,
1336*0a05e705Slc 				    ugenp->ug_log_hdl, "ugen_get_descr: fail");
1337*0a05e705Slc 
1338*0a05e705Slc 				return (ENODEV);
1339*0a05e705Slc 			}
1340*0a05e705Slc 		} else {
1341*0a05e705Slc 			bmAttribute = ugen_default_ep_descr.bmAttributes;
1342*0a05e705Slc 			bAddress = ugen_default_ep_descr.bEndpointAddress;
1343*0a05e705Slc 		}
1344*0a05e705Slc 
1345*0a05e705Slc 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1346*0a05e705Slc 		    "ugen_check_open_flags: epp = %p,"
1347*0a05e705Slc 		    "epp type = %d, bmAttr =0x%x, bAddr = 0x%02x", epp,
1348*0a05e705Slc 		    UGEN_XFER_TYPE(epp), bmAttribute, bAddress);
1349*0a05e705Slc 
1350*0a05e705Slc 		switch (bmAttribute & USB_EP_ATTR_MASK) {
13517c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_CONTROL:
13527c478bd9Sstevel@tonic-gate 			/* read and write must be set, ndelay not allowed */
13537c478bd9Sstevel@tonic-gate 			if (((flag & (FREAD | FWRITE)) != (FREAD | FWRITE)) ||
13547c478bd9Sstevel@tonic-gate 			    (flag & (FNDELAY | FNONBLOCK))) {
13557c478bd9Sstevel@tonic-gate 				rval = EACCES;
13567c478bd9Sstevel@tonic-gate 			}
13577c478bd9Sstevel@tonic-gate 
1358*0a05e705Slc 			break;
1359*0a05e705Slc 		case USB_EP_ATTR_ISOCH:
1360*0a05e705Slc 			/* read and write must be set */
1361*0a05e705Slc 			if ((flag & (FREAD | FWRITE)) != (FREAD | FWRITE)) {
1362*0a05e705Slc 				rval = EACCES;
1363*0a05e705Slc 			}
1364*0a05e705Slc 
13657c478bd9Sstevel@tonic-gate 			break;
13667c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_BULK:
13677c478bd9Sstevel@tonic-gate 			/* ndelay not allowed */
13687c478bd9Sstevel@tonic-gate 			if (flag & (FNDELAY | FNONBLOCK)) {
13697c478bd9Sstevel@tonic-gate 				rval = EACCES;
13707c478bd9Sstevel@tonic-gate 
13717c478bd9Sstevel@tonic-gate 				break;
13727c478bd9Sstevel@tonic-gate 			}
13737c478bd9Sstevel@tonic-gate 			/*FALLTHRU*/
13747c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_INTR:
13757c478bd9Sstevel@tonic-gate 			/* check flag versus direction */
1376*0a05e705Slc 			if ((flag & FWRITE) && (bAddress & USB_EP_DIR_IN)) {
13777c478bd9Sstevel@tonic-gate 				rval = EACCES;
13787c478bd9Sstevel@tonic-gate 			}
13797c478bd9Sstevel@tonic-gate 			if ((flag & FREAD) &&
1380*0a05e705Slc 			    ((bAddress & USB_EP_DIR_IN) == 0)) {
13817c478bd9Sstevel@tonic-gate 				rval = EACCES;
13827c478bd9Sstevel@tonic-gate 			}
13837c478bd9Sstevel@tonic-gate 
13847c478bd9Sstevel@tonic-gate 			break;
13857c478bd9Sstevel@tonic-gate 		default:
13867c478bd9Sstevel@tonic-gate 			rval = EINVAL;
13877c478bd9Sstevel@tonic-gate 
13887c478bd9Sstevel@tonic-gate 			break;
13897c478bd9Sstevel@tonic-gate 		}
13907c478bd9Sstevel@tonic-gate 		break;
13917c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_DEV_STAT_NODE:
13927c478bd9Sstevel@tonic-gate 		/* only reads are supported */
13937c478bd9Sstevel@tonic-gate 		if (flag & FWRITE) {
13947c478bd9Sstevel@tonic-gate 			rval = EACCES;
13957c478bd9Sstevel@tonic-gate 		}
13967c478bd9Sstevel@tonic-gate 
13977c478bd9Sstevel@tonic-gate 		break;
13987c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_EP_STAT_NODE:
13997c478bd9Sstevel@tonic-gate 
14007c478bd9Sstevel@tonic-gate 		break;
14017c478bd9Sstevel@tonic-gate 	default:
14027c478bd9Sstevel@tonic-gate 		rval = EINVAL;
14037c478bd9Sstevel@tonic-gate 
14047c478bd9Sstevel@tonic-gate 		break;
14057c478bd9Sstevel@tonic-gate 	}
14067c478bd9Sstevel@tonic-gate 
14077c478bd9Sstevel@tonic-gate 	return (rval);
14087c478bd9Sstevel@tonic-gate }
14097c478bd9Sstevel@tonic-gate 
14107c478bd9Sstevel@tonic-gate 
14117c478bd9Sstevel@tonic-gate /*
14127c478bd9Sstevel@tonic-gate  * endpoint management
14137c478bd9Sstevel@tonic-gate  *
14147c478bd9Sstevel@tonic-gate  * create/initialize all endpoint xfer/stat structures
14157c478bd9Sstevel@tonic-gate  */
14167c478bd9Sstevel@tonic-gate static int
14177c478bd9Sstevel@tonic-gate ugen_epxs_init(ugen_state_t *ugenp)
14187c478bd9Sstevel@tonic-gate {
14197c478bd9Sstevel@tonic-gate 	usb_cfg_data_t	*dev_cfg = ugenp->ug_dev_data->dev_cfg;
14207c478bd9Sstevel@tonic-gate 	uchar_t		cfgidx, cfgval, iface, alt, ep;
14217c478bd9Sstevel@tonic-gate 	usb_if_data_t	*if_data;
14227c478bd9Sstevel@tonic-gate 	usb_alt_if_data_t *alt_if_data;
14237c478bd9Sstevel@tonic-gate 	usb_ep_data_t	*ep_data;
14247c478bd9Sstevel@tonic-gate 
14257c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
14267c478bd9Sstevel@tonic-gate 	    "ugen_epxs_init:");
14277c478bd9Sstevel@tonic-gate 
14287c478bd9Sstevel@tonic-gate 	/* initialize each ep's mutex first */
14297c478bd9Sstevel@tonic-gate 	for (ep = 0; ep < UGEN_N_ENDPOINTS; ep++) {
14307c478bd9Sstevel@tonic-gate 		mutex_init(&ugenp->ug_ep[ep].ep_mutex, NULL, MUTEX_DRIVER,
14317c478bd9Sstevel@tonic-gate 		    ugenp->ug_dev_data->dev_iblock_cookie);
14327c478bd9Sstevel@tonic-gate 	}
14337c478bd9Sstevel@tonic-gate 
14347c478bd9Sstevel@tonic-gate 	/* init default ep as it does not have a descriptor */
14357c478bd9Sstevel@tonic-gate 	if (ugen_epxs_data_init(ugenp, NULL, 0, 0,
14367c478bd9Sstevel@tonic-gate 	    ugenp->ug_dev_data->dev_curr_if, 0) != USB_SUCCESS) {
14377c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
14387c478bd9Sstevel@tonic-gate 		    "creating default endpoint failed");
14397c478bd9Sstevel@tonic-gate 
14407c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
14417c478bd9Sstevel@tonic-gate 	}
14427c478bd9Sstevel@tonic-gate 
14437c478bd9Sstevel@tonic-gate 	/*
14447c478bd9Sstevel@tonic-gate 	 * walk all endpoints of all alternates of all interfaces of
14457c478bd9Sstevel@tonic-gate 	 * all cfs
14467c478bd9Sstevel@tonic-gate 	 */
14477c478bd9Sstevel@tonic-gate 	for (cfgidx = 0; cfgidx < ugenp->ug_dev_data->dev_n_cfg; cfgidx++) {
14487c478bd9Sstevel@tonic-gate 		dev_cfg = &ugenp->ug_dev_data->dev_cfg[cfgidx];
14497c478bd9Sstevel@tonic-gate 		cfgval = dev_cfg->cfg_descr.bConfigurationValue;
14507c478bd9Sstevel@tonic-gate 		for (iface = 0; iface < dev_cfg->cfg_n_if; iface++) {
14517c478bd9Sstevel@tonic-gate 			if_data = &dev_cfg->cfg_if[iface];
14527c478bd9Sstevel@tonic-gate 			for (alt = 0; alt < if_data->if_n_alt; alt++) {
14537c478bd9Sstevel@tonic-gate 				alt_if_data = &if_data->if_alt[alt];
14547c478bd9Sstevel@tonic-gate 				for (ep = 0; ep < alt_if_data->altif_n_ep;
14557c478bd9Sstevel@tonic-gate 				    ep++) {
14567c478bd9Sstevel@tonic-gate 					ep_data = &alt_if_data->altif_ep[ep];
14577c478bd9Sstevel@tonic-gate 					if (ugen_epxs_data_init(ugenp, ep_data,
14587c478bd9Sstevel@tonic-gate 					    cfgval, cfgidx, iface, alt) !=
14597c478bd9Sstevel@tonic-gate 					    USB_SUCCESS) {
14607c478bd9Sstevel@tonic-gate 
14617c478bd9Sstevel@tonic-gate 						return (USB_FAILURE);
14627c478bd9Sstevel@tonic-gate 					}
14637c478bd9Sstevel@tonic-gate 				}
14647c478bd9Sstevel@tonic-gate 			}
14657c478bd9Sstevel@tonic-gate 		}
14667c478bd9Sstevel@tonic-gate 	}
14677c478bd9Sstevel@tonic-gate 
14687c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
14697c478bd9Sstevel@tonic-gate }
14707c478bd9Sstevel@tonic-gate 
14717c478bd9Sstevel@tonic-gate 
14727c478bd9Sstevel@tonic-gate /*
14737c478bd9Sstevel@tonic-gate  * initialize one endpoint structure
14747c478bd9Sstevel@tonic-gate  */
14757c478bd9Sstevel@tonic-gate static int
14767c478bd9Sstevel@tonic-gate ugen_epxs_data_init(ugen_state_t *ugenp, usb_ep_data_t *ep_data,
14777c478bd9Sstevel@tonic-gate 	uchar_t cfgval, uchar_t cfgidx, uchar_t iface, uchar_t alt)
14787c478bd9Sstevel@tonic-gate {
14797c478bd9Sstevel@tonic-gate 	int			ep_index;
14807c478bd9Sstevel@tonic-gate 	ugen_ep_t		*epp;
14817c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*ep_descr;
14827c478bd9Sstevel@tonic-gate 
14837c478bd9Sstevel@tonic-gate 	/* is this the default endpoint */
14847c478bd9Sstevel@tonic-gate 	ep_index = (ep_data == NULL) ? 0 :
1485*0a05e705Slc 	    usb_get_ep_index(ep_data->ep_descr.bEndpointAddress);
14867c478bd9Sstevel@tonic-gate 	epp = &ugenp->ug_ep[ep_index];
14877c478bd9Sstevel@tonic-gate 
14887c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
14897c478bd9Sstevel@tonic-gate 	    "ugen_epxs_data_init: "
14907c478bd9Sstevel@tonic-gate 	    "cfgval=%d cfgidx=%d iface=%d alt=%d ep_index=%d",
14917c478bd9Sstevel@tonic-gate 	    cfgval, cfgidx, iface, alt, ep_index);
14927c478bd9Sstevel@tonic-gate 
14937c478bd9Sstevel@tonic-gate 	ep_descr = (ep_data == NULL) ? &ugen_default_ep_descr :
1494*0a05e705Slc 	    &ep_data->ep_descr;
14957c478bd9Sstevel@tonic-gate 
14967c478bd9Sstevel@tonic-gate 	mutex_init(&epp->ep_mutex, NULL, MUTEX_DRIVER,
1497*0a05e705Slc 	    ugenp->ug_dev_data->dev_iblock_cookie);
14987c478bd9Sstevel@tonic-gate 
14997c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
15007c478bd9Sstevel@tonic-gate 
15017c478bd9Sstevel@tonic-gate 	/* initialize if not yet init'ed */
15027c478bd9Sstevel@tonic-gate 	if (epp->ep_state == UGEN_EP_STATE_NONE) {
15037c478bd9Sstevel@tonic-gate 		epp->ep_descr		= *ep_descr;
15047c478bd9Sstevel@tonic-gate 		epp->ep_cfgidx		= cfgidx;
15057c478bd9Sstevel@tonic-gate 		epp->ep_if		= iface;
15067c478bd9Sstevel@tonic-gate 		epp->ep_alt		= alt;
15077c478bd9Sstevel@tonic-gate 		epp->ep_state		= UGEN_EP_STATE_ACTIVE;
15087c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status	= USB_LC_STAT_NOERROR;
15097c478bd9Sstevel@tonic-gate 		epp->ep_pipe_policy.pp_max_async_reqs = 1;
15107c478bd9Sstevel@tonic-gate 
15117c478bd9Sstevel@tonic-gate 		cv_init(&epp->ep_wait_cv, NULL, CV_DRIVER, NULL);
15127c478bd9Sstevel@tonic-gate 		epp->ep_ser_cookie	= usb_init_serialization(
1513*0a05e705Slc 		    ugenp->ug_dip, 0);
15147c478bd9Sstevel@tonic-gate 	}
15157c478bd9Sstevel@tonic-gate 
15167c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
15177c478bd9Sstevel@tonic-gate 
15187c478bd9Sstevel@tonic-gate 	/* create minor nodes for all alts */
15197c478bd9Sstevel@tonic-gate 
15207c478bd9Sstevel@tonic-gate 	return (ugen_epxs_minor_nodes_create(ugenp, ep_descr,
15217c478bd9Sstevel@tonic-gate 	    cfgval, cfgidx, iface, alt));
15227c478bd9Sstevel@tonic-gate }
15237c478bd9Sstevel@tonic-gate 
15247c478bd9Sstevel@tonic-gate 
15257c478bd9Sstevel@tonic-gate /*
15267c478bd9Sstevel@tonic-gate  * undo all endpoint initializations
15277c478bd9Sstevel@tonic-gate  */
15287c478bd9Sstevel@tonic-gate static void
15297c478bd9Sstevel@tonic-gate ugen_epxs_destroy(ugen_state_t *ugenp)
15307c478bd9Sstevel@tonic-gate {
15317c478bd9Sstevel@tonic-gate 	int	i;
15327c478bd9Sstevel@tonic-gate 
15337c478bd9Sstevel@tonic-gate 	for (i = 0; i < UGEN_N_ENDPOINTS; i++) {
15347c478bd9Sstevel@tonic-gate 		ugen_epxs_data_destroy(ugenp, &ugenp->ug_ep[i]);
15357c478bd9Sstevel@tonic-gate 	}
15367c478bd9Sstevel@tonic-gate }
15377c478bd9Sstevel@tonic-gate 
15387c478bd9Sstevel@tonic-gate 
15397c478bd9Sstevel@tonic-gate static void
15407c478bd9Sstevel@tonic-gate ugen_epxs_data_destroy(ugen_state_t *ugenp, ugen_ep_t *epp)
15417c478bd9Sstevel@tonic-gate {
15427c478bd9Sstevel@tonic-gate 	if (epp) {
15437c478bd9Sstevel@tonic-gate 		ASSERT(epp->ep_ph == NULL);
15447c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
15457c478bd9Sstevel@tonic-gate 		if (epp->ep_state != UGEN_EP_STATE_NONE) {
15467c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
15477c478bd9Sstevel@tonic-gate 			    "ugen_epxs_destroy: addr=0x%x",
15487c478bd9Sstevel@tonic-gate 			    UGEN_XFER_ADDR(epp));
15497c478bd9Sstevel@tonic-gate 			cv_destroy(&epp->ep_wait_cv);
15507c478bd9Sstevel@tonic-gate 		}
15517c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
15527c478bd9Sstevel@tonic-gate 
15537c478bd9Sstevel@tonic-gate 		mutex_destroy(&epp->ep_mutex);
15547c478bd9Sstevel@tonic-gate 		usb_fini_serialization(epp->ep_ser_cookie);
15557c478bd9Sstevel@tonic-gate 	}
15567c478bd9Sstevel@tonic-gate }
15577c478bd9Sstevel@tonic-gate 
15587c478bd9Sstevel@tonic-gate 
15597c478bd9Sstevel@tonic-gate /*
15607c478bd9Sstevel@tonic-gate  * create endpoint status and xfer minor nodes
15617c478bd9Sstevel@tonic-gate  *
15627c478bd9Sstevel@tonic-gate  * The actual minor node needs more than 18 bits. We create a table
15637c478bd9Sstevel@tonic-gate  * and store the full minor node in this table and use the
15647c478bd9Sstevel@tonic-gate  * index in the table as minor node. This allows 256 minor nodes
15657c478bd9Sstevel@tonic-gate  * and 1024 instances
15667c478bd9Sstevel@tonic-gate  */
15677c478bd9Sstevel@tonic-gate static int
15687c478bd9Sstevel@tonic-gate ugen_epxs_minor_nodes_create(ugen_state_t *ugenp, usb_ep_descr_t *ep_descr,
15697c478bd9Sstevel@tonic-gate     uchar_t cfgval, uchar_t cfgidx, uchar_t iface, uchar_t alt)
15707c478bd9Sstevel@tonic-gate {
15717c478bd9Sstevel@tonic-gate 	char		node_name[32], *type;
15727c478bd9Sstevel@tonic-gate 	int		vid = ugenp->ug_dev_data->dev_descr->idVendor;
15737c478bd9Sstevel@tonic-gate 	int		pid = ugenp->ug_dev_data->dev_descr->idProduct;
15747c478bd9Sstevel@tonic-gate 	minor_t		minor;
15757c478bd9Sstevel@tonic-gate 	int		minor_index;
15767c478bd9Sstevel@tonic-gate 	ugen_minor_t	minor_code, minor_code_base;
15777c478bd9Sstevel@tonic-gate 	int		owns_device = (usb_owns_device(ugenp->ug_dip) ?
1578*0a05e705Slc 	    UGEN_OWNS_DEVICE : 0);
15797c478bd9Sstevel@tonic-gate 	int		ep_index =
1580*0a05e705Slc 	    usb_get_ep_index(ep_descr->bEndpointAddress);
15817c478bd9Sstevel@tonic-gate 	int		ep_addr =
1582*0a05e705Slc 	    ep_descr->bEndpointAddress & USB_EP_NUM_MASK;
15837c478bd9Sstevel@tonic-gate 	int		ep_type =
1584*0a05e705Slc 	    ep_descr->bmAttributes & USB_EP_ATTR_MASK;
15857c478bd9Sstevel@tonic-gate 	int		ep_dir =
1586*0a05e705Slc 	    ep_descr->bEndpointAddress & USB_EP_DIR_IN;
15877c478bd9Sstevel@tonic-gate 
15887c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
15897c478bd9Sstevel@tonic-gate 	    "ugen_epxs_minor_nodes_create: "
15907c478bd9Sstevel@tonic-gate 	    "cfgval=%d cfgidx=%d if=%d alt=%d ep=0x%x",
15917c478bd9Sstevel@tonic-gate 	    cfgval, cfgidx, iface, alt, ep_addr);
15927c478bd9Sstevel@tonic-gate 
15937c478bd9Sstevel@tonic-gate 	if (ugenp->ug_instance >= UGEN_MINOR_INSTANCE_LIMIT(ugenp)) {
15947c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L0(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
15957c478bd9Sstevel@tonic-gate 		    "instance number too high (%d)", ugenp->ug_instance);
15967c478bd9Sstevel@tonic-gate 
15977c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
15987c478bd9Sstevel@tonic-gate 	}
15997c478bd9Sstevel@tonic-gate 
16007c478bd9Sstevel@tonic-gate 	/* create stat and xfer minor node */
16017c478bd9Sstevel@tonic-gate 	minor_code_base =
1602*0a05e705Slc 	    ((ugen_minor_t)cfgval) << UGEN_MINOR_CFGVAL_SHIFT |
1603*0a05e705Slc 	    ((ugen_minor_t)cfgidx) << UGEN_MINOR_CFGIDX_SHIFT |
1604*0a05e705Slc 	    iface << UGEN_MINOR_IF_SHIFT |
1605*0a05e705Slc 	    alt << UGEN_MINOR_ALT_SHIFT |
1606*0a05e705Slc 	    ep_index << UGEN_MINOR_EPIDX_SHIFT | owns_device;
16077c478bd9Sstevel@tonic-gate 	minor_code = minor_code_base | UGEN_MINOR_EP_XFER_NODE;
16087c478bd9Sstevel@tonic-gate 
16097c478bd9Sstevel@tonic-gate 	minor_index = ugen_minor_index_create(ugenp, minor_code);
16107c478bd9Sstevel@tonic-gate 	if (minor_index < 0) {
16117c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L1(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
16127c478bd9Sstevel@tonic-gate 		    "too many minor nodes, "
16137c478bd9Sstevel@tonic-gate 		    "cannot create %d.%d.%d.%x",
16147c478bd9Sstevel@tonic-gate 		    cfgval, iface, alt, ep_addr);
16157c478bd9Sstevel@tonic-gate 		/* carry on regardless */
16167c478bd9Sstevel@tonic-gate 
16177c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
16187c478bd9Sstevel@tonic-gate 	}
16197c478bd9Sstevel@tonic-gate 	minor = (minor_index << UGEN_MINOR_IDX_SHIFT(ugenp)) |
1620*0a05e705Slc 	    ugenp->ug_instance << UGEN_MINOR_INSTANCE_SHIFT(ugenp);
16217c478bd9Sstevel@tonic-gate 
16227c478bd9Sstevel@tonic-gate 	if (ep_type == USB_EP_ATTR_CONTROL) {
16237c478bd9Sstevel@tonic-gate 		type = "cntrl";
16247c478bd9Sstevel@tonic-gate 	} else {
16257c478bd9Sstevel@tonic-gate 		type = (ep_dir & USB_EP_DIR_IN) ? "in" : "out";
16267c478bd9Sstevel@tonic-gate 	}
16277c478bd9Sstevel@tonic-gate 
16287c478bd9Sstevel@tonic-gate 	/*
16297c478bd9Sstevel@tonic-gate 	 * xfer ep node name:
16307c478bd9Sstevel@tonic-gate 	 * vid.pid.[in|out|cntrl].[<cfg>.][if<iface>.][<alt>.]<ep addr>
16317c478bd9Sstevel@tonic-gate 	 */
16327c478bd9Sstevel@tonic-gate 	if ((ep_addr == 0) && owns_device) {
16337c478bd9Sstevel@tonic-gate 		(void) sprintf(node_name, "%x.%x.%s%d",
16347c478bd9Sstevel@tonic-gate 		    vid, pid, type, ep_addr);
16357c478bd9Sstevel@tonic-gate 	} else if (cfgidx == 0 && alt == 0) {
16367c478bd9Sstevel@tonic-gate 		(void) sprintf(node_name, "%x.%x.if%d%s%d",
16377c478bd9Sstevel@tonic-gate 		    vid, pid, iface, type, ep_addr);
16387c478bd9Sstevel@tonic-gate 	} else if (cfgidx == 0 && alt != 0) {
16397c478bd9Sstevel@tonic-gate 		(void) sprintf(node_name, "%x.%x.if%d.%d%s%d",
16407c478bd9Sstevel@tonic-gate 		    vid, pid, iface, alt, type, ep_addr);
16417c478bd9Sstevel@tonic-gate 	} else if (cfgidx != 0 && alt == 0) {
16427c478bd9Sstevel@tonic-gate 		(void) sprintf(node_name, "%x.%x.cfg%dif%d%s%d",
16437c478bd9Sstevel@tonic-gate 		    vid, pid, cfgval, iface, type, ep_addr);
16447c478bd9Sstevel@tonic-gate 	} else if (cfgidx != 0 && alt != 0) {
16457c478bd9Sstevel@tonic-gate 		(void) sprintf(node_name, "%x.%x.cfg%dif%d.%d%s%d",
16467c478bd9Sstevel@tonic-gate 		    vid, pid, cfgval, iface, alt,
16477c478bd9Sstevel@tonic-gate 		    type, ep_addr);
16487c478bd9Sstevel@tonic-gate 	}
16497c478bd9Sstevel@tonic-gate 
16507c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
16517c478bd9Sstevel@tonic-gate 	    "minor=0x%x index=%d code=0x%" PRIx64 " name=%s",
16527c478bd9Sstevel@tonic-gate 	    minor, minor_index, minor_code, node_name);
16537c478bd9Sstevel@tonic-gate 
16547c478bd9Sstevel@tonic-gate 	ASSERT(minor < L_MAXMIN);
16557c478bd9Sstevel@tonic-gate 
16567c478bd9Sstevel@tonic-gate 	if ((ddi_create_minor_node(ugenp->ug_dip, node_name,
16577c478bd9Sstevel@tonic-gate 	    S_IFCHR, minor, DDI_NT_UGEN, 0)) != DDI_SUCCESS) {
16587c478bd9Sstevel@tonic-gate 
16597c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
16607c478bd9Sstevel@tonic-gate 	}
16617c478bd9Sstevel@tonic-gate 
16627c478bd9Sstevel@tonic-gate 	ugen_store_devt(ugenp, minor);
16637c478bd9Sstevel@tonic-gate 
16647c478bd9Sstevel@tonic-gate 	minor_code = minor_code_base | UGEN_MINOR_EP_STAT_NODE;
16657c478bd9Sstevel@tonic-gate 	minor_index = ugen_minor_index_create(ugenp, minor_code);
16667c478bd9Sstevel@tonic-gate 	if (minor_index < 0) {
16677c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L1(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
16687c478bd9Sstevel@tonic-gate 		    "too many minor nodes, "
16697c478bd9Sstevel@tonic-gate 		    "cannot create %d.%d.%d.%x stat",
16707c478bd9Sstevel@tonic-gate 		    cfgval, iface, alt,
16717c478bd9Sstevel@tonic-gate 		    ep_descr->bEndpointAddress);
16727c478bd9Sstevel@tonic-gate 		/* carry on regardless */
16737c478bd9Sstevel@tonic-gate 
16747c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
16757c478bd9Sstevel@tonic-gate 	}
16767c478bd9Sstevel@tonic-gate 	minor = (minor_index << UGEN_MINOR_IDX_SHIFT(ugenp)) |
1677*0a05e705Slc 	    ugenp->ug_instance << UGEN_MINOR_INSTANCE_SHIFT(ugenp);
16787c478bd9Sstevel@tonic-gate 
16797c478bd9Sstevel@tonic-gate 	(void) strcat(node_name, "stat");
16807c478bd9Sstevel@tonic-gate 
16817c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
16827c478bd9Sstevel@tonic-gate 	    "minor=0x%x index=%d code=0x%" PRIx64 " name=%s",
16837c478bd9Sstevel@tonic-gate 	    minor, minor_index, minor_code, node_name);
16847c478bd9Sstevel@tonic-gate 
16857c478bd9Sstevel@tonic-gate 	ASSERT(minor < L_MAXMIN);
16867c478bd9Sstevel@tonic-gate 
16877c478bd9Sstevel@tonic-gate 	if ((ddi_create_minor_node(ugenp->ug_dip, node_name,
16887c478bd9Sstevel@tonic-gate 	    S_IFCHR, minor, DDI_NT_UGEN, 0)) != DDI_SUCCESS) {
16897c478bd9Sstevel@tonic-gate 
16907c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
16917c478bd9Sstevel@tonic-gate 	}
16927c478bd9Sstevel@tonic-gate 
16937c478bd9Sstevel@tonic-gate 	ugen_store_devt(ugenp, minor);
16947c478bd9Sstevel@tonic-gate 
16957c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
16967c478bd9Sstevel@tonic-gate }
16977c478bd9Sstevel@tonic-gate 
16987c478bd9Sstevel@tonic-gate 
16997c478bd9Sstevel@tonic-gate /*
17007c478bd9Sstevel@tonic-gate  * close all non-default pipes and drain default pipe
17017c478bd9Sstevel@tonic-gate  */
17027c478bd9Sstevel@tonic-gate static void
17037c478bd9Sstevel@tonic-gate ugen_epx_shutdown(ugen_state_t *ugenp)
17047c478bd9Sstevel@tonic-gate {
17057c478bd9Sstevel@tonic-gate 	int	i;
17067c478bd9Sstevel@tonic-gate 
17077c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
17087c478bd9Sstevel@tonic-gate 	    "ugen_epx_shutdown:");
17097c478bd9Sstevel@tonic-gate 
17107c478bd9Sstevel@tonic-gate 	for (i = 0; i < UGEN_N_ENDPOINTS; i++) {
17117c478bd9Sstevel@tonic-gate 		ugen_ep_t *epp = &ugenp->ug_ep[i];
17127c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
17137c478bd9Sstevel@tonic-gate 		if (epp->ep_state != UGEN_EP_STATE_NONE) {
17147c478bd9Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
17157c478bd9Sstevel@tonic-gate 			(void) usb_serialize_access(epp->ep_ser_cookie,
1716*0a05e705Slc 			    USB_WAIT, 0);
17177c478bd9Sstevel@tonic-gate 			(void) ugen_epx_close_pipe(ugenp, epp);
17187c478bd9Sstevel@tonic-gate 			usb_release_access(epp->ep_ser_cookie);
17197c478bd9Sstevel@tonic-gate 		} else {
17207c478bd9Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
17217c478bd9Sstevel@tonic-gate 		}
17227c478bd9Sstevel@tonic-gate 	}
17237c478bd9Sstevel@tonic-gate }
17247c478bd9Sstevel@tonic-gate 
17257c478bd9Sstevel@tonic-gate 
17267c478bd9Sstevel@tonic-gate /*
17277c478bd9Sstevel@tonic-gate  * find cfg index corresponding to cfg value
17287c478bd9Sstevel@tonic-gate  */
17297c478bd9Sstevel@tonic-gate static int
17307c478bd9Sstevel@tonic-gate ugen_cfgval2idx(ugen_state_t *ugenp, uint_t cfgval)
17317c478bd9Sstevel@tonic-gate {
17327c478bd9Sstevel@tonic-gate 	usb_cfg_data_t	*dev_cfg = ugenp->ug_dev_data->dev_cfg;
17337c478bd9Sstevel@tonic-gate 	int		cfgidx;
17347c478bd9Sstevel@tonic-gate 
17357c478bd9Sstevel@tonic-gate 	for (cfgidx = 0; cfgidx < ugenp->ug_dev_data->dev_n_cfg; cfgidx++) {
17367c478bd9Sstevel@tonic-gate 		dev_cfg = &ugenp->ug_dev_data->dev_cfg[cfgidx];
17377c478bd9Sstevel@tonic-gate 		if (cfgval == dev_cfg->cfg_descr.bConfigurationValue) {
17387c478bd9Sstevel@tonic-gate 
17397c478bd9Sstevel@tonic-gate 			return (cfgidx);
17407c478bd9Sstevel@tonic-gate 		}
17417c478bd9Sstevel@tonic-gate 	}
17427c478bd9Sstevel@tonic-gate 
17437c478bd9Sstevel@tonic-gate 	ASSERT(cfgidx < ugenp->ug_dev_data->dev_n_cfg);
17447c478bd9Sstevel@tonic-gate 
17457c478bd9Sstevel@tonic-gate 	return (0);
17467c478bd9Sstevel@tonic-gate }
17477c478bd9Sstevel@tonic-gate 
17487c478bd9Sstevel@tonic-gate 
17497c478bd9Sstevel@tonic-gate /*
17507c478bd9Sstevel@tonic-gate  * check if any node is open
17517c478bd9Sstevel@tonic-gate  */
17527c478bd9Sstevel@tonic-gate static int
17537c478bd9Sstevel@tonic-gate ugen_epxs_check_open_nodes(ugen_state_t *ugenp)
17547c478bd9Sstevel@tonic-gate {
17557c478bd9Sstevel@tonic-gate 	int	i;
17567c478bd9Sstevel@tonic-gate 
17577c478bd9Sstevel@tonic-gate 	for (i = 1; i < UGEN_N_ENDPOINTS; i++) {
17587c478bd9Sstevel@tonic-gate 		ugen_ep_t *epp = &ugenp->ug_ep[i];
17597c478bd9Sstevel@tonic-gate 
17607c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
17617c478bd9Sstevel@tonic-gate 
17627c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
17637c478bd9Sstevel@tonic-gate 		    "ugen_epxs_check_open_nodes: epp=%d, ep_state=0x%x",
17647c478bd9Sstevel@tonic-gate 		    i, epp->ep_state);
17657c478bd9Sstevel@tonic-gate 
17667c478bd9Sstevel@tonic-gate 		if (epp->ep_state & UGEN_EP_STATE_XS_OPEN) {
17677c478bd9Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
17687c478bd9Sstevel@tonic-gate 
17697c478bd9Sstevel@tonic-gate 			return (USB_SUCCESS);
17707c478bd9Sstevel@tonic-gate 		}
17717c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
17727c478bd9Sstevel@tonic-gate 	}
17737c478bd9Sstevel@tonic-gate 
17747c478bd9Sstevel@tonic-gate 	return (USB_FAILURE);
17757c478bd9Sstevel@tonic-gate }
17767c478bd9Sstevel@tonic-gate 
17777c478bd9Sstevel@tonic-gate 
17787c478bd9Sstevel@tonic-gate /*
17797c478bd9Sstevel@tonic-gate  * check if we can switch alternate
17807c478bd9Sstevel@tonic-gate  */
17817c478bd9Sstevel@tonic-gate static int
17827c478bd9Sstevel@tonic-gate ugen_epxs_check_alt_switch(ugen_state_t *ugenp, uchar_t iface, uchar_t cfgidx)
17837c478bd9Sstevel@tonic-gate {
17847c478bd9Sstevel@tonic-gate 	int	i;
17857c478bd9Sstevel@tonic-gate 
17867c478bd9Sstevel@tonic-gate 	for (i = 1; i < UGEN_N_ENDPOINTS; i++) {
17877c478bd9Sstevel@tonic-gate 		ugen_ep_t *epp = &ugenp->ug_ep[i];
17887c478bd9Sstevel@tonic-gate 
17897c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
17907c478bd9Sstevel@tonic-gate 
17917c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
17927c478bd9Sstevel@tonic-gate 		    "ugen_epxs_check_alt_switch: epp=%d, ep_state=0x%x",
17937c478bd9Sstevel@tonic-gate 		    i, epp->ep_state);
17947c478bd9Sstevel@tonic-gate 
17957c478bd9Sstevel@tonic-gate 		/*
17967c478bd9Sstevel@tonic-gate 		 * if the endpoint is open and part of this cfg and interface
17977c478bd9Sstevel@tonic-gate 		 * then we cannot switch alternates
17987c478bd9Sstevel@tonic-gate 		 */
17997c478bd9Sstevel@tonic-gate 		if ((epp->ep_state & UGEN_EP_STATE_XS_OPEN) &&
18007c478bd9Sstevel@tonic-gate 		    (epp->ep_cfgidx == cfgidx) &&
18017c478bd9Sstevel@tonic-gate 		    (epp->ep_if == iface)) {
18027c478bd9Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
18037c478bd9Sstevel@tonic-gate 
18047c478bd9Sstevel@tonic-gate 			return (USB_FAILURE);
18057c478bd9Sstevel@tonic-gate 		}
18067c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
18077c478bd9Sstevel@tonic-gate 	}
18087c478bd9Sstevel@tonic-gate 
18097c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
18107c478bd9Sstevel@tonic-gate }
18117c478bd9Sstevel@tonic-gate 
18127c478bd9Sstevel@tonic-gate 
18137c478bd9Sstevel@tonic-gate /*
18147c478bd9Sstevel@tonic-gate  * implicit switch to new cfg and alt
18157c478bd9Sstevel@tonic-gate  * If a crummy device fails usb_get_cfg or usb_get_alt_if, we carry on
18167c478bd9Sstevel@tonic-gate  * regardless so at least the device can be opened.
18177c478bd9Sstevel@tonic-gate  */
18187c478bd9Sstevel@tonic-gate static int
18197c478bd9Sstevel@tonic-gate ugen_epxs_switch_cfg_alt(ugen_state_t *ugenp, ugen_ep_t *epp, dev_t dev)
18207c478bd9Sstevel@tonic-gate {
18217c478bd9Sstevel@tonic-gate 	int	rval = USB_SUCCESS;
18227c478bd9Sstevel@tonic-gate 	uint_t	alt;
18237c478bd9Sstevel@tonic-gate 	uint_t	new_alt = UGEN_MINOR_ALT(ugenp, dev);
18247c478bd9Sstevel@tonic-gate 	uint_t	new_if = UGEN_MINOR_IF(ugenp, dev);
18257c478bd9Sstevel@tonic-gate 	uint_t	cur_if = epp->ep_if;
18267c478bd9Sstevel@tonic-gate 	uint_t	new_cfgidx = UGEN_MINOR_CFGIDX(ugenp, dev);
18277c478bd9Sstevel@tonic-gate 	uint_t	cur_cfgidx;
18287c478bd9Sstevel@tonic-gate 	uint_t	cfgval;
18297c478bd9Sstevel@tonic-gate 	int	switched = 0;
18307c478bd9Sstevel@tonic-gate 
18317c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
18327c478bd9Sstevel@tonic-gate 	    "ugen_epxs_switch_cfg_alt: old cfgidx=%d, if=%d alt=%d",
18337c478bd9Sstevel@tonic-gate 	    epp->ep_cfgidx, epp->ep_if, epp->ep_alt);
18347c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
18357c478bd9Sstevel@tonic-gate 	    "new cfgidx=%d, if=%d alt=%d ep_state=0x%x",
18367c478bd9Sstevel@tonic-gate 	    new_cfgidx, new_if, new_alt, epp->ep_state);
18377c478bd9Sstevel@tonic-gate 
18387c478bd9Sstevel@tonic-gate 	/* no need to switch if there is only 1 cfg, 1 iface and no alts */
18397c478bd9Sstevel@tonic-gate 	if ((new_if == 0) && (new_alt == 0) &&
18407c478bd9Sstevel@tonic-gate 	    (ugenp->ug_dev_data->dev_n_cfg == 1) &&
18417c478bd9Sstevel@tonic-gate 	    (ugenp->ug_dev_data->dev_cfg[0].cfg_n_if == 1) &&
18427c478bd9Sstevel@tonic-gate 	    (ugenp->ug_dev_data->
18437c478bd9Sstevel@tonic-gate 	    dev_cfg[0].cfg_if[new_if].if_n_alt == 1)) {
18447c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
18457c478bd9Sstevel@tonic-gate 		    "no need for switching: n_cfg=%d n_alt=%d",
18467c478bd9Sstevel@tonic-gate 		    ugenp->ug_dev_data->dev_n_cfg,
18477c478bd9Sstevel@tonic-gate 		    ugenp->ug_dev_data->
1848*0a05e705Slc 		    dev_cfg[0].cfg_if[new_if].if_n_alt);
18497c478bd9Sstevel@tonic-gate 
18507c478bd9Sstevel@tonic-gate 		ASSERT(epp->ep_alt == new_alt);
18517c478bd9Sstevel@tonic-gate 		ASSERT(epp->ep_cfgidx == new_cfgidx);
18527c478bd9Sstevel@tonic-gate 		ASSERT(epp->ep_if == new_if);
18537c478bd9Sstevel@tonic-gate 
18547c478bd9Sstevel@tonic-gate 		return (rval);
18557c478bd9Sstevel@tonic-gate 	}
18567c478bd9Sstevel@tonic-gate 
18577c478bd9Sstevel@tonic-gate 	/* no switch for default endpoint */
18587c478bd9Sstevel@tonic-gate 	if (epp->ep_descr.bEndpointAddress == 0) {
18597c478bd9Sstevel@tonic-gate 
18607c478bd9Sstevel@tonic-gate 		return (rval);
18617c478bd9Sstevel@tonic-gate 	}
18627c478bd9Sstevel@tonic-gate 
18637c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
18647c478bd9Sstevel@tonic-gate 	if ((ugenp->ug_dev_data->dev_n_cfg > 1) &&
18657c478bd9Sstevel@tonic-gate 	    usb_get_cfg(ugenp->ug_dip, &cfgval,
18667c478bd9Sstevel@tonic-gate 	    USB_FLAGS_SLEEP) == USB_SUCCESS) {
18677c478bd9Sstevel@tonic-gate 
18687c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
18697c478bd9Sstevel@tonic-gate 
18707c478bd9Sstevel@tonic-gate 		cur_cfgidx = ugen_cfgval2idx(ugenp, cfgval);
18717c478bd9Sstevel@tonic-gate 
18727c478bd9Sstevel@tonic-gate 		if (new_cfgidx != cur_cfgidx) {
18737c478bd9Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
18747c478bd9Sstevel@tonic-gate 
18757c478bd9Sstevel@tonic-gate 			/*
18767c478bd9Sstevel@tonic-gate 			 * we can't change config if any node
18777c478bd9Sstevel@tonic-gate 			 * is open
18787c478bd9Sstevel@tonic-gate 			 */
18797c478bd9Sstevel@tonic-gate 			if (ugen_epxs_check_open_nodes(ugenp) ==
18807c478bd9Sstevel@tonic-gate 			    USB_SUCCESS) {
18817c478bd9Sstevel@tonic-gate 				mutex_enter(&epp->ep_mutex);
18827c478bd9Sstevel@tonic-gate 
18837c478bd9Sstevel@tonic-gate 				return (USB_BUSY);
18847c478bd9Sstevel@tonic-gate 			}
18857c478bd9Sstevel@tonic-gate 
18867c478bd9Sstevel@tonic-gate 			/*
18877c478bd9Sstevel@tonic-gate 			 * we are going to do this synchronously to
18887c478bd9Sstevel@tonic-gate 			 * keep it simple.
18897c478bd9Sstevel@tonic-gate 			 * This should never hang forever.
18907c478bd9Sstevel@tonic-gate 			 */
18917c478bd9Sstevel@tonic-gate 			if ((rval = usb_set_cfg(ugenp->ug_dip,
18927c478bd9Sstevel@tonic-gate 			    new_cfgidx, USB_FLAGS_SLEEP, NULL,
18937c478bd9Sstevel@tonic-gate 			    NULL)) != USB_SUCCESS) {
18947c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(UGEN_PRINT_XFER,
18957c478bd9Sstevel@tonic-gate 				    ugenp->ug_log_hdl,
18967c478bd9Sstevel@tonic-gate 				    "implicit set cfg (%" PRId64
18977c478bd9Sstevel@tonic-gate 				    ") failed (%d)",
18987c478bd9Sstevel@tonic-gate 				    UGEN_MINOR_CFGIDX(ugenp, dev), rval);
18997c478bd9Sstevel@tonic-gate 				mutex_enter(&epp->ep_mutex);
19007c478bd9Sstevel@tonic-gate 
19017c478bd9Sstevel@tonic-gate 				return (rval);
19027c478bd9Sstevel@tonic-gate 			}
19037c478bd9Sstevel@tonic-gate 			mutex_enter(&epp->ep_mutex);
19047c478bd9Sstevel@tonic-gate 			epp->ep_if = new_if;
19057c478bd9Sstevel@tonic-gate 			switched++;
19067c478bd9Sstevel@tonic-gate 		}
19077c478bd9Sstevel@tonic-gate 		epp->ep_cfgidx = new_cfgidx;
19087c478bd9Sstevel@tonic-gate 
19097c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
19107c478bd9Sstevel@tonic-gate 	}
19117c478bd9Sstevel@tonic-gate 
19127c478bd9Sstevel@tonic-gate 	/*
19137c478bd9Sstevel@tonic-gate 	 * implicitly switch to new alternate if
19147c478bd9Sstevel@tonic-gate 	 * - we have not switched configuration (if we
19157c478bd9Sstevel@tonic-gate 	 *   we switched config, the alternate must be 0)
19167c478bd9Sstevel@tonic-gate 	 * - n_alts is > 1
19177c478bd9Sstevel@tonic-gate 	 * - if the device supports get_alternate iface
19187c478bd9Sstevel@tonic-gate 	 */
19197c478bd9Sstevel@tonic-gate 	if ((switched && (new_alt > 0)) ||
19207c478bd9Sstevel@tonic-gate 	    ((ugenp->ug_dev_data->dev_cfg[new_cfgidx].
19217c478bd9Sstevel@tonic-gate 	    cfg_if[new_if].if_n_alt > 1) &&
19227c478bd9Sstevel@tonic-gate 	    (usb_get_alt_if(ugenp->ug_dip, new_if, &alt,
19237c478bd9Sstevel@tonic-gate 	    USB_FLAGS_SLEEP) == USB_SUCCESS))) {
19247c478bd9Sstevel@tonic-gate 		if (switched || (alt != new_alt)) {
19257c478bd9Sstevel@tonic-gate 			if (ugen_epxs_check_alt_switch(ugenp, cur_if,
19267c478bd9Sstevel@tonic-gate 			    new_cfgidx) != USB_SUCCESS) {
19277c478bd9Sstevel@tonic-gate 				mutex_enter(&epp->ep_mutex);
19287c478bd9Sstevel@tonic-gate 
19297c478bd9Sstevel@tonic-gate 				return (USB_BUSY);
19307c478bd9Sstevel@tonic-gate 			}
19317c478bd9Sstevel@tonic-gate 			if ((rval = usb_set_alt_if(ugenp->ug_dip, new_if,
19327c478bd9Sstevel@tonic-gate 			    new_alt, USB_FLAGS_SLEEP, NULL, NULL)) !=
19337c478bd9Sstevel@tonic-gate 			    USB_SUCCESS) {
19347c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(UGEN_PRINT_XFER,
19357c478bd9Sstevel@tonic-gate 				    ugenp->ug_log_hdl,
19367c478bd9Sstevel@tonic-gate 				    "implicit set new alternate "
19377c478bd9Sstevel@tonic-gate 				    "(%d) failed (%d)", new_alt, rval);
19387c478bd9Sstevel@tonic-gate 				mutex_enter(&epp->ep_mutex);
19397c478bd9Sstevel@tonic-gate 
19407c478bd9Sstevel@tonic-gate 				return (rval);
19417c478bd9Sstevel@tonic-gate 			}
19427c478bd9Sstevel@tonic-gate 		}
19437c478bd9Sstevel@tonic-gate 	}
19447c478bd9Sstevel@tonic-gate 
19457c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
19467c478bd9Sstevel@tonic-gate 	epp->ep_alt = new_alt;
19477c478bd9Sstevel@tonic-gate 	ugen_update_ep_descr(ugenp, epp);
19487c478bd9Sstevel@tonic-gate 
19497c478bd9Sstevel@tonic-gate 	return (rval);
19507c478bd9Sstevel@tonic-gate }
19517c478bd9Sstevel@tonic-gate 
19527c478bd9Sstevel@tonic-gate 
19537c478bd9Sstevel@tonic-gate /*
19547c478bd9Sstevel@tonic-gate  * update endpoint descriptor in ugen_ep structure after
19557c478bd9Sstevel@tonic-gate  * switching configuration or alternate
19567c478bd9Sstevel@tonic-gate  */
19577c478bd9Sstevel@tonic-gate static void
19587c478bd9Sstevel@tonic-gate ugen_update_ep_descr(ugen_state_t *ugenp, ugen_ep_t *epp)
19597c478bd9Sstevel@tonic-gate {
19607c478bd9Sstevel@tonic-gate 	usb_cfg_data_t	*dev_cfg = ugenp->ug_dev_data->dev_cfg;
19617c478bd9Sstevel@tonic-gate 	usb_if_data_t	*if_data;
19627c478bd9Sstevel@tonic-gate 	usb_alt_if_data_t *alt_if_data;
19637c478bd9Sstevel@tonic-gate 	usb_ep_data_t	*ep_data;
19647c478bd9Sstevel@tonic-gate 	int		ep;
19657c478bd9Sstevel@tonic-gate 
19667c478bd9Sstevel@tonic-gate 	dev_cfg = &ugenp->ug_dev_data->dev_cfg[epp->ep_cfgidx];
19677c478bd9Sstevel@tonic-gate 	if_data = &dev_cfg->cfg_if[epp->ep_if];
19687c478bd9Sstevel@tonic-gate 	alt_if_data = &if_data->if_alt[epp->ep_alt];
19697c478bd9Sstevel@tonic-gate 	for (ep = 0; ep < alt_if_data->altif_n_ep; ep++) {
19707c478bd9Sstevel@tonic-gate 		ep_data = &alt_if_data->altif_ep[ep];
19717c478bd9Sstevel@tonic-gate 		if (usb_get_ep_index(ep_data->ep_descr.
19727c478bd9Sstevel@tonic-gate 		    bEndpointAddress) ==
19737c478bd9Sstevel@tonic-gate 		    usb_get_ep_index(epp->ep_descr.
19747c478bd9Sstevel@tonic-gate 		    bEndpointAddress)) {
19757c478bd9Sstevel@tonic-gate 			epp->ep_descr = ep_data->ep_descr;
19767c478bd9Sstevel@tonic-gate 
19777c478bd9Sstevel@tonic-gate 			break;
19787c478bd9Sstevel@tonic-gate 		}
19797c478bd9Sstevel@tonic-gate 	}
19807c478bd9Sstevel@tonic-gate }
19817c478bd9Sstevel@tonic-gate 
19827c478bd9Sstevel@tonic-gate 
19837c478bd9Sstevel@tonic-gate /*
19847c478bd9Sstevel@tonic-gate  * Xfer endpoint management
19857c478bd9Sstevel@tonic-gate  *
19867c478bd9Sstevel@tonic-gate  * open an endpoint for xfers
19877c478bd9Sstevel@tonic-gate  *
19887c478bd9Sstevel@tonic-gate  * Return values: errno
19897c478bd9Sstevel@tonic-gate  */
19907c478bd9Sstevel@tonic-gate static int
19917c478bd9Sstevel@tonic-gate ugen_epx_open(ugen_state_t *ugenp, dev_t dev, int flag)
19927c478bd9Sstevel@tonic-gate {
19937c478bd9Sstevel@tonic-gate 	ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
19947c478bd9Sstevel@tonic-gate 	int	rval;
19957c478bd9Sstevel@tonic-gate 
19967c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
19977c478bd9Sstevel@tonic-gate 
19987c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
19997c478bd9Sstevel@tonic-gate 	    "ugen_epx_open: minor=0x%x flag=0x%x ep_state=0x%x",
20007c478bd9Sstevel@tonic-gate 	    getminor(dev), flag, epp->ep_state);
20017c478bd9Sstevel@tonic-gate 
20027c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_ACTIVE);
20037c478bd9Sstevel@tonic-gate 
20047c478bd9Sstevel@tonic-gate 	/* implicit switch to new cfg & alt */
20057c478bd9Sstevel@tonic-gate 	if ((epp->ep_state & UGEN_EP_STATE_XFER_OPEN) != 0) {
20067c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
20077c478bd9Sstevel@tonic-gate 
20087c478bd9Sstevel@tonic-gate 		return (EBUSY);
20097c478bd9Sstevel@tonic-gate 	}
20107c478bd9Sstevel@tonic-gate 	if ((rval = ugen_epxs_switch_cfg_alt(ugenp, epp, dev)) ==
20117c478bd9Sstevel@tonic-gate 	    USB_SUCCESS) {
20127c478bd9Sstevel@tonic-gate 		rval = ugen_epx_open_pipe(ugenp, epp, flag);
20137c478bd9Sstevel@tonic-gate 	}
20147c478bd9Sstevel@tonic-gate 
20157c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
20167c478bd9Sstevel@tonic-gate 	    "ugen_epx_open: state=0x%x", epp->ep_state);
20177c478bd9Sstevel@tonic-gate 
20187c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_ACTIVE);
20197c478bd9Sstevel@tonic-gate 	epp->ep_done = epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
20207c478bd9Sstevel@tonic-gate 
20217c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
20227c478bd9Sstevel@tonic-gate 
20237c478bd9Sstevel@tonic-gate 	return (usb_rval2errno(rval));
20247c478bd9Sstevel@tonic-gate }
20257c478bd9Sstevel@tonic-gate 
20267c478bd9Sstevel@tonic-gate 
20277c478bd9Sstevel@tonic-gate /*
20287c478bd9Sstevel@tonic-gate  * close an endpoint for xfers
20297c478bd9Sstevel@tonic-gate  */
20307c478bd9Sstevel@tonic-gate static void
20317c478bd9Sstevel@tonic-gate ugen_epx_close(ugen_state_t *ugenp, dev_t dev, int flag)
20327c478bd9Sstevel@tonic-gate {
20337c478bd9Sstevel@tonic-gate 	ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
20347c478bd9Sstevel@tonic-gate 
20357c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
20367c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
20377c478bd9Sstevel@tonic-gate 	    "ugen_epx_close: dev=0x%lx flag=0x%x state=0x%x", dev, flag,
20387c478bd9Sstevel@tonic-gate 	    epp->ep_state);
20397c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
20407c478bd9Sstevel@tonic-gate 
20417c478bd9Sstevel@tonic-gate 	ugen_epx_close_pipe(ugenp, epp);
20427c478bd9Sstevel@tonic-gate 
20437c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
20447c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
20457c478bd9Sstevel@tonic-gate 	    "ugen_epx_close: state=0x%x", epp->ep_state);
20467c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_ACTIVE);
20477c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_bp == NULL);
20487c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_done == 0);
20497c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_data == NULL);
20507c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
20517c478bd9Sstevel@tonic-gate }
20527c478bd9Sstevel@tonic-gate 
20537c478bd9Sstevel@tonic-gate 
20547c478bd9Sstevel@tonic-gate /*
20557c478bd9Sstevel@tonic-gate  * open pipe for this endpoint
20567c478bd9Sstevel@tonic-gate  * If the pipe is an interrupt IN pipe, start polling immediately
20577c478bd9Sstevel@tonic-gate  */
20587c478bd9Sstevel@tonic-gate static int
20597c478bd9Sstevel@tonic-gate ugen_epx_open_pipe(ugen_state_t *ugenp, ugen_ep_t *epp, int flag)
20607c478bd9Sstevel@tonic-gate {
20617c478bd9Sstevel@tonic-gate 	int rval = USB_SUCCESS;
20627c478bd9Sstevel@tonic-gate 
20637c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
20647c478bd9Sstevel@tonic-gate 	    "ugen_epx_open_pipe: epp=0x%p flag=%d state=0x%x",
20657c478bd9Sstevel@tonic-gate 	    epp, flag, epp->ep_state);
20667c478bd9Sstevel@tonic-gate 
20677c478bd9Sstevel@tonic-gate 	epp->ep_state |= UGEN_EP_STATE_XFER_OPEN;
20687c478bd9Sstevel@tonic-gate 	epp->ep_xfer_oflag = flag;
20697c478bd9Sstevel@tonic-gate 
20707c478bd9Sstevel@tonic-gate 	/* if default pipe, just copy the handle */
20717c478bd9Sstevel@tonic-gate 	if ((epp->ep_descr.bEndpointAddress & USB_EP_NUM_MASK) == 0) {
20727c478bd9Sstevel@tonic-gate 		epp->ep_ph = ugenp->ug_dev_data->dev_default_ph;
20737c478bd9Sstevel@tonic-gate 	} else {
20747c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
20757c478bd9Sstevel@tonic-gate 
20767c478bd9Sstevel@tonic-gate 		/* open pipe */
20777c478bd9Sstevel@tonic-gate 		rval = usb_pipe_open(ugenp->ug_dip,
20787c478bd9Sstevel@tonic-gate 		    &epp->ep_descr, &epp->ep_pipe_policy,
20797c478bd9Sstevel@tonic-gate 		    USB_FLAGS_SLEEP, &epp->ep_ph);
20807c478bd9Sstevel@tonic-gate 
20817c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
20827c478bd9Sstevel@tonic-gate 
20837c478bd9Sstevel@tonic-gate 		if (rval == USB_SUCCESS) {
20847c478bd9Sstevel@tonic-gate 			(void) usb_pipe_set_private(epp->ep_ph,
2085*0a05e705Slc 			    (usb_opaque_t)epp);
20867c478bd9Sstevel@tonic-gate 
20877c478bd9Sstevel@tonic-gate 			/*
20887c478bd9Sstevel@tonic-gate 			 * if interrupt IN pipe, and one xfer mode
20897c478bd9Sstevel@tonic-gate 			 * has not been set, start polling immediately
20907c478bd9Sstevel@tonic-gate 			 */
20917c478bd9Sstevel@tonic-gate 			if ((UGEN_XFER_TYPE(epp) == USB_EP_ATTR_INTR) &&
20927c478bd9Sstevel@tonic-gate 			    (!(epp->ep_one_xfer)) &&
20937c478bd9Sstevel@tonic-gate 			    (UGEN_XFER_DIR(epp) == USB_EP_DIR_IN)) {
20947c478bd9Sstevel@tonic-gate 				if ((rval = ugen_epx_intr_IN_start_polling(
20957c478bd9Sstevel@tonic-gate 				    ugenp, epp)) != USB_SUCCESS) {
20967c478bd9Sstevel@tonic-gate 
20977c478bd9Sstevel@tonic-gate 					mutex_exit(&epp->ep_mutex);
20987c478bd9Sstevel@tonic-gate 					usb_pipe_close(ugenp->ug_dip,
20997c478bd9Sstevel@tonic-gate 					    epp->ep_ph, USB_FLAGS_SLEEP,
21007c478bd9Sstevel@tonic-gate 					    NULL, NULL);
21017c478bd9Sstevel@tonic-gate 					mutex_enter(&epp->ep_mutex);
21027c478bd9Sstevel@tonic-gate 
21037c478bd9Sstevel@tonic-gate 					epp->ep_ph = NULL;
21047c478bd9Sstevel@tonic-gate 				} else {
21057c478bd9Sstevel@tonic-gate 					epp->ep_state |=
21067c478bd9Sstevel@tonic-gate 					    UGEN_EP_STATE_INTR_IN_POLLING_ON;
21077c478bd9Sstevel@tonic-gate 
21087c478bd9Sstevel@tonic-gate 					/* allow for about 1 sec of data */
21097c478bd9Sstevel@tonic-gate 					epp->ep_buf_limit =
21107c478bd9Sstevel@tonic-gate 					    (1000/epp->ep_descr.bInterval) *
21117c478bd9Sstevel@tonic-gate 					    epp->ep_descr.wMaxPacketSize;
21127c478bd9Sstevel@tonic-gate 				}
21137c478bd9Sstevel@tonic-gate 			}
2114*0a05e705Slc 
2115*0a05e705Slc 			/* set ep_buf_limit for isoc IN pipe */
2116*0a05e705Slc 			if ((UGEN_XFER_TYPE(epp) == USB_EP_ATTR_ISOCH) &&
2117*0a05e705Slc 			    (UGEN_XFER_DIR(epp) == USB_EP_DIR_IN)) {
2118*0a05e705Slc 				uint16_t max_size;
2119*0a05e705Slc 				uint32_t framecnt;
2120*0a05e705Slc 
2121*0a05e705Slc 				max_size =
2122*0a05e705Slc 				    UGEN_PKT_SIZE(epp->ep_descr.wMaxPacketSize);
2123*0a05e705Slc 
2124*0a05e705Slc 				/*
2125*0a05e705Slc 				 * wMaxPacketSize bits 10..0 specifies maximum
2126*0a05e705Slc 				 * packet size, which can hold 1024 bytes. If
2127*0a05e705Slc 				 * bits 12..11 is non zero, max_size will be
2128*0a05e705Slc 				 * greater than 1024 and the endpoint is a
2129*0a05e705Slc 				 * high-bandwidth endpoint.
2130*0a05e705Slc 				 */
2131*0a05e705Slc 				if (max_size <= 1024) {
2132*0a05e705Slc 				/*
2133*0a05e705Slc 				 * allowing about 1s data of highspeed and 8s
2134*0a05e705Slc 				 * data of full speed device
2135*0a05e705Slc 				 */
2136*0a05e705Slc 					framecnt = ugen_isoc_buf_limit;
2137*0a05e705Slc 					epp->ep_buf_limit = framecnt *
2138*0a05e705Slc 					    max_size * 8;
2139*0a05e705Slc 				} else {
2140*0a05e705Slc 				/*
2141*0a05e705Slc 				 * allow for about 333 ms data for high-speed
2142*0a05e705Slc 				 * high-bandwidth data
2143*0a05e705Slc 				 */
2144*0a05e705Slc 					framecnt = ugen_isoc_buf_limit/3;
2145*0a05e705Slc 					epp->ep_buf_limit =
2146*0a05e705Slc 					    framecnt * max_size * 8;
2147*0a05e705Slc 				}
2148*0a05e705Slc 
2149*0a05e705Slc 				epp->ep_isoc_in_inited = 0;
2150*0a05e705Slc 			}
21517c478bd9Sstevel@tonic-gate 		}
21527c478bd9Sstevel@tonic-gate 	}
21537c478bd9Sstevel@tonic-gate 
21547c478bd9Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
21557c478bd9Sstevel@tonic-gate 		epp->ep_state &= ~(UGEN_EP_STATE_XFER_OPEN |
21567c478bd9Sstevel@tonic-gate 		    UGEN_EP_STATE_INTR_IN_POLLING_ON);
21577c478bd9Sstevel@tonic-gate 	}
21587c478bd9Sstevel@tonic-gate 
21597c478bd9Sstevel@tonic-gate 	return (rval);
21607c478bd9Sstevel@tonic-gate }
21617c478bd9Sstevel@tonic-gate 
21627c478bd9Sstevel@tonic-gate 
21637c478bd9Sstevel@tonic-gate /*
21647c478bd9Sstevel@tonic-gate  * close an endpoint pipe
21657c478bd9Sstevel@tonic-gate  */
21667c478bd9Sstevel@tonic-gate static void
21677c478bd9Sstevel@tonic-gate ugen_epx_close_pipe(ugen_state_t *ugenp, ugen_ep_t *epp)
21687c478bd9Sstevel@tonic-gate {
21697c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
21707c478bd9Sstevel@tonic-gate 	    "ugen_epx_close_pipe: epp=0x%p", epp);
21717c478bd9Sstevel@tonic-gate 
21727c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
21737c478bd9Sstevel@tonic-gate 	if (epp->ep_state & UGEN_EP_STATE_XFER_OPEN) {
2174*0a05e705Slc 
2175*0a05e705Slc 		/*  free isoc pipe private data ep_isoc_info.isoc_pkt_descr. */
2176*0a05e705Slc 		if (UGEN_XFER_TYPE(epp) == USB_EP_ATTR_ISOCH) {
2177*0a05e705Slc 			int len;
2178*0a05e705Slc 			int n_pkt;
2179*0a05e705Slc 
2180*0a05e705Slc 			if (UGEN_XFER_DIR(epp) == USB_EP_DIR_IN &&
2181*0a05e705Slc 			    (epp->ep_state &
2182*0a05e705Slc 			    UGEN_EP_STATE_ISOC_IN_POLLING_ON)) {
2183*0a05e705Slc 				mutex_exit(&epp->ep_mutex);
2184*0a05e705Slc 				usb_pipe_stop_isoc_polling(epp->ep_ph,
2185*0a05e705Slc 				    USB_FLAGS_SLEEP);
2186*0a05e705Slc 				mutex_enter(&epp->ep_mutex);
2187*0a05e705Slc 			}
2188*0a05e705Slc 
2189*0a05e705Slc 			if (epp->ep_isoc_info.isoc_pkt_descr) {
2190*0a05e705Slc 				n_pkt = epp->ep_isoc_info.
2191*0a05e705Slc 				    isoc_pkts_count;
2192*0a05e705Slc 				len = sizeof (ugen_isoc_pkt_descr_t) * n_pkt;
2193*0a05e705Slc 
2194*0a05e705Slc 				kmem_free(epp->ep_isoc_info.isoc_pkt_descr,
2195*0a05e705Slc 				    len);
2196*0a05e705Slc 
2197*0a05e705Slc 				epp->ep_isoc_info.isoc_pkt_descr = NULL;
2198*0a05e705Slc 			}
2199*0a05e705Slc 			epp->ep_isoc_in_inited = 0;
2200*0a05e705Slc 
2201*0a05e705Slc 		}
2202*0a05e705Slc 
2203*0a05e705Slc 
22047c478bd9Sstevel@tonic-gate 		epp->ep_state &= ~(UGEN_EP_STATE_XFER_OPEN |
2205*0a05e705Slc 		    UGEN_EP_STATE_INTR_IN_POLLING_IS_STOPPED |
2206*0a05e705Slc 		    UGEN_EP_STATE_INTR_IN_POLLING_ON |
2207*0a05e705Slc 		    UGEN_EP_STATE_ISOC_IN_POLLING_IS_STOPPED |
2208*0a05e705Slc 		    UGEN_EP_STATE_ISOC_IN_POLLING_ON);
22097c478bd9Sstevel@tonic-gate 
22107c478bd9Sstevel@tonic-gate 		if (epp->ep_ph == ugenp->ug_dev_data->dev_default_ph) {
22117c478bd9Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
22127c478bd9Sstevel@tonic-gate 
22137c478bd9Sstevel@tonic-gate 			(void) usb_pipe_drain_reqs(ugenp->ug_dip,
22147c478bd9Sstevel@tonic-gate 			    epp->ep_ph, 0, USB_FLAGS_SLEEP,
22157c478bd9Sstevel@tonic-gate 			    NULL, NULL);
22167c478bd9Sstevel@tonic-gate 			mutex_enter(&epp->ep_mutex);
22177c478bd9Sstevel@tonic-gate 		} else {
22187c478bd9Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
22197c478bd9Sstevel@tonic-gate 			usb_pipe_close(ugenp->ug_dip,
22207c478bd9Sstevel@tonic-gate 			    epp->ep_ph, USB_FLAGS_SLEEP, NULL, NULL);
22217c478bd9Sstevel@tonic-gate 
22227c478bd9Sstevel@tonic-gate 			mutex_enter(&epp->ep_mutex);
22237c478bd9Sstevel@tonic-gate 			epp->ep_ph = NULL;
22247c478bd9Sstevel@tonic-gate 		}
22257c478bd9Sstevel@tonic-gate 
22267c478bd9Sstevel@tonic-gate 		freemsg(epp->ep_data);
22277c478bd9Sstevel@tonic-gate 		epp->ep_ph = NULL;
22287c478bd9Sstevel@tonic-gate 		epp->ep_data = NULL;
22297c478bd9Sstevel@tonic-gate 	}
22307c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_ph == NULL);
22317c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_data == NULL);
22327c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
22337c478bd9Sstevel@tonic-gate }
22347c478bd9Sstevel@tonic-gate 
22357c478bd9Sstevel@tonic-gate 
22367c478bd9Sstevel@tonic-gate /*
22377c478bd9Sstevel@tonic-gate  * start endpoint xfer
22387c478bd9Sstevel@tonic-gate  *
22397c478bd9Sstevel@tonic-gate  * We first serialize at endpoint level for only one request at the time
22407c478bd9Sstevel@tonic-gate  *
22417c478bd9Sstevel@tonic-gate  * Return values: errno
22427c478bd9Sstevel@tonic-gate  */
22437c478bd9Sstevel@tonic-gate static int
22447c478bd9Sstevel@tonic-gate ugen_epx_req(ugen_state_t *ugenp, struct buf *bp)
22457c478bd9Sstevel@tonic-gate {
22467c478bd9Sstevel@tonic-gate 	dev_t		dev = bp->b_edev;
22477c478bd9Sstevel@tonic-gate 	ugen_ep_t	*epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
22487c478bd9Sstevel@tonic-gate 	boolean_t	wait = B_FALSE;
22497c478bd9Sstevel@tonic-gate 	int		rval = 0;
22507c478bd9Sstevel@tonic-gate 
22517c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
22527c478bd9Sstevel@tonic-gate 	    "ugen_epx_req: bp=0x%p dev=0x%lx", (void *)bp, dev);
22537c478bd9Sstevel@tonic-gate 
22547c478bd9Sstevel@tonic-gate 	/* single thread per endpoint, one request at the time */
22557c478bd9Sstevel@tonic-gate 	if (usb_serialize_access(epp->ep_ser_cookie, USB_WAIT_SIG, 0) <=
22567c478bd9Sstevel@tonic-gate 	    0) {
22577c478bd9Sstevel@tonic-gate 
22587c478bd9Sstevel@tonic-gate 		return (EINTR);
22597c478bd9Sstevel@tonic-gate 	}
22607c478bd9Sstevel@tonic-gate 
22617c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
22627c478bd9Sstevel@tonic-gate 	switch (ugenp->ug_dev_state) {
22637c478bd9Sstevel@tonic-gate 	case USB_DEV_ONLINE:
22647c478bd9Sstevel@tonic-gate 
22657c478bd9Sstevel@tonic-gate 		break;
22667c478bd9Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
22677c478bd9Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
22687c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
22697c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_DISCONNECTED;
22707c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
22717c478bd9Sstevel@tonic-gate 		rval = ENODEV;
22727c478bd9Sstevel@tonic-gate 
22737c478bd9Sstevel@tonic-gate 		break;
22747c478bd9Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RESUME:
22757c478bd9Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
22767c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
22777c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_SUSPENDED;
22787c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
22797c478bd9Sstevel@tonic-gate 		rval = EBADF;
22807c478bd9Sstevel@tonic-gate 
22817c478bd9Sstevel@tonic-gate 		break;
22827c478bd9Sstevel@tonic-gate 	default:
22837c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
22847c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_HW_ERR;
22857c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
22867c478bd9Sstevel@tonic-gate 		rval = EIO;
22877c478bd9Sstevel@tonic-gate 
22887c478bd9Sstevel@tonic-gate 		break;
22897c478bd9Sstevel@tonic-gate 	}
22907c478bd9Sstevel@tonic-gate 
22917c478bd9Sstevel@tonic-gate #ifndef __lock_lint
22927c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
22937c478bd9Sstevel@tonic-gate 	    "ugen_epx_req: lcmd_status=0x%x", epp->ep_lcmd_status);
22947c478bd9Sstevel@tonic-gate #endif
22957c478bd9Sstevel@tonic-gate 
22967c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
22977c478bd9Sstevel@tonic-gate 
22987c478bd9Sstevel@tonic-gate 	if (rval) {
22997c478bd9Sstevel@tonic-gate 		usb_release_access(epp->ep_ser_cookie);
23007c478bd9Sstevel@tonic-gate 
23017c478bd9Sstevel@tonic-gate 		return (rval);
23027c478bd9Sstevel@tonic-gate 	}
23037c478bd9Sstevel@tonic-gate 
23047c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
23057c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_XS_OPEN);
23067c478bd9Sstevel@tonic-gate 	epp->ep_done = 0;
23077c478bd9Sstevel@tonic-gate 	epp->ep_bp = bp;
23087c478bd9Sstevel@tonic-gate 
23097c478bd9Sstevel@tonic-gate 	switch (epp->ep_descr.bmAttributes & USB_EP_ATTR_MASK) {
23107c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_CONTROL:
23117c478bd9Sstevel@tonic-gate 		rval = ugen_epx_ctrl_req(ugenp, epp, bp, &wait);
23127c478bd9Sstevel@tonic-gate 
23137c478bd9Sstevel@tonic-gate 		break;
23147c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_BULK:
23157c478bd9Sstevel@tonic-gate 		rval = ugen_epx_bulk_req(ugenp, epp, bp, &wait);
23167c478bd9Sstevel@tonic-gate 
23177c478bd9Sstevel@tonic-gate 		break;
23187c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_INTR:
23197c478bd9Sstevel@tonic-gate 		if (bp->b_flags & B_READ) {
23207c478bd9Sstevel@tonic-gate 			rval = ugen_epx_intr_IN_req(ugenp, epp, bp, &wait);
23217c478bd9Sstevel@tonic-gate 		} else {
23227c478bd9Sstevel@tonic-gate 			rval = ugen_epx_intr_OUT_req(ugenp, epp, bp, &wait);
23237c478bd9Sstevel@tonic-gate 		}
23247c478bd9Sstevel@tonic-gate 
23257c478bd9Sstevel@tonic-gate 		break;
23267c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_ISOCH:
2327*0a05e705Slc 		if (bp->b_flags & B_READ) {
2328*0a05e705Slc 			rval = ugen_epx_isoc_IN_req(ugenp, epp, bp, &wait);
2329*0a05e705Slc 		} else {
2330*0a05e705Slc 			rval = ugen_epx_isoc_OUT_req(ugenp, epp, bp, &wait);
2331*0a05e705Slc 		}
2332*0a05e705Slc 
2333*0a05e705Slc 		break;
23347c478bd9Sstevel@tonic-gate 	default:
23357c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
23367c478bd9Sstevel@tonic-gate 		rval = USB_INVALID_REQUEST;
23377c478bd9Sstevel@tonic-gate 	}
23387c478bd9Sstevel@tonic-gate 
23397c478bd9Sstevel@tonic-gate 	/* if the xfer could not immediately be completed, block here */
23407c478bd9Sstevel@tonic-gate 	if ((rval == USB_SUCCESS) && wait) {
23417c478bd9Sstevel@tonic-gate 		while (!epp->ep_done) {
23427c478bd9Sstevel@tonic-gate 			if ((cv_wait_sig(&epp->ep_wait_cv,
23437c478bd9Sstevel@tonic-gate 			    &epp->ep_mutex) <= 0) && !epp->ep_done) {
23447c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(UGEN_PRINT_XFER,
23457c478bd9Sstevel@tonic-gate 				    ugenp->ug_log_hdl,
23467c478bd9Sstevel@tonic-gate 				    "ugen_epx_req: interrupted ep=0x%" PRIx64,
23477c478bd9Sstevel@tonic-gate 				    UGEN_MINOR_EPIDX(ugenp, dev));
23487c478bd9Sstevel@tonic-gate 
23497c478bd9Sstevel@tonic-gate 				/*
23507c478bd9Sstevel@tonic-gate 				 * blow away the request except for dflt pipe
23517c478bd9Sstevel@tonic-gate 				 * (this is prevented in USBA)
23527c478bd9Sstevel@tonic-gate 				 */
23537c478bd9Sstevel@tonic-gate 				mutex_exit(&epp->ep_mutex);
23547c478bd9Sstevel@tonic-gate 				usb_pipe_reset(ugenp->ug_dip, epp->ep_ph,
23557c478bd9Sstevel@tonic-gate 				    USB_FLAGS_SLEEP, NULL, NULL);
23567c478bd9Sstevel@tonic-gate 				(void) usb_pipe_drain_reqs(ugenp->ug_dip,
23577c478bd9Sstevel@tonic-gate 				    epp->ep_ph, 0,
23587c478bd9Sstevel@tonic-gate 				    USB_FLAGS_SLEEP, NULL, NULL);
23597c478bd9Sstevel@tonic-gate 
23607c478bd9Sstevel@tonic-gate 				mutex_enter(&epp->ep_mutex);
23617c478bd9Sstevel@tonic-gate 
23627c478bd9Sstevel@tonic-gate 				if (geterror(bp) == 0) {
23637c478bd9Sstevel@tonic-gate 					bioerror(bp, EINTR);
23647c478bd9Sstevel@tonic-gate 				}
23657c478bd9Sstevel@tonic-gate 				epp->ep_lcmd_status =
23667c478bd9Sstevel@tonic-gate 				    USB_LC_STAT_INTERRUPTED;
23677c478bd9Sstevel@tonic-gate 
23687c478bd9Sstevel@tonic-gate 				break;
23697c478bd9Sstevel@tonic-gate 			}
23707c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
23717c478bd9Sstevel@tonic-gate 			    "ugen_epx_req: wakeup");
23727c478bd9Sstevel@tonic-gate 		}
23737c478bd9Sstevel@tonic-gate 	}
23747c478bd9Sstevel@tonic-gate 
23757c478bd9Sstevel@tonic-gate 	/* always set lcmd_status if there was a failure */
23767c478bd9Sstevel@tonic-gate 	if ((rval != USB_SUCCESS) &&
23777c478bd9Sstevel@tonic-gate 	    (epp->ep_lcmd_status == USB_LC_STAT_NOERROR)) {
23787c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_UNSPECIFIED_ERR;
23797c478bd9Sstevel@tonic-gate 	}
23807c478bd9Sstevel@tonic-gate 
23817c478bd9Sstevel@tonic-gate 	epp->ep_done = 0;
23827c478bd9Sstevel@tonic-gate 	epp->ep_bp = NULL;
23837c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
23847c478bd9Sstevel@tonic-gate 
23857c478bd9Sstevel@tonic-gate 	usb_release_access(epp->ep_ser_cookie);
23867c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
23877c478bd9Sstevel@tonic-gate 	    "ugen_epx_req: done");
23887c478bd9Sstevel@tonic-gate 
23897c478bd9Sstevel@tonic-gate 	return (usb_rval2errno(rval));
23907c478bd9Sstevel@tonic-gate }
23917c478bd9Sstevel@tonic-gate 
23927c478bd9Sstevel@tonic-gate 
23937c478bd9Sstevel@tonic-gate /*
23947c478bd9Sstevel@tonic-gate  * handle control xfers
23957c478bd9Sstevel@tonic-gate  */
23967c478bd9Sstevel@tonic-gate static int
23977c478bd9Sstevel@tonic-gate ugen_epx_ctrl_req(ugen_state_t *ugenp, ugen_ep_t *epp,
23987c478bd9Sstevel@tonic-gate     struct buf *bp, boolean_t *wait)
23997c478bd9Sstevel@tonic-gate {
24007c478bd9Sstevel@tonic-gate 	usb_ctrl_req_t *reqp = NULL;
24017c478bd9Sstevel@tonic-gate 	uchar_t	*setup = ((uchar_t *)(bp->b_un.b_addr));
24027c478bd9Sstevel@tonic-gate 	int	rval;
24037c478bd9Sstevel@tonic-gate 	ushort_t wLength;
24047c478bd9Sstevel@tonic-gate 
24057c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
24067c478bd9Sstevel@tonic-gate 	    "ugen_epx_ctrl_req: epp=0x%p state=0x%x bp=0x%p",
24077c478bd9Sstevel@tonic-gate 	    epp, epp->ep_state, bp);
24087c478bd9Sstevel@tonic-gate 
24097c478bd9Sstevel@tonic-gate 	/* is this a read following a write with setup data? */
24107c478bd9Sstevel@tonic-gate 	if (bp->b_flags & B_READ) {
24117c478bd9Sstevel@tonic-gate 		if (epp->ep_data) {
24127c478bd9Sstevel@tonic-gate 			int ep_len = epp->ep_data->b_wptr -
2413*0a05e705Slc 			    epp->ep_data->b_rptr;
24147c478bd9Sstevel@tonic-gate 			int len = min(bp->b_bcount, ep_len);
24157c478bd9Sstevel@tonic-gate 
24167c478bd9Sstevel@tonic-gate 			bcopy(epp->ep_data->b_rptr, bp->b_un.b_addr, len);
24177c478bd9Sstevel@tonic-gate 			epp->ep_data->b_rptr += len;
24187c478bd9Sstevel@tonic-gate 			if ((epp->ep_data->b_wptr - epp->ep_data->b_rptr) ==
24197c478bd9Sstevel@tonic-gate 			    0) {
24207c478bd9Sstevel@tonic-gate 				freemsg(epp->ep_data);
24217c478bd9Sstevel@tonic-gate 				epp->ep_data = NULL;
24227c478bd9Sstevel@tonic-gate 			}
24237c478bd9Sstevel@tonic-gate 			bp->b_resid = bp->b_bcount - len;
24247c478bd9Sstevel@tonic-gate 		} else {
24257c478bd9Sstevel@tonic-gate 			bp->b_resid = bp->b_bcount;
24267c478bd9Sstevel@tonic-gate 		}
24277c478bd9Sstevel@tonic-gate 
24287c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
24297c478bd9Sstevel@tonic-gate 	}
24307c478bd9Sstevel@tonic-gate 
24317c478bd9Sstevel@tonic-gate 	/* discard old data if any */
24327c478bd9Sstevel@tonic-gate 	if (epp->ep_data) {
24337c478bd9Sstevel@tonic-gate 		freemsg(epp->ep_data);
24347c478bd9Sstevel@tonic-gate 		epp->ep_data = NULL;
24357c478bd9Sstevel@tonic-gate 	}
24367c478bd9Sstevel@tonic-gate 
24377c478bd9Sstevel@tonic-gate 	/* allocate and initialize request */
24387c478bd9Sstevel@tonic-gate 	wLength = (setup[7] << 8) | setup[6];
24397c478bd9Sstevel@tonic-gate 	reqp = usb_alloc_ctrl_req(ugenp->ug_dip, wLength, USB_FLAGS_NOSLEEP);
24407c478bd9Sstevel@tonic-gate 	if (reqp == NULL) {
24417c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_NO_RESOURCES;
24427c478bd9Sstevel@tonic-gate 
24437c478bd9Sstevel@tonic-gate 		return (USB_NO_RESOURCES);
24447c478bd9Sstevel@tonic-gate 	}
24457c478bd9Sstevel@tonic-gate 
24467c478bd9Sstevel@tonic-gate 	/* assume an LE data stream */
24477c478bd9Sstevel@tonic-gate 	reqp->ctrl_bmRequestType = setup[0];
24487c478bd9Sstevel@tonic-gate 	reqp->ctrl_bRequest	= setup[1];
24497c478bd9Sstevel@tonic-gate 	reqp->ctrl_wValue	= (setup[3] << 8) | setup[2];
24507c478bd9Sstevel@tonic-gate 	reqp->ctrl_wIndex	= (setup[5] << 8) | setup[4];
24517c478bd9Sstevel@tonic-gate 	reqp->ctrl_wLength	= wLength;
24527c478bd9Sstevel@tonic-gate 	reqp->ctrl_timeout	= ugen_ctrl_timeout;
24537c478bd9Sstevel@tonic-gate 	reqp->ctrl_attributes	= USB_ATTRS_AUTOCLEARING |
2454*0a05e705Slc 	    USB_ATTRS_SHORT_XFER_OK;
24557c478bd9Sstevel@tonic-gate 	reqp->ctrl_cb		= ugen_epx_ctrl_req_cb;
24567c478bd9Sstevel@tonic-gate 	reqp->ctrl_exc_cb	= ugen_epx_ctrl_req_cb;
24577c478bd9Sstevel@tonic-gate 	reqp->ctrl_client_private = (usb_opaque_t)ugenp;
24587c478bd9Sstevel@tonic-gate 
24597c478bd9Sstevel@tonic-gate 	/*
24607c478bd9Sstevel@tonic-gate 	 * is this a legal request? No accesses to device are
24617c478bd9Sstevel@tonic-gate 	 * allowed if we don't own the device
24627c478bd9Sstevel@tonic-gate 	 */
24637c478bd9Sstevel@tonic-gate 	if (((reqp->ctrl_bmRequestType & USB_DEV_REQ_RCPT_MASK) ==
24647c478bd9Sstevel@tonic-gate 	    USB_DEV_REQ_RCPT_DEV) &&
24657c478bd9Sstevel@tonic-gate 	    (((reqp->ctrl_bmRequestType & USB_DEV_REQ_DIR_MASK) ==
24667c478bd9Sstevel@tonic-gate 	    USB_DEV_REQ_HOST_TO_DEV) &&
24677c478bd9Sstevel@tonic-gate 	    (usb_owns_device(ugenp->ug_dip) == B_FALSE))) {
24687c478bd9Sstevel@tonic-gate 		rval = USB_INVALID_PERM;
24697c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
24707c478bd9Sstevel@tonic-gate 
24717c478bd9Sstevel@tonic-gate 		goto fail;
24727c478bd9Sstevel@tonic-gate 	}
24737c478bd9Sstevel@tonic-gate 
24747c478bd9Sstevel@tonic-gate 	/* filter out set_cfg and set_if standard requests */
24757c478bd9Sstevel@tonic-gate 	if ((reqp->ctrl_bmRequestType & USB_DEV_REQ_TYPE_MASK) ==
24767c478bd9Sstevel@tonic-gate 	    USB_DEV_REQ_TYPE_STANDARD) {
24777c478bd9Sstevel@tonic-gate 		switch (reqp->ctrl_bRequest) {
24787c478bd9Sstevel@tonic-gate 		case USB_REQ_SET_CFG:
24797c478bd9Sstevel@tonic-gate 		case USB_REQ_SET_IF:
24807c478bd9Sstevel@tonic-gate 			rval = USB_INVALID_REQUEST;
24817c478bd9Sstevel@tonic-gate 			epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
24827c478bd9Sstevel@tonic-gate 
24837c478bd9Sstevel@tonic-gate 			goto fail;
24847c478bd9Sstevel@tonic-gate 		default:
24857c478bd9Sstevel@tonic-gate 
24867c478bd9Sstevel@tonic-gate 			break;
24877c478bd9Sstevel@tonic-gate 		}
24887c478bd9Sstevel@tonic-gate 	}
24897c478bd9Sstevel@tonic-gate 
24907c478bd9Sstevel@tonic-gate 	/* is this from host to device? */
24917c478bd9Sstevel@tonic-gate 	if (((reqp->ctrl_bmRequestType & USB_DEV_REQ_DIR_MASK) ==
24927c478bd9Sstevel@tonic-gate 	    USB_DEV_REQ_HOST_TO_DEV) && reqp->ctrl_wLength) {
24937c478bd9Sstevel@tonic-gate 		if (((bp->b_bcount - UGEN_SETUP_PKT_SIZE) - wLength) != 0) {
24947c478bd9Sstevel@tonic-gate 			rval = USB_INVALID_REQUEST;
24957c478bd9Sstevel@tonic-gate 			epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
24967c478bd9Sstevel@tonic-gate 
24977c478bd9Sstevel@tonic-gate 			goto fail;
24987c478bd9Sstevel@tonic-gate 		}
24997c478bd9Sstevel@tonic-gate 		bcopy(bp->b_un.b_addr + UGEN_SETUP_PKT_SIZE,
25007c478bd9Sstevel@tonic-gate 		    reqp->ctrl_data->b_wptr, wLength);
25017c478bd9Sstevel@tonic-gate 		reqp->ctrl_data->b_wptr += wLength;
25027c478bd9Sstevel@tonic-gate 	} else	if ((reqp->ctrl_bmRequestType & USB_DEV_REQ_DIR_MASK) ==
25037c478bd9Sstevel@tonic-gate 	    USB_DEV_REQ_DEV_TO_HOST) {
25047c478bd9Sstevel@tonic-gate 		if (bp->b_bcount != UGEN_SETUP_PKT_SIZE) {
25057c478bd9Sstevel@tonic-gate 			rval = USB_INVALID_REQUEST;
25067c478bd9Sstevel@tonic-gate 			epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
25077c478bd9Sstevel@tonic-gate 
25087c478bd9Sstevel@tonic-gate 			goto fail;
25097c478bd9Sstevel@tonic-gate 		}
25107c478bd9Sstevel@tonic-gate 	}
25117c478bd9Sstevel@tonic-gate 
25127c478bd9Sstevel@tonic-gate 	/* submit the request */
25137c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
25147c478bd9Sstevel@tonic-gate 	rval = usb_pipe_ctrl_xfer(epp->ep_ph, reqp, USB_FLAGS_NOSLEEP);
25157c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
25167c478bd9Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
25177c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status =
25187c478bd9Sstevel@tonic-gate 		    ugen_cr2lcstat(reqp->ctrl_completion_reason);
25197c478bd9Sstevel@tonic-gate 
25207c478bd9Sstevel@tonic-gate 		goto fail;
25217c478bd9Sstevel@tonic-gate 	}
25227c478bd9Sstevel@tonic-gate done:
25237c478bd9Sstevel@tonic-gate 	*wait = B_TRUE;
25247c478bd9Sstevel@tonic-gate 
25257c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
25267c478bd9Sstevel@tonic-gate fail:
25277c478bd9Sstevel@tonic-gate 	*wait = B_FALSE;
25287c478bd9Sstevel@tonic-gate 
25297c478bd9Sstevel@tonic-gate 	usb_free_ctrl_req(reqp);
25307c478bd9Sstevel@tonic-gate 
25317c478bd9Sstevel@tonic-gate 	return (rval);
25327c478bd9Sstevel@tonic-gate }
25337c478bd9Sstevel@tonic-gate 
25347c478bd9Sstevel@tonic-gate 
25357c478bd9Sstevel@tonic-gate /*
25367c478bd9Sstevel@tonic-gate  * callback for control requests, normal and exception completion
25377c478bd9Sstevel@tonic-gate  */
25387c478bd9Sstevel@tonic-gate static void
25397c478bd9Sstevel@tonic-gate ugen_epx_ctrl_req_cb(usb_pipe_handle_t ph, usb_ctrl_req_t *reqp)
25407c478bd9Sstevel@tonic-gate {
25417c478bd9Sstevel@tonic-gate 	ugen_state_t *ugenp = (ugen_state_t *)reqp->ctrl_client_private;
25427c478bd9Sstevel@tonic-gate 	ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
25437c478bd9Sstevel@tonic-gate 
25447c478bd9Sstevel@tonic-gate 	if (epp == NULL) {
25457c478bd9Sstevel@tonic-gate 		epp = &ugenp->ug_ep[0];
25467c478bd9Sstevel@tonic-gate 	}
25477c478bd9Sstevel@tonic-gate 
25487c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
25497c478bd9Sstevel@tonic-gate 
25507c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
25517c478bd9Sstevel@tonic-gate 	    "ugen_epx_ctrl_req_cb:\n\t"
25527c478bd9Sstevel@tonic-gate 	    "epp=0x%p state=0x%x ph=0x%p reqp=0x%p cr=%d cb=0x%x",
25537c478bd9Sstevel@tonic-gate 	    epp, epp->ep_state, ph, reqp, reqp->ctrl_completion_reason,
25547c478bd9Sstevel@tonic-gate 	    reqp->ctrl_cb_flags);
25557c478bd9Sstevel@tonic-gate 
25567c478bd9Sstevel@tonic-gate 	ASSERT((reqp->ctrl_cb_flags & USB_CB_INTR_CONTEXT) == 0);
25577c478bd9Sstevel@tonic-gate 
25587c478bd9Sstevel@tonic-gate 	/* save any data for the next read */
25597c478bd9Sstevel@tonic-gate 	switch (reqp->ctrl_completion_reason) {
25607c478bd9Sstevel@tonic-gate 	case USB_CR_OK:
25617c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
25627c478bd9Sstevel@tonic-gate 
25637c478bd9Sstevel@tonic-gate 		break;
25647c478bd9Sstevel@tonic-gate 	case USB_CR_PIPE_RESET:
25657c478bd9Sstevel@tonic-gate 
25667c478bd9Sstevel@tonic-gate 		break;
25677c478bd9Sstevel@tonic-gate 	default:
25687c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status =
25697c478bd9Sstevel@tonic-gate 		    ugen_cr2lcstat(reqp->ctrl_completion_reason);
25707c478bd9Sstevel@tonic-gate 		if (epp->ep_bp) {
25717c478bd9Sstevel@tonic-gate 			bioerror(epp->ep_bp, EIO);
25727c478bd9Sstevel@tonic-gate 		}
25737c478bd9Sstevel@tonic-gate 
25747c478bd9Sstevel@tonic-gate 		break;
25757c478bd9Sstevel@tonic-gate 	}
25767c478bd9Sstevel@tonic-gate 
25777c478bd9Sstevel@tonic-gate 	if (reqp->ctrl_data) {
25787c478bd9Sstevel@tonic-gate 		ASSERT(epp->ep_data == NULL);
25797c478bd9Sstevel@tonic-gate 		epp->ep_data = reqp->ctrl_data;
25807c478bd9Sstevel@tonic-gate 		reqp->ctrl_data = NULL;
25817c478bd9Sstevel@tonic-gate 	}
25827c478bd9Sstevel@tonic-gate 	epp->ep_done++;
25837c478bd9Sstevel@tonic-gate 	cv_signal(&epp->ep_wait_cv);
25847c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
25857c478bd9Sstevel@tonic-gate 
25867c478bd9Sstevel@tonic-gate 	usb_free_ctrl_req(reqp);
25877c478bd9Sstevel@tonic-gate 
25887c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
25897c478bd9Sstevel@tonic-gate 	    "ugen_epx_ctrl_req_cb: done");
25907c478bd9Sstevel@tonic-gate }
25917c478bd9Sstevel@tonic-gate 
25927c478bd9Sstevel@tonic-gate 
25937c478bd9Sstevel@tonic-gate /*
25947c478bd9Sstevel@tonic-gate  * handle bulk xfers
25957c478bd9Sstevel@tonic-gate  */
25967c478bd9Sstevel@tonic-gate static int
25977c478bd9Sstevel@tonic-gate ugen_epx_bulk_req(ugen_state_t *ugenp, ugen_ep_t *epp,
25987c478bd9Sstevel@tonic-gate     struct buf *bp, boolean_t *wait)
25997c478bd9Sstevel@tonic-gate {
26007c478bd9Sstevel@tonic-gate 	int		rval;
26017c478bd9Sstevel@tonic-gate 	usb_bulk_req_t	*reqp = usb_alloc_bulk_req(ugenp->ug_dip,
2602*0a05e705Slc 	    bp->b_bcount, USB_FLAGS_NOSLEEP);
26037c478bd9Sstevel@tonic-gate 
26047c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
26057c478bd9Sstevel@tonic-gate 	    "ugen_epx_bulk_req: epp=0x%p state=0x%x bp=0x%p",
26067c478bd9Sstevel@tonic-gate 	    epp, epp->ep_state, bp);
26077c478bd9Sstevel@tonic-gate 
26087c478bd9Sstevel@tonic-gate 	if (reqp == NULL) {
26097c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_NO_RESOURCES;
26107c478bd9Sstevel@tonic-gate 
26117c478bd9Sstevel@tonic-gate 		return (USB_NO_RESOURCES);
26127c478bd9Sstevel@tonic-gate 	}
26137c478bd9Sstevel@tonic-gate 
26147c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_XS_OPEN);
26157c478bd9Sstevel@tonic-gate 
26167c478bd9Sstevel@tonic-gate 	/*
26177c478bd9Sstevel@tonic-gate 	 * the transfer count is limited in minphys with what the HCD can
26187c478bd9Sstevel@tonic-gate 	 * do
26197c478bd9Sstevel@tonic-gate 	 */
26207c478bd9Sstevel@tonic-gate 	reqp->bulk_len		= bp->b_bcount;
26217c478bd9Sstevel@tonic-gate 	reqp->bulk_timeout	= ugen_bulk_timeout;
26227c478bd9Sstevel@tonic-gate 	reqp->bulk_client_private = (usb_opaque_t)ugenp;
26237c478bd9Sstevel@tonic-gate 	reqp->bulk_attributes	= USB_ATTRS_AUTOCLEARING;
26247c478bd9Sstevel@tonic-gate 	reqp->bulk_cb		= ugen_epx_bulk_req_cb;
26257c478bd9Sstevel@tonic-gate 	reqp->bulk_exc_cb	= ugen_epx_bulk_req_cb;
26267c478bd9Sstevel@tonic-gate 
26277c478bd9Sstevel@tonic-gate 	/* copy data into bp for OUT pipes */
26287c478bd9Sstevel@tonic-gate 	if ((UGEN_XFER_DIR(epp) & USB_EP_DIR_IN) == 0) {
26297c478bd9Sstevel@tonic-gate 		bcopy(epp->ep_bp->b_un.b_addr, reqp->bulk_data->b_rptr,
2630*0a05e705Slc 		    bp->b_bcount);
26317c478bd9Sstevel@tonic-gate 		reqp->bulk_data->b_wptr += bp->b_bcount;
26327c478bd9Sstevel@tonic-gate 	} else {
26337c478bd9Sstevel@tonic-gate 		reqp->bulk_attributes |= USB_ATTRS_SHORT_XFER_OK;
26347c478bd9Sstevel@tonic-gate 	}
26357c478bd9Sstevel@tonic-gate 
26367c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
26377c478bd9Sstevel@tonic-gate 	if ((rval = usb_pipe_bulk_xfer(epp->ep_ph, reqp,
26387c478bd9Sstevel@tonic-gate 	    USB_FLAGS_NOSLEEP)) != USB_SUCCESS) {
26397c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
26407c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status =
26417c478bd9Sstevel@tonic-gate 		    ugen_cr2lcstat(reqp->bulk_completion_reason);
26427c478bd9Sstevel@tonic-gate 		usb_free_bulk_req(reqp);
26437c478bd9Sstevel@tonic-gate 		bioerror(bp, EIO);
26447c478bd9Sstevel@tonic-gate 	} else {
26457c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
26467c478bd9Sstevel@tonic-gate 	}
26477c478bd9Sstevel@tonic-gate 	*wait = (rval == USB_SUCCESS) ? B_TRUE : B_FALSE;
26487c478bd9Sstevel@tonic-gate 
26497c478bd9Sstevel@tonic-gate 	return (rval);
26507c478bd9Sstevel@tonic-gate }
26517c478bd9Sstevel@tonic-gate 
26527c478bd9Sstevel@tonic-gate 
26537c478bd9Sstevel@tonic-gate /*
26547c478bd9Sstevel@tonic-gate  * normal and exception bulk request callback
26557c478bd9Sstevel@tonic-gate  */
26567c478bd9Sstevel@tonic-gate static void
26577c478bd9Sstevel@tonic-gate ugen_epx_bulk_req_cb(usb_pipe_handle_t ph, usb_bulk_req_t *reqp)
26587c478bd9Sstevel@tonic-gate {
26597c478bd9Sstevel@tonic-gate 	ugen_state_t *ugenp = (ugen_state_t *)reqp->bulk_client_private;
26607c478bd9Sstevel@tonic-gate 	ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
26617c478bd9Sstevel@tonic-gate 
26627c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
26637c478bd9Sstevel@tonic-gate 	    "ugen_epx_bulk_req_cb: ph=0x%p reqp=0x%p cr=%d cb=0x%x",
26647c478bd9Sstevel@tonic-gate 	    ph, reqp, reqp->bulk_completion_reason, reqp->bulk_cb_flags);
26657c478bd9Sstevel@tonic-gate 
26667c478bd9Sstevel@tonic-gate 	ASSERT((reqp->bulk_cb_flags & USB_CB_INTR_CONTEXT) == 0);
26677c478bd9Sstevel@tonic-gate 
26687c478bd9Sstevel@tonic-gate 	/* epp might be NULL if we are closing the pipe */
26697c478bd9Sstevel@tonic-gate 	if (epp) {
26707c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
26717c478bd9Sstevel@tonic-gate 		if (epp->ep_bp && reqp->bulk_data) {
26727c478bd9Sstevel@tonic-gate 			int len = min(reqp->bulk_data->b_wptr -
2673*0a05e705Slc 			    reqp->bulk_data->b_rptr,
2674*0a05e705Slc 			    epp->ep_bp->b_bcount);
26757c478bd9Sstevel@tonic-gate 			if (UGEN_XFER_DIR(epp) & USB_EP_DIR_IN) {
26767c478bd9Sstevel@tonic-gate 				if (len) {
26777c478bd9Sstevel@tonic-gate 					bcopy(reqp->bulk_data->b_rptr,
26787c478bd9Sstevel@tonic-gate 					    epp->ep_bp->b_un.b_addr, len);
26797c478bd9Sstevel@tonic-gate 					epp->ep_bp->b_resid =
26807c478bd9Sstevel@tonic-gate 					    epp->ep_bp->b_bcount - len;
26817c478bd9Sstevel@tonic-gate 				}
26827c478bd9Sstevel@tonic-gate 			} else {
26837c478bd9Sstevel@tonic-gate 				epp->ep_bp->b_resid =
2684*0a05e705Slc 				    epp->ep_bp->b_bcount - len;
26857c478bd9Sstevel@tonic-gate 			}
26867c478bd9Sstevel@tonic-gate 		}
26877c478bd9Sstevel@tonic-gate 		switch (reqp->bulk_completion_reason) {
26887c478bd9Sstevel@tonic-gate 		case USB_CR_OK:
26897c478bd9Sstevel@tonic-gate 			epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
26907c478bd9Sstevel@tonic-gate 
26917c478bd9Sstevel@tonic-gate 			break;
26927c478bd9Sstevel@tonic-gate 		case USB_CR_PIPE_RESET:
26937c478bd9Sstevel@tonic-gate 
26947c478bd9Sstevel@tonic-gate 			break;
26957c478bd9Sstevel@tonic-gate 		default:
26967c478bd9Sstevel@tonic-gate 			epp->ep_lcmd_status =
26977c478bd9Sstevel@tonic-gate 			    ugen_cr2lcstat(reqp->bulk_completion_reason);
26987c478bd9Sstevel@tonic-gate 			if (epp->ep_bp) {
26997c478bd9Sstevel@tonic-gate 				bioerror(epp->ep_bp, EIO);
27007c478bd9Sstevel@tonic-gate 			}
27017c478bd9Sstevel@tonic-gate 		}
27027c478bd9Sstevel@tonic-gate 		epp->ep_done++;
27037c478bd9Sstevel@tonic-gate 		cv_signal(&epp->ep_wait_cv);
27047c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
27057c478bd9Sstevel@tonic-gate 	}
27067c478bd9Sstevel@tonic-gate 
27077c478bd9Sstevel@tonic-gate 	usb_free_bulk_req(reqp);
27087c478bd9Sstevel@tonic-gate }
27097c478bd9Sstevel@tonic-gate 
27107c478bd9Sstevel@tonic-gate 
27117c478bd9Sstevel@tonic-gate /*
27127c478bd9Sstevel@tonic-gate  * handle intr IN xfers
27137c478bd9Sstevel@tonic-gate  */
27147c478bd9Sstevel@tonic-gate static int
27157c478bd9Sstevel@tonic-gate ugen_epx_intr_IN_req(ugen_state_t *ugenp, ugen_ep_t *epp,
27167c478bd9Sstevel@tonic-gate     struct buf *bp, boolean_t *wait)
27177c478bd9Sstevel@tonic-gate {
27187c478bd9Sstevel@tonic-gate 	int	len = 0;
27197c478bd9Sstevel@tonic-gate 	int	rval = USB_SUCCESS;
27207c478bd9Sstevel@tonic-gate 
27217c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
27227c478bd9Sstevel@tonic-gate 	    "ugen_epx_intr_IN_req: epp=0x%p state=0x%x bp=0x%p",
27237c478bd9Sstevel@tonic-gate 	    epp, epp->ep_state, bp);
27247c478bd9Sstevel@tonic-gate 
27257c478bd9Sstevel@tonic-gate 	*wait = B_FALSE;
27267c478bd9Sstevel@tonic-gate 
27277c478bd9Sstevel@tonic-gate 	/* can we satisfy this read? */
27287c478bd9Sstevel@tonic-gate 	if (epp->ep_data) {
27297c478bd9Sstevel@tonic-gate 		len = min(epp->ep_data->b_wptr - epp->ep_data->b_rptr,
2730*0a05e705Slc 		    bp->b_bcount);
27317c478bd9Sstevel@tonic-gate 	}
27327c478bd9Sstevel@tonic-gate 
27337c478bd9Sstevel@tonic-gate 	/*
27347c478bd9Sstevel@tonic-gate 	 * if polling not active, restart, and return failure
27357c478bd9Sstevel@tonic-gate 	 * immediately unless one xfer mode has been requested
27367c478bd9Sstevel@tonic-gate 	 * if there is some data, return a short read
27377c478bd9Sstevel@tonic-gate 	 */
27387c478bd9Sstevel@tonic-gate 	if ((epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_ON) == 0) {
27397c478bd9Sstevel@tonic-gate 		if (len == 0) {
27407c478bd9Sstevel@tonic-gate 			if (!epp->ep_one_xfer) {
27417c478bd9Sstevel@tonic-gate 				rval = USB_FAILURE;
27427c478bd9Sstevel@tonic-gate 				if (epp->ep_lcmd_status ==
27437c478bd9Sstevel@tonic-gate 				    USB_LC_STAT_NOERROR) {
27447c478bd9Sstevel@tonic-gate 					epp->ep_lcmd_status =
2745*0a05e705Slc 					    USB_LC_STAT_INTR_BUF_FULL;
27467c478bd9Sstevel@tonic-gate 				}
27477c478bd9Sstevel@tonic-gate 			}
27487c478bd9Sstevel@tonic-gate 			if (ugen_epx_intr_IN_start_polling(ugenp,
27497c478bd9Sstevel@tonic-gate 			    epp) != USB_SUCCESS) {
27507c478bd9Sstevel@tonic-gate 				epp->ep_lcmd_status =
27517c478bd9Sstevel@tonic-gate 				    USB_LC_STAT_INTR_POLLING_FAILED;
27527c478bd9Sstevel@tonic-gate 			}
27537c478bd9Sstevel@tonic-gate 			if (epp->ep_one_xfer) {
27547c478bd9Sstevel@tonic-gate 				*wait = B_TRUE;
27557c478bd9Sstevel@tonic-gate 			}
27567c478bd9Sstevel@tonic-gate 			goto done;
27577c478bd9Sstevel@tonic-gate 		} else if (epp->ep_data && (len < bp->b_bcount)) {
27587c478bd9Sstevel@tonic-gate 			bcopy(epp->ep_data->b_rptr, bp->b_un.b_addr, len);
27597c478bd9Sstevel@tonic-gate 			bp->b_resid = bp->b_bcount - len;
27607c478bd9Sstevel@tonic-gate 			epp->ep_data->b_rptr += len;
27617c478bd9Sstevel@tonic-gate 
27627c478bd9Sstevel@tonic-gate 			goto done;
27637c478bd9Sstevel@tonic-gate 		}
27647c478bd9Sstevel@tonic-gate 	}
27657c478bd9Sstevel@tonic-gate 
27667c478bd9Sstevel@tonic-gate 	/*
27677c478bd9Sstevel@tonic-gate 	 * if there is data or FNDELAY, return available data
27687c478bd9Sstevel@tonic-gate 	 */
27697c478bd9Sstevel@tonic-gate 	if ((len >= bp->b_bcount) ||
27707c478bd9Sstevel@tonic-gate 	    (epp->ep_xfer_oflag & (FNDELAY | FNONBLOCK))) {
27717c478bd9Sstevel@tonic-gate 		if (epp->ep_data) {
27727c478bd9Sstevel@tonic-gate 			bcopy(epp->ep_data->b_rptr, bp->b_un.b_addr, len);
27737c478bd9Sstevel@tonic-gate 			epp->ep_data->b_rptr += len;
27747c478bd9Sstevel@tonic-gate 			bp->b_resid = bp->b_bcount - len;
27757c478bd9Sstevel@tonic-gate 		} else {
27767c478bd9Sstevel@tonic-gate 			bp->b_resid = bp->b_bcount;
27777c478bd9Sstevel@tonic-gate 		}
27787c478bd9Sstevel@tonic-gate 	} else {
27797c478bd9Sstevel@tonic-gate 		/* otherwise just wait for data */
27807c478bd9Sstevel@tonic-gate 		*wait = B_TRUE;
27817c478bd9Sstevel@tonic-gate 	}
27827c478bd9Sstevel@tonic-gate 
27837c478bd9Sstevel@tonic-gate done:
27847c478bd9Sstevel@tonic-gate 	if (epp->ep_data && (epp->ep_data->b_rptr == epp->ep_data->b_wptr)) {
27857c478bd9Sstevel@tonic-gate 		freemsg(epp->ep_data);
27867c478bd9Sstevel@tonic-gate 		epp->ep_data = NULL;
27877c478bd9Sstevel@tonic-gate 	}
27887c478bd9Sstevel@tonic-gate 
27897c478bd9Sstevel@tonic-gate 	if (*wait) {
27907c478bd9Sstevel@tonic-gate 		ASSERT(epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_ON);
27917c478bd9Sstevel@tonic-gate 	}
27927c478bd9Sstevel@tonic-gate 
27937c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
27947c478bd9Sstevel@tonic-gate 	    "ugen_epx_intr_IN_req end: rval=%d bcount=%lu len=%d data=0x%p",
27957c478bd9Sstevel@tonic-gate 	    rval, bp->b_bcount, len, epp->ep_data);
27967c478bd9Sstevel@tonic-gate 
27977c478bd9Sstevel@tonic-gate 	return (rval);
27987c478bd9Sstevel@tonic-gate }
27997c478bd9Sstevel@tonic-gate 
28007c478bd9Sstevel@tonic-gate 
28017c478bd9Sstevel@tonic-gate /*
28027c478bd9Sstevel@tonic-gate  * Start polling on interrupt endpoint, synchronously
28037c478bd9Sstevel@tonic-gate  */
28047c478bd9Sstevel@tonic-gate static int
28057c478bd9Sstevel@tonic-gate ugen_epx_intr_IN_start_polling(ugen_state_t *ugenp, ugen_ep_t *epp)
28067c478bd9Sstevel@tonic-gate {
28077c478bd9Sstevel@tonic-gate 	int rval = USB_FAILURE;
28087c478bd9Sstevel@tonic-gate 	usb_intr_req_t	*reqp;
28097c478bd9Sstevel@tonic-gate 	usb_flags_t uflag;
28107c478bd9Sstevel@tonic-gate 
28117c478bd9Sstevel@tonic-gate 	/*
28127c478bd9Sstevel@tonic-gate 	 * if polling is being stopped, we restart polling in the
28137c478bd9Sstevel@tonic-gate 	 * interrrupt callback again
28147c478bd9Sstevel@tonic-gate 	 */
28157c478bd9Sstevel@tonic-gate 	if (epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_IS_STOPPED) {
28167c478bd9Sstevel@tonic-gate 
28177c478bd9Sstevel@tonic-gate 		return (rval);
28187c478bd9Sstevel@tonic-gate 	}
28197c478bd9Sstevel@tonic-gate 	if ((epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_ON) == 0) {
28207c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
28217c478bd9Sstevel@tonic-gate 		    "ugen_epx_intr_IN_start_polling: epp=0x%p state=0x%x",
28227c478bd9Sstevel@tonic-gate 		    epp, epp->ep_state);
28237c478bd9Sstevel@tonic-gate 
28247c478bd9Sstevel@tonic-gate 		epp->ep_state |= UGEN_EP_STATE_INTR_IN_POLLING_ON;
28257c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
28267c478bd9Sstevel@tonic-gate 
28277c478bd9Sstevel@tonic-gate 		reqp = usb_alloc_intr_req(ugenp->ug_dip, 0,
2828*0a05e705Slc 		    USB_FLAGS_SLEEP);
28297c478bd9Sstevel@tonic-gate 		reqp->intr_client_private = (usb_opaque_t)ugenp;
28307c478bd9Sstevel@tonic-gate 
28317c478bd9Sstevel@tonic-gate 		reqp->intr_attributes	= USB_ATTRS_AUTOCLEARING |
2832*0a05e705Slc 		    USB_ATTRS_SHORT_XFER_OK;
28337c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
28347c478bd9Sstevel@tonic-gate 		if (epp->ep_one_xfer) {
28357c478bd9Sstevel@tonic-gate 			reqp->intr_attributes |= USB_ATTRS_ONE_XFER;
28367c478bd9Sstevel@tonic-gate 			uflag = USB_FLAGS_NOSLEEP;
28377c478bd9Sstevel@tonic-gate 		} else {
28387c478bd9Sstevel@tonic-gate 			uflag = USB_FLAGS_SLEEP;
28397c478bd9Sstevel@tonic-gate 		}
28407c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
28417c478bd9Sstevel@tonic-gate 
28427c478bd9Sstevel@tonic-gate 		reqp->intr_len		= epp->ep_descr.wMaxPacketSize;
28437c478bd9Sstevel@tonic-gate 		reqp->intr_cb		= ugen_epx_intr_IN_req_cb;
28447c478bd9Sstevel@tonic-gate 		reqp->intr_exc_cb	= ugen_epx_intr_IN_req_cb;
28457c478bd9Sstevel@tonic-gate 
28467c478bd9Sstevel@tonic-gate 
28477c478bd9Sstevel@tonic-gate 		if ((rval = usb_pipe_intr_xfer(epp->ep_ph, reqp,
28487c478bd9Sstevel@tonic-gate 		    uflag)) != USB_SUCCESS) {
28497c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
28507c478bd9Sstevel@tonic-gate 			    "ugen_epx_intr_IN_start_polling: failed %d", rval);
28517c478bd9Sstevel@tonic-gate 			usb_free_intr_req(reqp);
28527c478bd9Sstevel@tonic-gate 		}
28537c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
28547c478bd9Sstevel@tonic-gate 		if (rval != USB_SUCCESS) {
28557c478bd9Sstevel@tonic-gate 			epp->ep_state &= ~UGEN_EP_STATE_INTR_IN_POLLING_ON;
28567c478bd9Sstevel@tonic-gate 		}
28577c478bd9Sstevel@tonic-gate 	} else {
28587c478bd9Sstevel@tonic-gate 		rval = USB_SUCCESS;
28597c478bd9Sstevel@tonic-gate 	}
28607c478bd9Sstevel@tonic-gate 
28617c478bd9Sstevel@tonic-gate 	return (rval);
28627c478bd9Sstevel@tonic-gate }
28637c478bd9Sstevel@tonic-gate 
28647c478bd9Sstevel@tonic-gate 
28657c478bd9Sstevel@tonic-gate /*
28667c478bd9Sstevel@tonic-gate  * stop polling on an interrupt endpoint, asynchronously
28677c478bd9Sstevel@tonic-gate  */
28687c478bd9Sstevel@tonic-gate static void
28697c478bd9Sstevel@tonic-gate ugen_epx_intr_IN_stop_polling(ugen_state_t *ugenp, ugen_ep_t *epp)
28707c478bd9Sstevel@tonic-gate {
28717c478bd9Sstevel@tonic-gate 	if ((epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_ON) &&
28727c478bd9Sstevel@tonic-gate 	    ((epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_IS_STOPPED) == 0)) {
28737c478bd9Sstevel@tonic-gate 
28747c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
28757c478bd9Sstevel@tonic-gate 		    "ugen_epx_intr_IN_stop_polling: epp=0x%p state=0x%x",
28767c478bd9Sstevel@tonic-gate 		    epp, epp->ep_state);
28777c478bd9Sstevel@tonic-gate 
28787c478bd9Sstevel@tonic-gate 		epp->ep_state |= UGEN_EP_STATE_INTR_IN_POLLING_IS_STOPPED;
28797c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
28807c478bd9Sstevel@tonic-gate 		usb_pipe_stop_intr_polling(epp->ep_ph, USB_FLAGS_NOSLEEP);
28817c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
28827c478bd9Sstevel@tonic-gate 	}
28837c478bd9Sstevel@tonic-gate }
28847c478bd9Sstevel@tonic-gate 
28857c478bd9Sstevel@tonic-gate 
28867c478bd9Sstevel@tonic-gate /*
28877c478bd9Sstevel@tonic-gate  * poll management
28887c478bd9Sstevel@tonic-gate  */
28897c478bd9Sstevel@tonic-gate static void
28907c478bd9Sstevel@tonic-gate ugen_epx_intr_IN_poll_wakeup(ugen_state_t *ugenp, ugen_ep_t *epp)
28917c478bd9Sstevel@tonic-gate {
28927c478bd9Sstevel@tonic-gate 	if (epp->ep_state & UGEN_EP_STATE_INTR_IN_POLL_PENDING) {
28937c478bd9Sstevel@tonic-gate 		struct pollhead *phpp = &epp->ep_pollhead;
28947c478bd9Sstevel@tonic-gate 
28957c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
28967c478bd9Sstevel@tonic-gate 		    "ugen_epx_intr_IN_poll_wakeup: state=0x%x", epp->ep_state);
28977c478bd9Sstevel@tonic-gate 
28987c478bd9Sstevel@tonic-gate 		epp->ep_state &= ~UGEN_EP_STATE_INTR_IN_POLL_PENDING;
28997c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
29007c478bd9Sstevel@tonic-gate 		pollwakeup(phpp, POLLIN);
29017c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
29027c478bd9Sstevel@tonic-gate 	}
29037c478bd9Sstevel@tonic-gate }
29047c478bd9Sstevel@tonic-gate 
29057c478bd9Sstevel@tonic-gate 
29067c478bd9Sstevel@tonic-gate /*
29077c478bd9Sstevel@tonic-gate  * callback functions for interrupt IN pipe
29087c478bd9Sstevel@tonic-gate  */
29097c478bd9Sstevel@tonic-gate static void
29107c478bd9Sstevel@tonic-gate ugen_epx_intr_IN_req_cb(usb_pipe_handle_t ph, usb_intr_req_t *reqp)
29117c478bd9Sstevel@tonic-gate {
29127c478bd9Sstevel@tonic-gate 	ugen_state_t *ugenp = (ugen_state_t *)reqp->intr_client_private;
29137c478bd9Sstevel@tonic-gate 	ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
29147c478bd9Sstevel@tonic-gate 
29157c478bd9Sstevel@tonic-gate 	if (epp == NULL) {
29167c478bd9Sstevel@tonic-gate 		/* pipe is closing */
29177c478bd9Sstevel@tonic-gate 
29187c478bd9Sstevel@tonic-gate 		goto done;
29197c478bd9Sstevel@tonic-gate 	}
29207c478bd9Sstevel@tonic-gate 
29217c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
29227c478bd9Sstevel@tonic-gate 
29237c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
29247c478bd9Sstevel@tonic-gate 	    "ugen_epx_intr_IN_req_cb:\n\t"
29257c478bd9Sstevel@tonic-gate 	    "epp=0x%p state=0x%x ph=0x%p reqp=0x%p cr=%d cb=0x%x len=%d",
29267c478bd9Sstevel@tonic-gate 	    epp, epp->ep_state, ph, reqp, reqp->intr_completion_reason,
29277c478bd9Sstevel@tonic-gate 	    reqp->intr_cb_flags,
29287c478bd9Sstevel@tonic-gate 	    (reqp->intr_data == NULL) ? 0 :
29297c478bd9Sstevel@tonic-gate 	    reqp->intr_data->b_wptr - reqp->intr_data->b_rptr);
29307c478bd9Sstevel@tonic-gate 
29317c478bd9Sstevel@tonic-gate 	ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
29327c478bd9Sstevel@tonic-gate 
29337c478bd9Sstevel@tonic-gate 	if (epp->ep_data && reqp->intr_data) {
29347c478bd9Sstevel@tonic-gate 		mblk_t *mp;
29357c478bd9Sstevel@tonic-gate 
29367c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2937*0a05e705Slc 		    "intr ep%x coalesce data", epp->ep_descr.bEndpointAddress);
29387c478bd9Sstevel@tonic-gate 
29397c478bd9Sstevel@tonic-gate 		/* coalesce the data into one mblk */
29407c478bd9Sstevel@tonic-gate 		epp->ep_data->b_cont = reqp->intr_data;
29417c478bd9Sstevel@tonic-gate 		if ((mp = msgpullup(epp->ep_data, -1)) != NULL) {
29427c478bd9Sstevel@tonic-gate 			reqp->intr_data = NULL;
29437c478bd9Sstevel@tonic-gate 			freemsg(epp->ep_data);
29447c478bd9Sstevel@tonic-gate 			epp->ep_data = mp;
29457c478bd9Sstevel@tonic-gate 		} else {
29467c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
29477c478bd9Sstevel@tonic-gate 			    "msgpullup failed, discard data");
29487c478bd9Sstevel@tonic-gate 			epp->ep_data->b_cont = NULL;
29497c478bd9Sstevel@tonic-gate 		}
29507c478bd9Sstevel@tonic-gate 	} else if (reqp->intr_data) {
29517c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
29527c478bd9Sstevel@tonic-gate 		    "setting ep_data");
29537c478bd9Sstevel@tonic-gate 
29547c478bd9Sstevel@tonic-gate 		epp->ep_data = reqp->intr_data;
29557c478bd9Sstevel@tonic-gate 		reqp->intr_data = NULL;
29567c478bd9Sstevel@tonic-gate 	}
29577c478bd9Sstevel@tonic-gate 
29587c478bd9Sstevel@tonic-gate 	switch (reqp->intr_completion_reason) {
29597c478bd9Sstevel@tonic-gate 	case USB_CR_OK:
29607c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
29617c478bd9Sstevel@tonic-gate 
29627c478bd9Sstevel@tonic-gate 		break;
29637c478bd9Sstevel@tonic-gate 	case USB_CR_PIPE_RESET:
29647c478bd9Sstevel@tonic-gate 	case USB_CR_STOPPED_POLLING:
29657c478bd9Sstevel@tonic-gate 
29667c478bd9Sstevel@tonic-gate 		break;
29677c478bd9Sstevel@tonic-gate 	default:
29687c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status =
29697c478bd9Sstevel@tonic-gate 		    ugen_cr2lcstat(reqp->intr_completion_reason);
29707c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
29717c478bd9Sstevel@tonic-gate 		    "ugen_exp_intr_cb_req: lcmd_status=0x%x",
29727c478bd9Sstevel@tonic-gate 		    epp->ep_lcmd_status);
29737c478bd9Sstevel@tonic-gate 
29747c478bd9Sstevel@tonic-gate 		break;
29757c478bd9Sstevel@tonic-gate 	}
29767c478bd9Sstevel@tonic-gate 
29777c478bd9Sstevel@tonic-gate 	/* any non-zero completion reason stops polling */
29787c478bd9Sstevel@tonic-gate 	if ((reqp->intr_completion_reason) ||
29797c478bd9Sstevel@tonic-gate 	    (epp->ep_one_xfer)) {
29807c478bd9Sstevel@tonic-gate 		epp->ep_state &= ~(UGEN_EP_STATE_INTR_IN_POLLING_ON |
2981*0a05e705Slc 		    UGEN_EP_STATE_INTR_IN_POLLING_IS_STOPPED);
29827c478bd9Sstevel@tonic-gate 	}
29837c478bd9Sstevel@tonic-gate 
29847c478bd9Sstevel@tonic-gate 	/* is there a poll pending? should we stop polling? */
29857c478bd9Sstevel@tonic-gate 	if (epp->ep_data) {
29867c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
29877c478bd9Sstevel@tonic-gate 		    "ugen_epx_intr_IN_req_cb: data len=0x%x",
29887c478bd9Sstevel@tonic-gate 		    epp->ep_data->b_wptr - epp->ep_data->b_rptr);
29897c478bd9Sstevel@tonic-gate 
29907c478bd9Sstevel@tonic-gate 		ugen_epx_intr_IN_poll_wakeup(ugenp, epp);
29917c478bd9Sstevel@tonic-gate 
29927c478bd9Sstevel@tonic-gate 		/* if there is no space left, stop polling */
29937c478bd9Sstevel@tonic-gate 		if (epp->ep_data &&
29947c478bd9Sstevel@tonic-gate 		    ((epp->ep_data->b_wptr - epp->ep_data->b_rptr) >=
29957c478bd9Sstevel@tonic-gate 		    epp->ep_buf_limit)) {
29967c478bd9Sstevel@tonic-gate 			ugen_epx_intr_IN_stop_polling(ugenp, epp);
29977c478bd9Sstevel@tonic-gate 		}
29987c478bd9Sstevel@tonic-gate 	}
29997c478bd9Sstevel@tonic-gate 
30007c478bd9Sstevel@tonic-gate 	if (reqp->intr_completion_reason && epp->ep_bp) {
30017c478bd9Sstevel@tonic-gate 		bioerror(epp->ep_bp, EIO);
30027c478bd9Sstevel@tonic-gate 		epp->ep_done++;
30037c478bd9Sstevel@tonic-gate 		cv_signal(&epp->ep_wait_cv);
30047c478bd9Sstevel@tonic-gate 
30057c478bd9Sstevel@tonic-gate 	/* can we satisfy the read now */
30067c478bd9Sstevel@tonic-gate 	} else if (epp->ep_data && epp->ep_bp &&
30077c478bd9Sstevel@tonic-gate 	    (!epp->ep_done || epp->ep_one_xfer)) {
30087c478bd9Sstevel@tonic-gate 		boolean_t wait;
30097c478bd9Sstevel@tonic-gate 
30107c478bd9Sstevel@tonic-gate 		if ((ugen_epx_intr_IN_req(ugenp, epp, epp->ep_bp, &wait) ==
30117c478bd9Sstevel@tonic-gate 		    USB_SUCCESS) && (wait == B_FALSE)) {
30127c478bd9Sstevel@tonic-gate 			epp->ep_done++;
30137c478bd9Sstevel@tonic-gate 			cv_signal(&epp->ep_wait_cv);
30147c478bd9Sstevel@tonic-gate 		}
30157c478bd9Sstevel@tonic-gate 	}
30167c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
30177c478bd9Sstevel@tonic-gate 
30187c478bd9Sstevel@tonic-gate done:
30197c478bd9Sstevel@tonic-gate 	usb_free_intr_req(reqp);
30207c478bd9Sstevel@tonic-gate }
30217c478bd9Sstevel@tonic-gate 
30227c478bd9Sstevel@tonic-gate 
30237c478bd9Sstevel@tonic-gate /*
30247c478bd9Sstevel@tonic-gate  * handle intr OUT xfers
30257c478bd9Sstevel@tonic-gate  */
30267c478bd9Sstevel@tonic-gate static int
30277c478bd9Sstevel@tonic-gate ugen_epx_intr_OUT_req(ugen_state_t *ugenp, ugen_ep_t *epp,
30287c478bd9Sstevel@tonic-gate     struct buf *bp, boolean_t *wait)
30297c478bd9Sstevel@tonic-gate {
30307c478bd9Sstevel@tonic-gate 	int	rval = USB_SUCCESS;
30317c478bd9Sstevel@tonic-gate 	usb_intr_req_t	*reqp;
30327c478bd9Sstevel@tonic-gate 
30337c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
30347c478bd9Sstevel@tonic-gate 	    "ugen_epx_intr_OUT_req: epp=0x%p state=0x%x bp=0x%p",
30357c478bd9Sstevel@tonic-gate 	    epp, epp->ep_state, bp);
30367c478bd9Sstevel@tonic-gate 
30377c478bd9Sstevel@tonic-gate 	reqp = usb_alloc_intr_req(ugenp->ug_dip, bp->b_bcount,
3038*0a05e705Slc 	    USB_FLAGS_NOSLEEP);
30397c478bd9Sstevel@tonic-gate 	if (reqp == NULL) {
30407c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_NO_RESOURCES;
30417c478bd9Sstevel@tonic-gate 
30427c478bd9Sstevel@tonic-gate 		return (USB_NO_RESOURCES);
30437c478bd9Sstevel@tonic-gate 	}
30447c478bd9Sstevel@tonic-gate 
30457c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_XS_OPEN);
30467c478bd9Sstevel@tonic-gate 
30477c478bd9Sstevel@tonic-gate 	reqp->intr_timeout	= ugen_intr_timeout;
30487c478bd9Sstevel@tonic-gate 	reqp->intr_client_private = (usb_opaque_t)ugenp;
30497c478bd9Sstevel@tonic-gate 	reqp->intr_len		= bp->b_bcount;
30507c478bd9Sstevel@tonic-gate 	reqp->intr_attributes	= USB_ATTRS_AUTOCLEARING;
30517c478bd9Sstevel@tonic-gate 	reqp->intr_cb		= ugen_epx_intr_OUT_req_cb;
30527c478bd9Sstevel@tonic-gate 	reqp->intr_exc_cb	= ugen_epx_intr_OUT_req_cb;
30537c478bd9Sstevel@tonic-gate 
30547c478bd9Sstevel@tonic-gate 	/* copy data from bp */
30557c478bd9Sstevel@tonic-gate 	bcopy(epp->ep_bp->b_un.b_addr, reqp->intr_data->b_rptr,
3056*0a05e705Slc 	    bp->b_bcount);
30577c478bd9Sstevel@tonic-gate 	reqp->intr_data->b_wptr += bp->b_bcount;
30587c478bd9Sstevel@tonic-gate 
30597c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
30607c478bd9Sstevel@tonic-gate 	if ((rval = usb_pipe_intr_xfer(epp->ep_ph, reqp,
30617c478bd9Sstevel@tonic-gate 	    USB_FLAGS_NOSLEEP)) != USB_SUCCESS) {
30627c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
30637c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status =
30647c478bd9Sstevel@tonic-gate 		    ugen_cr2lcstat(reqp->intr_completion_reason);
30657c478bd9Sstevel@tonic-gate 		usb_free_intr_req(reqp);
30667c478bd9Sstevel@tonic-gate 		bioerror(bp, EIO);
30677c478bd9Sstevel@tonic-gate 	} else {
30687c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
30697c478bd9Sstevel@tonic-gate 	}
30707c478bd9Sstevel@tonic-gate 	*wait = (rval == USB_SUCCESS) ? B_TRUE : B_FALSE;
30717c478bd9Sstevel@tonic-gate 
30727c478bd9Sstevel@tonic-gate 	return (rval);
30737c478bd9Sstevel@tonic-gate }
30747c478bd9Sstevel@tonic-gate 
30757c478bd9Sstevel@tonic-gate 
30767c478bd9Sstevel@tonic-gate /*
30777c478bd9Sstevel@tonic-gate  * callback functions for interrupt OUT pipe
30787c478bd9Sstevel@tonic-gate  */
30797c478bd9Sstevel@tonic-gate static void
30807c478bd9Sstevel@tonic-gate ugen_epx_intr_OUT_req_cb(usb_pipe_handle_t ph, usb_intr_req_t *reqp)
30817c478bd9Sstevel@tonic-gate {
30827c478bd9Sstevel@tonic-gate 	ugen_state_t *ugenp = (ugen_state_t *)reqp->intr_client_private;
30837c478bd9Sstevel@tonic-gate 	ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
30847c478bd9Sstevel@tonic-gate 
30857c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
30867c478bd9Sstevel@tonic-gate 	    "ugen_epx_intr_OUT_req_cb: ph=0x%p reqp=0x%p cr=%d cb=0x%x",
30877c478bd9Sstevel@tonic-gate 	    ph, reqp, reqp->intr_completion_reason, reqp->intr_cb_flags);
30887c478bd9Sstevel@tonic-gate 
30897c478bd9Sstevel@tonic-gate 	ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
30907c478bd9Sstevel@tonic-gate 
30917c478bd9Sstevel@tonic-gate 	/* epp might be NULL if we are closing the pipe */
30927c478bd9Sstevel@tonic-gate 	if (epp) {
30937c478bd9Sstevel@tonic-gate 		int len;
30947c478bd9Sstevel@tonic-gate 
30957c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
30967c478bd9Sstevel@tonic-gate 		if (epp->ep_bp) {
30977c478bd9Sstevel@tonic-gate 			len = min(reqp->intr_data->b_wptr -
30987c478bd9Sstevel@tonic-gate 			    reqp->intr_data->b_rptr, epp->ep_bp->b_bcount);
30997c478bd9Sstevel@tonic-gate 
31007c478bd9Sstevel@tonic-gate 			epp->ep_bp->b_resid = epp->ep_bp->b_bcount - len;
31017c478bd9Sstevel@tonic-gate 
31027c478bd9Sstevel@tonic-gate 			switch (reqp->intr_completion_reason) {
31037c478bd9Sstevel@tonic-gate 			case USB_CR_OK:
31047c478bd9Sstevel@tonic-gate 				epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
31057c478bd9Sstevel@tonic-gate 
31067c478bd9Sstevel@tonic-gate 				break;
31077c478bd9Sstevel@tonic-gate 			case USB_CR_PIPE_RESET:
31087c478bd9Sstevel@tonic-gate 
31097c478bd9Sstevel@tonic-gate 				break;
31107c478bd9Sstevel@tonic-gate 			default:
31117c478bd9Sstevel@tonic-gate 				epp->ep_lcmd_status =
31127c478bd9Sstevel@tonic-gate 				    ugen_cr2lcstat(
31137c478bd9Sstevel@tonic-gate 				    reqp->intr_completion_reason);
31147c478bd9Sstevel@tonic-gate 				bioerror(epp->ep_bp, EIO);
31157c478bd9Sstevel@tonic-gate 			}
31167c478bd9Sstevel@tonic-gate 		}
31177c478bd9Sstevel@tonic-gate 		epp->ep_done++;
31187c478bd9Sstevel@tonic-gate 		cv_signal(&epp->ep_wait_cv);
31197c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
31207c478bd9Sstevel@tonic-gate 	}
31217c478bd9Sstevel@tonic-gate 
31227c478bd9Sstevel@tonic-gate 	usb_free_intr_req(reqp);
31237c478bd9Sstevel@tonic-gate }
31247c478bd9Sstevel@tonic-gate 
31257c478bd9Sstevel@tonic-gate 
3126*0a05e705Slc /*
3127*0a05e705Slc  * handle isoc IN xfers
3128*0a05e705Slc  */
3129*0a05e705Slc static int
3130*0a05e705Slc ugen_epx_isoc_IN_req(ugen_state_t *ugenp, ugen_ep_t *epp,
3131*0a05e705Slc     struct buf *bp, boolean_t *wait)
3132*0a05e705Slc {
3133*0a05e705Slc 	int rval = USB_SUCCESS;
3134*0a05e705Slc 	ugen_isoc_pkt_descr_t *pkt_descr;
3135*0a05e705Slc 	ushort_t n_pkt;
3136*0a05e705Slc 	uint_t pkts_len, len = 0;
3137*0a05e705Slc 
3138*0a05e705Slc 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3139*0a05e705Slc 	    "ugen_epx_isoc_IN_req: epp=0x%p state=0x%x bp=0x%p",
3140*0a05e705Slc 	    epp, epp->ep_state, bp);
3141*0a05e705Slc 
3142*0a05e705Slc 	*wait = B_FALSE;
3143*0a05e705Slc 
3144*0a05e705Slc 	/* check if the isoc in pkt info has been initialized */
3145*0a05e705Slc 	pkt_descr = epp->ep_isoc_info.isoc_pkt_descr;
3146*0a05e705Slc 	n_pkt = epp->ep_isoc_info.isoc_pkts_count;
3147*0a05e705Slc 	if ((n_pkt == 0) || (pkt_descr == NULL)) {
3148*0a05e705Slc 		rval = USB_FAILURE;
3149*0a05e705Slc 		epp->ep_lcmd_status = USB_LC_STAT_ISOC_UNINITIALIZED;
3150*0a05e705Slc 
3151*0a05e705Slc 		goto done;
3152*0a05e705Slc 	}
3153*0a05e705Slc 
3154*0a05e705Slc 
3155*0a05e705Slc 	/* For OUT endpoint, return pkts transfer status of last request */
3156*0a05e705Slc 	if (UGEN_XFER_DIR(epp) != USB_EP_DIR_IN) {
3157*0a05e705Slc 		if (bp->b_bcount < sizeof (ugen_isoc_pkt_descr_t) * n_pkt) {
3158*0a05e705Slc 			rval = USB_INVALID_REQUEST;
3159*0a05e705Slc 			epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
3160*0a05e705Slc 
3161*0a05e705Slc 			return (rval);
3162*0a05e705Slc 		}
3163*0a05e705Slc 		bcopy(epp->ep_isoc_info.isoc_pkt_descr, bp->b_un.b_addr,
3164*0a05e705Slc 		    n_pkt * sizeof (ugen_isoc_pkt_descr_t));
3165*0a05e705Slc 		epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
3166*0a05e705Slc 
3167*0a05e705Slc 		return (USB_SUCCESS);
3168*0a05e705Slc 	}
3169*0a05e705Slc 
3170*0a05e705Slc 	/* read length should be the sum of pkt descrs and data length */
3171*0a05e705Slc 	pkts_len = epp->ep_isoc_info.isoc_pkts_length;
3172*0a05e705Slc 	if (bp->b_bcount != pkts_len + sizeof (ugen_isoc_pkt_descr_t) * n_pkt) {
3173*0a05e705Slc 		rval = USB_INVALID_REQUEST;
3174*0a05e705Slc 		epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
3175*0a05e705Slc 
3176*0a05e705Slc 		goto done;
3177*0a05e705Slc 	}
3178*0a05e705Slc 
3179*0a05e705Slc 	/* can we satisfy this read? */
3180*0a05e705Slc 	if (epp->ep_data) {
3181*0a05e705Slc 		len = min(epp->ep_data->b_wptr - epp->ep_data->b_rptr,
3182*0a05e705Slc 		    bp->b_bcount);
3183*0a05e705Slc 		/*
3184*0a05e705Slc 		 * every msg block in ep_data must be the size of
3185*0a05e705Slc 		 * pkts_len(payload length) + pkt descrs len
3186*0a05e705Slc 		 */
3187*0a05e705Slc 		ASSERT((len == 0) || (len == bp->b_bcount));
3188*0a05e705Slc 	}
3189*0a05e705Slc 
3190*0a05e705Slc 	/*
3191*0a05e705Slc 	 * if polling not active, restart
3192*0a05e705Slc 	 * if there is some data, return the data
3193*0a05e705Slc 	 */
3194*0a05e705Slc 	if ((epp->ep_state & UGEN_EP_STATE_ISOC_IN_POLLING_ON) == 0) {
3195*0a05e705Slc 		if (len == 0) {
3196*0a05e705Slc 			rval = USB_FAILURE;
3197*0a05e705Slc 			if ((rval = ugen_epx_isoc_IN_start_polling(ugenp,
3198*0a05e705Slc 			    epp)) != USB_SUCCESS) {
3199*0a05e705Slc 				epp->ep_lcmd_status =
3200*0a05e705Slc 				    USB_LC_STAT_ISOC_POLLING_FAILED;
3201*0a05e705Slc 			}
3202*0a05e705Slc 
3203*0a05e705Slc 			goto done;
3204*0a05e705Slc 
3205*0a05e705Slc 		} else if (epp->ep_data && (len >= bp->b_bcount)) {
3206*0a05e705Slc 			bcopy(epp->ep_data->b_rptr, bp->b_un.b_addr,
3207*0a05e705Slc 			    bp->b_bcount);
3208*0a05e705Slc 			bp->b_resid = 0;
3209*0a05e705Slc 			epp->ep_data->b_rptr += bp->b_bcount;
3210*0a05e705Slc 
3211*0a05e705Slc 			goto done;
3212*0a05e705Slc 		}
3213*0a05e705Slc 	}
3214*0a05e705Slc 
3215*0a05e705Slc 	/*
3216*0a05e705Slc 	 * if there is data or FNDELAY, return available data
3217*0a05e705Slc 	 */
3218*0a05e705Slc 	if (epp->ep_data && (len >= bp->b_bcount)) {
3219*0a05e705Slc 		/* can fulfill this read request */
3220*0a05e705Slc 		bcopy(epp->ep_data->b_rptr, bp->b_un.b_addr, bp->b_bcount);
3221*0a05e705Slc 		epp->ep_data->b_rptr += bp->b_bcount;
3222*0a05e705Slc 		bp->b_resid = 0;
3223*0a05e705Slc 	} else if (epp->ep_xfer_oflag & (FNDELAY | FNONBLOCK)) {
3224*0a05e705Slc 		bp->b_resid = bp->b_bcount;
3225*0a05e705Slc 	} else {
3226*0a05e705Slc 		/* otherwise just wait for data */
3227*0a05e705Slc 		*wait = B_TRUE;
3228*0a05e705Slc 	}
3229*0a05e705Slc 
3230*0a05e705Slc done:
3231*0a05e705Slc 	/* data have been read */
3232*0a05e705Slc 	if (epp->ep_data && (epp->ep_data->b_rptr == epp->ep_data->b_wptr)) {
3233*0a05e705Slc 		mblk_t *mp = NULL;
3234*0a05e705Slc 
3235*0a05e705Slc 		/* remove the just read msg block */
3236*0a05e705Slc 		mp = unlinkb(epp->ep_data);
3237*0a05e705Slc 		freemsg(epp->ep_data);
3238*0a05e705Slc 
3239*0a05e705Slc 		if (mp) {
3240*0a05e705Slc 			epp->ep_data = mp;
3241*0a05e705Slc 		} else {
3242*0a05e705Slc 			epp->ep_data = NULL;
3243*0a05e705Slc 		}
3244*0a05e705Slc 	}
3245*0a05e705Slc 
3246*0a05e705Slc 	if (*wait) {
3247*0a05e705Slc 		ASSERT(epp->ep_state & UGEN_EP_STATE_ISOC_IN_POLLING_ON);
3248*0a05e705Slc 	}
3249*0a05e705Slc 
3250*0a05e705Slc 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3251*0a05e705Slc 	    "ugen_epx_isoc_IN_req end: rval=%d bcount=%lu len=%d data=0x%p",
3252*0a05e705Slc 	    rval, bp->b_bcount, len, epp->ep_data);
3253*0a05e705Slc 
3254*0a05e705Slc 	return (rval);
3255*0a05e705Slc }
3256*0a05e705Slc 
3257*0a05e705Slc 
3258*0a05e705Slc /*
3259*0a05e705Slc  * Start polling on isoc endpoint, asynchronously
3260*0a05e705Slc  */
3261*0a05e705Slc static int
3262*0a05e705Slc ugen_epx_isoc_IN_start_polling(ugen_state_t *ugenp, ugen_ep_t *epp)
3263*0a05e705Slc {
3264*0a05e705Slc 	int rval = USB_FAILURE;
3265*0a05e705Slc 	usb_isoc_req_t	*reqp;
3266*0a05e705Slc 	ugen_isoc_pkt_descr_t *pkt_descr;
3267*0a05e705Slc 	ushort_t n_pkt, pkt;
3268*0a05e705Slc 	uint_t pkts_len;
3269*0a05e705Slc 
3270*0a05e705Slc 	/*
3271*0a05e705Slc 	 * if polling is being stopped, we restart polling in the
3272*0a05e705Slc 	 * isoc callback again
3273*0a05e705Slc 	 */
3274*0a05e705Slc 	if (epp->ep_state & UGEN_EP_STATE_ISOC_IN_POLLING_IS_STOPPED) {
3275*0a05e705Slc 
3276*0a05e705Slc 		return (rval);
3277*0a05e705Slc 	}
3278*0a05e705Slc 
3279*0a05e705Slc 	if ((epp->ep_state & UGEN_EP_STATE_ISOC_IN_POLLING_ON) == 0) {
3280*0a05e705Slc 		USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3281*0a05e705Slc 		    "ugen_epx_isoc_IN_start_polling: epp=0x%p state=0x%x",
3282*0a05e705Slc 		    epp, epp->ep_state);
3283*0a05e705Slc 
3284*0a05e705Slc 		pkts_len = epp->ep_isoc_info.isoc_pkts_length;
3285*0a05e705Slc 		n_pkt = epp->ep_isoc_info.isoc_pkts_count;
3286*0a05e705Slc 		pkt_descr = epp->ep_isoc_info.isoc_pkt_descr;
3287*0a05e705Slc 
3288*0a05e705Slc 		epp->ep_state |= UGEN_EP_STATE_ISOC_IN_POLLING_ON;
3289*0a05e705Slc 		mutex_exit(&epp->ep_mutex);
3290*0a05e705Slc 
3291*0a05e705Slc 		if ((reqp = usb_alloc_isoc_req(ugenp->ug_dip, n_pkt, pkts_len,
3292*0a05e705Slc 		    USB_FLAGS_NOSLEEP)) == NULL) {
3293*0a05e705Slc 			USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3294*0a05e705Slc 			    "ugen_epx_isoc_IN_start_polling: alloc isoc "
3295*0a05e705Slc 			    "req failed");
3296*0a05e705Slc 			mutex_enter(&epp->ep_mutex);
3297*0a05e705Slc 			epp->ep_state &= ~UGEN_EP_STATE_ISOC_IN_POLLING_ON;
3298*0a05e705Slc 
3299*0a05e705Slc 			return (USB_NO_RESOURCES);
3300*0a05e705Slc 		}
3301*0a05e705Slc 		reqp->isoc_client_private = (usb_opaque_t)ugenp;
3302*0a05e705Slc 
3303*0a05e705Slc 		reqp->isoc_attributes	= USB_ATTRS_AUTOCLEARING |
3304*0a05e705Slc 		    USB_ATTRS_SHORT_XFER_OK | USB_ATTRS_ISOC_XFER_ASAP;
3305*0a05e705Slc 
3306*0a05e705Slc 		/*
3307*0a05e705Slc 		 * isoc_pkts_length was defined to be ushort_t. This
3308*0a05e705Slc 		 * has been obsoleted by usb high speed isoc support.
3309*0a05e705Slc 		 * It is set here just for compatibility reason
3310*0a05e705Slc 		 */
3311*0a05e705Slc 		reqp->isoc_pkts_length = 0;
3312*0a05e705Slc 
3313*0a05e705Slc 		for (pkt = 0; pkt < n_pkt; pkt++) {
3314*0a05e705Slc 			reqp->isoc_pkt_descr[pkt].isoc_pkt_length =
3315*0a05e705Slc 			    pkt_descr[pkt].dsc_isoc_pkt_len;
3316*0a05e705Slc 		}
3317*0a05e705Slc 		reqp->isoc_pkts_count	= n_pkt;
3318*0a05e705Slc 		reqp->isoc_cb		= ugen_epx_isoc_IN_req_cb;
3319*0a05e705Slc 		reqp->isoc_exc_cb	= ugen_epx_isoc_IN_req_cb;
3320*0a05e705Slc 
3321*0a05e705Slc 		if ((rval = usb_pipe_isoc_xfer(epp->ep_ph, reqp,
3322*0a05e705Slc 		    USB_FLAGS_NOSLEEP)) != USB_SUCCESS) {
3323*0a05e705Slc 			USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3324*0a05e705Slc 			    "ugen_epx_isoc_IN_start_polling: failed %d", rval);
3325*0a05e705Slc 			usb_free_isoc_req(reqp);
3326*0a05e705Slc 		}
3327*0a05e705Slc 
3328*0a05e705Slc 		mutex_enter(&epp->ep_mutex);
3329*0a05e705Slc 		if (rval != USB_SUCCESS) {
3330*0a05e705Slc 			epp->ep_state &= ~UGEN_EP_STATE_ISOC_IN_POLLING_ON;
3331*0a05e705Slc 		}
3332*0a05e705Slc 	} else {
3333*0a05e705Slc 		rval = USB_SUCCESS;
3334*0a05e705Slc 	}
3335*0a05e705Slc 
3336*0a05e705Slc 	return (rval);
3337*0a05e705Slc }
3338*0a05e705Slc 
3339*0a05e705Slc 
3340*0a05e705Slc /*
3341*0a05e705Slc  * stop polling on an isoc endpoint, asynchronously
3342*0a05e705Slc  */
3343*0a05e705Slc static void
3344*0a05e705Slc ugen_epx_isoc_IN_stop_polling(ugen_state_t *ugenp, ugen_ep_t *epp)
3345*0a05e705Slc {
3346*0a05e705Slc 	if ((epp->ep_state & UGEN_EP_STATE_ISOC_IN_POLLING_ON) &&
3347*0a05e705Slc 	    ((epp->ep_state & UGEN_EP_STATE_ISOC_IN_POLLING_IS_STOPPED) == 0)) {
3348*0a05e705Slc 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3349*0a05e705Slc 		    "ugen_epx_isoc_IN_stop_polling: epp=0x%p state=0x%x",
3350*0a05e705Slc 		    epp, epp->ep_state);
3351*0a05e705Slc 
3352*0a05e705Slc 		epp->ep_state |= UGEN_EP_STATE_ISOC_IN_POLLING_IS_STOPPED;
3353*0a05e705Slc 		mutex_exit(&epp->ep_mutex);
3354*0a05e705Slc 		usb_pipe_stop_isoc_polling(epp->ep_ph, USB_FLAGS_NOSLEEP);
3355*0a05e705Slc 		mutex_enter(&epp->ep_mutex);
3356*0a05e705Slc 	}
3357*0a05e705Slc }
3358*0a05e705Slc 
3359*0a05e705Slc 
3360*0a05e705Slc /*
3361*0a05e705Slc  * poll management
3362*0a05e705Slc  */
3363*0a05e705Slc static void
3364*0a05e705Slc ugen_epx_isoc_IN_poll_wakeup(ugen_state_t *ugenp, ugen_ep_t *epp)
3365*0a05e705Slc {
3366*0a05e705Slc 	if (epp->ep_state & UGEN_EP_STATE_ISOC_IN_POLL_PENDING) {
3367*0a05e705Slc 		struct pollhead *phpp = &epp->ep_pollhead;
3368*0a05e705Slc 
3369*0a05e705Slc 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3370*0a05e705Slc 		    "ugen_epx_isoc_IN_poll_wakeup: state=0x%x", epp->ep_state);
3371*0a05e705Slc 
3372*0a05e705Slc 		epp->ep_state &= ~UGEN_EP_STATE_ISOC_IN_POLL_PENDING;
3373*0a05e705Slc 		mutex_exit(&epp->ep_mutex);
3374*0a05e705Slc 		pollwakeup(phpp, POLLIN);
3375*0a05e705Slc 		mutex_enter(&epp->ep_mutex);
3376*0a05e705Slc 	}
3377*0a05e705Slc }
3378*0a05e705Slc 
3379*0a05e705Slc 
3380*0a05e705Slc /*
3381*0a05e705Slc  * callback functions for isoc IN pipe
3382*0a05e705Slc  */
3383*0a05e705Slc static void
3384*0a05e705Slc ugen_epx_isoc_IN_req_cb(usb_pipe_handle_t ph, usb_isoc_req_t *reqp)
3385*0a05e705Slc {
3386*0a05e705Slc 	ugen_state_t *ugenp = (ugen_state_t *)reqp->isoc_client_private;
3387*0a05e705Slc 	ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
3388*0a05e705Slc 
3389*0a05e705Slc 	if (epp == NULL) {
3390*0a05e705Slc 		/* pipe is closing */
3391*0a05e705Slc 
3392*0a05e705Slc 		goto done;
3393*0a05e705Slc 	}
3394*0a05e705Slc 
3395*0a05e705Slc 	ASSERT(!mutex_owned(&epp->ep_mutex)); /* not owned */
3396*0a05e705Slc 
3397*0a05e705Slc 	mutex_enter(&epp->ep_mutex);
3398*0a05e705Slc 
3399*0a05e705Slc 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3400*0a05e705Slc 	    "ugen_epx_isoc_IN_req_cb: "
3401*0a05e705Slc 	    "epp=0x%p state=0x%x ph=0x%p reqp=0x%p cr=%d cb=0x%x len=%d "
3402*0a05e705Slc 	    "isoc error count=%d, pkt cnt=%d", epp, epp->ep_state, ph, reqp,
3403*0a05e705Slc 	    reqp->isoc_completion_reason, reqp->isoc_cb_flags,
3404*0a05e705Slc 	    (reqp->isoc_data == NULL) ? 0 :
3405*0a05e705Slc 	    reqp->isoc_data->b_wptr - reqp->isoc_data->b_rptr,
3406*0a05e705Slc 	    reqp->isoc_error_count, reqp->isoc_pkts_count);
3407*0a05e705Slc 
3408*0a05e705Slc 	/* Too many packet errors during isoc transfer of this request */
3409*0a05e705Slc 	if (reqp->isoc_error_count == reqp->isoc_pkts_count) {
3410*0a05e705Slc 		USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3411*0a05e705Slc 		    "too many errors(%d) in this req, stop polling",
3412*0a05e705Slc 		    reqp->isoc_error_count);
3413*0a05e705Slc 		epp->ep_lcmd_status = USB_LC_STAT_ISOC_PKT_ERROR;
3414*0a05e705Slc 		ugen_epx_isoc_IN_stop_polling(ugenp, epp);
3415*0a05e705Slc 	}
3416*0a05e705Slc 
3417*0a05e705Slc 	/* Data OK */
3418*0a05e705Slc 	if (reqp->isoc_data && !reqp->isoc_completion_reason) {
3419*0a05e705Slc 		mblk_t *mp1 = NULL, *mp2 = NULL;
3420*0a05e705Slc 		usb_isoc_pkt_descr_t *pkt_descr =
3421*0a05e705Slc 		    reqp->isoc_pkt_descr;
3422*0a05e705Slc 		ushort_t i, n_pkt = reqp->isoc_pkts_count;
3423*0a05e705Slc 
3424*0a05e705Slc 		for (i = 0; i < n_pkt; i++) {
3425*0a05e705Slc 			USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3426*0a05e705Slc 			    "pkt %d: len=%d status=%d actual_len=%d", i,
3427*0a05e705Slc 			    pkt_descr[i].isoc_pkt_length,
3428*0a05e705Slc 			    pkt_descr[i].isoc_pkt_status,
3429*0a05e705Slc 			    pkt_descr[i].isoc_pkt_actual_length);
3430*0a05e705Slc 
3431*0a05e705Slc 			/* translate cr to ugen lcstat */
3432*0a05e705Slc 			pkt_descr[i].isoc_pkt_status =
3433*0a05e705Slc 			    ugen_cr2lcstat(pkt_descr[i].isoc_pkt_status);
3434*0a05e705Slc 		}
3435*0a05e705Slc 
3436*0a05e705Slc 		/* construct data buffer: pkt descriptors + payload */
3437*0a05e705Slc 		mp2 = allocb(sizeof (ugen_isoc_pkt_descr_t) * n_pkt, BPRI_HI);
3438*0a05e705Slc 		if (mp2 == NULL) {
3439*0a05e705Slc 			USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3440*0a05e705Slc 			    "alloc msgblk failed, discard data");
3441*0a05e705Slc 		} else {
3442*0a05e705Slc 			/* pkt descrs first */
3443*0a05e705Slc 			bcopy(pkt_descr, mp2->b_wptr,
3444*0a05e705Slc 			    sizeof (ugen_isoc_pkt_descr_t) * n_pkt);
3445*0a05e705Slc 
3446*0a05e705Slc 			mp2->b_wptr += sizeof (ugen_isoc_pkt_descr_t) * n_pkt;
3447*0a05e705Slc 
3448*0a05e705Slc 			/* payload follows */
3449*0a05e705Slc 			linkb(mp2, reqp->isoc_data);
3450*0a05e705Slc 
3451*0a05e705Slc 			/* concatenate data bytes in mp2 */
3452*0a05e705Slc 			if ((mp1 = msgpullup(mp2, -1)) != NULL) {
3453*0a05e705Slc 				/*
3454*0a05e705Slc 				 * now we get the required data:
3455*0a05e705Slc 				 *	pkt descrs + payload
3456*0a05e705Slc 				 */
3457*0a05e705Slc 				reqp->isoc_data = NULL;
3458*0a05e705Slc 			} else {
3459*0a05e705Slc 				USB_DPRINTF_L2(UGEN_PRINT_XFER,
3460*0a05e705Slc 				    ugenp->ug_log_hdl,
3461*0a05e705Slc 				    "msgpullup status blk failed, "
3462*0a05e705Slc 				    "discard data");
3463*0a05e705Slc 				mp2->b_cont = NULL;
3464*0a05e705Slc 			}
3465*0a05e705Slc 
3466*0a05e705Slc 			freemsg(mp2);
3467*0a05e705Slc 			mp2 = NULL;
3468*0a05e705Slc 		}
3469*0a05e705Slc 
3470*0a05e705Slc 		if (epp->ep_data && (mp1 != NULL)) {
3471*0a05e705Slc 			USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3472*0a05e705Slc 			    "ISOC ep%x coalesce ep_data",
3473*0a05e705Slc 			    epp->ep_descr.bEndpointAddress);
3474*0a05e705Slc 
3475*0a05e705Slc 			/* add mp1 to the tail of ep_data */
3476*0a05e705Slc 			linkb(epp->ep_data, mp1);
3477*0a05e705Slc 
3478*0a05e705Slc 		} else if (mp1 != NULL) {
3479*0a05e705Slc 			USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3480*0a05e705Slc 			    "setting ep_data");
3481*0a05e705Slc 			epp->ep_data = mp1;
3482*0a05e705Slc 		}
3483*0a05e705Slc 	}
3484*0a05e705Slc 
3485*0a05e705Slc 	switch (reqp->isoc_completion_reason) {
3486*0a05e705Slc 	case USB_CR_OK:
3487*0a05e705Slc 		epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
3488*0a05e705Slc 
3489*0a05e705Slc 		break;
3490*0a05e705Slc 	case USB_CR_PIPE_RESET:
3491*0a05e705Slc 	case USB_CR_STOPPED_POLLING:
3492*0a05e705Slc 	case USB_CR_PIPE_CLOSING:
3493*0a05e705Slc 
3494*0a05e705Slc 		break;
3495*0a05e705Slc 	default:
3496*0a05e705Slc 		epp->ep_lcmd_status =
3497*0a05e705Slc 		    ugen_cr2lcstat(reqp->isoc_completion_reason);
3498*0a05e705Slc 		USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3499*0a05e705Slc 		    "ugen_exp_isoc_cb_req: error lcmd_status=0x%x ",
3500*0a05e705Slc 		    epp->ep_lcmd_status);
3501*0a05e705Slc 
3502*0a05e705Slc 		break;
3503*0a05e705Slc 	}
3504*0a05e705Slc 
3505*0a05e705Slc 	/* any non-zero completion reason signifies polling has stopped */
3506*0a05e705Slc 	if (reqp->isoc_completion_reason) {
3507*0a05e705Slc 		epp->ep_state &= ~(UGEN_EP_STATE_ISOC_IN_POLLING_ON |
3508*0a05e705Slc 		    UGEN_EP_STATE_ISOC_IN_POLLING_IS_STOPPED);
3509*0a05e705Slc 	}
3510*0a05e705Slc 
3511*0a05e705Slc 
3512*0a05e705Slc 	/* is there a poll pending? should we stop polling? */
3513*0a05e705Slc 	if (epp->ep_data) {
3514*0a05e705Slc 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3515*0a05e705Slc 		    "ugen_epx_isoc_IN_req_cb: data len=0x%x, limit=0x%x",
3516*0a05e705Slc 		    msgdsize(epp->ep_data),
3517*0a05e705Slc 		    epp->ep_buf_limit);
3518*0a05e705Slc 
3519*0a05e705Slc 		ugen_epx_isoc_IN_poll_wakeup(ugenp, epp);
3520*0a05e705Slc 
3521*0a05e705Slc 
3522*0a05e705Slc 		/*
3523*0a05e705Slc 		 * Since isoc is unreliable xfer, if buffered data size exceeds
3524*0a05e705Slc 		 * the limit, we just discard and free data in the oldest mblk
3525*0a05e705Slc 		 */
3526*0a05e705Slc 		if (epp->ep_data &&
3527*0a05e705Slc 		    (msgdsize(epp->ep_data) >= epp->ep_buf_limit)) {
3528*0a05e705Slc 			mblk_t *mp = NULL;
3529*0a05e705Slc 
3530*0a05e705Slc 			/* exceed buf lenth limit, remove the oldest one */
3531*0a05e705Slc 			USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3532*0a05e705Slc 			    "ugen_epx_isoc_IN_req_cb: overflow!");
3533*0a05e705Slc 			mp = unlinkb(epp->ep_data);
3534*0a05e705Slc 			if (epp->ep_data) {
3535*0a05e705Slc 				freeb(epp->ep_data);
3536*0a05e705Slc 			}
3537*0a05e705Slc 			epp->ep_data = mp;
3538*0a05e705Slc 		}
3539*0a05e705Slc 
3540*0a05e705Slc 	}
3541*0a05e705Slc 
3542*0a05e705Slc 	if (reqp->isoc_completion_reason && epp->ep_bp) {
3543*0a05e705Slc 		bioerror(epp->ep_bp, EIO);
3544*0a05e705Slc 		epp->ep_done++;
3545*0a05e705Slc 		cv_signal(&epp->ep_wait_cv);
3546*0a05e705Slc 
3547*0a05e705Slc 	} else if (epp->ep_data && epp->ep_bp && !epp->ep_done) {
3548*0a05e705Slc 		boolean_t wait;
3549*0a05e705Slc 
3550*0a05e705Slc 		/* can we satisfy the read now */
3551*0a05e705Slc 		if ((ugen_epx_isoc_IN_req(ugenp, epp, epp->ep_bp, &wait) ==
3552*0a05e705Slc 		    USB_SUCCESS) && (wait == B_FALSE)) {
3553*0a05e705Slc 			epp->ep_done++;
3554*0a05e705Slc 			cv_signal(&epp->ep_wait_cv);
3555*0a05e705Slc 		}
3556*0a05e705Slc 	}
3557*0a05e705Slc 	mutex_exit(&epp->ep_mutex);
3558*0a05e705Slc 
3559*0a05e705Slc done:
3560*0a05e705Slc 
3561*0a05e705Slc 	usb_free_isoc_req(reqp);
3562*0a05e705Slc }
3563*0a05e705Slc 
3564*0a05e705Slc /*
3565*0a05e705Slc  * handle isoc OUT xfers or init isoc IN polling
3566*0a05e705Slc  */
3567*0a05e705Slc static int
3568*0a05e705Slc ugen_epx_isoc_OUT_req(ugen_state_t *ugenp, ugen_ep_t *epp,
3569*0a05e705Slc     struct buf *bp, boolean_t *wait)
3570*0a05e705Slc {
3571*0a05e705Slc 	int rval = USB_SUCCESS;
3572*0a05e705Slc 	usb_isoc_req_t *reqp;
3573*0a05e705Slc 	ugen_isoc_pkt_descr_t *pkt_descr;
3574*0a05e705Slc 	ushort_t pkt, n_pkt = 0;
3575*0a05e705Slc 	uint_t pkts_len = 0;
3576*0a05e705Slc 	uint_t head_len;
3577*0a05e705Slc 	char *p;
3578*0a05e705Slc 	ugen_isoc_req_head_t *pkth;
3579*0a05e705Slc 
3580*0a05e705Slc 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3581*0a05e705Slc 	    "ugen_epx_isoc_OUT_req: epp=0x%p state=0x%x bp=0x%p",
3582*0a05e705Slc 	    epp, epp->ep_state, bp);
3583*0a05e705Slc 
3584*0a05e705Slc 	*wait = B_FALSE;
3585*0a05e705Slc 
3586*0a05e705Slc 	if (bp->b_bcount < sizeof (int)) {
3587*0a05e705Slc 		epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
3588*0a05e705Slc 		rval = USB_INVALID_REQUEST;
3589*0a05e705Slc 
3590*0a05e705Slc 		goto done;
3591*0a05e705Slc 	}
3592*0a05e705Slc 
3593*0a05e705Slc 	pkth = (ugen_isoc_req_head_t *)bp->b_un.b_addr;
3594*0a05e705Slc 	n_pkt = pkth->req_isoc_pkts_count;
3595*0a05e705Slc 	head_len = sizeof (ugen_isoc_pkt_descr_t) * n_pkt +
3596*0a05e705Slc 	    sizeof (int);
3597*0a05e705Slc 
3598*0a05e705Slc 	if ((n_pkt == 0) ||
3599*0a05e705Slc 	    (n_pkt > usb_get_max_pkts_per_isoc_request(ugenp->ug_dip)) ||
3600*0a05e705Slc 	    (bp->b_bcount < head_len)) {
3601*0a05e705Slc 		USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3602*0a05e705Slc 		    "Invalid params: bcount=%d, head_len=%d, pktcnt=%d",
3603*0a05e705Slc 		    bp->b_bcount, head_len, n_pkt);
3604*0a05e705Slc 
3605*0a05e705Slc 		epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
3606*0a05e705Slc 		rval = USB_INVALID_REQUEST;
3607*0a05e705Slc 
3608*0a05e705Slc 		goto done;
3609*0a05e705Slc 	}
3610*0a05e705Slc 
3611*0a05e705Slc 	p = bp->b_un.b_addr;
3612*0a05e705Slc 	p += sizeof (int); /* points to pkt_descrs */
3613*0a05e705Slc 
3614*0a05e705Slc 	pkt_descr = kmem_zalloc(sizeof (ugen_isoc_pkt_descr_t) * n_pkt,
3615*0a05e705Slc 	    KM_NOSLEEP);
3616*0a05e705Slc 	if (pkt_descr == NULL) {
3617*0a05e705Slc 		epp->ep_lcmd_status = USB_LC_STAT_NO_RESOURCES;
3618*0a05e705Slc 		rval = USB_NO_RESOURCES;
3619*0a05e705Slc 
3620*0a05e705Slc 		goto done;
3621*0a05e705Slc 	}
3622*0a05e705Slc 	bcopy(p, pkt_descr, sizeof (ugen_isoc_pkt_descr_t) * n_pkt);
3623*0a05e705Slc 	p += sizeof (ugen_isoc_pkt_descr_t) * n_pkt;
3624*0a05e705Slc 
3625*0a05e705Slc 	/* total packet payload length */
3626*0a05e705Slc 	for (pkt = 0; pkt < n_pkt; pkt++) {
3627*0a05e705Slc 		pkts_len += pkt_descr[pkt].dsc_isoc_pkt_len;
3628*0a05e705Slc 	}
3629*0a05e705Slc 
3630*0a05e705Slc 	/*
3631*0a05e705Slc 	 * write length may either be header length for isoc IN endpoint or
3632*0a05e705Slc 	 * the sum of header and data pkts length for isoc OUT endpoint
3633*0a05e705Slc 	 */
3634*0a05e705Slc 	if (((bp->b_bcount != head_len) &&
3635*0a05e705Slc 	    (bp->b_bcount != head_len + pkts_len))) {
3636*0a05e705Slc 		USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3637*0a05e705Slc 		    "invalid length: bcount=%d, head_len=%d, pkts_len = %d,"
3638*0a05e705Slc 		    "pktcnt=%d", bp->b_bcount, head_len, pkts_len, n_pkt);
3639*0a05e705Slc 
3640*0a05e705Slc 		epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
3641*0a05e705Slc 		kmem_free(pkt_descr, sizeof (ugen_isoc_pkt_descr_t) * n_pkt);
3642*0a05e705Slc 		rval = USB_INVALID_REQUEST;
3643*0a05e705Slc 
3644*0a05e705Slc 		goto done;
3645*0a05e705Slc 	}
3646*0a05e705Slc 
3647*0a05e705Slc 
3648*0a05e705Slc 	ASSERT(epp->ep_state & UGEN_EP_STATE_XS_OPEN);
3649*0a05e705Slc 
3650*0a05e705Slc 	/* Set parameters for READ */
3651*0a05e705Slc 	if (bp->b_bcount == head_len) {
3652*0a05e705Slc 		/* must be isoc IN endpoint */
3653*0a05e705Slc 		if ((UGEN_XFER_DIR(epp) & USB_EP_DIR_IN) == 0) {
3654*0a05e705Slc 			epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
3655*0a05e705Slc 			kmem_free(pkt_descr, sizeof (ugen_isoc_pkt_descr_t) *
3656*0a05e705Slc 			    n_pkt);
3657*0a05e705Slc 			rval = USB_INVALID_REQUEST;
3658*0a05e705Slc 			USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3659*0a05e705Slc 			    "write length invalid for OUT ep%x",
3660*0a05e705Slc 			    epp->ep_descr.bEndpointAddress);
3661*0a05e705Slc 
3662*0a05e705Slc 			goto done;
3663*0a05e705Slc 		}
3664*0a05e705Slc 
3665*0a05e705Slc 		if (epp->ep_isoc_in_inited) {
3666*0a05e705Slc 			epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
3667*0a05e705Slc 			kmem_free(pkt_descr, sizeof (ugen_isoc_pkt_descr_t) *
3668*0a05e705Slc 			    n_pkt);
3669*0a05e705Slc 			rval = USB_INVALID_REQUEST;
3670*0a05e705Slc 			USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3671*0a05e705Slc 			    "isoc IN polling fail: already inited, need to"
3672*0a05e705Slc 			    "close the ep before initing again");
3673*0a05e705Slc 
3674*0a05e705Slc 			goto done;
3675*0a05e705Slc 		}
3676*0a05e705Slc 
3677*0a05e705Slc 		/* save pkts info for the READ */
3678*0a05e705Slc 		epp->ep_isoc_info.isoc_pkts_count = n_pkt;
3679*0a05e705Slc 		epp->ep_isoc_info.isoc_pkts_length = pkts_len;
3680*0a05e705Slc 		epp->ep_isoc_info.isoc_pkt_descr = pkt_descr;
3681*0a05e705Slc 
3682*0a05e705Slc 		if ((rval = ugen_epx_isoc_IN_start_polling(ugenp,
3683*0a05e705Slc 		    epp)) != USB_SUCCESS) {
3684*0a05e705Slc 			epp->ep_lcmd_status =
3685*0a05e705Slc 			    USB_LC_STAT_ISOC_POLLING_FAILED;
3686*0a05e705Slc 			kmem_free(pkt_descr, sizeof (ugen_isoc_pkt_descr_t) *
3687*0a05e705Slc 			    n_pkt);
3688*0a05e705Slc 			epp->ep_isoc_info.isoc_pkts_count = 0;
3689*0a05e705Slc 			epp->ep_isoc_info.isoc_pkts_length = 0;
3690*0a05e705Slc 			epp->ep_isoc_info.isoc_pkt_descr = NULL;
3691*0a05e705Slc 
3692*0a05e705Slc 			USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3693*0a05e705Slc 			    "isoc IN start polling failed");
3694*0a05e705Slc 
3695*0a05e705Slc 			goto done;
3696*0a05e705Slc 		}
3697*0a05e705Slc 
3698*0a05e705Slc 		epp->ep_bp->b_resid = epp->ep_bp->b_bcount - head_len;
3699*0a05e705Slc 
3700*0a05e705Slc 		epp->ep_isoc_in_inited++;
3701*0a05e705Slc 		USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3702*0a05e705Slc 		    "isoc IN ep inited");
3703*0a05e705Slc 
3704*0a05e705Slc 		goto done;
3705*0a05e705Slc 	}
3706*0a05e705Slc 
3707*0a05e705Slc 	/* must be isoc OUT endpoint */
3708*0a05e705Slc 	if (UGEN_XFER_DIR(epp) & USB_EP_DIR_IN) {
3709*0a05e705Slc 		epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
3710*0a05e705Slc 		kmem_free(pkt_descr, sizeof (ugen_isoc_pkt_descr_t) * n_pkt);
3711*0a05e705Slc 		rval = USB_INVALID_REQUEST;
3712*0a05e705Slc 		USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3713*0a05e705Slc 		    "write length invalid for an IN ep%x",
3714*0a05e705Slc 		    epp->ep_descr.bEndpointAddress);
3715*0a05e705Slc 
3716*0a05e705Slc 		goto done;
3717*0a05e705Slc 	}
3718*0a05e705Slc 
3719*0a05e705Slc 	/* OUT endpoint, free previous info if there's any */
3720*0a05e705Slc 	if (epp->ep_isoc_info.isoc_pkt_descr) {
3721*0a05e705Slc 		kmem_free(epp->ep_isoc_info.isoc_pkt_descr,
3722*0a05e705Slc 		    sizeof (ugen_isoc_pkt_descr_t) *
3723*0a05e705Slc 		    epp->ep_isoc_info.isoc_pkts_count);
3724*0a05e705Slc 	}
3725*0a05e705Slc 
3726*0a05e705Slc 	/* save pkts info for the WRITE */
3727*0a05e705Slc 	epp->ep_isoc_info.isoc_pkts_count = n_pkt;
3728*0a05e705Slc 	epp->ep_isoc_info.isoc_pkts_length = pkts_len;
3729*0a05e705Slc 	epp->ep_isoc_info.isoc_pkt_descr = pkt_descr;
3730*0a05e705Slc 
3731*0a05e705Slc 	reqp = usb_alloc_isoc_req(ugenp->ug_dip, n_pkt, pkts_len,
3732*0a05e705Slc 	    USB_FLAGS_NOSLEEP);
3733*0a05e705Slc 	if (reqp == NULL) {
3734*0a05e705Slc 		epp->ep_lcmd_status = USB_LC_STAT_NO_RESOURCES;
3735*0a05e705Slc 		kmem_free(pkt_descr, sizeof (ugen_isoc_pkt_descr_t) * n_pkt);
3736*0a05e705Slc 		rval = USB_NO_RESOURCES;
3737*0a05e705Slc 		epp->ep_isoc_info.isoc_pkts_count = 0;
3738*0a05e705Slc 		epp->ep_isoc_info.isoc_pkts_length = 0;
3739*0a05e705Slc 		epp->ep_isoc_info.isoc_pkt_descr = NULL;
3740*0a05e705Slc 
3741*0a05e705Slc 		USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3742*0a05e705Slc 		    "alloc isoc out req failed");
3743*0a05e705Slc 		goto done;
3744*0a05e705Slc 	}
3745*0a05e705Slc 
3746*0a05e705Slc 	for (pkt = 0; pkt < n_pkt; pkt++) {
3747*0a05e705Slc 		reqp->isoc_pkt_descr[pkt].isoc_pkt_length =
3748*0a05e705Slc 		    pkt_descr[pkt].dsc_isoc_pkt_len;
3749*0a05e705Slc 	}
3750*0a05e705Slc 	reqp->isoc_pkts_count = n_pkt;
3751*0a05e705Slc 	reqp->isoc_client_private = (usb_opaque_t)ugenp;
3752*0a05e705Slc 	reqp->isoc_attributes	= USB_ATTRS_AUTOCLEARING |
3753*0a05e705Slc 	    USB_ATTRS_ISOC_XFER_ASAP;
3754*0a05e705Slc 
3755*0a05e705Slc 	reqp->isoc_cb		= ugen_epx_isoc_OUT_req_cb;
3756*0a05e705Slc 	reqp->isoc_exc_cb	= ugen_epx_isoc_OUT_req_cb;
3757*0a05e705Slc 
3758*0a05e705Slc 	/* copy data from bp */
3759*0a05e705Slc 	bcopy(p, reqp->isoc_data->b_wptr, pkts_len);
3760*0a05e705Slc 	reqp->isoc_data->b_wptr += pkts_len;
3761*0a05e705Slc 
3762*0a05e705Slc 	mutex_exit(&epp->ep_mutex);
3763*0a05e705Slc 	if ((rval = usb_pipe_isoc_xfer(epp->ep_ph, reqp,
3764*0a05e705Slc 	    USB_FLAGS_NOSLEEP)) != USB_SUCCESS) {
3765*0a05e705Slc 		mutex_enter(&epp->ep_mutex);
3766*0a05e705Slc 		epp->ep_lcmd_status =
3767*0a05e705Slc 		    ugen_cr2lcstat(reqp->isoc_completion_reason);
3768*0a05e705Slc 		usb_free_isoc_req(reqp);
3769*0a05e705Slc 		kmem_free(pkt_descr, sizeof (ugen_isoc_pkt_descr_t) * n_pkt);
3770*0a05e705Slc 
3771*0a05e705Slc 		epp->ep_isoc_info.isoc_pkt_descr = NULL;
3772*0a05e705Slc 		epp->ep_isoc_info.isoc_pkts_count = 0;
3773*0a05e705Slc 		epp->ep_isoc_info.isoc_pkts_length = 0;
3774*0a05e705Slc 
3775*0a05e705Slc 		USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3776*0a05e705Slc 		    "isoc out xfer failed");
3777*0a05e705Slc 
3778*0a05e705Slc 		bioerror(bp, EIO);
3779*0a05e705Slc 	} else {
3780*0a05e705Slc 		mutex_enter(&epp->ep_mutex);
3781*0a05e705Slc 	}
3782*0a05e705Slc 	*wait = (rval == USB_SUCCESS) ? B_TRUE : B_FALSE;
3783*0a05e705Slc 
3784*0a05e705Slc done:
3785*0a05e705Slc 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3786*0a05e705Slc 	    "ugen_epx_isoc_OUT_req end: rval=%d bcount=%lu xfer_len=%d",
3787*0a05e705Slc 	    rval, bp->b_bcount, pkts_len);
3788*0a05e705Slc 
3789*0a05e705Slc 	return (rval);
3790*0a05e705Slc }
3791*0a05e705Slc 
3792*0a05e705Slc 
3793*0a05e705Slc /*
3794*0a05e705Slc  * callback functions for isoc OUT pipe
3795*0a05e705Slc  */
3796*0a05e705Slc static void
3797*0a05e705Slc ugen_epx_isoc_OUT_req_cb(usb_pipe_handle_t ph, usb_isoc_req_t *reqp)
3798*0a05e705Slc {
3799*0a05e705Slc 	ugen_state_t *ugenp = (ugen_state_t *)reqp->isoc_client_private;
3800*0a05e705Slc 	ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
3801*0a05e705Slc 
3802*0a05e705Slc 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3803*0a05e705Slc 	    "ugen_epx_isoc_OUT_req_cb: ph=0x%p reqp=0x%p cr=%d cb=0x%x",
3804*0a05e705Slc 	    ph, reqp, reqp->isoc_completion_reason, reqp->isoc_cb_flags);
3805*0a05e705Slc 
3806*0a05e705Slc 	/* epp might be NULL if we are closing the pipe */
3807*0a05e705Slc 	if (epp) {
3808*0a05e705Slc 		ugen_isoc_pkt_info_t info;
3809*0a05e705Slc 
3810*0a05e705Slc 		mutex_enter(&epp->ep_mutex);
3811*0a05e705Slc 
3812*0a05e705Slc 		info = epp->ep_isoc_info;
3813*0a05e705Slc 		if (epp->ep_bp) {
3814*0a05e705Slc 			int len, i;
3815*0a05e705Slc 			int headlen;
3816*0a05e705Slc 			usb_isoc_pkt_descr_t *pktdesc;
3817*0a05e705Slc 
3818*0a05e705Slc 			pktdesc = reqp->isoc_pkt_descr;
3819*0a05e705Slc 			headlen = info.isoc_pkts_count *
3820*0a05e705Slc 			    sizeof (ugen_isoc_pkt_descr_t);
3821*0a05e705Slc 
3822*0a05e705Slc 			len = min(headlen + reqp->isoc_data->b_wptr -
3823*0a05e705Slc 			    reqp->isoc_data->b_rptr, epp->ep_bp->b_bcount);
3824*0a05e705Slc 
3825*0a05e705Slc 			epp->ep_bp->b_resid = epp->ep_bp->b_bcount - len;
3826*0a05e705Slc 
3827*0a05e705Slc 
3828*0a05e705Slc 			switch (reqp->isoc_completion_reason) {
3829*0a05e705Slc 			case USB_CR_OK:
3830*0a05e705Slc 
3831*0a05e705Slc 				epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
3832*0a05e705Slc 
3833*0a05e705Slc 				for (i = 0; i < reqp->isoc_pkts_count; i++) {
3834*0a05e705Slc 					pktdesc[i].isoc_pkt_status =
3835*0a05e705Slc 					    ugen_cr2lcstat(pktdesc[i].
3836*0a05e705Slc 					    isoc_pkt_status);
3837*0a05e705Slc 				}
3838*0a05e705Slc 
3839*0a05e705Slc 				/* save the status info */
3840*0a05e705Slc 				bcopy(reqp->isoc_pkt_descr,
3841*0a05e705Slc 				    info.isoc_pkt_descr,
3842*0a05e705Slc 				    (sizeof (ugen_isoc_pkt_descr_t) *
3843*0a05e705Slc 				    info.isoc_pkts_count));
3844*0a05e705Slc 
3845*0a05e705Slc 				break;
3846*0a05e705Slc 			case USB_CR_PIPE_RESET:
3847*0a05e705Slc 
3848*0a05e705Slc 				break;
3849*0a05e705Slc 			default:
3850*0a05e705Slc 				epp->ep_lcmd_status =
3851*0a05e705Slc 				    ugen_cr2lcstat(
3852*0a05e705Slc 				    reqp->isoc_completion_reason);
3853*0a05e705Slc 				bioerror(epp->ep_bp, EIO);
3854*0a05e705Slc 			}
3855*0a05e705Slc 		}
3856*0a05e705Slc 		epp->ep_done++;
3857*0a05e705Slc 		cv_signal(&epp->ep_wait_cv);
3858*0a05e705Slc 		mutex_exit(&epp->ep_mutex);
3859*0a05e705Slc 	}
3860*0a05e705Slc 
3861*0a05e705Slc 	usb_free_isoc_req(reqp);
3862*0a05e705Slc }
3863*0a05e705Slc 
3864*0a05e705Slc 
38657c478bd9Sstevel@tonic-gate /*
38667c478bd9Sstevel@tonic-gate  * Endpoint status node management
38677c478bd9Sstevel@tonic-gate  *
38687c478bd9Sstevel@tonic-gate  * open/close an endpoint status node.
38697c478bd9Sstevel@tonic-gate  *
38707c478bd9Sstevel@tonic-gate  * Return values: errno
38717c478bd9Sstevel@tonic-gate  */
38727c478bd9Sstevel@tonic-gate static int
38737c478bd9Sstevel@tonic-gate ugen_eps_open(ugen_state_t *ugenp, dev_t dev, int flag)
38747c478bd9Sstevel@tonic-gate {
38757c478bd9Sstevel@tonic-gate 	ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
38767c478bd9Sstevel@tonic-gate 	int rval = EBUSY;
38777c478bd9Sstevel@tonic-gate 
38787c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
38797c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_STAT, ugenp->ug_log_hdl,
38807c478bd9Sstevel@tonic-gate 	    "ugen_eps_open: dev=0x%lx flag=0x%x state=0x%x",
38817c478bd9Sstevel@tonic-gate 	    dev, flag, epp->ep_state);
38827c478bd9Sstevel@tonic-gate 
38837c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_ACTIVE);
38847c478bd9Sstevel@tonic-gate 
38857c478bd9Sstevel@tonic-gate 	/* only one open at the time */
38867c478bd9Sstevel@tonic-gate 	if ((epp->ep_state & UGEN_EP_STATE_STAT_OPEN) == 0) {
38877c478bd9Sstevel@tonic-gate 		epp->ep_state |= UGEN_EP_STATE_STAT_OPEN;
38887c478bd9Sstevel@tonic-gate 		epp->ep_stat_oflag = flag;
38897c478bd9Sstevel@tonic-gate 		rval = 0;
38907c478bd9Sstevel@tonic-gate 	}
38917c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
38927c478bd9Sstevel@tonic-gate 
38937c478bd9Sstevel@tonic-gate 	return (rval);
38947c478bd9Sstevel@tonic-gate }
38957c478bd9Sstevel@tonic-gate 
38967c478bd9Sstevel@tonic-gate 
38977c478bd9Sstevel@tonic-gate /*
38987c478bd9Sstevel@tonic-gate  * close endpoint status
38997c478bd9Sstevel@tonic-gate  */
39007c478bd9Sstevel@tonic-gate static void
39017c478bd9Sstevel@tonic-gate ugen_eps_close(ugen_state_t *ugenp, dev_t dev, int flag)
39027c478bd9Sstevel@tonic-gate {
39037c478bd9Sstevel@tonic-gate 	ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
39047c478bd9Sstevel@tonic-gate 
39057c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
39067c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_STAT, ugenp->ug_log_hdl,
39077c478bd9Sstevel@tonic-gate 	    "ugen_eps_close: dev=0x%lx flag=0x%x state=0x%x",
39087c478bd9Sstevel@tonic-gate 	    dev, flag, epp->ep_state);
39097c478bd9Sstevel@tonic-gate 
39107c478bd9Sstevel@tonic-gate 	epp->ep_state &= ~(UGEN_EP_STATE_STAT_OPEN |
3911*0a05e705Slc 	    UGEN_EP_STATE_INTR_IN_POLL_PENDING |
3912*0a05e705Slc 	    UGEN_EP_STATE_ISOC_IN_POLL_PENDING);
39137c478bd9Sstevel@tonic-gate 	epp->ep_one_xfer = B_FALSE;
39147c478bd9Sstevel@tonic-gate 
39157c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_STAT, ugenp->ug_log_hdl,
39167c478bd9Sstevel@tonic-gate 	    "ugen_eps_close: state=0x%x", epp->ep_state);
39177c478bd9Sstevel@tonic-gate 
39187c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_ACTIVE);
39197c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
39207c478bd9Sstevel@tonic-gate }
39217c478bd9Sstevel@tonic-gate 
39227c478bd9Sstevel@tonic-gate 
39237c478bd9Sstevel@tonic-gate /*
39247c478bd9Sstevel@tonic-gate  * return status info
39257c478bd9Sstevel@tonic-gate  *
39267c478bd9Sstevel@tonic-gate  * Return values: errno
39277c478bd9Sstevel@tonic-gate  */
39287c478bd9Sstevel@tonic-gate static int
39297c478bd9Sstevel@tonic-gate ugen_eps_req(ugen_state_t *ugenp, struct buf *bp)
39307c478bd9Sstevel@tonic-gate {
39317c478bd9Sstevel@tonic-gate 	ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, bp->b_edev)];
39327c478bd9Sstevel@tonic-gate 
39337c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
39347c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
39357c478bd9Sstevel@tonic-gate 	    "ugen_eps_req: bp=0x%p lcmd_status=0x%x bcount=%lu",
39367c478bd9Sstevel@tonic-gate 	    bp, epp->ep_lcmd_status, bp->b_bcount);
39377c478bd9Sstevel@tonic-gate 
39387c478bd9Sstevel@tonic-gate 	if (bp->b_flags & B_READ) {
39397c478bd9Sstevel@tonic-gate 		int len = min(sizeof (epp->ep_lcmd_status), bp->b_bcount);
39407c478bd9Sstevel@tonic-gate 		if (len) {
39417c478bd9Sstevel@tonic-gate 			bcopy(&epp->ep_lcmd_status, bp->b_un.b_addr, len);
39427c478bd9Sstevel@tonic-gate 		}
39437c478bd9Sstevel@tonic-gate 		bp->b_resid = bp->b_bcount - len;
39447c478bd9Sstevel@tonic-gate 	} else {
39457c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
39467c478bd9Sstevel@tonic-gate 		    "ugen_eps_req: control=0x%x",
39477c478bd9Sstevel@tonic-gate 		    *((char *)(bp->b_un.b_addr)));
39487c478bd9Sstevel@tonic-gate 
39497c478bd9Sstevel@tonic-gate 		if (epp->ep_state & UGEN_EP_STATE_XFER_OPEN) {
39507c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
39517c478bd9Sstevel@tonic-gate 			    "ugen_eps_req: cannot change one xfer mode if "
39527c478bd9Sstevel@tonic-gate 			    "endpoint is open");
39537c478bd9Sstevel@tonic-gate 
39547c478bd9Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
39557c478bd9Sstevel@tonic-gate 
39567c478bd9Sstevel@tonic-gate 			return (EINVAL);
39577c478bd9Sstevel@tonic-gate 		}
39587c478bd9Sstevel@tonic-gate 
39597c478bd9Sstevel@tonic-gate 		if ((epp->ep_descr.bmAttributes & USB_EP_ATTR_INTR) &&
39607c478bd9Sstevel@tonic-gate 		    (epp->ep_descr.bEndpointAddress & USB_EP_DIR_IN)) {
39617c478bd9Sstevel@tonic-gate 			epp->ep_one_xfer = (*((char *)(bp->b_un.b_addr)) &
39627c478bd9Sstevel@tonic-gate 			    USB_EP_INTR_ONE_XFER) ? B_TRUE : B_FALSE;
39637c478bd9Sstevel@tonic-gate 		} else {
39647c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
39657c478bd9Sstevel@tonic-gate 			    "ugen_eps_req: not an interrupt endpoint");
39667c478bd9Sstevel@tonic-gate 
39677c478bd9Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
39687c478bd9Sstevel@tonic-gate 
39697c478bd9Sstevel@tonic-gate 			return (EINVAL);
39707c478bd9Sstevel@tonic-gate 		}
39717c478bd9Sstevel@tonic-gate 
39727c478bd9Sstevel@tonic-gate 		bp->b_resid = bp->b_bcount - 1;
39737c478bd9Sstevel@tonic-gate 	}
39747c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
39757c478bd9Sstevel@tonic-gate 
39767c478bd9Sstevel@tonic-gate 	return (0);
39777c478bd9Sstevel@tonic-gate }
39787c478bd9Sstevel@tonic-gate 
39797c478bd9Sstevel@tonic-gate 
39807c478bd9Sstevel@tonic-gate /*
39817c478bd9Sstevel@tonic-gate  * device status node management
39827c478bd9Sstevel@tonic-gate  */
39837c478bd9Sstevel@tonic-gate static int
39847c478bd9Sstevel@tonic-gate ugen_ds_init(ugen_state_t *ugenp)
39857c478bd9Sstevel@tonic-gate {
39867c478bd9Sstevel@tonic-gate 	cv_init(&ugenp->ug_ds.dev_wait_cv, NULL, CV_DRIVER, NULL);
39877c478bd9Sstevel@tonic-gate 
39887c478bd9Sstevel@tonic-gate 	/* Create devstat minor node for this instance */
39897c478bd9Sstevel@tonic-gate 	if (ugen_ds_minor_nodes_create(ugenp) != USB_SUCCESS) {
39907c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
39917c478bd9Sstevel@tonic-gate 		    "ugen_create_dev_stat_minor_nodes failed");
39927c478bd9Sstevel@tonic-gate 
39937c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
39947c478bd9Sstevel@tonic-gate 	}
39957c478bd9Sstevel@tonic-gate 
39967c478bd9Sstevel@tonic-gate 
39977c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
39987c478bd9Sstevel@tonic-gate }
39997c478bd9Sstevel@tonic-gate 
40007c478bd9Sstevel@tonic-gate 
40017c478bd9Sstevel@tonic-gate static void
40027c478bd9Sstevel@tonic-gate ugen_ds_destroy(ugen_state_t *ugenp)
40037c478bd9Sstevel@tonic-gate {
40047c478bd9Sstevel@tonic-gate 	cv_destroy(&ugenp->ug_ds.dev_wait_cv);
40057c478bd9Sstevel@tonic-gate }
40067c478bd9Sstevel@tonic-gate 
40077c478bd9Sstevel@tonic-gate 
40087c478bd9Sstevel@tonic-gate /*
40097c478bd9Sstevel@tonic-gate  * open devstat minor node
40107c478bd9Sstevel@tonic-gate  *
40117c478bd9Sstevel@tonic-gate  * Return values: errno
40127c478bd9Sstevel@tonic-gate  */
40137c478bd9Sstevel@tonic-gate static int
40147c478bd9Sstevel@tonic-gate ugen_ds_open(ugen_state_t *ugenp, dev_t dev, int flag)
40157c478bd9Sstevel@tonic-gate {
40167c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
40177c478bd9Sstevel@tonic-gate 	    "ugen_ds_open: dev=0x%lx flag=0x%x", dev, flag);
40187c478bd9Sstevel@tonic-gate 
40197c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
40207c478bd9Sstevel@tonic-gate 	if ((ugenp->ug_ds.dev_stat & UGEN_DEV_STATUS_ACTIVE) == 0) {
40217c478bd9Sstevel@tonic-gate 		/*
40227c478bd9Sstevel@tonic-gate 		 * first read on device node should return status
40237c478bd9Sstevel@tonic-gate 		 */
40247c478bd9Sstevel@tonic-gate 		ugenp->ug_ds.dev_stat |= UGEN_DEV_STATUS_CHANGED |
4025*0a05e705Slc 		    UGEN_DEV_STATUS_ACTIVE;
40267c478bd9Sstevel@tonic-gate 		ugenp->ug_ds.dev_oflag = flag;
40277c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
40287c478bd9Sstevel@tonic-gate 
40297c478bd9Sstevel@tonic-gate 		return (0);
40307c478bd9Sstevel@tonic-gate 	} else {
40317c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
40327c478bd9Sstevel@tonic-gate 
40337c478bd9Sstevel@tonic-gate 		return (EBUSY);
40347c478bd9Sstevel@tonic-gate 	}
40357c478bd9Sstevel@tonic-gate }
40367c478bd9Sstevel@tonic-gate 
40377c478bd9Sstevel@tonic-gate 
40387c478bd9Sstevel@tonic-gate static void
40397c478bd9Sstevel@tonic-gate ugen_ds_close(ugen_state_t *ugenp, dev_t dev, int flag)
40407c478bd9Sstevel@tonic-gate {
40417c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
40427c478bd9Sstevel@tonic-gate 	    "ugen_ds_close: dev=0x%lx flag=0x%x", dev, flag);
40437c478bd9Sstevel@tonic-gate 
40447c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
40457c478bd9Sstevel@tonic-gate 	ugenp->ug_ds.dev_stat = UGEN_DEV_STATUS_INACTIVE;
40467c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
40477c478bd9Sstevel@tonic-gate }
40487c478bd9Sstevel@tonic-gate 
40497c478bd9Sstevel@tonic-gate 
40507c478bd9Sstevel@tonic-gate /*
40517c478bd9Sstevel@tonic-gate  * request for devstat
40527c478bd9Sstevel@tonic-gate  *
40537c478bd9Sstevel@tonic-gate  * Return values: errno
40547c478bd9Sstevel@tonic-gate  */
40557c478bd9Sstevel@tonic-gate static int
40567c478bd9Sstevel@tonic-gate ugen_ds_req(ugen_state_t *ugenp, struct buf *bp)
40577c478bd9Sstevel@tonic-gate {
40587c478bd9Sstevel@tonic-gate 	int len = min(sizeof (ugenp->ug_ds.dev_state), bp->b_bcount);
40597c478bd9Sstevel@tonic-gate 
40607c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
40617c478bd9Sstevel@tonic-gate 	    "ugen_ds_req: bp=0x%p", bp);
40627c478bd9Sstevel@tonic-gate 
40637c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
40647c478bd9Sstevel@tonic-gate 	if ((ugenp->ug_ds.dev_oflag & (FNDELAY | FNONBLOCK)) == 0) {
40657c478bd9Sstevel@tonic-gate 		while ((ugenp->ug_ds.dev_stat &
40667c478bd9Sstevel@tonic-gate 		    UGEN_DEV_STATUS_CHANGED) == 0) {
40677c478bd9Sstevel@tonic-gate 			if (cv_wait_sig(&ugenp->ug_ds.dev_wait_cv,
40687c478bd9Sstevel@tonic-gate 			    &ugenp->ug_mutex) <= 0) {
40697c478bd9Sstevel@tonic-gate 				mutex_exit(&ugenp->ug_mutex);
40707c478bd9Sstevel@tonic-gate 
40717c478bd9Sstevel@tonic-gate 				return (EINTR);
40727c478bd9Sstevel@tonic-gate 			}
40737c478bd9Sstevel@tonic-gate 		}
40747c478bd9Sstevel@tonic-gate 	} else if ((ugenp->ug_ds.dev_stat & UGEN_DEV_STATUS_CHANGED) ==
40757c478bd9Sstevel@tonic-gate 	    0) {
40767c478bd9Sstevel@tonic-gate 		bp->b_resid = bp->b_bcount;
40777c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
40787c478bd9Sstevel@tonic-gate 
40797c478bd9Sstevel@tonic-gate 		return (0);
40807c478bd9Sstevel@tonic-gate 	}
40817c478bd9Sstevel@tonic-gate 
40827c478bd9Sstevel@tonic-gate 	ugenp->ug_ds.dev_stat &= ~UGEN_DEV_STATUS_CHANGED;
40837c478bd9Sstevel@tonic-gate 	switch (ugenp->ug_dev_state) {
40847c478bd9Sstevel@tonic-gate 	case USB_DEV_ONLINE:
40857c478bd9Sstevel@tonic-gate 		ugenp->ug_ds.dev_state = USB_DEV_STAT_ONLINE;
40867c478bd9Sstevel@tonic-gate 
40877c478bd9Sstevel@tonic-gate 		break;
40887c478bd9Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
40897c478bd9Sstevel@tonic-gate 		ugenp->ug_ds.dev_state = USB_DEV_STAT_DISCONNECTED;
40907c478bd9Sstevel@tonic-gate 
40917c478bd9Sstevel@tonic-gate 		break;
40927c478bd9Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
40937c478bd9Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RESUME:
40947c478bd9Sstevel@tonic-gate 		ugenp->ug_ds.dev_state = USB_DEV_STAT_RESUMED;
40957c478bd9Sstevel@tonic-gate 
40967c478bd9Sstevel@tonic-gate 		break;
40977c478bd9Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
40987c478bd9Sstevel@tonic-gate 	default:
40997c478bd9Sstevel@tonic-gate 		ugenp->ug_ds.dev_state = USB_DEV_STAT_UNAVAILABLE;
41007c478bd9Sstevel@tonic-gate 
41017c478bd9Sstevel@tonic-gate 		break;
41027c478bd9Sstevel@tonic-gate 	}
41037c478bd9Sstevel@tonic-gate 
41047c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
41057c478bd9Sstevel@tonic-gate 	    "ugen_ds_req: dev_state=0x%x dev_stat=0x%x",
41067c478bd9Sstevel@tonic-gate 	    ugenp->ug_dev_state, ugenp->ug_ds.dev_stat);
41077c478bd9Sstevel@tonic-gate 
41087c478bd9Sstevel@tonic-gate 	bcopy(&ugenp->ug_ds.dev_state, bp->b_un.b_addr, len);
41097c478bd9Sstevel@tonic-gate 	bp->b_resid = bp->b_bcount - len;
41107c478bd9Sstevel@tonic-gate 
41117c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
41127c478bd9Sstevel@tonic-gate 
41137c478bd9Sstevel@tonic-gate 	return (0);
41147c478bd9Sstevel@tonic-gate }
41157c478bd9Sstevel@tonic-gate 
41167c478bd9Sstevel@tonic-gate 
41177c478bd9Sstevel@tonic-gate static void
41187c478bd9Sstevel@tonic-gate ugen_ds_change(ugen_state_t *ugenp)
41197c478bd9Sstevel@tonic-gate {
41207c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
41217c478bd9Sstevel@tonic-gate 	    "ugen_ds_change:");
41227c478bd9Sstevel@tonic-gate 
41237c478bd9Sstevel@tonic-gate 	ugenp->ug_ds.dev_stat |= UGEN_DEV_STATUS_CHANGED;
41247c478bd9Sstevel@tonic-gate 	cv_signal(&ugenp->ug_ds.dev_wait_cv);
41257c478bd9Sstevel@tonic-gate }
41267c478bd9Sstevel@tonic-gate 
41277c478bd9Sstevel@tonic-gate 
41287c478bd9Sstevel@tonic-gate /*
41297c478bd9Sstevel@tonic-gate  * poll management
41307c478bd9Sstevel@tonic-gate  */
41317c478bd9Sstevel@tonic-gate static void
41327c478bd9Sstevel@tonic-gate ugen_ds_poll_wakeup(ugen_state_t *ugenp)
41337c478bd9Sstevel@tonic-gate {
41347c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
41357c478bd9Sstevel@tonic-gate 	    "ugen_ds_poll_wakeup:");
41367c478bd9Sstevel@tonic-gate 
41377c478bd9Sstevel@tonic-gate 	if (ugenp->ug_ds.dev_stat & UGEN_DEV_STATUS_POLL_PENDING) {
41387c478bd9Sstevel@tonic-gate 		struct pollhead *phpp = &ugenp->ug_ds.dev_pollhead;
41397c478bd9Sstevel@tonic-gate 		ugenp->ug_ds.dev_stat &= ~UGEN_DEV_STATUS_POLL_PENDING;
41407c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
41417c478bd9Sstevel@tonic-gate 		pollwakeup(phpp, POLLIN);
41427c478bd9Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
41437c478bd9Sstevel@tonic-gate 	}
41447c478bd9Sstevel@tonic-gate }
41457c478bd9Sstevel@tonic-gate 
41467c478bd9Sstevel@tonic-gate 
41477c478bd9Sstevel@tonic-gate /*
41487c478bd9Sstevel@tonic-gate  * minor node management:
41497c478bd9Sstevel@tonic-gate  */
41507c478bd9Sstevel@tonic-gate static int
41517c478bd9Sstevel@tonic-gate ugen_ds_minor_nodes_create(ugen_state_t *ugenp)
41527c478bd9Sstevel@tonic-gate {
41537c478bd9Sstevel@tonic-gate 	char	node_name[32];
41547c478bd9Sstevel@tonic-gate 	int	vid = ugenp->ug_dev_data->dev_descr->idVendor;
41557c478bd9Sstevel@tonic-gate 	int	pid = ugenp->ug_dev_data->dev_descr->idProduct;
41567c478bd9Sstevel@tonic-gate 	minor_t	minor;
41577c478bd9Sstevel@tonic-gate 	int	minor_index;
41587c478bd9Sstevel@tonic-gate 	int	owns_device = (usb_owns_device(ugenp->ug_dip) ?
4159*0a05e705Slc 	    UGEN_OWNS_DEVICE : 0);
41607c478bd9Sstevel@tonic-gate 
41617c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
41627c478bd9Sstevel@tonic-gate 	    "ugen_ds_minor_nodes_create: idx shift=%d inst shift=%d",
41637c478bd9Sstevel@tonic-gate 	    UGEN_MINOR_IDX_SHIFT(ugenp),
41647c478bd9Sstevel@tonic-gate 	    UGEN_MINOR_INSTANCE_SHIFT(ugenp));
41657c478bd9Sstevel@tonic-gate 
41667c478bd9Sstevel@tonic-gate 	if (ugenp->ug_instance >= UGEN_MINOR_INSTANCE_LIMIT(ugenp)) {
41677c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L0(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
41687c478bd9Sstevel@tonic-gate 		    "instance number too high (%d)", ugenp->ug_instance);
41697c478bd9Sstevel@tonic-gate 
41707c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
41717c478bd9Sstevel@tonic-gate 	}
41727c478bd9Sstevel@tonic-gate 
41737c478bd9Sstevel@tonic-gate 	/* create devstat minor node */
41747c478bd9Sstevel@tonic-gate 	if (owns_device) {
41757c478bd9Sstevel@tonic-gate 		(void) sprintf(node_name, "%x.%x.devstat", vid, pid);
41767c478bd9Sstevel@tonic-gate 	} else {
41777c478bd9Sstevel@tonic-gate 		(void) sprintf(node_name, "%x.%x.if%ddevstat", vid, pid,
41787c478bd9Sstevel@tonic-gate 		    ugenp->ug_dev_data->dev_curr_if);
41797c478bd9Sstevel@tonic-gate 	}
41807c478bd9Sstevel@tonic-gate 
41817c478bd9Sstevel@tonic-gate 	minor_index = ugen_minor_index_create(ugenp,
41827c478bd9Sstevel@tonic-gate 	    (UGEN_MINOR_DEV_STAT_NODE | owns_device) <<
41837c478bd9Sstevel@tonic-gate 	    UGEN_MINOR_IDX_SHIFT(ugenp));
41847c478bd9Sstevel@tonic-gate 
41857c478bd9Sstevel@tonic-gate 	if (minor_index < 0) {
41867c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L0(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
41877c478bd9Sstevel@tonic-gate 		    "too many minor nodes");
41887c478bd9Sstevel@tonic-gate 
41897c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
41907c478bd9Sstevel@tonic-gate 	}
41917c478bd9Sstevel@tonic-gate 	minor = (minor_index << UGEN_MINOR_IDX_SHIFT(ugenp)) |
41927c478bd9Sstevel@tonic-gate 	    ugenp->ug_instance << UGEN_MINOR_INSTANCE_SHIFT(ugenp);
41937c478bd9Sstevel@tonic-gate 
41947c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
41957c478bd9Sstevel@tonic-gate 	    "minor=0x%x minor_index=%d name=%s",
41967c478bd9Sstevel@tonic-gate 	    minor, minor_index, node_name);
41977c478bd9Sstevel@tonic-gate 
41987c478bd9Sstevel@tonic-gate 	ASSERT(minor < L_MAXMIN);
41997c478bd9Sstevel@tonic-gate 
42007c478bd9Sstevel@tonic-gate 	if ((ddi_create_minor_node(ugenp->ug_dip, node_name,
42017c478bd9Sstevel@tonic-gate 	    S_IFCHR, minor, DDI_NT_UGEN, 0)) != DDI_SUCCESS) {
42027c478bd9Sstevel@tonic-gate 
42037c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
42047c478bd9Sstevel@tonic-gate 	}
42057c478bd9Sstevel@tonic-gate 
42067c478bd9Sstevel@tonic-gate 	ugen_store_devt(ugenp, minor);
42077c478bd9Sstevel@tonic-gate 
42087c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
42097c478bd9Sstevel@tonic-gate }
42107c478bd9Sstevel@tonic-gate 
42117c478bd9Sstevel@tonic-gate 
42127c478bd9Sstevel@tonic-gate /*
42137c478bd9Sstevel@tonic-gate  * utility functions:
42147c478bd9Sstevel@tonic-gate  *
42157c478bd9Sstevel@tonic-gate  * conversion from completion reason to  USB_LC_STAT_*
42167c478bd9Sstevel@tonic-gate  */
42177c478bd9Sstevel@tonic-gate static struct ugen_cr2lcstat_entry {
42187c478bd9Sstevel@tonic-gate 	int	cr;
42197c478bd9Sstevel@tonic-gate 	int	lcstat;
42207c478bd9Sstevel@tonic-gate } ugen_cr2lcstat_table[] = {
42217c478bd9Sstevel@tonic-gate 	{ USB_CR_OK,			USB_LC_STAT_NOERROR	},
42227c478bd9Sstevel@tonic-gate 	{ USB_CR_CRC,			USB_LC_STAT_CRC		},
42237c478bd9Sstevel@tonic-gate 	{ USB_CR_BITSTUFFING,		USB_LC_STAT_BITSTUFFING },
42247c478bd9Sstevel@tonic-gate 	{ USB_CR_DATA_TOGGLE_MM,	USB_LC_STAT_DATA_TOGGLE_MM },
42257c478bd9Sstevel@tonic-gate 	{ USB_CR_STALL,			USB_LC_STAT_STALL	},
42267c478bd9Sstevel@tonic-gate 	{ USB_CR_DEV_NOT_RESP,		USB_LC_STAT_DEV_NOT_RESP },
42277c478bd9Sstevel@tonic-gate 	{ USB_CR_PID_CHECKFAILURE,	USB_LC_STAT_PID_CHECKFAILURE },
42287c478bd9Sstevel@tonic-gate 	{ USB_CR_UNEXP_PID,		USB_LC_STAT_UNEXP_PID	},
42297c478bd9Sstevel@tonic-gate 	{ USB_CR_DATA_OVERRUN,		USB_LC_STAT_DATA_OVERRUN },
42307c478bd9Sstevel@tonic-gate 	{ USB_CR_DATA_UNDERRUN,		USB_LC_STAT_DATA_UNDERRUN },
42317c478bd9Sstevel@tonic-gate 	{ USB_CR_BUFFER_OVERRUN,	USB_LC_STAT_BUFFER_OVERRUN },
42327c478bd9Sstevel@tonic-gate 	{ USB_CR_BUFFER_UNDERRUN,	USB_LC_STAT_BUFFER_UNDERRUN },
42337c478bd9Sstevel@tonic-gate 	{ USB_CR_TIMEOUT,		USB_LC_STAT_TIMEOUT	},
42347c478bd9Sstevel@tonic-gate 	{ USB_CR_NOT_ACCESSED,		USB_LC_STAT_NOT_ACCESSED },
42357c478bd9Sstevel@tonic-gate 	{ USB_CR_NO_RESOURCES,		USB_LC_STAT_NO_BANDWIDTH },
42367c478bd9Sstevel@tonic-gate 	{ USB_CR_UNSPECIFIED_ERR,	USB_LC_STAT_UNSPECIFIED_ERR },
42377c478bd9Sstevel@tonic-gate 	{ USB_CR_STOPPED_POLLING,	USB_LC_STAT_HW_ERR	},
42387c478bd9Sstevel@tonic-gate 	{ USB_CR_PIPE_CLOSING,		USB_LC_STAT_UNSPECIFIED_ERR	},
42397c478bd9Sstevel@tonic-gate 	{ USB_CR_PIPE_RESET,		USB_LC_STAT_UNSPECIFIED_ERR	},
42407c478bd9Sstevel@tonic-gate 	{ USB_CR_NOT_SUPPORTED,		USB_LC_STAT_UNSPECIFIED_ERR },
42417c478bd9Sstevel@tonic-gate 	{ USB_CR_FLUSHED,		USB_LC_STAT_UNSPECIFIED_ERR }
42427c478bd9Sstevel@tonic-gate };
42437c478bd9Sstevel@tonic-gate 
42447c478bd9Sstevel@tonic-gate #define	UGEN_CR2LCSTAT_TABLE_SIZE (sizeof (ugen_cr2lcstat_table) / \
42457c478bd9Sstevel@tonic-gate 			sizeof (struct ugen_cr2lcstat_entry))
42467c478bd9Sstevel@tonic-gate static int
42477c478bd9Sstevel@tonic-gate ugen_cr2lcstat(int cr)
42487c478bd9Sstevel@tonic-gate {
42497c478bd9Sstevel@tonic-gate 	int i;
42507c478bd9Sstevel@tonic-gate 
42517c478bd9Sstevel@tonic-gate 	for (i = 0; i < UGEN_CR2LCSTAT_TABLE_SIZE; i++) {
42527c478bd9Sstevel@tonic-gate 		if (ugen_cr2lcstat_table[i].cr == cr) {
42537c478bd9Sstevel@tonic-gate 
42547c478bd9Sstevel@tonic-gate 			return (ugen_cr2lcstat_table[i].lcstat);
42557c478bd9Sstevel@tonic-gate 		}
42567c478bd9Sstevel@tonic-gate 	}
42577c478bd9Sstevel@tonic-gate 
42587c478bd9Sstevel@tonic-gate 	return (USB_LC_STAT_UNSPECIFIED_ERR);
42597c478bd9Sstevel@tonic-gate }
42607c478bd9Sstevel@tonic-gate 
42617c478bd9Sstevel@tonic-gate 
42627c478bd9Sstevel@tonic-gate /*
42637c478bd9Sstevel@tonic-gate  * create and lookup minor index
42647c478bd9Sstevel@tonic-gate  */
42657c478bd9Sstevel@tonic-gate static int
42667c478bd9Sstevel@tonic-gate ugen_minor_index_create(ugen_state_t *ugenp, ugen_minor_t minor)
42677c478bd9Sstevel@tonic-gate {
42687c478bd9Sstevel@tonic-gate 	int i;
42697c478bd9Sstevel@tonic-gate 
42707c478bd9Sstevel@tonic-gate 	/* check if already in the table */
42717c478bd9Sstevel@tonic-gate 	for (i = 1; i < ugenp->ug_minor_node_table_index; i++) {
42727c478bd9Sstevel@tonic-gate 		if (ugenp->ug_minor_node_table[i] == minor) {
42737c478bd9Sstevel@tonic-gate 
42747c478bd9Sstevel@tonic-gate 			return (-1);
42757c478bd9Sstevel@tonic-gate 		}
42767c478bd9Sstevel@tonic-gate 	}
42777c478bd9Sstevel@tonic-gate 	if (ugenp->ug_minor_node_table_index <
42787c478bd9Sstevel@tonic-gate 	    (ugenp->ug_minor_node_table_size/sizeof (ugen_minor_t))) {
42797c478bd9Sstevel@tonic-gate 		ugenp->ug_minor_node_table[ugenp->
4280*0a05e705Slc 		    ug_minor_node_table_index] = minor;
42817c478bd9Sstevel@tonic-gate 
42827c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
42837c478bd9Sstevel@tonic-gate 		    "ugen_minor_index_create: %d: 0x%lx",
42847c478bd9Sstevel@tonic-gate 		    ugenp->ug_minor_node_table_index,
42857c478bd9Sstevel@tonic-gate 		    minor);
42867c478bd9Sstevel@tonic-gate 
42877c478bd9Sstevel@tonic-gate 		return (ugenp->ug_minor_node_table_index++);
42887c478bd9Sstevel@tonic-gate 	} else {
42897c478bd9Sstevel@tonic-gate 
42907c478bd9Sstevel@tonic-gate 		return (-1);
42917c478bd9Sstevel@tonic-gate 	}
42927c478bd9Sstevel@tonic-gate }
42937c478bd9Sstevel@tonic-gate 
42947c478bd9Sstevel@tonic-gate 
42957c478bd9Sstevel@tonic-gate static ugen_minor_t
42967c478bd9Sstevel@tonic-gate ugen_devt2minor(ugen_state_t *ugenp, dev_t dev)
42977c478bd9Sstevel@tonic-gate {
42987c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
429996603521Sfrits 	    "ugen_devt2minor: minorindex=%d, minor=0x%" PRIx64,
43007c478bd9Sstevel@tonic-gate 	    UGEN_MINOR_GET_IDX(ugenp, dev),
43017c478bd9Sstevel@tonic-gate 	    ugenp->ug_minor_node_table[UGEN_MINOR_GET_IDX(ugenp, dev)]);
43027c478bd9Sstevel@tonic-gate 
43037c478bd9Sstevel@tonic-gate 	ASSERT(UGEN_MINOR_GET_IDX(ugenp, dev) <
4304*0a05e705Slc 	    ugenp->ug_minor_node_table_index);
43057c478bd9Sstevel@tonic-gate 
43067c478bd9Sstevel@tonic-gate 	return (ugenp->ug_minor_node_table[UGEN_MINOR_GET_IDX(ugenp, dev)]);
43077c478bd9Sstevel@tonic-gate }
43087c478bd9Sstevel@tonic-gate 
43097c478bd9Sstevel@tonic-gate 
431096603521Sfrits static int
431196603521Sfrits ugen_is_valid_minor_node(ugen_state_t *ugenp, dev_t dev)
431296603521Sfrits {
431396603521Sfrits 	int idx = UGEN_MINOR_GET_IDX(ugenp, dev);
431496603521Sfrits 
431596603521Sfrits 	if ((idx < ugenp->ug_minor_node_table_index) &&
431696603521Sfrits 	    (idx > 0)) {
431796603521Sfrits 
431896603521Sfrits 		return (USB_SUCCESS);
431996603521Sfrits 	}
432096603521Sfrits 	USB_DPRINTF_L2(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
432196603521Sfrits 	    "ugen_is_valid_minor_node: invalid minorindex=%d", idx);
432296603521Sfrits 
432396603521Sfrits 	return (USB_FAILURE);
432496603521Sfrits }
432596603521Sfrits 
432696603521Sfrits 
43277c478bd9Sstevel@tonic-gate static void
43287c478bd9Sstevel@tonic-gate ugen_minor_node_table_create(ugen_state_t *ugenp)
43297c478bd9Sstevel@tonic-gate {
43307c478bd9Sstevel@tonic-gate 	size_t	size = sizeof (ugen_minor_t) * UGEN_MINOR_IDX_LIMIT(ugenp);
43317c478bd9Sstevel@tonic-gate 
43327c478bd9Sstevel@tonic-gate 	/* allocate the max table size needed, we reduce later */
43337c478bd9Sstevel@tonic-gate 	ugenp->ug_minor_node_table = kmem_zalloc(size, KM_SLEEP);
43347c478bd9Sstevel@tonic-gate 	ugenp->ug_minor_node_table_size = size;
43357c478bd9Sstevel@tonic-gate 	ugenp->ug_minor_node_table_index = 1;
43367c478bd9Sstevel@tonic-gate }
43377c478bd9Sstevel@tonic-gate 
43387c478bd9Sstevel@tonic-gate 
43397c478bd9Sstevel@tonic-gate static void
43407c478bd9Sstevel@tonic-gate ugen_minor_node_table_shrink(ugen_state_t *ugenp)
43417c478bd9Sstevel@tonic-gate {
43427c478bd9Sstevel@tonic-gate 	/* reduce the table size to save some memory */
43437c478bd9Sstevel@tonic-gate 	if (ugenp->ug_minor_node_table_index < UGEN_MINOR_IDX_LIMIT(ugenp)) {
43447c478bd9Sstevel@tonic-gate 		size_t newsize = sizeof (ugen_minor_t) *
4345*0a05e705Slc 		    ugenp->ug_minor_node_table_index;
43467c478bd9Sstevel@tonic-gate 		ugen_minor_t *buf = kmem_zalloc(newsize, KM_SLEEP);
43477c478bd9Sstevel@tonic-gate 
43487c478bd9Sstevel@tonic-gate 		bcopy(ugenp->ug_minor_node_table, buf, newsize);
43497c478bd9Sstevel@tonic-gate 		kmem_free(ugenp->ug_minor_node_table,
4350*0a05e705Slc 		    ugenp->ug_minor_node_table_size);
43517c478bd9Sstevel@tonic-gate 		ugenp->ug_minor_node_table = buf;
43527c478bd9Sstevel@tonic-gate 		ugenp->ug_minor_node_table_size = newsize;
43537c478bd9Sstevel@tonic-gate 	}
43547c478bd9Sstevel@tonic-gate }
43557c478bd9Sstevel@tonic-gate 
43567c478bd9Sstevel@tonic-gate 
43577c478bd9Sstevel@tonic-gate static void
43587c478bd9Sstevel@tonic-gate ugen_minor_node_table_destroy(ugen_state_t *ugenp)
43597c478bd9Sstevel@tonic-gate {
43607c478bd9Sstevel@tonic-gate 	if (ugenp->ug_minor_node_table) {
43617c478bd9Sstevel@tonic-gate 		kmem_free(ugenp->ug_minor_node_table,
4362*0a05e705Slc 		    ugenp->ug_minor_node_table_size);
43637c478bd9Sstevel@tonic-gate 	}
43647c478bd9Sstevel@tonic-gate }
43657c478bd9Sstevel@tonic-gate 
43667c478bd9Sstevel@tonic-gate 
43677c478bd9Sstevel@tonic-gate static void
43687c478bd9Sstevel@tonic-gate ugen_check_mask(uint_t mask, uint_t *shift, uint_t *limit)
43697c478bd9Sstevel@tonic-gate {
43707c478bd9Sstevel@tonic-gate 	uint_t i, j;
43717c478bd9Sstevel@tonic-gate 
43727c478bd9Sstevel@tonic-gate 	for (i = 0; i < UGEN_MINOR_NODE_SIZE; i++) {
43737c478bd9Sstevel@tonic-gate 		if ((1 << i)  & mask) {
43747c478bd9Sstevel@tonic-gate 
43757c478bd9Sstevel@tonic-gate 			break;
43767c478bd9Sstevel@tonic-gate 		}
43777c478bd9Sstevel@tonic-gate 	}
43787c478bd9Sstevel@tonic-gate 
43797c478bd9Sstevel@tonic-gate 	for (j = i; j < UGEN_MINOR_NODE_SIZE; j++) {
43807c478bd9Sstevel@tonic-gate 		if (((1 << j) & mask) == 0) {
43817c478bd9Sstevel@tonic-gate 
43827c478bd9Sstevel@tonic-gate 			break;
43837c478bd9Sstevel@tonic-gate 		}
43847c478bd9Sstevel@tonic-gate 	}
43857c478bd9Sstevel@tonic-gate 
43867c478bd9Sstevel@tonic-gate 	*limit = (i == j) ? 0 : 1 << (j - i);
43877c478bd9Sstevel@tonic-gate 	*shift = i;
43887c478bd9Sstevel@tonic-gate }
43897c478bd9Sstevel@tonic-gate 
43907c478bd9Sstevel@tonic-gate 
43917c478bd9Sstevel@tonic-gate 
43927c478bd9Sstevel@tonic-gate /*
43937c478bd9Sstevel@tonic-gate  * power management:
43947c478bd9Sstevel@tonic-gate  *
43957c478bd9Sstevel@tonic-gate  * ugen_pm_init:
43967c478bd9Sstevel@tonic-gate  *	Initialize power management and remote wakeup functionality.
43977c478bd9Sstevel@tonic-gate  *	No mutex is necessary in this function as it's called only by attach.
43987c478bd9Sstevel@tonic-gate  */
43997c478bd9Sstevel@tonic-gate static void
44007c478bd9Sstevel@tonic-gate ugen_pm_init(ugen_state_t *ugenp)
44017c478bd9Sstevel@tonic-gate {
44027c478bd9Sstevel@tonic-gate 	dev_info_t	*dip = ugenp->ug_dip;
44037c478bd9Sstevel@tonic-gate 	ugen_power_t	*ugenpm;
44047c478bd9Sstevel@tonic-gate 
44057c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_PM, ugenp->ug_log_hdl,
44067c478bd9Sstevel@tonic-gate 	    "ugen_pm_init:");
44077c478bd9Sstevel@tonic-gate 
44087c478bd9Sstevel@tonic-gate 	/* Allocate the state structure */
44097c478bd9Sstevel@tonic-gate 	ugenpm = kmem_zalloc(sizeof (ugen_power_t), KM_SLEEP);
44107c478bd9Sstevel@tonic-gate 
44117c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
44127c478bd9Sstevel@tonic-gate 	ugenp->ug_pm = ugenpm;
44137c478bd9Sstevel@tonic-gate 	ugenpm->pwr_wakeup_enabled = B_FALSE;
44147c478bd9Sstevel@tonic-gate 	ugenpm->pwr_current = USB_DEV_OS_FULL_PWR;
44157c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
44167c478bd9Sstevel@tonic-gate 
44177c478bd9Sstevel@tonic-gate 	/*
44187c478bd9Sstevel@tonic-gate 	 * If remote wakeup is not available you may not want to do
44197c478bd9Sstevel@tonic-gate 	 * power management.
44207c478bd9Sstevel@tonic-gate 	 */
44217c478bd9Sstevel@tonic-gate 	if (ugen_enable_pm || usb_handle_remote_wakeup(dip,
44227c478bd9Sstevel@tonic-gate 	    USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) {
44237c478bd9Sstevel@tonic-gate 		if (usb_create_pm_components(dip,
44247c478bd9Sstevel@tonic-gate 		    &ugenpm->pwr_states) == USB_SUCCESS) {
44257c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L4(UGEN_PRINT_PM,
44267c478bd9Sstevel@tonic-gate 			    ugenp->ug_log_hdl,
44277c478bd9Sstevel@tonic-gate 			    "ugen_pm_init: "
44287c478bd9Sstevel@tonic-gate 			    "created PM components");
44297c478bd9Sstevel@tonic-gate 
44307c478bd9Sstevel@tonic-gate 			mutex_enter(&ugenp->ug_mutex);
44317c478bd9Sstevel@tonic-gate 			ugenpm->pwr_wakeup_enabled = B_TRUE;
44327c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
44337c478bd9Sstevel@tonic-gate 
44347c478bd9Sstevel@tonic-gate 			if (pm_raise_power(dip, 0,
44357c478bd9Sstevel@tonic-gate 			    USB_DEV_OS_FULL_PWR) != DDI_SUCCESS) {
44367c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(UGEN_PRINT_PM,
44377c478bd9Sstevel@tonic-gate 				    ugenp->ug_log_hdl,
44387c478bd9Sstevel@tonic-gate 				    "ugen_pm_init: "
44397c478bd9Sstevel@tonic-gate 				    "raising power failed");
44407c478bd9Sstevel@tonic-gate 			}
44417c478bd9Sstevel@tonic-gate 		} else {
44427c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(UGEN_PRINT_PM,
44437c478bd9Sstevel@tonic-gate 			    ugenp->ug_log_hdl,
44447c478bd9Sstevel@tonic-gate 			    "ugen_pm_init: "
44457c478bd9Sstevel@tonic-gate 			    "create_pm_comps failed");
44467c478bd9Sstevel@tonic-gate 		}
44477c478bd9Sstevel@tonic-gate 	} else {
44487c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_PM,
44497c478bd9Sstevel@tonic-gate 		    ugenp->ug_log_hdl, "ugen_pm_init: "
44507c478bd9Sstevel@tonic-gate 		    "failure enabling remote wakeup");
44517c478bd9Sstevel@tonic-gate 	}
44527c478bd9Sstevel@tonic-gate 
44537c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_PM, ugenp->ug_log_hdl,
44547c478bd9Sstevel@tonic-gate 	    "ugen_pm_init: end");
44557c478bd9Sstevel@tonic-gate }
44567c478bd9Sstevel@tonic-gate 
44577c478bd9Sstevel@tonic-gate 
44587c478bd9Sstevel@tonic-gate /*
44597c478bd9Sstevel@tonic-gate  * ugen_pm_destroy:
44607c478bd9Sstevel@tonic-gate  *	Shut down and destroy power management and remote wakeup functionality.
44617c478bd9Sstevel@tonic-gate  */
44627c478bd9Sstevel@tonic-gate static void
44637c478bd9Sstevel@tonic-gate ugen_pm_destroy(ugen_state_t *ugenp)
44647c478bd9Sstevel@tonic-gate {
44657c478bd9Sstevel@tonic-gate 	dev_info_t *dip = ugenp->ug_dip;
44667c478bd9Sstevel@tonic-gate 
44677c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_PM, ugenp->ug_log_hdl,
44687c478bd9Sstevel@tonic-gate 	    "ugen_pm_destroy:");
44697c478bd9Sstevel@tonic-gate 
44707c478bd9Sstevel@tonic-gate 	if (ugenp->ug_pm) {
44717c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
44727c478bd9Sstevel@tonic-gate 		ugen_pm_busy_component(ugenp);
44737c478bd9Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
44747c478bd9Sstevel@tonic-gate 
44757c478bd9Sstevel@tonic-gate 		if ((ugenp->ug_pm->pwr_wakeup_enabled) &&
44767c478bd9Sstevel@tonic-gate 		    (ugenp->ug_dev_state != USB_DEV_DISCONNECTED)) {
44777c478bd9Sstevel@tonic-gate 			int rval;
44787c478bd9Sstevel@tonic-gate 
44797c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
44807c478bd9Sstevel@tonic-gate 			(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
44817c478bd9Sstevel@tonic-gate 
44827c478bd9Sstevel@tonic-gate 			if ((rval = usb_handle_remote_wakeup(dip,
44837c478bd9Sstevel@tonic-gate 			    USB_REMOTE_WAKEUP_DISABLE)) != USB_SUCCESS) {
44847c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L4(UGEN_PRINT_PM,
44857c478bd9Sstevel@tonic-gate 				    ugenp->ug_log_hdl, "ugen_pm_destroy: "
44867c478bd9Sstevel@tonic-gate 				    "disabling rmt wakeup: rval=%d", rval);
44877c478bd9Sstevel@tonic-gate 			}
44887c478bd9Sstevel@tonic-gate 			/*
44897c478bd9Sstevel@tonic-gate 			 * Since remote wakeup is disabled now,
44907c478bd9Sstevel@tonic-gate 			 * no one can raise power
44917c478bd9Sstevel@tonic-gate 			 * and get to device once power is lowered here.
44927c478bd9Sstevel@tonic-gate 			 */
44937c478bd9Sstevel@tonic-gate 		} else {
44947c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
44957c478bd9Sstevel@tonic-gate 		}
44967c478bd9Sstevel@tonic-gate 		(void) pm_lower_power(dip, 0, USB_DEV_OS_PWR_OFF);
44977c478bd9Sstevel@tonic-gate 		ugen_pm_idle_component(ugenp);
44987c478bd9Sstevel@tonic-gate 
44997c478bd9Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
45007c478bd9Sstevel@tonic-gate 		kmem_free(ugenp->ug_pm, sizeof (ugen_power_t));
45017c478bd9Sstevel@tonic-gate 		ugenp->ug_pm = NULL;
45027c478bd9Sstevel@tonic-gate 	}
45037c478bd9Sstevel@tonic-gate }
45047c478bd9Sstevel@tonic-gate 
45057c478bd9Sstevel@tonic-gate 
45067c478bd9Sstevel@tonic-gate /*
45077c478bd9Sstevel@tonic-gate  * ugen_power :
45087c478bd9Sstevel@tonic-gate  *	Power entry point, the workhorse behind pm_raise_power, pm_lower_power,
45097c478bd9Sstevel@tonic-gate  *	usb_req_raise_power and usb_req_lower_power.
45107c478bd9Sstevel@tonic-gate  */
45117c478bd9Sstevel@tonic-gate /*ARGSUSED*/
45127c478bd9Sstevel@tonic-gate int
45137c478bd9Sstevel@tonic-gate usb_ugen_power(usb_ugen_hdl_t usb_ugen_hdl, int comp, int level)
45147c478bd9Sstevel@tonic-gate {
45157c478bd9Sstevel@tonic-gate 	ugen_power_t		*pm;
45167c478bd9Sstevel@tonic-gate 	int			rval = USB_FAILURE;
45177c478bd9Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
4518*0a05e705Slc 	    (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
45197c478bd9Sstevel@tonic-gate 	ugen_state_t		*ugenp;
45207c478bd9Sstevel@tonic-gate 	dev_info_t		*dip;
45217c478bd9Sstevel@tonic-gate 
45227c478bd9Sstevel@tonic-gate 	if (usb_ugen_hdl == NULL) {
45237c478bd9Sstevel@tonic-gate 
45247c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
45257c478bd9Sstevel@tonic-gate 	}
45267c478bd9Sstevel@tonic-gate 
45277c478bd9Sstevel@tonic-gate 	ugenp = usb_ugen_hdl_impl->hdl_ugenp;
45287c478bd9Sstevel@tonic-gate 	dip = ugenp->ug_dip;
45297c478bd9Sstevel@tonic-gate 
45307c478bd9Sstevel@tonic-gate 	if (ugenp->ug_pm == NULL) {
45317c478bd9Sstevel@tonic-gate 
45327c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
45337c478bd9Sstevel@tonic-gate 	}
45347c478bd9Sstevel@tonic-gate 
45357c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_PM, ugenp->ug_log_hdl,
45367c478bd9Sstevel@tonic-gate 	    "usb_ugen_power: level=%d", level);
45377c478bd9Sstevel@tonic-gate 
45387c478bd9Sstevel@tonic-gate 	(void) usb_serialize_access(ugenp->ug_ser_cookie,
4539*0a05e705Slc 	    USB_WAIT, 0);
45407c478bd9Sstevel@tonic-gate 	/*
45417c478bd9Sstevel@tonic-gate 	 * If we are disconnected/suspended, return success. Note that if we
45427c478bd9Sstevel@tonic-gate 	 * return failure, bringing down the system will hang when
45437c478bd9Sstevel@tonic-gate 	 * PM tries to power up all devices
45447c478bd9Sstevel@tonic-gate 	 */
45457c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
45467c478bd9Sstevel@tonic-gate 	switch (ugenp->ug_dev_state) {
45477c478bd9Sstevel@tonic-gate 	case USB_DEV_ONLINE:
45487c478bd9Sstevel@tonic-gate 
45497c478bd9Sstevel@tonic-gate 		break;
45507c478bd9Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
45517c478bd9Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
45527c478bd9Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RESUME:
45537c478bd9Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
45547c478bd9Sstevel@tonic-gate 	default:
45557c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_PM, ugenp->ug_log_hdl,
45567c478bd9Sstevel@tonic-gate 		    "ugen_power: disconnected/suspended "
45577c478bd9Sstevel@tonic-gate 		    "dev_state=%d", ugenp->ug_dev_state);
45587c478bd9Sstevel@tonic-gate 		rval = USB_SUCCESS;
45597c478bd9Sstevel@tonic-gate 
45607c478bd9Sstevel@tonic-gate 		goto done;
45617c478bd9Sstevel@tonic-gate 	}
45627c478bd9Sstevel@tonic-gate 
45637c478bd9Sstevel@tonic-gate 	pm = ugenp->ug_pm;
45647c478bd9Sstevel@tonic-gate 
45657c478bd9Sstevel@tonic-gate 	/* Check if we are transitioning to a legal power level */
45667c478bd9Sstevel@tonic-gate 	if (USB_DEV_PWRSTATE_OK(pm->pwr_states, level)) {
45677c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_PM, ugenp->ug_log_hdl,
45687c478bd9Sstevel@tonic-gate 		    "ugen_power: illegal power level=%d "
45697c478bd9Sstevel@tonic-gate 		    "pwr_states: 0x%x", level, pm->pwr_states);
45707c478bd9Sstevel@tonic-gate 
45717c478bd9Sstevel@tonic-gate 		goto done;
45727c478bd9Sstevel@tonic-gate 	}
45737c478bd9Sstevel@tonic-gate 
45747c478bd9Sstevel@tonic-gate 	switch (level) {
45757c478bd9Sstevel@tonic-gate 	case USB_DEV_OS_PWR_OFF :
45767c478bd9Sstevel@tonic-gate 		switch (ugenp->ug_dev_state) {
45777c478bd9Sstevel@tonic-gate 		case USB_DEV_ONLINE:
45787c478bd9Sstevel@tonic-gate 			/* Deny the powerdown request if the device is busy */
45797c478bd9Sstevel@tonic-gate 			if (ugenp->ug_pm->pwr_busy != 0) {
45807c478bd9Sstevel@tonic-gate 
45817c478bd9Sstevel@tonic-gate 				break;
45827c478bd9Sstevel@tonic-gate 			}
45837c478bd9Sstevel@tonic-gate 			ASSERT(ugenp->ug_open_count == 0);
45847c478bd9Sstevel@tonic-gate 			ASSERT(ugenp->ug_pending_cmds == 0);
45857c478bd9Sstevel@tonic-gate 			ugenp->ug_pm->pwr_current = USB_DEV_OS_PWR_OFF;
45867c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
45877c478bd9Sstevel@tonic-gate 
45887c478bd9Sstevel@tonic-gate 			/* Issue USB D3 command to the device here */
45897c478bd9Sstevel@tonic-gate 			rval = usb_set_device_pwrlvl3(dip);
45907c478bd9Sstevel@tonic-gate 			mutex_enter(&ugenp->ug_mutex);
45917c478bd9Sstevel@tonic-gate 
45927c478bd9Sstevel@tonic-gate 			break;
45937c478bd9Sstevel@tonic-gate 		default:
45947c478bd9Sstevel@tonic-gate 			rval = USB_SUCCESS;
45957c478bd9Sstevel@tonic-gate 
45967c478bd9Sstevel@tonic-gate 			break;
45977c478bd9Sstevel@tonic-gate 		}
45987c478bd9Sstevel@tonic-gate 		break;
45997c478bd9Sstevel@tonic-gate 	case USB_DEV_OS_FULL_PWR :
46007c478bd9Sstevel@tonic-gate 		/*
46017c478bd9Sstevel@tonic-gate 		 * PM framework tries to put us in full power during system
46027c478bd9Sstevel@tonic-gate 		 * shutdown.
46037c478bd9Sstevel@tonic-gate 		 */
46047c478bd9Sstevel@tonic-gate 		switch (ugenp->ug_dev_state) {
46057c478bd9Sstevel@tonic-gate 		case USB_UGEN_DEV_UNAVAILABLE_RESUME:
46067c478bd9Sstevel@tonic-gate 		case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
46077c478bd9Sstevel@tonic-gate 
46087c478bd9Sstevel@tonic-gate 			break;
46097c478bd9Sstevel@tonic-gate 		default:
46107c478bd9Sstevel@tonic-gate 			ugenp->ug_dev_state = USB_DEV_ONLINE;
46117c478bd9Sstevel@tonic-gate 
46127c478bd9Sstevel@tonic-gate 			/* wakeup devstat reads and polls */
46137c478bd9Sstevel@tonic-gate 			ugen_ds_change(ugenp);
46147c478bd9Sstevel@tonic-gate 			ugen_ds_poll_wakeup(ugenp);
46157c478bd9Sstevel@tonic-gate 
46167c478bd9Sstevel@tonic-gate 			break;
46177c478bd9Sstevel@tonic-gate 		}
46187c478bd9Sstevel@tonic-gate 		ugenp->ug_pm->pwr_current = USB_DEV_OS_FULL_PWR;
46197c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
46207c478bd9Sstevel@tonic-gate 		rval = usb_set_device_pwrlvl0(dip);
46217c478bd9Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
46227c478bd9Sstevel@tonic-gate 
46237c478bd9Sstevel@tonic-gate 		break;
46247c478bd9Sstevel@tonic-gate 	default:
46257c478bd9Sstevel@tonic-gate 		/* Levels 1 and 2 are not supported to keep it simple. */
46267c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_PM, ugenp->ug_log_hdl,
46277c478bd9Sstevel@tonic-gate 		    "ugen_power: power level %d not supported", level);
46287c478bd9Sstevel@tonic-gate 
46297c478bd9Sstevel@tonic-gate 		break;
46307c478bd9Sstevel@tonic-gate 	}
46317c478bd9Sstevel@tonic-gate done:
46327c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
46337c478bd9Sstevel@tonic-gate 	usb_release_access(ugenp->ug_ser_cookie);
46347c478bd9Sstevel@tonic-gate 
46357c478bd9Sstevel@tonic-gate 	return (rval);
46367c478bd9Sstevel@tonic-gate }
46377c478bd9Sstevel@tonic-gate 
46387c478bd9Sstevel@tonic-gate 
46397c478bd9Sstevel@tonic-gate static void
46407c478bd9Sstevel@tonic-gate ugen_pm_busy_component(ugen_state_t *ugen_statep)
46417c478bd9Sstevel@tonic-gate {
46427c478bd9Sstevel@tonic-gate 	ASSERT(!mutex_owned(&ugen_statep->ug_mutex));
46437c478bd9Sstevel@tonic-gate 
46447c478bd9Sstevel@tonic-gate 	if (ugen_statep->ug_pm != NULL) {
46457c478bd9Sstevel@tonic-gate 		mutex_enter(&ugen_statep->ug_mutex);
46467c478bd9Sstevel@tonic-gate 		ugen_statep->ug_pm->pwr_busy++;
46477c478bd9Sstevel@tonic-gate 
46487c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_PM, ugen_statep->ug_log_hdl,
46497c478bd9Sstevel@tonic-gate 		    "ugen_pm_busy_component: %d", ugen_statep->ug_pm->pwr_busy);
46507c478bd9Sstevel@tonic-gate 
46517c478bd9Sstevel@tonic-gate 		mutex_exit(&ugen_statep->ug_mutex);
46527c478bd9Sstevel@tonic-gate 		if (pm_busy_component(ugen_statep->ug_dip, 0) != DDI_SUCCESS) {
46537c478bd9Sstevel@tonic-gate 			mutex_enter(&ugen_statep->ug_mutex);
46547c478bd9Sstevel@tonic-gate 			ugen_statep->ug_pm->pwr_busy--;
46557c478bd9Sstevel@tonic-gate 
46567c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(UGEN_PRINT_PM, ugen_statep->ug_log_hdl,
46577c478bd9Sstevel@tonic-gate 			    "ugen_pm_busy_component failed: %d",
46587c478bd9Sstevel@tonic-gate 			    ugen_statep->ug_pm->pwr_busy);
46597c478bd9Sstevel@tonic-gate 
46607c478bd9Sstevel@tonic-gate 			mutex_exit(&ugen_statep->ug_mutex);
46617c478bd9Sstevel@tonic-gate 		}
46627c478bd9Sstevel@tonic-gate 	}
46637c478bd9Sstevel@tonic-gate }
46647c478bd9Sstevel@tonic-gate 
46657c478bd9Sstevel@tonic-gate 
46667c478bd9Sstevel@tonic-gate static void
46677c478bd9Sstevel@tonic-gate ugen_pm_idle_component(ugen_state_t *ugen_statep)
46687c478bd9Sstevel@tonic-gate {
46697c478bd9Sstevel@tonic-gate 	ASSERT(!mutex_owned(&ugen_statep->ug_mutex));
46707c478bd9Sstevel@tonic-gate 
46717c478bd9Sstevel@tonic-gate 	if (ugen_statep->ug_pm != NULL) {
46727c478bd9Sstevel@tonic-gate 		if (pm_idle_component(ugen_statep->ug_dip, 0) == DDI_SUCCESS) {
46737c478bd9Sstevel@tonic-gate 			mutex_enter(&ugen_statep->ug_mutex);
46747c478bd9Sstevel@tonic-gate 			ASSERT(ugen_statep->ug_pm->pwr_busy > 0);
46757c478bd9Sstevel@tonic-gate 			ugen_statep->ug_pm->pwr_busy--;
46767c478bd9Sstevel@tonic-gate 
46777c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L4(UGEN_PRINT_PM, ugen_statep->ug_log_hdl,
46787c478bd9Sstevel@tonic-gate 			    "ugen_pm_idle_component: %d",
46797c478bd9Sstevel@tonic-gate 			    ugen_statep->ug_pm->pwr_busy);
46807c478bd9Sstevel@tonic-gate 
46817c478bd9Sstevel@tonic-gate 			mutex_exit(&ugen_statep->ug_mutex);
46827c478bd9Sstevel@tonic-gate 		}
46837c478bd9Sstevel@tonic-gate 	}
46847c478bd9Sstevel@tonic-gate }
46857c478bd9Sstevel@tonic-gate 
46867c478bd9Sstevel@tonic-gate 
46877c478bd9Sstevel@tonic-gate /*
46887c478bd9Sstevel@tonic-gate  * devt lookup support
46897c478bd9Sstevel@tonic-gate  *	In ugen_strategy and ugen_minphys, we only have the devt and need
46907c478bd9Sstevel@tonic-gate  *	the ugen_state pointer. Since we don't know instance mask, we can't
46917c478bd9Sstevel@tonic-gate  *	easily derive a softstate pointer. Therefore, we use a list
46927c478bd9Sstevel@tonic-gate  */
46937c478bd9Sstevel@tonic-gate static void
46947c478bd9Sstevel@tonic-gate ugen_store_devt(ugen_state_t *ugenp, minor_t minor)
46957c478bd9Sstevel@tonic-gate {
46967c478bd9Sstevel@tonic-gate 	ugen_devt_list_entry_t *e = kmem_zalloc(
4697*0a05e705Slc 	    sizeof (ugen_devt_list_entry_t), KM_SLEEP);
46987c478bd9Sstevel@tonic-gate 	ugen_devt_list_entry_t *t;
46997c478bd9Sstevel@tonic-gate 
47007c478bd9Sstevel@tonic-gate 	mutex_enter(&ugen_devt_list_mutex);
47017c478bd9Sstevel@tonic-gate 	e->list_dev = makedevice(ddi_driver_major(ugenp->ug_dip), minor);
47027c478bd9Sstevel@tonic-gate 	e->list_state = ugenp;
47037c478bd9Sstevel@tonic-gate 
47047c478bd9Sstevel@tonic-gate 	t = ugen_devt_list.list_next;
47057c478bd9Sstevel@tonic-gate 
47067c478bd9Sstevel@tonic-gate 	/* check if the entry is already in the list */
47077c478bd9Sstevel@tonic-gate 	while (t) {
47087c478bd9Sstevel@tonic-gate 		ASSERT(t->list_dev != e->list_dev);
47097c478bd9Sstevel@tonic-gate 		t = t->list_next;
47107c478bd9Sstevel@tonic-gate 	}
47117c478bd9Sstevel@tonic-gate 
47127c478bd9Sstevel@tonic-gate 	/* add to the head of the list */
47137c478bd9Sstevel@tonic-gate 	e->list_next = ugen_devt_list.list_next;
47147c478bd9Sstevel@tonic-gate 	if (ugen_devt_list.list_next) {
47157c478bd9Sstevel@tonic-gate 		ugen_devt_list.list_next->list_prev = e;
47167c478bd9Sstevel@tonic-gate 	}
47177c478bd9Sstevel@tonic-gate 	ugen_devt_list.list_next = e;
47187c478bd9Sstevel@tonic-gate 	mutex_exit(&ugen_devt_list_mutex);
47197c478bd9Sstevel@tonic-gate }
47207c478bd9Sstevel@tonic-gate 
47217c478bd9Sstevel@tonic-gate 
47227c478bd9Sstevel@tonic-gate static ugen_state_t *
47237c478bd9Sstevel@tonic-gate ugen_devt2state(dev_t dev)
47247c478bd9Sstevel@tonic-gate {
47257c478bd9Sstevel@tonic-gate 	ugen_devt_list_entry_t *t;
47267c478bd9Sstevel@tonic-gate 	ugen_state_t	*ugenp = NULL;
47277c478bd9Sstevel@tonic-gate 	int		index, count;
47287c478bd9Sstevel@tonic-gate 
47297c478bd9Sstevel@tonic-gate 	mutex_enter(&ugen_devt_list_mutex);
47307c478bd9Sstevel@tonic-gate 
47317c478bd9Sstevel@tonic-gate 	for (index = ugen_devt_cache_index, count = 0;
47327c478bd9Sstevel@tonic-gate 	    count < UGEN_DEVT_CACHE_SIZE; count++) {
47337c478bd9Sstevel@tonic-gate 		if (ugen_devt_cache[index].cache_dev == dev) {
47347c478bd9Sstevel@tonic-gate 			ugen_devt_cache[index].cache_hit++;
47357c478bd9Sstevel@tonic-gate 			ugenp = ugen_devt_cache[index].cache_state;
47367c478bd9Sstevel@tonic-gate 
47377c478bd9Sstevel@tonic-gate 			mutex_exit(&ugen_devt_list_mutex);
47387c478bd9Sstevel@tonic-gate 
47397c478bd9Sstevel@tonic-gate 			return (ugenp);
47407c478bd9Sstevel@tonic-gate 		}
47417c478bd9Sstevel@tonic-gate 		index++;
47427c478bd9Sstevel@tonic-gate 		index %= UGEN_DEVT_CACHE_SIZE;
47437c478bd9Sstevel@tonic-gate 	}
47447c478bd9Sstevel@tonic-gate 
47457c478bd9Sstevel@tonic-gate 	t = ugen_devt_list.list_next;
47467c478bd9Sstevel@tonic-gate 
47477c478bd9Sstevel@tonic-gate 	while (t) {
47487c478bd9Sstevel@tonic-gate 		if (t->list_dev == dev) {
47497c478bd9Sstevel@tonic-gate 			ugenp = t->list_state;
47507c478bd9Sstevel@tonic-gate 			ugen_devt_cache_index++;
47517c478bd9Sstevel@tonic-gate 			ugen_devt_cache_index %= UGEN_DEVT_CACHE_SIZE;
47527c478bd9Sstevel@tonic-gate 			ugen_devt_cache[ugen_devt_cache_index].cache_dev = dev;
47537c478bd9Sstevel@tonic-gate 			ugen_devt_cache[ugen_devt_cache_index].cache_state =
4754*0a05e705Slc 			    ugenp;
47557c478bd9Sstevel@tonic-gate 			mutex_exit(&ugen_devt_list_mutex);
47567c478bd9Sstevel@tonic-gate 
47577c478bd9Sstevel@tonic-gate 			return (ugenp);
47587c478bd9Sstevel@tonic-gate 		}
47597c478bd9Sstevel@tonic-gate 		t = t->list_next;
47607c478bd9Sstevel@tonic-gate 	}
47617c478bd9Sstevel@tonic-gate 	mutex_exit(&ugen_devt_list_mutex);
47627c478bd9Sstevel@tonic-gate 
47637c478bd9Sstevel@tonic-gate 	return (ugenp);
47647c478bd9Sstevel@tonic-gate }
47657c478bd9Sstevel@tonic-gate 
47667c478bd9Sstevel@tonic-gate 
47677c478bd9Sstevel@tonic-gate static void
47687c478bd9Sstevel@tonic-gate ugen_free_devt(ugen_state_t *ugenp)
47697c478bd9Sstevel@tonic-gate {
47707c478bd9Sstevel@tonic-gate 	ugen_devt_list_entry_t *e, *next, *prev;
47717c478bd9Sstevel@tonic-gate 	major_t		major = ddi_driver_major(ugenp->ug_dip);
47727c478bd9Sstevel@tonic-gate 	int		instance = ddi_get_instance(ugenp->ug_dip);
47737c478bd9Sstevel@tonic-gate 
47747c478bd9Sstevel@tonic-gate 	mutex_enter(&ugen_devt_list_mutex);
47757c478bd9Sstevel@tonic-gate 	prev = &ugen_devt_list;
47767c478bd9Sstevel@tonic-gate 	for (e = prev->list_next; e != 0; e = next) {
47777c478bd9Sstevel@tonic-gate 		int i = (getminor(e->list_dev) &
4778*0a05e705Slc 		    ugenp->ug_hdl->hdl_minor_node_instance_mask) >>
4779*0a05e705Slc 		    ugenp->ug_hdl->hdl_minor_node_instance_shift;
47807c478bd9Sstevel@tonic-gate 		int m = getmajor(e->list_dev);
47817c478bd9Sstevel@tonic-gate 
47827c478bd9Sstevel@tonic-gate 		next = e->list_next;
47837c478bd9Sstevel@tonic-gate 
47847c478bd9Sstevel@tonic-gate 		if ((i == instance) && (m == major)) {
47857c478bd9Sstevel@tonic-gate 			prev->list_next = e->list_next;
47867c478bd9Sstevel@tonic-gate 			if (e->list_next) {
47877c478bd9Sstevel@tonic-gate 				e->list_next->list_prev = prev;
47887c478bd9Sstevel@tonic-gate 			}
47897c478bd9Sstevel@tonic-gate 			kmem_free(e, sizeof (ugen_devt_list_entry_t));
47907c478bd9Sstevel@tonic-gate 		} else {
47917c478bd9Sstevel@tonic-gate 			prev = e;
47927c478bd9Sstevel@tonic-gate 		}
47937c478bd9Sstevel@tonic-gate 	}
47947c478bd9Sstevel@tonic-gate 
47957c478bd9Sstevel@tonic-gate 	bzero(ugen_devt_cache, sizeof (ugen_devt_cache));
47967c478bd9Sstevel@tonic-gate 	ugen_devt_cache_index = 0;
47977c478bd9Sstevel@tonic-gate 	mutex_exit(&ugen_devt_list_mutex);
47987c478bd9Sstevel@tonic-gate }
4799