2019-05-27 13:55:05 +07:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2010-03-05 01:46:13 +07:00
|
|
|
/*
|
|
|
|
* (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)
|
|
|
|
*
|
2018-03-21 07:03:59 +07:00
|
|
|
* Audio Class 3.0 support by Ruslan Bilovol <ruslan.bilovol@gmail.com>
|
2010-03-05 01:46:13 +07:00
|
|
|
*
|
|
|
|
* 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).
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include <linux/bitops.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/list.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/string.h>
|
2011-03-21 18:00:00 +07:00
|
|
|
#include <linux/ctype.h>
|
2010-03-05 01:46:13 +07:00
|
|
|
#include <linux/usb.h>
|
|
|
|
#include <linux/moduleparam.h>
|
|
|
|
#include <linux/mutex.h>
|
|
|
|
#include <linux/usb/audio.h>
|
2010-03-12 03:13:20 +07:00
|
|
|
#include <linux/usb/audio-v2.h>
|
2018-03-21 07:03:59 +07:00
|
|
|
#include <linux/usb/audio-v3.h>
|
2011-07-15 23:38:28 +07:00
|
|
|
#include <linux/module.h>
|
2010-03-05 01:46:13 +07:00
|
|
|
|
2011-05-25 14:09:01 +07:00
|
|
|
#include <sound/control.h>
|
2010-03-05 01:46:13 +07:00
|
|
|
#include <sound/core.h>
|
|
|
|
#include <sound/info.h>
|
|
|
|
#include <sound/pcm.h>
|
|
|
|
#include <sound/pcm_params.h>
|
|
|
|
#include <sound/initval.h>
|
|
|
|
|
|
|
|
#include "usbaudio.h"
|
|
|
|
#include "card.h"
|
|
|
|
#include "midi.h"
|
2010-03-12 03:13:23 +07:00
|
|
|
#include "mixer.h"
|
2010-03-05 01:46:13 +07:00
|
|
|
#include "proc.h"
|
|
|
|
#include "quirks.h"
|
|
|
|
#include "endpoint.h"
|
|
|
|
#include "helper.h"
|
|
|
|
#include "debug.h"
|
|
|
|
#include "pcm.h"
|
|
|
|
#include "format.h"
|
2011-03-11 20:51:12 +07:00
|
|
|
#include "power.h"
|
2011-09-12 23:54:12 +07:00
|
|
|
#include "stream.h"
|
media: sound/usb: Use Media Controller API to share media resources
Media Device Allocator API to allows multiple drivers share a media device.
This API solves a very common use-case for media devices where one physical
device (an USB stick) provides both audio and video. When such media device
exposes a standard USB Audio class, a proprietary Video class, two or more
independent drivers will share a single physical USB bridge. In such cases,
it is necessary to coordinate access to the shared resource.
Using this API, drivers can allocate a media device with the shared struct
device as the key. Once the media device is allocated by a driver, other
drivers can get a reference to it. The media device is released when all
the references are released.
Change the ALSA driver to use the Media Controller API to share media
resources with DVB, and V4L2 drivers on a AU0828 media device.
The Media Controller specific initialization is done after sound card is
registered. ALSA creates Media interface and entity function graph nodes
for Control, Mixer, PCM Playback, and PCM Capture devices.
snd_usb_hw_params() will call Media Controller enable source handler
interface to request the media resource. If resource request is granted,
it will release it from snd_usb_hw_free(). If resource is busy, -EBUSY is
returned.
Media specific cleanup is done in usb_audio_disconnect().
Reviewed-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Shuah Khan <shuah@kernel.org>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2019-04-02 07:40:22 +07:00
|
|
|
#include "media.h"
|
2010-03-05 01:46:13 +07:00
|
|
|
|
|
|
|
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
|
|
|
|
MODULE_DESCRIPTION("USB Audio");
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_SUPPORTED_DEVICE("{{Generic,USB Audio}}");
|
|
|
|
|
|
|
|
|
|
|
|
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
|
|
|
|
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
|
2011-12-15 10:19:36 +07:00
|
|
|
static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */
|
2010-03-05 01:46:13 +07:00
|
|
|
/* Vendor/product IDs for this card */
|
|
|
|
static int vid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 };
|
|
|
|
static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 };
|
|
|
|
static int device_setup[SNDRV_CARDS]; /* device parameter for this card */
|
2011-12-15 10:19:36 +07:00
|
|
|
static bool ignore_ctl_error;
|
2013-04-04 04:18:56 +07:00
|
|
|
static bool autoclock = true;
|
ALSA: usb-audio: Add quirk_alias option
This patch adds a new option "quirk_alias" to snd-usb-audio driver for
allowing user to pass the quirk alias list. A quirk alias consists of
a string form like 0123abcd:5678beef, which makes to apply a quirk to
a device with USB ID 0123:abcd treated as if it were 5678:beef.
This feature is useful to test an existing quirk, typically for a
newer model of the same vendor, without patching / rebuilding the
kernel driver.
The current implementation is fairly simplistic: since there is no API
for matching a usb_device_id to the given ID pair, it has an open code
to loop over the id table and matches only with vendor:product pair.
So far, this is OK, as all existing entries are with vendor:product
pairs, indeed. Once when we have another matching entry, however,
we'd need to update get_alias_quirk() as well.
Note that this option is provided only for testing / development. If
you want to have a proper support, contact to upstream for adding the
matching quirk in the driver code statically.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2016-01-11 20:39:12 +07:00
|
|
|
static char *quirk_alias[SNDRV_CARDS];
|
2020-03-25 17:33:20 +07:00
|
|
|
static char *delayed_register[SNDRV_CARDS];
|
2010-03-05 01:46:13 +07:00
|
|
|
|
2018-05-27 18:01:17 +07:00
|
|
|
bool snd_usb_use_vmalloc = true;
|
2019-11-14 23:56:13 +07:00
|
|
|
bool snd_usb_skip_validation;
|
2018-05-27 18:01:17 +07:00
|
|
|
|
2010-03-05 01:46:13 +07:00
|
|
|
module_param_array(index, int, NULL, 0444);
|
|
|
|
MODULE_PARM_DESC(index, "Index value for the USB audio adapter.");
|
|
|
|
module_param_array(id, charp, NULL, 0444);
|
|
|
|
MODULE_PARM_DESC(id, "ID string for the USB audio adapter.");
|
|
|
|
module_param_array(enable, bool, NULL, 0444);
|
|
|
|
MODULE_PARM_DESC(enable, "Enable USB audio adapter.");
|
|
|
|
module_param_array(vid, int, NULL, 0444);
|
|
|
|
MODULE_PARM_DESC(vid, "Vendor ID for the USB audio device.");
|
|
|
|
module_param_array(pid, int, NULL, 0444);
|
|
|
|
MODULE_PARM_DESC(pid, "Product ID for the USB audio device.");
|
|
|
|
module_param_array(device_setup, int, NULL, 0444);
|
|
|
|
MODULE_PARM_DESC(device_setup, "Specific device setup (if needed).");
|
|
|
|
module_param(ignore_ctl_error, bool, 0444);
|
|
|
|
MODULE_PARM_DESC(ignore_ctl_error,
|
|
|
|
"Ignore errors from USB controller for mixer interfaces.");
|
2013-04-04 04:18:56 +07:00
|
|
|
module_param(autoclock, bool, 0444);
|
|
|
|
MODULE_PARM_DESC(autoclock, "Enable auto-clock selection for UAC2 devices (default: yes).");
|
ALSA: usb-audio: Add quirk_alias option
This patch adds a new option "quirk_alias" to snd-usb-audio driver for
allowing user to pass the quirk alias list. A quirk alias consists of
a string form like 0123abcd:5678beef, which makes to apply a quirk to
a device with USB ID 0123:abcd treated as if it were 5678:beef.
This feature is useful to test an existing quirk, typically for a
newer model of the same vendor, without patching / rebuilding the
kernel driver.
The current implementation is fairly simplistic: since there is no API
for matching a usb_device_id to the given ID pair, it has an open code
to loop over the id table and matches only with vendor:product pair.
So far, this is OK, as all existing entries are with vendor:product
pairs, indeed. Once when we have another matching entry, however,
we'd need to update get_alias_quirk() as well.
Note that this option is provided only for testing / development. If
you want to have a proper support, contact to upstream for adding the
matching quirk in the driver code statically.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2016-01-11 20:39:12 +07:00
|
|
|
module_param_array(quirk_alias, charp, NULL, 0444);
|
|
|
|
MODULE_PARM_DESC(quirk_alias, "Quirk aliases, e.g. 0123abcd:5678beef.");
|
2020-03-25 17:33:20 +07:00
|
|
|
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.");
|
2018-05-27 18:01:17 +07:00
|
|
|
module_param_named(use_vmalloc, snd_usb_use_vmalloc, bool, 0444);
|
|
|
|
MODULE_PARM_DESC(use_vmalloc, "Use vmalloc for PCM intermediate buffers (default: yes).");
|
2019-11-14 23:56:13 +07:00
|
|
|
module_param_named(skip_validation, snd_usb_skip_validation, bool, 0444);
|
|
|
|
MODULE_PARM_DESC(skip_validation, "Skip unit descriptor validation (default: no).");
|
2010-03-05 01:46:13 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* we keep the snd_usb_audio_t instances by ourselves for merging
|
|
|
|
* the all interfaces on the same card as one sound device.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static DEFINE_MUTEX(register_mutex);
|
|
|
|
static struct snd_usb_audio *usb_chip[SNDRV_CARDS];
|
|
|
|
static struct usb_driver usb_audio_driver;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* disconnect streams
|
2014-10-31 17:00:23 +07:00
|
|
|
* called from usb_audio_disconnect()
|
2010-03-05 01:46:13 +07:00
|
|
|
*/
|
2014-10-31 17:24:32 +07:00
|
|
|
static void snd_usb_stream_disconnect(struct snd_usb_stream *as)
|
2010-03-05 01:46:13 +07:00
|
|
|
{
|
|
|
|
int idx;
|
|
|
|
struct snd_usb_substream *subs;
|
|
|
|
|
|
|
|
for (idx = 0; idx < 2; idx++) {
|
|
|
|
subs = &as->substream[idx];
|
|
|
|
if (!subs->num_formats)
|
2010-09-08 13:27:02 +07:00
|
|
|
continue;
|
2010-03-05 01:46:13 +07:00
|
|
|
subs->interface = -1;
|
2012-04-12 18:51:12 +07:00
|
|
|
subs->data_endpoint = NULL;
|
|
|
|
subs->sync_endpoint = NULL;
|
2010-03-05 01:46:13 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int interface)
|
|
|
|
{
|
|
|
|
struct usb_device *dev = chip->dev;
|
|
|
|
struct usb_host_interface *alts;
|
|
|
|
struct usb_interface_descriptor *altsd;
|
|
|
|
struct usb_interface *iface = usb_ifnum_to_if(dev, interface);
|
|
|
|
|
|
|
|
if (!iface) {
|
2014-02-26 19:02:17 +07:00
|
|
|
dev_err(&dev->dev, "%u:%d : does not exist\n",
|
|
|
|
ctrlif, interface);
|
2010-03-05 01:46:13 +07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2013-06-15 16:21:09 +07:00
|
|
|
alts = &iface->altsetting[0];
|
|
|
|
altsd = get_iface_desc(alts);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Android with both accessory and audio interfaces enabled gets the
|
|
|
|
* interface numbers wrong.
|
|
|
|
*/
|
|
|
|
if ((chip->usb_id == USB_ID(0x18d1, 0x2d04) ||
|
|
|
|
chip->usb_id == USB_ID(0x18d1, 0x2d05)) &&
|
|
|
|
interface == 0 &&
|
|
|
|
altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC &&
|
|
|
|
altsd->bInterfaceSubClass == USB_SUBCLASS_VENDOR_SPEC) {
|
|
|
|
interface = 2;
|
|
|
|
iface = usb_ifnum_to_if(dev, interface);
|
|
|
|
if (!iface)
|
|
|
|
return -EINVAL;
|
|
|
|
alts = &iface->altsetting[0];
|
|
|
|
altsd = get_iface_desc(alts);
|
|
|
|
}
|
|
|
|
|
2010-03-05 01:46:13 +07:00
|
|
|
if (usb_interface_claimed(iface)) {
|
2014-02-26 19:02:17 +07:00
|
|
|
dev_dbg(&dev->dev, "%d:%d: skipping, already claimed\n",
|
|
|
|
ctrlif, interface);
|
2010-03-05 01:46:13 +07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((altsd->bInterfaceClass == USB_CLASS_AUDIO ||
|
|
|
|
altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC) &&
|
|
|
|
altsd->bInterfaceSubClass == USB_SUBCLASS_MIDISTREAMING) {
|
2016-01-11 17:33:34 +07:00
|
|
|
int err = __snd_usbmidi_create(chip->card, iface,
|
|
|
|
&chip->midi_list, NULL,
|
|
|
|
chip->usb_id);
|
2010-03-05 01:46:13 +07:00
|
|
|
if (err < 0) {
|
2014-02-26 19:02:17 +07:00
|
|
|
dev_err(&dev->dev,
|
|
|
|
"%u:%d: cannot create sequencer device\n",
|
|
|
|
ctrlif, interface);
|
2010-03-05 01:46:13 +07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((altsd->bInterfaceClass != USB_CLASS_AUDIO &&
|
|
|
|
altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC) ||
|
|
|
|
altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING) {
|
2014-02-26 19:02:17 +07:00
|
|
|
dev_dbg(&dev->dev,
|
|
|
|
"%u:%d: skipping non-supported interface %d\n",
|
|
|
|
ctrlif, interface, altsd->bInterfaceClass);
|
2010-03-05 01:46:13 +07:00
|
|
|
/* skip non-supported classes */
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (snd_usb_get_speed(dev) == USB_SPEED_LOW) {
|
2014-02-26 19:02:17 +07:00
|
|
|
dev_err(&dev->dev, "low speed audio streaming not supported\n");
|
2010-03-05 01:46:13 +07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2011-09-12 23:54:12 +07:00
|
|
|
if (! snd_usb_parse_audio_interface(chip, interface)) {
|
2010-03-05 01:46:13 +07:00
|
|
|
usb_set_interface(dev, interface, 0); /* reset the current interface */
|
|
|
|
usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* parse audio control descriptor and create pcm/midi streams
|
|
|
|
*/
|
|
|
|
static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
|
|
|
|
{
|
|
|
|
struct usb_device *dev = chip->dev;
|
|
|
|
struct usb_host_interface *host_iface;
|
|
|
|
struct usb_interface_descriptor *altsd;
|
|
|
|
int i, protocol;
|
|
|
|
|
|
|
|
/* find audiocontrol interface */
|
|
|
|
host_iface = &usb_ifnum_to_if(dev, ctrlif)->altsetting[0];
|
|
|
|
altsd = get_iface_desc(host_iface);
|
|
|
|
protocol = altsd->bInterfaceProtocol;
|
|
|
|
|
|
|
|
switch (protocol) {
|
2010-09-03 15:53:11 +07:00
|
|
|
default:
|
2014-02-26 19:02:17 +07:00
|
|
|
dev_warn(&dev->dev,
|
|
|
|
"unknown interface protocol %#02x, assuming v1\n",
|
|
|
|
protocol);
|
2020-07-09 03:32:36 +07:00
|
|
|
fallthrough;
|
2010-09-03 15:53:11 +07:00
|
|
|
|
2010-03-05 01:46:13 +07:00
|
|
|
case UAC_VERSION_1: {
|
2018-05-04 08:24:01 +07:00
|
|
|
struct uac1_ac_header_descriptor *h1;
|
|
|
|
int rest_bytes;
|
|
|
|
|
|
|
|
h1 = snd_usb_find_csint_desc(host_iface->extra,
|
|
|
|
host_iface->extralen,
|
|
|
|
NULL, UAC_HEADER);
|
2019-01-02 23:12:21 +07:00
|
|
|
if (!h1 || h1->bLength < sizeof(*h1)) {
|
2018-05-04 08:24:01 +07:00
|
|
|
dev_err(&dev->dev, "cannot find UAC_HEADER\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
rest_bytes = (void *)(host_iface->extra +
|
|
|
|
host_iface->extralen) - (void *)h1;
|
|
|
|
|
|
|
|
/* just to be sure -- this shouldn't hit at all */
|
|
|
|
if (rest_bytes <= 0) {
|
|
|
|
dev_err(&dev->dev, "invalid control header\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2010-03-05 01:46:13 +07:00
|
|
|
|
2017-09-22 21:18:53 +07:00
|
|
|
if (rest_bytes < sizeof(*h1)) {
|
|
|
|
dev_err(&dev->dev, "too short v1 buffer descriptor\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2010-03-05 01:46:13 +07:00
|
|
|
if (!h1->bInCollection) {
|
2014-02-26 19:02:17 +07:00
|
|
|
dev_info(&dev->dev, "skipping empty audio interface (v1)\n");
|
2010-03-05 01:46:13 +07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2017-09-22 21:18:53 +07:00
|
|
|
if (rest_bytes < h1->bLength) {
|
|
|
|
dev_err(&dev->dev, "invalid buffer length (v1)\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2010-03-05 01:46:13 +07:00
|
|
|
if (h1->bLength < sizeof(*h1) + h1->bInCollection) {
|
2014-02-26 19:02:17 +07:00
|
|
|
dev_err(&dev->dev, "invalid UAC_HEADER (v1)\n");
|
2010-03-05 01:46:13 +07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < h1->bInCollection; i++)
|
|
|
|
snd_usb_create_stream(chip, ctrlif, h1->baInterfaceNr[i]);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-03-21 07:03:59 +07:00
|
|
|
case UAC_VERSION_2:
|
|
|
|
case UAC_VERSION_3: {
|
2010-03-05 01:46:13 +07:00
|
|
|
struct usb_interface_assoc_descriptor *assoc =
|
|
|
|
usb_ifnum_to_if(dev, ctrlif)->intf_assoc;
|
|
|
|
|
2013-03-12 02:15:34 +07:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2010-03-05 01:46:13 +07:00
|
|
|
if (!assoc) {
|
2018-03-21 07:03:59 +07:00
|
|
|
dev_err(&dev->dev, "Audio class v2/v3 interfaces need an interface association\n");
|
2010-03-05 01:46:13 +07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-05-04 08:24:04 +07:00
|
|
|
if (protocol == UAC_VERSION_3) {
|
|
|
|
int badd = assoc->bFunctionSubClass;
|
|
|
|
|
|
|
|
if (badd != UAC3_FUNCTION_SUBCLASS_FULL_ADC_3_0 &&
|
|
|
|
(badd < UAC3_FUNCTION_SUBCLASS_GENERIC_IO ||
|
|
|
|
badd > UAC3_FUNCTION_SUBCLASS_SPEAKERPHONE)) {
|
|
|
|
dev_err(&dev->dev,
|
|
|
|
"Unsupported UAC3 BADD profile\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
chip->badd_profile = badd;
|
|
|
|
}
|
|
|
|
|
2010-03-05 01:46:13 +07:00
|
|
|
for (i = 0; i < assoc->bInterfaceCount; i++) {
|
|
|
|
int intf = assoc->bFirstInterface + i;
|
|
|
|
|
|
|
|
if (intf != ctrlif)
|
|
|
|
snd_usb_create_stream(chip, ctrlif, intf);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-08-17 15:21:38 +07:00
|
|
|
/*
|
|
|
|
* 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"),
|
|
|
|
|
2020-11-15 22:38:42 +07:00
|
|
|
/* ASUS ROG Strix */
|
|
|
|
PROFILE_NAME(0x0b05, 0x1917,
|
|
|
|
"Realtek", "ALC1220-VB-DT", "Realtek-ALC1220-VB-Desktop"),
|
2020-12-21 15:01:59 +07:00
|
|
|
/* ASUS PRIME TRX40 PRO-S */
|
|
|
|
PROFILE_NAME(0x0b05, 0x1918,
|
|
|
|
"Realtek", "ALC1220-VB-DT", "Realtek-ALC1220-VB-Desktop"),
|
2020-11-15 22:38:42 +07:00
|
|
|
|
2020-08-17 15:21:38 +07:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2010-03-05 01:46:13 +07:00
|
|
|
/*
|
|
|
|
* free the chip instance
|
|
|
|
*
|
|
|
|
* here we have to do not much, since pcm and controls are already freed
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2018-05-27 20:07:01 +07:00
|
|
|
static void snd_usb_audio_free(struct snd_card *card)
|
2010-03-05 01:46:13 +07:00
|
|
|
{
|
2018-05-27 20:07:01 +07:00
|
|
|
struct snd_usb_audio *chip = card->private_data;
|
2014-10-31 17:24:32 +07:00
|
|
|
struct snd_usb_endpoint *ep, *n;
|
2014-06-25 19:24:47 +07:00
|
|
|
|
2014-10-31 17:24:32 +07:00
|
|
|
list_for_each_entry_safe(ep, n, &chip->ep_list, list)
|
|
|
|
snd_usb_endpoint_free(ep);
|
2014-06-25 19:24:47 +07:00
|
|
|
|
2012-04-12 18:51:10 +07:00
|
|
|
mutex_destroy(&chip->mutex);
|
2016-11-15 03:46:47 +07:00
|
|
|
if (!atomic_read(&chip->shutdown))
|
|
|
|
dev_set_drvdata(&chip->dev->dev, NULL);
|
2010-03-05 01:46:13 +07:00
|
|
|
}
|
|
|
|
|
2018-05-02 19:45:37 +07:00
|
|
|
static void usb_audio_make_shortname(struct usb_device *dev,
|
|
|
|
struct snd_usb_audio *chip,
|
|
|
|
const struct snd_usb_audio_quirk *quirk)
|
|
|
|
{
|
|
|
|
struct snd_card *card = chip->card;
|
2020-08-17 15:21:38 +07:00
|
|
|
const struct usb_audio_device_name *preset;
|
|
|
|
const char *s = NULL;
|
|
|
|
|
|
|
|
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));
|
2018-05-02 19:45:37 +07:00
|
|
|
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 */
|
|
|
|
sprintf(card->shortname, "USB Device %#04x:%#04x",
|
|
|
|
USB_ID_VENDOR(chip->usb_id),
|
|
|
|
USB_ID_PRODUCT(chip->usb_id));
|
|
|
|
}
|
|
|
|
|
|
|
|
strim(card->shortname);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usb_audio_make_longname(struct usb_device *dev,
|
|
|
|
struct snd_usb_audio *chip,
|
|
|
|
const struct snd_usb_audio_quirk *quirk)
|
|
|
|
{
|
|
|
|
struct snd_card *card = chip->card;
|
2020-08-17 15:21:38 +07:00
|
|
|
const struct usb_audio_device_name *preset;
|
|
|
|
const char *s = NULL;
|
2018-05-02 19:45:37 +07:00
|
|
|
int len;
|
|
|
|
|
2020-08-17 15:21:38 +07:00
|
|
|
preset = lookup_device_name(chip->usb_id);
|
|
|
|
|
2018-05-02 19:45:37 +07:00
|
|
|
/* shortcut - if any pre-defined string is given, use it */
|
2020-08-17 15:21:38 +07:00
|
|
|
if (preset && preset->profile_name)
|
|
|
|
s = preset->profile_name;
|
|
|
|
if (s && *s) {
|
|
|
|
strlcpy(card->longname, s, sizeof(card->longname));
|
2018-05-02 19:45:37 +07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-08-17 15:21:38 +07:00
|
|
|
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));
|
2018-05-02 19:45:37 +07:00
|
|
|
} else {
|
|
|
|
/* retrieve the vendor and device strings as longname */
|
|
|
|
if (dev->descriptor.iManufacturer)
|
|
|
|
len = usb_string(dev, dev->descriptor.iManufacturer,
|
|
|
|
card->longname, sizeof(card->longname));
|
|
|
|
else
|
|
|
|
len = 0;
|
|
|
|
/* we don't really care if there isn't any vendor string */
|
|
|
|
}
|
|
|
|
if (len > 0) {
|
|
|
|
strim(card->longname);
|
|
|
|
if (*card->longname)
|
|
|
|
strlcat(card->longname, " ", sizeof(card->longname));
|
|
|
|
}
|
|
|
|
|
|
|
|
strlcat(card->longname, card->shortname, sizeof(card->longname));
|
|
|
|
|
|
|
|
len = strlcat(card->longname, " at ", sizeof(card->longname));
|
|
|
|
|
|
|
|
if (len < sizeof(card->longname))
|
|
|
|
usb_make_path(dev, card->longname + len, sizeof(card->longname) - len);
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-03-05 01:46:13 +07:00
|
|
|
/*
|
|
|
|
* create a chip instance and set its names.
|
|
|
|
*/
|
2014-01-29 20:22:20 +07:00
|
|
|
static int snd_usb_audio_create(struct usb_interface *intf,
|
|
|
|
struct usb_device *dev, int idx,
|
2010-03-05 01:46:13 +07:00
|
|
|
const struct snd_usb_audio_quirk *quirk,
|
2017-03-31 16:19:19 +07:00
|
|
|
unsigned int usb_id,
|
2010-03-05 01:46:13 +07:00
|
|
|
struct snd_usb_audio **rchip)
|
|
|
|
{
|
|
|
|
struct snd_card *card;
|
|
|
|
struct snd_usb_audio *chip;
|
2018-05-02 19:45:37 +07:00
|
|
|
int err;
|
2010-03-05 01:46:13 +07:00
|
|
|
char component[14];
|
|
|
|
|
|
|
|
*rchip = NULL;
|
|
|
|
|
2010-08-14 02:42:07 +07:00
|
|
|
switch (snd_usb_get_speed(dev)) {
|
|
|
|
case USB_SPEED_LOW:
|
|
|
|
case USB_SPEED_FULL:
|
|
|
|
case USB_SPEED_HIGH:
|
2013-10-02 02:32:15 +07:00
|
|
|
case USB_SPEED_WIRELESS:
|
2010-08-14 02:42:07 +07:00
|
|
|
case USB_SPEED_SUPER:
|
2016-05-04 19:18:39 +07:00
|
|
|
case USB_SPEED_SUPER_PLUS:
|
2010-08-14 02:42:07 +07:00
|
|
|
break;
|
|
|
|
default:
|
2014-02-26 19:02:17 +07:00
|
|
|
dev_err(&dev->dev, "unknown device speed %d\n", snd_usb_get_speed(dev));
|
2010-03-05 01:46:13 +07:00
|
|
|
return -ENXIO;
|
|
|
|
}
|
|
|
|
|
2014-01-29 20:22:20 +07:00
|
|
|
err = snd_card_new(&intf->dev, index[idx], id[idx], THIS_MODULE,
|
2018-05-27 20:07:01 +07:00
|
|
|
sizeof(*chip), &card);
|
2010-03-05 01:46:13 +07:00
|
|
|
if (err < 0) {
|
2014-02-26 19:02:17 +07:00
|
|
|
dev_err(&dev->dev, "cannot create card instance %d\n", idx);
|
2010-03-05 01:46:13 +07:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2018-05-27 20:07:01 +07:00
|
|
|
chip = card->private_data;
|
2012-04-12 18:51:10 +07:00
|
|
|
mutex_init(&chip->mutex);
|
ALSA: usb-audio: Avoid nested autoresume calls
After the recent fix of runtime PM for USB-audio driver, we got a
lockdep warning like:
=============================================
[ INFO: possible recursive locking detected ]
4.2.0-rc8+ #61 Not tainted
---------------------------------------------
pulseaudio/980 is trying to acquire lock:
(&chip->shutdown_rwsem){.+.+.+}, at: [<ffffffffa0355dac>] snd_usb_autoresume+0x1d/0x52 [snd_usb_audio]
but task is already holding lock:
(&chip->shutdown_rwsem){.+.+.+}, at: [<ffffffffa0355dac>] snd_usb_autoresume+0x1d/0x52 [snd_usb_audio]
This comes from snd_usb_autoresume() invoking down_read() and it's
used in a nested way. Although it's basically safe, per se (as these
are read locks), it's better to reduce such spurious warnings.
The read lock is needed to guarantee the execution of "shutdown"
(cleanup at disconnection) task after all concurrent tasks are
finished. This can be implemented in another better way.
Also, the current check of chip->in_pm isn't good enough for
protecting the racy execution of multiple auto-resumes.
This patch rewrites the logic of snd_usb_autoresume() & co; namely,
- The recursive call of autopm is avoided by the new refcount,
chip->active. The chip->in_pm flag is removed accordingly.
- Instead of rwsem, another refcount, chip->usage_count, is introduced
for tracking the period to delay the shutdown procedure. At
the last clear of this refcount, wake_up() to the shutdown waiter is
called.
- The shutdown flag is replaced with shutdown atomic count; this is
for reducing the lock.
- Two new helpers are introduced to simplify the management of these
refcounts; snd_usb_lock_shutdown() increases the usage_count, checks
the shutdown state, and does autoresume. snd_usb_unlock_shutdown()
does the opposite. Most of mixer and other codes just need this,
and simply returns an error if it receives an error from lock.
Fixes: 9003ebb13f61 ('ALSA: usb-audio: Fix runtime PM unbalance')
Reported-and-tested-by: Alexnader Kuleshov <kuleshovmail@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2015-08-25 21:09:00 +07:00
|
|
|
init_waitqueue_head(&chip->shutdown_wait);
|
2010-03-05 01:46:13 +07:00
|
|
|
chip->index = idx;
|
|
|
|
chip->dev = dev;
|
|
|
|
chip->card = card;
|
|
|
|
chip->setup = device_setup[idx];
|
2013-04-04 04:18:56 +07:00
|
|
|
chip->autoclock = autoclock;
|
2015-08-26 15:20:59 +07:00
|
|
|
atomic_set(&chip->active, 1); /* avoid autopm during probing */
|
ALSA: usb-audio: Avoid nested autoresume calls
After the recent fix of runtime PM for USB-audio driver, we got a
lockdep warning like:
=============================================
[ INFO: possible recursive locking detected ]
4.2.0-rc8+ #61 Not tainted
---------------------------------------------
pulseaudio/980 is trying to acquire lock:
(&chip->shutdown_rwsem){.+.+.+}, at: [<ffffffffa0355dac>] snd_usb_autoresume+0x1d/0x52 [snd_usb_audio]
but task is already holding lock:
(&chip->shutdown_rwsem){.+.+.+}, at: [<ffffffffa0355dac>] snd_usb_autoresume+0x1d/0x52 [snd_usb_audio]
This comes from snd_usb_autoresume() invoking down_read() and it's
used in a nested way. Although it's basically safe, per se (as these
are read locks), it's better to reduce such spurious warnings.
The read lock is needed to guarantee the execution of "shutdown"
(cleanup at disconnection) task after all concurrent tasks are
finished. This can be implemented in another better way.
Also, the current check of chip->in_pm isn't good enough for
protecting the racy execution of multiple auto-resumes.
This patch rewrites the logic of snd_usb_autoresume() & co; namely,
- The recursive call of autopm is avoided by the new refcount,
chip->active. The chip->in_pm flag is removed accordingly.
- Instead of rwsem, another refcount, chip->usage_count, is introduced
for tracking the period to delay the shutdown procedure. At
the last clear of this refcount, wake_up() to the shutdown waiter is
called.
- The shutdown flag is replaced with shutdown atomic count; this is
for reducing the lock.
- Two new helpers are introduced to simplify the management of these
refcounts; snd_usb_lock_shutdown() increases the usage_count, checks
the shutdown state, and does autoresume. snd_usb_unlock_shutdown()
does the opposite. Most of mixer and other codes just need this,
and simply returns an error if it receives an error from lock.
Fixes: 9003ebb13f61 ('ALSA: usb-audio: Fix runtime PM unbalance')
Reported-and-tested-by: Alexnader Kuleshov <kuleshovmail@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2015-08-25 21:09:00 +07:00
|
|
|
atomic_set(&chip->usage_count, 0);
|
|
|
|
atomic_set(&chip->shutdown, 0);
|
2010-03-05 01:46:13 +07:00
|
|
|
|
2017-03-31 16:19:19 +07:00
|
|
|
chip->usb_id = usb_id;
|
2010-03-05 01:46:13 +07:00
|
|
|
INIT_LIST_HEAD(&chip->pcm_list);
|
2012-04-12 18:51:12 +07:00
|
|
|
INIT_LIST_HEAD(&chip->ep_list);
|
2010-03-05 01:46:13 +07:00
|
|
|
INIT_LIST_HEAD(&chip->midi_list);
|
|
|
|
INIT_LIST_HEAD(&chip->mixer_list);
|
|
|
|
|
2018-05-27 20:07:01 +07:00
|
|
|
card->private_free = snd_usb_audio_free;
|
2010-03-05 01:46:13 +07:00
|
|
|
|
|
|
|
strcpy(card->driver, "USB-Audio");
|
|
|
|
sprintf(component, "USB%04x:%04x",
|
|
|
|
USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id));
|
|
|
|
snd_component_add(card, component);
|
|
|
|
|
2018-05-02 19:45:37 +07:00
|
|
|
usb_audio_make_shortname(dev, chip, quirk);
|
|
|
|
usb_audio_make_longname(dev, chip, quirk);
|
2010-03-05 01:46:13 +07:00
|
|
|
|
|
|
|
snd_usb_audio_create_proc(chip);
|
|
|
|
|
|
|
|
*rchip = chip;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
ALSA: usb-audio: Add quirk_alias option
This patch adds a new option "quirk_alias" to snd-usb-audio driver for
allowing user to pass the quirk alias list. A quirk alias consists of
a string form like 0123abcd:5678beef, which makes to apply a quirk to
a device with USB ID 0123:abcd treated as if it were 5678:beef.
This feature is useful to test an existing quirk, typically for a
newer model of the same vendor, without patching / rebuilding the
kernel driver.
The current implementation is fairly simplistic: since there is no API
for matching a usb_device_id to the given ID pair, it has an open code
to loop over the id table and matches only with vendor:product pair.
So far, this is OK, as all existing entries are with vendor:product
pairs, indeed. Once when we have another matching entry, however,
we'd need to update get_alias_quirk() as well.
Note that this option is provided only for testing / development. If
you want to have a proper support, contact to upstream for adding the
matching quirk in the driver code statically.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2016-01-11 20:39:12 +07:00
|
|
|
/* look for a matching quirk alias id */
|
|
|
|
static bool get_alias_id(struct usb_device *dev, unsigned int *id)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
unsigned int 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;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-03-25 17:33:20 +07:00
|
|
|
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 inum != iface;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-08-06 13:48:56 +07:00
|
|
|
static const struct usb_device_id usb_audio_ids[]; /* defined below */
|
ALSA: usb-audio: Add quirk_alias option
This patch adds a new option "quirk_alias" to snd-usb-audio driver for
allowing user to pass the quirk alias list. A quirk alias consists of
a string form like 0123abcd:5678beef, which makes to apply a quirk to
a device with USB ID 0123:abcd treated as if it were 5678:beef.
This feature is useful to test an existing quirk, typically for a
newer model of the same vendor, without patching / rebuilding the
kernel driver.
The current implementation is fairly simplistic: since there is no API
for matching a usb_device_id to the given ID pair, it has an open code
to loop over the id table and matches only with vendor:product pair.
So far, this is OK, as all existing entries are with vendor:product
pairs, indeed. Once when we have another matching entry, however,
we'd need to update get_alias_quirk() as well.
Note that this option is provided only for testing / development. If
you want to have a proper support, contact to upstream for adding the
matching quirk in the driver code statically.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2016-01-11 20:39:12 +07:00
|
|
|
|
|
|
|
/* look for the corresponding quirk */
|
|
|
|
static const struct snd_usb_audio_quirk *
|
|
|
|
get_alias_quirk(struct usb_device *dev, unsigned int id)
|
|
|
|
{
|
|
|
|
const struct 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 (const struct snd_usb_audio_quirk *)p->driver_info;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-03-05 01:46:13 +07:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2014-10-31 17:00:23 +07:00
|
|
|
static int usb_audio_probe(struct usb_interface *intf,
|
|
|
|
const struct usb_device_id *usb_id)
|
2010-03-05 01:46:13 +07:00
|
|
|
{
|
2014-10-31 17:00:23 +07:00
|
|
|
struct usb_device *dev = interface_to_usbdev(intf);
|
|
|
|
const struct snd_usb_audio_quirk *quirk =
|
|
|
|
(const struct snd_usb_audio_quirk *)usb_id->driver_info;
|
2010-03-05 01:46:13 +07:00
|
|
|
struct snd_usb_audio *chip;
|
2014-10-31 17:00:23 +07:00
|
|
|
int i, err;
|
2010-03-05 01:46:13 +07:00
|
|
|
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));
|
ALSA: usb-audio: Add quirk_alias option
This patch adds a new option "quirk_alias" to snd-usb-audio driver for
allowing user to pass the quirk alias list. A quirk alias consists of
a string form like 0123abcd:5678beef, which makes to apply a quirk to
a device with USB ID 0123:abcd treated as if it were 5678:beef.
This feature is useful to test an existing quirk, typically for a
newer model of the same vendor, without patching / rebuilding the
kernel driver.
The current implementation is fairly simplistic: since there is no API
for matching a usb_device_id to the given ID pair, it has an open code
to loop over the id table and matches only with vendor:product pair.
So far, this is OK, as all existing entries are with vendor:product
pairs, indeed. Once when we have another matching entry, however,
we'd need to update get_alias_quirk() as well.
Note that this option is provided only for testing / development. If
you want to have a proper support, contact to upstream for adding the
matching quirk in the driver code statically.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2016-01-11 20:39:12 +07:00
|
|
|
if (get_alias_id(dev, &id))
|
|
|
|
quirk = get_alias_quirk(dev, id);
|
2010-03-05 01:46:13 +07:00
|
|
|
if (quirk && quirk->ifnum >= 0 && ifnum != quirk->ifnum)
|
2014-10-31 17:00:23 +07:00
|
|
|
return -ENXIO;
|
2010-03-05 01:46:13 +07:00
|
|
|
|
2016-01-11 17:33:34 +07:00
|
|
|
err = snd_usb_apply_boot_quirk(dev, intf, quirk, id);
|
2014-10-31 17:00:23 +07:00
|
|
|
if (err < 0)
|
|
|
|
return err;
|
2010-03-05 01:46:13 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* found a config. now register to ALSA
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* 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) {
|
ALSA: usb-audio: Avoid nested autoresume calls
After the recent fix of runtime PM for USB-audio driver, we got a
lockdep warning like:
=============================================
[ INFO: possible recursive locking detected ]
4.2.0-rc8+ #61 Not tainted
---------------------------------------------
pulseaudio/980 is trying to acquire lock:
(&chip->shutdown_rwsem){.+.+.+}, at: [<ffffffffa0355dac>] snd_usb_autoresume+0x1d/0x52 [snd_usb_audio]
but task is already holding lock:
(&chip->shutdown_rwsem){.+.+.+}, at: [<ffffffffa0355dac>] snd_usb_autoresume+0x1d/0x52 [snd_usb_audio]
This comes from snd_usb_autoresume() invoking down_read() and it's
used in a nested way. Although it's basically safe, per se (as these
are read locks), it's better to reduce such spurious warnings.
The read lock is needed to guarantee the execution of "shutdown"
(cleanup at disconnection) task after all concurrent tasks are
finished. This can be implemented in another better way.
Also, the current check of chip->in_pm isn't good enough for
protecting the racy execution of multiple auto-resumes.
This patch rewrites the logic of snd_usb_autoresume() & co; namely,
- The recursive call of autopm is avoided by the new refcount,
chip->active. The chip->in_pm flag is removed accordingly.
- Instead of rwsem, another refcount, chip->usage_count, is introduced
for tracking the period to delay the shutdown procedure. At
the last clear of this refcount, wake_up() to the shutdown waiter is
called.
- The shutdown flag is replaced with shutdown atomic count; this is
for reducing the lock.
- Two new helpers are introduced to simplify the management of these
refcounts; snd_usb_lock_shutdown() increases the usage_count, checks
the shutdown state, and does autoresume. snd_usb_unlock_shutdown()
does the opposite. Most of mixer and other codes just need this,
and simply returns an error if it receives an error from lock.
Fixes: 9003ebb13f61 ('ALSA: usb-audio: Fix runtime PM unbalance')
Reported-and-tested-by: Alexnader Kuleshov <kuleshovmail@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2015-08-25 21:09:00 +07:00
|
|
|
if (atomic_read(&usb_chip[i]->shutdown)) {
|
2014-02-26 19:02:17 +07:00
|
|
|
dev_err(&dev->dev, "USB device is in the shutdown state, cannot create a card instance\n");
|
2014-10-31 17:00:23 +07:00
|
|
|
err = -EIO;
|
2010-03-05 01:46:13 +07:00
|
|
|
goto __error;
|
|
|
|
}
|
|
|
|
chip = usb_chip[i];
|
2015-08-26 15:20:59 +07:00
|
|
|
atomic_inc(&chip->active); /* avoid autopm */
|
2010-03-05 01:46:13 +07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (! chip) {
|
2020-01-12 17:23:58 +07:00
|
|
|
err = snd_usb_apply_boot_quirk_once(dev, intf, quirk, id);
|
|
|
|
if (err < 0)
|
2020-01-16 00:46:04 +07:00
|
|
|
goto __error;
|
2020-01-12 17:23:58 +07:00
|
|
|
|
2010-03-05 01:46:13 +07:00
|
|
|
/* it's a fresh one.
|
|
|
|
* now look for an empty slot and create a new card instance
|
|
|
|
*/
|
|
|
|
for (i = 0; i < SNDRV_CARDS; i++)
|
2017-11-30 18:27:52 +07:00
|
|
|
if (!usb_chip[i] &&
|
2010-03-05 01:46:13 +07:00
|
|
|
(vid[i] == -1 || vid[i] == USB_ID_VENDOR(id)) &&
|
|
|
|
(pid[i] == -1 || pid[i] == USB_ID_PRODUCT(id))) {
|
2017-11-30 18:27:52 +07:00
|
|
|
if (enable[i]) {
|
|
|
|
err = snd_usb_audio_create(intf, dev, i, quirk,
|
|
|
|
id, &chip);
|
|
|
|
if (err < 0)
|
|
|
|
goto __error;
|
|
|
|
break;
|
|
|
|
} else if (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;
|
2010-03-05 01:46:13 +07:00
|
|
|
goto __error;
|
2017-11-30 18:27:52 +07:00
|
|
|
}
|
2010-03-05 01:46:13 +07:00
|
|
|
}
|
|
|
|
if (!chip) {
|
2014-02-26 19:02:17 +07:00
|
|
|
dev_err(&dev->dev, "no available usb audio device\n");
|
2014-10-31 17:00:23 +07:00
|
|
|
err = -ENODEV;
|
2010-03-05 01:46:13 +07:00
|
|
|
goto __error;
|
|
|
|
}
|
|
|
|
}
|
2020-06-05 13:41:17 +07:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-07-17 23:16:15 +07:00
|
|
|
dev_set_drvdata(&dev->dev, chip);
|
2010-03-05 01:46:13 +07:00
|
|
|
|
2010-09-02 16:13:15 +07:00
|
|
|
/*
|
|
|
|
* 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;
|
2010-05-31 19:51:31 +07:00
|
|
|
|
2011-05-25 14:08:59 +07:00
|
|
|
chip->txfr_quirk = 0;
|
|
|
|
err = 1; /* continue */
|
|
|
|
if (quirk && quirk->ifnum != QUIRK_NO_INTERFACE) {
|
|
|
|
/* need some special handlings */
|
2014-10-31 17:00:23 +07:00
|
|
|
err = snd_usb_create_quirk(chip, intf, &usb_audio_driver, quirk);
|
|
|
|
if (err < 0)
|
2011-05-25 14:08:59 +07:00
|
|
|
goto __error;
|
|
|
|
}
|
|
|
|
|
2010-03-05 01:46:13 +07:00
|
|
|
if (err > 0) {
|
|
|
|
/* create normal USB audio interfaces */
|
2014-10-31 17:00:23 +07:00
|
|
|
err = snd_usb_create_streams(chip, ifnum);
|
|
|
|
if (err < 0)
|
|
|
|
goto __error;
|
|
|
|
err = snd_usb_create_mixer(chip, ifnum, ignore_ctl_error);
|
|
|
|
if (err < 0)
|
2010-03-05 01:46:13 +07:00
|
|
|
goto __error;
|
|
|
|
}
|
|
|
|
|
2020-03-25 17:33:21 +07:00
|
|
|
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 */
|
|
|
|
}
|
|
|
|
|
2020-03-14 23:54:49 +07:00
|
|
|
/* 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
|
|
|
|
*/
|
2020-03-25 17:33:20 +07:00
|
|
|
if (!snd_usb_registration_quirk(chip, ifnum) &&
|
|
|
|
!check_delayed_register_option(chip, ifnum)) {
|
2020-03-14 23:54:49 +07:00
|
|
|
err = snd_card_register(chip->card);
|
|
|
|
if (err < 0)
|
|
|
|
goto __error;
|
|
|
|
}
|
2010-03-05 01:46:13 +07:00
|
|
|
|
media: sound/usb: Use Media Controller API to share media resources
Media Device Allocator API to allows multiple drivers share a media device.
This API solves a very common use-case for media devices where one physical
device (an USB stick) provides both audio and video. When such media device
exposes a standard USB Audio class, a proprietary Video class, two or more
independent drivers will share a single physical USB bridge. In such cases,
it is necessary to coordinate access to the shared resource.
Using this API, drivers can allocate a media device with the shared struct
device as the key. Once the media device is allocated by a driver, other
drivers can get a reference to it. The media device is released when all
the references are released.
Change the ALSA driver to use the Media Controller API to share media
resources with DVB, and V4L2 drivers on a AU0828 media device.
The Media Controller specific initialization is done after sound card is
registered. ALSA creates Media interface and entity function graph nodes
for Control, Mixer, PCM Playback, and PCM Capture devices.
snd_usb_hw_params() will call Media Controller enable source handler
interface to request the media resource. If resource request is granted,
it will release it from snd_usb_hw_free(). If resource is busy, -EBUSY is
returned.
Media specific cleanup is done in usb_audio_disconnect().
Reviewed-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Shuah Khan <shuah@kernel.org>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2019-04-02 07:40:22 +07:00
|
|
|
if (quirk && quirk->shares_media_device) {
|
|
|
|
/* don't want to fail when snd_media_device_create() fails */
|
|
|
|
snd_media_device_create(chip, intf);
|
|
|
|
}
|
|
|
|
|
2010-03-05 01:46:13 +07:00
|
|
|
usb_chip[chip->index] = chip;
|
2020-06-05 13:41:17 +07:00
|
|
|
chip->intf[chip->num_interfaces] = intf;
|
2010-03-05 01:46:13 +07:00
|
|
|
chip->num_interfaces++;
|
2014-10-31 17:00:23 +07:00
|
|
|
usb_set_intfdata(intf, chip);
|
2015-08-26 15:20:59 +07:00
|
|
|
atomic_dec(&chip->active);
|
2010-03-05 01:46:13 +07:00
|
|
|
mutex_unlock(®ister_mutex);
|
2014-10-31 17:00:23 +07:00
|
|
|
return 0;
|
2010-03-05 01:46:13 +07:00
|
|
|
|
|
|
|
__error:
|
2011-09-26 20:43:59 +07:00
|
|
|
if (chip) {
|
2018-12-03 22:09:34 +07:00
|
|
|
/* chip->active is inside the chip->card object,
|
|
|
|
* decrement before memory is possibly returned.
|
|
|
|
*/
|
|
|
|
atomic_dec(&chip->active);
|
2011-09-26 20:43:59 +07:00
|
|
|
if (!chip->num_interfaces)
|
|
|
|
snd_card_free(chip->card);
|
|
|
|
}
|
2010-03-05 01:46:13 +07:00
|
|
|
mutex_unlock(®ister_mutex);
|
2014-10-31 17:00:23 +07:00
|
|
|
return err;
|
2010-03-05 01:46:13 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* we need to take care of counter, since disconnection can be called also
|
|
|
|
* many times as well as usb_audio_probe().
|
|
|
|
*/
|
2014-10-31 17:00:23 +07:00
|
|
|
static void usb_audio_disconnect(struct usb_interface *intf)
|
2010-03-05 01:46:13 +07:00
|
|
|
{
|
2014-10-31 17:00:23 +07:00
|
|
|
struct snd_usb_audio *chip = usb_get_intfdata(intf);
|
2010-03-05 01:46:13 +07:00
|
|
|
struct snd_card *card;
|
2014-06-25 19:24:47 +07:00
|
|
|
struct list_head *p;
|
2010-03-05 01:46:13 +07:00
|
|
|
|
2011-07-06 22:20:13 +07:00
|
|
|
if (chip == (void *)-1L)
|
2010-03-05 01:46:13 +07:00
|
|
|
return;
|
|
|
|
|
|
|
|
card = chip->card;
|
2012-11-13 17:22:48 +07:00
|
|
|
|
|
|
|
mutex_lock(®ister_mutex);
|
ALSA: usb-audio: Avoid nested autoresume calls
After the recent fix of runtime PM for USB-audio driver, we got a
lockdep warning like:
=============================================
[ INFO: possible recursive locking detected ]
4.2.0-rc8+ #61 Not tainted
---------------------------------------------
pulseaudio/980 is trying to acquire lock:
(&chip->shutdown_rwsem){.+.+.+}, at: [<ffffffffa0355dac>] snd_usb_autoresume+0x1d/0x52 [snd_usb_audio]
but task is already holding lock:
(&chip->shutdown_rwsem){.+.+.+}, at: [<ffffffffa0355dac>] snd_usb_autoresume+0x1d/0x52 [snd_usb_audio]
This comes from snd_usb_autoresume() invoking down_read() and it's
used in a nested way. Although it's basically safe, per se (as these
are read locks), it's better to reduce such spurious warnings.
The read lock is needed to guarantee the execution of "shutdown"
(cleanup at disconnection) task after all concurrent tasks are
finished. This can be implemented in another better way.
Also, the current check of chip->in_pm isn't good enough for
protecting the racy execution of multiple auto-resumes.
This patch rewrites the logic of snd_usb_autoresume() & co; namely,
- The recursive call of autopm is avoided by the new refcount,
chip->active. The chip->in_pm flag is removed accordingly.
- Instead of rwsem, another refcount, chip->usage_count, is introduced
for tracking the period to delay the shutdown procedure. At
the last clear of this refcount, wake_up() to the shutdown waiter is
called.
- The shutdown flag is replaced with shutdown atomic count; this is
for reducing the lock.
- Two new helpers are introduced to simplify the management of these
refcounts; snd_usb_lock_shutdown() increases the usage_count, checks
the shutdown state, and does autoresume. snd_usb_unlock_shutdown()
does the opposite. Most of mixer and other codes just need this,
and simply returns an error if it receives an error from lock.
Fixes: 9003ebb13f61 ('ALSA: usb-audio: Fix runtime PM unbalance')
Reported-and-tested-by: Alexnader Kuleshov <kuleshovmail@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2015-08-25 21:09:00 +07:00
|
|
|
if (atomic_inc_return(&chip->shutdown) == 1) {
|
2014-10-31 17:24:32 +07:00
|
|
|
struct snd_usb_stream *as;
|
2014-06-25 19:24:47 +07:00
|
|
|
struct snd_usb_endpoint *ep;
|
2014-10-31 17:24:32 +07:00
|
|
|
struct usb_mixer_interface *mixer;
|
2014-06-25 19:24:47 +07:00
|
|
|
|
ALSA: usb-audio: Avoid nested autoresume calls
After the recent fix of runtime PM for USB-audio driver, we got a
lockdep warning like:
=============================================
[ INFO: possible recursive locking detected ]
4.2.0-rc8+ #61 Not tainted
---------------------------------------------
pulseaudio/980 is trying to acquire lock:
(&chip->shutdown_rwsem){.+.+.+}, at: [<ffffffffa0355dac>] snd_usb_autoresume+0x1d/0x52 [snd_usb_audio]
but task is already holding lock:
(&chip->shutdown_rwsem){.+.+.+}, at: [<ffffffffa0355dac>] snd_usb_autoresume+0x1d/0x52 [snd_usb_audio]
This comes from snd_usb_autoresume() invoking down_read() and it's
used in a nested way. Although it's basically safe, per se (as these
are read locks), it's better to reduce such spurious warnings.
The read lock is needed to guarantee the execution of "shutdown"
(cleanup at disconnection) task after all concurrent tasks are
finished. This can be implemented in another better way.
Also, the current check of chip->in_pm isn't good enough for
protecting the racy execution of multiple auto-resumes.
This patch rewrites the logic of snd_usb_autoresume() & co; namely,
- The recursive call of autopm is avoided by the new refcount,
chip->active. The chip->in_pm flag is removed accordingly.
- Instead of rwsem, another refcount, chip->usage_count, is introduced
for tracking the period to delay the shutdown procedure. At
the last clear of this refcount, wake_up() to the shutdown waiter is
called.
- The shutdown flag is replaced with shutdown atomic count; this is
for reducing the lock.
- Two new helpers are introduced to simplify the management of these
refcounts; snd_usb_lock_shutdown() increases the usage_count, checks
the shutdown state, and does autoresume. snd_usb_unlock_shutdown()
does the opposite. Most of mixer and other codes just need this,
and simply returns an error if it receives an error from lock.
Fixes: 9003ebb13f61 ('ALSA: usb-audio: Fix runtime PM unbalance')
Reported-and-tested-by: Alexnader Kuleshov <kuleshovmail@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2015-08-25 21:09:00 +07:00
|
|
|
/* wait until all pending tasks done;
|
|
|
|
* they are protected by snd_usb_lock_shutdown()
|
|
|
|
*/
|
|
|
|
wait_event(chip->shutdown_wait,
|
|
|
|
!atomic_read(&chip->usage_count));
|
2010-03-05 01:46:13 +07:00
|
|
|
snd_card_disconnect(card);
|
|
|
|
/* release the pcm resources */
|
2014-10-31 17:24:32 +07:00
|
|
|
list_for_each_entry(as, &chip->pcm_list, list) {
|
|
|
|
snd_usb_stream_disconnect(as);
|
2010-03-05 01:46:13 +07:00
|
|
|
}
|
2012-04-12 18:51:12 +07:00
|
|
|
/* release the endpoint resources */
|
2014-06-25 19:24:47 +07:00
|
|
|
list_for_each_entry(ep, &chip->ep_list, list) {
|
|
|
|
snd_usb_endpoint_release(ep);
|
2012-04-12 18:51:12 +07:00
|
|
|
}
|
2010-03-05 01:46:13 +07:00
|
|
|
/* release the midi resources */
|
|
|
|
list_for_each(p, &chip->midi_list) {
|
|
|
|
snd_usbmidi_disconnect(p);
|
|
|
|
}
|
media: sound/usb: Use Media Controller API to share media resources
Media Device Allocator API to allows multiple drivers share a media device.
This API solves a very common use-case for media devices where one physical
device (an USB stick) provides both audio and video. When such media device
exposes a standard USB Audio class, a proprietary Video class, two or more
independent drivers will share a single physical USB bridge. In such cases,
it is necessary to coordinate access to the shared resource.
Using this API, drivers can allocate a media device with the shared struct
device as the key. Once the media device is allocated by a driver, other
drivers can get a reference to it. The media device is released when all
the references are released.
Change the ALSA driver to use the Media Controller API to share media
resources with DVB, and V4L2 drivers on a AU0828 media device.
The Media Controller specific initialization is done after sound card is
registered. ALSA creates Media interface and entity function graph nodes
for Control, Mixer, PCM Playback, and PCM Capture devices.
snd_usb_hw_params() will call Media Controller enable source handler
interface to request the media resource. If resource request is granted,
it will release it from snd_usb_hw_free(). If resource is busy, -EBUSY is
returned.
Media specific cleanup is done in usb_audio_disconnect().
Reviewed-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Shuah Khan <shuah@kernel.org>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2019-04-02 07:40:22 +07:00
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
|
2010-03-05 01:46:13 +07:00
|
|
|
/* release mixer resources */
|
2014-10-31 17:24:32 +07:00
|
|
|
list_for_each_entry(mixer, &chip->mixer_list, list) {
|
|
|
|
snd_usb_mixer_disconnect(mixer);
|
2010-03-05 01:46:13 +07:00
|
|
|
}
|
2014-11-05 21:08:49 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
chip->num_interfaces--;
|
|
|
|
if (chip->num_interfaces <= 0) {
|
2010-03-05 01:46:13 +07:00
|
|
|
usb_chip[chip->index] = NULL;
|
|
|
|
mutex_unlock(®ister_mutex);
|
|
|
|
snd_card_free_when_closed(card);
|
|
|
|
} else {
|
|
|
|
mutex_unlock(®ister_mutex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
ALSA: usb-audio: Avoid nested autoresume calls
After the recent fix of runtime PM for USB-audio driver, we got a
lockdep warning like:
=============================================
[ INFO: possible recursive locking detected ]
4.2.0-rc8+ #61 Not tainted
---------------------------------------------
pulseaudio/980 is trying to acquire lock:
(&chip->shutdown_rwsem){.+.+.+}, at: [<ffffffffa0355dac>] snd_usb_autoresume+0x1d/0x52 [snd_usb_audio]
but task is already holding lock:
(&chip->shutdown_rwsem){.+.+.+}, at: [<ffffffffa0355dac>] snd_usb_autoresume+0x1d/0x52 [snd_usb_audio]
This comes from snd_usb_autoresume() invoking down_read() and it's
used in a nested way. Although it's basically safe, per se (as these
are read locks), it's better to reduce such spurious warnings.
The read lock is needed to guarantee the execution of "shutdown"
(cleanup at disconnection) task after all concurrent tasks are
finished. This can be implemented in another better way.
Also, the current check of chip->in_pm isn't good enough for
protecting the racy execution of multiple auto-resumes.
This patch rewrites the logic of snd_usb_autoresume() & co; namely,
- The recursive call of autopm is avoided by the new refcount,
chip->active. The chip->in_pm flag is removed accordingly.
- Instead of rwsem, another refcount, chip->usage_count, is introduced
for tracking the period to delay the shutdown procedure. At
the last clear of this refcount, wake_up() to the shutdown waiter is
called.
- The shutdown flag is replaced with shutdown atomic count; this is
for reducing the lock.
- Two new helpers are introduced to simplify the management of these
refcounts; snd_usb_lock_shutdown() increases the usage_count, checks
the shutdown state, and does autoresume. snd_usb_unlock_shutdown()
does the opposite. Most of mixer and other codes just need this,
and simply returns an error if it receives an error from lock.
Fixes: 9003ebb13f61 ('ALSA: usb-audio: Fix runtime PM unbalance')
Reported-and-tested-by: Alexnader Kuleshov <kuleshovmail@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2015-08-25 21:09:00 +07:00
|
|
|
/* lock the shutdown (disconnect) task and autoresume */
|
|
|
|
int snd_usb_lock_shutdown(struct snd_usb_audio *chip)
|
2011-03-11 20:51:12 +07:00
|
|
|
{
|
ALSA: usb-audio: Avoid nested autoresume calls
After the recent fix of runtime PM for USB-audio driver, we got a
lockdep warning like:
=============================================
[ INFO: possible recursive locking detected ]
4.2.0-rc8+ #61 Not tainted
---------------------------------------------
pulseaudio/980 is trying to acquire lock:
(&chip->shutdown_rwsem){.+.+.+}, at: [<ffffffffa0355dac>] snd_usb_autoresume+0x1d/0x52 [snd_usb_audio]
but task is already holding lock:
(&chip->shutdown_rwsem){.+.+.+}, at: [<ffffffffa0355dac>] snd_usb_autoresume+0x1d/0x52 [snd_usb_audio]
This comes from snd_usb_autoresume() invoking down_read() and it's
used in a nested way. Although it's basically safe, per se (as these
are read locks), it's better to reduce such spurious warnings.
The read lock is needed to guarantee the execution of "shutdown"
(cleanup at disconnection) task after all concurrent tasks are
finished. This can be implemented in another better way.
Also, the current check of chip->in_pm isn't good enough for
protecting the racy execution of multiple auto-resumes.
This patch rewrites the logic of snd_usb_autoresume() & co; namely,
- The recursive call of autopm is avoided by the new refcount,
chip->active. The chip->in_pm flag is removed accordingly.
- Instead of rwsem, another refcount, chip->usage_count, is introduced
for tracking the period to delay the shutdown procedure. At
the last clear of this refcount, wake_up() to the shutdown waiter is
called.
- The shutdown flag is replaced with shutdown atomic count; this is
for reducing the lock.
- Two new helpers are introduced to simplify the management of these
refcounts; snd_usb_lock_shutdown() increases the usage_count, checks
the shutdown state, and does autoresume. snd_usb_unlock_shutdown()
does the opposite. Most of mixer and other codes just need this,
and simply returns an error if it receives an error from lock.
Fixes: 9003ebb13f61 ('ALSA: usb-audio: Fix runtime PM unbalance')
Reported-and-tested-by: Alexnader Kuleshov <kuleshovmail@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2015-08-25 21:09:00 +07:00
|
|
|
int err;
|
2011-03-11 20:51:12 +07:00
|
|
|
|
ALSA: usb-audio: Avoid nested autoresume calls
After the recent fix of runtime PM for USB-audio driver, we got a
lockdep warning like:
=============================================
[ INFO: possible recursive locking detected ]
4.2.0-rc8+ #61 Not tainted
---------------------------------------------
pulseaudio/980 is trying to acquire lock:
(&chip->shutdown_rwsem){.+.+.+}, at: [<ffffffffa0355dac>] snd_usb_autoresume+0x1d/0x52 [snd_usb_audio]
but task is already holding lock:
(&chip->shutdown_rwsem){.+.+.+}, at: [<ffffffffa0355dac>] snd_usb_autoresume+0x1d/0x52 [snd_usb_audio]
This comes from snd_usb_autoresume() invoking down_read() and it's
used in a nested way. Although it's basically safe, per se (as these
are read locks), it's better to reduce such spurious warnings.
The read lock is needed to guarantee the execution of "shutdown"
(cleanup at disconnection) task after all concurrent tasks are
finished. This can be implemented in another better way.
Also, the current check of chip->in_pm isn't good enough for
protecting the racy execution of multiple auto-resumes.
This patch rewrites the logic of snd_usb_autoresume() & co; namely,
- The recursive call of autopm is avoided by the new refcount,
chip->active. The chip->in_pm flag is removed accordingly.
- Instead of rwsem, another refcount, chip->usage_count, is introduced
for tracking the period to delay the shutdown procedure. At
the last clear of this refcount, wake_up() to the shutdown waiter is
called.
- The shutdown flag is replaced with shutdown atomic count; this is
for reducing the lock.
- Two new helpers are introduced to simplify the management of these
refcounts; snd_usb_lock_shutdown() increases the usage_count, checks
the shutdown state, and does autoresume. snd_usb_unlock_shutdown()
does the opposite. Most of mixer and other codes just need this,
and simply returns an error if it receives an error from lock.
Fixes: 9003ebb13f61 ('ALSA: usb-audio: Fix runtime PM unbalance')
Reported-and-tested-by: Alexnader Kuleshov <kuleshovmail@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2015-08-25 21:09:00 +07:00
|
|
|
atomic_inc(&chip->usage_count);
|
|
|
|
if (atomic_read(&chip->shutdown)) {
|
|
|
|
err = -EIO;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
err = snd_usb_autoresume(chip);
|
|
|
|
if (err < 0)
|
|
|
|
goto error;
|
|
|
|
return 0;
|
2011-03-11 20:51:12 +07:00
|
|
|
|
ALSA: usb-audio: Avoid nested autoresume calls
After the recent fix of runtime PM for USB-audio driver, we got a
lockdep warning like:
=============================================
[ INFO: possible recursive locking detected ]
4.2.0-rc8+ #61 Not tainted
---------------------------------------------
pulseaudio/980 is trying to acquire lock:
(&chip->shutdown_rwsem){.+.+.+}, at: [<ffffffffa0355dac>] snd_usb_autoresume+0x1d/0x52 [snd_usb_audio]
but task is already holding lock:
(&chip->shutdown_rwsem){.+.+.+}, at: [<ffffffffa0355dac>] snd_usb_autoresume+0x1d/0x52 [snd_usb_audio]
This comes from snd_usb_autoresume() invoking down_read() and it's
used in a nested way. Although it's basically safe, per se (as these
are read locks), it's better to reduce such spurious warnings.
The read lock is needed to guarantee the execution of "shutdown"
(cleanup at disconnection) task after all concurrent tasks are
finished. This can be implemented in another better way.
Also, the current check of chip->in_pm isn't good enough for
protecting the racy execution of multiple auto-resumes.
This patch rewrites the logic of snd_usb_autoresume() & co; namely,
- The recursive call of autopm is avoided by the new refcount,
chip->active. The chip->in_pm flag is removed accordingly.
- Instead of rwsem, another refcount, chip->usage_count, is introduced
for tracking the period to delay the shutdown procedure. At
the last clear of this refcount, wake_up() to the shutdown waiter is
called.
- The shutdown flag is replaced with shutdown atomic count; this is
for reducing the lock.
- Two new helpers are introduced to simplify the management of these
refcounts; snd_usb_lock_shutdown() increases the usage_count, checks
the shutdown state, and does autoresume. snd_usb_unlock_shutdown()
does the opposite. Most of mixer and other codes just need this,
and simply returns an error if it receives an error from lock.
Fixes: 9003ebb13f61 ('ALSA: usb-audio: Fix runtime PM unbalance')
Reported-and-tested-by: Alexnader Kuleshov <kuleshovmail@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2015-08-25 21:09:00 +07:00
|
|
|
error:
|
|
|
|
if (atomic_dec_and_test(&chip->usage_count))
|
|
|
|
wake_up(&chip->shutdown_wait);
|
2011-03-11 20:51:12 +07:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
ALSA: usb-audio: Avoid nested autoresume calls
After the recent fix of runtime PM for USB-audio driver, we got a
lockdep warning like:
=============================================
[ INFO: possible recursive locking detected ]
4.2.0-rc8+ #61 Not tainted
---------------------------------------------
pulseaudio/980 is trying to acquire lock:
(&chip->shutdown_rwsem){.+.+.+}, at: [<ffffffffa0355dac>] snd_usb_autoresume+0x1d/0x52 [snd_usb_audio]
but task is already holding lock:
(&chip->shutdown_rwsem){.+.+.+}, at: [<ffffffffa0355dac>] snd_usb_autoresume+0x1d/0x52 [snd_usb_audio]
This comes from snd_usb_autoresume() invoking down_read() and it's
used in a nested way. Although it's basically safe, per se (as these
are read locks), it's better to reduce such spurious warnings.
The read lock is needed to guarantee the execution of "shutdown"
(cleanup at disconnection) task after all concurrent tasks are
finished. This can be implemented in another better way.
Also, the current check of chip->in_pm isn't good enough for
protecting the racy execution of multiple auto-resumes.
This patch rewrites the logic of snd_usb_autoresume() & co; namely,
- The recursive call of autopm is avoided by the new refcount,
chip->active. The chip->in_pm flag is removed accordingly.
- Instead of rwsem, another refcount, chip->usage_count, is introduced
for tracking the period to delay the shutdown procedure. At
the last clear of this refcount, wake_up() to the shutdown waiter is
called.
- The shutdown flag is replaced with shutdown atomic count; this is
for reducing the lock.
- Two new helpers are introduced to simplify the management of these
refcounts; snd_usb_lock_shutdown() increases the usage_count, checks
the shutdown state, and does autoresume. snd_usb_unlock_shutdown()
does the opposite. Most of mixer and other codes just need this,
and simply returns an error if it receives an error from lock.
Fixes: 9003ebb13f61 ('ALSA: usb-audio: Fix runtime PM unbalance')
Reported-and-tested-by: Alexnader Kuleshov <kuleshovmail@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2015-08-25 21:09:00 +07:00
|
|
|
/* autosuspend and unlock the shutdown */
|
|
|
|
void snd_usb_unlock_shutdown(struct snd_usb_audio *chip)
|
|
|
|
{
|
|
|
|
snd_usb_autosuspend(chip);
|
|
|
|
if (atomic_dec_and_test(&chip->usage_count))
|
|
|
|
wake_up(&chip->shutdown_wait);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_PM
|
|
|
|
|
|
|
|
int snd_usb_autoresume(struct snd_usb_audio *chip)
|
|
|
|
{
|
2020-06-05 13:41:17 +07:00
|
|
|
int i, err;
|
|
|
|
|
ALSA: usb-audio: Avoid nested autoresume calls
After the recent fix of runtime PM for USB-audio driver, we got a
lockdep warning like:
=============================================
[ INFO: possible recursive locking detected ]
4.2.0-rc8+ #61 Not tainted
---------------------------------------------
pulseaudio/980 is trying to acquire lock:
(&chip->shutdown_rwsem){.+.+.+}, at: [<ffffffffa0355dac>] snd_usb_autoresume+0x1d/0x52 [snd_usb_audio]
but task is already holding lock:
(&chip->shutdown_rwsem){.+.+.+}, at: [<ffffffffa0355dac>] snd_usb_autoresume+0x1d/0x52 [snd_usb_audio]
This comes from snd_usb_autoresume() invoking down_read() and it's
used in a nested way. Although it's basically safe, per se (as these
are read locks), it's better to reduce such spurious warnings.
The read lock is needed to guarantee the execution of "shutdown"
(cleanup at disconnection) task after all concurrent tasks are
finished. This can be implemented in another better way.
Also, the current check of chip->in_pm isn't good enough for
protecting the racy execution of multiple auto-resumes.
This patch rewrites the logic of snd_usb_autoresume() & co; namely,
- The recursive call of autopm is avoided by the new refcount,
chip->active. The chip->in_pm flag is removed accordingly.
- Instead of rwsem, another refcount, chip->usage_count, is introduced
for tracking the period to delay the shutdown procedure. At
the last clear of this refcount, wake_up() to the shutdown waiter is
called.
- The shutdown flag is replaced with shutdown atomic count; this is
for reducing the lock.
- Two new helpers are introduced to simplify the management of these
refcounts; snd_usb_lock_shutdown() increases the usage_count, checks
the shutdown state, and does autoresume. snd_usb_unlock_shutdown()
does the opposite. Most of mixer and other codes just need this,
and simply returns an error if it receives an error from lock.
Fixes: 9003ebb13f61 ('ALSA: usb-audio: Fix runtime PM unbalance')
Reported-and-tested-by: Alexnader Kuleshov <kuleshovmail@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2015-08-25 21:09:00 +07:00
|
|
|
if (atomic_read(&chip->shutdown))
|
|
|
|
return -EIO;
|
2020-06-05 13:41:17 +07:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
ALSA: usb-audio: Avoid nested autoresume calls
After the recent fix of runtime PM for USB-audio driver, we got a
lockdep warning like:
=============================================
[ INFO: possible recursive locking detected ]
4.2.0-rc8+ #61 Not tainted
---------------------------------------------
pulseaudio/980 is trying to acquire lock:
(&chip->shutdown_rwsem){.+.+.+}, at: [<ffffffffa0355dac>] snd_usb_autoresume+0x1d/0x52 [snd_usb_audio]
but task is already holding lock:
(&chip->shutdown_rwsem){.+.+.+}, at: [<ffffffffa0355dac>] snd_usb_autoresume+0x1d/0x52 [snd_usb_audio]
This comes from snd_usb_autoresume() invoking down_read() and it's
used in a nested way. Although it's basically safe, per se (as these
are read locks), it's better to reduce such spurious warnings.
The read lock is needed to guarantee the execution of "shutdown"
(cleanup at disconnection) task after all concurrent tasks are
finished. This can be implemented in another better way.
Also, the current check of chip->in_pm isn't good enough for
protecting the racy execution of multiple auto-resumes.
This patch rewrites the logic of snd_usb_autoresume() & co; namely,
- The recursive call of autopm is avoided by the new refcount,
chip->active. The chip->in_pm flag is removed accordingly.
- Instead of rwsem, another refcount, chip->usage_count, is introduced
for tracking the period to delay the shutdown procedure. At
the last clear of this refcount, wake_up() to the shutdown waiter is
called.
- The shutdown flag is replaced with shutdown atomic count; this is
for reducing the lock.
- Two new helpers are introduced to simplify the management of these
refcounts; snd_usb_lock_shutdown() increases the usage_count, checks
the shutdown state, and does autoresume. snd_usb_unlock_shutdown()
does the opposite. Most of mixer and other codes just need this,
and simply returns an error if it receives an error from lock.
Fixes: 9003ebb13f61 ('ALSA: usb-audio: Fix runtime PM unbalance')
Reported-and-tested-by: Alexnader Kuleshov <kuleshovmail@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2015-08-25 21:09:00 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-03-11 20:51:12 +07:00
|
|
|
void snd_usb_autosuspend(struct snd_usb_audio *chip)
|
|
|
|
{
|
2020-06-05 13:41:17 +07:00
|
|
|
int i;
|
|
|
|
|
2016-01-12 20:03:33 +07:00
|
|
|
if (atomic_read(&chip->shutdown))
|
|
|
|
return;
|
2020-06-05 13:41:17 +07:00
|
|
|
if (!atomic_dec_and_test(&chip->active))
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < chip->num_interfaces; i++)
|
|
|
|
usb_autopm_put_interface(chip->intf[i]);
|
2011-03-11 20:51:12 +07:00
|
|
|
}
|
|
|
|
|
2010-03-05 01:46:13 +07:00
|
|
|
static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
|
|
|
|
{
|
|
|
|
struct snd_usb_audio *chip = usb_get_intfdata(intf);
|
|
|
|
struct snd_usb_stream *as;
|
2011-03-11 19:19:43 +07:00
|
|
|
struct usb_mixer_interface *mixer;
|
2014-08-05 23:44:50 +07:00
|
|
|
struct list_head *p;
|
2010-03-05 01:46:13 +07:00
|
|
|
|
|
|
|
if (chip == (void *)-1L)
|
|
|
|
return 0;
|
|
|
|
|
2015-08-26 15:23:26 +07:00
|
|
|
if (!chip->num_suspended_intf++) {
|
|
|
|
list_for_each_entry(as, &chip->pcm_list, list) {
|
2018-07-31 19:28:44 +07:00
|
|
|
snd_usb_pcm_suspend(as);
|
2015-08-26 15:23:26 +07:00
|
|
|
as->substream[0].need_setup_ep =
|
|
|
|
as->substream[1].need_setup_ep = true;
|
2013-01-29 18:56:30 +07:00
|
|
|
}
|
2015-08-26 15:23:26 +07:00
|
|
|
list_for_each(p, &chip->midi_list)
|
|
|
|
snd_usbmidi_suspend(p);
|
2014-05-02 23:14:42 +07:00
|
|
|
list_for_each_entry(mixer, &chip->mixer_list, list)
|
|
|
|
snd_usb_mixer_suspend(mixer);
|
2015-08-26 15:23:26 +07:00
|
|
|
}
|
2011-03-11 20:51:12 +07:00
|
|
|
|
ALSA: usb-audio: Fix inconsistent card PM state after resume
When a USB-audio interface gets runtime-suspended via auto-pm feature,
the driver suspends all functionality and increment
chip->num_suspended_intf. Later on, when the system gets suspended to
S3, the driver increments chip->num_suspended_intf again, skips the
device changes, and sets the card power state to
SNDRV_CTL_POWER_D3hot. In return, when the system gets resumed from
S3, the resume callback decrements chip->num_suspended_intf. Since
this refcount is still not zero (it's been runtime-suspended), the
whole resume is skipped. But there is a small pitfall here.
The problem is that the driver doesn't restore the card power state
after this resume call, leaving it as SNDRV_CTL_POWER_D3hot. So,
even after the system resume finishes, the card instance still appears
as if it were system-suspended, and this confuses many ioctl accesses
that are blocked unexpectedly.
In details, we have two issues behind the scene: one is that the card
power state is changed only when the refcount becomes zero, and
another is that the prior auto-suspend check is kept in a boolean
flag. Although the latter problem is almost negligible since the
auto-pm feature is imposed only on the primary interface, but this can
be a potential problem on the devices with multiple interfaces.
This patch addresses those issues by the following:
- Replace chip->autosuspended boolean flag with chip->system_suspend
counter
- At the first system-suspend, chip->num_suspended_intf is recorded to
chip->system_suspend
- At system-resume, the card power state is restored when the
chip->num_suspended_intf refcount reaches to chip->system_suspend,
i.e. the state returns to the auto-suspended
Also, the patch fixes yet another hidden problem by the code
refactoring along with the fixes above: namely, when some resume
procedure failed, the driver left chip->num_suspended_intf that was
already decreased, and it might lead to the refcount unbalance.
In the new code, the refcount decrement is done after the whole resume
procedure, and the problem is avoided as well.
Fixes: 0662292aec05 ("ALSA: usb-audio: Handle normal and auto-suspend equally")
Reported-and-tested-by: Macpaul Lin <macpaul.lin@mediatek.com>
Cc: <stable@vger.kernel.org>
Link: https://lore.kernel.org/r/20200603153709.6293-1-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2020-06-03 22:37:08 +07:00
|
|
|
if (!PMSG_IS_AUTO(message) && !chip->system_suspend) {
|
|
|
|
snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
|
|
|
|
chip->system_suspend = chip->num_suspended_intf;
|
|
|
|
}
|
|
|
|
|
2010-03-05 01:46:13 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-01-20 22:51:16 +07:00
|
|
|
static int __usb_audio_resume(struct usb_interface *intf, bool reset_resume)
|
2010-03-05 01:46:13 +07:00
|
|
|
{
|
|
|
|
struct snd_usb_audio *chip = usb_get_intfdata(intf);
|
2018-07-31 19:28:44 +07:00
|
|
|
struct snd_usb_stream *as;
|
2011-03-11 19:19:43 +07:00
|
|
|
struct usb_mixer_interface *mixer;
|
2014-08-05 23:44:50 +07:00
|
|
|
struct list_head *p;
|
2011-03-11 20:51:12 +07:00
|
|
|
int err = 0;
|
2010-03-05 01:46:13 +07:00
|
|
|
|
|
|
|
if (chip == (void *)-1L)
|
|
|
|
return 0;
|
2014-05-02 23:17:06 +07:00
|
|
|
|
ALSA: usb-audio: Avoid nested autoresume calls
After the recent fix of runtime PM for USB-audio driver, we got a
lockdep warning like:
=============================================
[ INFO: possible recursive locking detected ]
4.2.0-rc8+ #61 Not tainted
---------------------------------------------
pulseaudio/980 is trying to acquire lock:
(&chip->shutdown_rwsem){.+.+.+}, at: [<ffffffffa0355dac>] snd_usb_autoresume+0x1d/0x52 [snd_usb_audio]
but task is already holding lock:
(&chip->shutdown_rwsem){.+.+.+}, at: [<ffffffffa0355dac>] snd_usb_autoresume+0x1d/0x52 [snd_usb_audio]
This comes from snd_usb_autoresume() invoking down_read() and it's
used in a nested way. Although it's basically safe, per se (as these
are read locks), it's better to reduce such spurious warnings.
The read lock is needed to guarantee the execution of "shutdown"
(cleanup at disconnection) task after all concurrent tasks are
finished. This can be implemented in another better way.
Also, the current check of chip->in_pm isn't good enough for
protecting the racy execution of multiple auto-resumes.
This patch rewrites the logic of snd_usb_autoresume() & co; namely,
- The recursive call of autopm is avoided by the new refcount,
chip->active. The chip->in_pm flag is removed accordingly.
- Instead of rwsem, another refcount, chip->usage_count, is introduced
for tracking the period to delay the shutdown procedure. At
the last clear of this refcount, wake_up() to the shutdown waiter is
called.
- The shutdown flag is replaced with shutdown atomic count; this is
for reducing the lock.
- Two new helpers are introduced to simplify the management of these
refcounts; snd_usb_lock_shutdown() increases the usage_count, checks
the shutdown state, and does autoresume. snd_usb_unlock_shutdown()
does the opposite. Most of mixer and other codes just need this,
and simply returns an error if it receives an error from lock.
Fixes: 9003ebb13f61 ('ALSA: usb-audio: Fix runtime PM unbalance')
Reported-and-tested-by: Alexnader Kuleshov <kuleshovmail@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2015-08-25 21:09:00 +07:00
|
|
|
atomic_inc(&chip->active); /* avoid autopm */
|
ALSA: usb-audio: Fix inconsistent card PM state after resume
When a USB-audio interface gets runtime-suspended via auto-pm feature,
the driver suspends all functionality and increment
chip->num_suspended_intf. Later on, when the system gets suspended to
S3, the driver increments chip->num_suspended_intf again, skips the
device changes, and sets the card power state to
SNDRV_CTL_POWER_D3hot. In return, when the system gets resumed from
S3, the resume callback decrements chip->num_suspended_intf. Since
this refcount is still not zero (it's been runtime-suspended), the
whole resume is skipped. But there is a small pitfall here.
The problem is that the driver doesn't restore the card power state
after this resume call, leaving it as SNDRV_CTL_POWER_D3hot. So,
even after the system resume finishes, the card instance still appears
as if it were system-suspended, and this confuses many ioctl accesses
that are blocked unexpectedly.
In details, we have two issues behind the scene: one is that the card
power state is changed only when the refcount becomes zero, and
another is that the prior auto-suspend check is kept in a boolean
flag. Although the latter problem is almost negligible since the
auto-pm feature is imposed only on the primary interface, but this can
be a potential problem on the devices with multiple interfaces.
This patch addresses those issues by the following:
- Replace chip->autosuspended boolean flag with chip->system_suspend
counter
- At the first system-suspend, chip->num_suspended_intf is recorded to
chip->system_suspend
- At system-resume, the card power state is restored when the
chip->num_suspended_intf refcount reaches to chip->system_suspend,
i.e. the state returns to the auto-suspended
Also, the patch fixes yet another hidden problem by the code
refactoring along with the fixes above: namely, when some resume
procedure failed, the driver left chip->num_suspended_intf that was
already decreased, and it might lead to the refcount unbalance.
In the new code, the refcount decrement is done after the whole resume
procedure, and the problem is avoided as well.
Fixes: 0662292aec05 ("ALSA: usb-audio: Handle normal and auto-suspend equally")
Reported-and-tested-by: Macpaul Lin <macpaul.lin@mediatek.com>
Cc: <stable@vger.kernel.org>
Link: https://lore.kernel.org/r/20200603153709.6293-1-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2020-06-03 22:37:08 +07:00
|
|
|
if (chip->num_suspended_intf > 1)
|
|
|
|
goto out;
|
2018-07-31 19:28:44 +07:00
|
|
|
|
|
|
|
list_for_each_entry(as, &chip->pcm_list, list) {
|
|
|
|
err = snd_usb_pcm_resume(as);
|
|
|
|
if (err < 0)
|
|
|
|
goto err_out;
|
|
|
|
}
|
|
|
|
|
2010-03-05 01:46:13 +07:00
|
|
|
/*
|
|
|
|
* ALSA leaves material resumption to user space
|
2011-03-11 19:19:43 +07:00
|
|
|
* we just notify and restart the mixers
|
2010-03-05 01:46:13 +07:00
|
|
|
*/
|
2011-03-11 20:51:12 +07:00
|
|
|
list_for_each_entry(mixer, &chip->mixer_list, list) {
|
2014-01-20 22:51:16 +07:00
|
|
|
err = snd_usb_mixer_resume(mixer, reset_resume);
|
2011-03-11 20:51:12 +07:00
|
|
|
if (err < 0)
|
|
|
|
goto err_out;
|
|
|
|
}
|
2010-03-05 01:46:13 +07:00
|
|
|
|
2014-08-05 23:44:50 +07:00
|
|
|
list_for_each(p, &chip->midi_list) {
|
|
|
|
snd_usbmidi_resume(p);
|
|
|
|
}
|
|
|
|
|
ALSA: usb-audio: Fix inconsistent card PM state after resume
When a USB-audio interface gets runtime-suspended via auto-pm feature,
the driver suspends all functionality and increment
chip->num_suspended_intf. Later on, when the system gets suspended to
S3, the driver increments chip->num_suspended_intf again, skips the
device changes, and sets the card power state to
SNDRV_CTL_POWER_D3hot. In return, when the system gets resumed from
S3, the resume callback decrements chip->num_suspended_intf. Since
this refcount is still not zero (it's been runtime-suspended), the
whole resume is skipped. But there is a small pitfall here.
The problem is that the driver doesn't restore the card power state
after this resume call, leaving it as SNDRV_CTL_POWER_D3hot. So,
even after the system resume finishes, the card instance still appears
as if it were system-suspended, and this confuses many ioctl accesses
that are blocked unexpectedly.
In details, we have two issues behind the scene: one is that the card
power state is changed only when the refcount becomes zero, and
another is that the prior auto-suspend check is kept in a boolean
flag. Although the latter problem is almost negligible since the
auto-pm feature is imposed only on the primary interface, but this can
be a potential problem on the devices with multiple interfaces.
This patch addresses those issues by the following:
- Replace chip->autosuspended boolean flag with chip->system_suspend
counter
- At the first system-suspend, chip->num_suspended_intf is recorded to
chip->system_suspend
- At system-resume, the card power state is restored when the
chip->num_suspended_intf refcount reaches to chip->system_suspend,
i.e. the state returns to the auto-suspended
Also, the patch fixes yet another hidden problem by the code
refactoring along with the fixes above: namely, when some resume
procedure failed, the driver left chip->num_suspended_intf that was
already decreased, and it might lead to the refcount unbalance.
In the new code, the refcount decrement is done after the whole resume
procedure, and the problem is avoided as well.
Fixes: 0662292aec05 ("ALSA: usb-audio: Handle normal and auto-suspend equally")
Reported-and-tested-by: Macpaul Lin <macpaul.lin@mediatek.com>
Cc: <stable@vger.kernel.org>
Link: https://lore.kernel.org/r/20200603153709.6293-1-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2020-06-03 22:37:08 +07:00
|
|
|
out:
|
|
|
|
if (chip->num_suspended_intf == chip->system_suspend) {
|
2011-03-11 20:51:12 +07:00
|
|
|
snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
|
ALSA: usb-audio: Fix inconsistent card PM state after resume
When a USB-audio interface gets runtime-suspended via auto-pm feature,
the driver suspends all functionality and increment
chip->num_suspended_intf. Later on, when the system gets suspended to
S3, the driver increments chip->num_suspended_intf again, skips the
device changes, and sets the card power state to
SNDRV_CTL_POWER_D3hot. In return, when the system gets resumed from
S3, the resume callback decrements chip->num_suspended_intf. Since
this refcount is still not zero (it's been runtime-suspended), the
whole resume is skipped. But there is a small pitfall here.
The problem is that the driver doesn't restore the card power state
after this resume call, leaving it as SNDRV_CTL_POWER_D3hot. So,
even after the system resume finishes, the card instance still appears
as if it were system-suspended, and this confuses many ioctl accesses
that are blocked unexpectedly.
In details, we have two issues behind the scene: one is that the card
power state is changed only when the refcount becomes zero, and
another is that the prior auto-suspend check is kept in a boolean
flag. Although the latter problem is almost negligible since the
auto-pm feature is imposed only on the primary interface, but this can
be a potential problem on the devices with multiple interfaces.
This patch addresses those issues by the following:
- Replace chip->autosuspended boolean flag with chip->system_suspend
counter
- At the first system-suspend, chip->num_suspended_intf is recorded to
chip->system_suspend
- At system-resume, the card power state is restored when the
chip->num_suspended_intf refcount reaches to chip->system_suspend,
i.e. the state returns to the auto-suspended
Also, the patch fixes yet another hidden problem by the code
refactoring along with the fixes above: namely, when some resume
procedure failed, the driver left chip->num_suspended_intf that was
already decreased, and it might lead to the refcount unbalance.
In the new code, the refcount decrement is done after the whole resume
procedure, and the problem is avoided as well.
Fixes: 0662292aec05 ("ALSA: usb-audio: Handle normal and auto-suspend equally")
Reported-and-tested-by: Macpaul Lin <macpaul.lin@mediatek.com>
Cc: <stable@vger.kernel.org>
Link: https://lore.kernel.org/r/20200603153709.6293-1-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2020-06-03 22:37:08 +07:00
|
|
|
chip->system_suspend = 0;
|
|
|
|
}
|
|
|
|
chip->num_suspended_intf--;
|
2010-03-05 01:46:13 +07:00
|
|
|
|
2011-03-11 20:51:12 +07:00
|
|
|
err_out:
|
ALSA: usb-audio: Avoid nested autoresume calls
After the recent fix of runtime PM for USB-audio driver, we got a
lockdep warning like:
=============================================
[ INFO: possible recursive locking detected ]
4.2.0-rc8+ #61 Not tainted
---------------------------------------------
pulseaudio/980 is trying to acquire lock:
(&chip->shutdown_rwsem){.+.+.+}, at: [<ffffffffa0355dac>] snd_usb_autoresume+0x1d/0x52 [snd_usb_audio]
but task is already holding lock:
(&chip->shutdown_rwsem){.+.+.+}, at: [<ffffffffa0355dac>] snd_usb_autoresume+0x1d/0x52 [snd_usb_audio]
This comes from snd_usb_autoresume() invoking down_read() and it's
used in a nested way. Although it's basically safe, per se (as these
are read locks), it's better to reduce such spurious warnings.
The read lock is needed to guarantee the execution of "shutdown"
(cleanup at disconnection) task after all concurrent tasks are
finished. This can be implemented in another better way.
Also, the current check of chip->in_pm isn't good enough for
protecting the racy execution of multiple auto-resumes.
This patch rewrites the logic of snd_usb_autoresume() & co; namely,
- The recursive call of autopm is avoided by the new refcount,
chip->active. The chip->in_pm flag is removed accordingly.
- Instead of rwsem, another refcount, chip->usage_count, is introduced
for tracking the period to delay the shutdown procedure. At
the last clear of this refcount, wake_up() to the shutdown waiter is
called.
- The shutdown flag is replaced with shutdown atomic count; this is
for reducing the lock.
- Two new helpers are introduced to simplify the management of these
refcounts; snd_usb_lock_shutdown() increases the usage_count, checks
the shutdown state, and does autoresume. snd_usb_unlock_shutdown()
does the opposite. Most of mixer and other codes just need this,
and simply returns an error if it receives an error from lock.
Fixes: 9003ebb13f61 ('ALSA: usb-audio: Fix runtime PM unbalance')
Reported-and-tested-by: Alexnader Kuleshov <kuleshovmail@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2015-08-25 21:09:00 +07:00
|
|
|
atomic_dec(&chip->active); /* allow autopm after this point */
|
2011-03-11 20:51:12 +07:00
|
|
|
return err;
|
2010-03-05 01:46:13 +07:00
|
|
|
}
|
2014-01-20 22:51:16 +07:00
|
|
|
|
|
|
|
static int usb_audio_resume(struct usb_interface *intf)
|
|
|
|
{
|
|
|
|
return __usb_audio_resume(intf, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int usb_audio_reset_resume(struct usb_interface *intf)
|
|
|
|
{
|
|
|
|
return __usb_audio_resume(intf, true);
|
|
|
|
}
|
2010-03-22 22:55:35 +07:00
|
|
|
#else
|
|
|
|
#define usb_audio_suspend NULL
|
|
|
|
#define usb_audio_resume NULL
|
2014-01-20 22:51:16 +07:00
|
|
|
#define usb_audio_reset_resume NULL
|
2010-03-05 01:46:13 +07:00
|
|
|
#endif /* CONFIG_PM */
|
|
|
|
|
2017-08-06 13:48:56 +07:00
|
|
|
static const struct usb_device_id usb_audio_ids [] = {
|
2010-03-05 01:46:13 +07:00
|
|
|
#include "quirks-table.h"
|
|
|
|
{ .match_flags = (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS),
|
|
|
|
.bInterfaceClass = USB_CLASS_AUDIO,
|
|
|
|
.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL },
|
|
|
|
{ } /* Terminating entry */
|
|
|
|
};
|
2013-04-04 04:18:50 +07:00
|
|
|
MODULE_DEVICE_TABLE(usb, usb_audio_ids);
|
2010-03-05 01:46:13 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* entry point for linux usb interface
|
|
|
|
*/
|
|
|
|
|
|
|
|
static struct usb_driver usb_audio_driver = {
|
|
|
|
.name = "snd-usb-audio",
|
|
|
|
.probe = usb_audio_probe,
|
|
|
|
.disconnect = usb_audio_disconnect,
|
|
|
|
.suspend = usb_audio_suspend,
|
|
|
|
.resume = usb_audio_resume,
|
2014-01-20 22:51:16 +07:00
|
|
|
.reset_resume = usb_audio_reset_resume,
|
2010-03-05 01:46:13 +07:00
|
|
|
.id_table = usb_audio_ids,
|
2011-03-11 20:51:12 +07:00
|
|
|
.supports_autosuspend = 1,
|
2010-03-05 01:46:13 +07:00
|
|
|
};
|
|
|
|
|
2013-10-09 18:52:32 +07:00
|
|
|
module_usb_driver(usb_audio_driver);
|