1 /*
2 # libv4l1 userspace v4l1 api emulation for v4l2 devices
3
4 # (C) 2008-2010 Hans de Goede <hdegoede@redhat.com>
5
6 # Based in part on the kernels v4l1 ioctl compatibility code which is:
7
8 # (C) 2003-2009 Bill Dirks <bill@thedirks.org>, et al.
9
10 # The code taken from the kernel has been relicensed from GPLv2+ to LGPLv2+
11 # with permission from the authors see libv4l1-kernelcode-license.txt
12
13 # This program is free software; you can redistribute it and/or modify
14 # it under the terms of the GNU Lesser General Public License as published by
15 # the Free Software Foundation; either version 2.1 of the License, or
16 # (at your option) any later version.
17 #
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU Lesser General Public License for more details.
22 #
23 # You should have received a copy of the GNU Lesser General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
26 */
27
28 /* MAKING CHANGES TO THIS FILE?? READ THIS FIRST!!!
29
30 Important note to people making changes to this file: All functions
31 (v4l1_close, v4l1_ioctl, etc.) are designed to function as their regular
32 counterpart when they get passed a fd that is not "registered" by libv4l1,
33 there are 2 reasons for this:
34 1) This allows us to get completely out of the way when dealing with non
35 capture only devices, or non v4l2 devices.
36 2) libv4l1 is the base of the v4l1compat.so wrapper lib, which is a .so
37 which can be LD_PRELOAD-ed and the overrules the libc's open/close/etc,
38 and when opening /dev/videoX or /dev/v4l/ calls v4l1_open. Because we
39 behave as the regular counterpart when the fd is not known (instead of
40 say throwing an error), v4l1compat.so can simply call the v4l1_ prefixed
41 function for all wrapped functions. This way the wrapper does not have
42 to keep track of which fd's are being handled by libv4l1, as libv4l1
43 already keeps track of this itself.
44
45 This also means that libv4l1 may not use any of the regular functions
46 it mimics, as for example open could be a symbol in v4l1compat.so, which
47 in turn will call v4l1_open, so therefor v4l1_open (for example) may not
48 use the regular open()!
49 */
50 #include <config.h>
51 #include <errno.h>
52 #include <stdarg.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <fcntl.h>
56 #include <string.h>
57 #include <unistd.h>
58 #include <sys/types.h>
59 #include <sys/mman.h>
60 #include "../libv4lconvert/libv4lsyscall-priv.h"
61 #if defined(__OpenBSD__)
62 #include <sys/videoio.h>
63 #else
64 #include <linux/videodev2.h>
65 #endif
66 #include <libv4l2.h>
67 #include "libv4l1.h"
68 #include "libv4l1-priv.h"
69
70 #define V4L1_SUPPORTS_ENUMINPUT 0x01
71 #define V4L1_SUPPORTS_ENUMSTD 0x02
72 #define V4L1_PIX_FMT_TOUCHED 0x04
73 #define V4L1_PIX_SIZE_TOUCHED 0x08
74
75 static pthread_mutex_t v4l1_open_mutex = PTHREAD_MUTEX_INITIALIZER;
76 static struct v4l1_dev_info devices[V4L1_MAX_DEVICES] = {
77 { .fd = -1 },
78 { .fd = -1 }, { .fd = -1 }, { .fd = -1 }, { .fd = -1 }, { .fd = -1 },
79 { .fd = -1 }, { .fd = -1 }, { .fd = -1 }, { .fd = -1 }, { .fd = -1 },
80 { .fd = -1 }, { .fd = -1 }, { .fd = -1 }, { .fd = -1 }, { .fd = -1 }
81 };
82 static int devices_used;
83
palette_to_pixelformat(unsigned int palette)84 static unsigned int palette_to_pixelformat(unsigned int palette)
85 {
86 switch (palette) {
87 case VIDEO_PALETTE_GREY:
88 return V4L2_PIX_FMT_GREY;
89 case VIDEO_PALETTE_RGB555:
90 return V4L2_PIX_FMT_RGB555;
91 case VIDEO_PALETTE_RGB565:
92 return V4L2_PIX_FMT_RGB565;
93 case VIDEO_PALETTE_RGB24:
94 /* Yes v4l1 RGB24 really is BGR24, this is a weirdness in the
95 V4L1 API. Which unfortunately some V4L1 apps get wrong.
96 If you have an app which has r and b swapped and end up here
97 fix the app, this is not a libv4l1 bug */
98 return V4L2_PIX_FMT_BGR24;
99 case VIDEO_PALETTE_RGB32:
100 return V4L2_PIX_FMT_BGR32;
101 case VIDEO_PALETTE_YUYV:
102 return V4L2_PIX_FMT_YUYV;
103 case VIDEO_PALETTE_YUV422:
104 return V4L2_PIX_FMT_YUYV;
105 case VIDEO_PALETTE_UYVY:
106 return V4L2_PIX_FMT_UYVY;
107 case VIDEO_PALETTE_YUV410P:
108 return V4L2_PIX_FMT_YUV410;
109 case VIDEO_PALETTE_YUV420:
110 case VIDEO_PALETTE_YUV420P:
111 return V4L2_PIX_FMT_YUV420;
112 case VIDEO_PALETTE_YUV411P:
113 return V4L2_PIX_FMT_YUV411P;
114 case VIDEO_PALETTE_YUV422P:
115 return V4L2_PIX_FMT_YUV422P;
116 }
117 return 0;
118 }
119
pixelformat_to_palette(unsigned int pixelformat)120 static unsigned int pixelformat_to_palette(unsigned int pixelformat)
121 {
122 switch (pixelformat) {
123 case V4L2_PIX_FMT_GREY:
124 return VIDEO_PALETTE_GREY;
125 case V4L2_PIX_FMT_RGB555:
126 return VIDEO_PALETTE_RGB555;
127 case V4L2_PIX_FMT_RGB565:
128 return VIDEO_PALETTE_RGB565;
129 case V4L2_PIX_FMT_BGR24:
130 /* See the comment in palette_to_pixelformat above */
131 return VIDEO_PALETTE_RGB24;
132 case V4L2_PIX_FMT_BGR32:
133 return VIDEO_PALETTE_RGB32;
134 case V4L2_PIX_FMT_YUYV:
135 return VIDEO_PALETTE_YUYV;
136 case V4L2_PIX_FMT_UYVY:
137 return VIDEO_PALETTE_UYVY;
138 case V4L2_PIX_FMT_YUV410:
139 case V4L2_PIX_FMT_YUV420:
140 return VIDEO_PALETTE_YUV420P;
141 case V4L2_PIX_FMT_YUV411P:
142 return VIDEO_PALETTE_YUV411P;
143 case V4L2_PIX_FMT_YUV422P:
144 return VIDEO_PALETTE_YUV422P;
145 }
146 return 0;
147 }
148
count_inputs(int fd)149 static int count_inputs(int fd)
150 {
151 struct v4l2_input input2;
152 int i;
153 for (i = 0;; i++) {
154 memset(&input2, 0, sizeof(input2));
155 input2.index = i;
156 if (0 != v4l2_ioctl(fd, VIDIOC_ENUMINPUT, &input2))
157 break;
158 }
159 return i;
160 }
161
v4l1_set_format(int index,unsigned int width,unsigned int height,int v4l1_pal,int width_height_may_differ)162 static int v4l1_set_format(int index, unsigned int width,
163 unsigned int height, int v4l1_pal, int width_height_may_differ)
164 {
165 int result;
166 unsigned int v4l2_pixfmt;
167 struct v4l2_format fmt2 = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE };
168
169 if (v4l1_pal != -1) {
170 v4l2_pixfmt = palette_to_pixelformat(v4l1_pal);
171 if (!v4l2_pixfmt) {
172 V4L1_LOG("Unknown v4l1 palette number: %d\n", v4l1_pal);
173 errno = EINVAL;
174 return -1;
175 }
176 } else {
177 v4l2_pixfmt = devices[index].v4l2_pixfmt;
178 v4l1_pal = devices[index].v4l1_pal;
179 }
180
181 /* Do we need to change the resolution / format ? */
182 if (width == devices[index].width && height == devices[index].height &&
183 v4l2_pixfmt == devices[index].v4l2_pixfmt) {
184 devices[index].v4l1_pal = v4l1_pal;
185 return 0;
186 }
187
188 /* Get current settings, apply our changes and try the new setting */
189 result = v4l2_ioctl(devices[index].fd, VIDIOC_G_FMT, &fmt2);
190 if (result) {
191 int saved_err = errno;
192
193 V4L1_LOG_ERR("getting pixformat: %s\n", strerror(errno));
194 errno = saved_err;
195 return result;
196 }
197
198 fmt2.fmt.pix.pixelformat = v4l2_pixfmt;
199 fmt2.fmt.pix.width = width;
200 fmt2.fmt.pix.height = height;
201 result = v4l2_ioctl(devices[index].fd, VIDIOC_TRY_FMT, &fmt2);
202 if (result) {
203 int saved_err = errno;
204
205 V4L1_LOG("error trying pixformat: %s\n", strerror(errno));
206 errno = saved_err;
207 return result;
208 }
209
210 /* Check if we get what we asked for */
211 if (fmt2.fmt.pix.pixelformat != v4l2_pixfmt || (!width_height_may_differ &&
212 (fmt2.fmt.pix.width != width || fmt2.fmt.pix.height != height))) {
213 V4L1_LOG("requested fmt, width, height combo not available\n");
214 errno = EINVAL;
215 return -1;
216 }
217
218 /* Maybe after the TRY_FMT things haven't changed after all ? */
219 if (fmt2.fmt.pix.width == devices[index].width &&
220 fmt2.fmt.pix.height == devices[index].height &&
221 fmt2.fmt.pix.pixelformat == devices[index].v4l2_pixfmt) {
222 devices[index].v4l1_pal = v4l1_pal;
223 return 0;
224 }
225
226 result = v4l2_ioctl(devices[index].fd, VIDIOC_S_FMT, &fmt2);
227 if (result) {
228 int saved_err = errno;
229
230 V4L1_LOG_ERR("setting pixformat: %s\n", strerror(errno));
231 errno = saved_err;
232 return result;
233 }
234
235 devices[index].width = fmt2.fmt.pix.width;
236 devices[index].height = fmt2.fmt.pix.height;
237 devices[index].v4l2_pixfmt = v4l2_pixfmt;
238 devices[index].v4l1_pal = v4l1_pal;
239 devices[index].depth = ((fmt2.fmt.pix.bytesperline << 3) +
240 (fmt2.fmt.pix.width - 1)) / fmt2.fmt.pix.width;
241
242 return result;
243 }
244
v4l1_find_min_and_max_size(int index,struct v4l2_format * fmt2)245 static void v4l1_find_min_and_max_size(int index, struct v4l2_format *fmt2)
246 {
247 int i;
248 struct v4l2_fmtdesc fmtdesc2 = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE };
249
250 devices[index].min_width = -1;
251 devices[index].min_height = -1;
252 devices[index].max_width = 0;
253 devices[index].max_height = 0;
254
255 for (i = 0; ; i++) {
256 fmtdesc2.index = i;
257
258 if (v4l2_ioctl(devices[index].fd, VIDIOC_ENUM_FMT, &fmtdesc2))
259 break;
260
261 fmt2->fmt.pix.pixelformat = fmtdesc2.pixelformat;
262 fmt2->fmt.pix.width = 48;
263 fmt2->fmt.pix.height = 32;
264
265 if (v4l2_ioctl(devices[index].fd, VIDIOC_TRY_FMT, fmt2) == 0) {
266 if (fmt2->fmt.pix.width < devices[index].min_width)
267 devices[index].min_width = fmt2->fmt.pix.width;
268 if (fmt2->fmt.pix.height < devices[index].min_height)
269 devices[index].min_height = fmt2->fmt.pix.height;
270 }
271
272 fmt2->fmt.pix.pixelformat = fmtdesc2.pixelformat;
273 fmt2->fmt.pix.width = 100000;
274 fmt2->fmt.pix.height = 100000;
275
276 if (v4l2_ioctl(devices[index].fd, VIDIOC_TRY_FMT, fmt2) == 0) {
277 if (fmt2->fmt.pix.width > devices[index].max_width)
278 devices[index].max_width = fmt2->fmt.pix.width;
279 if (fmt2->fmt.pix.height > devices[index].max_height)
280 devices[index].max_height = fmt2->fmt.pix.height;
281 }
282 }
283 }
284
285
v4l1_open(const char * file,int oflag,...)286 int v4l1_open(const char *file, int oflag, ...)
287 {
288 int index, fd;
289 char *lfname;
290 struct v4l2_capability cap2;
291 struct v4l2_format fmt2;
292 struct v4l2_input input2;
293 struct v4l2_standard standard2;
294 int v4l_device = 0;
295
296 /* check if we're opening a video4linux2 device */
297 if (!strncmp(file, "/dev/video", 10) || !strncmp(file, "/dev/v4l/", 9)) {
298 /* Some apps open the device read only, but we need rw rights as the
299 buffers *MUST* be mapped rw */
300 oflag = (oflag & ~O_ACCMODE) | O_RDWR;
301 v4l_device = 1;
302 }
303
304 /* original open code */
305 if (oflag & O_CREAT) {
306 va_list ap;
307 mode_t mode;
308
309 va_start(ap, oflag);
310 mode = va_arg(ap, PROMOTED_MODE_T);
311
312 fd = SYS_OPEN(file, oflag, mode);
313
314 va_end(ap);
315 } else {
316 fd = SYS_OPEN(file, oflag, 0);
317 }
318
319 /* end of original open code */
320
321 if (fd == -1 || !v4l_device)
322 return fd;
323
324 /* check that this is an v4l2 device, no need to emulate v4l1 on
325 a v4l1 device */
326 if (SYS_IOCTL(fd, VIDIOC_QUERYCAP, &cap2))
327 return fd;
328
329 /* If no log file was set by the app, see if one was specified through the
330 environment */
331 if (!v4l1_log_file) {
332 lfname = getenv("LIBV4L1_LOG_FILENAME");
333 if (lfname)
334 v4l1_log_file = fopen(lfname, "w");
335 }
336
337 /* redirect libv4l2 log messages to our logfile if no libv4l2 logfile is
338 specified */
339 if (!v4l2_log_file)
340 v4l2_log_file = v4l1_log_file;
341
342 /* Register with libv4l2, as we use that todo format conversion and read()
343 emulation for us */
344 if (v4l2_fd_open(fd, 0) == -1) {
345 int saved_err = errno;
346
347 SYS_CLOSE(fd);
348 errno = saved_err;
349 return -1;
350 }
351
352 /* Get initial width, height and pixelformat */
353 fmt2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
354 if (v4l2_ioctl(fd, VIDIOC_G_FMT, &fmt2)) {
355 int saved_err = errno;
356
357 SYS_CLOSE(fd);
358 errno = saved_err;
359 return -1;
360 }
361
362 /* So we have a device on which we can (and want to) emulate v4l1, register
363 it in our devices array */
364 pthread_mutex_lock(&v4l1_open_mutex);
365 for (index = 0; index < V4L1_MAX_DEVICES; index++)
366 if (devices[index].fd == -1) {
367 devices[index].fd = fd;
368 break;
369 }
370 pthread_mutex_unlock(&v4l1_open_mutex);
371
372 if (index == V4L1_MAX_DEVICES) {
373 V4L1_LOG_ERR("attempting to open more than %d video devices\n",
374 V4L1_MAX_DEVICES);
375 v4l2_close(fd);
376 errno = EBUSY;
377 return -1;
378 }
379
380 if (index >= devices_used)
381 devices_used = index + 1;
382
383 devices[index].flags = 0;
384 devices[index].open_count = 1;
385 devices[index].v4l1_frame_buf_map_count = 0;
386 devices[index].v4l1_frame_pointer = MAP_FAILED;
387 devices[index].width = fmt2.fmt.pix.width;
388 devices[index].height = fmt2.fmt.pix.height;
389 devices[index].v4l2_pixfmt = fmt2.fmt.pix.pixelformat;
390 devices[index].v4l1_pal = pixelformat_to_palette(fmt2.fmt.pix.pixelformat);
391 devices[index].depth = ((fmt2.fmt.pix.bytesperline << 3) +
392 (fmt2.fmt.pix.width - 1)) / fmt2.fmt.pix.width;
393
394 v4l1_find_min_and_max_size(index, &fmt2);
395
396 /* Check ENUM_INPUT and ENUM_STD support */
397 input2.index = 0;
398 if (v4l2_ioctl(fd, VIDIOC_ENUMINPUT, &input2) == 0)
399 devices[index].flags |= V4L1_SUPPORTS_ENUMINPUT;
400
401 standard2.index = 0;
402 if (v4l2_ioctl(fd, VIDIOC_ENUMSTD, &standard2) == 0)
403 devices[index].flags |= V4L1_SUPPORTS_ENUMSTD;
404
405 V4L1_LOG("open: %d\n", fd);
406
407 return fd;
408 }
409
410 /* Is this an fd for which we are emulating v4l1 ? */
v4l1_get_index(int fd)411 static int v4l1_get_index(int fd)
412 {
413 int index;
414
415 /* We never handle fd -1 */
416 if (fd == -1)
417 return -1;
418
419 for (index = 0; index < devices_used; index++)
420 if (devices[index].fd == fd)
421 break;
422
423 if (index == devices_used)
424 return -1;
425
426 return index;
427 }
428
v4l1_close(int fd)429 int v4l1_close(int fd)
430 {
431 int index, result;
432
433 index = v4l1_get_index(fd);
434 if (index == -1)
435 return SYS_CLOSE(fd);
436
437 /* Abuse stream_lock to stop 2 closes from racing and trying to free the
438 resources twice */
439 pthread_mutex_lock(&devices[index].stream_lock);
440 devices[index].open_count--;
441 result = devices[index].open_count != 0;
442 pthread_mutex_unlock(&devices[index].stream_lock);
443
444 if (result)
445 return v4l2_close(fd);
446
447 /* Free resources */
448 if (devices[index].v4l1_frame_pointer != MAP_FAILED) {
449 if (devices[index].v4l1_frame_buf_map_count)
450 V4L1_LOG("v4l1 capture buffer still mapped: %d times on close()\n",
451 devices[index].v4l1_frame_buf_map_count);
452 else
453 SYS_MUNMAP(devices[index].v4l1_frame_pointer,
454 V4L1_NO_FRAMES * V4L1_FRAME_BUF_SIZE);
455 devices[index].v4l1_frame_pointer = MAP_FAILED;
456 }
457
458 /* Remove the fd from our list of managed fds before closing it, because as
459 soon as we've done the actual close the fd maybe returned by an open in
460 another thread and we don't want to intercept calls to this new fd. */
461 devices[index].fd = -1;
462
463 result = v4l2_close(fd);
464
465 V4L1_LOG("close: %d\n", fd);
466
467 return result;
468 }
469
v4l1_dup(int fd)470 int v4l1_dup(int fd)
471 {
472 int index = v4l1_get_index(fd);
473
474 if (index == -1)
475 return syscall(SYS_dup, fd);
476
477 devices[index].open_count++;
478
479 return v4l2_dup(fd);
480 }
481
v4l1_ioctl(int fd,unsigned long int request,...)482 int v4l1_ioctl(int fd, unsigned long int request, ...)
483 {
484 void *arg;
485 va_list ap;
486 int result, index, saved_err, stream_locked = 0;
487
488 va_start(ap, request);
489 arg = va_arg(ap, void *);
490 va_end(ap);
491
492 index = v4l1_get_index(fd);
493 if (index == -1)
494 return SYS_IOCTL(fd, request, arg);
495
496 /* Appearantly the kernel and / or glibc ignore the 32 most significant bits
497 when long = 64 bits, and some applications pass an int holding the req to
498 ioctl, causing it to get sign extended, depending upon this behavior */
499 request = (unsigned int)request;
500
501 /* do we need to take the stream lock for this ioctl? */
502 switch (request) {
503 case VIDIOCSPICT:
504 case VIDIOCGPICT:
505 case VIDIOCSWIN:
506 case VIDIOCGWIN:
507 case VIDIOCGMBUF:
508 case VIDIOCMCAPTURE:
509 case VIDIOCSYNC:
510 case VIDIOC_S_FMT:
511 pthread_mutex_lock(&devices[index].stream_lock);
512 stream_locked = 1;
513 }
514
515 switch (request) {
516 case VIDIOCGCAP: {
517 struct video_capability *cap = arg;
518 struct v4l2_framebuffer fbuf = { 0, };
519 struct v4l2_capability cap2 = { { 0 }, };
520
521 result = v4l2_ioctl(fd, VIDIOC_QUERYCAP, &cap2);
522 if (result < 0)
523 break;
524
525 if (cap2.capabilities & V4L2_CAP_DEVICE_CAPS)
526 cap2.capabilities = cap2.device_caps;
527 if (cap2.capabilities & V4L2_CAP_VIDEO_OVERLAY) {
528 result = v4l2_ioctl(fd, VIDIOC_G_FBUF, &fbuf);
529 if (result < 0)
530 memset(&fbuf, 0, sizeof(fbuf));
531 result = 0;
532 }
533
534 memcpy(cap->name, cap2.card,
535 min(sizeof(cap->name), sizeof(cap2.card)));
536
537 cap->name[sizeof(cap->name) - 1] = 0;
538
539 if (cap2.capabilities & V4L2_CAP_VIDEO_CAPTURE)
540 cap->type |= VID_TYPE_CAPTURE;
541 if (cap2.capabilities & V4L2_CAP_TUNER)
542 cap->type |= VID_TYPE_TUNER;
543 if (cap2.capabilities & V4L2_CAP_VBI_CAPTURE)
544 cap->type |= VID_TYPE_TELETEXT;
545 if (cap2.capabilities & V4L2_CAP_VIDEO_OVERLAY)
546 cap->type |= VID_TYPE_OVERLAY;
547 if (fbuf.capability & V4L2_FBUF_CAP_LIST_CLIPPING)
548 cap->type |= VID_TYPE_CLIPPING;
549
550 cap->channels = count_inputs(fd);
551 cap->minwidth = devices[index].min_width;
552 cap->minheight = devices[index].min_height;
553 cap->maxwidth = devices[index].max_width;
554 cap->maxheight = devices[index].max_height;
555 break;
556 }
557
558 case VIDIOCSPICT: {
559 struct video_picture *pic = arg;
560
561 devices[index].flags |= V4L1_PIX_FMT_TOUCHED;
562
563 v4l2_set_control(fd, V4L2_CID_BRIGHTNESS, pic->brightness);
564 v4l2_set_control(fd, V4L2_CID_HUE, pic->hue);
565 v4l2_set_control(fd, V4L2_CID_CONTRAST, pic->contrast);
566 v4l2_set_control(fd, V4L2_CID_SATURATION, pic->colour);
567 v4l2_set_control(fd, V4L2_CID_WHITENESS, pic->whiteness);
568
569 result = v4l1_set_format(index, devices[index].width,
570 devices[index].height, pic->palette, 0);
571 break;
572 }
573
574 case VIDIOCGPICT: {
575 struct video_picture *pic = arg;
576 int i;
577
578 /* If our v4l2 pixformat has no corresponding v4l1 palette, and
579 the app has not touched the pixformat sofar, try setting a
580 palette which does (and which we emulate when necessary) so
581 that applications which just query the current format and
582 then take whatever they get will work */
583 if (!(devices[index].flags & V4L1_PIX_FMT_TOUCHED) &&
584 !pixelformat_to_palette(devices[index].v4l2_pixfmt))
585 v4l1_set_format(index, devices[index].width,
586 devices[index].height,
587 VIDEO_PALETTE_RGB24,
588 (devices[index].flags &
589 V4L1_PIX_SIZE_TOUCHED) ? 0 : 1);
590
591 devices[index].flags |= V4L1_PIX_FMT_TOUCHED;
592
593 memset(pic, 0, sizeof(*pic));
594 pic->depth = devices[index].depth;
595 pic->palette = devices[index].v4l1_pal;
596 i = v4l2_get_control(devices[index].fd, V4L2_CID_HUE);
597 if (i >= 0)
598 pic->hue = i;
599 i = v4l2_get_control(devices[index].fd, V4L2_CID_SATURATION);
600 if (i >= 0)
601 pic->colour = i;
602 i = v4l2_get_control(devices[index].fd, V4L2_CID_CONTRAST);
603 if (i >= 0)
604 pic->contrast = i;
605 i = v4l2_get_control(devices[index].fd, V4L2_CID_WHITENESS);
606 if (i >= 0)
607 pic->whiteness = i;
608 i = v4l2_get_control(devices[index].fd, V4L2_CID_BRIGHTNESS);
609 if (i >= 0)
610 pic->brightness = i;
611
612 result = 0;
613 break;
614 }
615
616 case VIDIOCSWIN:
617 case VIDIOCGWIN: {
618 struct video_window *win = arg;
619
620 devices[index].flags |= V4L1_PIX_SIZE_TOUCHED;
621
622 if (request == VIDIOCSWIN)
623 result = v4l1_set_format(index, win->width, win->height, -1, 1);
624 else
625 result = 0;
626
627 if (result == 0) {
628 win->x = 0;
629 win->y = 0;
630 win->width = devices[index].width;
631 win->height = devices[index].height;
632 win->flags = 0;
633 }
634 break;
635 }
636
637 case VIDIOCGCHAN: {
638 struct video_channel *chan = arg;
639
640 /* Set some defaults */
641 chan->tuners = 0;
642 chan->flags = 0;
643 chan->type = VIDEO_TYPE_CAMERA;
644 chan->norm = 0;
645
646 if (devices[index].flags & V4L1_SUPPORTS_ENUMINPUT) {
647 struct v4l2_input input2 = { .index = chan->channel };
648
649 result = v4l2_ioctl(fd, VIDIOC_ENUMINPUT, &input2);
650 if (result < 0)
651 break;
652
653 snprintf(chan->name, sizeof(chan->name), "%s",
654 (char *)input2.name);
655 if (input2.type == V4L2_INPUT_TYPE_TUNER) {
656 chan->tuners = 1;
657 chan->type = VIDEO_TYPE_TV;
658 chan->flags = VIDEO_VC_TUNER;
659 }
660 } else {
661 /* No ENUMINPUT support, fake it. */
662 if (chan->channel == 0) {
663 snprintf(chan->name, sizeof(chan->name),
664 "Camera");
665 result = 0;
666 } else {
667 errno = EINVAL;
668 result = -1;
669 break;
670 }
671 }
672
673 /* In case of no ENUMSTD support, ignore the norm member of the
674 channel struct */
675 if (devices[index].flags & V4L1_SUPPORTS_ENUMSTD) {
676 v4l2_std_id sid;
677
678 result = v4l2_ioctl(fd, VIDIOC_G_STD, &sid);
679 if (result < 0)
680 break;
681
682 if (sid & V4L2_STD_PAL)
683 chan->norm = VIDEO_MODE_PAL;
684 if (sid & V4L2_STD_NTSC)
685 chan->norm = VIDEO_MODE_NTSC;
686 if (sid & V4L2_STD_SECAM)
687 chan->norm = VIDEO_MODE_SECAM;
688 if (sid == V4L2_STD_ALL)
689 chan->norm = VIDEO_MODE_AUTO;
690 }
691 break;
692 }
693
694 case VIDIOCSCHAN: {
695 struct video_channel *chan = arg;
696
697 if (devices[index].flags & V4L1_SUPPORTS_ENUMINPUT) {
698 result = v4l2_ioctl(fd, VIDIOC_S_INPUT, &chan->channel);
699 if (result < 0)
700 break;
701 } else {
702 /* No ENUMINPUT support, assume a single input */
703 if (chan->channel != 0) {
704 errno = EINVAL;
705 result = -1;
706 break;
707 }
708 result = 0;
709 }
710
711 /* In case of no ENUMSTD support, ignore the norm member of the
712 channel struct */
713 if (devices[index].flags & V4L1_SUPPORTS_ENUMSTD) {
714 v4l2_std_id sid = 0;
715
716 switch (chan->norm) {
717 case VIDEO_MODE_PAL:
718 sid = V4L2_STD_PAL;
719 break;
720 case VIDEO_MODE_NTSC:
721 sid = V4L2_STD_NTSC;
722 break;
723 case VIDEO_MODE_SECAM:
724 sid = V4L2_STD_SECAM;
725 break;
726 case VIDEO_MODE_AUTO:
727 sid = V4L2_STD_ALL;
728 break;
729 }
730
731 if (sid)
732 result = v4l2_ioctl(fd, VIDIOC_S_STD, &sid);
733 }
734 break;
735 }
736
737 case VIDIOCGMBUF: {
738 /* When VIDIOCGMBUF is done, we don't necessarrily know the format the
739 application wants yet (with some apps this is passed for the first
740 time through VIDIOCMCAPTURE), so we just create an anonymous mapping
741 that should be large enough to hold any sort of frame. Note this only
742 takes virtual memory, and does not use memory until actually used. */
743 int i;
744 struct video_mbuf *mbuf = arg;
745
746 mbuf->size = V4L1_NO_FRAMES * V4L1_FRAME_BUF_SIZE;
747 mbuf->frames = V4L1_NO_FRAMES;
748 for (i = 0; i < mbuf->frames; i++)
749 mbuf->offsets[i] = i * V4L1_FRAME_BUF_SIZE;
750
751 if (devices[index].v4l1_frame_pointer == MAP_FAILED) {
752 devices[index].v4l1_frame_pointer = (void *)SYS_MMAP(NULL,
753 (size_t)mbuf->size,
754 PROT_READ | PROT_WRITE,
755 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
756 if (devices[index].v4l1_frame_pointer == MAP_FAILED) {
757 saved_err = errno;
758 V4L1_LOG_ERR("allocating v4l1 buffer: %s\n", strerror(errno));
759 errno = saved_err;
760 result = -1;
761 break;
762 }
763 V4L1_LOG("allocated v4l1 buffer @ %p\n",
764 devices[index].v4l1_frame_pointer);
765 }
766 result = 0;
767 break;
768 }
769
770 case VIDIOCMCAPTURE: {
771 struct video_mmap *map = arg;
772
773 devices[index].flags |= V4L1_PIX_FMT_TOUCHED |
774 V4L1_PIX_SIZE_TOUCHED;
775
776 result = v4l1_set_format(index, map->width, map->height,
777 map->format, 0);
778 break;
779 }
780
781 case VIDIOCSYNC: {
782 int *frame_index = arg;
783
784 if (devices[index].v4l1_frame_pointer == MAP_FAILED ||
785 *frame_index < 0 || *frame_index >= V4L1_NO_FRAMES) {
786 errno = EINVAL;
787 result = -1;
788 break;
789 }
790
791 result = v4l2_read(devices[index].fd,
792 devices[index].v4l1_frame_pointer +
793 *frame_index * V4L1_FRAME_BUF_SIZE,
794 V4L1_FRAME_BUF_SIZE);
795 result = (result > 0) ? 0 : result;
796 break;
797 }
798
799 /* We are passing through v4l2 calls to libv4l2 for applications which are
800 using v4l2 through libv4l1 (possible with the v4l1compat.so wrapper).
801
802 So the application could be calling VIDIOC_S_FMT, in this case update
803 our own bookkeeping of the cam's format. Note that this really only is
804 relevant if an application is mixing and matching v4l1 and v4l2 calls,
805 which is crazy, but better safe than sorry. */
806 case VIDIOC_S_FMT: {
807 struct v4l2_format *fmt2 = arg;
808
809 result = v4l2_ioctl(fd, request, arg);
810
811 if (result == 0 && fmt2->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
812 if (devices[index].v4l2_pixfmt != fmt2->fmt.pix.pixelformat) {
813 devices[index].v4l2_pixfmt = fmt2->fmt.pix.pixelformat;
814 devices[index].v4l1_pal =
815 pixelformat_to_palette(fmt2->fmt.pix.pixelformat);
816 }
817 devices[index].width = fmt2->fmt.pix.width;
818 devices[index].height = fmt2->fmt.pix.height;
819 }
820 break;
821 }
822
823 case VIDIOCGFBUF: {
824 struct video_buffer *buffer = arg;
825 struct v4l2_framebuffer fbuf = { 0, };
826
827 result = v4l2_ioctl(fd, VIDIOC_G_FBUF, &fbuf);
828 if (result < 0)
829 break;
830
831 buffer->base = fbuf.base;
832 buffer->height = fbuf.fmt.height;
833 buffer->width = fbuf.fmt.width;
834
835 switch (fbuf.fmt.pixelformat) {
836 case V4L2_PIX_FMT_RGB332:
837 buffer->depth = 8;
838 break;
839 case V4L2_PIX_FMT_RGB555:
840 buffer->depth = 15;
841 break;
842 case V4L2_PIX_FMT_RGB565:
843 buffer->depth = 16;
844 break;
845 case V4L2_PIX_FMT_BGR24:
846 buffer->depth = 24;
847 break;
848 case V4L2_PIX_FMT_BGR32:
849 buffer->depth = 32;
850 break;
851 default:
852 buffer->depth = 0;
853 }
854
855 if (fbuf.fmt.bytesperline) {
856 buffer->bytesperline = fbuf.fmt.bytesperline;
857 if (!buffer->depth && buffer->width)
858 buffer->depth = ((fbuf.fmt.bytesperline << 3)
859 + (buffer->width - 1))
860 / buffer->width;
861 } else {
862 buffer->bytesperline =
863 (buffer->width * buffer->depth + 7) & 7;
864 buffer->bytesperline >>= 3;
865 }
866 break;
867 }
868
869 case VIDIOCSFBUF: {
870 struct video_buffer *buffer = arg;
871 struct v4l2_framebuffer fbuf = { 0, };
872
873 fbuf.base = buffer->base;
874 fbuf.fmt.height = buffer->height;
875 fbuf.fmt.width = buffer->width;
876
877 switch (buffer->depth) {
878 case 8:
879 fbuf.fmt.pixelformat = V4L2_PIX_FMT_RGB332;
880 break;
881 case 15:
882 fbuf.fmt.pixelformat = V4L2_PIX_FMT_RGB555;
883 break;
884 case 16:
885 fbuf.fmt.pixelformat = V4L2_PIX_FMT_RGB565;
886 break;
887 case 24:
888 fbuf.fmt.pixelformat = V4L2_PIX_FMT_BGR24;
889 break;
890 case 32:
891 fbuf.fmt.pixelformat = V4L2_PIX_FMT_BGR32;
892 break;
893 }
894
895 fbuf.fmt.bytesperline = buffer->bytesperline;
896 result = v4l2_ioctl(fd, VIDIOC_S_FBUF, &fbuf);
897 break;
898 }
899
900 case VIDIOCSTUNER: {
901 struct video_tuner *tun = arg;
902 struct v4l2_tuner t = { 0, };
903
904 t.index = tun->tuner;
905 result = v4l2_ioctl(fd, VIDIOC_S_TUNER, &t);
906
907 break;
908 }
909
910 case VIDIOCGTUNER: {
911 int i;
912 struct video_tuner *tun = arg;
913 struct v4l2_tuner tun2 = { 0, };
914 struct v4l2_standard std2 = { 0, };
915 v4l2_std_id sid;
916
917 result = v4l2_ioctl(fd, VIDIOC_G_TUNER, &tun2);
918 if (result < 0)
919 break;
920
921 memcpy(tun->name, tun2.name,
922 min(sizeof(tun->name), sizeof(tun2.name)));
923 tun->name[sizeof(tun->name) - 1] = 0;
924 tun->rangelow = tun2.rangelow;
925 tun->rangehigh = tun2.rangehigh;
926 tun->flags = 0;
927 tun->mode = VIDEO_MODE_AUTO;
928
929 for (i = 0; i < 64; i++) {
930 std2.index = i;
931 if (0 != v4l2_ioctl(fd, VIDIOC_ENUMSTD, &std2))
932 break;
933 if (std2.id & V4L2_STD_PAL)
934 tun->flags |= VIDEO_TUNER_PAL;
935 if (std2.id & V4L2_STD_NTSC)
936 tun->flags |= VIDEO_TUNER_NTSC;
937 if (std2.id & V4L2_STD_SECAM)
938 tun->flags |= VIDEO_TUNER_SECAM;
939 }
940
941 if (v4l2_ioctl(fd, VIDIOC_G_STD, &sid) == 0) {
942 if (sid & V4L2_STD_PAL)
943 tun->mode = VIDEO_MODE_PAL;
944 if (sid & V4L2_STD_NTSC)
945 tun->mode = VIDEO_MODE_NTSC;
946 if (sid & V4L2_STD_SECAM)
947 tun->mode = VIDEO_MODE_SECAM;
948 }
949 if (tun2.capability & V4L2_TUNER_CAP_LOW)
950 tun->flags |= VIDEO_TUNER_LOW;
951 if (tun2.rxsubchans & V4L2_TUNER_SUB_STEREO)
952 tun->flags |= VIDEO_TUNER_STEREO_ON;
953 tun->signal = tun2.signal;
954
955 break;
956 }
957
958 case VIDIOCSFREQ: {
959 unsigned long *freq = arg;
960 struct v4l2_frequency freq2 = { 0, };
961
962 result = v4l2_ioctl(fd, VIDIOC_G_FREQUENCY, &freq2);
963 if (result < 0)
964 break;
965
966 freq2.frequency = *freq;
967
968 result = v4l2_ioctl(fd, VIDIOC_S_FREQUENCY, &freq2);
969
970 break;
971 }
972
973 case VIDIOCGFREQ: {
974 unsigned long *freq = arg;
975 struct v4l2_frequency freq2 = { 0, };
976
977 freq2.tuner = 0;
978 result = v4l2_ioctl(fd, VIDIOC_G_FREQUENCY, &freq2);
979 if (result < 0)
980 break;
981 if (0 == result)
982 *freq = freq2.frequency;
983
984 break;
985 }
986
987 case VIDIOCCAPTURE: {
988 int *on = arg;
989 enum v4l2_buf_type captype = V4L2_BUF_TYPE_VIDEO_CAPTURE;
990
991 if (0 == *on) {
992 /* dirty hack time. But v4l1 has no STREAMOFF
993 * equivalent in the API, and this one at
994 * least comes close ... */
995 v4l2_ioctl(fd, VIDIOC_STREAMOFF, &captype);
996 }
997
998 result = v4l2_ioctl(fd, VIDIOC_OVERLAY, on);
999
1000 break;
1001 }
1002
1003 case VIDIOCSAUDIO: {
1004 struct video_audio *aud = arg;
1005 struct v4l2_audio aud2 = { 0, };
1006 struct v4l2_tuner tun2 = { 0, };
1007
1008 aud2.index = aud->audio;
1009 result = v4l2_ioctl(fd, VIDIOC_S_AUDIO, &aud2);
1010 if (result < 0)
1011 break;
1012
1013 v4l2_set_control(fd, V4L2_CID_AUDIO_VOLUME,
1014 aud->volume);
1015 v4l2_set_control(fd, V4L2_CID_AUDIO_BASS,
1016 aud->bass);
1017 v4l2_set_control(fd, V4L2_CID_AUDIO_TREBLE,
1018 aud->treble);
1019 v4l2_set_control(fd, V4L2_CID_AUDIO_BALANCE,
1020 aud->balance);
1021 v4l2_set_control(fd, V4L2_CID_AUDIO_MUTE,
1022 !!(aud->flags & VIDEO_AUDIO_MUTE));
1023
1024 result = v4l2_ioctl(fd, VIDIOC_G_TUNER, &tun2);
1025 if (result == 0) {
1026 switch (aud->mode) {
1027 default:
1028 case VIDEO_SOUND_MONO:
1029 case VIDEO_SOUND_LANG1:
1030 tun2.audmode = V4L2_TUNER_MODE_MONO;
1031 break;
1032 case VIDEO_SOUND_STEREO:
1033 tun2.audmode = V4L2_TUNER_MODE_STEREO;
1034 break;
1035 case VIDEO_SOUND_LANG2:
1036 tun2.audmode = V4L2_TUNER_MODE_LANG2;
1037 break;
1038 }
1039 result = v4l2_ioctl(fd, VIDIOC_S_TUNER, &tun2);
1040 }
1041 /* Ignore errors modifying the tuner settings. */
1042 result = 0;
1043 break;
1044 }
1045
1046 case VIDIOCGAUDIO: {
1047 int i;
1048 struct video_audio *aud = arg;
1049 struct v4l2_queryctrl qctrl2;
1050 struct v4l2_audio aud2 = { 0, };
1051 struct v4l2_tuner tun2;
1052
1053 result = v4l2_ioctl(fd, VIDIOC_G_AUDIO, &aud2);
1054 if (result < 0)
1055 break;
1056
1057 memcpy(aud->name, aud2.name,
1058 min(sizeof(aud->name), sizeof(aud2.name)));
1059 aud->name[sizeof(aud->name) - 1] = 0;
1060 aud->audio = aud2.index;
1061 aud->flags = 0;
1062 i = v4l2_get_control(fd, V4L2_CID_AUDIO_VOLUME);
1063 if (i >= 0) {
1064 aud->volume = i;
1065 aud->flags |= VIDEO_AUDIO_VOLUME;
1066 }
1067 i = v4l2_get_control(fd, V4L2_CID_AUDIO_BASS);
1068 if (i >= 0) {
1069 aud->bass = i;
1070 aud->flags |= VIDEO_AUDIO_BASS;
1071 }
1072 i = v4l2_get_control(fd, V4L2_CID_AUDIO_TREBLE);
1073 if (i >= 0) {
1074 aud->treble = i;
1075 aud->flags |= VIDEO_AUDIO_TREBLE;
1076 }
1077 i = v4l2_get_control(fd, V4L2_CID_AUDIO_BALANCE);
1078 if (i >= 0) {
1079 aud->balance = i;
1080 aud->flags |= VIDEO_AUDIO_BALANCE;
1081 }
1082 i = v4l2_get_control(fd, V4L2_CID_AUDIO_MUTE);
1083 if (i >= 0) {
1084 if (i)
1085 aud->flags |= VIDEO_AUDIO_MUTE;
1086
1087 aud->flags |= VIDEO_AUDIO_MUTABLE;
1088 }
1089 aud->step = 1;
1090 qctrl2.id = V4L2_CID_AUDIO_VOLUME;
1091 if (v4l2_ioctl(fd, VIDIOC_QUERYCTRL, &qctrl2) == 0 &&
1092 !(qctrl2.flags & V4L2_CTRL_FLAG_DISABLED))
1093 aud->step = qctrl2.step;
1094 aud->mode = 0;
1095
1096 result = v4l2_ioctl(fd, VIDIOC_G_TUNER, &tun2);
1097 if (result < 0) {
1098 result = 0;
1099 break;
1100 }
1101
1102 if (tun2.rxsubchans & V4L2_TUNER_SUB_LANG2)
1103 aud->mode = VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
1104 else if (tun2.rxsubchans & V4L2_TUNER_SUB_STEREO)
1105 aud->mode = VIDEO_SOUND_STEREO;
1106 else if (tun2.rxsubchans & V4L2_TUNER_SUB_MONO)
1107 aud->mode = VIDEO_SOUND_MONO;
1108
1109 break;
1110 }
1111
1112 case VIDIOCSVBIFMT: {
1113 struct vbi_format *fmt = arg;
1114 struct v4l2_format fmt2;
1115
1116 if (VIDEO_PALETTE_RAW != fmt->sample_format) {
1117 result = -EINVAL;
1118 break;
1119 }
1120
1121 fmt2.type = V4L2_BUF_TYPE_VBI_CAPTURE;
1122 fmt2.fmt.vbi.samples_per_line = fmt->samples_per_line;
1123 fmt2.fmt.vbi.sampling_rate = fmt->sampling_rate;
1124 fmt2.fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
1125 fmt2.fmt.vbi.start[0] = fmt->start[0];
1126 fmt2.fmt.vbi.count[0] = fmt->count[0];
1127 fmt2.fmt.vbi.start[1] = fmt->start[1];
1128 fmt2.fmt.vbi.count[1] = fmt->count[1];
1129 fmt2.fmt.vbi.flags = fmt->flags;
1130
1131 result = v4l2_ioctl(fd, VIDIOC_TRY_FMT, fmt2);
1132 if (result < 0)
1133 break;
1134
1135 if (fmt2.fmt.vbi.samples_per_line != fmt->samples_per_line ||
1136 fmt2.fmt.vbi.sampling_rate != fmt->sampling_rate ||
1137 fmt2.fmt.vbi.sample_format != V4L2_PIX_FMT_GREY ||
1138 fmt2.fmt.vbi.start[0] != fmt->start[0] ||
1139 fmt2.fmt.vbi.count[0] != fmt->count[0] ||
1140 fmt2.fmt.vbi.start[1] != fmt->start[1] ||
1141 fmt2.fmt.vbi.count[1] != fmt->count[1] ||
1142 fmt2.fmt.vbi.flags != fmt->flags) {
1143 result = -EINVAL;
1144 break;
1145 }
1146 result = v4l2_ioctl(fd, VIDIOC_S_FMT, fmt2);
1147 break;
1148 }
1149
1150 case VIDIOCGVBIFMT: {
1151 struct vbi_format *fmt = arg;
1152 struct v4l2_format fmt2 = { 0, };
1153
1154 fmt2.type = V4L2_BUF_TYPE_VBI_CAPTURE;
1155 result = v4l2_ioctl(fd, VIDIOC_G_FMT, &fmt2);
1156
1157 if (result < 0)
1158 break;
1159
1160 if (fmt2.fmt.vbi.sample_format != V4L2_PIX_FMT_GREY) {
1161 result = -EINVAL;
1162 break;
1163 }
1164
1165 fmt->samples_per_line = fmt2.fmt.vbi.samples_per_line;
1166 fmt->sampling_rate = fmt2.fmt.vbi.sampling_rate;
1167 fmt->sample_format = VIDEO_PALETTE_RAW;
1168 fmt->start[0] = fmt2.fmt.vbi.start[0];
1169 fmt->count[0] = fmt2.fmt.vbi.count[0];
1170 fmt->start[1] = fmt2.fmt.vbi.start[1];
1171 fmt->count[1] = fmt2.fmt.vbi.count[1];
1172 fmt->flags = fmt2.fmt.vbi.flags & 0x03;
1173
1174 break;
1175 }
1176
1177 default:
1178 /* Pass through libv4l2 for applications which are using v4l2 through
1179 libv4l1 (this can happen with the v4l1compat.so wrapper preloaded */
1180 result = v4l2_ioctl(fd, request, arg);
1181 break;
1182 }
1183
1184 if (stream_locked)
1185 pthread_mutex_unlock(&devices[index].stream_lock);
1186
1187 saved_err = errno;
1188 v4l1_log_ioctl(request, arg, result);
1189 errno = saved_err;
1190
1191 return result;
1192 }
1193
1194
v4l1_read(int fd,void * buffer,size_t n)1195 ssize_t v4l1_read(int fd, void *buffer, size_t n)
1196 {
1197 int index = v4l1_get_index(fd);
1198 ssize_t result;
1199
1200 if (index == -1)
1201 return SYS_READ(fd, buffer, n);
1202
1203 pthread_mutex_lock(&devices[index].stream_lock);
1204 result = v4l2_read(fd, buffer, n);
1205 pthread_mutex_unlock(&devices[index].stream_lock);
1206
1207 return result;
1208 }
1209
1210
v4l1_mmap(void * start,size_t length,int prot,int flags,int fd,int64_t offset)1211 void *v4l1_mmap(void *start, size_t length, int prot, int flags, int fd,
1212 int64_t offset)
1213 {
1214 int index;
1215 void *result;
1216
1217 /* Check if the mmap data matches our answer to VIDIOCGMBUF, if not
1218 pass through libv4l2 for applications which are using v4l2 through
1219 libv4l1 (this can happen with the v4l1compat.so wrapper preloaded */
1220 index = v4l1_get_index(fd);
1221 if (index == -1 || start || offset ||
1222 length != (V4L1_NO_FRAMES * V4L1_FRAME_BUF_SIZE))
1223 return v4l2_mmap(start, length, prot, flags, fd, offset);
1224
1225
1226 pthread_mutex_lock(&devices[index].stream_lock);
1227
1228 /* It could be that we get called with an mmap which seems to match what
1229 we expect, but no VIDIOCGMBUF has been done yet, then it is certainly not
1230 for us so pass it through */
1231 if (devices[index].v4l1_frame_pointer == MAP_FAILED) {
1232 result = v4l2_mmap(start, length, prot, flags, fd, offset);
1233 goto leave;
1234 }
1235
1236 devices[index].v4l1_frame_buf_map_count++;
1237
1238 V4L1_LOG("v4l1 buffer @ %p mapped by application\n",
1239 devices[index].v4l1_frame_pointer);
1240
1241 result = devices[index].v4l1_frame_pointer;
1242
1243 leave:
1244 pthread_mutex_unlock(&devices[index].stream_lock);
1245
1246 return result;
1247 }
1248
v4l1_munmap(void * _start,size_t length)1249 int v4l1_munmap(void *_start, size_t length)
1250 {
1251 int index;
1252 unsigned char *start = _start;
1253
1254 /* Is this memory ours? */
1255 if (start != MAP_FAILED &&
1256 length == (V4L1_FRAME_BUF_SIZE * V4L1_NO_FRAMES)) {
1257 for (index = 0; index < devices_used; index++)
1258 if (devices[index].fd != -1 &&
1259 start == devices[index].v4l1_frame_pointer)
1260 break;
1261
1262 if (index != devices_used) {
1263 int unmapped = 0;
1264
1265 pthread_mutex_lock(&devices[index].stream_lock);
1266
1267 /* Redo our checks now that we have the lock, things may have changed */
1268 if (start == devices[index].v4l1_frame_pointer) {
1269 if (devices[index].v4l1_frame_buf_map_count > 0)
1270 devices[index].v4l1_frame_buf_map_count--;
1271
1272 unmapped = 1;
1273 }
1274
1275 pthread_mutex_unlock(&devices[index].stream_lock);
1276
1277 if (unmapped) {
1278 V4L1_LOG("v4l1 buffer munmap %p, %d\n", start, (int)length);
1279 return 0;
1280 }
1281 }
1282 }
1283
1284 V4L1_LOG("v4l1 unknown munmap %p, %d\n", start, (int)length);
1285
1286 /* If not pass through libv4l2 for applications which are using v4l2 through
1287 libv4l1 (this can happen with the v4l1compat.so wrapper preloaded */
1288 return v4l2_munmap(start, length);
1289 }
1290