xref: /freebsd/sys/dev/usb/video/uvideo.c (revision 3b6f833c95eb65b29a9f506928467236a11d5169)
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