xref: /illumos-gate/usr/src/uts/common/io/usb/clients/video/usbvc/usbvc.c (revision c77a61a72b5ecdc507d6cf104142edd371a16c84)
1*c77a61a7Syz /*
2*c77a61a7Syz  * CDDL HEADER START
3*c77a61a7Syz  *
4*c77a61a7Syz  * The contents of this file are subject to the terms of the
5*c77a61a7Syz  * Common Development and Distribution License (the "License").
6*c77a61a7Syz  * You may not use this file except in compliance with the License.
7*c77a61a7Syz  *
8*c77a61a7Syz  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*c77a61a7Syz  * or http://www.opensolaris.org/os/licensing.
10*c77a61a7Syz  * See the License for the specific language governing permissions
11*c77a61a7Syz  * and limitations under the License.
12*c77a61a7Syz  *
13*c77a61a7Syz  * When distributing Covered Code, include this CDDL HEADER in each
14*c77a61a7Syz  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*c77a61a7Syz  * If applicable, add the following below this CDDL HEADER, with the
16*c77a61a7Syz  * fields enclosed by brackets "[]" replaced with your own identifying
17*c77a61a7Syz  * information: Portions Copyright [yyyy] [name of copyright owner]
18*c77a61a7Syz  *
19*c77a61a7Syz  * CDDL HEADER END
20*c77a61a7Syz  */
21*c77a61a7Syz /*
22*c77a61a7Syz  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23*c77a61a7Syz  * Use is subject to license terms.
24*c77a61a7Syz  */
25*c77a61a7Syz 
26*c77a61a7Syz #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*c77a61a7Syz 
28*c77a61a7Syz /*
29*c77a61a7Syz  * USB video class driver (usbvc(7D))
30*c77a61a7Syz  *
31*c77a61a7Syz  * 1. Overview
32*c77a61a7Syz  * ------------
33*c77a61a7Syz  *
34*c77a61a7Syz  * This driver supports USB video class devices that used to capture video,
35*c77a61a7Syz  * e.g., some webcams. It is developed according to "USB Device Class
36*c77a61a7Syz  * Definition for Video Devices" spec. This spec defines detail info needed by
37*c77a61a7Syz  * designing a USB video device. It is available at:
38*c77a61a7Syz  * http://www.usb.org/developers/devclass_docs
39*c77a61a7Syz  *
40*c77a61a7Syz  * This driver implements:
41*c77a61a7Syz  *
42*c77a61a7Syz  *   - V4L2 interfaces for applications to communicate with video devices.
43*c77a61a7Syz  *     V4L2 is an API that is widely used by video applications, like Ekiga,
44*c77a61a7Syz  *     luvcview, etc. The API spec is at:
45*c77a61a7Syz  *     http://www.thedirks.org/v4l2/
46*c77a61a7Syz  *     This driver is according to V4L2 spec version 0.20
47*c77a61a7Syz  *
48*c77a61a7Syz  *   - Video capture function. (Video output is not supported by now.)
49*c77a61a7Syz  *
50*c77a61a7Syz  *   - Isochronous transfer for video data. (Bulk transfer is not supported.)
51*c77a61a7Syz  *
52*c77a61a7Syz  *   - read & mmap I/O methods for userland video applications to get video
53*c77a61a7Syz  *     data. Userland video applications can use read() system call directly,
54*c77a61a7Syz  *     it is the simplest way but not the most efficient way. Applications can
55*c77a61a7Syz  *     also use mmap() system call to map several bufs (they are linked as a
56*c77a61a7Syz  *     buf list), and then use some specific ioctls to start/stop isoc polling,
57*c77a61a7Syz  *     to queue/dequeue bufs.
58*c77a61a7Syz  *
59*c77a61a7Syz  * 2. Source and header files
60*c77a61a7Syz  * ---------------------------
61*c77a61a7Syz  *
62*c77a61a7Syz  * There are two source files and three header files for this driver:
63*c77a61a7Syz  *
64*c77a61a7Syz  *   - usbvc.c		Main source file, implements usb video class spec.
65*c77a61a7Syz  *
66*c77a61a7Syz  *   - usbvc_v4l2.c	V4L2 interface specific code.
67*c77a61a7Syz  *
68*c77a61a7Syz  *   - usbvc_var.h	Main header file, includes soft state structure.
69*c77a61a7Syz  *
70*c77a61a7Syz  *   - usbvc.h		The descriptors in usb video class spec.
71*c77a61a7Syz  *
72*c77a61a7Syz  *   - videodev2.h	This header file is included in V4L2 spec. It defines
73*c77a61a7Syz  *     ioctls and data structures that used as an interface between video
74*c77a61a7Syz  *     applications and video drivers. This is the only header file that
75*c77a61a7Syz  *     usbvc driver should export to userland application.
76*c77a61a7Syz  *
77*c77a61a7Syz  * 3. USB video class devices overview
78*c77a61a7Syz  * -----------------------------------
79*c77a61a7Syz  * According to UVC spec, there must be one control interface in a UVC device.
80*c77a61a7Syz  * Control interface is used to receive control commands from user, all the
81*c77a61a7Syz  * commands are sent through default ctrl pipe. usbvc driver implements V4L2
82*c77a61a7Syz  * API, so ioctls are implemented to relay user commands to UVC device.
83*c77a61a7Syz  *
84*c77a61a7Syz  * There can be no or multiple stream interfaces in a UVC device. Stream
85*c77a61a7Syz  * interfaces are used to do video data I/O. In practice, if no stream
86*c77a61a7Syz  * interface, the video device can do nothing since it has no data I/O.
87*c77a61a7Syz  *
88*c77a61a7Syz  * usbvc driver parses descriptors of control interface and stream interfaces.
89*c77a61a7Syz  * The descriptors tell the function layout and the capability of the device.
90*c77a61a7Syz  * During attach, usbvc driver set up some key data structures according to
91*c77a61a7Syz  * the descriptors.
92*c77a61a7Syz  *
93*c77a61a7Syz  * 4. I/O methods
94*c77a61a7Syz  * ---------------
95*c77a61a7Syz  *
96*c77a61a7Syz  * Userland applications use ioctls to set/get video formats of the device,
97*c77a61a7Syz  * and control brightness, contrast, image size, etc.
98*c77a61a7Syz  *
99*c77a61a7Syz  * Besides implementing standard read I/O method to get video data from
100*c77a61a7Syz  * the device, usbvc driver also implements some specific ioctls to implement
101*c77a61a7Syz  * mmap I/O method.
102*c77a61a7Syz  *
103*c77a61a7Syz  * A view from userland application: ioctl and mmap flow chart:
104*c77a61a7Syz  *
105*c77a61a7Syz  * REQBUFS -> QUERYBUF -> mmap() ->
106*c77a61a7Syz  *
107*c77a61a7Syz  *    -> QBUF -> STREAMON -> DQBUF -> process image -> QBUF
108*c77a61a7Syz  *			       ^			|
109*c77a61a7Syz  *			       |			|
110*c77a61a7Syz  *			       |			v
111*c77a61a7Syz  *			       |---<--------------------
112*c77a61a7Syz  *
113*c77a61a7Syz  * The above queue and dequeue buf operations can be stopped by issuing a
114*c77a61a7Syz  * STREAMOFF ioctl.
115*c77a61a7Syz  *
116*c77a61a7Syz  * 5. Device states
117*c77a61a7Syz  * ----------------
118*c77a61a7Syz  *
119*c77a61a7Syz  * The device has four states (refer to usbai.h):
120*c77a61a7Syz  *
121*c77a61a7Syz  *	- USB_DEV_ONLINE: In action or ready for action.
122*c77a61a7Syz  *
123*c77a61a7Syz  *	- USB_DEV_DISCONNECTED: Hotplug removed, or device not present/correct
124*c77a61a7Syz  *				on resume (CPR).
125*c77a61a7Syz  *
126*c77a61a7Syz  *	- USB_DEV_SUSPENDED: Device has been suspended along with the system.
127*c77a61a7Syz  *
128*c77a61a7Syz  *	- USB_DEV_PWRED_DOWN: Device has been powered down.  (Note that this
129*c77a61a7Syz  *		driver supports only two power states, powered down and
130*c77a61a7Syz  *		full power.)
131*c77a61a7Syz  *
132*c77a61a7Syz  * 6. Serialize
133*c77a61a7Syz  * -------------
134*c77a61a7Syz  * In order to avoid race conditions between driver entry points, access to
135*c77a61a7Syz  * the device is serialized. All the ioctls, and read, open/close are
136*c77a61a7Syz  * serialized. The functions usbvc_serialize/release_access are implemented
137*c77a61a7Syz  * for this purpose.
138*c77a61a7Syz  *
139*c77a61a7Syz  * 7. PM & CPR
140*c77a61a7Syz  * ------------
141*c77a61a7Syz  * PM & CPR are supported. pm_busy_component and pm_idle_component mark
142*c77a61a7Syz  * the device as busy or idle to the system.
143*c77a61a7Syz  */
144*c77a61a7Syz 
145*c77a61a7Syz #if defined(lint) && !defined(DEBUG)
146*c77a61a7Syz #define	DEBUG
147*c77a61a7Syz #endif
148*c77a61a7Syz 
149*c77a61a7Syz #define	USBDRV_MAJOR_VER	2
150*c77a61a7Syz #define	USBDRV_MINOR_VER	0
151*c77a61a7Syz 
152*c77a61a7Syz #include <sys/usb/usba.h>
153*c77a61a7Syz #include <sys/fcntl.h>
154*c77a61a7Syz #include <sys/cmn_err.h>
155*c77a61a7Syz #include <sys/usb/clients/video/usbvc/usbvc_var.h>
156*c77a61a7Syz #include <sys/videodev2.h> /* V4L2 API header file */
157*c77a61a7Syz 
158*c77a61a7Syz /* Descriptors according to USB video class spec */
159*c77a61a7Syz #include <sys/usb/clients/video/usbvc/usbvc.h>
160*c77a61a7Syz 
161*c77a61a7Syz static uint_t	usbvc_errmask		= (uint_t)PRINT_MASK_ALL;
162*c77a61a7Syz static uint_t	usbvc_errlevel = 4;
163*c77a61a7Syz static uint_t	usbvc_instance_debug = (uint_t)-1;
164*c77a61a7Syz 
165*c77a61a7Syz static char	*name = "usbvc";	/* Driver name, used all over. */
166*c77a61a7Syz 
167*c77a61a7Syz /*
168*c77a61a7Syz  * Function Prototypes
169*c77a61a7Syz  */
170*c77a61a7Syz 
171*c77a61a7Syz /* Entries */
172*c77a61a7Syz static int	usbvc_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
173*c77a61a7Syz static int	usbvc_attach(dev_info_t *, ddi_attach_cmd_t);
174*c77a61a7Syz static int	usbvc_detach(dev_info_t *, ddi_detach_cmd_t);
175*c77a61a7Syz static void	usbvc_cleanup(dev_info_t *, usbvc_state_t *);
176*c77a61a7Syz static int	usbvc_open(dev_t *, int, int, cred_t *);
177*c77a61a7Syz static int	usbvc_close(dev_t, int, int, cred_t *);
178*c77a61a7Syz static int	usbvc_read(dev_t, struct uio *uip_p, cred_t *);
179*c77a61a7Syz static int	usbvc_strategy(struct buf *);
180*c77a61a7Syz static void	usbvc_minphys(struct buf *);
181*c77a61a7Syz static int	usbvc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
182*c77a61a7Syz static int	usbvc_devmap(dev_t, devmap_cookie_t, offset_t,
183*c77a61a7Syz 		    size_t, size_t *, uint_t);
184*c77a61a7Syz 
185*c77a61a7Syz /* pm and cpr */
186*c77a61a7Syz static int	usbvc_power(dev_info_t *, int, int);
187*c77a61a7Syz static void	usbvc_init_power_mgmt(usbvc_state_t *);
188*c77a61a7Syz static void	usbvc_destroy_power_mgmt(usbvc_state_t *);
189*c77a61a7Syz static void	usbvc_pm_busy_component(usbvc_state_t *);
190*c77a61a7Syz static void	usbvc_pm_idle_component(usbvc_state_t *);
191*c77a61a7Syz static int	usbvc_pwrlvl0(usbvc_state_t *);
192*c77a61a7Syz static int	usbvc_pwrlvl1(usbvc_state_t *);
193*c77a61a7Syz static int	usbvc_pwrlvl2(usbvc_state_t *);
194*c77a61a7Syz static int	usbvc_pwrlvl3(usbvc_state_t *);
195*c77a61a7Syz static void	usbvc_cpr_suspend(dev_info_t *);
196*c77a61a7Syz static void	usbvc_cpr_resume(dev_info_t *);
197*c77a61a7Syz static void	usbvc_restore_device_state(dev_info_t *, usbvc_state_t *);
198*c77a61a7Syz 
199*c77a61a7Syz /* Events */
200*c77a61a7Syz static int	usbvc_disconnect_event_cb(dev_info_t *);
201*c77a61a7Syz static int	usbvc_reconnect_event_cb(dev_info_t *);
202*c77a61a7Syz 
203*c77a61a7Syz /* Sync objs and lists */
204*c77a61a7Syz static void	usbvc_init_sync_objs(usbvc_state_t *);
205*c77a61a7Syz static void	usbvc_fini_sync_objs(usbvc_state_t *);
206*c77a61a7Syz static void	usbvc_init_lists(usbvc_state_t *);
207*c77a61a7Syz static void	usbvc_fini_lists(usbvc_state_t *);
208*c77a61a7Syz static void	usbvc_free_ctrl_descr(usbvc_state_t *);
209*c77a61a7Syz static void	usbvc_free_stream_descr(usbvc_state_t *);
210*c77a61a7Syz 
211*c77a61a7Syz /* Parse descriptors */
212*c77a61a7Syz static int	usbvc_chk_descr_len(uint8_t, uint8_t, uint8_t,
213*c77a61a7Syz 		    usb_cvs_data_t *);
214*c77a61a7Syz static usbvc_stream_if_t *usbvc_parse_stream_if(usbvc_state_t *, int);
215*c77a61a7Syz static int	usbvc_parse_ctrl_if(usbvc_state_t *);
216*c77a61a7Syz static int	usbvc_parse_stream_ifs(usbvc_state_t *);
217*c77a61a7Syz static void	usbvc_parse_color_still(usbvc_state_t *,
218*c77a61a7Syz 		    usbvc_format_group_t *, usb_cvs_data_t *, uint_t, uint_t);
219*c77a61a7Syz static void	usbvc_parse_frames(usbvc_state_t *, usbvc_format_group_t *,
220*c77a61a7Syz 		    usb_cvs_data_t *, uint_t, uint_t);
221*c77a61a7Syz static int	usbvc_parse_format_group(usbvc_state_t *,
222*c77a61a7Syz 		    usbvc_format_group_t *, usb_cvs_data_t *, uint_t, uint_t);
223*c77a61a7Syz static int	usbvc_parse_format_groups(usbvc_state_t *, usbvc_stream_if_t *);
224*c77a61a7Syz static int	usbvc_parse_stream_header(usbvc_state_t *, usbvc_stream_if_t *);
225*c77a61a7Syz 
226*c77a61a7Syz /* read I/O functions */
227*c77a61a7Syz static int	usbvc_alloc_read_bufs(usbvc_state_t *, usbvc_stream_if_t *);
228*c77a61a7Syz static int	usbvc_read_buf(usbvc_state_t *, struct buf *);
229*c77a61a7Syz static void	usbvc_free_read_buf(usbvc_buf_t *);
230*c77a61a7Syz static void	usbvc_free_read_bufs(usbvc_state_t *, usbvc_stream_if_t *);
231*c77a61a7Syz static void	usbvc_close_isoc_pipe(usbvc_state_t *, usbvc_stream_if_t *);
232*c77a61a7Syz 
233*c77a61a7Syz /* callbacks */
234*c77a61a7Syz static void	usbvc_isoc_cb(usb_pipe_handle_t, usb_isoc_req_t *);
235*c77a61a7Syz static void	usbvc_isoc_exc_cb(usb_pipe_handle_t, usb_isoc_req_t *);
236*c77a61a7Syz 
237*c77a61a7Syz /* Others */
238*c77a61a7Syz static int	usbvc_set_alt(usbvc_state_t *, usbvc_stream_if_t *);
239*c77a61a7Syz static int	usbvc_decode_stream_header(usbvc_state_t *, usbvc_buf_grp_t *,
240*c77a61a7Syz 		    mblk_t *, int);
241*c77a61a7Syz static int	usbvc_serialize_access(usbvc_state_t *, boolean_t);
242*c77a61a7Syz static void	usbvc_release_access(usbvc_state_t *);
243*c77a61a7Syz static int		usbvc_set_default_stream_fmt(usbvc_state_t *);
244*c77a61a7Syz 
245*c77a61a7Syz static usb_event_t usbvc_events = {
246*c77a61a7Syz 	usbvc_disconnect_event_cb,
247*c77a61a7Syz 	usbvc_reconnect_event_cb,
248*c77a61a7Syz 	NULL, NULL
249*c77a61a7Syz };
250*c77a61a7Syz 
251*c77a61a7Syz /* module loading stuff */
252*c77a61a7Syz struct cb_ops usbvc_cb_ops = {
253*c77a61a7Syz 	usbvc_open,		/* open  */
254*c77a61a7Syz 	usbvc_close,		/* close */
255*c77a61a7Syz 	usbvc_strategy,	/* strategy */
256*c77a61a7Syz 	nulldev,		/* print */
257*c77a61a7Syz 	nulldev,		/* dump */
258*c77a61a7Syz 	usbvc_read,		/* read */
259*c77a61a7Syz 	nodev,			/* write */
260*c77a61a7Syz 	usbvc_ioctl,		/* ioctl */
261*c77a61a7Syz 	usbvc_devmap,		/* devmap */
262*c77a61a7Syz 	nodev,			/* mmap */
263*c77a61a7Syz 	ddi_devmap_segmap,	/* segmap */
264*c77a61a7Syz 	nochpoll,		/* poll */
265*c77a61a7Syz 	ddi_prop_op,		/* cb_prop_op */
266*c77a61a7Syz 	NULL,			/* streamtab  */
267*c77a61a7Syz 	D_MP | D_DEVMAP
268*c77a61a7Syz };
269*c77a61a7Syz 
270*c77a61a7Syz static struct dev_ops usbvc_ops = {
271*c77a61a7Syz 	DEVO_REV,		/* devo_rev, */
272*c77a61a7Syz 	0,			/* refcnt  */
273*c77a61a7Syz 	usbvc_info,		/* info */
274*c77a61a7Syz 	nulldev,		/* identify */
275*c77a61a7Syz 	nulldev,		/* probe */
276*c77a61a7Syz 	usbvc_attach,		/* attach */
277*c77a61a7Syz 	usbvc_detach,		/* detach */
278*c77a61a7Syz 	nodev,			/* reset */
279*c77a61a7Syz 	&usbvc_cb_ops,	/* driver operations */
280*c77a61a7Syz 	NULL,			/* bus operations */
281*c77a61a7Syz 	usbvc_power		/* power */
282*c77a61a7Syz };
283*c77a61a7Syz 
284*c77a61a7Syz static struct modldrv usbvc_modldrv =	{
285*c77a61a7Syz 	&mod_driverops,
286*c77a61a7Syz 	"USB video class driver %I%",
287*c77a61a7Syz 	&usbvc_ops
288*c77a61a7Syz };
289*c77a61a7Syz 
290*c77a61a7Syz static struct modlinkage modlinkage = {
291*c77a61a7Syz 	MODREV_1,
292*c77a61a7Syz 	&usbvc_modldrv,
293*c77a61a7Syz 	NULL
294*c77a61a7Syz };
295*c77a61a7Syz 
296*c77a61a7Syz /* Soft state structures */
297*c77a61a7Syz #define	USBVC_INITIAL_SOFT_SPACE	1
298*c77a61a7Syz static void *usbvc_statep;
299*c77a61a7Syz 
300*c77a61a7Syz 
301*c77a61a7Syz /*
302*c77a61a7Syz  * Module-wide initialization routine.
303*c77a61a7Syz  */
304*c77a61a7Syz int
305*c77a61a7Syz _init(void)
306*c77a61a7Syz {
307*c77a61a7Syz 	int rval;
308*c77a61a7Syz 
309*c77a61a7Syz 	if ((rval = ddi_soft_state_init(&usbvc_statep,
310*c77a61a7Syz 	    sizeof (usbvc_state_t), USBVC_INITIAL_SOFT_SPACE)) != 0) {
311*c77a61a7Syz 
312*c77a61a7Syz 		return (rval);
313*c77a61a7Syz 	}
314*c77a61a7Syz 
315*c77a61a7Syz 	if ((rval = mod_install(&modlinkage)) != 0) {
316*c77a61a7Syz 		ddi_soft_state_fini(&usbvc_statep);
317*c77a61a7Syz 	}
318*c77a61a7Syz 
319*c77a61a7Syz 	return (rval);
320*c77a61a7Syz }
321*c77a61a7Syz 
322*c77a61a7Syz 
323*c77a61a7Syz /*
324*c77a61a7Syz  * Module-wide tear-down routine.
325*c77a61a7Syz  */
326*c77a61a7Syz int
327*c77a61a7Syz _fini(void)
328*c77a61a7Syz {
329*c77a61a7Syz 	int rval;
330*c77a61a7Syz 
331*c77a61a7Syz 	if ((rval = mod_remove(&modlinkage)) != 0) {
332*c77a61a7Syz 
333*c77a61a7Syz 		return (rval);
334*c77a61a7Syz 	}
335*c77a61a7Syz 
336*c77a61a7Syz 	ddi_soft_state_fini(&usbvc_statep);
337*c77a61a7Syz 
338*c77a61a7Syz 	return (rval);
339*c77a61a7Syz }
340*c77a61a7Syz 
341*c77a61a7Syz 
342*c77a61a7Syz int
343*c77a61a7Syz _info(struct modinfo *modinfop)
344*c77a61a7Syz {
345*c77a61a7Syz 	return (mod_info(&modlinkage, modinfop));
346*c77a61a7Syz }
347*c77a61a7Syz 
348*c77a61a7Syz 
349*c77a61a7Syz /*
350*c77a61a7Syz  * usbvc_info:
351*c77a61a7Syz  *	Get minor number, soft state structure, etc.
352*c77a61a7Syz  */
353*c77a61a7Syz /*ARGSUSED*/
354*c77a61a7Syz static int
355*c77a61a7Syz usbvc_info(dev_info_t *dip, ddi_info_cmd_t infocmd,
356*c77a61a7Syz 			void *arg, void **result)
357*c77a61a7Syz {
358*c77a61a7Syz 	usbvc_state_t	*usbvcp;
359*c77a61a7Syz 	int error = DDI_FAILURE;
360*c77a61a7Syz 
361*c77a61a7Syz 	switch (infocmd) {
362*c77a61a7Syz 	case DDI_INFO_DEVT2DEVINFO:
363*c77a61a7Syz 		if ((usbvcp = ddi_get_soft_state(usbvc_statep,
364*c77a61a7Syz 		    getminor((dev_t)arg))) != NULL) {
365*c77a61a7Syz 			*result = usbvcp->usbvc_dip;
366*c77a61a7Syz 			if (*result != NULL) {
367*c77a61a7Syz 				error = DDI_SUCCESS;
368*c77a61a7Syz 			}
369*c77a61a7Syz 		} else {
370*c77a61a7Syz 			*result = NULL;
371*c77a61a7Syz 		}
372*c77a61a7Syz 		break;
373*c77a61a7Syz 	case DDI_INFO_DEVT2INSTANCE:
374*c77a61a7Syz 		*result = (void *)(uintptr_t)getminor((dev_t)arg);
375*c77a61a7Syz 		error = DDI_SUCCESS;
376*c77a61a7Syz 		break;
377*c77a61a7Syz 	default:
378*c77a61a7Syz 		break;
379*c77a61a7Syz 	}
380*c77a61a7Syz 
381*c77a61a7Syz 	return (error);
382*c77a61a7Syz }
383*c77a61a7Syz 
384*c77a61a7Syz 
385*c77a61a7Syz /*
386*c77a61a7Syz  * Entry functions.
387*c77a61a7Syz  */
388*c77a61a7Syz 
389*c77a61a7Syz /*
390*c77a61a7Syz  * usbvc_attach:
391*c77a61a7Syz  *	Attach or resume.
392*c77a61a7Syz  *
393*c77a61a7Syz  *	For attach, initialize state and device, including:
394*c77a61a7Syz  *		state variables, locks, device node
395*c77a61a7Syz  *		device registration with system
396*c77a61a7Syz  *		power management, hotplugging
397*c77a61a7Syz  *	For resume, restore device and state
398*c77a61a7Syz  */
399*c77a61a7Syz static int
400*c77a61a7Syz usbvc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
401*c77a61a7Syz {
402*c77a61a7Syz 	int			instance = ddi_get_instance(dip);
403*c77a61a7Syz 	usbvc_state_t		*usbvcp = NULL;
404*c77a61a7Syz 	int			status;
405*c77a61a7Syz 
406*c77a61a7Syz 	switch (cmd) {
407*c77a61a7Syz 	case DDI_ATTACH:
408*c77a61a7Syz 
409*c77a61a7Syz 		break;
410*c77a61a7Syz 	case DDI_RESUME:
411*c77a61a7Syz 		usbvc_cpr_resume(dip);
412*c77a61a7Syz 
413*c77a61a7Syz 		return (DDI_SUCCESS);
414*c77a61a7Syz 	default:
415*c77a61a7Syz 
416*c77a61a7Syz 		return (DDI_FAILURE);
417*c77a61a7Syz 	}
418*c77a61a7Syz 
419*c77a61a7Syz 	if (ddi_soft_state_zalloc(usbvc_statep, instance) == DDI_SUCCESS) {
420*c77a61a7Syz 		usbvcp = ddi_get_soft_state(usbvc_statep, instance);
421*c77a61a7Syz 	}
422*c77a61a7Syz 	if (usbvcp == NULL)  {
423*c77a61a7Syz 
424*c77a61a7Syz 		return (DDI_FAILURE);
425*c77a61a7Syz 	}
426*c77a61a7Syz 
427*c77a61a7Syz 	usbvcp->usbvc_dip = dip;
428*c77a61a7Syz 
429*c77a61a7Syz 	usbvcp->usbvc_log_handle = usb_alloc_log_hdl(dip,
430*c77a61a7Syz 				"usbvc", &usbvc_errlevel,
431*c77a61a7Syz 				&usbvc_errmask, &usbvc_instance_debug, 0);
432*c77a61a7Syz 
433*c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
434*c77a61a7Syz 		    "usbvc_attach: enter");
435*c77a61a7Syz 
436*c77a61a7Syz 	if ((status = usb_client_attach(dip, USBDRV_VERSION, 0)) !=
437*c77a61a7Syz 	    USB_SUCCESS) {
438*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
439*c77a61a7Syz 		    "usbvc_attach: usb_client_attach failed, error code:%d",
440*c77a61a7Syz 		    status);
441*c77a61a7Syz 
442*c77a61a7Syz 		goto fail;
443*c77a61a7Syz 	}
444*c77a61a7Syz 
445*c77a61a7Syz 	if ((status = usb_get_dev_data(dip, &usbvcp->usbvc_reg,
446*c77a61a7Syz 	    USB_PARSE_LVL_ALL, 0)) != USB_SUCCESS) {
447*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
448*c77a61a7Syz 		    "usbvc_attach: usb_get_dev_data failed, error code:%d",
449*c77a61a7Syz 		    status);
450*c77a61a7Syz 
451*c77a61a7Syz 		goto fail;
452*c77a61a7Syz 	}
453*c77a61a7Syz 	usbvc_init_sync_objs(usbvcp);
454*c77a61a7Syz 
455*c77a61a7Syz 	/* create minor node */
456*c77a61a7Syz 	if ((status = ddi_create_minor_node(dip, name, S_IFCHR, instance,
457*c77a61a7Syz 	    "usb_video", 0)) != DDI_SUCCESS) {
458*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
459*c77a61a7Syz 		    "usbvc_attach: Error creating minor node, error code:%d",
460*c77a61a7Syz 		    status);
461*c77a61a7Syz 
462*c77a61a7Syz 		goto fail;
463*c77a61a7Syz 	}
464*c77a61a7Syz 
465*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
466*c77a61a7Syz 	usbvc_init_lists(usbvcp);
467*c77a61a7Syz 
468*c77a61a7Syz 	usbvcp->usbvc_default_ph = usbvcp->usbvc_reg->dev_default_ph;
469*c77a61a7Syz 
470*c77a61a7Syz 	/* Put online before PM init as can get power managed afterward. */
471*c77a61a7Syz 	usbvcp->usbvc_dev_state = USB_DEV_ONLINE;
472*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
473*c77a61a7Syz 
474*c77a61a7Syz 	/* initialize power management */
475*c77a61a7Syz 	usbvc_init_power_mgmt(usbvcp);
476*c77a61a7Syz 
477*c77a61a7Syz 	if ((status = usbvc_parse_ctrl_if(usbvcp)) != USB_SUCCESS) {
478*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
479*c77a61a7Syz 		    "usbvc_attach: parse ctrl interface fail, error code:%d",
480*c77a61a7Syz 		    status);
481*c77a61a7Syz 
482*c77a61a7Syz 		goto fail;
483*c77a61a7Syz 	}
484*c77a61a7Syz 	if ((status = usbvc_parse_stream_ifs(usbvcp)) != USB_SUCCESS) {
485*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
486*c77a61a7Syz 		    "usbvc_attach: parse stream interfaces fail, error code:%d",
487*c77a61a7Syz 		    status);
488*c77a61a7Syz 
489*c77a61a7Syz 		goto fail;
490*c77a61a7Syz 	}
491*c77a61a7Syz 	(void) usbvc_set_default_stream_fmt(usbvcp);
492*c77a61a7Syz 
493*c77a61a7Syz 	/* Register for events */
494*c77a61a7Syz 	if ((status = usb_register_event_cbs(dip, &usbvc_events, 0)) !=
495*c77a61a7Syz 	    USB_SUCCESS) {
496*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
497*c77a61a7Syz 		    "usbvc_attach: register_event_cbs failed, error code:%d",
498*c77a61a7Syz 		    status);
499*c77a61a7Syz 
500*c77a61a7Syz 		goto fail;
501*c77a61a7Syz 	}
502*c77a61a7Syz 
503*c77a61a7Syz 	/* Report device */
504*c77a61a7Syz 	ddi_report_dev(dip);
505*c77a61a7Syz 
506*c77a61a7Syz 	return (DDI_SUCCESS);
507*c77a61a7Syz 
508*c77a61a7Syz fail:
509*c77a61a7Syz 	if (usbvcp) {
510*c77a61a7Syz 		usbvc_cleanup(dip, usbvcp);
511*c77a61a7Syz 	}
512*c77a61a7Syz 
513*c77a61a7Syz 	return (DDI_FAILURE);
514*c77a61a7Syz }
515*c77a61a7Syz 
516*c77a61a7Syz 
517*c77a61a7Syz /*
518*c77a61a7Syz  * usbvc_detach:
519*c77a61a7Syz  *	detach or suspend driver instance
520*c77a61a7Syz  *
521*c77a61a7Syz  * Note: in detach, only contention threads is from pm and disconnnect.
522*c77a61a7Syz  */
523*c77a61a7Syz static int
524*c77a61a7Syz usbvc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
525*c77a61a7Syz {
526*c77a61a7Syz 	int		instance = ddi_get_instance(dip);
527*c77a61a7Syz 	usbvc_state_t	*usbvcp = ddi_get_soft_state(usbvc_statep, instance);
528*c77a61a7Syz 	int		rval = USB_FAILURE;
529*c77a61a7Syz 
530*c77a61a7Syz 	switch (cmd) {
531*c77a61a7Syz 	case DDI_DETACH:
532*c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
533*c77a61a7Syz 		ASSERT((usbvcp->usbvc_drv_state & USBVC_OPEN) == 0);
534*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
535*c77a61a7Syz 
536*c77a61a7Syz 		USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
537*c77a61a7Syz 		    "usbvc_detach: enter for detach");
538*c77a61a7Syz 
539*c77a61a7Syz 		usbvc_cleanup(dip, usbvcp);
540*c77a61a7Syz 		rval = USB_SUCCESS;
541*c77a61a7Syz 
542*c77a61a7Syz 		break;
543*c77a61a7Syz 	case DDI_SUSPEND:
544*c77a61a7Syz 		USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
545*c77a61a7Syz 		    "usbvc_detach: enter for suspend");
546*c77a61a7Syz 
547*c77a61a7Syz 		usbvc_cpr_suspend(dip);
548*c77a61a7Syz 		rval = USB_SUCCESS;
549*c77a61a7Syz 
550*c77a61a7Syz 		break;
551*c77a61a7Syz 	default:
552*c77a61a7Syz 
553*c77a61a7Syz 		break;
554*c77a61a7Syz 	}
555*c77a61a7Syz 
556*c77a61a7Syz 	return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
557*c77a61a7Syz }
558*c77a61a7Syz 
559*c77a61a7Syz 
560*c77a61a7Syz /*
561*c77a61a7Syz  * usbvc_cleanup:
562*c77a61a7Syz  *	clean up the driver state for detach
563*c77a61a7Syz  */
564*c77a61a7Syz static void
565*c77a61a7Syz usbvc_cleanup(dev_info_t *dip, usbvc_state_t *usbvcp)
566*c77a61a7Syz {
567*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
568*c77a61a7Syz 	    "Cleanup: enter");
569*c77a61a7Syz 
570*c77a61a7Syz 	if (usbvcp->usbvc_locks_initialized) {
571*c77a61a7Syz 
572*c77a61a7Syz 		/* This must be done 1st to prevent more events from coming. */
573*c77a61a7Syz 		usb_unregister_event_cbs(dip, &usbvc_events);
574*c77a61a7Syz 
575*c77a61a7Syz 		/*
576*c77a61a7Syz 		 * At this point, no new activity can be initiated. The driver
577*c77a61a7Syz 		 * has disabled hotplug callbacks. The Solaris framework has
578*c77a61a7Syz 		 * disabled new opens on a device being detached, and does not
579*c77a61a7Syz 		 * allow detaching an open device.
580*c77a61a7Syz 		 *
581*c77a61a7Syz 		 * The following ensures that all driver activity has drained.
582*c77a61a7Syz 		 */
583*c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
584*c77a61a7Syz 		(void) usbvc_serialize_access(usbvcp, USBVC_SER_NOSIG);
585*c77a61a7Syz 		usbvc_release_access(usbvcp);
586*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
587*c77a61a7Syz 
588*c77a61a7Syz 		/* All device activity has died down. */
589*c77a61a7Syz 		usbvc_destroy_power_mgmt(usbvcp);
590*c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
591*c77a61a7Syz 		usbvc_fini_lists(usbvcp);
592*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
593*c77a61a7Syz 
594*c77a61a7Syz 		ddi_remove_minor_node(dip, NULL);
595*c77a61a7Syz 		usbvc_fini_sync_objs(usbvcp);
596*c77a61a7Syz 	}
597*c77a61a7Syz 
598*c77a61a7Syz 	usb_client_detach(dip, usbvcp->usbvc_reg);
599*c77a61a7Syz 	usb_free_log_hdl(usbvcp->usbvc_log_handle);
600*c77a61a7Syz 	ddi_soft_state_free(usbvc_statep, ddi_get_instance(dip));
601*c77a61a7Syz 	ddi_prop_remove_all(dip);
602*c77a61a7Syz }
603*c77a61a7Syz 
604*c77a61a7Syz 
605*c77a61a7Syz /*ARGSUSED*/
606*c77a61a7Syz static int
607*c77a61a7Syz usbvc_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
608*c77a61a7Syz {
609*c77a61a7Syz 	usbvc_state_t	*usbvcp =
610*c77a61a7Syz 			    ddi_get_soft_state(usbvc_statep, getminor(*devp));
611*c77a61a7Syz 
612*c77a61a7Syz 	if (usbvcp == NULL) {
613*c77a61a7Syz 
614*c77a61a7Syz 		return (ENXIO);
615*c77a61a7Syz 	}
616*c77a61a7Syz 
617*c77a61a7Syz 	/*
618*c77a61a7Syz 	 * Keep it simple: one client at a time.
619*c77a61a7Syz 	 * Exclusive open only
620*c77a61a7Syz 	 */
621*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
622*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
623*c77a61a7Syz 	    "usbvc_open: enter, dev_stat=%d", usbvcp->usbvc_dev_state);
624*c77a61a7Syz 
625*c77a61a7Syz 	if (usbvcp->usbvc_dev_state == USB_DEV_DISCONNECTED) {
626*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
627*c77a61a7Syz 
628*c77a61a7Syz 		return (ENODEV);
629*c77a61a7Syz 	}
630*c77a61a7Syz 	if (usbvcp->usbvc_dev_state == USB_DEV_SUSPENDED) {
631*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
632*c77a61a7Syz 
633*c77a61a7Syz 		return (EIO);
634*c77a61a7Syz 	}
635*c77a61a7Syz 	if ((usbvcp->usbvc_drv_state & USBVC_OPEN) != 0) {
636*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
637*c77a61a7Syz 
638*c77a61a7Syz 		return (EBUSY);
639*c77a61a7Syz 	}
640*c77a61a7Syz 	usbvcp->usbvc_drv_state |= USBVC_OPEN;
641*c77a61a7Syz 
642*c77a61a7Syz 	if (usbvc_serialize_access(usbvcp, USBVC_SER_SIG) == 0) {
643*c77a61a7Syz 		usbvcp->usbvc_drv_state &= ~USBVC_OPEN;
644*c77a61a7Syz 		usbvcp->usbvc_serial_inuse = B_FALSE;
645*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
646*c77a61a7Syz 
647*c77a61a7Syz 		return (EINTR);
648*c77a61a7Syz 	}
649*c77a61a7Syz 
650*c77a61a7Syz 	/* raise power */
651*c77a61a7Syz 	usbvc_pm_busy_component(usbvcp);
652*c77a61a7Syz 	if (usbvcp->usbvc_pm->usbvc_current_power != USB_DEV_OS_FULL_PWR) {
653*c77a61a7Syz 		usbvcp->usbvc_pm->usbvc_raise_power = B_TRUE;
654*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
655*c77a61a7Syz 		(void) pm_raise_power(usbvcp->usbvc_dip,
656*c77a61a7Syz 					0, USB_DEV_OS_FULL_PWR);
657*c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
658*c77a61a7Syz 		usbvcp->usbvc_pm->usbvc_raise_power = B_FALSE;
659*c77a61a7Syz 	}
660*c77a61a7Syz 
661*c77a61a7Syz 	/* Device is idle until it is used. */
662*c77a61a7Syz 	usbvc_release_access(usbvcp);
663*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
664*c77a61a7Syz 
665*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
666*c77a61a7Syz 	    "usbvc_open: end.");
667*c77a61a7Syz 
668*c77a61a7Syz 	return (0);
669*c77a61a7Syz }
670*c77a61a7Syz 
671*c77a61a7Syz 
672*c77a61a7Syz /*ARGSUSED*/
673*c77a61a7Syz static int
674*c77a61a7Syz usbvc_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
675*c77a61a7Syz {
676*c77a61a7Syz 	usbvc_stream_if_t *strm_if;
677*c77a61a7Syz 	int		if_num;
678*c77a61a7Syz 	usbvc_state_t	*usbvcp =
679*c77a61a7Syz 	    ddi_get_soft_state(usbvc_statep, getminor(dev));
680*c77a61a7Syz 
681*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
682*c77a61a7Syz 	    "close: enter");
683*c77a61a7Syz 
684*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
685*c77a61a7Syz 	(void) usbvc_serialize_access(usbvcp, USBVC_SER_NOSIG);
686*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
687*c77a61a7Syz 
688*c77a61a7Syz 	/* Perform device session cleanup here. */
689*c77a61a7Syz 
690*c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
691*c77a61a7Syz 	    "close: cleaning up...");
692*c77a61a7Syz 
693*c77a61a7Syz 	/*
694*c77a61a7Syz 	 * USBA automatically flushes/resets active non-default pipes
695*c77a61a7Syz 	 * when they are closed.  We can't reset default pipe, but we
696*c77a61a7Syz 	 * can wait for all requests on it from this dip to drain.
697*c77a61a7Syz 	 */
698*c77a61a7Syz 	(void) usb_pipe_drain_reqs(usbvcp->usbvc_dip,
699*c77a61a7Syz 	    usbvcp->usbvc_reg->dev_default_ph, 0,
700*c77a61a7Syz 	    USB_FLAGS_SLEEP, NULL, 0);
701*c77a61a7Syz 
702*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
703*c77a61a7Syz 	strm_if = usbvcp->usbvc_curr_strm;
704*c77a61a7Syz 	if (strm_if->start_polling == 1) {
705*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
706*c77a61a7Syz 		usb_pipe_stop_isoc_polling(strm_if->datain_ph, USB_FLAGS_SLEEP);
707*c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
708*c77a61a7Syz 		strm_if->start_polling = 0;
709*c77a61a7Syz 	}
710*c77a61a7Syz 	usbvc_close_isoc_pipe(usbvcp, strm_if);
711*c77a61a7Syz 	if_num = strm_if->if_descr->if_alt->altif_descr.bInterfaceNumber;
712*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
713*c77a61a7Syz 
714*c77a61a7Syz 	/* reset alternate to the default one. */
715*c77a61a7Syz 	(void) usb_set_alt_if(usbvcp->usbvc_dip, if_num, 0,
716*c77a61a7Syz 	    USB_FLAGS_SLEEP, NULL, NULL);
717*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
718*c77a61a7Syz 
719*c77a61a7Syz 	usbvc_free_read_bufs(usbvcp, strm_if);
720*c77a61a7Syz 	usbvc_free_map_bufs(usbvcp, strm_if);
721*c77a61a7Syz 	usbvcp->usbvc_drv_state &= ~USBVC_OPEN;
722*c77a61a7Syz 
723*c77a61a7Syz 	usbvc_release_access(usbvcp);
724*c77a61a7Syz 	usbvc_pm_idle_component(usbvcp);
725*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
726*c77a61a7Syz 
727*c77a61a7Syz 	return (0);
728*c77a61a7Syz }
729*c77a61a7Syz 
730*c77a61a7Syz 
731*c77a61a7Syz /*ARGSUSED*/
732*c77a61a7Syz /* Read isoc data from usb video devices */
733*c77a61a7Syz static int
734*c77a61a7Syz usbvc_read(dev_t dev, struct uio *uio_p, cred_t *cred_p)
735*c77a61a7Syz {
736*c77a61a7Syz 	int			rval;
737*c77a61a7Syz 	usbvc_stream_if_t	*strm_if;
738*c77a61a7Syz 	usbvc_state_t	*usbvcp =
739*c77a61a7Syz 			    ddi_get_soft_state(usbvc_statep, getminor(dev));
740*c77a61a7Syz 
741*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
742*c77a61a7Syz 	    "usbvc_read: enter");
743*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
744*c77a61a7Syz 	if (usbvcp->usbvc_dev_state != USB_DEV_ONLINE) {
745*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
746*c77a61a7Syz 		    "usbvc_read: Device is not available,"
747*c77a61a7Syz 		    " dev_stat=%d", usbvcp->usbvc_dev_state);
748*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
749*c77a61a7Syz 
750*c77a61a7Syz 		return (EFAULT);
751*c77a61a7Syz 	}
752*c77a61a7Syz 	if ((uio_p->uio_fmode & (FNDELAY|FNONBLOCK)) &&
753*c77a61a7Syz 	    (usbvcp->usbvc_serial_inuse != B_FALSE)) {
754*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
755*c77a61a7Syz 		    "usbvc_read: non-blocking read, return fail.");
756*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
757*c77a61a7Syz 
758*c77a61a7Syz 		return (EAGAIN);
759*c77a61a7Syz 	}
760*c77a61a7Syz 	if (usbvc_serialize_access(usbvcp, USBVC_SER_SIG) <= 0) {
761*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
762*c77a61a7Syz 		    "usbvc_read: serialize_access failed.");
763*c77a61a7Syz 		rval = EFAULT;
764*c77a61a7Syz 
765*c77a61a7Syz 		goto fail;
766*c77a61a7Syz 	}
767*c77a61a7Syz 
768*c77a61a7Syz 	/* Get the first stream interface */
769*c77a61a7Syz 	strm_if = usbvcp->usbvc_curr_strm;
770*c77a61a7Syz 	if (!strm_if) {
771*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
772*c77a61a7Syz 		    "usbvc_read: no stream interfaces");
773*c77a61a7Syz 		rval = EFAULT;
774*c77a61a7Syz 
775*c77a61a7Syz 		goto fail;
776*c77a61a7Syz 	}
777*c77a61a7Syz 
778*c77a61a7Syz 	/*
779*c77a61a7Syz 	 * If it is the first read, open isoc pipe and allocate bufs for
780*c77a61a7Syz 	 * read I/O method.
781*c77a61a7Syz 	 */
782*c77a61a7Syz 	if (strm_if->datain_ph == NULL) {
783*c77a61a7Syz 		if (usbvc_open_isoc_pipe(usbvcp, strm_if) != USB_SUCCESS) {
784*c77a61a7Syz 			USB_DPRINTF_L2(PRINT_MASK_READ,
785*c77a61a7Syz 			    usbvcp->usbvc_log_handle,
786*c77a61a7Syz 			    "usbvc_read: first read, open pipe fail");
787*c77a61a7Syz 			rval = EFAULT;
788*c77a61a7Syz 
789*c77a61a7Syz 			goto fail;
790*c77a61a7Syz 		}
791*c77a61a7Syz 		if (usbvc_alloc_read_bufs(usbvcp, strm_if) != USB_SUCCESS) {
792*c77a61a7Syz 			USB_DPRINTF_L2(PRINT_MASK_READ,
793*c77a61a7Syz 			    usbvcp->usbvc_log_handle,
794*c77a61a7Syz 			    "usbvc_read: allocate rw bufs fail");
795*c77a61a7Syz 			rval = EFAULT;
796*c77a61a7Syz 
797*c77a61a7Syz 			goto fail;
798*c77a61a7Syz 		}
799*c77a61a7Syz 	}
800*c77a61a7Syz 
801*c77a61a7Syz 	/* start polling if it is not started yet */
802*c77a61a7Syz 	if (strm_if->start_polling != 1) {
803*c77a61a7Syz 		if (usbvc_start_isoc_polling(usbvcp, strm_if, NULL) !=
804*c77a61a7Syz 		    USB_SUCCESS) {
805*c77a61a7Syz 			USB_DPRINTF_L2(PRINT_MASK_READ,
806*c77a61a7Syz 			    usbvcp->usbvc_log_handle,
807*c77a61a7Syz 			    "usbvc_read: usbvc_start_isoc_polling fail");
808*c77a61a7Syz 			rval = EFAULT;
809*c77a61a7Syz 
810*c77a61a7Syz 			goto fail;
811*c77a61a7Syz 		}
812*c77a61a7Syz 		strm_if->start_polling = 1;
813*c77a61a7Syz 	}
814*c77a61a7Syz 
815*c77a61a7Syz 	if (list_is_empty(&strm_if->buf_read.uv_buf_done)) {
816*c77a61a7Syz 		USB_DPRINTF_L3(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
817*c77a61a7Syz 		    "usbvc_read: full buf list is empty.");
818*c77a61a7Syz 
819*c77a61a7Syz 		if (uio_p->uio_fmode & (FNDELAY | FNONBLOCK)) {
820*c77a61a7Syz 			USB_DPRINTF_L2(PRINT_MASK_READ,
821*c77a61a7Syz 			    usbvcp->usbvc_log_handle, "usbvc_read: fail, "
822*c77a61a7Syz 			    "non-blocking read, done buf is empty.");
823*c77a61a7Syz 			rval = EAGAIN;
824*c77a61a7Syz 
825*c77a61a7Syz 			goto fail;
826*c77a61a7Syz 		}
827*c77a61a7Syz 
828*c77a61a7Syz 		/* no available buffers, block here */
829*c77a61a7Syz 		while (list_is_empty(&strm_if->buf_read.uv_buf_done)) {
830*c77a61a7Syz 			USB_DPRINTF_L3(PRINT_MASK_READ,
831*c77a61a7Syz 			    usbvcp->usbvc_log_handle,
832*c77a61a7Syz 			    "usbvc_read: wait for done buf");
833*c77a61a7Syz 			if (cv_wait_sig(&usbvcp->usbvc_read_cv,
834*c77a61a7Syz 			    &usbvcp->usbvc_mutex) <= 0) {
835*c77a61a7Syz 				/* no done buf and cv is signaled */
836*c77a61a7Syz 				rval = EINTR;
837*c77a61a7Syz 
838*c77a61a7Syz 				goto fail;
839*c77a61a7Syz 			}
840*c77a61a7Syz 			if (usbvcp->usbvc_dev_state != USB_DEV_ONLINE) {
841*c77a61a7Syz 
842*c77a61a7Syz 				/* Device is disconnected. */
843*c77a61a7Syz 				rval = EINTR;
844*c77a61a7Syz 
845*c77a61a7Syz 				goto fail;
846*c77a61a7Syz 			}
847*c77a61a7Syz 		}
848*c77a61a7Syz 
849*c77a61a7Syz 	}
850*c77a61a7Syz 
851*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
852*c77a61a7Syz 	rval = physio(usbvc_strategy, NULL, dev, B_READ,
853*c77a61a7Syz 		    usbvc_minphys, uio_p);
854*c77a61a7Syz 
855*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
856*c77a61a7Syz 	usbvc_release_access(usbvcp);
857*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
858*c77a61a7Syz 
859*c77a61a7Syz 	return (rval);
860*c77a61a7Syz 
861*c77a61a7Syz fail:
862*c77a61a7Syz 	usbvc_release_access(usbvcp);
863*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
864*c77a61a7Syz 
865*c77a61a7Syz 	return (rval);
866*c77a61a7Syz }
867*c77a61a7Syz 
868*c77a61a7Syz 
869*c77a61a7Syz /*
870*c77a61a7Syz  * strategy:
871*c77a61a7Syz  *	Called through physio to setup and start the transfer.
872*c77a61a7Syz  */
873*c77a61a7Syz static int
874*c77a61a7Syz usbvc_strategy(struct buf *bp)
875*c77a61a7Syz {
876*c77a61a7Syz 	usbvc_state_t *usbvcp = ddi_get_soft_state(usbvc_statep,
877*c77a61a7Syz 				    getminor(bp->b_edev));
878*c77a61a7Syz 
879*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
880*c77a61a7Syz 	    "usbvc_strategy: enter");
881*c77a61a7Syz 
882*c77a61a7Syz 	/*
883*c77a61a7Syz 	 * Initialize residual count here in case transfer doesn't even get
884*c77a61a7Syz 	 * started.
885*c77a61a7Syz 	 */
886*c77a61a7Syz 	bp->b_resid = bp->b_bcount;
887*c77a61a7Syz 
888*c77a61a7Syz 	/* Needed as this is a character driver. */
889*c77a61a7Syz 	if (bp->b_flags & (B_PHYS | B_PAGEIO)) {
890*c77a61a7Syz 		bp_mapin(bp);
891*c77a61a7Syz 	}
892*c77a61a7Syz 
893*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
894*c77a61a7Syz 
895*c77a61a7Syz 	/* Make sure device has not been disconnected. */
896*c77a61a7Syz 	if (usbvcp->usbvc_dev_state != USB_DEV_ONLINE) {
897*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
898*c77a61a7Syz 		    "usbvc_strategy: device can't be accessed");
899*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
900*c77a61a7Syz 
901*c77a61a7Syz 		goto fail;
902*c77a61a7Syz 	}
903*c77a61a7Syz 
904*c77a61a7Syz 	/* read data from uv_buf_done list */
905*c77a61a7Syz 	if (usbvc_read_buf(usbvcp, bp) != USB_SUCCESS) {
906*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
907*c77a61a7Syz 		    "usbvc_strategy: read full buf list fail");
908*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
909*c77a61a7Syz 
910*c77a61a7Syz 		goto fail;
911*c77a61a7Syz 	}
912*c77a61a7Syz 
913*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
914*c77a61a7Syz 
915*c77a61a7Syz 	biodone(bp);
916*c77a61a7Syz 
917*c77a61a7Syz 	return (0);
918*c77a61a7Syz 
919*c77a61a7Syz fail:
920*c77a61a7Syz 	USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
921*c77a61a7Syz 	    "usbvc_strategy: strategy fail");
922*c77a61a7Syz 	bp->b_private = NULL;
923*c77a61a7Syz 
924*c77a61a7Syz 	bioerror(bp, EIO);
925*c77a61a7Syz 	biodone(bp);
926*c77a61a7Syz 
927*c77a61a7Syz 	return (0);
928*c77a61a7Syz }
929*c77a61a7Syz 
930*c77a61a7Syz 
931*c77a61a7Syz static void
932*c77a61a7Syz usbvc_minphys(struct buf *bp)
933*c77a61a7Syz {
934*c77a61a7Syz 	dev_t			dev = bp->b_edev;
935*c77a61a7Syz 	usbvc_stream_if_t	*strm_if;
936*c77a61a7Syz 	uint32_t		maxsize;
937*c77a61a7Syz 	usbvc_state_t	*usbvcp =
938*c77a61a7Syz 			    ddi_get_soft_state(usbvc_statep, getminor(dev));
939*c77a61a7Syz 
940*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
941*c77a61a7Syz 	strm_if = usbvcp->usbvc_curr_strm;
942*c77a61a7Syz 	LE_TO_UINT32(strm_if->ctrl_pc.dwMaxVideoFrameSize, 0, maxsize);
943*c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
944*c77a61a7Syz 	    "usbvc_minphys: max read size=%d", maxsize);
945*c77a61a7Syz 
946*c77a61a7Syz 	if (bp->b_bcount > maxsize) {
947*c77a61a7Syz 		bp->b_bcount = maxsize;
948*c77a61a7Syz 	}
949*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
950*c77a61a7Syz }
951*c77a61a7Syz 
952*c77a61a7Syz 
953*c77a61a7Syz /*
954*c77a61a7Syz  * ioctl entry.
955*c77a61a7Syz  */
956*c77a61a7Syz /*ARGSUSED*/
957*c77a61a7Syz static int
958*c77a61a7Syz usbvc_ioctl(dev_t dev, int cmd, intptr_t arg,
959*c77a61a7Syz 		int mode, cred_t *cred_p, int *rval_p)
960*c77a61a7Syz {
961*c77a61a7Syz 	int		rv = 0;
962*c77a61a7Syz 	usbvc_state_t	*usbvcp =
963*c77a61a7Syz 	    ddi_get_soft_state(usbvc_statep, getminor(dev));
964*c77a61a7Syz 
965*c77a61a7Syz 	if (usbvcp == NULL) {
966*c77a61a7Syz 
967*c77a61a7Syz 		return (ENXIO);
968*c77a61a7Syz 	}
969*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
970*c77a61a7Syz 	    "ioctl enter, cmd=%x", cmd);
971*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
972*c77a61a7Syz 	if (usbvcp->usbvc_dev_state != USB_DEV_ONLINE) {
973*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
974*c77a61a7Syz 		    "ioctl: Device is not online,"
975*c77a61a7Syz 		    " dev_stat=%d", usbvcp->usbvc_dev_state);
976*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
977*c77a61a7Syz 
978*c77a61a7Syz 		return (EFAULT);
979*c77a61a7Syz 	}
980*c77a61a7Syz 	if (usbvc_serialize_access(usbvcp, USBVC_SER_SIG) <= 0) {
981*c77a61a7Syz 		usbvcp->usbvc_serial_inuse = B_FALSE;
982*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
983*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
984*c77a61a7Syz 		    "serialize_access failed.");
985*c77a61a7Syz 
986*c77a61a7Syz 		return (EFAULT);
987*c77a61a7Syz 	}
988*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
989*c77a61a7Syz 
990*c77a61a7Syz 	rv = usbvc_v4l2_ioctl(usbvcp, cmd, arg, mode);
991*c77a61a7Syz 
992*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
993*c77a61a7Syz 	usbvc_release_access(usbvcp);
994*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
995*c77a61a7Syz 
996*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
997*c77a61a7Syz 	    "usbvc_ioctl exit");
998*c77a61a7Syz 
999*c77a61a7Syz 	return (rv);
1000*c77a61a7Syz }
1001*c77a61a7Syz 
1002*c77a61a7Syz 
1003*c77a61a7Syz /* Entry for mmap system call */
1004*c77a61a7Syz static int
1005*c77a61a7Syz usbvc_devmap(dev_t dev, devmap_cookie_t handle, offset_t off,
1006*c77a61a7Syz 	size_t len, size_t *maplen, uint_t model)
1007*c77a61a7Syz {
1008*c77a61a7Syz 	usbvc_state_t		*usbvcp;
1009*c77a61a7Syz 	int			error, i;
1010*c77a61a7Syz 	usbvc_buf_t		*buf = NULL;
1011*c77a61a7Syz 	usbvc_stream_if_t	*strm_if;
1012*c77a61a7Syz 	usbvc_buf_grp_t		*bufgrp;
1013*c77a61a7Syz 
1014*c77a61a7Syz 	usbvcp = ddi_get_soft_state(usbvc_statep, getminor(dev));
1015*c77a61a7Syz 	if (usbvcp == NULL) {
1016*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_DEVMAP, usbvcp->usbvc_log_handle,
1017*c77a61a7Syz 		    "usbvc_devmap: usbvcp == NULL");
1018*c77a61a7Syz 
1019*c77a61a7Syz 		return (ENXIO);
1020*c77a61a7Syz 	}
1021*c77a61a7Syz 
1022*c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_DEVMAP, usbvcp->usbvc_log_handle,
1023*c77a61a7Syz 	    "devmap: memory map for instance(%d), off=%llx, len=%d, maplen=%d,"
1024*c77a61a7Syz 	    " model=%d", getminor(dev), off, len, maplen, model);
1025*c77a61a7Syz 
1026*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
1027*c77a61a7Syz 	(void) usbvc_serialize_access(usbvcp, USBVC_SER_NOSIG);
1028*c77a61a7Syz 	strm_if = usbvcp->usbvc_curr_strm;
1029*c77a61a7Syz 	if (!strm_if) {
1030*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_DEVMAP, usbvcp->usbvc_log_handle,
1031*c77a61a7Syz 		    "usbvc_devmap: No current strm if");
1032*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
1033*c77a61a7Syz 
1034*c77a61a7Syz 		return (ENXIO);
1035*c77a61a7Syz 	}
1036*c77a61a7Syz 	bufgrp = &strm_if->buf_map;
1037*c77a61a7Syz 	for (i = 0; i < bufgrp->buf_cnt; i++) {
1038*c77a61a7Syz 		if (bufgrp->buf_head[i].v4l2_buf.m.offset == off) {
1039*c77a61a7Syz 			buf = &bufgrp->buf_head[i];
1040*c77a61a7Syz 
1041*c77a61a7Syz 			break;
1042*c77a61a7Syz 		}
1043*c77a61a7Syz 	}
1044*c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_DEVMAP, usbvcp->usbvc_log_handle,
1045*c77a61a7Syz 	    "usbvc_devmap: idx=%d", i);
1046*c77a61a7Syz 	if (buf == NULL) {
1047*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
1048*c77a61a7Syz 
1049*c77a61a7Syz 		return (ENXIO);
1050*c77a61a7Syz 	}
1051*c77a61a7Syz 	/*
1052*c77a61a7Syz 	 * round up len to a multiple of a page size, according to chapter
1053*c77a61a7Syz 	 * 10 of "writing device drivers"
1054*c77a61a7Syz 	 */
1055*c77a61a7Syz 	len = ptob(btopr(len));
1056*c77a61a7Syz 	if (len > ptob(btopr(buf->len))) {
1057*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_DEVMAP, usbvcp->usbvc_log_handle,
1058*c77a61a7Syz 		    "usbvc_devmap: len=%x", len);
1059*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
1060*c77a61a7Syz 
1061*c77a61a7Syz 		return (ENXIO);
1062*c77a61a7Syz 	}
1063*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
1064*c77a61a7Syz 
1065*c77a61a7Syz 	error = devmap_umem_setup(handle, usbvcp->usbvc_dip, NULL,
1066*c77a61a7Syz 		buf->umem_cookie, off, len, PROT_ALL, DEVMAP_DEFAULTS, NULL);
1067*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
1068*c77a61a7Syz 	*maplen = len;
1069*c77a61a7Syz 	if (error == 0 && buf->status == USBVC_BUF_INIT) {
1070*c77a61a7Syz 		buf->status = USBVC_BUF_MAPPED;
1071*c77a61a7Syz 	} else {
1072*c77a61a7Syz 		USB_DPRINTF_L3(PRINT_MASK_DEVMAP, usbvcp->usbvc_log_handle,
1073*c77a61a7Syz 		    "usbvc_devmap: devmap_umem_setup, err=%d", error);
1074*c77a61a7Syz 	}
1075*c77a61a7Syz 
1076*c77a61a7Syz 	(void) usbvc_release_access(usbvcp);
1077*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
1078*c77a61a7Syz 
1079*c77a61a7Syz 	return (error);
1080*c77a61a7Syz }
1081*c77a61a7Syz 
1082*c77a61a7Syz /*
1083*c77a61a7Syz  * pm and cpr
1084*c77a61a7Syz  */
1085*c77a61a7Syz 
1086*c77a61a7Syz /*
1087*c77a61a7Syz  * usbvc_power :
1088*c77a61a7Syz  *	Power entry point, the workhorse behind pm_raise_power, pm_lower_power,
1089*c77a61a7Syz  *	usb_req_raise_power and usb_req_lower_power.
1090*c77a61a7Syz  */
1091*c77a61a7Syz /* ARGSUSED */
1092*c77a61a7Syz static int
1093*c77a61a7Syz usbvc_power(dev_info_t *dip, int comp, int level)
1094*c77a61a7Syz {
1095*c77a61a7Syz 	usbvc_state_t	*usbvcp;
1096*c77a61a7Syz 	usbvc_power_t	*pm;
1097*c77a61a7Syz 	int		rval = USB_FAILURE;
1098*c77a61a7Syz 
1099*c77a61a7Syz 	usbvcp = ddi_get_soft_state(usbvc_statep, ddi_get_instance(dip));
1100*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
1101*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1102*c77a61a7Syz 	    "usbvc_power: enter: level = %d, dev_state: %x",
1103*c77a61a7Syz 	    level, usbvcp->usbvc_dev_state);
1104*c77a61a7Syz 
1105*c77a61a7Syz 	if (usbvcp->usbvc_pm == NULL) {
1106*c77a61a7Syz 
1107*c77a61a7Syz 		goto done;
1108*c77a61a7Syz 	}
1109*c77a61a7Syz 
1110*c77a61a7Syz 	pm = usbvcp->usbvc_pm;
1111*c77a61a7Syz 
1112*c77a61a7Syz 	/* Check if we are transitioning to a legal power level */
1113*c77a61a7Syz 	if (USB_DEV_PWRSTATE_OK(pm->usbvc_pwr_states, level)) {
1114*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
1115*c77a61a7Syz 		    "usbvc_power: illegal power level = %d "
1116*c77a61a7Syz 		    "pwr_states: %x", level, pm->usbvc_pwr_states);
1117*c77a61a7Syz 
1118*c77a61a7Syz 		goto done;
1119*c77a61a7Syz 	}
1120*c77a61a7Syz 	/*
1121*c77a61a7Syz 	 * if we are about to raise power and asked to lower power, fail
1122*c77a61a7Syz 	 */
1123*c77a61a7Syz 	if (pm->usbvc_raise_power && (level < (int)pm->usbvc_current_power)) {
1124*c77a61a7Syz 
1125*c77a61a7Syz 		goto done;
1126*c77a61a7Syz 	}
1127*c77a61a7Syz 	switch (level) {
1128*c77a61a7Syz 	case USB_DEV_OS_PWR_OFF :
1129*c77a61a7Syz 		rval = usbvc_pwrlvl0(usbvcp);
1130*c77a61a7Syz 
1131*c77a61a7Syz 		break;
1132*c77a61a7Syz 	case USB_DEV_OS_PWR_1 :
1133*c77a61a7Syz 		rval = usbvc_pwrlvl1(usbvcp);
1134*c77a61a7Syz 
1135*c77a61a7Syz 		break;
1136*c77a61a7Syz 	case USB_DEV_OS_PWR_2 :
1137*c77a61a7Syz 		rval = usbvc_pwrlvl2(usbvcp);
1138*c77a61a7Syz 
1139*c77a61a7Syz 		break;
1140*c77a61a7Syz 	case USB_DEV_OS_FULL_PWR :
1141*c77a61a7Syz 		rval = usbvc_pwrlvl3(usbvcp);
1142*c77a61a7Syz 
1143*c77a61a7Syz 		break;
1144*c77a61a7Syz 	}
1145*c77a61a7Syz 
1146*c77a61a7Syz done:
1147*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
1148*c77a61a7Syz 
1149*c77a61a7Syz 	return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
1150*c77a61a7Syz }
1151*c77a61a7Syz 
1152*c77a61a7Syz 
1153*c77a61a7Syz /*
1154*c77a61a7Syz  * usbvc_init_power_mgmt:
1155*c77a61a7Syz  *	Initialize power management and remote wakeup functionality.
1156*c77a61a7Syz  *	No mutex is necessary in this function as it's called only by attach.
1157*c77a61a7Syz  */
1158*c77a61a7Syz static void
1159*c77a61a7Syz usbvc_init_power_mgmt(usbvc_state_t *usbvcp)
1160*c77a61a7Syz {
1161*c77a61a7Syz 	usbvc_power_t	*usbvcpm;
1162*c77a61a7Syz 	uint_t		pwr_states;
1163*c77a61a7Syz 
1164*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1165*c77a61a7Syz 	    "init_power_mgmt enter");
1166*c77a61a7Syz 
1167*c77a61a7Syz 	/* Allocate the state structure */
1168*c77a61a7Syz 	usbvcpm = kmem_zalloc(sizeof (usbvc_power_t), KM_SLEEP);
1169*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
1170*c77a61a7Syz 	usbvcp->usbvc_pm = usbvcpm;
1171*c77a61a7Syz 	usbvcpm->usbvc_state = usbvcp;
1172*c77a61a7Syz 	usbvcpm->usbvc_pm_capabilities = 0;
1173*c77a61a7Syz 	usbvcpm->usbvc_current_power = USB_DEV_OS_FULL_PWR;
1174*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
1175*c77a61a7Syz 
1176*c77a61a7Syz 	if (usb_create_pm_components(usbvcp->usbvc_dip, &pwr_states) ==
1177*c77a61a7Syz 		USB_SUCCESS) {
1178*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1179*c77a61a7Syz 		    "usbvc_init_power_mgmt: created PM components");
1180*c77a61a7Syz 
1181*c77a61a7Syz 		if (usb_handle_remote_wakeup(usbvcp->usbvc_dip,
1182*c77a61a7Syz 		    USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) {
1183*c77a61a7Syz 			usbvcpm->usbvc_wakeup_enabled = 1;
1184*c77a61a7Syz 		} else {
1185*c77a61a7Syz 			USB_DPRINTF_L2(PRINT_MASK_ATTA,
1186*c77a61a7Syz 			    usbvcp->usbvc_log_handle, "usbvc_init_power_mgmt:"
1187*c77a61a7Syz 			    " remote wakeup not supported");
1188*c77a61a7Syz 		}
1189*c77a61a7Syz 
1190*c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
1191*c77a61a7Syz 		usbvcpm->usbvc_pwr_states = (uint8_t)pwr_states;
1192*c77a61a7Syz 		usbvc_pm_busy_component(usbvcp);
1193*c77a61a7Syz 		usbvcpm->usbvc_raise_power = B_TRUE;
1194*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
1195*c77a61a7Syz 
1196*c77a61a7Syz 		(void) pm_raise_power(
1197*c77a61a7Syz 		    usbvcp->usbvc_dip, 0, USB_DEV_OS_FULL_PWR);
1198*c77a61a7Syz 
1199*c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
1200*c77a61a7Syz 		usbvcpm->usbvc_raise_power = B_FALSE;
1201*c77a61a7Syz 		usbvc_pm_idle_component(usbvcp);
1202*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
1203*c77a61a7Syz 
1204*c77a61a7Syz 	}
1205*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1206*c77a61a7Syz 	    "usbvc_init_power_mgmt: end");
1207*c77a61a7Syz }
1208*c77a61a7Syz 
1209*c77a61a7Syz 
1210*c77a61a7Syz /*
1211*c77a61a7Syz  * usbvc_destroy_power_mgmt:
1212*c77a61a7Syz  *	Shut down and destroy power management and remote wakeup functionality.
1213*c77a61a7Syz  */
1214*c77a61a7Syz static void
1215*c77a61a7Syz usbvc_destroy_power_mgmt(usbvc_state_t *usbvcp)
1216*c77a61a7Syz {
1217*c77a61a7Syz 	usbvc_power_t	*pm;
1218*c77a61a7Syz 	int		rval;
1219*c77a61a7Syz 
1220*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1221*c77a61a7Syz 	    "destroy_power_mgmt enter");
1222*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
1223*c77a61a7Syz 	pm = usbvcp->usbvc_pm;
1224*c77a61a7Syz 	if (pm && (usbvcp->usbvc_dev_state != USB_DEV_DISCONNECTED)) {
1225*c77a61a7Syz 
1226*c77a61a7Syz 		usbvc_pm_busy_component(usbvcp);
1227*c77a61a7Syz 		if (pm->usbvc_wakeup_enabled) {
1228*c77a61a7Syz 			pm->usbvc_raise_power = B_TRUE;
1229*c77a61a7Syz 			mutex_exit(&usbvcp->usbvc_mutex);
1230*c77a61a7Syz 
1231*c77a61a7Syz 			/* First bring the device to full power */
1232*c77a61a7Syz 			(void) pm_raise_power(usbvcp->usbvc_dip, 0,
1233*c77a61a7Syz 			    USB_DEV_OS_FULL_PWR);
1234*c77a61a7Syz 			if ((rval = usb_handle_remote_wakeup(
1235*c77a61a7Syz 			    usbvcp->usbvc_dip,
1236*c77a61a7Syz 			    USB_REMOTE_WAKEUP_DISABLE)) !=
1237*c77a61a7Syz 			    USB_SUCCESS) {
1238*c77a61a7Syz 				USB_DPRINTF_L2(PRINT_MASK_ATTA,
1239*c77a61a7Syz 				    usbvcp->usbvc_log_handle,
1240*c77a61a7Syz 				    "usbvc_destroy_power_mgmt: "
1241*c77a61a7Syz 				    "Error disabling rmt wakeup: rval = %d",
1242*c77a61a7Syz 				    rval);
1243*c77a61a7Syz 			}
1244*c77a61a7Syz 			mutex_enter(&usbvcp->usbvc_mutex);
1245*c77a61a7Syz 			pm->usbvc_raise_power = B_FALSE;
1246*c77a61a7Syz 
1247*c77a61a7Syz 		}
1248*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
1249*c77a61a7Syz 
1250*c77a61a7Syz 		/*
1251*c77a61a7Syz 		 * Since remote wakeup is disabled now,
1252*c77a61a7Syz 		 * no one can raise power
1253*c77a61a7Syz 		 * and get to device once power is lowered here.
1254*c77a61a7Syz 		 */
1255*c77a61a7Syz 		(void) pm_lower_power(usbvcp->usbvc_dip, 0, USB_DEV_OS_PWR_OFF);
1256*c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
1257*c77a61a7Syz 		usbvc_pm_idle_component(usbvcp);
1258*c77a61a7Syz 	}
1259*c77a61a7Syz 
1260*c77a61a7Syz 	if (pm) {
1261*c77a61a7Syz 		kmem_free(pm, sizeof (usbvc_power_t));
1262*c77a61a7Syz 		usbvcp->usbvc_pm = NULL;
1263*c77a61a7Syz 	}
1264*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
1265*c77a61a7Syz }
1266*c77a61a7Syz 
1267*c77a61a7Syz 
1268*c77a61a7Syz static void
1269*c77a61a7Syz usbvc_pm_busy_component(usbvc_state_t *usbvcp)
1270*c77a61a7Syz {
1271*c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
1272*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1273*c77a61a7Syz 	    "usbvc_pm_busy_component: enter");
1274*c77a61a7Syz 
1275*c77a61a7Syz 	usbvcp->usbvc_pm->usbvc_pm_busy++;
1276*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
1277*c77a61a7Syz 
1278*c77a61a7Syz 	if (pm_busy_component(usbvcp->usbvc_dip, 0) !=
1279*c77a61a7Syz 	    DDI_SUCCESS) {
1280*c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
1281*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1282*c77a61a7Syz 		    "usbvc_pm_busy_component: pm busy fail, usbvc_pm_busy=%d",
1283*c77a61a7Syz 		    usbvcp->usbvc_pm->usbvc_pm_busy);
1284*c77a61a7Syz 
1285*c77a61a7Syz 		usbvcp->usbvc_pm->usbvc_pm_busy--;
1286*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
1287*c77a61a7Syz 	}
1288*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
1289*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1290*c77a61a7Syz 	    "usbvc_pm_busy_component: exit");
1291*c77a61a7Syz }
1292*c77a61a7Syz 
1293*c77a61a7Syz 
1294*c77a61a7Syz static void
1295*c77a61a7Syz usbvc_pm_idle_component(usbvc_state_t *usbvcp)
1296*c77a61a7Syz {
1297*c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
1298*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1299*c77a61a7Syz 	    "usbvc_pm_idle_component: enter");
1300*c77a61a7Syz 
1301*c77a61a7Syz 	if (usbvcp->usbvc_pm != NULL) {
1302*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
1303*c77a61a7Syz 		if (pm_idle_component(usbvcp->usbvc_dip, 0) ==
1304*c77a61a7Syz 		    DDI_SUCCESS) {
1305*c77a61a7Syz 			mutex_enter(&usbvcp->usbvc_mutex);
1306*c77a61a7Syz 			ASSERT(usbvcp->usbvc_pm->usbvc_pm_busy > 0);
1307*c77a61a7Syz 			usbvcp->usbvc_pm->usbvc_pm_busy--;
1308*c77a61a7Syz 			mutex_exit(&usbvcp->usbvc_mutex);
1309*c77a61a7Syz 		}
1310*c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
1311*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1312*c77a61a7Syz 		    "usbvc_pm_idle_component: %d",
1313*c77a61a7Syz 		    usbvcp->usbvc_pm->usbvc_pm_busy);
1314*c77a61a7Syz 	}
1315*c77a61a7Syz }
1316*c77a61a7Syz 
1317*c77a61a7Syz 
1318*c77a61a7Syz /*
1319*c77a61a7Syz  * usbvc_pwrlvl0:
1320*c77a61a7Syz  * Functions to handle power transition for OS levels 0 -> 3
1321*c77a61a7Syz  */
1322*c77a61a7Syz static int
1323*c77a61a7Syz usbvc_pwrlvl0(usbvc_state_t *usbvcp)
1324*c77a61a7Syz {
1325*c77a61a7Syz 	int rval;
1326*c77a61a7Syz 
1327*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1328*c77a61a7Syz 	    "usbvc_pwrlvl0, dev_state: %x", usbvcp->usbvc_dev_state);
1329*c77a61a7Syz 
1330*c77a61a7Syz 	switch (usbvcp->usbvc_dev_state) {
1331*c77a61a7Syz 	case USB_DEV_ONLINE:
1332*c77a61a7Syz 		/* Deny the powerdown request if the device is busy */
1333*c77a61a7Syz 		if (usbvcp->usbvc_pm->usbvc_pm_busy != 0) {
1334*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1335*c77a61a7Syz 		    "usbvc_pwrlvl0: usbvc_pm_busy");
1336*c77a61a7Syz 
1337*c77a61a7Syz 			return (USB_FAILURE);
1338*c77a61a7Syz 		}
1339*c77a61a7Syz 
1340*c77a61a7Syz 		/* Issue USB D3 command to the device here */
1341*c77a61a7Syz 		rval = usb_set_device_pwrlvl3(usbvcp->usbvc_dip);
1342*c77a61a7Syz 		ASSERT(rval == USB_SUCCESS);
1343*c77a61a7Syz 
1344*c77a61a7Syz 		usbvcp->usbvc_dev_state = USB_DEV_PWRED_DOWN;
1345*c77a61a7Syz 		usbvcp->usbvc_pm->usbvc_current_power =
1346*c77a61a7Syz 						USB_DEV_OS_PWR_OFF;
1347*c77a61a7Syz 		/* FALLTHRU */
1348*c77a61a7Syz 	case USB_DEV_DISCONNECTED:
1349*c77a61a7Syz 	case USB_DEV_SUSPENDED:
1350*c77a61a7Syz 		/* allow a disconnect/cpr'ed device to go to lower power */
1351*c77a61a7Syz 
1352*c77a61a7Syz 		return (USB_SUCCESS);
1353*c77a61a7Syz 	case USB_DEV_PWRED_DOWN:
1354*c77a61a7Syz 	default:
1355*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1356*c77a61a7Syz 		    "usbvc_pwrlvl0: illegal dev state");
1357*c77a61a7Syz 
1358*c77a61a7Syz 		return (USB_FAILURE);
1359*c77a61a7Syz 	}
1360*c77a61a7Syz }
1361*c77a61a7Syz 
1362*c77a61a7Syz 
1363*c77a61a7Syz /*
1364*c77a61a7Syz  * usbvc_pwrlvl1:
1365*c77a61a7Syz  *	Functions to handle power transition to OS levels -> 2
1366*c77a61a7Syz  */
1367*c77a61a7Syz static int
1368*c77a61a7Syz usbvc_pwrlvl1(usbvc_state_t *usbvcp)
1369*c77a61a7Syz {
1370*c77a61a7Syz 	int	rval;
1371*c77a61a7Syz 
1372*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1373*c77a61a7Syz 	    "usbvc_pwrlvl1");
1374*c77a61a7Syz 
1375*c77a61a7Syz 	/* Issue USB D2 command to the device here */
1376*c77a61a7Syz 	rval = usb_set_device_pwrlvl2(usbvcp->usbvc_dip);
1377*c77a61a7Syz 	ASSERT(rval == USB_SUCCESS);
1378*c77a61a7Syz 
1379*c77a61a7Syz 	return (USB_FAILURE);
1380*c77a61a7Syz }
1381*c77a61a7Syz 
1382*c77a61a7Syz 
1383*c77a61a7Syz /*
1384*c77a61a7Syz  * usbvc_pwrlvl2:
1385*c77a61a7Syz  *	Functions to handle power transition to OS levels -> 1
1386*c77a61a7Syz  */
1387*c77a61a7Syz static int
1388*c77a61a7Syz usbvc_pwrlvl2(usbvc_state_t *usbvcp)
1389*c77a61a7Syz {
1390*c77a61a7Syz 	int	rval;
1391*c77a61a7Syz 
1392*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1393*c77a61a7Syz 	    "usbvc_pwrlvl2");
1394*c77a61a7Syz 
1395*c77a61a7Syz 	/* Issue USB D1 command to the device here */
1396*c77a61a7Syz 	rval = usb_set_device_pwrlvl1(usbvcp->usbvc_dip);
1397*c77a61a7Syz 	ASSERT(rval == USB_SUCCESS);
1398*c77a61a7Syz 
1399*c77a61a7Syz 	return (USB_FAILURE);
1400*c77a61a7Syz }
1401*c77a61a7Syz 
1402*c77a61a7Syz 
1403*c77a61a7Syz /*
1404*c77a61a7Syz  * usbvc_pwrlvl3:
1405*c77a61a7Syz  *	Functions to handle power transition to OS level -> 0
1406*c77a61a7Syz  */
1407*c77a61a7Syz static int
1408*c77a61a7Syz usbvc_pwrlvl3(usbvc_state_t *usbvcp)
1409*c77a61a7Syz {
1410*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1411*c77a61a7Syz 	    "usbvc_pwrlvl3, dev_stat=%d", usbvcp->usbvc_dev_state);
1412*c77a61a7Syz 
1413*c77a61a7Syz 	switch (usbvcp->usbvc_dev_state) {
1414*c77a61a7Syz 	case USB_DEV_PWRED_DOWN:
1415*c77a61a7Syz 		/* Issue USB D0 command to the device here */
1416*c77a61a7Syz 		(void) usb_set_device_pwrlvl0(usbvcp->usbvc_dip);
1417*c77a61a7Syz 
1418*c77a61a7Syz 		usbvcp->usbvc_dev_state = USB_DEV_ONLINE;
1419*c77a61a7Syz 		usbvcp->usbvc_pm->usbvc_current_power =
1420*c77a61a7Syz 		    USB_DEV_OS_FULL_PWR;
1421*c77a61a7Syz 
1422*c77a61a7Syz 		/* FALLTHRU */
1423*c77a61a7Syz 	case USB_DEV_ONLINE:
1424*c77a61a7Syz 		/* we are already in full power */
1425*c77a61a7Syz 		/* FALLTHRU */
1426*c77a61a7Syz 	case USB_DEV_DISCONNECTED:
1427*c77a61a7Syz 	case USB_DEV_SUSPENDED:
1428*c77a61a7Syz 		/*
1429*c77a61a7Syz 		 * PM framework tries to put us in full power
1430*c77a61a7Syz 		 * during system shutdown. If we are disconnected/cpr'ed
1431*c77a61a7Syz 		 * return success anyways
1432*c77a61a7Syz 		 */
1433*c77a61a7Syz 
1434*c77a61a7Syz 		return (USB_SUCCESS);
1435*c77a61a7Syz 	default:
1436*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1437*c77a61a7Syz 		    "usbvc_pwrlvl3: illegal dev state");
1438*c77a61a7Syz 
1439*c77a61a7Syz 		return (USB_FAILURE);
1440*c77a61a7Syz 	}
1441*c77a61a7Syz }
1442*c77a61a7Syz 
1443*c77a61a7Syz 
1444*c77a61a7Syz /*
1445*c77a61a7Syz  * usbvc_cpr_suspend:
1446*c77a61a7Syz  *	Clean up device.
1447*c77a61a7Syz  *	Wait for any IO to finish, then close pipes.
1448*c77a61a7Syz  *	Quiesce device.
1449*c77a61a7Syz  */
1450*c77a61a7Syz static void
1451*c77a61a7Syz usbvc_cpr_suspend(dev_info_t *dip)
1452*c77a61a7Syz {
1453*c77a61a7Syz 	int		instance = ddi_get_instance(dip);
1454*c77a61a7Syz 	usbvc_state_t	*usbvcp = ddi_get_soft_state(usbvc_statep,
1455*c77a61a7Syz 							instance);
1456*c77a61a7Syz 
1457*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1458*c77a61a7Syz 	    "usbvc_cpr_suspend enter");
1459*c77a61a7Syz 
1460*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
1461*c77a61a7Syz 
1462*c77a61a7Syz 	/*
1463*c77a61a7Syz 	 * Set dev_state to suspended so other driver threads don't start any
1464*c77a61a7Syz 	 * new I/O.
1465*c77a61a7Syz 	 */
1466*c77a61a7Syz 	usbvcp->usbvc_dev_state = USB_DEV_SUSPENDED;
1467*c77a61a7Syz 
1468*c77a61a7Syz 	/*
1469*c77a61a7Syz 	 * Wake up the read threads in case there are any threads are blocking.
1470*c77a61a7Syz 	 * After being waked up, those threads will quit immediately since the
1471*c77a61a7Syz 	 * dev_state is not ONLINE
1472*c77a61a7Syz 	 */
1473*c77a61a7Syz 	if (usbvcp->usbvc_io_type == V4L2_MEMORY_MMAP) {
1474*c77a61a7Syz 		cv_broadcast(&usbvcp->usbvc_mapio_cv);
1475*c77a61a7Syz 	} else {
1476*c77a61a7Syz 		cv_broadcast(&usbvcp->usbvc_read_cv);
1477*c77a61a7Syz 	}
1478*c77a61a7Syz 	/* Wait for the other threads to quit */
1479*c77a61a7Syz 	(void) usbvc_serialize_access(usbvcp, USBVC_SER_NOSIG);
1480*c77a61a7Syz 	usbvc_release_access(usbvcp);
1481*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
1482*c77a61a7Syz 
1483*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
1484*c77a61a7Syz 	    "usbvc_cpr_suspend: return");
1485*c77a61a7Syz }
1486*c77a61a7Syz 
1487*c77a61a7Syz 
1488*c77a61a7Syz /*
1489*c77a61a7Syz  * usbvc_cpr_resume:
1490*c77a61a7Syz  *
1491*c77a61a7Syz  *	usbvc_restore_device_state marks success by putting device back online
1492*c77a61a7Syz  */
1493*c77a61a7Syz static void
1494*c77a61a7Syz usbvc_cpr_resume(dev_info_t *dip)
1495*c77a61a7Syz {
1496*c77a61a7Syz 	int		instance = ddi_get_instance(dip);
1497*c77a61a7Syz 	usbvc_state_t	*usbvcp = ddi_get_soft_state(usbvc_statep,
1498*c77a61a7Syz 							instance);
1499*c77a61a7Syz 
1500*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
1501*c77a61a7Syz 	    "resume: enter");
1502*c77a61a7Syz 
1503*c77a61a7Syz 	/*
1504*c77a61a7Syz 	 * NOTE: A pm_raise_power in usbvc_restore_device_state will bring
1505*c77a61a7Syz 	 * the power-up state of device into synch with the system.
1506*c77a61a7Syz 	 */
1507*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
1508*c77a61a7Syz 	usbvc_restore_device_state(dip, usbvcp);
1509*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
1510*c77a61a7Syz }
1511*c77a61a7Syz 
1512*c77a61a7Syz 
1513*c77a61a7Syz /*
1514*c77a61a7Syz  * usbvc_restore_device_state:
1515*c77a61a7Syz  *	Called during hotplug-reconnect and resume.
1516*c77a61a7Syz  *		reenable power management
1517*c77a61a7Syz  *		Verify the device is the same as before the disconnect/suspend.
1518*c77a61a7Syz  *		Restore device state
1519*c77a61a7Syz  *		Thaw any IO which was frozen.
1520*c77a61a7Syz  *		Quiesce device.  (Other routines will activate if thawed IO.)
1521*c77a61a7Syz  *		Set device online.
1522*c77a61a7Syz  *		Leave device disconnected if there are problems.
1523*c77a61a7Syz  */
1524*c77a61a7Syz static void
1525*c77a61a7Syz usbvc_restore_device_state(dev_info_t *dip, usbvc_state_t *usbvcp)
1526*c77a61a7Syz {
1527*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1528*c77a61a7Syz 	    "usbvc_restore_device_state: enter");
1529*c77a61a7Syz 
1530*c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
1531*c77a61a7Syz 
1532*c77a61a7Syz 	ASSERT((usbvcp->usbvc_dev_state == USB_DEV_DISCONNECTED) ||
1533*c77a61a7Syz 	    (usbvcp->usbvc_dev_state == USB_DEV_SUSPENDED));
1534*c77a61a7Syz 
1535*c77a61a7Syz 	usbvc_pm_busy_component(usbvcp);
1536*c77a61a7Syz 	usbvcp->usbvc_pm->usbvc_raise_power = B_TRUE;
1537*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
1538*c77a61a7Syz 	(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
1539*c77a61a7Syz 
1540*c77a61a7Syz 	/* Check if we are talking to the same device */
1541*c77a61a7Syz 	if (usb_check_same_device(dip, usbvcp->usbvc_log_handle,
1542*c77a61a7Syz 	    USB_LOG_L0, PRINT_MASK_ALL,
1543*c77a61a7Syz 	    USB_CHK_BASIC|USB_CHK_CFG, NULL) != USB_SUCCESS) {
1544*c77a61a7Syz 
1545*c77a61a7Syz 		goto fail;
1546*c77a61a7Syz 	}
1547*c77a61a7Syz 
1548*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
1549*c77a61a7Syz 	usbvcp->usbvc_pm->usbvc_raise_power = B_FALSE;
1550*c77a61a7Syz 	usbvcp->usbvc_dev_state = USB_DEV_ONLINE;
1551*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
1552*c77a61a7Syz 
1553*c77a61a7Syz 	if (usbvcp->usbvc_pm->usbvc_wakeup_enabled) {
1554*c77a61a7Syz 
1555*c77a61a7Syz 		/* Failure here means device disappeared again. */
1556*c77a61a7Syz 		if (usb_handle_remote_wakeup(dip, USB_REMOTE_WAKEUP_ENABLE) !=
1557*c77a61a7Syz 		    USB_SUCCESS) {
1558*c77a61a7Syz 			USB_DPRINTF_L2(PRINT_MASK_ATTA,
1559*c77a61a7Syz 			    usbvcp->usbvc_log_handle,
1560*c77a61a7Syz 			    "device may or may not be accessible. "
1561*c77a61a7Syz 			    "Please verify reconnection");
1562*c77a61a7Syz 		}
1563*c77a61a7Syz 	}
1564*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
1565*c77a61a7Syz 
1566*c77a61a7Syz 	usbvc_pm_idle_component(usbvcp);
1567*c77a61a7Syz 
1568*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1569*c77a61a7Syz 	    "usbvc_restore_device_state: end");
1570*c77a61a7Syz 
1571*c77a61a7Syz 	return;
1572*c77a61a7Syz 
1573*c77a61a7Syz fail:
1574*c77a61a7Syz 	/* change the device state from suspended to disconnected */
1575*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
1576*c77a61a7Syz 	usbvcp->usbvc_dev_state = USB_DEV_DISCONNECTED;
1577*c77a61a7Syz 	usbvc_pm_idle_component(usbvcp);
1578*c77a61a7Syz }
1579*c77a61a7Syz 
1580*c77a61a7Syz 
1581*c77a61a7Syz /* Events */
1582*c77a61a7Syz 
1583*c77a61a7Syz /*
1584*c77a61a7Syz  * usbvc_disconnect_event_cb:
1585*c77a61a7Syz  *	Called when device hotplug-removed.
1586*c77a61a7Syz  *		Close pipes. (This does not attempt to contact device.)
1587*c77a61a7Syz  *		Set state to DISCONNECTED
1588*c77a61a7Syz  */
1589*c77a61a7Syz static int
1590*c77a61a7Syz usbvc_disconnect_event_cb(dev_info_t *dip)
1591*c77a61a7Syz {
1592*c77a61a7Syz 	int		instance = ddi_get_instance(dip);
1593*c77a61a7Syz 	usbvc_state_t	*usbvcp =
1594*c77a61a7Syz 			    ddi_get_soft_state(usbvc_statep, instance);
1595*c77a61a7Syz 
1596*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_HOTPLUG, usbvcp->usbvc_log_handle,
1597*c77a61a7Syz 	    "disconnect: enter");
1598*c77a61a7Syz 
1599*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
1600*c77a61a7Syz 	/*
1601*c77a61a7Syz 	 * Save any state of device or IO in progress required by
1602*c77a61a7Syz 	 * usbvc_restore_device_state for proper device "thawing" later.
1603*c77a61a7Syz 	 */
1604*c77a61a7Syz 	usbvcp->usbvc_dev_state = USB_DEV_DISCONNECTED;
1605*c77a61a7Syz 
1606*c77a61a7Syz 	/*
1607*c77a61a7Syz 	 * wake up the read threads in case there are any threads are blocking,
1608*c77a61a7Syz 	 * after being waked up, those threads will quit fail immediately since
1609*c77a61a7Syz 	 * we have changed the dev_stat.
1610*c77a61a7Syz 	 */
1611*c77a61a7Syz 	if (usbvcp->usbvc_io_type == V4L2_MEMORY_MMAP) {
1612*c77a61a7Syz 		cv_broadcast(&usbvcp->usbvc_mapio_cv);
1613*c77a61a7Syz 	} else {
1614*c77a61a7Syz 		cv_broadcast(&usbvcp->usbvc_read_cv);
1615*c77a61a7Syz 	}
1616*c77a61a7Syz 	/* Wait for the other threads to quit */
1617*c77a61a7Syz 	(void) usbvc_serialize_access(usbvcp, USBVC_SER_SIG);
1618*c77a61a7Syz 	usbvc_release_access(usbvcp);
1619*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
1620*c77a61a7Syz 
1621*c77a61a7Syz 	return (USB_SUCCESS);
1622*c77a61a7Syz }
1623*c77a61a7Syz 
1624*c77a61a7Syz 
1625*c77a61a7Syz /*
1626*c77a61a7Syz  * usbvc_reconnect_event_cb:
1627*c77a61a7Syz  *	Called with device hotplug-inserted
1628*c77a61a7Syz  *		Restore state
1629*c77a61a7Syz  */
1630*c77a61a7Syz static int
1631*c77a61a7Syz usbvc_reconnect_event_cb(dev_info_t *dip)
1632*c77a61a7Syz {
1633*c77a61a7Syz 	int		instance = ddi_get_instance(dip);
1634*c77a61a7Syz 	usbvc_state_t	*usbvcp = ddi_get_soft_state(usbvc_statep, instance);
1635*c77a61a7Syz 
1636*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_HOTPLUG, usbvcp->usbvc_log_handle,
1637*c77a61a7Syz 	    "reconnect: enter");
1638*c77a61a7Syz 
1639*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
1640*c77a61a7Syz 	(void) usbvc_serialize_access(usbvcp, USBVC_SER_SIG);
1641*c77a61a7Syz 	usbvc_restore_device_state(dip, usbvcp);
1642*c77a61a7Syz 	usbvc_release_access(usbvcp);
1643*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
1644*c77a61a7Syz 
1645*c77a61a7Syz 	return (USB_SUCCESS);
1646*c77a61a7Syz }
1647*c77a61a7Syz 
1648*c77a61a7Syz /* Sync objs and lists */
1649*c77a61a7Syz 
1650*c77a61a7Syz /*
1651*c77a61a7Syz  * init/fini sync objects during attach
1652*c77a61a7Syz  */
1653*c77a61a7Syz static void
1654*c77a61a7Syz usbvc_init_sync_objs(usbvc_state_t *usbvcp)
1655*c77a61a7Syz {
1656*c77a61a7Syz 	mutex_init(&usbvcp->usbvc_mutex, NULL, MUTEX_DRIVER,
1657*c77a61a7Syz 			usbvcp->usbvc_reg->dev_iblock_cookie);
1658*c77a61a7Syz 
1659*c77a61a7Syz 	cv_init(&usbvcp->usbvc_serial_cv, NULL, CV_DRIVER, NULL);
1660*c77a61a7Syz 	cv_init(&usbvcp->usbvc_read_cv, NULL, CV_DRIVER, NULL);
1661*c77a61a7Syz 	cv_init(&usbvcp->usbvc_mapio_cv, NULL, CV_DRIVER, NULL);
1662*c77a61a7Syz 
1663*c77a61a7Syz 	usbvcp->usbvc_serial_inuse = B_FALSE;
1664*c77a61a7Syz 
1665*c77a61a7Syz 	usbvcp->usbvc_locks_initialized = B_TRUE;
1666*c77a61a7Syz }
1667*c77a61a7Syz 
1668*c77a61a7Syz 
1669*c77a61a7Syz static void
1670*c77a61a7Syz usbvc_fini_sync_objs(usbvc_state_t *usbvcp)
1671*c77a61a7Syz {
1672*c77a61a7Syz 	cv_destroy(&usbvcp->usbvc_serial_cv);
1673*c77a61a7Syz 	cv_destroy(&usbvcp->usbvc_read_cv);
1674*c77a61a7Syz 	cv_destroy(&usbvcp->usbvc_mapio_cv);
1675*c77a61a7Syz 
1676*c77a61a7Syz 	mutex_destroy(&usbvcp->usbvc_mutex);
1677*c77a61a7Syz }
1678*c77a61a7Syz 
1679*c77a61a7Syz 
1680*c77a61a7Syz static void
1681*c77a61a7Syz usbvc_init_lists(usbvc_state_t *usbvcp)
1682*c77a61a7Syz {
1683*c77a61a7Syz 	/* video terminals */
1684*c77a61a7Syz 	list_create(&(usbvcp->usbvc_term_list), sizeof (usbvc_terms_t),
1685*c77a61a7Syz 	    offsetof(usbvc_terms_t, term_node));
1686*c77a61a7Syz 
1687*c77a61a7Syz 	/* video units */
1688*c77a61a7Syz 	list_create(&(usbvcp->usbvc_unit_list), sizeof (usbvc_units_t),
1689*c77a61a7Syz 	    offsetof(usbvc_units_t, unit_node));
1690*c77a61a7Syz 
1691*c77a61a7Syz 	/* stream interfaces */
1692*c77a61a7Syz 	list_create(&(usbvcp->usbvc_stream_list), sizeof (usbvc_stream_if_t),
1693*c77a61a7Syz 	    offsetof(usbvc_stream_if_t, stream_if_node));
1694*c77a61a7Syz }
1695*c77a61a7Syz 
1696*c77a61a7Syz 
1697*c77a61a7Syz /*
1698*c77a61a7Syz  * Free all the data structures allocated when parsing descriptors of ctrl
1699*c77a61a7Syz  * and stream interfaces. It is safe to call this function because it always
1700*c77a61a7Syz  * checks the pointer before free mem.
1701*c77a61a7Syz  */
1702*c77a61a7Syz static void
1703*c77a61a7Syz usbvc_fini_lists(usbvc_state_t *usbvcp)
1704*c77a61a7Syz {
1705*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
1706*c77a61a7Syz 	    "usbvc_fini_lists: enter");
1707*c77a61a7Syz 
1708*c77a61a7Syz 	usbvc_free_ctrl_descr(usbvcp);
1709*c77a61a7Syz 
1710*c77a61a7Syz 	/* Free all video stream structure and the sub-structures */
1711*c77a61a7Syz 	usbvc_free_stream_descr(usbvcp);
1712*c77a61a7Syz 
1713*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
1714*c77a61a7Syz 	    "usbvc_fini_lists: end");
1715*c77a61a7Syz }
1716*c77a61a7Syz 
1717*c77a61a7Syz 
1718*c77a61a7Syz /*
1719*c77a61a7Syz  * Free all the data structures allocated when parsing descriptors of ctrl
1720*c77a61a7Syz  * interface.
1721*c77a61a7Syz  */
1722*c77a61a7Syz static void
1723*c77a61a7Syz usbvc_free_ctrl_descr(usbvc_state_t *usbvcp)
1724*c77a61a7Syz {
1725*c77a61a7Syz 	usbvc_terms_t	*term;
1726*c77a61a7Syz 	usbvc_units_t	*unit;
1727*c77a61a7Syz 
1728*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
1729*c77a61a7Syz 	    "usbvc_free_ctrl_descr: enter");
1730*c77a61a7Syz 
1731*c77a61a7Syz 	if (usbvcp->usbvc_vc_header) {
1732*c77a61a7Syz 		kmem_free(usbvcp->usbvc_vc_header, sizeof (usbvc_vc_header_t));
1733*c77a61a7Syz 	}
1734*c77a61a7Syz 
1735*c77a61a7Syz 	/* Free all video terminal structure */
1736*c77a61a7Syz 	while (!list_is_empty(&usbvcp->usbvc_term_list)) {
1737*c77a61a7Syz 			term = list_head(&usbvcp->usbvc_term_list);
1738*c77a61a7Syz 			if (term != NULL) {
1739*c77a61a7Syz 				list_remove(&(usbvcp->usbvc_term_list), term);
1740*c77a61a7Syz 				kmem_free(term, sizeof (usbvc_terms_t));
1741*c77a61a7Syz 			}
1742*c77a61a7Syz 	}
1743*c77a61a7Syz 
1744*c77a61a7Syz 	/* Free all video unit structure */
1745*c77a61a7Syz 	while (!list_is_empty(&usbvcp->usbvc_unit_list)) {
1746*c77a61a7Syz 			unit = list_head(&usbvcp->usbvc_unit_list);
1747*c77a61a7Syz 			if (unit != NULL) {
1748*c77a61a7Syz 				list_remove(&(usbvcp->usbvc_unit_list), unit);
1749*c77a61a7Syz 				kmem_free(unit, sizeof (usbvc_units_t));
1750*c77a61a7Syz 			}
1751*c77a61a7Syz 	}
1752*c77a61a7Syz }
1753*c77a61a7Syz 
1754*c77a61a7Syz 
1755*c77a61a7Syz /*
1756*c77a61a7Syz  * Free all the data structures allocated when parsing descriptors of stream
1757*c77a61a7Syz  * interfaces.
1758*c77a61a7Syz  */
1759*c77a61a7Syz static void
1760*c77a61a7Syz usbvc_free_stream_descr(usbvc_state_t *usbvcp)
1761*c77a61a7Syz {
1762*c77a61a7Syz 	usbvc_stream_if_t	*strm;
1763*c77a61a7Syz 	usbvc_input_header_t	*in_hdr;
1764*c77a61a7Syz 	usbvc_output_header_t	*out_hdr;
1765*c77a61a7Syz 	uint8_t			fmt_cnt, frm_cnt;
1766*c77a61a7Syz 
1767*c77a61a7Syz 	while (!list_is_empty(&usbvcp->usbvc_stream_list)) {
1768*c77a61a7Syz 		USB_DPRINTF_L3(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
1769*c77a61a7Syz 		    "usbvc_fini_lists: stream list not empty.");
1770*c77a61a7Syz 
1771*c77a61a7Syz 		strm = list_head(&usbvcp->usbvc_stream_list);
1772*c77a61a7Syz 		if (strm != NULL) {
1773*c77a61a7Syz 
1774*c77a61a7Syz 			/* unlink this stream's data structure from the list */
1775*c77a61a7Syz 			list_remove(&(usbvcp->usbvc_stream_list), strm);
1776*c77a61a7Syz 		} else {
1777*c77a61a7Syz 
1778*c77a61a7Syz 			/* No real stream data structure in the list */
1779*c77a61a7Syz 			return;
1780*c77a61a7Syz 		}
1781*c77a61a7Syz 
1782*c77a61a7Syz 		in_hdr = strm->input_header;
1783*c77a61a7Syz 		out_hdr = strm->output_header;
1784*c77a61a7Syz 
1785*c77a61a7Syz 		if (in_hdr) {
1786*c77a61a7Syz 			fmt_cnt = in_hdr->descr->bNumFormats;
1787*c77a61a7Syz 		} else if (out_hdr) {
1788*c77a61a7Syz 			fmt_cnt = out_hdr->descr->bNumFormats;
1789*c77a61a7Syz 		}
1790*c77a61a7Syz 
1791*c77a61a7Syz 		USB_DPRINTF_L3(PRINT_MASK_CLOSE,
1792*c77a61a7Syz 		    usbvcp->usbvc_log_handle, "usbvc_fini_lists:"
1793*c77a61a7Syz 		    " fmtgrp cnt=%d", fmt_cnt);
1794*c77a61a7Syz 
1795*c77a61a7Syz 		/* Free headers */
1796*c77a61a7Syz 		if (in_hdr) {
1797*c77a61a7Syz 			kmem_free(in_hdr, sizeof (usbvc_input_header_t));
1798*c77a61a7Syz 		}
1799*c77a61a7Syz 		if (out_hdr) {
1800*c77a61a7Syz 			kmem_free(out_hdr, sizeof (usbvc_output_header_t));
1801*c77a61a7Syz 		}
1802*c77a61a7Syz 
1803*c77a61a7Syz 		/* Free format descriptors */
1804*c77a61a7Syz 		if (strm->format_group) {
1805*c77a61a7Syz 			int i;
1806*c77a61a7Syz 			usbvc_format_group_t *fmtgrp;
1807*c77a61a7Syz 
1808*c77a61a7Syz 			for (i = 0; i < fmt_cnt; i++) {
1809*c77a61a7Syz 				fmtgrp = &strm->format_group[i];
1810*c77a61a7Syz 				if (fmtgrp->format == NULL) {
1811*c77a61a7Syz 
1812*c77a61a7Syz 					break;
1813*c77a61a7Syz 				}
1814*c77a61a7Syz 				if (fmtgrp->still) {
1815*c77a61a7Syz 					kmem_free(fmtgrp->still,
1816*c77a61a7Syz 					    sizeof (usbvc_still_image_frame_t));
1817*c77a61a7Syz 				}
1818*c77a61a7Syz 				frm_cnt = fmtgrp->format->bNumFrameDescriptors;
1819*c77a61a7Syz 
1820*c77a61a7Syz 				USB_DPRINTF_L3(PRINT_MASK_CLOSE,
1821*c77a61a7Syz 				    usbvcp->usbvc_log_handle,
1822*c77a61a7Syz 				    "usbvc_fini_lists:"
1823*c77a61a7Syz 				    " frame cnt=%d", frm_cnt);
1824*c77a61a7Syz 
1825*c77a61a7Syz 				if (fmtgrp->frames) {
1826*c77a61a7Syz 					kmem_free(fmtgrp->frames,
1827*c77a61a7Syz 					    sizeof (usbvc_frames_t) * frm_cnt);
1828*c77a61a7Syz 				}
1829*c77a61a7Syz 			}
1830*c77a61a7Syz 			kmem_free(strm->format_group,
1831*c77a61a7Syz 			    sizeof (usbvc_format_group_t) * fmt_cnt);
1832*c77a61a7Syz 		}
1833*c77a61a7Syz 		USB_DPRINTF_L3(PRINT_MASK_CLOSE,
1834*c77a61a7Syz 		    usbvcp->usbvc_log_handle, "usbvc_fini_lists:"
1835*c77a61a7Syz 		    " free stream_if_t");
1836*c77a61a7Syz 
1837*c77a61a7Syz 		kmem_free(strm, sizeof (usbvc_stream_if_t));
1838*c77a61a7Syz 	}
1839*c77a61a7Syz }
1840*c77a61a7Syz 
1841*c77a61a7Syz /*
1842*c77a61a7Syz  * Parse class specific descriptors of the video device
1843*c77a61a7Syz  */
1844*c77a61a7Syz 
1845*c77a61a7Syz /*
1846*c77a61a7Syz  * Check the length of a class specific descriptor. Make sure cvs_buf_len is
1847*c77a61a7Syz  * not less than the length expected according to uvc spec.
1848*c77a61a7Syz  *
1849*c77a61a7Syz  * Args:
1850*c77a61a7Syz  * - off_num: the cvs_buf offset of the descriptor element that
1851*c77a61a7Syz  *   indicates the number of variable descriptor elements;
1852*c77a61a7Syz  * - size: the size of each variable descriptor element, if zero, then the
1853*c77a61a7Syz  *   size value is offered by off_size;
1854*c77a61a7Syz  * - off_size: the cvs_buf offset of the descriptor element that indicates
1855*c77a61a7Syz  *   the size of each variable descriptor element;
1856*c77a61a7Syz  */
1857*c77a61a7Syz static int
1858*c77a61a7Syz usbvc_chk_descr_len(uint8_t off_num, uint8_t size, uint8_t off_size,
1859*c77a61a7Syz     usb_cvs_data_t *cvs_data)
1860*c77a61a7Syz {
1861*c77a61a7Syz 	uchar_t			*cvs_buf;
1862*c77a61a7Syz 	uint_t			cvs_buf_len;
1863*c77a61a7Syz 
1864*c77a61a7Syz 	cvs_buf = cvs_data->cvs_buf;
1865*c77a61a7Syz 	cvs_buf_len = cvs_data->cvs_buf_len;
1866*c77a61a7Syz 
1867*c77a61a7Syz 	if (size == 0) {
1868*c77a61a7Syz 		if (cvs_buf_len > off_size) {
1869*c77a61a7Syz 			size = cvs_buf[off_size];
1870*c77a61a7Syz 		} else {
1871*c77a61a7Syz 
1872*c77a61a7Syz 			return (USB_FAILURE);
1873*c77a61a7Syz 		}
1874*c77a61a7Syz 	}
1875*c77a61a7Syz 	if (cvs_buf_len < (off_num + 1)) {
1876*c77a61a7Syz 
1877*c77a61a7Syz 		return (USB_FAILURE);
1878*c77a61a7Syz 	}
1879*c77a61a7Syz 
1880*c77a61a7Syz 	if (cvs_buf_len < (cvs_buf[off_num] * size + off_num +1)) {
1881*c77a61a7Syz 
1882*c77a61a7Syz 		return (USB_FAILURE);
1883*c77a61a7Syz 	}
1884*c77a61a7Syz 
1885*c77a61a7Syz 	return (USB_SUCCESS);
1886*c77a61a7Syz }
1887*c77a61a7Syz 
1888*c77a61a7Syz 
1889*c77a61a7Syz /* Parse the descriptors of control interface */
1890*c77a61a7Syz static int
1891*c77a61a7Syz usbvc_parse_ctrl_if(usbvc_state_t *usbvcp)
1892*c77a61a7Syz {
1893*c77a61a7Syz 	int			if_num;
1894*c77a61a7Syz 	int			cvs_num;
1895*c77a61a7Syz 	usb_alt_if_data_t	*if_alt_data;
1896*c77a61a7Syz 	usb_cvs_data_t		*cvs_data;
1897*c77a61a7Syz 	uchar_t			*cvs_buf;
1898*c77a61a7Syz 	uint_t			cvs_buf_len;
1899*c77a61a7Syz 	uint16_t		version;
1900*c77a61a7Syz 
1901*c77a61a7Syz 	if_num = usbvcp->usbvc_reg->dev_curr_if;
1902*c77a61a7Syz 	if_alt_data = usbvcp->usbvc_reg->dev_curr_cfg->cfg_if[if_num].if_alt;
1903*c77a61a7Syz 	cvs_data = if_alt_data->altif_cvs;
1904*c77a61a7Syz 
1905*c77a61a7Syz 	for (cvs_num = 0; cvs_num < if_alt_data->altif_n_cvs; cvs_num++) {
1906*c77a61a7Syz 		cvs_buf = cvs_data[cvs_num].cvs_buf;
1907*c77a61a7Syz 		cvs_buf_len = cvs_data[cvs_num].cvs_buf_len;
1908*c77a61a7Syz 		USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
1909*c77a61a7Syz 		    "usbvc_parse_ctrl_if: cvs_num= %d, cvs_buf_len=%d",
1910*c77a61a7Syz 		    cvs_num, cvs_buf_len);
1911*c77a61a7Syz 
1912*c77a61a7Syz 		/*
1913*c77a61a7Syz 		 * parse interface cvs descriptors here; by checking
1914*c77a61a7Syz 		 * bDescriptorType (cvs_buf[1])
1915*c77a61a7Syz 		 */
1916*c77a61a7Syz 		if (cvs_buf[1] != CS_INTERFACE) {
1917*c77a61a7Syz 
1918*c77a61a7Syz 			continue;
1919*c77a61a7Syz 		}
1920*c77a61a7Syz 
1921*c77a61a7Syz 		/*
1922*c77a61a7Syz 		 * Different descriptors in VC interface; according to
1923*c77a61a7Syz 		 * bDescriptorSubType (cvs_buf[2])
1924*c77a61a7Syz 		 */
1925*c77a61a7Syz 		switch (cvs_buf[2]) {
1926*c77a61a7Syz 		case VC_HEADER:
1927*c77a61a7Syz 
1928*c77a61a7Syz 			/*
1929*c77a61a7Syz 			 * According to uvc spec, there must be one and only
1930*c77a61a7Syz 			 * be one header. If more than one, return failure.
1931*c77a61a7Syz 			 */
1932*c77a61a7Syz 			if (usbvcp->usbvc_vc_header) {
1933*c77a61a7Syz 
1934*c77a61a7Syz 				return (USB_FAILURE);
1935*c77a61a7Syz 			}
1936*c77a61a7Syz 			/*
1937*c77a61a7Syz 			 * Check if it is a valid HEADER descriptor in case of
1938*c77a61a7Syz 			 * a device not compliant to uvc spec. This descriptor
1939*c77a61a7Syz 			 * is critical, return failure if not a valid one.
1940*c77a61a7Syz 			 */
1941*c77a61a7Syz 			if (usbvc_chk_descr_len(11, 1, 0, cvs_data) !=
1942*c77a61a7Syz 			    USB_SUCCESS) {
1943*c77a61a7Syz 
1944*c77a61a7Syz 				return (USB_FAILURE);
1945*c77a61a7Syz 			}
1946*c77a61a7Syz 			usbvcp->usbvc_vc_header =
1947*c77a61a7Syz 			    (usbvc_vc_header_t *)kmem_zalloc(
1948*c77a61a7Syz 			    sizeof (usbvc_vc_header_t), KM_SLEEP);
1949*c77a61a7Syz 			usbvcp->usbvc_vc_header->descr =
1950*c77a61a7Syz 			(usbvc_vc_header_descr_t *)&cvs_buf[0];
1951*c77a61a7Syz 
1952*c77a61a7Syz 			LE_TO_UINT16(usbvcp->usbvc_vc_header->descr->bcdUVC,
1953*c77a61a7Syz 			    0, version);
1954*c77a61a7Syz 			USB_DPRINTF_L3(PRINT_MASK_ATTA,
1955*c77a61a7Syz 			    usbvcp->usbvc_log_handle, "usbvc_parse_ctrl_if:"
1956*c77a61a7Syz 			    " VC header, bcdUVC=%x", version);
1957*c77a61a7Syz 			if (usbvcp->usbvc_vc_header->descr->bInCollection ==
1958*c77a61a7Syz 			    0) {
1959*c77a61a7Syz 				USB_DPRINTF_L3(PRINT_MASK_ATTA,
1960*c77a61a7Syz 				    usbvcp->usbvc_log_handle,
1961*c77a61a7Syz 				    "usbvc_parse_ctrl_if: no strm interfaces");
1962*c77a61a7Syz 
1963*c77a61a7Syz 				break;
1964*c77a61a7Syz 			}
1965*c77a61a7Syz 
1966*c77a61a7Syz 			/* stream interface numbers */
1967*c77a61a7Syz 			usbvcp->usbvc_vc_header->baInterfaceNr = &cvs_buf[12];
1968*c77a61a7Syz 
1969*c77a61a7Syz 			break;
1970*c77a61a7Syz 		case VC_INPUT_TERMINAL:
1971*c77a61a7Syz 		{
1972*c77a61a7Syz 			usbvc_terms_t *term;
1973*c77a61a7Syz 
1974*c77a61a7Syz 			/*
1975*c77a61a7Syz 			 * Check if it is a valid descriptor in case of a
1976*c77a61a7Syz 			 * device not compliant to uvc spec
1977*c77a61a7Syz 			 */
1978*c77a61a7Syz 			if (cvs_buf_len < USBVC_I_TERM_LEN_MIN) {
1979*c77a61a7Syz 
1980*c77a61a7Syz 				break;
1981*c77a61a7Syz 			}
1982*c77a61a7Syz 			term = (usbvc_terms_t *)
1983*c77a61a7Syz 			    kmem_zalloc(sizeof (usbvc_terms_t), KM_SLEEP);
1984*c77a61a7Syz 			term->descr = (usbvc_term_descr_t *)cvs_buf;
1985*c77a61a7Syz 
1986*c77a61a7Syz 			USB_DPRINTF_L3(PRINT_MASK_ATTA,
1987*c77a61a7Syz 			    usbvcp->usbvc_log_handle, "usbvc_parse_ctrl_if: "
1988*c77a61a7Syz 			    "input term type=%x", term->descr->wTerminalType);
1989*c77a61a7Syz 			if (term->descr->wTerminalType == ITT_CAMERA) {
1990*c77a61a7Syz 				if (usbvc_chk_descr_len(14, 1, 0, cvs_data) !=
1991*c77a61a7Syz 				    USB_SUCCESS) {
1992*c77a61a7Syz 					kmem_free(term, sizeof (usbvc_terms_t));
1993*c77a61a7Syz 
1994*c77a61a7Syz 					break;
1995*c77a61a7Syz 				}
1996*c77a61a7Syz 				term->bmControls = &cvs_buf[15];
1997*c77a61a7Syz 			} else if (cvs_buf_len > 8) { /* other input terms */
1998*c77a61a7Syz 				term->bSpecific = &cvs_buf[8];
1999*c77a61a7Syz 			}
2000*c77a61a7Syz 			list_insert_tail(&(usbvcp->usbvc_term_list), term);
2001*c77a61a7Syz 
2002*c77a61a7Syz 			break;
2003*c77a61a7Syz 		}
2004*c77a61a7Syz 		case VC_OUTPUT_TERMINAL:
2005*c77a61a7Syz 		{
2006*c77a61a7Syz 			usbvc_terms_t *term;
2007*c77a61a7Syz 
2008*c77a61a7Syz 			if (cvs_buf_len < USBVC_O_TERM_LEN_MIN) {
2009*c77a61a7Syz 
2010*c77a61a7Syz 				break;
2011*c77a61a7Syz 			}
2012*c77a61a7Syz 			term = (usbvc_terms_t *)
2013*c77a61a7Syz 			    kmem_zalloc(sizeof (usbvc_terms_t), KM_SLEEP);
2014*c77a61a7Syz 			term->descr = (usbvc_term_descr_t *)cvs_buf;
2015*c77a61a7Syz 
2016*c77a61a7Syz 			USB_DPRINTF_L3(PRINT_MASK_ATTA,
2017*c77a61a7Syz 			    usbvcp->usbvc_log_handle, "usbvc_parse_ctrl_if:"
2018*c77a61a7Syz 			    " output term id= %x", term->descr->bTerminalID);
2019*c77a61a7Syz 			if (cvs_buf_len > 9) {
2020*c77a61a7Syz 				term->bSpecific = &cvs_buf[9];
2021*c77a61a7Syz 			}
2022*c77a61a7Syz 			list_insert_tail(&(usbvcp->usbvc_term_list), term);
2023*c77a61a7Syz 
2024*c77a61a7Syz 			break;
2025*c77a61a7Syz 		}
2026*c77a61a7Syz 		case VC_PROCESSING_UNIT:
2027*c77a61a7Syz 		{
2028*c77a61a7Syz 			uint8_t sz;
2029*c77a61a7Syz 			usbvc_units_t *unit;
2030*c77a61a7Syz 
2031*c77a61a7Syz 			if (usbvc_chk_descr_len(7, 1, 0, cvs_data) !=
2032*c77a61a7Syz 			    USB_SUCCESS) {
2033*c77a61a7Syz 
2034*c77a61a7Syz 				break;
2035*c77a61a7Syz 			}
2036*c77a61a7Syz 
2037*c77a61a7Syz 			/* bControlSize */
2038*c77a61a7Syz 			sz = cvs_buf[7];
2039*c77a61a7Syz 
2040*c77a61a7Syz 			if ((sz + 8) >= cvs_buf_len) {
2041*c77a61a7Syz 
2042*c77a61a7Syz 				break;
2043*c77a61a7Syz 			}
2044*c77a61a7Syz 			unit = (usbvc_units_t *)
2045*c77a61a7Syz 			    kmem_zalloc(sizeof (usbvc_units_t), KM_SLEEP);
2046*c77a61a7Syz 
2047*c77a61a7Syz 			unit->descr = (usbvc_unit_descr_t *)cvs_buf;
2048*c77a61a7Syz 
2049*c77a61a7Syz 			USB_DPRINTF_L3(PRINT_MASK_ATTA,
2050*c77a61a7Syz 			    usbvcp->usbvc_log_handle, "usbvc_parse_ctrl_if: "
2051*c77a61a7Syz 			    "unit type=%x", unit->descr->bDescriptorSubType);
2052*c77a61a7Syz 
2053*c77a61a7Syz 			if (sz != 0) {
2054*c77a61a7Syz 				unit->bmControls = &cvs_buf[8];
2055*c77a61a7Syz 			}
2056*c77a61a7Syz 			unit->iProcessing = cvs_buf[8 + sz];
2057*c77a61a7Syz 
2058*c77a61a7Syz 			/*
2059*c77a61a7Syz 			 * video class 1.1 version add one element
2060*c77a61a7Syz 			 * (bmVideoStandards) to processing unit descriptor
2061*c77a61a7Syz 			 */
2062*c77a61a7Syz 			if (cvs_buf_len > (9 + sz)) {
2063*c77a61a7Syz 				unit->bmVideoStandards = cvs_buf[9 + sz];
2064*c77a61a7Syz 			}
2065*c77a61a7Syz 			list_insert_tail(&(usbvcp->usbvc_unit_list), unit);
2066*c77a61a7Syz 
2067*c77a61a7Syz 			break;
2068*c77a61a7Syz 		}
2069*c77a61a7Syz 		case VC_SELECTOR_UNIT:
2070*c77a61a7Syz 		{
2071*c77a61a7Syz 			uint8_t  pins;
2072*c77a61a7Syz 			usbvc_units_t *unit;
2073*c77a61a7Syz 
2074*c77a61a7Syz 			if (usbvc_chk_descr_len(4, 1, 0, cvs_data) !=
2075*c77a61a7Syz 			    USB_SUCCESS) {
2076*c77a61a7Syz 
2077*c77a61a7Syz 				break;
2078*c77a61a7Syz 			}
2079*c77a61a7Syz 			pins = cvs_buf[4];
2080*c77a61a7Syz 			if ((pins + 5) >= cvs_buf_len) {
2081*c77a61a7Syz 
2082*c77a61a7Syz 				break;
2083*c77a61a7Syz 			}
2084*c77a61a7Syz 			unit = (usbvc_units_t *)
2085*c77a61a7Syz 			    kmem_zalloc(sizeof (usbvc_units_t), KM_SLEEP);
2086*c77a61a7Syz 
2087*c77a61a7Syz 			unit->descr = (usbvc_unit_descr_t *)cvs_buf;
2088*c77a61a7Syz 
2089*c77a61a7Syz 			USB_DPRINTF_L3(PRINT_MASK_ATTA,
2090*c77a61a7Syz 			    usbvcp->usbvc_log_handle, "usbvc_parse_ctrl_if: "
2091*c77a61a7Syz 			    "unit type=%x", unit->descr->bDescriptorSubType);
2092*c77a61a7Syz 			if (pins > 0) {
2093*c77a61a7Syz 				unit->baSourceID = &cvs_buf[5];
2094*c77a61a7Syz 			}
2095*c77a61a7Syz 			unit->iSelector = cvs_buf[5 + pins];
2096*c77a61a7Syz 
2097*c77a61a7Syz 			list_insert_tail(&(usbvcp->usbvc_unit_list), unit);
2098*c77a61a7Syz 
2099*c77a61a7Syz 			break;
2100*c77a61a7Syz 		}
2101*c77a61a7Syz 		case VC_EXTENSION_UNIT:
2102*c77a61a7Syz 		{
2103*c77a61a7Syz 			uint8_t  pins, n;
2104*c77a61a7Syz 			usbvc_units_t *unit;
2105*c77a61a7Syz 
2106*c77a61a7Syz 			if (usbvc_chk_descr_len(21, 1, 0, cvs_data) !=
2107*c77a61a7Syz 			    USB_SUCCESS) {
2108*c77a61a7Syz 
2109*c77a61a7Syz 				break;
2110*c77a61a7Syz 			}
2111*c77a61a7Syz 			pins = cvs_buf[21];
2112*c77a61a7Syz 			if ((pins + 22) >= cvs_buf_len) {
2113*c77a61a7Syz 
2114*c77a61a7Syz 				break;
2115*c77a61a7Syz 			}
2116*c77a61a7Syz 
2117*c77a61a7Syz 			/* Size of bmControls */
2118*c77a61a7Syz 			n = cvs_buf[pins + 22];
2119*c77a61a7Syz 
2120*c77a61a7Syz 			if (usbvc_chk_descr_len(pins + 22, 1, 0, cvs_data) !=
2121*c77a61a7Syz 			    USB_SUCCESS) {
2122*c77a61a7Syz 
2123*c77a61a7Syz 				break;
2124*c77a61a7Syz 			}
2125*c77a61a7Syz 			if ((23 + pins + n) >= cvs_buf_len) {
2126*c77a61a7Syz 
2127*c77a61a7Syz 				break;
2128*c77a61a7Syz 			}
2129*c77a61a7Syz 			unit = (usbvc_units_t *)
2130*c77a61a7Syz 			    kmem_zalloc(sizeof (usbvc_units_t), KM_SLEEP);
2131*c77a61a7Syz 
2132*c77a61a7Syz 			unit->descr = (usbvc_unit_descr_t *)cvs_buf;
2133*c77a61a7Syz 
2134*c77a61a7Syz 			USB_DPRINTF_L3(PRINT_MASK_ATTA,
2135*c77a61a7Syz 			    usbvcp->usbvc_log_handle, "usbvc_parse_ctrl_if: "
2136*c77a61a7Syz 			    "unit type=%x", unit->descr->bDescriptorSubType);
2137*c77a61a7Syz 			if (pins != 0) {
2138*c77a61a7Syz 				unit->baSourceID = &cvs_buf[22];
2139*c77a61a7Syz 			}
2140*c77a61a7Syz 			unit->bControlSize = cvs_buf[22 + pins];
2141*c77a61a7Syz 
2142*c77a61a7Syz 			if (unit->bControlSize != 0) {
2143*c77a61a7Syz 				unit->bmControls = &cvs_buf[23 + pins];
2144*c77a61a7Syz 			}
2145*c77a61a7Syz 			unit->iExtension = cvs_buf[23 + pins + n];
2146*c77a61a7Syz 
2147*c77a61a7Syz 			list_insert_tail(&(usbvcp->usbvc_unit_list), unit);
2148*c77a61a7Syz 
2149*c77a61a7Syz 			break;
2150*c77a61a7Syz 		}
2151*c77a61a7Syz 		default:
2152*c77a61a7Syz 
2153*c77a61a7Syz 			break;
2154*c77a61a7Syz 		}
2155*c77a61a7Syz 	}
2156*c77a61a7Syz 
2157*c77a61a7Syz 	return (USB_SUCCESS);
2158*c77a61a7Syz }
2159*c77a61a7Syz 
2160*c77a61a7Syz 
2161*c77a61a7Syz /* Parse all the cvs descriptors in one stream interface. */
2162*c77a61a7Syz usbvc_stream_if_t *
2163*c77a61a7Syz usbvc_parse_stream_if(usbvc_state_t *usbvcp, int if_num)
2164*c77a61a7Syz {
2165*c77a61a7Syz 	usb_alt_if_data_t	*if_alt_data;
2166*c77a61a7Syz 	uint_t			i, j;
2167*c77a61a7Syz 	usbvc_stream_if_t	*strm_if;
2168*c77a61a7Syz 	uint16_t		pktsize;
2169*c77a61a7Syz 	uint8_t			ep_adr;
2170*c77a61a7Syz 
2171*c77a61a7Syz 	strm_if = (usbvc_stream_if_t *)kmem_zalloc(sizeof (usbvc_stream_if_t),
2172*c77a61a7Syz 	    KM_SLEEP);
2173*c77a61a7Syz 	strm_if->if_descr = &usbvcp->usbvc_reg->dev_curr_cfg->cfg_if[if_num];
2174*c77a61a7Syz 	if_alt_data = strm_if->if_descr->if_alt;
2175*c77a61a7Syz 	if (usbvc_parse_stream_header(usbvcp, strm_if) != USB_SUCCESS) {
2176*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2177*c77a61a7Syz 		    "usbvc_parse_stream_if: parse header fail");
2178*c77a61a7Syz 		kmem_free(strm_if, sizeof (usbvc_stream_if_t));
2179*c77a61a7Syz 
2180*c77a61a7Syz 		return (NULL);
2181*c77a61a7Syz 	}
2182*c77a61a7Syz 	if (usbvc_parse_format_groups(usbvcp, strm_if) != USB_SUCCESS) {
2183*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2184*c77a61a7Syz 		    "usbvc_parse_stream_if: parse groups fail");
2185*c77a61a7Syz 		kmem_free(strm_if, sizeof (usbvc_stream_if_t));
2186*c77a61a7Syz 
2187*c77a61a7Syz 		return (NULL);
2188*c77a61a7Syz 	}
2189*c77a61a7Syz 
2190*c77a61a7Syz 	/* Parse the alternate settings to find the maximum bandwidth. */
2191*c77a61a7Syz 	for (i = 0; i < strm_if->if_descr->if_n_alt; i++) {
2192*c77a61a7Syz 		if_alt_data = &strm_if->if_descr->if_alt[i];
2193*c77a61a7Syz 		for (j = 0; j < if_alt_data->altif_n_ep; j++) {
2194*c77a61a7Syz 			ep_adr =
2195*c77a61a7Syz 			    if_alt_data->altif_ep[j].ep_descr.bEndpointAddress;
2196*c77a61a7Syz 			if (strm_if->input_header != NULL &&
2197*c77a61a7Syz 			    ep_adr !=
2198*c77a61a7Syz 			    strm_if->input_header->descr->bEndpointAddress) {
2199*c77a61a7Syz 
2200*c77a61a7Syz 				continue;
2201*c77a61a7Syz 			}
2202*c77a61a7Syz 			if (strm_if->output_header != NULL &&
2203*c77a61a7Syz 			    ep_adr !=
2204*c77a61a7Syz 			    strm_if->output_header->descr->bEndpointAddress) {
2205*c77a61a7Syz 
2206*c77a61a7Syz 				continue;
2207*c77a61a7Syz 			}
2208*c77a61a7Syz 			pktsize =
2209*c77a61a7Syz 			    if_alt_data->altif_ep[j].ep_descr.wMaxPacketSize;
2210*c77a61a7Syz 			pktsize = HS_PKT_SIZE(pktsize);
2211*c77a61a7Syz 			if (pktsize > strm_if->max_isoc_payload) {
2212*c77a61a7Syz 				strm_if->max_isoc_payload = pktsize;
2213*c77a61a7Syz 			}
2214*c77a61a7Syz 		}
2215*c77a61a7Syz 	}
2216*c77a61a7Syz 
2217*c77a61a7Syz 	/* initialize MJPEC FID toggle */
2218*c77a61a7Syz 	strm_if->fid = 0xff;
2219*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2220*c77a61a7Syz 	    "usbvc_parse_stream_if: return. max_isoc_payload=%x",
2221*c77a61a7Syz 	    strm_if->max_isoc_payload);
2222*c77a61a7Syz 
2223*c77a61a7Syz 	return (strm_if);
2224*c77a61a7Syz }
2225*c77a61a7Syz 
2226*c77a61a7Syz 
2227*c77a61a7Syz /*
2228*c77a61a7Syz  * Parse all the stream interfaces asociated with the video control interface.
2229*c77a61a7Syz  * This driver will attach to a video control interface on the device,
2230*c77a61a7Syz  * there might be multiple video stream interfaces associated with one video
2231*c77a61a7Syz  * control interface.
2232*c77a61a7Syz  */
2233*c77a61a7Syz static int
2234*c77a61a7Syz usbvc_parse_stream_ifs(usbvc_state_t *usbvcp)
2235*c77a61a7Syz {
2236*c77a61a7Syz 	int			i, if_cnt, if_num;
2237*c77a61a7Syz 	usbvc_stream_if_t	*strm_if;
2238*c77a61a7Syz 
2239*c77a61a7Syz 	if_cnt = usbvcp->usbvc_vc_header->descr->bInCollection;
2240*c77a61a7Syz 	if (if_cnt == 0) {
2241*c77a61a7Syz 		ASSERT(list_is_empty(&usbvcp->usbvc_stream_list));
2242*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2243*c77a61a7Syz 		    "usbvc_parse_stream_ifs: no stream interfaces");
2244*c77a61a7Syz 
2245*c77a61a7Syz 		return (USB_SUCCESS);
2246*c77a61a7Syz 	}
2247*c77a61a7Syz 	for (i = 0; i < if_cnt; i++) {
2248*c77a61a7Syz 		if_num = usbvcp->usbvc_vc_header->baInterfaceNr[i];
2249*c77a61a7Syz 		strm_if = usbvc_parse_stream_if(usbvcp, if_num);
2250*c77a61a7Syz 		if (strm_if == NULL) {
2251*c77a61a7Syz 			USB_DPRINTF_L2(PRINT_MASK_ATTA,
2252*c77a61a7Syz 			    usbvcp->usbvc_log_handle, "usbvc_parse_stream_ifs:"
2253*c77a61a7Syz 			    " parse stream interface %d failed.", if_num);
2254*c77a61a7Syz 
2255*c77a61a7Syz 			return (USB_FAILURE);
2256*c77a61a7Syz 		}
2257*c77a61a7Syz 		/* video data buffers */
2258*c77a61a7Syz 		list_create(&(strm_if->buf_map.uv_buf_free),
2259*c77a61a7Syz 		    sizeof (usbvc_buf_t), offsetof(usbvc_buf_t, buf_node));
2260*c77a61a7Syz 		list_create(&(strm_if->buf_map.uv_buf_done),
2261*c77a61a7Syz 		    sizeof (usbvc_buf_t), offsetof(usbvc_buf_t, buf_node));
2262*c77a61a7Syz 		list_create(&(strm_if->buf_read.uv_buf_free),
2263*c77a61a7Syz 		    sizeof (usbvc_buf_t), offsetof(usbvc_buf_t, buf_node));
2264*c77a61a7Syz 		list_create(&(strm_if->buf_read.uv_buf_done),
2265*c77a61a7Syz 		    sizeof (usbvc_buf_t), offsetof(usbvc_buf_t, buf_node));
2266*c77a61a7Syz 
2267*c77a61a7Syz 		list_insert_tail(&(usbvcp->usbvc_stream_list), strm_if);
2268*c77a61a7Syz 	}
2269*c77a61a7Syz 
2270*c77a61a7Syz 	/* Make the first stream interface as the default one. */
2271*c77a61a7Syz 	usbvcp->usbvc_curr_strm =
2272*c77a61a7Syz 	    (usbvc_stream_if_t *)list_head(&usbvcp->usbvc_stream_list);
2273*c77a61a7Syz 
2274*c77a61a7Syz 	return (USB_SUCCESS);
2275*c77a61a7Syz }
2276*c77a61a7Syz 
2277*c77a61a7Syz 
2278*c77a61a7Syz /*
2279*c77a61a7Syz  * Parse colorspace descriptor and still image descriptor of a format group.
2280*c77a61a7Syz  * There is only one colorspace or still image descriptor in one format group.
2281*c77a61a7Syz  */
2282*c77a61a7Syz static void
2283*c77a61a7Syz usbvc_parse_color_still(usbvc_state_t *usbvcp, usbvc_format_group_t *fmtgrp,
2284*c77a61a7Syz 	usb_cvs_data_t *cvs_data, uint_t cvs_num, uint_t altif_n_cvs)
2285*c77a61a7Syz {
2286*c77a61a7Syz 	uint8_t		frame_cnt;
2287*c77a61a7Syz 	uint_t		last_frame, i;
2288*c77a61a7Syz 	uchar_t		*cvs_buf;
2289*c77a61a7Syz 	uint_t			cvs_buf_len;
2290*c77a61a7Syz 
2291*c77a61a7Syz 	frame_cnt = fmtgrp->format->bNumFrameDescriptors;
2292*c77a61a7Syz 	last_frame = frame_cnt + cvs_num;
2293*c77a61a7Syz 
2294*c77a61a7Syz 	/*
2295*c77a61a7Syz 	 * Find the still image descr and color format descr if there are any.
2296*c77a61a7Syz 	 * UVC Spec: only one still image and one color descr is allowed in
2297*c77a61a7Syz 	 * one format group.
2298*c77a61a7Syz 	 */
2299*c77a61a7Syz 	for (i = 1; i <= 2; i++) {
2300*c77a61a7Syz 		if ((last_frame + i) >= altif_n_cvs) {
2301*c77a61a7Syz 
2302*c77a61a7Syz 			break;
2303*c77a61a7Syz 		}
2304*c77a61a7Syz 		cvs_buf = cvs_data[last_frame + i].cvs_buf;
2305*c77a61a7Syz 		cvs_buf_len = cvs_data[last_frame + i].cvs_buf_len;
2306*c77a61a7Syz 
2307*c77a61a7Syz 		if (cvs_buf[2] == VS_STILL_IMAGE_FRAME) {
2308*c77a61a7Syz 			uint8_t m, n, off;
2309*c77a61a7Syz 			usbvc_still_image_frame_t *st;
2310*c77a61a7Syz 
2311*c77a61a7Syz 			if (usbvc_chk_descr_len(4, 4, 0, cvs_data) !=
2312*c77a61a7Syz 			    USB_SUCCESS) {
2313*c77a61a7Syz 
2314*c77a61a7Syz 				continue;
2315*c77a61a7Syz 			}
2316*c77a61a7Syz 
2317*c77a61a7Syz 			/* Number of Image Size patterns of this format */
2318*c77a61a7Syz 			n = cvs_buf[4];
2319*c77a61a7Syz 
2320*c77a61a7Syz 			/* offset of bNumCompressionPattern */
2321*c77a61a7Syz 			off = 9 + 4 * n -4;
2322*c77a61a7Syz 
2323*c77a61a7Syz 			if (off >= cvs_buf_len) {
2324*c77a61a7Syz 
2325*c77a61a7Syz 				continue;
2326*c77a61a7Syz 			}
2327*c77a61a7Syz 
2328*c77a61a7Syz 			/* Number of compression pattern of this format */
2329*c77a61a7Syz 			m = cvs_buf[off];
2330*c77a61a7Syz 
2331*c77a61a7Syz 			if (usbvc_chk_descr_len(m, 1, 0, cvs_data) !=
2332*c77a61a7Syz 			    USB_SUCCESS) {
2333*c77a61a7Syz 
2334*c77a61a7Syz 				continue;
2335*c77a61a7Syz 			}
2336*c77a61a7Syz 			fmtgrp->still = (usbvc_still_image_frame_t *)
2337*c77a61a7Syz 			    kmem_zalloc(sizeof (usbvc_still_image_frame_t),
2338*c77a61a7Syz 			    KM_SLEEP);
2339*c77a61a7Syz 			st = fmtgrp->still;
2340*c77a61a7Syz 			st->descr = (usbvc_still_image_frame_descr_t *)cvs_buf;
2341*c77a61a7Syz 			n = st->descr->bNumImageSizePatterns;
2342*c77a61a7Syz 			if (n > 0) {
2343*c77a61a7Syz 				st->width_height =
2344*c77a61a7Syz 				    (width_height_t *)&cvs_buf[5];
2345*c77a61a7Syz 			}
2346*c77a61a7Syz 			st->bNumCompressionPattern = cvs_buf[off];
2347*c77a61a7Syz 			if (cvs_buf[off] > 0) {
2348*c77a61a7Syz 				st->bCompression = &cvs_buf[off + 1];
2349*c77a61a7Syz 			}
2350*c77a61a7Syz 		}
2351*c77a61a7Syz 		if (cvs_buf[2] == VS_COLORFORMAT) {
2352*c77a61a7Syz 			fmtgrp->color = (usbvc_color_matching_descr_t *)cvs_buf;
2353*c77a61a7Syz 			fmtgrp->v4l2_color = usbvc_v4l2_colorspace(
2354*c77a61a7Syz 			    fmtgrp->color->bColorPrimaries);
2355*c77a61a7Syz 		}
2356*c77a61a7Syz 	}
2357*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2358*c77a61a7Syz 	    "usbvc_parse_color_still: still=%p, color=%p",
2359*c77a61a7Syz 	    fmtgrp->still, fmtgrp->color);
2360*c77a61a7Syz }
2361*c77a61a7Syz 
2362*c77a61a7Syz 
2363*c77a61a7Syz /*
2364*c77a61a7Syz  * Parse frame descriptors of a format group. There might be multi frame
2365*c77a61a7Syz  * descriptors in one format group.
2366*c77a61a7Syz  */
2367*c77a61a7Syz static void
2368*c77a61a7Syz usbvc_parse_frames(usbvc_state_t *usbvcp, usbvc_format_group_t *fmtgrp,
2369*c77a61a7Syz 	usb_cvs_data_t *cvs_data, uint_t cvs_num, uint_t altif_n_cvs)
2370*c77a61a7Syz {
2371*c77a61a7Syz 	uint_t		last_frame;
2372*c77a61a7Syz 	usbvc_frames_t	*frm;
2373*c77a61a7Syz 	usb_cvs_data_t		*cvs;
2374*c77a61a7Syz 	uchar_t		*cvs_buf;
2375*c77a61a7Syz 	uint_t			cvs_buf_len;
2376*c77a61a7Syz 	uint8_t		i;
2377*c77a61a7Syz 	uint8_t		frame_cnt = fmtgrp->format->bNumFrameDescriptors;
2378*c77a61a7Syz 
2379*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2380*c77a61a7Syz 	    "usbvc_parse_format_group: frame_cnt=%d", frame_cnt);
2381*c77a61a7Syz 
2382*c77a61a7Syz 	if (frame_cnt == 0) {
2383*c77a61a7Syz 		fmtgrp->frames = NULL;
2384*c77a61a7Syz 
2385*c77a61a7Syz 		return;
2386*c77a61a7Syz 	}
2387*c77a61a7Syz 
2388*c77a61a7Syz 	/* All these mem allocated will be freed in cleanup() */
2389*c77a61a7Syz 	fmtgrp->frames = (usbvc_frames_t *)
2390*c77a61a7Syz 	    kmem_zalloc(sizeof (usbvc_frames_t) * frame_cnt, KM_SLEEP);
2391*c77a61a7Syz 
2392*c77a61a7Syz 	last_frame = frame_cnt + cvs_num;
2393*c77a61a7Syz 	cvs_num++;
2394*c77a61a7Syz 	i = 0;
2395*c77a61a7Syz 
2396*c77a61a7Syz 	/*
2397*c77a61a7Syz 	 * Traverse from the format decr's first frame decr to the the last
2398*c77a61a7Syz 	 * frame descr.
2399*c77a61a7Syz 	 */
2400*c77a61a7Syz 	for (; cvs_num <= last_frame; cvs_num++) {
2401*c77a61a7Syz 		USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2402*c77a61a7Syz 		    "usbvc_parse_frames: cvs_num=%d, i=%d", cvs_num, i);
2403*c77a61a7Syz 		if (cvs_num >= altif_n_cvs) {
2404*c77a61a7Syz 			USB_DPRINTF_L3(PRINT_MASK_ATTA,
2405*c77a61a7Syz 			    usbvcp->usbvc_log_handle,
2406*c77a61a7Syz 			    "usbvc_parse_frames: less frames than "
2407*c77a61a7Syz 			    "expected, cvs_num=%d, i=%d", cvs_num, i);
2408*c77a61a7Syz 
2409*c77a61a7Syz 			break;
2410*c77a61a7Syz 		}
2411*c77a61a7Syz 		cvs = &cvs_data[cvs_num];
2412*c77a61a7Syz 		cvs_buf = cvs->cvs_buf;
2413*c77a61a7Syz 		cvs_buf_len = cvs->cvs_buf_len;
2414*c77a61a7Syz 		if (cvs_buf_len < USBVC_FRAME_LEN_MIN) {
2415*c77a61a7Syz 			i++;
2416*c77a61a7Syz 
2417*c77a61a7Syz 			continue;
2418*c77a61a7Syz 		}
2419*c77a61a7Syz 		frm = &fmtgrp->frames[i];
2420*c77a61a7Syz 		frm->descr = (usbvc_frame_descr_t *)cvs->cvs_buf;
2421*c77a61a7Syz 
2422*c77a61a7Syz 		/* Descriptor for discrete frame interval */
2423*c77a61a7Syz 		if (frm->descr->bFrameIntervalType > 0) {
2424*c77a61a7Syz 			if (usbvc_chk_descr_len(25, 4, 0, cvs) != USB_SUCCESS) {
2425*c77a61a7Syz 				frm->descr = NULL;
2426*c77a61a7Syz 				i++;
2427*c77a61a7Syz 
2428*c77a61a7Syz 				continue;
2429*c77a61a7Syz 			}
2430*c77a61a7Syz 
2431*c77a61a7Syz 			frm->dwFrameInterval = (uint32_t *)&cvs_buf[26];
2432*c77a61a7Syz 		} else {	/* Continuous interval */
2433*c77a61a7Syz 			if (cvs_buf_len < USBVC_FRAME_LEN_CON) {
2434*c77a61a7Syz 				frm->descr = NULL;
2435*c77a61a7Syz 				i++;
2436*c77a61a7Syz 
2437*c77a61a7Syz 				continue;
2438*c77a61a7Syz 			}
2439*c77a61a7Syz 
2440*c77a61a7Syz 			/* Continuous frame intervals */
2441*c77a61a7Syz 			LE_TO_UINT32(cvs_buf, 26, frm->dwMinFrameInterval);
2442*c77a61a7Syz 			LE_TO_UINT32(cvs_buf, 30, frm->dwMaxFrameInterval);
2443*c77a61a7Syz 			LE_TO_UINT32(cvs_buf, 34, frm->dwFrameIntervalStep);
2444*c77a61a7Syz 		}
2445*c77a61a7Syz 
2446*c77a61a7Syz 		i++;
2447*c77a61a7Syz 	}
2448*c77a61a7Syz 	fmtgrp->frame_cnt = i;
2449*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2450*c77a61a7Syz 	    "usbvc_parse_frames: %d frames are actually parsed",
2451*c77a61a7Syz 	    fmtgrp->frame_cnt);
2452*c77a61a7Syz }
2453*c77a61a7Syz 
2454*c77a61a7Syz 
2455*c77a61a7Syz /* Parse one of the format groups in a stream interface */
2456*c77a61a7Syz static int
2457*c77a61a7Syz usbvc_parse_format_group(usbvc_state_t *usbvcp, usbvc_format_group_t *fmtgrp,
2458*c77a61a7Syz 	usb_cvs_data_t *cvs_data, uint_t cvs_num, uint_t altif_n_cvs)
2459*c77a61a7Syz {
2460*c77a61a7Syz 	usbvc_format_descr_t *fmt;
2461*c77a61a7Syz 
2462*c77a61a7Syz 	fmt = fmtgrp->format;
2463*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2464*c77a61a7Syz 		"usbvc_parse_format_group: frame_cnt=%d, cvs_num=%d",
2465*c77a61a7Syz 		fmt->bNumFrameDescriptors, cvs_num);
2466*c77a61a7Syz 
2467*c77a61a7Syz 	switch (fmt->bDescriptorSubType) {
2468*c77a61a7Syz 	case VS_FORMAT_UNCOMPRESSED:
2469*c77a61a7Syz 		usbvc_parse_color_still(usbvcp, fmtgrp, cvs_data, cvs_num,
2470*c77a61a7Syz 		    altif_n_cvs);
2471*c77a61a7Syz 		usbvc_parse_frames(usbvcp, fmtgrp, cvs_data, cvs_num,
2472*c77a61a7Syz 		    altif_n_cvs);
2473*c77a61a7Syz 		fmtgrp->v4l2_bpp = 16;
2474*c77a61a7Syz 		fmtgrp->v4l2_pixelformat = usbvc_v4l2_guid2fcc(
2475*c77a61a7Syz 		    (uint8_t *)&fmt->fmt.uncompressed.guidFormat);
2476*c77a61a7Syz 
2477*c77a61a7Syz 		break;
2478*c77a61a7Syz 	case VS_FORMAT_MJPEG:
2479*c77a61a7Syz 		usbvc_parse_color_still(usbvcp, fmtgrp, cvs_data, cvs_num,
2480*c77a61a7Syz 		    altif_n_cvs);
2481*c77a61a7Syz 		usbvc_parse_frames(usbvcp, fmtgrp, cvs_data, cvs_num,
2482*c77a61a7Syz 		    altif_n_cvs);
2483*c77a61a7Syz 		fmtgrp->v4l2_bpp = 0;
2484*c77a61a7Syz 		fmtgrp->v4l2_pixelformat = V4L2_PIX_FMT_MJPEG;
2485*c77a61a7Syz 
2486*c77a61a7Syz 		break;
2487*c77a61a7Syz 	case VS_FORMAT_MPEG2TS:
2488*c77a61a7Syz 	case VS_FORMAT_DV:
2489*c77a61a7Syz 	case VS_FORMAT_FRAME_BASED:
2490*c77a61a7Syz 	case VS_FORMAT_STREAM_BASED:
2491*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2492*c77a61a7Syz 		    "usbvc_parse_format_group: format not supported yet.");
2493*c77a61a7Syz 
2494*c77a61a7Syz 		return (USB_FAILURE);
2495*c77a61a7Syz 	default:
2496*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2497*c77a61a7Syz 		    "usbvc_parse_format_group: unknown format.");
2498*c77a61a7Syz 
2499*c77a61a7Syz 		return (USB_FAILURE);
2500*c77a61a7Syz 	}
2501*c77a61a7Syz 
2502*c77a61a7Syz 	return (USB_SUCCESS);
2503*c77a61a7Syz }
2504*c77a61a7Syz 
2505*c77a61a7Syz 
2506*c77a61a7Syz /* Parse the descriptors belong to one format */
2507*c77a61a7Syz static int
2508*c77a61a7Syz usbvc_parse_format_groups(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
2509*c77a61a7Syz {
2510*c77a61a7Syz 	usb_alt_if_data_t	*if_alt_data;
2511*c77a61a7Syz 	usb_cvs_data_t		*cvs_data;
2512*c77a61a7Syz 	uint8_t			fmtgrp_num, fmtgrp_cnt;
2513*c77a61a7Syz 	uchar_t			*cvs_buf;
2514*c77a61a7Syz 	uint_t			cvs_num = 0;
2515*c77a61a7Syz 	usbvc_format_group_t	*fmtgrp;
2516*c77a61a7Syz 
2517*c77a61a7Syz 	fmtgrp_cnt = 0;
2518*c77a61a7Syz 	/*
2519*c77a61a7Syz 	 * bNumFormats indicates the number of formats in this stream
2520*c77a61a7Syz 	 * interface. On some devices, we see this number is larger than
2521*c77a61a7Syz 	 * the truth.
2522*c77a61a7Syz 	 */
2523*c77a61a7Syz 	if (strm_if->input_header) {
2524*c77a61a7Syz 		fmtgrp_cnt = strm_if->input_header->descr->bNumFormats;
2525*c77a61a7Syz 	} else if (strm_if->output_header) {
2526*c77a61a7Syz 		fmtgrp_cnt = strm_if->output_header->descr->bNumFormats;
2527*c77a61a7Syz 	}
2528*c77a61a7Syz 	if (!fmtgrp_cnt) {
2529*c77a61a7Syz 
2530*c77a61a7Syz 		return (USB_FAILURE);
2531*c77a61a7Syz 	}
2532*c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2533*c77a61a7Syz 	    "usbvc_parse_format_groups: fmtgrp_cnt=%d", fmtgrp_cnt);
2534*c77a61a7Syz 
2535*c77a61a7Syz 	fmtgrp = (usbvc_format_group_t *)
2536*c77a61a7Syz 	    kmem_zalloc(sizeof (usbvc_format_group_t) * fmtgrp_cnt, KM_SLEEP);
2537*c77a61a7Syz 
2538*c77a61a7Syz 	if_alt_data = strm_if->if_descr->if_alt;
2539*c77a61a7Syz 	cvs_data = if_alt_data->altif_cvs;
2540*c77a61a7Syz 	for (fmtgrp_num = 0; fmtgrp_num < fmtgrp_cnt; fmtgrp_num++) {
2541*c77a61a7Syz 
2542*c77a61a7Syz 		/* find the next format descriptor */
2543*c77a61a7Syz 		for (; cvs_num < if_alt_data->altif_n_cvs; cvs_num++) {
2544*c77a61a7Syz 			cvs_buf = cvs_data[cvs_num].cvs_buf;
2545*c77a61a7Syz 			switch (cvs_buf[2]) {
2546*c77a61a7Syz 			case VS_FORMAT_UNCOMPRESSED:
2547*c77a61a7Syz 			case VS_FORMAT_MJPEG:
2548*c77a61a7Syz 			case VS_FORMAT_MPEG2TS:
2549*c77a61a7Syz 			case VS_FORMAT_DV:
2550*c77a61a7Syz 			case VS_FORMAT_FRAME_BASED:
2551*c77a61a7Syz 			case VS_FORMAT_STREAM_BASED:
2552*c77a61a7Syz 				fmtgrp[fmtgrp_num].format =
2553*c77a61a7Syz 				    (usbvc_format_descr_t *)cvs_buf;
2554*c77a61a7Syz 
2555*c77a61a7Syz 				break;
2556*c77a61a7Syz 			default:
2557*c77a61a7Syz 
2558*c77a61a7Syz 				break;
2559*c77a61a7Syz 			}
2560*c77a61a7Syz 			if (fmtgrp[fmtgrp_num].format) {
2561*c77a61a7Syz 
2562*c77a61a7Syz 				break;
2563*c77a61a7Syz 			}
2564*c77a61a7Syz 		}
2565*c77a61a7Syz 
2566*c77a61a7Syz 		/* If can't find the next format, then break. */
2567*c77a61a7Syz 		if (!(fmtgrp[fmtgrp_num].format)) {
2568*c77a61a7Syz 			strm_if->fmtgrp_cnt = fmtgrp_num;
2569*c77a61a7Syz 			USB_DPRINTF_L2(PRINT_MASK_ATTA,
2570*c77a61a7Syz 			    usbvcp->usbvc_log_handle,
2571*c77a61a7Syz 			    "usbvc_parse_format_groups: acctually %d formats"
2572*c77a61a7Syz 			    " parsed", fmtgrp_num);
2573*c77a61a7Syz 
2574*c77a61a7Syz 			break;
2575*c77a61a7Syz 		}
2576*c77a61a7Syz 
2577*c77a61a7Syz 		/*
2578*c77a61a7Syz 		 * Now cvs_data[cvs_num].cvs_buf is format descriptor,
2579*c77a61a7Syz 		 * usbvc_parse_format_group will then parse the frame
2580*c77a61a7Syz 		 * descriptors following this format descriptor.
2581*c77a61a7Syz 		 */
2582*c77a61a7Syz 		(void) usbvc_parse_format_group(usbvcp, &fmtgrp[fmtgrp_num],
2583*c77a61a7Syz 		    cvs_data, cvs_num, if_alt_data->altif_n_cvs);
2584*c77a61a7Syz 
2585*c77a61a7Syz 		/*
2586*c77a61a7Syz 		 * cvs_data[cvs_num].cvs_buf is the one was parsed, so plus
2587*c77a61a7Syz 		 * 1 to move to the next format group
2588*c77a61a7Syz 		 */
2589*c77a61a7Syz 		cvs_num++;
2590*c77a61a7Syz 	}
2591*c77a61a7Syz 
2592*c77a61a7Syz 	/*
2593*c77a61a7Syz 	 * If can't find any formats, then free all allocated
2594*c77a61a7Syz 	 * usbvc_format_group_t, return failure.
2595*c77a61a7Syz 	 */
2596*c77a61a7Syz 	if (!(fmtgrp[0].format)) {
2597*c77a61a7Syz 		kmem_free(fmtgrp, sizeof (usbvc_format_group_t) * fmtgrp_cnt);
2598*c77a61a7Syz 		strm_if->format_group = NULL;
2599*c77a61a7Syz 
2600*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2601*c77a61a7Syz 		    "usbvc_parse_format_groups: can't find any formats");
2602*c77a61a7Syz 
2603*c77a61a7Syz 		return (USB_FAILURE);
2604*c77a61a7Syz 	}
2605*c77a61a7Syz 	strm_if->format_group = fmtgrp;
2606*c77a61a7Syz 	USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2607*c77a61a7Syz 	    "usbvc_parse_format_groups: %d format groups parsed", fmtgrp_num);
2608*c77a61a7Syz 
2609*c77a61a7Syz 	return (USB_SUCCESS);
2610*c77a61a7Syz }
2611*c77a61a7Syz 
2612*c77a61a7Syz 
2613*c77a61a7Syz /*
2614*c77a61a7Syz  * Parse the input/output header in one stream interface.
2615*c77a61a7Syz  * UVC Spec: there must be one and only one header in one stream interface.
2616*c77a61a7Syz  */
2617*c77a61a7Syz int
2618*c77a61a7Syz usbvc_parse_stream_header(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
2619*c77a61a7Syz {
2620*c77a61a7Syz 	usb_alt_if_data_t	*if_alt_data;
2621*c77a61a7Syz 	usb_cvs_data_t		*cvs_data;
2622*c77a61a7Syz 	int			cvs_num;
2623*c77a61a7Syz 	uchar_t			*cvs_buf;
2624*c77a61a7Syz 	usbvc_input_header_t	*in_hdr;
2625*c77a61a7Syz 	usbvc_output_header_t	*out_hdr;
2626*c77a61a7Syz 
2627*c77a61a7Syz 	if_alt_data = strm_if->if_descr->if_alt;
2628*c77a61a7Syz 	cvs_data = if_alt_data->altif_cvs;
2629*c77a61a7Syz 	for (cvs_num = 0; cvs_num < if_alt_data->altif_n_cvs; cvs_num++) {
2630*c77a61a7Syz 	    cvs_buf = cvs_data[cvs_num].cvs_buf;
2631*c77a61a7Syz 	    USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2632*c77a61a7Syz 		    "usbvc_parse_stream_header: cvs_num= %d", cvs_num);
2633*c77a61a7Syz 
2634*c77a61a7Syz 		/*
2635*c77a61a7Syz 		 * parse interface cvs descriptors here; by checking
2636*c77a61a7Syz 		 * bDescriptorType (cvs_buf[1])
2637*c77a61a7Syz 		 */
2638*c77a61a7Syz 		if (cvs_buf[1] != CS_INTERFACE) {
2639*c77a61a7Syz 
2640*c77a61a7Syz 			continue;
2641*c77a61a7Syz 		}
2642*c77a61a7Syz 
2643*c77a61a7Syz 		if (cvs_buf[2] == VS_INPUT_HEADER) {
2644*c77a61a7Syz 			if (usbvc_chk_descr_len(3, 0, 12, cvs_data) !=
2645*c77a61a7Syz 			    USB_SUCCESS) {
2646*c77a61a7Syz 
2647*c77a61a7Syz 				continue;
2648*c77a61a7Syz 			}
2649*c77a61a7Syz 
2650*c77a61a7Syz 			strm_if->input_header =
2651*c77a61a7Syz 			    (usbvc_input_header_t *)
2652*c77a61a7Syz 			    kmem_zalloc(sizeof (usbvc_input_header_t),
2653*c77a61a7Syz 			    KM_SLEEP);
2654*c77a61a7Syz 			in_hdr = strm_if->input_header;
2655*c77a61a7Syz 			in_hdr->descr = (usbvc_input_header_descr_t *)cvs_buf;
2656*c77a61a7Syz 			if (in_hdr->descr->bNumFormats > 0) {
2657*c77a61a7Syz 				in_hdr->bmaControls = &cvs_buf[13];
2658*c77a61a7Syz 			}
2659*c77a61a7Syz 
2660*c77a61a7Syz 			return (USB_SUCCESS);
2661*c77a61a7Syz 		} else if (cvs_buf[2] == VS_OUTPUT_HEADER) {
2662*c77a61a7Syz 			if (usbvc_chk_descr_len(3, 0, 8, cvs_data) !=
2663*c77a61a7Syz 			    USB_SUCCESS) {
2664*c77a61a7Syz 
2665*c77a61a7Syz 				continue;
2666*c77a61a7Syz 			}
2667*c77a61a7Syz 			strm_if->output_header =
2668*c77a61a7Syz 			    (usbvc_output_header_t *)
2669*c77a61a7Syz 			    kmem_zalloc(sizeof (usbvc_output_header_t),
2670*c77a61a7Syz 			    KM_SLEEP);
2671*c77a61a7Syz 			out_hdr = strm_if->output_header;
2672*c77a61a7Syz 			out_hdr->descr =
2673*c77a61a7Syz 			    (usbvc_output_header_descr_t *)cvs_buf;
2674*c77a61a7Syz 			if (out_hdr->descr->bNumFormats > 0) {
2675*c77a61a7Syz 				out_hdr->bmaControls = &cvs_buf[13];
2676*c77a61a7Syz 			}
2677*c77a61a7Syz 
2678*c77a61a7Syz 			return (USB_SUCCESS);
2679*c77a61a7Syz 		} else {
2680*c77a61a7Syz 
2681*c77a61a7Syz 			continue;
2682*c77a61a7Syz 		}
2683*c77a61a7Syz 	}
2684*c77a61a7Syz 	/* Didn't find one header descriptor. */
2685*c77a61a7Syz 	USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2686*c77a61a7Syz 	    "usbvc_parse_stream_header: FAIL");
2687*c77a61a7Syz 
2688*c77a61a7Syz 	return (USB_FAILURE);
2689*c77a61a7Syz }
2690*c77a61a7Syz 
2691*c77a61a7Syz /* read I/O functions */
2692*c77a61a7Syz 
2693*c77a61a7Syz /* Allocate bufs for read I/O method */
2694*c77a61a7Syz static int
2695*c77a61a7Syz usbvc_alloc_read_bufs(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
2696*c77a61a7Syz {
2697*c77a61a7Syz 	usbvc_buf_t	*buf;
2698*c77a61a7Syz 	uchar_t		*data;
2699*c77a61a7Syz 	int		i;
2700*c77a61a7Syz 	uint32_t	len;
2701*c77a61a7Syz 
2702*c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
2703*c77a61a7Syz 
2704*c77a61a7Syz 	LE_TO_UINT32(strm_if->ctrl_pc.dwMaxVideoFrameSize, 0, len);
2705*c77a61a7Syz 	if (!len) {
2706*c77a61a7Syz 
2707*c77a61a7Syz 		return (USB_FAILURE);
2708*c77a61a7Syz 	}
2709*c77a61a7Syz 	for (i = 0; i < USBVC_READ_BUF_NUM; i++) {
2710*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
2711*c77a61a7Syz 		buf = (usbvc_buf_t *)kmem_zalloc(sizeof (usbvc_buf_t),
2712*c77a61a7Syz 		    KM_SLEEP);
2713*c77a61a7Syz 		data = (uchar_t *)kmem_zalloc(len, KM_SLEEP);
2714*c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
2715*c77a61a7Syz 		buf->data = data;
2716*c77a61a7Syz 		buf->len = len;
2717*c77a61a7Syz 		list_insert_tail(&(strm_if->buf_read.uv_buf_free), buf);
2718*c77a61a7Syz 	}
2719*c77a61a7Syz 	strm_if->buf_read.buf_cnt = USBVC_READ_BUF_NUM;
2720*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
2721*c77a61a7Syz 	    "read_bufs: %d bufs allocated", strm_if->buf_read.buf_cnt);
2722*c77a61a7Syz 
2723*c77a61a7Syz 	return (USB_SUCCESS);
2724*c77a61a7Syz }
2725*c77a61a7Syz 
2726*c77a61a7Syz 
2727*c77a61a7Syz /* Read a done buf, copy data to bp. This function is for read I/O method */
2728*c77a61a7Syz static int
2729*c77a61a7Syz usbvc_read_buf(usbvc_state_t *usbvcp, struct buf *bp)
2730*c77a61a7Syz {
2731*c77a61a7Syz 	usbvc_buf_t	*buf;
2732*c77a61a7Syz 
2733*c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
2734*c77a61a7Syz 
2735*c77a61a7Syz 	/* read a buf from full list and then put it to free list */
2736*c77a61a7Syz 	buf = list_head(&usbvcp->usbvc_curr_strm->buf_read.uv_buf_done);
2737*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
2738*c77a61a7Syz 	    "usbvc_read_buf: buf=%p, buf->filled=%d, bfu->len=%d,"
2739*c77a61a7Syz 	    " bp->b_bcount=%d, bp->b_resid=%d",
2740*c77a61a7Syz 	    buf, buf->filled, buf->len, bp->b_bcount, bp->b_resid);
2741*c77a61a7Syz 
2742*c77a61a7Syz 	list_remove(&usbvcp->usbvc_curr_strm->buf_read.uv_buf_done, buf);
2743*c77a61a7Syz 	bcopy(buf->data, bp->b_un.b_addr, buf->filled);
2744*c77a61a7Syz 	bp->b_private = NULL;
2745*c77a61a7Syz 	bp->b_resid = bp->b_bcount - buf->filled;
2746*c77a61a7Syz 	list_insert_tail(&usbvcp->usbvc_curr_strm->buf_read.uv_buf_free, buf);
2747*c77a61a7Syz 
2748*c77a61a7Syz 	return (USB_SUCCESS);
2749*c77a61a7Syz }
2750*c77a61a7Syz 
2751*c77a61a7Syz 
2752*c77a61a7Syz /* Free one buf which is for read/write IO style */
2753*c77a61a7Syz static void
2754*c77a61a7Syz usbvc_free_read_buf(usbvc_buf_t *buf)
2755*c77a61a7Syz {
2756*c77a61a7Syz 	if (buf != NULL) {
2757*c77a61a7Syz 		if (buf->data) {
2758*c77a61a7Syz 			kmem_free(buf->data, buf->len);
2759*c77a61a7Syz 		}
2760*c77a61a7Syz 		kmem_free(buf, sizeof (usbvc_buf_t));
2761*c77a61a7Syz 	}
2762*c77a61a7Syz }
2763*c77a61a7Syz 
2764*c77a61a7Syz 
2765*c77a61a7Syz /* Free all bufs which are for read/write IO style */
2766*c77a61a7Syz static void
2767*c77a61a7Syz usbvc_free_read_bufs(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
2768*c77a61a7Syz {
2769*c77a61a7Syz 	usbvc_buf_t	*buf;
2770*c77a61a7Syz 
2771*c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
2772*c77a61a7Syz 
2773*c77a61a7Syz 	if (!strm_if) {
2774*c77a61a7Syz 
2775*c77a61a7Syz 		return;
2776*c77a61a7Syz 	}
2777*c77a61a7Syz 	buf = strm_if->buf_read.buf_filling;
2778*c77a61a7Syz 	usbvc_free_read_buf(buf);
2779*c77a61a7Syz 	strm_if->buf_read.buf_filling = NULL;
2780*c77a61a7Syz 
2781*c77a61a7Syz 	while (!list_is_empty(&strm_if->buf_read.uv_buf_free)) {
2782*c77a61a7Syz 		buf = list_head(&strm_if->buf_read.uv_buf_free);
2783*c77a61a7Syz 		if (buf != NULL) {
2784*c77a61a7Syz 			list_remove(&(strm_if->buf_read.uv_buf_free), buf);
2785*c77a61a7Syz 			usbvc_free_read_buf(buf);
2786*c77a61a7Syz 		}
2787*c77a61a7Syz 	}
2788*c77a61a7Syz 	while (!list_is_empty(&strm_if->buf_read.uv_buf_done)) {
2789*c77a61a7Syz 		buf = list_head(&strm_if->buf_read.uv_buf_done);
2790*c77a61a7Syz 		if (buf != NULL) {
2791*c77a61a7Syz 			list_remove(&(strm_if->buf_read.uv_buf_done), buf);
2792*c77a61a7Syz 			usbvc_free_read_buf(buf);
2793*c77a61a7Syz 		}
2794*c77a61a7Syz 	}
2795*c77a61a7Syz 	strm_if->buf_read.buf_cnt = 0;
2796*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
2797*c77a61a7Syz 	    "usbvc_free_read_bufs: return");
2798*c77a61a7Syz }
2799*c77a61a7Syz 
2800*c77a61a7Syz 
2801*c77a61a7Syz /*
2802*c77a61a7Syz  * Allocate bufs for mapped I/O , return the number of allocated bufs
2803*c77a61a7Syz  * if success, return 0 if fail.
2804*c77a61a7Syz  */
2805*c77a61a7Syz int
2806*c77a61a7Syz usbvc_alloc_map_bufs(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if,
2807*c77a61a7Syz 	int buf_cnt, int buf_len)
2808*c77a61a7Syz {
2809*c77a61a7Syz 	int		i = 0;
2810*c77a61a7Syz 	usbvc_buf_t	*bufs;
2811*c77a61a7Syz 
2812*c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
2813*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
2814*c77a61a7Syz 	    "usbvc_alloc_map_bufs: bufcnt=%d, buflen=%d", buf_cnt, buf_len);
2815*c77a61a7Syz 	if (buf_len <= 0 || buf_cnt <= 0) {
2816*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
2817*c77a61a7Syz 		    "usbvc_alloc_map_bufs: len<=0, cnt<=0");
2818*c77a61a7Syz 
2819*c77a61a7Syz 		return (0);
2820*c77a61a7Syz 	}
2821*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
2822*c77a61a7Syz 
2823*c77a61a7Syz 	bufs = (usbvc_buf_t *) kmem_zalloc(sizeof (usbvc_buf_t) * buf_cnt,
2824*c77a61a7Syz 	    KM_SLEEP);
2825*c77a61a7Syz 
2826*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
2827*c77a61a7Syz 	strm_if->buf_map.buf_head = bufs;
2828*c77a61a7Syz 	buf_len = ptob(btopr(buf_len));
2829*c77a61a7Syz 
2830*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
2831*c77a61a7Syz 	bufs[0].data = ddi_umem_alloc(buf_len * buf_cnt, DDI_UMEM_SLEEP,
2832*c77a61a7Syz 				    &bufs[0].umem_cookie);
2833*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
2834*c77a61a7Syz 
2835*c77a61a7Syz 	for (i = 0; i < buf_cnt; i++) {
2836*c77a61a7Syz 		bufs[i].len = buf_len;
2837*c77a61a7Syz 		bufs[i].data = bufs[0].data + (buf_len * i);
2838*c77a61a7Syz 		bufs[i].umem_cookie = bufs[0].umem_cookie;
2839*c77a61a7Syz 		bufs[i].status = USBVC_BUF_INIT;
2840*c77a61a7Syz 
2841*c77a61a7Syz 		bufs[i].v4l2_buf.index = i;
2842*c77a61a7Syz 		bufs[i].v4l2_buf.m.offset = i * bufs[i].len;
2843*c77a61a7Syz 		bufs[i].v4l2_buf.length = bufs[i].len;
2844*c77a61a7Syz 		bufs[i].v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
2845*c77a61a7Syz 		bufs[i].v4l2_buf.sequence = 0;
2846*c77a61a7Syz 		bufs[i].v4l2_buf.field = V4L2_FIELD_NONE;
2847*c77a61a7Syz 		bufs[i].v4l2_buf.memory = V4L2_MEMORY_MMAP;
2848*c77a61a7Syz 		bufs[i].v4l2_buf.flags = V4L2_MEMORY_MMAP;
2849*c77a61a7Syz 
2850*c77a61a7Syz 		list_insert_tail(&strm_if->buf_map.uv_buf_free, &bufs[i]);
2851*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
2852*c77a61a7Syz 		    "usbvc_alloc_map_bufs: prepare %d buffers of %d bytes",
2853*c77a61a7Syz 		    buf_cnt, bufs[i].len);
2854*c77a61a7Syz 	}
2855*c77a61a7Syz 	strm_if->buf_map.buf_cnt = buf_cnt;
2856*c77a61a7Syz 	strm_if->buf_map.buf_filling = NULL;
2857*c77a61a7Syz 
2858*c77a61a7Syz 	return (buf_cnt);
2859*c77a61a7Syz }
2860*c77a61a7Syz 
2861*c77a61a7Syz 
2862*c77a61a7Syz /* Free all bufs which are for memory map IO style */
2863*c77a61a7Syz void
2864*c77a61a7Syz usbvc_free_map_bufs(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
2865*c77a61a7Syz {
2866*c77a61a7Syz 	usbvc_buf_t	*buf;
2867*c77a61a7Syz 
2868*c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
2869*c77a61a7Syz 	if (!strm_if) {
2870*c77a61a7Syz 
2871*c77a61a7Syz 		return;
2872*c77a61a7Syz 	}
2873*c77a61a7Syz 	strm_if->buf_map.buf_filling = NULL;
2874*c77a61a7Syz 	while (!list_is_empty(&strm_if->buf_map.uv_buf_free)) {
2875*c77a61a7Syz 		buf = (usbvc_buf_t *)list_head(&strm_if->buf_map.uv_buf_free);
2876*c77a61a7Syz 		list_remove(&(strm_if->buf_map.uv_buf_free), buf);
2877*c77a61a7Syz 	}
2878*c77a61a7Syz 	while (!list_is_empty(&strm_if->buf_map.uv_buf_done)) {
2879*c77a61a7Syz 		buf = (usbvc_buf_t *)list_head(&strm_if->buf_map.uv_buf_done);
2880*c77a61a7Syz 		list_remove(&(strm_if->buf_map.uv_buf_done), buf);
2881*c77a61a7Syz 	}
2882*c77a61a7Syz 	buf = strm_if->buf_map.buf_head;
2883*c77a61a7Syz 	if (!buf) {
2884*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
2885*c77a61a7Syz 		    "usbvc_free_map_bufs: no data buf need be freed, return");
2886*c77a61a7Syz 
2887*c77a61a7Syz 		return;
2888*c77a61a7Syz 	}
2889*c77a61a7Syz 	if (buf->umem_cookie) {
2890*c77a61a7Syz 		ddi_umem_free(buf->umem_cookie);
2891*c77a61a7Syz 	}
2892*c77a61a7Syz 	kmem_free(buf, sizeof (usbvc_buf_t) * strm_if->buf_map.buf_cnt);
2893*c77a61a7Syz 	strm_if->buf_map.buf_cnt = 0;
2894*c77a61a7Syz 	strm_if->buf_map.buf_head = NULL;
2895*c77a61a7Syz 
2896*c77a61a7Syz 	USB_DPRINTF_L2(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
2897*c77a61a7Syz 	    "usbvc_free_map_bufs: return");
2898*c77a61a7Syz }
2899*c77a61a7Syz 
2900*c77a61a7Syz 
2901*c77a61a7Syz /*
2902*c77a61a7Syz  * Open the isoc pipe, this pipe is for video data transfer
2903*c77a61a7Syz  */
2904*c77a61a7Syz int
2905*c77a61a7Syz usbvc_open_isoc_pipe(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
2906*c77a61a7Syz {
2907*c77a61a7Syz 	usb_pipe_policy_t policy;
2908*c77a61a7Syz 	int	rval = USB_SUCCESS;
2909*c77a61a7Syz 
2910*c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
2911*c77a61a7Syz 
2912*c77a61a7Syz 	if ((rval = usbvc_set_alt(usbvcp, strm_if)) != USB_SUCCESS) {
2913*c77a61a7Syz 
2914*c77a61a7Syz 		return (rval);
2915*c77a61a7Syz 	}
2916*c77a61a7Syz 	bzero(&policy, sizeof (usb_pipe_policy_t));
2917*c77a61a7Syz 	policy.pp_max_async_reqs = 2;
2918*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
2919*c77a61a7Syz 	if ((rval = usb_pipe_open(usbvcp->usbvc_dip, strm_if->curr_ep, &policy,
2920*c77a61a7Syz 	    USB_FLAGS_SLEEP, &strm_if->datain_ph)) != USB_SUCCESS) {
2921*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
2922*c77a61a7Syz 		    "usbvc_open_isoc_pipe: open pipe fail");
2923*c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
2924*c77a61a7Syz 
2925*c77a61a7Syz 		return (rval);
2926*c77a61a7Syz 	}
2927*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
2928*c77a61a7Syz 	strm_if->start_polling = 0;
2929*c77a61a7Syz 
2930*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
2931*c77a61a7Syz 	    "usbvc_open_isoc_pipe: success, datain_ph=%p", strm_if->datain_ph);
2932*c77a61a7Syz 
2933*c77a61a7Syz 	return (rval);
2934*c77a61a7Syz }
2935*c77a61a7Syz 
2936*c77a61a7Syz 
2937*c77a61a7Syz /*
2938*c77a61a7Syz  * Open the isoc pipe
2939*c77a61a7Syz  */
2940*c77a61a7Syz static void
2941*c77a61a7Syz usbvc_close_isoc_pipe(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
2942*c77a61a7Syz {
2943*c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
2944*c77a61a7Syz 	if (!strm_if) {
2945*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
2946*c77a61a7Syz 		    "usbvc_close_isoc_pipe: stream interface is NULL");
2947*c77a61a7Syz 
2948*c77a61a7Syz 		return;
2949*c77a61a7Syz 	}
2950*c77a61a7Syz 	if (strm_if->datain_ph) {
2951*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
2952*c77a61a7Syz 		usb_pipe_close(usbvcp->usbvc_dip, strm_if->datain_ph,
2953*c77a61a7Syz 		    USB_FLAGS_SLEEP, NULL, NULL);
2954*c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
2955*c77a61a7Syz 	}
2956*c77a61a7Syz 	strm_if->datain_ph = NULL;
2957*c77a61a7Syz }
2958*c77a61a7Syz 
2959*c77a61a7Syz 
2960*c77a61a7Syz /*
2961*c77a61a7Syz  * Start to get video data from isoc pipe in the stream interface,
2962*c77a61a7Syz  * issue isoc req.
2963*c77a61a7Syz  */
2964*c77a61a7Syz int
2965*c77a61a7Syz usbvc_start_isoc_polling(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if,
2966*c77a61a7Syz     uchar_t io_type)
2967*c77a61a7Syz {
2968*c77a61a7Syz 	int		rval = USB_SUCCESS;
2969*c77a61a7Syz 	uint_t		if_num;
2970*c77a61a7Syz 	usb_isoc_req_t	*req;
2971*c77a61a7Syz 	ushort_t	pkt_size;
2972*c77a61a7Syz 	ushort_t	n_pkt, pkt;
2973*c77a61a7Syz 	uint32_t	frame_size;
2974*c77a61a7Syz 
2975*c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
2976*c77a61a7Syz 	pkt_size = strm_if->curr_ep->wMaxPacketSize;
2977*c77a61a7Syz 	if_num = strm_if->if_descr->if_alt->altif_descr.bInterfaceNumber;
2978*c77a61a7Syz 	LE_TO_UINT32(strm_if->ctrl_pc.dwMaxVideoFrameSize, 0, frame_size);
2979*c77a61a7Syz 	n_pkt = (frame_size + HS_PKT_SIZE(pkt_size) -1)
2980*c77a61a7Syz 	    / HS_PKT_SIZE(pkt_size);
2981*c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
2982*c77a61a7Syz 	    "usbvc_start_isoc_polling: if_num=%d, alt=%d, n_pkt=%d,"
2983*c77a61a7Syz 	    " pkt_size=0x%x, frame_size=0x%x",
2984*c77a61a7Syz 	    if_num, strm_if->curr_alt, n_pkt, pkt_size, frame_size);
2985*c77a61a7Syz 
2986*c77a61a7Syz 	if (n_pkt > USBVC_MAX_PKTS) {
2987*c77a61a7Syz 		n_pkt = USBVC_MAX_PKTS;
2988*c77a61a7Syz 	}
2989*c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
2990*c77a61a7Syz 		"usbvc_start_isoc_polling: n_pkt=%d", n_pkt);
2991*c77a61a7Syz 
2992*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
2993*c77a61a7Syz 	if ((req = usb_alloc_isoc_req(usbvcp->usbvc_dip, n_pkt,
2994*c77a61a7Syz 	    n_pkt * pkt_size, USB_FLAGS_SLEEP)) != NULL) {
2995*c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
2996*c77a61a7Syz 
2997*c77a61a7Syz 		/* Initialize the packet descriptor */
2998*c77a61a7Syz 		for (pkt = 0; pkt < n_pkt; pkt++) {
2999*c77a61a7Syz 			req->isoc_pkt_descr[pkt].isoc_pkt_length = pkt_size;
3000*c77a61a7Syz 		}
3001*c77a61a7Syz 
3002*c77a61a7Syz 		req->isoc_pkts_count = n_pkt;
3003*c77a61a7Syz 
3004*c77a61a7Syz 		/*
3005*c77a61a7Syz 		 * zero here indicates that HCDs will use
3006*c77a61a7Syz 		 * isoc_pkt_descr->isoc_pkt_length to calculate
3007*c77a61a7Syz 		 * isoc_pkts_length.
3008*c77a61a7Syz 		 */
3009*c77a61a7Syz 		req->isoc_pkts_length = 0;
3010*c77a61a7Syz 		req->isoc_attributes = USB_ATTRS_ISOC_XFER_ASAP |
3011*c77a61a7Syz 		    USB_ATTRS_SHORT_XFER_OK | USB_ATTRS_AUTOCLEARING;
3012*c77a61a7Syz 		req->isoc_cb = usbvc_isoc_cb;
3013*c77a61a7Syz 		req->isoc_exc_cb = usbvc_isoc_exc_cb;
3014*c77a61a7Syz 		usbvcp->usbvc_io_type = io_type;
3015*c77a61a7Syz 		req->isoc_client_private = (usb_opaque_t)usbvcp;
3016*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
3017*c77a61a7Syz 		rval = usb_pipe_isoc_xfer(strm_if->datain_ph, req, 0);
3018*c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
3019*c77a61a7Syz 	} else {
3020*c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
3021*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
3022*c77a61a7Syz 		    "usbvc_start_isoc_polling: alloc_isoc_req fail");
3023*c77a61a7Syz 
3024*c77a61a7Syz 		return (USB_FAILURE);
3025*c77a61a7Syz 	}
3026*c77a61a7Syz 
3027*c77a61a7Syz 	if (rval != USB_SUCCESS) {
3028*c77a61a7Syz 		if (req) {
3029*c77a61a7Syz 			usb_free_isoc_req(req);
3030*c77a61a7Syz 			req = NULL;
3031*c77a61a7Syz 		}
3032*c77a61a7Syz 	}
3033*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
3034*c77a61a7Syz 		"usbvc_start_isoc_polling: return, rval=%d", rval);
3035*c77a61a7Syz 
3036*c77a61a7Syz 	return (rval);
3037*c77a61a7Syz }
3038*c77a61a7Syz 
3039*c77a61a7Syz /* callbacks for receiving video data (isco in transfer) */
3040*c77a61a7Syz 
3041*c77a61a7Syz /*ARGSUSED*/
3042*c77a61a7Syz /* Isoc transfer callback, get video data */
3043*c77a61a7Syz static void
3044*c77a61a7Syz usbvc_isoc_cb(usb_pipe_handle_t ph, usb_isoc_req_t *isoc_req)
3045*c77a61a7Syz {
3046*c77a61a7Syz 	usbvc_state_t	*usbvcp =
3047*c77a61a7Syz 			    (usbvc_state_t *)isoc_req->isoc_client_private;
3048*c77a61a7Syz 	int		i;
3049*c77a61a7Syz 	mblk_t		*data = isoc_req->isoc_data;
3050*c77a61a7Syz 	usbvc_buf_grp_t	*bufgrp;
3051*c77a61a7Syz 
3052*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
3053*c77a61a7Syz 
3054*c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3055*c77a61a7Syz 	    "usbvc_isoc_cb: rq=0x%p, fno=%" PRId64 ", n_pkts=%u, flag=0x%x,"
3056*c77a61a7Syz 	    " data=0x%p, cnt=%d",
3057*c77a61a7Syz 	    isoc_req, isoc_req->isoc_frame_no, isoc_req->isoc_pkts_count,
3058*c77a61a7Syz 	    isoc_req->isoc_attributes, isoc_req->isoc_data,
3059*c77a61a7Syz 	    isoc_req->isoc_error_count);
3060*c77a61a7Syz 
3061*c77a61a7Syz 	ASSERT((isoc_req->isoc_cb_flags & USB_CB_INTR_CONTEXT) != 0);
3062*c77a61a7Syz 	for (i = 0; i < isoc_req->isoc_pkts_count; i++) {
3063*c77a61a7Syz 
3064*c77a61a7Syz 		USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3065*c77a61a7Syz 		    "\tpkt%d: "
3066*c77a61a7Syz 		    "pktsize=%d status=%d resid=%d",
3067*c77a61a7Syz 		    i,
3068*c77a61a7Syz 		    isoc_req->isoc_pkt_descr[i].isoc_pkt_length,
3069*c77a61a7Syz 		    isoc_req->isoc_pkt_descr[i].isoc_pkt_status,
3070*c77a61a7Syz 		    isoc_req->isoc_pkt_descr[i].isoc_pkt_actual_length);
3071*c77a61a7Syz 
3072*c77a61a7Syz 		if (isoc_req->isoc_pkt_descr[i].isoc_pkt_status !=
3073*c77a61a7Syz 		    USB_CR_OK) {
3074*c77a61a7Syz 			USB_DPRINTF_L3(PRINT_MASK_CB,
3075*c77a61a7Syz 			    usbvcp->usbvc_log_handle,
3076*c77a61a7Syz 			    "record: pkt=%d status=%s", i, usb_str_cr(
3077*c77a61a7Syz 			    isoc_req->isoc_pkt_descr[i].isoc_pkt_status));
3078*c77a61a7Syz 		}
3079*c77a61a7Syz 
3080*c77a61a7Syz 		if (usbvcp->usbvc_io_type == V4L2_MEMORY_MMAP) {
3081*c77a61a7Syz 			bufgrp = &usbvcp->usbvc_curr_strm->buf_map;
3082*c77a61a7Syz 		} else {
3083*c77a61a7Syz 			bufgrp = &usbvcp->usbvc_curr_strm->buf_read;
3084*c77a61a7Syz 		}
3085*c77a61a7Syz 
3086*c77a61a7Syz 		if (isoc_req->isoc_pkt_descr[i].isoc_pkt_actual_length) {
3087*c77a61a7Syz 			if (usbvc_decode_stream_header(usbvcp, bufgrp, data,
3088*c77a61a7Syz 			    isoc_req->isoc_pkt_descr[i].isoc_pkt_actual_length)
3089*c77a61a7Syz 			    != USB_SUCCESS) {
3090*c77a61a7Syz 				USB_DPRINTF_L3(PRINT_MASK_CB,
3091*c77a61a7Syz 				    usbvcp->usbvc_log_handle, "decode error");
3092*c77a61a7Syz 			}
3093*c77a61a7Syz 			if (bufgrp->buf_filling &&
3094*c77a61a7Syz 			    (bufgrp->buf_filling->status == USBVC_BUF_ERR ||
3095*c77a61a7Syz 			    bufgrp->buf_filling->status == USBVC_BUF_DONE)) {
3096*c77a61a7Syz 
3097*c77a61a7Syz 				/* Move the buf to the full list */
3098*c77a61a7Syz 				list_insert_tail(&bufgrp->uv_buf_done,
3099*c77a61a7Syz 				    bufgrp->buf_filling);
3100*c77a61a7Syz 
3101*c77a61a7Syz 				bufgrp->buf_filling = NULL;
3102*c77a61a7Syz 
3103*c77a61a7Syz 				if (usbvcp->usbvc_io_type == V4L2_MEMORY_MMAP) {
3104*c77a61a7Syz 					cv_broadcast(&usbvcp->usbvc_mapio_cv);
3105*c77a61a7Syz 				} else {
3106*c77a61a7Syz 					cv_broadcast(&usbvcp->usbvc_read_cv);
3107*c77a61a7Syz 				}
3108*c77a61a7Syz 			}
3109*c77a61a7Syz 		}
3110*c77a61a7Syz 		data->b_rptr +=
3111*c77a61a7Syz 		    HS_PKT_SIZE(isoc_req->isoc_pkt_descr[i].isoc_pkt_length);
3112*c77a61a7Syz 	}
3113*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
3114*c77a61a7Syz 	usb_free_isoc_req(isoc_req);
3115*c77a61a7Syz }
3116*c77a61a7Syz 
3117*c77a61a7Syz 
3118*c77a61a7Syz /*ARGSUSED*/
3119*c77a61a7Syz static void
3120*c77a61a7Syz usbvc_isoc_exc_cb(usb_pipe_handle_t ph, usb_isoc_req_t *isoc_req)
3121*c77a61a7Syz {
3122*c77a61a7Syz 	usbvc_state_t	*usbvcp =
3123*c77a61a7Syz 			    (usbvc_state_t *)isoc_req->isoc_client_private;
3124*c77a61a7Syz 	usb_cr_t	completion_reason;
3125*c77a61a7Syz 	int		rval;
3126*c77a61a7Syz 	usbvc_stream_if_t	*strm_if;
3127*c77a61a7Syz 
3128*c77a61a7Syz 	ASSERT(!list_is_empty(&usbvcp->usbvc_stream_list));
3129*c77a61a7Syz 
3130*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
3131*c77a61a7Syz 
3132*c77a61a7Syz 	/* get the first stream interface */
3133*c77a61a7Syz 	strm_if = usbvcp->usbvc_curr_strm;
3134*c77a61a7Syz 
3135*c77a61a7Syz 	completion_reason = isoc_req->isoc_completion_reason;
3136*c77a61a7Syz 
3137*c77a61a7Syz 	USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3138*c77a61a7Syz 	    "usbvc_isoc_exc_cb: ph=0x%p, isoc_req=0x%p, cr=%d",
3139*c77a61a7Syz 	    (void *)ph, (void *)isoc_req, completion_reason);
3140*c77a61a7Syz 
3141*c77a61a7Syz 	ASSERT((isoc_req->isoc_cb_flags & USB_CB_INTR_CONTEXT) == 0);
3142*c77a61a7Syz 
3143*c77a61a7Syz 	switch (completion_reason) {
3144*c77a61a7Syz 	case USB_CR_STOPPED_POLLING:
3145*c77a61a7Syz 	case USB_CR_PIPE_CLOSING:
3146*c77a61a7Syz 	case USB_CR_PIPE_RESET:
3147*c77a61a7Syz 
3148*c77a61a7Syz 		break;
3149*c77a61a7Syz 	case USB_CR_NO_RESOURCES:
3150*c77a61a7Syz 		/*
3151*c77a61a7Syz 		 * keep the show going: Since we have the original
3152*c77a61a7Syz 		 * request, we just resubmit it
3153*c77a61a7Syz 		 */
3154*c77a61a7Syz 		rval = usb_pipe_isoc_xfer(strm_if->datain_ph, isoc_req,
3155*c77a61a7Syz 		    USB_FLAGS_NOSLEEP);
3156*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3157*c77a61a7Syz 		    "usbvc_isoc_exc_cb: restart capture rval=%d", rval);
3158*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
3159*c77a61a7Syz 
3160*c77a61a7Syz 		return;
3161*c77a61a7Syz 	default:
3162*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
3163*c77a61a7Syz 		usb_pipe_stop_isoc_polling(ph, USB_FLAGS_NOSLEEP);
3164*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3165*c77a61a7Syz 		    "usbvc_isoc_exc_cb: stop polling");
3166*c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
3167*c77a61a7Syz 	}
3168*c77a61a7Syz 	usb_free_isoc_req(isoc_req);
3169*c77a61a7Syz 	strm_if->start_polling = 0;
3170*c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3171*c77a61a7Syz 	    "usbvc_isoc_exc_cb: start_polling=%d cr=%d",
3172*c77a61a7Syz 	    strm_if->start_polling, completion_reason);
3173*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
3174*c77a61a7Syz }
3175*c77a61a7Syz 
3176*c77a61a7Syz /*
3177*c77a61a7Syz  * Other utility functions
3178*c77a61a7Syz  */
3179*c77a61a7Syz 
3180*c77a61a7Syz /*
3181*c77a61a7Syz  * Find a proper alternate according to the bandwidth that the current video
3182*c77a61a7Syz  * format need;
3183*c77a61a7Syz  * Set alternate by calling usb_set_alt_if;
3184*c77a61a7Syz  * Called before open pipes in stream interface.
3185*c77a61a7Syz  */
3186*c77a61a7Syz static int
3187*c77a61a7Syz usbvc_set_alt(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
3188*c77a61a7Syz {
3189*c77a61a7Syz 	usb_alt_if_data_t	*alt;
3190*c77a61a7Syz 	uint_t			i, j, if_num;
3191*c77a61a7Syz 	uint16_t		pktsize, curr_pktsize;
3192*c77a61a7Syz 	uint32_t		bandwidth;
3193*c77a61a7Syz 	int			rval = USB_SUCCESS;
3194*c77a61a7Syz 	usbvc_input_header_t	*ihd;
3195*c77a61a7Syz 	usbvc_output_header_t	*ohd;
3196*c77a61a7Syz 
3197*c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
3198*c77a61a7Syz 
3199*c77a61a7Syz 	LE_TO_UINT32(strm_if->ctrl_pc.dwMaxPayloadTransferSize, 0, bandwidth);
3200*c77a61a7Syz 	if (!bandwidth) {
3201*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
3202*c77a61a7Syz 		    "usbvc_set_alt: bandwidth is not set yet");
3203*c77a61a7Syz 
3204*c77a61a7Syz 		return (USB_FAILURE);
3205*c77a61a7Syz 	}
3206*c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
3207*c77a61a7Syz 		"usbvc_set_alt: bandwidth=%x", bandwidth);
3208*c77a61a7Syz 
3209*c77a61a7Syz 	strm_if->curr_ep = NULL;
3210*c77a61a7Syz 	curr_pktsize = 0xffff;
3211*c77a61a7Syz 	ohd = strm_if->output_header;
3212*c77a61a7Syz 	ihd = strm_if->input_header;
3213*c77a61a7Syz 	/*
3214*c77a61a7Syz 	 * Find one alternate setting whose isoc ep's max pktsize is just
3215*c77a61a7Syz 	 * enough for the bandwidth.
3216*c77a61a7Syz 	 */
3217*c77a61a7Syz 	for (i = 0; i < strm_if->if_descr->if_n_alt; i++) {
3218*c77a61a7Syz 		alt = &strm_if->if_descr->if_alt[i];
3219*c77a61a7Syz 
3220*c77a61a7Syz 		for (j = 0; j < alt->altif_n_ep; j++) {
3221*c77a61a7Syz 
3222*c77a61a7Syz 			/* if this stream interface is for input */
3223*c77a61a7Syz 			if (ihd != NULL &&
3224*c77a61a7Syz 			    alt->altif_ep[j].ep_descr.bEndpointAddress !=
3225*c77a61a7Syz 			    ihd->descr->bEndpointAddress) {
3226*c77a61a7Syz 
3227*c77a61a7Syz 				continue;
3228*c77a61a7Syz 			}
3229*c77a61a7Syz 			/*  if this stream interface is for output */
3230*c77a61a7Syz 			if (ohd != NULL &&
3231*c77a61a7Syz 			    alt->altif_ep[j].ep_descr.bEndpointAddress !=
3232*c77a61a7Syz 			    ohd->descr->bEndpointAddress) {
3233*c77a61a7Syz 
3234*c77a61a7Syz 				continue;
3235*c77a61a7Syz 			}
3236*c77a61a7Syz 			pktsize =
3237*c77a61a7Syz 			    alt->altif_ep[j].ep_descr.wMaxPacketSize;
3238*c77a61a7Syz 			pktsize = HS_PKT_SIZE(pktsize);
3239*c77a61a7Syz 			if (pktsize >= bandwidth && pktsize < curr_pktsize) {
3240*c77a61a7Syz 				curr_pktsize = pktsize;
3241*c77a61a7Syz 				strm_if->curr_alt = i;
3242*c77a61a7Syz 				strm_if->curr_ep = &alt->altif_ep[j].ep_descr;
3243*c77a61a7Syz 			}
3244*c77a61a7Syz 		}
3245*c77a61a7Syz 	}
3246*c77a61a7Syz 	if (!strm_if->curr_ep) {
3247*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
3248*c77a61a7Syz 		    "usbvc_set_alt: can't find a proper ep to satisfy"
3249*c77a61a7Syz 		    " the given bandwidth");
3250*c77a61a7Syz 
3251*c77a61a7Syz 		return (USB_FAILURE);
3252*c77a61a7Syz 	}
3253*c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
3254*c77a61a7Syz 	    "usbvc_set_alt: strm_if->curr_alt=%d", strm_if->curr_alt);
3255*c77a61a7Syz 	if_num = strm_if->if_descr->if_alt->altif_descr.bInterfaceNumber;
3256*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
3257*c77a61a7Syz 	if ((rval = usb_set_alt_if(usbvcp->usbvc_dip, if_num, strm_if->curr_alt,
3258*c77a61a7Syz 		USB_FLAGS_SLEEP, NULL, NULL)) != USB_SUCCESS) {
3259*c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
3260*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
3261*c77a61a7Syz 		    "usbvc_set_alt: usb_set_alt_if fail, if.alt=%d.%d, rval=%d",
3262*c77a61a7Syz 		    if_num, strm_if->curr_alt, rval);
3263*c77a61a7Syz 
3264*c77a61a7Syz 		return (rval);
3265*c77a61a7Syz 	}
3266*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
3267*c77a61a7Syz 
3268*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
3269*c77a61a7Syz 	    "usbvc_set_alt: return, if_num=%d, alt=%d",
3270*c77a61a7Syz 	    if_num, strm_if->curr_alt);
3271*c77a61a7Syz 
3272*c77a61a7Syz 	return (rval);
3273*c77a61a7Syz }
3274*c77a61a7Syz 
3275*c77a61a7Syz 
3276*c77a61a7Syz /*
3277*c77a61a7Syz  * Decode stream header for mjpeg and uncompressed format video data.
3278*c77a61a7Syz  * mjpeg and uncompressed format have the same stream header. See their
3279*c77a61a7Syz  * payload spec, 2.2 and 2.4
3280*c77a61a7Syz  */
3281*c77a61a7Syz static int
3282*c77a61a7Syz usbvc_decode_stream_header(usbvc_state_t *usbvcp, usbvc_buf_grp_t *bufgrp,
3283*c77a61a7Syz 	mblk_t *data, int actual_len)
3284*c77a61a7Syz {
3285*c77a61a7Syz 	uint32_t len, buf_left, data_len;
3286*c77a61a7Syz 	usbvc_stream_if_t *strm_if;
3287*c77a61a7Syz 	uchar_t head_flag, head_len;
3288*c77a61a7Syz 	usbvc_buf_t *buf_filling;
3289*c77a61a7Syz 
3290*c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
3291*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3292*c77a61a7Syz 	    "usbvc_decode_stream_header: enter. actual_len=%x", actual_len);
3293*c77a61a7Syz 
3294*c77a61a7Syz 	/* header length check. */
3295*c77a61a7Syz 	if (actual_len < 2) {
3296*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3297*c77a61a7Syz 		    "usbvc_decode_stream_header: header is not completed");
3298*c77a61a7Syz 
3299*c77a61a7Syz 		return (USB_FAILURE);
3300*c77a61a7Syz 	}
3301*c77a61a7Syz 	head_len = data->b_rptr[0];
3302*c77a61a7Syz 	head_flag = data->b_rptr[1];
3303*c77a61a7Syz 
3304*c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3305*c77a61a7Syz 	    "usbvc_decode_stream_header: headlen=%x", head_len);
3306*c77a61a7Syz 
3307*c77a61a7Syz 	/* header length check. */
3308*c77a61a7Syz 	if (actual_len < head_len) {
3309*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3310*c77a61a7Syz 		    "usbvc_decode_stream_header: actual_len < head_len");
3311*c77a61a7Syz 
3312*c77a61a7Syz 		return (USB_FAILURE);
3313*c77a61a7Syz 	}
3314*c77a61a7Syz 
3315*c77a61a7Syz 	/*
3316*c77a61a7Syz 	 * If there is no stream data in this packet and this packet is not
3317*c77a61a7Syz 	 * used to indicate the end of a frame, then just skip it.
3318*c77a61a7Syz 	 */
3319*c77a61a7Syz 	if ((actual_len == head_len) && !(head_flag & USBVC_STREAM_EOF)) {
3320*c77a61a7Syz 	    USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3321*c77a61a7Syz 		    "usbvc_decode_stream_header: only header, no data");
3322*c77a61a7Syz 
3323*c77a61a7Syz 		return (USB_FAILURE);
3324*c77a61a7Syz 	}
3325*c77a61a7Syz 
3326*c77a61a7Syz 	/* Get the first stream interface */
3327*c77a61a7Syz 	strm_if = usbvcp->usbvc_curr_strm;
3328*c77a61a7Syz 
3329*c77a61a7Syz 	LE_TO_UINT32(strm_if->ctrl_pc.dwMaxVideoFrameSize, 0, len);
3330*c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3331*c77a61a7Syz 	    "usbvc_decode_stream_header: dwMaxVideoFrameSize=%x, head_flag=%x",
3332*c77a61a7Syz 	    len, head_flag);
3333*c77a61a7Syz 
3334*c77a61a7Syz 	/*
3335*c77a61a7Syz 	 * if no buf is filling, pick one buf from free list and alloc data
3336*c77a61a7Syz 	 * mem for the buf.
3337*c77a61a7Syz 	 */
3338*c77a61a7Syz 	if (!bufgrp->buf_filling) {
3339*c77a61a7Syz 		if (list_is_empty(&bufgrp->uv_buf_free)) {
3340*c77a61a7Syz 			strm_if->fid = head_flag & USBVC_STREAM_FID;
3341*c77a61a7Syz 			USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3342*c77a61a7Syz 			    "usbvc_decode_stream_header: free list are empty");
3343*c77a61a7Syz 
3344*c77a61a7Syz 			return (USB_FAILURE);
3345*c77a61a7Syz 
3346*c77a61a7Syz 		} else {
3347*c77a61a7Syz 			bufgrp->buf_filling =
3348*c77a61a7Syz 			    (usbvc_buf_t *)list_head(&bufgrp->uv_buf_free);
3349*c77a61a7Syz 
3350*c77a61a7Syz 			/* unlink from buf free list */
3351*c77a61a7Syz 			list_remove(&bufgrp->uv_buf_free, bufgrp->buf_filling);
3352*c77a61a7Syz 		}
3353*c77a61a7Syz 		bufgrp->buf_filling->filled = 0;
3354*c77a61a7Syz 		USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3355*c77a61a7Syz 		    "usbvc_decode_stream_header: status=%d",
3356*c77a61a7Syz 		    bufgrp->buf_filling->status);
3357*c77a61a7Syz 		bufgrp->buf_filling->status = USBVC_BUF_EMPTY;
3358*c77a61a7Syz 	}
3359*c77a61a7Syz 	buf_filling = bufgrp->buf_filling;
3360*c77a61a7Syz 	ASSERT(buf_filling->len >= buf_filling->filled);
3361*c77a61a7Syz 	buf_left = buf_filling->len - buf_filling->filled;
3362*c77a61a7Syz 
3363*c77a61a7Syz 	/* if no buf room left, then return with a err status */
3364*c77a61a7Syz 	if (buf_left == 0) {
3365*c77a61a7Syz 		buf_filling->status = USBVC_BUF_ERR;
3366*c77a61a7Syz 
3367*c77a61a7Syz 		return (USB_FAILURE);
3368*c77a61a7Syz 	}
3369*c77a61a7Syz 
3370*c77a61a7Syz 	/* get this sample's data length except header */
3371*c77a61a7Syz 	data_len = actual_len - head_len;
3372*c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3373*c77a61a7Syz 	    "usbvc_decode_stream_header: fid=%x, len=%x, filled=%x",
3374*c77a61a7Syz 	    strm_if->fid, buf_filling->len, buf_filling->filled);
3375*c77a61a7Syz 
3376*c77a61a7Syz 	/* if the first sample for a frame */
3377*c77a61a7Syz 	if (buf_filling->filled == 0) {
3378*c77a61a7Syz 		/*
3379*c77a61a7Syz 		 * Only if it is the frist packet of a frame,
3380*c77a61a7Syz 		 * we will begin filling a frame.
3381*c77a61a7Syz 		 */
3382*c77a61a7Syz 		if (strm_if->fid != 0xff && strm_if->fid ==
3383*c77a61a7Syz 		    (head_flag & USBVC_STREAM_FID)) {
3384*c77a61a7Syz 			USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3385*c77a61a7Syz 			    "usbvc_decode_stream_header: 1st sample of a frame,"
3386*c77a61a7Syz 			    " fid is incorrect.");
3387*c77a61a7Syz 
3388*c77a61a7Syz 			return (USB_FAILURE);
3389*c77a61a7Syz 		}
3390*c77a61a7Syz 		strm_if->fid = head_flag & USBVC_STREAM_FID;
3391*c77a61a7Syz 
3392*c77a61a7Syz 	/* If in the middle of a frame, fid should be consistent. */
3393*c77a61a7Syz 	} else if (strm_if->fid != (head_flag & USBVC_STREAM_FID)) {
3394*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3395*c77a61a7Syz 		    "usbvc_decode_stream_header: fid is incorrect.");
3396*c77a61a7Syz 		strm_if->fid = head_flag & USBVC_STREAM_FID;
3397*c77a61a7Syz 		buf_filling->status = USBVC_BUF_ERR;
3398*c77a61a7Syz 
3399*c77a61a7Syz 		return (USB_FAILURE);
3400*c77a61a7Syz 	}
3401*c77a61a7Syz 	if (data_len) {
3402*c77a61a7Syz 		bcopy((void *)(data->b_rptr + head_len),
3403*c77a61a7Syz 		    (void *)(buf_filling->data + buf_filling->filled),
3404*c77a61a7Syz 		    min(data_len, buf_left));
3405*c77a61a7Syz 
3406*c77a61a7Syz 		buf_filling->filled += min(data_len, buf_left);
3407*c77a61a7Syz 	}
3408*c77a61a7Syz 
3409*c77a61a7Syz 	/* If the last packet for this frame */
3410*c77a61a7Syz 	if (head_flag & USBVC_STREAM_EOF) {
3411*c77a61a7Syz 		buf_filling->status = USBVC_BUF_DONE;
3412*c77a61a7Syz 	}
3413*c77a61a7Syz 	if (data_len > buf_left) {
3414*c77a61a7Syz 		buf_filling->status = USBVC_BUF_ERR;
3415*c77a61a7Syz 	}
3416*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3417*c77a61a7Syz 	    "usbvc_decode_stream_header: buf_status=%d", buf_filling->status);
3418*c77a61a7Syz 
3419*c77a61a7Syz 	return (USB_SUCCESS);
3420*c77a61a7Syz }
3421*c77a61a7Syz 
3422*c77a61a7Syz 
3423*c77a61a7Syz /*
3424*c77a61a7Syz  * usbvc_serialize_access:
3425*c77a61a7Syz  *    Get the serial synchronization object before returning.
3426*c77a61a7Syz  *
3427*c77a61a7Syz  * Arguments:
3428*c77a61a7Syz  *    usbvcp - Pointer to usbvc state structure
3429*c77a61a7Syz  *    waitsig - Set to:
3430*c77a61a7Syz  *	USBVC_SER_SIG - to wait such that a signal can interrupt
3431*c77a61a7Syz  *	USBVC_SER_NOSIG - to wait such that a signal cannot interrupt
3432*c77a61a7Syz  */
3433*c77a61a7Syz static int
3434*c77a61a7Syz usbvc_serialize_access(usbvc_state_t *usbvcp, boolean_t waitsig)
3435*c77a61a7Syz {
3436*c77a61a7Syz 	int rval = 1;
3437*c77a61a7Syz 
3438*c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
3439*c77a61a7Syz 
3440*c77a61a7Syz 	while (usbvcp->usbvc_serial_inuse) {
3441*c77a61a7Syz 		if (waitsig == USBVC_SER_SIG) {
3442*c77a61a7Syz 			rval = cv_wait_sig(&usbvcp->usbvc_serial_cv,
3443*c77a61a7Syz 			    &usbvcp->usbvc_mutex);
3444*c77a61a7Syz 		} else {
3445*c77a61a7Syz 			cv_wait(&usbvcp->usbvc_serial_cv,
3446*c77a61a7Syz 			    &usbvcp->usbvc_mutex);
3447*c77a61a7Syz 		}
3448*c77a61a7Syz 	}
3449*c77a61a7Syz 	usbvcp->usbvc_serial_inuse = B_TRUE;
3450*c77a61a7Syz 
3451*c77a61a7Syz 	return (rval);
3452*c77a61a7Syz }
3453*c77a61a7Syz 
3454*c77a61a7Syz 
3455*c77a61a7Syz /*
3456*c77a61a7Syz  * usbvc_release_access:
3457*c77a61a7Syz  *    Release the serial synchronization object.
3458*c77a61a7Syz  */
3459*c77a61a7Syz static void
3460*c77a61a7Syz usbvc_release_access(usbvc_state_t *usbvcp)
3461*c77a61a7Syz {
3462*c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
3463*c77a61a7Syz 	usbvcp->usbvc_serial_inuse = B_FALSE;
3464*c77a61a7Syz 	cv_broadcast(&usbvcp->usbvc_serial_cv);
3465*c77a61a7Syz }
3466*c77a61a7Syz 
3467*c77a61a7Syz 
3468*c77a61a7Syz /* Send req to video control interface to get ctrl */
3469*c77a61a7Syz int
3470*c77a61a7Syz usbvc_vc_get_ctrl(usbvc_state_t *usbvcp, uint8_t req_code, uint8_t entity_id,
3471*c77a61a7Syz     uint16_t cs, uint16_t wlength, mblk_t *data)
3472*c77a61a7Syz {
3473*c77a61a7Syz 	usb_cb_flags_t	cb_flags;
3474*c77a61a7Syz 	usb_cr_t	cr;
3475*c77a61a7Syz 	usb_ctrl_setup_t setup;
3476*c77a61a7Syz 
3477*c77a61a7Syz 	setup.bmRequestType = USBVC_GET_IF;	/* bmRequestType */
3478*c77a61a7Syz 	setup.bRequest = req_code;		/* bRequest */
3479*c77a61a7Syz 	setup.wValue = cs<<8;
3480*c77a61a7Syz 	setup.wIndex = entity_id<<8;
3481*c77a61a7Syz 	setup.wLength = wlength;
3482*c77a61a7Syz 	setup.attrs = 0;
3483*c77a61a7Syz 
3484*c77a61a7Syz 	if (usb_pipe_ctrl_xfer_wait(usbvcp->usbvc_default_ph, &setup, &data,
3485*c77a61a7Syz 	    &cr, &cb_flags, 0) != USB_SUCCESS) {
3486*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3487*c77a61a7Syz 		    "usbvc_vc_get_ctrl: cmd failed, cr=%d, cb_flags=%x",
3488*c77a61a7Syz 		    cr, cb_flags);
3489*c77a61a7Syz 
3490*c77a61a7Syz 		return (USB_FAILURE);
3491*c77a61a7Syz 	}
3492*c77a61a7Syz 
3493*c77a61a7Syz 	return (USB_SUCCESS);
3494*c77a61a7Syz }
3495*c77a61a7Syz 
3496*c77a61a7Syz 
3497*c77a61a7Syz /* Send req to video control interface to get ctrl */
3498*c77a61a7Syz int
3499*c77a61a7Syz usbvc_vc_set_ctrl(usbvc_state_t *usbvcp, uint8_t req_code,  uint8_t entity_id,
3500*c77a61a7Syz 	uint16_t cs, uint16_t wlength, mblk_t *data)
3501*c77a61a7Syz {
3502*c77a61a7Syz 	usb_cb_flags_t	cb_flags;
3503*c77a61a7Syz 	usb_cr_t	cr;
3504*c77a61a7Syz 	usb_ctrl_setup_t setup;
3505*c77a61a7Syz 
3506*c77a61a7Syz 	setup.bmRequestType = USBVC_SET_IF;	/* bmRequestType */
3507*c77a61a7Syz 	setup.bRequest = req_code;		/* bRequest */
3508*c77a61a7Syz 	setup.wValue = cs<<8;
3509*c77a61a7Syz 	setup.wIndex = entity_id<<8;
3510*c77a61a7Syz 	setup.wLength = wlength;
3511*c77a61a7Syz 	setup.attrs = 0;
3512*c77a61a7Syz 
3513*c77a61a7Syz 	if (usb_pipe_ctrl_xfer_wait(usbvcp->usbvc_default_ph, &setup, &data,
3514*c77a61a7Syz 	    &cr, &cb_flags, 0) != USB_SUCCESS) {
3515*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3516*c77a61a7Syz 		    "usbvc_vc_set_ctrl: cmd failed, cr=%d, cb_flags=%x",
3517*c77a61a7Syz 		    cr, cb_flags);
3518*c77a61a7Syz 
3519*c77a61a7Syz 		return (USB_FAILURE);
3520*c77a61a7Syz 	}
3521*c77a61a7Syz 
3522*c77a61a7Syz 	return (USB_SUCCESS);
3523*c77a61a7Syz }
3524*c77a61a7Syz 
3525*c77a61a7Syz 
3526*c77a61a7Syz /* Set probe or commit ctrl for video stream interface */
3527*c77a61a7Syz int
3528*c77a61a7Syz usbvc_vs_set_probe_commit(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if,
3529*c77a61a7Syz 	usbvc_vs_probe_commit_t *ctrl_pc, uchar_t cs)
3530*c77a61a7Syz {
3531*c77a61a7Syz 	mblk_t *data;
3532*c77a61a7Syz 	usb_cb_flags_t	cb_flags;
3533*c77a61a7Syz 	usb_cr_t	cr;
3534*c77a61a7Syz 	usb_ctrl_setup_t setup;
3535*c77a61a7Syz 	int rval;
3536*c77a61a7Syz 
3537*c77a61a7Syz 	setup.bmRequestType = USBVC_SET_IF;	/* bmRequestType */
3538*c77a61a7Syz 	setup.bRequest = SET_CUR;		/* bRequest */
3539*c77a61a7Syz 
3540*c77a61a7Syz 	/* wValue, VS_PROBE_CONTROL or VS_COMMIT_CONTROL */
3541*c77a61a7Syz 	setup.wValue = cs;
3542*c77a61a7Syz 
3543*c77a61a7Syz 	/* UVC Spec: this value must be put to the high byte */
3544*c77a61a7Syz 	setup.wValue = setup.wValue << 8;
3545*c77a61a7Syz 
3546*c77a61a7Syz 	setup.wIndex = strm_if->if_descr->if_alt->altif_descr.bInterfaceNumber;
3547*c77a61a7Syz 	setup.wLength = usbvcp->usbvc_vc_header->descr->bcdUVC[0] ? 34 : 26;
3548*c77a61a7Syz 	setup.attrs = 0;
3549*c77a61a7Syz 
3550*c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3551*c77a61a7Syz 	    "usbvc_vs_set_probe_commit: wLength=%d", setup.wLength);
3552*c77a61a7Syz 
3553*c77a61a7Syz 	/* Data block */
3554*c77a61a7Syz 	if ((data = allocb(setup.wLength, BPRI_HI)) == NULL) {
3555*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3556*c77a61a7Syz 		    "usbvc_vs_set_probe_commit: allocb failed");
3557*c77a61a7Syz 
3558*c77a61a7Syz 		return (USB_FAILURE);
3559*c77a61a7Syz 	}
3560*c77a61a7Syz 
3561*c77a61a7Syz 	bcopy(ctrl_pc, data->b_rptr, setup.wLength);
3562*c77a61a7Syz 	data->b_wptr += setup.wLength;
3563*c77a61a7Syz 
3564*c77a61a7Syz 	if ((rval = usb_pipe_ctrl_xfer_wait(usbvcp->usbvc_default_ph, &setup,
3565*c77a61a7Syz 	    &data, &cr, &cb_flags, 0)) != USB_SUCCESS) {
3566*c77a61a7Syz 		if (data) {
3567*c77a61a7Syz 			freemsg(data);
3568*c77a61a7Syz 		}
3569*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3570*c77a61a7Syz 		    "usbvc_vs_set_probe_commit: fail, rval=%d, cr=%d, "
3571*c77a61a7Syz 		    "cb_flags=%x", rval, cr, cb_flags);
3572*c77a61a7Syz 
3573*c77a61a7Syz 		return (rval);
3574*c77a61a7Syz 	}
3575*c77a61a7Syz 	if (data) {
3576*c77a61a7Syz 		freemsg(data);
3577*c77a61a7Syz 	}
3578*c77a61a7Syz 
3579*c77a61a7Syz 	return (USB_SUCCESS);
3580*c77a61a7Syz }
3581*c77a61a7Syz 
3582*c77a61a7Syz 
3583*c77a61a7Syz /* Get probe ctrl for vodeo stream interface */
3584*c77a61a7Syz int
3585*c77a61a7Syz usbvc_vs_get_probe(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if,
3586*c77a61a7Syz 	usbvc_vs_probe_commit_t *ctrl_pc, uchar_t bRequest)
3587*c77a61a7Syz {
3588*c77a61a7Syz 	mblk_t *data = NULL;
3589*c77a61a7Syz 	usb_cb_flags_t	cb_flags;
3590*c77a61a7Syz 	usb_cr_t	cr;
3591*c77a61a7Syz 	usb_ctrl_setup_t setup;
3592*c77a61a7Syz 
3593*c77a61a7Syz 	setup.bmRequestType = USBVC_GET_IF;	/* bmRequestType */
3594*c77a61a7Syz 	setup.bRequest = bRequest;		/* bRequest */
3595*c77a61a7Syz 	setup.wValue = VS_PROBE_CONTROL;	/* wValue, PROBE or COMMIT */
3596*c77a61a7Syz 	setup.wValue = setup.wValue << 8;
3597*c77a61a7Syz 	setup.wIndex =
3598*c77a61a7Syz 	    (uint16_t)strm_if->if_descr->if_alt->altif_descr.bInterfaceNumber;
3599*c77a61a7Syz 	setup.wLength = usbvcp->usbvc_vc_header->descr->bcdUVC[0] ? 34 : 26;
3600*c77a61a7Syz 
3601*c77a61a7Syz 	setup.attrs = 0;
3602*c77a61a7Syz 
3603*c77a61a7Syz 	if (usb_pipe_ctrl_xfer_wait(usbvcp->usbvc_default_ph, &setup, &data,
3604*c77a61a7Syz 	    &cr, &cb_flags, 0) != USB_SUCCESS) {
3605*c77a61a7Syz 		if (data) {
3606*c77a61a7Syz 			freemsg(data);
3607*c77a61a7Syz 		}
3608*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3609*c77a61a7Syz 		    "usbvc_vs_get_probe: cmd failed, cr=%d, cb_flags=%x",
3610*c77a61a7Syz 		    cr, cb_flags);
3611*c77a61a7Syz 
3612*c77a61a7Syz 		return (USB_FAILURE);
3613*c77a61a7Syz 	}
3614*c77a61a7Syz 	bcopy(data->b_rptr, ctrl_pc, setup.wLength);
3615*c77a61a7Syz 	if (data) {
3616*c77a61a7Syz 		freemsg(data);
3617*c77a61a7Syz 	}
3618*c77a61a7Syz 
3619*c77a61a7Syz 	return (USB_SUCCESS);
3620*c77a61a7Syz }
3621*c77a61a7Syz 
3622*c77a61a7Syz 
3623*c77a61a7Syz /* Set a default format when open the device */
3624*c77a61a7Syz static int
3625*c77a61a7Syz usbvc_set_default_stream_fmt(usbvc_state_t *usbvcp)
3626*c77a61a7Syz {
3627*c77a61a7Syz 	usbvc_vs_probe_commit_t ctrl, ctrl_get;
3628*c77a61a7Syz 	usbvc_stream_if_t *strm_if;
3629*c77a61a7Syz 	usbvc_format_group_t *curr_fmtgrp;
3630*c77a61a7Syz 	uint32_t bandwidth;
3631*c77a61a7Syz 	uint8_t  index, i;
3632*c77a61a7Syz 
3633*c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3634*c77a61a7Syz 	    "usbvc_set_default_stream_fmt: enter");
3635*c77a61a7Syz 
3636*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
3637*c77a61a7Syz 	if (list_is_empty(&usbvcp->usbvc_stream_list)) {
3638*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3639*c77a61a7Syz 		    "usbvc_set_default_stream_fmt: no stream interface, fail");
3640*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
3641*c77a61a7Syz 
3642*c77a61a7Syz 		return (USB_FAILURE);
3643*c77a61a7Syz 	}
3644*c77a61a7Syz 	bzero((void *)&ctrl, sizeof (usbvc_vs_probe_commit_t));
3645*c77a61a7Syz 
3646*c77a61a7Syz 	/* Get the current stream interface */
3647*c77a61a7Syz 	strm_if = usbvcp->usbvc_curr_strm;
3648*c77a61a7Syz 
3649*c77a61a7Syz 	/* Fill the probe commit req data */
3650*c77a61a7Syz 	ctrl.bmHint[0] = 0;
3651*c77a61a7Syz 
3652*c77a61a7Syz 	for (i = 0; i < strm_if->fmtgrp_cnt; i++) {
3653*c77a61a7Syz 		curr_fmtgrp = &strm_if->format_group[i];
3654*c77a61a7Syz 
3655*c77a61a7Syz 		/*
3656*c77a61a7Syz 		 * If v4l2_pixelformat is NULL, then that means there is not
3657*c77a61a7Syz 		 * a parsed format in format_group[i].
3658*c77a61a7Syz 		 */
3659*c77a61a7Syz 		if (!curr_fmtgrp || !curr_fmtgrp->v4l2_pixelformat ||
3660*c77a61a7Syz 		    curr_fmtgrp->frame_cnt == 0) {
3661*c77a61a7Syz 			USB_DPRINTF_L2(PRINT_MASK_DEVCTRL,
3662*c77a61a7Syz 			    usbvcp->usbvc_log_handle,
3663*c77a61a7Syz 			    "usbvc_set_default_stream_fmt: no frame, fail");
3664*c77a61a7Syz 
3665*c77a61a7Syz 			continue;
3666*c77a61a7Syz 		} else {
3667*c77a61a7Syz 
3668*c77a61a7Syz 			break;
3669*c77a61a7Syz 		}
3670*c77a61a7Syz 	}
3671*c77a61a7Syz 	if (!curr_fmtgrp || curr_fmtgrp->frame_cnt == 0) {
3672*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3673*c77a61a7Syz 		    "usbvc_set_default_stream_fmt: can't find a fmtgrp"
3674*c77a61a7Syz 		    "which has a frame, fail");
3675*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
3676*c77a61a7Syz 
3677*c77a61a7Syz 		return (USB_FAILURE);
3678*c77a61a7Syz 	}
3679*c77a61a7Syz 
3680*c77a61a7Syz 	ctrl.bFormatIndex = curr_fmtgrp->format->bFormatIndex;
3681*c77a61a7Syz 
3682*c77a61a7Syz 	/* use the first frame descr as default */
3683*c77a61a7Syz 	ctrl.bFrameIndex = curr_fmtgrp->frames[0].descr->bFrameIndex;
3684*c77a61a7Syz 
3685*c77a61a7Syz 	/* use bcopy to keep the byte sequence as 32 bit little endian */
3686*c77a61a7Syz 	bcopy(&(curr_fmtgrp->frames[0].descr->dwDefaultFrameInterval[0]),
3687*c77a61a7Syz 	    &(ctrl.dwFrameInterval[0]), 4);
3688*c77a61a7Syz 
3689*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
3690*c77a61a7Syz 	if (usbvc_vs_set_probe_commit(usbvcp, strm_if, &ctrl, VS_PROBE_CONTROL)
3691*c77a61a7Syz 	    != USB_SUCCESS) {
3692*c77a61a7Syz 
3693*c77a61a7Syz 		return (USB_FAILURE);
3694*c77a61a7Syz 	}
3695*c77a61a7Syz 	if (usbvc_vs_get_probe(usbvcp, strm_if, &ctrl_get, GET_CUR)
3696*c77a61a7Syz 	    != USB_SUCCESS) {
3697*c77a61a7Syz 
3698*c77a61a7Syz 		return (USB_FAILURE);
3699*c77a61a7Syz 	}
3700*c77a61a7Syz 
3701*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
3702*c77a61a7Syz 	LE_TO_UINT32(strm_if->ctrl_pc.dwMaxPayloadTransferSize, 0, bandwidth);
3703*c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3704*c77a61a7Syz 	    "usbvc_set_default_stream_fmt: get bandwidth=%x", bandwidth);
3705*c77a61a7Syz 
3706*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
3707*c77a61a7Syz 	if (usbvc_vs_set_probe_commit(usbvcp, strm_if, &ctrl_get,
3708*c77a61a7Syz 	    VS_COMMIT_CONTROL) != USB_SUCCESS) {
3709*c77a61a7Syz 
3710*c77a61a7Syz 		return (USB_FAILURE);
3711*c77a61a7Syz 	}
3712*c77a61a7Syz 
3713*c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
3714*c77a61a7Syz 
3715*c77a61a7Syz 	/*  it's good to check index here before use it */
3716*c77a61a7Syz 	index = ctrl_get.bFormatIndex - curr_fmtgrp->format->bFormatIndex;
3717*c77a61a7Syz 	if (index < strm_if->fmtgrp_cnt) {
3718*c77a61a7Syz 		strm_if->cur_format_group = &strm_if->format_group[index];
3719*c77a61a7Syz 	} else {
3720*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3721*c77a61a7Syz 		    "usbvc_set_default_stream_fmt: format index out of range");
3722*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
3723*c77a61a7Syz 
3724*c77a61a7Syz 		return (USB_FAILURE);
3725*c77a61a7Syz 	}
3726*c77a61a7Syz 
3727*c77a61a7Syz 	index = ctrl_get.bFrameIndex -
3728*c77a61a7Syz 	    strm_if->cur_format_group->frames[0].descr->bFrameIndex;
3729*c77a61a7Syz 	if (index < strm_if->cur_format_group->frame_cnt) {
3730*c77a61a7Syz 		strm_if->cur_format_group->cur_frame =
3731*c77a61a7Syz 		    &strm_if->cur_format_group->frames[index];
3732*c77a61a7Syz 	} else {
3733*c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3734*c77a61a7Syz 		    "usbvc_set_default_stream: frame index out of range");
3735*c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
3736*c77a61a7Syz 
3737*c77a61a7Syz 		return (USB_FAILURE);
3738*c77a61a7Syz 	}
3739*c77a61a7Syz 
3740*c77a61a7Syz 	/*
3741*c77a61a7Syz 	 * by now, the video format is set successfully. record the current
3742*c77a61a7Syz 	 * setting to strm_if->ctrl_pc
3743*c77a61a7Syz 	 */
3744*c77a61a7Syz 	bcopy(&ctrl_get, &strm_if->ctrl_pc, sizeof (usbvc_vs_probe_commit_t));
3745*c77a61a7Syz 
3746*c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
3747*c77a61a7Syz 
3748*c77a61a7Syz 	return (USB_SUCCESS);
3749*c77a61a7Syz }
3750