v4l2-compliance: add tests for VIDIOC_REQBUFS/CREATE_BUFS.
authorHans Verkuil <hans.verkuil@cisco.com>
Wed, 6 Jun 2012 22:16:12 +0000 (00:16 +0200)
committerHans Verkuil <hans.verkuil@cisco.com>
Wed, 6 Jun 2012 22:16:12 +0000 (00:16 +0200)
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
utils/v4l2-compliance/Makefile.am
utils/v4l2-compliance/v4l2-compliance.cpp
utils/v4l2-compliance/v4l2-compliance.h
utils/v4l2-compliance/v4l2-test-buffers.cpp [new file with mode: 0644]

index 2f4d8bb..8302126 100644 (file)
@@ -1,5 +1,5 @@
 bin_PROGRAMS = v4l2-compliance
-v4l2_compliance_SOURCES = v4l2-compliance.cpp v4l2-test-debug.cpp v4l2-test-input-output.cpp v4l2-test-controls.cpp v4l2-test-io-config.cpp v4l2-test-formats.cpp v4l2-compliance.h
+v4l2_compliance_SOURCES = v4l2-compliance.cpp v4l2-test-debug.cpp v4l2-test-input-output.cpp v4l2-test-controls.cpp v4l2-test-io-config.cpp v4l2-test-formats.cpp v4l2-test-buffers.cpp v4l2-compliance.h
 v4l2_compliance_LDADD = ../../lib/libv4l2/libv4l2.la
 
 EXTRA_DIST = fixme.txt
index 4869520..3ccd533 100644 (file)
@@ -258,6 +258,7 @@ static int testCap(struct node *node)
                        V4L2_CAP_VIDEO_OUTPUT_OVERLAY | V4L2_CAP_VBI_OUTPUT |
                        V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_MODULATOR |
                        V4L2_CAP_RDS_OUTPUT;
+       const __u32 io_caps = V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
 
        // Must always be there
        fail_on_test(doioctl(node, VIDIOC_QUERYCAP, &vcap));
@@ -291,6 +292,10 @@ static int testCap(struct node *node)
                fail_on_test(dcaps & output_caps);
        if (dcaps & output_caps)
                fail_on_test(dcaps & input_caps);
+       if (node->can_capture || node->can_output)
+               fail_on_test(!(dcaps & io_caps));
+       else
+               fail_on_test(dcaps & io_caps);
 
        return 0;
 }
@@ -473,6 +478,7 @@ int main(int argc, char **argv)
                device = vbi_device;
                node.is_vbi = true;
        }
+       node.device = device;
 
        doioctl(&node, VIDIOC_QUERYCAP, &vcap);
        if (vcap.capabilities & V4L2_CAP_DEVICE_CAPS)
@@ -487,6 +493,12 @@ int main(int argc, char **argv)
                         V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_RDS_OUTPUT |
                         V4L2_CAP_MODULATOR))
                node.has_outputs = true;
+       if (node.caps & (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VBI_CAPTURE |
+                        V4L2_CAP_SLICED_VBI_CAPTURE))
+               node.can_capture = true;
+       if (node.caps & (V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VBI_OUTPUT |
+                        V4L2_CAP_SLICED_VBI_OUTPUT))
+               node.can_output = true;
 
        /* Information Opts */
 
@@ -528,7 +540,7 @@ int main(int argc, char **argv)
                        printf("\ttest VIDIOC_QUERYCAP: %s\n", ok(testCap(&video_node2)));
                        printf("\ttest VIDIOC_G/S_PRIORITY: %s\n",
                                        ok(testPrio(&video_node, &video_node2)));
-                       test_close(video_node2.fd);
+                       node.node2 = &video_node2;
                }
        }
        if (radio_device) {
@@ -539,7 +551,7 @@ int main(int argc, char **argv)
                        printf("\ttest VIDIOC_QUERYCAP: %s\n", ok(testCap(&radio_node2)));
                        printf("\ttest VIDIOC_G/S_PRIORITY: %s\n",
                                        ok(testPrio(&radio_node, &radio_node2)));
-                       test_close(radio_node2.fd);
+                       node.node2 = &video_node2;
                }
        }
        if (vbi_device) {
@@ -550,7 +562,7 @@ int main(int argc, char **argv)
                        printf("\ttest VIDIOC_QUERYCAP: %s\n", ok(testCap(&vbi_node2)));
                        printf("\ttest VIDIOC_G/S_PRIORITY: %s\n",
                                        ok(testPrio(&vbi_node, &vbi_node2)));
-                       test_close(vbi_node2.fd);
+                       node.node2 = &video_node2;
                }
        }
        printf("\n");
