Commit 745656a3 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman
Browse files

Merge tag 'media-uvc-next-20230115' of...

Merge tag 'media-uvc-next-20230115' of git://git.kernel.org/pub/scm/linux/kernel/git/pinchartl/linux

 into usb-next

Merge in this tag from the media tree so that future USB uvc patches
will apply properly.

Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>

* tag 'media-uvc-next-20230115' of git://git.kernel.org/pub/scm/linux/kernel/git/pinchartl/linux: (27 commits)
  media: uvcvideo: Silence memcpy() run-time false positive warnings
  media: uvcvideo: Quirk for autosuspend in Logitech B910 and C910
  media: uvcvideo: Fix race condition with usb_kill_urb
  media: uvcvideo: Use standard names for menus
  media: uvcvideo: Fix power line control for Lenovo Integrated Camera
  media: uvcvideo: Refactor power_line_frequency_controls_limited
  media: uvcvideo: Refactor uvc_ctrl_mappings_uvcXX
  media: uvcvideo: Implement mask for V4L2_CTRL_TYPE_MENU
  media: uvcvideo: Extend documentation of uvc_video_clock_decode()
  media: uvcvideo: Limit power line control for Acer EasyCamera
  media: uvcvideo: Refactor __uvc_ctrl_add_mapping
  media: uvcvideo: Fix handling on Bitmask controls
  media: uvcvideo: Do not return positive errors in uvc_query_ctrl()
  media: uvcvideo: Return -EACCES for Wrong state error
  media: uvcvideo: Improve error logging in uvc_query_ctrl()
  media: uvcvideo: Check for INACTIVE in uvc_ctrl_is_accessible()
  media: uvcvideo: Factor out usb_string() calls
  media: uvcvideo: Limit power line control for Acer EasyCamera
  media: uvcvideo: Recover stalled ElGato devices
  media: uvcvideo: Remove void casting for the status endpoint
  ...
parents c52c9acc b8392129
Loading
Loading
Loading
Loading
+250 −90
Original line number Diff line number Diff line
@@ -6,6 +6,8 @@
 *          Laurent Pinchart (laurent.pinchart@ideasonboard.com)
 */

#include <asm/barrier.h>
#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
@@ -363,19 +365,45 @@ static const u32 uvc_control_classes[] = {
	V4L2_CID_USER_CLASS,
};

static const struct uvc_menu_info power_line_frequency_controls[] = {
	{ 0, "Disabled" },
	{ 1, "50 Hz" },
	{ 2, "60 Hz" },
	{ 3, "Auto" },
};
static const int exposure_auto_mapping[] = { 2, 1, 4, 8 };

static const struct uvc_menu_info exposure_auto_controls[] = {
	{ 2, "Auto Mode" },
	{ 1, "Manual Mode" },
	{ 4, "Shutter Priority Mode" },
	{ 8, "Aperture Priority Mode" },
};
/*
 * This function translates the V4L2 menu index @idx, as exposed to userspace as
 * the V4L2 control value, to the corresponding UVC control value used by the
 * device. The custom menu_mapping in the control @mapping is used when
 * available, otherwise the function assumes that the V4L2 and UVC values are
 * identical.
 *
 * For controls of type UVC_CTRL_DATA_TYPE_BITMASK, the UVC control value is
 * expressed as a bitmask and is thus guaranteed to have a single bit set.
 *
 * The function returns -EINVAL if the V4L2 menu index @idx isn't valid for the
 * control, which includes all controls whose type isn't UVC_CTRL_DATA_TYPE_ENUM
 * or UVC_CTRL_DATA_TYPE_BITMASK.
 */
static int uvc_mapping_get_menu_value(const struct uvc_control_mapping *mapping,
				      u32 idx)
{
	if (!test_bit(idx, &mapping->menu_mask))
		return -EINVAL;

	if (mapping->menu_mapping)
		return mapping->menu_mapping[idx];

	return idx;
}

static const char *
uvc_mapping_get_menu_name(const struct uvc_control_mapping *mapping, u32 idx)
{
	if (!test_bit(idx, &mapping->menu_mask))
		return NULL;

	if (mapping->menu_names)
		return mapping->menu_names[idx];

	return v4l2_ctrl_get_menu(mapping->id)[idx];
}

static s32 uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping,
	u8 query, const u8 *data)
