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