1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  *
22*7c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
23*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
24*7c478bd9Sstevel@tonic-gate  */
25*7c478bd9Sstevel@tonic-gate 
26*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*7c478bd9Sstevel@tonic-gate 
28*7c478bd9Sstevel@tonic-gate /*
29*7c478bd9Sstevel@tonic-gate  * UGEN: USB Generic Driver support code
30*7c478bd9Sstevel@tonic-gate  *
31*7c478bd9Sstevel@tonic-gate  * This code provides entry points called by the ugen driver or other
32*7c478bd9Sstevel@tonic-gate  * drivers that want to export a ugen interface
33*7c478bd9Sstevel@tonic-gate  *
34*7c478bd9Sstevel@tonic-gate  * The "Universal Generic Driver"  (UGEN) for USB devices provides interfaces
35*7c478bd9Sstevel@tonic-gate  * to  talk to	USB  devices.  This is	very  useful for  Point of Sale sale
36*7c478bd9Sstevel@tonic-gate  * devices and other simple  devices like  USB	scanner, USB palm  pilot.
37*7c478bd9Sstevel@tonic-gate  * The UGEN provides a system call interface to USB  devices  enabling
38*7c478bd9Sstevel@tonic-gate  * a USB device vendor to  write an  application for his
39*7c478bd9Sstevel@tonic-gate  * device instead of  writing a driver. This facilitates the vendor to write
40*7c478bd9Sstevel@tonic-gate  * device management s/w quickly in userland.
41*7c478bd9Sstevel@tonic-gate  *
42*7c478bd9Sstevel@tonic-gate  * UGEN supports read/write/poll entry points. An application can be written
43*7c478bd9Sstevel@tonic-gate  * using  read/write/aioread/aiowrite/poll  system calls to communicate
44*7c478bd9Sstevel@tonic-gate  * with the device.
45*7c478bd9Sstevel@tonic-gate  *
46*7c478bd9Sstevel@tonic-gate  * XXX Theory of Operations
47*7c478bd9Sstevel@tonic-gate  */
48*7c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usbai_version.h>
49*7c478bd9Sstevel@tonic-gate #include <sys/usb/usba.h>
50*7c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
51*7c478bd9Sstevel@tonic-gate 
52*7c478bd9Sstevel@tonic-gate #include "sys/usb/clients/ugen/usb_ugen.h"
53*7c478bd9Sstevel@tonic-gate #include "sys/usb/usba/usba_ugen.h"
54*7c478bd9Sstevel@tonic-gate #include "sys/usb/usba/usba_ugend.h"
55*7c478bd9Sstevel@tonic-gate 
56*7c478bd9Sstevel@tonic-gate /* Debugging information */
57*7c478bd9Sstevel@tonic-gate static uint_t ugen_errmask		= (uint_t)UGEN_PRINT_ALL;
58*7c478bd9Sstevel@tonic-gate static uint_t ugen_errlevel		= USB_LOG_L4;
59*7c478bd9Sstevel@tonic-gate static uint_t ugen_instance_debug	= (uint_t)-1;
60*7c478bd9Sstevel@tonic-gate 
61*7c478bd9Sstevel@tonic-gate /* default endpoint descriptor */
62*7c478bd9Sstevel@tonic-gate static usb_ep_descr_t  ugen_default_ep_descr =
63*7c478bd9Sstevel@tonic-gate 	{7, 5, 0, USB_EP_ATTR_CONTROL, 8, 0};
64*7c478bd9Sstevel@tonic-gate 
65*7c478bd9Sstevel@tonic-gate /* tunables */
66*7c478bd9Sstevel@tonic-gate static int	ugen_busy_loop		= 60;	/* secs */
67*7c478bd9Sstevel@tonic-gate static int	ugen_ctrl_timeout	= 10;
68*7c478bd9Sstevel@tonic-gate static int	ugen_bulk_timeout	= 10;
69*7c478bd9Sstevel@tonic-gate static int	ugen_intr_timeout	= 10;
70*7c478bd9Sstevel@tonic-gate static int	ugen_enable_pm		= 0;
71*7c478bd9Sstevel@tonic-gate 
72*7c478bd9Sstevel@tonic-gate 
73*7c478bd9Sstevel@tonic-gate /* local function prototypes */
74*7c478bd9Sstevel@tonic-gate static int	ugen_cleanup(ugen_state_t *);
75*7c478bd9Sstevel@tonic-gate static int	ugen_cpr_suspend(ugen_state_t *);
76*7c478bd9Sstevel@tonic-gate static void	ugen_cpr_resume(ugen_state_t *);
77*7c478bd9Sstevel@tonic-gate 
78*7c478bd9Sstevel@tonic-gate static void	ugen_restore_state(ugen_state_t *);
79*7c478bd9Sstevel@tonic-gate static int	ugen_check_open_flags(ugen_state_t *, dev_t, int);
80*7c478bd9Sstevel@tonic-gate static int	ugen_strategy(struct buf *);
81*7c478bd9Sstevel@tonic-gate static void	ugen_minphys(struct buf *);
82*7c478bd9Sstevel@tonic-gate 
83*7c478bd9Sstevel@tonic-gate static void	ugen_pm_init(ugen_state_t *);
84*7c478bd9Sstevel@tonic-gate static void	ugen_pm_destroy(ugen_state_t *);
85*7c478bd9Sstevel@tonic-gate static void	ugen_pm_busy_component(ugen_state_t *);
86*7c478bd9Sstevel@tonic-gate static void	ugen_pm_idle_component(ugen_state_t *);
87*7c478bd9Sstevel@tonic-gate 
88*7c478bd9Sstevel@tonic-gate /* endpoint xfer and status management */
89*7c478bd9Sstevel@tonic-gate static int	ugen_epxs_init(ugen_state_t *);
90*7c478bd9Sstevel@tonic-gate static void	ugen_epxs_destroy(ugen_state_t *);
91*7c478bd9Sstevel@tonic-gate static int	ugen_epxs_data_init(ugen_state_t *, usb_ep_data_t *,
92*7c478bd9Sstevel@tonic-gate 					uchar_t, uchar_t, uchar_t, uchar_t);
93*7c478bd9Sstevel@tonic-gate static void	ugen_epxs_data_destroy(ugen_state_t *, ugen_ep_t *);
94*7c478bd9Sstevel@tonic-gate static int	ugen_epxs_minor_nodes_create(ugen_state_t *,
95*7c478bd9Sstevel@tonic-gate 					usb_ep_descr_t *, uchar_t,
96*7c478bd9Sstevel@tonic-gate 					uchar_t, uchar_t, uchar_t);
97*7c478bd9Sstevel@tonic-gate static int	ugen_epxs_check_open_nodes(ugen_state_t *);
98*7c478bd9Sstevel@tonic-gate 
99*7c478bd9Sstevel@tonic-gate static int	ugen_epx_open(ugen_state_t *, dev_t, int);
100*7c478bd9Sstevel@tonic-gate static void	ugen_epx_close(ugen_state_t *, dev_t, int);
101*7c478bd9Sstevel@tonic-gate static void	ugen_epx_shutdown(ugen_state_t *);
102*7c478bd9Sstevel@tonic-gate 
103*7c478bd9Sstevel@tonic-gate static int	ugen_epx_open_pipe(ugen_state_t *, ugen_ep_t *, int);
104*7c478bd9Sstevel@tonic-gate static void	ugen_epx_close_pipe(ugen_state_t *, ugen_ep_t *);
105*7c478bd9Sstevel@tonic-gate 
106*7c478bd9Sstevel@tonic-gate static int	ugen_epx_req(ugen_state_t *, struct buf *);
107*7c478bd9Sstevel@tonic-gate static int	ugen_epx_ctrl_req(ugen_state_t *, ugen_ep_t *,
108*7c478bd9Sstevel@tonic-gate 					struct buf *, boolean_t *);
109*7c478bd9Sstevel@tonic-gate static void	ugen_epx_ctrl_req_cb(usb_pipe_handle_t, usb_ctrl_req_t *);
110*7c478bd9Sstevel@tonic-gate static int	ugen_epx_bulk_req(ugen_state_t *, ugen_ep_t *,
111*7c478bd9Sstevel@tonic-gate 					struct buf *, boolean_t *);
112*7c478bd9Sstevel@tonic-gate static void	ugen_epx_bulk_req_cb(usb_pipe_handle_t, usb_bulk_req_t *);
113*7c478bd9Sstevel@tonic-gate static int	ugen_epx_intr_IN_req(ugen_state_t *, ugen_ep_t *,
114*7c478bd9Sstevel@tonic-gate 					struct buf *, boolean_t *);
115*7c478bd9Sstevel@tonic-gate static int	ugen_epx_intr_IN_start_polling(ugen_state_t *, ugen_ep_t *);
116*7c478bd9Sstevel@tonic-gate static void	ugen_epx_intr_IN_stop_polling(ugen_state_t *, ugen_ep_t *);
117*7c478bd9Sstevel@tonic-gate static void	ugen_epx_intr_IN_req_cb(usb_pipe_handle_t, usb_intr_req_t *);
118*7c478bd9Sstevel@tonic-gate static int	ugen_epx_intr_OUT_req(ugen_state_t *, ugen_ep_t *,
119*7c478bd9Sstevel@tonic-gate 					struct buf *, boolean_t *);
120*7c478bd9Sstevel@tonic-gate static void	ugen_epx_intr_OUT_req_cb(usb_pipe_handle_t, usb_intr_req_t *);
121*7c478bd9Sstevel@tonic-gate 
122*7c478bd9Sstevel@tonic-gate static int	ugen_eps_open(ugen_state_t *, dev_t, int);
123*7c478bd9Sstevel@tonic-gate static void	ugen_eps_close(ugen_state_t *, dev_t, int);
124*7c478bd9Sstevel@tonic-gate static int	ugen_eps_req(ugen_state_t *, struct buf *);
125*7c478bd9Sstevel@tonic-gate static void	ugen_update_ep_descr(ugen_state_t *, ugen_ep_t *);
126*7c478bd9Sstevel@tonic-gate 
127*7c478bd9Sstevel@tonic-gate /* device status management */
128*7c478bd9Sstevel@tonic-gate static int	ugen_ds_init(ugen_state_t *);
129*7c478bd9Sstevel@tonic-gate static void	ugen_ds_destroy(ugen_state_t *);
130*7c478bd9Sstevel@tonic-gate static int	ugen_ds_open(ugen_state_t *, dev_t, int);
131*7c478bd9Sstevel@tonic-gate static void	ugen_ds_close(ugen_state_t *, dev_t, int);
132*7c478bd9Sstevel@tonic-gate static int	ugen_ds_req(ugen_state_t *, struct buf *);
133*7c478bd9Sstevel@tonic-gate static void	ugen_ds_change(ugen_state_t *);
134*7c478bd9Sstevel@tonic-gate static int	ugen_ds_minor_nodes_create(ugen_state_t *);
135*7c478bd9Sstevel@tonic-gate static void	ugen_ds_poll_wakeup(ugen_state_t *);
136*7c478bd9Sstevel@tonic-gate 
137*7c478bd9Sstevel@tonic-gate /* utility functions */
138*7c478bd9Sstevel@tonic-gate static int	ugen_minor_index_create(ugen_state_t *, ugen_minor_t);
139*7c478bd9Sstevel@tonic-gate static ugen_minor_t ugen_devt2minor(ugen_state_t *, dev_t);
140*7c478bd9Sstevel@tonic-gate static void	ugen_minor_node_table_create(ugen_state_t *);
141*7c478bd9Sstevel@tonic-gate static void	ugen_minor_node_table_destroy(ugen_state_t *);
142*7c478bd9Sstevel@tonic-gate static void	ugen_minor_node_table_shrink(ugen_state_t *);
143*7c478bd9Sstevel@tonic-gate static int	ugen_cr2lcstat(int);
144*7c478bd9Sstevel@tonic-gate static void	ugen_check_mask(uint_t, uint_t *, uint_t *);
145*7c478bd9Sstevel@tonic-gate 
146*7c478bd9Sstevel@tonic-gate static kmutex_t	ugen_devt_list_mutex;
147*7c478bd9Sstevel@tonic-gate static ugen_devt_list_entry_t ugen_devt_list;
148*7c478bd9Sstevel@tonic-gate static ugen_devt_cache_entry_t ugen_devt_cache[UGEN_DEVT_CACHE_SIZE];
149*7c478bd9Sstevel@tonic-gate static uint_t	ugen_devt_cache_index;
150*7c478bd9Sstevel@tonic-gate static void	ugen_store_devt(ugen_state_t *, minor_t);
151*7c478bd9Sstevel@tonic-gate static ugen_state_t *ugen_devt2state(dev_t);
152*7c478bd9Sstevel@tonic-gate static void	ugen_free_devt(ugen_state_t *);
153*7c478bd9Sstevel@tonic-gate 
154*7c478bd9Sstevel@tonic-gate /*
155*7c478bd9Sstevel@tonic-gate  * usb_ugen entry points
156*7c478bd9Sstevel@tonic-gate  *
157*7c478bd9Sstevel@tonic-gate  * usb_ugen_get_hdl:
158*7c478bd9Sstevel@tonic-gate  *	allocate and initialize handle
159*7c478bd9Sstevel@tonic-gate  */
160*7c478bd9Sstevel@tonic-gate usb_ugen_hdl_t
161*7c478bd9Sstevel@tonic-gate usb_ugen_get_hdl(dev_info_t *dip, usb_ugen_info_t *usb_ugen_info)
162*7c478bd9Sstevel@tonic-gate {
163*7c478bd9Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*hdl = kmem_zalloc(sizeof (*hdl), KM_SLEEP);
164*7c478bd9Sstevel@tonic-gate 	ugen_state_t		*ugenp = kmem_zalloc(sizeof (ugen_state_t),
165*7c478bd9Sstevel@tonic-gate 								KM_SLEEP);
166*7c478bd9Sstevel@tonic-gate 	uint_t			len, shift, limit;
167*7c478bd9Sstevel@tonic-gate 	int			rval;
168*7c478bd9Sstevel@tonic-gate 
169*7c478bd9Sstevel@tonic-gate 	hdl->hdl_ugenp = ugenp;
170*7c478bd9Sstevel@tonic-gate 
171*7c478bd9Sstevel@tonic-gate 	/* masks may not overlap */
172*7c478bd9Sstevel@tonic-gate 	if (usb_ugen_info->usb_ugen_minor_node_ugen_bits_mask &
173*7c478bd9Sstevel@tonic-gate 	    usb_ugen_info->usb_ugen_minor_node_instance_mask) {
174*7c478bd9Sstevel@tonic-gate 		usb_ugen_release_hdl((usb_ugen_hdl_t)hdl);
175*7c478bd9Sstevel@tonic-gate 
176*7c478bd9Sstevel@tonic-gate 		return (NULL);
177*7c478bd9Sstevel@tonic-gate 	}
178*7c478bd9Sstevel@tonic-gate 
179*7c478bd9Sstevel@tonic-gate 	if ((rval = usb_get_dev_data(dip, &ugenp->ug_dev_data,
180*7c478bd9Sstevel@tonic-gate 	    usb_owns_device(dip) ? USB_PARSE_LVL_ALL : USB_PARSE_LVL_IF,
181*7c478bd9Sstevel@tonic-gate 	    0)) != USB_SUCCESS) {
182*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
183*7c478bd9Sstevel@tonic-gate 		    "usb_ugen_attach: usb_get_dev_data failed, rval=%d", rval);
184*7c478bd9Sstevel@tonic-gate 
185*7c478bd9Sstevel@tonic-gate 		return (NULL);
186*7c478bd9Sstevel@tonic-gate 	}
187*7c478bd9Sstevel@tonic-gate 
188*7c478bd9Sstevel@tonic-gate 	/* Initialize state structure for this instance */
189*7c478bd9Sstevel@tonic-gate 	mutex_init(&ugenp->ug_mutex, NULL, MUTEX_DRIVER,
190*7c478bd9Sstevel@tonic-gate 				ugenp->ug_dev_data->dev_iblock_cookie);
191*7c478bd9Sstevel@tonic-gate 
192*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
193*7c478bd9Sstevel@tonic-gate 	ugenp->ug_dip		= dip;
194*7c478bd9Sstevel@tonic-gate 	ugenp->ug_instance	= ddi_get_instance(dip);
195*7c478bd9Sstevel@tonic-gate 	ugenp->ug_hdl		= hdl;
196*7c478bd9Sstevel@tonic-gate 
197*7c478bd9Sstevel@tonic-gate 	/* Allocate a log handle for debug/error messages */
198*7c478bd9Sstevel@tonic-gate 	if (strcmp(ddi_driver_name(dip), "ugen") != 0) {
199*7c478bd9Sstevel@tonic-gate 		char	*name;
200*7c478bd9Sstevel@tonic-gate 
201*7c478bd9Sstevel@tonic-gate 		len = strlen(ddi_driver_name(dip)) + sizeof ("_ugen") + 1;
202*7c478bd9Sstevel@tonic-gate 		name = kmem_alloc(len, KM_SLEEP);
203*7c478bd9Sstevel@tonic-gate 		(void) snprintf(name, len, "%s_ugen", ddi_driver_name(dip));
204*7c478bd9Sstevel@tonic-gate 
205*7c478bd9Sstevel@tonic-gate 		ugenp->ug_log_hdl = usb_alloc_log_hdl(dip, name, &ugen_errlevel,
206*7c478bd9Sstevel@tonic-gate 					&ugen_errmask, &ugen_instance_debug, 0);
207*7c478bd9Sstevel@tonic-gate 		hdl->hdl_log_name = name;
208*7c478bd9Sstevel@tonic-gate 		hdl->hdl_log_name_length = len;
209*7c478bd9Sstevel@tonic-gate 	} else {
210*7c478bd9Sstevel@tonic-gate 		ugenp->ug_log_hdl = usb_alloc_log_hdl(dip, "ugen",
211*7c478bd9Sstevel@tonic-gate 					&ugen_errlevel,
212*7c478bd9Sstevel@tonic-gate 					&ugen_errmask, &ugen_instance_debug, 0);
213*7c478bd9Sstevel@tonic-gate 	}
214*7c478bd9Sstevel@tonic-gate 
215*7c478bd9Sstevel@tonic-gate 	hdl->hdl_dip = dip;
216*7c478bd9Sstevel@tonic-gate 	hdl->hdl_flags = usb_ugen_info->usb_ugen_flags;
217*7c478bd9Sstevel@tonic-gate 
218*7c478bd9Sstevel@tonic-gate 	ugen_check_mask(usb_ugen_info->usb_ugen_minor_node_ugen_bits_mask,
219*7c478bd9Sstevel@tonic-gate 							&shift, &limit);
220*7c478bd9Sstevel@tonic-gate 	if (limit == 0) {
221*7c478bd9Sstevel@tonic-gate 		usb_ugen_release_hdl((usb_ugen_hdl_t)hdl);
222*7c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
223*7c478bd9Sstevel@tonic-gate 
224*7c478bd9Sstevel@tonic-gate 		return (NULL);
225*7c478bd9Sstevel@tonic-gate 	}
226*7c478bd9Sstevel@tonic-gate 	hdl->hdl_minor_node_ugen_bits_mask = usb_ugen_info->
227*7c478bd9Sstevel@tonic-gate 					usb_ugen_minor_node_ugen_bits_mask;
228*7c478bd9Sstevel@tonic-gate 	hdl->hdl_minor_node_ugen_bits_shift = shift;
229*7c478bd9Sstevel@tonic-gate 	hdl->hdl_minor_node_ugen_bits_limit = limit;
230*7c478bd9Sstevel@tonic-gate 
231*7c478bd9Sstevel@tonic-gate 	ugen_check_mask(usb_ugen_info->usb_ugen_minor_node_instance_mask,
232*7c478bd9Sstevel@tonic-gate 							&shift, &limit);
233*7c478bd9Sstevel@tonic-gate 	if (limit == 0) {
234*7c478bd9Sstevel@tonic-gate 		usb_ugen_release_hdl((usb_ugen_hdl_t)hdl);
235*7c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
236*7c478bd9Sstevel@tonic-gate 
237*7c478bd9Sstevel@tonic-gate 		return (NULL);
238*7c478bd9Sstevel@tonic-gate 	}
239*7c478bd9Sstevel@tonic-gate 
240*7c478bd9Sstevel@tonic-gate 	hdl->hdl_minor_node_instance_mask = usb_ugen_info->
241*7c478bd9Sstevel@tonic-gate 					usb_ugen_minor_node_instance_mask;
242*7c478bd9Sstevel@tonic-gate 	hdl->hdl_minor_node_instance_shift = shift;
243*7c478bd9Sstevel@tonic-gate 	hdl->hdl_minor_node_instance_limit = limit;
244*7c478bd9Sstevel@tonic-gate 
245*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
246*7c478bd9Sstevel@tonic-gate 	    "usb_ugen_get_hdl: instance shift=%d instance limit=%d",
247*7c478bd9Sstevel@tonic-gate 	    hdl->hdl_minor_node_instance_shift,
248*7c478bd9Sstevel@tonic-gate 	    hdl->hdl_minor_node_instance_limit);
249*7c478bd9Sstevel@tonic-gate 
250*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
251*7c478bd9Sstevel@tonic-gate 	    "usb_ugen_get_hdl: bits shift=%d bits limit=%d",
252*7c478bd9Sstevel@tonic-gate 	    hdl->hdl_minor_node_ugen_bits_shift,
253*7c478bd9Sstevel@tonic-gate 	    hdl->hdl_minor_node_ugen_bits_limit);
254*7c478bd9Sstevel@tonic-gate 
255*7c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
256*7c478bd9Sstevel@tonic-gate 
257*7c478bd9Sstevel@tonic-gate 	return ((usb_ugen_hdl_t)hdl);
258*7c478bd9Sstevel@tonic-gate }
259*7c478bd9Sstevel@tonic-gate 
260*7c478bd9Sstevel@tonic-gate 
261*7c478bd9Sstevel@tonic-gate /*
262*7c478bd9Sstevel@tonic-gate  * usb_ugen_release_hdl:
263*7c478bd9Sstevel@tonic-gate  *	deallocate a handle
264*7c478bd9Sstevel@tonic-gate  */
265*7c478bd9Sstevel@tonic-gate void
266*7c478bd9Sstevel@tonic-gate usb_ugen_release_hdl(usb_ugen_hdl_t usb_ugen_hdl)
267*7c478bd9Sstevel@tonic-gate {
268*7c478bd9Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
269*7c478bd9Sstevel@tonic-gate 				(usb_ugen_hdl_impl_t *)usb_ugen_hdl;
270*7c478bd9Sstevel@tonic-gate 
271*7c478bd9Sstevel@tonic-gate 	if (usb_ugen_hdl_impl) {
272*7c478bd9Sstevel@tonic-gate 		ugen_state_t *ugenp = usb_ugen_hdl_impl->hdl_ugenp;
273*7c478bd9Sstevel@tonic-gate 
274*7c478bd9Sstevel@tonic-gate 		if (ugenp) {
275*7c478bd9Sstevel@tonic-gate 			mutex_destroy(&ugenp->ug_mutex);
276*7c478bd9Sstevel@tonic-gate 			usb_free_log_hdl(ugenp->ug_log_hdl);
277*7c478bd9Sstevel@tonic-gate 			usb_free_dev_data(usb_ugen_hdl_impl->hdl_dip,
278*7c478bd9Sstevel@tonic-gate 				ugenp->ug_dev_data);
279*7c478bd9Sstevel@tonic-gate 			kmem_free(ugenp, sizeof (*ugenp));
280*7c478bd9Sstevel@tonic-gate 		}
281*7c478bd9Sstevel@tonic-gate 		if (usb_ugen_hdl_impl->hdl_log_name) {
282*7c478bd9Sstevel@tonic-gate 			kmem_free(usb_ugen_hdl_impl->hdl_log_name,
283*7c478bd9Sstevel@tonic-gate 				usb_ugen_hdl_impl->hdl_log_name_length);
284*7c478bd9Sstevel@tonic-gate 		}
285*7c478bd9Sstevel@tonic-gate 		kmem_free(usb_ugen_hdl_impl, sizeof (*usb_ugen_hdl_impl));
286*7c478bd9Sstevel@tonic-gate 	}
287*7c478bd9Sstevel@tonic-gate }
288*7c478bd9Sstevel@tonic-gate 
289*7c478bd9Sstevel@tonic-gate 
290*7c478bd9Sstevel@tonic-gate /*
291*7c478bd9Sstevel@tonic-gate  * usb_ugen_attach()
292*7c478bd9Sstevel@tonic-gate  */
293*7c478bd9Sstevel@tonic-gate int
294*7c478bd9Sstevel@tonic-gate usb_ugen_attach(usb_ugen_hdl_t usb_ugen_hdl, ddi_attach_cmd_t cmd)
295*7c478bd9Sstevel@tonic-gate {
296*7c478bd9Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
297*7c478bd9Sstevel@tonic-gate 				(usb_ugen_hdl_impl_t *)usb_ugen_hdl;
298*7c478bd9Sstevel@tonic-gate 	ugen_state_t		*ugenp;
299*7c478bd9Sstevel@tonic-gate 	dev_info_t		*dip;
300*7c478bd9Sstevel@tonic-gate 
301*7c478bd9Sstevel@tonic-gate 	if (usb_ugen_hdl == NULL) {
302*7c478bd9Sstevel@tonic-gate 
303*7c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
304*7c478bd9Sstevel@tonic-gate 	}
305*7c478bd9Sstevel@tonic-gate 
306*7c478bd9Sstevel@tonic-gate 	ugenp = usb_ugen_hdl_impl->hdl_ugenp;
307*7c478bd9Sstevel@tonic-gate 	dip = usb_ugen_hdl_impl->hdl_dip;
308*7c478bd9Sstevel@tonic-gate 
309*7c478bd9Sstevel@tonic-gate 
310*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
311*7c478bd9Sstevel@tonic-gate 	    "usb_ugen_attach: cmd=%d", cmd);
312*7c478bd9Sstevel@tonic-gate 
313*7c478bd9Sstevel@tonic-gate 	switch (cmd) {
314*7c478bd9Sstevel@tonic-gate 	case DDI_ATTACH:
315*7c478bd9Sstevel@tonic-gate 
316*7c478bd9Sstevel@tonic-gate 		break;
317*7c478bd9Sstevel@tonic-gate 	case DDI_RESUME:
318*7c478bd9Sstevel@tonic-gate 		ugen_cpr_resume(ugenp);
319*7c478bd9Sstevel@tonic-gate 
320*7c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
321*7c478bd9Sstevel@tonic-gate 	default:
322*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, NULL,
323*7c478bd9Sstevel@tonic-gate 		    "usb_ugen_attach: unknown command");
324*7c478bd9Sstevel@tonic-gate 
325*7c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
326*7c478bd9Sstevel@tonic-gate 	}
327*7c478bd9Sstevel@tonic-gate 
328*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
329*7c478bd9Sstevel@tonic-gate 	ugenp->ug_ser_cookie =
330*7c478bd9Sstevel@tonic-gate 	    usb_init_serialization(dip, USB_INIT_SER_CHECK_SAME_THREAD);
331*7c478bd9Sstevel@tonic-gate 	ugenp->ug_cleanup_flags |= UGEN_INIT_LOCKS;
332*7c478bd9Sstevel@tonic-gate 
333*7c478bd9Sstevel@tonic-gate 	/* Get maximum bulk transfer size supported by the HCD */
334*7c478bd9Sstevel@tonic-gate 	if (usb_pipe_get_max_bulk_transfer_size(dip,
335*7c478bd9Sstevel@tonic-gate 	    &ugenp->ug_max_bulk_xfer_sz) != USB_SUCCESS) {
336*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
337*7c478bd9Sstevel@tonic-gate 		    "usb_ugen_attach: Getting max bulk xfer sz failed");
338*7c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
339*7c478bd9Sstevel@tonic-gate 
340*7c478bd9Sstevel@tonic-gate 		goto fail;
341*7c478bd9Sstevel@tonic-gate 	}
342*7c478bd9Sstevel@tonic-gate 
343*7c478bd9Sstevel@tonic-gate 	/* table for mapping 48 bit minor codes to 9 bit index (for ugen) */
344*7c478bd9Sstevel@tonic-gate 	ugen_minor_node_table_create(ugenp);
345*7c478bd9Sstevel@tonic-gate 
346*7c478bd9Sstevel@tonic-gate 	/* prepare device status node handling */
347*7c478bd9Sstevel@tonic-gate 	if (ugen_ds_init(ugenp) != USB_SUCCESS) {
348*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
349*7c478bd9Sstevel@tonic-gate 		    "usb_ugen_attach: preparing dev status failed");
350*7c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
351*7c478bd9Sstevel@tonic-gate 
352*7c478bd9Sstevel@tonic-gate 		goto fail;
353*7c478bd9Sstevel@tonic-gate 	}
354*7c478bd9Sstevel@tonic-gate 
355*7c478bd9Sstevel@tonic-gate 	/* prepare all available xfer and status endpoints nodes */
356*7c478bd9Sstevel@tonic-gate 	if (ugen_epxs_init(ugenp) != USB_SUCCESS) {
357*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
358*7c478bd9Sstevel@tonic-gate 		    "usb_ugen_attach: preparing endpoints failed");
359*7c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
360*7c478bd9Sstevel@tonic-gate 
361*7c478bd9Sstevel@tonic-gate 		goto fail;
362*7c478bd9Sstevel@tonic-gate 	}
363*7c478bd9Sstevel@tonic-gate 
364*7c478bd9Sstevel@tonic-gate 	/* reduce table size if not all entries are used */
365*7c478bd9Sstevel@tonic-gate 	ugen_minor_node_table_shrink(ugenp);
366*7c478bd9Sstevel@tonic-gate 
367*7c478bd9Sstevel@tonic-gate 	/* we are ready to go */
368*7c478bd9Sstevel@tonic-gate 	ugenp->ug_dev_state = USB_DEV_ONLINE;
369*7c478bd9Sstevel@tonic-gate 
370*7c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
371*7c478bd9Sstevel@tonic-gate 
372*7c478bd9Sstevel@tonic-gate 	/* prepare PM */
373*7c478bd9Sstevel@tonic-gate 	if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
374*7c478bd9Sstevel@tonic-gate 		ugen_pm_init(ugenp);
375*7c478bd9Sstevel@tonic-gate 	}
376*7c478bd9Sstevel@tonic-gate 
377*7c478bd9Sstevel@tonic-gate 	/*
378*7c478bd9Sstevel@tonic-gate 	 * if ugen driver, kill all child nodes otherwise set cfg fails
379*7c478bd9Sstevel@tonic-gate 	 * if requested
380*7c478bd9Sstevel@tonic-gate 	 */
381*7c478bd9Sstevel@tonic-gate 	if (usb_owns_device(dip) &&
382*7c478bd9Sstevel@tonic-gate 	    (usb_ugen_hdl_impl->hdl_flags & USB_UGEN_REMOVE_CHILDREN)) {
383*7c478bd9Sstevel@tonic-gate 		dev_info_t *cdip;
384*7c478bd9Sstevel@tonic-gate 
385*7c478bd9Sstevel@tonic-gate 		/* save cfgidx so we can restore on detach */
386*7c478bd9Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
387*7c478bd9Sstevel@tonic-gate 		ugenp->ug_initial_cfgidx = usb_get_current_cfgidx(dip);
388*7c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
389*7c478bd9Sstevel@tonic-gate 
390*7c478bd9Sstevel@tonic-gate 		for (cdip = ddi_get_child(dip); cdip; ) {
391*7c478bd9Sstevel@tonic-gate 			dev_info_t *next = ddi_get_next_sibling(cdip);
392*7c478bd9Sstevel@tonic-gate 			(void) ddi_remove_child(cdip, 0);
393*7c478bd9Sstevel@tonic-gate 			cdip = next;
394*7c478bd9Sstevel@tonic-gate 		}
395*7c478bd9Sstevel@tonic-gate 	}
396*7c478bd9Sstevel@tonic-gate 
397*7c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
398*7c478bd9Sstevel@tonic-gate fail:
399*7c478bd9Sstevel@tonic-gate 	if (ugenp) {
400*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
401*7c478bd9Sstevel@tonic-gate 		    "attach fail");
402*7c478bd9Sstevel@tonic-gate 		(void) ugen_cleanup(ugenp);
403*7c478bd9Sstevel@tonic-gate 	}
404*7c478bd9Sstevel@tonic-gate 
405*7c478bd9Sstevel@tonic-gate 	return (DDI_FAILURE);
406*7c478bd9Sstevel@tonic-gate }
407*7c478bd9Sstevel@tonic-gate 
408*7c478bd9Sstevel@tonic-gate 
409*7c478bd9Sstevel@tonic-gate /*
410*7c478bd9Sstevel@tonic-gate  * usb_ugen_detach()
411*7c478bd9Sstevel@tonic-gate  */
412*7c478bd9Sstevel@tonic-gate int
413*7c478bd9Sstevel@tonic-gate usb_ugen_detach(usb_ugen_hdl_t usb_ugen_hdl, ddi_detach_cmd_t cmd)
414*7c478bd9Sstevel@tonic-gate {
415*7c478bd9Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
416*7c478bd9Sstevel@tonic-gate 				(usb_ugen_hdl_impl_t *)usb_ugen_hdl;
417*7c478bd9Sstevel@tonic-gate 	int			rval = USB_FAILURE;
418*7c478bd9Sstevel@tonic-gate 
419*7c478bd9Sstevel@tonic-gate 	if (usb_ugen_hdl) {
420*7c478bd9Sstevel@tonic-gate 		ugen_state_t *ugenp = usb_ugen_hdl_impl->hdl_ugenp;
421*7c478bd9Sstevel@tonic-gate 
422*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
423*7c478bd9Sstevel@tonic-gate 		    "usb_ugen_detach cmd %d", cmd);
424*7c478bd9Sstevel@tonic-gate 
425*7c478bd9Sstevel@tonic-gate 		switch (cmd) {
426*7c478bd9Sstevel@tonic-gate 		case DDI_DETACH:
427*7c478bd9Sstevel@tonic-gate 			rval = ugen_cleanup(ugenp);
428*7c478bd9Sstevel@tonic-gate 
429*7c478bd9Sstevel@tonic-gate 			break;
430*7c478bd9Sstevel@tonic-gate 		case DDI_SUSPEND:
431*7c478bd9Sstevel@tonic-gate 			rval = ugen_cpr_suspend(ugenp);
432*7c478bd9Sstevel@tonic-gate 
433*7c478bd9Sstevel@tonic-gate 			break;
434*7c478bd9Sstevel@tonic-gate 		default:
435*7c478bd9Sstevel@tonic-gate 
436*7c478bd9Sstevel@tonic-gate 			break;
437*7c478bd9Sstevel@tonic-gate 		}
438*7c478bd9Sstevel@tonic-gate 	}
439*7c478bd9Sstevel@tonic-gate 
440*7c478bd9Sstevel@tonic-gate 	return (rval);
441*7c478bd9Sstevel@tonic-gate }
442*7c478bd9Sstevel@tonic-gate 
443*7c478bd9Sstevel@tonic-gate 
444*7c478bd9Sstevel@tonic-gate /*
445*7c478bd9Sstevel@tonic-gate  * ugen_cleanup()
446*7c478bd9Sstevel@tonic-gate  */
447*7c478bd9Sstevel@tonic-gate static int
448*7c478bd9Sstevel@tonic-gate ugen_cleanup(ugen_state_t *ugenp)
449*7c478bd9Sstevel@tonic-gate {
450*7c478bd9Sstevel@tonic-gate 	dev_info_t *dip = ugenp->ug_dip;
451*7c478bd9Sstevel@tonic-gate 
452*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl, "ugen_cleanup");
453*7c478bd9Sstevel@tonic-gate 
454*7c478bd9Sstevel@tonic-gate 	if (ugenp->ug_cleanup_flags & UGEN_INIT_LOCKS) {
455*7c478bd9Sstevel@tonic-gate 
456*7c478bd9Sstevel@tonic-gate 		/* shutdown all endpoints */
457*7c478bd9Sstevel@tonic-gate 		ugen_epx_shutdown(ugenp);
458*7c478bd9Sstevel@tonic-gate 
459*7c478bd9Sstevel@tonic-gate 		/*
460*7c478bd9Sstevel@tonic-gate 		 * At this point, no new activity can be initiated.
461*7c478bd9Sstevel@tonic-gate 		 * The driver has disabled hotplug callbacks.
462*7c478bd9Sstevel@tonic-gate 		 * The Solaris framework has disabled
463*7c478bd9Sstevel@tonic-gate 		 * new opens on a device being detached, and does not
464*7c478bd9Sstevel@tonic-gate 		 * allow detaching an open device. PM should power
465*7c478bd9Sstevel@tonic-gate 		 * down while we are detaching
466*7c478bd9Sstevel@tonic-gate 		 *
467*7c478bd9Sstevel@tonic-gate 		 * The following ensures that any other driver
468*7c478bd9Sstevel@tonic-gate 		 * activity must have drained (paranoia)
469*7c478bd9Sstevel@tonic-gate 		 */
470*7c478bd9Sstevel@tonic-gate 		(void) usb_serialize_access(ugenp->ug_ser_cookie,
471*7c478bd9Sstevel@tonic-gate 							USB_WAIT, 0);
472*7c478bd9Sstevel@tonic-gate 		usb_release_access(ugenp->ug_ser_cookie);
473*7c478bd9Sstevel@tonic-gate 
474*7c478bd9Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
475*7c478bd9Sstevel@tonic-gate 		ASSERT(ugenp->ug_open_count == 0);
476*7c478bd9Sstevel@tonic-gate 		ASSERT(ugenp->ug_pending_cmds == 0);
477*7c478bd9Sstevel@tonic-gate 
478*7c478bd9Sstevel@tonic-gate 		/* dismantle in reverse order */
479*7c478bd9Sstevel@tonic-gate 		ugen_pm_destroy(ugenp);
480*7c478bd9Sstevel@tonic-gate 		ugen_epxs_destroy(ugenp);
481*7c478bd9Sstevel@tonic-gate 		ugen_ds_destroy(ugenp);
482*7c478bd9Sstevel@tonic-gate 		ugen_minor_node_table_destroy(ugenp);
483*7c478bd9Sstevel@tonic-gate 
484*7c478bd9Sstevel@tonic-gate 
485*7c478bd9Sstevel@tonic-gate 		/* restore to initial configuration */
486*7c478bd9Sstevel@tonic-gate 		if (usb_owns_device(dip) &&
487*7c478bd9Sstevel@tonic-gate 		    (ugenp->ug_dev_state != USB_DEV_DISCONNECTED)) {
488*7c478bd9Sstevel@tonic-gate 			int idx = ugenp->ug_initial_cfgidx;
489*7c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
490*7c478bd9Sstevel@tonic-gate 			(void) usb_set_cfg(dip, idx,
491*7c478bd9Sstevel@tonic-gate 			    USB_FLAGS_SLEEP, NULL, NULL);
492*7c478bd9Sstevel@tonic-gate 		} else {
493*7c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
494*7c478bd9Sstevel@tonic-gate 		}
495*7c478bd9Sstevel@tonic-gate 
496*7c478bd9Sstevel@tonic-gate 		usb_fini_serialization(ugenp->ug_ser_cookie);
497*7c478bd9Sstevel@tonic-gate 	}
498*7c478bd9Sstevel@tonic-gate 
499*7c478bd9Sstevel@tonic-gate 	ddi_prop_remove_all(dip);
500*7c478bd9Sstevel@tonic-gate 	ddi_remove_minor_node(dip, NULL);
501*7c478bd9Sstevel@tonic-gate 
502*7c478bd9Sstevel@tonic-gate 	ugen_free_devt(ugenp);
503*7c478bd9Sstevel@tonic-gate 
504*7c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
505*7c478bd9Sstevel@tonic-gate }
506*7c478bd9Sstevel@tonic-gate 
507*7c478bd9Sstevel@tonic-gate 
508*7c478bd9Sstevel@tonic-gate /*
509*7c478bd9Sstevel@tonic-gate  * ugen_cpr_suspend
510*7c478bd9Sstevel@tonic-gate  */
511*7c478bd9Sstevel@tonic-gate static int
512*7c478bd9Sstevel@tonic-gate ugen_cpr_suspend(ugen_state_t *ugenp)
513*7c478bd9Sstevel@tonic-gate {
514*7c478bd9Sstevel@tonic-gate 	int		rval = USB_FAILURE;
515*7c478bd9Sstevel@tonic-gate 	int		i;
516*7c478bd9Sstevel@tonic-gate 	int		prev_state;
517*7c478bd9Sstevel@tonic-gate 
518*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CPR, ugenp->ug_log_hdl,
519*7c478bd9Sstevel@tonic-gate 	    "ugen_cpr_suspend:");
520*7c478bd9Sstevel@tonic-gate 
521*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
522*7c478bd9Sstevel@tonic-gate 	switch (ugenp->ug_dev_state) {
523*7c478bd9Sstevel@tonic-gate 	case USB_DEV_ONLINE:
524*7c478bd9Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
525*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_CPR, ugenp->ug_log_hdl,
526*7c478bd9Sstevel@tonic-gate 		    "ugen_cpr_suspend:");
527*7c478bd9Sstevel@tonic-gate 
528*7c478bd9Sstevel@tonic-gate 		prev_state = ugenp->ug_dev_state;
529*7c478bd9Sstevel@tonic-gate 		ugenp->ug_dev_state = USB_DEV_SUSPENDED;
530*7c478bd9Sstevel@tonic-gate 
531*7c478bd9Sstevel@tonic-gate 		if (ugenp->ug_open_count) {
532*7c478bd9Sstevel@tonic-gate 			/* drain outstanding cmds */
533*7c478bd9Sstevel@tonic-gate 			for (i = 0; i < ugen_busy_loop; i++) {
534*7c478bd9Sstevel@tonic-gate 				if (ugenp->ug_pending_cmds == 0) {
535*7c478bd9Sstevel@tonic-gate 
536*7c478bd9Sstevel@tonic-gate 					break;
537*7c478bd9Sstevel@tonic-gate 				}
538*7c478bd9Sstevel@tonic-gate 				mutex_exit(&ugenp->ug_mutex);
539*7c478bd9Sstevel@tonic-gate 				delay(drv_usectohz(100000));
540*7c478bd9Sstevel@tonic-gate 				mutex_enter(&ugenp->ug_mutex);
541*7c478bd9Sstevel@tonic-gate 			}
542*7c478bd9Sstevel@tonic-gate 
543*7c478bd9Sstevel@tonic-gate 			/* if still outstanding cmds, fail suspend */
544*7c478bd9Sstevel@tonic-gate 			if (ugenp->ug_pending_cmds) {
545*7c478bd9Sstevel@tonic-gate 				ugenp->ug_dev_state = prev_state;
546*7c478bd9Sstevel@tonic-gate 
547*7c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(UGEN_PRINT_CPR,
548*7c478bd9Sstevel@tonic-gate 				    ugenp->ug_log_hdl,
549*7c478bd9Sstevel@tonic-gate 				    "ugen_cpr_suspend: pending %d",
550*7c478bd9Sstevel@tonic-gate 				    ugenp->ug_pending_cmds);
551*7c478bd9Sstevel@tonic-gate 
552*7c478bd9Sstevel@tonic-gate 				rval =	USB_FAILURE;
553*7c478bd9Sstevel@tonic-gate 				break;
554*7c478bd9Sstevel@tonic-gate 			}
555*7c478bd9Sstevel@tonic-gate 
556*7c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
557*7c478bd9Sstevel@tonic-gate 			(void) usb_serialize_access(ugenp->ug_ser_cookie,
558*7c478bd9Sstevel@tonic-gate 								USB_WAIT, 0);
559*7c478bd9Sstevel@tonic-gate 			/* close all pipes */
560*7c478bd9Sstevel@tonic-gate 			ugen_epx_shutdown(ugenp);
561*7c478bd9Sstevel@tonic-gate 
562*7c478bd9Sstevel@tonic-gate 			usb_release_access(ugenp->ug_ser_cookie);
563*7c478bd9Sstevel@tonic-gate 
564*7c478bd9Sstevel@tonic-gate 			mutex_enter(&ugenp->ug_mutex);
565*7c478bd9Sstevel@tonic-gate 		}
566*7c478bd9Sstevel@tonic-gate 
567*7c478bd9Sstevel@tonic-gate 		/* wakeup devstat reads and polls */
568*7c478bd9Sstevel@tonic-gate 		ugen_ds_change(ugenp);
569*7c478bd9Sstevel@tonic-gate 		ugen_ds_poll_wakeup(ugenp);
570*7c478bd9Sstevel@tonic-gate 
571*7c478bd9Sstevel@tonic-gate 		rval = USB_SUCCESS;
572*7c478bd9Sstevel@tonic-gate 		break;
573*7c478bd9Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
574*7c478bd9Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RESUME:
575*7c478bd9Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
576*7c478bd9Sstevel@tonic-gate 	default:
577*7c478bd9Sstevel@tonic-gate 
578*7c478bd9Sstevel@tonic-gate 		break;
579*7c478bd9Sstevel@tonic-gate 	}
580*7c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
581*7c478bd9Sstevel@tonic-gate 
582*7c478bd9Sstevel@tonic-gate 	return (rval);
583*7c478bd9Sstevel@tonic-gate }
584*7c478bd9Sstevel@tonic-gate 
585*7c478bd9Sstevel@tonic-gate /*
586*7c478bd9Sstevel@tonic-gate  * ugen_cpr_resume
587*7c478bd9Sstevel@tonic-gate  */
588*7c478bd9Sstevel@tonic-gate static void
589*7c478bd9Sstevel@tonic-gate ugen_cpr_resume(ugen_state_t *ugenp)
590*7c478bd9Sstevel@tonic-gate {
591*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CPR, ugenp->ug_log_hdl,
592*7c478bd9Sstevel@tonic-gate 	    "ugen_cpr_resume:");
593*7c478bd9Sstevel@tonic-gate 
594*7c478bd9Sstevel@tonic-gate 	ugen_restore_state(ugenp);
595*7c478bd9Sstevel@tonic-gate }
596*7c478bd9Sstevel@tonic-gate 
597*7c478bd9Sstevel@tonic-gate /*
598*7c478bd9Sstevel@tonic-gate  * usb_ugen_disconnect_ev_cb:
599*7c478bd9Sstevel@tonic-gate  */
600*7c478bd9Sstevel@tonic-gate int
601*7c478bd9Sstevel@tonic-gate usb_ugen_disconnect_ev_cb(usb_ugen_hdl_t usb_ugen_hdl)
602*7c478bd9Sstevel@tonic-gate {
603*7c478bd9Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
604*7c478bd9Sstevel@tonic-gate 				(usb_ugen_hdl_impl_t *)usb_ugen_hdl;
605*7c478bd9Sstevel@tonic-gate 	ugen_state_t		*ugenp;
606*7c478bd9Sstevel@tonic-gate 
607*7c478bd9Sstevel@tonic-gate 	if (usb_ugen_hdl_impl == NULL) {
608*7c478bd9Sstevel@tonic-gate 
609*7c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
610*7c478bd9Sstevel@tonic-gate 	}
611*7c478bd9Sstevel@tonic-gate 
612*7c478bd9Sstevel@tonic-gate 	ugenp = usb_ugen_hdl_impl->hdl_ugenp;
613*7c478bd9Sstevel@tonic-gate 
614*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_HOTPLUG, ugenp->ug_log_hdl,
615*7c478bd9Sstevel@tonic-gate 	    "usb_ugen_disconnect_ev_cb:");
616*7c478bd9Sstevel@tonic-gate 
617*7c478bd9Sstevel@tonic-gate 	/* get exclusive access */
618*7c478bd9Sstevel@tonic-gate 	(void) usb_serialize_access(ugenp->ug_ser_cookie, USB_WAIT, 0);
619*7c478bd9Sstevel@tonic-gate 
620*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
621*7c478bd9Sstevel@tonic-gate 	ugenp->ug_dev_state = USB_DEV_DISCONNECTED;
622*7c478bd9Sstevel@tonic-gate 	if (ugenp->ug_open_count) {
623*7c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
624*7c478bd9Sstevel@tonic-gate 
625*7c478bd9Sstevel@tonic-gate 		/* close all pipes */
626*7c478bd9Sstevel@tonic-gate 		(void) ugen_epx_shutdown(ugenp);
627*7c478bd9Sstevel@tonic-gate 
628*7c478bd9Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
629*7c478bd9Sstevel@tonic-gate 	}
630*7c478bd9Sstevel@tonic-gate 
631*7c478bd9Sstevel@tonic-gate 
632*7c478bd9Sstevel@tonic-gate 	/* wakeup devstat reads and polls */
633*7c478bd9Sstevel@tonic-gate 	ugen_ds_change(ugenp);
634*7c478bd9Sstevel@tonic-gate 	ugen_ds_poll_wakeup(ugenp);
635*7c478bd9Sstevel@tonic-gate 
636*7c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
637*7c478bd9Sstevel@tonic-gate 	usb_release_access(ugenp->ug_ser_cookie);
638*7c478bd9Sstevel@tonic-gate 
639*7c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
640*7c478bd9Sstevel@tonic-gate }
641*7c478bd9Sstevel@tonic-gate 
642*7c478bd9Sstevel@tonic-gate 
643*7c478bd9Sstevel@tonic-gate /*
644*7c478bd9Sstevel@tonic-gate  * usb_ugen_reconnect_ev_cb:
645*7c478bd9Sstevel@tonic-gate  */
646*7c478bd9Sstevel@tonic-gate int
647*7c478bd9Sstevel@tonic-gate usb_ugen_reconnect_ev_cb(usb_ugen_hdl_t usb_ugen_hdl)
648*7c478bd9Sstevel@tonic-gate {
649*7c478bd9Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
650*7c478bd9Sstevel@tonic-gate 				(usb_ugen_hdl_impl_t *)usb_ugen_hdl;
651*7c478bd9Sstevel@tonic-gate 	ugen_state_t		*ugenp = usb_ugen_hdl_impl->hdl_ugenp;
652*7c478bd9Sstevel@tonic-gate 
653*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_HOTPLUG, ugenp->ug_log_hdl,
654*7c478bd9Sstevel@tonic-gate 	    "usb_ugen_reconnect_ev_cb:");
655*7c478bd9Sstevel@tonic-gate 
656*7c478bd9Sstevel@tonic-gate 	ugen_restore_state(ugenp);
657*7c478bd9Sstevel@tonic-gate 
658*7c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
659*7c478bd9Sstevel@tonic-gate }
660*7c478bd9Sstevel@tonic-gate 
661*7c478bd9Sstevel@tonic-gate 
662*7c478bd9Sstevel@tonic-gate /*
663*7c478bd9Sstevel@tonic-gate  * ugen_restore_state:
664*7c478bd9Sstevel@tonic-gate  *	Check for same device; if a different device is attached, set
665*7c478bd9Sstevel@tonic-gate  *	the device status to disconnected.
666*7c478bd9Sstevel@tonic-gate  *	If we were open, then set to UNAVAILABLE until all endpoints have
667*7c478bd9Sstevel@tonic-gate  *	be closed.
668*7c478bd9Sstevel@tonic-gate  */
669*7c478bd9Sstevel@tonic-gate static void
670*7c478bd9Sstevel@tonic-gate ugen_restore_state(ugen_state_t *ugenp)
671*7c478bd9Sstevel@tonic-gate {
672*7c478bd9Sstevel@tonic-gate 	dev_info_t *dip = ugenp->ug_dip;
673*7c478bd9Sstevel@tonic-gate 
674*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_HOTPLUG, ugenp->ug_log_hdl,
675*7c478bd9Sstevel@tonic-gate 	    "ugen_restore_state");
676*7c478bd9Sstevel@tonic-gate 
677*7c478bd9Sstevel@tonic-gate 	/* first raise power */
678*7c478bd9Sstevel@tonic-gate 	if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
679*7c478bd9Sstevel@tonic-gate 		ugen_pm_busy_component(ugenp);
680*7c478bd9Sstevel@tonic-gate 		(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
681*7c478bd9Sstevel@tonic-gate 	}
682*7c478bd9Sstevel@tonic-gate 
683*7c478bd9Sstevel@tonic-gate 	/* Check if we are talking to the same device */
684*7c478bd9Sstevel@tonic-gate 	if (usb_check_same_device(dip, ugenp->ug_log_hdl,
685*7c478bd9Sstevel@tonic-gate 	    USB_LOG_L0, UGEN_PRINT_HOTPLUG, USB_CHK_ALL, NULL) ==
686*7c478bd9Sstevel@tonic-gate 	    USB_FAILURE) {
687*7c478bd9Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
688*7c478bd9Sstevel@tonic-gate 		ugenp->ug_dev_state = USB_DEV_DISCONNECTED;
689*7c478bd9Sstevel@tonic-gate 
690*7c478bd9Sstevel@tonic-gate 		/* wakeup devstat reads and polls */
691*7c478bd9Sstevel@tonic-gate 		ugen_ds_change(ugenp);
692*7c478bd9Sstevel@tonic-gate 		ugen_ds_poll_wakeup(ugenp);
693*7c478bd9Sstevel@tonic-gate 
694*7c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
695*7c478bd9Sstevel@tonic-gate 
696*7c478bd9Sstevel@tonic-gate 		if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
697*7c478bd9Sstevel@tonic-gate 			ugen_pm_idle_component(ugenp);
698*7c478bd9Sstevel@tonic-gate 		}
699*7c478bd9Sstevel@tonic-gate 
700*7c478bd9Sstevel@tonic-gate 		return;
701*7c478bd9Sstevel@tonic-gate 	}
702*7c478bd9Sstevel@tonic-gate 
703*7c478bd9Sstevel@tonic-gate 	/*
704*7c478bd9Sstevel@tonic-gate 	 * get exclusive access, we don't want to change state in the
705*7c478bd9Sstevel@tonic-gate 	 * middle of some other actions
706*7c478bd9Sstevel@tonic-gate 	 */
707*7c478bd9Sstevel@tonic-gate 	(void) usb_serialize_access(ugenp->ug_ser_cookie, USB_WAIT, 0);
708*7c478bd9Sstevel@tonic-gate 
709*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
710*7c478bd9Sstevel@tonic-gate 	switch (ugenp->ug_dev_state) {
711*7c478bd9Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
712*7c478bd9Sstevel@tonic-gate 		ugenp->ug_dev_state = (ugenp->ug_open_count == 0) ?
713*7c478bd9Sstevel@tonic-gate 		    USB_DEV_ONLINE : USB_UGEN_DEV_UNAVAILABLE_RECONNECT;
714*7c478bd9Sstevel@tonic-gate 
715*7c478bd9Sstevel@tonic-gate 		break;
716*7c478bd9Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
717*7c478bd9Sstevel@tonic-gate 		ugenp->ug_dev_state = (ugenp->ug_open_count == 0) ?
718*7c478bd9Sstevel@tonic-gate 		    USB_DEV_ONLINE : USB_UGEN_DEV_UNAVAILABLE_RESUME;
719*7c478bd9Sstevel@tonic-gate 
720*7c478bd9Sstevel@tonic-gate 		break;
721*7c478bd9Sstevel@tonic-gate 	}
722*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_HOTPLUG, ugenp->ug_log_hdl,
723*7c478bd9Sstevel@tonic-gate 	    "ugen_restore_state: state=%d, opencount=%d",
724*7c478bd9Sstevel@tonic-gate 	    ugenp->ug_dev_state, ugenp->ug_open_count);
725*7c478bd9Sstevel@tonic-gate 
726*7c478bd9Sstevel@tonic-gate 	/* wakeup devstat reads and polls */
727*7c478bd9Sstevel@tonic-gate 	ugen_ds_change(ugenp);
728*7c478bd9Sstevel@tonic-gate 	ugen_ds_poll_wakeup(ugenp);
729*7c478bd9Sstevel@tonic-gate 
730*7c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
731*7c478bd9Sstevel@tonic-gate 	usb_release_access(ugenp->ug_ser_cookie);
732*7c478bd9Sstevel@tonic-gate 
733*7c478bd9Sstevel@tonic-gate 	if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
734*7c478bd9Sstevel@tonic-gate 		ugen_pm_idle_component(ugenp);
735*7c478bd9Sstevel@tonic-gate 	}
736*7c478bd9Sstevel@tonic-gate }
737*7c478bd9Sstevel@tonic-gate 
738*7c478bd9Sstevel@tonic-gate 
739*7c478bd9Sstevel@tonic-gate /*
740*7c478bd9Sstevel@tonic-gate  * usb_ugen_open:
741*7c478bd9Sstevel@tonic-gate  */
742*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
743*7c478bd9Sstevel@tonic-gate int
744*7c478bd9Sstevel@tonic-gate usb_ugen_open(usb_ugen_hdl_t usb_ugen_hdl, dev_t *devp, int flag, int sflag,
745*7c478bd9Sstevel@tonic-gate     cred_t *cr)
746*7c478bd9Sstevel@tonic-gate {
747*7c478bd9Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
748*7c478bd9Sstevel@tonic-gate 				(usb_ugen_hdl_impl_t *)usb_ugen_hdl;
749*7c478bd9Sstevel@tonic-gate 	ugen_state_t		*ugenp;
750*7c478bd9Sstevel@tonic-gate 	int			rval;
751*7c478bd9Sstevel@tonic-gate 	int			minor_node_type;
752*7c478bd9Sstevel@tonic-gate 
753*7c478bd9Sstevel@tonic-gate 	if (usb_ugen_hdl == NULL) {
754*7c478bd9Sstevel@tonic-gate 
755*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
756*7c478bd9Sstevel@tonic-gate 	}
757*7c478bd9Sstevel@tonic-gate 
758*7c478bd9Sstevel@tonic-gate 	ugenp = usb_ugen_hdl_impl->hdl_ugenp;
759*7c478bd9Sstevel@tonic-gate 	minor_node_type = UGEN_MINOR_TYPE(ugenp, *devp);
760*7c478bd9Sstevel@tonic-gate 
761*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
762*7c478bd9Sstevel@tonic-gate 	    "usb_ugen_open: minor=%u", getminor(*devp));
763*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
764*7c478bd9Sstevel@tonic-gate 	    "cfgval=%" PRIu64 " cfgidx=%" PRIu64 " if=%" PRIu64
765*7c478bd9Sstevel@tonic-gate 	    " alt=%" PRIu64 " epidx=%" PRIu64 " type=0x%" PRIx64,
766*7c478bd9Sstevel@tonic-gate 	    UGEN_MINOR_CFGVAL(ugenp, *devp), UGEN_MINOR_CFGIDX(ugenp, *devp),
767*7c478bd9Sstevel@tonic-gate 	    UGEN_MINOR_IF(ugenp, *devp), UGEN_MINOR_ALT(ugenp, *devp),
768*7c478bd9Sstevel@tonic-gate 	    UGEN_MINOR_EPIDX(ugenp, *devp), UGEN_MINOR_TYPE(ugenp, *devp));
769*7c478bd9Sstevel@tonic-gate 
770*7c478bd9Sstevel@tonic-gate 	/* first check for legal open flags */
771*7c478bd9Sstevel@tonic-gate 	if ((rval = ugen_check_open_flags(ugenp, *devp, flag)) != 0) {
772*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
773*7c478bd9Sstevel@tonic-gate 		    "usb_ugen_open: check failed, rval=%d", rval);
774*7c478bd9Sstevel@tonic-gate 
775*7c478bd9Sstevel@tonic-gate 		return (rval);
776*7c478bd9Sstevel@tonic-gate 	}
777*7c478bd9Sstevel@tonic-gate 
778*7c478bd9Sstevel@tonic-gate 	/* exclude other threads including other opens */
779*7c478bd9Sstevel@tonic-gate 	if (usb_serialize_access(ugenp->ug_ser_cookie,
780*7c478bd9Sstevel@tonic-gate 	    USB_WAIT_SIG, 0) <= 0) {
781*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
782*7c478bd9Sstevel@tonic-gate 		    "usb_ugen_open: interrupted");
783*7c478bd9Sstevel@tonic-gate 
784*7c478bd9Sstevel@tonic-gate 		return (EINTR);
785*7c478bd9Sstevel@tonic-gate 	}
786*7c478bd9Sstevel@tonic-gate 
787*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
788*7c478bd9Sstevel@tonic-gate 
789*7c478bd9Sstevel@tonic-gate 	/* always allow open of dev stat node */
790*7c478bd9Sstevel@tonic-gate 	if (minor_node_type != UGEN_MINOR_DEV_STAT_NODE) {
791*7c478bd9Sstevel@tonic-gate 
792*7c478bd9Sstevel@tonic-gate 		/* if we are not online or powered down, fail open */
793*7c478bd9Sstevel@tonic-gate 		switch (ugenp->ug_dev_state) {
794*7c478bd9Sstevel@tonic-gate 		case USB_DEV_ONLINE:
795*7c478bd9Sstevel@tonic-gate 
796*7c478bd9Sstevel@tonic-gate 			break;
797*7c478bd9Sstevel@tonic-gate 		case USB_DEV_DISCONNECTED:
798*7c478bd9Sstevel@tonic-gate 			rval = ENODEV;
799*7c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
800*7c478bd9Sstevel@tonic-gate 
801*7c478bd9Sstevel@tonic-gate 			goto done;
802*7c478bd9Sstevel@tonic-gate 		case USB_DEV_SUSPENDED:
803*7c478bd9Sstevel@tonic-gate 		case USB_UGEN_DEV_UNAVAILABLE_RESUME:
804*7c478bd9Sstevel@tonic-gate 		case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
805*7c478bd9Sstevel@tonic-gate 		default:
806*7c478bd9Sstevel@tonic-gate 			rval = EBADF;
807*7c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
808*7c478bd9Sstevel@tonic-gate 
809*7c478bd9Sstevel@tonic-gate 			goto done;
810*7c478bd9Sstevel@tonic-gate 		}
811*7c478bd9Sstevel@tonic-gate 	}
812*7c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
813*7c478bd9Sstevel@tonic-gate 
814*7c478bd9Sstevel@tonic-gate 	/* open node depending on type */
815*7c478bd9Sstevel@tonic-gate 	switch (minor_node_type) {
816*7c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_EP_XFER_NODE:
817*7c478bd9Sstevel@tonic-gate 		if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
818*7c478bd9Sstevel@tonic-gate 			ugen_pm_busy_component(ugenp);
819*7c478bd9Sstevel@tonic-gate 			(void) pm_raise_power(ugenp->ug_dip, 0,
820*7c478bd9Sstevel@tonic-gate 						USB_DEV_OS_FULL_PWR);
821*7c478bd9Sstevel@tonic-gate 		}
822*7c478bd9Sstevel@tonic-gate 
823*7c478bd9Sstevel@tonic-gate 		rval = ugen_epx_open(ugenp, *devp, flag);
824*7c478bd9Sstevel@tonic-gate 		if (rval == 0) {
825*7c478bd9Sstevel@tonic-gate 			mutex_enter(&ugenp->ug_mutex);
826*7c478bd9Sstevel@tonic-gate 			ugenp->ug_open_count++;
827*7c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
828*7c478bd9Sstevel@tonic-gate 		} else {
829*7c478bd9Sstevel@tonic-gate 			if (ugenp->ug_hdl->hdl_flags &
830*7c478bd9Sstevel@tonic-gate 			    USB_UGEN_ENABLE_PM) {
831*7c478bd9Sstevel@tonic-gate 				ugen_pm_idle_component(ugenp);
832*7c478bd9Sstevel@tonic-gate 			}
833*7c478bd9Sstevel@tonic-gate 		}
834*7c478bd9Sstevel@tonic-gate 
835*7c478bd9Sstevel@tonic-gate 		break;
836*7c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_EP_STAT_NODE:
837*7c478bd9Sstevel@tonic-gate 		rval = ugen_eps_open(ugenp, *devp, flag);
838*7c478bd9Sstevel@tonic-gate 		if (rval == 0) {
839*7c478bd9Sstevel@tonic-gate 			mutex_enter(&ugenp->ug_mutex);
840*7c478bd9Sstevel@tonic-gate 			ugenp->ug_open_count++;
841*7c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
842*7c478bd9Sstevel@tonic-gate 		}
843*7c478bd9Sstevel@tonic-gate 
844*7c478bd9Sstevel@tonic-gate 		break;
845*7c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_DEV_STAT_NODE:
846*7c478bd9Sstevel@tonic-gate 		rval = ugen_ds_open(ugenp, *devp, flag);
847*7c478bd9Sstevel@tonic-gate 
848*7c478bd9Sstevel@tonic-gate 		break;
849*7c478bd9Sstevel@tonic-gate 	default:
850*7c478bd9Sstevel@tonic-gate 		rval = EINVAL;
851*7c478bd9Sstevel@tonic-gate 
852*7c478bd9Sstevel@tonic-gate 		break;
853*7c478bd9Sstevel@tonic-gate 	}
854*7c478bd9Sstevel@tonic-gate done:
855*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
856*7c478bd9Sstevel@tonic-gate 
857*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
858*7c478bd9Sstevel@tonic-gate 	    "usb_ugen_open: minor=0x%x rval=%d state=%d cnt=%d",
859*7c478bd9Sstevel@tonic-gate 	    getminor(*devp), rval, ugenp->ug_dev_state,
860*7c478bd9Sstevel@tonic-gate 	    ugenp->ug_open_count);
861*7c478bd9Sstevel@tonic-gate 
862*7c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
863*7c478bd9Sstevel@tonic-gate 
864*7c478bd9Sstevel@tonic-gate 	usb_release_access(ugenp->ug_ser_cookie);
865*7c478bd9Sstevel@tonic-gate 
866*7c478bd9Sstevel@tonic-gate 	return (rval);
867*7c478bd9Sstevel@tonic-gate }
868*7c478bd9Sstevel@tonic-gate 
869*7c478bd9Sstevel@tonic-gate 
870*7c478bd9Sstevel@tonic-gate /*
871*7c478bd9Sstevel@tonic-gate  * usb_ugen_close()
872*7c478bd9Sstevel@tonic-gate  */
873*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
874*7c478bd9Sstevel@tonic-gate int
875*7c478bd9Sstevel@tonic-gate usb_ugen_close(usb_ugen_hdl_t usb_ugen_hdl, dev_t dev, int flag, int otype,
876*7c478bd9Sstevel@tonic-gate     cred_t *cr)
877*7c478bd9Sstevel@tonic-gate {
878*7c478bd9Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
879*7c478bd9Sstevel@tonic-gate 				(usb_ugen_hdl_impl_t *)usb_ugen_hdl;
880*7c478bd9Sstevel@tonic-gate 	ugen_state_t		*ugenp;
881*7c478bd9Sstevel@tonic-gate 	int			minor_node_type;
882*7c478bd9Sstevel@tonic-gate 
883*7c478bd9Sstevel@tonic-gate 	if (usb_ugen_hdl == NULL) {
884*7c478bd9Sstevel@tonic-gate 
885*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
886*7c478bd9Sstevel@tonic-gate 	}
887*7c478bd9Sstevel@tonic-gate 
888*7c478bd9Sstevel@tonic-gate 	ugenp = usb_ugen_hdl_impl->hdl_ugenp;
889*7c478bd9Sstevel@tonic-gate 	minor_node_type = UGEN_MINOR_TYPE(ugenp, dev);
890*7c478bd9Sstevel@tonic-gate 
891*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
892*7c478bd9Sstevel@tonic-gate 	    "usb_ugen_close: minor=0x%x", getminor(dev));
893*7c478bd9Sstevel@tonic-gate 
894*7c478bd9Sstevel@tonic-gate 	/* exclude other threads, including other opens */
895*7c478bd9Sstevel@tonic-gate 	if (usb_serialize_access(ugenp->ug_ser_cookie,
896*7c478bd9Sstevel@tonic-gate 	    USB_WAIT_SIG, 0) <= 0) {
897*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
898*7c478bd9Sstevel@tonic-gate 		    "usb_ugen_close: interrupted");
899*7c478bd9Sstevel@tonic-gate 
900*7c478bd9Sstevel@tonic-gate 		return (EINTR);
901*7c478bd9Sstevel@tonic-gate 	}
902*7c478bd9Sstevel@tonic-gate 
903*7c478bd9Sstevel@tonic-gate 	/* close node depending on type */
904*7c478bd9Sstevel@tonic-gate 	switch (minor_node_type) {
905*7c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_EP_XFER_NODE:
906*7c478bd9Sstevel@tonic-gate 		ugen_epx_close(ugenp, dev, flag);
907*7c478bd9Sstevel@tonic-gate 		if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
908*7c478bd9Sstevel@tonic-gate 			ugen_pm_idle_component(ugenp);
909*7c478bd9Sstevel@tonic-gate 		}
910*7c478bd9Sstevel@tonic-gate 
911*7c478bd9Sstevel@tonic-gate 		break;
912*7c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_EP_STAT_NODE:
913*7c478bd9Sstevel@tonic-gate 		ugen_eps_close(ugenp, dev, flag);
914*7c478bd9Sstevel@tonic-gate 
915*7c478bd9Sstevel@tonic-gate 		break;
916*7c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_DEV_STAT_NODE:
917*7c478bd9Sstevel@tonic-gate 		ugen_ds_close(ugenp, dev, flag);
918*7c478bd9Sstevel@tonic-gate 
919*7c478bd9Sstevel@tonic-gate 		break;
920*7c478bd9Sstevel@tonic-gate 	default:
921*7c478bd9Sstevel@tonic-gate 		usb_release_access(ugenp->ug_ser_cookie);
922*7c478bd9Sstevel@tonic-gate 
923*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
924*7c478bd9Sstevel@tonic-gate 	}
925*7c478bd9Sstevel@tonic-gate 
926*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
927*7c478bd9Sstevel@tonic-gate 	if (minor_node_type != UGEN_MINOR_DEV_STAT_NODE) {
928*7c478bd9Sstevel@tonic-gate 		ASSERT(ugenp->ug_open_count > 0);
929*7c478bd9Sstevel@tonic-gate 		if ((--ugenp->ug_open_count == 0) &&
930*7c478bd9Sstevel@tonic-gate 		    ((ugenp->ug_dev_state == USB_UGEN_DEV_UNAVAILABLE_RESUME) ||
931*7c478bd9Sstevel@tonic-gate 		    (ugenp->ug_dev_state ==
932*7c478bd9Sstevel@tonic-gate 		    USB_UGEN_DEV_UNAVAILABLE_RECONNECT))) {
933*7c478bd9Sstevel@tonic-gate 			ugenp->ug_dev_state = USB_DEV_ONLINE;
934*7c478bd9Sstevel@tonic-gate 
935*7c478bd9Sstevel@tonic-gate 			/* wakeup devstat reads and polls */
936*7c478bd9Sstevel@tonic-gate 			ugen_ds_change(ugenp);
937*7c478bd9Sstevel@tonic-gate 			ugen_ds_poll_wakeup(ugenp);
938*7c478bd9Sstevel@tonic-gate 		}
939*7c478bd9Sstevel@tonic-gate 	}
940*7c478bd9Sstevel@tonic-gate 
941*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
942*7c478bd9Sstevel@tonic-gate 	    "usb_ugen_close: minor=0x%x state=%d cnt=%d",
943*7c478bd9Sstevel@tonic-gate 	    getminor(dev), ugenp->ug_dev_state, ugenp->ug_open_count);
944*7c478bd9Sstevel@tonic-gate 
945*7c478bd9Sstevel@tonic-gate 	if (ugenp->ug_open_count == 0) {
946*7c478bd9Sstevel@tonic-gate 		ASSERT(ugen_epxs_check_open_nodes(ugenp) == USB_FAILURE);
947*7c478bd9Sstevel@tonic-gate 	}
948*7c478bd9Sstevel@tonic-gate 
949*7c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
950*7c478bd9Sstevel@tonic-gate 
951*7c478bd9Sstevel@tonic-gate 	usb_release_access(ugenp->ug_ser_cookie);
952*7c478bd9Sstevel@tonic-gate 
953*7c478bd9Sstevel@tonic-gate 	return (0);
954*7c478bd9Sstevel@tonic-gate }
955*7c478bd9Sstevel@tonic-gate 
956*7c478bd9Sstevel@tonic-gate 
957*7c478bd9Sstevel@tonic-gate /*
958*7c478bd9Sstevel@tonic-gate  * usb_ugen_read/write()
959*7c478bd9Sstevel@tonic-gate  */
960*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
961*7c478bd9Sstevel@tonic-gate int
962*7c478bd9Sstevel@tonic-gate usb_ugen_read(usb_ugen_hdl_t usb_ugen_hdl, dev_t dev, struct uio *uiop,
963*7c478bd9Sstevel@tonic-gate     cred_t *credp)
964*7c478bd9Sstevel@tonic-gate {
965*7c478bd9Sstevel@tonic-gate 	return (physio(ugen_strategy,
966*7c478bd9Sstevel@tonic-gate 	    (struct buf *)0, dev, B_READ, ugen_minphys, uiop));
967*7c478bd9Sstevel@tonic-gate }
968*7c478bd9Sstevel@tonic-gate 
969*7c478bd9Sstevel@tonic-gate 
970*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
971*7c478bd9Sstevel@tonic-gate int
972*7c478bd9Sstevel@tonic-gate usb_ugen_write(usb_ugen_hdl_t usb_ugen_hdl, dev_t dev, struct uio *uiop,
973*7c478bd9Sstevel@tonic-gate     cred_t *credp)
974*7c478bd9Sstevel@tonic-gate {
975*7c478bd9Sstevel@tonic-gate 	return (physio(ugen_strategy,
976*7c478bd9Sstevel@tonic-gate 	    (struct buf *)0, dev, B_WRITE, ugen_minphys, uiop));
977*7c478bd9Sstevel@tonic-gate }
978*7c478bd9Sstevel@tonic-gate 
979*7c478bd9Sstevel@tonic-gate 
980*7c478bd9Sstevel@tonic-gate /*
981*7c478bd9Sstevel@tonic-gate  * usb_ugen_poll
982*7c478bd9Sstevel@tonic-gate  */
983*7c478bd9Sstevel@tonic-gate int
984*7c478bd9Sstevel@tonic-gate usb_ugen_poll(usb_ugen_hdl_t usb_ugen_hdl, dev_t dev, short events,
985*7c478bd9Sstevel@tonic-gate     int anyyet,  short *reventsp, struct pollhead **phpp)
986*7c478bd9Sstevel@tonic-gate {
987*7c478bd9Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
988*7c478bd9Sstevel@tonic-gate 				(usb_ugen_hdl_impl_t *)usb_ugen_hdl;
989*7c478bd9Sstevel@tonic-gate 	ugen_state_t		*ugenp;
990*7c478bd9Sstevel@tonic-gate 	int			minor_node_type;
991*7c478bd9Sstevel@tonic-gate 	uint_t			ep_index;
992*7c478bd9Sstevel@tonic-gate 	ugen_ep_t		*epp;
993*7c478bd9Sstevel@tonic-gate 
994*7c478bd9Sstevel@tonic-gate 	if (usb_ugen_hdl == NULL) {
995*7c478bd9Sstevel@tonic-gate 
996*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
997*7c478bd9Sstevel@tonic-gate 	}
998*7c478bd9Sstevel@tonic-gate 
999*7c478bd9Sstevel@tonic-gate 	ugenp = usb_ugen_hdl_impl->hdl_ugenp;
1000*7c478bd9Sstevel@tonic-gate 
1001*7c478bd9Sstevel@tonic-gate 	minor_node_type = UGEN_MINOR_TYPE(ugenp, dev);
1002*7c478bd9Sstevel@tonic-gate 	ep_index	= UGEN_MINOR_EPIDX(ugenp, dev);
1003*7c478bd9Sstevel@tonic-gate 	epp		= &ugenp->ug_ep[ep_index];
1004*7c478bd9Sstevel@tonic-gate 
1005*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
1006*7c478bd9Sstevel@tonic-gate 
1007*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_POLL, ugenp->ug_log_hdl,
1008*7c478bd9Sstevel@tonic-gate 	    "usb_ugen_poll: "
1009*7c478bd9Sstevel@tonic-gate 	    "dev=0x%lx events=0x%x anyyet=0x%x rev=0x%p type=%d "
1010*7c478bd9Sstevel@tonic-gate 	    "devstat=0x%x devstate=0x%x",
1011*7c478bd9Sstevel@tonic-gate 	    dev, events, anyyet, (void *)reventsp, minor_node_type,
1012*7c478bd9Sstevel@tonic-gate 	    ugenp->ug_ds.dev_stat, ugenp->ug_ds.dev_state);
1013*7c478bd9Sstevel@tonic-gate 
1014*7c478bd9Sstevel@tonic-gate 	*reventsp = 0;
1015*7c478bd9Sstevel@tonic-gate 
1016*7c478bd9Sstevel@tonic-gate 	if (ugenp->ug_dev_state == USB_DEV_ONLINE) {
1017*7c478bd9Sstevel@tonic-gate 		switch (minor_node_type) {
1018*7c478bd9Sstevel@tonic-gate 		case UGEN_MINOR_EP_XFER_NODE:
1019*7c478bd9Sstevel@tonic-gate 			/* if interrupt IN ep and there is data, set POLLIN */
1020*7c478bd9Sstevel@tonic-gate 			if ((UGEN_XFER_TYPE(epp) == USB_EP_ATTR_INTR) &&
1021*7c478bd9Sstevel@tonic-gate 			    (UGEN_XFER_DIR(epp) & USB_EP_DIR_IN)) {
1022*7c478bd9Sstevel@tonic-gate 
1023*7c478bd9Sstevel@tonic-gate 				/*
1024*7c478bd9Sstevel@tonic-gate 				 * if we are not polling, force another
1025*7c478bd9Sstevel@tonic-gate 				 * read to kick off polling
1026*7c478bd9Sstevel@tonic-gate 				 */
1027*7c478bd9Sstevel@tonic-gate 				mutex_enter(&epp->ep_mutex);
1028*7c478bd9Sstevel@tonic-gate 				if ((epp->ep_data) ||
1029*7c478bd9Sstevel@tonic-gate 				    ((epp->ep_state &
1030*7c478bd9Sstevel@tonic-gate 				    UGEN_EP_STATE_INTR_IN_POLLING_ON) == 0)) {
1031*7c478bd9Sstevel@tonic-gate 					*reventsp |= POLLIN;
1032*7c478bd9Sstevel@tonic-gate 				} else if (!anyyet) {
1033*7c478bd9Sstevel@tonic-gate 					*phpp = &epp->ep_pollhead;
1034*7c478bd9Sstevel@tonic-gate 					epp->ep_state |=
1035*7c478bd9Sstevel@tonic-gate 					    UGEN_EP_STATE_INTR_IN_POLL_PENDING;
1036*7c478bd9Sstevel@tonic-gate 				}
1037*7c478bd9Sstevel@tonic-gate 				mutex_exit(&epp->ep_mutex);
1038*7c478bd9Sstevel@tonic-gate 			} else {
1039*7c478bd9Sstevel@tonic-gate 				/* no poll on other ep nodes */
1040*7c478bd9Sstevel@tonic-gate 				*reventsp |= POLLERR;
1041*7c478bd9Sstevel@tonic-gate 			}
1042*7c478bd9Sstevel@tonic-gate 
1043*7c478bd9Sstevel@tonic-gate 			break;
1044*7c478bd9Sstevel@tonic-gate 		case UGEN_MINOR_DEV_STAT_NODE:
1045*7c478bd9Sstevel@tonic-gate 			if (ugenp->ug_ds.dev_stat & UGEN_DEV_STATUS_CHANGED) {
1046*7c478bd9Sstevel@tonic-gate 				*reventsp |= POLLIN;
1047*7c478bd9Sstevel@tonic-gate 			} else if (!anyyet) {
1048*7c478bd9Sstevel@tonic-gate 				*phpp = &ugenp->ug_ds.dev_pollhead;
1049*7c478bd9Sstevel@tonic-gate 				ugenp->ug_ds.dev_stat |=
1050*7c478bd9Sstevel@tonic-gate 				    UGEN_DEV_STATUS_POLL_PENDING;
1051*7c478bd9Sstevel@tonic-gate 			}
1052*7c478bd9Sstevel@tonic-gate 
1053*7c478bd9Sstevel@tonic-gate 			break;
1054*7c478bd9Sstevel@tonic-gate 		case UGEN_MINOR_EP_STAT_NODE:
1055*7c478bd9Sstevel@tonic-gate 		default:
1056*7c478bd9Sstevel@tonic-gate 			*reventsp |= POLLERR;
1057*7c478bd9Sstevel@tonic-gate 
1058*7c478bd9Sstevel@tonic-gate 			break;
1059*7c478bd9Sstevel@tonic-gate 		}
1060*7c478bd9Sstevel@tonic-gate 	} else {
1061*7c478bd9Sstevel@tonic-gate 		if (ugenp->ug_ds.dev_stat & UGEN_DEV_STATUS_CHANGED) {
1062*7c478bd9Sstevel@tonic-gate 			*reventsp |= POLLHUP|POLLIN;
1063*7c478bd9Sstevel@tonic-gate 		} else if (!anyyet) {
1064*7c478bd9Sstevel@tonic-gate 			*phpp = &ugenp->ug_ds.dev_pollhead;
1065*7c478bd9Sstevel@tonic-gate 			ugenp->ug_ds.dev_stat |=
1066*7c478bd9Sstevel@tonic-gate 				    UGEN_DEV_STATUS_POLL_PENDING;
1067*7c478bd9Sstevel@tonic-gate 		}
1068*7c478bd9Sstevel@tonic-gate 	}
1069*7c478bd9Sstevel@tonic-gate 
1070*7c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
1071*7c478bd9Sstevel@tonic-gate 
1072*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_POLL, ugenp->ug_log_hdl,
1073*7c478bd9Sstevel@tonic-gate 	    "usb_ugen_poll end: reventsp=0x%x", *reventsp);
1074*7c478bd9Sstevel@tonic-gate 
1075*7c478bd9Sstevel@tonic-gate 	return (0);
1076*7c478bd9Sstevel@tonic-gate }
1077*7c478bd9Sstevel@tonic-gate 
1078*7c478bd9Sstevel@tonic-gate 
1079*7c478bd9Sstevel@tonic-gate /*
1080*7c478bd9Sstevel@tonic-gate  * ugen_strategy
1081*7c478bd9Sstevel@tonic-gate  */
1082*7c478bd9Sstevel@tonic-gate static int
1083*7c478bd9Sstevel@tonic-gate ugen_strategy(struct buf *bp)
1084*7c478bd9Sstevel@tonic-gate {
1085*7c478bd9Sstevel@tonic-gate 	dev_t		dev = bp->b_edev;
1086*7c478bd9Sstevel@tonic-gate 	int		rval = 0;
1087*7c478bd9Sstevel@tonic-gate 	ugen_state_t	*ugenp = ugen_devt2state(dev);
1088*7c478bd9Sstevel@tonic-gate 	int		minor_node_type = UGEN_MINOR_TYPE(ugenp, dev);
1089*7c478bd9Sstevel@tonic-gate 
1090*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1091*7c478bd9Sstevel@tonic-gate 	    "ugen_strategy: bp=0x%p minor=0x%x", bp, getminor(dev));
1092*7c478bd9Sstevel@tonic-gate 
1093*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
1094*7c478bd9Sstevel@tonic-gate 	ugenp->ug_pending_cmds++;
1095*7c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
1096*7c478bd9Sstevel@tonic-gate 
1097*7c478bd9Sstevel@tonic-gate 	bp_mapin(bp);
1098*7c478bd9Sstevel@tonic-gate 
1099*7c478bd9Sstevel@tonic-gate 	switch (minor_node_type) {
1100*7c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_EP_XFER_NODE:
1101*7c478bd9Sstevel@tonic-gate 		rval = ugen_epx_req(ugenp, bp);
1102*7c478bd9Sstevel@tonic-gate 
1103*7c478bd9Sstevel@tonic-gate 		break;
1104*7c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_EP_STAT_NODE:
1105*7c478bd9Sstevel@tonic-gate 		rval = ugen_eps_req(ugenp, bp);
1106*7c478bd9Sstevel@tonic-gate 
1107*7c478bd9Sstevel@tonic-gate 		break;
1108*7c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_DEV_STAT_NODE:
1109*7c478bd9Sstevel@tonic-gate 		rval = ugen_ds_req(ugenp, bp);
1110*7c478bd9Sstevel@tonic-gate 
1111*7c478bd9Sstevel@tonic-gate 		break;
1112*7c478bd9Sstevel@tonic-gate 	default:
1113*7c478bd9Sstevel@tonic-gate 		rval = EINVAL;
1114*7c478bd9Sstevel@tonic-gate 
1115*7c478bd9Sstevel@tonic-gate 		break;
1116*7c478bd9Sstevel@tonic-gate 	}
1117*7c478bd9Sstevel@tonic-gate 
1118*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
1119*7c478bd9Sstevel@tonic-gate 	ugenp->ug_pending_cmds--;
1120*7c478bd9Sstevel@tonic-gate 
1121*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1122*7c478bd9Sstevel@tonic-gate 	    "ugen_strategy: "
1123*7c478bd9Sstevel@tonic-gate 	    "bp=0x%p cnt=%lu resid=%lu err=%d minor=0x%x rval=%d #cmds=%d",
1124*7c478bd9Sstevel@tonic-gate 	    (void *)bp, bp->b_bcount, bp->b_resid, geterror(bp),
1125*7c478bd9Sstevel@tonic-gate 	    getminor(dev), rval, ugenp->ug_pending_cmds);
1126*7c478bd9Sstevel@tonic-gate 
1127*7c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
1128*7c478bd9Sstevel@tonic-gate 
1129*7c478bd9Sstevel@tonic-gate 	if (rval) {
1130*7c478bd9Sstevel@tonic-gate 		if (geterror(bp) == 0) {
1131*7c478bd9Sstevel@tonic-gate 			bioerror(bp, rval);
1132*7c478bd9Sstevel@tonic-gate 		}
1133*7c478bd9Sstevel@tonic-gate 	}
1134*7c478bd9Sstevel@tonic-gate 
1135*7c478bd9Sstevel@tonic-gate 	biodone(bp);
1136*7c478bd9Sstevel@tonic-gate 
1137*7c478bd9Sstevel@tonic-gate 	return (0);
1138*7c478bd9Sstevel@tonic-gate }
1139*7c478bd9Sstevel@tonic-gate 
1140*7c478bd9Sstevel@tonic-gate 
1141*7c478bd9Sstevel@tonic-gate /*
1142*7c478bd9Sstevel@tonic-gate  * ugen_minphys:
1143*7c478bd9Sstevel@tonic-gate  */
1144*7c478bd9Sstevel@tonic-gate static void
1145*7c478bd9Sstevel@tonic-gate ugen_minphys(struct buf *bp)
1146*7c478bd9Sstevel@tonic-gate {
1147*7c478bd9Sstevel@tonic-gate 	dev_t		dev = bp->b_edev;
1148*7c478bd9Sstevel@tonic-gate 	ugen_state_t	*ugenp = ugen_devt2state(dev);
1149*7c478bd9Sstevel@tonic-gate 	int		minor_node_type = UGEN_MINOR_TYPE(ugenp, dev);
1150*7c478bd9Sstevel@tonic-gate 	uint_t		ep_index = UGEN_MINOR_EPIDX(ugenp, dev);
1151*7c478bd9Sstevel@tonic-gate 	ugen_ep_t	*epp = &ugenp->ug_ep[ep_index];
1152*7c478bd9Sstevel@tonic-gate 
1153*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1154*7c478bd9Sstevel@tonic-gate 	    "ugen_phys: bp=0x%p dev=0x%lx index=%d type=0x%x",
1155*7c478bd9Sstevel@tonic-gate 	    (void *)bp, dev, ep_index, minor_node_type);
1156*7c478bd9Sstevel@tonic-gate 
1157*7c478bd9Sstevel@tonic-gate 	switch (minor_node_type) {
1158*7c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_EP_XFER_NODE:
1159*7c478bd9Sstevel@tonic-gate 		switch (UGEN_XFER_TYPE(epp)) {
1160*7c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_BULK:
1161*7c478bd9Sstevel@tonic-gate 			if (bp->b_bcount > ugenp->ug_max_bulk_xfer_sz) {
1162*7c478bd9Sstevel@tonic-gate 				bp->b_bcount = ugenp->ug_max_bulk_xfer_sz;
1163*7c478bd9Sstevel@tonic-gate 			}
1164*7c478bd9Sstevel@tonic-gate 
1165*7c478bd9Sstevel@tonic-gate 			break;
1166*7c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_INTR:
1167*7c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_CONTROL:
1168*7c478bd9Sstevel@tonic-gate 		default:
1169*7c478bd9Sstevel@tonic-gate 
1170*7c478bd9Sstevel@tonic-gate 			break;
1171*7c478bd9Sstevel@tonic-gate 		}
1172*7c478bd9Sstevel@tonic-gate 		break;
1173*7c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_EP_STAT_NODE:
1174*7c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_DEV_STAT_NODE:
1175*7c478bd9Sstevel@tonic-gate 	default:
1176*7c478bd9Sstevel@tonic-gate 
1177*7c478bd9Sstevel@tonic-gate 		break;
1178*7c478bd9Sstevel@tonic-gate 	}
1179*7c478bd9Sstevel@tonic-gate }
1180*7c478bd9Sstevel@tonic-gate 
1181*7c478bd9Sstevel@tonic-gate 
1182*7c478bd9Sstevel@tonic-gate /*
1183*7c478bd9Sstevel@tonic-gate  * check whether flag is appropriate for node type
1184*7c478bd9Sstevel@tonic-gate  */
1185*7c478bd9Sstevel@tonic-gate static int
1186*7c478bd9Sstevel@tonic-gate ugen_check_open_flags(ugen_state_t *ugenp, dev_t dev, int flag)
1187*7c478bd9Sstevel@tonic-gate {
1188*7c478bd9Sstevel@tonic-gate 	ugen_ep_t *epp;
1189*7c478bd9Sstevel@tonic-gate 	int	minor_node_type = UGEN_MINOR_TYPE(ugenp, dev);
1190*7c478bd9Sstevel@tonic-gate 	int	rval = 0;
1191*7c478bd9Sstevel@tonic-gate 
1192*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1193*7c478bd9Sstevel@tonic-gate 	    "ugen_check_open_flags: "
1194*7c478bd9Sstevel@tonic-gate 	    "dev=0x%lx, type=0x%x flag=0x%x idx=%" PRIu64,
1195*7c478bd9Sstevel@tonic-gate 	    dev, minor_node_type, flag, UGEN_MINOR_EPIDX(ugenp, dev));
1196*7c478bd9Sstevel@tonic-gate 
1197*7c478bd9Sstevel@tonic-gate 	switch (minor_node_type) {
1198*7c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_EP_XFER_NODE:
1199*7c478bd9Sstevel@tonic-gate 		epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
1200*7c478bd9Sstevel@tonic-gate 		switch (UGEN_XFER_TYPE(epp)) {
1201*7c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_CONTROL:
1202*7c478bd9Sstevel@tonic-gate 			/* read and write must be set, ndelay not allowed */
1203*7c478bd9Sstevel@tonic-gate 			if (((flag & (FREAD | FWRITE)) != (FREAD | FWRITE)) ||
1204*7c478bd9Sstevel@tonic-gate 			    (flag & (FNDELAY | FNONBLOCK))) {
1205*7c478bd9Sstevel@tonic-gate 				rval = EACCES;
1206*7c478bd9Sstevel@tonic-gate 			}
1207*7c478bd9Sstevel@tonic-gate 
1208*7c478bd9Sstevel@tonic-gate 			break;
1209*7c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_BULK:
1210*7c478bd9Sstevel@tonic-gate 			/* ndelay not allowed */
1211*7c478bd9Sstevel@tonic-gate 			if (flag & (FNDELAY | FNONBLOCK)) {
1212*7c478bd9Sstevel@tonic-gate 				rval = EACCES;
1213*7c478bd9Sstevel@tonic-gate 
1214*7c478bd9Sstevel@tonic-gate 				break;
1215*7c478bd9Sstevel@tonic-gate 			}
1216*7c478bd9Sstevel@tonic-gate 			/*FALLTHRU*/
1217*7c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_ISOCH:
1218*7c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_INTR:
1219*7c478bd9Sstevel@tonic-gate 			/* check flag versus direction */
1220*7c478bd9Sstevel@tonic-gate 			if ((flag & FWRITE) &&
1221*7c478bd9Sstevel@tonic-gate 			    (UGEN_XFER_DIR(epp) & USB_EP_DIR_IN)) {
1222*7c478bd9Sstevel@tonic-gate 				rval = EACCES;
1223*7c478bd9Sstevel@tonic-gate 			}
1224*7c478bd9Sstevel@tonic-gate 			if ((flag & FREAD) &&
1225*7c478bd9Sstevel@tonic-gate 			    ((UGEN_XFER_DIR(epp) & USB_EP_DIR_IN) == 0)) {
1226*7c478bd9Sstevel@tonic-gate 				rval = EACCES;
1227*7c478bd9Sstevel@tonic-gate 			}
1228*7c478bd9Sstevel@tonic-gate 
1229*7c478bd9Sstevel@tonic-gate 			break;
1230*7c478bd9Sstevel@tonic-gate 		default:
1231*7c478bd9Sstevel@tonic-gate 			rval = EINVAL;
1232*7c478bd9Sstevel@tonic-gate 
1233*7c478bd9Sstevel@tonic-gate 			break;
1234*7c478bd9Sstevel@tonic-gate 		}
1235*7c478bd9Sstevel@tonic-gate 		break;
1236*7c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_DEV_STAT_NODE:
1237*7c478bd9Sstevel@tonic-gate 		/* only reads are supported */
1238*7c478bd9Sstevel@tonic-gate 		if (flag & FWRITE) {
1239*7c478bd9Sstevel@tonic-gate 			rval = EACCES;
1240*7c478bd9Sstevel@tonic-gate 		}
1241*7c478bd9Sstevel@tonic-gate 
1242*7c478bd9Sstevel@tonic-gate 		break;
1243*7c478bd9Sstevel@tonic-gate 	case UGEN_MINOR_EP_STAT_NODE:
1244*7c478bd9Sstevel@tonic-gate 
1245*7c478bd9Sstevel@tonic-gate 		break;
1246*7c478bd9Sstevel@tonic-gate 	default:
1247*7c478bd9Sstevel@tonic-gate 		rval = EINVAL;
1248*7c478bd9Sstevel@tonic-gate 
1249*7c478bd9Sstevel@tonic-gate 		break;
1250*7c478bd9Sstevel@tonic-gate 	}
1251*7c478bd9Sstevel@tonic-gate 
1252*7c478bd9Sstevel@tonic-gate 	return (rval);
1253*7c478bd9Sstevel@tonic-gate }
1254*7c478bd9Sstevel@tonic-gate 
1255*7c478bd9Sstevel@tonic-gate 
1256*7c478bd9Sstevel@tonic-gate /*
1257*7c478bd9Sstevel@tonic-gate  * endpoint management
1258*7c478bd9Sstevel@tonic-gate  *
1259*7c478bd9Sstevel@tonic-gate  * create/initialize all endpoint xfer/stat structures
1260*7c478bd9Sstevel@tonic-gate  */
1261*7c478bd9Sstevel@tonic-gate static int
1262*7c478bd9Sstevel@tonic-gate ugen_epxs_init(ugen_state_t *ugenp)
1263*7c478bd9Sstevel@tonic-gate {
1264*7c478bd9Sstevel@tonic-gate 	usb_cfg_data_t	*dev_cfg = ugenp->ug_dev_data->dev_cfg;
1265*7c478bd9Sstevel@tonic-gate 	uchar_t		cfgidx, cfgval, iface, alt, ep;
1266*7c478bd9Sstevel@tonic-gate 	usb_if_data_t	*if_data;
1267*7c478bd9Sstevel@tonic-gate 	usb_alt_if_data_t *alt_if_data;
1268*7c478bd9Sstevel@tonic-gate 	usb_ep_data_t	*ep_data;
1269*7c478bd9Sstevel@tonic-gate 
1270*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1271*7c478bd9Sstevel@tonic-gate 	    "ugen_epxs_init:");
1272*7c478bd9Sstevel@tonic-gate 
1273*7c478bd9Sstevel@tonic-gate 	/* initialize each ep's mutex first */
1274*7c478bd9Sstevel@tonic-gate 	for (ep = 0; ep < UGEN_N_ENDPOINTS; ep++) {
1275*7c478bd9Sstevel@tonic-gate 		mutex_init(&ugenp->ug_ep[ep].ep_mutex, NULL, MUTEX_DRIVER,
1276*7c478bd9Sstevel@tonic-gate 		    ugenp->ug_dev_data->dev_iblock_cookie);
1277*7c478bd9Sstevel@tonic-gate 	}
1278*7c478bd9Sstevel@tonic-gate 
1279*7c478bd9Sstevel@tonic-gate 	/* init default ep as it does not have a descriptor */
1280*7c478bd9Sstevel@tonic-gate 	if (ugen_epxs_data_init(ugenp, NULL, 0, 0,
1281*7c478bd9Sstevel@tonic-gate 	    ugenp->ug_dev_data->dev_curr_if, 0) != USB_SUCCESS) {
1282*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
1283*7c478bd9Sstevel@tonic-gate 		    "creating default endpoint failed");
1284*7c478bd9Sstevel@tonic-gate 
1285*7c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
1286*7c478bd9Sstevel@tonic-gate 	}
1287*7c478bd9Sstevel@tonic-gate 
1288*7c478bd9Sstevel@tonic-gate 	/*
1289*7c478bd9Sstevel@tonic-gate 	 * walk all endpoints of all alternates of all interfaces of
1290*7c478bd9Sstevel@tonic-gate 	 * all cfs
1291*7c478bd9Sstevel@tonic-gate 	 */
1292*7c478bd9Sstevel@tonic-gate 	for (cfgidx = 0; cfgidx < ugenp->ug_dev_data->dev_n_cfg; cfgidx++) {
1293*7c478bd9Sstevel@tonic-gate 		dev_cfg = &ugenp->ug_dev_data->dev_cfg[cfgidx];
1294*7c478bd9Sstevel@tonic-gate 		cfgval = dev_cfg->cfg_descr.bConfigurationValue;
1295*7c478bd9Sstevel@tonic-gate 		for (iface = 0; iface < dev_cfg->cfg_n_if; iface++) {
1296*7c478bd9Sstevel@tonic-gate 			if_data = &dev_cfg->cfg_if[iface];
1297*7c478bd9Sstevel@tonic-gate 			for (alt = 0; alt < if_data->if_n_alt; alt++) {
1298*7c478bd9Sstevel@tonic-gate 				alt_if_data = &if_data->if_alt[alt];
1299*7c478bd9Sstevel@tonic-gate 				for (ep = 0; ep < alt_if_data->altif_n_ep;
1300*7c478bd9Sstevel@tonic-gate 				    ep++) {
1301*7c478bd9Sstevel@tonic-gate 					ep_data = &alt_if_data->altif_ep[ep];
1302*7c478bd9Sstevel@tonic-gate 					if (ugen_epxs_data_init(ugenp, ep_data,
1303*7c478bd9Sstevel@tonic-gate 					    cfgval, cfgidx, iface, alt) !=
1304*7c478bd9Sstevel@tonic-gate 					    USB_SUCCESS) {
1305*7c478bd9Sstevel@tonic-gate 
1306*7c478bd9Sstevel@tonic-gate 						return (USB_FAILURE);
1307*7c478bd9Sstevel@tonic-gate 					}
1308*7c478bd9Sstevel@tonic-gate 				}
1309*7c478bd9Sstevel@tonic-gate 			}
1310*7c478bd9Sstevel@tonic-gate 		}
1311*7c478bd9Sstevel@tonic-gate 	}
1312*7c478bd9Sstevel@tonic-gate 
1313*7c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
1314*7c478bd9Sstevel@tonic-gate }
1315*7c478bd9Sstevel@tonic-gate 
1316*7c478bd9Sstevel@tonic-gate 
1317*7c478bd9Sstevel@tonic-gate /*
1318*7c478bd9Sstevel@tonic-gate  * initialize one endpoint structure
1319*7c478bd9Sstevel@tonic-gate  */
1320*7c478bd9Sstevel@tonic-gate static int
1321*7c478bd9Sstevel@tonic-gate ugen_epxs_data_init(ugen_state_t *ugenp, usb_ep_data_t *ep_data,
1322*7c478bd9Sstevel@tonic-gate 	uchar_t cfgval, uchar_t cfgidx, uchar_t iface, uchar_t alt)
1323*7c478bd9Sstevel@tonic-gate {
1324*7c478bd9Sstevel@tonic-gate 	int			ep_index;
1325*7c478bd9Sstevel@tonic-gate 	ugen_ep_t		*epp;
1326*7c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*ep_descr;
1327*7c478bd9Sstevel@tonic-gate 
1328*7c478bd9Sstevel@tonic-gate 	/* is this the default endpoint */
1329*7c478bd9Sstevel@tonic-gate 	ep_index = (ep_data == NULL) ? 0 :
1330*7c478bd9Sstevel@tonic-gate 		    usb_get_ep_index(ep_data->ep_descr.bEndpointAddress);
1331*7c478bd9Sstevel@tonic-gate 	epp = &ugenp->ug_ep[ep_index];
1332*7c478bd9Sstevel@tonic-gate 
1333*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1334*7c478bd9Sstevel@tonic-gate 	    "ugen_epxs_data_init: "
1335*7c478bd9Sstevel@tonic-gate 	    "cfgval=%d cfgidx=%d iface=%d alt=%d ep_index=%d",
1336*7c478bd9Sstevel@tonic-gate 	    cfgval, cfgidx, iface, alt, ep_index);
1337*7c478bd9Sstevel@tonic-gate 
1338*7c478bd9Sstevel@tonic-gate 	ep_descr = (ep_data == NULL) ? &ugen_default_ep_descr :
1339*7c478bd9Sstevel@tonic-gate 						&ep_data->ep_descr;
1340*7c478bd9Sstevel@tonic-gate 
1341*7c478bd9Sstevel@tonic-gate 	mutex_init(&epp->ep_mutex, NULL, MUTEX_DRIVER,
1342*7c478bd9Sstevel@tonic-gate 		    ugenp->ug_dev_data->dev_iblock_cookie);
1343*7c478bd9Sstevel@tonic-gate 
1344*7c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
1345*7c478bd9Sstevel@tonic-gate 
1346*7c478bd9Sstevel@tonic-gate 	/* initialize if not yet init'ed */
1347*7c478bd9Sstevel@tonic-gate 	if (epp->ep_state == UGEN_EP_STATE_NONE) {
1348*7c478bd9Sstevel@tonic-gate 		epp->ep_descr		= *ep_descr;
1349*7c478bd9Sstevel@tonic-gate 		epp->ep_cfgidx		= cfgidx;
1350*7c478bd9Sstevel@tonic-gate 		epp->ep_if		= iface;
1351*7c478bd9Sstevel@tonic-gate 		epp->ep_alt		= alt;
1352*7c478bd9Sstevel@tonic-gate 		epp->ep_state		= UGEN_EP_STATE_ACTIVE;
1353*7c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status	= USB_LC_STAT_NOERROR;
1354*7c478bd9Sstevel@tonic-gate 		epp->ep_pipe_policy.pp_max_async_reqs = 1;
1355*7c478bd9Sstevel@tonic-gate 
1356*7c478bd9Sstevel@tonic-gate 		cv_init(&epp->ep_wait_cv, NULL, CV_DRIVER, NULL);
1357*7c478bd9Sstevel@tonic-gate 		epp->ep_ser_cookie	= usb_init_serialization(
1358*7c478bd9Sstevel@tonic-gate 						ugenp->ug_dip, 0);
1359*7c478bd9Sstevel@tonic-gate 	}
1360*7c478bd9Sstevel@tonic-gate 
1361*7c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
1362*7c478bd9Sstevel@tonic-gate 
1363*7c478bd9Sstevel@tonic-gate 	/* create minor nodes for all alts */
1364*7c478bd9Sstevel@tonic-gate 
1365*7c478bd9Sstevel@tonic-gate 	return (ugen_epxs_minor_nodes_create(ugenp, ep_descr,
1366*7c478bd9Sstevel@tonic-gate 	    cfgval, cfgidx, iface, alt));
1367*7c478bd9Sstevel@tonic-gate }
1368*7c478bd9Sstevel@tonic-gate 
1369*7c478bd9Sstevel@tonic-gate 
1370*7c478bd9Sstevel@tonic-gate /*
1371*7c478bd9Sstevel@tonic-gate  * undo all endpoint initializations
1372*7c478bd9Sstevel@tonic-gate  */
1373*7c478bd9Sstevel@tonic-gate static void
1374*7c478bd9Sstevel@tonic-gate ugen_epxs_destroy(ugen_state_t *ugenp)
1375*7c478bd9Sstevel@tonic-gate {
1376*7c478bd9Sstevel@tonic-gate 	int	i;
1377*7c478bd9Sstevel@tonic-gate 
1378*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < UGEN_N_ENDPOINTS; i++) {
1379*7c478bd9Sstevel@tonic-gate 		ugen_epxs_data_destroy(ugenp, &ugenp->ug_ep[i]);
1380*7c478bd9Sstevel@tonic-gate 	}
1381*7c478bd9Sstevel@tonic-gate }
1382*7c478bd9Sstevel@tonic-gate 
1383*7c478bd9Sstevel@tonic-gate 
1384*7c478bd9Sstevel@tonic-gate static void
1385*7c478bd9Sstevel@tonic-gate ugen_epxs_data_destroy(ugen_state_t *ugenp, ugen_ep_t *epp)
1386*7c478bd9Sstevel@tonic-gate {
1387*7c478bd9Sstevel@tonic-gate 	if (epp) {
1388*7c478bd9Sstevel@tonic-gate 		ASSERT(epp->ep_ph == NULL);
1389*7c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
1390*7c478bd9Sstevel@tonic-gate 		if (epp->ep_state != UGEN_EP_STATE_NONE) {
1391*7c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1392*7c478bd9Sstevel@tonic-gate 			    "ugen_epxs_destroy: addr=0x%x",
1393*7c478bd9Sstevel@tonic-gate 			    UGEN_XFER_ADDR(epp));
1394*7c478bd9Sstevel@tonic-gate 			cv_destroy(&epp->ep_wait_cv);
1395*7c478bd9Sstevel@tonic-gate 		}
1396*7c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
1397*7c478bd9Sstevel@tonic-gate 
1398*7c478bd9Sstevel@tonic-gate 		mutex_destroy(&epp->ep_mutex);
1399*7c478bd9Sstevel@tonic-gate 		usb_fini_serialization(epp->ep_ser_cookie);
1400*7c478bd9Sstevel@tonic-gate 	}
1401*7c478bd9Sstevel@tonic-gate }
1402*7c478bd9Sstevel@tonic-gate 
1403*7c478bd9Sstevel@tonic-gate 
1404*7c478bd9Sstevel@tonic-gate /*
1405*7c478bd9Sstevel@tonic-gate  * create endpoint status and xfer minor nodes
1406*7c478bd9Sstevel@tonic-gate  *
1407*7c478bd9Sstevel@tonic-gate  * The actual minor node needs more than 18 bits. We create a table
1408*7c478bd9Sstevel@tonic-gate  * and store the full minor node in this table and use the
1409*7c478bd9Sstevel@tonic-gate  * index in the table as minor node. This allows 256 minor nodes
1410*7c478bd9Sstevel@tonic-gate  * and 1024 instances
1411*7c478bd9Sstevel@tonic-gate  */
1412*7c478bd9Sstevel@tonic-gate static int
1413*7c478bd9Sstevel@tonic-gate ugen_epxs_minor_nodes_create(ugen_state_t *ugenp, usb_ep_descr_t *ep_descr,
1414*7c478bd9Sstevel@tonic-gate     uchar_t cfgval, uchar_t cfgidx, uchar_t iface, uchar_t alt)
1415*7c478bd9Sstevel@tonic-gate {
1416*7c478bd9Sstevel@tonic-gate 	char		node_name[32], *type;
1417*7c478bd9Sstevel@tonic-gate 	int		vid = ugenp->ug_dev_data->dev_descr->idVendor;
1418*7c478bd9Sstevel@tonic-gate 	int		pid = ugenp->ug_dev_data->dev_descr->idProduct;
1419*7c478bd9Sstevel@tonic-gate 	minor_t		minor;
1420*7c478bd9Sstevel@tonic-gate 	int		minor_index;
1421*7c478bd9Sstevel@tonic-gate 	ugen_minor_t	minor_code, minor_code_base;
1422*7c478bd9Sstevel@tonic-gate 	int		owns_device = (usb_owns_device(ugenp->ug_dip) ?
1423*7c478bd9Sstevel@tonic-gate 						    UGEN_OWNS_DEVICE : 0);
1424*7c478bd9Sstevel@tonic-gate 	int		ep_index =
1425*7c478bd9Sstevel@tonic-gate 			    usb_get_ep_index(ep_descr->bEndpointAddress);
1426*7c478bd9Sstevel@tonic-gate 	int		ep_addr =
1427*7c478bd9Sstevel@tonic-gate 			    ep_descr->bEndpointAddress & USB_EP_NUM_MASK;
1428*7c478bd9Sstevel@tonic-gate 	int		ep_type =
1429*7c478bd9Sstevel@tonic-gate 			    ep_descr->bmAttributes & USB_EP_ATTR_MASK;
1430*7c478bd9Sstevel@tonic-gate 	int		ep_dir =
1431*7c478bd9Sstevel@tonic-gate 			    ep_descr->bEndpointAddress & USB_EP_DIR_IN;
1432*7c478bd9Sstevel@tonic-gate 
1433*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
1434*7c478bd9Sstevel@tonic-gate 	    "ugen_epxs_minor_nodes_create: "
1435*7c478bd9Sstevel@tonic-gate 	    "cfgval=%d cfgidx=%d if=%d alt=%d ep=0x%x",
1436*7c478bd9Sstevel@tonic-gate 	    cfgval, cfgidx, iface, alt, ep_addr);
1437*7c478bd9Sstevel@tonic-gate 
1438*7c478bd9Sstevel@tonic-gate 	if (ugenp->ug_instance >= UGEN_MINOR_INSTANCE_LIMIT(ugenp)) {
1439*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L0(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
1440*7c478bd9Sstevel@tonic-gate 		    "instance number too high (%d)", ugenp->ug_instance);
1441*7c478bd9Sstevel@tonic-gate 
1442*7c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
1443*7c478bd9Sstevel@tonic-gate 	}
1444*7c478bd9Sstevel@tonic-gate 
1445*7c478bd9Sstevel@tonic-gate 	/* create stat and xfer minor node */
1446*7c478bd9Sstevel@tonic-gate 	minor_code_base =
1447*7c478bd9Sstevel@tonic-gate 		((ugen_minor_t)cfgval) << UGEN_MINOR_CFGVAL_SHIFT |
1448*7c478bd9Sstevel@tonic-gate 		((ugen_minor_t)cfgidx) << UGEN_MINOR_CFGIDX_SHIFT |
1449*7c478bd9Sstevel@tonic-gate 		iface << UGEN_MINOR_IF_SHIFT |
1450*7c478bd9Sstevel@tonic-gate 		alt << UGEN_MINOR_ALT_SHIFT |
1451*7c478bd9Sstevel@tonic-gate 		ep_index << UGEN_MINOR_EPIDX_SHIFT | owns_device;
1452*7c478bd9Sstevel@tonic-gate 	minor_code = minor_code_base | UGEN_MINOR_EP_XFER_NODE;
1453*7c478bd9Sstevel@tonic-gate 
1454*7c478bd9Sstevel@tonic-gate 	minor_index = ugen_minor_index_create(ugenp, minor_code);
1455*7c478bd9Sstevel@tonic-gate 	if (minor_index < 0) {
1456*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L1(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
1457*7c478bd9Sstevel@tonic-gate 		    "too many minor nodes, "
1458*7c478bd9Sstevel@tonic-gate 		    "cannot create %d.%d.%d.%x",
1459*7c478bd9Sstevel@tonic-gate 		    cfgval, iface, alt, ep_addr);
1460*7c478bd9Sstevel@tonic-gate 		/* carry on regardless */
1461*7c478bd9Sstevel@tonic-gate 
1462*7c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
1463*7c478bd9Sstevel@tonic-gate 	}
1464*7c478bd9Sstevel@tonic-gate 	minor = (minor_index << UGEN_MINOR_IDX_SHIFT(ugenp)) |
1465*7c478bd9Sstevel@tonic-gate 		ugenp->ug_instance << UGEN_MINOR_INSTANCE_SHIFT(ugenp);
1466*7c478bd9Sstevel@tonic-gate 
1467*7c478bd9Sstevel@tonic-gate 	if (ep_type == USB_EP_ATTR_CONTROL) {
1468*7c478bd9Sstevel@tonic-gate 		type = "cntrl";
1469*7c478bd9Sstevel@tonic-gate 	} else {
1470*7c478bd9Sstevel@tonic-gate 		type = (ep_dir & USB_EP_DIR_IN) ? "in" : "out";
1471*7c478bd9Sstevel@tonic-gate 	}
1472*7c478bd9Sstevel@tonic-gate 
1473*7c478bd9Sstevel@tonic-gate 	/*
1474*7c478bd9Sstevel@tonic-gate 	 * xfer ep node name:
1475*7c478bd9Sstevel@tonic-gate 	 * vid.pid.[in|out|cntrl].[<cfg>.][if<iface>.][<alt>.]<ep addr>
1476*7c478bd9Sstevel@tonic-gate 	 */
1477*7c478bd9Sstevel@tonic-gate 	if ((ep_addr == 0) && owns_device) {
1478*7c478bd9Sstevel@tonic-gate 		(void) sprintf(node_name, "%x.%x.%s%d",
1479*7c478bd9Sstevel@tonic-gate 		    vid, pid, type, ep_addr);
1480*7c478bd9Sstevel@tonic-gate 	} else if (cfgidx == 0 && alt == 0) {
1481*7c478bd9Sstevel@tonic-gate 		(void) sprintf(node_name, "%x.%x.if%d%s%d",
1482*7c478bd9Sstevel@tonic-gate 		    vid, pid, iface, type, ep_addr);
1483*7c478bd9Sstevel@tonic-gate 	} else if (cfgidx == 0 && alt != 0) {
1484*7c478bd9Sstevel@tonic-gate 		(void) sprintf(node_name, "%x.%x.if%d.%d%s%d",
1485*7c478bd9Sstevel@tonic-gate 		    vid, pid, iface, alt, type, ep_addr);
1486*7c478bd9Sstevel@tonic-gate 	} else if (cfgidx != 0 && alt == 0) {
1487*7c478bd9Sstevel@tonic-gate 		(void) sprintf(node_name, "%x.%x.cfg%dif%d%s%d",
1488*7c478bd9Sstevel@tonic-gate 		    vid, pid, cfgval, iface, type, ep_addr);
1489*7c478bd9Sstevel@tonic-gate 	} else if (cfgidx != 0 && alt != 0) {
1490*7c478bd9Sstevel@tonic-gate 		(void) sprintf(node_name, "%x.%x.cfg%dif%d.%d%s%d",
1491*7c478bd9Sstevel@tonic-gate 		    vid, pid, cfgval, iface, alt,
1492*7c478bd9Sstevel@tonic-gate 		    type, ep_addr);
1493*7c478bd9Sstevel@tonic-gate 	}
1494*7c478bd9Sstevel@tonic-gate 
1495*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
1496*7c478bd9Sstevel@tonic-gate 	    "minor=0x%x index=%d code=0x%" PRIx64 " name=%s",
1497*7c478bd9Sstevel@tonic-gate 	    minor, minor_index, minor_code, node_name);
1498*7c478bd9Sstevel@tonic-gate 
1499*7c478bd9Sstevel@tonic-gate 	ASSERT(minor < L_MAXMIN);
1500*7c478bd9Sstevel@tonic-gate 
1501*7c478bd9Sstevel@tonic-gate 	if ((ddi_create_minor_node(ugenp->ug_dip, node_name,
1502*7c478bd9Sstevel@tonic-gate 	    S_IFCHR, minor, DDI_NT_UGEN, 0)) != DDI_SUCCESS) {
1503*7c478bd9Sstevel@tonic-gate 
1504*7c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
1505*7c478bd9Sstevel@tonic-gate 	}
1506*7c478bd9Sstevel@tonic-gate 
1507*7c478bd9Sstevel@tonic-gate 	ugen_store_devt(ugenp, minor);
1508*7c478bd9Sstevel@tonic-gate 
1509*7c478bd9Sstevel@tonic-gate 	minor_code = minor_code_base | UGEN_MINOR_EP_STAT_NODE;
1510*7c478bd9Sstevel@tonic-gate 	minor_index = ugen_minor_index_create(ugenp, minor_code);
1511*7c478bd9Sstevel@tonic-gate 	if (minor_index < 0) {
1512*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L1(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
1513*7c478bd9Sstevel@tonic-gate 		    "too many minor nodes, "
1514*7c478bd9Sstevel@tonic-gate 		    "cannot create %d.%d.%d.%x stat",
1515*7c478bd9Sstevel@tonic-gate 		    cfgval, iface, alt,
1516*7c478bd9Sstevel@tonic-gate 		    ep_descr->bEndpointAddress);
1517*7c478bd9Sstevel@tonic-gate 		/* carry on regardless */
1518*7c478bd9Sstevel@tonic-gate 
1519*7c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
1520*7c478bd9Sstevel@tonic-gate 	}
1521*7c478bd9Sstevel@tonic-gate 	minor = (minor_index << UGEN_MINOR_IDX_SHIFT(ugenp)) |
1522*7c478bd9Sstevel@tonic-gate 		ugenp->ug_instance << UGEN_MINOR_INSTANCE_SHIFT(ugenp);
1523*7c478bd9Sstevel@tonic-gate 
1524*7c478bd9Sstevel@tonic-gate 	(void) strcat(node_name, "stat");
1525*7c478bd9Sstevel@tonic-gate 
1526*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
1527*7c478bd9Sstevel@tonic-gate 	    "minor=0x%x index=%d code=0x%" PRIx64 " name=%s",
1528*7c478bd9Sstevel@tonic-gate 	    minor, minor_index, minor_code, node_name);
1529*7c478bd9Sstevel@tonic-gate 
1530*7c478bd9Sstevel@tonic-gate 	ASSERT(minor < L_MAXMIN);
1531*7c478bd9Sstevel@tonic-gate 
1532*7c478bd9Sstevel@tonic-gate 	if ((ddi_create_minor_node(ugenp->ug_dip, node_name,
1533*7c478bd9Sstevel@tonic-gate 	    S_IFCHR, minor, DDI_NT_UGEN, 0)) != DDI_SUCCESS) {
1534*7c478bd9Sstevel@tonic-gate 
1535*7c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
1536*7c478bd9Sstevel@tonic-gate 	}
1537*7c478bd9Sstevel@tonic-gate 
1538*7c478bd9Sstevel@tonic-gate 	ugen_store_devt(ugenp, minor);
1539*7c478bd9Sstevel@tonic-gate 
1540*7c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
1541*7c478bd9Sstevel@tonic-gate }
1542*7c478bd9Sstevel@tonic-gate 
1543*7c478bd9Sstevel@tonic-gate 
1544*7c478bd9Sstevel@tonic-gate /*
1545*7c478bd9Sstevel@tonic-gate  * close all non-default pipes and drain default pipe
1546*7c478bd9Sstevel@tonic-gate  */
1547*7c478bd9Sstevel@tonic-gate static void
1548*7c478bd9Sstevel@tonic-gate ugen_epx_shutdown(ugen_state_t *ugenp)
1549*7c478bd9Sstevel@tonic-gate {
1550*7c478bd9Sstevel@tonic-gate 	int	i;
1551*7c478bd9Sstevel@tonic-gate 
1552*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1553*7c478bd9Sstevel@tonic-gate 	    "ugen_epx_shutdown:");
1554*7c478bd9Sstevel@tonic-gate 
1555*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < UGEN_N_ENDPOINTS; i++) {
1556*7c478bd9Sstevel@tonic-gate 		ugen_ep_t *epp = &ugenp->ug_ep[i];
1557*7c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
1558*7c478bd9Sstevel@tonic-gate 		if (epp->ep_state != UGEN_EP_STATE_NONE) {
1559*7c478bd9Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
1560*7c478bd9Sstevel@tonic-gate 			(void) usb_serialize_access(epp->ep_ser_cookie,
1561*7c478bd9Sstevel@tonic-gate 							USB_WAIT, 0);
1562*7c478bd9Sstevel@tonic-gate 			(void) ugen_epx_close_pipe(ugenp, epp);
1563*7c478bd9Sstevel@tonic-gate 			usb_release_access(epp->ep_ser_cookie);
1564*7c478bd9Sstevel@tonic-gate 		} else {
1565*7c478bd9Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
1566*7c478bd9Sstevel@tonic-gate 		}
1567*7c478bd9Sstevel@tonic-gate 	}
1568*7c478bd9Sstevel@tonic-gate }
1569*7c478bd9Sstevel@tonic-gate 
1570*7c478bd9Sstevel@tonic-gate 
1571*7c478bd9Sstevel@tonic-gate /*
1572*7c478bd9Sstevel@tonic-gate  * find cfg index corresponding to cfg value
1573*7c478bd9Sstevel@tonic-gate  */
1574*7c478bd9Sstevel@tonic-gate static int
1575*7c478bd9Sstevel@tonic-gate ugen_cfgval2idx(ugen_state_t *ugenp, uint_t cfgval)
1576*7c478bd9Sstevel@tonic-gate {
1577*7c478bd9Sstevel@tonic-gate 	usb_cfg_data_t	*dev_cfg = ugenp->ug_dev_data->dev_cfg;
1578*7c478bd9Sstevel@tonic-gate 	int		cfgidx;
1579*7c478bd9Sstevel@tonic-gate 
1580*7c478bd9Sstevel@tonic-gate 	for (cfgidx = 0; cfgidx < ugenp->ug_dev_data->dev_n_cfg; cfgidx++) {
1581*7c478bd9Sstevel@tonic-gate 		dev_cfg = &ugenp->ug_dev_data->dev_cfg[cfgidx];
1582*7c478bd9Sstevel@tonic-gate 		if (cfgval == dev_cfg->cfg_descr.bConfigurationValue) {
1583*7c478bd9Sstevel@tonic-gate 
1584*7c478bd9Sstevel@tonic-gate 			return (cfgidx);
1585*7c478bd9Sstevel@tonic-gate 		}
1586*7c478bd9Sstevel@tonic-gate 	}
1587*7c478bd9Sstevel@tonic-gate 
1588*7c478bd9Sstevel@tonic-gate 	ASSERT(cfgidx < ugenp->ug_dev_data->dev_n_cfg);
1589*7c478bd9Sstevel@tonic-gate 
1590*7c478bd9Sstevel@tonic-gate 	return (0);
1591*7c478bd9Sstevel@tonic-gate }
1592*7c478bd9Sstevel@tonic-gate 
1593*7c478bd9Sstevel@tonic-gate 
1594*7c478bd9Sstevel@tonic-gate /*
1595*7c478bd9Sstevel@tonic-gate  * check if any node is open
1596*7c478bd9Sstevel@tonic-gate  */
1597*7c478bd9Sstevel@tonic-gate static int
1598*7c478bd9Sstevel@tonic-gate ugen_epxs_check_open_nodes(ugen_state_t *ugenp)
1599*7c478bd9Sstevel@tonic-gate {
1600*7c478bd9Sstevel@tonic-gate 	int	i;
1601*7c478bd9Sstevel@tonic-gate 
1602*7c478bd9Sstevel@tonic-gate 	for (i = 1; i < UGEN_N_ENDPOINTS; i++) {
1603*7c478bd9Sstevel@tonic-gate 		ugen_ep_t *epp = &ugenp->ug_ep[i];
1604*7c478bd9Sstevel@tonic-gate 
1605*7c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
1606*7c478bd9Sstevel@tonic-gate 
1607*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1608*7c478bd9Sstevel@tonic-gate 		    "ugen_epxs_check_open_nodes: epp=%d, ep_state=0x%x",
1609*7c478bd9Sstevel@tonic-gate 		    i, epp->ep_state);
1610*7c478bd9Sstevel@tonic-gate 
1611*7c478bd9Sstevel@tonic-gate 		if (epp->ep_state & UGEN_EP_STATE_XS_OPEN) {
1612*7c478bd9Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
1613*7c478bd9Sstevel@tonic-gate 
1614*7c478bd9Sstevel@tonic-gate 			return (USB_SUCCESS);
1615*7c478bd9Sstevel@tonic-gate 		}
1616*7c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
1617*7c478bd9Sstevel@tonic-gate 	}
1618*7c478bd9Sstevel@tonic-gate 
1619*7c478bd9Sstevel@tonic-gate 	return (USB_FAILURE);
1620*7c478bd9Sstevel@tonic-gate }
1621*7c478bd9Sstevel@tonic-gate 
1622*7c478bd9Sstevel@tonic-gate 
1623*7c478bd9Sstevel@tonic-gate /*
1624*7c478bd9Sstevel@tonic-gate  * check if we can switch alternate
1625*7c478bd9Sstevel@tonic-gate  */
1626*7c478bd9Sstevel@tonic-gate static int
1627*7c478bd9Sstevel@tonic-gate ugen_epxs_check_alt_switch(ugen_state_t *ugenp, uchar_t iface, uchar_t cfgidx)
1628*7c478bd9Sstevel@tonic-gate {
1629*7c478bd9Sstevel@tonic-gate 	int	i;
1630*7c478bd9Sstevel@tonic-gate 
1631*7c478bd9Sstevel@tonic-gate 	for (i = 1; i < UGEN_N_ENDPOINTS; i++) {
1632*7c478bd9Sstevel@tonic-gate 		ugen_ep_t *epp = &ugenp->ug_ep[i];
1633*7c478bd9Sstevel@tonic-gate 
1634*7c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
1635*7c478bd9Sstevel@tonic-gate 
1636*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1637*7c478bd9Sstevel@tonic-gate 		    "ugen_epxs_check_alt_switch: epp=%d, ep_state=0x%x",
1638*7c478bd9Sstevel@tonic-gate 		    i, epp->ep_state);
1639*7c478bd9Sstevel@tonic-gate 
1640*7c478bd9Sstevel@tonic-gate 		/*
1641*7c478bd9Sstevel@tonic-gate 		 * if the endpoint is open and part of this cfg and interface
1642*7c478bd9Sstevel@tonic-gate 		 * then we cannot switch alternates
1643*7c478bd9Sstevel@tonic-gate 		 */
1644*7c478bd9Sstevel@tonic-gate 		if ((epp->ep_state & UGEN_EP_STATE_XS_OPEN) &&
1645*7c478bd9Sstevel@tonic-gate 		    (epp->ep_cfgidx == cfgidx) &&
1646*7c478bd9Sstevel@tonic-gate 		    (epp->ep_if == iface)) {
1647*7c478bd9Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
1648*7c478bd9Sstevel@tonic-gate 
1649*7c478bd9Sstevel@tonic-gate 			return (USB_FAILURE);
1650*7c478bd9Sstevel@tonic-gate 		}
1651*7c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
1652*7c478bd9Sstevel@tonic-gate 	}
1653*7c478bd9Sstevel@tonic-gate 
1654*7c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
1655*7c478bd9Sstevel@tonic-gate }
1656*7c478bd9Sstevel@tonic-gate 
1657*7c478bd9Sstevel@tonic-gate 
1658*7c478bd9Sstevel@tonic-gate /*
1659*7c478bd9Sstevel@tonic-gate  * implicit switch to new cfg and alt
1660*7c478bd9Sstevel@tonic-gate  * If a crummy device fails usb_get_cfg or usb_get_alt_if, we carry on
1661*7c478bd9Sstevel@tonic-gate  * regardless so at least the device can be opened.
1662*7c478bd9Sstevel@tonic-gate  */
1663*7c478bd9Sstevel@tonic-gate static int
1664*7c478bd9Sstevel@tonic-gate ugen_epxs_switch_cfg_alt(ugen_state_t *ugenp, ugen_ep_t *epp, dev_t dev)
1665*7c478bd9Sstevel@tonic-gate {
1666*7c478bd9Sstevel@tonic-gate 	int	rval = USB_SUCCESS;
1667*7c478bd9Sstevel@tonic-gate 	uint_t	alt;
1668*7c478bd9Sstevel@tonic-gate 	uint_t	new_alt = UGEN_MINOR_ALT(ugenp, dev);
1669*7c478bd9Sstevel@tonic-gate 	uint_t	new_if = UGEN_MINOR_IF(ugenp, dev);
1670*7c478bd9Sstevel@tonic-gate 	uint_t	cur_if = epp->ep_if;
1671*7c478bd9Sstevel@tonic-gate 	uint_t	new_cfgidx = UGEN_MINOR_CFGIDX(ugenp, dev);
1672*7c478bd9Sstevel@tonic-gate 	uint_t	cur_cfgidx;
1673*7c478bd9Sstevel@tonic-gate 	uint_t	cfgval;
1674*7c478bd9Sstevel@tonic-gate 	int	switched = 0;
1675*7c478bd9Sstevel@tonic-gate 
1676*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1677*7c478bd9Sstevel@tonic-gate 	    "ugen_epxs_switch_cfg_alt: old cfgidx=%d, if=%d alt=%d",
1678*7c478bd9Sstevel@tonic-gate 	    epp->ep_cfgidx, epp->ep_if, epp->ep_alt);
1679*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1680*7c478bd9Sstevel@tonic-gate 	    "new cfgidx=%d, if=%d alt=%d ep_state=0x%x",
1681*7c478bd9Sstevel@tonic-gate 	    new_cfgidx, new_if, new_alt, epp->ep_state);
1682*7c478bd9Sstevel@tonic-gate 
1683*7c478bd9Sstevel@tonic-gate 	/* no need to switch if there is only 1 cfg, 1 iface and no alts */
1684*7c478bd9Sstevel@tonic-gate 	if ((new_if == 0) && (new_alt == 0) &&
1685*7c478bd9Sstevel@tonic-gate 	    (ugenp->ug_dev_data->dev_n_cfg == 1) &&
1686*7c478bd9Sstevel@tonic-gate 	    (ugenp->ug_dev_data->dev_cfg[0].cfg_n_if == 1) &&
1687*7c478bd9Sstevel@tonic-gate 	    (ugenp->ug_dev_data->
1688*7c478bd9Sstevel@tonic-gate 	    dev_cfg[0].cfg_if[new_if].if_n_alt == 1)) {
1689*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1690*7c478bd9Sstevel@tonic-gate 		    "no need for switching: n_cfg=%d n_alt=%d",
1691*7c478bd9Sstevel@tonic-gate 		    ugenp->ug_dev_data->dev_n_cfg,
1692*7c478bd9Sstevel@tonic-gate 		    ugenp->ug_dev_data->
1693*7c478bd9Sstevel@tonic-gate 				dev_cfg[0].cfg_if[new_if].if_n_alt);
1694*7c478bd9Sstevel@tonic-gate 
1695*7c478bd9Sstevel@tonic-gate 		ASSERT(epp->ep_alt == new_alt);
1696*7c478bd9Sstevel@tonic-gate 		ASSERT(epp->ep_cfgidx == new_cfgidx);
1697*7c478bd9Sstevel@tonic-gate 		ASSERT(epp->ep_if == new_if);
1698*7c478bd9Sstevel@tonic-gate 
1699*7c478bd9Sstevel@tonic-gate 		return (rval);
1700*7c478bd9Sstevel@tonic-gate 	}
1701*7c478bd9Sstevel@tonic-gate 
1702*7c478bd9Sstevel@tonic-gate 	/* no switch for default endpoint */
1703*7c478bd9Sstevel@tonic-gate 	if (epp->ep_descr.bEndpointAddress == 0) {
1704*7c478bd9Sstevel@tonic-gate 
1705*7c478bd9Sstevel@tonic-gate 		return (rval);
1706*7c478bd9Sstevel@tonic-gate 	}
1707*7c478bd9Sstevel@tonic-gate 
1708*7c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
1709*7c478bd9Sstevel@tonic-gate 	if ((ugenp->ug_dev_data->dev_n_cfg > 1) &&
1710*7c478bd9Sstevel@tonic-gate 	    usb_get_cfg(ugenp->ug_dip, &cfgval,
1711*7c478bd9Sstevel@tonic-gate 	    USB_FLAGS_SLEEP) == USB_SUCCESS) {
1712*7c478bd9Sstevel@tonic-gate 
1713*7c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
1714*7c478bd9Sstevel@tonic-gate 
1715*7c478bd9Sstevel@tonic-gate 		cur_cfgidx = ugen_cfgval2idx(ugenp, cfgval);
1716*7c478bd9Sstevel@tonic-gate 
1717*7c478bd9Sstevel@tonic-gate 		if (new_cfgidx != cur_cfgidx) {
1718*7c478bd9Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
1719*7c478bd9Sstevel@tonic-gate 
1720*7c478bd9Sstevel@tonic-gate 			/*
1721*7c478bd9Sstevel@tonic-gate 			 * we can't change config if any node
1722*7c478bd9Sstevel@tonic-gate 			 * is open
1723*7c478bd9Sstevel@tonic-gate 			 */
1724*7c478bd9Sstevel@tonic-gate 			if (ugen_epxs_check_open_nodes(ugenp) ==
1725*7c478bd9Sstevel@tonic-gate 			    USB_SUCCESS) {
1726*7c478bd9Sstevel@tonic-gate 				mutex_enter(&epp->ep_mutex);
1727*7c478bd9Sstevel@tonic-gate 
1728*7c478bd9Sstevel@tonic-gate 				return (USB_BUSY);
1729*7c478bd9Sstevel@tonic-gate 			}
1730*7c478bd9Sstevel@tonic-gate 
1731*7c478bd9Sstevel@tonic-gate 			/*
1732*7c478bd9Sstevel@tonic-gate 			 * we are going to do this synchronously to
1733*7c478bd9Sstevel@tonic-gate 			 * keep it simple.
1734*7c478bd9Sstevel@tonic-gate 			 * This should never hang forever.
1735*7c478bd9Sstevel@tonic-gate 			 */
1736*7c478bd9Sstevel@tonic-gate 			if ((rval = usb_set_cfg(ugenp->ug_dip,
1737*7c478bd9Sstevel@tonic-gate 			    new_cfgidx, USB_FLAGS_SLEEP, NULL,
1738*7c478bd9Sstevel@tonic-gate 			    NULL)) != USB_SUCCESS) {
1739*7c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(UGEN_PRINT_XFER,
1740*7c478bd9Sstevel@tonic-gate 				    ugenp->ug_log_hdl,
1741*7c478bd9Sstevel@tonic-gate 				    "implicit set cfg (%" PRId64
1742*7c478bd9Sstevel@tonic-gate 				    ") failed (%d)",
1743*7c478bd9Sstevel@tonic-gate 				    UGEN_MINOR_CFGIDX(ugenp, dev), rval);
1744*7c478bd9Sstevel@tonic-gate 				mutex_enter(&epp->ep_mutex);
1745*7c478bd9Sstevel@tonic-gate 
1746*7c478bd9Sstevel@tonic-gate 				return (rval);
1747*7c478bd9Sstevel@tonic-gate 			}
1748*7c478bd9Sstevel@tonic-gate 			mutex_enter(&epp->ep_mutex);
1749*7c478bd9Sstevel@tonic-gate 			epp->ep_if = new_if;
1750*7c478bd9Sstevel@tonic-gate 			switched++;
1751*7c478bd9Sstevel@tonic-gate 		}
1752*7c478bd9Sstevel@tonic-gate 		epp->ep_cfgidx = new_cfgidx;
1753*7c478bd9Sstevel@tonic-gate 
1754*7c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
1755*7c478bd9Sstevel@tonic-gate 	}
1756*7c478bd9Sstevel@tonic-gate 
1757*7c478bd9Sstevel@tonic-gate 	/*
1758*7c478bd9Sstevel@tonic-gate 	 * implicitly switch to new alternate if
1759*7c478bd9Sstevel@tonic-gate 	 * - we have not switched configuration (if we
1760*7c478bd9Sstevel@tonic-gate 	 *   we switched config, the alternate must be 0)
1761*7c478bd9Sstevel@tonic-gate 	 * - n_alts is > 1
1762*7c478bd9Sstevel@tonic-gate 	 * - if the device supports get_alternate iface
1763*7c478bd9Sstevel@tonic-gate 	 */
1764*7c478bd9Sstevel@tonic-gate 	if ((switched && (new_alt > 0)) ||
1765*7c478bd9Sstevel@tonic-gate 	    ((ugenp->ug_dev_data->dev_cfg[new_cfgidx].
1766*7c478bd9Sstevel@tonic-gate 	    cfg_if[new_if].if_n_alt > 1) &&
1767*7c478bd9Sstevel@tonic-gate 	    (usb_get_alt_if(ugenp->ug_dip, new_if, &alt,
1768*7c478bd9Sstevel@tonic-gate 	    USB_FLAGS_SLEEP) == USB_SUCCESS))) {
1769*7c478bd9Sstevel@tonic-gate 		if (switched || (alt != new_alt)) {
1770*7c478bd9Sstevel@tonic-gate 			if (ugen_epxs_check_alt_switch(ugenp, cur_if,
1771*7c478bd9Sstevel@tonic-gate 			    new_cfgidx) != USB_SUCCESS) {
1772*7c478bd9Sstevel@tonic-gate 				mutex_enter(&epp->ep_mutex);
1773*7c478bd9Sstevel@tonic-gate 
1774*7c478bd9Sstevel@tonic-gate 				return (USB_BUSY);
1775*7c478bd9Sstevel@tonic-gate 			}
1776*7c478bd9Sstevel@tonic-gate 			if ((rval = usb_set_alt_if(ugenp->ug_dip, new_if,
1777*7c478bd9Sstevel@tonic-gate 			    new_alt, USB_FLAGS_SLEEP, NULL, NULL)) !=
1778*7c478bd9Sstevel@tonic-gate 			    USB_SUCCESS) {
1779*7c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(UGEN_PRINT_XFER,
1780*7c478bd9Sstevel@tonic-gate 				    ugenp->ug_log_hdl,
1781*7c478bd9Sstevel@tonic-gate 				    "implicit set new alternate "
1782*7c478bd9Sstevel@tonic-gate 				    "(%d) failed (%d)", new_alt, rval);
1783*7c478bd9Sstevel@tonic-gate 				mutex_enter(&epp->ep_mutex);
1784*7c478bd9Sstevel@tonic-gate 
1785*7c478bd9Sstevel@tonic-gate 				return (rval);
1786*7c478bd9Sstevel@tonic-gate 			}
1787*7c478bd9Sstevel@tonic-gate 		}
1788*7c478bd9Sstevel@tonic-gate 	}
1789*7c478bd9Sstevel@tonic-gate 
1790*7c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
1791*7c478bd9Sstevel@tonic-gate 	epp->ep_alt = new_alt;
1792*7c478bd9Sstevel@tonic-gate 	ugen_update_ep_descr(ugenp, epp);
1793*7c478bd9Sstevel@tonic-gate 
1794*7c478bd9Sstevel@tonic-gate 	return (rval);
1795*7c478bd9Sstevel@tonic-gate }
1796*7c478bd9Sstevel@tonic-gate 
1797*7c478bd9Sstevel@tonic-gate 
1798*7c478bd9Sstevel@tonic-gate /*
1799*7c478bd9Sstevel@tonic-gate  * update endpoint descriptor in ugen_ep structure after
1800*7c478bd9Sstevel@tonic-gate  * switching configuration or alternate
1801*7c478bd9Sstevel@tonic-gate  */
1802*7c478bd9Sstevel@tonic-gate static void
1803*7c478bd9Sstevel@tonic-gate ugen_update_ep_descr(ugen_state_t *ugenp, ugen_ep_t *epp)
1804*7c478bd9Sstevel@tonic-gate {
1805*7c478bd9Sstevel@tonic-gate 	usb_cfg_data_t	*dev_cfg = ugenp->ug_dev_data->dev_cfg;
1806*7c478bd9Sstevel@tonic-gate 	usb_if_data_t	*if_data;
1807*7c478bd9Sstevel@tonic-gate 	usb_alt_if_data_t *alt_if_data;
1808*7c478bd9Sstevel@tonic-gate 	usb_ep_data_t	*ep_data;
1809*7c478bd9Sstevel@tonic-gate 	int		ep;
1810*7c478bd9Sstevel@tonic-gate 
1811*7c478bd9Sstevel@tonic-gate 	dev_cfg = &ugenp->ug_dev_data->dev_cfg[epp->ep_cfgidx];
1812*7c478bd9Sstevel@tonic-gate 	if_data = &dev_cfg->cfg_if[epp->ep_if];
1813*7c478bd9Sstevel@tonic-gate 	alt_if_data = &if_data->if_alt[epp->ep_alt];
1814*7c478bd9Sstevel@tonic-gate 	for (ep = 0; ep < alt_if_data->altif_n_ep; ep++) {
1815*7c478bd9Sstevel@tonic-gate 		ep_data = &alt_if_data->altif_ep[ep];
1816*7c478bd9Sstevel@tonic-gate 		if (usb_get_ep_index(ep_data->ep_descr.
1817*7c478bd9Sstevel@tonic-gate 		    bEndpointAddress) ==
1818*7c478bd9Sstevel@tonic-gate 		    usb_get_ep_index(epp->ep_descr.
1819*7c478bd9Sstevel@tonic-gate 		    bEndpointAddress)) {
1820*7c478bd9Sstevel@tonic-gate 			epp->ep_descr = ep_data->ep_descr;
1821*7c478bd9Sstevel@tonic-gate 
1822*7c478bd9Sstevel@tonic-gate 			break;
1823*7c478bd9Sstevel@tonic-gate 		}
1824*7c478bd9Sstevel@tonic-gate 	}
1825*7c478bd9Sstevel@tonic-gate }
1826*7c478bd9Sstevel@tonic-gate 
1827*7c478bd9Sstevel@tonic-gate 
1828*7c478bd9Sstevel@tonic-gate /*
1829*7c478bd9Sstevel@tonic-gate  * Xfer endpoint management
1830*7c478bd9Sstevel@tonic-gate  *
1831*7c478bd9Sstevel@tonic-gate  * open an endpoint for xfers
1832*7c478bd9Sstevel@tonic-gate  *
1833*7c478bd9Sstevel@tonic-gate  * Return values: errno
1834*7c478bd9Sstevel@tonic-gate  */
1835*7c478bd9Sstevel@tonic-gate static int
1836*7c478bd9Sstevel@tonic-gate ugen_epx_open(ugen_state_t *ugenp, dev_t dev, int flag)
1837*7c478bd9Sstevel@tonic-gate {
1838*7c478bd9Sstevel@tonic-gate 	ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
1839*7c478bd9Sstevel@tonic-gate 	int	rval;
1840*7c478bd9Sstevel@tonic-gate 
1841*7c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
1842*7c478bd9Sstevel@tonic-gate 
1843*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1844*7c478bd9Sstevel@tonic-gate 	    "ugen_epx_open: minor=0x%x flag=0x%x ep_state=0x%x",
1845*7c478bd9Sstevel@tonic-gate 	    getminor(dev), flag, epp->ep_state);
1846*7c478bd9Sstevel@tonic-gate 
1847*7c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_ACTIVE);
1848*7c478bd9Sstevel@tonic-gate 
1849*7c478bd9Sstevel@tonic-gate 	/* implicit switch to new cfg & alt */
1850*7c478bd9Sstevel@tonic-gate 	if ((epp->ep_state & UGEN_EP_STATE_XFER_OPEN) != 0) {
1851*7c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
1852*7c478bd9Sstevel@tonic-gate 
1853*7c478bd9Sstevel@tonic-gate 		return (EBUSY);
1854*7c478bd9Sstevel@tonic-gate 	}
1855*7c478bd9Sstevel@tonic-gate 	if ((rval = ugen_epxs_switch_cfg_alt(ugenp, epp, dev)) ==
1856*7c478bd9Sstevel@tonic-gate 	    USB_SUCCESS) {
1857*7c478bd9Sstevel@tonic-gate 		rval = ugen_epx_open_pipe(ugenp, epp, flag);
1858*7c478bd9Sstevel@tonic-gate 	}
1859*7c478bd9Sstevel@tonic-gate 
1860*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1861*7c478bd9Sstevel@tonic-gate 	    "ugen_epx_open: state=0x%x", epp->ep_state);
1862*7c478bd9Sstevel@tonic-gate 
1863*7c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_ACTIVE);
1864*7c478bd9Sstevel@tonic-gate 	epp->ep_done = epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
1865*7c478bd9Sstevel@tonic-gate 
1866*7c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
1867*7c478bd9Sstevel@tonic-gate 
1868*7c478bd9Sstevel@tonic-gate 	return (usb_rval2errno(rval));
1869*7c478bd9Sstevel@tonic-gate }
1870*7c478bd9Sstevel@tonic-gate 
1871*7c478bd9Sstevel@tonic-gate 
1872*7c478bd9Sstevel@tonic-gate /*
1873*7c478bd9Sstevel@tonic-gate  * close an endpoint for xfers
1874*7c478bd9Sstevel@tonic-gate  */
1875*7c478bd9Sstevel@tonic-gate static void
1876*7c478bd9Sstevel@tonic-gate ugen_epx_close(ugen_state_t *ugenp, dev_t dev, int flag)
1877*7c478bd9Sstevel@tonic-gate {
1878*7c478bd9Sstevel@tonic-gate 	ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
1879*7c478bd9Sstevel@tonic-gate 
1880*7c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
1881*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1882*7c478bd9Sstevel@tonic-gate 	    "ugen_epx_close: dev=0x%lx flag=0x%x state=0x%x", dev, flag,
1883*7c478bd9Sstevel@tonic-gate 	    epp->ep_state);
1884*7c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
1885*7c478bd9Sstevel@tonic-gate 
1886*7c478bd9Sstevel@tonic-gate 	ugen_epx_close_pipe(ugenp, epp);
1887*7c478bd9Sstevel@tonic-gate 
1888*7c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
1889*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1890*7c478bd9Sstevel@tonic-gate 	    "ugen_epx_close: state=0x%x", epp->ep_state);
1891*7c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_ACTIVE);
1892*7c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_bp == NULL);
1893*7c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_done == 0);
1894*7c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_data == NULL);
1895*7c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
1896*7c478bd9Sstevel@tonic-gate }
1897*7c478bd9Sstevel@tonic-gate 
1898*7c478bd9Sstevel@tonic-gate 
1899*7c478bd9Sstevel@tonic-gate /*
1900*7c478bd9Sstevel@tonic-gate  * open pipe for this endpoint
1901*7c478bd9Sstevel@tonic-gate  * If the pipe is an interrupt IN pipe, start polling immediately
1902*7c478bd9Sstevel@tonic-gate  */
1903*7c478bd9Sstevel@tonic-gate static int
1904*7c478bd9Sstevel@tonic-gate ugen_epx_open_pipe(ugen_state_t *ugenp, ugen_ep_t *epp, int flag)
1905*7c478bd9Sstevel@tonic-gate {
1906*7c478bd9Sstevel@tonic-gate 	int rval = USB_SUCCESS;
1907*7c478bd9Sstevel@tonic-gate 
1908*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1909*7c478bd9Sstevel@tonic-gate 	    "ugen_epx_open_pipe: epp=0x%p flag=%d state=0x%x",
1910*7c478bd9Sstevel@tonic-gate 	    epp, flag, epp->ep_state);
1911*7c478bd9Sstevel@tonic-gate 
1912*7c478bd9Sstevel@tonic-gate 	epp->ep_state |= UGEN_EP_STATE_XFER_OPEN;
1913*7c478bd9Sstevel@tonic-gate 	epp->ep_xfer_oflag = flag;
1914*7c478bd9Sstevel@tonic-gate 
1915*7c478bd9Sstevel@tonic-gate 	/* if default pipe, just copy the handle */
1916*7c478bd9Sstevel@tonic-gate 	if ((epp->ep_descr.bEndpointAddress & USB_EP_NUM_MASK) == 0) {
1917*7c478bd9Sstevel@tonic-gate 		epp->ep_ph = ugenp->ug_dev_data->dev_default_ph;
1918*7c478bd9Sstevel@tonic-gate 	} else {
1919*7c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
1920*7c478bd9Sstevel@tonic-gate 
1921*7c478bd9Sstevel@tonic-gate 		/* open pipe */
1922*7c478bd9Sstevel@tonic-gate 		rval = usb_pipe_open(ugenp->ug_dip,
1923*7c478bd9Sstevel@tonic-gate 		    &epp->ep_descr, &epp->ep_pipe_policy,
1924*7c478bd9Sstevel@tonic-gate 		    USB_FLAGS_SLEEP, &epp->ep_ph);
1925*7c478bd9Sstevel@tonic-gate 
1926*7c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
1927*7c478bd9Sstevel@tonic-gate 
1928*7c478bd9Sstevel@tonic-gate 		if (rval == USB_SUCCESS) {
1929*7c478bd9Sstevel@tonic-gate 			(void) usb_pipe_set_private(epp->ep_ph,
1930*7c478bd9Sstevel@tonic-gate 						(usb_opaque_t)epp);
1931*7c478bd9Sstevel@tonic-gate 
1932*7c478bd9Sstevel@tonic-gate 			/*
1933*7c478bd9Sstevel@tonic-gate 			 * if interrupt IN pipe, and one xfer mode
1934*7c478bd9Sstevel@tonic-gate 			 * has not been set, start polling immediately
1935*7c478bd9Sstevel@tonic-gate 			 */
1936*7c478bd9Sstevel@tonic-gate 			if ((UGEN_XFER_TYPE(epp) == USB_EP_ATTR_INTR) &&
1937*7c478bd9Sstevel@tonic-gate 			    (!(epp->ep_one_xfer)) &&
1938*7c478bd9Sstevel@tonic-gate 			    (UGEN_XFER_DIR(epp) == USB_EP_DIR_IN)) {
1939*7c478bd9Sstevel@tonic-gate 				if ((rval = ugen_epx_intr_IN_start_polling(
1940*7c478bd9Sstevel@tonic-gate 				    ugenp, epp)) != USB_SUCCESS) {
1941*7c478bd9Sstevel@tonic-gate 
1942*7c478bd9Sstevel@tonic-gate 					mutex_exit(&epp->ep_mutex);
1943*7c478bd9Sstevel@tonic-gate 					usb_pipe_close(ugenp->ug_dip,
1944*7c478bd9Sstevel@tonic-gate 					    epp->ep_ph, USB_FLAGS_SLEEP,
1945*7c478bd9Sstevel@tonic-gate 					    NULL, NULL);
1946*7c478bd9Sstevel@tonic-gate 					mutex_enter(&epp->ep_mutex);
1947*7c478bd9Sstevel@tonic-gate 
1948*7c478bd9Sstevel@tonic-gate 					epp->ep_ph = NULL;
1949*7c478bd9Sstevel@tonic-gate 				} else {
1950*7c478bd9Sstevel@tonic-gate 					epp->ep_state |=
1951*7c478bd9Sstevel@tonic-gate 					    UGEN_EP_STATE_INTR_IN_POLLING_ON;
1952*7c478bd9Sstevel@tonic-gate 
1953*7c478bd9Sstevel@tonic-gate 					/* allow for about 1 sec of data */
1954*7c478bd9Sstevel@tonic-gate 					epp->ep_buf_limit =
1955*7c478bd9Sstevel@tonic-gate 					    (1000/epp->ep_descr.bInterval) *
1956*7c478bd9Sstevel@tonic-gate 					    epp->ep_descr.wMaxPacketSize;
1957*7c478bd9Sstevel@tonic-gate 				}
1958*7c478bd9Sstevel@tonic-gate 			}
1959*7c478bd9Sstevel@tonic-gate 		}
1960*7c478bd9Sstevel@tonic-gate 	}
1961*7c478bd9Sstevel@tonic-gate 
1962*7c478bd9Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
1963*7c478bd9Sstevel@tonic-gate 		epp->ep_state &= ~(UGEN_EP_STATE_XFER_OPEN |
1964*7c478bd9Sstevel@tonic-gate 		    UGEN_EP_STATE_INTR_IN_POLLING_ON);
1965*7c478bd9Sstevel@tonic-gate 	}
1966*7c478bd9Sstevel@tonic-gate 
1967*7c478bd9Sstevel@tonic-gate 	return (rval);
1968*7c478bd9Sstevel@tonic-gate }
1969*7c478bd9Sstevel@tonic-gate 
1970*7c478bd9Sstevel@tonic-gate 
1971*7c478bd9Sstevel@tonic-gate /*
1972*7c478bd9Sstevel@tonic-gate  * close an endpoint pipe
1973*7c478bd9Sstevel@tonic-gate  */
1974*7c478bd9Sstevel@tonic-gate static void
1975*7c478bd9Sstevel@tonic-gate ugen_epx_close_pipe(ugen_state_t *ugenp, ugen_ep_t *epp)
1976*7c478bd9Sstevel@tonic-gate {
1977*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1978*7c478bd9Sstevel@tonic-gate 	    "ugen_epx_close_pipe: epp=0x%p", epp);
1979*7c478bd9Sstevel@tonic-gate 
1980*7c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
1981*7c478bd9Sstevel@tonic-gate 	if (epp->ep_state & UGEN_EP_STATE_XFER_OPEN) {
1982*7c478bd9Sstevel@tonic-gate 		epp->ep_state &= ~(UGEN_EP_STATE_XFER_OPEN |
1983*7c478bd9Sstevel@tonic-gate 				UGEN_EP_STATE_INTR_IN_POLLING_IS_STOPPED |
1984*7c478bd9Sstevel@tonic-gate 				UGEN_EP_STATE_INTR_IN_POLLING_ON);
1985*7c478bd9Sstevel@tonic-gate 
1986*7c478bd9Sstevel@tonic-gate 		if (epp->ep_ph == ugenp->ug_dev_data->dev_default_ph) {
1987*7c478bd9Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
1988*7c478bd9Sstevel@tonic-gate 
1989*7c478bd9Sstevel@tonic-gate 			(void) usb_pipe_drain_reqs(ugenp->ug_dip,
1990*7c478bd9Sstevel@tonic-gate 			    epp->ep_ph, 0, USB_FLAGS_SLEEP,
1991*7c478bd9Sstevel@tonic-gate 			    NULL, NULL);
1992*7c478bd9Sstevel@tonic-gate 			mutex_enter(&epp->ep_mutex);
1993*7c478bd9Sstevel@tonic-gate 		} else {
1994*7c478bd9Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
1995*7c478bd9Sstevel@tonic-gate 			usb_pipe_close(ugenp->ug_dip,
1996*7c478bd9Sstevel@tonic-gate 			    epp->ep_ph, USB_FLAGS_SLEEP, NULL, NULL);
1997*7c478bd9Sstevel@tonic-gate 
1998*7c478bd9Sstevel@tonic-gate 			mutex_enter(&epp->ep_mutex);
1999*7c478bd9Sstevel@tonic-gate 			epp->ep_ph = NULL;
2000*7c478bd9Sstevel@tonic-gate 		}
2001*7c478bd9Sstevel@tonic-gate 
2002*7c478bd9Sstevel@tonic-gate 		freemsg(epp->ep_data);
2003*7c478bd9Sstevel@tonic-gate 		epp->ep_ph = NULL;
2004*7c478bd9Sstevel@tonic-gate 		epp->ep_data = NULL;
2005*7c478bd9Sstevel@tonic-gate 	}
2006*7c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_ph == NULL);
2007*7c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_data == NULL);
2008*7c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
2009*7c478bd9Sstevel@tonic-gate }
2010*7c478bd9Sstevel@tonic-gate 
2011*7c478bd9Sstevel@tonic-gate 
2012*7c478bd9Sstevel@tonic-gate /*
2013*7c478bd9Sstevel@tonic-gate  * start endpoint xfer
2014*7c478bd9Sstevel@tonic-gate  *
2015*7c478bd9Sstevel@tonic-gate  * We first serialize at endpoint level for only one request at the time
2016*7c478bd9Sstevel@tonic-gate  *
2017*7c478bd9Sstevel@tonic-gate  * Return values: errno
2018*7c478bd9Sstevel@tonic-gate  */
2019*7c478bd9Sstevel@tonic-gate static int
2020*7c478bd9Sstevel@tonic-gate ugen_epx_req(ugen_state_t *ugenp, struct buf *bp)
2021*7c478bd9Sstevel@tonic-gate {
2022*7c478bd9Sstevel@tonic-gate 	dev_t		dev = bp->b_edev;
2023*7c478bd9Sstevel@tonic-gate 	ugen_ep_t	*epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
2024*7c478bd9Sstevel@tonic-gate 	boolean_t	wait = B_FALSE;
2025*7c478bd9Sstevel@tonic-gate 	int		rval = 0;
2026*7c478bd9Sstevel@tonic-gate 
2027*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2028*7c478bd9Sstevel@tonic-gate 	    "ugen_epx_req: bp=0x%p dev=0x%lx", (void *)bp, dev);
2029*7c478bd9Sstevel@tonic-gate 
2030*7c478bd9Sstevel@tonic-gate 	/* single thread per endpoint, one request at the time */
2031*7c478bd9Sstevel@tonic-gate 	if (usb_serialize_access(epp->ep_ser_cookie, USB_WAIT_SIG, 0) <=
2032*7c478bd9Sstevel@tonic-gate 	    0) {
2033*7c478bd9Sstevel@tonic-gate 
2034*7c478bd9Sstevel@tonic-gate 		return (EINTR);
2035*7c478bd9Sstevel@tonic-gate 	}
2036*7c478bd9Sstevel@tonic-gate 
2037*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
2038*7c478bd9Sstevel@tonic-gate 	switch (ugenp->ug_dev_state) {
2039*7c478bd9Sstevel@tonic-gate 	case USB_DEV_ONLINE:
2040*7c478bd9Sstevel@tonic-gate 
2041*7c478bd9Sstevel@tonic-gate 		break;
2042*7c478bd9Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
2043*7c478bd9Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
2044*7c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
2045*7c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_DISCONNECTED;
2046*7c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
2047*7c478bd9Sstevel@tonic-gate 		rval = ENODEV;
2048*7c478bd9Sstevel@tonic-gate 
2049*7c478bd9Sstevel@tonic-gate 		break;
2050*7c478bd9Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RESUME:
2051*7c478bd9Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
2052*7c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
2053*7c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_SUSPENDED;
2054*7c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
2055*7c478bd9Sstevel@tonic-gate 		rval = EBADF;
2056*7c478bd9Sstevel@tonic-gate 
2057*7c478bd9Sstevel@tonic-gate 		break;
2058*7c478bd9Sstevel@tonic-gate 	default:
2059*7c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
2060*7c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_HW_ERR;
2061*7c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
2062*7c478bd9Sstevel@tonic-gate 		rval = EIO;
2063*7c478bd9Sstevel@tonic-gate 
2064*7c478bd9Sstevel@tonic-gate 		break;
2065*7c478bd9Sstevel@tonic-gate 	}
2066*7c478bd9Sstevel@tonic-gate 
2067*7c478bd9Sstevel@tonic-gate #ifndef __lock_lint
2068*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2069*7c478bd9Sstevel@tonic-gate 	    "ugen_epx_req: lcmd_status=0x%x", epp->ep_lcmd_status);
2070*7c478bd9Sstevel@tonic-gate #endif
2071*7c478bd9Sstevel@tonic-gate 
2072*7c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
2073*7c478bd9Sstevel@tonic-gate 
2074*7c478bd9Sstevel@tonic-gate 	if (rval) {
2075*7c478bd9Sstevel@tonic-gate 		usb_release_access(epp->ep_ser_cookie);
2076*7c478bd9Sstevel@tonic-gate 
2077*7c478bd9Sstevel@tonic-gate 		return (rval);
2078*7c478bd9Sstevel@tonic-gate 	}
2079*7c478bd9Sstevel@tonic-gate 
2080*7c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
2081*7c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_XS_OPEN);
2082*7c478bd9Sstevel@tonic-gate 	epp->ep_done = 0;
2083*7c478bd9Sstevel@tonic-gate 	epp->ep_bp = bp;
2084*7c478bd9Sstevel@tonic-gate 
2085*7c478bd9Sstevel@tonic-gate 	switch (epp->ep_descr.bmAttributes & USB_EP_ATTR_MASK) {
2086*7c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_CONTROL:
2087*7c478bd9Sstevel@tonic-gate 		rval = ugen_epx_ctrl_req(ugenp, epp, bp, &wait);
2088*7c478bd9Sstevel@tonic-gate 
2089*7c478bd9Sstevel@tonic-gate 		break;
2090*7c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_BULK:
2091*7c478bd9Sstevel@tonic-gate 		rval = ugen_epx_bulk_req(ugenp, epp, bp, &wait);
2092*7c478bd9Sstevel@tonic-gate 
2093*7c478bd9Sstevel@tonic-gate 		break;
2094*7c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_INTR:
2095*7c478bd9Sstevel@tonic-gate 		if (bp->b_flags & B_READ) {
2096*7c478bd9Sstevel@tonic-gate 			rval = ugen_epx_intr_IN_req(ugenp, epp, bp, &wait);
2097*7c478bd9Sstevel@tonic-gate 		} else {
2098*7c478bd9Sstevel@tonic-gate 			rval = ugen_epx_intr_OUT_req(ugenp, epp, bp, &wait);
2099*7c478bd9Sstevel@tonic-gate 		}
2100*7c478bd9Sstevel@tonic-gate 
2101*7c478bd9Sstevel@tonic-gate 		break;
2102*7c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_ISOCH:
2103*7c478bd9Sstevel@tonic-gate 	default:
2104*7c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
2105*7c478bd9Sstevel@tonic-gate 		rval = USB_INVALID_REQUEST;
2106*7c478bd9Sstevel@tonic-gate 	}
2107*7c478bd9Sstevel@tonic-gate 
2108*7c478bd9Sstevel@tonic-gate 	/* if the xfer could not immediately be completed, block here */
2109*7c478bd9Sstevel@tonic-gate 	if ((rval == USB_SUCCESS) && wait) {
2110*7c478bd9Sstevel@tonic-gate 		while (!epp->ep_done) {
2111*7c478bd9Sstevel@tonic-gate 			if ((cv_wait_sig(&epp->ep_wait_cv,
2112*7c478bd9Sstevel@tonic-gate 			    &epp->ep_mutex) <= 0) && !epp->ep_done) {
2113*7c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(UGEN_PRINT_XFER,
2114*7c478bd9Sstevel@tonic-gate 				    ugenp->ug_log_hdl,
2115*7c478bd9Sstevel@tonic-gate 				    "ugen_epx_req: interrupted ep=0x%" PRIx64,
2116*7c478bd9Sstevel@tonic-gate 				    UGEN_MINOR_EPIDX(ugenp, dev));
2117*7c478bd9Sstevel@tonic-gate 
2118*7c478bd9Sstevel@tonic-gate 				/*
2119*7c478bd9Sstevel@tonic-gate 				 * blow away the request except for dflt pipe
2120*7c478bd9Sstevel@tonic-gate 				 * (this is prevented in USBA)
2121*7c478bd9Sstevel@tonic-gate 				 */
2122*7c478bd9Sstevel@tonic-gate 				mutex_exit(&epp->ep_mutex);
2123*7c478bd9Sstevel@tonic-gate 				usb_pipe_reset(ugenp->ug_dip, epp->ep_ph,
2124*7c478bd9Sstevel@tonic-gate 				    USB_FLAGS_SLEEP, NULL, NULL);
2125*7c478bd9Sstevel@tonic-gate 				(void) usb_pipe_drain_reqs(ugenp->ug_dip,
2126*7c478bd9Sstevel@tonic-gate 				    epp->ep_ph, 0,
2127*7c478bd9Sstevel@tonic-gate 				    USB_FLAGS_SLEEP, NULL, NULL);
2128*7c478bd9Sstevel@tonic-gate 
2129*7c478bd9Sstevel@tonic-gate 				mutex_enter(&epp->ep_mutex);
2130*7c478bd9Sstevel@tonic-gate 
2131*7c478bd9Sstevel@tonic-gate 				if (geterror(bp) == 0) {
2132*7c478bd9Sstevel@tonic-gate 					bioerror(bp, EINTR);
2133*7c478bd9Sstevel@tonic-gate 				}
2134*7c478bd9Sstevel@tonic-gate 				epp->ep_lcmd_status =
2135*7c478bd9Sstevel@tonic-gate 				    USB_LC_STAT_INTERRUPTED;
2136*7c478bd9Sstevel@tonic-gate 
2137*7c478bd9Sstevel@tonic-gate 				break;
2138*7c478bd9Sstevel@tonic-gate 			}
2139*7c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2140*7c478bd9Sstevel@tonic-gate 			    "ugen_epx_req: wakeup");
2141*7c478bd9Sstevel@tonic-gate 		}
2142*7c478bd9Sstevel@tonic-gate 	}
2143*7c478bd9Sstevel@tonic-gate 
2144*7c478bd9Sstevel@tonic-gate 	/* always set lcmd_status if there was a failure */
2145*7c478bd9Sstevel@tonic-gate 	if ((rval != USB_SUCCESS) &&
2146*7c478bd9Sstevel@tonic-gate 	    (epp->ep_lcmd_status == USB_LC_STAT_NOERROR)) {
2147*7c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_UNSPECIFIED_ERR;
2148*7c478bd9Sstevel@tonic-gate 	}
2149*7c478bd9Sstevel@tonic-gate 
2150*7c478bd9Sstevel@tonic-gate 	epp->ep_done = 0;
2151*7c478bd9Sstevel@tonic-gate 	epp->ep_bp = NULL;
2152*7c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
2153*7c478bd9Sstevel@tonic-gate 
2154*7c478bd9Sstevel@tonic-gate 	usb_release_access(epp->ep_ser_cookie);
2155*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2156*7c478bd9Sstevel@tonic-gate 	    "ugen_epx_req: done");
2157*7c478bd9Sstevel@tonic-gate 
2158*7c478bd9Sstevel@tonic-gate 	return (usb_rval2errno(rval));
2159*7c478bd9Sstevel@tonic-gate }
2160*7c478bd9Sstevel@tonic-gate 
2161*7c478bd9Sstevel@tonic-gate 
2162*7c478bd9Sstevel@tonic-gate /*
2163*7c478bd9Sstevel@tonic-gate  * handle control xfers
2164*7c478bd9Sstevel@tonic-gate  */
2165*7c478bd9Sstevel@tonic-gate static int
2166*7c478bd9Sstevel@tonic-gate ugen_epx_ctrl_req(ugen_state_t *ugenp, ugen_ep_t *epp,
2167*7c478bd9Sstevel@tonic-gate     struct buf *bp, boolean_t *wait)
2168*7c478bd9Sstevel@tonic-gate {
2169*7c478bd9Sstevel@tonic-gate 	usb_ctrl_req_t *reqp = NULL;
2170*7c478bd9Sstevel@tonic-gate 	uchar_t	*setup = ((uchar_t *)(bp->b_un.b_addr));
2171*7c478bd9Sstevel@tonic-gate 	int	rval;
2172*7c478bd9Sstevel@tonic-gate 	ushort_t wLength;
2173*7c478bd9Sstevel@tonic-gate 
2174*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2175*7c478bd9Sstevel@tonic-gate 	    "ugen_epx_ctrl_req: epp=0x%p state=0x%x bp=0x%p",
2176*7c478bd9Sstevel@tonic-gate 	    epp, epp->ep_state, bp);
2177*7c478bd9Sstevel@tonic-gate 
2178*7c478bd9Sstevel@tonic-gate 	/* is this a read following a write with setup data? */
2179*7c478bd9Sstevel@tonic-gate 	if (bp->b_flags & B_READ) {
2180*7c478bd9Sstevel@tonic-gate 		if (epp->ep_data) {
2181*7c478bd9Sstevel@tonic-gate 			int ep_len = epp->ep_data->b_wptr -
2182*7c478bd9Sstevel@tonic-gate 						epp->ep_data->b_rptr;
2183*7c478bd9Sstevel@tonic-gate 			int len = min(bp->b_bcount, ep_len);
2184*7c478bd9Sstevel@tonic-gate 
2185*7c478bd9Sstevel@tonic-gate 			bcopy(epp->ep_data->b_rptr, bp->b_un.b_addr, len);
2186*7c478bd9Sstevel@tonic-gate 			epp->ep_data->b_rptr += len;
2187*7c478bd9Sstevel@tonic-gate 			if ((epp->ep_data->b_wptr - epp->ep_data->b_rptr) ==
2188*7c478bd9Sstevel@tonic-gate 			    0) {
2189*7c478bd9Sstevel@tonic-gate 				freemsg(epp->ep_data);
2190*7c478bd9Sstevel@tonic-gate 				epp->ep_data = NULL;
2191*7c478bd9Sstevel@tonic-gate 			}
2192*7c478bd9Sstevel@tonic-gate 			bp->b_resid = bp->b_bcount - len;
2193*7c478bd9Sstevel@tonic-gate 		} else {
2194*7c478bd9Sstevel@tonic-gate 			bp->b_resid = bp->b_bcount;
2195*7c478bd9Sstevel@tonic-gate 		}
2196*7c478bd9Sstevel@tonic-gate 
2197*7c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
2198*7c478bd9Sstevel@tonic-gate 	}
2199*7c478bd9Sstevel@tonic-gate 
2200*7c478bd9Sstevel@tonic-gate 	/* discard old data if any */
2201*7c478bd9Sstevel@tonic-gate 	if (epp->ep_data) {
2202*7c478bd9Sstevel@tonic-gate 		freemsg(epp->ep_data);
2203*7c478bd9Sstevel@tonic-gate 		epp->ep_data = NULL;
2204*7c478bd9Sstevel@tonic-gate 	}
2205*7c478bd9Sstevel@tonic-gate 
2206*7c478bd9Sstevel@tonic-gate 	/* allocate and initialize request */
2207*7c478bd9Sstevel@tonic-gate 	wLength = (setup[7] << 8) | setup[6];
2208*7c478bd9Sstevel@tonic-gate 	reqp = usb_alloc_ctrl_req(ugenp->ug_dip, wLength, USB_FLAGS_NOSLEEP);
2209*7c478bd9Sstevel@tonic-gate 	if (reqp == NULL) {
2210*7c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_NO_RESOURCES;
2211*7c478bd9Sstevel@tonic-gate 
2212*7c478bd9Sstevel@tonic-gate 		return (USB_NO_RESOURCES);
2213*7c478bd9Sstevel@tonic-gate 	}
2214*7c478bd9Sstevel@tonic-gate 
2215*7c478bd9Sstevel@tonic-gate 	/* assume an LE data stream */
2216*7c478bd9Sstevel@tonic-gate 	reqp->ctrl_bmRequestType = setup[0];
2217*7c478bd9Sstevel@tonic-gate 	reqp->ctrl_bRequest	= setup[1];
2218*7c478bd9Sstevel@tonic-gate 	reqp->ctrl_wValue	= (setup[3] << 8) | setup[2];
2219*7c478bd9Sstevel@tonic-gate 	reqp->ctrl_wIndex	= (setup[5] << 8) | setup[4];
2220*7c478bd9Sstevel@tonic-gate 	reqp->ctrl_wLength	= wLength;
2221*7c478bd9Sstevel@tonic-gate 	reqp->ctrl_timeout	= ugen_ctrl_timeout;
2222*7c478bd9Sstevel@tonic-gate 	reqp->ctrl_attributes	= USB_ATTRS_AUTOCLEARING |
2223*7c478bd9Sstevel@tonic-gate 					USB_ATTRS_SHORT_XFER_OK;
2224*7c478bd9Sstevel@tonic-gate 	reqp->ctrl_cb		= ugen_epx_ctrl_req_cb;
2225*7c478bd9Sstevel@tonic-gate 	reqp->ctrl_exc_cb	= ugen_epx_ctrl_req_cb;
2226*7c478bd9Sstevel@tonic-gate 	reqp->ctrl_client_private = (usb_opaque_t)ugenp;
2227*7c478bd9Sstevel@tonic-gate 
2228*7c478bd9Sstevel@tonic-gate 	/*
2229*7c478bd9Sstevel@tonic-gate 	 * is this a legal request? No accesses to device are
2230*7c478bd9Sstevel@tonic-gate 	 * allowed if we don't own the device
2231*7c478bd9Sstevel@tonic-gate 	 */
2232*7c478bd9Sstevel@tonic-gate 	if (((reqp->ctrl_bmRequestType & USB_DEV_REQ_RCPT_MASK) ==
2233*7c478bd9Sstevel@tonic-gate 	    USB_DEV_REQ_RCPT_DEV) &&
2234*7c478bd9Sstevel@tonic-gate 	    (((reqp->ctrl_bmRequestType & USB_DEV_REQ_DIR_MASK) ==
2235*7c478bd9Sstevel@tonic-gate 	    USB_DEV_REQ_HOST_TO_DEV) &&
2236*7c478bd9Sstevel@tonic-gate 	    (usb_owns_device(ugenp->ug_dip) == B_FALSE))) {
2237*7c478bd9Sstevel@tonic-gate 		rval = USB_INVALID_PERM;
2238*7c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
2239*7c478bd9Sstevel@tonic-gate 
2240*7c478bd9Sstevel@tonic-gate 		goto fail;
2241*7c478bd9Sstevel@tonic-gate 	}
2242*7c478bd9Sstevel@tonic-gate 
2243*7c478bd9Sstevel@tonic-gate 	/* filter out set_cfg and set_if standard requests */
2244*7c478bd9Sstevel@tonic-gate 	if ((reqp->ctrl_bmRequestType & USB_DEV_REQ_TYPE_MASK) ==
2245*7c478bd9Sstevel@tonic-gate 	    USB_DEV_REQ_TYPE_STANDARD) {
2246*7c478bd9Sstevel@tonic-gate 		switch (reqp->ctrl_bRequest) {
2247*7c478bd9Sstevel@tonic-gate 		case USB_REQ_SET_CFG:
2248*7c478bd9Sstevel@tonic-gate 		case USB_REQ_SET_IF:
2249*7c478bd9Sstevel@tonic-gate 			rval = USB_INVALID_REQUEST;
2250*7c478bd9Sstevel@tonic-gate 			epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
2251*7c478bd9Sstevel@tonic-gate 
2252*7c478bd9Sstevel@tonic-gate 			goto fail;
2253*7c478bd9Sstevel@tonic-gate 		default:
2254*7c478bd9Sstevel@tonic-gate 
2255*7c478bd9Sstevel@tonic-gate 			break;
2256*7c478bd9Sstevel@tonic-gate 		}
2257*7c478bd9Sstevel@tonic-gate 	}
2258*7c478bd9Sstevel@tonic-gate 
2259*7c478bd9Sstevel@tonic-gate 	/* is this from host to device? */
2260*7c478bd9Sstevel@tonic-gate 	if (((reqp->ctrl_bmRequestType & USB_DEV_REQ_DIR_MASK) ==
2261*7c478bd9Sstevel@tonic-gate 	    USB_DEV_REQ_HOST_TO_DEV) && reqp->ctrl_wLength) {
2262*7c478bd9Sstevel@tonic-gate 		if (((bp->b_bcount - UGEN_SETUP_PKT_SIZE) - wLength) != 0) {
2263*7c478bd9Sstevel@tonic-gate 			rval = USB_INVALID_REQUEST;
2264*7c478bd9Sstevel@tonic-gate 			epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
2265*7c478bd9Sstevel@tonic-gate 
2266*7c478bd9Sstevel@tonic-gate 			goto fail;
2267*7c478bd9Sstevel@tonic-gate 		}
2268*7c478bd9Sstevel@tonic-gate 		bcopy(bp->b_un.b_addr + UGEN_SETUP_PKT_SIZE,
2269*7c478bd9Sstevel@tonic-gate 		    reqp->ctrl_data->b_wptr, wLength);
2270*7c478bd9Sstevel@tonic-gate 		reqp->ctrl_data->b_wptr += wLength;
2271*7c478bd9Sstevel@tonic-gate 	} else	if ((reqp->ctrl_bmRequestType & USB_DEV_REQ_DIR_MASK) ==
2272*7c478bd9Sstevel@tonic-gate 	    USB_DEV_REQ_DEV_TO_HOST) {
2273*7c478bd9Sstevel@tonic-gate 		if (bp->b_bcount != UGEN_SETUP_PKT_SIZE) {
2274*7c478bd9Sstevel@tonic-gate 			rval = USB_INVALID_REQUEST;
2275*7c478bd9Sstevel@tonic-gate 			epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
2276*7c478bd9Sstevel@tonic-gate 
2277*7c478bd9Sstevel@tonic-gate 			goto fail;
2278*7c478bd9Sstevel@tonic-gate 		}
2279*7c478bd9Sstevel@tonic-gate 	}
2280*7c478bd9Sstevel@tonic-gate 
2281*7c478bd9Sstevel@tonic-gate 	/* submit the request */
2282*7c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
2283*7c478bd9Sstevel@tonic-gate 	rval = usb_pipe_ctrl_xfer(epp->ep_ph, reqp, USB_FLAGS_NOSLEEP);
2284*7c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
2285*7c478bd9Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
2286*7c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status =
2287*7c478bd9Sstevel@tonic-gate 		    ugen_cr2lcstat(reqp->ctrl_completion_reason);
2288*7c478bd9Sstevel@tonic-gate 
2289*7c478bd9Sstevel@tonic-gate 		goto fail;
2290*7c478bd9Sstevel@tonic-gate 	}
2291*7c478bd9Sstevel@tonic-gate done:
2292*7c478bd9Sstevel@tonic-gate 	*wait = B_TRUE;
2293*7c478bd9Sstevel@tonic-gate 
2294*7c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
2295*7c478bd9Sstevel@tonic-gate fail:
2296*7c478bd9Sstevel@tonic-gate 	*wait = B_FALSE;
2297*7c478bd9Sstevel@tonic-gate 
2298*7c478bd9Sstevel@tonic-gate 	usb_free_ctrl_req(reqp);
2299*7c478bd9Sstevel@tonic-gate 
2300*7c478bd9Sstevel@tonic-gate 	return (rval);
2301*7c478bd9Sstevel@tonic-gate }
2302*7c478bd9Sstevel@tonic-gate 
2303*7c478bd9Sstevel@tonic-gate 
2304*7c478bd9Sstevel@tonic-gate /*
2305*7c478bd9Sstevel@tonic-gate  * callback for control requests, normal and exception completion
2306*7c478bd9Sstevel@tonic-gate  */
2307*7c478bd9Sstevel@tonic-gate static void
2308*7c478bd9Sstevel@tonic-gate ugen_epx_ctrl_req_cb(usb_pipe_handle_t ph, usb_ctrl_req_t *reqp)
2309*7c478bd9Sstevel@tonic-gate {
2310*7c478bd9Sstevel@tonic-gate 	ugen_state_t *ugenp = (ugen_state_t *)reqp->ctrl_client_private;
2311*7c478bd9Sstevel@tonic-gate 	ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
2312*7c478bd9Sstevel@tonic-gate 
2313*7c478bd9Sstevel@tonic-gate 	if (epp == NULL) {
2314*7c478bd9Sstevel@tonic-gate 		epp = &ugenp->ug_ep[0];
2315*7c478bd9Sstevel@tonic-gate 	}
2316*7c478bd9Sstevel@tonic-gate 
2317*7c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
2318*7c478bd9Sstevel@tonic-gate 
2319*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2320*7c478bd9Sstevel@tonic-gate 	    "ugen_epx_ctrl_req_cb:\n\t"
2321*7c478bd9Sstevel@tonic-gate 	    "epp=0x%p state=0x%x ph=0x%p reqp=0x%p cr=%d cb=0x%x",
2322*7c478bd9Sstevel@tonic-gate 	    epp, epp->ep_state, ph, reqp, reqp->ctrl_completion_reason,
2323*7c478bd9Sstevel@tonic-gate 	    reqp->ctrl_cb_flags);
2324*7c478bd9Sstevel@tonic-gate 
2325*7c478bd9Sstevel@tonic-gate 	ASSERT((reqp->ctrl_cb_flags & USB_CB_INTR_CONTEXT) == 0);
2326*7c478bd9Sstevel@tonic-gate 
2327*7c478bd9Sstevel@tonic-gate 	/* save any data for the next read */
2328*7c478bd9Sstevel@tonic-gate 	switch (reqp->ctrl_completion_reason) {
2329*7c478bd9Sstevel@tonic-gate 	case USB_CR_OK:
2330*7c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
2331*7c478bd9Sstevel@tonic-gate 
2332*7c478bd9Sstevel@tonic-gate 		break;
2333*7c478bd9Sstevel@tonic-gate 	case USB_CR_PIPE_RESET:
2334*7c478bd9Sstevel@tonic-gate 
2335*7c478bd9Sstevel@tonic-gate 		break;
2336*7c478bd9Sstevel@tonic-gate 	default:
2337*7c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status =
2338*7c478bd9Sstevel@tonic-gate 		    ugen_cr2lcstat(reqp->ctrl_completion_reason);
2339*7c478bd9Sstevel@tonic-gate 		if (epp->ep_bp) {
2340*7c478bd9Sstevel@tonic-gate 			bioerror(epp->ep_bp, EIO);
2341*7c478bd9Sstevel@tonic-gate 		}
2342*7c478bd9Sstevel@tonic-gate 
2343*7c478bd9Sstevel@tonic-gate 		break;
2344*7c478bd9Sstevel@tonic-gate 	}
2345*7c478bd9Sstevel@tonic-gate 
2346*7c478bd9Sstevel@tonic-gate 	if (reqp->ctrl_data) {
2347*7c478bd9Sstevel@tonic-gate 		ASSERT(epp->ep_data == NULL);
2348*7c478bd9Sstevel@tonic-gate 		epp->ep_data = reqp->ctrl_data;
2349*7c478bd9Sstevel@tonic-gate 		reqp->ctrl_data = NULL;
2350*7c478bd9Sstevel@tonic-gate 	}
2351*7c478bd9Sstevel@tonic-gate 	epp->ep_done++;
2352*7c478bd9Sstevel@tonic-gate 	cv_signal(&epp->ep_wait_cv);
2353*7c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
2354*7c478bd9Sstevel@tonic-gate 
2355*7c478bd9Sstevel@tonic-gate 	usb_free_ctrl_req(reqp);
2356*7c478bd9Sstevel@tonic-gate 
2357*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2358*7c478bd9Sstevel@tonic-gate 	    "ugen_epx_ctrl_req_cb: done");
2359*7c478bd9Sstevel@tonic-gate }
2360*7c478bd9Sstevel@tonic-gate 
2361*7c478bd9Sstevel@tonic-gate 
2362*7c478bd9Sstevel@tonic-gate /*
2363*7c478bd9Sstevel@tonic-gate  * handle bulk xfers
2364*7c478bd9Sstevel@tonic-gate  */
2365*7c478bd9Sstevel@tonic-gate static int
2366*7c478bd9Sstevel@tonic-gate ugen_epx_bulk_req(ugen_state_t *ugenp, ugen_ep_t *epp,
2367*7c478bd9Sstevel@tonic-gate     struct buf *bp, boolean_t *wait)
2368*7c478bd9Sstevel@tonic-gate {
2369*7c478bd9Sstevel@tonic-gate 	int		rval;
2370*7c478bd9Sstevel@tonic-gate 	usb_bulk_req_t	*reqp = usb_alloc_bulk_req(ugenp->ug_dip,
2371*7c478bd9Sstevel@tonic-gate 				bp->b_bcount, USB_FLAGS_NOSLEEP);
2372*7c478bd9Sstevel@tonic-gate 
2373*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2374*7c478bd9Sstevel@tonic-gate 	    "ugen_epx_bulk_req: epp=0x%p state=0x%x bp=0x%p",
2375*7c478bd9Sstevel@tonic-gate 	    epp, epp->ep_state, bp);
2376*7c478bd9Sstevel@tonic-gate 
2377*7c478bd9Sstevel@tonic-gate 	if (reqp == NULL) {
2378*7c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_NO_RESOURCES;
2379*7c478bd9Sstevel@tonic-gate 
2380*7c478bd9Sstevel@tonic-gate 		return (USB_NO_RESOURCES);
2381*7c478bd9Sstevel@tonic-gate 	}
2382*7c478bd9Sstevel@tonic-gate 
2383*7c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_XS_OPEN);
2384*7c478bd9Sstevel@tonic-gate 
2385*7c478bd9Sstevel@tonic-gate 	/*
2386*7c478bd9Sstevel@tonic-gate 	 * the transfer count is limited in minphys with what the HCD can
2387*7c478bd9Sstevel@tonic-gate 	 * do
2388*7c478bd9Sstevel@tonic-gate 	 */
2389*7c478bd9Sstevel@tonic-gate 	reqp->bulk_len		= bp->b_bcount;
2390*7c478bd9Sstevel@tonic-gate 	reqp->bulk_timeout	= ugen_bulk_timeout;
2391*7c478bd9Sstevel@tonic-gate 	reqp->bulk_client_private = (usb_opaque_t)ugenp;
2392*7c478bd9Sstevel@tonic-gate 	reqp->bulk_attributes	= USB_ATTRS_AUTOCLEARING;
2393*7c478bd9Sstevel@tonic-gate 	reqp->bulk_cb		= ugen_epx_bulk_req_cb;
2394*7c478bd9Sstevel@tonic-gate 	reqp->bulk_exc_cb	= ugen_epx_bulk_req_cb;
2395*7c478bd9Sstevel@tonic-gate 
2396*7c478bd9Sstevel@tonic-gate 	/* copy data into bp for OUT pipes */
2397*7c478bd9Sstevel@tonic-gate 	if ((UGEN_XFER_DIR(epp) & USB_EP_DIR_IN) == 0) {
2398*7c478bd9Sstevel@tonic-gate 		bcopy(epp->ep_bp->b_un.b_addr, reqp->bulk_data->b_rptr,
2399*7c478bd9Sstevel@tonic-gate 								bp->b_bcount);
2400*7c478bd9Sstevel@tonic-gate 		reqp->bulk_data->b_wptr += bp->b_bcount;
2401*7c478bd9Sstevel@tonic-gate 	} else {
2402*7c478bd9Sstevel@tonic-gate 		reqp->bulk_attributes |= USB_ATTRS_SHORT_XFER_OK;
2403*7c478bd9Sstevel@tonic-gate 	}
2404*7c478bd9Sstevel@tonic-gate 
2405*7c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
2406*7c478bd9Sstevel@tonic-gate 	if ((rval = usb_pipe_bulk_xfer(epp->ep_ph, reqp,
2407*7c478bd9Sstevel@tonic-gate 	    USB_FLAGS_NOSLEEP)) != USB_SUCCESS) {
2408*7c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
2409*7c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status =
2410*7c478bd9Sstevel@tonic-gate 		    ugen_cr2lcstat(reqp->bulk_completion_reason);
2411*7c478bd9Sstevel@tonic-gate 		usb_free_bulk_req(reqp);
2412*7c478bd9Sstevel@tonic-gate 		bioerror(bp, EIO);
2413*7c478bd9Sstevel@tonic-gate 	} else {
2414*7c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
2415*7c478bd9Sstevel@tonic-gate 	}
2416*7c478bd9Sstevel@tonic-gate 	*wait = (rval == USB_SUCCESS) ? B_TRUE : B_FALSE;
2417*7c478bd9Sstevel@tonic-gate 
2418*7c478bd9Sstevel@tonic-gate 	return (rval);
2419*7c478bd9Sstevel@tonic-gate }
2420*7c478bd9Sstevel@tonic-gate 
2421*7c478bd9Sstevel@tonic-gate 
2422*7c478bd9Sstevel@tonic-gate /*
2423*7c478bd9Sstevel@tonic-gate  * normal and exception bulk request callback
2424*7c478bd9Sstevel@tonic-gate  */
2425*7c478bd9Sstevel@tonic-gate static void
2426*7c478bd9Sstevel@tonic-gate ugen_epx_bulk_req_cb(usb_pipe_handle_t ph, usb_bulk_req_t *reqp)
2427*7c478bd9Sstevel@tonic-gate {
2428*7c478bd9Sstevel@tonic-gate 	ugen_state_t *ugenp = (ugen_state_t *)reqp->bulk_client_private;
2429*7c478bd9Sstevel@tonic-gate 	ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
2430*7c478bd9Sstevel@tonic-gate 
2431*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2432*7c478bd9Sstevel@tonic-gate 	    "ugen_epx_bulk_req_cb: ph=0x%p reqp=0x%p cr=%d cb=0x%x",
2433*7c478bd9Sstevel@tonic-gate 	    ph, reqp, reqp->bulk_completion_reason, reqp->bulk_cb_flags);
2434*7c478bd9Sstevel@tonic-gate 
2435*7c478bd9Sstevel@tonic-gate 	ASSERT((reqp->bulk_cb_flags & USB_CB_INTR_CONTEXT) == 0);
2436*7c478bd9Sstevel@tonic-gate 
2437*7c478bd9Sstevel@tonic-gate 	/* epp might be NULL if we are closing the pipe */
2438*7c478bd9Sstevel@tonic-gate 	if (epp) {
2439*7c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
2440*7c478bd9Sstevel@tonic-gate 		if (epp->ep_bp && reqp->bulk_data) {
2441*7c478bd9Sstevel@tonic-gate 			int len = min(reqp->bulk_data->b_wptr -
2442*7c478bd9Sstevel@tonic-gate 					reqp->bulk_data->b_rptr,
2443*7c478bd9Sstevel@tonic-gate 					epp->ep_bp->b_bcount);
2444*7c478bd9Sstevel@tonic-gate 			if (UGEN_XFER_DIR(epp) & USB_EP_DIR_IN) {
2445*7c478bd9Sstevel@tonic-gate 				if (len) {
2446*7c478bd9Sstevel@tonic-gate 					bcopy(reqp->bulk_data->b_rptr,
2447*7c478bd9Sstevel@tonic-gate 					    epp->ep_bp->b_un.b_addr, len);
2448*7c478bd9Sstevel@tonic-gate 					epp->ep_bp->b_resid =
2449*7c478bd9Sstevel@tonic-gate 					    epp->ep_bp->b_bcount - len;
2450*7c478bd9Sstevel@tonic-gate 				}
2451*7c478bd9Sstevel@tonic-gate 			} else {
2452*7c478bd9Sstevel@tonic-gate 				epp->ep_bp->b_resid =
2453*7c478bd9Sstevel@tonic-gate 					epp->ep_bp->b_bcount - len;
2454*7c478bd9Sstevel@tonic-gate 			}
2455*7c478bd9Sstevel@tonic-gate 		}
2456*7c478bd9Sstevel@tonic-gate 		switch (reqp->bulk_completion_reason) {
2457*7c478bd9Sstevel@tonic-gate 		case USB_CR_OK:
2458*7c478bd9Sstevel@tonic-gate 			epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
2459*7c478bd9Sstevel@tonic-gate 
2460*7c478bd9Sstevel@tonic-gate 			break;
2461*7c478bd9Sstevel@tonic-gate 		case USB_CR_PIPE_RESET:
2462*7c478bd9Sstevel@tonic-gate 
2463*7c478bd9Sstevel@tonic-gate 			break;
2464*7c478bd9Sstevel@tonic-gate 		default:
2465*7c478bd9Sstevel@tonic-gate 			epp->ep_lcmd_status =
2466*7c478bd9Sstevel@tonic-gate 			    ugen_cr2lcstat(reqp->bulk_completion_reason);
2467*7c478bd9Sstevel@tonic-gate 			if (epp->ep_bp) {
2468*7c478bd9Sstevel@tonic-gate 				bioerror(epp->ep_bp, EIO);
2469*7c478bd9Sstevel@tonic-gate 			}
2470*7c478bd9Sstevel@tonic-gate 		}
2471*7c478bd9Sstevel@tonic-gate 		epp->ep_done++;
2472*7c478bd9Sstevel@tonic-gate 		cv_signal(&epp->ep_wait_cv);
2473*7c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
2474*7c478bd9Sstevel@tonic-gate 	}
2475*7c478bd9Sstevel@tonic-gate 
2476*7c478bd9Sstevel@tonic-gate 	usb_free_bulk_req(reqp);
2477*7c478bd9Sstevel@tonic-gate }
2478*7c478bd9Sstevel@tonic-gate 
2479*7c478bd9Sstevel@tonic-gate 
2480*7c478bd9Sstevel@tonic-gate /*
2481*7c478bd9Sstevel@tonic-gate  * handle intr IN xfers
2482*7c478bd9Sstevel@tonic-gate  */
2483*7c478bd9Sstevel@tonic-gate static int
2484*7c478bd9Sstevel@tonic-gate ugen_epx_intr_IN_req(ugen_state_t *ugenp, ugen_ep_t *epp,
2485*7c478bd9Sstevel@tonic-gate     struct buf *bp, boolean_t *wait)
2486*7c478bd9Sstevel@tonic-gate {
2487*7c478bd9Sstevel@tonic-gate 	int	len = 0;
2488*7c478bd9Sstevel@tonic-gate 	int	rval = USB_SUCCESS;
2489*7c478bd9Sstevel@tonic-gate 
2490*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2491*7c478bd9Sstevel@tonic-gate 	    "ugen_epx_intr_IN_req: epp=0x%p state=0x%x bp=0x%p",
2492*7c478bd9Sstevel@tonic-gate 	    epp, epp->ep_state, bp);
2493*7c478bd9Sstevel@tonic-gate 
2494*7c478bd9Sstevel@tonic-gate 	*wait = B_FALSE;
2495*7c478bd9Sstevel@tonic-gate 
2496*7c478bd9Sstevel@tonic-gate 	/* can we satisfy this read? */
2497*7c478bd9Sstevel@tonic-gate 	if (epp->ep_data) {
2498*7c478bd9Sstevel@tonic-gate 		len = min(epp->ep_data->b_wptr - epp->ep_data->b_rptr,
2499*7c478bd9Sstevel@tonic-gate 							bp->b_bcount);
2500*7c478bd9Sstevel@tonic-gate 	}
2501*7c478bd9Sstevel@tonic-gate 
2502*7c478bd9Sstevel@tonic-gate 	/*
2503*7c478bd9Sstevel@tonic-gate 	 * if polling not active, restart, and return failure
2504*7c478bd9Sstevel@tonic-gate 	 * immediately unless one xfer mode has been requested
2505*7c478bd9Sstevel@tonic-gate 	 * if there is some data, return a short read
2506*7c478bd9Sstevel@tonic-gate 	 */
2507*7c478bd9Sstevel@tonic-gate 	if ((epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_ON) == 0) {
2508*7c478bd9Sstevel@tonic-gate 		if (len == 0) {
2509*7c478bd9Sstevel@tonic-gate 			if (!epp->ep_one_xfer) {
2510*7c478bd9Sstevel@tonic-gate 				rval = USB_FAILURE;
2511*7c478bd9Sstevel@tonic-gate 				if (epp->ep_lcmd_status ==
2512*7c478bd9Sstevel@tonic-gate 				    USB_LC_STAT_NOERROR) {
2513*7c478bd9Sstevel@tonic-gate 					epp->ep_lcmd_status =
2514*7c478bd9Sstevel@tonic-gate 						USB_LC_STAT_INTR_BUF_FULL;
2515*7c478bd9Sstevel@tonic-gate 				}
2516*7c478bd9Sstevel@tonic-gate 			}
2517*7c478bd9Sstevel@tonic-gate 			if (ugen_epx_intr_IN_start_polling(ugenp,
2518*7c478bd9Sstevel@tonic-gate 			    epp) != USB_SUCCESS) {
2519*7c478bd9Sstevel@tonic-gate 				epp->ep_lcmd_status =
2520*7c478bd9Sstevel@tonic-gate 				    USB_LC_STAT_INTR_POLLING_FAILED;
2521*7c478bd9Sstevel@tonic-gate 			}
2522*7c478bd9Sstevel@tonic-gate 			if (epp->ep_one_xfer) {
2523*7c478bd9Sstevel@tonic-gate 				*wait = B_TRUE;
2524*7c478bd9Sstevel@tonic-gate 			}
2525*7c478bd9Sstevel@tonic-gate 			goto done;
2526*7c478bd9Sstevel@tonic-gate 		} else if (epp->ep_data && (len < bp->b_bcount)) {
2527*7c478bd9Sstevel@tonic-gate 			bcopy(epp->ep_data->b_rptr, bp->b_un.b_addr, len);
2528*7c478bd9Sstevel@tonic-gate 			bp->b_resid = bp->b_bcount - len;
2529*7c478bd9Sstevel@tonic-gate 			epp->ep_data->b_rptr += len;
2530*7c478bd9Sstevel@tonic-gate 
2531*7c478bd9Sstevel@tonic-gate 			goto done;
2532*7c478bd9Sstevel@tonic-gate 		}
2533*7c478bd9Sstevel@tonic-gate 	}
2534*7c478bd9Sstevel@tonic-gate 
2535*7c478bd9Sstevel@tonic-gate 	/*
2536*7c478bd9Sstevel@tonic-gate 	 * if there is data or FNDELAY, return available data
2537*7c478bd9Sstevel@tonic-gate 	 */
2538*7c478bd9Sstevel@tonic-gate 	if ((len >= bp->b_bcount) ||
2539*7c478bd9Sstevel@tonic-gate 	    (epp->ep_xfer_oflag & (FNDELAY | FNONBLOCK))) {
2540*7c478bd9Sstevel@tonic-gate 		if (epp->ep_data) {
2541*7c478bd9Sstevel@tonic-gate 			bcopy(epp->ep_data->b_rptr, bp->b_un.b_addr, len);
2542*7c478bd9Sstevel@tonic-gate 			epp->ep_data->b_rptr += len;
2543*7c478bd9Sstevel@tonic-gate 			bp->b_resid = bp->b_bcount - len;
2544*7c478bd9Sstevel@tonic-gate 		} else {
2545*7c478bd9Sstevel@tonic-gate 			bp->b_resid = bp->b_bcount;
2546*7c478bd9Sstevel@tonic-gate 		}
2547*7c478bd9Sstevel@tonic-gate 	} else {
2548*7c478bd9Sstevel@tonic-gate 		/* otherwise just wait for data */
2549*7c478bd9Sstevel@tonic-gate 		*wait = B_TRUE;
2550*7c478bd9Sstevel@tonic-gate 	}
2551*7c478bd9Sstevel@tonic-gate 
2552*7c478bd9Sstevel@tonic-gate done:
2553*7c478bd9Sstevel@tonic-gate 	if (epp->ep_data && (epp->ep_data->b_rptr == epp->ep_data->b_wptr)) {
2554*7c478bd9Sstevel@tonic-gate 		freemsg(epp->ep_data);
2555*7c478bd9Sstevel@tonic-gate 		epp->ep_data = NULL;
2556*7c478bd9Sstevel@tonic-gate 	}
2557*7c478bd9Sstevel@tonic-gate 
2558*7c478bd9Sstevel@tonic-gate 	if (*wait) {
2559*7c478bd9Sstevel@tonic-gate 		ASSERT(epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_ON);
2560*7c478bd9Sstevel@tonic-gate 	}
2561*7c478bd9Sstevel@tonic-gate 
2562*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2563*7c478bd9Sstevel@tonic-gate 	    "ugen_epx_intr_IN_req end: rval=%d bcount=%lu len=%d data=0x%p",
2564*7c478bd9Sstevel@tonic-gate 	    rval, bp->b_bcount, len, epp->ep_data);
2565*7c478bd9Sstevel@tonic-gate 
2566*7c478bd9Sstevel@tonic-gate 	return (rval);
2567*7c478bd9Sstevel@tonic-gate }
2568*7c478bd9Sstevel@tonic-gate 
2569*7c478bd9Sstevel@tonic-gate 
2570*7c478bd9Sstevel@tonic-gate /*
2571*7c478bd9Sstevel@tonic-gate  * Start polling on interrupt endpoint, synchronously
2572*7c478bd9Sstevel@tonic-gate  */
2573*7c478bd9Sstevel@tonic-gate static int
2574*7c478bd9Sstevel@tonic-gate ugen_epx_intr_IN_start_polling(ugen_state_t *ugenp, ugen_ep_t *epp)
2575*7c478bd9Sstevel@tonic-gate {
2576*7c478bd9Sstevel@tonic-gate 	int rval = USB_FAILURE;
2577*7c478bd9Sstevel@tonic-gate 	usb_intr_req_t	*reqp;
2578*7c478bd9Sstevel@tonic-gate 	usb_flags_t uflag;
2579*7c478bd9Sstevel@tonic-gate 
2580*7c478bd9Sstevel@tonic-gate 	/*
2581*7c478bd9Sstevel@tonic-gate 	 * if polling is being stopped, we restart polling in the
2582*7c478bd9Sstevel@tonic-gate 	 * interrrupt callback again
2583*7c478bd9Sstevel@tonic-gate 	 */
2584*7c478bd9Sstevel@tonic-gate 	if (epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_IS_STOPPED) {
2585*7c478bd9Sstevel@tonic-gate 
2586*7c478bd9Sstevel@tonic-gate 		return (rval);
2587*7c478bd9Sstevel@tonic-gate 	}
2588*7c478bd9Sstevel@tonic-gate 	if ((epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_ON) == 0) {
2589*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2590*7c478bd9Sstevel@tonic-gate 		    "ugen_epx_intr_IN_start_polling: epp=0x%p state=0x%x",
2591*7c478bd9Sstevel@tonic-gate 		    epp, epp->ep_state);
2592*7c478bd9Sstevel@tonic-gate 
2593*7c478bd9Sstevel@tonic-gate 		epp->ep_state |= UGEN_EP_STATE_INTR_IN_POLLING_ON;
2594*7c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
2595*7c478bd9Sstevel@tonic-gate 
2596*7c478bd9Sstevel@tonic-gate 		reqp = usb_alloc_intr_req(ugenp->ug_dip, 0,
2597*7c478bd9Sstevel@tonic-gate 						USB_FLAGS_SLEEP);
2598*7c478bd9Sstevel@tonic-gate 		reqp->intr_client_private = (usb_opaque_t)ugenp;
2599*7c478bd9Sstevel@tonic-gate 
2600*7c478bd9Sstevel@tonic-gate 		reqp->intr_attributes	= USB_ATTRS_AUTOCLEARING |
2601*7c478bd9Sstevel@tonic-gate 						USB_ATTRS_SHORT_XFER_OK;
2602*7c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
2603*7c478bd9Sstevel@tonic-gate 		if (epp->ep_one_xfer) {
2604*7c478bd9Sstevel@tonic-gate 			reqp->intr_attributes |= USB_ATTRS_ONE_XFER;
2605*7c478bd9Sstevel@tonic-gate 			uflag = USB_FLAGS_NOSLEEP;
2606*7c478bd9Sstevel@tonic-gate 		} else {
2607*7c478bd9Sstevel@tonic-gate 			uflag = USB_FLAGS_SLEEP;
2608*7c478bd9Sstevel@tonic-gate 		}
2609*7c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
2610*7c478bd9Sstevel@tonic-gate 
2611*7c478bd9Sstevel@tonic-gate 		reqp->intr_len		= epp->ep_descr.wMaxPacketSize;
2612*7c478bd9Sstevel@tonic-gate 		reqp->intr_cb		= ugen_epx_intr_IN_req_cb;
2613*7c478bd9Sstevel@tonic-gate 		reqp->intr_exc_cb	= ugen_epx_intr_IN_req_cb;
2614*7c478bd9Sstevel@tonic-gate 
2615*7c478bd9Sstevel@tonic-gate 
2616*7c478bd9Sstevel@tonic-gate 		if ((rval = usb_pipe_intr_xfer(epp->ep_ph, reqp,
2617*7c478bd9Sstevel@tonic-gate 		    uflag)) != USB_SUCCESS) {
2618*7c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2619*7c478bd9Sstevel@tonic-gate 			    "ugen_epx_intr_IN_start_polling: failed %d", rval);
2620*7c478bd9Sstevel@tonic-gate 			usb_free_intr_req(reqp);
2621*7c478bd9Sstevel@tonic-gate 		}
2622*7c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
2623*7c478bd9Sstevel@tonic-gate 		if (rval != USB_SUCCESS) {
2624*7c478bd9Sstevel@tonic-gate 			epp->ep_state &= ~UGEN_EP_STATE_INTR_IN_POLLING_ON;
2625*7c478bd9Sstevel@tonic-gate 		}
2626*7c478bd9Sstevel@tonic-gate 	} else {
2627*7c478bd9Sstevel@tonic-gate 		rval = USB_SUCCESS;
2628*7c478bd9Sstevel@tonic-gate 	}
2629*7c478bd9Sstevel@tonic-gate 
2630*7c478bd9Sstevel@tonic-gate 	return (rval);
2631*7c478bd9Sstevel@tonic-gate }
2632*7c478bd9Sstevel@tonic-gate 
2633*7c478bd9Sstevel@tonic-gate 
2634*7c478bd9Sstevel@tonic-gate /*
2635*7c478bd9Sstevel@tonic-gate  * stop polling on an interrupt endpoint, asynchronously
2636*7c478bd9Sstevel@tonic-gate  */
2637*7c478bd9Sstevel@tonic-gate static void
2638*7c478bd9Sstevel@tonic-gate ugen_epx_intr_IN_stop_polling(ugen_state_t *ugenp, ugen_ep_t *epp)
2639*7c478bd9Sstevel@tonic-gate {
2640*7c478bd9Sstevel@tonic-gate 	if ((epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_ON) &&
2641*7c478bd9Sstevel@tonic-gate 	    ((epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_IS_STOPPED) == 0)) {
2642*7c478bd9Sstevel@tonic-gate 
2643*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2644*7c478bd9Sstevel@tonic-gate 		    "ugen_epx_intr_IN_stop_polling: epp=0x%p state=0x%x",
2645*7c478bd9Sstevel@tonic-gate 		    epp, epp->ep_state);
2646*7c478bd9Sstevel@tonic-gate 
2647*7c478bd9Sstevel@tonic-gate 		epp->ep_state |= UGEN_EP_STATE_INTR_IN_POLLING_IS_STOPPED;
2648*7c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
2649*7c478bd9Sstevel@tonic-gate 		usb_pipe_stop_intr_polling(epp->ep_ph, USB_FLAGS_NOSLEEP);
2650*7c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
2651*7c478bd9Sstevel@tonic-gate 	}
2652*7c478bd9Sstevel@tonic-gate }
2653*7c478bd9Sstevel@tonic-gate 
2654*7c478bd9Sstevel@tonic-gate 
2655*7c478bd9Sstevel@tonic-gate /*
2656*7c478bd9Sstevel@tonic-gate  * poll management
2657*7c478bd9Sstevel@tonic-gate  */
2658*7c478bd9Sstevel@tonic-gate static void
2659*7c478bd9Sstevel@tonic-gate ugen_epx_intr_IN_poll_wakeup(ugen_state_t *ugenp, ugen_ep_t *epp)
2660*7c478bd9Sstevel@tonic-gate {
2661*7c478bd9Sstevel@tonic-gate 	if (epp->ep_state & UGEN_EP_STATE_INTR_IN_POLL_PENDING) {
2662*7c478bd9Sstevel@tonic-gate 		struct pollhead *phpp = &epp->ep_pollhead;
2663*7c478bd9Sstevel@tonic-gate 
2664*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2665*7c478bd9Sstevel@tonic-gate 		    "ugen_epx_intr_IN_poll_wakeup: state=0x%x", epp->ep_state);
2666*7c478bd9Sstevel@tonic-gate 
2667*7c478bd9Sstevel@tonic-gate 		epp->ep_state &= ~UGEN_EP_STATE_INTR_IN_POLL_PENDING;
2668*7c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
2669*7c478bd9Sstevel@tonic-gate 		pollwakeup(phpp, POLLIN);
2670*7c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
2671*7c478bd9Sstevel@tonic-gate 	}
2672*7c478bd9Sstevel@tonic-gate }
2673*7c478bd9Sstevel@tonic-gate 
2674*7c478bd9Sstevel@tonic-gate 
2675*7c478bd9Sstevel@tonic-gate /*
2676*7c478bd9Sstevel@tonic-gate  * callback functions for interrupt IN pipe
2677*7c478bd9Sstevel@tonic-gate  */
2678*7c478bd9Sstevel@tonic-gate static void
2679*7c478bd9Sstevel@tonic-gate ugen_epx_intr_IN_req_cb(usb_pipe_handle_t ph, usb_intr_req_t *reqp)
2680*7c478bd9Sstevel@tonic-gate {
2681*7c478bd9Sstevel@tonic-gate 	ugen_state_t *ugenp = (ugen_state_t *)reqp->intr_client_private;
2682*7c478bd9Sstevel@tonic-gate 	ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
2683*7c478bd9Sstevel@tonic-gate 
2684*7c478bd9Sstevel@tonic-gate 	if (epp == NULL) {
2685*7c478bd9Sstevel@tonic-gate 		/* pipe is closing */
2686*7c478bd9Sstevel@tonic-gate 
2687*7c478bd9Sstevel@tonic-gate 		goto done;
2688*7c478bd9Sstevel@tonic-gate 	}
2689*7c478bd9Sstevel@tonic-gate 
2690*7c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
2691*7c478bd9Sstevel@tonic-gate 
2692*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2693*7c478bd9Sstevel@tonic-gate 	    "ugen_epx_intr_IN_req_cb:\n\t"
2694*7c478bd9Sstevel@tonic-gate 	    "epp=0x%p state=0x%x ph=0x%p reqp=0x%p cr=%d cb=0x%x len=%d",
2695*7c478bd9Sstevel@tonic-gate 	    epp, epp->ep_state, ph, reqp, reqp->intr_completion_reason,
2696*7c478bd9Sstevel@tonic-gate 	    reqp->intr_cb_flags,
2697*7c478bd9Sstevel@tonic-gate 	    (reqp->intr_data == NULL) ? 0 :
2698*7c478bd9Sstevel@tonic-gate 	    reqp->intr_data->b_wptr - reqp->intr_data->b_rptr);
2699*7c478bd9Sstevel@tonic-gate 
2700*7c478bd9Sstevel@tonic-gate 	ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
2701*7c478bd9Sstevel@tonic-gate 
2702*7c478bd9Sstevel@tonic-gate 	if (epp->ep_data && reqp->intr_data) {
2703*7c478bd9Sstevel@tonic-gate 		mblk_t *mp;
2704*7c478bd9Sstevel@tonic-gate 
2705*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2706*7c478bd9Sstevel@tonic-gate 		    "ep%x coalesce data", epp->ep_descr.bEndpointAddress);
2707*7c478bd9Sstevel@tonic-gate 
2708*7c478bd9Sstevel@tonic-gate 		/* coalesce the data into one mblk */
2709*7c478bd9Sstevel@tonic-gate 		epp->ep_data->b_cont = reqp->intr_data;
2710*7c478bd9Sstevel@tonic-gate 		if ((mp = msgpullup(epp->ep_data, -1)) != NULL) {
2711*7c478bd9Sstevel@tonic-gate 			reqp->intr_data = NULL;
2712*7c478bd9Sstevel@tonic-gate 			freemsg(epp->ep_data);
2713*7c478bd9Sstevel@tonic-gate 			epp->ep_data = mp;
2714*7c478bd9Sstevel@tonic-gate 		} else {
2715*7c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2716*7c478bd9Sstevel@tonic-gate 			    "msgpullup failed, discard data");
2717*7c478bd9Sstevel@tonic-gate 			epp->ep_data->b_cont = NULL;
2718*7c478bd9Sstevel@tonic-gate 		}
2719*7c478bd9Sstevel@tonic-gate 	} else if (reqp->intr_data) {
2720*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2721*7c478bd9Sstevel@tonic-gate 		    "setting ep_data");
2722*7c478bd9Sstevel@tonic-gate 
2723*7c478bd9Sstevel@tonic-gate 		epp->ep_data = reqp->intr_data;
2724*7c478bd9Sstevel@tonic-gate 		reqp->intr_data = NULL;
2725*7c478bd9Sstevel@tonic-gate 	}
2726*7c478bd9Sstevel@tonic-gate 
2727*7c478bd9Sstevel@tonic-gate 	switch (reqp->intr_completion_reason) {
2728*7c478bd9Sstevel@tonic-gate 	case USB_CR_OK:
2729*7c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
2730*7c478bd9Sstevel@tonic-gate 
2731*7c478bd9Sstevel@tonic-gate 		break;
2732*7c478bd9Sstevel@tonic-gate 	case USB_CR_PIPE_RESET:
2733*7c478bd9Sstevel@tonic-gate 	case USB_CR_STOPPED_POLLING:
2734*7c478bd9Sstevel@tonic-gate 
2735*7c478bd9Sstevel@tonic-gate 		break;
2736*7c478bd9Sstevel@tonic-gate 	default:
2737*7c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status =
2738*7c478bd9Sstevel@tonic-gate 		    ugen_cr2lcstat(reqp->intr_completion_reason);
2739*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2740*7c478bd9Sstevel@tonic-gate 		    "ugen_exp_intr_cb_req: lcmd_status=0x%x",
2741*7c478bd9Sstevel@tonic-gate 		    epp->ep_lcmd_status);
2742*7c478bd9Sstevel@tonic-gate 
2743*7c478bd9Sstevel@tonic-gate 		break;
2744*7c478bd9Sstevel@tonic-gate 	}
2745*7c478bd9Sstevel@tonic-gate 
2746*7c478bd9Sstevel@tonic-gate 	/* any non-zero completion reason stops polling */
2747*7c478bd9Sstevel@tonic-gate 	if ((reqp->intr_completion_reason) ||
2748*7c478bd9Sstevel@tonic-gate 	    (epp->ep_one_xfer)) {
2749*7c478bd9Sstevel@tonic-gate 		epp->ep_state &= ~(UGEN_EP_STATE_INTR_IN_POLLING_ON |
2750*7c478bd9Sstevel@tonic-gate 				    UGEN_EP_STATE_INTR_IN_POLLING_IS_STOPPED);
2751*7c478bd9Sstevel@tonic-gate 	}
2752*7c478bd9Sstevel@tonic-gate 
2753*7c478bd9Sstevel@tonic-gate 	/* is there a poll pending? should we stop polling? */
2754*7c478bd9Sstevel@tonic-gate 	if (epp->ep_data) {
2755*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2756*7c478bd9Sstevel@tonic-gate 		    "ugen_epx_intr_IN_req_cb: data len=0x%x",
2757*7c478bd9Sstevel@tonic-gate 		    epp->ep_data->b_wptr - epp->ep_data->b_rptr);
2758*7c478bd9Sstevel@tonic-gate 
2759*7c478bd9Sstevel@tonic-gate 		ugen_epx_intr_IN_poll_wakeup(ugenp, epp);
2760*7c478bd9Sstevel@tonic-gate 
2761*7c478bd9Sstevel@tonic-gate 		/* if there is no space left, stop polling */
2762*7c478bd9Sstevel@tonic-gate 		if (epp->ep_data &&
2763*7c478bd9Sstevel@tonic-gate 		    ((epp->ep_data->b_wptr - epp->ep_data->b_rptr) >=
2764*7c478bd9Sstevel@tonic-gate 		    epp->ep_buf_limit)) {
2765*7c478bd9Sstevel@tonic-gate 			ugen_epx_intr_IN_stop_polling(ugenp, epp);
2766*7c478bd9Sstevel@tonic-gate 		}
2767*7c478bd9Sstevel@tonic-gate 	}
2768*7c478bd9Sstevel@tonic-gate 
2769*7c478bd9Sstevel@tonic-gate 	if (reqp->intr_completion_reason && epp->ep_bp) {
2770*7c478bd9Sstevel@tonic-gate 		bioerror(epp->ep_bp, EIO);
2771*7c478bd9Sstevel@tonic-gate 		epp->ep_done++;
2772*7c478bd9Sstevel@tonic-gate 		cv_signal(&epp->ep_wait_cv);
2773*7c478bd9Sstevel@tonic-gate 
2774*7c478bd9Sstevel@tonic-gate 	/* can we satisfy the read now */
2775*7c478bd9Sstevel@tonic-gate 	} else if (epp->ep_data && epp->ep_bp &&
2776*7c478bd9Sstevel@tonic-gate 	    (!epp->ep_done || epp->ep_one_xfer)) {
2777*7c478bd9Sstevel@tonic-gate 		boolean_t wait;
2778*7c478bd9Sstevel@tonic-gate 
2779*7c478bd9Sstevel@tonic-gate 		if ((ugen_epx_intr_IN_req(ugenp, epp, epp->ep_bp, &wait) ==
2780*7c478bd9Sstevel@tonic-gate 		    USB_SUCCESS) && (wait == B_FALSE)) {
2781*7c478bd9Sstevel@tonic-gate 			epp->ep_done++;
2782*7c478bd9Sstevel@tonic-gate 			cv_signal(&epp->ep_wait_cv);
2783*7c478bd9Sstevel@tonic-gate 		}
2784*7c478bd9Sstevel@tonic-gate 	}
2785*7c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
2786*7c478bd9Sstevel@tonic-gate 
2787*7c478bd9Sstevel@tonic-gate done:
2788*7c478bd9Sstevel@tonic-gate 	usb_free_intr_req(reqp);
2789*7c478bd9Sstevel@tonic-gate }
2790*7c478bd9Sstevel@tonic-gate 
2791*7c478bd9Sstevel@tonic-gate 
2792*7c478bd9Sstevel@tonic-gate /*
2793*7c478bd9Sstevel@tonic-gate  * handle intr OUT xfers
2794*7c478bd9Sstevel@tonic-gate  */
2795*7c478bd9Sstevel@tonic-gate static int
2796*7c478bd9Sstevel@tonic-gate ugen_epx_intr_OUT_req(ugen_state_t *ugenp, ugen_ep_t *epp,
2797*7c478bd9Sstevel@tonic-gate     struct buf *bp, boolean_t *wait)
2798*7c478bd9Sstevel@tonic-gate {
2799*7c478bd9Sstevel@tonic-gate 	int	rval = USB_SUCCESS;
2800*7c478bd9Sstevel@tonic-gate 	usb_intr_req_t	*reqp;
2801*7c478bd9Sstevel@tonic-gate 
2802*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2803*7c478bd9Sstevel@tonic-gate 	    "ugen_epx_intr_OUT_req: epp=0x%p state=0x%x bp=0x%p",
2804*7c478bd9Sstevel@tonic-gate 	    epp, epp->ep_state, bp);
2805*7c478bd9Sstevel@tonic-gate 
2806*7c478bd9Sstevel@tonic-gate 	reqp = usb_alloc_intr_req(ugenp->ug_dip, bp->b_bcount,
2807*7c478bd9Sstevel@tonic-gate 					USB_FLAGS_NOSLEEP);
2808*7c478bd9Sstevel@tonic-gate 	if (reqp == NULL) {
2809*7c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status = USB_LC_STAT_NO_RESOURCES;
2810*7c478bd9Sstevel@tonic-gate 
2811*7c478bd9Sstevel@tonic-gate 		return (USB_NO_RESOURCES);
2812*7c478bd9Sstevel@tonic-gate 	}
2813*7c478bd9Sstevel@tonic-gate 
2814*7c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_XS_OPEN);
2815*7c478bd9Sstevel@tonic-gate 
2816*7c478bd9Sstevel@tonic-gate 	reqp->intr_timeout	= ugen_intr_timeout;
2817*7c478bd9Sstevel@tonic-gate 	reqp->intr_client_private = (usb_opaque_t)ugenp;
2818*7c478bd9Sstevel@tonic-gate 	reqp->intr_len		= bp->b_bcount;
2819*7c478bd9Sstevel@tonic-gate 	reqp->intr_attributes	= USB_ATTRS_AUTOCLEARING;
2820*7c478bd9Sstevel@tonic-gate 	reqp->intr_cb		= ugen_epx_intr_OUT_req_cb;
2821*7c478bd9Sstevel@tonic-gate 	reqp->intr_exc_cb	= ugen_epx_intr_OUT_req_cb;
2822*7c478bd9Sstevel@tonic-gate 
2823*7c478bd9Sstevel@tonic-gate 	/* copy data from bp */
2824*7c478bd9Sstevel@tonic-gate 	bcopy(epp->ep_bp->b_un.b_addr, reqp->intr_data->b_rptr,
2825*7c478bd9Sstevel@tonic-gate 							bp->b_bcount);
2826*7c478bd9Sstevel@tonic-gate 	reqp->intr_data->b_wptr += bp->b_bcount;
2827*7c478bd9Sstevel@tonic-gate 
2828*7c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
2829*7c478bd9Sstevel@tonic-gate 	if ((rval = usb_pipe_intr_xfer(epp->ep_ph, reqp,
2830*7c478bd9Sstevel@tonic-gate 	    USB_FLAGS_NOSLEEP)) != USB_SUCCESS) {
2831*7c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
2832*7c478bd9Sstevel@tonic-gate 		epp->ep_lcmd_status =
2833*7c478bd9Sstevel@tonic-gate 		    ugen_cr2lcstat(reqp->intr_completion_reason);
2834*7c478bd9Sstevel@tonic-gate 		usb_free_intr_req(reqp);
2835*7c478bd9Sstevel@tonic-gate 		bioerror(bp, EIO);
2836*7c478bd9Sstevel@tonic-gate 	} else {
2837*7c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
2838*7c478bd9Sstevel@tonic-gate 	}
2839*7c478bd9Sstevel@tonic-gate 	*wait = (rval == USB_SUCCESS) ? B_TRUE : B_FALSE;
2840*7c478bd9Sstevel@tonic-gate 
2841*7c478bd9Sstevel@tonic-gate 	return (rval);
2842*7c478bd9Sstevel@tonic-gate }
2843*7c478bd9Sstevel@tonic-gate 
2844*7c478bd9Sstevel@tonic-gate 
2845*7c478bd9Sstevel@tonic-gate /*
2846*7c478bd9Sstevel@tonic-gate  * callback functions for interrupt OUT pipe
2847*7c478bd9Sstevel@tonic-gate  */
2848*7c478bd9Sstevel@tonic-gate static void
2849*7c478bd9Sstevel@tonic-gate ugen_epx_intr_OUT_req_cb(usb_pipe_handle_t ph, usb_intr_req_t *reqp)
2850*7c478bd9Sstevel@tonic-gate {
2851*7c478bd9Sstevel@tonic-gate 	ugen_state_t *ugenp = (ugen_state_t *)reqp->intr_client_private;
2852*7c478bd9Sstevel@tonic-gate 	ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
2853*7c478bd9Sstevel@tonic-gate 
2854*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2855*7c478bd9Sstevel@tonic-gate 	    "ugen_epx_intr_OUT_req_cb: ph=0x%p reqp=0x%p cr=%d cb=0x%x",
2856*7c478bd9Sstevel@tonic-gate 	    ph, reqp, reqp->intr_completion_reason, reqp->intr_cb_flags);
2857*7c478bd9Sstevel@tonic-gate 
2858*7c478bd9Sstevel@tonic-gate 	ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
2859*7c478bd9Sstevel@tonic-gate 
2860*7c478bd9Sstevel@tonic-gate 	/* epp might be NULL if we are closing the pipe */
2861*7c478bd9Sstevel@tonic-gate 	if (epp) {
2862*7c478bd9Sstevel@tonic-gate 		int len;
2863*7c478bd9Sstevel@tonic-gate 
2864*7c478bd9Sstevel@tonic-gate 		mutex_enter(&epp->ep_mutex);
2865*7c478bd9Sstevel@tonic-gate 		if (epp->ep_bp) {
2866*7c478bd9Sstevel@tonic-gate 			len = min(reqp->intr_data->b_wptr -
2867*7c478bd9Sstevel@tonic-gate 			    reqp->intr_data->b_rptr, epp->ep_bp->b_bcount);
2868*7c478bd9Sstevel@tonic-gate 
2869*7c478bd9Sstevel@tonic-gate 			epp->ep_bp->b_resid = epp->ep_bp->b_bcount - len;
2870*7c478bd9Sstevel@tonic-gate 
2871*7c478bd9Sstevel@tonic-gate 			switch (reqp->intr_completion_reason) {
2872*7c478bd9Sstevel@tonic-gate 			case USB_CR_OK:
2873*7c478bd9Sstevel@tonic-gate 				epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
2874*7c478bd9Sstevel@tonic-gate 
2875*7c478bd9Sstevel@tonic-gate 				break;
2876*7c478bd9Sstevel@tonic-gate 			case USB_CR_PIPE_RESET:
2877*7c478bd9Sstevel@tonic-gate 
2878*7c478bd9Sstevel@tonic-gate 				break;
2879*7c478bd9Sstevel@tonic-gate 			default:
2880*7c478bd9Sstevel@tonic-gate 				epp->ep_lcmd_status =
2881*7c478bd9Sstevel@tonic-gate 				    ugen_cr2lcstat(
2882*7c478bd9Sstevel@tonic-gate 				    reqp->intr_completion_reason);
2883*7c478bd9Sstevel@tonic-gate 				bioerror(epp->ep_bp, EIO);
2884*7c478bd9Sstevel@tonic-gate 			}
2885*7c478bd9Sstevel@tonic-gate 		}
2886*7c478bd9Sstevel@tonic-gate 		epp->ep_done++;
2887*7c478bd9Sstevel@tonic-gate 		cv_signal(&epp->ep_wait_cv);
2888*7c478bd9Sstevel@tonic-gate 		mutex_exit(&epp->ep_mutex);
2889*7c478bd9Sstevel@tonic-gate 	}
2890*7c478bd9Sstevel@tonic-gate 
2891*7c478bd9Sstevel@tonic-gate 	usb_free_intr_req(reqp);
2892*7c478bd9Sstevel@tonic-gate }
2893*7c478bd9Sstevel@tonic-gate 
2894*7c478bd9Sstevel@tonic-gate 
2895*7c478bd9Sstevel@tonic-gate /*
2896*7c478bd9Sstevel@tonic-gate  * Endpoint status node management
2897*7c478bd9Sstevel@tonic-gate  *
2898*7c478bd9Sstevel@tonic-gate  * open/close an endpoint status node.
2899*7c478bd9Sstevel@tonic-gate  *
2900*7c478bd9Sstevel@tonic-gate  * Return values: errno
2901*7c478bd9Sstevel@tonic-gate  */
2902*7c478bd9Sstevel@tonic-gate static int
2903*7c478bd9Sstevel@tonic-gate ugen_eps_open(ugen_state_t *ugenp, dev_t dev, int flag)
2904*7c478bd9Sstevel@tonic-gate {
2905*7c478bd9Sstevel@tonic-gate 	ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
2906*7c478bd9Sstevel@tonic-gate 	int rval = EBUSY;
2907*7c478bd9Sstevel@tonic-gate 
2908*7c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
2909*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_STAT, ugenp->ug_log_hdl,
2910*7c478bd9Sstevel@tonic-gate 	    "ugen_eps_open: dev=0x%lx flag=0x%x state=0x%x",
2911*7c478bd9Sstevel@tonic-gate 	    dev, flag, epp->ep_state);
2912*7c478bd9Sstevel@tonic-gate 
2913*7c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_ACTIVE);
2914*7c478bd9Sstevel@tonic-gate 
2915*7c478bd9Sstevel@tonic-gate 	/* only one open at the time */
2916*7c478bd9Sstevel@tonic-gate 	if ((epp->ep_state & UGEN_EP_STATE_STAT_OPEN) == 0) {
2917*7c478bd9Sstevel@tonic-gate 		epp->ep_state |= UGEN_EP_STATE_STAT_OPEN;
2918*7c478bd9Sstevel@tonic-gate 		epp->ep_stat_oflag = flag;
2919*7c478bd9Sstevel@tonic-gate 		rval = 0;
2920*7c478bd9Sstevel@tonic-gate 	}
2921*7c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
2922*7c478bd9Sstevel@tonic-gate 
2923*7c478bd9Sstevel@tonic-gate 	return (rval);
2924*7c478bd9Sstevel@tonic-gate }
2925*7c478bd9Sstevel@tonic-gate 
2926*7c478bd9Sstevel@tonic-gate 
2927*7c478bd9Sstevel@tonic-gate /*
2928*7c478bd9Sstevel@tonic-gate  * close endpoint status
2929*7c478bd9Sstevel@tonic-gate  */
2930*7c478bd9Sstevel@tonic-gate static void
2931*7c478bd9Sstevel@tonic-gate ugen_eps_close(ugen_state_t *ugenp, dev_t dev, int flag)
2932*7c478bd9Sstevel@tonic-gate {
2933*7c478bd9Sstevel@tonic-gate 	ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
2934*7c478bd9Sstevel@tonic-gate 
2935*7c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
2936*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_STAT, ugenp->ug_log_hdl,
2937*7c478bd9Sstevel@tonic-gate 	    "ugen_eps_close: dev=0x%lx flag=0x%x state=0x%x",
2938*7c478bd9Sstevel@tonic-gate 	    dev, flag, epp->ep_state);
2939*7c478bd9Sstevel@tonic-gate 
2940*7c478bd9Sstevel@tonic-gate 	epp->ep_state &= ~(UGEN_EP_STATE_STAT_OPEN |
2941*7c478bd9Sstevel@tonic-gate 			UGEN_EP_STATE_INTR_IN_POLL_PENDING);
2942*7c478bd9Sstevel@tonic-gate 	epp->ep_one_xfer = B_FALSE;
2943*7c478bd9Sstevel@tonic-gate 
2944*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_STAT, ugenp->ug_log_hdl,
2945*7c478bd9Sstevel@tonic-gate 	    "ugen_eps_close: state=0x%x", epp->ep_state);
2946*7c478bd9Sstevel@tonic-gate 
2947*7c478bd9Sstevel@tonic-gate 	ASSERT(epp->ep_state & UGEN_EP_STATE_ACTIVE);
2948*7c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
2949*7c478bd9Sstevel@tonic-gate }
2950*7c478bd9Sstevel@tonic-gate 
2951*7c478bd9Sstevel@tonic-gate 
2952*7c478bd9Sstevel@tonic-gate /*
2953*7c478bd9Sstevel@tonic-gate  * return status info
2954*7c478bd9Sstevel@tonic-gate  *
2955*7c478bd9Sstevel@tonic-gate  * Return values: errno
2956*7c478bd9Sstevel@tonic-gate  */
2957*7c478bd9Sstevel@tonic-gate static int
2958*7c478bd9Sstevel@tonic-gate ugen_eps_req(ugen_state_t *ugenp, struct buf *bp)
2959*7c478bd9Sstevel@tonic-gate {
2960*7c478bd9Sstevel@tonic-gate 	ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, bp->b_edev)];
2961*7c478bd9Sstevel@tonic-gate 
2962*7c478bd9Sstevel@tonic-gate 	mutex_enter(&epp->ep_mutex);
2963*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2964*7c478bd9Sstevel@tonic-gate 	    "ugen_eps_req: bp=0x%p lcmd_status=0x%x bcount=%lu",
2965*7c478bd9Sstevel@tonic-gate 	    bp, epp->ep_lcmd_status, bp->b_bcount);
2966*7c478bd9Sstevel@tonic-gate 
2967*7c478bd9Sstevel@tonic-gate 	if (bp->b_flags & B_READ) {
2968*7c478bd9Sstevel@tonic-gate 		int len = min(sizeof (epp->ep_lcmd_status), bp->b_bcount);
2969*7c478bd9Sstevel@tonic-gate 		if (len) {
2970*7c478bd9Sstevel@tonic-gate 			bcopy(&epp->ep_lcmd_status, bp->b_un.b_addr, len);
2971*7c478bd9Sstevel@tonic-gate 		}
2972*7c478bd9Sstevel@tonic-gate 		bp->b_resid = bp->b_bcount - len;
2973*7c478bd9Sstevel@tonic-gate 	} else {
2974*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2975*7c478bd9Sstevel@tonic-gate 		    "ugen_eps_req: control=0x%x",
2976*7c478bd9Sstevel@tonic-gate 		    *((char *)(bp->b_un.b_addr)));
2977*7c478bd9Sstevel@tonic-gate 
2978*7c478bd9Sstevel@tonic-gate 		if (epp->ep_state & UGEN_EP_STATE_XFER_OPEN) {
2979*7c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2980*7c478bd9Sstevel@tonic-gate 			    "ugen_eps_req: cannot change one xfer mode if "
2981*7c478bd9Sstevel@tonic-gate 			    "endpoint is open");
2982*7c478bd9Sstevel@tonic-gate 
2983*7c478bd9Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
2984*7c478bd9Sstevel@tonic-gate 
2985*7c478bd9Sstevel@tonic-gate 			return (EINVAL);
2986*7c478bd9Sstevel@tonic-gate 		}
2987*7c478bd9Sstevel@tonic-gate 
2988*7c478bd9Sstevel@tonic-gate 		if ((epp->ep_descr.bmAttributes & USB_EP_ATTR_INTR) &&
2989*7c478bd9Sstevel@tonic-gate 		    (epp->ep_descr.bEndpointAddress & USB_EP_DIR_IN)) {
2990*7c478bd9Sstevel@tonic-gate 			epp->ep_one_xfer = (*((char *)(bp->b_un.b_addr)) &
2991*7c478bd9Sstevel@tonic-gate 			    USB_EP_INTR_ONE_XFER) ? B_TRUE : B_FALSE;
2992*7c478bd9Sstevel@tonic-gate 		} else {
2993*7c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2994*7c478bd9Sstevel@tonic-gate 			    "ugen_eps_req: not an interrupt endpoint");
2995*7c478bd9Sstevel@tonic-gate 
2996*7c478bd9Sstevel@tonic-gate 			mutex_exit(&epp->ep_mutex);
2997*7c478bd9Sstevel@tonic-gate 
2998*7c478bd9Sstevel@tonic-gate 			return (EINVAL);
2999*7c478bd9Sstevel@tonic-gate 		}
3000*7c478bd9Sstevel@tonic-gate 
3001*7c478bd9Sstevel@tonic-gate 		bp->b_resid = bp->b_bcount - 1;
3002*7c478bd9Sstevel@tonic-gate 	}
3003*7c478bd9Sstevel@tonic-gate 	mutex_exit(&epp->ep_mutex);
3004*7c478bd9Sstevel@tonic-gate 
3005*7c478bd9Sstevel@tonic-gate 	return (0);
3006*7c478bd9Sstevel@tonic-gate }
3007*7c478bd9Sstevel@tonic-gate 
3008*7c478bd9Sstevel@tonic-gate 
3009*7c478bd9Sstevel@tonic-gate /*
3010*7c478bd9Sstevel@tonic-gate  * device status node management
3011*7c478bd9Sstevel@tonic-gate  */
3012*7c478bd9Sstevel@tonic-gate static int
3013*7c478bd9Sstevel@tonic-gate ugen_ds_init(ugen_state_t *ugenp)
3014*7c478bd9Sstevel@tonic-gate {
3015*7c478bd9Sstevel@tonic-gate 	cv_init(&ugenp->ug_ds.dev_wait_cv, NULL, CV_DRIVER, NULL);
3016*7c478bd9Sstevel@tonic-gate 
3017*7c478bd9Sstevel@tonic-gate 	/* Create devstat minor node for this instance */
3018*7c478bd9Sstevel@tonic-gate 	if (ugen_ds_minor_nodes_create(ugenp) != USB_SUCCESS) {
3019*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
3020*7c478bd9Sstevel@tonic-gate 		    "ugen_create_dev_stat_minor_nodes failed");
3021*7c478bd9Sstevel@tonic-gate 
3022*7c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
3023*7c478bd9Sstevel@tonic-gate 	}
3024*7c478bd9Sstevel@tonic-gate 
3025*7c478bd9Sstevel@tonic-gate 
3026*7c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
3027*7c478bd9Sstevel@tonic-gate }
3028*7c478bd9Sstevel@tonic-gate 
3029*7c478bd9Sstevel@tonic-gate 
3030*7c478bd9Sstevel@tonic-gate static void
3031*7c478bd9Sstevel@tonic-gate ugen_ds_destroy(ugen_state_t *ugenp)
3032*7c478bd9Sstevel@tonic-gate {
3033*7c478bd9Sstevel@tonic-gate 	cv_destroy(&ugenp->ug_ds.dev_wait_cv);
3034*7c478bd9Sstevel@tonic-gate }
3035*7c478bd9Sstevel@tonic-gate 
3036*7c478bd9Sstevel@tonic-gate 
3037*7c478bd9Sstevel@tonic-gate /*
3038*7c478bd9Sstevel@tonic-gate  * open devstat minor node
3039*7c478bd9Sstevel@tonic-gate  *
3040*7c478bd9Sstevel@tonic-gate  * Return values: errno
3041*7c478bd9Sstevel@tonic-gate  */
3042*7c478bd9Sstevel@tonic-gate static int
3043*7c478bd9Sstevel@tonic-gate ugen_ds_open(ugen_state_t *ugenp, dev_t dev, int flag)
3044*7c478bd9Sstevel@tonic-gate {
3045*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3046*7c478bd9Sstevel@tonic-gate 	    "ugen_ds_open: dev=0x%lx flag=0x%x", dev, flag);
3047*7c478bd9Sstevel@tonic-gate 
3048*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
3049*7c478bd9Sstevel@tonic-gate 	if ((ugenp->ug_ds.dev_stat & UGEN_DEV_STATUS_ACTIVE) == 0) {
3050*7c478bd9Sstevel@tonic-gate 		/*
3051*7c478bd9Sstevel@tonic-gate 		 * first read on device node should return status
3052*7c478bd9Sstevel@tonic-gate 		 */
3053*7c478bd9Sstevel@tonic-gate 		ugenp->ug_ds.dev_stat |= UGEN_DEV_STATUS_CHANGED |
3054*7c478bd9Sstevel@tonic-gate 						UGEN_DEV_STATUS_ACTIVE;
3055*7c478bd9Sstevel@tonic-gate 		ugenp->ug_ds.dev_oflag = flag;
3056*7c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
3057*7c478bd9Sstevel@tonic-gate 
3058*7c478bd9Sstevel@tonic-gate 		return (0);
3059*7c478bd9Sstevel@tonic-gate 	} else {
3060*7c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
3061*7c478bd9Sstevel@tonic-gate 
3062*7c478bd9Sstevel@tonic-gate 		return (EBUSY);
3063*7c478bd9Sstevel@tonic-gate 	}
3064*7c478bd9Sstevel@tonic-gate }
3065*7c478bd9Sstevel@tonic-gate 
3066*7c478bd9Sstevel@tonic-gate 
3067*7c478bd9Sstevel@tonic-gate static void
3068*7c478bd9Sstevel@tonic-gate ugen_ds_close(ugen_state_t *ugenp, dev_t dev, int flag)
3069*7c478bd9Sstevel@tonic-gate {
3070*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3071*7c478bd9Sstevel@tonic-gate 	    "ugen_ds_close: dev=0x%lx flag=0x%x", dev, flag);
3072*7c478bd9Sstevel@tonic-gate 
3073*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
3074*7c478bd9Sstevel@tonic-gate 	ugenp->ug_ds.dev_stat = UGEN_DEV_STATUS_INACTIVE;
3075*7c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
3076*7c478bd9Sstevel@tonic-gate }
3077*7c478bd9Sstevel@tonic-gate 
3078*7c478bd9Sstevel@tonic-gate 
3079*7c478bd9Sstevel@tonic-gate /*
3080*7c478bd9Sstevel@tonic-gate  * request for devstat
3081*7c478bd9Sstevel@tonic-gate  *
3082*7c478bd9Sstevel@tonic-gate  * Return values: errno
3083*7c478bd9Sstevel@tonic-gate  */
3084*7c478bd9Sstevel@tonic-gate static int
3085*7c478bd9Sstevel@tonic-gate ugen_ds_req(ugen_state_t *ugenp, struct buf *bp)
3086*7c478bd9Sstevel@tonic-gate {
3087*7c478bd9Sstevel@tonic-gate 	int len = min(sizeof (ugenp->ug_ds.dev_state), bp->b_bcount);
3088*7c478bd9Sstevel@tonic-gate 
3089*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3090*7c478bd9Sstevel@tonic-gate 	    "ugen_ds_req: bp=0x%p", bp);
3091*7c478bd9Sstevel@tonic-gate 
3092*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
3093*7c478bd9Sstevel@tonic-gate 	if ((ugenp->ug_ds.dev_oflag & (FNDELAY | FNONBLOCK)) == 0) {
3094*7c478bd9Sstevel@tonic-gate 		while ((ugenp->ug_ds.dev_stat &
3095*7c478bd9Sstevel@tonic-gate 		    UGEN_DEV_STATUS_CHANGED) == 0) {
3096*7c478bd9Sstevel@tonic-gate 			if (cv_wait_sig(&ugenp->ug_ds.dev_wait_cv,
3097*7c478bd9Sstevel@tonic-gate 			    &ugenp->ug_mutex) <= 0) {
3098*7c478bd9Sstevel@tonic-gate 				mutex_exit(&ugenp->ug_mutex);
3099*7c478bd9Sstevel@tonic-gate 
3100*7c478bd9Sstevel@tonic-gate 				return (EINTR);
3101*7c478bd9Sstevel@tonic-gate 			}
3102*7c478bd9Sstevel@tonic-gate 		}
3103*7c478bd9Sstevel@tonic-gate 	} else if ((ugenp->ug_ds.dev_stat & UGEN_DEV_STATUS_CHANGED) ==
3104*7c478bd9Sstevel@tonic-gate 	    0) {
3105*7c478bd9Sstevel@tonic-gate 		bp->b_resid = bp->b_bcount;
3106*7c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
3107*7c478bd9Sstevel@tonic-gate 
3108*7c478bd9Sstevel@tonic-gate 		return (0);
3109*7c478bd9Sstevel@tonic-gate 	}
3110*7c478bd9Sstevel@tonic-gate 
3111*7c478bd9Sstevel@tonic-gate 	ugenp->ug_ds.dev_stat &= ~UGEN_DEV_STATUS_CHANGED;
3112*7c478bd9Sstevel@tonic-gate 	switch (ugenp->ug_dev_state) {
3113*7c478bd9Sstevel@tonic-gate 	case USB_DEV_ONLINE:
3114*7c478bd9Sstevel@tonic-gate 		ugenp->ug_ds.dev_state = USB_DEV_STAT_ONLINE;
3115*7c478bd9Sstevel@tonic-gate 
3116*7c478bd9Sstevel@tonic-gate 		break;
3117*7c478bd9Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
3118*7c478bd9Sstevel@tonic-gate 		ugenp->ug_ds.dev_state = USB_DEV_STAT_DISCONNECTED;
3119*7c478bd9Sstevel@tonic-gate 
3120*7c478bd9Sstevel@tonic-gate 		break;
3121*7c478bd9Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
3122*7c478bd9Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RESUME:
3123*7c478bd9Sstevel@tonic-gate 		ugenp->ug_ds.dev_state = USB_DEV_STAT_RESUMED;
3124*7c478bd9Sstevel@tonic-gate 
3125*7c478bd9Sstevel@tonic-gate 		break;
3126*7c478bd9Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
3127*7c478bd9Sstevel@tonic-gate 	default:
3128*7c478bd9Sstevel@tonic-gate 		ugenp->ug_ds.dev_state = USB_DEV_STAT_UNAVAILABLE;
3129*7c478bd9Sstevel@tonic-gate 
3130*7c478bd9Sstevel@tonic-gate 		break;
3131*7c478bd9Sstevel@tonic-gate 	}
3132*7c478bd9Sstevel@tonic-gate 
3133*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3134*7c478bd9Sstevel@tonic-gate 	    "ugen_ds_req: dev_state=0x%x dev_stat=0x%x",
3135*7c478bd9Sstevel@tonic-gate 	    ugenp->ug_dev_state, ugenp->ug_ds.dev_stat);
3136*7c478bd9Sstevel@tonic-gate 
3137*7c478bd9Sstevel@tonic-gate 	bcopy(&ugenp->ug_ds.dev_state, bp->b_un.b_addr, len);
3138*7c478bd9Sstevel@tonic-gate 	bp->b_resid = bp->b_bcount - len;
3139*7c478bd9Sstevel@tonic-gate 
3140*7c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
3141*7c478bd9Sstevel@tonic-gate 
3142*7c478bd9Sstevel@tonic-gate 	return (0);
3143*7c478bd9Sstevel@tonic-gate }
3144*7c478bd9Sstevel@tonic-gate 
3145*7c478bd9Sstevel@tonic-gate 
3146*7c478bd9Sstevel@tonic-gate static void
3147*7c478bd9Sstevel@tonic-gate ugen_ds_change(ugen_state_t *ugenp)
3148*7c478bd9Sstevel@tonic-gate {
3149*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3150*7c478bd9Sstevel@tonic-gate 	    "ugen_ds_change:");
3151*7c478bd9Sstevel@tonic-gate 
3152*7c478bd9Sstevel@tonic-gate 	ugenp->ug_ds.dev_stat |= UGEN_DEV_STATUS_CHANGED;
3153*7c478bd9Sstevel@tonic-gate 	cv_signal(&ugenp->ug_ds.dev_wait_cv);
3154*7c478bd9Sstevel@tonic-gate }
3155*7c478bd9Sstevel@tonic-gate 
3156*7c478bd9Sstevel@tonic-gate 
3157*7c478bd9Sstevel@tonic-gate /*
3158*7c478bd9Sstevel@tonic-gate  * poll management
3159*7c478bd9Sstevel@tonic-gate  */
3160*7c478bd9Sstevel@tonic-gate static void
3161*7c478bd9Sstevel@tonic-gate ugen_ds_poll_wakeup(ugen_state_t *ugenp)
3162*7c478bd9Sstevel@tonic-gate {
3163*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3164*7c478bd9Sstevel@tonic-gate 	    "ugen_ds_poll_wakeup:");
3165*7c478bd9Sstevel@tonic-gate 
3166*7c478bd9Sstevel@tonic-gate 	if (ugenp->ug_ds.dev_stat & UGEN_DEV_STATUS_POLL_PENDING) {
3167*7c478bd9Sstevel@tonic-gate 		struct pollhead *phpp = &ugenp->ug_ds.dev_pollhead;
3168*7c478bd9Sstevel@tonic-gate 		ugenp->ug_ds.dev_stat &= ~UGEN_DEV_STATUS_POLL_PENDING;
3169*7c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
3170*7c478bd9Sstevel@tonic-gate 		pollwakeup(phpp, POLLIN);
3171*7c478bd9Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
3172*7c478bd9Sstevel@tonic-gate 	}
3173*7c478bd9Sstevel@tonic-gate }
3174*7c478bd9Sstevel@tonic-gate 
3175*7c478bd9Sstevel@tonic-gate 
3176*7c478bd9Sstevel@tonic-gate /*
3177*7c478bd9Sstevel@tonic-gate  * minor node management:
3178*7c478bd9Sstevel@tonic-gate  */
3179*7c478bd9Sstevel@tonic-gate static int
3180*7c478bd9Sstevel@tonic-gate ugen_ds_minor_nodes_create(ugen_state_t *ugenp)
3181*7c478bd9Sstevel@tonic-gate {
3182*7c478bd9Sstevel@tonic-gate 	char	node_name[32];
3183*7c478bd9Sstevel@tonic-gate 	int	vid = ugenp->ug_dev_data->dev_descr->idVendor;
3184*7c478bd9Sstevel@tonic-gate 	int	pid = ugenp->ug_dev_data->dev_descr->idProduct;
3185*7c478bd9Sstevel@tonic-gate 	minor_t	minor;
3186*7c478bd9Sstevel@tonic-gate 	int	minor_index;
3187*7c478bd9Sstevel@tonic-gate 	int	owns_device = (usb_owns_device(ugenp->ug_dip) ?
3188*7c478bd9Sstevel@tonic-gate 						UGEN_OWNS_DEVICE : 0);
3189*7c478bd9Sstevel@tonic-gate 
3190*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
3191*7c478bd9Sstevel@tonic-gate 	    "ugen_ds_minor_nodes_create: idx shift=%d inst shift=%d",
3192*7c478bd9Sstevel@tonic-gate 	    UGEN_MINOR_IDX_SHIFT(ugenp),
3193*7c478bd9Sstevel@tonic-gate 	    UGEN_MINOR_INSTANCE_SHIFT(ugenp));
3194*7c478bd9Sstevel@tonic-gate 
3195*7c478bd9Sstevel@tonic-gate 	if (ugenp->ug_instance >= UGEN_MINOR_INSTANCE_LIMIT(ugenp)) {
3196*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L0(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
3197*7c478bd9Sstevel@tonic-gate 		    "instance number too high (%d)", ugenp->ug_instance);
3198*7c478bd9Sstevel@tonic-gate 
3199*7c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
3200*7c478bd9Sstevel@tonic-gate 	}
3201*7c478bd9Sstevel@tonic-gate 
3202*7c478bd9Sstevel@tonic-gate 	/* create devstat minor node */
3203*7c478bd9Sstevel@tonic-gate 	if (owns_device) {
3204*7c478bd9Sstevel@tonic-gate 		(void) sprintf(node_name, "%x.%x.devstat", vid, pid);
3205*7c478bd9Sstevel@tonic-gate 	} else {
3206*7c478bd9Sstevel@tonic-gate 		(void) sprintf(node_name, "%x.%x.if%ddevstat", vid, pid,
3207*7c478bd9Sstevel@tonic-gate 		    ugenp->ug_dev_data->dev_curr_if);
3208*7c478bd9Sstevel@tonic-gate 	}
3209*7c478bd9Sstevel@tonic-gate 
3210*7c478bd9Sstevel@tonic-gate 	minor_index = ugen_minor_index_create(ugenp,
3211*7c478bd9Sstevel@tonic-gate 	    (UGEN_MINOR_DEV_STAT_NODE | owns_device) <<
3212*7c478bd9Sstevel@tonic-gate 	    UGEN_MINOR_IDX_SHIFT(ugenp));
3213*7c478bd9Sstevel@tonic-gate 
3214*7c478bd9Sstevel@tonic-gate 	if (minor_index < 0) {
3215*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L0(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
3216*7c478bd9Sstevel@tonic-gate 		    "too many minor nodes");
3217*7c478bd9Sstevel@tonic-gate 
3218*7c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
3219*7c478bd9Sstevel@tonic-gate 	}
3220*7c478bd9Sstevel@tonic-gate 	minor = (minor_index << UGEN_MINOR_IDX_SHIFT(ugenp)) |
3221*7c478bd9Sstevel@tonic-gate 	    ugenp->ug_instance << UGEN_MINOR_INSTANCE_SHIFT(ugenp);
3222*7c478bd9Sstevel@tonic-gate 
3223*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
3224*7c478bd9Sstevel@tonic-gate 	    "minor=0x%x minor_index=%d name=%s",
3225*7c478bd9Sstevel@tonic-gate 	    minor, minor_index, node_name);
3226*7c478bd9Sstevel@tonic-gate 
3227*7c478bd9Sstevel@tonic-gate 	ASSERT(minor < L_MAXMIN);
3228*7c478bd9Sstevel@tonic-gate 
3229*7c478bd9Sstevel@tonic-gate 	if ((ddi_create_minor_node(ugenp->ug_dip, node_name,
3230*7c478bd9Sstevel@tonic-gate 	    S_IFCHR, minor, DDI_NT_UGEN, 0)) != DDI_SUCCESS) {
3231*7c478bd9Sstevel@tonic-gate 
3232*7c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
3233*7c478bd9Sstevel@tonic-gate 	}
3234*7c478bd9Sstevel@tonic-gate 
3235*7c478bd9Sstevel@tonic-gate 	ugen_store_devt(ugenp, minor);
3236*7c478bd9Sstevel@tonic-gate 
3237*7c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
3238*7c478bd9Sstevel@tonic-gate }
3239*7c478bd9Sstevel@tonic-gate 
3240*7c478bd9Sstevel@tonic-gate 
3241*7c478bd9Sstevel@tonic-gate /*
3242*7c478bd9Sstevel@tonic-gate  * utility functions:
3243*7c478bd9Sstevel@tonic-gate  *
3244*7c478bd9Sstevel@tonic-gate  * conversion from completion reason to  USB_LC_STAT_*
3245*7c478bd9Sstevel@tonic-gate  */
3246*7c478bd9Sstevel@tonic-gate static struct ugen_cr2lcstat_entry {
3247*7c478bd9Sstevel@tonic-gate 	int	cr;
3248*7c478bd9Sstevel@tonic-gate 	int	lcstat;
3249*7c478bd9Sstevel@tonic-gate } ugen_cr2lcstat_table[] = {
3250*7c478bd9Sstevel@tonic-gate 	{ USB_CR_OK,			USB_LC_STAT_NOERROR	},
3251*7c478bd9Sstevel@tonic-gate 	{ USB_CR_CRC,			USB_LC_STAT_CRC		},
3252*7c478bd9Sstevel@tonic-gate 	{ USB_CR_BITSTUFFING,		USB_LC_STAT_BITSTUFFING },
3253*7c478bd9Sstevel@tonic-gate 	{ USB_CR_DATA_TOGGLE_MM,	USB_LC_STAT_DATA_TOGGLE_MM },
3254*7c478bd9Sstevel@tonic-gate 	{ USB_CR_STALL,			USB_LC_STAT_STALL	},
3255*7c478bd9Sstevel@tonic-gate 	{ USB_CR_DEV_NOT_RESP,		USB_LC_STAT_DEV_NOT_RESP },
3256*7c478bd9Sstevel@tonic-gate 	{ USB_CR_PID_CHECKFAILURE,	USB_LC_STAT_PID_CHECKFAILURE },
3257*7c478bd9Sstevel@tonic-gate 	{ USB_CR_UNEXP_PID,		USB_LC_STAT_UNEXP_PID	},
3258*7c478bd9Sstevel@tonic-gate 	{ USB_CR_DATA_OVERRUN,		USB_LC_STAT_DATA_OVERRUN },
3259*7c478bd9Sstevel@tonic-gate 	{ USB_CR_DATA_UNDERRUN,		USB_LC_STAT_DATA_UNDERRUN },
3260*7c478bd9Sstevel@tonic-gate 	{ USB_CR_BUFFER_OVERRUN,	USB_LC_STAT_BUFFER_OVERRUN },
3261*7c478bd9Sstevel@tonic-gate 	{ USB_CR_BUFFER_UNDERRUN,	USB_LC_STAT_BUFFER_UNDERRUN },
3262*7c478bd9Sstevel@tonic-gate 	{ USB_CR_TIMEOUT,		USB_LC_STAT_TIMEOUT	},
3263*7c478bd9Sstevel@tonic-gate 	{ USB_CR_NOT_ACCESSED,		USB_LC_STAT_NOT_ACCESSED },
3264*7c478bd9Sstevel@tonic-gate 	{ USB_CR_NO_RESOURCES,		USB_LC_STAT_NO_BANDWIDTH },
3265*7c478bd9Sstevel@tonic-gate 	{ USB_CR_UNSPECIFIED_ERR,	USB_LC_STAT_UNSPECIFIED_ERR },
3266*7c478bd9Sstevel@tonic-gate 	{ USB_CR_STOPPED_POLLING,	USB_LC_STAT_HW_ERR	},
3267*7c478bd9Sstevel@tonic-gate 	{ USB_CR_PIPE_CLOSING,		USB_LC_STAT_UNSPECIFIED_ERR	},
3268*7c478bd9Sstevel@tonic-gate 	{ USB_CR_PIPE_RESET,		USB_LC_STAT_UNSPECIFIED_ERR	},
3269*7c478bd9Sstevel@tonic-gate 	{ USB_CR_NOT_SUPPORTED,		USB_LC_STAT_UNSPECIFIED_ERR },
3270*7c478bd9Sstevel@tonic-gate 	{ USB_CR_FLUSHED,		USB_LC_STAT_UNSPECIFIED_ERR }
3271*7c478bd9Sstevel@tonic-gate };
3272*7c478bd9Sstevel@tonic-gate 
3273*7c478bd9Sstevel@tonic-gate #define	UGEN_CR2LCSTAT_TABLE_SIZE (sizeof (ugen_cr2lcstat_table) / \
3274*7c478bd9Sstevel@tonic-gate 			sizeof (struct ugen_cr2lcstat_entry))
3275*7c478bd9Sstevel@tonic-gate static int
3276*7c478bd9Sstevel@tonic-gate ugen_cr2lcstat(int cr)
3277*7c478bd9Sstevel@tonic-gate {
3278*7c478bd9Sstevel@tonic-gate 	int i;
3279*7c478bd9Sstevel@tonic-gate 
3280*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < UGEN_CR2LCSTAT_TABLE_SIZE; i++) {
3281*7c478bd9Sstevel@tonic-gate 		if (ugen_cr2lcstat_table[i].cr == cr) {
3282*7c478bd9Sstevel@tonic-gate 
3283*7c478bd9Sstevel@tonic-gate 			return (ugen_cr2lcstat_table[i].lcstat);
3284*7c478bd9Sstevel@tonic-gate 		}
3285*7c478bd9Sstevel@tonic-gate 	}
3286*7c478bd9Sstevel@tonic-gate 
3287*7c478bd9Sstevel@tonic-gate 	return (USB_LC_STAT_UNSPECIFIED_ERR);
3288*7c478bd9Sstevel@tonic-gate }
3289*7c478bd9Sstevel@tonic-gate 
3290*7c478bd9Sstevel@tonic-gate 
3291*7c478bd9Sstevel@tonic-gate /*
3292*7c478bd9Sstevel@tonic-gate  * create and lookup minor index
3293*7c478bd9Sstevel@tonic-gate  */
3294*7c478bd9Sstevel@tonic-gate static int
3295*7c478bd9Sstevel@tonic-gate ugen_minor_index_create(ugen_state_t *ugenp, ugen_minor_t minor)
3296*7c478bd9Sstevel@tonic-gate {
3297*7c478bd9Sstevel@tonic-gate 	int i;
3298*7c478bd9Sstevel@tonic-gate 
3299*7c478bd9Sstevel@tonic-gate 	/* check if already in the table */
3300*7c478bd9Sstevel@tonic-gate 	for (i = 1; i < ugenp->ug_minor_node_table_index; i++) {
3301*7c478bd9Sstevel@tonic-gate 		if (ugenp->ug_minor_node_table[i] == minor) {
3302*7c478bd9Sstevel@tonic-gate 
3303*7c478bd9Sstevel@tonic-gate 			return (-1);
3304*7c478bd9Sstevel@tonic-gate 		}
3305*7c478bd9Sstevel@tonic-gate 	}
3306*7c478bd9Sstevel@tonic-gate 	if (ugenp->ug_minor_node_table_index <
3307*7c478bd9Sstevel@tonic-gate 	    (ugenp->ug_minor_node_table_size/sizeof (ugen_minor_t))) {
3308*7c478bd9Sstevel@tonic-gate 		ugenp->ug_minor_node_table[ugenp->
3309*7c478bd9Sstevel@tonic-gate 				ug_minor_node_table_index] = minor;
3310*7c478bd9Sstevel@tonic-gate 
3311*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
3312*7c478bd9Sstevel@tonic-gate 		    "ugen_minor_index_create: %d: 0x%lx",
3313*7c478bd9Sstevel@tonic-gate 		    ugenp->ug_minor_node_table_index,
3314*7c478bd9Sstevel@tonic-gate 		    minor);
3315*7c478bd9Sstevel@tonic-gate 
3316*7c478bd9Sstevel@tonic-gate 		return (ugenp->ug_minor_node_table_index++);
3317*7c478bd9Sstevel@tonic-gate 	} else {
3318*7c478bd9Sstevel@tonic-gate 
3319*7c478bd9Sstevel@tonic-gate 		return (-1);
3320*7c478bd9Sstevel@tonic-gate 	}
3321*7c478bd9Sstevel@tonic-gate }
3322*7c478bd9Sstevel@tonic-gate 
3323*7c478bd9Sstevel@tonic-gate 
3324*7c478bd9Sstevel@tonic-gate static ugen_minor_t
3325*7c478bd9Sstevel@tonic-gate ugen_devt2minor(ugen_state_t *ugenp, dev_t dev)
3326*7c478bd9Sstevel@tonic-gate {
3327*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
3328*7c478bd9Sstevel@tonic-gate 	    "ugen_devt2minor: minorcode=%d, minor=0x%" PRIx64,
3329*7c478bd9Sstevel@tonic-gate 	    UGEN_MINOR_GET_IDX(ugenp, dev),
3330*7c478bd9Sstevel@tonic-gate 	    ugenp->ug_minor_node_table[UGEN_MINOR_GET_IDX(ugenp, dev)]);
3331*7c478bd9Sstevel@tonic-gate 
3332*7c478bd9Sstevel@tonic-gate 	ASSERT(UGEN_MINOR_GET_IDX(ugenp, dev) <
3333*7c478bd9Sstevel@tonic-gate 			ugenp->ug_minor_node_table_index);
3334*7c478bd9Sstevel@tonic-gate 
3335*7c478bd9Sstevel@tonic-gate 	return (ugenp->ug_minor_node_table[UGEN_MINOR_GET_IDX(ugenp, dev)]);
3336*7c478bd9Sstevel@tonic-gate }
3337*7c478bd9Sstevel@tonic-gate 
3338*7c478bd9Sstevel@tonic-gate 
3339*7c478bd9Sstevel@tonic-gate static void
3340*7c478bd9Sstevel@tonic-gate ugen_minor_node_table_create(ugen_state_t *ugenp)
3341*7c478bd9Sstevel@tonic-gate {
3342*7c478bd9Sstevel@tonic-gate 	size_t	size = sizeof (ugen_minor_t) * UGEN_MINOR_IDX_LIMIT(ugenp);
3343*7c478bd9Sstevel@tonic-gate 
3344*7c478bd9Sstevel@tonic-gate 	/* allocate the max table size needed, we reduce later */
3345*7c478bd9Sstevel@tonic-gate 	ugenp->ug_minor_node_table = kmem_zalloc(size, KM_SLEEP);
3346*7c478bd9Sstevel@tonic-gate 	ugenp->ug_minor_node_table_size = size;
3347*7c478bd9Sstevel@tonic-gate 	ugenp->ug_minor_node_table_index = 1;
3348*7c478bd9Sstevel@tonic-gate }
3349*7c478bd9Sstevel@tonic-gate 
3350*7c478bd9Sstevel@tonic-gate 
3351*7c478bd9Sstevel@tonic-gate static void
3352*7c478bd9Sstevel@tonic-gate ugen_minor_node_table_shrink(ugen_state_t *ugenp)
3353*7c478bd9Sstevel@tonic-gate {
3354*7c478bd9Sstevel@tonic-gate 	/* reduce the table size to save some memory */
3355*7c478bd9Sstevel@tonic-gate 	if (ugenp->ug_minor_node_table_index < UGEN_MINOR_IDX_LIMIT(ugenp)) {
3356*7c478bd9Sstevel@tonic-gate 		size_t newsize = sizeof (ugen_minor_t) *
3357*7c478bd9Sstevel@tonic-gate 				ugenp->ug_minor_node_table_index;
3358*7c478bd9Sstevel@tonic-gate 		ugen_minor_t *buf = kmem_zalloc(newsize, KM_SLEEP);
3359*7c478bd9Sstevel@tonic-gate 
3360*7c478bd9Sstevel@tonic-gate 		bcopy(ugenp->ug_minor_node_table, buf, newsize);
3361*7c478bd9Sstevel@tonic-gate 		kmem_free(ugenp->ug_minor_node_table,
3362*7c478bd9Sstevel@tonic-gate 					ugenp->ug_minor_node_table_size);
3363*7c478bd9Sstevel@tonic-gate 		ugenp->ug_minor_node_table = buf;
3364*7c478bd9Sstevel@tonic-gate 		ugenp->ug_minor_node_table_size = newsize;
3365*7c478bd9Sstevel@tonic-gate 	}
3366*7c478bd9Sstevel@tonic-gate }
3367*7c478bd9Sstevel@tonic-gate 
3368*7c478bd9Sstevel@tonic-gate 
3369*7c478bd9Sstevel@tonic-gate static void
3370*7c478bd9Sstevel@tonic-gate ugen_minor_node_table_destroy(ugen_state_t *ugenp)
3371*7c478bd9Sstevel@tonic-gate {
3372*7c478bd9Sstevel@tonic-gate 	if (ugenp->ug_minor_node_table) {
3373*7c478bd9Sstevel@tonic-gate 		kmem_free(ugenp->ug_minor_node_table,
3374*7c478bd9Sstevel@tonic-gate 				ugenp->ug_minor_node_table_size);
3375*7c478bd9Sstevel@tonic-gate 	}
3376*7c478bd9Sstevel@tonic-gate }
3377*7c478bd9Sstevel@tonic-gate 
3378*7c478bd9Sstevel@tonic-gate 
3379*7c478bd9Sstevel@tonic-gate static void
3380*7c478bd9Sstevel@tonic-gate ugen_check_mask(uint_t mask, uint_t *shift, uint_t *limit)
3381*7c478bd9Sstevel@tonic-gate {
3382*7c478bd9Sstevel@tonic-gate 	uint_t i, j;
3383*7c478bd9Sstevel@tonic-gate 
3384*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < UGEN_MINOR_NODE_SIZE; i++) {
3385*7c478bd9Sstevel@tonic-gate 		if ((1 << i)  & mask) {
3386*7c478bd9Sstevel@tonic-gate 
3387*7c478bd9Sstevel@tonic-gate 			break;
3388*7c478bd9Sstevel@tonic-gate 		}
3389*7c478bd9Sstevel@tonic-gate 	}
3390*7c478bd9Sstevel@tonic-gate 
3391*7c478bd9Sstevel@tonic-gate 	for (j = i; j < UGEN_MINOR_NODE_SIZE; j++) {
3392*7c478bd9Sstevel@tonic-gate 		if (((1 << j) & mask) == 0) {
3393*7c478bd9Sstevel@tonic-gate 
3394*7c478bd9Sstevel@tonic-gate 			break;
3395*7c478bd9Sstevel@tonic-gate 		}
3396*7c478bd9Sstevel@tonic-gate 	}
3397*7c478bd9Sstevel@tonic-gate 
3398*7c478bd9Sstevel@tonic-gate 	*limit = (i == j) ? 0 : 1 << (j - i);
3399*7c478bd9Sstevel@tonic-gate 	*shift = i;
3400*7c478bd9Sstevel@tonic-gate }
3401*7c478bd9Sstevel@tonic-gate 
3402*7c478bd9Sstevel@tonic-gate 
3403*7c478bd9Sstevel@tonic-gate 
3404*7c478bd9Sstevel@tonic-gate /*
3405*7c478bd9Sstevel@tonic-gate  * power management:
3406*7c478bd9Sstevel@tonic-gate  *
3407*7c478bd9Sstevel@tonic-gate  * ugen_pm_init:
3408*7c478bd9Sstevel@tonic-gate  *	Initialize power management and remote wakeup functionality.
3409*7c478bd9Sstevel@tonic-gate  *	No mutex is necessary in this function as it's called only by attach.
3410*7c478bd9Sstevel@tonic-gate  */
3411*7c478bd9Sstevel@tonic-gate static void
3412*7c478bd9Sstevel@tonic-gate ugen_pm_init(ugen_state_t *ugenp)
3413*7c478bd9Sstevel@tonic-gate {
3414*7c478bd9Sstevel@tonic-gate 	dev_info_t	*dip = ugenp->ug_dip;
3415*7c478bd9Sstevel@tonic-gate 	ugen_power_t	*ugenpm;
3416*7c478bd9Sstevel@tonic-gate 
3417*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_PM, ugenp->ug_log_hdl,
3418*7c478bd9Sstevel@tonic-gate 	    "ugen_pm_init:");
3419*7c478bd9Sstevel@tonic-gate 
3420*7c478bd9Sstevel@tonic-gate 	/* Allocate the state structure */
3421*7c478bd9Sstevel@tonic-gate 	ugenpm = kmem_zalloc(sizeof (ugen_power_t), KM_SLEEP);
3422*7c478bd9Sstevel@tonic-gate 
3423*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
3424*7c478bd9Sstevel@tonic-gate 	ugenp->ug_pm = ugenpm;
3425*7c478bd9Sstevel@tonic-gate 	ugenpm->pwr_wakeup_enabled = B_FALSE;
3426*7c478bd9Sstevel@tonic-gate 	ugenpm->pwr_current = USB_DEV_OS_FULL_PWR;
3427*7c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
3428*7c478bd9Sstevel@tonic-gate 
3429*7c478bd9Sstevel@tonic-gate 	/*
3430*7c478bd9Sstevel@tonic-gate 	 * If remote wakeup is not available you may not want to do
3431*7c478bd9Sstevel@tonic-gate 	 * power management.
3432*7c478bd9Sstevel@tonic-gate 	 */
3433*7c478bd9Sstevel@tonic-gate 	if (ugen_enable_pm || usb_handle_remote_wakeup(dip,
3434*7c478bd9Sstevel@tonic-gate 	    USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) {
3435*7c478bd9Sstevel@tonic-gate 		if (usb_create_pm_components(dip,
3436*7c478bd9Sstevel@tonic-gate 		    &ugenpm->pwr_states) == USB_SUCCESS) {
3437*7c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L4(UGEN_PRINT_PM,
3438*7c478bd9Sstevel@tonic-gate 			    ugenp->ug_log_hdl,
3439*7c478bd9Sstevel@tonic-gate 			    "ugen_pm_init: "
3440*7c478bd9Sstevel@tonic-gate 			    "created PM components");
3441*7c478bd9Sstevel@tonic-gate 
3442*7c478bd9Sstevel@tonic-gate 			mutex_enter(&ugenp->ug_mutex);
3443*7c478bd9Sstevel@tonic-gate 			ugenpm->pwr_wakeup_enabled = B_TRUE;
3444*7c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
3445*7c478bd9Sstevel@tonic-gate 
3446*7c478bd9Sstevel@tonic-gate 			if (pm_raise_power(dip, 0,
3447*7c478bd9Sstevel@tonic-gate 			    USB_DEV_OS_FULL_PWR) != DDI_SUCCESS) {
3448*7c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(UGEN_PRINT_PM,
3449*7c478bd9Sstevel@tonic-gate 				    ugenp->ug_log_hdl,
3450*7c478bd9Sstevel@tonic-gate 				    "ugen_pm_init: "
3451*7c478bd9Sstevel@tonic-gate 				    "raising power failed");
3452*7c478bd9Sstevel@tonic-gate 			}
3453*7c478bd9Sstevel@tonic-gate 		} else {
3454*7c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(UGEN_PRINT_PM,
3455*7c478bd9Sstevel@tonic-gate 			    ugenp->ug_log_hdl,
3456*7c478bd9Sstevel@tonic-gate 			    "ugen_pm_init: "
3457*7c478bd9Sstevel@tonic-gate 			    "create_pm_comps failed");
3458*7c478bd9Sstevel@tonic-gate 		}
3459*7c478bd9Sstevel@tonic-gate 	} else {
3460*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_PM,
3461*7c478bd9Sstevel@tonic-gate 		    ugenp->ug_log_hdl, "ugen_pm_init: "
3462*7c478bd9Sstevel@tonic-gate 		    "failure enabling remote wakeup");
3463*7c478bd9Sstevel@tonic-gate 	}
3464*7c478bd9Sstevel@tonic-gate 
3465*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_PM, ugenp->ug_log_hdl,
3466*7c478bd9Sstevel@tonic-gate 	    "ugen_pm_init: end");
3467*7c478bd9Sstevel@tonic-gate }
3468*7c478bd9Sstevel@tonic-gate 
3469*7c478bd9Sstevel@tonic-gate 
3470*7c478bd9Sstevel@tonic-gate /*
3471*7c478bd9Sstevel@tonic-gate  * ugen_pm_destroy:
3472*7c478bd9Sstevel@tonic-gate  *	Shut down and destroy power management and remote wakeup functionality.
3473*7c478bd9Sstevel@tonic-gate  */
3474*7c478bd9Sstevel@tonic-gate static void
3475*7c478bd9Sstevel@tonic-gate ugen_pm_destroy(ugen_state_t *ugenp)
3476*7c478bd9Sstevel@tonic-gate {
3477*7c478bd9Sstevel@tonic-gate 	dev_info_t *dip = ugenp->ug_dip;
3478*7c478bd9Sstevel@tonic-gate 
3479*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_PM, ugenp->ug_log_hdl,
3480*7c478bd9Sstevel@tonic-gate 	    "ugen_pm_destroy:");
3481*7c478bd9Sstevel@tonic-gate 
3482*7c478bd9Sstevel@tonic-gate 	if (ugenp->ug_pm) {
3483*7c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
3484*7c478bd9Sstevel@tonic-gate 		ugen_pm_busy_component(ugenp);
3485*7c478bd9Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
3486*7c478bd9Sstevel@tonic-gate 
3487*7c478bd9Sstevel@tonic-gate 		if ((ugenp->ug_pm->pwr_wakeup_enabled) &&
3488*7c478bd9Sstevel@tonic-gate 		    (ugenp->ug_dev_state != USB_DEV_DISCONNECTED)) {
3489*7c478bd9Sstevel@tonic-gate 			int rval;
3490*7c478bd9Sstevel@tonic-gate 
3491*7c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
3492*7c478bd9Sstevel@tonic-gate 			(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
3493*7c478bd9Sstevel@tonic-gate 
3494*7c478bd9Sstevel@tonic-gate 			if ((rval = usb_handle_remote_wakeup(dip,
3495*7c478bd9Sstevel@tonic-gate 			    USB_REMOTE_WAKEUP_DISABLE)) != USB_SUCCESS) {
3496*7c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L4(UGEN_PRINT_PM,
3497*7c478bd9Sstevel@tonic-gate 				    ugenp->ug_log_hdl, "ugen_pm_destroy: "
3498*7c478bd9Sstevel@tonic-gate 				    "disabling rmt wakeup: rval=%d", rval);
3499*7c478bd9Sstevel@tonic-gate 			}
3500*7c478bd9Sstevel@tonic-gate 			/*
3501*7c478bd9Sstevel@tonic-gate 			 * Since remote wakeup is disabled now,
3502*7c478bd9Sstevel@tonic-gate 			 * no one can raise power
3503*7c478bd9Sstevel@tonic-gate 			 * and get to device once power is lowered here.
3504*7c478bd9Sstevel@tonic-gate 			 */
3505*7c478bd9Sstevel@tonic-gate 		} else {
3506*7c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
3507*7c478bd9Sstevel@tonic-gate 		}
3508*7c478bd9Sstevel@tonic-gate 		(void) pm_lower_power(dip, 0, USB_DEV_OS_PWR_OFF);
3509*7c478bd9Sstevel@tonic-gate 		ugen_pm_idle_component(ugenp);
3510*7c478bd9Sstevel@tonic-gate 
3511*7c478bd9Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
3512*7c478bd9Sstevel@tonic-gate 		kmem_free(ugenp->ug_pm, sizeof (ugen_power_t));
3513*7c478bd9Sstevel@tonic-gate 		ugenp->ug_pm = NULL;
3514*7c478bd9Sstevel@tonic-gate 	}
3515*7c478bd9Sstevel@tonic-gate }
3516*7c478bd9Sstevel@tonic-gate 
3517*7c478bd9Sstevel@tonic-gate 
3518*7c478bd9Sstevel@tonic-gate /*
3519*7c478bd9Sstevel@tonic-gate  * ugen_power :
3520*7c478bd9Sstevel@tonic-gate  *	Power entry point, the workhorse behind pm_raise_power, pm_lower_power,
3521*7c478bd9Sstevel@tonic-gate  *	usb_req_raise_power and usb_req_lower_power.
3522*7c478bd9Sstevel@tonic-gate  */
3523*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
3524*7c478bd9Sstevel@tonic-gate int
3525*7c478bd9Sstevel@tonic-gate usb_ugen_power(usb_ugen_hdl_t usb_ugen_hdl, int comp, int level)
3526*7c478bd9Sstevel@tonic-gate {
3527*7c478bd9Sstevel@tonic-gate 	ugen_power_t		*pm;
3528*7c478bd9Sstevel@tonic-gate 	int			rval = USB_FAILURE;
3529*7c478bd9Sstevel@tonic-gate 	usb_ugen_hdl_impl_t	*usb_ugen_hdl_impl =
3530*7c478bd9Sstevel@tonic-gate 				(usb_ugen_hdl_impl_t *)usb_ugen_hdl;
3531*7c478bd9Sstevel@tonic-gate 	ugen_state_t		*ugenp;
3532*7c478bd9Sstevel@tonic-gate 	dev_info_t		*dip;
3533*7c478bd9Sstevel@tonic-gate 
3534*7c478bd9Sstevel@tonic-gate 	if (usb_ugen_hdl == NULL) {
3535*7c478bd9Sstevel@tonic-gate 
3536*7c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
3537*7c478bd9Sstevel@tonic-gate 	}
3538*7c478bd9Sstevel@tonic-gate 
3539*7c478bd9Sstevel@tonic-gate 	ugenp = usb_ugen_hdl_impl->hdl_ugenp;
3540*7c478bd9Sstevel@tonic-gate 	dip = ugenp->ug_dip;
3541*7c478bd9Sstevel@tonic-gate 
3542*7c478bd9Sstevel@tonic-gate 	if (ugenp->ug_pm == NULL) {
3543*7c478bd9Sstevel@tonic-gate 
3544*7c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
3545*7c478bd9Sstevel@tonic-gate 	}
3546*7c478bd9Sstevel@tonic-gate 
3547*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(UGEN_PRINT_PM, ugenp->ug_log_hdl,
3548*7c478bd9Sstevel@tonic-gate 	    "usb_ugen_power: level=%d", level);
3549*7c478bd9Sstevel@tonic-gate 
3550*7c478bd9Sstevel@tonic-gate 	(void) usb_serialize_access(ugenp->ug_ser_cookie,
3551*7c478bd9Sstevel@tonic-gate 						USB_WAIT, 0);
3552*7c478bd9Sstevel@tonic-gate 	/*
3553*7c478bd9Sstevel@tonic-gate 	 * If we are disconnected/suspended, return success. Note that if we
3554*7c478bd9Sstevel@tonic-gate 	 * return failure, bringing down the system will hang when
3555*7c478bd9Sstevel@tonic-gate 	 * PM tries to power up all devices
3556*7c478bd9Sstevel@tonic-gate 	 */
3557*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ugenp->ug_mutex);
3558*7c478bd9Sstevel@tonic-gate 	switch (ugenp->ug_dev_state) {
3559*7c478bd9Sstevel@tonic-gate 	case USB_DEV_ONLINE:
3560*7c478bd9Sstevel@tonic-gate 
3561*7c478bd9Sstevel@tonic-gate 		break;
3562*7c478bd9Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
3563*7c478bd9Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
3564*7c478bd9Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RESUME:
3565*7c478bd9Sstevel@tonic-gate 	case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
3566*7c478bd9Sstevel@tonic-gate 	default:
3567*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_PM, ugenp->ug_log_hdl,
3568*7c478bd9Sstevel@tonic-gate 		    "ugen_power: disconnected/suspended "
3569*7c478bd9Sstevel@tonic-gate 		    "dev_state=%d", ugenp->ug_dev_state);
3570*7c478bd9Sstevel@tonic-gate 		rval = USB_SUCCESS;
3571*7c478bd9Sstevel@tonic-gate 
3572*7c478bd9Sstevel@tonic-gate 		goto done;
3573*7c478bd9Sstevel@tonic-gate 	}
3574*7c478bd9Sstevel@tonic-gate 
3575*7c478bd9Sstevel@tonic-gate 	pm = ugenp->ug_pm;
3576*7c478bd9Sstevel@tonic-gate 
3577*7c478bd9Sstevel@tonic-gate 	/* Check if we are transitioning to a legal power level */
3578*7c478bd9Sstevel@tonic-gate 	if (USB_DEV_PWRSTATE_OK(pm->pwr_states, level)) {
3579*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_PM, ugenp->ug_log_hdl,
3580*7c478bd9Sstevel@tonic-gate 		    "ugen_power: illegal power level=%d "
3581*7c478bd9Sstevel@tonic-gate 		    "pwr_states: 0x%x", level, pm->pwr_states);
3582*7c478bd9Sstevel@tonic-gate 
3583*7c478bd9Sstevel@tonic-gate 		goto done;
3584*7c478bd9Sstevel@tonic-gate 	}
3585*7c478bd9Sstevel@tonic-gate 
3586*7c478bd9Sstevel@tonic-gate 	switch (level) {
3587*7c478bd9Sstevel@tonic-gate 	case USB_DEV_OS_PWR_OFF :
3588*7c478bd9Sstevel@tonic-gate 		switch (ugenp->ug_dev_state) {
3589*7c478bd9Sstevel@tonic-gate 		case USB_DEV_ONLINE:
3590*7c478bd9Sstevel@tonic-gate 			/* Deny the powerdown request if the device is busy */
3591*7c478bd9Sstevel@tonic-gate 			if (ugenp->ug_pm->pwr_busy != 0) {
3592*7c478bd9Sstevel@tonic-gate 
3593*7c478bd9Sstevel@tonic-gate 				break;
3594*7c478bd9Sstevel@tonic-gate 			}
3595*7c478bd9Sstevel@tonic-gate 			ASSERT(ugenp->ug_open_count == 0);
3596*7c478bd9Sstevel@tonic-gate 			ASSERT(ugenp->ug_pending_cmds == 0);
3597*7c478bd9Sstevel@tonic-gate 			ugenp->ug_pm->pwr_current = USB_DEV_OS_PWR_OFF;
3598*7c478bd9Sstevel@tonic-gate 			mutex_exit(&ugenp->ug_mutex);
3599*7c478bd9Sstevel@tonic-gate 
3600*7c478bd9Sstevel@tonic-gate 			/* Issue USB D3 command to the device here */
3601*7c478bd9Sstevel@tonic-gate 			rval = usb_set_device_pwrlvl3(dip);
3602*7c478bd9Sstevel@tonic-gate 			mutex_enter(&ugenp->ug_mutex);
3603*7c478bd9Sstevel@tonic-gate 
3604*7c478bd9Sstevel@tonic-gate 			break;
3605*7c478bd9Sstevel@tonic-gate 		default:
3606*7c478bd9Sstevel@tonic-gate 			rval = USB_SUCCESS;
3607*7c478bd9Sstevel@tonic-gate 
3608*7c478bd9Sstevel@tonic-gate 			break;
3609*7c478bd9Sstevel@tonic-gate 		}
3610*7c478bd9Sstevel@tonic-gate 		break;
3611*7c478bd9Sstevel@tonic-gate 	case USB_DEV_OS_FULL_PWR :
3612*7c478bd9Sstevel@tonic-gate 		/*
3613*7c478bd9Sstevel@tonic-gate 		 * PM framework tries to put us in full power during system
3614*7c478bd9Sstevel@tonic-gate 		 * shutdown.
3615*7c478bd9Sstevel@tonic-gate 		 */
3616*7c478bd9Sstevel@tonic-gate 		switch (ugenp->ug_dev_state) {
3617*7c478bd9Sstevel@tonic-gate 		case USB_UGEN_DEV_UNAVAILABLE_RESUME:
3618*7c478bd9Sstevel@tonic-gate 		case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
3619*7c478bd9Sstevel@tonic-gate 
3620*7c478bd9Sstevel@tonic-gate 			break;
3621*7c478bd9Sstevel@tonic-gate 		default:
3622*7c478bd9Sstevel@tonic-gate 			ugenp->ug_dev_state = USB_DEV_ONLINE;
3623*7c478bd9Sstevel@tonic-gate 
3624*7c478bd9Sstevel@tonic-gate 			/* wakeup devstat reads and polls */
3625*7c478bd9Sstevel@tonic-gate 			ugen_ds_change(ugenp);
3626*7c478bd9Sstevel@tonic-gate 			ugen_ds_poll_wakeup(ugenp);
3627*7c478bd9Sstevel@tonic-gate 
3628*7c478bd9Sstevel@tonic-gate 			break;
3629*7c478bd9Sstevel@tonic-gate 		}
3630*7c478bd9Sstevel@tonic-gate 		ugenp->ug_pm->pwr_current = USB_DEV_OS_FULL_PWR;
3631*7c478bd9Sstevel@tonic-gate 		mutex_exit(&ugenp->ug_mutex);
3632*7c478bd9Sstevel@tonic-gate 		rval = usb_set_device_pwrlvl0(dip);
3633*7c478bd9Sstevel@tonic-gate 		mutex_enter(&ugenp->ug_mutex);
3634*7c478bd9Sstevel@tonic-gate 
3635*7c478bd9Sstevel@tonic-gate 		break;
3636*7c478bd9Sstevel@tonic-gate 	default:
3637*7c478bd9Sstevel@tonic-gate 		/* Levels 1 and 2 are not supported to keep it simple. */
3638*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(UGEN_PRINT_PM, ugenp->ug_log_hdl,
3639*7c478bd9Sstevel@tonic-gate 		    "ugen_power: power level %d not supported", level);
3640*7c478bd9Sstevel@tonic-gate 
3641*7c478bd9Sstevel@tonic-gate 		break;
3642*7c478bd9Sstevel@tonic-gate 	}
3643*7c478bd9Sstevel@tonic-gate done:
3644*7c478bd9Sstevel@tonic-gate 	mutex_exit(&ugenp->ug_mutex);
3645*7c478bd9Sstevel@tonic-gate 	usb_release_access(ugenp->ug_ser_cookie);
3646*7c478bd9Sstevel@tonic-gate 
3647*7c478bd9Sstevel@tonic-gate 	return (rval);
3648*7c478bd9Sstevel@tonic-gate }
3649*7c478bd9Sstevel@tonic-gate 
3650*7c478bd9Sstevel@tonic-gate 
3651*7c478bd9Sstevel@tonic-gate static void
3652*7c478bd9Sstevel@tonic-gate ugen_pm_busy_component(ugen_state_t *ugen_statep)
3653*7c478bd9Sstevel@tonic-gate {
3654*7c478bd9Sstevel@tonic-gate 	ASSERT(!mutex_owned(&ugen_statep->ug_mutex));
3655*7c478bd9Sstevel@tonic-gate 
3656*7c478bd9Sstevel@tonic-gate 	if (ugen_statep->ug_pm != NULL) {
3657*7c478bd9Sstevel@tonic-gate 		mutex_enter(&ugen_statep->ug_mutex);
3658*7c478bd9Sstevel@tonic-gate 		ugen_statep->ug_pm->pwr_busy++;
3659*7c478bd9Sstevel@tonic-gate 
3660*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(UGEN_PRINT_PM, ugen_statep->ug_log_hdl,
3661*7c478bd9Sstevel@tonic-gate 		    "ugen_pm_busy_component: %d", ugen_statep->ug_pm->pwr_busy);
3662*7c478bd9Sstevel@tonic-gate 
3663*7c478bd9Sstevel@tonic-gate 		mutex_exit(&ugen_statep->ug_mutex);
3664*7c478bd9Sstevel@tonic-gate 		if (pm_busy_component(ugen_statep->ug_dip, 0) != DDI_SUCCESS) {
3665*7c478bd9Sstevel@tonic-gate 			mutex_enter(&ugen_statep->ug_mutex);
3666*7c478bd9Sstevel@tonic-gate 			ugen_statep->ug_pm->pwr_busy--;
3667*7c478bd9Sstevel@tonic-gate 
3668*7c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(UGEN_PRINT_PM, ugen_statep->ug_log_hdl,
3669*7c478bd9Sstevel@tonic-gate 			    "ugen_pm_busy_component failed: %d",
3670*7c478bd9Sstevel@tonic-gate 			    ugen_statep->ug_pm->pwr_busy);
3671*7c478bd9Sstevel@tonic-gate 
3672*7c478bd9Sstevel@tonic-gate 			mutex_exit(&ugen_statep->ug_mutex);
3673*7c478bd9Sstevel@tonic-gate 		}
3674*7c478bd9Sstevel@tonic-gate 	}
3675*7c478bd9Sstevel@tonic-gate }
3676*7c478bd9Sstevel@tonic-gate 
3677*7c478bd9Sstevel@tonic-gate 
3678*7c478bd9Sstevel@tonic-gate static void
3679*7c478bd9Sstevel@tonic-gate ugen_pm_idle_component(ugen_state_t *ugen_statep)
3680*7c478bd9Sstevel@tonic-gate {
3681*7c478bd9Sstevel@tonic-gate 	ASSERT(!mutex_owned(&ugen_statep->ug_mutex));
3682*7c478bd9Sstevel@tonic-gate 
3683*7c478bd9Sstevel@tonic-gate 	if (ugen_statep->ug_pm != NULL) {
3684*7c478bd9Sstevel@tonic-gate 		if (pm_idle_component(ugen_statep->ug_dip, 0) == DDI_SUCCESS) {
3685*7c478bd9Sstevel@tonic-gate 			mutex_enter(&ugen_statep->ug_mutex);
3686*7c478bd9Sstevel@tonic-gate 			ASSERT(ugen_statep->ug_pm->pwr_busy > 0);
3687*7c478bd9Sstevel@tonic-gate 			ugen_statep->ug_pm->pwr_busy--;
3688*7c478bd9Sstevel@tonic-gate 
3689*7c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L4(UGEN_PRINT_PM, ugen_statep->ug_log_hdl,
3690*7c478bd9Sstevel@tonic-gate 			    "ugen_pm_idle_component: %d",
3691*7c478bd9Sstevel@tonic-gate 			    ugen_statep->ug_pm->pwr_busy);
3692*7c478bd9Sstevel@tonic-gate 
3693*7c478bd9Sstevel@tonic-gate 			mutex_exit(&ugen_statep->ug_mutex);
3694*7c478bd9Sstevel@tonic-gate 		}
3695*7c478bd9Sstevel@tonic-gate 	}
3696*7c478bd9Sstevel@tonic-gate }
3697*7c478bd9Sstevel@tonic-gate 
3698*7c478bd9Sstevel@tonic-gate 
3699*7c478bd9Sstevel@tonic-gate /*
3700*7c478bd9Sstevel@tonic-gate  * devt lookup support
3701*7c478bd9Sstevel@tonic-gate  *	In ugen_strategy and ugen_minphys, we only have the devt and need
3702*7c478bd9Sstevel@tonic-gate  *	the ugen_state pointer. Since we don't know instance mask, we can't
3703*7c478bd9Sstevel@tonic-gate  *	easily derive a softstate pointer. Therefore, we use a list
3704*7c478bd9Sstevel@tonic-gate  */
3705*7c478bd9Sstevel@tonic-gate static void
3706*7c478bd9Sstevel@tonic-gate ugen_store_devt(ugen_state_t *ugenp, minor_t minor)
3707*7c478bd9Sstevel@tonic-gate {
3708*7c478bd9Sstevel@tonic-gate 	ugen_devt_list_entry_t *e = kmem_zalloc(
3709*7c478bd9Sstevel@tonic-gate 				sizeof (ugen_devt_list_entry_t), KM_SLEEP);
3710*7c478bd9Sstevel@tonic-gate 	ugen_devt_list_entry_t *t;
3711*7c478bd9Sstevel@tonic-gate 
3712*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ugen_devt_list_mutex);
3713*7c478bd9Sstevel@tonic-gate 	e->list_dev = makedevice(ddi_driver_major(ugenp->ug_dip), minor);
3714*7c478bd9Sstevel@tonic-gate 	e->list_state = ugenp;
3715*7c478bd9Sstevel@tonic-gate 
3716*7c478bd9Sstevel@tonic-gate 	t = ugen_devt_list.list_next;
3717*7c478bd9Sstevel@tonic-gate 
3718*7c478bd9Sstevel@tonic-gate 	/* check if the entry is already in the list */
3719*7c478bd9Sstevel@tonic-gate 	while (t) {
3720*7c478bd9Sstevel@tonic-gate 		ASSERT(t->list_dev != e->list_dev);
3721*7c478bd9Sstevel@tonic-gate 		t = t->list_next;
3722*7c478bd9Sstevel@tonic-gate 	}
3723*7c478bd9Sstevel@tonic-gate 
3724*7c478bd9Sstevel@tonic-gate 	/* add to the head of the list */
3725*7c478bd9Sstevel@tonic-gate 	e->list_next = ugen_devt_list.list_next;
3726*7c478bd9Sstevel@tonic-gate 	if (ugen_devt_list.list_next) {
3727*7c478bd9Sstevel@tonic-gate 		ugen_devt_list.list_next->list_prev = e;
3728*7c478bd9Sstevel@tonic-gate 	}
3729*7c478bd9Sstevel@tonic-gate 	ugen_devt_list.list_next = e;
3730*7c478bd9Sstevel@tonic-gate 	mutex_exit(&ugen_devt_list_mutex);
3731*7c478bd9Sstevel@tonic-gate }
3732*7c478bd9Sstevel@tonic-gate 
3733*7c478bd9Sstevel@tonic-gate 
3734*7c478bd9Sstevel@tonic-gate static ugen_state_t *
3735*7c478bd9Sstevel@tonic-gate ugen_devt2state(dev_t dev)
3736*7c478bd9Sstevel@tonic-gate {
3737*7c478bd9Sstevel@tonic-gate 	ugen_devt_list_entry_t *t;
3738*7c478bd9Sstevel@tonic-gate 	ugen_state_t	*ugenp = NULL;
3739*7c478bd9Sstevel@tonic-gate 	int		index, count;
3740*7c478bd9Sstevel@tonic-gate 
3741*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ugen_devt_list_mutex);
3742*7c478bd9Sstevel@tonic-gate 
3743*7c478bd9Sstevel@tonic-gate 	for (index = ugen_devt_cache_index, count = 0;
3744*7c478bd9Sstevel@tonic-gate 	    count < UGEN_DEVT_CACHE_SIZE; count++) {
3745*7c478bd9Sstevel@tonic-gate 		if (ugen_devt_cache[index].cache_dev == dev) {
3746*7c478bd9Sstevel@tonic-gate 			ugen_devt_cache[index].cache_hit++;
3747*7c478bd9Sstevel@tonic-gate 			ugenp = ugen_devt_cache[index].cache_state;
3748*7c478bd9Sstevel@tonic-gate 
3749*7c478bd9Sstevel@tonic-gate 			mutex_exit(&ugen_devt_list_mutex);
3750*7c478bd9Sstevel@tonic-gate 
3751*7c478bd9Sstevel@tonic-gate 			return (ugenp);
3752*7c478bd9Sstevel@tonic-gate 		}
3753*7c478bd9Sstevel@tonic-gate 		index++;
3754*7c478bd9Sstevel@tonic-gate 		index %= UGEN_DEVT_CACHE_SIZE;
3755*7c478bd9Sstevel@tonic-gate 	}
3756*7c478bd9Sstevel@tonic-gate 
3757*7c478bd9Sstevel@tonic-gate 	t = ugen_devt_list.list_next;
3758*7c478bd9Sstevel@tonic-gate 
3759*7c478bd9Sstevel@tonic-gate 	while (t) {
3760*7c478bd9Sstevel@tonic-gate 		if (t->list_dev == dev) {
3761*7c478bd9Sstevel@tonic-gate 			ugenp = t->list_state;
3762*7c478bd9Sstevel@tonic-gate 			ugen_devt_cache_index++;
3763*7c478bd9Sstevel@tonic-gate 			ugen_devt_cache_index %= UGEN_DEVT_CACHE_SIZE;
3764*7c478bd9Sstevel@tonic-gate 			ugen_devt_cache[ugen_devt_cache_index].cache_dev = dev;
3765*7c478bd9Sstevel@tonic-gate 			ugen_devt_cache[ugen_devt_cache_index].cache_state =
3766*7c478bd9Sstevel@tonic-gate 									ugenp;
3767*7c478bd9Sstevel@tonic-gate 			mutex_exit(&ugen_devt_list_mutex);
3768*7c478bd9Sstevel@tonic-gate 
3769*7c478bd9Sstevel@tonic-gate 			return (ugenp);
3770*7c478bd9Sstevel@tonic-gate 		}
3771*7c478bd9Sstevel@tonic-gate 		t = t->list_next;
3772*7c478bd9Sstevel@tonic-gate 	}
3773*7c478bd9Sstevel@tonic-gate 	mutex_exit(&ugen_devt_list_mutex);
3774*7c478bd9Sstevel@tonic-gate 
3775*7c478bd9Sstevel@tonic-gate 	return (ugenp);
3776*7c478bd9Sstevel@tonic-gate }
3777*7c478bd9Sstevel@tonic-gate 
3778*7c478bd9Sstevel@tonic-gate 
3779*7c478bd9Sstevel@tonic-gate static void
3780*7c478bd9Sstevel@tonic-gate ugen_free_devt(ugen_state_t *ugenp)
3781*7c478bd9Sstevel@tonic-gate {
3782*7c478bd9Sstevel@tonic-gate 	ugen_devt_list_entry_t *e, *next, *prev;
3783*7c478bd9Sstevel@tonic-gate 	major_t		major = ddi_driver_major(ugenp->ug_dip);
3784*7c478bd9Sstevel@tonic-gate 	int		instance = ddi_get_instance(ugenp->ug_dip);
3785*7c478bd9Sstevel@tonic-gate 
3786*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ugen_devt_list_mutex);
3787*7c478bd9Sstevel@tonic-gate 	prev = &ugen_devt_list;
3788*7c478bd9Sstevel@tonic-gate 	for (e = prev->list_next; e != 0; e = next) {
3789*7c478bd9Sstevel@tonic-gate 		int i = (getminor(e->list_dev) &
3790*7c478bd9Sstevel@tonic-gate 			ugenp->ug_hdl->hdl_minor_node_instance_mask) >>
3791*7c478bd9Sstevel@tonic-gate 			ugenp->ug_hdl->hdl_minor_node_instance_shift;
3792*7c478bd9Sstevel@tonic-gate 		int m = getmajor(e->list_dev);
3793*7c478bd9Sstevel@tonic-gate 
3794*7c478bd9Sstevel@tonic-gate 		next = e->list_next;
3795*7c478bd9Sstevel@tonic-gate 
3796*7c478bd9Sstevel@tonic-gate 		if ((i == instance) && (m == major)) {
3797*7c478bd9Sstevel@tonic-gate 			prev->list_next = e->list_next;
3798*7c478bd9Sstevel@tonic-gate 			if (e->list_next) {
3799*7c478bd9Sstevel@tonic-gate 				e->list_next->list_prev = prev;
3800*7c478bd9Sstevel@tonic-gate 			}
3801*7c478bd9Sstevel@tonic-gate 			kmem_free(e, sizeof (ugen_devt_list_entry_t));
3802*7c478bd9Sstevel@tonic-gate 		} else {
3803*7c478bd9Sstevel@tonic-gate 			prev = e;
3804*7c478bd9Sstevel@tonic-gate 		}
3805*7c478bd9Sstevel@tonic-gate 	}
3806*7c478bd9Sstevel@tonic-gate 
3807*7c478bd9Sstevel@tonic-gate 	bzero(ugen_devt_cache, sizeof (ugen_devt_cache));
3808*7c478bd9Sstevel@tonic-gate 	ugen_devt_cache_index = 0;
3809*7c478bd9Sstevel@tonic-gate 	mutex_exit(&ugen_devt_list_mutex);
3810*7c478bd9Sstevel@tonic-gate }
3811