ALSA: usb-audio: Fix races at disconnection in mixer_quirks.c
[linux-2.6.git] / sound / usb / midi.c
index db2dc5f..c83f614 100644 (file)
@@ -47,6 +47,7 @@
 #include <linux/usb.h>
 #include <linux/wait.h>
 #include <linux/usb/audio.h>
+#include <linux/module.h>
 
 #include <sound/core.h>
 #include <sound/control.h>
@@ -54,6 +55,7 @@
 #include <sound/asequencer.h>
 #include "usbaudio.h"
 #include "midi.h"
+#include "power.h"
 #include "helper.h"
 
 /*
@@ -815,6 +817,22 @@ static struct usb_protocol_ops snd_usbmidi_raw_ops = {
        .output = snd_usbmidi_raw_output,
 };
 
+/*
+ * FTDI protocol: raw MIDI bytes, but input packets have two modem status bytes.
+ */
+
+static void snd_usbmidi_ftdi_input(struct snd_usb_midi_in_endpoint* ep,
+                                  uint8_t* buffer, int buffer_length)
+{
+       if (buffer_length > 2)
+               snd_usbmidi_input_data(ep, 0, buffer + 2, buffer_length - 2);
+}
+
+static struct usb_protocol_ops snd_usbmidi_ftdi_ops = {
+       .input = snd_usbmidi_ftdi_input,
+       .output = snd_usbmidi_raw_output,
+};
+
 static void snd_usbmidi_us122l_input(struct snd_usb_midi_in_endpoint *ep,
                                     uint8_t *buffer, int buffer_length)
 {
@@ -1044,6 +1062,7 @@ static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream)
        struct snd_usb_midi* umidi = substream->rmidi->private_data;
        struct usbmidi_out_port* port = NULL;
        int i, j;
+       int err;
 
        for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i)
                if (umidi->endpoints[i].out)
@@ -1056,6 +1075,9 @@ static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream)
                snd_BUG();
                return -ENXIO;
        }
+       err = usb_autopm_get_interface(umidi->iface);
+       if (err < 0)
+               return -EIO;
        substream->runtime->private_data = port;
        port->state = STATE_UNKNOWN;
        substream_open(substream, 1);
@@ -1064,7 +1086,10 @@ static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream)
 
 static int snd_usbmidi_output_close(struct snd_rawmidi_substream *substream)
 {
+       struct snd_usb_midi* umidi = substream->rmidi->private_data;
+
        substream_open(substream, 0);
+       usb_autopm_put_interface(umidi->iface);
        return 0;
 }
 
@@ -1293,6 +1318,7 @@ static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi* umidi,
        case USB_ID(0x15ca, 0x0101): /* Textech USB Midi Cable */
        case USB_ID(0x15ca, 0x1806): /* Textech USB Midi Cable */
        case USB_ID(0x1a86, 0x752d): /* QinHeng CH345 "USB2.0-MIDI" */
+       case USB_ID(0xfc08, 0x0101): /* Unknown vendor Cable */
                ep->max_transfer = 4;
                break;
                /*
@@ -2154,6 +2180,17 @@ int snd_usbmidi_create(struct snd_card *card,
                /* endpoint 1 is input-only */
                endpoints[1].out_cables = 0;
                break;
+       case QUIRK_MIDI_FTDI:
+               umidi->usb_protocol_ops = &snd_usbmidi_ftdi_ops;
+
+               /* set baud rate to 31250 (48 MHz / 16 / 96) */
+               err = usb_control_msg(umidi->dev, usb_sndctrlpipe(umidi->dev, 0),
+                                     3, 0x40, 0x60, 0, NULL, 0, 1000);
+               if (err < 0)
+                       break;
+
+               err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints);
+               break;
        default:
                snd_printd(KERN_ERR "invalid quirk type %d\n", quirk->type);
                err = -ENXIO;