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

Commit 561474c2 authored by Laurent Pinchart's avatar Laurent Pinchart Committed by Mauro Carvalho Chehab
Browse files

V4L/DVB: uvcvideo: Support menu controls in the control mapping API



The UVCIOC_CTRL_MAP ioctl doesn't support menu entries for menu
controls. As the uvc_xu_control_mapping structure has no reserved
fields, this can't be fixed while keeping ABI compatibility.

Modify the UVCIOC_CTRL_MAP ioctl to add menu entries support, and define
UVCIOC_CTRL_MAP_OLD that supports the old ABI without any ability to add
menu controls.

Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 3653639e
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
@@ -1606,6 +1606,28 @@ void uvc_ctrl_cleanup_device(struct uvc_device *dev)
	}
}

void uvc_ctrl_cleanup(void)
{
	struct uvc_control_info *info;
	struct uvc_control_info *ni;
	struct uvc_control_mapping *mapping;
	struct uvc_control_mapping *nm;

	list_for_each_entry_safe(info, ni, &uvc_driver.controls, list) {
		if (!(info->flags & UVC_CONTROL_EXTENSION))
			continue;

		list_for_each_entry_safe(mapping, nm, &info->mappings, list) {
			list_del(&mapping->list);
			kfree(mapping->menu_info);
			kfree(mapping);
		}

		list_del(&info->list);
		kfree(info);
	}
}

void uvc_ctrl_init(void)
{
	struct uvc_control_info *ctrl = uvc_ctrls;
+1 −0
Original line number Diff line number Diff line
@@ -2260,6 +2260,7 @@ static int __init uvc_init(void)
static void __exit uvc_cleanup(void)
{
	usb_deregister(&uvc_driver.driver);
	uvc_ctrl_cleanup();
}

module_init(uvc_init);
+69 −23
Original line number Diff line number Diff line
@@ -28,6 +28,71 @@

#include "uvcvideo.h"

/* ------------------------------------------------------------------------
 * UVC ioctls
 */
static int uvc_ioctl_ctrl_map(struct uvc_xu_control_mapping *xmap, int old)
{
	struct uvc_control_mapping *map;
	unsigned int size;
	int ret;

	map = kzalloc(sizeof *map, GFP_KERNEL);
	if (map == NULL)
		return -ENOMEM;

	map->id = xmap->id;
	memcpy(map->name, xmap->name, sizeof map->name);
	memcpy(map->entity, xmap->entity, sizeof map->entity);
	map->selector = xmap->selector;
	map->size = xmap->size;
	map->offset = xmap->offset;
	map->v4l2_type = xmap->v4l2_type;
	map->data_type = xmap->data_type;

	switch (xmap->v4l2_type) {
	case V4L2_CTRL_TYPE_INTEGER:
	case V4L2_CTRL_TYPE_BOOLEAN:
	case V4L2_CTRL_TYPE_BUTTON:
		break;

	case V4L2_CTRL_TYPE_MENU:
		if (old) {
			ret = -EINVAL;
			goto done;
		}

		size = xmap->menu_count * sizeof(*map->menu_info);
		map->menu_info = kmalloc(size, GFP_KERNEL);
		if (map->menu_info == NULL) {
			ret = -ENOMEM;
			goto done;
		}

		if (copy_from_user(map->menu_info, xmap->menu_info, size)) {
			ret = -EFAULT;
			goto done;
		}

		map->menu_count = xmap->menu_count;
		break;

	default:
		ret = -EINVAL;
		goto done;
	}

	ret = uvc_ctrl_add_mapping(map);

done:
	if (ret < 0) {
		kfree(map->menu_info);
		kfree(map);
	}

	return ret;
}

/* ------------------------------------------------------------------------
 * V4L2 interface
 */
@@ -974,7 +1039,8 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
		info->flags = xinfo->flags;

		info->flags |= UVC_CONTROL_GET_MIN | UVC_CONTROL_GET_MAX |
				UVC_CONTROL_GET_RES | UVC_CONTROL_GET_DEF;
			       UVC_CONTROL_GET_RES | UVC_CONTROL_GET_DEF |
			       UVC_CONTROL_EXTENSION;

		ret = uvc_ctrl_add_info(info);
		if (ret < 0)
@@ -982,32 +1048,12 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
		break;
	}

	case UVCIOC_CTRL_MAP_OLD:
	case UVCIOC_CTRL_MAP:
	{
		struct uvc_xu_control_mapping *xmap = arg;
		struct uvc_control_mapping *map;

		if (!capable(CAP_SYS_ADMIN))
			return -EPERM;

		map = kzalloc(sizeof *map, GFP_KERNEL);
		if (map == NULL)
			return -ENOMEM;

		map->id = xmap->id;
		memcpy(map->name, xmap->name, sizeof map->name);
		memcpy(map->entity, xmap->entity, sizeof map->entity);
		map->selector = xmap->selector;
		map->size = xmap->size;
		map->offset = xmap->offset;
		map->v4l2_type = xmap->v4l2_type;
		map->data_type = xmap->data_type;

		ret = uvc_ctrl_add_mapping(map);
		if (ret < 0)
			kfree(map);
		break;
	}
		return uvc_ioctl_ctrl_map(arg, cmd == UVCIOC_CTRL_MAP_OLD);

	case UVCIOC_CTRL_GET:
		return uvc_xu_ctrl_query(chain, arg, 0);
