usb: gadget: f_midi: allow customizing the USB MIDI interface string through configfs

When using f_midi from configfs the USB MIDI interface string is hardcoded
to 'MIDI function'.

This USB string descriptor is used by some third-party OS or software to
display the name of the MIDI device

Since we add an additional string option a new macro block was created to
factorize declarations

Signed-off-by: Victor Krawiec <victor.krawiec@arturia.com>
Reviewed-by: Takashi Iwai <tiwai@suse.de>
Link: https://patch.msgid.link/20251209164006.143219-1-victor.krawiec@arturia.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Victor Krawiec 2025-12-09 17:40:06 +01:00 committed by Greg Kroah-Hartman
parent 4dee13db29
commit 1c93738177
4 changed files with 78 additions and 68 deletions

View file

@ -4,11 +4,12 @@ KernelVersion: 3.19
Description:
The attributes:
========== ====================================
index index value for the USB MIDI adapter
id ID string for the USB MIDI adapter
buflen MIDI buffer length
qlen USB read request queue length
in_ports number of MIDI input ports
out_ports number of MIDI output ports
========== ====================================
================ ====================================
index index value for the USB MIDI adapter
id ID string for the USB MIDI adapter
buflen MIDI buffer length
qlen USB read request queue length
in_ports number of MIDI input ports
out_ports number of MIDI output ports
interface_string USB AudioControl interface string
================ ====================================

View file

@ -368,14 +368,15 @@ Function-specific configfs interface
The function name to use when creating the function directory is "midi".
The MIDI function provides these attributes in its function directory:
=============== ====================================
buflen MIDI buffer length
id ID string for the USB MIDI adapter
in_ports number of MIDI input ports
index index value for the USB MIDI adapter
out_ports number of MIDI output ports
qlen USB read request queue length
=============== ====================================
================ ====================================
buflen MIDI buffer length
id ID string for the USB MIDI adapter
in_ports number of MIDI input ports
index index value for the USB MIDI adapter
out_ports number of MIDI output ports
qlen USB read request queue length
interface_string USB AudioControl interface string
================ ====================================
Testing the MIDI function
-------------------------

View file

@ -875,6 +875,7 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f)
struct usb_composite_dev *cdev = c->cdev;
struct f_midi *midi = func_to_midi(f);
struct usb_string *us;
struct f_midi_opts *opts;
int status, n, jack = 1, i = 0, endpoint_descriptor_index = 0;
midi->gadget = cdev->gadget;
@ -883,6 +884,10 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f)
if (status < 0)
goto fail_register;
opts = container_of(f->fi, struct f_midi_opts, func_inst);
if (opts->interface_string)
midi_string_defs[STRING_FUNC_IDX].s = opts->interface_string;
/* maybe allocate device-global string ID */
us = usb_gstrings_attach(c->cdev, midi_strings,
ARRAY_SIZE(midi_string_defs));
@ -1178,59 +1183,60 @@ end: \
\
CONFIGFS_ATTR(f_midi_opts_, name);
#define F_MIDI_OPT_STRING(name) \
static ssize_t f_midi_opts_##name##_show(struct config_item *item, char *page) \
{ \
struct f_midi_opts *opts = to_f_midi_opts(item); \
ssize_t result; \
\
mutex_lock(&opts->lock); \
if (opts->name) { \
result = strscpy(page, opts->name, PAGE_SIZE); \
} else { \
page[0] = 0; \
result = 0; \
} \
\
mutex_unlock(&opts->lock); \
\
return result; \
} \
\
static ssize_t f_midi_opts_##name##_store(struct config_item *item, \
const char *page, size_t len) \
{ \
struct f_midi_opts *opts = to_f_midi_opts(item); \
int ret; \
char *c; \
\
mutex_lock(&opts->lock); \
if (opts->refcnt > 1) { \
ret = -EBUSY; \
goto end; \
} \
\
c = kstrndup(page, len, GFP_KERNEL); \
if (!c) { \
ret = -ENOMEM; \
goto end; \
} \
kfree(opts->name); \
opts->name = c; \
ret = len; \
end: \
mutex_unlock(&opts->lock); \
return ret; \
} \
\
CONFIGFS_ATTR(f_midi_opts_, name)
F_MIDI_OPT_SIGNED(index, true, SNDRV_CARDS);
F_MIDI_OPT(buflen, false, 0);
F_MIDI_OPT(qlen, false, 0);
F_MIDI_OPT(in_ports, true, MAX_PORTS);
F_MIDI_OPT(out_ports, true, MAX_PORTS);
static ssize_t f_midi_opts_id_show(struct config_item *item, char *page)
{
struct f_midi_opts *opts = to_f_midi_opts(item);
ssize_t result;
mutex_lock(&opts->lock);
if (opts->id) {
result = strscpy(page, opts->id, PAGE_SIZE);
} else {
page[0] = 0;
result = 0;
}
mutex_unlock(&opts->lock);
return result;
}
static ssize_t f_midi_opts_id_store(struct config_item *item,
const char *page, size_t len)
{
struct f_midi_opts *opts = to_f_midi_opts(item);
int ret;
char *c;
mutex_lock(&opts->lock);
if (opts->refcnt > 1) {
ret = -EBUSY;
goto end;
}
c = kstrndup(page, len, GFP_KERNEL);
if (!c) {
ret = -ENOMEM;
goto end;
}
if (opts->id_allocated)
kfree(opts->id);
opts->id = c;
opts->id_allocated = true;
ret = len;
end:
mutex_unlock(&opts->lock);
return ret;
}
CONFIGFS_ATTR(f_midi_opts_, id);
F_MIDI_OPT_STRING(id);
F_MIDI_OPT_STRING(interface_string);
static struct configfs_attribute *midi_attrs[] = {
&f_midi_opts_attr_index,
@ -1239,6 +1245,7 @@ static struct configfs_attribute *midi_attrs[] = {
&f_midi_opts_attr_in_ports,
&f_midi_opts_attr_out_ports,
&f_midi_opts_attr_id,
&f_midi_opts_attr_interface_string,
NULL,
};
@ -1262,8 +1269,8 @@ static void f_midi_free_inst(struct usb_function_instance *f)
mutex_unlock(&opts->lock);
if (free) {
if (opts->id_allocated)
kfree(opts->id);
kfree(opts->id);
kfree(opts->interface_string);
kfree(opts);
}
}
@ -1279,7 +1286,8 @@ static struct usb_function_instance *f_midi_alloc_inst(void)
mutex_init(&opts->lock);
opts->func_inst.free_func_inst = f_midi_free_inst;
opts->index = SNDRV_DEFAULT_IDX1;
opts->id = SNDRV_DEFAULT_STR1;
opts->id = NULL;
opts->interface_string = NULL;
opts->buflen = 512;
opts->qlen = 32;
opts->in_ports = 1;

View file

@ -19,7 +19,7 @@ struct f_midi_opts {
struct usb_function_instance func_inst;
int index;
char *id;
bool id_allocated;
char *interface_string;
unsigned int in_ports;
unsigned int out_ports;
unsigned int buflen;