Message ID | 20221121092517.225242-6-dan.scally@ideasonboard.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Add XU support to UVC Gadget | expand |
Hi Daniel, I love your patch! Perhaps something to improve: [auto build test WARNING on usb/usb-testing] [also build test WARNING on usb/usb-next usb/usb-linus westeri-thunderbolt/next linus/master v6.1-rc6 next-20221118] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/Daniel-Scally/Add-XU-support-to-UVC-Gadget/20221121-172732 base: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing patch link: https://lore.kernel.org/r/20221121092517.225242-6-dan.scally%40ideasonboard.com patch subject: [PATCH v2 5/9] usb: gadget: uvc: Support arbitrary string descriptors config: m68k-allyesconfig compiler: m68k-linux-gcc (GCC) 12.1.0 reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/intel-lab-lkp/linux/commit/8356f287e416350c3398c8b51fe0e91d47563f85 git remote add linux-review https://github.com/intel-lab-lkp/linux git fetch --no-tags linux-review Daniel-Scally/Add-XU-support-to-UVC-Gadget/20221121-172732 git checkout 8356f287e416350c3398c8b51fe0e91d47563f85 # save the config file mkdir build_dir && cp config build_dir/.config COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=m68k SHELL=/bin/bash drivers/usb/gadget/function/ If you fix the issue, kindly add following tag where applicable | Reported-by: kernel test robot <lkp@intel.com> All warnings (new ones prefixed by >>): drivers/usb/gadget/function/uvc_configfs.c: In function 'uvcg_string_s_store': >> drivers/usb/gadget/function/uvc_configfs.c:2889:13: warning: variable 'ret' set but not used [-Wunused-but-set-variable] 2889 | int ret; | ^~~ vim +/ret +2889 drivers/usb/gadget/function/uvc_configfs.c 2879 2880 static ssize_t uvcg_string_s_store(struct config_item *item, const char *page, 2881 size_t len) 2882 { 2883 struct config_group *group = to_config_group(item->ci_parent); 2884 struct mutex *su_mutex = &group->cg_subsys->su_mutex; 2885 struct uvcg_string *string = to_uvcg_string(item); 2886 int size = min(sizeof(string->string), len + 1); 2887 struct config_item *opts_item; 2888 struct f_uvc_opts *opts; > 2889 int ret; 2890 2891 if (len > USB_MAX_STRING_LEN) 2892 return -EINVAL; 2893 2894 mutex_lock(su_mutex); 2895 2896 opts_item = item->ci_parent->ci_parent->ci_parent; 2897 opts = to_f_uvc_opts(opts_item); 2898 2899 mutex_lock(&opts->lock); 2900 ret = strscpy(string->string, page, size); 2901 mutex_unlock(&opts->lock); 2902 2903 mutex_unlock(su_mutex); 2904 2905 return len; 2906 } 2907 UVC_ATTR(uvcg_string_, s, s); 2908
Hi Dan, Thank you for the patch. On Mon, Nov 21, 2022 at 09:25:13AM +0000, Daniel Scally wrote: > Currently string descriptors for the UVC function are largely hard > coded. It's not practically possible to support string descriptors > that describe Extension Units that way, so add a mechanism to the > configfs tree that allows the definition of arbitrary string > descriptors. Hmmmm... Is this something that should be specific to the UVC gadget, or should it be a core gadget helper ? > Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com> > --- > Changes in v2: > > - New patch > > .../ABI/testing/configfs-usb-gadget-uvc | 20 ++ > drivers/usb/gadget/function/f_uvc.c | 1 + > drivers/usb/gadget/function/u_uvc.h | 7 + > drivers/usb/gadget/function/uvc_configfs.c | 302 ++++++++++++++++++ > drivers/usb/gadget/function/uvc_configfs.h | 30 ++ > 5 files changed, 360 insertions(+) > > diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uvc b/Documentation/ABI/testing/configfs-usb-gadget-uvc > index 045c57e7e245..5faa049ed759 100644 > --- a/Documentation/ABI/testing/configfs-usb-gadget-uvc > +++ b/Documentation/ABI/testing/configfs-usb-gadget-uvc > @@ -10,6 +10,26 @@ Description: UVC function directory > function_name string [32] > =================== ============================= > > +What: /config/usb-gadget/gadget/functions/uvc.name/strings > +Date: Nov 2022 > +KernelVersion: 6.1 > +Description: String descriptors > + > +What: /config/usb-gadget/gadget/functions/uvc.name/strings/langid > +Date: Nov 2022 > +KernelVersion: 6.1 > +Description: String descriptors for langid (e.g. 0x409) > + > +What: /config/usb-gadget/gadget/functions/uvc.name/strings/langid/name > +Date: Nov 2022 > +KernelVersion: 6.1 > +Description: String descriptor > + > + =================== ============================= > + id id of the string descriptor > + s 126 character string > + =================== ============================= > + > What: /config/usb-gadget/gadget/functions/uvc.name/control > Date: Dec 2014 > KernelVersion: 4.0 > diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c > index e0a308f1355c..1b8871a24be8 100644 > --- a/drivers/usb/gadget/function/f_uvc.c > +++ b/drivers/usb/gadget/function/f_uvc.c > @@ -912,6 +912,7 @@ static struct usb_function_instance *uvc_alloc_inst(void) > (const struct uvc_descriptor_header * const *)ctl_cls; > > INIT_LIST_HEAD(&opts->extension_units); > + INIT_LIST_HEAD(&opts->languages); > > opts->streaming_interval = 1; > opts->streaming_maxpacket = 1024; > diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h > index 5119cfe5ee4e..c1c9ea5931d3 100644 > --- a/drivers/usb/gadget/function/u_uvc.h > +++ b/drivers/usb/gadget/function/u_uvc.h > @@ -81,6 +81,13 @@ struct f_uvc_opts { > struct uvc_descriptor_header **uvc_hs_streaming_cls; > struct uvc_descriptor_header **uvc_ss_streaming_cls; > > + /* > + * A list of languages, associated with which may be string descriptors > + * for various parts of the gadget, including the IAD and XUs. > + */ > + struct list_head languages; > + unsigned int nlangs; > + > /* > * Read/write access to configfs attributes is handled by configfs. > * > diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c > index 0a69eb6cf221..da2f70036993 100644 > --- a/drivers/usb/gadget/function/uvc_configfs.c > +++ b/drivers/usb/gadget/function/uvc_configfs.c > @@ -13,6 +13,8 @@ > #include "uvc_configfs.h" > > #include <linux/sort.h> > +#include <linux/usb/gadget.h> > + > > /* ----------------------------------------------------------------------------- > * Global Utility Structures and Macros > @@ -2824,6 +2826,305 @@ static const struct uvcg_config_group_type uvcg_streaming_grp_type = { > }, > }; > > +/* ----------------------------------------------------------------------------- > + * strings/<langid> > + */ > + > +static ssize_t uvcg_string_id_show(struct config_item *item, char *page) > +{ > + struct config_group *group = to_config_group(item->ci_parent); > + struct mutex *su_mutex = &group->cg_subsys->su_mutex; > + struct uvcg_string *string = to_uvcg_string(item); > + struct config_item *opts_item; > + struct f_uvc_opts *opts; > + int ret; > + > + mutex_lock(su_mutex); > + > + opts_item = item->ci_parent->ci_parent->ci_parent; > + opts = to_f_uvc_opts(opts_item); > + > + mutex_lock(&opts->lock); > + ret = sprintf(page, "%u\n", string->usb_string.id); > + mutex_unlock(&opts->lock); > + > + mutex_unlock(su_mutex); > + > + return ret; > +} > +UVC_ATTR_RO(uvcg_string_, id, id); > + > +static ssize_t uvcg_string_s_show(struct config_item *item, char *page) > +{ > + struct config_group *group = to_config_group(item->ci_parent); > + struct mutex *su_mutex = &group->cg_subsys->su_mutex; > + struct uvcg_string *string = to_uvcg_string(item); > + struct config_item *opts_item; > + struct f_uvc_opts *opts; > + int ret; > + > + mutex_lock(su_mutex); > + > + opts_item = item->ci_parent->ci_parent->ci_parent; > + opts = to_f_uvc_opts(opts_item); > + > + mutex_lock(&opts->lock); > + ret = snprintf(page, sizeof(string->string), "%s\n", string->string); > + mutex_unlock(&opts->lock); > + > + mutex_unlock(su_mutex); > + > + return ret; > +} > + > +static ssize_t uvcg_string_s_store(struct config_item *item, const char *page, > + size_t len) > +{ > + struct config_group *group = to_config_group(item->ci_parent); > + struct mutex *su_mutex = &group->cg_subsys->su_mutex; > + struct uvcg_string *string = to_uvcg_string(item); > + int size = min(sizeof(string->string), len + 1); > + struct config_item *opts_item; > + struct f_uvc_opts *opts; > + int ret; > + > + if (len > USB_MAX_STRING_LEN) > + return -EINVAL; > + > + mutex_lock(su_mutex); > + > + opts_item = item->ci_parent->ci_parent->ci_parent; > + opts = to_f_uvc_opts(opts_item); > + > + mutex_lock(&opts->lock); > + ret = strscpy(string->string, page, size); > + mutex_unlock(&opts->lock); > + > + mutex_unlock(su_mutex); > + > + return len; > +} > +UVC_ATTR(uvcg_string_, s, s); > + > +static struct configfs_attribute *uvcg_string_attrs[] = { > + &uvcg_string_attr_id, > + &uvcg_string_attr_s, > + NULL, > +}; > + > +static void uvcg_string_release(struct config_item *item) > +{ > + struct uvcg_string *string = to_uvcg_string(item); > + > + kfree(string); > +} > + > +static struct configfs_item_operations uvcg_string_item_ops = { > + .release = uvcg_string_release, > +}; > + > +static const struct config_item_type uvcg_string_type = { > + .ct_item_ops = &uvcg_string_item_ops, > + .ct_attrs = uvcg_string_attrs, > + .ct_owner = THIS_MODULE, > +}; > + > +static struct config_item *uvcg_string_make(struct config_group *group, > + const char *name) > +{ > + struct uvcg_language *language; > + struct config_item *opts_item; > + struct uvcg_string *string; > + struct f_uvc_opts *opts; > + > + language = to_uvcg_language(group); > + > + string = kzalloc(sizeof(*string), GFP_KERNEL); > + if (!string) > + return ERR_PTR(-ENOMEM); > + > + opts_item = group->cg_item.ci_parent->ci_parent; > + opts = to_f_uvc_opts(opts_item); > + > + mutex_lock(&opts->lock); > + > + string->usb_string.id = language->nstrings++; > + string->usb_string.s = string->string; > + list_add_tail(&string->list, &language->strings); > + > + config_item_init_type_name(&string->item, name, &uvcg_string_type); > + > + mutex_unlock(&opts->lock); > + > + return &string->item; > +} > + > +static void uvcg_string_drop(struct config_group *group, struct config_item *item) > +{ > + struct uvcg_language *language; > + struct config_item *opts_item; > + struct uvcg_string *string; > + struct f_uvc_opts *opts; > + unsigned int i = 1; > + > + language = to_uvcg_language(group); > + string = to_uvcg_string(item); > + > + opts_item = group->cg_item.ci_parent->ci_parent; > + opts = to_f_uvc_opts(opts_item); > + > + mutex_lock(&opts->lock); > + > + list_del(&string->list); > + language->nstrings--; > + > + /* Reset the ids for the language's strings to guarantee a continuous set */ > + list_for_each_entry(string, &language->strings, list) > + string->usb_string.id = i++; > + > + mutex_unlock(&opts->lock); > +} > + > +/* ----------------------------------------------------------------------------- > + * strings/ > + */ > + > +static struct configfs_group_operations uvcg_language_group_ops = { > + .make_item = uvcg_string_make, > + .drop_item = uvcg_string_drop, > +}; > + > +static void uvcg_language_release(struct config_item *item) > +{ > + struct uvcg_language *language = to_uvcg_language(to_config_group(item)); > + > + kfree(language); > +} > + > +static struct configfs_item_operations uvcg_language_item_ops = { > + .release = uvcg_language_release, > +}; > + > +/* > + * The strings attribute is really a helper for users - having defined some string > + * descriptors for this language, actually checking what's set when they're all > + * in separate directories would be a bit...suboptimal. This read-only attribute > + * gives a summary at language-level. > + */ > +static ssize_t uvcg_language_strings_show(struct config_item *item, char *page) > +{ > + struct config_group *group = to_config_group(item); > + struct uvcg_language *language = to_uvcg_language(group); > + struct mutex *su_mutex = &group->cg_subsys->su_mutex; > + struct config_item *opts_item; > + struct uvcg_string *string; > + struct f_uvc_opts *opts; > + char *pg = page; > + int ret = 0; > + > + mutex_lock(su_mutex); > + > + opts_item = item->ci_parent->ci_parent; > + opts = to_f_uvc_opts(opts_item); > + > + mutex_lock(&opts->lock); > + > + list_for_each_entry(string, &language->strings, list) { > + ret += sprintf(pg, "%s: %s [%u]\n", string->item.ci_name, > + string->usb_string.s, string->usb_string.id); > + pg = page + ret; > + } > + > + mutex_unlock(&opts->lock); > + > + mutex_unlock(su_mutex); > + > + return ret; > +} > +UVC_ATTR_RO(uvcg_language_, strings, strings); > + > +static struct configfs_attribute *uvcg_language_attrs[] = { > + &uvcg_language_attr_strings, > + NULL > +}; > + > +static const struct config_item_type uvcg_language_type = { > + .ct_item_ops = &uvcg_language_item_ops, > + .ct_group_ops = &uvcg_language_group_ops, > + .ct_attrs = uvcg_language_attrs, > + .ct_owner = THIS_MODULE, > +}; > + > +static struct config_group *uvcg_language_make(struct config_group *group, > + const char *name) > +{ > + struct uvcg_language *language; > + struct config_item *opts_item; > + struct f_uvc_opts *opts; > + int ret; > + u16 num; > + > + ret = kstrtou16(name, 0, &num); > + if (ret) > + return ERR_PTR(ret); > + > + if (!usb_validate_langid(num)) > + return ERR_PTR(-EINVAL); > + > + language = kzalloc(sizeof(*language), GFP_KERNEL); > + if (!language) > + return ERR_PTR(-ENOMEM); > + > + opts_item = group->cg_item.ci_parent; > + opts = to_f_uvc_opts(opts_item); > + > + mutex_lock(&opts->lock); > + > + language->stringtab.language = num; > + list_add_tail(&language->list, &opts->languages); > + opts->nlangs++; > + INIT_LIST_HEAD(&language->strings); > + > + config_group_init_type_name(&language->group, name, &uvcg_language_type); > + > + mutex_unlock(&opts->lock); > + > + return &language->group; > +} > + > +static void uvcg_language_drop(struct config_group *group, struct config_item *item) > +{ > + struct uvcg_language *language; > + struct config_item *opts_item; > + struct f_uvc_opts *opts; > + > + language = to_uvcg_language(to_config_group(item)); > + > + opts_item = group->cg_item.ci_parent; > + opts = to_f_uvc_opts(opts_item); > + > + mutex_lock(&opts->lock); > + > + list_del(&language->list); > + config_item_put(item); > + > + mutex_unlock(&opts->lock); > +} > + > +static struct configfs_group_operations uvcg_strings_grp_ops = { > + .make_group = uvcg_language_make, > + .drop_item = uvcg_language_drop, > +}; > + > +static const struct uvcg_config_group_type uvcg_strings_grp_type = { > + .type = { > + .ct_item_ops = &uvcg_config_item_ops, > + .ct_group_ops = &uvcg_strings_grp_ops, > + .ct_owner = THIS_MODULE, > + }, > + .name = "strings", > +}; > + > /* ----------------------------------------------------------------------------- > * UVC function > */ > @@ -2951,6 +3252,7 @@ static const struct uvcg_config_group_type uvc_func_type = { > .children = (const struct uvcg_config_group_type*[]) { > &uvcg_control_grp_type, > &uvcg_streaming_grp_type, > + &uvcg_strings_grp_type, > NULL, > }, > }; > diff --git a/drivers/usb/gadget/function/uvc_configfs.h b/drivers/usb/gadget/function/uvc_configfs.h > index c9a4182fb26f..a714426a174a 100644 > --- a/drivers/usb/gadget/function/uvc_configfs.h > +++ b/drivers/usb/gadget/function/uvc_configfs.h > @@ -13,6 +13,7 @@ > #define UVC_CONFIGFS_H > > #include <linux/configfs.h> > +#include <linux/usb/gadget.h> > > #include "u_uvc.h" > > @@ -132,6 +133,35 @@ static inline struct uvcg_mjpeg *to_uvcg_mjpeg(struct config_item *item) > return container_of(to_uvcg_format(item), struct uvcg_mjpeg, fmt); > } > > +/* ----------------------------------------------------------------------------- > + * strings/ > + */ > + > +struct uvcg_language { > + struct config_group group; > + unsigned int nstrings; > + struct list_head list; > + struct list_head strings; > + struct usb_gadget_strings stringtab; > +}; > + > +#define to_uvcg_language(language) \ > +container_of(language, struct uvcg_language, group) > + > +/* ----------------------------------------------------------------------------- > + * strings/<LANGID> > + */ > + > +struct uvcg_string { > + struct config_item item; > + struct list_head list; > + char string[USB_MAX_STRING_LEN]; > + struct usb_string usb_string; > +}; > + > +#define to_uvcg_string(str_item)\ > +container_of(str_item, struct uvcg_string, item) > + > /* ----------------------------------------------------------------------------- > * control/extensions/<NAME> > */
diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uvc b/Documentation/ABI/testing/configfs-usb-gadget-uvc index 045c57e7e245..5faa049ed759 100644 --- a/Documentation/ABI/testing/configfs-usb-gadget-uvc +++ b/Documentation/ABI/testing/configfs-usb-gadget-uvc @@ -10,6 +10,26 @@ Description: UVC function directory function_name string [32] =================== ============================= +What: /config/usb-gadget/gadget/functions/uvc.name/strings +Date: Nov 2022 +KernelVersion: 6.1 +Description: String descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/strings/langid +Date: Nov 2022 +KernelVersion: 6.1 +Description: String descriptors for langid (e.g. 0x409) + +What: /config/usb-gadget/gadget/functions/uvc.name/strings/langid/name +Date: Nov 2022 +KernelVersion: 6.1 +Description: String descriptor + + =================== ============================= + id id of the string descriptor + s 126 character string + =================== ============================= + What: /config/usb-gadget/gadget/functions/uvc.name/control Date: Dec 2014 KernelVersion: 4.0 diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index e0a308f1355c..1b8871a24be8 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -912,6 +912,7 @@ static struct usb_function_instance *uvc_alloc_inst(void) (const struct uvc_descriptor_header * const *)ctl_cls; INIT_LIST_HEAD(&opts->extension_units); + INIT_LIST_HEAD(&opts->languages); opts->streaming_interval = 1; opts->streaming_maxpacket = 1024; diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h index 5119cfe5ee4e..c1c9ea5931d3 100644 --- a/drivers/usb/gadget/function/u_uvc.h +++ b/drivers/usb/gadget/function/u_uvc.h @@ -81,6 +81,13 @@ struct f_uvc_opts { struct uvc_descriptor_header **uvc_hs_streaming_cls; struct uvc_descriptor_header **uvc_ss_streaming_cls; + /* + * A list of languages, associated with which may be string descriptors + * for various parts of the gadget, including the IAD and XUs. + */ + struct list_head languages; + unsigned int nlangs; + /* * Read/write access to configfs attributes is handled by configfs. * diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index 0a69eb6cf221..da2f70036993 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -13,6 +13,8 @@ #include "uvc_configfs.h" #include <linux/sort.h> +#include <linux/usb/gadget.h> + /* ----------------------------------------------------------------------------- * Global Utility Structures and Macros @@ -2824,6 +2826,305 @@ static const struct uvcg_config_group_type uvcg_streaming_grp_type = { }, }; +/* ----------------------------------------------------------------------------- + * strings/<langid> + */ + +static ssize_t uvcg_string_id_show(struct config_item *item, char *page) +{ + struct config_group *group = to_config_group(item->ci_parent); + struct mutex *su_mutex = &group->cg_subsys->su_mutex; + struct uvcg_string *string = to_uvcg_string(item); + struct config_item *opts_item; + struct f_uvc_opts *opts; + int ret; + + mutex_lock(su_mutex); + + opts_item = item->ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + ret = sprintf(page, "%u\n", string->usb_string.id); + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + + return ret; +} +UVC_ATTR_RO(uvcg_string_, id, id); + +static ssize_t uvcg_string_s_show(struct config_item *item, char *page) +{ + struct config_group *group = to_config_group(item->ci_parent); + struct mutex *su_mutex = &group->cg_subsys->su_mutex; + struct uvcg_string *string = to_uvcg_string(item); + struct config_item *opts_item; + struct f_uvc_opts *opts; + int ret; + + mutex_lock(su_mutex); + + opts_item = item->ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + ret = snprintf(page, sizeof(string->string), "%s\n", string->string); + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + + return ret; +} + +static ssize_t uvcg_string_s_store(struct config_item *item, const char *page, + size_t len) +{ + struct config_group *group = to_config_group(item->ci_parent); + struct mutex *su_mutex = &group->cg_subsys->su_mutex; + struct uvcg_string *string = to_uvcg_string(item); + int size = min(sizeof(string->string), len + 1); + struct config_item *opts_item; + struct f_uvc_opts *opts; + int ret; + + if (len > USB_MAX_STRING_LEN) + return -EINVAL; + + mutex_lock(su_mutex); + + opts_item = item->ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + ret = strscpy(string->string, page, size); + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + + return len; +} +UVC_ATTR(uvcg_string_, s, s); + +static struct configfs_attribute *uvcg_string_attrs[] = { + &uvcg_string_attr_id, + &uvcg_string_attr_s, + NULL, +}; + +static void uvcg_string_release(struct config_item *item) +{ + struct uvcg_string *string = to_uvcg_string(item); + + kfree(string); +} + +static struct configfs_item_operations uvcg_string_item_ops = { + .release = uvcg_string_release, +}; + +static const struct config_item_type uvcg_string_type = { + .ct_item_ops = &uvcg_string_item_ops, + .ct_attrs = uvcg_string_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_item *uvcg_string_make(struct config_group *group, + const char *name) +{ + struct uvcg_language *language; + struct config_item *opts_item; + struct uvcg_string *string; + struct f_uvc_opts *opts; + + language = to_uvcg_language(group); + + string = kzalloc(sizeof(*string), GFP_KERNEL); + if (!string) + return ERR_PTR(-ENOMEM); + + opts_item = group->cg_item.ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + + string->usb_string.id = language->nstrings++; + string->usb_string.s = string->string; + list_add_tail(&string->list, &language->strings); + + config_item_init_type_name(&string->item, name, &uvcg_string_type); + + mutex_unlock(&opts->lock); + + return &string->item; +} + +static void uvcg_string_drop(struct config_group *group, struct config_item *item) +{ + struct uvcg_language *language; + struct config_item *opts_item; + struct uvcg_string *string; + struct f_uvc_opts *opts; + unsigned int i = 1; + + language = to_uvcg_language(group); + string = to_uvcg_string(item); + + opts_item = group->cg_item.ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + + list_del(&string->list); + language->nstrings--; + + /* Reset the ids for the language's strings to guarantee a continuous set */ + list_for_each_entry(string, &language->strings, list) + string->usb_string.id = i++; + + mutex_unlock(&opts->lock); +} + +/* ----------------------------------------------------------------------------- + * strings/ + */ + +static struct configfs_group_operations uvcg_language_group_ops = { + .make_item = uvcg_string_make, + .drop_item = uvcg_string_drop, +}; + +static void uvcg_language_release(struct config_item *item) +{ + struct uvcg_language *language = to_uvcg_language(to_config_group(item)); + + kfree(language); +} + +static struct configfs_item_operations uvcg_language_item_ops = { + .release = uvcg_language_release, +}; + +/* + * The strings attribute is really a helper for users - having defined some string + * descriptors for this language, actually checking what's set when they're all + * in separate directories would be a bit...suboptimal. This read-only attribute + * gives a summary at language-level. + */ +static ssize_t uvcg_language_strings_show(struct config_item *item, char *page) +{ + struct config_group *group = to_config_group(item); + struct uvcg_language *language = to_uvcg_language(group); + struct mutex *su_mutex = &group->cg_subsys->su_mutex; + struct config_item *opts_item; + struct uvcg_string *string; + struct f_uvc_opts *opts; + char *pg = page; + int ret = 0; + + mutex_lock(su_mutex); + + opts_item = item->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + + list_for_each_entry(string, &language->strings, list) { + ret += sprintf(pg, "%s: %s [%u]\n", string->item.ci_name, + string->usb_string.s, string->usb_string.id); + pg = page + ret; + } + + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + + return ret; +} +UVC_ATTR_RO(uvcg_language_, strings, strings); + +static struct configfs_attribute *uvcg_language_attrs[] = { + &uvcg_language_attr_strings, + NULL +}; + +static const struct config_item_type uvcg_language_type = { + .ct_item_ops = &uvcg_language_item_ops, + .ct_group_ops = &uvcg_language_group_ops, + .ct_attrs = uvcg_language_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_group *uvcg_language_make(struct config_group *group, + const char *name) +{ + struct uvcg_language *language; + struct config_item *opts_item; + struct f_uvc_opts *opts; + int ret; + u16 num; + + ret = kstrtou16(name, 0, &num); + if (ret) + return ERR_PTR(ret); + + if (!usb_validate_langid(num)) + return ERR_PTR(-EINVAL); + + language = kzalloc(sizeof(*language), GFP_KERNEL); + if (!language) + return ERR_PTR(-ENOMEM); + + opts_item = group->cg_item.ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + + language->stringtab.language = num; + list_add_tail(&language->list, &opts->languages); + opts->nlangs++; + INIT_LIST_HEAD(&language->strings); + + config_group_init_type_name(&language->group, name, &uvcg_language_type); + + mutex_unlock(&opts->lock); + + return &language->group; +} + +static void uvcg_language_drop(struct config_group *group, struct config_item *item) +{ + struct uvcg_language *language; + struct config_item *opts_item; + struct f_uvc_opts *opts; + + language = to_uvcg_language(to_config_group(item)); + + opts_item = group->cg_item.ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + + list_del(&language->list); + config_item_put(item); + + mutex_unlock(&opts->lock); +} + +static struct configfs_group_operations uvcg_strings_grp_ops = { + .make_group = uvcg_language_make, + .drop_item = uvcg_language_drop, +}; + +static const struct uvcg_config_group_type uvcg_strings_grp_type = { + .type = { + .ct_item_ops = &uvcg_config_item_ops, + .ct_group_ops = &uvcg_strings_grp_ops, + .ct_owner = THIS_MODULE, + }, + .name = "strings", +}; + /* ----------------------------------------------------------------------------- * UVC function */ @@ -2951,6 +3252,7 @@ static const struct uvcg_config_group_type uvc_func_type = { .children = (const struct uvcg_config_group_type*[]) { &uvcg_control_grp_type, &uvcg_streaming_grp_type, + &uvcg_strings_grp_type, NULL, }, }; diff --git a/drivers/usb/gadget/function/uvc_configfs.h b/drivers/usb/gadget/function/uvc_configfs.h index c9a4182fb26f..a714426a174a 100644 --- a/drivers/usb/gadget/function/uvc_configfs.h +++ b/drivers/usb/gadget/function/uvc_configfs.h @@ -13,6 +13,7 @@ #define UVC_CONFIGFS_H #include <linux/configfs.h> +#include <linux/usb/gadget.h> #include "u_uvc.h" @@ -132,6 +133,35 @@ static inline struct uvcg_mjpeg *to_uvcg_mjpeg(struct config_item *item) return container_of(to_uvcg_format(item), struct uvcg_mjpeg, fmt); } +/* ----------------------------------------------------------------------------- + * strings/ + */ + +struct uvcg_language { + struct config_group group; + unsigned int nstrings; + struct list_head list; + struct list_head strings; + struct usb_gadget_strings stringtab; +}; + +#define to_uvcg_language(language) \ +container_of(language, struct uvcg_language, group) + +/* ----------------------------------------------------------------------------- + * strings/<LANGID> + */ + +struct uvcg_string { + struct config_item item; + struct list_head list; + char string[USB_MAX_STRING_LEN]; + struct usb_string usb_string; +}; + +#define to_uvcg_string(str_item)\ +container_of(str_item, struct uvcg_string, item) + /* ----------------------------------------------------------------------------- * control/extensions/<NAME> */
Currently string descriptors for the UVC function are largely hard coded. It's not practically possible to support string descriptors that describe Extension Units that way, so add a mechanism to the configfs tree that allows the definition of arbitrary string descriptors. Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com> --- Changes in v2: - New patch .../ABI/testing/configfs-usb-gadget-uvc | 20 ++ drivers/usb/gadget/function/f_uvc.c | 1 + drivers/usb/gadget/function/u_uvc.h | 7 + drivers/usb/gadget/function/uvc_configfs.c | 302 ++++++++++++++++++ drivers/usb/gadget/function/uvc_configfs.h | 30 ++ 5 files changed, 360 insertions(+)