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>
This commit is contained in:
Daniel Scally 2023-02-06 16:17:57 +00:00 committed by Greg Kroah-Hartman
parent 6e2a512d95
commit 15a7cf8caa
3 changed files with 191 additions and 2 deletions

View File

@ -90,6 +90,16 @@ Then the strings can be specified::
$ echo <manufacturer> > strings/0x409/manufacturer
$ echo <product> > strings/0x409/product
Further custom string descriptors can be created as directories within the
language's directory, with the string text being written to the "s" attribute
within the string's directory:
$ mkdir strings/0x409/xu.0
$ echo <string text> > strings/0x409/xu.0/s
Where function drivers support it, functions may allow symlinks to these custom
string descriptors to associate those strings with class descriptors.
2. Creating the configurations
------------------------------

View File

@ -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)

View File

@ -15,6 +15,7 @@
#ifndef __LINUX_USB_GADGET_H
#define __LINUX_USB_GADGET_H
#include <linux/configfs.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/init.h>
@ -821,6 +822,16 @@ int usb_gadget_get_string(const struct usb_gadget_strings *table, int id, u8 *bu
/* check if the given language identifier is valid */
bool usb_validate_langid(u16 langid);
struct gadget_string {
struct config_item item;
struct list_head list;
char string[USB_MAX_STRING_LEN];
struct usb_string usb_string;
};
#define to_gadget_string(str_item)\
container_of(str_item, struct gadget_string, item)
/*-------------------------------------------------------------------------*/
/* utility to simplify managing config descriptors */