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;
}
예제 #4
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);
}
예제 #9
0
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;
}
예제 #10
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);
}
예제 #13
0
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;
}