From 3852b5872d84e5bfde53666d607616714fa1beb2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 11 Feb 2014 08:36:05 +0100 Subject: [PATCH] v4l2-compliance: add streaming tests Add tests for the streaming ioctls. If the -s flag is specified then the streaming tests are enabled. These assume a valid input/output and/or frequency. Options to set those are also added. Currently only single plane video capture is supported. This is however 90% of all drivers, so this should be OK for now while work progresses on other formats. Also the DMABUF streaming is not tested. Signed-off-by: Hans Verkuil --- utils/v4l2-compliance/v4l2-compliance.cpp | 78 ++++- utils/v4l2-compliance/v4l2-compliance.h | 2 + utils/v4l2-compliance/v4l2-test-buffers.cpp | 459 +++++++++++++++++++++++++++- 3 files changed, 514 insertions(+), 25 deletions(-) diff --git a/utils/v4l2-compliance/v4l2-compliance.cpp b/utils/v4l2-compliance/v4l2-compliance.cpp index d9fef7b..28af6c9 100644 --- a/utils/v4l2-compliance/v4l2-compliance.cpp +++ b/utils/v4l2-compliance/v4l2-compliance.cpp @@ -45,10 +45,13 @@ case is used to retrieve a setting. */ enum Option { OptSetDevice = 'd', + OptSetFreq = 'f', OptHelp = 'h', + OptSetInput = 'i', OptNoWarnings = 'n', + OptSetOutput = 'o', OptSetRadioDevice = 'r', - OptTest = 't', + OptStreaming = 's', OptTrace = 'T', OptVerbose = 'v', OptSetVbiDevice = 'V', @@ -68,6 +71,10 @@ bool wrapper; int kernel_version; unsigned warnings; +static unsigned select_input; +static unsigned select_output; +static double select_freq; + static struct option long_options[] = { {"device", required_argument, 0, OptSetDevice}, {"radio-device", required_argument, 0, OptSetRadioDevice}, @@ -77,6 +84,10 @@ static struct option long_options[] = { {"no-warnings", no_argument, 0, OptNoWarnings}, {"trace", no_argument, 0, OptTrace}, {"wrapper", no_argument, 0, OptUseWrapper}, + {"set-input", required_argument, 0, OptSetInput}, + {"set-output", required_argument, 0, OptSetOutput}, + {"set-freq", required_argument, 0, OptSetFreq}, + {"streaming", no_argument, 0, OptStreaming}, {0, 0, 0, 0} }; @@ -84,13 +95,18 @@ static void usage(void) { printf("Usage:\n"); printf("Common options:\n"); - printf(" -d, --device= use device as the video device\n"); - printf(" if starts with a digit, then /dev/video is used\n"); - printf(" -r, --radio-device= use device as the radio device\n"); - printf(" if starts with a digit, then /dev/radio is used\n"); - printf(" -V, --vbi-device= use device as the vbi device\n"); - printf(" if starts with a digit, then /dev/vbi is used\n"); - printf(" -h, --help display this help message\n"); + printf(" -d, --device= use device as the video device.\n"); + printf(" if starts with a digit, then /dev/video is used.\n"); + printf(" -r, --radio-device= use device as the radio device.\n"); + printf(" if starts with a digit, then /dev/radio is used.\n"); + printf(" -V, --vbi-device= use device as the vbi device.\n"); + printf(" if starts with a digit, then /dev/vbi is used.\n"); + printf(" -i, --set-input select input for streaming tests (default is 0).\n"); + printf(" -o, --set-output select output for streaming tests (default is 0).\n"); + printf(" -f, --set-freq select frequency in MHz (kHz for radio) for streaming tests.\n"); + printf(" -s, --streaming enable the streaming tests. Requires a valid input/output and\n"); + printf(" frequency (when dealing with a tuner).\n"); + printf(" -h, --help display this help message.\n"); printf(" -n, --no-warnings turn off warning messages.\n"); printf(" -T, --trace trace all called ioctls.\n"); printf(" -v, --verbose turn on verbose reporting.\n"); @@ -445,6 +461,15 @@ int main(int argc, char **argv) vbi_device = newdev; } break; + case OptSetInput: + select_input = strtoul(optarg, NULL, 0); + break; + case OptSetOutput: + select_output = strtoul(optarg, NULL, 0); + break; + case OptSetFreq: + select_freq = strtod(optarg, NULL); + break; case OptNoWarnings: show_warnings = false; break; @@ -685,15 +710,46 @@ int main(int argc, char **argv) printf("Buffer ioctls:\n"); printf("\ttest VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: %s\n", ok(testReqBufs(&node))); - //printf("\ttest read/write: %s\n", ok(testReadWrite(&node))); + if (options[OptStreaming]) { + if (options[OptSetInput]) + doioctl(&node, VIDIOC_S_INPUT, &select_input); + if (options[OptSetOutput]) + doioctl(&node, VIDIOC_S_OUTPUT, &select_output); + if (options[OptSetFreq]) { + struct v4l2_frequency f = { 0 }; + unsigned freq_caps; + + if (node.caps & V4L2_CAP_MODULATOR) { + struct v4l2_modulator m = { 0 }; + + doioctl(&node, VIDIOC_G_MODULATOR, &m); + freq_caps = m.capability; + f.type = V4L2_TUNER_RADIO; + } else { + struct v4l2_tuner t = { 0 }; + + doioctl(&node, VIDIOC_G_TUNER, &t); + f.type = t.type; + freq_caps = t.capability; + } + if (freq_caps & V4L2_TUNER_CAP_LOW) + f.frequency = select_freq / 62.5; + else + f.frequency = select_freq / 62500; + doioctl(&node, VIDIOC_S_FREQUENCY, &f); + } + + printf("\ttest read/write: %s\n", ok(testReadWrite(&node))); + printf("\ttest MMAP: %s\n", ok(testMmap(&node))); + printf("\ttest USERPTR: %s\n", ok(testUserPtr(&node))); + } printf("\n"); /* TODO: VIDIOC_CROPCAP, VIDIOC_G/S_CROP, VIDIOC_G/S_SELECTION VIDIOC_S_FBUF/OVERLAY - VIDIOC_QBUF/DQBUF/QUERYBUF/PREPARE_BUFS/EXPBUF - VIDIOC_STREAMON/OFF + VIDIOC_EXPBUF */ /* Final test report */ diff --git a/utils/v4l2-compliance/v4l2-compliance.h b/utils/v4l2-compliance/v4l2-compliance.h index f9b5291..c53e4a0 100644 --- a/utils/v4l2-compliance/v4l2-compliance.h +++ b/utils/v4l2-compliance/v4l2-compliance.h @@ -200,5 +200,7 @@ int testDecoder(struct node *node); // Buffer ioctl tests int testReqBufs(struct node *node); int testReadWrite(struct node *node); +int testMmap(struct node *node); +int testUserPtr(struct node *node); #endif diff --git a/utils/v4l2-compliance/v4l2-test-buffers.cpp b/utils/v4l2-compliance/v4l2-test-buffers.cpp index b7e84cf..be7cb2b 100644 --- a/utils/v4l2-compliance/v4l2-test-buffers.cpp +++ b/utils/v4l2-compliance/v4l2-test-buffers.cpp @@ -25,33 +25,194 @@ #include #include #include +#include #include #include #include #include #include "v4l2-compliance.h" +static inline void *test_mmap(void *start, size_t length, int prot, int flags, + int fd, int64_t offset) +{ + return wrapper ? v4l2_mmap(start, length, prot, flags, fd, offset) : + mmap(start, length, prot, flags, fd, offset); +} + +static void *ptrs[VIDEO_MAX_FRAME]; +static struct v4l2_format cur_fmt; +static int last_seq; +static unsigned last_field; +static unsigned field_nr; + +enum QueryBufMode { + Unqueued, + Prepared, + Queued, + Dequeued +}; + +static std::string num2s(unsigned num) +{ + char buf[10]; + + sprintf(buf, "%08x", num); + return buf; +} + +static std::string field2s(unsigned 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 int checkQueryBuf(struct node *node, const struct v4l2_buffer &buf, + __u32 type, __u32 memory, unsigned index, enum QueryBufMode mode, + unsigned count) +{ + unsigned timestamp = buf.flags & V4L2_BUF_FLAG_TIMESTAMP_MASK; + unsigned frame_types = 0; + unsigned buf_states = 0; + + fail_on_test(buf.type != type); + fail_on_test(buf.memory == 0); + fail_on_test(buf.memory != memory); + fail_on_test(buf.index >= VIDEO_MAX_FRAME); + fail_on_test(buf.index != index); + fail_on_test(buf.reserved2 || buf.reserved); + fail_on_test(timestamp != V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC && + timestamp != V4L2_BUF_FLAG_TIMESTAMP_COPY); + if (!node->is_m2m) + fail_on_test(timestamp == V4L2_BUF_FLAG_TIMESTAMP_COPY); + if (buf.flags & V4L2_BUF_FLAG_KEYFRAME) + frame_types++; + if (buf.flags & V4L2_BUF_FLAG_PFRAME) + frame_types++; + if (buf.flags & V4L2_BUF_FLAG_BFRAME) + frame_types++; + fail_on_test(frame_types > 1); + fail_on_test(buf.flags & (V4L2_BUF_FLAG_NO_CACHE_INVALIDATE | + V4L2_BUF_FLAG_NO_CACHE_CLEAN)); + if (buf.flags & V4L2_BUF_FLAG_QUEUED) + buf_states++; + if (buf.flags & V4L2_BUF_FLAG_DONE) + buf_states++; + if (buf.flags & V4L2_BUF_FLAG_ERROR) + buf_states++; + if (buf.flags & V4L2_BUF_FLAG_PREPARED) + buf_states++; + fail_on_test(buf_states > 1); + fail_on_test(buf.length == 0); + if (V4L2_TYPE_IS_MULTIPLANAR(type)) { + fail_on_test(buf.length > VIDEO_MAX_PLANES); + for (unsigned p = 0; p < buf.length; p++) { + struct v4l2_plane *vp = buf.m.planes + p; + + fail_on_test(check_0(vp->reserved, sizeof(vp->reserved))); + fail_on_test(vp->length == 0); + } + } + + if (mode == Dequeued || mode == Prepared) { + fail_on_test(!(buf.flags & (V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR))); + if (V4L2_TYPE_IS_MULTIPLANAR(type)) { + fail_on_test(buf.length <= VIDEO_MAX_PLANES); + for (unsigned p = 0; p < buf.length; p++) { + struct v4l2_plane *vp = buf.m.planes + p; + + if (buf.memory == V4L2_MEMORY_USERPTR) + fail_on_test((void *)vp->m.userptr != ptrs[buf.index]); + fail_on_test(vp->data_offset + vp->bytesused > vp->length); + } + } else { + if (buf.memory == V4L2_MEMORY_USERPTR) + fail_on_test((void *)buf.m.userptr != ptrs[buf.index]); + } + } + + if (mode == Dequeued) { + fail_on_test(!buf.bytesused); + fail_on_test(buf.bytesused > buf.length); + fail_on_test(!buf.timestamp.tv_sec && !buf.timestamp.tv_usec); + fail_on_test(buf.field == V4L2_FIELD_ALTERNATE); + fail_on_test(buf.field == V4L2_FIELD_ANY); + if (cur_fmt.fmt.pix.field == V4L2_FIELD_ALTERNATE) { + fail_on_test(buf.field != V4L2_FIELD_BOTTOM && + buf.field != V4L2_FIELD_TOP); + fail_on_test(buf.field == last_field); + field_nr ^= 1; + if (field_nr) + fail_on_test((int)buf.sequence != last_seq); + else + fail_on_test((int)buf.sequence != last_seq + 1); + } else { + fail_on_test(buf.field != cur_fmt.fmt.pix.field); + fail_on_test((int)buf.sequence != last_seq + 1); + } + last_seq = (int)buf.sequence; + last_field = buf.field; + + if (buf.flags & V4L2_BUF_FLAG_TIMECODE) + warn("V4L2_BUF_FLAG_TIMECODE was used!\n"); + } else { + fail_on_test(buf.sequence); + fail_on_test(buf.timestamp.tv_sec || buf.timestamp.tv_usec); + fail_on_test(buf.flags & V4L2_BUF_FLAG_TIMECODE); + fail_on_test(frame_types); + if (mode == Unqueued) + fail_on_test(buf.flags & (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_PREPARED | + V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR)); + else if (mode == Prepared) + fail_on_test((buf.flags & (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_PREPARED | + V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR)) != + V4L2_BUF_FLAG_PREPARED); + else + fail_on_test(!(buf.flags & (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_PREPARED))); + } + return 0; +} + static int testQueryBuf(struct node *node, unsigned type, unsigned count) { + struct v4l2_plane planes[VIDEO_MAX_PLANES]; struct v4l2_buffer buf; int ret; unsigned i; memset(&buf, 0, sizeof(buf)); buf.type = type; + if (V4L2_TYPE_IS_MULTIPLANAR(type)) { + buf.m.planes = planes; + buf.length = VIDEO_MAX_PLANES; + } for (i = 0; i < count; i++) { - unsigned timestamp; - buf.index = i; fail_on_test(doioctl(node, VIDIOC_QUERYBUF, &buf)); - timestamp = buf.flags & V4L2_BUF_FLAG_TIMESTAMP_MASK; - fail_on_test(buf.index != i); - fail_on_test(buf.type != type); - fail_on_test(buf.flags & (V4L2_BUF_FLAG_QUEUED | - V4L2_BUF_FLAG_DONE | - V4L2_BUF_FLAG_ERROR)); - fail_on_test(timestamp != V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC && - timestamp != V4L2_BUF_FLAG_TIMESTAMP_COPY); + if (V4L2_TYPE_IS_MULTIPLANAR(type)) + fail_on_test(buf.m.planes != planes); + fail_on_test(checkQueryBuf(node, buf, type, buf.memory, i, Unqueued, 0)); } buf.index = count; ret = doioctl(node, VIDIOC_QUERYBUF, &buf); @@ -59,6 +220,7 @@ static int testQueryBuf(struct node *node, unsigned type, unsigned count) return 0; } + int testReqBufs(struct node *node) { struct v4l2_requestbuffers bufs; @@ -125,7 +287,7 @@ int testReqBufs(struct node *node) fail_on_test(bufs.memory != V4L2_MEMORY_MMAP); fail_on_test(bufs.type != i); fail_on_test(doioctl(node, VIDIOC_REQBUFS, &bufs)); - testQueryBuf(node, i, bufs.count); + fail_on_test(testQueryBuf(node, i, bufs.count)); } if (userptr_valid) { @@ -136,7 +298,7 @@ int testReqBufs(struct node *node) fail_on_test(bufs.memory != V4L2_MEMORY_USERPTR); fail_on_test(bufs.type != i); fail_on_test(doioctl(node, VIDIOC_REQBUFS, &bufs)); - testQueryBuf(node, i, bufs.count); + fail_on_test(testQueryBuf(node, i, bufs.count)); } if (dmabuf_valid) { @@ -147,7 +309,7 @@ int testReqBufs(struct node *node) fail_on_test(bufs.memory != V4L2_MEMORY_DMABUF); fail_on_test(bufs.type != i); fail_on_test(doioctl(node, VIDIOC_REQBUFS, &bufs)); - testQueryBuf(node, i, bufs.count); + fail_on_test(testQueryBuf(node, i, bufs.count)); } if (can_rw) { @@ -184,7 +346,7 @@ int testReqBufs(struct node *node) fail_on_test(cbufs.count == 0); fail_on_test(cbufs.memory != bufs.memory); fail_on_test(cbufs.format.type != i); - testQueryBuf(node, i, cbufs.count); + fail_on_test(testQueryBuf(node, i, cbufs.count)); cbufs.count = 1; fail_on_test(doioctl(node, VIDIOC_CREATE_BUFS, &cbufs)); if (!node->is_m2m) { @@ -231,3 +393,272 @@ rw_exit: reopen(node); return 0; } + +static int setupMmap(struct node *node, struct v4l2_requestbuffers &bufs) +{ + for (unsigned i = 0; i < bufs.count; i++) { + struct v4l2_plane planes[VIDEO_MAX_PLANES]; + struct v4l2_buffer buf; + int ret; + + memset(&buf, 0, sizeof(buf)); + buf.type = bufs.type; + buf.memory = bufs.memory; + buf.index = i; + if (V4L2_TYPE_IS_MULTIPLANAR(bufs.type)) { + buf.m.planes = planes; + buf.length = VIDEO_MAX_PLANES; + } + fail_on_test(doioctl(node, VIDIOC_QUERYBUF, &buf)); + fail_on_test(checkQueryBuf(node, buf, bufs.type, bufs.memory, i, Unqueued, 0)); + ptrs[i] = test_mmap(NULL, buf.length, + PROT_READ | PROT_WRITE, MAP_SHARED, node->fd, buf.m.offset); + + fail_on_test(ptrs[i] == MAP_FAILED); + + ret = doioctl(node, VIDIOC_PREPARE_BUF, &buf); + fail_on_test(ret && ret != ENOTTY); + if (ret == 0) { + fail_on_test(doioctl(node, VIDIOC_QUERYBUF, &buf)); + fail_on_test(checkQueryBuf(node, buf, bufs.type, bufs.memory, i, Prepared, 0)); + } + + fail_on_test(doioctl(node, VIDIOC_QBUF, &buf)); + fail_on_test(doioctl(node, VIDIOC_QUERYBUF, &buf)); + fail_on_test(checkQueryBuf(node, buf, bufs.type, bufs.memory, i, Queued, 0)); + } + return 0; +} + +static int releaseMmap(struct node *node, struct v4l2_requestbuffers &bufs) +{ + for (unsigned i = 0; i < bufs.count; i++) { + struct v4l2_plane planes[VIDEO_MAX_PLANES]; + struct v4l2_buffer buf; + + memset(&buf, 0, sizeof(buf)); + buf.type = bufs.type; + buf.memory = bufs.memory; + buf.index = i; + if (V4L2_TYPE_IS_MULTIPLANAR(bufs.type)) { + buf.m.planes = planes; + buf.length = VIDEO_MAX_PLANES; + } + fail_on_test(doioctl(node, VIDIOC_QUERYBUF, &buf)); + munmap(ptrs[i], buf.length); + } + return 0; +} + +static int captureBufs(struct node *node, const struct v4l2_requestbuffers &bufs, + unsigned count, bool use_poll) +{ + int fd_flags = fcntl(node->fd, F_GETFL); + struct v4l2_buffer buf; + int ret; + + if (use_poll) + fcntl(node->fd, F_SETFL, fd_flags | O_NONBLOCK); + for (;;) { + if (use_poll) { + struct timeval tv = { 2, 0 }; + fd_set read_fds; + + FD_ZERO(&read_fds); + FD_SET(node->fd, &read_fds); + ret = select(node->fd + 1, &read_fds, NULL, NULL, &tv); + fail_on_test(ret <= 0); + fail_on_test(!FD_ISSET(node->fd, &read_fds)); + } + + buf.type = bufs.type; + buf.memory = bufs.memory; + + ret = doioctl(node, VIDIOC_DQBUF, &buf); + if (ret == EAGAIN) + continue; + if (show_info) + printf("\t\tBuffer: %d Sequence: %d Field: %s Timestamp: %ld.%06lds\n", + buf.index, buf.sequence, field2s(buf.field).c_str(), + buf.timestamp.tv_sec, buf.timestamp.tv_usec); + fail_on_test(ret); + fail_on_test(checkQueryBuf(node, buf, bufs.type, bufs.memory, buf.index, Dequeued, 100 - count)); + if (!show_info) { + printf("\r\tFrame #%03d%s", 100 - count, use_poll ? " (polling)" : ""); + fflush(stdout); + } + fail_on_test(doioctl(node, VIDIOC_QBUF, &buf)); + if (--count == 0) + break; + } + if (use_poll) + fcntl(node->fd, F_SETFL, fd_flags); + if (!show_info) + printf("\r\t \r"); + return 0; +} + +int testMmap(struct node *node) +{ + struct v4l2_requestbuffers bufs; + struct v4l2_create_buffers cbufs; + struct v4l2_input input; + bool can_stream = node->caps & V4L2_CAP_STREAMING; + bool have_createbufs = true; + int ret; + + if (!(node->caps & V4L2_CAP_VIDEO_CAPTURE)) + return 0; + + memset(&input, 0, sizeof(input)); + doioctl(node, VIDIOC_G_INPUT, &input.index); + doioctl(node, VIDIOC_ENUMINPUT, &input); + + if (input.capabilities & V4L2_IN_CAP_STD) { + v4l2_std_id std; + + doioctl(node, VIDIOC_QUERYSTD, &std); + if (std) + doioctl(node, VIDIOC_S_STD, &std); + } + + if (input.capabilities & V4L2_IN_CAP_DV_TIMINGS) { + struct v4l2_dv_timings t; + + if (doioctl(node, VIDIOC_QUERY_DV_TIMINGS, &t) == 0) + doioctl(node, VIDIOC_S_DV_TIMINGS, &t); + } + + memset(&bufs, 0, sizeof(bufs)); + bufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + bufs.memory = V4L2_MEMORY_MMAP; + ret = doioctl(node, VIDIOC_REQBUFS, &bufs); + if (ret) { + fail_on_test(can_stream); + return ret; + } + + fail_on_test(doioctl(node, VIDIOC_STREAMON, &bufs.type)); + fail_on_test(doioctl(node, VIDIOC_STREAMOFF, &bufs.type)); + + cur_fmt.type = bufs.type; + doioctl(node, VIDIOC_G_FMT, &cur_fmt); + bufs.count = 1; + fail_on_test(doioctl(node, VIDIOC_REQBUFS, &bufs)); + fail_on_test(doioctl(node, VIDIOC_STREAMOFF, &bufs.type)); + last_seq = -1; + field_nr = 1; + last_field = cur_fmt.fmt.pix.field; + + cbufs.format = cur_fmt; + cbufs.format.fmt.pix.height /= 2; + cbufs.format.fmt.pix.sizeimage /= 2; + cbufs.count = 1; + cbufs.memory = bufs.memory; + ret = doioctl(node, VIDIOC_CREATE_BUFS, &cbufs); + if (ret == ENOTTY) + have_createbufs = false; + else + fail_on_test(ret != EINVAL); + fail_on_test(testQueryBuf(node, cur_fmt.type, bufs.count)); + if (have_createbufs) { + cbufs.format = cur_fmt; + cbufs.format.fmt.pix.sizeimage *= 2; + cbufs.count = 1; + cbufs.memory = bufs.memory; + fail_on_test(doioctl(node, VIDIOC_CREATE_BUFS, &cbufs)); + } + fail_on_test(setupMmap(node, bufs)); + + fail_on_test(doioctl(node, VIDIOC_STREAMON, &bufs.type)); + fail_on_test(doioctl(node, VIDIOC_STREAMON, &bufs.type)); + fail_on_test(captureBufs(node, bufs, 100, false)); + fail_on_test(captureBufs(node, bufs, 100, true)); + fail_on_test(doioctl(node, VIDIOC_STREAMOFF, &bufs.type)); + fail_on_test(doioctl(node, VIDIOC_STREAMOFF, &bufs.type)); + fail_on_test(releaseMmap(node, bufs)); + bufs.count = 0; + fail_on_test(doioctl(node, VIDIOC_REQBUFS, &bufs)); + return 0; +} + +static int setupUserPtr(struct node *node, struct v4l2_requestbuffers &bufs) +{ + for (unsigned i = 0; i < bufs.count; i++) { + struct v4l2_plane planes[VIDEO_MAX_PLANES]; + struct v4l2_buffer buf; + int ret; + + memset(&buf, 0, sizeof(buf)); + buf.type = bufs.type; + buf.memory = bufs.memory; + buf.index = i; + if (V4L2_TYPE_IS_MULTIPLANAR(bufs.type)) { + buf.m.planes = planes; + buf.length = VIDEO_MAX_PLANES; + } + fail_on_test(doioctl(node, VIDIOC_QUERYBUF, &buf)); + fail_on_test(checkQueryBuf(node, buf, bufs.type, bufs.memory, i, Unqueued, 0)); + ptrs[i] = malloc(buf.length); + fail_on_test(ptrs[i] == NULL); + buf.m.userptr = (unsigned long)ptrs[i]; + + ret = doioctl(node, VIDIOC_PREPARE_BUF, &buf); + fail_on_test(ret && ret != ENOTTY); + if (ret == 0) { + fail_on_test(doioctl(node, VIDIOC_QUERYBUF, &buf)); + fail_on_test(checkQueryBuf(node, buf, bufs.type, bufs.memory, i, Prepared, 0)); + } + + fail_on_test(doioctl(node, VIDIOC_QBUF, &buf)); + fail_on_test(doioctl(node, VIDIOC_QUERYBUF, &buf)); + fail_on_test(checkQueryBuf(node, buf, bufs.type, bufs.memory, i, Queued, 0)); + } + return 0; +} + +static int releaseUserPtr(struct node *node, struct v4l2_requestbuffers &bufs) +{ + for (unsigned i = 0; i < bufs.count; i++) + free(ptrs[i]); + return 0; +} + +int testUserPtr(struct node *node) +{ + struct v4l2_requestbuffers bufs; + bool can_stream = node->caps & V4L2_CAP_STREAMING; + int ret; + + if (!(node->caps & V4L2_CAP_VIDEO_CAPTURE)) + return 0; + + memset(&bufs, 0, sizeof(bufs)); + bufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + bufs.memory = V4L2_MEMORY_USERPTR; + ret = doioctl(node, VIDIOC_REQBUFS, &bufs); + if (ret) { + fail_on_test(can_stream); + return ret; + } + + bufs.count = 1; + fail_on_test(doioctl(node, VIDIOC_REQBUFS, &bufs)); + fail_on_test(doioctl(node, VIDIOC_STREAMOFF, &bufs.type)); + last_seq = -1; + field_nr = 1; + last_field = cur_fmt.fmt.pix.field; + + fail_on_test(setupUserPtr(node, bufs)); + + fail_on_test(doioctl(node, VIDIOC_STREAMON, &bufs.type)); + fail_on_test(doioctl(node, VIDIOC_STREAMON, &bufs.type)); + fail_on_test(captureBufs(node, bufs, 100, false)); + fail_on_test(captureBufs(node, bufs, 100, true)); + fail_on_test(doioctl(node, VIDIOC_STREAMOFF, &bufs.type)); + fail_on_test(doioctl(node, VIDIOC_STREAMOFF, &bufs.type)); + fail_on_test(releaseUserPtr(node, bufs)); + bufs.count = 0; + fail_on_test(doioctl(node, VIDIOC_REQBUFS, &bufs)); + return 0; +} -- 2.7.4