diff options
author | Daniel Scally <dan.scally@ideasonboard.com> | 2023-02-06 19:17:57 +0300 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2023-02-07 10:46:36 +0300 |
commit | 15a7cf8caabee4613764abe7814dd3162cb64137 (patch) | |
tree | 1152eea6bfffa3025c5cd354c064138ddce46b84 /drivers/usb | |
parent | 6e2a512d9532c51889a2601cba338c4673a09374 (diff) | |
download | linux-15a7cf8caabee4613764abe7814dd3162cb64137.tar.xz |
usb: gadget: configfs: Support arbitrary string descriptors
Add a framework to allow users to define arbitrary string descriptors
for a USB Gadget. This is modelled as a new type of config item rather
than as hardcoded attributes so as to be as flexible as possible.
Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com>
Link: https://lore.kernel.org/r/20230206161802.892954-7-dan.scally@ideasonboard.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/gadget/configfs.c | 172 |
1 files changed, 170 insertions, 2 deletions
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 3beeafcf2e3b..ac275855eeb6 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -95,6 +95,8 @@ struct gadget_language { struct config_group group; struct list_head list; + struct list_head gadget_strings; + unsigned int nstrings; }; struct gadget_config_name { @@ -791,8 +793,174 @@ static void gadget_language_attr_release(struct config_item *item) kfree(gs); } -USB_CONFIG_STRING_RW_OPS(gadget_language); -USB_CONFIG_STRINGS_LANG(gadget_language, gadget_info); +static struct configfs_item_operations gadget_language_langid_item_ops = { + .release = gadget_language_attr_release, +}; + +static ssize_t gadget_string_id_show(struct config_item *item, char *page) +{ + struct gadget_string *string = to_gadget_string(item); + int ret; + + ret = sprintf(page, "%u\n", string->usb_string.id); + return ret; +} +CONFIGFS_ATTR_RO(gadget_string_, id); + +static ssize_t gadget_string_s_show(struct config_item *item, char *page) +{ + struct gadget_string *string = to_gadget_string(item); + int ret; + + ret = snprintf(page, sizeof(string->string), "%s\n", string->string); + return ret; +} + +static ssize_t gadget_string_s_store(struct config_item *item, const char *page, + size_t len) +{ + struct gadget_string *string = to_gadget_string(item); + int size = min(sizeof(string->string), len + 1); + int ret; + + if (len > USB_MAX_STRING_LEN) + return -EINVAL; + + ret = strscpy(string->string, page, size); + return len; +} +CONFIGFS_ATTR(gadget_string_, s); + +static struct configfs_attribute *gadget_string_attrs[] = { + &gadget_string_attr_id, + &gadget_string_attr_s, + NULL, +}; + +static void gadget_string_release(struct config_item *item) +{ + struct gadget_string *string = to_gadget_string(item); + + kfree(string); +} + +static struct configfs_item_operations gadget_string_item_ops = { + .release = gadget_string_release, +}; + +static const struct config_item_type gadget_string_type = { + .ct_item_ops = &gadget_string_item_ops, + .ct_attrs = gadget_string_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_item *gadget_language_string_make(struct config_group *group, + const char *name) +{ + struct gadget_language *language; + struct gadget_string *string; + + language = to_gadget_language(&group->cg_item); + + string = kzalloc(sizeof(*string), GFP_KERNEL); + if (!string) + return ERR_PTR(-ENOMEM); + + string->usb_string.id = language->nstrings++; + string->usb_string.s = string->string; + list_add_tail(&string->list, &language->gadget_strings); + + config_item_init_type_name(&string->item, name, &gadget_string_type); + + return &string->item; +} + +static void gadget_language_string_drop(struct config_group *group, + struct config_item *item) +{ + struct gadget_language *language; + struct gadget_string *string; + unsigned int i = USB_GADGET_FIRST_AVAIL_IDX; + + language = to_gadget_language(&group->cg_item); + string = to_gadget_string(item); + + 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->gadget_strings, list) + string->usb_string.id = i++; +} + +static struct configfs_group_operations gadget_language_langid_group_ops = { + .make_item = gadget_language_string_make, + .drop_item = gadget_language_string_drop, +}; + +static struct config_item_type gadget_language_type = { + .ct_item_ops = &gadget_language_langid_item_ops, + .ct_group_ops = &gadget_language_langid_group_ops, + .ct_attrs = gadget_language_langid_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_group *gadget_language_make(struct config_group *group, + const char *name) +{ + struct gadget_info *gi; + struct gadget_language *gs; + struct gadget_language *new; + int langs = 0; + int ret; + + new = kzalloc(sizeof(*new), GFP_KERNEL); + if (!new) + return ERR_PTR(-ENOMEM); + + ret = check_user_usb_string(name, &new->stringtab_dev); + if (ret) + goto err; + config_group_init_type_name(&new->group, name, + &gadget_language_type); + + gi = container_of(group, struct gadget_info, strings_group); + ret = -EEXIST; + list_for_each_entry(gs, &gi->string_list, list) { + if (gs->stringtab_dev.language == new->stringtab_dev.language) + goto err; + langs++; + } + ret = -EOVERFLOW; + if (langs >= MAX_USB_STRING_LANGS) + goto err; + + list_add_tail(&new->list, &gi->string_list); + INIT_LIST_HEAD(&new->gadget_strings); + + /* We have the default manufacturer, product and serialnumber strings */ + new->nstrings = 3; + return &new->group; +err: + kfree(new); + return ERR_PTR(ret); +} + +static void gadget_language_drop(struct config_group *group, + struct config_item *item) +{ + config_item_put(item); +} + +static struct configfs_group_operations gadget_language_group_ops = { + .make_group = &gadget_language_make, + .drop_item = &gadget_language_drop, +}; + +static struct config_item_type gadget_language_strings_type = { + .ct_group_ops = &gadget_language_group_ops, + .ct_owner = THIS_MODULE, +}; static inline struct gadget_info *webusb_item_to_gadget_info( struct config_item *item) |