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
pgpGHEaRy03aA.pgp
Description: PGP signature