@@ -617,6 +629,12 @@ int main(int argc, char **argv)
        printf("\ttest VIDIOC_G_FMT: %s\n", ok(testFormats(&node)));
        printf("\ttest VIDIOC_G_SLICED_VBI_CAP: %s\n", ok(testSlicedVBICap(&node)));
 
+       /* Buffer ioctls */
+
+       printf("Buffer ioctls:\n");
+       printf("\ttest VIDIOC_REQBUFS/CREATE_BUFS: %s\n", ok(testReqBufs(&node)));
+       printf("\ttest read/write: %s\n", ok(testReadWrite(&node)));
+
        /* TODO:
 
           VIDIOC_CROPCAP, VIDIOC_G/S_CROP, VIDIOC_G/S_SELECTION
@@ -625,7 +643,7 @@ int main(int argc, char **argv)
           VIDIOC_(TRY_)ENCODER_CMD
           VIDIOC_(TRY_)DECODER_CMD
           VIDIOC_G_ENC_INDEX
-          VIDIOC_REQBUFS/QBUF/DQBUF/QUERYBUF/CREATE_BUFS/PREPARE_BUFS
+          VIDIOC_QBUF/DQBUF/QUERYBUF/PREPARE_BUFS
           VIDIOC_STREAMON/OFF
 
           */
@@ -633,6 +651,7 @@ int main(int argc, char **argv)
        /* Final test report */
 
        test_close(node.fd);
+       test_close(node.node2->fd);
        printf("Total: %d Succeeded: %d Failed: %d Warnings: %d\n",
                        tests_total, tests_ok, tests_total - tests_ok, warnings);
        exit(app_result);
