File: | dev/usb/utvfu.c |
Warning: | line 998, column 8 Although the value stored to 'nep' is used in the enclosing expression, the value is never actually read from 'nep' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: utvfu.c,v 1.15 2021/11/28 14:10:32 mglocker 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_get_props(void *); |
799 | int utvfu_audio_trigger_output(void *, void *, void *, int, |
800 | void (*)(void *), void *, struct audio_params *); |
801 | int utvfu_audio_trigger_input(void *, void *, void *, int, |
802 | void (*)(void *), void *, struct audio_params *); |
803 | |
804 | struct cfdriver utvfu_cd = { |
805 | NULL((void *)0), "utvfu", DV_DULL |
806 | }; |
807 | |
808 | const struct cfattach utvfu_ca = { |
809 | sizeof(struct utvfu_softc), |
810 | utvfu_match, |
811 | utvfu_attach, |
812 | utvfu_detach, |
813 | NULL((void *)0) |
814 | }; |
815 | |
816 | struct video_hw_if utvfu_vid_hw_if = { |
817 | utvfu_open, /* open */ |
818 | utvfu_close, /* close */ |
819 | utvfu_querycap, /* VIDIOC_QUERYCAP */ |
820 | utvfu_enum_fmt_vid_cap, /* VIDIOC_ENUM_FMT */ |
821 | utvfu_enum_fsizes, /* VIDIOC_ENUM_FRAMESIZES */ |
822 | NULL((void *)0), /* VIDIOC_ENUM_FRAMEINTERVALS */ |
823 | utvfu_s_fmt, /* VIDIOC_S_FMT */ |
824 | utvfu_g_fmt, /* VIDIOC_G_FMT */ |
825 | utvfu_s_parm, /* VIDIOC_S_PARM */ |
826 | utvfu_g_parm, /* VIDIOC_G_PARM */ |
827 | utvfu_enum_input, /* VIDIOC_ENUMINPUT */ |
828 | utvfu_s_input, /* VIDIOC_S_INPUT */ |
829 | utvfu_g_input, /* VIDIOC_G_INPUT */ |
830 | utvfu_reqbufs, /* VIDIOC_REQBUFS */ |
831 | utvfu_querybuf, /* VIDIOC_QUERYBUF */ |
832 | utvfu_qbuf, /* VIDIOC_QBUF */ |
833 | utvfu_dqbuf, /* VIDIOC_DQBUF */ |
834 | utvfu_streamon, /* VIDIOC_STREAMON */ |
835 | utvfu_streamoff, /* VIDIOC_STREAMOFF */ |
836 | NULL((void *)0), /* VIDIOC_TRY_FMT */ |
837 | utvfu_queryctrl, /* VIDIOC_QUERYCTRL */ |
838 | NULL((void *)0), /* VIDIOC_G_CTRL */ |
839 | NULL((void *)0), /* VIDIOC_S_CTRL */ |
840 | utvfu_mappage, /* mmap */ |
841 | utvfu_get_bufsize, /* read */ |
842 | utvfu_start_read /* start stream for read */ |
843 | }; |
844 | |
845 | struct audio_hw_if utvfu_au_hw_if = { |
846 | utvfu_audio_open, /* open hardware */ |
847 | utvfu_audio_close, /* close hardware */ |
848 | utvfu_audio_set_params, |
849 | NULL((void *)0), |
850 | NULL((void *)0), |
851 | NULL((void *)0), |
852 | NULL((void *)0), |
853 | NULL((void *)0), |
854 | NULL((void *)0), |
855 | utvfu_audio_halt_out, |
856 | utvfu_audio_halt_in, |
857 | NULL((void *)0), |
858 | NULL((void *)0), |
859 | utvfu_audio_mixer_set_port, |
860 | utvfu_audio_mixer_get_port, |
861 | utvfu_audio_query_devinfo, |
862 | NULL((void *)0), |
863 | NULL((void *)0), |
864 | NULL((void *)0), |
865 | utvfu_audio_get_props, |
866 | utvfu_audio_trigger_output, |
867 | utvfu_audio_trigger_input |
868 | }; |
869 | |
870 | int |
871 | utvfu_match(struct device *parent, void *match, void *aux) |
872 | { |
873 | struct usb_attach_arg *uaa = aux; |
874 | const struct usb_descriptor *ud; |
875 | struct usbd_desc_iter iter; |
876 | struct usb_interface_descriptor *uid = NULL((void *)0); |
877 | const struct usb_endpoint_descriptor *ued = NULL((void *)0); |
878 | usb_device_descriptor_t *dd; |
879 | int ret = UMATCH_NONE0; |
880 | int nep, nalt; |
881 | uint16_t psize = 0; |
882 | |
883 | if (uaa->iface == NULL((void *)0)) |
884 | return ret; |
885 | |
886 | dd = usbd_get_device_descriptor(uaa->device); |
887 | |
888 | if (UGETW(dd->idVendor)(*(u_int16_t *)(dd->idVendor)) == USB_VENDOR_FUSHICAI0x1b71 && |
889 | UGETW(dd->idProduct)(*(u_int16_t *)(dd->idProduct)) == USB_PRODUCT_FUSHICAI_USBTV0070x3002) |
890 | ret = UMATCH_VENDOR_PRODUCT13; |
891 | /* |
892 | * This seems like a fragile check, but the original driver ensures |
893 | * there are two alternate settings for the interface, and alternate |
894 | * setting 1 has four endpoints. |
895 | * |
896 | * Comment says "Checks that the device is what we think it is." |
897 | * |
898 | * Adding check that wMaxPacketSize for the video endpoint is > 0. |
899 | */ |
900 | nep = nalt = 0; |
901 | usbd_desc_iter_init(uaa->device, &iter); |
902 | while ((ud = usbd_desc_iter_next(&iter)) != NULL((void *)0)) { |
903 | switch (ud->bDescriptorType) { |
904 | default: |
905 | break; |
906 | case UDESC_INTERFACE0x04: |
907 | uid = (void *)ud; |
908 | if (uid->bInterfaceNumber == 0) |
909 | nalt++; |
910 | break; |
911 | case UDESC_ENDPOINT0x05: |
912 | if (uid->bAlternateSetting == 1) { |
913 | ued = (void *)ud; |
914 | if (ued->bEndpointAddress == UTVFU_VIDEO_ENDP0x81) |
915 | psize = UGETW(ued->wMaxPacketSize)(*(u_int16_t *)(ued->wMaxPacketSize)); |
916 | nep++; |
917 | } |
918 | break; |
919 | } |
920 | if (uid != NULL((void *)0) && uid->bInterfaceNumber > 0) |
921 | break; |
922 | } |
923 | |
924 | if (nalt != 2 || nep != 4 || psize == 0) |
925 | ret = UMATCH_NONE0; |
926 | |
927 | return (ret); |
928 | } |
929 | |
930 | void |
931 | utvfu_attach(struct device *parent, struct device *self, void *aux) |
932 | { |
933 | int i; |
934 | struct utvfu_softc *sc = (struct utvfu_softc *)self; |
935 | struct usb_attach_arg *uaa = aux; |
936 | |
937 | sc->sc_udev = uaa->device; |
938 | for (i = 0; i < uaa->nifaces; i++) { |
939 | if (usbd_iface_claimed(sc->sc_udev, i)) |
940 | continue; |
941 | usbd_claim_iface(sc->sc_udev, i); |
942 | } |
943 | |
944 | utvfu_parse_desc(sc); |
945 | |
946 | /* init mmap queue */ |
947 | 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); |
948 | sc->sc_mmap_count = 0; |
949 | |
950 | sc->sc_max_frame_sz = utvfu_max_frame_size(); |
951 | |
952 | /* calculate optimal isoc xfer size */ |
953 | sc->sc_nframes = (sc->sc_max_frame_sz + sc->sc_iface.psize - 1) |
954 | / sc->sc_iface.psize; |
955 | if (sc->sc_nframes > UTVFU_NFRAMES_MAX40) |
956 | sc->sc_nframes = UTVFU_NFRAMES_MAX40; |
957 | DPRINTF(1, "%s: nframes=%d\n", DEVNAME(sc), sc->sc_nframes); |
958 | |
959 | rw_init(&sc->sc_audio.rwlock, "audiorwl")_rw_init_flags(&sc->sc_audio.rwlock, "audiorwl", 0, (( void *)0)); |
960 | |
961 | sc->sc_audiodev = audio_attach_mi(&utvfu_au_hw_if, sc, &sc->sc_dev); |
962 | sc->sc_videodev = video_attach_mi(&utvfu_vid_hw_if, sc, &sc->sc_dev); |
963 | } |
964 | |
965 | int |
966 | utvfu_detach(struct device *self, int flags) |
967 | { |
968 | struct utvfu_softc *sc = (struct utvfu_softc *)self; |
969 | int rv = 0; |
970 | |
971 | /* Wait for outstanding requests to complete */ |
972 | usbd_delay_ms(sc->sc_udev, UTVFU_NFRAMES_MAX40); /* XXX meh? */ |
973 | |
974 | if (sc->sc_videodev != NULL((void *)0)) |
975 | rv = config_detach(sc->sc_videodev, flags); |
976 | |
977 | if (sc->sc_audiodev != NULL((void *)0)) |
978 | rv += config_detach(sc->sc_audiodev, flags); |
979 | |
980 | utvfu_as_free(sc); |
981 | utvfu_vs_free(sc); |
982 | |
983 | sc->sc_flags = 0; |
984 | |
985 | return (rv); |
986 | } |
987 | |
988 | usbd_status |
989 | utvfu_parse_desc(struct utvfu_softc *sc) |
990 | { |
991 | int nif, nep; |
992 | uint32_t psize; |
993 | struct usbd_desc_iter iter; |
994 | const struct usb_descriptor *ud; |
995 | struct usb_endpoint_descriptor *ued; |
996 | struct usb_interface_descriptor *uid = NULL((void *)0); |
997 | |
998 | nif = nep = 0; |
Although the value stored to 'nep' is used in the enclosing expression, the value is never actually read from 'nep' | |
999 | usbd_desc_iter_init(sc->sc_udev, &iter); |
1000 | while ((ud = usbd_desc_iter_next(&iter)) != NULL((void *)0)) { |
1001 | if (ud->bDescriptorType != UDESC_INTERFACE0x04) |
1002 | continue; |
1003 | /* looking for interface 0, alt-setting 1 */ |
1004 | uid = (void *)ud; |
1005 | if (uid->bInterfaceNumber > 0) |
1006 | break; |
1007 | if (uid->bAlternateSetting == 1) |
1008 | break; |
1009 | } |
1010 | |
1011 | /* this should not fail as it was ensured during match */ |
1012 | if (uid == NULL((void *)0) || uid->bInterfaceNumber != 0 || |
1013 | uid->bAlternateSetting != 1) { |
1014 | printf("%s: no valid alternate interface found!\n", |
1015 | DEVNAME(sc)((sc)->sc_dev.dv_xname)); |
1016 | return (USBD_INVAL); |
1017 | } |
1018 | |
1019 | /* bInterfaceNumber = 0 */ |
1020 | sc->sc_uifaceh = &sc->sc_udev->ifaces[0]; |
1021 | |
1022 | /* looking for video endpoint to on alternate setting 1 */ |
1023 | while ((ud = usbd_desc_iter_next(&iter)) != NULL((void *)0)) { |
1024 | if (ud->bDescriptorType != UDESC_ENDPOINT0x05) |
1025 | break; |
1026 | |
1027 | ued = (void *)ud; |
1028 | if (ued->bEndpointAddress != UTVFU_VIDEO_ENDP0x81) |
1029 | continue; |
1030 | |
1031 | psize = UGETW(ued->wMaxPacketSize)(*(u_int16_t *)(ued->wMaxPacketSize)); |
1032 | psize = UE_GET_SIZE(psize)((psize) & 0x7ff) * (1 + UE_GET_TRANS(psize)(((psize) >> 11) & 0x3)); |
1033 | sc->sc_iface.psize = psize; |
1034 | break; |
1035 | } |
1036 | |
1037 | return (USBD_NORMAL_COMPLETION); |
1038 | } |
1039 | |
1040 | int |
1041 | utvfu_open(void *addr, int flags, int *size, uint8_t *buffer, |
1042 | void (*intr)(void *), void *arg) |
1043 | { |
1044 | struct utvfu_softc *sc = addr; |
1045 | int rv; |
1046 | |
1047 | DPRINTF(1, "%s: utvfu_open: sc=%p\n", DEVNAME(sc), sc); |
1048 | |
1049 | if (usbd_is_dying(sc->sc_udev)) |
1050 | return (EIO5); |
1051 | |
1052 | if ((rv = utvfu_vs_init(sc)) != 0) |
1053 | return (rv); |
1054 | |
1055 | /* pointers to upper video layer */ |
1056 | sc->sc_uplayer_arg = arg; |
1057 | sc->sc_uplayer_fsize = size; |
1058 | sc->sc_uplayer_fbuffer = buffer; |
1059 | sc->sc_uplayer_intr = intr; |
1060 | |
1061 | sc->sc_flags &= ~UTVFU_FLAG_MMAP0x01; |
1062 | |
1063 | return (0); |
1064 | } |
1065 | |
1066 | int |
1067 | utvfu_close(void *addr) |
1068 | { |
1069 | struct utvfu_softc *sc = addr; |
1070 | |
1071 | DPRINTF(1, "%s: utvfu_close: sc=%p\n", DEVNAME(sc), sc); |
1072 | |
1073 | /* free & clean up video stream */ |
1074 | utvfu_vs_free(sc); |
1075 | |
1076 | return (0); |
1077 | } |
1078 | |
1079 | usbd_status |
1080 | utvfu_as_open(struct utvfu_softc *sc) |
1081 | { |
1082 | usb_endpoint_descriptor_t *ed; |
1083 | usbd_status error; |
1084 | |
1085 | DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); |
1086 | |
1087 | if (sc->sc_audio.iface.pipeh != NULL((void *)0)) { |
1088 | printf("%s: %s called while sc_audio.iface.pipeh not NULL\n", |
1089 | DEVNAME(sc)((sc)->sc_dev.dv_xname), __func__); |
1090 | return (USBD_INVAL); |
1091 | } |
1092 | |
1093 | ed = usbd_get_endpoint_descriptor(sc->sc_uifaceh, UTVFU_AUDIO_ENDP0x83); |
1094 | if (ed == NULL((void *)0)) { |
1095 | printf("%s: no endpoint descriptor for AS iface\n", |
1096 | DEVNAME(sc)((sc)->sc_dev.dv_xname)); |
1097 | return (USBD_INVAL); |
1098 | } |
1099 | DPRINTF(1, "%s: open pipe for ", DEVNAME(sc)); |
1100 | DPRINTF(1, "bEndpointAddress=0x%02x (0x%02x), wMaxPacketSize=" |
1101 | "0x%04x (%d)\n", |
1102 | UE_GET_ADDR(ed->bEndpointAddress), |
1103 | UTVFU_AUDIO_ENDP, |
1104 | UGETW(ed->wMaxPacketSize), |
1105 | UE_GET_SIZE(UGETW(ed->wMaxPacketSize)) |
1106 | * (1 + UE_GET_TRANS(UGETW(ed->wMaxPacketSize)))); |
1107 | |
1108 | error = usbd_open_pipe( |
1109 | sc->sc_uifaceh, |
1110 | UTVFU_AUDIO_ENDP0x83, |
1111 | USBD_EXCLUSIVE_USE0x01, |
1112 | &sc->sc_audio.iface.pipeh); |
1113 | if (error != USBD_NORMAL_COMPLETION) { |
1114 | printf("%s: could not open AS pipe: %s\n", |
1115 | DEVNAME(sc)((sc)->sc_dev.dv_xname), usbd_errstr(error)); |
1116 | } |
1117 | |
1118 | return (error); |
1119 | } |
1120 | |
1121 | usbd_status |
1122 | utvfu_vs_open(struct utvfu_softc *sc) |
1123 | { |
1124 | usb_endpoint_descriptor_t *ed; |
1125 | usbd_status error; |
1126 | |
1127 | DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); |
1128 | |
1129 | if (sc->sc_iface.pipeh != NULL((void *)0)) { |
1130 | printf("%s: %s called while sc_iface.pipeh not NULL\n", |
1131 | DEVNAME(sc)((sc)->sc_dev.dv_xname), __func__); |
1132 | return (USBD_INVAL); |
1133 | } |
1134 | |
1135 | ed = usbd_get_endpoint_descriptor(sc->sc_uifaceh, UTVFU_VIDEO_ENDP0x81); |
1136 | if (ed == NULL((void *)0)) { |
1137 | printf("%s: no endpoint descriptor for VS iface\n", |
1138 | DEVNAME(sc)((sc)->sc_dev.dv_xname)); |
1139 | return (USBD_INVAL); |
1140 | } |
1141 | DPRINTF(1, "%s: open pipe for ", DEVNAME(sc)); |
1142 | DPRINTF(1, "bEndpointAddress=0x%02x (0x%02x), wMaxPacketSize=" |
1143 | "0x%04x (%d)\n", |
1144 | UE_GET_ADDR(ed->bEndpointAddress), |
1145 | UTVFU_VIDEO_ENDP, |
1146 | UGETW(ed->wMaxPacketSize), |
1147 | sc->sc_iface.psize); |
1148 | |
1149 | error = usbd_open_pipe( |
1150 | sc->sc_uifaceh, |
1151 | UTVFU_VIDEO_ENDP0x81, |
1152 | USBD_EXCLUSIVE_USE0x01, |
1153 | &sc->sc_iface.pipeh); |
1154 | if (error != USBD_NORMAL_COMPLETION) { |
1155 | printf("%s: could not open VS pipe: %s\n", |
1156 | DEVNAME(sc)((sc)->sc_dev.dv_xname), usbd_errstr(error)); |
1157 | } |
1158 | |
1159 | return (error); |
1160 | } |
1161 | |
1162 | void |
1163 | utvfu_as_close(struct utvfu_softc *sc) |
1164 | { |
1165 | DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); |
1166 | |
1167 | CLR(sc->sc_flags, UTVFU_FLAG_AS_RUNNING)((sc->sc_flags) &= ~(0x02)); |
1168 | |
1169 | if (sc->sc_audio.iface.pipeh != NULL((void *)0)) { |
1170 | usbd_abort_pipe(sc->sc_audio.iface.pipeh); |
1171 | |
1172 | usbd_ref_wait(sc->sc_udev); |
1173 | |
1174 | usbd_close_pipe(sc->sc_audio.iface.pipeh); |
1175 | sc->sc_audio.iface.pipeh = NULL((void *)0); |
1176 | } |
1177 | } |
1178 | |
1179 | void |
1180 | utvfu_vs_close(struct utvfu_softc *sc) |
1181 | { |
1182 | if (sc->sc_iface.pipeh != NULL((void *)0)) { |
1183 | usbd_close_pipe(sc->sc_iface.pipeh); |
1184 | sc->sc_iface.pipeh = NULL((void *)0); |
1185 | } |
1186 | |
1187 | /* |
1188 | * Some devices need time to shutdown before we switch back to |
1189 | * the default interface (0). Not doing so can leave the device |
1190 | * back in a undefined condition. |
1191 | */ |
1192 | usbd_delay_ms(sc->sc_udev, 100); |
1193 | |
1194 | /* switch back to default interface (turns off cam LED) */ |
1195 | (void)usbd_set_interface(sc->sc_uifaceh, UTVFU_DFLT_IFACE_IDX0); |
1196 | } |
1197 | |
1198 | void |
1199 | utvfu_read(struct utvfu_softc *sc, uint8_t *buf, int len) |
1200 | { |
1201 | /* |
1202 | * Copy video frame to upper layer buffer and call |
1203 | * upper layer interrupt. |
1204 | */ |
1205 | *sc->sc_uplayer_fsize = len; |
1206 | memcpy(sc->sc_uplayer_fbuffer, buf, len)__builtin_memcpy((sc->sc_uplayer_fbuffer), (buf), (len)); |
1207 | (*sc->sc_uplayer_intr)(sc->sc_uplayer_arg); |
1208 | } |
1209 | |
1210 | int |
1211 | utvfu_as_start_bulk(struct utvfu_softc *sc) |
1212 | { |
1213 | int error; |
1214 | |
1215 | if (ISSET(sc->sc_flags, UTVFU_FLAG_AS_RUNNING)((sc->sc_flags) & (0x02))) |
1216 | return (0); |
1217 | if (sc->sc_audio.iface.pipeh == NULL((void *)0)) |
1218 | return (ENXIO6); |
1219 | |
1220 | SET(sc->sc_flags, UTVFU_FLAG_AS_RUNNING)((sc->sc_flags) |= (0x02)); |
1221 | error = kthread_create(utvfu_as_bulk_thread, sc, NULL((void *)0), DEVNAME(sc)((sc)->sc_dev.dv_xname)); |
1222 | if (error) { |
1223 | CLR(sc->sc_flags, UTVFU_FLAG_AS_RUNNING)((sc->sc_flags) &= ~(0x02)); |
1224 | printf("%s: can't create kernel thread!", DEVNAME(sc)((sc)->sc_dev.dv_xname)); |
1225 | } |
1226 | |
1227 | return (error); |
1228 | } |
1229 | |
1230 | void |
1231 | utvfu_as_bulk_thread(void *arg) |
1232 | { |
1233 | struct utvfu_softc *sc = arg; |
1234 | struct utvfu_as_iface *iface; |
1235 | usbd_status error; |
1236 | uint32_t actlen; |
1237 | |
1238 | DPRINTF(1, "%s %s\n", DEVNAME(sc), __func__); |
1239 | |
1240 | iface = &sc->sc_audio.iface; |
1241 | usbd_ref_incr(sc->sc_udev); |
1242 | while (ISSET(sc->sc_flags, UTVFU_FLAG_AS_RUNNING)((sc->sc_flags) & (0x02))) { |
1243 | usbd_setup_xfer( |
1244 | iface->xfer, |
1245 | iface->pipeh, |
1246 | 0, |
1247 | NULL((void *)0), |
1248 | UTVFU_AUDIO_URBSIZE20480, |
1249 | USBD_NO_COPY0x01 | USBD_SHORT_XFER_OK0x04 | USBD_SYNCHRONOUS0x02, |
1250 | 0, |
1251 | NULL((void *)0)); |
1252 | error = usbd_transfer(iface->xfer); |
1253 | |
1254 | if (error != USBD_NORMAL_COMPLETION) { |
1255 | DPRINTF(1, "%s: error in bulk xfer: %s!\n", |
1256 | DEVNAME(sc), usbd_errstr(error)); |
1257 | break; |
1258 | } |
1259 | |
1260 | usbd_get_xfer_status(iface->xfer, NULL((void *)0), NULL((void *)0), &actlen, |
1261 | NULL((void *)0)); |
1262 | DPRINTF(2, "%s: *** buffer len = %d\n", DEVNAME(sc), actlen); |
1263 | |
1264 | rw_enter_read(&sc->sc_audio.rwlock); |
1265 | utvfu_audio_decode(sc, actlen); |
1266 | rw_exit_read(&sc->sc_audio.rwlock); |
1267 | } |
1268 | |
1269 | CLR(sc->sc_flags, UTVFU_FLAG_AS_RUNNING)((sc->sc_flags) &= ~(0x02)); |
1270 | usbd_ref_decr(sc->sc_udev); |
1271 | |
1272 | DPRINTF(1, "%s %s: exiting\n", DEVNAME(sc), __func__); |
1273 | |
1274 | kthread_exit(0); |
1275 | } |
1276 | |
1277 | void |
1278 | utvfu_vs_start_isoc(struct utvfu_softc *sc) |
1279 | { |
1280 | int i; |
1281 | |
1282 | for (i = 0; i < UTVFU_ISOC_TRANSFERS3; i++) |
1283 | utvfu_vs_start_isoc_ixfer(sc, &sc->sc_iface.ixfer[i]); |
1284 | } |
1285 | |
1286 | void |
1287 | utvfu_vs_start_isoc_ixfer(struct utvfu_softc *sc, |
1288 | struct utvfu_isoc_xfer *ixfer) |
1289 | { |
1290 | int i; |
1291 | usbd_status error; |
1292 | |
1293 | DPRINTF(2, "%s: %s\n", DEVNAME(sc), __func__); |
1294 | |
1295 | if (usbd_is_dying(sc->sc_udev)) |
1296 | return; |
1297 | |
1298 | for (i = 0; i < sc->sc_nframes; i++) |
1299 | ixfer->size[i] = sc->sc_iface.psize; |
1300 | |
1301 | usbd_setup_isoc_xfer( |
1302 | ixfer->xfer, |
1303 | sc->sc_iface.pipeh, |
1304 | ixfer, |
1305 | ixfer->size, |
1306 | sc->sc_nframes, |
1307 | USBD_NO_COPY0x01 | USBD_SHORT_XFER_OK0x04, |
1308 | utvfu_vs_cb); |
1309 | |
1310 | error = usbd_transfer(ixfer->xfer); |
1311 | if (error && error != USBD_IN_PROGRESS) { |
1312 | DPRINTF(1, "%s: usbd_transfer error=%s!\n", |
1313 | DEVNAME(sc), usbd_errstr(error)); |
1314 | } |
1315 | } |
1316 | |
1317 | /* |
1318 | * Each packet contains a number of 256-byte chunks composing the image frame. |
1319 | */ |
1320 | void |
1321 | utvfu_vs_cb(struct usbd_xfer *xfer, void *priv, usbd_status status) |
1322 | { |
1323 | struct utvfu_isoc_xfer *ixfer = priv; |
1324 | struct utvfu_softc *sc = ixfer->sc; |
1325 | int i, off, frame_size; |
1326 | uint32_t actlen; |
1327 | uint8_t *frame; |
1328 | |
1329 | DPRINTF(2, "%s: %s\n", DEVNAME(sc), __func__); |
1330 | |
1331 | if (status != USBD_NORMAL_COMPLETION) { |
1332 | DPRINTF(1, "%s: %s: %s\n", DEVNAME(sc), __func__, |
1333 | usbd_errstr(status)); |
1334 | return; |
1335 | } |
1336 | usbd_get_xfer_status(xfer, NULL((void *)0), NULL((void *)0), &actlen, NULL((void *)0)); |
1337 | |
1338 | DPRINTF(2, "%s: *** buffer len = %d\n", DEVNAME(sc), actlen); |
1339 | if (actlen == 0) |
1340 | goto skip; |
1341 | |
1342 | frame = KERNADDR(&xfer->dmabuf, 0)((void *)((char *)((&xfer->dmabuf)->block->kaddr + (&xfer->dmabuf)->offs) + (0))); |
1343 | for (i = 0; i < sc->sc_nframes; i++, frame += sc->sc_iface.psize) { |
1344 | frame_size = ixfer->size[i]; |
1345 | |
1346 | if (frame_size == 0) |
1347 | /* frame is empty */ |
1348 | continue; |
1349 | |
1350 | #define CHUNK_STRIDE (UTVFU_CHUNK_SIZE256*4) |
1351 | for (off = 0; off + CHUNK_STRIDE <= frame_size; |
1352 | off += CHUNK_STRIDE) { |
1353 | utvfu_image_chunk(sc, frame + off); |
1354 | } |
1355 | #undef CHUNK_STRIDE |
1356 | } |
1357 | |
1358 | skip: /* setup new transfer */ |
1359 | utvfu_vs_start_isoc_ixfer(sc, ixfer); |
1360 | } |
1361 | |
1362 | int |
1363 | utvfu_find_queued(struct utvfu_softc *sc) |
1364 | { |
1365 | int i; |
1366 | |
1367 | /* find a buffer which is ready for queueing */ |
1368 | for (i = 0; i < sc->sc_mmap_count; i++) { |
1369 | if (sc->sc_mmap[i].v4l2_buf.flags & V4L2_BUF_FLAG_DONE0x00000004) |
1370 | continue; |
1371 | if (sc->sc_mmap[i].v4l2_buf.flags & V4L2_BUF_FLAG_QUEUED0x00000002) |
1372 | return (i); |
1373 | } |
1374 | |
1375 | return (-1); |
1376 | } |
1377 | |
1378 | int |
1379 | utvfu_mmap_queue(struct utvfu_softc *sc, uint8_t *buf, int len) |
1380 | { |
1381 | int i; |
1382 | |
1383 | if (sc->sc_mmap_count == 0 || sc->sc_mmap_buffer == NULL((void *)0)) |
1384 | panic("%s: mmap buffers not allocated", __func__); |
1385 | |
1386 | /* find a buffer which is ready for queueing */ |
1387 | if ((i = utvfu_find_queued(sc)) == -1) { |
1388 | DPRINTF(2, "%s: mmap queue is full!\n", DEVNAME(sc)); |
1389 | return (ENOMEM12); |
1390 | } |
1391 | |
1392 | /* copy frame to mmap buffer and report length */ |
1393 | memcpy(sc->sc_mmap[i].buf, buf, len)__builtin_memcpy((sc->sc_mmap[i].buf), (buf), (len)); |
1394 | sc->sc_mmap[i].v4l2_buf.bytesused = len; |
1395 | |
1396 | /* timestamp it */ |
1397 | getmicrotime(&sc->sc_mmap[i].v4l2_buf.timestamp); |
1398 | |
1399 | /* appropriately set/clear flags */ |
1400 | sc->sc_mmap[i].v4l2_buf.flags &= ~V4L2_BUF_FLAG_QUEUED0x00000002; |
1401 | sc->sc_mmap[i].v4l2_buf.flags |= V4L2_BUF_FLAG_DONE0x00000004; |
1402 | |
1403 | /* queue it */ |
1404 | 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); |
1405 | DPRINTF(2, "%s: %s: frame queued on index %d\n", |
1406 | DEVNAME(sc), __func__, i); |
1407 | |
1408 | wakeup(sc); |
1409 | |
1410 | /* |
1411 | * In case userland uses poll(2), signal that we have a frame |
1412 | * ready to dequeue. |
1413 | */ |
1414 | (*sc->sc_uplayer_intr)(sc->sc_uplayer_arg); |
1415 | |
1416 | return (0); |
1417 | } |
1418 | |
1419 | caddr_t |
1420 | utvfu_mappage(void *v, off_t off, int prot) |
1421 | { |
1422 | struct utvfu_softc *sc = v; |
1423 | caddr_t p = NULL((void *)0); |
1424 | |
1425 | if (off < sc->sc_mmap_bufsz) { |
1426 | if ((sc->sc_flags & UTVFU_FLAG_MMAP0x01) == 0) |
1427 | sc->sc_flags |= UTVFU_FLAG_MMAP0x01; |
1428 | |
1429 | p = sc->sc_mmap_buffer + off; |
1430 | } |
1431 | |
1432 | return (p); |
1433 | } |
1434 | |
1435 | int |
1436 | utvfu_get_bufsize(void *v) |
1437 | { |
1438 | struct utvfu_softc *sc = v; |
1439 | |
1440 | /* YUYV/YUV-422: 4 bytes/2 pixel */ |
1441 | return (utvfu_norm_params[sc->sc_normi].cap_width * |
1442 | utvfu_norm_params[sc->sc_normi].cap_height * 2); |
1443 | } |
1444 | |
1445 | int |
1446 | utvfu_start_read(void *v) |
1447 | { |
1448 | struct utvfu_softc *sc = v; |
1449 | usbd_status error; |
1450 | |
1451 | if (sc->sc_flags & UTVFU_FLAG_MMAP0x01) |
1452 | sc->sc_flags &= ~UTVFU_FLAG_MMAP0x01; |
1453 | |
1454 | /* open video stream pipe */ |
1455 | error = utvfu_vs_open(sc); |
1456 | if (error != USBD_NORMAL_COMPLETION) |
1457 | return (EINVAL22); |
1458 | |
1459 | utvfu_vs_start_isoc(sc); |
1460 | |
1461 | return (0); |
1462 | } |
1463 | |
1464 | void |
1465 | utvfu_audio_clear_client(struct utvfu_softc *sc) |
1466 | { |
1467 | rw_enter_write(&sc->sc_audio.rwlock); |
1468 | |
1469 | sc->sc_audio.intr = NULL((void *)0); |
1470 | sc->sc_audio.intr_arg = NULL((void *)0); |
1471 | sc->sc_audio.start = NULL((void *)0); |
1472 | sc->sc_audio.end = NULL((void *)0); |
1473 | sc->sc_audio.cur = NULL((void *)0); |
1474 | sc->sc_audio.blksize = 0; |
1475 | |
1476 | rw_exit_write(&sc->sc_audio.rwlock); |
1477 | } |
1478 | |
1479 | void |
1480 | utvfu_as_free(struct utvfu_softc *sc) |
1481 | { |
1482 | DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); |
1483 | |
1484 | utvfu_as_close(sc); |
1485 | utvfu_as_free_bulk(sc); |
1486 | } |
1487 | |
1488 | void |
1489 | utvfu_vs_free(struct utvfu_softc *sc) |
1490 | { |
1491 | DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); |
1492 | utvfu_vs_close(sc); |
1493 | utvfu_vs_free_isoc(sc); |
1494 | utvfu_vs_free_frame(sc); |
1495 | } |
1496 | |
1497 | int |
1498 | utvfu_as_init(struct utvfu_softc *sc) |
1499 | { |
1500 | DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); |
1501 | |
1502 | if (sc->sc_audio.iface.xfer != NULL((void *)0)) |
1503 | return (0); |
1504 | |
1505 | /* allocate audio and video stream xfer buffer */ |
1506 | return utvfu_as_alloc_bulk(sc); |
1507 | } |
1508 | |
1509 | int |
1510 | utvfu_vs_init(struct utvfu_softc *sc) |
1511 | { |
1512 | DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); |
1513 | |
1514 | if (utvfu_start_capture(sc) != 0) |
1515 | return (EINVAL22); |
1516 | |
1517 | if (utvfu_vs_alloc_isoc(sc) != 0 || utvfu_vs_alloc_frame(sc) != 0) |
1518 | return (ENOMEM12); |
1519 | |
1520 | return (0); |
1521 | } |
1522 | |
1523 | int |
1524 | utvfu_vs_alloc_frame(struct utvfu_softc *sc) |
1525 | { |
1526 | struct utvfu_frame_buf *fb = &sc->sc_fb; |
1527 | |
1528 | fb->size = sc->sc_max_frame_sz; |
1529 | fb->buf = malloc(fb->size, M_USBDEV102, M_NOWAIT0x0002); |
1530 | if (fb->buf == NULL((void *)0)) { |
1531 | printf("%s: can't allocate frame buffer!\n", DEVNAME(sc)((sc)->sc_dev.dv_xname)); |
1532 | return (ENOMEM12); |
1533 | } |
1534 | |
1535 | DPRINTF(1, "%s: %s: allocated %d bytes frame buffer\n", |
1536 | DEVNAME(sc), __func__, fb->size); |
1537 | |
1538 | fb->chunks_done = 0; |
1539 | fb->fid = 0; |
1540 | fb->last_odd = 1; |
1541 | |
1542 | return (0); |
1543 | } |
1544 | |
1545 | void |
1546 | utvfu_vs_free_frame(struct utvfu_softc *sc) |
1547 | { |
1548 | struct utvfu_frame_buf *fb = &sc->sc_fb; |
1549 | |
1550 | if (fb->buf != NULL((void *)0)) { |
1551 | free(fb->buf, M_USBDEV102, fb->size); |
1552 | fb->buf = NULL((void *)0); |
1553 | } |
1554 | |
1555 | if (sc->sc_mmap_buffer != NULL((void *)0)) { |
1556 | free(sc->sc_mmap_buffer, M_USBDEV102, sc->sc_mmap_bufsz); |
1557 | sc->sc_mmap_buffer = NULL((void *)0); |
1558 | memset(sc->sc_mmap, 0, sizeof(sc->sc_mmap))__builtin_memset((sc->sc_mmap), (0), (sizeof(sc->sc_mmap ))); |
1559 | } |
1560 | |
1561 | while (!SIMPLEQ_EMPTY(&sc->sc_mmap_q)(((&sc->sc_mmap_q)->sqh_first) == ((void *)0))) |
1562 | 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); |
1563 | |
1564 | sc->sc_mmap_count = 0; |
1565 | } |
1566 | |
1567 | usbd_status |
1568 | utvfu_vs_alloc_isoc(struct utvfu_softc *sc) |
1569 | { |
1570 | int size, i; |
1571 | void *buf; |
1572 | |
1573 | DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); |
1574 | |
1575 | for (i = 0; i < UTVFU_ISOC_TRANSFERS3; i++) { |
1576 | sc->sc_iface.ixfer[i].sc = sc; |
1577 | sc->sc_iface.ixfer[i].xfer = usbd_alloc_xfer(sc->sc_udev); |
1578 | if (sc->sc_iface.ixfer[i].xfer == NULL((void *)0)) { |
1579 | printf("%s: could not allocate isoc VS xfer!\n", |
1580 | DEVNAME(sc)((sc)->sc_dev.dv_xname)); |
1581 | return (USBD_NOMEM); |
1582 | } |
1583 | |
1584 | size = sc->sc_iface.psize * sc->sc_nframes; |
1585 | |
1586 | buf = usbd_alloc_buffer(sc->sc_iface.ixfer[i].xfer, size); |
1587 | if (buf == NULL((void *)0)) { |
1588 | printf("%s: could not allocate isoc VS buffer!\n", |
1589 | DEVNAME(sc)((sc)->sc_dev.dv_xname)); |
1590 | return (USBD_NOMEM); |
1591 | } |
1592 | DPRINTF(1, "%s: allocated %d bytes isoc VS xfer buffer\n", |
1593 | DEVNAME(sc), size); |
1594 | } |
1595 | |
1596 | return (USBD_NORMAL_COMPLETION); |
1597 | } |
1598 | |
1599 | int |
1600 | utvfu_as_alloc_bulk(struct utvfu_softc *sc) |
1601 | { |
1602 | struct usbd_xfer *xfer; |
1603 | |
1604 | xfer = usbd_alloc_xfer(sc->sc_udev); |
1605 | if (xfer == NULL((void *)0)) { |
1606 | printf("%s: could not allocate bulk AUDIO xfer!\n", |
1607 | DEVNAME(sc)((sc)->sc_dev.dv_xname)); |
1608 | return (ENOMEM12); |
1609 | } |
1610 | |
1611 | if (usbd_alloc_buffer(xfer, UTVFU_AUDIO_URBSIZE20480) == NULL((void *)0)) { |
1612 | usbd_free_xfer(xfer); |
1613 | printf("%s: could not allocate bulk AUDIO buffer!\n", |
1614 | DEVNAME(sc)((sc)->sc_dev.dv_xname)); |
1615 | return (ENOMEM12); |
1616 | } |
1617 | DPRINTF(1, "%s: allocated %d bytes bulk AUDIO xfer buffer\n", |
1618 | DEVNAME(sc), UTVFU_AUDIO_URBSIZE); |
1619 | |
1620 | sc->sc_audio.iface.xfer = xfer; |
1621 | |
1622 | return (0); |
1623 | } |
1624 | |
1625 | void |
1626 | utvfu_vs_free_isoc(struct utvfu_softc *sc) |
1627 | { |
1628 | int i; |
1629 | |
1630 | DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); |
1631 | |
1632 | for (i = 0; i < UTVFU_ISOC_TRANSFERS3; i++) { |
1633 | if (sc->sc_iface.ixfer[i].xfer != NULL((void *)0)) { |
1634 | usbd_free_xfer(sc->sc_iface.ixfer[i].xfer); |
1635 | sc->sc_iface.ixfer[i].xfer = NULL((void *)0); |
1636 | } |
1637 | } |
1638 | } |
1639 | |
1640 | void |
1641 | utvfu_as_free_bulk(struct utvfu_softc *sc) |
1642 | { |
1643 | DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); |
1644 | |
1645 | if (sc->sc_audio.iface.xfer != NULL((void *)0)) { |
1646 | usbd_free_xfer(sc->sc_audio.iface.xfer); |
1647 | sc->sc_audio.iface.xfer = NULL((void *)0); |
1648 | } |
1649 | } |
1650 | |
1651 | int |
1652 | utvfu_reqbufs(void *v, struct v4l2_requestbuffers *rb) |
1653 | { |
1654 | struct utvfu_softc *sc = v; |
1655 | int i; |
1656 | |
1657 | DPRINTF(1, "%s: %s: count=%d\n", DEVNAME(sc), __func__, rb->count); |
1658 | |
1659 | /* We do not support freeing buffers via reqbufs(0) */ |
1660 | if (rb->count == 0) |
1661 | return (EINVAL22); |
1662 | |
1663 | if (sc->sc_mmap_count > 0 || sc->sc_mmap_buffer != NULL((void *)0)) { |
1664 | DPRINTF(1, "%s: %s: mmap buffers already allocated\n", |
1665 | DEVNAME(sc), __func__); |
1666 | return (EINVAL22); |
1667 | } |
1668 | |
1669 | /* limit the buffers */ |
1670 | if (rb->count > UTVFU_MAX_BUFFERS32) |
1671 | sc->sc_mmap_count = UTVFU_MAX_BUFFERS32; |
1672 | else |
1673 | sc->sc_mmap_count = rb->count; |
1674 | |
1675 | /* allocate the total mmap buffer */ |
1676 | sc->sc_mmap_bufsz = sc->sc_max_frame_sz; |
1677 | if (INT_MAX0x7fffffff / sc->sc_mmap_count < sc->sc_max_frame_sz) /* overflow */ |
1678 | return (ENOMEM12); |
1679 | sc->sc_mmap_bufsz *= sc->sc_mmap_count; |
1680 | sc->sc_mmap_bufsz = round_page(sc->sc_mmap_bufsz)(((sc->sc_mmap_bufsz) + ((1 << 12) - 1)) & ~((1 << 12) - 1)); /* page align */ |
1681 | sc->sc_mmap_buffer = malloc(sc->sc_mmap_bufsz, M_USBDEV102, M_NOWAIT0x0002); |
1682 | if (sc->sc_mmap_buffer == NULL((void *)0)) { |
1683 | printf("%s: can't allocate mmap buffer!\n", DEVNAME(sc)((sc)->sc_dev.dv_xname)); |
1684 | return (ENOMEM12); |
1685 | } |
1686 | DPRINTF(1, "%s: allocated %d bytes mmap buffer\n", |
1687 | DEVNAME(sc), sc->sc_mmap_bufsz); |
1688 | |
1689 | /* fill the v4l2_buffer structure */ |
1690 | for (i = 0; i < sc->sc_mmap_count; i++) { |
1691 | sc->sc_mmap[i].buf = sc->sc_mmap_buffer |
1692 | + (i * sc->sc_max_frame_sz); |
1693 | sc->sc_mmap[i].v4l2_buf.index = i; |
1694 | sc->sc_mmap[i].v4l2_buf.m.offset = i * sc->sc_max_frame_sz; |
1695 | sc->sc_mmap[i].v4l2_buf.length = sc->sc_max_frame_sz; |
1696 | sc->sc_mmap[i].v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
1697 | sc->sc_mmap[i].v4l2_buf.sequence = 0; |
1698 | sc->sc_mmap[i].v4l2_buf.field = V4L2_FIELD_NONE; |
1699 | sc->sc_mmap[i].v4l2_buf.memory = V4L2_MEMORY_MMAP; |
1700 | sc->sc_mmap[i].v4l2_buf.flags = V4L2_BUF_FLAG_MAPPED0x00000001; |
1701 | |
1702 | DPRINTF(1, "%s: %s: index=%d, offset=%d, length=%d\n", |
1703 | DEVNAME(sc), __func__, |
1704 | sc->sc_mmap[i].v4l2_buf.index, |
1705 | sc->sc_mmap[i].v4l2_buf.m.offset, |
1706 | sc->sc_mmap[i].v4l2_buf.length); |
1707 | } |
1708 | |
1709 | /* tell how many buffers we have really allocated */ |
1710 | rb->count = sc->sc_mmap_count; |
1711 | |
1712 | return (0); |
1713 | } |
1714 | |
1715 | int |
1716 | utvfu_querybuf(void *v, struct v4l2_buffer *qb) |
1717 | { |
1718 | struct utvfu_softc *sc = v; |
1719 | |
1720 | if (qb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || |
1721 | qb->memory != V4L2_MEMORY_MMAP || |
1722 | qb->index >= sc->sc_mmap_count) |
1723 | return (EINVAL22); |
1724 | |
1725 | memcpy(qb, &sc->sc_mmap[qb->index].v4l2_buf,__builtin_memcpy((qb), (&sc->sc_mmap[qb->index].v4l2_buf ), (sizeof(struct v4l2_buffer))) |
1726 | sizeof(struct v4l2_buffer))__builtin_memcpy((qb), (&sc->sc_mmap[qb->index].v4l2_buf ), (sizeof(struct v4l2_buffer))); |
1727 | |
1728 | DPRINTF(1, "%s: %s: index=%d, offset=%d, length=%d\n", |
1729 | DEVNAME(sc), __func__, qb->index, qb->m.offset, qb->length); |
1730 | |
1731 | return (0); |
1732 | } |
1733 | |
1734 | int |
1735 | utvfu_qbuf(void *v, struct v4l2_buffer *qb) |
1736 | { |
1737 | struct utvfu_softc *sc = v; |
1738 | |
1739 | if (qb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || |
1740 | qb->memory != V4L2_MEMORY_MMAP || |
1741 | qb->index >= sc->sc_mmap_count) |
1742 | return (EINVAL22); |
1743 | |
1744 | sc->sc_mmap[qb->index].v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE0x00000004; |
1745 | sc->sc_mmap[qb->index].v4l2_buf.flags |= V4L2_BUF_FLAG_MAPPED0x00000001; |
1746 | sc->sc_mmap[qb->index].v4l2_buf.flags |= V4L2_BUF_FLAG_QUEUED0x00000002; |
1747 | |
1748 | DPRINTF(2, "%s: %s: buffer on index %d ready for queueing\n", |
1749 | DEVNAME(sc), __func__, qb->index); |
1750 | |
1751 | return (0); |
1752 | } |
1753 | |
1754 | int |
1755 | utvfu_dqbuf(void *v, struct v4l2_buffer *dqb) |
1756 | { |
1757 | struct utvfu_softc *sc = v; |
1758 | struct utvfu_mmap *mmap; |
1759 | int error; |
1760 | |
1761 | if (dqb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || |
1762 | dqb->memory != V4L2_MEMORY_MMAP) |
1763 | return (EINVAL22); |
1764 | |
1765 | if (SIMPLEQ_EMPTY(&sc->sc_mmap_q)(((&sc->sc_mmap_q)->sqh_first) == ((void *)0))) { |
1766 | /* mmap queue is empty, block until first frame is queued */ |
1767 | error = tsleep_nsec(sc, 0, "vid_mmap", SEC_TO_NSEC(10)); |
1768 | if (error) |
1769 | return (EINVAL22); |
1770 | } |
1771 | |
1772 | mmap = SIMPLEQ_FIRST(&sc->sc_mmap_q)((&sc->sc_mmap_q)->sqh_first); |
1773 | if (mmap == NULL((void *)0)) |
1774 | panic("utvfu_dqbuf: NULL pointer!"); |
1775 | |
1776 | memcpy(dqb, &mmap->v4l2_buf, sizeof(struct v4l2_buffer))__builtin_memcpy((dqb), (&mmap->v4l2_buf), (sizeof(struct v4l2_buffer))); |
1777 | |
1778 | mmap->v4l2_buf.flags &= ~(V4L2_BUF_FLAG_DONE0x00000004|V4L2_BUF_FLAG_QUEUED0x00000002); |
1779 | mmap->v4l2_buf.flags |= V4L2_BUF_FLAG_MAPPED0x00000001; |
1780 | |
1781 | DPRINTF(2, "%s: %s: frame dequeued from index %d\n", |
1782 | DEVNAME(sc), __func__, mmap->v4l2_buf.index); |
1783 | 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); |
1784 | |
1785 | return (0); |
1786 | } |
1787 | |
1788 | int |
1789 | utvfu_streamon(void *v, int type) |
1790 | { |
1791 | struct utvfu_softc *sc = v; |
1792 | usbd_status error; |
1793 | |
1794 | /* open video stream pipe */ |
1795 | error = utvfu_vs_open(sc); |
1796 | if (error != USBD_NORMAL_COMPLETION) |
1797 | return (EINVAL22); |
1798 | |
1799 | utvfu_vs_start_isoc(sc); |
1800 | |
1801 | return (0); |
1802 | } |
1803 | |
1804 | int |
1805 | utvfu_streamoff(void *v, int type) |
1806 | { |
1807 | utvfu_vs_close(v); |
1808 | |
1809 | return (0); |
1810 | } |
1811 | |
1812 | int |
1813 | utvfu_queryctrl(void *v, struct v4l2_queryctrl *qctrl) |
1814 | { |
1815 | qctrl->flags = V4L2_CTRL_FLAG_DISABLED0x0001; |
1816 | |
1817 | return (0); |
1818 | } |
1819 | |
1820 | int |
1821 | utvfu_g_parm(void *v, struct v4l2_streamparm *parm) |
1822 | { |
1823 | struct utvfu_softc *sc = v; |
1824 | |
1825 | if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) |
1826 | return (EINVAL22); |
1827 | |
1828 | /* |
1829 | * XXX Unsure whether there is a way to negotiate this with the |
1830 | * device, but returning 0 will allow xenocara's video to run |
1831 | */ |
1832 | switch (utvfu_norm_params[sc->sc_normi].norm) { |
1833 | default: |
1834 | return (EINVAL22); |
1835 | 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)): |
1836 | parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME0x1000; |
1837 | parm->parm.capture.capturemode = 0; |
1838 | parm->parm.capture.timeperframe.numerator = 30; |
1839 | parm->parm.capture.timeperframe.denominator = 1; |
1840 | break; |
1841 | 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)): |
1842 | parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME0x1000; |
1843 | parm->parm.capture.capturemode = 0; |
1844 | parm->parm.capture.timeperframe.numerator = 25; |
1845 | parm->parm.capture.timeperframe.denominator = 1; |
1846 | break; |
1847 | } |
1848 | |
1849 | return (0); |
1850 | } |
1851 | |
1852 | int |
1853 | utvfu_s_parm(void *v, struct v4l2_streamparm *parm) |
1854 | { |
1855 | if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) |
1856 | return (EINVAL22); |
1857 | |
1858 | return (0); |
1859 | } |
1860 | |
1861 | /* |
1862 | * A U D I O O P S |
1863 | */ |
1864 | |
1865 | int |
1866 | utvfu_audio_open(void *v, int flags) |
1867 | { |
1868 | struct utvfu_softc *sc = v; |
1869 | |
1870 | if (usbd_is_dying(sc->sc_udev)) |
1871 | return (EIO5); |
1872 | |
1873 | if ((flags & FWRITE0x0002)) |
1874 | return (ENXIO6); |
1875 | |
1876 | if (ISSET(sc->sc_flags, UTVFU_FLAG_AS_RUNNING)((sc->sc_flags) & (0x02))) |
1877 | return (EBUSY16); |
1878 | |
1879 | return utvfu_as_init(sc); |
1880 | } |
1881 | |
1882 | void |
1883 | utvfu_audio_close(void *v) |
1884 | { |
1885 | struct utvfu_softc *sc = v; |
1886 | |
1887 | DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); |
1888 | |
1889 | utvfu_audio_stop(sc); |
1890 | utvfu_audio_clear_client(sc); |
1891 | } |
1892 | |
1893 | int |
1894 | utvfu_audio_set_params(void *v, int setmode, int usemode, |
1895 | struct audio_params *play, struct audio_params *rec) |
1896 | { |
1897 | struct utvfu_softc *sc = v; |
1898 | |
1899 | if (usbd_is_dying(sc->sc_udev)) |
1900 | return (EIO5); |
1901 | |
1902 | DPRINTF(1, "%s %s\n", DEVNAME(sc), __func__); |
1903 | |
1904 | /* XXX ? */ |
1905 | play->sample_rate = 0; |
1906 | play->encoding = AUDIO_ENCODING_NONE0; |
1907 | |
1908 | rec->sample_rate = 48000; |
1909 | rec->encoding = AUDIO_ENCODING_SLINEAR_LE6; |
1910 | rec->precision = 16; |
1911 | rec->bps = 2; |
1912 | rec->msb = 1; |
1913 | rec->channels = 2; |
1914 | |
1915 | return (0); |
1916 | } |
1917 | |
1918 | int |
1919 | utvfu_audio_halt_out(void *v) |
1920 | { |
1921 | return (EIO5); |
1922 | } |
1923 | |
1924 | int |
1925 | utvfu_audio_halt_in(void *v) |
1926 | { |
1927 | struct utvfu_softc *sc = v; |
1928 | |
1929 | if (usbd_is_dying(sc->sc_udev)) |
1930 | return (EIO5); |
1931 | |
1932 | DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); |
1933 | |
1934 | utvfu_audio_stop(sc); |
1935 | utvfu_audio_clear_client(sc); |
1936 | |
1937 | return (0); |
1938 | } |
1939 | |
1940 | int |
1941 | utvfu_audio_mixer_set_port(void *v, struct mixer_ctrl *cp) |
1942 | { |
1943 | struct utvfu_softc *sc = v; |
1944 | |
1945 | if (usbd_is_dying(sc->sc_udev)) |
1946 | return (EIO5); |
1947 | |
1948 | DPRINTF(1, "%s %s\n", DEVNAME(sc), __func__); |
1949 | |
1950 | if (cp->type != AUDIO_MIXER_ENUM1 || |
1951 | cp->un.ord < 0 || cp->un.ord > 1) |
1952 | return (EINVAL22); |
1953 | |
1954 | /* XXX TODO */ |
1955 | |
1956 | DPRINTF(1, "%s %s: cp->un.ord=%d\n", DEVNAME(sc), __func__, cp->un.ord); |
1957 | |
1958 | return (0); |
1959 | } |
1960 | |
1961 | int |
1962 | utvfu_audio_mixer_get_port(void *v, struct mixer_ctrl *cp) |
1963 | { |
1964 | struct utvfu_softc *sc = v; |
1965 | |
1966 | if (usbd_is_dying(sc->sc_udev)) |
1967 | return (EIO5); |
1968 | |
1969 | DPRINTF(1, "%s %s\n", DEVNAME(sc), __func__); |
1970 | |
1971 | if (cp->type != AUDIO_MIXER_ENUM1 || |
1972 | cp->un.ord < 0 || cp->un.ord > 1) |
1973 | return (EINVAL22); |
1974 | |
1975 | /* XXX TODO */ |
1976 | |
1977 | DPRINTF(1, "%s %s: cp->un.ord=%d\n", DEVNAME(sc), __func__, cp->un.ord); |
1978 | |
1979 | return (0); |
1980 | } |
1981 | |
1982 | int |
1983 | utvfu_audio_query_devinfo(void *v, struct mixer_devinfo *mi) |
1984 | { |
1985 | struct utvfu_softc *sc = v; |
1986 | |
1987 | if (usbd_is_dying(sc->sc_udev)) |
1988 | return (EIO5); |
1989 | |
1990 | DPRINTF(1, "%s %s\n", DEVNAME(sc), __func__); |
1991 | |
1992 | if (mi->index != 0) |
1993 | return (EINVAL22); |
1994 | |
1995 | /* XXX SOMEONE WITH AUDIO EXPERTIZE NEEDS TO HELP HERE */ |
1996 | strlcpy(mi->label.name, "mix0-i0", sizeof(mi->label.name)); |
1997 | mi->type = AUDIO_MIXER_ENUM1; |
1998 | mi->un.e.num_mem = 2; |
1999 | mi->un.e.member[0].ord = 0; |
2000 | strlcpy(mi->un.e.member[0].label.name, AudioNoff"off", |
2001 | sizeof(mi->un.e.member[0].label.name)); |
2002 | mi->un.e.member[1].ord = 1; |
2003 | strlcpy(mi->un.e.member[1].label.name, AudioNon"on", |
2004 | sizeof(mi->un.e.member[1].label.name)); |
2005 | |
2006 | return (0); |
2007 | } |
2008 | |
2009 | int |
2010 | utvfu_audio_get_props(void *v) |
2011 | { |
2012 | return (0); |
2013 | } |
2014 | |
2015 | int |
2016 | utvfu_audio_trigger_output(void *v, void *start, void *end, int blksize, |
2017 | void (*intr)(void *), void *arg, struct audio_params *param) |
2018 | { |
2019 | return (EIO5); |
2020 | } |
2021 | |
2022 | int |
2023 | utvfu_audio_trigger_input(void *v, void *start, void *end, int blksize, |
2024 | void (*intr)(void *), void *arg, struct audio_params *param) |
2025 | { |
2026 | struct utvfu_softc *sc = v; |
2027 | |
2028 | if (usbd_is_dying(sc->sc_udev)) |
2029 | return (EIO5); |
2030 | |
2031 | rw_enter_write(&sc->sc_audio.rwlock); |
2032 | |
2033 | sc->sc_audio.intr_arg = arg; |
2034 | sc->sc_audio.intr = intr; |
2035 | sc->sc_audio.start = start; |
2036 | sc->sc_audio.end = end; |
2037 | sc->sc_audio.cur = start; |
2038 | sc->sc_audio.blksize = blksize; |
2039 | |
2040 | rw_exit_write(&sc->sc_audio.rwlock); |
2041 | |
2042 | DPRINTF(1, "%s %s: start=%p end=%p diff=%lu blksize=%d\n", |
2043 | DEVNAME(sc), __func__, start, end, |
2044 | ((u_char *)end - (u_char *)start), blksize); |
2045 | |
2046 | return utvfu_audio_start(sc); |
2047 | } |
2048 | |
2049 | int |
2050 | utvfu_audio_start(struct utvfu_softc *sc) |
2051 | { |
2052 | DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); |
2053 | |
2054 | if (ISSET(sc->sc_flags, UTVFU_FLAG_AS_RUNNING)((sc->sc_flags) & (0x02))) |
2055 | return (0); |
2056 | |
2057 | utvfu_audio_start_chip(sc); |
2058 | |
2059 | if (utvfu_as_init(sc) != 0) |
2060 | return (ENOMEM12); |
2061 | if (sc->sc_audio.iface.pipeh == NULL((void *)0)) { |
2062 | if (utvfu_as_open(sc) != USBD_NORMAL_COMPLETION) |
2063 | return (ENOMEM12); |
2064 | } |
2065 | |
2066 | return utvfu_as_start_bulk(sc); |
2067 | } |
2068 | |
2069 | int |
2070 | utvfu_audio_stop(struct utvfu_softc *sc) |
2071 | { |
2072 | DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); |
2073 | |
2074 | utvfu_audio_stop_chip(sc); |
2075 | utvfu_as_free(sc); |
2076 | |
2077 | return (0); |
2078 | } |