This is work in progress.  To build it, you must disable
SND_CS46XX_NEW_DSP.  I have not yet run this due to difficulties
with LVM, but I am posting this in case someone more
knowledgeable about request_firmware cares to comment.
cs46xx_image.h should be converted to a binary file on a 
little-endian machine where unsigned long is 32-bit.
The driver will then convert it to CPU byte order.

diff -ru linux-2.6-2.6.24/sound/pci/cs46xx/cs46xx_lib.c 
HACKED/linux-2.6-2.6.24/sound/pci/cs46xx/cs46xx_lib.c
--- linux-2.6-2.6.24/sound/pci/cs46xx/cs46xx_lib.c      2008-01-25 
00:58:37.000000000 +0200
+++ HACKED/linux-2.6-2.6.24/sound/pci/cs46xx/cs46xx_lib.c       2008-02-24 
19:10:48.000000000 +0200
@@ -4,6 +4,8 @@
  *                   Cirrus Logic, Inc.
  *  Routines for control of Cirrus Logic CS461x chips
  *
+ *  Modified on 2008-02-24 by Kalle Olavi Niemitalo.
+ *
  *  KNOWN BUGS:
  *    - Sometimes the SPDIF input DSP tasks get's unsynchronized
  *      and the SPDIF get somewhat "distorcionated", or/and left right channel
@@ -54,6 +56,7 @@
 #include <linux/slab.h>
 #include <linux/gameport.h>
 #include <linux/mutex.h>
+#include <linux/firmware.h>
 
 
 #include <sound/core.h>
@@ -358,22 +361,70 @@
 
 #else /* old DSP image */
 
-#include "cs46xx_image.h"
-
-int snd_cs46xx_download_image(struct snd_cs46xx *chip)
+static int snd_cs46xx_download_image(struct snd_cs46xx *chip)
 {
        int idx, err;
-       unsigned long offset = 0;
+       size_t offset;
+       const size_t maxbytes = BA1_MEMORY_COUNT * 0x10000;
+       const struct firmware *firmware = NULL;
+       const struct fwhunk { u32 offset, size; } *hunks;
+
+       err = request_firmware(&firmware, "cs46xx/cwcealdr1_cwcimage",
+                              &chip->pci->dev);
+       if (err < 0) {
+               snd_printk( KERN_ERR "cs46xx: no firmware\n");
+               goto end;
+       }
 
+       for (offset = 0; offset < firmware->size; offset += sizeof(u32))
+               le32_to_cpup((u32 *) (firmware->data + offset));
+       hunks = (const struct fwhunk *) firmware->data;
+
+       /* some validation, mostly pointless from a security viewpoint
+        * as malicious firmware can do random DMA anyway */
+       offset = BA1_MEMORY_COUNT * sizeof(struct fwhunk);
+       if (firmware->size < offset) {
+               snd_printk( KERN_ERR "cs46xx: firmware too small\n");
+               err = -EINVAL;
+               goto end;
+       }
        for (idx = 0; idx < BA1_MEMORY_COUNT; idx++) {
-               if ((err = snd_cs46xx_download(chip,
-                                              &BA1Struct.map[offset],
-                                              BA1Struct.memory[idx].offset,
-                                              BA1Struct.memory[idx].size)) < 0)
-                       return err;
-               offset += BA1Struct.memory[idx].size >> 2;
-       }       
-       return 0;
+               if (hunks[idx].offset % sizeof(u32)
+                   || hunks[idx].size % sizeof(u32)) {
+                       snd_printk( KERN_ERR "cs46xx: firmware hunk 
misaligned\n");
+                       err = -EINVAL;
+                       goto end;
+               }
+
+               if (hunks[idx].offset >= maxbytes
+                   || hunks[idx].size >= maxbytes - hunks[idx].offset) {
+                       snd_printk( KERN_ERR "cs46xx: firmware hunk out of 
range\n");
+                       err = -EINVAL;
+                       goto end;
+               }
+               offset += hunks[idx].size;
+       }
+       if (firmware->size != offset) {
+               snd_printk( KERN_ERR "cs46xx: firmware size mismatch\n");
+               err = -EINVAL;
+               goto end;
+       }
+
+       /* the actual download */
+       offset = BA1_MEMORY_COUNT * sizeof(struct fwhunk);
+       for (idx = 0; idx < BA1_MEMORY_COUNT; idx++) {
+               err = snd_cs46xx_download(chip,
+                                         (__u32 *) (firmware->data + offset),
+                                         hunks[idx].offset,
+                                         hunks[idx].size);
+               if (err < 0)
+                       goto end;
+               offset += hunks[idx].size;
+       }
+       err = 0;
+end:
+       release_firmware(firmware);
+       return err;
 }
 #endif /* CONFIG_SND_CS46XX_NEW_DSP */
 
@@ -3942,3 +3993,5 @@
        *rchip = chip;
        return 0;
 }
+
+MODULE_FIRMWARE("cs46xx/cwcealdr1_cwcimage");
diff -ru linux-2.6-2.6.24/sound/pci/Kconfig 
HACKED/linux-2.6-2.6.24/sound/pci/Kconfig
--- linux-2.6-2.6.24/sound/pci/Kconfig  2008-01-25 09:59:32.000000000 +0200
+++ HACKED/linux-2.6-2.6.24/sound/pci/Kconfig   2008-02-24 15:19:57.000000000 
+0200
@@ -1,4 +1,5 @@
 # ALSA PCI drivers
+# Modified on 2008-02-24 by Kalle Olavi Niemitalo
 
 menu "PCI devices"
        depends on SND!=n && PCI
@@ -197,8 +198,8 @@
 
 config SND_CS46XX
        tristate "Cirrus Logic (Sound Fusion) CS4280/CS461x/CS462x/CS463x"
-       depends on BROKEN
        depends on SND
+       select FW_LOADER
        select SND_RAWMIDI
        select SND_AC97_CODEC
        help

Attachment: pgpPZusJD6oNi.pgp
Description: PGP signature

Reply via email to