usb: gadget: f_midi: add configfs support
authorAndrzej Pietrasiewicz <andrzej.p@samsung.com>
Thu, 16 Oct 2014 11:33:31 +0000 (13:33 +0200)
committerFelipe Balbi <balbi@ti.com>
Wed, 5 Nov 2014 19:37:17 +0000 (13:37 -0600)
Make the midi function available for gadgets composed with configfs.

Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
Documentation/ABI/testing/configfs-usb-gadget-midi [new file with mode: 0644]
drivers/usb/gadget/Kconfig
drivers/usb/gadget/function/f_midi.c
drivers/usb/gadget/function/u_midi.h

diff --git a/Documentation/ABI/testing/configfs-usb-gadget-midi b/Documentation/ABI/testing/configfs-usb-gadget-midi
new file mode 100644 (file)
index 0000000..6b341df
--- /dev/null
@@ -0,0 +1,12 @@
+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
index b513078051514551aa114cb12bacb3f361ab7afe..501c2a38d0714a5705f5aa2cf3a11b86e22edf3b 100644 (file)
@@ -396,6 +396,20 @@ config USB_CONFIGFS_F_UAC2
          received from the USB Host and choose to provide whatever it
          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"
 
 endchoice
index ec2a9cee4589980428040bed391d065c6d8eaaf9..1f94dad57307960424a531504e9c84ae01b1f338 100644 (file)
@@ -896,12 +896,145 @@ fail_register:
        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)
 {
        struct f_midi_opts *opts;
 
        opts = container_of(f, struct f_midi_opts, func_inst);
 
+       if (opts->id_allocated)
+               kfree(opts->id);
+
        kfree(opts);
 }
 
@@ -912,7 +1045,18 @@ static struct usb_function_instance *f_midi_alloc_inst(void)
        opts = kzalloc(sizeof(*opts), GFP_KERNEL);
        if (!opts)
                return ERR_PTR(-ENOMEM);
+
+       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->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;
 }
@@ -926,9 +1070,12 @@ static void f_midi_free(struct usb_function *f)
        midi = func_to_midi(f);
        opts = container_of(f->fi, struct f_midi_opts, func_inst);
        kfree(midi->id);
+       mutex_lock(&opts->lock);
        for (i = opts->in_ports - 1; i >= 0; --i)
                kfree(midi->in_port[i]);
        kfree(midi);
+       --opts->refcnt;
+       mutex_unlock(&opts->lock);
 }
 
 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)
        int status, i;
 
        opts = container_of(fi, struct f_midi_opts, func_inst);
+
+       mutex_lock(&opts->lock);
        /* 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);
+       }
 
        /* allocate and initialize one new instance */
        midi = kzalloc(sizeof(*midi), GFP_KERNEL);
-       if (!midi)
+       if (!midi) {
+               mutex_unlock(&opts->lock);
                return ERR_PTR(-ENOMEM);
+       }
 
        for (i = 0; i < opts->in_ports; i++) {
                struct gmidi_in_port *port = kzalloc(sizeof(*port), GFP_KERNEL);
 
                if (!port) {
                        status = -ENOMEM;
+                       mutex_unlock(&opts->lock);
                        goto setup_fail;
                }
 
@@ -984,6 +1138,7 @@ struct usb_function *f_midi_alloc(struct usb_function_instance *fi)
        midi->id = kstrdup(opts->id, GFP_KERNEL);
        if (opts->id && !midi->id) {
                status = -ENOMEM;
+               mutex_unlock(&opts->lock);
                goto kstrdup_fail;
        }
        midi->in_ports = opts->in_ports;
@@ -991,6 +1146,8 @@ struct usb_function *f_midi_alloc(struct usb_function_instance *fi)
        midi->index = opts->index;
        midi->buflen = opts->buflen;
        midi->qlen = opts->qlen;
+       ++opts->refcnt;
+       mutex_unlock(&opts->lock);
 
        midi->func.name         = "gmidi function";
        midi->func.bind         = f_midi_bind;
index 76bccc1fdbb4524e0f47eef9f01abd08c12b3553..22510189758eae2bcd7010a168b22e756ed9f0e9 100644 (file)
@@ -22,10 +22,18 @@ struct f_midi_opts {
        struct usb_function_instance    func_inst;
        int                             index;
        char                            *id;
+       bool                            id_allocated;
        unsigned int                    in_ports;
        unsigned int                    out_ports;
        unsigned int                    buflen;
        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 */