@@ -524,8 +552,9 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
		.offset		= 0,
		.v4l2_type	= V4L2_CTRL_TYPE_MENU,
		.data_type	= UVC_CTRL_DATA_TYPE_BITMASK,
		.menu_info	= exposure_auto_controls,
		.menu_count	= ARRAY_SIZE(exposure_auto_controls),
		.menu_mapping	= exposure_auto_mapping,
		.menu_mask	= GENMASK(V4L2_EXPOSURE_APERTURE_PRIORITY,
					  V4L2_EXPOSURE_AUTO),
		.slave_ids	= { V4L2_CID_EXPOSURE_ABSOLUTE, },
	},
	{
@@ -721,8 +750,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
	},
};

static const struct uvc_control_mapping uvc_ctrl_mappings_uvc11[] = {
	{
const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited = {
	.id		= V4L2_CID_POWER_LINE_FREQUENCY,
	.entity		= UVC_GUID_UVC_PROCESSING,
	.selector	= UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
@@ -730,13 +758,11 @@ static const struct uvc_control_mapping uvc_ctrl_mappings_uvc11[] = {
	.offset		= 0,
	.v4l2_type	= V4L2_CTRL_TYPE_MENU,
	.data_type	= UVC_CTRL_DATA_TYPE_ENUM,
		.menu_info	= power_line_frequency_controls,
		.menu_count	= ARRAY_SIZE(power_line_frequency_controls) - 1,
	},
	.menu_mask	= GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_60HZ,
				  V4L2_CID_POWER_LINE_FREQUENCY_50HZ),
};

static const struct uvc_control_mapping uvc_ctrl_mappings_uvc15[] = {
	{
const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc11 = {
	.id		= V4L2_CID_POWER_LINE_FREQUENCY,
	.entity		= UVC_GUID_UVC_PROCESSING,
	.selector	= UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
@@ -744,9 +770,30 @@ static const struct uvc_control_mapping uvc_ctrl_mappings_uvc15[] = {
	.offset		= 0,
	.v4l2_type	= V4L2_CTRL_TYPE_MENU,
	.data_type	= UVC_CTRL_DATA_TYPE_ENUM,
		.menu_info	= power_line_frequency_controls,
		.menu_count	= ARRAY_SIZE(power_line_frequency_controls),
	},
	.menu_mask	= GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_60HZ,
				  V4L2_CID_POWER_LINE_FREQUENCY_DISABLED),
};

static const struct uvc_control_mapping *uvc_ctrl_mappings_uvc11[] = {
	&uvc_ctrl_power_line_mapping_uvc11,
	NULL, /* Sentinel */
};

static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc15 = {
	.id		= V4L2_CID_POWER_LINE_FREQUENCY,
	.entity		= UVC_GUID_UVC_PROCESSING,
	.selector	= UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
	.size		= 2,
	.offset		= 0,
	.v4l2_type	= V4L2_CTRL_TYPE_MENU,
	.data_type	= UVC_CTRL_DATA_TYPE_ENUM,
	.menu_mask	= GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_AUTO,
				  V4L2_CID_POWER_LINE_FREQUENCY_DISABLED),
};

static const struct uvc_control_mapping *uvc_ctrl_mappings_uvc15[] = {
	&uvc_ctrl_power_line_mapping_uvc15,
	NULL, /* Sentinel */
};

/* ------------------------------------------------------------------------
@@ -972,11 +1019,17 @@ static s32 __uvc_ctrl_get_value(struct uvc_control_mapping *mapping,
	s32 value = mapping->get(mapping, UVC_GET_CUR, data);

	if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) {
		const struct uvc_menu_info *menu = mapping->menu_info;
		unsigned int i;

		for (i = 0; i < mapping->menu_count; ++i, ++menu) {
			if (menu->value == value) {
		for (i = 0; BIT(i) <= mapping->menu_mask; ++i) {
			u32 menu_value;

			if (!test_bit(i, &mapping->menu_mask))
				continue;

			menu_value = uvc_mapping_get_menu_value(mapping, i);

			if (menu_value == value) {
				value = i;
				break;
			}
@@ -1085,11 +1138,28 @@ static int uvc_query_v4l2_class(struct uvc_video_chain *chain, u32 req_id,
	return 0;
}

/*
 * Check if control @v4l2_id can be accessed by the given control @ioctl
 * (VIDIOC_G_EXT_CTRLS, VIDIOC_TRY_EXT_CTRLS or VIDIOC_S_EXT_CTRLS).
 *
 * For set operations on slave controls, check if the master's value is set to
 * manual, either in the others controls set in the same ioctl call, or from
 * the master's current value. This catches VIDIOC_S_EXT_CTRLS calls that set
 * both the master and slave control, such as for instance setting
 * auto_exposure=1, exposure_time_absolute=251.
 */
int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
			   bool read)
			   const struct v4l2_ext_controls *ctrls,
			   unsigned long ioctl)
{
	struct uvc_control_mapping *master_map = NULL;
	struct uvc_control *master_ctrl = NULL;
	struct uvc_control_mapping *mapping;
	struct uvc_control *ctrl;
	bool read = ioctl == VIDIOC_G_EXT_CTRLS;
	s32 val;
	int ret;
	int i;

	if (__uvc_query_v4l2_class(chain, v4l2_id, 0) >= 0)
		return -EACCES;
@@ -1104,6 +1174,29 @@ int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
	if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR) && !read)
		return -EACCES;

	if (ioctl != VIDIOC_S_EXT_CTRLS || !mapping->master_id)
		return 0;

	/*
	 * Iterate backwards in cases where the master control is accessed
	 * multiple times in the same ioctl. We want the last value.
	 */
	for (i = ctrls->count - 1; i >= 0; i--) {
		if (ctrls->controls[i].id == mapping->master_id)
			return ctrls->controls[i].value ==
					mapping->master_manual ? 0 : -EACCES;
	}

	__uvc_find_control(ctrl->entity, mapping->master_id, &master_map,
			   &master_ctrl, 0);

	if (!master_ctrl || !(master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR))
		return 0;

	ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val);
	if (ret >= 0 && val != mapping->master_manual)
		return -EACCES;

	return 0;
}

