Skip to content

Commit

Permalink
media: uvcvideo: Use standard names for menus
Browse files Browse the repository at this point in the history
Instead of duplicating the menu info, use the one from the core.
Also, do not use extra memory for 1:1 mappings.

Suggested-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
  • Loading branch information
ribalda authored and pinchartl committed Jan 15, 2023
1 parent a7c2815 commit 716c330
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 65 deletions.
129 changes: 93 additions & 36 deletions drivers/media/usb/uvc/uvc_ctrl.c
Original file line number Diff line number Diff line change
Expand Up @@ -364,19 +364,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)
Expand Down Expand Up @@ -525,7 +551,7 @@ 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_mapping = exposure_auto_mapping,
.menu_mask = GENMASK(V4L2_EXPOSURE_APERTURE_PRIORITY,
V4L2_EXPOSURE_AUTO),
.slave_ids = { V4L2_CID_EXPOSURE_ABSOLUTE, },
Expand Down Expand Up @@ -731,7 +757,6 @@ const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited = {
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_MENU,
.data_type = UVC_CTRL_DATA_TYPE_ENUM,
.menu_info = power_line_frequency_controls,
.menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_60HZ,
V4L2_CID_POWER_LINE_FREQUENCY_50HZ),
};
Expand All @@ -744,7 +769,6 @@ const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc11 = {
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_MENU,
.data_type = UVC_CTRL_DATA_TYPE_ENUM,
.menu_info = power_line_frequency_controls,
.menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_60HZ,
V4L2_CID_POWER_LINE_FREQUENCY_DISABLED),
};
Expand All @@ -762,7 +786,6 @@ static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc15 = {
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_MENU,
.data_type = UVC_CTRL_DATA_TYPE_ENUM,
.menu_info = power_line_frequency_controls,
.menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_AUTO,
V4L2_CID_POWER_LINE_FREQUENCY_DISABLED),
};
Expand Down Expand Up @@ -995,13 +1018,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; BIT(i) <= mapping->menu_mask; ++i, ++menu) {
for (i = 0; BIT(i) <= mapping->menu_mask; ++i) {
u32 menu_value;

if (!test_bit(i, &mapping->menu_mask))
continue;
if (menu->value == value) {

menu_value = uvc_mapping_get_menu_value(mapping, i);

if (menu_value == value) {
value = i;
break;
}
Expand Down Expand Up @@ -1212,7 +1239,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));
Expand Down Expand Up @@ -1257,11 +1283,15 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
v4l2_ctrl->maximum = fls(mapping->menu_mask) - 1;
v4l2_ctrl->step = 1;

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

if (!test_bit(i, &mapping->menu_mask))
continue;
if (menu->value == v4l2_ctrl->default_value) {

menu_value = uvc_mapping_get_menu_value(mapping, i);

if (menu_value == v4l2_ctrl->default_value) {
v4l2_ctrl->default_value = i;
break;
}
Expand Down Expand Up @@ -1360,11 +1390,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));
Expand All @@ -1386,22 +1416,34 @@ int uvc_query_v4l2_menu(struct uvc_video_chain *chain,
goto done;
}

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

if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK) {
int mask;

if (!ctrl->cached) {
ret = uvc_ctrl_populate_cache(chain, ctrl);
if (ret < 0)
goto done;
}

if (!(uvc_get_ctrl_bitmap(ctrl, mapping) & 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);
Expand Down Expand Up @@ -1902,7 +1944,7 @@ int uvc_ctrl_set(struct uvc_fh *handle,
if (!test_bit(xctrl->value, &mapping->menu_mask))
return -EINVAL;

value = mapping->menu_info[xctrl->value].value;
value = uvc_mapping_get_menu_value(mapping, xctrl->value);

/*
* Valid menu indices are reported by the GET_RES request for
Expand Down Expand Up @@ -2327,7 +2369,8 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
return -ENOMEM;

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

/* For UVCIOC_CTRL_MAP custom control */
if (mapping->name) {
Expand All @@ -2338,10 +2381,22 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain,

INIT_LIST_HEAD(&map->ev_subs);

size = sizeof(*mapping->menu_info) * fls(mapping->menu_mask);
map->menu_info = kmemdup(mapping->menu_info, size, GFP_KERNEL);
if (!map->menu_info)
goto err_nomem;
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)
map->get = uvc_get_le_value;
Expand All @@ -2364,7 +2419,8 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
return 0;

err_nomem:
kfree(map->menu_info);
kfree(map->menu_names);
kfree(map->menu_mapping);
kfree(map->name);
kfree(map);
return -ENOMEM;
Expand Down Expand Up @@ -2689,7 +2745,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);
}
Expand Down
Loading

0 comments on commit 716c330

Please sign in to comment.