Discussion:
[PATCH 0/7] Equivalent of g_midi with configfs
Andrzej Pietrasiewicz
2014-10-16 10:13:41 UTC
Permalink
This series aims at integrating configfs into hid, the way
it has been done for acm, ncm, ecm, eem, ecm subset, rndis, obex, phonet,
mass_storage, FunctionFS, loopback, sourcesink, uac1, uac2, uvc and hid.
It concludes converting functions explicitly available as f_xyz.c files
to configfs.

Rebased onto Felipe's testing/next.

Since Felipe has closed his tree for 3.18, this is meant for 3.19.

BACKWARD COMPATIBILITY
======================

Please note that the old g_midi.ko is still available and works.

USING THE NEW "GADGET"
======================

Please refer to this post:

http://www.spinics.net/lists/linux-usb/msg76388.html

for general information from Sebastian on how to use configfs-based
gadgets (*).

With configfs the procedure is as follows, compared to the information
mentioned above (*):

instead of mkdir functions/acm.ttyS1 do

mkdir functions/hid.<instance name>

e.g. mkdir functions/midi.usb0.

In the midi.usb0 directory there will be the following attributes:

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

Below is a script which creates a midi gadget:

# modprobe libcomposite
# mount none cfg -t configfs
# mkdir cfg/usb_gadget/g1
# cd cfg/usb_gadget/g1
# mkdir configs/c.1
# mkdir functions/midi.usb0
# mkdir strings/0x409
# mkdir configs/c.1/strings/0x409
# echo 0x0004 > idProduct
# echo 0x17b3 > idVendor
# echo serial > strings/0x409/serialnumber
# echo manufacturer > strings/0x409/manufacturer
# echo MIDI Gadget > strings/0x409/product
# echo "Conf 1" > configs/c.1/strings/0x409/configuration
# echo 120 > configs/c.1/MaxPower
# ln -s functions/midi.usb0 configs/c.1
# echo 12480000.hsotg > UDC

TESTING THE FUNCTION
====================

Run the gadget as above. There are two cases: playing a mid from the gadget to
the host and playing a mid from the host to the gadget.

1) Playing a mid from the gadget to the host
host)

$ arecordmidi -l
Port Client name Port name
14:0 Midi Through Midi Through Port-0
24:0 MIDI Gadget MIDI Gadget MIDI 1
$ arecordmidi -p 24:0 from_gadget.mid

gadget)

$ aplaymidi -l
Port Client name Port name
20:0 f_midi f_midi

$ aplaymidi -p 20:0 to_host.mid

2) Playing a mid from the host to the gadget
gadget)

$ arecordmidi -l
Port Client name Port name
20:0 f_midi f_midi

$ arecordmidi -p 20:0 from_host.mid

host)

$ aplaymidi -l
Port Client name Port name
14:0 Midi Through Midi Through Port-0
24:0 MIDI Gadget MIDI Gadget MIDI 1

$ aplaymidi -p24:0 to_gadget.mid

The from_gadget.mid should sound identical to the to_host.mid.
The from_host.id should sound identical to the to_gadget.mid.

MIDI files can be played to speakers/headphones with e.g. timidity installed

$ aplaymidi -l
Port Client name Port name
14:0 Midi Through Midi Through Port-0
24:0 MIDI Gadget MIDI Gadget MIDI 1
128:0 TiMidity TiMidity port 0
128:1 TiMidity TiMidity port 1
128:2 TiMidity TiMidity port 2
128:3 TiMidity TiMidity port 3

$ aplaymidi -p 128:0 file.mid

MIDI ports can be logically connected using the aconnect utility, e.g.:

$ aconnect 24:0 128:0 # try it on the host

After the gadget's MIDI port is connected to timidity's MIDI port,
whatever is played at the gadget side with aplaymidi -l is audible
in host's speakers/headphones.

Andrzej Pietrasiewicz (7):
usb: gadget: f_midi: enable use of the index parameter
usb: gadget: f_midi: check kstrdup() return value
usb: gadget: f_midi: convert to new function interface with backward
compatibility
usb: gadget: midi: convert to new interface of f_midi
usb: gadget: f_midi: remove compatibility layer
usb: gadget: f_midi: use usb_gstrings_attach
usb: gadget: f_midi: add configfs support

