diff options
Diffstat (limited to 'sound/usb/card.c')
| -rw-r--r-- | sound/usb/card.c | 304 |
1 files changed, 259 insertions, 45 deletions
diff --git a/sound/usb/card.c b/sound/usb/card.c index 376962291c4d..270dad84d825 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -44,6 +44,7 @@ #include "usbaudio.h" #include "card.h" #include "midi.h" +#include "midi2.h" #include "mixer.h" #include "proc.h" #include "quirks.h" @@ -72,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; @@ -102,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. @@ -117,6 +137,95 @@ MODULE_PARM_DESC(skip_validation, "Skip unit descriptor validation (default: no) static DEFINE_MUTEX(register_mutex); static struct snd_usb_audio *usb_chip[SNDRV_CARDS]; static struct usb_driver usb_audio_driver; +static struct snd_usb_platform_ops *platform_ops; + +/* + * Register platform specific operations that will be notified on events + * which occur in USB SND. The platform driver can utilize this path to + * enable features, such as USB audio offloading, which allows for audio data + * to be queued by an audio DSP. + * + * Only one set of platform operations can be registered to USB SND. The + * platform register operation is protected by the register_mutex. + */ +int snd_usb_register_platform_ops(struct snd_usb_platform_ops *ops) +{ + guard(mutex)(®ister_mutex); + if (platform_ops) + return -EEXIST; + + platform_ops = ops; + return 0; +} +EXPORT_SYMBOL_GPL(snd_usb_register_platform_ops); + +/* + * Unregisters the current set of platform operations. This allows for + * a new set to be registered if required. + * + * The platform unregister operation is protected by the register_mutex. + */ +int snd_usb_unregister_platform_ops(void) +{ + guard(mutex)(®ister_mutex); + platform_ops = NULL; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_usb_unregister_platform_ops); + +/* + * in case the platform driver was not ready at the time of USB SND + * device connect, expose an API to discover all connected USB devices + * so it can populate any dependent resources/structures. + */ +void snd_usb_rediscover_devices(void) +{ + int i; + + guard(mutex)(®ister_mutex); + + if (!platform_ops || !platform_ops->connect_cb) + return; + + for (i = 0; i < SNDRV_CARDS; i++) { + if (usb_chip[i]) + platform_ops->connect_cb(usb_chip[i]); + } +} +EXPORT_SYMBOL_GPL(snd_usb_rediscover_devices); + +/* + * Checks to see if requested audio profile, i.e sample rate, # of + * channels, etc... is supported by the substream associated to the + * USB audio device. + */ +struct snd_usb_stream * +snd_usb_find_suppported_substream(int card_idx, struct snd_pcm_hw_params *params, + int direction) +{ + struct snd_usb_audio *chip; + struct snd_usb_substream *subs; + struct snd_usb_stream *as; + + /* + * Register mutex is held when populating and clearing usb_chip + * array. + */ + guard(mutex)(®ister_mutex); + chip = usb_chip[card_idx]; + + if (chip && enable[card_idx]) { + list_for_each_entry(as, &chip->pcm_list, list) { + subs = &as->substream[direction]; + if (snd_usb_find_substream_format(subs, params)) + return as; + } + } + + return NULL; +} +EXPORT_SYMBOL_GPL(snd_usb_find_suppported_substream); /* * disconnect streams @@ -178,9 +287,8 @@ static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int int if ((altsd->bInterfaceClass == USB_CLASS_AUDIO || altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC) && altsd->bInterfaceSubClass == USB_SUBCLASS_MIDISTREAMING) { - int err = __snd_usbmidi_create(chip->card, iface, - &chip->midi_list, NULL, - chip->usb_id); + int err = snd_usb_midi_v2_create(chip, iface, NULL, + chip->usb_id); if (err < 0) { dev_err(&dev->dev, "%u:%d: cannot create sequencer device\n", @@ -206,6 +314,8 @@ static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int int return -EINVAL; } + snd_usb_add_ctrl_interface_link(chip, interface, ctrlif); + if (! snd_usb_parse_audio_interface(chip, interface)) { usb_set_interface(dev, interface, 0); /* reset the current interface */ return usb_driver_claim_interface(&usb_audio_driver, iface, @@ -382,11 +492,25 @@ static const struct usb_audio_device_name usb_audio_names[] = { /* Creative/Toshiba Multimedia Center SB-0500 */ DEVICE_NAME(0x041e, 0x3048, "Toshiba", "SB-0500"), + /* Logitech Audio Devices */ + DEVICE_NAME(0x046d, 0x0867, "Logitech, Inc.", "Logi-MeetUp"), + DEVICE_NAME(0x046d, 0x0874, "Logitech, Inc.", "Logi-Tap-Audio"), + DEVICE_NAME(0x046d, 0x087c, "Logitech, Inc.", "Logi-Huddle"), + DEVICE_NAME(0x046d, 0x0898, "Logitech, Inc.", "Logi-RB-Audio"), + DEVICE_NAME(0x046d, 0x08d2, "Logitech, Inc.", "Logi-RBM-Audio"), DEVICE_NAME(0x046d, 0x0990, "Logitech, Inc.", "QuickCam Pro 9000"), DEVICE_NAME(0x05e1, 0x0408, "Syntek", "STK1160"), DEVICE_NAME(0x05e1, 0x0480, "Hauppauge", "Woodbury"), + /* ASUS ROG Zenith II: this machine has also two devices, one for + * the front headphone and another for the rest + */ + PROFILE_NAME(0x0b05, 0x1915, "ASUS", "Zenith II Front Headphone", + "Zenith-II-Front-Headphone"), + PROFILE_NAME(0x0b05, 0x1916, "ASUS", "Zenith II Main Audio", + "Zenith-II-Main-Audio"), + /* ASUS ROG Strix */ PROFILE_NAME(0x0b05, 0x1917, "Realtek", "ALC1220-VB-DT", "Realtek-ALC1220-VB-Desktop"), @@ -417,6 +541,10 @@ static const struct usb_audio_device_name usb_audio_names[] = { DEVICE_NAME(0x0fd9, 0x0008, "Hauppauge", "HVR-950Q"), + /* Dock/Stand for HP Engage Go */ + PROFILE_NAME(0x103c, 0x830a, "HP", "HP Engage Go Dock", + "HP-Engage-Go-Dock"), + /* Stanton/N2IT Final Scratch v1 device ('Scratchamp') */ DEVICE_NAME(0x103d, 0x0100, "Stanton", "ScratchAmp"), DEVICE_NAME(0x103d, 0x0101, "Stanton", "ScratchAmp"), @@ -477,6 +605,7 @@ static void snd_usb_audio_free(struct snd_card *card) struct snd_usb_audio *chip = card->private_data; snd_usb_endpoint_free_all(chip); + snd_usb_midi_v2_free_all(chip); mutex_destroy(&chip->mutex); if (!atomic_read(&chip->shutdown)) @@ -506,9 +635,10 @@ static void usb_audio_make_shortname(struct usb_device *dev, usb_string(dev, dev->descriptor.iProduct, card->shortname, sizeof(card->shortname)) <= 0) { /* no name available from anywhere, so use ID */ - sprintf(card->shortname, "USB Device %#04x:%#04x", - USB_ID_VENDOR(chip->usb_id), - USB_ID_PRODUCT(chip->usb_id)); + scnprintf(card->shortname, sizeof(card->shortname), + "USB Device %#04x:%#04x", + USB_ID_VENDOR(chip->usb_id), + USB_ID_PRODUCT(chip->usb_id)); } strim(card->shortname); @@ -581,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. */ @@ -601,7 +756,6 @@ static int snd_usb_audio_create(struct usb_interface *intf, case USB_SPEED_LOW: case USB_SPEED_FULL: case USB_SPEED_HIGH: - case USB_SPEED_WIRELESS: case USB_SPEED_SUPER: case USB_SPEED_SUPER_PLUS: break; @@ -635,19 +789,18 @@ static int snd_usb_audio_create(struct usb_interface *intf, INIT_LIST_HEAD(&chip->pcm_list); INIT_LIST_HEAD(&chip->ep_list); INIT_LIST_HEAD(&chip->iface_ref_list); + INIT_LIST_HEAD(&chip->clock_ref_list); INIT_LIST_HEAD(&chip->midi_list); + 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; - strcpy(card->driver, "USB-Audio"); - sprintf(component, "USB%04x:%04x", - USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id)); + strscpy(card->driver, "USB-Audio"); + scnprintf(component, sizeof(component), "USB%04x:%04x", + USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id)); snd_component_add(card, component); usb_audio_make_shortname(dev, chip, quirk); @@ -681,7 +834,7 @@ static bool get_alias_id(struct usb_device *dev, unsigned int *id) return false; } -static bool check_delayed_register_option(struct snd_usb_audio *chip, int iface) +static int check_delayed_register_option(struct snd_usb_audio *chip) { int i; unsigned int id, inum; @@ -690,14 +843,31 @@ static bool check_delayed_register_option(struct snd_usb_audio *chip, int iface) if (delayed_register[i] && sscanf(delayed_register[i], "%x:%x", &id, &inum) == 2 && id == chip->usb_id) - return inum != iface; + return inum; } - return false; + return -1; } static const struct usb_device_id usb_audio_ids[]; /* defined below */ +/* look for the last interface that matches with our ids and remember it */ +static void find_last_interface(struct snd_usb_audio *chip) +{ + struct usb_host_config *config = chip->dev->actconfig; + struct usb_interface *intf; + int i; + + if (!config) + return; + for (i = 0; i < config->desc.bNumInterfaces; i++) { + intf = config->interface[i]; + if (usb_match_id(intf, usb_audio_ids)) + chip->last_iface = intf->altsetting[0].desc.bInterfaceNumber; + } + usb_audio_dbg(chip, "Found last interface = %d\n", chip->last_iface); +} + /* look for the corresponding quirk */ static const struct snd_usb_audio_quirk * get_alias_quirk(struct usb_device *dev, unsigned int id) @@ -716,6 +886,24 @@ get_alias_quirk(struct usb_device *dev, unsigned int id) return NULL; } +/* register card if we reach to the last interface or to the specified + * one given via option + */ +static int try_to_register_card(struct snd_usb_audio *chip, int ifnum) +{ + struct usb_interface *iface; + + if (check_delayed_register_option(chip) == ifnum || + chip->last_iface == ifnum) + return snd_card_register(chip->card); + + iface = usb_ifnum_to_if(chip->dev, chip->last_iface); + if (iface && usb_interface_claimed(iface)) + return snd_card_register(chip->card); + + return 0; +} + /* * probe the active usb device * @@ -759,7 +947,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)) { @@ -804,6 +992,7 @@ static int usb_audio_probe(struct usb_interface *intf, err = -ENODEV; goto __error; } + find_last_interface(chip); } if (chip->num_interfaces >= MAX_CARD_INTERFACES) { @@ -853,15 +1042,9 @@ static int usb_audio_probe(struct usb_interface *intf, chip->need_delayed_register = false; /* clear again */ } - /* we are allowed to call snd_card_register() many times, but first - * check to see if a device needs to skip it or do anything special - */ - if (!snd_usb_registration_quirk(chip, ifnum) && - !check_delayed_register_option(chip, ifnum)) { - err = snd_card_register(chip->card); - if (err < 0) - goto __error; - } + err = try_to_register_card(chip, ifnum); + if (err < 0) + goto __error_no_register; if (chip->quirk_flags & QUIRK_FLAG_SHARE_MEDIA_DEVICE) { /* don't want to fail when snd_media_device_create() fails */ @@ -876,10 +1059,18 @@ static int usb_audio_probe(struct usb_interface *intf, chip->num_interfaces++; usb_set_intfdata(intf, chip); atomic_dec(&chip->active); - mutex_unlock(®ister_mutex); + + if (platform_ops && platform_ops->connect_cb) + platform_ops->connect_cb(chip); + return 0; __error: + /* in the case of error in secondary interface, still try to register */ + if (chip) + try_to_register_card(chip, ifnum); + + __error_no_register: if (chip) { /* chip->active is inside the chip->card object, * decrement before memory is possibly returned. @@ -888,7 +1079,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; } @@ -896,18 +1086,17 @@ 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; + guard(mutex)(®ister_mutex); - card = chip->card; + if (platform_ops && platform_ops->disconnect_cb) + platform_ops->disconnect_cb(chip); - mutex_lock(®ister_mutex); if (atomic_inc_return(&chip->shutdown) == 1) { struct snd_usb_stream *as; struct snd_usb_endpoint *ep; @@ -931,6 +1120,7 @@ static void usb_audio_disconnect(struct usb_interface *intf) list_for_each(p, &chip->midi_list) { snd_usbmidi_disconnect(p); } + snd_usb_midi_v2_disconnect_all(chip); /* * Nice to check quirk && quirk->shares_media_device and * then call the snd_media_device_delete(). Don't have @@ -949,13 +1139,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 */ @@ -978,6 +1179,7 @@ int snd_usb_lock_shutdown(struct snd_usb_audio *chip) wake_up(&chip->shutdown_wait); return err; } +EXPORT_SYMBOL_GPL(snd_usb_lock_shutdown); /* autosuspend and unlock the shutdown */ void snd_usb_unlock_shutdown(struct snd_usb_audio *chip) @@ -986,6 +1188,7 @@ void snd_usb_unlock_shutdown(struct snd_usb_audio *chip) if (atomic_dec_and_test(&chip->usage_count)) wake_up(&chip->shutdown_wait); } +EXPORT_SYMBOL_GPL(snd_usb_unlock_shutdown); int snd_usb_autoresume(struct snd_usb_audio *chip) { @@ -1008,6 +1211,7 @@ int snd_usb_autoresume(struct snd_usb_audio *chip) } return 0; } +EXPORT_SYMBOL_GPL(snd_usb_autoresume); void snd_usb_autosuspend(struct snd_usb_audio *chip) { @@ -1021,6 +1225,7 @@ void snd_usb_autosuspend(struct snd_usb_audio *chip) for (i = 0; i < chip->num_interfaces; i++) usb_autopm_put_interface(chip->intf[i]); } +EXPORT_SYMBOL_GPL(snd_usb_autosuspend); static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message) { @@ -1042,6 +1247,7 @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message) snd_usbmidi_suspend(p); list_for_each_entry(mixer, &chip->mixer_list, list) snd_usb_mixer_suspend(mixer); + snd_usb_midi_v2_suspend_all(chip); } if (!PMSG_IS_AUTO(message) && !chip->system_suspend) { @@ -1049,6 +1255,9 @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message) chip->system_suspend = chip->num_suspended_intf; } + if (platform_ops && platform_ops->suspend_cb) + platform_ops->suspend_cb(intf, message); + return 0; } @@ -1087,6 +1296,11 @@ static int usb_audio_resume(struct usb_interface *intf) snd_usbmidi_resume(p); } + snd_usb_midi_v2_resume_all(chip); + + if (platform_ops && platform_ops->resume_cb) + platform_ops->resume_cb(intf); + out: if (chip->num_suspended_intf == chip->system_suspend) { snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0); |
