diff options
Diffstat (limited to 'sound/usb/card.c')
-rw-r--r-- | sound/usb/card.c | 94 |
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(®ister_mutex); + guard(mutex)(®ister_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(®ister_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(®ister_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)(®ister_mutex); - mutex_lock(®ister_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(®ister_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(®ister_mutex); - } } /* lock the shutdown (disconnect) task and autoresume */ |