ALSA: usb-audio: Fix bogus error return in snd_usb_create_stream()
[pandora-kernel.git] / sound / usb / card.c
index 0f6dc0d..413b32d 100644 (file)
@@ -149,14 +149,32 @@ static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int int
                return -EINVAL;
        }
 
+       alts = &iface->altsetting[0];
+       altsd = get_iface_desc(alts);
+
+       /*
+        * Android with both accessory and audio interfaces enabled gets the
+        * interface numbers wrong.
+        */
+       if ((chip->usb_id == USB_ID(0x18d1, 0x2d04) ||
+            chip->usb_id == USB_ID(0x18d1, 0x2d05)) &&
+           interface == 0 &&
+           altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC &&
+           altsd->bInterfaceSubClass == USB_SUBCLASS_VENDOR_SPEC) {
+               interface = 2;
+               iface = usb_ifnum_to_if(dev, interface);
+               if (!iface)
+                       return -EINVAL;
+               alts = &iface->altsetting[0];
+               altsd = get_iface_desc(alts);
+       }
+
        if (usb_interface_claimed(iface)) {
                snd_printdd(KERN_INFO "%d:%d:%d: skipping, already claimed\n",
                                                dev->devnum, ctrlif, interface);
                return -EINVAL;
        }
 
-       alts = &iface->altsetting[0];
-       altsd = get_iface_desc(alts);
        if ((altsd->bInterfaceClass == USB_CLASS_AUDIO ||
             altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC) &&
            altsd->bInterfaceSubClass == USB_SUBCLASS_MIDISTREAMING) {
@@ -189,7 +207,6 @@ static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int int
        if (! snd_usb_parse_audio_interface(chip, interface)) {
                usb_set_interface(dev, interface, 0); /* reset the current interface */
                usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L);
-               return -EINVAL;
        }
 
        return 0;
@@ -336,7 +353,7 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
                return -ENOMEM;
        }
 
-       mutex_init(&chip->shutdown_mutex);
+       init_rwsem(&chip->shutdown_rwsem);
        chip->index = idx;
        chip->dev = dev;
        chip->card = card;
@@ -550,16 +567,19 @@ static void snd_usb_audio_disconnect(struct usb_device *dev,
 {
        struct snd_card *card;
        struct list_head *p;
+       bool was_shutdown;
 
        if (chip == (void *)-1L)
                return;
 
        card = chip->card;
-       mutex_lock(&register_mutex);
-       mutex_lock(&chip->shutdown_mutex);
+       down_write(&chip->shutdown_rwsem);
+       was_shutdown = chip->shutdown;
        chip->shutdown = 1;
-       chip->num_interfaces--;
-       if (chip->num_interfaces <= 0) {
+       up_write(&chip->shutdown_rwsem);
+
+       mutex_lock(&register_mutex);
+       if (!was_shutdown) {
                snd_card_disconnect(card);
                /* release the pcm resources */
                list_for_each(p, &chip->pcm_list) {
@@ -573,12 +593,14 @@ static void snd_usb_audio_disconnect(struct usb_device *dev,
                list_for_each(p, &chip->mixer_list) {
                        snd_usb_mixer_disconnect(p);
                }
+       }
+
+       chip->num_interfaces--;
+       if (chip->num_interfaces <= 0) {
                usb_chip[chip->index] = NULL;
-               mutex_unlock(&chip->shutdown_mutex);
                mutex_unlock(&register_mutex);
                snd_card_free_when_closed(card);
        } else {
-               mutex_unlock(&chip->shutdown_mutex);
                mutex_unlock(&register_mutex);
        }
 }
@@ -610,16 +632,22 @@ int snd_usb_autoresume(struct snd_usb_audio *chip)
 {
        int err = -ENODEV;
 
-       if (!chip->shutdown && !chip->probing)
+       down_read(&chip->shutdown_rwsem);
+       if (chip->probing)
+               err = 0;
+       else if (!chip->shutdown)
                err = usb_autopm_get_interface(chip->pm_intf);
+       up_read(&chip->shutdown_rwsem);
 
        return err;
 }
 
 void snd_usb_autosuspend(struct snd_usb_audio *chip)
 {
+       down_read(&chip->shutdown_rwsem);
        if (!chip->shutdown && !chip->probing)
                usb_autopm_put_interface(chip->pm_intf);
+       up_read(&chip->shutdown_rwsem);
 }
 
 static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)