From 0dca01c37a68017fe7112b46bc4b48c927db18c6 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Tue, 5 Nov 2013 04:41:06 +0100 Subject: ALSA: usb: supply channel maps even when wChannelConfig is unspecified If wChannelconfig is given for some formats but not others, userspace might not be able to set the channel map. This is RFC because I'm not sure what the best behaviour is - to guess the channel map from the given number of channels (it's quite likely that one channel is MONO and two channels is FL FR), or just to supply UNKNOWN for all channels. But the complete lack of channel map for a format leads userspace to believe that the format is not available at all. Or am I misunderstanding how this should be used? Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/usb/stream.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'sound/usb/stream.c') diff --git a/sound/usb/stream.c b/sound/usb/stream.c index c4339f9..b43b6ee 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -281,8 +281,6 @@ static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits, const unsigned int *maps; int c; - if (!bits) - return NULL; if (channels > ARRAY_SIZE(chmap->map)) return NULL; @@ -293,9 +291,19 @@ static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits, maps = protocol == UAC_VERSION_2 ? uac2_maps : uac1_maps; chmap->channels = channels; c = 0; - for (; bits && *maps; maps++, bits >>= 1) { - if (bits & 1) - chmap->map[c++] = *maps; + + if (bits) { + for (; bits && *maps; maps++, bits >>= 1) + if (bits & 1) + chmap->map[c++] = *maps; + } else { + /* If we're missing wChannelConfig, then guess something + to make sure the channel map is not skipped entirely */ + if (channels == 1) + chmap->map[c++] = SNDRV_CHMAP_MONO; + else + for (; c < channels && *maps; maps++) + chmap->map[c++] = *maps; } for (; c < channels; c++) -- cgit v1.1 From e3e35f750fff74e701c8913fd7dd714e37a848cd Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Tue, 5 Nov 2013 04:41:07 +0100 Subject: ALSA: usb - For class 2 devices, use channel map from altsettings The channel config from the streaming descriptor is probably a better indicator of the channel map than the input terminal. Use the input terminal's channel map as fallback only. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/usb/stream.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'sound/usb/stream.c') diff --git a/sound/usb/stream.c b/sound/usb/stream.c index b43b6ee..badd1d6d 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -587,6 +587,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) 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 */ @@ -594,7 +595,8 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) as->bTerminalLink); if (input_term) { clock = input_term->bCSourceID; - chconfig = le32_to_cpu(input_term->bmChannelConfig); + if (!chconfig && (num_channels == input_term->bNrChannels)) + chconfig = le32_to_cpu(input_term->bmChannelConfig); break; } -- cgit v1.1 From 504333df8ba5fc310260285a22ab5d7cf3208795 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Tue, 5 Nov 2013 04:41:08 +0100 Subject: ALSA: usb - Don't trust the channel config if the channel count changed In case the channel count of the input terminal is not the same as the channel count of the streaming descriptor, the channel config of the input terminal can not be trusted. Instead fall back to a default (guessed) channel map. This was found on a Logitech USB Headset. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/usb/stream.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'sound/usb/stream.c') diff --git a/sound/usb/stream.c b/sound/usb/stream.c index badd1d6d..d737d0e 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -662,7 +662,6 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) * (fp->maxpacksize & 0x7ff); fp->attributes = parse_uac_endpoint_attributes(chip, alts, protocol, iface_no); fp->clock = clock; - fp->chmap = convert_chmap(num_channels, chconfig, protocol); /* some quirks for attributes here */ @@ -698,12 +697,16 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) /* ok, let's parse further... */ if (snd_usb_parse_audio_format(chip, fp, format, fmt, stream) < 0) { kfree(fp->rate_table); - kfree(fp->chmap); kfree(fp); fp = NULL; continue; } + /* Create chmap */ + if (fp->channels != num_channels) + chconfig = 0; + fp->chmap = convert_chmap(fp->channels, chconfig, protocol); + snd_printdd(KERN_INFO "%d:%u:%d: add audio endpoint %#x\n", dev->devnum, iface_no, altno, fp->endpoint); err = snd_usb_add_audio_stream(chip, stream, fp); if (err < 0) { -- cgit v1.1