1b711ef9cSBaptiste Daroussin /*-
2b711ef9cSBaptiste Daroussin * SPDX-License-Identifier: ISC
3b711ef9cSBaptiste Daroussin *
4b711ef9cSBaptiste Daroussin * Copyright (c) 2008 Robert Nagy <robert@openbsd.org>
5b711ef9cSBaptiste Daroussin * Copyright (c) 2008 Marcus Glocker <mglocker@openbsd.org>
6b711ef9cSBaptiste Daroussin *
7b711ef9cSBaptiste Daroussin * Permission to use, copy, modify, and distribute this software for any
8b711ef9cSBaptiste Daroussin * purpose with or without fee is hereby granted, provided that the above
9b711ef9cSBaptiste Daroussin * copyright notice and this permission notice appear in all copies.
10b711ef9cSBaptiste Daroussin *
11b711ef9cSBaptiste Daroussin * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12b711ef9cSBaptiste Daroussin * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13b711ef9cSBaptiste Daroussin * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14b711ef9cSBaptiste Daroussin * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15b711ef9cSBaptiste Daroussin * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16b711ef9cSBaptiste Daroussin * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17b711ef9cSBaptiste Daroussin * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18b711ef9cSBaptiste Daroussin *
19b711ef9cSBaptiste Daroussin * Ported from OpenBSD to FreeBSD by Baptiste Daroussin <bapt@FreeBSD.org>
20b711ef9cSBaptiste Daroussin */
21b711ef9cSBaptiste Daroussin
22b711ef9cSBaptiste Daroussin /*
23b711ef9cSBaptiste Daroussin * USB Video Class (UVC) driver.
24b711ef9cSBaptiste Daroussin *
25b711ef9cSBaptiste Daroussin * Implements standard UVC 1.0/1.1/1.5 devices only.
26b711ef9cSBaptiste Daroussin * Creates /dev/videoN character devices with V4L2 ioctl interface.
27b711ef9cSBaptiste Daroussin */
28b711ef9cSBaptiste Daroussin
29b711ef9cSBaptiste Daroussin #include <sys/param.h>
30b711ef9cSBaptiste Daroussin #include <sys/systm.h>
31b711ef9cSBaptiste Daroussin #include <sys/kernel.h>
32b711ef9cSBaptiste Daroussin #include <sys/bus.h>
33b711ef9cSBaptiste Daroussin #include <sys/conf.h>
34b711ef9cSBaptiste Daroussin #include <sys/fcntl.h>
35b711ef9cSBaptiste Daroussin #include <sys/lock.h>
36b711ef9cSBaptiste Daroussin #include <sys/malloc.h>
37b711ef9cSBaptiste Daroussin #include <sys/module.h>
38b711ef9cSBaptiste Daroussin #include <sys/mutex.h>
39b711ef9cSBaptiste Daroussin #include <sys/poll.h>
40b711ef9cSBaptiste Daroussin #include <sys/proc.h>
4154df18cbSBaptiste Daroussin #include <sys/event.h>
42b711ef9cSBaptiste Daroussin #include <sys/selinfo.h>
43b711ef9cSBaptiste Daroussin #include <sys/limits.h>
44b711ef9cSBaptiste Daroussin #include <sys/sysctl.h>
45b711ef9cSBaptiste Daroussin #include <sys/uio.h>
46b711ef9cSBaptiste Daroussin
47b711ef9cSBaptiste Daroussin #include <vm/vm.h>
48b711ef9cSBaptiste Daroussin #include <vm/pmap.h>
49b711ef9cSBaptiste Daroussin
50b711ef9cSBaptiste Daroussin #include <dev/usb/usb.h>
51b711ef9cSBaptiste Daroussin #include <dev/usb/usbdi.h>
52b711ef9cSBaptiste Daroussin #include <dev/usb/usbdi_util.h>
53b711ef9cSBaptiste Daroussin #include <dev/usb/usb_request.h>
54b711ef9cSBaptiste Daroussin #include "usbdevs.h"
55b711ef9cSBaptiste Daroussin
56b711ef9cSBaptiste Daroussin #include <dev/usb/video/uvideo.h>
57b711ef9cSBaptiste Daroussin
58b711ef9cSBaptiste Daroussin #define USB_DEBUG_VAR uvideo_debug
59b711ef9cSBaptiste Daroussin #include <dev/usb/usb_debug.h>
60b711ef9cSBaptiste Daroussin
61b711ef9cSBaptiste Daroussin static SYSCTL_NODE(_hw_usb, OID_AUTO, uvideo, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
62b711ef9cSBaptiste Daroussin "USB uvideo");
63b711ef9cSBaptiste Daroussin
64b711ef9cSBaptiste Daroussin #ifdef USB_DEBUG
65b711ef9cSBaptiste Daroussin static int uvideo_debug = 0;
66b711ef9cSBaptiste Daroussin
67b711ef9cSBaptiste Daroussin SYSCTL_INT(_hw_usb_uvideo, OID_AUTO, debug, CTLFLAG_RWTUN,
68b711ef9cSBaptiste Daroussin &uvideo_debug, 0, "Debug level");
69b711ef9cSBaptiste Daroussin #endif
70b711ef9cSBaptiste Daroussin
71b711ef9cSBaptiste Daroussin #define byteof(x) ((x) >> 3)
72b711ef9cSBaptiste Daroussin #define bitof(x) (1L << ((x) & 0x7))
73b711ef9cSBaptiste Daroussin
74b711ef9cSBaptiste Daroussin /* OpenBSD macros not present in FreeBSD USB headers */
75b711ef9cSBaptiste Daroussin #define UE_GET_SIZE(x) ((x) & 0x7FF)
76b711ef9cSBaptiste Daroussin #define UE_GET_TRANS(x) (((x) >> 11) & 0x03)
77b711ef9cSBaptiste Daroussin
78b711ef9cSBaptiste Daroussin /* IO_NDELAY from sys/vnode.h - avoid pulling in vnode_if.h dependency */
79b711ef9cSBaptiste Daroussin #ifndef IO_NDELAY
80b711ef9cSBaptiste Daroussin #define IO_NDELAY 0x0004
81b711ef9cSBaptiste Daroussin #endif
82b711ef9cSBaptiste Daroussin
83b711ef9cSBaptiste Daroussin /* Forward declarations */
84b711ef9cSBaptiste Daroussin struct uvideo_softc;
85b711ef9cSBaptiste Daroussin
86b711ef9cSBaptiste Daroussin static device_probe_t uvideo_probe;
87b711ef9cSBaptiste Daroussin static device_attach_t uvideo_attach;
88b711ef9cSBaptiste Daroussin static device_detach_t uvideo_detach;
89b711ef9cSBaptiste Daroussin
90b711ef9cSBaptiste Daroussin static usb_callback_t uvideo_isoc_callback;
91b711ef9cSBaptiste Daroussin static usb_callback_t uvideo_bulk_callback;
92b711ef9cSBaptiste Daroussin
93b711ef9cSBaptiste Daroussin static usb_error_t uvideo_vc_parse_desc(struct uvideo_softc *);
94b711ef9cSBaptiste Daroussin static usb_error_t uvideo_vc_parse_desc_header(struct uvideo_softc *,
95b711ef9cSBaptiste Daroussin const struct usb_descriptor *);
96b711ef9cSBaptiste Daroussin static usb_error_t uvideo_vc_parse_desc_pu(struct uvideo_softc *,
97b711ef9cSBaptiste Daroussin const struct usb_descriptor *);
98d0450cbeSBaptiste Daroussin static usb_error_t uvideo_vc_parse_desc_ct(struct uvideo_softc *,
99d0450cbeSBaptiste Daroussin const struct usb_descriptor *);
100d0450cbeSBaptiste Daroussin static int uvideo_has_ct_ctrl(
101d0450cbeSBaptiste Daroussin struct usb_video_camera_terminal_desc *, int);
102b711ef9cSBaptiste Daroussin static usb_error_t uvideo_vc_get_ctrl(struct uvideo_softc *, uint8_t *,
103b711ef9cSBaptiste Daroussin uint8_t, uint8_t, uint16_t, uint16_t);
104b711ef9cSBaptiste Daroussin static usb_error_t uvideo_vc_set_ctrl(struct uvideo_softc *, uint8_t *,
105b711ef9cSBaptiste Daroussin uint8_t, uint8_t, uint16_t, uint16_t);
106b711ef9cSBaptiste Daroussin static int uvideo_find_ctrl(struct uvideo_softc *, int);
107b711ef9cSBaptiste Daroussin static int uvideo_has_ctrl(struct usb_video_vc_processing_desc *,
108b711ef9cSBaptiste Daroussin int);
109b711ef9cSBaptiste Daroussin
110b711ef9cSBaptiste Daroussin static usb_error_t uvideo_vs_parse_desc(struct uvideo_softc *,
111b711ef9cSBaptiste Daroussin struct usb_config_descriptor *);
112b711ef9cSBaptiste Daroussin static usb_error_t uvideo_vs_parse_desc_input_header(struct uvideo_softc *,
113b711ef9cSBaptiste Daroussin const struct usb_descriptor *);
114b711ef9cSBaptiste Daroussin static usb_error_t uvideo_vs_parse_desc_format(struct uvideo_softc *);
115b711ef9cSBaptiste Daroussin static void uvideo_vs_parse_desc_colorformat(struct uvideo_softc *,
116b711ef9cSBaptiste Daroussin const struct usb_descriptor *);
117b711ef9cSBaptiste Daroussin static void uvideo_vs_parse_desc_format_frame_based(
118b711ef9cSBaptiste Daroussin struct uvideo_softc *,
119b711ef9cSBaptiste Daroussin const struct usb_descriptor *);
120b711ef9cSBaptiste Daroussin static void uvideo_vs_parse_desc_format_h264(struct uvideo_softc *,
121b711ef9cSBaptiste Daroussin const struct usb_descriptor *);
122b711ef9cSBaptiste Daroussin static void uvideo_vs_parse_desc_format_mjpeg(struct uvideo_softc *,
123b711ef9cSBaptiste Daroussin const struct usb_descriptor *);
124b711ef9cSBaptiste Daroussin static void uvideo_vs_parse_desc_format_uncompressed(
125b711ef9cSBaptiste Daroussin struct uvideo_softc *,
126b711ef9cSBaptiste Daroussin const struct usb_descriptor *);
127b711ef9cSBaptiste Daroussin static usb_error_t uvideo_vs_parse_desc_frame(struct uvideo_softc *);
128b711ef9cSBaptiste Daroussin static usb_error_t uvideo_vs_parse_desc_frame_buffer_size(
129b711ef9cSBaptiste Daroussin struct uvideo_softc *,
130b711ef9cSBaptiste Daroussin const struct usb_descriptor *);
131b711ef9cSBaptiste Daroussin static usb_error_t uvideo_vs_parse_desc_frame_max_rate(
132b711ef9cSBaptiste Daroussin struct uvideo_softc *,
133b711ef9cSBaptiste Daroussin const struct usb_descriptor *);
134b711ef9cSBaptiste Daroussin static usb_error_t uvideo_vs_parse_desc_alt(struct uvideo_softc *, int,
135b711ef9cSBaptiste Daroussin int, int);
136b711ef9cSBaptiste Daroussin static int uvideo_desc_len(const struct usb_descriptor *, int,
137b711ef9cSBaptiste Daroussin int, int, int);
138b711ef9cSBaptiste Daroussin static void uvideo_find_res(struct uvideo_softc *, int, int, int,
139b711ef9cSBaptiste Daroussin struct uvideo_res *);
140b711ef9cSBaptiste Daroussin static usb_error_t uvideo_vs_negotiation(struct uvideo_softc *, int);
141b711ef9cSBaptiste Daroussin static usb_error_t uvideo_vs_set_probe(struct uvideo_softc *, uint8_t *);
142b711ef9cSBaptiste Daroussin static usb_error_t uvideo_vs_get_probe(struct uvideo_softc *, uint8_t *,
143b711ef9cSBaptiste Daroussin uint8_t);
144b711ef9cSBaptiste Daroussin static usb_error_t uvideo_vs_set_commit(struct uvideo_softc *, uint8_t *);
145b711ef9cSBaptiste Daroussin static usb_error_t uvideo_vs_alloc_frame(struct uvideo_softc *);
146b711ef9cSBaptiste Daroussin static void uvideo_vs_free_frame(struct uvideo_softc *);
147b711ef9cSBaptiste Daroussin static usb_error_t uvideo_vs_open(struct uvideo_softc *);
148b711ef9cSBaptiste Daroussin static void uvideo_vs_close(struct uvideo_softc *);
149b711ef9cSBaptiste Daroussin static usb_error_t uvideo_vs_init(struct uvideo_softc *);
150b711ef9cSBaptiste Daroussin static void uvideo_vs_decode_stream_header(struct uvideo_softc *,
151b711ef9cSBaptiste Daroussin uint8_t *, int);
152b711ef9cSBaptiste Daroussin static void uvideo_isoc_decode(struct uvideo_softc *,
153b711ef9cSBaptiste Daroussin struct usb_page_cache *, int, int);
154b711ef9cSBaptiste Daroussin static uint8_t *uvideo_mmap_getbuf(struct uvideo_softc *);
155b711ef9cSBaptiste Daroussin static void uvideo_mmap_queue(struct uvideo_softc *, int, int);
156b711ef9cSBaptiste Daroussin static void uvideo_read_frame(struct uvideo_softc *, uint8_t *, int);
157b711ef9cSBaptiste Daroussin
158b711ef9cSBaptiste Daroussin static d_open_t uvideo_cdev_open;
159b711ef9cSBaptiste Daroussin static d_close_t uvideo_cdev_close;
160b711ef9cSBaptiste Daroussin static d_read_t uvideo_cdev_read;
161b711ef9cSBaptiste Daroussin static d_ioctl_t uvideo_cdev_ioctl;
162b711ef9cSBaptiste Daroussin static d_poll_t uvideo_cdev_poll;
16354df18cbSBaptiste Daroussin static d_kqfilter_t uvideo_cdev_kqfilter;
164b711ef9cSBaptiste Daroussin static d_mmap_t uvideo_cdev_mmap;
165b711ef9cSBaptiste Daroussin
166b711ef9cSBaptiste Daroussin static int uvideo_querycap(struct uvideo_softc *, struct v4l2_capability *);
167b711ef9cSBaptiste Daroussin static int uvideo_enum_fmt(struct uvideo_softc *, struct v4l2_fmtdesc *);
168b711ef9cSBaptiste Daroussin static int uvideo_enum_fsizes(struct uvideo_softc *,
169b711ef9cSBaptiste Daroussin struct v4l2_frmsizeenum *);
170b711ef9cSBaptiste Daroussin static int uvideo_enum_fivals(struct uvideo_softc *,
171b711ef9cSBaptiste Daroussin struct v4l2_frmivalenum *);
172b711ef9cSBaptiste Daroussin static int uvideo_s_fmt(struct uvideo_softc *, struct v4l2_format *);
173b711ef9cSBaptiste Daroussin static int uvideo_g_fmt(struct uvideo_softc *, struct v4l2_format *);
174b711ef9cSBaptiste Daroussin static int uvideo_s_parm(struct uvideo_softc *, struct v4l2_streamparm *);
175b711ef9cSBaptiste Daroussin static int uvideo_g_parm(struct uvideo_softc *, struct v4l2_streamparm *);
176b711ef9cSBaptiste Daroussin static int uvideo_enum_input(struct uvideo_softc *, struct v4l2_input *);
177b711ef9cSBaptiste Daroussin static int uvideo_s_input(struct uvideo_softc *, int);
178b711ef9cSBaptiste Daroussin static int uvideo_g_input(struct uvideo_softc *, int *);
179b711ef9cSBaptiste Daroussin static int uvideo_reqbufs(struct uvideo_softc *,
180b711ef9cSBaptiste Daroussin struct v4l2_requestbuffers *);
181b711ef9cSBaptiste Daroussin static int uvideo_querybuf(struct uvideo_softc *, struct v4l2_buffer *);
182b711ef9cSBaptiste Daroussin static int uvideo_qbuf(struct uvideo_softc *, struct v4l2_buffer *);
183b711ef9cSBaptiste Daroussin static int uvideo_dqbuf(struct uvideo_softc *, struct v4l2_buffer *);
184b711ef9cSBaptiste Daroussin static int uvideo_streamon(struct uvideo_softc *, int);
185b711ef9cSBaptiste Daroussin static int uvideo_streamoff(struct uvideo_softc *, int);
186b711ef9cSBaptiste Daroussin static int uvideo_try_fmt(struct uvideo_softc *, struct v4l2_format *);
187b711ef9cSBaptiste Daroussin static int uvideo_queryctrl(struct uvideo_softc *,
188b711ef9cSBaptiste Daroussin struct v4l2_queryctrl *);
189b711ef9cSBaptiste Daroussin static int uvideo_g_ctrl(struct uvideo_softc *, struct v4l2_control *);
190b711ef9cSBaptiste Daroussin static int uvideo_s_ctrl(struct uvideo_softc *, struct v4l2_control *);
191b711ef9cSBaptiste Daroussin
192b711ef9cSBaptiste Daroussin /*
193b711ef9cSBaptiste Daroussin * Transfer configuration indices.
194b711ef9cSBaptiste Daroussin */
195b711ef9cSBaptiste Daroussin enum {
196b711ef9cSBaptiste Daroussin UVIDEO_ISOC_RX_0,
197b711ef9cSBaptiste Daroussin UVIDEO_ISOC_RX_1,
198b711ef9cSBaptiste Daroussin UVIDEO_ISOC_RX_2,
199*3b6f833cSBaptiste Daroussin UVIDEO_ISOC_RX_3,
200*3b6f833cSBaptiste Daroussin UVIDEO_ISOC_RX_4,
201b711ef9cSBaptiste Daroussin UVIDEO_BULK_RX,
202b711ef9cSBaptiste Daroussin UVIDEO_N_XFER
203b711ef9cSBaptiste Daroussin };
204b711ef9cSBaptiste Daroussin
205b711ef9cSBaptiste Daroussin /*
206b711ef9cSBaptiste Daroussin * The softc structure.
207b711ef9cSBaptiste Daroussin */
208b711ef9cSBaptiste Daroussin struct uvideo_softc {
209b711ef9cSBaptiste Daroussin device_t sc_dev;
210b711ef9cSBaptiste Daroussin struct usb_device *sc_udev;
211b711ef9cSBaptiste Daroussin struct mtx sc_mtx;
212b711ef9cSBaptiste Daroussin struct cdev *sc_cdev;
213b711ef9cSBaptiste Daroussin int sc_unit;
214b711ef9cSBaptiste Daroussin
215b711ef9cSBaptiste Daroussin uint8_t sc_iface_index;
216b711ef9cSBaptiste Daroussin uint8_t sc_nifaces;
217b711ef9cSBaptiste Daroussin int sc_dying;
218b711ef9cSBaptiste Daroussin int sc_open;
219b711ef9cSBaptiste Daroussin uint32_t sc_priority;
220b711ef9cSBaptiste Daroussin struct proc *sc_owner;
221b711ef9cSBaptiste Daroussin
222b711ef9cSBaptiste Daroussin struct usb_xfer *sc_xfer[UVIDEO_N_XFER];
223b711ef9cSBaptiste Daroussin int sc_streaming;
224b711ef9cSBaptiste Daroussin
225b711ef9cSBaptiste Daroussin int sc_max_ctrl_size;
226b711ef9cSBaptiste Daroussin int sc_max_fbuf_size;
227b711ef9cSBaptiste Daroussin int sc_negotiated_flag;
228b711ef9cSBaptiste Daroussin int sc_frame_rate;
229b711ef9cSBaptiste Daroussin
230b711ef9cSBaptiste Daroussin struct uvideo_frame_buffer sc_frame_buffer;
231b711ef9cSBaptiste Daroussin
232b711ef9cSBaptiste Daroussin struct uvideo_mmap sc_mmap[UVIDEO_MAX_BUFFERS];
233b711ef9cSBaptiste Daroussin struct uvideo_mmap *sc_mmap_cur;
234b711ef9cSBaptiste Daroussin uint8_t *sc_mmap_buffer;
235b711ef9cSBaptiste Daroussin size_t sc_mmap_buffer_size;
236b711ef9cSBaptiste Daroussin int sc_mmap_buffer_idx;
237b711ef9cSBaptiste Daroussin q_mmap sc_mmap_q;
238b711ef9cSBaptiste Daroussin int sc_mmap_count;
239b711ef9cSBaptiste Daroussin int sc_mmap_flag;
240b711ef9cSBaptiste Daroussin
241b711ef9cSBaptiste Daroussin uint8_t *sc_tmpbuf;
242b711ef9cSBaptiste Daroussin int sc_tmpbuf_size;
243b711ef9cSBaptiste Daroussin
244b711ef9cSBaptiste Daroussin int sc_nframes;
245b711ef9cSBaptiste Daroussin struct usb_video_probe_commit sc_desc_probe;
246b711ef9cSBaptiste Daroussin struct usb_video_header_desc_all sc_desc_vc_header;
247b711ef9cSBaptiste Daroussin struct usb_video_input_header_desc_all sc_desc_vs_input_header;
248b711ef9cSBaptiste Daroussin
249b711ef9cSBaptiste Daroussin #define UVIDEO_MAX_PU 8
250b711ef9cSBaptiste Daroussin int sc_desc_vc_pu_num;
251b711ef9cSBaptiste Daroussin struct usb_video_vc_processing_desc *sc_desc_vc_pu_cur;
252b711ef9cSBaptiste Daroussin struct usb_video_vc_processing_desc *sc_desc_vc_pu[UVIDEO_MAX_PU];
253b711ef9cSBaptiste Daroussin
254d0450cbeSBaptiste Daroussin #define UVIDEO_MAX_CT 8
255d0450cbeSBaptiste Daroussin int sc_desc_vc_ct_num;
256d0450cbeSBaptiste Daroussin struct usb_video_camera_terminal_desc *sc_desc_vc_ct_cur;
257d0450cbeSBaptiste Daroussin struct usb_video_camera_terminal_desc *sc_desc_vc_ct[UVIDEO_MAX_CT];
258d0450cbeSBaptiste Daroussin
259b711ef9cSBaptiste Daroussin #define UVIDEO_MAX_FORMAT 8
260b711ef9cSBaptiste Daroussin int sc_fmtgrp_idx;
261b711ef9cSBaptiste Daroussin int sc_fmtgrp_num;
262b711ef9cSBaptiste Daroussin struct uvideo_format_group *sc_fmtgrp_cur;
263b711ef9cSBaptiste Daroussin struct uvideo_format_group sc_fmtgrp[UVIDEO_MAX_FORMAT];
264b711ef9cSBaptiste Daroussin
265b711ef9cSBaptiste Daroussin #define UVIDEO_MAX_VS_NUM 8
266b711ef9cSBaptiste Daroussin struct uvideo_vs_iface *sc_vs_cur;
267b711ef9cSBaptiste Daroussin struct uvideo_vs_iface sc_vs_coll[UVIDEO_MAX_VS_NUM];
268b711ef9cSBaptiste Daroussin
269b711ef9cSBaptiste Daroussin int sc_fsize;
270b711ef9cSBaptiste Daroussin uint8_t *sc_fbuffer;
271b711ef9cSBaptiste Daroussin size_t sc_fbufferlen;
272b711ef9cSBaptiste Daroussin int sc_vidmode;
273b711ef9cSBaptiste Daroussin #define VIDMODE_NONE 0
274b711ef9cSBaptiste Daroussin #define VIDMODE_MMAP 1
275b711ef9cSBaptiste Daroussin #define VIDMODE_READ 2
276b711ef9cSBaptiste Daroussin int sc_frames_ready;
277b711ef9cSBaptiste Daroussin
278b711ef9cSBaptiste Daroussin struct selinfo sc_selinfo;
279b711ef9cSBaptiste Daroussin
280b711ef9cSBaptiste Daroussin void (*sc_decode_stream_header)(
281b711ef9cSBaptiste Daroussin struct uvideo_softc *, uint8_t *, int);
282b711ef9cSBaptiste Daroussin };
283b711ef9cSBaptiste Daroussin
284b711ef9cSBaptiste Daroussin /*
285b711ef9cSBaptiste Daroussin * Processing Unit control descriptors
286b711ef9cSBaptiste Daroussin */
287b711ef9cSBaptiste Daroussin static struct uvideo_controls uvideo_ctrls[] = {
288b711ef9cSBaptiste Daroussin {
289b711ef9cSBaptiste Daroussin V4L2_CID_BRIGHTNESS,
290b711ef9cSBaptiste Daroussin V4L2_CTRL_TYPE_INTEGER,
291b711ef9cSBaptiste Daroussin "Brightness",
292b711ef9cSBaptiste Daroussin 0,
293b711ef9cSBaptiste Daroussin PU_BRIGHTNESS_CONTROL,
294b711ef9cSBaptiste Daroussin 2,
295b711ef9cSBaptiste Daroussin 1
296b711ef9cSBaptiste Daroussin },
297b711ef9cSBaptiste Daroussin {
298b711ef9cSBaptiste Daroussin V4L2_CID_CONTRAST,
299b711ef9cSBaptiste Daroussin V4L2_CTRL_TYPE_INTEGER,
300b711ef9cSBaptiste Daroussin "Contrast",
301b711ef9cSBaptiste Daroussin 1,
302b711ef9cSBaptiste Daroussin PU_CONTRAST_CONTROL,
303b711ef9cSBaptiste Daroussin 2,
304b711ef9cSBaptiste Daroussin 0
305b711ef9cSBaptiste Daroussin },
306b711ef9cSBaptiste Daroussin {
307b711ef9cSBaptiste Daroussin V4L2_CID_HUE,
308b711ef9cSBaptiste Daroussin V4L2_CTRL_TYPE_INTEGER,
309b711ef9cSBaptiste Daroussin "Hue",
310b711ef9cSBaptiste Daroussin 2,
311b711ef9cSBaptiste Daroussin PU_HUE_CONTROL,
312b711ef9cSBaptiste Daroussin 2,
313b711ef9cSBaptiste Daroussin 1
314b711ef9cSBaptiste Daroussin },
315b711ef9cSBaptiste Daroussin {
316b711ef9cSBaptiste Daroussin V4L2_CID_SATURATION,
317b711ef9cSBaptiste Daroussin V4L2_CTRL_TYPE_INTEGER,
318b711ef9cSBaptiste Daroussin "Saturation",
319b711ef9cSBaptiste Daroussin 3,
320b711ef9cSBaptiste Daroussin PU_SATURATION_CONTROL,
321b711ef9cSBaptiste Daroussin 2,
322b711ef9cSBaptiste Daroussin 0
323b711ef9cSBaptiste Daroussin },
324b711ef9cSBaptiste Daroussin {
325b711ef9cSBaptiste Daroussin V4L2_CID_SHARPNESS,
326b711ef9cSBaptiste Daroussin V4L2_CTRL_TYPE_INTEGER,
327b711ef9cSBaptiste Daroussin "Sharpness",
328b711ef9cSBaptiste Daroussin 4,
329b711ef9cSBaptiste Daroussin PU_SHARPNESS_CONTROL,
330b711ef9cSBaptiste Daroussin 2,
331b711ef9cSBaptiste Daroussin 0
332b711ef9cSBaptiste Daroussin },
333b711ef9cSBaptiste Daroussin {
334b711ef9cSBaptiste Daroussin V4L2_CID_GAMMA,
335b711ef9cSBaptiste Daroussin V4L2_CTRL_TYPE_INTEGER,
336b711ef9cSBaptiste Daroussin "Gamma",
337b711ef9cSBaptiste Daroussin 5,
338b711ef9cSBaptiste Daroussin PU_GAMMA_CONTROL,
339b711ef9cSBaptiste Daroussin 2,
340b711ef9cSBaptiste Daroussin 0
341b711ef9cSBaptiste Daroussin },
342b711ef9cSBaptiste Daroussin {
343b711ef9cSBaptiste Daroussin V4L2_CID_WHITE_BALANCE_TEMPERATURE,
344b711ef9cSBaptiste Daroussin V4L2_CTRL_TYPE_INTEGER,
345b711ef9cSBaptiste Daroussin "White Balance Temperature",
346b711ef9cSBaptiste Daroussin 6,
347b711ef9cSBaptiste Daroussin PU_WHITE_BALANCE_TEMPERATURE_CONTROL,
348b711ef9cSBaptiste Daroussin 2,
349b711ef9cSBaptiste Daroussin 0
350b711ef9cSBaptiste Daroussin },
351b711ef9cSBaptiste Daroussin {
352b711ef9cSBaptiste Daroussin V4L2_CID_BACKLIGHT_COMPENSATION,
353b711ef9cSBaptiste Daroussin V4L2_CTRL_TYPE_INTEGER,
354b711ef9cSBaptiste Daroussin "Backlight Compensation",
355b711ef9cSBaptiste Daroussin 8,
356b711ef9cSBaptiste Daroussin PU_BACKLIGHT_COMPENSATION_CONTROL,
357b711ef9cSBaptiste Daroussin 2,
358b711ef9cSBaptiste Daroussin 0
359b711ef9cSBaptiste Daroussin },
360b711ef9cSBaptiste Daroussin {
361b711ef9cSBaptiste Daroussin V4L2_CID_GAIN,
362b711ef9cSBaptiste Daroussin V4L2_CTRL_TYPE_INTEGER,
363b711ef9cSBaptiste Daroussin "Gain",
364b711ef9cSBaptiste Daroussin 9,
365b711ef9cSBaptiste Daroussin PU_GAIN_CONTROL,
366b711ef9cSBaptiste Daroussin 2,
367b711ef9cSBaptiste Daroussin 0
368b711ef9cSBaptiste Daroussin },
369b711ef9cSBaptiste Daroussin {
370b711ef9cSBaptiste Daroussin V4L2_CID_POWER_LINE_FREQUENCY,
371b711ef9cSBaptiste Daroussin V4L2_CTRL_TYPE_MENU,
372b711ef9cSBaptiste Daroussin "Power Line Frequency",
373b711ef9cSBaptiste Daroussin 10,
374b711ef9cSBaptiste Daroussin PU_POWER_LINE_FREQUENCY_CONTROL,
375b711ef9cSBaptiste Daroussin 2,
376b711ef9cSBaptiste Daroussin 0
377b711ef9cSBaptiste Daroussin },
378b711ef9cSBaptiste Daroussin {
379b711ef9cSBaptiste Daroussin V4L2_CID_HUE_AUTO,
380b711ef9cSBaptiste Daroussin V4L2_CTRL_TYPE_BOOLEAN,
381b711ef9cSBaptiste Daroussin "Hue Auto",
382b711ef9cSBaptiste Daroussin 11,
383b711ef9cSBaptiste Daroussin PU_HUE_AUTO_CONTROL,
384b711ef9cSBaptiste Daroussin 1,
385b711ef9cSBaptiste Daroussin 0
386b711ef9cSBaptiste Daroussin },
387b711ef9cSBaptiste Daroussin {
388b711ef9cSBaptiste Daroussin V4L2_CID_AUTO_WHITE_BALANCE,
389b711ef9cSBaptiste Daroussin V4L2_CTRL_TYPE_BOOLEAN,
390b711ef9cSBaptiste Daroussin "White Balance Temperature Auto",
391b711ef9cSBaptiste Daroussin 12,
392b711ef9cSBaptiste Daroussin PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL,
393b711ef9cSBaptiste Daroussin 1,
394b711ef9cSBaptiste Daroussin 0
395b711ef9cSBaptiste Daroussin },
396b711ef9cSBaptiste Daroussin {
397b711ef9cSBaptiste Daroussin V4L2_CID_AUTO_WHITE_BALANCE,
398b711ef9cSBaptiste Daroussin V4L2_CTRL_TYPE_BOOLEAN,
399b711ef9cSBaptiste Daroussin "White Balance Component Auto",
400b711ef9cSBaptiste Daroussin 13,
401b711ef9cSBaptiste Daroussin PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL,
402b711ef9cSBaptiste Daroussin 1,
403b711ef9cSBaptiste Daroussin 0
404b711ef9cSBaptiste Daroussin },
405d0450cbeSBaptiste Daroussin /* Camera Terminal Controls (UVC 1.5 spec Table A-12) */
406d0450cbeSBaptiste Daroussin {
407d0450cbeSBaptiste Daroussin V4L2_CID_EXPOSURE_AUTO,
408d0450cbeSBaptiste Daroussin V4L2_CTRL_TYPE_MENU,
409d0450cbeSBaptiste Daroussin "Exposure, Auto",
410d0450cbeSBaptiste Daroussin 1,
411d0450cbeSBaptiste Daroussin CT_AE_MODE_CONTROL,
412d0450cbeSBaptiste Daroussin 1,
413d0450cbeSBaptiste Daroussin 0
414d0450cbeSBaptiste Daroussin },
415d0450cbeSBaptiste Daroussin {
416d0450cbeSBaptiste Daroussin V4L2_CID_EXPOSURE_AUTO_PRIORITY,
417d0450cbeSBaptiste Daroussin V4L2_CTRL_TYPE_BOOLEAN,
418d0450cbeSBaptiste Daroussin "Exposure, Auto Priority",
419d0450cbeSBaptiste Daroussin 2,
420d0450cbeSBaptiste Daroussin CT_AE_PRIORITY_CONTROL,
421d0450cbeSBaptiste Daroussin 1,
422d0450cbeSBaptiste Daroussin 0
423d0450cbeSBaptiste Daroussin },
424d0450cbeSBaptiste Daroussin {
425d0450cbeSBaptiste Daroussin V4L2_CID_EXPOSURE_ABSOLUTE,
426d0450cbeSBaptiste Daroussin V4L2_CTRL_TYPE_INTEGER,
427d0450cbeSBaptiste Daroussin "Exposure (Absolute)",
428d0450cbeSBaptiste Daroussin 3,
429d0450cbeSBaptiste Daroussin CT_EXPOSURE_TIME_ABSOLUTE_CONTROL,
430d0450cbeSBaptiste Daroussin 4,
431d0450cbeSBaptiste Daroussin 0
432d0450cbeSBaptiste Daroussin },
433d0450cbeSBaptiste Daroussin {
434d0450cbeSBaptiste Daroussin V4L2_CID_FOCUS_ABSOLUTE,
435d0450cbeSBaptiste Daroussin V4L2_CTRL_TYPE_INTEGER,
436d0450cbeSBaptiste Daroussin "Focus (Absolute)",
437d0450cbeSBaptiste Daroussin 5,
438d0450cbeSBaptiste Daroussin CT_FOCUS_ABSOLUTE_CONTROL,
439d0450cbeSBaptiste Daroussin 2,
440d0450cbeSBaptiste Daroussin 0
441d0450cbeSBaptiste Daroussin },
442d0450cbeSBaptiste Daroussin {
443d0450cbeSBaptiste Daroussin V4L2_CID_FOCUS_AUTO,
444d0450cbeSBaptiste Daroussin V4L2_CTRL_TYPE_BOOLEAN,
445d0450cbeSBaptiste Daroussin "Focus, Auto",
446d0450cbeSBaptiste Daroussin 17,
447d0450cbeSBaptiste Daroussin CT_FOCUS_AUTO_CONTROL,
448d0450cbeSBaptiste Daroussin 1,
449d0450cbeSBaptiste Daroussin 0
450d0450cbeSBaptiste Daroussin },
451d0450cbeSBaptiste Daroussin {
452d0450cbeSBaptiste Daroussin V4L2_CID_ZOOM_ABSOLUTE,
453d0450cbeSBaptiste Daroussin V4L2_CTRL_TYPE_INTEGER,
454d0450cbeSBaptiste Daroussin "Zoom (Absolute)",
455d0450cbeSBaptiste Daroussin 9,
456d0450cbeSBaptiste Daroussin CT_ZOOM_ABSOLUTE_CONTROL,
457d0450cbeSBaptiste Daroussin 2,
458d0450cbeSBaptiste Daroussin 0
459d0450cbeSBaptiste Daroussin },
460d0450cbeSBaptiste Daroussin {
461d0450cbeSBaptiste Daroussin V4L2_CID_PAN_ABSOLUTE,
462d0450cbeSBaptiste Daroussin V4L2_CTRL_TYPE_INTEGER,
463d0450cbeSBaptiste Daroussin "Pan (Absolute)",
464d0450cbeSBaptiste Daroussin 11,
465d0450cbeSBaptiste Daroussin CT_PANTILT_ABSOLUTE_CONTROL,
466d0450cbeSBaptiste Daroussin 4,
467d0450cbeSBaptiste Daroussin 1
468d0450cbeSBaptiste Daroussin },
469d0450cbeSBaptiste Daroussin {
470d0450cbeSBaptiste Daroussin V4L2_CID_TILT_ABSOLUTE,
471d0450cbeSBaptiste Daroussin V4L2_CTRL_TYPE_INTEGER,
472d0450cbeSBaptiste Daroussin "Tilt (Absolute)",
473d0450cbeSBaptiste Daroussin 11,
474d0450cbeSBaptiste Daroussin CT_PANTILT_ABSOLUTE_CONTROL,
475d0450cbeSBaptiste Daroussin 4,
476d0450cbeSBaptiste Daroussin 1
477d0450cbeSBaptiste Daroussin },
478d0450cbeSBaptiste Daroussin {
479d0450cbeSBaptiste Daroussin V4L2_CID_PRIVACY,
480d0450cbeSBaptiste Daroussin V4L2_CTRL_TYPE_BOOLEAN,
481d0450cbeSBaptiste Daroussin "Privacy",
482d0450cbeSBaptiste Daroussin 18,
483d0450cbeSBaptiste Daroussin CT_PRIVACY_CONTROL,
484d0450cbeSBaptiste Daroussin 1,
485d0450cbeSBaptiste Daroussin 0
486d0450cbeSBaptiste Daroussin },
487b711ef9cSBaptiste Daroussin { 0, 0, "", 0, 0, 0, 0 }
488b711ef9cSBaptiste Daroussin };
489b711ef9cSBaptiste Daroussin
490b711ef9cSBaptiste Daroussin /*
491b711ef9cSBaptiste Daroussin * Format GUID to V4L2 pixel format mapping
492b711ef9cSBaptiste Daroussin */
493b711ef9cSBaptiste Daroussin static const struct {
494b711ef9cSBaptiste Daroussin uint8_t guidFormat[16];
495b711ef9cSBaptiste Daroussin uint32_t pixelformat;
496b711ef9cSBaptiste Daroussin } uvideo_map_fmts[] = {
497b711ef9cSBaptiste Daroussin { UVIDEO_FORMAT_GUID_YUY2, V4L2_PIX_FMT_YUYV },
4988bc06ffbSBaptiste Daroussin { UVIDEO_FORMAT_GUID_NV12, V4L2_PIX_FMT_NV12 },
4998bc06ffbSBaptiste Daroussin { UVIDEO_FORMAT_GUID_NV21, V4L2_PIX_FMT_NV21 },
500b711ef9cSBaptiste Daroussin { UVIDEO_FORMAT_GUID_YV12, V4L2_PIX_FMT_YVU420 },
501b711ef9cSBaptiste Daroussin { UVIDEO_FORMAT_GUID_I420, V4L2_PIX_FMT_YUV420 },
5028bc06ffbSBaptiste Daroussin { UVIDEO_FORMAT_GUID_M420, V4L2_PIX_FMT_M420 },
5038bc06ffbSBaptiste Daroussin { UVIDEO_FORMAT_GUID_UYVY, V4L2_PIX_FMT_UYVY },
504b711ef9cSBaptiste Daroussin { UVIDEO_FORMAT_GUID_Y800, V4L2_PIX_FMT_GREY },
505b711ef9cSBaptiste Daroussin { UVIDEO_FORMAT_GUID_Y8, V4L2_PIX_FMT_GREY },
506b711ef9cSBaptiste Daroussin { UVIDEO_FORMAT_GUID_D3DFMT_L8, V4L2_PIX_FMT_GREY },
507b711ef9cSBaptiste Daroussin { UVIDEO_FORMAT_GUID_KSMEDIA_L8_IR, V4L2_PIX_FMT_GREY },
5088bc06ffbSBaptiste Daroussin { UVIDEO_FORMAT_GUID_Y12, V4L2_PIX_FMT_Y12 },
5098bc06ffbSBaptiste Daroussin { UVIDEO_FORMAT_GUID_Y16, V4L2_PIX_FMT_Y16 },
510b711ef9cSBaptiste Daroussin { UVIDEO_FORMAT_GUID_BY8, V4L2_PIX_FMT_SBGGR8 },
511b711ef9cSBaptiste Daroussin { UVIDEO_FORMAT_GUID_BA81, V4L2_PIX_FMT_SBGGR8 },
512b711ef9cSBaptiste Daroussin { UVIDEO_FORMAT_GUID_GBRG, V4L2_PIX_FMT_SGBRG8 },
513b711ef9cSBaptiste Daroussin { UVIDEO_FORMAT_GUID_GRBG, V4L2_PIX_FMT_SGRBG8 },
514b711ef9cSBaptiste Daroussin { UVIDEO_FORMAT_GUID_RGGB, V4L2_PIX_FMT_SRGGB8 },
515b711ef9cSBaptiste Daroussin { UVIDEO_FORMAT_GUID_RGBP, V4L2_PIX_FMT_RGB565 },
516b711ef9cSBaptiste Daroussin { UVIDEO_FORMAT_GUID_D3DFMT_R5G6B5, V4L2_PIX_FMT_RGB565 },
517b711ef9cSBaptiste Daroussin { UVIDEO_FORMAT_GUID_BGR3, V4L2_PIX_FMT_BGR24 },
518b711ef9cSBaptiste Daroussin { UVIDEO_FORMAT_GUID_BGR4, V4L2_PIX_FMT_XBGR32 },
519b711ef9cSBaptiste Daroussin { UVIDEO_FORMAT_GUID_H265, V4L2_PIX_FMT_HEVC },
520b711ef9cSBaptiste Daroussin { UVIDEO_FORMAT_GUID_RW10, V4L2_PIX_FMT_SRGGB10P },
521b711ef9cSBaptiste Daroussin { UVIDEO_FORMAT_GUID_BG16, V4L2_PIX_FMT_SBGGR16 },
522b711ef9cSBaptiste Daroussin { UVIDEO_FORMAT_GUID_GB16, V4L2_PIX_FMT_SGBRG16 },
523b711ef9cSBaptiste Daroussin { UVIDEO_FORMAT_GUID_RG16, V4L2_PIX_FMT_SRGGB16 },
524b711ef9cSBaptiste Daroussin { UVIDEO_FORMAT_GUID_GR16, V4L2_PIX_FMT_SGRBG16 },
525b711ef9cSBaptiste Daroussin { UVIDEO_FORMAT_GUID_INVZ, V4L2_PIX_FMT_Z16 },
526b711ef9cSBaptiste Daroussin { UVIDEO_FORMAT_GUID_INVI, V4L2_PIX_FMT_Y10 },
527b711ef9cSBaptiste Daroussin };
528b711ef9cSBaptiste Daroussin
529b711ef9cSBaptiste Daroussin /*
530b711ef9cSBaptiste Daroussin * Color matching tables from UVC spec
531b711ef9cSBaptiste Daroussin */
532b711ef9cSBaptiste Daroussin static const enum v4l2_colorspace uvideo_color_primaries[] = {
533b711ef9cSBaptiste Daroussin V4L2_COLORSPACE_SRGB, /* Unspecified */
534b711ef9cSBaptiste Daroussin V4L2_COLORSPACE_SRGB,
535b711ef9cSBaptiste Daroussin V4L2_COLORSPACE_470_SYSTEM_M,
536b711ef9cSBaptiste Daroussin V4L2_COLORSPACE_470_SYSTEM_BG,
537b711ef9cSBaptiste Daroussin V4L2_COLORSPACE_SMPTE170M,
538b711ef9cSBaptiste Daroussin V4L2_COLORSPACE_SMPTE240M,
539b711ef9cSBaptiste Daroussin };
540b711ef9cSBaptiste Daroussin
541b711ef9cSBaptiste Daroussin static const enum v4l2_xfer_func uvideo_xfer_characteristics[] = {
542b711ef9cSBaptiste Daroussin V4L2_XFER_FUNC_DEFAULT, /* Unspecified */
543b711ef9cSBaptiste Daroussin V4L2_XFER_FUNC_709,
544b711ef9cSBaptiste Daroussin V4L2_XFER_FUNC_709, /* Substitution for BT.470-2 M */
545b711ef9cSBaptiste Daroussin V4L2_XFER_FUNC_709, /* Substitution for BT.470-2 B, G */
546b711ef9cSBaptiste Daroussin V4L2_XFER_FUNC_709, /* Substitution for SMPTE 170M */
547b711ef9cSBaptiste Daroussin V4L2_XFER_FUNC_SMPTE240M,
548b711ef9cSBaptiste Daroussin V4L2_XFER_FUNC_NONE,
549b711ef9cSBaptiste Daroussin V4L2_XFER_FUNC_SRGB,
550b711ef9cSBaptiste Daroussin };
551b711ef9cSBaptiste Daroussin
552b711ef9cSBaptiste Daroussin static const enum v4l2_ycbcr_encoding uvideo_matrix_coefficients[] = {
553b711ef9cSBaptiste Daroussin V4L2_YCBCR_ENC_DEFAULT, /* Unspecified */
554b711ef9cSBaptiste Daroussin V4L2_YCBCR_ENC_709,
555b711ef9cSBaptiste Daroussin V4L2_YCBCR_ENC_601, /* Substitution for FCC */
556b711ef9cSBaptiste Daroussin V4L2_YCBCR_ENC_601, /* Substitution for BT.470-2 B, G */
557b711ef9cSBaptiste Daroussin V4L2_YCBCR_ENC_601,
558b711ef9cSBaptiste Daroussin V4L2_YCBCR_ENC_SMPTE240M,
559b711ef9cSBaptiste Daroussin };
560b711ef9cSBaptiste Daroussin
561b711ef9cSBaptiste Daroussin /*
562b711ef9cSBaptiste Daroussin * USB device ID table - match standard UVC devices
563b711ef9cSBaptiste Daroussin */
564b711ef9cSBaptiste Daroussin static const STRUCT_USB_HOST_ID uvideo_devs[] = {
565b711ef9cSBaptiste Daroussin {USB_IFACE_CLASS(UICLASS_VIDEO),
566b711ef9cSBaptiste Daroussin USB_IFACE_SUBCLASS(UISUBCLASS_VIDEOCONTROL),},
567b711ef9cSBaptiste Daroussin };
568b711ef9cSBaptiste Daroussin
569b711ef9cSBaptiste Daroussin /*
570b711ef9cSBaptiste Daroussin * Device methods
571b711ef9cSBaptiste Daroussin */
572b711ef9cSBaptiste Daroussin static device_method_t uvideo_methods[] = {
573b711ef9cSBaptiste Daroussin DEVMETHOD(device_probe, uvideo_probe),
574b711ef9cSBaptiste Daroussin DEVMETHOD(device_attach, uvideo_attach),
575b711ef9cSBaptiste Daroussin DEVMETHOD(device_detach, uvideo_detach),
576b711ef9cSBaptiste Daroussin DEVMETHOD_END
577b711ef9cSBaptiste Daroussin };
578b711ef9cSBaptiste Daroussin
579b711ef9cSBaptiste Daroussin static driver_t uvideo_driver = {
580b711ef9cSBaptiste Daroussin .name = "uvideo",
581b711ef9cSBaptiste Daroussin .methods = uvideo_methods,
582b711ef9cSBaptiste Daroussin .size = sizeof(struct uvideo_softc),
583b711ef9cSBaptiste Daroussin };
584b711ef9cSBaptiste Daroussin
585b711ef9cSBaptiste Daroussin DRIVER_MODULE(uvideo, uhub, uvideo_driver, NULL, NULL);
586b711ef9cSBaptiste Daroussin MODULE_DEPEND(uvideo, usb, 1, 1, 1);
587b711ef9cSBaptiste Daroussin MODULE_VERSION(uvideo, 1);
588b711ef9cSBaptiste Daroussin USB_PNP_HOST_INFO(uvideo_devs);
589b711ef9cSBaptiste Daroussin
590b711ef9cSBaptiste Daroussin /*
591b711ef9cSBaptiste Daroussin * Transfer configuration: triple-buffered isochronous + single bulk
592b711ef9cSBaptiste Daroussin */
593b711ef9cSBaptiste Daroussin static const struct usb_config uvideo_isoc_config[UVIDEO_IXFERS] = {
594b711ef9cSBaptiste Daroussin [0] = {
595b711ef9cSBaptiste Daroussin .type = UE_ISOCHRONOUS,
596b711ef9cSBaptiste Daroussin .endpoint = UE_ADDR_ANY,
597b711ef9cSBaptiste Daroussin .direction = UE_DIR_IN,
598b711ef9cSBaptiste Daroussin .bufsize = 0, /* use wMaxPacketSize * frames */
599b711ef9cSBaptiste Daroussin .frames = UVIDEO_NFRAMES_MAX,
600b711ef9cSBaptiste Daroussin .flags = {.short_xfer_ok = 1, .short_frames_ok = 1,},
601b711ef9cSBaptiste Daroussin .callback = &uvideo_isoc_callback,
602b711ef9cSBaptiste Daroussin },
603b711ef9cSBaptiste Daroussin [1] = {
604b711ef9cSBaptiste Daroussin .type = UE_ISOCHRONOUS,
605b711ef9cSBaptiste Daroussin .endpoint = UE_ADDR_ANY,
606b711ef9cSBaptiste Daroussin .direction = UE_DIR_IN,
607b711ef9cSBaptiste Daroussin .bufsize = 0,
608b711ef9cSBaptiste Daroussin .frames = UVIDEO_NFRAMES_MAX,
609b711ef9cSBaptiste Daroussin .flags = {.short_xfer_ok = 1, .short_frames_ok = 1,},
610b711ef9cSBaptiste Daroussin .callback = &uvideo_isoc_callback,
611b711ef9cSBaptiste Daroussin },
612b711ef9cSBaptiste Daroussin [2] = {
613b711ef9cSBaptiste Daroussin .type = UE_ISOCHRONOUS,
614b711ef9cSBaptiste Daroussin .endpoint = UE_ADDR_ANY,
615b711ef9cSBaptiste Daroussin .direction = UE_DIR_IN,
616b711ef9cSBaptiste Daroussin .bufsize = 0,
617b711ef9cSBaptiste Daroussin .frames = UVIDEO_NFRAMES_MAX,
618b711ef9cSBaptiste Daroussin .flags = {.short_xfer_ok = 1, .short_frames_ok = 1,},
619b711ef9cSBaptiste Daroussin .callback = &uvideo_isoc_callback,
620b711ef9cSBaptiste Daroussin },
621*3b6f833cSBaptiste Daroussin [3] = {
622*3b6f833cSBaptiste Daroussin .type = UE_ISOCHRONOUS,
623*3b6f833cSBaptiste Daroussin .endpoint = UE_ADDR_ANY,
624*3b6f833cSBaptiste Daroussin .direction = UE_DIR_IN,
625*3b6f833cSBaptiste Daroussin .bufsize = 0,
626*3b6f833cSBaptiste Daroussin .frames = UVIDEO_NFRAMES_MAX,
627*3b6f833cSBaptiste Daroussin .flags = {.short_xfer_ok = 1, .short_frames_ok = 1,},
628*3b6f833cSBaptiste Daroussin .callback = &uvideo_isoc_callback,
629*3b6f833cSBaptiste Daroussin },
630*3b6f833cSBaptiste Daroussin [4] = {
631*3b6f833cSBaptiste Daroussin .type = UE_ISOCHRONOUS,
632*3b6f833cSBaptiste Daroussin .endpoint = UE_ADDR_ANY,
633*3b6f833cSBaptiste Daroussin .direction = UE_DIR_IN,
634*3b6f833cSBaptiste Daroussin .bufsize = 0,
635*3b6f833cSBaptiste Daroussin .frames = UVIDEO_NFRAMES_MAX,
636*3b6f833cSBaptiste Daroussin .flags = {.short_xfer_ok = 1, .short_frames_ok = 1,},
637*3b6f833cSBaptiste Daroussin .callback = &uvideo_isoc_callback,
638*3b6f833cSBaptiste Daroussin },
639b711ef9cSBaptiste Daroussin };
640b711ef9cSBaptiste Daroussin
641b711ef9cSBaptiste Daroussin static const struct usb_config uvideo_bulk_config[1] = {
642b711ef9cSBaptiste Daroussin [0] = {
643b711ef9cSBaptiste Daroussin .type = UE_BULK,
644b711ef9cSBaptiste Daroussin .endpoint = UE_ADDR_ANY,
645b711ef9cSBaptiste Daroussin .direction = UE_DIR_IN,
646b711ef9cSBaptiste Daroussin .bufsize = 65536,
647b711ef9cSBaptiste Daroussin .flags = {.short_xfer_ok = 1, .pipe_bof = 1,},
648b711ef9cSBaptiste Daroussin .callback = &uvideo_bulk_callback,
649b711ef9cSBaptiste Daroussin },
650b711ef9cSBaptiste Daroussin };
651b711ef9cSBaptiste Daroussin
652b711ef9cSBaptiste Daroussin /*
653b711ef9cSBaptiste Daroussin * Character device switch
654b711ef9cSBaptiste Daroussin */
655b711ef9cSBaptiste Daroussin static struct cdevsw uvideo_cdevsw = {
656b711ef9cSBaptiste Daroussin .d_version = D_VERSION,
657b711ef9cSBaptiste Daroussin .d_open = uvideo_cdev_open,
658b711ef9cSBaptiste Daroussin .d_close = uvideo_cdev_close,
659b711ef9cSBaptiste Daroussin .d_read = uvideo_cdev_read,
660b711ef9cSBaptiste Daroussin .d_ioctl = uvideo_cdev_ioctl,
661b711ef9cSBaptiste Daroussin .d_poll = uvideo_cdev_poll,
66254df18cbSBaptiste Daroussin .d_kqfilter = uvideo_cdev_kqfilter,
663b711ef9cSBaptiste Daroussin .d_mmap = uvideo_cdev_mmap,
664b711ef9cSBaptiste Daroussin .d_name = "video",
665b711ef9cSBaptiste Daroussin };
666b711ef9cSBaptiste Daroussin
667b711ef9cSBaptiste Daroussin /*
668b711ef9cSBaptiste Daroussin * Unit number allocator
669b711ef9cSBaptiste Daroussin */
670b711ef9cSBaptiste Daroussin /* Unit number allocation is handled by scanning for free /dev/videoN names */
671b711ef9cSBaptiste Daroussin
672b711ef9cSBaptiste Daroussin /* ---------------------------------------------------------------- */
673b711ef9cSBaptiste Daroussin /* Probe / Attach / Detach */
674b711ef9cSBaptiste Daroussin /* ---------------------------------------------------------------- */
675b711ef9cSBaptiste Daroussin
676b711ef9cSBaptiste Daroussin static int
uvideo_probe(device_t dev)677b711ef9cSBaptiste Daroussin uvideo_probe(device_t dev)
678b711ef9cSBaptiste Daroussin {
679b711ef9cSBaptiste Daroussin struct usb_attach_arg *uaa = device_get_ivars(dev);
680b711ef9cSBaptiste Daroussin
681b711ef9cSBaptiste Daroussin if (uaa->usb_mode != USB_MODE_HOST)
682b711ef9cSBaptiste Daroussin return (ENXIO);
683b711ef9cSBaptiste Daroussin
684b711ef9cSBaptiste Daroussin if (uaa->info.bInterfaceClass != UICLASS_VIDEO)
685b711ef9cSBaptiste Daroussin return (ENXIO);
686b711ef9cSBaptiste Daroussin
687b711ef9cSBaptiste Daroussin if (uaa->info.bInterfaceSubClass != UISUBCLASS_VIDEOCONTROL)
688b711ef9cSBaptiste Daroussin return (ENXIO);
689b711ef9cSBaptiste Daroussin
690b711ef9cSBaptiste Daroussin return (usbd_lookup_id_by_uaa(uvideo_devs, sizeof(uvideo_devs), uaa));
691b711ef9cSBaptiste Daroussin }
692b711ef9cSBaptiste Daroussin
693b711ef9cSBaptiste Daroussin static int
uvideo_attach(device_t dev)694b711ef9cSBaptiste Daroussin uvideo_attach(device_t dev)
695b711ef9cSBaptiste Daroussin {
696b711ef9cSBaptiste Daroussin struct uvideo_softc *sc = device_get_softc(dev);
697b711ef9cSBaptiste Daroussin struct usb_attach_arg *uaa = device_get_ivars(dev);
698b711ef9cSBaptiste Daroussin struct usb_config_descriptor *cdesc;
699b711ef9cSBaptiste Daroussin struct usb_descriptor *desc;
700b711ef9cSBaptiste Daroussin struct usb_interface_assoc_descriptor *iad;
701b711ef9cSBaptiste Daroussin struct make_dev_args args;
702b711ef9cSBaptiste Daroussin usb_error_t error;
703b711ef9cSBaptiste Daroussin int first_iface, nifaces;
704b711ef9cSBaptiste Daroussin int i;
705b711ef9cSBaptiste Daroussin
706b711ef9cSBaptiste Daroussin sc->sc_dev = dev;
707b711ef9cSBaptiste Daroussin sc->sc_udev = uaa->device;
708b711ef9cSBaptiste Daroussin sc->sc_iface_index = uaa->info.bIfaceIndex;
709b711ef9cSBaptiste Daroussin
710b711ef9cSBaptiste Daroussin device_set_usb_desc(dev);
711b711ef9cSBaptiste Daroussin mtx_init(&sc->sc_mtx, "uvideo", NULL, MTX_DEF);
712b711ef9cSBaptiste Daroussin knlist_init_mtx(&sc->sc_selinfo.si_note, &sc->sc_mtx);
713b711ef9cSBaptiste Daroussin
714b711ef9cSBaptiste Daroussin /* Get the config descriptor to iterate */
715b711ef9cSBaptiste Daroussin cdesc = usbd_get_config_descriptor(sc->sc_udev);
716b711ef9cSBaptiste Daroussin if (cdesc == NULL) {
717b711ef9cSBaptiste Daroussin device_printf(dev, "failed to get config descriptor\n");
718b711ef9cSBaptiste Daroussin goto detach;
719b711ef9cSBaptiste Daroussin }
720b711ef9cSBaptiste Daroussin
721b711ef9cSBaptiste Daroussin /*
722b711ef9cSBaptiste Daroussin * Find the Interface Association Descriptor (IAD) that groups
723b711ef9cSBaptiste Daroussin * the video control and video streaming interfaces.
724b711ef9cSBaptiste Daroussin */
725b711ef9cSBaptiste Daroussin iad = NULL;
726b711ef9cSBaptiste Daroussin desc = NULL;
727b711ef9cSBaptiste Daroussin while ((desc = usb_desc_foreach(cdesc, desc)) != NULL) {
728b711ef9cSBaptiste Daroussin if (desc->bDescriptorType != UDESC_IFACE_ASSOC)
729b711ef9cSBaptiste Daroussin continue;
730b711ef9cSBaptiste Daroussin iad = (struct usb_interface_assoc_descriptor *)desc;
731b711ef9cSBaptiste Daroussin if (uaa->info.bIfaceIndex >= iad->bFirstInterface &&
732b711ef9cSBaptiste Daroussin uaa->info.bIfaceIndex <
733b711ef9cSBaptiste Daroussin iad->bFirstInterface + iad->bInterfaceCount)
734b711ef9cSBaptiste Daroussin break;
735b711ef9cSBaptiste Daroussin iad = NULL;
736b711ef9cSBaptiste Daroussin }
737b711ef9cSBaptiste Daroussin if (iad == NULL) {
738b711ef9cSBaptiste Daroussin device_printf(dev, "can't find interface association\n");
739b711ef9cSBaptiste Daroussin goto detach;
740b711ef9cSBaptiste Daroussin }
741b711ef9cSBaptiste Daroussin
742b711ef9cSBaptiste Daroussin first_iface = iad->bFirstInterface;
743b711ef9cSBaptiste Daroussin nifaces = iad->bInterfaceCount;
744b711ef9cSBaptiste Daroussin
745b711ef9cSBaptiste Daroussin /* Claim all interfaces in this association */
746b711ef9cSBaptiste Daroussin for (i = first_iface; i < first_iface + nifaces; i++) {
747b711ef9cSBaptiste Daroussin if (i == uaa->info.bIfaceIndex)
748b711ef9cSBaptiste Daroussin continue;
749b711ef9cSBaptiste Daroussin usbd_set_parent_iface(sc->sc_udev, i, uaa->info.bIfaceIndex);
750b711ef9cSBaptiste Daroussin }
751b711ef9cSBaptiste Daroussin
752b711ef9cSBaptiste Daroussin sc->sc_iface_index = first_iface;
753b711ef9cSBaptiste Daroussin sc->sc_nifaces = nifaces;
754b711ef9cSBaptiste Daroussin
755b711ef9cSBaptiste Daroussin /* Standard UVC stream header decode */
756b711ef9cSBaptiste Daroussin sc->sc_decode_stream_header = uvideo_vs_decode_stream_header;
757b711ef9cSBaptiste Daroussin
758b711ef9cSBaptiste Daroussin /* Parse video control descriptors */
759b711ef9cSBaptiste Daroussin error = uvideo_vc_parse_desc(sc);
760b711ef9cSBaptiste Daroussin if (error != USB_ERR_NORMAL_COMPLETION) {
761b711ef9cSBaptiste Daroussin device_printf(dev, "failed to parse VC descriptors\n");
762b711ef9cSBaptiste Daroussin goto detach;
763b711ef9cSBaptiste Daroussin }
764b711ef9cSBaptiste Daroussin
765b711ef9cSBaptiste Daroussin /* Parse video stream descriptors */
766b711ef9cSBaptiste Daroussin error = uvideo_vs_parse_desc(sc, cdesc);
767b711ef9cSBaptiste Daroussin if (error != USB_ERR_NORMAL_COMPLETION) {
768b711ef9cSBaptiste Daroussin device_printf(dev, "failed to parse VS descriptors\n");
769b711ef9cSBaptiste Daroussin goto detach;
770b711ef9cSBaptiste Daroussin }
771b711ef9cSBaptiste Daroussin
772b711ef9cSBaptiste Daroussin /* Set default video stream interface to alt 0 */
773b711ef9cSBaptiste Daroussin if (sc->sc_vs_cur != NULL) {
774b711ef9cSBaptiste Daroussin error = usbd_set_alt_interface_index(sc->sc_udev,
775b711ef9cSBaptiste Daroussin sc->sc_vs_cur->iface_index, 0);
776b711ef9cSBaptiste Daroussin if (error != USB_ERR_NORMAL_COMPLETION) {
777b711ef9cSBaptiste Daroussin device_printf(dev,
778b711ef9cSBaptiste Daroussin "failed to set default alt interface\n");
779b711ef9cSBaptiste Daroussin goto detach;
780b711ef9cSBaptiste Daroussin }
781b711ef9cSBaptiste Daroussin }
782b711ef9cSBaptiste Daroussin
783b711ef9cSBaptiste Daroussin /* Do device negotiation without commit */
784b711ef9cSBaptiste Daroussin error = uvideo_vs_negotiation(sc, 0);
785b711ef9cSBaptiste Daroussin if (error != USB_ERR_NORMAL_COMPLETION) {
786b711ef9cSBaptiste Daroussin device_printf(dev, "initial negotiation failed\n");
787b711ef9cSBaptiste Daroussin goto detach;
788b711ef9cSBaptiste Daroussin }
789b711ef9cSBaptiste Daroussin
790b711ef9cSBaptiste Daroussin /* Report what we found */
791b711ef9cSBaptiste Daroussin if (sc->sc_vs_cur != NULL) {
792b711ef9cSBaptiste Daroussin device_printf(dev, "%d format(s), iface_index=%d, "
793b711ef9cSBaptiste Daroussin "endpoint=0x%02x, psize=%u, %s\n",
794b711ef9cSBaptiste Daroussin sc->sc_fmtgrp_num,
795b711ef9cSBaptiste Daroussin sc->sc_vs_cur->iface_index,
796b711ef9cSBaptiste Daroussin sc->sc_vs_cur->endpoint,
797b711ef9cSBaptiste Daroussin sc->sc_vs_cur->psize,
798b711ef9cSBaptiste Daroussin sc->sc_vs_cur->bulk_endpoint ? "bulk" : "isoc");
799b711ef9cSBaptiste Daroussin if (sc->sc_fmtgrp_cur != NULL) {
800b711ef9cSBaptiste Daroussin struct usb_video_frame_desc *fr =
801b711ef9cSBaptiste Daroussin sc->sc_fmtgrp_cur->frame_cur;
802b711ef9cSBaptiste Daroussin device_printf(dev, "default format: pixfmt=0x%08x, "
803b711ef9cSBaptiste Daroussin "%dx%d, max_fbuf=%d\n",
804b711ef9cSBaptiste Daroussin sc->sc_fmtgrp_cur->pixelformat,
805b711ef9cSBaptiste Daroussin fr ? UGETW(UVIDEO_FRAME_FIELD(fr, wWidth)) : 0,
806b711ef9cSBaptiste Daroussin fr ? UGETW(UVIDEO_FRAME_FIELD(fr, wHeight)) : 0,
807b711ef9cSBaptiste Daroussin sc->sc_max_fbuf_size);
808b711ef9cSBaptiste Daroussin }
809b711ef9cSBaptiste Daroussin }
810b711ef9cSBaptiste Daroussin
811b711ef9cSBaptiste Daroussin /* Init mmap queue */
812b711ef9cSBaptiste Daroussin STAILQ_INIT(&sc->sc_mmap_q);
813b711ef9cSBaptiste Daroussin sc->sc_mmap_count = 0;
814b711ef9cSBaptiste Daroussin
815b711ef9cSBaptiste Daroussin /* Allocate unit number and create character device */
816b711ef9cSBaptiste Daroussin make_dev_args_init(&args);
817b711ef9cSBaptiste Daroussin args.mda_devsw = &uvideo_cdevsw;
818b711ef9cSBaptiste Daroussin args.mda_uid = UID_ROOT;
819b711ef9cSBaptiste Daroussin args.mda_gid = GID_VIDEO;
820b711ef9cSBaptiste Daroussin args.mda_mode = 0660;
821b711ef9cSBaptiste Daroussin args.mda_si_drv1 = sc;
822b711ef9cSBaptiste Daroussin args.mda_flags = MAKEDEV_CHECKNAME;
823b711ef9cSBaptiste Daroussin
824b711ef9cSBaptiste Daroussin sc->sc_unit = -1;
825b711ef9cSBaptiste Daroussin for (i = 0; i < 256; i++) {
826b711ef9cSBaptiste Daroussin if (make_dev_s(&args, &sc->sc_cdev, "video%d", i) == 0) {
827b711ef9cSBaptiste Daroussin sc->sc_unit = i;
828b711ef9cSBaptiste Daroussin break;
829b711ef9cSBaptiste Daroussin }
830b711ef9cSBaptiste Daroussin }
831b711ef9cSBaptiste Daroussin if (sc->sc_unit < 0) {
832b711ef9cSBaptiste Daroussin device_printf(dev, "failed to create /dev/video device\n");
833b711ef9cSBaptiste Daroussin goto detach;
834b711ef9cSBaptiste Daroussin }
835b711ef9cSBaptiste Daroussin
836b711ef9cSBaptiste Daroussin device_printf(dev, "UVC camera on /dev/video%d\n", sc->sc_unit);
837b711ef9cSBaptiste Daroussin
838b711ef9cSBaptiste Daroussin return (0);
839b711ef9cSBaptiste Daroussin
840b711ef9cSBaptiste Daroussin detach:
841b711ef9cSBaptiste Daroussin uvideo_detach(dev);
842b711ef9cSBaptiste Daroussin return (ENXIO);
843b711ef9cSBaptiste Daroussin }
844b711ef9cSBaptiste Daroussin
845b711ef9cSBaptiste Daroussin static int
uvideo_detach(device_t dev)846b711ef9cSBaptiste Daroussin uvideo_detach(device_t dev)
847b711ef9cSBaptiste Daroussin {
848b711ef9cSBaptiste Daroussin struct uvideo_softc *sc = device_get_softc(dev);
849b711ef9cSBaptiste Daroussin
850b711ef9cSBaptiste Daroussin sc->sc_dying = 1;
851b711ef9cSBaptiste Daroussin
852b711ef9cSBaptiste Daroussin /* Stop any active streaming */
853b711ef9cSBaptiste Daroussin if (sc->sc_streaming) {
854b711ef9cSBaptiste Daroussin mtx_lock(&sc->sc_mtx);
855b711ef9cSBaptiste Daroussin sc->sc_streaming = 0;
856b711ef9cSBaptiste Daroussin mtx_unlock(&sc->sc_mtx);
857b711ef9cSBaptiste Daroussin uvideo_vs_close(sc);
858b711ef9cSBaptiste Daroussin }
859b711ef9cSBaptiste Daroussin
860b711ef9cSBaptiste Daroussin /* Destroy character device */
861b711ef9cSBaptiste Daroussin if (sc->sc_cdev != NULL) {
862b711ef9cSBaptiste Daroussin destroy_dev(sc->sc_cdev);
863b711ef9cSBaptiste Daroussin sc->sc_cdev = NULL;
864b711ef9cSBaptiste Daroussin }
865b711ef9cSBaptiste Daroussin
866b711ef9cSBaptiste Daroussin /* Unit number is implicitly freed when the cdev is destroyed */
867b711ef9cSBaptiste Daroussin
868b711ef9cSBaptiste Daroussin /* Free frame buffers */
869b711ef9cSBaptiste Daroussin uvideo_vs_free_frame(sc);
870b711ef9cSBaptiste Daroussin
871b711ef9cSBaptiste Daroussin /* Unsetup USB transfers */
872b711ef9cSBaptiste Daroussin usbd_transfer_unsetup(sc->sc_xfer, UVIDEO_N_XFER);
873b711ef9cSBaptiste Daroussin
874b711ef9cSBaptiste Daroussin seldrain(&sc->sc_selinfo);
875b711ef9cSBaptiste Daroussin knlist_destroy(&sc->sc_selinfo.si_note);
876b711ef9cSBaptiste Daroussin mtx_destroy(&sc->sc_mtx);
877b711ef9cSBaptiste Daroussin
878b711ef9cSBaptiste Daroussin return (0);
879b711ef9cSBaptiste Daroussin }
880b711ef9cSBaptiste Daroussin
881b711ef9cSBaptiste Daroussin /* ---------------------------------------------------------------- */
882b711ef9cSBaptiste Daroussin /* Descriptor Parsing */
883b711ef9cSBaptiste Daroussin /* ---------------------------------------------------------------- */
884b711ef9cSBaptiste Daroussin
885b711ef9cSBaptiste Daroussin static usb_error_t
uvideo_vc_parse_desc(struct uvideo_softc * sc)886b711ef9cSBaptiste Daroussin uvideo_vc_parse_desc(struct uvideo_softc *sc)
887b711ef9cSBaptiste Daroussin {
888b711ef9cSBaptiste Daroussin struct usb_config_descriptor *cdesc;
889b711ef9cSBaptiste Daroussin struct usb_descriptor *desc;
890b711ef9cSBaptiste Daroussin struct usb_interface_descriptor *id;
891b711ef9cSBaptiste Daroussin int vc_header_found;
892b711ef9cSBaptiste Daroussin usb_error_t error;
893b711ef9cSBaptiste Daroussin int past_our_iface;
894b711ef9cSBaptiste Daroussin
895b711ef9cSBaptiste Daroussin DPRINTFN(1, "uvideo_vc_parse_desc\n");
896b711ef9cSBaptiste Daroussin
897b711ef9cSBaptiste Daroussin vc_header_found = 0;
898b711ef9cSBaptiste Daroussin past_our_iface = 0;
899b711ef9cSBaptiste Daroussin
900b711ef9cSBaptiste Daroussin cdesc = usbd_get_config_descriptor(sc->sc_udev);
901b711ef9cSBaptiste Daroussin if (cdesc == NULL)
902b711ef9cSBaptiste Daroussin return (USB_ERR_INVAL);
903b711ef9cSBaptiste Daroussin
904b711ef9cSBaptiste Daroussin desc = NULL;
905b711ef9cSBaptiste Daroussin while ((desc = usb_desc_foreach(cdesc, desc)) != NULL) {
906b711ef9cSBaptiste Daroussin /* Look for our VC interface */
907b711ef9cSBaptiste Daroussin if (desc->bDescriptorType == UDESC_INTERFACE) {
908b711ef9cSBaptiste Daroussin id = (struct usb_interface_descriptor *)desc;
909b711ef9cSBaptiste Daroussin if (id->bInterfaceNumber == sc->sc_iface_index) {
910b711ef9cSBaptiste Daroussin past_our_iface = 1;
911b711ef9cSBaptiste Daroussin continue;
912b711ef9cSBaptiste Daroussin } else if (past_our_iface &&
913b711ef9cSBaptiste Daroussin id->bInterfaceNumber != sc->sc_iface_index) {
914b711ef9cSBaptiste Daroussin /*
915b711ef9cSBaptiste Daroussin * We have left our VC interface;
916b711ef9cSBaptiste Daroussin * stop if we hit a new IAD or unrelated iface.
917b711ef9cSBaptiste Daroussin */
918b711ef9cSBaptiste Daroussin }
919b711ef9cSBaptiste Daroussin }
920b711ef9cSBaptiste Daroussin if (desc->bDescriptorType == UDESC_IFACE_ASSOC &&
921b711ef9cSBaptiste Daroussin past_our_iface)
922b711ef9cSBaptiste Daroussin break;
923b711ef9cSBaptiste Daroussin
924b711ef9cSBaptiste Daroussin if (!past_our_iface)
925b711ef9cSBaptiste Daroussin continue;
926b711ef9cSBaptiste Daroussin
927b711ef9cSBaptiste Daroussin if (desc->bDescriptorType != UDESC_CS_INTERFACE)
928b711ef9cSBaptiste Daroussin continue;
929b711ef9cSBaptiste Daroussin
930b711ef9cSBaptiste Daroussin switch (desc->bDescriptorSubtype) {
931b711ef9cSBaptiste Daroussin case UDESCSUB_VC_HEADER:
932b711ef9cSBaptiste Daroussin if (!uvideo_desc_len(desc, 12, 11, 1, 0))
933b711ef9cSBaptiste Daroussin break;
934b711ef9cSBaptiste Daroussin if (vc_header_found) {
935b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev,
936b711ef9cSBaptiste Daroussin "too many VC_HEADERs!\n");
937b711ef9cSBaptiste Daroussin return (USB_ERR_INVAL);
938b711ef9cSBaptiste Daroussin }
939b711ef9cSBaptiste Daroussin error = uvideo_vc_parse_desc_header(sc, desc);
940b711ef9cSBaptiste Daroussin if (error != USB_ERR_NORMAL_COMPLETION)
941b711ef9cSBaptiste Daroussin return (error);
942b711ef9cSBaptiste Daroussin vc_header_found = 1;
943b711ef9cSBaptiste Daroussin break;
944d0450cbeSBaptiste Daroussin case UDESCSUB_VC_INPUT_TERMINAL:
945d0450cbeSBaptiste Daroussin {
946d0450cbeSBaptiste Daroussin struct usb_video_input_terminal_desc *itd;
947d0450cbeSBaptiste Daroussin itd = (struct usb_video_input_terminal_desc *)desc;
948d0450cbeSBaptiste Daroussin if (UGETW(itd->wTerminalType) == ITT_CAMERA)
949d0450cbeSBaptiste Daroussin (void)uvideo_vc_parse_desc_ct(sc, desc);
950d0450cbeSBaptiste Daroussin break;
951d0450cbeSBaptiste Daroussin }
952b711ef9cSBaptiste Daroussin case UDESCSUB_VC_PROCESSING_UNIT:
953b711ef9cSBaptiste Daroussin (void)uvideo_vc_parse_desc_pu(sc, desc);
954b711ef9cSBaptiste Daroussin break;
955b711ef9cSBaptiste Daroussin }
956b711ef9cSBaptiste Daroussin }
957b711ef9cSBaptiste Daroussin
958b711ef9cSBaptiste Daroussin if (vc_header_found == 0) {
959b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev, "no VC_HEADER found!\n");
960b711ef9cSBaptiste Daroussin return (USB_ERR_INVAL);
961b711ef9cSBaptiste Daroussin }
962b711ef9cSBaptiste Daroussin
963b711ef9cSBaptiste Daroussin return (USB_ERR_NORMAL_COMPLETION);
964b711ef9cSBaptiste Daroussin }
965b711ef9cSBaptiste Daroussin
966b711ef9cSBaptiste Daroussin static usb_error_t
uvideo_vc_parse_desc_header(struct uvideo_softc * sc,const struct usb_descriptor * desc)967b711ef9cSBaptiste Daroussin uvideo_vc_parse_desc_header(struct uvideo_softc *sc,
968b711ef9cSBaptiste Daroussin const struct usb_descriptor *desc)
969b711ef9cSBaptiste Daroussin {
970b711ef9cSBaptiste Daroussin struct usb_video_header_desc *d;
971b711ef9cSBaptiste Daroussin
972b711ef9cSBaptiste Daroussin d = __DECONST(struct usb_video_header_desc *, desc);
973b711ef9cSBaptiste Daroussin
974b711ef9cSBaptiste Daroussin if (d->bInCollection == 0) {
975b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev, "no VS interface found!\n");
976b711ef9cSBaptiste Daroussin return (USB_ERR_INVAL);
977b711ef9cSBaptiste Daroussin }
978b711ef9cSBaptiste Daroussin
979b711ef9cSBaptiste Daroussin sc->sc_desc_vc_header.fix = d;
980b711ef9cSBaptiste Daroussin sc->sc_desc_vc_header.baInterfaceNr = (uByte *)(d + 1);
981b711ef9cSBaptiste Daroussin if (UGETW(d->bcdUVC) < 0x0110)
982b711ef9cSBaptiste Daroussin sc->sc_max_ctrl_size = 26;
983b711ef9cSBaptiste Daroussin else if (UGETW(d->bcdUVC) < 0x0150)
984b711ef9cSBaptiste Daroussin sc->sc_max_ctrl_size = 34;
985b711ef9cSBaptiste Daroussin else
986b711ef9cSBaptiste Daroussin sc->sc_max_ctrl_size = 48;
987b711ef9cSBaptiste Daroussin
988b711ef9cSBaptiste Daroussin return (USB_ERR_NORMAL_COMPLETION);
989b711ef9cSBaptiste Daroussin }
990b711ef9cSBaptiste Daroussin
991b711ef9cSBaptiste Daroussin static usb_error_t
uvideo_vc_parse_desc_pu(struct uvideo_softc * sc,const struct usb_descriptor * desc)992b711ef9cSBaptiste Daroussin uvideo_vc_parse_desc_pu(struct uvideo_softc *sc,
993b711ef9cSBaptiste Daroussin const struct usb_descriptor *desc)
994b711ef9cSBaptiste Daroussin {
995b711ef9cSBaptiste Daroussin struct usb_video_vc_processing_desc *d;
996b711ef9cSBaptiste Daroussin
997b711ef9cSBaptiste Daroussin d = __DECONST(struct usb_video_vc_processing_desc *, desc);
998b711ef9cSBaptiste Daroussin
999b711ef9cSBaptiste Daroussin if (sc->sc_desc_vc_pu_num == UVIDEO_MAX_PU) {
1000b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev,
1001b711ef9cSBaptiste Daroussin "too many PU descriptors found!\n");
1002b711ef9cSBaptiste Daroussin return (USB_ERR_INVAL);
1003b711ef9cSBaptiste Daroussin }
1004b711ef9cSBaptiste Daroussin
1005b711ef9cSBaptiste Daroussin sc->sc_desc_vc_pu[sc->sc_desc_vc_pu_num] = d;
1006b711ef9cSBaptiste Daroussin sc->sc_desc_vc_pu_num++;
1007b711ef9cSBaptiste Daroussin
1008b711ef9cSBaptiste Daroussin return (USB_ERR_NORMAL_COMPLETION);
1009b711ef9cSBaptiste Daroussin }
1010b711ef9cSBaptiste Daroussin
1011b711ef9cSBaptiste Daroussin static usb_error_t
uvideo_vc_parse_desc_ct(struct uvideo_softc * sc,const struct usb_descriptor * desc)1012d0450cbeSBaptiste Daroussin uvideo_vc_parse_desc_ct(struct uvideo_softc *sc,
1013d0450cbeSBaptiste Daroussin const struct usb_descriptor *desc)
1014d0450cbeSBaptiste Daroussin {
1015d0450cbeSBaptiste Daroussin struct usb_video_camera_terminal_desc *d;
1016d0450cbeSBaptiste Daroussin
1017d0450cbeSBaptiste Daroussin d = __DECONST(struct usb_video_camera_terminal_desc *, desc);
1018d0450cbeSBaptiste Daroussin
1019d0450cbeSBaptiste Daroussin if (sc->sc_desc_vc_ct_num == UVIDEO_MAX_CT) {
1020d0450cbeSBaptiste Daroussin device_printf(sc->sc_dev, "too many CT descriptors\n");
1021d0450cbeSBaptiste Daroussin return (USB_ERR_INVAL);
1022d0450cbeSBaptiste Daroussin }
1023d0450cbeSBaptiste Daroussin
1024d0450cbeSBaptiste Daroussin sc->sc_desc_vc_ct[sc->sc_desc_vc_ct_num] = d;
1025d0450cbeSBaptiste Daroussin sc->sc_desc_vc_ct_num++;
1026d0450cbeSBaptiste Daroussin
1027d0450cbeSBaptiste Daroussin return (USB_ERR_NORMAL_COMPLETION);
1028d0450cbeSBaptiste Daroussin }
1029d0450cbeSBaptiste Daroussin
1030d0450cbeSBaptiste Daroussin static usb_error_t
uvideo_vc_get_ctrl(struct uvideo_softc * sc,uint8_t * ctrl_data,uint8_t request,uint8_t unitid,uint16_t ctrl_selector,uint16_t ctrl_len)1031b711ef9cSBaptiste Daroussin uvideo_vc_get_ctrl(struct uvideo_softc *sc, uint8_t *ctrl_data,
1032b711ef9cSBaptiste Daroussin uint8_t request, uint8_t unitid, uint16_t ctrl_selector, uint16_t ctrl_len)
1033b711ef9cSBaptiste Daroussin {
1034b711ef9cSBaptiste Daroussin struct usb_device_request req;
1035b711ef9cSBaptiste Daroussin usb_error_t error;
1036b711ef9cSBaptiste Daroussin
1037b711ef9cSBaptiste Daroussin req.bmRequestType = UVIDEO_GET_IF;
1038b711ef9cSBaptiste Daroussin req.bRequest = request;
1039b711ef9cSBaptiste Daroussin USETW(req.wValue, (ctrl_selector << 8));
1040b711ef9cSBaptiste Daroussin USETW(req.wIndex, (unitid << 8));
1041b711ef9cSBaptiste Daroussin USETW(req.wLength, ctrl_len);
1042b711ef9cSBaptiste Daroussin
1043b711ef9cSBaptiste Daroussin error = usbd_do_request(sc->sc_udev, NULL, &req, ctrl_data);
1044b711ef9cSBaptiste Daroussin if (error) {
1045b711ef9cSBaptiste Daroussin DPRINTFN(1, "could not GET ctrl: %s\n",
1046b711ef9cSBaptiste Daroussin usbd_errstr(error));
1047b711ef9cSBaptiste Daroussin return (USB_ERR_INVAL);
1048b711ef9cSBaptiste Daroussin }
1049b711ef9cSBaptiste Daroussin
1050b711ef9cSBaptiste Daroussin return (USB_ERR_NORMAL_COMPLETION);
1051b711ef9cSBaptiste Daroussin }
1052b711ef9cSBaptiste Daroussin
1053b711ef9cSBaptiste Daroussin static usb_error_t
uvideo_vc_set_ctrl(struct uvideo_softc * sc,uint8_t * ctrl_data,uint8_t request,uint8_t unitid,uint16_t ctrl_selector,uint16_t ctrl_len)1054b711ef9cSBaptiste Daroussin uvideo_vc_set_ctrl(struct uvideo_softc *sc, uint8_t *ctrl_data,
1055b711ef9cSBaptiste Daroussin uint8_t request, uint8_t unitid, uint16_t ctrl_selector, uint16_t ctrl_len)
1056b711ef9cSBaptiste Daroussin {
1057b711ef9cSBaptiste Daroussin struct usb_device_request req;
1058b711ef9cSBaptiste Daroussin usb_error_t error;
1059b711ef9cSBaptiste Daroussin
1060b711ef9cSBaptiste Daroussin req.bmRequestType = UVIDEO_SET_IF;
1061b711ef9cSBaptiste Daroussin req.bRequest = request;
1062b711ef9cSBaptiste Daroussin USETW(req.wValue, (ctrl_selector << 8));
1063b711ef9cSBaptiste Daroussin USETW(req.wIndex, (unitid << 8));
1064b711ef9cSBaptiste Daroussin USETW(req.wLength, ctrl_len);
1065b711ef9cSBaptiste Daroussin
1066b711ef9cSBaptiste Daroussin error = usbd_do_request(sc->sc_udev, NULL, &req, ctrl_data);
1067b711ef9cSBaptiste Daroussin if (error) {
1068b711ef9cSBaptiste Daroussin DPRINTFN(1, "could not SET ctrl: %s\n",
1069b711ef9cSBaptiste Daroussin usbd_errstr(error));
1070b711ef9cSBaptiste Daroussin return (USB_ERR_INVAL);
1071b711ef9cSBaptiste Daroussin }
1072b711ef9cSBaptiste Daroussin
1073b711ef9cSBaptiste Daroussin return (USB_ERR_NORMAL_COMPLETION);
1074b711ef9cSBaptiste Daroussin }
1075b711ef9cSBaptiste Daroussin
1076b711ef9cSBaptiste Daroussin static int
uvideo_find_ctrl(struct uvideo_softc * sc,int id)1077b711ef9cSBaptiste Daroussin uvideo_find_ctrl(struct uvideo_softc *sc, int id)
1078b711ef9cSBaptiste Daroussin {
1079b711ef9cSBaptiste Daroussin int i, j, found;
1080b711ef9cSBaptiste Daroussin
1081d0450cbeSBaptiste Daroussin if (sc->sc_desc_vc_pu_num == 0 && sc->sc_desc_vc_ct_num == 0) {
1082d0450cbeSBaptiste Daroussin DPRINTFN(1, "no PU or CT descriptors found!\n");
1083b711ef9cSBaptiste Daroussin return (EINVAL);
1084b711ef9cSBaptiste Daroussin }
1085b711ef9cSBaptiste Daroussin
1086b711ef9cSBaptiste Daroussin /* do we support this control? */
1087b711ef9cSBaptiste Daroussin for (found = 0, i = 0; uvideo_ctrls[i].cid != 0; i++) {
1088b711ef9cSBaptiste Daroussin if (id == uvideo_ctrls[i].cid) {
1089b711ef9cSBaptiste Daroussin found = 1;
1090b711ef9cSBaptiste Daroussin break;
1091b711ef9cSBaptiste Daroussin }
1092b711ef9cSBaptiste Daroussin }
1093b711ef9cSBaptiste Daroussin if (found == 0) {
1094b711ef9cSBaptiste Daroussin DPRINTFN(1, "control not supported by driver!\n");
1095b711ef9cSBaptiste Daroussin return (EINVAL);
1096b711ef9cSBaptiste Daroussin }
1097b711ef9cSBaptiste Daroussin
1098d0450cbeSBaptiste Daroussin /* does a PU support this control? */
1099d0450cbeSBaptiste Daroussin sc->sc_desc_vc_pu_cur = NULL;
1100d0450cbeSBaptiste Daroussin sc->sc_desc_vc_ct_cur = NULL;
1101b711ef9cSBaptiste Daroussin for (found = 0, j = 0; j < sc->sc_desc_vc_pu_num; j++) {
1102b711ef9cSBaptiste Daroussin if (uvideo_has_ctrl(sc->sc_desc_vc_pu[j],
1103b711ef9cSBaptiste Daroussin uvideo_ctrls[i].ctrl_bit) != 0) {
1104b711ef9cSBaptiste Daroussin found = 1;
1105d0450cbeSBaptiste Daroussin sc->sc_desc_vc_pu_cur = sc->sc_desc_vc_pu[j];
1106b711ef9cSBaptiste Daroussin break;
1107b711ef9cSBaptiste Daroussin }
1108b711ef9cSBaptiste Daroussin }
1109d0450cbeSBaptiste Daroussin
1110d0450cbeSBaptiste Daroussin /* does a CT support this control? */
1111d0450cbeSBaptiste Daroussin if (found == 0) {
1112d0450cbeSBaptiste Daroussin for (j = 0; j < sc->sc_desc_vc_ct_num; j++) {
1113d0450cbeSBaptiste Daroussin if (uvideo_has_ct_ctrl(sc->sc_desc_vc_ct[j],
1114d0450cbeSBaptiste Daroussin uvideo_ctrls[i].ctrl_bit) != 0) {
1115d0450cbeSBaptiste Daroussin found = 1;
1116d0450cbeSBaptiste Daroussin sc->sc_desc_vc_ct_cur = sc->sc_desc_vc_ct[j];
1117d0450cbeSBaptiste Daroussin break;
1118d0450cbeSBaptiste Daroussin }
1119d0450cbeSBaptiste Daroussin }
1120d0450cbeSBaptiste Daroussin }
1121d0450cbeSBaptiste Daroussin
1122b711ef9cSBaptiste Daroussin if (found == 0) {
1123b711ef9cSBaptiste Daroussin DPRINTFN(1, "control not supported by device!\n");
1124b711ef9cSBaptiste Daroussin return (EINVAL);
1125b711ef9cSBaptiste Daroussin }
1126b711ef9cSBaptiste Daroussin
1127b711ef9cSBaptiste Daroussin return (i);
1128b711ef9cSBaptiste Daroussin }
1129b711ef9cSBaptiste Daroussin
1130b711ef9cSBaptiste Daroussin static int
uvideo_has_ctrl(struct usb_video_vc_processing_desc * desc,int ctrl_bit)1131b711ef9cSBaptiste Daroussin uvideo_has_ctrl(struct usb_video_vc_processing_desc *desc, int ctrl_bit)
1132b711ef9cSBaptiste Daroussin {
1133b711ef9cSBaptiste Daroussin
1134b711ef9cSBaptiste Daroussin if (desc->bControlSize * 8 <= ctrl_bit)
1135b711ef9cSBaptiste Daroussin return (0);
1136b711ef9cSBaptiste Daroussin
1137b711ef9cSBaptiste Daroussin return (desc->bmControls[byteof(ctrl_bit)] & bitof(ctrl_bit));
1138b711ef9cSBaptiste Daroussin }
1139b711ef9cSBaptiste Daroussin
1140d0450cbeSBaptiste Daroussin static int
uvideo_has_ct_ctrl(struct usb_video_camera_terminal_desc * desc,int ctrl_bit)1141d0450cbeSBaptiste Daroussin uvideo_has_ct_ctrl(struct usb_video_camera_terminal_desc *desc, int ctrl_bit)
1142d0450cbeSBaptiste Daroussin {
1143d0450cbeSBaptiste Daroussin
1144d0450cbeSBaptiste Daroussin if (desc->bControlSize * 8 <= ctrl_bit)
1145d0450cbeSBaptiste Daroussin return (0);
1146d0450cbeSBaptiste Daroussin
1147d0450cbeSBaptiste Daroussin return (desc->bmControls[byteof(ctrl_bit)] & bitof(ctrl_bit));
1148d0450cbeSBaptiste Daroussin }
1149d0450cbeSBaptiste Daroussin
1150b711ef9cSBaptiste Daroussin static usb_error_t
uvideo_vs_parse_desc(struct uvideo_softc * sc,struct usb_config_descriptor * cdesc)1151b711ef9cSBaptiste Daroussin uvideo_vs_parse_desc(struct uvideo_softc *sc,
1152b711ef9cSBaptiste Daroussin struct usb_config_descriptor *cdesc)
1153b711ef9cSBaptiste Daroussin {
1154b711ef9cSBaptiste Daroussin struct usb_descriptor *desc;
1155b711ef9cSBaptiste Daroussin struct usb_interface_descriptor *id;
1156b711ef9cSBaptiste Daroussin struct usb_interface *iface;
1157b711ef9cSBaptiste Daroussin int i, iface_num, numalts;
1158b711ef9cSBaptiste Daroussin usb_error_t error;
1159b711ef9cSBaptiste Daroussin int past_our_iface;
1160b711ef9cSBaptiste Daroussin
1161b711ef9cSBaptiste Daroussin DPRINTFN(1, "number of total interfaces=%d\n", sc->sc_nifaces);
1162b711ef9cSBaptiste Daroussin DPRINTFN(1, "number of VS interfaces=%d\n",
1163b711ef9cSBaptiste Daroussin sc->sc_desc_vc_header.fix->bInCollection);
1164b711ef9cSBaptiste Daroussin
1165b711ef9cSBaptiste Daroussin /* First pass: find VS_INPUT_HEADER */
1166b711ef9cSBaptiste Daroussin past_our_iface = 0;
1167b711ef9cSBaptiste Daroussin desc = NULL;
1168b711ef9cSBaptiste Daroussin while ((desc = usb_desc_foreach(cdesc, desc)) != NULL) {
1169b711ef9cSBaptiste Daroussin if (desc->bDescriptorType == UDESC_INTERFACE) {
1170b711ef9cSBaptiste Daroussin id = (struct usb_interface_descriptor *)desc;
1171b711ef9cSBaptiste Daroussin if (id->bInterfaceNumber == sc->sc_iface_index) {
1172b711ef9cSBaptiste Daroussin past_our_iface = 1;
1173b711ef9cSBaptiste Daroussin continue;
1174b711ef9cSBaptiste Daroussin }
1175b711ef9cSBaptiste Daroussin }
1176b711ef9cSBaptiste Daroussin if (desc->bDescriptorType == UDESC_IFACE_ASSOC &&
1177b711ef9cSBaptiste Daroussin past_our_iface)
1178b711ef9cSBaptiste Daroussin break;
1179b711ef9cSBaptiste Daroussin if (!past_our_iface)
1180b711ef9cSBaptiste Daroussin continue;
1181b711ef9cSBaptiste Daroussin if (desc->bDescriptorType != UDESC_CS_INTERFACE)
1182b711ef9cSBaptiste Daroussin continue;
1183b711ef9cSBaptiste Daroussin
1184b711ef9cSBaptiste Daroussin switch (desc->bDescriptorSubtype) {
1185b711ef9cSBaptiste Daroussin case UDESCSUB_VS_INPUT_HEADER:
1186b711ef9cSBaptiste Daroussin if (!uvideo_desc_len(desc, 13, 3, 0, 12))
1187b711ef9cSBaptiste Daroussin break;
1188b711ef9cSBaptiste Daroussin error = uvideo_vs_parse_desc_input_header(sc, desc);
1189b711ef9cSBaptiste Daroussin if (error != USB_ERR_NORMAL_COMPLETION)
1190b711ef9cSBaptiste Daroussin return (error);
1191b711ef9cSBaptiste Daroussin break;
1192b711ef9cSBaptiste Daroussin }
1193b711ef9cSBaptiste Daroussin }
1194b711ef9cSBaptiste Daroussin
1195b711ef9cSBaptiste Daroussin /* Parse video stream format descriptors */
1196b711ef9cSBaptiste Daroussin error = uvideo_vs_parse_desc_format(sc);
1197b711ef9cSBaptiste Daroussin if (error != USB_ERR_NORMAL_COMPLETION)
1198b711ef9cSBaptiste Daroussin return (error);
1199b711ef9cSBaptiste Daroussin
1200b711ef9cSBaptiste Daroussin /* Parse video stream frame descriptors */
1201b711ef9cSBaptiste Daroussin error = uvideo_vs_parse_desc_frame(sc);
1202b711ef9cSBaptiste Daroussin if (error != USB_ERR_NORMAL_COMPLETION)
1203b711ef9cSBaptiste Daroussin return (error);
1204b711ef9cSBaptiste Daroussin
1205b711ef9cSBaptiste Daroussin /* Parse interface collection (alternates for each VS interface) */
1206b711ef9cSBaptiste Daroussin for (i = 0; i < sc->sc_desc_vc_header.fix->bInCollection; i++) {
1207b711ef9cSBaptiste Daroussin iface_num = sc->sc_desc_vc_header.baInterfaceNr[i];
1208b711ef9cSBaptiste Daroussin
1209b711ef9cSBaptiste Daroussin iface = usbd_get_iface(sc->sc_udev, iface_num);
1210b711ef9cSBaptiste Daroussin if (iface == NULL) {
1211b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev,
1212b711ef9cSBaptiste Daroussin "can't get VS interface %d!\n", iface_num);
1213b711ef9cSBaptiste Daroussin return (USB_ERR_INVAL);
1214b711ef9cSBaptiste Daroussin }
1215b711ef9cSBaptiste Daroussin
1216b711ef9cSBaptiste Daroussin id = usbd_get_interface_descriptor(iface);
1217b711ef9cSBaptiste Daroussin if (id == NULL) {
1218b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev,
1219b711ef9cSBaptiste Daroussin "can't get VS iface descriptor %d!\n", iface_num);
1220b711ef9cSBaptiste Daroussin return (USB_ERR_INVAL);
1221b711ef9cSBaptiste Daroussin }
1222b711ef9cSBaptiste Daroussin
1223b711ef9cSBaptiste Daroussin /* Claim this interface */
1224b711ef9cSBaptiste Daroussin usbd_set_parent_iface(sc->sc_udev, iface_num,
1225b711ef9cSBaptiste Daroussin sc->sc_iface_index);
1226b711ef9cSBaptiste Daroussin
1227b711ef9cSBaptiste Daroussin /* Count alternates by iterating descriptors */
1228b711ef9cSBaptiste Daroussin numalts = usbd_get_no_alts(cdesc, id);
1229b711ef9cSBaptiste Daroussin
1230b711ef9cSBaptiste Daroussin DPRINTFN(1, "VS interface %d, bInterfaceNumber=0x%02x, "
1231b711ef9cSBaptiste Daroussin "numalts=%d\n", i, id->bInterfaceNumber, numalts);
1232b711ef9cSBaptiste Daroussin
1233b711ef9cSBaptiste Daroussin error = uvideo_vs_parse_desc_alt(sc, i, iface_num, numalts);
1234b711ef9cSBaptiste Daroussin if (error != USB_ERR_NORMAL_COMPLETION)
1235b711ef9cSBaptiste Daroussin return (error);
1236b711ef9cSBaptiste Daroussin }
1237b711ef9cSBaptiste Daroussin
1238b711ef9cSBaptiste Daroussin /* For now always use the first video stream */
1239b711ef9cSBaptiste Daroussin sc->sc_vs_cur = &sc->sc_vs_coll[0];
1240b711ef9cSBaptiste Daroussin
1241b711ef9cSBaptiste Daroussin return (USB_ERR_NORMAL_COMPLETION);
1242b711ef9cSBaptiste Daroussin }
1243b711ef9cSBaptiste Daroussin
1244b711ef9cSBaptiste Daroussin static usb_error_t
uvideo_vs_parse_desc_input_header(struct uvideo_softc * sc,const struct usb_descriptor * desc)1245b711ef9cSBaptiste Daroussin uvideo_vs_parse_desc_input_header(struct uvideo_softc *sc,
1246b711ef9cSBaptiste Daroussin const struct usb_descriptor *desc)
1247b711ef9cSBaptiste Daroussin {
1248b711ef9cSBaptiste Daroussin struct usb_video_input_header_desc *d;
1249b711ef9cSBaptiste Daroussin
1250b711ef9cSBaptiste Daroussin d = __DECONST(struct usb_video_input_header_desc *, desc);
1251b711ef9cSBaptiste Daroussin
1252b711ef9cSBaptiste Daroussin if (d->bNumFormats == 0) {
1253b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev,
1254b711ef9cSBaptiste Daroussin "no INPUT FORMAT descriptors found!\n");
1255b711ef9cSBaptiste Daroussin return (USB_ERR_INVAL);
1256b711ef9cSBaptiste Daroussin }
1257b711ef9cSBaptiste Daroussin
1258b711ef9cSBaptiste Daroussin sc->sc_desc_vs_input_header.fix = d;
1259b711ef9cSBaptiste Daroussin sc->sc_desc_vs_input_header.bmaControls = (uByte *)(d + 1);
1260b711ef9cSBaptiste Daroussin
1261b711ef9cSBaptiste Daroussin return (USB_ERR_NORMAL_COMPLETION);
1262b711ef9cSBaptiste Daroussin }
1263b711ef9cSBaptiste Daroussin
1264b711ef9cSBaptiste Daroussin static usb_error_t
uvideo_vs_parse_desc_format(struct uvideo_softc * sc)1265b711ef9cSBaptiste Daroussin uvideo_vs_parse_desc_format(struct uvideo_softc *sc)
1266b711ef9cSBaptiste Daroussin {
1267b711ef9cSBaptiste Daroussin struct usb_config_descriptor *cdesc;
1268b711ef9cSBaptiste Daroussin struct usb_descriptor *desc;
1269b711ef9cSBaptiste Daroussin struct usb_interface_descriptor *id;
1270b711ef9cSBaptiste Daroussin int past_our_iface;
1271b711ef9cSBaptiste Daroussin
1272b711ef9cSBaptiste Daroussin DPRINTFN(1, "uvideo_vs_parse_desc_format\n");
1273b711ef9cSBaptiste Daroussin
1274b711ef9cSBaptiste Daroussin cdesc = usbd_get_config_descriptor(sc->sc_udev);
1275b711ef9cSBaptiste Daroussin if (cdesc == NULL)
1276b711ef9cSBaptiste Daroussin return (USB_ERR_INVAL);
1277b711ef9cSBaptiste Daroussin
1278b711ef9cSBaptiste Daroussin past_our_iface = 0;
1279b711ef9cSBaptiste Daroussin desc = NULL;
1280b711ef9cSBaptiste Daroussin while ((desc = usb_desc_foreach(cdesc, desc)) != NULL) {
1281b711ef9cSBaptiste Daroussin if (desc->bDescriptorType == UDESC_INTERFACE) {
1282b711ef9cSBaptiste Daroussin id = (struct usb_interface_descriptor *)desc;
1283b711ef9cSBaptiste Daroussin if (id->bInterfaceNumber == sc->sc_iface_index) {
1284b711ef9cSBaptiste Daroussin past_our_iface = 1;
1285b711ef9cSBaptiste Daroussin continue;
1286b711ef9cSBaptiste Daroussin }
1287b711ef9cSBaptiste Daroussin }
1288b711ef9cSBaptiste Daroussin if (desc->bDescriptorType == UDESC_IFACE_ASSOC &&
1289b711ef9cSBaptiste Daroussin past_our_iface)
1290b711ef9cSBaptiste Daroussin break;
1291b711ef9cSBaptiste Daroussin if (!past_our_iface)
1292b711ef9cSBaptiste Daroussin continue;
1293b711ef9cSBaptiste Daroussin
1294b711ef9cSBaptiste Daroussin if (desc->bDescriptorType != UDESC_CS_INTERFACE)
1295b711ef9cSBaptiste Daroussin continue;
1296b711ef9cSBaptiste Daroussin
1297b711ef9cSBaptiste Daroussin if (desc->bLength != UVIDEO_FORMAT_LEN(desc))
1298b711ef9cSBaptiste Daroussin continue;
1299b711ef9cSBaptiste Daroussin
1300b711ef9cSBaptiste Daroussin switch (desc->bDescriptorSubtype) {
1301b711ef9cSBaptiste Daroussin case UDESCSUB_VS_COLORFORMAT:
1302b711ef9cSBaptiste Daroussin uvideo_vs_parse_desc_colorformat(sc, desc);
1303b711ef9cSBaptiste Daroussin break;
1304b711ef9cSBaptiste Daroussin case UDESCSUB_VS_FORMAT_MJPEG:
1305b711ef9cSBaptiste Daroussin uvideo_vs_parse_desc_format_mjpeg(sc, desc);
1306b711ef9cSBaptiste Daroussin break;
1307b711ef9cSBaptiste Daroussin case UDESCSUB_VS_FORMAT_UNCOMPRESSED:
1308b711ef9cSBaptiste Daroussin uvideo_vs_parse_desc_format_uncompressed(sc, desc);
1309b711ef9cSBaptiste Daroussin break;
1310b711ef9cSBaptiste Daroussin case UDESCSUB_VS_FORMAT_FRAME_BASED:
1311b711ef9cSBaptiste Daroussin uvideo_vs_parse_desc_format_frame_based(sc, desc);
1312b711ef9cSBaptiste Daroussin break;
1313b711ef9cSBaptiste Daroussin case UDESCSUB_VS_FORMAT_H264:
1314b711ef9cSBaptiste Daroussin case UDESCSUB_VS_FORMAT_H264_SIMULCAST:
1315b711ef9cSBaptiste Daroussin uvideo_vs_parse_desc_format_h264(sc, desc);
1316b711ef9cSBaptiste Daroussin break;
1317b711ef9cSBaptiste Daroussin }
1318b711ef9cSBaptiste Daroussin }
1319b711ef9cSBaptiste Daroussin
1320b711ef9cSBaptiste Daroussin sc->sc_fmtgrp_idx = 0;
1321b711ef9cSBaptiste Daroussin
1322b711ef9cSBaptiste Daroussin if (sc->sc_fmtgrp_num == 0) {
1323b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev, "no format descriptors found!\n");
1324b711ef9cSBaptiste Daroussin return (USB_ERR_INVAL);
1325b711ef9cSBaptiste Daroussin }
1326b711ef9cSBaptiste Daroussin DPRINTFN(1, "number of total format descriptors=%d\n",
1327b711ef9cSBaptiste Daroussin sc->sc_fmtgrp_num);
1328b711ef9cSBaptiste Daroussin
1329b711ef9cSBaptiste Daroussin return (USB_ERR_NORMAL_COMPLETION);
1330b711ef9cSBaptiste Daroussin }
1331b711ef9cSBaptiste Daroussin
1332b711ef9cSBaptiste Daroussin static void
uvideo_vs_parse_desc_colorformat(struct uvideo_softc * sc,const struct usb_descriptor * desc)1333b711ef9cSBaptiste Daroussin uvideo_vs_parse_desc_colorformat(struct uvideo_softc *sc,
1334b711ef9cSBaptiste Daroussin const struct usb_descriptor *desc)
1335b711ef9cSBaptiste Daroussin {
1336b711ef9cSBaptiste Daroussin int fmtidx;
1337b711ef9cSBaptiste Daroussin struct usb_video_colorformat_desc *d;
1338b711ef9cSBaptiste Daroussin
1339b711ef9cSBaptiste Daroussin d = __DECONST(struct usb_video_colorformat_desc *, desc);
1340b711ef9cSBaptiste Daroussin
1341b711ef9cSBaptiste Daroussin fmtidx = sc->sc_fmtgrp_idx - 1;
1342b711ef9cSBaptiste Daroussin if (fmtidx < 0 || sc->sc_fmtgrp[fmtidx].has_colorformat)
1343b711ef9cSBaptiste Daroussin return;
1344b711ef9cSBaptiste Daroussin
1345b711ef9cSBaptiste Daroussin if (d->bColorPrimaries < nitems(uvideo_color_primaries))
1346b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[fmtidx].colorspace =
1347b711ef9cSBaptiste Daroussin uvideo_color_primaries[d->bColorPrimaries];
1348b711ef9cSBaptiste Daroussin else
1349b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[fmtidx].colorspace = V4L2_COLORSPACE_SRGB;
1350b711ef9cSBaptiste Daroussin
1351b711ef9cSBaptiste Daroussin if (d->bTransferCharacteristics < nitems(uvideo_xfer_characteristics))
1352b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[fmtidx].xfer_func =
1353b711ef9cSBaptiste Daroussin uvideo_xfer_characteristics[d->bTransferCharacteristics];
1354b711ef9cSBaptiste Daroussin else
1355b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[fmtidx].xfer_func = V4L2_XFER_FUNC_DEFAULT;
1356b711ef9cSBaptiste Daroussin
1357b711ef9cSBaptiste Daroussin if (d->bMatrixCoefficients < nitems(uvideo_matrix_coefficients))
1358b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[fmtidx].ycbcr_enc =
1359b711ef9cSBaptiste Daroussin uvideo_matrix_coefficients[d->bMatrixCoefficients];
1360b711ef9cSBaptiste Daroussin else
1361b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[fmtidx].ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
1362b711ef9cSBaptiste Daroussin
1363b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[fmtidx].has_colorformat = 1;
1364b711ef9cSBaptiste Daroussin }
1365b711ef9cSBaptiste Daroussin
1366b711ef9cSBaptiste Daroussin static void
uvideo_vs_parse_desc_format_mjpeg(struct uvideo_softc * sc,const struct usb_descriptor * desc)1367b711ef9cSBaptiste Daroussin uvideo_vs_parse_desc_format_mjpeg(struct uvideo_softc *sc,
1368b711ef9cSBaptiste Daroussin const struct usb_descriptor *desc)
1369b711ef9cSBaptiste Daroussin {
1370b711ef9cSBaptiste Daroussin struct usb_video_format_desc *d;
1371b711ef9cSBaptiste Daroussin
1372b711ef9cSBaptiste Daroussin d = __DECONST(struct usb_video_format_desc *, desc);
1373b711ef9cSBaptiste Daroussin
1374b711ef9cSBaptiste Daroussin if (d->bNumFrameDescriptors == 0) {
1375b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev,
1376b711ef9cSBaptiste Daroussin "no MJPEG frame descriptors available!\n");
1377b711ef9cSBaptiste Daroussin return;
1378b711ef9cSBaptiste Daroussin }
1379b711ef9cSBaptiste Daroussin
1380b711ef9cSBaptiste Daroussin if (sc->sc_fmtgrp_idx >= UVIDEO_MAX_FORMAT) {
1381b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev,
1382b711ef9cSBaptiste Daroussin "too many format descriptors found!\n");
1383b711ef9cSBaptiste Daroussin return;
1384b711ef9cSBaptiste Daroussin }
1385b711ef9cSBaptiste Daroussin
1386b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[sc->sc_fmtgrp_idx].format = d;
1387b711ef9cSBaptiste Daroussin if (d->u.mjpeg.bDefaultFrameIndex > d->bNumFrameDescriptors ||
1388b711ef9cSBaptiste Daroussin d->u.mjpeg.bDefaultFrameIndex < 1)
1389b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[sc->sc_fmtgrp_idx].format_dfidx = 1;
1390b711ef9cSBaptiste Daroussin else
1391b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[sc->sc_fmtgrp_idx].format_dfidx =
1392b711ef9cSBaptiste Daroussin d->u.mjpeg.bDefaultFrameIndex;
1393b711ef9cSBaptiste Daroussin
1394b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[sc->sc_fmtgrp_idx].pixelformat = V4L2_PIX_FMT_MJPEG;
1395b711ef9cSBaptiste Daroussin
1396b711ef9cSBaptiste Daroussin if (sc->sc_fmtgrp_cur == NULL)
1397b711ef9cSBaptiste Daroussin sc->sc_fmtgrp_cur = &sc->sc_fmtgrp[sc->sc_fmtgrp_idx];
1398b711ef9cSBaptiste Daroussin
1399b711ef9cSBaptiste Daroussin sc->sc_fmtgrp_idx++;
1400b711ef9cSBaptiste Daroussin sc->sc_fmtgrp_num++;
1401b711ef9cSBaptiste Daroussin }
1402b711ef9cSBaptiste Daroussin
1403b711ef9cSBaptiste Daroussin static void
uvideo_vs_parse_desc_format_h264(struct uvideo_softc * sc,const struct usb_descriptor * desc)1404b711ef9cSBaptiste Daroussin uvideo_vs_parse_desc_format_h264(struct uvideo_softc *sc,
1405b711ef9cSBaptiste Daroussin const struct usb_descriptor *desc)
1406b711ef9cSBaptiste Daroussin {
1407b711ef9cSBaptiste Daroussin struct usb_video_format_desc *d;
1408b711ef9cSBaptiste Daroussin
1409b711ef9cSBaptiste Daroussin d = __DECONST(struct usb_video_format_desc *, desc);
1410b711ef9cSBaptiste Daroussin
1411b711ef9cSBaptiste Daroussin if (d->bNumFrameDescriptors == 0) {
1412b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev,
1413b711ef9cSBaptiste Daroussin "no H264 frame descriptors available!\n");
1414b711ef9cSBaptiste Daroussin return;
1415b711ef9cSBaptiste Daroussin }
1416b711ef9cSBaptiste Daroussin
1417b711ef9cSBaptiste Daroussin if (sc->sc_fmtgrp_idx >= UVIDEO_MAX_FORMAT) {
1418b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev,
1419b711ef9cSBaptiste Daroussin "too many format descriptors found!\n");
1420b711ef9cSBaptiste Daroussin return;
1421b711ef9cSBaptiste Daroussin }
1422b711ef9cSBaptiste Daroussin
1423b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[sc->sc_fmtgrp_idx].format = d;
1424b711ef9cSBaptiste Daroussin if (d->u.h264.bDefaultFrameIndex > d->bNumFrameDescriptors ||
1425b711ef9cSBaptiste Daroussin d->u.h264.bDefaultFrameIndex < 1)
1426b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[sc->sc_fmtgrp_idx].format_dfidx = 1;
1427b711ef9cSBaptiste Daroussin else
1428b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[sc->sc_fmtgrp_idx].format_dfidx =
1429b711ef9cSBaptiste Daroussin d->u.h264.bDefaultFrameIndex;
1430b711ef9cSBaptiste Daroussin
1431b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[sc->sc_fmtgrp_idx].pixelformat = V4L2_PIX_FMT_H264;
1432b711ef9cSBaptiste Daroussin
1433b711ef9cSBaptiste Daroussin if (sc->sc_fmtgrp_cur == NULL)
1434b711ef9cSBaptiste Daroussin sc->sc_fmtgrp_cur = &sc->sc_fmtgrp[sc->sc_fmtgrp_idx];
1435b711ef9cSBaptiste Daroussin
1436b711ef9cSBaptiste Daroussin sc->sc_fmtgrp_idx++;
1437b711ef9cSBaptiste Daroussin sc->sc_fmtgrp_num++;
1438b711ef9cSBaptiste Daroussin }
1439b711ef9cSBaptiste Daroussin
1440b711ef9cSBaptiste Daroussin static void
uvideo_vs_parse_desc_format_frame_based(struct uvideo_softc * sc,const struct usb_descriptor * desc)1441b711ef9cSBaptiste Daroussin uvideo_vs_parse_desc_format_frame_based(struct uvideo_softc *sc,
1442b711ef9cSBaptiste Daroussin const struct usb_descriptor *desc)
1443b711ef9cSBaptiste Daroussin {
1444b711ef9cSBaptiste Daroussin struct usb_video_format_desc *d;
1445b711ef9cSBaptiste Daroussin int i, j, nent;
1446b711ef9cSBaptiste Daroussin
1447b711ef9cSBaptiste Daroussin d = __DECONST(struct usb_video_format_desc *, desc);
1448b711ef9cSBaptiste Daroussin
1449b711ef9cSBaptiste Daroussin if (d->bNumFrameDescriptors == 0) {
1450b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev,
1451b711ef9cSBaptiste Daroussin "no frame-based frame descriptors available!\n");
1452b711ef9cSBaptiste Daroussin return;
1453b711ef9cSBaptiste Daroussin }
1454b711ef9cSBaptiste Daroussin
1455b711ef9cSBaptiste Daroussin if (sc->sc_fmtgrp_idx >= UVIDEO_MAX_FORMAT) {
1456b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev,
1457b711ef9cSBaptiste Daroussin "too many format descriptors found!\n");
1458b711ef9cSBaptiste Daroussin return;
1459b711ef9cSBaptiste Daroussin }
1460b711ef9cSBaptiste Daroussin
1461b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[sc->sc_fmtgrp_idx].format = d;
1462b711ef9cSBaptiste Daroussin if (d->u.fb.bDefaultFrameIndex > d->bNumFrameDescriptors ||
1463b711ef9cSBaptiste Daroussin d->u.fb.bDefaultFrameIndex < 1)
1464b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[sc->sc_fmtgrp_idx].format_dfidx = 1;
1465b711ef9cSBaptiste Daroussin else
1466b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[sc->sc_fmtgrp_idx].format_dfidx =
1467b711ef9cSBaptiste Daroussin d->u.fb.bDefaultFrameIndex;
1468b711ef9cSBaptiste Daroussin
1469b711ef9cSBaptiste Daroussin i = sc->sc_fmtgrp_idx;
1470b711ef9cSBaptiste Daroussin
1471b711ef9cSBaptiste Daroussin /* Map GUID to pixel format */
1472b711ef9cSBaptiste Daroussin nent = nitems(uvideo_map_fmts);
1473b711ef9cSBaptiste Daroussin for (j = 0; j < nent; j++) {
1474b711ef9cSBaptiste Daroussin if (!memcmp(sc->sc_fmtgrp[i].format->u.uc.guidFormat,
1475b711ef9cSBaptiste Daroussin uvideo_map_fmts[j].guidFormat, 16)) {
1476b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[i].pixelformat =
1477b711ef9cSBaptiste Daroussin uvideo_map_fmts[j].pixelformat;
1478b711ef9cSBaptiste Daroussin break;
1479b711ef9cSBaptiste Daroussin }
1480b711ef9cSBaptiste Daroussin }
1481b711ef9cSBaptiste Daroussin if (j == nent)
1482b711ef9cSBaptiste Daroussin memcpy(&sc->sc_fmtgrp[i].pixelformat,
1483b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[i].format->u.uc.guidFormat,
1484b711ef9cSBaptiste Daroussin sizeof(uint32_t));
1485b711ef9cSBaptiste Daroussin
1486b711ef9cSBaptiste Daroussin if (sc->sc_fmtgrp_cur == NULL)
1487b711ef9cSBaptiste Daroussin sc->sc_fmtgrp_cur = &sc->sc_fmtgrp[sc->sc_fmtgrp_idx];
1488b711ef9cSBaptiste Daroussin
1489b711ef9cSBaptiste Daroussin sc->sc_fmtgrp_idx++;
1490b711ef9cSBaptiste Daroussin sc->sc_fmtgrp_num++;
1491b711ef9cSBaptiste Daroussin }
1492b711ef9cSBaptiste Daroussin
1493b711ef9cSBaptiste Daroussin static void
uvideo_vs_parse_desc_format_uncompressed(struct uvideo_softc * sc,const struct usb_descriptor * desc)1494b711ef9cSBaptiste Daroussin uvideo_vs_parse_desc_format_uncompressed(struct uvideo_softc *sc,
1495b711ef9cSBaptiste Daroussin const struct usb_descriptor *desc)
1496b711ef9cSBaptiste Daroussin {
1497b711ef9cSBaptiste Daroussin struct usb_video_format_desc *d;
1498b711ef9cSBaptiste Daroussin int i, j, nent;
1499b711ef9cSBaptiste Daroussin
1500b711ef9cSBaptiste Daroussin d = __DECONST(struct usb_video_format_desc *, desc);
1501b711ef9cSBaptiste Daroussin
1502b711ef9cSBaptiste Daroussin if (d->bNumFrameDescriptors == 0) {
1503b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev,
1504b711ef9cSBaptiste Daroussin "no UNCOMPRESSED frame descriptors available!\n");
1505b711ef9cSBaptiste Daroussin return;
1506b711ef9cSBaptiste Daroussin }
1507b711ef9cSBaptiste Daroussin
1508b711ef9cSBaptiste Daroussin if (sc->sc_fmtgrp_idx >= UVIDEO_MAX_FORMAT) {
1509b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev,
1510b711ef9cSBaptiste Daroussin "too many format descriptors found!\n");
1511b711ef9cSBaptiste Daroussin return;
1512b711ef9cSBaptiste Daroussin }
1513b711ef9cSBaptiste Daroussin
1514b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[sc->sc_fmtgrp_idx].format = d;
1515b711ef9cSBaptiste Daroussin if (d->u.uc.bDefaultFrameIndex > d->bNumFrameDescriptors ||
1516b711ef9cSBaptiste Daroussin d->u.uc.bDefaultFrameIndex < 1)
1517b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[sc->sc_fmtgrp_idx].format_dfidx = 1;
1518b711ef9cSBaptiste Daroussin else
1519b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[sc->sc_fmtgrp_idx].format_dfidx =
1520b711ef9cSBaptiste Daroussin d->u.uc.bDefaultFrameIndex;
1521b711ef9cSBaptiste Daroussin
1522b711ef9cSBaptiste Daroussin i = sc->sc_fmtgrp_idx;
1523b711ef9cSBaptiste Daroussin
1524b711ef9cSBaptiste Daroussin /* Map GUID to pixel format */
1525b711ef9cSBaptiste Daroussin nent = nitems(uvideo_map_fmts);
1526b711ef9cSBaptiste Daroussin for (j = 0; j < nent; j++) {
1527b711ef9cSBaptiste Daroussin if (!memcmp(sc->sc_fmtgrp[i].format->u.uc.guidFormat,
1528b711ef9cSBaptiste Daroussin uvideo_map_fmts[j].guidFormat, 16)) {
1529b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[i].pixelformat =
1530b711ef9cSBaptiste Daroussin uvideo_map_fmts[j].pixelformat;
1531b711ef9cSBaptiste Daroussin break;
1532b711ef9cSBaptiste Daroussin }
1533b711ef9cSBaptiste Daroussin }
1534b711ef9cSBaptiste Daroussin if (j == nent)
1535b711ef9cSBaptiste Daroussin memcpy(&sc->sc_fmtgrp[i].pixelformat,
1536b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[i].format->u.uc.guidFormat,
1537b711ef9cSBaptiste Daroussin sizeof(uint32_t));
1538b711ef9cSBaptiste Daroussin
1539b711ef9cSBaptiste Daroussin if (sc->sc_fmtgrp_cur == NULL)
1540b711ef9cSBaptiste Daroussin sc->sc_fmtgrp_cur = &sc->sc_fmtgrp[sc->sc_fmtgrp_idx];
1541b711ef9cSBaptiste Daroussin
1542b711ef9cSBaptiste Daroussin sc->sc_fmtgrp_idx++;
1543b711ef9cSBaptiste Daroussin sc->sc_fmtgrp_num++;
1544b711ef9cSBaptiste Daroussin }
1545b711ef9cSBaptiste Daroussin
1546b711ef9cSBaptiste Daroussin static usb_error_t
uvideo_vs_parse_desc_frame(struct uvideo_softc * sc)1547b711ef9cSBaptiste Daroussin uvideo_vs_parse_desc_frame(struct uvideo_softc *sc)
1548b711ef9cSBaptiste Daroussin {
1549b711ef9cSBaptiste Daroussin struct usb_config_descriptor *cdesc;
1550b711ef9cSBaptiste Daroussin struct usb_descriptor *desc;
1551b711ef9cSBaptiste Daroussin struct usb_interface_descriptor *id;
1552b711ef9cSBaptiste Daroussin usb_error_t error;
1553b711ef9cSBaptiste Daroussin int past_our_iface;
1554b711ef9cSBaptiste Daroussin
1555b711ef9cSBaptiste Daroussin DPRINTFN(1, "uvideo_vs_parse_desc_frame\n");
1556b711ef9cSBaptiste Daroussin
1557b711ef9cSBaptiste Daroussin cdesc = usbd_get_config_descriptor(sc->sc_udev);
1558b711ef9cSBaptiste Daroussin if (cdesc == NULL)
1559b711ef9cSBaptiste Daroussin return (USB_ERR_INVAL);
1560b711ef9cSBaptiste Daroussin
1561b711ef9cSBaptiste Daroussin past_our_iface = 0;
1562b711ef9cSBaptiste Daroussin desc = NULL;
1563b711ef9cSBaptiste Daroussin while ((desc = usb_desc_foreach(cdesc, desc)) != NULL) {
1564b711ef9cSBaptiste Daroussin if (desc->bDescriptorType == UDESC_INTERFACE) {
1565b711ef9cSBaptiste Daroussin id = (struct usb_interface_descriptor *)desc;
1566b711ef9cSBaptiste Daroussin if (id->bInterfaceNumber == sc->sc_iface_index) {
1567b711ef9cSBaptiste Daroussin past_our_iface = 1;
1568b711ef9cSBaptiste Daroussin continue;
1569b711ef9cSBaptiste Daroussin }
1570b711ef9cSBaptiste Daroussin }
1571b711ef9cSBaptiste Daroussin if (desc->bDescriptorType == UDESC_IFACE_ASSOC &&
1572b711ef9cSBaptiste Daroussin past_our_iface)
1573b711ef9cSBaptiste Daroussin break;
1574b711ef9cSBaptiste Daroussin if (!past_our_iface)
1575b711ef9cSBaptiste Daroussin continue;
1576b711ef9cSBaptiste Daroussin
1577b711ef9cSBaptiste Daroussin if (desc->bDescriptorType == UDESC_CS_INTERFACE &&
1578b711ef9cSBaptiste Daroussin desc->bLength > UVIDEO_FRAME_MIN_LEN(desc) &&
1579b711ef9cSBaptiste Daroussin (desc->bDescriptorSubtype == UDESCSUB_VS_FRAME_MJPEG ||
1580b711ef9cSBaptiste Daroussin desc->bDescriptorSubtype ==
1581b711ef9cSBaptiste Daroussin UDESCSUB_VS_FRAME_UNCOMPRESSED)) {
1582b711ef9cSBaptiste Daroussin error = uvideo_vs_parse_desc_frame_buffer_size(sc,
1583b711ef9cSBaptiste Daroussin desc);
1584b711ef9cSBaptiste Daroussin if (error != USB_ERR_NORMAL_COMPLETION)
1585b711ef9cSBaptiste Daroussin return (error);
1586b711ef9cSBaptiste Daroussin }
1587b711ef9cSBaptiste Daroussin if (desc->bDescriptorType == UDESC_CS_INTERFACE &&
1588b711ef9cSBaptiste Daroussin desc->bLength > UVIDEO_FRAME_MIN_LEN(desc) &&
1589b711ef9cSBaptiste Daroussin (desc->bDescriptorSubtype == UDESCSUB_VS_FRAME_H264 ||
1590b711ef9cSBaptiste Daroussin desc->bDescriptorSubtype ==
1591b711ef9cSBaptiste Daroussin UDESCSUB_VS_FRAME_FRAME_BASED)) {
1592b711ef9cSBaptiste Daroussin error = uvideo_vs_parse_desc_frame_max_rate(sc, desc);
1593b711ef9cSBaptiste Daroussin if (error != USB_ERR_NORMAL_COMPLETION)
1594b711ef9cSBaptiste Daroussin return (error);
1595b711ef9cSBaptiste Daroussin }
1596b711ef9cSBaptiste Daroussin }
1597b711ef9cSBaptiste Daroussin
1598b711ef9cSBaptiste Daroussin return (USB_ERR_NORMAL_COMPLETION);
1599b711ef9cSBaptiste Daroussin }
1600b711ef9cSBaptiste Daroussin
1601b711ef9cSBaptiste Daroussin static usb_error_t
uvideo_vs_parse_desc_frame_buffer_size(struct uvideo_softc * sc,const struct usb_descriptor * desc)1602b711ef9cSBaptiste Daroussin uvideo_vs_parse_desc_frame_buffer_size(struct uvideo_softc *sc,
1603b711ef9cSBaptiste Daroussin const struct usb_descriptor *desc)
1604b711ef9cSBaptiste Daroussin {
1605b711ef9cSBaptiste Daroussin struct usb_video_frame_desc *fd =
1606b711ef9cSBaptiste Daroussin __DECONST(struct usb_video_frame_desc *, desc);
1607b711ef9cSBaptiste Daroussin int fmtidx, frame_num;
1608b711ef9cSBaptiste Daroussin uint32_t fbuf_size;
1609b711ef9cSBaptiste Daroussin
1610b711ef9cSBaptiste Daroussin fmtidx = sc->sc_fmtgrp_idx;
1611b711ef9cSBaptiste Daroussin frame_num = sc->sc_fmtgrp[fmtidx].frame_num;
1612b711ef9cSBaptiste Daroussin if (frame_num >= UVIDEO_MAX_FRAME) {
1613b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev,
1614b711ef9cSBaptiste Daroussin "too many %s frame descriptors found!\n",
1615b711ef9cSBaptiste Daroussin desc->bDescriptorSubtype == UDESCSUB_VS_FRAME_MJPEG ?
1616b711ef9cSBaptiste Daroussin "MJPEG" : "UNCOMPRESSED");
1617b711ef9cSBaptiste Daroussin return (USB_ERR_INVAL);
1618b711ef9cSBaptiste Daroussin }
1619b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[fmtidx].frame[frame_num] = fd;
1620b711ef9cSBaptiste Daroussin
1621b711ef9cSBaptiste Daroussin if (sc->sc_fmtgrp[fmtidx].frame_cur == NULL ||
1622b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[fmtidx].format_dfidx == fd->bFrameIndex)
1623b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[fmtidx].frame_cur = fd;
1624b711ef9cSBaptiste Daroussin
1625b711ef9cSBaptiste Daroussin /*
1626b711ef9cSBaptiste Daroussin * For uncompressed formats, compute the frame buffer size from
1627b711ef9cSBaptiste Daroussin * width * height * bpp since dwMaxVideoFrameBufferSize may be wrong.
1628b711ef9cSBaptiste Daroussin */
1629b711ef9cSBaptiste Daroussin if (desc->bDescriptorSubtype == UDESCSUB_VS_FRAME_UNCOMPRESSED) {
1630b711ef9cSBaptiste Daroussin fbuf_size = UGETW(fd->u.uc.wWidth) *
1631b711ef9cSBaptiste Daroussin UGETW(fd->u.uc.wHeight) *
1632b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[fmtidx].format->u.uc.bBitsPerPixel / NBBY;
1633b711ef9cSBaptiste Daroussin } else
1634b711ef9cSBaptiste Daroussin fbuf_size = UGETDW(fd->u.uc.dwMaxVideoFrameBufferSize);
1635b711ef9cSBaptiste Daroussin
1636b711ef9cSBaptiste Daroussin if (fbuf_size > sc->sc_max_fbuf_size)
1637b711ef9cSBaptiste Daroussin sc->sc_max_fbuf_size = fbuf_size;
1638b711ef9cSBaptiste Daroussin
1639b711ef9cSBaptiste Daroussin if (++sc->sc_fmtgrp[fmtidx].frame_num ==
1640b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[fmtidx].format->bNumFrameDescriptors)
1641b711ef9cSBaptiste Daroussin sc->sc_fmtgrp_idx++;
1642b711ef9cSBaptiste Daroussin
1643b711ef9cSBaptiste Daroussin return (USB_ERR_NORMAL_COMPLETION);
1644b711ef9cSBaptiste Daroussin }
1645b711ef9cSBaptiste Daroussin
1646b711ef9cSBaptiste Daroussin static usb_error_t
uvideo_vs_parse_desc_frame_max_rate(struct uvideo_softc * sc,const struct usb_descriptor * desc)1647b711ef9cSBaptiste Daroussin uvideo_vs_parse_desc_frame_max_rate(struct uvideo_softc *sc,
1648b711ef9cSBaptiste Daroussin const struct usb_descriptor *desc)
1649b711ef9cSBaptiste Daroussin {
1650b711ef9cSBaptiste Daroussin struct usb_video_frame_desc *fd =
1651b711ef9cSBaptiste Daroussin __DECONST(struct usb_video_frame_desc *, desc);
1652b711ef9cSBaptiste Daroussin uint8_t *p;
1653b711ef9cSBaptiste Daroussin int i, fmtidx, frame_num, length, nivals;
1654b711ef9cSBaptiste Daroussin uint32_t fbuf_size, frame_ival, next_frame_ival;
1655b711ef9cSBaptiste Daroussin
1656b711ef9cSBaptiste Daroussin fmtidx = sc->sc_fmtgrp_idx;
1657b711ef9cSBaptiste Daroussin frame_num = sc->sc_fmtgrp[fmtidx].frame_num;
1658b711ef9cSBaptiste Daroussin if (frame_num >= UVIDEO_MAX_FRAME) {
1659b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev,
1660b711ef9cSBaptiste Daroussin "too many %s frame descriptors found!\n",
1661b711ef9cSBaptiste Daroussin desc->bDescriptorSubtype == UDESCSUB_VS_FRAME_H264 ?
1662b711ef9cSBaptiste Daroussin "H264" : "FRAME BASED");
1663b711ef9cSBaptiste Daroussin return (USB_ERR_INVAL);
1664b711ef9cSBaptiste Daroussin }
1665b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[fmtidx].frame[frame_num] = fd;
1666b711ef9cSBaptiste Daroussin
1667b711ef9cSBaptiste Daroussin if (sc->sc_fmtgrp[fmtidx].frame_cur == NULL ||
1668b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[fmtidx].format_dfidx == fd->bFrameIndex)
1669b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[fmtidx].frame_cur = fd;
1670b711ef9cSBaptiste Daroussin
1671b711ef9cSBaptiste Daroussin /*
1672b711ef9cSBaptiste Daroussin * Frame Based and H264 frames don't have dwMaxVideoFrameBufferSize;
1673b711ef9cSBaptiste Daroussin * compute required buffer from dwMaxBitRate and dwFrameInterval.
1674b711ef9cSBaptiste Daroussin */
1675b711ef9cSBaptiste Daroussin frame_ival = UGETDW(fd->u.h264.dwDefaultFrameInterval);
1676b711ef9cSBaptiste Daroussin
1677b711ef9cSBaptiste Daroussin p = __DECONST(uint8_t *, desc) + UVIDEO_FRAME_MIN_LEN(fd);
1678b711ef9cSBaptiste Daroussin length = fd->bLength - UVIDEO_FRAME_MIN_LEN(fd);
1679b711ef9cSBaptiste Daroussin
1680b711ef9cSBaptiste Daroussin nivals = UVIDEO_FRAME_NUM_INTERVALS(fd);
1681b711ef9cSBaptiste Daroussin
1682b711ef9cSBaptiste Daroussin for (i = 0; i < nivals; i++) {
1683b711ef9cSBaptiste Daroussin if (length <= 0)
1684b711ef9cSBaptiste Daroussin break;
1685b711ef9cSBaptiste Daroussin next_frame_ival = UGETDW(p);
1686b711ef9cSBaptiste Daroussin if (next_frame_ival > frame_ival)
1687b711ef9cSBaptiste Daroussin frame_ival = next_frame_ival;
1688b711ef9cSBaptiste Daroussin p += sizeof(uDWord);
1689b711ef9cSBaptiste Daroussin length -= sizeof(uDWord);
1690b711ef9cSBaptiste Daroussin }
1691b711ef9cSBaptiste Daroussin
1692b711ef9cSBaptiste Daroussin fbuf_size = UGETDW(UVIDEO_FRAME_FIELD(fd, dwMaxBitRate)) * frame_ival;
1693b711ef9cSBaptiste Daroussin fbuf_size /= 8 * 10000000;
1694b711ef9cSBaptiste Daroussin
1695b711ef9cSBaptiste Daroussin if (fbuf_size > sc->sc_max_fbuf_size)
1696b711ef9cSBaptiste Daroussin sc->sc_max_fbuf_size = fbuf_size;
1697b711ef9cSBaptiste Daroussin
1698b711ef9cSBaptiste Daroussin if (++sc->sc_fmtgrp[fmtidx].frame_num ==
1699b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[fmtidx].format->bNumFrameDescriptors)
1700b711ef9cSBaptiste Daroussin sc->sc_fmtgrp_idx++;
1701b711ef9cSBaptiste Daroussin
1702b711ef9cSBaptiste Daroussin return (USB_ERR_NORMAL_COMPLETION);
1703b711ef9cSBaptiste Daroussin }
1704b711ef9cSBaptiste Daroussin
1705b711ef9cSBaptiste Daroussin static usb_error_t
uvideo_vs_parse_desc_alt(struct uvideo_softc * sc,int vs_nr,int iface,int numalts)1706b711ef9cSBaptiste Daroussin uvideo_vs_parse_desc_alt(struct uvideo_softc *sc, int vs_nr, int iface,
1707b711ef9cSBaptiste Daroussin int numalts)
1708b711ef9cSBaptiste Daroussin {
1709b711ef9cSBaptiste Daroussin struct uvideo_vs_iface *vs;
1710b711ef9cSBaptiste Daroussin struct usb_config_descriptor *cdesc;
1711b711ef9cSBaptiste Daroussin struct usb_descriptor *desc;
1712b711ef9cSBaptiste Daroussin struct usb_interface_descriptor *id;
1713b711ef9cSBaptiste Daroussin struct usb_endpoint_descriptor *ed;
1714b711ef9cSBaptiste Daroussin uint8_t ep_dir, ep_type;
1715b711ef9cSBaptiste Daroussin int bulk_endpoint;
1716b711ef9cSBaptiste Daroussin uint32_t psize;
1717b711ef9cSBaptiste Daroussin int past_our_iface;
1718b711ef9cSBaptiste Daroussin
1719b711ef9cSBaptiste Daroussin vs = &sc->sc_vs_coll[vs_nr];
1720b711ef9cSBaptiste Daroussin
1721b711ef9cSBaptiste Daroussin cdesc = usbd_get_config_descriptor(sc->sc_udev);
1722b711ef9cSBaptiste Daroussin if (cdesc == NULL)
1723b711ef9cSBaptiste Daroussin return (USB_ERR_INVAL);
1724b711ef9cSBaptiste Daroussin
1725b711ef9cSBaptiste Daroussin vs->bulk_endpoint = 1;
1726b711ef9cSBaptiste Daroussin past_our_iface = 0;
1727b711ef9cSBaptiste Daroussin
1728b711ef9cSBaptiste Daroussin desc = NULL;
1729b711ef9cSBaptiste Daroussin while ((desc = usb_desc_foreach(cdesc, desc)) != NULL) {
1730b711ef9cSBaptiste Daroussin if (desc->bDescriptorType == UDESC_INTERFACE) {
1731b711ef9cSBaptiste Daroussin id = (struct usb_interface_descriptor *)desc;
1732b711ef9cSBaptiste Daroussin if (id->bInterfaceNumber == sc->sc_iface_index) {
1733b711ef9cSBaptiste Daroussin past_our_iface = 1;
1734b711ef9cSBaptiste Daroussin continue;
1735b711ef9cSBaptiste Daroussin }
1736b711ef9cSBaptiste Daroussin }
1737b711ef9cSBaptiste Daroussin if (desc->bDescriptorType == UDESC_IFACE_ASSOC &&
1738b711ef9cSBaptiste Daroussin past_our_iface)
1739b711ef9cSBaptiste Daroussin break;
1740b711ef9cSBaptiste Daroussin if (!past_our_iface)
1741b711ef9cSBaptiste Daroussin continue;
1742b711ef9cSBaptiste Daroussin
1743b711ef9cSBaptiste Daroussin /* Find video stream interface */
1744b711ef9cSBaptiste Daroussin if (desc->bDescriptorType != UDESC_INTERFACE)
1745b711ef9cSBaptiste Daroussin continue;
1746b711ef9cSBaptiste Daroussin id = (struct usb_interface_descriptor *)(uint8_t *)desc;
1747b711ef9cSBaptiste Daroussin if (id->bInterfaceNumber != iface)
1748b711ef9cSBaptiste Daroussin continue;
1749b711ef9cSBaptiste Daroussin
1750b711ef9cSBaptiste Daroussin DPRINTFN(1, "bAlternateSetting=0x%02x\n",
1751b711ef9cSBaptiste Daroussin id->bAlternateSetting);
1752b711ef9cSBaptiste Daroussin if (id->bNumEndpoints == 0)
1753b711ef9cSBaptiste Daroussin continue;
1754b711ef9cSBaptiste Daroussin
1755b711ef9cSBaptiste Daroussin /* Jump to the endpoint descriptor */
1756b711ef9cSBaptiste Daroussin while ((desc = usb_desc_foreach(cdesc, desc)) != NULL) {
1757b711ef9cSBaptiste Daroussin if (desc->bDescriptorType == UDESC_ENDPOINT)
1758b711ef9cSBaptiste Daroussin break;
1759b711ef9cSBaptiste Daroussin }
1760b711ef9cSBaptiste Daroussin if (desc == NULL)
1761b711ef9cSBaptiste Daroussin break;
1762b711ef9cSBaptiste Daroussin ed = (struct usb_endpoint_descriptor *)(uint8_t *)desc;
1763b711ef9cSBaptiste Daroussin
1764b711ef9cSBaptiste Daroussin /* Locate endpoint type */
1765b711ef9cSBaptiste Daroussin ep_dir = UE_GET_DIR(ed->bEndpointAddress);
1766b711ef9cSBaptiste Daroussin ep_type = UE_GET_XFERTYPE(ed->bmAttributes);
1767b711ef9cSBaptiste Daroussin if (ep_dir == UE_DIR_IN && ep_type == UE_ISOCHRONOUS)
1768b711ef9cSBaptiste Daroussin bulk_endpoint = 0;
1769b711ef9cSBaptiste Daroussin else if (ep_dir == UE_DIR_IN && ep_type == UE_BULK)
1770b711ef9cSBaptiste Daroussin bulk_endpoint = 1;
1771b711ef9cSBaptiste Daroussin else
1772b711ef9cSBaptiste Daroussin continue;
1773b711ef9cSBaptiste Daroussin
1774b711ef9cSBaptiste Daroussin if (bulk_endpoint && !vs->bulk_endpoint)
1775b711ef9cSBaptiste Daroussin continue;
1776b711ef9cSBaptiste Daroussin
1777b711ef9cSBaptiste Daroussin psize = UGETW(ed->wMaxPacketSize);
1778b711ef9cSBaptiste Daroussin psize = UE_GET_SIZE(psize) * (1 + UE_GET_TRANS(psize));
1779b711ef9cSBaptiste Daroussin
1780b711ef9cSBaptiste Daroussin /* Save endpoint with largest bandwidth */
1781b711ef9cSBaptiste Daroussin if (psize > vs->psize) {
1782b711ef9cSBaptiste Daroussin vs->endpoint = ed->bEndpointAddress;
1783b711ef9cSBaptiste Daroussin vs->numalts = numalts;
1784b711ef9cSBaptiste Daroussin vs->curalt = id->bAlternateSetting;
1785b711ef9cSBaptiste Daroussin vs->psize = psize;
1786b711ef9cSBaptiste Daroussin vs->iface_index = iface;
1787b711ef9cSBaptiste Daroussin vs->bulk_endpoint = bulk_endpoint;
1788b711ef9cSBaptiste Daroussin }
1789b711ef9cSBaptiste Daroussin }
1790b711ef9cSBaptiste Daroussin
1791b711ef9cSBaptiste Daroussin /* Check if we found a valid alternate interface */
1792b711ef9cSBaptiste Daroussin if (vs->psize == 0) {
1793b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev,
1794b711ef9cSBaptiste Daroussin "no valid alternate interface found!\n");
1795b711ef9cSBaptiste Daroussin return (USB_ERR_INVAL);
1796b711ef9cSBaptiste Daroussin }
1797b711ef9cSBaptiste Daroussin
1798b711ef9cSBaptiste Daroussin return (USB_ERR_NORMAL_COMPLETION);
1799b711ef9cSBaptiste Daroussin }
1800b711ef9cSBaptiste Daroussin
1801b711ef9cSBaptiste Daroussin /*
1802b711ef9cSBaptiste Daroussin * Validate a variable-length descriptor.
1803b711ef9cSBaptiste Daroussin */
1804b711ef9cSBaptiste Daroussin static int
uvideo_desc_len(const struct usb_descriptor * desc,int size_fix,int off_num_elements,int size_element,int off_size_element)1805b711ef9cSBaptiste Daroussin uvideo_desc_len(const struct usb_descriptor *desc,
1806b711ef9cSBaptiste Daroussin int size_fix, int off_num_elements, int size_element, int off_size_element)
1807b711ef9cSBaptiste Daroussin {
1808b711ef9cSBaptiste Daroussin uint8_t *buf;
1809b711ef9cSBaptiste Daroussin int size_elements, size_total;
1810b711ef9cSBaptiste Daroussin
1811b711ef9cSBaptiste Daroussin if (desc->bLength < size_fix)
1812b711ef9cSBaptiste Daroussin return (0);
1813b711ef9cSBaptiste Daroussin
1814b711ef9cSBaptiste Daroussin buf = __DECONST(uint8_t *, desc);
1815b711ef9cSBaptiste Daroussin
1816b711ef9cSBaptiste Daroussin if (size_element == 0)
1817b711ef9cSBaptiste Daroussin size_element = buf[off_size_element];
1818b711ef9cSBaptiste Daroussin
1819b711ef9cSBaptiste Daroussin size_elements = buf[off_num_elements] * size_element;
1820b711ef9cSBaptiste Daroussin size_total = size_fix + size_elements;
1821b711ef9cSBaptiste Daroussin
1822b711ef9cSBaptiste Daroussin if (desc->bLength == size_total && size_elements != 0)
1823b711ef9cSBaptiste Daroussin return (1);
1824b711ef9cSBaptiste Daroussin
1825b711ef9cSBaptiste Daroussin return (0);
1826b711ef9cSBaptiste Daroussin }
1827b711ef9cSBaptiste Daroussin
1828b711ef9cSBaptiste Daroussin /*
1829b711ef9cSBaptiste Daroussin * Find the best matching resolution for a given format group.
1830b711ef9cSBaptiste Daroussin */
1831b711ef9cSBaptiste Daroussin static void
uvideo_find_res(struct uvideo_softc * sc,int idx,int width,int height,struct uvideo_res * r)1832b711ef9cSBaptiste Daroussin uvideo_find_res(struct uvideo_softc *sc, int idx, int width, int height,
1833b711ef9cSBaptiste Daroussin struct uvideo_res *r)
1834b711ef9cSBaptiste Daroussin {
1835b711ef9cSBaptiste Daroussin int i, w, h, diff, diff_best, size_want, size_is;
1836b711ef9cSBaptiste Daroussin struct usb_video_frame_desc *frame;
1837b711ef9cSBaptiste Daroussin
1838b711ef9cSBaptiste Daroussin size_want = width * height;
1839b711ef9cSBaptiste Daroussin
1840b711ef9cSBaptiste Daroussin for (i = 0; i < sc->sc_fmtgrp[idx].frame_num; i++) {
1841b711ef9cSBaptiste Daroussin frame = sc->sc_fmtgrp[idx].frame[i];
1842b711ef9cSBaptiste Daroussin w = UGETW(UVIDEO_FRAME_FIELD(frame, wWidth));
1843b711ef9cSBaptiste Daroussin h = UGETW(UVIDEO_FRAME_FIELD(frame, wHeight));
1844b711ef9cSBaptiste Daroussin size_is = w * h;
1845b711ef9cSBaptiste Daroussin if (size_is > size_want)
1846b711ef9cSBaptiste Daroussin diff = size_is - size_want;
1847b711ef9cSBaptiste Daroussin else
1848b711ef9cSBaptiste Daroussin diff = size_want - size_is;
1849b711ef9cSBaptiste Daroussin if (i == 0)
1850b711ef9cSBaptiste Daroussin diff_best = diff;
1851b711ef9cSBaptiste Daroussin if (diff <= diff_best) {
1852b711ef9cSBaptiste Daroussin diff_best = diff;
1853b711ef9cSBaptiste Daroussin r->width = w;
1854b711ef9cSBaptiste Daroussin r->height = h;
1855b711ef9cSBaptiste Daroussin r->fidx = i;
1856b711ef9cSBaptiste Daroussin }
1857b711ef9cSBaptiste Daroussin }
1858b711ef9cSBaptiste Daroussin }
1859b711ef9cSBaptiste Daroussin
1860b711ef9cSBaptiste Daroussin /* ---------------------------------------------------------------- */
1861b711ef9cSBaptiste Daroussin /* UVC Protocol (Negotiation, Probe/Commit) */
1862b711ef9cSBaptiste Daroussin /* ---------------------------------------------------------------- */
1863b711ef9cSBaptiste Daroussin
1864b711ef9cSBaptiste Daroussin static usb_error_t
uvideo_vs_negotiation(struct uvideo_softc * sc,int commit)1865b711ef9cSBaptiste Daroussin uvideo_vs_negotiation(struct uvideo_softc *sc, int commit)
1866b711ef9cSBaptiste Daroussin {
1867b711ef9cSBaptiste Daroussin struct usb_video_probe_commit *pc;
1868b711ef9cSBaptiste Daroussin struct uvideo_format_group *fmtgrp;
1869b711ef9cSBaptiste Daroussin struct usb_video_header_desc *hd;
1870b711ef9cSBaptiste Daroussin struct usb_video_frame_desc *frame;
1871b711ef9cSBaptiste Daroussin uint8_t *p, *cur;
1872b711ef9cSBaptiste Daroussin uint8_t probe_data[48];
1873b711ef9cSBaptiste Daroussin uint32_t frame_ival, nivals, min, max, step, diff;
1874b711ef9cSBaptiste Daroussin usb_error_t error;
1875b711ef9cSBaptiste Daroussin int i, ival_bytes, changed = 0;
1876b711ef9cSBaptiste Daroussin size_t len;
1877b711ef9cSBaptiste Daroussin
1878b711ef9cSBaptiste Daroussin pc = (struct usb_video_probe_commit *)probe_data;
1879b711ef9cSBaptiste Daroussin
1880b711ef9cSBaptiste Daroussin fmtgrp = sc->sc_fmtgrp_cur;
1881b711ef9cSBaptiste Daroussin
1882b711ef9cSBaptiste Daroussin if (fmtgrp->frame_num == 0) {
1883b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev,
1884b711ef9cSBaptiste Daroussin "negotiation: no frame descriptors found!\n");
1885b711ef9cSBaptiste Daroussin return (USB_ERR_INVAL);
1886b711ef9cSBaptiste Daroussin }
1887b711ef9cSBaptiste Daroussin
1888b711ef9cSBaptiste Daroussin /* Set probe */
1889b711ef9cSBaptiste Daroussin bzero(probe_data, sizeof(probe_data));
1890b711ef9cSBaptiste Daroussin USETW(pc->bmHint, 0x1);
1891b711ef9cSBaptiste Daroussin pc->bFormatIndex = fmtgrp->format->bFormatIndex;
1892b711ef9cSBaptiste Daroussin pc->bFrameIndex = fmtgrp->frame_cur->bFrameIndex;
1893b711ef9cSBaptiste Daroussin
1894b711ef9cSBaptiste Daroussin frame = fmtgrp->frame_cur;
1895b711ef9cSBaptiste Daroussin frame_ival = UGETDW(UVIDEO_FRAME_FIELD(frame, dwDefaultFrameInterval));
1896b711ef9cSBaptiste Daroussin if (sc->sc_frame_rate != 0) {
1897b711ef9cSBaptiste Daroussin frame_ival = 10000000 / sc->sc_frame_rate;
1898b711ef9cSBaptiste Daroussin /* Find closest matching interval */
1899b711ef9cSBaptiste Daroussin len = UVIDEO_FRAME_MIN_LEN(frame);
1900b711ef9cSBaptiste Daroussin nivals = UVIDEO_FRAME_NUM_INTERVALS(frame);
1901b711ef9cSBaptiste Daroussin p = (uint8_t *)fmtgrp->frame_cur;
1902b711ef9cSBaptiste Daroussin p += len;
1903b711ef9cSBaptiste Daroussin ival_bytes = frame->bLength - len;
1904b711ef9cSBaptiste Daroussin if (!nivals && (ival_bytes >= (int)sizeof(uDWord) * 3)) {
1905b711ef9cSBaptiste Daroussin /* continuous */
1906b711ef9cSBaptiste Daroussin min = UGETDW(p);
1907b711ef9cSBaptiste Daroussin p += sizeof(uDWord);
1908b711ef9cSBaptiste Daroussin max = UGETDW(p);
1909b711ef9cSBaptiste Daroussin p += sizeof(uDWord);
1910b711ef9cSBaptiste Daroussin step = UGETDW(p);
1911b711ef9cSBaptiste Daroussin if (frame_ival <= min)
1912b711ef9cSBaptiste Daroussin frame_ival = min;
1913b711ef9cSBaptiste Daroussin else if (frame_ival >= max)
1914b711ef9cSBaptiste Daroussin frame_ival = max;
1915b711ef9cSBaptiste Daroussin else {
1916b711ef9cSBaptiste Daroussin for (i = min;
1917b711ef9cSBaptiste Daroussin i + step / 2 < frame_ival;
1918b711ef9cSBaptiste Daroussin i += step)
1919b711ef9cSBaptiste Daroussin ;
1920b711ef9cSBaptiste Daroussin frame_ival = i;
1921b711ef9cSBaptiste Daroussin }
1922b711ef9cSBaptiste Daroussin } else if (nivals > 0 &&
1923b711ef9cSBaptiste Daroussin ival_bytes >= (int)sizeof(uDWord)) {
1924b711ef9cSBaptiste Daroussin /* discrete */
1925b711ef9cSBaptiste Daroussin cur = p;
1926b711ef9cSBaptiste Daroussin min = UINT_MAX;
1927b711ef9cSBaptiste Daroussin for (i = 0; i < (int)nivals; i++) {
1928b711ef9cSBaptiste Daroussin if (ival_bytes < (int)sizeof(uDWord))
1929b711ef9cSBaptiste Daroussin break;
1930b711ef9cSBaptiste Daroussin diff = abs((int)UGETDW(p) -
1931b711ef9cSBaptiste Daroussin (int)frame_ival);
1932b711ef9cSBaptiste Daroussin if (diff < min) {
1933b711ef9cSBaptiste Daroussin min = diff;
1934b711ef9cSBaptiste Daroussin cur = p;
1935b711ef9cSBaptiste Daroussin if (diff == 0)
1936b711ef9cSBaptiste Daroussin break;
1937b711ef9cSBaptiste Daroussin }
1938b711ef9cSBaptiste Daroussin p += sizeof(uDWord);
1939b711ef9cSBaptiste Daroussin ival_bytes -= sizeof(uDWord);
1940b711ef9cSBaptiste Daroussin }
1941b711ef9cSBaptiste Daroussin frame_ival = UGETDW(cur);
1942b711ef9cSBaptiste Daroussin }
1943b711ef9cSBaptiste Daroussin }
1944b711ef9cSBaptiste Daroussin USETDW(pc->dwFrameInterval, frame_ival);
1945b711ef9cSBaptiste Daroussin error = uvideo_vs_set_probe(sc, probe_data);
1946b711ef9cSBaptiste Daroussin if (error != USB_ERR_NORMAL_COMPLETION)
1947b711ef9cSBaptiste Daroussin return (error);
1948b711ef9cSBaptiste Daroussin
1949b711ef9cSBaptiste Daroussin /* Get probe */
1950b711ef9cSBaptiste Daroussin bzero(probe_data, sizeof(probe_data));
1951b711ef9cSBaptiste Daroussin error = uvideo_vs_get_probe(sc, probe_data, GET_CUR);
1952b711ef9cSBaptiste Daroussin if (error != USB_ERR_NORMAL_COMPLETION)
1953b711ef9cSBaptiste Daroussin return (error);
1954b711ef9cSBaptiste Daroussin
1955b711ef9cSBaptiste Daroussin /* Check that the format/frame indexes match */
1956b711ef9cSBaptiste Daroussin if (pc->bFormatIndex != fmtgrp->format->bFormatIndex) {
1957b711ef9cSBaptiste Daroussin changed++;
1958b711ef9cSBaptiste Daroussin DPRINTFN(1, "wanted format 0x%x, got 0x%x\n",
1959b711ef9cSBaptiste Daroussin fmtgrp->format->bFormatIndex, pc->bFormatIndex);
1960b711ef9cSBaptiste Daroussin for (i = 0; i < sc->sc_fmtgrp_num; i++) {
1961b711ef9cSBaptiste Daroussin if (sc->sc_fmtgrp[i].format->bFormatIndex ==
1962b711ef9cSBaptiste Daroussin pc->bFormatIndex) {
1963b711ef9cSBaptiste Daroussin fmtgrp = &sc->sc_fmtgrp[i];
1964b711ef9cSBaptiste Daroussin break;
1965b711ef9cSBaptiste Daroussin }
1966b711ef9cSBaptiste Daroussin }
1967b711ef9cSBaptiste Daroussin if (i == sc->sc_fmtgrp_num) {
1968b711ef9cSBaptiste Daroussin DPRINTFN(1, "invalid format index 0x%x\n",
1969b711ef9cSBaptiste Daroussin pc->bFormatIndex);
1970b711ef9cSBaptiste Daroussin return (USB_ERR_INVAL);
1971b711ef9cSBaptiste Daroussin }
1972b711ef9cSBaptiste Daroussin }
1973b711ef9cSBaptiste Daroussin if (pc->bFrameIndex != fmtgrp->frame_cur->bFrameIndex) {
1974b711ef9cSBaptiste Daroussin changed++;
1975b711ef9cSBaptiste Daroussin DPRINTFN(1, "wanted frame 0x%x, got 0x%x\n",
1976b711ef9cSBaptiste Daroussin fmtgrp->frame_cur->bFrameIndex, pc->bFrameIndex);
1977b711ef9cSBaptiste Daroussin for (i = 0; i < fmtgrp->frame_num; i++) {
1978b711ef9cSBaptiste Daroussin if (fmtgrp->frame[i]->bFrameIndex ==
1979b711ef9cSBaptiste Daroussin pc->bFrameIndex) {
1980b711ef9cSBaptiste Daroussin frame = fmtgrp->frame[i];
1981b711ef9cSBaptiste Daroussin break;
1982b711ef9cSBaptiste Daroussin }
1983b711ef9cSBaptiste Daroussin }
1984b711ef9cSBaptiste Daroussin if (i == fmtgrp->frame_num) {
1985b711ef9cSBaptiste Daroussin DPRINTFN(1, "invalid frame index 0x%x\n",
1986b711ef9cSBaptiste Daroussin pc->bFrameIndex);
1987b711ef9cSBaptiste Daroussin return (USB_ERR_INVAL);
1988b711ef9cSBaptiste Daroussin }
1989b711ef9cSBaptiste Daroussin } else
1990b711ef9cSBaptiste Daroussin frame = fmtgrp->frame_cur;
1991b711ef9cSBaptiste Daroussin
1992b711ef9cSBaptiste Daroussin /* Fix uncompressed frame sizes */
1993b711ef9cSBaptiste Daroussin if (frame->bDescriptorSubtype == UDESCSUB_VS_FRAME_UNCOMPRESSED) {
1994b711ef9cSBaptiste Daroussin USETDW(pc->dwMaxVideoFrameSize,
1995b711ef9cSBaptiste Daroussin UGETW(frame->u.uc.wWidth) *
1996b711ef9cSBaptiste Daroussin UGETW(frame->u.uc.wHeight) *
1997b711ef9cSBaptiste Daroussin fmtgrp->format->u.uc.bBitsPerPixel / NBBY);
1998b711ef9cSBaptiste Daroussin } else {
1999b711ef9cSBaptiste Daroussin hd = sc->sc_desc_vc_header.fix;
2000b711ef9cSBaptiste Daroussin if (UGETDW(pc->dwMaxVideoFrameSize) == 0 &&
2001b711ef9cSBaptiste Daroussin UGETW(hd->bcdUVC) < 0x0110) {
2002b711ef9cSBaptiste Daroussin USETDW(pc->dwMaxVideoFrameSize,
2003b711ef9cSBaptiste Daroussin UGETDW(frame->u.uc.dwMaxVideoFrameBufferSize));
2004b711ef9cSBaptiste Daroussin }
2005b711ef9cSBaptiste Daroussin }
2006b711ef9cSBaptiste Daroussin
2007b711ef9cSBaptiste Daroussin /* Commit */
2008b711ef9cSBaptiste Daroussin if (commit) {
2009b711ef9cSBaptiste Daroussin if (changed > 0)
2010b711ef9cSBaptiste Daroussin return (USB_ERR_INVAL);
2011b711ef9cSBaptiste Daroussin error = uvideo_vs_set_commit(sc, probe_data);
2012b711ef9cSBaptiste Daroussin if (error != USB_ERR_NORMAL_COMPLETION)
2013b711ef9cSBaptiste Daroussin return (error);
2014b711ef9cSBaptiste Daroussin }
2015b711ef9cSBaptiste Daroussin
2016b711ef9cSBaptiste Daroussin /* Save a copy of probe commit */
2017b711ef9cSBaptiste Daroussin bcopy(pc, &sc->sc_desc_probe, sizeof(sc->sc_desc_probe));
2018b711ef9cSBaptiste Daroussin
2019b711ef9cSBaptiste Daroussin return (USB_ERR_NORMAL_COMPLETION);
2020b711ef9cSBaptiste Daroussin }
2021b711ef9cSBaptiste Daroussin
2022b711ef9cSBaptiste Daroussin static usb_error_t
uvideo_vs_set_probe(struct uvideo_softc * sc,uint8_t * probe_data)2023b711ef9cSBaptiste Daroussin uvideo_vs_set_probe(struct uvideo_softc *sc, uint8_t *probe_data)
2024b711ef9cSBaptiste Daroussin {
2025b711ef9cSBaptiste Daroussin struct usb_device_request req;
2026b711ef9cSBaptiste Daroussin usb_error_t error;
2027b711ef9cSBaptiste Daroussin uint16_t tmp;
2028b711ef9cSBaptiste Daroussin
2029b711ef9cSBaptiste Daroussin req.bmRequestType = UVIDEO_SET_IF;
2030b711ef9cSBaptiste Daroussin req.bRequest = SET_CUR;
2031b711ef9cSBaptiste Daroussin tmp = VS_PROBE_CONTROL;
2032b711ef9cSBaptiste Daroussin tmp = tmp << 8;
2033b711ef9cSBaptiste Daroussin USETW(req.wValue, tmp);
2034b711ef9cSBaptiste Daroussin USETW(req.wIndex, sc->sc_vs_cur->iface_index);
2035b711ef9cSBaptiste Daroussin USETW(req.wLength, sc->sc_max_ctrl_size);
2036b711ef9cSBaptiste Daroussin
2037b711ef9cSBaptiste Daroussin error = usbd_do_request(sc->sc_udev, NULL, &req, probe_data);
2038b711ef9cSBaptiste Daroussin if (error) {
2039b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev, "could not SET probe: %s\n",
2040b711ef9cSBaptiste Daroussin usbd_errstr(error));
2041b711ef9cSBaptiste Daroussin return (USB_ERR_INVAL);
2042b711ef9cSBaptiste Daroussin }
2043b711ef9cSBaptiste Daroussin
2044b711ef9cSBaptiste Daroussin DPRINTFN(1, "SET probe OK\n");
2045b711ef9cSBaptiste Daroussin return (USB_ERR_NORMAL_COMPLETION);
2046b711ef9cSBaptiste Daroussin }
2047b711ef9cSBaptiste Daroussin
2048b711ef9cSBaptiste Daroussin static usb_error_t
uvideo_vs_get_probe(struct uvideo_softc * sc,uint8_t * probe_data,uint8_t request)2049b711ef9cSBaptiste Daroussin uvideo_vs_get_probe(struct uvideo_softc *sc, uint8_t *probe_data,
2050b711ef9cSBaptiste Daroussin uint8_t request)
2051b711ef9cSBaptiste Daroussin {
2052b711ef9cSBaptiste Daroussin struct usb_device_request req;
2053b711ef9cSBaptiste Daroussin usb_error_t error;
2054b711ef9cSBaptiste Daroussin uint16_t tmp, actlen;
2055b711ef9cSBaptiste Daroussin
2056b711ef9cSBaptiste Daroussin req.bmRequestType = UVIDEO_GET_IF;
2057b711ef9cSBaptiste Daroussin req.bRequest = request;
2058b711ef9cSBaptiste Daroussin tmp = VS_PROBE_CONTROL;
2059b711ef9cSBaptiste Daroussin tmp = tmp << 8;
2060b711ef9cSBaptiste Daroussin USETW(req.wValue, tmp);
2061b711ef9cSBaptiste Daroussin USETW(req.wIndex, sc->sc_vs_cur->iface_index);
2062b711ef9cSBaptiste Daroussin USETW(req.wLength, sc->sc_max_ctrl_size);
2063b711ef9cSBaptiste Daroussin
2064b711ef9cSBaptiste Daroussin error = usbd_do_request_flags(sc->sc_udev, NULL, &req,
2065b711ef9cSBaptiste Daroussin probe_data, USB_SHORT_XFER_OK, &actlen, 5000);
2066b711ef9cSBaptiste Daroussin if (error != USB_ERR_NORMAL_COMPLETION) {
2067b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev, "could not GET probe: %s\n",
2068b711ef9cSBaptiste Daroussin usbd_errstr(error));
2069b711ef9cSBaptiste Daroussin return (USB_ERR_INVAL);
2070b711ef9cSBaptiste Daroussin }
2071b711ef9cSBaptiste Daroussin
2072b711ef9cSBaptiste Daroussin /* Zero unused portion */
2073b711ef9cSBaptiste Daroussin if (actlen < sizeof(struct usb_video_probe_commit))
2074b711ef9cSBaptiste Daroussin bzero(probe_data + actlen,
2075b711ef9cSBaptiste Daroussin sizeof(struct usb_video_probe_commit) - actlen);
2076b711ef9cSBaptiste Daroussin
2077b711ef9cSBaptiste Daroussin DPRINTFN(1, "GET probe OK, length=%d\n", actlen);
2078b711ef9cSBaptiste Daroussin return (USB_ERR_NORMAL_COMPLETION);
2079b711ef9cSBaptiste Daroussin }
2080b711ef9cSBaptiste Daroussin
2081b711ef9cSBaptiste Daroussin static usb_error_t
uvideo_vs_set_commit(struct uvideo_softc * sc,uint8_t * probe_data)2082b711ef9cSBaptiste Daroussin uvideo_vs_set_commit(struct uvideo_softc *sc, uint8_t *probe_data)
2083b711ef9cSBaptiste Daroussin {
2084b711ef9cSBaptiste Daroussin struct usb_device_request req;
2085b711ef9cSBaptiste Daroussin usb_error_t error;
2086b711ef9cSBaptiste Daroussin uint16_t tmp;
2087b711ef9cSBaptiste Daroussin
2088b711ef9cSBaptiste Daroussin req.bmRequestType = UVIDEO_SET_IF;
2089b711ef9cSBaptiste Daroussin req.bRequest = SET_CUR;
2090b711ef9cSBaptiste Daroussin tmp = VS_COMMIT_CONTROL;
2091b711ef9cSBaptiste Daroussin tmp = tmp << 8;
2092b711ef9cSBaptiste Daroussin USETW(req.wValue, tmp);
2093b711ef9cSBaptiste Daroussin USETW(req.wIndex, sc->sc_vs_cur->iface_index);
2094b711ef9cSBaptiste Daroussin USETW(req.wLength, sc->sc_max_ctrl_size);
2095b711ef9cSBaptiste Daroussin
2096b711ef9cSBaptiste Daroussin error = usbd_do_request(sc->sc_udev, NULL, &req, probe_data);
2097b711ef9cSBaptiste Daroussin if (error) {
2098b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev, "could not SET commit: %s\n",
2099b711ef9cSBaptiste Daroussin usbd_errstr(error));
2100b711ef9cSBaptiste Daroussin return (USB_ERR_INVAL);
2101b711ef9cSBaptiste Daroussin }
2102b711ef9cSBaptiste Daroussin
2103b711ef9cSBaptiste Daroussin DPRINTFN(1, "SET commit OK\n");
2104b711ef9cSBaptiste Daroussin return (USB_ERR_NORMAL_COMPLETION);
2105b711ef9cSBaptiste Daroussin }
2106b711ef9cSBaptiste Daroussin
2107b711ef9cSBaptiste Daroussin /* ---------------------------------------------------------------- */
2108b711ef9cSBaptiste Daroussin /* Stream Management */
2109b711ef9cSBaptiste Daroussin /* ---------------------------------------------------------------- */
2110b711ef9cSBaptiste Daroussin
2111b711ef9cSBaptiste Daroussin static usb_error_t
uvideo_vs_alloc_frame(struct uvideo_softc * sc)2112b711ef9cSBaptiste Daroussin uvideo_vs_alloc_frame(struct uvideo_softc *sc)
2113b711ef9cSBaptiste Daroussin {
2114b711ef9cSBaptiste Daroussin struct uvideo_frame_buffer *fb = &sc->sc_frame_buffer;
2115b711ef9cSBaptiste Daroussin
2116b711ef9cSBaptiste Daroussin fb->buf_size = UGETDW(sc->sc_desc_probe.dwMaxVideoFrameSize);
2117b711ef9cSBaptiste Daroussin
2118b711ef9cSBaptiste Daroussin if (sc->sc_max_fbuf_size < fb->buf_size && sc->sc_mmap_flag == 0) {
2119b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev,
2120b711ef9cSBaptiste Daroussin "software video buffer too small!\n");
2121b711ef9cSBaptiste Daroussin return (USB_ERR_NOMEM);
2122b711ef9cSBaptiste Daroussin }
2123b711ef9cSBaptiste Daroussin
2124b711ef9cSBaptiste Daroussin fb->buf = malloc(fb->buf_size, M_USBDEV, M_WAITOK | M_ZERO);
2125b711ef9cSBaptiste Daroussin if (fb->buf == NULL) {
2126b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev,
2127b711ef9cSBaptiste Daroussin "can't allocate frame buffer!\n");
2128b711ef9cSBaptiste Daroussin return (USB_ERR_NOMEM);
2129b711ef9cSBaptiste Daroussin }
2130b711ef9cSBaptiste Daroussin
2131b711ef9cSBaptiste Daroussin DPRINTFN(1, "allocated %d bytes frame buffer\n", fb->buf_size);
2132b711ef9cSBaptiste Daroussin
2133b711ef9cSBaptiste Daroussin fb->sample = 0;
2134b711ef9cSBaptiste Daroussin fb->fid = 0;
2135b711ef9cSBaptiste Daroussin fb->offset = 0;
2136b711ef9cSBaptiste Daroussin fb->error = 0;
2137b711ef9cSBaptiste Daroussin fb->mmap_q_full = 0;
2138b711ef9cSBaptiste Daroussin fb->fmt_flags = sc->sc_fmtgrp_cur->frame_cur->bDescriptorSubtype ==
2139b711ef9cSBaptiste Daroussin UDESCSUB_VS_FRAME_UNCOMPRESSED ? 0 : V4L2_FMT_FLAG_COMPRESSED;
2140b711ef9cSBaptiste Daroussin
2141b711ef9cSBaptiste Daroussin return (USB_ERR_NORMAL_COMPLETION);
2142b711ef9cSBaptiste Daroussin }
2143b711ef9cSBaptiste Daroussin
2144b711ef9cSBaptiste Daroussin static void
uvideo_vs_free_frame(struct uvideo_softc * sc)2145b711ef9cSBaptiste Daroussin uvideo_vs_free_frame(struct uvideo_softc *sc)
2146b711ef9cSBaptiste Daroussin {
2147b711ef9cSBaptiste Daroussin struct uvideo_frame_buffer *fb = &sc->sc_frame_buffer;
2148b711ef9cSBaptiste Daroussin
2149b711ef9cSBaptiste Daroussin if (fb->buf != NULL) {
2150b711ef9cSBaptiste Daroussin free(fb->buf, M_USBDEV);
2151b711ef9cSBaptiste Daroussin fb->buf = NULL;
2152b711ef9cSBaptiste Daroussin }
2153b711ef9cSBaptiste Daroussin
2154b711ef9cSBaptiste Daroussin if (sc->sc_mmap_buffer != NULL) {
2155b711ef9cSBaptiste Daroussin contigfree(sc->sc_mmap_buffer, sc->sc_mmap_buffer_size,
2156b711ef9cSBaptiste Daroussin M_USBDEV);
2157b711ef9cSBaptiste Daroussin sc->sc_mmap_buffer = NULL;
2158b711ef9cSBaptiste Daroussin sc->sc_mmap_buffer_size = 0;
2159b711ef9cSBaptiste Daroussin }
2160b711ef9cSBaptiste Daroussin
2161b711ef9cSBaptiste Daroussin while (!STAILQ_EMPTY(&sc->sc_mmap_q))
2162b711ef9cSBaptiste Daroussin STAILQ_REMOVE_HEAD(&sc->sc_mmap_q, q_frames);
2163b711ef9cSBaptiste Daroussin
2164b711ef9cSBaptiste Daroussin sc->sc_mmap_count = 0;
2165b711ef9cSBaptiste Daroussin }
2166b711ef9cSBaptiste Daroussin
2167b711ef9cSBaptiste Daroussin static usb_error_t
uvideo_vs_open(struct uvideo_softc * sc)2168b711ef9cSBaptiste Daroussin uvideo_vs_open(struct uvideo_softc *sc)
2169b711ef9cSBaptiste Daroussin {
2170b711ef9cSBaptiste Daroussin usb_error_t error;
2171b711ef9cSBaptiste Daroussin uint32_t dwMaxVideoFrameSize;
2172b711ef9cSBaptiste Daroussin uint8_t iface_index;
2173b711ef9cSBaptiste Daroussin
2174b711ef9cSBaptiste Daroussin DPRINTFN(1, "uvideo_vs_open\n");
2175b711ef9cSBaptiste Daroussin
2176b711ef9cSBaptiste Daroussin if (sc->sc_negotiated_flag == 0) {
2177b711ef9cSBaptiste Daroussin error = uvideo_vs_negotiation(sc, 1);
2178b711ef9cSBaptiste Daroussin if (error != USB_ERR_NORMAL_COMPLETION)
2179b711ef9cSBaptiste Daroussin return (error);
2180b711ef9cSBaptiste Daroussin }
2181b711ef9cSBaptiste Daroussin
2182b711ef9cSBaptiste Daroussin /* For bulk endpoints, alt 0 is always used */
2183b711ef9cSBaptiste Daroussin if (!sc->sc_vs_cur->bulk_endpoint) {
2184b711ef9cSBaptiste Daroussin /*
2185b711ef9cSBaptiste Daroussin * Set alternate interface to the one matching
2186b711ef9cSBaptiste Daroussin * dwMaxPayloadTransferSize.
2187b711ef9cSBaptiste Daroussin */
2188b711ef9cSBaptiste Daroussin error = usbd_set_alt_interface_index(sc->sc_udev,
2189b711ef9cSBaptiste Daroussin sc->sc_vs_cur->iface_index, sc->sc_vs_cur->curalt);
2190b711ef9cSBaptiste Daroussin if (error != USB_ERR_NORMAL_COMPLETION) {
2191b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev,
2192b711ef9cSBaptiste Daroussin "could not set alt interface %d!\n",
2193b711ef9cSBaptiste Daroussin sc->sc_vs_cur->curalt);
2194b711ef9cSBaptiste Daroussin return (error);
2195b711ef9cSBaptiste Daroussin }
2196b711ef9cSBaptiste Daroussin }
2197b711ef9cSBaptiste Daroussin
2198b711ef9cSBaptiste Daroussin /*
2199b711ef9cSBaptiste Daroussin * Setup USB transfers. FreeBSD uses declarative config + callback.
2200b711ef9cSBaptiste Daroussin */
2201b711ef9cSBaptiste Daroussin iface_index = sc->sc_vs_cur->iface_index;
2202b711ef9cSBaptiste Daroussin if (sc->sc_vs_cur->bulk_endpoint) {
2203b711ef9cSBaptiste Daroussin error = usbd_transfer_setup(sc->sc_udev, &iface_index,
2204b711ef9cSBaptiste Daroussin sc->sc_xfer, uvideo_bulk_config, 1, sc, &sc->sc_mtx);
2205b711ef9cSBaptiste Daroussin } else {
2206b711ef9cSBaptiste Daroussin error = usbd_transfer_setup(sc->sc_udev, &iface_index,
2207b711ef9cSBaptiste Daroussin sc->sc_xfer, uvideo_isoc_config, UVIDEO_IXFERS, sc,
2208b711ef9cSBaptiste Daroussin &sc->sc_mtx);
2209b711ef9cSBaptiste Daroussin }
2210b711ef9cSBaptiste Daroussin if (error != USB_ERR_NORMAL_COMPLETION) {
2211b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev, "transfer setup failed: %s\n",
2212b711ef9cSBaptiste Daroussin usbd_errstr(error));
2213b711ef9cSBaptiste Daroussin return (error);
2214b711ef9cSBaptiste Daroussin }
2215b711ef9cSBaptiste Daroussin
2216b711ef9cSBaptiste Daroussin /* Calculate optimal isoc transfer size */
2217b711ef9cSBaptiste Daroussin dwMaxVideoFrameSize = UGETDW(sc->sc_desc_probe.dwMaxVideoFrameSize);
2218b711ef9cSBaptiste Daroussin sc->sc_nframes = (dwMaxVideoFrameSize + sc->sc_vs_cur->psize - 1) /
2219b711ef9cSBaptiste Daroussin sc->sc_vs_cur->psize;
2220b711ef9cSBaptiste Daroussin if (sc->sc_nframes > UVIDEO_NFRAMES_MAX)
2221b711ef9cSBaptiste Daroussin sc->sc_nframes = UVIDEO_NFRAMES_MAX;
2222b711ef9cSBaptiste Daroussin
2223b711ef9cSBaptiste Daroussin /* Pre-allocate scratch buffer for bulk USB callbacks */
2224b711ef9cSBaptiste Daroussin if (sc->sc_vs_cur->bulk_endpoint) {
2225b711ef9cSBaptiste Daroussin sc->sc_tmpbuf_size = 65536;
2226b711ef9cSBaptiste Daroussin sc->sc_tmpbuf = malloc(sc->sc_tmpbuf_size, M_USBDEV, M_WAITOK);
2227b711ef9cSBaptiste Daroussin }
2228b711ef9cSBaptiste Daroussin
2229b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev, "stream open: nframes=%d, psize=%u, "
2230b711ef9cSBaptiste Daroussin "maxVideoFrameSize=%u, maxPayloadSize=%u, alt=%d, %s\n",
2231b711ef9cSBaptiste Daroussin sc->sc_nframes, sc->sc_vs_cur->psize,
2232b711ef9cSBaptiste Daroussin UGETDW(sc->sc_desc_probe.dwMaxVideoFrameSize),
2233b711ef9cSBaptiste Daroussin UGETDW(sc->sc_desc_probe.dwMaxPayloadTransferSize),
2234b711ef9cSBaptiste Daroussin sc->sc_vs_cur->curalt,
2235b711ef9cSBaptiste Daroussin sc->sc_vs_cur->bulk_endpoint ? "bulk" : "isoc");
2236b711ef9cSBaptiste Daroussin
2237b711ef9cSBaptiste Daroussin return (USB_ERR_NORMAL_COMPLETION);
2238b711ef9cSBaptiste Daroussin }
2239b711ef9cSBaptiste Daroussin
2240b711ef9cSBaptiste Daroussin static void
uvideo_vs_close(struct uvideo_softc * sc)2241b711ef9cSBaptiste Daroussin uvideo_vs_close(struct uvideo_softc *sc)
2242b711ef9cSBaptiste Daroussin {
2243b711ef9cSBaptiste Daroussin
2244b711ef9cSBaptiste Daroussin DPRINTFN(1, "uvideo_vs_close\n");
2245b711ef9cSBaptiste Daroussin
2246b711ef9cSBaptiste Daroussin /* Stop and drain all transfers */
2247b711ef9cSBaptiste Daroussin usbd_transfer_unsetup(sc->sc_xfer, UVIDEO_N_XFER);
2248b711ef9cSBaptiste Daroussin
2249b711ef9cSBaptiste Daroussin if (sc->sc_tmpbuf != NULL) {
2250b711ef9cSBaptiste Daroussin free(sc->sc_tmpbuf, M_USBDEV);
2251b711ef9cSBaptiste Daroussin sc->sc_tmpbuf = NULL;
2252b711ef9cSBaptiste Daroussin sc->sc_tmpbuf_size = 0;
2253b711ef9cSBaptiste Daroussin }
2254b711ef9cSBaptiste Daroussin
2255b711ef9cSBaptiste Daroussin if (sc->sc_dying)
2256b711ef9cSBaptiste Daroussin return;
2257b711ef9cSBaptiste Daroussin
2258b711ef9cSBaptiste Daroussin if (!sc->sc_vs_cur->bulk_endpoint) {
2259b711ef9cSBaptiste Daroussin /* Switch back to alt 0 (turns off camera LED) */
2260b711ef9cSBaptiste Daroussin usbd_set_alt_interface_index(sc->sc_udev,
2261b711ef9cSBaptiste Daroussin sc->sc_vs_cur->iface_index, 0);
2262b711ef9cSBaptiste Daroussin }
2263b711ef9cSBaptiste Daroussin }
2264b711ef9cSBaptiste Daroussin
2265b711ef9cSBaptiste Daroussin static usb_error_t
uvideo_vs_init(struct uvideo_softc * sc)2266b711ef9cSBaptiste Daroussin uvideo_vs_init(struct uvideo_softc *sc)
2267b711ef9cSBaptiste Daroussin {
2268b711ef9cSBaptiste Daroussin usb_error_t error;
2269b711ef9cSBaptiste Daroussin
2270b711ef9cSBaptiste Daroussin error = uvideo_vs_open(sc);
2271b711ef9cSBaptiste Daroussin if (error != USB_ERR_NORMAL_COMPLETION)
2272b711ef9cSBaptiste Daroussin return (USB_ERR_INVAL);
2273b711ef9cSBaptiste Daroussin
2274b711ef9cSBaptiste Daroussin error = uvideo_vs_alloc_frame(sc);
2275b711ef9cSBaptiste Daroussin if (error != USB_ERR_NORMAL_COMPLETION)
2276b711ef9cSBaptiste Daroussin return (USB_ERR_INVAL);
2277b711ef9cSBaptiste Daroussin
2278b711ef9cSBaptiste Daroussin return (USB_ERR_NORMAL_COMPLETION);
2279b711ef9cSBaptiste Daroussin }
2280b711ef9cSBaptiste Daroussin
2281b711ef9cSBaptiste Daroussin /* ---------------------------------------------------------------- */
2282b711ef9cSBaptiste Daroussin /* Transfer Callbacks */
2283b711ef9cSBaptiste Daroussin /* ---------------------------------------------------------------- */
2284b711ef9cSBaptiste Daroussin
2285b711ef9cSBaptiste Daroussin /*
2286b711ef9cSBaptiste Daroussin * Zero-copy isochronous decode: read only the 2-byte UVC stream header
2287b711ef9cSBaptiste Daroussin * from the USB page cache, then copy the payload directly into the
2288b711ef9cSBaptiste Daroussin * destination frame buffer, skipping the intermediate staging buffer.
2289b711ef9cSBaptiste Daroussin */
2290b711ef9cSBaptiste Daroussin static void
uvideo_isoc_decode(struct uvideo_softc * sc,struct usb_page_cache * pc,int offset,int len)2291b711ef9cSBaptiste Daroussin uvideo_isoc_decode(struct uvideo_softc *sc, struct usb_page_cache *pc,
2292b711ef9cSBaptiste Daroussin int offset, int len)
2293b711ef9cSBaptiste Daroussin {
2294b711ef9cSBaptiste Daroussin struct uvideo_frame_buffer *fb = &sc->sc_frame_buffer;
2295b711ef9cSBaptiste Daroussin uint8_t shdr[2];
2296b711ef9cSBaptiste Daroussin uint8_t flags;
2297b711ef9cSBaptiste Daroussin uint8_t *buf;
2298b711ef9cSBaptiste Daroussin int hdrlen, payload_len;
2299b711ef9cSBaptiste Daroussin
2300b711ef9cSBaptiste Daroussin if (len < UVIDEO_SH_MIN_LEN)
2301b711ef9cSBaptiste Daroussin return;
2302b711ef9cSBaptiste Daroussin
2303b711ef9cSBaptiste Daroussin /* Read only bLength and bFlags (2 bytes) */
2304b711ef9cSBaptiste Daroussin usbd_copy_out(pc, offset, shdr, sizeof(shdr));
2305b711ef9cSBaptiste Daroussin
2306b711ef9cSBaptiste Daroussin hdrlen = shdr[0];
2307b711ef9cSBaptiste Daroussin flags = shdr[1];
2308b711ef9cSBaptiste Daroussin if (hdrlen > len || hdrlen < UVIDEO_SH_MIN_LEN)
2309b711ef9cSBaptiste Daroussin return;
2310b711ef9cSBaptiste Daroussin
2311b711ef9cSBaptiste Daroussin if (fb->sample == 0) {
2312b711ef9cSBaptiste Daroussin fb->sample = 1;
2313b711ef9cSBaptiste Daroussin fb->fid = flags & UVIDEO_SH_FLAG_FID;
2314b711ef9cSBaptiste Daroussin fb->offset = 0;
2315b711ef9cSBaptiste Daroussin fb->error = 0;
2316b711ef9cSBaptiste Daroussin fb->mmap_q_full = 0;
2317b711ef9cSBaptiste Daroussin } else if (fb->fid != (flags & UVIDEO_SH_FLAG_FID)) {
2318b711ef9cSBaptiste Daroussin DPRINTFN(1, "wrong FID, ignoring last frame\n");
2319b711ef9cSBaptiste Daroussin fb->sample = 1;
2320b711ef9cSBaptiste Daroussin fb->fid = flags & UVIDEO_SH_FLAG_FID;
2321b711ef9cSBaptiste Daroussin fb->offset = 0;
2322b711ef9cSBaptiste Daroussin fb->error = 0;
2323b711ef9cSBaptiste Daroussin fb->mmap_q_full = 0;
2324b711ef9cSBaptiste Daroussin }
2325b711ef9cSBaptiste Daroussin
2326b711ef9cSBaptiste Daroussin if (flags & UVIDEO_SH_FLAG_ERR) {
2327b711ef9cSBaptiste Daroussin DPRINTFN(1, "stream error!\n");
2328b711ef9cSBaptiste Daroussin fb->error = 1;
2329b711ef9cSBaptiste Daroussin }
2330b711ef9cSBaptiste Daroussin
2331b711ef9cSBaptiste Daroussin /* Get destination buffer */
2332b711ef9cSBaptiste Daroussin if (sc->sc_mmap_flag) {
2333b711ef9cSBaptiste Daroussin if (!fb->mmap_q_full) {
2334b711ef9cSBaptiste Daroussin buf = uvideo_mmap_getbuf(sc);
2335b711ef9cSBaptiste Daroussin if (buf == NULL)
2336b711ef9cSBaptiste Daroussin fb->mmap_q_full = 1;
2337b711ef9cSBaptiste Daroussin }
2338b711ef9cSBaptiste Daroussin } else
2339b711ef9cSBaptiste Daroussin buf = fb->buf;
2340b711ef9cSBaptiste Daroussin
2341b711ef9cSBaptiste Daroussin /* Copy payload directly from USB DMA into frame buffer */
2342b711ef9cSBaptiste Daroussin payload_len = len - hdrlen;
2343b711ef9cSBaptiste Daroussin if (payload_len > fb->buf_size - fb->offset) {
2344b711ef9cSBaptiste Daroussin DPRINTFN(1, "frame too large, marked as error\n");
2345b711ef9cSBaptiste Daroussin payload_len = fb->buf_size - fb->offset;
2346b711ef9cSBaptiste Daroussin fb->error = 1;
2347b711ef9cSBaptiste Daroussin }
2348b711ef9cSBaptiste Daroussin if (!fb->mmap_q_full && payload_len > 0) {
2349b711ef9cSBaptiste Daroussin usbd_copy_out(pc, offset + hdrlen,
2350b711ef9cSBaptiste Daroussin buf + fb->offset, payload_len);
2351b711ef9cSBaptiste Daroussin fb->offset += payload_len;
2352b711ef9cSBaptiste Daroussin }
2353b711ef9cSBaptiste Daroussin
2354b711ef9cSBaptiste Daroussin if (flags & UVIDEO_SH_FLAG_EOF) {
2355b711ef9cSBaptiste Daroussin DPRINTFN(2, "EOF (frame size=%d bytes)\n", fb->offset);
2356b711ef9cSBaptiste Daroussin
2357b711ef9cSBaptiste Daroussin if (fb->offset < fb->buf_size &&
2358b711ef9cSBaptiste Daroussin !(fb->fmt_flags & V4L2_FMT_FLAG_COMPRESSED)) {
2359b711ef9cSBaptiste Daroussin DPRINTFN(1, "frame too small, marked as error\n");
2360b711ef9cSBaptiste Daroussin fb->error = 1;
2361b711ef9cSBaptiste Daroussin }
2362b711ef9cSBaptiste Daroussin
2363b711ef9cSBaptiste Daroussin if (sc->sc_mmap_flag) {
2364b711ef9cSBaptiste Daroussin if (!fb->mmap_q_full)
2365b711ef9cSBaptiste Daroussin uvideo_mmap_queue(sc, fb->offset, fb->error);
2366b711ef9cSBaptiste Daroussin } else if (fb->error) {
2367b711ef9cSBaptiste Daroussin DPRINTFN(1, "error frame, skipped\n");
2368b711ef9cSBaptiste Daroussin } else {
2369b711ef9cSBaptiste Daroussin uvideo_read_frame(sc, fb->buf, fb->offset);
2370b711ef9cSBaptiste Daroussin }
2371b711ef9cSBaptiste Daroussin
2372b711ef9cSBaptiste Daroussin fb->sample = 0;
2373b711ef9cSBaptiste Daroussin fb->fid = 0;
2374b711ef9cSBaptiste Daroussin fb->error = 0;
2375b711ef9cSBaptiste Daroussin fb->mmap_q_full = 0;
2376b711ef9cSBaptiste Daroussin }
2377b711ef9cSBaptiste Daroussin }
2378b711ef9cSBaptiste Daroussin
2379b711ef9cSBaptiste Daroussin static void
uvideo_isoc_callback(struct usb_xfer * xfer,usb_error_t error)2380b711ef9cSBaptiste Daroussin uvideo_isoc_callback(struct usb_xfer *xfer, usb_error_t error)
2381b711ef9cSBaptiste Daroussin {
2382b711ef9cSBaptiste Daroussin struct uvideo_softc *sc = usbd_xfer_softc(xfer);
2383b711ef9cSBaptiste Daroussin struct usb_page_cache *pc;
2384b711ef9cSBaptiste Daroussin int nframes, i, offset, len;
2385b711ef9cSBaptiste Daroussin
2386b711ef9cSBaptiste Daroussin switch (USB_GET_STATE(xfer)) {
2387b711ef9cSBaptiste Daroussin case USB_ST_TRANSFERRED:
2388b711ef9cSBaptiste Daroussin pc = usbd_xfer_get_frame(xfer, 0);
2389b711ef9cSBaptiste Daroussin nframes = usbd_xfer_max_frames(xfer);
2390b711ef9cSBaptiste Daroussin offset = 0;
2391b711ef9cSBaptiste Daroussin for (i = 0; i < nframes; i++) {
2392b711ef9cSBaptiste Daroussin len = usbd_xfer_frame_len(xfer, i);
2393b711ef9cSBaptiste Daroussin if (len > 0)
2394b711ef9cSBaptiste Daroussin uvideo_isoc_decode(sc, pc, offset, len);
2395b711ef9cSBaptiste Daroussin offset += usbd_xfer_old_frame_length(xfer, i);
2396b711ef9cSBaptiste Daroussin }
2397b711ef9cSBaptiste Daroussin /* FALLTHROUGH */
2398b711ef9cSBaptiste Daroussin case USB_ST_SETUP:
2399b711ef9cSBaptiste Daroussin tr_setup:
2400b711ef9cSBaptiste Daroussin nframes = usbd_xfer_max_frames(xfer);
2401b711ef9cSBaptiste Daroussin usbd_xfer_set_frames(xfer, nframes);
2402b711ef9cSBaptiste Daroussin for (i = 0; i < nframes; i++)
2403b711ef9cSBaptiste Daroussin usbd_xfer_set_frame_len(xfer, i,
2404b711ef9cSBaptiste Daroussin sc->sc_vs_cur->psize);
2405b711ef9cSBaptiste Daroussin usbd_transfer_submit(xfer);
2406b711ef9cSBaptiste Daroussin break;
2407b711ef9cSBaptiste Daroussin default:
2408b711ef9cSBaptiste Daroussin if (error != USB_ERR_CANCELLED) {
2409b711ef9cSBaptiste Daroussin usbd_xfer_set_stall(xfer);
2410b711ef9cSBaptiste Daroussin goto tr_setup;
2411b711ef9cSBaptiste Daroussin }
2412b711ef9cSBaptiste Daroussin break;
2413b711ef9cSBaptiste Daroussin }
2414b711ef9cSBaptiste Daroussin }
2415b711ef9cSBaptiste Daroussin
2416b711ef9cSBaptiste Daroussin static void
uvideo_bulk_callback(struct usb_xfer * xfer,usb_error_t error)2417b711ef9cSBaptiste Daroussin uvideo_bulk_callback(struct usb_xfer *xfer, usb_error_t error)
2418b711ef9cSBaptiste Daroussin {
2419b711ef9cSBaptiste Daroussin struct uvideo_softc *sc = usbd_xfer_softc(xfer);
2420b711ef9cSBaptiste Daroussin struct usb_page_cache *pc;
2421b711ef9cSBaptiste Daroussin int actlen;
2422b711ef9cSBaptiste Daroussin
2423b711ef9cSBaptiste Daroussin usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
2424b711ef9cSBaptiste Daroussin
2425b711ef9cSBaptiste Daroussin switch (USB_GET_STATE(xfer)) {
2426b711ef9cSBaptiste Daroussin case USB_ST_TRANSFERRED:
2427b711ef9cSBaptiste Daroussin if (actlen > 0 && actlen <= sc->sc_tmpbuf_size) {
2428b711ef9cSBaptiste Daroussin pc = usbd_xfer_get_frame(xfer, 0);
2429b711ef9cSBaptiste Daroussin usbd_copy_out(pc, 0, sc->sc_tmpbuf, actlen);
2430b711ef9cSBaptiste Daroussin sc->sc_decode_stream_header(sc, sc->sc_tmpbuf,
2431b711ef9cSBaptiste Daroussin actlen);
2432b711ef9cSBaptiste Daroussin }
2433b711ef9cSBaptiste Daroussin /* FALLTHROUGH */
2434b711ef9cSBaptiste Daroussin case USB_ST_SETUP:
2435b711ef9cSBaptiste Daroussin tr_setup:
2436b711ef9cSBaptiste Daroussin usbd_xfer_set_frame_len(xfer, 0,
2437b711ef9cSBaptiste Daroussin usbd_xfer_max_len(xfer));
2438b711ef9cSBaptiste Daroussin usbd_transfer_submit(xfer);
2439b711ef9cSBaptiste Daroussin break;
2440b711ef9cSBaptiste Daroussin default:
2441b711ef9cSBaptiste Daroussin if (error != USB_ERR_CANCELLED) {
2442b711ef9cSBaptiste Daroussin usbd_xfer_set_stall(xfer);
2443b711ef9cSBaptiste Daroussin goto tr_setup;
2444b711ef9cSBaptiste Daroussin }
2445b711ef9cSBaptiste Daroussin break;
2446b711ef9cSBaptiste Daroussin }
2447b711ef9cSBaptiste Daroussin }
2448b711ef9cSBaptiste Daroussin
2449b711ef9cSBaptiste Daroussin /* ---------------------------------------------------------------- */
2450b711ef9cSBaptiste Daroussin /* Frame Assembly */
2451b711ef9cSBaptiste Daroussin /* ---------------------------------------------------------------- */
2452b711ef9cSBaptiste Daroussin
2453b711ef9cSBaptiste Daroussin static void
uvideo_vs_decode_stream_header(struct uvideo_softc * sc,uint8_t * frame,int frame_size)2454b711ef9cSBaptiste Daroussin uvideo_vs_decode_stream_header(struct uvideo_softc *sc, uint8_t *frame,
2455b711ef9cSBaptiste Daroussin int frame_size)
2456b711ef9cSBaptiste Daroussin {
2457b711ef9cSBaptiste Daroussin struct uvideo_frame_buffer *fb = &sc->sc_frame_buffer;
2458b711ef9cSBaptiste Daroussin struct usb_video_stream_header *sh;
2459b711ef9cSBaptiste Daroussin int sample_len;
2460b711ef9cSBaptiste Daroussin uint8_t *buf;
2461b711ef9cSBaptiste Daroussin
2462b711ef9cSBaptiste Daroussin if (frame_size < UVIDEO_SH_MIN_LEN)
2463b711ef9cSBaptiste Daroussin return;
2464b711ef9cSBaptiste Daroussin
2465b711ef9cSBaptiste Daroussin sh = (struct usb_video_stream_header *)frame;
2466b711ef9cSBaptiste Daroussin
2467b711ef9cSBaptiste Daroussin if (sh->bLength > frame_size || sh->bLength < UVIDEO_SH_MIN_LEN)
2468b711ef9cSBaptiste Daroussin return;
2469b711ef9cSBaptiste Daroussin
2470b711ef9cSBaptiste Daroussin if (fb->sample == 0) {
2471b711ef9cSBaptiste Daroussin /* First sample for a frame */
2472b711ef9cSBaptiste Daroussin fb->sample = 1;
2473b711ef9cSBaptiste Daroussin fb->fid = sh->bFlags & UVIDEO_SH_FLAG_FID;
2474b711ef9cSBaptiste Daroussin fb->offset = 0;
2475b711ef9cSBaptiste Daroussin fb->error = 0;
2476b711ef9cSBaptiste Daroussin fb->mmap_q_full = 0;
2477b711ef9cSBaptiste Daroussin } else {
2478b711ef9cSBaptiste Daroussin /* Continuation sample; check FID consistency */
2479b711ef9cSBaptiste Daroussin if (fb->fid != (sh->bFlags & UVIDEO_SH_FLAG_FID)) {
2480b711ef9cSBaptiste Daroussin DPRINTFN(1, "wrong FID, ignoring last frame\n");
2481b711ef9cSBaptiste Daroussin fb->sample = 1;
2482b711ef9cSBaptiste Daroussin fb->fid = sh->bFlags & UVIDEO_SH_FLAG_FID;
2483b711ef9cSBaptiste Daroussin fb->offset = 0;
2484b711ef9cSBaptiste Daroussin fb->error = 0;
2485b711ef9cSBaptiste Daroussin fb->mmap_q_full = 0;
2486b711ef9cSBaptiste Daroussin }
2487b711ef9cSBaptiste Daroussin }
2488b711ef9cSBaptiste Daroussin
2489b711ef9cSBaptiste Daroussin if (sh->bFlags & UVIDEO_SH_FLAG_ERR) {
2490b711ef9cSBaptiste Daroussin DPRINTFN(1, "stream error!\n");
2491b711ef9cSBaptiste Daroussin fb->error = 1;
2492b711ef9cSBaptiste Daroussin }
2493b711ef9cSBaptiste Daroussin
2494b711ef9cSBaptiste Daroussin if (sc->sc_mmap_flag) {
2495b711ef9cSBaptiste Daroussin if (!fb->mmap_q_full) {
2496b711ef9cSBaptiste Daroussin buf = uvideo_mmap_getbuf(sc);
2497b711ef9cSBaptiste Daroussin if (buf == NULL)
2498b711ef9cSBaptiste Daroussin fb->mmap_q_full = 1;
2499b711ef9cSBaptiste Daroussin }
2500b711ef9cSBaptiste Daroussin } else
2501b711ef9cSBaptiste Daroussin buf = sc->sc_frame_buffer.buf;
2502b711ef9cSBaptiste Daroussin
2503b711ef9cSBaptiste Daroussin /* Save sample data */
2504b711ef9cSBaptiste Daroussin sample_len = frame_size - sh->bLength;
2505b711ef9cSBaptiste Daroussin if (sample_len > fb->buf_size - fb->offset) {
2506b711ef9cSBaptiste Daroussin DPRINTFN(1, "frame too large, marked as error\n");
2507b711ef9cSBaptiste Daroussin sample_len = fb->buf_size - fb->offset;
2508b711ef9cSBaptiste Daroussin fb->error = 1;
2509b711ef9cSBaptiste Daroussin }
2510b711ef9cSBaptiste Daroussin if (!fb->mmap_q_full && sample_len > 0) {
2511b711ef9cSBaptiste Daroussin bcopy(frame + sh->bLength, buf + fb->offset, sample_len);
2512b711ef9cSBaptiste Daroussin fb->offset += sample_len;
2513b711ef9cSBaptiste Daroussin }
2514b711ef9cSBaptiste Daroussin
2515b711ef9cSBaptiste Daroussin if (sh->bFlags & UVIDEO_SH_FLAG_EOF) {
2516b711ef9cSBaptiste Daroussin /* Got a full frame */
2517b711ef9cSBaptiste Daroussin DPRINTFN(2, "EOF (frame size=%d bytes)\n", fb->offset);
2518b711ef9cSBaptiste Daroussin
2519b711ef9cSBaptiste Daroussin if (fb->offset < fb->buf_size &&
2520b711ef9cSBaptiste Daroussin !(fb->fmt_flags & V4L2_FMT_FLAG_COMPRESSED)) {
2521b711ef9cSBaptiste Daroussin DPRINTFN(1, "frame too small, marked as error\n");
2522b711ef9cSBaptiste Daroussin fb->error = 1;
2523b711ef9cSBaptiste Daroussin }
2524b711ef9cSBaptiste Daroussin
2525b711ef9cSBaptiste Daroussin if (sc->sc_mmap_flag) {
2526b711ef9cSBaptiste Daroussin if (!fb->mmap_q_full)
2527b711ef9cSBaptiste Daroussin uvideo_mmap_queue(sc, fb->offset, fb->error);
2528b711ef9cSBaptiste Daroussin } else if (fb->error) {
2529b711ef9cSBaptiste Daroussin DPRINTFN(1, "error frame, skipped\n");
2530b711ef9cSBaptiste Daroussin } else {
2531b711ef9cSBaptiste Daroussin uvideo_read_frame(sc, fb->buf, fb->offset);
2532b711ef9cSBaptiste Daroussin }
2533b711ef9cSBaptiste Daroussin
2534b711ef9cSBaptiste Daroussin fb->sample = 0;
2535b711ef9cSBaptiste Daroussin fb->fid = 0;
2536b711ef9cSBaptiste Daroussin fb->error = 0;
2537b711ef9cSBaptiste Daroussin fb->mmap_q_full = 0;
2538b711ef9cSBaptiste Daroussin }
2539b711ef9cSBaptiste Daroussin }
2540b711ef9cSBaptiste Daroussin
2541b711ef9cSBaptiste Daroussin static uint8_t *
uvideo_mmap_getbuf(struct uvideo_softc * sc)2542b711ef9cSBaptiste Daroussin uvideo_mmap_getbuf(struct uvideo_softc *sc)
2543b711ef9cSBaptiste Daroussin {
2544b711ef9cSBaptiste Daroussin int i, idx;
2545b711ef9cSBaptiste Daroussin
2546b711ef9cSBaptiste Daroussin /*
2547b711ef9cSBaptiste Daroussin * Multiple frames per transfer / multiple transfers per frame.
2548b711ef9cSBaptiste Daroussin */
2549b711ef9cSBaptiste Daroussin if (sc->sc_mmap_cur != NULL)
2550b711ef9cSBaptiste Daroussin return (sc->sc_mmap_cur->buf);
2551b711ef9cSBaptiste Daroussin
2552b711ef9cSBaptiste Daroussin if (sc->sc_mmap_count == 0 || sc->sc_mmap_buffer == NULL)
2553b711ef9cSBaptiste Daroussin return (NULL);
2554b711ef9cSBaptiste Daroussin
2555b711ef9cSBaptiste Daroussin idx = sc->sc_mmap_buffer_idx;
2556b711ef9cSBaptiste Daroussin
2557b711ef9cSBaptiste Daroussin /* Find a buffer which is queued and ready */
2558b711ef9cSBaptiste Daroussin for (i = 0; i < sc->sc_mmap_count; i++) {
2559b711ef9cSBaptiste Daroussin if (sc->sc_mmap[sc->sc_mmap_buffer_idx].v4l2_buf.flags &
2560b711ef9cSBaptiste Daroussin V4L2_BUF_FLAG_QUEUED) {
2561b711ef9cSBaptiste Daroussin idx = sc->sc_mmap_buffer_idx;
2562b711ef9cSBaptiste Daroussin if (++sc->sc_mmap_buffer_idx == sc->sc_mmap_count)
2563b711ef9cSBaptiste Daroussin sc->sc_mmap_buffer_idx = 0;
2564b711ef9cSBaptiste Daroussin break;
2565b711ef9cSBaptiste Daroussin }
2566b711ef9cSBaptiste Daroussin if (++sc->sc_mmap_buffer_idx == sc->sc_mmap_count)
2567b711ef9cSBaptiste Daroussin sc->sc_mmap_buffer_idx = 0;
2568b711ef9cSBaptiste Daroussin }
2569b711ef9cSBaptiste Daroussin
2570b711ef9cSBaptiste Daroussin if (i == sc->sc_mmap_count) {
2571b711ef9cSBaptiste Daroussin DPRINTFN(1, "mmap queue is full!\n");
2572b711ef9cSBaptiste Daroussin return (NULL);
2573b711ef9cSBaptiste Daroussin }
2574b711ef9cSBaptiste Daroussin
2575b711ef9cSBaptiste Daroussin sc->sc_mmap_cur = &sc->sc_mmap[idx];
2576b711ef9cSBaptiste Daroussin return (sc->sc_mmap_cur->buf);
2577b711ef9cSBaptiste Daroussin }
2578b711ef9cSBaptiste Daroussin
2579b711ef9cSBaptiste Daroussin static void
uvideo_mmap_queue(struct uvideo_softc * sc,int len,int err)2580b711ef9cSBaptiste Daroussin uvideo_mmap_queue(struct uvideo_softc *sc, int len, int err)
2581b711ef9cSBaptiste Daroussin {
2582b711ef9cSBaptiste Daroussin
2583b711ef9cSBaptiste Daroussin if (sc->sc_mmap_cur == NULL)
2584b711ef9cSBaptiste Daroussin return;
2585b711ef9cSBaptiste Daroussin
2586b711ef9cSBaptiste Daroussin sc->sc_mmap_cur->v4l2_buf.bytesused = len;
2587b711ef9cSBaptiste Daroussin
2588b711ef9cSBaptiste Daroussin getmicrouptime(&sc->sc_mmap_cur->v4l2_buf.timestamp);
2589b711ef9cSBaptiste Daroussin sc->sc_mmap_cur->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TIMESTAMP_MASK;
2590b711ef9cSBaptiste Daroussin sc->sc_mmap_cur->v4l2_buf.flags |= V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
2591b711ef9cSBaptiste Daroussin sc->sc_mmap_cur->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
2592b711ef9cSBaptiste Daroussin sc->sc_mmap_cur->v4l2_buf.flags |= V4L2_BUF_FLAG_TSTAMP_SRC_EOF;
2593b711ef9cSBaptiste Daroussin sc->sc_mmap_cur->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TIMECODE;
2594b711ef9cSBaptiste Daroussin
2595b711ef9cSBaptiste Daroussin sc->sc_mmap_cur->v4l2_buf.flags &= ~V4L2_BUF_FLAG_ERROR;
2596b711ef9cSBaptiste Daroussin if (err)
2597b711ef9cSBaptiste Daroussin sc->sc_mmap_cur->v4l2_buf.flags |= V4L2_BUF_FLAG_ERROR;
2598b711ef9cSBaptiste Daroussin
2599b711ef9cSBaptiste Daroussin sc->sc_mmap_cur->v4l2_buf.flags |= V4L2_BUF_FLAG_DONE;
2600b711ef9cSBaptiste Daroussin sc->sc_mmap_cur->v4l2_buf.flags &= ~V4L2_BUF_FLAG_QUEUED;
2601b711ef9cSBaptiste Daroussin STAILQ_INSERT_TAIL(&sc->sc_mmap_q, sc->sc_mmap_cur, q_frames);
2602b711ef9cSBaptiste Daroussin sc->sc_mmap_cur = NULL;
2603b711ef9cSBaptiste Daroussin
2604b711ef9cSBaptiste Daroussin DPRINTFN(2, "frame queued\n");
2605b711ef9cSBaptiste Daroussin
2606b711ef9cSBaptiste Daroussin wakeup(sc);
2607b711ef9cSBaptiste Daroussin selwakeup(&sc->sc_selinfo);
260854df18cbSBaptiste Daroussin KNOTE_LOCKED(&sc->sc_selinfo.si_note, 0);
2609b711ef9cSBaptiste Daroussin }
2610b711ef9cSBaptiste Daroussin
2611b711ef9cSBaptiste Daroussin static void
uvideo_read_frame(struct uvideo_softc * sc,uint8_t * buf,int len)2612b711ef9cSBaptiste Daroussin uvideo_read_frame(struct uvideo_softc *sc, uint8_t *buf, int len)
2613b711ef9cSBaptiste Daroussin {
2614b711ef9cSBaptiste Daroussin
2615b711ef9cSBaptiste Daroussin /*
2616b711ef9cSBaptiste Daroussin * In read mode, copy the frame into the upper-layer buffer
2617b711ef9cSBaptiste Daroussin * so the USB callback can start assembling the next frame
2618b711ef9cSBaptiste Daroussin * without racing with the cdev read.
2619b711ef9cSBaptiste Daroussin */
2620b711ef9cSBaptiste Daroussin if (sc->sc_fbuffer == NULL || len > sc->sc_fbufferlen)
2621b711ef9cSBaptiste Daroussin return;
2622b711ef9cSBaptiste Daroussin
2623b711ef9cSBaptiste Daroussin bcopy(buf, sc->sc_fbuffer, len);
2624b711ef9cSBaptiste Daroussin sc->sc_fsize = len;
2625b711ef9cSBaptiste Daroussin sc->sc_frames_ready++;
2626b711ef9cSBaptiste Daroussin
2627b711ef9cSBaptiste Daroussin wakeup(sc);
2628b711ef9cSBaptiste Daroussin selwakeup(&sc->sc_selinfo);
262954df18cbSBaptiste Daroussin KNOTE_LOCKED(&sc->sc_selinfo.si_note, 0);
2630b711ef9cSBaptiste Daroussin }
2631b711ef9cSBaptiste Daroussin
2632b711ef9cSBaptiste Daroussin /* ---------------------------------------------------------------- */
2633b711ef9cSBaptiste Daroussin /* Character Device Operations */
2634b711ef9cSBaptiste Daroussin /* ---------------------------------------------------------------- */
2635b711ef9cSBaptiste Daroussin
2636b711ef9cSBaptiste Daroussin static int
uvideo_cdev_open(struct cdev * dev,int flags,int fmt,struct thread * td)2637b711ef9cSBaptiste Daroussin uvideo_cdev_open(struct cdev *dev, int flags, int fmt, struct thread *td)
2638b711ef9cSBaptiste Daroussin {
2639b711ef9cSBaptiste Daroussin struct uvideo_softc *sc = dev->si_drv1;
2640b711ef9cSBaptiste Daroussin
2641b711ef9cSBaptiste Daroussin if (sc == NULL || sc->sc_dying)
2642b711ef9cSBaptiste Daroussin return (ENXIO);
2643b711ef9cSBaptiste Daroussin
2644b711ef9cSBaptiste Daroussin if (sc->sc_vs_cur == NULL)
2645b711ef9cSBaptiste Daroussin return (EIO);
2646b711ef9cSBaptiste Daroussin
2647b711ef9cSBaptiste Daroussin mtx_lock(&sc->sc_mtx);
2648b711ef9cSBaptiste Daroussin if (sc->sc_open == 0) {
2649b711ef9cSBaptiste Daroussin /* First open: initialize state */
2650b711ef9cSBaptiste Daroussin sc->sc_owner = td->td_proc;
2651b711ef9cSBaptiste Daroussin sc->sc_mmap_flag = 0;
2652b711ef9cSBaptiste Daroussin sc->sc_negotiated_flag = 0;
2653b711ef9cSBaptiste Daroussin sc->sc_vidmode = VIDMODE_NONE;
2654b711ef9cSBaptiste Daroussin sc->sc_frames_ready = 0;
2655b711ef9cSBaptiste Daroussin sc->sc_priority = 1; /* V4L2_PRIORITY_DEFAULT */
2656b711ef9cSBaptiste Daroussin }
2657b711ef9cSBaptiste Daroussin sc->sc_open++;
2658b711ef9cSBaptiste Daroussin mtx_unlock(&sc->sc_mtx);
2659b711ef9cSBaptiste Daroussin
2660b711ef9cSBaptiste Daroussin return (0);
2661b711ef9cSBaptiste Daroussin }
2662b711ef9cSBaptiste Daroussin
2663b711ef9cSBaptiste Daroussin static int
uvideo_cdev_close(struct cdev * dev,int flags,int fmt,struct thread * td)2664b711ef9cSBaptiste Daroussin uvideo_cdev_close(struct cdev *dev, int flags, int fmt, struct thread *td)
2665b711ef9cSBaptiste Daroussin {
2666b711ef9cSBaptiste Daroussin struct uvideo_softc *sc = dev->si_drv1;
2667b711ef9cSBaptiste Daroussin
2668b711ef9cSBaptiste Daroussin if (sc == NULL)
2669b711ef9cSBaptiste Daroussin return (0);
2670b711ef9cSBaptiste Daroussin
2671b711ef9cSBaptiste Daroussin mtx_lock(&sc->sc_mtx);
2672b711ef9cSBaptiste Daroussin sc->sc_open--;
2673b711ef9cSBaptiste Daroussin if (sc->sc_open > 0) {
2674b711ef9cSBaptiste Daroussin mtx_unlock(&sc->sc_mtx);
2675b711ef9cSBaptiste Daroussin return (0);
2676b711ef9cSBaptiste Daroussin }
2677b711ef9cSBaptiste Daroussin mtx_unlock(&sc->sc_mtx);
2678b711ef9cSBaptiste Daroussin
2679b711ef9cSBaptiste Daroussin /* Last close: stop streaming if active */
2680b711ef9cSBaptiste Daroussin if (sc->sc_streaming) {
2681b711ef9cSBaptiste Daroussin mtx_lock(&sc->sc_mtx);
2682b711ef9cSBaptiste Daroussin sc->sc_streaming = 0;
2683b711ef9cSBaptiste Daroussin mtx_unlock(&sc->sc_mtx);
2684b711ef9cSBaptiste Daroussin uvideo_vs_close(sc);
2685b711ef9cSBaptiste Daroussin uvideo_vs_free_frame(sc);
2686b711ef9cSBaptiste Daroussin }
2687b711ef9cSBaptiste Daroussin
2688b711ef9cSBaptiste Daroussin if (sc->sc_fbuffer != NULL) {
2689b711ef9cSBaptiste Daroussin free(sc->sc_fbuffer, M_USBDEV);
2690b711ef9cSBaptiste Daroussin sc->sc_fbuffer = NULL;
2691b711ef9cSBaptiste Daroussin sc->sc_fbufferlen = 0;
2692b711ef9cSBaptiste Daroussin }
2693b711ef9cSBaptiste Daroussin
2694b711ef9cSBaptiste Daroussin sc->sc_open = 0;
2695b711ef9cSBaptiste Daroussin sc->sc_owner = NULL;
2696b711ef9cSBaptiste Daroussin sc->sc_vidmode = VIDMODE_NONE;
2697b711ef9cSBaptiste Daroussin
2698b711ef9cSBaptiste Daroussin return (0);
2699b711ef9cSBaptiste Daroussin }
2700b711ef9cSBaptiste Daroussin
2701b711ef9cSBaptiste Daroussin static int
uvideo_cdev_read(struct cdev * dev,struct uio * uio,int ioflag)2702b711ef9cSBaptiste Daroussin uvideo_cdev_read(struct cdev *dev, struct uio *uio, int ioflag)
2703b711ef9cSBaptiste Daroussin {
2704b711ef9cSBaptiste Daroussin struct uvideo_softc *sc = dev->si_drv1;
2705b711ef9cSBaptiste Daroussin usb_error_t error;
2706b711ef9cSBaptiste Daroussin int ret;
2707b711ef9cSBaptiste Daroussin
2708b711ef9cSBaptiste Daroussin if (sc == NULL || sc->sc_dying)
2709b711ef9cSBaptiste Daroussin return (ENXIO);
2710b711ef9cSBaptiste Daroussin
2711b711ef9cSBaptiste Daroussin if (sc->sc_vs_cur == NULL)
2712b711ef9cSBaptiste Daroussin return (EIO);
2713b711ef9cSBaptiste Daroussin
2714b711ef9cSBaptiste Daroussin /* Start streaming in read mode if not already running */
2715b711ef9cSBaptiste Daroussin if (sc->sc_vidmode == VIDMODE_NONE) {
2716b711ef9cSBaptiste Daroussin sc->sc_mmap_flag = 0;
2717b711ef9cSBaptiste Daroussin sc->sc_vidmode = VIDMODE_READ;
2718b711ef9cSBaptiste Daroussin
2719b711ef9cSBaptiste Daroussin error = uvideo_vs_init(sc);
2720b711ef9cSBaptiste Daroussin if (error != USB_ERR_NORMAL_COMPLETION) {
2721b711ef9cSBaptiste Daroussin sc->sc_vidmode = VIDMODE_NONE;
2722b711ef9cSBaptiste Daroussin return (EIO);
2723b711ef9cSBaptiste Daroussin }
2724b711ef9cSBaptiste Daroussin
2725b711ef9cSBaptiste Daroussin /* Allocate a separate read buffer for frame delivery */
2726b711ef9cSBaptiste Daroussin sc->sc_fbufferlen = sc->sc_max_fbuf_size;
2727b711ef9cSBaptiste Daroussin if (sc->sc_fbufferlen == 0)
2728b711ef9cSBaptiste Daroussin sc->sc_fbufferlen =
2729b711ef9cSBaptiste Daroussin UGETDW(sc->sc_desc_probe.dwMaxVideoFrameSize);
2730b711ef9cSBaptiste Daroussin if (sc->sc_fbuffer == NULL) {
2731b711ef9cSBaptiste Daroussin sc->sc_fbuffer = malloc(sc->sc_fbufferlen, M_USBDEV,
2732b711ef9cSBaptiste Daroussin M_WAITOK | M_ZERO);
2733b711ef9cSBaptiste Daroussin if (sc->sc_fbuffer == NULL) {
2734b711ef9cSBaptiste Daroussin sc->sc_vidmode = VIDMODE_NONE;
2735b711ef9cSBaptiste Daroussin return (ENOMEM);
2736b711ef9cSBaptiste Daroussin }
2737b711ef9cSBaptiste Daroussin }
2738b711ef9cSBaptiste Daroussin
2739b711ef9cSBaptiste Daroussin mtx_lock(&sc->sc_mtx);
2740b711ef9cSBaptiste Daroussin sc->sc_streaming = 1;
2741b711ef9cSBaptiste Daroussin if (sc->sc_vs_cur->bulk_endpoint)
2742b711ef9cSBaptiste Daroussin usbd_transfer_start(sc->sc_xfer[0]);
2743b711ef9cSBaptiste Daroussin else {
2744b711ef9cSBaptiste Daroussin int i;
2745b711ef9cSBaptiste Daroussin for (i = 0; i < UVIDEO_IXFERS; i++)
2746b711ef9cSBaptiste Daroussin usbd_transfer_start(sc->sc_xfer[i]);
2747b711ef9cSBaptiste Daroussin }
2748b711ef9cSBaptiste Daroussin mtx_unlock(&sc->sc_mtx);
2749b711ef9cSBaptiste Daroussin }
2750b711ef9cSBaptiste Daroussin
2751b711ef9cSBaptiste Daroussin if (sc->sc_vidmode != VIDMODE_READ)
2752b711ef9cSBaptiste Daroussin return (EBUSY);
2753b711ef9cSBaptiste Daroussin
2754b711ef9cSBaptiste Daroussin /* Wait for a frame */
2755b711ef9cSBaptiste Daroussin while (sc->sc_frames_ready == 0) {
2756b711ef9cSBaptiste Daroussin if (ioflag & IO_NDELAY)
2757b711ef9cSBaptiste Daroussin return (EWOULDBLOCK);
2758b711ef9cSBaptiste Daroussin ret = tsleep(sc, PCATCH, "uvread", hz * 10);
2759b711ef9cSBaptiste Daroussin if (ret != 0)
2760b711ef9cSBaptiste Daroussin return (ret);
2761b711ef9cSBaptiste Daroussin if (sc->sc_dying)
2762b711ef9cSBaptiste Daroussin return (ENXIO);
2763b711ef9cSBaptiste Daroussin }
2764b711ef9cSBaptiste Daroussin
2765b711ef9cSBaptiste Daroussin sc->sc_frames_ready--;
2766b711ef9cSBaptiste Daroussin
2767b711ef9cSBaptiste Daroussin if (sc->sc_fsize == 0)
2768b711ef9cSBaptiste Daroussin return (0);
2769b711ef9cSBaptiste Daroussin
2770b711ef9cSBaptiste Daroussin return (uiomove(sc->sc_fbuffer, MIN(uio->uio_resid, sc->sc_fsize),
2771b711ef9cSBaptiste Daroussin uio));
2772b711ef9cSBaptiste Daroussin }
2773b711ef9cSBaptiste Daroussin
2774b711ef9cSBaptiste Daroussin static int
uvideo_cdev_ioctl(struct cdev * dev,u_long cmd,caddr_t data,int fflag,struct thread * td)2775b711ef9cSBaptiste Daroussin uvideo_cdev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
2776b711ef9cSBaptiste Daroussin struct thread *td)
2777b711ef9cSBaptiste Daroussin {
2778b711ef9cSBaptiste Daroussin struct uvideo_softc *sc = dev->si_drv1;
2779b711ef9cSBaptiste Daroussin
2780b711ef9cSBaptiste Daroussin if (sc == NULL || sc->sc_dying)
2781b711ef9cSBaptiste Daroussin return (ENXIO);
2782b711ef9cSBaptiste Daroussin
2783b711ef9cSBaptiste Daroussin DPRINTFN(2, "ioctl cmd=0x%08lx\n", cmd);
2784b711ef9cSBaptiste Daroussin
2785b711ef9cSBaptiste Daroussin switch (cmd) {
2786b711ef9cSBaptiste Daroussin case VIDIOC_QUERYCAP:
2787b711ef9cSBaptiste Daroussin return (uvideo_querycap(sc, (struct v4l2_capability *)data));
2788b711ef9cSBaptiste Daroussin case VIDIOC_ENUM_FMT:
2789b711ef9cSBaptiste Daroussin return (uvideo_enum_fmt(sc, (struct v4l2_fmtdesc *)data));
2790b711ef9cSBaptiste Daroussin case VIDIOC_ENUM_FRAMESIZES:
2791b711ef9cSBaptiste Daroussin return (uvideo_enum_fsizes(sc,
2792b711ef9cSBaptiste Daroussin (struct v4l2_frmsizeenum *)data));
2793b711ef9cSBaptiste Daroussin case VIDIOC_ENUM_FRAMEINTERVALS:
2794b711ef9cSBaptiste Daroussin return (uvideo_enum_fivals(sc,
2795b711ef9cSBaptiste Daroussin (struct v4l2_frmivalenum *)data));
2796b711ef9cSBaptiste Daroussin case VIDIOC_S_FMT:
2797b711ef9cSBaptiste Daroussin return (uvideo_s_fmt(sc, (struct v4l2_format *)data));
2798b711ef9cSBaptiste Daroussin case VIDIOC_G_FMT:
2799b711ef9cSBaptiste Daroussin return (uvideo_g_fmt(sc, (struct v4l2_format *)data));
2800b711ef9cSBaptiste Daroussin case VIDIOC_TRY_FMT:
2801b711ef9cSBaptiste Daroussin return (uvideo_try_fmt(sc, (struct v4l2_format *)data));
2802b711ef9cSBaptiste Daroussin case VIDIOC_S_PARM:
2803b711ef9cSBaptiste Daroussin return (uvideo_s_parm(sc, (struct v4l2_streamparm *)data));
2804b711ef9cSBaptiste Daroussin case VIDIOC_G_PARM:
2805b711ef9cSBaptiste Daroussin return (uvideo_g_parm(sc, (struct v4l2_streamparm *)data));
2806b711ef9cSBaptiste Daroussin case VIDIOC_ENUMINPUT:
2807b711ef9cSBaptiste Daroussin return (uvideo_enum_input(sc, (struct v4l2_input *)data));
2808b711ef9cSBaptiste Daroussin case VIDIOC_S_INPUT:
2809b711ef9cSBaptiste Daroussin return (uvideo_s_input(sc, *(int *)data));
2810b711ef9cSBaptiste Daroussin case VIDIOC_G_INPUT:
2811b711ef9cSBaptiste Daroussin return (uvideo_g_input(sc, (int *)data));
2812b711ef9cSBaptiste Daroussin case VIDIOC_REQBUFS:
2813b711ef9cSBaptiste Daroussin return (uvideo_reqbufs(sc,
2814b711ef9cSBaptiste Daroussin (struct v4l2_requestbuffers *)data));
2815b711ef9cSBaptiste Daroussin case VIDIOC_QUERYBUF:
2816b711ef9cSBaptiste Daroussin return (uvideo_querybuf(sc, (struct v4l2_buffer *)data));
2817b711ef9cSBaptiste Daroussin case VIDIOC_QBUF:
2818b711ef9cSBaptiste Daroussin return (uvideo_qbuf(sc, (struct v4l2_buffer *)data));
2819b711ef9cSBaptiste Daroussin case VIDIOC_DQBUF:
2820b711ef9cSBaptiste Daroussin return (uvideo_dqbuf(sc, (struct v4l2_buffer *)data));
2821b711ef9cSBaptiste Daroussin case VIDIOC_STREAMON:
2822b711ef9cSBaptiste Daroussin return (uvideo_streamon(sc, *(int *)data));
2823b711ef9cSBaptiste Daroussin case VIDIOC_STREAMOFF:
2824b711ef9cSBaptiste Daroussin return (uvideo_streamoff(sc, *(int *)data));
2825b711ef9cSBaptiste Daroussin case VIDIOC_QUERYCTRL:
2826b711ef9cSBaptiste Daroussin return (uvideo_queryctrl(sc,
2827b711ef9cSBaptiste Daroussin (struct v4l2_queryctrl *)data));
2828b711ef9cSBaptiste Daroussin case VIDIOC_G_CTRL:
2829b711ef9cSBaptiste Daroussin return (uvideo_g_ctrl(sc, (struct v4l2_control *)data));
2830b711ef9cSBaptiste Daroussin case VIDIOC_S_CTRL:
2831b711ef9cSBaptiste Daroussin return (uvideo_s_ctrl(sc, (struct v4l2_control *)data));
2832b711ef9cSBaptiste Daroussin case VIDIOC_G_PRIORITY:
2833b711ef9cSBaptiste Daroussin *(uint32_t *)data = sc->sc_priority;
2834b711ef9cSBaptiste Daroussin return (0);
2835b711ef9cSBaptiste Daroussin case VIDIOC_S_PRIORITY:
2836b711ef9cSBaptiste Daroussin sc->sc_priority = *(uint32_t *)data;
2837b711ef9cSBaptiste Daroussin return (0);
2838b711ef9cSBaptiste Daroussin default:
2839b711ef9cSBaptiste Daroussin return (ENOTTY);
2840b711ef9cSBaptiste Daroussin }
2841b711ef9cSBaptiste Daroussin }
2842b711ef9cSBaptiste Daroussin
2843b711ef9cSBaptiste Daroussin static int
uvideo_cdev_poll(struct cdev * dev,int events,struct thread * td)2844b711ef9cSBaptiste Daroussin uvideo_cdev_poll(struct cdev *dev, int events, struct thread *td)
2845b711ef9cSBaptiste Daroussin {
2846b711ef9cSBaptiste Daroussin struct uvideo_softc *sc = dev->si_drv1;
2847b711ef9cSBaptiste Daroussin int revents = 0;
2848b711ef9cSBaptiste Daroussin
2849b711ef9cSBaptiste Daroussin if (sc == NULL || sc->sc_dying)
2850b711ef9cSBaptiste Daroussin return (POLLHUP);
2851b711ef9cSBaptiste Daroussin
2852b711ef9cSBaptiste Daroussin if (events & (POLLIN | POLLRDNORM)) {
2853b711ef9cSBaptiste Daroussin if (sc->sc_mmap_flag) {
2854b711ef9cSBaptiste Daroussin if (!STAILQ_EMPTY(&sc->sc_mmap_q))
2855b711ef9cSBaptiste Daroussin revents |= events & (POLLIN | POLLRDNORM);
2856b711ef9cSBaptiste Daroussin } else {
2857b711ef9cSBaptiste Daroussin if (sc->sc_frames_ready > 0)
2858b711ef9cSBaptiste Daroussin revents |= events & (POLLIN | POLLRDNORM);
2859b711ef9cSBaptiste Daroussin }
2860b711ef9cSBaptiste Daroussin if (revents == 0)
2861b711ef9cSBaptiste Daroussin selrecord(td, &sc->sc_selinfo);
2862b711ef9cSBaptiste Daroussin }
2863b711ef9cSBaptiste Daroussin
2864b711ef9cSBaptiste Daroussin return (revents);
2865b711ef9cSBaptiste Daroussin }
2866b711ef9cSBaptiste Daroussin
286754df18cbSBaptiste Daroussin static void
uvideo_kqfilter_detach(struct knote * kn)286854df18cbSBaptiste Daroussin uvideo_kqfilter_detach(struct knote *kn)
286954df18cbSBaptiste Daroussin {
287054df18cbSBaptiste Daroussin struct uvideo_softc *sc = kn->kn_hook;
287154df18cbSBaptiste Daroussin
287254df18cbSBaptiste Daroussin knlist_remove(&sc->sc_selinfo.si_note, kn, 0);
287354df18cbSBaptiste Daroussin }
287454df18cbSBaptiste Daroussin
287554df18cbSBaptiste Daroussin static int
uvideo_kqfilter_read(struct knote * kn,long hint __unused)287654df18cbSBaptiste Daroussin uvideo_kqfilter_read(struct knote *kn, long hint __unused)
287754df18cbSBaptiste Daroussin {
287854df18cbSBaptiste Daroussin struct uvideo_softc *sc = kn->kn_hook;
287954df18cbSBaptiste Daroussin
288054df18cbSBaptiste Daroussin if (sc->sc_mmap_flag)
288154df18cbSBaptiste Daroussin return (!STAILQ_EMPTY(&sc->sc_mmap_q));
288254df18cbSBaptiste Daroussin return (sc->sc_frames_ready > 0);
288354df18cbSBaptiste Daroussin }
288454df18cbSBaptiste Daroussin
288554df18cbSBaptiste Daroussin static struct filterops uvideo_filtops_read = {
288654df18cbSBaptiste Daroussin .f_isfd = 1,
288754df18cbSBaptiste Daroussin .f_detach = uvideo_kqfilter_detach,
288854df18cbSBaptiste Daroussin .f_event = uvideo_kqfilter_read,
288954df18cbSBaptiste Daroussin };
289054df18cbSBaptiste Daroussin
289154df18cbSBaptiste Daroussin static int
uvideo_cdev_kqfilter(struct cdev * dev,struct knote * kn)289254df18cbSBaptiste Daroussin uvideo_cdev_kqfilter(struct cdev *dev, struct knote *kn)
289354df18cbSBaptiste Daroussin {
289454df18cbSBaptiste Daroussin struct uvideo_softc *sc = dev->si_drv1;
289554df18cbSBaptiste Daroussin
289654df18cbSBaptiste Daroussin if (sc == NULL || sc->sc_dying)
289754df18cbSBaptiste Daroussin return (ENXIO);
289854df18cbSBaptiste Daroussin
289954df18cbSBaptiste Daroussin switch (kn->kn_filter) {
290054df18cbSBaptiste Daroussin case EVFILT_READ:
290154df18cbSBaptiste Daroussin kn->kn_fop = &uvideo_filtops_read;
290254df18cbSBaptiste Daroussin kn->kn_hook = sc;
290354df18cbSBaptiste Daroussin knlist_add(&sc->sc_selinfo.si_note, kn, 0);
290454df18cbSBaptiste Daroussin return (0);
290554df18cbSBaptiste Daroussin default:
290654df18cbSBaptiste Daroussin return (EINVAL);
290754df18cbSBaptiste Daroussin }
290854df18cbSBaptiste Daroussin }
290954df18cbSBaptiste Daroussin
2910b711ef9cSBaptiste Daroussin static int
uvideo_cdev_mmap(struct cdev * dev,vm_ooffset_t offset,vm_paddr_t * paddr,int nprot,vm_memattr_t * memattr)2911b711ef9cSBaptiste Daroussin uvideo_cdev_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr,
2912b711ef9cSBaptiste Daroussin int nprot, vm_memattr_t *memattr)
2913b711ef9cSBaptiste Daroussin {
2914b711ef9cSBaptiste Daroussin struct uvideo_softc *sc = dev->si_drv1;
2915b711ef9cSBaptiste Daroussin
2916b711ef9cSBaptiste Daroussin if (sc == NULL || sc->sc_dying)
2917b711ef9cSBaptiste Daroussin return (ENXIO);
2918b711ef9cSBaptiste Daroussin
2919b711ef9cSBaptiste Daroussin if (offset >= sc->sc_mmap_buffer_size)
2920b711ef9cSBaptiste Daroussin return (EINVAL);
2921b711ef9cSBaptiste Daroussin
2922b711ef9cSBaptiste Daroussin if (sc->sc_mmap_buffer == NULL)
2923b711ef9cSBaptiste Daroussin return (EINVAL);
2924b711ef9cSBaptiste Daroussin
2925b711ef9cSBaptiste Daroussin if (!sc->sc_mmap_flag)
2926b711ef9cSBaptiste Daroussin sc->sc_mmap_flag = 1;
2927b711ef9cSBaptiste Daroussin
2928b711ef9cSBaptiste Daroussin *paddr = vtophys(sc->sc_mmap_buffer + offset);
2929b711ef9cSBaptiste Daroussin
2930b711ef9cSBaptiste Daroussin return (0);
2931b711ef9cSBaptiste Daroussin }
2932b711ef9cSBaptiste Daroussin
2933b711ef9cSBaptiste Daroussin /* ---------------------------------------------------------------- */
2934b711ef9cSBaptiste Daroussin /* V4L2 Ioctl Handlers */
2935b711ef9cSBaptiste Daroussin /* ---------------------------------------------------------------- */
2936b711ef9cSBaptiste Daroussin
2937b711ef9cSBaptiste Daroussin static int
uvideo_querycap(struct uvideo_softc * sc,struct v4l2_capability * caps)2938b711ef9cSBaptiste Daroussin uvideo_querycap(struct uvideo_softc *sc, struct v4l2_capability *caps)
2939b711ef9cSBaptiste Daroussin {
2940b711ef9cSBaptiste Daroussin
2941b711ef9cSBaptiste Daroussin bzero(caps, sizeof(*caps));
2942b711ef9cSBaptiste Daroussin strlcpy(caps->driver, "uvideo", sizeof(caps->driver));
2943b711ef9cSBaptiste Daroussin strlcpy(caps->card, usb_get_product(sc->sc_udev),
2944b711ef9cSBaptiste Daroussin sizeof(caps->card));
2945b711ef9cSBaptiste Daroussin snprintf(caps->bus_info, sizeof(caps->bus_info), "usb-%s",
2946b711ef9cSBaptiste Daroussin device_get_nameunit(sc->sc_dev));
2947b711ef9cSBaptiste Daroussin
2948b711ef9cSBaptiste Daroussin caps->version = (5 << 16) | (0 << 8) | 0; /* 5.0.0 */
2949b711ef9cSBaptiste Daroussin caps->device_caps = V4L2_CAP_VIDEO_CAPTURE |
2950b711ef9cSBaptiste Daroussin V4L2_CAP_STREAMING | V4L2_CAP_READWRITE |
2951b711ef9cSBaptiste Daroussin V4L2_CAP_EXT_PIX_FORMAT;
2952b711ef9cSBaptiste Daroussin caps->capabilities = caps->device_caps | V4L2_CAP_DEVICE_CAPS;
2953b711ef9cSBaptiste Daroussin
2954b711ef9cSBaptiste Daroussin return (0);
2955b711ef9cSBaptiste Daroussin }
2956b711ef9cSBaptiste Daroussin
2957b711ef9cSBaptiste Daroussin /*
2958b711ef9cSBaptiste Daroussin * Map pixel format to canonical V4L2 description string.
2959b711ef9cSBaptiste Daroussin * v4l2-compliance checks these names against an internal table.
2960b711ef9cSBaptiste Daroussin */
2961b711ef9cSBaptiste Daroussin static const struct {
2962b711ef9cSBaptiste Daroussin uint32_t pixfmt;
2963b711ef9cSBaptiste Daroussin const char *name;
2964b711ef9cSBaptiste Daroussin uint32_t flags;
2965b711ef9cSBaptiste Daroussin } uvideo_fmt_names[] = {
2966b711ef9cSBaptiste Daroussin { V4L2_PIX_FMT_MJPEG, "Motion-JPEG", V4L2_FMT_FLAG_COMPRESSED },
2967b711ef9cSBaptiste Daroussin { V4L2_PIX_FMT_YUYV, "YUYV 4:2:2", 0 },
29688bc06ffbSBaptiste Daroussin { V4L2_PIX_FMT_UYVY, "UYVY 4:2:2", 0 },
29698bc06ffbSBaptiste Daroussin { V4L2_PIX_FMT_NV12, "Y/UV 4:2:0", 0 },
29708bc06ffbSBaptiste Daroussin { V4L2_PIX_FMT_NV21, "Y/VU 4:2:0", 0 },
2971b711ef9cSBaptiste Daroussin { V4L2_PIX_FMT_YVU420, "Planar YVU 4:2:0", 0 },
2972b711ef9cSBaptiste Daroussin { V4L2_PIX_FMT_YUV420, "Planar YUV 4:2:0", 0 },
29738bc06ffbSBaptiste Daroussin { V4L2_PIX_FMT_M420, "M420 YUV 4:2:0", 0 },
2974b711ef9cSBaptiste Daroussin { V4L2_PIX_FMT_GREY, "8-bit Greyscale", 0 },
29758bc06ffbSBaptiste Daroussin { V4L2_PIX_FMT_Y10, "10-bit Greyscale", 0 },
29768bc06ffbSBaptiste Daroussin { V4L2_PIX_FMT_Y12, "12-bit Greyscale", 0 },
29778bc06ffbSBaptiste Daroussin { V4L2_PIX_FMT_Y16, "16-bit Greyscale", 0 },
2978b711ef9cSBaptiste Daroussin { V4L2_PIX_FMT_RGB565, "16-bit RGB 5-6-5", 0 },
2979b711ef9cSBaptiste Daroussin { V4L2_PIX_FMT_BGR24, "24-bit BGR 8-8-8", 0 },
2980b711ef9cSBaptiste Daroussin { V4L2_PIX_FMT_XBGR32, "32-bit BGRX 8-8-8-8", 0 },
2981b711ef9cSBaptiste Daroussin { V4L2_PIX_FMT_H264, "H.264", V4L2_FMT_FLAG_COMPRESSED },
2982b711ef9cSBaptiste Daroussin { V4L2_PIX_FMT_HEVC, "HEVC", V4L2_FMT_FLAG_COMPRESSED },
2983b711ef9cSBaptiste Daroussin { V4L2_PIX_FMT_SBGGR8, "8-bit Bayer BGBG/GRGR", 0 },
2984b711ef9cSBaptiste Daroussin { V4L2_PIX_FMT_SGBRG8, "8-bit Bayer GBGB/RGRG", 0 },
2985b711ef9cSBaptiste Daroussin { V4L2_PIX_FMT_SGRBG8, "8-bit Bayer GRGR/BGBG", 0 },
2986b711ef9cSBaptiste Daroussin { V4L2_PIX_FMT_SRGGB8, "8-bit Bayer RGRG/GBGB", 0 },
29878bc06ffbSBaptiste Daroussin { V4L2_PIX_FMT_SBGGR16, "16-bit Bayer BGBG/GRGR", 0 },
29888bc06ffbSBaptiste Daroussin { V4L2_PIX_FMT_SGBRG16, "16-bit Bayer GBGB/RGRG", 0 },
29898bc06ffbSBaptiste Daroussin { V4L2_PIX_FMT_SGRBG16, "16-bit Bayer GRGR/BGBG", 0 },
29908bc06ffbSBaptiste Daroussin { V4L2_PIX_FMT_SRGGB16, "16-bit Bayer RGRG/GBGB", 0 },
29918bc06ffbSBaptiste Daroussin { V4L2_PIX_FMT_SRGGB10P, "10-bit Bayer RGRG/GBGB Packed", 0 },
29928bc06ffbSBaptiste Daroussin { V4L2_PIX_FMT_Z16, "16-bit Depth", 0 },
2993b711ef9cSBaptiste Daroussin { 0, NULL, 0 }
2994b711ef9cSBaptiste Daroussin };
2995b711ef9cSBaptiste Daroussin
2996b711ef9cSBaptiste Daroussin static int
uvideo_enum_fmt(struct uvideo_softc * sc,struct v4l2_fmtdesc * fmtdesc)2997b711ef9cSBaptiste Daroussin uvideo_enum_fmt(struct uvideo_softc *sc, struct v4l2_fmtdesc *fmtdesc)
2998b711ef9cSBaptiste Daroussin {
2999b711ef9cSBaptiste Daroussin uint32_t idx, type, pixfmt, flags;
3000b711ef9cSBaptiste Daroussin const char *name;
3001b711ef9cSBaptiste Daroussin int i;
3002b711ef9cSBaptiste Daroussin
3003b711ef9cSBaptiste Daroussin type = fmtdesc->type;
3004b711ef9cSBaptiste Daroussin idx = fmtdesc->index;
3005b711ef9cSBaptiste Daroussin
3006b711ef9cSBaptiste Daroussin if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
3007b711ef9cSBaptiste Daroussin return (EINVAL);
3008b711ef9cSBaptiste Daroussin
3009b711ef9cSBaptiste Daroussin if (idx >= (uint32_t)sc->sc_fmtgrp_num)
3010b711ef9cSBaptiste Daroussin return (EINVAL);
3011b711ef9cSBaptiste Daroussin
3012b711ef9cSBaptiste Daroussin pixfmt = sc->sc_fmtgrp[idx].pixelformat;
3013b711ef9cSBaptiste Daroussin flags = 0;
3014b711ef9cSBaptiste Daroussin name = "Unknown Format";
3015b711ef9cSBaptiste Daroussin
3016b711ef9cSBaptiste Daroussin /* Look up canonical name and flags */
3017b711ef9cSBaptiste Daroussin for (i = 0; uvideo_fmt_names[i].name != NULL; i++) {
3018b711ef9cSBaptiste Daroussin if (uvideo_fmt_names[i].pixfmt == pixfmt) {
3019b711ef9cSBaptiste Daroussin name = uvideo_fmt_names[i].name;
3020b711ef9cSBaptiste Daroussin flags = uvideo_fmt_names[i].flags;
3021b711ef9cSBaptiste Daroussin break;
3022b711ef9cSBaptiste Daroussin }
3023b711ef9cSBaptiste Daroussin }
3024b711ef9cSBaptiste Daroussin
3025b711ef9cSBaptiste Daroussin /* Override flags for special descriptor subtypes */
3026b711ef9cSBaptiste Daroussin switch (sc->sc_fmtgrp[idx].format->bDescriptorSubtype) {
3027b711ef9cSBaptiste Daroussin case UDESCSUB_VS_FORMAT_MJPEG:
3028b711ef9cSBaptiste Daroussin pixfmt = V4L2_PIX_FMT_MJPEG;
3029b711ef9cSBaptiste Daroussin flags = V4L2_FMT_FLAG_COMPRESSED;
3030b711ef9cSBaptiste Daroussin name = "Motion-JPEG";
3031b711ef9cSBaptiste Daroussin break;
3032b711ef9cSBaptiste Daroussin case UDESCSUB_VS_FORMAT_H264:
3033b711ef9cSBaptiste Daroussin case UDESCSUB_VS_FORMAT_H264_SIMULCAST:
3034b711ef9cSBaptiste Daroussin pixfmt = V4L2_PIX_FMT_H264;
3035b711ef9cSBaptiste Daroussin flags = V4L2_FMT_FLAG_COMPRESSED;
3036b711ef9cSBaptiste Daroussin name = "H.264";
3037b711ef9cSBaptiste Daroussin break;
3038b711ef9cSBaptiste Daroussin case UDESCSUB_VS_FORMAT_FRAME_BASED:
3039b711ef9cSBaptiste Daroussin if (sc->sc_fmtgrp[idx].format->u.fb.bVariableSize)
3040b711ef9cSBaptiste Daroussin flags = V4L2_FMT_FLAG_COMPRESSED;
3041b711ef9cSBaptiste Daroussin break;
3042b711ef9cSBaptiste Daroussin }
3043b711ef9cSBaptiste Daroussin
3044b711ef9cSBaptiste Daroussin bzero(fmtdesc, sizeof(*fmtdesc));
3045b711ef9cSBaptiste Daroussin fmtdesc->index = idx;
3046b711ef9cSBaptiste Daroussin fmtdesc->type = type;
3047b711ef9cSBaptiste Daroussin fmtdesc->flags = flags;
3048b711ef9cSBaptiste Daroussin fmtdesc->pixelformat = pixfmt;
3049b711ef9cSBaptiste Daroussin strlcpy(fmtdesc->description, name, sizeof(fmtdesc->description));
3050b711ef9cSBaptiste Daroussin
3051b711ef9cSBaptiste Daroussin return (0);
3052b711ef9cSBaptiste Daroussin }
3053b711ef9cSBaptiste Daroussin
3054b711ef9cSBaptiste Daroussin static int
uvideo_enum_fsizes(struct uvideo_softc * sc,struct v4l2_frmsizeenum * fsizes)3055b711ef9cSBaptiste Daroussin uvideo_enum_fsizes(struct uvideo_softc *sc, struct v4l2_frmsizeenum *fsizes)
3056b711ef9cSBaptiste Daroussin {
3057b711ef9cSBaptiste Daroussin int idx, found = 0;
3058b711ef9cSBaptiste Daroussin uint32_t index, pixel_format;
3059b711ef9cSBaptiste Daroussin struct usb_video_frame_desc *frame;
3060b711ef9cSBaptiste Daroussin
3061b711ef9cSBaptiste Daroussin index = fsizes->index;
3062b711ef9cSBaptiste Daroussin pixel_format = fsizes->pixel_format;
3063b711ef9cSBaptiste Daroussin
3064b711ef9cSBaptiste Daroussin for (idx = 0; idx < sc->sc_fmtgrp_num; idx++) {
3065b711ef9cSBaptiste Daroussin if (sc->sc_fmtgrp[idx].pixelformat == pixel_format) {
3066b711ef9cSBaptiste Daroussin found = 1;
3067b711ef9cSBaptiste Daroussin break;
3068b711ef9cSBaptiste Daroussin }
3069b711ef9cSBaptiste Daroussin }
3070b711ef9cSBaptiste Daroussin if (found == 0)
3071b711ef9cSBaptiste Daroussin return (EINVAL);
3072b711ef9cSBaptiste Daroussin
3073b711ef9cSBaptiste Daroussin if (index >= (uint32_t)sc->sc_fmtgrp[idx].frame_num)
3074b711ef9cSBaptiste Daroussin return (EINVAL);
3075b711ef9cSBaptiste Daroussin
3076b711ef9cSBaptiste Daroussin bzero(fsizes, sizeof(*fsizes));
3077b711ef9cSBaptiste Daroussin fsizes->index = index;
3078b711ef9cSBaptiste Daroussin fsizes->pixel_format = pixel_format;
3079b711ef9cSBaptiste Daroussin fsizes->type = V4L2_FRMSIZE_TYPE_DISCRETE;
3080b711ef9cSBaptiste Daroussin frame = sc->sc_fmtgrp[idx].frame[index];
3081b711ef9cSBaptiste Daroussin fsizes->discrete.width = UGETW(UVIDEO_FRAME_FIELD(frame, wWidth));
3082b711ef9cSBaptiste Daroussin fsizes->discrete.height = UGETW(UVIDEO_FRAME_FIELD(frame, wHeight));
3083b711ef9cSBaptiste Daroussin
3084b711ef9cSBaptiste Daroussin return (0);
3085b711ef9cSBaptiste Daroussin }
3086b711ef9cSBaptiste Daroussin
3087b711ef9cSBaptiste Daroussin static int
uvideo_enum_fivals(struct uvideo_softc * sc,struct v4l2_frmivalenum * fivals)3088b711ef9cSBaptiste Daroussin uvideo_enum_fivals(struct uvideo_softc *sc, struct v4l2_frmivalenum *fivals)
3089b711ef9cSBaptiste Daroussin {
3090b711ef9cSBaptiste Daroussin int idx;
3091b711ef9cSBaptiste Daroussin struct uvideo_format_group *fmtgrp = NULL;
3092b711ef9cSBaptiste Daroussin struct usb_video_frame_desc *frame = NULL;
3093b711ef9cSBaptiste Daroussin uint8_t *p;
3094b711ef9cSBaptiste Daroussin uint32_t fi_index, fi_pixfmt, fi_width, fi_height;
3095b711ef9cSBaptiste Daroussin
3096b711ef9cSBaptiste Daroussin fi_index = fivals->index;
3097b711ef9cSBaptiste Daroussin fi_pixfmt = fivals->pixel_format;
3098b711ef9cSBaptiste Daroussin fi_width = fivals->width;
3099b711ef9cSBaptiste Daroussin fi_height = fivals->height;
3100b711ef9cSBaptiste Daroussin
3101b711ef9cSBaptiste Daroussin for (idx = 0; idx < sc->sc_fmtgrp_num; idx++) {
3102b711ef9cSBaptiste Daroussin if (sc->sc_fmtgrp[idx].pixelformat == fi_pixfmt) {
3103b711ef9cSBaptiste Daroussin fmtgrp = &sc->sc_fmtgrp[idx];
3104b711ef9cSBaptiste Daroussin break;
3105b711ef9cSBaptiste Daroussin }
3106b711ef9cSBaptiste Daroussin }
3107b711ef9cSBaptiste Daroussin if (fmtgrp == NULL)
3108b711ef9cSBaptiste Daroussin return (EINVAL);
3109b711ef9cSBaptiste Daroussin
3110b711ef9cSBaptiste Daroussin for (idx = 0; idx < fmtgrp->frame_num; idx++) {
3111b711ef9cSBaptiste Daroussin if (UGETW(UVIDEO_FRAME_FIELD(fmtgrp->frame[idx], wWidth))
3112b711ef9cSBaptiste Daroussin == fi_width &&
3113b711ef9cSBaptiste Daroussin UGETW(UVIDEO_FRAME_FIELD(fmtgrp->frame[idx], wHeight))
3114b711ef9cSBaptiste Daroussin == fi_height) {
3115b711ef9cSBaptiste Daroussin frame = fmtgrp->frame[idx];
3116b711ef9cSBaptiste Daroussin break;
3117b711ef9cSBaptiste Daroussin }
3118b711ef9cSBaptiste Daroussin }
3119b711ef9cSBaptiste Daroussin if (frame == NULL)
3120b711ef9cSBaptiste Daroussin return (EINVAL);
3121b711ef9cSBaptiste Daroussin
3122b711ef9cSBaptiste Daroussin p = (uint8_t *)frame + UVIDEO_FRAME_MIN_LEN(frame);
3123b711ef9cSBaptiste Daroussin
3124b711ef9cSBaptiste Daroussin bzero(fivals, sizeof(*fivals));
3125b711ef9cSBaptiste Daroussin fivals->index = fi_index;
3126b711ef9cSBaptiste Daroussin fivals->pixel_format = fi_pixfmt;
3127b711ef9cSBaptiste Daroussin fivals->width = fi_width;
3128b711ef9cSBaptiste Daroussin fivals->height = fi_height;
3129b711ef9cSBaptiste Daroussin
3130b711ef9cSBaptiste Daroussin if (UVIDEO_FRAME_NUM_INTERVALS(frame) == 0) {
3131b711ef9cSBaptiste Daroussin if (fi_index != 0)
3132b711ef9cSBaptiste Daroussin return (EINVAL);
3133b711ef9cSBaptiste Daroussin fivals->type = V4L2_FRMIVAL_TYPE_STEPWISE;
3134b711ef9cSBaptiste Daroussin fivals->stepwise.min.numerator = UGETDW(p);
3135b711ef9cSBaptiste Daroussin fivals->stepwise.min.denominator = 10000000;
3136b711ef9cSBaptiste Daroussin p += sizeof(uDWord);
3137b711ef9cSBaptiste Daroussin fivals->stepwise.max.numerator = UGETDW(p);
3138b711ef9cSBaptiste Daroussin fivals->stepwise.max.denominator = 10000000;
3139b711ef9cSBaptiste Daroussin p += sizeof(uDWord);
3140b711ef9cSBaptiste Daroussin fivals->stepwise.step.numerator = UGETDW(p);
3141b711ef9cSBaptiste Daroussin fivals->stepwise.step.denominator = 10000000;
3142b711ef9cSBaptiste Daroussin } else {
3143b711ef9cSBaptiste Daroussin if (fi_index >= (uint32_t)UVIDEO_FRAME_NUM_INTERVALS(frame))
3144b711ef9cSBaptiste Daroussin return (EINVAL);
3145b711ef9cSBaptiste Daroussin p += sizeof(uDWord) * fi_index;
3146b711ef9cSBaptiste Daroussin if (p > frame->bLength + (uint8_t *)frame) {
3147b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev,
3148b711ef9cSBaptiste Daroussin "frame desc too short?\n");
3149b711ef9cSBaptiste Daroussin return (EINVAL);
3150b711ef9cSBaptiste Daroussin }
3151b711ef9cSBaptiste Daroussin fivals->type = V4L2_FRMIVAL_TYPE_DISCRETE;
3152b711ef9cSBaptiste Daroussin fivals->discrete.numerator = UGETDW(p);
3153b711ef9cSBaptiste Daroussin fivals->discrete.denominator = 10000000;
3154b711ef9cSBaptiste Daroussin }
3155b711ef9cSBaptiste Daroussin
3156b711ef9cSBaptiste Daroussin return (0);
3157b711ef9cSBaptiste Daroussin }
3158b711ef9cSBaptiste Daroussin
3159b711ef9cSBaptiste Daroussin static int
uvideo_s_fmt(struct uvideo_softc * sc,struct v4l2_format * fmt)3160b711ef9cSBaptiste Daroussin uvideo_s_fmt(struct uvideo_softc *sc, struct v4l2_format *fmt)
3161b711ef9cSBaptiste Daroussin {
3162b711ef9cSBaptiste Daroussin struct uvideo_format_group *fmtgrp_save;
3163b711ef9cSBaptiste Daroussin struct usb_video_frame_desc *frame_save;
3164b711ef9cSBaptiste Daroussin struct uvideo_res r;
3165b711ef9cSBaptiste Daroussin int found, i;
3166b711ef9cSBaptiste Daroussin usb_error_t error;
3167b711ef9cSBaptiste Daroussin
3168b711ef9cSBaptiste Daroussin if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
3169b711ef9cSBaptiste Daroussin return (EINVAL);
3170b711ef9cSBaptiste Daroussin
3171b711ef9cSBaptiste Daroussin DPRINTFN(1, "s_fmt: requested %dx%d\n",
3172b711ef9cSBaptiste Daroussin fmt->fmt.pix.width, fmt->fmt.pix.height);
3173b711ef9cSBaptiste Daroussin
3174b711ef9cSBaptiste Daroussin /* Search requested pixel format */
3175b711ef9cSBaptiste Daroussin for (found = 0, i = 0; i < sc->sc_fmtgrp_num; i++) {
3176b711ef9cSBaptiste Daroussin if (fmt->fmt.pix.pixelformat == sc->sc_fmtgrp[i].pixelformat) {
3177b711ef9cSBaptiste Daroussin found = 1;
3178b711ef9cSBaptiste Daroussin break;
3179b711ef9cSBaptiste Daroussin }
3180b711ef9cSBaptiste Daroussin }
3181b711ef9cSBaptiste Daroussin if (found == 0)
3182b711ef9cSBaptiste Daroussin return (EINVAL);
3183b711ef9cSBaptiste Daroussin
3184b711ef9cSBaptiste Daroussin if (sc->sc_fmtgrp[i].frame_num == 0) {
3185b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev, "no frame descriptors!\n");
3186b711ef9cSBaptiste Daroussin return (EINVAL);
3187b711ef9cSBaptiste Daroussin }
3188b711ef9cSBaptiste Daroussin
3189b711ef9cSBaptiste Daroussin uvideo_find_res(sc, i, fmt->fmt.pix.width, fmt->fmt.pix.height, &r);
3190b711ef9cSBaptiste Daroussin
3191b711ef9cSBaptiste Daroussin /* Save current format in case negotiation fails */
3192b711ef9cSBaptiste Daroussin fmtgrp_save = sc->sc_fmtgrp_cur;
3193b711ef9cSBaptiste Daroussin frame_save = sc->sc_fmtgrp_cur->frame_cur;
3194b711ef9cSBaptiste Daroussin
3195b711ef9cSBaptiste Daroussin sc->sc_fmtgrp_cur = &sc->sc_fmtgrp[i];
3196b711ef9cSBaptiste Daroussin sc->sc_fmtgrp[i].frame_cur = sc->sc_fmtgrp[i].frame[r.fidx];
3197b711ef9cSBaptiste Daroussin
3198b711ef9cSBaptiste Daroussin error = uvideo_vs_negotiation(sc, 1);
3199b711ef9cSBaptiste Daroussin if (error != USB_ERR_NORMAL_COMPLETION) {
3200b711ef9cSBaptiste Daroussin sc->sc_fmtgrp_cur = fmtgrp_save;
3201b711ef9cSBaptiste Daroussin sc->sc_fmtgrp_cur->frame_cur = frame_save;
3202b711ef9cSBaptiste Daroussin return (EINVAL);
3203b711ef9cSBaptiste Daroussin }
3204b711ef9cSBaptiste Daroussin sc->sc_negotiated_flag = 1;
3205b711ef9cSBaptiste Daroussin
3206b711ef9cSBaptiste Daroussin fmt->fmt.pix.width = r.width;
3207b711ef9cSBaptiste Daroussin fmt->fmt.pix.height = r.height;
3208b711ef9cSBaptiste Daroussin fmt->fmt.pix.sizeimage =
3209b711ef9cSBaptiste Daroussin UGETDW(sc->sc_desc_probe.dwMaxVideoFrameSize);
3210b711ef9cSBaptiste Daroussin
3211b711ef9cSBaptiste Daroussin DPRINTFN(1, "s_fmt: offered %dx%d\n", r.width, r.height);
3212b711ef9cSBaptiste Daroussin
3213b711ef9cSBaptiste Daroussin return (0);
3214b711ef9cSBaptiste Daroussin }
3215b711ef9cSBaptiste Daroussin
3216b711ef9cSBaptiste Daroussin static int
uvideo_g_fmt(struct uvideo_softc * sc,struct v4l2_format * fmt)3217b711ef9cSBaptiste Daroussin uvideo_g_fmt(struct uvideo_softc *sc, struct v4l2_format *fmt)
3218b711ef9cSBaptiste Daroussin {
3219b711ef9cSBaptiste Daroussin struct usb_video_frame_desc *frame;
3220b711ef9cSBaptiste Daroussin uint32_t type;
3221b711ef9cSBaptiste Daroussin
3222b711ef9cSBaptiste Daroussin type = fmt->type;
3223b711ef9cSBaptiste Daroussin if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
3224b711ef9cSBaptiste Daroussin return (EINVAL);
3225b711ef9cSBaptiste Daroussin
3226b711ef9cSBaptiste Daroussin bzero(fmt, sizeof(*fmt));
3227b711ef9cSBaptiste Daroussin fmt->type = type;
3228b711ef9cSBaptiste Daroussin fmt->fmt.pix.pixelformat = sc->sc_fmtgrp_cur->pixelformat;
3229b711ef9cSBaptiste Daroussin fmt->fmt.pix.field = V4L2_FIELD_NONE;
3230b711ef9cSBaptiste Daroussin
3231b711ef9cSBaptiste Daroussin frame = sc->sc_fmtgrp_cur->frame_cur;
3232b711ef9cSBaptiste Daroussin fmt->fmt.pix.width = UGETW(UVIDEO_FRAME_FIELD(frame, wWidth));
3233b711ef9cSBaptiste Daroussin fmt->fmt.pix.height = UGETW(UVIDEO_FRAME_FIELD(frame, wHeight));
3234b711ef9cSBaptiste Daroussin fmt->fmt.pix.sizeimage =
3235b711ef9cSBaptiste Daroussin UGETDW(sc->sc_desc_probe.dwMaxVideoFrameSize);
3236b711ef9cSBaptiste Daroussin
3237b711ef9cSBaptiste Daroussin if (sc->sc_fmtgrp_cur->has_colorformat) {
3238b711ef9cSBaptiste Daroussin fmt->fmt.pix.colorspace = sc->sc_fmtgrp_cur->colorspace;
3239b711ef9cSBaptiste Daroussin fmt->fmt.pix.xfer_func = sc->sc_fmtgrp_cur->xfer_func;
3240b711ef9cSBaptiste Daroussin fmt->fmt.pix.ycbcr_enc = sc->sc_fmtgrp_cur->ycbcr_enc;
3241b711ef9cSBaptiste Daroussin }
3242b711ef9cSBaptiste Daroussin
3243b711ef9cSBaptiste Daroussin
3244b711ef9cSBaptiste Daroussin return (0);
3245b711ef9cSBaptiste Daroussin }
3246b711ef9cSBaptiste Daroussin
3247b711ef9cSBaptiste Daroussin static int
uvideo_s_parm(struct uvideo_softc * sc,struct v4l2_streamparm * parm)3248b711ef9cSBaptiste Daroussin uvideo_s_parm(struct uvideo_softc *sc, struct v4l2_streamparm *parm)
3249b711ef9cSBaptiste Daroussin {
3250b711ef9cSBaptiste Daroussin usb_error_t error;
3251b711ef9cSBaptiste Daroussin
3252b711ef9cSBaptiste Daroussin if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
3253b711ef9cSBaptiste Daroussin if (parm->parm.capture.timeperframe.numerator == 0 ||
3254b711ef9cSBaptiste Daroussin parm->parm.capture.timeperframe.denominator == 0)
3255b711ef9cSBaptiste Daroussin sc->sc_frame_rate = 0;
3256b711ef9cSBaptiste Daroussin else
3257b711ef9cSBaptiste Daroussin sc->sc_frame_rate =
3258b711ef9cSBaptiste Daroussin parm->parm.capture.timeperframe.denominator /
3259b711ef9cSBaptiste Daroussin parm->parm.capture.timeperframe.numerator;
3260b711ef9cSBaptiste Daroussin } else
3261b711ef9cSBaptiste Daroussin return (EINVAL);
3262b711ef9cSBaptiste Daroussin
3263b711ef9cSBaptiste Daroussin /* Renegotiate if needed */
3264b711ef9cSBaptiste Daroussin if (sc->sc_negotiated_flag) {
3265b711ef9cSBaptiste Daroussin error = uvideo_vs_negotiation(sc, 1);
3266b711ef9cSBaptiste Daroussin if (error != USB_ERR_NORMAL_COMPLETION)
3267b711ef9cSBaptiste Daroussin return (EINVAL);
3268b711ef9cSBaptiste Daroussin }
3269b711ef9cSBaptiste Daroussin
3270b711ef9cSBaptiste Daroussin /* Return current parameters (zeroes reserved fields) */
3271b711ef9cSBaptiste Daroussin return (uvideo_g_parm(sc, parm));
3272b711ef9cSBaptiste Daroussin }
3273b711ef9cSBaptiste Daroussin
3274b711ef9cSBaptiste Daroussin static int
uvideo_g_parm(struct uvideo_softc * sc,struct v4l2_streamparm * parm)3275b711ef9cSBaptiste Daroussin uvideo_g_parm(struct uvideo_softc *sc, struct v4l2_streamparm *parm)
3276b711ef9cSBaptiste Daroussin {
3277b711ef9cSBaptiste Daroussin uint32_t type;
3278b711ef9cSBaptiste Daroussin
3279b711ef9cSBaptiste Daroussin type = parm->type;
3280b711ef9cSBaptiste Daroussin if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
3281b711ef9cSBaptiste Daroussin return (EINVAL);
3282b711ef9cSBaptiste Daroussin
3283b711ef9cSBaptiste Daroussin bzero(parm, sizeof(*parm));
3284b711ef9cSBaptiste Daroussin parm->type = type;
3285b711ef9cSBaptiste Daroussin parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
3286b711ef9cSBaptiste Daroussin parm->parm.capture.capturemode = 0;
3287b711ef9cSBaptiste Daroussin parm->parm.capture.readbuffers = UVIDEO_MAX_BUFFERS;
3288b711ef9cSBaptiste Daroussin parm->parm.capture.timeperframe.numerator =
3289b711ef9cSBaptiste Daroussin UGETDW(sc->sc_desc_probe.dwFrameInterval);
3290b711ef9cSBaptiste Daroussin parm->parm.capture.timeperframe.denominator = 10000000;
3291b711ef9cSBaptiste Daroussin
3292b711ef9cSBaptiste Daroussin return (0);
3293b711ef9cSBaptiste Daroussin }
3294b711ef9cSBaptiste Daroussin
3295b711ef9cSBaptiste Daroussin static int
uvideo_enum_input(struct uvideo_softc * sc,struct v4l2_input * input)3296b711ef9cSBaptiste Daroussin uvideo_enum_input(struct uvideo_softc *sc, struct v4l2_input *input)
3297b711ef9cSBaptiste Daroussin {
3298b711ef9cSBaptiste Daroussin uint32_t idx;
3299b711ef9cSBaptiste Daroussin
3300b711ef9cSBaptiste Daroussin idx = input->index;
3301b711ef9cSBaptiste Daroussin if (idx != 0)
3302b711ef9cSBaptiste Daroussin return (EINVAL);
3303b711ef9cSBaptiste Daroussin
3304b711ef9cSBaptiste Daroussin bzero(input, sizeof(*input));
3305b711ef9cSBaptiste Daroussin input->index = idx;
3306b711ef9cSBaptiste Daroussin strlcpy(input->name, "Camera Terminal", sizeof(input->name));
3307b711ef9cSBaptiste Daroussin input->type = V4L2_INPUT_TYPE_CAMERA;
3308b711ef9cSBaptiste Daroussin input->status = 0; /* no error */
3309b711ef9cSBaptiste Daroussin input->std = 0; /* no standard TV norms */
3310b711ef9cSBaptiste Daroussin
3311b711ef9cSBaptiste Daroussin return (0);
3312b711ef9cSBaptiste Daroussin }
3313b711ef9cSBaptiste Daroussin
3314b711ef9cSBaptiste Daroussin static int
uvideo_s_input(struct uvideo_softc * sc,int input)3315b711ef9cSBaptiste Daroussin uvideo_s_input(struct uvideo_softc *sc, int input)
3316b711ef9cSBaptiste Daroussin {
3317b711ef9cSBaptiste Daroussin
3318b711ef9cSBaptiste Daroussin if (input != 0)
3319b711ef9cSBaptiste Daroussin return (EINVAL);
3320b711ef9cSBaptiste Daroussin
3321b711ef9cSBaptiste Daroussin return (0);
3322b711ef9cSBaptiste Daroussin }
3323b711ef9cSBaptiste Daroussin
3324b711ef9cSBaptiste Daroussin static int
uvideo_g_input(struct uvideo_softc * sc,int * input)3325b711ef9cSBaptiste Daroussin uvideo_g_input(struct uvideo_softc *sc, int *input)
3326b711ef9cSBaptiste Daroussin {
3327b711ef9cSBaptiste Daroussin
3328b711ef9cSBaptiste Daroussin *input = 0;
3329b711ef9cSBaptiste Daroussin return (0);
3330b711ef9cSBaptiste Daroussin }
3331b711ef9cSBaptiste Daroussin
3332b711ef9cSBaptiste Daroussin static int
uvideo_reqbufs(struct uvideo_softc * sc,struct v4l2_requestbuffers * rb)3333b711ef9cSBaptiste Daroussin uvideo_reqbufs(struct uvideo_softc *sc, struct v4l2_requestbuffers *rb)
3334b711ef9cSBaptiste Daroussin {
3335b711ef9cSBaptiste Daroussin int i, buf_size, buf_size_total;
3336b711ef9cSBaptiste Daroussin
3337b711ef9cSBaptiste Daroussin DPRINTFN(1, "reqbufs: count=%d\n", rb->count);
3338b711ef9cSBaptiste Daroussin
3339b711ef9cSBaptiste Daroussin if (rb->count == 0)
3340b711ef9cSBaptiste Daroussin return (EINVAL);
3341b711ef9cSBaptiste Daroussin
3342b711ef9cSBaptiste Daroussin if (sc->sc_mmap_count > 0 || sc->sc_mmap_buffer != NULL) {
3343b711ef9cSBaptiste Daroussin DPRINTFN(1, "mmap buffers already allocated\n");
3344b711ef9cSBaptiste Daroussin return (EINVAL);
3345b711ef9cSBaptiste Daroussin }
3346b711ef9cSBaptiste Daroussin
3347b711ef9cSBaptiste Daroussin if (rb->count > UVIDEO_MAX_BUFFERS)
3348b711ef9cSBaptiste Daroussin sc->sc_mmap_count = UVIDEO_MAX_BUFFERS;
3349b711ef9cSBaptiste Daroussin else
3350b711ef9cSBaptiste Daroussin sc->sc_mmap_count = rb->count;
3351b711ef9cSBaptiste Daroussin
3352b711ef9cSBaptiste Daroussin buf_size = UGETDW(sc->sc_desc_probe.dwMaxVideoFrameSize);
3353b711ef9cSBaptiste Daroussin buf_size_total = sc->sc_mmap_count * buf_size;
3354b711ef9cSBaptiste Daroussin buf_size_total = round_page(buf_size_total);
3355b711ef9cSBaptiste Daroussin
3356b711ef9cSBaptiste Daroussin sc->sc_mmap_buffer = contigmalloc(buf_size_total, M_USBDEV,
3357b711ef9cSBaptiste Daroussin M_WAITOK | M_ZERO, 0, ~0UL, PAGE_SIZE, 0);
3358b711ef9cSBaptiste Daroussin if (sc->sc_mmap_buffer == NULL) {
3359b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev, "can't allocate mmap buffer!\n");
3360b711ef9cSBaptiste Daroussin sc->sc_mmap_count = 0;
3361b711ef9cSBaptiste Daroussin return (ENOMEM);
3362b711ef9cSBaptiste Daroussin }
3363b711ef9cSBaptiste Daroussin sc->sc_mmap_buffer_size = buf_size_total;
3364b711ef9cSBaptiste Daroussin
3365b711ef9cSBaptiste Daroussin DPRINTFN(1, "allocated %d bytes mmap buffer\n", buf_size_total);
3366b711ef9cSBaptiste Daroussin
3367b711ef9cSBaptiste Daroussin for (i = 0; i < sc->sc_mmap_count; i++) {
3368b711ef9cSBaptiste Daroussin sc->sc_mmap[i].buf = sc->sc_mmap_buffer + (i * buf_size);
3369b711ef9cSBaptiste Daroussin
3370b711ef9cSBaptiste Daroussin sc->sc_mmap[i].v4l2_buf.index = i;
3371b711ef9cSBaptiste Daroussin sc->sc_mmap[i].v4l2_buf.m.offset = i * buf_size;
3372b711ef9cSBaptiste Daroussin sc->sc_mmap[i].v4l2_buf.length = buf_size;
3373b711ef9cSBaptiste Daroussin sc->sc_mmap[i].v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
3374b711ef9cSBaptiste Daroussin sc->sc_mmap[i].v4l2_buf.sequence = 0;
3375b711ef9cSBaptiste Daroussin sc->sc_mmap[i].v4l2_buf.field = V4L2_FIELD_NONE;
3376b711ef9cSBaptiste Daroussin sc->sc_mmap[i].v4l2_buf.memory = V4L2_MEMORY_MMAP;
3377b711ef9cSBaptiste Daroussin sc->sc_mmap[i].v4l2_buf.flags = V4L2_BUF_FLAG_MAPPED;
3378b711ef9cSBaptiste Daroussin }
3379b711ef9cSBaptiste Daroussin
3380b711ef9cSBaptiste Daroussin sc->sc_mmap_buffer_idx = 0;
3381b711ef9cSBaptiste Daroussin sc->sc_mmap_cur = NULL;
3382b711ef9cSBaptiste Daroussin
3383b711ef9cSBaptiste Daroussin rb->count = sc->sc_mmap_count;
3384b711ef9cSBaptiste Daroussin rb->capabilities = V4L2_BUF_CAP_SUPPORTS_MMAP;
3385b711ef9cSBaptiste Daroussin
3386b711ef9cSBaptiste Daroussin return (0);
3387b711ef9cSBaptiste Daroussin }
3388b711ef9cSBaptiste Daroussin
3389b711ef9cSBaptiste Daroussin static int
uvideo_querybuf(struct uvideo_softc * sc,struct v4l2_buffer * qb)3390b711ef9cSBaptiste Daroussin uvideo_querybuf(struct uvideo_softc *sc, struct v4l2_buffer *qb)
3391b711ef9cSBaptiste Daroussin {
3392b711ef9cSBaptiste Daroussin
3393b711ef9cSBaptiste Daroussin if (qb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
3394b711ef9cSBaptiste Daroussin qb->memory != V4L2_MEMORY_MMAP ||
3395b711ef9cSBaptiste Daroussin qb->index >= sc->sc_mmap_count)
3396b711ef9cSBaptiste Daroussin return (EINVAL);
3397b711ef9cSBaptiste Daroussin
3398b711ef9cSBaptiste Daroussin bcopy(&sc->sc_mmap[qb->index].v4l2_buf, qb,
3399b711ef9cSBaptiste Daroussin sizeof(struct v4l2_buffer));
3400b711ef9cSBaptiste Daroussin
3401b711ef9cSBaptiste Daroussin return (0);
3402b711ef9cSBaptiste Daroussin }
3403b711ef9cSBaptiste Daroussin
3404b711ef9cSBaptiste Daroussin static int
uvideo_qbuf(struct uvideo_softc * sc,struct v4l2_buffer * qb)3405b711ef9cSBaptiste Daroussin uvideo_qbuf(struct uvideo_softc *sc, struct v4l2_buffer *qb)
3406b711ef9cSBaptiste Daroussin {
3407b711ef9cSBaptiste Daroussin
3408b711ef9cSBaptiste Daroussin if (qb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
3409b711ef9cSBaptiste Daroussin qb->memory != V4L2_MEMORY_MMAP ||
3410b711ef9cSBaptiste Daroussin qb->index >= sc->sc_mmap_count)
3411b711ef9cSBaptiste Daroussin return (EINVAL);
3412b711ef9cSBaptiste Daroussin
3413b711ef9cSBaptiste Daroussin sc->sc_mmap[qb->index].v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE;
3414b711ef9cSBaptiste Daroussin sc->sc_mmap[qb->index].v4l2_buf.flags |= V4L2_BUF_FLAG_QUEUED;
3415b711ef9cSBaptiste Daroussin
3416b711ef9cSBaptiste Daroussin DPRINTFN(2, "buffer %d ready for queueing\n", qb->index);
3417b711ef9cSBaptiste Daroussin
3418b711ef9cSBaptiste Daroussin return (0);
3419b711ef9cSBaptiste Daroussin }
3420b711ef9cSBaptiste Daroussin
3421b711ef9cSBaptiste Daroussin static int
uvideo_dqbuf(struct uvideo_softc * sc,struct v4l2_buffer * dqb)3422b711ef9cSBaptiste Daroussin uvideo_dqbuf(struct uvideo_softc *sc, struct v4l2_buffer *dqb)
3423b711ef9cSBaptiste Daroussin {
3424b711ef9cSBaptiste Daroussin struct uvideo_mmap *mmap;
3425b711ef9cSBaptiste Daroussin int error;
3426b711ef9cSBaptiste Daroussin
3427b711ef9cSBaptiste Daroussin if (dqb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
3428b711ef9cSBaptiste Daroussin dqb->memory != V4L2_MEMORY_MMAP)
3429b711ef9cSBaptiste Daroussin return (EINVAL);
3430b711ef9cSBaptiste Daroussin
3431b711ef9cSBaptiste Daroussin if (STAILQ_EMPTY(&sc->sc_mmap_q)) {
3432b711ef9cSBaptiste Daroussin error = tsleep(sc, PCATCH, "uvdqbuf", hz * 10);
3433b711ef9cSBaptiste Daroussin if (error)
3434b711ef9cSBaptiste Daroussin return (EINVAL);
3435b711ef9cSBaptiste Daroussin }
3436b711ef9cSBaptiste Daroussin
3437b711ef9cSBaptiste Daroussin mmap = STAILQ_FIRST(&sc->sc_mmap_q);
3438b711ef9cSBaptiste Daroussin if (mmap == NULL)
3439b711ef9cSBaptiste Daroussin return (EINVAL);
3440b711ef9cSBaptiste Daroussin
3441b711ef9cSBaptiste Daroussin bcopy(&mmap->v4l2_buf, dqb, sizeof(struct v4l2_buffer));
3442b711ef9cSBaptiste Daroussin
3443b711ef9cSBaptiste Daroussin mmap->v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE;
3444b711ef9cSBaptiste Daroussin mmap->v4l2_buf.flags &= ~V4L2_BUF_FLAG_QUEUED;
3445b711ef9cSBaptiste Daroussin
3446b711ef9cSBaptiste Daroussin DPRINTFN(2, "frame dequeued from index %d\n",
3447b711ef9cSBaptiste Daroussin mmap->v4l2_buf.index);
3448b711ef9cSBaptiste Daroussin STAILQ_REMOVE_HEAD(&sc->sc_mmap_q, q_frames);
3449b711ef9cSBaptiste Daroussin
3450b711ef9cSBaptiste Daroussin return (0);
3451b711ef9cSBaptiste Daroussin }
3452b711ef9cSBaptiste Daroussin
3453b711ef9cSBaptiste Daroussin static int
uvideo_streamon(struct uvideo_softc * sc,int type)3454b711ef9cSBaptiste Daroussin uvideo_streamon(struct uvideo_softc *sc, int type)
3455b711ef9cSBaptiste Daroussin {
3456b711ef9cSBaptiste Daroussin usb_error_t error;
3457b711ef9cSBaptiste Daroussin
3458b711ef9cSBaptiste Daroussin if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
3459b711ef9cSBaptiste Daroussin return (EINVAL);
3460b711ef9cSBaptiste Daroussin
3461b711ef9cSBaptiste Daroussin if (sc->sc_streaming)
3462b711ef9cSBaptiste Daroussin return (0);
3463b711ef9cSBaptiste Daroussin
3464b711ef9cSBaptiste Daroussin sc->sc_vidmode = VIDMODE_MMAP;
3465b711ef9cSBaptiste Daroussin
3466b711ef9cSBaptiste Daroussin error = uvideo_vs_init(sc);
3467b711ef9cSBaptiste Daroussin if (error != USB_ERR_NORMAL_COMPLETION)
3468b711ef9cSBaptiste Daroussin return (EINVAL);
3469b711ef9cSBaptiste Daroussin
3470b711ef9cSBaptiste Daroussin mtx_lock(&sc->sc_mtx);
3471b711ef9cSBaptiste Daroussin sc->sc_streaming = 1;
3472b711ef9cSBaptiste Daroussin if (sc->sc_vs_cur->bulk_endpoint)
3473b711ef9cSBaptiste Daroussin usbd_transfer_start(sc->sc_xfer[0]);
3474b711ef9cSBaptiste Daroussin else {
3475b711ef9cSBaptiste Daroussin int i;
3476b711ef9cSBaptiste Daroussin for (i = 0; i < UVIDEO_IXFERS; i++)
3477b711ef9cSBaptiste Daroussin usbd_transfer_start(sc->sc_xfer[i]);
3478b711ef9cSBaptiste Daroussin }
3479b711ef9cSBaptiste Daroussin mtx_unlock(&sc->sc_mtx);
3480b711ef9cSBaptiste Daroussin
3481b711ef9cSBaptiste Daroussin return (0);
3482b711ef9cSBaptiste Daroussin }
3483b711ef9cSBaptiste Daroussin
3484b711ef9cSBaptiste Daroussin static int
uvideo_streamoff(struct uvideo_softc * sc,int type)3485b711ef9cSBaptiste Daroussin uvideo_streamoff(struct uvideo_softc *sc, int type)
3486b711ef9cSBaptiste Daroussin {
3487b711ef9cSBaptiste Daroussin
3488b711ef9cSBaptiste Daroussin if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
3489b711ef9cSBaptiste Daroussin return (EINVAL);
3490b711ef9cSBaptiste Daroussin
3491b711ef9cSBaptiste Daroussin if (!sc->sc_streaming)
3492b711ef9cSBaptiste Daroussin return (0);
3493b711ef9cSBaptiste Daroussin
3494b711ef9cSBaptiste Daroussin mtx_lock(&sc->sc_mtx);
3495b711ef9cSBaptiste Daroussin sc->sc_streaming = 0;
3496b711ef9cSBaptiste Daroussin mtx_unlock(&sc->sc_mtx);
3497b711ef9cSBaptiste Daroussin
3498b711ef9cSBaptiste Daroussin uvideo_vs_close(sc);
3499b711ef9cSBaptiste Daroussin uvideo_vs_free_frame(sc);
3500b711ef9cSBaptiste Daroussin
3501b711ef9cSBaptiste Daroussin return (0);
3502b711ef9cSBaptiste Daroussin }
3503b711ef9cSBaptiste Daroussin
3504b711ef9cSBaptiste Daroussin static int
uvideo_try_fmt(struct uvideo_softc * sc,struct v4l2_format * fmt)3505b711ef9cSBaptiste Daroussin uvideo_try_fmt(struct uvideo_softc *sc, struct v4l2_format *fmt)
3506b711ef9cSBaptiste Daroussin {
3507b711ef9cSBaptiste Daroussin struct uvideo_res r;
3508b711ef9cSBaptiste Daroussin int found, i;
3509b711ef9cSBaptiste Daroussin
3510b711ef9cSBaptiste Daroussin if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
3511b711ef9cSBaptiste Daroussin return (EINVAL);
3512b711ef9cSBaptiste Daroussin
3513b711ef9cSBaptiste Daroussin for (found = 0, i = 0; i < sc->sc_fmtgrp_num; i++) {
3514b711ef9cSBaptiste Daroussin if (fmt->fmt.pix.pixelformat == sc->sc_fmtgrp[i].pixelformat) {
3515b711ef9cSBaptiste Daroussin found = 1;
3516b711ef9cSBaptiste Daroussin break;
3517b711ef9cSBaptiste Daroussin }
3518b711ef9cSBaptiste Daroussin }
3519b711ef9cSBaptiste Daroussin if (found == 0)
3520b711ef9cSBaptiste Daroussin return (EINVAL);
3521b711ef9cSBaptiste Daroussin
3522b711ef9cSBaptiste Daroussin uvideo_find_res(sc, i, fmt->fmt.pix.width, fmt->fmt.pix.height, &r);
3523b711ef9cSBaptiste Daroussin
3524b711ef9cSBaptiste Daroussin fmt->fmt.pix.width = r.width;
3525b711ef9cSBaptiste Daroussin fmt->fmt.pix.height = r.height;
3526b711ef9cSBaptiste Daroussin fmt->fmt.pix.sizeimage = sc->sc_frame_buffer.buf_size;
3527b711ef9cSBaptiste Daroussin
3528b711ef9cSBaptiste Daroussin return (0);
3529b711ef9cSBaptiste Daroussin }
3530b711ef9cSBaptiste Daroussin
3531b711ef9cSBaptiste Daroussin static int
uvideo_queryctrl(struct uvideo_softc * sc,struct v4l2_queryctrl * qctrl)3532b711ef9cSBaptiste Daroussin uvideo_queryctrl(struct uvideo_softc *sc, struct v4l2_queryctrl *qctrl)
3533b711ef9cSBaptiste Daroussin {
3534b711ef9cSBaptiste Daroussin int i, ret = 0;
3535b711ef9cSBaptiste Daroussin usb_error_t error;
3536b711ef9cSBaptiste Daroussin uint8_t *ctrl_data;
3537b711ef9cSBaptiste Daroussin uint16_t ctrl_len;
3538d0450cbeSBaptiste Daroussin uint8_t unit_id;
3539b711ef9cSBaptiste Daroussin
3540b711ef9cSBaptiste Daroussin i = uvideo_find_ctrl(sc, qctrl->id);
3541b711ef9cSBaptiste Daroussin if (i == EINVAL)
3542b711ef9cSBaptiste Daroussin return (i);
3543b711ef9cSBaptiste Daroussin
3544d0450cbeSBaptiste Daroussin if (sc->sc_desc_vc_ct_cur != NULL)
3545d0450cbeSBaptiste Daroussin unit_id = sc->sc_desc_vc_ct_cur->bTerminalID;
3546d0450cbeSBaptiste Daroussin else
3547d0450cbeSBaptiste Daroussin unit_id = sc->sc_desc_vc_pu_cur->bUnitID;
3548d0450cbeSBaptiste Daroussin
3549b711ef9cSBaptiste Daroussin ctrl_len = uvideo_ctrls[i].ctrl_len;
3550d0450cbeSBaptiste Daroussin if (ctrl_len < 1 || ctrl_len > 4) {
3551b711ef9cSBaptiste Daroussin device_printf(sc->sc_dev,
3552b711ef9cSBaptiste Daroussin "invalid control length: %d\n", ctrl_len);
3553b711ef9cSBaptiste Daroussin return (EINVAL);
3554b711ef9cSBaptiste Daroussin }
3555b711ef9cSBaptiste Daroussin
3556b711ef9cSBaptiste Daroussin ctrl_data = malloc(ctrl_len, M_USBDEV, M_WAITOK | M_ZERO);
3557b711ef9cSBaptiste Daroussin if (ctrl_data == NULL)
3558b711ef9cSBaptiste Daroussin return (ENOMEM);
3559b711ef9cSBaptiste Daroussin
3560b711ef9cSBaptiste Daroussin qctrl->type = uvideo_ctrls[i].type;
3561b711ef9cSBaptiste Daroussin strlcpy(qctrl->name, uvideo_ctrls[i].name, sizeof(qctrl->name));
3562b711ef9cSBaptiste Daroussin
3563b711ef9cSBaptiste Daroussin /* get minimum */
3564b711ef9cSBaptiste Daroussin error = uvideo_vc_get_ctrl(sc, ctrl_data, GET_MIN,
3565d0450cbeSBaptiste Daroussin unit_id,
3566b711ef9cSBaptiste Daroussin uvideo_ctrls[i].ctrl_selector, uvideo_ctrls[i].ctrl_len);
3567b711ef9cSBaptiste Daroussin if (error != USB_ERR_NORMAL_COMPLETION) {
3568b711ef9cSBaptiste Daroussin ret = EINVAL;
3569b711ef9cSBaptiste Daroussin goto out;
3570b711ef9cSBaptiste Daroussin }
3571b711ef9cSBaptiste Daroussin switch (ctrl_len) {
3572b711ef9cSBaptiste Daroussin case 1:
3573b711ef9cSBaptiste Daroussin qctrl->minimum = uvideo_ctrls[i].sig ?
3574b711ef9cSBaptiste Daroussin *(int8_t *)ctrl_data : *ctrl_data;
3575b711ef9cSBaptiste Daroussin break;
3576b711ef9cSBaptiste Daroussin case 2:
3577b711ef9cSBaptiste Daroussin qctrl->minimum = uvideo_ctrls[i].sig ?
3578b711ef9cSBaptiste Daroussin (int16_t)UGETW(ctrl_data) : UGETW(ctrl_data);
3579b711ef9cSBaptiste Daroussin break;
3580d0450cbeSBaptiste Daroussin case 4:
3581d0450cbeSBaptiste Daroussin qctrl->minimum = uvideo_ctrls[i].sig ?
3582d0450cbeSBaptiste Daroussin (int32_t)UGETDW(ctrl_data) : UGETDW(ctrl_data);
3583d0450cbeSBaptiste Daroussin break;
3584b711ef9cSBaptiste Daroussin }
3585b711ef9cSBaptiste Daroussin
3586b711ef9cSBaptiste Daroussin /* get maximum */
3587b711ef9cSBaptiste Daroussin error = uvideo_vc_get_ctrl(sc, ctrl_data, GET_MAX,
3588d0450cbeSBaptiste Daroussin unit_id,
3589b711ef9cSBaptiste Daroussin uvideo_ctrls[i].ctrl_selector, uvideo_ctrls[i].ctrl_len);
3590b711ef9cSBaptiste Daroussin if (error != USB_ERR_NORMAL_COMPLETION) {
3591b711ef9cSBaptiste Daroussin ret = EINVAL;
3592b711ef9cSBaptiste Daroussin goto out;
3593b711ef9cSBaptiste Daroussin }
3594b711ef9cSBaptiste Daroussin switch (ctrl_len) {
3595b711ef9cSBaptiste Daroussin case 1:
3596b711ef9cSBaptiste Daroussin qctrl->maximum = uvideo_ctrls[i].sig ?
3597b711ef9cSBaptiste Daroussin *(int8_t *)ctrl_data : *ctrl_data;
3598b711ef9cSBaptiste Daroussin break;
3599b711ef9cSBaptiste Daroussin case 2:
3600b711ef9cSBaptiste Daroussin qctrl->maximum = uvideo_ctrls[i].sig ?
3601b711ef9cSBaptiste Daroussin (int16_t)UGETW(ctrl_data) : UGETW(ctrl_data);
3602b711ef9cSBaptiste Daroussin break;
3603d0450cbeSBaptiste Daroussin case 4:
3604d0450cbeSBaptiste Daroussin qctrl->maximum = uvideo_ctrls[i].sig ?
3605d0450cbeSBaptiste Daroussin (int32_t)UGETDW(ctrl_data) : UGETDW(ctrl_data);
3606d0450cbeSBaptiste Daroussin break;
3607b711ef9cSBaptiste Daroussin }
3608b711ef9cSBaptiste Daroussin
3609b711ef9cSBaptiste Daroussin /* get resolution/step */
3610b711ef9cSBaptiste Daroussin error = uvideo_vc_get_ctrl(sc, ctrl_data, GET_RES,
3611d0450cbeSBaptiste Daroussin unit_id,
3612b711ef9cSBaptiste Daroussin uvideo_ctrls[i].ctrl_selector, uvideo_ctrls[i].ctrl_len);
3613b711ef9cSBaptiste Daroussin if (error != USB_ERR_NORMAL_COMPLETION) {
3614b711ef9cSBaptiste Daroussin ret = EINVAL;
3615b711ef9cSBaptiste Daroussin goto out;
3616b711ef9cSBaptiste Daroussin }
3617b711ef9cSBaptiste Daroussin switch (ctrl_len) {
3618b711ef9cSBaptiste Daroussin case 1:
3619b711ef9cSBaptiste Daroussin qctrl->step = uvideo_ctrls[i].sig ?
3620b711ef9cSBaptiste Daroussin *(int8_t *)ctrl_data : *ctrl_data;
3621b711ef9cSBaptiste Daroussin break;
3622b711ef9cSBaptiste Daroussin case 2:
3623b711ef9cSBaptiste Daroussin qctrl->step = uvideo_ctrls[i].sig ?
3624b711ef9cSBaptiste Daroussin (int16_t)UGETW(ctrl_data) : UGETW(ctrl_data);
3625b711ef9cSBaptiste Daroussin break;
3626d0450cbeSBaptiste Daroussin case 4:
3627d0450cbeSBaptiste Daroussin qctrl->step = uvideo_ctrls[i].sig ?
3628d0450cbeSBaptiste Daroussin (int32_t)UGETDW(ctrl_data) : UGETDW(ctrl_data);
3629d0450cbeSBaptiste Daroussin break;
3630b711ef9cSBaptiste Daroussin }
3631b711ef9cSBaptiste Daroussin
3632b711ef9cSBaptiste Daroussin /* get default */
3633b711ef9cSBaptiste Daroussin error = uvideo_vc_get_ctrl(sc, ctrl_data, GET_DEF,
3634d0450cbeSBaptiste Daroussin unit_id,
3635b711ef9cSBaptiste Daroussin uvideo_ctrls[i].ctrl_selector, uvideo_ctrls[i].ctrl_len);
3636b711ef9cSBaptiste Daroussin if (error != USB_ERR_NORMAL_COMPLETION) {
3637b711ef9cSBaptiste Daroussin ret = EINVAL;
3638b711ef9cSBaptiste Daroussin goto out;
3639b711ef9cSBaptiste Daroussin }
3640b711ef9cSBaptiste Daroussin switch (ctrl_len) {
3641b711ef9cSBaptiste Daroussin case 1:
3642b711ef9cSBaptiste Daroussin qctrl->default_value = uvideo_ctrls[i].sig ?
3643b711ef9cSBaptiste Daroussin *(int8_t *)ctrl_data : *ctrl_data;
3644b711ef9cSBaptiste Daroussin break;
3645b711ef9cSBaptiste Daroussin case 2:
3646b711ef9cSBaptiste Daroussin qctrl->default_value = uvideo_ctrls[i].sig ?
3647b711ef9cSBaptiste Daroussin (int16_t)UGETW(ctrl_data) : UGETW(ctrl_data);
3648b711ef9cSBaptiste Daroussin break;
3649d0450cbeSBaptiste Daroussin case 4:
3650d0450cbeSBaptiste Daroussin qctrl->default_value = uvideo_ctrls[i].sig ?
3651d0450cbeSBaptiste Daroussin (int32_t)UGETDW(ctrl_data) : UGETDW(ctrl_data);
3652d0450cbeSBaptiste Daroussin break;
3653b711ef9cSBaptiste Daroussin }
3654b711ef9cSBaptiste Daroussin
3655b711ef9cSBaptiste Daroussin qctrl->flags = 0;
3656b711ef9cSBaptiste Daroussin
3657b711ef9cSBaptiste Daroussin out:
3658b711ef9cSBaptiste Daroussin free(ctrl_data, M_USBDEV);
3659b711ef9cSBaptiste Daroussin return (ret);
3660b711ef9cSBaptiste Daroussin }
3661b711ef9cSBaptiste Daroussin
3662b711ef9cSBaptiste Daroussin static int
uvideo_g_ctrl(struct uvideo_softc * sc,struct v4l2_control * gctrl)3663b711ef9cSBaptiste Daroussin uvideo_g_ctrl(struct uvideo_softc *sc, struct v4l2_control *gctrl)
3664b711ef9cSBaptiste Daroussin {
3665b711ef9cSBaptiste Daroussin int i, ret = 0;
3666b711ef9cSBaptiste Daroussin usb_error_t error;
3667b711ef9cSBaptiste Daroussin uint8_t *ctrl_data;
3668b711ef9cSBaptiste Daroussin uint16_t ctrl_len;
3669d0450cbeSBaptiste Daroussin uint8_t unit_id;
3670b711ef9cSBaptiste Daroussin
3671b711ef9cSBaptiste Daroussin i = uvideo_find_ctrl(sc, gctrl->id);
3672b711ef9cSBaptiste Daroussin if (i == EINVAL)
3673b711ef9cSBaptiste Daroussin return (i);
3674b711ef9cSBaptiste Daroussin
3675d0450cbeSBaptiste Daroussin if (sc->sc_desc_vc_ct_cur != NULL)
3676d0450cbeSBaptiste Daroussin unit_id = sc->sc_desc_vc_ct_cur->bTerminalID;
3677d0450cbeSBaptiste Daroussin else
3678d0450cbeSBaptiste Daroussin unit_id = sc->sc_desc_vc_pu_cur->bUnitID;
3679d0450cbeSBaptiste Daroussin
3680b711ef9cSBaptiste Daroussin ctrl_len = uvideo_ctrls[i].ctrl_len;
3681d0450cbeSBaptiste Daroussin if (ctrl_len < 1 || ctrl_len > 4)
3682b711ef9cSBaptiste Daroussin return (EINVAL);
3683b711ef9cSBaptiste Daroussin
3684b711ef9cSBaptiste Daroussin ctrl_data = malloc(ctrl_len, M_USBDEV, M_WAITOK | M_ZERO);
3685b711ef9cSBaptiste Daroussin if (ctrl_data == NULL)
3686b711ef9cSBaptiste Daroussin return (ENOMEM);
3687b711ef9cSBaptiste Daroussin
3688b711ef9cSBaptiste Daroussin error = uvideo_vc_get_ctrl(sc, ctrl_data, GET_CUR,
3689d0450cbeSBaptiste Daroussin unit_id,
3690b711ef9cSBaptiste Daroussin uvideo_ctrls[i].ctrl_selector, uvideo_ctrls[i].ctrl_len);
3691b711ef9cSBaptiste Daroussin if (error != USB_ERR_NORMAL_COMPLETION) {
3692b711ef9cSBaptiste Daroussin ret = EINVAL;
3693b711ef9cSBaptiste Daroussin goto out;
3694b711ef9cSBaptiste Daroussin }
3695b711ef9cSBaptiste Daroussin switch (ctrl_len) {
3696b711ef9cSBaptiste Daroussin case 1:
3697b711ef9cSBaptiste Daroussin gctrl->value = uvideo_ctrls[i].sig ?
3698b711ef9cSBaptiste Daroussin *(int8_t *)ctrl_data : *ctrl_data;
3699b711ef9cSBaptiste Daroussin break;
3700b711ef9cSBaptiste Daroussin case 2:
3701b711ef9cSBaptiste Daroussin gctrl->value = uvideo_ctrls[i].sig ?
3702b711ef9cSBaptiste Daroussin (int16_t)UGETW(ctrl_data) : UGETW(ctrl_data);
3703b711ef9cSBaptiste Daroussin break;
3704d0450cbeSBaptiste Daroussin case 4:
3705d0450cbeSBaptiste Daroussin gctrl->value = uvideo_ctrls[i].sig ?
3706d0450cbeSBaptiste Daroussin (int32_t)UGETDW(ctrl_data) : UGETDW(ctrl_data);
3707d0450cbeSBaptiste Daroussin break;
3708b711ef9cSBaptiste Daroussin }
3709b711ef9cSBaptiste Daroussin
3710b711ef9cSBaptiste Daroussin out:
3711b711ef9cSBaptiste Daroussin free(ctrl_data, M_USBDEV);
3712b711ef9cSBaptiste Daroussin return (ret);
3713b711ef9cSBaptiste Daroussin }
3714b711ef9cSBaptiste Daroussin
3715b711ef9cSBaptiste Daroussin static int
uvideo_s_ctrl(struct uvideo_softc * sc,struct v4l2_control * sctrl)3716b711ef9cSBaptiste Daroussin uvideo_s_ctrl(struct uvideo_softc *sc, struct v4l2_control *sctrl)
3717b711ef9cSBaptiste Daroussin {
3718b711ef9cSBaptiste Daroussin int i, ret = 0;
3719b711ef9cSBaptiste Daroussin usb_error_t error;
3720b711ef9cSBaptiste Daroussin uint8_t *ctrl_data;
3721b711ef9cSBaptiste Daroussin uint16_t ctrl_len;
3722d0450cbeSBaptiste Daroussin uint8_t unit_id;
3723b711ef9cSBaptiste Daroussin
3724b711ef9cSBaptiste Daroussin i = uvideo_find_ctrl(sc, sctrl->id);
3725b711ef9cSBaptiste Daroussin if (i == EINVAL)
3726b711ef9cSBaptiste Daroussin return (i);
3727b711ef9cSBaptiste Daroussin
3728d0450cbeSBaptiste Daroussin if (sc->sc_desc_vc_ct_cur != NULL)
3729d0450cbeSBaptiste Daroussin unit_id = sc->sc_desc_vc_ct_cur->bTerminalID;
3730d0450cbeSBaptiste Daroussin else
3731d0450cbeSBaptiste Daroussin unit_id = sc->sc_desc_vc_pu_cur->bUnitID;
3732d0450cbeSBaptiste Daroussin
3733b711ef9cSBaptiste Daroussin ctrl_len = uvideo_ctrls[i].ctrl_len;
3734d0450cbeSBaptiste Daroussin if (ctrl_len < 1 || ctrl_len > 4)
3735b711ef9cSBaptiste Daroussin return (EINVAL);
3736b711ef9cSBaptiste Daroussin
3737b711ef9cSBaptiste Daroussin ctrl_data = malloc(ctrl_len, M_USBDEV, M_WAITOK | M_ZERO);
3738b711ef9cSBaptiste Daroussin if (ctrl_data == NULL)
3739b711ef9cSBaptiste Daroussin return (ENOMEM);
3740b711ef9cSBaptiste Daroussin
3741b711ef9cSBaptiste Daroussin switch (ctrl_len) {
3742b711ef9cSBaptiste Daroussin case 1:
3743b711ef9cSBaptiste Daroussin if (uvideo_ctrls[i].sig)
3744b711ef9cSBaptiste Daroussin *(int8_t *)ctrl_data = sctrl->value;
3745b711ef9cSBaptiste Daroussin else
3746b711ef9cSBaptiste Daroussin *ctrl_data = sctrl->value;
3747b711ef9cSBaptiste Daroussin break;
3748b711ef9cSBaptiste Daroussin case 2:
3749b711ef9cSBaptiste Daroussin USETW(ctrl_data, sctrl->value);
3750b711ef9cSBaptiste Daroussin break;
3751d0450cbeSBaptiste Daroussin case 4:
3752d0450cbeSBaptiste Daroussin USETDW(ctrl_data, sctrl->value);
3753d0450cbeSBaptiste Daroussin break;
3754b711ef9cSBaptiste Daroussin }
3755b711ef9cSBaptiste Daroussin
3756b711ef9cSBaptiste Daroussin error = uvideo_vc_set_ctrl(sc, ctrl_data, SET_CUR,
3757d0450cbeSBaptiste Daroussin unit_id,
3758b711ef9cSBaptiste Daroussin uvideo_ctrls[i].ctrl_selector, uvideo_ctrls[i].ctrl_len);
3759b711ef9cSBaptiste Daroussin if (error != USB_ERR_NORMAL_COMPLETION)
3760b711ef9cSBaptiste Daroussin ret = EINVAL;
3761b711ef9cSBaptiste Daroussin
3762b711ef9cSBaptiste Daroussin free(ctrl_data, M_USBDEV);
3763b711ef9cSBaptiste Daroussin return (ret);
3764b711ef9cSBaptiste Daroussin }
3765