1 /*------------------------------------------------------------------------
2  *  Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net>
3  *
4  *  This file is part of the ZBar Bar Code Reader.
5  *
6  *  The ZBar Bar Code Reader is free software; you can redistribute it
7  *  and/or modify it under the terms of the GNU Lesser Public License as
8  *  published by the Free Software Foundation; either version 2.1 of
9  *  the License, or (at your option) any later version.
10  *
11  *  The ZBar Bar Code Reader is distributed in the hope that it will be
12  *  useful, but WITHOUT ANY WARRANTY; without even the implied warranty
13  *  of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU Lesser Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser Public License
17  *  along with the ZBar Bar Code Reader; if not, write to the Free
18  *  Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  *  Boston, MA  02110-1301  USA
20  *
21  *  http://sourceforge.net/projects/zbar
22  *------------------------------------------------------------------------*/
23 
24 #include <config.h>
25 #ifdef HAVE_INTTYPES_H
26 # include <inttypes.h>
27 #endif
28 #ifdef HAVE_STDLIB_H
29 # include <stdlib.h>
30 #endif
31 #include <string.h>
32 #include <unistd.h>
33 #include <errno.h>
34 #include <assert.h>
35 #ifdef HAVE_SYS_IOCTL_H
36 # include <sys/ioctl.h>
37 #endif
38 #ifdef HAVE_SYS_MMAN_H
39 # include <sys/mman.h>
40 #endif
41 #ifdef HAVE_LIBV4L2_H
42 # include <fcntl.h>
43 # include <libv4l2.h>
44 #else
45 # define v4l2_close close
46 # define v4l2_ioctl ioctl
47 # define v4l2_mmap mmap
48 # define v4l2_munmap munmap
49 #endif
50 #include <linux/videodev2.h>
51 
52 #include "video.h"
53 #include "image.h"
54 
55 #define V4L2_FORMATS_MAX 64
56 #define V4L2_FORMATS_SIZE_MAX 256
57 
58 typedef struct video_controls_priv_s {
59     struct video_controls_s s;
60 
61     // Private fields
62     __u32 id;
63 } video_controls_priv_t;
64 
v4l2_nq(zbar_video_t * vdo,zbar_image_t * img)65 static int v4l2_nq (zbar_video_t *vdo,
66                     zbar_image_t *img)
67 {
68     if(vdo->iomode == VIDEO_READWRITE)
69         return(video_nq_image(vdo, img));
70 
71     if(video_unlock(vdo))
72         return(-1);
73 
74     struct v4l2_buffer vbuf;
75     memset(&vbuf, 0, sizeof(vbuf));
76     vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
77     if(vdo->iomode == VIDEO_MMAP) {
78         vbuf.memory = V4L2_MEMORY_MMAP;
79         vbuf.index = img->srcidx;
80     }
81     else {
82         vbuf.memory = V4L2_MEMORY_USERPTR;
83         vbuf.m.userptr = (unsigned long)img->data;
84         vbuf.length = img->datalen;
85         vbuf.index = img->srcidx; /* FIXME workaround broken drivers */
86     }
87     if(v4l2_ioctl(vdo->fd, VIDIOC_QBUF, &vbuf) < 0)
88         return(err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__,
89                            "queuing video buffer (VIDIOC_QBUF)"));
90     return(0);
91 }
92 
v4l2_dq(zbar_video_t * vdo)93 static zbar_image_t *v4l2_dq (zbar_video_t *vdo)
94 {
95     zbar_image_t *img;
96     int fd = vdo->fd;
97 
98     if(vdo->iomode != VIDEO_READWRITE) {
99         video_iomode_t iomode = vdo->iomode;
100         if(video_unlock(vdo))
101             return(NULL);
102 
103         struct v4l2_buffer vbuf;
104         memset(&vbuf, 0, sizeof(vbuf));
105         vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
106         if(iomode == VIDEO_MMAP)
107             vbuf.memory = V4L2_MEMORY_MMAP;
108         else
109             vbuf.memory = V4L2_MEMORY_USERPTR;
110 
111         if(v4l2_ioctl(fd, VIDIOC_DQBUF, &vbuf) < 0)
112             return(NULL);
113 
114         if(iomode == VIDEO_MMAP) {
115             assert(vbuf.index >= 0);
116             assert(vbuf.index < vdo->num_images);
117             img = vdo->images[vbuf.index];
118         }
119         else {
120             /* reverse map pointer back to image (FIXME) */
121             assert(vbuf.m.userptr >= (unsigned long)vdo->buf);
122             assert(vbuf.m.userptr < (unsigned long)(vdo->buf + vdo->buflen));
123             int i = (vbuf.m.userptr - (unsigned long)vdo->buf) / vdo->datalen;
124             assert(i >= 0);
125             assert(i < vdo->num_images);
126             img = vdo->images[i];
127             assert(vbuf.m.userptr == (unsigned long)img->data);
128         }
129     }
130     else {
131         img = video_dq_image(vdo);
132         if(!img)
133             return(NULL);
134 
135         /* FIXME should read entire image */
136         ssize_t datalen = read(fd, (void*)img->data, img->datalen);
137         if(datalen < 0) {
138             perror("v4l2_dq read");
139             return(NULL);
140         }
141         else if(datalen != img->datalen)
142             zprintf(0, "WARNING: read() size mismatch: 0x%lx != 0x%lx\n",
143                     datalen, img->datalen);
144     }
145     return(img);
146 }
147 
v4l2_start(zbar_video_t * vdo)148 static int v4l2_start (zbar_video_t *vdo)
149 {
150     if(vdo->iomode == VIDEO_READWRITE)
151         return(0);
152 
153     enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
154     if(v4l2_ioctl(vdo->fd, VIDIOC_STREAMON, &type) < 0)
155         return(err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__,
156                            "starting video stream (VIDIOC_STREAMON)"));
157     return(0);
158 }
159 
v4l2_stop(zbar_video_t * vdo)160 static int v4l2_stop (zbar_video_t *vdo)
161 {
162     if(vdo->iomode == VIDEO_READWRITE)
163         return(0);
164 
165     enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
166     if(v4l2_ioctl(vdo->fd, VIDIOC_STREAMOFF, &type) < 0)
167         return(err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__,
168                            "stopping video stream (VIDIOC_STREAMOFF)"));
169     return(0);
170 }
171 
v4l2_cleanup(zbar_video_t * vdo)172 static int v4l2_cleanup (zbar_video_t *vdo)
173 {
174     if(vdo->iomode == VIDEO_READWRITE)
175         return(0);
176 
177     struct v4l2_requestbuffers rb;
178     memset(&rb, 0, sizeof(rb));
179     rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
180     if(vdo->iomode == VIDEO_MMAP) {
181         rb.memory = V4L2_MEMORY_MMAP;
182         int i;
183         for(i = 0; i < vdo->num_images; i++) {
184             zbar_image_t *img = vdo->images[i];
185             if(img->data &&
186                v4l2_munmap((void*)img->data, img->datalen))
187                 err_capture(vdo, SEV_WARNING, ZBAR_ERR_SYSTEM, __func__,
188                             "unmapping video frame buffers");
189             img->data = NULL;
190             img->datalen = 0;
191         }
192     }
193     else
194         rb.memory = V4L2_MEMORY_USERPTR;
195 
196     /* requesting 0 buffers
197      * should implicitly disable streaming
198      */
199     if(v4l2_ioctl(vdo->fd, VIDIOC_REQBUFS, &rb) < 0)
200         err_capture(vdo, SEV_WARNING, ZBAR_ERR_SYSTEM, __func__,
201                     "releasing video frame buffers (VIDIOC_REQBUFS)");
202 
203 
204     /* v4l2_close v4l2_open device */
205     if(vdo->fd >= 0) {
206         v4l2_close(vdo->fd);
207         vdo->fd = -1;
208     }
209     return(0);
210 }
211 
v4l2_mmap_buffers(zbar_video_t * vdo)212 static int v4l2_mmap_buffers (zbar_video_t *vdo)
213 {
214 
215     struct v4l2_buffer vbuf;
216     memset(&vbuf, 0, sizeof(vbuf));
217     vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
218     vbuf.memory = V4L2_MEMORY_MMAP;
219 
220     int i;
221     for(i = 0; i < vdo->num_images; i++) {
222         vbuf.index = i;
223         if(v4l2_ioctl(vdo->fd, VIDIOC_QUERYBUF, &vbuf) < 0)
224             /* FIXME cleanup */
225             return(err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__,
226                                "querying video buffer (VIDIOC_QUERYBUF)"));
227 
228         if(vbuf.length < vdo->datalen)
229             fprintf(stderr, "WARNING: insufficient v4l2 video buffer size:\n"
230                     "\tvbuf[%d].length=%x datalen=%lx image=%d x %d %.4s(%08x)\n",
231                     i, vbuf.length, vdo->datalen, vdo->width, vdo->height,
232                     (char*)&vdo->format, vdo->format);
233 
234         zbar_image_t *img = vdo->images[i];
235         img->datalen = vbuf.length;
236         img->data = v4l2_mmap(NULL, vbuf.length, PROT_READ | PROT_WRITE, MAP_SHARED,
237                          vdo->fd, vbuf.m.offset);
238         if(img->data == MAP_FAILED)
239             /* FIXME cleanup */
240             return(err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__,
241                                "mapping video frame buffers"));
242         zprintf(2, "    buf[%d] 0x%lx bytes @%p\n",
243                 i, img->datalen, img->data);
244     }
245     return(0);
246 }
247 
v4l2_request_buffers(zbar_video_t * vdo,uint32_t num_images)248 static int v4l2_request_buffers (zbar_video_t *vdo, uint32_t num_images)
249 {
250     struct v4l2_requestbuffers rb;
251     memset(&rb, 0, sizeof(rb));
252     rb.count = num_images;
253     rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
254 
255     if(vdo->iomode == VIDEO_MMAP)
256         rb.memory = V4L2_MEMORY_MMAP;
257     else
258         rb.memory = V4L2_MEMORY_USERPTR;
259 
260     if(v4l2_ioctl(vdo->fd, VIDIOC_REQBUFS, &rb) < 0)
261         return(err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__,
262                            "requesting video frame buffers (VIDIOC_REQBUFS)"));
263     if(num_images && rb.count)
264         vdo->num_images = rb.count;
265     return(0);
266 }
267 
v4l2_set_format(zbar_video_t * vdo,uint32_t fmt)268 static int v4l2_set_format (zbar_video_t *vdo,
269                             uint32_t fmt)
270 {
271     struct v4l2_format vfmt;
272     struct v4l2_pix_format *vpix = &vfmt.fmt.pix;
273     memset(&vfmt, 0, sizeof(vfmt));
274     vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
275     vpix->width = vdo->width;
276     vpix->height = vdo->height;
277     vpix->pixelformat = fmt;
278     vpix->field = V4L2_FIELD_NONE;
279     int rc = 0;
280     if((rc = v4l2_ioctl(vdo->fd, VIDIOC_S_FMT, &vfmt)) < 0) {
281         /* several broken drivers return an error if we request
282          * no interlacing (NB v4l2 spec violation)
283          * ...try again with an interlaced request
284          */
285         zprintf(1, "VIDIOC_S_FMT returned %d(%d), trying interlaced...\n",
286                 rc, errno);
287 
288         /* FIXME this might be _ANY once we can de-interlace */
289         vpix->field = V4L2_FIELD_INTERLACED;
290 
291         if(v4l2_ioctl(vdo->fd, VIDIOC_S_FMT, &vfmt) < 0)
292             return(err_capture_int(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__,
293                                    "setting format %x (VIDIOC_S_FMT)", fmt));
294 
295         zprintf(0, "WARNING: broken driver returned error when non-interlaced"
296                 " format requested\n");
297     }
298 
299     struct v4l2_format newfmt;
300     struct v4l2_pix_format *newpix = &newfmt.fmt.pix;
301     memset(&newfmt, 0, sizeof(newfmt));
302     newfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
303     if(v4l2_ioctl(vdo->fd, VIDIOC_G_FMT, &newfmt) < 0)
304         return(err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__,
305                            "querying format (VIDIOC_G_FMT)"));
306 
307     if(newpix->field != V4L2_FIELD_NONE)
308         err_capture(vdo, SEV_WARNING, ZBAR_ERR_INVALID, __func__,
309                     "video driver only supports interlaced format,"
310                     " vertical scanning may not work");
311 
312     if(newpix->pixelformat != fmt
313        /* FIXME bpl/bpp checks? */)
314         return(err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__,
315                            "video driver can't provide compatible format"));
316 
317     vdo->format = fmt;
318     vdo->width = newpix->width;
319     vdo->height = newpix->height;
320     vdo->datalen = newpix->sizeimage;
321 
322     zprintf(1, "set new format: %.4s(%08x) %u x %u (0x%lx)\n",
323             (char*)&vdo->format, vdo->format, vdo->width, vdo->height,
324             vdo->datalen);
325     return(0);
326 }
327 
v4l2_init(zbar_video_t * vdo,uint32_t fmt)328 static int v4l2_init (zbar_video_t *vdo,
329                       uint32_t fmt)
330 {
331     struct v4l2_requestbuffers rb;
332     if(v4l2_set_format(vdo, fmt))
333         return(-1);
334 
335     memset(&rb, 0, sizeof(rb));
336     rb.count = vdo->num_images;
337     rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
338     if(vdo->iomode == VIDEO_MMAP)
339         rb.memory = V4L2_MEMORY_MMAP;
340     else
341         rb.memory = V4L2_MEMORY_USERPTR;
342 
343     if(v4l2_ioctl(vdo->fd, VIDIOC_REQBUFS, &rb) < 0)
344         return(err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__,
345                            "requesting video frame buffers (VIDIOC_REQBUFS)"));
346 
347     if(!rb.count)
348         return(err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__,
349                            "driver returned 0 buffers"));
350 
351     if(vdo->num_images > rb.count)
352         vdo->num_images = rb.count;
353 
354     zprintf(1, "using %u buffers (of %d requested)\n",
355             rb.count, vdo->num_images);
356 
357     if(vdo->iomode == VIDEO_MMAP)
358         return(v4l2_mmap_buffers(vdo));
359     if(vdo->iomode == VIDEO_USERPTR)
360         return(v4l2_request_buffers(vdo, vdo->num_images));
361     return(0);
362 }
363 
v4l2_probe_iomode(zbar_video_t * vdo)364 static int v4l2_probe_iomode (zbar_video_t *vdo)
365 {
366     struct v4l2_requestbuffers rb;
367     memset(&rb, 0, sizeof(rb));
368     rb.count = vdo->num_images;
369     rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
370     if(vdo->iomode == VIDEO_MMAP)
371         rb.memory = V4L2_MEMORY_MMAP;
372     else
373         rb.memory = V4L2_MEMORY_USERPTR;
374 
375     if(v4l2_ioctl(vdo->fd, VIDIOC_REQBUFS, &rb) < 0) {
376         if(vdo->iomode)
377             return(err_capture_int(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__,
378                                    "unsupported iomode requested (%d)",
379                                    vdo->iomode));
380         else if(errno != EINVAL)
381             return(err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__,
382                                "querying streaming mode (VIDIOC_REQBUFS)"));
383 #ifdef HAVE_SYS_MMAN_H
384 	err_capture(vdo, SEV_WARNING, ZBAR_ERR_SYSTEM, __func__,
385                                "USERPTR failed. Falling back to mmap");
386         vdo->iomode = VIDEO_MMAP;
387 #else
388 	return err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__,
389                            "Userptr not supported, and zbar was compiled without mmap support"));
390 #endif
391     }
392     else {
393         if(!vdo->iomode)
394             rb.memory = V4L2_MEMORY_USERPTR;
395 	/* Update the num_images with the max supported by the driver */
396         if(rb.count)
397             vdo->num_images = rb.count;
398 	else
399             err_capture(vdo, SEV_WARNING, ZBAR_ERR_SYSTEM, __func__,
400                         "Something is wrong: number of buffers returned by REQBUF is zero!");
401 
402         /* requesting 0 buffers
403          * This cleans up the buffers allocated previously on probe
404          */
405 	rb.count = 0;
406         if(v4l2_ioctl(vdo->fd, VIDIOC_REQBUFS, &rb) < 0)
407             err_capture(vdo, SEV_WARNING, ZBAR_ERR_SYSTEM, __func__,
408                         "releasing video frame buffers (VIDIOC_REQBUFS)");
409     }
410     return(0);
411 }
412 
413 static inline void v4l2_max_size (zbar_video_t *vdo, uint32_t pixfmt,
414                                   uint32_t *max_width,  uint32_t *max_height)
415 {
416     int mwidth = 0, mheight = 0, i;
417     struct v4l2_frmsizeenum frm;
418 
419     for(i = 0; i < V4L2_FORMATS_SIZE_MAX; i++) {
420         memset(&frm, 0, sizeof(frm));
421         frm.index = i;
422         frm.pixel_format = pixfmt;
423 
424         if(v4l2_ioctl(vdo->fd, VIDIOC_ENUM_FRAMESIZES, &frm))
425             break;
426 
427         switch (frm.type) {
428         case V4L2_FRMSIZE_TYPE_DISCRETE:
429             mwidth = frm.discrete.width;
430             mheight = frm.discrete.height;
431             break;
432         case V4L2_FRMSIZE_TYPE_CONTINUOUS:
433         case V4L2_FRMSIZE_TYPE_STEPWISE:
434             mwidth = frm.stepwise.max_width;
435             mheight = frm.stepwise.max_height;
436             break;
437         default:
438             continue;
439         }
440         if (mwidth > *max_width)
441             *max_width = mwidth;
442         if (mheight > *max_height)
443             *max_height = mheight;
444     }
445 }
446 
447 static inline int v4l2_probe_formats (zbar_video_t *vdo)
448 {
449     int n_formats = 0, n_emu_formats = 0;
450     uint32_t max_width = 0, max_height = 0;
451 
452     if(vdo->width && vdo->height)
453             zprintf(1, "Caller requested an specific size: %d x %d\n",
454                     vdo->width, vdo->height);
455 
456     zprintf(2, "enumerating supported formats:\n");
457     struct v4l2_fmtdesc desc;
458     memset(&desc, 0, sizeof(desc));
459     desc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
460 
461     for(desc.index = 0; desc.index < V4L2_FORMATS_MAX; desc.index++) {
462         if(v4l2_ioctl(vdo->fd, VIDIOC_ENUM_FMT, &desc) < 0)
463             break;
464         zprintf(2, "    [%d] %.4s : %s%s%s\n",
465                 desc.index, (char*)&desc.pixelformat, desc.description,
466                 (desc.flags & V4L2_FMT_FLAG_COMPRESSED) ? " COMPRESSED" : "",
467                 (desc.flags & V4L2_FMT_FLAG_EMULATED) ? " EMULATED" : "");
468         if (desc.flags & V4L2_FMT_FLAG_EMULATED) {
469             vdo->emu_formats = realloc(vdo->emu_formats,
470                                    (n_emu_formats + 2) * sizeof(uint32_t));
471             vdo->emu_formats[n_emu_formats++] = desc.pixelformat;
472         } else {
473             vdo->formats = realloc(vdo->formats,
474                                    (n_formats + 2) * sizeof(uint32_t));
475             vdo->formats[n_formats++] = desc.pixelformat;
476         }
477 
478         if(!vdo->width || !vdo->height)
479             v4l2_max_size(vdo, desc.pixelformat, &max_width, &max_height);
480     }
481 
482     if(!vdo->width || !vdo->height) {
483         zprintf(1, "Max supported size: %d x %d\n", max_width, max_height);
484         if (max_width && max_height) {
485             vdo->width = max_width;
486             vdo->height = max_height;
487         } else {
488             /* fallback to large size, driver reduces to max available */
489             vdo->width = 640 * 64;
490             vdo->height = 480 * 64;
491 
492         }
493     }
494 
495     if(!desc.index)
496         return(err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__,
497                            "enumerating video formats (VIDIOC_ENUM_FMT)"));
498     if(vdo->formats)
499        vdo->formats[n_formats] = 0;
500     if(vdo->emu_formats)
501        vdo->emu_formats[n_emu_formats] = 0;
502     if(!vdo->formats && vdo->emu_formats) {
503        /*
504         * If only emu formats are available, just move them to vdo->formats.
505         * This happens when libv4l detects that the only available fourcc
506         * formats are webcam proprietary formats or bayer formats.
507         */
508        vdo->formats = vdo->emu_formats;
509        vdo->emu_formats = NULL;
510     }
511 
512     zprintf(2, "Found %d formats and %d emulated formats.\n",
513             n_formats, n_emu_formats);
514 
515     struct v4l2_format fmt;
516     struct v4l2_pix_format *pix = &fmt.fmt.pix;
517     memset(&fmt, 0, sizeof(fmt));
518     fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
519     if(v4l2_ioctl(vdo->fd, VIDIOC_G_FMT, &fmt) < 0)
520         return(err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__,
521                            "querying current video format (VIDIO_G_FMT)"));
522 
523     zprintf(1, "current format: %.4s(%08x) %u x %u%s (line=0x%x size=0x%x)\n",
524             (char*)&pix->pixelformat, pix->pixelformat,
525             pix->width, pix->height,
526             (pix->field != V4L2_FIELD_NONE) ? " INTERLACED" : "",
527             pix->bytesperline, pix->sizeimage);
528 
529     vdo->format = pix->pixelformat;
530     vdo->datalen = pix->sizeimage;
531     if(pix->width == vdo->width && pix->height == vdo->height)
532         return(0);
533 
534     struct v4l2_format maxfmt;
535     struct v4l2_pix_format *maxpix = &maxfmt.fmt.pix;
536     memcpy(&maxfmt, &fmt, sizeof(maxfmt));
537     maxpix->width = vdo->width;
538     maxpix->height = vdo->height;
539 
540     zprintf(1, "setting requested size: %d x %d\n", vdo->width, vdo->height);
541     if(v4l2_ioctl(vdo->fd, VIDIOC_S_FMT, &maxfmt) < 0) {
542         zprintf(1, "set FAILED...trying to recover original format\n");
543         /* ignore errors (driver broken anyway) */
544         v4l2_ioctl(vdo->fd, VIDIOC_S_FMT, &fmt);
545     }
546 
547     memset(&fmt, 0, sizeof(fmt));
548     fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
549     if(v4l2_ioctl(vdo->fd, VIDIOC_G_FMT, &fmt) < 0)
550         return(err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__,
551                            "querying current video format (VIDIOC_G_FMT)"));
552 
553     zprintf(1, "final format: %.4s(%08x) %u x %u%s (line=0x%x size=0x%x)\n",
554             (char*)&pix->pixelformat, pix->pixelformat,
555             pix->width, pix->height,
556             (pix->field != V4L2_FIELD_NONE) ? " INTERLACED" : "",
557             pix->bytesperline, pix->sizeimage);
558 
559     vdo->width = pix->width;
560     vdo->height = pix->height;
561     vdo->datalen = pix->sizeimage;
562     return(0);
563 }
564 
565 static inline int v4l2_reset_crop (zbar_video_t *vdo)
566 {
567     /* check cropping */
568     struct v4l2_cropcap ccap;
569     memset(&ccap, 0, sizeof(ccap));
570     ccap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
571     if(v4l2_ioctl(vdo->fd, VIDIOC_CROPCAP, &ccap) < 0)
572         return(err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__,
573                            "querying crop support (VIDIOC_CROPCAP)"));
574 
575     zprintf(1, "crop bounds: %d x %d @ (%d, %d)\n",
576             ccap.bounds.width, ccap.bounds.height,
577             ccap.bounds.left, ccap.bounds.top);
578     zprintf(1, "current crop win: %d x %d @ (%d, %d) aspect %d / %d\n",
579             ccap.defrect.width, ccap.defrect.height,
580             ccap.defrect.left, ccap.defrect.top,
581             ccap.pixelaspect.numerator, ccap.pixelaspect.denominator);
582 
583 #if 0
584     // This logic causes the device to fallback to the current resolution
585     if(!vdo->width || !vdo->height) {
586         vdo->width = ccap.defrect.width;
587         vdo->height = ccap.defrect.height;
588     }
589 #endif
590 
591     /* reset crop parameters */
592     struct v4l2_crop crop;
593     memset(&crop, 0, sizeof(crop));
594     crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
595     crop.c = ccap.defrect;
596     if(v4l2_ioctl(vdo->fd, VIDIOC_S_CROP, &crop) < 0 && errno != EINVAL && errno != ENOTTY)
597         return(err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__,
598                            "setting default crop window (VIDIOC_S_CROP)"));
599     return(0);
600 }
601 
602 /** locate a control entry
603  */
604 static struct video_controls_priv_s *v4l2_g_control_def(zbar_video_t *vdo,
605                                                    const char *name)
606 {
607     struct video_controls_priv_s *p = (void *)vdo->controls;
608 
609     while (p) {
610         if (!strcasecmp(p->s.name, name))
611             break;
612         p = p->s.next;
613     }
614 
615     if (!p->s.name) {
616         zprintf(1, "Control not found: %s", name);
617         return NULL;
618     }
619 
620     return p;
621 }
622 
623 void v4l2_free_controls(zbar_video_t *vdo)
624 {
625     int i;
626 
627     if(vdo->controls) {
628         struct video_controls_s *p = vdo->controls;
629         while (p) {
630             free(p->name);
631             free(p->group);
632             if(p->menu) {
633                 for (i = 0; i < p->menu_size; i++)
634                     free(p->menu[i].name);
635                 free(p->menu);
636             }
637             p = p->next;
638         }
639         free(vdo->controls);
640     }
641     vdo->controls = NULL;
642 }
643 
644 #ifdef VIDIOC_QUERY_EXT_CTRL
645 static const char *v4l2_ctrl_type(uint32_t type)
646 {
647     switch(type) {
648     // All controls below are available since, at least, Kernel 2.6.31
649     case V4L2_CTRL_TYPE_INTEGER:
650         return "int";
651     case V4L2_CTRL_TYPE_BOOLEAN:
652         return "bool";
653     case V4L2_CTRL_TYPE_MENU:
654         return "menu";
655     case V4L2_CTRL_TYPE_BUTTON:
656         return "button";
657     case V4L2_CTRL_TYPE_INTEGER64:
658         return "int64";
659     case V4L2_CTRL_TYPE_CTRL_CLASS:
660         return "ctrl class";
661     case V4L2_CTRL_TYPE_STRING:
662         return "string";
663 #ifdef V4L2_CTRL_TYPE_INTEGER_MENU
664     case V4L2_CTRL_TYPE_INTEGER_MENU:
665         return "int menu";
666 #endif
667 #ifdef V4L2_CTRL_TYPE_U32
668     // Newer controls. All of them should be there since Kernel 3.16
669     case V4L2_CTRL_TYPE_BITMASK:
670         return "bitmask";
671     case V4L2_CTRL_TYPE_U8:
672         return "compound u8";
673     case V4L2_CTRL_TYPE_U16:
674         return "compound u16";
675     case V4L2_CTRL_TYPE_U32:
676         return "compound 32";
677 #endif
678     default:
679         return "unknown";
680     }
681 }
682 
683 static const char *v4l2_ctrl_class(uint32_t class)
684 {
685     switch(class) {
686     // All classes below are available since, at least, Kernel 2.6.31
687     case V4L2_CTRL_CLASS_USER:
688         return "User";
689     case V4L2_CTRL_CLASS_MPEG:
690         return "MPEG-compression";
691     case V4L2_CTRL_CLASS_CAMERA:
692         return "Camera";
693     case V4L2_CTRL_CLASS_FM_TX:
694         return "FM Modulator";
695 #ifdef V4L2_CTRL_CLASS_DETECT
696     // Newer classes added up to Kernel 3.16
697     case V4L2_CTRL_CLASS_FLASH:
698         return "Camera flash";
699     case V4L2_CTRL_CLASS_JPEG:
700         return "JPEG-compression";
701     case V4L2_CTRL_CLASS_IMAGE_SOURCE:
702         return "Image source";
703     case V4L2_CTRL_CLASS_IMAGE_PROC:
704         return "Image processing";
705     case V4L2_CTRL_CLASS_DV:
706         return "Digital Video";
707     case V4L2_CTRL_CLASS_FM_RX:
708         return "FM Receiver";
709     case V4L2_CTRL_CLASS_RF_TUNER:
710         return "RF tuner";
711     case V4L2_CTRL_CLASS_DETECT:
712         return "Detection";
713 #endif
714     default:
715         return "Unknown";
716     }
717 }
718 
719 // return values: 1: ignore, 0: added, -1: silently ignore
720 static int v4l2_add_control(zbar_video_t *vdo,
721                             struct v4l2_query_ext_ctrl *query,
722                             struct video_controls_priv_s **ptr)
723 {
724     // Control is disabled, ignore it. Please notice that disabled controls
725     // can be re-enabled. The right thing here would be to get those too,
726     // and add a logic to
727     if (query->flags & V4L2_CTRL_FLAG_DISABLED)
728         return 1;
729 
730     /* Silently ignore control classes */
731     if (query->type == V4L2_CTRL_TYPE_CTRL_CLASS)
732         return -1;
733 
734     // There's not much sense on displaying permanent read-only controls
735     if (query->flags & V4L2_CTRL_FLAG_READ_ONLY)
736         return 1;
737 
738     // Allocate a new element on the linked list
739     if (!vdo->controls) {
740         *ptr = calloc(1, sizeof(**ptr));
741         vdo->controls = (void *)*ptr;
742     } else {
743         (*ptr)->s.next = calloc(1, sizeof(**ptr));
744         *ptr = (*ptr)->s.next;
745     }
746 
747     // Fill control data
748     (*ptr)->id = query->id;
749     (*ptr)->s.name = strdup((const char *)query->name);
750     (*ptr)->s.group = strdup(v4l2_ctrl_class(V4L2_CTRL_ID2CLASS(query->id)));
751     switch (query->type) {
752     case V4L2_CTRL_TYPE_INTEGER:
753         (*ptr)->s.type = VIDEO_CNTL_INTEGER;
754         (*ptr)->s.min = query->minimum;
755         (*ptr)->s.max = query->maximum;
756         (*ptr)->s.def = query->default_value;
757         (*ptr)->s.step = query->step;
758         return(0);
759     case V4L2_CTRL_TYPE_INTEGER64:
760         (*ptr)->s.type = VIDEO_CNTL_INTEGER64;
761         (*ptr)->s.min = query->minimum;
762         (*ptr)->s.max = query->maximum;
763         (*ptr)->s.def = query->default_value;
764         (*ptr)->s.step = query->step;
765         return(0);
766     case V4L2_CTRL_TYPE_BOOLEAN:
767         (*ptr)->s.type = VIDEO_CNTL_BOOLEAN;
768         return(0);
769     case V4L2_CTRL_TYPE_BUTTON:
770         (*ptr)->s.type = VIDEO_CNTL_BUTTON;
771         return (0);
772     case V4L2_CTRL_TYPE_STRING:
773         (*ptr)->s.type = VIDEO_CNTL_STRING;
774         return (0);
775 #ifdef V4L2_CTRL_TYPE_INTEGER_MENU
776     case V4L2_CTRL_TYPE_INTEGER_MENU:
777 #endif
778     case V4L2_CTRL_TYPE_MENU: {
779         struct v4l2_querymenu menu;
780         struct video_control_menu_s *first = NULL, *p;
781         int n_menu = 0;
782 
783         memset(&menu, 0, sizeof(menu));
784         menu.id = query->id;
785 
786         for (menu.index = query->minimum;
787              menu.index <= query->maximum;
788              menu.index++) {
789             if (!ioctl(vdo->fd, VIDIOC_QUERYMENU, &menu)) {
790                 first = realloc(first, (n_menu + 1) * sizeof(*(*ptr)->s.menu));
791 
792                 p = &first[n_menu];
793                 p->value = menu.index;
794 
795 #ifdef V4L2_CTRL_TYPE_INTEGER_MENU
796                 if (query->type == V4L2_CTRL_TYPE_INTEGER_MENU)
797                     asprintf(p->name, "%i", menu.value);
798                 else
799 #endif /* V4L2_CTRL_TYPE_INTEGER_MENU */
800                     p->name = strdup((const char *)menu.name);
801 
802                 n_menu++;
803             }
804         }
805         (*ptr)->s.menu = first;
806         (*ptr)->s.menu_size = n_menu;
807         (*ptr)->s.min = query->minimum;
808         (*ptr)->s.max = query->maximum;
809         (*ptr)->s.def = query->default_value;
810         (*ptr)->s.type = VIDEO_CNTL_MENU;
811         return (0);
812         }
813     default:
814         return (1);
815     }
816 }
817 
818 static int v4l2_query_controls(zbar_video_t *vdo)
819 {
820     struct video_controls_priv_s *ptr = NULL;
821     struct v4l2_query_ext_ctrl query;
822     int ignore;
823     const char *old_class = NULL;
824 
825     // Free controls list if not NULL
826     v4l2_free_controls(vdo);
827 
828     memset(&query, 0, sizeof(query));
829     query.id = V4L2_CTRL_FLAG_NEXT_CTRL;
830     while (!v4l2_ioctl(vdo->fd, VIDIOC_QUERY_EXT_CTRL, &query)) {
831         ignore = v4l2_add_control(vdo, &query, &ptr);
832 
833         if (ignore >= 0 && _zbar_verbosity) {
834             int i;
835             const char *class = v4l2_ctrl_class(V4L2_CTRL_ID2CLASS(query.id));
836             if (class != old_class)
837                 zprintf(1, "Control class %s:\n", class);
838 
839             zprintf(1, "%-10s %-32s - 0x%x%s\n",
840                     v4l2_ctrl_type(query.type),
841                     query.name,
842                     query.id,
843                     ignore ? " - Ignored" : "");
844 
845             for (i = 0; i < ptr->s.menu_size; i++)
846                 zprintf(1, "           %" PRId64 ": %s\n",
847                         ptr->s.menu[i].value, ptr->s.menu[i].name);
848 
849             old_class = class;
850         }
851 
852         query.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
853     }
854 
855     return(0);
856 }
857 
858 static int v4l2_s_control(zbar_video_t *vdo,
859                           const char *name,
860                           void *value)
861 {
862     struct v4l2_ext_controls ctrls;
863     struct v4l2_ext_control c;
864     struct video_controls_priv_s *p;
865 
866     p = v4l2_g_control_def(vdo, name);
867     if (!p)
868         return ZBAR_ERR_UNSUPPORTED; // we have no such a control on the list
869 
870     memset(&ctrls, 0, sizeof(ctrls));
871     ctrls.count = 1;
872 #ifdef V4L2_CTRL_ID2WHICH
873     ctrls.which = V4L2_CTRL_ID2WHICH(p->id);
874 #else
875     ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id);
876 #endif
877     ctrls.controls = &c;
878 
879     memset(&c, 0, sizeof(c));
880     c.id = p->id;
881 
882     switch (p->s.type) {
883     case VIDEO_CNTL_INTEGER:
884     case VIDEO_CNTL_BOOLEAN:
885     case VIDEO_CNTL_BUTTON:
886     case VIDEO_CNTL_MENU:
887         c.value = *(int *)value;
888         break;
889 #if 0
890     //FIXME: Need to check callers with respect bufffer size
891     case VIDEO_CNTL_INTEGER64:
892         c.value64 = *(int64_t *)value;
893         break;
894 #endif
895     default:
896         return ZBAR_ERR_UNSUPPORTED;
897     }
898 
899     int rv = v4l2_ioctl(vdo->fd, VIDIOC_S_EXT_CTRLS, &ctrls);
900     if(rv) {
901         zprintf(1, "v4l2 set user control \"%s\" returned %d\n", p->s.name, rv);
902         rv = ZBAR_ERR_INVALID;
903     }
904     zprintf(1, "%-32s id: 0x%x set to value %d\n",
905             name, p->id, *(int*)value);
906 
907     return 0;
908 }
909 
910 static int v4l2_g_control(zbar_video_t *vdo,
911                             const char *name,
912                             void *value)
913 {
914     struct v4l2_ext_controls ctrls;
915     struct v4l2_ext_control c;
916     struct video_controls_priv_s *p;
917 
918     p = v4l2_g_control_def(vdo, name);
919     if (!p)
920         return ZBAR_ERR_UNSUPPORTED; // we have no such a control on the list
921 
922     memset(&ctrls, 0, sizeof(ctrls));
923     ctrls.count = 1;
924 #ifdef V4L2_CTRL_ID2WHICH
925     ctrls.which = V4L2_CTRL_ID2WHICH(p->id);
926 #else
927     ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id);
928 #endif
929     ctrls.controls = &c;
930 
931     memset(&c, 0, sizeof(c));
932     c.id = p->id;
933 
934     int rv = v4l2_ioctl(vdo->fd, VIDIOC_G_EXT_CTRLS, &ctrls);
935     if (rv) {
936         zprintf(1, "v4l2 get user control \"%s\" returned %d\n", p->s.name, rv);
937         return ZBAR_ERR_UNSUPPORTED;
938     }
939 
940     switch (p->s.type) {
941     case VIDEO_CNTL_INTEGER:
942     case VIDEO_CNTL_BOOLEAN:
943     case VIDEO_CNTL_BUTTON:
944     case VIDEO_CNTL_MENU:
945         *(int *)value = c.value;
946         zprintf(1, "v4l2 get user control \"%s\" = %d\n", p->s.name, c.value);
947         return(0);
948 #if 0
949     //FIXME: Need to check callers with respect bufffer size
950     case VIDEO_CNTL_INTEGER64:
951         *(int64_t *)value = c.value64;
952         return(0);
953 #endif
954     default:
955         return ZBAR_ERR_UNSUPPORTED;
956     }
957 }
958 
959 #else /* For very old Kernels < 3.16 (2014) */
960 static void v4l2_add_control(zbar_video_t *vdo,
961                             char *group_name,
962                             struct v4l2_queryctrl *query,
963                             struct video_controls_priv_s **ptr)
964 {
965     // Control is disabled, ignore it. Please notice that disabled controls
966     // can be re-enabled. The right thing here would be to get those too,
967     // and add a logic to
968     if (query->flags & V4L2_CTRL_FLAG_DISABLED)
969         return;
970 
971     // FIXME: add support also for other control types
972     if (!((query->type == V4L2_CTRL_TYPE_INTEGER) ||
973           (query->type == V4L2_CTRL_TYPE_BOOLEAN)))
974         return;
975 
976     zprintf(1, "%s %s ctrl %-32s id: 0x%x\n",
977             group_name,
978             (query->type == V4L2_CTRL_TYPE_INTEGER) ? "int " :
979                                                       "bool",
980             query->name,
981             query->id);
982 
983     // Allocate a new element on the linked list
984     if (!vdo->controls) {
985         *ptr = calloc(1, sizeof(**ptr));
986         vdo->controls = (void *)*ptr;
987     } else {
988         (*ptr)->s.next = calloc(1, sizeof(**ptr));
989         *ptr = (*ptr)->s.next;
990     }
991 
992     // Fill control data
993     (*ptr)->id = query->id;
994     (*ptr)->s.name = strdup((const char *)query->name);
995     if (query->type == V4L2_CTRL_TYPE_INTEGER) {
996         (*ptr)->s.type = VIDEO_CNTL_INTEGER;
997         (*ptr)->s.min = query->minimum;
998         (*ptr)->s.max = query->maximum;
999         (*ptr)->s.def = query->default_value;
1000         (*ptr)->s.step = query->step;
1001     } else {
1002         (*ptr)->s.type = VIDEO_CNTL_BOOLEAN;
1003     }
1004 }
1005 
1006 static int v4l2_query_controls(zbar_video_t *vdo)
1007 {
1008     int id = 0;
1009     struct video_controls_priv_s *ptr;
1010     struct v4l2_queryctrl query;
1011 
1012     // Free controls list if not NULL
1013     ptr = (void *)vdo->controls;
1014     while (ptr) {
1015         free(ptr->s.name);
1016         ptr = ptr->s.next;
1017     }
1018     free(vdo->controls);
1019     vdo->controls = NULL;
1020     ptr = NULL;
1021 
1022     id=0;
1023     do {
1024         query.id = id | V4L2_CTRL_FLAG_NEXT_CTRL;
1025         if(v4l2_ioctl(vdo->fd, VIDIOC_QUERYCTRL, &query))
1026             break;
1027 
1028         v4l2_add_control(vdo, "extended", &query, &ptr);
1029         id = query.id;
1030     } while (1);
1031 
1032     id=V4L2_CID_PRIVATE_BASE;
1033     do {
1034         query.id = id;
1035         if(v4l2_ioctl(vdo->fd, VIDIOC_QUERYCTRL, &query))
1036             break;
1037         v4l2_add_control(vdo, "private", &query, &ptr);
1038         id = query.id;
1039     } while (1);
1040 
1041     return(0);
1042 }
1043 
1044 static int v4l2_s_control(zbar_video_t *vdo,
1045                           const char *name,
1046                           void *value)
1047 {
1048     struct v4l2_control cs;
1049     struct video_controls_priv_s *p;
1050 
1051     p = v4l2_g_control_def(vdo, name);
1052     if (!p)
1053         return ZBAR_ERR_UNSUPPORTED; // we have no such a control on the list
1054 
1055     zprintf(1, "%-32s id: 0x%x set to value %d\n",
1056             name, p->id, *(int*)value);
1057 
1058     // FIXME: add support for VIDIOC_S_EXT_CTRL
1059     memset(&cs, 0, sizeof(cs));
1060     cs.id = p->id;
1061     cs.value = *(int*)value;
1062     int rv = v4l2_ioctl(vdo->fd, VIDIOC_S_CTRL, &cs);
1063     if(rv!=0) {
1064         zprintf(1, "v4l2 set user control \"%s\" returned %d\n", p->s.name, rv);
1065         rv = ZBAR_ERR_INVALID;
1066     }
1067     return rv;
1068 }
1069 
1070 static int v4l2_g_control(zbar_video_t *vdo,
1071                             const char *name,
1072                             void *value)
1073 {
1074     struct v4l2_control cs;
1075     struct video_controls_priv_s *p;
1076 
1077     p = v4l2_g_control_def(vdo, name);
1078     if (!p)
1079         return ZBAR_ERR_UNSUPPORTED; // we have no such a control on the list
1080 
1081     memset(&cs, 0, sizeof(cs));
1082 
1083     cs.id = p->id;
1084     cs.value = *(int*)value;
1085     int rv = v4l2_ioctl(vdo->fd, VIDIOC_G_CTRL, &cs);
1086     *(int*)value = cs.value;
1087     if(rv!=0) {
1088         zprintf(1, "v4l2 get user control \"%s\" returned %d\n", p->s.name, rv);
1089         rv = ZBAR_ERR_UNSUPPORTED;
1090     }
1091     return rv;
1092 }
1093 #endif /* VIDIOC_QUERY_EXT_CTRL */
1094 
1095 static int v4l2_sort_resolutions(const void *__a, const void *__b)
1096 {
1097     const struct video_resolution_s *a = __a;
1098     const struct video_resolution_s *b = __b;
1099     int r;
1100 
1101     r = (int)b->width - a->width;
1102     if (!r)
1103          r = (int)b->height - a->height;
1104 
1105     return r;
1106 }
1107 
1108 static float v4l2_get_max_fps_discrete(zbar_video_t *vdo,
1109                                        struct v4l2_frmsizeenum *frmsize)
1110 {
1111     struct v4l2_frmivalenum frmival = { 0 };
1112     float fps, max_fps = -1;
1113 
1114     frmival.width = frmsize->discrete.width;
1115     frmival.height = frmsize->discrete.height;
1116     frmival.pixel_format = frmsize->pixel_format;
1117     frmival.index = 0;
1118 
1119     for (frmival.index = 0;
1120          !v4l2_ioctl(vdo->fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival);
1121          frmival.index++) {
1122             fps = ((float)frmival.discrete.denominator)/frmival.discrete.numerator;
1123             if (fps > max_fps)
1124                 max_fps = fps;
1125     }
1126     return max_fps;
1127 }
1128 
1129 static void v4l2_insert_resolution(zbar_video_t *vdo,
1130                                    unsigned int *n_res,
1131                                    unsigned int width, unsigned int height,
1132                                    float max_fps)
1133 {
1134     unsigned int i;
1135 
1136     for (i = 0; i < *n_res; i++) {
1137         if (vdo->res[i].width == width && vdo->res[i].height == height)
1138         return;
1139     }
1140 
1141     vdo->res = realloc(vdo->res, (*n_res + 1) * sizeof(struct video_resolution_s));
1142 
1143     vdo->res[*n_res].width = width;
1144     vdo->res[*n_res].height = height;
1145     vdo->res[*n_res].max_fps = max_fps;
1146 
1147     (*n_res)++;
1148 }
1149 
1150 static int v4l2_get_supported_resolutions(zbar_video_t *vdo)
1151 {
1152     struct v4l2_fmtdesc fmt = { 0 };
1153     struct v4l2_frmsizeenum frmsize = { 0 };
1154     int i;
1155     unsigned int width, height, n_res = 0;
1156 
1157     vdo->res = NULL;
1158 
1159     fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1160     for (fmt.index = 0;
1161          !v4l2_ioctl(vdo->fd, VIDIOC_ENUM_FMT, &fmt);
1162          fmt.index++) {
1163         if (vdo->format != fmt.pixelformat)
1164             continue;
1165 
1166         frmsize.pixel_format = fmt.pixelformat;
1167         frmsize.index = 0;
1168 
1169         while (!v4l2_ioctl(vdo->fd, VIDIOC_ENUM_FRAMESIZES, &frmsize)) {
1170             if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
1171                     v4l2_insert_resolution(vdo, &n_res, frmsize.discrete.width,
1172                                            frmsize.discrete.height,
1173                                            v4l2_get_max_fps_discrete(vdo, &frmsize));
1174             } else if (frmsize.type == V4L2_FRMSIZE_TYPE_STEPWISE) {
1175                     for (i = 0; i <= 4; i++) {
1176                         width = frmsize.stepwise.min_width +
1177                             i * (frmsize.stepwise.max_width -
1178                                  frmsize.stepwise.min_width) / 4;
1179                         height = frmsize.stepwise.min_height +
1180                             i * (frmsize.stepwise.max_height -
1181                                  frmsize.stepwise.min_height) / 4;
1182                         v4l2_insert_resolution(vdo, &n_res, width, height, -1);
1183                     }
1184             }
1185             frmsize.index++;
1186         }
1187     }
1188     qsort(vdo->res, n_res, sizeof(struct video_resolution_s),
1189           v4l2_sort_resolutions);
1190 
1191     for (i = 0; i < n_res; i++) {
1192         zprintf(1, "%dx%d (%0.2f fps)\n",
1193                 vdo->res[i].width, vdo->res[i].height, vdo->res[i].max_fps);
1194     }
1195 
1196     /* Make the list zero-terminated */
1197     v4l2_insert_resolution(vdo, &n_res, 0, 0, 0);
1198 
1199     return 0;
1200 }
1201 
1202 int _zbar_v4l2_probe (zbar_video_t *vdo)
1203 {
1204     /* check capabilities */
1205     struct v4l2_capability vcap;
1206     memset(&vcap, 0, sizeof(vcap));
1207     if(v4l2_ioctl(vdo->fd, VIDIOC_QUERYCAP, &vcap) < 0)
1208         return(err_capture(vdo, SEV_WARNING, ZBAR_ERR_UNSUPPORTED, __func__,
1209                            "video4linux version 2 not supported (VIDIOC_QUERYCAP)"));
1210 
1211 
1212     zprintf(1, "%.32s on %.32s driver %.16s (version %u.%u.%u)\n", vcap.card,
1213             (vcap.bus_info[0]) ? (char*)vcap.bus_info : "<unknown>",
1214             vcap.driver, (vcap.version >> 16) & 0xff,
1215             (vcap.version >> 8) & 0xff, vcap.version & 0xff);
1216     zprintf(1, "    capabilities:%s%s%s%s\n",
1217             (vcap.device_caps & V4L2_CAP_VIDEO_CAPTURE) ? " CAPTURE" : "",
1218             (vcap.device_caps & V4L2_CAP_VIDEO_OVERLAY) ? " OVERLAY" : "",
1219             (vcap.device_caps & V4L2_CAP_READWRITE) ? " READWRITE" : "",
1220             (vcap.device_caps & V4L2_CAP_STREAMING) ? " STREAMING" : "");
1221 
1222     if(!(vcap.device_caps & V4L2_CAP_VIDEO_CAPTURE) ||
1223        !(vcap.device_caps & (V4L2_CAP_READWRITE | V4L2_CAP_STREAMING)))
1224         return(err_capture(vdo, SEV_WARNING, ZBAR_ERR_UNSUPPORTED, __func__,
1225                            "v4l2 device does not support usable CAPTURE"));
1226 
1227     if(v4l2_reset_crop(vdo))
1228         /* ignoring errors (driver cropping support questionable) */;
1229 
1230     if(v4l2_probe_formats(vdo))
1231         return(-1);
1232 
1233     if (v4l2_query_controls(vdo))
1234         return(-1);
1235 
1236     if (v4l2_get_supported_resolutions(vdo))
1237         return(-1);
1238 
1239     /* FIXME report error and fallback to readwrite? (if supported...) */
1240     if(vdo->iomode != VIDEO_READWRITE &&
1241        (vcap.device_caps & V4L2_CAP_STREAMING) &&
1242        v4l2_probe_iomode(vdo))
1243         return(-1);
1244     if(!vdo->iomode)
1245         vdo->iomode = VIDEO_READWRITE;
1246 
1247     zprintf(1, "using I/O mode: %s\n",
1248             (vdo->iomode == VIDEO_READWRITE) ? "READWRITE" :
1249             (vdo->iomode == VIDEO_MMAP) ? "MMAP" :
1250             (vdo->iomode == VIDEO_USERPTR) ? "USERPTR" : "<UNKNOWN>");
1251 
1252     vdo->intf = VIDEO_V4L2;
1253     vdo->init = v4l2_init;
1254     vdo->cleanup = v4l2_cleanup;
1255     vdo->start = v4l2_start;
1256     vdo->stop = v4l2_stop;
1257     vdo->nq = v4l2_nq;
1258     vdo->dq = v4l2_dq;
1259     vdo->set_control = v4l2_s_control;
1260     vdo->get_control = v4l2_g_control;
1261     vdo->free = v4l2_free_controls;
1262     return(0);
1263 }
1264