Documentation/ABI/testing/configfs-usb-gadget-midi | 12 +
drivers/usb/gadget/Kconfig | 16 +
drivers/usb/gadget/function/Makefile | 2 +
drivers/usb/gadget/function/f_midi.c | 364 ++++++++++++++++-----
drivers/usb/gadget/function/u_midi.h | 40 +++
drivers/usb/gadget/legacy/Kconfig | 1 +
drivers/usb/gadget/legacy/gmidi.c | 43 ++-
7 files changed, 382 insertions(+), 96 deletions(-)
create mode 100644 Documentation/ABI/testing/configfs-usb-gadget-midi
create mode 100644 drivers/usb/gadget/function/u_midi.h
--
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-***@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Andrzej Pietrasiewicz
2014-10-16 10:13:46 UTC
Permalink
There are no old f_midi interface users left, so remove it.

Signed-off-by: Andrzej Pietrasiewicz <andrzej.p-***@public.gmane.org>
---
drivers/usb/gadget/function/f_midi.c | 122 -----------------------------------
1 file changed, 122 deletions(-)

diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index f3e7d95..5cd77be 100644
--- a/drivers/usb/gadget/function/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
@@ -389,31 +389,6 @@ static void f_midi_disable(struct usb_function *f)
usb_ep_disable(midi->out_ep);
}

-#ifdef USBF_MIDI_INCLUDED
-static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f)
-{
- struct usb_composite_dev *cdev = f->config->cdev;
- struct f_midi *midi = func_to_midi(f);
- struct snd_card *card;
-
- DBG(cdev, "unbind\n");
-
- /* just to be sure */
- f_midi_disable(f);
-
- card = midi->card;
- midi->card = NULL;
- if (card)
- snd_card_free(card);
-
- kfree(midi->id);
- midi->id = NULL;
-
- usb_free_all_descriptors(f);
- kfree(midi);
-}
-#endif
-
static int f_midi_snd_free(struct snd_device *device)
{
return 0;
@@ -744,14 +719,12 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f)
struct f_midi *midi = func_to_midi(f);
int status, n, jack = 1, i = 0;

-#ifndef USBF_MIDI_INCLUDED
midi->gadget = cdev->gadget;
tasklet_init(&midi->tasklet, f_midi_in_tasklet, (unsigned long) midi);
status = f_midi_register_card(midi);
if (status < 0)
goto fail_register;

-#endif
/* maybe allocate device-global string ID */
if (midi_string_defs[0].id == 0) {
status = usb_string_id(c->cdev);
@@ -908,10 +881,8 @@ fail_f_midi:
kfree(midi_function);
usb_free_descriptors(f->hs_descriptors);
fail:
-#ifndef USBF_MIDI_INCLUDED
f_midi_unregister_card(midi);
fail_register:
-#endif
/* we might as well release our claims on endpoints */
if (midi->out_ep)
midi->out_ep->driver_data = NULL;
@@ -923,98 +894,6 @@ fail_register:
return status;
}

-#ifdef USBF_MIDI_INCLUDED
-/**
- * f_midi_bind_config - add USB MIDI function to a configuration
- * @c: the configuration to supcard the USB audio function
- * @index: the soundcard index to use for the ALSA device creation
- * @id: the soundcard id to use for the ALSA device creation
- * @buflen: the buffer length to use
- * @qlen the number of read requests to pre-allocate
- * Context: single threaded during gadget setup
- *
- * Returns zero on success, else negative errno.
- */
-int __init f_midi_bind_config(struct usb_configuration *c,
- int index, char *id,
- unsigned int in_ports,
- unsigned int out_ports,
- unsigned int buflen,
- unsigned int qlen)
-{
- struct f_midi *midi;
- int status, i;
-
- /* sanity check */
- if (in_ports > MAX_PORTS || out_ports > MAX_PORTS)
- return -EINVAL;
-
- /* allocate and initialize one new instance */
- midi = kzalloc(sizeof *midi, GFP_KERNEL);
- if (!midi) {
- status = -ENOMEM;
- goto fail;
- }
-
- for (i = 0; i < in_ports; i++) {
- struct gmidi_in_port *port = kzalloc(sizeof(*port), GFP_KERNEL);
- if (!port) {
- status = -ENOMEM;
- goto setup_fail;
- }
-
- port->midi = midi;
- port->active = 0;
- port->cable = i;
- midi->in_port[i] = port;
- }
-
- midi->gadget = c->cdev->gadget;
- tasklet_init(&midi->tasklet, f_midi_in_tasklet, (unsigned long) midi);
-
- /* set up ALSA midi devices */
- midi->in_ports = in_ports;
- midi->out_ports = out_ports;
- midi->index = index;
- status = f_midi_register_card(midi);
- if (status < 0)
- goto setup_fail;
-
- midi->func.name = "gmidi function";
- midi->func.strings = midi_strings;
- midi->func.bind = f_midi_bind;
- midi->func.unbind = f_midi_unbind;
- midi->func.set_alt = f_midi_set_alt;
- midi->func.disable = f_midi_disable;
-
- midi->id = kstrdup(id, GFP_KERNEL);
- if (id && !midi->id) {
- status = -ENOMEM;
- goto kstrdup_fail;
- }
- midi->buflen = buflen;
- midi->qlen = qlen;
-
- status = usb_add_function(c, &midi->func);
- if (status)
- goto add_fail;
-
- return 0;
-
-add_fail:
- kfree(midi->id);
-kstrdup_fail:
- f_midi_unregister_card(midi);
-setup_fail:
- for (--i; i >= 0; i--)
- kfree(midi->in_port[i]);
- kfree(midi);
-fail:
- return status;
-}
-
-#else
Andrzej Pietrasiewicz
2014-10-16 10:13:44 UTC
Permalink
Converting midi to the new function interface requires converting
the USB midi's function code and its users.

This patch converts the f_midi.c to the new function interface.
The file can now be compiled into a separate usb_f_midi.ko module.

The old function interface is provided by means of a preprocessor
conditional directives. After all users are converted, the old interface
can be removed.

Signed-off-by: Andrzej Pietrasiewicz <andrzej.p-***@public.gmane.org>
---
drivers/usb/gadget/Kconfig | 3 +
drivers/usb/gadget/function/Makefile | 2 +
drivers/usb/gadget/function/f_midi.c | 147 +++++++++++++++++++++++++++++++++--
drivers/usb/gadget/function/u_midi.h | 32 ++++++++
drivers/usb/gadget/legacy/gmidi.c | 1 +
5 files changed, 179 insertions(+), 6 deletions(-)
create mode 100644 drivers/usb/gadget/function/u_midi.h

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index c4880fc..9815237 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -190,6 +190,9 @@ config USB_F_UAC2
config USB_F_UVC
tristate

+config USB_F_MIDI
+ tristate
+
choice
tristate "USB Gadget Drivers"
default USB_ETH
diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile
index 90701aa..576eea5 100644
--- a/drivers/usb/gadget/function/Makefile
+++ b/drivers/usb/gadget/function/Makefile
@@ -38,3 +38,5 @@ usb_f_uac2-y := f_uac2.o
obj-$(CONFIG_USB_F_UAC2) += usb_f_uac2.o
usb_f_uvc-y := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o
obj-$(CONFIG_USB_F_UVC) += usb_f_uvc.o
+usb_f_midi-y := f_midi.o
+obj-$(CONFIG_USB_F_MIDI) += usb_f_midi.o
diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index a920eee..f3e7d95 100644
--- a/drivers/usb/gadget/function/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
@@ -20,6 +20,7 @@
*/

#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/slab.h>
#include <linux/device.h>

@@ -33,6 +34,7 @@
#include <linux/usb/midi.h>

#include "u_f.h"
+#include "u_midi.h"

MODULE_AUTHOR("Ben Williamson");
MODULE_LICENSE("GPL v2");
@@ -99,7 +101,7 @@ DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);
DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(16);