@@ -1121,6 +1214,25 @@ static const char *uvc_map_get_name(const struct uvc_control_mapping *map)
	return "Unknown Control";
}

static u32 uvc_get_ctrl_bitmap(struct uvc_control *ctrl,
			       struct uvc_control_mapping *mapping)
{
	/*
	 * Some controls, like CT_AE_MODE_CONTROL, use GET_RES to represent
	 * the number of bits supported. Those controls do not list GET_MAX
	 * as supported.
	 */
	if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)
		return mapping->get(mapping, UVC_GET_RES,
				    uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));

	if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX)
		return mapping->get(mapping, UVC_GET_MAX,
				    uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));

	return ~0;
}

static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
	struct uvc_control *ctrl,
	struct uvc_control_mapping *mapping,
@@ -1128,7 +1240,6 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
{
	struct uvc_control_mapping *master_map = NULL;
	struct uvc_control *master_ctrl = NULL;
	const struct uvc_menu_info *menu;
	unsigned int i;

	memset(v4l2_ctrl, 0, sizeof(*v4l2_ctrl));
@@ -1169,13 +1280,19 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,

	switch (mapping->v4l2_type) {
	case V4L2_CTRL_TYPE_MENU:
		v4l2_ctrl->minimum = 0;
		v4l2_ctrl->maximum = mapping->menu_count - 1;
		v4l2_ctrl->minimum = ffs(mapping->menu_mask) - 1;
		v4l2_ctrl->maximum = fls(mapping->menu_mask) - 1;
		v4l2_ctrl->step = 1;

		menu = mapping->menu_info;
		for (i = 0; i < mapping->menu_count; ++i, ++menu) {
			if (menu->value == v4l2_ctrl->default_value) {
		for (i = 0; BIT(i) <= mapping->menu_mask; ++i) {
			u32 menu_value;

			if (!test_bit(i, &mapping->menu_mask))
				continue;

			menu_value = uvc_mapping_get_menu_value(mapping, i);

			if (menu_value == v4l2_ctrl->default_value) {
				v4l2_ctrl->default_value = i;
				break;
			}
@@ -1195,6 +1312,12 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
		v4l2_ctrl->step = 0;
		return 0;

	case V4L2_CTRL_TYPE_BITMASK:
		v4l2_ctrl->minimum = 0;
		v4l2_ctrl->maximum = uvc_get_ctrl_bitmap(ctrl, mapping);
		v4l2_ctrl->step = 0;
		return 0;

	default:
		break;
	}
@@ -1268,11 +1391,11 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
int uvc_query_v4l2_menu(struct uvc_video_chain *chain,
	struct v4l2_querymenu *query_menu)
{
	const struct uvc_menu_info *menu_info;
	struct uvc_control_mapping *mapping;
	struct uvc_control *ctrl;
	u32 index = query_menu->index;
	u32 id = query_menu->id;
	const char *name;
	int ret;

	memset(query_menu, 0, sizeof(*query_menu));
@@ -1289,16 +1412,13 @@ int uvc_query_v4l2_menu(struct uvc_video_chain *chain,
		goto done;
	}

	if (query_menu->index >= mapping->menu_count) {
	if (!test_bit(query_menu->index, &mapping->menu_mask)) {
		ret = -EINVAL;
		goto done;
	}

	menu_info = &mapping->menu_info[query_menu->index];

	if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK &&
	    (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)) {
		s32 bitmap;
	if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK) {
		int mask;

		if (!ctrl->cached) {
			ret = uvc_ctrl_populate_cache(chain, ctrl);
@@ -1306,15 +1426,25 @@ int uvc_query_v4l2_menu(struct uvc_video_chain *chain,
				goto done;
		}

		bitmap = mapping->get(mapping, UVC_GET_RES,
				      uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
		if (!(bitmap & menu_info->value)) {
		mask = uvc_mapping_get_menu_value(mapping, query_menu->index);
		if (mask < 0) {
			ret = mask;
			goto done;
		}

		if (!(uvc_get_ctrl_bitmap(ctrl, mapping) & mask)) {
			ret = -EINVAL;
			goto done;
		}
	}

	strscpy(query_menu->name, menu_info->name, sizeof(query_menu->name));
	name = uvc_mapping_get_menu_name(mapping, query_menu->index);
	if (!name) {
		ret = -EINVAL;
		goto done;
	}

	strscpy(query_menu->name, name, sizeof(query_menu->name));

done:
	mutex_unlock(&chain->ctrl_mutex);
@@ -1442,6 +1572,10 @@ static void uvc_ctrl_status_event_work(struct work_struct *work)

	uvc_ctrl_status_event(w->chain, w->ctrl, w->data);

	/* The barrier is needed to synchronize with uvc_status_stop(). */
	if (smp_load_acquire(&dev->flush_status))
		return;

	/* Resubmit the URB. */
	w->urb->interval = dev->int_ep->desc.bInterval;
	ret = usb_submit_urb(w->urb, GFP_KERNEL);
@@ -1791,31 +1925,44 @@ int uvc_ctrl_set(struct uvc_fh *handle,
		value = xctrl->value;
		break;

	case V4L2_CTRL_TYPE_BITMASK:
		if (!ctrl->cached) {
			ret = uvc_ctrl_populate_cache(chain, ctrl);
			if (ret < 0)
				return ret;
		}

		xctrl->value &= uvc_get_ctrl_bitmap(ctrl, mapping);
		value = xctrl->value;
		break;

	case V4L2_CTRL_TYPE_BOOLEAN:
		xctrl->value = clamp(xctrl->value, 0, 1);
		value = xctrl->value;
		break;

	case V4L2_CTRL_TYPE_MENU:
		if (xctrl->value < 0 || xctrl->value >= mapping->menu_count)
		if (xctrl->value < (ffs(mapping->menu_mask) - 1) ||
		    xctrl->value > (fls(mapping->menu_mask) - 1))
			return -ERANGE;
		value = mapping->menu_info[xctrl->value].value;

		if (!test_bit(xctrl->value, &mapping->menu_mask))
			return -EINVAL;

		value = uvc_mapping_get_menu_value(mapping, xctrl->value);

		/*
		 * Valid menu indices are reported by the GET_RES request for
		 * UVC controls that support it.
		 */
		if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK &&
		    (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)) {
		if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK) {
			if (!ctrl->cached) {
				ret = uvc_ctrl_populate_cache(chain, ctrl);
				if (ret < 0)
					return ret;
			}

			step = mapping->get(mapping, UVC_GET_RES,
					uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
			if (!(step & value))
			if (!(uvc_get_ctrl_bitmap(ctrl, mapping) & value))
				return -EINVAL;
		}

@@ -2218,31 +2365,42 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
	unsigned int i;

	/*
	 * Most mappings come from static kernel data and need to be duplicated.
	 * Most mappings come from static kernel data, and need to be duplicated.
	 * Mappings that come from userspace will be unnecessarily duplicated,
	 * this could be optimized.
	 */
	map = kmemdup(mapping, sizeof(*mapping), GFP_KERNEL);
	if (map == NULL)
	if (!map)
		return -ENOMEM;

	map->name = NULL;
	map->menu_names = NULL;
	map->menu_mapping = NULL;

	/* For UVCIOC_CTRL_MAP custom control */
	if (mapping->name) {
		map->name = kstrdup(mapping->name, GFP_KERNEL);
		if (!map->name) {
			kfree(map);
			return -ENOMEM;
		}
		if (!map->name)
			goto err_nomem;
	}

	INIT_LIST_HEAD(&map->ev_subs);

	size = sizeof(*mapping->menu_info) * mapping->menu_count;
	map->menu_info = kmemdup(mapping->menu_info, size, GFP_KERNEL);
	if (map->menu_info == NULL) {
		kfree(map->name);
		kfree(map);
		return -ENOMEM;
	if (mapping->menu_mapping && mapping->menu_mask) {
		size = sizeof(mapping->menu_mapping[0])
		       * fls(mapping->menu_mask);
		map->menu_mapping = kmemdup(mapping->menu_mapping, size,
					    GFP_KERNEL);
		if (!map->menu_mapping)
			goto err_nomem;
	}
	if (mapping->menu_names && mapping->menu_mask) {
		size = sizeof(mapping->menu_names[0])
		       * fls(mapping->menu_mask);
		map->menu_names = kmemdup(mapping->menu_names, size,
					  GFP_KERNEL);
		if (!map->menu_names)
			goto err_nomem;
	}

	if (map->get == NULL)
@@ -2264,6 +2422,13 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
		ctrl->info.selector);

	return 0;

err_nomem:
	kfree(map->menu_names);
	kfree(map->menu_mapping);
	kfree(map->name);
	kfree(map);
	return -ENOMEM;
}

int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
@@ -2421,8 +2586,7 @@ static void uvc_ctrl_prune_entity(struct uvc_device *dev,
static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain,
			       struct uvc_control *ctrl)
{
	const struct uvc_control_mapping *mappings;
	unsigned int num_mappings;
	const struct uvc_control_mapping **mappings;
	unsigned int i;

	/*
@@ -2489,16 +2653,11 @@ static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain,
	}

	/* Finally process version-specific mappings. */
	if (chain->dev->uvc_version < 0x0150) {
		mappings = uvc_ctrl_mappings_uvc11;
		num_mappings = ARRAY_SIZE(uvc_ctrl_mappings_uvc11);
	} else {
		mappings = uvc_ctrl_mappings_uvc15;
		num_mappings = ARRAY_SIZE(uvc_ctrl_mappings_uvc15);
	}
	mappings = chain->dev->uvc_version < 0x0150
		 ? uvc_ctrl_mappings_uvc11 : uvc_ctrl_mappings_uvc15;

	for (i = 0; i < num_mappings; ++i) {
		const struct uvc_control_mapping *mapping = &mappings[i];
	for (i = 0; mappings[i]; ++i) {
		const struct uvc_control_mapping *mapping = mappings[i];

		if (uvc_entity_match_guid(ctrl->entity, mapping->entity) &&
		    ctrl->info.selector == mapping->selector)
@@ -2591,7 +2750,8 @@ static void uvc_ctrl_cleanup_mappings(struct uvc_device *dev,

	list_for_each_entry_safe(mapping, nm, &ctrl->info.mappings, list) {
		list_del(&mapping->list);
		kfree(mapping->menu_info);
		kfree(mapping->menu_names);
		kfree(mapping->menu_mapping);
		kfree(mapping->name);
		kfree(mapping);
	}
+102 −79

File changed.

Preview size limit exceeded, changes collapsed.

+1 −1
Original line number Diff line number Diff line
@@ -37,7 +37,7 @@ static int uvc_mc_create_links(struct uvc_video_chain *chain,
			continue;

		remote = uvc_entity_by_id(chain->dev, entity->baSourceID[i]);
		if (remote == NULL)
		if (remote == NULL || remote->num_pads == 0)
			return -EINVAL;

		source = (UVC_ENTITY_TYPE(remote) == UVC_TT_STREAMING)
+82 −43
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@
 *          Laurent Pinchart (laurent.pinchart@ideasonboard.com)
 */

#include <asm/barrier.h>
#include <linux/kernel.h>
#include <linux/input.h>
#include <linux/slab.h>
@@ -18,11 +19,34 @@
 * Input device
 */
#ifdef CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV

static bool uvc_input_has_button(struct uvc_device *dev)
{
	struct uvc_streaming *stream;

	/*
	 * The device has button events if both bTriggerSupport and
	 * bTriggerUsage are one. Otherwise the camera button does not
	 * exist or is handled automatically by the camera without host
	 * driver or client application intervention.
	 */
	list_for_each_entry(stream, &dev->streams, list) {
		if (stream->header.bTriggerSupport == 1 &&
		    stream->header.bTriggerUsage == 1)
			return true;
	}

	return false;
}

static int uvc_input_init(struct uvc_device *dev)
{
	struct input_dev *input;
	int ret;

	if (!uvc_input_has_button(dev))
		return 0;

	input = input_allocate_device();
	if (input == NULL)
		return -ENOMEM;
@@ -73,38 +97,23 @@ static void uvc_input_report_key(struct uvc_device *dev, unsigned int code,
/* --------------------------------------------------------------------------
 * Status interrupt endpoint
 */
struct uvc_streaming_status {
	u8	bStatusType;
	u8	bOriginator;
	u8	bEvent;
	u8	bValue[];
} __packed;

struct uvc_control_status {
	u8	bStatusType;
	u8	bOriginator;
	u8	bEvent;
	u8	bSelector;
	u8	bAttribute;
	u8	bValue[];
} __packed;

static void uvc_event_streaming(struct uvc_device *dev,
				struct uvc_streaming_status *status, int len)
				struct uvc_status *status, int len)
{
	if (len < 3) {
	if (len <= offsetof(struct uvc_status, bEvent)) {
		uvc_dbg(dev, STATUS,
			"Invalid streaming status event received\n");
		return;
	}

	if (status->bEvent == 0) {
		if (len < 4)
		if (len <= offsetof(struct uvc_status, streaming))
			return;

		uvc_dbg(dev, STATUS, "Button (intf %u) %s len %d\n",
			status->bOriginator,
			status->bValue[0] ? "pressed" : "released", len);
		uvc_input_report_key(dev, KEY_CAMERA, status->bValue[0]);
			status->streaming.button ? "pressed" : "released", len);
		uvc_input_report_key(dev, KEY_CAMERA, status->streaming.button);
	} else {
		uvc_dbg(dev, STATUS, "Stream %u error event %02x len %d\n",
			status->bOriginator, status->bEvent, len);
@@ -131,7 +140,7 @@ static struct uvc_control *uvc_event_entity_find_ctrl(struct uvc_entity *entity,
}

static struct uvc_control *uvc_event_find_ctrl(struct uvc_device *dev,
					const struct uvc_control_status *status,
					const struct uvc_status *status,
					struct uvc_video_chain **chain)
{
	list_for_each_entry((*chain), &dev->chains, list) {
@@ -143,7 +152,7 @@ static struct uvc_control *uvc_event_find_ctrl(struct uvc_device *dev,
				continue;

			ctrl = uvc_event_entity_find_ctrl(entity,
							  status->bSelector);
						     status->control.bSelector);
			if (ctrl)
				return ctrl;
		}
@@ -153,7 +162,7 @@ static struct uvc_control *uvc_event_find_ctrl(struct uvc_device *dev,
}

static bool uvc_event_control(struct urb *urb,
			      const struct uvc_control_status *status, int len)
			      const struct uvc_status *status, int len)
{
	static const char *attrs[] = { "value", "info", "failure", "min", "max" };
	struct uvc_device *dev = urb->context;
@@ -161,24 +170,24 @@ static bool uvc_event_control(struct urb *urb,
	struct uvc_control *ctrl;

	if (len < 6 || status->bEvent != 0 ||
	    status->bAttribute >= ARRAY_SIZE(attrs)) {
	    status->control.bAttribute >= ARRAY_SIZE(attrs)) {
		uvc_dbg(dev, STATUS, "Invalid control status event received\n");
		return false;
	}

	uvc_dbg(dev, STATUS, "Control %u/%u %s change len %d\n",
		status->bOriginator, status->bSelector,
		attrs[status->bAttribute], len);
		status->bOriginator, status->control.bSelector,
		attrs[status->control.bAttribute], len);

	/* Find the control. */
	ctrl = uvc_event_find_ctrl(dev, status, &chain);
	if (!ctrl)
		return false;

	switch (status->bAttribute) {
	switch (status->control.bAttribute) {
	case UVC_CTRL_VALUE_CHANGE:
		return uvc_ctrl_status_event_async(urb, chain, ctrl,
						   status->bValue);
						   status->control.bValue);

	case UVC_CTRL_INFO_CHANGE:
	case UVC_CTRL_FAILURE_CHANGE:
@@ -214,28 +223,22 @@ static void uvc_status_complete(struct urb *urb)

	len = urb->actual_length;
	if (len > 0) {
		switch (dev->status[0] & 0x0f) {
		switch (dev->status->bStatusType & 0x0f) {
		case UVC_STATUS_TYPE_CONTROL: {
			struct uvc_control_status *status =
				(struct uvc_control_status *)dev->status;

			if (uvc_event_control(urb, status, len))
			if (uvc_event_control(urb, dev->status, len))
				/* The URB will be resubmitted in work context. */
				return;
			break;
		}

		case UVC_STATUS_TYPE_STREAMING: {
			struct uvc_streaming_status *status =
				(struct uvc_streaming_status *)dev->status;

			uvc_event_streaming(dev, status, len);
			uvc_event_streaming(dev, dev->status, len);
			break;
		}

		default:
			uvc_dbg(dev, STATUS, "Unknown status event type %u\n",
				dev->status[0]);
				dev->status->bStatusType);
			break;
		}
	}
@@ -259,12 +262,12 @@ int uvc_status_init(struct uvc_device *dev)

	uvc_input_init(dev);

	dev->status = kzalloc(UVC_MAX_STATUS_SIZE, GFP_KERNEL);
	if (dev->status == NULL)
	dev->status = kzalloc(sizeof(*dev->status), GFP_KERNEL);
	if (!dev->status)
		return -ENOMEM;

	dev->int_urb = usb_alloc_urb(0, GFP_KERNEL);
	if (dev->int_urb == NULL) {
	if (!dev->int_urb) {
		kfree(dev->status);
		return -ENOMEM;
	}
@@ -281,7 +284,7 @@ int uvc_status_init(struct uvc_device *dev)
		interval = fls(interval) - 1;

	usb_fill_int_urb(dev->int_urb, dev->udev, pipe,
		dev->status, UVC_MAX_STATUS_SIZE, uvc_status_complete,
		dev->status, sizeof(*dev->status), uvc_status_complete,
		dev, interval);

	return 0;
@@ -309,5 +312,41 @@ int uvc_status_start(struct uvc_device *dev, gfp_t flags)

void uvc_status_stop(struct uvc_device *dev)
{
	struct uvc_ctrl_work *w = &dev->async_ctrl;

	/*
	 * Prevent the asynchronous control handler from requeing the URB. The
	 * barrier is needed so the flush_status change is visible to other
	 * CPUs running the asynchronous handler before usb_kill_urb() is
	 * called below.
	 */
	smp_store_release(&dev->flush_status, true);

	/*
	 * Cancel any pending asynchronous work. If any status event was queued,
	 * process it synchronously.
	 */
	if (cancel_work_sync(&w->work))
		uvc_ctrl_status_event(w->chain, w->ctrl, w->data);

	/* Kill the urb. */
	usb_kill_urb(dev->int_urb);

	/*
	 * The URB completion handler may have queued asynchronous work. This
	 * won't resubmit the URB as flush_status is set, but it needs to be
	 * cancelled before returning or it could then race with a future
	 * uvc_status_start() call.
	 */
	if (cancel_work_sync(&w->work))
		uvc_ctrl_status_event(w->chain, w->ctrl, w->data);

	/*
	 * From this point, there are no events on the queue and the status URB
	 * is dead. No events will be queued until uvc_status_start() is called.
	 * The barrier is needed to make sure that flush_status is visible to
	 * uvc_ctrl_status_event_work() when uvc_status_start() will be called
	 * again.
	 */
	smp_store_release(&dev->flush_status, false);
}
+80 −31
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@
 *          Laurent Pinchart (laurent.pinchart@ideasonboard.com)
 */

#include <linux/bits.h>
#include <linux/compat.h>
#include <linux/kernel.h>
#include <linux/list.h>
@@ -25,14 +26,84 @@

#include "uvcvideo.h"

static int uvc_control_add_xu_mapping(struct uvc_video_chain *chain,
				      struct uvc_control_mapping *map,
				      const struct uvc_xu_control_mapping *xmap)
{
	unsigned int i;
	size_t size;
	int ret;

	/*
	 * Prevent excessive memory consumption, as well as integer
	 * overflows.
	 */
	if (xmap->menu_count == 0 ||
	    xmap->menu_count > UVC_MAX_CONTROL_MENU_ENTRIES)
		return -EINVAL;

	map->menu_names = NULL;
	map->menu_mapping = NULL;

	map->menu_mask = BIT_MASK(xmap->menu_count);

	size = xmap->menu_count * sizeof(*map->menu_mapping);
	map->menu_mapping = kzalloc(size, GFP_KERNEL);
	if (!map->menu_mapping) {
		ret = -ENOMEM;
		goto done;
	}

	for (i = 0; i < xmap->menu_count ; i++) {
		if (copy_from_user((u32 *)&map->menu_mapping[i],
				   &xmap->menu_info[i].value,
				   sizeof(map->menu_mapping[i]))) {
			ret = -EACCES;
			goto done;
		}
	}

	/*
	 * Always use the standard naming if available, otherwise copy the
	 * names supplied by userspace.
	 */
	if (!v4l2_ctrl_get_menu(map->id)) {
		size = xmap->menu_count * sizeof(map->menu_names[0]);
		map->menu_names = kzalloc(size, GFP_KERNEL);
		if (!map->menu_names) {
			ret = -ENOMEM;
			goto done;
		}

		for (i = 0; i < xmap->menu_count ; i++) {
			/* sizeof(names[i]) - 1: to take care of \0 */
			if (copy_from_user((char *)map->menu_names[i],
					   xmap->menu_info[i].name,
					   sizeof(map->menu_names[i]) - 1)) {
				ret = -EACCES;
				goto done;
			}
		}
	}

	ret = uvc_ctrl_add_mapping(chain, map);

done:
	kfree(map->menu_names);
	map->menu_names = NULL;
	kfree(map->menu_mapping);
	map->menu_mapping = NULL;

	return ret;
}

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

	map = kzalloc(sizeof(*map), GFP_KERNEL);
@@ -60,39 +131,20 @@ static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain,
	case V4L2_CTRL_TYPE_INTEGER:
	case V4L2_CTRL_TYPE_BOOLEAN:
	case V4L2_CTRL_TYPE_BUTTON:
		ret = uvc_ctrl_add_mapping(chain, map);
		break;

	case V4L2_CTRL_TYPE_MENU:
		/*
		 * Prevent excessive memory consumption, as well as integer
		 * overflows.
		 */
		if (xmap->menu_count == 0 ||
		    xmap->menu_count > UVC_MAX_CONTROL_MENU_ENTRIES) {
			ret = -EINVAL;
			goto free_map;
		}

		size = xmap->menu_count * sizeof(*map->menu_info);
		map->menu_info = memdup_user(xmap->menu_info, size);
		if (IS_ERR(map->menu_info)) {
			ret = PTR_ERR(map->menu_info);
			goto free_map;
		}

		map->menu_count = xmap->menu_count;
		ret = uvc_control_add_xu_mapping(chain, map, xmap);
		break;

	default:
		uvc_dbg(chain->dev, CONTROL,
			"Unsupported V4L2 control type %u\n", xmap->v4l2_type);
		ret = -ENOTTY;
		goto free_map;
		break;
	}

	ret = uvc_ctrl_add_mapping(chain, map);

	kfree(map->menu_info);
free_map:
	kfree(map);

@@ -660,8 +712,6 @@ static int uvc_ioctl_enum_fmt(struct uvc_streaming *stream,
	fmt->flags = 0;
	if (format->flags & UVC_FMT_FLAG_COMPRESSED)
		fmt->flags |= V4L2_FMT_FLAG_COMPRESSED;
	strscpy(fmt->description, format->name, sizeof(fmt->description));
	fmt->description[sizeof(fmt->description) - 1] = 0;
	fmt->pixelformat = format->fcc;
	return 0;
}
@@ -1020,8 +1070,7 @@ static int uvc_ctrl_check_access(struct uvc_video_chain *chain,
	int ret = 0;

	for (i = 0; i < ctrls->count; ++ctrl, ++i) {
		ret = uvc_ctrl_is_accessible(chain, ctrl->id,
					    ioctl == VIDIOC_G_EXT_CTRLS);
		ret = uvc_ctrl_is_accessible(chain, ctrl->id, ctrls, ioctl);
		if (ret)
			break;
	}
@@ -1316,7 +1365,7 @@ static long uvc_ioctl_default(struct file *file, void *fh, bool valid_prio,
	switch (cmd) {
	/* Dynamic controls. */
	case UVCIOC_CTRL_MAP:
		return uvc_ioctl_ctrl_map(chain, arg);
		return uvc_ioctl_xu_ctrl_map(chain, arg);

	case UVCIOC_CTRL_QUERY:
		return uvc_xu_ctrl_query(chain, arg);
@@ -1429,7 +1478,7 @@ static long uvc_v4l2_compat_ioctl32(struct file *file,
		ret = uvc_v4l2_get_xu_mapping(&karg.xmap, up);
		if (ret)
			return ret;
		ret = uvc_ioctl_ctrl_map(handle->chain, &karg.xmap);
		ret = uvc_ioctl_xu_ctrl_map(handle->chain, &karg.xmap);
		if (ret)
			return ret;
		ret = uvc_v4l2_put_xu_mapping(&karg.xmap, up);
Loading