|
@@ -205,6 +205,60 @@ static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audiof
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Helper function to walk the array of sample rate triplets reported by
|
|
|
|
+ * the device. The problem is that we need to parse whole array first to
|
|
|
|
+ * get to know how many sample rates we have to expect.
|
|
|
|
+ * Then fp->rate_table can be allocated and filled.
|
|
|
|
+ */
|
|
|
|
+static int parse_uac2_sample_rate_range(struct audioformat *fp, int nr_triplets,
|
|
|
|
+ const unsigned char *data)
|
|
|
|
+{
|
|
|
|
+ int i, nr_rates = 0;
|
|
|
|
+
|
|
|
|
+ fp->rates = fp->rate_min = fp->rate_max = 0;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < nr_triplets; i++) {
|
|
|
|
+ int min = combine_quad(&data[2 + 12 * i]);
|
|
|
|
+ int max = combine_quad(&data[6 + 12 * i]);
|
|
|
|
+ int res = combine_quad(&data[10 + 12 * i]);
|
|
|
|
+ int rate;
|
|
|
|
+
|
|
|
|
+ if ((max < 0) || (min < 0) || (res < 0) || (max < min))
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * for ranges with res == 1, we announce a continuous sample
|
|
|
|
+ * rate range, and this function should return 0 for no further
|
|
|
|
+ * parsing.
|
|
|
|
+ */
|
|
|
|
+ if (res == 1) {
|
|
|
|
+ fp->rate_min = min;
|
|
|
|
+ fp->rate_max = max;
|
|
|
|
+ fp->rates = SNDRV_PCM_RATE_CONTINUOUS;
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for (rate = min; rate <= max; rate += res) {
|
|
|
|
+ if (fp->rate_table)
|
|
|
|
+ fp->rate_table[nr_rates] = rate;
|
|
|
|
+ if (!fp->rate_min || rate < fp->rate_min)
|
|
|
|
+ fp->rate_min = rate;
|
|
|
|
+ if (!fp->rate_max || rate > fp->rate_max)
|
|
|
|
+ fp->rate_max = rate;
|
|
|
|
+ fp->rates |= snd_pcm_rate_to_rate_bit(rate);
|
|
|
|
+
|
|
|
|
+ nr_rates++;
|
|
|
|
+
|
|
|
|
+ /* avoid endless loop */
|
|
|
|
+ if (res == 0)
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return nr_rates;
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* parse the format descriptor and stores the possible sample rates
|
|
* parse the format descriptor and stores the possible sample rates
|
|
* on the audioformat table (audio class v2).
|
|
* on the audioformat table (audio class v2).
|
|
@@ -215,7 +269,7 @@ static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
|
|
{
|
|
{
|
|
struct usb_device *dev = chip->dev;
|
|
struct usb_device *dev = chip->dev;
|
|
unsigned char tmp[2], *data;
|
|
unsigned char tmp[2], *data;
|
|
- int i, nr_rates, data_size, ret = 0;
|
|
|
|
|
|
+ int nr_triplets, data_size, ret = 0;
|
|
int clock = snd_usb_clock_find_source(chip, chip->ctrl_intf, fp->clock);
|
|
int clock = snd_usb_clock_find_source(chip, chip->ctrl_intf, fp->clock);
|
|
|
|
|
|
if (clock < 0) {
|
|
if (clock < 0) {
|
|
@@ -237,8 +291,8 @@ static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
|
|
goto err;
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
|
|
- nr_rates = (tmp[1] << 8) | tmp[0];
|
|
|
|
- data_size = 2 + 12 * nr_rates;
|
|
|
|
|
|
+ nr_triplets = (tmp[1] << 8) | tmp[0];
|
|
|
|
+ data_size = 2 + 12 * nr_triplets;
|
|
data = kzalloc(data_size, GFP_KERNEL);
|
|
data = kzalloc(data_size, GFP_KERNEL);
|
|
if (!data) {
|
|
if (!data) {
|
|
ret = -ENOMEM;
|
|
ret = -ENOMEM;
|
|
@@ -259,26 +313,28 @@ static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
|
|
goto err_free;
|
|
goto err_free;
|
|
}
|
|
}
|
|
|
|
|
|
- fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL);
|
|
|
|
|
|
+ /* Call the triplet parser, and make sure fp->rate_table is NULL.
|
|
|
|
+ * We just use the return value to know how many sample rates we
|
|
|
|
+ * will have to deal with. */
|
|
|
|
+ kfree(fp->rate_table);
|
|
|
|
+ fp->rate_table = NULL;
|
|
|
|
+ fp->nr_rates = parse_uac2_sample_rate_range(fp, nr_triplets, data);
|
|
|
|
+
|
|
|
|
+ if (fp->nr_rates == 0) {
|
|
|
|
+ /* SNDRV_PCM_RATE_CONTINUOUS */
|
|
|
|
+ ret = 0;
|
|
|
|
+ goto err_free;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ fp->rate_table = kmalloc(sizeof(int) * fp->nr_rates, GFP_KERNEL);
|
|
if (!fp->rate_table) {
|
|
if (!fp->rate_table) {
|
|
ret = -ENOMEM;
|
|
ret = -ENOMEM;
|
|
goto err_free;
|
|
goto err_free;
|
|
}
|
|
}
|
|
|
|
|
|
- fp->nr_rates = 0;
|
|
|
|
- fp->rate_min = fp->rate_max = 0;
|
|
|
|
-
|
|
|
|
- for (i = 0; i < nr_rates; i++) {
|
|
|
|
- int rate = combine_quad(&data[2 + 12 * i]);
|
|
|
|
-
|
|
|
|
- fp->rate_table[fp->nr_rates] = rate;
|
|
|
|
- if (!fp->rate_min || rate < fp->rate_min)
|
|
|
|
- fp->rate_min = rate;
|
|
|
|
- if (!fp->rate_max || rate > fp->rate_max)
|
|
|
|
- fp->rate_max = rate;
|
|
|
|
- fp->rates |= snd_pcm_rate_to_rate_bit(rate);
|
|
|
|
- fp->nr_rates++;
|
|
|
|
- }
|
|
|
|
|
|
+ /* Call the triplet parser again, but this time, fp->rate_table is
|
|
|
|
+ * allocated, so the rates will be stored */
|
|
|
|
+ parse_uac2_sample_rate_range(fp, nr_triplets, data);
|
|
|
|
|
|
err_free:
|
|
err_free:
|
|
kfree(data);
|
|
kfree(data);
|