// SPDX-License-Identifier: GPL-2.0-or-later /* * (Tentative) USB Audio Driver for ALSA * * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> * * Many codes borrowed from audio.c by * Alan Cox (alan@lxorguk.ukuu.org.uk) * Thomas Sailer (sailer@ife.ee.ethz.ch) * * Audio Class 3.0 support by Ruslan Bilovol <ruslan.bilovol@gmail.com> * * NOTES: * * - the linked URBs would be preferred but not used so far because of * the instability of unlinking. * - type II is not supported properly. there is no device which supports * this type *correctly*. SB extigy looks as if it supports, but it's * indeed an AC3 stream packed in SPDIF frames (i.e. no real AC3 stream).
*/
/* * 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;
/* * 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;
/* * 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;
}
}
for (i = 0; i < h1->bInCollection; i++)
snd_usb_create_stream(chip, ctrlif, h1->baInterfaceNr[i]);
break;
}
case UAC_VERSION_2: case UAC_VERSION_3: { struct usb_interface_assoc_descriptor *assoc =
usb_ifnum_to_if(dev, ctrlif)->intf_assoc;
if (!assoc) { /* * Firmware writers cannot count to three. So to find * the IAD on the NuForce UDH-100, also check the next * interface.
*/ struct usb_interface *iface =
usb_ifnum_to_if(dev, ctrlif + 1); if (iface &&
iface->intf_assoc &&
iface->intf_assoc->bFunctionClass == USB_CLASS_AUDIO &&
iface->intf_assoc->bFunctionProtocol == UAC_VERSION_2)
assoc = iface->intf_assoc;
}
if (!assoc) {
dev_err(&dev->dev, "Audio class v2/v3 interfaces need an interface association\n"); return -EINVAL;
}
if (protocol == UAC_VERSION_3) { int badd = assoc->bFunctionSubClass;
/* vendor/product and profile name presets, sorted in device id order */ staticconststruct usb_audio_device_name usb_audio_names[] = { /* HP Thunderbolt Dock Audio Headset */
PROFILE_NAME(0x03f0, 0x0269, "HP", "Thunderbolt Dock Audio Headset", "HP-Thunderbolt-Dock-Audio-Headset"), /* HP Thunderbolt Dock Audio Module */
PROFILE_NAME(0x03f0, 0x0567, "HP", "Thunderbolt Dock Audio Module", "HP-Thunderbolt-Dock-Audio-Module"),
/* Two entries for Gigabyte TRX40 Aorus Master: * TRX40 Aorus Master has two USB-audio devices, one for the front * headphone with ESS SABRE9218 DAC chip, while another for the rest * I/O (the rear panel and the front mic) with Realtek ALC1220-VB. * Here we provide two distinct names for making UCM profiles easier.
*/
PROFILE_NAME(0x0414, 0xa000, "Gigabyte", "Aorus Master Front Headphone", "Gigabyte-Aorus-Master-Front-Headphone"),
PROFILE_NAME(0x0414, 0xa001, "Gigabyte", "Aorus Master Main Audio", "Gigabyte-Aorus-Master-Main-Audio"),
/* 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"), /* ASUS PRIME TRX40 PRO-S */
PROFILE_NAME(0x0b05, 0x1918, "Realtek", "ALC1220-VB-DT", "Realtek-ALC1220-VB-Desktop"),
/* * The original product_name is "USB Sound Device", however this name * is also used by the CM106 based cards, so make it unique.
*/
DEVICE_NAME(0x0d8c, 0x0102, NULL, "ICUSBAUDIO7D"),
DEVICE_NAME(0x0d8c, 0x0103, NULL, "Audio Advantage MicroII"),
preset = lookup_device_name(chip->usb_id); if (preset && preset->product_name)
s = preset->product_name; elseif (quirk && quirk->product_name)
s = quirk->product_name; if (s && *s) {
strscpy(card->shortname, s, sizeof(card->shortname)); return;
}
/* retrieve the device string as shortname */ if (!dev->descriptor.iProduct ||
usb_string(dev, dev->descriptor.iProduct,
card->shortname, sizeof(card->shortname)) <= 0) { /* no name available from anywhere, so use ID */
scnprintf(card->shortname, sizeof(card->shortname), "USB Device %#04x:%#04x",
USB_ID_VENDOR(chip->usb_id),
USB_ID_PRODUCT(chip->usb_id));
}
/* shortcut - if any pre-defined string is given, use it */ if (preset && preset->profile_name)
s = preset->profile_name; if (s && *s) {
strscpy(card->longname, s, sizeof(card->longname)); return;
}
if (preset && preset->vendor_name)
s = preset->vendor_name; elseif (quirk && quirk->vendor_name)
s = quirk->vendor_name;
*card->longname = 0; if (s && *s) {
strscpy(card->longname, s, sizeof(card->longname));
} else { /* retrieve the vendor and device strings as longname */ if (dev->descriptor.iManufacturer)
usb_string(dev, dev->descriptor.iManufacturer,
card->longname, sizeof(card->longname)); /* we don't really care if there isn't any vendor string */
} if (*card->longname) {
strim(card->longname); if (*card->longname)
strlcat(card->longname, " ", sizeof(card->longname));
}
switch (snd_usb_get_speed(dev)) { case USB_SPEED_LOW:
strlcat(card->longname, ", low speed", sizeof(card->longname)); break; case USB_SPEED_FULL:
strlcat(card->longname, ", full speed", sizeof(card->longname)); break; case USB_SPEED_HIGH:
strlcat(card->longname, ", high speed", sizeof(card->longname)); break; case USB_SPEED_SUPER:
strlcat(card->longname, ", super speed", sizeof(card->longname)); break; case USB_SPEED_SUPER_PLUS:
strlcat(card->longname, ", super speed plus", sizeof(card->longname)); break; default: break;
}
}
/* * create a chip instance and set its names.
*/ staticint snd_usb_audio_create(struct usb_interface *intf, struct usb_device *dev, int idx, conststruct snd_usb_audio_quirk *quirk, unsignedint usb_id, struct snd_usb_audio **rchip)
{ struct snd_card *card; struct snd_usb_audio *chip; int err; char component[14];
*rchip = NULL;
switch (snd_usb_get_speed(dev)) { case USB_SPEED_LOW: case USB_SPEED_FULL: case USB_SPEED_HIGH: case USB_SPEED_SUPER: case USB_SPEED_SUPER_PLUS: break; default:
dev_err(&dev->dev, "unknown device speed %d\n", snd_usb_get_speed(dev)); return -ENXIO;
}
/* look for a matching quirk alias id */ staticbool get_alias_id(struct usb_device *dev, unsignedint *id)
{ int i; unsignedint src, dst;
for (i = 0; i < ARRAY_SIZE(quirk_alias); i++) { if (!quirk_alias[i] ||
sscanf(quirk_alias[i], "%x:%x", &src, &dst) != 2 ||
src != *id) continue;
dev_info(&dev->dev, "device (%04x:%04x): applying quirk alias %04x:%04x\n",
USB_ID_VENDOR(*id), USB_ID_PRODUCT(*id),
USB_ID_VENDOR(dst), USB_ID_PRODUCT(dst));
*id = dst; returntrue;
}
returnfalse;
}
staticint check_delayed_register_option(struct snd_usb_audio *chip)
{ int i; unsignedint id, inum;
for (i = 0; i < ARRAY_SIZE(delayed_register); i++) { if (delayed_register[i] &&
sscanf(delayed_register[i], "%x:%x", &id, &inum) == 2 &&
id == chip->usb_id) return inum;
}
return -1;
}
staticconststruct usb_device_id usb_audio_ids[]; /* defined below */
/* look for the last interface that matches with our ids and remember it */ staticvoid 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 */ staticconststruct snd_usb_audio_quirk *
get_alias_quirk(struct usb_device *dev, unsignedint id)
{ conststruct usb_device_id *p;
for (p = usb_audio_ids; p->match_flags; p++) { /* FIXME: this checks only vendor:product pair in the list */ if ((p->match_flags & USB_DEVICE_ID_MATCH_DEVICE) ==
USB_DEVICE_ID_MATCH_DEVICE &&
p->idVendor == USB_ID_VENDOR(id) &&
p->idProduct == USB_ID_PRODUCT(id)) return (conststruct snd_usb_audio_quirk *)p->driver_info;
}
return NULL;
}
/* register card if we reach to the last interface or to the specified * one given via option
*/ staticint 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 * * note that this can be called multiple times per a device, when it * includes multiple audio control interfaces. * * thus we check the usb device pointer and creates the card instance * only at the first time. the successive calls of this function will * append the pcm interface to the corresponding card.
*/ staticint usb_audio_probe(struct usb_interface *intf, conststruct usb_device_id *usb_id)
{ struct usb_device *dev = interface_to_usbdev(intf); conststruct snd_usb_audio_quirk *quirk =
(conststruct snd_usb_audio_quirk *)usb_id->driver_info; struct snd_usb_audio *chip; int i, err; struct usb_host_interface *alts; int ifnum;
u32 id;
alts = &intf->altsetting[0];
ifnum = get_iface_desc(alts)->bInterfaceNumber;
id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct)); if (get_alias_id(dev, &id))
quirk = get_alias_quirk(dev, id); if (quirk && quirk->ifnum >= 0 && ifnum != quirk->ifnum) return -ENXIO; if (quirk && quirk->ifnum == QUIRK_NODEV_INTERFACE) return -ENODEV;
/* check whether it's already registered */
chip = NULL;
mutex_lock(®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)) {
dev_err(&dev->dev, "USB device is in the shutdown state, cannot create a card instance\n");
err = -EIO; goto __error;
}
chip = usb_chip[i];
atomic_inc(&chip->active); /* avoid autopm */ break;
}
} if (! chip) {
err = snd_usb_apply_boot_quirk_once(dev, intf, quirk, id); if (err < 0) goto __error;
/* it's a fresh one. * now look for an empty slot and create a new card instance
*/ for (i = 0; i < SNDRV_CARDS; i++) if (!usb_chip[i] &&
(vid[i] == -1 || vid[i] == USB_ID_VENDOR(id)) &&
(pid[i] == -1 || pid[i] == USB_ID_PRODUCT(id))) { if (enable[i]) {
err = snd_usb_audio_create(intf, dev, i, quirk,
id, &chip); if (err < 0) goto __error; break;
} elseif (vid[i] != -1 || pid[i] != -1) {
dev_info(&dev->dev, "device (%04x:%04x) is disabled\n",
USB_ID_VENDOR(id),
USB_ID_PRODUCT(id));
err = -ENOENT; goto __error;
}
} if (!chip) {
dev_err(&dev->dev, "no available usb audio device\n");
err = -ENODEV; goto __error;
}
find_last_interface(chip);
}
if (chip->num_interfaces >= MAX_CARD_INTERFACES) {
dev_info(&dev->dev, "Too many interfaces assigned to the single USB-audio card\n");
err = -EINVAL; goto __error;
}
dev_set_drvdata(&dev->dev, chip);
if (ignore_ctl_error)
chip->quirk_flags |= QUIRK_FLAG_IGNORE_CTL_ERROR;
if (chip->quirk_flags & QUIRK_FLAG_DISABLE_AUTOSUSPEND)
usb_disable_autosuspend(interface_to_usbdev(intf));
/* * For devices with more than one control interface, we assume the * first contains the audio controls. We might need a more specific * check here in the future.
*/ if (!chip->ctrl_intf)
chip->ctrl_intf = alts;
err = 1; /* continue */ if (quirk && quirk->ifnum != QUIRK_NO_INTERFACE) { /* need some special handlings */
err = snd_usb_create_quirk(chip, intf, &usb_audio_driver, quirk); if (err < 0) goto __error;
}
if (err > 0) { /* create normal USB audio interfaces */
err = snd_usb_create_streams(chip, ifnum); if (err < 0) goto __error;
err = snd_usb_create_mixer(chip, ifnum); if (err < 0) goto __error;
}
if (chip->need_delayed_register) {
dev_info(&dev->dev, "Found post-registration device assignment: %08x:%02x\n",
chip->usb_id, ifnum);
chip->need_delayed_register = false; /* clear again */
}
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 */
snd_media_device_create(chip, intf);
}
if (platform_ops && platform_ops->connect_cb)
platform_ops->connect_cb(chip);
mutex_unlock(®ister_mutex);
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.
*/
atomic_dec(&chip->active); if (!chip->num_interfaces)
snd_card_free(chip->card);
}
mutex_unlock(®ister_mutex); return err;
}
/* * we need to take care of counter, since disconnection can be called also * many times as well as usb_audio_probe().
*/ staticvoid usb_audio_disconnect(struct usb_interface *intf)
{ 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;
mutex_lock(®ister_mutex); if (platform_ops && platform_ops->disconnect_cb)
platform_ops->disconnect_cb(chip);
/* * ALSA leaves material resumption to user space * we just notify and restart the mixers
*/
list_for_each_entry(mixer, &chip->mixer_list, list) {
err = snd_usb_mixer_resume(mixer); if (err < 0) goto err_out;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.