static int checkModulator(struct node *node, const struct v4l2_modulator &mod, unsigned m) { bool tv = !node->is_radio; if (mod.index != m) return fail("invalid index\n"); if (check_ustring(mod.name, sizeof(mod.name))) return fail("invalid name\n"); if (check_0(mod.reserved, sizeof(mod.reserved))) return fail("non-zero reserved fields\n"); if (tv) return fail("currently only radio modulators are supported\n"); if (!(mod.capability & V4L2_TUNER_CAP_LOW)) return fail("V4L2_TUNER_CAP_LOW was not set for a radio modulator\n"); if (mod.capability & (V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2)) return fail("TV capabilities for radio modulator?\n"); if (mod.rangelow >= mod.rangehigh) return fail("rangelow >= rangehigh\n"); if (mod.rangelow == 0 || mod.rangehigh == 0xffffffff) return fail("invalid rangelow or rangehigh\n"); if (!(mod.capability & V4L2_TUNER_CAP_STEREO) && (mod.txsubchans & V4L2_TUNER_SUB_STEREO)) return fail("stereo subchan, but no stereo caps?\n"); if (!(mod.capability & V4L2_TUNER_CAP_LANG1) && (mod.txsubchans & V4L2_TUNER_SUB_LANG1)) return fail("lang1 subchan, but no lang1 caps?\n"); if (!(mod.capability & V4L2_TUNER_CAP_LANG2) && (mod.txsubchans & V4L2_TUNER_SUB_LANG2)) return fail("lang2 subchan, but no lang2 caps?\n"); if (!(mod.capability & V4L2_TUNER_CAP_RDS) && (mod.txsubchans & V4L2_TUNER_SUB_RDS)) return fail("RDS subchan, but no RDS caps?\n"); return 0; }
static int checkOutput(struct node *node, const struct v4l2_output &descr, unsigned o) { __u32 mask = (1 << node->audio_outputs) - 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"); if (descr.modulator && descr.modulator >= node->modulators) return fail("invalid modulator\n"); return 0; }
static int checkInput(struct node *node, const struct v4l2_input &descr, unsigned i) { __u32 mask = (1 << node->audio_inputs) - 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"); if (descr.tuner && descr.tuner >= node->tuners) return fail("invalid tuner\n"); return 0; }
static int testEnumFormatsType(struct node *node, unsigned type) { pixfmt_set &set = node->buftype_pixfmts[type]; struct v4l2_fmtdesc fmtdesc; unsigned f = 0; int ret; for (;;) { memset(&fmtdesc, 0xff, sizeof(fmtdesc)); fmtdesc.type = type; fmtdesc.index = f; ret = doioctl(node, VIDIOC_ENUM_FMT, &fmtdesc); if (ret == ENOTTY) return ret; if (f == 0 && ret == EINVAL) return ENOTTY; if (ret == EINVAL) break; if (ret) return fail("expected EINVAL, but got %d when enumerating buftype %d\n", ret, type); ret = check_0(fmtdesc.reserved, sizeof(fmtdesc.reserved)); if (ret) return fail("fmtdesc.reserved not zeroed\n"); if (fmtdesc.index != f) return fail("fmtdesc.index was modified\n"); if (fmtdesc.type != type) return fail("fmtdesc.type was modified\n"); ret = check_ustring(fmtdesc.description, sizeof(fmtdesc.description)); if (ret) return fail("fmtdesc.description not set\n"); if (!fmtdesc.pixelformat) return fail("fmtdesc.pixelformat not set\n"); if (!wrapper && (fmtdesc.flags & V4L2_FMT_FLAG_EMULATED)) return fail("drivers must never set the emulated flag\n"); if (fmtdesc.flags & ~(V4L2_FMT_FLAG_COMPRESSED | V4L2_FMT_FLAG_EMULATED)) return fail("unknown flag %08x returned\n", fmtdesc.flags); ret = testEnumFrameSizes(node, fmtdesc.pixelformat); if (ret && ret != ENOTTY) return ret; if (ret == 0 && !(node->caps & (V4L2_CAP_VIDEO_CAPTURE | V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE))) return fail("found framesizes when no video capture is supported\n"); f++; if (type == V4L2_BUF_TYPE_PRIVATE) continue; // Update array in v4l2-compliance.h if new buffer types are added assert(type <= V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); if (set.find(fmtdesc.pixelformat) != set.end()) return fail("duplicate format %08x\n", fmtdesc.pixelformat); set.insert(fmtdesc.pixelformat); } info("found %d formats for buftype %d\n", f, type); return 0; }
static int checkPresets(struct node *node, bool has_presets) { struct v4l2_dv_enum_preset enumpreset; struct v4l2_dv_preset preset; unsigned i; int ret; memset(&preset, 0xff, sizeof(preset)); ret = doioctl(node, VIDIOC_G_DV_PRESET, &preset); if (!ret && check_0(preset.reserved, sizeof(preset.reserved))) return fail("reserved not zeroed\n"); if (ret && has_presets) return fail("PRESET cap set, but could not get current preset\n"); if (!ret && !has_presets) return fail("PRESET cap not set, but could still get a preset\n"); if (preset.preset != V4L2_DV_INVALID) { ret = doioctl(node, VIDIOC_S_DV_PRESET, &preset); if (ret && has_presets) return fail("PRESET cap set, but could not set preset\n"); if (!ret && !has_presets) return fail("PRESET cap not set, but could still set a preset\n"); } preset.preset = V4L2_DV_INVALID; ret = doioctl(node, VIDIOC_S_DV_PRESET, &preset); if (ret != EINVAL && ret != ENOTTY) return fail("could set preset V4L2_DV_INVALID\n"); for (i = 0; ; i++) { memset(&enumpreset, 0xff, sizeof(enumpreset)); enumpreset.index = i; ret = doioctl(node, VIDIOC_ENUM_DV_PRESETS, &enumpreset); if (ret) break; if (check_ustring(enumpreset.name, sizeof(enumpreset.name))) return fail("invalid preset name\n"); if (check_0(enumpreset.reserved, sizeof(enumpreset.reserved))) return fail("reserved not zeroed\n"); if (enumpreset.index != i) return fail("index changed!\n"); if (enumpreset.preset == V4L2_DV_INVALID) return fail("invalid preset!\n"); if (enumpreset.width == 0 || enumpreset.height == 0) return fail("width or height not set\n"); } if (i == 0 && has_presets) return fail("PRESET cap set, but no presets can be enumerated\n"); if (i && !has_presets) return fail("PRESET cap was not set, but presets can be enumerated\n"); ret = doioctl(node, VIDIOC_QUERY_DV_PRESET, &preset); if (!ret && !has_presets) return fail("PRESET cap was not set, but could still query preset\n"); return 0; }
static int checkOutputAudio(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; }
static int checkInputAudio(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; }
static int checkModulator(struct node *node, const struct v4l2_modulator &mod, unsigned m) { bool tv = !node->is_radio; if (mod.index != m) return fail("invalid index\n"); if (check_ustring(mod.name, sizeof(mod.name))) return fail("invalid name\n"); if (check_0(mod.reserved, sizeof(mod.reserved))) return fail("non-zero reserved fields\n"); if (tv) return fail("currently only radio modulators are supported\n"); if (!(mod.capability & V4L2_TUNER_CAP_LOW)) return fail("V4L2_TUNER_CAP_LOW was not set for a radio modulator\n"); if (mod.capability & (V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2)) return fail("TV capabilities for radio modulator?\n"); fail_on_test(!(mod.capability & V4L2_TUNER_CAP_FREQ_BANDS)); if (mod.rangelow >= mod.rangehigh) return fail("rangelow >= rangehigh\n"); if (mod.rangelow == 0 || mod.rangehigh == 0xffffffff) return fail("invalid rangelow or rangehigh\n"); if (!(mod.capability & V4L2_TUNER_CAP_STEREO) && (mod.txsubchans & V4L2_TUNER_SUB_STEREO)) return fail("stereo subchan, but no stereo caps?\n"); if (!(mod.capability & V4L2_TUNER_CAP_LANG1) && (mod.txsubchans & V4L2_TUNER_SUB_LANG1)) return fail("lang1 subchan, but no lang1 caps?\n"); if (!(mod.capability & V4L2_TUNER_CAP_LANG2) && (mod.txsubchans & V4L2_TUNER_SUB_LANG2)) return fail("lang2 subchan, but no lang2 caps?\n"); if (!(mod.capability & V4L2_TUNER_CAP_RDS) && (mod.txsubchans & V4L2_TUNER_SUB_RDS)) return fail("RDS subchan, but no RDS caps?\n"); bool have_rds = mod.capability & V4L2_TUNER_CAP_RDS; bool have_rds_method = mod.capability & (V4L2_TUNER_CAP_RDS_BLOCK_IO | V4L2_TUNER_CAP_RDS_CONTROLS); if (have_rds ^ have_rds_method) return fail("V4L2_TUNER_CAP_RDS is set, but not V4L2_TUNER_CAP_RDS_* or vice versa\n"); if ((mod.capability & V4L2_TUNER_CAP_RDS_BLOCK_IO) && !(node->caps & V4L2_CAP_READWRITE)) return fail("V4L2_TUNER_CAP_RDS_BLOCK_IO is set, but not V4L2_CAP_READWRITE\n"); if (!(mod.capability & V4L2_TUNER_CAP_RDS_BLOCK_IO) && (node->caps & V4L2_CAP_READWRITE)) return fail("V4L2_TUNER_CAP_RDS_BLOCK_IO is not set, but V4L2_CAP_READWRITE is\n"); return checkEnumFreqBands(node, mod.index, V4L2_TUNER_RADIO, mod.capability); }
static int checkStd(struct node *node, bool has_std, v4l2_std_id mask, bool is_input) { v4l2_std_id std_mask = 0; v4l2_std_id std; struct v4l2_standard enumstd; unsigned i; int ret; ret = doioctl(node, VIDIOC_G_STD, &std); if (ret && has_std) return fail("STD cap set, but could not get standard\n"); if (!ret && !has_std) return fail("STD cap not set, but could still get a standard\n"); if (ret != ENOTTY && ret != ENODATA && !has_std) return fail("STD cap not set, but got wrong error code (%d)\n", ret); if (!ret && has_std) { if (std & ~mask) warn("current standard is invalid according to the standard mask\n"); if (std == 0) return fail("Standard == 0?!\n"); if (std & V4L2_STD_ATSC) return fail("Current standard contains ATSC standards. This is no longer supported\n"); } ret = doioctl(node, VIDIOC_S_STD, &std); if (ret && has_std) return fail("STD cap set, but could not set standard\n"); if (!ret && !has_std) return fail("STD cap not set, but could still set a standard\n"); std = V4L2_STD_ATSC; ret = doioctl(node, VIDIOC_S_STD, &std); if (ret != EINVAL && ret != ENOTTY) return fail("could set standard to ATSC, which is not supported anymore\n"); for (i = 0; ; i++) { memset(&enumstd, 0xff, sizeof(enumstd)); enumstd.index = i; ret = doioctl(node, VIDIOC_ENUMSTD, &enumstd); if (ret) break; std_mask |= enumstd.id; if (check_ustring(enumstd.name, sizeof(enumstd.name))) return fail("invalid standard name\n"); if (check_0(enumstd.reserved, sizeof(enumstd.reserved))) return fail("reserved not zeroed\n"); if (enumstd.framelines == 0) return fail("framelines not filled\n"); if (enumstd.framelines != 625 && enumstd.framelines != 525) warn("strange framelines value %d for standard %08llx\n", enumstd.framelines, enumstd.id); if (enumstd.frameperiod.numerator == 0 || enumstd.frameperiod.denominator == 0) return fail("frameperiod is not filled\n"); if (enumstd.index != i) return fail("index changed!\n"); if (enumstd.id == 0) return fail("empty standard returned\n"); } if (i == 0 && has_std) return fail("STD cap set, but no standards can be enumerated\n"); if (i && !has_std) return fail("STD cap was not set, but standards can be enumerated\n"); if (ret != ENOTTY && ret != ENODATA && !has_std) return fail("STD cap not set, but got wrong error code for enumeration (%d)\n", ret); if (std_mask & V4L2_STD_ATSC) return fail("STD mask contains ATSC standards. This is no longer supported\n"); if (has_std && std_mask != mask) return fail("the union of ENUMSTD does not match the standard mask (%llx != %llx)\n", std_mask, mask); ret = doioctl(node, VIDIOC_QUERYSTD, &std); if (!ret && !has_std) return fail("STD cap was not set, but could still query standard\n"); if (ret != ENOTTY && ret != ENODATA && !has_std) return fail("STD cap not set, but got wrong error code for query (%d)\n", ret); if (ret != ENOTTY && !is_input) return fail("this is an output, but could still query standard\n"); if (!ret && is_input && (std & ~std_mask)) return fail("QUERYSTD gives back an unsupported standard\n"); return 0; }
static int checkQCtrl(struct node *node, struct test_queryctrl &qctrl) { struct v4l2_querymenu qmenu; __u32 fl = qctrl.flags; __u32 rw_mask = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY; int ret; int i; qctrl.menu_mask = 0; if (check_ustring(qctrl.name, sizeof(qctrl.name))) return fail("invalid name\n"); info("checking v4l2_queryctrl of control '%s' (0x%08x)\n", qctrl.name, qctrl.id); if (qctrl.id & V4L2_CTRL_FLAG_NEXT_CTRL) return fail("V4L2_CTRL_FLAG_NEXT_CTRL not cleared\n"); if (check_0(qctrl.reserved, sizeof(qctrl.reserved))) return fail("non-zero reserved fields\n"); if (qctrl.type == V4L2_CTRL_TYPE_CTRL_CLASS) { if ((qctrl.id & 0xffff) != 1) return fail("invalid control ID for a control class\n"); } else if (qctrl.id < V4L2_CID_PRIVATE_BASE) { if ((qctrl.id & 0xffff) < 0x900) return fail("invalid control ID\n"); } switch (qctrl.type) { case V4L2_CTRL_TYPE_BOOLEAN: if (qctrl.maximum > 1) return fail("invalid boolean max value\n"); /* fall through */ case V4L2_CTRL_TYPE_MENU: case V4L2_CTRL_TYPE_INTEGER_MENU: if (qctrl.step != 1) return fail("invalid step value %d\n", qctrl.step); if (qctrl.minimum < 0) return fail("min < 0\n"); /* fall through */ case V4L2_CTRL_TYPE_INTEGER: if (qctrl.default_value < qctrl.minimum || qctrl.default_value > qctrl.maximum) return fail("def < min || def > max\n"); /* fall through */ case V4L2_CTRL_TYPE_STRING: if (qctrl.minimum > qctrl.maximum) return fail("min > max\n"); if (qctrl.step == 0) return fail("step == 0\n"); if (qctrl.step < 0) return fail("step < 0\n"); if ((unsigned)qctrl.step > (unsigned)(qctrl.maximum - qctrl.minimum)) return fail("step > max - min\n"); if ((qctrl.maximum - qctrl.minimum) % qctrl.step) { // This really should be a fail, but there are so few // drivers that do this right that I made it a warning // for now. warn("%s: (max - min) %% step != 0\n", qctrl.name); } break; case V4L2_CTRL_TYPE_BITMASK: if (qctrl.minimum) return fail("minimum must be 0 for a bitmask control\n"); if (qctrl.step) return fail("step must be 0 for a bitmask control\n"); if (!qctrl.maximum) return fail("maximum must be non-zero for a bitmask control\n"); if (qctrl.default_value & ~qctrl.maximum) return fail("default_value is out of range for a bitmask control\n"); break; case V4L2_CTRL_TYPE_CTRL_CLASS: case V4L2_CTRL_TYPE_INTEGER64: case V4L2_CTRL_TYPE_BUTTON: if (qctrl.minimum || qctrl.maximum || qctrl.step || qctrl.default_value) return fail("non-zero min/max/step/def\n"); break; default: return fail("unknown control type\n"); } if (qctrl.type == V4L2_CTRL_TYPE_STRING && qctrl.default_value) return fail("non-zero default value for string\n"); switch (qctrl.type) { case V4L2_CTRL_TYPE_BUTTON: if ((fl & rw_mask) != V4L2_CTRL_FLAG_WRITE_ONLY) return fail("button control not write only\n"); /* fall through */ case V4L2_CTRL_TYPE_BOOLEAN: case V4L2_CTRL_TYPE_MENU: case V4L2_CTRL_TYPE_INTEGER_MENU: case V4L2_CTRL_TYPE_STRING: case V4L2_CTRL_TYPE_BITMASK: if (fl & V4L2_CTRL_FLAG_SLIDER) return fail("slider makes only sense for integer controls\n"); /* fall through */ case V4L2_CTRL_TYPE_INTEGER64: case V4L2_CTRL_TYPE_INTEGER: if ((fl & rw_mask) == rw_mask) return fail("can't read nor write this control\n"); break; case V4L2_CTRL_TYPE_CTRL_CLASS: if (fl != (V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY)) return fail("invalid flags for control class\n"); break; } if (fl & V4L2_CTRL_FLAG_GRABBED) return fail("GRABBED flag set\n"); if (fl & V4L2_CTRL_FLAG_DISABLED) return fail("DISABLED flag set\n"); if (qctrl.type != V4L2_CTRL_TYPE_MENU && qctrl.type != V4L2_CTRL_TYPE_INTEGER_MENU) { memset(&qmenu, 0xff, sizeof(qmenu)); qmenu.id = qctrl.id; qmenu.index = qctrl.minimum; ret = doioctl(node, VIDIOC_QUERYMENU, &qmenu); if (ret != EINVAL) return fail("can do querymenu on a non-menu control\n"); return 0; } if (qctrl.maximum >= 32) return fail("currently more than 32 menu items are not supported\n"); for (i = 0; i <= qctrl.maximum + 1; i++) { memset(&qmenu, 0xff, sizeof(qmenu)); qmenu.id = qctrl.id; qmenu.index = i; ret = doioctl(node, VIDIOC_QUERYMENU, &qmenu); if (ret && ret != EINVAL) return fail("invalid QUERYMENU return code\n"); if (ret) continue; if (i < qctrl.minimum || i > qctrl.maximum) return fail("can get menu for out-of-range index\n"); if (qmenu.index != (__u32)i || qmenu.id != qctrl.id) return fail("id or index changed\n"); if (qctrl.type == V4L2_CTRL_TYPE_MENU && check_ustring(qmenu.name, sizeof(qmenu.name))) return fail("invalid menu name\n"); if (qmenu.reserved) return fail("reserved is non-zero\n"); qctrl.menu_mask |= 1 << i; } if (qctrl.menu_mask == 0) return fail("no menu items found\n"); if (!(qctrl.menu_mask & (1 << qctrl.default_value))) return fail("the default_value is an invalid menu item\n"); return 0; }
static int checkTuner(struct node *node, const struct v4l2_tuner &tuner, unsigned t, v4l2_std_id std) { bool valid_modes[5] = { true, false, false, true, false }; bool tv = !node->is_radio; enum v4l2_tuner_type type = tv ? V4L2_TUNER_ANALOG_TV : V4L2_TUNER_RADIO; __u32 audmode; if (tuner.index != t) return fail("invalid index\n"); if (check_ustring(tuner.name, sizeof(tuner.name))) return fail("invalid name\n"); if (check_0(tuner.reserved, sizeof(tuner.reserved))) return fail("non-zero reserved fields\n"); if (tuner.type != type) return fail("invalid tuner type %d\n", tuner.type); if (tv && (tuner.capability & V4L2_TUNER_CAP_RDS)) return fail("RDS for TV tuner?\n"); if (!tv && (tuner.capability & (V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2))) return fail("TV capabilities for radio tuner?\n"); if (tv && (tuner.capability & V4L2_TUNER_CAP_LOW)) return fail("did not expect to see V4L2_TUNER_CAP_LOW set for a tv tuner\n"); if (!tv && !(tuner.capability & V4L2_TUNER_CAP_LOW)) return fail("V4L2_TUNER_CAP_LOW was not set for a radio tuner\n"); if (tuner.rangelow >= tuner.rangehigh) return fail("rangelow >= rangehigh\n"); if (tuner.rangelow == 0 || tuner.rangehigh == 0xffffffff) return fail("invalid rangelow or rangehigh\n"); if (!(tuner.capability & V4L2_TUNER_CAP_STEREO) && (tuner.rxsubchans & V4L2_TUNER_SUB_STEREO)) return fail("stereo subchan, but no stereo caps?\n"); if (!(tuner.capability & V4L2_TUNER_CAP_LANG1) && (tuner.rxsubchans & V4L2_TUNER_SUB_LANG1)) return fail("lang1 subchan, but no lang1 caps?\n"); if (!(tuner.capability & V4L2_TUNER_CAP_LANG2) && (tuner.rxsubchans & V4L2_TUNER_SUB_LANG2)) return fail("lang2 subchan, but no lang2 caps?\n"); if (!(tuner.capability & V4L2_TUNER_CAP_RDS) && (tuner.rxsubchans & V4L2_TUNER_SUB_RDS)) return fail("RDS subchan, but no RDS caps?\n"); if (std == V4L2_STD_NTSC_M && (tuner.rxsubchans & V4L2_TUNER_SUB_LANG1)) return fail("LANG1 subchan, but NTSC-M standard\n"); if (tuner.audmode > V4L2_TUNER_MODE_LANG1_LANG2) return fail("invalid audio mode\n"); // Ambiguous whether this is allowed or not // if (!tv && tuner.audmode > V4L2_TUNER_MODE_STEREO) // return -16; if (tuner.signal > 65535) return fail("signal too large\n"); if (tuner.capability & V4L2_TUNER_CAP_STEREO) valid_modes[V4L2_TUNER_MODE_STEREO] = true; if (tuner.capability & V4L2_TUNER_CAP_LANG2) { valid_modes[V4L2_TUNER_MODE_LANG2] = true; valid_modes[V4L2_TUNER_MODE_LANG1_LANG2] = true; } for (audmode = 0; audmode < 5; audmode++) { struct v4l2_tuner tun = { 0 }; tun.index = tuner.index; tun.audmode = audmode; if (doioctl(node, VIDIOC_S_TUNER, &tun)) return fail("cannot set audmode %d\n", audmode); if (doioctl(node, VIDIOC_G_TUNER, &tun)) fail("failure to get new tuner audmode\n"); if (tun.audmode > V4L2_TUNER_MODE_LANG1_LANG2) return fail("invalid new audmode\n"); // Ambiguous whether this is allowed or not // if (!tv && tun.audmode > V4L2_TUNER_MODE_STEREO) // return -21; // if (!valid_modes[tun.audmode]) // return fail("accepted invalid audmode %d\n", audmode); } return 0; }
static int checkTuner(struct node *node, const struct v4l2_tuner &tuner, unsigned t, v4l2_std_id std) { bool valid_modes[5] = { true, false, false, false, false }; bool tv = node->is_video || node->is_vbi; bool hwseek_caps = tuner.capability & (V4L2_TUNER_CAP_HWSEEK_BOUNDED | V4L2_TUNER_CAP_HWSEEK_WRAP | V4L2_TUNER_CAP_HWSEEK_PROG_LIM); unsigned type = tv ? V4L2_TUNER_ANALOG_TV : V4L2_TUNER_RADIO; __u32 audmode; if (tuner.index != t) return fail("invalid index\n"); if (check_ustring(tuner.name, sizeof(tuner.name))) return fail("invalid name\n"); if (check_0(tuner.reserved, sizeof(tuner.reserved))) return fail("non-zero reserved fields\n"); if (node->is_sdr) { fail_on_test(tuner.type != V4L2_TUNER_ADC && tuner.type != V4L2_TUNER_RF); } else if (tuner.type != type) { return fail("invalid tuner type %d\n", tuner.type); } if (tv && (tuner.capability & V4L2_TUNER_CAP_RDS)) return fail("RDS for TV tuner?\n"); if (!tv && (tuner.capability & (V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2))) return fail("TV capabilities for radio tuner?\n"); if (tv && (tuner.capability & (V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_1HZ))) return fail("did not expect to see V4L2_TUNER_CAP_LOW/1HZ set for a tv tuner\n"); if (node->is_radio && !(tuner.capability & V4L2_TUNER_CAP_LOW)) return fail("V4L2_TUNER_CAP_LOW was not set for a radio tuner\n"); fail_on_test((tuner.capability & V4L2_TUNER_CAP_LOW) && (tuner.capability & V4L2_TUNER_CAP_1HZ)); if (node->is_sdr) fail_on_test(!(V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_1HZ)); fail_on_test(!(tuner.capability & V4L2_TUNER_CAP_FREQ_BANDS)); fail_on_test(!(node->caps & V4L2_CAP_HW_FREQ_SEEK) && hwseek_caps); fail_on_test((node->caps & V4L2_CAP_HW_FREQ_SEEK) && !(tuner.capability & (V4L2_TUNER_CAP_HWSEEK_BOUNDED | V4L2_TUNER_CAP_HWSEEK_WRAP))); if (tuner.rangelow >= tuner.rangehigh) return fail("rangelow >= rangehigh\n"); if (tuner.rangelow == 0 || tuner.rangehigh == 0xffffffff) return fail("invalid rangelow or rangehigh\n"); if (!(tuner.capability & V4L2_TUNER_CAP_STEREO) && (tuner.rxsubchans & V4L2_TUNER_SUB_STEREO)) return fail("stereo subchan, but no stereo caps?\n"); if (!(tuner.capability & V4L2_TUNER_CAP_LANG1) && (tuner.rxsubchans & V4L2_TUNER_SUB_LANG1)) return fail("lang1 subchan, but no lang1 caps?\n"); if (!(tuner.capability & V4L2_TUNER_CAP_LANG2) && (tuner.rxsubchans & V4L2_TUNER_SUB_LANG2)) return fail("lang2 subchan, but no lang2 caps?\n"); if (!(tuner.capability & V4L2_TUNER_CAP_RDS) && (tuner.rxsubchans & V4L2_TUNER_SUB_RDS)) return fail("RDS subchan, but no RDS caps?\n"); bool have_rds = tuner.capability & V4L2_TUNER_CAP_RDS; bool have_rds_method = tuner.capability & (V4L2_TUNER_CAP_RDS_BLOCK_IO | V4L2_TUNER_CAP_RDS_CONTROLS); if (have_rds ^ have_rds_method) return fail("V4L2_TUNER_CAP_RDS is set, but not V4L2_TUNER_CAP_RDS_* or vice versa\n"); fail_on_test(node->is_sdr && have_rds); if ((tuner.capability & V4L2_TUNER_CAP_RDS_BLOCK_IO) && !(node->caps & V4L2_CAP_READWRITE)) return fail("V4L2_TUNER_CAP_RDS_BLOCK_IO is set, but not V4L2_CAP_READWRITE\n"); if (node->is_radio && !(tuner.capability & V4L2_TUNER_CAP_RDS_BLOCK_IO) && (node->caps & V4L2_CAP_READWRITE)) return fail("V4L2_TUNER_CAP_RDS_BLOCK_IO is not set, but V4L2_CAP_READWRITE is\n"); if (std == V4L2_STD_NTSC_M && (tuner.rxsubchans & V4L2_TUNER_SUB_LANG1)) return fail("LANG1 subchan, but NTSC-M standard\n"); if (tuner.audmode > V4L2_TUNER_MODE_LANG1_LANG2) return fail("invalid audio mode\n"); if (!tv && tuner.audmode > V4L2_TUNER_MODE_STEREO) return fail("invalid audio mode for radio device\n"); if (tuner.signal > 65535) return fail("signal too large\n"); if (tuner.capability & V4L2_TUNER_CAP_STEREO) valid_modes[V4L2_TUNER_MODE_STEREO] = true; if (tuner.capability & V4L2_TUNER_CAP_LANG1) valid_modes[V4L2_TUNER_MODE_LANG1] = true; if (tuner.capability & V4L2_TUNER_CAP_LANG2) { valid_modes[V4L2_TUNER_MODE_LANG2] = true; valid_modes[V4L2_TUNER_MODE_LANG1_LANG2] = true; } for (audmode = 0; audmode < 5; audmode++) { struct v4l2_tuner tun = { 0 }; tun.index = tuner.index; tun.audmode = audmode; if (doioctl(node, VIDIOC_S_TUNER, &tun)) return fail("cannot set audmode %d\n", audmode); if (doioctl(node, VIDIOC_G_TUNER, &tun)) fail("failure to get new tuner audmode\n"); if (tun.audmode > V4L2_TUNER_MODE_LANG1_LANG2) return fail("invalid new audmode\n"); if (!valid_modes[tun.audmode]) return fail("accepted invalid audmode %d\n", audmode); } return checkEnumFreqBands(node, tuner.index, tuner.type, tuner.capability); }
static int testCap(struct node *node) { struct v4l2_capability vcap; __u32 caps, dcaps; const __u32 video_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_VIDEO_M2M | V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_VIDEO_OUTPUT_OVERLAY; const __u32 vbi_caps = V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE | V4L2_CAP_VBI_OUTPUT | V4L2_CAP_SLICED_VBI_OUTPUT; const __u32 radio_caps = V4L2_CAP_RADIO | V4L2_CAP_MODULATOR; const __u32 input_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE | V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_TUNER; const __u32 output_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_VIDEO_OUTPUT_OVERLAY | V4L2_CAP_VBI_OUTPUT | V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_MODULATOR; const __u32 overlay_caps = V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_VIDEO_OUTPUT_OVERLAY; const __u32 m2m_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_VIDEO_M2M_MPLANE; const __u32 io_caps = V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; const __u32 mplane_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_VIDEO_M2M_MPLANE; const __u32 splane_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_M2M; memset(&vcap, 0xff, sizeof(vcap)); // Must always be there fail_on_test(doioctl(node, VIDIOC_QUERYCAP, &vcap)); fail_on_test(check_ustring(vcap.driver, sizeof(vcap.driver))); fail_on_test(check_ustring(vcap.card, sizeof(vcap.card))); fail_on_test(check_ustring(vcap.bus_info, sizeof(vcap.bus_info))); // Check for valid prefixes if (memcmp(vcap.bus_info, "usb-", 4) && memcmp(vcap.bus_info, "PCI:", 4) && memcmp(vcap.bus_info, "PCIe:", 5) && memcmp(vcap.bus_info, "ISA:", 4) && memcmp(vcap.bus_info, "I2C:", 4) && memcmp(vcap.bus_info, "parport", 7) && memcmp(vcap.bus_info, "platform:", 9)) return fail("missing bus_info prefix ('%s')\n", vcap.bus_info); fail_on_test((vcap.version >> 16) < 3); fail_on_test(check_0(vcap.reserved, sizeof(vcap.reserved))); caps = vcap.capabilities; dcaps = vcap.device_caps; node->is_m2m = dcaps & m2m_caps; fail_on_test(caps == 0); fail_on_test(caps & V4L2_CAP_ASYNCIO); fail_on_test(!(caps & V4L2_CAP_DEVICE_CAPS)); fail_on_test(dcaps & V4L2_CAP_DEVICE_CAPS); fail_on_test(dcaps & ~caps); fail_on_test(!(dcaps & caps)); fail_on_test(node->is_video && !(dcaps & video_caps)); fail_on_test(node->is_radio && !(dcaps & radio_caps)); // V4L2_CAP_AUDIO is invalid for radio fail_on_test(node->is_radio && (dcaps & V4L2_CAP_AUDIO)); fail_on_test(node->is_vbi && !(dcaps & vbi_caps)); // You can't have both set due to missing buffer type in VIDIOC_G/S_FBUF fail_on_test((dcaps & overlay_caps) == overlay_caps); // Overlay support makes no sense for m2m devices fail_on_test((dcaps & m2m_caps) && (dcaps & overlay_caps)); fail_on_test(node->is_video && (dcaps & (vbi_caps | radio_caps))); fail_on_test(node->is_radio && (dcaps & (vbi_caps | video_caps))); fail_on_test(node->is_vbi && (dcaps & (video_caps | radio_caps))); if (node->is_m2m) { // This will become an error as this combination of caps // is on the feature removal list. if ((dcaps & input_caps) && (dcaps & output_caps)) warn("VIDIOC_QUERYCAP: m2m with video input and output caps\n"); } else { if (dcaps & input_caps) fail_on_test(dcaps & output_caps); if (dcaps & output_caps) fail_on_test(dcaps & input_caps); } if (node->can_capture || node->can_output) { // whether io_caps need to be set for RDS capture/output is // checked elsewhere as that depends on the tuner/modulator // capabilities. if (!(dcaps & (V4L2_CAP_RDS_CAPTURE | V4L2_CAP_RDS_OUTPUT))) fail_on_test(!(dcaps & io_caps)); } else { fail_on_test(dcaps & io_caps); } // having both mplane and splane caps is not allowed (at least for now) fail_on_test((dcaps & mplane_caps) && (dcaps & splane_caps)); return 0; }