Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 04324cc

Browse files
committed
ALSA: usb-audio: add channel map support
Add the support for channel maps of the PCM streams on USB audio devices. The channel map information is already found in ChannelConfig descriptor entries, which haven't been referred until now. Each chmap entry is added to audioformat list entry and copied to TLV dynamically instead of creating a whole chmap array. Signed-off-by: Takashi Iwai <[email protected]>
1 parent 7cc17a3 commit 04324cc

File tree

2 files changed

+227
-5
lines changed

2 files changed

+227
-5
lines changed

sound/usb/card.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ struct audioformat {
2727
unsigned int nr_rates; /* number of rate table entries */
2828
unsigned int *rate_table; /* rate table */
2929
unsigned char clock; /* associated clock */
30+
struct snd_pcm_chmap_elem *chmap; /* (optional) channel map */
3031
};
3132

3233
struct snd_usb_substream;
@@ -109,6 +110,7 @@ struct snd_usb_substream {
109110
struct audioformat *cur_audiofmt; /* current audioformat pointer (for hw_params callback) */
110111
snd_pcm_format_t pcm_format; /* current audio format (for hw_params callback) */
111112
unsigned int channels; /* current number of channels (for hw_params callback) */
113+
unsigned int channels_max; /* max channels in the all audiofmts */
112114
unsigned int cur_rate; /* current rate (for hw_params callback) */
113115
unsigned int period_bytes; /* current period bytes (for hw_params callback) */
114116
unsigned int altset_idx; /* USB data format: index of alternate setting */

sound/usb/stream.c

Lines changed: 225 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323

2424
#include <sound/core.h>
2525
#include <sound/pcm.h>
26+
#include <sound/control.h>
27+
#include <sound/tlv.h>
2628

2729
#include "usbaudio.h"
2830
#include "card.h"
@@ -47,6 +49,7 @@ static void free_substream(struct snd_usb_substream *subs)
4749
list_for_each_safe(p, n, &subs->fmt_list) {
4850
struct audioformat *fp = list_entry(p, struct audioformat, list);
4951
kfree(fp->rate_table);
52+
kfree(fp->chmap);
5053
kfree(fp);
5154
}
5255
kfree(subs->rate_list.list);
@@ -99,6 +102,206 @@ static void snd_usb_init_substream(struct snd_usb_stream *as,
99102
subs->num_formats++;
100103
subs->fmt_type = fp->fmt_type;
101104
subs->ep_num = fp->endpoint;
105+
if (fp->channels > subs->channels_max)
106+
subs->channels_max = fp->channels;
107+
}
108+
109+
/* kctl callbacks for usb-audio channel maps */
110+
static int usb_chmap_ctl_info(struct snd_kcontrol *kcontrol,
111+
struct snd_ctl_elem_info *uinfo)
112+
{
113+
struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
114+
struct snd_usb_substream *subs = info->private_data;
115+
116+
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
117+
uinfo->count = subs->channels_max;
118+
uinfo->value.integer.min = 0;
119+
uinfo->value.integer.max = SNDRV_CHMAP_LAST;
120+
return 0;
121+
}
122+
123+
/* check whether a duplicated entry exists in the audiofmt list */
124+
static bool have_dup_chmap(struct snd_usb_substream *subs,
125+
struct audioformat *fp)
126+
{
127+
struct list_head *p;
128+
129+
for (p = fp->list.prev; p != &subs->fmt_list; p = p->prev) {
130+
struct audioformat *prev;
131+
prev = list_entry(p, struct audioformat, list);
132+
if (prev->chmap &&
133+
!memcmp(prev->chmap, fp->chmap, sizeof(*fp->chmap)))
134+
return true;
135+
}
136+
return false;
137+
}
138+
139+
static int usb_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
140+
unsigned int size, unsigned int __user *tlv)
141+
{
142+
struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
143+
struct snd_usb_substream *subs = info->private_data;
144+
struct audioformat *fp;
145+
unsigned int __user *dst;
146+
int count = 0;
147+
148+
if (size < 8)
149+
return -ENOMEM;
150+
if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv))
151+
return -EFAULT;
152+
size -= 8;
153+
dst = tlv + 2;
154+
list_for_each_entry(fp, &subs->fmt_list, list) {
155+
int i, ch_bytes;
156+
157+
if (!fp->chmap)
158+
continue;
159+
if (have_dup_chmap(subs, fp))
160+
continue;
161+
/* copy the entry */
162+
ch_bytes = fp->chmap->channels * 4;
163+
if (size < 8 + ch_bytes)
164+
return -ENOMEM;
165+
if (put_user(SNDRV_CTL_TLVT_CHMAP_FIXED, dst) ||
166+
put_user(ch_bytes, dst + 1))
167+
return -EFAULT;
168+
dst += 2;
169+
for (i = 0; i < fp->chmap->channels; i++, dst++) {
170+
if (put_user(fp->chmap->map[i], dst))
171+
return -EFAULT;
172+
}
173+
174+
count += 8 + ch_bytes;
175+
size -= 8 + ch_bytes;
176+
}
177+
if (put_user(count, tlv + 1))
178+
return -EFAULT;
179+
return 0;
180+
}
181+
182+
static int usb_chmap_ctl_get(struct snd_kcontrol *kcontrol,
183+
struct snd_ctl_elem_value *ucontrol)
184+
{
185+
struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
186+
struct snd_usb_substream *subs = info->private_data;
187+
struct snd_pcm_chmap_elem *chmap = NULL;
188+
int i;
189+
190+
memset(ucontrol->value.integer.value, 0,
191+
sizeof(ucontrol->value.integer.value));
192+
if (subs->cur_audiofmt)
193+
chmap = subs->cur_audiofmt->chmap;
194+
if (chmap) {
195+
for (i = 0; i < chmap->channels; i++)
196+
ucontrol->value.integer.value[i] = chmap->map[i];
197+
}
198+
return 0;
199+
}
200+
201+
/* create a chmap kctl assigned to the given USB substream */
202+
static int add_chmap(struct snd_pcm *pcm, int stream,
203+
struct snd_usb_substream *subs)
204+
{
205+
struct audioformat *fp;
206+
struct snd_pcm_chmap *chmap;
207+
struct snd_kcontrol *kctl;
208+
int err;
209+
210+
list_for_each_entry(fp, &subs->fmt_list, list)
211+
if (fp->chmap)
212+
goto ok;
213+
/* no chmap is found */
214+
return 0;
215+
216+
ok:
217+
err = snd_pcm_add_chmap_ctls(pcm, stream, NULL, 0, 0, &chmap);
218+
if (err < 0)
219+
return err;
220+
221+
/* override handlers */
222+
chmap->private_data = subs;
223+
kctl = chmap->kctl;
224+
kctl->info = usb_chmap_ctl_info;
225+
kctl->get = usb_chmap_ctl_get;
226+
kctl->tlv.c = usb_chmap_ctl_tlv;
227+
228+
return 0;
229+
}
230+
231+
/* convert from USB ChannelConfig bits to ALSA chmap element */
232+
static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits,
233+
int protocol)
234+
{
235+
static unsigned int uac1_maps[] = {
236+
SNDRV_CHMAP_FL, /* left front */
237+
SNDRV_CHMAP_FR, /* right front */
238+
SNDRV_CHMAP_FC, /* center front */
239+
SNDRV_CHMAP_LFE, /* LFE */
240+
SNDRV_CHMAP_SL, /* left surround */
241+
SNDRV_CHMAP_SR, /* right surround */
242+
SNDRV_CHMAP_FLC, /* left of center */
243+
SNDRV_CHMAP_FRC, /* right of center */
244+
SNDRV_CHMAP_RC, /* surround */
245+
SNDRV_CHMAP_SL, /* side left */
246+
SNDRV_CHMAP_SR, /* side right */
247+
SNDRV_CHMAP_TC, /* top */
248+
0 /* terminator */
249+
};
250+
static unsigned int uac2_maps[] = {
251+
SNDRV_CHMAP_FL, /* front left */
252+
SNDRV_CHMAP_FR, /* front right */
253+
SNDRV_CHMAP_FC, /* front center */
254+
SNDRV_CHMAP_LFE, /* LFE */
255+
SNDRV_CHMAP_RL, /* back left */
256+
SNDRV_CHMAP_RR, /* back right */
257+
SNDRV_CHMAP_FLC, /* front left of center */
258+
SNDRV_CHMAP_FRC, /* front right of center */
259+
SNDRV_CHMAP_RC, /* back center */
260+
SNDRV_CHMAP_SL, /* side left */
261+
SNDRV_CHMAP_SR, /* side right */
262+
SNDRV_CHMAP_TC, /* top center */
263+
SNDRV_CHMAP_TFL, /* top front left */
264+
SNDRV_CHMAP_TFC, /* top front center */
265+
SNDRV_CHMAP_TFR, /* top front right */
266+
SNDRV_CHMAP_TRL, /* top back left */
267+
SNDRV_CHMAP_TRC, /* top back center */
268+
SNDRV_CHMAP_TRR, /* top back right */
269+
SNDRV_CHMAP_TFLC, /* top front left of center */
270+
SNDRV_CHMAP_TFRC, /* top front right of center */
271+
SNDRV_CHMAP_LLFE, /* left LFE */
272+
SNDRV_CHMAP_RLFE, /* right LFE */
273+
SNDRV_CHMAP_TSL, /* top side left */
274+
SNDRV_CHMAP_TSR, /* top side right */
275+
SNDRV_CHMAP_BC, /* bottom center */
276+
SNDRV_CHMAP_BLC, /* bottom left center */
277+
SNDRV_CHMAP_BRC, /* bottom right center */
278+
0 /* terminator */
279+
};
280+
struct snd_pcm_chmap_elem *chmap;
281+
const unsigned int *maps;
282+
int c;
283+
284+
if (!bits)
285+
return NULL;
286+
if (channels > ARRAY_SIZE(chmap->map))
287+
return NULL;
288+
289+
chmap = kzalloc(sizeof(*chmap), GFP_KERNEL);
290+
if (!chmap)
291+
return NULL;
292+
293+
maps = protocol == UAC_VERSION_2 ? uac2_maps : uac1_maps;
294+
chmap->channels = channels;
295+
c = 0;
296+
for (; bits && *maps; maps++, bits >>= 1) {
297+
if (bits & 1)
298+
chmap->map[c++] = *maps;
299+
}
300+
301+
for (; c < channels; c++)
302+
chmap->map[c] = SNDRV_CHMAP_UNKNOWN;
303+
304+
return chmap;
102305
}
103306

