usb: gadget: uvc: Allow linking function to string descs
authorDaniel Scally <dan.scally@ideasonboard.com>
Mon, 6 Feb 2023 16:18:01 +0000 (16:18 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 7 Feb 2023 07:46:37 +0000 (08:46 +0100)
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: Daniel Scally <dan.scally@ideasonboard.com>
Link: https://lore.kernel.org/r/20230206161802.892954-11-dan.scally@ideasonboard.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/gadget/function/u_uvc.h
drivers/usb/gadget/function/uvc_configfs.c

index 0345b8fc36ff52d9c8b78b1c12f27186835a6e6a..1ce58f61253c9afaa53f51bf3e6164bb87a90321 100644 (file)
@@ -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.
         *
index 3ac27838514c6da8f4b4bf3f791a924736672a8c..18c6a1461b7eab25afe34a8d1be55cab8c10933f 100644 (file)
@@ -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)                            \