xref: /onnv-gate/usr/src/uts/common/io/usb/usba/usba_ugen.c (revision 7632:91aa3d8541b5)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
55653Slc152243  * Common Development and Distribution License (the "License").
65653Slc152243  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  *
216898Sfb209375  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
220Sstevel@tonic-gate  * Use is subject to license terms.
230Sstevel@tonic-gate  */
240Sstevel@tonic-gate 
250Sstevel@tonic-gate /*
260Sstevel@tonic-gate  * UGEN: USB Generic Driver support code
270Sstevel@tonic-gate  *
280Sstevel@tonic-gate  * This code provides entry points called by the ugen driver or other
290Sstevel@tonic-gate  * drivers that want to export a ugen interface
300Sstevel@tonic-gate  *
310Sstevel@tonic-gate  * The "Universal Generic Driver"  (UGEN) for USB devices provides interfaces
320Sstevel@tonic-gate  * to  talk to	USB  devices.  This is	very  useful for  Point of Sale sale
330Sstevel@tonic-gate  * devices and other simple  devices like  USB	scanner, USB palm  pilot.
340Sstevel@tonic-gate  * The UGEN provides a system call interface to USB  devices  enabling
350Sstevel@tonic-gate  * a USB device vendor to  write an  application for his
360Sstevel@tonic-gate  * device instead of  writing a driver. This facilitates the vendor to write
370Sstevel@tonic-gate  * device management s/w quickly in userland.
380Sstevel@tonic-gate  *
390Sstevel@tonic-gate  * UGEN supports read/write/poll entry points. An application can be written
400Sstevel@tonic-gate  * using  read/write/aioread/aiowrite/poll  system calls to communicate
410Sstevel@tonic-gate  * with the device.
420Sstevel@tonic-gate  *
430Sstevel@tonic-gate  * XXX Theory of Operations
440Sstevel@tonic-gate  */
450Sstevel@tonic-gate #include <sys/usb/usba/usbai_version.h>
460Sstevel@tonic-gate #include <sys/usb/usba.h>
470Sstevel@tonic-gate #include <sys/sysmacros.h>
487492SZhigang.Lu@Sun.COM #include <sys/strsun.h>
490Sstevel@tonic-gate 
500Sstevel@tonic-gate #include "sys/usb/clients/ugen/usb_ugen.h"
510Sstevel@tonic-gate #include "sys/usb/usba/usba_ugen.h"
520Sstevel@tonic-gate #include "sys/usb/usba/usba_ugend.h"
530Sstevel@tonic-gate 
540Sstevel@tonic-gate /* Debugging information */
55880Sfrits uint_t	ugen_errmask		= (uint_t)UGEN_PRINT_ALL;
56880Sfrits uint_t	ugen_errlevel		= USB_LOG_L4;
57880Sfrits uint_t	ugen_instance_debug	= (uint_t)-1;
580Sstevel@tonic-gate 
590Sstevel@tonic-gate /* default endpoint descriptor */
600Sstevel@tonic-gate static usb_ep_descr_t  ugen_default_ep_descr =
610Sstevel@tonic-gate 	{7, 5, 0, USB_EP_ATTR_CONTROL, 8, 0};
620Sstevel@tonic-gate 
630Sstevel@tonic-gate /* tunables */
64880Sfrits int	ugen_busy_loop		= 60;	/* secs */
65880Sfrits int	ugen_ctrl_timeout	= 10;
66880Sfrits int	ugen_bulk_timeout	= 10;
67880Sfrits int	ugen_intr_timeout	= 10;
68880Sfrits int	ugen_enable_pm		= 0;
695653Slc152243 int	ugen_isoc_buf_limit	= 1000;	/* ms */
700Sstevel@tonic-gate 
710Sstevel@tonic-gate 
720Sstevel@tonic-gate /* local function prototypes */
730Sstevel@tonic-gate static int	ugen_cleanup(ugen_state_t *);
740Sstevel@tonic-gate static int	ugen_cpr_suspend(ugen_state_t *);
750Sstevel@tonic-gate static void	ugen_cpr_resume(ugen_state_t *);
760Sstevel@tonic-gate 
770Sstevel@tonic-gate static void	ugen_restore_state(ugen_state_t *);
780Sstevel@tonic-gate static int	ugen_check_open_flags(ugen_state_t *, dev_t, int);
790Sstevel@tonic-gate static int	ugen_strategy(struct buf *);
800Sstevel@tonic-gate static void	ugen_minphys(struct buf *);
810Sstevel@tonic-gate 
820Sstevel@tonic-gate static void	ugen_pm_init(ugen_state_t *);
830Sstevel@tonic-gate static void	ugen_pm_destroy(ugen_state_t *);
840Sstevel@tonic-gate static void	ugen_pm_busy_component(ugen_state_t *);
850Sstevel@tonic-gate static void	ugen_pm_idle_component(ugen_state_t *);
860Sstevel@tonic-gate 
870Sstevel@tonic-gate /* endpoint xfer and status management */
880Sstevel@tonic-gate static int	ugen_epxs_init(ugen_state_t *);
890Sstevel@tonic-gate static void	ugen_epxs_destroy(ugen_state_t *);
900Sstevel@tonic-gate static int	ugen_epxs_data_init(ugen_state_t *, usb_ep_data_t *,
910Sstevel@tonic-gate 					uchar_t, uchar_t, uchar_t, uchar_t);
920Sstevel@tonic-gate static void	ugen_epxs_data_destroy(ugen_state_t *, ugen_ep_t *);
930Sstevel@tonic-gate static int	ugen_epxs_minor_nodes_create(ugen_state_t *,
940Sstevel@tonic-gate 					usb_ep_descr_t *, uchar_t,
950Sstevel@tonic-gate 					uchar_t, uchar_t, uchar_t);
960Sstevel@tonic-gate static int	ugen_epxs_check_open_nodes(ugen_state_t *);
970Sstevel@tonic-gate 
980Sstevel@tonic-gate static int	ugen_epx_open(ugen_state_t *, dev_t, int);
990Sstevel@tonic-gate static void	ugen_epx_close(ugen_state_t *, dev_t, int);
1000Sstevel@tonic-gate static void	ugen_epx_shutdown(ugen_state_t *);
1010Sstevel@tonic-gate 
1020Sstevel@tonic-gate static int	ugen_epx_open_pipe(ugen_state_t *, ugen_ep_t *, int);
1030Sstevel@tonic-gate static void	ugen_epx_close_pipe(ugen_state_t *, ugen_ep_t *);
1040Sstevel@tonic-gate 
1050Sstevel@tonic-gate static int	ugen_epx_req(ugen_state_t *, struct buf *);
1060Sstevel@tonic-gate static int	ugen_epx_ctrl_req(ugen_state_t *, ugen_ep_t *,
1070Sstevel@tonic-gate 					struct buf *, boolean_t *);
1080Sstevel@tonic-gate static void	ugen_epx_ctrl_req_cb(usb_pipe_handle_t, usb_ctrl_req_t *);
1090Sstevel@tonic-gate static int	ugen_epx_bulk_req(ugen_state_t *, ugen_ep_t *,
1100Sstevel@tonic-gate 					struct buf *, boolean_t *);
1110Sstevel@tonic-gate static void	ugen_epx_bulk_req_cb(usb_pipe_handle_t, usb_bulk_req_t *);
1120Sstevel@tonic-gate static int	ugen_epx_intr_IN_req(ugen_state_t *, ugen_ep_t *,
1130Sstevel@tonic-gate 					struct buf *, boolean_t *);
1140Sstevel@tonic-gate static int	ugen_epx_intr_IN_start_polling(ugen_state_t *, ugen_ep_t *);
1150Sstevel@tonic-gate static void	ugen_epx_intr_IN_stop_polling(ugen_state_t *, ugen_ep_t *);
1160Sstevel@tonic-gate static void	ugen_epx_intr_IN_req_cb(usb_pipe_handle_t, usb_intr_req_t *);
1170Sstevel@tonic-gate static int	ugen_epx_intr_OUT_req(ugen_state_t *, ugen_ep_t *,
1180Sstevel@tonic-gate 					struct buf *, boolean_t *);
1190Sstevel@tonic-gate static void	ugen_epx_intr_OUT_req_cb(usb_pipe_handle_t, usb_intr_req_t *);
1205653Slc152243 static int	ugen_epx_isoc_IN_req(ugen_state_t *, ugen_ep_t *,
1215653Slc152243 					struct buf *, boolean_t *);
1225653Slc152243 static int	ugen_epx_isoc_IN_start_polling(ugen_state_t *, ugen_ep_t *);
1235653Slc152243 static void	ugen_epx_isoc_IN_stop_polling(ugen_state_t *, ugen_ep_t *);
1245653Slc152243 static void	ugen_epx_isoc_IN_req_cb(usb_pipe_handle_t, usb_isoc_req_t *);
1255653Slc152243 static int	ugen_epx_isoc_OUT_req(ugen_state_t *, ugen_ep_t *,
1265653Slc152243 					struct buf *, boolean_t *);
1275653Slc152243 static void	ugen_epx_isoc_OUT_req_cb(usb_pipe_handle_t, usb_isoc_req_t *);
1280Sstevel@tonic-gate 
1290Sstevel@tonic-gate static int	ugen_eps_open(ugen_state_t *, dev_t, int);
1300Sstevel@tonic-gate static void	ugen_eps_close(ugen_state_t *, dev_t, int);
1310Sstevel@tonic-gate static int	ugen_eps_req(ugen_state_t *, struct buf *);
1320Sstevel@tonic-gate static void	ugen_update_ep_descr(ugen_state_t *, ugen_ep_t *);
1330Sstevel@tonic-gate 
1340Sstevel@tonic-gate /* device status management */
1350Sstevel@tonic-gate static int	ugen_ds_init(ugen_state_t *);
1360Sstevel@tonic-gate static void	ugen_ds_destroy(ugen_state_t *);
1370Sstevel@tonic-gate static int	ugen_ds_open(ugen_state_t *, dev_t, int);
1380Sstevel@tonic-gate static void	ugen_ds_close(ugen_state_t *, dev_t, int);
1390Sstevel@tonic-gate static int	ugen_ds_req(ugen_state_t *, struct buf *);
1400Sstevel@tonic-gate static void	ugen_ds_change(ugen_state_t *);
1410Sstevel@tonic-gate static int	ugen_ds_minor_nodes_create(ugen_state_t *);
1420Sstevel@tonic-gate static void	ugen_ds_poll_wakeup(ugen_state_t *);
1430Sstevel@tonic-gate 
1440Sstevel@tonic-gate /* utility functions */
1450Sstevel@tonic-gate static int	ugen_minor_index_create(ugen_state_t *, ugen_minor_t);
1460Sstevel@tonic-gate static ugen_minor_t ugen_devt2minor(ugen_state_t *, dev_t);
1470Sstevel@tonic-gate static void	ugen_minor_node_table_create(ugen_state_t *);
1480Sstevel@tonic-gate static void	ugen_minor_node_table_destroy(ugen_state_t *);
1490Sstevel@tonic-gate static void	ugen_minor_node_table_shrink(ugen_state_t *);
1500Sstevel@tonic-gate static int	ugen_cr2lcstat(int);
1510Sstevel@tonic-gate static void	ugen_check_mask(uint_t, uint_t *, uint_t *);
15270Sfrits static int	ugen_is_valid_minor_node(ugen_state_t *, dev_t);
1530Sstevel@tonic-gate 
1540Sstevel@tonic-gate static kmutex_t	ugen_devt_list_mutex;
1550Sstevel@tonic-gate static ugen_devt_list_entry_t ugen_devt_list;
1560Sstevel@tonic-gate static ugen_devt_cache_entry_t ugen_devt_cache[UGEN_DEVT_CACHE_SIZE];
1570Sstevel@tonic-gate static uint_t	ugen_devt_cache_index;
1580Sstevel@tonic-gate static void	ugen_store_devt(ugen_state_t *, minor_t);
1590Sstevel@tonic-gate static ugen_state_t *ugen_devt2state(dev_t);
1600Sstevel@tonic-gate static void	ugen_free_devt(ugen_state_t *);
1610Sstevel@tonic-gate 
1620Sstevel@tonic-gate /*
1630Sstevel@tonic-gate  * usb_ugen entry points
1640Sstevel@tonic-gate  *
1650Sstevel@tonic-gate  * usb_ugen_get_hdl:
1660Sstevel@tonic-gate  *	allocate and initialize handle
1670Sstevel@tonic-gate  */
1680Sstevel@tonic-gate usb_ugen_hdl_t
usb_ugen_get_hdl(dev_info_t * dip,usb_ugen_info_t * usb_ugen_info)1690Sstevel@tonic-gate usb_ugen_get_hdl(dev_info_t *dip, usb_ugen_info_t *usb_ugen_info)
1700Sstevel@tonic-gate {
1710Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*hdl = kmem_zalloc(sizeof (*hdl), KM_SLEEP);
1720Sstevel@tonic-gate 	ugen_state_t		*ugenp = kmem_zalloc(sizeof (ugen_state_t),
1735653Slc152243 	    KM_SLEEP);
1740Sstevel@tonic-gate 	uint_t			len, shift, limit;
1750Sstevel@tonic-gate 	int			rval;
1760Sstevel@tonic-gate 
1770Sstevel@tonic-gate 	hdl->hdl_ugenp = ugenp;
1780Sstevel@tonic-gate 
1790Sstevel@tonic-gate 	/* masks may not overlap */
1800Sstevel@tonic-gate 	if (usb_ugen_info->usb_ugen_minor_node_ugen_bits_mask &
1810Sstevel@tonic-gate 	    usb_ugen_info->usb_ugen_minor_node_instance_mask) {
1820Sstevel@tonic-gate 		usb_ugen_release_hdl((usb_ugen_hdl_t)hdl);
1830Sstevel@tonic-gate 
1840Sstevel@tonic-gate 		return (NULL);
1850Sstevel@tonic-gate 	}
1860Sstevel@tonic-gate 
1870Sstevel@tonic-gate 	if ((rval = usb_get_dev_data(dip, &ugenp->ug_dev_data,
1880Sstevel@tonic-gate 	    usb_owns_device(dip) ? USB_PARSE_LVL_ALL : USB_PARSE_LVL_IF,
1890Sstevel@tonic-gate 	    0)) != USB_SUCCESS) {
1900Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
1910Sstevel@tonic-gate 		    "usb_ugen_attach: usb_get_dev_data failed, rval=%d", rval);
1920Sstevel@tonic-gate 
1930Sstevel@tonic-gate 		return (NULL);
1940Sstevel@tonic-gate 	}
1950Sstevel@tonic-gate 
1960Sstevel@tonic-gate 	/* Initialize state structure for this instance */
1970Sstevel@tonic-gate 	mutex_init(&ugenp->ug_mutex, NULL, MUTEX_DRIVER,
1985653Slc152243 	    ugenp->ug_dev_data->dev_iblock_cookie);
1990Sstevel@tonic-gate 
2000Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
2010Sstevel@tonic-gate 	ugenp->ug_dip		= dip;
2020Sstevel@tonic-gate 	ugenp->ug_instance	= ddi_get_instance(dip);
2030Sstevel@tonic-gate 	ugenp->ug_hdl		= hdl;
2040Sstevel@tonic-gate 
2050Sstevel@tonic-gate 	/* Allocate a log handle for debug/error messages */
2060Sstevel@tonic-gate 	if (strcmp(ddi_driver_name(dip), "ugen") != 0) {
2070Sstevel@tonic-gate 		char	*name;
2080Sstevel@tonic-gate 
2090Sstevel@tonic-gate 		len = strlen(ddi_driver_name(dip)) + sizeof ("_ugen") + 1;
2100Sstevel@tonic-gate 		name = kmem_alloc(len, KM_SLEEP);
2110Sstevel@tonic-gate 		(void) snprintf(name, len, "%s_ugen", ddi_driver_name(dip));
2120Sstevel@tonic-gate 
2130Sstevel@tonic-gate 		ugenp->ug_log_hdl = usb_alloc_log_hdl(dip, name, &ugen_errlevel,
2145653Slc152243 		    &ugen_errmask, &ugen_instance_debug, 0);
2150Sstevel@tonic-gate 		hdl->hdl_log_name = name;
2160Sstevel@tonic-gate 		hdl->hdl_log_name_length = len;
2170Sstevel@tonic-gate 	} else {
2180Sstevel@tonic-gate 		ugenp->ug_log_hdl = usb_alloc_log_hdl(dip, "ugen",
2195653Slc152243 		    &ugen_errlevel,
2205653Slc152243 		    &ugen_errmask, &ugen_instance_debug, 0);
2210Sstevel@tonic-gate 	}
2220Sstevel@tonic-gate 
2230Sstevel@tonic-gate 	hdl->hdl_dip = dip;
2240Sstevel@tonic-gate 	hdl->hdl_flags = usb_ugen_info->usb_ugen_flags;
2250Sstevel@tonic-gate 
2260Sstevel@tonic-gate 	ugen_check_mask(usb_ugen_info->usb_ugen_minor_node_ugen_bits_mask,
2275653Slc152243 	    &shift, &limit);
2280Sstevel@tonic-gate 	if (limit == 0) {
2290Sstevel@tonic-gate 		usb_ugen_release_hdl((usb_ugen_hdl_t)hdl);
2300Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
2310Sstevel@tonic-gate 
2320Sstevel@tonic-gate 		return (NULL);
2330Sstevel@tonic-gate 	}
2340Sstevel@tonic-gate 	hdl->hdl_minor_node_ugen_bits_mask = usb_ugen_info->
2355653Slc152243 	    usb_ugen_minor_node_ugen_bits_mask;
2360Sstevel@tonic-gate 	hdl->hdl_minor_node_ugen_bits_shift = shift;
2370Sstevel@tonic-gate 	hdl->hdl_minor_node_ugen_bits_limit = limit;
2380Sstevel@tonic-gate 
2390Sstevel@tonic-gate 	ugen_check_mask(usb_ugen_info->usb_ugen_minor_node_instance_mask,
2405653Slc152243 	    &shift, &limit);
2410Sstevel@tonic-gate 	if (limit == 0) {
2420Sstevel@tonic-gate 		usb_ugen_release_hdl((usb_ugen_hdl_t)hdl);
2430Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate 		return (NULL);
2460Sstevel@tonic-gate 	}
2470Sstevel@tonic-gate 
2480Sstevel@tonic-gate 	hdl->hdl_minor_node_instance_mask = usb_ugen_info->
2495653Slc152243 	    usb_ugen_minor_node_instance_mask;
2500Sstevel@tonic-gate 	hdl->hdl_minor_node_instance_shift = shift;
2510Sstevel@tonic-gate 	hdl->hdl_minor_node_instance_limit = limit;
2520Sstevel@tonic-gate 
2530Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
2540Sstevel@tonic-gate 	    "usb_ugen_get_hdl: instance shift=%d instance limit=%d",
2550Sstevel@tonic-gate 	    hdl->hdl_minor_node_instance_shift,
2560Sstevel@tonic-gate 	    hdl->hdl_minor_node_instance_limit);
2570Sstevel@tonic-gate 
2580Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
2590Sstevel@tonic-gate 	    "usb_ugen_get_hdl: bits shift=%d bits limit=%d",
2600Sstevel@tonic-gate 	    hdl->hdl_minor_node_ugen_bits_shift,
2610Sstevel@tonic-gate 	    hdl->hdl_minor_node_ugen_bits_limit);
2620Sstevel@tonic-gate 
2630Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate 	return ((usb_ugen_hdl_t)hdl);
2660Sstevel@tonic-gate }
2670Sstevel@tonic-gate 
2680Sstevel@tonic-gate 
2690Sstevel@tonic-gate /*
2700Sstevel@tonic-gate  * usb_ugen_release_hdl:
2710Sstevel@tonic-gate  *	deallocate a handle
2720Sstevel@tonic-gate  */
2730Sstevel@tonic-gate void
usb_ugen_release_hdl(usb_ugen_hdl_t usb_ugen_hdl)2740Sstevel@tonic-gate usb_ugen_release_hdl(usb_ugen_hdl_t usb_ugen_hdl)
2750Sstevel@tonic-gate {
2760Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
2775653Slc152243 	    (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
2780Sstevel@tonic-gate 
2790Sstevel@tonic-gate 	if (usb_ugen_hdl_impl) {
2800Sstevel@tonic-gate 		ugen_state_t *ugenp = usb_ugen_hdl_impl->hdl_ugenp;
2810Sstevel@tonic-gate 
2820Sstevel@tonic-gate 		if (ugenp) {
2830Sstevel@tonic-gate 			mutex_destroy(&ugenp->ug_mutex);
2840Sstevel@tonic-gate 			usb_free_log_hdl(ugenp->ug_log_hdl);
2850Sstevel@tonic-gate 			usb_free_dev_data(usb_ugen_hdl_impl->hdl_dip,
2865653Slc152243 			    ugenp->ug_dev_data);
2870Sstevel@tonic-gate 			kmem_free(ugenp, sizeof (*ugenp));
2880Sstevel@tonic-gate 		}
2890Sstevel@tonic-gate 		if (usb_ugen_hdl_impl->hdl_log_name) {
2900Sstevel@tonic-gate 			kmem_free(usb_ugen_hdl_impl->hdl_log_name,
2915653Slc152243 			    usb_ugen_hdl_impl->hdl_log_name_length);
2920Sstevel@tonic-gate 		}
2930Sstevel@tonic-gate 		kmem_free(usb_ugen_hdl_impl, sizeof (*usb_ugen_hdl_impl));
2940Sstevel@tonic-gate 	}
2950Sstevel@tonic-gate }
2960Sstevel@tonic-gate 
2970Sstevel@tonic-gate 
2980Sstevel@tonic-gate /*
2990Sstevel@tonic-gate  * usb_ugen_attach()
3000Sstevel@tonic-gate  */
3010Sstevel@tonic-gate int
usb_ugen_attach(usb_ugen_hdl_t usb_ugen_hdl,ddi_attach_cmd_t cmd)3020Sstevel@tonic-gate usb_ugen_attach(usb_ugen_hdl_t usb_ugen_hdl, ddi_attach_cmd_t cmd)
3030Sstevel@tonic-gate {
3040Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
3055653Slc152243 	    (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
3060Sstevel@tonic-gate 	ugen_state_t		*ugenp;
3070Sstevel@tonic-gate 	dev_info_t		*dip;
3080Sstevel@tonic-gate 
3090Sstevel@tonic-gate 	if (usb_ugen_hdl == NULL) {
3100Sstevel@tonic-gate 
3110Sstevel@tonic-gate 		return (USB_FAILURE);
3120Sstevel@tonic-gate 	}
3130Sstevel@tonic-gate 
3140Sstevel@tonic-gate 	ugenp = usb_ugen_hdl_impl->hdl_ugenp;
3150Sstevel@tonic-gate 	dip = usb_ugen_hdl_impl->hdl_dip;
3160Sstevel@tonic-gate 
3170Sstevel@tonic-gate 
3180Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
3190Sstevel@tonic-gate 	    "usb_ugen_attach: cmd=%d", cmd);
3200Sstevel@tonic-gate 
3210Sstevel@tonic-gate 	switch (cmd) {
3220Sstevel@tonic-gate 	case DDI_ATTACH:
3230Sstevel@tonic-gate 
3240Sstevel@tonic-gate 		break;
3250Sstevel@tonic-gate 	case DDI_RESUME:
3260Sstevel@tonic-gate 		ugen_cpr_resume(ugenp);
3270Sstevel@tonic-gate 
3280Sstevel@tonic-gate 		return (USB_SUCCESS);
3290Sstevel@tonic-gate 	default:
3300Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, NULL,
3310Sstevel@tonic-gate 		    "usb_ugen_attach: unknown command");
3320Sstevel@tonic-gate 
3330Sstevel@tonic-gate 		return (USB_FAILURE);
3340Sstevel@tonic-gate 	}
3350Sstevel@tonic-gate 
3360Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
3370Sstevel@tonic-gate 	ugenp->ug_ser_cookie =
3380Sstevel@tonic-gate 	    usb_init_serialization(dip, USB_INIT_SER_CHECK_SAME_THREAD);
3390Sstevel@tonic-gate 	ugenp->ug_cleanup_flags |= UGEN_INIT_LOCKS;
3400Sstevel@tonic-gate 
3410Sstevel@tonic-gate 	/* Get maximum bulk transfer size supported by the HCD */
3420Sstevel@tonic-gate 	if (usb_pipe_get_max_bulk_transfer_size(dip,
3430Sstevel@tonic-gate 	    &ugenp->ug_max_bulk_xfer_sz) != USB_SUCCESS) {
3440Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
3450Sstevel@tonic-gate 		    "usb_ugen_attach: Getting max bulk xfer sz failed");
3460Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
3470Sstevel@tonic-gate 
3480Sstevel@tonic-gate 		goto fail;
3490Sstevel@tonic-gate 	}
3500Sstevel@tonic-gate 
3510Sstevel@tonic-gate 	/* table for mapping 48 bit minor codes to 9 bit index (for ugen) */
3520Sstevel@tonic-gate 	ugen_minor_node_table_create(ugenp);
3530Sstevel@tonic-gate 
3540Sstevel@tonic-gate 	/* prepare device status node handling */
3550Sstevel@tonic-gate 	if (ugen_ds_init(ugenp) != USB_SUCCESS) {
3560Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
3570Sstevel@tonic-gate 		    "usb_ugen_attach: preparing dev status failed");
3580Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
3590Sstevel@tonic-gate 
3600Sstevel@tonic-gate 		goto fail;
3610Sstevel@tonic-gate 	}
3620Sstevel@tonic-gate 
3630Sstevel@tonic-gate 	/* prepare all available xfer and status endpoints nodes */
3640Sstevel@tonic-gate 	if (ugen_epxs_init(ugenp) != USB_SUCCESS) {
3650Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
3660Sstevel@tonic-gate 		    "usb_ugen_attach: preparing endpoints failed");
3670Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
3680Sstevel@tonic-gate 
3690Sstevel@tonic-gate 		goto fail;
3700Sstevel@tonic-gate 	}
3710Sstevel@tonic-gate 
3720Sstevel@tonic-gate 	/* reduce table size if not all entries are used */
3730Sstevel@tonic-gate 	ugen_minor_node_table_shrink(ugenp);
3740Sstevel@tonic-gate 
3750Sstevel@tonic-gate 	/* we are ready to go */
3760Sstevel@tonic-gate 	ugenp->ug_dev_state = USB_DEV_ONLINE;
3770Sstevel@tonic-gate 
3780Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
3790Sstevel@tonic-gate 
3800Sstevel@tonic-gate 	/* prepare PM */
3810Sstevel@tonic-gate 	if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
3820Sstevel@tonic-gate 		ugen_pm_init(ugenp);
3830Sstevel@tonic-gate 	}
3840Sstevel@tonic-gate 
3850Sstevel@tonic-gate 	/*
3860Sstevel@tonic-gate 	 * if ugen driver, kill all child nodes otherwise set cfg fails
3870Sstevel@tonic-gate 	 * if requested
3880Sstevel@tonic-gate 	 */
3890Sstevel@tonic-gate 	if (usb_owns_device(dip) &&
3900Sstevel@tonic-gate 	    (usb_ugen_hdl_impl->hdl_flags & USB_UGEN_REMOVE_CHILDREN)) {
3910Sstevel@tonic-gate 		dev_info_t *cdip;
3920Sstevel@tonic-gate 
3930Sstevel@tonic-gate 		/* save cfgidx so we can restore on detach */
3940Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
3950Sstevel@tonic-gate 		ugenp->ug_initial_cfgidx = usb_get_current_cfgidx(dip);
3960Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
3970Sstevel@tonic-gate 
3980Sstevel@tonic-gate 		for (cdip = ddi_get_child(dip); cdip; ) {
3990Sstevel@tonic-gate 			dev_info_t *next = ddi_get_next_sibling(cdip);
4000Sstevel@tonic-gate 			(void) ddi_remove_child(cdip, 0);
4010Sstevel@tonic-gate 			cdip = next;
4020Sstevel@tonic-gate 		}
4030Sstevel@tonic-gate 	}
4040Sstevel@tonic-gate 
4050Sstevel@tonic-gate 	return (DDI_SUCCESS);
4060Sstevel@tonic-gate fail:
4070Sstevel@tonic-gate 	if (ugenp) {
4080Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
4090Sstevel@tonic-gate 		    "attach fail");
4100Sstevel@tonic-gate 		(void) ugen_cleanup(ugenp);
4110Sstevel@tonic-gate 	}
4120Sstevel@tonic-gate 
4130Sstevel@tonic-gate 	return (DDI_FAILURE);
4140Sstevel@tonic-gate }
4150Sstevel@tonic-gate 
4160Sstevel@tonic-gate 
4170Sstevel@tonic-gate /*
4180Sstevel@tonic-gate  * usb_ugen_detach()
4190Sstevel@tonic-gate  */
4200Sstevel@tonic-gate int
usb_ugen_detach(usb_ugen_hdl_t usb_ugen_hdl,ddi_detach_cmd_t cmd)4210Sstevel@tonic-gate usb_ugen_detach(usb_ugen_hdl_t usb_ugen_hdl, ddi_detach_cmd_t cmd)
4220Sstevel@tonic-gate {
4230Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
4245653Slc152243 	    (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
4250Sstevel@tonic-gate 	int			rval = USB_FAILURE;
4260Sstevel@tonic-gate 
4270Sstevel@tonic-gate 	if (usb_ugen_hdl) {
4280Sstevel@tonic-gate 		ugen_state_t *ugenp = usb_ugen_hdl_impl->hdl_ugenp;
4290Sstevel@tonic-gate 
4300Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
4310Sstevel@tonic-gate 		    "usb_ugen_detach cmd %d", cmd);
4320Sstevel@tonic-gate 
4330Sstevel@tonic-gate 		switch (cmd) {
4340Sstevel@tonic-gate 		case DDI_DETACH:
4350Sstevel@tonic-gate 			rval = ugen_cleanup(ugenp);
4360Sstevel@tonic-gate 
4370Sstevel@tonic-gate 			break;
4380Sstevel@tonic-gate 		case DDI_SUSPEND:
4390Sstevel@tonic-gate 			rval = ugen_cpr_suspend(ugenp);
4400Sstevel@tonic-gate 
4410Sstevel@tonic-gate 			break;
4420Sstevel@tonic-gate 		default:
4430Sstevel@tonic-gate 
4440Sstevel@tonic-gate 			break;
4450Sstevel@tonic-gate 		}
4460Sstevel@tonic-gate 	}
4470Sstevel@tonic-gate 
4480Sstevel@tonic-gate 	return (rval);
4490Sstevel@tonic-gate }
4500Sstevel@tonic-gate 
4510Sstevel@tonic-gate 
4520Sstevel@tonic-gate /*
4530Sstevel@tonic-gate  * ugen_cleanup()
4540Sstevel@tonic-gate  */
4550Sstevel@tonic-gate static int
ugen_cleanup(ugen_state_t * ugenp)4560Sstevel@tonic-gate ugen_cleanup(ugen_state_t *ugenp)
4570Sstevel@tonic-gate {
4580Sstevel@tonic-gate 	dev_info_t *dip = ugenp->ug_dip;
4590Sstevel@tonic-gate 
4600Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl, "ugen_cleanup");
4610Sstevel@tonic-gate 
4620Sstevel@tonic-gate 	if (ugenp->ug_cleanup_flags & UGEN_INIT_LOCKS) {
4630Sstevel@tonic-gate 
4640Sstevel@tonic-gate 		/* shutdown all endpoints */
4650Sstevel@tonic-gate 		ugen_epx_shutdown(ugenp);
4660Sstevel@tonic-gate 
4670Sstevel@tonic-gate 		/*
4680Sstevel@tonic-gate 		 * At this point, no new activity can be initiated.
4690Sstevel@tonic-gate 		 * The driver has disabled hotplug callbacks.
4700Sstevel@tonic-gate 		 * The Solaris framework has disabled
4710Sstevel@tonic-gate 		 * new opens on a device being detached, and does not
4720Sstevel@tonic-gate 		 * allow detaching an open device. PM should power
4730Sstevel@tonic-gate 		 * down while we are detaching
4740Sstevel@tonic-gate 		 *
4750Sstevel@tonic-gate 		 * The following ensures that any other driver
4760Sstevel@tonic-gate 		 * activity must have drained (paranoia)
4770Sstevel@tonic-gate 		 */
4780Sstevel@tonic-gate 		(void) usb_serialize_access(ugenp->ug_ser_cookie,
4795653Slc152243 		    USB_WAIT, 0);
4800Sstevel@tonic-gate 		usb_release_access(ugenp->ug_ser_cookie);
4810Sstevel@tonic-gate 
4820Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
4830Sstevel@tonic-gate 		ASSERT(ugenp->ug_open_count == 0);
4840Sstevel@tonic-gate 		ASSERT(ugenp->ug_pending_cmds == 0);
4850Sstevel@tonic-gate 
4860Sstevel@tonic-gate 		/* dismantle in reverse order */
4870Sstevel@tonic-gate 		ugen_pm_destroy(ugenp);
4880Sstevel@tonic-gate 		ugen_epxs_destroy(ugenp);
4890Sstevel@tonic-gate 		ugen_ds_destroy(ugenp);
4900Sstevel@tonic-gate 		ugen_minor_node_table_destroy(ugenp);
4910Sstevel@tonic-gate 
4920Sstevel@tonic-gate 
4930Sstevel@tonic-gate 		/* restore to initial configuration */
4940Sstevel@tonic-gate 		if (usb_owns_device(dip) &&
4950Sstevel@tonic-gate 		    (ugenp->ug_dev_state != USB_DEV_DISCONNECTED)) {
4960Sstevel@tonic-gate 			int idx = ugenp->ug_initial_cfgidx;
4970Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
4980Sstevel@tonic-gate 			(void) usb_set_cfg(dip, idx,
4990Sstevel@tonic-gate 			    USB_FLAGS_SLEEP, NULL, NULL);
5000Sstevel@tonic-gate 		} else {
5010Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
5020Sstevel@tonic-gate 		}
5030Sstevel@tonic-gate 
5040Sstevel@tonic-gate 		usb_fini_serialization(ugenp->ug_ser_cookie);
5050Sstevel@tonic-gate 	}
5060Sstevel@tonic-gate 
5070Sstevel@tonic-gate 	ddi_prop_remove_all(dip);
5080Sstevel@tonic-gate 	ddi_remove_minor_node(dip, NULL);
5090Sstevel@tonic-gate 
5100Sstevel@tonic-gate 	ugen_free_devt(ugenp);
5110Sstevel@tonic-gate 
5120Sstevel@tonic-gate 	return (USB_SUCCESS);
5130Sstevel@tonic-gate }
5140Sstevel@tonic-gate 
5150Sstevel@tonic-gate 
5160Sstevel@tonic-gate /*
5170Sstevel@tonic-gate  * ugen_cpr_suspend
5180Sstevel@tonic-gate  */
5190Sstevel@tonic-gate static int
ugen_cpr_suspend(ugen_state_t * ugenp)5200Sstevel@tonic-gate ugen_cpr_suspend(ugen_state_t *ugenp)
5210Sstevel@tonic-gate {
5220Sstevel@tonic-gate 	int		rval = USB_FAILURE;
5230Sstevel@tonic-gate 	int		i;
5240Sstevel@tonic-gate 	int		prev_state;
5250Sstevel@tonic-gate 
5260Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CPR, ugenp->ug_log_hdl,
5270Sstevel@tonic-gate 	    "ugen_cpr_suspend:");
5280Sstevel@tonic-gate 
5290Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
5300Sstevel@tonic-gate 	switch (ugenp->ug_dev_state) {
5310Sstevel@tonic-gate 	case USB_DEV_ONLINE:
5320Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
5330Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_CPR, ugenp->ug_log_hdl,
5340Sstevel@tonic-gate 		    "ugen_cpr_suspend:");
5350Sstevel@tonic-gate 
5360Sstevel@tonic-gate 		prev_state = ugenp->ug_dev_state;
5370Sstevel@tonic-gate 		ugenp->ug_dev_state = USB_DEV_SUSPENDED;
5380Sstevel@tonic-gate 
5390Sstevel@tonic-gate 		if (ugenp->ug_open_count) {
5400Sstevel@tonic-gate 			/* drain outstanding cmds */
5410Sstevel@tonic-gate 			for (i = 0; i < ugen_busy_loop; i++) {
5420Sstevel@tonic-gate 				if (ugenp->ug_pending_cmds == 0) {
5430Sstevel@tonic-gate 
5440Sstevel@tonic-gate 					break;
5450Sstevel@tonic-gate 				}
5460Sstevel@tonic-gate 				mutex_exit(&ugenp->ug_mutex);
5470Sstevel@tonic-gate 				delay(drv_usectohz(100000));
5480Sstevel@tonic-gate 				mutex_enter(&ugenp->ug_mutex);
5490Sstevel@tonic-gate 			}
5500Sstevel@tonic-gate 
5510Sstevel@tonic-gate 			/* if still outstanding cmds, fail suspend */
5520Sstevel@tonic-gate 			if (ugenp->ug_pending_cmds) {
5530Sstevel@tonic-gate 				ugenp->ug_dev_state = prev_state;
5540Sstevel@tonic-gate 
5550Sstevel@tonic-gate 				USB_DPRINTF_L2(UGEN_PRINT_CPR,
5560Sstevel@tonic-gate 				    ugenp->ug_log_hdl,
5570Sstevel@tonic-gate 				    "ugen_cpr_suspend: pending %d",
5580Sstevel@tonic-gate 				    ugenp->ug_pending_cmds);
5590Sstevel@tonic-gate 
5600Sstevel@tonic-gate 				rval =	USB_FAILURE;
5610Sstevel@tonic-gate 				break;
5620Sstevel@tonic-gate 			}
5630Sstevel@tonic-gate 
5640Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
5650Sstevel@tonic-gate 			(void) usb_serialize_access(ugenp->ug_ser_cookie,
5665653Slc152243 			    USB_WAIT, 0);
5670Sstevel@tonic-gate 			/* close all pipes */
5680Sstevel@tonic-gate 			ugen_epx_shutdown(ugenp);
5690Sstevel@tonic-gate 
5700Sstevel@tonic-gate 			usb_release_access(ugenp->ug_ser_cookie);
5710Sstevel@tonic-gate 
5720Sstevel@tonic-gate 			mutex_enter(&ugenp->ug_mutex);
5730Sstevel@tonic-gate 		}
5740Sstevel@tonic-gate 
5750Sstevel@tonic-gate 		/* wakeup devstat reads and polls */
5760Sstevel@tonic-gate 		ugen_ds_change(ugenp);
5770Sstevel@tonic-gate 		ugen_ds_poll_wakeup(ugenp);
5780Sstevel@tonic-gate 
5790Sstevel@tonic-gate 		rval = USB_SUCCESS;
5800Sstevel@tonic-gate 		break;
5810Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
5820Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RESUME:
5830Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
5840Sstevel@tonic-gate 	default:
5850Sstevel@tonic-gate 
5860Sstevel@tonic-gate 		break;
5870Sstevel@tonic-gate 	}
5880Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
5890Sstevel@tonic-gate 
5900Sstevel@tonic-gate 	return (rval);
5910Sstevel@tonic-gate }
5920Sstevel@tonic-gate 
5930Sstevel@tonic-gate /*
5940Sstevel@tonic-gate  * ugen_cpr_resume
5950Sstevel@tonic-gate  */
5960Sstevel@tonic-gate static void
ugen_cpr_resume(ugen_state_t * ugenp)5970Sstevel@tonic-gate ugen_cpr_resume(ugen_state_t *ugenp)
5980Sstevel@tonic-gate {
5990Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CPR, ugenp->ug_log_hdl,
6000Sstevel@tonic-gate 	    "ugen_cpr_resume:");
6010Sstevel@tonic-gate 
6020Sstevel@tonic-gate 	ugen_restore_state(ugenp);
6030Sstevel@tonic-gate }
6040Sstevel@tonic-gate 
6050Sstevel@tonic-gate /*
6060Sstevel@tonic-gate  * usb_ugen_disconnect_ev_cb:
6070Sstevel@tonic-gate  */
6080Sstevel@tonic-gate int
usb_ugen_disconnect_ev_cb(usb_ugen_hdl_t usb_ugen_hdl)6090Sstevel@tonic-gate usb_ugen_disconnect_ev_cb(usb_ugen_hdl_t usb_ugen_hdl)
6100Sstevel@tonic-gate {
6110Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
6125653Slc152243 	    (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
6130Sstevel@tonic-gate 	ugen_state_t		*ugenp;
6140Sstevel@tonic-gate 
6150Sstevel@tonic-gate 	if (usb_ugen_hdl_impl == NULL) {
6160Sstevel@tonic-gate 
6170Sstevel@tonic-gate 		return (USB_FAILURE);
6180Sstevel@tonic-gate 	}
6190Sstevel@tonic-gate 
6200Sstevel@tonic-gate 	ugenp = usb_ugen_hdl_impl->hdl_ugenp;
6210Sstevel@tonic-gate 
6220Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_HOTPLUG, ugenp->ug_log_hdl,
6230Sstevel@tonic-gate 	    "usb_ugen_disconnect_ev_cb:");
6240Sstevel@tonic-gate 
6250Sstevel@tonic-gate 	/* get exclusive access */
6260Sstevel@tonic-gate 	(void) usb_serialize_access(ugenp->ug_ser_cookie, USB_WAIT, 0);
6270Sstevel@tonic-gate 
6280Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
6290Sstevel@tonic-gate 	ugenp->ug_dev_state = USB_DEV_DISCONNECTED;
6300Sstevel@tonic-gate 	if (ugenp->ug_open_count) {
6310Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
6320Sstevel@tonic-gate 
6330Sstevel@tonic-gate 		/* close all pipes */
6340Sstevel@tonic-gate 		(void) ugen_epx_shutdown(ugenp);
6350Sstevel@tonic-gate 
6360Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
6370Sstevel@tonic-gate 	}
6380Sstevel@tonic-gate 
6390Sstevel@tonic-gate 
6400Sstevel@tonic-gate 	/* wakeup devstat reads and polls */
6410Sstevel@tonic-gate 	ugen_ds_change(ugenp);
6420Sstevel@tonic-gate 	ugen_ds_poll_wakeup(ugenp);
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
6450Sstevel@tonic-gate 	usb_release_access(ugenp->ug_ser_cookie);
6460Sstevel@tonic-gate 
6470Sstevel@tonic-gate 	return (USB_SUCCESS);
6480Sstevel@tonic-gate }
6490Sstevel@tonic-gate 
6500Sstevel@tonic-gate 
6510Sstevel@tonic-gate /*
6520Sstevel@tonic-gate  * usb_ugen_reconnect_ev_cb:
6530Sstevel@tonic-gate  */
6540Sstevel@tonic-gate int
usb_ugen_reconnect_ev_cb(usb_ugen_hdl_t usb_ugen_hdl)6550Sstevel@tonic-gate usb_ugen_reconnect_ev_cb(usb_ugen_hdl_t usb_ugen_hdl)
6560Sstevel@tonic-gate {
6570Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
6585653Slc152243 	    (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
6590Sstevel@tonic-gate 	ugen_state_t		*ugenp = usb_ugen_hdl_impl->hdl_ugenp;
6600Sstevel@tonic-gate 
6610Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_HOTPLUG, ugenp->ug_log_hdl,
6620Sstevel@tonic-gate 	    "usb_ugen_reconnect_ev_cb:");
6630Sstevel@tonic-gate 
6640Sstevel@tonic-gate 	ugen_restore_state(ugenp);
6650Sstevel@tonic-gate 
6660Sstevel@tonic-gate 	return (USB_SUCCESS);
6670Sstevel@tonic-gate }
6680Sstevel@tonic-gate 
6690Sstevel@tonic-gate 
6700Sstevel@tonic-gate /*
6710Sstevel@tonic-gate  * ugen_restore_state:
6720Sstevel@tonic-gate  *	Check for same device; if a different device is attached, set
6730Sstevel@tonic-gate  *	the device status to disconnected.
6740Sstevel@tonic-gate  *	If we were open, then set to UNAVAILABLE until all endpoints have
6750Sstevel@tonic-gate  *	be closed.
6760Sstevel@tonic-gate  */
6770Sstevel@tonic-gate static void
ugen_restore_state(ugen_state_t * ugenp)6780Sstevel@tonic-gate ugen_restore_state(ugen_state_t *ugenp)
6790Sstevel@tonic-gate {
6800Sstevel@tonic-gate 	dev_info_t *dip = ugenp->ug_dip;
6810Sstevel@tonic-gate 
6820Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_HOTPLUG, ugenp->ug_log_hdl,
6830Sstevel@tonic-gate 	    "ugen_restore_state");
6840Sstevel@tonic-gate 
6850Sstevel@tonic-gate 	/* first raise power */
6860Sstevel@tonic-gate 	if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
6870Sstevel@tonic-gate 		ugen_pm_busy_component(ugenp);
6880Sstevel@tonic-gate 		(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
6890Sstevel@tonic-gate 	}
6900Sstevel@tonic-gate 
6910Sstevel@tonic-gate 	/* Check if we are talking to the same device */
6920Sstevel@tonic-gate 	if (usb_check_same_device(dip, ugenp->ug_log_hdl,
6930Sstevel@tonic-gate 	    USB_LOG_L0, UGEN_PRINT_HOTPLUG, USB_CHK_ALL, NULL) ==
6940Sstevel@tonic-gate 	    USB_FAILURE) {
6950Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
6960Sstevel@tonic-gate 		ugenp->ug_dev_state = USB_DEV_DISCONNECTED;
6970Sstevel@tonic-gate 
6980Sstevel@tonic-gate 		/* wakeup devstat reads and polls */
6990Sstevel@tonic-gate 		ugen_ds_change(ugenp);
7000Sstevel@tonic-gate 		ugen_ds_poll_wakeup(ugenp);
7010Sstevel@tonic-gate 
7020Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
7030Sstevel@tonic-gate 
7040Sstevel@tonic-gate 		if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
7050Sstevel@tonic-gate 			ugen_pm_idle_component(ugenp);
7060Sstevel@tonic-gate 		}
7070Sstevel@tonic-gate 
7080Sstevel@tonic-gate 		return;
7090Sstevel@tonic-gate 	}
7100Sstevel@tonic-gate 
7110Sstevel@tonic-gate 	/*
7120Sstevel@tonic-gate 	 * get exclusive access, we don't want to change state in the
7130Sstevel@tonic-gate 	 * middle of some other actions
7140Sstevel@tonic-gate 	 */
7150Sstevel@tonic-gate 	(void) usb_serialize_access(ugenp->ug_ser_cookie, USB_WAIT, 0);
7160Sstevel@tonic-gate 
7170Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
7180Sstevel@tonic-gate 	switch (ugenp->ug_dev_state) {
7190Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
7200Sstevel@tonic-gate 		ugenp->ug_dev_state = (ugenp->ug_open_count == 0) ?
7210Sstevel@tonic-gate 		    USB_DEV_ONLINE : USB_UGEN_DEV_UNAVAILABLE_RECONNECT;
7220Sstevel@tonic-gate 
7230Sstevel@tonic-gate 		break;
7240Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
7250Sstevel@tonic-gate 		ugenp->ug_dev_state = (ugenp->ug_open_count == 0) ?
7260Sstevel@tonic-gate 		    USB_DEV_ONLINE : USB_UGEN_DEV_UNAVAILABLE_RESUME;
7270Sstevel@tonic-gate 
7280Sstevel@tonic-gate 		break;
7290Sstevel@tonic-gate 	}
7300Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_HOTPLUG, ugenp->ug_log_hdl,
7310Sstevel@tonic-gate 	    "ugen_restore_state: state=%d, opencount=%d",
7320Sstevel@tonic-gate 	    ugenp->ug_dev_state, ugenp->ug_open_count);
7330Sstevel@tonic-gate 
7340Sstevel@tonic-gate 	/* wakeup devstat reads and polls */
7350Sstevel@tonic-gate 	ugen_ds_change(ugenp);
7360Sstevel@tonic-gate 	ugen_ds_poll_wakeup(ugenp);
7370Sstevel@tonic-gate 
7380Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
7390Sstevel@tonic-gate 	usb_release_access(ugenp->ug_ser_cookie);
7400Sstevel@tonic-gate 
7410Sstevel@tonic-gate 	if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
7420Sstevel@tonic-gate 		ugen_pm_idle_component(ugenp);
7430Sstevel@tonic-gate 	}
7440Sstevel@tonic-gate }
7450Sstevel@tonic-gate 
7460Sstevel@tonic-gate 
7470Sstevel@tonic-gate /*
7480Sstevel@tonic-gate  * usb_ugen_open:
7490Sstevel@tonic-gate  */
7500Sstevel@tonic-gate /* ARGSUSED */
7510Sstevel@tonic-gate int
usb_ugen_open(usb_ugen_hdl_t usb_ugen_hdl,dev_t * devp,int flag,int sflag,cred_t * cr)7520Sstevel@tonic-gate usb_ugen_open(usb_ugen_hdl_t usb_ugen_hdl, dev_t *devp, int flag, int sflag,
7530Sstevel@tonic-gate     cred_t *cr)
7540Sstevel@tonic-gate {
7550Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
7565653Slc152243 	    (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
7570Sstevel@tonic-gate 	ugen_state_t		*ugenp;
7580Sstevel@tonic-gate 	int			rval;
7590Sstevel@tonic-gate 	int			minor_node_type;
7600Sstevel@tonic-gate 
7610Sstevel@tonic-gate 	if (usb_ugen_hdl == NULL) {
7620Sstevel@tonic-gate 
7630Sstevel@tonic-gate 		return (EINVAL);
7640Sstevel@tonic-gate 	}
7650Sstevel@tonic-gate 
7660Sstevel@tonic-gate 	ugenp = usb_ugen_hdl_impl->hdl_ugenp;
76770Sfrits 
76870Sfrits 	if (ugen_is_valid_minor_node(ugenp, *devp) != USB_SUCCESS) {
76970Sfrits 
77070Sfrits 		return (EINVAL);
77170Sfrits 	}
77270Sfrits 
7730Sstevel@tonic-gate 	minor_node_type = UGEN_MINOR_TYPE(ugenp, *devp);
7740Sstevel@tonic-gate 
7750Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
7760Sstevel@tonic-gate 	    "usb_ugen_open: minor=%u", getminor(*devp));
7770Sstevel@tonic-gate 	USB_DPRINTF_L3(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
7780Sstevel@tonic-gate 	    "cfgval=%" PRIu64 " cfgidx=%" PRIu64 " if=%" PRIu64
7790Sstevel@tonic-gate 	    " alt=%" PRIu64 " epidx=%" PRIu64 " type=0x%" PRIx64,
7800Sstevel@tonic-gate 	    UGEN_MINOR_CFGVAL(ugenp, *devp), UGEN_MINOR_CFGIDX(ugenp, *devp),
7810Sstevel@tonic-gate 	    UGEN_MINOR_IF(ugenp, *devp), UGEN_MINOR_ALT(ugenp, *devp),
7820Sstevel@tonic-gate 	    UGEN_MINOR_EPIDX(ugenp, *devp), UGEN_MINOR_TYPE(ugenp, *devp));
7830Sstevel@tonic-gate 
7840Sstevel@tonic-gate 	/* first check for legal open flags */
7850Sstevel@tonic-gate 	if ((rval = ugen_check_open_flags(ugenp, *devp, flag)) != 0) {
7860Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
7870Sstevel@tonic-gate 		    "usb_ugen_open: check failed, rval=%d", rval);
7880Sstevel@tonic-gate 
7890Sstevel@tonic-gate 		return (rval);
7900Sstevel@tonic-gate 	}
7910Sstevel@tonic-gate 
7920Sstevel@tonic-gate 	/* exclude other threads including other opens */
7930Sstevel@tonic-gate 	if (usb_serialize_access(ugenp->ug_ser_cookie,
7940Sstevel@tonic-gate 	    USB_WAIT_SIG, 0) <= 0) {
7950Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
7960Sstevel@tonic-gate 		    "usb_ugen_open: interrupted");
7970Sstevel@tonic-gate 
7980Sstevel@tonic-gate 		return (EINTR);
7990Sstevel@tonic-gate 	}
8000Sstevel@tonic-gate 
8010Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
8020Sstevel@tonic-gate 
8030Sstevel@tonic-gate 	/* always allow open of dev stat node */
8040Sstevel@tonic-gate 	if (minor_node_type != UGEN_MINOR_DEV_STAT_NODE) {
8050Sstevel@tonic-gate 
8060Sstevel@tonic-gate 		/* if we are not online or powered down, fail open */
8070Sstevel@tonic-gate 		switch (ugenp->ug_dev_state) {
8080Sstevel@tonic-gate 		case USB_DEV_ONLINE:
8090Sstevel@tonic-gate 
8100Sstevel@tonic-gate 			break;
8110Sstevel@tonic-gate 		case USB_DEV_DISCONNECTED:
8120Sstevel@tonic-gate 			rval = ENODEV;
8130Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
8140Sstevel@tonic-gate 
8150Sstevel@tonic-gate 			goto done;
8160Sstevel@tonic-gate 		case USB_DEV_SUSPENDED:
8170Sstevel@tonic-gate 		case USB_UGEN_DEV_UNAVAILABLE_RESUME:
8180Sstevel@tonic-gate 		case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
8190Sstevel@tonic-gate 		default:
8200Sstevel@tonic-gate 			rval = EBADF;
8210Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
8220Sstevel@tonic-gate 
8230Sstevel@tonic-gate 			goto done;
8240Sstevel@tonic-gate 		}
8250Sstevel@tonic-gate 	}
8260Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
8270Sstevel@tonic-gate 
8280Sstevel@tonic-gate 	/* open node depending on type */
8290Sstevel@tonic-gate 	switch (minor_node_type) {
8300Sstevel@tonic-gate 	case UGEN_MINOR_EP_XFER_NODE:
8310Sstevel@tonic-gate 		if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
8320Sstevel@tonic-gate 			ugen_pm_busy_component(ugenp);
8330Sstevel@tonic-gate 			(void) pm_raise_power(ugenp->ug_dip, 0,
8345653Slc152243 			    USB_DEV_OS_FULL_PWR);
8350Sstevel@tonic-gate 		}
8360Sstevel@tonic-gate 
8370Sstevel@tonic-gate 		rval = ugen_epx_open(ugenp, *devp, flag);
8380Sstevel@tonic-gate 		if (rval == 0) {
8390Sstevel@tonic-gate 			mutex_enter(&ugenp->ug_mutex);
8400Sstevel@tonic-gate 			ugenp->ug_open_count++;
8410Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
8420Sstevel@tonic-gate 		} else {
8430Sstevel@tonic-gate 			if (ugenp->ug_hdl->hdl_flags &
8440Sstevel@tonic-gate 			    USB_UGEN_ENABLE_PM) {
8450Sstevel@tonic-gate 				ugen_pm_idle_component(ugenp);
8460Sstevel@tonic-gate 			}
8470Sstevel@tonic-gate 		}
8480Sstevel@tonic-gate 
8490Sstevel@tonic-gate 		break;
8500Sstevel@tonic-gate 	case UGEN_MINOR_EP_STAT_NODE:
8510Sstevel@tonic-gate 		rval = ugen_eps_open(ugenp, *devp, flag);
8520Sstevel@tonic-gate 		if (rval == 0) {
8530Sstevel@tonic-gate 			mutex_enter(&ugenp->ug_mutex);
8540Sstevel@tonic-gate 			ugenp->ug_open_count++;
8550Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
8560Sstevel@tonic-gate 		}
8570Sstevel@tonic-gate 
8580Sstevel@tonic-gate 		break;
8590Sstevel@tonic-gate 	case UGEN_MINOR_DEV_STAT_NODE:
8600Sstevel@tonic-gate 		rval = ugen_ds_open(ugenp, *devp, flag);
8610Sstevel@tonic-gate 
8620Sstevel@tonic-gate 		break;
8630Sstevel@tonic-gate 	default:
8640Sstevel@tonic-gate 		rval = EINVAL;
8650Sstevel@tonic-gate 
8660Sstevel@tonic-gate 		break;
8670Sstevel@tonic-gate 	}
8680Sstevel@tonic-gate done:
8690Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
8700Sstevel@tonic-gate 
8710Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
8720Sstevel@tonic-gate 	    "usb_ugen_open: minor=0x%x rval=%d state=%d cnt=%d",
8730Sstevel@tonic-gate 	    getminor(*devp), rval, ugenp->ug_dev_state,
8740Sstevel@tonic-gate 	    ugenp->ug_open_count);
8750Sstevel@tonic-gate 
8760Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
8770Sstevel@tonic-gate 
8780Sstevel@tonic-gate 	usb_release_access(ugenp->ug_ser_cookie);
8790Sstevel@tonic-gate 
8800Sstevel@tonic-gate 	return (rval);
8810Sstevel@tonic-gate }
8820Sstevel@tonic-gate 
8830Sstevel@tonic-gate 
8840Sstevel@tonic-gate /*
8850Sstevel@tonic-gate  * usb_ugen_close()
8860Sstevel@tonic-gate  */
8870Sstevel@tonic-gate /* ARGSUSED */
8880Sstevel@tonic-gate int
usb_ugen_close(usb_ugen_hdl_t usb_ugen_hdl,dev_t dev,int flag,int otype,cred_t * cr)8890Sstevel@tonic-gate usb_ugen_close(usb_ugen_hdl_t usb_ugen_hdl, dev_t dev, int flag, int otype,
8900Sstevel@tonic-gate     cred_t *cr)
8910Sstevel@tonic-gate {
8920Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
8935653Slc152243 	    (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
8940Sstevel@tonic-gate 	ugen_state_t		*ugenp;
8950Sstevel@tonic-gate 	int			minor_node_type;
8960Sstevel@tonic-gate 
8970Sstevel@tonic-gate 	if (usb_ugen_hdl == NULL) {
8980Sstevel@tonic-gate 
8990Sstevel@tonic-gate 		return (EINVAL);
9000Sstevel@tonic-gate 	}
9010Sstevel@tonic-gate 
9020Sstevel@tonic-gate 	ugenp = usb_ugen_hdl_impl->hdl_ugenp;
90370Sfrits 	if (ugen_is_valid_minor_node(ugenp, dev) != USB_SUCCESS) {
90470Sfrits 
90570Sfrits 		return (EINVAL);
90670Sfrits 	}
90770Sfrits 
9080Sstevel@tonic-gate 	minor_node_type = UGEN_MINOR_TYPE(ugenp, dev);
9090Sstevel@tonic-gate 
9100Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
9110Sstevel@tonic-gate 	    "usb_ugen_close: minor=0x%x", getminor(dev));
9120Sstevel@tonic-gate 
9130Sstevel@tonic-gate 	/* exclude other threads, including other opens */
9140Sstevel@tonic-gate 	if (usb_serialize_access(ugenp->ug_ser_cookie,
9150Sstevel@tonic-gate 	    USB_WAIT_SIG, 0) <= 0) {
9160Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
9170Sstevel@tonic-gate 		    "usb_ugen_close: interrupted");
9180Sstevel@tonic-gate 
9190Sstevel@tonic-gate 		return (EINTR);
9200Sstevel@tonic-gate 	}
9210Sstevel@tonic-gate 
9220Sstevel@tonic-gate 	/* close node depending on type */
9230Sstevel@tonic-gate 	switch (minor_node_type) {
9240Sstevel@tonic-gate 	case UGEN_MINOR_EP_XFER_NODE:
9250Sstevel@tonic-gate 		ugen_epx_close(ugenp, dev, flag);
9260Sstevel@tonic-gate 		if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
9270Sstevel@tonic-gate 			ugen_pm_idle_component(ugenp);
9280Sstevel@tonic-gate 		}
9290Sstevel@tonic-gate 
9300Sstevel@tonic-gate 		break;
9310Sstevel@tonic-gate 	case UGEN_MINOR_EP_STAT_NODE:
9320Sstevel@tonic-gate 		ugen_eps_close(ugenp, dev, flag);
9330Sstevel@tonic-gate 
9340Sstevel@tonic-gate 		break;
9350Sstevel@tonic-gate 	case UGEN_MINOR_DEV_STAT_NODE:
9360Sstevel@tonic-gate 		ugen_ds_close(ugenp, dev, flag);
9370Sstevel@tonic-gate 
9380Sstevel@tonic-gate 		break;
9390Sstevel@tonic-gate 	default:
9400Sstevel@tonic-gate 		usb_release_access(ugenp->ug_ser_cookie);
9410Sstevel@tonic-gate 
9420Sstevel@tonic-gate 		return (EINVAL);
9430Sstevel@tonic-gate 	}
9440Sstevel@tonic-gate 
9450Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
9460Sstevel@tonic-gate 	if (minor_node_type != UGEN_MINOR_DEV_STAT_NODE) {
9470Sstevel@tonic-gate 		ASSERT(ugenp->ug_open_count > 0);
9480Sstevel@tonic-gate 		if ((--ugenp->ug_open_count == 0) &&
9490Sstevel@tonic-gate 		    ((ugenp->ug_dev_state == USB_UGEN_DEV_UNAVAILABLE_RESUME) ||
9500Sstevel@tonic-gate 		    (ugenp->ug_dev_state ==
9510Sstevel@tonic-gate 		    USB_UGEN_DEV_UNAVAILABLE_RECONNECT))) {
9520Sstevel@tonic-gate 			ugenp->ug_dev_state = USB_DEV_ONLINE;
9530Sstevel@tonic-gate 
9540Sstevel@tonic-gate 			/* wakeup devstat reads and polls */
9550Sstevel@tonic-gate 			ugen_ds_change(ugenp);
9560Sstevel@tonic-gate 			ugen_ds_poll_wakeup(ugenp);
9570Sstevel@tonic-gate 		}
9580Sstevel@tonic-gate 	}
9590Sstevel@tonic-gate 
9600Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
9610Sstevel@tonic-gate 	    "usb_ugen_close: minor=0x%x state=%d cnt=%d",
9620Sstevel@tonic-gate 	    getminor(dev), ugenp->ug_dev_state, ugenp->ug_open_count);
9630Sstevel@tonic-gate 
9640Sstevel@tonic-gate 	if (ugenp->ug_open_count == 0) {
9650Sstevel@tonic-gate 		ASSERT(ugen_epxs_check_open_nodes(ugenp) == USB_FAILURE);
9660Sstevel@tonic-gate 	}
9670Sstevel@tonic-gate 
9680Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
9690Sstevel@tonic-gate 
9700Sstevel@tonic-gate 	usb_release_access(ugenp->ug_ser_cookie);
9710Sstevel@tonic-gate 
9720Sstevel@tonic-gate 	return (0);
9730Sstevel@tonic-gate }
9740Sstevel@tonic-gate 
9750Sstevel@tonic-gate 
9760Sstevel@tonic-gate /*
9770Sstevel@tonic-gate  * usb_ugen_read/write()
9780Sstevel@tonic-gate  */
9790Sstevel@tonic-gate /*ARGSUSED*/
9800Sstevel@tonic-gate int
usb_ugen_read(usb_ugen_hdl_t usb_ugen_hdl,dev_t dev,struct uio * uiop,cred_t * credp)9810Sstevel@tonic-gate usb_ugen_read(usb_ugen_hdl_t usb_ugen_hdl, dev_t dev, struct uio *uiop,
9820Sstevel@tonic-gate     cred_t *credp)
9830Sstevel@tonic-gate {
98470Sfrits 	ugen_state_t		*ugenp;
98570Sfrits 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
9865653Slc152243 	    (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
98770Sfrits 
98870Sfrits 	if (usb_ugen_hdl == NULL) {
98970Sfrits 
99070Sfrits 		return (EINVAL);
99170Sfrits 	}
99270Sfrits 	ugenp = usb_ugen_hdl_impl->hdl_ugenp;
99370Sfrits 
99470Sfrits 	if (ugen_is_valid_minor_node(ugenp, dev) != USB_SUCCESS) {
99570Sfrits 
99670Sfrits 		return (EINVAL);
99770Sfrits 	}
99870Sfrits 
9990Sstevel@tonic-gate 	return (physio(ugen_strategy,
10000Sstevel@tonic-gate 	    (struct buf *)0, dev, B_READ, ugen_minphys, uiop));
10010Sstevel@tonic-gate }
10020Sstevel@tonic-gate 
10030Sstevel@tonic-gate 
10040Sstevel@tonic-gate /*ARGSUSED*/
10050Sstevel@tonic-gate int
usb_ugen_write(usb_ugen_hdl_t usb_ugen_hdl,dev_t dev,struct uio * uiop,cred_t * credp)10060Sstevel@tonic-gate usb_ugen_write(usb_ugen_hdl_t usb_ugen_hdl, dev_t dev, struct uio *uiop,
10070Sstevel@tonic-gate     cred_t *credp)
10080Sstevel@tonic-gate {
100970Sfrits 	ugen_state_t		*ugenp;
101070Sfrits 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
10115653Slc152243 	    (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
101270Sfrits 
101370Sfrits 	if (usb_ugen_hdl == NULL) {
101470Sfrits 
101570Sfrits 		return (EINVAL);
101670Sfrits 	}
101770Sfrits 	ugenp = usb_ugen_hdl_impl->hdl_ugenp;
101870Sfrits 
101970Sfrits 	if (ugen_is_valid_minor_node(ugenp, dev) != USB_SUCCESS) {
102070Sfrits 
102170Sfrits 		return (EINVAL);
102270Sfrits 	}
102370Sfrits 
10240Sstevel@tonic-gate 	return (physio(ugen_strategy,
10250Sstevel@tonic-gate 	    (struct buf *)0, dev, B_WRITE, ugen_minphys, uiop));
10260Sstevel@tonic-gate }
10270Sstevel@tonic-gate 
10280Sstevel@tonic-gate 
10290Sstevel@tonic-gate /*
10300Sstevel@tonic-gate  * usb_ugen_poll
10310Sstevel@tonic-gate  */
10320Sstevel@tonic-gate int
usb_ugen_poll(usb_ugen_hdl_t usb_ugen_hdl,dev_t dev,short events,int anyyet,short * reventsp,struct pollhead ** phpp)10330Sstevel@tonic-gate usb_ugen_poll(usb_ugen_hdl_t usb_ugen_hdl, dev_t dev, short events,
10340Sstevel@tonic-gate     int anyyet,  short *reventsp, struct pollhead **phpp)
10350Sstevel@tonic-gate {
10360Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
10375653Slc152243 	    (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
10380Sstevel@tonic-gate 	ugen_state_t		*ugenp;
10390Sstevel@tonic-gate 	int			minor_node_type;
10400Sstevel@tonic-gate 	uint_t			ep_index;
10410Sstevel@tonic-gate 	ugen_ep_t		*epp;
10420Sstevel@tonic-gate 
10430Sstevel@tonic-gate 	if (usb_ugen_hdl == NULL) {
10440Sstevel@tonic-gate 
10450Sstevel@tonic-gate 		return (EINVAL);
10460Sstevel@tonic-gate 	}
10470Sstevel@tonic-gate 
10480Sstevel@tonic-gate 	ugenp = usb_ugen_hdl_impl->hdl_ugenp;
104970Sfrits 	if (ugen_is_valid_minor_node(ugenp, dev) != USB_SUCCESS) {
105070Sfrits 
105170Sfrits 		return (EINVAL);
105270Sfrits 	}
10530Sstevel@tonic-gate 
10540Sstevel@tonic-gate 	minor_node_type = UGEN_MINOR_TYPE(ugenp, dev);
10550Sstevel@tonic-gate 	ep_index	= UGEN_MINOR_EPIDX(ugenp, dev);
10560Sstevel@tonic-gate 	epp		= &ugenp->ug_ep[ep_index];
10570Sstevel@tonic-gate 
10580Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
10590Sstevel@tonic-gate 
10600Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_POLL, ugenp->ug_log_hdl,
10610Sstevel@tonic-gate 	    "usb_ugen_poll: "
10620Sstevel@tonic-gate 	    "dev=0x%lx events=0x%x anyyet=0x%x rev=0x%p type=%d "
10630Sstevel@tonic-gate 	    "devstat=0x%x devstate=0x%x",
10640Sstevel@tonic-gate 	    dev, events, anyyet, (void *)reventsp, minor_node_type,
10650Sstevel@tonic-gate 	    ugenp->ug_ds.dev_stat, ugenp->ug_ds.dev_state);
10660Sstevel@tonic-gate 
10670Sstevel@tonic-gate 	*reventsp = 0;
10680Sstevel@tonic-gate 
10690Sstevel@tonic-gate 	if (ugenp->ug_dev_state == USB_DEV_ONLINE) {
10700Sstevel@tonic-gate 		switch (minor_node_type) {
10710Sstevel@tonic-gate 		case UGEN_MINOR_EP_XFER_NODE:
10720Sstevel@tonic-gate 			/* if interrupt IN ep and there is data, set POLLIN */
10730Sstevel@tonic-gate 			if ((UGEN_XFER_TYPE(epp) == USB_EP_ATTR_INTR) &&
10740Sstevel@tonic-gate 			    (UGEN_XFER_DIR(epp) & USB_EP_DIR_IN)) {
10750Sstevel@tonic-gate 
10760Sstevel@tonic-gate 				/*
10770Sstevel@tonic-gate 				 * if we are not polling, force another
10780Sstevel@tonic-gate 				 * read to kick off polling
10790Sstevel@tonic-gate 				 */
10800Sstevel@tonic-gate 				mutex_enter(&epp->ep_mutex);
10810Sstevel@tonic-gate 				if ((epp->ep_data) ||
10820Sstevel@tonic-gate 				    ((epp->ep_state &
10830Sstevel@tonic-gate 				    UGEN_EP_STATE_INTR_IN_POLLING_ON) == 0)) {
10840Sstevel@tonic-gate 					*reventsp |= POLLIN;
10850Sstevel@tonic-gate 				} else if (!anyyet) {
10860Sstevel@tonic-gate 					*phpp = &epp->ep_pollhead;
10870Sstevel@tonic-gate 					epp->ep_state |=
10880Sstevel@tonic-gate 					    UGEN_EP_STATE_INTR_IN_POLL_PENDING;
10890Sstevel@tonic-gate 				}
10900Sstevel@tonic-gate 				mutex_exit(&epp->ep_mutex);
10915653Slc152243 
10925653Slc152243 			} else if ((UGEN_XFER_TYPE(epp) == USB_EP_ATTR_ISOCH) &&
10935653Slc152243 			    (UGEN_XFER_DIR(epp) & USB_EP_DIR_IN)) {
10945653Slc152243 
10955653Slc152243 				/*
10965653Slc152243 				 * if we are not polling, force another
10975653Slc152243 				 * read to kick off polling
10985653Slc152243 				 */
10995653Slc152243 				mutex_enter(&epp->ep_mutex);
11005653Slc152243 				if ((epp->ep_data) ||
11015653Slc152243 				    ((epp->ep_state &
11025653Slc152243 				    UGEN_EP_STATE_ISOC_IN_POLLING_ON) == 0)) {
11035653Slc152243 					*reventsp |= POLLIN;
11045653Slc152243 				} else if (!anyyet) {
11055653Slc152243 					*phpp = &epp->ep_pollhead;
11065653Slc152243 					epp->ep_state |=
11075653Slc152243 					    UGEN_EP_STATE_ISOC_IN_POLL_PENDING;
11085653Slc152243 				}
11095653Slc152243 				mutex_exit(&epp->ep_mutex);
11105653Slc152243 
11110Sstevel@tonic-gate 			} else {
11120Sstevel@tonic-gate 				/* no poll on other ep nodes */
11130Sstevel@tonic-gate 				*reventsp |= POLLERR;
11140Sstevel@tonic-gate 			}
11150Sstevel@tonic-gate 
11160Sstevel@tonic-gate 			break;
11170Sstevel@tonic-gate 		case UGEN_MINOR_DEV_STAT_NODE:
11180Sstevel@tonic-gate 			if (ugenp->ug_ds.dev_stat & UGEN_DEV_STATUS_CHANGED) {
11190Sstevel@tonic-gate 				*reventsp |= POLLIN;
11200Sstevel@tonic-gate 			} else if (!anyyet) {
11210Sstevel@tonic-gate 				*phpp = &ugenp->ug_ds.dev_pollhead;
11220Sstevel@tonic-gate 				ugenp->ug_ds.dev_stat |=
11230Sstevel@tonic-gate 				    UGEN_DEV_STATUS_POLL_PENDING;
11240Sstevel@tonic-gate 			}
11250Sstevel@tonic-gate 
11260Sstevel@tonic-gate 			break;
11270Sstevel@tonic-gate 		case UGEN_MINOR_EP_STAT_NODE:
11280Sstevel@tonic-gate 		default:
11290Sstevel@tonic-gate 			*reventsp |= POLLERR;
11300Sstevel@tonic-gate 
11310Sstevel@tonic-gate 			break;
11320Sstevel@tonic-gate 		}
11330Sstevel@tonic-gate 	} else {
11340Sstevel@tonic-gate 		if (ugenp->ug_ds.dev_stat & UGEN_DEV_STATUS_CHANGED) {
11350Sstevel@tonic-gate 			*reventsp |= POLLHUP|POLLIN;
11360Sstevel@tonic-gate 		} else if (!anyyet) {
11370Sstevel@tonic-gate 			*phpp = &ugenp->ug_ds.dev_pollhead;
11380Sstevel@tonic-gate 			ugenp->ug_ds.dev_stat |=
11395653Slc152243 			    UGEN_DEV_STATUS_POLL_PENDING;
11400Sstevel@tonic-gate 		}
11410Sstevel@tonic-gate 	}
11420Sstevel@tonic-gate 
11430Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
11440Sstevel@tonic-gate 
11450Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_POLL, ugenp->ug_log_hdl,
11460Sstevel@tonic-gate 	    "usb_ugen_poll end: reventsp=0x%x", *reventsp);
11470Sstevel@tonic-gate 
11480Sstevel@tonic-gate 	return (0);
11490Sstevel@tonic-gate }
11500Sstevel@tonic-gate 
11510Sstevel@tonic-gate 
11520Sstevel@tonic-gate /*
11530Sstevel@tonic-gate  * ugen_strategy
11540Sstevel@tonic-gate  */
11550Sstevel@tonic-gate static int
ugen_strategy(struct buf * bp)11560Sstevel@tonic-gate ugen_strategy(struct buf *bp)
11570Sstevel@tonic-gate {
11580Sstevel@tonic-gate 	dev_t		dev = bp->b_edev;
11590Sstevel@tonic-gate 	int		rval = 0;
11600Sstevel@tonic-gate 	ugen_state_t	*ugenp = ugen_devt2state(dev);
11610Sstevel@tonic-gate 	int		minor_node_type = UGEN_MINOR_TYPE(ugenp, dev);
11620Sstevel@tonic-gate 
11630Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
11646898Sfb209375 	    "ugen_strategy: bp=0x%p minor=0x%x", (void *)bp, getminor(dev));
11650Sstevel@tonic-gate 
116670Sfrits 	if (ugen_is_valid_minor_node(ugenp, dev) != USB_SUCCESS) {
116770Sfrits 
116870Sfrits 		return (EINVAL);
116970Sfrits 	}
117070Sfrits 
11710Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
11720Sstevel@tonic-gate 	ugenp->ug_pending_cmds++;
11730Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
11740Sstevel@tonic-gate 
11750Sstevel@tonic-gate 	bp_mapin(bp);
11760Sstevel@tonic-gate 
11770Sstevel@tonic-gate 	switch (minor_node_type) {
11780Sstevel@tonic-gate 	case UGEN_MINOR_EP_XFER_NODE:
11790Sstevel@tonic-gate 		rval = ugen_epx_req(ugenp, bp);
11800Sstevel@tonic-gate 
11810Sstevel@tonic-gate 		break;
11820Sstevel@tonic-gate 	case UGEN_MINOR_EP_STAT_NODE:
11830Sstevel@tonic-gate 		rval = ugen_eps_req(ugenp, bp);
11840Sstevel@tonic-gate 
11850Sstevel@tonic-gate 		break;
11860Sstevel@tonic-gate 	case UGEN_MINOR_DEV_STAT_NODE:
11870Sstevel@tonic-gate 		rval = ugen_ds_req(ugenp, bp);
11880Sstevel@tonic-gate 
11890Sstevel@tonic-gate 		break;
11900Sstevel@tonic-gate 	default:
11910Sstevel@tonic-gate 		rval = EINVAL;
11920Sstevel@tonic-gate 
11930Sstevel@tonic-gate 		break;
11940Sstevel@tonic-gate 	}
11950Sstevel@tonic-gate 
11960Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
11970Sstevel@tonic-gate 	ugenp->ug_pending_cmds--;
11980Sstevel@tonic-gate 
11990Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
12000Sstevel@tonic-gate 	    "ugen_strategy: "
12010Sstevel@tonic-gate 	    "bp=0x%p cnt=%lu resid=%lu err=%d minor=0x%x rval=%d #cmds=%d",
12020Sstevel@tonic-gate 	    (void *)bp, bp->b_bcount, bp->b_resid, geterror(bp),
12030Sstevel@tonic-gate 	    getminor(dev), rval, ugenp->ug_pending_cmds);
12040Sstevel@tonic-gate 
12050Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
12060Sstevel@tonic-gate 
12070Sstevel@tonic-gate 	if (rval) {
12080Sstevel@tonic-gate 		if (geterror(bp) == 0) {
12090Sstevel@tonic-gate 			bioerror(bp, rval);
12100Sstevel@tonic-gate 		}
12110Sstevel@tonic-gate 	}
12120Sstevel@tonic-gate 
12130Sstevel@tonic-gate 	biodone(bp);
12140Sstevel@tonic-gate 
12150Sstevel@tonic-gate 	return (0);
12160Sstevel@tonic-gate }
12170Sstevel@tonic-gate 
12180Sstevel@tonic-gate 
12190Sstevel@tonic-gate /*
12200Sstevel@tonic-gate  * ugen_minphys:
12210Sstevel@tonic-gate  */
12220Sstevel@tonic-gate static void
ugen_minphys(struct buf * bp)12230Sstevel@tonic-gate ugen_minphys(struct buf *bp)
12240Sstevel@tonic-gate {
12250Sstevel@tonic-gate 	dev_t		dev = bp->b_edev;
12260Sstevel@tonic-gate 	ugen_state_t	*ugenp = ugen_devt2state(dev);
12270Sstevel@tonic-gate 	int		minor_node_type = UGEN_MINOR_TYPE(ugenp, dev);
12280Sstevel@tonic-gate 	uint_t		ep_index = UGEN_MINOR_EPIDX(ugenp, dev);
12290Sstevel@tonic-gate 	ugen_ep_t	*epp = &ugenp->ug_ep[ep_index];
12300Sstevel@tonic-gate 
12310Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
12320Sstevel@tonic-gate 	    "ugen_phys: bp=0x%p dev=0x%lx index=%d type=0x%x",
12330Sstevel@tonic-gate 	    (void *)bp, dev, ep_index, minor_node_type);
12340Sstevel@tonic-gate 
12350Sstevel@tonic-gate 	switch (minor_node_type) {
12360Sstevel@tonic-gate 	case UGEN_MINOR_EP_XFER_NODE:
12370Sstevel@tonic-gate 		switch (UGEN_XFER_TYPE(epp)) {
12380Sstevel@tonic-gate 		case USB_EP_ATTR_BULK:
12390Sstevel@tonic-gate 			if (bp->b_bcount > ugenp->ug_max_bulk_xfer_sz) {
12400Sstevel@tonic-gate 				bp->b_bcount = ugenp->ug_max_bulk_xfer_sz;
12410Sstevel@tonic-gate 			}
12420Sstevel@tonic-gate 
12430Sstevel@tonic-gate 			break;
12440Sstevel@tonic-gate 		case USB_EP_ATTR_INTR:
12450Sstevel@tonic-gate 		case USB_EP_ATTR_CONTROL:
12465653Slc152243 		case USB_EP_ATTR_ISOCH:
12470Sstevel@tonic-gate 		default:
12480Sstevel@tonic-gate 
12490Sstevel@tonic-gate 			break;
12500Sstevel@tonic-gate 		}
12510Sstevel@tonic-gate 		break;
12520Sstevel@tonic-gate 	case UGEN_MINOR_EP_STAT_NODE:
12530Sstevel@tonic-gate 	case UGEN_MINOR_DEV_STAT_NODE:
12540Sstevel@tonic-gate 	default:
12550Sstevel@tonic-gate 
12560Sstevel@tonic-gate 		break;
12570Sstevel@tonic-gate 	}
12580Sstevel@tonic-gate }
12590Sstevel@tonic-gate 
12605653Slc152243 /*
12615653Slc152243  * Get bmAttributes and bAddress of the endpoint which is going to
12625653Slc152243  * be opened
12635653Slc152243  */
12645653Slc152243 static int
ugen_get_ep_descr(ugen_state_t * ugenp,dev_t dev,uint8_t * bmAttr,uint8_t * bAddr)12655653Slc152243 ugen_get_ep_descr(ugen_state_t *ugenp, dev_t dev, uint8_t *bmAttr,
12665653Slc152243     uint8_t *bAddr)
12675653Slc152243 {
12685653Slc152243 	uint_t	alt = UGEN_MINOR_ALT(ugenp, dev);
12695653Slc152243 	uint_t	ifc = UGEN_MINOR_IF(ugenp, dev);
12705653Slc152243 	uint_t	cfgidx = UGEN_MINOR_CFGIDX(ugenp, dev);
12715653Slc152243 	usb_cfg_data_t	*dev_cfg;
12725653Slc152243 	usb_if_data_t	*if_data;
12735653Slc152243 	usb_alt_if_data_t *alt_if_data;
12745653Slc152243 	usb_ep_data_t	*ep_data;
12755653Slc152243 	int ep;
12765653Slc152243 	int epidx = UGEN_MINOR_EPIDX(ugenp, dev);
12775653Slc152243 
12785653Slc152243 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
12795653Slc152243 	    "cfg=%d, if=%d, alt=%d, ep=0x%x", cfgidx, ifc,
12805653Slc152243 	    alt, epidx);
12815653Slc152243 
12825653Slc152243 	dev_cfg = &ugenp->ug_dev_data->dev_cfg[cfgidx];
12835653Slc152243 	if_data = &dev_cfg->cfg_if[ifc];
12845653Slc152243 	alt_if_data = &if_data->if_alt[alt];
12855653Slc152243 	for (ep = 0; ep < alt_if_data->altif_n_ep; ep++) {
12865653Slc152243 		ep_data = &alt_if_data->altif_ep[ep];
12875653Slc152243 
12885653Slc152243 		if (usb_get_ep_index(ep_data->ep_descr.
12895653Slc152243 		    bEndpointAddress) == epidx) {
12905653Slc152243 
12915653Slc152243 			*bmAttr = ep_data->ep_descr.bmAttributes;
12925653Slc152243 			*bAddr = ep_data->ep_descr.bEndpointAddress;
12935653Slc152243 
12945653Slc152243 			return (USB_SUCCESS);
12955653Slc152243 		}
12965653Slc152243 	}
12975653Slc152243 
12985653Slc152243 	return (USB_FAILURE);
12995653Slc152243 }
13000Sstevel@tonic-gate 
13010Sstevel@tonic-gate /*
13020Sstevel@tonic-gate  * check whether flag is appropriate for node type
13030Sstevel@tonic-gate  */
13040Sstevel@tonic-gate static int
ugen_check_open_flags(ugen_state_t * ugenp,dev_t dev,int flag)13050Sstevel@tonic-gate ugen_check_open_flags(ugen_state_t *ugenp, dev_t dev, int flag)
13060Sstevel@tonic-gate {
13070Sstevel@tonic-gate 	ugen_ep_t *epp;
13080Sstevel@tonic-gate 	int	minor_node_type = UGEN_MINOR_TYPE(ugenp, dev);
13090Sstevel@tonic-gate 	int	rval = 0;
13105653Slc152243 	uint8_t bmAttribute;
13115653Slc152243 	uint8_t bAddress;
13120Sstevel@tonic-gate 
13130Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
13140Sstevel@tonic-gate 	    "ugen_check_open_flags: "
13150Sstevel@tonic-gate 	    "dev=0x%lx, type=0x%x flag=0x%x idx=%" PRIu64,
13160Sstevel@tonic-gate 	    dev, minor_node_type, flag, UGEN_MINOR_EPIDX(ugenp, dev));
13170Sstevel@tonic-gate 
13180Sstevel@tonic-gate 	switch (minor_node_type) {
13190Sstevel@tonic-gate 	case UGEN_MINOR_EP_XFER_NODE:
13200Sstevel@tonic-gate 		epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
13215653Slc152243 
13225653Slc152243 		/*
13235653Slc152243 		 * Endpoints in two altsetting happen to have the same
13245653Slc152243 		 * bEndpointAddress, but they are different type, e.g,
13255653Slc152243 		 * one is BULK and the other is ISOC. They use the same
13265653Slc152243 		 * slot of ug_ep array. It's OK after switch_alt, because
13275653Slc152243 		 * after alt switch, ep info is updated to the new endpoint.
13285653Slc152243 		 * But it's not right here to use the other EP's info for
13295653Slc152243 		 * checking.
13305653Slc152243 		 */
13315653Slc152243 		if (UGEN_MINOR_EPIDX(ugenp, dev) != 0) {
13325653Slc152243 			if ((rval = ugen_get_ep_descr(ugenp, dev, &bmAttribute,
13335653Slc152243 			    &bAddress)) != USB_SUCCESS) {
13345653Slc152243 				USB_DPRINTF_L2(UGEN_PRINT_XFER,
13355653Slc152243 				    ugenp->ug_log_hdl, "ugen_get_descr: fail");
13365653Slc152243 
13375653Slc152243 				return (ENODEV);
13385653Slc152243 			}
13395653Slc152243 		} else {
13405653Slc152243 			bmAttribute = ugen_default_ep_descr.bmAttributes;
13415653Slc152243 			bAddress = ugen_default_ep_descr.bEndpointAddress;
13425653Slc152243 		}
13435653Slc152243 
13445653Slc152243 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
13455653Slc152243 		    "ugen_check_open_flags: epp = %p,"
13466898Sfb209375 		    "epp type = %d, bmAttr =0x%x, bAddr = 0x%02x", (void *)epp,
13475653Slc152243 		    UGEN_XFER_TYPE(epp), bmAttribute, bAddress);
13485653Slc152243 
13495653Slc152243 		switch (bmAttribute & USB_EP_ATTR_MASK) {
13500Sstevel@tonic-gate 		case USB_EP_ATTR_CONTROL:
13510Sstevel@tonic-gate 			/* read and write must be set, ndelay not allowed */
13520Sstevel@tonic-gate 			if (((flag & (FREAD | FWRITE)) != (FREAD | FWRITE)) ||
13530Sstevel@tonic-gate 			    (flag & (FNDELAY | FNONBLOCK))) {
13540Sstevel@tonic-gate 				rval = EACCES;
13550Sstevel@tonic-gate 			}
13560Sstevel@tonic-gate 
13570Sstevel@tonic-gate 			break;
13585653Slc152243 		case USB_EP_ATTR_ISOCH:
13595653Slc152243 			/* read and write must be set */
13605653Slc152243 			if ((flag & (FREAD | FWRITE)) != (FREAD | FWRITE)) {
13615653Slc152243 				rval = EACCES;
13625653Slc152243 			}
13635653Slc152243 
13645653Slc152243 			break;
13650Sstevel@tonic-gate 		case USB_EP_ATTR_BULK:
13660Sstevel@tonic-gate 			/* ndelay not allowed */
13670Sstevel@tonic-gate 			if (flag & (FNDELAY | FNONBLOCK)) {
13680Sstevel@tonic-gate 				rval = EACCES;
13690Sstevel@tonic-gate 
13700Sstevel@tonic-gate 				break;
13710Sstevel@tonic-gate 			}
13720Sstevel@tonic-gate 			/*FALLTHRU*/
13730Sstevel@tonic-gate 		case USB_EP_ATTR_INTR:
13740Sstevel@tonic-gate 			/* check flag versus direction */
13755653Slc152243 			if ((flag & FWRITE) && (bAddress & USB_EP_DIR_IN)) {
13760Sstevel@tonic-gate 				rval = EACCES;
13770Sstevel@tonic-gate 			}
13780Sstevel@tonic-gate 			if ((flag & FREAD) &&
13795653Slc152243 			    ((bAddress & USB_EP_DIR_IN) == 0)) {
13800Sstevel@tonic-gate 				rval = EACCES;
13810Sstevel@tonic-gate 			}
13820Sstevel@tonic-gate 
13830Sstevel@tonic-gate 			break;
13840Sstevel@tonic-gate 		default:
13850Sstevel@tonic-gate 			rval = EINVAL;
13860Sstevel@tonic-gate 
13870Sstevel@tonic-gate 			break;
13880Sstevel@tonic-gate 		}
13890Sstevel@tonic-gate 		break;
13900Sstevel@tonic-gate 	case UGEN_MINOR_DEV_STAT_NODE:
13910Sstevel@tonic-gate 		/* only reads are supported */
13920Sstevel@tonic-gate 		if (flag & FWRITE) {
13930Sstevel@tonic-gate 			rval = EACCES;
13940Sstevel@tonic-gate 		}
13950Sstevel@tonic-gate 
13960Sstevel@tonic-gate 		break;
13970Sstevel@tonic-gate 	case UGEN_MINOR_EP_STAT_NODE:
13980Sstevel@tonic-gate 
13990Sstevel@tonic-gate 		break;
14000Sstevel@tonic-gate 	default:
14010Sstevel@tonic-gate 		rval = EINVAL;
14020Sstevel@tonic-gate 
14030Sstevel@tonic-gate 		break;
14040Sstevel@tonic-gate 	}
14050Sstevel@tonic-gate 
14060Sstevel@tonic-gate 	return (rval);
14070Sstevel@tonic-gate }
14080Sstevel@tonic-gate 
14090Sstevel@tonic-gate 
14100Sstevel@tonic-gate /*
14110Sstevel@tonic-gate  * endpoint management
14120Sstevel@tonic-gate  *
14130Sstevel@tonic-gate  * create/initialize all endpoint xfer/stat structures
14140Sstevel@tonic-gate  */
14150Sstevel@tonic-gate static int
ugen_epxs_init(ugen_state_t * ugenp)14160Sstevel@tonic-gate ugen_epxs_init(ugen_state_t *ugenp)
14170Sstevel@tonic-gate {
14180Sstevel@tonic-gate 	usb_cfg_data_t	*dev_cfg = ugenp->ug_dev_data->dev_cfg;
14190Sstevel@tonic-gate 	uchar_t		cfgidx, cfgval, iface, alt, ep;
14200Sstevel@tonic-gate 	usb_if_data_t	*if_data;
14210Sstevel@tonic-gate 	usb_alt_if_data_t *alt_if_data;
14220Sstevel@tonic-gate 	usb_ep_data_t	*ep_data;
14230Sstevel@tonic-gate 
14240Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
14250Sstevel@tonic-gate 	    "ugen_epxs_init:");
14260Sstevel@tonic-gate 
14270Sstevel@tonic-gate 	/* initialize each ep's mutex first */
14280Sstevel@tonic-gate 	for (ep = 0; ep < UGEN_N_ENDPOINTS; ep++) {
14290Sstevel@tonic-gate 		mutex_init(&ugenp->ug_ep[ep].ep_mutex, NULL, MUTEX_DRIVER,
14300Sstevel@tonic-gate 		    ugenp->ug_dev_data->dev_iblock_cookie);
14310Sstevel@tonic-gate 	}
14320Sstevel@tonic-gate 
14330Sstevel@tonic-gate 	/* init default ep as it does not have a descriptor */
14340Sstevel@tonic-gate 	if (ugen_epxs_data_init(ugenp, NULL, 0, 0,
14350Sstevel@tonic-gate 	    ugenp->ug_dev_data->dev_curr_if, 0) != USB_SUCCESS) {
14360Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
14370Sstevel@tonic-gate 		    "creating default endpoint failed");
14380Sstevel@tonic-gate 
14390Sstevel@tonic-gate 		return (USB_FAILURE);
14400Sstevel@tonic-gate 	}
14410Sstevel@tonic-gate 
14420Sstevel@tonic-gate 	/*
14430Sstevel@tonic-gate 	 * walk all endpoints of all alternates of all interfaces of
14440Sstevel@tonic-gate 	 * all cfs
14450Sstevel@tonic-gate 	 */
14460Sstevel@tonic-gate 	for (cfgidx = 0; cfgidx < ugenp->ug_dev_data->dev_n_cfg; cfgidx++) {
14470Sstevel@tonic-gate 		dev_cfg = &ugenp->ug_dev_data->dev_cfg[cfgidx];
14480Sstevel@tonic-gate 		cfgval = dev_cfg->cfg_descr.bConfigurationValue;
14490Sstevel@tonic-gate 		for (iface = 0; iface < dev_cfg->cfg_n_if; iface++) {
14500Sstevel@tonic-gate 			if_data = &dev_cfg->cfg_if[iface];
14510Sstevel@tonic-gate 			for (alt = 0; alt < if_data->if_n_alt; alt++) {
14520Sstevel@tonic-gate 				alt_if_data = &if_data->if_alt[alt];
14530Sstevel@tonic-gate 				for (ep = 0; ep < alt_if_data->altif_n_ep;
14540Sstevel@tonic-gate 				    ep++) {
14550Sstevel@tonic-gate 					ep_data = &alt_if_data->altif_ep[ep];
14560Sstevel@tonic-gate 					if (ugen_epxs_data_init(ugenp, ep_data,
14570Sstevel@tonic-gate 					    cfgval, cfgidx, iface, alt) !=
14580Sstevel@tonic-gate 					    USB_SUCCESS) {
14590Sstevel@tonic-gate 
14600Sstevel@tonic-gate 						return (USB_FAILURE);
14610Sstevel@tonic-gate 					}
14620Sstevel@tonic-gate 				}
14630Sstevel@tonic-gate 			}
14640Sstevel@tonic-gate 		}
14650Sstevel@tonic-gate 	}
14660Sstevel@tonic-gate 
14670Sstevel@tonic-gate 	return (USB_SUCCESS);
14680Sstevel@tonic-gate }
14690Sstevel@tonic-gate 
14700Sstevel@tonic-gate 
14710Sstevel@tonic-gate /*
14720Sstevel@tonic-gate  * initialize one endpoint structure
14730Sstevel@tonic-gate  */
14740Sstevel@tonic-gate static int
ugen_epxs_data_init(ugen_state_t * ugenp,usb_ep_data_t * ep_data,uchar_t cfgval,uchar_t cfgidx,uchar_t iface,uchar_t alt)14750Sstevel@tonic-gate ugen_epxs_data_init(ugen_state_t *ugenp, usb_ep_data_t *ep_data,
14760Sstevel@tonic-gate 	uchar_t cfgval, uchar_t cfgidx, uchar_t iface, uchar_t alt)
14770Sstevel@tonic-gate {
14780Sstevel@tonic-gate 	int			ep_index;
14790Sstevel@tonic-gate 	ugen_ep_t		*epp;
14800Sstevel@tonic-gate 	usb_ep_descr_t		*ep_descr;
14810Sstevel@tonic-gate 
14820Sstevel@tonic-gate 	/* is this the default endpoint */
14830Sstevel@tonic-gate 	ep_index = (ep_data == NULL) ? 0 :
14845653Slc152243 	    usb_get_ep_index(ep_data->ep_descr.bEndpointAddress);
14850Sstevel@tonic-gate 	epp = &ugenp->ug_ep[ep_index];
14860Sstevel@tonic-gate 
14870Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
14880Sstevel@tonic-gate 	    "ugen_epxs_data_init: "
14890Sstevel@tonic-gate 	    "cfgval=%d cfgidx=%d iface=%d alt=%d ep_index=%d",
14900Sstevel@tonic-gate 	    cfgval, cfgidx, iface, alt, ep_index);
14910Sstevel@tonic-gate 
14920Sstevel@tonic-gate 	ep_descr = (ep_data == NULL) ? &ugen_default_ep_descr :
14935653Slc152243 	    &ep_data->ep_descr;
14940Sstevel@tonic-gate 
14950Sstevel@tonic-gate 	mutex_init(&epp->ep_mutex, NULL, MUTEX_DRIVER,
14965653Slc152243 	    ugenp->ug_dev_data->dev_iblock_cookie);
14970Sstevel@tonic-gate 
14980Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
14990Sstevel@tonic-gate 
15000Sstevel@tonic-gate 	/* initialize if not yet init'ed */
15010Sstevel@tonic-gate 	if (epp->ep_state == UGEN_EP_STATE_NONE) {
15020Sstevel@tonic-gate 		epp->ep_descr		= *ep_descr;
15030Sstevel@tonic-gate 		epp->ep_cfgidx		= cfgidx;
15040Sstevel@tonic-gate 		epp->ep_if		= iface;
15050Sstevel@tonic-gate 		epp->ep_alt		= alt;
15060Sstevel@tonic-gate 		epp->ep_state		= UGEN_EP_STATE_ACTIVE;
15070Sstevel@tonic-gate 		epp->ep_lcmd_status	= USB_LC_STAT_NOERROR;
15080Sstevel@tonic-gate 		epp->ep_pipe_policy.pp_max_async_reqs = 1;
15090Sstevel@tonic-gate 
15100Sstevel@tonic-gate 		cv_init(&epp->ep_wait_cv, NULL, CV_DRIVER, NULL);
15110Sstevel@tonic-gate 		epp->ep_ser_cookie	= usb_init_serialization(
15125653Slc152243 		    ugenp->ug_dip, 0);
15130Sstevel@tonic-gate 	}
15140Sstevel@tonic-gate 
15150Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
15160Sstevel@tonic-gate 
15170Sstevel@tonic-gate 	/* create minor nodes for all alts */
15180Sstevel@tonic-gate 
15190Sstevel@tonic-gate 	return (ugen_epxs_minor_nodes_create(ugenp, ep_descr,
15200Sstevel@tonic-gate 	    cfgval, cfgidx, iface, alt));
15210Sstevel@tonic-gate }
15220Sstevel@tonic-gate 
15230Sstevel@tonic-gate 
15240Sstevel@tonic-gate /*
15250Sstevel@tonic-gate  * undo all endpoint initializations
15260Sstevel@tonic-gate  */
15270Sstevel@tonic-gate static void
ugen_epxs_destroy(ugen_state_t * ugenp)15280Sstevel@tonic-gate ugen_epxs_destroy(ugen_state_t *ugenp)
15290Sstevel@tonic-gate {
15300Sstevel@tonic-gate 	int	i;
15310Sstevel@tonic-gate 
15320Sstevel@tonic-gate 	for (i = 0; i < UGEN_N_ENDPOINTS; i++) {
15330Sstevel@tonic-gate 		ugen_epxs_data_destroy(ugenp, &ugenp->ug_ep[i]);
15340Sstevel@tonic-gate 	}
15350Sstevel@tonic-gate }
15360Sstevel@tonic-gate 
15370Sstevel@tonic-gate 
15380Sstevel@tonic-gate static void
ugen_epxs_data_destroy(ugen_state_t * ugenp,ugen_ep_t * epp)15390Sstevel@tonic-gate ugen_epxs_data_destroy(ugen_state_t *ugenp, ugen_ep_t *epp)
15400Sstevel@tonic-gate {
15410Sstevel@tonic-gate 	if (epp) {
15420Sstevel@tonic-gate 		ASSERT(epp->ep_ph == NULL);
15430Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
15440Sstevel@tonic-gate 		if (epp->ep_state != UGEN_EP_STATE_NONE) {
15450Sstevel@tonic-gate 			USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
15460Sstevel@tonic-gate 			    "ugen_epxs_destroy: addr=0x%x",
15470Sstevel@tonic-gate 			    UGEN_XFER_ADDR(epp));
15480Sstevel@tonic-gate 			cv_destroy(&epp->ep_wait_cv);
15490Sstevel@tonic-gate 		}
15500Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
15510Sstevel@tonic-gate 
15520Sstevel@tonic-gate 		mutex_destroy(&epp->ep_mutex);
15530Sstevel@tonic-gate 		usb_fini_serialization(epp->ep_ser_cookie);
15540Sstevel@tonic-gate 	}
15550Sstevel@tonic-gate }
15560Sstevel@tonic-gate 
15570Sstevel@tonic-gate 
15580Sstevel@tonic-gate /*
15590Sstevel@tonic-gate  * create endpoint status and xfer minor nodes
15600Sstevel@tonic-gate  *
15610Sstevel@tonic-gate  * The actual minor node needs more than 18 bits. We create a table
15620Sstevel@tonic-gate  * and store the full minor node in this table and use the
15630Sstevel@tonic-gate  * index in the table as minor node. This allows 256 minor nodes
15640Sstevel@tonic-gate  * and 1024 instances
15650Sstevel@tonic-gate  */
15660Sstevel@tonic-gate static int
ugen_epxs_minor_nodes_create(ugen_state_t * ugenp,usb_ep_descr_t * ep_descr,uchar_t cfgval,uchar_t cfgidx,uchar_t iface,uchar_t alt)15670Sstevel@tonic-gate ugen_epxs_minor_nodes_create(ugen_state_t *ugenp, usb_ep_descr_t *ep_descr,
15680Sstevel@tonic-gate     uchar_t cfgval, uchar_t cfgidx, uchar_t iface, uchar_t alt)
15690Sstevel@tonic-gate {
15700Sstevel@tonic-gate 	char		node_name[32], *type;
15710Sstevel@tonic-gate 	int		vid = ugenp->ug_dev_data->dev_descr->idVendor;
15720Sstevel@tonic-gate 	int		pid = ugenp->ug_dev_data->dev_descr->idProduct;
15730Sstevel@tonic-gate 	minor_t		minor;
15740Sstevel@tonic-gate 	int		minor_index;
15750Sstevel@tonic-gate 	ugen_minor_t	minor_code, minor_code_base;
15760Sstevel@tonic-gate 	int		owns_device = (usb_owns_device(ugenp->ug_dip) ?
15775653Slc152243 	    UGEN_OWNS_DEVICE : 0);
15780Sstevel@tonic-gate 	int		ep_index =
15795653Slc152243 	    usb_get_ep_index(ep_descr->bEndpointAddress);
15800Sstevel@tonic-gate 	int		ep_addr =
15815653Slc152243 	    ep_descr->bEndpointAddress & USB_EP_NUM_MASK;
15820Sstevel@tonic-gate 	int		ep_type =
15835653Slc152243 	    ep_descr->bmAttributes & USB_EP_ATTR_MASK;
15840Sstevel@tonic-gate 	int		ep_dir =
15855653Slc152243 	    ep_descr->bEndpointAddress & USB_EP_DIR_IN;
15860Sstevel@tonic-gate 
15870Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
15880Sstevel@tonic-gate 	    "ugen_epxs_minor_nodes_create: "
15890Sstevel@tonic-gate 	    "cfgval=%d cfgidx=%d if=%d alt=%d ep=0x%x",
15900Sstevel@tonic-gate 	    cfgval, cfgidx, iface, alt, ep_addr);
15910Sstevel@tonic-gate 
15920Sstevel@tonic-gate 	if (ugenp->ug_instance >= UGEN_MINOR_INSTANCE_LIMIT(ugenp)) {
15930Sstevel@tonic-gate 		USB_DPRINTF_L0(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
15940Sstevel@tonic-gate 		    "instance number too high (%d)", ugenp->ug_instance);
15950Sstevel@tonic-gate 
15960Sstevel@tonic-gate 		return (USB_FAILURE);
15970Sstevel@tonic-gate 	}
15980Sstevel@tonic-gate 
15990Sstevel@tonic-gate 	/* create stat and xfer minor node */
16000Sstevel@tonic-gate 	minor_code_base =
16015653Slc152243 	    ((ugen_minor_t)cfgval) << UGEN_MINOR_CFGVAL_SHIFT |
16025653Slc152243 	    ((ugen_minor_t)cfgidx) << UGEN_MINOR_CFGIDX_SHIFT |
16035653Slc152243 	    iface << UGEN_MINOR_IF_SHIFT |
16045653Slc152243 	    alt << UGEN_MINOR_ALT_SHIFT |
16055653Slc152243 	    ep_index << UGEN_MINOR_EPIDX_SHIFT | owns_device;
16060Sstevel@tonic-gate 	minor_code = minor_code_base | UGEN_MINOR_EP_XFER_NODE;
16070Sstevel@tonic-gate 
16080Sstevel@tonic-gate 	minor_index = ugen_minor_index_create(ugenp, minor_code);
16090Sstevel@tonic-gate 	if (minor_index < 0) {
16100Sstevel@tonic-gate 		USB_DPRINTF_L1(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
16110Sstevel@tonic-gate 		    "too many minor nodes, "
16120Sstevel@tonic-gate 		    "cannot create %d.%d.%d.%x",
16130Sstevel@tonic-gate 		    cfgval, iface, alt, ep_addr);
16140Sstevel@tonic-gate 		/* carry on regardless */
16150Sstevel@tonic-gate 
16160Sstevel@tonic-gate 		return (USB_SUCCESS);
16170Sstevel@tonic-gate 	}
16180Sstevel@tonic-gate 	minor = (minor_index << UGEN_MINOR_IDX_SHIFT(ugenp)) |
16195653Slc152243 	    ugenp->ug_instance << UGEN_MINOR_INSTANCE_SHIFT(ugenp);
16200Sstevel@tonic-gate 
16210Sstevel@tonic-gate 	if (ep_type == USB_EP_ATTR_CONTROL) {
16220Sstevel@tonic-gate 		type = "cntrl";
16230Sstevel@tonic-gate 	} else {
16240Sstevel@tonic-gate 		type = (ep_dir & USB_EP_DIR_IN) ? "in" : "out";
16250Sstevel@tonic-gate 	}
16260Sstevel@tonic-gate 
16270Sstevel@tonic-gate 	/*
16280Sstevel@tonic-gate 	 * xfer ep node name:
16290Sstevel@tonic-gate 	 * vid.pid.[in|out|cntrl].[<cfg>.][if<iface>.][<alt>.]<ep addr>
16300Sstevel@tonic-gate 	 */
16310Sstevel@tonic-gate 	if ((ep_addr == 0) && owns_device) {
16320Sstevel@tonic-gate 		(void) sprintf(node_name, "%x.%x.%s%d",
16330Sstevel@tonic-gate 		    vid, pid, type, ep_addr);
16340Sstevel@tonic-gate 	} else if (cfgidx == 0 && alt == 0) {
16350Sstevel@tonic-gate 		(void) sprintf(node_name, "%x.%x.if%d%s%d",
16360Sstevel@tonic-gate 		    vid, pid, iface, type, ep_addr);
16370Sstevel@tonic-gate 	} else if (cfgidx == 0 && alt != 0) {
16380Sstevel@tonic-gate 		(void) sprintf(node_name, "%x.%x.if%d.%d%s%d",
16390Sstevel@tonic-gate 		    vid, pid, iface, alt, type, ep_addr);
16400Sstevel@tonic-gate 	} else if (cfgidx != 0 && alt == 0) {
16410Sstevel@tonic-gate 		(void) sprintf(node_name, "%x.%x.cfg%dif%d%s%d",
16420Sstevel@tonic-gate 		    vid, pid, cfgval, iface, type, ep_addr);
16430Sstevel@tonic-gate 	} else if (cfgidx != 0 && alt != 0) {
16440Sstevel@tonic-gate 		(void) sprintf(node_name, "%x.%x.cfg%dif%d.%d%s%d",
16450Sstevel@tonic-gate 		    vid, pid, cfgval, iface, alt,
16460Sstevel@tonic-gate 		    type, ep_addr);
16470Sstevel@tonic-gate 	}
16480Sstevel@tonic-gate 
16490Sstevel@tonic-gate 	USB_DPRINTF_L3(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
16500Sstevel@tonic-gate 	    "minor=0x%x index=%d code=0x%" PRIx64 " name=%s",
16510Sstevel@tonic-gate 	    minor, minor_index, minor_code, node_name);
16520Sstevel@tonic-gate 
16530Sstevel@tonic-gate 	ASSERT(minor < L_MAXMIN);
16540Sstevel@tonic-gate 
16550Sstevel@tonic-gate 	if ((ddi_create_minor_node(ugenp->ug_dip, node_name,
16560Sstevel@tonic-gate 	    S_IFCHR, minor, DDI_NT_UGEN, 0)) != DDI_SUCCESS) {
16570Sstevel@tonic-gate 
16580Sstevel@tonic-gate 		return (USB_FAILURE);
16590Sstevel@tonic-gate 	}
16600Sstevel@tonic-gate 
16610Sstevel@tonic-gate 	ugen_store_devt(ugenp, minor);
16620Sstevel@tonic-gate 
16630Sstevel@tonic-gate 	minor_code = minor_code_base | UGEN_MINOR_EP_STAT_NODE;
16640Sstevel@tonic-gate 	minor_index = ugen_minor_index_create(ugenp, minor_code);
16650Sstevel@tonic-gate 	if (minor_index < 0) {
16660Sstevel@tonic-gate 		USB_DPRINTF_L1(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
16670Sstevel@tonic-gate 		    "too many minor nodes, "
16680Sstevel@tonic-gate 		    "cannot create %d.%d.%d.%x stat",
16690Sstevel@tonic-gate 		    cfgval, iface, alt,
16700Sstevel@tonic-gate 		    ep_descr->bEndpointAddress);
16710Sstevel@tonic-gate 		/* carry on regardless */
16720Sstevel@tonic-gate 
16730Sstevel@tonic-gate 		return (USB_SUCCESS);
16740Sstevel@tonic-gate 	}
16750Sstevel@tonic-gate 	minor = (minor_index << UGEN_MINOR_IDX_SHIFT(ugenp)) |
16765653Slc152243 	    ugenp->ug_instance << UGEN_MINOR_INSTANCE_SHIFT(ugenp);
16770Sstevel@tonic-gate 
16780Sstevel@tonic-gate 	(void) strcat(node_name, "stat");
16790Sstevel@tonic-gate 
16800Sstevel@tonic-gate 	USB_DPRINTF_L3(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
16810Sstevel@tonic-gate 	    "minor=0x%x index=%d code=0x%" PRIx64 " name=%s",
16820Sstevel@tonic-gate 	    minor, minor_index, minor_code, node_name);
16830Sstevel@tonic-gate 
16840Sstevel@tonic-gate 	ASSERT(minor < L_MAXMIN);
16850Sstevel@tonic-gate 
16860Sstevel@tonic-gate 	if ((ddi_create_minor_node(ugenp->ug_dip, node_name,
16870Sstevel@tonic-gate 	    S_IFCHR, minor, DDI_NT_UGEN, 0)) != DDI_SUCCESS) {
16880Sstevel@tonic-gate 
16890Sstevel@tonic-gate 		return (USB_FAILURE);
16900Sstevel@tonic-gate 	}
16910Sstevel@tonic-gate 
16920Sstevel@tonic-gate 	ugen_store_devt(ugenp, minor);
16930Sstevel@tonic-gate 
16940Sstevel@tonic-gate 	return (USB_SUCCESS);
16950Sstevel@tonic-gate }
16960Sstevel@tonic-gate 
16970Sstevel@tonic-gate 
16980Sstevel@tonic-gate /*
16990Sstevel@tonic-gate  * close all non-default pipes and drain default pipe
17000Sstevel@tonic-gate  */
17010Sstevel@tonic-gate static void
ugen_epx_shutdown(ugen_state_t * ugenp)17020Sstevel@tonic-gate ugen_epx_shutdown(ugen_state_t *ugenp)
17030Sstevel@tonic-gate {
17040Sstevel@tonic-gate 	int	i;
17050Sstevel@tonic-gate 
17060Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
17070Sstevel@tonic-gate 	    "ugen_epx_shutdown:");
17080Sstevel@tonic-gate 
17090Sstevel@tonic-gate 	for (i = 0; i < UGEN_N_ENDPOINTS; i++) {
17100Sstevel@tonic-gate 		ugen_ep_t *epp = &ugenp->ug_ep[i];
17110Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
17120Sstevel@tonic-gate 		if (epp->ep_state != UGEN_EP_STATE_NONE) {
17130Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
17140Sstevel@tonic-gate 			(void) usb_serialize_access(epp->ep_ser_cookie,
17155653Slc152243 			    USB_WAIT, 0);
17160Sstevel@tonic-gate 			(void) ugen_epx_close_pipe(ugenp, epp);
17170Sstevel@tonic-gate 			usb_release_access(epp->ep_ser_cookie);
17180Sstevel@tonic-gate 		} else {
17190Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
17200Sstevel@tonic-gate 		}
17210Sstevel@tonic-gate 	}
17220Sstevel@tonic-gate }
17230Sstevel@tonic-gate 
17240Sstevel@tonic-gate 
17250Sstevel@tonic-gate /*
17260Sstevel@tonic-gate  * find cfg index corresponding to cfg value
17270Sstevel@tonic-gate  */
17280Sstevel@tonic-gate static int
ugen_cfgval2idx(ugen_state_t * ugenp,uint_t cfgval)17290Sstevel@tonic-gate ugen_cfgval2idx(ugen_state_t *ugenp, uint_t cfgval)
17300Sstevel@tonic-gate {
17310Sstevel@tonic-gate 	usb_cfg_data_t	*dev_cfg = ugenp->ug_dev_data->dev_cfg;
17320Sstevel@tonic-gate 	int		cfgidx;
17330Sstevel@tonic-gate 
17340Sstevel@tonic-gate 	for (cfgidx = 0; cfgidx < ugenp->ug_dev_data->dev_n_cfg; cfgidx++) {
17350Sstevel@tonic-gate 		dev_cfg = &ugenp->ug_dev_data->dev_cfg[cfgidx];
17360Sstevel@tonic-gate 		if (cfgval == dev_cfg->cfg_descr.bConfigurationValue) {
17370Sstevel@tonic-gate 
17380Sstevel@tonic-gate 			return (cfgidx);
17390Sstevel@tonic-gate 		}
17400Sstevel@tonic-gate 	}
17410Sstevel@tonic-gate 
17420Sstevel@tonic-gate 	ASSERT(cfgidx < ugenp->ug_dev_data->dev_n_cfg);
17430Sstevel@tonic-gate 
17440Sstevel@tonic-gate 	return (0);
17450Sstevel@tonic-gate }
17460Sstevel@tonic-gate 
17470Sstevel@tonic-gate 
17480Sstevel@tonic-gate /*
17490Sstevel@tonic-gate  * check if any node is open
17500Sstevel@tonic-gate  */
17510Sstevel@tonic-gate static int
ugen_epxs_check_open_nodes(ugen_state_t * ugenp)17520Sstevel@tonic-gate ugen_epxs_check_open_nodes(ugen_state_t *ugenp)
17530Sstevel@tonic-gate {
17540Sstevel@tonic-gate 	int	i;
17550Sstevel@tonic-gate 
17560Sstevel@tonic-gate 	for (i = 1; i < UGEN_N_ENDPOINTS; i++) {
17570Sstevel@tonic-gate 		ugen_ep_t *epp = &ugenp->ug_ep[i];
17580Sstevel@tonic-gate 
17590Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
17600Sstevel@tonic-gate 
17610Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
17620Sstevel@tonic-gate 		    "ugen_epxs_check_open_nodes: epp=%d, ep_state=0x%x",
17630Sstevel@tonic-gate 		    i, epp->ep_state);
17640Sstevel@tonic-gate 
17650Sstevel@tonic-gate 		if (epp->ep_state & UGEN_EP_STATE_XS_OPEN) {
17660Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
17670Sstevel@tonic-gate 
17680Sstevel@tonic-gate 			return (USB_SUCCESS);
17690Sstevel@tonic-gate 		}
17700Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
17710Sstevel@tonic-gate 	}
17720Sstevel@tonic-gate 
17730Sstevel@tonic-gate 	return (USB_FAILURE);
17740Sstevel@tonic-gate }
17750Sstevel@tonic-gate 
17760Sstevel@tonic-gate 
17770Sstevel@tonic-gate /*
17780Sstevel@tonic-gate  * check if we can switch alternate
17790Sstevel@tonic-gate  */
17800Sstevel@tonic-gate static int
ugen_epxs_check_alt_switch(ugen_state_t * ugenp,uchar_t iface,uchar_t cfgidx)17810Sstevel@tonic-gate ugen_epxs_check_alt_switch(ugen_state_t *ugenp, uchar_t iface, uchar_t cfgidx)
17820Sstevel@tonic-gate {
17830Sstevel@tonic-gate 	int	i;
17840Sstevel@tonic-gate 
17850Sstevel@tonic-gate 	for (i = 1; i < UGEN_N_ENDPOINTS; i++) {
17860Sstevel@tonic-gate 		ugen_ep_t *epp = &ugenp->ug_ep[i];
17870Sstevel@tonic-gate 
17880Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
17890Sstevel@tonic-gate 
17900Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
17910Sstevel@tonic-gate 		    "ugen_epxs_check_alt_switch: epp=%d, ep_state=0x%x",
17920Sstevel@tonic-gate 		    i, epp->ep_state);
17930Sstevel@tonic-gate 
17940Sstevel@tonic-gate 		/*
17950Sstevel@tonic-gate 		 * if the endpoint is open and part of this cfg and interface
17960Sstevel@tonic-gate 		 * then we cannot switch alternates
17970Sstevel@tonic-gate 		 */
17980Sstevel@tonic-gate 		if ((epp->ep_state & UGEN_EP_STATE_XS_OPEN) &&
17990Sstevel@tonic-gate 		    (epp->ep_cfgidx == cfgidx) &&
18000Sstevel@tonic-gate 		    (epp->ep_if == iface)) {
18010Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
18020Sstevel@tonic-gate 
18030Sstevel@tonic-gate 			return (USB_FAILURE);
18040Sstevel@tonic-gate 		}
18050Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
18060Sstevel@tonic-gate 	}
18070Sstevel@tonic-gate 
18080Sstevel@tonic-gate 	return (USB_SUCCESS);
18090Sstevel@tonic-gate }
18100Sstevel@tonic-gate 
18110Sstevel@tonic-gate 
18120Sstevel@tonic-gate /*
18130Sstevel@tonic-gate  * implicit switch to new cfg and alt
18140Sstevel@tonic-gate  * If a crummy device fails usb_get_cfg or usb_get_alt_if, we carry on
18150Sstevel@tonic-gate  * regardless so at least the device can be opened.
18160Sstevel@tonic-gate  */
18170Sstevel@tonic-gate static int
ugen_epxs_switch_cfg_alt(ugen_state_t * ugenp,ugen_ep_t * epp,dev_t dev)18180Sstevel@tonic-gate ugen_epxs_switch_cfg_alt(ugen_state_t *ugenp, ugen_ep_t *epp, dev_t dev)
18190Sstevel@tonic-gate {
18200Sstevel@tonic-gate 	int	rval = USB_SUCCESS;
18210Sstevel@tonic-gate 	uint_t	alt;
18220Sstevel@tonic-gate 	uint_t	new_alt = UGEN_MINOR_ALT(ugenp, dev);
18230Sstevel@tonic-gate 	uint_t	new_if = UGEN_MINOR_IF(ugenp, dev);
18240Sstevel@tonic-gate 	uint_t	cur_if = epp->ep_if;
18250Sstevel@tonic-gate 	uint_t	new_cfgidx = UGEN_MINOR_CFGIDX(ugenp, dev);
18260Sstevel@tonic-gate 	uint_t	cur_cfgidx;
18270Sstevel@tonic-gate 	uint_t	cfgval;
18280Sstevel@tonic-gate 	int	switched = 0;
18290Sstevel@tonic-gate 
18300Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
18310Sstevel@tonic-gate 	    "ugen_epxs_switch_cfg_alt: old cfgidx=%d, if=%d alt=%d",
18320Sstevel@tonic-gate 	    epp->ep_cfgidx, epp->ep_if, epp->ep_alt);
18330Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
18340Sstevel@tonic-gate 	    "new cfgidx=%d, if=%d alt=%d ep_state=0x%x",
18350Sstevel@tonic-gate 	    new_cfgidx, new_if, new_alt, epp->ep_state);
18360Sstevel@tonic-gate 
18370Sstevel@tonic-gate 	/* no need to switch if there is only 1 cfg, 1 iface and no alts */
18380Sstevel@tonic-gate 	if ((new_if == 0) && (new_alt == 0) &&
18390Sstevel@tonic-gate 	    (ugenp->ug_dev_data->dev_n_cfg == 1) &&
18400Sstevel@tonic-gate 	    (ugenp->ug_dev_data->dev_cfg[0].cfg_n_if == 1) &&
18410Sstevel@tonic-gate 	    (ugenp->ug_dev_data->
18420Sstevel@tonic-gate 	    dev_cfg[0].cfg_if[new_if].if_n_alt == 1)) {
18430Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
18440Sstevel@tonic-gate 		    "no need for switching: n_cfg=%d n_alt=%d",
18450Sstevel@tonic-gate 		    ugenp->ug_dev_data->dev_n_cfg,
18460Sstevel@tonic-gate 		    ugenp->ug_dev_data->
18475653Slc152243 		    dev_cfg[0].cfg_if[new_if].if_n_alt);
18480Sstevel@tonic-gate 
18490Sstevel@tonic-gate 		ASSERT(epp->ep_alt == new_alt);
18500Sstevel@tonic-gate 		ASSERT(epp->ep_cfgidx == new_cfgidx);
18510Sstevel@tonic-gate 		ASSERT(epp->ep_if == new_if);
18520Sstevel@tonic-gate 
18530Sstevel@tonic-gate 		return (rval);
18540Sstevel@tonic-gate 	}
18550Sstevel@tonic-gate 
18560Sstevel@tonic-gate 	/* no switch for default endpoint */
18570Sstevel@tonic-gate 	if (epp->ep_descr.bEndpointAddress == 0) {
18580Sstevel@tonic-gate 
18590Sstevel@tonic-gate 		return (rval);
18600Sstevel@tonic-gate 	}
18610Sstevel@tonic-gate 
18620Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
18630Sstevel@tonic-gate 	if ((ugenp->ug_dev_data->dev_n_cfg > 1) &&
18640Sstevel@tonic-gate 	    usb_get_cfg(ugenp->ug_dip, &cfgval,
18650Sstevel@tonic-gate 	    USB_FLAGS_SLEEP) == USB_SUCCESS) {
18660Sstevel@tonic-gate 
18670Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
18680Sstevel@tonic-gate 
18690Sstevel@tonic-gate 		cur_cfgidx = ugen_cfgval2idx(ugenp, cfgval);
18700Sstevel@tonic-gate 
18710Sstevel@tonic-gate 		if (new_cfgidx != cur_cfgidx) {
18720Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
18730Sstevel@tonic-gate 
18740Sstevel@tonic-gate 			/*
18750Sstevel@tonic-gate 			 * we can't change config if any node
18760Sstevel@tonic-gate 			 * is open
18770Sstevel@tonic-gate 			 */
18780Sstevel@tonic-gate 			if (ugen_epxs_check_open_nodes(ugenp) ==
18790Sstevel@tonic-gate 			    USB_SUCCESS) {
18800Sstevel@tonic-gate 				mutex_enter(&epp->ep_mutex);
18810Sstevel@tonic-gate 
18820Sstevel@tonic-gate 				return (USB_BUSY);
18830Sstevel@tonic-gate 			}
18840Sstevel@tonic-gate 
18850Sstevel@tonic-gate 			/*
18860Sstevel@tonic-gate 			 * we are going to do this synchronously to
18870Sstevel@tonic-gate 			 * keep it simple.
18880Sstevel@tonic-gate 			 * This should never hang forever.
18890Sstevel@tonic-gate 			 */
18900Sstevel@tonic-gate 			if ((rval = usb_set_cfg(ugenp->ug_dip,
18910Sstevel@tonic-gate 			    new_cfgidx, USB_FLAGS_SLEEP, NULL,
18920Sstevel@tonic-gate 			    NULL)) != USB_SUCCESS) {
18930Sstevel@tonic-gate 				USB_DPRINTF_L2(UGEN_PRINT_XFER,
18940Sstevel@tonic-gate 				    ugenp->ug_log_hdl,
18950Sstevel@tonic-gate 				    "implicit set cfg (%" PRId64
18960Sstevel@tonic-gate 				    ") failed (%d)",
18970Sstevel@tonic-gate 				    UGEN_MINOR_CFGIDX(ugenp, dev), rval);
18980Sstevel@tonic-gate 				mutex_enter(&epp->ep_mutex);
18990Sstevel@tonic-gate 
19000Sstevel@tonic-gate 				return (rval);
19010Sstevel@tonic-gate 			}
19020Sstevel@tonic-gate 			mutex_enter(&epp->ep_mutex);
19037492SZhigang.Lu@Sun.COM 			epp->ep_if = (uchar_t)new_if;
19040Sstevel@tonic-gate 			switched++;
19050Sstevel@tonic-gate 		}
19067492SZhigang.Lu@Sun.COM 		epp->ep_cfgidx = (uchar_t)new_cfgidx;
19070Sstevel@tonic-gate 
19080Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
19090Sstevel@tonic-gate 	}
19100Sstevel@tonic-gate 
19110Sstevel@tonic-gate 	/*
19120Sstevel@tonic-gate 	 * implicitly switch to new alternate if
19130Sstevel@tonic-gate 	 * - we have not switched configuration (if we
19140Sstevel@tonic-gate 	 *   we switched config, the alternate must be 0)
19150Sstevel@tonic-gate 	 * - n_alts is > 1
19160Sstevel@tonic-gate 	 * - if the device supports get_alternate iface
19170Sstevel@tonic-gate 	 */
19180Sstevel@tonic-gate 	if ((switched && (new_alt > 0)) ||
19190Sstevel@tonic-gate 	    ((ugenp->ug_dev_data->dev_cfg[new_cfgidx].
19200Sstevel@tonic-gate 	    cfg_if[new_if].if_n_alt > 1) &&
19210Sstevel@tonic-gate 	    (usb_get_alt_if(ugenp->ug_dip, new_if, &alt,
19220Sstevel@tonic-gate 	    USB_FLAGS_SLEEP) == USB_SUCCESS))) {
19230Sstevel@tonic-gate 		if (switched || (alt != new_alt)) {
19240Sstevel@tonic-gate 			if (ugen_epxs_check_alt_switch(ugenp, cur_if,
19250Sstevel@tonic-gate 			    new_cfgidx) != USB_SUCCESS) {
19260Sstevel@tonic-gate 				mutex_enter(&epp->ep_mutex);
19270Sstevel@tonic-gate 
19280Sstevel@tonic-gate 				return (USB_BUSY);
19290Sstevel@tonic-gate 			}
19300Sstevel@tonic-gate 			if ((rval = usb_set_alt_if(ugenp->ug_dip, new_if,
19310Sstevel@tonic-gate 			    new_alt, USB_FLAGS_SLEEP, NULL, NULL)) !=
19320Sstevel@tonic-gate 			    USB_SUCCESS) {
19330Sstevel@tonic-gate 				USB_DPRINTF_L2(UGEN_PRINT_XFER,
19340Sstevel@tonic-gate 				    ugenp->ug_log_hdl,
19350Sstevel@tonic-gate 				    "implicit set new alternate "
19360Sstevel@tonic-gate 				    "(%d) failed (%d)", new_alt, rval);
19370Sstevel@tonic-gate 				mutex_enter(&epp->ep_mutex);
19380Sstevel@tonic-gate 
19390Sstevel@tonic-gate 				return (rval);
19400Sstevel@tonic-gate 			}
19410Sstevel@tonic-gate 		}
19420Sstevel@tonic-gate 	}
19430Sstevel@tonic-gate 
19440Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
19457492SZhigang.Lu@Sun.COM 	epp->ep_alt = (uchar_t)new_alt;
19460Sstevel@tonic-gate 	ugen_update_ep_descr(ugenp, epp);
19470Sstevel@tonic-gate 
19480Sstevel@tonic-gate 	return (rval);
19490Sstevel@tonic-gate }
19500Sstevel@tonic-gate 
19510Sstevel@tonic-gate 
19520Sstevel@tonic-gate /*
19530Sstevel@tonic-gate  * update endpoint descriptor in ugen_ep structure after
19540Sstevel@tonic-gate  * switching configuration or alternate
19550Sstevel@tonic-gate  */
19560Sstevel@tonic-gate static void
ugen_update_ep_descr(ugen_state_t * ugenp,ugen_ep_t * epp)19570Sstevel@tonic-gate ugen_update_ep_descr(ugen_state_t *ugenp, ugen_ep_t *epp)
19580Sstevel@tonic-gate {
19590Sstevel@tonic-gate 	usb_cfg_data_t	*dev_cfg = ugenp->ug_dev_data->dev_cfg;
19600Sstevel@tonic-gate 	usb_if_data_t	*if_data;
19610Sstevel@tonic-gate 	usb_alt_if_data_t *alt_if_data;
19620Sstevel@tonic-gate 	usb_ep_data_t	*ep_data;
19630Sstevel@tonic-gate 	int		ep;
19640Sstevel@tonic-gate 
19650Sstevel@tonic-gate 	dev_cfg = &ugenp->ug_dev_data->dev_cfg[epp->ep_cfgidx];
19660Sstevel@tonic-gate 	if_data = &dev_cfg->cfg_if[epp->ep_if];
19670Sstevel@tonic-gate 	alt_if_data = &if_data->if_alt[epp->ep_alt];
19680Sstevel@tonic-gate 	for (ep = 0; ep < alt_if_data->altif_n_ep; ep++) {
19690Sstevel@tonic-gate 		ep_data = &alt_if_data->altif_ep[ep];
19700Sstevel@tonic-gate 		if (usb_get_ep_index(ep_data->ep_descr.
19710Sstevel@tonic-gate 		    bEndpointAddress) ==
19720Sstevel@tonic-gate 		    usb_get_ep_index(epp->ep_descr.
19730Sstevel@tonic-gate 		    bEndpointAddress)) {
19740Sstevel@tonic-gate 			epp->ep_descr = ep_data->ep_descr;
19750Sstevel@tonic-gate 
19760Sstevel@tonic-gate 			break;
19770Sstevel@tonic-gate 		}
19780Sstevel@tonic-gate 	}
19790Sstevel@tonic-gate }
19800Sstevel@tonic-gate 
19810Sstevel@tonic-gate 
19820Sstevel@tonic-gate /*
19830Sstevel@tonic-gate  * Xfer endpoint management
19840Sstevel@tonic-gate  *
19850Sstevel@tonic-gate  * open an endpoint for xfers
19860Sstevel@tonic-gate  *
19870Sstevel@tonic-gate  * Return values: errno
19880Sstevel@tonic-gate  */
19890Sstevel@tonic-gate static int
ugen_epx_open(ugen_state_t * ugenp,dev_t dev,int flag)19900Sstevel@tonic-gate ugen_epx_open(ugen_state_t *ugenp, dev_t dev, int flag)
19910Sstevel@tonic-gate {
19920Sstevel@tonic-gate 	ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
19930Sstevel@tonic-gate 	int	rval;
19940Sstevel@tonic-gate 
19950Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
19960Sstevel@tonic-gate 
19970Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
19980Sstevel@tonic-gate 	    "ugen_epx_open: minor=0x%x flag=0x%x ep_state=0x%x",
19990Sstevel@tonic-gate 	    getminor(dev), flag, epp->ep_state);
20000Sstevel@tonic-gate 
20010Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_ACTIVE);
20020Sstevel@tonic-gate 
20030Sstevel@tonic-gate 	/* implicit switch to new cfg & alt */
20040Sstevel@tonic-gate 	if ((epp->ep_state & UGEN_EP_STATE_XFER_OPEN) != 0) {
20050Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
20060Sstevel@tonic-gate 
20070Sstevel@tonic-gate 		return (EBUSY);
20080Sstevel@tonic-gate 	}
20090Sstevel@tonic-gate 	if ((rval = ugen_epxs_switch_cfg_alt(ugenp, epp, dev)) ==
20100Sstevel@tonic-gate 	    USB_SUCCESS) {
20110Sstevel@tonic-gate 		rval = ugen_epx_open_pipe(ugenp, epp, flag);
20120Sstevel@tonic-gate 	}
20130Sstevel@tonic-gate 
20140Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
20150Sstevel@tonic-gate 	    "ugen_epx_open: state=0x%x", epp->ep_state);
20160Sstevel@tonic-gate 
20170Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_ACTIVE);
20180Sstevel@tonic-gate 	epp->ep_done = epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
20190Sstevel@tonic-gate 
20200Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
20210Sstevel@tonic-gate 
20220Sstevel@tonic-gate 	return (usb_rval2errno(rval));
20230Sstevel@tonic-gate }
20240Sstevel@tonic-gate 
20250Sstevel@tonic-gate 
20260Sstevel@tonic-gate /*
20270Sstevel@tonic-gate  * close an endpoint for xfers
20280Sstevel@tonic-gate  */
20290Sstevel@tonic-gate static void
ugen_epx_close(ugen_state_t * ugenp,dev_t dev,int flag)20300Sstevel@tonic-gate ugen_epx_close(ugen_state_t *ugenp, dev_t dev, int flag)
20310Sstevel@tonic-gate {
20320Sstevel@tonic-gate 	ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
20330Sstevel@tonic-gate 
20340Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
20350Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
20360Sstevel@tonic-gate 	    "ugen_epx_close: dev=0x%lx flag=0x%x state=0x%x", dev, flag,
20370Sstevel@tonic-gate 	    epp->ep_state);
20380Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
20390Sstevel@tonic-gate 
20400Sstevel@tonic-gate 	ugen_epx_close_pipe(ugenp, epp);
20410Sstevel@tonic-gate 
20420Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
20430Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
20440Sstevel@tonic-gate 	    "ugen_epx_close: state=0x%x", epp->ep_state);
20450Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_ACTIVE);
20460Sstevel@tonic-gate 	ASSERT(epp->ep_bp == NULL);
20470Sstevel@tonic-gate 	ASSERT(epp->ep_done == 0);
20480Sstevel@tonic-gate 	ASSERT(epp->ep_data == NULL);
20490Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
20500Sstevel@tonic-gate }
20510Sstevel@tonic-gate 
20520Sstevel@tonic-gate 
20530Sstevel@tonic-gate /*
20540Sstevel@tonic-gate  * open pipe for this endpoint
20550Sstevel@tonic-gate  * If the pipe is an interrupt IN pipe, start polling immediately
20560Sstevel@tonic-gate  */
20570Sstevel@tonic-gate static int
ugen_epx_open_pipe(ugen_state_t * ugenp,ugen_ep_t * epp,int flag)20580Sstevel@tonic-gate ugen_epx_open_pipe(ugen_state_t *ugenp, ugen_ep_t *epp, int flag)
20590Sstevel@tonic-gate {
20600Sstevel@tonic-gate 	int rval = USB_SUCCESS;
20610Sstevel@tonic-gate 
20620Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
20630Sstevel@tonic-gate 	    "ugen_epx_open_pipe: epp=0x%p flag=%d state=0x%x",
20646898Sfb209375 	    (void *)epp, flag, epp->ep_state);
20650Sstevel@tonic-gate 
20660Sstevel@tonic-gate 	epp->ep_state |= UGEN_EP_STATE_XFER_OPEN;
20670Sstevel@tonic-gate 	epp->ep_xfer_oflag = flag;
20680Sstevel@tonic-gate 
20690Sstevel@tonic-gate 	/* if default pipe, just copy the handle */
20700Sstevel@tonic-gate 	if ((epp->ep_descr.bEndpointAddress & USB_EP_NUM_MASK) == 0) {
20710Sstevel@tonic-gate 		epp->ep_ph = ugenp->ug_dev_data->dev_default_ph;
20720Sstevel@tonic-gate 	} else {
20730Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
20740Sstevel@tonic-gate 
20750Sstevel@tonic-gate 		/* open pipe */
20760Sstevel@tonic-gate 		rval = usb_pipe_open(ugenp->ug_dip,
20770Sstevel@tonic-gate 		    &epp->ep_descr, &epp->ep_pipe_policy,
20780Sstevel@tonic-gate 		    USB_FLAGS_SLEEP, &epp->ep_ph);
20790Sstevel@tonic-gate 
20800Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
20810Sstevel@tonic-gate 
20820Sstevel@tonic-gate 		if (rval == USB_SUCCESS) {
20830Sstevel@tonic-gate 			(void) usb_pipe_set_private(epp->ep_ph,
20845653Slc152243 			    (usb_opaque_t)epp);
20850Sstevel@tonic-gate 
20860Sstevel@tonic-gate 			/*
20870Sstevel@tonic-gate 			 * if interrupt IN pipe, and one xfer mode
20880Sstevel@tonic-gate 			 * has not been set, start polling immediately
20890Sstevel@tonic-gate 			 */
20900Sstevel@tonic-gate 			if ((UGEN_XFER_TYPE(epp) == USB_EP_ATTR_INTR) &&
20910Sstevel@tonic-gate 			    (!(epp->ep_one_xfer)) &&
20920Sstevel@tonic-gate 			    (UGEN_XFER_DIR(epp) == USB_EP_DIR_IN)) {
20930Sstevel@tonic-gate 				if ((rval = ugen_epx_intr_IN_start_polling(
20940Sstevel@tonic-gate 				    ugenp, epp)) != USB_SUCCESS) {
20950Sstevel@tonic-gate 
20960Sstevel@tonic-gate 					mutex_exit(&epp->ep_mutex);
20970Sstevel@tonic-gate 					usb_pipe_close(ugenp->ug_dip,
20980Sstevel@tonic-gate 					    epp->ep_ph, USB_FLAGS_SLEEP,
20990Sstevel@tonic-gate 					    NULL, NULL);
21000Sstevel@tonic-gate 					mutex_enter(&epp->ep_mutex);
21010Sstevel@tonic-gate 
21020Sstevel@tonic-gate 					epp->ep_ph = NULL;
21030Sstevel@tonic-gate 				} else {
21040Sstevel@tonic-gate 					epp->ep_state |=
21050Sstevel@tonic-gate 					    UGEN_EP_STATE_INTR_IN_POLLING_ON;
21060Sstevel@tonic-gate 
21070Sstevel@tonic-gate 					/* allow for about 1 sec of data */
21080Sstevel@tonic-gate 					epp->ep_buf_limit =
21090Sstevel@tonic-gate 					    (1000/epp->ep_descr.bInterval) *
21100Sstevel@tonic-gate 					    epp->ep_descr.wMaxPacketSize;
21110Sstevel@tonic-gate 				}
21120Sstevel@tonic-gate 			}
21135653Slc152243 
21145653Slc152243 			/* set ep_buf_limit for isoc IN pipe */
21155653Slc152243 			if ((UGEN_XFER_TYPE(epp) == USB_EP_ATTR_ISOCH) &&
21165653Slc152243 			    (UGEN_XFER_DIR(epp) == USB_EP_DIR_IN)) {
21175653Slc152243 				uint16_t max_size;
21185653Slc152243 				uint32_t framecnt;
21195653Slc152243 
21205653Slc152243 				max_size =
21215653Slc152243 				    UGEN_PKT_SIZE(epp->ep_descr.wMaxPacketSize);
21225653Slc152243 
21235653Slc152243 				/*
21245653Slc152243 				 * wMaxPacketSize bits 10..0 specifies maximum
21255653Slc152243 				 * packet size, which can hold 1024 bytes. If
21265653Slc152243 				 * bits 12..11 is non zero, max_size will be
21275653Slc152243 				 * greater than 1024 and the endpoint is a
21285653Slc152243 				 * high-bandwidth endpoint.
21295653Slc152243 				 */
21305653Slc152243 				if (max_size <= 1024) {
21315653Slc152243 				/*
21325653Slc152243 				 * allowing about 1s data of highspeed and 8s
21335653Slc152243 				 * data of full speed device
21345653Slc152243 				 */
21355653Slc152243 					framecnt = ugen_isoc_buf_limit;
21365653Slc152243 					epp->ep_buf_limit = framecnt *
21375653Slc152243 					    max_size * 8;
21385653Slc152243 				} else {
21395653Slc152243 				/*
21405653Slc152243 				 * allow for about 333 ms data for high-speed
21415653Slc152243 				 * high-bandwidth data
21425653Slc152243 				 */
21435653Slc152243 					framecnt = ugen_isoc_buf_limit/3;
21445653Slc152243 					epp->ep_buf_limit =
21455653Slc152243 					    framecnt * max_size * 8;
21465653Slc152243 				}
21475653Slc152243 
21485653Slc152243 				epp->ep_isoc_in_inited = 0;
21495653Slc152243 			}
21500Sstevel@tonic-gate 		}
21510Sstevel@tonic-gate 	}
21520Sstevel@tonic-gate 
21530Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
21540Sstevel@tonic-gate 		epp->ep_state &= ~(UGEN_EP_STATE_XFER_OPEN |
21550Sstevel@tonic-gate 		    UGEN_EP_STATE_INTR_IN_POLLING_ON);
21560Sstevel@tonic-gate 	}
21570Sstevel@tonic-gate 
21580Sstevel@tonic-gate 	return (rval);
21590Sstevel@tonic-gate }
21600Sstevel@tonic-gate 
21610Sstevel@tonic-gate 
21620Sstevel@tonic-gate /*
21630Sstevel@tonic-gate  * close an endpoint pipe
21640Sstevel@tonic-gate  */
21650Sstevel@tonic-gate static void
ugen_epx_close_pipe(ugen_state_t * ugenp,ugen_ep_t * epp)21660Sstevel@tonic-gate ugen_epx_close_pipe(ugen_state_t *ugenp, ugen_ep_t *epp)
21670Sstevel@tonic-gate {
21680Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
21696898Sfb209375 	    "ugen_epx_close_pipe: epp=0x%p", (void *)epp);
21700Sstevel@tonic-gate 
21710Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
21720Sstevel@tonic-gate 	if (epp->ep_state & UGEN_EP_STATE_XFER_OPEN) {
21735653Slc152243 
21745653Slc152243 		/*  free isoc pipe private data ep_isoc_info.isoc_pkt_descr. */
21755653Slc152243 		if (UGEN_XFER_TYPE(epp) == USB_EP_ATTR_ISOCH) {
21765653Slc152243 			int len;
21775653Slc152243 			int n_pkt;
21785653Slc152243 
21795653Slc152243 			if (UGEN_XFER_DIR(epp) == USB_EP_DIR_IN &&
21805653Slc152243 			    (epp->ep_state &
21815653Slc152243 			    UGEN_EP_STATE_ISOC_IN_POLLING_ON)) {
21825653Slc152243 				mutex_exit(&epp->ep_mutex);
21835653Slc152243 				usb_pipe_stop_isoc_polling(epp->ep_ph,
21845653Slc152243 				    USB_FLAGS_SLEEP);
21855653Slc152243 				mutex_enter(&epp->ep_mutex);
21865653Slc152243 			}
21875653Slc152243 
21885653Slc152243 			if (epp->ep_isoc_info.isoc_pkt_descr) {
21895653Slc152243 				n_pkt = epp->ep_isoc_info.
21905653Slc152243 				    isoc_pkts_count;
21915653Slc152243 				len = sizeof (ugen_isoc_pkt_descr_t) * n_pkt;
21925653Slc152243 
21935653Slc152243 				kmem_free(epp->ep_isoc_info.isoc_pkt_descr,
21945653Slc152243 				    len);
21955653Slc152243 
21965653Slc152243 				epp->ep_isoc_info.isoc_pkt_descr = NULL;
21975653Slc152243 			}
21985653Slc152243 			epp->ep_isoc_in_inited = 0;
21995653Slc152243 
22005653Slc152243 		}
22015653Slc152243 
22025653Slc152243 
22030Sstevel@tonic-gate 		epp->ep_state &= ~(UGEN_EP_STATE_XFER_OPEN |
22045653Slc152243 		    UGEN_EP_STATE_INTR_IN_POLLING_IS_STOPPED |
22055653Slc152243 		    UGEN_EP_STATE_INTR_IN_POLLING_ON |
22065653Slc152243 		    UGEN_EP_STATE_ISOC_IN_POLLING_IS_STOPPED |
22075653Slc152243 		    UGEN_EP_STATE_ISOC_IN_POLLING_ON);
22080Sstevel@tonic-gate 
22090Sstevel@tonic-gate 		if (epp->ep_ph == ugenp->ug_dev_data->dev_default_ph) {
22100Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
22110Sstevel@tonic-gate 
22120Sstevel@tonic-gate 			(void) usb_pipe_drain_reqs(ugenp->ug_dip,
22130Sstevel@tonic-gate 			    epp->ep_ph, 0, USB_FLAGS_SLEEP,
22140Sstevel@tonic-gate 			    NULL, NULL);
22150Sstevel@tonic-gate 			mutex_enter(&epp->ep_mutex);
22160Sstevel@tonic-gate 		} else {
22170Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
22180Sstevel@tonic-gate 			usb_pipe_close(ugenp->ug_dip,
22190Sstevel@tonic-gate 			    epp->ep_ph, USB_FLAGS_SLEEP, NULL, NULL);
22200Sstevel@tonic-gate 
22210Sstevel@tonic-gate 			mutex_enter(&epp->ep_mutex);
22220Sstevel@tonic-gate 			epp->ep_ph = NULL;
22230Sstevel@tonic-gate 		}
22240Sstevel@tonic-gate 
22250Sstevel@tonic-gate 		freemsg(epp->ep_data);
22260Sstevel@tonic-gate 		epp->ep_ph = NULL;
22270Sstevel@tonic-gate 		epp->ep_data = NULL;
22280Sstevel@tonic-gate 	}
22290Sstevel@tonic-gate 	ASSERT(epp->ep_ph == NULL);
22300Sstevel@tonic-gate 	ASSERT(epp->ep_data == NULL);
22310Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
22320Sstevel@tonic-gate }
22330Sstevel@tonic-gate 
22340Sstevel@tonic-gate 
22350Sstevel@tonic-gate /*
22360Sstevel@tonic-gate  * start endpoint xfer
22370Sstevel@tonic-gate  *
22380Sstevel@tonic-gate  * We first serialize at endpoint level for only one request at the time
22390Sstevel@tonic-gate  *
22400Sstevel@tonic-gate  * Return values: errno
22410Sstevel@tonic-gate  */
22420Sstevel@tonic-gate static int
ugen_epx_req(ugen_state_t * ugenp,struct buf * bp)22430Sstevel@tonic-gate ugen_epx_req(ugen_state_t *ugenp, struct buf *bp)
22440Sstevel@tonic-gate {
22450Sstevel@tonic-gate 	dev_t		dev = bp->b_edev;
22460Sstevel@tonic-gate 	ugen_ep_t	*epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
22470Sstevel@tonic-gate 	boolean_t	wait = B_FALSE;
22480Sstevel@tonic-gate 	int		rval = 0;
22490Sstevel@tonic-gate 
22500Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
22510Sstevel@tonic-gate 	    "ugen_epx_req: bp=0x%p dev=0x%lx", (void *)bp, dev);
22520Sstevel@tonic-gate 
22530Sstevel@tonic-gate 	/* single thread per endpoint, one request at the time */
22540Sstevel@tonic-gate 	if (usb_serialize_access(epp->ep_ser_cookie, USB_WAIT_SIG, 0) <=
22550Sstevel@tonic-gate 	    0) {
22560Sstevel@tonic-gate 
22570Sstevel@tonic-gate 		return (EINTR);
22580Sstevel@tonic-gate 	}
22590Sstevel@tonic-gate 
22600Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
22610Sstevel@tonic-gate 	switch (ugenp->ug_dev_state) {
22620Sstevel@tonic-gate 	case USB_DEV_ONLINE:
22630Sstevel@tonic-gate 
22640Sstevel@tonic-gate 		break;
22650Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
22660Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
22670Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
22680Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_DISCONNECTED;
22690Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
22700Sstevel@tonic-gate 		rval = ENODEV;
22710Sstevel@tonic-gate 
22720Sstevel@tonic-gate 		break;
22730Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RESUME:
22740Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
22750Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
22760Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_SUSPENDED;
22770Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
22780Sstevel@tonic-gate 		rval = EBADF;
22790Sstevel@tonic-gate 
22800Sstevel@tonic-gate 		break;
22810Sstevel@tonic-gate 	default:
22820Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
22830Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_HW_ERR;
22840Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
22850Sstevel@tonic-gate 		rval = EIO;
22860Sstevel@tonic-gate 
22870Sstevel@tonic-gate 		break;
22880Sstevel@tonic-gate 	}
22890Sstevel@tonic-gate 
22900Sstevel@tonic-gate #ifndef __lock_lint
22910Sstevel@tonic-gate 	USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
22920Sstevel@tonic-gate 	    "ugen_epx_req: lcmd_status=0x%x", epp->ep_lcmd_status);
22930Sstevel@tonic-gate #endif
22940Sstevel@tonic-gate 
22950Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
22960Sstevel@tonic-gate 
22970Sstevel@tonic-gate 	if (rval) {
22980Sstevel@tonic-gate 		usb_release_access(epp->ep_ser_cookie);
22990Sstevel@tonic-gate 
23000Sstevel@tonic-gate 		return (rval);
23010Sstevel@tonic-gate 	}
23020Sstevel@tonic-gate 
23030Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
23040Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_XS_OPEN);
23050Sstevel@tonic-gate 	epp->ep_done = 0;
23060Sstevel@tonic-gate 	epp->ep_bp = bp;
23070Sstevel@tonic-gate 
23080Sstevel@tonic-gate 	switch (epp->ep_descr.bmAttributes & USB_EP_ATTR_MASK) {
23090Sstevel@tonic-gate 	case USB_EP_ATTR_CONTROL:
23100Sstevel@tonic-gate 		rval = ugen_epx_ctrl_req(ugenp, epp, bp, &wait);
23110Sstevel@tonic-gate 
23120Sstevel@tonic-gate 		break;
23130Sstevel@tonic-gate 	case USB_EP_ATTR_BULK:
23140Sstevel@tonic-gate 		rval = ugen_epx_bulk_req(ugenp, epp, bp, &wait);
23150Sstevel@tonic-gate 
23160Sstevel@tonic-gate 		break;
23170Sstevel@tonic-gate 	case USB_EP_ATTR_INTR:
23180Sstevel@tonic-gate 		if (bp->b_flags & B_READ) {
23190Sstevel@tonic-gate 			rval = ugen_epx_intr_IN_req(ugenp, epp, bp, &wait);
23200Sstevel@tonic-gate 		} else {
23210Sstevel@tonic-gate 			rval = ugen_epx_intr_OUT_req(ugenp, epp, bp, &wait);
23220Sstevel@tonic-gate 		}
23230Sstevel@tonic-gate 
23240Sstevel@tonic-gate 		break;
23250Sstevel@tonic-gate 	case USB_EP_ATTR_ISOCH:
23265653Slc152243 		if (bp->b_flags & B_READ) {
23275653Slc152243 			rval = ugen_epx_isoc_IN_req(ugenp, epp, bp, &wait);
23285653Slc152243 		} else {
23295653Slc152243 			rval = ugen_epx_isoc_OUT_req(ugenp, epp, bp, &wait);
23305653Slc152243 		}
23315653Slc152243 
23325653Slc152243 		break;
23330Sstevel@tonic-gate 	default:
23340Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
23350Sstevel@tonic-gate 		rval = USB_INVALID_REQUEST;
23360Sstevel@tonic-gate 	}
23370Sstevel@tonic-gate 
23380Sstevel@tonic-gate 	/* if the xfer could not immediately be completed, block here */
23390Sstevel@tonic-gate 	if ((rval == USB_SUCCESS) && wait) {
23400Sstevel@tonic-gate 		while (!epp->ep_done) {
23410Sstevel@tonic-gate 			if ((cv_wait_sig(&epp->ep_wait_cv,
23420Sstevel@tonic-gate 			    &epp->ep_mutex) <= 0) && !epp->ep_done) {
23430Sstevel@tonic-gate 				USB_DPRINTF_L2(UGEN_PRINT_XFER,
23440Sstevel@tonic-gate 				    ugenp->ug_log_hdl,
23450Sstevel@tonic-gate 				    "ugen_epx_req: interrupted ep=0x%" PRIx64,
23460Sstevel@tonic-gate 				    UGEN_MINOR_EPIDX(ugenp, dev));
23470Sstevel@tonic-gate 
23480Sstevel@tonic-gate 				/*
23490Sstevel@tonic-gate 				 * blow away the request except for dflt pipe
23500Sstevel@tonic-gate 				 * (this is prevented in USBA)
23510Sstevel@tonic-gate 				 */
23520Sstevel@tonic-gate 				mutex_exit(&epp->ep_mutex);
23530Sstevel@tonic-gate 				usb_pipe_reset(ugenp->ug_dip, epp->ep_ph,
23540Sstevel@tonic-gate 				    USB_FLAGS_SLEEP, NULL, NULL);
23550Sstevel@tonic-gate 				(void) usb_pipe_drain_reqs(ugenp->ug_dip,
23560Sstevel@tonic-gate 				    epp->ep_ph, 0,
23570Sstevel@tonic-gate 				    USB_FLAGS_SLEEP, NULL, NULL);
23580Sstevel@tonic-gate 
23590Sstevel@tonic-gate 				mutex_enter(&epp->ep_mutex);
23600Sstevel@tonic-gate 
23610Sstevel@tonic-gate 				if (geterror(bp) == 0) {
23620Sstevel@tonic-gate 					bioerror(bp, EINTR);
23630Sstevel@tonic-gate 				}
23640Sstevel@tonic-gate 				epp->ep_lcmd_status =
23650Sstevel@tonic-gate 				    USB_LC_STAT_INTERRUPTED;
23660Sstevel@tonic-gate 
23670Sstevel@tonic-gate 				break;
23680Sstevel@tonic-gate 			}
23690Sstevel@tonic-gate 			USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
23700Sstevel@tonic-gate 			    "ugen_epx_req: wakeup");
23710Sstevel@tonic-gate 		}
23720Sstevel@tonic-gate 	}
23730Sstevel@tonic-gate 
23740Sstevel@tonic-gate 	/* always set lcmd_status if there was a failure */
23750Sstevel@tonic-gate 	if ((rval != USB_SUCCESS) &&
23760Sstevel@tonic-gate 	    (epp->ep_lcmd_status == USB_LC_STAT_NOERROR)) {
23770Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_UNSPECIFIED_ERR;
23780Sstevel@tonic-gate 	}
23790Sstevel@tonic-gate 
23800Sstevel@tonic-gate 	epp->ep_done = 0;
23810Sstevel@tonic-gate 	epp->ep_bp = NULL;
23820Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
23830Sstevel@tonic-gate 
23840Sstevel@tonic-gate 	usb_release_access(epp->ep_ser_cookie);
23850Sstevel@tonic-gate 	USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
23860Sstevel@tonic-gate 	    "ugen_epx_req: done");
23870Sstevel@tonic-gate 
23880Sstevel@tonic-gate 	return (usb_rval2errno(rval));
23890Sstevel@tonic-gate }
23900Sstevel@tonic-gate 
23910Sstevel@tonic-gate 
23920Sstevel@tonic-gate /*
23930Sstevel@tonic-gate  * handle control xfers
23940Sstevel@tonic-gate  */
23950Sstevel@tonic-gate static int
ugen_epx_ctrl_req(ugen_state_t * ugenp,ugen_ep_t * epp,struct buf * bp,boolean_t * wait)23960Sstevel@tonic-gate ugen_epx_ctrl_req(ugen_state_t *ugenp, ugen_ep_t *epp,
23970Sstevel@tonic-gate     struct buf *bp, boolean_t *wait)
23980Sstevel@tonic-gate {
23990Sstevel@tonic-gate 	usb_ctrl_req_t *reqp = NULL;
24000Sstevel@tonic-gate 	uchar_t	*setup = ((uchar_t *)(bp->b_un.b_addr));
24010Sstevel@tonic-gate 	int	rval;
24020Sstevel@tonic-gate 	ushort_t wLength;
24030Sstevel@tonic-gate 
24040Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
24050Sstevel@tonic-gate 	    "ugen_epx_ctrl_req: epp=0x%p state=0x%x bp=0x%p",
24066898Sfb209375 	    (void *)epp, epp->ep_state, (void *)bp);
24070Sstevel@tonic-gate 
24080Sstevel@tonic-gate 	/* is this a read following a write with setup data? */
24090Sstevel@tonic-gate 	if (bp->b_flags & B_READ) {
24100Sstevel@tonic-gate 		if (epp->ep_data) {
24117492SZhigang.Lu@Sun.COM 			int ep_len = MBLKL(epp->ep_data);
24120Sstevel@tonic-gate 			int len = min(bp->b_bcount, ep_len);
24130Sstevel@tonic-gate 
24140Sstevel@tonic-gate 			bcopy(epp->ep_data->b_rptr, bp->b_un.b_addr, len);
24150Sstevel@tonic-gate 			epp->ep_data->b_rptr += len;
24167492SZhigang.Lu@Sun.COM 			if (MBLKL(epp->ep_data) == 0) {
24170Sstevel@tonic-gate 				freemsg(epp->ep_data);
24180Sstevel@tonic-gate 				epp->ep_data = NULL;
24190Sstevel@tonic-gate 			}
24200Sstevel@tonic-gate 			bp->b_resid = bp->b_bcount - len;
24210Sstevel@tonic-gate 		} else {
24220Sstevel@tonic-gate 			bp->b_resid = bp->b_bcount;
24230Sstevel@tonic-gate 		}
24240Sstevel@tonic-gate 
24250Sstevel@tonic-gate 		return (USB_SUCCESS);
24260Sstevel@tonic-gate 	}
24270Sstevel@tonic-gate 
24280Sstevel@tonic-gate 	/* discard old data if any */
24290Sstevel@tonic-gate 	if (epp->ep_data) {
24300Sstevel@tonic-gate 		freemsg(epp->ep_data);
24310Sstevel@tonic-gate 		epp->ep_data = NULL;
24320Sstevel@tonic-gate 	}
24330Sstevel@tonic-gate 
24340Sstevel@tonic-gate 	/* allocate and initialize request */
24350Sstevel@tonic-gate 	wLength = (setup[7] << 8) | setup[6];
24360Sstevel@tonic-gate 	reqp = usb_alloc_ctrl_req(ugenp->ug_dip, wLength, USB_FLAGS_NOSLEEP);
24370Sstevel@tonic-gate 	if (reqp == NULL) {
24380Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_NO_RESOURCES;
24390Sstevel@tonic-gate 
24400Sstevel@tonic-gate 		return (USB_NO_RESOURCES);
24410Sstevel@tonic-gate 	}
24420Sstevel@tonic-gate 
24430Sstevel@tonic-gate 	/* assume an LE data stream */
24440Sstevel@tonic-gate 	reqp->ctrl_bmRequestType = setup[0];
24450Sstevel@tonic-gate 	reqp->ctrl_bRequest	= setup[1];
24460Sstevel@tonic-gate 	reqp->ctrl_wValue	= (setup[3] << 8) | setup[2];
24470Sstevel@tonic-gate 	reqp->ctrl_wIndex	= (setup[5] << 8) | setup[4];
24480Sstevel@tonic-gate 	reqp->ctrl_wLength	= wLength;
24490Sstevel@tonic-gate 	reqp->ctrl_timeout	= ugen_ctrl_timeout;
24500Sstevel@tonic-gate 	reqp->ctrl_attributes	= USB_ATTRS_AUTOCLEARING |
24515653Slc152243 	    USB_ATTRS_SHORT_XFER_OK;
24520Sstevel@tonic-gate 	reqp->ctrl_cb		= ugen_epx_ctrl_req_cb;
24530Sstevel@tonic-gate 	reqp->ctrl_exc_cb	= ugen_epx_ctrl_req_cb;
24540Sstevel@tonic-gate 	reqp->ctrl_client_private = (usb_opaque_t)ugenp;
24550Sstevel@tonic-gate 
24560Sstevel@tonic-gate 	/*
24570Sstevel@tonic-gate 	 * is this a legal request? No accesses to device are
24580Sstevel@tonic-gate 	 * allowed if we don't own the device
24590Sstevel@tonic-gate 	 */
24600Sstevel@tonic-gate 	if (((reqp->ctrl_bmRequestType & USB_DEV_REQ_RCPT_MASK) ==
24610Sstevel@tonic-gate 	    USB_DEV_REQ_RCPT_DEV) &&
24620Sstevel@tonic-gate 	    (((reqp->ctrl_bmRequestType & USB_DEV_REQ_DIR_MASK) ==
24630Sstevel@tonic-gate 	    USB_DEV_REQ_HOST_TO_DEV) &&
24640Sstevel@tonic-gate 	    (usb_owns_device(ugenp->ug_dip) == B_FALSE))) {
24650Sstevel@tonic-gate 		rval = USB_INVALID_PERM;
24660Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
24670Sstevel@tonic-gate 
24680Sstevel@tonic-gate 		goto fail;
24690Sstevel@tonic-gate 	}
24700Sstevel@tonic-gate 
24710Sstevel@tonic-gate 	/* filter out set_cfg and set_if standard requests */
24720Sstevel@tonic-gate 	if ((reqp->ctrl_bmRequestType & USB_DEV_REQ_TYPE_MASK) ==
24730Sstevel@tonic-gate 	    USB_DEV_REQ_TYPE_STANDARD) {
24740Sstevel@tonic-gate 		switch (reqp->ctrl_bRequest) {
24750Sstevel@tonic-gate 		case USB_REQ_SET_CFG:
24760Sstevel@tonic-gate 		case USB_REQ_SET_IF:
24770Sstevel@tonic-gate 			rval = USB_INVALID_REQUEST;
24780Sstevel@tonic-gate 			epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
24790Sstevel@tonic-gate 
24800Sstevel@tonic-gate 			goto fail;
24810Sstevel@tonic-gate 		default:
24820Sstevel@tonic-gate 
24830Sstevel@tonic-gate 			break;
24840Sstevel@tonic-gate 		}
24850Sstevel@tonic-gate 	}
24860Sstevel@tonic-gate 
24870Sstevel@tonic-gate 	/* is this from host to device? */
24880Sstevel@tonic-gate 	if (((reqp->ctrl_bmRequestType & USB_DEV_REQ_DIR_MASK) ==
24890Sstevel@tonic-gate 	    USB_DEV_REQ_HOST_TO_DEV) && reqp->ctrl_wLength) {
24900Sstevel@tonic-gate 		if (((bp->b_bcount - UGEN_SETUP_PKT_SIZE) - wLength) != 0) {
24910Sstevel@tonic-gate 			rval = USB_INVALID_REQUEST;
24920Sstevel@tonic-gate 			epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
24930Sstevel@tonic-gate 
24940Sstevel@tonic-gate 			goto fail;
24950Sstevel@tonic-gate 		}
24960Sstevel@tonic-gate 		bcopy(bp->b_un.b_addr + UGEN_SETUP_PKT_SIZE,
24970Sstevel@tonic-gate 		    reqp->ctrl_data->b_wptr, wLength);
24980Sstevel@tonic-gate 		reqp->ctrl_data->b_wptr += wLength;
24990Sstevel@tonic-gate 	} else	if ((reqp->ctrl_bmRequestType & USB_DEV_REQ_DIR_MASK) ==
25000Sstevel@tonic-gate 	    USB_DEV_REQ_DEV_TO_HOST) {
25010Sstevel@tonic-gate 		if (bp->b_bcount != UGEN_SETUP_PKT_SIZE) {
25020Sstevel@tonic-gate 			rval = USB_INVALID_REQUEST;
25030Sstevel@tonic-gate 			epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
25040Sstevel@tonic-gate 
25050Sstevel@tonic-gate 			goto fail;
25060Sstevel@tonic-gate 		}
25070Sstevel@tonic-gate 	}
25080Sstevel@tonic-gate 
25090Sstevel@tonic-gate 	/* submit the request */
25100Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
25110Sstevel@tonic-gate 	rval = usb_pipe_ctrl_xfer(epp->ep_ph, reqp, USB_FLAGS_NOSLEEP);
25120Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
25130Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
25140Sstevel@tonic-gate 		epp->ep_lcmd_status =
25150Sstevel@tonic-gate 		    ugen_cr2lcstat(reqp->ctrl_completion_reason);
25160Sstevel@tonic-gate 
25170Sstevel@tonic-gate 		goto fail;
25180Sstevel@tonic-gate 	}
25190Sstevel@tonic-gate done:
25200Sstevel@tonic-gate 	*wait = B_TRUE;
25210Sstevel@tonic-gate 
25220Sstevel@tonic-gate 	return (USB_SUCCESS);
25230Sstevel@tonic-gate fail:
25240Sstevel@tonic-gate 	*wait = B_FALSE;
25250Sstevel@tonic-gate 
25260Sstevel@tonic-gate 	usb_free_ctrl_req(reqp);
25270Sstevel@tonic-gate 
25280Sstevel@tonic-gate 	return (rval);
25290Sstevel@tonic-gate }
25300Sstevel@tonic-gate 
25310Sstevel@tonic-gate 
25320Sstevel@tonic-gate /*
25330Sstevel@tonic-gate  * callback for control requests, normal and exception completion
25340Sstevel@tonic-gate  */
25350Sstevel@tonic-gate static void
ugen_epx_ctrl_req_cb(usb_pipe_handle_t ph,usb_ctrl_req_t * reqp)25360Sstevel@tonic-gate ugen_epx_ctrl_req_cb(usb_pipe_handle_t ph, usb_ctrl_req_t *reqp)
25370Sstevel@tonic-gate {
25380Sstevel@tonic-gate 	ugen_state_t *ugenp = (ugen_state_t *)reqp->ctrl_client_private;
25390Sstevel@tonic-gate 	ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
25400Sstevel@tonic-gate 
25410Sstevel@tonic-gate 	if (epp == NULL) {
25420Sstevel@tonic-gate 		epp = &ugenp->ug_ep[0];
25430Sstevel@tonic-gate 	}
25440Sstevel@tonic-gate 
25450Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
25460Sstevel@tonic-gate 
25470Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
25480Sstevel@tonic-gate 	    "ugen_epx_ctrl_req_cb:\n\t"
25490Sstevel@tonic-gate 	    "epp=0x%p state=0x%x ph=0x%p reqp=0x%p cr=%d cb=0x%x",
25506898Sfb209375 	    (void *)epp, epp->ep_state, (void *)ph, (void *)reqp,
25516898Sfb209375 	    reqp->ctrl_completion_reason, reqp->ctrl_cb_flags);
25520Sstevel@tonic-gate 
25530Sstevel@tonic-gate 	ASSERT((reqp->ctrl_cb_flags & USB_CB_INTR_CONTEXT) == 0);
25540Sstevel@tonic-gate 
25550Sstevel@tonic-gate 	/* save any data for the next read */
25560Sstevel@tonic-gate 	switch (reqp->ctrl_completion_reason) {
25570Sstevel@tonic-gate 	case USB_CR_OK:
25580Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
25590Sstevel@tonic-gate 
25600Sstevel@tonic-gate 		break;
25610Sstevel@tonic-gate 	case USB_CR_PIPE_RESET:
25620Sstevel@tonic-gate 
25630Sstevel@tonic-gate 		break;
25640Sstevel@tonic-gate 	default:
25650Sstevel@tonic-gate 		epp->ep_lcmd_status =
25660Sstevel@tonic-gate 		    ugen_cr2lcstat(reqp->ctrl_completion_reason);
25670Sstevel@tonic-gate 		if (epp->ep_bp) {
25680Sstevel@tonic-gate 			bioerror(epp->ep_bp, EIO);
25690Sstevel@tonic-gate 		}
25700Sstevel@tonic-gate 
25710Sstevel@tonic-gate 		break;
25720Sstevel@tonic-gate 	}
25730Sstevel@tonic-gate 
25740Sstevel@tonic-gate 	if (reqp->ctrl_data) {
25750Sstevel@tonic-gate 		ASSERT(epp->ep_data == NULL);
25760Sstevel@tonic-gate 		epp->ep_data = reqp->ctrl_data;
25770Sstevel@tonic-gate 		reqp->ctrl_data = NULL;
25780Sstevel@tonic-gate 	}
25790Sstevel@tonic-gate 	epp->ep_done++;
25800Sstevel@tonic-gate 	cv_signal(&epp->ep_wait_cv);
25810Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
25820Sstevel@tonic-gate 
25830Sstevel@tonic-gate 	usb_free_ctrl_req(reqp);
25840Sstevel@tonic-gate 
25850Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
25860Sstevel@tonic-gate 	    "ugen_epx_ctrl_req_cb: done");
25870Sstevel@tonic-gate }
25880Sstevel@tonic-gate 
25890Sstevel@tonic-gate 
25900Sstevel@tonic-gate /*
25910Sstevel@tonic-gate  * handle bulk xfers
25920Sstevel@tonic-gate  */
25930Sstevel@tonic-gate static int
ugen_epx_bulk_req(ugen_state_t * ugenp,ugen_ep_t * epp,struct buf * bp,boolean_t * wait)25940Sstevel@tonic-gate ugen_epx_bulk_req(ugen_state_t *ugenp, ugen_ep_t *epp,
25950Sstevel@tonic-gate     struct buf *bp, boolean_t *wait)
25960Sstevel@tonic-gate {
25970Sstevel@tonic-gate 	int		rval;
25980Sstevel@tonic-gate 	usb_bulk_req_t	*reqp = usb_alloc_bulk_req(ugenp->ug_dip,
25995653Slc152243 	    bp->b_bcount, USB_FLAGS_NOSLEEP);
26000Sstevel@tonic-gate 
26010Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
26020Sstevel@tonic-gate 	    "ugen_epx_bulk_req: epp=0x%p state=0x%x bp=0x%p",
26036898Sfb209375 	    (void *)epp, epp->ep_state, (void *)bp);
26040Sstevel@tonic-gate 
26050Sstevel@tonic-gate 	if (reqp == NULL) {
26060Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_NO_RESOURCES;
26070Sstevel@tonic-gate 
26080Sstevel@tonic-gate 		return (USB_NO_RESOURCES);
26090Sstevel@tonic-gate 	}
26100Sstevel@tonic-gate 
26110Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_XS_OPEN);
26120Sstevel@tonic-gate 
26130Sstevel@tonic-gate 	/*
26140Sstevel@tonic-gate 	 * the transfer count is limited in minphys with what the HCD can
26150Sstevel@tonic-gate 	 * do
26160Sstevel@tonic-gate 	 */
26170Sstevel@tonic-gate 	reqp->bulk_len		= bp->b_bcount;
26180Sstevel@tonic-gate 	reqp->bulk_timeout	= ugen_bulk_timeout;
26190Sstevel@tonic-gate 	reqp->bulk_client_private = (usb_opaque_t)ugenp;
26200Sstevel@tonic-gate 	reqp->bulk_attributes	= USB_ATTRS_AUTOCLEARING;
26210Sstevel@tonic-gate 	reqp->bulk_cb		= ugen_epx_bulk_req_cb;
26220Sstevel@tonic-gate 	reqp->bulk_exc_cb	= ugen_epx_bulk_req_cb;
26230Sstevel@tonic-gate 
26240Sstevel@tonic-gate 	/* copy data into bp for OUT pipes */
26250Sstevel@tonic-gate 	if ((UGEN_XFER_DIR(epp) & USB_EP_DIR_IN) == 0) {
26260Sstevel@tonic-gate 		bcopy(epp->ep_bp->b_un.b_addr, reqp->bulk_data->b_rptr,
26275653Slc152243 		    bp->b_bcount);
26280Sstevel@tonic-gate 		reqp->bulk_data->b_wptr += bp->b_bcount;
26290Sstevel@tonic-gate 	} else {
26300Sstevel@tonic-gate 		reqp->bulk_attributes |= USB_ATTRS_SHORT_XFER_OK;
26310Sstevel@tonic-gate 	}
26320Sstevel@tonic-gate 
26330Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
26340Sstevel@tonic-gate 	if ((rval = usb_pipe_bulk_xfer(epp->ep_ph, reqp,
26350Sstevel@tonic-gate 	    USB_FLAGS_NOSLEEP)) != USB_SUCCESS) {
26360Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
26370Sstevel@tonic-gate 		epp->ep_lcmd_status =
26380Sstevel@tonic-gate 		    ugen_cr2lcstat(reqp->bulk_completion_reason);
26390Sstevel@tonic-gate 		usb_free_bulk_req(reqp);
26400Sstevel@tonic-gate 		bioerror(bp, EIO);
26410Sstevel@tonic-gate 	} else {
26420Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
26430Sstevel@tonic-gate 	}
26440Sstevel@tonic-gate 	*wait = (rval == USB_SUCCESS) ? B_TRUE : B_FALSE;
26450Sstevel@tonic-gate 
26460Sstevel@tonic-gate 	return (rval);
26470Sstevel@tonic-gate }
26480Sstevel@tonic-gate 
26490Sstevel@tonic-gate 
26500Sstevel@tonic-gate /*
26510Sstevel@tonic-gate  * normal and exception bulk request callback
26520Sstevel@tonic-gate  */
26530Sstevel@tonic-gate static void
ugen_epx_bulk_req_cb(usb_pipe_handle_t ph,usb_bulk_req_t * reqp)26540Sstevel@tonic-gate ugen_epx_bulk_req_cb(usb_pipe_handle_t ph, usb_bulk_req_t *reqp)
26550Sstevel@tonic-gate {
26560Sstevel@tonic-gate 	ugen_state_t *ugenp = (ugen_state_t *)reqp->bulk_client_private;
26570Sstevel@tonic-gate 	ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
26580Sstevel@tonic-gate 
26590Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
26600Sstevel@tonic-gate 	    "ugen_epx_bulk_req_cb: ph=0x%p reqp=0x%p cr=%d cb=0x%x",
26616898Sfb209375 	    (void *)ph, (void *)reqp, reqp->bulk_completion_reason,
26626898Sfb209375 	    reqp->bulk_cb_flags);
26630Sstevel@tonic-gate 
26640Sstevel@tonic-gate 	ASSERT((reqp->bulk_cb_flags & USB_CB_INTR_CONTEXT) == 0);
26650Sstevel@tonic-gate 
26660Sstevel@tonic-gate 	/* epp might be NULL if we are closing the pipe */
26670Sstevel@tonic-gate 	if (epp) {
26680Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
26690Sstevel@tonic-gate 		if (epp->ep_bp && reqp->bulk_data) {
26707492SZhigang.Lu@Sun.COM 			int len = min(MBLKL(reqp->bulk_data),
26715653Slc152243 			    epp->ep_bp->b_bcount);
26720Sstevel@tonic-gate 			if (UGEN_XFER_DIR(epp) & USB_EP_DIR_IN) {
26730Sstevel@tonic-gate 				if (len) {
26740Sstevel@tonic-gate 					bcopy(reqp->bulk_data->b_rptr,
26750Sstevel@tonic-gate 					    epp->ep_bp->b_un.b_addr, len);
26760Sstevel@tonic-gate 					epp->ep_bp->b_resid =
26770Sstevel@tonic-gate 					    epp->ep_bp->b_bcount - len;
26780Sstevel@tonic-gate 				}
26790Sstevel@tonic-gate 			} else {
26800Sstevel@tonic-gate 				epp->ep_bp->b_resid =
26815653Slc152243 				    epp->ep_bp->b_bcount - len;
26820Sstevel@tonic-gate 			}
26830Sstevel@tonic-gate 		}
26840Sstevel@tonic-gate 		switch (reqp->bulk_completion_reason) {
26850Sstevel@tonic-gate 		case USB_CR_OK:
26860Sstevel@tonic-gate 			epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
26870Sstevel@tonic-gate 
26880Sstevel@tonic-gate 			break;
26890Sstevel@tonic-gate 		case USB_CR_PIPE_RESET:
26900Sstevel@tonic-gate 
26910Sstevel@tonic-gate 			break;
26920Sstevel@tonic-gate 		default:
26930Sstevel@tonic-gate 			epp->ep_lcmd_status =
26940Sstevel@tonic-gate 			    ugen_cr2lcstat(reqp->bulk_completion_reason);
26950Sstevel@tonic-gate 			if (epp->ep_bp) {
26960Sstevel@tonic-gate 				bioerror(epp->ep_bp, EIO);
26970Sstevel@tonic-gate 			}
26980Sstevel@tonic-gate 		}
26990Sstevel@tonic-gate 		epp->ep_done++;
27000Sstevel@tonic-gate 		cv_signal(&epp->ep_wait_cv);
27010Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
27020Sstevel@tonic-gate 	}
27030Sstevel@tonic-gate 
27040Sstevel@tonic-gate 	usb_free_bulk_req(reqp);
27050Sstevel@tonic-gate }
27060Sstevel@tonic-gate 
27070Sstevel@tonic-gate 
27080Sstevel@tonic-gate /*
27090Sstevel@tonic-gate  * handle intr IN xfers
27100Sstevel@tonic-gate  */
27110Sstevel@tonic-gate static int
ugen_epx_intr_IN_req(ugen_state_t * ugenp,ugen_ep_t * epp,struct buf * bp,boolean_t * wait)27120Sstevel@tonic-gate ugen_epx_intr_IN_req(ugen_state_t *ugenp, ugen_ep_t *epp,
27130Sstevel@tonic-gate     struct buf *bp, boolean_t *wait)
27140Sstevel@tonic-gate {
27150Sstevel@tonic-gate 	int	len = 0;
27160Sstevel@tonic-gate 	int	rval = USB_SUCCESS;
27170Sstevel@tonic-gate 
27180Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
27190Sstevel@tonic-gate 	    "ugen_epx_intr_IN_req: epp=0x%p state=0x%x bp=0x%p",
27206898Sfb209375 	    (void *)epp, epp->ep_state, (void *)bp);
27210Sstevel@tonic-gate 
27220Sstevel@tonic-gate 	*wait = B_FALSE;
27230Sstevel@tonic-gate 
27240Sstevel@tonic-gate 	/* can we satisfy this read? */
27250Sstevel@tonic-gate 	if (epp->ep_data) {
27267492SZhigang.Lu@Sun.COM 		len = min(MBLKL(epp->ep_data),
27275653Slc152243 		    bp->b_bcount);
27280Sstevel@tonic-gate 	}
27290Sstevel@tonic-gate 
27300Sstevel@tonic-gate 	/*
27310Sstevel@tonic-gate 	 * if polling not active, restart, and return failure
27320Sstevel@tonic-gate 	 * immediately unless one xfer mode has been requested
27330Sstevel@tonic-gate 	 * if there is some data, return a short read
27340Sstevel@tonic-gate 	 */
27350Sstevel@tonic-gate 	if ((epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_ON) == 0) {
27360Sstevel@tonic-gate 		if (len == 0) {
27370Sstevel@tonic-gate 			if (!epp->ep_one_xfer) {
27380Sstevel@tonic-gate 				rval = USB_FAILURE;
27390Sstevel@tonic-gate 				if (epp->ep_lcmd_status ==
27400Sstevel@tonic-gate 				    USB_LC_STAT_NOERROR) {
27410Sstevel@tonic-gate 					epp->ep_lcmd_status =
27425653Slc152243 					    USB_LC_STAT_INTR_BUF_FULL;
27430Sstevel@tonic-gate 				}
27440Sstevel@tonic-gate 			}
27450Sstevel@tonic-gate 			if (ugen_epx_intr_IN_start_polling(ugenp,
27460Sstevel@tonic-gate 			    epp) != USB_SUCCESS) {
27470Sstevel@tonic-gate 				epp->ep_lcmd_status =
27480Sstevel@tonic-gate 				    USB_LC_STAT_INTR_POLLING_FAILED;
27490Sstevel@tonic-gate 			}
27500Sstevel@tonic-gate 			if (epp->ep_one_xfer) {
27510Sstevel@tonic-gate 				*wait = B_TRUE;
27520Sstevel@tonic-gate 			}
27530Sstevel@tonic-gate 			goto done;
27540Sstevel@tonic-gate 		} else if (epp->ep_data && (len < bp->b_bcount)) {
27550Sstevel@tonic-gate 			bcopy(epp->ep_data->b_rptr, bp->b_un.b_addr, len);
27560Sstevel@tonic-gate 			bp->b_resid = bp->b_bcount - len;
27570Sstevel@tonic-gate 			epp->ep_data->b_rptr += len;
27580Sstevel@tonic-gate 
27590Sstevel@tonic-gate 			goto done;
27600Sstevel@tonic-gate 		}
27610Sstevel@tonic-gate 	}
27620Sstevel@tonic-gate 
27630Sstevel@tonic-gate 	/*
27640Sstevel@tonic-gate 	 * if there is data or FNDELAY, return available data
27650Sstevel@tonic-gate 	 */
27660Sstevel@tonic-gate 	if ((len >= bp->b_bcount) ||
27670Sstevel@tonic-gate 	    (epp->ep_xfer_oflag & (FNDELAY | FNONBLOCK))) {
27680Sstevel@tonic-gate 		if (epp->ep_data) {
27690Sstevel@tonic-gate 			bcopy(epp->ep_data->b_rptr, bp->b_un.b_addr, len);
27700Sstevel@tonic-gate 			epp->ep_data->b_rptr += len;
27710Sstevel@tonic-gate 			bp->b_resid = bp->b_bcount - len;
27720Sstevel@tonic-gate 		} else {
27730Sstevel@tonic-gate 			bp->b_resid = bp->b_bcount;
27740Sstevel@tonic-gate 		}
27750Sstevel@tonic-gate 	} else {
27760Sstevel@tonic-gate 		/* otherwise just wait for data */
27770Sstevel@tonic-gate 		*wait = B_TRUE;
27780Sstevel@tonic-gate 	}
27790Sstevel@tonic-gate 
27800Sstevel@tonic-gate done:
27810Sstevel@tonic-gate 	if (epp->ep_data && (epp->ep_data->b_rptr == epp->ep_data->b_wptr)) {
27820Sstevel@tonic-gate 		freemsg(epp->ep_data);
27830Sstevel@tonic-gate 		epp->ep_data = NULL;
27840Sstevel@tonic-gate 	}
27850Sstevel@tonic-gate 
27860Sstevel@tonic-gate 	if (*wait) {
27870Sstevel@tonic-gate 		ASSERT(epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_ON);
27880Sstevel@tonic-gate 	}
27890Sstevel@tonic-gate 
27900Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
27910Sstevel@tonic-gate 	    "ugen_epx_intr_IN_req end: rval=%d bcount=%lu len=%d data=0x%p",
27926898Sfb209375 	    rval, bp->b_bcount, len, (void *)epp->ep_data);
27930Sstevel@tonic-gate 
27940Sstevel@tonic-gate 	return (rval);
27950Sstevel@tonic-gate }
27960Sstevel@tonic-gate 
27970Sstevel@tonic-gate 
27980Sstevel@tonic-gate /*
27990Sstevel@tonic-gate  * Start polling on interrupt endpoint, synchronously
28000Sstevel@tonic-gate  */
28010Sstevel@tonic-gate static int
ugen_epx_intr_IN_start_polling(ugen_state_t * ugenp,ugen_ep_t * epp)28020Sstevel@tonic-gate ugen_epx_intr_IN_start_polling(ugen_state_t *ugenp, ugen_ep_t *epp)
28030Sstevel@tonic-gate {
28040Sstevel@tonic-gate 	int rval = USB_FAILURE;
28050Sstevel@tonic-gate 	usb_intr_req_t	*reqp;
28060Sstevel@tonic-gate 	usb_flags_t uflag;
28070Sstevel@tonic-gate 
28080Sstevel@tonic-gate 	/*
28090Sstevel@tonic-gate 	 * if polling is being stopped, we restart polling in the
28100Sstevel@tonic-gate 	 * interrrupt callback again
28110Sstevel@tonic-gate 	 */
28120Sstevel@tonic-gate 	if (epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_IS_STOPPED) {
28130Sstevel@tonic-gate 
28140Sstevel@tonic-gate 		return (rval);
28150Sstevel@tonic-gate 	}
28160Sstevel@tonic-gate 	if ((epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_ON) == 0) {
28170Sstevel@tonic-gate 		USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
28180Sstevel@tonic-gate 		    "ugen_epx_intr_IN_start_polling: epp=0x%p state=0x%x",
28196898Sfb209375 		    (void *)epp, epp->ep_state);
28200Sstevel@tonic-gate 
28210Sstevel@tonic-gate 		epp->ep_state |= UGEN_EP_STATE_INTR_IN_POLLING_ON;
28220Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
28230Sstevel@tonic-gate 
28240Sstevel@tonic-gate 		reqp = usb_alloc_intr_req(ugenp->ug_dip, 0,
28255653Slc152243 		    USB_FLAGS_SLEEP);
28260Sstevel@tonic-gate 		reqp->intr_client_private = (usb_opaque_t)ugenp;
28270Sstevel@tonic-gate 
28280Sstevel@tonic-gate 		reqp->intr_attributes	= USB_ATTRS_AUTOCLEARING |
28295653Slc152243 		    USB_ATTRS_SHORT_XFER_OK;
28300Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
28310Sstevel@tonic-gate 		if (epp->ep_one_xfer) {
28320Sstevel@tonic-gate 			reqp->intr_attributes |= USB_ATTRS_ONE_XFER;
28330Sstevel@tonic-gate 			uflag = USB_FLAGS_NOSLEEP;
28340Sstevel@tonic-gate 		} else {
28350Sstevel@tonic-gate 			uflag = USB_FLAGS_SLEEP;
28360Sstevel@tonic-gate 		}
28370Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
28380Sstevel@tonic-gate 
28390Sstevel@tonic-gate 		reqp->intr_len		= epp->ep_descr.wMaxPacketSize;
28400Sstevel@tonic-gate 		reqp->intr_cb		= ugen_epx_intr_IN_req_cb;
28410Sstevel@tonic-gate 		reqp->intr_exc_cb	= ugen_epx_intr_IN_req_cb;
28420Sstevel@tonic-gate 
28430Sstevel@tonic-gate 
28440Sstevel@tonic-gate 		if ((rval = usb_pipe_intr_xfer(epp->ep_ph, reqp,
28450Sstevel@tonic-gate 		    uflag)) != USB_SUCCESS) {
28460Sstevel@tonic-gate 			USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
28470Sstevel@tonic-gate 			    "ugen_epx_intr_IN_start_polling: failed %d", rval);
28480Sstevel@tonic-gate 			usb_free_intr_req(reqp);
28490Sstevel@tonic-gate 		}
28500Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
28510Sstevel@tonic-gate 		if (rval != USB_SUCCESS) {
28520Sstevel@tonic-gate 			epp->ep_state &= ~UGEN_EP_STATE_INTR_IN_POLLING_ON;
28530Sstevel@tonic-gate 		}
28540Sstevel@tonic-gate 	} else {
28550Sstevel@tonic-gate 		rval = USB_SUCCESS;
28560Sstevel@tonic-gate 	}
28570Sstevel@tonic-gate 
28580Sstevel@tonic-gate 	return (rval);
28590Sstevel@tonic-gate }
28600Sstevel@tonic-gate 
28610Sstevel@tonic-gate 
28620Sstevel@tonic-gate /*
28630Sstevel@tonic-gate  * stop polling on an interrupt endpoint, asynchronously
28640Sstevel@tonic-gate  */
28650Sstevel@tonic-gate static void
ugen_epx_intr_IN_stop_polling(ugen_state_t * ugenp,ugen_ep_t * epp)28660Sstevel@tonic-gate ugen_epx_intr_IN_stop_polling(ugen_state_t *ugenp, ugen_ep_t *epp)
28670Sstevel@tonic-gate {
28680Sstevel@tonic-gate 	if ((epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_ON) &&
28690Sstevel@tonic-gate 	    ((epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_IS_STOPPED) == 0)) {
28700Sstevel@tonic-gate 
28710Sstevel@tonic-gate 		USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
28720Sstevel@tonic-gate 		    "ugen_epx_intr_IN_stop_polling: epp=0x%p state=0x%x",
28736898Sfb209375 		    (void *)epp, epp->ep_state);
28740Sstevel@tonic-gate 
28750Sstevel@tonic-gate 		epp->ep_state |= UGEN_EP_STATE_INTR_IN_POLLING_IS_STOPPED;
28760Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
28770Sstevel@tonic-gate 		usb_pipe_stop_intr_polling(epp->ep_ph, USB_FLAGS_NOSLEEP);
28780Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
28790Sstevel@tonic-gate 	}
28800Sstevel@tonic-gate }
28810Sstevel@tonic-gate 
28820Sstevel@tonic-gate 
28830Sstevel@tonic-gate /*
28840Sstevel@tonic-gate  * poll management
28850Sstevel@tonic-gate  */
28860Sstevel@tonic-gate static void
ugen_epx_intr_IN_poll_wakeup(ugen_state_t * ugenp,ugen_ep_t * epp)28870Sstevel@tonic-gate ugen_epx_intr_IN_poll_wakeup(ugen_state_t *ugenp, ugen_ep_t *epp)
28880Sstevel@tonic-gate {
28890Sstevel@tonic-gate 	if (epp->ep_state & UGEN_EP_STATE_INTR_IN_POLL_PENDING) {
28900Sstevel@tonic-gate 		struct pollhead *phpp = &epp->ep_pollhead;
28910Sstevel@tonic-gate 
28920Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
28930Sstevel@tonic-gate 		    "ugen_epx_intr_IN_poll_wakeup: state=0x%x", epp->ep_state);
28940Sstevel@tonic-gate 
28950Sstevel@tonic-gate 		epp->ep_state &= ~UGEN_EP_STATE_INTR_IN_POLL_PENDING;
28960Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
28970Sstevel@tonic-gate 		pollwakeup(phpp, POLLIN);
28980Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
28990Sstevel@tonic-gate 	}
29000Sstevel@tonic-gate }
29010Sstevel@tonic-gate 
29020Sstevel@tonic-gate 
29030Sstevel@tonic-gate /*
29040Sstevel@tonic-gate  * callback functions for interrupt IN pipe
29050Sstevel@tonic-gate  */
29060Sstevel@tonic-gate static void
ugen_epx_intr_IN_req_cb(usb_pipe_handle_t ph,usb_intr_req_t * reqp)29070Sstevel@tonic-gate ugen_epx_intr_IN_req_cb(usb_pipe_handle_t ph, usb_intr_req_t *reqp)
29080Sstevel@tonic-gate {
29090Sstevel@tonic-gate 	ugen_state_t *ugenp = (ugen_state_t *)reqp->intr_client_private;
29100Sstevel@tonic-gate 	ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
29110Sstevel@tonic-gate 
29120Sstevel@tonic-gate 	if (epp == NULL) {
29130Sstevel@tonic-gate 		/* pipe is closing */
29140Sstevel@tonic-gate 
29150Sstevel@tonic-gate 		goto done;
29160Sstevel@tonic-gate 	}
29170Sstevel@tonic-gate 
29180Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
29190Sstevel@tonic-gate 
29200Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
29210Sstevel@tonic-gate 	    "ugen_epx_intr_IN_req_cb:\n\t"
29226898Sfb209375 	    "epp=0x%p state=0x%x ph=0x%p reqp=0x%p cr=%d cb=0x%x len=%ld",
29236898Sfb209375 	    (void *)epp, epp->ep_state, (void *)ph, (void *)reqp,
29246898Sfb209375 	    reqp->intr_completion_reason, reqp->intr_cb_flags,
29250Sstevel@tonic-gate 	    (reqp->intr_data == NULL) ? 0 :
29267492SZhigang.Lu@Sun.COM 	    MBLKL(reqp->intr_data));
29270Sstevel@tonic-gate 
29280Sstevel@tonic-gate 	ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
29290Sstevel@tonic-gate 
29300Sstevel@tonic-gate 	if (epp->ep_data && reqp->intr_data) {
29310Sstevel@tonic-gate 		mblk_t *mp;
29320Sstevel@tonic-gate 
29330Sstevel@tonic-gate 		USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
29345653Slc152243 		    "intr ep%x coalesce data", epp->ep_descr.bEndpointAddress);
29350Sstevel@tonic-gate 
29360Sstevel@tonic-gate 		/* coalesce the data into one mblk */
29370Sstevel@tonic-gate 		epp->ep_data->b_cont = reqp->intr_data;
29380Sstevel@tonic-gate 		if ((mp = msgpullup(epp->ep_data, -1)) != NULL) {
29390Sstevel@tonic-gate 			reqp->intr_data = NULL;
29400Sstevel@tonic-gate 			freemsg(epp->ep_data);
29410Sstevel@tonic-gate 			epp->ep_data = mp;
29420Sstevel@tonic-gate 		} else {
29430Sstevel@tonic-gate 			USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
29440Sstevel@tonic-gate 			    "msgpullup failed, discard data");
29450Sstevel@tonic-gate 			epp->ep_data->b_cont = NULL;
29460Sstevel@tonic-gate 		}
29470Sstevel@tonic-gate 	} else if (reqp->intr_data) {
29480Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
29490Sstevel@tonic-gate 		    "setting ep_data");
29500Sstevel@tonic-gate 
29510Sstevel@tonic-gate 		epp->ep_data = reqp->intr_data;
29520Sstevel@tonic-gate 		reqp->intr_data = NULL;
29530Sstevel@tonic-gate 	}
29540Sstevel@tonic-gate 
29550Sstevel@tonic-gate 	switch (reqp->intr_completion_reason) {
29560Sstevel@tonic-gate 	case USB_CR_OK:
29570Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
29580Sstevel@tonic-gate 
29590Sstevel@tonic-gate 		break;
29600Sstevel@tonic-gate 	case USB_CR_PIPE_RESET:
29610Sstevel@tonic-gate 	case USB_CR_STOPPED_POLLING:
29620Sstevel@tonic-gate 
29630Sstevel@tonic-gate 		break;
29640Sstevel@tonic-gate 	default:
29650Sstevel@tonic-gate 		epp->ep_lcmd_status =
29660Sstevel@tonic-gate 		    ugen_cr2lcstat(reqp->intr_completion_reason);
29670Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
29680Sstevel@tonic-gate 		    "ugen_exp_intr_cb_req: lcmd_status=0x%x",
29690Sstevel@tonic-gate 		    epp->ep_lcmd_status);
29700Sstevel@tonic-gate 
29710Sstevel@tonic-gate 		break;
29720Sstevel@tonic-gate 	}
29730Sstevel@tonic-gate 
29740Sstevel@tonic-gate 	/* any non-zero completion reason stops polling */
29750Sstevel@tonic-gate 	if ((reqp->intr_completion_reason) ||
29760Sstevel@tonic-gate 	    (epp->ep_one_xfer)) {
29770Sstevel@tonic-gate 		epp->ep_state &= ~(UGEN_EP_STATE_INTR_IN_POLLING_ON |
29785653Slc152243 		    UGEN_EP_STATE_INTR_IN_POLLING_IS_STOPPED);
29790Sstevel@tonic-gate 	}
29800Sstevel@tonic-gate 
29810Sstevel@tonic-gate 	/* is there a poll pending? should we stop polling? */
29820Sstevel@tonic-gate 	if (epp->ep_data) {
29830Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
29846898Sfb209375 		    "ugen_epx_intr_IN_req_cb: data len=0x%lx",
29857492SZhigang.Lu@Sun.COM 		    MBLKL(epp->ep_data));
29860Sstevel@tonic-gate 
29870Sstevel@tonic-gate 		ugen_epx_intr_IN_poll_wakeup(ugenp, epp);
29880Sstevel@tonic-gate 
29890Sstevel@tonic-gate 		/* if there is no space left, stop polling */
29900Sstevel@tonic-gate 		if (epp->ep_data &&
29917492SZhigang.Lu@Sun.COM 		    (MBLKL(epp->ep_data) >=
29920Sstevel@tonic-gate 		    epp->ep_buf_limit)) {
29930Sstevel@tonic-gate 			ugen_epx_intr_IN_stop_polling(ugenp, epp);
29940Sstevel@tonic-gate 		}
29950Sstevel@tonic-gate 	}
29960Sstevel@tonic-gate 
29970Sstevel@tonic-gate 	if (reqp->intr_completion_reason && epp->ep_bp) {
29980Sstevel@tonic-gate 		bioerror(epp->ep_bp, EIO);
29990Sstevel@tonic-gate 		epp->ep_done++;
30000Sstevel@tonic-gate 		cv_signal(&epp->ep_wait_cv);
30010Sstevel@tonic-gate 
30020Sstevel@tonic-gate 	/* can we satisfy the read now */
30030Sstevel@tonic-gate 	} else if (epp->ep_data && epp->ep_bp &&
30040Sstevel@tonic-gate 	    (!epp->ep_done || epp->ep_one_xfer)) {
30050Sstevel@tonic-gate 		boolean_t wait;
30060Sstevel@tonic-gate 
30070Sstevel@tonic-gate 		if ((ugen_epx_intr_IN_req(ugenp, epp, epp->ep_bp, &wait) ==
30080Sstevel@tonic-gate 		    USB_SUCCESS) && (wait == B_FALSE)) {
30090Sstevel@tonic-gate 			epp->ep_done++;
30100Sstevel@tonic-gate 			cv_signal(&epp->ep_wait_cv);
30110Sstevel@tonic-gate 		}
30120Sstevel@tonic-gate 	}
30130Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
30140Sstevel@tonic-gate 
30150Sstevel@tonic-gate done:
30160Sstevel@tonic-gate 	usb_free_intr_req(reqp);
30170Sstevel@tonic-gate }
30180Sstevel@tonic-gate 
30190Sstevel@tonic-gate 
30200Sstevel@tonic-gate /*
30210Sstevel@tonic-gate  * handle intr OUT xfers
30220Sstevel@tonic-gate  */
30230Sstevel@tonic-gate static int
ugen_epx_intr_OUT_req(ugen_state_t * ugenp,ugen_ep_t * epp,struct buf * bp,boolean_t * wait)30240Sstevel@tonic-gate ugen_epx_intr_OUT_req(ugen_state_t *ugenp, ugen_ep_t *epp,
30250Sstevel@tonic-gate     struct buf *bp, boolean_t *wait)
30260Sstevel@tonic-gate {
30270Sstevel@tonic-gate 	int	rval = USB_SUCCESS;
30280Sstevel@tonic-gate 	usb_intr_req_t	*reqp;
30290Sstevel@tonic-gate 
30300Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
30310Sstevel@tonic-gate 	    "ugen_epx_intr_OUT_req: epp=0x%p state=0x%x bp=0x%p",
30326898Sfb209375 	    (void *)epp, epp->ep_state, (void *)bp);
30330Sstevel@tonic-gate 
30340Sstevel@tonic-gate 	reqp = usb_alloc_intr_req(ugenp->ug_dip, bp->b_bcount,
30355653Slc152243 	    USB_FLAGS_NOSLEEP);
30360Sstevel@tonic-gate 	if (reqp == NULL) {
30370Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_NO_RESOURCES;
30380Sstevel@tonic-gate 
30390Sstevel@tonic-gate 		return (USB_NO_RESOURCES);
30400Sstevel@tonic-gate 	}
30410Sstevel@tonic-gate 
30420Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_XS_OPEN);
30430Sstevel@tonic-gate 
30440Sstevel@tonic-gate 	reqp->intr_timeout	= ugen_intr_timeout;
30450Sstevel@tonic-gate 	reqp->intr_client_private = (usb_opaque_t)ugenp;
30460Sstevel@tonic-gate 	reqp->intr_len		= bp->b_bcount;
30470Sstevel@tonic-gate 	reqp->intr_attributes	= USB_ATTRS_AUTOCLEARING;
30480Sstevel@tonic-gate 	reqp->intr_cb		= ugen_epx_intr_OUT_req_cb;
30490Sstevel@tonic-gate 	reqp->intr_exc_cb	= ugen_epx_intr_OUT_req_cb;
30500Sstevel@tonic-gate 
30510Sstevel@tonic-gate 	/* copy data from bp */
30520Sstevel@tonic-gate 	bcopy(epp->ep_bp->b_un.b_addr, reqp->intr_data->b_rptr,
30535653Slc152243 	    bp->b_bcount);
30540Sstevel@tonic-gate 	reqp->intr_data->b_wptr += bp->b_bcount;
30550Sstevel@tonic-gate 
30560Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
30570Sstevel@tonic-gate 	if ((rval = usb_pipe_intr_xfer(epp->ep_ph, reqp,
30580Sstevel@tonic-gate 	    USB_FLAGS_NOSLEEP)) != USB_SUCCESS) {
30590Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
30600Sstevel@tonic-gate 		epp->ep_lcmd_status =
30610Sstevel@tonic-gate 		    ugen_cr2lcstat(reqp->intr_completion_reason);
30620Sstevel@tonic-gate 		usb_free_intr_req(reqp);
30630Sstevel@tonic-gate 		bioerror(bp, EIO);
30640Sstevel@tonic-gate 	} else {
30650Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
30660Sstevel@tonic-gate 	}
30670Sstevel@tonic-gate 	*wait = (rval == USB_SUCCESS) ? B_TRUE : B_FALSE;
30680Sstevel@tonic-gate 
30690Sstevel@tonic-gate 	return (rval);
30700Sstevel@tonic-gate }
30710Sstevel@tonic-gate 
30720Sstevel@tonic-gate 
30730Sstevel@tonic-gate /*
30740Sstevel@tonic-gate  * callback functions for interrupt OUT pipe
30750Sstevel@tonic-gate  */
30760Sstevel@tonic-gate static void
ugen_epx_intr_OUT_req_cb(usb_pipe_handle_t ph,usb_intr_req_t * reqp)30770Sstevel@tonic-gate ugen_epx_intr_OUT_req_cb(usb_pipe_handle_t ph, usb_intr_req_t *reqp)
30780Sstevel@tonic-gate {
30790Sstevel@tonic-gate 	ugen_state_t *ugenp = (ugen_state_t *)reqp->intr_client_private;
30800Sstevel@tonic-gate 	ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
30810Sstevel@tonic-gate 
30820Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
30830Sstevel@tonic-gate 	    "ugen_epx_intr_OUT_req_cb: ph=0x%p reqp=0x%p cr=%d cb=0x%x",
30846898Sfb209375 	    (void *)ph, (void *)reqp, reqp->intr_completion_reason,
30856898Sfb209375 	    reqp->intr_cb_flags);
30860Sstevel@tonic-gate 
30870Sstevel@tonic-gate 	ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
30880Sstevel@tonic-gate 
30890Sstevel@tonic-gate 	/* epp might be NULL if we are closing the pipe */
30900Sstevel@tonic-gate 	if (epp) {
30910Sstevel@tonic-gate 		int len;
30920Sstevel@tonic-gate 
30930Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
30940Sstevel@tonic-gate 		if (epp->ep_bp) {
30957492SZhigang.Lu@Sun.COM 			len = min(MBLKL(reqp->intr_data), epp->ep_bp->b_bcount);
30960Sstevel@tonic-gate 
30970Sstevel@tonic-gate 			epp->ep_bp->b_resid = epp->ep_bp->b_bcount - len;
30980Sstevel@tonic-gate 
30990Sstevel@tonic-gate 			switch (reqp->intr_completion_reason) {
31000Sstevel@tonic-gate 			case USB_CR_OK:
31010Sstevel@tonic-gate 				epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
31020Sstevel@tonic-gate 
31030Sstevel@tonic-gate 				break;
31040Sstevel@tonic-gate 			case USB_CR_PIPE_RESET:
31050Sstevel@tonic-gate 
31060Sstevel@tonic-gate 				break;
31070Sstevel@tonic-gate 			default:
31080Sstevel@tonic-gate 				epp->ep_lcmd_status =
31090Sstevel@tonic-gate 				    ugen_cr2lcstat(
31100Sstevel@tonic-gate 				    reqp->intr_completion_reason);
31110Sstevel@tonic-gate 				bioerror(epp->ep_bp, EIO);
31120Sstevel@tonic-gate 			}
31130Sstevel@tonic-gate 		}
31140Sstevel@tonic-gate 		epp->ep_done++;
31150Sstevel@tonic-gate 		cv_signal(&epp->ep_wait_cv);
31160Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
31170Sstevel@tonic-gate 	}
31180Sstevel@tonic-gate 
31190Sstevel@tonic-gate 	usb_free_intr_req(reqp);
31200Sstevel@tonic-gate }
31210Sstevel@tonic-gate 
31220Sstevel@tonic-gate 
31230Sstevel@tonic-gate /*
31245653Slc152243  * handle isoc IN xfers
31255653Slc152243  */
31265653Slc152243 static int
ugen_epx_isoc_IN_req(ugen_state_t * ugenp,ugen_ep_t * epp,struct buf * bp,boolean_t * wait)31275653Slc152243 ugen_epx_isoc_IN_req(ugen_state_t *ugenp, ugen_ep_t *epp,
31285653Slc152243     struct buf *bp, boolean_t *wait)
31295653Slc152243 {
31305653Slc152243 	int rval = USB_SUCCESS;
31315653Slc152243 	ugen_isoc_pkt_descr_t *pkt_descr;
31325653Slc152243 	ushort_t n_pkt;
31335653Slc152243 	uint_t pkts_len, len = 0;
31345653Slc152243 
31355653Slc152243 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
31365653Slc152243 	    "ugen_epx_isoc_IN_req: epp=0x%p state=0x%x bp=0x%p",
31376898Sfb209375 	    (void *)epp, epp->ep_state, (void *)bp);
31385653Slc152243 
31395653Slc152243 	*wait = B_FALSE;
31405653Slc152243 
31415653Slc152243 	/* check if the isoc in pkt info has been initialized */
31425653Slc152243 	pkt_descr = epp->ep_isoc_info.isoc_pkt_descr;
31435653Slc152243 	n_pkt = epp->ep_isoc_info.isoc_pkts_count;
31445653Slc152243 	if ((n_pkt == 0) || (pkt_descr == NULL)) {
31455653Slc152243 		rval = USB_FAILURE;
31465653Slc152243 		epp->ep_lcmd_status = USB_LC_STAT_ISOC_UNINITIALIZED;
31475653Slc152243 
31485653Slc152243 		goto done;
31495653Slc152243 	}
31505653Slc152243 
31515653Slc152243 
31525653Slc152243 	/* For OUT endpoint, return pkts transfer status of last request */
31535653Slc152243 	if (UGEN_XFER_DIR(epp) != USB_EP_DIR_IN) {
31545653Slc152243 		if (bp->b_bcount < sizeof (ugen_isoc_pkt_descr_t) * n_pkt) {
31555653Slc152243 			rval = USB_INVALID_REQUEST;
31565653Slc152243 			epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
31575653Slc152243 
31585653Slc152243 			return (rval);
31595653Slc152243 		}
31605653Slc152243 		bcopy(epp->ep_isoc_info.isoc_pkt_descr, bp->b_un.b_addr,
31615653Slc152243 		    n_pkt * sizeof (ugen_isoc_pkt_descr_t));
31625653Slc152243 		epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
31635653Slc152243 
31645653Slc152243 		return (USB_SUCCESS);
31655653Slc152243 	}
31665653Slc152243 
31675653Slc152243 	/* read length should be the sum of pkt descrs and data length */
31685653Slc152243 	pkts_len = epp->ep_isoc_info.isoc_pkts_length;
31695653Slc152243 	if (bp->b_bcount != pkts_len + sizeof (ugen_isoc_pkt_descr_t) * n_pkt) {
31705653Slc152243 		rval = USB_INVALID_REQUEST;
31715653Slc152243 		epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
31725653Slc152243 
31735653Slc152243 		goto done;
31745653Slc152243 	}
31755653Slc152243 
31765653Slc152243 	/* can we satisfy this read? */
31775653Slc152243 	if (epp->ep_data) {
31787492SZhigang.Lu@Sun.COM 		len = min(MBLKL(epp->ep_data),
31795653Slc152243 		    bp->b_bcount);
31805653Slc152243 		/*
31815653Slc152243 		 * every msg block in ep_data must be the size of
31825653Slc152243 		 * pkts_len(payload length) + pkt descrs len
31835653Slc152243 		 */
31845653Slc152243 		ASSERT((len == 0) || (len == bp->b_bcount));
31855653Slc152243 	}
31865653Slc152243 
31875653Slc152243 	/*
31885653Slc152243 	 * if polling not active, restart
31895653Slc152243 	 * if there is some data, return the data
31905653Slc152243 	 */
31915653Slc152243 	if ((epp->ep_state & UGEN_EP_STATE_ISOC_IN_POLLING_ON) == 0) {
31925653Slc152243 		if (len == 0) {
31935653Slc152243 			rval = USB_FAILURE;
31945653Slc152243 			if ((rval = ugen_epx_isoc_IN_start_polling(ugenp,
31955653Slc152243 			    epp)) != USB_SUCCESS) {
31965653Slc152243 				epp->ep_lcmd_status =
31975653Slc152243 				    USB_LC_STAT_ISOC_POLLING_FAILED;
31985653Slc152243 			}
31995653Slc152243 
32005653Slc152243 			goto done;
32015653Slc152243 
32025653Slc152243 		} else if (epp->ep_data && (len >= bp->b_bcount)) {
32035653Slc152243 			bcopy(epp->ep_data->b_rptr, bp->b_un.b_addr,
32045653Slc152243 			    bp->b_bcount);
32055653Slc152243 			bp->b_resid = 0;
32065653Slc152243 			epp->ep_data->b_rptr += bp->b_bcount;
32075653Slc152243 
32085653Slc152243 			goto done;
32095653Slc152243 		}
32105653Slc152243 	}
32115653Slc152243 
32125653Slc152243 	/*
32135653Slc152243 	 * if there is data or FNDELAY, return available data
32145653Slc152243 	 */
32155653Slc152243 	if (epp->ep_data && (len >= bp->b_bcount)) {
32165653Slc152243 		/* can fulfill this read request */
32175653Slc152243 		bcopy(epp->ep_data->b_rptr, bp->b_un.b_addr, bp->b_bcount);
32185653Slc152243 		epp->ep_data->b_rptr += bp->b_bcount;
32195653Slc152243 		bp->b_resid = 0;
32205653Slc152243 	} else if (epp->ep_xfer_oflag & (FNDELAY | FNONBLOCK)) {
32215653Slc152243 		bp->b_resid = bp->b_bcount;
32225653Slc152243 	} else {
32235653Slc152243 		/* otherwise just wait for data */
32245653Slc152243 		*wait = B_TRUE;
32255653Slc152243 	}
32265653Slc152243 
32275653Slc152243 done:
32285653Slc152243 	/* data have been read */
32295653Slc152243 	if (epp->ep_data && (epp->ep_data->b_rptr == epp->ep_data->b_wptr)) {
32305653Slc152243 		mblk_t *mp = NULL;
32315653Slc152243 
32325653Slc152243 		/* remove the just read msg block */
32335653Slc152243 		mp = unlinkb(epp->ep_data);
32345653Slc152243 		freemsg(epp->ep_data);
32355653Slc152243 
32365653Slc152243 		if (mp) {
32375653Slc152243 			epp->ep_data = mp;
32385653Slc152243 		} else {
32395653Slc152243 			epp->ep_data = NULL;
32405653Slc152243 		}
32415653Slc152243 	}
32425653Slc152243 
32435653Slc152243 	if (*wait) {
32445653Slc152243 		ASSERT(epp->ep_state & UGEN_EP_STATE_ISOC_IN_POLLING_ON);
32455653Slc152243 	}
32465653Slc152243 
32475653Slc152243 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
32485653Slc152243 	    "ugen_epx_isoc_IN_req end: rval=%d bcount=%lu len=%d data=0x%p",
32496898Sfb209375 	    rval, bp->b_bcount, len, (void *)epp->ep_data);
32505653Slc152243 
32515653Slc152243 	return (rval);
32525653Slc152243 }
32535653Slc152243 
32545653Slc152243 
32555653Slc152243 /*
32565653Slc152243  * Start polling on isoc endpoint, asynchronously
32575653Slc152243  */
32585653Slc152243 static int
ugen_epx_isoc_IN_start_polling(ugen_state_t * ugenp,ugen_ep_t * epp)32595653Slc152243 ugen_epx_isoc_IN_start_polling(ugen_state_t *ugenp, ugen_ep_t *epp)
32605653Slc152243 {
32615653Slc152243 	int rval = USB_FAILURE;
32625653Slc152243 	usb_isoc_req_t	*reqp;
32635653Slc152243 	ugen_isoc_pkt_descr_t *pkt_descr;
32645653Slc152243 	ushort_t n_pkt, pkt;
32655653Slc152243 	uint_t pkts_len;
32665653Slc152243 
32675653Slc152243 	/*
32685653Slc152243 	 * if polling is being stopped, we restart polling in the
32695653Slc152243 	 * isoc callback again
32705653Slc152243 	 */
32715653Slc152243 	if (epp->ep_state & UGEN_EP_STATE_ISOC_IN_POLLING_IS_STOPPED) {
32725653Slc152243 
32735653Slc152243 		return (rval);
32745653Slc152243 	}
32755653Slc152243 
32765653Slc152243 	if ((epp->ep_state & UGEN_EP_STATE_ISOC_IN_POLLING_ON) == 0) {
32775653Slc152243 		USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
32785653Slc152243 		    "ugen_epx_isoc_IN_start_polling: epp=0x%p state=0x%x",
32796898Sfb209375 		    (void *)epp, epp->ep_state);
32805653Slc152243 
32815653Slc152243 		pkts_len = epp->ep_isoc_info.isoc_pkts_length;
32825653Slc152243 		n_pkt = epp->ep_isoc_info.isoc_pkts_count;
32835653Slc152243 		pkt_descr = epp->ep_isoc_info.isoc_pkt_descr;
32845653Slc152243 
32855653Slc152243 		epp->ep_state |= UGEN_EP_STATE_ISOC_IN_POLLING_ON;
32865653Slc152243 		mutex_exit(&epp->ep_mutex);
32875653Slc152243 
32885653Slc152243 		if ((reqp = usb_alloc_isoc_req(ugenp->ug_dip, n_pkt, pkts_len,
32895653Slc152243 		    USB_FLAGS_NOSLEEP)) == NULL) {
32905653Slc152243 			USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
32915653Slc152243 			    "ugen_epx_isoc_IN_start_polling: alloc isoc "
32925653Slc152243 			    "req failed");
32935653Slc152243 			mutex_enter(&epp->ep_mutex);
32945653Slc152243 			epp->ep_state &= ~UGEN_EP_STATE_ISOC_IN_POLLING_ON;
32955653Slc152243 
32965653Slc152243 			return (USB_NO_RESOURCES);
32975653Slc152243 		}
32985653Slc152243 		reqp->isoc_client_private = (usb_opaque_t)ugenp;
32995653Slc152243 
33005653Slc152243 		reqp->isoc_attributes	= USB_ATTRS_AUTOCLEARING |
33015653Slc152243 		    USB_ATTRS_SHORT_XFER_OK | USB_ATTRS_ISOC_XFER_ASAP;
33025653Slc152243 
33035653Slc152243 		/*
33045653Slc152243 		 * isoc_pkts_length was defined to be ushort_t. This
33055653Slc152243 		 * has been obsoleted by usb high speed isoc support.
33065653Slc152243 		 * It is set here just for compatibility reason
33075653Slc152243 		 */
33085653Slc152243 		reqp->isoc_pkts_length = 0;
33095653Slc152243 
33105653Slc152243 		for (pkt = 0; pkt < n_pkt; pkt++) {
33115653Slc152243 			reqp->isoc_pkt_descr[pkt].isoc_pkt_length =
33125653Slc152243 			    pkt_descr[pkt].dsc_isoc_pkt_len;
33135653Slc152243 		}
33145653Slc152243 		reqp->isoc_pkts_count	= n_pkt;
33155653Slc152243 		reqp->isoc_cb		= ugen_epx_isoc_IN_req_cb;
33165653Slc152243 		reqp->isoc_exc_cb	= ugen_epx_isoc_IN_req_cb;
33175653Slc152243 
33185653Slc152243 		if ((rval = usb_pipe_isoc_xfer(epp->ep_ph, reqp,
33195653Slc152243 		    USB_FLAGS_NOSLEEP)) != USB_SUCCESS) {
33205653Slc152243 			USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
33215653Slc152243 			    "ugen_epx_isoc_IN_start_polling: failed %d", rval);
33225653Slc152243 			usb_free_isoc_req(reqp);
33235653Slc152243 		}
33245653Slc152243 
33255653Slc152243 		mutex_enter(&epp->ep_mutex);
33265653Slc152243 		if (rval != USB_SUCCESS) {
33275653Slc152243 			epp->ep_state &= ~UGEN_EP_STATE_ISOC_IN_POLLING_ON;
33285653Slc152243 		}
33295653Slc152243 	} else {
33305653Slc152243 		rval = USB_SUCCESS;
33315653Slc152243 	}
33325653Slc152243 
33335653Slc152243 	return (rval);
33345653Slc152243 }
33355653Slc152243 
33365653Slc152243 
33375653Slc152243 /*
33385653Slc152243  * stop polling on an isoc endpoint, asynchronously
33395653Slc152243  */
33405653Slc152243 static void
ugen_epx_isoc_IN_stop_polling(ugen_state_t * ugenp,ugen_ep_t * epp)33415653Slc152243 ugen_epx_isoc_IN_stop_polling(ugen_state_t *ugenp, ugen_ep_t *epp)
33425653Slc152243 {
33435653Slc152243 	if ((epp->ep_state & UGEN_EP_STATE_ISOC_IN_POLLING_ON) &&
33445653Slc152243 	    ((epp->ep_state & UGEN_EP_STATE_ISOC_IN_POLLING_IS_STOPPED) == 0)) {
33455653Slc152243 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
33465653Slc152243 		    "ugen_epx_isoc_IN_stop_polling: epp=0x%p state=0x%x",
33476898Sfb209375 		    (void *)epp, epp->ep_state);
33485653Slc152243 
33495653Slc152243 		epp->ep_state |= UGEN_EP_STATE_ISOC_IN_POLLING_IS_STOPPED;
33505653Slc152243 		mutex_exit(&epp->ep_mutex);
33515653Slc152243 		usb_pipe_stop_isoc_polling(epp->ep_ph, USB_FLAGS_NOSLEEP);
33525653Slc152243 		mutex_enter(&epp->ep_mutex);
33535653Slc152243 	}
33545653Slc152243 }
33555653Slc152243 
33565653Slc152243 
33575653Slc152243 /*
33585653Slc152243  * poll management
33595653Slc152243  */
33605653Slc152243 static void
ugen_epx_isoc_IN_poll_wakeup(ugen_state_t * ugenp,ugen_ep_t * epp)33615653Slc152243 ugen_epx_isoc_IN_poll_wakeup(ugen_state_t *ugenp, ugen_ep_t *epp)
33625653Slc152243 {
33635653Slc152243 	if (epp->ep_state & UGEN_EP_STATE_ISOC_IN_POLL_PENDING) {
33645653Slc152243 		struct pollhead *phpp = &epp->ep_pollhead;
33655653Slc152243 
33665653Slc152243 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
33675653Slc152243 		    "ugen_epx_isoc_IN_poll_wakeup: state=0x%x", epp->ep_state);
33685653Slc152243 
33695653Slc152243 		epp->ep_state &= ~UGEN_EP_STATE_ISOC_IN_POLL_PENDING;
33705653Slc152243 		mutex_exit(&epp->ep_mutex);
33715653Slc152243 		pollwakeup(phpp, POLLIN);
33725653Slc152243 		mutex_enter(&epp->ep_mutex);
33735653Slc152243 	}
33745653Slc152243 }
33755653Slc152243 
33765653Slc152243 
33775653Slc152243 /*
33785653Slc152243  * callback functions for isoc IN pipe
33795653Slc152243  */
33805653Slc152243 static void
ugen_epx_isoc_IN_req_cb(usb_pipe_handle_t ph,usb_isoc_req_t * reqp)33815653Slc152243 ugen_epx_isoc_IN_req_cb(usb_pipe_handle_t ph, usb_isoc_req_t *reqp)
33825653Slc152243 {
33835653Slc152243 	ugen_state_t *ugenp = (ugen_state_t *)reqp->isoc_client_private;
33845653Slc152243 	ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
33855653Slc152243 
33865653Slc152243 	if (epp == NULL) {
33875653Slc152243 		/* pipe is closing */
33885653Slc152243 
33895653Slc152243 		goto done;
33905653Slc152243 	}
33915653Slc152243 
33925653Slc152243 	ASSERT(!mutex_owned(&epp->ep_mutex)); /* not owned */
33935653Slc152243 
33945653Slc152243 	mutex_enter(&epp->ep_mutex);
33955653Slc152243 
33965653Slc152243 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
33975653Slc152243 	    "ugen_epx_isoc_IN_req_cb: "
33986898Sfb209375 	    "epp=0x%p state=0x%x ph=0x%p reqp=0x%p cr=%d cb=0x%x len=%ld "
33996898Sfb209375 	    "isoc error count=%d, pkt cnt=%d", (void *)epp, epp->ep_state,
34006898Sfb209375 	    (void *)ph, (void *)reqp, reqp->isoc_completion_reason,
34016898Sfb209375 	    reqp->isoc_cb_flags, (reqp->isoc_data == NULL) ? 0 :
34027492SZhigang.Lu@Sun.COM 	    MBLKL(reqp->isoc_data),
34035653Slc152243 	    reqp->isoc_error_count, reqp->isoc_pkts_count);
34045653Slc152243 
34055653Slc152243 	/* Too many packet errors during isoc transfer of this request */
34065653Slc152243 	if (reqp->isoc_error_count == reqp->isoc_pkts_count) {
34075653Slc152243 		USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
34085653Slc152243 		    "too many errors(%d) in this req, stop polling",
34095653Slc152243 		    reqp->isoc_error_count);
34105653Slc152243 		epp->ep_lcmd_status = USB_LC_STAT_ISOC_PKT_ERROR;
34115653Slc152243 		ugen_epx_isoc_IN_stop_polling(ugenp, epp);
34125653Slc152243 	}
34135653Slc152243 
34145653Slc152243 	/* Data OK */
34155653Slc152243 	if (reqp->isoc_data && !reqp->isoc_completion_reason) {
34165653Slc152243 		mblk_t *mp1 = NULL, *mp2 = NULL;
34175653Slc152243 		usb_isoc_pkt_descr_t *pkt_descr =
34185653Slc152243 		    reqp->isoc_pkt_descr;
34195653Slc152243 		ushort_t i, n_pkt = reqp->isoc_pkts_count;
34205653Slc152243 
34215653Slc152243 		for (i = 0; i < n_pkt; i++) {
34225653Slc152243 			USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
34235653Slc152243 			    "pkt %d: len=%d status=%d actual_len=%d", i,
34245653Slc152243 			    pkt_descr[i].isoc_pkt_length,
34255653Slc152243 			    pkt_descr[i].isoc_pkt_status,
34265653Slc152243 			    pkt_descr[i].isoc_pkt_actual_length);
34275653Slc152243 
34285653Slc152243 			/* translate cr to ugen lcstat */
34295653Slc152243 			pkt_descr[i].isoc_pkt_status =
34305653Slc152243 			    ugen_cr2lcstat(pkt_descr[i].isoc_pkt_status);
34315653Slc152243 		}
34325653Slc152243 
34335653Slc152243 		/* construct data buffer: pkt descriptors + payload */
34345653Slc152243 		mp2 = allocb(sizeof (ugen_isoc_pkt_descr_t) * n_pkt, BPRI_HI);
34355653Slc152243 		if (mp2 == NULL) {
34365653Slc152243 			USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
34375653Slc152243 			    "alloc msgblk failed, discard data");
34385653Slc152243 		} else {
34395653Slc152243 			/* pkt descrs first */
34405653Slc152243 			bcopy(pkt_descr, mp2->b_wptr,
34415653Slc152243 			    sizeof (ugen_isoc_pkt_descr_t) * n_pkt);
34425653Slc152243 
34435653Slc152243 			mp2->b_wptr += sizeof (ugen_isoc_pkt_descr_t) * n_pkt;
34445653Slc152243 
34455653Slc152243 			/* payload follows */
34465653Slc152243 			linkb(mp2, reqp->isoc_data);
34475653Slc152243 
34485653Slc152243 			/* concatenate data bytes in mp2 */
34495653Slc152243 			if ((mp1 = msgpullup(mp2, -1)) != NULL) {
34505653Slc152243 				/*
34515653Slc152243 				 * now we get the required data:
34525653Slc152243 				 *	pkt descrs + payload
34535653Slc152243 				 */
34545653Slc152243 				reqp->isoc_data = NULL;
34555653Slc152243 			} else {
34565653Slc152243 				USB_DPRINTF_L2(UGEN_PRINT_XFER,
34575653Slc152243 				    ugenp->ug_log_hdl,
34585653Slc152243 				    "msgpullup status blk failed, "
34595653Slc152243 				    "discard data");
34605653Slc152243 				mp2->b_cont = NULL;
34615653Slc152243 			}
34625653Slc152243 
34635653Slc152243 			freemsg(mp2);
34645653Slc152243 			mp2 = NULL;
34655653Slc152243 		}
34665653Slc152243 
34675653Slc152243 		if (epp->ep_data && (mp1 != NULL)) {
34685653Slc152243 			USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
34695653Slc152243 			    "ISOC ep%x coalesce ep_data",
34705653Slc152243 			    epp->ep_descr.bEndpointAddress);
34715653Slc152243 
34725653Slc152243 			/* add mp1 to the tail of ep_data */
34735653Slc152243 			linkb(epp->ep_data, mp1);
34745653Slc152243 
34755653Slc152243 		} else if (mp1 != NULL) {
34765653Slc152243 			USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
34775653Slc152243 			    "setting ep_data");
34785653Slc152243 			epp->ep_data = mp1;
34795653Slc152243 		}
34805653Slc152243 	}
34815653Slc152243 
34825653Slc152243 	switch (reqp->isoc_completion_reason) {
34835653Slc152243 	case USB_CR_OK:
34845653Slc152243 		epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
34855653Slc152243 
34865653Slc152243 		break;
34875653Slc152243 	case USB_CR_PIPE_RESET:
34885653Slc152243 	case USB_CR_STOPPED_POLLING:
34895653Slc152243 	case USB_CR_PIPE_CLOSING:
34905653Slc152243 
34915653Slc152243 		break;
34925653Slc152243 	default:
34935653Slc152243 		epp->ep_lcmd_status =
34945653Slc152243 		    ugen_cr2lcstat(reqp->isoc_completion_reason);
34955653Slc152243 		USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
34965653Slc152243 		    "ugen_exp_isoc_cb_req: error lcmd_status=0x%x ",
34975653Slc152243 		    epp->ep_lcmd_status);
34985653Slc152243 
34995653Slc152243 		break;
35005653Slc152243 	}
35015653Slc152243 
35025653Slc152243 	/* any non-zero completion reason signifies polling has stopped */
35035653Slc152243 	if (reqp->isoc_completion_reason) {
35045653Slc152243 		epp->ep_state &= ~(UGEN_EP_STATE_ISOC_IN_POLLING_ON |
35055653Slc152243 		    UGEN_EP_STATE_ISOC_IN_POLLING_IS_STOPPED);
35065653Slc152243 	}
35075653Slc152243 
35085653Slc152243 
35095653Slc152243 	/* is there a poll pending? should we stop polling? */
35105653Slc152243 	if (epp->ep_data) {
35115653Slc152243 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
35126898Sfb209375 		    "ugen_epx_isoc_IN_req_cb: data len=0x%lx, limit=0x%lx",
35135653Slc152243 		    msgdsize(epp->ep_data),
35145653Slc152243 		    epp->ep_buf_limit);
35155653Slc152243 
35165653Slc152243 		ugen_epx_isoc_IN_poll_wakeup(ugenp, epp);
35175653Slc152243 
35185653Slc152243 
35195653Slc152243 		/*
35205653Slc152243 		 * Since isoc is unreliable xfer, if buffered data size exceeds
35215653Slc152243 		 * the limit, we just discard and free data in the oldest mblk
35225653Slc152243 		 */
35235653Slc152243 		if (epp->ep_data &&
35245653Slc152243 		    (msgdsize(epp->ep_data) >= epp->ep_buf_limit)) {
35255653Slc152243 			mblk_t *mp = NULL;
35265653Slc152243 
35275653Slc152243 			/* exceed buf lenth limit, remove the oldest one */
35285653Slc152243 			USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
35295653Slc152243 			    "ugen_epx_isoc_IN_req_cb: overflow!");
35305653Slc152243 			mp = unlinkb(epp->ep_data);
35315653Slc152243 			if (epp->ep_data) {
35325653Slc152243 				freeb(epp->ep_data);
35335653Slc152243 			}
35345653Slc152243 			epp->ep_data = mp;
35355653Slc152243 		}
35365653Slc152243 
35375653Slc152243 	}
35385653Slc152243 
35395653Slc152243 	if (reqp->isoc_completion_reason && epp->ep_bp) {
35405653Slc152243 		bioerror(epp->ep_bp, EIO);
35415653Slc152243 		epp->ep_done++;
35425653Slc152243 		cv_signal(&epp->ep_wait_cv);
35435653Slc152243 
35445653Slc152243 	} else if (epp->ep_data && epp->ep_bp && !epp->ep_done) {
35455653Slc152243 		boolean_t wait;
35465653Slc152243 
35475653Slc152243 		/* can we satisfy the read now */
35485653Slc152243 		if ((ugen_epx_isoc_IN_req(ugenp, epp, epp->ep_bp, &wait) ==
35495653Slc152243 		    USB_SUCCESS) && (wait == B_FALSE)) {
35505653Slc152243 			epp->ep_done++;
35515653Slc152243 			cv_signal(&epp->ep_wait_cv);
35525653Slc152243 		}
35535653Slc152243 	}
35545653Slc152243 	mutex_exit(&epp->ep_mutex);
35555653Slc152243 
35565653Slc152243 done:
35575653Slc152243 
35585653Slc152243 	usb_free_isoc_req(reqp);
35595653Slc152243 }
35605653Slc152243 
35615653Slc152243 /*
35625653Slc152243  * handle isoc OUT xfers or init isoc IN polling
35635653Slc152243  */
35645653Slc152243 static int
ugen_epx_isoc_OUT_req(ugen_state_t * ugenp,ugen_ep_t * epp,struct buf * bp,boolean_t * wait)35655653Slc152243 ugen_epx_isoc_OUT_req(ugen_state_t *ugenp, ugen_ep_t *epp,
35665653Slc152243     struct buf *bp, boolean_t *wait)
35675653Slc152243 {
35685653Slc152243 	int rval = USB_SUCCESS;
35695653Slc152243 	usb_isoc_req_t *reqp;
35705653Slc152243 	ugen_isoc_pkt_descr_t *pkt_descr;
35715653Slc152243 	ushort_t pkt, n_pkt = 0;
35725653Slc152243 	uint_t pkts_len = 0;
35735653Slc152243 	uint_t head_len;
35745653Slc152243 	char *p;
35755653Slc152243 	ugen_isoc_req_head_t *pkth;
35765653Slc152243 
35775653Slc152243 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
35785653Slc152243 	    "ugen_epx_isoc_OUT_req: epp=0x%p state=0x%x bp=0x%p",
35796898Sfb209375 	    (void *)epp, epp->ep_state, (void *)bp);
35805653Slc152243 
35815653Slc152243 	*wait = B_FALSE;
35825653Slc152243 
35835653Slc152243 	if (bp->b_bcount < sizeof (int)) {
35845653Slc152243 		epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
35855653Slc152243 		rval = USB_INVALID_REQUEST;
35865653Slc152243 
35875653Slc152243 		goto done;
35885653Slc152243 	}
35895653Slc152243 
35907492SZhigang.Lu@Sun.COM 	/* LINTED E_BAD_PTR_CAST_ALIGN */
35915653Slc152243 	pkth = (ugen_isoc_req_head_t *)bp->b_un.b_addr;
35925653Slc152243 	n_pkt = pkth->req_isoc_pkts_count;
35935653Slc152243 	head_len = sizeof (ugen_isoc_pkt_descr_t) * n_pkt +
35945653Slc152243 	    sizeof (int);
35955653Slc152243 
35965653Slc152243 	if ((n_pkt == 0) ||
35975653Slc152243 	    (n_pkt > usb_get_max_pkts_per_isoc_request(ugenp->ug_dip)) ||
35985653Slc152243 	    (bp->b_bcount < head_len)) {
35995653Slc152243 		USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
36006898Sfb209375 		    "Invalid params: bcount=%lu, head_len=%d, pktcnt=%d",
36015653Slc152243 		    bp->b_bcount, head_len, n_pkt);
36025653Slc152243 
36035653Slc152243 		epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
36045653Slc152243 		rval = USB_INVALID_REQUEST;
36055653Slc152243 
36065653Slc152243 		goto done;
36075653Slc152243 	}
36085653Slc152243 
36095653Slc152243 	p = bp->b_un.b_addr;
36105653Slc152243 	p += sizeof (int); /* points to pkt_descrs */
36115653Slc152243 
36125653Slc152243 	pkt_descr = kmem_zalloc(sizeof (ugen_isoc_pkt_descr_t) * n_pkt,
36135653Slc152243 	    KM_NOSLEEP);
36145653Slc152243 	if (pkt_descr == NULL) {
36155653Slc152243 		epp->ep_lcmd_status = USB_LC_STAT_NO_RESOURCES;
36165653Slc152243 		rval = USB_NO_RESOURCES;
36175653Slc152243 
36185653Slc152243 		goto done;
36195653Slc152243 	}
36205653Slc152243 	bcopy(p, pkt_descr, sizeof (ugen_isoc_pkt_descr_t) * n_pkt);
36215653Slc152243 	p += sizeof (ugen_isoc_pkt_descr_t) * n_pkt;
36225653Slc152243 
36235653Slc152243 	/* total packet payload length */
36245653Slc152243 	for (pkt = 0; pkt < n_pkt; pkt++) {
36255653Slc152243 		pkts_len += pkt_descr[pkt].dsc_isoc_pkt_len;
36265653Slc152243 	}
36275653Slc152243 
36285653Slc152243 	/*
36295653Slc152243 	 * write length may either be header length for isoc IN endpoint or
36305653Slc152243 	 * the sum of header and data pkts length for isoc OUT endpoint
36315653Slc152243 	 */
36325653Slc152243 	if (((bp->b_bcount != head_len) &&
36335653Slc152243 	    (bp->b_bcount != head_len + pkts_len))) {
36345653Slc152243 		USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
36356898Sfb209375 		    "invalid length: bcount=%lu, head_len=%d, pkts_len = %d,"
36365653Slc152243 		    "pktcnt=%d", bp->b_bcount, head_len, pkts_len, n_pkt);
36375653Slc152243 
36385653Slc152243 		epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
36395653Slc152243 		kmem_free(pkt_descr, sizeof (ugen_isoc_pkt_descr_t) * n_pkt);
36405653Slc152243 		rval = USB_INVALID_REQUEST;
36415653Slc152243 
36425653Slc152243 		goto done;
36435653Slc152243 	}
36445653Slc152243 
36455653Slc152243 
36465653Slc152243 	ASSERT(epp->ep_state & UGEN_EP_STATE_XS_OPEN);
36475653Slc152243 
36485653Slc152243 	/* Set parameters for READ */
36495653Slc152243 	if (bp->b_bcount == head_len) {
36505653Slc152243 		/* must be isoc IN endpoint */
36515653Slc152243 		if ((UGEN_XFER_DIR(epp) & USB_EP_DIR_IN) == 0) {
36525653Slc152243 			epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
36535653Slc152243 			kmem_free(pkt_descr, sizeof (ugen_isoc_pkt_descr_t) *
36545653Slc152243 			    n_pkt);
36555653Slc152243 			rval = USB_INVALID_REQUEST;
36565653Slc152243 			USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
36575653Slc152243 			    "write length invalid for OUT ep%x",
36585653Slc152243 			    epp->ep_descr.bEndpointAddress);
36595653Slc152243 
36605653Slc152243 			goto done;
36615653Slc152243 		}
36625653Slc152243 
36635653Slc152243 		if (epp->ep_isoc_in_inited) {
36645653Slc152243 			epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
36655653Slc152243 			kmem_free(pkt_descr, sizeof (ugen_isoc_pkt_descr_t) *
36665653Slc152243 			    n_pkt);
36675653Slc152243 			rval = USB_INVALID_REQUEST;
36685653Slc152243 			USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
36695653Slc152243 			    "isoc IN polling fail: already inited, need to"
36705653Slc152243 			    "close the ep before initing again");
36715653Slc152243 
36725653Slc152243 			goto done;
36735653Slc152243 		}
36745653Slc152243 
36755653Slc152243 		/* save pkts info for the READ */
36765653Slc152243 		epp->ep_isoc_info.isoc_pkts_count = n_pkt;
36775653Slc152243 		epp->ep_isoc_info.isoc_pkts_length = pkts_len;
36785653Slc152243 		epp->ep_isoc_info.isoc_pkt_descr = pkt_descr;
36795653Slc152243 
36805653Slc152243 		if ((rval = ugen_epx_isoc_IN_start_polling(ugenp,
36815653Slc152243 		    epp)) != USB_SUCCESS) {
36825653Slc152243 			epp->ep_lcmd_status =
36835653Slc152243 			    USB_LC_STAT_ISOC_POLLING_FAILED;
36845653Slc152243 			kmem_free(pkt_descr, sizeof (ugen_isoc_pkt_descr_t) *
36855653Slc152243 			    n_pkt);
36865653Slc152243 			epp->ep_isoc_info.isoc_pkts_count = 0;
36875653Slc152243 			epp->ep_isoc_info.isoc_pkts_length = 0;
36885653Slc152243 			epp->ep_isoc_info.isoc_pkt_descr = NULL;
36895653Slc152243 
36905653Slc152243 			USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
36915653Slc152243 			    "isoc IN start polling failed");
36925653Slc152243 
36935653Slc152243 			goto done;
36945653Slc152243 		}
36955653Slc152243 
36965653Slc152243 		epp->ep_bp->b_resid = epp->ep_bp->b_bcount - head_len;
36975653Slc152243 
36985653Slc152243 		epp->ep_isoc_in_inited++;
36995653Slc152243 		USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
37005653Slc152243 		    "isoc IN ep inited");
37015653Slc152243 
37025653Slc152243 		goto done;
37035653Slc152243 	}
37045653Slc152243 
37055653Slc152243 	/* must be isoc OUT endpoint */
37065653Slc152243 	if (UGEN_XFER_DIR(epp) & USB_EP_DIR_IN) {
37075653Slc152243 		epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
37085653Slc152243 		kmem_free(pkt_descr, sizeof (ugen_isoc_pkt_descr_t) * n_pkt);
37095653Slc152243 		rval = USB_INVALID_REQUEST;
37105653Slc152243 		USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
37115653Slc152243 		    "write length invalid for an IN ep%x",
37125653Slc152243 		    epp->ep_descr.bEndpointAddress);
37135653Slc152243 
37145653Slc152243 		goto done;
37155653Slc152243 	}
37165653Slc152243 
37175653Slc152243 	/* OUT endpoint, free previous info if there's any */
37185653Slc152243 	if (epp->ep_isoc_info.isoc_pkt_descr) {
37195653Slc152243 		kmem_free(epp->ep_isoc_info.isoc_pkt_descr,
37205653Slc152243 		    sizeof (ugen_isoc_pkt_descr_t) *
37215653Slc152243 		    epp->ep_isoc_info.isoc_pkts_count);
37225653Slc152243 	}
37235653Slc152243 
37245653Slc152243 	/* save pkts info for the WRITE */
37255653Slc152243 	epp->ep_isoc_info.isoc_pkts_count = n_pkt;
37265653Slc152243 	epp->ep_isoc_info.isoc_pkts_length = pkts_len;
37275653Slc152243 	epp->ep_isoc_info.isoc_pkt_descr = pkt_descr;
37285653Slc152243 
37295653Slc152243 	reqp = usb_alloc_isoc_req(ugenp->ug_dip, n_pkt, pkts_len,
37305653Slc152243 	    USB_FLAGS_NOSLEEP);
37315653Slc152243 	if (reqp == NULL) {
37325653Slc152243 		epp->ep_lcmd_status = USB_LC_STAT_NO_RESOURCES;
37335653Slc152243 		kmem_free(pkt_descr, sizeof (ugen_isoc_pkt_descr_t) * n_pkt);
37345653Slc152243 		rval = USB_NO_RESOURCES;
37355653Slc152243 		epp->ep_isoc_info.isoc_pkts_count = 0;
37365653Slc152243 		epp->ep_isoc_info.isoc_pkts_length = 0;
37375653Slc152243 		epp->ep_isoc_info.isoc_pkt_descr = NULL;
37385653Slc152243 
37395653Slc152243 		USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
37405653Slc152243 		    "alloc isoc out req failed");
37415653Slc152243 		goto done;
37425653Slc152243 	}
37435653Slc152243 
37445653Slc152243 	for (pkt = 0; pkt < n_pkt; pkt++) {
37455653Slc152243 		reqp->isoc_pkt_descr[pkt].isoc_pkt_length =
37465653Slc152243 		    pkt_descr[pkt].dsc_isoc_pkt_len;
37475653Slc152243 	}
37485653Slc152243 	reqp->isoc_pkts_count = n_pkt;
37495653Slc152243 	reqp->isoc_client_private = (usb_opaque_t)ugenp;
37505653Slc152243 	reqp->isoc_attributes	= USB_ATTRS_AUTOCLEARING |
37515653Slc152243 	    USB_ATTRS_ISOC_XFER_ASAP;
37525653Slc152243 
37535653Slc152243 	reqp->isoc_cb		= ugen_epx_isoc_OUT_req_cb;
37545653Slc152243 	reqp->isoc_exc_cb	= ugen_epx_isoc_OUT_req_cb;
37555653Slc152243 
37565653Slc152243 	/* copy data from bp */
37575653Slc152243 	bcopy(p, reqp->isoc_data->b_wptr, pkts_len);
37585653Slc152243 	reqp->isoc_data->b_wptr += pkts_len;
37595653Slc152243 
37605653Slc152243 	mutex_exit(&epp->ep_mutex);
37615653Slc152243 	if ((rval = usb_pipe_isoc_xfer(epp->ep_ph, reqp,
37625653Slc152243 	    USB_FLAGS_NOSLEEP)) != USB_SUCCESS) {
37635653Slc152243 		mutex_enter(&epp->ep_mutex);
37645653Slc152243 		epp->ep_lcmd_status =
37655653Slc152243 		    ugen_cr2lcstat(reqp->isoc_completion_reason);
37665653Slc152243 		usb_free_isoc_req(reqp);
37675653Slc152243 		kmem_free(pkt_descr, sizeof (ugen_isoc_pkt_descr_t) * n_pkt);
37685653Slc152243 
37695653Slc152243 		epp->ep_isoc_info.isoc_pkt_descr = NULL;
37705653Slc152243 		epp->ep_isoc_info.isoc_pkts_count = 0;
37715653Slc152243 		epp->ep_isoc_info.isoc_pkts_length = 0;
37725653Slc152243 
37735653Slc152243 		USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
37745653Slc152243 		    "isoc out xfer failed");
37755653Slc152243 
37765653Slc152243 		bioerror(bp, EIO);
37775653Slc152243 	} else {
37785653Slc152243 		mutex_enter(&epp->ep_mutex);
37795653Slc152243 	}
37805653Slc152243 	*wait = (rval == USB_SUCCESS) ? B_TRUE : B_FALSE;
37815653Slc152243 
37825653Slc152243 done:
37835653Slc152243 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
37845653Slc152243 	    "ugen_epx_isoc_OUT_req end: rval=%d bcount=%lu xfer_len=%d",
37855653Slc152243 	    rval, bp->b_bcount, pkts_len);
37865653Slc152243 
37875653Slc152243 	return (rval);
37885653Slc152243 }
37895653Slc152243 
37905653Slc152243 
37915653Slc152243 /*
37925653Slc152243  * callback functions for isoc OUT pipe
37935653Slc152243  */
37945653Slc152243 static void
ugen_epx_isoc_OUT_req_cb(usb_pipe_handle_t ph,usb_isoc_req_t * reqp)37955653Slc152243 ugen_epx_isoc_OUT_req_cb(usb_pipe_handle_t ph, usb_isoc_req_t *reqp)
37965653Slc152243 {
37975653Slc152243 	ugen_state_t *ugenp = (ugen_state_t *)reqp->isoc_client_private;
37985653Slc152243 	ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
37995653Slc152243 
38005653Slc152243 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
38015653Slc152243 	    "ugen_epx_isoc_OUT_req_cb: ph=0x%p reqp=0x%p cr=%d cb=0x%x",
38026898Sfb209375 	    (void *)ph, (void *)reqp, reqp->isoc_completion_reason,
38036898Sfb209375 	    reqp->isoc_cb_flags);
38045653Slc152243 
38055653Slc152243 	/* epp might be NULL if we are closing the pipe */
38065653Slc152243 	if (epp) {
38075653Slc152243 		ugen_isoc_pkt_info_t info;
38085653Slc152243 
38095653Slc152243 		mutex_enter(&epp->ep_mutex);
38105653Slc152243 
38115653Slc152243 		info = epp->ep_isoc_info;
38125653Slc152243 		if (epp->ep_bp) {
38135653Slc152243 			int len, i;
38145653Slc152243 			int headlen;
38155653Slc152243 			usb_isoc_pkt_descr_t *pktdesc;
38165653Slc152243 
38175653Slc152243 			pktdesc = reqp->isoc_pkt_descr;
38185653Slc152243 			headlen = info.isoc_pkts_count *
38195653Slc152243 			    sizeof (ugen_isoc_pkt_descr_t);
38205653Slc152243 
38217492SZhigang.Lu@Sun.COM 			len = min(headlen + MBLKL(reqp->isoc_data),
38227492SZhigang.Lu@Sun.COM 			    epp->ep_bp->b_bcount);
38235653Slc152243 
38245653Slc152243 			epp->ep_bp->b_resid = epp->ep_bp->b_bcount - len;
38255653Slc152243 
38265653Slc152243 
38275653Slc152243 			switch (reqp->isoc_completion_reason) {
38285653Slc152243 			case USB_CR_OK:
38295653Slc152243 
38305653Slc152243 				epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
38315653Slc152243 
38325653Slc152243 				for (i = 0; i < reqp->isoc_pkts_count; i++) {
38335653Slc152243 					pktdesc[i].isoc_pkt_status =
38345653Slc152243 					    ugen_cr2lcstat(pktdesc[i].
38355653Slc152243 					    isoc_pkt_status);
38365653Slc152243 				}
38375653Slc152243 
38385653Slc152243 				/* save the status info */
38395653Slc152243 				bcopy(reqp->isoc_pkt_descr,
38405653Slc152243 				    info.isoc_pkt_descr,
38415653Slc152243 				    (sizeof (ugen_isoc_pkt_descr_t) *
38425653Slc152243 				    info.isoc_pkts_count));
38435653Slc152243 
38445653Slc152243 				break;
38455653Slc152243 			case USB_CR_PIPE_RESET:
38465653Slc152243 
38475653Slc152243 				break;
38485653Slc152243 			default:
38495653Slc152243 				epp->ep_lcmd_status =
38505653Slc152243 				    ugen_cr2lcstat(
38515653Slc152243 				    reqp->isoc_completion_reason);
38525653Slc152243 				bioerror(epp->ep_bp, EIO);
38535653Slc152243 			}
38545653Slc152243 		}
38555653Slc152243 		epp->ep_done++;
38565653Slc152243 		cv_signal(&epp->ep_wait_cv);
38575653Slc152243 		mutex_exit(&epp->ep_mutex);
38585653Slc152243 	}
38595653Slc152243 
38605653Slc152243 	usb_free_isoc_req(reqp);
38615653Slc152243 }
38625653Slc152243 
38635653Slc152243 
38645653Slc152243 /*
38650Sstevel@tonic-gate  * Endpoint status node management
38660Sstevel@tonic-gate  *
38670Sstevel@tonic-gate  * open/close an endpoint status node.
38680Sstevel@tonic-gate  *
38690Sstevel@tonic-gate  * Return values: errno
38700Sstevel@tonic-gate  */
38710Sstevel@tonic-gate static int
ugen_eps_open(ugen_state_t * ugenp,dev_t dev,int flag)38720Sstevel@tonic-gate ugen_eps_open(ugen_state_t *ugenp, dev_t dev, int flag)
38730Sstevel@tonic-gate {
38740Sstevel@tonic-gate 	ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
38750Sstevel@tonic-gate 	int rval = EBUSY;
38760Sstevel@tonic-gate 
38770Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
38780Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_STAT, ugenp->ug_log_hdl,
38790Sstevel@tonic-gate 	    "ugen_eps_open: dev=0x%lx flag=0x%x state=0x%x",
38800Sstevel@tonic-gate 	    dev, flag, epp->ep_state);
38810Sstevel@tonic-gate 
38820Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_ACTIVE);
38830Sstevel@tonic-gate 
38840Sstevel@tonic-gate 	/* only one open at the time */
38850Sstevel@tonic-gate 	if ((epp->ep_state & UGEN_EP_STATE_STAT_OPEN) == 0) {
38860Sstevel@tonic-gate 		epp->ep_state |= UGEN_EP_STATE_STAT_OPEN;
38870Sstevel@tonic-gate 		epp->ep_stat_oflag = flag;
38880Sstevel@tonic-gate 		rval = 0;
38890Sstevel@tonic-gate 	}
38900Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
38910Sstevel@tonic-gate 
38920Sstevel@tonic-gate 	return (rval);
38930Sstevel@tonic-gate }
38940Sstevel@tonic-gate 
38950Sstevel@tonic-gate 
38960Sstevel@tonic-gate /*
38970Sstevel@tonic-gate  * close endpoint status
38980Sstevel@tonic-gate  */
38990Sstevel@tonic-gate static void
ugen_eps_close(ugen_state_t * ugenp,dev_t dev,int flag)39000Sstevel@tonic-gate ugen_eps_close(ugen_state_t *ugenp, dev_t dev, int flag)
39010Sstevel@tonic-gate {
39020Sstevel@tonic-gate 	ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
39030Sstevel@tonic-gate 
39040Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
39050Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_STAT, ugenp->ug_log_hdl,
39060Sstevel@tonic-gate 	    "ugen_eps_close: dev=0x%lx flag=0x%x state=0x%x",
39070Sstevel@tonic-gate 	    dev, flag, epp->ep_state);
39080Sstevel@tonic-gate 
39090Sstevel@tonic-gate 	epp->ep_state &= ~(UGEN_EP_STATE_STAT_OPEN |
39105653Slc152243 	    UGEN_EP_STATE_INTR_IN_POLL_PENDING |
39115653Slc152243 	    UGEN_EP_STATE_ISOC_IN_POLL_PENDING);
39120Sstevel@tonic-gate 	epp->ep_one_xfer = B_FALSE;
39130Sstevel@tonic-gate 
39140Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_STAT, ugenp->ug_log_hdl,
39150Sstevel@tonic-gate 	    "ugen_eps_close: state=0x%x", epp->ep_state);
39160Sstevel@tonic-gate 
39170Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_ACTIVE);
39180Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
39190Sstevel@tonic-gate }
39200Sstevel@tonic-gate 
39210Sstevel@tonic-gate 
39220Sstevel@tonic-gate /*
39230Sstevel@tonic-gate  * return status info
39240Sstevel@tonic-gate  *
39250Sstevel@tonic-gate  * Return values: errno
39260Sstevel@tonic-gate  */
39270Sstevel@tonic-gate static int
ugen_eps_req(ugen_state_t * ugenp,struct buf * bp)39280Sstevel@tonic-gate ugen_eps_req(ugen_state_t *ugenp, struct buf *bp)
39290Sstevel@tonic-gate {
39300Sstevel@tonic-gate 	ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, bp->b_edev)];
39310Sstevel@tonic-gate 
39320Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
39330Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
39340Sstevel@tonic-gate 	    "ugen_eps_req: bp=0x%p lcmd_status=0x%x bcount=%lu",
39356898Sfb209375 	    (void *)bp, epp->ep_lcmd_status, bp->b_bcount);
39360Sstevel@tonic-gate 
39370Sstevel@tonic-gate 	if (bp->b_flags & B_READ) {
39380Sstevel@tonic-gate 		int len = min(sizeof (epp->ep_lcmd_status), bp->b_bcount);
39390Sstevel@tonic-gate 		if (len) {
39400Sstevel@tonic-gate 			bcopy(&epp->ep_lcmd_status, bp->b_un.b_addr, len);
39410Sstevel@tonic-gate 		}
39420Sstevel@tonic-gate 		bp->b_resid = bp->b_bcount - len;
39430Sstevel@tonic-gate 	} else {
39440Sstevel@tonic-gate 		USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
39450Sstevel@tonic-gate 		    "ugen_eps_req: control=0x%x",
39460Sstevel@tonic-gate 		    *((char *)(bp->b_un.b_addr)));
39470Sstevel@tonic-gate 
39480Sstevel@tonic-gate 		if (epp->ep_state & UGEN_EP_STATE_XFER_OPEN) {
39490Sstevel@tonic-gate 			USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
39500Sstevel@tonic-gate 			    "ugen_eps_req: cannot change one xfer mode if "
39510Sstevel@tonic-gate 			    "endpoint is open");
39520Sstevel@tonic-gate 
39530Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
39540Sstevel@tonic-gate 
39550Sstevel@tonic-gate 			return (EINVAL);
39560Sstevel@tonic-gate 		}
39570Sstevel@tonic-gate 
39580Sstevel@tonic-gate 		if ((epp->ep_descr.bmAttributes & USB_EP_ATTR_INTR) &&
39590Sstevel@tonic-gate 		    (epp->ep_descr.bEndpointAddress & USB_EP_DIR_IN)) {
39600Sstevel@tonic-gate 			epp->ep_one_xfer = (*((char *)(bp->b_un.b_addr)) &
39610Sstevel@tonic-gate 			    USB_EP_INTR_ONE_XFER) ? B_TRUE : B_FALSE;
39620Sstevel@tonic-gate 		} else {
39630Sstevel@tonic-gate 			USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
39640Sstevel@tonic-gate 			    "ugen_eps_req: not an interrupt endpoint");
39650Sstevel@tonic-gate 
39660Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
39670Sstevel@tonic-gate 
39680Sstevel@tonic-gate 			return (EINVAL);
39690Sstevel@tonic-gate 		}
39700Sstevel@tonic-gate 
39710Sstevel@tonic-gate 		bp->b_resid = bp->b_bcount - 1;
39720Sstevel@tonic-gate 	}
39730Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
39740Sstevel@tonic-gate 
39750Sstevel@tonic-gate 	return (0);
39760Sstevel@tonic-gate }
39770Sstevel@tonic-gate 
39780Sstevel@tonic-gate 
39790Sstevel@tonic-gate /*
39800Sstevel@tonic-gate  * device status node management
39810Sstevel@tonic-gate  */
39820Sstevel@tonic-gate static int
ugen_ds_init(ugen_state_t * ugenp)39830Sstevel@tonic-gate ugen_ds_init(ugen_state_t *ugenp)
39840Sstevel@tonic-gate {
39850Sstevel@tonic-gate 	cv_init(&ugenp->ug_ds.dev_wait_cv, NULL, CV_DRIVER, NULL);
39860Sstevel@tonic-gate 
39870Sstevel@tonic-gate 	/* Create devstat minor node for this instance */
39880Sstevel@tonic-gate 	if (ugen_ds_minor_nodes_create(ugenp) != USB_SUCCESS) {
39890Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
39900Sstevel@tonic-gate 		    "ugen_create_dev_stat_minor_nodes failed");
39910Sstevel@tonic-gate 
39920Sstevel@tonic-gate 		return (USB_FAILURE);
39930Sstevel@tonic-gate 	}
39940Sstevel@tonic-gate 
39950Sstevel@tonic-gate 
39960Sstevel@tonic-gate 	return (USB_SUCCESS);
39970Sstevel@tonic-gate }
39980Sstevel@tonic-gate 
39990Sstevel@tonic-gate 
40000Sstevel@tonic-gate static void
ugen_ds_destroy(ugen_state_t * ugenp)40010Sstevel@tonic-gate ugen_ds_destroy(ugen_state_t *ugenp)
40020Sstevel@tonic-gate {
40030Sstevel@tonic-gate 	cv_destroy(&ugenp->ug_ds.dev_wait_cv);
40040Sstevel@tonic-gate }
40050Sstevel@tonic-gate 
40060Sstevel@tonic-gate 
40070Sstevel@tonic-gate /*
40080Sstevel@tonic-gate  * open devstat minor node
40090Sstevel@tonic-gate  *
40100Sstevel@tonic-gate  * Return values: errno
40110Sstevel@tonic-gate  */
40120Sstevel@tonic-gate static int
ugen_ds_open(ugen_state_t * ugenp,dev_t dev,int flag)40130Sstevel@tonic-gate ugen_ds_open(ugen_state_t *ugenp, dev_t dev, int flag)
40140Sstevel@tonic-gate {
40150Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
40160Sstevel@tonic-gate 	    "ugen_ds_open: dev=0x%lx flag=0x%x", dev, flag);
40170Sstevel@tonic-gate 
40180Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
40190Sstevel@tonic-gate 	if ((ugenp->ug_ds.dev_stat & UGEN_DEV_STATUS_ACTIVE) == 0) {
40200Sstevel@tonic-gate 		/*
40210Sstevel@tonic-gate 		 * first read on device node should return status
40220Sstevel@tonic-gate 		 */
40230Sstevel@tonic-gate 		ugenp->ug_ds.dev_stat |= UGEN_DEV_STATUS_CHANGED |
40245653Slc152243 		    UGEN_DEV_STATUS_ACTIVE;
40250Sstevel@tonic-gate 		ugenp->ug_ds.dev_oflag = flag;
40260Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
40270Sstevel@tonic-gate 
40280Sstevel@tonic-gate 		return (0);
40290Sstevel@tonic-gate 	} else {
40300Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
40310Sstevel@tonic-gate 
40320Sstevel@tonic-gate 		return (EBUSY);
40330Sstevel@tonic-gate 	}
40340Sstevel@tonic-gate }
40350Sstevel@tonic-gate 
40360Sstevel@tonic-gate 
40370Sstevel@tonic-gate static void
ugen_ds_close(ugen_state_t * ugenp,dev_t dev,int flag)40380Sstevel@tonic-gate ugen_ds_close(ugen_state_t *ugenp, dev_t dev, int flag)
40390Sstevel@tonic-gate {
40400Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
40410Sstevel@tonic-gate 	    "ugen_ds_close: dev=0x%lx flag=0x%x", dev, flag);
40420Sstevel@tonic-gate 
40430Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
40440Sstevel@tonic-gate 	ugenp->ug_ds.dev_stat = UGEN_DEV_STATUS_INACTIVE;
40450Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
40460Sstevel@tonic-gate }
40470Sstevel@tonic-gate 
40480Sstevel@tonic-gate 
40490Sstevel@tonic-gate /*
40500Sstevel@tonic-gate  * request for devstat
40510Sstevel@tonic-gate  *
40520Sstevel@tonic-gate  * Return values: errno
40530Sstevel@tonic-gate  */
40540Sstevel@tonic-gate static int
ugen_ds_req(ugen_state_t * ugenp,struct buf * bp)40550Sstevel@tonic-gate ugen_ds_req(ugen_state_t *ugenp, struct buf *bp)
40560Sstevel@tonic-gate {
40570Sstevel@tonic-gate 	int len = min(sizeof (ugenp->ug_ds.dev_state), bp->b_bcount);
40580Sstevel@tonic-gate 
40590Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
40606898Sfb209375 	    "ugen_ds_req: bp=0x%p", (void *)bp);
40610Sstevel@tonic-gate 
40620Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
40630Sstevel@tonic-gate 	if ((ugenp->ug_ds.dev_oflag & (FNDELAY | FNONBLOCK)) == 0) {
40640Sstevel@tonic-gate 		while ((ugenp->ug_ds.dev_stat &
40650Sstevel@tonic-gate 		    UGEN_DEV_STATUS_CHANGED) == 0) {
40660Sstevel@tonic-gate 			if (cv_wait_sig(&ugenp->ug_ds.dev_wait_cv,
40670Sstevel@tonic-gate 			    &ugenp->ug_mutex) <= 0) {
40680Sstevel@tonic-gate 				mutex_exit(&ugenp->ug_mutex);
40690Sstevel@tonic-gate 
40700Sstevel@tonic-gate 				return (EINTR);
40710Sstevel@tonic-gate 			}
40720Sstevel@tonic-gate 		}
40730Sstevel@tonic-gate 	} else if ((ugenp->ug_ds.dev_stat & UGEN_DEV_STATUS_CHANGED) ==
40740Sstevel@tonic-gate 	    0) {
40750Sstevel@tonic-gate 		bp->b_resid = bp->b_bcount;
40760Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
40770Sstevel@tonic-gate 
40780Sstevel@tonic-gate 		return (0);
40790Sstevel@tonic-gate 	}
40800Sstevel@tonic-gate 
40810Sstevel@tonic-gate 	ugenp->ug_ds.dev_stat &= ~UGEN_DEV_STATUS_CHANGED;
40820Sstevel@tonic-gate 	switch (ugenp->ug_dev_state) {
40830Sstevel@tonic-gate 	case USB_DEV_ONLINE:
40840Sstevel@tonic-gate 		ugenp->ug_ds.dev_state = USB_DEV_STAT_ONLINE;
40850Sstevel@tonic-gate 
40860Sstevel@tonic-gate 		break;
40870Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
40880Sstevel@tonic-gate 		ugenp->ug_ds.dev_state = USB_DEV_STAT_DISCONNECTED;
40890Sstevel@tonic-gate 
40900Sstevel@tonic-gate 		break;
40910Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
40920Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RESUME:
40930Sstevel@tonic-gate 		ugenp->ug_ds.dev_state = USB_DEV_STAT_RESUMED;
40940Sstevel@tonic-gate 
40950Sstevel@tonic-gate 		break;
40960Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
40970Sstevel@tonic-gate 	default:
40980Sstevel@tonic-gate 		ugenp->ug_ds.dev_state = USB_DEV_STAT_UNAVAILABLE;
40990Sstevel@tonic-gate 
41000Sstevel@tonic-gate 		break;
41010Sstevel@tonic-gate 	}
41020Sstevel@tonic-gate 
41030Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
41040Sstevel@tonic-gate 	    "ugen_ds_req: dev_state=0x%x dev_stat=0x%x",
41050Sstevel@tonic-gate 	    ugenp->ug_dev_state, ugenp->ug_ds.dev_stat);
41060Sstevel@tonic-gate 
41070Sstevel@tonic-gate 	bcopy(&ugenp->ug_ds.dev_state, bp->b_un.b_addr, len);
41080Sstevel@tonic-gate 	bp->b_resid = bp->b_bcount - len;
41090Sstevel@tonic-gate 
41100Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
41110Sstevel@tonic-gate 
41120Sstevel@tonic-gate 	return (0);
41130Sstevel@tonic-gate }
41140Sstevel@tonic-gate 
41150Sstevel@tonic-gate 
41160Sstevel@tonic-gate static void
ugen_ds_change(ugen_state_t * ugenp)41170Sstevel@tonic-gate ugen_ds_change(ugen_state_t *ugenp)
41180Sstevel@tonic-gate {
41190Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
41200Sstevel@tonic-gate 	    "ugen_ds_change:");
41210Sstevel@tonic-gate 
41220Sstevel@tonic-gate 	ugenp->ug_ds.dev_stat |= UGEN_DEV_STATUS_CHANGED;
41230Sstevel@tonic-gate 	cv_signal(&ugenp->ug_ds.dev_wait_cv);
41240Sstevel@tonic-gate }
41250Sstevel@tonic-gate 
41260Sstevel@tonic-gate 
41270Sstevel@tonic-gate /*
41280Sstevel@tonic-gate  * poll management
41290Sstevel@tonic-gate  */
41300Sstevel@tonic-gate static void
ugen_ds_poll_wakeup(ugen_state_t * ugenp)41310Sstevel@tonic-gate ugen_ds_poll_wakeup(ugen_state_t *ugenp)
41320Sstevel@tonic-gate {
41330Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
41340Sstevel@tonic-gate 	    "ugen_ds_poll_wakeup:");
41350Sstevel@tonic-gate 
41360Sstevel@tonic-gate 	if (ugenp->ug_ds.dev_stat & UGEN_DEV_STATUS_POLL_PENDING) {
41370Sstevel@tonic-gate 		struct pollhead *phpp = &ugenp->ug_ds.dev_pollhead;
41380Sstevel@tonic-gate 		ugenp->ug_ds.dev_stat &= ~UGEN_DEV_STATUS_POLL_PENDING;
41390Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
41400Sstevel@tonic-gate 		pollwakeup(phpp, POLLIN);
41410Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
41420Sstevel@tonic-gate 	}
41430Sstevel@tonic-gate }
41440Sstevel@tonic-gate 
41450Sstevel@tonic-gate 
41460Sstevel@tonic-gate /*
41470Sstevel@tonic-gate  * minor node management:
41480Sstevel@tonic-gate  */
41490Sstevel@tonic-gate static int
ugen_ds_minor_nodes_create(ugen_state_t * ugenp)41500Sstevel@tonic-gate ugen_ds_minor_nodes_create(ugen_state_t *ugenp)
41510Sstevel@tonic-gate {
41520Sstevel@tonic-gate 	char	node_name[32];
41530Sstevel@tonic-gate 	int	vid = ugenp->ug_dev_data->dev_descr->idVendor;
41540Sstevel@tonic-gate 	int	pid = ugenp->ug_dev_data->dev_descr->idProduct;
41550Sstevel@tonic-gate 	minor_t	minor;
41560Sstevel@tonic-gate 	int	minor_index;
41570Sstevel@tonic-gate 	int	owns_device = (usb_owns_device(ugenp->ug_dip) ?
41585653Slc152243 	    UGEN_OWNS_DEVICE : 0);
41590Sstevel@tonic-gate 
41600Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
41610Sstevel@tonic-gate 	    "ugen_ds_minor_nodes_create: idx shift=%d inst shift=%d",
41620Sstevel@tonic-gate 	    UGEN_MINOR_IDX_SHIFT(ugenp),
41630Sstevel@tonic-gate 	    UGEN_MINOR_INSTANCE_SHIFT(ugenp));
41640Sstevel@tonic-gate 
41650Sstevel@tonic-gate 	if (ugenp->ug_instance >= UGEN_MINOR_INSTANCE_LIMIT(ugenp)) {
41660Sstevel@tonic-gate 		USB_DPRINTF_L0(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
41670Sstevel@tonic-gate 		    "instance number too high (%d)", ugenp->ug_instance);
41680Sstevel@tonic-gate 
41690Sstevel@tonic-gate 		return (USB_FAILURE);
41700Sstevel@tonic-gate 	}
41710Sstevel@tonic-gate 
41720Sstevel@tonic-gate 	/* create devstat minor node */
41730Sstevel@tonic-gate 	if (owns_device) {
41740Sstevel@tonic-gate 		(void) sprintf(node_name, "%x.%x.devstat", vid, pid);
41750Sstevel@tonic-gate 	} else {
41760Sstevel@tonic-gate 		(void) sprintf(node_name, "%x.%x.if%ddevstat", vid, pid,
41770Sstevel@tonic-gate 		    ugenp->ug_dev_data->dev_curr_if);
41780Sstevel@tonic-gate 	}
41790Sstevel@tonic-gate 
41800Sstevel@tonic-gate 	minor_index = ugen_minor_index_create(ugenp,
41810Sstevel@tonic-gate 	    (UGEN_MINOR_DEV_STAT_NODE | owns_device) <<
41820Sstevel@tonic-gate 	    UGEN_MINOR_IDX_SHIFT(ugenp));
41830Sstevel@tonic-gate 
41840Sstevel@tonic-gate 	if (minor_index < 0) {
41850Sstevel@tonic-gate 		USB_DPRINTF_L0(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
41860Sstevel@tonic-gate 		    "too many minor nodes");
41870Sstevel@tonic-gate 
41880Sstevel@tonic-gate 		return (USB_FAILURE);
41890Sstevel@tonic-gate 	}
41900Sstevel@tonic-gate 	minor = (minor_index << UGEN_MINOR_IDX_SHIFT(ugenp)) |
41910Sstevel@tonic-gate 	    ugenp->ug_instance << UGEN_MINOR_INSTANCE_SHIFT(ugenp);
41920Sstevel@tonic-gate 
41930Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
41940Sstevel@tonic-gate 	    "minor=0x%x minor_index=%d name=%s",
41950Sstevel@tonic-gate 	    minor, minor_index, node_name);
41960Sstevel@tonic-gate 
41970Sstevel@tonic-gate 	ASSERT(minor < L_MAXMIN);
41980Sstevel@tonic-gate 
41990Sstevel@tonic-gate 	if ((ddi_create_minor_node(ugenp->ug_dip, node_name,
42000Sstevel@tonic-gate 	    S_IFCHR, minor, DDI_NT_UGEN, 0)) != DDI_SUCCESS) {
42010Sstevel@tonic-gate 
42020Sstevel@tonic-gate 		return (USB_FAILURE);
42030Sstevel@tonic-gate 	}
42040Sstevel@tonic-gate 
42050Sstevel@tonic-gate 	ugen_store_devt(ugenp, minor);
42060Sstevel@tonic-gate 
42070Sstevel@tonic-gate 	return (USB_SUCCESS);
42080Sstevel@tonic-gate }
42090Sstevel@tonic-gate 
42100Sstevel@tonic-gate 
42110Sstevel@tonic-gate /*
42120Sstevel@tonic-gate  * utility functions:
42130Sstevel@tonic-gate  *
42140Sstevel@tonic-gate  * conversion from completion reason to  USB_LC_STAT_*
42150Sstevel@tonic-gate  */
42160Sstevel@tonic-gate static struct ugen_cr2lcstat_entry {
42170Sstevel@tonic-gate 	int	cr;
42180Sstevel@tonic-gate 	int	lcstat;
42190Sstevel@tonic-gate } ugen_cr2lcstat_table[] = {
42200Sstevel@tonic-gate 	{ USB_CR_OK,			USB_LC_STAT_NOERROR	},
42210Sstevel@tonic-gate 	{ USB_CR_CRC,			USB_LC_STAT_CRC		},
42220Sstevel@tonic-gate 	{ USB_CR_BITSTUFFING,		USB_LC_STAT_BITSTUFFING },
42230Sstevel@tonic-gate 	{ USB_CR_DATA_TOGGLE_MM,	USB_LC_STAT_DATA_TOGGLE_MM },
42240Sstevel@tonic-gate 	{ USB_CR_STALL,			USB_LC_STAT_STALL	},
42250Sstevel@tonic-gate 	{ USB_CR_DEV_NOT_RESP,		USB_LC_STAT_DEV_NOT_RESP },
42260Sstevel@tonic-gate 	{ USB_CR_PID_CHECKFAILURE,	USB_LC_STAT_PID_CHECKFAILURE },
42270Sstevel@tonic-gate 	{ USB_CR_UNEXP_PID,		USB_LC_STAT_UNEXP_PID	},
42280Sstevel@tonic-gate 	{ USB_CR_DATA_OVERRUN,		USB_LC_STAT_DATA_OVERRUN },
42290Sstevel@tonic-gate 	{ USB_CR_DATA_UNDERRUN,		USB_LC_STAT_DATA_UNDERRUN },
42300Sstevel@tonic-gate 	{ USB_CR_BUFFER_OVERRUN,	USB_LC_STAT_BUFFER_OVERRUN },
42310Sstevel@tonic-gate 	{ USB_CR_BUFFER_UNDERRUN,	USB_LC_STAT_BUFFER_UNDERRUN },
42320Sstevel@tonic-gate 	{ USB_CR_TIMEOUT,		USB_LC_STAT_TIMEOUT	},
42330Sstevel@tonic-gate 	{ USB_CR_NOT_ACCESSED,		USB_LC_STAT_NOT_ACCESSED },
42340Sstevel@tonic-gate 	{ USB_CR_NO_RESOURCES,		USB_LC_STAT_NO_BANDWIDTH },
42350Sstevel@tonic-gate 	{ USB_CR_UNSPECIFIED_ERR,	USB_LC_STAT_UNSPECIFIED_ERR },
42360Sstevel@tonic-gate 	{ USB_CR_STOPPED_POLLING,	USB_LC_STAT_HW_ERR	},
42370Sstevel@tonic-gate 	{ USB_CR_PIPE_CLOSING,		USB_LC_STAT_UNSPECIFIED_ERR	},
42380Sstevel@tonic-gate 	{ USB_CR_PIPE_RESET,		USB_LC_STAT_UNSPECIFIED_ERR	},
42390Sstevel@tonic-gate 	{ USB_CR_NOT_SUPPORTED,		USB_LC_STAT_UNSPECIFIED_ERR },
42400Sstevel@tonic-gate 	{ USB_CR_FLUSHED,		USB_LC_STAT_UNSPECIFIED_ERR }
42410Sstevel@tonic-gate };
42420Sstevel@tonic-gate 
42430Sstevel@tonic-gate #define	UGEN_CR2LCSTAT_TABLE_SIZE (sizeof (ugen_cr2lcstat_table) / \
42440Sstevel@tonic-gate 			sizeof (struct ugen_cr2lcstat_entry))
42450Sstevel@tonic-gate static int
ugen_cr2lcstat(int cr)42460Sstevel@tonic-gate ugen_cr2lcstat(int cr)
42470Sstevel@tonic-gate {
42480Sstevel@tonic-gate 	int i;
42490Sstevel@tonic-gate 
42500Sstevel@tonic-gate 	for (i = 0; i < UGEN_CR2LCSTAT_TABLE_SIZE; i++) {
42510Sstevel@tonic-gate 		if (ugen_cr2lcstat_table[i].cr == cr) {
42520Sstevel@tonic-gate 
42530Sstevel@tonic-gate 			return (ugen_cr2lcstat_table[i].lcstat);
42540Sstevel@tonic-gate 		}
42550Sstevel@tonic-gate 	}
42560Sstevel@tonic-gate 
42570Sstevel@tonic-gate 	return (USB_LC_STAT_UNSPECIFIED_ERR);
42580Sstevel@tonic-gate }
42590Sstevel@tonic-gate 
42600Sstevel@tonic-gate 
42610Sstevel@tonic-gate /*
42620Sstevel@tonic-gate  * create and lookup minor index
42630Sstevel@tonic-gate  */
42640Sstevel@tonic-gate static int
ugen_minor_index_create(ugen_state_t * ugenp,ugen_minor_t minor)42650Sstevel@tonic-gate ugen_minor_index_create(ugen_state_t *ugenp, ugen_minor_t minor)
42660Sstevel@tonic-gate {
42670Sstevel@tonic-gate 	int i;
42680Sstevel@tonic-gate 
42690Sstevel@tonic-gate 	/* check if already in the table */
42700Sstevel@tonic-gate 	for (i = 1; i < ugenp->ug_minor_node_table_index; i++) {
42710Sstevel@tonic-gate 		if (ugenp->ug_minor_node_table[i] == minor) {
42720Sstevel@tonic-gate 
42730Sstevel@tonic-gate 			return (-1);
42740Sstevel@tonic-gate 		}
42750Sstevel@tonic-gate 	}
42760Sstevel@tonic-gate 	if (ugenp->ug_minor_node_table_index <
42770Sstevel@tonic-gate 	    (ugenp->ug_minor_node_table_size/sizeof (ugen_minor_t))) {
42780Sstevel@tonic-gate 		ugenp->ug_minor_node_table[ugenp->
42795653Slc152243 		    ug_minor_node_table_index] = minor;
42800Sstevel@tonic-gate 
42810Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
42820Sstevel@tonic-gate 		    "ugen_minor_index_create: %d: 0x%lx",
42830Sstevel@tonic-gate 		    ugenp->ug_minor_node_table_index,
4284*7632SNick.Todd@Sun.COM 		    (unsigned long)minor);
42850Sstevel@tonic-gate 
42860Sstevel@tonic-gate 		return (ugenp->ug_minor_node_table_index++);
42870Sstevel@tonic-gate 	} else {
42880Sstevel@tonic-gate 
42890Sstevel@tonic-gate 		return (-1);
42900Sstevel@tonic-gate 	}
42910Sstevel@tonic-gate }
42920Sstevel@tonic-gate 
42930Sstevel@tonic-gate 
42940Sstevel@tonic-gate static ugen_minor_t
ugen_devt2minor(ugen_state_t * ugenp,dev_t dev)42950Sstevel@tonic-gate ugen_devt2minor(ugen_state_t *ugenp, dev_t dev)
42960Sstevel@tonic-gate {
42970Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
42986898Sfb209375 	    "ugen_devt2minor: minorindex=%lu, minor=0x%" PRIx64,
42990Sstevel@tonic-gate 	    UGEN_MINOR_GET_IDX(ugenp, dev),
43000Sstevel@tonic-gate 	    ugenp->ug_minor_node_table[UGEN_MINOR_GET_IDX(ugenp, dev)]);
43010Sstevel@tonic-gate 
43020Sstevel@tonic-gate 	ASSERT(UGEN_MINOR_GET_IDX(ugenp, dev) <
43035653Slc152243 	    ugenp->ug_minor_node_table_index);
43040Sstevel@tonic-gate 
43050Sstevel@tonic-gate 	return (ugenp->ug_minor_node_table[UGEN_MINOR_GET_IDX(ugenp, dev)]);
43060Sstevel@tonic-gate }
43070Sstevel@tonic-gate 
43080Sstevel@tonic-gate 
430970Sfrits static int
ugen_is_valid_minor_node(ugen_state_t * ugenp,dev_t dev)431070Sfrits ugen_is_valid_minor_node(ugen_state_t *ugenp, dev_t dev)
431170Sfrits {
431270Sfrits 	int idx = UGEN_MINOR_GET_IDX(ugenp, dev);
431370Sfrits 
431470Sfrits 	if ((idx < ugenp->ug_minor_node_table_index) &&
431570Sfrits 	    (idx > 0)) {
431670Sfrits 
431770Sfrits 		return (USB_SUCCESS);
431870Sfrits 	}
431970Sfrits 	USB_DPRINTF_L2(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
432070Sfrits 	    "ugen_is_valid_minor_node: invalid minorindex=%d", idx);
432170Sfrits 
432270Sfrits 	return (USB_FAILURE);
432370Sfrits }
432470Sfrits 
432570Sfrits 
43260Sstevel@tonic-gate static void
ugen_minor_node_table_create(ugen_state_t * ugenp)43270Sstevel@tonic-gate ugen_minor_node_table_create(ugen_state_t *ugenp)
43280Sstevel@tonic-gate {
43290Sstevel@tonic-gate 	size_t	size = sizeof (ugen_minor_t) * UGEN_MINOR_IDX_LIMIT(ugenp);
43300Sstevel@tonic-gate 
43310Sstevel@tonic-gate 	/* allocate the max table size needed, we reduce later */
43320Sstevel@tonic-gate 	ugenp->ug_minor_node_table = kmem_zalloc(size, KM_SLEEP);
43330Sstevel@tonic-gate 	ugenp->ug_minor_node_table_size = size;
43340Sstevel@tonic-gate 	ugenp->ug_minor_node_table_index = 1;
43350Sstevel@tonic-gate }
43360Sstevel@tonic-gate 
43370Sstevel@tonic-gate 
43380Sstevel@tonic-gate static void
ugen_minor_node_table_shrink(ugen_state_t * ugenp)43390Sstevel@tonic-gate ugen_minor_node_table_shrink(ugen_state_t *ugenp)
43400Sstevel@tonic-gate {
43410Sstevel@tonic-gate 	/* reduce the table size to save some memory */
43420Sstevel@tonic-gate 	if (ugenp->ug_minor_node_table_index < UGEN_MINOR_IDX_LIMIT(ugenp)) {
43430Sstevel@tonic-gate 		size_t newsize = sizeof (ugen_minor_t) *
43445653Slc152243 		    ugenp->ug_minor_node_table_index;
43450Sstevel@tonic-gate 		ugen_minor_t *buf = kmem_zalloc(newsize, KM_SLEEP);
43460Sstevel@tonic-gate 
43470Sstevel@tonic-gate 		bcopy(ugenp->ug_minor_node_table, buf, newsize);
43480Sstevel@tonic-gate 		kmem_free(ugenp->ug_minor_node_table,
43495653Slc152243 		    ugenp->ug_minor_node_table_size);
43500Sstevel@tonic-gate 		ugenp->ug_minor_node_table = buf;
43510Sstevel@tonic-gate 		ugenp->ug_minor_node_table_size = newsize;
43520Sstevel@tonic-gate 	}
43530Sstevel@tonic-gate }
43540Sstevel@tonic-gate 
43550Sstevel@tonic-gate 
43560Sstevel@tonic-gate static void
ugen_minor_node_table_destroy(ugen_state_t * ugenp)43570Sstevel@tonic-gate ugen_minor_node_table_destroy(ugen_state_t *ugenp)
43580Sstevel@tonic-gate {
43590Sstevel@tonic-gate 	if (ugenp->ug_minor_node_table) {
43600Sstevel@tonic-gate 		kmem_free(ugenp->ug_minor_node_table,
43615653Slc152243 		    ugenp->ug_minor_node_table_size);
43620Sstevel@tonic-gate 	}
43630Sstevel@tonic-gate }
43640Sstevel@tonic-gate 
43650Sstevel@tonic-gate 
43660Sstevel@tonic-gate static void
ugen_check_mask(uint_t mask,uint_t * shift,uint_t * limit)43670Sstevel@tonic-gate ugen_check_mask(uint_t mask, uint_t *shift, uint_t *limit)
43680Sstevel@tonic-gate {
43690Sstevel@tonic-gate 	uint_t i, j;
43700Sstevel@tonic-gate 
43710Sstevel@tonic-gate 	for (i = 0; i < UGEN_MINOR_NODE_SIZE; i++) {
43720Sstevel@tonic-gate 		if ((1 << i)  & mask) {
43730Sstevel@tonic-gate 
43740Sstevel@tonic-gate 			break;
43750Sstevel@tonic-gate 		}
43760Sstevel@tonic-gate 	}
43770Sstevel@tonic-gate 
43780Sstevel@tonic-gate 	for (j = i; j < UGEN_MINOR_NODE_SIZE; j++) {
43790Sstevel@tonic-gate 		if (((1 << j) & mask) == 0) {
43800Sstevel@tonic-gate 
43810Sstevel@tonic-gate 			break;
43820Sstevel@tonic-gate 		}
43830Sstevel@tonic-gate 	}
43840Sstevel@tonic-gate 
43850Sstevel@tonic-gate 	*limit = (i == j) ? 0 : 1 << (j - i);
43860Sstevel@tonic-gate 	*shift = i;
43870Sstevel@tonic-gate }
43880Sstevel@tonic-gate 
43890Sstevel@tonic-gate 
43900Sstevel@tonic-gate 
43910Sstevel@tonic-gate /*
43920Sstevel@tonic-gate  * power management:
43930Sstevel@tonic-gate  *
43940Sstevel@tonic-gate  * ugen_pm_init:
43950Sstevel@tonic-gate  *	Initialize power management and remote wakeup functionality.
43960Sstevel@tonic-gate  *	No mutex is necessary in this function as it's called only by attach.
43970Sstevel@tonic-gate  */
43980Sstevel@tonic-gate static void
ugen_pm_init(ugen_state_t * ugenp)43990Sstevel@tonic-gate ugen_pm_init(ugen_state_t *ugenp)
44000Sstevel@tonic-gate {
44010Sstevel@tonic-gate 	dev_info_t	*dip = ugenp->ug_dip;
44020Sstevel@tonic-gate 	ugen_power_t	*ugenpm;
44030Sstevel@tonic-gate 
44040Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_PM, ugenp->ug_log_hdl,
44050Sstevel@tonic-gate 	    "ugen_pm_init:");
44060Sstevel@tonic-gate 
44070Sstevel@tonic-gate 	/* Allocate the state structure */
44080Sstevel@tonic-gate 	ugenpm = kmem_zalloc(sizeof (ugen_power_t), KM_SLEEP);
44090Sstevel@tonic-gate 
44100Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
44110Sstevel@tonic-gate 	ugenp->ug_pm = ugenpm;
44120Sstevel@tonic-gate 	ugenpm->pwr_wakeup_enabled = B_FALSE;
44130Sstevel@tonic-gate 	ugenpm->pwr_current = USB_DEV_OS_FULL_PWR;
44140Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
44150Sstevel@tonic-gate 
44160Sstevel@tonic-gate 	/*
44170Sstevel@tonic-gate 	 * If remote wakeup is not available you may not want to do
44180Sstevel@tonic-gate 	 * power management.
44190Sstevel@tonic-gate 	 */
44200Sstevel@tonic-gate 	if (ugen_enable_pm || usb_handle_remote_wakeup(dip,
44210Sstevel@tonic-gate 	    USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) {
44220Sstevel@tonic-gate 		if (usb_create_pm_components(dip,
44230Sstevel@tonic-gate 		    &ugenpm->pwr_states) == USB_SUCCESS) {
44240Sstevel@tonic-gate 			USB_DPRINTF_L4(UGEN_PRINT_PM,
44250Sstevel@tonic-gate 			    ugenp->ug_log_hdl,
44260Sstevel@tonic-gate 			    "ugen_pm_init: "
44270Sstevel@tonic-gate 			    "created PM components");
44280Sstevel@tonic-gate 
44290Sstevel@tonic-gate 			mutex_enter(&ugenp->ug_mutex);
44300Sstevel@tonic-gate 			ugenpm->pwr_wakeup_enabled = B_TRUE;
44310Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
44320Sstevel@tonic-gate 
44330Sstevel@tonic-gate 			if (pm_raise_power(dip, 0,
44340Sstevel@tonic-gate 			    USB_DEV_OS_FULL_PWR) != DDI_SUCCESS) {
44350Sstevel@tonic-gate 				USB_DPRINTF_L2(UGEN_PRINT_PM,
44360Sstevel@tonic-gate 				    ugenp->ug_log_hdl,
44370Sstevel@tonic-gate 				    "ugen_pm_init: "
44380Sstevel@tonic-gate 				    "raising power failed");
44390Sstevel@tonic-gate 			}
44400Sstevel@tonic-gate 		} else {
44410Sstevel@tonic-gate 			USB_DPRINTF_L2(UGEN_PRINT_PM,
44420Sstevel@tonic-gate 			    ugenp->ug_log_hdl,
44430Sstevel@tonic-gate 			    "ugen_pm_init: "
44440Sstevel@tonic-gate 			    "create_pm_comps failed");
44450Sstevel@tonic-gate 		}
44460Sstevel@tonic-gate 	} else {
44470Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_PM,
44480Sstevel@tonic-gate 		    ugenp->ug_log_hdl, "ugen_pm_init: "
44490Sstevel@tonic-gate 		    "failure enabling remote wakeup");
44500Sstevel@tonic-gate 	}
44510Sstevel@tonic-gate 
44520Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_PM, ugenp->ug_log_hdl,
44530Sstevel@tonic-gate 	    "ugen_pm_init: end");
44540Sstevel@tonic-gate }
44550Sstevel@tonic-gate 
44560Sstevel@tonic-gate 
44570Sstevel@tonic-gate /*
44580Sstevel@tonic-gate  * ugen_pm_destroy:
44590Sstevel@tonic-gate  *	Shut down and destroy power management and remote wakeup functionality.
44600Sstevel@tonic-gate  */
44610Sstevel@tonic-gate static void
ugen_pm_destroy(ugen_state_t * ugenp)44620Sstevel@tonic-gate ugen_pm_destroy(ugen_state_t *ugenp)
44630Sstevel@tonic-gate {
44640Sstevel@tonic-gate 	dev_info_t *dip = ugenp->ug_dip;
44650Sstevel@tonic-gate 
44660Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_PM, ugenp->ug_log_hdl,
44670Sstevel@tonic-gate 	    "ugen_pm_destroy:");
44680Sstevel@tonic-gate 
44690Sstevel@tonic-gate 	if (ugenp->ug_pm) {
44700Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
44710Sstevel@tonic-gate 		ugen_pm_busy_component(ugenp);
44720Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
44730Sstevel@tonic-gate 
44740Sstevel@tonic-gate 		if ((ugenp->ug_pm->pwr_wakeup_enabled) &&
44750Sstevel@tonic-gate 		    (ugenp->ug_dev_state != USB_DEV_DISCONNECTED)) {
44760Sstevel@tonic-gate 			int rval;
44770Sstevel@tonic-gate 
44780Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
44790Sstevel@tonic-gate 			(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
44800Sstevel@tonic-gate 
44810Sstevel@tonic-gate 			if ((rval = usb_handle_remote_wakeup(dip,
44820Sstevel@tonic-gate 			    USB_REMOTE_WAKEUP_DISABLE)) != USB_SUCCESS) {
44830Sstevel@tonic-gate 				USB_DPRINTF_L4(UGEN_PRINT_PM,
44840Sstevel@tonic-gate 				    ugenp->ug_log_hdl, "ugen_pm_destroy: "
44850Sstevel@tonic-gate 				    "disabling rmt wakeup: rval=%d", rval);
44860Sstevel@tonic-gate 			}
44870Sstevel@tonic-gate 			/*
44880Sstevel@tonic-gate 			 * Since remote wakeup is disabled now,
44890Sstevel@tonic-gate 			 * no one can raise power
44900Sstevel@tonic-gate 			 * and get to device once power is lowered here.
44910Sstevel@tonic-gate 			 */
44920Sstevel@tonic-gate 		} else {
44930Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
44940Sstevel@tonic-gate 		}
44950Sstevel@tonic-gate 		(void) pm_lower_power(dip, 0, USB_DEV_OS_PWR_OFF);
44960Sstevel@tonic-gate 		ugen_pm_idle_component(ugenp);
44970Sstevel@tonic-gate 
44980Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
44990Sstevel@tonic-gate 		kmem_free(ugenp->ug_pm, sizeof (ugen_power_t));
45000Sstevel@tonic-gate 		ugenp->ug_pm = NULL;
45010Sstevel@tonic-gate 	}
45020Sstevel@tonic-gate }
45030Sstevel@tonic-gate 
45040Sstevel@tonic-gate 
45050Sstevel@tonic-gate /*
45060Sstevel@tonic-gate  * ugen_power :
45070Sstevel@tonic-gate  *	Power entry point, the workhorse behind pm_raise_power, pm_lower_power,
45080Sstevel@tonic-gate  *	usb_req_raise_power and usb_req_lower_power.
45090Sstevel@tonic-gate  */
45100Sstevel@tonic-gate /*ARGSUSED*/
45110Sstevel@tonic-gate int
usb_ugen_power(usb_ugen_hdl_t usb_ugen_hdl,int comp,int level)45120Sstevel@tonic-gate usb_ugen_power(usb_ugen_hdl_t usb_ugen_hdl, int comp, int level)
45130Sstevel@tonic-gate {
45140Sstevel@tonic-gate 	ugen_power_t		*pm;
45150Sstevel@tonic-gate 	int			rval = USB_FAILURE;
45160Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
45175653Slc152243 	    (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
45180Sstevel@tonic-gate 	ugen_state_t		*ugenp;
45190Sstevel@tonic-gate 	dev_info_t		*dip;
45200Sstevel@tonic-gate 
45210Sstevel@tonic-gate 	if (usb_ugen_hdl == NULL) {
45220Sstevel@tonic-gate 
45230Sstevel@tonic-gate 		return (USB_FAILURE);
45240Sstevel@tonic-gate 	}
45250Sstevel@tonic-gate 
45260Sstevel@tonic-gate 	ugenp = usb_ugen_hdl_impl->hdl_ugenp;
45270Sstevel@tonic-gate 	dip = ugenp->ug_dip;
45280Sstevel@tonic-gate 
45290Sstevel@tonic-gate 	if (ugenp->ug_pm == NULL) {
45300Sstevel@tonic-gate 
45310Sstevel@tonic-gate 		return (USB_SUCCESS);
45320Sstevel@tonic-gate 	}
45330Sstevel@tonic-gate 
45340Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_PM, ugenp->ug_log_hdl,
45350Sstevel@tonic-gate 	    "usb_ugen_power: level=%d", level);
45360Sstevel@tonic-gate 
45370Sstevel@tonic-gate 	(void) usb_serialize_access(ugenp->ug_ser_cookie,
45385653Slc152243 	    USB_WAIT, 0);
45390Sstevel@tonic-gate 	/*
45400Sstevel@tonic-gate 	 * If we are disconnected/suspended, return success. Note that if we
45410Sstevel@tonic-gate 	 * return failure, bringing down the system will hang when
45420Sstevel@tonic-gate 	 * PM tries to power up all devices
45430Sstevel@tonic-gate 	 */
45440Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
45450Sstevel@tonic-gate 	switch (ugenp->ug_dev_state) {
45460Sstevel@tonic-gate 	case USB_DEV_ONLINE:
45470Sstevel@tonic-gate 
45480Sstevel@tonic-gate 		break;
45490Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
45500Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
45510Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RESUME:
45520Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
45530Sstevel@tonic-gate 	default:
45540Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_PM, ugenp->ug_log_hdl,
45550Sstevel@tonic-gate 		    "ugen_power: disconnected/suspended "
45560Sstevel@tonic-gate 		    "dev_state=%d", ugenp->ug_dev_state);
45570Sstevel@tonic-gate 		rval = USB_SUCCESS;
45580Sstevel@tonic-gate 
45590Sstevel@tonic-gate 		goto done;
45600Sstevel@tonic-gate 	}
45610Sstevel@tonic-gate 
45620Sstevel@tonic-gate 	pm = ugenp->ug_pm;
45630Sstevel@tonic-gate 
45640Sstevel@tonic-gate 	/* Check if we are transitioning to a legal power level */
45650Sstevel@tonic-gate 	if (USB_DEV_PWRSTATE_OK(pm->pwr_states, level)) {
45660Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_PM, ugenp->ug_log_hdl,
45670Sstevel@tonic-gate 		    "ugen_power: illegal power level=%d "
45680Sstevel@tonic-gate 		    "pwr_states: 0x%x", level, pm->pwr_states);
45690Sstevel@tonic-gate 
45700Sstevel@tonic-gate 		goto done;
45710Sstevel@tonic-gate 	}
45720Sstevel@tonic-gate 
45730Sstevel@tonic-gate 	switch (level) {
45740Sstevel@tonic-gate 	case USB_DEV_OS_PWR_OFF :
45750Sstevel@tonic-gate 		switch (ugenp->ug_dev_state) {
45760Sstevel@tonic-gate 		case USB_DEV_ONLINE:
45770Sstevel@tonic-gate 			/* Deny the powerdown request if the device is busy */
45780Sstevel@tonic-gate 			if (ugenp->ug_pm->pwr_busy != 0) {
45790Sstevel@tonic-gate 
45800Sstevel@tonic-gate 				break;
45810Sstevel@tonic-gate 			}
45820Sstevel@tonic-gate 			ASSERT(ugenp->ug_open_count == 0);
45830Sstevel@tonic-gate 			ASSERT(ugenp->ug_pending_cmds == 0);
45840Sstevel@tonic-gate 			ugenp->ug_pm->pwr_current = USB_DEV_OS_PWR_OFF;
45850Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
45860Sstevel@tonic-gate 
45870Sstevel@tonic-gate 			/* Issue USB D3 command to the device here */
45880Sstevel@tonic-gate 			rval = usb_set_device_pwrlvl3(dip);
45890Sstevel@tonic-gate 			mutex_enter(&ugenp->ug_mutex);
45900Sstevel@tonic-gate 
45910Sstevel@tonic-gate 			break;
45920Sstevel@tonic-gate 		default:
45930Sstevel@tonic-gate 			rval = USB_SUCCESS;
45940Sstevel@tonic-gate 
45950Sstevel@tonic-gate 			break;
45960Sstevel@tonic-gate 		}
45970Sstevel@tonic-gate 		break;
45980Sstevel@tonic-gate 	case USB_DEV_OS_FULL_PWR :
45990Sstevel@tonic-gate 		/*
46000Sstevel@tonic-gate 		 * PM framework tries to put us in full power during system
46010Sstevel@tonic-gate 		 * shutdown.
46020Sstevel@tonic-gate 		 */
46030Sstevel@tonic-gate 		switch (ugenp->ug_dev_state) {
46040Sstevel@tonic-gate 		case USB_UGEN_DEV_UNAVAILABLE_RESUME:
46050Sstevel@tonic-gate 		case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
46060Sstevel@tonic-gate 
46070Sstevel@tonic-gate 			break;
46080Sstevel@tonic-gate 		default:
46090Sstevel@tonic-gate 			ugenp->ug_dev_state = USB_DEV_ONLINE;
46100Sstevel@tonic-gate 
46110Sstevel@tonic-gate 			/* wakeup devstat reads and polls */
46120Sstevel@tonic-gate 			ugen_ds_change(ugenp);
46130Sstevel@tonic-gate 			ugen_ds_poll_wakeup(ugenp);
46140Sstevel@tonic-gate 
46150Sstevel@tonic-gate 			break;
46160Sstevel@tonic-gate 		}
46170Sstevel@tonic-gate 		ugenp->ug_pm->pwr_current = USB_DEV_OS_FULL_PWR;
46180Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
46190Sstevel@tonic-gate 		rval = usb_set_device_pwrlvl0(dip);
46200Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
46210Sstevel@tonic-gate 
46220Sstevel@tonic-gate 		break;
46230Sstevel@tonic-gate 	default:
46240Sstevel@tonic-gate 		/* Levels 1 and 2 are not supported to keep it simple. */
46250Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_PM, ugenp->ug_log_hdl,
46260Sstevel@tonic-gate 		    "ugen_power: power level %d not supported", level);
46270Sstevel@tonic-gate 
46280Sstevel@tonic-gate 		break;
46290Sstevel@tonic-gate 	}
46300Sstevel@tonic-gate done:
46310Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
46320Sstevel@tonic-gate 	usb_release_access(ugenp->ug_ser_cookie);
46330Sstevel@tonic-gate 
46340Sstevel@tonic-gate 	return (rval);
46350Sstevel@tonic-gate }
46360Sstevel@tonic-gate 
46370Sstevel@tonic-gate 
46380Sstevel@tonic-gate static void
ugen_pm_busy_component(ugen_state_t * ugen_statep)46390Sstevel@tonic-gate ugen_pm_busy_component(ugen_state_t *ugen_statep)
46400Sstevel@tonic-gate {
46410Sstevel@tonic-gate 	ASSERT(!mutex_owned(&ugen_statep->ug_mutex));
46420Sstevel@tonic-gate 
46430Sstevel@tonic-gate 	if (ugen_statep->ug_pm != NULL) {
46440Sstevel@tonic-gate 		mutex_enter(&ugen_statep->ug_mutex);
46450Sstevel@tonic-gate 		ugen_statep->ug_pm->pwr_busy++;
46460Sstevel@tonic-gate 
46470Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_PM, ugen_statep->ug_log_hdl,
46480Sstevel@tonic-gate 		    "ugen_pm_busy_component: %d", ugen_statep->ug_pm->pwr_busy);
46490Sstevel@tonic-gate 
46500Sstevel@tonic-gate 		mutex_exit(&ugen_statep->ug_mutex);
46510Sstevel@tonic-gate 		if (pm_busy_component(ugen_statep->ug_dip, 0) != DDI_SUCCESS) {
46520Sstevel@tonic-gate 			mutex_enter(&ugen_statep->ug_mutex);
46530Sstevel@tonic-gate 			ugen_statep->ug_pm->pwr_busy--;
46540Sstevel@tonic-gate 
46550Sstevel@tonic-gate 			USB_DPRINTF_L2(UGEN_PRINT_PM, ugen_statep->ug_log_hdl,
46560Sstevel@tonic-gate 			    "ugen_pm_busy_component failed: %d",
46570Sstevel@tonic-gate 			    ugen_statep->ug_pm->pwr_busy);
46580Sstevel@tonic-gate 
46590Sstevel@tonic-gate 			mutex_exit(&ugen_statep->ug_mutex);
46600Sstevel@tonic-gate 		}
46610Sstevel@tonic-gate 	}
46620Sstevel@tonic-gate }
46630Sstevel@tonic-gate 
46640Sstevel@tonic-gate 
46650Sstevel@tonic-gate static void
ugen_pm_idle_component(ugen_state_t * ugen_statep)46660Sstevel@tonic-gate ugen_pm_idle_component(ugen_state_t *ugen_statep)
46670Sstevel@tonic-gate {
46680Sstevel@tonic-gate 	ASSERT(!mutex_owned(&ugen_statep->ug_mutex));
46690Sstevel@tonic-gate 
46700Sstevel@tonic-gate 	if (ugen_statep->ug_pm != NULL) {
46710Sstevel@tonic-gate 		if (pm_idle_component(ugen_statep->ug_dip, 0) == DDI_SUCCESS) {
46720Sstevel@tonic-gate 			mutex_enter(&ugen_statep->ug_mutex);
46730Sstevel@tonic-gate 			ASSERT(ugen_statep->ug_pm->pwr_busy > 0);
46740Sstevel@tonic-gate 			ugen_statep->ug_pm->pwr_busy--;
46750Sstevel@tonic-gate 
46760Sstevel@tonic-gate 			USB_DPRINTF_L4(UGEN_PRINT_PM, ugen_statep->ug_log_hdl,
46770Sstevel@tonic-gate 			    "ugen_pm_idle_component: %d",
46780Sstevel@tonic-gate 			    ugen_statep->ug_pm->pwr_busy);
46790Sstevel@tonic-gate 
46800Sstevel@tonic-gate 			mutex_exit(&ugen_statep->ug_mutex);
46810Sstevel@tonic-gate 		}
46820Sstevel@tonic-gate 	}
46830Sstevel@tonic-gate }
46840Sstevel@tonic-gate 
46850Sstevel@tonic-gate 
46860Sstevel@tonic-gate /*
46870Sstevel@tonic-gate  * devt lookup support
46880Sstevel@tonic-gate  *	In ugen_strategy and ugen_minphys, we only have the devt and need
46890Sstevel@tonic-gate  *	the ugen_state pointer. Since we don't know instance mask, we can't
46900Sstevel@tonic-gate  *	easily derive a softstate pointer. Therefore, we use a list
46910Sstevel@tonic-gate  */
46920Sstevel@tonic-gate static void
ugen_store_devt(ugen_state_t * ugenp,minor_t minor)46930Sstevel@tonic-gate ugen_store_devt(ugen_state_t *ugenp, minor_t minor)
46940Sstevel@tonic-gate {
46950Sstevel@tonic-gate 	ugen_devt_list_entry_t *e = kmem_zalloc(
46965653Slc152243 	    sizeof (ugen_devt_list_entry_t), KM_SLEEP);
46970Sstevel@tonic-gate 	ugen_devt_list_entry_t *t;
46980Sstevel@tonic-gate 
46990Sstevel@tonic-gate 	mutex_enter(&ugen_devt_list_mutex);
47000Sstevel@tonic-gate 	e->list_dev = makedevice(ddi_driver_major(ugenp->ug_dip), minor);
47010Sstevel@tonic-gate 	e->list_state = ugenp;
47020Sstevel@tonic-gate 
47030Sstevel@tonic-gate 	t = ugen_devt_list.list_next;
47040Sstevel@tonic-gate 
47050Sstevel@tonic-gate 	/* check if the entry is already in the list */
47060Sstevel@tonic-gate 	while (t) {
47070Sstevel@tonic-gate 		ASSERT(t->list_dev != e->list_dev);
47080Sstevel@tonic-gate 		t = t->list_next;
47090Sstevel@tonic-gate 	}
47100Sstevel@tonic-gate 
47110Sstevel@tonic-gate 	/* add to the head of the list */
47120Sstevel@tonic-gate 	e->list_next = ugen_devt_list.list_next;
47130Sstevel@tonic-gate 	if (ugen_devt_list.list_next) {
47140Sstevel@tonic-gate 		ugen_devt_list.list_next->list_prev = e;
47150Sstevel@tonic-gate 	}
47160Sstevel@tonic-gate 	ugen_devt_list.list_next = e;
47170Sstevel@tonic-gate 	mutex_exit(&ugen_devt_list_mutex);
47180Sstevel@tonic-gate }
47190Sstevel@tonic-gate 
47200Sstevel@tonic-gate 
47210Sstevel@tonic-gate static ugen_state_t *
ugen_devt2state(dev_t dev)47220Sstevel@tonic-gate ugen_devt2state(dev_t dev)
47230Sstevel@tonic-gate {
47240Sstevel@tonic-gate 	ugen_devt_list_entry_t *t;
47250Sstevel@tonic-gate 	ugen_state_t	*ugenp = NULL;
47260Sstevel@tonic-gate 	int		index, count;
47270Sstevel@tonic-gate 
47280Sstevel@tonic-gate 	mutex_enter(&ugen_devt_list_mutex);
47290Sstevel@tonic-gate 
47300Sstevel@tonic-gate 	for (index = ugen_devt_cache_index, count = 0;
47310Sstevel@tonic-gate 	    count < UGEN_DEVT_CACHE_SIZE; count++) {
47320Sstevel@tonic-gate 		if (ugen_devt_cache[index].cache_dev == dev) {
47330Sstevel@tonic-gate 			ugen_devt_cache[index].cache_hit++;
47340Sstevel@tonic-gate 			ugenp = ugen_devt_cache[index].cache_state;
47350Sstevel@tonic-gate 
47360Sstevel@tonic-gate 			mutex_exit(&ugen_devt_list_mutex);
47370Sstevel@tonic-gate 
47380Sstevel@tonic-gate 			return (ugenp);
47390Sstevel@tonic-gate 		}
47400Sstevel@tonic-gate 		index++;
47410Sstevel@tonic-gate 		index %= UGEN_DEVT_CACHE_SIZE;
47420Sstevel@tonic-gate 	}
47430Sstevel@tonic-gate 
47440Sstevel@tonic-gate 	t = ugen_devt_list.list_next;
47450Sstevel@tonic-gate 
47460Sstevel@tonic-gate 	while (t) {
47470Sstevel@tonic-gate 		if (t->list_dev == dev) {
47480Sstevel@tonic-gate 			ugenp = t->list_state;
47490Sstevel@tonic-gate 			ugen_devt_cache_index++;
47500Sstevel@tonic-gate 			ugen_devt_cache_index %= UGEN_DEVT_CACHE_SIZE;
47510Sstevel@tonic-gate 			ugen_devt_cache[ugen_devt_cache_index].cache_dev = dev;
47520Sstevel@tonic-gate 			ugen_devt_cache[ugen_devt_cache_index].cache_state =
47535653Slc152243 			    ugenp;
47540Sstevel@tonic-gate 			mutex_exit(&ugen_devt_list_mutex);
47550Sstevel@tonic-gate 
47560Sstevel@tonic-gate 			return (ugenp);
47570Sstevel@tonic-gate 		}
47580Sstevel@tonic-gate 		t = t->list_next;
47590Sstevel@tonic-gate 	}
47600Sstevel@tonic-gate 	mutex_exit(&ugen_devt_list_mutex);
47610Sstevel@tonic-gate 
47620Sstevel@tonic-gate 	return (ugenp);
47630Sstevel@tonic-gate }
47640Sstevel@tonic-gate 
47650Sstevel@tonic-gate 
47660Sstevel@tonic-gate static void
ugen_free_devt(ugen_state_t * ugenp)47670Sstevel@tonic-gate ugen_free_devt(ugen_state_t *ugenp)
47680Sstevel@tonic-gate {
47690Sstevel@tonic-gate 	ugen_devt_list_entry_t *e, *next, *prev;
47700Sstevel@tonic-gate 	major_t		major = ddi_driver_major(ugenp->ug_dip);
47710Sstevel@tonic-gate 	int		instance = ddi_get_instance(ugenp->ug_dip);
47720Sstevel@tonic-gate 
47730Sstevel@tonic-gate 	mutex_enter(&ugen_devt_list_mutex);
47740Sstevel@tonic-gate 	prev = &ugen_devt_list;
47750Sstevel@tonic-gate 	for (e = prev->list_next; e != 0; e = next) {
47760Sstevel@tonic-gate 		int i = (getminor(e->list_dev) &
47775653Slc152243 		    ugenp->ug_hdl->hdl_minor_node_instance_mask) >>
47785653Slc152243 		    ugenp->ug_hdl->hdl_minor_node_instance_shift;
47790Sstevel@tonic-gate 		int m = getmajor(e->list_dev);
47800Sstevel@tonic-gate 
47810Sstevel@tonic-gate 		next = e->list_next;
47820Sstevel@tonic-gate 
47830Sstevel@tonic-gate 		if ((i == instance) && (m == major)) {
47840Sstevel@tonic-gate 			prev->list_next = e->list_next;
47850Sstevel@tonic-gate 			if (e->list_next) {
47860Sstevel@tonic-gate 				e->list_next->list_prev = prev;
47870Sstevel@tonic-gate 			}
47880Sstevel@tonic-gate 			kmem_free(e, sizeof (ugen_devt_list_entry_t));
47890Sstevel@tonic-gate 		} else {
47900Sstevel@tonic-gate 			prev = e;
47910Sstevel@tonic-gate 		}
47920Sstevel@tonic-gate 	}
47930Sstevel@tonic-gate 
47940Sstevel@tonic-gate 	bzero(ugen_devt_cache, sizeof (ugen_devt_cache));
47950Sstevel@tonic-gate 	ugen_devt_cache_index = 0;
47960Sstevel@tonic-gate 	mutex_exit(&ugen_devt_list_mutex);
47970Sstevel@tonic-gate }
4798