[ALSA] usbaudio - Fix Oops with unconventional sample rates
authorTakashi Iwai <tiwai@suse.de>
Thu, 1 Feb 2007 10:50:56 +0000 (11:50 +0100)
committerJaroslav Kysela <perex@suse.cz>
Fri, 9 Feb 2007 08:03:29 +0000 (09:03 +0100)
The patch fixes the memory corruption by the support of unconventional
sample rates.  Also, it avoids the too restrictive constraints if
any of usb descriptions contain continuous rates.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@suse.cz>
sound/usb/usbaudio.c

index 56e42b88addfcbe206dc45eb30a15290109fbe1f..8fd37596e3a171a1a3ecded3c63c23a9ea1354e5 100644 (file)
@@ -186,6 +186,7 @@ struct snd_usb_substream {
        u64 formats;                    /* format bitmasks (all or'ed) */
        unsigned int num_formats;               /* number of supported audio formats (list) */
        struct list_head fmt_list;      /* format list */
+       struct snd_pcm_hw_constraint_list rate_list;    /* limited rates */
        spinlock_t lock;
 
        struct snd_urb_ops ops;         /* callbacks (must be filled at init) */
@@ -1818,28 +1819,33 @@ static int check_hw_params_convention(struct snd_usb_substream *subs)
 static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime,
                                  struct snd_usb_substream *subs)
 {
-       struct list_head *p;
-       struct snd_pcm_hw_constraint_list constraints_rates;
+       struct audioformat *fp;
+       int count = 0, needs_knot = 0;
        int err;
 
-       list_for_each(p, &subs->fmt_list) {
-               struct audioformat *fp;
-               fp = list_entry(p, struct audioformat, list);
-
-               if (!fp->needs_knot)
-                       continue;
-
-               constraints_rates.count = fp->nr_rates;
-               constraints_rates.list = fp->rate_table;
-               constraints_rates.mask = 0;
-
-               err = snd_pcm_hw_constraint_list(runtime, 0,
-                       SNDRV_PCM_HW_PARAM_RATE,
-                       &constraints_rates);
-
-               if (err < 0)
-                       return err;
+       list_for_each_entry(fp, &subs->fmt_list, list) {
+               if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)
+                       return 0;
+               count += fp->nr_rates;
+               if (fp->needs_knot)
+                       needs_knot = 1;
        }
+       if (!needs_knot)
+               return 0;
+
+       subs->rate_list.count = count;
+       subs->rate_list.list = kmalloc(sizeof(int) * count, GFP_KERNEL);
+       subs->rate_list.mask = 0;
+       count = 0;
+       list_for_each_entry(fp, &subs->fmt_list, list) {
+               int i;
+               for (i = 0; i < fp->nr_rates; i++)
+                       subs->rate_list.list[count++] = fp->rate_table[i];
+       }
+       err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+                                        &subs->rate_list);
+       if (err < 0)
+               return err;
 
        return 0;
 }
@@ -2238,6 +2244,7 @@ static void free_substream(struct snd_usb_substream *subs)
                kfree(fp->rate_table);
                kfree(fp);
        }
+       kfree(subs->rate_list.list);
 }