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 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 checkTimings(struct node *node, bool has_timings) { struct v4l2_enum_dv_timings enumtimings; struct v4l2_dv_timings timings; int ret; unsigned i; memset(&timings, 0xff, sizeof(timings)); ret = doioctl(node, VIDIOC_G_DV_TIMINGS, &timings); if (ret && has_timings) return fail("TIMINGS cap set, but could not get current timings\n"); if (!ret && !has_timings) return fail("TIMINGS cap not set, but could still get timings\n"); for (i = 0; ; i++) { memset(&enumtimings, 0xff, sizeof(enumtimings)); enumtimings.index = i; ret = doioctl(node, VIDIOC_ENUM_DV_TIMINGS, &enumtimings); if (ret) break; if (check_0(enumtimings.reserved, sizeof(enumtimings.reserved))) return fail("reserved not zeroed\n"); if (enumtimings.index != i) return fail("index changed!\n"); } if (i == 0 && has_timings) return fail("TIMINGS cap set, but no timings can be enumerated\n"); if (i && !has_timings) return fail("TIMINGS cap was not set, but timings can be enumerated\n"); ret = doioctl(node, VIDIOC_QUERY_DV_TIMINGS, &timings); if (!ret && !has_timings) return fail("TIMINGS cap was not set, but could still query timings\n"); return 0; }
static int testSlicedVBICapType(struct node *node, enum v4l2_buf_type type) { struct v4l2_sliced_vbi_cap cap; bool sliced_type = (type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE || type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT); __u32 service_set = 0; int ret; memset(&cap, 0xff, sizeof(cap)); memset(&cap.reserved, 0, sizeof(cap.reserved)); cap.type = type; ret = doioctl(node, VIDIOC_G_SLICED_VBI_CAP, &cap); fail_on_test(check_0(cap.reserved, sizeof(cap.reserved))); fail_on_test(cap.type != type); fail_on_test(ret && ret != EINVAL && sliced_type); if (ret == EINVAL) { fail_on_test(sliced_type && (node->caps & buftype2cap[type])); if (node->caps & (V4L2_CAP_SLICED_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_OUTPUT)) return 0; return -ENOSYS; } if (ret) return fail("expected EINVAL, but got %d when getting sliced VBI caps buftype %d\n", ret, type); fail_on_test(!(node->caps & buftype2cap[type])); for (int f = 0; f < 2; f++) for (int i = 0; i < 24; i++) service_set |= cap.service_lines[f][i]; fail_on_test(cap.service_set != service_set); fail_on_test(cap.service_lines[0][0] || cap.service_lines[1][0]); 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 testSlicedVBICapType(struct node *node, unsigned type) { struct v4l2_sliced_vbi_cap cap; bool sliced_type = (type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE || type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT); __u32 service_set = 0; int ret; memset(&cap, 0xff, sizeof(cap)); memset(&cap.reserved, 0, sizeof(cap.reserved)); cap.type = type; ret = doioctl(node, VIDIOC_G_SLICED_VBI_CAP, &cap); if (ret == ENOTTY) { fail_on_test(sliced_type && (node->caps & buftype2cap[type])); return ret; } fail_on_test(check_0(cap.reserved, sizeof(cap.reserved))); fail_on_test(cap.type != type); fail_on_test(ret && ret != EINVAL); fail_on_test(ret && sliced_type && (node->caps & buftype2cap[type])); fail_on_test(!ret && (!sliced_type || !(node->caps & buftype2cap[type]))); if (ret) return 0; for (int f = 0; f < 2; f++) for (int i = 0; i < 24; i++) service_set |= cap.service_lines[f][i]; fail_on_test(cap.service_set != service_set); fail_on_test(cap.service_lines[0][0] || cap.service_lines[1][0]); return 0; }
static int checkEnumFreqBands(struct node *node, __u32 tuner, __u32 type, __u32 caps) { unsigned i; __u32 caps_union = 0; for (i = 0; ; i++) { struct v4l2_frequency_band band; int ret; memset(band.reserved, 0, sizeof(band.reserved)); band.tuner = tuner; band.type = type; band.index = i; ret = doioctl(node, VIDIOC_ENUM_FREQ_BANDS, &band); if (ret == EINVAL && i) return 0; if (ret) return fail("couldn't get freq band\n"); caps_union |= band.capability; if ((caps & V4L2_TUNER_CAP_LOW) != (band.capability & V4L2_TUNER_CAP_LOW)) return fail("Inconsistent CAP_LOW usage\n"); fail_on_test(band.rangehigh < band.rangelow); fail_on_test(band.index != i); fail_on_test(band.type != type); fail_on_test(band.tuner != tuner); fail_on_test((band.capability & V4L2_TUNER_CAP_FREQ_BANDS) == 0); check_0(band.reserved, sizeof(band.reserved)); } fail_on_test(caps_union != caps); 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"); 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 testParmStruct(struct node *node, struct v4l2_streamparm &parm) { struct v4l2_captureparm *cap = &parm.parm.capture; struct v4l2_outputparm *out = &parm.parm.output; int ret; switch (parm.type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: ret = check_0(cap->reserved, sizeof(cap->reserved)); if (ret) return fail("reserved not zeroed\n"); fail_on_test(cap->readbuffers > VIDEO_MAX_FRAME); if (!(node->caps & V4L2_CAP_READWRITE)) fail_on_test(cap->readbuffers); else if (node->caps & V4L2_CAP_STREAMING) fail_on_test(!cap->readbuffers); fail_on_test(cap->capability & ~V4L2_CAP_TIMEPERFRAME); fail_on_test(cap->capturemode & ~V4L2_MODE_HIGHQUALITY); if (cap->capturemode & V4L2_MODE_HIGHQUALITY) warn("V4L2_MODE_HIGHQUALITY is poorly defined\n"); fail_on_test(cap->extendedmode); if (cap->capability & V4L2_CAP_TIMEPERFRAME) fail_on_test(cap->timeperframe.numerator == 0 || cap->timeperframe.denominator == 0); break; case V4L2_BUF_TYPE_VIDEO_OUTPUT: case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: ret = check_0(out->reserved, sizeof(out->reserved)); if (ret) return fail("reserved not zeroed\n"); fail_on_test(out->writebuffers > VIDEO_MAX_FRAME); if (!(node->caps & V4L2_CAP_READWRITE)) fail_on_test(out->writebuffers); else if (node->caps & V4L2_CAP_STREAMING) fail_on_test(!out->writebuffers); fail_on_test(out->capability & ~V4L2_CAP_TIMEPERFRAME); fail_on_test(out->outputmode); fail_on_test(out->extendedmode); if (out->capability & V4L2_CAP_TIMEPERFRAME) fail_on_test(out->timeperframe.numerator == 0 || out->timeperframe.denominator == 0); break; default: break; } 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; }
int testTunerHwSeek(struct node *node) { struct v4l2_hw_freq_seek seek; unsigned t; int ret; for (t = 0; t < node->tuners; t++) { struct v4l2_tuner tuner; tuner.index = t; ret = doioctl(node, VIDIOC_G_TUNER, &tuner); if (ret) return fail("could not get tuner %d\n", t); memset(&seek, 0, sizeof(seek)); seek.tuner = t; seek.type = V4L2_TUNER_RADIO; ret = doioctl(node, VIDIOC_S_HW_FREQ_SEEK, &seek); if (!(node->caps & V4L2_CAP_HW_FREQ_SEEK) && ret != ENOTTY) return fail("hw seek supported but capability not set\n"); if (!node->is_radio && ret != ENOTTY) return fail("hw seek supported on a non-radio node?!\n"); if (!node->is_radio || !(node->caps & V4L2_CAP_HW_FREQ_SEEK)) return ENOTTY; seek.type = V4L2_TUNER_ANALOG_TV; ret = doioctl(node, VIDIOC_S_HW_FREQ_SEEK, &seek); if (ret != EINVAL) return fail("hw seek accepted TV tuner\n"); seek.type = V4L2_TUNER_RADIO; seek.seek_upward = 1; ret = doioctl(node, VIDIOC_S_HW_FREQ_SEEK, &seek); if (ret == EINVAL && (tuner.capability & V4L2_TUNER_CAP_HWSEEK_BOUNDED)) return fail("hw bounded seek failed\n"); if (ret && ret != EINVAL && ret != ENODATA) return fail("hw bounded seek failed with error %d\n", ret); seek.wrap_around = 1; ret = doioctl(node, VIDIOC_S_HW_FREQ_SEEK, &seek); if (ret == EINVAL && (tuner.capability & V4L2_TUNER_CAP_HWSEEK_WRAP)) return fail("hw wrapped seek failed\n"); if (ret && ret != EINVAL && ret != ENODATA) return fail("hw wrapped seek failed with error %d\n", ret); if (check_0(seek.reserved, sizeof(seek.reserved))) return fail("non-zero reserved fields\n"); } memset(&seek, 0, sizeof(seek)); seek.tuner = node->tuners; seek.type = V4L2_TUNER_RADIO; ret = doioctl(node, VIDIOC_S_HW_FREQ_SEEK, &seek); if (ret != EINVAL && ret != ENOTTY) return fail("hw seek for invalid tuner didn't return EINVAL or ENOTTY\n"); return ret == ENOTTY ? ret : 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 checkTimingsCap(struct node *node, bool has_timings) { struct v4l2_dv_timings_cap timingscap; int ret; memset(&timingscap, 0xff, sizeof(timingscap)); ret = doioctl(node, VIDIOC_DV_TIMINGS_CAP, &timingscap); if (ret && has_timings) return fail("TIMINGS cap set, but could not get timings caps\n"); if (!ret && !has_timings) return fail("TIMINGS cap not set, but could still get timings caps\n"); if (ret && !has_timings) return 0; if (check_0(timingscap.reserved, sizeof(timingscap.reserved))) return fail("reserved not zeroed\n"); fail_on_test(timingscap.type != V4L2_DV_BT_656_1120); if (check_0(timingscap.bt.reserved, sizeof(timingscap.bt.reserved))) return fail("reserved not zeroed\n"); fail_on_test(timingscap.bt.min_width > timingscap.bt.max_width); fail_on_test(timingscap.bt.min_height > timingscap.bt.max_height); fail_on_test(timingscap.bt.min_pixelclock > timingscap.bt.max_pixelclock); return 0; }
int testEncIndex(struct node *node) { struct v4l2_enc_idx idx; int ret; memset(&idx, 0xff, sizeof(idx)); ret = doioctl(node, VIDIOC_G_ENC_INDEX, &idx); if (ret == ENOTTY) return ret; if (check_0(idx.reserved, sizeof(idx.reserved))) return fail("idx.reserved not zeroed\n"); fail_on_test(ret); fail_on_test(idx.entries != 0); fail_on_test(idx.entries_cap == 0); 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); }
int testExtendedControls(struct node *node) { qctrl_list::iterator iter; struct v4l2_ext_controls ctrls; std::vector<struct v4l2_ext_control> total_vec; std::vector<struct v4l2_ext_control> class_vec; struct v4l2_ext_control ctrl; __u32 ctrl_class = 0; bool multiple_classes = false; int ret; memset(&ctrls, 0, sizeof(ctrls)); ret = doioctl(node, VIDIOC_G_EXT_CTRLS, &ctrls); if (ret == ENOTTY && node->controls.empty()) return ret; if (ret) return fail("g_ext_ctrls does not support count == 0\n"); if (node->controls.empty()) return fail("g_ext_ctrls worked even when no controls are present\n"); if (ctrls.ctrl_class) return fail("field ctrl_class changed\n"); if (ctrls.count) return fail("field count changed\n"); if (check_0(ctrls.reserved, sizeof(ctrls.reserved))) return fail("reserved not zeroed\n"); memset(&ctrls, 0, sizeof(ctrls)); ret = doioctl(node, VIDIOC_TRY_EXT_CTRLS, &ctrls); if (ret == ENOTTY && node->controls.empty()) return ret; if (ret) return fail("try_ext_ctrls does not support count == 0\n"); if (node->controls.empty()) return fail("try_ext_ctrls worked even when no controls are present\n"); if (ctrls.ctrl_class) return fail("field ctrl_class changed\n"); if (ctrls.count) return fail("field count changed\n"); if (check_0(ctrls.reserved, sizeof(ctrls.reserved))) return fail("reserved not zeroed\n"); for (iter = node->controls.begin(); iter != node->controls.end(); ++iter) { info("checking extended control '%s' (0x%08x)\n", iter->name, iter->id); ctrl.id = iter->id; ctrl.size = 0; ctrl.reserved2[0] = 0; ctrls.count = 1; // Either should work, so try both semi-randomly ctrls.ctrl_class = (ctrl.id & 1) ? 0 : V4L2_CTRL_ID2CLASS(ctrl.id); ctrls.controls = &ctrl; // Get the current value ret = doioctl(node, VIDIOC_G_EXT_CTRLS, &ctrls); if ((iter->flags & V4L2_CTRL_FLAG_WRITE_ONLY)) { if (ret != EACCES) return fail("g_ext_ctrls did not check the write-only flag\n"); if (ctrls.error_idx != ctrls.count) return fail("invalid error index write only control\n"); ctrl.id = iter->id; ctrl.value = iter->default_value; } else { if (ret != ENOSPC && iter->type == V4L2_CTRL_TYPE_STRING) return fail("did not check against size\n"); if (ret == ENOSPC && iter->type == V4L2_CTRL_TYPE_STRING) { if (ctrls.error_idx != 0) return fail("invalid error index string control\n"); ctrl.string = new char[iter->maximum + 1]; ctrl.size = iter->maximum + 1; ret = doioctl(node, VIDIOC_G_EXT_CTRLS, &ctrls); } if (ret == EIO) { warn("g_ext_ctrls returned EIO\n"); ret = 0; } if (ret) return fail("g_ext_ctrls returned an error (%d)\n", ret); if (checkExtendedCtrl(ctrl, *iter)) return fail("invalid control %08x\n", iter->id); } // Try the current value (or the default value for write only controls) ret = doioctl(node, VIDIOC_TRY_EXT_CTRLS, &ctrls); if (iter->flags & V4L2_CTRL_FLAG_READ_ONLY) { if (ret != EACCES) return fail("try_ext_ctrls did not check the read-only flag\n"); if (ctrls.error_idx != 0) return fail("invalid error index read only control\n"); } else if (ret) { return fail("try_ext_ctrls returned an error\n"); } // Try to set the current value (or the default value for write only controls) ret = doioctl(node, VIDIOC_S_EXT_CTRLS, &ctrls); if (iter->flags & V4L2_CTRL_FLAG_READ_ONLY) { if (ret != EACCES) return fail("s_ext_ctrls did not check the read-only flag\n"); if (ctrls.error_idx != ctrls.count) return fail("invalid error index\n"); } else { if (ret == EIO) { warn("s_ext_ctrls returned EIO\n"); ret = 0; } if (ret) return fail("s_ext_ctrls returned an error\n"); if (checkExtendedCtrl(ctrl, *iter)) return fail("s_ext_ctrls returned invalid control contents (%08x)\n", iter->id); } if (iter->type == V4L2_CTRL_TYPE_STRING) delete [] ctrl.string; ctrl.string = NULL; } ctrls.ctrl_class = 0; ctrl.id = 0; ctrl.size = 0; ret = doioctl(node, VIDIOC_G_EXT_CTRLS, &ctrls); if (ret != EINVAL) return fail("g_ext_ctrls accepted invalid control ID\n"); if (ctrls.error_idx != ctrls.count) return fail("g_ext_ctrls(0) invalid error_idx\n"); ctrl.id = 0; ctrl.size = 0; ctrl.value = 0; ret = doioctl(node, VIDIOC_TRY_EXT_CTRLS, &ctrls); if (ret != EINVAL) return fail("try_ext_ctrls accepted invalid control ID\n"); if (ctrls.error_idx != 0) return fail("try_ext_ctrls(0) invalid error_idx\n"); ret = doioctl(node, VIDIOC_S_EXT_CTRLS, &ctrls); if (ret != EINVAL) return fail("s_ext_ctrls accepted invalid control ID\n"); if (ctrls.error_idx != ctrls.count) return fail("s_ext_ctrls(0) invalid error_idx\n"); for (iter = node->controls.begin(); iter != node->controls.end(); ++iter) { struct v4l2_ext_control ctrl; if (iter->flags & (V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY)) continue; ctrl.id = iter->id; ctrl.size = 0; if (iter->type == V4L2_CTRL_TYPE_STRING) { ctrl.size = iter->maximum + 1; ctrl.string = new char[ctrl.size]; } ctrl.reserved2[0] = 0; if (!ctrl_class) ctrl_class = V4L2_CTRL_ID2CLASS(ctrl.id); else if (ctrl_class != V4L2_CTRL_ID2CLASS(ctrl.id)) multiple_classes = true; total_vec.push_back(ctrl); } ctrls.count = total_vec.size(); ctrls.controls = &total_vec[0]; ret = doioctl(node, VIDIOC_G_EXT_CTRLS, &ctrls); if (ret) return fail("could not get all controls\n"); ret = doioctl(node, VIDIOC_TRY_EXT_CTRLS, &ctrls); if (ret) return fail("could not try all controls\n"); ret = doioctl(node, VIDIOC_S_EXT_CTRLS, &ctrls); if (ret == EIO) { warn("s_ext_ctrls returned EIO\n"); ret = 0; } if (ret) return fail("could not set all controls\n"); ctrls.ctrl_class = ctrl_class; ret = doioctl(node, VIDIOC_G_EXT_CTRLS, &ctrls); if (ret && !multiple_classes) return fail("could not get all controls of a specific class\n"); if (ret != EINVAL && multiple_classes) return fail("should get EINVAL when getting mixed-class controls\n"); if (multiple_classes && ctrls.error_idx != ctrls.count) return fail("error_idx should be equal to count\n"); ret = doioctl(node, VIDIOC_TRY_EXT_CTRLS, &ctrls); if (ret && !multiple_classes) return fail("could not try all controls of a specific class\n"); if (ret != EINVAL && multiple_classes) return fail("should get EINVAL when trying mixed-class controls\n"); if (multiple_classes && ctrls.error_idx >= ctrls.count) return fail("error_idx should be < count\n"); ret = doioctl(node, VIDIOC_S_EXT_CTRLS, &ctrls); if (ret == EIO) { warn("s_ext_ctrls returned EIO\n"); ret = 0; } if (ret && !multiple_classes) return fail("could not set all controls of a specific class\n"); if (ret != EINVAL && multiple_classes) return fail("should get EINVAL when setting mixed-class controls\n"); if (multiple_classes && ctrls.error_idx != ctrls.count) return fail("error_idx should be equal to count\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; }
int testModulatorFreq(struct node *node) { struct v4l2_frequency freq; unsigned m; int ret; for (m = 0; m < node->modulators; m++) { struct v4l2_modulator modulator; modulator.index = m; ret = doioctl(node, VIDIOC_G_MODULATOR, &modulator); if (ret) return fail("could not get modulator %d\n", m); memset(&freq, 0, sizeof(freq)); freq.tuner = m; ret = doioctl(node, VIDIOC_G_FREQUENCY, &freq); if (ret) return fail("could not get frequency for modulator %d\n", m); if (check_0(freq.reserved, sizeof(freq.reserved))) return fail("reserved was not zeroed\n"); if (freq.tuner != m) return fail("frequency modulator field changed!\n"); if (freq.frequency == 0) return fail("frequency not set\n"); if (freq.frequency < modulator.rangelow || freq.frequency > modulator.rangehigh) warn("returned modulator %d frequency out of range (%d not in [%d...%d])\n", m, freq.frequency, modulator.rangelow, modulator.rangehigh); ret = doioctl(node, VIDIOC_S_FREQUENCY, &freq); if (ret) return fail("could not set current frequency\n"); freq.frequency = modulator.rangelow; ret = doioctl(node, VIDIOC_S_FREQUENCY, &freq); if (ret) return fail("could not set rangelow frequency\n"); freq.frequency = modulator.rangehigh; ret = doioctl(node, VIDIOC_S_FREQUENCY, &freq); if (ret) return fail("could not set rangehigh frequency\n"); freq.frequency = modulator.rangelow - 1; ret = doioctl(node, VIDIOC_S_FREQUENCY, &freq); if (ret) return fail("could not set rangelow-1 frequency\n"); ret = doioctl(node, VIDIOC_G_FREQUENCY, &freq); if (ret || freq.frequency != modulator.rangelow) return fail("frequency rangelow-1 wasn't mapped to rangelow\n"); freq.frequency = modulator.rangehigh + 1; ret = doioctl(node, VIDIOC_S_FREQUENCY, &freq); if (ret) return fail("could not set rangehigh+1 frequency\n"); ret = doioctl(node, VIDIOC_G_FREQUENCY, &freq); if (ret || freq.frequency != modulator.rangehigh) return fail("frequency rangehigh+1 wasn't mapped to rangehigh\n"); } /* If this is a tuner device, then skip the remaining tests */ if (node->caps & V4L2_CAP_TUNER) return 0; freq.tuner = m; ret = doioctl(node, VIDIOC_G_FREQUENCY, &freq); if (ret != EINVAL && ret != ENOTTY) return fail("could get frequency for invalid modulator %d\n", m); freq.tuner = m; // Radio: 100 MHz freq.frequency = 1600000; ret = doioctl(node, VIDIOC_S_FREQUENCY, &freq); if (ret != EINVAL && ret != ENOTTY) return fail("could set frequency for invalid modulator %d\n", m); return node->modulators ? 0 : ENOTTY; }
static int testEnumFrameIntervals(struct node *node, __u32 pixfmt, __u32 w, __u32 h, bool valid) { struct v4l2_frmivalenum frmival; struct v4l2_frmival_stepwise *sw = &frmival.stepwise; bool found_stepwise = false; unsigned f = 0; int ret; for (;;) { memset(&frmival, 0xff, sizeof(frmival)); frmival.index = f; frmival.pixel_format = pixfmt; frmival.width = w; frmival.height = h; ret = doioctl(node, VIDIOC_ENUM_FRAMEINTERVALS, &frmival); if (ret == ENOTTY) return ret; if (f == 0 && ret == EINVAL) { if (valid) warn("found framesize %dx%d, but no frame intervals\n", w, h); return ENOTTY; } if (ret == EINVAL) break; if (ret) return fail("expected EINVAL, but got %d when enumerating frameinterval %d\n", ret, f); ret = check_0(frmival.reserved, sizeof(frmival.reserved)); if (ret) return fail("frmival.reserved not zeroed\n"); if (frmival.pixel_format != pixfmt || frmival.index != f || frmival.width != w || frmival.height != h) return fail("frmival.pixel_format, index, width or height changed\n"); switch (frmival.type) { case V4L2_FRMIVAL_TYPE_DISCRETE: ret = check_fract(&frmival.discrete); if (found_stepwise) return fail("mixing discrete and stepwise is not allowed\n"); break; case V4L2_FRMIVAL_TYPE_CONTINUOUS: if (sw->step.numerator != 1 || sw->step.denominator != 1) return fail("invalid step for continuous frameinterval\n"); /* fallthrough */ case V4L2_FRMIVAL_TYPE_STEPWISE: if (frmival.index) return fail("index must be 0 for stepwise/continuous frameintervals\n"); found_stepwise = true; ret = check_fract(&sw->min); if (ret == 0) ret = check_fract(&sw->max); if (ret == 0) ret = check_fract(&sw->step); if (ret) return fail("invalid min, max or step for frameinterval %d\n", f); if (fract2f(&sw->min) > fract2f(&sw->max)) return fail("min > max\n"); if (fract2f(&sw->step) > fract2f(&sw->max) - fract2f(&sw->min)) return fail("step > (max - min)\n"); break; default: return fail("frmival.type is invalid\n"); } f++; } if (!valid) return fail("found frame intervals for invalid size %dx%d\n", w, h); info("found %d frameintervals for pixel format %08x and size %dx%d\n", f, pixfmt, w, h); return 0; }
static int testFormatsType(struct node *node, int ret, unsigned type, struct v4l2_format &fmt) { pixfmt_set &set = node->buftype_pixfmts[type]; pixfmt_set *set_splane; struct v4l2_pix_format &pix = fmt.fmt.pix; struct v4l2_pix_format_mplane &pix_mp = fmt.fmt.pix_mp; struct v4l2_window &win = fmt.fmt.win; struct v4l2_vbi_format &vbi = fmt.fmt.vbi; struct v4l2_sliced_vbi_format &sliced = fmt.fmt.sliced; unsigned min_data_samples; unsigned min_sampling_rate; v4l2_std_id std; __u32 service_set = 0; unsigned cnt = 0; if (ret == ENOTTY) return ret; if (ret == EINVAL) return ENOTTY; if (ret) return fail("expected EINVAL, but got %d when getting format for buftype %d\n", ret, type); fail_on_test(fmt.type != type); switch (type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_OUTPUT: fail_on_test(!pix.width || !pix.height); if (set.find(pix.pixelformat) == set.end()) return fail("unknown pixelformat %08x for buftype %d\n", pix.pixelformat, type); fail_on_test(pix.bytesperline && pix.bytesperline < pix.width); fail_on_test(!pix.sizeimage); fail_on_test(!pix.colorspace); fail_on_test(pix.field == V4L2_FIELD_ANY); if (pix.priv) warn("priv is non-zero!\n"); break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: fail_on_test(!pix_mp.width || !pix_mp.height); set_splane = &node->buftype_pixfmts[type - 8]; if (set.find(pix_mp.pixelformat) == set.end() && set_splane->find(pix_mp.pixelformat) == set_splane->end()) return fail("unknown pixelformat %08x for buftype %d\n", pix_mp.pixelformat, type); fail_on_test(!pix_mp.colorspace); fail_on_test(pix.field == V4L2_FIELD_ANY); ret = check_0(pix_mp.reserved, sizeof(pix_mp.reserved)); if (ret) return fail("pix_mp.reserved not zeroed\n"); fail_on_test(pix_mp.num_planes == 0 || pix_mp.num_planes >= VIDEO_MAX_PLANES); for (int i = 0; i < pix_mp.num_planes; i++) { struct v4l2_plane_pix_format &pfmt = pix_mp.plane_fmt[i]; ret = check_0(pfmt.reserved, sizeof(pfmt.reserved)); if (ret) return fail("pix_mp.plane_fmt[%d].reserved not zeroed\n", i); fail_on_test(!pfmt.sizeimage); fail_on_test(pfmt.bytesperline && pfmt.bytesperline < pix_mp.width); } break; case V4L2_BUF_TYPE_VBI_CAPTURE: case V4L2_BUF_TYPE_VBI_OUTPUT: // Currently VBI assumes that you have G_STD as well. fail_on_test(doioctl(node, VIDIOC_G_STD, &std)); if (std & V4L2_STD_625_50) { min_sampling_rate = 6937500; // the number of databits for PAL teletext is 18 (clock run in) + // 6 (framing code) + 42 * 8 (data). min_data_samples = (vbi.sampling_rate * (18 + 6 + 42 * 8)) / min_sampling_rate; } else { min_sampling_rate = 5727272; // the number of databits for NTSC teletext is 18 (clock run in) + // 6 (framing code) + 34 * 8 (data). min_data_samples = (vbi.sampling_rate * (18 + 6 + 34 * 8)) / min_sampling_rate; } fail_on_test(vbi.sampling_rate < min_sampling_rate); fail_on_test(!vbi.samples_per_line); fail_on_test(vbi.sample_format != V4L2_PIX_FMT_GREY); fail_on_test(vbi.offset > vbi.samples_per_line); ret = check_0(vbi.reserved, sizeof(vbi.reserved)); if (ret) return fail("vbi.reserved not zeroed\n"); // Check that offset leaves enough room for the maximum required // amount of data. fail_on_test(min_data_samples > vbi.samples_per_line - vbi.offset); fail_on_test(!vbi.count[0] || !vbi.count[1]); fail_on_test(vbi.flags & ~(V4L2_VBI_UNSYNC | V4L2_VBI_INTERLACED)); if (vbi.flags & V4L2_VBI_INTERLACED) fail_on_test(vbi.count[0] != vbi.count[1]); break; case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: ret = check_0(sliced.reserved, sizeof(sliced.reserved)); if (ret) return fail("sliced.reserved not zeroed\n"); fail_on_test(sliced.service_lines[0][0] || sliced.service_lines[1][0]); for (int f = 0; f < 2; f++) { for (int i = 0; i < 24; i++) { if (sliced.service_lines[f][i]) cnt++; service_set |= sliced.service_lines[f][i]; } } fail_on_test(sliced.io_size < sizeof(struct v4l2_sliced_vbi_data) * cnt); fail_on_test(sliced.service_set != service_set); break; case V4L2_BUF_TYPE_VIDEO_OVERLAY: case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: fail_on_test(win.field == V4L2_FIELD_ANY); fail_on_test(win.clipcount && !(node->fbuf_caps & V4L2_FBUF_CAP_LIST_CLIPPING)); for (struct v4l2_clip *clip = win.clips; clip; win.clipcount--) { fail_on_test(clip == NULL); clip = clip->next; } fail_on_test(win.clipcount); fail_on_test(win.chromakey && !(node->fbuf_caps & (V4L2_FBUF_CAP_CHROMAKEY | V4L2_FBUF_CAP_SRC_CHROMAKEY))); if (!(node->fbuf_caps & V4L2_FBUF_CAP_BITMAP_CLIPPING)) fail_on_test(win.bitmap); fail_on_test(win.global_alpha && !(node->fbuf_caps & V4L2_FBUF_CAP_GLOBAL_ALPHA)); break; case V4L2_BUF_TYPE_PRIVATE: break; } return 0; }
int testModulatorFreq(struct node *node) { struct v4l2_frequency freq; unsigned m; int ret; for (m = 0; m < node->modulators; m++) { struct v4l2_modulator modulator; modulator.index = m; ret = doioctl(node, VIDIOC_G_MODULATOR, &modulator); if (ret) return fail("could not get modulator %d\n", m); memset(&freq, 0, sizeof(freq)); freq.tuner = m; ret = doioctl(node, VIDIOC_G_FREQUENCY, &freq); if (ret) return fail("could not get frequency for modulator %d\n", m); if (check_0(freq.reserved, sizeof(freq.reserved))) return fail("reserved was not zeroed\n"); if (freq.tuner != m) return fail("frequency modulator field changed!\n"); if (freq.frequency == 0) return fail("frequency not set\n"); if (freq.frequency < modulator.rangelow || freq.frequency > modulator.rangehigh) warn("returned modulator %d frequency out of range (%d not in [%d...%d])\n", m, freq.frequency, modulator.rangelow, modulator.rangehigh); ret = doioctl(node, VIDIOC_S_FREQUENCY, &freq); if (ret) return fail("could not set current frequency\n"); freq.frequency = modulator.rangelow; ret = doioctl(node, VIDIOC_S_FREQUENCY, &freq); if (ret) return fail("could not set rangelow frequency\n"); freq.frequency = modulator.rangehigh; ret = doioctl(node, VIDIOC_S_FREQUENCY, &freq); if (ret) return fail("could not set rangehigh frequency\n"); freq.frequency = modulator.rangelow - 1; ret = doioctl(node, VIDIOC_S_FREQUENCY, &freq); if (ret != EINVAL) return fail("set rangelow-1 frequency did not return EINVAL\n"); freq.frequency = modulator.rangehigh + 1; ret = doioctl(node, VIDIOC_S_FREQUENCY, &freq); if (ret != EINVAL) return fail("set rangehigh+1 frequency did not return EINVAL\n"); } /* There is an ambiguity in the API and G/S_FREQUENCY: you cannot specify correctly whether to the ioctl is for a tuner or a modulator. This should be corrected, but until then the tests below have to be skipped if there is a tuner of index m. */ if (node->tuners > m) return 0; freq.tuner = m; ret = doioctl(node, VIDIOC_G_FREQUENCY, &freq); if (ret != EINVAL) return fail("could get frequency for invalid modulator %d\n", m); freq.tuner = m; // Radio: 100 MHz freq.frequency = 1600000; ret = doioctl(node, VIDIOC_S_FREQUENCY, &freq); if (ret != EINVAL) return fail("could set frequency for invalid modulator %d\n", m); return node->modulators ? 0 : -ENOSYS; }
static int testEnumFrameSizes(struct node *node, __u32 pixfmt) { struct v4l2_frmsizeenum frmsize; struct v4l2_frmsize_stepwise *sw = &frmsize.stepwise; bool found_stepwise = false; unsigned f = 0; int ret; for (;;) { memset(&frmsize, 0xff, sizeof(frmsize)); frmsize.index = f; frmsize.pixel_format = pixfmt; ret = doioctl(node, VIDIOC_ENUM_FRAMESIZES, &frmsize); 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 framesize %d\n", ret, f); ret = check_0(frmsize.reserved, sizeof(frmsize.reserved)); if (ret) return fail("frmsize.reserved not zeroed\n"); if (frmsize.pixel_format != pixfmt || frmsize.index != f) return fail("frmsize.pixel_format or index changed\n"); switch (frmsize.type) { case V4L2_FRMSIZE_TYPE_DISCRETE: if (frmsize.discrete.width == 0 || frmsize.discrete.height == 0) return fail("invalid width/height for discrete framesize\n"); if (found_stepwise) return fail("mixing discrete and stepwise is not allowed\n"); ret = testEnumFrameIntervals(node, pixfmt, frmsize.discrete.width, frmsize.discrete.height, true); if (ret && ret != ENOTTY) return ret; ret = testEnumFrameIntervals(node, pixfmt, frmsize.discrete.width + 1, frmsize.discrete.height, false); if (ret && ret != ENOTTY) return ret; break; case V4L2_FRMSIZE_TYPE_CONTINUOUS: if (frmsize.stepwise.step_width != 1 || frmsize.stepwise.step_height != 1) return fail("invalid step_width/height for continuous framesize\n"); /* fallthrough */ case V4L2_FRMSIZE_TYPE_STEPWISE: if (frmsize.index) return fail("index must be 0 for stepwise/continuous framesizes\n"); found_stepwise = true; if (!sw->min_width || !sw->min_height || !sw->step_width || !sw->step_height) return fail("0 for min_width/height or step_width/height\n"); if (sw->min_width > sw->max_width || sw->min_height > sw->max_height) return fail("min_width/height > max_width/height\n"); if (sw->step_width > sw->max_width - sw->min_width || sw->step_height > sw->max_height - sw->min_height) return fail("step > max - min for width or height\n"); ret = testEnumFrameIntervals(node, pixfmt, sw->min_width, sw->min_height, true); if (ret && ret != ENOTTY) return ret; ret = testEnumFrameIntervals(node, pixfmt, sw->max_width, sw->max_height, true); if (ret && ret != ENOTTY) return ret; ret = testEnumFrameIntervals(node, pixfmt, sw->min_width - 1, sw->min_height, false); if (ret && ret != ENOTTY) return ret; ret = testEnumFrameIntervals(node, pixfmt, sw->max_width, sw->max_height + 1, false); if (ret && ret != ENOTTY) return ret; break; default: return fail("frmsize.type is invalid\n"); } f++; } info("found %d framesizes for pixel format %08x\n", f, pixfmt); return 0; }
int testTunerFreq(struct node *node) { struct v4l2_frequency freq; enum v4l2_tuner_type last_type = V4L2_TUNER_ANALOG_TV; unsigned t; int ret; for (t = 0; t < node->tuners; t++) { struct v4l2_tuner tuner; tuner.index = t; ret = doioctl(node, VIDIOC_G_TUNER, &tuner); if (ret) return fail("could not get tuner %d\n", t); last_type = tuner.type; memset(&freq, 0, sizeof(freq)); freq.tuner = t; ret = doioctl(node, VIDIOC_G_FREQUENCY, &freq); if (ret) return fail("could not get frequency for tuner %d\n", t); if (check_0(freq.reserved, sizeof(freq.reserved))) return fail("reserved was not zeroed\n"); if (freq.type != V4L2_TUNER_RADIO && freq.type != V4L2_TUNER_ANALOG_TV) return fail("returned invalid tuner type %d\n", freq.type); if (freq.type == V4L2_TUNER_RADIO && !(node->caps & V4L2_CAP_RADIO)) return fail("radio tuner found but no radio capability set\n"); if (freq.type != tuner.type) return fail("frequency tuner type and tuner type mismatch\n"); if (freq.tuner != t) return fail("frequency tuner field changed!\n"); if (freq.frequency == 0) return fail("frequency not set\n"); if (freq.frequency < tuner.rangelow || freq.frequency > tuner.rangehigh) warn("returned tuner %d frequency out of range (%d not in [%d...%d])\n", t, freq.frequency, tuner.rangelow, tuner.rangehigh); freq.type = (enum v4l2_tuner_type)0; ret = doioctl(node, VIDIOC_S_FREQUENCY, &freq); if (ret != EINVAL) return fail("did not return EINVAL when passed tuner type 0\n"); freq.type = tuner.type; ret = doioctl(node, VIDIOC_S_FREQUENCY, &freq); if (ret) return fail("could not set current frequency\n"); freq.frequency = tuner.rangelow; ret = doioctl(node, VIDIOC_S_FREQUENCY, &freq); if (ret) return fail("could not set rangelow frequency\n"); freq.frequency = tuner.rangehigh; ret = doioctl(node, VIDIOC_S_FREQUENCY, &freq); if (ret) return fail("could not set rangehigh frequency\n"); freq.frequency = tuner.rangelow - 1; ret = doioctl(node, VIDIOC_S_FREQUENCY, &freq); if (ret != EINVAL) return fail("set rangelow-1 frequency did not return EINVAL\n"); freq.frequency = tuner.rangehigh + 1; ret = doioctl(node, VIDIOC_S_FREQUENCY, &freq); if (ret != EINVAL) return fail("set rangehigh+1 frequency did not return EINVAL\n"); } /* There is an ambiguity in the API and G/S_FREQUENCY: you cannot specify correctly whether to the ioctl is for a tuner or a modulator. This should be corrected, but until then the tests below have to be skipped if there is a modulator of index t. */ if (node->modulators > t) return 0; freq.tuner = t; ret = doioctl(node, VIDIOC_G_FREQUENCY, &freq); if (ret != EINVAL) return fail("could get frequency for invalid tuner %d\n", t); freq.tuner = t; freq.type = last_type; // TV: 400 Mhz Radio: 100 MHz freq.frequency = last_type == V4L2_TUNER_ANALOG_TV ? 6400 : 1600000; ret = doioctl(node, VIDIOC_S_FREQUENCY, &freq); if (ret != EINVAL) return fail("could set frequency for invalid tuner %d\n", t); return node->tuners ? 0 : -ENOSYS; }
static int testFormatsType(struct node *node, enum v4l2_buf_type type) { pixfmt_set &set = node->buftype_pixfmts[type]; pixfmt_set *set_splane; struct v4l2_format fmt; struct v4l2_pix_format &pix = fmt.fmt.pix; struct v4l2_pix_format_mplane &pix_mp = fmt.fmt.pix_mp; struct v4l2_window &win = fmt.fmt.win; struct v4l2_vbi_format &vbi = fmt.fmt.vbi; struct v4l2_sliced_vbi_format &sliced = fmt.fmt.sliced; __u32 service_set = 0; unsigned cnt = 0; int ret; memset(&fmt, 0xff, sizeof(fmt)); fmt.type = type; ret = doioctl(node, VIDIOC_G_FMT, &fmt); if (ret == EINVAL) return -ENOSYS; if (ret) return fail("expected EINVAL, but got %d when getting format for buftype %d\n", ret, type); fail_on_test(fmt.type != type); switch (type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_OUTPUT: fail_on_test(!pix.width || !pix.height); if (set.find(pix.pixelformat) == set.end()) return fail("unknown pixelformat %08x for buftype %d\n", pix.pixelformat, type); fail_on_test(pix.bytesperline && pix.bytesperline < pix.width); fail_on_test(!pix.sizeimage); fail_on_test(!pix.colorspace); if (pix.priv) warn("priv is non-zero!\n"); break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: fail_on_test(!pix_mp.width || !pix_mp.height); set_splane = &node->buftype_pixfmts[type - 8]; if (set.find(pix_mp.pixelformat) == set.end() && set_splane->find(pix_mp.pixelformat) == set_splane->end()) return fail("unknown pixelformat %08x for buftype %d\n", pix_mp.pixelformat, type); fail_on_test(!pix_mp.colorspace); ret = check_0(pix_mp.reserved, sizeof(pix_mp.reserved)); if (ret) return fail("pix_mp.reserved not zeroed\n"); fail_on_test(pix_mp.num_planes == 0 || pix_mp.num_planes >= VIDEO_MAX_PLANES); for (int i = 0; i < pix_mp.num_planes; i++) { struct v4l2_plane_pix_format &pfmt = pix_mp.plane_fmt[i]; ret = check_0(pfmt.reserved, sizeof(pfmt.reserved)); if (ret) return fail("pix_mp.plane_fmt[%d].reserved not zeroed\n", i); fail_on_test(!pfmt.sizeimage); fail_on_test(pfmt.bytesperline && pfmt.bytesperline < pix_mp.width); } break; case V4L2_BUF_TYPE_VBI_CAPTURE: case V4L2_BUF_TYPE_VBI_OUTPUT: fail_on_test(!vbi.sampling_rate); fail_on_test(!vbi.samples_per_line); fail_on_test(vbi.sample_format != V4L2_PIX_FMT_GREY); fail_on_test(vbi.offset > vbi.samples_per_line); ret = check_0(vbi.reserved, sizeof(vbi.reserved)); if (ret) return fail("vbi.reserved not zeroed\n"); fail_on_test(!vbi.count[0] || !vbi.count[1]); fail_on_test(vbi.flags & ~(V4L2_VBI_UNSYNC | V4L2_VBI_INTERLACED)); if (vbi.flags & V4L2_VBI_INTERLACED) fail_on_test(vbi.count[0] != vbi.count[1]); break; case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: ret = check_0(sliced.reserved, sizeof(sliced.reserved)); if (ret) return fail("sliced.reserved not zeroed\n"); fail_on_test(sliced.service_lines[0][0] || sliced.service_lines[1][0]); for (int f = 0; f < 2; f++) { for (int i = 0; i < 24; i++) { if (sliced.service_lines[f][i]) cnt++; service_set |= sliced.service_lines[f][i]; } } fail_on_test(sliced.io_size < sizeof(struct v4l2_sliced_vbi_data) * cnt); fail_on_test(sliced.service_set != service_set); break; case V4L2_BUF_TYPE_VIDEO_OVERLAY: case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: fail_on_test(win.field != V4L2_FIELD_ANY && win.field != V4L2_FIELD_TOP && win.field != V4L2_FIELD_BOTTOM && win.field != V4L2_FIELD_INTERLACED); fail_on_test(win.clipcount && !(node->fbuf_caps & V4L2_FBUF_CAP_LIST_CLIPPING)); for (struct v4l2_clip *clip = win.clips; clip; win.clipcount--) { fail_on_test(clip == NULL); clip = clip->next; } fail_on_test(win.clipcount); fail_on_test(win.chromakey && !(node->fbuf_caps & (V4L2_FBUF_CAP_CHROMAKEY | V4L2_FBUF_CAP_SRC_CHROMAKEY))); if (!(node->fbuf_caps & V4L2_FBUF_CAP_BITMAP_CLIPPING)) fail_on_test(win.bitmap); fail_on_test(win.global_alpha && !(node->fbuf_caps & V4L2_FBUF_CAP_GLOBAL_ALPHA)); break; case V4L2_BUF_TYPE_PRIVATE: break; } return 0; }
int testTunerFreq(struct node *node) { struct v4l2_frequency freq; enum v4l2_tuner_type last_type = V4L2_TUNER_ANALOG_TV; unsigned t; int ret; for (t = 0; t < node->tuners; t++) { struct v4l2_tuner tuner; tuner.index = t; ret = doioctl(node, VIDIOC_G_TUNER, &tuner); if (ret) return fail("could not get tuner %d\n", t); last_type = (enum v4l2_tuner_type)tuner.type; memset(&freq, 0, sizeof(freq)); freq.tuner = t; ret = doioctl(node, VIDIOC_G_FREQUENCY, &freq); if (ret) return fail("could not get frequency for tuner %d\n", t); if (check_0(freq.reserved, sizeof(freq.reserved))) return fail("reserved was not zeroed\n"); if (freq.type != V4L2_TUNER_RADIO && freq.type != V4L2_TUNER_ANALOG_TV && freq.type != V4L2_TUNER_ADC && freq.type != V4L2_TUNER_RF) return fail("returned invalid tuner type %d\n", freq.type); if (freq.type == V4L2_TUNER_RADIO && !(node->caps & V4L2_CAP_RADIO)) return fail("radio tuner found but no radio capability set\n"); if ((freq.type == V4L2_TUNER_ADC || freq.type == V4L2_TUNER_RF) && !(node->caps & V4L2_CAP_SDR_CAPTURE)) return fail("sdr tuner found but no sdr capture capability set\n"); if (freq.type != tuner.type) return fail("frequency tuner type and tuner type mismatch\n"); if (freq.tuner != t) return fail("frequency tuner field changed!\n"); if (freq.frequency == 0) return fail("frequency not set\n"); if (freq.frequency < tuner.rangelow || freq.frequency > tuner.rangehigh) warn("returned tuner %d frequency out of range (%d not in [%d...%d])\n", t, freq.frequency, tuner.rangelow, tuner.rangehigh); freq.type = (enum v4l2_tuner_type)0; ret = doioctl(node, VIDIOC_S_FREQUENCY, &freq); if (ret != EINVAL) return fail("did not return EINVAL when passed tuner type 0\n"); freq.type = tuner.type; ret = doioctl(node, VIDIOC_S_FREQUENCY, &freq); if (ret) return fail("could not set current frequency\n"); freq.frequency = tuner.rangelow; ret = doioctl(node, VIDIOC_S_FREQUENCY, &freq); if (ret) return fail("could not set rangelow frequency\n"); freq.frequency = tuner.rangehigh; ret = doioctl(node, VIDIOC_S_FREQUENCY, &freq); if (ret) return fail("could not set rangehigh frequency\n"); freq.frequency = tuner.rangelow - 1; ret = doioctl(node, VIDIOC_S_FREQUENCY, &freq); if (ret) return fail("could not set rangelow-1 frequency\n"); ret = doioctl(node, VIDIOC_G_FREQUENCY, &freq); if (ret || freq.frequency != tuner.rangelow) return fail("frequency rangelow-1 wasn't mapped to rangelow\n"); freq.frequency = tuner.rangehigh + 1; ret = doioctl(node, VIDIOC_S_FREQUENCY, &freq); if (ret) return fail("could not set rangehigh+1 frequency\n"); ret = doioctl(node, VIDIOC_G_FREQUENCY, &freq); if (ret || freq.frequency != tuner.rangehigh) return fail("frequency rangehigh+1 wasn't mapped to rangehigh\n"); } /* If this is a modulator device, then skip the remaining tests */ if (node->caps & V4L2_CAP_MODULATOR) return 0; freq.tuner = t; ret = doioctl(node, VIDIOC_G_FREQUENCY, &freq); if (ret != EINVAL && ret != ENOTTY) return fail("could get frequency for invalid tuner %d\n", t); freq.tuner = t; freq.type = last_type; // TV: 400 Mhz Radio: 100 MHz freq.frequency = last_type == V4L2_TUNER_ANALOG_TV ? 6400 : 1600000; ret = doioctl(node, VIDIOC_S_FREQUENCY, &freq); if (ret != EINVAL && ret != ENOTTY) return fail("could set frequency for invalid tuner %d\n", t); return node->tuners ? 0 : ENOTTY; }
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 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; }
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; }