v4l2-compliance: cleanup and add tests for multiple opens.
authorHans Verkuil <hverkuil@xs4all.nl>
Sun, 2 May 2010 13:37:51 +0000 (15:37 +0200)
committerHans Verkuil <hverkuil@xs4all.nl>
Sun, 2 May 2010 13:37:51 +0000 (15:37 +0200)
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
utils/v4l2-compliance/Makefile
utils/v4l2-compliance/v4l2-compliance.cpp
utils/v4l2-compliance/v4l2-compliance.h [new file with mode: 0644]
utils/v4l2-compliance/v4l2-test-debug.cpp [new file with mode: 0644]

index 7a031c9..d85be49 100644 (file)
@@ -4,7 +4,7 @@ all: $(TARGETS)
 
 -include *.d
 
-v4l2-compliance: v4l2-compliance.o
+v4l2-compliance: v4l2-compliance.o v4l2-test-debug.o
        $(CXX) $(LDFLAGS) -o $@ $^
 
 install: $(TARGETS)
index 52b4e3a..46b9547 100644 (file)
@@ -1,7 +1,7 @@
 /*
     V4L2 API compliance test tool.
 
-    Copyright (C) 2008  Hans Verkuil <hverkuil@xs4all.nl>
+    Copyright (C) 2008, 2010  Hans Verkuil <hverkuil@xs4all.nl>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -19,7 +19,6 @@
  */
 
 #include <unistd.h>
-#include <features.h>          /* Uses _GNU_SOURCE to define getsubopt in stdlib.h */
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 #include <math.h>
 #include <sys/klog.h>
 
