ALSA: usb-audio: Avoid autopm calls after disconnection
[linux-2.6.git] / sound / usb / midi.c
index 156cd07..c0054ee 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"
 
 /*
@@ -114,6 +116,7 @@ struct snd_usb_midi {
        struct list_head list;
        struct timer_list error_timer;
        spinlock_t disc_lock;
+       struct rw_semaphore disc_rwsem;
        struct mutex mutex;
        u32 usb_id;
        int next_midi_device;
@@ -146,6 +149,7 @@ struct snd_usb_midi_out_endpoint {
                struct snd_usb_midi_out_endpoint* ep;
                struct snd_rawmidi_substream *substream;
                int active;
+               bool autopm_reference;
                uint8_t cable;          /* cable number << 4 */
                uint8_t state;
 #define STATE_UNKNOWN  0
@@ -784,7 +788,7 @@ static struct usb_protocol_ops snd_usbmidi_novation_ops = {
 };
 
 /*
- * "raw" protocol: used by the MOTU FastLane.
+ * "raw" protocol: just move raw MIDI bytes from/to the endpoint
  */
 
 static void snd_usbmidi_raw_input(struct snd_usb_midi_in_endpoint* ep,
@@ -815,6 +819,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)
 {
@@ -850,8 +870,8 @@ static void snd_usbmidi_us122l_output(struct snd_usb_midi_out_endpoint *ep,
                return;
        }
 
-       memset(urb->transfer_buffer + count, 0xFD, 9 - count);
-       urb->transfer_buffer_length = count;
+       memset(urb->transfer_buffer + count, 0xFD, ep->max_transfer - count);
+       urb->transfer_buffer_length = ep->max_transfer;
 }
 
 static struct usb_protocol_ops snd_usbmidi_122l_ops = {
@@ -1019,6 +1039,12 @@ static void substream_open(struct snd_rawmidi_substream *substream, int open)
        struct snd_usb_midi* umidi = substream->rmidi->private_data;
        struct snd_kcontrol *ctl;
 
+       down_read(&umidi->disc_rwsem);
+       if (umidi->disconnected) {
+               up_read(&umidi->disc_rwsem);
+               return;
+       }
+
        mutex_lock(&umidi->mutex);
        if (open) {
                if (umidi->opened++ == 0 && umidi->roland_load_ctl) {
@@ -1037,6 +1063,7 @@ static void substream_open(struct snd_rawmidi_substream *substream, int open)
                }
        }
        mutex_unlock(&umidi->mutex);
+       up_read(&umidi->disc_rwsem);
 }
 
 static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream)
@@ -1044,6 +1071,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 +1084,17 @@ static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream)
                snd_BUG();
                return -ENXIO;
        }
+
+       down_read(&umidi->disc_rwsem);
+       if (umidi->disconnected) {
+               up_read(&umidi->disc_rwsem);
+               return -ENODEV;
+       }
+       err = usb_autopm_get_interface(umidi->iface);
+       port->autopm_reference = err >= 0;
+       up_read(&umidi->disc_rwsem);
+       if (err < 0 && err != -EACCES)
+               return -EIO;
        substream->runtime->private_data = port;
        port->state = STATE_UNKNOWN;
        substream_open(substream, 1);
@@ -1064,7 +1103,14 @@ 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;
+       struct usbmidi_out_port *port = substream->runtime->private_data;
+
        substream_open(substream, 0);
+       down_read(&umidi->disc_rwsem);
+       if (!umidi->disconnected && port->autopm_reference)
+               usb_autopm_put_interface(umidi->iface);
+       up_read(&umidi->disc_rwsem);
        return 0;
 }
 
@@ -1293,8 +1339,16 @@ 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;
+               /*
+                * Some devices only work with 9 bytes packet size:
+                */
+       case USB_ID(0x0644, 0x800E): /* Tascam US-122L */
+       case USB_ID(0x0644, 0x800F): /* Tascam US-144 */
+               ep->max_transfer = 9;
+               break;
        }
        for (i = 0; i < OUTPUT_URBS; ++i) {
                buffer = usb_alloc_coherent(umidi->dev,
@@ -1366,9 +1420,12 @@ void snd_usbmidi_disconnect(struct list_head* p)
         * a timer may submit an URB. To reliably break the cycle
         * a flag under lock must be used
         */
+       down_write(&umidi->disc_rwsem);
        spin_lock_irq(&umidi->disc_lock);
        umidi->disconnected = 1;
        spin_unlock_irq(&umidi->disc_lock);
+       up_write(&umidi->disc_rwsem);
+
        for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
                struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i];
                if (ep->out)
@@ -1729,13 +1786,7 @@ static int roland_load_info(struct snd_kcontrol *kcontrol,
 {
        static const char *const names[] = { "High Load", "Light Load" };
 
-       info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-       info->count = 1;
-       info->value.enumerated.items = 2;
-       if (info->value.enumerated.item > 1)
-               info->value.enumerated.item = 1;
-       strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
-       return 0;
+       return snd_ctl_enum_info(info, 1, 2, names);
 }
 
 static int roland_load_get(struct snd_kcontrol *kcontrol,
@@ -2086,6 +2137,7 @@ int snd_usbmidi_create(struct snd_card *card,
        umidi->usb_protocol_ops = &snd_usbmidi_standard_ops;
        init_timer(&umidi->error_timer);
        spin_lock_init(&umidi->disc_lock);
+       init_rwsem(&umidi->disc_rwsem);
        mutex_init(&umidi->mutex);
        umidi->usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor),
                               le16_to_cpu(umidi->dev->descriptor.idProduct));
@@ -2122,7 +2174,7 @@ int snd_usbmidi_create(struct snd_card *card,
                umidi->usb_protocol_ops = &snd_usbmidi_novation_ops;
                err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints);
                break;
-       case QUIRK_MIDI_FASTLANE:
+       case QUIRK_MIDI_RAW_BYTES:
                umidi->usb_protocol_ops = &snd_usbmidi_raw_ops;
                /*
                 * Interface 1 contains isochronous endpoints, but with the same
@@ -2133,7 +2185,8 @@ int snd_usbmidi_create(struct snd_card *card,
                 * 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->dev, 0, 0);
+               if (umidi->usb_id == USB_ID(0x07fd, 0x0001)) /* MOTU Fastlane */
+                       usb_set_interface(umidi->dev, 0, 0);
                err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints);
                break;
        case QUIRK_MIDI_EMAGIC:
@@ -2152,6 +2205,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;