index 18a91e4..0e50a63 100644 (file)
@@ -40,12 +40,18 @@ struct test_queryctrl: v4l2_queryctrl {
 typedef std::list<test_queryctrl> qctrl_list;
 typedef std::set<__u32> pixfmt_set;
 
+struct node;
+
 struct node {
        int fd;
        bool is_video;
        bool is_radio;
        bool is_vbi;
+       bool can_capture;
+       bool can_output;
+       const char *device;
        unsigned caps;
+       struct node *node2;     /* second open filehandle */
        bool has_outputs;
        bool has_inputs;
        unsigned tuners;
@@ -59,6 +65,7 @@ struct node {
        qctrl_list controls;
        __u32 fbuf_caps;
        pixfmt_set buftype_pixfmts[V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE + 1];
+       __u32 valid_buftype;
 };
 
 #define info(fmt, args...)                                     \
@@ -96,6 +103,16 @@ static inline int test_close(int fd)
        return wrapper ? v4l2_close(fd) : close(fd);
 }
 
+static inline void reopen(struct node *node)
+{
+       test_close(node->fd);
+       if ((node->fd = test_open(node->device, O_RDWR)) < 0) {
+               fprintf(stderr, "Failed to open %s: %s\n", node->device,
+                       strerror(errno));
+               exit(1);
+       }
+}
+
 static inline int test_ioctl(int fd, int cmd, void *arg)
 {
        return wrapper ? v4l2_ioctl(fd, cmd, arg) : ioctl(fd, cmd, arg);
@@ -167,4 +184,8 @@ int testFBuf(struct node *node);
 int testFormats(struct node *node);
 int testSlicedVBICap(struct node *node);
 
+// Buffer ioctl tests
+int testReqBufs(struct node *node);
+int testReadWrite(struct node *node);
+
 #endif
diff --git a/utils/v4l2-compliance/v4l2-test-buffers.cpp b/utils/v4l2-compliance/v4l2-test-buffers.cpp
new file mode 100644 (file)
index 0000000..28b05ce
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+    V4L2 API compliance buffer ioctl tests.
+
+    Copyright (C) 2012  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., 51 Franklin Street, Suite 500, Boston, MA  02110-1335  USA
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include "v4l2-compliance.h"
+
+int testReqBufs(struct node *node)
+{
+       struct v4l2_requestbuffers bufs;
+       struct v4l2_create_buffers cbufs;
+       struct v4l2_format fmt;
+       bool can_stream = node->caps & V4L2_CAP_STREAMING;
+       bool can_rw = node->caps & V4L2_CAP_READWRITE;
+       bool mmap_valid;
+       bool userptr_valid;
+       int ret;
+       unsigned i;
+       
+       memset(&bufs, 0, sizeof(bufs));
+       memset(&cbufs, 0, sizeof(cbufs));
+       ret = doioctl(node, VIDIOC_REQBUFS, &bufs);
+       if (ret == ENOTTY) {
+               fail_on_test(can_stream);
+               return ret;
+       }
+       fail_on_test(ret != EINVAL);
+       fail_on_test(node->node2 == NULL);
+       for (i = 1; i <= V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; i++) {
+               if (node->buftype_pixfmts[i].empty())
+                       continue;
+               info("test buftype %d\n", i);
+               if (node->valid_buftype == 0)
+                       node->valid_buftype = i;
+               fmt.type = i;
+               fail_on_test(doioctl(node, VIDIOC_G_FMT, &fmt));
+               bufs.type = fmt.type;
+               fail_on_test(doioctl(node, VIDIOC_REQBUFS, &bufs) != EINVAL);
+               bufs.memory = V4L2_MEMORY_MMAP;
+               ret = doioctl(node, VIDIOC_REQBUFS, &bufs);
+               fail_on_test(ret && ret != EINVAL);
+               mmap_valid = !ret;
+               bufs.memory = V4L2_MEMORY_USERPTR;
+               ret = doioctl(node, VIDIOC_REQBUFS, &bufs);
+               fail_on_test(ret && ret != EINVAL);
+               userptr_valid = !ret;
+               fail_on_test(can_stream && !mmap_valid && !userptr_valid);
+               fail_on_test(!can_stream && (mmap_valid || userptr_valid));
+               if (!can_stream)
+                       continue;
+
+               if (mmap_valid) {
+                       bufs.count = 1;
+                       bufs.memory = V4L2_MEMORY_MMAP;
+                       fail_on_test(doioctl(node, VIDIOC_REQBUFS, &bufs));
+                       fail_on_test(bufs.count == 0);
+                       fail_on_test(bufs.memory != V4L2_MEMORY_MMAP);
+                       fail_on_test(bufs.type != i);
+                       fail_on_test(doioctl(node, VIDIOC_REQBUFS, &bufs));
+               }
+               if (userptr_valid) {
+                       bufs.count = 1;
+                       bufs.memory = V4L2_MEMORY_USERPTR;
+                       fail_on_test(doioctl(node, VIDIOC_REQBUFS, &bufs));
+                       fail_on_test(bufs.count == 0);
+                       fail_on_test(bufs.memory != V4L2_MEMORY_USERPTR);
+                       fail_on_test(bufs.type != i);
+                       fail_on_test(doioctl(node, VIDIOC_REQBUFS, &bufs));
+               }
+
+               if (can_rw) {
+                       char buf = 0;
+
+                       if (node->can_capture)
+                               ret = read(node->fd, &buf, 1);
+                       else
+                               ret = write(node->fd, &buf, 1);
+                       fail_on_test(ret != -1);
+                       fail_on_test(errno != EBUSY);
+               }
+               bufs.count = 1;
+               fail_on_test(doioctl(node->node2, VIDIOC_REQBUFS, &bufs) != EBUSY);
+               bufs.count = 0;
+               fail_on_test(doioctl(node->node2, VIDIOC_REQBUFS, &bufs) != EBUSY);
+               fail_on_test(doioctl(node, VIDIOC_REQBUFS, &bufs));
+               bufs.count = 1;
+               fail_on_test(doioctl(node->node2, VIDIOC_REQBUFS, &bufs));
+               bufs.count = 0;
+               fail_on_test(doioctl(node->node2, VIDIOC_REQBUFS, &bufs));
+               cbufs.format = fmt;
+               cbufs.count = 1;
+               cbufs.memory = bufs.memory;
+               ret = doioctl(node, VIDIOC_CREATE_BUFS, &cbufs);
+               if (ret == ENOTTY) {
+                       warn("VIDIOC_CREATE_BUFS not supported\n");
+                       continue;
+               }
+               fail_on_test(cbufs.count == 0);
+               fail_on_test(cbufs.memory != bufs.memory);
+               fail_on_test(cbufs.format.type != i);
+               cbufs.count = 1;
+               fail_on_test(doioctl(node, VIDIOC_CREATE_BUFS, &cbufs));
+               bufs.count = 1;
+               fail_on_test(doioctl(node->node2, VIDIOC_REQBUFS, &bufs) != EBUSY);
+               bufs.count = 0;
+               fail_on_test(doioctl(node, VIDIOC_REQBUFS, &bufs));
+       }
+       return 0;
+}
+
+int testReadWrite(struct node *node)
+{
+       bool can_rw = node->caps & V4L2_CAP_READWRITE;
+       char buf = 0;
+       int ret;
+
+       if (node->can_capture)
+               ret = read(node->fd, &buf, 1);
+       else
+               ret = write(node->fd, &buf, 1);
+       if (can_rw)
+               fail_on_test(ret != 1);
+       else
+               fail_on_test(ret < 0 && errno != EINVAL);
+       if (!can_rw)
+               return 0;
+
+       reopen(node);
+
+       /* check that the close cleared the busy flag */
+       if (node->can_capture)
+               ret = read(node->fd, &buf, 1);
+       else
+               ret = write(node->fd, &buf, 1);
+       fail_on_test(ret != 1);
+       reopen(node);
+       return 0;
+}