aboutsummaryrefslogtreecommitdiffstats
path: root/sound/usb
diff options
context:
space:
mode:
authorDaniel Mack <daniel@caiaq.de>2010-06-11 11:46:33 -0400
committerTakashi Iwai <tiwai@suse.de>2010-06-11 12:06:12 -0400
commit67c103664a06fa590f2990c01773dfa1dffcefdc (patch)
tree8b68605b1381b65e793222fd5c0625c642c27b35 /sound/usb
parent11bcbc443a17653c65bc20029172fae76f4bcca4 (diff)
ALSA: usb-audio: parse UAC2 sample rate ranges correctly
A device may report its supported sample rates in ranges rather than in discrete triplets. The code used to only parse the MIN field instead of properly paying attention to the MAX and RES values. Also, handle RES values of 1 correctly and announce a continous sample rate range in this case. Signed-off-by: Daniel Mack <daniel@caiaq.de> Reported-by: Alex Lee <alexlee188@gmail.com> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/usb')
-rw-r--r--sound/usb/format.c92
1 files changed, 74 insertions, 18 deletions
diff --git a/sound/usb/format.c b/sound/usb/format.c
index 8eccf17a4ac6..30364aba79cc 100644
--- a/sound/usb/format.c
+++ b/sound/usb/format.c
@@ -206,6 +206,60 @@ static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audiof
206} 206}
207 207
208/* 208/*
209 * Helper function to walk the array of sample rate triplets reported by
210 * the device. The problem is that we need to parse whole array first to
211 * get to know how many sample rates we have to expect.
212 * Then fp->rate_table can be allocated and filled.
213 */
214static int parse_uac2_sample_rate_range(struct audioformat *fp, int nr_triplets,
215 const unsigned char *data)
216{
217 int i, nr_rates = 0;
218
219 fp->rates = fp->rate_min = fp->rate_max = 0;
220
221 for (i = 0; i < nr_triplets; i++) {
222 int min = combine_quad(&data[2 + 12 * i]);
223 int max = combine_quad(&data[6 + 12 * i]);
224 int res = combine_quad(&data[10 + 12 * i]);
225 int rate;
226
227 if ((max < 0) || (min < 0) || (res < 0) || (max < min))
228 continue;
229
230 /*
231 * for ranges with res == 1, we announce a continuous sample
232 * rate range, and this function should return 0 for no further
233 * parsing.
234 */
235 if (res == 1) {
236 fp->rate_min = min;
237 fp->rate_max = max;
238 fp->rates = SNDRV_PCM_RATE_CONTINUOUS;
239 return 0;
240 }
241
242 for (rate = min; rate <= max; rate += res) {
243 if (fp->rate_table)
244 fp->rate_table[nr_rates] = rate;
245 if (!fp->rate_min || rate < fp->rate_min)
246 fp->rate_min = rate;
247 if (!fp->rate_max || rate > fp->rate_max)
248 fp->rate_max = rate;
249 fp->rates |= snd_pcm_rate_to_rate_bit(rate);
250
251 nr_rates++;
252
253 /* avoid endless loop */
254 if (res == 0)
255 break;
256 }
257 }
258
259 return nr_rates;
260}
261
262/*
209 * parse the format descriptor and stores the possible sample rates 263 * parse the format descriptor and stores the possible sample rates
210 * on the audioformat table (audio class v2). 264 * on the audioformat table (audio class v2).
211 */ 265 */
@@ -215,7 +269,7 @@ static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
215{ 269{
216 struct usb_device *dev = chip->dev; 270 struct usb_device *dev = chip->dev;
217 unsigned char tmp[2], *data; 271 unsigned char tmp[2], *data;
218 int i, nr_rates, data_size, ret = 0; 272 int nr_triplets, data_size, ret = 0;
219 int clock = snd_usb_clock_find_source(chip, chip->ctrl_intf, fp->clock); 273 int clock = snd_usb_clock_find_source(chip, chip->ctrl_intf, fp->clock);
220 274
221 if (clock < 0) { 275 if (clock < 0) {
@@ -237,8 +291,8 @@ static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
237 goto err; 291 goto err;
238 } 292 }
239 293
240 nr_rates = (tmp[1] << 8) | tmp[0]; 294 nr_triplets = (tmp[1] << 8) | tmp[0];
241 data_size = 2 + 12 * nr_rates; 295 data_size = 2 + 12 * nr_triplets;
242 data = kzalloc(data_size, GFP_KERNEL); 296 data = kzalloc(data_size, GFP_KERNEL);
243 if (!data) { 297 if (!data) {
244 ret = -ENOMEM; 298 ret = -ENOMEM;
@@ -259,26 +313,28 @@ static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
259 goto err_free; 313 goto err_free;
260 } 314 }
261 315
262 fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL); 316 /* Call the triplet parser, and make sure fp->rate_table is NULL.
317 * We just use the return value to know how many sample rates we
318 * will have to deal with. */
319 kfree(fp->rate_table);
320 fp->rate_table = NULL;
321 fp->nr_rates = parse_uac2_sample_rate_range(fp, nr_triplets, data);
322
323 if (fp->nr_rates == 0) {
324 /* SNDRV_PCM_RATE_CONTINUOUS */
325 ret = 0;
326 goto err_free;
327 }
328
329 fp->rate_table = kmalloc(sizeof(int) * fp->nr_rates, GFP_KERNEL);
263 if (!fp->rate_table) { 330 if (!fp->rate_table) {
264 ret = -ENOMEM; 331 ret = -ENOMEM;
265 goto err_free; 332 goto err_free;
266 } 333 }
267 334
268 fp->nr_rates = 0; 335 /* Call the triplet parser again, but this time, fp->rate_table is
269 fp->rate_min = fp->rate_max = 0; 336 * allocated, so the rates will be stored */
270 337 parse_uac2_sample_rate_range(fp, nr_triplets, data);
271 for (i = 0; i < nr_rates; i++) {
272 int rate = combine_quad(&data[2 + 12 * i]);
273
274 fp->rate_table[fp->nr_rates] = rate;
275 if (!fp->rate_min || rate < fp->rate_min)
276 fp->rate_min = rate;
277 if (!fp->rate_max || rate > fp->rate_max)
278 fp->rate_max = rate;
279 fp->rates |= snd_pcm_rate_to_rate_bit(rate);
280 fp->nr_rates++;
281 }
282 338
283err_free: 339err_free:
284 kfree(data); 340 kfree(data);