Hi,
Broadcom has many, many firmware versions for their bcm43xx hardware:
19 microcode2
24 microcode4
24 microcode5
6 microcode11
3 microcode13
2 pcm4
2 pcm5
I still think that we should allow as much versions as possible. This is
free software and the user should have all possibilities. This can
produce some trouble, but OTOH the user can switch a fw image easily.
This is more important for me because we don't know what kind of bugs
are in all those firmware images. And switching fw images is the only
way we have if a bug is in the fw.
But the driver doesn't know which fw version he is using. And now it
looks like that we have to treat the 4.x firmware in a diffferent way.
The driver doesn't know if he talks to a 4.x or a 3.x firmware. And in
the end we'll have to work with both versions because microcode2 is
only available in 3.x format and microcode13 only in 4.x format.
It's time to introduce some firmware version detection mechanism...
Here is the basic idea. It's similar to the bcm43xx-fwcutter philosohy.
It calculates the md5 signature and then it will look into a table to
get the firmware version number. But look at the patch for more details.
Am I on the wrong way?
Martin
( The attached bcm43xx code is more an example. Some microcode tables
are missing completely and a lot of versions are missing in the tables,
too. But so it wouldn't hurt too much if I have to switch to sha1. :)
Signed-off-by: Martin Langer <[EMAIL PROTECTED]>
--- ../../linux-2.6.17/drivers/base/firmware_class.c 2006-06-18
03:49:35.000000000 +0200
+++ drivers/base/firmware_class.c 2006-07-06 14:06:35.000000000 +0200
@@ -15,6 +15,8 @@
#include <linux/vmalloc.h>
#include <linux/interrupt.h>
#include <linux/bitops.h>
+#include <linux/scatterlist.h>
+#include <linux/crypto.h>
#include <asm/semaphore.h>
#include <linux/firmware.h>
@@ -48,6 +50,40 @@
struct timer_list timeout;
};
+s32 firmware_version(const struct firmware *fw, struct firmware_files *elmnt)
+{
+ struct scatterlist sg[1];
+ struct crypto_tfm *tfm;
+ char signature[17];
+ char longsig[33];
+
+ tfm = crypto_alloc_tfm("md5", 0);
+ if (tfm == NULL)
+ return -1;
+
+ sg_set_buf(sg, fw->data, fw->size);
+
+ crypto_digest_init(tfm);
+ crypto_digest_update(tfm, sg, 1);
+ crypto_digest_final(tfm, signature);
+ crypto_free_tfm(tfm);
+
+ snprintf(longsig, 33, "%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x"
+ "%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x",
+ signature[0], signature[1], signature[2], signature [3],
+ signature[4], signature[5], signature[6], signature [7],
+ signature[8], signature[9], signature[10], signature [11],
+ signature[12], signature[13], signature[14], signature [15]);
+
+ while (elmnt->id) {
+ if (strncmp(longsig, elmnt->signature, 32) == 0)
+ return elmnt->id;
+ elmnt++;
+ }
+
+ return 0;
+}
+
static void
fw_load_abort(struct firmware_priv *fw_priv)
{
@@ -605,6 +641,7 @@
module_init(firmware_class_init);
module_exit(firmware_class_exit);
+EXPORT_SYMBOL(firmware_version);
EXPORT_SYMBOL(release_firmware);
EXPORT_SYMBOL(request_firmware);
EXPORT_SYMBOL(request_firmware_nowait);
--- ../../linux-2.6.17/include/linux/firmware.h 2006-06-18 03:49:35.000000000
+0200
+++ include/linux/firmware.h 2006-07-06 14:06:56.000000000 +0200
@@ -10,7 +10,12 @@
size_t size;
u8 *data;
};
+struct firmware_files {
+ u32 id;
+ char signature[128];
+};
struct device;
+s32 firmware_version(const struct firmware *fw, struct firmware_files *elmnt);
int request_firmware(const struct firmware **fw, const char *name,
struct device *device);
int request_firmware_nowait(
--- ../../linux-2.6.17/drivers/net/wireless/bcm43xx/bcm43xx_main.c
2006-06-18 03:49:35.000000000 +0200
+++ drivers/net/wireless/bcm43xx/bcm43xx_main.c 2006-07-06 14:05:44.000000000
+0200
@@ -148,6 +148,30 @@
};
MODULE_DEVICE_TABLE(pci, bcm43xx_pci_tbl);
+static struct firmware_files ucode5_sig_tbl[] = {
+ { 1, "ed5526f92ecb8f769034a0f16733005f" }, /* 3.130.20.0 */
+ { 2, "651e4fc192b6ae16b2f63956c5668dbd" }, /* 4.10.40.0 */
+ { 0 },
+};
+
+static struct firmware_bcm_versions ucode5_version_tbl[] = {
+ { 1, 3, 130, 20, 0 },
+ { 2, 4, 10, 40, 0 },
+ { 0 },
+};
+
+static struct firmware_files pcm5_sig_tbl[] = {
+ { 1, "2d0fd402889df1d1f6b3dd8b4d37cc9a" }, /* 3.30.15.0 */
+ { 2, "ff29cd075675a544bdc5d5ae953d964a" }, /* 3.90.7.0 */
+ { 0 },
+};
+
+static struct firmware_bcm_versions pcm5_version_tbl[] = {
+ { 1, 3, 30, 15, 0 },
+ { 2, 3, 90, 7, 0 },
+ { 0 },
+};
+
static void bcm43xx_ram_write(struct bcm43xx_private *bcm, u16 offset, u32 val)
{
u32 status;
@@ -1929,7 +1953,10 @@
static int bcm43xx_request_firmware(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ struct firmware_bcm_versions *ucode5v = ucode5_version_tbl;
+ struct firmware_bcm_versions *pcm5v = pcm5_version_tbl;
u8 rev = bcm->current_core->rev;
+ u32 ver = 0;
int err = 0;
int nr;
char buf[22 + sizeof(modparam_fwpostfix) - 1] = { 0 };
@@ -1945,6 +1972,24 @@
buf);
goto error;
}
+
+ ver = firmware_version(bcm->ucode, ucode5_sig_tbl);
+ if (ver > 0) {
+ while (ucode5v->id) {
+ if (ucode5v->id == ver)
+ break;
+ ucode5v++;
+ }
+
+ printk(KERN_INFO PFX "UCODE: %s version %i.%i.%i.%i\n",
+ buf,
+ ucode5v->major_version,
+ ucode5v->minor_version,
+ ucode5v->release_candidate,
+ ucode5v->patchlevel);
+ } else
+ printk(KERN_INFO PFX "UCODE: %s detection failure.\n",
+ buf);
}
if (!bcm->pcm) {
@@ -1959,6 +2004,24 @@
buf);
goto error;
}
+
+ ver = firmware_version(bcm->pcm, pcm5_sig_tbl);
+ if (ver > 0) {
+ while (pcm5v->id) {
+ if (pcm5v->id == ver)
+ break;
+ pcm5v++;
+ }
+
+ printk(KERN_INFO PFX "PCM: %s version %i.%i.%i.%i\n",
+ buf,
+ pcm5v->major_version,
+ pcm5v->minor_version,
+ pcm5v->release_candidate,
+ pcm5v->patchlevel);
+ } else
+ printk(KERN_INFO PFX "PCM: %s detection failure.\n",
+ buf);
}
if (!bcm->initvals0) {
--- ../../linux-2.6.17/drivers/net/wireless/bcm43xx/bcm43xx.h 2006-06-18
03:49:35.000000000 +0200
+++ drivers/net/wireless/bcm43xx/bcm43xx.h 2006-07-06 12:19:22.000000000
+0200
@@ -403,6 +403,13 @@
# define dprintk(f, x...) do { /* nothing */ } while (0)
#endif
+struct firmware_bcm_versions {
+ const u32 id;
+ const u16 major_version;
+ const u16 minor_version;
+ const u16 release_candidate;
+ const u16 patchlevel;
+};
struct net_device;
struct pci_dev;