Commit 6f1de344 authored by Andrzej Pietrasiewicz's avatar Andrzej Pietrasiewicz Committed by Felipe Balbi

usb: gadget: f_midi: add configfs support

Make the midi function available for gadgets composed with configfs.
Signed-off-by: default avatarAndrzej Pietrasiewicz <andrzej.p@samsung.com>
Signed-off-by: default avatarFelipe Balbi <balbi@ti.com>
parent 9caa0d77
What: /config/usb-gadget/gadget/functions/midi.name
Date: Nov 2014
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
...@@ -396,6 +396,20 @@ config USB_CONFIGFS_F_UAC2 ...@@ -396,6 +396,20 @@ config USB_CONFIGFS_F_UAC2
received from the USB Host and choose to provide whatever it received from the USB Host and choose to provide whatever it
wants as audio data to the USB Host. wants as audio data to the USB Host.
config USB_CONFIGFS_F_MIDI
boolean "MIDI function"
depends on USB_CONFIGFS
depends on SND
select USB_LIBCOMPOSITE
select SND_RAWMIDI
select USB_F_MIDI
help
The MIDI Function acts as a USB Audio device, with one MIDI
input and one MIDI output. These MIDI jacks appear as
a sound "card" in the ALSA sound system. Other MIDI
connections can then be made on the gadget system, using
ALSA's aconnect utility etc.
source "drivers/usb/gadget/legacy/Kconfig" source "drivers/usb/gadget/legacy/Kconfig"
endchoice endchoice
......
...@@ -896,12 +896,145 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) ...@@ -896,12 +896,145 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f)
return status; return status;
} }
static inline struct f_midi_opts *to_f_midi_opts(struct config_item *item)
{
return container_of(to_config_group(item), struct f_midi_opts,
func_inst.group);
}
CONFIGFS_ATTR_STRUCT(f_midi_opts);
CONFIGFS_ATTR_OPS(f_midi_opts);
static void midi_attr_release(struct config_item *item)
{
struct f_midi_opts *opts = to_f_midi_opts(item);
usb_put_function_instance(&opts->func_inst);
}
static struct configfs_item_operations midi_item_ops = {
.release = midi_attr_release,
.show_attribute = f_midi_opts_attr_show,
.store_attribute = f_midi_opts_attr_store,
};
#define F_MIDI_OPT(name, test_limit, limit) \
static ssize_t f_midi_opts_##name##_show(struct f_midi_opts *opts, char *page) \
{ \
int result; \
\
mutex_lock(&opts->lock); \
result = sprintf(page, "%d\n", opts->name); \
mutex_unlock(&opts->lock); \
\
return result; \
} \
\
static ssize_t f_midi_opts_##name##_store(struct f_midi_opts *opts, \
const char *page, size_t len) \
{ \
int ret; \
u32 num; \
\
mutex_lock(&opts->lock); \
if (opts->refcnt) { \
ret = -EBUSY; \
goto end; \
} \
\
ret = kstrtou32(page, 0, &num); \
if (ret) \
goto end; \
\
if (test_limit && num > limit) { \
ret = -EINVAL; \
goto end; \
} \
opts->name = num; \
ret = len; \
\
end: \
mutex_unlock(&opts->lock); \
return ret; \
} \
\
static struct f_midi_opts_attribute f_midi_opts_##name = \
__CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, f_midi_opts_##name##_show, \
f_midi_opts_##name##_store)
F_MIDI_OPT(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 f_midi_opts *opts, char *page)
{
int result;
mutex_lock(&opts->lock);
result = strlcpy(page, opts->id, PAGE_SIZE);
mutex_unlock(&opts->lock);
return result;
}
static ssize_t f_midi_opts_id_store(struct f_midi_opts *opts,
const char *page, size_t len)
{
int ret;
char *c;
mutex_lock(&opts->lock);
if (opts->refcnt) {
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;
}
static struct f_midi_opts_attribute f_midi_opts_id =
__CONFIGFS_ATTR(id, S_IRUGO | S_IWUSR, f_midi_opts_id_show,
f_midi_opts_id_store);
static struct configfs_attribute *midi_attrs[] = {
&f_midi_opts_index.attr,
&f_midi_opts_buflen.attr,
&f_midi_opts_qlen.attr,
&f_midi_opts_in_ports.attr,
&f_midi_opts_out_ports.attr,
&f_midi_opts_id.attr,
NULL,
};
static struct config_item_type midi_func_type = {
.ct_item_ops = &midi_item_ops,
.ct_attrs = midi_attrs,
.ct_owner = THIS_MODULE,
};
static void f_midi_free_inst(struct usb_function_instance *f) static void f_midi_free_inst(struct usb_function_instance *f)
{ {
struct f_midi_opts *opts; struct f_midi_opts *opts;
opts = container_of(f, struct f_midi_opts, func_inst); opts = container_of(f, struct f_midi_opts, func_inst);
if (opts->id_allocated)
kfree(opts->id);
kfree(opts); kfree(opts);
} }
...@@ -912,7 +1045,18 @@ static struct usb_function_instance *f_midi_alloc_inst(void) ...@@ -912,7 +1045,18 @@ static struct usb_function_instance *f_midi_alloc_inst(void)
opts = kzalloc(sizeof(*opts), GFP_KERNEL); opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts) if (!opts)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
mutex_init(&opts->lock);
opts->func_inst.free_func_inst = f_midi_free_inst; opts->func_inst.free_func_inst = f_midi_free_inst;
opts->index = SNDRV_DEFAULT_IDX1;
opts->id = SNDRV_DEFAULT_STR1;
opts->buflen = 256;
opts->qlen = 32;
opts->in_ports = 1;
opts->out_ports = 1;
config_group_init_type_name(&opts->func_inst.group, "",
&midi_func_type);
return &opts->func_inst; return &opts->func_inst;
} }
...@@ -926,9 +1070,12 @@ static void f_midi_free(struct usb_function *f) ...@@ -926,9 +1070,12 @@ static void f_midi_free(struct usb_function *f)
midi = func_to_midi(f); midi = func_to_midi(f);
opts = container_of(f->fi, struct f_midi_opts, func_inst); opts = container_of(f->fi, struct f_midi_opts, func_inst);
kfree(midi->id); kfree(midi->id);
mutex_lock(&opts->lock);
for (i = opts->in_ports - 1; i >= 0; --i) for (i = opts->in_ports - 1; i >= 0; --i)
kfree(midi->in_port[i]); kfree(midi->in_port[i]);
kfree(midi); kfree(midi);
--opts->refcnt;
mutex_unlock(&opts->lock);
} }
static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f) static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f)
...@@ -957,20 +1104,27 @@ struct usb_function *f_midi_alloc(struct usb_function_instance *fi) ...@@ -957,20 +1104,27 @@ struct usb_function *f_midi_alloc(struct usb_function_instance *fi)
int status, i; int status, i;
opts = container_of(fi, struct f_midi_opts, func_inst); opts = container_of(fi, struct f_midi_opts, func_inst);
mutex_lock(&opts->lock);
/* sanity check */ /* sanity check */
if (opts->in_ports > MAX_PORTS || opts->out_ports > MAX_PORTS) if (opts->in_ports > MAX_PORTS || opts->out_ports > MAX_PORTS) {
mutex_unlock(&opts->lock);
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
}
/* allocate and initialize one new instance */ /* allocate and initialize one new instance */
midi = kzalloc(sizeof(*midi), GFP_KERNEL); midi = kzalloc(sizeof(*midi), GFP_KERNEL);
if (!midi) if (!midi) {
mutex_unlock(&opts->lock);
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
}
for (i = 0; i < opts->in_ports; i++) { for (i = 0; i < opts->in_ports; i++) {
struct gmidi_in_port *port = kzalloc(sizeof(*port), GFP_KERNEL); struct gmidi_in_port *port = kzalloc(sizeof(*port), GFP_KERNEL);
if (!port) { if (!port) {
status = -ENOMEM; status = -ENOMEM;
mutex_unlock(&opts->lock);
goto setup_fail; goto setup_fail;
} }
...@@ -984,6 +1138,7 @@ struct usb_function *f_midi_alloc(struct usb_function_instance *fi) ...@@ -984,6 +1138,7 @@ struct usb_function *f_midi_alloc(struct usb_function_instance *fi)
midi->id = kstrdup(opts->id, GFP_KERNEL); midi->id = kstrdup(opts->id, GFP_KERNEL);
if (opts->id && !midi->id) { if (opts->id && !midi->id) {
status = -ENOMEM; status = -ENOMEM;
mutex_unlock(&opts->lock);
goto kstrdup_fail; goto kstrdup_fail;
} }
midi->in_ports = opts->in_ports; midi->in_ports = opts->in_ports;
...@@ -991,6 +1146,8 @@ struct usb_function *f_midi_alloc(struct usb_function_instance *fi) ...@@ -991,6 +1146,8 @@ struct usb_function *f_midi_alloc(struct usb_function_instance *fi)
midi->index = opts->index; midi->index = opts->index;
midi->buflen = opts->buflen; midi->buflen = opts->buflen;
midi->qlen = opts->qlen; midi->qlen = opts->qlen;
++opts->refcnt;
mutex_unlock(&opts->lock);
midi->func.name = "gmidi function"; midi->func.name = "gmidi function";
midi->func.bind = f_midi_bind; midi->func.bind = f_midi_bind;
......
...@@ -22,10 +22,18 @@ struct f_midi_opts { ...@@ -22,10 +22,18 @@ struct f_midi_opts {
struct usb_function_instance func_inst; struct usb_function_instance func_inst;
int index; int index;
char *id; char *id;
bool id_allocated;
unsigned int in_ports; unsigned int in_ports;
unsigned int out_ports; unsigned int out_ports;
unsigned int buflen; unsigned int buflen;
unsigned int qlen; unsigned int qlen;
/*
* Protect the data form concurrent access by read/write
* and create symlink/remove symlink.
*/
struct mutex lock;
int refcnt;
}; };
#endif /* U_MIDI_H */ #endif /* U_MIDI_H */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment