File: | dev/usb/utvfu.c |
Warning: | line 899, column 8 Access to field 'bAlternateSetting' results in a dereference of a null pointer (loaded from variable 'uid') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: utvfu.c,v 1.19 2022/10/28 15:02:20 kn Exp $ */ | |||
2 | /* | |||
3 | * Copyright (c) 2013 Lubomir Rintel | |||
4 | * Copyright (c) 2013 Federico Simoncelli | |||
5 | * All rights reserved. | |||
6 | * | |||
7 | * Redistribution and use in source and binary forms, with or without | |||
8 | * modification, are permitted provided that the following conditions | |||
9 | * are met: | |||
10 | * 1. Redistributions of source code must retain the above copyright | |||
11 | * notice, this list of conditions, and the following disclaimer, | |||
12 | * without modification. | |||
13 | * 2. The name of the author may not be used to endorse or promote products | |||
14 | * derived from this software without specific prior written permission. | |||
15 | * | |||
16 | * Alternatively, this software may be distributed under the terms of the | |||
17 | * GNU General Public License ("GPL"). | |||
18 | * | |||
19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||
23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||
26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||
27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||
29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
30 | */ | |||
31 | /* | |||
32 | * Fushicai USBTV007 Audio-Video Grabber Driver | |||
33 | * | |||
34 | * Product web site: | |||
35 | * http://www.fushicai.com/products_detail/&productId=d05449ee-b690-42f9-a661-aa7353894bed.html | |||
36 | * | |||
37 | * Following LWN articles were very useful in construction of this driver: | |||
38 | * Video4Linux2 API series: http://lwn.net/Articles/203924/ | |||
39 | * videobuf2 API explanation: http://lwn.net/Articles/447435/ | |||
40 | * Thanks go to Jonathan Corbet for providing this quality documentation. | |||
41 | * He is awesome. | |||
42 | * | |||
43 | * No physical hardware was harmed running Windows during the | |||
44 | * reverse-engineering activity | |||
45 | */ | |||
46 | ||||
47 | #include <sys/param.h> | |||
48 | #include <sys/systm.h> | |||
49 | #include <sys/fcntl.h> | |||
50 | #include <sys/kernel.h> | |||
51 | #include <sys/kthread.h> | |||
52 | #include <sys/malloc.h> | |||
53 | #include <sys/device.h> | |||
54 | #include <sys/audioio.h> | |||
55 | #include <sys/videoio.h> | |||
56 | ||||
57 | #include <uvm/uvm_extern.h> | |||
58 | ||||
59 | #include <machine/bus.h> | |||
60 | ||||
61 | #include <dev/audio_if.h> | |||
62 | #include <dev/usb/usb.h> | |||
63 | #include <dev/usb/usbdi.h> | |||
64 | #include <dev/usb/usbdivar.h> | |||
65 | #include <dev/usb/usb_mem.h> | |||
66 | #include <dev/usb/usbdi_util.h> | |||
67 | #include <dev/usb/usbdevs.h> | |||
68 | #include <dev/video_if.h> | |||
69 | ||||
70 | #include "utvfu.h" | |||
71 | ||||
72 | #ifdef UTVFU_DEBUG | |||
73 | int utvfu_debug = 1; | |||
74 | #define DPRINTF(l, x...) do { if ((l) <= utvfu_debug) printf(x); } while (0) | |||
75 | #else | |||
76 | #define DPRINTF(l, x...) | |||
77 | #endif | |||
78 | ||||
79 | #define DEVNAME(_s)((_s)->sc_dev.dv_xname) ((_s)->sc_dev.dv_xname) | |||
80 | ||||
81 | struct utvfu_norm_params utvfu_norm_params[] = { | |||
82 | { | |||
83 | .norm = V4L2_STD_525_60(((v4l2_std_id)0x00000100) | ((v4l2_std_id)0x00000800) | (((v4l2_std_id )0x00001000) | ((v4l2_std_id)0x00002000) | ((v4l2_std_id)0x00008000 )) | ((v4l2_std_id)0x00004000)), | |||
84 | .cap_width = 720, | |||
85 | .cap_height = 480, | |||
86 | /* 4 bytes/2 pixel YUYV/YUV 4:2:2 */ | |||
87 | .frame_len = (720 * 480 * 2), | |||
88 | }, | |||
89 | { | |||
90 | .norm = V4L2_STD_PAL((((v4l2_std_id)0x00000001) | ((v4l2_std_id)0x00000002) | ((v4l2_std_id )0x00000004)) | (((v4l2_std_id)0x00000020) | ((v4l2_std_id)0x00000040 ) | ((v4l2_std_id)0x00000080)) | ((v4l2_std_id)0x00000008) | ( (v4l2_std_id)0x00000010)), | |||
91 | .cap_width = 720, | |||
92 | .cap_height = 576, | |||
93 | /* 4 bytes/2 pixel YUYV/YUV 4:2:2 */ | |||
94 | .frame_len = (720 * 576 * 2), | |||
95 | } | |||
96 | }; | |||
97 | ||||
98 | int | |||
99 | utvfu_set_regs(struct utvfu_softc *sc, const uint16_t regs[][2], int size) | |||
100 | { | |||
101 | int i; | |||
102 | usbd_status error; | |||
103 | usb_device_request_t req; | |||
104 | ||||
105 | req.bmRequestType = UT_WRITE_VENDOR_DEVICE(0x00 | 0x40 | 0x00); | |||
106 | req.bRequest = UTVFU_REQUEST_REG12; | |||
107 | USETW(req.wLength, 0)(*(u_int16_t *)(req.wLength) = (0)); | |||
108 | ||||
109 | for (i = 0; i < size; i++) { | |||
110 | USETW(req.wIndex, regs[i][0])(*(u_int16_t *)(req.wIndex) = (regs[i][0])); | |||
111 | USETW(req.wValue, regs[i][1])(*(u_int16_t *)(req.wValue) = (regs[i][1])); | |||
112 | ||||
113 | error = usbd_do_request(sc->sc_udev, &req, NULL((void *)0)); | |||
114 | if (error != USBD_NORMAL_COMPLETION) { | |||
115 | DPRINTF(1, "%s: %s: exit EINVAL\n", | |||
116 | DEVNAME(sc), __func__); | |||
117 | return (EINVAL22); | |||
118 | } | |||
119 | } | |||
120 | ||||
121 | return (0); | |||
122 | } | |||
123 | ||||
124 | int | |||
125 | utvfu_max_frame_size(void) | |||
126 | { | |||
127 | int i, sz = 0; | |||
128 | ||||
129 | for (i = 0; i < nitems(utvfu_norm_params)(sizeof((utvfu_norm_params)) / sizeof((utvfu_norm_params)[0]) ); i++) { | |||
130 | if (sz < utvfu_norm_params[i].frame_len) | |||
131 | sz = utvfu_norm_params[i].frame_len; | |||
132 | } | |||
133 | ||||
134 | return (sz); | |||
135 | } | |||
136 | ||||
137 | int | |||
138 | utvfu_configure_for_norm(struct utvfu_softc *sc, v4l2_std_id norm) | |||
139 | { | |||
140 | int i, ret = EINVAL22; | |||
141 | struct utvfu_norm_params *params = NULL((void *)0); | |||
142 | ||||
143 | for (i = 0; i < nitems(utvfu_norm_params)(sizeof((utvfu_norm_params)) / sizeof((utvfu_norm_params)[0]) ); i++) { | |||
144 | if (utvfu_norm_params[i].norm & norm) { | |||
145 | params = &utvfu_norm_params[i]; | |||
146 | break; | |||
147 | } | |||
148 | } | |||
149 | ||||
150 | if (params != NULL((void *)0)) { | |||
151 | sc->sc_normi = i; | |||
152 | sc->sc_nchunks = params->cap_width * params->cap_height | |||
153 | / 4 / UTVFU_CHUNK240; | |||
154 | ret = 0; | |||
155 | } | |||
156 | ||||
157 | return (ret); | |||
158 | } | |||
159 | ||||
160 | int | |||
161 | utvfu_select_input(struct utvfu_softc *sc, int input) | |||
162 | { | |||
163 | int ret; | |||
164 | ||||
165 | static const uint16_t composite[][2] = { | |||
166 | { UTVFU_BASE0xc000 + 0x0105, 0x0060 }, | |||
167 | { UTVFU_BASE0xc000 + 0x011f, 0x00f2 }, | |||
168 | { UTVFU_BASE0xc000 + 0x0127, 0x0060 }, | |||
169 | { UTVFU_BASE0xc000 + 0x00ae, 0x0010 }, | |||
170 | { UTVFU_BASE0xc000 + 0x0239, 0x0060 }, | |||
171 | }; | |||
172 | ||||
173 | static const uint16_t svideo[][2] = { | |||
174 | { UTVFU_BASE0xc000 + 0x0105, 0x0010 }, | |||
175 | { UTVFU_BASE0xc000 + 0x011f, 0x00ff }, | |||
176 | { UTVFU_BASE0xc000 + 0x0127, 0x0060 }, | |||
177 | { UTVFU_BASE0xc000 + 0x00ae, 0x0030 }, | |||
178 | { UTVFU_BASE0xc000 + 0x0239, 0x0060 }, | |||
179 | }; | |||
180 | ||||
181 | switch (input) { | |||
182 | case UTVFU_COMPOSITE_INPUT0: | |||
183 | ret = utvfu_set_regs(sc, composite, nitems(composite)(sizeof((composite)) / sizeof((composite)[0]))); | |||
184 | break; | |||
185 | case UTVFU_SVIDEO_INPUT1: | |||
186 | ret = utvfu_set_regs(sc, svideo, nitems(svideo)(sizeof((svideo)) / sizeof((svideo)[0]))); | |||
187 | break; | |||
188 | default: | |||
189 | ret = EINVAL22; | |||
190 | } | |||
191 | ||||
192 | if (ret == 0) | |||
193 | sc->sc_input = input; | |||
194 | ||||
195 | return (ret); | |||
196 | } | |||
197 | ||||
198 | int | |||
199 | utvfu_select_norm(struct utvfu_softc *sc, v4l2_std_id norm) | |||
200 | { | |||
201 | int ret; | |||
202 | static const uint16_t pal[][2] = { | |||
203 | { UTVFU_BASE0xc000 + 0x001a, 0x0068 }, | |||
204 | { UTVFU_BASE0xc000 + 0x010e, 0x0072 }, | |||
205 | { UTVFU_BASE0xc000 + 0x010f, 0x00a2 }, | |||
206 | { UTVFU_BASE0xc000 + 0x0112, 0x00b0 }, | |||
207 | { UTVFU_BASE0xc000 + 0x0117, 0x0001 }, | |||
208 | { UTVFU_BASE0xc000 + 0x0118, 0x002c }, | |||
209 | { UTVFU_BASE0xc000 + 0x012d, 0x0010 }, | |||
210 | { UTVFU_BASE0xc000 + 0x012f, 0x0020 }, | |||
211 | { UTVFU_BASE0xc000 + 0x024f, 0x0002 }, | |||
212 | { UTVFU_BASE0xc000 + 0x0254, 0x0059 }, | |||
213 | { UTVFU_BASE0xc000 + 0x025a, 0x0016 }, | |||
214 | { UTVFU_BASE0xc000 + 0x025b, 0x0035 }, | |||
215 | { UTVFU_BASE0xc000 + 0x0263, 0x0017 }, | |||
216 | { UTVFU_BASE0xc000 + 0x0266, 0x0016 }, | |||
217 | { UTVFU_BASE0xc000 + 0x0267, 0x0036 } | |||
218 | }; | |||
219 | ||||
220 | static const uint16_t ntsc[][2] = { | |||
221 | { UTVFU_BASE0xc000 + 0x001a, 0x0079 }, | |||
222 | { UTVFU_BASE0xc000 + 0x010e, 0x0068 }, | |||
223 | { UTVFU_BASE0xc000 + 0x010f, 0x009c }, | |||
224 | { UTVFU_BASE0xc000 + 0x0112, 0x00f0 }, | |||
225 | { UTVFU_BASE0xc000 + 0x0117, 0x0000 }, | |||
226 | { UTVFU_BASE0xc000 + 0x0118, 0x00fc }, | |||
227 | { UTVFU_BASE0xc000 + 0x012d, 0x0004 }, | |||
228 | { UTVFU_BASE0xc000 + 0x012f, 0x0008 }, | |||
229 | { UTVFU_BASE0xc000 + 0x024f, 0x0001 }, | |||
230 | { UTVFU_BASE0xc000 + 0x0254, 0x005f }, | |||
231 | { UTVFU_BASE0xc000 + 0x025a, 0x0012 }, | |||
232 | { UTVFU_BASE0xc000 + 0x025b, 0x0001 }, | |||
233 | { UTVFU_BASE0xc000 + 0x0263, 0x001c }, | |||
234 | { UTVFU_BASE0xc000 + 0x0266, 0x0011 }, | |||
235 | { UTVFU_BASE0xc000 + 0x0267, 0x0005 } | |||
236 | }; | |||
237 | ||||
238 | ret = utvfu_configure_for_norm(sc, norm); | |||
239 | ||||
240 | if (ret == 0) { | |||
241 | if (norm & V4L2_STD_525_60(((v4l2_std_id)0x00000100) | ((v4l2_std_id)0x00000800) | (((v4l2_std_id )0x00001000) | ((v4l2_std_id)0x00002000) | ((v4l2_std_id)0x00008000 )) | ((v4l2_std_id)0x00004000))) | |||
242 | ret = utvfu_set_regs(sc, ntsc, nitems(ntsc)(sizeof((ntsc)) / sizeof((ntsc)[0]))); | |||
243 | else if (norm & V4L2_STD_PAL((((v4l2_std_id)0x00000001) | ((v4l2_std_id)0x00000002) | ((v4l2_std_id )0x00000004)) | (((v4l2_std_id)0x00000020) | ((v4l2_std_id)0x00000040 ) | ((v4l2_std_id)0x00000080)) | ((v4l2_std_id)0x00000008) | ( (v4l2_std_id)0x00000010))) | |||
244 | ret = utvfu_set_regs(sc, pal, nitems(pal)(sizeof((pal)) / sizeof((pal)[0]))); | |||
245 | } | |||
246 | ||||
247 | return (ret); | |||
248 | } | |||
249 | ||||
250 | int | |||
251 | utvfu_setup_capture(struct utvfu_softc *sc) | |||
252 | { | |||
253 | int ret; | |||
254 | static const uint16_t setup[][2] = { | |||
255 | /* These seem to enable the device. */ | |||
256 | { UTVFU_BASE0xc000 + 0x0008, 0x0001 }, | |||
257 | { UTVFU_BASE0xc000 + 0x01d0, 0x00ff }, | |||
258 | { UTVFU_BASE0xc000 + 0x01d9, 0x0002 }, | |||
259 | ||||
260 | /* | |||
261 | * These seem to influence color parameters, such as | |||
262 | * brightness, etc. | |||
263 | */ | |||
264 | { UTVFU_BASE0xc000 + 0x0239, 0x0040 }, | |||
265 | { UTVFU_BASE0xc000 + 0x0240, 0x0000 }, | |||
266 | { UTVFU_BASE0xc000 + 0x0241, 0x0000 }, | |||
267 | { UTVFU_BASE0xc000 + 0x0242, 0x0002 }, | |||
268 | { UTVFU_BASE0xc000 + 0x0243, 0x0080 }, | |||
269 | { UTVFU_BASE0xc000 + 0x0244, 0x0012 }, | |||
270 | { UTVFU_BASE0xc000 + 0x0245, 0x0090 }, | |||
271 | { UTVFU_BASE0xc000 + 0x0246, 0x0000 }, | |||
272 | ||||
273 | { UTVFU_BASE0xc000 + 0x0278, 0x002d }, | |||
274 | { UTVFU_BASE0xc000 + 0x0279, 0x000a }, | |||
275 | { UTVFU_BASE0xc000 + 0x027a, 0x0032 }, | |||
276 | { 0xf890, 0x000c }, | |||
277 | { 0xf894, 0x0086 }, | |||
278 | ||||
279 | { UTVFU_BASE0xc000 + 0x00ac, 0x00c0 }, | |||
280 | { UTVFU_BASE0xc000 + 0x00ad, 0x0000 }, | |||
281 | { UTVFU_BASE0xc000 + 0x00a2, 0x0012 }, | |||
282 | { UTVFU_BASE0xc000 + 0x00a3, 0x00e0 }, | |||
283 | { UTVFU_BASE0xc000 + 0x00a4, 0x0028 }, | |||
284 | { UTVFU_BASE0xc000 + 0x00a5, 0x0082 }, | |||
285 | { UTVFU_BASE0xc000 + 0x00a7, 0x0080 }, | |||
286 | { UTVFU_BASE0xc000 + 0x0000, 0x0014 }, | |||
287 | { UTVFU_BASE0xc000 + 0x0006, 0x0003 }, | |||
288 | { UTVFU_BASE0xc000 + 0x0090, 0x0099 }, | |||
289 | { UTVFU_BASE0xc000 + 0x0091, 0x0090 }, | |||
290 | { UTVFU_BASE0xc000 + 0x0094, 0x0068 }, | |||
291 | { UTVFU_BASE0xc000 + 0x0095, 0x0070 }, | |||
292 | { UTVFU_BASE0xc000 + 0x009c, 0x0030 }, | |||
293 | { UTVFU_BASE0xc000 + 0x009d, 0x00c0 }, | |||
294 | { UTVFU_BASE0xc000 + 0x009e, 0x00e0 }, | |||
295 | { UTVFU_BASE0xc000 + 0x0019, 0x0006 }, | |||
296 | { UTVFU_BASE0xc000 + 0x008c, 0x00ba }, | |||
297 | { UTVFU_BASE0xc000 + 0x0101, 0x00ff }, | |||
298 | { UTVFU_BASE0xc000 + 0x010c, 0x00b3 }, | |||
299 | { UTVFU_BASE0xc000 + 0x01b2, 0x0080 }, | |||
300 | { UTVFU_BASE0xc000 + 0x01b4, 0x00a0 }, | |||
301 | { UTVFU_BASE0xc000 + 0x014c, 0x00ff }, | |||
302 | { UTVFU_BASE0xc000 + 0x014d, 0x00ca }, | |||
303 | { UTVFU_BASE0xc000 + 0x0113, 0x0053 }, | |||
304 | { UTVFU_BASE0xc000 + 0x0119, 0x008a }, | |||
305 | { UTVFU_BASE0xc000 + 0x013c, 0x0003 }, | |||
306 | { UTVFU_BASE0xc000 + 0x0150, 0x009c }, | |||
307 | { UTVFU_BASE0xc000 + 0x0151, 0x0071 }, | |||
308 | { UTVFU_BASE0xc000 + 0x0152, 0x00c6 }, | |||
309 | { UTVFU_BASE0xc000 + 0x0153, 0x0084 }, | |||
310 | { UTVFU_BASE0xc000 + 0x0154, 0x00bc }, | |||
311 | { UTVFU_BASE0xc000 + 0x0155, 0x00a0 }, | |||
312 | { UTVFU_BASE0xc000 + 0x0156, 0x00a0 }, | |||
313 | { UTVFU_BASE0xc000 + 0x0157, 0x009c }, | |||
314 | { UTVFU_BASE0xc000 + 0x0158, 0x001f }, | |||
315 | { UTVFU_BASE0xc000 + 0x0159, 0x0006 }, | |||
316 | { UTVFU_BASE0xc000 + 0x015d, 0x0000 }, | |||
317 | ||||
318 | { UTVFU_BASE0xc000 + 0x0003, 0x0004 }, | |||
319 | { UTVFU_BASE0xc000 + 0x0100, 0x00d3 }, | |||
320 | { UTVFU_BASE0xc000 + 0x0115, 0x0015 }, | |||
321 | { UTVFU_BASE0xc000 + 0x0220, 0x002e }, | |||
322 | { UTVFU_BASE0xc000 + 0x0225, 0x0008 }, | |||
323 | { UTVFU_BASE0xc000 + 0x024e, 0x0002 }, | |||
324 | { UTVFU_BASE0xc000 + 0x024e, 0x0002 }, | |||
325 | { UTVFU_BASE0xc000 + 0x024f, 0x0002 }, | |||
326 | }; | |||
327 | ||||
328 | ret = utvfu_set_regs(sc, setup, nitems(setup)(sizeof((setup)) / sizeof((setup)[0]))); | |||
329 | if (ret) | |||
330 | return (ret); | |||
331 | ||||
332 | ret = utvfu_select_norm(sc, utvfu_norm_params[sc->sc_normi].norm); | |||
333 | if (ret) | |||
334 | return (ret); | |||
335 | ||||
336 | ret = utvfu_select_input(sc, sc->sc_input); | |||
337 | if (ret) | |||
338 | return (ret); | |||
339 | ||||
340 | return (0); | |||
341 | } | |||
342 | ||||
343 | /* | |||
344 | * Copy data from chunk into a frame buffer, deinterlacing the data | |||
345 | * into every second line. Unfortunately, they don't align nicely into | |||
346 | * 720 pixel lines, as the chunk is 240 words long, which is 480 pixels. | |||
347 | * Therefore, we break down the chunk into two halves before copying, | |||
348 | * so that we can interleave a line if needed. | |||
349 | * | |||
350 | * Each "chunk" is 240 words; a word in this context equals 4 bytes. | |||
351 | * Image format is YUYV/YUV 4:2:2, consisting of Y Cr Y Cb, defining two | |||
352 | * pixels, the Cr and Cb shared between the two pixels, but each having | |||
353 | * separate Y values. Thus, the 240 words equal 480 pixels. It therefore, | |||
354 | * takes 1.5 chunks to make a 720 pixel-wide line for the frame. | |||
355 | * The image is interlaced, so there is a "scan" of odd lines, followed | |||
356 | * by "scan" of even numbered lines. | |||
357 | * | |||
358 | * Following code is writing the chunks in correct sequence, skipping | |||
359 | * the rows based on "odd" value. | |||
360 | * line 1: chunk[0][ 0..479] chunk[0][480..959] chunk[1][ 0..479] | |||
361 | * line 3: chunk[1][480..959] chunk[2][ 0..479] chunk[2][480..959] | |||
362 | * ...etc | |||
363 | */ | |||
364 | void | |||
365 | utvfu_chunk_to_vbuf(uint8_t *frame, uint8_t *src, int chunk_no, int odd) | |||
366 | { | |||
367 | uint8_t *dst; | |||
368 | int half, line, part_no, part_index; | |||
369 | #define UTVFU_STRIDE (UTVFU_CHUNK240/2 * 4) | |||
370 | ||||
371 | for (half = 0; half < 2; half++) { | |||
372 | part_no = chunk_no * 2 + half; | |||
373 | line = part_no / 3; | |||
374 | part_index = (line * 2 + !odd) * 3 + (part_no % 3); | |||
375 | ||||
376 | dst = &frame[part_index * UTVFU_STRIDE]; | |||
377 | ||||
378 | memcpy(dst, src, UTVFU_STRIDE)__builtin_memcpy((dst), (src), (UTVFU_STRIDE)); | |||
379 | src += UTVFU_STRIDE; | |||
380 | } | |||
381 | #undef UTVFU_STRIDE | |||
382 | } | |||
383 | ||||
384 | /* | |||
385 | * Called for each 256-byte image chunk. | |||
386 | * First word identifies the chunk, followed by 240 words of image | |||
387 | * data and padding. | |||
388 | */ | |||
389 | void | |||
390 | utvfu_image_chunk(struct utvfu_softc *sc, u_char *chunk) | |||
391 | { | |||
392 | int frame_id, odd, chunk_no, frame_len; | |||
393 | uint32_t hdr; | |||
394 | ||||
395 | memcpy(&hdr, chunk, sizeof(hdr))__builtin_memcpy((&hdr), (chunk), (sizeof(hdr))); | |||
396 | chunk += sizeof(hdr); | |||
397 | hdr = be32toh(hdr)(__uint32_t)(__builtin_constant_p(hdr) ? (__uint32_t)(((__uint32_t )(hdr) & 0xff) << 24 | ((__uint32_t)(hdr) & 0xff00 ) << 8 | ((__uint32_t)(hdr) & 0xff0000) >> 8 | ((__uint32_t)(hdr) & 0xff000000) >> 24) : __swap32md (hdr)); | |||
398 | ||||
399 | /* Ignore corrupted lines. */ | |||
400 | if (!UTVFU_MAGIC_OK(hdr)((hdr & 0xff000000U) == 0x88000000U)) { | |||
401 | DPRINTF(2, "%s: bad magic=0x%08x\n", | |||
402 | DEVNAME(sc), UTVFU_MAGIC(hdr)); | |||
403 | return; | |||
404 | } | |||
405 | ||||
406 | frame_id = UTVFU_FRAME_ID(hdr)((hdr & 0x00ff0000U) >> 16); | |||
407 | odd = UTVFU_ODD(hdr)((hdr & 0x0000f000U) >> 15); | |||
408 | chunk_no = UTVFU_CHUNK_NO(hdr)(hdr & 0x00000fffU); | |||
409 | if (chunk_no >= sc->sc_nchunks) { | |||
410 | DPRINTF(2, "%s: chunk_no=%d >= sc_nchunks=%d\n", | |||
411 | DEVNAME(sc), chunk_no, sc->sc_nchunks); | |||
412 | return; | |||
413 | } | |||
414 | ||||
415 | /* Beginning of a frame. */ | |||
416 | if (chunk_no == 0) { | |||
417 | sc->sc_fb.fid = frame_id; | |||
418 | sc->sc_fb.chunks_done = 0; | |||
419 | } | |||
420 | else if (sc->sc_fb.fid != frame_id) { | |||
421 | DPRINTF(2, "%s: frame id mismatch expecting=%d got=%d\n", | |||
422 | DEVNAME(sc), sc->sc_fb.fid, frame_id); | |||
423 | return; | |||
424 | } | |||
425 | ||||
426 | frame_len = utvfu_norm_params[sc->sc_normi].frame_len; | |||
427 | ||||
428 | /* Copy the chunk data. */ | |||
429 | utvfu_chunk_to_vbuf(sc->sc_fb.buf, chunk, chunk_no, odd); | |||
430 | sc->sc_fb.chunks_done++; | |||
431 | ||||
432 | /* Last chunk in a field */ | |||
433 | if (chunk_no == sc->sc_nchunks-1) { | |||
434 | /* Last chunk in a frame, signalling an end */ | |||
435 | if (odd && !sc->sc_fb.last_odd) { | |||
436 | if (sc->sc_fb.chunks_done != sc->sc_nchunks) { | |||
437 | DPRINTF(1, "%s: chunks_done=%d != nchunks=%d\n", | |||
438 | DEVNAME(sc), | |||
439 | sc->sc_fb.chunks_done, sc->sc_nchunks); | |||
440 | } | |||
441 | ||||
442 | if (sc->sc_flags & UTVFU_FLAG_MMAP0x01) { | |||
443 | utvfu_mmap_queue(sc, sc->sc_fb.buf, frame_len); | |||
444 | } | |||
445 | else { | |||
446 | utvfu_read(sc, sc->sc_fb.buf, frame_len); | |||
447 | } | |||
448 | } | |||
449 | sc->sc_fb.last_odd = odd; | |||
450 | } | |||
451 | } | |||
452 | ||||
453 | int | |||
454 | utvfu_start_capture(struct utvfu_softc *sc) | |||
455 | { | |||
456 | usbd_status error; | |||
457 | int restart_au; | |||
458 | ||||
459 | DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); | |||
460 | ||||
461 | restart_au = ISSET(sc->sc_flags, UTVFU_FLAG_AS_RUNNING)((sc->sc_flags) & (0x02)); | |||
462 | utvfu_audio_stop(sc); | |||
463 | ||||
464 | /* default video stream interface */ | |||
465 | error = usbd_set_interface(sc->sc_uifaceh, UTVFU_DFLT_IFACE_IDX0); | |||
466 | if (error != USBD_NORMAL_COMPLETION) | |||
467 | return (EINVAL22); | |||
468 | ||||
469 | if (utvfu_setup_capture(sc) != 0) | |||
470 | return (EINVAL22); | |||
471 | ||||
472 | /* alt setting */ | |||
473 | error = usbd_set_interface(sc->sc_uifaceh, UTVFU_ALT_IFACE_IDX1); | |||
474 | if (error != USBD_NORMAL_COMPLETION) | |||
475 | return (EINVAL22); | |||
476 | ||||
477 | if (restart_au) | |||
478 | utvfu_audio_start(sc); | |||
479 | ||||
480 | return (0); | |||
481 | } | |||
482 | ||||
483 | int | |||
484 | utvfu_querycap(void *v, struct v4l2_capability *cap) | |||
485 | { | |||
486 | struct utvfu_softc *sc = v; | |||
487 | ||||
488 | memset(cap, 0, sizeof(*cap))__builtin_memset((cap), (0), (sizeof(*cap))); | |||
489 | strlcpy(cap->driver, DEVNAME(sc)((sc)->sc_dev.dv_xname), sizeof(cap->driver)); | |||
490 | strlcpy(cap->card, "utvfu", sizeof(cap->card)); | |||
491 | strlcpy(cap->bus_info, "usb", sizeof(cap->bus_info)); | |||
492 | cap->device_caps = V4L2_CAP_VIDEO_CAPTURE0x00000001; | |||
493 | cap->device_caps |= V4L2_CAP_READWRITE0x01000000 | V4L2_CAP_STREAMING0x04000000; | |||
494 | cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS0x80000000; | |||
495 | ||||
496 | return (0); | |||
497 | } | |||
498 | ||||
499 | int | |||
500 | utvfu_enum_input(void *v, struct v4l2_input *i) | |||
501 | { | |||
502 | struct utvfu_softc *sc = v; | |||
503 | ||||
504 | switch (i->index) { | |||
505 | case UTVFU_COMPOSITE_INPUT0: | |||
506 | strlcpy(i->name, "Composite", sizeof(i->name)); | |||
507 | break; | |||
508 | case UTVFU_SVIDEO_INPUT1: | |||
509 | strlcpy(i->name, "S-Video", sizeof(i->name)); | |||
510 | break; | |||
511 | default: | |||
512 | return (EINVAL22); | |||
513 | } | |||
514 | ||||
515 | i->type = V4L2_INPUT_TYPE_CAMERA2; | |||
516 | i->std = utvfu_norm_params[sc->sc_normi].norm; | |||
517 | ||||
518 | return (0); | |||
519 | } | |||
520 | ||||
521 | int | |||
522 | utvfu_enum_fmt_vid_cap(void *v, struct v4l2_fmtdesc *f) | |||
523 | { | |||
524 | if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || f->index != 0) | |||
525 | return (EINVAL22); | |||
526 | ||||
527 | strlcpy(f->description, "16 bpp YUY2, 4:2:2, packed", | |||
528 | sizeof(f->description)); | |||
529 | f->pixelformat = V4L2_PIX_FMT_YUYV((u_int32_t)('Y') | ((u_int32_t)('U') << 8) | ((u_int32_t )('Y') << 16) | ((u_int32_t)('V') << 24)); | |||
530 | ||||
531 | return (0); | |||
532 | } | |||
533 | ||||
534 | int | |||
535 | utvfu_enum_fsizes(void *v, struct v4l2_frmsizeenum *fsizes) | |||
536 | { | |||
537 | struct utvfu_softc *sc = v; | |||
538 | ||||
539 | if (fsizes->pixel_format != V4L2_PIX_FMT_YUYV((u_int32_t)('Y') | ((u_int32_t)('U') << 8) | ((u_int32_t )('Y') << 16) | ((u_int32_t)('V') << 24))) | |||
540 | return (EINVAL22); | |||
541 | ||||
542 | /* The device only supports one frame size. */ | |||
543 | if (fsizes->index >= 1) | |||
544 | return (EINVAL22); | |||
545 | ||||
546 | fsizes->type = V4L2_FRMSIZE_TYPE_DISCRETE; | |||
547 | fsizes->discrete.width = utvfu_norm_params[sc->sc_normi].cap_width; | |||
548 | fsizes->discrete.height = utvfu_norm_params[sc->sc_normi].cap_height; | |||
549 | ||||
550 | return (0); | |||
551 | } | |||
552 | ||||
553 | int | |||
554 | utvfu_g_fmt(void *v, struct v4l2_format *f) | |||
555 | { | |||
556 | struct utvfu_softc *sc = v; | |||
557 | ||||
558 | f->fmt.pix.width = utvfu_norm_params[sc->sc_normi].cap_width; | |||
559 | f->fmt.pix.height = utvfu_norm_params[sc->sc_normi].cap_height; | |||
560 | f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV((u_int32_t)('Y') | ((u_int32_t)('U') << 8) | ((u_int32_t )('Y') << 16) | ((u_int32_t)('V') << 24)); | |||
561 | f->fmt.pix.field = V4L2_FIELD_INTERLACED; | |||
562 | f->fmt.pix.bytesperline = f->fmt.pix.width * 2; | |||
563 | f->fmt.pix.sizeimage = (f->fmt.pix.bytesperline * f->fmt.pix.height); | |||
564 | f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; | |||
565 | ||||
566 | return (0); | |||
567 | } | |||
568 | ||||
569 | int | |||
570 | utvfu_s_fmt(void *v, struct v4l2_format *f) | |||
571 | { | |||
572 | if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV((u_int32_t)('Y') | ((u_int32_t)('U') << 8) | ((u_int32_t )('Y') << 16) | ((u_int32_t)('V') << 24))) | |||
573 | return (EINVAL22); | |||
574 | ||||
575 | return (0); | |||
576 | } | |||
577 | ||||
578 | int | |||
579 | utvfu_g_std(void *v, v4l2_std_id *norm) | |||
580 | { | |||
581 | struct utvfu_softc *sc = v; | |||
582 | ||||
583 | *norm = utvfu_norm_params[sc->sc_normi].norm; | |||
584 | ||||
585 | return (0); | |||
586 | } | |||
587 | ||||
588 | int | |||
589 | utvfu_s_std(void *v, v4l2_std_id norm) | |||
590 | { | |||
591 | int ret = EINVAL22; | |||
592 | struct utvfu_softc *sc = v; | |||
593 | ||||
594 | if ((norm & V4L2_STD_525_60(((v4l2_std_id)0x00000100) | ((v4l2_std_id)0x00000800) | (((v4l2_std_id )0x00001000) | ((v4l2_std_id)0x00002000) | ((v4l2_std_id)0x00008000 )) | ((v4l2_std_id)0x00004000))) || (norm & V4L2_STD_PAL((((v4l2_std_id)0x00000001) | ((v4l2_std_id)0x00000002) | ((v4l2_std_id )0x00000004)) | (((v4l2_std_id)0x00000020) | ((v4l2_std_id)0x00000040 ) | ((v4l2_std_id)0x00000080)) | ((v4l2_std_id)0x00000008) | ( (v4l2_std_id)0x00000010)))) | |||
595 | ret = utvfu_select_norm(sc, norm); | |||
596 | ||||
597 | return (ret); | |||
598 | } | |||
599 | ||||
600 | int | |||
601 | utvfu_g_input(void *v, int *i) | |||
602 | { | |||
603 | struct utvfu_softc *sc = v; | |||
604 | ||||
605 | *i = sc->sc_input; | |||
606 | ||||
607 | return (0); | |||
608 | } | |||
609 | ||||
610 | int | |||
611 | utvfu_s_input(void *v, int i) | |||
612 | { | |||
613 | return utvfu_select_input(v, i); | |||
614 | } | |||
615 | ||||
616 | /* A U D I O */ | |||
617 | ||||
618 | void | |||
619 | utvfu_audio_decode(struct utvfu_softc *sc, int len) | |||
620 | { | |||
621 | uint8_t *dst, *src; | |||
622 | int n, chunk, ncopied; | |||
623 | ||||
624 | if (sc->sc_audio.blksize == 0) | |||
625 | return; | |||
626 | ||||
627 | src = KERNADDR(&sc->sc_audio.iface.xfer->dmabuf, 0)((void *)((char *)((&sc->sc_audio.iface.xfer->dmabuf )->block->kaddr + (&sc->sc_audio.iface.xfer-> dmabuf)->offs) + (0))); | |||
628 | dst = sc->sc_audio.cur; | |||
629 | ncopied = sc->sc_audio.cur - sc->sc_audio.start; | |||
630 | /* b/c region start->end is a multiple blksize chunks */ | |||
631 | ncopied %= sc->sc_audio.blksize; | |||
632 | ||||
633 | while (len >= UTVFU_CHUNK_SIZE256) { | |||
634 | /* | |||
635 | * The header, skipped here, ranges from 0xdd000000 to | |||
636 | * 0xdd0003ff. The 0xdd seems to be the "magic" and | |||
637 | * 0x3ff masks the chunk number. | |||
638 | */ | |||
639 | src += UTVFU_AUDIO_HDRSIZE4; | |||
640 | chunk = UTVFU_CHUNK240; | |||
641 | while (chunk > 0) { | |||
642 | n = min(chunk, sc->sc_audio.blksize - ncopied); | |||
643 | memcpy(dst, src, n)__builtin_memcpy((dst), (src), (n)); | |||
644 | dst += n; | |||
645 | src += n; | |||
646 | chunk -= n; | |||
647 | ncopied += n; | |||
648 | if (ncopied >= sc->sc_audio.blksize) { | |||
649 | mtx_enter(&audio_lock); | |||
650 | (*sc->sc_audio.intr)(sc->sc_audio.intr_arg); | |||
651 | mtx_leave(&audio_lock); | |||
652 | ncopied -= sc->sc_audio.blksize; | |||
653 | } | |||
654 | if (dst > sc->sc_audio.end) | |||
655 | dst = sc->sc_audio.start; | |||
656 | } | |||
657 | len -= UTVFU_CHUNK_SIZE256; /* _CHUNK + _AUDIO_HDRSIZE */ | |||
658 | } | |||
659 | sc->sc_audio.cur = dst; | |||
660 | } | |||
661 | ||||
662 | int | |||
663 | utvfu_audio_start_chip(struct utvfu_softc *sc) | |||
664 | { | |||
665 | static const uint16_t setup[][2] = { | |||
666 | /* These seem to enable the device. */ | |||
667 | { UTVFU_BASE0xc000 + 0x0008, 0x0001 }, | |||
668 | { UTVFU_BASE0xc000 + 0x01d0, 0x00ff }, | |||
669 | { UTVFU_BASE0xc000 + 0x01d9, 0x0002 }, | |||
670 | ||||
671 | { UTVFU_BASE0xc000 + 0x01da, 0x0013 }, | |||
672 | { UTVFU_BASE0xc000 + 0x01db, 0x0012 }, | |||
673 | { UTVFU_BASE0xc000 + 0x01e9, 0x0002 }, | |||
674 | { UTVFU_BASE0xc000 + 0x01ec, 0x006c }, | |||
675 | { UTVFU_BASE0xc000 + 0x0294, 0x0020 }, | |||
676 | { UTVFU_BASE0xc000 + 0x0255, 0x00cf }, | |||
677 | { UTVFU_BASE0xc000 + 0x0256, 0x0020 }, | |||
678 | { UTVFU_BASE0xc000 + 0x01eb, 0x0030 }, | |||
679 | { UTVFU_BASE0xc000 + 0x027d, 0x00a6 }, | |||
680 | { UTVFU_BASE0xc000 + 0x0280, 0x0011 }, | |||
681 | { UTVFU_BASE0xc000 + 0x0281, 0x0040 }, | |||
682 | { UTVFU_BASE0xc000 + 0x0282, 0x0011 }, | |||
683 | { UTVFU_BASE0xc000 + 0x0283, 0x0040 }, | |||
684 | { 0xf891, 0x0010 }, | |||
685 | ||||
686 | /* this sets the input from composite */ | |||
687 | { UTVFU_BASE0xc000 + 0x0284, 0x00aa }, | |||
688 | }; | |||
689 | ||||
690 | /* starting the stream */ | |||
691 | utvfu_set_regs(sc, setup, nitems(setup)(sizeof((setup)) / sizeof((setup)[0]))); | |||
692 | ||||
693 | return (0); | |||
694 | } | |||
695 | ||||
696 | int | |||
697 | utvfu_audio_stop_chip(struct utvfu_softc *sc) | |||
698 | { | |||
699 | static const uint16_t setup[][2] = { | |||
700 | /* | |||
701 | * The original windows driver sometimes sends also: | |||
702 | * { UTVFU_BASE + 0x00a2, 0x0013 } | |||
703 | * but it seems useless and its real effects are untested at | |||
704 | * the moment. | |||
705 | */ | |||
706 | { UTVFU_BASE0xc000 + 0x027d, 0x0000 }, | |||
707 | { UTVFU_BASE0xc000 + 0x0280, 0x0010 }, | |||
708 | { UTVFU_BASE0xc000 + 0x0282, 0x0010 }, | |||
709 | }; | |||
710 | ||||
711 | utvfu_set_regs(sc, setup, nitems(setup)(sizeof((setup)) / sizeof((setup)[0]))); | |||
712 | ||||
713 | return (0); | |||
714 | } | |||
715 | ||||
716 | /* | |||
717 | * Copyright (c) 2008 Robert Nagy <robert@openbsd.org> | |||
718 | * Copyright (c) 2008 Marcus Glocker <mglocker@openbsd.org> | |||
719 | * Copyright (c) 2016 Patrick Keshishian <patrick@boxsoft.com> | |||
720 | * | |||
721 | * Permission to use, copy, modify, and distribute this software for any | |||
722 | * purpose with or without fee is hereby granted, provided that the above | |||
723 | * copyright notice and this permission notice appear in all copies. | |||
724 | * | |||
725 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
726 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
727 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
728 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
729 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
730 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
731 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
732 | */ | |||
733 | /* | |||
734 | * Heavily based on uvideo.c source. | |||
735 | */ | |||
736 | ||||
737 | int utvfu_match(struct device *, void *, void *); | |||
738 | void utvfu_attach(struct device *, struct device *, void *); | |||
739 | int utvfu_detach(struct device *, int); | |||
740 | ||||
741 | usbd_status utvfu_parse_desc(struct utvfu_softc *); | |||
742 | ||||
743 | void utvfu_vs_close(struct utvfu_softc *); | |||
744 | void utvfu_vs_free_frame(struct utvfu_softc *); | |||
745 | void utvfu_vs_free_isoc(struct utvfu_softc *); | |||
746 | void utvfu_vs_start_isoc_ixfer(struct utvfu_softc *, | |||
747 | struct utvfu_isoc_xfer *); | |||
748 | void utvfu_vs_cb(struct usbd_xfer *, void *, usbd_status); | |||
749 | ||||
750 | void utvfu_vs_free(struct utvfu_softc *); | |||
751 | int utvfu_vs_init(struct utvfu_softc *); | |||
752 | int utvfu_vs_alloc_frame(struct utvfu_softc *); | |||
753 | usbd_status utvfu_vs_alloc_isoc(struct utvfu_softc *); | |||
754 | ||||
755 | int utvfu_open(void *, int, int *, uint8_t *, | |||
756 | void (*)(void *), void *); | |||
757 | int utvfu_close(void *); | |||
758 | int utvfu_querycap(void *, struct v4l2_capability *); | |||
759 | int utvfu_enum_fmt_vid_cap(void *, struct v4l2_fmtdesc *); | |||
760 | int utvfu_enum_fsizes(void *, struct v4l2_frmsizeenum *); | |||
761 | int utvfu_g_fmt(void *, struct v4l2_format *); | |||
762 | int utvfu_s_fmt(void *, struct v4l2_format *); | |||
763 | int utvfu_g_parm(void *, struct v4l2_streamparm *); | |||
764 | int utvfu_s_parm(void *, struct v4l2_streamparm *); | |||
765 | int utvfu_enum_input(void *, struct v4l2_input *); | |||
766 | int utvfu_s_input(void *, int); | |||
767 | int utvfu_g_input(void *, int *); | |||
768 | ||||
769 | int utvfu_reqbufs(void *, struct v4l2_requestbuffers *); | |||
770 | int utvfu_querybuf(void *, struct v4l2_buffer *); | |||
771 | int utvfu_qbuf(void *, struct v4l2_buffer *); | |||
772 | int utvfu_dqbuf(void *, struct v4l2_buffer *); | |||
773 | int utvfu_streamon(void *, int); | |||
774 | int utvfu_streamoff(void *, int); | |||
775 | int utvfu_queryctrl(void *, struct v4l2_queryctrl *); | |||
776 | caddr_t utvfu_mappage(void *, off_t, int); | |||
777 | int utvfu_get_bufsize(void *); | |||
778 | int utvfu_start_read(void *); | |||
779 | ||||
780 | int utvfu_as_init(struct utvfu_softc *); | |||
781 | void utvfu_as_free(struct utvfu_softc *); | |||
782 | ||||
783 | usbd_status utvfu_as_open(struct utvfu_softc *); | |||
784 | int utvfu_as_alloc_bulk(struct utvfu_softc *); | |||
785 | void utvfu_as_free_bulk(struct utvfu_softc *); | |||
786 | int utvfu_as_start_bulk(struct utvfu_softc *); | |||
787 | void utvfu_as_bulk_thread(void *); | |||
788 | ||||
789 | int utvfu_audio_open(void *, int); | |||
790 | void utvfu_audio_close(void *); | |||
791 | int utvfu_audio_set_params(void *, int, int, | |||
792 | struct audio_params *, struct audio_params *); | |||
793 | int utvfu_audio_halt_out(void *); | |||
794 | int utvfu_audio_halt_in(void *); | |||
795 | int utvfu_audio_mixer_set_port(void *, struct mixer_ctrl *); | |||
796 | int utvfu_audio_mixer_get_port(void *, struct mixer_ctrl *); | |||
797 | int utvfu_audio_query_devinfo(void *, struct mixer_devinfo *); | |||
798 | int utvfu_audio_trigger_output(void *, void *, void *, int, | |||
799 | void (*)(void *), void *, struct audio_params *); | |||
800 | int utvfu_audio_trigger_input(void *, void *, void *, int, | |||
801 | void (*)(void *), void *, struct audio_params *); | |||
802 | ||||
803 | struct cfdriver utvfu_cd = { | |||
804 | NULL((void *)0), "utvfu", DV_DULL | |||
805 | }; | |||
806 | ||||
807 | const struct cfattach utvfu_ca = { | |||
808 | sizeof(struct utvfu_softc), | |||
809 | utvfu_match, | |||
810 | utvfu_attach, | |||
811 | utvfu_detach, | |||
812 | NULL((void *)0) | |||
813 | }; | |||
814 | ||||
815 | const struct video_hw_if utvfu_vid_hw_if = { | |||
816 | utvfu_open, /* open */ | |||
817 | utvfu_close, /* close */ | |||
818 | utvfu_querycap, /* VIDIOC_QUERYCAP */ | |||
819 | utvfu_enum_fmt_vid_cap, /* VIDIOC_ENUM_FMT */ | |||
820 | utvfu_enum_fsizes, /* VIDIOC_ENUM_FRAMESIZES */ | |||
821 | NULL((void *)0), /* VIDIOC_ENUM_FRAMEINTERVALS */ | |||
822 | utvfu_s_fmt, /* VIDIOC_S_FMT */ | |||
823 | utvfu_g_fmt, /* VIDIOC_G_FMT */ | |||
824 | utvfu_s_parm, /* VIDIOC_S_PARM */ | |||
825 | utvfu_g_parm, /* VIDIOC_G_PARM */ | |||
826 | utvfu_enum_input, /* VIDIOC_ENUMINPUT */ | |||
827 | utvfu_s_input, /* VIDIOC_S_INPUT */ | |||
828 | utvfu_g_input, /* VIDIOC_G_INPUT */ | |||
829 | utvfu_reqbufs, /* VIDIOC_REQBUFS */ | |||
830 | utvfu_querybuf, /* VIDIOC_QUERYBUF */ | |||
831 | utvfu_qbuf, /* VIDIOC_QBUF */ | |||
832 | utvfu_dqbuf, /* VIDIOC_DQBUF */ | |||
833 | utvfu_streamon, /* VIDIOC_STREAMON */ | |||
834 | utvfu_streamoff, /* VIDIOC_STREAMOFF */ | |||
835 | NULL((void *)0), /* VIDIOC_TRY_FMT */ | |||
836 | utvfu_queryctrl, /* VIDIOC_QUERYCTRL */ | |||
837 | NULL((void *)0), /* VIDIOC_G_CTRL */ | |||
838 | NULL((void *)0), /* VIDIOC_S_CTRL */ | |||
839 | utvfu_mappage, /* mmap */ | |||
840 | utvfu_get_bufsize, /* read */ | |||
841 | utvfu_start_read /* start stream for read */ | |||
842 | }; | |||
843 | ||||
844 | const struct audio_hw_if utvfu_au_hw_if = { | |||
845 | .open = utvfu_audio_open, | |||
846 | .close = utvfu_audio_close, | |||
847 | .set_params = utvfu_audio_set_params, | |||
848 | .halt_output = utvfu_audio_halt_out, | |||
849 | .halt_input = utvfu_audio_halt_in, | |||
850 | .set_port = utvfu_audio_mixer_set_port, | |||
851 | .get_port = utvfu_audio_mixer_get_port, | |||
852 | .query_devinfo = utvfu_audio_query_devinfo, | |||
853 | .trigger_output = utvfu_audio_trigger_output, | |||
854 | .trigger_input = utvfu_audio_trigger_input, | |||
855 | }; | |||
856 | ||||
857 | int | |||
858 | utvfu_match(struct device *parent, void *match, void *aux) | |||
859 | { | |||
860 | struct usb_attach_arg *uaa = aux; | |||
861 | const struct usb_descriptor *ud; | |||
862 | struct usbd_desc_iter iter; | |||
863 | struct usb_interface_descriptor *uid = NULL((void *)0); | |||
| ||||
864 | const struct usb_endpoint_descriptor *ued = NULL((void *)0); | |||
865 | usb_device_descriptor_t *dd; | |||
866 | int ret = UMATCH_NONE0; | |||
867 | int nep, nalt; | |||
868 | uint16_t psize = 0; | |||
869 | ||||
870 | if (uaa->iface == NULL((void *)0)) | |||
871 | return ret; | |||
872 | ||||
873 | dd = usbd_get_device_descriptor(uaa->device); | |||
874 | ||||
875 | if (UGETW(dd->idVendor)(*(u_int16_t *)(dd->idVendor)) == USB_VENDOR_FUSHICAI0x1b71 && | |||
876 | UGETW(dd->idProduct)(*(u_int16_t *)(dd->idProduct)) == USB_PRODUCT_FUSHICAI_USBTV0070x3002) | |||
877 | ret = UMATCH_VENDOR_PRODUCT13; | |||
878 | /* | |||
879 | * This seems like a fragile check, but the original driver ensures | |||
880 | * there are two alternate settings for the interface, and alternate | |||
881 | * setting 1 has four endpoints. | |||
882 | * | |||
883 | * Comment says "Checks that the device is what we think it is." | |||
884 | * | |||
885 | * Adding check that wMaxPacketSize for the video endpoint is > 0. | |||
886 | */ | |||
887 | nep = nalt = 0; | |||
888 | usbd_desc_iter_init(uaa->device, &iter); | |||
889 | while ((ud = usbd_desc_iter_next(&iter)) != NULL((void *)0)) { | |||
890 | switch (ud->bDescriptorType) { | |||
891 | default: | |||
892 | break; | |||
893 | case UDESC_INTERFACE0x04: | |||
894 | uid = (void *)ud; | |||
895 | if (uid->bInterfaceNumber == 0) | |||
896 | nalt++; | |||
897 | break; | |||
898 | case UDESC_ENDPOINT0x05: | |||
899 | if (uid->bAlternateSetting == 1) { | |||
| ||||
900 | ued = (void *)ud; | |||
901 | if (ued->bEndpointAddress == UTVFU_VIDEO_ENDP0x81) | |||
902 | psize = UGETW(ued->wMaxPacketSize)(*(u_int16_t *)(ued->wMaxPacketSize)); | |||
903 | nep++; | |||
904 | } | |||
905 | break; | |||
906 | } | |||
907 | if (uid != NULL((void *)0) && uid->bInterfaceNumber > 0) | |||
908 | break; | |||
909 | } | |||
910 | ||||
911 | if (nalt != 2 || nep != 4 || psize == 0) | |||
912 | ret = UMATCH_NONE0; | |||
913 | ||||
914 | return (ret); | |||
915 | } | |||
916 | ||||
917 | void | |||
918 | utvfu_attach(struct device *parent, struct device *self, void *aux) | |||
919 | { | |||
920 | int i; | |||
921 | struct utvfu_softc *sc = (struct utvfu_softc *)self; | |||
922 | struct usb_attach_arg *uaa = aux; | |||
923 | ||||
924 | sc->sc_udev = uaa->device; | |||
925 | for (i = 0; i < uaa->nifaces; i++) { | |||
926 | if (usbd_iface_claimed(sc->sc_udev, i)) | |||
927 | continue; | |||
928 | usbd_claim_iface(sc->sc_udev, i); | |||
929 | } | |||
930 | ||||
931 | utvfu_parse_desc(sc); | |||
932 | ||||
933 | /* init mmap queue */ | |||
934 | SIMPLEQ_INIT(&sc->sc_mmap_q)do { (&sc->sc_mmap_q)->sqh_first = ((void *)0); (& sc->sc_mmap_q)->sqh_last = &(&sc->sc_mmap_q) ->sqh_first; } while (0); | |||
935 | sc->sc_mmap_count = 0; | |||
936 | ||||
937 | sc->sc_max_frame_sz = utvfu_max_frame_size(); | |||
938 | ||||
939 | /* calculate optimal isoc xfer size */ | |||
940 | sc->sc_nframes = (sc->sc_max_frame_sz + sc->sc_iface.psize - 1) | |||
941 | / sc->sc_iface.psize; | |||
942 | if (sc->sc_nframes > UTVFU_NFRAMES_MAX40) | |||
943 | sc->sc_nframes = UTVFU_NFRAMES_MAX40; | |||
944 | DPRINTF(1, "%s: nframes=%d\n", DEVNAME(sc), sc->sc_nframes); | |||
945 | ||||
946 | rw_init(&sc->sc_audio.rwlock, "audiorwl")_rw_init_flags(&sc->sc_audio.rwlock, "audiorwl", 0, (( void *)0)); | |||
947 | ||||
948 | sc->sc_audiodev = audio_attach_mi(&utvfu_au_hw_if, sc, NULL((void *)0), &sc->sc_dev); | |||
949 | sc->sc_videodev = video_attach_mi(&utvfu_vid_hw_if, sc, &sc->sc_dev); | |||
950 | } | |||
951 | ||||
952 | int | |||
953 | utvfu_detach(struct device *self, int flags) | |||
954 | { | |||
955 | struct utvfu_softc *sc = (struct utvfu_softc *)self; | |||
956 | int rv = 0; | |||
957 | ||||
958 | /* Wait for outstanding requests to complete */ | |||
959 | usbd_delay_ms(sc->sc_udev, UTVFU_NFRAMES_MAX40); /* XXX meh? */ | |||
960 | ||||
961 | if (sc->sc_videodev != NULL((void *)0)) | |||
962 | rv = config_detach(sc->sc_videodev, flags); | |||
963 | ||||
964 | if (sc->sc_audiodev != NULL((void *)0)) | |||
965 | rv += config_detach(sc->sc_audiodev, flags); | |||
966 | ||||
967 | utvfu_as_free(sc); | |||
968 | utvfu_vs_free(sc); | |||
969 | ||||
970 | sc->sc_flags = 0; | |||
971 | ||||
972 | return (rv); | |||
973 | } | |||
974 | ||||
975 | usbd_status | |||
976 | utvfu_parse_desc(struct utvfu_softc *sc) | |||
977 | { | |||
978 | int nif, nep; | |||
979 | uint32_t psize; | |||
980 | struct usbd_desc_iter iter; | |||
981 | const struct usb_descriptor *ud; | |||
982 | struct usb_endpoint_descriptor *ued; | |||
983 | struct usb_interface_descriptor *uid = NULL((void *)0); | |||
984 | ||||
985 | nif = nep = 0; | |||
986 | usbd_desc_iter_init(sc->sc_udev, &iter); | |||
987 | while ((ud = usbd_desc_iter_next(&iter)) != NULL((void *)0)) { | |||
988 | if (ud->bDescriptorType != UDESC_INTERFACE0x04) | |||
989 | continue; | |||
990 | /* looking for interface 0, alt-setting 1 */ | |||
991 | uid = (void *)ud; | |||
992 | if (uid->bInterfaceNumber > 0) | |||
993 | break; | |||
994 | if (uid->bAlternateSetting == 1) | |||
995 | break; | |||
996 | } | |||
997 | ||||
998 | /* this should not fail as it was ensured during match */ | |||
999 | if (uid == NULL((void *)0) || uid->bInterfaceNumber != 0 || | |||
1000 | uid->bAlternateSetting != 1) { | |||
1001 | printf("%s: no valid alternate interface found!\n", | |||
1002 | DEVNAME(sc)((sc)->sc_dev.dv_xname)); | |||
1003 | return (USBD_INVAL); | |||
1004 | } | |||
1005 | ||||
1006 | /* bInterfaceNumber = 0 */ | |||
1007 | sc->sc_uifaceh = &sc->sc_udev->ifaces[0]; | |||
1008 | ||||
1009 | /* looking for video endpoint to on alternate setting 1 */ | |||
1010 | while ((ud = usbd_desc_iter_next(&iter)) != NULL((void *)0)) { | |||
1011 | if (ud->bDescriptorType != UDESC_ENDPOINT0x05) | |||
1012 | break; | |||
1013 | ||||
1014 | ued = (void *)ud; | |||
1015 | if (ued->bEndpointAddress != UTVFU_VIDEO_ENDP0x81) | |||
1016 | continue; | |||
1017 | ||||
1018 | psize = UGETW(ued->wMaxPacketSize)(*(u_int16_t *)(ued->wMaxPacketSize)); | |||
1019 | psize = UE_GET_SIZE(psize)((psize) & 0x7ff) * (1 + UE_GET_TRANS(psize)(((psize) >> 11) & 0x3)); | |||
1020 | sc->sc_iface.psize = psize; | |||
1021 | break; | |||
1022 | } | |||
1023 | ||||
1024 | return (USBD_NORMAL_COMPLETION); | |||
1025 | } | |||
1026 | ||||
1027 | int | |||
1028 | utvfu_open(void *addr, int flags, int *size, uint8_t *buffer, | |||
1029 | void (*intr)(void *), void *arg) | |||
1030 | { | |||
1031 | struct utvfu_softc *sc = addr; | |||
1032 | int rv; | |||
1033 | ||||
1034 | DPRINTF(1, "%s: utvfu_open: sc=%p\n", DEVNAME(sc), sc); | |||
1035 | ||||
1036 | if (usbd_is_dying(sc->sc_udev)) | |||
1037 | return (EIO5); | |||
1038 | ||||
1039 | if ((rv = utvfu_vs_init(sc)) != 0) | |||
1040 | return (rv); | |||
1041 | ||||
1042 | /* pointers to upper video layer */ | |||
1043 | sc->sc_uplayer_arg = arg; | |||
1044 | sc->sc_uplayer_fsize = size; | |||
1045 | sc->sc_uplayer_fbuffer = buffer; | |||
1046 | sc->sc_uplayer_intr = intr; | |||
1047 | ||||
1048 | sc->sc_flags &= ~UTVFU_FLAG_MMAP0x01; | |||
1049 | ||||
1050 | return (0); | |||
1051 | } | |||
1052 | ||||
1053 | int | |||
1054 | utvfu_close(void *addr) | |||
1055 | { | |||
1056 | struct utvfu_softc *sc = addr; | |||
1057 | ||||
1058 | DPRINTF(1, "%s: utvfu_close: sc=%p\n", DEVNAME(sc), sc); | |||
1059 | ||||
1060 | /* free & clean up video stream */ | |||
1061 | utvfu_vs_free(sc); | |||
1062 | ||||
1063 | return (0); | |||
1064 | } | |||
1065 | ||||
1066 | usbd_status | |||
1067 | utvfu_as_open(struct utvfu_softc *sc) | |||
1068 | { | |||
1069 | usb_endpoint_descriptor_t *ed; | |||
1070 | usbd_status error; | |||
1071 | ||||
1072 | DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); | |||
1073 | ||||
1074 | if (sc->sc_audio.iface.pipeh != NULL((void *)0)) { | |||
1075 | printf("%s: %s called while sc_audio.iface.pipeh not NULL\n", | |||
1076 | DEVNAME(sc)((sc)->sc_dev.dv_xname), __func__); | |||
1077 | return (USBD_INVAL); | |||
1078 | } | |||
1079 | ||||
1080 | ed = usbd_get_endpoint_descriptor(sc->sc_uifaceh, UTVFU_AUDIO_ENDP0x83); | |||
1081 | if (ed == NULL((void *)0)) { | |||
1082 | printf("%s: no endpoint descriptor for AS iface\n", | |||
1083 | DEVNAME(sc)((sc)->sc_dev.dv_xname)); | |||
1084 | return (USBD_INVAL); | |||
1085 | } | |||
1086 | DPRINTF(1, "%s: open pipe for ", DEVNAME(sc)); | |||
1087 | DPRINTF(1, "bEndpointAddress=0x%02x (0x%02x), wMaxPacketSize=" | |||
1088 | "0x%04x (%d)\n", | |||
1089 | UE_GET_ADDR(ed->bEndpointAddress), | |||
1090 | UTVFU_AUDIO_ENDP, | |||
1091 | UGETW(ed->wMaxPacketSize), | |||
1092 | UE_GET_SIZE(UGETW(ed->wMaxPacketSize)) | |||
1093 | * (1 + UE_GET_TRANS(UGETW(ed->wMaxPacketSize)))); | |||
1094 | ||||
1095 | error = usbd_open_pipe( | |||
1096 | sc->sc_uifaceh, | |||
1097 | UTVFU_AUDIO_ENDP0x83, | |||
1098 | USBD_EXCLUSIVE_USE0x01, | |||
1099 | &sc->sc_audio.iface.pipeh); | |||
1100 | if (error != USBD_NORMAL_COMPLETION) { | |||
1101 | printf("%s: could not open AS pipe: %s\n", | |||
1102 | DEVNAME(sc)((sc)->sc_dev.dv_xname), usbd_errstr(error)); | |||
1103 | } | |||
1104 | ||||
1105 | return (error); | |||
1106 | } | |||
1107 | ||||
1108 | usbd_status | |||
1109 | utvfu_vs_open(struct utvfu_softc *sc) | |||
1110 | { | |||
1111 | usb_endpoint_descriptor_t *ed; | |||
1112 | usbd_status error; | |||
1113 | ||||
1114 | DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); | |||
1115 | ||||
1116 | if (sc->sc_iface.pipeh != NULL((void *)0)) { | |||
1117 | printf("%s: %s called while sc_iface.pipeh not NULL\n", | |||
1118 | DEVNAME(sc)((sc)->sc_dev.dv_xname), __func__); | |||
1119 | return (USBD_INVAL); | |||
1120 | } | |||
1121 | ||||
1122 | ed = usbd_get_endpoint_descriptor(sc->sc_uifaceh, UTVFU_VIDEO_ENDP0x81); | |||
1123 | if (ed == NULL((void *)0)) { | |||
1124 | printf("%s: no endpoint descriptor for VS iface\n", | |||
1125 | DEVNAME(sc)((sc)->sc_dev.dv_xname)); | |||
1126 | return (USBD_INVAL); | |||
1127 | } | |||
1128 | DPRINTF(1, "%s: open pipe for ", DEVNAME(sc)); | |||
1129 | DPRINTF(1, "bEndpointAddress=0x%02x (0x%02x), wMaxPacketSize=" | |||
1130 | "0x%04x (%d)\n", | |||
1131 | UE_GET_ADDR(ed->bEndpointAddress), | |||
1132 | UTVFU_VIDEO_ENDP, | |||
1133 | UGETW(ed->wMaxPacketSize), | |||
1134 | sc->sc_iface.psize); | |||
1135 | ||||
1136 | error = usbd_open_pipe( | |||
1137 | sc->sc_uifaceh, | |||
1138 | UTVFU_VIDEO_ENDP0x81, | |||
1139 | USBD_EXCLUSIVE_USE0x01, | |||
1140 | &sc->sc_iface.pipeh); | |||
1141 | if (error != USBD_NORMAL_COMPLETION) { | |||
1142 | printf("%s: could not open VS pipe: %s\n", | |||
1143 | DEVNAME(sc)((sc)->sc_dev.dv_xname), usbd_errstr(error)); | |||
1144 | } | |||
1145 | ||||
1146 | return (error); | |||
1147 | } | |||
1148 | ||||
1149 | void | |||
1150 | utvfu_as_close(struct utvfu_softc *sc) | |||
1151 | { | |||
1152 | DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); | |||
1153 | ||||
1154 | CLR(sc->sc_flags, UTVFU_FLAG_AS_RUNNING)((sc->sc_flags) &= ~(0x02)); | |||
1155 | ||||
1156 | if (sc->sc_audio.iface.pipeh != NULL((void *)0)) { | |||
1157 | usbd_abort_pipe(sc->sc_audio.iface.pipeh); | |||
1158 | ||||
1159 | usbd_ref_wait(sc->sc_udev); | |||
1160 | ||||
1161 | usbd_close_pipe(sc->sc_audio.iface.pipeh); | |||
1162 | sc->sc_audio.iface.pipeh = NULL((void *)0); | |||
1163 | } | |||
1164 | } | |||
1165 | ||||
1166 | void | |||
1167 | utvfu_vs_close(struct utvfu_softc *sc) | |||
1168 | { | |||
1169 | if (sc->sc_iface.pipeh != NULL((void *)0)) { | |||
1170 | usbd_close_pipe(sc->sc_iface.pipeh); | |||
1171 | sc->sc_iface.pipeh = NULL((void *)0); | |||
1172 | } | |||
1173 | ||||
1174 | /* | |||
1175 | * Some devices need time to shutdown before we switch back to | |||
1176 | * the default interface (0). Not doing so can leave the device | |||
1177 | * back in a undefined condition. | |||
1178 | */ | |||
1179 | usbd_delay_ms(sc->sc_udev, 100); | |||
1180 | ||||
1181 | /* switch back to default interface (turns off cam LED) */ | |||
1182 | (void)usbd_set_interface(sc->sc_uifaceh, UTVFU_DFLT_IFACE_IDX0); | |||
1183 | } | |||
1184 | ||||
1185 | void | |||
1186 | utvfu_read(struct utvfu_softc *sc, uint8_t *buf, int len) | |||
1187 | { | |||
1188 | /* | |||
1189 | * Copy video frame to upper layer buffer and call | |||
1190 | * upper layer interrupt. | |||
1191 | */ | |||
1192 | *sc->sc_uplayer_fsize = len; | |||
1193 | memcpy(sc->sc_uplayer_fbuffer, buf, len)__builtin_memcpy((sc->sc_uplayer_fbuffer), (buf), (len)); | |||
1194 | (*sc->sc_uplayer_intr)(sc->sc_uplayer_arg); | |||
1195 | } | |||
1196 | ||||
1197 | int | |||
1198 | utvfu_as_start_bulk(struct utvfu_softc *sc) | |||
1199 | { | |||
1200 | int error; | |||
1201 | ||||
1202 | if (ISSET(sc->sc_flags, UTVFU_FLAG_AS_RUNNING)((sc->sc_flags) & (0x02))) | |||
1203 | return (0); | |||
1204 | if (sc->sc_audio.iface.pipeh == NULL((void *)0)) | |||
1205 | return (ENXIO6); | |||
1206 | ||||
1207 | SET(sc->sc_flags, UTVFU_FLAG_AS_RUNNING)((sc->sc_flags) |= (0x02)); | |||
1208 | error = kthread_create(utvfu_as_bulk_thread, sc, NULL((void *)0), DEVNAME(sc)((sc)->sc_dev.dv_xname)); | |||
1209 | if (error) { | |||
1210 | CLR(sc->sc_flags, UTVFU_FLAG_AS_RUNNING)((sc->sc_flags) &= ~(0x02)); | |||
1211 | printf("%s: can't create kernel thread!", DEVNAME(sc)((sc)->sc_dev.dv_xname)); | |||
1212 | } | |||
1213 | ||||
1214 | return (error); | |||
1215 | } | |||
1216 | ||||
1217 | void | |||
1218 | utvfu_as_bulk_thread(void *arg) | |||
1219 | { | |||
1220 | struct utvfu_softc *sc = arg; | |||
1221 | struct utvfu_as_iface *iface; | |||
1222 | usbd_status error; | |||
1223 | uint32_t actlen; | |||
1224 | ||||
1225 | DPRINTF(1, "%s %s\n", DEVNAME(sc), __func__); | |||
1226 | ||||
1227 | iface = &sc->sc_audio.iface; | |||
1228 | usbd_ref_incr(sc->sc_udev); | |||
1229 | while (ISSET(sc->sc_flags, UTVFU_FLAG_AS_RUNNING)((sc->sc_flags) & (0x02))) { | |||
1230 | usbd_setup_xfer( | |||
1231 | iface->xfer, | |||
1232 | iface->pipeh, | |||
1233 | 0, | |||
1234 | NULL((void *)0), | |||
1235 | UTVFU_AUDIO_URBSIZE20480, | |||
1236 | USBD_NO_COPY0x01 | USBD_SHORT_XFER_OK0x04 | USBD_SYNCHRONOUS0x02, | |||
1237 | 0, | |||
1238 | NULL((void *)0)); | |||
1239 | error = usbd_transfer(iface->xfer); | |||
1240 | ||||
1241 | if (error != USBD_NORMAL_COMPLETION) { | |||
1242 | DPRINTF(1, "%s: error in bulk xfer: %s!\n", | |||
1243 | DEVNAME(sc), usbd_errstr(error)); | |||
1244 | break; | |||
1245 | } | |||
1246 | ||||
1247 | usbd_get_xfer_status(iface->xfer, NULL((void *)0), NULL((void *)0), &actlen, | |||
1248 | NULL((void *)0)); | |||
1249 | DPRINTF(2, "%s: *** buffer len = %d\n", DEVNAME(sc), actlen); | |||
1250 | ||||
1251 | rw_enter_read(&sc->sc_audio.rwlock); | |||
1252 | utvfu_audio_decode(sc, actlen); | |||
1253 | rw_exit_read(&sc->sc_audio.rwlock); | |||
1254 | } | |||
1255 | ||||
1256 | CLR(sc->sc_flags, UTVFU_FLAG_AS_RUNNING)((sc->sc_flags) &= ~(0x02)); | |||
1257 | usbd_ref_decr(sc->sc_udev); | |||
1258 | ||||
1259 | DPRINTF(1, "%s %s: exiting\n", DEVNAME(sc), __func__); | |||
1260 | ||||
1261 | kthread_exit(0); | |||
1262 | } | |||
1263 | ||||
1264 | void | |||
1265 | utvfu_vs_start_isoc(struct utvfu_softc *sc) | |||
1266 | { | |||
1267 | int i; | |||
1268 | ||||
1269 | for (i = 0; i < UTVFU_ISOC_TRANSFERS3; i++) | |||
1270 | utvfu_vs_start_isoc_ixfer(sc, &sc->sc_iface.ixfer[i]); | |||
1271 | } | |||
1272 | ||||
1273 | void | |||
1274 | utvfu_vs_start_isoc_ixfer(struct utvfu_softc *sc, | |||
1275 | struct utvfu_isoc_xfer *ixfer) | |||
1276 | { | |||
1277 | int i; | |||
1278 | usbd_status error; | |||
1279 | ||||
1280 | DPRINTF(2, "%s: %s\n", DEVNAME(sc), __func__); | |||
1281 | ||||
1282 | if (usbd_is_dying(sc->sc_udev)) | |||
1283 | return; | |||
1284 | ||||
1285 | for (i = 0; i < sc->sc_nframes; i++) | |||
1286 | ixfer->size[i] = sc->sc_iface.psize; | |||
1287 | ||||
1288 | usbd_setup_isoc_xfer( | |||
1289 | ixfer->xfer, | |||
1290 | sc->sc_iface.pipeh, | |||
1291 | ixfer, | |||
1292 | ixfer->size, | |||
1293 | sc->sc_nframes, | |||
1294 | USBD_NO_COPY0x01 | USBD_SHORT_XFER_OK0x04, | |||
1295 | utvfu_vs_cb); | |||
1296 | ||||
1297 | error = usbd_transfer(ixfer->xfer); | |||
1298 | if (error && error != USBD_IN_PROGRESS) { | |||
1299 | DPRINTF(1, "%s: usbd_transfer error=%s!\n", | |||
1300 | DEVNAME(sc), usbd_errstr(error)); | |||
1301 | } | |||
1302 | } | |||
1303 | ||||
1304 | /* | |||
1305 | * Each packet contains a number of 256-byte chunks composing the image frame. | |||
1306 | */ | |||
1307 | void | |||
1308 | utvfu_vs_cb(struct usbd_xfer *xfer, void *priv, usbd_status status) | |||
1309 | { | |||
1310 | struct utvfu_isoc_xfer *ixfer = priv; | |||
1311 | struct utvfu_softc *sc = ixfer->sc; | |||
1312 | int i, off, frame_size; | |||
1313 | uint32_t actlen; | |||
1314 | uint8_t *frame; | |||
1315 | ||||
1316 | DPRINTF(2, "%s: %s\n", DEVNAME(sc), __func__); | |||
1317 | ||||
1318 | if (status != USBD_NORMAL_COMPLETION) { | |||
1319 | DPRINTF(1, "%s: %s: %s\n", DEVNAME(sc), __func__, | |||
1320 | usbd_errstr(status)); | |||
1321 | return; | |||
1322 | } | |||
1323 | usbd_get_xfer_status(xfer, NULL((void *)0), NULL((void *)0), &actlen, NULL((void *)0)); | |||
1324 | ||||
1325 | DPRINTF(2, "%s: *** buffer len = %d\n", DEVNAME(sc), actlen); | |||
1326 | if (actlen == 0) | |||
1327 | goto skip; | |||
1328 | ||||
1329 | frame = KERNADDR(&xfer->dmabuf, 0)((void *)((char *)((&xfer->dmabuf)->block->kaddr + (&xfer->dmabuf)->offs) + (0))); | |||
1330 | for (i = 0; i < sc->sc_nframes; i++, frame += sc->sc_iface.psize) { | |||
1331 | frame_size = ixfer->size[i]; | |||
1332 | ||||
1333 | if (frame_size == 0) | |||
1334 | /* frame is empty */ | |||
1335 | continue; | |||
1336 | ||||
1337 | #define CHUNK_STRIDE (UTVFU_CHUNK_SIZE256*4) | |||
1338 | for (off = 0; off + CHUNK_STRIDE <= frame_size; | |||
1339 | off += CHUNK_STRIDE) { | |||
1340 | utvfu_image_chunk(sc, frame + off); | |||
1341 | } | |||
1342 | #undef CHUNK_STRIDE | |||
1343 | } | |||
1344 | ||||
1345 | skip: /* setup new transfer */ | |||
1346 | utvfu_vs_start_isoc_ixfer(sc, ixfer); | |||
1347 | } | |||
1348 | ||||
1349 | int | |||
1350 | utvfu_find_queued(struct utvfu_softc *sc) | |||
1351 | { | |||
1352 | int i; | |||
1353 | ||||
1354 | /* find a buffer which is ready for queueing */ | |||
1355 | for (i = 0; i < sc->sc_mmap_count; i++) { | |||
1356 | if (sc->sc_mmap[i].v4l2_buf.flags & V4L2_BUF_FLAG_DONE0x00000004) | |||
1357 | continue; | |||
1358 | if (sc->sc_mmap[i].v4l2_buf.flags & V4L2_BUF_FLAG_QUEUED0x00000002) | |||
1359 | return (i); | |||
1360 | } | |||
1361 | ||||
1362 | return (-1); | |||
1363 | } | |||
1364 | ||||
1365 | int | |||
1366 | utvfu_mmap_queue(struct utvfu_softc *sc, uint8_t *buf, int len) | |||
1367 | { | |||
1368 | int i; | |||
1369 | ||||
1370 | if (sc->sc_mmap_count == 0 || sc->sc_mmap_buffer == NULL((void *)0)) | |||
1371 | panic("%s: mmap buffers not allocated", __func__); | |||
1372 | ||||
1373 | /* find a buffer which is ready for queueing */ | |||
1374 | if ((i = utvfu_find_queued(sc)) == -1) { | |||
1375 | DPRINTF(2, "%s: mmap queue is full!\n", DEVNAME(sc)); | |||
1376 | return (ENOMEM12); | |||
1377 | } | |||
1378 | ||||
1379 | /* copy frame to mmap buffer and report length */ | |||
1380 | memcpy(sc->sc_mmap[i].buf, buf, len)__builtin_memcpy((sc->sc_mmap[i].buf), (buf), (len)); | |||
1381 | sc->sc_mmap[i].v4l2_buf.bytesused = len; | |||
1382 | ||||
1383 | /* timestamp it */ | |||
1384 | getmicrotime(&sc->sc_mmap[i].v4l2_buf.timestamp); | |||
1385 | ||||
1386 | /* appropriately set/clear flags */ | |||
1387 | sc->sc_mmap[i].v4l2_buf.flags &= ~V4L2_BUF_FLAG_QUEUED0x00000002; | |||
1388 | sc->sc_mmap[i].v4l2_buf.flags |= V4L2_BUF_FLAG_DONE0x00000004; | |||
1389 | ||||
1390 | /* queue it */ | |||
1391 | SIMPLEQ_INSERT_TAIL(&sc->sc_mmap_q, &sc->sc_mmap[i], q_frames)do { (&sc->sc_mmap[i])->q_frames.sqe_next = ((void * )0); *(&sc->sc_mmap_q)->sqh_last = (&sc->sc_mmap [i]); (&sc->sc_mmap_q)->sqh_last = &(&sc-> sc_mmap[i])->q_frames.sqe_next; } while (0); | |||
1392 | DPRINTF(2, "%s: %s: frame queued on index %d\n", | |||
1393 | DEVNAME(sc), __func__, i); | |||
1394 | ||||
1395 | wakeup(sc); | |||
1396 | ||||
1397 | /* | |||
1398 | * In case userland uses poll(2), signal that we have a frame | |||
1399 | * ready to dequeue. | |||
1400 | */ | |||
1401 | (*sc->sc_uplayer_intr)(sc->sc_uplayer_arg); | |||
1402 | ||||
1403 | return (0); | |||
1404 | } | |||
1405 | ||||
1406 | caddr_t | |||
1407 | utvfu_mappage(void *v, off_t off, int prot) | |||
1408 | { | |||
1409 | struct utvfu_softc *sc = v; | |||
1410 | caddr_t p = NULL((void *)0); | |||
1411 | ||||
1412 | if (off < sc->sc_mmap_bufsz) { | |||
1413 | if ((sc->sc_flags & UTVFU_FLAG_MMAP0x01) == 0) | |||
1414 | sc->sc_flags |= UTVFU_FLAG_MMAP0x01; | |||
1415 | ||||
1416 | p = sc->sc_mmap_buffer + off; | |||
1417 | } | |||
1418 | ||||
1419 | return (p); | |||
1420 | } | |||
1421 | ||||
1422 | int | |||
1423 | utvfu_get_bufsize(void *v) | |||
1424 | { | |||
1425 | struct utvfu_softc *sc = v; | |||
1426 | ||||
1427 | /* YUYV/YUV-422: 4 bytes/2 pixel */ | |||
1428 | return (utvfu_norm_params[sc->sc_normi].cap_width * | |||
1429 | utvfu_norm_params[sc->sc_normi].cap_height * 2); | |||
1430 | } | |||
1431 | ||||
1432 | int | |||
1433 | utvfu_start_read(void *v) | |||
1434 | { | |||
1435 | struct utvfu_softc *sc = v; | |||
1436 | usbd_status error; | |||
1437 | ||||
1438 | if (sc->sc_flags & UTVFU_FLAG_MMAP0x01) | |||
1439 | sc->sc_flags &= ~UTVFU_FLAG_MMAP0x01; | |||
1440 | ||||
1441 | /* open video stream pipe */ | |||
1442 | error = utvfu_vs_open(sc); | |||
1443 | if (error != USBD_NORMAL_COMPLETION) | |||
1444 | return (EINVAL22); | |||
1445 | ||||
1446 | utvfu_vs_start_isoc(sc); | |||
1447 | ||||
1448 | return (0); | |||
1449 | } | |||
1450 | ||||
1451 | void | |||
1452 | utvfu_audio_clear_client(struct utvfu_softc *sc) | |||
1453 | { | |||
1454 | rw_enter_write(&sc->sc_audio.rwlock); | |||
1455 | ||||
1456 | sc->sc_audio.intr = NULL((void *)0); | |||
1457 | sc->sc_audio.intr_arg = NULL((void *)0); | |||
1458 | sc->sc_audio.start = NULL((void *)0); | |||
1459 | sc->sc_audio.end = NULL((void *)0); | |||
1460 | sc->sc_audio.cur = NULL((void *)0); | |||
1461 | sc->sc_audio.blksize = 0; | |||
1462 | ||||
1463 | rw_exit_write(&sc->sc_audio.rwlock); | |||
1464 | } | |||
1465 | ||||
1466 | void | |||
1467 | utvfu_as_free(struct utvfu_softc *sc) | |||
1468 | { | |||
1469 | DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); | |||
1470 | ||||
1471 | utvfu_as_close(sc); | |||
1472 | utvfu_as_free_bulk(sc); | |||
1473 | } | |||
1474 | ||||
1475 | void | |||
1476 | utvfu_vs_free(struct utvfu_softc *sc) | |||
1477 | { | |||
1478 | DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); | |||
1479 | utvfu_vs_close(sc); | |||
1480 | utvfu_vs_free_isoc(sc); | |||
1481 | utvfu_vs_free_frame(sc); | |||
1482 | } | |||
1483 | ||||
1484 | int | |||
1485 | utvfu_as_init(struct utvfu_softc *sc) | |||
1486 | { | |||
1487 | DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); | |||
1488 | ||||
1489 | if (sc->sc_audio.iface.xfer != NULL((void *)0)) | |||
1490 | return (0); | |||
1491 | ||||
1492 | /* allocate audio and video stream xfer buffer */ | |||
1493 | return utvfu_as_alloc_bulk(sc); | |||
1494 | } | |||
1495 | ||||
1496 | int | |||
1497 | utvfu_vs_init(struct utvfu_softc *sc) | |||
1498 | { | |||
1499 | DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); | |||
1500 | ||||
1501 | if (utvfu_start_capture(sc) != 0) | |||
1502 | return (EINVAL22); | |||
1503 | ||||
1504 | if (utvfu_vs_alloc_isoc(sc) != 0 || utvfu_vs_alloc_frame(sc) != 0) | |||
1505 | return (ENOMEM12); | |||
1506 | ||||
1507 | return (0); | |||
1508 | } | |||
1509 | ||||
1510 | int | |||
1511 | utvfu_vs_alloc_frame(struct utvfu_softc *sc) | |||
1512 | { | |||
1513 | struct utvfu_frame_buf *fb = &sc->sc_fb; | |||
1514 | ||||
1515 | fb->size = sc->sc_max_frame_sz; | |||
1516 | fb->buf = malloc(fb->size, M_USBDEV102, M_NOWAIT0x0002); | |||
1517 | if (fb->buf == NULL((void *)0)) { | |||
1518 | printf("%s: can't allocate frame buffer!\n", DEVNAME(sc)((sc)->sc_dev.dv_xname)); | |||
1519 | return (ENOMEM12); | |||
1520 | } | |||
1521 | ||||
1522 | DPRINTF(1, "%s: %s: allocated %d bytes frame buffer\n", | |||
1523 | DEVNAME(sc), __func__, fb->size); | |||
1524 | ||||
1525 | fb->chunks_done = 0; | |||
1526 | fb->fid = 0; | |||
1527 | fb->last_odd = 1; | |||
1528 | ||||
1529 | return (0); | |||
1530 | } | |||
1531 | ||||
1532 | void | |||
1533 | utvfu_vs_free_frame(struct utvfu_softc *sc) | |||
1534 | { | |||
1535 | struct utvfu_frame_buf *fb = &sc->sc_fb; | |||
1536 | ||||
1537 | if (fb->buf != NULL((void *)0)) { | |||
1538 | free(fb->buf, M_USBDEV102, fb->size); | |||
1539 | fb->buf = NULL((void *)0); | |||
1540 | } | |||
1541 | ||||
1542 | if (sc->sc_mmap_buffer != NULL((void *)0)) { | |||
1543 | free(sc->sc_mmap_buffer, M_USBDEV102, sc->sc_mmap_bufsz); | |||
1544 | sc->sc_mmap_buffer = NULL((void *)0); | |||
1545 | memset(sc->sc_mmap, 0, sizeof(sc->sc_mmap))__builtin_memset((sc->sc_mmap), (0), (sizeof(sc->sc_mmap ))); | |||
1546 | } | |||
1547 | ||||
1548 | while (!SIMPLEQ_EMPTY(&sc->sc_mmap_q)(((&sc->sc_mmap_q)->sqh_first) == ((void *)0))) | |||
1549 | SIMPLEQ_REMOVE_HEAD(&sc->sc_mmap_q, q_frames)do { if (((&sc->sc_mmap_q)->sqh_first = (&sc-> sc_mmap_q)->sqh_first->q_frames.sqe_next) == ((void *)0 )) (&sc->sc_mmap_q)->sqh_last = &(&sc->sc_mmap_q )->sqh_first; } while (0); | |||
1550 | ||||
1551 | sc->sc_mmap_count = 0; | |||
1552 | } | |||
1553 | ||||
1554 | usbd_status | |||
1555 | utvfu_vs_alloc_isoc(struct utvfu_softc *sc) | |||
1556 | { | |||
1557 | int size, i; | |||
1558 | void *buf; | |||
1559 | ||||
1560 | DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); | |||
1561 | ||||
1562 | for (i = 0; i < UTVFU_ISOC_TRANSFERS3; i++) { | |||
1563 | sc->sc_iface.ixfer[i].sc = sc; | |||
1564 | sc->sc_iface.ixfer[i].xfer = usbd_alloc_xfer(sc->sc_udev); | |||
1565 | if (sc->sc_iface.ixfer[i].xfer == NULL((void *)0)) { | |||
1566 | printf("%s: could not allocate isoc VS xfer!\n", | |||
1567 | DEVNAME(sc)((sc)->sc_dev.dv_xname)); | |||
1568 | return (USBD_NOMEM); | |||
1569 | } | |||
1570 | ||||
1571 | size = sc->sc_iface.psize * sc->sc_nframes; | |||
1572 | ||||
1573 | buf = usbd_alloc_buffer(sc->sc_iface.ixfer[i].xfer, size); | |||
1574 | if (buf == NULL((void *)0)) { | |||
1575 | printf("%s: could not allocate isoc VS buffer!\n", | |||
1576 | DEVNAME(sc)((sc)->sc_dev.dv_xname)); | |||
1577 | return (USBD_NOMEM); | |||
1578 | } | |||
1579 | DPRINTF(1, "%s: allocated %d bytes isoc VS xfer buffer\n", | |||
1580 | DEVNAME(sc), size); | |||
1581 | } | |||
1582 | ||||
1583 | return (USBD_NORMAL_COMPLETION); | |||
1584 | } | |||
1585 | ||||
1586 | int | |||
1587 | utvfu_as_alloc_bulk(struct utvfu_softc *sc) | |||
1588 | { | |||
1589 | struct usbd_xfer *xfer; | |||
1590 | ||||
1591 | xfer = usbd_alloc_xfer(sc->sc_udev); | |||
1592 | if (xfer == NULL((void *)0)) { | |||
1593 | printf("%s: could not allocate bulk AUDIO xfer!\n", | |||
1594 | DEVNAME(sc)((sc)->sc_dev.dv_xname)); | |||
1595 | return (ENOMEM12); | |||
1596 | } | |||
1597 | ||||
1598 | if (usbd_alloc_buffer(xfer, UTVFU_AUDIO_URBSIZE20480) == NULL((void *)0)) { | |||
1599 | usbd_free_xfer(xfer); | |||
1600 | printf("%s: could not allocate bulk AUDIO buffer!\n", | |||
1601 | DEVNAME(sc)((sc)->sc_dev.dv_xname)); | |||
1602 | return (ENOMEM12); | |||
1603 | } | |||
1604 | DPRINTF(1, "%s: allocated %d bytes bulk AUDIO xfer buffer\n", | |||
1605 | DEVNAME(sc), UTVFU_AUDIO_URBSIZE); | |||
1606 | ||||
1607 | sc->sc_audio.iface.xfer = xfer; | |||
1608 | ||||
1609 | return (0); | |||
1610 | } | |||
1611 | ||||
1612 | void | |||
1613 | utvfu_vs_free_isoc(struct utvfu_softc *sc) | |||
1614 | { | |||
1615 | int i; | |||
1616 | ||||
1617 | DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); | |||
1618 | ||||
1619 | for (i = 0; i < UTVFU_ISOC_TRANSFERS3; i++) { | |||
1620 | if (sc->sc_iface.ixfer[i].xfer != NULL((void *)0)) { | |||
1621 | usbd_free_xfer(sc->sc_iface.ixfer[i].xfer); | |||
1622 | sc->sc_iface.ixfer[i].xfer = NULL((void *)0); | |||
1623 | } | |||
1624 | } | |||
1625 | } | |||
1626 | ||||
1627 | void | |||
1628 | utvfu_as_free_bulk(struct utvfu_softc *sc) | |||
1629 | { | |||
1630 | DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); | |||
1631 | ||||
1632 | if (sc->sc_audio.iface.xfer != NULL((void *)0)) { | |||
1633 | usbd_free_xfer(sc->sc_audio.iface.xfer); | |||
1634 | sc->sc_audio.iface.xfer = NULL((void *)0); | |||
1635 | } | |||
1636 | } | |||
1637 | ||||
1638 | int | |||
1639 | utvfu_reqbufs(void *v, struct v4l2_requestbuffers *rb) | |||
1640 | { | |||
1641 | struct utvfu_softc *sc = v; | |||
1642 | int i; | |||
1643 | ||||
1644 | DPRINTF(1, "%s: %s: count=%d\n", DEVNAME(sc), __func__, rb->count); | |||
1645 | ||||
1646 | /* We do not support freeing buffers via reqbufs(0) */ | |||
1647 | if (rb->count == 0) | |||
1648 | return (EINVAL22); | |||
1649 | ||||
1650 | if (sc->sc_mmap_count > 0 || sc->sc_mmap_buffer != NULL((void *)0)) { | |||
1651 | DPRINTF(1, "%s: %s: mmap buffers already allocated\n", | |||
1652 | DEVNAME(sc), __func__); | |||
1653 | return (EINVAL22); | |||
1654 | } | |||
1655 | ||||
1656 | /* limit the buffers */ | |||
1657 | if (rb->count > UTVFU_MAX_BUFFERS32) | |||
1658 | sc->sc_mmap_count = UTVFU_MAX_BUFFERS32; | |||
1659 | else | |||
1660 | sc->sc_mmap_count = rb->count; | |||
1661 | ||||
1662 | /* allocate the total mmap buffer */ | |||
1663 | sc->sc_mmap_bufsz = sc->sc_max_frame_sz; | |||
1664 | if (INT_MAX0x7fffffff / sc->sc_mmap_count < sc->sc_max_frame_sz) /* overflow */ | |||
1665 | return (ENOMEM12); | |||
1666 | sc->sc_mmap_bufsz *= sc->sc_mmap_count; | |||
1667 | sc->sc_mmap_bufsz = round_page(sc->sc_mmap_bufsz)(((sc->sc_mmap_bufsz) + ((1 << 12) - 1)) & ~((1 << 12) - 1)); /* page align */ | |||
1668 | sc->sc_mmap_buffer = malloc(sc->sc_mmap_bufsz, M_USBDEV102, M_NOWAIT0x0002); | |||
1669 | if (sc->sc_mmap_buffer == NULL((void *)0)) { | |||
1670 | printf("%s: can't allocate mmap buffer!\n", DEVNAME(sc)((sc)->sc_dev.dv_xname)); | |||
1671 | return (ENOMEM12); | |||
1672 | } | |||
1673 | DPRINTF(1, "%s: allocated %d bytes mmap buffer\n", | |||
1674 | DEVNAME(sc), sc->sc_mmap_bufsz); | |||
1675 | ||||
1676 | /* fill the v4l2_buffer structure */ | |||
1677 | for (i = 0; i < sc->sc_mmap_count; i++) { | |||
1678 | sc->sc_mmap[i].buf = sc->sc_mmap_buffer | |||
1679 | + (i * sc->sc_max_frame_sz); | |||
1680 | sc->sc_mmap[i].v4l2_buf.index = i; | |||
1681 | sc->sc_mmap[i].v4l2_buf.m.offset = i * sc->sc_max_frame_sz; | |||
1682 | sc->sc_mmap[i].v4l2_buf.length = sc->sc_max_frame_sz; | |||
1683 | sc->sc_mmap[i].v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | |||
1684 | sc->sc_mmap[i].v4l2_buf.sequence = 0; | |||
1685 | sc->sc_mmap[i].v4l2_buf.field = V4L2_FIELD_NONE; | |||
1686 | sc->sc_mmap[i].v4l2_buf.memory = V4L2_MEMORY_MMAP; | |||
1687 | sc->sc_mmap[i].v4l2_buf.flags = V4L2_BUF_FLAG_MAPPED0x00000001; | |||
1688 | ||||
1689 | DPRINTF(1, "%s: %s: index=%d, offset=%d, length=%d\n", | |||
1690 | DEVNAME(sc), __func__, | |||
1691 | sc->sc_mmap[i].v4l2_buf.index, | |||
1692 | sc->sc_mmap[i].v4l2_buf.m.offset, | |||
1693 | sc->sc_mmap[i].v4l2_buf.length); | |||
1694 | } | |||
1695 | ||||
1696 | /* tell how many buffers we have really allocated */ | |||
1697 | rb->count = sc->sc_mmap_count; | |||
1698 | ||||
1699 | return (0); | |||
1700 | } | |||
1701 | ||||
1702 | int | |||
1703 | utvfu_querybuf(void *v, struct v4l2_buffer *qb) | |||
1704 | { | |||
1705 | struct utvfu_softc *sc = v; | |||
1706 | ||||
1707 | if (qb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || | |||
1708 | qb->memory != V4L2_MEMORY_MMAP || | |||
1709 | qb->index >= sc->sc_mmap_count) | |||
1710 | return (EINVAL22); | |||
1711 | ||||
1712 | memcpy(qb, &sc->sc_mmap[qb->index].v4l2_buf,__builtin_memcpy((qb), (&sc->sc_mmap[qb->index].v4l2_buf ), (sizeof(struct v4l2_buffer))) | |||
1713 | sizeof(struct v4l2_buffer))__builtin_memcpy((qb), (&sc->sc_mmap[qb->index].v4l2_buf ), (sizeof(struct v4l2_buffer))); | |||
1714 | ||||
1715 | DPRINTF(1, "%s: %s: index=%d, offset=%d, length=%d\n", | |||
1716 | DEVNAME(sc), __func__, qb->index, qb->m.offset, qb->length); | |||
1717 | ||||
1718 | return (0); | |||
1719 | } | |||
1720 | ||||
1721 | int | |||
1722 | utvfu_qbuf(void *v, struct v4l2_buffer *qb) | |||
1723 | { | |||
1724 | struct utvfu_softc *sc = v; | |||
1725 | ||||
1726 | if (qb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || | |||
1727 | qb->memory != V4L2_MEMORY_MMAP || | |||
1728 | qb->index >= sc->sc_mmap_count) | |||
1729 | return (EINVAL22); | |||
1730 | ||||
1731 | sc->sc_mmap[qb->index].v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE0x00000004; | |||
1732 | sc->sc_mmap[qb->index].v4l2_buf.flags |= V4L2_BUF_FLAG_MAPPED0x00000001; | |||
1733 | sc->sc_mmap[qb->index].v4l2_buf.flags |= V4L2_BUF_FLAG_QUEUED0x00000002; | |||
1734 | ||||
1735 | DPRINTF(2, "%s: %s: buffer on index %d ready for queueing\n", | |||
1736 | DEVNAME(sc), __func__, qb->index); | |||
1737 | ||||
1738 | return (0); | |||
1739 | } | |||
1740 | ||||
1741 | int | |||
1742 | utvfu_dqbuf(void *v, struct v4l2_buffer *dqb) | |||
1743 | { | |||
1744 | struct utvfu_softc *sc = v; | |||
1745 | struct utvfu_mmap *mmap; | |||
1746 | int error; | |||
1747 | ||||
1748 | if (dqb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || | |||
1749 | dqb->memory != V4L2_MEMORY_MMAP) | |||
1750 | return (EINVAL22); | |||
1751 | ||||
1752 | if (SIMPLEQ_EMPTY(&sc->sc_mmap_q)(((&sc->sc_mmap_q)->sqh_first) == ((void *)0))) { | |||
1753 | /* mmap queue is empty, block until first frame is queued */ | |||
1754 | error = tsleep_nsec(sc, 0, "vid_mmap", SEC_TO_NSEC(10)); | |||
1755 | if (error) | |||
1756 | return (EINVAL22); | |||
1757 | } | |||
1758 | ||||
1759 | mmap = SIMPLEQ_FIRST(&sc->sc_mmap_q)((&sc->sc_mmap_q)->sqh_first); | |||
1760 | if (mmap == NULL((void *)0)) | |||
1761 | panic("utvfu_dqbuf: NULL pointer!"); | |||
1762 | ||||
1763 | memcpy(dqb, &mmap->v4l2_buf, sizeof(struct v4l2_buffer))__builtin_memcpy((dqb), (&mmap->v4l2_buf), (sizeof(struct v4l2_buffer))); | |||
1764 | ||||
1765 | mmap->v4l2_buf.flags &= ~(V4L2_BUF_FLAG_DONE0x00000004|V4L2_BUF_FLAG_QUEUED0x00000002); | |||
1766 | mmap->v4l2_buf.flags |= V4L2_BUF_FLAG_MAPPED0x00000001; | |||
1767 | ||||
1768 | DPRINTF(2, "%s: %s: frame dequeued from index %d\n", | |||
1769 | DEVNAME(sc), __func__, mmap->v4l2_buf.index); | |||
1770 | SIMPLEQ_REMOVE_HEAD(&sc->sc_mmap_q, q_frames)do { if (((&sc->sc_mmap_q)->sqh_first = (&sc-> sc_mmap_q)->sqh_first->q_frames.sqe_next) == ((void *)0 )) (&sc->sc_mmap_q)->sqh_last = &(&sc->sc_mmap_q )->sqh_first; } while (0); | |||
1771 | ||||
1772 | return (0); | |||
1773 | } | |||
1774 | ||||
1775 | int | |||
1776 | utvfu_streamon(void *v, int type) | |||
1777 | { | |||
1778 | struct utvfu_softc *sc = v; | |||
1779 | usbd_status error; | |||
1780 | ||||
1781 | /* open video stream pipe */ | |||
1782 | error = utvfu_vs_open(sc); | |||
1783 | if (error != USBD_NORMAL_COMPLETION) | |||
1784 | return (EINVAL22); | |||
1785 | ||||
1786 | utvfu_vs_start_isoc(sc); | |||
1787 | ||||
1788 | return (0); | |||
1789 | } | |||
1790 | ||||
1791 | int | |||
1792 | utvfu_streamoff(void *v, int type) | |||
1793 | { | |||
1794 | utvfu_vs_close(v); | |||
1795 | ||||
1796 | return (0); | |||
1797 | } | |||
1798 | ||||
1799 | int | |||
1800 | utvfu_queryctrl(void *v, struct v4l2_queryctrl *qctrl) | |||
1801 | { | |||
1802 | qctrl->flags = V4L2_CTRL_FLAG_DISABLED0x0001; | |||
1803 | ||||
1804 | return (0); | |||
1805 | } | |||
1806 | ||||
1807 | int | |||
1808 | utvfu_g_parm(void *v, struct v4l2_streamparm *parm) | |||
1809 | { | |||
1810 | struct utvfu_softc *sc = v; | |||
1811 | ||||
1812 | if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | |||
1813 | return (EINVAL22); | |||
1814 | ||||
1815 | /* | |||
1816 | * XXX Unsure whether there is a way to negotiate this with the | |||
1817 | * device, but returning 0 will allow xenocara's video to run | |||
1818 | */ | |||
1819 | switch (utvfu_norm_params[sc->sc_normi].norm) { | |||
1820 | default: | |||
1821 | return (EINVAL22); | |||
1822 | case V4L2_STD_525_60(((v4l2_std_id)0x00000100) | ((v4l2_std_id)0x00000800) | (((v4l2_std_id )0x00001000) | ((v4l2_std_id)0x00002000) | ((v4l2_std_id)0x00008000 )) | ((v4l2_std_id)0x00004000)): | |||
1823 | parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME0x1000; | |||
1824 | parm->parm.capture.capturemode = 0; | |||
1825 | parm->parm.capture.timeperframe.numerator = 30; | |||
1826 | parm->parm.capture.timeperframe.denominator = 1; | |||
1827 | break; | |||
1828 | case V4L2_STD_PAL((((v4l2_std_id)0x00000001) | ((v4l2_std_id)0x00000002) | ((v4l2_std_id )0x00000004)) | (((v4l2_std_id)0x00000020) | ((v4l2_std_id)0x00000040 ) | ((v4l2_std_id)0x00000080)) | ((v4l2_std_id)0x00000008) | ( (v4l2_std_id)0x00000010)): | |||
1829 | parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME0x1000; | |||
1830 | parm->parm.capture.capturemode = 0; | |||
1831 | parm->parm.capture.timeperframe.numerator = 25; | |||
1832 | parm->parm.capture.timeperframe.denominator = 1; | |||
1833 | break; | |||
1834 | } | |||
1835 | ||||
1836 | return (0); | |||
1837 | } | |||
1838 | ||||
1839 | int | |||
1840 | utvfu_s_parm(void *v, struct v4l2_streamparm *parm) | |||
1841 | { | |||
1842 | if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | |||
1843 | return (EINVAL22); | |||
1844 | ||||
1845 | return (0); | |||
1846 | } | |||
1847 | ||||
1848 | /* | |||
1849 | * A U D I O O P S | |||
1850 | */ | |||
1851 | ||||
1852 | int | |||
1853 | utvfu_audio_open(void *v, int flags) | |||
1854 | { | |||
1855 | struct utvfu_softc *sc = v; | |||
1856 | ||||
1857 | if (usbd_is_dying(sc->sc_udev)) | |||
1858 | return (EIO5); | |||
1859 | ||||
1860 | if ((flags & FWRITE0x0002)) | |||
1861 | return (ENXIO6); | |||
1862 | ||||
1863 | if (ISSET(sc->sc_flags, UTVFU_FLAG_AS_RUNNING)((sc->sc_flags) & (0x02))) | |||
1864 | return (EBUSY16); | |||
1865 | ||||
1866 | return utvfu_as_init(sc); | |||
1867 | } | |||
1868 | ||||
1869 | void | |||
1870 | utvfu_audio_close(void *v) | |||
1871 | { | |||
1872 | struct utvfu_softc *sc = v; | |||
1873 | ||||
1874 | DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); | |||
1875 | ||||
1876 | utvfu_audio_stop(sc); | |||
1877 | utvfu_audio_clear_client(sc); | |||
1878 | } | |||
1879 | ||||
1880 | int | |||
1881 | utvfu_audio_set_params(void *v, int setmode, int usemode, | |||
1882 | struct audio_params *play, struct audio_params *rec) | |||
1883 | { | |||
1884 | struct utvfu_softc *sc = v; | |||
1885 | ||||
1886 | if (usbd_is_dying(sc->sc_udev)) | |||
1887 | return (EIO5); | |||
1888 | ||||
1889 | DPRINTF(1, "%s %s\n", DEVNAME(sc), __func__); | |||
1890 | ||||
1891 | /* XXX ? */ | |||
1892 | play->sample_rate = 0; | |||
1893 | play->encoding = AUDIO_ENCODING_NONE0; | |||
1894 | ||||
1895 | rec->sample_rate = 48000; | |||
1896 | rec->encoding = AUDIO_ENCODING_SLINEAR_LE6; | |||
1897 | rec->precision = 16; | |||
1898 | rec->bps = 2; | |||
1899 | rec->msb = 1; | |||
1900 | rec->channels = 2; | |||
1901 | ||||
1902 | return (0); | |||
1903 | } | |||
1904 | ||||
1905 | int | |||
1906 | utvfu_audio_halt_out(void *v) | |||
1907 | { | |||
1908 | return (EIO5); | |||
1909 | } | |||
1910 | ||||
1911 | int | |||
1912 | utvfu_audio_halt_in(void *v) | |||
1913 | { | |||
1914 | struct utvfu_softc *sc = v; | |||
1915 | ||||
1916 | if (usbd_is_dying(sc->sc_udev)) | |||
1917 | return (EIO5); | |||
1918 | ||||
1919 | DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); | |||
1920 | ||||
1921 | utvfu_audio_stop(sc); | |||
1922 | utvfu_audio_clear_client(sc); | |||
1923 | ||||
1924 | return (0); | |||
1925 | } | |||
1926 | ||||
1927 | int | |||
1928 | utvfu_audio_mixer_set_port(void *v, struct mixer_ctrl *cp) | |||
1929 | { | |||
1930 | struct utvfu_softc *sc = v; | |||
1931 | ||||
1932 | if (usbd_is_dying(sc->sc_udev)) | |||
1933 | return (EIO5); | |||
1934 | ||||
1935 | DPRINTF(1, "%s %s\n", DEVNAME(sc), __func__); | |||
1936 | ||||
1937 | if (cp->type != AUDIO_MIXER_ENUM1 || | |||
1938 | cp->un.ord < 0 || cp->un.ord > 1) | |||
1939 | return (EINVAL22); | |||
1940 | ||||
1941 | /* XXX TODO */ | |||
1942 | ||||
1943 | DPRINTF(1, "%s %s: cp->un.ord=%d\n", DEVNAME(sc), __func__, cp->un.ord); | |||
1944 | ||||
1945 | return (0); | |||
1946 | } | |||
1947 | ||||
1948 | int | |||
1949 | utvfu_audio_mixer_get_port(void *v, struct mixer_ctrl *cp) | |||
1950 | { | |||
1951 | struct utvfu_softc *sc = v; | |||
1952 | ||||
1953 | if (usbd_is_dying(sc->sc_udev)) | |||
1954 | return (EIO5); | |||
1955 | ||||
1956 | DPRINTF(1, "%s %s\n", DEVNAME(sc), __func__); | |||
1957 | ||||
1958 | if (cp->type != AUDIO_MIXER_ENUM1 || | |||
1959 | cp->un.ord < 0 || cp->un.ord > 1) | |||
1960 | return (EINVAL22); | |||
1961 | ||||
1962 | /* XXX TODO */ | |||
1963 | ||||
1964 | DPRINTF(1, "%s %s: cp->un.ord=%d\n", DEVNAME(sc), __func__, cp->un.ord); | |||
1965 | ||||
1966 | return (0); | |||
1967 | } | |||
1968 | ||||
1969 | int | |||
1970 | utvfu_audio_query_devinfo(void *v, struct mixer_devinfo *mi) | |||
1971 | { | |||
1972 | struct utvfu_softc *sc = v; | |||
1973 | ||||
1974 | if (usbd_is_dying(sc->sc_udev)) | |||
1975 | return (EIO5); | |||
1976 | ||||
1977 | DPRINTF(1, "%s %s\n", DEVNAME(sc), __func__); | |||
1978 | ||||
1979 | if (mi->index != 0) | |||
1980 | return (EINVAL22); | |||
1981 | ||||
1982 | /* XXX SOMEONE WITH AUDIO EXPERTIZE NEEDS TO HELP HERE */ | |||
1983 | strlcpy(mi->label.name, "mix0-i0", sizeof(mi->label.name)); | |||
1984 | mi->type = AUDIO_MIXER_ENUM1; | |||
1985 | mi->un.e.num_mem = 2; | |||
1986 | mi->un.e.member[0].ord = 0; | |||
1987 | strlcpy(mi->un.e.member[0].label.name, AudioNoff"off", | |||
1988 | sizeof(mi->un.e.member[0].label.name)); | |||
1989 | mi->un.e.member[1].ord = 1; | |||
1990 | strlcpy(mi->un.e.member[1].label.name, AudioNon"on", | |||
1991 | sizeof(mi->un.e.member[1].label.name)); | |||
1992 | ||||
1993 | return (0); | |||
1994 | } | |||
1995 | ||||
1996 | int | |||
1997 | utvfu_audio_trigger_output(void *v, void *start, void *end, int blksize, | |||
1998 | void (*intr)(void *), void *arg, struct audio_params *param) | |||
1999 | { | |||
2000 | return (EIO5); | |||
2001 | } | |||
2002 | ||||
2003 | int | |||
2004 | utvfu_audio_trigger_input(void *v, void *start, void *end, int blksize, | |||
2005 | void (*intr)(void *), void *arg, struct audio_params *param) | |||
2006 | { | |||
2007 | struct utvfu_softc *sc = v; | |||
2008 | ||||
2009 | if (usbd_is_dying(sc->sc_udev)) | |||
2010 | return (EIO5); | |||
2011 | ||||
2012 | rw_enter_write(&sc->sc_audio.rwlock); | |||
2013 | ||||
2014 | sc->sc_audio.intr_arg = arg; | |||
2015 | sc->sc_audio.intr = intr; | |||
2016 | sc->sc_audio.start = start; | |||
2017 | sc->sc_audio.end = end; | |||
2018 | sc->sc_audio.cur = start; | |||
2019 | sc->sc_audio.blksize = blksize; | |||
2020 | ||||
2021 | rw_exit_write(&sc->sc_audio.rwlock); | |||
2022 | ||||
2023 | DPRINTF(1, "%s %s: start=%p end=%p diff=%lu blksize=%d\n", | |||
2024 | DEVNAME(sc), __func__, start, end, | |||
2025 | ((u_char *)end - (u_char *)start), blksize); | |||
2026 | ||||
2027 | return utvfu_audio_start(sc); | |||
2028 | } | |||
2029 | ||||
2030 | int | |||
2031 | utvfu_audio_start(struct utvfu_softc *sc) | |||
2032 | { | |||
2033 | DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); | |||
2034 | ||||
2035 | if (ISSET(sc->sc_flags, UTVFU_FLAG_AS_RUNNING)((sc->sc_flags) & (0x02))) | |||
2036 | return (0); | |||
2037 | ||||
2038 | utvfu_audio_start_chip(sc); | |||
2039 | ||||
2040 | if (utvfu_as_init(sc) != 0) | |||
2041 | return (ENOMEM12); | |||
2042 | if (sc->sc_audio.iface.pipeh == NULL((void *)0)) { | |||
2043 | if (utvfu_as_open(sc) != USBD_NORMAL_COMPLETION) | |||
2044 | return (ENOMEM12); | |||
2045 | } | |||
2046 | ||||
2047 | return utvfu_as_start_bulk(sc); | |||
2048 | } | |||
2049 | ||||
2050 | int | |||
2051 | utvfu_audio_stop(struct utvfu_softc *sc) | |||
2052 | { | |||
2053 | DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); | |||
2054 | ||||
2055 | utvfu_audio_stop_chip(sc); | |||
2056 | utvfu_as_free(sc); | |||
2057 | ||||
2058 | return (0); | |||
2059 | } |