Bug Summary

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'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name utvfu.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -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 -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 -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/lib/clang/13.0.0 -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/swsmu -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/powerplay -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/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 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 -D CONFIG_DRM_AMD_DC_DCN3_0 -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 -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 /usr/obj/sys/arch/amd64/compile/GENERIC.MP/scan-build/2022-01-12-131800-47421-1 -x c /usr/src/sys/dev/usb/utvfu.c
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
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_get_props(void *);
799int utvfu_audio_trigger_output(void *, void *, void *, int,
800 void (*)(void *), void *, struct audio_params *);
801int utvfu_audio_trigger_input(void *, void *, void *, int,
802 void (*)(void *), void *, struct audio_params *);
803
804struct cfdriver utvfu_cd = {
805 NULL((void *)0), "utvfu", DV_DULL
806};
807
808const struct cfattach utvfu_ca = {
809 sizeof(struct utvfu_softc),
810 utvfu_match,
811 utvfu_attach,
812 utvfu_detach,
813 NULL((void *)0)
814};
815
816struct 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
845struct 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
870int
871utvfu_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
930void
931utvfu_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
965int
966utvfu_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
988usbd_status
989utvfu_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
1040int
1041utvfu_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
1066int
1067utvfu_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
1079usbd_status
1080utvfu_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
1121usbd_status
1122utvfu_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
1162void
1163utvfu_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
1179void
1180utvfu_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
1198void
1199utvfu_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
1210int
1211utvfu_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
1230void
1231utvfu_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
1277void
1278utvfu_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
1286void
1287utvfu_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 */
1320void
1321utvfu_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
1358skip: /* setup new transfer */
1359 utvfu_vs_start_isoc_ixfer(sc, ixfer);
1360}
1361
1362int
1363utvfu_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
1378int
1379utvfu_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
1419caddr_t
1420utvfu_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
1435int
1436utvfu_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
1445int
1446utvfu_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
1464void
1465utvfu_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
1479void
1480utvfu_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
1488void
1489utvfu_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
1497int
1498utvfu_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
1509int
1510utvfu_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
1523int
1524utvfu_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
1545void
1546utvfu_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
1567usbd_status
1568utvfu_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
1599int
1600utvfu_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
1625void
1626utvfu_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
1640void
1641utvfu_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
1651int
1652utvfu_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
1715int
1716utvfu_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
1734int
1735utvfu_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
1754int
1755utvfu_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
1788int
1789utvfu_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
1804int
1805utvfu_streamoff(void *v, int type)
1806{
1807 utvfu_vs_close(v);
1808
1809 return (0);
1810}
1811
1812int
1813utvfu_queryctrl(void *v, struct v4l2_queryctrl *qctrl)
1814{
1815 qctrl->flags = V4L2_CTRL_FLAG_DISABLED0x0001;
1816
1817 return (0);
1818}
1819
1820int
1821utvfu_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
1852int
1853utvfu_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
1865int
1866utvfu_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
1882void
1883utvfu_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
1893int
1894utvfu_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
1918int
1919utvfu_audio_halt_out(void *v)
1920{
1921 return (EIO5);
1922}
1923
1924int
1925utvfu_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
1940int
1941utvfu_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
1961int
1962utvfu_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
1982int
1983utvfu_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
2009int
2010utvfu_audio_get_props(void *v)
2011{
2012 return (0);
2013}
2014
2015int
2016utvfu_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
2022int
2023utvfu_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
2049int
2050utvfu_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
2069int
2070utvfu_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}