xref: /illumos-gate/usr/src/uts/common/io/usb/clients/video/usbvc/usbvc.c (revision 61b2e298a43ac1155abb6369dd7c6a117fbeb473)
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 /*
22112116d8Sfb  * Copyright 2008 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,
286*61b2e298Slc 	"USB video class driver",
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,
430ef772e54Sfb 	    "usbvc", &usbvc_errlevel,
431ef772e54Sfb 	    &usbvc_errmask, &usbvc_instance_debug, 0);
432c77a61a7Syz 
433c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
434ef772e54Sfb 	    "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 =
610ef772e54Sfb 	    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,
656ef772e54Sfb 		    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);
720*61b2e298Slc 
721*61b2e298Slc 	/* reset the desired read buf number to the default value on close */
722*61b2e298Slc 	strm_if->buf_read_num = USBVC_DEFAULT_READ_BUF_NUM;
723*61b2e298Slc 
724c77a61a7Syz 	usbvc_free_map_bufs(usbvcp, strm_if);
725c77a61a7Syz 	usbvcp->usbvc_drv_state &= ~USBVC_OPEN;
726c77a61a7Syz 
727c77a61a7Syz 	usbvc_release_access(usbvcp);
728c77a61a7Syz 	usbvc_pm_idle_component(usbvcp);
729c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
730c77a61a7Syz 
731c77a61a7Syz 	return (0);
732c77a61a7Syz }
733c77a61a7Syz 
734c77a61a7Syz 
735c77a61a7Syz /*ARGSUSED*/
736c77a61a7Syz /* Read isoc data from usb video devices */
737c77a61a7Syz static int
738c77a61a7Syz usbvc_read(dev_t dev, struct uio *uio_p, cred_t *cred_p)
739c77a61a7Syz {
740c77a61a7Syz 	int			rval;
741c77a61a7Syz 	usbvc_stream_if_t	*strm_if;
742c77a61a7Syz 	usbvc_state_t	*usbvcp =
743ef772e54Sfb 	    ddi_get_soft_state(usbvc_statep, getminor(dev));
744c77a61a7Syz 
745c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
746c77a61a7Syz 	    "usbvc_read: enter");
747c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
748c77a61a7Syz 	if (usbvcp->usbvc_dev_state != USB_DEV_ONLINE) {
749c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
750c77a61a7Syz 		    "usbvc_read: Device is not available,"
751c77a61a7Syz 		    " dev_stat=%d", usbvcp->usbvc_dev_state);
752c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
753c77a61a7Syz 
754c77a61a7Syz 		return (EFAULT);
755c77a61a7Syz 	}
756c77a61a7Syz 	if ((uio_p->uio_fmode & (FNDELAY|FNONBLOCK)) &&
757c77a61a7Syz 	    (usbvcp->usbvc_serial_inuse != B_FALSE)) {
758c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
759c77a61a7Syz 		    "usbvc_read: non-blocking read, return fail.");
760c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
761c77a61a7Syz 
762c77a61a7Syz 		return (EAGAIN);
763c77a61a7Syz 	}
764c77a61a7Syz 	if (usbvc_serialize_access(usbvcp, USBVC_SER_SIG) <= 0) {
765c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
766c77a61a7Syz 		    "usbvc_read: serialize_access failed.");
767c77a61a7Syz 		rval = EFAULT;
768c77a61a7Syz 
769c77a61a7Syz 		goto fail;
770c77a61a7Syz 	}
771c77a61a7Syz 
772c77a61a7Syz 	/* Get the first stream interface */
773c77a61a7Syz 	strm_if = usbvcp->usbvc_curr_strm;
774c77a61a7Syz 	if (!strm_if) {
775c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
776c77a61a7Syz 		    "usbvc_read: no stream interfaces");
777c77a61a7Syz 		rval = EFAULT;
778c77a61a7Syz 
779c77a61a7Syz 		goto fail;
780c77a61a7Syz 	}
781c77a61a7Syz 
782c77a61a7Syz 	/*
783c77a61a7Syz 	 * If it is the first read, open isoc pipe and allocate bufs for
784c77a61a7Syz 	 * read I/O method.
785c77a61a7Syz 	 */
786c77a61a7Syz 	if (strm_if->datain_ph == NULL) {
787c77a61a7Syz 		if (usbvc_open_isoc_pipe(usbvcp, strm_if) != USB_SUCCESS) {
788c77a61a7Syz 			USB_DPRINTF_L2(PRINT_MASK_READ,
789c77a61a7Syz 			    usbvcp->usbvc_log_handle,
790c77a61a7Syz 			    "usbvc_read: first read, open pipe fail");
791c77a61a7Syz 			rval = EFAULT;
792c77a61a7Syz 
793c77a61a7Syz 			goto fail;
794c77a61a7Syz 		}
795c77a61a7Syz 		if (usbvc_alloc_read_bufs(usbvcp, strm_if) != USB_SUCCESS) {
796c77a61a7Syz 			USB_DPRINTF_L2(PRINT_MASK_READ,
797c77a61a7Syz 			    usbvcp->usbvc_log_handle,
798c77a61a7Syz 			    "usbvc_read: allocate rw bufs fail");
799c77a61a7Syz 			rval = EFAULT;
800c77a61a7Syz 
801c77a61a7Syz 			goto fail;
802c77a61a7Syz 		}
803c77a61a7Syz 	}
804c77a61a7Syz 
805c77a61a7Syz 	/* start polling if it is not started yet */
806c77a61a7Syz 	if (strm_if->start_polling != 1) {
807c77a61a7Syz 		if (usbvc_start_isoc_polling(usbvcp, strm_if, NULL) !=
808c77a61a7Syz 		    USB_SUCCESS) {
809c77a61a7Syz 			USB_DPRINTF_L2(PRINT_MASK_READ,
810c77a61a7Syz 			    usbvcp->usbvc_log_handle,
811c77a61a7Syz 			    "usbvc_read: usbvc_start_isoc_polling fail");
812c77a61a7Syz 			rval = EFAULT;
813c77a61a7Syz 
814c77a61a7Syz 			goto fail;
815c77a61a7Syz 		}
816c77a61a7Syz 		strm_if->start_polling = 1;
817c77a61a7Syz 	}
818c77a61a7Syz 
819c77a61a7Syz 	if (list_is_empty(&strm_if->buf_read.uv_buf_done)) {
820c77a61a7Syz 		USB_DPRINTF_L3(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
821c77a61a7Syz 		    "usbvc_read: full buf list is empty.");
822c77a61a7Syz 
823c77a61a7Syz 		if (uio_p->uio_fmode & (FNDELAY | FNONBLOCK)) {
824c77a61a7Syz 			USB_DPRINTF_L2(PRINT_MASK_READ,
825c77a61a7Syz 			    usbvcp->usbvc_log_handle, "usbvc_read: fail, "
826c77a61a7Syz 			    "non-blocking read, done buf is empty.");
827c77a61a7Syz 			rval = EAGAIN;
828c77a61a7Syz 
829c77a61a7Syz 			goto fail;
830c77a61a7Syz 		}
831c77a61a7Syz 
832c77a61a7Syz 		/* no available buffers, block here */
833c77a61a7Syz 		while (list_is_empty(&strm_if->buf_read.uv_buf_done)) {
834c77a61a7Syz 			USB_DPRINTF_L3(PRINT_MASK_READ,
835c77a61a7Syz 			    usbvcp->usbvc_log_handle,
836c77a61a7Syz 			    "usbvc_read: wait for done buf");
837c77a61a7Syz 			if (cv_wait_sig(&usbvcp->usbvc_read_cv,
838c77a61a7Syz 			    &usbvcp->usbvc_mutex) <= 0) {
839c77a61a7Syz 				/* no done buf and cv is signaled */
840c77a61a7Syz 				rval = EINTR;
841c77a61a7Syz 
842c77a61a7Syz 				goto fail;
843c77a61a7Syz 			}
844c77a61a7Syz 			if (usbvcp->usbvc_dev_state != USB_DEV_ONLINE) {
845c77a61a7Syz 
846c77a61a7Syz 				/* Device is disconnected. */
847c77a61a7Syz 				rval = EINTR;
848c77a61a7Syz 
849c77a61a7Syz 				goto fail;
850c77a61a7Syz 			}
851c77a61a7Syz 		}
852c77a61a7Syz 
853c77a61a7Syz 	}
854c77a61a7Syz 
855c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
856c77a61a7Syz 	rval = physio(usbvc_strategy, NULL, dev, B_READ,
857ef772e54Sfb 	    usbvc_minphys, uio_p);
858c77a61a7Syz 
859c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
860c77a61a7Syz 	usbvc_release_access(usbvcp);
861c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
862c77a61a7Syz 
863c77a61a7Syz 	return (rval);
864c77a61a7Syz 
865c77a61a7Syz fail:
866c77a61a7Syz 	usbvc_release_access(usbvcp);
867c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
868c77a61a7Syz 
869c77a61a7Syz 	return (rval);
870c77a61a7Syz }
871c77a61a7Syz 
872c77a61a7Syz 
873c77a61a7Syz /*
874c77a61a7Syz  * strategy:
875c77a61a7Syz  *	Called through physio to setup and start the transfer.
876c77a61a7Syz  */
877c77a61a7Syz static int
878c77a61a7Syz usbvc_strategy(struct buf *bp)
879c77a61a7Syz {
880c77a61a7Syz 	usbvc_state_t *usbvcp = ddi_get_soft_state(usbvc_statep,
881ef772e54Sfb 	    getminor(bp->b_edev));
882c77a61a7Syz 
883c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
884c77a61a7Syz 	    "usbvc_strategy: enter");
885c77a61a7Syz 
886c77a61a7Syz 	/*
887c77a61a7Syz 	 * Initialize residual count here in case transfer doesn't even get
888c77a61a7Syz 	 * started.
889c77a61a7Syz 	 */
890c77a61a7Syz 	bp->b_resid = bp->b_bcount;
891c77a61a7Syz 
892c77a61a7Syz 	/* Needed as this is a character driver. */
893c77a61a7Syz 	if (bp->b_flags & (B_PHYS | B_PAGEIO)) {
894c77a61a7Syz 		bp_mapin(bp);
895c77a61a7Syz 	}
896c77a61a7Syz 
897c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
898c77a61a7Syz 
899c77a61a7Syz 	/* Make sure device has not been disconnected. */
900c77a61a7Syz 	if (usbvcp->usbvc_dev_state != USB_DEV_ONLINE) {
901c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
902c77a61a7Syz 		    "usbvc_strategy: device can't be accessed");
903c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
904c77a61a7Syz 
905c77a61a7Syz 		goto fail;
906c77a61a7Syz 	}
907c77a61a7Syz 
908c77a61a7Syz 	/* read data from uv_buf_done list */
909c77a61a7Syz 	if (usbvc_read_buf(usbvcp, bp) != USB_SUCCESS) {
910c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
911c77a61a7Syz 		    "usbvc_strategy: read full buf list fail");
912c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
913c77a61a7Syz 
914c77a61a7Syz 		goto fail;
915c77a61a7Syz 	}
916c77a61a7Syz 
917c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
918c77a61a7Syz 
919c77a61a7Syz 	biodone(bp);
920c77a61a7Syz 
921c77a61a7Syz 	return (0);
922c77a61a7Syz 
923c77a61a7Syz fail:
924c77a61a7Syz 	USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
925c77a61a7Syz 	    "usbvc_strategy: strategy fail");
926c77a61a7Syz 	bp->b_private = NULL;
927c77a61a7Syz 
928c77a61a7Syz 	bioerror(bp, EIO);
929c77a61a7Syz 	biodone(bp);
930c77a61a7Syz 
931c77a61a7Syz 	return (0);
932c77a61a7Syz }
933c77a61a7Syz 
934c77a61a7Syz 
935c77a61a7Syz static void
936c77a61a7Syz usbvc_minphys(struct buf *bp)
937c77a61a7Syz {
938c77a61a7Syz 	dev_t			dev = bp->b_edev;
939c77a61a7Syz 	usbvc_stream_if_t	*strm_if;
940c77a61a7Syz 	uint32_t		maxsize;
941ef772e54Sfb 	usbvc_state_t		*usbvcp =
942ef772e54Sfb 	    ddi_get_soft_state(usbvc_statep, getminor(dev));
943c77a61a7Syz 
944c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
945c77a61a7Syz 	strm_if = usbvcp->usbvc_curr_strm;
946c77a61a7Syz 	LE_TO_UINT32(strm_if->ctrl_pc.dwMaxVideoFrameSize, 0, maxsize);
947c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
948c77a61a7Syz 	    "usbvc_minphys: max read size=%d", maxsize);
949c77a61a7Syz 
950c77a61a7Syz 	if (bp->b_bcount > maxsize) {
951c77a61a7Syz 		bp->b_bcount = maxsize;
952c77a61a7Syz 	}
953c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
954c77a61a7Syz }
955c77a61a7Syz 
956c77a61a7Syz 
957c77a61a7Syz /*
958c77a61a7Syz  * ioctl entry.
959c77a61a7Syz  */
960c77a61a7Syz /*ARGSUSED*/
961c77a61a7Syz static int
962c77a61a7Syz usbvc_ioctl(dev_t dev, int cmd, intptr_t arg,
963c77a61a7Syz 		int mode, cred_t *cred_p, int *rval_p)
964c77a61a7Syz {
965c77a61a7Syz 	int		rv = 0;
966c77a61a7Syz 	usbvc_state_t	*usbvcp =
967c77a61a7Syz 	    ddi_get_soft_state(usbvc_statep, getminor(dev));
968c77a61a7Syz 
969c77a61a7Syz 	if (usbvcp == NULL) {
970c77a61a7Syz 
971c77a61a7Syz 		return (ENXIO);
972c77a61a7Syz 	}
973c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
974c77a61a7Syz 	    "ioctl enter, cmd=%x", cmd);
975c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
976c77a61a7Syz 	if (usbvcp->usbvc_dev_state != USB_DEV_ONLINE) {
977c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
978c77a61a7Syz 		    "ioctl: Device is not online,"
979c77a61a7Syz 		    " dev_stat=%d", usbvcp->usbvc_dev_state);
980c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
981c77a61a7Syz 
982c77a61a7Syz 		return (EFAULT);
983c77a61a7Syz 	}
984c77a61a7Syz 	if (usbvc_serialize_access(usbvcp, USBVC_SER_SIG) <= 0) {
985c77a61a7Syz 		usbvcp->usbvc_serial_inuse = B_FALSE;
986c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
987c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
988c77a61a7Syz 		    "serialize_access failed.");
989c77a61a7Syz 
990c77a61a7Syz 		return (EFAULT);
991c77a61a7Syz 	}
992c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
993c77a61a7Syz 
994c77a61a7Syz 	rv = usbvc_v4l2_ioctl(usbvcp, cmd, arg, mode);
995c77a61a7Syz 
996c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
997c77a61a7Syz 	usbvc_release_access(usbvcp);
998c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
999c77a61a7Syz 
1000c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
1001c77a61a7Syz 	    "usbvc_ioctl exit");
1002c77a61a7Syz 
1003c77a61a7Syz 	return (rv);
1004c77a61a7Syz }
1005c77a61a7Syz 
1006c77a61a7Syz 
1007c77a61a7Syz /* Entry for mmap system call */
1008c77a61a7Syz static int
1009c77a61a7Syz usbvc_devmap(dev_t dev, devmap_cookie_t handle, offset_t off,
1010c77a61a7Syz 	size_t len, size_t *maplen, uint_t model)
1011c77a61a7Syz {
1012c77a61a7Syz 	usbvc_state_t		*usbvcp;
1013c77a61a7Syz 	int			error, i;
1014c77a61a7Syz 	usbvc_buf_t		*buf = NULL;
1015c77a61a7Syz 	usbvc_stream_if_t	*strm_if;
1016c77a61a7Syz 	usbvc_buf_grp_t		*bufgrp;
1017c77a61a7Syz 
1018c77a61a7Syz 	usbvcp = ddi_get_soft_state(usbvc_statep, getminor(dev));
1019c77a61a7Syz 	if (usbvcp == NULL) {
1020c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_DEVMAP, usbvcp->usbvc_log_handle,
1021c77a61a7Syz 		    "usbvc_devmap: usbvcp == NULL");
1022c77a61a7Syz 
1023c77a61a7Syz 		return (ENXIO);
1024c77a61a7Syz 	}
1025c77a61a7Syz 
1026c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_DEVMAP, usbvcp->usbvc_log_handle,
1027112116d8Sfb 	    "devmap: memory map for instance(%d), off=%llx,"
1028112116d8Sfb 	    "len=%ld, maplen=%ld, model=%d", getminor(dev), off,
1029112116d8Sfb 	    len, *maplen, model);
1030c77a61a7Syz 
1031c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
1032c77a61a7Syz 	(void) usbvc_serialize_access(usbvcp, USBVC_SER_NOSIG);
1033c77a61a7Syz 	strm_if = usbvcp->usbvc_curr_strm;
1034c77a61a7Syz 	if (!strm_if) {
1035c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_DEVMAP, usbvcp->usbvc_log_handle,
1036c77a61a7Syz 		    "usbvc_devmap: No current strm if");
1037c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
1038c77a61a7Syz 
1039c77a61a7Syz 		return (ENXIO);
1040c77a61a7Syz 	}
1041c77a61a7Syz 	bufgrp = &strm_if->buf_map;
1042c77a61a7Syz 	for (i = 0; i < bufgrp->buf_cnt; i++) {
1043c77a61a7Syz 		if (bufgrp->buf_head[i].v4l2_buf.m.offset == off) {
1044c77a61a7Syz 			buf = &bufgrp->buf_head[i];
1045c77a61a7Syz 
1046c77a61a7Syz 			break;
1047c77a61a7Syz 		}
1048c77a61a7Syz 	}
1049c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_DEVMAP, usbvcp->usbvc_log_handle,
1050c77a61a7Syz 	    "usbvc_devmap: idx=%d", i);
1051c77a61a7Syz 	if (buf == NULL) {
1052c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
1053c77a61a7Syz 
1054c77a61a7Syz 		return (ENXIO);
1055c77a61a7Syz 	}
1056c77a61a7Syz 	/*
1057c77a61a7Syz 	 * round up len to a multiple of a page size, according to chapter
1058c77a61a7Syz 	 * 10 of "writing device drivers"
1059c77a61a7Syz 	 */
1060c77a61a7Syz 	len = ptob(btopr(len));
1061c77a61a7Syz 	if (len > ptob(btopr(buf->len))) {
1062c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_DEVMAP, usbvcp->usbvc_log_handle,
1063112116d8Sfb 		    "usbvc_devmap: len=0x%lx", len);
1064c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
1065c77a61a7Syz 
1066c77a61a7Syz 		return (ENXIO);
1067c77a61a7Syz 	}
1068c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
1069c77a61a7Syz 
1070c77a61a7Syz 	error = devmap_umem_setup(handle, usbvcp->usbvc_dip, NULL,
1071ef772e54Sfb 	    buf->umem_cookie, off, len, PROT_ALL, DEVMAP_DEFAULTS, NULL);
1072c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
1073c77a61a7Syz 	*maplen = len;
1074c77a61a7Syz 	if (error == 0 && buf->status == USBVC_BUF_INIT) {
1075c77a61a7Syz 		buf->status = USBVC_BUF_MAPPED;
1076c77a61a7Syz 	} else {
1077c77a61a7Syz 		USB_DPRINTF_L3(PRINT_MASK_DEVMAP, usbvcp->usbvc_log_handle,
1078c77a61a7Syz 		    "usbvc_devmap: devmap_umem_setup, err=%d", error);
1079c77a61a7Syz 	}
1080c77a61a7Syz 
1081c77a61a7Syz 	(void) usbvc_release_access(usbvcp);
1082c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
1083c77a61a7Syz 
1084c77a61a7Syz 	return (error);
1085c77a61a7Syz }
1086c77a61a7Syz 
1087c77a61a7Syz /*
1088c77a61a7Syz  * pm and cpr
1089c77a61a7Syz  */
1090c77a61a7Syz 
1091c77a61a7Syz /*
1092112116d8Sfb  *  usbvc_power :
1093c77a61a7Syz  *	Power entry point, the workhorse behind pm_raise_power, pm_lower_power,
1094c77a61a7Syz  *	usb_req_raise_power and usb_req_lower_power.
1095c77a61a7Syz  */
1096c77a61a7Syz /* ARGSUSED */
1097c77a61a7Syz static int
1098c77a61a7Syz usbvc_power(dev_info_t *dip, int comp, int level)
1099c77a61a7Syz {
1100c77a61a7Syz 	usbvc_state_t	*usbvcp;
1101c77a61a7Syz 	usbvc_power_t	*pm;
1102c77a61a7Syz 	int		rval = USB_FAILURE;
1103c77a61a7Syz 
1104c77a61a7Syz 	usbvcp = ddi_get_soft_state(usbvc_statep, ddi_get_instance(dip));
1105c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
1106c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1107c77a61a7Syz 	    "usbvc_power: enter: level = %d, dev_state: %x",
1108c77a61a7Syz 	    level, usbvcp->usbvc_dev_state);
1109c77a61a7Syz 
1110c77a61a7Syz 	if (usbvcp->usbvc_pm == NULL) {
1111c77a61a7Syz 
1112c77a61a7Syz 		goto done;
1113c77a61a7Syz 	}
1114c77a61a7Syz 
1115c77a61a7Syz 	pm = usbvcp->usbvc_pm;
1116c77a61a7Syz 
1117c77a61a7Syz 	/* Check if we are transitioning to a legal power level */
1118c77a61a7Syz 	if (USB_DEV_PWRSTATE_OK(pm->usbvc_pwr_states, level)) {
1119c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
1120c77a61a7Syz 		    "usbvc_power: illegal power level = %d "
1121c77a61a7Syz 		    "pwr_states: %x", level, pm->usbvc_pwr_states);
1122c77a61a7Syz 
1123c77a61a7Syz 		goto done;
1124c77a61a7Syz 	}
1125c77a61a7Syz 	/*
1126c77a61a7Syz 	 * if we are about to raise power and asked to lower power, fail
1127c77a61a7Syz 	 */
1128c77a61a7Syz 	if (pm->usbvc_raise_power && (level < (int)pm->usbvc_current_power)) {
1129c77a61a7Syz 
1130c77a61a7Syz 		goto done;
1131c77a61a7Syz 	}
1132c77a61a7Syz 	switch (level) {
1133c77a61a7Syz 	case USB_DEV_OS_PWR_OFF :
1134c77a61a7Syz 		rval = usbvc_pwrlvl0(usbvcp);
1135c77a61a7Syz 
1136c77a61a7Syz 		break;
1137c77a61a7Syz 	case USB_DEV_OS_PWR_1 :
1138c77a61a7Syz 		rval = usbvc_pwrlvl1(usbvcp);
1139c77a61a7Syz 
1140c77a61a7Syz 		break;
1141c77a61a7Syz 	case USB_DEV_OS_PWR_2 :
1142c77a61a7Syz 		rval = usbvc_pwrlvl2(usbvcp);
1143c77a61a7Syz 
1144c77a61a7Syz 		break;
1145c77a61a7Syz 	case USB_DEV_OS_FULL_PWR :
1146c77a61a7Syz 		rval = usbvc_pwrlvl3(usbvcp);
1147c77a61a7Syz 
1148c77a61a7Syz 		break;
1149c77a61a7Syz 	}
1150c77a61a7Syz 
1151c77a61a7Syz done:
1152c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
1153c77a61a7Syz 
1154c77a61a7Syz 	return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
1155c77a61a7Syz }
1156c77a61a7Syz 
1157c77a61a7Syz 
1158c77a61a7Syz /*
1159c77a61a7Syz  * usbvc_init_power_mgmt:
1160c77a61a7Syz  *	Initialize power management and remote wakeup functionality.
1161c77a61a7Syz  *	No mutex is necessary in this function as it's called only by attach.
1162c77a61a7Syz  */
1163c77a61a7Syz static void
1164c77a61a7Syz usbvc_init_power_mgmt(usbvc_state_t *usbvcp)
1165c77a61a7Syz {
1166c77a61a7Syz 	usbvc_power_t	*usbvcpm;
1167c77a61a7Syz 	uint_t		pwr_states;
1168c77a61a7Syz 
1169c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1170c77a61a7Syz 	    "init_power_mgmt enter");
1171c77a61a7Syz 
1172c77a61a7Syz 	/* Allocate the state structure */
1173c77a61a7Syz 	usbvcpm = kmem_zalloc(sizeof (usbvc_power_t), KM_SLEEP);
1174c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
1175c77a61a7Syz 	usbvcp->usbvc_pm = usbvcpm;
1176c77a61a7Syz 	usbvcpm->usbvc_state = usbvcp;
1177c77a61a7Syz 	usbvcpm->usbvc_pm_capabilities = 0;
1178c77a61a7Syz 	usbvcpm->usbvc_current_power = USB_DEV_OS_FULL_PWR;
1179c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
1180c77a61a7Syz 
1181c77a61a7Syz 	if (usb_create_pm_components(usbvcp->usbvc_dip, &pwr_states) ==
1182ef772e54Sfb 	    USB_SUCCESS) {
1183c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1184c77a61a7Syz 		    "usbvc_init_power_mgmt: created PM components");
1185c77a61a7Syz 
1186c77a61a7Syz 		if (usb_handle_remote_wakeup(usbvcp->usbvc_dip,
1187c77a61a7Syz 		    USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) {
1188c77a61a7Syz 			usbvcpm->usbvc_wakeup_enabled = 1;
1189c77a61a7Syz 		} else {
1190c77a61a7Syz 			USB_DPRINTF_L2(PRINT_MASK_ATTA,
1191c77a61a7Syz 			    usbvcp->usbvc_log_handle, "usbvc_init_power_mgmt:"
1192c77a61a7Syz 			    " remote wakeup not supported");
1193c77a61a7Syz 		}
1194c77a61a7Syz 
1195c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
1196c77a61a7Syz 		usbvcpm->usbvc_pwr_states = (uint8_t)pwr_states;
1197c77a61a7Syz 		usbvc_pm_busy_component(usbvcp);
1198c77a61a7Syz 		usbvcpm->usbvc_raise_power = B_TRUE;
1199c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
1200c77a61a7Syz 
1201c77a61a7Syz 		(void) pm_raise_power(
1202c77a61a7Syz 		    usbvcp->usbvc_dip, 0, USB_DEV_OS_FULL_PWR);
1203c77a61a7Syz 
1204c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
1205c77a61a7Syz 		usbvcpm->usbvc_raise_power = B_FALSE;
1206c77a61a7Syz 		usbvc_pm_idle_component(usbvcp);
1207c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
1208c77a61a7Syz 
1209c77a61a7Syz 	}
1210c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1211c77a61a7Syz 	    "usbvc_init_power_mgmt: end");
1212c77a61a7Syz }
1213c77a61a7Syz 
1214c77a61a7Syz 
1215c77a61a7Syz /*
1216112116d8Sfb  *  usbvc_destroy_power_mgmt:
1217c77a61a7Syz  *	Shut down and destroy power management and remote wakeup functionality.
1218c77a61a7Syz  */
1219c77a61a7Syz static void
1220c77a61a7Syz usbvc_destroy_power_mgmt(usbvc_state_t *usbvcp)
1221c77a61a7Syz {
1222c77a61a7Syz 	usbvc_power_t	*pm;
1223c77a61a7Syz 	int		rval;
1224c77a61a7Syz 
1225c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1226c77a61a7Syz 	    "destroy_power_mgmt enter");
1227c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
1228c77a61a7Syz 	pm = usbvcp->usbvc_pm;
1229c77a61a7Syz 	if (pm && (usbvcp->usbvc_dev_state != USB_DEV_DISCONNECTED)) {
1230c77a61a7Syz 
1231c77a61a7Syz 		usbvc_pm_busy_component(usbvcp);
1232c77a61a7Syz 		if (pm->usbvc_wakeup_enabled) {
1233c77a61a7Syz 			pm->usbvc_raise_power = B_TRUE;
1234c77a61a7Syz 			mutex_exit(&usbvcp->usbvc_mutex);
1235c77a61a7Syz 
1236c77a61a7Syz 			/* First bring the device to full power */
1237c77a61a7Syz 			(void) pm_raise_power(usbvcp->usbvc_dip, 0,
1238c77a61a7Syz 			    USB_DEV_OS_FULL_PWR);
1239c77a61a7Syz 			if ((rval = usb_handle_remote_wakeup(
1240c77a61a7Syz 			    usbvcp->usbvc_dip,
1241c77a61a7Syz 			    USB_REMOTE_WAKEUP_DISABLE)) !=
1242c77a61a7Syz 			    USB_SUCCESS) {
1243c77a61a7Syz 				USB_DPRINTF_L2(PRINT_MASK_ATTA,
1244c77a61a7Syz 				    usbvcp->usbvc_log_handle,
1245c77a61a7Syz 				    "usbvc_destroy_power_mgmt: "
1246c77a61a7Syz 				    "Error disabling rmt wakeup: rval = %d",
1247c77a61a7Syz 				    rval);
1248c77a61a7Syz 			}
1249c77a61a7Syz 			mutex_enter(&usbvcp->usbvc_mutex);
1250c77a61a7Syz 			pm->usbvc_raise_power = B_FALSE;
1251c77a61a7Syz 
1252c77a61a7Syz 		}
1253c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
1254c77a61a7Syz 
1255c77a61a7Syz 		/*
1256c77a61a7Syz 		 * Since remote wakeup is disabled now,
1257c77a61a7Syz 		 * no one can raise power
1258c77a61a7Syz 		 * and get to device once power is lowered here.
1259c77a61a7Syz 		 */
1260c77a61a7Syz 		(void) pm_lower_power(usbvcp->usbvc_dip, 0, USB_DEV_OS_PWR_OFF);
1261c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
1262c77a61a7Syz 		usbvc_pm_idle_component(usbvcp);
1263c77a61a7Syz 	}
1264c77a61a7Syz 
1265c77a61a7Syz 	if (pm) {
1266c77a61a7Syz 		kmem_free(pm, sizeof (usbvc_power_t));
1267c77a61a7Syz 		usbvcp->usbvc_pm = NULL;
1268c77a61a7Syz 	}
1269c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
1270c77a61a7Syz }
1271c77a61a7Syz 
1272c77a61a7Syz 
1273c77a61a7Syz static void
1274c77a61a7Syz usbvc_pm_busy_component(usbvc_state_t *usbvcp)
1275c77a61a7Syz {
1276c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
1277c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1278c77a61a7Syz 	    "usbvc_pm_busy_component: enter");
1279c77a61a7Syz 
1280c77a61a7Syz 	usbvcp->usbvc_pm->usbvc_pm_busy++;
1281c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
1282c77a61a7Syz 
1283c77a61a7Syz 	if (pm_busy_component(usbvcp->usbvc_dip, 0) !=
1284c77a61a7Syz 	    DDI_SUCCESS) {
1285c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
1286c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1287c77a61a7Syz 		    "usbvc_pm_busy_component: pm busy fail, usbvc_pm_busy=%d",
1288c77a61a7Syz 		    usbvcp->usbvc_pm->usbvc_pm_busy);
1289c77a61a7Syz 
1290c77a61a7Syz 		usbvcp->usbvc_pm->usbvc_pm_busy--;
1291c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
1292c77a61a7Syz 	}
1293c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
1294c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1295c77a61a7Syz 	    "usbvc_pm_busy_component: exit");
1296c77a61a7Syz }
1297c77a61a7Syz 
1298c77a61a7Syz 
1299c77a61a7Syz static void
1300c77a61a7Syz usbvc_pm_idle_component(usbvc_state_t *usbvcp)
1301c77a61a7Syz {
1302c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
1303c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1304c77a61a7Syz 	    "usbvc_pm_idle_component: enter");
1305c77a61a7Syz 
1306c77a61a7Syz 	if (usbvcp->usbvc_pm != NULL) {
1307c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
1308c77a61a7Syz 		if (pm_idle_component(usbvcp->usbvc_dip, 0) ==
1309c77a61a7Syz 		    DDI_SUCCESS) {
1310c77a61a7Syz 			mutex_enter(&usbvcp->usbvc_mutex);
1311c77a61a7Syz 			ASSERT(usbvcp->usbvc_pm->usbvc_pm_busy > 0);
1312c77a61a7Syz 			usbvcp->usbvc_pm->usbvc_pm_busy--;
1313c77a61a7Syz 			mutex_exit(&usbvcp->usbvc_mutex);
1314c77a61a7Syz 		}
1315c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
1316c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1317c77a61a7Syz 		    "usbvc_pm_idle_component: %d",
1318c77a61a7Syz 		    usbvcp->usbvc_pm->usbvc_pm_busy);
1319c77a61a7Syz 	}
1320c77a61a7Syz }
1321c77a61a7Syz 
1322c77a61a7Syz 
1323c77a61a7Syz /*
1324c77a61a7Syz  * usbvc_pwrlvl0:
1325c77a61a7Syz  * Functions to handle power transition for OS levels 0 -> 3
1326c77a61a7Syz  */
1327c77a61a7Syz static int
1328c77a61a7Syz usbvc_pwrlvl0(usbvc_state_t *usbvcp)
1329c77a61a7Syz {
1330c77a61a7Syz 	int rval;
1331c77a61a7Syz 
1332c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1333c77a61a7Syz 	    "usbvc_pwrlvl0, dev_state: %x", usbvcp->usbvc_dev_state);
1334c77a61a7Syz 
1335c77a61a7Syz 	switch (usbvcp->usbvc_dev_state) {
1336c77a61a7Syz 	case USB_DEV_ONLINE:
1337c77a61a7Syz 		/* Deny the powerdown request if the device is busy */
1338c77a61a7Syz 		if (usbvcp->usbvc_pm->usbvc_pm_busy != 0) {
1339c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1340c77a61a7Syz 		    "usbvc_pwrlvl0: usbvc_pm_busy");
1341c77a61a7Syz 
1342c77a61a7Syz 			return (USB_FAILURE);
1343c77a61a7Syz 		}
1344c77a61a7Syz 
1345c77a61a7Syz 		/* Issue USB D3 command to the device here */
1346c77a61a7Syz 		rval = usb_set_device_pwrlvl3(usbvcp->usbvc_dip);
1347c77a61a7Syz 		ASSERT(rval == USB_SUCCESS);
1348c77a61a7Syz 
1349c77a61a7Syz 		usbvcp->usbvc_dev_state = USB_DEV_PWRED_DOWN;
1350ef772e54Sfb 		usbvcp->usbvc_pm->usbvc_current_power = USB_DEV_OS_PWR_OFF;
1351ef772e54Sfb 
1352c77a61a7Syz 		/* FALLTHRU */
1353c77a61a7Syz 	case USB_DEV_DISCONNECTED:
1354c77a61a7Syz 	case USB_DEV_SUSPENDED:
1355c77a61a7Syz 		/* allow a disconnect/cpr'ed device to go to lower power */
1356c77a61a7Syz 
1357c77a61a7Syz 		return (USB_SUCCESS);
1358c77a61a7Syz 	case USB_DEV_PWRED_DOWN:
1359c77a61a7Syz 	default:
1360c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1361c77a61a7Syz 		    "usbvc_pwrlvl0: illegal dev state");
1362c77a61a7Syz 
1363c77a61a7Syz 		return (USB_FAILURE);
1364c77a61a7Syz 	}
1365c77a61a7Syz }
1366c77a61a7Syz 
1367c77a61a7Syz 
1368c77a61a7Syz /*
1369c77a61a7Syz  * usbvc_pwrlvl1:
1370c77a61a7Syz  *	Functions to handle power transition to OS levels -> 2
1371c77a61a7Syz  */
1372c77a61a7Syz static int
1373c77a61a7Syz usbvc_pwrlvl1(usbvc_state_t *usbvcp)
1374c77a61a7Syz {
1375c77a61a7Syz 	int	rval;
1376c77a61a7Syz 
1377c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1378c77a61a7Syz 	    "usbvc_pwrlvl1");
1379c77a61a7Syz 
1380c77a61a7Syz 	/* Issue USB D2 command to the device here */
1381c77a61a7Syz 	rval = usb_set_device_pwrlvl2(usbvcp->usbvc_dip);
1382c77a61a7Syz 	ASSERT(rval == USB_SUCCESS);
1383c77a61a7Syz 
1384c77a61a7Syz 	return (USB_FAILURE);
1385c77a61a7Syz }
1386c77a61a7Syz 
1387c77a61a7Syz 
1388c77a61a7Syz /*
1389c77a61a7Syz  * usbvc_pwrlvl2:
1390c77a61a7Syz  *	Functions to handle power transition to OS levels -> 1
1391c77a61a7Syz  */
1392c77a61a7Syz static int
1393c77a61a7Syz usbvc_pwrlvl2(usbvc_state_t *usbvcp)
1394c77a61a7Syz {
1395c77a61a7Syz 	int	rval;
1396c77a61a7Syz 
1397c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1398c77a61a7Syz 	    "usbvc_pwrlvl2");
1399c77a61a7Syz 
1400c77a61a7Syz 	/* Issue USB D1 command to the device here */
1401c77a61a7Syz 	rval = usb_set_device_pwrlvl1(usbvcp->usbvc_dip);
1402c77a61a7Syz 	ASSERT(rval == USB_SUCCESS);
1403c77a61a7Syz 
1404c77a61a7Syz 	return (USB_FAILURE);
1405c77a61a7Syz }
1406c77a61a7Syz 
1407c77a61a7Syz 
1408c77a61a7Syz /*
1409c77a61a7Syz  * usbvc_pwrlvl3:
1410c77a61a7Syz  *	Functions to handle power transition to OS level -> 0
1411c77a61a7Syz  */
1412c77a61a7Syz static int
1413c77a61a7Syz usbvc_pwrlvl3(usbvc_state_t *usbvcp)
1414c77a61a7Syz {
1415c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1416c77a61a7Syz 	    "usbvc_pwrlvl3, dev_stat=%d", usbvcp->usbvc_dev_state);
1417c77a61a7Syz 
1418c77a61a7Syz 	switch (usbvcp->usbvc_dev_state) {
1419c77a61a7Syz 	case USB_DEV_PWRED_DOWN:
1420c77a61a7Syz 		/* Issue USB D0 command to the device here */
1421c77a61a7Syz 		(void) usb_set_device_pwrlvl0(usbvcp->usbvc_dip);
1422c77a61a7Syz 
1423c77a61a7Syz 		usbvcp->usbvc_dev_state = USB_DEV_ONLINE;
1424c77a61a7Syz 		usbvcp->usbvc_pm->usbvc_current_power =
1425c77a61a7Syz 		    USB_DEV_OS_FULL_PWR;
1426c77a61a7Syz 
1427c77a61a7Syz 		/* FALLTHRU */
1428c77a61a7Syz 	case USB_DEV_ONLINE:
1429c77a61a7Syz 		/* we are already in full power */
1430c77a61a7Syz 		/* FALLTHRU */
1431c77a61a7Syz 	case USB_DEV_DISCONNECTED:
1432c77a61a7Syz 	case USB_DEV_SUSPENDED:
1433c77a61a7Syz 		/*
1434c77a61a7Syz 		 * PM framework tries to put us in full power
1435c77a61a7Syz 		 * during system shutdown. If we are disconnected/cpr'ed
1436c77a61a7Syz 		 * return success anyways
1437c77a61a7Syz 		 */
1438c77a61a7Syz 
1439c77a61a7Syz 		return (USB_SUCCESS);
1440c77a61a7Syz 	default:
1441c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1442c77a61a7Syz 		    "usbvc_pwrlvl3: illegal dev state");
1443c77a61a7Syz 
1444c77a61a7Syz 		return (USB_FAILURE);
1445c77a61a7Syz 	}
1446c77a61a7Syz }
1447c77a61a7Syz 
1448c77a61a7Syz 
1449c77a61a7Syz /*
1450c77a61a7Syz  * usbvc_cpr_suspend:
1451c77a61a7Syz  *	Clean up device.
1452c77a61a7Syz  *	Wait for any IO to finish, then close pipes.
1453c77a61a7Syz  *	Quiesce device.
1454c77a61a7Syz  */
1455c77a61a7Syz static void
1456c77a61a7Syz usbvc_cpr_suspend(dev_info_t *dip)
1457c77a61a7Syz {
1458c77a61a7Syz 	int		instance = ddi_get_instance(dip);
1459ef772e54Sfb 	usbvc_state_t	*usbvcp = ddi_get_soft_state(usbvc_statep, instance);
1460c77a61a7Syz 
1461c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1462c77a61a7Syz 	    "usbvc_cpr_suspend enter");
1463c77a61a7Syz 
1464c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
1465c77a61a7Syz 
1466c77a61a7Syz 	/*
1467c77a61a7Syz 	 * Set dev_state to suspended so other driver threads don't start any
1468c77a61a7Syz 	 * new I/O.
1469c77a61a7Syz 	 */
1470c77a61a7Syz 	usbvcp->usbvc_dev_state = USB_DEV_SUSPENDED;
1471c77a61a7Syz 
1472c77a61a7Syz 	/*
1473c77a61a7Syz 	 * Wake up the read threads in case there are any threads are blocking.
1474c77a61a7Syz 	 * After being waked up, those threads will quit immediately since the
1475c77a61a7Syz 	 * dev_state is not ONLINE
1476c77a61a7Syz 	 */
1477c77a61a7Syz 	if (usbvcp->usbvc_io_type == V4L2_MEMORY_MMAP) {
1478c77a61a7Syz 		cv_broadcast(&usbvcp->usbvc_mapio_cv);
1479c77a61a7Syz 	} else {
1480c77a61a7Syz 		cv_broadcast(&usbvcp->usbvc_read_cv);
1481c77a61a7Syz 	}
1482c77a61a7Syz 	/* Wait for the other threads to quit */
1483c77a61a7Syz 	(void) usbvc_serialize_access(usbvcp, USBVC_SER_NOSIG);
1484c77a61a7Syz 	usbvc_release_access(usbvcp);
1485c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
1486c77a61a7Syz 
1487c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
1488c77a61a7Syz 	    "usbvc_cpr_suspend: return");
1489c77a61a7Syz }
1490c77a61a7Syz 
1491c77a61a7Syz 
1492c77a61a7Syz /*
1493c77a61a7Syz  * usbvc_cpr_resume:
1494c77a61a7Syz  *
1495c77a61a7Syz  *	usbvc_restore_device_state marks success by putting device back online
1496c77a61a7Syz  */
1497c77a61a7Syz static void
1498c77a61a7Syz usbvc_cpr_resume(dev_info_t *dip)
1499c77a61a7Syz {
1500c77a61a7Syz 	int		instance = ddi_get_instance(dip);
1501ef772e54Sfb 	usbvc_state_t	*usbvcp = ddi_get_soft_state(usbvc_statep, instance);
1502c77a61a7Syz 
1503c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
1504c77a61a7Syz 	    "resume: enter");
1505c77a61a7Syz 
1506c77a61a7Syz 	/*
1507c77a61a7Syz 	 * NOTE: A pm_raise_power in usbvc_restore_device_state will bring
1508c77a61a7Syz 	 * the power-up state of device into synch with the system.
1509c77a61a7Syz 	 */
1510c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
1511c77a61a7Syz 	usbvc_restore_device_state(dip, usbvcp);
1512c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
1513c77a61a7Syz }
1514c77a61a7Syz 
1515c77a61a7Syz 
1516c77a61a7Syz /*
1517112116d8Sfb  *  usbvc_restore_device_state:
1518c77a61a7Syz  *	Called during hotplug-reconnect and resume.
1519c77a61a7Syz  *		reenable power management
1520c77a61a7Syz  *		Verify the device is the same as before the disconnect/suspend.
1521c77a61a7Syz  *		Restore device state
1522c77a61a7Syz  *		Thaw any IO which was frozen.
1523c77a61a7Syz  *		Quiesce device.  (Other routines will activate if thawed IO.)
1524c77a61a7Syz  *		Set device online.
1525c77a61a7Syz  *		Leave device disconnected if there are problems.
1526c77a61a7Syz  */
1527c77a61a7Syz static void
1528c77a61a7Syz usbvc_restore_device_state(dev_info_t *dip, usbvc_state_t *usbvcp)
1529c77a61a7Syz {
1530c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1531c77a61a7Syz 	    "usbvc_restore_device_state: enter");
1532c77a61a7Syz 
1533c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
1534c77a61a7Syz 
1535c77a61a7Syz 	ASSERT((usbvcp->usbvc_dev_state == USB_DEV_DISCONNECTED) ||
1536c77a61a7Syz 	    (usbvcp->usbvc_dev_state == USB_DEV_SUSPENDED));
1537c77a61a7Syz 
1538c77a61a7Syz 	usbvc_pm_busy_component(usbvcp);
1539c77a61a7Syz 	usbvcp->usbvc_pm->usbvc_raise_power = B_TRUE;
1540c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
1541c77a61a7Syz 	(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
1542c77a61a7Syz 
1543c77a61a7Syz 	/* Check if we are talking to the same device */
1544c77a61a7Syz 	if (usb_check_same_device(dip, usbvcp->usbvc_log_handle,
1545c77a61a7Syz 	    USB_LOG_L0, PRINT_MASK_ALL,
1546c77a61a7Syz 	    USB_CHK_BASIC|USB_CHK_CFG, NULL) != USB_SUCCESS) {
1547c77a61a7Syz 
1548c77a61a7Syz 		goto fail;
1549c77a61a7Syz 	}
1550c77a61a7Syz 
1551c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
1552c77a61a7Syz 	usbvcp->usbvc_pm->usbvc_raise_power = B_FALSE;
1553c77a61a7Syz 	usbvcp->usbvc_dev_state = USB_DEV_ONLINE;
1554c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
1555c77a61a7Syz 
1556c77a61a7Syz 	if (usbvcp->usbvc_pm->usbvc_wakeup_enabled) {
1557c77a61a7Syz 
1558c77a61a7Syz 		/* Failure here means device disappeared again. */
1559c77a61a7Syz 		if (usb_handle_remote_wakeup(dip, USB_REMOTE_WAKEUP_ENABLE) !=
1560c77a61a7Syz 		    USB_SUCCESS) {
1561c77a61a7Syz 			USB_DPRINTF_L2(PRINT_MASK_ATTA,
1562c77a61a7Syz 			    usbvcp->usbvc_log_handle,
1563c77a61a7Syz 			    "device may or may not be accessible. "
1564c77a61a7Syz 			    "Please verify reconnection");
1565c77a61a7Syz 		}
1566c77a61a7Syz 	}
1567c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
1568c77a61a7Syz 
1569c77a61a7Syz 	usbvc_pm_idle_component(usbvcp);
1570c77a61a7Syz 
1571c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1572c77a61a7Syz 	    "usbvc_restore_device_state: end");
1573c77a61a7Syz 
1574c77a61a7Syz 	return;
1575c77a61a7Syz 
1576c77a61a7Syz fail:
1577c77a61a7Syz 	/* change the device state from suspended to disconnected */
1578c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
1579c77a61a7Syz 	usbvcp->usbvc_dev_state = USB_DEV_DISCONNECTED;
1580c77a61a7Syz 	usbvc_pm_idle_component(usbvcp);
1581c77a61a7Syz }
1582c77a61a7Syz 
1583c77a61a7Syz 
1584c77a61a7Syz /* Events */
1585c77a61a7Syz 
1586c77a61a7Syz /*
1587c77a61a7Syz  * usbvc_disconnect_event_cb:
1588c77a61a7Syz  *	Called when device hotplug-removed.
1589c77a61a7Syz  *		Close pipes. (This does not attempt to contact device.)
1590c77a61a7Syz  *		Set state to DISCONNECTED
1591c77a61a7Syz  */
1592c77a61a7Syz static int
1593c77a61a7Syz usbvc_disconnect_event_cb(dev_info_t *dip)
1594c77a61a7Syz {
1595c77a61a7Syz 	int		instance = ddi_get_instance(dip);
1596ef772e54Sfb 	usbvc_state_t	*usbvcp = ddi_get_soft_state(usbvc_statep, instance);
1597c77a61a7Syz 
1598c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_HOTPLUG, usbvcp->usbvc_log_handle,
1599c77a61a7Syz 	    "disconnect: enter");
1600c77a61a7Syz 
1601c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
1602c77a61a7Syz 	/*
1603c77a61a7Syz 	 * Save any state of device or IO in progress required by
1604c77a61a7Syz 	 * usbvc_restore_device_state for proper device "thawing" later.
1605c77a61a7Syz 	 */
1606c77a61a7Syz 	usbvcp->usbvc_dev_state = USB_DEV_DISCONNECTED;
1607c77a61a7Syz 
1608c77a61a7Syz 	/*
1609c77a61a7Syz 	 * wake up the read threads in case there are any threads are blocking,
1610c77a61a7Syz 	 * after being waked up, those threads will quit fail immediately since
1611c77a61a7Syz 	 * we have changed the dev_stat.
1612c77a61a7Syz 	 */
1613c77a61a7Syz 	if (usbvcp->usbvc_io_type == V4L2_MEMORY_MMAP) {
1614c77a61a7Syz 		cv_broadcast(&usbvcp->usbvc_mapio_cv);
1615c77a61a7Syz 	} else {
1616c77a61a7Syz 		cv_broadcast(&usbvcp->usbvc_read_cv);
1617c77a61a7Syz 	}
1618c77a61a7Syz 	/* Wait for the other threads to quit */
1619c77a61a7Syz 	(void) usbvc_serialize_access(usbvcp, USBVC_SER_SIG);
1620c77a61a7Syz 	usbvc_release_access(usbvcp);
1621c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
1622c77a61a7Syz 
1623c77a61a7Syz 	return (USB_SUCCESS);
1624c77a61a7Syz }
1625c77a61a7Syz 
1626c77a61a7Syz 
1627c77a61a7Syz /*
1628c77a61a7Syz  * usbvc_reconnect_event_cb:
1629c77a61a7Syz  *	Called with device hotplug-inserted
1630c77a61a7Syz  *		Restore state
1631c77a61a7Syz  */
1632c77a61a7Syz static int
1633c77a61a7Syz usbvc_reconnect_event_cb(dev_info_t *dip)
1634c77a61a7Syz {
1635c77a61a7Syz 	int		instance = ddi_get_instance(dip);
1636c77a61a7Syz 	usbvc_state_t	*usbvcp = ddi_get_soft_state(usbvc_statep, instance);
1637c77a61a7Syz 
1638c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_HOTPLUG, usbvcp->usbvc_log_handle,
1639c77a61a7Syz 	    "reconnect: enter");
1640c77a61a7Syz 
1641c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
1642c77a61a7Syz 	(void) usbvc_serialize_access(usbvcp, USBVC_SER_SIG);
1643c77a61a7Syz 	usbvc_restore_device_state(dip, usbvcp);
1644c77a61a7Syz 	usbvc_release_access(usbvcp);
1645c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
1646c77a61a7Syz 
1647c77a61a7Syz 	return (USB_SUCCESS);
1648c77a61a7Syz }
1649c77a61a7Syz 
1650c77a61a7Syz /* Sync objs and lists */
1651c77a61a7Syz 
1652c77a61a7Syz /*
1653c77a61a7Syz  * init/fini sync objects during attach
1654c77a61a7Syz  */
1655c77a61a7Syz static void
1656c77a61a7Syz usbvc_init_sync_objs(usbvc_state_t *usbvcp)
1657c77a61a7Syz {
1658c77a61a7Syz 	mutex_init(&usbvcp->usbvc_mutex, NULL, MUTEX_DRIVER,
1659ef772e54Sfb 	    usbvcp->usbvc_reg->dev_iblock_cookie);
1660c77a61a7Syz 
1661c77a61a7Syz 	cv_init(&usbvcp->usbvc_serial_cv, NULL, CV_DRIVER, NULL);
1662c77a61a7Syz 	cv_init(&usbvcp->usbvc_read_cv, NULL, CV_DRIVER, NULL);
1663c77a61a7Syz 	cv_init(&usbvcp->usbvc_mapio_cv, NULL, CV_DRIVER, NULL);
1664c77a61a7Syz 
1665c77a61a7Syz 	usbvcp->usbvc_serial_inuse = B_FALSE;
1666c77a61a7Syz 
1667c77a61a7Syz 	usbvcp->usbvc_locks_initialized = B_TRUE;
1668c77a61a7Syz }
1669c77a61a7Syz 
1670c77a61a7Syz 
1671c77a61a7Syz static void
1672c77a61a7Syz usbvc_fini_sync_objs(usbvc_state_t *usbvcp)
1673c77a61a7Syz {
1674c77a61a7Syz 	cv_destroy(&usbvcp->usbvc_serial_cv);
1675c77a61a7Syz 	cv_destroy(&usbvcp->usbvc_read_cv);
1676c77a61a7Syz 	cv_destroy(&usbvcp->usbvc_mapio_cv);
1677c77a61a7Syz 
1678c77a61a7Syz 	mutex_destroy(&usbvcp->usbvc_mutex);
1679c77a61a7Syz }
1680c77a61a7Syz 
1681c77a61a7Syz 
1682c77a61a7Syz static void
1683c77a61a7Syz usbvc_init_lists(usbvc_state_t *usbvcp)
1684c77a61a7Syz {
1685c77a61a7Syz 	/* video terminals */
1686c77a61a7Syz 	list_create(&(usbvcp->usbvc_term_list), sizeof (usbvc_terms_t),
1687c77a61a7Syz 	    offsetof(usbvc_terms_t, term_node));
1688c77a61a7Syz 
1689c77a61a7Syz 	/* video units */
1690c77a61a7Syz 	list_create(&(usbvcp->usbvc_unit_list), sizeof (usbvc_units_t),
1691c77a61a7Syz 	    offsetof(usbvc_units_t, unit_node));
1692c77a61a7Syz 
1693c77a61a7Syz 	/* stream interfaces */
1694c77a61a7Syz 	list_create(&(usbvcp->usbvc_stream_list), sizeof (usbvc_stream_if_t),
1695c77a61a7Syz 	    offsetof(usbvc_stream_if_t, stream_if_node));
1696c77a61a7Syz }
1697c77a61a7Syz 
1698c77a61a7Syz 
1699c77a61a7Syz /*
1700c77a61a7Syz  * Free all the data structures allocated when parsing descriptors of ctrl
1701c77a61a7Syz  * and stream interfaces. It is safe to call this function because it always
1702c77a61a7Syz  * checks the pointer before free mem.
1703c77a61a7Syz  */
1704c77a61a7Syz static void
1705c77a61a7Syz usbvc_fini_lists(usbvc_state_t *usbvcp)
1706c77a61a7Syz {
1707c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
1708c77a61a7Syz 	    "usbvc_fini_lists: enter");
1709c77a61a7Syz 
1710c77a61a7Syz 	usbvc_free_ctrl_descr(usbvcp);
1711c77a61a7Syz 
1712c77a61a7Syz 	/* Free all video stream structure and the sub-structures */
1713c77a61a7Syz 	usbvc_free_stream_descr(usbvcp);
1714c77a61a7Syz 
1715c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
1716c77a61a7Syz 	    "usbvc_fini_lists: end");
1717c77a61a7Syz }
1718c77a61a7Syz 
1719c77a61a7Syz 
1720c77a61a7Syz /*
1721c77a61a7Syz  * Free all the data structures allocated when parsing descriptors of ctrl
1722c77a61a7Syz  * interface.
1723c77a61a7Syz  */
1724c77a61a7Syz static void
1725c77a61a7Syz usbvc_free_ctrl_descr(usbvc_state_t *usbvcp)
1726c77a61a7Syz {
1727c77a61a7Syz 	usbvc_terms_t	*term;
1728c77a61a7Syz 	usbvc_units_t	*unit;
1729c77a61a7Syz 
1730c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
1731c77a61a7Syz 	    "usbvc_free_ctrl_descr: enter");
1732c77a61a7Syz 
1733c77a61a7Syz 	if (usbvcp->usbvc_vc_header) {
1734c77a61a7Syz 		kmem_free(usbvcp->usbvc_vc_header, sizeof (usbvc_vc_header_t));
1735c77a61a7Syz 	}
1736c77a61a7Syz 
1737c77a61a7Syz 	/* Free all video terminal structure */
1738c77a61a7Syz 	while (!list_is_empty(&usbvcp->usbvc_term_list)) {
1739c77a61a7Syz 			term = list_head(&usbvcp->usbvc_term_list);
1740c77a61a7Syz 			if (term != NULL) {
1741c77a61a7Syz 				list_remove(&(usbvcp->usbvc_term_list), term);
1742c77a61a7Syz 				kmem_free(term, sizeof (usbvc_terms_t));
1743c77a61a7Syz 			}
1744c77a61a7Syz 	}
1745c77a61a7Syz 
1746c77a61a7Syz 	/* Free all video unit structure */
1747c77a61a7Syz 	while (!list_is_empty(&usbvcp->usbvc_unit_list)) {
1748c77a61a7Syz 			unit = list_head(&usbvcp->usbvc_unit_list);
1749c77a61a7Syz 			if (unit != NULL) {
1750c77a61a7Syz 				list_remove(&(usbvcp->usbvc_unit_list), unit);
1751c77a61a7Syz 				kmem_free(unit, sizeof (usbvc_units_t));
1752c77a61a7Syz 			}
1753c77a61a7Syz 	}
1754c77a61a7Syz }
1755c77a61a7Syz 
1756c77a61a7Syz 
1757c77a61a7Syz /*
1758c77a61a7Syz  * Free all the data structures allocated when parsing descriptors of stream
1759c77a61a7Syz  * interfaces.
1760c77a61a7Syz  */
1761c77a61a7Syz static void
1762c77a61a7Syz usbvc_free_stream_descr(usbvc_state_t *usbvcp)
1763c77a61a7Syz {
1764c77a61a7Syz 	usbvc_stream_if_t	*strm;
1765c77a61a7Syz 	usbvc_input_header_t	*in_hdr;
1766c77a61a7Syz 	usbvc_output_header_t	*out_hdr;
1767c77a61a7Syz 	uint8_t			fmt_cnt, frm_cnt;
1768c77a61a7Syz 
1769c77a61a7Syz 	while (!list_is_empty(&usbvcp->usbvc_stream_list)) {
1770c77a61a7Syz 		USB_DPRINTF_L3(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
1771c77a61a7Syz 		    "usbvc_fini_lists: stream list not empty.");
1772c77a61a7Syz 
1773c77a61a7Syz 		strm = list_head(&usbvcp->usbvc_stream_list);
1774c77a61a7Syz 		if (strm != NULL) {
1775c77a61a7Syz 
1776c77a61a7Syz 			/* unlink this stream's data structure from the list */
1777c77a61a7Syz 			list_remove(&(usbvcp->usbvc_stream_list), strm);
1778c77a61a7Syz 		} else {
1779c77a61a7Syz 
1780c77a61a7Syz 			/* No real stream data structure in the list */
1781c77a61a7Syz 			return;
1782c77a61a7Syz 		}
1783c77a61a7Syz 
1784c77a61a7Syz 		in_hdr = strm->input_header;
1785c77a61a7Syz 		out_hdr = strm->output_header;
1786c77a61a7Syz 
1787c77a61a7Syz 		if (in_hdr) {
1788c77a61a7Syz 			fmt_cnt = in_hdr->descr->bNumFormats;
1789c77a61a7Syz 		} else if (out_hdr) {
1790c77a61a7Syz 			fmt_cnt = out_hdr->descr->bNumFormats;
1791c77a61a7Syz 		}
1792c77a61a7Syz 
1793c77a61a7Syz 		USB_DPRINTF_L3(PRINT_MASK_CLOSE,
1794c77a61a7Syz 		    usbvcp->usbvc_log_handle, "usbvc_fini_lists:"
1795c77a61a7Syz 		    " fmtgrp cnt=%d", fmt_cnt);
1796c77a61a7Syz 
1797c77a61a7Syz 		/* Free headers */
1798c77a61a7Syz 		if (in_hdr) {
1799c77a61a7Syz 			kmem_free(in_hdr, sizeof (usbvc_input_header_t));
1800c77a61a7Syz 		}
1801c77a61a7Syz 		if (out_hdr) {
1802c77a61a7Syz 			kmem_free(out_hdr, sizeof (usbvc_output_header_t));
1803c77a61a7Syz 		}
1804c77a61a7Syz 
1805c77a61a7Syz 		/* Free format descriptors */
1806c77a61a7Syz 		if (strm->format_group) {
1807c77a61a7Syz 			int i;
1808c77a61a7Syz 			usbvc_format_group_t *fmtgrp;
1809c77a61a7Syz 
1810c77a61a7Syz 			for (i = 0; i < fmt_cnt; i++) {
1811c77a61a7Syz 				fmtgrp = &strm->format_group[i];
1812c77a61a7Syz 				if (fmtgrp->format == NULL) {
1813c77a61a7Syz 
1814c77a61a7Syz 					break;
1815c77a61a7Syz 				}
1816c77a61a7Syz 				if (fmtgrp->still) {
1817c77a61a7Syz 					kmem_free(fmtgrp->still,
1818c77a61a7Syz 					    sizeof (usbvc_still_image_frame_t));
1819c77a61a7Syz 				}
1820c77a61a7Syz 				frm_cnt = fmtgrp->format->bNumFrameDescriptors;
1821c77a61a7Syz 
1822c77a61a7Syz 				USB_DPRINTF_L3(PRINT_MASK_CLOSE,
1823c77a61a7Syz 				    usbvcp->usbvc_log_handle,
1824c77a61a7Syz 				    "usbvc_fini_lists:"
1825c77a61a7Syz 				    " frame cnt=%d", frm_cnt);
1826c77a61a7Syz 
1827c77a61a7Syz 				if (fmtgrp->frames) {
1828c77a61a7Syz 					kmem_free(fmtgrp->frames,
1829c77a61a7Syz 					    sizeof (usbvc_frames_t) * frm_cnt);
1830c77a61a7Syz 				}
1831c77a61a7Syz 			}
1832c77a61a7Syz 			kmem_free(strm->format_group,
1833c77a61a7Syz 			    sizeof (usbvc_format_group_t) * fmt_cnt);
1834c77a61a7Syz 		}
1835c77a61a7Syz 		USB_DPRINTF_L3(PRINT_MASK_CLOSE,
1836c77a61a7Syz 		    usbvcp->usbvc_log_handle, "usbvc_fini_lists:"
1837c77a61a7Syz 		    " free stream_if_t");
1838c77a61a7Syz 
1839c77a61a7Syz 		kmem_free(strm, sizeof (usbvc_stream_if_t));
1840c77a61a7Syz 	}
1841c77a61a7Syz }
1842c77a61a7Syz 
1843c77a61a7Syz /*
1844c77a61a7Syz  * Parse class specific descriptors of the video device
1845c77a61a7Syz  */
1846c77a61a7Syz 
1847c77a61a7Syz /*
1848c77a61a7Syz  * Check the length of a class specific descriptor. Make sure cvs_buf_len is
1849c77a61a7Syz  * not less than the length expected according to uvc spec.
1850c77a61a7Syz  *
1851c77a61a7Syz  * Args:
1852c77a61a7Syz  * - off_num: the cvs_buf offset of the descriptor element that
1853c77a61a7Syz  *   indicates the number of variable descriptor elements;
1854c77a61a7Syz  * - size: the size of each variable descriptor element, if zero, then the
1855c77a61a7Syz  *   size value is offered by off_size;
1856c77a61a7Syz  * - off_size: the cvs_buf offset of the descriptor element that indicates
1857c77a61a7Syz  *   the size of each variable descriptor element;
1858c77a61a7Syz  */
1859c77a61a7Syz static int
1860c77a61a7Syz usbvc_chk_descr_len(uint8_t off_num, uint8_t size, uint8_t off_size,
1861c77a61a7Syz     usb_cvs_data_t *cvs_data)
1862c77a61a7Syz {
1863c77a61a7Syz 	uchar_t			*cvs_buf;
1864c77a61a7Syz 	uint_t			cvs_buf_len;
1865c77a61a7Syz 
1866c77a61a7Syz 	cvs_buf = cvs_data->cvs_buf;
1867c77a61a7Syz 	cvs_buf_len = cvs_data->cvs_buf_len;
1868c77a61a7Syz 
1869c77a61a7Syz 	if (size == 0) {
1870c77a61a7Syz 		if (cvs_buf_len > off_size) {
1871c77a61a7Syz 			size = cvs_buf[off_size];
1872c77a61a7Syz 		} else {
1873c77a61a7Syz 
1874c77a61a7Syz 			return (USB_FAILURE);
1875c77a61a7Syz 		}
1876c77a61a7Syz 	}
1877c77a61a7Syz 	if (cvs_buf_len < (off_num + 1)) {
1878c77a61a7Syz 
1879c77a61a7Syz 		return (USB_FAILURE);
1880c77a61a7Syz 	}
1881c77a61a7Syz 
1882c77a61a7Syz 	if (cvs_buf_len < (cvs_buf[off_num] * size + off_num +1)) {
1883c77a61a7Syz 
1884c77a61a7Syz 		return (USB_FAILURE);
1885c77a61a7Syz 	}
1886c77a61a7Syz 
1887c77a61a7Syz 	return (USB_SUCCESS);
1888c77a61a7Syz }
1889c77a61a7Syz 
1890c77a61a7Syz 
1891c77a61a7Syz /* Parse the descriptors of control interface */
1892c77a61a7Syz static int
1893c77a61a7Syz usbvc_parse_ctrl_if(usbvc_state_t *usbvcp)
1894c77a61a7Syz {
1895c77a61a7Syz 	int			if_num;
1896c77a61a7Syz 	int			cvs_num;
1897c77a61a7Syz 	usb_alt_if_data_t	*if_alt_data;
1898c77a61a7Syz 	usb_cvs_data_t		*cvs_data;
1899c77a61a7Syz 	uchar_t			*cvs_buf;
1900c77a61a7Syz 	uint_t			cvs_buf_len;
1901c77a61a7Syz 	uint16_t		version;
1902c77a61a7Syz 
1903c77a61a7Syz 	if_num = usbvcp->usbvc_reg->dev_curr_if;
1904c77a61a7Syz 	if_alt_data = usbvcp->usbvc_reg->dev_curr_cfg->cfg_if[if_num].if_alt;
1905c77a61a7Syz 	cvs_data = if_alt_data->altif_cvs;
1906c77a61a7Syz 
1907c77a61a7Syz 	for (cvs_num = 0; cvs_num < if_alt_data->altif_n_cvs; cvs_num++) {
1908c77a61a7Syz 		cvs_buf = cvs_data[cvs_num].cvs_buf;
1909c77a61a7Syz 		cvs_buf_len = cvs_data[cvs_num].cvs_buf_len;
1910c77a61a7Syz 		USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
1911c77a61a7Syz 		    "usbvc_parse_ctrl_if: cvs_num= %d, cvs_buf_len=%d",
1912c77a61a7Syz 		    cvs_num, cvs_buf_len);
1913c77a61a7Syz 
1914c77a61a7Syz 		/*
1915c77a61a7Syz 		 * parse interface cvs descriptors here; by checking
1916c77a61a7Syz 		 * bDescriptorType (cvs_buf[1])
1917c77a61a7Syz 		 */
1918c77a61a7Syz 		if (cvs_buf[1] != CS_INTERFACE) {
1919c77a61a7Syz 
1920c77a61a7Syz 			continue;
1921c77a61a7Syz 		}
1922c77a61a7Syz 
1923c77a61a7Syz 		/*
1924c77a61a7Syz 		 * Different descriptors in VC interface; according to
1925c77a61a7Syz 		 * bDescriptorSubType (cvs_buf[2])
1926c77a61a7Syz 		 */
1927c77a61a7Syz 		switch (cvs_buf[2]) {
1928c77a61a7Syz 		case VC_HEADER:
1929c77a61a7Syz 
1930c77a61a7Syz 			/*
1931c77a61a7Syz 			 * According to uvc spec, there must be one and only
1932c77a61a7Syz 			 * be one header. If more than one, return failure.
1933c77a61a7Syz 			 */
1934c77a61a7Syz 			if (usbvcp->usbvc_vc_header) {
1935c77a61a7Syz 
1936c77a61a7Syz 				return (USB_FAILURE);
1937c77a61a7Syz 			}
1938c77a61a7Syz 			/*
1939c77a61a7Syz 			 * Check if it is a valid HEADER descriptor in case of
1940c77a61a7Syz 			 * a device not compliant to uvc spec. This descriptor
1941c77a61a7Syz 			 * is critical, return failure if not a valid one.
1942c77a61a7Syz 			 */
1943c77a61a7Syz 			if (usbvc_chk_descr_len(11, 1, 0, cvs_data) !=
1944c77a61a7Syz 			    USB_SUCCESS) {
1945c77a61a7Syz 
1946c77a61a7Syz 				return (USB_FAILURE);
1947c77a61a7Syz 			}
1948c77a61a7Syz 			usbvcp->usbvc_vc_header =
1949c77a61a7Syz 			    (usbvc_vc_header_t *)kmem_zalloc(
1950c77a61a7Syz 			    sizeof (usbvc_vc_header_t), KM_SLEEP);
1951c77a61a7Syz 			usbvcp->usbvc_vc_header->descr =
1952ef772e54Sfb 			    (usbvc_vc_header_descr_t *)&cvs_buf[0];
1953c77a61a7Syz 
1954c77a61a7Syz 			LE_TO_UINT16(usbvcp->usbvc_vc_header->descr->bcdUVC,
1955c77a61a7Syz 			    0, version);
1956c77a61a7Syz 			USB_DPRINTF_L3(PRINT_MASK_ATTA,
1957c77a61a7Syz 			    usbvcp->usbvc_log_handle, "usbvc_parse_ctrl_if:"
1958c77a61a7Syz 			    " VC header, bcdUVC=%x", version);
1959c77a61a7Syz 			if (usbvcp->usbvc_vc_header->descr->bInCollection ==
1960c77a61a7Syz 			    0) {
1961c77a61a7Syz 				USB_DPRINTF_L3(PRINT_MASK_ATTA,
1962c77a61a7Syz 				    usbvcp->usbvc_log_handle,
1963c77a61a7Syz 				    "usbvc_parse_ctrl_if: no strm interfaces");
1964c77a61a7Syz 
1965c77a61a7Syz 				break;
1966c77a61a7Syz 			}
1967c77a61a7Syz 
1968c77a61a7Syz 			/* stream interface numbers */
1969c77a61a7Syz 			usbvcp->usbvc_vc_header->baInterfaceNr = &cvs_buf[12];
1970c77a61a7Syz 
1971c77a61a7Syz 			break;
1972c77a61a7Syz 		case VC_INPUT_TERMINAL:
1973c77a61a7Syz 		{
1974c77a61a7Syz 			usbvc_terms_t *term;
1975c77a61a7Syz 
1976c77a61a7Syz 			/*
1977c77a61a7Syz 			 * Check if it is a valid descriptor in case of a
1978c77a61a7Syz 			 * device not compliant to uvc spec
1979c77a61a7Syz 			 */
1980c77a61a7Syz 			if (cvs_buf_len < USBVC_I_TERM_LEN_MIN) {
1981c77a61a7Syz 
1982c77a61a7Syz 				break;
1983c77a61a7Syz 			}
1984c77a61a7Syz 			term = (usbvc_terms_t *)
1985c77a61a7Syz 			    kmem_zalloc(sizeof (usbvc_terms_t), KM_SLEEP);
1986c77a61a7Syz 			term->descr = (usbvc_term_descr_t *)cvs_buf;
1987c77a61a7Syz 
1988c77a61a7Syz 			USB_DPRINTF_L3(PRINT_MASK_ATTA,
1989c77a61a7Syz 			    usbvcp->usbvc_log_handle, "usbvc_parse_ctrl_if: "
1990c77a61a7Syz 			    "input term type=%x", term->descr->wTerminalType);
1991c77a61a7Syz 			if (term->descr->wTerminalType == ITT_CAMERA) {
1992c77a61a7Syz 				if (usbvc_chk_descr_len(14, 1, 0, cvs_data) !=
1993c77a61a7Syz 				    USB_SUCCESS) {
1994c77a61a7Syz 					kmem_free(term, sizeof (usbvc_terms_t));
1995c77a61a7Syz 
1996c77a61a7Syz 					break;
1997c77a61a7Syz 				}
1998c77a61a7Syz 				term->bmControls = &cvs_buf[15];
1999c77a61a7Syz 			} else if (cvs_buf_len > 8) { /* other input terms */
2000c77a61a7Syz 				term->bSpecific = &cvs_buf[8];
2001c77a61a7Syz 			}
2002c77a61a7Syz 			list_insert_tail(&(usbvcp->usbvc_term_list), term);
2003c77a61a7Syz 
2004c77a61a7Syz 			break;
2005c77a61a7Syz 		}
2006c77a61a7Syz 		case VC_OUTPUT_TERMINAL:
2007c77a61a7Syz 		{
2008c77a61a7Syz 			usbvc_terms_t *term;
2009c77a61a7Syz 
2010c77a61a7Syz 			if (cvs_buf_len < USBVC_O_TERM_LEN_MIN) {
2011c77a61a7Syz 
2012c77a61a7Syz 				break;
2013c77a61a7Syz 			}
2014c77a61a7Syz 			term = (usbvc_terms_t *)
2015c77a61a7Syz 			    kmem_zalloc(sizeof (usbvc_terms_t), KM_SLEEP);
2016c77a61a7Syz 			term->descr = (usbvc_term_descr_t *)cvs_buf;
2017c77a61a7Syz 
2018c77a61a7Syz 			USB_DPRINTF_L3(PRINT_MASK_ATTA,
2019c77a61a7Syz 			    usbvcp->usbvc_log_handle, "usbvc_parse_ctrl_if:"
2020c77a61a7Syz 			    " output term id= %x", term->descr->bTerminalID);
2021c77a61a7Syz 			if (cvs_buf_len > 9) {
2022c77a61a7Syz 				term->bSpecific = &cvs_buf[9];
2023c77a61a7Syz 			}
2024c77a61a7Syz 			list_insert_tail(&(usbvcp->usbvc_term_list), term);
2025c77a61a7Syz 
2026c77a61a7Syz 			break;
2027c77a61a7Syz 		}
2028c77a61a7Syz 		case VC_PROCESSING_UNIT:
2029c77a61a7Syz 		{
2030c77a61a7Syz 			uint8_t sz;
2031c77a61a7Syz 			usbvc_units_t *unit;
2032c77a61a7Syz 
2033c77a61a7Syz 			if (usbvc_chk_descr_len(7, 1, 0, cvs_data) !=
2034c77a61a7Syz 			    USB_SUCCESS) {
2035c77a61a7Syz 
2036c77a61a7Syz 				break;
2037c77a61a7Syz 			}
2038c77a61a7Syz 
2039c77a61a7Syz 			/* bControlSize */
2040c77a61a7Syz 			sz = cvs_buf[7];
2041c77a61a7Syz 
2042c77a61a7Syz 			if ((sz + 8) >= cvs_buf_len) {
2043c77a61a7Syz 
2044c77a61a7Syz 				break;
2045c77a61a7Syz 			}
2046c77a61a7Syz 			unit = (usbvc_units_t *)
2047c77a61a7Syz 			    kmem_zalloc(sizeof (usbvc_units_t), KM_SLEEP);
2048c77a61a7Syz 
2049c77a61a7Syz 			unit->descr = (usbvc_unit_descr_t *)cvs_buf;
2050c77a61a7Syz 
2051c77a61a7Syz 			USB_DPRINTF_L3(PRINT_MASK_ATTA,
2052c77a61a7Syz 			    usbvcp->usbvc_log_handle, "usbvc_parse_ctrl_if: "
2053c77a61a7Syz 			    "unit type=%x", unit->descr->bDescriptorSubType);
2054c77a61a7Syz 
2055c77a61a7Syz 			if (sz != 0) {
2056c77a61a7Syz 				unit->bmControls = &cvs_buf[8];
2057c77a61a7Syz 			}
2058c77a61a7Syz 			unit->iProcessing = cvs_buf[8 + sz];
2059c77a61a7Syz 
2060c77a61a7Syz 			/*
2061c77a61a7Syz 			 * video class 1.1 version add one element
2062c77a61a7Syz 			 * (bmVideoStandards) to processing unit descriptor
2063c77a61a7Syz 			 */
2064c77a61a7Syz 			if (cvs_buf_len > (9 + sz)) {
2065c77a61a7Syz 				unit->bmVideoStandards = cvs_buf[9 + sz];
2066c77a61a7Syz 			}
2067c77a61a7Syz 			list_insert_tail(&(usbvcp->usbvc_unit_list), unit);
2068c77a61a7Syz 
2069c77a61a7Syz 			break;
2070c77a61a7Syz 		}
2071c77a61a7Syz 		case VC_SELECTOR_UNIT:
2072c77a61a7Syz 		{
2073c77a61a7Syz 			uint8_t  pins;
2074c77a61a7Syz 			usbvc_units_t *unit;
2075c77a61a7Syz 
2076c77a61a7Syz 			if (usbvc_chk_descr_len(4, 1, 0, cvs_data) !=
2077c77a61a7Syz 			    USB_SUCCESS) {
2078c77a61a7Syz 
2079c77a61a7Syz 				break;
2080c77a61a7Syz 			}
2081c77a61a7Syz 			pins = cvs_buf[4];
2082c77a61a7Syz 			if ((pins + 5) >= cvs_buf_len) {
2083c77a61a7Syz 
2084c77a61a7Syz 				break;
2085c77a61a7Syz 			}
2086c77a61a7Syz 			unit = (usbvc_units_t *)
2087c77a61a7Syz 			    kmem_zalloc(sizeof (usbvc_units_t), KM_SLEEP);
2088c77a61a7Syz 
2089c77a61a7Syz 			unit->descr = (usbvc_unit_descr_t *)cvs_buf;
2090c77a61a7Syz 
2091c77a61a7Syz 			USB_DPRINTF_L3(PRINT_MASK_ATTA,
2092c77a61a7Syz 			    usbvcp->usbvc_log_handle, "usbvc_parse_ctrl_if: "
2093c77a61a7Syz 			    "unit type=%x", unit->descr->bDescriptorSubType);
2094c77a61a7Syz 			if (pins > 0) {
2095c77a61a7Syz 				unit->baSourceID = &cvs_buf[5];
2096c77a61a7Syz 			}
2097c77a61a7Syz 			unit->iSelector = cvs_buf[5 + pins];
2098c77a61a7Syz 
2099c77a61a7Syz 			list_insert_tail(&(usbvcp->usbvc_unit_list), unit);
2100c77a61a7Syz 
2101c77a61a7Syz 			break;
2102c77a61a7Syz 		}
2103c77a61a7Syz 		case VC_EXTENSION_UNIT:
2104c77a61a7Syz 		{
2105c77a61a7Syz 			uint8_t  pins, n;
2106c77a61a7Syz 			usbvc_units_t *unit;
2107c77a61a7Syz 
2108c77a61a7Syz 			if (usbvc_chk_descr_len(21, 1, 0, cvs_data) !=
2109c77a61a7Syz 			    USB_SUCCESS) {
2110c77a61a7Syz 
2111c77a61a7Syz 				break;
2112c77a61a7Syz 			}
2113c77a61a7Syz 			pins = cvs_buf[21];
2114c77a61a7Syz 			if ((pins + 22) >= cvs_buf_len) {
2115c77a61a7Syz 
2116c77a61a7Syz 				break;
2117c77a61a7Syz 			}
2118c77a61a7Syz 
2119c77a61a7Syz 			/* Size of bmControls */
2120c77a61a7Syz 			n = cvs_buf[pins + 22];
2121c77a61a7Syz 
2122c77a61a7Syz 			if (usbvc_chk_descr_len(pins + 22, 1, 0, cvs_data) !=
2123c77a61a7Syz 			    USB_SUCCESS) {
2124c77a61a7Syz 
2125c77a61a7Syz 				break;
2126c77a61a7Syz 			}
2127c77a61a7Syz 			if ((23 + pins + n) >= cvs_buf_len) {
2128c77a61a7Syz 
2129c77a61a7Syz 				break;
2130c77a61a7Syz 			}
2131c77a61a7Syz 			unit = (usbvc_units_t *)
2132c77a61a7Syz 			    kmem_zalloc(sizeof (usbvc_units_t), KM_SLEEP);
2133c77a61a7Syz 
2134c77a61a7Syz 			unit->descr = (usbvc_unit_descr_t *)cvs_buf;
2135c77a61a7Syz 
2136c77a61a7Syz 			USB_DPRINTF_L3(PRINT_MASK_ATTA,
2137c77a61a7Syz 			    usbvcp->usbvc_log_handle, "usbvc_parse_ctrl_if: "
2138c77a61a7Syz 			    "unit type=%x", unit->descr->bDescriptorSubType);
2139c77a61a7Syz 			if (pins != 0) {
2140c77a61a7Syz 				unit->baSourceID = &cvs_buf[22];
2141c77a61a7Syz 			}
2142c77a61a7Syz 			unit->bControlSize = cvs_buf[22 + pins];
2143c77a61a7Syz 
2144c77a61a7Syz 			if (unit->bControlSize != 0) {
2145c77a61a7Syz 				unit->bmControls = &cvs_buf[23 + pins];
2146c77a61a7Syz 			}
2147c77a61a7Syz 			unit->iExtension = cvs_buf[23 + pins + n];
2148c77a61a7Syz 
2149c77a61a7Syz 			list_insert_tail(&(usbvcp->usbvc_unit_list), unit);
2150c77a61a7Syz 
2151c77a61a7Syz 			break;
2152c77a61a7Syz 		}
2153c77a61a7Syz 		default:
2154c77a61a7Syz 
2155c77a61a7Syz 			break;
2156c77a61a7Syz 		}
2157c77a61a7Syz 	}
2158c77a61a7Syz 
215926d97b1bSfb 	/*
216026d97b1bSfb 	 * For webcam which is not compliant to video class specification
216126d97b1bSfb 	 * and no header descriptor in VC interface, return USB_FAILURE.
216226d97b1bSfb 	 */
216326d97b1bSfb 	if (!usbvcp->usbvc_vc_header) {
216426d97b1bSfb 		USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
216526d97b1bSfb 		    "usbvc_parse_ctrl_if: no header descriptor");
216626d97b1bSfb 
216726d97b1bSfb 		return (USB_FAILURE);
216826d97b1bSfb 	}
216926d97b1bSfb 
2170c77a61a7Syz 	return (USB_SUCCESS);
2171c77a61a7Syz }
2172c77a61a7Syz 
2173c77a61a7Syz 
2174c77a61a7Syz /* Parse all the cvs descriptors in one stream interface. */
2175c77a61a7Syz usbvc_stream_if_t *
2176c77a61a7Syz usbvc_parse_stream_if(usbvc_state_t *usbvcp, int if_num)
2177c77a61a7Syz {
2178c77a61a7Syz 	usb_alt_if_data_t	*if_alt_data;
2179c77a61a7Syz 	uint_t			i, j;
2180c77a61a7Syz 	usbvc_stream_if_t	*strm_if;
2181c77a61a7Syz 	uint16_t		pktsize;
2182c77a61a7Syz 	uint8_t			ep_adr;
2183c77a61a7Syz 
2184c77a61a7Syz 	strm_if = (usbvc_stream_if_t *)kmem_zalloc(sizeof (usbvc_stream_if_t),
2185c77a61a7Syz 	    KM_SLEEP);
2186c77a61a7Syz 	strm_if->if_descr = &usbvcp->usbvc_reg->dev_curr_cfg->cfg_if[if_num];
2187c77a61a7Syz 	if_alt_data = strm_if->if_descr->if_alt;
2188c77a61a7Syz 	if (usbvc_parse_stream_header(usbvcp, strm_if) != USB_SUCCESS) {
2189c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2190c77a61a7Syz 		    "usbvc_parse_stream_if: parse header fail");
2191c77a61a7Syz 		kmem_free(strm_if, sizeof (usbvc_stream_if_t));
2192c77a61a7Syz 
2193c77a61a7Syz 		return (NULL);
2194c77a61a7Syz 	}
2195c77a61a7Syz 	if (usbvc_parse_format_groups(usbvcp, strm_if) != USB_SUCCESS) {
2196c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2197c77a61a7Syz 		    "usbvc_parse_stream_if: parse groups fail");
2198c77a61a7Syz 		kmem_free(strm_if, sizeof (usbvc_stream_if_t));
2199c77a61a7Syz 
2200c77a61a7Syz 		return (NULL);
2201c77a61a7Syz 	}
2202c77a61a7Syz 
2203c77a61a7Syz 	/* Parse the alternate settings to find the maximum bandwidth. */
2204c77a61a7Syz 	for (i = 0; i < strm_if->if_descr->if_n_alt; i++) {
2205c77a61a7Syz 		if_alt_data = &strm_if->if_descr->if_alt[i];
2206c77a61a7Syz 		for (j = 0; j < if_alt_data->altif_n_ep; j++) {
2207c77a61a7Syz 			ep_adr =
2208c77a61a7Syz 			    if_alt_data->altif_ep[j].ep_descr.bEndpointAddress;
2209c77a61a7Syz 			if (strm_if->input_header != NULL &&
2210c77a61a7Syz 			    ep_adr !=
2211c77a61a7Syz 			    strm_if->input_header->descr->bEndpointAddress) {
2212c77a61a7Syz 
2213c77a61a7Syz 				continue;
2214c77a61a7Syz 			}
2215c77a61a7Syz 			if (strm_if->output_header != NULL &&
2216c77a61a7Syz 			    ep_adr !=
2217c77a61a7Syz 			    strm_if->output_header->descr->bEndpointAddress) {
2218c77a61a7Syz 
2219c77a61a7Syz 				continue;
2220c77a61a7Syz 			}
2221c77a61a7Syz 			pktsize =
2222c77a61a7Syz 			    if_alt_data->altif_ep[j].ep_descr.wMaxPacketSize;
2223c77a61a7Syz 			pktsize = HS_PKT_SIZE(pktsize);
2224c77a61a7Syz 			if (pktsize > strm_if->max_isoc_payload) {
2225c77a61a7Syz 				strm_if->max_isoc_payload = pktsize;
2226c77a61a7Syz 			}
2227c77a61a7Syz 		}
2228c77a61a7Syz 	}
2229c77a61a7Syz 
2230c77a61a7Syz 	/* initialize MJPEC FID toggle */
2231c77a61a7Syz 	strm_if->fid = 0xff;
2232*61b2e298Slc 
2233*61b2e298Slc 	/*
2234*61b2e298Slc 	 * initialize desired number of buffers used internally in read() mode
2235*61b2e298Slc 	 */
2236*61b2e298Slc 	strm_if->buf_read_num = USBVC_DEFAULT_READ_BUF_NUM;
2237*61b2e298Slc 
2238c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2239c77a61a7Syz 	    "usbvc_parse_stream_if: return. max_isoc_payload=%x",
2240c77a61a7Syz 	    strm_if->max_isoc_payload);
2241c77a61a7Syz 
2242c77a61a7Syz 	return (strm_if);
2243c77a61a7Syz }
2244c77a61a7Syz 
2245c77a61a7Syz 
2246c77a61a7Syz /*
2247c77a61a7Syz  * Parse all the stream interfaces asociated with the video control interface.
2248c77a61a7Syz  * This driver will attach to a video control interface on the device,
2249c77a61a7Syz  * there might be multiple video stream interfaces associated with one video
2250c77a61a7Syz  * control interface.
2251c77a61a7Syz  */
2252c77a61a7Syz static int
2253c77a61a7Syz usbvc_parse_stream_ifs(usbvc_state_t *usbvcp)
2254c77a61a7Syz {
2255c77a61a7Syz 	int			i, if_cnt, if_num;
2256c77a61a7Syz 	usbvc_stream_if_t	*strm_if;
2257c77a61a7Syz 
2258c77a61a7Syz 	if_cnt = usbvcp->usbvc_vc_header->descr->bInCollection;
2259c77a61a7Syz 	if (if_cnt == 0) {
2260c77a61a7Syz 		ASSERT(list_is_empty(&usbvcp->usbvc_stream_list));
2261c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2262c77a61a7Syz 		    "usbvc_parse_stream_ifs: no stream interfaces");
2263c77a61a7Syz 
2264c77a61a7Syz 		return (USB_SUCCESS);
2265c77a61a7Syz 	}
2266c77a61a7Syz 	for (i = 0; i < if_cnt; i++) {
2267c77a61a7Syz 		if_num = usbvcp->usbvc_vc_header->baInterfaceNr[i];
2268c77a61a7Syz 		strm_if = usbvc_parse_stream_if(usbvcp, if_num);
2269c77a61a7Syz 		if (strm_if == NULL) {
2270c77a61a7Syz 			USB_DPRINTF_L2(PRINT_MASK_ATTA,
2271c77a61a7Syz 			    usbvcp->usbvc_log_handle, "usbvc_parse_stream_ifs:"
2272c77a61a7Syz 			    " parse stream interface %d failed.", if_num);
2273c77a61a7Syz 
2274c77a61a7Syz 			return (USB_FAILURE);
2275c77a61a7Syz 		}
2276c77a61a7Syz 		/* video data buffers */
2277c77a61a7Syz 		list_create(&(strm_if->buf_map.uv_buf_free),
2278c77a61a7Syz 		    sizeof (usbvc_buf_t), offsetof(usbvc_buf_t, buf_node));
2279c77a61a7Syz 		list_create(&(strm_if->buf_map.uv_buf_done),
2280c77a61a7Syz 		    sizeof (usbvc_buf_t), offsetof(usbvc_buf_t, buf_node));
2281c77a61a7Syz 		list_create(&(strm_if->buf_read.uv_buf_free),
2282c77a61a7Syz 		    sizeof (usbvc_buf_t), offsetof(usbvc_buf_t, buf_node));
2283c77a61a7Syz 		list_create(&(strm_if->buf_read.uv_buf_done),
2284c77a61a7Syz 		    sizeof (usbvc_buf_t), offsetof(usbvc_buf_t, buf_node));
2285c77a61a7Syz 
2286c77a61a7Syz 		list_insert_tail(&(usbvcp->usbvc_stream_list), strm_if);
2287c77a61a7Syz 	}
2288c77a61a7Syz 
2289c77a61a7Syz 	/* Make the first stream interface as the default one. */
2290c77a61a7Syz 	usbvcp->usbvc_curr_strm =
2291c77a61a7Syz 	    (usbvc_stream_if_t *)list_head(&usbvcp->usbvc_stream_list);
2292c77a61a7Syz 
2293c77a61a7Syz 	return (USB_SUCCESS);
2294c77a61a7Syz }
2295c77a61a7Syz 
2296c77a61a7Syz 
2297c77a61a7Syz /*
2298c77a61a7Syz  * Parse colorspace descriptor and still image descriptor of a format group.
2299c77a61a7Syz  * There is only one colorspace or still image descriptor in one format group.
2300c77a61a7Syz  */
2301c77a61a7Syz static void
2302c77a61a7Syz usbvc_parse_color_still(usbvc_state_t *usbvcp, usbvc_format_group_t *fmtgrp,
2303c77a61a7Syz 	usb_cvs_data_t *cvs_data, uint_t cvs_num, uint_t altif_n_cvs)
2304c77a61a7Syz {
2305c77a61a7Syz 	uint8_t		frame_cnt;
2306c77a61a7Syz 	uint_t		last_frame, i;
2307c77a61a7Syz 	uchar_t		*cvs_buf;
2308c77a61a7Syz 	uint_t			cvs_buf_len;
2309c77a61a7Syz 
2310c77a61a7Syz 	frame_cnt = fmtgrp->format->bNumFrameDescriptors;
2311c77a61a7Syz 	last_frame = frame_cnt + cvs_num;
2312c77a61a7Syz 
2313c77a61a7Syz 	/*
2314c77a61a7Syz 	 * Find the still image descr and color format descr if there are any.
2315c77a61a7Syz 	 * UVC Spec: only one still image and one color descr is allowed in
2316c77a61a7Syz 	 * one format group.
2317c77a61a7Syz 	 */
2318c77a61a7Syz 	for (i = 1; i <= 2; i++) {
2319c77a61a7Syz 		if ((last_frame + i) >= altif_n_cvs) {
2320c77a61a7Syz 
2321c77a61a7Syz 			break;
2322c77a61a7Syz 		}
2323c77a61a7Syz 		cvs_buf = cvs_data[last_frame + i].cvs_buf;
2324c77a61a7Syz 		cvs_buf_len = cvs_data[last_frame + i].cvs_buf_len;
2325c77a61a7Syz 
2326c77a61a7Syz 		if (cvs_buf[2] == VS_STILL_IMAGE_FRAME) {
2327c77a61a7Syz 			uint8_t m, n, off;
2328c77a61a7Syz 			usbvc_still_image_frame_t *st;
2329c77a61a7Syz 
2330c77a61a7Syz 			if (usbvc_chk_descr_len(4, 4, 0, cvs_data) !=
2331c77a61a7Syz 			    USB_SUCCESS) {
2332c77a61a7Syz 
2333c77a61a7Syz 				continue;
2334c77a61a7Syz 			}
2335c77a61a7Syz 
2336c77a61a7Syz 			/* Number of Image Size patterns of this format */
2337c77a61a7Syz 			n = cvs_buf[4];
2338c77a61a7Syz 
2339c77a61a7Syz 			/* offset of bNumCompressionPattern */
2340c77a61a7Syz 			off = 9 + 4 * n -4;
2341c77a61a7Syz 
2342c77a61a7Syz 			if (off >= cvs_buf_len) {
2343c77a61a7Syz 
2344c77a61a7Syz 				continue;
2345c77a61a7Syz 			}
2346c77a61a7Syz 
2347c77a61a7Syz 			/* Number of compression pattern of this format */
2348c77a61a7Syz 			m = cvs_buf[off];
2349c77a61a7Syz 
2350c77a61a7Syz 			if (usbvc_chk_descr_len(m, 1, 0, cvs_data) !=
2351c77a61a7Syz 			    USB_SUCCESS) {
2352c77a61a7Syz 
2353c77a61a7Syz 				continue;
2354c77a61a7Syz 			}
2355c77a61a7Syz 			fmtgrp->still = (usbvc_still_image_frame_t *)
2356c77a61a7Syz 			    kmem_zalloc(sizeof (usbvc_still_image_frame_t),
2357c77a61a7Syz 			    KM_SLEEP);
2358c77a61a7Syz 			st = fmtgrp->still;
2359c77a61a7Syz 			st->descr = (usbvc_still_image_frame_descr_t *)cvs_buf;
2360c77a61a7Syz 			n = st->descr->bNumImageSizePatterns;
2361c77a61a7Syz 			if (n > 0) {
2362c77a61a7Syz 				st->width_height =
2363c77a61a7Syz 				    (width_height_t *)&cvs_buf[5];
2364c77a61a7Syz 			}
2365c77a61a7Syz 			st->bNumCompressionPattern = cvs_buf[off];
2366c77a61a7Syz 			if (cvs_buf[off] > 0) {
2367c77a61a7Syz 				st->bCompression = &cvs_buf[off + 1];
2368c77a61a7Syz 			}
2369c77a61a7Syz 		}
2370c77a61a7Syz 		if (cvs_buf[2] == VS_COLORFORMAT) {
2371c77a61a7Syz 			fmtgrp->color = (usbvc_color_matching_descr_t *)cvs_buf;
2372c77a61a7Syz 			fmtgrp->v4l2_color = usbvc_v4l2_colorspace(
2373c77a61a7Syz 			    fmtgrp->color->bColorPrimaries);
2374c77a61a7Syz 		}
2375c77a61a7Syz 	}
2376c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2377c77a61a7Syz 	    "usbvc_parse_color_still: still=%p, color=%p",
2378112116d8Sfb 	    (void *)fmtgrp->still, (void *)fmtgrp->color);
2379c77a61a7Syz }
2380c77a61a7Syz 
2381c77a61a7Syz 
2382c77a61a7Syz /*
2383c77a61a7Syz  * Parse frame descriptors of a format group. There might be multi frame
2384c77a61a7Syz  * descriptors in one format group.
2385c77a61a7Syz  */
2386c77a61a7Syz static void
2387c77a61a7Syz usbvc_parse_frames(usbvc_state_t *usbvcp, usbvc_format_group_t *fmtgrp,
2388c77a61a7Syz 	usb_cvs_data_t *cvs_data, uint_t cvs_num, uint_t altif_n_cvs)
2389c77a61a7Syz {
2390c77a61a7Syz 	uint_t		last_frame;
2391c77a61a7Syz 	usbvc_frames_t	*frm;
2392c77a61a7Syz 	usb_cvs_data_t		*cvs;
2393c77a61a7Syz 	uchar_t		*cvs_buf;
2394c77a61a7Syz 	uint_t			cvs_buf_len;
2395c77a61a7Syz 	uint8_t		i;
2396c77a61a7Syz 	uint8_t		frame_cnt = fmtgrp->format->bNumFrameDescriptors;
2397c77a61a7Syz 
2398c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2399c77a61a7Syz 	    "usbvc_parse_format_group: frame_cnt=%d", frame_cnt);
2400c77a61a7Syz 
2401c77a61a7Syz 	if (frame_cnt == 0) {
2402c77a61a7Syz 		fmtgrp->frames = NULL;
2403c77a61a7Syz 
2404c77a61a7Syz 		return;
2405c77a61a7Syz 	}
2406c77a61a7Syz 
2407c77a61a7Syz 	/* All these mem allocated will be freed in cleanup() */
2408c77a61a7Syz 	fmtgrp->frames = (usbvc_frames_t *)
2409c77a61a7Syz 	    kmem_zalloc(sizeof (usbvc_frames_t) * frame_cnt, KM_SLEEP);
2410c77a61a7Syz 
2411c77a61a7Syz 	last_frame = frame_cnt + cvs_num;
2412c77a61a7Syz 	cvs_num++;
2413c77a61a7Syz 	i = 0;
2414c77a61a7Syz 
2415c77a61a7Syz 	/*
2416c77a61a7Syz 	 * Traverse from the format decr's first frame decr to the the last
2417c77a61a7Syz 	 * frame descr.
2418c77a61a7Syz 	 */
2419c77a61a7Syz 	for (; cvs_num <= last_frame; cvs_num++) {
2420c77a61a7Syz 		USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2421c77a61a7Syz 		    "usbvc_parse_frames: cvs_num=%d, i=%d", cvs_num, i);
2422c77a61a7Syz 		if (cvs_num >= altif_n_cvs) {
2423c77a61a7Syz 			USB_DPRINTF_L3(PRINT_MASK_ATTA,
2424c77a61a7Syz 			    usbvcp->usbvc_log_handle,
2425c77a61a7Syz 			    "usbvc_parse_frames: less frames than "
2426c77a61a7Syz 			    "expected, cvs_num=%d, i=%d", cvs_num, i);
2427c77a61a7Syz 
2428c77a61a7Syz 			break;
2429c77a61a7Syz 		}
2430c77a61a7Syz 		cvs = &cvs_data[cvs_num];
2431c77a61a7Syz 		cvs_buf = cvs->cvs_buf;
2432c77a61a7Syz 		cvs_buf_len = cvs->cvs_buf_len;
2433c77a61a7Syz 		if (cvs_buf_len < USBVC_FRAME_LEN_MIN) {
2434c77a61a7Syz 			i++;
2435c77a61a7Syz 
2436c77a61a7Syz 			continue;
2437c77a61a7Syz 		}
2438c77a61a7Syz 		frm = &fmtgrp->frames[i];
2439c77a61a7Syz 		frm->descr = (usbvc_frame_descr_t *)cvs->cvs_buf;
2440c77a61a7Syz 
2441c77a61a7Syz 		/* Descriptor for discrete frame interval */
2442c77a61a7Syz 		if (frm->descr->bFrameIntervalType > 0) {
2443c77a61a7Syz 			if (usbvc_chk_descr_len(25, 4, 0, cvs) != USB_SUCCESS) {
2444c77a61a7Syz 				frm->descr = NULL;
2445c77a61a7Syz 				i++;
2446c77a61a7Syz 
2447c77a61a7Syz 				continue;
2448c77a61a7Syz 			}
2449c77a61a7Syz 
2450*61b2e298Slc 			frm->dwFrameInterval = (uint8_t *)&cvs_buf[26];
2451c77a61a7Syz 		} else {	/* Continuous interval */
2452c77a61a7Syz 			if (cvs_buf_len < USBVC_FRAME_LEN_CON) {
2453c77a61a7Syz 				frm->descr = NULL;
2454c77a61a7Syz 				i++;
2455c77a61a7Syz 
2456c77a61a7Syz 				continue;
2457c77a61a7Syz 			}
2458c77a61a7Syz 
2459c77a61a7Syz 			/* Continuous frame intervals */
2460c77a61a7Syz 			LE_TO_UINT32(cvs_buf, 26, frm->dwMinFrameInterval);
2461c77a61a7Syz 			LE_TO_UINT32(cvs_buf, 30, frm->dwMaxFrameInterval);
2462c77a61a7Syz 			LE_TO_UINT32(cvs_buf, 34, frm->dwFrameIntervalStep);
2463c77a61a7Syz 		}
2464c77a61a7Syz 
2465c77a61a7Syz 		i++;
2466c77a61a7Syz 	}
2467c77a61a7Syz 	fmtgrp->frame_cnt = i;
2468c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2469c77a61a7Syz 	    "usbvc_parse_frames: %d frames are actually parsed",
2470c77a61a7Syz 	    fmtgrp->frame_cnt);
2471c77a61a7Syz }
2472c77a61a7Syz 
2473c77a61a7Syz 
2474c77a61a7Syz /* Parse one of the format groups in a stream interface */
2475c77a61a7Syz static int
2476c77a61a7Syz usbvc_parse_format_group(usbvc_state_t *usbvcp, usbvc_format_group_t *fmtgrp,
2477c77a61a7Syz 	usb_cvs_data_t *cvs_data, uint_t cvs_num, uint_t altif_n_cvs)
2478c77a61a7Syz {
2479c77a61a7Syz 	usbvc_format_descr_t *fmt;
2480c77a61a7Syz 
2481c77a61a7Syz 	fmt = fmtgrp->format;
2482c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2483ef772e54Sfb 	    "usbvc_parse_format_group: frame_cnt=%d, cvs_num=%d",
2484ef772e54Sfb 	    fmt->bNumFrameDescriptors, cvs_num);
2485c77a61a7Syz 
2486c77a61a7Syz 	switch (fmt->bDescriptorSubType) {
2487c77a61a7Syz 	case VS_FORMAT_UNCOMPRESSED:
2488c77a61a7Syz 		usbvc_parse_color_still(usbvcp, fmtgrp, cvs_data, cvs_num,
2489c77a61a7Syz 		    altif_n_cvs);
2490c77a61a7Syz 		usbvc_parse_frames(usbvcp, fmtgrp, cvs_data, cvs_num,
2491c77a61a7Syz 		    altif_n_cvs);
2492f6517e58Syz 		fmtgrp->v4l2_bpp = fmt->fmt.uncompressed.bBitsPerPixel / 8;
2493c77a61a7Syz 		fmtgrp->v4l2_pixelformat = usbvc_v4l2_guid2fcc(
2494c77a61a7Syz 		    (uint8_t *)&fmt->fmt.uncompressed.guidFormat);
2495c77a61a7Syz 
2496c77a61a7Syz 		break;
2497c77a61a7Syz 	case VS_FORMAT_MJPEG:
2498c77a61a7Syz 		usbvc_parse_color_still(usbvcp, fmtgrp, cvs_data, cvs_num,
2499c77a61a7Syz 		    altif_n_cvs);
2500c77a61a7Syz 		usbvc_parse_frames(usbvcp, fmtgrp, cvs_data, cvs_num,
2501c77a61a7Syz 		    altif_n_cvs);
2502c77a61a7Syz 		fmtgrp->v4l2_bpp = 0;
2503c77a61a7Syz 		fmtgrp->v4l2_pixelformat = V4L2_PIX_FMT_MJPEG;
2504c77a61a7Syz 
2505c77a61a7Syz 		break;
2506c77a61a7Syz 	case VS_FORMAT_MPEG2TS:
2507c77a61a7Syz 	case VS_FORMAT_DV:
2508c77a61a7Syz 	case VS_FORMAT_FRAME_BASED:
2509c77a61a7Syz 	case VS_FORMAT_STREAM_BASED:
2510c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2511c77a61a7Syz 		    "usbvc_parse_format_group: format not supported yet.");
2512c77a61a7Syz 
2513c77a61a7Syz 		return (USB_FAILURE);
2514c77a61a7Syz 	default:
2515c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2516c77a61a7Syz 		    "usbvc_parse_format_group: unknown format.");
2517c77a61a7Syz 
2518c77a61a7Syz 		return (USB_FAILURE);
2519c77a61a7Syz 	}
2520c77a61a7Syz 
2521c77a61a7Syz 	return (USB_SUCCESS);
2522c77a61a7Syz }
2523c77a61a7Syz 
2524c77a61a7Syz 
2525c77a61a7Syz /* Parse the descriptors belong to one format */
2526c77a61a7Syz static int
2527c77a61a7Syz usbvc_parse_format_groups(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
2528c77a61a7Syz {
2529c77a61a7Syz 	usb_alt_if_data_t	*if_alt_data;
2530c77a61a7Syz 	usb_cvs_data_t		*cvs_data;
2531c77a61a7Syz 	uint8_t			fmtgrp_num, fmtgrp_cnt;
2532c77a61a7Syz 	uchar_t			*cvs_buf;
2533c77a61a7Syz 	uint_t			cvs_num = 0;
2534c77a61a7Syz 	usbvc_format_group_t	*fmtgrp;
2535c77a61a7Syz 
2536c77a61a7Syz 	fmtgrp_cnt = 0;
2537c77a61a7Syz 	/*
2538c77a61a7Syz 	 * bNumFormats indicates the number of formats in this stream
2539c77a61a7Syz 	 * interface. On some devices, we see this number is larger than
2540c77a61a7Syz 	 * the truth.
2541c77a61a7Syz 	 */
2542c77a61a7Syz 	if (strm_if->input_header) {
2543c77a61a7Syz 		fmtgrp_cnt = strm_if->input_header->descr->bNumFormats;
2544c77a61a7Syz 	} else if (strm_if->output_header) {
2545c77a61a7Syz 		fmtgrp_cnt = strm_if->output_header->descr->bNumFormats;
2546c77a61a7Syz 	}
2547c77a61a7Syz 	if (!fmtgrp_cnt) {
2548c77a61a7Syz 
2549c77a61a7Syz 		return (USB_FAILURE);
2550c77a61a7Syz 	}
2551c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2552c77a61a7Syz 	    "usbvc_parse_format_groups: fmtgrp_cnt=%d", fmtgrp_cnt);
2553c77a61a7Syz 
2554c77a61a7Syz 	fmtgrp = (usbvc_format_group_t *)
2555c77a61a7Syz 	    kmem_zalloc(sizeof (usbvc_format_group_t) * fmtgrp_cnt, KM_SLEEP);
2556c77a61a7Syz 
2557c77a61a7Syz 	if_alt_data = strm_if->if_descr->if_alt;
2558c77a61a7Syz 	cvs_data = if_alt_data->altif_cvs;
2559c77a61a7Syz 
2560ef772e54Sfb 	for (fmtgrp_num = 0; fmtgrp_num < fmtgrp_cnt &&
2561ef772e54Sfb 	    cvs_num < if_alt_data->altif_n_cvs; cvs_num++) {
2562ef772e54Sfb 		cvs_buf = cvs_data[cvs_num].cvs_buf;
2563ef772e54Sfb 		switch (cvs_buf[2]) {
2564ef772e54Sfb 		case VS_FORMAT_UNCOMPRESSED:
2565ef772e54Sfb 		case VS_FORMAT_MJPEG:
2566ef772e54Sfb 		case VS_FORMAT_MPEG2TS:
2567ef772e54Sfb 		case VS_FORMAT_DV:
2568ef772e54Sfb 		case VS_FORMAT_FRAME_BASED:
2569ef772e54Sfb 		case VS_FORMAT_STREAM_BASED:
2570ef772e54Sfb 			fmtgrp[fmtgrp_num].format =
2571ef772e54Sfb 			    (usbvc_format_descr_t *)cvs_buf;
2572c77a61a7Syz 
2573ef772e54Sfb 			/*
2574ef772e54Sfb 			 * Now cvs_data[cvs_num].cvs_buf is format descriptor,
2575ef772e54Sfb 			 * usbvc_parse_format_group will then parse the frame
2576ef772e54Sfb 			 * descriptors following this format descriptor.
2577ef772e54Sfb 			 */
2578ef772e54Sfb 			(void) usbvc_parse_format_group(usbvcp,
2579ef772e54Sfb 			    &fmtgrp[fmtgrp_num], cvs_data, cvs_num,
2580ef772e54Sfb 			    if_alt_data->altif_n_cvs);
2581c77a61a7Syz 
2582ef772e54Sfb 			fmtgrp_num++;
2583c77a61a7Syz 
2584ef772e54Sfb 			break;
2585ef772e54Sfb 		default:
2586c77a61a7Syz 			break;
2587c77a61a7Syz 		}
2588c77a61a7Syz 	}
2589c77a61a7Syz 
2590ef772e54Sfb 	/* Save the number of parsed format groups. */
2591ef772e54Sfb 	strm_if->fmtgrp_cnt = fmtgrp_num;
2592*61b2e298Slc 	USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2593ef772e54Sfb 	    "usbvc_parse_format_groups: acctually %d formats parsed",
2594ef772e54Sfb 	    fmtgrp_num);
2595ef772e54Sfb 
2596c77a61a7Syz 	/*
2597c77a61a7Syz 	 * If can't find any formats, then free all allocated
2598c77a61a7Syz 	 * usbvc_format_group_t, return failure.
2599c77a61a7Syz 	 */
2600c77a61a7Syz 	if (!(fmtgrp[0].format)) {
2601c77a61a7Syz 		kmem_free(fmtgrp, sizeof (usbvc_format_group_t) * fmtgrp_cnt);
2602c77a61a7Syz 		strm_if->format_group = NULL;
2603c77a61a7Syz 
2604c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2605c77a61a7Syz 		    "usbvc_parse_format_groups: can't find any formats");
2606c77a61a7Syz 
2607c77a61a7Syz 		return (USB_FAILURE);
2608c77a61a7Syz 	}
2609c77a61a7Syz 	strm_if->format_group = fmtgrp;
2610*61b2e298Slc 	USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2611c77a61a7Syz 	    "usbvc_parse_format_groups: %d format groups parsed", fmtgrp_num);
2612c77a61a7Syz 
2613c77a61a7Syz 	return (USB_SUCCESS);
2614c77a61a7Syz }
2615c77a61a7Syz 
2616c77a61a7Syz 
2617c77a61a7Syz /*
2618c77a61a7Syz  * Parse the input/output header in one stream interface.
2619c77a61a7Syz  * UVC Spec: there must be one and only one header in one stream interface.
2620c77a61a7Syz  */
2621c77a61a7Syz int
2622c77a61a7Syz usbvc_parse_stream_header(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
2623c77a61a7Syz {
2624c77a61a7Syz 	usb_alt_if_data_t	*if_alt_data;
2625c77a61a7Syz 	usb_cvs_data_t		*cvs_data;
2626c77a61a7Syz 	int			cvs_num;
2627c77a61a7Syz 	uchar_t			*cvs_buf;
2628c77a61a7Syz 	usbvc_input_header_t	*in_hdr;
2629c77a61a7Syz 	usbvc_output_header_t	*out_hdr;
2630c77a61a7Syz 
2631c77a61a7Syz 	if_alt_data = strm_if->if_descr->if_alt;
2632c77a61a7Syz 	cvs_data = if_alt_data->altif_cvs;
2633c77a61a7Syz 	for (cvs_num = 0; cvs_num < if_alt_data->altif_n_cvs; cvs_num++) {
2634ef772e54Sfb 		cvs_buf = cvs_data[cvs_num].cvs_buf;
2635*61b2e298Slc 		USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2636c77a61a7Syz 		    "usbvc_parse_stream_header: cvs_num= %d", cvs_num);
2637c77a61a7Syz 
2638c77a61a7Syz 		/*
2639c77a61a7Syz 		 * parse interface cvs descriptors here; by checking
2640c77a61a7Syz 		 * bDescriptorType (cvs_buf[1])
2641c77a61a7Syz 		 */
2642c77a61a7Syz 		if (cvs_buf[1] != CS_INTERFACE) {
2643c77a61a7Syz 
2644c77a61a7Syz 			continue;
2645c77a61a7Syz 		}
2646c77a61a7Syz 
2647c77a61a7Syz 		if (cvs_buf[2] == VS_INPUT_HEADER) {
2648c77a61a7Syz 			if (usbvc_chk_descr_len(3, 0, 12, cvs_data) !=
2649c77a61a7Syz 			    USB_SUCCESS) {
2650c77a61a7Syz 
2651c77a61a7Syz 				continue;
2652c77a61a7Syz 			}
2653c77a61a7Syz 
2654c77a61a7Syz 			strm_if->input_header =
2655c77a61a7Syz 			    (usbvc_input_header_t *)
2656c77a61a7Syz 			    kmem_zalloc(sizeof (usbvc_input_header_t),
2657c77a61a7Syz 			    KM_SLEEP);
2658c77a61a7Syz 			in_hdr = strm_if->input_header;
2659c77a61a7Syz 			in_hdr->descr = (usbvc_input_header_descr_t *)cvs_buf;
2660c77a61a7Syz 			if (in_hdr->descr->bNumFormats > 0) {
2661c77a61a7Syz 				in_hdr->bmaControls = &cvs_buf[13];
2662c77a61a7Syz 			}
2663c77a61a7Syz 
2664c77a61a7Syz 			return (USB_SUCCESS);
2665c77a61a7Syz 		} else if (cvs_buf[2] == VS_OUTPUT_HEADER) {
2666c77a61a7Syz 			if (usbvc_chk_descr_len(3, 0, 8, cvs_data) !=
2667c77a61a7Syz 			    USB_SUCCESS) {
2668c77a61a7Syz 
2669c77a61a7Syz 				continue;
2670c77a61a7Syz 			}
2671c77a61a7Syz 			strm_if->output_header =
2672c77a61a7Syz 			    (usbvc_output_header_t *)
2673c77a61a7Syz 			    kmem_zalloc(sizeof (usbvc_output_header_t),
2674c77a61a7Syz 			    KM_SLEEP);
2675c77a61a7Syz 			out_hdr = strm_if->output_header;
2676c77a61a7Syz 			out_hdr->descr =
2677c77a61a7Syz 			    (usbvc_output_header_descr_t *)cvs_buf;
2678c77a61a7Syz 			if (out_hdr->descr->bNumFormats > 0) {
2679c77a61a7Syz 				out_hdr->bmaControls = &cvs_buf[13];
2680c77a61a7Syz 			}
2681c77a61a7Syz 
2682c77a61a7Syz 			return (USB_SUCCESS);
2683c77a61a7Syz 		} else {
2684c77a61a7Syz 
2685c77a61a7Syz 			continue;
2686c77a61a7Syz 		}
2687c77a61a7Syz 	}
2688c77a61a7Syz 	/* Didn't find one header descriptor. */
2689c77a61a7Syz 	USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2690c77a61a7Syz 	    "usbvc_parse_stream_header: FAIL");
2691c77a61a7Syz 
2692c77a61a7Syz 	return (USB_FAILURE);
2693c77a61a7Syz }
2694c77a61a7Syz 
2695c77a61a7Syz /* read I/O functions */
2696c77a61a7Syz 
2697c77a61a7Syz /* Allocate bufs for read I/O method */
2698c77a61a7Syz static int
2699c77a61a7Syz usbvc_alloc_read_bufs(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
2700c77a61a7Syz {
2701c77a61a7Syz 	usbvc_buf_t	*buf;
2702c77a61a7Syz 	uchar_t		*data;
2703c77a61a7Syz 	int		i;
2704c77a61a7Syz 	uint32_t	len;
2705c77a61a7Syz 
2706c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
2707c77a61a7Syz 
2708c77a61a7Syz 	LE_TO_UINT32(strm_if->ctrl_pc.dwMaxVideoFrameSize, 0, len);
2709c77a61a7Syz 	if (!len) {
2710c77a61a7Syz 
2711c77a61a7Syz 		return (USB_FAILURE);
2712c77a61a7Syz 	}
2713*61b2e298Slc 	for (i = 0; i < strm_if->buf_read_num; i++) {
2714c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
2715c77a61a7Syz 		buf = (usbvc_buf_t *)kmem_zalloc(sizeof (usbvc_buf_t),
2716c77a61a7Syz 		    KM_SLEEP);
2717c77a61a7Syz 		data = (uchar_t *)kmem_zalloc(len, KM_SLEEP);
2718c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
2719c77a61a7Syz 		buf->data = data;
2720c77a61a7Syz 		buf->len = len;
2721c77a61a7Syz 		list_insert_tail(&(strm_if->buf_read.uv_buf_free), buf);
2722c77a61a7Syz 	}
2723*61b2e298Slc 	strm_if->buf_read.buf_cnt = strm_if->buf_read_num;
2724c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
2725c77a61a7Syz 	    "read_bufs: %d bufs allocated", strm_if->buf_read.buf_cnt);
2726c77a61a7Syz 
2727c77a61a7Syz 	return (USB_SUCCESS);
2728c77a61a7Syz }
2729c77a61a7Syz 
2730c77a61a7Syz 
2731c77a61a7Syz /* Read a done buf, copy data to bp. This function is for read I/O method */
2732c77a61a7Syz static int
2733c77a61a7Syz usbvc_read_buf(usbvc_state_t *usbvcp, struct buf *bp)
2734c77a61a7Syz {
2735c77a61a7Syz 	usbvc_buf_t	*buf;
2736c77a61a7Syz 
2737c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
2738c77a61a7Syz 
2739c77a61a7Syz 	/* read a buf from full list and then put it to free list */
2740c77a61a7Syz 	buf = list_head(&usbvcp->usbvc_curr_strm->buf_read.uv_buf_done);
2741c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
2742c77a61a7Syz 	    "usbvc_read_buf: buf=%p, buf->filled=%d, bfu->len=%d,"
2743112116d8Sfb 	    " bp->b_bcount=%ld, bp->b_resid=%lu",
2744112116d8Sfb 	    (void *)buf, buf->filled, buf->len, bp->b_bcount, bp->b_resid);
2745c77a61a7Syz 
2746c77a61a7Syz 	list_remove(&usbvcp->usbvc_curr_strm->buf_read.uv_buf_done, buf);
2747c77a61a7Syz 	bcopy(buf->data, bp->b_un.b_addr, buf->filled);
2748c77a61a7Syz 	bp->b_private = NULL;
2749c77a61a7Syz 	bp->b_resid = bp->b_bcount - buf->filled;
2750c77a61a7Syz 	list_insert_tail(&usbvcp->usbvc_curr_strm->buf_read.uv_buf_free, buf);
2751c77a61a7Syz 
2752c77a61a7Syz 	return (USB_SUCCESS);
2753c77a61a7Syz }
2754c77a61a7Syz 
2755c77a61a7Syz 
2756c77a61a7Syz /* Free one buf which is for read/write IO style */
2757c77a61a7Syz static void
2758c77a61a7Syz usbvc_free_read_buf(usbvc_buf_t *buf)
2759c77a61a7Syz {
2760c77a61a7Syz 	if (buf != NULL) {
2761c77a61a7Syz 		if (buf->data) {
2762c77a61a7Syz 			kmem_free(buf->data, buf->len);
2763c77a61a7Syz 		}
2764c77a61a7Syz 		kmem_free(buf, sizeof (usbvc_buf_t));
2765c77a61a7Syz 	}
2766c77a61a7Syz }
2767c77a61a7Syz 
2768c77a61a7Syz 
2769c77a61a7Syz /* Free all bufs which are for read/write IO style */
2770c77a61a7Syz static void
2771c77a61a7Syz usbvc_free_read_bufs(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
2772c77a61a7Syz {
2773c77a61a7Syz 	usbvc_buf_t	*buf;
2774c77a61a7Syz 
2775c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
2776c77a61a7Syz 
2777c77a61a7Syz 	if (!strm_if) {
2778c77a61a7Syz 
2779c77a61a7Syz 		return;
2780c77a61a7Syz 	}
2781c77a61a7Syz 	buf = strm_if->buf_read.buf_filling;
2782c77a61a7Syz 	usbvc_free_read_buf(buf);
2783c77a61a7Syz 	strm_if->buf_read.buf_filling = NULL;
2784c77a61a7Syz 
2785c77a61a7Syz 	while (!list_is_empty(&strm_if->buf_read.uv_buf_free)) {
2786c77a61a7Syz 		buf = list_head(&strm_if->buf_read.uv_buf_free);
2787c77a61a7Syz 		if (buf != NULL) {
2788c77a61a7Syz 			list_remove(&(strm_if->buf_read.uv_buf_free), buf);
2789c77a61a7Syz 			usbvc_free_read_buf(buf);
2790c77a61a7Syz 		}
2791c77a61a7Syz 	}
2792c77a61a7Syz 	while (!list_is_empty(&strm_if->buf_read.uv_buf_done)) {
2793c77a61a7Syz 		buf = list_head(&strm_if->buf_read.uv_buf_done);
2794c77a61a7Syz 		if (buf != NULL) {
2795c77a61a7Syz 			list_remove(&(strm_if->buf_read.uv_buf_done), buf);
2796c77a61a7Syz 			usbvc_free_read_buf(buf);
2797c77a61a7Syz 		}
2798c77a61a7Syz 	}
2799c77a61a7Syz 	strm_if->buf_read.buf_cnt = 0;
2800c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
2801c77a61a7Syz 	    "usbvc_free_read_bufs: return");
2802c77a61a7Syz }
2803c77a61a7Syz 
2804c77a61a7Syz 
2805c77a61a7Syz /*
2806c77a61a7Syz  * Allocate bufs for mapped I/O , return the number of allocated bufs
2807c77a61a7Syz  * if success, return 0 if fail.
2808c77a61a7Syz  */
2809c77a61a7Syz int
2810c77a61a7Syz usbvc_alloc_map_bufs(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if,
2811c77a61a7Syz 	int buf_cnt, int buf_len)
2812c77a61a7Syz {
2813c77a61a7Syz 	int		i = 0;
2814c77a61a7Syz 	usbvc_buf_t	*bufs;
2815c77a61a7Syz 
2816c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
2817c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
2818c77a61a7Syz 	    "usbvc_alloc_map_bufs: bufcnt=%d, buflen=%d", buf_cnt, buf_len);
2819c77a61a7Syz 	if (buf_len <= 0 || buf_cnt <= 0) {
2820c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
2821c77a61a7Syz 		    "usbvc_alloc_map_bufs: len<=0, cnt<=0");
2822c77a61a7Syz 
2823c77a61a7Syz 		return (0);
2824c77a61a7Syz 	}
2825c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
2826c77a61a7Syz 
2827c77a61a7Syz 	bufs = (usbvc_buf_t *) kmem_zalloc(sizeof (usbvc_buf_t) * buf_cnt,
2828c77a61a7Syz 	    KM_SLEEP);
2829c77a61a7Syz 
2830c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
2831c77a61a7Syz 	strm_if->buf_map.buf_head = bufs;
2832c77a61a7Syz 	buf_len = ptob(btopr(buf_len));
2833c77a61a7Syz 
2834c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
2835c77a61a7Syz 	bufs[0].data = ddi_umem_alloc(buf_len * buf_cnt, DDI_UMEM_SLEEP,
2836ef772e54Sfb 	    &bufs[0].umem_cookie);
2837c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
2838c77a61a7Syz 
2839c77a61a7Syz 	for (i = 0; i < buf_cnt; i++) {
2840c77a61a7Syz 		bufs[i].len = buf_len;
2841c77a61a7Syz 		bufs[i].data = bufs[0].data + (buf_len * i);
2842c77a61a7Syz 		bufs[i].umem_cookie = bufs[0].umem_cookie;
2843c77a61a7Syz 		bufs[i].status = USBVC_BUF_INIT;
2844c77a61a7Syz 
2845c77a61a7Syz 		bufs[i].v4l2_buf.index = i;
2846c77a61a7Syz 		bufs[i].v4l2_buf.m.offset = i * bufs[i].len;
2847c77a61a7Syz 		bufs[i].v4l2_buf.length = bufs[i].len;
2848c77a61a7Syz 		bufs[i].v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
2849c77a61a7Syz 		bufs[i].v4l2_buf.sequence = 0;
2850c77a61a7Syz 		bufs[i].v4l2_buf.field = V4L2_FIELD_NONE;
2851c77a61a7Syz 		bufs[i].v4l2_buf.memory = V4L2_MEMORY_MMAP;
2852c77a61a7Syz 		bufs[i].v4l2_buf.flags = V4L2_MEMORY_MMAP;
2853c77a61a7Syz 
2854c77a61a7Syz 		list_insert_tail(&strm_if->buf_map.uv_buf_free, &bufs[i]);
2855*61b2e298Slc 		USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
2856c77a61a7Syz 		    "usbvc_alloc_map_bufs: prepare %d buffers of %d bytes",
2857c77a61a7Syz 		    buf_cnt, bufs[i].len);
2858c77a61a7Syz 	}
2859c77a61a7Syz 	strm_if->buf_map.buf_cnt = buf_cnt;
2860c77a61a7Syz 	strm_if->buf_map.buf_filling = NULL;
2861c77a61a7Syz 
2862c77a61a7Syz 	return (buf_cnt);
2863c77a61a7Syz }
2864c77a61a7Syz 
2865c77a61a7Syz 
2866c77a61a7Syz /* Free all bufs which are for memory map IO style */
2867c77a61a7Syz void
2868c77a61a7Syz usbvc_free_map_bufs(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
2869c77a61a7Syz {
2870c77a61a7Syz 	usbvc_buf_t	*buf;
2871c77a61a7Syz 
2872c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
2873c77a61a7Syz 	if (!strm_if) {
2874c77a61a7Syz 
2875c77a61a7Syz 		return;
2876c77a61a7Syz 	}
2877c77a61a7Syz 	strm_if->buf_map.buf_filling = NULL;
2878c77a61a7Syz 	while (!list_is_empty(&strm_if->buf_map.uv_buf_free)) {
2879c77a61a7Syz 		buf = (usbvc_buf_t *)list_head(&strm_if->buf_map.uv_buf_free);
2880c77a61a7Syz 		list_remove(&(strm_if->buf_map.uv_buf_free), buf);
2881c77a61a7Syz 	}
2882c77a61a7Syz 	while (!list_is_empty(&strm_if->buf_map.uv_buf_done)) {
2883c77a61a7Syz 		buf = (usbvc_buf_t *)list_head(&strm_if->buf_map.uv_buf_done);
2884c77a61a7Syz 		list_remove(&(strm_if->buf_map.uv_buf_done), buf);
2885c77a61a7Syz 	}
2886c77a61a7Syz 	buf = strm_if->buf_map.buf_head;
2887c77a61a7Syz 	if (!buf) {
2888c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
2889c77a61a7Syz 		    "usbvc_free_map_bufs: no data buf need be freed, return");
2890c77a61a7Syz 
2891c77a61a7Syz 		return;
2892c77a61a7Syz 	}
2893c77a61a7Syz 	if (buf->umem_cookie) {
2894c77a61a7Syz 		ddi_umem_free(buf->umem_cookie);
2895c77a61a7Syz 	}
2896c77a61a7Syz 	kmem_free(buf, sizeof (usbvc_buf_t) * strm_if->buf_map.buf_cnt);
2897c77a61a7Syz 	strm_if->buf_map.buf_cnt = 0;
2898c77a61a7Syz 	strm_if->buf_map.buf_head = NULL;
2899c77a61a7Syz 
2900*61b2e298Slc 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
2901c77a61a7Syz 	    "usbvc_free_map_bufs: return");
2902c77a61a7Syz }
2903c77a61a7Syz 
2904c77a61a7Syz 
2905c77a61a7Syz /*
2906c77a61a7Syz  * Open the isoc pipe, this pipe is for video data transfer
2907c77a61a7Syz  */
2908c77a61a7Syz int
2909c77a61a7Syz usbvc_open_isoc_pipe(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
2910c77a61a7Syz {
2911c77a61a7Syz 	usb_pipe_policy_t policy;
2912c77a61a7Syz 	int	rval = USB_SUCCESS;
2913c77a61a7Syz 
2914c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
2915c77a61a7Syz 
2916c77a61a7Syz 	if ((rval = usbvc_set_alt(usbvcp, strm_if)) != USB_SUCCESS) {
2917c77a61a7Syz 
2918c77a61a7Syz 		return (rval);
2919c77a61a7Syz 	}
2920c77a61a7Syz 	bzero(&policy, sizeof (usb_pipe_policy_t));
2921c77a61a7Syz 	policy.pp_max_async_reqs = 2;
2922c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
2923c77a61a7Syz 	if ((rval = usb_pipe_open(usbvcp->usbvc_dip, strm_if->curr_ep, &policy,
2924c77a61a7Syz 	    USB_FLAGS_SLEEP, &strm_if->datain_ph)) != USB_SUCCESS) {
2925c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
2926c77a61a7Syz 		    "usbvc_open_isoc_pipe: open pipe fail");
2927c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
2928c77a61a7Syz 
2929c77a61a7Syz 		return (rval);
2930c77a61a7Syz 	}
2931c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
2932c77a61a7Syz 	strm_if->start_polling = 0;
2933c77a61a7Syz 
2934c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
2935112116d8Sfb 	    "usbvc_open_isoc_pipe: success, datain_ph=%p",
2936112116d8Sfb 	    (void *)strm_if->datain_ph);
2937c77a61a7Syz 
2938c77a61a7Syz 	return (rval);
2939c77a61a7Syz }
2940c77a61a7Syz 
2941c77a61a7Syz 
2942c77a61a7Syz /*
2943c77a61a7Syz  * Open the isoc pipe
2944c77a61a7Syz  */
2945c77a61a7Syz static void
2946c77a61a7Syz usbvc_close_isoc_pipe(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
2947c77a61a7Syz {
2948c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
2949c77a61a7Syz 	if (!strm_if) {
2950c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
2951c77a61a7Syz 		    "usbvc_close_isoc_pipe: stream interface is NULL");
2952c77a61a7Syz 
2953c77a61a7Syz 		return;
2954c77a61a7Syz 	}
2955c77a61a7Syz 	if (strm_if->datain_ph) {
2956c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
2957c77a61a7Syz 		usb_pipe_close(usbvcp->usbvc_dip, strm_if->datain_ph,
2958c77a61a7Syz 		    USB_FLAGS_SLEEP, NULL, NULL);
2959c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
2960c77a61a7Syz 	}
2961c77a61a7Syz 	strm_if->datain_ph = NULL;
2962c77a61a7Syz }
2963c77a61a7Syz 
2964c77a61a7Syz 
2965c77a61a7Syz /*
2966c77a61a7Syz  * Start to get video data from isoc pipe in the stream interface,
2967c77a61a7Syz  * issue isoc req.
2968c77a61a7Syz  */
2969c77a61a7Syz int
2970c77a61a7Syz usbvc_start_isoc_polling(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if,
2971c77a61a7Syz     uchar_t io_type)
2972c77a61a7Syz {
2973c77a61a7Syz 	int		rval = USB_SUCCESS;
2974c77a61a7Syz 	uint_t		if_num;
2975c77a61a7Syz 	usb_isoc_req_t	*req;
2976c77a61a7Syz 	ushort_t	pkt_size;
2977c77a61a7Syz 	ushort_t	n_pkt, pkt;
2978c77a61a7Syz 	uint32_t	frame_size;
2979c77a61a7Syz 
2980c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
2981*61b2e298Slc 	pkt_size = HS_PKT_SIZE(strm_if->curr_ep->wMaxPacketSize);
2982c77a61a7Syz 	if_num = strm_if->if_descr->if_alt->altif_descr.bInterfaceNumber;
2983c77a61a7Syz 	LE_TO_UINT32(strm_if->ctrl_pc.dwMaxVideoFrameSize, 0, frame_size);
2984*61b2e298Slc 	n_pkt = (frame_size + (pkt_size) - 1) / (pkt_size);
2985*61b2e298Slc 
2986c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
2987c77a61a7Syz 	    "usbvc_start_isoc_polling: if_num=%d, alt=%d, n_pkt=%d,"
2988*61b2e298Slc 	    " pkt_size=0x%x, MaxPacketSize=0x%x(Tsac#=%d), frame_size=0x%x",
2989*61b2e298Slc 	    if_num, strm_if->curr_alt, n_pkt, pkt_size,
2990*61b2e298Slc 	    strm_if->curr_ep->wMaxPacketSize,
2991*61b2e298Slc 	    (1 + ((strm_if->curr_ep->wMaxPacketSize>> 11) & 3)),
2992*61b2e298Slc 	    frame_size);
2993c77a61a7Syz 
2994c77a61a7Syz 	if (n_pkt > USBVC_MAX_PKTS) {
2995c77a61a7Syz 		n_pkt = USBVC_MAX_PKTS;
2996c77a61a7Syz 	}
2997c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
2998ef772e54Sfb 	    "usbvc_start_isoc_polling: n_pkt=%d", n_pkt);
2999c77a61a7Syz 
3000c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
3001c77a61a7Syz 	if ((req = usb_alloc_isoc_req(usbvcp->usbvc_dip, n_pkt,
3002c77a61a7Syz 	    n_pkt * pkt_size, USB_FLAGS_SLEEP)) != NULL) {
3003c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
3004c77a61a7Syz 
3005c77a61a7Syz 		/* Initialize the packet descriptor */
3006c77a61a7Syz 		for (pkt = 0; pkt < n_pkt; pkt++) {
3007c77a61a7Syz 			req->isoc_pkt_descr[pkt].isoc_pkt_length = pkt_size;
3008c77a61a7Syz 		}
3009c77a61a7Syz 
3010c77a61a7Syz 		req->isoc_pkts_count = n_pkt;
3011c77a61a7Syz 
3012c77a61a7Syz 		/*
3013c77a61a7Syz 		 * zero here indicates that HCDs will use
3014c77a61a7Syz 		 * isoc_pkt_descr->isoc_pkt_length to calculate
3015c77a61a7Syz 		 * isoc_pkts_length.
3016c77a61a7Syz 		 */
3017c77a61a7Syz 		req->isoc_pkts_length = 0;
3018c77a61a7Syz 		req->isoc_attributes = USB_ATTRS_ISOC_XFER_ASAP |
3019c77a61a7Syz 		    USB_ATTRS_SHORT_XFER_OK | USB_ATTRS_AUTOCLEARING;
3020c77a61a7Syz 		req->isoc_cb = usbvc_isoc_cb;
3021c77a61a7Syz 		req->isoc_exc_cb = usbvc_isoc_exc_cb;
3022c77a61a7Syz 		usbvcp->usbvc_io_type = io_type;
3023c77a61a7Syz 		req->isoc_client_private = (usb_opaque_t)usbvcp;
3024c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
3025c77a61a7Syz 		rval = usb_pipe_isoc_xfer(strm_if->datain_ph, req, 0);
3026c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
3027c77a61a7Syz 	} else {
3028c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
3029c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
3030c77a61a7Syz 		    "usbvc_start_isoc_polling: alloc_isoc_req fail");
3031c77a61a7Syz 
3032c77a61a7Syz 		return (USB_FAILURE);
3033c77a61a7Syz 	}
3034c77a61a7Syz 
3035c77a61a7Syz 	if (rval != USB_SUCCESS) {
3036c77a61a7Syz 		if (req) {
3037c77a61a7Syz 			usb_free_isoc_req(req);
3038c77a61a7Syz 			req = NULL;
3039c77a61a7Syz 		}
3040c77a61a7Syz 	}
3041c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
3042ef772e54Sfb 	    "usbvc_start_isoc_polling: return, rval=%d", rval);
3043c77a61a7Syz 
3044c77a61a7Syz 	return (rval);
3045c77a61a7Syz }
3046c77a61a7Syz 
3047c77a61a7Syz /* callbacks for receiving video data (isco in transfer) */
3048c77a61a7Syz 
3049c77a61a7Syz /*ARGSUSED*/
3050c77a61a7Syz /* Isoc transfer callback, get video data */
3051c77a61a7Syz static void
3052c77a61a7Syz usbvc_isoc_cb(usb_pipe_handle_t ph, usb_isoc_req_t *isoc_req)
3053c77a61a7Syz {
3054c77a61a7Syz 	usbvc_state_t	*usbvcp =
3055ef772e54Sfb 	    (usbvc_state_t *)isoc_req->isoc_client_private;
3056c77a61a7Syz 	int		i;
3057c77a61a7Syz 	mblk_t		*data = isoc_req->isoc_data;
3058c77a61a7Syz 	usbvc_buf_grp_t	*bufgrp;
3059c77a61a7Syz 
3060c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
3061c77a61a7Syz 
3062c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3063c77a61a7Syz 	    "usbvc_isoc_cb: rq=0x%p, fno=%" PRId64 ", n_pkts=%u, flag=0x%x,"
3064c77a61a7Syz 	    " data=0x%p, cnt=%d",
3065112116d8Sfb 	    (void *)isoc_req, isoc_req->isoc_frame_no,
3066112116d8Sfb 	    isoc_req->isoc_pkts_count, isoc_req->isoc_attributes,
3067112116d8Sfb 	    (void *)isoc_req->isoc_data, isoc_req->isoc_error_count);
3068c77a61a7Syz 
3069c77a61a7Syz 	ASSERT((isoc_req->isoc_cb_flags & USB_CB_INTR_CONTEXT) != 0);
3070c77a61a7Syz 	for (i = 0; i < isoc_req->isoc_pkts_count; i++) {
3071c77a61a7Syz 
3072c77a61a7Syz 		USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3073c77a61a7Syz 		    "\tpkt%d: "
3074c77a61a7Syz 		    "pktsize=%d status=%d resid=%d",
3075c77a61a7Syz 		    i,
3076c77a61a7Syz 		    isoc_req->isoc_pkt_descr[i].isoc_pkt_length,
3077c77a61a7Syz 		    isoc_req->isoc_pkt_descr[i].isoc_pkt_status,
3078c77a61a7Syz 		    isoc_req->isoc_pkt_descr[i].isoc_pkt_actual_length);
3079c77a61a7Syz 
3080c77a61a7Syz 		if (isoc_req->isoc_pkt_descr[i].isoc_pkt_status !=
3081c77a61a7Syz 		    USB_CR_OK) {
3082c77a61a7Syz 			USB_DPRINTF_L3(PRINT_MASK_CB,
3083c77a61a7Syz 			    usbvcp->usbvc_log_handle,
3084c77a61a7Syz 			    "record: pkt=%d status=%s", i, usb_str_cr(
3085c77a61a7Syz 			    isoc_req->isoc_pkt_descr[i].isoc_pkt_status));
3086c77a61a7Syz 		}
3087c77a61a7Syz 
3088c77a61a7Syz 		if (usbvcp->usbvc_io_type == V4L2_MEMORY_MMAP) {
3089c77a61a7Syz 			bufgrp = &usbvcp->usbvc_curr_strm->buf_map;
3090c77a61a7Syz 		} else {
3091c77a61a7Syz 			bufgrp = &usbvcp->usbvc_curr_strm->buf_read;
3092c77a61a7Syz 		}
3093c77a61a7Syz 
3094c77a61a7Syz 		if (isoc_req->isoc_pkt_descr[i].isoc_pkt_actual_length) {
3095c77a61a7Syz 			if (usbvc_decode_stream_header(usbvcp, bufgrp, data,
3096c77a61a7Syz 			    isoc_req->isoc_pkt_descr[i].isoc_pkt_actual_length)
3097c77a61a7Syz 			    != USB_SUCCESS) {
3098c77a61a7Syz 				USB_DPRINTF_L3(PRINT_MASK_CB,
3099c77a61a7Syz 				    usbvcp->usbvc_log_handle, "decode error");
3100c77a61a7Syz 			}
3101c77a61a7Syz 			if (bufgrp->buf_filling &&
3102c77a61a7Syz 			    (bufgrp->buf_filling->status == USBVC_BUF_ERR ||
3103c77a61a7Syz 			    bufgrp->buf_filling->status == USBVC_BUF_DONE)) {
3104c77a61a7Syz 
3105c77a61a7Syz 				/* Move the buf to the full list */
3106c77a61a7Syz 				list_insert_tail(&bufgrp->uv_buf_done,
3107c77a61a7Syz 				    bufgrp->buf_filling);
3108c77a61a7Syz 
3109c77a61a7Syz 				bufgrp->buf_filling = NULL;
3110c77a61a7Syz 
3111c77a61a7Syz 				if (usbvcp->usbvc_io_type == V4L2_MEMORY_MMAP) {
3112c77a61a7Syz 					cv_broadcast(&usbvcp->usbvc_mapio_cv);
3113c77a61a7Syz 				} else {
3114c77a61a7Syz 					cv_broadcast(&usbvcp->usbvc_read_cv);
3115c77a61a7Syz 				}
3116c77a61a7Syz 			}
3117c77a61a7Syz 		}
3118*61b2e298Slc 
3119*61b2e298Slc 		data->b_rptr += isoc_req->isoc_pkt_descr[i].isoc_pkt_length;
3120c77a61a7Syz 	}
3121c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
3122c77a61a7Syz 	usb_free_isoc_req(isoc_req);
3123c77a61a7Syz }
3124c77a61a7Syz 
3125c77a61a7Syz 
3126c77a61a7Syz /*ARGSUSED*/
3127c77a61a7Syz static void
3128c77a61a7Syz usbvc_isoc_exc_cb(usb_pipe_handle_t ph, usb_isoc_req_t *isoc_req)
3129c77a61a7Syz {
3130c77a61a7Syz 	usbvc_state_t	*usbvcp =
3131ef772e54Sfb 	    (usbvc_state_t *)isoc_req->isoc_client_private;
3132c77a61a7Syz 	usb_cr_t	completion_reason;
3133c77a61a7Syz 	int		rval;
3134c77a61a7Syz 	usbvc_stream_if_t	*strm_if;
3135c77a61a7Syz 
3136c77a61a7Syz 	ASSERT(!list_is_empty(&usbvcp->usbvc_stream_list));
3137c77a61a7Syz 
3138c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
3139c77a61a7Syz 
3140c77a61a7Syz 	/* get the first stream interface */
3141c77a61a7Syz 	strm_if = usbvcp->usbvc_curr_strm;
3142c77a61a7Syz 
3143c77a61a7Syz 	completion_reason = isoc_req->isoc_completion_reason;
3144c77a61a7Syz 
3145*61b2e298Slc 	USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3146c77a61a7Syz 	    "usbvc_isoc_exc_cb: ph=0x%p, isoc_req=0x%p, cr=%d",
3147c77a61a7Syz 	    (void *)ph, (void *)isoc_req, completion_reason);
3148c77a61a7Syz 
3149c77a61a7Syz 	ASSERT((isoc_req->isoc_cb_flags & USB_CB_INTR_CONTEXT) == 0);
3150c77a61a7Syz 
3151c77a61a7Syz 	switch (completion_reason) {
3152c77a61a7Syz 	case USB_CR_STOPPED_POLLING:
3153c77a61a7Syz 	case USB_CR_PIPE_CLOSING:
3154c77a61a7Syz 	case USB_CR_PIPE_RESET:
3155c77a61a7Syz 
3156c77a61a7Syz 		break;
3157c77a61a7Syz 	case USB_CR_NO_RESOURCES:
3158c77a61a7Syz 		/*
3159c77a61a7Syz 		 * keep the show going: Since we have the original
3160c77a61a7Syz 		 * request, we just resubmit it
3161c77a61a7Syz 		 */
3162c77a61a7Syz 		rval = usb_pipe_isoc_xfer(strm_if->datain_ph, isoc_req,
3163c77a61a7Syz 		    USB_FLAGS_NOSLEEP);
3164c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3165c77a61a7Syz 		    "usbvc_isoc_exc_cb: restart capture rval=%d", rval);
3166c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
3167c77a61a7Syz 
3168c77a61a7Syz 		return;
3169c77a61a7Syz 	default:
3170c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
3171c77a61a7Syz 		usb_pipe_stop_isoc_polling(ph, USB_FLAGS_NOSLEEP);
3172c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3173c77a61a7Syz 		    "usbvc_isoc_exc_cb: stop polling");
3174c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
3175c77a61a7Syz 	}
3176c77a61a7Syz 	usb_free_isoc_req(isoc_req);
3177c77a61a7Syz 	strm_if->start_polling = 0;
3178c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3179c77a61a7Syz 	    "usbvc_isoc_exc_cb: start_polling=%d cr=%d",
3180c77a61a7Syz 	    strm_if->start_polling, completion_reason);
3181c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
3182c77a61a7Syz }
3183c77a61a7Syz 
3184c77a61a7Syz /*
3185c77a61a7Syz  * Other utility functions
3186c77a61a7Syz  */
3187c77a61a7Syz 
3188c77a61a7Syz /*
3189c77a61a7Syz  * Find a proper alternate according to the bandwidth that the current video
3190c77a61a7Syz  * format need;
3191c77a61a7Syz  * Set alternate by calling usb_set_alt_if;
3192c77a61a7Syz  * Called before open pipes in stream interface.
3193c77a61a7Syz  */
3194c77a61a7Syz static int
3195c77a61a7Syz usbvc_set_alt(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
3196c77a61a7Syz {
3197c77a61a7Syz 	usb_alt_if_data_t	*alt;
3198c77a61a7Syz 	uint_t			i, j, if_num;
3199c77a61a7Syz 	uint16_t		pktsize, curr_pktsize;
3200c77a61a7Syz 	uint32_t		bandwidth;
3201c77a61a7Syz 	int			rval = USB_SUCCESS;
3202c77a61a7Syz 	usbvc_input_header_t	*ihd;
3203c77a61a7Syz 	usbvc_output_header_t	*ohd;
3204c77a61a7Syz 
3205c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
3206c77a61a7Syz 
3207c77a61a7Syz 	LE_TO_UINT32(strm_if->ctrl_pc.dwMaxPayloadTransferSize, 0, bandwidth);
3208c77a61a7Syz 	if (!bandwidth) {
3209c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
3210c77a61a7Syz 		    "usbvc_set_alt: bandwidth is not set yet");
3211c77a61a7Syz 
3212c77a61a7Syz 		return (USB_FAILURE);
3213c77a61a7Syz 	}
3214c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
3215ef772e54Sfb 	    "usbvc_set_alt: bandwidth=%x", bandwidth);
3216c77a61a7Syz 
3217c77a61a7Syz 	strm_if->curr_ep = NULL;
3218c77a61a7Syz 	curr_pktsize = 0xffff;
3219c77a61a7Syz 	ohd = strm_if->output_header;
3220c77a61a7Syz 	ihd = strm_if->input_header;
3221c77a61a7Syz 	/*
3222c77a61a7Syz 	 * Find one alternate setting whose isoc ep's max pktsize is just
3223c77a61a7Syz 	 * enough for the bandwidth.
3224c77a61a7Syz 	 */
3225c77a61a7Syz 	for (i = 0; i < strm_if->if_descr->if_n_alt; i++) {
3226c77a61a7Syz 		alt = &strm_if->if_descr->if_alt[i];
3227c77a61a7Syz 
3228c77a61a7Syz 		for (j = 0; j < alt->altif_n_ep; j++) {
3229c77a61a7Syz 
3230c77a61a7Syz 			/* if this stream interface is for input */
3231c77a61a7Syz 			if (ihd != NULL &&
3232c77a61a7Syz 			    alt->altif_ep[j].ep_descr.bEndpointAddress !=
3233c77a61a7Syz 			    ihd->descr->bEndpointAddress) {
3234c77a61a7Syz 
3235c77a61a7Syz 				continue;
3236c77a61a7Syz 			}
3237c77a61a7Syz 			/*  if this stream interface is for output */
3238c77a61a7Syz 			if (ohd != NULL &&
3239c77a61a7Syz 			    alt->altif_ep[j].ep_descr.bEndpointAddress !=
3240c77a61a7Syz 			    ohd->descr->bEndpointAddress) {
3241c77a61a7Syz 
3242c77a61a7Syz 				continue;
3243c77a61a7Syz 			}
3244c77a61a7Syz 			pktsize =
3245c77a61a7Syz 			    alt->altif_ep[j].ep_descr.wMaxPacketSize;
3246c77a61a7Syz 			pktsize = HS_PKT_SIZE(pktsize);
3247c77a61a7Syz 			if (pktsize >= bandwidth && pktsize < curr_pktsize) {
3248c77a61a7Syz 				curr_pktsize = pktsize;
3249c77a61a7Syz 				strm_if->curr_alt = i;
3250c77a61a7Syz 				strm_if->curr_ep = &alt->altif_ep[j].ep_descr;
3251c77a61a7Syz 			}
3252c77a61a7Syz 		}
3253c77a61a7Syz 	}
3254c77a61a7Syz 	if (!strm_if->curr_ep) {
3255c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
3256c77a61a7Syz 		    "usbvc_set_alt: can't find a proper ep to satisfy"
3257c77a61a7Syz 		    " the given bandwidth");
3258c77a61a7Syz 
3259c77a61a7Syz 		return (USB_FAILURE);
3260c77a61a7Syz 	}
3261c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
3262c77a61a7Syz 	    "usbvc_set_alt: strm_if->curr_alt=%d", strm_if->curr_alt);
3263c77a61a7Syz 	if_num = strm_if->if_descr->if_alt->altif_descr.bInterfaceNumber;
3264c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
3265c77a61a7Syz 	if ((rval = usb_set_alt_if(usbvcp->usbvc_dip, if_num, strm_if->curr_alt,
3266ef772e54Sfb 	    USB_FLAGS_SLEEP, NULL, NULL)) != USB_SUCCESS) {
3267c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
3268c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
3269c77a61a7Syz 		    "usbvc_set_alt: usb_set_alt_if fail, if.alt=%d.%d, rval=%d",
3270c77a61a7Syz 		    if_num, strm_if->curr_alt, rval);
3271c77a61a7Syz 
3272c77a61a7Syz 		return (rval);
3273c77a61a7Syz 	}
3274c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
3275c77a61a7Syz 
3276c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
3277c77a61a7Syz 	    "usbvc_set_alt: return, if_num=%d, alt=%d",
3278c77a61a7Syz 	    if_num, strm_if->curr_alt);
3279c77a61a7Syz 
3280c77a61a7Syz 	return (rval);
3281c77a61a7Syz }
3282c77a61a7Syz 
3283c77a61a7Syz 
3284c77a61a7Syz /*
3285c77a61a7Syz  * Decode stream header for mjpeg and uncompressed format video data.
3286c77a61a7Syz  * mjpeg and uncompressed format have the same stream header. See their
3287c77a61a7Syz  * payload spec, 2.2 and 2.4
3288c77a61a7Syz  */
3289c77a61a7Syz static int
3290c77a61a7Syz usbvc_decode_stream_header(usbvc_state_t *usbvcp, usbvc_buf_grp_t *bufgrp,
3291c77a61a7Syz 	mblk_t *data, int actual_len)
3292c77a61a7Syz {
3293c77a61a7Syz 	uint32_t len, buf_left, data_len;
3294c77a61a7Syz 	usbvc_stream_if_t *strm_if;
3295c77a61a7Syz 	uchar_t head_flag, head_len;
3296c77a61a7Syz 	usbvc_buf_t *buf_filling;
3297c77a61a7Syz 
3298c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
3299c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3300c77a61a7Syz 	    "usbvc_decode_stream_header: enter. actual_len=%x", actual_len);
3301c77a61a7Syz 
3302c77a61a7Syz 	/* header length check. */
3303c77a61a7Syz 	if (actual_len < 2) {
3304c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3305c77a61a7Syz 		    "usbvc_decode_stream_header: header is not completed");
3306c77a61a7Syz 
3307c77a61a7Syz 		return (USB_FAILURE);
3308c77a61a7Syz 	}
3309c77a61a7Syz 	head_len = data->b_rptr[0];
3310c77a61a7Syz 	head_flag = data->b_rptr[1];
3311c77a61a7Syz 
3312c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3313c77a61a7Syz 	    "usbvc_decode_stream_header: headlen=%x", head_len);
3314c77a61a7Syz 
3315c77a61a7Syz 	/* header length check. */
3316c77a61a7Syz 	if (actual_len < head_len) {
3317c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3318c77a61a7Syz 		    "usbvc_decode_stream_header: actual_len < head_len");
3319c77a61a7Syz 
3320c77a61a7Syz 		return (USB_FAILURE);
3321c77a61a7Syz 	}
3322c77a61a7Syz 
3323c77a61a7Syz 	/*
3324c77a61a7Syz 	 * If there is no stream data in this packet and this packet is not
3325c77a61a7Syz 	 * used to indicate the end of a frame, then just skip it.
3326c77a61a7Syz 	 */
3327c77a61a7Syz 	if ((actual_len == head_len) && !(head_flag & USBVC_STREAM_EOF)) {
3328ef772e54Sfb 		USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3329c77a61a7Syz 		    "usbvc_decode_stream_header: only header, no data");
3330c77a61a7Syz 
3331c77a61a7Syz 		return (USB_FAILURE);
3332c77a61a7Syz 	}
3333c77a61a7Syz 
3334c77a61a7Syz 	/* Get the first stream interface */
3335c77a61a7Syz 	strm_if = usbvcp->usbvc_curr_strm;
3336c77a61a7Syz 
3337c77a61a7Syz 	LE_TO_UINT32(strm_if->ctrl_pc.dwMaxVideoFrameSize, 0, len);
3338c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3339c77a61a7Syz 	    "usbvc_decode_stream_header: dwMaxVideoFrameSize=%x, head_flag=%x",
3340c77a61a7Syz 	    len, head_flag);
3341c77a61a7Syz 
3342c77a61a7Syz 	/*
3343c77a61a7Syz 	 * if no buf is filling, pick one buf from free list and alloc data
3344c77a61a7Syz 	 * mem for the buf.
3345c77a61a7Syz 	 */
3346c77a61a7Syz 	if (!bufgrp->buf_filling) {
3347c77a61a7Syz 		if (list_is_empty(&bufgrp->uv_buf_free)) {
3348c77a61a7Syz 			strm_if->fid = head_flag & USBVC_STREAM_FID;
3349c77a61a7Syz 			USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3350c77a61a7Syz 			    "usbvc_decode_stream_header: free list are empty");
3351c77a61a7Syz 
3352c77a61a7Syz 			return (USB_FAILURE);
3353c77a61a7Syz 
3354c77a61a7Syz 		} else {
3355c77a61a7Syz 			bufgrp->buf_filling =
3356c77a61a7Syz 			    (usbvc_buf_t *)list_head(&bufgrp->uv_buf_free);
3357c77a61a7Syz 
3358c77a61a7Syz 			/* unlink from buf free list */
3359c77a61a7Syz 			list_remove(&bufgrp->uv_buf_free, bufgrp->buf_filling);
3360c77a61a7Syz 		}
3361c77a61a7Syz 		bufgrp->buf_filling->filled = 0;
3362c77a61a7Syz 		USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3363c77a61a7Syz 		    "usbvc_decode_stream_header: status=%d",
3364c77a61a7Syz 		    bufgrp->buf_filling->status);
3365c77a61a7Syz 		bufgrp->buf_filling->status = USBVC_BUF_EMPTY;
3366c77a61a7Syz 	}
3367c77a61a7Syz 	buf_filling = bufgrp->buf_filling;
3368c77a61a7Syz 	ASSERT(buf_filling->len >= buf_filling->filled);
3369c77a61a7Syz 	buf_left = buf_filling->len - buf_filling->filled;
3370c77a61a7Syz 
3371c77a61a7Syz 	/* if no buf room left, then return with a err status */
3372c77a61a7Syz 	if (buf_left == 0) {
3373c77a61a7Syz 		buf_filling->status = USBVC_BUF_ERR;
3374c77a61a7Syz 
3375c77a61a7Syz 		return (USB_FAILURE);
3376c77a61a7Syz 	}
3377c77a61a7Syz 
3378c77a61a7Syz 	/* get this sample's data length except header */
3379c77a61a7Syz 	data_len = actual_len - head_len;
3380c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3381c77a61a7Syz 	    "usbvc_decode_stream_header: fid=%x, len=%x, filled=%x",
3382c77a61a7Syz 	    strm_if->fid, buf_filling->len, buf_filling->filled);
3383c77a61a7Syz 
3384c77a61a7Syz 	/* if the first sample for a frame */
3385c77a61a7Syz 	if (buf_filling->filled == 0) {
3386c77a61a7Syz 		/*
3387c77a61a7Syz 		 * Only if it is the frist packet of a frame,
3388c77a61a7Syz 		 * we will begin filling a frame.
3389c77a61a7Syz 		 */
3390c77a61a7Syz 		if (strm_if->fid != 0xff && strm_if->fid ==
3391c77a61a7Syz 		    (head_flag & USBVC_STREAM_FID)) {
3392c77a61a7Syz 			USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3393c77a61a7Syz 			    "usbvc_decode_stream_header: 1st sample of a frame,"
3394c77a61a7Syz 			    " fid is incorrect.");
3395c77a61a7Syz 
3396c77a61a7Syz 			return (USB_FAILURE);
3397c77a61a7Syz 		}
3398c77a61a7Syz 		strm_if->fid = head_flag & USBVC_STREAM_FID;
3399c77a61a7Syz 
3400c77a61a7Syz 	/* If in the middle of a frame, fid should be consistent. */
3401c77a61a7Syz 	} else if (strm_if->fid != (head_flag & USBVC_STREAM_FID)) {
3402c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3403c77a61a7Syz 		    "usbvc_decode_stream_header: fid is incorrect.");
3404c77a61a7Syz 		strm_if->fid = head_flag & USBVC_STREAM_FID;
3405c77a61a7Syz 		buf_filling->status = USBVC_BUF_ERR;
3406c77a61a7Syz 
3407c77a61a7Syz 		return (USB_FAILURE);
3408c77a61a7Syz 	}
3409c77a61a7Syz 	if (data_len) {
3410c77a61a7Syz 		bcopy((void *)(data->b_rptr + head_len),
3411c77a61a7Syz 		    (void *)(buf_filling->data + buf_filling->filled),
3412c77a61a7Syz 		    min(data_len, buf_left));
3413c77a61a7Syz 
3414c77a61a7Syz 		buf_filling->filled += min(data_len, buf_left);
3415c77a61a7Syz 	}
3416c77a61a7Syz 
3417c77a61a7Syz 	/* If the last packet for this frame */
3418c77a61a7Syz 	if (head_flag & USBVC_STREAM_EOF) {
3419c77a61a7Syz 		buf_filling->status = USBVC_BUF_DONE;
3420c77a61a7Syz 	}
3421c77a61a7Syz 	if (data_len > buf_left) {
3422c77a61a7Syz 		buf_filling->status = USBVC_BUF_ERR;
3423c77a61a7Syz 	}
3424c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3425c77a61a7Syz 	    "usbvc_decode_stream_header: buf_status=%d", buf_filling->status);
3426c77a61a7Syz 
3427c77a61a7Syz 	return (USB_SUCCESS);
3428c77a61a7Syz }
3429c77a61a7Syz 
3430c77a61a7Syz 
3431c77a61a7Syz /*
3432c77a61a7Syz  * usbvc_serialize_access:
3433c77a61a7Syz  *    Get the serial synchronization object before returning.
3434c77a61a7Syz  *
3435c77a61a7Syz  * Arguments:
3436c77a61a7Syz  *    usbvcp - Pointer to usbvc state structure
3437c77a61a7Syz  *    waitsig - Set to:
3438c77a61a7Syz  *	USBVC_SER_SIG - to wait such that a signal can interrupt
3439c77a61a7Syz  *	USBVC_SER_NOSIG - to wait such that a signal cannot interrupt
3440c77a61a7Syz  */
3441c77a61a7Syz static int
3442c77a61a7Syz usbvc_serialize_access(usbvc_state_t *usbvcp, boolean_t waitsig)
3443c77a61a7Syz {
3444c77a61a7Syz 	int rval = 1;
3445c77a61a7Syz 
3446c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
3447c77a61a7Syz 
3448c77a61a7Syz 	while (usbvcp->usbvc_serial_inuse) {
3449c77a61a7Syz 		if (waitsig == USBVC_SER_SIG) {
3450c77a61a7Syz 			rval = cv_wait_sig(&usbvcp->usbvc_serial_cv,
3451c77a61a7Syz 			    &usbvcp->usbvc_mutex);
3452c77a61a7Syz 		} else {
3453c77a61a7Syz 			cv_wait(&usbvcp->usbvc_serial_cv,
3454c77a61a7Syz 			    &usbvcp->usbvc_mutex);
3455c77a61a7Syz 		}
3456c77a61a7Syz 	}
3457c77a61a7Syz 	usbvcp->usbvc_serial_inuse = B_TRUE;
3458c77a61a7Syz 
3459c77a61a7Syz 	return (rval);
3460c77a61a7Syz }
3461c77a61a7Syz 
3462c77a61a7Syz 
3463c77a61a7Syz /*
3464c77a61a7Syz  * usbvc_release_access:
3465c77a61a7Syz  *    Release the serial synchronization object.
3466c77a61a7Syz  */
3467c77a61a7Syz static void
3468c77a61a7Syz usbvc_release_access(usbvc_state_t *usbvcp)
3469c77a61a7Syz {
3470c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
3471c77a61a7Syz 	usbvcp->usbvc_serial_inuse = B_FALSE;
3472c77a61a7Syz 	cv_broadcast(&usbvcp->usbvc_serial_cv);
3473c77a61a7Syz }
3474c77a61a7Syz 
3475c77a61a7Syz 
3476c77a61a7Syz /* Send req to video control interface to get ctrl */
3477c77a61a7Syz int
3478c77a61a7Syz usbvc_vc_get_ctrl(usbvc_state_t *usbvcp, uint8_t req_code, uint8_t entity_id,
3479c77a61a7Syz     uint16_t cs, uint16_t wlength, mblk_t *data)
3480c77a61a7Syz {
3481c77a61a7Syz 	usb_cb_flags_t	cb_flags;
3482c77a61a7Syz 	usb_cr_t	cr;
3483c77a61a7Syz 	usb_ctrl_setup_t setup;
3484c77a61a7Syz 
3485c77a61a7Syz 	setup.bmRequestType = USBVC_GET_IF;	/* bmRequestType */
3486c77a61a7Syz 	setup.bRequest = req_code;		/* bRequest */
3487c77a61a7Syz 	setup.wValue = cs<<8;
3488c77a61a7Syz 	setup.wIndex = entity_id<<8;
3489c77a61a7Syz 	setup.wLength = wlength;
3490c77a61a7Syz 	setup.attrs = 0;
3491c77a61a7Syz 
3492c77a61a7Syz 	if (usb_pipe_ctrl_xfer_wait(usbvcp->usbvc_default_ph, &setup, &data,
3493c77a61a7Syz 	    &cr, &cb_flags, 0) != USB_SUCCESS) {
3494c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3495c77a61a7Syz 		    "usbvc_vc_get_ctrl: cmd failed, cr=%d, cb_flags=%x",
3496c77a61a7Syz 		    cr, cb_flags);
3497c77a61a7Syz 
3498c77a61a7Syz 		return (USB_FAILURE);
3499c77a61a7Syz 	}
3500c77a61a7Syz 
3501c77a61a7Syz 	return (USB_SUCCESS);
3502c77a61a7Syz }
3503c77a61a7Syz 
3504c77a61a7Syz 
3505c77a61a7Syz /* Send req to video control interface to get ctrl */
3506c77a61a7Syz int
3507c77a61a7Syz usbvc_vc_set_ctrl(usbvc_state_t *usbvcp, uint8_t req_code,  uint8_t entity_id,
3508c77a61a7Syz 	uint16_t cs, uint16_t wlength, mblk_t *data)
3509c77a61a7Syz {
3510c77a61a7Syz 	usb_cb_flags_t	cb_flags;
3511c77a61a7Syz 	usb_cr_t	cr;
3512c77a61a7Syz 	usb_ctrl_setup_t setup;
3513c77a61a7Syz 
3514c77a61a7Syz 	setup.bmRequestType = USBVC_SET_IF;	/* bmRequestType */
3515c77a61a7Syz 	setup.bRequest = req_code;		/* bRequest */
3516c77a61a7Syz 	setup.wValue = cs<<8;
3517c77a61a7Syz 	setup.wIndex = entity_id<<8;
3518c77a61a7Syz 	setup.wLength = wlength;
3519c77a61a7Syz 	setup.attrs = 0;
3520c77a61a7Syz 
3521c77a61a7Syz 	if (usb_pipe_ctrl_xfer_wait(usbvcp->usbvc_default_ph, &setup, &data,
3522c77a61a7Syz 	    &cr, &cb_flags, 0) != USB_SUCCESS) {
3523c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3524c77a61a7Syz 		    "usbvc_vc_set_ctrl: cmd failed, cr=%d, cb_flags=%x",
3525c77a61a7Syz 		    cr, cb_flags);
3526c77a61a7Syz 
3527c77a61a7Syz 		return (USB_FAILURE);
3528c77a61a7Syz 	}
3529c77a61a7Syz 
3530c77a61a7Syz 	return (USB_SUCCESS);
3531c77a61a7Syz }
3532c77a61a7Syz 
3533c77a61a7Syz 
3534c77a61a7Syz /* Set probe or commit ctrl for video stream interface */
3535c77a61a7Syz int
3536c77a61a7Syz usbvc_vs_set_probe_commit(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if,
3537c77a61a7Syz 	usbvc_vs_probe_commit_t *ctrl_pc, uchar_t cs)
3538c77a61a7Syz {
3539c77a61a7Syz 	mblk_t *data;
3540c77a61a7Syz 	usb_cb_flags_t	cb_flags;
3541c77a61a7Syz 	usb_cr_t	cr;
3542c77a61a7Syz 	usb_ctrl_setup_t setup;
3543c77a61a7Syz 	int rval;
3544c77a61a7Syz 
3545c77a61a7Syz 	setup.bmRequestType = USBVC_SET_IF;	/* bmRequestType */
3546c77a61a7Syz 	setup.bRequest = SET_CUR;		/* bRequest */
3547c77a61a7Syz 
3548c77a61a7Syz 	/* wValue, VS_PROBE_CONTROL or VS_COMMIT_CONTROL */
3549c77a61a7Syz 	setup.wValue = cs;
3550c77a61a7Syz 
3551c77a61a7Syz 	/* UVC Spec: this value must be put to the high byte */
3552c77a61a7Syz 	setup.wValue = setup.wValue << 8;
3553c77a61a7Syz 
3554c77a61a7Syz 	setup.wIndex = strm_if->if_descr->if_alt->altif_descr.bInterfaceNumber;
3555c77a61a7Syz 	setup.wLength = usbvcp->usbvc_vc_header->descr->bcdUVC[0] ? 34 : 26;
3556c77a61a7Syz 	setup.attrs = 0;
3557c77a61a7Syz 
3558c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3559c77a61a7Syz 	    "usbvc_vs_set_probe_commit: wLength=%d", setup.wLength);
3560c77a61a7Syz 
3561c77a61a7Syz 	/* Data block */
3562c77a61a7Syz 	if ((data = allocb(setup.wLength, BPRI_HI)) == NULL) {
3563c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3564c77a61a7Syz 		    "usbvc_vs_set_probe_commit: allocb failed");
3565c77a61a7Syz 
3566c77a61a7Syz 		return (USB_FAILURE);
3567c77a61a7Syz 	}
3568c77a61a7Syz 
3569c77a61a7Syz 	bcopy(ctrl_pc, data->b_rptr, setup.wLength);
3570c77a61a7Syz 	data->b_wptr += setup.wLength;
3571c77a61a7Syz 
3572c77a61a7Syz 	if ((rval = usb_pipe_ctrl_xfer_wait(usbvcp->usbvc_default_ph, &setup,
3573c77a61a7Syz 	    &data, &cr, &cb_flags, 0)) != USB_SUCCESS) {
3574c77a61a7Syz 		if (data) {
3575c77a61a7Syz 			freemsg(data);
3576c77a61a7Syz 		}
3577c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3578c77a61a7Syz 		    "usbvc_vs_set_probe_commit: fail, rval=%d, cr=%d, "
3579c77a61a7Syz 		    "cb_flags=%x", rval, cr, cb_flags);
3580c77a61a7Syz 
3581c77a61a7Syz 		return (rval);
3582c77a61a7Syz 	}
3583c77a61a7Syz 	if (data) {
3584c77a61a7Syz 		freemsg(data);
3585c77a61a7Syz 	}
3586c77a61a7Syz 
3587c77a61a7Syz 	return (USB_SUCCESS);
3588c77a61a7Syz }
3589c77a61a7Syz 
3590c77a61a7Syz 
3591c77a61a7Syz /* Get probe ctrl for vodeo stream interface */
3592c77a61a7Syz int
3593c77a61a7Syz usbvc_vs_get_probe(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if,
3594c77a61a7Syz 	usbvc_vs_probe_commit_t *ctrl_pc, uchar_t bRequest)
3595c77a61a7Syz {
3596c77a61a7Syz 	mblk_t *data = NULL;
3597c77a61a7Syz 	usb_cb_flags_t	cb_flags;
3598c77a61a7Syz 	usb_cr_t	cr;
3599c77a61a7Syz 	usb_ctrl_setup_t setup;
3600c77a61a7Syz 
3601c77a61a7Syz 	setup.bmRequestType = USBVC_GET_IF;	/* bmRequestType */
3602c77a61a7Syz 	setup.bRequest = bRequest;		/* bRequest */
3603c77a61a7Syz 	setup.wValue = VS_PROBE_CONTROL;	/* wValue, PROBE or COMMIT */
3604c77a61a7Syz 	setup.wValue = setup.wValue << 8;
3605c77a61a7Syz 	setup.wIndex =
3606c77a61a7Syz 	    (uint16_t)strm_if->if_descr->if_alt->altif_descr.bInterfaceNumber;
3607c77a61a7Syz 	setup.wLength = usbvcp->usbvc_vc_header->descr->bcdUVC[0] ? 34 : 26;
3608c77a61a7Syz 
3609c77a61a7Syz 	setup.attrs = 0;
3610c77a61a7Syz 
3611c77a61a7Syz 	if (usb_pipe_ctrl_xfer_wait(usbvcp->usbvc_default_ph, &setup, &data,
3612c77a61a7Syz 	    &cr, &cb_flags, 0) != USB_SUCCESS) {
3613c77a61a7Syz 		if (data) {
3614c77a61a7Syz 			freemsg(data);
3615c77a61a7Syz 		}
3616c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3617c77a61a7Syz 		    "usbvc_vs_get_probe: cmd failed, cr=%d, cb_flags=%x",
3618c77a61a7Syz 		    cr, cb_flags);
3619c77a61a7Syz 
3620c77a61a7Syz 		return (USB_FAILURE);
3621c77a61a7Syz 	}
3622c77a61a7Syz 	bcopy(data->b_rptr, ctrl_pc, setup.wLength);
3623c77a61a7Syz 	if (data) {
3624c77a61a7Syz 		freemsg(data);
3625c77a61a7Syz 	}
3626c77a61a7Syz 
3627c77a61a7Syz 	return (USB_SUCCESS);
3628c77a61a7Syz }
3629c77a61a7Syz 
3630c77a61a7Syz 
3631c77a61a7Syz /* Set a default format when open the device */
3632c77a61a7Syz static int
3633c77a61a7Syz usbvc_set_default_stream_fmt(usbvc_state_t *usbvcp)
3634c77a61a7Syz {
3635c77a61a7Syz 	usbvc_vs_probe_commit_t ctrl, ctrl_get;
3636c77a61a7Syz 	usbvc_stream_if_t *strm_if;
3637c77a61a7Syz 	usbvc_format_group_t *curr_fmtgrp;
3638c77a61a7Syz 	uint32_t bandwidth;
3639c77a61a7Syz 	uint8_t  index, i;
3640c77a61a7Syz 
3641c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3642c77a61a7Syz 	    "usbvc_set_default_stream_fmt: enter");
3643c77a61a7Syz 
3644c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
3645c77a61a7Syz 	if (list_is_empty(&usbvcp->usbvc_stream_list)) {
3646c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3647c77a61a7Syz 		    "usbvc_set_default_stream_fmt: no stream interface, fail");
3648c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
3649c77a61a7Syz 
3650c77a61a7Syz 		return (USB_FAILURE);
3651c77a61a7Syz 	}
3652c77a61a7Syz 	bzero((void *)&ctrl, sizeof (usbvc_vs_probe_commit_t));
3653c77a61a7Syz 
3654c77a61a7Syz 	/* Get the current stream interface */
3655c77a61a7Syz 	strm_if = usbvcp->usbvc_curr_strm;
3656c77a61a7Syz 
3657c77a61a7Syz 	/* Fill the probe commit req data */
3658c77a61a7Syz 	ctrl.bmHint[0] = 0;
3659c77a61a7Syz 
3660c77a61a7Syz 	for (i = 0; i < strm_if->fmtgrp_cnt; i++) {
3661c77a61a7Syz 		curr_fmtgrp = &strm_if->format_group[i];
3662c77a61a7Syz 
3663c77a61a7Syz 		/*
3664c77a61a7Syz 		 * If v4l2_pixelformat is NULL, then that means there is not
3665c77a61a7Syz 		 * a parsed format in format_group[i].
3666c77a61a7Syz 		 */
3667c77a61a7Syz 		if (!curr_fmtgrp || !curr_fmtgrp->v4l2_pixelformat ||
3668c77a61a7Syz 		    curr_fmtgrp->frame_cnt == 0) {
3669c77a61a7Syz 			USB_DPRINTF_L2(PRINT_MASK_DEVCTRL,
3670c77a61a7Syz 			    usbvcp->usbvc_log_handle,
3671c77a61a7Syz 			    "usbvc_set_default_stream_fmt: no frame, fail");
3672c77a61a7Syz 
3673c77a61a7Syz 			continue;
3674c77a61a7Syz 		} else {
3675c77a61a7Syz 
3676c77a61a7Syz 			break;
3677c77a61a7Syz 		}
3678c77a61a7Syz 	}
3679c77a61a7Syz 	if (!curr_fmtgrp || curr_fmtgrp->frame_cnt == 0) {
3680c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3681c77a61a7Syz 		    "usbvc_set_default_stream_fmt: can't find a fmtgrp"
3682c77a61a7Syz 		    "which has a frame, fail");
3683c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
3684c77a61a7Syz 
3685c77a61a7Syz 		return (USB_FAILURE);
3686c77a61a7Syz 	}
3687c77a61a7Syz 
3688c77a61a7Syz 	ctrl.bFormatIndex = curr_fmtgrp->format->bFormatIndex;
3689c77a61a7Syz 
3690c77a61a7Syz 	/* use the first frame descr as default */
3691c77a61a7Syz 	ctrl.bFrameIndex = curr_fmtgrp->frames[0].descr->bFrameIndex;
3692c77a61a7Syz 
3693c77a61a7Syz 	/* use bcopy to keep the byte sequence as 32 bit little endian */
3694c77a61a7Syz 	bcopy(&(curr_fmtgrp->frames[0].descr->dwDefaultFrameInterval[0]),
3695c77a61a7Syz 	    &(ctrl.dwFrameInterval[0]), 4);
3696c77a61a7Syz 
3697c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
3698c77a61a7Syz 	if (usbvc_vs_set_probe_commit(usbvcp, strm_if, &ctrl, VS_PROBE_CONTROL)
3699c77a61a7Syz 	    != USB_SUCCESS) {
3700c77a61a7Syz 
3701c77a61a7Syz 		return (USB_FAILURE);
3702c77a61a7Syz 	}
3703c77a61a7Syz 	if (usbvc_vs_get_probe(usbvcp, strm_if, &ctrl_get, GET_CUR)
3704c77a61a7Syz 	    != USB_SUCCESS) {
3705c77a61a7Syz 
3706c77a61a7Syz 		return (USB_FAILURE);
3707c77a61a7Syz 	}
3708c77a61a7Syz 
3709c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
3710c77a61a7Syz 	LE_TO_UINT32(strm_if->ctrl_pc.dwMaxPayloadTransferSize, 0, bandwidth);
3711c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3712c77a61a7Syz 	    "usbvc_set_default_stream_fmt: get bandwidth=%x", bandwidth);
3713c77a61a7Syz 
3714c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
3715c77a61a7Syz 	if (usbvc_vs_set_probe_commit(usbvcp, strm_if, &ctrl_get,
3716c77a61a7Syz 	    VS_COMMIT_CONTROL) != USB_SUCCESS) {
3717c77a61a7Syz 
3718c77a61a7Syz 		return (USB_FAILURE);
3719c77a61a7Syz 	}
3720c77a61a7Syz 
3721c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
3722c77a61a7Syz 
3723c77a61a7Syz 	/*  it's good to check index here before use it */
3724c77a61a7Syz 	index = ctrl_get.bFormatIndex - curr_fmtgrp->format->bFormatIndex;
3725c77a61a7Syz 	if (index < strm_if->fmtgrp_cnt) {
3726c77a61a7Syz 		strm_if->cur_format_group = &strm_if->format_group[index];
3727c77a61a7Syz 	} else {
3728c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3729c77a61a7Syz 		    "usbvc_set_default_stream_fmt: format index out of range");
3730c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
3731c77a61a7Syz 
3732c77a61a7Syz 		return (USB_FAILURE);
3733c77a61a7Syz 	}
3734c77a61a7Syz 
3735c77a61a7Syz 	index = ctrl_get.bFrameIndex -
3736c77a61a7Syz 	    strm_if->cur_format_group->frames[0].descr->bFrameIndex;
3737c77a61a7Syz 	if (index < strm_if->cur_format_group->frame_cnt) {
3738c77a61a7Syz 		strm_if->cur_format_group->cur_frame =
3739c77a61a7Syz 		    &strm_if->cur_format_group->frames[index];
3740c77a61a7Syz 	} else {
3741c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3742c77a61a7Syz 		    "usbvc_set_default_stream: frame index out of range");
3743c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
3744c77a61a7Syz 
3745c77a61a7Syz 		return (USB_FAILURE);
3746c77a61a7Syz 	}
3747c77a61a7Syz 
3748c77a61a7Syz 	/*
3749c77a61a7Syz 	 * by now, the video format is set successfully. record the current
3750c77a61a7Syz 	 * setting to strm_if->ctrl_pc
3751c77a61a7Syz 	 */
3752c77a61a7Syz 	bcopy(&ctrl_get, &strm_if->ctrl_pc, sizeof (usbvc_vs_probe_commit_t));
3753c77a61a7Syz 
3754c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
3755c77a61a7Syz 
3756c77a61a7Syz 	return (USB_SUCCESS);
3757c77a61a7Syz }
3758