From 072de836f53be56a70cecf70b43ae43b7ce17376 Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Mon, 11 Dec 2023 10:08:36 +0000
Subject: [PATCH] mk-rootfs.sh
---
kernel/sound/usb/card.c | 407 +++++++++++++++++++++++++++++++++++++++++++++++++++-------
1 files changed, 357 insertions(+), 50 deletions(-)
diff --git a/kernel/sound/usb/card.c b/kernel/sound/usb/card.c
index 861c041..ee5d28a 100644
--- a/kernel/sound/usb/card.c
+++ b/kernel/sound/usb/card.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* (Tentative) USB Audio Driver for ALSA
*
@@ -8,21 +9,6 @@
* Thomas Sailer (sailer@ife.ee.ethz.ch)
*
* Audio Class 3.0 support by Ruslan Bilovol <ruslan.bilovol@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*
* NOTES:
*
@@ -68,6 +54,7 @@
#include "format.h"
#include "power.h"
#include "stream.h"
+#include "media.h"
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("USB Audio");
@@ -85,8 +72,10 @@
static bool ignore_ctl_error;
static bool autoclock = true;
static char *quirk_alias[SNDRV_CARDS];
+static char *delayed_register[SNDRV_CARDS];
bool snd_usb_use_vmalloc = true;
+bool snd_usb_skip_validation;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for the USB audio adapter.");
@@ -107,8 +96,12 @@
MODULE_PARM_DESC(autoclock, "Enable auto-clock selection for UAC2 devices (default: yes).");
module_param_array(quirk_alias, charp, NULL, 0444);
MODULE_PARM_DESC(quirk_alias, "Quirk aliases, e.g. 0123abcd:5678beef.");
+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_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).");
/*
* we keep the snd_usb_audio_t instances by ourselves for merging
@@ -119,15 +112,119 @@
static struct snd_usb_audio *usb_chip[SNDRV_CARDS];
static struct usb_driver usb_audio_driver;
-/**
- * find_snd_usb_substream - helper API to find usb substream context
- * information using card number, pcm device number and direction.
- * @card_num: card number
- * @pcm_idx: pcm device number
- * @direction: SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE
- * @uchip: substream context.
- * disconnect_cb: callback to use for cleanup on disconnect.
- */
+static struct snd_usb_audio_vendor_ops *usb_vendor_ops;
+
+int snd_vendor_set_ops(struct snd_usb_audio_vendor_ops *ops)
+{
+ if ((!ops->connect) ||
+ (!ops->disconnect) ||
+ (!ops->set_interface) ||
+ (!ops->set_rate) ||
+ (!ops->set_pcm_buf) ||
+ (!ops->set_pcm_intf) ||
+ (!ops->set_pcm_connection) ||
+ (!ops->set_pcm_binterval) ||
+ (!ops->usb_add_ctls))
+ return -EINVAL;
+
+ usb_vendor_ops = ops;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_vendor_set_ops);
+
+struct snd_usb_audio_vendor_ops *snd_vendor_get_ops(void)
+{
+ return usb_vendor_ops;
+}
+
+static int snd_vendor_connect(struct usb_interface *intf)
+{
+ struct snd_usb_audio_vendor_ops *ops = snd_vendor_get_ops();
+
+ if (ops)
+ return ops->connect(intf);
+ return 0;
+}
+
+static void snd_vendor_disconnect(struct usb_interface *intf)
+{
+ struct snd_usb_audio_vendor_ops *ops = snd_vendor_get_ops();
+
+ if (ops)
+ ops->disconnect(intf);
+}
+
+int snd_vendor_set_interface(struct usb_device *udev,
+ struct usb_host_interface *intf,
+ int iface, int alt)
+{
+ struct snd_usb_audio_vendor_ops *ops = snd_vendor_get_ops();
+
+ if (ops)
+ return ops->set_interface(udev, intf, iface, alt);
+ return 0;
+}
+
+int snd_vendor_set_rate(struct usb_interface *intf, int iface, int rate,
+ int alt)
+{
+ struct snd_usb_audio_vendor_ops *ops = snd_vendor_get_ops();
+
+ if (ops)
+ return ops->set_rate(intf, iface, rate, alt);
+ return 0;
+}
+
+int snd_vendor_set_pcm_buf(struct usb_device *udev, int iface)
+{
+ struct snd_usb_audio_vendor_ops *ops = snd_vendor_get_ops();
+
+ if (ops)
+ ops->set_pcm_buf(udev, iface);
+ return 0;
+}
+
+int snd_vendor_set_pcm_intf(struct usb_interface *intf, int iface, int alt,
+ int direction)
+{
+ struct snd_usb_audio_vendor_ops *ops = snd_vendor_get_ops();
+
+ if (ops)
+ return ops->set_pcm_intf(intf, iface, alt, direction);
+ return 0;
+}
+
+int snd_vendor_set_pcm_connection(struct usb_device *udev,
+ enum snd_vendor_pcm_open_close onoff,
+ int direction)
+{
+ struct snd_usb_audio_vendor_ops *ops = snd_vendor_get_ops();
+
+ if (ops)
+ return ops->set_pcm_connection(udev, onoff, direction);
+ return 0;
+}
+
+int snd_vendor_set_pcm_binterval(struct audioformat *fp,
+ struct audioformat *found,
+ int *cur_attr, int *attr)
+{
+ struct snd_usb_audio_vendor_ops *ops = snd_vendor_get_ops();
+
+ if (ops)
+ return ops->set_pcm_binterval(fp, found, cur_attr, attr);
+ return 0;
+}
+
+static int snd_vendor_usb_add_ctls(struct snd_usb_audio *chip)
+{
+ struct snd_usb_audio_vendor_ops *ops = snd_vendor_get_ops();
+
+ if (ops)
+ return ops->usb_add_ctls(chip);
+ return 0;
+}
+
struct snd_usb_substream *find_snd_usb_substream(unsigned int card_num,
unsigned int pcm_idx, unsigned int direction, struct snd_usb_audio
**uchip, void (*disconnect_cb)(struct snd_usb_audio *chip))
@@ -143,7 +240,7 @@
* search using chip->card->number
*/
for (idx = 0; idx < SNDRV_CARDS; idx++) {
- if (!usb_chip[idx] || !usb_chip[idx]->card)
+ if (!usb_chip[idx])
continue;
if (usb_chip[idx]->card->number == card_num) {
chip = usb_chip[idx];
@@ -152,20 +249,19 @@
}
if (!chip || atomic_read(&chip->shutdown)) {
- pr_debug("%s: instance of usb card # %d does not exist\n",
+ pr_debug("%s: instance of usb crad # %d does not exist\n",
__func__, card_num);
goto err;
}
if (pcm_idx >= chip->pcm_devs) {
- usb_audio_err(chip, "%s: invalid pcm dev number %u > %d\n",
- __func__, pcm_idx, chip->pcm_devs);
+ pr_err("%s: invalid pcm dev number %u > %d\n", __func__,
+ pcm_idx, chip->pcm_devs);
goto err;
}
if (direction > SNDRV_PCM_STREAM_CAPTURE) {
- usb_audio_err(chip, "%s: invalid direction %u\n", __func__,
- direction);
+ pr_err("%s: invalid direction %u\n", __func__, direction);
goto err;
}
@@ -173,9 +269,9 @@
if (as->pcm_index == pcm_idx) {
subs = &as->substream[direction];
if (subs->interface < 0 && !subs->data_endpoint &&
- !subs->sync_endpoint) {
- usb_audio_err(chip, "%s: stream disconnected, bail out\n",
- __func__);
+ !subs->sync_endpoint) {
+ pr_debug("%s: stream disconnected, bail out\n",
+ __func__);
subs = NULL;
goto err;
}
@@ -189,7 +285,7 @@
err:
*uchip = chip;
if (!subs)
- pr_err("%s: substream instance not found\n", __func__);
+ pr_debug("%s: substream instance not found\n", __func__);
mutex_unlock(®ister_mutex);
return subs;
}
@@ -313,7 +409,7 @@
dev_warn(&dev->dev,
"unknown interface protocol %#02x, assuming v1\n",
protocol);
- /* fall through */
+ fallthrough;
case UAC_VERSION_1: {
struct uac1_ac_header_descriptor *h1;
@@ -416,6 +512,121 @@
}
/*
+ * Profile name preset table
+ */
+struct usb_audio_device_name {
+ u32 id;
+ const char *vendor_name;
+ const char *product_name;
+ const char *profile_name; /* override card->longname */
+};
+
+#define PROFILE_NAME(vid, pid, vendor, product, profile) \
+ { .id = USB_ID(vid, pid), .vendor_name = (vendor), \
+ .product_name = (product), .profile_name = (profile) }
+#define DEVICE_NAME(vid, pid, vendor, product) \
+ PROFILE_NAME(vid, pid, vendor, product, NULL)
+
+/* vendor/product and profile name presets, sorted in device id order */
+static const struct 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"),
+
+ /* Gigabyte TRX40 Aorus Pro WiFi */
+ PROFILE_NAME(0x0414, 0xa002,
+ "Realtek", "ALC1220-VB-DT", "Realtek-ALC1220-VB-Desktop"),
+
+ /* Creative/E-Mu devices */
+ DEVICE_NAME(0x041e, 0x3010, "Creative Labs", "Sound Blaster MP3+"),
+ /* Creative/Toshiba Multimedia Center SB-0500 */
+ DEVICE_NAME(0x041e, 0x3048, "Toshiba", "SB-0500"),
+
+ DEVICE_NAME(0x046d, 0x0990, "Logitech, Inc.", "QuickCam Pro 9000"),
+
+ /* 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"),
+
+ /* Dell WD15 Dock */
+ PROFILE_NAME(0x0bda, 0x4014, "Dell", "WD15 Dock", "Dell-WD15-Dock"),
+ /* Dell WD19 Dock */
+ PROFILE_NAME(0x0bda, 0x402e, "Dell", "WD19 Dock", "Dell-WD15-Dock"),
+
+ DEVICE_NAME(0x0ccd, 0x0028, "TerraTec", "Aureon5.1MkII"),
+
+ /*
+ * 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"),
+
+ /* MSI TRX40 Creator */
+ PROFILE_NAME(0x0db0, 0x0d64,
+ "Realtek", "ALC1220-VB-DT", "Realtek-ALC1220-VB-Desktop"),
+ /* MSI TRX40 */
+ PROFILE_NAME(0x0db0, 0x543d,
+ "Realtek", "ALC1220-VB-DT", "Realtek-ALC1220-VB-Desktop"),
+
+ /* Stanton/N2IT Final Scratch v1 device ('Scratchamp') */
+ DEVICE_NAME(0x103d, 0x0100, "Stanton", "ScratchAmp"),
+ DEVICE_NAME(0x103d, 0x0101, "Stanton", "ScratchAmp"),
+
+ /* aka. Serato Scratch Live DJ Box */
+ DEVICE_NAME(0x13e5, 0x0001, "Rane", "SL-1"),
+
+ /* Lenovo ThinkStation P620 Rear Line-in, Line-out and Microphone */
+ PROFILE_NAME(0x17aa, 0x1046, "Lenovo", "ThinkStation P620 Rear",
+ "Lenovo-ThinkStation-P620-Rear"),
+ /* Lenovo ThinkStation P620 Internal Speaker + Front Headset */
+ PROFILE_NAME(0x17aa, 0x104d, "Lenovo", "ThinkStation P620 Main",
+ "Lenovo-ThinkStation-P620-Main"),
+
+ /* Asrock TRX40 Creator */
+ PROFILE_NAME(0x26ce, 0x0a01,
+ "Realtek", "ALC1220-VB-DT", "Realtek-ALC1220-VB-Desktop"),
+
+ { } /* terminator */
+};
+
+static const struct usb_audio_device_name *
+lookup_device_name(u32 id)
+{
+ static const struct usb_audio_device_name *p;
+
+ for (p = usb_audio_names; p->id; p++)
+ if (p->id == id)
+ return p;
+ return NULL;
+}
+
+/*
* free the chip instance
*
* here we have to do not much, since pcm and controls are already freed
@@ -441,10 +652,16 @@
const struct snd_usb_audio_quirk *quirk)
{
struct snd_card *card = chip->card;
+ const struct usb_audio_device_name *preset;
+ const char *s = NULL;
- if (quirk && quirk->product_name && *quirk->product_name) {
- strlcpy(card->shortname, quirk->product_name,
- sizeof(card->shortname));
+ preset = lookup_device_name(chip->usb_id);
+ if (preset && preset->product_name)
+ s = preset->product_name;
+ else if (quirk && quirk->product_name)
+ s = quirk->product_name;
+ if (s && *s) {
+ strlcpy(card->shortname, s, sizeof(card->shortname));
return;
}
@@ -466,17 +683,26 @@
const struct snd_usb_audio_quirk *quirk)
{
struct snd_card *card = chip->card;
+ const struct usb_audio_device_name *preset;
+ const char *s = NULL;
int len;
+ preset = lookup_device_name(chip->usb_id);
+
/* shortcut - if any pre-defined string is given, use it */
- if (quirk && quirk->profile_name && *quirk->profile_name) {
- strlcpy(card->longname, quirk->profile_name,
- sizeof(card->longname));
+ if (preset && preset->profile_name)
+ s = preset->profile_name;
+ if (s && *s) {
+ strlcpy(card->longname, s, sizeof(card->longname));
return;
}
- if (quirk && quirk->vendor_name && *quirk->vendor_name) {
- len = strlcpy(card->longname, quirk->vendor_name, sizeof(card->longname));
+ if (preset && preset->vendor_name)
+ s = preset->vendor_name;
+ else if (quirk && quirk->vendor_name)
+ s = quirk->vendor_name;
+ if (s && *s) {
+ len = strlcpy(card->longname, s, sizeof(card->longname));
} else {
/* retrieve the vendor and device strings as longname */
if (dev->descriptor.iManufacturer)
@@ -613,6 +839,21 @@
return false;
}
+static bool check_delayed_register_option(struct snd_usb_audio *chip, int iface)
+{
+ int i;
+ unsigned int 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 iface < inum;
+ }
+
+ return false;
+}
+
static const struct usb_device_id usb_audio_ids[]; /* defined below */
/* look for the corresponding quirk */
@@ -668,6 +909,10 @@
if (err < 0)
return err;
+ err = snd_vendor_connect(intf);
+ if (err)
+ return err;
+
/*
* found a config. now register to ALSA
*/
@@ -688,6 +933,10 @@
}
}
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
*/
@@ -700,7 +949,6 @@
id, &chip);
if (err < 0)
goto __error;
- chip->pm_intf = intf;
break;
} else if (vid[i] != -1 || pid[i] != -1) {
dev_info(&dev->dev,
@@ -717,7 +965,16 @@
goto __error;
}
}
+
+ 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);
+
+ snd_vendor_usb_add_ctls(chip);
/*
* For devices with more than one control interface, we assume the
@@ -746,16 +1003,33 @@
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 */
+ }
+
/* 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)) {
+ if (!snd_usb_registration_quirk(chip, ifnum) &&
+ !check_delayed_register_option(chip, ifnum)) {
err = snd_card_register(chip->card);
if (err < 0)
goto __error;
}
+ if (quirk && quirk->shares_media_device) {
+ /* don't want to fail when snd_media_device_create() fails */
+ snd_media_device_create(chip, intf);
+ }
+
+ if (quirk)
+ chip->quirk_type = quirk->type;
+
usb_chip[chip->index] = chip;
+ chip->intf[chip->num_interfaces] = intf;
chip->num_interfaces++;
usb_set_intfdata(intf, chip);
atomic_dec(&chip->active);
@@ -790,6 +1064,11 @@
card = chip->card;
+ if (chip->disconnect_cb)
+ chip->disconnect_cb(chip);
+
+ snd_vendor_disconnect(intf);
+
mutex_lock(®ister_mutex);
if (atomic_inc_return(&chip->shutdown) == 1) {
struct snd_usb_stream *as;
@@ -814,11 +1093,22 @@
list_for_each(p, &chip->midi_list) {
snd_usbmidi_disconnect(p);
}
+ /*
+ * Nice to check quirk && quirk->shares_media_device and
+ * then call the snd_media_device_delete(). Don't have
+ * access to the quirk here. snd_media_device_delete()
+ * accesses mixer_list
+ */
+ snd_media_device_delete(chip);
+
/* release mixer resources */
list_for_each_entry(mixer, &chip->mixer_list, list) {
snd_usb_mixer_disconnect(mixer);
}
}
+
+ if (chip->quirk_type == QUIRK_SETUP_DISABLE_AUTOSUSPEND)
+ usb_enable_autosuspend(interface_to_usbdev(intf));
chip->num_interfaces--;
if (chip->num_interfaces <= 0) {
@@ -863,19 +1153,37 @@
int snd_usb_autoresume(struct snd_usb_audio *chip)
{
+ int i, err;
+
if (atomic_read(&chip->shutdown))
return -EIO;
- if (atomic_inc_return(&chip->active) == 1)
- return usb_autopm_get_interface(chip->pm_intf);
+ if (atomic_inc_return(&chip->active) != 1)
+ return 0;
+
+ for (i = 0; i < chip->num_interfaces; i++) {
+ err = usb_autopm_get_interface(chip->intf[i]);
+ if (err < 0) {
+ /* rollback */
+ while (--i >= 0)
+ usb_autopm_put_interface(chip->intf[i]);
+ atomic_dec(&chip->active);
+ return err;
+ }
+ }
return 0;
}
void snd_usb_autosuspend(struct snd_usb_audio *chip)
{
+ int i;
+
if (atomic_read(&chip->shutdown))
return;
- if (atomic_dec_and_test(&chip->active))
- usb_autopm_put_interface(chip->pm_intf);
+ if (!atomic_dec_and_test(&chip->active))
+ return;
+
+ for (i = 0; i < chip->num_interfaces; i++)
+ usb_autopm_put_interface(chip->intf[i]);
}
static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
@@ -890,7 +1198,6 @@
if (!chip->num_suspended_intf++) {
list_for_each_entry(as, &chip->pcm_list, list) {
- snd_pcm_suspend_all(as->pcm);
snd_usb_pcm_suspend(as);
as->substream[0].need_setup_ep =
as->substream[1].need_setup_ep = true;
--
Gitblit v1.6.2