Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 86748f35 authored by Hans Verkuil's avatar Hans Verkuil Committed by Mauro Carvalho Chehab
Browse files

[media] v4l2-ioctl.c: v4l2-ioctl: add debug and callback/offset functionality



Add the necessary plumbing to make it possible to replace the switch by a
table driven implementation.

The ioctls ops can either be called directly, or by calling a small function
that does some additional work if needed.

Signed-off-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 27cd2aba
Loading
Loading
Loading
Loading
+78 −13
Original line number Diff line number Diff line
@@ -396,12 +396,22 @@ struct v4l2_ioctl_info {
	unsigned int ioctl;
	u32 flags;
	const char * const name;
	union {
		u32 offset;
		int (*func)(const struct v4l2_ioctl_ops *ops,
				struct file *file, void *fh, void *p);
	};
	void (*debug)(const void *arg, bool write_only);
};

/* This control needs a priority check */
#define INFO_FL_PRIO	(1 << 0)
/* This control can be valid if the filehandle passes a control handler. */
#define INFO_FL_CTRL	(1 << 1)
/* This is a standard ioctl, no need for special code */
#define INFO_FL_STD	(1 << 2)
/* This is ioctl has its own function */
#define INFO_FL_FUNC	(1 << 3)
/* Zero struct from after the field to the end */
#define INFO_FL_CLEAR(v4l2_struct, field)			\
	((offsetof(struct v4l2_struct, field) +			\
@@ -414,6 +424,24 @@ struct v4l2_ioctl_info {
	.name = #_ioctl,					\
}

#define IOCTL_INFO_STD(_ioctl, _vidioc, _debug, _flags)			\
	[_IOC_NR(_ioctl)] = {						\
		.ioctl = _ioctl,					\
		.flags = _flags | INFO_FL_STD,				\
		.name = #_ioctl,					\
		.offset = offsetof(struct v4l2_ioctl_ops, _vidioc),	\
		.debug = _debug,					\
	}

#define IOCTL_INFO_FNC(_ioctl, _func, _debug, _flags)			\
	[_IOC_NR(_ioctl)] = {						\
		.ioctl = _ioctl,					\
		.flags = _flags | INFO_FL_FUNC,				\
		.name = #_ioctl,					\
		.func = _func,						\
		.debug = _debug,					\
	}

static struct v4l2_ioctl_info v4l2_ioctls[] = {
	IOCTL_INFO(VIDIOC_QUERYCAP, 0),
	IOCTL_INFO(VIDIOC_ENUM_FMT, INFO_FL_CLEAR(v4l2_fmtdesc, type)),
@@ -512,7 +540,7 @@ bool v4l2_is_known_ioctl(unsigned int cmd)
   external ioctl messages as well as internal V4L ioctl */
void v4l_printk_ioctl(unsigned int cmd)
{
	char *dir, *type;
	const char *dir, *type;

	switch (_IOC_TYPE(cmd)) {
	case 'd':
@@ -523,10 +551,11 @@ void v4l_printk_ioctl(unsigned int cmd)
			type = "v4l2";
			break;
		}
		printk("%s", v4l2_ioctls[_IOC_NR(cmd)].name);
		pr_cont("%s", v4l2_ioctls[_IOC_NR(cmd)].name);
		return;
	default:
		type = "unknown";
		break;
	}

	switch (_IOC_DIR(cmd)) {
@@ -536,7 +565,7 @@ void v4l_printk_ioctl(unsigned int cmd)
	case _IOC_READ | _IOC_WRITE: dir = "rw"; break;
	default:                     dir = "*ERR*"; break;
	}
	printk("%s ioctl '%c', dir=%s, #%d (0x%08x)",
	pr_cont("%s ioctl '%c', dir=%s, #%d (0x%08x)",
		type, _IOC_TYPE(cmd), dir, _IOC_NR(cmd), cmd);
}
EXPORT_SYMBOL(v4l_printk_ioctl);
@@ -546,6 +575,9 @@ static long __video_do_ioctl(struct file *file,
{
	struct video_device *vfd = video_devdata(file);
	const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
	bool write_only = false;
	struct v4l2_ioctl_info default_info;
	const struct v4l2_ioctl_info *info;
	void *fh = file->private_data;
	struct v4l2_fh *vfh = NULL;
	int use_fh_prio = 0;
@@ -563,23 +595,40 @@ static long __video_do_ioctl(struct file *file,
	}

	if (v4l2_is_known_ioctl(cmd)) {
		struct v4l2_ioctl_info *info = &v4l2_ioctls[_IOC_NR(cmd)];
		info = &v4l2_ioctls[_IOC_NR(cmd)];

	        if (!test_bit(_IOC_NR(cmd), vfd->valid_ioctls) &&
		    !((info->flags & INFO_FL_CTRL) && vfh && vfh->ctrl_handler))
			return -ENOTTY;
			goto done;

		if (use_fh_prio && (info->flags & INFO_FL_PRIO)) {
			ret = v4l2_prio_check(vfd->prio, vfh->prio);
			if (ret)
				return ret;
				goto done;
		}
	} else {
		default_info.ioctl = cmd;
		default_info.flags = 0;
		default_info.debug = NULL;
		info = &default_info;
	}

	if ((vfd->debug & V4L2_DEBUG_IOCTL) &&
				!(vfd->debug & V4L2_DEBUG_IOCTL_ARG)) {
	write_only = _IOC_DIR(cmd) == _IOC_WRITE;
	if (info->debug && write_only && vfd->debug > V4L2_DEBUG_IOCTL) {
		v4l_print_ioctl(vfd->name, cmd);
		printk(KERN_CONT "\n");
		pr_cont(": ");
		info->debug(arg, write_only);
	}
	if (info->flags & INFO_FL_STD) {
		typedef int (*vidioc_op)(struct file *file, void *fh, void *p);
		const void *p = vfd->ioctl_ops;
		const vidioc_op *vidioc = p + info->offset;

		ret = (*vidioc)(file, fh, arg);
		goto done;
	} else if (info->flags & INFO_FL_FUNC) {
		ret = info->func(ops, file, fh, arg);
		goto done;
	}

	switch (cmd) {
@@ -2101,10 +2150,26 @@ static long __video_do_ioctl(struct file *file,
		break;
	} /* switch */

	if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) {
		if (ret < 0) {
done:
	if (vfd->debug) {
		if (write_only && vfd->debug > V4L2_DEBUG_IOCTL) {
			if (ret < 0)
				printk(KERN_DEBUG "%s: error %ld\n",
					video_device_node_name(vfd), ret);
			return ret;
		}
		v4l_print_ioctl(vfd->name, cmd);
			printk(KERN_CONT " error %ld\n", ret);
		if (ret < 0)
			pr_cont(": error %ld\n", ret);
		else if (vfd->debug == V4L2_DEBUG_IOCTL)
			pr_cont("\n");
		else if (!info->debug)
			return ret;
		else if (_IOC_DIR(cmd) == _IOC_NONE)
			info->debug(arg, write_only);
		else {
			pr_cont(": ");
			info->debug(arg, write_only);
		}
	}