-#include <linux/videodev2.h>
-
-#include <list>
-#include <vector>
-#include <map>
-#include <string>
+#include "v4l2-compliance.h"
 
 /* Short option list
 
@@ -51,7 +45,8 @@
    case is used to retrieve a setting. */
 enum Option {
        OptSetDevice = 'd',
-       OptGetDriverInfo = 'D',
+       OptSetRadioDevice = 'r',
+       OptSetVbiDevice = 'V',
        OptHelp = 'h',
        OptTest = 't',
        OptVerbose = 'v',
@@ -71,54 +66,17 @@ static int test[TestMax];
 static char options[OptLast];
 
 static int app_result;
-static int verbose;
-
-static unsigned caps;
-
-typedef std::vector<struct v4l2_ext_control> ctrl_list;
-static ctrl_list user_ctrls;
-static ctrl_list mpeg_ctrls;
-
-typedef std::map<std::string, unsigned> ctrl_strmap;
-static ctrl_strmap ctrl_str2id;
-typedef std::map<unsigned, std::string> ctrl_idmap;
-static ctrl_idmap ctrl_id2str;
-
-typedef std::list<std::string> ctrl_get_list;
-static ctrl_get_list get_ctrls;
-
-typedef std::map<std::string,std::string> ctrl_set_map;
-static ctrl_set_map set_ctrls;
-
-typedef struct {
-       unsigned flag;
-       const char *str;
-} flag_def;
-
-static const flag_def service_def[] = {
-       { V4L2_SLICED_TELETEXT_B,  "teletext" },
-       { V4L2_SLICED_VPS,         "vps" },
-       { V4L2_SLICED_CAPTION_525, "cc" },
-       { V4L2_SLICED_WSS_625,     "wss" },
-       { 0, NULL }
-};
+static int tests_total, tests_ok;
 
-/* fmts specified */
-#define FmtWidth               (1L<<0)
-#define FmtHeight              (1L<<1)
-#define FmtChromaKey           (1L<<2)
-#define FmtGlobalAlpha         (1L<<3)
-
-/* crop specified */
-#define CropWidth              (1L<<0)
-#define CropHeight             (1L<<1)
-#define CropLeft               (1L<<2)
-#define CropTop                (1L<<3)
+// Globals
+int verbose;
+unsigned caps;
 
 static struct option long_options[] = {
        {"device", required_argument, 0, OptSetDevice},
+       {"radio-device", required_argument, 0, OptSetRadioDevice},
+       {"vbi-device", required_argument, 0, OptSetVbiDevice},
        {"help", no_argument, 0, OptHelp},
-       {"info", no_argument, 0, OptGetDriverInfo},
        {"verbose", no_argument, 0, OptVerbose},
        {"test", required_argument, 0, OptTest},
        {0, 0, 0, 0}
@@ -129,8 +87,12 @@ static void usage(void)
        printf("Usage:\n");
        printf("Common options:\n");
        printf("  -D, --info         show driver info [VIDIOC_QUERYCAP]\n");
-       printf("  -d, --device=<dev> use device <dev> instead of /dev/video0\n");
+       printf("  -d, --device=<dev> use device <dev> as the video device\n");
        printf("                     if <dev> is a single digit, then /dev/video<dev> is used\n");
+       printf("  -r, --radio-device=<dev> use device <dev> as the radio device\n");
+       printf("                     if <dev> is a single digit, then /dev/radio<dev> is used\n");
+       printf("  -V, --vbi-device=<dev> use device <dev> as the vbi device\n");
+       printf("                     if <dev> is a single digit, then /dev/vbi<dev> is used\n");
        printf("  -h, --help         display this help message\n");
        printf("  -t, --test=<num>   run specified test.\n");
        printf("                     By default all tests are run.\n");
@@ -139,519 +101,25 @@ static void usage(void)
        exit(0);
 }
 
-static std::string num2s(unsigned num)
-{
-       char buf[10];
-
-       sprintf(buf, "%08x", num);
-       return buf;
-}
-
-static std::string buftype2s(int type)
-{
-       switch (type) {
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-               return "Video Capture";
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-               return "Video Output";
-       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-               return "Video Overlay";
-       case V4L2_BUF_TYPE_VBI_CAPTURE:
-               return "VBI Capture";
-       case V4L2_BUF_TYPE_VBI_OUTPUT:
-               return "VBI Output";
-       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
-               return "Sliced VBI Capture";
-       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
-               return "Sliced VBI Output";
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
-               return "Video Output Overlay";
-       case V4L2_BUF_TYPE_PRIVATE:
-               return "Private";
-       default:
-               return "Unknown (" + num2s(type) + ")";
-       }
-}
-
-static std::string fcc2s(unsigned int val)
-{
-       std::string s;
-
-       s += val & 0xff;
-       s += (val >> 8) & 0xff;
-       s += (val >> 16) & 0xff;
-       s += (val >> 24) & 0xff;
-       return s;
-}
-
-static std::string field2s(int val)
-{
-       switch (val) {
-       case V4L2_FIELD_ANY:
-               return "Any";
-       case V4L2_FIELD_NONE:
-               return "None";
-       case V4L2_FIELD_TOP:
-               return "Top";
-       case V4L2_FIELD_BOTTOM:
-               return "Bottom";
-       case V4L2_FIELD_INTERLACED:
-               return "Interlaced";
-       case V4L2_FIELD_SEQ_TB:
-               return "Sequential Top-Bottom";
-       case V4L2_FIELD_SEQ_BT:
-               return "Sequential Bottom-Top";
-       case V4L2_FIELD_ALTERNATE:
-               return "Alternating";
-       case V4L2_FIELD_INTERLACED_TB:
-               return "Interlaced Top-Bottom";
-       case V4L2_FIELD_INTERLACED_BT:
-               return "Interlaced Bottom-Top";
-       default:
-               return "Unknown (" + num2s(val) + ")";
-       }
-}
-
-static std::string colorspace2s(int val)
-{
-       switch (val) {
-       case V4L2_COLORSPACE_SMPTE170M:
-               return "Broadcast NTSC/PAL (SMPTE170M/ITU601)";
-       case V4L2_COLORSPACE_SMPTE240M:
-               return "1125-Line (US) HDTV (SMPTE240M)";
-       case V4L2_COLORSPACE_REC709:
-               return "HDTV and modern devices (ITU709)";
-       case V4L2_COLORSPACE_BT878:
-               return "Broken Bt878";
-       case V4L2_COLORSPACE_470_SYSTEM_M:
-               return "NTSC/M (ITU470/ITU601)";
-       case V4L2_COLORSPACE_470_SYSTEM_BG:
-               return "PAL/SECAM BG (ITU470/ITU601)";
-       case V4L2_COLORSPACE_JPEG:
-               return "JPEG (JFIF/ITU601)";
-       case V4L2_COLORSPACE_SRGB:
-               return "SRGB";
-       default:
-               return "Unknown (" + num2s(val) + ")";
-       }
-}
-
-static std::string flags2s(unsigned val, const flag_def *def)
-{
-       std::string s;
-
-       while (def->flag) {
-               if (val & def->flag) {
-                       if (s.length()) s += " ";
-                       s += def->str;
-               }
-               def++;
-       }
-       return s;
-}
-
-static void print_sliced_vbi_cap(struct v4l2_sliced_vbi_cap &cap)
-{
-       printf("\tType           : %s\n", buftype2s(cap.type).c_str());
-       printf("\tService Set    : %s\n",
-                       flags2s(cap.service_set, service_def).c_str());
-       for (int i = 0; i < 24; i++) {
-               printf("\tService Line %2d: %8s / %-8s\n", i,
-                               flags2s(cap.service_lines[0][i], service_def).c_str(),
-                               flags2s(cap.service_lines[1][i], service_def).c_str());
-       }
-}
-
-static std::string name2var(unsigned char *name)
-{
-       std::string s;
-
-       while (*name) {
-               if (*name == ' ') s += "_";
-               else s += std::string(1, tolower(*name));
-               name++;
-       }
-       return s;
-}
-
-static void print_qctrl(int fd, struct v4l2_queryctrl *queryctrl,
-               struct v4l2_ext_control *ctrl, int show_menus)
+int doioctl(int fd, unsigned long int request, void *parm, const char *name)
 {
-       struct v4l2_querymenu qmenu = { 0 };
-       std::string s = name2var(queryctrl->name);
-       int i;
-
-       qmenu.id = queryctrl->id;
-       switch (queryctrl->type) {
-       case V4L2_CTRL_TYPE_INTEGER:
-               printf("%31s (int)  : min=%d max=%d step=%d default=%d value=%d",
-                               s.c_str(),
-                               queryctrl->minimum, queryctrl->maximum,
-                               queryctrl->step, queryctrl->default_value,
-                               ctrl->value);
-               break;
-       case V4L2_CTRL_TYPE_INTEGER64:
-               printf("%31s (int64): value=%lld", s.c_str(), ctrl->value64);
-               break;
-       case V4L2_CTRL_TYPE_BOOLEAN:
-               printf("%31s (bool) : default=%d value=%d",
-                               s.c_str(),
-                               queryctrl->default_value, ctrl->value);
-               break;
-       case V4L2_CTRL_TYPE_MENU:
-               printf("%31s (menu) : min=%d max=%d default=%d value=%d",
-                               s.c_str(),
-                               queryctrl->minimum, queryctrl->maximum,
-                               queryctrl->default_value, ctrl->value);
-               break;
-       case V4L2_CTRL_TYPE_BUTTON:
-               printf("%31s (button)\n", s.c_str());
-               break;
-       default: break;
-       }
-       if (queryctrl->flags) {
-               const flag_def def[] = {
-                       { V4L2_CTRL_FLAG_GRABBED,   "grabbed" },
-                       { V4L2_CTRL_FLAG_READ_ONLY, "readonly" },
-                       { V4L2_CTRL_FLAG_UPDATE,    "update" },
-                       { V4L2_CTRL_FLAG_INACTIVE,  "inactive" },
-                       { V4L2_CTRL_FLAG_SLIDER,    "slider" },
-                       { 0, NULL }
-               };
-               printf(" flags=%s", flags2s(queryctrl->flags, def).c_str());
-       }
-       printf("\n");
-       if (queryctrl->type == V4L2_CTRL_TYPE_MENU && show_menus) {
-               for (i = 0; i <= queryctrl->maximum; i++) {
-                       qmenu.index = i;
-                       if (ioctl(fd, VIDIOC_QUERYMENU, &qmenu))
-                               continue;
-                       printf("\t\t\t\t%d: %s\n", i, qmenu.name);
-               }
-       }
-}
-
-static int print_control(int fd, struct v4l2_queryctrl &qctrl, int show_menus)
-{
-       struct v4l2_control ctrl = { 0 };
-       struct v4l2_ext_control ext_ctrl = { 0 };
-       struct v4l2_ext_controls ctrls = { 0 };
-
-       if (qctrl.flags & V4L2_CTRL_FLAG_DISABLED)
-               return 1;
-       if (qctrl.type == V4L2_CTRL_TYPE_CTRL_CLASS) {
-               printf("\n%s\n\n", qctrl.name);
-               return 1;
-       }
-       ext_ctrl.id = qctrl.id;
-       ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(qctrl.id);
-       ctrls.count = 1;
-       ctrls.controls = &ext_ctrl;
-       if (V4L2_CTRL_ID2CLASS(qctrl.id) != V4L2_CTRL_CLASS_USER &&
-           qctrl.id < V4L2_CID_PRIVATE_BASE) {
-               if (ioctl(fd, VIDIOC_G_EXT_CTRLS, &ctrls)) {
-                       printf("error %d getting ext_ctrl %s\n",
-                                       errno, qctrl.name);
-                       return 0;
-               }
-       }
-       else {
-               ctrl.id = qctrl.id;
-               if (ioctl(fd, VIDIOC_G_CTRL, &ctrl)) {
-                       printf("error %d getting ctrl %s\n",
-                                       errno, qctrl.name);
-                       return 0;
-               }
-               ext_ctrl.value = ctrl.value;
-       }
-       print_qctrl(fd, &qctrl, &ext_ctrl, show_menus);
-       return 1;
-}
-
-static void list_controls(int fd, int show_menus)
-{
-       struct v4l2_queryctrl qctrl = { V4L2_CTRL_FLAG_NEXT_CTRL };
-       int id;
-
-       while (ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0) {
-                       print_control(fd, qctrl, show_menus);
-               qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
-       }
-       if (qctrl.id != V4L2_CTRL_FLAG_NEXT_CTRL)
-               return;
-       for (id = V4L2_CID_USER_BASE; id < V4L2_CID_LASTP1; id++) {
-               qctrl.id = id;
-               if (ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0)
-                       print_control(fd, qctrl, show_menus);
-       }
-       for (qctrl.id = V4L2_CID_PRIVATE_BASE;
-                       ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0; qctrl.id++) {
-               print_control(fd, qctrl, show_menus);
-       }
-}
-
-static void find_controls(int fd)
-{
-       struct v4l2_queryctrl qctrl = { V4L2_CTRL_FLAG_NEXT_CTRL };
-       int id;
-
-       while (ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0) {
-               if (qctrl.type != V4L2_CTRL_TYPE_CTRL_CLASS &&
-                   !(qctrl.flags & V4L2_CTRL_FLAG_DISABLED)) {
-                       ctrl_str2id[name2var(qctrl.name)] = qctrl.id;
-                       ctrl_id2str[qctrl.id] = name2var(qctrl.name);
-               }
-               qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
-       }
-       if (qctrl.id != V4L2_CTRL_FLAG_NEXT_CTRL)
-               return;
-       for (id = V4L2_CID_USER_BASE; id < V4L2_CID_LASTP1; id++) {
-               qctrl.id = id;
-               if (ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0 &&
-                   !(qctrl.flags & V4L2_CTRL_FLAG_DISABLED))
-                       ctrl_str2id[name2var(qctrl.name)] = qctrl.id;
-       }
-       for (qctrl.id = V4L2_CID_PRIVATE_BASE;
-                       ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0; qctrl.id++) {
-               if (!(qctrl.flags & V4L2_CTRL_FLAG_DISABLED))
-                       ctrl_str2id[name2var(qctrl.name)] = qctrl.id;
-       }
-}
-
-static std::string fbufcap2s(unsigned cap)
-{
-       std::string s;
-
-       if (cap & V4L2_FBUF_CAP_EXTERNOVERLAY)
-               s += "\t\t\tExtern Overlay\n";
-       if (cap & V4L2_FBUF_CAP_CHROMAKEY)
-               s += "\t\t\tChromakey\n";
-       if (cap & V4L2_FBUF_CAP_GLOBAL_ALPHA)
-               s += "\t\t\tGlobal Alpha\n";
-       if (cap & V4L2_FBUF_CAP_LOCAL_ALPHA)
-               s += "\t\t\tLocal Alpha\n";
-       if (cap & V4L2_FBUF_CAP_LOCAL_INV_ALPHA)
-               s += "\t\t\tLocal Inverted Alpha\n";
-       if (cap & V4L2_FBUF_CAP_LIST_CLIPPING)
-               s += "\t\t\tClipping List\n";
-       if (cap & V4L2_FBUF_CAP_BITMAP_CLIPPING)
-               s += "\t\t\tClipping Bitmap\n";
-       if (s.empty()) s += "\t\t\t\n";
-       return s;
-}
-
-static std::string fbufflags2s(unsigned fl)
-{
-       std::string s;
-
-       if (fl & V4L2_FBUF_FLAG_PRIMARY)
-               s += "\t\t\tPrimary Graphics Surface\n";
-       if (fl & V4L2_FBUF_FLAG_OVERLAY)
-               s += "\t\t\tOverlay Matches Capture/Output Size\n";
-       if (fl & V4L2_FBUF_FLAG_CHROMAKEY)
-               s += "\t\t\tChromakey\n";
-       if (fl & V4L2_FBUF_FLAG_GLOBAL_ALPHA)
-               s += "\t\t\tGlobal Alpha\n";
-       if (fl & V4L2_FBUF_FLAG_LOCAL_ALPHA)
-               s += "\t\t\tLocal Alpha\n";
-       if (fl & V4L2_FBUF_FLAG_LOCAL_INV_ALPHA)
-               s += "\t\t\tLocal Inverted Alpha\n";
-       if (s.empty()) s += "\t\t\t\n";
-       return s;
-}
-
-static void printfbuf(const struct v4l2_framebuffer &fb)
-{
-       int is_ext = fb.capability & V4L2_FBUF_CAP_EXTERNOVERLAY;
-
-       printf("Framebuffer Format:\n");
-       printf("\tCapability    : %s", fbufcap2s(fb.capability).c_str() + 3);
-       printf("\tFlags         : %s", fbufflags2s(fb.flags).c_str() + 3);
-       if (fb.base)
-               printf("\tBase          : 0x%p\n", fb.base);
-       printf("\tWidth         : %d\n", fb.fmt.width);
-       printf("\tHeight        : %d\n", fb.fmt.height);
-       printf("\tPixel Format  : %s\n", fcc2s(fb.fmt.pixelformat).c_str());
-       if (!is_ext) {
-               printf("\tBytes per Line: %d\n", fb.fmt.bytesperline);
-               printf("\tSize image    : %d\n", fb.fmt.sizeimage);
-               printf("\tColorspace    : %s\n", colorspace2s(fb.fmt.colorspace).c_str());
-               if (fb.fmt.priv)
-                       printf("\tCustom Info   : %08x\n", fb.fmt.priv);
-       }
-}
-
-static void printcrop(const struct v4l2_crop &crop)
-{
-       printf("Crop: Left %d, Top %d, Width %d, Height %d\n",
-                       crop.c.left, crop.c.top, crop.c.width, crop.c.height);
-}
-
-static void printcropcap(const struct v4l2_cropcap &cropcap)
-{
-       printf("Crop Capability %s:\n", buftype2s(cropcap.type).c_str());
-       printf("\tBounds      : Left %d, Top %d, Width %d, Height %d\n",
-                       cropcap.bounds.left, cropcap.bounds.top, cropcap.bounds.width, cropcap.bounds.height);
-       printf("\tDefault     : Left %d, Top %d, Width %d, Height %d\n",
-                       cropcap.defrect.left, cropcap.defrect.top, cropcap.defrect.width, cropcap.defrect.height);
-       printf("\tPixel Aspect: %u/%u\n", cropcap.pixelaspect.numerator, cropcap.pixelaspect.denominator);
-}
-
-static void printfmt(struct v4l2_format vfmt)
-{
-       const flag_def vbi_def[] = {
-               { V4L2_VBI_UNSYNC,     "unsynchronized" },
-               { V4L2_VBI_INTERLACED, "interlaced" },
-               { 0, NULL }
-       };
-       printf("Format %s:\n", buftype2s(vfmt.type).c_str());
-
-       switch (vfmt.type) {
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-               printf("\tWidth/Height  : %u/%u\n", vfmt.fmt.pix.width, vfmt.fmt.pix.height);
-               printf("\tPixel Format  : %s\n", fcc2s(vfmt.fmt.pix.pixelformat).c_str());
-               printf("\tField         : %s\n", field2s(vfmt.fmt.pix.field).c_str());
-               printf("\tBytes per Line: %u\n", vfmt.fmt.pix.bytesperline);
-               printf("\tSize Image    : %u\n", vfmt.fmt.pix.sizeimage);
-               printf("\tColorspace    : %s\n", colorspace2s(vfmt.fmt.pix.colorspace).c_str());
-               if (vfmt.fmt.pix.priv)
-                       printf("\tCustom Info   : %08x\n", vfmt.fmt.pix.priv);
-               break;
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
-       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-               printf("\tLeft/Top    : %d/%d\n",
-                               vfmt.fmt.win.w.left, vfmt.fmt.win.w.top);
-               printf("\tWidth/Height: %d/%d\n",
-                               vfmt.fmt.win.w.width, vfmt.fmt.win.w.height);
-               printf("\tField       : %s\n", field2s(vfmt.fmt.win.field).c_str());
-               printf("\tChroma Key  : 0x%08x\n", vfmt.fmt.win.chromakey);
-               printf("\tGlobal Alpha: 0x%02x\n", vfmt.fmt.win.global_alpha);
-               printf("\tClip Count  : %u\n", vfmt.fmt.win.clipcount);
-               printf("\tClip Bitmap : %s\n", vfmt.fmt.win.bitmap ? "Yes" : "No");
-               break;
-       case V4L2_BUF_TYPE_VBI_CAPTURE:
-       case V4L2_BUF_TYPE_VBI_OUTPUT:
-               printf("\tSampling Rate   : %u Hz\n", vfmt.fmt.vbi.sampling_rate);
-               printf("\tOffset          : %u samples (%g secs after leading edge)\n",
-                               vfmt.fmt.vbi.offset,
-                               (double)vfmt.fmt.vbi.offset / (double)vfmt.fmt.vbi.sampling_rate);
-               printf("\tSamples per Line: %u\n", vfmt.fmt.vbi.samples_per_line);
-               printf("\tSample Format   : %s\n", fcc2s(vfmt.fmt.vbi.sample_format).c_str());
-               printf("\tStart 1st Field : %u\n", vfmt.fmt.vbi.start[0]);
-               printf("\tCount 1st Field : %u\n", vfmt.fmt.vbi.count[0]);
-               printf("\tStart 2nd Field : %u\n", vfmt.fmt.vbi.start[1]);
-               printf("\tCount 2nd Field : %u\n", vfmt.fmt.vbi.count[1]);
-               if (vfmt.fmt.vbi.flags)
-                       printf("\tFlags           : %s\n", flags2s(vfmt.fmt.vbi.flags, vbi_def).c_str());
-               break;
-       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
-       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
-               printf("\tService Set    : %s\n",
-                               flags2s(vfmt.fmt.sliced.service_set, service_def).c_str());
-               for (int i = 0; i < 24; i++) {
-                       printf("\tService Line %2d: %8s / %-8s\n", i,
-                              flags2s(vfmt.fmt.sliced.service_lines[0][i], service_def).c_str(),
-                              flags2s(vfmt.fmt.sliced.service_lines[1][i], service_def).c_str());
-               }
-               printf("\tI/O Size       : %u\n", vfmt.fmt.sliced.io_size);
-               break;
-       case V4L2_BUF_TYPE_PRIVATE:
-               break;
-       }
-}
-
-static void print_video_formats(int fd, enum v4l2_buf_type type)
-{
-       struct v4l2_fmtdesc fmt;
-
-       fmt.index = 0;
-       fmt.type = type;
-       while (ioctl(fd, VIDIOC_ENUM_FMT, &fmt) >= 0) {
-               printf("\tType        : %s\n", buftype2s(type).c_str());
-               printf("\tPixelformat : %s", fcc2s(fmt.pixelformat).c_str());
-               if (fmt.flags)
-                       printf(" (compressed)");
-               printf("\n");
-               printf("\tName        : %s\n", fmt.description);
-               printf("\n");
-               fmt.index++;
-       }
-}
-
-static char *pts_to_string(char *str, unsigned long pts)
-{
-       static char buf[256];
-       int hours, minutes, seconds, fracsec;
-       float fps;
-       int frame;
-       char *p = (str) ? str : buf;
-
-       static const int MPEG_CLOCK_FREQ = 90000;
-       seconds = pts / MPEG_CLOCK_FREQ;
-       fracsec = pts % MPEG_CLOCK_FREQ;
-
-       minutes = seconds / 60;
-       seconds = seconds % 60;
-
-       hours = minutes / 60;
-       minutes = minutes % 60;
-
-       fps = 30;
-       frame = (int)ceilf(((float)fracsec / (float)MPEG_CLOCK_FREQ) * fps);
-
-       snprintf(p, sizeof(buf), "%d:%02d:%02d:%d", hours, minutes, seconds,
-                frame);
-       return p;
-}
+       int retVal;
+       int e;
 
-static const char *audmode2s(int audmode)
-{
-       switch (audmode) {
-               case V4L2_TUNER_MODE_STEREO: return "stereo";
-               case V4L2_TUNER_MODE_LANG1: return "lang1";
-               case V4L2_TUNER_MODE_LANG2: return "lang2";
-               case V4L2_TUNER_MODE_LANG1_LANG2: return "bilingual";
-               case V4L2_TUNER_MODE_MONO: return "mono";
-               default: return "unknown";
+       errno = 0;
+       retVal = ioctl(fd, request, parm);
+       e = errno;
+       if (verbose)
+               printf("\t\t%s returned %d (%s)\n", name, retVal, strerror(e));
+       if (retVal == 0) return retVal;
+       if (retVal != -1) {
+               return -1;
        }
+       retVal = e;
+       return retVal;
 }
 
-static std::string rxsubchans2s(int rxsubchans)
-{
-       std::string s;
-
-       if (rxsubchans & V4L2_TUNER_SUB_MONO)
-               s += "mono ";
-       if (rxsubchans & V4L2_TUNER_SUB_STEREO)
-               s += "stereo ";
-       if (rxsubchans & V4L2_TUNER_SUB_LANG1)
-               s += "lang1 ";
-       if (rxsubchans & V4L2_TUNER_SUB_LANG2)
-               s += "lang2 ";
-       return s;
-}
-
-static std::string tcap2s(unsigned cap)
-{
-       std::string s;
-
-       if (cap & V4L2_TUNER_CAP_LOW)
-               s += "62.5 Hz ";
-       else
-               s += "62.5 kHz ";
-       if (cap & V4L2_TUNER_CAP_NORM)
-               s += "multi-standard ";
-       if (cap & V4L2_TUNER_CAP_STEREO)
-               s += "stereo ";
-       if (cap & V4L2_TUNER_CAP_LANG1)
-               s += "lang1 ";
-       if (cap & V4L2_TUNER_CAP_LANG2)
-               s += "lang2 ";
-       return s;
-}
-
-static std::string cap2s(unsigned cap)
+std::string cap2s(unsigned cap)
 {
        std::string s;
 
@@ -673,8 +141,12 @@ static std::string cap2s(unsigned cap)
                s += "\t\tSliced VBI Output\n";
        if (cap & V4L2_CAP_RDS_CAPTURE)
                s += "\t\tRDS Capture\n";
+       if (cap & V4L2_CAP_RDS_OUTPUT)
+               s += "\t\tRDS Output\n";
        if (cap & V4L2_CAP_TUNER)
                s += "\t\tTuner\n";
+       if (cap & V4L2_CAP_MODULATOR)
+               s += "\t\tModulator\n";
        if (cap & V4L2_CAP_AUDIO)
                s += "\t\tAudio\n";
        if (cap & V4L2_CAP_RADIO)
@@ -688,171 +160,17 @@ static std::string cap2s(unsigned cap)
        return s;
 }
 
-static v4l2_std_id parse_pal(const char *pal)
-{
-       if (pal[0] == '-') {
-               switch (pal[1]) {
-                       case '6':
-                               return V4L2_STD_PAL_60;
-                       case 'b':
-                       case 'B':
-                       case 'g':
-                       case 'G':
-                               return V4L2_STD_PAL_BG;
-                       case 'h':
-                       case 'H':
-                               return V4L2_STD_PAL_H;
-                       case 'n':
-                       case 'N':
-                               if (pal[2] == 'c' || pal[2] == 'C')
-                                       return V4L2_STD_PAL_Nc;
-                               return V4L2_STD_PAL_N;
-                       case 'i':
-                       case 'I':
-                               return V4L2_STD_PAL_I;
-                       case 'd':
-                       case 'D':
-                       case 'k':
-                       case 'K':
-                               return V4L2_STD_PAL_DK;
-                       case 'M':
-                       case 'm':
-                               return V4L2_STD_PAL_M;
-                       case '-':
-                               break;
-               }
-       }
-       fprintf(stderr, "pal specifier not recognised\n");
-       return 0;
-}
-
-static v4l2_std_id parse_secam(const char *secam)
-{
-       if (secam[0] == '-') {
-               switch (secam[1]) {
-                       case 'b':
-                       case 'B':
-                       case 'g':
-                       case 'G':
-                       case 'h':
-                       case 'H':
-                               return V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
-                       case 'd':
-                       case 'D':
-                       case 'k':
-                       case 'K':
-                               return V4L2_STD_SECAM_DK;
-                       case 'l':
-                       case 'L':
-                               if (secam[2] == 'C' || secam[2] == 'c')
-                                       return V4L2_STD_SECAM_LC;
-                               return V4L2_STD_SECAM_L;
-                       case '-':
-                               break;
-               }
-       }
-       fprintf(stderr, "secam specifier not recognised\n");
-       return 0;
-}
-
-static v4l2_std_id parse_ntsc(const char *ntsc)
-{
-       if (ntsc[0] == '-') {
-               switch (ntsc[1]) {
-                       case 'm':
-                       case 'M':
-                               return V4L2_STD_NTSC_M;
-                       case 'j':
-                       case 'J':
-                               return V4L2_STD_NTSC_M_JP;
-                       case 'k':
-                       case 'K':
-                               return V4L2_STD_NTSC_M_KR;
-                       case '-':
-                               break;
-               }
-       }
-       fprintf(stderr, "ntsc specifier not recognised\n");
-       return 0;
-}
-
-static int doioctl(int fd, unsigned long int request, void *parm, const char *name)
-{
-       int retVal;
-       int e;
-
-       errno = 0;
-       retVal = ioctl(fd, request, parm);
-       e = errno;
-       if (verbose)
-               printf("\t\t%s returned %d (%s)\n", name, retVal, strerror(e));
-       if (retVal == 0) return retVal;
-       if (retVal != -1) {
-               return -1;
-       }
-       retVal = e;
-       return retVal;
-}
-
-static int parse_subopt(char **subs, char * const *subopts, char **value)
-{
-       int opt = getsubopt(subs, subopts, value);
-
-       if (opt == -1) {
-               fprintf(stderr, "Invalid suboptions specified\n");
-               usage();
-               exit(1);
-       }
-       if (value == NULL) {
-               fprintf(stderr, "No value given to suboption <%s>\n",
-                               subopts[opt]);
-               usage();
-               exit(1);
-       }
-       return opt;
-}
-
-static void parse_next_subopt(char **subs, char **value)
-{
-       static char *const subopts[] = {
-           NULL
-       };
-       int opt = getsubopt(subs, subopts, value);
-
-       if (value == NULL) {
-               fprintf(stderr, "No value given to suboption <%s>\n",
-                               subopts[opt]);
-               usage();
-               exit(1);
-       }
-}
-
-static void print_std(const char *prefix, const char *stds[], unsigned long long std)
-{
-       int first = 1;
-
-       printf("\t%s-", prefix);
-       while (*stds) {
-               if (std & 1) {
-                       if (!first)
-                               printf("/");
-                       first = 0;
-                       printf("%s", *stds);
-               }
-               stds++;
-               std >>= 1;
-       }
-       printf("\n");
-}
-
-static const char *ok(int res)
+const char *ok(int res)
 {
+       tests_total++;
        if (res)
                app_result = res;
+       else
+               tests_ok++;
        return res ? "FAIL" : "OK";
 }
 
-static int check_string(const char *s, size_t len, const char *fld)
+int check_string(const char *s, size_t len, const char *fld)
 {
        if (strlen(s) == 0) {
                if (verbose)
@@ -867,12 +185,12 @@ static int check_string(const char *s, size_t len, const char *fld)
        return 0;
 }
 
-static int check_ustring(const __u8 *s, int len, const char *fld)
+int check_ustring(const __u8 *s, int len, const char *fld)
 {
        return check_string((const char *)s, len, fld);
 }
 
-static int check_0(void *p, int len)
+int check_0(void *p, int len)
 {
        __u8 *q = (__u8 *)p;
 
@@ -908,99 +226,24 @@ static int testCap(int fd)
        return 0;
 }
 
-static int testChipIdent(int fd)
-{
-       struct v4l2_dbg_chip_ident chip;
-       int ret;
-
-       memset(&chip, 0, sizeof(chip));
-       chip.match.type = V4L2_CHIP_MATCH_HOST;
-       chip.match.addr = 0;
-       ret = doioctl(fd, VIDIOC_DBG_G_CHIP_IDENT, &chip, "VIDIOC_DBG_G_CHIP_IDENT");
-       // Must return either 0 (OK) or EINVAL (not supported)
-       if (ret == 0) {
-               struct v4l2_dbg_chip_ident orig;
-
-               memset(&orig, 0, sizeof(orig));
-               // set invalid match_type
-               chip.match.type = V4L2_CHIP_MATCH_I2C_ADDR + 1;
-               chip.match.addr = 0xdeadbeef;
-               chip.ident = 0xdeadbeef;
-               chip.revision = 0xdeadbeef;
-               orig = chip;
-               ret = doioctl(fd, VIDIOC_DBG_G_CHIP_IDENT, &chip, "VIDIOC_DBG_G_CHIP_IDENT");
-               if (ret != EINVAL) {
-                       if (verbose)
-                               printf("Invalid match_type accepted\n");
-                       return -1;
-               }
-               if (memcmp(&orig, &chip, sizeof(chip))) {
-                       if (verbose)
-                               printf("Error, but struct modified\n");
-                       return -1;
-               }
-               return 0;
-       }
-       return ret != EINVAL;
-}
-
-static int testRegister(int fd)
-{
-       struct v4l2_dbg_register reg;
-       struct v4l2_dbg_chip_ident chip;
-       int ret;
-       int uid = getuid();
-
-       reg.match.type = V4L2_CHIP_MATCH_HOST;
-       reg.match.addr = 0;
-       reg.reg = 0;
-       ret = doioctl(fd, VIDIOC_DBG_G_REGISTER, &reg, "VIDIOC_DBG_G_REGISTER");
-       if (ret == EINVAL)
-               return 0;
-       if (uid && ret != EPERM) {
-               printf("Not allowed to call VIDIOC_DBG_G_REGISTER unless root\n");
-               return -1;
-       }
-       if (uid == 0 && ret) {
-               printf("Not allowed to call VIDIOC_DBG_G_REGISTER even though we are root\n");
-               return -1;
-       }
-       chip.match.type = V4L2_CHIP_MATCH_HOST;
-       chip.match.addr = 0;
-       if (doioctl(fd, VIDIOC_DBG_G_CHIP_IDENT, &chip, "VIDIOC_DBG_G_CHIP_IDENT")) {
-               printf("Must support VIDIOC_DBG_G_CHIP_IDENT\n");
-               return -1;
-       }
-       if (uid) {
-               // Don't test S_REGISTER as root, don't want to risk
-               // messing with registers in the compliance test.
-               reg.reg = reg.val = 0;
-               ret = doioctl(fd, VIDIOC_DBG_S_REGISTER, &reg, "VIDIOC_DBG_S_REGISTER");
-               if (ret != EINVAL && ret != EPERM) {
-                       printf("Invalid error calling VIDIOC_DBG_S_REGISTER as non-root\n");
-                       return -1;
-               }
-       }
-       return 0;
-}
-
-static int testLogStatus(int fd)
-{
-       int ret = doioctl(fd, VIDIOC_LOG_STATUS, NULL, "VIDIOC_LOG_STATUS");
-
-       return (ret == 0 || ret == EINVAL) ? 0 : -1;
-}
-
 int main(int argc, char **argv)
 {
-       char *value, *subs;
        int i;
        unsigned t;
        int fd = -1;
+       int video_fd = -1;
+       int video_fd2 = -1;
+       int radio_fd = -1;
+       int radio_fd2 = -1;
+       int vbi_fd = -1;
+       int vbi_fd2 = -1;
 
        /* command args */
        int ch;
-       const char *device = "/dev/video0";     /* -d device */
+       const char *device = NULL;
+       const char *video_device = NULL;                /* -d device */
+       const char *radio_device = NULL;        /* -r device */
+       const char *vbi_device = NULL;          /* -V device */
        struct v4l2_capability vcap;    /* list_cap */
        char short_options[26 * 2 * 2 + 1];
        int idx = 0;
@@ -1036,13 +279,33 @@ int main(int argc, char **argv)
                        tests++;
                        break;
                case OptSetDevice:
-                       device = optarg;
-                       if (device[0] >= '0' && device[0] <= '9' && device[1] == 0) {
+                       video_device = optarg;
+                       if (video_device[0] >= '0' && video_device[0] <= '9' && video_device[1] == 0) {
                                static char newdev[20];
-                               char dev = device[0];
+                               char dev = video_device[0];
 
                                sprintf(newdev, "/dev/video%c", dev);
-                               device = newdev;
+                               video_device = newdev;
+                       }
+                       break;
+               case OptSetRadioDevice:
+                       radio_device = optarg;
+                       if (radio_device[0] >= '0' && radio_device[0] <= '9' && radio_device[1] == 0) {
+                               static char newdev[20];
+                               char dev = radio_device[0];
+
+                               sprintf(newdev, "/dev/radio%c", dev);
+                               radio_device = newdev;
+                       }
+                       break;
+               case OptSetVbiDevice:
+                       vbi_device = optarg;
+                       if (vbi_device[0] >= '0' && vbi_device[0] <= '9' && vbi_device[1] == 0) {
+                               static char newdev[20];
+                               char dev = vbi_device[0];
+
+                               sprintf(newdev, "/dev/vbi%c", dev);
+                               vbi_device = newdev;
                        }
                        break;
                case ':':
@@ -1071,45 +334,85 @@ int main(int argc, char **argv)
                        test[t] = 1;
        }
 
-       if ((fd = open(device, O_RDWR)) < 0) {
-               fprintf(stderr, "Failed to open %s: %s\n", device,
+       if (!video_device && !radio_device && !vbi_device) {
+               fprintf(stderr, "No device selected\n");
+               usage();
+               exit(1);
+       }
+
+       if (video_device && (video_fd = open(video_device, O_RDWR)) < 0) {
+               fprintf(stderr, "Failed to open %s: %s\n", video_device,
                        strerror(errno));
                exit(1);
        }
 
-       ioctl(fd, VIDIOC_QUERYCAP, &vcap, "VIDIOC_QUERYCAP");
-       caps = vcap.capabilities;
-       find_controls(fd);
-       for (ctrl_get_list::iterator iter = get_ctrls.begin(); iter != get_ctrls.end(); ++iter) {
-           if (ctrl_str2id.find(*iter) == ctrl_str2id.end()) {
-               fprintf(stderr, "unknown control '%s'\n", (*iter).c_str());
+       if (radio_device && (radio_fd = open(radio_device, O_RDWR)) < 0) {
+               fprintf(stderr, "Failed to open %s: %s\n", radio_device,
+                       strerror(errno));
                exit(1);
-           }
        }
-       for (ctrl_set_map::iterator iter = set_ctrls.begin(); iter != set_ctrls.end(); ++iter) {
-           if (ctrl_str2id.find(iter->first) == ctrl_str2id.end()) {
-               fprintf(stderr, "unknown control '%s'\n", iter->first.c_str());
+
+       if (vbi_device && (vbi_fd = open(vbi_device, O_RDWR)) < 0) {
+               fprintf(stderr, "Failed to open %s: %s\n", vbi_device,
+                       strerror(errno));
                exit(1);
-           }
        }
 
+       if (video_fd >= 0) {
+               fd = video_fd;
+               device = video_device;
+       } else if (radio_fd >= 0) {
+               fd = radio_fd;
+               device = radio_device;
+       } else if (vbi_fd >= 0) {
+               fd = vbi_fd;
+               device = vbi_device;
+       }
+
+       ioctl(fd, VIDIOC_QUERYCAP, &vcap, "VIDIOC_QUERYCAP");
+       caps = vcap.capabilities;
+
        /* Information Opts */
 
-       if (options[OptGetDriverInfo]) {
-               printf("Driver Info:\n");
-               printf("\tDriver name   : %s\n", vcap.driver);
-               printf("\tCard type     : %s\n", vcap.card);
-               printf("\tBus info      : %s\n", vcap.bus_info);
-               printf("\tDriver version: %d\n", vcap.version);
-               printf("\tCapabilities  : 0x%08X\n", vcap.capabilities);
-               printf("%s", cap2s(vcap.capabilities).c_str());
-       }
+       printf("Driver Info:\n");
+       printf("\tDriver name   : %s\n", vcap.driver);
+       printf("\tCard type     : %s\n", vcap.card);
+       printf("\tBus info      : %s\n", vcap.bus_info);
+       printf("\tDriver version: %d\n", vcap.version);
+       printf("\tCapabilities  : 0x%08X\n", vcap.capabilities);
+       printf("%s", cap2s(vcap.capabilities).c_str());
 
-       printf("Compliance test for device %s:\n\n", device);
+       printf("\nCompliance test for device %s:\n\n", device);
 
        printf("Required ioctls:\n");
-       if (test[TestCap])
-               printf("\ttest VIDIOC_QUERYCAP: %s\n", ok(testCap(fd)));
+       if (test[TestCap]) {
+               if (video_device)
+                       printf("\ttest VIDIOC_QUERYCAP: %s\n", ok(testCap(video_fd)));
+               if (radio_device)
+                       printf("\ttest VIDIOC_QUERYCAP for radio: %s\n", ok(testCap(radio_fd)));
+               if (vbi_device)
+                       printf("\ttest VIDIOC_QUERYCAP for vbi: %s\n", ok(testCap(vbi_fd)));
+       }
+
+       printf("Allow for multiple opens:\n");
+       if (video_device) {
+               printf("\ttest second video open: %s\n",
+                               ok((video_fd2 = open(video_device, O_RDWR)) < 0));
+               if (video_fd2 >= 0)
+                       printf("\ttest VIDIOC_QUERYCAP: %s\n", ok(testCap(video_fd2)));
+       }
+       if (radio_device) {
+               printf("\ttest second radio open: %s\n",
+                               ok((radio_fd2 = open(radio_device, O_RDWR)) < 0));
+               if (radio_fd2 >= 0)
+                       printf("\ttest VIDIOC_QUERYCAP: %s\n", ok(testCap(radio_fd2)));
+       }
+       if (vbi_device) {
+               printf("\ttest second vbi open: %s\n",
+                               ok((vbi_fd2 = open(vbi_device, O_RDWR)) < 0));
+               if (vbi_fd2 >= 0)
+                       printf("\ttest VIDIOC_QUERYCAP: %s\n", ok(testCap(vbi_fd2)));
+       }
 
        printf("Debug ioctls:\n");
        if (test[TestChipIdent])
@@ -1120,5 +423,7 @@ int main(int argc, char **argv)
                printf("\ttest VIDIOC_LOG_STATUS: %s\n", ok(testLogStatus(fd)));
 
        close(fd);
+       printf("Total: %d Succeeded: %d Failed: %d\n",
+                       tests_total, tests_ok, tests_total - tests_ok);
        exit(app_result);
 }
diff --git a/utils/v4l2-compliance/v4l2-compliance.h b/utils/v4l2-compliance/v4l2-compliance.h
new file mode 100644 (file)
index 0000000..cac3583
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+    V4L2 API compliance test tool.
+
+    Copyright (C) 2008, 2010  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _V4L2_COMPLIANCE_H_
+#define _V4L2_COMPLIANCE_H_
+
+#include <string>
+#include <linux/videodev2.h>
+
+extern int verbose;
+extern unsigned caps;
+
+int doioctl(int fd, unsigned long int request, void *parm, const char *name);
+std::string cap2s(unsigned cap);
+const char *ok(int res);
+int check_string(const char *s, size_t len, const char *fld);
+int check_ustring(const __u8 *s, int len, const char *fld);
+int check_0(void *p, int len);
+
+// Debug ioctl tests
+int testChipIdent(int fd);
+int testRegister(int fd);
+int testLogStatus(int fd);
+
+#endif
diff --git a/utils/v4l2-compliance/v4l2-test-debug.cpp b/utils/v4l2-compliance/v4l2-test-debug.cpp
new file mode 100644 (file)
index 0000000..d1515f4
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+    V4L2 API compliance debug ioctl tests.
+
+    Copyright (C) 2008, 2010  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <math.h>
+#include <sys/klog.h>
+#include "v4l2-compliance.h"
+
+int testChipIdent(int fd)
+{
+       struct v4l2_dbg_chip_ident chip;
+       int ret;
+
+       memset(&chip, 0, sizeof(chip));
+       chip.match.type = V4L2_CHIP_MATCH_HOST;
+       chip.match.addr = 0;
+       ret = doioctl(fd, VIDIOC_DBG_G_CHIP_IDENT, &chip, "VIDIOC_DBG_G_CHIP_IDENT");
+       // Must return either 0 (OK) or EINVAL (not supported)
+       if (ret == 0) {
+               struct v4l2_dbg_chip_ident orig;
+
+               memset(&orig, 0, sizeof(orig));
+               // set invalid match_type
+               chip.match.type = V4L2_CHIP_MATCH_I2C_ADDR + 1;
+               chip.match.addr = 0xdeadbeef;
+               chip.ident = 0xdeadbeef;
+               chip.revision = 0xdeadbeef;
+               orig = chip;
+               ret = doioctl(fd, VIDIOC_DBG_G_CHIP_IDENT, &chip, "VIDIOC_DBG_G_CHIP_IDENT");
+               if (ret != EINVAL) {
+                       if (verbose)
+                               printf("Invalid match_type accepted\n");
+                       return -1;
+               }
+               if (memcmp(&orig, &chip, sizeof(chip))) {
+                       if (verbose)
+                               printf("Error, but struct modified\n");
+                       return -1;
+               }
+               return 0;
+       }
+       return ret != EINVAL;
+}
+
+int testRegister(int fd)
+{
+       struct v4l2_dbg_register reg;
+       struct v4l2_dbg_chip_ident chip;
+       int ret;
+       int uid = getuid();
+
+       reg.match.type = V4L2_CHIP_MATCH_HOST;
+       reg.match.addr = 0;
+       reg.reg = 0;
+       ret = doioctl(fd, VIDIOC_DBG_G_REGISTER, &reg, "VIDIOC_DBG_G_REGISTER");
+       if (ret == EINVAL)
+               return 0;
+       if (uid && ret != EPERM) {
+               printf("Not allowed to call VIDIOC_DBG_G_REGISTER unless root\n");
+               return -1;
+       }
+       if (uid == 0 && ret) {
+               printf("Not allowed to call VIDIOC_DBG_G_REGISTER even though we are root\n");
+               return -1;
+       }
+       chip.match.type = V4L2_CHIP_MATCH_HOST;
+       chip.match.addr = 0;
+       if (doioctl(fd, VIDIOC_DBG_G_CHIP_IDENT, &chip, "VIDIOC_DBG_G_CHIP_IDENT")) {
+               printf("Must support VIDIOC_DBG_G_CHIP_IDENT\n");
+               return -1;
+       }
+       if (uid) {
+               // Don't test S_REGISTER as root, don't want to risk
+               // messing with registers in the compliance test.
+               reg.reg = reg.val = 0;
+               ret = doioctl(fd, VIDIOC_DBG_S_REGISTER, &reg, "VIDIOC_DBG_S_REGISTER");
+               if (ret != EINVAL && ret != EPERM) {
+                       printf("Invalid error calling VIDIOC_DBG_S_REGISTER as non-root\n");
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+int testLogStatus(int fd)
+{
+       int ret = doioctl(fd, VIDIOC_LOG_STATUS, NULL, "VIDIOC_LOG_STATUS");
+
+       return (ret == 0 || ret == EINVAL) ? 0 : -1;
+}