Bug Summary

File:dev/usb/utvfu.c
Warning:line 985, column 8
Although the value stored to 'nep' is used in the enclosing expression, the value is never actually read from 'nep'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.4 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name utvfu.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model static -mframe-pointer=all -relaxed-aliasing -ffp-contract=on -fno-rounding-math -mconstructor-aliases -ffreestanding -mcmodel=kernel -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -target-feature -sse2 -target-feature -sse -target-feature -3dnow -target-feature -mmx -target-feature +save-args -target-feature +retpoline-external-thunk -disable-red-zone -no-implicit-float -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/sys/arch/amd64/compile/GENERIC.MP/obj -nostdsysteminc -nobuiltininc -resource-dir /usr/local/llvm16/lib/clang/16 -I /usr/src/sys -I /usr/src/sys/arch/amd64/compile/GENERIC.MP/obj -I /usr/src/sys/arch -I /usr/src/sys/dev/pci/drm/include -I /usr/src/sys/dev/pci/drm/include/uapi -I /usr/src/sys/dev/pci/drm/amd/include/asic_reg -I /usr/src/sys/dev/pci/drm/amd/include -I /usr/src/sys/dev/pci/drm/amd/amdgpu -I /usr/src/sys/dev/pci/drm/amd/display -I /usr/src/sys/dev/pci/drm/amd/display/include -I /usr/src/sys/dev/pci/drm/amd/display/dc -I /usr/src/sys/dev/pci/drm/amd/display/amdgpu_dm -I /usr/src/sys/dev/pci/drm/amd/pm/inc -I /usr/src/sys/dev/pci/drm/amd/pm/legacy-dpm -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu/inc -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu/smu11 -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu/smu12 -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu/smu13 -I /usr/src/sys/dev/pci/drm/amd/pm/powerplay/inc -I /usr/src/sys/dev/pci/drm/amd/pm/powerplay/hwmgr -I /usr/src/sys/dev/pci/drm/amd/pm/powerplay/smumgr -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu/inc -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu/inc/pmfw_if -I /usr/src/sys/dev/pci/drm/amd/display/dc/inc -I /usr/src/sys/dev/pci/drm/amd/display/dc/inc/hw -I /usr/src/sys/dev/pci/drm/amd/display/dc/clk_mgr -I /usr/src/sys/dev/pci/drm/amd/display/modules/inc -I /usr/src/sys/dev/pci/drm/amd/display/modules/hdcp -I /usr/src/sys/dev/pci/drm/amd/display/dmub/inc -I /usr/src/sys/dev/pci/drm/i915 -D DDB -D DIAGNOSTIC -D KTRACE -D ACCOUNTING -D KMEMSTATS -D PTRACE -D POOL_DEBUG -D CRYPTO -D SYSVMSG -D SYSVSEM -D SYSVSHM -D UVM_SWAP_ENCRYPT -D FFS -D FFS2 -D FFS_SOFTUPDATES -D UFS_DIRHASH -D QUOTA -D EXT2FS -D MFS -D NFSCLIENT -D NFSSERVER -D CD9660 -D UDF -D MSDOSFS -D FIFO -D FUSE -D SOCKET_SPLICE -D TCP_ECN -D TCP_SIGNATURE -D INET6 -D IPSEC -D PPP_BSDCOMP -D PPP_DEFLATE -D PIPEX -D MROUTING -D MPLS -D BOOT_CONFIG -D USER_PCICONF -D APERTURE -D MTRR -D NTFS -D SUSPEND -D HIBERNATE -D PCIVERBOSE -D USBVERBOSE -D WSDISPLAY_COMPAT_USL -D WSDISPLAY_COMPAT_RAWKBD -D WSDISPLAY_DEFAULTSCREENS=6 -D X86EMU -D ONEWIREVERBOSE -D MULTIPROCESSOR -D MAXUSERS=80 -D _KERNEL -O2 -Wno-pointer-sign -Wno-address-of-packed-member -Wno-constant-conversion -Wno-unused-but-set-variable -Wno-gnu-folding-constant -fdebug-compilation-dir=/usr/src/sys/arch/amd64/compile/GENERIC.MP/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fcf-protection=branch -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -o /home/ben/Projects/scan/2024-01-11-110808-61670-1 -x c /usr/src/sys/dev/usb/utvfu.c
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
73int 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
81struct 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
98int
99utvfu_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
124int
125utvfu_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
137int
138utvfu_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
160int
161utvfu_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
198int
199utvfu_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
250int
251utvfu_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 */
364void
365utvfu_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 */
389void
390utvfu_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
453int
454utvfu_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
483int
484utvfu_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
499int
500utvfu_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
521int
522utvfu_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
534int
535utvfu_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
553int
554utvfu_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
569int
570utvfu_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
578int
579utvfu_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
588int
589utvfu_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
600int
601utvfu_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
610int
611utvfu_s_input(void *v, int i)
612{
613 return utvfu_select_input(v, i);
614}
615
616/* A U D I O */
617
618void
619utvfu_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
662int
663utvfu_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
696int
697utvfu_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
737int utvfu_match(struct device *, void *, void *);
738void utvfu_attach(struct device *, struct device *, void *);
739int utvfu_detach(struct device *, int);
740
741usbd_status utvfu_parse_desc(struct utvfu_softc *);
742
743void utvfu_vs_close(struct utvfu_softc *);
744void utvfu_vs_free_frame(struct utvfu_softc *);
745void utvfu_vs_free_isoc(struct utvfu_softc *);
746void utvfu_vs_start_isoc_ixfer(struct utvfu_softc *,
747 struct utvfu_isoc_xfer *);
748void utvfu_vs_cb(struct usbd_xfer *, void *, usbd_status);
749
750void utvfu_vs_free(struct utvfu_softc *);
751int utvfu_vs_init(struct utvfu_softc *);
752int utvfu_vs_alloc_frame(struct utvfu_softc *);
753usbd_status utvfu_vs_alloc_isoc(struct utvfu_softc *);
754
755int utvfu_open(void *, int, int *, uint8_t *,
756 void (*)(void *), void *);
757int utvfu_close(void *);
758int utvfu_querycap(void *, struct v4l2_capability *);
759int utvfu_enum_fmt_vid_cap(void *, struct v4l2_fmtdesc *);
760int utvfu_enum_fsizes(void *, struct v4l2_frmsizeenum *);
761int utvfu_g_fmt(void *, struct v4l2_format *);
762int utvfu_s_fmt(void *, struct v4l2_format *);
763int utvfu_g_parm(void *, struct v4l2_streamparm *);
764int utvfu_s_parm(void *, struct v4l2_streamparm *);
765int utvfu_enum_input(void *, struct v4l2_input *);
766int utvfu_s_input(void *, int);
767int utvfu_g_input(void *, int *);
768
769int utvfu_reqbufs(void *, struct v4l2_requestbuffers *);
770int utvfu_querybuf(void *, struct v4l2_buffer *);
771int utvfu_qbuf(void *, struct v4l2_buffer *);
772int utvfu_dqbuf(void *, struct v4l2_buffer *);
773int utvfu_streamon(void *, int);
774int utvfu_streamoff(void *, int);
775int utvfu_queryctrl(void *, struct v4l2_queryctrl *);
776caddr_t utvfu_mappage(void *, off_t, int);
777int utvfu_get_bufsize(void *);
778int utvfu_start_read(void *);
779
780int utvfu_as_init(struct utvfu_softc *);
781void utvfu_as_free(struct utvfu_softc *);
782
783usbd_status utvfu_as_open(struct utvfu_softc *);
784int utvfu_as_alloc_bulk(struct utvfu_softc *);
785void utvfu_as_free_bulk(struct utvfu_softc *);
786int utvfu_as_start_bulk(struct utvfu_softc *);
787void utvfu_as_bulk_thread(void *);
788
789int utvfu_audio_open(void *, int);
790void utvfu_audio_close(void *);
791int utvfu_audio_set_params(void *, int, int,
792 struct audio_params *, struct audio_params *);
793int utvfu_audio_halt_out(void *);
794int utvfu_audio_halt_in(void *);
795int utvfu_audio_mixer_set_port(void *, struct mixer_ctrl *);
796int utvfu_audio_mixer_get_port(void *, struct mixer_ctrl *);
797int utvfu_audio_query_devinfo(void *, struct mixer_devinfo *);
798int utvfu_audio_trigger_output(void *, void *, void *, int,
799 void (*)(void *), void *, struct audio_params *);
800int utvfu_audio_trigger_input(void *, void *, void *, int,
801 void (*)(void *), void *, struct audio_params *);
802
803struct cfdriver utvfu_cd = {
804 NULL((void *)0), "utvfu", DV_DULL
805};
806
807const struct cfattach utvfu_ca = {
808 sizeof(struct utvfu_softc),
809 utvfu_match,
810 utvfu_attach,
811 utvfu_detach,
812 NULL((void *)0)
813};
814
815const 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
844const 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
857int
858utvfu_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
917void
918utvfu_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
952int
953utvfu_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
975usbd_status
976utvfu_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;
Although the value stored to 'nep' is used in the enclosing expression, the value is never actually read from 'nep'
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
1027int
1028utvfu_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
1053int
1054utvfu_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
1066usbd_status
1067utvfu_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
1108usbd_status
1109utvfu_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
1149void
1150utvfu_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
1166void
1167utvfu_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
1185void
1186utvfu_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
1197int
1198utvfu_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
1217void
1218utvfu_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
1264void
1265utvfu_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
1273void
1274utvfu_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 */
1307void
1308utvfu_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
1345skip: /* setup new transfer */
1346 utvfu_vs_start_isoc_ixfer(sc, ixfer);
1347}
1348
1349int
1350utvfu_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
1365int
1366utvfu_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
1406caddr_t
1407utvfu_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
1422int
1423utvfu_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
1432int
1433utvfu_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
1451void
1452utvfu_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
1466void
1467utvfu_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
1475void
1476utvfu_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
1484int
1485utvfu_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
1496int
1497utvfu_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
1510int
1511utvfu_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
1532void
1533utvfu_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
1554usbd_status
1555utvfu_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
1586int
1587utvfu_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
1612void
1613utvfu_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
1627void
1628utvfu_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
1638int
1639utvfu_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
1702int
1703utvfu_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
1721int
1722utvfu_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
1741int
1742utvfu_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
1775int
1776utvfu_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
1791int
1792utvfu_streamoff(void *v, int type)
1793{
1794 utvfu_vs_close(v);
1795
1796 return (0);
1797}
1798
1799int
1800utvfu_queryctrl(void *v, struct v4l2_queryctrl *qctrl)
1801{
1802 qctrl->flags = V4L2_CTRL_FLAG_DISABLED0x0001;
1803
1804 return (0);
1805}
1806
1807int
1808utvfu_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
1839int
1840utvfu_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
1852int
1853utvfu_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
1869void
1870utvfu_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
1880int
1881utvfu_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
1905int
1906utvfu_audio_halt_out(void *v)
1907{
1908 return (EIO5);
1909}
1910
1911int
1912utvfu_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
1927int
1928utvfu_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
1948int
1949utvfu_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
1969int
1970utvfu_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
1996int
1997utvfu_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
2003int
2004utvfu_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
2030int
2031utvfu_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
2050int
2051utvfu_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}