Here's a variant that I've actually dared to run.
The sound seems to be working, however rear channels
and S/PDIF probably won't work because SND_CS46XX_NEW_DSP
is still BROKEN.

Like before, sound/pci/cs46xx/write_images.c must be run
on a computer where unsigned long is little-endian 32-bit.
/usr/lib/hotplug/firmware/cs46xx/cwcealdr1_cwcimage saved
this way will then be usable on all architectures.

diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 3b060ab..d963904 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -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_CS4281
 
 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
@@ -210,6 +211,7 @@ config SND_CS46XX
 
 config SND_CS46XX_NEW_DSP
        bool "Cirrus Logic (Sound Fusion) New DSP support"
+       depends on BROKEN
        depends on SND_CS46XX
        default y
        help
diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c
index 2c7bfc9..ed551e1 100644
--- a/sound/pci/cs46xx/cs46xx_lib.c
+++ b/sound/pci/cs46xx/cs46xx_lib.c
@@ -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,23 +361,91 @@ int snd_cs46xx_clear_BA1(struct snd_cs46xx *chip,
 
 #else /* old DSP image */
 
-#include "cs46xx_image.h"
+struct cs46xx_cwcimage_hunk {
+       u32 offset;
+       u32 size;
+};
 
-int snd_cs46xx_download_image(struct snd_cs46xx *chip)
+/* some validation, mostly pointless from a security viewpoint
+ * as malicious firmware can presumably do random DMA anyway */
+static int snd_cs46xx_check_image_size(const struct firmware *firmware)
 {
-       int idx, err;
-       unsigned long offset = 0;
+       const struct cs46xx_cwcimage_hunk *const hunks
+         = (const struct cs46xx_cwcimage_hunk *) firmware->data;
+       const size_t maxbytes = BA1_MEMORY_COUNT * 0x10000;
+       size_t offset;
+       int idx;
 
+       offset = BA1_MEMORY_COUNT * sizeof(struct cs46xx_cwcimage_hunk);
+       if (firmware->size < offset) {
+               snd_printk( KERN_ERR "cs46xx: firmware too small\n");
+               return -EINVAL;
+       }
        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;
-       }       
+               if (hunks[idx].offset % sizeof(u32)
+                   || hunks[idx].size % sizeof(u32)) {
+                       snd_printk( KERN_ERR "cs46xx: firmware hunk 
misaligned\n");
+                       return -EINVAL;
+               }
+
+               if (hunks[idx].offset >= maxbytes
+                   || hunks[idx].size >= maxbytes - hunks[idx].offset) {
+                       snd_printk( KERN_ERR "cs46xx: firmware hunk out of 
range\n");
+                       return -EINVAL;
+               }
+               offset += hunks[idx].size;
+       }
+       if (firmware->size != offset) {
+               snd_printk( KERN_ERR "cs46xx: firmware size mismatch\n");
+               return -EINVAL;
+       }
+
        return 0;
 }
+
+static int snd_cs46xx_download_image(struct snd_cs46xx *chip)
+{
+       int idx, err;
+       size_t offset;
+       const struct firmware *firmware = NULL;
+       const struct cs46xx_cwcimage_hunk *hunks;
+
+       err = request_firmware(&firmware, "cs46xx/cwcealdr1_cwcimage",
+                              &chip->pci->dev);
+       if (err < 0) {
+               snd_printk( KERN_ERR "cs46xx: no firmware\n");
+               goto end;
+       }
+
+       if (firmware->size % sizeof(u32) != 0) {
+               snd_printk( KERN_ERR "cs46xx: firmware size misaligned\n");
+               err = -EINVAL;
+               goto end;
+       }
+       for (offset = 0; offset < firmware->size; offset += sizeof(u32))
+               le32_to_cpup((u32 *) (firmware->data + offset));
+
+       err = snd_cs46xx_check_image_size(firmware);
+       if (err < 0)
+               goto end;
+       hunks = (const struct cs46xx_cwcimage_hunk *) firmware->data;
+
+       /* the actual download */
+       offset = BA1_MEMORY_COUNT * sizeof(struct cs46xx_cwcimage_hunk);
+       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 +4013,5 @@ int __devinit snd_cs46xx_create(struct snd_card *card,
        *rchip = chip;
        return 0;
 }
+
+MODULE_FIRMWARE("cs46xx/cwcealdr1_cwcimage");
diff --git a/sound/pci/cs46xx/write_images.c b/sound/pci/cs46xx/write_images.c
new file mode 100644
index 0000000..bc7f2c2
--- /dev/null
+++ b/sound/pci/cs46xx/write_images.c
@@ -0,0 +1,66 @@
+/*
+ *  Write out firmware images for Cirrus Logic's Sound Fusion CS46XX
+ *  based soundcards
+ *  Copyright (c) by Jaroslav Kysela <[EMAIL PROTECTED]>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ * Modified on 2008-04-06 by Kalle Olavi Niemitalo.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+/* The following two #defines were copied from
+ * linux-2.6.24/sound/pci/cs46xx/cs46xx_lib.h,
+ * Copyright (c) by Jaroslav Kysela <[EMAIL PROTECTED]> */
+/* 3*1024 parameter, 3.5*1024 sample, 2*3.5*1024 code */
+#define BA1_DWORD_SIZE         (13 * 1024 + 512)
+#define BA1_MEMORY_COUNT       3
+
+typedef uint32_t u32;
+#include "../../../../../linux-2.6-2.6.22/sound/pci/cs46xx/cs46xx_image.h"
+
+static int write_image(const char *filename, const void *data, size_t len)
+{
+  FILE *file = fopen(filename, "wb");
+  if (file == NULL) {
+    perror(filename);
+    return 1;
+  }
+  if (fwrite(data, len, 1, file) != 1) {
+    perror(filename);
+    fclose(file);
+    remove(filename);
+    return 1;
+  }
+  if (fclose(file) != 0) {
+    perror(filename);
+    remove(filename);
+    return 1;
+  }
+  return 0;
+}
+
+int main(void)
+{
+  mkdir("cs46xx", S_IRWXU | S_IRWXG | S_IRWXO);
+  if (write_image("cs46xx/cwcealdr1_cwcimage", &BA1Struct, sizeof BA1Struct))
+    return 1;
+  return 0;
+}

Attachment: pgpCRRoU3pF4o.pgp
Description: PGP signature

Reply via email to