+18 −5
Original line number Diff line number Diff line
@@ -27,6 +27,8 @@
#define UVC_CONTROL_RESTORE	(1 << 6)
/* Control can be updated by the camera. */
#define UVC_CONTROL_AUTO_UPDATE	(1 << 7)
/* Control is an extension unit control. */
#define UVC_CONTROL_EXTENSION	(1 << 8)

#define UVC_CONTROL_GET_RANGE	(UVC_CONTROL_GET_CUR | UVC_CONTROL_GET_MIN | \
				 UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES | \
@@ -40,6 +42,15 @@ struct uvc_xu_control_info {
	__u32 flags;
};

struct uvc_menu_info {
	__u32 value;
	__u8 name[32];
};

struct uvc_xu_control_mapping_old {
	__u8 reserved[64];
};

struct uvc_xu_control_mapping {
	__u32 id;
	__u8 name[32];
@@ -50,6 +61,11 @@ struct uvc_xu_control_mapping {
	__u8 offset;
	enum v4l2_ctrl_type v4l2_type;
	__u32 data_type;

	struct uvc_menu_info __user *menu_info;
	__u32 menu_count;

	__u32 reserved[4];
};

struct uvc_xu_control {
@@ -60,6 +76,7 @@ struct uvc_xu_control {
};

#define UVCIOC_CTRL_ADD		_IOW('U', 1, struct uvc_xu_control_info)
#define UVCIOC_CTRL_MAP_OLD	_IOWR('U', 2, struct uvc_xu_control_mapping_old)
#define UVCIOC_CTRL_MAP		_IOWR('U', 2, struct uvc_xu_control_mapping)
#define UVCIOC_CTRL_GET		_IOWR('U', 3, struct uvc_xu_control)
#define UVCIOC_CTRL_SET		_IOW('U', 4, struct uvc_xu_control)
@@ -198,11 +215,6 @@ struct uvc_streaming_control {
	__u8  bMaxVersion;
};

struct uvc_menu_info {
	__u32 value;
	__u8 name[32];
};

struct uvc_control_info {
	struct list_head list;
	struct list_head mappings;
@@ -625,6 +637,7 @@ extern int uvc_ctrl_init_device(struct uvc_device *dev);
extern void uvc_ctrl_cleanup_device(struct uvc_device *dev);
extern int uvc_ctrl_resume_device(struct uvc_device *dev);
extern void uvc_ctrl_init(void);
extern void uvc_ctrl_cleanup(void);

extern int uvc_ctrl_begin(struct uvc_video_chain *chain);
extern int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback);