Commit 74203de0 authored by Daniel Mack's avatar Daniel Mack Committed by Greg Kroah-Hartman

usb: gadget: fix MIDI gadget jack allocation

The dynamic jack allocation of the MIDI gadget currently links all
external jacks to one single instance of an embedded jack. According to
the spec, this is only valid if these streams always carry the same data
stream, as described in the USB MIDI 1.0 spec, chapter 3.3.1.

Also, genius Windows 7(tm) terminates it's life cycle instantly with a
blue screen of death once a device with more than one input and output
port with the current implementation is connected.

While at it, and because it grew again by this change, allocate the
temporary function pointer list on the heap, not on the stack.
Signed-off-by: default avatarDaniel Mack <zonque@gmail.com>
Signed-off-by: default avatarFelipe Balbi <balbi@ti.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent ce7b6121
...@@ -95,7 +95,6 @@ static void f_midi_transmit(struct f_midi *midi, struct usb_request *req); ...@@ -95,7 +95,6 @@ static void f_midi_transmit(struct f_midi *midi, struct usb_request *req);
DECLARE_UAC_AC_HEADER_DESCRIPTOR(1); DECLARE_UAC_AC_HEADER_DESCRIPTOR(1);
DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1); DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);
DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(16);
DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(16); DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(16);
/* B.3.1 Standard AC Interface Descriptor */ /* B.3.1 Standard AC Interface Descriptor */
...@@ -140,26 +139,6 @@ static struct usb_ms_header_descriptor ms_header_desc __initdata = { ...@@ -140,26 +139,6 @@ static struct usb_ms_header_descriptor ms_header_desc __initdata = {
/* .wTotalLength = DYNAMIC */ /* .wTotalLength = DYNAMIC */
}; };
/* B.4.3 Embedded MIDI IN Jack Descriptor */
static struct usb_midi_in_jack_descriptor jack_in_emb_desc = {
.bLength = USB_DT_MIDI_IN_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = USB_MS_MIDI_IN_JACK,
.bJackType = USB_MS_EMBEDDED,
/* .bJackID = DYNAMIC */
};
/* B.4.4 Embedded MIDI OUT Jack Descriptor */
static struct usb_midi_out_jack_descriptor_16 jack_out_emb_desc = {
/* .bLength = DYNAMIC */
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = USB_MS_MIDI_OUT_JACK,
.bJackType = USB_MS_EMBEDDED,
/* .bJackID = DYNAMIC */
/* .bNrInputPins = DYNAMIC */
/* .pins = DYNAMIC */
};
/* B.5.1 Standard Bulk OUT Endpoint Descriptor */ /* B.5.1 Standard Bulk OUT Endpoint Descriptor */
static struct usb_endpoint_descriptor bulk_out_desc = { static struct usb_endpoint_descriptor bulk_out_desc = {
.bLength = USB_DT_ENDPOINT_AUDIO_SIZE, .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
...@@ -758,9 +737,11 @@ static int f_midi_register_card(struct f_midi *midi) ...@@ -758,9 +737,11 @@ static int f_midi_register_card(struct f_midi *midi)
static int __init static int __init
f_midi_bind(struct usb_configuration *c, struct usb_function *f) f_midi_bind(struct usb_configuration *c, struct usb_function *f)
{ {
struct usb_descriptor_header *midi_function[(MAX_PORTS * 2) + 12]; struct usb_descriptor_header **midi_function;
struct usb_midi_in_jack_descriptor jack_in_ext_desc[MAX_PORTS]; struct usb_midi_in_jack_descriptor jack_in_ext_desc[MAX_PORTS];
struct usb_midi_in_jack_descriptor jack_in_emb_desc[MAX_PORTS];
struct usb_midi_out_jack_descriptor_1 jack_out_ext_desc[MAX_PORTS]; struct usb_midi_out_jack_descriptor_1 jack_out_ext_desc[MAX_PORTS];
struct usb_midi_out_jack_descriptor_1 jack_out_emb_desc[MAX_PORTS];
struct usb_composite_dev *cdev = c->cdev; struct usb_composite_dev *cdev = c->cdev;
struct f_midi *midi = func_to_midi(f); struct f_midi *midi = func_to_midi(f);
int status, n, jack = 1, i = 0; int status, n, jack = 1, i = 0;
...@@ -798,6 +779,14 @@ f_midi_bind(struct usb_configuration *c, struct usb_function *f) ...@@ -798,6 +779,14 @@ f_midi_bind(struct usb_configuration *c, struct usb_function *f)
goto fail; goto fail;
midi->out_ep->driver_data = cdev; /* claim */ midi->out_ep->driver_data = cdev; /* claim */
/* allocate temporary function list */
midi_function = kcalloc((MAX_PORTS * 4) + 9, sizeof(midi_function),
GFP_KERNEL);
if (!midi_function) {
status = -ENOMEM;
goto fail;
}
/* /*
* construct the function's descriptor set. As the number of * construct the function's descriptor set. As the number of
* input and output MIDI ports is configurable, we have to do * input and output MIDI ports is configurable, we have to do
...@@ -811,73 +800,74 @@ f_midi_bind(struct usb_configuration *c, struct usb_function *f) ...@@ -811,73 +800,74 @@ f_midi_bind(struct usb_configuration *c, struct usb_function *f)
/* calculate the header's wTotalLength */ /* calculate the header's wTotalLength */
n = USB_DT_MS_HEADER_SIZE n = USB_DT_MS_HEADER_SIZE
+ (1 + midi->in_ports) * USB_DT_MIDI_IN_SIZE + (midi->in_ports + midi->out_ports) *
+ (1 + midi->out_ports) * USB_DT_MIDI_OUT_SIZE(1); (USB_DT_MIDI_IN_SIZE + USB_DT_MIDI_OUT_SIZE(1));
ms_header_desc.wTotalLength = cpu_to_le16(n); ms_header_desc.wTotalLength = cpu_to_le16(n);
midi_function[i++] = (struct usb_descriptor_header *) &ms_header_desc; midi_function[i++] = (struct usb_descriptor_header *) &ms_header_desc;
/* we have one embedded IN jack */ /* configure the external IN jacks, each linked to an embedded OUT jack */
jack_in_emb_desc.bJackID = jack++;
midi_function[i++] = (struct usb_descriptor_header *) &jack_in_emb_desc;
/* and a dynamic amount of external IN jacks */
for (n = 0; n < midi->in_ports; n++) {
struct usb_midi_in_jack_descriptor *ext = &jack_in_ext_desc[n];
ext->bLength = USB_DT_MIDI_IN_SIZE;
ext->bDescriptorType = USB_DT_CS_INTERFACE;
ext->bDescriptorSubtype = USB_MS_MIDI_IN_JACK;
ext->bJackType = USB_MS_EXTERNAL;
ext->bJackID = jack++;
ext->iJack = 0;
midi_function[i++] = (struct usb_descriptor_header *) ext;
}
/* one embedded OUT jack ... */
jack_out_emb_desc.bLength = USB_DT_MIDI_OUT_SIZE(midi->in_ports);
jack_out_emb_desc.bJackID = jack++;
jack_out_emb_desc.bNrInputPins = midi->in_ports;
/* ... which referencess all external IN jacks */
for (n = 0; n < midi->in_ports; n++) { for (n = 0; n < midi->in_ports; n++) {
jack_out_emb_desc.pins[n].baSourceID = jack_in_ext_desc[n].bJackID; struct usb_midi_in_jack_descriptor *in_ext = &jack_in_ext_desc[n];
jack_out_emb_desc.pins[n].baSourcePin = 1; struct usb_midi_out_jack_descriptor_1 *out_emb = &jack_out_emb_desc[n];
in_ext->bLength = USB_DT_MIDI_IN_SIZE;
in_ext->bDescriptorType = USB_DT_CS_INTERFACE;
in_ext->bDescriptorSubtype = USB_MS_MIDI_IN_JACK;
in_ext->bJackType = USB_MS_EXTERNAL;
in_ext->bJackID = jack++;
in_ext->iJack = 0;
midi_function[i++] = (struct usb_descriptor_header *) in_ext;
out_emb->bLength = USB_DT_MIDI_OUT_SIZE(1);
out_emb->bDescriptorType = USB_DT_CS_INTERFACE;
out_emb->bDescriptorSubtype = USB_MS_MIDI_OUT_JACK;
out_emb->bJackType = USB_MS_EMBEDDED;
out_emb->bJackID = jack++;
out_emb->bNrInputPins = 1;
out_emb->pins[0].baSourcePin = 1;
out_emb->pins[0].baSourceID = in_ext->bJackID;
out_emb->iJack = 0;
midi_function[i++] = (struct usb_descriptor_header *) out_emb;
/* link it to the endpoint */
ms_in_desc.baAssocJackID[n] = out_emb->bJackID;
} }
midi_function[i++] = (struct usb_descriptor_header *) &jack_out_emb_desc; /* configure the external OUT jacks, each linked to an embedded IN jack */
/* and multiple external OUT jacks ... */
for (n = 0; n < midi->out_ports; n++) { for (n = 0; n < midi->out_ports; n++) {
struct usb_midi_out_jack_descriptor_1 *ext = &jack_out_ext_desc[n]; struct usb_midi_in_jack_descriptor *in_emb = &jack_in_emb_desc[n];
int m; struct usb_midi_out_jack_descriptor_1 *out_ext = &jack_out_ext_desc[n];
ext->bLength = USB_DT_MIDI_OUT_SIZE(1); in_emb->bLength = USB_DT_MIDI_IN_SIZE;
ext->bDescriptorType = USB_DT_CS_INTERFACE; in_emb->bDescriptorType = USB_DT_CS_INTERFACE;
ext->bDescriptorSubtype = USB_MS_MIDI_OUT_JACK; in_emb->bDescriptorSubtype = USB_MS_MIDI_IN_JACK;
ext->bJackType = USB_MS_EXTERNAL; in_emb->bJackType = USB_MS_EMBEDDED;
ext->bJackID = jack++; in_emb->bJackID = jack++;
ext->bNrInputPins = 1; in_emb->iJack = 0;
ext->iJack = 0; midi_function[i++] = (struct usb_descriptor_header *) in_emb;
/* ... which all reference the same embedded IN jack */
for (m = 0; m < midi->out_ports; m++) { out_ext->bLength = USB_DT_MIDI_OUT_SIZE(1);
ext->pins[m].baSourceID = jack_in_emb_desc.bJackID; out_ext->bDescriptorType = USB_DT_CS_INTERFACE;
ext->pins[m].baSourcePin = 1; out_ext->bDescriptorSubtype = USB_MS_MIDI_OUT_JACK;
} out_ext->bJackType = USB_MS_EXTERNAL;
out_ext->bJackID = jack++;
midi_function[i++] = (struct usb_descriptor_header *) ext; out_ext->bNrInputPins = 1;
out_ext->iJack = 0;
out_ext->pins[0].baSourceID = in_emb->bJackID;
out_ext->pins[0].baSourcePin = 1;
midi_function[i++] = (struct usb_descriptor_header *) out_ext;
/* link it to the endpoint */
ms_out_desc.baAssocJackID[n] = in_emb->bJackID;
} }
/* configure the endpoint descriptors ... */ /* configure the endpoint descriptors ... */
ms_out_desc.bLength = USB_DT_MS_ENDPOINT_SIZE(midi->in_ports); ms_out_desc.bLength = USB_DT_MS_ENDPOINT_SIZE(midi->in_ports);
ms_out_desc.bNumEmbMIDIJack = midi->in_ports; ms_out_desc.bNumEmbMIDIJack = midi->in_ports;
for (n = 0; n < midi->in_ports; n++)
ms_out_desc.baAssocJackID[n] = jack_in_emb_desc.bJackID;
ms_in_desc.bLength = USB_DT_MS_ENDPOINT_SIZE(midi->out_ports); ms_in_desc.bLength = USB_DT_MS_ENDPOINT_SIZE(midi->out_ports);
ms_in_desc.bNumEmbMIDIJack = midi->out_ports; ms_in_desc.bNumEmbMIDIJack = midi->out_ports;
for (n = 0; n < midi->out_ports; n++)
ms_in_desc.baAssocJackID[n] = jack_out_emb_desc.bJackID;
/* ... and add them to the list */ /* ... and add them to the list */
midi_function[i++] = (struct usb_descriptor_header *) &bulk_out_desc; midi_function[i++] = (struct usb_descriptor_header *) &bulk_out_desc;
...@@ -901,6 +891,8 @@ f_midi_bind(struct usb_configuration *c, struct usb_function *f) ...@@ -901,6 +891,8 @@ f_midi_bind(struct usb_configuration *c, struct usb_function *f)
f->descriptors = usb_copy_descriptors(midi_function); f->descriptors = usb_copy_descriptors(midi_function);
} }
kfree(midi_function);
return 0; return 0;
fail: fail:
......
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