Commit fe625755 authored by Daniel Scally's avatar Daniel Scally Committed by Greg Kroah-Hartman
Browse files

usb: gadget: uvc: Allow linking function to string descs



Currently the string descriptors for the IAD and VideoStreaming
Interfaces are hardcoded into f_uvc. Now that we can create arbitrary
string descriptors, add a mechanism to define string descriptors for
the IAD, VC and VS interfaces by linking to the appropriate directory
at function level.

Signed-off-by: default avatarDaniel Scally <dan.scally@ideasonboard.com>
Link: https://lore.kernel.org/r/20230206161802.892954-11-dan.scally@ideasonboard.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 9963f744
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -82,6 +82,14 @@ struct f_uvc_opts {
	struct uvc_descriptor_header			**uvc_hs_streaming_cls;
	struct uvc_descriptor_header			**uvc_ss_streaming_cls;

	/*
	 * Indexes into the function's string descriptors allowing users to set
	 * custom descriptions rather than the hard-coded defaults.
	 */
	u8						iad_index;
	u8						vs0_index;
	u8						vs1_index;

	/*
	 * Read/write access to configfs attributes is handled by configfs.
	 *
+60 −0
Original line number Diff line number Diff line
@@ -3174,8 +3174,68 @@ static void uvc_func_item_release(struct config_item *item)
	usb_put_function_instance(&opts->func_inst);
}

static int uvc_func_allow_link(struct config_item *src, struct config_item *tgt)
{
	struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
	struct gadget_string *string;
	struct config_item *strings;
	struct f_uvc_opts *opts;
	int ret = 0;

	mutex_lock(su_mutex); /* for navigating configfs hierarchy */

	/* Validate that the target is an entry in strings/<langid> */
	strings = config_group_find_item(to_config_group(src->ci_parent->ci_parent),
					 "strings");
	if (!strings || tgt->ci_parent->ci_parent != strings) {
		ret = -EINVAL;
		goto put_strings;
	}

	string = to_gadget_string(tgt);

	opts = to_f_uvc_opts(src);
	mutex_lock(&opts->lock);

	if (!strcmp(tgt->ci_name, "iad_desc"))
		opts->iad_index = string->usb_string.id;
	else if (!strcmp(tgt->ci_name, "vs0_desc"))
		opts->vs0_index = string->usb_string.id;
	else if (!strcmp(tgt->ci_name, "vs1_desc"))
		opts->vs1_index = string->usb_string.id;
	else
		ret = -EINVAL;

	mutex_unlock(&opts->lock);

put_strings:
	config_item_put(strings);
	mutex_unlock(su_mutex);

	return ret;
}

static void uvc_func_drop_link(struct config_item *src, struct config_item *tgt)
{
	struct f_uvc_opts *opts;

	opts = to_f_uvc_opts(src);
	mutex_lock(&opts->lock);

	if (!strcmp(tgt->ci_name, "iad_desc"))
		opts->iad_index = 0;
	else if (!strcmp(tgt->ci_name, "vs0_desc"))
		opts->vs0_index = 0;
	else if (!strcmp(tgt->ci_name, "vs1_desc"))
		opts->vs1_index = 0;

	mutex_unlock(&opts->lock);
}

static struct configfs_item_operations uvc_func_item_ops = {
	.release	= uvc_func_item_release,
	.allow_link	= uvc_func_allow_link,
	.drop_link	= uvc_func_drop_link,
};

#define UVCG_OPTS_ATTR(cname, aname, limit)				\