summaryrefslogtreecommitdiff
path: root/sound/usb
diff options
context:
space:
mode:
Diffstat (limited to 'sound/usb')
-rw-r--r--sound/usb/card.c199
-rw-r--r--sound/usb/clock.c9
-rw-r--r--sound/usb/mixer.c418
-rw-r--r--sound/usb/mixer.h6
-rw-r--r--sound/usb/mixer_maps.c65
-rw-r--r--sound/usb/mixer_quirks.c36
-rw-r--r--sound/usb/mixer_quirks.h4
-rw-r--r--sound/usb/mixer_scarlett.c6
-rw-r--r--sound/usb/pcm.c84
-rw-r--r--sound/usb/quirks-table.h10
-rw-r--r--sound/usb/quirks.c59
-rw-r--r--sound/usb/stream.c687
-rw-r--r--sound/usb/usbaudio.h6
13 files changed, 1124 insertions, 465 deletions
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 4a1c6bb3dfa0..f6c3c1cd591e 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -221,32 +221,13 @@ 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;
- void *control_header;
int i, protocol;
- int rest_bytes;
/* find audiocontrol interface */
host_iface = &usb_ifnum_to_if(dev, ctrlif)->altsetting[0];
- control_header = snd_usb_find_csint_desc(host_iface->extra,
- host_iface->extralen,
- NULL, UAC_HEADER);
altsd = get_iface_desc(host_iface);
protocol = altsd->bInterfaceProtocol;
- if (!control_header) {
- dev_err(&dev->dev, "cannot find UAC_HEADER\n");
- return -EINVAL;
- }
-
- rest_bytes = (void *)(host_iface->extra + host_iface->extralen) -
- control_header;
-
- /* just to be sure -- this shouldn't hit at all */
- if (rest_bytes <= 0) {
- dev_err(&dev->dev, "invalid control header\n");
- return -EINVAL;
- }
-
switch (protocol) {
default:
dev_warn(&dev->dev,
@@ -255,7 +236,25 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
/* fall through */
case UAC_VERSION_1: {
- struct uac1_ac_header_descriptor *h1 = control_header;
+ struct uac1_ac_header_descriptor *h1;
+ int rest_bytes;
+
+ h1 = snd_usb_find_csint_desc(host_iface->extra,
+ host_iface->extralen,
+ NULL, UAC_HEADER);
+ if (!h1) {
+ 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;
+ }
if (rest_bytes < sizeof(*h1)) {
dev_err(&dev->dev, "too short v1 buffer descriptor\n");
@@ -308,6 +307,20 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
return -EINVAL;
}
+ 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;
+ }
+
for (i = 0; i < assoc->bInterfaceCount; i++) {
int intf = assoc->bFirstInterface + i;
@@ -349,6 +362,90 @@ static int snd_usb_audio_dev_free(struct snd_device *device)
return snd_usb_audio_free(chip);
}
+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;
+
+ if (quirk && quirk->product_name && *quirk->product_name) {
+ strlcpy(card->shortname, quirk->product_name,
+ sizeof(card->shortname));
+ return;
+ }
+
+ /* retrieve the device string as shortname */
+ if (!dev->descriptor.iProduct ||
+ usb_string(dev, dev->descriptor.iProduct,
+ card->shortname, sizeof(card->shortname)) <= 0) {
+ /* no name available from anywhere, so use ID */
+ 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;
+ int len;
+
+ /* shortcut - if any pre-defined string is given, use it */
+ if (quirk && quirk->profile_name && *quirk->profile_name) {
+ strlcpy(card->longname, quirk->profile_name,
+ sizeof(card->longname));
+ return;
+ }
+
+ if (quirk && quirk->vendor_name && *quirk->vendor_name) {
+ len = strlcpy(card->longname, quirk->vendor_name, sizeof(card->longname));
+ } 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;
+ }
+}
+
/*
* create a chip instance and set its names.
*/
@@ -360,7 +457,7 @@ static int snd_usb_audio_create(struct usb_interface *intf,
{
struct snd_card *card;
struct snd_usb_audio *chip;
- int err, len;
+ int err;
char component[14];
static struct snd_device_ops ops = {
.dev_free = snd_usb_audio_dev_free,
@@ -422,64 +519,8 @@ static int snd_usb_audio_create(struct usb_interface *intf,
USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id));
snd_component_add(card, component);
- /* retrieve the device string as shortname */
- if (quirk && quirk->product_name && *quirk->product_name) {
- strlcpy(card->shortname, quirk->product_name, sizeof(card->shortname));
- } else {
- 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);
-
- /* retrieve the vendor and device strings as longname */
- if (quirk && quirk->vendor_name && *quirk->vendor_name) {
- len = strlcpy(card->longname, quirk->vendor_name, sizeof(card->longname));
- } else {
- 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;
- }
+ usb_audio_make_shortname(dev, chip, quirk);
+ usb_audio_make_longname(dev, chip, quirk);
snd_usb_audio_create_proc(chip);
diff --git a/sound/usb/clock.c b/sound/usb/clock.c
index 0b030d8fe3fa..17673f37fcc8 100644
--- a/sound/usb/clock.c
+++ b/sound/usb/clock.c
@@ -587,8 +587,15 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
default:
return set_sample_rate_v1(chip, iface, alts, fmt, rate);
- case UAC_VERSION_2:
case UAC_VERSION_3:
+ if (chip->badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) {
+ if (rate != UAC3_BADD_SAMPLING_RATE)
+ return -ENXIO;
+ else
+ return 0;
+ }
+ /* fall through */
+ case UAC_VERSION_2:
return set_sample_rate_v2v3(chip, iface, alts, fmt, rate);
}
}
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index bb5ab7a7dfa5..173979e05e63 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -112,14 +112,12 @@ enum {
#include "mixer_maps.c"
static const struct usbmix_name_map *
-find_map(struct mixer_build *state, int unitid, int control)
+find_map(const struct usbmix_name_map *p, int unitid, int control)
{
- const struct usbmix_name_map *p = state->map;
-
if (!p)
return NULL;
- for (p = state->map; p->id; p++) {
+ for (; p->id; p++) {
if (p->id == unitid &&
(!control || !p->control || control == p->control))
return p;
@@ -201,10 +199,10 @@ static void *find_audio_control_unit(struct mixer_build *state,
/*
* copy a string with the given id
*/
-static int snd_usb_copy_string_desc(struct mixer_build *state,
+static int snd_usb_copy_string_desc(struct snd_usb_audio *chip,
int index, char *buf, int maxlen)
{
- int len = usb_string(state->chip->dev, index, buf, maxlen - 1);
+ int len = usb_string(chip->dev, index, buf, maxlen - 1);
if (len < 0)
return 0;
@@ -658,14 +656,14 @@ static struct iterm_name_combo {
{ 0 },
};
-static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm,
+static int get_term_name(struct snd_usb_audio *chip, struct usb_audio_term *iterm,
unsigned char *name, int maxlen, int term_only)
{
struct iterm_name_combo *names;
int len;
if (iterm->name) {
- len = snd_usb_copy_string_desc(state, iterm->name,
+ len = snd_usb_copy_string_desc(chip, iterm->name,
name, maxlen);
if (len)
return len;
@@ -1341,16 +1339,16 @@ static struct usb_feature_control_info *get_feature_control_info(int control)
return NULL;
}
-static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
- unsigned int ctl_mask, int control,
- struct usb_audio_term *iterm, int unitid,
- int readonly_mask)
+static void __build_feature_ctl(struct usb_mixer_interface *mixer,
+ const struct usbmix_name_map *imap,
+ unsigned int ctl_mask, int control,
+ struct usb_audio_term *iterm,
+ struct usb_audio_term *oterm,
+ int unitid, int nameid, int readonly_mask)
{
- struct uac_feature_unit_descriptor *desc = raw_desc;
struct usb_feature_control_info *ctl_info;
unsigned int len = 0;
int mapped_name = 0;
- int nameid = uac_feature_unit_iFeature(desc);
struct snd_kcontrol *kctl;
struct usb_mixer_elem_info *cval;
const struct usbmix_name_map *map;
@@ -1361,14 +1359,14 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
return;
}
- map = find_map(state, unitid, control);
+ map = find_map(imap, unitid, control);
if (check_ignored_ctl(map))
return;
cval = kzalloc(sizeof(*cval), GFP_KERNEL);
if (!cval)
return;
- snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid);
+ snd_usb_mixer_elem_init_std(&cval->head, mixer, unitid);
cval->control = control;
cval->cmask = ctl_mask;
@@ -1377,7 +1375,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
kfree(cval);
return;
}
- if (state->mixer->protocol == UAC_VERSION_1)
+ if (mixer->protocol == UAC_VERSION_1)
cval->val_type = ctl_info->type;
else /* UAC_VERSION_2 */
cval->val_type = ctl_info->type_uac2 >= 0 ?
@@ -1406,7 +1404,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
if (!kctl) {
- usb_audio_err(state->chip, "cannot malloc kcontrol\n");
+ usb_audio_err(mixer->chip, "cannot malloc kcontrol\n");
kfree(cval);
return;
}
@@ -1415,7 +1413,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name));
mapped_name = len != 0;
if (!len && nameid)
- len = snd_usb_copy_string_desc(state, nameid,
+ len = snd_usb_copy_string_desc(mixer->chip, nameid,
kctl->id.name, sizeof(kctl->id.name));
switch (control) {
@@ -1430,10 +1428,12 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
* - otherwise, anonymous name.
*/
if (!len) {
- len = get_term_name(state, iterm, kctl->id.name,
- sizeof(kctl->id.name), 1);
- if (!len)
- len = get_term_name(state, &state->oterm,
+ if (iterm)
+ len = get_term_name(mixer->chip, iterm,
+ kctl->id.name,
+ sizeof(kctl->id.name), 1);
+ if (!len && oterm)
+ len = get_term_name(mixer->chip, oterm,
kctl->id.name,
sizeof(kctl->id.name), 1);
if (!len)
@@ -1442,15 +1442,15 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
}
if (!mapped_name)
- check_no_speaker_on_headset(kctl, state->mixer->chip->card);
+ check_no_speaker_on_headset(kctl, mixer->chip->card);
/*
* determine the stream direction:
* if the connected output is USB stream, then it's likely a
* capture stream. otherwise it should be playback (hopefully :)
*/
- if (!mapped_name && !(state->oterm.type >> 16)) {
- if ((state->oterm.type & 0xff00) == 0x0100)
+ if (!mapped_name && oterm && !(oterm->type >> 16)) {
+ if ((oterm->type & 0xff00) == 0x0100)
append_ctl_name(kctl, " Capture");
else
append_ctl_name(kctl, " Playback");
@@ -1478,7 +1478,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
}
}
- snd_usb_mixer_fu_apply_quirk(state->mixer, cval, unitid, kctl);
+ snd_usb_mixer_fu_apply_quirk(mixer, cval, unitid, kctl);
range = (cval->max - cval->min) / cval->res;
/*
@@ -1487,26 +1487,46 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
* devices. It will definitively catch all buggy Logitech devices.
*/
if (range > 384) {
- usb_audio_warn(state->chip,
+ usb_audio_warn(mixer->chip,
"Warning! Unlikely big volume range (=%u), cval->res is probably wrong.",
range);
- usb_audio_warn(state->chip,
+ usb_audio_warn(mixer->chip,
"[%d] FU [%s] ch = %d, val = %d/%d/%d",
cval->head.id, kctl->id.name, cval->channels,
cval->min, cval->max, cval->res);
}
- usb_audio_dbg(state->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d\n",
+ usb_audio_dbg(mixer->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d\n",
cval->head.id, kctl->id.name, cval->channels,
cval->min, cval->max, cval->res);
snd_usb_mixer_add_control(&cval->head, kctl);
}
+static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
+ unsigned int ctl_mask, int control,
+ struct usb_audio_term *iterm, int unitid,
+ int readonly_mask)
+{
+ struct uac_feature_unit_descriptor *desc = raw_desc;
+ int nameid = uac_feature_unit_iFeature(desc);
+
+ __build_feature_ctl(state->mixer, state->map, ctl_mask, control,
+ iterm, &state->oterm, unitid, nameid, readonly_mask);
+}
+
+static void build_feature_ctl_badd(struct usb_mixer_interface *mixer,
+ unsigned int ctl_mask, int control, int unitid,
+ const struct usbmix_name_map *badd_map)
+{
+ __build_feature_ctl(mixer, badd_map, ctl_mask, control,
+ NULL, NULL, unitid, 0, 0);
+}
+
static void get_connector_control_name(struct mixer_build *state,
struct usb_audio_term *term,
bool is_input, char *name, int name_size)
{
- int name_len = get_term_name(state, term, name, name_size, 0);
+ int name_len = get_term_name(state->chip, term, name, name_size, 0);
if (name_len == 0)
strlcpy(name, "Unknown", name_size);
@@ -1605,7 +1625,7 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid,
}
kctl->private_free = snd_usb_mixer_elem_free;
- ret = snd_usb_copy_string_desc(state, hdr->iClockSource,
+ ret = snd_usb_copy_string_desc(state->chip, hdr->iClockSource,
name, sizeof(name));
if (ret > 0)
snprintf(kctl->id.name, sizeof(kctl->id.name),
@@ -1815,7 +1835,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state,
struct snd_kcontrol *kctl;
const struct usbmix_name_map *map;
- map = find_map(state, unitid, 0);
+ map = find_map(state->map, unitid, 0);
if (check_ignored_ctl(map))
return;
@@ -1848,7 +1868,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state,
len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name));
if (!len)
- len = get_term_name(state, iterm, kctl->id.name,
+ len = get_term_name(state->chip, iterm, kctl->id.name,
sizeof(kctl->id.name), 0);
if (!len)
len = sprintf(kctl->id.name, "Mixer Source %d", in_ch + 1);
@@ -2114,7 +2134,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid,
if (!(controls[valinfo->control / 8] & (1 << ((valinfo->control % 8) - 1))))
continue;
- map = find_map(state, unitid, valinfo->control);
+ map = find_map(state->map, unitid, valinfo->control);
if (check_ignored_ctl(map))
continue;
cval = kzalloc(sizeof(*cval), GFP_KERNEL);
@@ -2162,7 +2182,8 @@ static int build_audio_procunit(struct mixer_build *state, int unitid,
nameid = uac_processing_unit_iProcessing(desc, state->mixer->protocol);
len = 0;
if (nameid)
- len = snd_usb_copy_string_desc(state, nameid,
+ len = snd_usb_copy_string_desc(state->chip,
+ nameid,
kctl->id.name,
sizeof(kctl->id.name));
if (!len)
@@ -2317,7 +2338,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
if (desc->bNrInPins == 1) /* only one ? nonsense! */
return 0;
- map = find_map(state, unitid, 0);
+ map = find_map(state->map, unitid, 0);
if (check_ignored_ctl(map))
return 0;
@@ -2358,7 +2379,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
len = check_mapped_selector_name(state, unitid, i, namelist[i],
MAX_ITEM_NAME_LEN);
if (! len && check_input_term(state, desc->baSourceID[i], &iterm) >= 0)
- len = get_term_name(state, &iterm, namelist[i], MAX_ITEM_NAME_LEN, 0);
+ len = get_term_name(state->chip, &iterm, namelist[i],
+ MAX_ITEM_NAME_LEN, 0);
if (! len)
sprintf(namelist[i], "Input %u", i);
}
@@ -2380,12 +2402,12 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
/* if iSelector is given, use it */
nameid = uac_selector_unit_iSelector(desc);
if (nameid)
- len = snd_usb_copy_string_desc(state, nameid,
+ len = snd_usb_copy_string_desc(state->chip, nameid,
kctl->id.name,
sizeof(kctl->id.name));
/* ... or pick up the terminal name at next */
if (!len)
- len = get_term_name(state, &state->oterm,
+ len = get_term_name(state->chip, &state->oterm,
kctl->id.name, sizeof(kctl->id.name), 0);
/* ... or use the fixed string "USB" as the last resort */
if (!len)
@@ -2503,6 +2525,246 @@ static int snd_usb_mixer_dev_free(struct snd_device *device)
return 0;
}
+/* UAC3 predefined channels configuration */
+struct uac3_badd_profile {
+ int subclass;
+ const char *name;
+ int c_chmask; /* capture channels mask */
+ int p_chmask; /* playback channels mask */
+ int st_chmask; /* side tone mixing channel mask */
+};
+
+static struct uac3_badd_profile uac3_badd_profiles[] = {
+ {
+ /*
+ * BAIF, BAOF or combination of both
+ * IN: Mono or Stereo cfg, Mono alt possible
+ * OUT: Mono or Stereo cfg, Mono alt possible
+ */
+ .subclass = UAC3_FUNCTION_SUBCLASS_GENERIC_IO,
+ .name = "GENERIC IO",
+ .c_chmask = -1, /* dynamic channels */
+ .p_chmask = -1, /* dynamic channels */
+ },
+ {
+ /* BAOF; Stereo only cfg, Mono alt possible */
+ .subclass = UAC3_FUNCTION_SUBCLASS_HEADPHONE,
+ .name = "HEADPHONE",
+ .p_chmask = 3,
+ },
+ {
+ /* BAOF; Mono or Stereo cfg, Mono alt possible */
+ .subclass = UAC3_FUNCTION_SUBCLASS_SPEAKER,
+ .name = "SPEAKER",
+ .p_chmask = -1, /* dynamic channels */
+ },
+ {
+ /* BAIF; Mono or Stereo cfg, Mono alt possible */
+ .subclass = UAC3_FUNCTION_SUBCLASS_MICROPHONE,
+ .name = "MICROPHONE",
+ .c_chmask = -1, /* dynamic channels */
+ },
+ {
+ /*
+ * BAIOF topology
+ * IN: Mono only
+ * OUT: Mono or Stereo cfg, Mono alt possible
+ */
+ .subclass = UAC3_FUNCTION_SUBCLASS_HEADSET,
+ .name = "HEADSET",
+ .c_chmask = 1,
+ .p_chmask = -1, /* dynamic channels */
+ .st_chmask = 1,
+ },
+ {
+ /* BAIOF; IN: Mono only; OUT: Stereo only, Mono alt possible */
+ .subclass = UAC3_FUNCTION_SUBCLASS_HEADSET_ADAPTER,
+ .name = "HEADSET ADAPTER",
+ .c_chmask = 1,
+ .p_chmask = 3,
+ .st_chmask = 1,
+ },
+ {
+ /* BAIF + BAOF; IN: Mono only; OUT: Mono only */
+ .subclass = UAC3_FUNCTION_SUBCLASS_SPEAKERPHONE,
+ .name = "SPEAKERPHONE",
+ .c_chmask = 1,
+ .p_chmask = 1,
+ },
+ { 0 } /* terminator */
+};
+
+static bool uac3_badd_func_has_valid_channels(struct usb_mixer_interface *mixer,
+ struct uac3_badd_profile *f,
+ int c_chmask, int p_chmask)
+{
+ /*
+ * If both playback/capture channels are dynamic, make sure
+ * at least one channel is present
+ */
+ if (f->c_chmask < 0 && f->p_chmask < 0) {
+ if (!c_chmask && !p_chmask) {
+ usb_audio_warn(mixer->chip, "BAAD %s: no channels?",
+ f->name);
+ return false;
+ }
+ return true;
+ }
+
+ if ((f->c_chmask < 0 && !c_chmask) ||
+ (f->c_chmask >= 0 && f->c_chmask != c_chmask)) {
+ usb_audio_warn(mixer->chip, "BAAD %s c_chmask mismatch",
+ f->name);
+ return false;
+ }
+ if ((f->p_chmask < 0 && !p_chmask) ||
+ (f->p_chmask >= 0 && f->p_chmask != p_chmask)) {
+ usb_audio_warn(mixer->chip, "BAAD %s p_chmask mismatch",
+ f->name);
+ return false;
+ }
+ return true;
+}
+
+/*
+ * create mixer controls for UAC3 BADD profiles
+ *
+ * UAC3 BADD device doesn't contain CS descriptors thus we will guess everything
+ *
+ * BADD device may contain Mixer Unit, which doesn't have any controls, skip it
+ */
+static int snd_usb_mixer_controls_badd(struct usb_mixer_interface *mixer,
+ int ctrlif)
+{
+ struct usb_device *dev = mixer->chip->dev;
+ struct usb_interface_assoc_descriptor *assoc;
+ int badd_profile = mixer->chip->badd_profile;
+ struct uac3_badd_profile *f;
+ const struct usbmix_ctl_map *map;
+ int p_chmask = 0, c_chmask = 0, st_chmask = 0;
+ int i;
+
+ assoc = usb_ifnum_to_if(dev, ctrlif)->intf_assoc;
+
+ /* Detect BADD capture/playback channels from AS EP descriptors */
+ for (i = 0; i < assoc->bInterfaceCount; i++) {
+ int intf = assoc->bFirstInterface + i;
+
+ struct usb_interface *iface;
+ struct usb_host_interface *alts;
+ struct usb_interface_descriptor *altsd;
+ unsigned int maxpacksize;
+ char dir_in;
+ int chmask, num;
+
+ if (intf == ctrlif)
+ continue;
+
+ iface = usb_ifnum_to_if(dev, intf);
+ num = iface->num_altsetting;
+
+ if (num < 2)
+ return -EINVAL;
+
+ /*
+ * The number of Channels in an AudioStreaming interface
+ * and the audio sample bit resolution (16 bits or 24
+ * bits) can be derived from the wMaxPacketSize field in
+ * the Standard AS Audio Data Endpoint descriptor in
+ * Alternate Setting 1
+ */
+ alts = &iface->altsetting[1];
+ altsd = get_iface_desc(alts);
+
+ if (altsd->bNumEndpoints < 1)
+ return -EINVAL;
+
+ /* check direction */
+ dir_in = (get_endpoint(alts, 0)->bEndpointAddress & USB_DIR_IN);
+ maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
+
+ switch (maxpacksize) {
+ default:
+ usb_audio_err(mixer->chip,
+ "incorrect wMaxPacketSize 0x%x for BADD profile\n",
+ maxpacksize);
+ return -EINVAL;
+ case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_16:
+ case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_16:
+ case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_24:
+ case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_24:
+ chmask = 1;
+ break;
+ case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_16:
+ case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_16:
+ case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_24:
+ case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_24:
+ chmask = 3;
+ break;
+ }
+
+ if (dir_in)
+ c_chmask = chmask;
+ else
+ p_chmask = chmask;
+ }
+
+ usb_audio_dbg(mixer->chip,
+ "UAC3 BADD profile 0x%x: detected c_chmask=%d p_chmask=%d\n",
+ badd_profile, c_chmask, p_chmask);
+
+ /* check the mapping table */
+ for (map = uac3_badd_usbmix_ctl_maps; map->id; map++) {
+ if (map->id == badd_profile)
+ break;
+ }
+
+ if (!map->id)
+ return -EINVAL;
+
+ for (f = uac3_badd_profiles; f->name; f++) {
+ if (badd_profile == f->subclass)
+ break;
+ }
+ if (!f->name)
+ return -EINVAL;
+ if (!uac3_badd_func_has_valid_channels(mixer, f, c_chmask, p_chmask))
+ return -EINVAL;
+ st_chmask = f->st_chmask;
+
+ /* Playback */
+ if (p_chmask) {
+ /* Master channel, always writable */
+ build_feature_ctl_badd(mixer, 0, UAC_FU_MUTE,
+ UAC3_BADD_FU_ID2, map->map);
+ /* Mono/Stereo volume channels, always writable */
+ build_feature_ctl_badd(mixer, p_chmask, UAC_FU_VOLUME,
+ UAC3_BADD_FU_ID2, map->map);
+ }
+
+ /* Capture */
+ if (c_chmask) {
+ /* Master channel, always writable */
+ build_feature_ctl_badd(mixer, 0, UAC_FU_MUTE,
+ UAC3_BADD_FU_ID5, map->map);
+ /* Mono/Stereo volume channels, always writable */
+ build_feature_ctl_badd(mixer, c_chmask, UAC_FU_VOLUME,
+ UAC3_BADD_FU_ID5, map->map);
+ }
+
+ /* Side tone-mixing */
+ if (st_chmask) {
+ /* Master channel, always writable */
+ build_feature_ctl_badd(mixer, 0, UAC_FU_MUTE,
+ UAC3_BADD_FU_ID7, map->map);
+ /* Mono volume channel, always writable */
+ build_feature_ctl_badd(mixer, 1, UAC_FU_VOLUME,
+ UAC3_BADD_FU_ID7, map->map);
+ }
+
+ return 0;
+}
+
/*
* create mixer controls
*
@@ -2606,9 +2868,9 @@ void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid)
{
struct usb_mixer_elem_list *list;
- for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem) {
+ for_each_mixer_elem(list, mixer, unitid) {
struct usb_mixer_elem_info *info =
- (struct usb_mixer_elem_info *)list;
+ mixer_elem_list_to_info(list);
/* invalidate cache, so the value is read from the device */
info->cached = 0;
snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
@@ -2619,7 +2881,7 @@ void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid)
static void snd_usb_mixer_dump_cval(struct snd_info_buffer *buffer,
struct usb_mixer_elem_list *list)
{
- struct usb_mixer_elem_info *cval = (struct usb_mixer_elem_info *)list;
+ struct usb_mixer_elem_info *cval = mixer_elem_list_to_info(list);
static char *val_types[] = {"BOOLEAN", "INV_BOOLEAN",
"S8", "U8", "S16", "U16"};
snd_iprintf(buffer, " Info: id=%i, control=%i, cmask=0x%x, "
@@ -2645,8 +2907,7 @@ static void snd_usb_mixer_proc_read(struct snd_info_entry *entry,
mixer->ignore_ctl_error);
snd_iprintf(buffer, "Card: %s\n", chip->card->longname);
for (unitid = 0; unitid < MAX_ID_ELEMS; unitid++) {
- for (list = mixer->id_elems[unitid]; list;
- list = list->next_id_elem) {
+ for_each_mixer_elem(list, mixer, unitid) {
snd_iprintf(buffer, " Unit: %i\n", list->id);
if (list->kctl)
snd_iprintf(buffer,
@@ -2676,19 +2937,19 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer,
return;
}
- for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem)
+ for_each_mixer_elem(list, mixer, unitid)
count++;
if (count == 0)
return;
- for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem) {
+ for_each_mixer_elem(list, mixer, unitid) {
struct usb_mixer_elem_info *info;
if (!list->kctl)
continue;
- info = (struct usb_mixer_elem_info *)list;
+ info = mixer_elem_list_to_info(list);
if (count > 1 && info->control != control)
continue;
@@ -2809,6 +3070,48 @@ static int snd_usb_mixer_status_create(struct usb_mixer_interface *mixer)
return 0;
}
+static int keep_iface_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = mixer->chip->keep_iface;
+ return 0;
+}
+
+static int keep_iface_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+ bool keep_iface = !!ucontrol->value.integer.value[0];
+
+ if (mixer->chip->keep_iface == keep_iface)
+ return 0;
+ mixer->chip->keep_iface = keep_iface;
+ return 1;
+}
+
+static const struct snd_kcontrol_new keep_iface_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .name = "Keep Interface",
+ .info = snd_ctl_boolean_mono_info,
+ .get = keep_iface_ctl_get,
+ .put = keep_iface_ctl_put,
+};
+
+static int create_keep_iface_ctl(struct usb_mixer_interface *mixer)
+{
+ struct snd_kcontrol *kctl = snd_ctl_new1(&keep_iface_ctl, mixer);
+
+ /* need only one control per card */
+ if (snd_ctl_find_id(mixer->chip->card, &kctl->id)) {
+ snd_ctl_free_one(kctl);
+ return 0;
+ }
+
+ return snd_ctl_add(mixer->chip->card, kctl);
+}
+
int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
int ignore_error)
{
@@ -2847,8 +3150,16 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
break;
}
- if ((err = snd_usb_mixer_controls(mixer)) < 0 ||
- (err = snd_usb_mixer_status_create(mixer)) < 0)
+ if (mixer->protocol == UAC_VERSION_3 &&
+ chip->badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) {
+ if ((err = snd_usb_mixer_controls_badd(mixer, ctrlif)) < 0)
+ goto _error;
+ } else if ((err = snd_usb_mixer_controls(mixer)) < 0 ||
+ (err = snd_usb_mixer_status_create(mixer)) < 0) {
+ goto _error;
+ }
+ err = create_keep_iface_ctl(mixer);
+ if (err < 0)
goto _error;
snd_usb_mixer_apply_create_quirk(mixer);
@@ -2909,7 +3220,7 @@ int snd_usb_mixer_suspend(struct usb_mixer_interface *mixer)
static int restore_mixer_value(struct usb_mixer_elem_list *list)
{
- struct usb_mixer_elem_info *cval = (struct usb_mixer_elem_info *)list;
+ struct usb_mixer_elem_info *cval = mixer_elem_list_to_info(list);
int c, err, idx;
if (cval->cmask) {
@@ -2945,8 +3256,7 @@ int snd_usb_mixer_resume(struct usb_mixer_interface *mixer, bool reset_resume)
if (reset_resume) {
/* restore cached mixer values */
for (id = 0; id < MAX_ID_ELEMS; id++) {
- for (list = mixer->id_elems[id]; list;
- list = list->next_id_elem) {
+ for_each_mixer_elem(list, mixer, id) {
if (list->resume) {
err = list->resume(list);
if (err < 0)
@@ -2956,6 +3266,8 @@ int snd_usb_mixer_resume(struct usb_mixer_interface *mixer, bool reset_resume)
}
}
+ snd_usb_mixer_resume_quirk(mixer);
+
return snd_usb_mixer_activate(mixer);
}
#endif
diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h
index ba27f7ade670..e02653465e29 100644
--- a/sound/usb/mixer.h
+++ b/sound/usb/mixer.h
@@ -53,6 +53,12 @@ struct usb_mixer_elem_list {
usb_mixer_elem_resume_func_t resume;
};
+/* iterate over mixer element list of the given unit id */
+#define for_each_mixer_elem(list, mixer, id) \
+ for ((list) = (mixer)->id_elems[id]; (list); (list) = (list)->next_id_elem)
+#define mixer_elem_list_to_info(list) \
+ container_of(list, struct usb_mixer_elem_info, head)
+
struct usb_mixer_elem_info {
struct usb_mixer_elem_list head;
unsigned int control; /* CS or ICN (high byte) */
diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c
index eaa03acd4686..71069e110897 100644
--- a/sound/usb/mixer_maps.c
+++ b/sound/usb/mixer_maps.c
@@ -485,3 +485,68 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = {
{ 0 } /* terminator */
};
+/*
+ * Control map entries for UAC3 BADD profiles
+ */
+
+static struct usbmix_name_map uac3_badd_generic_io_map[] = {
+ { UAC3_BADD_FU_ID2, "Generic Out Playback" },
+ { UAC3_BADD_FU_ID5, "Generic In Capture" },
+ { 0 } /* terminator */
+};
+static struct usbmix_name_map uac3_badd_headphone_map[] = {
+ { UAC3_BADD_FU_ID2, "Headphone Playback" },
+ { 0 } /* terminator */
+};
+static struct usbmix_name_map uac3_badd_speaker_map[] = {
+ { UAC3_BADD_FU_ID2, "Speaker Playback" },
+ { 0 } /* terminator */
+};
+static struct usbmix_name_map uac3_badd_microphone_map[] = {
+ { UAC3_BADD_FU_ID5, "Mic Capture" },
+ { 0 } /* terminator */
+};
+/* Covers also 'headset adapter' profile */
+static struct usbmix_name_map uac3_badd_headset_map[] = {
+ { UAC3_BADD_FU_ID2, "Headset Playback" },
+ { UAC3_BADD_FU_ID5, "Headset Capture" },
+ { UAC3_BADD_FU_ID7, "Sidetone Mixing" },
+ { 0 } /* terminator */
+};
+static struct usbmix_name_map uac3_badd_speakerphone_map[] = {
+ { UAC3_BADD_FU_ID2, "Speaker Playback" },
+ { UAC3_BADD_FU_ID5, "Mic Capture" },
+ { 0 } /* terminator */
+};
+
+static struct usbmix_ctl_map uac3_badd_usbmix_ctl_maps[] = {
+ {
+ .id = UAC3_FUNCTION_SUBCLASS_GENERIC_IO,
+ .map = uac3_badd_generic_io_map,
+ },
+ {
+ .id = UAC3_FUNCTION_SUBCLASS_HEADPHONE,
+ .map = uac3_badd_headphone_map,
+ },
+ {
+ .id = UAC3_FUNCTION_SUBCLASS_SPEAKER,
+ .map = uac3_badd_speaker_map,
+ },
+ {
+ .id = UAC3_FUNCTION_SUBCLASS_MICROPHONE,
+ .map = uac3_badd_microphone_map,
+ },
+ {
+ .id = UAC3_FUNCTION_SUBCLASS_HEADSET,
+ .map = uac3_badd_headset_map,
+ },
+ {
+ .id = UAC3_FUNCTION_SUBCLASS_HEADSET_ADAPTER,
+ .map = uac3_badd_headset_map,
+ },
+ {
+ .id = UAC3_FUNCTION_SUBCLASS_SPEAKERPHONE,
+ .map = uac3_badd_speakerphone_map,
+ },
+ { 0 } /* terminator */
+};
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index 56537a156580..1b94387e18b6 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -1172,7 +1172,7 @@ void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
int unitid = 12; /* SamleRate ExtensionUnit ID */
list_for_each_entry(mixer, &chip->mixer_list, list) {
- cval = (struct usb_mixer_elem_info *)mixer->id_elems[unitid];
+ cval = mixer_elem_list_to_info(mixer->id_elems[unitid]);
if (cval) {
snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR,
cval->control << 8,
@@ -1799,6 +1799,26 @@ static int snd_soundblaster_e1_switch_create(struct usb_mixer_interface *mixer)
NULL);
}
+static void dell_dock_init_vol(struct snd_usb_audio *chip, int ch, int id)
+{
+ u16 buf = 0;
+
+ snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR,
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
+ ch, snd_usb_ctrl_intf(chip) | (id << 8),
+ &buf, 2);
+}
+
+static int dell_dock_mixer_init(struct usb_mixer_interface *mixer)
+{
+ /* fix to 0dB playback volumes */
+ dell_dock_init_vol(mixer->chip, 1, 16);
+ dell_dock_init_vol(mixer->chip, 2, 16);
+ dell_dock_init_vol(mixer->chip, 1, 19);
+ dell_dock_init_vol(mixer->chip, 2, 19);
+ return 0;
+}
+
int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
{
int err = 0;
@@ -1884,11 +1904,25 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
case USB_ID(0x041e, 0x323b): /* Creative Sound Blaster E1 */
err = snd_soundblaster_e1_switch_create(mixer);
break;
+ case USB_ID(0x0bda, 0x4014): /* Dell WD15 dock */
+ err = dell_dock_mixer_init(mixer);
+ break;
}
return err;
}
+#ifdef CONFIG_PM
+void snd_usb_mixer_resume_quirk(struct usb_mixer_interface *mixer)
+{
+ switch (mixer->chip->usb_id) {
+ case USB_ID(0x0bda, 0x4014): /* Dell WD15 dock */
+ dell_dock_mixer_init(mixer);
+ break;
+ }
+}
+#endif
+
void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer,
int unitid)
{
diff --git a/sound/usb/mixer_quirks.h b/sound/usb/mixer_quirks.h
index b5abd328a361..52be26db558f 100644
--- a/sound/usb/mixer_quirks.h
+++ b/sound/usb/mixer_quirks.h
@@ -14,5 +14,9 @@ void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer,
struct usb_mixer_elem_info *cval, int unitid,
struct snd_kcontrol *kctl);
+#ifdef CONFIG_PM
+void snd_usb_mixer_resume_quirk(struct usb_mixer_interface *mixer);
+#endif
+
#endif /* SND_USB_MIXER_QUIRKS_H */
diff --git a/sound/usb/mixer_scarlett.c b/sound/usb/mixer_scarlett.c
index c33e2378089d..4aeb9488a0c9 100644
--- a/sound/usb/mixer_scarlett.c
+++ b/sound/usb/mixer_scarlett.c
@@ -287,8 +287,7 @@ static int scarlett_ctl_switch_put(struct snd_kcontrol *kctl,
static int scarlett_ctl_resume(struct usb_mixer_elem_list *list)
{
- struct usb_mixer_elem_info *elem =
- container_of(list, struct usb_mixer_elem_info, head);
+ struct usb_mixer_elem_info *elem = mixer_elem_list_to_info(list);
int i;
for (i = 0; i < elem->channels; i++)
@@ -447,8 +446,7 @@ static int scarlett_ctl_enum_put(struct snd_kcontrol *kctl,
static int scarlett_ctl_enum_resume(struct usb_mixer_elem_list *list)
{
- struct usb_mixer_elem_info *elem =
- container_of(list, struct usb_mixer_elem_info, head);
+ struct usb_mixer_elem_info *elem = mixer_elem_list_to_info(list);
if (elem->cached)
snd_usb_set_cur_mix_value(elem, 0, 0, *elem->cache_val);
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 3cbfae6604f9..dc2dfec9effd 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -321,6 +321,7 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs,
struct usb_host_interface *alts;
struct usb_interface *iface;
unsigned int ep;
+ unsigned int ifnum;
/* Implicit feedback sync EPs consumers are always playback EPs */
if (subs->direction != SNDRV_PCM_STREAM_PLAYBACK)
@@ -330,44 +331,27 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs,
case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */
case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C600 */
ep = 0x81;
- iface = usb_ifnum_to_if(dev, 3);
-
- if (!iface || iface->num_altsetting == 0)
- return -EINVAL;
-
- alts = &iface->altsetting[1];
- goto add_sync_ep;
- break;
+ ifnum = 3;
+ goto add_sync_ep_from_ifnum;
case USB_ID(0x0763, 0x2080): /* M-Audio FastTrack Ultra */
case USB_ID(0x0763, 0x2081):
ep = 0x81;
- iface = usb_ifnum_to_if(dev, 2);
-
- if (!iface || iface->num_altsetting == 0)
- return -EINVAL;
-
- alts = &iface->altsetting[1];
- goto add_sync_ep;
- case USB_ID(0x2466, 0x8003):
+ ifnum = 2;
+ goto add_sync_ep_from_ifnum;
+ case USB_ID(0x2466, 0x8003): /* Fractal Audio Axe-Fx II */
ep = 0x86;
- iface = usb_ifnum_to_if(dev, 2);
-
- if (!iface || iface->num_altsetting == 0)
- return -EINVAL;
-
- alts = &iface->altsetting[1];
- goto add_sync_ep;
- case USB_ID(0x1397, 0x0002):
+ ifnum = 2;
+ goto add_sync_ep_from_ifnum;
+ case USB_ID(0x2466, 0x8010): /* Fractal Audio Axe-Fx III */
ep = 0x81;
- iface = usb_ifnum_to_if(dev, 1);
-
- if (!iface || iface->num_altsetting == 0)
- return -EINVAL;
-
- alts = &iface->altsetting[1];
- goto add_sync_ep;
-
+ ifnum = 2;
+ goto add_sync_ep_from_ifnum;
+ case USB_ID(0x1397, 0x0002): /* Behringer UFX1204 */
+ ep = 0x81;
+ ifnum = 1;
+ goto add_sync_ep_from_ifnum;
}
+
if (attr == USB_ENDPOINT_SYNC_ASYNC &&
altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC &&
altsd->bInterfaceProtocol == 2 &&
@@ -382,6 +366,14 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs,
/* No quirk */
return 0;
+add_sync_ep_from_ifnum:
+ iface = usb_ifnum_to_if(dev, ifnum);
+
+ if (!iface || iface->num_altsetting == 0)
+ return -EINVAL;
+
+ alts = &iface->altsetting[1];
+
add_sync_ep:
subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip,
alts, ep, !subs->direction,
@@ -507,7 +499,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
iface = usb_ifnum_to_if(dev, fmt->iface);
if (WARN_ON(!iface))
return -EINVAL;
- alts = &iface->altsetting[fmt->altset_idx];
+ alts = usb_altnum_to_altsetting(iface, fmt->altsetting);
altsd = get_iface_desc(alts);
if (WARN_ON(altsd->bAlternateSetting != fmt->altsetting))
return -EINVAL;
@@ -517,21 +509,21 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
/* close the old interface */
if (subs->interface >= 0 && subs->interface != fmt->iface) {
- err = usb_set_interface(subs->dev, subs->interface, 0);
- if (err < 0) {
- dev_err(&dev->dev,
- "%d:%d: return to setting 0 failed (%d)\n",
- fmt->iface, fmt->altsetting, err);
- return -EIO;
+ if (!subs->stream->chip->keep_iface) {
+ err = usb_set_interface(subs->dev, subs->interface, 0);
+ if (err < 0) {
+ dev_err(&dev->dev,
+ "%d:%d: return to setting 0 failed (%d)\n",
+ fmt->iface, fmt->altsetting, err);
+ return -EIO;
+ }
}
subs->interface = -1;
subs->altset_idx = 0;
}
/* set interface */
- if (subs->interface != fmt->iface ||
- subs->altset_idx != fmt->altset_idx) {
-
+ if (iface->cur_altsetting != alts) {
err = snd_usb_select_mode_quirk(subs, fmt);
if (err < 0)
return -EIO;
@@ -545,12 +537,11 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
}
dev_dbg(&dev->dev, "setting usb interface %d:%d\n",
fmt->iface, fmt->altsetting);
- subs->interface = fmt->iface;
- subs->altset_idx = fmt->altset_idx;
-
snd_usb_set_interface_quirk(dev);
}
+ subs->interface = fmt->iface;
+ subs->altset_idx = fmt->altset_idx;
subs->data_endpoint = snd_usb_add_endpoint(subs->stream->chip,
alts, fmt->endpoint, subs->direction,
SND_USB_ENDPOINT_TYPE_DATA);
@@ -1264,7 +1255,8 @@ static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction)
stop_endpoints(subs, true);
- if (subs->interface >= 0 &&
+ if (!as->chip->keep_iface &&
+ subs->interface >= 0 &&
!snd_usb_lock_shutdown(subs->stream->chip)) {
usb_set_interface(subs->dev, subs->interface, 0);
subs->interface = -1;
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index 754e632a27bd..0e37e358ca97 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -3371,5 +3371,15 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
}
}
},
+/* Dell WD15 Dock */
+{
+ USB_DEVICE(0x0bda, 0x4014),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ .vendor_name = "Dell",
+ .product_name = "WD15 Dock",
+ .profile_name = "Dell-WD15-Dock",
+ .ifnum = QUIRK_NO_INTERFACE
+ }
+},
#undef USB_DEVICE_VENDOR_SPEC
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index acbeb52f6fd6..f4b69173682c 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -851,6 +851,36 @@ static int snd_usb_mbox2_boot_quirk(struct usb_device *dev)
return 0; /* Successful boot */
}
+static int snd_usb_axefx3_boot_quirk(struct usb_device *dev)
+{
+ int err;
+
+ dev_dbg(&dev->dev, "Waiting for Axe-Fx III to boot up...\n");
+
+ /* If the Axe-Fx III has not fully booted, it will timeout when trying
+ * to enable the audio streaming interface. A more generous timeout is
+ * used here to detect when the Axe-Fx III has finished booting as the
+ * set interface message will be acked once it has
+ */
+ err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE,
+ 1, 1, NULL, 0, 120000);
+ if (err < 0) {
+ dev_err(&dev->dev,
+ "failed waiting for Axe-Fx III to boot: %d\n", err);
+ return err;
+ }
+
+ dev_dbg(&dev->dev, "Axe-Fx III is now ready\n");
+
+ err = usb_set_interface(dev, 1, 0);
+ if (err < 0)
+ dev_dbg(&dev->dev,
+ "error stopping Axe-Fx III interface: %d\n", err);
+
+ return 0;
+}
+
/*
* Setup quirks
*/
@@ -1026,6 +1056,8 @@ int snd_usb_apply_boot_quirk(struct usb_device *dev,
return snd_usb_fasttrackpro_boot_quirk(dev);
case USB_ID(0x047f, 0xc010): /* Plantronics Gamecom 780 */
return snd_usb_gamecon780_boot_quirk(dev);
+ case USB_ID(0x2466, 0x8010): /* Fractal Audio Axe-Fx 3 */
+ return snd_usb_axefx3_boot_quirk(dev);
}
return 0;
@@ -1327,20 +1359,47 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
/* XMOS based USB DACs */
switch (chip->usb_id) {
+ case USB_ID(0x1511, 0x0037): /* AURALiC VEGA */
+ case USB_ID(0x20b1, 0x0002): /* Wyred 4 Sound DAC-2 DSD */
+ case USB_ID(0x20b1, 0x2004): /* Matrix Audio X-SPDIF 2 */
case USB_ID(0x20b1, 0x3008): /* iFi Audio micro/nano iDSD */
case USB_ID(0x20b1, 0x2008): /* Matrix Audio X-Sabre */
case USB_ID(0x20b1, 0x300a): /* Matrix Audio Mini-i Pro */
case USB_ID(0x22d9, 0x0416): /* OPPO HA-1 */
+ case USB_ID(0x22d9, 0x0436): /* OPPO Sonica */
+ case USB_ID(0x22d9, 0x0461): /* OPPO UDP-205 */
+ case USB_ID(0x2522, 0x0012): /* LH Labs VI DAC Infinity */
+ case USB_ID(0x25ce, 0x001f): /* Mytek Brooklyn DAC */
+ case USB_ID(0x25ce, 0x0021): /* Mytek Manhattan DAC */
+ case USB_ID(0x25ce, 0x8025): /* Mytek Brooklyn DAC+ */
case USB_ID(0x2772, 0x0230): /* Pro-Ject Pre Box S2 Digital */
if (fp->altsetting == 2)
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
break;
+ case USB_ID(0x0d8c, 0x0316): /* Hegel HD12 DSD */
+ case USB_ID(0x16b0, 0x06b2): /* NuPrime DAC-10 */
+ case USB_ID(0x16d0, 0x0733): /* Furutech ADL Stratos */
+ case USB_ID(0x16d0, 0x09db): /* NuPrime Audio DAC-9 */
+ case USB_ID(0x1db5, 0x0003): /* Bryston BDA3 */
case USB_ID(0x20b1, 0x000a): /* Gustard DAC-X20U */
+ case USB_ID(0x20b1, 0x2005): /* Denafrips Ares DAC */
case USB_ID(0x20b1, 0x2009): /* DIYINHK DSD DXD 384kHz USB to I2S/DSD */
case USB_ID(0x20b1, 0x2023): /* JLsounds I2SoverUSB */
+ case USB_ID(0x20b1, 0x3021): /* Eastern El. MiniMax Tube DAC Supreme */
case USB_ID(0x20b1, 0x3023): /* Aune X1S 32BIT/384 DSD DAC */
+ case USB_ID(0x20b1, 0x302d): /* Unison Research Unico CD Due */
+ case USB_ID(0x20b1, 0x3036): /* Holo Springs Level 3 R2R DAC */
+ case USB_ID(0x20b1, 0x307b): /* CH Precision C1 DAC */
+ case USB_ID(0x20b1, 0x3086): /* Singxer F-1 converter board */
+ case USB_ID(0x22d9, 0x0426): /* OPPO HA-2 */
+ case USB_ID(0x22e1, 0xca01): /* HDTA Serenade DSD */
+ case USB_ID(0x249c, 0x9326): /* M2Tech Young MkIII */
case USB_ID(0x2616, 0x0106): /* PS Audio NuWave DAC */
+ case USB_ID(0x2622, 0x0041): /* Audiolab M-DAC+ */
+ case USB_ID(0x27f7, 0x3002): /* W4S DAC-2v2SE */
+ case USB_ID(0x29a2, 0x0086): /* Mutec MC3+ USB */
+ case USB_ID(0x6b42, 0x0042): /* MSB Technology */
if (fp->altsetting == 3)
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
break;
diff --git a/sound/usb/stream.c b/sound/usb/stream.c
index 5ed334575fc7..bce315240955 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.c
@@ -633,6 +633,391 @@ snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface,
return NULL;
}
+static struct audioformat *
+audio_format_alloc_init(struct snd_usb_audio *chip,
+ struct usb_host_interface *alts,
+ int protocol, int iface_no, int altset_idx,
+ int altno, int num_channels, int clock)
+{
+ struct audioformat *fp;
+
+ fp = kzalloc(sizeof(*fp), GFP_KERNEL);
+ if (!fp)
+ return NULL;
+
+ fp->iface = iface_no;
+ fp->altsetting = altno;
+ fp->altset_idx = altset_idx;
+ fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
+ fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
+ fp->datainterval = snd_usb_parse_datainterval(chip, alts);
+ fp->protocol = protocol;
+ fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
+ fp->channels = num_channels;
+ if (snd_usb_get_speed(chip->dev) == USB_SPEED_HIGH)
+ fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1)
+ * (fp->maxpacksize & 0x7ff);
+ fp->clock = clock;
+ INIT_LIST_HEAD(&fp->list);
+
+ return fp;
+}
+
+static struct audioformat *
+snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip,
+ struct usb_host_interface *alts,
+ int protocol, int iface_no, int altset_idx,
+ int altno, int stream, int bm_quirk)
+{
+ struct usb_device *dev = chip->dev;
+ struct uac_format_type_i_continuous_descriptor *fmt;
+ unsigned int num_channels = 0, chconfig = 0;
+ struct audioformat *fp;
+ int clock = 0;
+ u64 format;
+
+ /* get audio formats */
+ if (protocol == UAC_VERSION_1) {
+ struct uac1_as_header_descriptor *as =
+ snd_usb_find_csint_desc(alts->extra, alts->extralen,
+ NULL, UAC_AS_GENERAL);
+ struct uac_input_terminal_descriptor *iterm;
+
+ if (!as) {
+ dev_err(&dev->dev,
+ "%u:%d : UAC_AS_GENERAL descriptor not found\n",
+ iface_no, altno);
+ return NULL;
+ }
+
+ if (as->bLength < sizeof(*as)) {
+ dev_err(&dev->dev,
+ "%u:%d : invalid UAC_AS_GENERAL desc\n",
+ iface_no, altno);
+ return NULL;
+ }
+
+ format = le16_to_cpu(as->wFormatTag); /* remember the format value */
+
+ iterm = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
+ as->bTerminalLink);
+ if (iterm) {
+ num_channels = iterm->bNrChannels;
+ chconfig = le16_to_cpu(iterm->wChannelConfig);
+ }
+ } else { /* UAC_VERSION_2 */
+ struct uac2_input_terminal_descriptor *input_term;
+ struct uac2_output_terminal_descriptor *output_term;
+ struct uac2_as_header_descriptor *as =
+ snd_usb_find_csint_desc(alts->extra, alts->extralen,
+ NULL, UAC_AS_GENERAL);
+
+ if (!as) {
+ dev_err(&dev->dev,
+ "%u:%d : UAC_AS_GENERAL descriptor not found\n",
+ iface_no, altno);
+ return NULL;
+ }
+
+ if (as->bLength < sizeof(*as)) {
+ dev_err(&dev->dev,
+ "%u:%d : invalid UAC_AS_GENERAL desc\n",
+ iface_no, altno);
+ return NULL;
+ }
+
+ num_channels = as->bNrChannels;
+ format = le32_to_cpu(as->bmFormats);
+ chconfig = le32_to_cpu(as->bmChannelConfig);
+
+ /*
+ * lookup the terminal associated to this interface
+ * to extract the clock
+ */
+ input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
+ as->bTerminalLink);
+ if (input_term) {
+ clock = input_term->bCSourceID;
+ if (!chconfig && (num_channels == input_term->bNrChannels))
+ chconfig = le32_to_cpu(input_term->bmChannelConfig);
+ goto found_clock;
+ }
+
+ output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf,
+ as->bTerminalLink);
+ if (output_term) {
+ clock = output_term->bCSourceID;
+ goto found_clock;
+ }
+
+ dev_err(&dev->dev,
+ "%u:%d : bogus bTerminalLink %d\n",
+ iface_no, altno, as->bTerminalLink);
+ return NULL;
+ }
+
+found_clock:
+ /* get format type */
+ fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen,
+ NULL, UAC_FORMAT_TYPE);
+ if (!fmt) {
+ dev_err(&dev->dev,
+ "%u:%d : no UAC_FORMAT_TYPE desc\n",
+ iface_no, altno);
+ return NULL;
+ }
+ if (((protocol == UAC_VERSION_1) && (fmt->bLength < 8))
+ || ((protocol == UAC_VERSION_2) &&
+ (fmt->bLength < 6))) {
+ dev_err(&dev->dev,
+ "%u:%d : invalid UAC_FORMAT_TYPE desc\n",
+ iface_no, altno);
+ return NULL;
+ }
+
+ /*
+ * Blue Microphones workaround: The last altsetting is
+ * identical with the previous one, except for a larger
+ * packet size, but is actually a mislabeled two-channel
+ * setting; ignore it.
+ *
+ * Part 2: analyze quirk flag and format
+ */
+ if (bm_quirk && fmt->bNrChannels == 1 && fmt->bSubframeSize == 2)
+ return NULL;
+
+ fp = audio_format_alloc_init(chip, alts, protocol, iface_no,
+ altset_idx, altno, num_channels, clock);
+ if (!fp)
+ return ERR_PTR(-ENOMEM);
+
+ fp->attributes = parse_uac_endpoint_attributes(chip, alts, protocol,
+ iface_no);
+
+ /* some quirks for attributes here */
+ snd_usb_audioformat_attributes_quirk(chip, fp, stream);
+
+ /* ok, let's parse further... */
+ if (snd_usb_parse_audio_format(chip, fp, format,
+ fmt, stream) < 0) {
+ kfree(fp->rate_table);
+ kfree(fp);
+ return NULL;
+ }
+
+ /* Create chmap */
+ if (fp->channels != num_channels)
+ chconfig = 0;
+
+ fp->chmap = convert_chmap(fp->channels, chconfig, protocol);
+
+ return fp;
+}
+
+static struct audioformat *
+snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip,
+ struct usb_host_interface *alts,
+ int iface_no, int altset_idx,
+ int altno, int stream)
+{
+ struct usb_device *dev = chip->dev;
+ struct uac3_input_terminal_descriptor *input_term;
+ struct uac3_output_terminal_descriptor *output_term;
+ struct uac3_cluster_header_descriptor *cluster;
+ struct uac3_as_header_descriptor *as = NULL;
+ struct uac3_hc_descriptor_header hc_header;
+ struct snd_pcm_chmap_elem *chmap;
+ unsigned char badd_profile;
+ u64 badd_formats = 0;
+ unsigned int num_channels;
+ struct audioformat *fp;
+ u16 cluster_id, wLength;
+ int clock = 0;
+ int err;
+
+ badd_profile = chip->badd_profile;
+
+ if (badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) {
+ unsigned int maxpacksize =
+ le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
+
+ switch (maxpacksize) {
+ default:
+ dev_err(&dev->dev,
+ "%u:%d : incorrect wMaxPacketSize for BADD profile\n",
+ iface_no, altno);
+ return NULL;
+ case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_16:
+ case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_16:
+ badd_formats = SNDRV_PCM_FMTBIT_S16_LE;
+ num_channels = 1;
+ break;
+ case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_24:
+ case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_24:
+ badd_formats = SNDRV_PCM_FMTBIT_S24_3LE;
+ num_channels = 1;
+ break;
+ case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_16:
+ case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_16:
+ badd_formats = SNDRV_PCM_FMTBIT_S16_LE;
+ num_channels = 2;
+ break;
+ case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_24:
+ case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_24:
+ badd_formats = SNDRV_PCM_FMTBIT_S24_3LE;
+ num_channels = 2;
+ break;
+ }
+
+ chmap = kzalloc(sizeof(*chmap), GFP_KERNEL);
+ if (!chmap)
+ return ERR_PTR(-ENOMEM);
+
+ if (num_channels == 1) {
+ chmap->map[0] = SNDRV_CHMAP_MONO;
+ } else {
+ chmap->map[0] = SNDRV_CHMAP_FL;
+ chmap->map[1] = SNDRV_CHMAP_FR;
+ }
+
+ chmap->channels = num_channels;
+ clock = UAC3_BADD_CS_ID9;
+ goto found_clock;
+ }
+
+ as = snd_usb_find_csint_desc(alts->extra, alts->extralen,
+ NULL, UAC_AS_GENERAL);
+ if (!as) {
+ dev_err(&dev->dev,
+ "%u:%d : UAC_AS_GENERAL descriptor not found\n",
+ iface_no, altno);
+ return NULL;
+ }
+
+ if (as->bLength < sizeof(*as)) {
+ dev_err(&dev->dev,
+ "%u:%d : invalid UAC_AS_GENERAL desc\n",
+ iface_no, altno);
+ return NULL;
+ }
+
+ cluster_id = le16_to_cpu(as->wClusterDescrID);
+ if (!cluster_id) {
+ dev_err(&dev->dev,
+ "%u:%d : no cluster descriptor\n",
+ iface_no, altno);
+ return NULL;
+ }
+
+ /*
+ * Get number of channels and channel map through
+ * High Capability Cluster Descriptor
+ *
+ * First step: get High Capability header and
+ * read size of Cluster Descriptor
+ */
+ err = snd_usb_ctl_msg(chip->dev,
+ usb_rcvctrlpipe(chip->dev, 0),
+ UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR,
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+ cluster_id,
+ snd_usb_ctrl_intf(chip),
+ &hc_header, sizeof(hc_header));
+ if (err < 0)
+ return ERR_PTR(err);
+ else if (err != sizeof(hc_header)) {
+ dev_err(&dev->dev,
+ "%u:%d : can't get High Capability descriptor\n",
+ iface_no, altno);
+ return ERR_PTR(-EIO);
+ }
+
+ /*
+ * Second step: allocate needed amount of memory
+ * and request Cluster Descriptor
+ */
+ wLength = le16_to_cpu(hc_header.wLength);
+ cluster = kzalloc(wLength, GFP_KERNEL);
+ if (!cluster)
+ return ERR_PTR(-ENOMEM);
+ err = snd_usb_ctl_msg(chip->dev,
+ usb_rcvctrlpipe(chip->dev, 0),
+ UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR,
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+ cluster_id,
+ snd_usb_ctrl_intf(chip),
+ cluster, wLength);
+ if (err < 0) {
+ kfree(cluster);
+ return ERR_PTR(err);
+ } else if (err != wLength) {
+ dev_err(&dev->dev,
+ "%u:%d : can't get Cluster Descriptor\n",
+ iface_no, altno);
+ kfree(cluster);
+ return ERR_PTR(-EIO);
+ }
+
+ num_channels = cluster->bNrChannels;
+ chmap = convert_chmap_v3(cluster);
+ kfree(cluster);
+
+ /*
+ * lookup the terminal associated to this interface
+ * to extract the clock
+ */
+ input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
+ as->bTerminalLink);
+ if (input_term) {
+ clock = input_term->bCSourceID;
+ goto found_clock;
+ }
+
+ output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf,
+ as->bTerminalLink);
+ if (output_term) {
+ clock = output_term->bCSourceID;
+ goto found_clock;
+ }
+
+ dev_err(&dev->dev, "%u:%d : bogus bTerminalLink %d\n",
+ iface_no, altno, as->bTerminalLink);
+ return NULL;
+
+found_clock:
+ fp = audio_format_alloc_init(chip, alts, UAC_VERSION_3, iface_no,
+ altset_idx, altno, num_channels, clock);
+ if (!fp)
+ return ERR_PTR(-ENOMEM);
+
+ fp->chmap = chmap;
+
+ if (badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) {
+ fp->attributes = 0; /* No attributes */
+
+ fp->fmt_type = UAC_FORMAT_TYPE_I;
+ fp->formats = badd_formats;
+
+ fp->nr_rates = 0; /* SNDRV_PCM_RATE_CONTINUOUS */
+ fp->rate_min = UAC3_BADD_SAMPLING_RATE;
+ fp->rate_max = UAC3_BADD_SAMPLING_RATE;
+ fp->rates = SNDRV_PCM_RATE_CONTINUOUS;
+
+ } else {
+ fp->attributes = parse_uac_endpoint_attributes(chip, alts,
+ UAC_VERSION_3,
+ iface_no);
+ /* ok, let's parse further... */
+ if (snd_usb_parse_audio_format_v3(chip, fp, as, stream) < 0) {
+ kfree(fp->rate_table);
+ kfree(fp);
+ return NULL;
+ }
+ }
+
+ return fp;
+}
+
int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
{
struct usb_device *dev;
@@ -640,13 +1025,8 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
struct usb_host_interface *alts;
struct usb_interface_descriptor *altsd;
int i, altno, err, stream;
- u64 format = 0;
- unsigned int num_channels = 0;
struct audioformat *fp = NULL;
- int num, protocol, clock = 0;
- struct uac_format_type_i_continuous_descriptor *fmt = NULL;
- struct snd_pcm_chmap_elem *chmap_v3 = NULL;
- unsigned int chconfig;
+ int num, protocol;
dev = chip->dev;
@@ -695,303 +1075,48 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
protocol <= 2)
protocol = UAC_VERSION_1;
- chconfig = 0;
- /* get audio formats */
switch (protocol) {
default:
dev_dbg(&dev->dev, "%u:%d: unknown interface protocol %#02x, assuming v1\n",
iface_no, altno, protocol);
protocol = UAC_VERSION_1;
/* fall through */
-
- case UAC_VERSION_1: {
- struct uac1_as_header_descriptor *as =
- snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL);
- struct uac_input_terminal_descriptor *iterm;
-
- if (!as) {
- dev_err(&dev->dev,
- "%u:%d : UAC_AS_GENERAL descriptor not found\n",
- iface_no, altno);
- continue;
- }
-
- if (as->bLength < sizeof(*as)) {
- dev_err(&dev->dev,
- "%u:%d : invalid UAC_AS_GENERAL desc\n",
- iface_no, altno);
- continue;
- }
-
- format = le16_to_cpu(as->wFormatTag); /* remember the format value */
-
- iterm = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
- as->bTerminalLink);
- if (iterm) {
- num_channels = iterm->bNrChannels;
- chconfig = le16_to_cpu(iterm->wChannelConfig);
- }
-
- break;
- }
-
+ case UAC_VERSION_1:
+ /* fall through */
case UAC_VERSION_2: {
- struct uac2_input_terminal_descriptor *input_term;
- struct uac2_output_terminal_descriptor *output_term;
- struct uac2_as_header_descriptor *as =
- snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL);
-
- if (!as) {
- dev_err(&dev->dev,
- "%u:%d : UAC_AS_GENERAL descriptor not found\n",
- iface_no, altno);
- continue;
- }
-
- if (as->bLength < sizeof(*as)) {
- dev_err(&dev->dev,
- "%u:%d : invalid UAC_AS_GENERAL desc\n",
- iface_no, altno);
- continue;
- }
-
- num_channels = as->bNrChannels;
- format = le32_to_cpu(as->bmFormats);
- chconfig = le32_to_cpu(as->bmChannelConfig);
-
- /* lookup the terminal associated to this interface
- * to extract the clock */
- input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
- as->bTerminalLink);
- if (input_term) {
- clock = input_term->bCSourceID;
- if (!chconfig && (num_channels == input_term->bNrChannels))
- chconfig = le32_to_cpu(input_term->bmChannelConfig);
- break;
- }
-
- output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf,
- as->bTerminalLink);
- if (output_term) {
- clock = output_term->bCSourceID;
- break;
- }
-
- dev_err(&dev->dev,
- "%u:%d : bogus bTerminalLink %d\n",
- iface_no, altno, as->bTerminalLink);
- continue;
- }
-
- case UAC_VERSION_3: {
- struct uac3_input_terminal_descriptor *input_term;
- struct uac3_output_terminal_descriptor *output_term;
- struct uac3_as_header_descriptor *as;
- struct uac3_cluster_header_descriptor *cluster;
- struct uac3_hc_descriptor_header hc_header;
- u16 cluster_id, wLength;
-
- as = snd_usb_find_csint_desc(alts->extra,
- alts->extralen,
- NULL, UAC_AS_GENERAL);
-
- if (!as) {
- dev_err(&dev->dev,
- "%u:%d : UAC_AS_GENERAL descriptor not found\n",
- iface_no, altno);
- continue;
- }
-
- if (as->bLength < sizeof(*as)) {
- dev_err(&dev->dev,
- "%u:%d : invalid UAC_AS_GENERAL desc\n",
- iface_no, altno);
- continue;
- }
-
- cluster_id = le16_to_cpu(as->wClusterDescrID);
- if (!cluster_id) {
- dev_err(&dev->dev,
- "%u:%d : no cluster descriptor\n",
- iface_no, altno);
- continue;
- }
-
- /*
- * Get number of channels and channel map through
- * High Capability Cluster Descriptor
- *
- * First step: get High Capability header and
- * read size of Cluster Descriptor
- */
- err = snd_usb_ctl_msg(chip->dev,
- usb_rcvctrlpipe(chip->dev, 0),
- UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR,
- USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
- cluster_id,
- snd_usb_ctrl_intf(chip),
- &hc_header, sizeof(hc_header));
- if (err < 0)
- return err;
- else if (err != sizeof(hc_header)) {
- dev_err(&dev->dev,
- "%u:%d : can't get High Capability descriptor\n",
- iface_no, altno);
- return -EIO;
- }
-
- /*
- * Second step: allocate needed amount of memory
- * and request Cluster Descriptor
- */
- wLength = le16_to_cpu(hc_header.wLength);
- cluster = kzalloc(wLength, GFP_KERNEL);
- if (!cluster)
- return -ENOMEM;
- err = snd_usb_ctl_msg(chip->dev,
- usb_rcvctrlpipe(chip->dev, 0),
- UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR,
- USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
- cluster_id,
- snd_usb_ctrl_intf(chip),
- cluster, wLength);
- if (err < 0) {
- kfree(cluster);
- return err;
- } else if (err != wLength) {
- dev_err(&dev->dev,
- "%u:%d : can't get Cluster Descriptor\n",
- iface_no, altno);
- kfree(cluster);
- return -EIO;
- }
-
- num_channels = cluster->bNrChannels;
- chmap_v3 = convert_chmap_v3(cluster);
-
- kfree(cluster);
-
- format = le64_to_cpu(as->bmFormats);
-
- /* lookup the terminal associated to this interface
- * to extract the clock */
- input_term = snd_usb_find_input_terminal_descriptor(
- chip->ctrl_intf,
- as->bTerminalLink);
-
- if (input_term) {
- clock = input_term->bCSourceID;
- break;
- }
-
- output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf,
- as->bTerminalLink);
- if (output_term) {
- clock = output_term->bCSourceID;
- break;
- }
-
- dev_err(&dev->dev,
- "%u:%d : bogus bTerminalLink %d\n",
- iface_no, altno, as->bTerminalLink);
- continue;
- }
- }
-
- if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) {
- /* get format type */
- fmt = snd_usb_find_csint_desc(alts->extra,
- alts->extralen,
- NULL, UAC_FORMAT_TYPE);
- if (!fmt) {
- dev_err(&dev->dev,
- "%u:%d : no UAC_FORMAT_TYPE desc\n",
- iface_no, altno);
- continue;
- }
- if (((protocol == UAC_VERSION_1) && (fmt->bLength < 8))
- || ((protocol == UAC_VERSION_2) &&
- (fmt->bLength < 6))) {
- dev_err(&dev->dev,
- "%u:%d : invalid UAC_FORMAT_TYPE desc\n",
- iface_no, altno);
- continue;
- }
+ int bm_quirk = 0;
/*
* Blue Microphones workaround: The last altsetting is
* identical with the previous one, except for a larger
* packet size, but is actually a mislabeled two-channel
* setting; ignore it.
+ *
+ * Part 1: prepare quirk flag
*/
- if (fmt->bNrChannels == 1 &&
- fmt->bSubframeSize == 2 &&
- altno == 2 && num == 3 &&
+ if (altno == 2 && num == 3 &&
fp && fp->altsetting == 1 && fp->channels == 1 &&
fp->formats == SNDRV_PCM_FMTBIT_S16_LE &&
protocol == UAC_VERSION_1 &&
le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) ==
fp->maxpacksize * 2)
- continue;
- }
-
- fp = kzalloc(sizeof(*fp), GFP_KERNEL);
- if (!fp)
- return -ENOMEM;
+ bm_quirk = 1;
- fp->iface = iface_no;
- fp->altsetting = altno;
- fp->altset_idx = i;
- fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
- fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
- fp->datainterval = snd_usb_parse_datainterval(chip, alts);
- fp->protocol = protocol;
- fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
- fp->channels = num_channels;
- if (snd_usb_get_speed(dev) == USB_SPEED_HIGH)
- fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1)
- * (fp->maxpacksize & 0x7ff);
- fp->attributes = parse_uac_endpoint_attributes(chip, alts, protocol, iface_no);
- fp->clock = clock;
- INIT_LIST_HEAD(&fp->list);
-
- /* some quirks for attributes here */
- snd_usb_audioformat_attributes_quirk(chip, fp, stream);
-
- /* ok, let's parse further... */
- if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) {
- if (snd_usb_parse_audio_format(chip, fp, format,
- fmt, stream) < 0) {
- kfree(fp->rate_table);
- kfree(fp);
- fp = NULL;
- continue;
- }
- } else {
- struct uac3_as_header_descriptor *as;
-
- as = snd_usb_find_csint_desc(alts->extra,
- alts->extralen,
- NULL, UAC_AS_GENERAL);
-
- if (snd_usb_parse_audio_format_v3(chip, fp, as,
- stream) < 0) {
- kfree(fp->rate_table);
- kfree(fp);
- fp = NULL;
- continue;
- }
+ fp = snd_usb_get_audioformat_uac12(chip, alts, protocol,
+ iface_no, i, altno,
+ stream, bm_quirk);
+ break;
+ }
+ case UAC_VERSION_3:
+ fp = snd_usb_get_audioformat_uac3(chip, alts,
+ iface_no, i, altno, stream);
+ break;
}
- /* Create chmap */
- if (fp->channels != num_channels)
- chconfig = 0;
-
- if (protocol == UAC_VERSION_3)
- fp->chmap = chmap_v3;
- else
- fp->chmap = convert_chmap(fp->channels, chconfig,
- protocol);
+ if (!fp)
+ continue;
+ else if (IS_ERR(fp))
+ return PTR_ERR(fp);
dev_dbg(&dev->dev, "%u:%d: add audio endpoint %#x\n", iface_no, altno, fp->endpoint);
err = snd_usb_add_audio_stream(chip, stream, fp);
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 4d5c89a7ba2b..7b28cbde22c0 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -49,6 +49,8 @@ struct snd_usb_audio {
int num_suspended_intf;
int sample_rate_read_error;
+ int badd_profile; /* UAC3 BADD profile */
+
struct list_head pcm_list; /* list of pcm streams */
struct list_head ep_list; /* list of audio-related endpoints */
int pcm_devs;
@@ -59,6 +61,9 @@ struct snd_usb_audio {
int setup; /* from the 'device_setup' module param */
bool autoclock; /* from the 'autoclock' module param */
+ bool keep_iface; /* keep interface/altset after closing
+ * or parameter change
+ */
struct usb_host_interface *ctrl_intf; /* the audio control interface */
};
@@ -109,6 +114,7 @@ enum quirk_type {
struct snd_usb_audio_quirk {
const char *vendor_name;
const char *product_name;
+ const char *profile_name; /* override the card->longname */
int16_t ifnum;
uint16_t type;
const void *data;