summaryrefslogtreecommitdiff
path: root/sound/usb/card.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/usb/card.c')
-rw-r--r--sound/usb/card.c94
1 files changed, 70 insertions, 24 deletions
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 10d9b7285597..1d5a65eac933 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -73,7 +73,7 @@ static bool lowlatency = true;
static char *quirk_alias[SNDRV_CARDS];
static char *delayed_register[SNDRV_CARDS];
static bool implicit_fb[SNDRV_CARDS];
-static unsigned int quirk_flags[SNDRV_CARDS];
+static char *quirk_flags[SNDRV_CARDS];
bool snd_usb_use_vmalloc = true;
bool snd_usb_skip_validation;
@@ -103,13 +103,32 @@ module_param_array(delayed_register, charp, NULL, 0444);
MODULE_PARM_DESC(delayed_register, "Quirk for delayed registration, given by id:iface, e.g. 0123abcd:4.");
module_param_array(implicit_fb, bool, NULL, 0444);
MODULE_PARM_DESC(implicit_fb, "Apply generic implicit feedback sync mode.");
-module_param_array(quirk_flags, uint, NULL, 0444);
-MODULE_PARM_DESC(quirk_flags, "Driver quirk bit flags.");
module_param_named(use_vmalloc, snd_usb_use_vmalloc, bool, 0444);
MODULE_PARM_DESC(use_vmalloc, "Use vmalloc for PCM intermediate buffers (default: yes).");
module_param_named(skip_validation, snd_usb_skip_validation, bool, 0444);
MODULE_PARM_DESC(skip_validation, "Skip unit descriptor validation (default: no).");
+/* protects quirk_flags */
+static DEFINE_MUTEX(quirk_flags_mutex);
+
+static int param_set_quirkp(const char *val,
+ const struct kernel_param *kp)
+{
+ guard(mutex)(&quirk_flags_mutex);
+ return param_set_charp(val, kp);
+}
+
+static const struct kernel_param_ops param_ops_quirkp = {
+ .set = param_set_quirkp,
+ .get = param_get_charp,
+ .free = param_free_charp,
+};
+
+#define param_check_quirkp param_check_charp
+
+module_param_array(quirk_flags, quirkp, NULL, 0644);
+MODULE_PARM_DESC(quirk_flags, "Add/modify USB audio quirks");
+
/*
* we keep the snd_usb_audio_t instances by ourselves for merging
* the all interfaces on the same card as one sound device.
@@ -692,6 +711,31 @@ static void usb_audio_make_longname(struct usb_device *dev,
}
}
+static void snd_usb_init_quirk_flags(int idx, struct snd_usb_audio *chip)
+{
+ size_t i;
+
+ guard(mutex)(&quirk_flags_mutex);
+
+ /* old style option found: the position-based integer value */
+ if (quirk_flags[idx] &&
+ !kstrtou32(quirk_flags[idx], 0, &chip->quirk_flags)) {
+ snd_usb_apply_flag_dbg("module param", chip, chip->quirk_flags);
+ return;
+ }
+
+ /* take the default quirk from the quirk table */
+ snd_usb_init_quirk_flags_table(chip);
+
+ /* add or correct quirk bits from options */
+ for (i = 0; i < ARRAY_SIZE(quirk_flags); i++) {
+ if (!quirk_flags[i] || !*quirk_flags[i])
+ break;
+
+ snd_usb_init_quirk_flags_parse_string(chip, quirk_flags[i]);
+ }
+}
+
/*
* create a chip instance and set its names.
*/
@@ -750,10 +794,7 @@ static int snd_usb_audio_create(struct usb_interface *intf,
INIT_LIST_HEAD(&chip->midi_v2_list);
INIT_LIST_HEAD(&chip->mixer_list);
- if (quirk_flags[idx])
- chip->quirk_flags = quirk_flags[idx];
- else
- snd_usb_init_quirk_flags(chip);
+ snd_usb_init_quirk_flags(idx, chip);
card->private_free = snd_usb_audio_free;
@@ -900,7 +941,7 @@ static int usb_audio_probe(struct usb_interface *intf,
/* check whether it's already registered */
chip = NULL;
- mutex_lock(&register_mutex);
+ guard(mutex)(&register_mutex);
for (i = 0; i < SNDRV_CARDS; i++) {
if (usb_chip[i] && usb_chip[i]->dev == dev) {
if (atomic_read(&usb_chip[i]->shutdown)) {
@@ -1015,7 +1056,6 @@ static int usb_audio_probe(struct usb_interface *intf,
if (platform_ops && platform_ops->connect_cb)
platform_ops->connect_cb(chip);
- mutex_unlock(&register_mutex);
return 0;
@@ -1033,7 +1073,6 @@ static int usb_audio_probe(struct usb_interface *intf,
if (!chip->num_interfaces)
snd_card_free(chip->card);
}
- mutex_unlock(&register_mutex);
return err;
}
@@ -1041,18 +1080,14 @@ static int usb_audio_probe(struct usb_interface *intf,
* we need to take care of counter, since disconnection can be called also
* many times as well as usb_audio_probe().
*/
-static void usb_audio_disconnect(struct usb_interface *intf)
+static bool __usb_audio_disconnect(struct usb_interface *intf,
+ struct snd_usb_audio *chip,
+ struct snd_card *card)
{
- struct snd_usb_audio *chip = usb_get_intfdata(intf);
- struct snd_card *card;
struct list_head *p;
- if (chip == USB_AUDIO_IFACE_UNUSED)
- return;
-
- card = chip->card;
+ guard(mutex)(&register_mutex);
- mutex_lock(&register_mutex);
if (platform_ops && platform_ops->disconnect_cb)
platform_ops->disconnect_cb(chip);
@@ -1098,13 +1133,24 @@ static void usb_audio_disconnect(struct usb_interface *intf)
usb_enable_autosuspend(interface_to_usbdev(intf));
chip->num_interfaces--;
- if (chip->num_interfaces <= 0) {
- usb_chip[chip->index] = NULL;
- mutex_unlock(&register_mutex);
+ if (chip->num_interfaces > 0)
+ return false;
+
+ usb_chip[chip->index] = NULL;
+ return true;
+}
+
+static void usb_audio_disconnect(struct usb_interface *intf)
+{
+ struct snd_usb_audio *chip = usb_get_intfdata(intf);
+ struct snd_card *card;
+
+ if (chip == USB_AUDIO_IFACE_UNUSED)
+ return;
+
+ card = chip->card;
+ if (__usb_audio_disconnect(intf, chip, card))
snd_card_free_when_closed(card);
- } else {
- mutex_unlock(&register_mutex);
- }
}
/* lock the shutdown (disconnect) task and autoresume */