Commit 17156f23 authored by Ruslan Bilovol's avatar Ruslan Bilovol Committed by Takashi Iwai

ALSA: usb: add UAC3 BADD profiles support

Recently released USB Audio Class 3.0 specification
contains BADD (Basic Audio Device Definition) document
which describes pre-defined UAC3 configurations.

BADD support is mandatory for UAC3 devices, it should be
implemented as a separate USB device configuration.
As per BADD document, class-specific descriptors
shall not be included in the Device’s Configuration
descriptor ("inferred"), but host can guess them
from BADD profile number, number of endpoints and
their max packed sizes.

This patch adds support of all BADD profiles from the spec
Signed-off-by: default avatarRuslan Bilovol <ruslan.bilovol@gmail.com>
Tested-by: default avatarJorge Sanjuan <jorge.sanjuan@codethink.co.uk>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 10aa7cad
......@@ -307,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;
......
......@@ -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);
}
}
......
This diff is collapsed.
......@@ -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 */
};
......@@ -817,15 +817,67 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip,
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;
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) {
......@@ -931,16 +983,29 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip,
if (!fp)
return ERR_PTR(-ENOMEM);
fp->attributes = parse_uac_endpoint_attributes(chip, alts,
UAC_VERSION_3,
iface_no);
fp->chmap = chmap;
/* 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;
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;
......
......@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment