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