104307
/*
@@ -140,7 +343,7 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
140343
if (err < 0)
141344
return err;
142345
snd_usb_init_substream(as, stream, fp);
143-
return 0;
346+
return add_chmap(as->pcm, stream, subs);
144347
}
145348

146349
/* create a new pcm */
@@ -174,7 +377,7 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
174377

175378
snd_usb_proc_pcm_format_add(as);
176379

177-
return 0;
380+
return add_chmap(pcm, stream, &as->substream[stream]);
178381
}
179382

180383
static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip,
@@ -218,8 +421,11 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip,
218421
return attributes;
219422
}
220423

221-
static struct uac2_input_terminal_descriptor *
222-
snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface,
424+
/* find an input terminal descriptor (either UAC1 or UAC2) with the given
425+
* terminal id
426+
*/
427+
static void *
428+
snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface,
223429
int terminal_id)
224430
{
225431
struct uac2_input_terminal_descriptor *term = NULL;
@@ -261,6 +467,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
261467
struct audioformat *fp = NULL;
262468
int num, protocol, clock = 0;
263469
struct uac_format_type_i_continuous_descriptor *fmt;
470+
unsigned int chconfig;
264471

265472
dev = chip->dev;
266473

@@ -300,6 +507,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
300507
if (snd_usb_apply_interface_quirk(chip, iface_no, altno))
301508
continue;
302509

510+
chconfig = 0;
303511
/* get audio formats */
304512
switch (protocol) {
305513
default:
@@ -311,6 +519,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
311519
case UAC_VERSION_1: {
312520
struct uac1_as_header_descriptor *as =
313521
snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL);
522+
struct uac_input_terminal_descriptor *iterm;
314523

315524
if (!as) {
316525
snd_printk(KERN_ERR "%d:%u:%d : UAC_AS_GENERAL descriptor not found\n",
@@ -325,6 +534,14 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
325534
}
326535

327536
format = le16_to_cpu(as->wFormatTag); /* remember the format value */
537+
538+
iterm = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
539+
as->bTerminalLink);
540+
if (iterm) {
541+
num_channels = iterm->bNrChannels;
542+
chconfig = le16_to_cpu(iterm->wChannelConfig);
543+
}
544+
328545
break;
329546
}
330547

@@ -355,6 +572,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
355572
as->bTerminalLink);
356573
if (input_term) {
357574
clock = input_term->bCSourceID;
575+
chconfig = le32_to_cpu(input_term->bmChannelConfig);
358576
break;
359577
}
360578

@@ -413,13 +631,13 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
413631
fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
414632
fp->datainterval = snd_usb_parse_datainterval(chip, alts);
415633
fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
416-
/* num_channels is only set for v2 interfaces */
417634
fp->channels = num_channels;
418635
if (snd_usb_get_speed(dev) == USB_SPEED_HIGH)
419636
fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1)
420637
* (fp->maxpacksize & 0x7ff);
421638
fp->attributes = parse_uac_endpoint_attributes(chip, alts, protocol, iface_no);
422639
fp->clock = clock;
640+
fp->chmap = convert_chmap(num_channels, chconfig, protocol);
423641

424642
/* some quirks for attributes here */
425643

@@ -455,6 +673,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
455673
/* ok, let's parse further... */
456674
if (snd_usb_parse_audio_format(chip, fp, format, fmt, stream, alts) < 0) {
457675
kfree(fp->rate_table);
676+
kfree(fp->chmap);
458677
kfree(fp);
459678
fp = NULL;
460679
continue;
@@ -464,6 +683,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
464683
err = snd_usb_add_audio_stream(chip, stream, fp);
465684
if (err < 0) {
466685
kfree(fp->rate_table);
686+
kfree(fp->chmap);
467687
kfree(fp);
468688
return err;
469689
}

0 commit comments

Comments
 (0)