diff options
Diffstat (limited to 'sound/usb/stream.c')
-rw-r--r-- | sound/usb/stream.c | 230 |
1 files changed, 225 insertions, 5 deletions
diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 1de0c8c002a8..ad181d538bd9 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c | |||
@@ -23,6 +23,8 @@ | |||
23 | 23 | ||
24 | #include <sound/core.h> | 24 | #include <sound/core.h> |
25 | #include <sound/pcm.h> | 25 | #include <sound/pcm.h> |
26 | #include <sound/control.h> | ||
27 | #include <sound/tlv.h> | ||
26 | 28 | ||
27 | #include "usbaudio.h" | 29 | #include "usbaudio.h" |
28 | #include "card.h" | 30 | #include "card.h" |
@@ -47,6 +49,7 @@ static void free_substream(struct snd_usb_substream *subs) | |||
47 | list_for_each_safe(p, n, &subs->fmt_list) { | 49 | list_for_each_safe(p, n, &subs->fmt_list) { |
48 | struct audioformat *fp = list_entry(p, struct audioformat, list); | 50 | struct audioformat *fp = list_entry(p, struct audioformat, list); |
49 | kfree(fp->rate_table); | 51 | kfree(fp->rate_table); |
52 | kfree(fp->chmap); | ||
50 | kfree(fp); | 53 | kfree(fp); |
51 | } | 54 | } |
52 | kfree(subs->rate_list.list); | 55 | kfree(subs->rate_list.list); |
@@ -99,6 +102,206 @@ static void snd_usb_init_substream(struct snd_usb_stream *as, | |||
99 | subs->num_formats++; | 102 | subs->num_formats++; |
100 | subs->fmt_type = fp->fmt_type; | 103 | subs->fmt_type = fp->fmt_type; |
101 | subs->ep_num = fp->endpoint; | 104 | 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; | ||
102 | } | 305 | } |
103 | 306 | ||
104 | /* | 307 | /* |
@@ -140,7 +343,7 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip, | |||
140 | if (err < 0) | 343 | if (err < 0) |
141 | return err; | 344 | return err; |
142 | snd_usb_init_substream(as, stream, fp); | 345 | snd_usb_init_substream(as, stream, fp); |
143 | return 0; | 346 | return add_chmap(as->pcm, stream, subs); |
144 | } | 347 | } |
145 | 348 | ||
146 | /* create a new pcm */ | 349 | /* create a new pcm */ |
@@ -174,7 +377,7 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip, | |||
174 | 377 | ||
175 | snd_usb_proc_pcm_format_add(as); | 378 | snd_usb_proc_pcm_format_add(as); |
176 | 379 | ||
177 | return 0; | 380 | return add_chmap(pcm, stream, &as->substream[stream]); |
178 | } | 381 | } |
179 | 382 | ||
180 | static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, | 383 | 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, | |||
218 | return attributes; | 421 | return attributes; |
219 | } | 422 | } |
220 | 423 | ||
221 | static struct uac2_input_terminal_descriptor * | 424 | /* find an input terminal descriptor (either UAC1 or UAC2) with the given |
222 | snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface, | 425 | * terminal id |
426 | */ | ||
427 | static void * | ||
428 | snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface, | ||
223 | int terminal_id) | 429 | int terminal_id) |
224 | { | 430 | { |
225 | struct uac2_input_terminal_descriptor *term = NULL; | 431 | 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) | |||
261 | struct audioformat *fp = NULL; | 467 | struct audioformat *fp = NULL; |
262 | int num, protocol, clock = 0; | 468 | int num, protocol, clock = 0; |
263 | struct uac_format_type_i_continuous_descriptor *fmt; | 469 | struct uac_format_type_i_continuous_descriptor *fmt; |
470 | unsigned int chconfig; | ||
264 | 471 | ||
265 | dev = chip->dev; | 472 | dev = chip->dev; |
266 | 473 | ||
@@ -300,6 +507,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) | |||
300 | if (snd_usb_apply_interface_quirk(chip, iface_no, altno)) | 507 | if (snd_usb_apply_interface_quirk(chip, iface_no, altno)) |
301 | continue; | 508 | continue; |
302 | 509 | ||
510 | chconfig = 0; | ||
303 | /* get audio formats */ | 511 | /* get audio formats */ |
304 | switch (protocol) { | 512 | switch (protocol) { |
305 | default: | 513 | default: |
@@ -311,6 +519,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) | |||
311 | case UAC_VERSION_1: { | 519 | case UAC_VERSION_1: { |
312 | struct uac1_as_header_descriptor *as = | 520 | struct uac1_as_header_descriptor *as = |
313 | snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL); | 521 | snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL); |
522 | struct uac_input_terminal_descriptor *iterm; | ||
314 | 523 | ||
315 | if (!as) { | 524 | if (!as) { |
316 | snd_printk(KERN_ERR "%d:%u:%d : UAC_AS_GENERAL descriptor not found\n", | 525 | 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) | |||
325 | } | 534 | } |
326 | 535 | ||
327 | format = le16_to_cpu(as->wFormatTag); /* remember the format value */ | 536 | 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 | |||
328 | break; | 545 | break; |
329 | } | 546 | } |
330 | 547 | ||
@@ -355,6 +572,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) | |||
355 | as->bTerminalLink); | 572 | as->bTerminalLink); |
356 | if (input_term) { | 573 | if (input_term) { |
357 | clock = input_term->bCSourceID; | 574 | clock = input_term->bCSourceID; |
575 | chconfig = le32_to_cpu(input_term->bmChannelConfig); | ||
358 | break; | 576 | break; |
359 | } | 577 | } |
360 | 578 | ||
@@ -413,13 +631,13 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) | |||
413 | fp->ep_attr = get_endpoint(alts, 0)->bmAttributes; | 631 | fp->ep_attr = get_endpoint(alts, 0)->bmAttributes; |
414 | fp->datainterval = snd_usb_parse_datainterval(chip, alts); | 632 | fp->datainterval = snd_usb_parse_datainterval(chip, alts); |
415 | fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); | 633 | fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); |
416 | /* num_channels is only set for v2 interfaces */ | ||
417 | fp->channels = num_channels; | 634 | fp->channels = num_channels; |
418 | if (snd_usb_get_speed(dev) == USB_SPEED_HIGH) | 635 | if (snd_usb_get_speed(dev) == USB_SPEED_HIGH) |
419 | fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1) | 636 | fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1) |
420 | * (fp->maxpacksize & 0x7ff); | 637 | * (fp->maxpacksize & 0x7ff); |
421 | fp->attributes = parse_uac_endpoint_attributes(chip, alts, protocol, iface_no); | 638 | fp->attributes = parse_uac_endpoint_attributes(chip, alts, protocol, iface_no); |
422 | fp->clock = clock; | 639 | fp->clock = clock; |
640 | fp->chmap = convert_chmap(num_channels, chconfig, protocol); | ||
423 | 641 | ||
424 | /* some quirks for attributes here */ | 642 | /* some quirks for attributes here */ |
425 | 643 | ||
@@ -455,6 +673,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) | |||
455 | /* ok, let's parse further... */ | 673 | /* ok, let's parse further... */ |
456 | if (snd_usb_parse_audio_format(chip, fp, format, fmt, stream, alts) < 0) { | 674 | if (snd_usb_parse_audio_format(chip, fp, format, fmt, stream, alts) < 0) { |
457 | kfree(fp->rate_table); | 675 | kfree(fp->rate_table); |
676 | kfree(fp->chmap); | ||
458 | kfree(fp); | 677 | kfree(fp); |
459 | fp = NULL; | 678 | fp = NULL; |
460 | continue; | 679 | continue; |
@@ -464,6 +683,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) | |||
464 | err = snd_usb_add_audio_stream(chip, stream, fp); | 683 | err = snd_usb_add_audio_stream(chip, stream, fp); |
465 | if (err < 0) { | 684 | if (err < 0) { |
466 | kfree(fp->rate_table); | 685 | kfree(fp->rate_table); |
686 | kfree(fp->chmap); | ||
467 | kfree(fp); | 687 | kfree(fp); |
468 | return err; | 688 | return err; |
469 | } | 689 | } |