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