]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - sound/usb/card.c
ALSA: usb-audio: Use rwsem for disconnect protection
[karo-tx-linux.git] / sound / usb / card.c
index a90662af2d6bbd7b2df46d17cb1da01ee849d29c..144f1317e2f44dde92a0bc90067fc247f7d76526 100644 (file)
@@ -48,6 +48,7 @@
 #include <linux/usb/audio.h>
 #include <linux/usb/audio-v2.h>
 
+#include <sound/control.h>
 #include <sound/core.h>
 #include <sound/info.h>
 #include <sound/pcm.h>
@@ -334,7 +335,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;
@@ -492,14 +493,6 @@ static void *snd_usb_audio_probe(struct usb_device *dev,
                }
        }
 
-       chip->txfr_quirk = 0;
-       err = 1; /* continue */
-       if (quirk && quirk->ifnum != QUIRK_NO_INTERFACE) {
-               /* need some special handlings */
-               if ((err = snd_usb_create_quirk(chip, intf, &usb_audio_driver, quirk)) < 0)
-                       goto __error;
-       }
-
        /*
         * For devices with more than one control interface, we assume the
         * first contains the audio controls. We might need a more specific
@@ -508,6 +501,14 @@ static void *snd_usb_audio_probe(struct usb_device *dev,
        if (!chip->ctrl_intf)
                chip->ctrl_intf = alts;
 
+       chip->txfr_quirk = 0;
+       err = 1; /* continue */
+       if (quirk && quirk->ifnum != QUIRK_NO_INTERFACE) {
+               /* need some special handlings */
+               if ((err = snd_usb_create_quirk(chip, intf, &usb_audio_driver, quirk)) < 0)
+                       goto __error;
+       }
+
        if (err > 0) {
                /* create normal USB audio interfaces */
                if (snd_usb_create_streams(chip, ifnum) < 0 ||
@@ -528,8 +529,11 @@ static void *snd_usb_audio_probe(struct usb_device *dev,
        return chip;
 
  __error:
-       if (chip && !chip->num_interfaces)
-               snd_card_free(chip->card);
+       if (chip) {
+               if (!chip->num_interfaces)
+                       snd_card_free(chip->card);
+               chip->probing = 0;
+       }
        mutex_unlock(&register_mutex);
  __err_val:
        return NULL;
@@ -551,7 +555,7 @@ static void snd_usb_audio_disconnect(struct usb_device *dev, void *ptr)
        chip = ptr;
        card = chip->card;
        mutex_lock(&register_mutex);
-       mutex_lock(&chip->shutdown_mutex);
+       down_write(&chip->shutdown_rwsem);
        chip->shutdown = 1;
        chip->num_interfaces--;
        if (chip->num_interfaces <= 0) {
@@ -569,11 +573,11 @@ static void snd_usb_audio_disconnect(struct usb_device *dev, void *ptr)
                        snd_usb_mixer_disconnect(p);
                }
                usb_chip[chip->index] = NULL;
-               mutex_unlock(&chip->shutdown_mutex);
+               up_write(&chip->shutdown_rwsem);
                mutex_unlock(&register_mutex);
                snd_card_free_when_closed(card);
        } else {
-               mutex_unlock(&chip->shutdown_mutex);
+               up_write(&chip->shutdown_rwsem);
                mutex_unlock(&register_mutex);
        }
 }
@@ -605,16 +609,20 @@ int snd_usb_autoresume(struct snd_usb_audio *chip)
 {
        int err = -ENODEV;
 
+       down_read(&chip->shutdown_rwsem);
        if (!chip->shutdown && !chip->probing)
                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)