/* B.3.1 Standard AC Interface Descriptor */
-static struct usb_interface_descriptor ac_interface_desc __initdata = {
+static struct usb_interface_descriptor ac_interface_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC */
@@ -110,7 +112,7 @@ static struct usb_interface_descriptor ac_interface_desc __initdata = {
};

/* B.3.2 Class-Specific AC Interface Descriptor */
-static struct uac1_ac_header_descriptor_1 ac_header_desc __initdata = {
+static struct uac1_ac_header_descriptor_1 ac_header_desc = {
.bLength = UAC_DT_AC_HEADER_SIZE(1),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = USB_MS_HEADER,
@@ -121,7 +123,7 @@ static struct uac1_ac_header_descriptor_1 ac_header_desc __initdata = {
};

/* B.4.1 Standard MS Interface Descriptor */
-static struct usb_interface_descriptor ms_interface_desc __initdata = {
+static struct usb_interface_descriptor ms_interface_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC */
@@ -132,7 +134,7 @@ static struct usb_interface_descriptor ms_interface_desc __initdata = {
};

/* B.4.2 Class-Specific MS Interface Descriptor */
-static struct usb_ms_header_descriptor ms_header_desc __initdata = {
+static struct usb_ms_header_descriptor ms_header_desc = {
.bLength = USB_DT_MS_HEADER_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = USB_MS_HEADER,
@@ -387,6 +389,7 @@ static void f_midi_disable(struct usb_function *f)
usb_ep_disable(midi->out_ep);
}

+#ifdef USBF_MIDI_INCLUDED
static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = f->config->cdev;
@@ -409,6 +412,7 @@ static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f)
usb_free_all_descriptors(f);
kfree(midi);
}
+#endif

static int f_midi_snd_free(struct snd_device *device)
{
@@ -729,8 +733,7 @@ fail:

/* MIDI function driver setup/binding */

-static int __init
-f_midi_bind(struct usb_configuration *c, struct usb_function *f)
+static int f_midi_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_descriptor_header **midi_function;
struct usb_midi_in_jack_descriptor jack_in_ext_desc[MAX_PORTS];
@@ -741,6 +744,14 @@ f_midi_bind(struct usb_configuration *c, struct usb_function *f)
struct f_midi *midi = func_to_midi(f);
int status, n, jack = 1, i = 0;

+#ifndef USBF_MIDI_INCLUDED
+ midi->gadget = cdev->gadget;
+ tasklet_init(&midi->tasklet, f_midi_in_tasklet, (unsigned long) midi);
+ status = f_midi_register_card(midi);
+ if (status < 0)
+ goto fail_register;
+
+#endif
/* maybe allocate device-global string ID */
if (midi_string_defs[0].id == 0) {
status = usb_string_id(c->cdev);
@@ -897,6 +908,10 @@ fail_f_midi:
kfree(midi_function);
usb_free_descriptors(f->hs_descriptors);
fail:
+#ifndef USBF_MIDI_INCLUDED
+ f_midi_unregister_card(midi);
+fail_register:
+#endif
/* we might as well release our claims on endpoints */
if (midi->out_ep)
midi->out_ep->driver_data = NULL;
@@ -908,6 +923,7 @@ fail:
return status;
}

+#ifdef USBF_MIDI_INCLUDED
/**
* f_midi_bind_config - add USB MIDI function to a configuration
* @c: the configuration to supcard the USB audio function
@@ -997,3 +1013,122 @@ fail:
return status;
}

+#else
+
+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);
+
+ kfree(opts);
+}
+
+static struct usb_function_instance *f_midi_alloc_inst(void)
+{
+ struct f_midi_opts *opts;
+
+ opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ if (!opts)
+ return ERR_PTR(-ENOMEM);
+ opts->func_inst.free_func_inst = f_midi_free_inst;
+
+ return &opts->func_inst;
+}
+
+static void f_midi_free(struct usb_function *f)
+{
+ struct f_midi *midi;
+ struct f_midi_opts *opts;
+ int i;
+
+ midi = func_to_midi(f);
+ opts = container_of(f->fi, struct f_midi_opts, func_inst);
+ kfree(midi->id);
+ for (i = opts->in_ports - 1; i >= 0; --i)
+ kfree(midi->in_port[i]);
+ kfree(midi);
+}
+
+static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = f->config->cdev;
+ struct f_midi *midi = func_to_midi(f);
+ struct snd_card *card;
+
+ DBG(cdev, "unbind\n");
+
+ /* just to be sure */
+ f_midi_disable(f);
+
+ card = midi->card;
+ midi->card = NULL;
+ if (card)
+ snd_card_free(card);
+
+ usb_free_all_descriptors(f);
+}
+
+struct usb_function *f_midi_alloc(struct usb_function_instance *fi)
+{
+ struct f_midi *midi;
+ struct f_midi_opts *opts;
+ int status, i;
+
+ opts = container_of(fi, struct f_midi_opts, func_inst);
+ /* sanity check */
+ if (opts->in_ports > MAX_PORTS || opts->out_ports > MAX_PORTS)
+ return ERR_PTR(-EINVAL);
+
+ /* allocate and initialize one new instance */
+ midi = kzalloc(sizeof(*midi), GFP_KERNEL);
+ if (!midi)
+ 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;
+ goto setup_fail;
+ }
+
+ port->midi = midi;
+ port->active = 0;
+ port->cable = i;
+ midi->in_port[i] = port;
+ }
+
+ /* set up ALSA midi devices */
+ midi->id = kstrdup(opts->id, GFP_KERNEL);
+ if (opts->id && !midi->id) {
+ status = -ENOMEM;
+ goto kstrdup_fail;
+ }
+ midi->in_ports = opts->in_ports;
+ midi->out_ports = opts->out_ports;
+ midi->index = opts->index;
+ midi->buflen = opts->buflen;
+ midi->qlen = opts->qlen;
+
+ midi->func.name = "gmidi function";
+ midi->func.strings = midi_strings;
+ midi->func.bind = f_midi_bind;
+ midi->func.unbind = f_midi_unbind;
+ midi->func.set_alt = f_midi_set_alt;
+ midi->func.disable = f_midi_disable;
+ midi->func.free_func = f_midi_free;
+
+ return &midi->func;
+
+kstrdup_fail:
+ f_midi_unregister_card(midi);
+setup_fail:
+ for (--i; i >= 0; i--)
+ kfree(midi->in_port[i]);
+ kfree(midi);
+ return ERR_PTR(status);
+}
+
+DECLARE_USB_FUNCTION_INIT(midi, f_midi_alloc_inst, f_midi_alloc);
+#endif
diff --git a/drivers/usb/gadget/function/u_midi.h b/drivers/usb/gadget/function/u_midi.h
new file mode 100644
index 0000000..76bccc1
--- /dev/null
+++ b/drivers/usb/gadget/function/u_midi.h
@@ -0,0 +1,32 @@
+/*
+ * u_midi.h
+ *
+ * Utility definitions for the midi function
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p-***@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef U_MIDI_H
+#define U_MIDI_H
+
+#include <linux/usb/composite.h>
+
+struct f_midi_opts {
+ struct usb_function_instance func_inst;
+ int index;
+ char *id;
+ unsigned int in_ports;
+ unsigned int out_ports;
+ unsigned int buflen;
+ unsigned int qlen;
+};
+
+#endif /* U_MIDI_H */
+
diff --git a/drivers/usb/gadget/legacy/gmidi.c b/drivers/usb/gadget/legacy/gmidi.c
index 3d696b8..f704c55 100644
--- a/drivers/usb/gadget/legacy/gmidi.c
+++ b/drivers/usb/gadget/legacy/gmidi.c
@@ -37,6 +37,7 @@

#include "gadget_chips.h"

+#define USBF_MIDI_INCLUDED
#include "f_midi.c"

/*-------------------------------------------------------------------------*/
--
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-***@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Andrzej Pietrasiewicz
2014-10-16 10:13:48 UTC
Permalink
Make the midi function available for gadgets composed with configfs.

Signed-off-by: Andrzej Pietrasiewicz <andrzej.p-***@public.gmane.org>
---
Documentation/ABI/testing/configfs-usb-gadget-midi | 12 ++
drivers/usb/gadget/Kconfig | 13 ++
drivers/usb/gadget/function/f_midi.c | 161 ++++++++++++++++++++-
drivers/usb/gadget/function/u_midi.h | 8 +
4 files changed, 192 insertions(+), 2 deletions(-)
create mode 100644 Documentation/ABI/testing/configfs-usb-gadget-midi

diff --git a/Documentation/ABI/testing/configfs-usb-gadget-midi b/Documentation/ABI/testing/configfs-usb-gadget-midi
new file mode 100644
index 0000000..6b341df
--- /dev/null
+++ b/Documentation/ABI/testing/configfs-usb-gadget-midi
@@ -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
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 9815237..e283046 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -365,6 +365,19 @@ config USB_CONFIGFS_F_FS
implemented in kernel space (for instance Ethernet, serial or
mass storage) and other are implemented in user space.

+config USB_CONFIGFS_F_MIDI
+ boolean "MIDI function"
+ 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
diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index ec2a9ce..1f94dad 100644
--- a/drivers/usb/gadget/function/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
@@ -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;
diff --git a/drivers/usb/gadget/function/u_midi.h b/drivers/usb/gadget/function/u_midi.h
index 76bccc1..2251018 100644
--- a/drivers/usb/gadget/function/u_midi.h
+++ b/drivers/usb/gadget/function/u_midi.h
@@ -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 */
--
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-***@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Andrzej Pietrasiewicz
2014-10-16 10:13:43 UTC
Permalink
kstrdup() might fail, so check its return value and react appropriately.

Signed-off-by: Andrzej Pietrasiewicz <andrzej.p-***@public.gmane.org>
---
drivers/usb/gadget/function/f_midi.c | 23 ++++++++++++++++++-----
1 file changed, 18 insertions(+), 5 deletions(-)

diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index bf32957..a920eee 100644
--- a/drivers/usb/gadget/function/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
@@ -654,6 +654,14 @@ static struct snd_rawmidi_ops gmidi_out_ops = {
.trigger = f_midi_out_trigger
};

+static inline void f_midi_unregister_card(struct f_midi *midi)
+{
+ if (midi->card) {
+ snd_card_free(midi->card);
+ midi->card = NULL;
+ }
+}
+
/* register as a sound "card" */
static int f_midi_register_card(struct f_midi *midi)
{
@@ -715,10 +723,7 @@ static int f_midi_register_card(struct f_midi *midi)
return 0;

fail:
- if (midi->card) {
- snd_card_free(midi->card);
- midi->card = NULL;
- }
+ f_midi_unregister_card(midi);
return err;
}

@@ -967,15 +972,23 @@ int __init f_midi_bind_config(struct usb_configuration *c,
midi->func.disable = f_midi_disable;

midi->id = kstrdup(id, GFP_KERNEL);
+ if (id && !midi->id) {
+ status = -ENOMEM;
+ goto kstrdup_fail;
+ }
midi->buflen = buflen;
midi->qlen = qlen;

status = usb_add_function(c, &midi->func);
if (status)
- goto setup_fail;
+ goto add_fail;

return 0;

+add_fail:
+ kfree(midi->id);
+kstrdup_fail:
+ f_midi_unregister_card(midi);
setup_fail:
for (--i; i >= 0; i--)
kfree(midi->in_port[i]);
--
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-***@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Andrzej Pietrasiewicz
2014-10-16 10:13:45 UTC
Permalink
Use the new f_midi interface so that the old can be removed.

Signed-off-by: Andrzej Pietrasiewicz <andrzej.p-***@public.gmane.org>
---
drivers/usb/gadget/legacy/Kconfig | 1 +
drivers/usb/gadget/legacy/gmidi.c | 44 ++++++++++++++++++++++++++++++++-------
2 files changed, 38 insertions(+), 7 deletions(-)

diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig
index 24392d2..8011b19 100644
--- a/drivers/usb/gadget/legacy/Kconfig
+++ b/drivers/usb/gadget/legacy/Kconfig
@@ -287,6 +287,7 @@ config USB_MIDI_GADGET
depends on SND
select USB_LIBCOMPOSITE
select SND_RAWMIDI
+ select USB_F_MIDI
help
The MIDI Gadget acts as a USB Audio device, with one MIDI
input and one MIDI output. These MIDI jacks appear as
diff --git a/drivers/usb/gadget/legacy/gmidi.c b/drivers/usb/gadget/legacy/gmidi.c
index f704c55..0d01e46 100644
--- a/drivers/usb/gadget/legacy/gmidi.c
+++ b/drivers/usb/gadget/legacy/gmidi.c
@@ -37,8 +37,7 @@

#include "gadget_chips.h"

-#define USBF_MIDI_INCLUDED
-#include "f_midi.c"
+#include "u_midi.h"

/*-------------------------------------------------------------------------*/

@@ -116,8 +115,13 @@ static struct usb_gadget_strings *dev_strings[] = {
NULL,
};

+struct usb_function_instance *fi_midi;
+struct usb_function *f_midi;
+
static int __exit midi_unbind(struct usb_composite_dev *dev)
{
+ usb_put_function(f_midi);
+ usb_put_function_instance(fi_midi);
return 0;
}

@@ -131,28 +135,54 @@ static struct usb_configuration midi_config = {

static int __init midi_bind_config(struct usb_configuration *c)
{
- return f_midi_bind_config(c, index, id,
- in_ports, out_ports,
- buflen, qlen);
+ int status;
+
+ f_midi = usb_get_function(fi_midi);
+ if (IS_ERR(f_midi))
+ return PTR_ERR(f_midi);
+
+ status = usb_add_function(c, f_midi);
+ if (status < 0) {
+ usb_put_function(f_midi);
+ return status;
+ }
+
+ return 0;
}

static int __init midi_bind(struct usb_composite_dev *cdev)
{
+ struct f_midi_opts *midi_opts;
int status;

+ fi_midi = usb_get_function_instance("midi");
+ if (IS_ERR(fi_midi))
+ return PTR_ERR(fi_midi);
+
+ midi_opts = container_of(fi_midi, struct f_midi_opts, func_inst);
+ midi_opts->index = index;
+ midi_opts->id = id;
+ midi_opts->in_ports = in_ports;
+ midi_opts->out_ports = out_ports;
+ midi_opts->buflen = buflen;
+ midi_opts->qlen = qlen;
+
status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
- return status;
+ goto put;
device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
midi_config.iConfiguration = strings_dev[STRING_DESCRIPTION_IDX].id;

status = usb_add_config(cdev, &midi_config, midi_bind_config);
if (status < 0)
- return status;
+ goto put;
usb_composite_overwrite_options(cdev, &coverwrite);
pr_info("%s\n", longname);
return 0;
+put:
+ usb_put_function_instance(fi_midi);
+ return status;
}

static __refdata struct usb_composite_driver midi_driver = {
--
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-***@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Andrzej Pietrasiewicz
2014-10-16 10:13:42 UTC
Permalink
The soundcard index to use for the ALSA device creation is passed as a
parameter to f_midi_bind_config(), but is assigned to midi->index only
after the call to f_midi_register_card(midi). So no matter what is passed
to f_midi_bind_config(), the actual index for snd_card_new() is always 0.
This probably works ok if at the moment of f_midi's bind there are no
other snd_cards, but if there are, it is not possible to bind f_midi.

This patch moves the assignment to a place before the call to
f_midi_register_card(midi).

Signed-off-by: Andrzej Pietrasiewicz <andrzej.p-***@public.gmane.org>
---
drivers/usb/gadget/function/f_midi.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index 807b31c..bf32957 100644
--- a/drivers/usb/gadget/function/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
@@ -954,6 +954,7 @@ int __init f_midi_bind_config(struct usb_configuration *c,
/* set up ALSA midi devices */
midi->in_ports = in_ports;
midi->out_ports = out_ports;
+ midi->index = index;
status = f_midi_register_card(midi);
if (status < 0)
goto setup_fail;
@@ -966,7 +967,6 @@ int __init f_midi_bind_config(struct usb_configuration *c,
midi->func.disable = f_midi_disable;

midi->id = kstrdup(id, GFP_KERNEL);
- midi->index = index;
midi->buflen = buflen;
midi->qlen = qlen;
--
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-***@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Andrzej Pietrasiewicz
2014-10-16 10:13:47 UTC
Permalink
In order to add configfs support the usb_gstrings_attach must be used.

Signed-off-by: Andrzej Pietrasiewicz <andrzej.p-***@public.gmane.org>
---
drivers/usb/gadget/function/f_midi.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index 5cd77be..ec2a9ce 100644
--- a/drivers/usb/gadget/function/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
@@ -717,6 +717,7 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f)
struct usb_midi_out_jack_descriptor_1 jack_out_emb_desc[MAX_PORTS];
struct usb_composite_dev *cdev = c->cdev;
struct f_midi *midi = func_to_midi(f);
+ struct usb_string *us;
int status, n, jack = 1, i = 0;

midi->gadget = cdev->gadget;
@@ -726,12 +727,13 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f)
goto fail_register;

/* maybe allocate device-global string ID */
- if (midi_string_defs[0].id == 0) {
- status = usb_string_id(c->cdev);
- if (status < 0)
- goto fail;
- midi_string_defs[0].id = status;
+ us = usb_gstrings_attach(c->cdev, midi_strings,
+ ARRAY_SIZE(midi_string_defs));
+ if (IS_ERR(us)) {
+ status = PTR_ERR(us);
+ goto fail;
}
+ ac_interface_desc.iInterface = us[STRING_FUNC_IDX].id;

/* We have two interfaces, AudioControl and MIDIStreaming */
status = usb_interface_id(c, f);
@@ -991,7 +993,6 @@ struct usb_function *f_midi_alloc(struct usb_function_instance *fi)
midi->qlen = opts->qlen;

midi->func.name = "gmidi function";
- midi->func.strings = midi_strings;
midi->func.bind = f_midi_bind;
midi->func.unbind = f_midi_unbind;
midi->func.set_alt = f_midi_set_alt;
--
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-***@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Loading...