diff options
author | Hans Verkuil <hans.verkuil@cisco.com> | 2018-09-01 13:55:30 +0200 |
---|---|---|
committer | Hans Verkuil <hans.verkuil@cisco.com> | 2018-09-01 14:03:13 +0200 |
commit | b551f293d9e6c36ac6bce525d681ad2a62068e86 (patch) | |
tree | fecd6ebd3842186f7e72356f1e885b93998d7de2 | |
parent | d7d95f821ce6dd9c21f549524e731ba2b40f5c7b (diff) |
v4l2-compliance: add request compliance testsrequest
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
-rw-r--r-- | utils/v4l2-compliance/v4l2-compliance.cpp | 1 | ||||
-rw-r--r-- | utils/v4l2-compliance/v4l2-compliance.h | 1 | ||||
-rw-r--r-- | utils/v4l2-compliance/v4l2-test-buffers.cpp | 308 |
3 files changed, 307 insertions, 3 deletions
diff --git a/utils/v4l2-compliance/v4l2-compliance.cpp b/utils/v4l2-compliance/v4l2-compliance.cpp index 7167f549..a3fabe31 100644 --- a/utils/v4l2-compliance/v4l2-compliance.cpp +++ b/utils/v4l2-compliance/v4l2-compliance.cpp @@ -987,6 +987,7 @@ void testNode(struct node &node, struct node &expbuf_node, media_type type, printf("Buffer ioctls%s:\n", suffix); printf("\ttest VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: %s\n", ok(testReqBufs(&node))); printf("\ttest VIDIOC_EXPBUF: %s\n", ok(testExpBuf(&node))); + printf("\ttest Requests: %s\n", ok(testRequests(&node))); printf("\n"); } diff --git a/utils/v4l2-compliance/v4l2-compliance.h b/utils/v4l2-compliance/v4l2-compliance.h index f7c90c13..7ca46813 100644 --- a/utils/v4l2-compliance/v4l2-compliance.h +++ b/utils/v4l2-compliance/v4l2-compliance.h @@ -265,6 +265,7 @@ int testBlockingWait(struct node *node); int testMmap(struct node *node, unsigned frame_count); int testUserPtr(struct node *node, unsigned frame_count); int testDmaBuf(struct node *expbuf_node, struct node *node, unsigned frame_count); +int testRequests(struct node *node); void streamAllFormats(struct node *node); // Color tests diff --git a/utils/v4l2-compliance/v4l2-test-buffers.cpp b/utils/v4l2-compliance/v4l2-test-buffers.cpp index d654a7cd..dca21ae1 100644 --- a/utils/v4l2-compliance/v4l2-test-buffers.cpp +++ b/utils/v4l2-compliance/v4l2-test-buffers.cpp @@ -30,6 +30,7 @@ #include <fcntl.h> #include <ctype.h> #include <errno.h> +#include <poll.h> #include <sys/ioctl.h> #include <map> #include <vector> @@ -58,6 +59,22 @@ struct buf_seq { static struct buf_seq last_seq, last_m2m_seq; +static int buf_req_fds[VIDEO_MAX_FRAME * 2]; + +static inline int named_ioctl_fd(int fd, bool trace, const char *cmd_name, unsigned long cmd, void *arg) +{ + int retval; + int e; + + retval = ioctl(fd, cmd, arg); + e = retval == 0 ? 0 : errno; + if (trace) + fprintf(stderr, "\t\t%s returned %d (%s)\n", + cmd_name, retval, strerror(e)); + return retval == -1 ? e : (retval ? -1 : 0); +} +#define doioctl_fd(fd, r, p) named_ioctl_fd((fd), node->g_trace(), #r, r, p) + enum QueryBufMode { Unqueued, Prepared, @@ -148,7 +165,7 @@ private: s_timestamp_ts(ts); s_timestamp_src(V4L2_BUF_FLAG_TSTAMP_SRC_SOE); } - s_flags(V4L2_BUF_FLAG_TIMECODE | V4L2_BUF_FLAG_KEYFRAME); + s_flags(g_flags() | V4L2_BUF_FLAG_TIMECODE | V4L2_BUF_FLAG_KEYFRAME); tc.type = V4L2_TC_TYPE_30FPS; tc.flags = V4L2_TC_USERBITS_8BITCHARS; tc.frames = ts.tv_nsec * 30 / 1000000000; @@ -197,7 +214,11 @@ int buffer::check(unsigned type, unsigned memory, unsigned index, fail_on_test(g_memory() != memory); fail_on_test(g_index() >= VIDEO_MAX_FRAME); fail_on_test(g_index() != index); - fail_on_test(buf.reserved2 || buf.reserved); + fail_on_test(buf.reserved2); + if (g_flags() & V4L2_BUF_FLAG_REQUEST_FD) + fail_on_test(g_request_fd() < 0); + else + fail_on_test(g_request_fd()); fail_on_test(timestamp != V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC && timestamp != V4L2_BUF_FLAG_TIMESTAMP_COPY); fail_on_test(timestamp_src != V4L2_BUF_FLAG_TSTAMP_SRC_SOE && @@ -219,6 +240,11 @@ int buffer::check(unsigned type, unsigned memory, unsigned index, buf_states++; if (g_flags() & V4L2_BUF_FLAG_PREPARED) buf_states++; + if (g_flags() & V4L2_BUF_FLAG_IN_REQUEST) { + fail_on_test(!(g_flags() & V4L2_BUF_FLAG_REQUEST_FD)); + if (!(g_flags() & V4L2_BUF_FLAG_PREPARED)) + buf_states++; + } fail_on_test(buf_states > 1); fail_on_test(buf.length == 0); if (v4l_type_is_planar(g_type())) { @@ -679,8 +705,19 @@ static int captureBufs(struct node *node, const cv4l_queue &q, sizeof(orig_buf.timecode))); } fail_on_test(buf.g_flags() & V4L2_BUF_FLAG_DONE); + if (buf.g_flags() & V4L2_BUF_FLAG_REQUEST_FD) { + buf.querybuf(node, buf.g_index()); + fail_on_test(buf.g_flags() & V4L2_BUF_FLAG_REQUEST_FD); + fail_on_test(buf.g_request_fd()); + fail_on_test(!buf.qbuf(node)); + buf.s_flags(V4L2_BUF_FLAG_REQUEST_FD); + buf.s_request_fd(buf_req_fds[2 * frame_count - count]); + } fail_on_test(buf.qbuf(node)); fail_on_test(buf.g_flags() & V4L2_BUF_FLAG_DONE); + if (buf.g_flags() & V4L2_BUF_FLAG_REQUEST_FD) + fail_on_test(doioctl_fd(buf_req_fds[2 * frame_count - count], + MEDIA_REQUEST_IOC_QUEUE, 0)); if (--count == 0) break; } @@ -708,6 +745,7 @@ static int captureBufs(struct node *node, const cv4l_queue &q, sizeof(orig_buf.timecode))); } fail_on_test(buf.g_flags() & V4L2_BUF_FLAG_DONE); + buf.s_flags(buf.g_flags() & ~V4L2_BUF_FLAG_REQUEST_FD); fail_on_test(buf.qbuf(node)); fail_on_test(buf.g_flags() & V4L2_BUF_FLAG_DONE); } @@ -725,10 +763,12 @@ static int setupM2M(struct node *node, cv4l_queue &q) last_m2m_seq.init(); fail_on_test(q.reqbufs(node, 2)); + fail_on_test(node->streamon(q.g_type())); for (unsigned i = 0; i < q.g_buffers(); i++) { buffer buf(q); fail_on_test(buf.querybuf(node, i)); + buf.s_flags(buf.g_flags() & ~V4L2_BUF_FLAG_REQUEST_FD); fail_on_test(buf.qbuf(node)); fail_on_test(buf.g_flags() & V4L2_BUF_FLAG_DONE); } @@ -738,7 +778,6 @@ static int setupM2M(struct node *node, cv4l_queue &q) node->g_fmt(fmt); last_m2m_seq.last_field = fmt.g_field(); } - fail_on_test(node->streamon(q.g_type())); return 0; } @@ -882,6 +921,8 @@ int testMmap(struct node *node, unsigned frame_count) fail_on_test(buf.querybuf(node, i)); fail_on_test(buf.qbuf(node)); fail_on_test(buf.g_flags() & V4L2_BUF_FLAG_DONE); + fail_on_test(buf.g_flags() & V4L2_BUF_FLAG_REQUEST_FD); + fail_on_test(buf.g_request_fd()); } // calling STREAMOFF... fail_on_test(node->streamoff(q.g_type())); @@ -1171,6 +1212,267 @@ int testDmaBuf(struct node *expbuf_node, struct node *node, unsigned frame_count return 0; } +int testRequests(struct node *node) +{ + int media_fd = mi_get_media_fd(node->g_fd()); + int req_fd; + qctrl_map::iterator iter; + struct test_query_ext_ctrl valid_qctrl; + v4l2_ext_controls ctrls; + v4l2_ext_control ctrl; + bool have_controls; + int ret; + + memset(&valid_qctrl, 0, sizeof(valid_qctrl)); + memset(&ctrls, 0, sizeof(ctrls)); + memset(&ctrl, 0, sizeof(ctrl)); + for (iter = node->controls.begin(); iter != node->controls.end(); ++iter) { + test_query_ext_ctrl &qctrl = iter->second; + + if (qctrl.type != V4L2_CTRL_TYPE_INTEGER && + qctrl.type != V4L2_CTRL_TYPE_BOOLEAN) + continue; + if (qctrl.minimum != qctrl.maximum) { + valid_qctrl = qctrl; + ctrl.id = qctrl.id; + break; + } + } + + ctrls.which = V4L2_CTRL_WHICH_REQUEST_VAL; + ret = doioctl(node, VIDIOC_G_EXT_CTRLS, &ctrls); + fail_on_test(ret != EINVAL && ret != EACCES && ret != ENOTTY); + have_controls = ret != ENOTTY; + + if (media_fd < 0 || ret == EACCES) + return ENOTTY; + if (have_controls) { + ctrls.request_fd = 10; + doioctl(node, VIDIOC_G_EXT_CTRLS, &ctrls); + fail_on_test(doioctl(node, VIDIOC_G_EXT_CTRLS, &ctrls) != EINVAL); + } + fail_on_test(doioctl_fd(media_fd, MEDIA_IOC_REQUEST_ALLOC, &req_fd)); + fail_on_test(req_fd < 0); + if (have_controls) { + ctrls.request_fd = req_fd; + fail_on_test(doioctl(node, VIDIOC_G_EXT_CTRLS, &ctrls) != EACCES); + } + fail_on_test(doioctl_fd(req_fd, MEDIA_REQUEST_IOC_QUEUE, 0) != ENOENT); + fail_on_test(doioctl_fd(req_fd, MEDIA_REQUEST_IOC_REINIT, 0)); + close(media_fd); + fail_on_test(doioctl_fd(req_fd, MEDIA_REQUEST_IOC_QUEUE, 0) != ENOENT); + fail_on_test(doioctl_fd(req_fd, MEDIA_REQUEST_IOC_REINIT, 0)); + close(req_fd); + if (have_controls) + fail_on_test(doioctl(node, VIDIOC_G_EXT_CTRLS, &ctrls) != EINVAL); + fail_on_test(doioctl_fd(req_fd, MEDIA_REQUEST_IOC_QUEUE, 0) != EBADF); + fail_on_test(doioctl_fd(req_fd, MEDIA_REQUEST_IOC_REINIT, 0) != EBADF); + + media_fd = mi_get_media_fd(node->g_fd()); + fail_on_test(doioctl_fd(media_fd, MEDIA_IOC_REQUEST_ALLOC, &req_fd)); + ctrls.count = 1; + ctrls.controls = &ctrl; + if (have_controls) { + ctrl.value = valid_qctrl.minimum; + ctrls.which = 0; + fail_on_test(doioctl(node, VIDIOC_S_EXT_CTRLS, &ctrls)); + ctrl.value = valid_qctrl.maximum; + ctrls.which = V4L2_CTRL_WHICH_REQUEST_VAL; + ctrls.request_fd = req_fd; + fail_on_test(doioctl(node, VIDIOC_S_EXT_CTRLS, &ctrls)); + ctrl.value = valid_qctrl.minimum; + ctrls.request_fd = req_fd; + fail_on_test(doioctl(node, VIDIOC_G_EXT_CTRLS, &ctrls) != EACCES); + ctrls.which = 0; + fail_on_test(doioctl(node, VIDIOC_G_EXT_CTRLS, &ctrls)); + fail_on_test(ctrl.value != valid_qctrl.minimum); + ctrls.request_fd = req_fd; + ctrls.which = V4L2_CTRL_WHICH_REQUEST_VAL; + ctrl.id = 1; + fail_on_test(!doioctl(node, VIDIOC_S_EXT_CTRLS, &ctrls)); + fail_on_test(doioctl(node, VIDIOC_G_EXT_CTRLS, &ctrls) != EACCES); + } + ctrl.id = valid_qctrl.id; + close(req_fd); + close(media_fd); + node->reopen(); + + int type = node->is_planar ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE : + V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (node->can_output) + type = v4l_type_invert(type); + + if (!(node->valid_buftypes & (1 << type))) + return ENOTTY; + + buffer_info.clear(); + + cv4l_queue q(type, V4L2_MEMORY_MMAP); + cv4l_queue m2m_q(v4l_type_invert(type)); + + q.init(type, V4L2_MEMORY_MMAP); + fail_on_test(q.reqbufs(node, 2)); + unsigned num_bufs = q.g_buffers(); + unsigned num_requests = 2 * num_bufs; + last_seq.init(); + + media_fd = mi_get_media_fd(node->g_fd()); + + for (unsigned i = 0; i < num_requests; i++) { + fail_on_test(doioctl_fd(media_fd, MEDIA_IOC_REQUEST_ALLOC, &buf_req_fds[i])); + fail_on_test(buf_req_fds[i] < 0); + fail_on_test(!doioctl_fd(buf_req_fds[i], MEDIA_REQUEST_IOC_QUEUE, 0)); + } + close(media_fd); + + buffer buf(q); + + fail_on_test(buf.querybuf(node, 0)); + fail_on_test(buf.qbuf(node)); + fail_on_test(buf.querybuf(node, 1)); + buf.s_flags(V4L2_BUF_FLAG_REQUEST_FD); + buf.s_request_fd(buf_req_fds[1]); + fail_on_test(!buf.qbuf(node)); + + node->reopen(); + + q.init(type, V4L2_MEMORY_MMAP); + fail_on_test(q.reqbufs(node, 2)); + + fail_on_test(node->streamon(q.g_type())); + if (node->is_m2m) { + fail_on_test(m2m_q.reqbufs(node, 2)); + fail_on_test(node->streamon(m2m_q.g_type())); + + buffer buf(m2m_q); + + fail_on_test(buf.querybuf(node, 0)); + buf.s_flags(V4L2_BUF_FLAG_REQUEST_FD); + buf.s_request_fd(buf_req_fds[0]); + fail_on_test(!buf.qbuf(node)); + fail_on_test(node->streamoff(m2m_q.g_type())); + fail_on_test(m2m_q.reqbufs(node, 0)); + + fail_on_test(setupM2M(node, m2m_q)); + } + + ctrls.which = V4L2_CTRL_WHICH_REQUEST_VAL; + // Test queuing buffers... + for (unsigned i = 0; i < num_bufs; i++) { + buffer buf(q); + + fail_on_test(buf.querybuf(node, i)); + fail_on_test(buf.g_request_fd()); + buf.s_flags(V4L2_BUF_FLAG_REQUEST_FD); + if (i == 0) { + buf.s_request_fd(-1); + fail_on_test(!buf.qbuf(node)); + buf.s_request_fd(0xdead); + fail_on_test(!buf.qbuf(node)); + } + buf.s_request_fd(buf_req_fds[i]); + if (!(i & 1)) { + fail_on_test(buf.prepare_buf(node) != EINVAL); + buf.s_flags(0); + fail_on_test(buf.prepare_buf(node)); + fail_on_test(buf.querybuf(node, i)); + fail_on_test(!(buf.g_flags() & V4L2_BUF_FLAG_PREPARED)); + buf.s_flags(buf.g_flags() | V4L2_BUF_FLAG_REQUEST_FD); + buf.s_request_fd(buf_req_fds[i]); + } + fail_on_test(buf.qbuf(node)); + fail_on_test(buf.g_flags() & V4L2_BUF_FLAG_DONE); + fail_on_test(!(buf.g_flags() & V4L2_BUF_FLAG_IN_REQUEST)); + fail_on_test(!(buf.g_flags() & V4L2_BUF_FLAG_REQUEST_FD)); + fail_on_test(buf.g_request_fd() < 0); + fail_on_test(buf.querybuf(node, i)); + fail_on_test(buf.g_flags() & V4L2_BUF_FLAG_DONE); + fail_on_test(!(buf.g_flags() & V4L2_BUF_FLAG_IN_REQUEST)); + fail_on_test(!(buf.g_flags() & V4L2_BUF_FLAG_REQUEST_FD)); + fail_on_test(buf.g_request_fd() < 0); + if (i & 1) + fail_on_test(buf.g_flags() & V4L2_BUF_FLAG_PREPARED); + else + fail_on_test(!(buf.g_flags() & V4L2_BUF_FLAG_PREPARED)); + buf.s_request_fd(buf_req_fds[i]); + fail_on_test(!buf.qbuf(node)); + + ctrl.value = (i & 1) ? valid_qctrl.maximum : valid_qctrl.minimum; + ctrls.request_fd = buf_req_fds[i]; + fail_on_test(doioctl(node, VIDIOC_S_EXT_CTRLS, &ctrls)); + fail_on_test(doioctl_fd(buf_req_fds[i], MEDIA_REQUEST_IOC_REINIT, 0)); + + fail_on_test(buf.querybuf(node, i)); + fail_on_test(buf.g_flags() & V4L2_BUF_FLAG_IN_REQUEST); + fail_on_test(buf.g_flags() & V4L2_BUF_FLAG_REQUEST_FD); + + ctrls.request_fd = buf_req_fds[i]; + fail_on_test(doioctl(node, VIDIOC_S_EXT_CTRLS, &ctrls)); + + if (i) + fail_on_test(!buf.qbuf(node)); + buf.s_flags(buf.g_flags() | V4L2_BUF_FLAG_REQUEST_FD); + buf.s_request_fd(buf_req_fds[i]); + fail_on_test(buf.qbuf(node)); + fail_on_test(buf.querybuf(node, i)); + fail_on_test(!(buf.g_flags() & V4L2_BUF_FLAG_IN_REQUEST)); + fail_on_test(!(buf.g_flags() & V4L2_BUF_FLAG_REQUEST_FD)); + fail_on_test(doioctl_fd(buf_req_fds[i], MEDIA_REQUEST_IOC_QUEUE, 0)); + fail_on_test(buf.querybuf(node, i)); + fail_on_test(buf.g_flags() & V4L2_BUF_FLAG_IN_REQUEST); + fail_on_test(!(buf.g_flags() & V4L2_BUF_FLAG_REQUEST_FD)); + fail_on_test(!(buf.g_flags() & V4L2_BUF_FLAG_QUEUED)); + fail_on_test(doioctl_fd(buf_req_fds[i], MEDIA_REQUEST_IOC_REINIT, 0) != EBUSY); + fail_on_test(doioctl_fd(buf_req_fds[i], MEDIA_REQUEST_IOC_QUEUE, 0) != EBUSY); + } + + fail_on_test(node->g_fmt(cur_fmt, q.g_type())); + + fail_on_test(captureBufs(node, q, m2m_q, num_bufs, true)); + fail_on_test(node->streamoff(q.g_type())); + + for (unsigned i = 0; i < num_bufs; i++) { + buffer buf(q); + + ctrls.request_fd = buf_req_fds[i]; + fail_on_test(doioctl(node, VIDIOC_G_EXT_CTRLS, &ctrls)); + fail_on_test(ctrl.value != ((i & 1) ? valid_qctrl.maximum : + valid_qctrl.minimum)); + fail_on_test(buf.querybuf(node, i)); + fail_on_test(buf.g_flags() & V4L2_BUF_FLAG_REQUEST_FD); + fail_on_test(buf.g_request_fd()); + struct pollfd pfd = { + buf_req_fds[i], + POLLPRI, 0 + }; + fail_on_test(poll(&pfd, 1, 100) != 1); + fail_on_test(doioctl_fd(buf_req_fds[i], MEDIA_REQUEST_IOC_QUEUE, 0) != EBUSY); + fail_on_test(doioctl_fd(buf_req_fds[i], MEDIA_REQUEST_IOC_REINIT, 0)); + fail_on_test(buf.querybuf(node, i)); + fail_on_test(buf.g_flags() & V4L2_BUF_FLAG_REQUEST_FD); + fail_on_test(buf.g_request_fd()); + close(buf_req_fds[i]); + ctrls.request_fd = buf_req_fds[i]; + fail_on_test(!doioctl(node, VIDIOC_G_EXT_CTRLS, &ctrls)); + } + for (unsigned i = num_bufs; i < num_requests; i++) + close(buf_req_fds[i]); + + ctrls.which = 0; + fail_on_test(doioctl(node, VIDIOC_G_EXT_CTRLS, &ctrls)); + fail_on_test(ctrl.value != (((num_bufs - 1) & 1) ? valid_qctrl.maximum : + valid_qctrl.minimum)); + + fail_on_test(q.reqbufs(node, 0)); + if (node->is_m2m) { + fail_on_test(node->streamoff(m2m_q.g_type())); + m2m_q.munmap_bufs(node); + fail_on_test(m2m_q.reqbufs(node, 0)); + } + + return 0; +} + static int testBlockingDQBuf(struct node *node, cv4l_queue &q) { int pid_dqbuf; |