X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=sound%2Fusb%2Fusbmidi.c;h=0eff19ceb7e1f9d74653d80c402d3930a6be8bf8;hb=a65dd997b3cf057f6524466cf8dfb8382c132bd5;hp=5f19b494923e0347d31558b0c91ccd903fabc2d0;hpb=c347e9fca710551f0def6a4d58505a6f4c0d87f6;p=safe%2Fjmp%2Flinux-2.6 diff --git a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c index 5f19b49..0eff19c 100644 --- a/sound/usb/usbmidi.c +++ b/sound/usb/usbmidi.c @@ -1,7 +1,7 @@ /* * usbmidi.c - ALSA USB MIDI driver * - * Copyright (c) 2002-2005 Clemens Ladisch + * Copyright (c) 2002-2007 Clemens Ladisch * All rights reserved. * * Based on the OSS usb-midi driver by NAGANO Daisuke, @@ -35,7 +35,6 @@ * SUCH DAMAGE. */ -#include #include #include #include @@ -44,10 +43,12 @@ #include #include #include +#include #include +#include #include -#include #include +#include #include "usbaudio.h" @@ -56,6 +57,15 @@ */ /* #define DUMP_PACKETS */ +/* + * how long to wait after some USB errors, so that khubd can disconnect() us + * without too many spurious errors + */ +#define ERROR_DELAY_JIFFIES (HZ / 10) + +#define OUTPUT_URBS 7 +#define INPUT_URBS 7 + MODULE_AUTHOR("Clemens Ladisch "); MODULE_DESCRIPTION("USB Audio/MIDI helper module"); @@ -78,48 +88,52 @@ struct usb_ms_endpoint_descriptor { __u8 baAssocJackID[0]; } __attribute__ ((packed)); -typedef struct snd_usb_midi snd_usb_midi_t; -typedef struct snd_usb_midi_endpoint snd_usb_midi_endpoint_t; -typedef struct snd_usb_midi_out_endpoint snd_usb_midi_out_endpoint_t; -typedef struct snd_usb_midi_in_endpoint snd_usb_midi_in_endpoint_t; -typedef struct usbmidi_out_port usbmidi_out_port_t; -typedef struct usbmidi_in_port usbmidi_in_port_t; +struct snd_usb_midi_in_endpoint; +struct snd_usb_midi_out_endpoint; +struct snd_usb_midi_endpoint; struct usb_protocol_ops { - void (*input)(snd_usb_midi_in_endpoint_t*, uint8_t*, int); - void (*output)(snd_usb_midi_out_endpoint_t*); + void (*input)(struct snd_usb_midi_in_endpoint*, uint8_t*, int); + void (*output)(struct snd_usb_midi_out_endpoint *ep, struct urb *urb); void (*output_packet)(struct urb*, uint8_t, uint8_t, uint8_t, uint8_t); - void (*init_out_endpoint)(snd_usb_midi_out_endpoint_t*); - void (*finish_out_endpoint)(snd_usb_midi_out_endpoint_t*); + void (*init_out_endpoint)(struct snd_usb_midi_out_endpoint*); + void (*finish_out_endpoint)(struct snd_usb_midi_out_endpoint*); }; struct snd_usb_midi { - snd_usb_audio_t *chip; + struct snd_usb_audio *chip; struct usb_interface *iface; - const snd_usb_audio_quirk_t *quirk; - snd_rawmidi_t* rmidi; + const struct snd_usb_audio_quirk *quirk; + struct snd_rawmidi *rmidi; struct usb_protocol_ops* usb_protocol_ops; struct list_head list; + struct timer_list error_timer; + spinlock_t disc_lock; struct snd_usb_midi_endpoint { - snd_usb_midi_out_endpoint_t *out; - snd_usb_midi_in_endpoint_t *in; + struct snd_usb_midi_out_endpoint *out; + struct snd_usb_midi_in_endpoint *in; } endpoints[MIDI_MAX_ENDPOINTS]; unsigned long input_triggered; + unsigned char disconnected; }; struct snd_usb_midi_out_endpoint { - snd_usb_midi_t* umidi; - struct urb* urb; - int urb_active; + struct snd_usb_midi* umidi; + struct out_urb_context { + struct urb *urb; + struct snd_usb_midi_out_endpoint *ep; + } urbs[OUTPUT_URBS]; + unsigned int active_urbs; + unsigned int drain_urbs; int max_transfer; /* size of urb buffer */ struct tasklet_struct tasklet; - + unsigned int next_urb; spinlock_t buffer_lock; struct usbmidi_out_port { - snd_usb_midi_out_endpoint_t* ep; - snd_rawmidi_substream_t* substream; + struct snd_usb_midi_out_endpoint* ep; + struct snd_rawmidi_substream *substream; int active; uint8_t cable; /* cable number << 4 */ uint8_t state; @@ -133,19 +147,23 @@ struct snd_usb_midi_out_endpoint { uint8_t data[2]; } ports[0x10]; int current_port; + + wait_queue_head_t drain_wait; }; struct snd_usb_midi_in_endpoint { - snd_usb_midi_t* umidi; - struct urb* urb; + struct snd_usb_midi* umidi; + struct urb* urbs[INPUT_URBS]; struct usbmidi_in_port { - snd_rawmidi_substream_t* substream; + struct snd_rawmidi_substream *substream; + u8 running_status_length; } ports[0x10]; - int seen_f5; + u8 seen_f5; + u8 error_resubmit; int current_port; }; -static void snd_usbmidi_do_output(snd_usb_midi_out_endpoint_t* ep); +static void snd_usbmidi_do_output(struct snd_usb_midi_out_endpoint* ep); static const uint8_t snd_usbmidi_cin_length[] = { 0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1 @@ -154,7 +172,7 @@ static const uint8_t snd_usbmidi_cin_length[] = { /* * Submits the URB, with error handling. */ -static int snd_usbmidi_submit_urb(struct urb* urb, int flags) +static int snd_usbmidi_submit_urb(struct urb* urb, gfp_t flags) { int err = usb_submit_urb(urb, flags); if (err < 0 && err != -ENODEV) @@ -167,23 +185,31 @@ static int snd_usbmidi_submit_urb(struct urb* urb, int flags) */ static int snd_usbmidi_urb_error(int status) { - if (status == -ENOENT) - return status; /* killed */ - if (status == -EILSEQ || - status == -ECONNRESET || - status == -ETIMEDOUT) - return -ENODEV; /* device removed/shutdown */ - snd_printk(KERN_ERR "urb status %d\n", status); - return 0; /* continue */ + switch (status) { + /* manually unlinked, or device gone */ + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + case -ENODEV: + return -ENODEV; + /* errors that might occur during unplugging */ + case -EPROTO: + case -ETIME: + case -EILSEQ: + return -EIO; + default: + snd_printk(KERN_ERR "urb status %d\n", status); + return 0; /* continue */ + } } /* * Receives a chunk of MIDI data. */ -static void snd_usbmidi_input_data(snd_usb_midi_in_endpoint_t* ep, int portidx, +static void snd_usbmidi_input_data(struct snd_usb_midi_in_endpoint* ep, int portidx, uint8_t* data, int length) { - usbmidi_in_port_t* port = &ep->ports[portidx]; + struct usbmidi_in_port* port = &ep->ports[portidx]; if (!port->substream) { snd_printd("unexpected port %d!\n", portidx); @@ -209,35 +235,52 @@ static void dump_urb(const char *type, const u8 *data, int length) /* * Processes the data read from the device. */ -static void snd_usbmidi_in_urb_complete(struct urb* urb, struct pt_regs *regs) +static void snd_usbmidi_in_urb_complete(struct urb* urb) { - snd_usb_midi_in_endpoint_t* ep = urb->context; + struct snd_usb_midi_in_endpoint* ep = urb->context; if (urb->status == 0) { dump_urb("received", urb->transfer_buffer, urb->actual_length); ep->umidi->usb_protocol_ops->input(ep, urb->transfer_buffer, urb->actual_length); } else { - if (snd_usbmidi_urb_error(urb->status) < 0) + int err = snd_usbmidi_urb_error(urb->status); + if (err < 0) { + if (err != -ENODEV) { + ep->error_resubmit = 1; + mod_timer(&ep->umidi->error_timer, + jiffies + ERROR_DELAY_JIFFIES); + } return; + } } - if (usb_pipe_needs_resubmit(urb->pipe)) { - urb->dev = ep->umidi->chip->dev; - snd_usbmidi_submit_urb(urb, GFP_ATOMIC); - } + urb->dev = ep->umidi->chip->dev; + snd_usbmidi_submit_urb(urb, GFP_ATOMIC); } -static void snd_usbmidi_out_urb_complete(struct urb* urb, struct pt_regs *regs) +static void snd_usbmidi_out_urb_complete(struct urb* urb) { - snd_usb_midi_out_endpoint_t* ep = urb->context; + struct out_urb_context *context = urb->context; + struct snd_usb_midi_out_endpoint* ep = context->ep; + unsigned int urb_index; spin_lock(&ep->buffer_lock); - ep->urb_active = 0; + urb_index = context - ep->urbs; + ep->active_urbs &= ~(1 << urb_index); + if (unlikely(ep->drain_urbs)) { + ep->drain_urbs &= ~(1 << urb_index); + wake_up(&ep->drain_wait); + } spin_unlock(&ep->buffer_lock); if (urb->status < 0) { - if (snd_usbmidi_urb_error(urb->status) < 0) + int err = snd_usbmidi_urb_error(urb->status); + if (err < 0) { + if (err != -ENODEV) + mod_timer(&ep->umidi->error_timer, + jiffies + ERROR_DELAY_JIFFIES); return; + } } snd_usbmidi_do_output(ep); } @@ -246,48 +289,88 @@ static void snd_usbmidi_out_urb_complete(struct urb* urb, struct pt_regs *regs) * This is called when some data should be transferred to the device * (from one or more substreams). */ -static void snd_usbmidi_do_output(snd_usb_midi_out_endpoint_t* ep) +static void snd_usbmidi_do_output(struct snd_usb_midi_out_endpoint* ep) { - struct urb* urb = ep->urb; + unsigned int urb_index; + struct urb* urb; unsigned long flags; spin_lock_irqsave(&ep->buffer_lock, flags); - if (ep->urb_active || ep->umidi->chip->shutdown) { + if (ep->umidi->chip->shutdown) { spin_unlock_irqrestore(&ep->buffer_lock, flags); return; } - urb->transfer_buffer_length = 0; - ep->umidi->usb_protocol_ops->output(ep); + urb_index = ep->next_urb; + for (;;) { + if (!(ep->active_urbs & (1 << urb_index))) { + urb = ep->urbs[urb_index].urb; + urb->transfer_buffer_length = 0; + ep->umidi->usb_protocol_ops->output(ep, urb); + if (urb->transfer_buffer_length == 0) + break; - if (urb->transfer_buffer_length > 0) { - dump_urb("sending", urb->transfer_buffer, - urb->transfer_buffer_length); - urb->dev = ep->umidi->chip->dev; - ep->urb_active = snd_usbmidi_submit_urb(urb, GFP_ATOMIC) >= 0; + dump_urb("sending", urb->transfer_buffer, + urb->transfer_buffer_length); + urb->dev = ep->umidi->chip->dev; + if (snd_usbmidi_submit_urb(urb, GFP_ATOMIC) < 0) + break; + ep->active_urbs |= 1 << urb_index; + } + if (++urb_index >= OUTPUT_URBS) + urb_index = 0; + if (urb_index == ep->next_urb) + break; } + ep->next_urb = urb_index; spin_unlock_irqrestore(&ep->buffer_lock, flags); } static void snd_usbmidi_out_tasklet(unsigned long data) { - snd_usb_midi_out_endpoint_t* ep = (snd_usb_midi_out_endpoint_t *) data; + struct snd_usb_midi_out_endpoint* ep = (struct snd_usb_midi_out_endpoint *) data; snd_usbmidi_do_output(ep); } +/* called after transfers had been interrupted due to some USB error */ +static void snd_usbmidi_error_timer(unsigned long data) +{ + struct snd_usb_midi *umidi = (struct snd_usb_midi *)data; + unsigned int i, j; + + spin_lock(&umidi->disc_lock); + if (umidi->disconnected) { + spin_unlock(&umidi->disc_lock); + return; + } + for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { + struct snd_usb_midi_in_endpoint *in = umidi->endpoints[i].in; + if (in && in->error_resubmit) { + in->error_resubmit = 0; + for (j = 0; j < INPUT_URBS; ++j) { + in->urbs[j]->dev = umidi->chip->dev; + snd_usbmidi_submit_urb(in->urbs[j], GFP_ATOMIC); + } + } + if (umidi->endpoints[i].out) + snd_usbmidi_do_output(umidi->endpoints[i].out); + } + spin_unlock(&umidi->disc_lock); +} + /* helper function to send static data that may not DMA-able */ -static int send_bulk_static_data(snd_usb_midi_out_endpoint_t* ep, +static int send_bulk_static_data(struct snd_usb_midi_out_endpoint* ep, const void *data, int len) { - int err; - void *buf = kmalloc(len, GFP_KERNEL); + int err = 0; + void *buf = kmemdup(data, len, GFP_KERNEL); if (!buf) return -ENOMEM; - memcpy(buf, data, len); dump_urb("sending", buf, len); - err = usb_bulk_msg(ep->umidi->chip->dev, ep->urb->pipe, buf, len, - NULL, 250); + if (ep->urbs[0].urb) + err = usb_bulk_msg(ep->umidi->chip->dev, ep->urbs[0].urb->pipe, + buf, len, NULL, 250); kfree(buf); return err; } @@ -298,7 +381,7 @@ static int send_bulk_static_data(snd_usb_midi_out_endpoint_t* ep, * fourth byte in each packet, and uses length instead of CIN. */ -static void snd_usbmidi_standard_input(snd_usb_midi_in_endpoint_t* ep, +static void snd_usbmidi_standard_input(struct snd_usb_midi_in_endpoint* ep, uint8_t* buffer, int buffer_length) { int i; @@ -311,7 +394,7 @@ static void snd_usbmidi_standard_input(snd_usb_midi_in_endpoint_t* ep, } } -static void snd_usbmidi_midiman_input(snd_usb_midi_in_endpoint_t* ep, +static void snd_usbmidi_midiman_input(struct snd_usb_midi_in_endpoint* ep, uint8_t* buffer, int buffer_length) { int i; @@ -325,6 +408,60 @@ static void snd_usbmidi_midiman_input(snd_usb_midi_in_endpoint_t* ep, } /* + * Buggy M-Audio device: running status on input results in a packet that has + * the data bytes but not the status byte and that is marked with CIN 4. + */ +static void snd_usbmidi_maudio_broken_running_status_input( + struct snd_usb_midi_in_endpoint* ep, + uint8_t* buffer, int buffer_length) +{ + int i; + + for (i = 0; i + 3 < buffer_length; i += 4) + if (buffer[i] != 0) { + int cable = buffer[i] >> 4; + u8 cin = buffer[i] & 0x0f; + struct usbmidi_in_port *port = &ep->ports[cable]; + int length; + + length = snd_usbmidi_cin_length[cin]; + if (cin == 0xf && buffer[i + 1] >= 0xf8) + ; /* realtime msg: no running status change */ + else if (cin >= 0x8 && cin <= 0xe) + /* channel msg */ + port->running_status_length = length - 1; + else if (cin == 0x4 && + port->running_status_length != 0 && + buffer[i + 1] < 0x80) + /* CIN 4 that is not a SysEx */ + length = port->running_status_length; + else + /* + * All other msgs cannot begin running status. + * (A channel msg sent as two or three CIN 0xF + * packets could in theory, but this device + * doesn't use this format.) + */ + port->running_status_length = 0; + snd_usbmidi_input_data(ep, cable, &buffer[i + 1], length); + } +} + +/* + * CME protocol: like the standard protocol, but SysEx commands are sent as a + * single USB packet preceded by a 0x0F byte. + */ +static void snd_usbmidi_cme_input(struct snd_usb_midi_in_endpoint *ep, + uint8_t *buffer, int buffer_length) +{ + if (buffer_length < 2 || (buffer[0] & 0x0f) != 0x0f) + snd_usbmidi_standard_input(ep, buffer, buffer_length); + else + snd_usbmidi_input_data(ep, buffer[0] >> 4, + &buffer[1], buffer_length - 1); +} + +/* * Adds one USB MIDI packet to the output buffer. */ static void snd_usbmidi_output_standard_packet(struct urb* urb, uint8_t p0, @@ -357,7 +494,7 @@ static void snd_usbmidi_output_midiman_packet(struct urb* urb, uint8_t p0, /* * Converts MIDI commands to USB MIDI packets. */ -static void snd_usbmidi_transmit_byte(usbmidi_out_port_t* port, +static void snd_usbmidi_transmit_byte(struct usbmidi_out_port* port, uint8_t b, struct urb* urb) { uint8_t p0 = port->cable; @@ -451,14 +588,14 @@ static void snd_usbmidi_transmit_byte(usbmidi_out_port_t* port, } } -static void snd_usbmidi_standard_output(snd_usb_midi_out_endpoint_t* ep) +static void snd_usbmidi_standard_output(struct snd_usb_midi_out_endpoint* ep, + struct urb *urb) { - struct urb* urb = ep->urb; int p; /* FIXME: lower-numbered ports can starve higher-numbered ports */ for (p = 0; p < 0x10; ++p) { - usbmidi_out_port_t* port = &ep->ports[p]; + struct usbmidi_out_port* port = &ep->ports[p]; if (!port->active) continue; while (urb->transfer_buffer_length + 3 < ep->max_transfer) { @@ -484,13 +621,25 @@ static struct usb_protocol_ops snd_usbmidi_midiman_ops = { .output_packet = snd_usbmidi_output_midiman_packet, }; +static struct usb_protocol_ops snd_usbmidi_maudio_broken_running_status_ops = { + .input = snd_usbmidi_maudio_broken_running_status_input, + .output = snd_usbmidi_standard_output, + .output_packet = snd_usbmidi_output_standard_packet, +}; + +static struct usb_protocol_ops snd_usbmidi_cme_ops = { + .input = snd_usbmidi_cme_input, + .output = snd_usbmidi_standard_output, + .output_packet = snd_usbmidi_output_standard_packet, +}; + /* * Novation USB MIDI protocol: number of data bytes is in the first byte * (when receiving) (+1!) or in the second byte (when sending); data begins * at the third byte. */ -static void snd_usbmidi_novation_input(snd_usb_midi_in_endpoint_t* ep, +static void snd_usbmidi_novation_input(struct snd_usb_midi_in_endpoint* ep, uint8_t* buffer, int buffer_length) { if (buffer_length < 2 || !buffer[0] || buffer_length < buffer[0] + 1) @@ -498,14 +647,15 @@ static void snd_usbmidi_novation_input(snd_usb_midi_in_endpoint_t* ep, snd_usbmidi_input_data(ep, 0, &buffer[2], buffer[0] - 1); } -static void snd_usbmidi_novation_output(snd_usb_midi_out_endpoint_t* ep) +static void snd_usbmidi_novation_output(struct snd_usb_midi_out_endpoint* ep, + struct urb *urb) { uint8_t* transfer_buffer; int count; if (!ep->ports[0].active) return; - transfer_buffer = ep->urb->transfer_buffer; + transfer_buffer = urb->transfer_buffer; count = snd_rawmidi_transmit(ep->ports[0].substream, &transfer_buffer[2], ep->max_transfer - 2); @@ -515,7 +665,7 @@ static void snd_usbmidi_novation_output(snd_usb_midi_out_endpoint_t* ep) } transfer_buffer[0] = 0; transfer_buffer[1] = count; - ep->urb->transfer_buffer_length = 2 + count; + urb->transfer_buffer_length = 2 + count; } static struct usb_protocol_ops snd_usbmidi_novation_ops = { @@ -527,26 +677,27 @@ static struct usb_protocol_ops snd_usbmidi_novation_ops = { * "raw" protocol: used by the MOTU FastLane. */ -static void snd_usbmidi_raw_input(snd_usb_midi_in_endpoint_t* ep, +static void snd_usbmidi_raw_input(struct snd_usb_midi_in_endpoint* ep, uint8_t* buffer, int buffer_length) { snd_usbmidi_input_data(ep, 0, buffer, buffer_length); } -static void snd_usbmidi_raw_output(snd_usb_midi_out_endpoint_t* ep) +static void snd_usbmidi_raw_output(struct snd_usb_midi_out_endpoint* ep, + struct urb *urb) { int count; if (!ep->ports[0].active) return; count = snd_rawmidi_transmit(ep->ports[0].substream, - ep->urb->transfer_buffer, + urb->transfer_buffer, ep->max_transfer); if (count < 1) { ep->ports[0].active = 0; return; } - ep->urb->transfer_buffer_length = count; + urb->transfer_buffer_length = count; } static struct usb_protocol_ops snd_usbmidi_raw_ops = { @@ -554,11 +705,49 @@ static struct usb_protocol_ops snd_usbmidi_raw_ops = { .output = snd_usbmidi_raw_output, }; +static void snd_usbmidi_us122l_input(struct snd_usb_midi_in_endpoint *ep, + uint8_t *buffer, int buffer_length) +{ + if (buffer_length != 9) + return; + buffer_length = 8; + while (buffer_length && buffer[buffer_length - 1] == 0xFD) + buffer_length--; + if (buffer_length) + snd_usbmidi_input_data(ep, 0, buffer, buffer_length); +} + +static void snd_usbmidi_us122l_output(struct snd_usb_midi_out_endpoint *ep, + struct urb *urb) +{ + int count; + + if (!ep->ports[0].active) + return; + count = snd_usb_get_speed(ep->umidi->chip->dev) == USB_SPEED_HIGH + ? 1 : 2; + count = snd_rawmidi_transmit(ep->ports[0].substream, + urb->transfer_buffer, + count); + if (count < 1) { + ep->ports[0].active = 0; + return; + } + + memset(urb->transfer_buffer + count, 0xFD, 9 - count); + urb->transfer_buffer_length = count; +} + +static struct usb_protocol_ops snd_usbmidi_122l_ops = { + .input = snd_usbmidi_us122l_input, + .output = snd_usbmidi_us122l_output, +}; + /* * Emagic USB MIDI protocol: raw MIDI with "F5 xx" port switching. */ -static void snd_usbmidi_emagic_init_out(snd_usb_midi_out_endpoint_t* ep) +static void snd_usbmidi_emagic_init_out(struct snd_usb_midi_out_endpoint* ep) { static const u8 init_data[] = { /* initialization magic: "get version" */ @@ -575,7 +764,7 @@ static void snd_usbmidi_emagic_init_out(snd_usb_midi_out_endpoint_t* ep) send_bulk_static_data(ep, init_data, sizeof(init_data)); } -static void snd_usbmidi_emagic_finish_out(snd_usb_midi_out_endpoint_t* ep) +static void snd_usbmidi_emagic_finish_out(struct snd_usb_midi_out_endpoint* ep) { static const u8 finish_data[] = { /* switch to patch mode with last preset */ @@ -591,7 +780,7 @@ static void snd_usbmidi_emagic_finish_out(snd_usb_midi_out_endpoint_t* ep) send_bulk_static_data(ep, finish_data, sizeof(finish_data)); } -static void snd_usbmidi_emagic_input(snd_usb_midi_in_endpoint_t* ep, +static void snd_usbmidi_emagic_input(struct snd_usb_midi_in_endpoint* ep, uint8_t* buffer, int buffer_length) { int i; @@ -635,17 +824,18 @@ static void snd_usbmidi_emagic_input(snd_usb_midi_in_endpoint_t* ep, } } -static void snd_usbmidi_emagic_output(snd_usb_midi_out_endpoint_t* ep) +static void snd_usbmidi_emagic_output(struct snd_usb_midi_out_endpoint* ep, + struct urb *urb) { int port0 = ep->current_port; - uint8_t* buf = ep->urb->transfer_buffer; + uint8_t* buf = urb->transfer_buffer; int buf_free = ep->max_transfer; int length, i; for (i = 0; i < 0x10; ++i) { /* round-robin, starting at the last current port */ int portnum = (port0 + i) & 15; - usbmidi_out_port_t* port = &ep->ports[portnum]; + struct usbmidi_out_port* port = &ep->ports[portnum]; if (!port->active) continue; @@ -678,7 +868,7 @@ static void snd_usbmidi_emagic_output(snd_usb_midi_out_endpoint_t* ep) *buf = 0xff; --buf_free; } - ep->urb->transfer_buffer_length = ep->max_transfer - buf_free; + urb->transfer_buffer_length = ep->max_transfer - buf_free; } static struct usb_protocol_ops snd_usbmidi_emagic_ops = { @@ -689,10 +879,10 @@ static struct usb_protocol_ops snd_usbmidi_emagic_ops = { }; -static int snd_usbmidi_output_open(snd_rawmidi_substream_t* substream) +static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream) { - snd_usb_midi_t* umidi = substream->rmidi->private_data; - usbmidi_out_port_t* port = NULL; + struct snd_usb_midi* umidi = substream->rmidi->private_data; + struct usbmidi_out_port* port = NULL; int i, j; for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) @@ -711,14 +901,14 @@ static int snd_usbmidi_output_open(snd_rawmidi_substream_t* substream) return 0; } -static int snd_usbmidi_output_close(snd_rawmidi_substream_t* substream) +static int snd_usbmidi_output_close(struct snd_rawmidi_substream *substream) { return 0; } -static void snd_usbmidi_output_trigger(snd_rawmidi_substream_t* substream, int up) +static void snd_usbmidi_output_trigger(struct snd_rawmidi_substream *substream, int up) { - usbmidi_out_port_t* port = (usbmidi_out_port_t*)substream->runtime->private_data; + struct usbmidi_out_port* port = (struct usbmidi_out_port*)substream->runtime->private_data; port->active = up; if (up) { @@ -729,23 +919,52 @@ static void snd_usbmidi_output_trigger(snd_rawmidi_substream_t* substream, int u snd_rawmidi_transmit_ack(substream, 1); return; } - tasklet_hi_schedule(&port->ep->tasklet); + tasklet_schedule(&port->ep->tasklet); } } -static int snd_usbmidi_input_open(snd_rawmidi_substream_t* substream) +static void snd_usbmidi_output_drain(struct snd_rawmidi_substream *substream) +{ + struct usbmidi_out_port* port = substream->runtime->private_data; + struct snd_usb_midi_out_endpoint *ep = port->ep; + unsigned int drain_urbs; + DEFINE_WAIT(wait); + long timeout = msecs_to_jiffies(50); + + /* + * The substream buffer is empty, but some data might still be in the + * currently active URBs, so we have to wait for those to complete. + */ + spin_lock_irq(&ep->buffer_lock); + drain_urbs = ep->active_urbs; + if (drain_urbs) { + ep->drain_urbs |= drain_urbs; + do { + prepare_to_wait(&ep->drain_wait, &wait, + TASK_UNINTERRUPTIBLE); + spin_unlock_irq(&ep->buffer_lock); + timeout = schedule_timeout(timeout); + spin_lock_irq(&ep->buffer_lock); + drain_urbs &= ep->drain_urbs; + } while (drain_urbs && timeout); + finish_wait(&ep->drain_wait, &wait); + } + spin_unlock_irq(&ep->buffer_lock); +} + +static int snd_usbmidi_input_open(struct snd_rawmidi_substream *substream) { return 0; } -static int snd_usbmidi_input_close(snd_rawmidi_substream_t* substream) +static int snd_usbmidi_input_close(struct snd_rawmidi_substream *substream) { return 0; } -static void snd_usbmidi_input_trigger(snd_rawmidi_substream_t* substream, int up) +static void snd_usbmidi_input_trigger(struct snd_rawmidi_substream *substream, int up) { - snd_usb_midi_t* umidi = substream->rmidi->private_data; + struct snd_usb_midi* umidi = substream->rmidi->private_data; if (up) set_bit(substream->number, &umidi->input_triggered); @@ -753,77 +972,91 @@ static void snd_usbmidi_input_trigger(snd_rawmidi_substream_t* substream, int up clear_bit(substream->number, &umidi->input_triggered); } -static snd_rawmidi_ops_t snd_usbmidi_output_ops = { +static struct snd_rawmidi_ops snd_usbmidi_output_ops = { .open = snd_usbmidi_output_open, .close = snd_usbmidi_output_close, .trigger = snd_usbmidi_output_trigger, + .drain = snd_usbmidi_output_drain, }; -static snd_rawmidi_ops_t snd_usbmidi_input_ops = { +static struct snd_rawmidi_ops snd_usbmidi_input_ops = { .open = snd_usbmidi_input_open, .close = snd_usbmidi_input_close, .trigger = snd_usbmidi_input_trigger }; +static void free_urb_and_buffer(struct snd_usb_midi *umidi, struct urb *urb, + unsigned int buffer_length) +{ + usb_buffer_free(umidi->chip->dev, buffer_length, + urb->transfer_buffer, urb->transfer_dma); + usb_free_urb(urb); +} + /* * Frees an input endpoint. * May be called when ep hasn't been initialized completely. */ -static void snd_usbmidi_in_endpoint_delete(snd_usb_midi_in_endpoint_t* ep) +static void snd_usbmidi_in_endpoint_delete(struct snd_usb_midi_in_endpoint* ep) { - if (ep->urb) { - usb_buffer_free(ep->umidi->chip->dev, - ep->urb->transfer_buffer_length, - ep->urb->transfer_buffer, - ep->urb->transfer_dma); - usb_free_urb(ep->urb); - } + unsigned int i; + + for (i = 0; i < INPUT_URBS; ++i) + if (ep->urbs[i]) + free_urb_and_buffer(ep->umidi, ep->urbs[i], + ep->urbs[i]->transfer_buffer_length); kfree(ep); } /* * Creates an input endpoint. */ -static int snd_usbmidi_in_endpoint_create(snd_usb_midi_t* umidi, - snd_usb_midi_endpoint_info_t* ep_info, - snd_usb_midi_endpoint_t* rep) +static int snd_usbmidi_in_endpoint_create(struct snd_usb_midi* umidi, + struct snd_usb_midi_endpoint_info* ep_info, + struct snd_usb_midi_endpoint* rep) { - snd_usb_midi_in_endpoint_t* ep; + struct snd_usb_midi_in_endpoint* ep; void* buffer; unsigned int pipe; int length; + unsigned int i; rep->in = NULL; - ep = kcalloc(1, sizeof(*ep), GFP_KERNEL); + ep = kzalloc(sizeof(*ep), GFP_KERNEL); if (!ep) return -ENOMEM; ep->umidi = umidi; - ep->urb = usb_alloc_urb(0, GFP_KERNEL); - if (!ep->urb) { - snd_usbmidi_in_endpoint_delete(ep); - return -ENOMEM; + for (i = 0; i < INPUT_URBS; ++i) { + ep->urbs[i] = usb_alloc_urb(0, GFP_KERNEL); + if (!ep->urbs[i]) { + snd_usbmidi_in_endpoint_delete(ep); + return -ENOMEM; + } } if (ep_info->in_interval) pipe = usb_rcvintpipe(umidi->chip->dev, ep_info->in_ep); else pipe = usb_rcvbulkpipe(umidi->chip->dev, ep_info->in_ep); length = usb_maxpacket(umidi->chip->dev, pipe, 0); - buffer = usb_buffer_alloc(umidi->chip->dev, length, GFP_KERNEL, - &ep->urb->transfer_dma); - if (!buffer) { - snd_usbmidi_in_endpoint_delete(ep); - return -ENOMEM; + for (i = 0; i < INPUT_URBS; ++i) { + buffer = usb_buffer_alloc(umidi->chip->dev, length, GFP_KERNEL, + &ep->urbs[i]->transfer_dma); + if (!buffer) { + snd_usbmidi_in_endpoint_delete(ep); + return -ENOMEM; + } + if (ep_info->in_interval) + usb_fill_int_urb(ep->urbs[i], umidi->chip->dev, + pipe, buffer, length, + snd_usbmidi_in_urb_complete, + ep, ep_info->in_interval); + else + usb_fill_bulk_urb(ep->urbs[i], umidi->chip->dev, + pipe, buffer, length, + snd_usbmidi_in_urb_complete, ep); + ep->urbs[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP; } - if (ep_info->in_interval) - usb_fill_int_urb(ep->urb, umidi->chip->dev, pipe, buffer, length, - snd_usb_complete_callback(snd_usbmidi_in_urb_complete), - ep, ep_info->in_interval); - else - usb_fill_bulk_urb(ep->urb, umidi->chip->dev, pipe, buffer, length, - snd_usb_complete_callback(snd_usbmidi_in_urb_complete), - ep); - ep->urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; rep->in = ep; return 0; @@ -831,10 +1064,10 @@ static int snd_usbmidi_in_endpoint_create(snd_usb_midi_t* umidi, static unsigned int snd_usbmidi_count_bits(unsigned int x) { - unsigned int bits = 0; + unsigned int bits; - for (; x; x >>= 1) - bits += x & 1; + for (bits = 0; x; ++bits) + x &= x - 1; return bits; } @@ -842,58 +1075,75 @@ static unsigned int snd_usbmidi_count_bits(unsigned int x) * Frees an output endpoint. * May be called when ep hasn't been initialized completely. */ -static void snd_usbmidi_out_endpoint_delete(snd_usb_midi_out_endpoint_t* ep) +static void snd_usbmidi_out_endpoint_delete(struct snd_usb_midi_out_endpoint* ep) { - if (ep->tasklet.func) - tasklet_kill(&ep->tasklet); - if (ep->urb) { - usb_buffer_free(ep->umidi->chip->dev, ep->max_transfer, - ep->urb->transfer_buffer, - ep->urb->transfer_dma); - usb_free_urb(ep->urb); - } + unsigned int i; + + for (i = 0; i < OUTPUT_URBS; ++i) + if (ep->urbs[i].urb) + free_urb_and_buffer(ep->umidi, ep->urbs[i].urb, + ep->max_transfer); kfree(ep); } /* * Creates an output endpoint, and initializes output ports. */ -static int snd_usbmidi_out_endpoint_create(snd_usb_midi_t* umidi, - snd_usb_midi_endpoint_info_t* ep_info, - snd_usb_midi_endpoint_t* rep) +static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi* umidi, + struct snd_usb_midi_endpoint_info* ep_info, + struct snd_usb_midi_endpoint* rep) { - snd_usb_midi_out_endpoint_t* ep; - int i; + struct snd_usb_midi_out_endpoint* ep; + unsigned int i; unsigned int pipe; void* buffer; rep->out = NULL; - ep = kcalloc(1, sizeof(*ep), GFP_KERNEL); + ep = kzalloc(sizeof(*ep), GFP_KERNEL); if (!ep) return -ENOMEM; ep->umidi = umidi; - ep->urb = usb_alloc_urb(0, GFP_KERNEL); - if (!ep->urb) { - snd_usbmidi_out_endpoint_delete(ep); - return -ENOMEM; + for (i = 0; i < OUTPUT_URBS; ++i) { + ep->urbs[i].urb = usb_alloc_urb(0, GFP_KERNEL); + if (!ep->urbs[i].urb) { + snd_usbmidi_out_endpoint_delete(ep); + return -ENOMEM; + } + ep->urbs[i].ep = ep; } - /* we never use interrupt output pipes */ - pipe = usb_sndbulkpipe(umidi->chip->dev, ep_info->out_ep); - ep->max_transfer = usb_maxpacket(umidi->chip->dev, pipe, 1); - buffer = usb_buffer_alloc(umidi->chip->dev, ep->max_transfer, - GFP_KERNEL, &ep->urb->transfer_dma); - if (!buffer) { - snd_usbmidi_out_endpoint_delete(ep); - return -ENOMEM; + if (ep_info->out_interval) + pipe = usb_sndintpipe(umidi->chip->dev, ep_info->out_ep); + else + pipe = usb_sndbulkpipe(umidi->chip->dev, ep_info->out_ep); + if (umidi->chip->usb_id == USB_ID(0x0a92, 0x1020)) /* ESI M4U */ + ep->max_transfer = 4; + else + ep->max_transfer = usb_maxpacket(umidi->chip->dev, pipe, 1); + for (i = 0; i < OUTPUT_URBS; ++i) { + buffer = usb_buffer_alloc(umidi->chip->dev, + ep->max_transfer, GFP_KERNEL, + &ep->urbs[i].urb->transfer_dma); + if (!buffer) { + snd_usbmidi_out_endpoint_delete(ep); + return -ENOMEM; + } + if (ep_info->out_interval) + usb_fill_int_urb(ep->urbs[i].urb, umidi->chip->dev, + pipe, buffer, ep->max_transfer, + snd_usbmidi_out_urb_complete, + &ep->urbs[i], ep_info->out_interval); + else + usb_fill_bulk_urb(ep->urbs[i].urb, umidi->chip->dev, + pipe, buffer, ep->max_transfer, + snd_usbmidi_out_urb_complete, + &ep->urbs[i]); + ep->urbs[i].urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; } - usb_fill_bulk_urb(ep->urb, umidi->chip->dev, pipe, buffer, - ep->max_transfer, - snd_usb_complete_callback(snd_usbmidi_out_urb_complete), ep); - ep->urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; spin_lock_init(&ep->buffer_lock); tasklet_init(&ep->tasklet, snd_usbmidi_out_tasklet, (unsigned long)ep); + init_waitqueue_head(&ep->drain_wait); for (i = 0; i < 0x10; ++i) if (ep_info->out_cables & (1 << i)) { @@ -911,12 +1161,12 @@ static int snd_usbmidi_out_endpoint_create(snd_usb_midi_t* umidi, /* * Frees everything. */ -static void snd_usbmidi_free(snd_usb_midi_t* umidi) +static void snd_usbmidi_free(struct snd_usb_midi* umidi) { int i; for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { - snd_usb_midi_endpoint_t* ep = &umidi->endpoints[i]; + struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i]; if (ep->out) snd_usbmidi_out_endpoint_delete(ep->out); if (ep->in) @@ -930,35 +1180,57 @@ static void snd_usbmidi_free(snd_usb_midi_t* umidi) */ void snd_usbmidi_disconnect(struct list_head* p) { - snd_usb_midi_t* umidi; - int i; + struct snd_usb_midi* umidi; + unsigned int i, j; - umidi = list_entry(p, snd_usb_midi_t, list); + umidi = list_entry(p, struct snd_usb_midi, list); + /* + * an URB's completion handler may start the timer and + * a timer may submit an URB. To reliably break the cycle + * a flag under lock must be used + */ + spin_lock_irq(&umidi->disc_lock); + umidi->disconnected = 1; + spin_unlock_irq(&umidi->disc_lock); for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { - snd_usb_midi_endpoint_t* ep = &umidi->endpoints[i]; - if (ep->out && ep->out->urb) { - usb_kill_urb(ep->out->urb); + struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i]; + if (ep->out) + tasklet_kill(&ep->out->tasklet); + if (ep->out) { + for (j = 0; j < OUTPUT_URBS; ++j) + usb_kill_urb(ep->out->urbs[j].urb); if (umidi->usb_protocol_ops->finish_out_endpoint) umidi->usb_protocol_ops->finish_out_endpoint(ep->out); } - if (ep->in && ep->in->urb) - usb_kill_urb(ep->in->urb); + if (ep->in) + for (j = 0; j < INPUT_URBS; ++j) + usb_kill_urb(ep->in->urbs[j]); + /* free endpoints here; later call can result in Oops */ + if (ep->out) { + snd_usbmidi_out_endpoint_delete(ep->out); + ep->out = NULL; + } + if (ep->in) { + snd_usbmidi_in_endpoint_delete(ep->in); + ep->in = NULL; + } } + del_timer_sync(&umidi->error_timer); } -static void snd_usbmidi_rawmidi_free(snd_rawmidi_t* rmidi) +static void snd_usbmidi_rawmidi_free(struct snd_rawmidi *rmidi) { - snd_usb_midi_t* umidi = rmidi->private_data; + struct snd_usb_midi* umidi = rmidi->private_data; snd_usbmidi_free(umidi); } -static snd_rawmidi_substream_t* snd_usbmidi_find_substream(snd_usb_midi_t* umidi, +static struct snd_rawmidi_substream *snd_usbmidi_find_substream(struct snd_usb_midi* umidi, int stream, int number) { struct list_head* list; list_for_each(list, &umidi->rmidi->streams[stream].substreams) { - snd_rawmidi_substream_t* substream = list_entry(list, snd_rawmidi_substream_t, list); + struct snd_rawmidi_substream *substream = list_entry(list, struct snd_rawmidi_substream, list); if (substream->number == number) return substream; } @@ -970,112 +1242,168 @@ static snd_rawmidi_substream_t* snd_usbmidi_find_substream(snd_usb_midi_t* umidi * "(product) MIDI (n)" schema because they aren't external MIDI ports, * such as internal control or synthesizer ports. */ -static struct { +static struct port_info { u32 id; - int port; - const char *name_format; -} snd_usbmidi_port_names[] = { + short int port; + short int voices; + const char *name; + unsigned int seq_flags; +} snd_usbmidi_port_info[] = { +#define PORT_INFO(vendor, product, num, name_, voices_, flags) \ + { .id = USB_ID(vendor, product), \ + .port = num, .voices = voices_, \ + .name = name_, .seq_flags = flags } +#define EXTERNAL_PORT(vendor, product, num, name) \ + PORT_INFO(vendor, product, num, name, 0, \ + SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | \ + SNDRV_SEQ_PORT_TYPE_HARDWARE | \ + SNDRV_SEQ_PORT_TYPE_PORT) +#define CONTROL_PORT(vendor, product, num, name) \ + PORT_INFO(vendor, product, num, name, 0, \ + SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | \ + SNDRV_SEQ_PORT_TYPE_HARDWARE) +#define ROLAND_SYNTH_PORT(vendor, product, num, name, voices) \ + PORT_INFO(vendor, product, num, name, voices, \ + SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | \ + SNDRV_SEQ_PORT_TYPE_MIDI_GM | \ + SNDRV_SEQ_PORT_TYPE_MIDI_GM2 | \ + SNDRV_SEQ_PORT_TYPE_MIDI_GS | \ + SNDRV_SEQ_PORT_TYPE_MIDI_XG | \ + SNDRV_SEQ_PORT_TYPE_HARDWARE | \ + SNDRV_SEQ_PORT_TYPE_SYNTHESIZER) +#define SOUNDCANVAS_PORT(vendor, product, num, name, voices) \ + PORT_INFO(vendor, product, num, name, voices, \ + SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | \ + SNDRV_SEQ_PORT_TYPE_MIDI_GM | \ + SNDRV_SEQ_PORT_TYPE_MIDI_GM2 | \ + SNDRV_SEQ_PORT_TYPE_MIDI_GS | \ + SNDRV_SEQ_PORT_TYPE_MIDI_XG | \ + SNDRV_SEQ_PORT_TYPE_MIDI_MT32 | \ + SNDRV_SEQ_PORT_TYPE_HARDWARE | \ + SNDRV_SEQ_PORT_TYPE_SYNTHESIZER) /* Roland UA-100 */ - { USB_ID(0x0582, 0x0000), 2, "%s Control" }, + CONTROL_PORT(0x0582, 0x0000, 2, "%s Control"), /* Roland SC-8850 */ - { USB_ID(0x0582, 0x0003), 0, "%s Part A" }, - { USB_ID(0x0582, 0x0003), 1, "%s Part B" }, - { USB_ID(0x0582, 0x0003), 2, "%s Part C" }, - { USB_ID(0x0582, 0x0003), 3, "%s Part D" }, - { USB_ID(0x0582, 0x0003), 4, "%s MIDI 1" }, - { USB_ID(0x0582, 0x0003), 5, "%s MIDI 2" }, + SOUNDCANVAS_PORT(0x0582, 0x0003, 0, "%s Part A", 128), + SOUNDCANVAS_PORT(0x0582, 0x0003, 1, "%s Part B", 128), + SOUNDCANVAS_PORT(0x0582, 0x0003, 2, "%s Part C", 128), + SOUNDCANVAS_PORT(0x0582, 0x0003, 3, "%s Part D", 128), + EXTERNAL_PORT(0x0582, 0x0003, 4, "%s MIDI 1"), + EXTERNAL_PORT(0x0582, 0x0003, 5, "%s MIDI 2"), /* Roland U-8 */ - { USB_ID(0x0582, 0x0004), 0, "%s MIDI" }, - { USB_ID(0x0582, 0x0004), 1, "%s Control" }, + EXTERNAL_PORT(0x0582, 0x0004, 0, "%s MIDI"), + CONTROL_PORT(0x0582, 0x0004, 1, "%s Control"), /* Roland SC-8820 */ - { USB_ID(0x0582, 0x0007), 0, "%s Part A" }, - { USB_ID(0x0582, 0x0007), 1, "%s Part B" }, - { USB_ID(0x0582, 0x0007), 2, "%s MIDI" }, + SOUNDCANVAS_PORT(0x0582, 0x0007, 0, "%s Part A", 64), + SOUNDCANVAS_PORT(0x0582, 0x0007, 1, "%s Part B", 64), + EXTERNAL_PORT(0x0582, 0x0007, 2, "%s MIDI"), /* Roland SK-500 */ - { USB_ID(0x0582, 0x000b), 0, "%s Part A" }, - { USB_ID(0x0582, 0x000b), 1, "%s Part B" }, - { USB_ID(0x0582, 0x000b), 2, "%s MIDI" }, + SOUNDCANVAS_PORT(0x0582, 0x000b, 0, "%s Part A", 64), + SOUNDCANVAS_PORT(0x0582, 0x000b, 1, "%s Part B", 64), + EXTERNAL_PORT(0x0582, 0x000b, 2, "%s MIDI"), /* Roland SC-D70 */ - { USB_ID(0x0582, 0x000c), 0, "%s Part A" }, - { USB_ID(0x0582, 0x000c), 1, "%s Part B" }, - { USB_ID(0x0582, 0x000c), 2, "%s MIDI" }, + SOUNDCANVAS_PORT(0x0582, 0x000c, 0, "%s Part A", 64), + SOUNDCANVAS_PORT(0x0582, 0x000c, 1, "%s Part B", 64), + EXTERNAL_PORT(0x0582, 0x000c, 2, "%s MIDI"), /* Edirol UM-880 */ - { USB_ID(0x0582, 0x0014), 8, "%s Control" }, + CONTROL_PORT(0x0582, 0x0014, 8, "%s Control"), /* Edirol SD-90 */ - { USB_ID(0x0582, 0x0016), 0, "%s Part A" }, - { USB_ID(0x0582, 0x0016), 1, "%s Part B" }, - { USB_ID(0x0582, 0x0016), 2, "%s MIDI 1" }, - { USB_ID(0x0582, 0x0016), 3, "%s MIDI 2" }, + ROLAND_SYNTH_PORT(0x0582, 0x0016, 0, "%s Part A", 128), + ROLAND_SYNTH_PORT(0x0582, 0x0016, 1, "%s Part B", 128), + EXTERNAL_PORT(0x0582, 0x0016, 2, "%s MIDI 1"), + EXTERNAL_PORT(0x0582, 0x0016, 3, "%s MIDI 2"), /* Edirol UM-550 */ - { USB_ID(0x0582, 0x0023), 5, "%s Control" }, + CONTROL_PORT(0x0582, 0x0023, 5, "%s Control"), /* Edirol SD-20 */ - { USB_ID(0x0582, 0x0027), 0, "%s Part A" }, - { USB_ID(0x0582, 0x0027), 1, "%s Part B" }, - { USB_ID(0x0582, 0x0027), 2, "%s MIDI" }, + ROLAND_SYNTH_PORT(0x0582, 0x0027, 0, "%s Part A", 64), + ROLAND_SYNTH_PORT(0x0582, 0x0027, 1, "%s Part B", 64), + EXTERNAL_PORT(0x0582, 0x0027, 2, "%s MIDI"), /* Edirol SD-80 */ - { USB_ID(0x0582, 0x0029), 0, "%s Part A" }, - { USB_ID(0x0582, 0x0029), 1, "%s Part B" }, - { USB_ID(0x0582, 0x0029), 2, "%s MIDI 1" }, - { USB_ID(0x0582, 0x0029), 3, "%s MIDI 2" }, + ROLAND_SYNTH_PORT(0x0582, 0x0029, 0, "%s Part A", 128), + ROLAND_SYNTH_PORT(0x0582, 0x0029, 1, "%s Part B", 128), + EXTERNAL_PORT(0x0582, 0x0029, 2, "%s MIDI 1"), + EXTERNAL_PORT(0x0582, 0x0029, 3, "%s MIDI 2"), /* Edirol UA-700 */ - { USB_ID(0x0582, 0x002b), 0, "%s MIDI" }, - { USB_ID(0x0582, 0x002b), 1, "%s Control" }, + EXTERNAL_PORT(0x0582, 0x002b, 0, "%s MIDI"), + CONTROL_PORT(0x0582, 0x002b, 1, "%s Control"), /* Roland VariOS */ - { USB_ID(0x0582, 0x002f), 0, "%s MIDI" }, - { USB_ID(0x0582, 0x002f), 1, "%s External MIDI" }, - { USB_ID(0x0582, 0x002f), 2, "%s Sync" }, + EXTERNAL_PORT(0x0582, 0x002f, 0, "%s MIDI"), + EXTERNAL_PORT(0x0582, 0x002f, 1, "%s External MIDI"), + EXTERNAL_PORT(0x0582, 0x002f, 2, "%s Sync"), /* Edirol PCR */ - { USB_ID(0x0582, 0x0033), 0, "%s MIDI" }, - { USB_ID(0x0582, 0x0033), 1, "%s 1" }, - { USB_ID(0x0582, 0x0033), 2, "%s 2" }, + EXTERNAL_PORT(0x0582, 0x0033, 0, "%s MIDI"), + EXTERNAL_PORT(0x0582, 0x0033, 1, "%s 1"), + EXTERNAL_PORT(0x0582, 0x0033, 2, "%s 2"), /* BOSS GS-10 */ - { USB_ID(0x0582, 0x003b), 0, "%s MIDI" }, - { USB_ID(0x0582, 0x003b), 1, "%s Control" }, + EXTERNAL_PORT(0x0582, 0x003b, 0, "%s MIDI"), + CONTROL_PORT(0x0582, 0x003b, 1, "%s Control"), /* Edirol UA-1000 */ - { USB_ID(0x0582, 0x0044), 0, "%s MIDI" }, - { USB_ID(0x0582, 0x0044), 1, "%s Control" }, + EXTERNAL_PORT(0x0582, 0x0044, 0, "%s MIDI"), + CONTROL_PORT(0x0582, 0x0044, 1, "%s Control"), /* Edirol UR-80 */ - { USB_ID(0x0582, 0x0048), 0, "%s MIDI" }, - { USB_ID(0x0582, 0x0048), 1, "%s 1" }, - { USB_ID(0x0582, 0x0048), 2, "%s 2" }, + EXTERNAL_PORT(0x0582, 0x0048, 0, "%s MIDI"), + EXTERNAL_PORT(0x0582, 0x0048, 1, "%s 1"), + EXTERNAL_PORT(0x0582, 0x0048, 2, "%s 2"), /* Edirol PCR-A */ - { USB_ID(0x0582, 0x004d), 0, "%s MIDI" }, - { USB_ID(0x0582, 0x004d), 1, "%s 1" }, - { USB_ID(0x0582, 0x004d), 2, "%s 2" }, + EXTERNAL_PORT(0x0582, 0x004d, 0, "%s MIDI"), + EXTERNAL_PORT(0x0582, 0x004d, 1, "%s 1"), + EXTERNAL_PORT(0x0582, 0x004d, 2, "%s 2"), + /* Edirol UM-3EX */ + CONTROL_PORT(0x0582, 0x009a, 3, "%s Control"), /* M-Audio MidiSport 8x8 */ - { USB_ID(0x0763, 0x1031), 8, "%s Control" }, - { USB_ID(0x0763, 0x1033), 8, "%s Control" }, + CONTROL_PORT(0x0763, 0x1031, 8, "%s Control"), + CONTROL_PORT(0x0763, 0x1033, 8, "%s Control"), /* MOTU Fastlane */ - { USB_ID(0x07fd, 0x0001), 0, "%s MIDI A" }, - { USB_ID(0x07fd, 0x0001), 1, "%s MIDI B" }, + EXTERNAL_PORT(0x07fd, 0x0001, 0, "%s MIDI A"), + EXTERNAL_PORT(0x07fd, 0x0001, 1, "%s MIDI B"), /* Emagic Unitor8/AMT8/MT4 */ - { USB_ID(0x086a, 0x0001), 8, "%s Broadcast" }, - { USB_ID(0x086a, 0x0002), 8, "%s Broadcast" }, - { USB_ID(0x086a, 0x0003), 4, "%s Broadcast" }, + EXTERNAL_PORT(0x086a, 0x0001, 8, "%s Broadcast"), + EXTERNAL_PORT(0x086a, 0x0002, 8, "%s Broadcast"), + EXTERNAL_PORT(0x086a, 0x0003, 4, "%s Broadcast"), }; -static void snd_usbmidi_init_substream(snd_usb_midi_t* umidi, - int stream, int number, - snd_rawmidi_substream_t** rsubstream) +static struct port_info *find_port_info(struct snd_usb_midi* umidi, int number) { int i; + + for (i = 0; i < ARRAY_SIZE(snd_usbmidi_port_info); ++i) { + if (snd_usbmidi_port_info[i].id == umidi->chip->usb_id && + snd_usbmidi_port_info[i].port == number) + return &snd_usbmidi_port_info[i]; + } + return NULL; +} + +static void snd_usbmidi_get_port_info(struct snd_rawmidi *rmidi, int number, + struct snd_seq_port_info *seq_port_info) +{ + struct snd_usb_midi *umidi = rmidi->private_data; + struct port_info *port_info; + + /* TODO: read port flags from descriptors */ + port_info = find_port_info(umidi, number); + if (port_info) { + seq_port_info->type = port_info->seq_flags; + seq_port_info->midi_voices = port_info->voices; + } +} + +static void snd_usbmidi_init_substream(struct snd_usb_midi* umidi, + int stream, int number, + struct snd_rawmidi_substream ** rsubstream) +{ + struct port_info *port_info; const char *name_format; - snd_rawmidi_substream_t* substream = snd_usbmidi_find_substream(umidi, stream, number); + struct snd_rawmidi_substream *substream = snd_usbmidi_find_substream(umidi, stream, number); if (!substream) { snd_printd(KERN_ERR "substream %d:%d not found\n", stream, number); return; } /* TODO: read port name from jack descriptor */ - name_format = "%s MIDI %d"; - for (i = 0; i < ARRAY_SIZE(snd_usbmidi_port_names); ++i) { - if (snd_usbmidi_port_names[i].id == umidi->chip->usb_id && - snd_usbmidi_port_names[i].port == number) { - name_format = snd_usbmidi_port_names[i].name_format; - break; - } - } + port_info = find_port_info(umidi, number); + name_format = port_info ? port_info->name : "%s MIDI %d"; snprintf(substream->name, sizeof(substream->name), name_format, umidi->chip->card->shortname, number + 1); @@ -1085,8 +1413,8 @@ static void snd_usbmidi_init_substream(snd_usb_midi_t* umidi, /* * Creates the endpoints and their ports. */ -static int snd_usbmidi_create_endpoints(snd_usb_midi_t* umidi, - snd_usb_midi_endpoint_info_t* endpoints) +static int snd_usbmidi_create_endpoints(struct snd_usb_midi* umidi, + struct snd_usb_midi_endpoint_info* endpoints) { int i, j, err; int out_ports = 0, in_ports = 0; @@ -1126,8 +1454,8 @@ static int snd_usbmidi_create_endpoints(snd_usb_midi_t* umidi, /* * Returns MIDIStreaming device capabilities. */ -static int snd_usbmidi_get_ms_info(snd_usb_midi_t* umidi, - snd_usb_midi_endpoint_info_t* endpoints) +static int snd_usbmidi_get_ms_info(struct snd_usb_midi* umidi, + struct snd_usb_midi_endpoint_info* endpoints) { struct usb_interface* intf; struct usb_host_interface *hostif; @@ -1157,8 +1485,7 @@ static int snd_usbmidi_get_ms_info(snd_usb_midi_t* umidi, for (i = 0; i < intfd->bNumEndpoints; ++i) { hostep = &hostif->endpoint[i]; ep = get_ep_desc(hostep); - if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK && - (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) + if (!usb_endpoint_xfer_bulk(ep) && !usb_endpoint_xfer_int(ep)) continue; ms_ep = (struct usb_ms_endpoint_descriptor*)hostep->extra; if (hostep->extralen < 4 || @@ -1166,16 +1493,23 @@ static int snd_usbmidi_get_ms_info(snd_usb_midi_t* umidi, ms_ep->bDescriptorType != USB_DT_CS_ENDPOINT || ms_ep->bDescriptorSubtype != MS_GENERAL) continue; - if ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) { + if (usb_endpoint_dir_out(ep)) { if (endpoints[epidx].out_ep) { if (++epidx >= MIDI_MAX_ENDPOINTS) { snd_printk(KERN_WARNING "too many endpoints\n"); break; } } - endpoints[epidx].out_ep = ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) + endpoints[epidx].out_ep = usb_endpoint_num(ep); + if (usb_endpoint_xfer_int(ep)) endpoints[epidx].out_interval = ep->bInterval; + else if (snd_usb_get_speed(umidi->chip->dev) == USB_SPEED_LOW) + /* + * Low speed bulk transfers don't exist, so + * force interrupt transfers for devices like + * ESI MIDI Mate that try to use them anyway. + */ + endpoints[epidx].out_interval = 1; endpoints[epidx].out_cables = (1 << ms_ep->bNumEmbMIDIJack) - 1; snd_printdd(KERN_INFO "EP %02X: %d jack(s)\n", ep->bEndpointAddress, ms_ep->bNumEmbMIDIJack); @@ -1186,9 +1520,11 @@ static int snd_usbmidi_get_ms_info(snd_usb_midi_t* umidi, break; } } - endpoints[epidx].in_ep = ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) + endpoints[epidx].in_ep = usb_endpoint_num(ep); + if (usb_endpoint_xfer_int(ep)) endpoints[epidx].in_interval = ep->bInterval; + else if (snd_usb_get_speed(umidi->chip->dev) == USB_SPEED_LOW) + endpoints[epidx].in_interval = 1; endpoints[epidx].in_cables = (1 << ms_ep->bNumEmbMIDIJack) - 1; snd_printdd(KERN_INFO "EP %02X: %d jack(s)\n", ep->bEndpointAddress, ms_ep->bNumEmbMIDIJack); @@ -1201,7 +1537,7 @@ static int snd_usbmidi_get_ms_info(snd_usb_midi_t* umidi, * On Roland devices, use the second alternate setting to be able to use * the interrupt input endpoint. */ -static void snd_usbmidi_switch_roland_altsetting(snd_usb_midi_t* umidi) +static void snd_usbmidi_switch_roland_altsetting(struct snd_usb_midi* umidi) { struct usb_interface* intf; struct usb_host_interface *hostif; @@ -1227,8 +1563,8 @@ static void snd_usbmidi_switch_roland_altsetting(snd_usb_midi_t* umidi) /* * Try to find any usable endpoints in the interface. */ -static int snd_usbmidi_detect_endpoints(snd_usb_midi_t* umidi, - snd_usb_midi_endpoint_info_t* endpoint, +static int snd_usbmidi_detect_endpoints(struct snd_usb_midi* umidi, + struct snd_usb_midi_endpoint_info* endpoint, int max_endpoints) { struct usb_interface* intf; @@ -1251,20 +1587,20 @@ static int snd_usbmidi_detect_endpoints(snd_usb_midi_t* umidi, for (i = 0; i < intfd->bNumEndpoints; ++i) { epd = get_endpoint(hostif, i); - if ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK && - (epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) + if (!usb_endpoint_xfer_bulk(epd) && + !usb_endpoint_xfer_int(epd)) continue; if (out_eps < max_endpoints && - (epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) { - endpoint[out_eps].out_ep = epd->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - if ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) + usb_endpoint_dir_out(epd)) { + endpoint[out_eps].out_ep = usb_endpoint_num(epd); + if (usb_endpoint_xfer_int(epd)) endpoint[out_eps].out_interval = epd->bInterval; ++out_eps; } if (in_eps < max_endpoints && - (epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { - endpoint[in_eps].in_ep = epd->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - if ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) + usb_endpoint_dir_in(epd)) { + endpoint[in_eps].in_ep = usb_endpoint_num(epd); + if (usb_endpoint_xfer_int(epd)) endpoint[in_eps].in_interval = epd->bInterval; ++in_eps; } @@ -1275,8 +1611,8 @@ static int snd_usbmidi_detect_endpoints(snd_usb_midi_t* umidi, /* * Detects the endpoints for one-port-per-endpoint protocols. */ -static int snd_usbmidi_detect_per_port_endpoints(snd_usb_midi_t* umidi, - snd_usb_midi_endpoint_info_t* endpoints) +static int snd_usbmidi_detect_per_port_endpoints(struct snd_usb_midi* umidi, + struct snd_usb_midi_endpoint_info* endpoints) { int err, i; @@ -1293,8 +1629,8 @@ static int snd_usbmidi_detect_per_port_endpoints(snd_usb_midi_t* umidi, /* * Detects the endpoints and ports of Yamaha devices. */ -static int snd_usbmidi_detect_yamaha(snd_usb_midi_t* umidi, - snd_usb_midi_endpoint_info_t* endpoint) +static int snd_usbmidi_detect_yamaha(struct snd_usb_midi* umidi, + struct snd_usb_midi_endpoint_info* endpoint) { struct usb_interface* intf; struct usb_host_interface *hostif; @@ -1316,7 +1652,7 @@ static int snd_usbmidi_detect_yamaha(snd_usb_midi_t* umidi, for (cs_desc = hostif->extra; cs_desc < hostif->extra + hostif->extralen && cs_desc[0] >= 2; cs_desc += cs_desc[0]) { - if (cs_desc[1] == CS_AUDIO_INTERFACE) { + if (cs_desc[1] == USB_DT_CS_INTERFACE) { if (cs_desc[2] == MIDI_IN_JACK) endpoint->in_cables = (endpoint->in_cables << 1) | 1; else if (cs_desc[2] == MIDI_OUT_JACK) @@ -1332,10 +1668,10 @@ static int snd_usbmidi_detect_yamaha(snd_usb_midi_t* umidi, /* * Creates the endpoints and their ports for Midiman devices. */ -static int snd_usbmidi_create_endpoints_midiman(snd_usb_midi_t* umidi, - snd_usb_midi_endpoint_info_t* endpoint) +static int snd_usbmidi_create_endpoints_midiman(struct snd_usb_midi* umidi, + struct snd_usb_midi_endpoint_info* endpoint) { - snd_usb_midi_endpoint_info_t ep_info; + struct snd_usb_midi_endpoint_info ep_info; struct usb_interface* intf; struct usb_host_interface *hostif; struct usb_interface_descriptor* intfd; @@ -1363,27 +1699,26 @@ static int snd_usbmidi_create_endpoints_midiman(snd_usb_midi_t* umidi, } epd = get_endpoint(hostif, 0); - if ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != USB_DIR_IN || - (epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) { + if (!usb_endpoint_dir_in(epd) || !usb_endpoint_xfer_int(epd)) { snd_printdd(KERN_ERR "endpoint[0] isn't interrupt\n"); return -ENXIO; } epd = get_endpoint(hostif, 2); - if ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != USB_DIR_OUT || - (epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) { + if (!usb_endpoint_dir_out(epd) || !usb_endpoint_xfer_bulk(epd)) { snd_printdd(KERN_ERR "endpoint[2] isn't bulk output\n"); return -ENXIO; } if (endpoint->out_cables > 0x0001) { epd = get_endpoint(hostif, 4); - if ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != USB_DIR_OUT || - (epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) { + if (!usb_endpoint_dir_out(epd) || + !usb_endpoint_xfer_bulk(epd)) { snd_printdd(KERN_ERR "endpoint[4] isn't bulk output\n"); return -ENXIO; } } ep_info.out_ep = get_endpoint(hostif, 2)->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + ep_info.out_interval = 0; ep_info.out_cables = endpoint->out_cables & 0x5555; err = snd_usbmidi_out_endpoint_create(umidi, &ep_info, &umidi->endpoints[0]); if (err < 0) @@ -1415,10 +1750,14 @@ static int snd_usbmidi_create_endpoints_midiman(snd_usb_midi_t* umidi, return 0; } -static int snd_usbmidi_create_rawmidi(snd_usb_midi_t* umidi, +static struct snd_rawmidi_global_ops snd_usbmidi_ops = { + .get_port_info = snd_usbmidi_get_port_info, +}; + +static int snd_usbmidi_create_rawmidi(struct snd_usb_midi* umidi, int out_ports, int in_ports) { - snd_rawmidi_t* rmidi; + struct snd_rawmidi *rmidi; int err; err = snd_rawmidi_new(umidi->chip->card, "USB MIDI", @@ -1430,6 +1769,7 @@ static int snd_usbmidi_create_rawmidi(snd_usb_midi_t* umidi, rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; + rmidi->ops = &snd_usbmidi_ops; rmidi->private_data = umidi; rmidi->private_free = snd_usbmidi_rawmidi_free; snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_usbmidi_output_ops); @@ -1444,21 +1784,26 @@ static int snd_usbmidi_create_rawmidi(snd_usb_midi_t* umidi, */ void snd_usbmidi_input_stop(struct list_head* p) { - snd_usb_midi_t* umidi; - int i; + struct snd_usb_midi* umidi; + unsigned int i, j; - umidi = list_entry(p, snd_usb_midi_t, list); + umidi = list_entry(p, struct snd_usb_midi, list); for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { - snd_usb_midi_endpoint_t* ep = &umidi->endpoints[i]; + struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i]; if (ep->in) - usb_kill_urb(ep->in->urb); + for (j = 0; j < INPUT_URBS; ++j) + usb_kill_urb(ep->in->urbs[j]); } } -static void snd_usbmidi_input_start_ep(snd_usb_midi_in_endpoint_t* ep) +static void snd_usbmidi_input_start_ep(struct snd_usb_midi_in_endpoint* ep) { - if (ep) { - struct urb* urb = ep->urb; + unsigned int i; + + if (!ep) + return; + for (i = 0; i < INPUT_URBS; ++i) { + struct urb* urb = ep->urbs[i]; urb->dev = ep->umidi->chip->dev; snd_usbmidi_submit_urb(urb, GFP_KERNEL); } @@ -1469,10 +1814,10 @@ static void snd_usbmidi_input_start_ep(snd_usb_midi_in_endpoint_t* ep) */ void snd_usbmidi_input_start(struct list_head* p) { - snd_usb_midi_t* umidi; + struct snd_usb_midi* umidi; int i; - umidi = list_entry(p, snd_usb_midi_t, list); + umidi = list_entry(p, struct snd_usb_midi, list); for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) snd_usbmidi_input_start_ep(umidi->endpoints[i].in); } @@ -1480,65 +1825,85 @@ void snd_usbmidi_input_start(struct list_head* p) /* * Creates and registers everything needed for a MIDI streaming interface. */ -int snd_usb_create_midi_interface(snd_usb_audio_t* chip, +int snd_usb_create_midi_interface(struct snd_usb_audio* chip, struct usb_interface* iface, - const snd_usb_audio_quirk_t* quirk) + const struct snd_usb_audio_quirk* quirk) { - snd_usb_midi_t* umidi; - snd_usb_midi_endpoint_info_t endpoints[MIDI_MAX_ENDPOINTS]; + struct snd_usb_midi* umidi; + struct snd_usb_midi_endpoint_info endpoints[MIDI_MAX_ENDPOINTS]; int out_ports, in_ports; int i, err; - umidi = kcalloc(1, sizeof(*umidi), GFP_KERNEL); + umidi = kzalloc(sizeof(*umidi), GFP_KERNEL); if (!umidi) return -ENOMEM; umidi->chip = chip; umidi->iface = iface; umidi->quirk = quirk; umidi->usb_protocol_ops = &snd_usbmidi_standard_ops; + init_timer(&umidi->error_timer); + spin_lock_init(&umidi->disc_lock); + umidi->error_timer.function = snd_usbmidi_error_timer; + umidi->error_timer.data = (unsigned long)umidi; /* detect the endpoint(s) to use */ memset(endpoints, 0, sizeof(endpoints)); - if (!quirk) { + switch (quirk ? quirk->type : QUIRK_MIDI_STANDARD_INTERFACE) { + case QUIRK_MIDI_STANDARD_INTERFACE: err = snd_usbmidi_get_ms_info(umidi, endpoints); - } else { - switch (quirk->type) { - case QUIRK_MIDI_FIXED_ENDPOINT: - memcpy(&endpoints[0], quirk->data, - sizeof(snd_usb_midi_endpoint_info_t)); - err = snd_usbmidi_detect_endpoints(umidi, &endpoints[0], 1); - break; - case QUIRK_MIDI_YAMAHA: - err = snd_usbmidi_detect_yamaha(umidi, &endpoints[0]); - break; - case QUIRK_MIDI_MIDIMAN: - umidi->usb_protocol_ops = &snd_usbmidi_midiman_ops; - memcpy(&endpoints[0], quirk->data, - sizeof(snd_usb_midi_endpoint_info_t)); - err = 0; - break; - case QUIRK_MIDI_NOVATION: - umidi->usb_protocol_ops = &snd_usbmidi_novation_ops; - err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints); - break; - case QUIRK_MIDI_RAW: - umidi->usb_protocol_ops = &snd_usbmidi_raw_ops; - err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints); - break; - case QUIRK_MIDI_EMAGIC: - umidi->usb_protocol_ops = &snd_usbmidi_emagic_ops; - memcpy(&endpoints[0], quirk->data, - sizeof(snd_usb_midi_endpoint_info_t)); - err = snd_usbmidi_detect_endpoints(umidi, &endpoints[0], 1); - break; - case QUIRK_MIDI_MIDITECH: - err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints); - break; - default: - snd_printd(KERN_ERR "invalid quirk type %d\n", quirk->type); - err = -ENXIO; - break; - } + if (chip->usb_id == USB_ID(0x0763, 0x0150)) /* M-Audio Uno */ + umidi->usb_protocol_ops = + &snd_usbmidi_maudio_broken_running_status_ops; + break; + case QUIRK_MIDI_US122L: + umidi->usb_protocol_ops = &snd_usbmidi_122l_ops; + /* fall through */ + case QUIRK_MIDI_FIXED_ENDPOINT: + memcpy(&endpoints[0], quirk->data, + sizeof(struct snd_usb_midi_endpoint_info)); + err = snd_usbmidi_detect_endpoints(umidi, &endpoints[0], 1); + break; + case QUIRK_MIDI_YAMAHA: + err = snd_usbmidi_detect_yamaha(umidi, &endpoints[0]); + break; + case QUIRK_MIDI_MIDIMAN: + umidi->usb_protocol_ops = &snd_usbmidi_midiman_ops; + memcpy(&endpoints[0], quirk->data, + sizeof(struct snd_usb_midi_endpoint_info)); + err = 0; + break; + case QUIRK_MIDI_NOVATION: + umidi->usb_protocol_ops = &snd_usbmidi_novation_ops; + err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints); + break; + case QUIRK_MIDI_FASTLANE: + umidi->usb_protocol_ops = &snd_usbmidi_raw_ops; + /* + * Interface 1 contains isochronous endpoints, but with the same + * numbers as in interface 0. Since it is interface 1 that the + * USB core has most recently seen, these descriptors are now + * associated with the endpoint numbers. This will foul up our + * attempts to submit bulk/interrupt URBs to the endpoints in + * interface 0, so we have to make sure that the USB core looks + * again at interface 0 by calling usb_set_interface() on it. + */ + usb_set_interface(umidi->chip->dev, 0, 0); + err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints); + break; + case QUIRK_MIDI_EMAGIC: + umidi->usb_protocol_ops = &snd_usbmidi_emagic_ops; + memcpy(&endpoints[0], quirk->data, + sizeof(struct snd_usb_midi_endpoint_info)); + err = snd_usbmidi_detect_endpoints(umidi, &endpoints[0], 1); + break; + case QUIRK_MIDI_CME: + umidi->usb_protocol_ops = &snd_usbmidi_cme_ops; + err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints); + break; + default: + snd_printd(KERN_ERR "invalid quirk type %d\n", quirk->type); + err = -ENXIO; + break; } if (err < 0) { kfree(umidi);