1c77a61a7Syz /*
2c77a61a7Syz  * CDDL HEADER START
3c77a61a7Syz  *
4c77a61a7Syz  * The contents of this file are subject to the terms of the
5c77a61a7Syz  * Common Development and Distribution License (the "License").
6c77a61a7Syz  * You may not use this file except in compliance with the License.
7c77a61a7Syz  *
8c77a61a7Syz  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9c77a61a7Syz  * or http://www.opensolaris.org/os/licensing.
10c77a61a7Syz  * See the License for the specific language governing permissions
11c77a61a7Syz  * and limitations under the License.
12c77a61a7Syz  *
13c77a61a7Syz  * When distributing Covered Code, include this CDDL HEADER in each
14c77a61a7Syz  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15c77a61a7Syz  * If applicable, add the following below this CDDL HEADER, with the
16c77a61a7Syz  * fields enclosed by brackets "[]" replaced with your own identifying
17c77a61a7Syz  * information: Portions Copyright [yyyy] [name of copyright owner]
18c77a61a7Syz  *
19c77a61a7Syz  * CDDL HEADER END
20c77a61a7Syz  */
21c77a61a7Syz /*
22112116d8Sfb  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23c77a61a7Syz  * Use is subject to license terms.
24c77a61a7Syz  */
25c77a61a7Syz 
26c77a61a7Syz 
27c77a61a7Syz /*
28c77a61a7Syz  * USB video class driver: V4L2 interface implementation.
29c77a61a7Syz  */
30c77a61a7Syz 
31c77a61a7Syz #include <sys/usb/usba.h>
32c77a61a7Syz #include <sys/fcntl.h>
33c77a61a7Syz #include <sys/cmn_err.h>
34c77a61a7Syz 
35c77a61a7Syz #include <sys/usb/clients/video/usbvc/usbvc_var.h>
36c77a61a7Syz #include <sys/usb/clients/video/usbvc/usbvc.h>
37c77a61a7Syz #include <sys/videodev2.h>
38c77a61a7Syz 
39c77a61a7Syz static int usbvc_v4l2_set_format(usbvc_state_t *, struct v4l2_format *);
40c77a61a7Syz static int usbvc_v4l2_get_format(usbvc_state_t *, struct v4l2_format *);
41c77a61a7Syz static void usbvc_v4l2_query_buf(usbvc_state_t *, usbvc_buf_t *,
42c77a61a7Syz 				struct v4l2_buffer *);
43c77a61a7Syz static int usbvc_v4l2_enqueue_buf(usbvc_state_t *, usbvc_buf_t *,
44c77a61a7Syz 				struct v4l2_buffer *);
45c77a61a7Syz static int usbvc_v4l2_dequeue_buffer(usbvc_state_t *,
46c77a61a7Syz 				struct v4l2_buffer *, int);
47c77a61a7Syz static int usbvc_v4l2_query_ctrl(usbvc_state_t *, struct v4l2_queryctrl *);
48c77a61a7Syz static int usbvc_v4l2_get_ctrl(usbvc_state_t *, struct v4l2_control *);
49c77a61a7Syz static int usbvc_v4l2_set_ctrl(usbvc_state_t *, struct v4l2_control *);
5061b2e298Slc static int usbvc_v4l2_set_parm(usbvc_state_t *, struct v4l2_streamparm *);
5161b2e298Slc static int usbvc_v4l2_get_parm(usbvc_state_t *, struct v4l2_streamparm *);
52c77a61a7Syz /* Video controls that supported by usbvc driver */
53c77a61a7Syz static usbvc_v4l2_ctrl_map_t usbvc_v4l2_ctrls[] = {
54c77a61a7Syz 	{
55c77a61a7Syz 		"Brightness",
56c77a61a7Syz 		PU_BRIGHTNESS_CONTROL,
57c77a61a7Syz 		2,
58c77a61a7Syz 		0,
59c77a61a7Syz 		V4L2_CTRL_TYPE_INTEGER
60c77a61a7Syz 	},
61c77a61a7Syz 	{
62c77a61a7Syz 		"Contrast",
63c77a61a7Syz 		PU_CONTRAST_CONTROL,
64c77a61a7Syz 		2,
65c77a61a7Syz 		1,
66c77a61a7Syz 		V4L2_CTRL_TYPE_INTEGER
67c77a61a7Syz 	},
68c77a61a7Syz 	{
69c77a61a7Syz 		"Saturation",
70c77a61a7Syz 		PU_SATURATION_CONTROL,
71c77a61a7Syz 		2,
72c77a61a7Syz 		3,
73c77a61a7Syz 		V4L2_CTRL_TYPE_INTEGER
74c77a61a7Syz 	},
75c77a61a7Syz 	{
76c77a61a7Syz 		"Hue",
77c77a61a7Syz 		PU_HUE_CONTROL,
78c77a61a7Syz 		2,
79c77a61a7Syz 		2,
80c77a61a7Syz 		V4L2_CTRL_TYPE_INTEGER
81c77a61a7Syz 	},
82c77a61a7Syz 	{
83c77a61a7Syz 		"Gamma",
84c77a61a7Syz 		PU_GAMMA_CONTROL,
85c77a61a7Syz 		2,
86c77a61a7Syz 		5,
87c77a61a7Syz 		V4L2_CTRL_TYPE_INTEGER
88c77a61a7Syz 	}
89c77a61a7Syz };
90c77a61a7Syz 
91c77a61a7Syz 
92c77a61a7Syz /*
93c77a61a7Syz  * V4L2 colorspaces.
94c77a61a7Syz  */
95c77a61a7Syz static const uint8_t color_primaries[] = {
96c77a61a7Syz 		0,
97c77a61a7Syz 		V4L2_COLORSPACE_SRGB,
98c77a61a7Syz 		V4L2_COLORSPACE_470_SYSTEM_M,
99c77a61a7Syz 		V4L2_COLORSPACE_470_SYSTEM_BG,
100c77a61a7Syz 		V4L2_COLORSPACE_SMPTE170M,
101c77a61a7Syz 		V4L2_COLORSPACE_SMPTE240M,
102c77a61a7Syz };
103c77a61a7Syz 
104c77a61a7Syz /* V4L2 ioctls */
105c77a61a7Syz int
usbvc_v4l2_ioctl(usbvc_state_t * usbvcp,int cmd,intptr_t arg,int mode)106c77a61a7Syz usbvc_v4l2_ioctl(usbvc_state_t *usbvcp, int cmd, intptr_t arg, int mode)
107c77a61a7Syz {
108c77a61a7Syz 	int	rv = 0;
109c77a61a7Syz 
110c77a61a7Syz 	switch (cmd) {
111c77a61a7Syz 	case VIDIOC_QUERYCAP:	/* Query capabilities */
112c77a61a7Syz 	{
113c77a61a7Syz 		struct v4l2_capability caps;
114c77a61a7Syz 
11561b2e298Slc 		USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
116c77a61a7Syz 		    "V4L2 ioctl: VIDIOC_QUERYCAP");
117c77a61a7Syz 		bzero(&caps, sizeof (caps));
118c77a61a7Syz 		(void) strncpy((char *)&caps.driver, "usbvc",
119c77a61a7Syz 		    sizeof (caps.driver));
120c77a61a7Syz 		if (usbvcp->usbvc_reg->dev_product) {
121c77a61a7Syz 			(void) strncpy((char *)&caps.card,
122c77a61a7Syz 			    usbvcp->usbvc_reg->dev_product, sizeof (caps.card));
123c77a61a7Syz 		} else {
124c77a61a7Syz 			(void) strncpy((char *)&caps.card, "Generic USB video"
125c77a61a7Syz 			    "class device", sizeof (caps.card));
126c77a61a7Syz 		}
127c77a61a7Syz 		(void) strncpy((char *)&caps.bus_info, "usb",
128c77a61a7Syz 		    sizeof (caps.bus_info));
129c77a61a7Syz 		caps.version = 1;
130c77a61a7Syz 		caps.capabilities = V4L2_CAP_VIDEO_CAPTURE
131c77a61a7Syz 		    | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
132c77a61a7Syz 		USBVC_COPYOUT(caps);
133c77a61a7Syz 
134c77a61a7Syz 		break;
135c77a61a7Syz 	}
136c77a61a7Syz 	case VIDIOC_ENUM_FMT:
137c77a61a7Syz 	{
138c77a61a7Syz 		struct v4l2_fmtdesc	fmtdesc;
139c77a61a7Syz 		usbvc_format_group_t	*fmtgrp;
140c77a61a7Syz 		usbvc_stream_if_t	*strm_if;
141c77a61a7Syz 
14261b2e298Slc 		USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
143c77a61a7Syz 		    "V4L2 ioctl: VIDIOC_ENUM_FMT");
144c77a61a7Syz 		USBVC_COPYIN(fmtdesc);
145c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
146c77a61a7Syz 		strm_if = usbvcp->usbvc_curr_strm;
147c77a61a7Syz 		if (fmtdesc.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
148c77a61a7Syz 		    fmtdesc.index >= strm_if->fmtgrp_cnt) {
149c77a61a7Syz 			rv = EINVAL;
150c77a61a7Syz 			mutex_exit(&usbvcp->usbvc_mutex);
151c77a61a7Syz 
152c77a61a7Syz 			break;
153c77a61a7Syz 		}
154c77a61a7Syz 		fmtgrp = &strm_if->format_group[fmtdesc.index];
155c77a61a7Syz 		fmtdesc.pixelformat = fmtgrp->v4l2_pixelformat;
15661b2e298Slc 		USB_DPRINTF_L3(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
157c77a61a7Syz 		    "V4L2 ioctl: VIDIOC_ENUM_FMT, idx=%d, grpcnt=%d",
158c77a61a7Syz 		    fmtdesc.index, strm_if->fmtgrp_cnt);
159c77a61a7Syz 
160c77a61a7Syz 		switch (fmtgrp->format->bDescriptorSubType) {
161c77a61a7Syz 		case VS_FORMAT_MJPEG:
162c77a61a7Syz 			fmtdesc.flags = V4L2_FMT_FLAG_COMPRESSED;
163c77a61a7Syz 			(void) strncpy(fmtdesc.description, "MJPEG",
164c77a61a7Syz 			    sizeof (fmtdesc.description));
165c77a61a7Syz 
166c77a61a7Syz 			break;
167c77a61a7Syz 		case VS_FORMAT_UNCOMPRESSED:
168c77a61a7Syz 			fmtdesc.flags = 0;
169c77a61a7Syz 			if (fmtdesc.pixelformat == V4L2_PIX_FMT_YUYV) {
170c77a61a7Syz 				(void) strncpy(fmtdesc.description, "YUYV",
171c77a61a7Syz 				    sizeof (fmtdesc.description));
172c77a61a7Syz 			} else if (fmtdesc.pixelformat == V4L2_PIX_FMT_NV12) {
173c77a61a7Syz 				(void) strncpy(fmtdesc.description, "NV12",
174c77a61a7Syz 				    sizeof (fmtdesc.description));
175c77a61a7Syz 			} else {
176c77a61a7Syz 				(void) strncpy(fmtdesc.description,
177c77a61a7Syz 				    "Unknown format",
178c77a61a7Syz 				    sizeof (fmtdesc.description));
179c77a61a7Syz 			}
180c77a61a7Syz 
181c77a61a7Syz 			break;
182c77a61a7Syz 		default:
183c77a61a7Syz 			fmtdesc.flags = 0;
184c77a61a7Syz 			(void) strncpy(fmtdesc.description, "Unknown format",
185c77a61a7Syz 			    sizeof (fmtdesc.description));
186c77a61a7Syz 		}
187c77a61a7Syz 
188c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
189c77a61a7Syz 		USBVC_COPYOUT(fmtdesc);
190c77a61a7Syz 
191c77a61a7Syz 		break;
192c77a61a7Syz 	}
193c77a61a7Syz 	case VIDIOC_S_FMT:
194c77a61a7Syz 	{
195c77a61a7Syz 		struct v4l2_format	fmt;
196c77a61a7Syz 		usbvc_stream_if_t	*strm_if;
197c77a61a7Syz 
19861b2e298Slc 		USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
199c77a61a7Syz 		    "V4L2 ioctl: VIDIOC_S_FMT");
200c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
201c77a61a7Syz 		strm_if = usbvcp->usbvc_curr_strm;
202c77a61a7Syz 
203c77a61a7Syz 		/* If data I/O is in progress */
204c77a61a7Syz 		if (strm_if->start_polling == 1) {
205c77a61a7Syz 			rv = EBUSY;
206c77a61a7Syz 			mutex_exit(&usbvcp->usbvc_mutex);
207c77a61a7Syz 
208c77a61a7Syz 			break;
209c77a61a7Syz 		}
210c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
211c77a61a7Syz 
212c77a61a7Syz 		USBVC_COPYIN(fmt);
213c77a61a7Syz 		if (usbvc_v4l2_set_format(usbvcp, &fmt) != USB_SUCCESS) {
214c77a61a7Syz 			rv = EFAULT;
215c77a61a7Syz 			USB_DPRINTF_L2(PRINT_MASK_IOCTL,
216c77a61a7Syz 			    usbvcp->usbvc_log_handle,
217c77a61a7Syz 			    "V4L2 ioctl VIDIOC_S_FMT fail");
218c77a61a7Syz 		}
219c77a61a7Syz 		USBVC_COPYOUT(fmt);
220c77a61a7Syz 
221c77a61a7Syz 		break;
222c77a61a7Syz 	}
223c77a61a7Syz 	case VIDIOC_G_FMT:
224c77a61a7Syz 	{
225c77a61a7Syz 		struct v4l2_format	fmt;
226c77a61a7Syz 
22761b2e298Slc 		USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
228c77a61a7Syz 		    "V4L2 ioctl: VIDIOC_G_FMT");
229c77a61a7Syz 		USBVC_COPYIN(fmt);
230c77a61a7Syz 
231c77a61a7Syz 		if ((rv = usbvc_v4l2_get_format(usbvcp, &fmt)) != 0) {
232c77a61a7Syz 
233c77a61a7Syz 			break;
234c77a61a7Syz 		}
235c77a61a7Syz 
236c77a61a7Syz 		USBVC_COPYOUT(fmt);
237c77a61a7Syz 
238c77a61a7Syz 		break;
239c77a61a7Syz 	}
240c77a61a7Syz 	case VIDIOC_REQBUFS: /* for memory mapping IO method */
241c77a61a7Syz 	{
242c77a61a7Syz 		struct v4l2_requestbuffers	reqbuf;
243c77a61a7Syz 		uint_t				bufsize;
244c77a61a7Syz 		usbvc_stream_if_t		*strm_if;
245c77a61a7Syz 
24661b2e298Slc 		USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
247c77a61a7Syz 		    "V4L2 ioctl: VIDIOC_REQBUFS");
248c77a61a7Syz 		USBVC_COPYIN(reqbuf);
249c77a61a7Syz 		if (reqbuf.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
250c77a61a7Syz 		    reqbuf.memory != V4L2_MEMORY_MMAP) {
251c77a61a7Syz 			rv = EINVAL;
252c77a61a7Syz 
253c77a61a7Syz 			break;
254c77a61a7Syz 		}
255c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
256c77a61a7Syz 		strm_if = usbvcp->usbvc_curr_strm;
257c77a61a7Syz 		if (!strm_if) {
258c77a61a7Syz 			mutex_exit(&usbvcp->usbvc_mutex);
259c77a61a7Syz 			rv = EINVAL;
260c77a61a7Syz 
261c77a61a7Syz 			break;
262c77a61a7Syz 		}
263c77a61a7Syz 		if (reqbuf.count > USBVC_MAX_MAP_BUF_NUM) {
264c77a61a7Syz 			mutex_exit(&usbvcp->usbvc_mutex);
265c77a61a7Syz 			USB_DPRINTF_L2(PRINT_MASK_IOCTL,
266c77a61a7Syz 			    usbvcp->usbvc_log_handle,
267c77a61a7Syz 			    "V4L2 ioctl: req too many buffers, fail");
268c77a61a7Syz 			rv = EINVAL;
269c77a61a7Syz 
270c77a61a7Syz 			break;
271c77a61a7Syz 		}
272c77a61a7Syz 
273c77a61a7Syz 		/* If some bufs were already allocated */
274c77a61a7Syz 		if (strm_if->buf_map.buf_cnt) {
275c77a61a7Syz 			/*
276c77a61a7Syz 			 * According to v4l2 spec, application can change the
277c77a61a7Syz 			 * buffer number and also free all buffers if set
278c77a61a7Syz 			 * count to 0
279c77a61a7Syz 			 */
280c77a61a7Syz 			if (reqbuf.count == 0) {
281c77a61a7Syz 				if (strm_if->start_polling == 1) {
282c77a61a7Syz 					mutex_exit(&usbvcp->usbvc_mutex);
283c77a61a7Syz 					usb_pipe_stop_isoc_polling(
284c77a61a7Syz 					    strm_if->datain_ph,
285c77a61a7Syz 					    USB_FLAGS_SLEEP);
286c77a61a7Syz 					mutex_enter(&usbvcp->usbvc_mutex);
287c77a61a7Syz 					strm_if->start_polling = 0;
288c77a61a7Syz 				}
289c77a61a7Syz 				usbvc_free_map_bufs(usbvcp, strm_if);
290c77a61a7Syz 				mutex_exit(&usbvcp->usbvc_mutex);
291c77a61a7Syz 
292c77a61a7Syz 				break;
293c77a61a7Syz 			}
294c77a61a7Syz 			if (reqbuf.count == strm_if->buf_map.buf_cnt) {
295c77a61a7Syz 				mutex_exit(&usbvcp->usbvc_mutex);
296c77a61a7Syz 				USB_DPRINTF_L2(PRINT_MASK_IOCTL,
297c77a61a7Syz 				    usbvcp->usbvc_log_handle,
298c77a61a7Syz 				    "v4l2 ioctls: req the same buffers"
299c77a61a7Syz 				    " as we already have, just return success");
300c77a61a7Syz 
301c77a61a7Syz 				break;
302c77a61a7Syz 			} else {
303c77a61a7Syz 				/*
304c77a61a7Syz 				 * req different number of bufs, according to
305c77a61a7Syz 				 * v4l2 spec, this is not allowed when there
306c77a61a7Syz 				 * are some bufs still mapped.
307c77a61a7Syz 				 */
308c77a61a7Syz 				mutex_exit(&usbvcp->usbvc_mutex);
309c77a61a7Syz 				USB_DPRINTF_L2(PRINT_MASK_IOCTL,
310c77a61a7Syz 				    usbvcp->usbvc_log_handle,
311c77a61a7Syz 				    "v4l2 ioctls: req different number bufs"
312c77a61a7Syz 				    "than the exist ones, fail");
313c77a61a7Syz 				rv = EINVAL;
314c77a61a7Syz 
315c77a61a7Syz 				break;
316c77a61a7Syz 			}
317c77a61a7Syz 		}
318c77a61a7Syz 
319c77a61a7Syz 		if (reqbuf.count == 0) {
320c77a61a7Syz 			mutex_exit(&usbvcp->usbvc_mutex);
321c77a61a7Syz 			rv = EINVAL;
322c77a61a7Syz 
323c77a61a7Syz 			break;
324c77a61a7Syz 		}
325c77a61a7Syz 		LE_TO_UINT32(strm_if->ctrl_pc.dwMaxVideoFrameSize, 0, bufsize);
326c77a61a7Syz 		if ((reqbuf.count =
327c77a61a7Syz 		    (uint32_t)usbvc_alloc_map_bufs(usbvcp, strm_if,
328c77a61a7Syz 		    reqbuf.count, bufsize)) == 0) {
329c77a61a7Syz 			mutex_exit(&usbvcp->usbvc_mutex);
330c77a61a7Syz 			USB_DPRINTF_L2(PRINT_MASK_IOCTL,
331c77a61a7Syz 			    usbvcp->usbvc_log_handle,
332c77a61a7Syz 			    "V4L2 ioctl: VIDIOC_REQBUFS: alloc fail");
333c77a61a7Syz 			rv = EINVAL;
334c77a61a7Syz 
335c77a61a7Syz 			break;
336c77a61a7Syz 		}
337c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
338c77a61a7Syz 
339c77a61a7Syz 		/*
340c77a61a7Syz 		 * return buf number that acctually allocated to application
341c77a61a7Syz 		 */
342c77a61a7Syz 		USBVC_COPYOUT(reqbuf);
343c77a61a7Syz 
344c77a61a7Syz 		break;
345c77a61a7Syz 	}
346c77a61a7Syz 	case VIDIOC_QUERYBUF: /* for memory mapping IO method */
347c77a61a7Syz 	{
348c77a61a7Syz 		struct v4l2_buffer	buf;
349c77a61a7Syz 		usbvc_buf_grp_t		*usbvc_bufg;
350c77a61a7Syz 
351c77a61a7Syz 		USBVC_COPYIN(buf);
35261b2e298Slc 		USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
353c77a61a7Syz 		    "V4L2 ioctl: VIDIOC_QUERYBUF: idx=%d", buf.index);
354c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
355c77a61a7Syz 		usbvc_bufg = &usbvcp->usbvc_curr_strm->buf_map;
356c77a61a7Syz 		if ((buf.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
357c77a61a7Syz 		    (buf.index >= usbvc_bufg->buf_cnt)) {
358c77a61a7Syz 			mutex_exit(&usbvcp->usbvc_mutex);
359c77a61a7Syz 			rv = EINVAL;
360c77a61a7Syz 
361c77a61a7Syz 			break;
362c77a61a7Syz 		}
363c77a61a7Syz 
364c77a61a7Syz 		USB_DPRINTF_L3(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
365c77a61a7Syz 		    "V4L2 ioctl: VIDIOC_QUERYBUF: len=%d",
366c77a61a7Syz 		    usbvc_bufg->buf_head[buf.index].v4l2_buf.length);
367c77a61a7Syz 
368c77a61a7Syz 		usbvc_v4l2_query_buf(usbvcp, &usbvc_bufg->buf_head[buf.index],
369c77a61a7Syz 		    &buf);
370c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
371c77a61a7Syz 		USBVC_COPYOUT(buf);
372c77a61a7Syz 		USB_DPRINTF_L3(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
373c77a61a7Syz 		    "V4L2 ioctl: VIDIOC_QUERYBUF,(index=%d)len=%d",
374c77a61a7Syz 		    buf.index, buf.length);
375c77a61a7Syz 
376c77a61a7Syz 		break;
377c77a61a7Syz 	}
378c77a61a7Syz 	case VIDIOC_QBUF:
379c77a61a7Syz 	{
380c77a61a7Syz 		struct v4l2_buffer	buf;
381c77a61a7Syz 		usbvc_buf_grp_t		*usbvc_bufg;
382c77a61a7Syz 
383c77a61a7Syz 		USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
384c77a61a7Syz 		    "V4L2 ioctl: VIDIOC_QBUF");
385c77a61a7Syz 		USBVC_COPYIN(buf);
386c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
387c77a61a7Syz 		usbvc_bufg = &usbvcp->usbvc_curr_strm->buf_map;
388c77a61a7Syz 
389c77a61a7Syz 		if ((buf.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
390c77a61a7Syz 		    (buf.index >= usbvc_bufg->buf_cnt) ||
391c77a61a7Syz 		    (buf.memory != V4L2_MEMORY_MMAP)) {
392c77a61a7Syz 			mutex_exit(&usbvcp->usbvc_mutex);
393c77a61a7Syz 			USB_DPRINTF_L2(PRINT_MASK_IOCTL,
394c77a61a7Syz 			    usbvcp->usbvc_log_handle,  "V4L2 ioctl: "
395c77a61a7Syz 			    "VIDIOC_QBUF error:index=%d,type=%d,memory=%d",
396c77a61a7Syz 			    buf.index, buf.type, buf.memory);
397c77a61a7Syz 			rv = EINVAL;
398c77a61a7Syz 
399c77a61a7Syz 			break;
400c77a61a7Syz 		}
401c77a61a7Syz 		rv = usbvc_v4l2_enqueue_buf(usbvcp,
402c77a61a7Syz 		    &usbvc_bufg->buf_head[buf.index], &buf);
403c77a61a7Syz 		if (rv < 0) {
404c77a61a7Syz 			mutex_exit(&usbvcp->usbvc_mutex);
405c77a61a7Syz 
406c77a61a7Syz 			break;
407c77a61a7Syz 		}
408c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
409c77a61a7Syz 		USBVC_COPYOUT(buf);
410c77a61a7Syz 
411c77a61a7Syz 		break;
412c77a61a7Syz 	}
413c77a61a7Syz 
414c77a61a7Syz 	case VIDIOC_DQBUF:
415c77a61a7Syz 	{
416c77a61a7Syz 		struct v4l2_buffer	buf;
417c77a61a7Syz 
418c77a61a7Syz 		USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
419c77a61a7Syz 		    "V4L2 ioctl: VIDIOC_DQBUF");
420c77a61a7Syz 		USBVC_COPYIN(buf);
421c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
422c77a61a7Syz 		if ((rv = usbvc_v4l2_dequeue_buffer(usbvcp, &buf, mode)) != 0) {
423c77a61a7Syz 			mutex_exit(&usbvcp->usbvc_mutex);
424c77a61a7Syz 			USB_DPRINTF_L2(PRINT_MASK_IOCTL,
425c77a61a7Syz 			    usbvcp->usbvc_log_handle, "V4L2 ioctl: "
426c77a61a7Syz 			    "VIDIOC_DQBUF: fail, rv=%d", rv);
427c77a61a7Syz 
428c77a61a7Syz 			break;
429c77a61a7Syz 		}
430c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
431c77a61a7Syz 		USBVC_COPYOUT(buf);
432c77a61a7Syz 
433c77a61a7Syz 		break;
434c77a61a7Syz 	}
435c77a61a7Syz 
436c77a61a7Syz 	case VIDIOC_STREAMON:
437c77a61a7Syz 	{
438c77a61a7Syz 		int			type; /* v4l2_buf_type */
439c77a61a7Syz 		usbvc_stream_if_t	*strm_if;
440c77a61a7Syz 
44161b2e298Slc 		USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
442c77a61a7Syz 		    "V4L2 ioctl: VIDIOC_STREAMON");
443c77a61a7Syz 		USBVC_COPYIN(type);
444c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
445c77a61a7Syz 		strm_if = usbvcp->usbvc_curr_strm;
446c77a61a7Syz 		if (!strm_if) {
447c77a61a7Syz 			mutex_exit(&usbvcp->usbvc_mutex);
448c77a61a7Syz 			rv = EINVAL;
449c77a61a7Syz 
450c77a61a7Syz 			break;
451c77a61a7Syz 		}
452c77a61a7Syz 		if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
453c77a61a7Syz 			mutex_exit(&usbvcp->usbvc_mutex);
454c77a61a7Syz 			USB_DPRINTF_L2(PRINT_MASK_IOCTL,
455c77a61a7Syz 			    usbvcp->usbvc_log_handle, "V4L2 ioctl: "
456c77a61a7Syz 			    "VIDIOC_STREAMON: fail. Only capture type is"
457c77a61a7Syz 			    " supported by now.");
458c77a61a7Syz 			rv = EINVAL;
459c77a61a7Syz 
460c77a61a7Syz 			break;
461c77a61a7Syz 		}
462c77a61a7Syz 		/* if the first read, open isoc pipe */
463c77a61a7Syz 		if (!strm_if->datain_ph) {
464c77a61a7Syz 			if (usbvc_open_isoc_pipe(usbvcp, strm_if) !=
465c77a61a7Syz 			    USB_SUCCESS) {
466c77a61a7Syz 				mutex_exit(&usbvcp->usbvc_mutex);
467c77a61a7Syz 				USB_DPRINTF_L2(PRINT_MASK_IOCTL,
468c77a61a7Syz 				    usbvcp->usbvc_log_handle, "V4L2 ioctl:"
469c77a61a7Syz 				    " first read, open pipe fail");
470c77a61a7Syz 				rv = EINVAL;
471c77a61a7Syz 
472c77a61a7Syz 				break;
473c77a61a7Syz 			}
474c77a61a7Syz 		}
475c77a61a7Syz 		/* If it is already started */
476c77a61a7Syz 		if (strm_if->start_polling == 1) {
477c77a61a7Syz 			mutex_exit(&usbvcp->usbvc_mutex);
478c77a61a7Syz 
479c77a61a7Syz 			break;
480c77a61a7Syz 		}
481c77a61a7Syz 		/* At present, VIDIOC_STREAMON supports mmap io only. */
482c77a61a7Syz 		if (usbvc_start_isoc_polling(usbvcp, strm_if,
483c77a61a7Syz 		    V4L2_MEMORY_MMAP) != USB_SUCCESS) {
484c77a61a7Syz 			rv = EFAULT;
485c77a61a7Syz 			mutex_exit(&usbvcp->usbvc_mutex);
486c77a61a7Syz 
487c77a61a7Syz 			break;
488c77a61a7Syz 		}
489c77a61a7Syz 		strm_if->start_polling = 1;
490*09dd0d6cSRaymond Chen 		strm_if->stream_on = 1; /* the only place to set this value */
491c77a61a7Syz 
492c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
493c77a61a7Syz 
494c77a61a7Syz 		break;
495c77a61a7Syz 	}
496c77a61a7Syz 
497c77a61a7Syz 	case VIDIOC_STREAMOFF:
498c77a61a7Syz 	{
499c77a61a7Syz 		int			type;	/* v4l2_buf_type */
500c77a61a7Syz 		usbvc_stream_if_t	*strm_if;
501c77a61a7Syz 
50261b2e298Slc 		USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
503c77a61a7Syz 		    "V4L2 ioctl: VIDIOC_STREAMOFF");
504c77a61a7Syz 		USBVC_COPYIN(type);
505c77a61a7Syz 		mutex_enter(&usbvcp->usbvc_mutex);
506c77a61a7Syz 		strm_if = usbvcp->usbvc_curr_strm;
507c77a61a7Syz 		if (!strm_if) {
508c77a61a7Syz 			mutex_exit(&usbvcp->usbvc_mutex);
509c77a61a7Syz 			rv = EINVAL;
510c77a61a7Syz 
511c77a61a7Syz 			break;
512c77a61a7Syz 		}
513c77a61a7Syz 		if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
514c77a61a7Syz 			mutex_exit(&usbvcp->usbvc_mutex);
515c77a61a7Syz 			USB_DPRINTF_L2(PRINT_MASK_IOCTL,
516c77a61a7Syz 			    usbvcp->usbvc_log_handle, "V4L2 ioctl: "
517c77a61a7Syz 			    "VIDIOC_STREAMON: fail. Only capture type is "
518c77a61a7Syz 			    "supported by now.");
519c77a61a7Syz 			rv = EINVAL;
520c77a61a7Syz 
521c77a61a7Syz 			break;
522c77a61a7Syz 		}
523c77a61a7Syz 
524c77a61a7Syz 		/* Need close the isoc data pipe if any reads are performed. */
525c77a61a7Syz 		strm_if = usbvcp->usbvc_curr_strm;
526c77a61a7Syz 		if (strm_if->start_polling == 1) {
527c77a61a7Syz 			mutex_exit(&usbvcp->usbvc_mutex);
528c77a61a7Syz 			usb_pipe_stop_isoc_polling(strm_if->datain_ph,
529c77a61a7Syz 			    USB_FLAGS_SLEEP);
530c77a61a7Syz 			mutex_enter(&usbvcp->usbvc_mutex);
531c77a61a7Syz 			strm_if->start_polling = 0;
532c77a61a7Syz 		}
533*09dd0d6cSRaymond Chen 		strm_if->stream_on = 0;
534c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
535c77a61a7Syz 
536c77a61a7Syz 		break;
537c77a61a7Syz 	}
538c77a61a7Syz 
539c77a61a7Syz 	case VIDIOC_ENUMINPUT:
540c77a61a7Syz 	{
541c77a61a7Syz 		struct v4l2_input input;
542c77a61a7Syz 
54361b2e298Slc 		USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
544c77a61a7Syz 		    "V4L2 ioctl: ENUMINPUT");
545c77a61a7Syz 		USBVC_COPYIN(input);
546c77a61a7Syz 
547c77a61a7Syz 		if (input.index != 0) { /* Support only one INPUT now */
548c77a61a7Syz 			rv = EINVAL;
549c77a61a7Syz 
550c77a61a7Syz 			break;
551c77a61a7Syz 		}
552c77a61a7Syz 		(void) strncpy((char *)input.name, "Camera Terminal",
553c77a61a7Syz 		    sizeof (input.name));
554c77a61a7Syz 		input.type = V4L2_INPUT_TYPE_CAMERA;
555c77a61a7Syz 		USBVC_COPYOUT(input);
556c77a61a7Syz 
557c77a61a7Syz 		break;
558c77a61a7Syz 	}
559c77a61a7Syz 
560c77a61a7Syz 	case VIDIOC_G_INPUT:
561c77a61a7Syz 	{
562c77a61a7Syz 		int input_idx = 0;	/* Support only one input now */
563c77a61a7Syz 
56461b2e298Slc 		USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
565c77a61a7Syz 		    "V4L2 ioctl: G_INPUT");
566c77a61a7Syz 		USBVC_COPYOUT(input_idx);
567c77a61a7Syz 
568c77a61a7Syz 		break;
569c77a61a7Syz 	}
570c77a61a7Syz 
571c77a61a7Syz 	case VIDIOC_S_INPUT:
572c77a61a7Syz 	{
573c77a61a7Syz 		int input_idx;
574c77a61a7Syz 
575c77a61a7Syz 		USBVC_COPYIN(input_idx);
57661b2e298Slc 		USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
577c77a61a7Syz 		    "V4L2 ioctl: S_INPUT");
578c77a61a7Syz 		if (input_idx != 0) {	/* Support only one input now */
579c77a61a7Syz 			rv = EINVAL;
580c77a61a7Syz 		}
581c77a61a7Syz 
582c77a61a7Syz 		break;
583c77a61a7Syz 	}
584c77a61a7Syz 
585c77a61a7Syz 	/* Query the device that what kinds of video ctrls are supported */
586c77a61a7Syz 	case VIDIOC_QUERYCTRL:
587c77a61a7Syz 	{
588c77a61a7Syz 		struct v4l2_queryctrl queryctrl;
589c77a61a7Syz 
59061b2e298Slc 		USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
591c77a61a7Syz 		    "V4L2 ioctl: QUERYCTRL");
592c77a61a7Syz 		USBVC_COPYIN(queryctrl);
593c77a61a7Syz 
594c77a61a7Syz 		if (usbvc_v4l2_query_ctrl(usbvcp, &queryctrl) != USB_SUCCESS) {
595c77a61a7Syz 			rv = EINVAL;
596c77a61a7Syz 
597c77a61a7Syz 			break;
598c77a61a7Syz 		}
599c77a61a7Syz 
600c77a61a7Syz 		USBVC_COPYOUT(queryctrl);
601c77a61a7Syz 
602c77a61a7Syz 		break;
603c77a61a7Syz 	}
604c77a61a7Syz 	case VIDIOC_G_CTRL:
605c77a61a7Syz 	{
606c77a61a7Syz 		struct v4l2_control ctrl;
607c77a61a7Syz 
60861b2e298Slc 		USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
609c77a61a7Syz 		    "V4L2 ioctl: G_CTRL");
610c77a61a7Syz 		USBVC_COPYIN(ctrl);
611c77a61a7Syz 		if (usbvc_v4l2_get_ctrl(usbvcp, &ctrl) != USB_SUCCESS) {
612c77a61a7Syz 			rv = EINVAL;
613c77a61a7Syz 
614c77a61a7Syz 			break;
615c77a61a7Syz 		}
616c77a61a7Syz 
617c77a61a7Syz 		USBVC_COPYOUT(ctrl);
618c77a61a7Syz 
619c77a61a7Syz 		break;
620c77a61a7Syz 	}
621c77a61a7Syz 	case VIDIOC_S_CTRL:
622c77a61a7Syz 	{
623c77a61a7Syz 		struct v4l2_control ctrl;
624c77a61a7Syz 
62561b2e298Slc 		USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
626c77a61a7Syz 		    "V4L2 ioctl: S_CTRL");
627c77a61a7Syz 		USBVC_COPYIN(ctrl);
628c77a61a7Syz 		if (usbvc_v4l2_set_ctrl(usbvcp, &ctrl) != USB_SUCCESS) {
629c77a61a7Syz 			rv = EINVAL;
630c77a61a7Syz 
631c77a61a7Syz 			break;
632c77a61a7Syz 		}
633c77a61a7Syz 
634c77a61a7Syz 		USBVC_COPYOUT(ctrl);
635c77a61a7Syz 
636c77a61a7Syz 		break;
637c77a61a7Syz 	}
63861b2e298Slc 	case VIDIOC_S_PARM:
63961b2e298Slc 	{
64061b2e298Slc 		struct v4l2_streamparm	parm;
64161b2e298Slc 		usbvc_stream_if_t	*strm_if;
64261b2e298Slc 
64361b2e298Slc 		USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
64461b2e298Slc 		    "V4L2 ioctl: VIDIOC_S_PARM");
64561b2e298Slc 		mutex_enter(&usbvcp->usbvc_mutex);
64661b2e298Slc 		strm_if = usbvcp->usbvc_curr_strm;
64761b2e298Slc 
64861b2e298Slc 		/* If data I/O is in progress */
64961b2e298Slc 		if (strm_if->start_polling == 1) {
65061b2e298Slc 			rv = EBUSY;
65161b2e298Slc 			mutex_exit(&usbvcp->usbvc_mutex);
65261b2e298Slc 
65361b2e298Slc 			break;
65461b2e298Slc 		}
65561b2e298Slc 		mutex_exit(&usbvcp->usbvc_mutex);
65661b2e298Slc 
65761b2e298Slc 		USBVC_COPYIN(parm);
65861b2e298Slc 
65961b2e298Slc 		/* Support capture only, so far. */
66061b2e298Slc 		if (parm.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
66161b2e298Slc 			rv = EINVAL;
66261b2e298Slc 
66361b2e298Slc 			break;
66461b2e298Slc 		}
66561b2e298Slc 
66661b2e298Slc 		if (usbvc_v4l2_set_parm(usbvcp, &parm) != USB_SUCCESS) {
66761b2e298Slc 			rv = EINVAL;
66861b2e298Slc 			USB_DPRINTF_L2(PRINT_MASK_IOCTL,
66961b2e298Slc 			    usbvcp->usbvc_log_handle,
67061b2e298Slc 			    "V4L2 ioctl VIDIOC_S_PARM fail");
67161b2e298Slc 		}
67261b2e298Slc 		USBVC_COPYOUT(parm);
67361b2e298Slc 
67461b2e298Slc 		break;
67561b2e298Slc 	}
67661b2e298Slc 	case VIDIOC_G_PARM:
67761b2e298Slc 	{
67861b2e298Slc 		struct v4l2_streamparm	parm;
67961b2e298Slc 
68061b2e298Slc 		USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
68161b2e298Slc 		    "V4L2 ioctl: VIDIOC_G_PARM");
68261b2e298Slc 		USBVC_COPYIN(parm);
68361b2e298Slc 
68461b2e298Slc 		/* Support capture only, so far. */
68561b2e298Slc 		if (parm.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
68661b2e298Slc 			rv = EINVAL;
68761b2e298Slc 
68861b2e298Slc 			break;
68961b2e298Slc 		}
69061b2e298Slc 
69161b2e298Slc 		if ((rv = usbvc_v4l2_get_parm(usbvcp, &parm)) != USB_SUCCESS) {
69261b2e298Slc 
69361b2e298Slc 			break;
69461b2e298Slc 		}
69561b2e298Slc 
69661b2e298Slc 		USBVC_COPYOUT(parm);
69761b2e298Slc 
69861b2e298Slc 		break;
69961b2e298Slc 	}
700c77a61a7Syz 	/* These ioctls are for analog video standards. */
701c77a61a7Syz 	case VIDIOC_G_STD:
702c77a61a7Syz 	case VIDIOC_S_STD:
703c77a61a7Syz 	case VIDIOC_ENUMSTD:
704c77a61a7Syz 	case VIDIOC_QUERYSTD:
705c77a61a7Syz 		rv = EINVAL;
706c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
707c77a61a7Syz 		    "usbvc_v4l2_ioctl: not a supported cmd, cmd=%x", cmd);
708c77a61a7Syz 
709c77a61a7Syz 		break;
710c77a61a7Syz 	default:
711c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
712c77a61a7Syz 		    "usbvc_v4l2_ioctl: not a valid cmd value, cmd=%x", cmd);
713c77a61a7Syz 		rv = ENOTTY;
714c77a61a7Syz 	}
715c77a61a7Syz 
71661b2e298Slc 	USB_DPRINTF_L3(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
717c77a61a7Syz 	    "usbvc_v4l2_ioctl: exit, rv=%d", rv);
718c77a61a7Syz 
719c77a61a7Syz 	return (rv);
720c77a61a7Syz 
721c77a61a7Syz }
722c77a61a7Syz 
723c77a61a7Syz 
724c77a61a7Syz /*
725c77a61a7Syz  * Convert GUID in uncompressed format descriptor to the pixelformat element
726c77a61a7Syz  * in struct v4l2_pix_format
727c77a61a7Syz  */
728c77a61a7Syz uint32_t
usbvc_v4l2_guid2fcc(uint8_t * guid)729c77a61a7Syz usbvc_v4l2_guid2fcc(uint8_t *guid)
730c77a61a7Syz {
731c77a61a7Syz 	uint32_t ret;
732c77a61a7Syz 
733c77a61a7Syz 	uint8_t y[16] = USBVC_FORMAT_GUID_YUY2;
734c77a61a7Syz 	uint8_t n[16] = USBVC_FORMAT_GUID_NV12;
735c77a61a7Syz 	if (!memcmp((void *)guid, (void *) &y[0], 16)) {
736c77a61a7Syz 		ret = V4L2_PIX_FMT_YUYV;
737c77a61a7Syz 
738c77a61a7Syz 		return (ret);
739c77a61a7Syz 	}
740c77a61a7Syz 	if (!memcmp((void *)guid, (void *) &n, 16)) {
741c77a61a7Syz 		ret = V4L2_PIX_FMT_NV12;
742c77a61a7Syz 
743c77a61a7Syz 		return (ret);
744c77a61a7Syz 	}
745c77a61a7Syz 
746c77a61a7Syz 	return (0);
747c77a61a7Syz }
748c77a61a7Syz 
749c77a61a7Syz 
750c77a61a7Syz /*
751c77a61a7Syz  * Find a frame which has the closest image size as the input args
752c77a61a7Syz  * (width, height)
753c77a61a7Syz  */
754c77a61a7Syz static usbvc_frames_t *
usbvc_match_image_size(uint32_t width,uint32_t height,usbvc_format_group_t * fmtgrp)755c77a61a7Syz usbvc_match_image_size(uint32_t width, uint32_t height,
756c77a61a7Syz     usbvc_format_group_t *fmtgrp)
757c77a61a7Syz {
758c77a61a7Syz 	uint32_t w, h, diff, sz, i;
759c77a61a7Syz 	usbvc_frames_t *frame = NULL;
760c77a61a7Syz 	usbvc_frame_descr_t *descr;
761c77a61a7Syz 
762c77a61a7Syz 	diff = 0xffffffff;
763c77a61a7Syz 
764c77a61a7Syz 	for (i = 0; i < fmtgrp->frame_cnt; i++) {
765c77a61a7Syz 
766c77a61a7Syz 		descr = fmtgrp->frames[i].descr;
767c77a61a7Syz 		if (descr == NULL) {
768c77a61a7Syz 
769c77a61a7Syz 			continue;
770c77a61a7Syz 		}
771c77a61a7Syz 		LE_TO_UINT16(descr->wWidth, 0, w);
772c77a61a7Syz 		LE_TO_UINT16(descr->wHeight, 0, h);
773c77a61a7Syz 
774c77a61a7Syz 		sz = min(w, width) * min(h, height);
775c77a61a7Syz 		sz = (w * h + width * height - sz * 2);
776c77a61a7Syz 		if (sz < diff) {
777c77a61a7Syz 			frame = &fmtgrp->frames[i];
778c77a61a7Syz 			diff = sz;
779c77a61a7Syz 		}
780c77a61a7Syz 
781c77a61a7Syz 		if (diff == 0) {
782c77a61a7Syz 
783c77a61a7Syz 			return (frame);
784c77a61a7Syz 		}
785c77a61a7Syz 	}
786c77a61a7Syz 
787c77a61a7Syz 	return (frame);
788c77a61a7Syz }
789c77a61a7Syz 
790c77a61a7Syz 
791c77a61a7Syz /* Implement ioctl VIDIOC_S_FMT, set a video format */
792c77a61a7Syz static int
usbvc_v4l2_set_format(usbvc_state_t * usbvcp,struct v4l2_format * format)793c77a61a7Syz usbvc_v4l2_set_format(usbvc_state_t *usbvcp, struct v4l2_format *format)
794c77a61a7Syz {
795c77a61a7Syz 	usbvc_vs_probe_commit_t	ctrl, ctrl_max, ctrl_min, ctrl_curr;
796c77a61a7Syz 	usbvc_stream_if_t	*strm_if;
797c77a61a7Syz 	usbvc_format_group_t	*fmtgrp;
798c77a61a7Syz 	usbvc_frames_t		*frame;
799c77a61a7Syz 	uint32_t		w, h, interval, bandwidth;
800c77a61a7Syz 	uint8_t			type, i;
801c77a61a7Syz 
802c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
803c77a61a7Syz 
804c77a61a7Syz 	/*
805c77a61a7Syz 	 * Get the first stream interface. Todo: deal with multi stream
806c77a61a7Syz 	 * interfaces.
807c77a61a7Syz 	 */
808c77a61a7Syz 	strm_if = usbvcp->usbvc_curr_strm;
809c77a61a7Syz 
810c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
811c77a61a7Syz 	    "usbvc_v4l2_set_format: strm_if->fmtgrp_cnt=%d",
812c77a61a7Syz 	    strm_if->fmtgrp_cnt);
813c77a61a7Syz 
814c77a61a7Syz 	/* Find the proper format group according to compress type and guid */
815c77a61a7Syz 	for (i = 0; i < strm_if->fmtgrp_cnt; i++) {
816c77a61a7Syz 		fmtgrp = &strm_if->format_group[i];
817c77a61a7Syz 
818c77a61a7Syz 		/*
819c77a61a7Syz 		 * If v4l2_pixelformat is NULL, then that means there is not
820c77a61a7Syz 		 * a parsed format in format_group[i].
821c77a61a7Syz 		 */
822c77a61a7Syz 		if (!fmtgrp->v4l2_pixelformat || fmtgrp->frame_cnt == 0) {
823c77a61a7Syz 			USB_DPRINTF_L3(PRINT_MASK_DEVCTRL,
824c77a61a7Syz 			    usbvcp->usbvc_log_handle,
825c77a61a7Syz 			    "usbvc_set_default_stream_fmt: no frame, fail");
826c77a61a7Syz 
827c77a61a7Syz 			continue;
828c77a61a7Syz 		}
829c77a61a7Syz 		type = fmtgrp->format->bDescriptorSubType;
830c77a61a7Syz 		USB_DPRINTF_L3(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
831c77a61a7Syz 		    "usbvc_v4l2_set_format: type =%x, i =%d", type, i);
832c77a61a7Syz 
833c77a61a7Syz 		if ((type == VS_FORMAT_MJPEG) ||
834c77a61a7Syz 		    (type == VS_FORMAT_UNCOMPRESSED)) {
835c77a61a7Syz 			if (format->fmt.pix.pixelformat ==
836c77a61a7Syz 			    fmtgrp->v4l2_pixelformat) {
837c77a61a7Syz 
838c77a61a7Syz 				break;
839c77a61a7Syz 			}
840c77a61a7Syz 		}
841c77a61a7Syz 	}
842c77a61a7Syz 
843c77a61a7Syz 	if (i >= strm_if->fmtgrp_cnt) {
844c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
845c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
846c77a61a7Syz 		    "usbvc_v4l2_set_format: can't find a proper format, "
847c77a61a7Syz 		    "pixelformat=%x", format->fmt.pix.pixelformat);
848c77a61a7Syz 
849c77a61a7Syz 		return (USB_FAILURE);
850c77a61a7Syz 	}
851c77a61a7Syz 
852c77a61a7Syz 	fmtgrp = &strm_if->format_group[i];
853c77a61a7Syz 
854c77a61a7Syz 	frame = usbvc_match_image_size(format->fmt.pix.width,
855c77a61a7Syz 	    format->fmt.pix.height, fmtgrp);
856c77a61a7Syz 
857c77a61a7Syz 	if (frame == NULL) {
858c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
859c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
860c77a61a7Syz 		    "usbvc_v4l2_set_format: can't find a proper frame, rw=%d, "
861c77a61a7Syz 		    "rh=%d", format->fmt.pix.width, format->fmt.pix.height);
862c77a61a7Syz 
863c77a61a7Syz 		return (USB_FAILURE);
864c77a61a7Syz 	}
865c77a61a7Syz 
866c77a61a7Syz 	/* frame interval */
867c77a61a7Syz 	LE_TO_UINT32(frame->descr->dwDefaultFrameInterval, 0, interval);
868c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
869c77a61a7Syz 	    "usbvc_v4l2_set_format: Default Frame Interval=%x", interval);
870c77a61a7Syz 
871c77a61a7Syz 	/*
872c77a61a7Syz 	 * Begin negotiate formats.
873c77a61a7Syz 	 */
874c77a61a7Syz 	bzero((void *)&ctrl, sizeof (usbvc_vs_probe_commit_t));
875c77a61a7Syz 
876c77a61a7Syz 	/* dwFrameInterval is fixed */
877c77a61a7Syz 	ctrl.bmHint[0] = 1;
878c77a61a7Syz 
879c77a61a7Syz 	ctrl.bFormatIndex = fmtgrp->format->bFormatIndex;
880c77a61a7Syz 	ctrl.bFrameIndex = frame->descr->bFrameIndex;
881c77a61a7Syz 	UINT32_TO_LE(interval, 0, ctrl.dwFrameInterval);
882c77a61a7Syz 
883c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
884c77a61a7Syz 
885c77a61a7Syz 	/* Probe, just a test before the real try */
886c77a61a7Syz 	if (usbvc_vs_set_probe_commit(usbvcp, strm_if, &ctrl, VS_PROBE_CONTROL)
887c77a61a7Syz 	    != USB_SUCCESS) {
888c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
889c77a61a7Syz 		    "usbvc_v4l2_set_format: set probe failed");
890c77a61a7Syz 
891c77a61a7Syz 		return (USB_FAILURE);
892c77a61a7Syz 	}
893c77a61a7Syz 
894c77a61a7Syz 	/* Get max values */
895c77a61a7Syz 	if (usbvc_vs_get_probe(usbvcp, strm_if, &ctrl_max, GET_MAX) !=
896c77a61a7Syz 	    USB_SUCCESS) {
897c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
898c77a61a7Syz 		    "usbvc_v4l2_set_format: get probe MAX failed");
899c77a61a7Syz 
900c77a61a7Syz 		return (USB_FAILURE);
901c77a61a7Syz 	}
902c77a61a7Syz 
903c77a61a7Syz 	/* Use the best quality first */
904c77a61a7Syz 	bcopy(&ctrl_max.wCompQuality, &ctrl.wCompQuality, 2);
905c77a61a7Syz 
906c77a61a7Syz 	/*
907c77a61a7Syz 	 * By now, we've get some parametres of ctrl req, next try to set ctrl.
908c77a61a7Syz 	 */
909c77a61a7Syz 	for (i = 0; i < 2; i++) {
910c77a61a7Syz 
911c77a61a7Syz 		/* Probe */
912c77a61a7Syz 		if (usbvc_vs_set_probe_commit(usbvcp, strm_if, &ctrl,
913c77a61a7Syz 		    VS_PROBE_CONTROL) != USB_SUCCESS) {
914c77a61a7Syz 
915c77a61a7Syz 			return (USB_FAILURE);
916c77a61a7Syz 		}
917c77a61a7Syz 
918c77a61a7Syz 		/* Get current value after probe */
919c77a61a7Syz 		if (usbvc_vs_get_probe(usbvcp, strm_if, &ctrl_curr, GET_CUR)
920c77a61a7Syz 		    != USB_SUCCESS) {
921c77a61a7Syz 
922c77a61a7Syz 			return (USB_FAILURE);
923c77a61a7Syz 		}
924c77a61a7Syz 		LE_TO_UINT32(ctrl_curr.dwMaxPayloadTransferSize, 0, bandwidth);
925c77a61a7Syz 		USB_DPRINTF_L3(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
926c77a61a7Syz 		    "usbvc_v4l2_set_format: bandwidth=%x", bandwidth);
927c77a61a7Syz 
928c77a61a7Syz 		/*
929c77a61a7Syz 		 * If the bandwidth does not exceed the max value of all the
930c77a61a7Syz 		 * alternatives in this interface, we done.
931c77a61a7Syz 		 */
932c77a61a7Syz 		if (bandwidth <= strm_if->max_isoc_payload) {
933c77a61a7Syz 
934c77a61a7Syz 			break;
935c77a61a7Syz 		}
936c77a61a7Syz 		if (i >= 1) {
937c77a61a7Syz 
938c77a61a7Syz 			return (USB_FAILURE);
939c77a61a7Syz 		}
940c77a61a7Syz 
941c77a61a7Syz 		/* Get minimum values since the bandwidth is not enough */
942c77a61a7Syz 		if (usbvc_vs_get_probe(usbvcp, strm_if, &ctrl_min, GET_MIN) !=
943c77a61a7Syz 		    USB_SUCCESS) {
944c77a61a7Syz 			USB_DPRINTF_L2(PRINT_MASK_IOCTL,
945c77a61a7Syz 			    usbvcp->usbvc_log_handle,
946c77a61a7Syz 			    "usbvc_v4l2_set_format: get probe MIN failed");
947c77a61a7Syz 
948c77a61a7Syz 			return (USB_FAILURE);
949c77a61a7Syz 		}
950c77a61a7Syz 
951c77a61a7Syz 		/* To keep simple, just use some minimum values to try again */
952c77a61a7Syz 		bcopy(&ctrl_min.wKeyFrameRate, &ctrl_curr.wKeyFrameRate, 2);
953c77a61a7Syz 		bcopy(&ctrl_min.wPFrameRate, &ctrl_curr.wPFrameRate, 2);
954c77a61a7Syz 		bcopy(&ctrl_min.wCompWindowSize, &ctrl_curr.wCompWindowSize, 2);
955c77a61a7Syz 		bcopy(&ctrl_max.wCompQuality, &ctrl_curr.wCompQuality, 2);
956c77a61a7Syz 
957c77a61a7Syz 		bcopy(&ctrl_curr, &ctrl,
958c77a61a7Syz 		    sizeof (usbvc_vs_probe_commit_t));
959c77a61a7Syz 	}
960c77a61a7Syz 
961c77a61a7Syz 	bcopy(&ctrl_curr, &ctrl, sizeof (usbvc_vs_probe_commit_t));
962c77a61a7Syz 
963c77a61a7Syz 	/* commit the values we negotiated above */
964c77a61a7Syz 	if (usbvc_vs_set_probe_commit(usbvcp, strm_if, &ctrl,
965c77a61a7Syz 	    VS_COMMIT_CONTROL) != USB_SUCCESS) {
966c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
967c77a61a7Syz 		    "usbvc_v4l2_set_format: set probe failed, i=%d", i);
968c77a61a7Syz 
969c77a61a7Syz 		return (USB_FAILURE);
970c77a61a7Syz 	}
971c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
972c77a61a7Syz 
973c77a61a7Syz 	/*
974c77a61a7Syz 	 * It's good to check index here before use it. bFormatIndex is based
975c77a61a7Syz 	 * on 1, and format_group[i] is based on 0, so minus 1
976c77a61a7Syz 	 */
977c77a61a7Syz 	i = ctrl.bFormatIndex - 1;
978c77a61a7Syz 	if (i < strm_if->fmtgrp_cnt) {
979c77a61a7Syz 		strm_if->cur_format_group = &strm_if->format_group[i];
980c77a61a7Syz 	} else {
981c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
982112116d8Sfb 		    "usbvc_v4l2_set_format: format index out of range");
983c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
984c77a61a7Syz 
985c77a61a7Syz 		return (USB_FAILURE);
986c77a61a7Syz 	}
987c77a61a7Syz 
988c77a61a7Syz 	/* bFrameIndex is based on 1, and frames[i] is based on 0, so minus 1 */
989c77a61a7Syz 	i = ctrl.bFrameIndex -1;
990c77a61a7Syz 	if (i < strm_if->cur_format_group->frame_cnt) {
991c77a61a7Syz 		strm_if->cur_format_group->cur_frame =
992c77a61a7Syz 		    &strm_if->cur_format_group->frames[i];
993c77a61a7Syz 	} else {
994c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
995c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
996c77a61a7Syz 		    "usbvc_v4l2_set_format: frame index out of range");
997c77a61a7Syz 
998c77a61a7Syz 		return (USB_FAILURE);
999c77a61a7Syz 	}
1000c77a61a7Syz 
1001c77a61a7Syz 	/*
1002c77a61a7Syz 	 * by now, the video format is set successfully. record the current
1003c77a61a7Syz 	 * setting to strm_if->ctrl_pc
1004c77a61a7Syz 	 */
1005c77a61a7Syz 	bcopy(&ctrl_curr, &strm_if->ctrl_pc, sizeof (usbvc_vs_probe_commit_t));
1006c77a61a7Syz 
1007c77a61a7Syz 	format->fmt.pix.colorspace = fmtgrp->v4l2_color;
1008c77a61a7Syz 	format->fmt.pix.field = V4L2_FIELD_NONE;
1009c77a61a7Syz 	format->fmt.pix.priv = 0;
1010c77a61a7Syz 
1011c77a61a7Syz 	LE_TO_UINT16(frame->descr->wWidth, 0, w);
1012c77a61a7Syz 	LE_TO_UINT16(frame->descr->wHeight, 0, h);
1013c77a61a7Syz 	format->fmt.pix.width = w;
1014c77a61a7Syz 	format->fmt.pix.height = h;
1015c77a61a7Syz 	format->fmt.pix.bytesperline = fmtgrp->v4l2_bpp * w;
1016c77a61a7Syz 	LE_TO_UINT32(strm_if->ctrl_pc.dwMaxVideoFrameSize, 0,
1017c77a61a7Syz 	    format->fmt.pix.sizeimage);
1018c77a61a7Syz 
1019c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
1020c77a61a7Syz 
1021c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
1022c77a61a7Syz 	    "usbvc_v4l2_set_format: dwMaxVideoFrameSize=%x, w=%x, h=%x",
1023c77a61a7Syz 	    format->fmt.pix.sizeimage, w, h);
1024c77a61a7Syz 
1025c77a61a7Syz 	return (USB_SUCCESS);
1026c77a61a7Syz }
1027c77a61a7Syz 
1028c77a61a7Syz 
1029c77a61a7Syz /* Implement ioctl VIDIOC_G_FMT, get the current video format */
1030c77a61a7Syz static int
usbvc_v4l2_get_format(usbvc_state_t * usbvcp,struct v4l2_format * format)1031c77a61a7Syz usbvc_v4l2_get_format(usbvc_state_t *usbvcp, struct v4l2_format *format)
1032c77a61a7Syz {
1033c77a61a7Syz 	usbvc_stream_if_t	*strm_if;
1034c77a61a7Syz 	usbvc_format_group_t	*fmtgrp;
1035c77a61a7Syz 	uint16_t		w, h;
1036c77a61a7Syz 
1037c77a61a7Syz 	if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
1038c77a61a7Syz 
1039c77a61a7Syz 		return (EINVAL);
1040c77a61a7Syz 	}
1041c77a61a7Syz 	mutex_enter(&usbvcp->usbvc_mutex);
1042c77a61a7Syz 
1043c77a61a7Syz 	/* get the current interface. */
1044c77a61a7Syz 	strm_if = usbvcp->usbvc_curr_strm;
1045c77a61a7Syz 	fmtgrp = strm_if->cur_format_group;
1046c77a61a7Syz 
1047c77a61a7Syz 	if (!fmtgrp || !fmtgrp->cur_frame) {
1048c77a61a7Syz 		mutex_exit(&usbvcp->usbvc_mutex);
1049c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
1050c77a61a7Syz 		    "usbvc_v4l2_get_format: fail, no current format or frame,"
1051112116d8Sfb 		    "fmtgrp=%p", (void *)fmtgrp);
1052c77a61a7Syz 
1053c77a61a7Syz 		return (EINVAL);
1054c77a61a7Syz 	}
1055c77a61a7Syz 	format->fmt.pix.colorspace = fmtgrp->v4l2_color;
1056c77a61a7Syz 	format->fmt.pix.priv = 0;
1057c77a61a7Syz 	format->fmt.pix.pixelformat = fmtgrp->v4l2_pixelformat;
1058c77a61a7Syz 
1059c77a61a7Syz 	LE_TO_UINT16(fmtgrp->cur_frame->descr->wWidth, 0, w);
1060c77a61a7Syz 	LE_TO_UINT16(fmtgrp->cur_frame->descr->wHeight, 0, h);
1061c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
1062c77a61a7Syz 	    "v4l2 ioctl get format ");
1063c77a61a7Syz 	format->fmt.pix.width = w;
1064c77a61a7Syz 	format->fmt.pix.height = h;
1065c77a61a7Syz 
1066c77a61a7Syz 	format->fmt.pix.field = V4L2_FIELD_NONE;
1067c77a61a7Syz 	format->fmt.pix.bytesperline = fmtgrp->v4l2_bpp * w;
1068c77a61a7Syz 
1069c77a61a7Syz 	LE_TO_UINT32(strm_if->ctrl_pc.dwMaxVideoFrameSize, 0,
1070c77a61a7Syz 	    format->fmt.pix.sizeimage);
1071c77a61a7Syz 
1072c77a61a7Syz 	mutex_exit(&usbvcp->usbvc_mutex);
1073c77a61a7Syz 
1074c77a61a7Syz 	return (0);
1075c77a61a7Syz }
1076c77a61a7Syz 
1077c77a61a7Syz 
1078c77a61a7Syz /*
1079c77a61a7Syz  * Convert color space descriptor's bColorPrimaries to the colorspace element
1080c77a61a7Syz  * in struct v4l2_pix_format
1081c77a61a7Syz  */
1082c77a61a7Syz uint8_t
usbvc_v4l2_colorspace(uint8_t color_prim)1083c77a61a7Syz usbvc_v4l2_colorspace(uint8_t color_prim)
1084c77a61a7Syz {
1085c77a61a7Syz 
1086c77a61a7Syz 	if (color_prim < NELEM(color_primaries)) {
1087c77a61a7Syz 
1088c77a61a7Syz 		return (color_primaries[color_prim]);
1089c77a61a7Syz 	}
1090c77a61a7Syz 
1091c77a61a7Syz 	return (0);
1092c77a61a7Syz }
1093c77a61a7Syz 
1094c77a61a7Syz 
1095c77a61a7Syz /* Implement ioctl VIDIOC_QUERYBUF, get the buf status */
1096c77a61a7Syz static void
usbvc_v4l2_query_buf(usbvc_state_t * usbvcp,usbvc_buf_t * usbvc_buf,struct v4l2_buffer * v4l2_buf)1097c77a61a7Syz usbvc_v4l2_query_buf(usbvc_state_t *usbvcp, usbvc_buf_t *usbvc_buf,
1098c77a61a7Syz 	struct v4l2_buffer *v4l2_buf)
1099c77a61a7Syz {
1100c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
1101c77a61a7Syz 
1102c77a61a7Syz 	bcopy(&(usbvc_buf->v4l2_buf), v4l2_buf, sizeof (struct v4l2_buffer));
1103c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
1104c77a61a7Syz 	    "usbvc_v4l2_query_buf: uv_buf_len=%d, len=%d",
1105c77a61a7Syz 	    usbvc_buf->v4l2_buf.length, v4l2_buf->length);
1106c77a61a7Syz 
1107c77a61a7Syz 	if (usbvc_buf->status >= USBVC_BUF_MAPPED) {
1108c77a61a7Syz 		v4l2_buf->flags |= V4L2_BUF_FLAG_MAPPED;
1109c77a61a7Syz 	}
1110c77a61a7Syz 
1111c77a61a7Syz 	switch (usbvc_buf->status) {
1112c77a61a7Syz 	case USBVC_BUF_DONE:
1113c77a61a7Syz 	case USBVC_BUF_ERR:
1114c77a61a7Syz 		v4l2_buf->flags |= V4L2_BUF_FLAG_DONE;
1115c77a61a7Syz 
1116c77a61a7Syz 		break;
1117c77a61a7Syz 	case USBVC_BUF_EMPTY:
1118c77a61a7Syz 		v4l2_buf->flags |= V4L2_BUF_FLAG_QUEUED;
1119c77a61a7Syz 
1120c77a61a7Syz 		break;
1121c77a61a7Syz 	case USBVC_BUF_INIT:
1122c77a61a7Syz 	default:
1123c77a61a7Syz 
1124c77a61a7Syz 		break;
1125c77a61a7Syz 	}
1126c77a61a7Syz }
1127c77a61a7Syz 
1128c77a61a7Syz 
1129c77a61a7Syz /* Implement ioctl VIDIOC_QBUF, queue a empty buf to the free list */
1130c77a61a7Syz static int
usbvc_v4l2_enqueue_buf(usbvc_state_t * usbvcp,usbvc_buf_t * usbvc_buf,struct v4l2_buffer * buf)1131c77a61a7Syz usbvc_v4l2_enqueue_buf(usbvc_state_t *usbvcp, usbvc_buf_t *usbvc_buf,
1132c77a61a7Syz 	struct v4l2_buffer *buf)
1133c77a61a7Syz {
1134c77a61a7Syz 	usbvc_buf_t	*donebuf;
1135c77a61a7Syz 	boolean_t	queued = B_FALSE;
1136c77a61a7Syz 	usbvc_buf_grp_t	*bufgrp;
1137c77a61a7Syz 
1138c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
1139c77a61a7Syz 
1140c77a61a7Syz 	bufgrp = &usbvcp->usbvc_curr_strm->buf_map;
1141c77a61a7Syz 
1142c77a61a7Syz 	if (usbvc_buf == bufgrp->buf_filling) {
1143c77a61a7Syz 		USB_DPRINTF_L3(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
1144c77a61a7Syz 		    "enqueue_buffer(%d) , want to queue buf_filling, "
1145c77a61a7Syz 		    "just return success", buf->index);
1146c77a61a7Syz 
1147c77a61a7Syz 		return (0);
1148c77a61a7Syz 	}
1149c77a61a7Syz 
1150c77a61a7Syz 	if (!list_is_empty(&bufgrp->uv_buf_done)) {
1151c77a61a7Syz 		donebuf = (usbvc_buf_t *)list_head(&bufgrp->uv_buf_done);
1152c77a61a7Syz 		while (donebuf) {
1153c77a61a7Syz 
1154c77a61a7Syz 			if (donebuf == &(bufgrp->buf_head[buf->index])) {
1155c77a61a7Syz 				queued = B_TRUE;
1156c77a61a7Syz 
1157c77a61a7Syz 				break;
1158c77a61a7Syz 			}
1159c77a61a7Syz 			donebuf = (usbvc_buf_t *)list_next(&bufgrp->uv_buf_done,
1160c77a61a7Syz 			    donebuf);
1161c77a61a7Syz 		}
1162c77a61a7Syz 	}
1163c77a61a7Syz 	if (queued) {
1164c77a61a7Syz 		USB_DPRINTF_L3(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
1165c77a61a7Syz 		    "enqueue_buffer(%d), still in done list, don't insert to"
1166c77a61a7Syz 		    " free list", buf->index);
1167c77a61a7Syz 
1168c77a61a7Syz 		return (0);
1169c77a61a7Syz 	}
1170c77a61a7Syz 
1171c77a61a7Syz 	if (usbvc_buf->status == USBVC_BUF_EMPTY) {
1172c77a61a7Syz 		USB_DPRINTF_L3(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
1173c77a61a7Syz 		    "enqueue buffer(%d), already queued.", buf->index);
1174c77a61a7Syz 
1175c77a61a7Syz 		return (0);
1176c77a61a7Syz 
1177c77a61a7Syz 	}
1178c77a61a7Syz 	if (usbvc_buf->status < USBVC_BUF_MAPPED) {
1179c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
1180c77a61a7Syz 		    "enqueue buffer(%d), state error, not mapped.", buf->index);
1181c77a61a7Syz 
1182c77a61a7Syz 		return (EINVAL);
1183c77a61a7Syz 	}
1184c77a61a7Syz 
1185c77a61a7Syz 	/*
1186c77a61a7Syz 	 * The buf is put to the buf free list when allocated, so, if the buf
1187c77a61a7Syz 	 * is the first time to enqueue, just change the state to empty is
1188c77a61a7Syz 	 * enough.
1189c77a61a7Syz 	 */
1190c77a61a7Syz 	if (usbvc_buf->status == USBVC_BUF_MAPPED) {
1191c77a61a7Syz 		USB_DPRINTF_L3(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
1192c77a61a7Syz 		    "queue_buffer(%d), 1st time queue this buf", buf->index);
1193c77a61a7Syz 
1194c77a61a7Syz 		usbvc_buf->status = USBVC_BUF_EMPTY;
1195c77a61a7Syz 
1196c77a61a7Syz 	} else {
1197c77a61a7Syz 		USB_DPRINTF_L3(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
1198c77a61a7Syz 		    "enqueue_buffer(%d) , USBVC_BUF_EMPTY", buf->index);
1199c77a61a7Syz 
1200c77a61a7Syz 		usbvc_buf->status = USBVC_BUF_EMPTY;
1201c77a61a7Syz 		usbvc_buf->v4l2_buf.bytesused = 0;
1202c77a61a7Syz 		list_insert_tail(&bufgrp->uv_buf_free, usbvc_buf);
1203c77a61a7Syz 	}
1204c77a61a7Syz 	buf->flags &= ~V4L2_BUF_FLAG_DONE;
1205c77a61a7Syz 	buf->flags |= V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED;
1206c77a61a7Syz 
1207c77a61a7Syz 	return (0);
1208c77a61a7Syz }
1209c77a61a7Syz 
1210c77a61a7Syz 
1211c77a61a7Syz /* Implement ioctl VIDIOC_DQBUF, pick a buf from done list */
1212c77a61a7Syz static int
usbvc_v4l2_dequeue_buffer(usbvc_state_t * usbvcp,struct v4l2_buffer * buf,int mode)1213c77a61a7Syz usbvc_v4l2_dequeue_buffer(usbvc_state_t *usbvcp, struct v4l2_buffer *buf,
1214c77a61a7Syz 	int mode)
1215c77a61a7Syz {
1216c77a61a7Syz 	usbvc_buf_t *buf_done;
1217c77a61a7Syz 
1218c77a61a7Syz 	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
1219c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
1220c77a61a7Syz 	    "usbvc_v4l2_dequeue_buffer: idx=%x", buf->index);
1221c77a61a7Syz 
1222c77a61a7Syz 	/* v4l2 spec: app just set type and memory field */
1223c77a61a7Syz 	if ((buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
1224112116d8Sfb 	    (buf->memory != V4L2_MEMORY_MMAP)) {
1225c77a61a7Syz 
1226c77a61a7Syz 		return (EINVAL);
1227c77a61a7Syz 	}
1228c77a61a7Syz 	if ((mode & (O_NDELAY|O_NONBLOCK)) &&
1229c77a61a7Syz 	    (list_is_empty(&usbvcp->usbvc_curr_strm->buf_map.uv_buf_done))) {
1230c77a61a7Syz 
1231c77a61a7Syz 		/* non-blocking */
1232c77a61a7Syz 		return (EAGAIN);
1233c77a61a7Syz 	}
1234c77a61a7Syz 
1235c77a61a7Syz 	/* no available buffers, block here */
1236c77a61a7Syz 	while (list_is_empty(&usbvcp->usbvc_curr_strm->buf_map.uv_buf_done)) {
1237c77a61a7Syz 		USB_DPRINTF_L3(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
1238c77a61a7Syz 		    "usbvc_v4l2_dequeue_buffer: wait for done buf");
1239c77a61a7Syz 		if (cv_wait_sig(&usbvcp->usbvc_mapio_cv, &usbvcp->usbvc_mutex)
1240c77a61a7Syz 		    <= 0) {
1241c77a61a7Syz 
1242c77a61a7Syz 			/* no done buf and is signaled */
1243c77a61a7Syz 			return (EINTR);
1244c77a61a7Syz 		}
1245c77a61a7Syz 		if (usbvcp->usbvc_dev_state != USB_DEV_ONLINE) {
1246c77a61a7Syz 
1247c77a61a7Syz 			/* Device is disconnected. */
1248c77a61a7Syz 			return (EINTR);
1249c77a61a7Syz 		}
1250c77a61a7Syz 	}
1251c77a61a7Syz 
1252c77a61a7Syz 	buf_done = list_head(&usbvcp->usbvc_curr_strm->buf_map.uv_buf_done);
1253c77a61a7Syz 
1254c77a61a7Syz 	list_remove(&usbvcp->usbvc_curr_strm->buf_map.uv_buf_done, buf_done);
1255c77a61a7Syz 
1256c77a61a7Syz 	/*
1257c77a61a7Syz 	 * just copy the v4l2_buf structure because app need only the index
1258c77a61a7Syz 	 * value to locate the mapped memory
1259c77a61a7Syz 	 */
1260c77a61a7Syz 	bcopy(&buf_done->v4l2_buf, buf, sizeof (struct v4l2_buffer));
1261c77a61a7Syz 	buf->flags |= V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_MAPPED;
1262c77a61a7Syz 	buf->bytesused = buf_done->filled;
1263c77a61a7Syz 	USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
1264c77a61a7Syz 	    "usbvc_v4l2_dequeue_buffer: bytesused=%d, idx=%x, status=%d",
1265c77a61a7Syz 	    buf->bytesused, buf->index, buf_done->status);
1266c77a61a7Syz 
1267c77a61a7Syz 	return (0);
1268c77a61a7Syz }
1269c77a61a7Syz 
1270c77a61a7Syz 
1271c77a61a7Syz /*
1272c77a61a7Syz  * Check if a ctrl_id is supported by the device, if yes, find the
1273c77a61a7Syz  * corresponding processing unit and fill usbvc_v4l2_ctrl_t
1274c77a61a7Syz  */
1275c77a61a7Syz static int
usbvc_v4l2_match_ctrl(usbvc_state_t * usbvcp,usbvc_v4l2_ctrl_t * ctrl,uint32_t ctrl_id)1276c77a61a7Syz usbvc_v4l2_match_ctrl(usbvc_state_t *usbvcp, usbvc_v4l2_ctrl_t *ctrl,
1277c77a61a7Syz     uint32_t ctrl_id)
1278c77a61a7Syz {
1279c77a61a7Syz 	uint8_t		idx;
1280c77a61a7Syz 	usbvc_units_t	*unit;
1281c77a61a7Syz 	uchar_t		bit;
1282c77a61a7Syz 
1283c77a61a7Syz 	USB_DPRINTF_L3(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
1284c77a61a7Syz 	    "usbvc_v4l2_match_ctrl: ctrl_id=%x", ctrl_id);
1285c77a61a7Syz 	if (ctrl_id >= V4L2_CID_PRIVATE_BASE) {
1286c77a61a7Syz 
1287c77a61a7Syz 		return (USB_FAILURE);
1288c77a61a7Syz 	}
1289c77a61a7Syz 	if (ctrl_id < V4L2_CID_BASE) {
1290c77a61a7Syz 
1291c77a61a7Syz 		return (USB_FAILURE);
1292c77a61a7Syz 	}
1293c77a61a7Syz 
1294c77a61a7Syz 	/* get the idx of ctrl array usbvc_v4l2_ctrl */
1295c77a61a7Syz 	idx = ctrl_id - V4L2_CID_BASE;
1296c77a61a7Syz 	if (ctrl_id == V4L2_CID_GAMMA) {
1297c77a61a7Syz 
1298c77a61a7Syz 		/* The 4th one is for Gamma ctrl */
1299c77a61a7Syz 		bit = usbvc_v4l2_ctrls[4].bit;
1300c77a61a7Syz 	} else if ((ctrl_id >= V4L2_CID_BRIGHTNESS) &&
1301c77a61a7Syz 	    (ctrl_id <= V4L2_CID_HUE)) {
1302c77a61a7Syz 
1303c77a61a7Syz 		/* The idxth one is for this ctrl */
1304c77a61a7Syz 		bit = usbvc_v4l2_ctrls[idx].bit;
1305c77a61a7Syz 	} else {
1306c77a61a7Syz 
1307c77a61a7Syz 		return (USB_FAILURE);
1308c77a61a7Syz 	}
1309c77a61a7Syz 	unit = (usbvc_units_t *)list_head(&usbvcp->usbvc_unit_list);
1310c77a61a7Syz 
1311c77a61a7Syz 	/*
1312c77a61a7Syz 	 * Check if there is a processing unit supportting this ctrl.
1313c77a61a7Syz 	 * Todo: check if the ctrl and the unit is really for the right
1314c77a61a7Syz 	 * stream interface in case of multi stream interfaces.
1315c77a61a7Syz 	 */
1316c77a61a7Syz 	while (unit != NULL) {
1317c77a61a7Syz 
1318c77a61a7Syz 		if (unit->descr->bDescriptorSubType == VC_PROCESSING_UNIT) {
1319c77a61a7Syz 
1320c77a61a7Syz 			if (bit >=
1321c77a61a7Syz 			    (unit->descr->unit.processing.bControlSize * 8)) {
1322c77a61a7Syz 
1323c77a61a7Syz 				/*
1324c77a61a7Syz 				 * If this unit's bmControls size is smaller
1325c77a61a7Syz 				 * than bit, then next
1326c77a61a7Syz 				 */
1327c77a61a7Syz 				unit = (usbvc_units_t *)
1328c77a61a7Syz 				    list_next(&usbvcp->usbvc_unit_list, unit);
1329c77a61a7Syz 
1330c77a61a7Syz 				continue;
1331c77a61a7Syz 			} else {
1332c77a61a7Syz 
1333c77a61a7Syz 				/*
1334c77a61a7Syz 				 * The first two bytes of bmControls are
1335c77a61a7Syz 				 * for ctrls
1336c77a61a7Syz 				 */
1337c77a61a7Syz 				if ((bit < 8) &&
1338c77a61a7Syz 				    unit->bmControls[0] & (0x1 << bit)) {
1339c77a61a7Syz 
1340c77a61a7Syz 					break;
1341c77a61a7Syz 				}
1342c77a61a7Syz 				if ((bit >= 8 && bit < 16) &&
1343c77a61a7Syz 				    unit->bmControls[1] & (0x1 << bit)) {
1344c77a61a7Syz 
1345c77a61a7Syz 					break;
1346c77a61a7Syz 				}
1347c77a61a7Syz 			}
1348c77a61a7Syz 		}
1349c77a61a7Syz 		unit = (usbvc_units_t *)list_next(&usbvcp->usbvc_unit_list,
1350c77a61a7Syz 		    unit);
1351c77a61a7Syz 	}
1352c77a61a7Syz 	if (unit == NULL) {
1353c77a61a7Syz 
1354c77a61a7Syz 		return (USB_FAILURE);
1355c77a61a7Syz 	}
1356c77a61a7Syz 	ctrl->entity_id = unit->descr->bUnitID;
1357c77a61a7Syz 	if (ctrl_id == V4L2_CID_GAMMA) {
1358c77a61a7Syz 		ctrl->ctrl_map = &usbvc_v4l2_ctrls[4];
1359c77a61a7Syz 	} else {
1360c77a61a7Syz 		ctrl->ctrl_map = &usbvc_v4l2_ctrls[idx];
1361c77a61a7Syz 	}
1362c77a61a7Syz 
1363c77a61a7Syz 	return (USB_SUCCESS);
1364c77a61a7Syz }
1365c77a61a7Syz 
1366c77a61a7Syz 
1367c77a61a7Syz /*
1368c77a61a7Syz  * Implement ioctl VIDIOC_QUERYCTRL, query the ctrl types that the device
1369c77a61a7Syz  * supports
1370c77a61a7Syz  */
1371c77a61a7Syz static int
usbvc_v4l2_query_ctrl(usbvc_state_t * usbvcp,struct v4l2_queryctrl * queryctrl)1372c77a61a7Syz usbvc_v4l2_query_ctrl(usbvc_state_t *usbvcp, struct v4l2_queryctrl *queryctrl)
1373c77a61a7Syz {
1374c77a61a7Syz 	usbvc_v4l2_ctrl_t	ctrl;
1375c77a61a7Syz 	mblk_t			*data;
1376c77a61a7Syz 	char			req[16];
1377c77a61a7Syz 
1378c77a61a7Syz 	if (usbvc_v4l2_match_ctrl(usbvcp, &ctrl, queryctrl->id) !=
1379c77a61a7Syz 	    USB_SUCCESS) {
1380c77a61a7Syz 
1381c77a61a7Syz 		return (USB_FAILURE);
1382c77a61a7Syz 	}
1383c77a61a7Syz 	if ((data = allocb(ctrl.ctrl_map->len, BPRI_LO)) == NULL) {
1384c77a61a7Syz 
1385c77a61a7Syz 		return (USB_FAILURE);
1386c77a61a7Syz 	}
1387c77a61a7Syz 
1388c77a61a7Syz 	if (usbvc_vc_get_ctrl(usbvcp, GET_MIN, ctrl.entity_id,
1389c77a61a7Syz 	    ctrl.ctrl_map->selector, ctrl.ctrl_map->len, data) !=
1390c77a61a7Syz 	    USB_SUCCESS) {
1391c77a61a7Syz 		(void) strncpy(&req[0], "GET_MIN", sizeof (req));
1392c77a61a7Syz 
1393c77a61a7Syz 		goto fail;
1394c77a61a7Syz 	}
1395c77a61a7Syz 	LE_TO_UINT16(data->b_rptr, 0, queryctrl->minimum);
1396c77a61a7Syz 	if (usbvc_vc_get_ctrl(usbvcp, GET_MAX, ctrl.entity_id,
1397c77a61a7Syz 	    ctrl.ctrl_map->selector, ctrl.ctrl_map->len, data) != USB_SUCCESS) {
1398112116d8Sfb 		(void) strncpy(&req[0], "GET_MAX", sizeof (req));
1399c77a61a7Syz 
1400c77a61a7Syz 		goto fail;
1401c77a61a7Syz 	}
1402c77a61a7Syz 	LE_TO_UINT16(data->b_rptr, 0, queryctrl->maximum);
1403c77a61a7Syz 
1404c77a61a7Syz 	if (usbvc_vc_get_ctrl(usbvcp, GET_RES, ctrl.entity_id,
1405c77a61a7Syz 	    ctrl.ctrl_map->selector, ctrl.ctrl_map->len, data) != USB_SUCCESS) {
1406112116d8Sfb 		(void) strncpy(&req[0], "GET_RES", sizeof (req));
1407c77a61a7Syz 
1408c77a61a7Syz 		goto fail;
1409c77a61a7Syz 	}
1410c77a61a7Syz 	LE_TO_UINT16(data->b_rptr, 0, queryctrl->step);
1411c77a61a7Syz 
1412c77a61a7Syz 	if (usbvc_vc_get_ctrl(usbvcp, GET_DEF, ctrl.entity_id,
1413c77a61a7Syz 	    ctrl.ctrl_map->selector, ctrl.ctrl_map->len, data) != USB_SUCCESS) {
1414112116d8Sfb 		(void) strncpy(&req[0], "GET_DEF", sizeof (req));
1415c77a61a7Syz 
1416c77a61a7Syz 		goto fail;
1417c77a61a7Syz 	}
1418c77a61a7Syz 	LE_TO_UINT16(data->b_rptr, 0, queryctrl->default_value);
1419c77a61a7Syz 
1420c77a61a7Syz 	(void) strncpy(queryctrl->name, ctrl.ctrl_map->name,
1421c77a61a7Syz 	    sizeof (queryctrl->name));
1422c77a61a7Syz 	queryctrl->type = ctrl.ctrl_map->type;
1423c77a61a7Syz 	queryctrl->flags = 0;
1424c77a61a7Syz 
1425c77a61a7Syz 	if (data) {
1426c77a61a7Syz 		freemsg(data);
1427c77a61a7Syz 	}
1428c77a61a7Syz 
1429c77a61a7Syz 	return (USB_SUCCESS);
1430c77a61a7Syz 
1431c77a61a7Syz fail:
1432c77a61a7Syz 	if (data) {
1433c77a61a7Syz 		freemsg(data);
1434c77a61a7Syz 	}
1435c77a61a7Syz 	USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
1436c77a61a7Syz 	    "usbvc_v4l2_query_ctrl: fail when %s", req);
1437c77a61a7Syz 
1438c77a61a7Syz 	return (USB_FAILURE);
1439c77a61a7Syz 
1440c77a61a7Syz }
1441c77a61a7Syz 
1442c77a61a7Syz 
1443c77a61a7Syz /* Implement ioctl VIDIOC_G_CTRL, get current ctrl */
1444c77a61a7Syz static int
usbvc_v4l2_get_ctrl(usbvc_state_t * usbvcp,struct v4l2_control * v4l2_ctrl)1445c77a61a7Syz usbvc_v4l2_get_ctrl(usbvc_state_t *usbvcp, struct v4l2_control *v4l2_ctrl)
1446c77a61a7Syz {
1447c77a61a7Syz 	usbvc_v4l2_ctrl_t	ctrl;
1448c77a61a7Syz 	mblk_t			*data;
1449c77a61a7Syz 
1450c77a61a7Syz 	if (usbvc_v4l2_match_ctrl(usbvcp, &ctrl, v4l2_ctrl->id) !=
1451c77a61a7Syz 	    USB_SUCCESS) {
1452c77a61a7Syz 
1453c77a61a7Syz 		return (USB_FAILURE);
1454c77a61a7Syz 	}
1455c77a61a7Syz 	if ((data = allocb(ctrl.ctrl_map->len, BPRI_LO)) == NULL) {
1456c77a61a7Syz 
1457c77a61a7Syz 		return (USB_FAILURE);
1458c77a61a7Syz 	}
1459c77a61a7Syz 
1460c77a61a7Syz 	if (usbvc_vc_get_ctrl(usbvcp, GET_CUR, ctrl.entity_id,
1461c77a61a7Syz 	    ctrl.ctrl_map->selector, ctrl.ctrl_map->len, data) != USB_SUCCESS) {
1462c77a61a7Syz 		if (data) {
1463c77a61a7Syz 			freemsg(data);
1464c77a61a7Syz 		}
1465c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
1466c77a61a7Syz 		    "usbvc_v4l2_get_ctrl: fail");
1467c77a61a7Syz 
1468c77a61a7Syz 		return (USB_FAILURE);
1469c77a61a7Syz 	}
1470c77a61a7Syz 	LE_TO_UINT16(data->b_rptr, 0, v4l2_ctrl->value);
1471c77a61a7Syz 
1472c77a61a7Syz 	if (data) {
1473c77a61a7Syz 		freemsg(data);
1474c77a61a7Syz 	}
1475c77a61a7Syz 
1476c77a61a7Syz 	return (USB_SUCCESS);
1477c77a61a7Syz }
1478c77a61a7Syz 
1479c77a61a7Syz 
1480c77a61a7Syz /* Implement ioctl VIDIOC_S_CTRL */
1481c77a61a7Syz static int
usbvc_v4l2_set_ctrl(usbvc_state_t * usbvcp,struct v4l2_control * v4l2_ctrl)1482c77a61a7Syz usbvc_v4l2_set_ctrl(usbvc_state_t *usbvcp, struct v4l2_control *v4l2_ctrl)
1483c77a61a7Syz {
1484c77a61a7Syz 	usbvc_v4l2_ctrl_t	ctrl;
1485c77a61a7Syz 	mblk_t			*data;
1486c77a61a7Syz 
1487c77a61a7Syz 	if (usbvc_v4l2_match_ctrl(usbvcp, &ctrl, v4l2_ctrl->id) !=
1488c77a61a7Syz 	    USB_SUCCESS) {
1489c77a61a7Syz 
1490c77a61a7Syz 		return (USB_FAILURE);
1491c77a61a7Syz 	}
1492c77a61a7Syz 	if ((data = allocb(ctrl.ctrl_map->len, BPRI_LO)) == NULL) {
1493c77a61a7Syz 
1494c77a61a7Syz 		return (USB_FAILURE);
1495c77a61a7Syz 	}
1496c77a61a7Syz 
1497c77a61a7Syz 	UINT16_TO_LE(v4l2_ctrl->value, 0, data->b_wptr);
1498c77a61a7Syz 	data->b_wptr += 2;
1499c77a61a7Syz 	if (usbvc_vc_set_ctrl(usbvcp, SET_CUR, ctrl.entity_id,
1500112116d8Sfb 	    ctrl.ctrl_map->selector, ctrl.ctrl_map->len, data) !=
1501112116d8Sfb 	    USB_SUCCESS) {
1502c77a61a7Syz 		if (data) {
1503c77a61a7Syz 			freemsg(data);
1504c77a61a7Syz 		}
1505c77a61a7Syz 		USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
1506c77a61a7Syz 		    "usbvc_v4l2_set_ctrl: fail");
1507c77a61a7Syz 
1508c77a61a7Syz 		return (USB_FAILURE);
1509c77a61a7Syz 	}
1510c77a61a7Syz 	if (data) {
1511c77a61a7Syz 		freemsg(data);
1512c77a61a7Syz 	}
1513c77a61a7Syz 
1514c77a61a7Syz 	return (USB_SUCCESS);
1515c77a61a7Syz }
151661b2e298Slc 
151761b2e298Slc /* For the given interval, find the closest frame interval to it. */
151861b2e298Slc static uint32_t
usbvc_find_interval(usbvc_frames_t * frame,uint32_t interval)151961b2e298Slc usbvc_find_interval(usbvc_frames_t *frame, uint32_t interval)
152061b2e298Slc {
152161b2e298Slc 	uint32_t step, i, closest, index, approx1, approx2;
152261b2e298Slc 
152361b2e298Slc 
152461b2e298Slc 	/*
152561b2e298Slc 	 * for continuous case, there is a min and a max, and also a step
152661b2e298Slc 	 * value. The available intervals are those between min and max
152761b2e298Slc 	 * values.
152861b2e298Slc 	 */
152961b2e298Slc 	if (!frame->descr->bFrameIntervalType) {
153061b2e298Slc 		step = frame->dwFrameIntervalStep;
153161b2e298Slc 
153261b2e298Slc 		if (step == 0) {
153361b2e298Slc 		/* a malfunction device */
153461b2e298Slc 
153561b2e298Slc 			return (0);
153661b2e298Slc 		} else if (interval <= frame->dwMinFrameInterval) {
153761b2e298Slc 		/* return the most possible interval we can handle */
153861b2e298Slc 
153961b2e298Slc 			return (frame->dwMinFrameInterval);
154061b2e298Slc 		} else if (interval >= frame->dwMaxFrameInterval) {
154161b2e298Slc 		/* return the most possible interval we can handle */
154261b2e298Slc 
154361b2e298Slc 			return (frame->dwMaxFrameInterval);
154461b2e298Slc 		}
154561b2e298Slc 
154661b2e298Slc 		approx1 = (interval / step) * step;
154761b2e298Slc 		approx2 = approx1 + step;
154861b2e298Slc 		closest = ((interval - approx1) < (approx2 - interval)) ?
154961b2e298Slc 		    approx1 : approx2;
155061b2e298Slc 
155161b2e298Slc 		return (closest);
155261b2e298Slc 	}
155361b2e298Slc 
155461b2e298Slc 	/*
155561b2e298Slc 	 * for discrete case, search all the available intervals, find the
155661b2e298Slc 	 * closest one.
155761b2e298Slc 	 */
155861b2e298Slc 	closest = 0;
155961b2e298Slc 	approx2 = (uint32_t)-1;
156061b2e298Slc 	for (index = 0; index < frame->descr->bFrameIntervalType; index++) {
156161b2e298Slc 		LE_TO_UINT32(frame->dwFrameInterval, index * 4, i);
156261b2e298Slc 		approx1 = (i > interval) ? (i - interval) : (interval - i);
156361b2e298Slc 
156461b2e298Slc 		if (approx1 == 0) {
156561b2e298Slc 		/* find the matched one, return it immediately */
156661b2e298Slc 			return (i);
156761b2e298Slc 		}
156861b2e298Slc 
156961b2e298Slc 		if (approx1 < approx2) {
157061b2e298Slc 			approx2 = approx1;
157161b2e298Slc 			closest = i;
157261b2e298Slc 		}
157361b2e298Slc 	}
157461b2e298Slc 
157561b2e298Slc 	return (closest);
157661b2e298Slc }
157761b2e298Slc 
157861b2e298Slc /* Implement ioctl VIDIOC_S_PARM. Support capture only, so far. */
157961b2e298Slc static int
usbvc_v4l2_set_parm(usbvc_state_t * usbvcp,struct v4l2_streamparm * parm)158061b2e298Slc usbvc_v4l2_set_parm(usbvc_state_t *usbvcp, struct v4l2_streamparm *parm)
158161b2e298Slc {
158261b2e298Slc 	usbvc_stream_if_t	*strm_if;
158361b2e298Slc 	usbvc_format_group_t	*cur_fmt;
158461b2e298Slc 	usbvc_frames_t			*cur_frame;
158561b2e298Slc 	uint32_t n, d, c, i;
158661b2e298Slc 	usbvc_vs_probe_commit_t	ctrl;
158761b2e298Slc 
158861b2e298Slc 	mutex_enter(&usbvcp->usbvc_mutex);
158961b2e298Slc 	strm_if = usbvcp->usbvc_curr_strm;
159061b2e298Slc 
159161b2e298Slc 	if (!strm_if->cur_format_group ||
159261b2e298Slc 	    !strm_if->cur_format_group->cur_frame) {
159361b2e298Slc 		USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
159461b2e298Slc 		    "usbvc_v4l2_set_parm: current format or"
159561b2e298Slc 		    " frame is not set. cur_fmt=%p",
159661b2e298Slc 		    (void *)strm_if->cur_format_group);
159761b2e298Slc 
159861b2e298Slc 		mutex_exit(&usbvcp->usbvc_mutex);
159961b2e298Slc 
160061b2e298Slc 		return (USB_FAILURE);
160161b2e298Slc 	}
160261b2e298Slc 
160361b2e298Slc 	cur_fmt = strm_if->cur_format_group;
160461b2e298Slc 	cur_frame = cur_fmt->cur_frame;
160561b2e298Slc 
160661b2e298Slc 	mutex_exit(&usbvcp->usbvc_mutex);
160761b2e298Slc 	if (parm->parm.capture.readbuffers > USBVC_MAX_READ_BUF_NUM) {
160861b2e298Slc 		USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
160961b2e298Slc 		    "usbvc_v4l2_set_parm: ask too many read buffers,"
161061b2e298Slc 		    " readbuffers=%d",
161161b2e298Slc 		    parm->parm.capture.readbuffers);
161261b2e298Slc 
161361b2e298Slc 		return (USB_FAILURE);
161461b2e298Slc 	}
161561b2e298Slc 
161661b2e298Slc 	n = parm->parm.capture.timeperframe.numerator;
161761b2e298Slc 	d = parm->parm.capture.timeperframe.denominator;
161861b2e298Slc 
161961b2e298Slc 	/* check the values passed in, in case of zero devide */
162061b2e298Slc 	if (d == 0) {
162161b2e298Slc 		USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
162261b2e298Slc 		    "usbvc_v4l2_set_parm: invalid denominator=%d", d);
162361b2e298Slc 
162461b2e298Slc 		return (USB_FAILURE);
162561b2e298Slc 	}
162661b2e298Slc 
162761b2e298Slc 	/*
162861b2e298Slc 	 * UVC frame intervals are in 100ns units, need convert from
162961b2e298Slc 	 * 1s unit to 100ns unit
163061b2e298Slc 	 */
163161b2e298Slc 	c = USBVC_FRAME_INTERVAL_DENOMINATOR;
163261b2e298Slc 
163361b2e298Slc 	/* check the values passed in, in case of overflow */
163461b2e298Slc 	if (n / d >= ((uint32_t)-1) / c) {
163561b2e298Slc 		USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
163661b2e298Slc 		    "usbvc_v4l2_set_parm: overflow, numerator=%d,"
163761b2e298Slc 		    " denominator=%d", n, d);
163861b2e298Slc 
163961b2e298Slc 		return (USB_FAILURE);
164061b2e298Slc 	}
164161b2e298Slc 
164261b2e298Slc 	USB_DPRINTF_L3(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
164361b2e298Slc 	    "usbvc_v4l2_set_parm: numerator=%d, denominator=%d", n, d);
164461b2e298Slc 
164561b2e298Slc 	/* compute the interval in 100ns unit */
164661b2e298Slc 	if (n <= ((uint32_t)-1) / c) {
164761b2e298Slc 		i = (n * c) / d;
164861b2e298Slc 	} else {
164961b2e298Slc 		do {
165061b2e298Slc 			n >>= 1;
165161b2e298Slc 			d >>= 1;
165261b2e298Slc 		/* decrease both n and d, in case overflow */
165361b2e298Slc 		} while (n && d && n > ((uint32_t)-1) / c);
165461b2e298Slc 
165561b2e298Slc 		if (!d) {
165661b2e298Slc 			USB_DPRINTF_L2(PRINT_MASK_IOCTL,
165761b2e298Slc 			    usbvcp->usbvc_log_handle,
165861b2e298Slc 			    "usbvc_v4l2_set_parm: can't compute interval,"
165961b2e298Slc 			    " denominator=%d", d);
166061b2e298Slc 
166161b2e298Slc 			return (USB_FAILURE);
166261b2e298Slc 		}
166361b2e298Slc 		i = (n * c) / d;
166461b2e298Slc 	}
166561b2e298Slc 
166661b2e298Slc 	USB_DPRINTF_L3(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
166761b2e298Slc 	    "usbvc_v4l2_set_parm: want interval=%d, n=%d, d=%d, c=%d",
166861b2e298Slc 	    i, n, d, c);
166961b2e298Slc 
167061b2e298Slc 	/*
167161b2e298Slc 	 * Begin negotiate frame intervals.
167261b2e298Slc 	 */
167361b2e298Slc 	bcopy(&strm_if->ctrl_pc, &ctrl, sizeof (usbvc_vs_probe_commit_t));
167461b2e298Slc 	i = usbvc_find_interval(cur_frame, i);
167561b2e298Slc 
167661b2e298Slc 	if (i == 0) {
167761b2e298Slc 		USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
167861b2e298Slc 		    "usbvc_v4l2_set_parm: can not find an proper interval."
167961b2e298Slc 		    " i=%d, n=%d, d=%d", i, n, d);
168061b2e298Slc 
168161b2e298Slc 		return (USB_FAILURE);
168261b2e298Slc 	}
168361b2e298Slc 
168461b2e298Slc 	USB_DPRINTF_L3(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
168561b2e298Slc 	    "usbvc_v4l2_set_parm: get interval=%d", i);
168661b2e298Slc 
168761b2e298Slc 	UINT32_TO_LE(i, 0, ctrl.dwFrameInterval);
168861b2e298Slc 
168961b2e298Slc 	/* Probe, just a test before the real try */
169061b2e298Slc 	if (usbvc_vs_set_probe_commit(usbvcp, strm_if, &ctrl, VS_PROBE_CONTROL)
169161b2e298Slc 	    != USB_SUCCESS) {
169261b2e298Slc 		USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
169361b2e298Slc 		    "usbvc_v4l2_set_parm: set probe failed");
169461b2e298Slc 
169561b2e298Slc 		return (USB_FAILURE);
169661b2e298Slc 	}
169761b2e298Slc 
169861b2e298Slc 	/* Commit the frame interval. */
169961b2e298Slc 	if (usbvc_vs_set_probe_commit(usbvcp, strm_if, &ctrl, VS_COMMIT_CONTROL)
170061b2e298Slc 	    != USB_SUCCESS) {
170161b2e298Slc 		USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
170261b2e298Slc 		    "usbvc_v4l2_set_parm: set commit failed");
170361b2e298Slc 
170461b2e298Slc 		return (USB_FAILURE);
170561b2e298Slc 	}
170661b2e298Slc 
170761b2e298Slc 	bcopy(&ctrl, &strm_if->ctrl_pc, sizeof (usbvc_vs_probe_commit_t));
170861b2e298Slc 
170961b2e298Slc 	LE_TO_UINT32(ctrl.dwFrameInterval, 0, i);
171061b2e298Slc 	parm->parm.capture.timeperframe.numerator = i;
171161b2e298Slc 	parm->parm.capture.timeperframe.denominator = c;
171261b2e298Slc 
171361b2e298Slc 	mutex_enter(&usbvcp->usbvc_mutex);
171461b2e298Slc 	/*
171561b2e298Slc 	 * According to ioctl VIDIOC_S_PARM, zero value of readbuffers will not
171661b2e298Slc 	 * be set. And the current value is expected to return to application.
171761b2e298Slc 	 */
171861b2e298Slc 	if (parm->parm.capture.readbuffers != 0) {
171961b2e298Slc 		strm_if->buf_read_num = parm->parm.capture.readbuffers;
172061b2e298Slc 	} else {
172161b2e298Slc 		parm->parm.capture.readbuffers = strm_if->buf_read_num;
172261b2e298Slc 	}
172361b2e298Slc 	mutex_exit(&usbvcp->usbvc_mutex);
172461b2e298Slc 
172561b2e298Slc 	return (USB_SUCCESS);
172661b2e298Slc }
172761b2e298Slc 
172861b2e298Slc /* Implement ioctl VIDIOC_G_PARM. */
172961b2e298Slc static int
usbvc_v4l2_get_parm(usbvc_state_t * usbvcp,struct v4l2_streamparm * parm)173061b2e298Slc usbvc_v4l2_get_parm(usbvc_state_t *usbvcp, struct v4l2_streamparm *parm)
173161b2e298Slc {
173261b2e298Slc 	usbvc_stream_if_t	*strm_if;
173361b2e298Slc 	uint32_t n, d;
173461b2e298Slc 
173561b2e298Slc 	bzero(parm, sizeof (*parm));
173661b2e298Slc 
173761b2e298Slc 	mutex_enter(&usbvcp->usbvc_mutex);
173861b2e298Slc 	strm_if = usbvcp->usbvc_curr_strm;
173961b2e298Slc 
174061b2e298Slc 	/* return the actual number of buffers allocated for read() I/O */
174161b2e298Slc 	parm->parm.capture.readbuffers = strm_if->buf_read.buf_cnt;
174261b2e298Slc 
174361b2e298Slc 	/* in 100ns units */
174461b2e298Slc 	LE_TO_UINT32(strm_if->ctrl_pc.dwFrameInterval, 0, n);
174561b2e298Slc 	mutex_exit(&usbvcp->usbvc_mutex);
174661b2e298Slc 
174761b2e298Slc 	/*
174861b2e298Slc 	 * According to UVC payload specs, the dwFrameInterval in frame
174961b2e298Slc 	 * descriptors is in 100ns unit.
175061b2e298Slc 	 */
175161b2e298Slc 	d = USBVC_FRAME_INTERVAL_DENOMINATOR;
175261b2e298Slc 	parm->parm.capture.timeperframe.numerator = n;
175361b2e298Slc 	parm->parm.capture.timeperframe.denominator = d;
175461b2e298Slc 
175561b2e298Slc 	/* Support capture only, so far. */
175661b2e298Slc 	parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
175761b2e298Slc 	parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
175861b2e298Slc 	parm->parm.capture.capturemode = 0; /* no high quality imaging mode */
175961b2e298Slc 	parm->parm.capture.extendedmode = 0; /* no driver specific parameters */
176061b2e298Slc 
176161b2e298Slc 	/* Always success for current support of this command */
176261b2e298Slc 	return (USB_SUCCESS);
176361b2e298Slc }
1764