Skip to content

Commit

Permalink
ALSA: usb-audio: support multiple formats with audio class v2 devices
Browse files Browse the repository at this point in the history
Change the parser to correctly handle v2 descriptors with multiple
format bits set.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
  • Loading branch information
Clemens Ladisch authored and Takashi Iwai committed Mar 5, 2010
1 parent 015eb0b commit 29088fe
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 56 deletions.
93 changes: 38 additions & 55 deletions sound/usb/format.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,60 +37,44 @@
* @format: the format tag (wFormatTag)
* @fmt: the format type descriptor
*/
static int parse_audio_format_i_type(struct snd_usb_audio *chip,
static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
struct audioformat *fp,
int format, void *_fmt,
int protocol)
{
int pcm_format, i;
int sample_width, sample_bytes;
u64 pcm_formats;

switch (protocol) {
case UAC_VERSION_1: {
struct uac_format_type_i_discrete_descriptor *fmt = _fmt;
sample_width = fmt->bBitResolution;
sample_bytes = fmt->bSubframeSize;
format = 1 << format;
break;
}

case UAC_VERSION_2: {
struct uac_format_type_i_ext_descriptor *fmt = _fmt;
sample_width = fmt->bBitResolution;
sample_bytes = fmt->bSubslotSize;

/*
* FIXME
* USB audio class v2 devices specify a bitmap of possible
* audio formats rather than one fix value. For now, we just
* pick one of them and report that as the only possible
* value for this setting.
* The bit allocation map is in fact compatible to the
* wFormatTag of the v1 AS streaming descriptors, which is why
* we can simply map the matrix.
*/

for (i = 0; i < 5; i++)
if (format & (1UL << i)) {
format = i + 1;
break;
}

format <<= 1;
break;
}

default:
return -EINVAL;
}

/* FIXME: correct endianess and sign? */
pcm_format = -1;
pcm_formats = 0;

switch (format) {
case UAC_FORMAT_TYPE_I_UNDEFINED: /* some devices don't define this correctly... */
if (format == 0 || format == (1 << UAC_FORMAT_TYPE_I_UNDEFINED)) {
/* some devices don't define this correctly... */
snd_printdd(KERN_INFO "%d:%u:%d : format type 0 is detected, processed as PCM\n",
chip->dev->devnum, fp->iface, fp->altsetting);
/* fall-through */
case UAC_FORMAT_TYPE_I_PCM:
format = 1 << UAC_FORMAT_TYPE_I_PCM;
}
if (format & (1 << UAC_FORMAT_TYPE_I_PCM)) {
if (sample_width > sample_bytes * 8) {
snd_printk(KERN_INFO "%d:%u:%d : sample bitwidth %d in over sample bytes %d\n",
chip->dev->devnum, fp->iface, fp->altsetting,
Expand All @@ -99,53 +83,52 @@ static int parse_audio_format_i_type(struct snd_usb_audio *chip,
/* check the format byte size */
switch (sample_bytes) {
case 1:
pcm_format = SNDRV_PCM_FORMAT_S8;
pcm_formats |= SNDRV_PCM_FMTBIT_S8;
break;
case 2:
if (snd_usb_is_big_endian_format(chip, fp))
pcm_format = SNDRV_PCM_FORMAT_S16_BE; /* grrr, big endian!! */
pcm_formats |= SNDRV_PCM_FMTBIT_S16_BE; /* grrr, big endian!! */
else
pcm_format = SNDRV_PCM_FORMAT_S16_LE;
pcm_formats |= SNDRV_PCM_FMTBIT_S16_LE;
break;
case 3:
if (snd_usb_is_big_endian_format(chip, fp))
pcm_format = SNDRV_PCM_FORMAT_S24_3BE; /* grrr, big endian!! */
pcm_formats |= SNDRV_PCM_FMTBIT_S24_3BE; /* grrr, big endian!! */
else
pcm_format = SNDRV_PCM_FORMAT_S24_3LE;
pcm_formats |= SNDRV_PCM_FMTBIT_S24_3LE;
break;
case 4:
pcm_format = SNDRV_PCM_FORMAT_S32_LE;
pcm_formats |= SNDRV_PCM_FMTBIT_S32_LE;
break;
default:
snd_printk(KERN_INFO "%d:%u:%d : unsupported sample bitwidth %d in %d bytes\n",
chip->dev->devnum, fp->iface, fp->altsetting,
sample_width, sample_bytes);
break;
}
break;
case UAC_FORMAT_TYPE_I_PCM8:
pcm_format = SNDRV_PCM_FORMAT_U8;

}
if (format & (1 << UAC_FORMAT_TYPE_I_PCM8)) {
/* Dallas DS4201 workaround: it advertises U8 format, but really
supports S8. */
if (chip->usb_id == USB_ID(0x04fa, 0x4201))
pcm_format = SNDRV_PCM_FORMAT_S8;
break;
case UAC_FORMAT_TYPE_I_IEEE_FLOAT:
pcm_format = SNDRV_PCM_FORMAT_FLOAT_LE;
break;
case UAC_FORMAT_TYPE_I_ALAW:
pcm_format = SNDRV_PCM_FORMAT_A_LAW;
break;
case UAC_FORMAT_TYPE_I_MULAW:
pcm_format = SNDRV_PCM_FORMAT_MU_LAW;
break;
default:
snd_printk(KERN_INFO "%d:%u:%d : unsupported format type %d\n",
pcm_formats |= SNDRV_PCM_FMTBIT_S8;
else
pcm_formats |= SNDRV_PCM_FMTBIT_U8;
}
if (format & (1 << UAC_FORMAT_TYPE_I_IEEE_FLOAT)) {
pcm_formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
}
if (format & (1 << UAC_FORMAT_TYPE_I_ALAW)) {
pcm_formats |= SNDRV_PCM_FMTBIT_A_LAW;
}
if (format & (1 << UAC_FORMAT_TYPE_I_MULAW)) {
pcm_formats |= SNDRV_PCM_FMTBIT_MU_LAW;
}
if (format & ~0x3f) {
snd_printk(KERN_INFO "%d:%u:%d : unsupported format bits %#x\n",
chip->dev->devnum, fp->iface, fp->altsetting, format);
break;
}
return pcm_format;
return pcm_formats;
}


Expand Down Expand Up @@ -317,14 +300,14 @@ static int parse_audio_format_i(struct snd_usb_audio *chip,
default:
pcm_format = SNDRV_PCM_FORMAT_S16_LE;
}
fp->formats = 1uLL << pcm_format;
} else {
pcm_format = parse_audio_format_i_type(chip, fp, format, fmt, protocol);
if (pcm_format < 0)
fp->formats = parse_audio_format_i_type(chip, fp, format,
fmt, protocol);
if (!fp->formats)
return -1;
}

fp->formats = 1uLL << pcm_format;

/* gather possible sample rates */
/* audio class v1 reports possible sample rates as part of the
* proprietary class specific descriptor.
Expand Down
2 changes: 1 addition & 1 deletion sound/usb/quirks-table.h
Original file line number Diff line number Diff line change
Expand Up @@ -2203,7 +2203,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.ifnum = 1,
.type = QUIRK_AUDIO_FIXED_ENDPOINT,
.data = &(const struct audioformat) {
.format = SNDRV_PCM_FORMAT_S24_3BE,
.formats = SNDRV_PCM_FMTBIT_S24_3BE,
.channels = 2,
.iface = 1,
.altsetting = 1,
Expand Down

0 comments on commit 29088fe

Please sign in to comment.