-include *.d
-v4l2-compliance: v4l2-compliance.o v4l2-test-debug.o
+v4l2-compliance: v4l2-compliance.o v4l2-test-debug.o v4l2-test-input-output.o
$(CXX) $(LDFLAGS) -o $@ $^
install: $(TARGETS)
OptHelp = 'h',
OptTest = 't',
OptVerbose = 'v',
+ OptTrace = 'T',
OptLast = 256
};
TestChipIdent,
TestRegister,
TestLogStatus,
+ TestInput,
+ TestOutput,
TestMax
};
{"vbi-device", required_argument, 0, OptSetVbiDevice},
{"help", no_argument, 0, OptHelp},
{"verbose", no_argument, 0, OptVerbose},
+ {"trace", no_argument, 0, OptTrace},
{"test", required_argument, 0, OptTest},
{0, 0, 0, 0}
};
printf(" -t, --test=<num> run specified test.\n");
printf(" By default all tests are run.\n");
printf(" 0 = test VIDIOC_QUERYCAP\n");
- printf(" -v, --verbose turn on verbose ioctl error reporting.\n");
+ printf(" -v, --verbose turn on verbose error reporting.\n");
+ printf(" -T, --trace trace all called ioctls.\n");
exit(0);
}
-int doioctl(int fd, unsigned long int request, void *parm, const char *name)
+int doioctl(struct node *node, unsigned long int request, void *parm, const char *name)
{
- int retVal;
+ int retval;
int e;
errno = 0;
- retVal = ioctl(fd, request, parm);
+ retval = ioctl(node->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) {
+ if (options[OptTrace])
+ printf("\t\t%s returned %d (%s)\n", name, retval, strerror(e));
+ if (retval == 0)
+ return 0;
+ if (retval != -1) {
+ fail("%s returned %d instead of 0 or -1\n", name, retval);
return -1;
}
- retVal = e;
- return retVal;
+ return e;
}
std::string cap2s(unsigned cap)
{
static char buf[100];
- if (res == ENOSYS) {
+ if (res == -ENOSYS) {
strcpy(buf, "Not Supported");
res = 0;
} else {
tests_total++;
if (res) {
app_result = res;
- sprintf(buf, "FAIL (%d)\n", res);
+ sprintf(buf, "FAIL");
} else {
tests_ok++;
}
return buf;
}
-int check_string(const char *s, size_t len, const char *fld)
+int check_string(const char *s, size_t len)
{
- if (strlen(s) == 0) {
- if (verbose)
- printf("%s field empty\n", fld);
- return -1;
- }
- if (strlen(s) >= len) {
- if (verbose)
- printf("%s field not 0-terminated\n", fld);
- return -2;
- }
+ size_t sz = strnlen(s, len);
+
+ if (sz == 0)
+ return fail("string empty\n");
+ if (sz == len)
+ return fail("string not 0-terminated\n");
return 0;
}
-int check_ustring(const __u8 *s, int len, const char *fld)
+int check_ustring(const __u8 *s, int len)
{
- return check_string((const char *)s, len, fld);
+ return check_string((const char *)s, len);
}
-int check_0(void *p, int len)
+int check_0(const void *p, int len)
{
- __u8 *q = (__u8 *)p;
+ const __u8 *q = (const __u8 *)p;
while (len--)
- if (*q++) {
- if (verbose)
- printf("array not zeroed by driver\n");
- return -1;
- }
+ if (*q++)
+ return 1;
return 0;
}
-static int testCap(int fd)
+static int testCap(struct node *node)
{
struct v4l2_capability vcap;
__u32 caps;
- if (doioctl(fd, VIDIOC_QUERYCAP, &vcap, "VIDIOC_QUERYCAP"))
- return -1;
- if (check_ustring(vcap.driver, sizeof(vcap.driver), "driver"))
- return -2;
- if (check_ustring(vcap.card, sizeof(vcap.card), "card"))
- return -3;
+ if (doioctl(node, VIDIOC_QUERYCAP, &vcap, "VIDIOC_QUERYCAP"))
+ return fail("VIDIOC_QUERYCAP not implemented\n");
+ if (check_ustring(vcap.driver, sizeof(vcap.driver)))
+ return fail("invalid driver name\n");
+ if (check_ustring(vcap.card, sizeof(vcap.card)))
+ return fail("invalid card name\n");
if (check_0(vcap.reserved, sizeof(vcap.reserved)))
- return -4;
+ return fail("non-zero reserved fields\n");
caps = vcap.capabilities;
- if (caps == 0) {
- if (verbose) printf("no capabilities set\n");
- return -6;
- }
+ if (caps == 0)
+ return fail("no capabilities set\n");
return 0;
}
-static int check_prio(int fd, int fd2, enum v4l2_priority match)
+static int check_prio(struct node *node, struct node *node2, enum v4l2_priority match)
{
enum v4l2_priority prio;
int err;
- err = doioctl(fd, VIDIOC_G_PRIORITY, &prio, "VIDIOC_G_PRIORITY");
+ err = doioctl(node, VIDIOC_G_PRIORITY, &prio, "VIDIOC_G_PRIORITY");
if (err == EINVAL)
- return ENOSYS;
+ return -ENOSYS;
if (err)
- return -1;
- if (prio != match) {
- if (verbose) printf("wrong priority returned (%d, expected %d)\n", prio, match);
- return -2;
- }
- if (doioctl(fd2, VIDIOC_G_PRIORITY, &prio, "VIDIOC_G_PRIORITY"))
- return -3;
- if (prio != match) {
- if (verbose) printf("wrong priority returned on second fh (%d, expected %d)\n", prio, match);
- return -4;
- }
+ return fail("VIDIOC_G_PRIORITY failed\n");
+ if (prio != match)
+ return fail("wrong priority returned (%d, expected %d)\n", prio, match);
+ if (doioctl(node2, VIDIOC_G_PRIORITY, &prio, "VIDIOC_G_PRIORITY"))
+ return fail("second VIDIOC_G_PRIORITY failed\n");
+ if (prio != match)
+ return fail("wrong priority returned on second fh (%d, expected %d)\n", prio, match);
return 0;
}
-static int testPrio(int fd, int fd2)
+static int testPrio(struct node *node, struct node *node2)
{
enum v4l2_priority prio;
int err;
- err = check_prio(fd, fd2, V4L2_PRIORITY_DEFAULT);
+ err = check_prio(node, node2, V4L2_PRIORITY_DEFAULT);
if (err)
return err;
prio = V4L2_PRIORITY_RECORD;
- if (doioctl(fd, VIDIOC_S_PRIORITY, &prio, "VIDIOC_S_PRIORITY"))
- return -10;
- if (check_prio(fd, fd2, V4L2_PRIORITY_RECORD))
- return -11;
+ if (doioctl(node, VIDIOC_S_PRIORITY, &prio, "VIDIOC_S_PRIORITY"))
+ return fail("VIDIOC_S_PRIORITY RECORD failed\n");
+ if (check_prio(node, node2, V4L2_PRIORITY_RECORD))
+ return fail("expected priority RECORD");
prio = V4L2_PRIORITY_INTERACTIVE;
- if (!doioctl(fd2, VIDIOC_S_PRIORITY, &prio, "VIDIOC_S_PRIORITY")) {
- if (verbose) printf("Can lower prio on second filehandle\n");
- return -12;
- }
+ if (!doioctl(node2, VIDIOC_S_PRIORITY, &prio, "VIDIOC_S_PRIORITY"))
+ return fail("Can lower prio on second filehandle\n");
prio = V4L2_PRIORITY_INTERACTIVE;
- if (doioctl(fd, VIDIOC_S_PRIORITY, &prio, "VIDIOC_S_PRIORITY")) {
- if (verbose) printf("Could not lower prio\n");
- return -13;
- }
-
- if (check_prio(fd, fd2, V4L2_PRIORITY_INTERACTIVE))
- return -14;
+ if (doioctl(node, VIDIOC_S_PRIORITY, &prio, "VIDIOC_S_PRIORITY"))
+ return fail("Could not lower prio\n");
+ if (check_prio(node, node2, V4L2_PRIORITY_INTERACTIVE))
+ return fail("expected priority INTERACTIVE");
return 0;
}
{
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;
+ struct node node = { -1 };
+ struct node video_node = { -1 };
+ struct node video_node2 = { -1 };
+ struct node radio_node = { -1 };
+ struct node radio_node2 = { -1 };
+ struct node vbi_node = { -1 };
+ struct node vbi_node2 = { -1 };
/* command args */
int ch;
exit(1);
}
- if (video_device && (video_fd = open(video_device, O_RDWR)) < 0) {
+ if (video_device && (video_node.fd = open(video_device, O_RDWR)) < 0) {
fprintf(stderr, "Failed to open %s: %s\n", video_device,
strerror(errno));
exit(1);
}
- if (radio_device && (radio_fd = open(radio_device, O_RDWR)) < 0) {
+ if (radio_device && (radio_node.fd = open(radio_device, O_RDWR)) < 0) {
fprintf(stderr, "Failed to open %s: %s\n", radio_device,
strerror(errno));
exit(1);
}
- if (vbi_device && (vbi_fd = open(vbi_device, O_RDWR)) < 0) {
+ if (vbi_device && (vbi_node.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;
+ if (video_node.fd >= 0) {
+ node.fd = video_node.fd;
device = video_device;
- } else if (radio_fd >= 0) {
- fd = radio_fd;
+ } else if (radio_node.fd >= 0) {
+ node.fd = radio_node.fd;
device = radio_device;
- } else if (vbi_fd >= 0) {
- fd = vbi_fd;
+ } else if (vbi_node.fd >= 0) {
+ node.fd = vbi_node.fd;
device = vbi_device;
}
- ioctl(fd, VIDIOC_QUERYCAP, &vcap, "VIDIOC_QUERYCAP");
- caps = vcap.capabilities;
+ ioctl(node.fd, VIDIOC_QUERYCAP, &vcap, "VIDIOC_QUERYCAP");
+ node.caps = vcap.capabilities;
/* Information Opts */
printf("Required ioctls:\n");
if (test[TestCap]) {
if (video_device)
- printf("\ttest VIDIOC_QUERYCAP: %s\n", ok(testCap(video_fd)));
+ printf("\ttest VIDIOC_QUERYCAP: %s\n", ok(testCap(&video_node)));
if (radio_device)
- printf("\ttest VIDIOC_QUERYCAP for radio: %s\n", ok(testCap(radio_fd)));
+ printf("\ttest VIDIOC_QUERYCAP for radio: %s\n", ok(testCap(&radio_node)));
if (vbi_device)
- printf("\ttest VIDIOC_QUERYCAP for vbi: %s\n", ok(testCap(vbi_fd)));
+ printf("\ttest VIDIOC_QUERYCAP for vbi: %s\n", ok(testCap(&vbi_node)));
}
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)));
+ ok((video_node2.fd = open(video_device, O_RDWR)) < 0));
+ if (video_node2.fd >= 0) {
+ printf("\ttest VIDIOC_QUERYCAP: %s\n", ok(testCap(&video_node2)));
printf("\ttest VIDIOC_S_PRIORITY: %s\n",
- ok(testPrio(video_fd, video_fd2)));
- close(video_fd2);
+ ok(testPrio(&video_node, &video_node2)));
+ close(video_node2.fd);
}
}
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)));
+ ok((radio_node2.fd = open(radio_device, O_RDWR)) < 0));
+ if (radio_node2.fd >= 0) {
+ printf("\ttest VIDIOC_QUERYCAP: %s\n", ok(testCap(&radio_node2)));
printf("\ttest VIDIOC_S_PRIORITY: %s\n",
- ok(testPrio(radio_fd, radio_fd2)));
- close(radio_fd2);
+ ok(testPrio(&radio_node, &radio_node2)));
+ close(radio_node2.fd);
}
}
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)));
+ ok((vbi_node2.fd = open(vbi_device, O_RDWR)) < 0));
+ if (vbi_node2.fd >= 0) {
+ printf("\ttest VIDIOC_QUERYCAP: %s\n", ok(testCap(&vbi_node2)));
printf("\ttest VIDIOC_S_PRIORITY: %s\n",
- ok(testPrio(vbi_fd, vbi_fd2)));
- close(vbi_fd2);
+ ok(testPrio(&vbi_node, &vbi_node2)));
+ close(vbi_node2.fd);
}
}
printf("Debug ioctls:\n");
if (test[TestChipIdent])
- printf("\ttest VIDIOC_DBG_G_CHIP_IDENT: %s\n", ok(testChipIdent(fd)));
+ printf("\ttest VIDIOC_DBG_G_CHIP_IDENT: %s\n", ok(testChipIdent(&node)));
if (test[TestRegister])
- printf("\ttest VIDIOC_DBG_G/S_REGISTER: %s\n", ok(testRegister(fd)));
+ printf("\ttest VIDIOC_DBG_G/S_REGISTER: %s\n", ok(testRegister(&node)));
if (test[TestLogStatus])
- printf("\ttest VIDIOC_LOG_STATUS: %s\n", ok(testLogStatus(fd)));
+ printf("\ttest VIDIOC_LOG_STATUS: %s\n", ok(testLogStatus(&node)));
+
+ printf("Input ioctls:\n");
+ if (test[TestInput]) {
+ printf("\ttest VIDIOC_S/G/ENUMAUDIO: %s\n", ok(testInputAudio(&node)));
+ printf("\ttest VIDIOC_G/S/ENUMINPUT: %s\n", ok(testInput(&node)));
+ }
+
+ printf("Output ioctls:\n");
+ if (test[TestOutput]) {
+ printf("\ttest VIDIOC_S/G/ENUMAUDOUT: %s\n", ok(testOutputAudio(&node)));
+ printf("\ttest VIDIOC_G/S/ENUMOUTPUT: %s\n", ok(testOutput(&node)));
+ }
- close(fd);
+ close(node.fd);
printf("Total: %d Succeeded: %d Failed: %d\n",
tests_total, tests_ok, tests_total - tests_ok);
exit(app_result);
extern int verbose;
extern unsigned caps;
-int doioctl(int fd, unsigned long int request, void *parm, const char *name);
+struct node {
+ int fd;
+ unsigned caps;
+ unsigned tuners;
+ unsigned modulators;
+ unsigned audio_inputs;
+ unsigned audio_outputs;
+};
+
+#define fail(fmt, args...) \
+({ \
+ if (verbose) \
+ printf("\t\tfail: " fmt, ##args); \
+ 1; \
+})
+
+int doioctl(struct node *node, 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);
+int check_string(const char *s, size_t len);
+int check_ustring(const __u8 *s, int len);
+int check_0(const void *p, int len);
// Debug ioctl tests
-int testChipIdent(int fd);
-int testRegister(int fd);
-int testLogStatus(int fd);
+int testChipIdent(struct node *node);
+int testRegister(struct node *node);
+int testLogStatus(struct node *node);
+
+// Input ioctl tests
+int testInput(struct node *node);
+int testInputAudio(struct node *node);
+
+// Output ioctl tests
+int testOutput(struct node *node);
+int testOutputAudio(struct node *node);
#endif
#include <sys/klog.h>
#include "v4l2-compliance.h"
-int testChipIdent(int fd)
+int testChipIdent(struct node *node)
{
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");
+ ret = doioctl(node, 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;
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 -2;
- }
+ ret = doioctl(node, VIDIOC_DBG_G_CHIP_IDENT, &chip, "VIDIOC_DBG_G_CHIP_IDENT");
+ if (ret != EINVAL)
+ return fail("Invalid match_type accepted\n");
+ if (memcmp(&orig, &chip, sizeof(chip)))
+ return fail("Error, but struct modified\n");
return 0;
}
- return ret == EINVAL ? ENOSYS : ret;
+ return ret == EINVAL ? -ENOSYS : ret;
}
-int testRegister(int fd)
+int testRegister(struct node *node)
{
struct v4l2_dbg_register reg;
struct v4l2_dbg_chip_ident chip;
reg.match.type = V4L2_CHIP_MATCH_HOST;
reg.match.addr = 0;
reg.reg = 0;
- ret = doioctl(fd, VIDIOC_DBG_G_REGISTER, ®, "VIDIOC_DBG_G_REGISTER");
+ ret = doioctl(node, VIDIOC_DBG_G_REGISTER, ®, "VIDIOC_DBG_G_REGISTER");
if (ret == EINVAL)
- return ENOSYS;
- 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 -2;
- }
+ return -ENOSYS;
+ if (uid && ret != EPERM)
+ return fail("Not allowed to call VIDIOC_DBG_G_REGISTER unless root\n");
+ if (uid == 0 && ret)
+ return fail("Not allowed to call VIDIOC_DBG_G_REGISTER even though we are root\n");
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 -3;
- }
+ if (doioctl(node, VIDIOC_DBG_G_CHIP_IDENT, &chip, "VIDIOC_DBG_G_CHIP_IDENT"))
+ return fail("Must support VIDIOC_DBG_G_CHIP_IDENT\n");
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, ®, "VIDIOC_DBG_S_REGISTER");
- if (ret != EINVAL && ret != EPERM) {
- printf("Invalid error calling VIDIOC_DBG_S_REGISTER as non-root\n");
- return -4;
- }
+ ret = doioctl(node, VIDIOC_DBG_S_REGISTER, ®, "VIDIOC_DBG_S_REGISTER");
+ if (ret != EINVAL && ret != EPERM)
+ return fail("Invalid error calling VIDIOC_DBG_S_REGISTER as non-root\n");
}
return 0;
}
-int testLogStatus(int fd)
+int testLogStatus(struct node *node)
{
- int ret = doioctl(fd, VIDIOC_LOG_STATUS, NULL, "VIDIOC_LOG_STATUS");
+ int ret = doioctl(node, VIDIOC_LOG_STATUS, NULL, "VIDIOC_LOG_STATUS");
- return (ret == EINVAL) ? ENOSYS : ret;
+ return (ret == EINVAL) ? -ENOSYS : ret;
}
--- /dev/null
+/*
+ V4L2 API compliance input/output ioctl tests.
+
+ Copyright (C) 2011 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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include "v4l2-compliance.h"
+
+#define MAGIC 0x1eadbeef
+
+static int validInput(const struct v4l2_input *descr, unsigned i, unsigned max_audio)
+{
+ __u32 mask = (1 << max_audio) - 1;
+
+ if (descr->index != i)
+ return fail("invalid index\n");
+ if (check_ustring(descr->name, sizeof(descr->name)))
+ return fail("invalid name\n");
+ if (descr->type != V4L2_INPUT_TYPE_TUNER && descr->type != V4L2_INPUT_TYPE_CAMERA)
+ return fail("invalid type\n");
+ if (descr->type == V4L2_INPUT_TYPE_CAMERA && descr->tuner)
+ return fail("invalid tuner\n");
+ if (!(descr->capabilities & V4L2_IN_CAP_STD) && descr->std)
+ return fail("invalid std\n");
+ if ((descr->capabilities & V4L2_IN_CAP_STD) && !descr->std)
+ return fail("std == 0\n");
+ if (descr->capabilities & ~0x7)
+ return fail("invalid capabilities\n");
+ if (check_0(descr->reserved, sizeof(descr->reserved)))
+ return fail("non-zero reserved fields\n");
+ if (descr->status & ~0x07070337)
+ return fail("invalid status\n");
+ if (descr->status & 0x02070000)
+ return fail("use of deprecated digital video status\n");
+ if (descr->audioset & ~mask)
+ return fail("invalid audioset\n");
+ return 0;
+}
+
+int testInput(struct node *node)
+{
+ struct v4l2_input descr;
+ struct v4l2_audio audio;
+ int cur_input = MAGIC;
+ int input;
+ int ret = doioctl(node, VIDIOC_G_INPUT, &cur_input, "VIDIOC_G_INPUT");
+ int i = 0;
+ unsigned a;
+
+ if (ret == EINVAL)
+ return -ENOSYS;
+ if (ret)
+ return ret;
+ if (cur_input == MAGIC)
+ return fail("VIDIOC_G_INPUT didn't fill in the input\n");
+ for (;;) {
+ memset(&descr, 0xff, sizeof(descr));
+ descr.index = i;
+ ret = doioctl(node, VIDIOC_ENUMINPUT, &descr, "VIDIOC_ENUMINPUT");
+ if (ret == EINVAL)
+ break;
+ if (ret)
+ return fail("could not enumerate input %d\n", i);
+ input = i;
+ if (doioctl(node, VIDIOC_S_INPUT, &input, "VIDIOC_S_INPUT"))
+ return fail("could not set input to %d\n", i);
+ if (input != i)
+ return fail("input set to %d, but becomes %d?!\n", i, input);
+ if (validInput(&descr, i, node->audio_inputs))
+ return fail("invalid attributes for input %d\n", i);
+ for (a = 0; a <= node->audio_inputs; a++) {
+ memset(&audio, 0, sizeof(audio));
+ audio.index = a;
+ ret = doioctl(node, VIDIOC_S_AUDIO, &audio, "VIDIOC_S_AUDIO");
+ if (ret && (descr.audioset & (1 << a)))
+ return fail("could not set audio input to %d for video input %d\n", a, i);
+ if (ret != EINVAL && !(descr.audioset & (1 << a)))
+ return fail("could set invalid audio input %d for video input %d\n", a, i);
+ }
+ i++;
+ }
+ input = i;
+ if (doioctl(node, VIDIOC_S_INPUT, &input, "VIDIOC_S_INPUT") != EINVAL)
+ return fail("could set input to invalid input %d\n", i);
+ if (doioctl(node, VIDIOC_S_INPUT, &cur_input, "VIDIOC_S_INPUT"))
+ return fail("couldn't set input to the original input %d\n", cur_input);
+ return 0;
+}
+
+static int validInputAudio(const struct v4l2_audio *descr, unsigned i)
+{
+ if (descr->index != i)
+ return fail("invalid index\n");
+ if (check_ustring(descr->name, sizeof(descr->name)))
+ return fail("invalid name\n");
+ if (descr->capability & ~0x3)
+ return fail("invalid capabilities\n");
+ if (descr->mode != 0 && descr->mode != V4L2_AUDMODE_AVL)
+ return fail("invalid mode\n");
+ if (!(descr->capability & V4L2_AUDCAP_AVL) && descr->mode)
+ return fail("mode != 0\n");
+ if (check_0(descr->reserved, sizeof(descr->reserved)))
+ return fail("non-zero reserved fields\n");
+ return 0;
+}
+
+int testInputAudio(struct node *node)
+{
+ struct v4l2_audio input;
+ int i = 0;
+ int ret;
+
+ for (;;) {
+ memset(&input, 0xff, sizeof(input));
+ input.index = i;
+
+ ret = doioctl(node, VIDIOC_ENUMAUDIO, &input, "VIDIOC_ENUMAUDIO");
+ if (i == 0 && ret == EINVAL)
+ return -ENOSYS;
+ if (ret == EINVAL)
+ break;
+ if (ret)
+ return fail("could not enumerate audio input %d\n", i);
+ if (validInputAudio(&input, i))
+ return fail("invalid attributes for audio input %d\n", i);
+ node->audio_inputs++;
+ i++;
+ }
+ memset(&input, 0xff, sizeof(input));
+ memset(input.reserved, 0, sizeof(input.reserved));
+ input.index = i;
+ input.mode = 0;
+ if (doioctl(node, VIDIOC_S_AUDIO, &input, "VIDIOC_S_AUDIO") != EINVAL)
+ return fail("can set invalid audio input\n");
+ memset(&input, 0xff, sizeof(input));
+ ret = doioctl(node, VIDIOC_G_AUDIO, &input, "VIDIOC_G_AUDIO");
+ if (i == 0) {
+ if (ret != EINVAL)
+ return fail("can get current audio input, but no inputs enumerated\n");
+ return 0;
+ }
+ if (ret)
+ return fail("cannot get current audio input\n");
+ if (input.index >= node->audio_inputs)
+ return fail("invalid current audio input %d\n", input.index);
+ if (validInputAudio(&input, input.index))
+ return fail("invalid attributes for audio input %d\n", input.index);
+ return 0;
+}
+
+static int validOutput(const struct v4l2_output *descr, unsigned o, unsigned max_audio)
+{
+ __u32 mask = (1 << max_audio) - 1;
+
+ if (descr->index != o)
+ return fail("invalid index\n");
+ if (check_ustring(descr->name, sizeof(descr->name)))
+ return fail("invalid name\n");
+ if (descr->type != V4L2_OUTPUT_TYPE_MODULATOR && descr->type != V4L2_OUTPUT_TYPE_ANALOG)
+ return fail("invalid type\n");
+ if (descr->type == V4L2_OUTPUT_TYPE_ANALOG && descr->modulator)
+ return fail("invalid modulator\n");
+ if (!(descr->capabilities & V4L2_OUT_CAP_STD) && descr->std)
+ return fail("invalid std\n");
+ if ((descr->capabilities & V4L2_OUT_CAP_STD) && !descr->std)
+ return fail("std == 0\n");
+ if (descr->capabilities & ~0x7)
+ return fail("invalid capabilities\n");
+ if (check_0(descr->reserved, sizeof(descr->reserved)))
+ return fail("non-zero reserved fields\n");
+ if (descr->audioset & ~mask)
+ return fail("invalid audioset\n");
+ return 0;
+}
+
+int testOutput(struct node *node)
+{
+ struct v4l2_output descr;
+ struct v4l2_audioout audio;
+ int cur_output = MAGIC;
+ int output;
+ int ret = doioctl(node, VIDIOC_G_OUTPUT, &cur_output, "VIDIOC_G_OUTPUT");
+ int o = 0;
+ unsigned a;
+
+ if (ret == EINVAL)
+ return -ENOSYS;
+ if (ret)
+ return ret;
+ if (cur_output == MAGIC)
+ return fail("VIDIOC_G_OUTPUT didn't fill in the output\n");
+ for (;;) {
+ memset(&descr, 0xff, sizeof(descr));
+ descr.index = o;
+ ret = doioctl(node, VIDIOC_ENUMOUTPUT, &descr, "VIDIOC_ENUMOUTPUT");
+ if (ret)
+ break;
+ output = o;
+ if (doioctl(node, VIDIOC_S_OUTPUT, &output, "VIDIOC_S_OUTPUT"))
+ return fail("could not set output to %d\n", o);
+ if (output != o)
+ return fail("output set to %d, but becomes %d?!\n", o, output);
+ if (validOutput(&descr, o, node->audio_outputs))
+ return fail("invalid attributes for output %d\n", o);
+ for (a = 0; a <= node->audio_outputs; a++) {
+ memset(&audio, 0, sizeof(audio));
+ audio.index = a;
+ ret = doioctl(node, VIDIOC_S_AUDOUT, &audio, "VIDIOC_S_AUDOUT");
+ if (ret && (descr.audioset & (1 << a)))
+ return fail("could not set audio output to %d for video output %d\n", a, o);
+ if (ret != EINVAL && !(descr.audioset & (1 << a)))
+ return fail("could set invalid audio output %d for video output %d\n", a, o);
+ }
+ o++;
+ }
+ output = o;
+ if (doioctl(node, VIDIOC_S_OUTPUT, &output, "VIDIOC_S_OUTPUT") != EINVAL)
+ return fail("could set output to invalid output %d\n", o);
+ if (doioctl(node, VIDIOC_S_OUTPUT, &cur_output, "VIDIOC_S_OUTPUT"))
+ return fail("couldn't set output to the original output %d\n", cur_output);
+ return 0;
+}
+
+static int validOutputAudio(const struct v4l2_audioout *descr, unsigned o)
+{
+ if (descr->index != o)
+ return fail("invalid index\n");
+ if (check_ustring(descr->name, sizeof(descr->name)))
+ return fail("invalid name\n");
+ if (descr->capability)
+ return fail("invalid capabilities\n");
+ if (descr->mode)
+ return fail("invalid mode\n");
+ if (check_0(descr->reserved, sizeof(descr->reserved)))
+ return fail("non-zero reserved fields\n");
+ return 0;
+}
+
+int testOutputAudio(struct node *node)
+{
+ struct v4l2_audioout output;
+ int o = 0;
+ int ret;
+
+ for (;;) {
+ memset(&output, 0xff, sizeof(output));
+ output.index = o;
+
+ ret = doioctl(node, VIDIOC_ENUMAUDOUT, &output, "VIDIOC_ENUMAUDOUT");
+ if (o == 0 && ret == EINVAL)
+ return -ENOSYS;
+ if (ret == EINVAL)
+ break;
+ if (ret)
+ return fail("could not enumerate audio output %d\n", o);
+ if (validOutputAudio(&output, o))
+ return fail("invalid attributes for audio output %d\n", o);
+ node->audio_outputs++;
+ o++;
+ }
+ memset(&output, 0xff, sizeof(output));
+ memset(output.reserved, 0, sizeof(output.reserved));
+ output.index = o;
+ output.mode = 0;
+ if (doioctl(node, VIDIOC_S_AUDOUT, &output, "VIDIOC_S_AUDOUT") != EINVAL)
+ return fail("can set invalid audio output\n");
+ memset(&output, 0xff, sizeof(output));
+ ret = doioctl(node, VIDIOC_G_AUDOUT, &output, "VIDIOC_G_AUDOUT");
+ if (o == 0) {
+ if (ret != EINVAL)
+ return fail("can get current audio output, but no outputs enumerated\n");
+ return 0;
+ }
+ if (ret)
+ return fail("cannot get current audio output\n");
+ if (output.index >= node->audio_outputs)
+ return fail("invalid current audio output %d\n", output.index);
+ if (validOutputAudio(&output, output.index))
+ return fail("invalid attributes for audio output %d\n", output.index);
+ return 0;
+}