[PATCH] zd1211rw: Firmware version vs bootcode version mismatch handling
authorDaniel Drake <dsd@gentoo.org>
Sat, 12 Aug 2006 16:59:59 +0000 (17:59 +0100)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 14 Aug 2006 19:43:22 +0000 (15:43 -0400)
This is needed for my G220F, otherwise it fails to initialize after the
existing firmware upload routine.

The vendor driver actually does more than what I have done here: it
downloads the firmware + boot code, modifies it, and uploads it again
(really messy). I have not copied that part over, as my device can get
on its feet without it.

Signed-off-by: Daniel Drake <dsd@gentoo.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/zd1211rw/zd_chip.h
drivers/net/wireless/zd1211rw/zd_usb.c

index 9f663f6..4b12508 100644 (file)
@@ -638,6 +638,7 @@ enum {
        LOAD_CODE_SIZE                  = 0xe, /* words */
        LOAD_VECT_SIZE                  = 0x10000 - 0xfff7, /* words */
        EEPROM_REGS_OFFSET              = LOAD_CODE_SIZE + LOAD_VECT_SIZE,
+       EEPROM_REGS_SIZE                = 0x7e, /* words */
        E2P_BASE_OFFSET                 = EEPROM_START_OFFSET +
                                          EEPROM_REGS_OFFSET,
 };
index 2e89c2e..06f72cb 100644 (file)
@@ -16,6 +16,7 @@
  */
 
 #include <asm/unaligned.h>
+#include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/firmware.h>
@@ -267,6 +268,39 @@ static char *get_fw_name(char *buffer, size_t size, u8 device_type,
        return buffer;
 }
 
+static int handle_version_mismatch(struct usb_device *udev, u8 device_type,
+       const struct firmware *ub_fw)
+{
+       const struct firmware *ur_fw = NULL;
+       int offset;
+       int r = 0;
+       char fw_name[128];
+
+       r = request_fw_file(&ur_fw,
+               get_fw_name(fw_name, sizeof(fw_name), device_type, "ur"),
+               &udev->dev);
+       if (r)
+               goto error;
+
+       r = upload_code(udev, ur_fw->data, ur_fw->size, FW_START_OFFSET,
+               REBOOT);
+       if (r)
+               goto error;
+
+       offset = ((EEPROM_REGS_OFFSET + EEPROM_REGS_SIZE) * sizeof(u16));
+       r = upload_code(udev, ub_fw->data + offset, ub_fw->size - offset,
+               E2P_BASE_OFFSET + EEPROM_REGS_SIZE, REBOOT);
+
+       /* At this point, the vendor driver downloads the whole firmware
+        * image, hacks around with version IDs, and uploads it again,
+        * completely overwriting the boot code. We do not do this here as
+        * it is not required on any tested devices, and it is suspected to
+        * cause problems. */
+error:
+       release_firmware(ur_fw);
+       return r;
+}
+
 static int upload_firmware(struct usb_device *udev, u8 device_type)
 {
        int r;
@@ -286,15 +320,17 @@ static int upload_firmware(struct usb_device *udev, u8 device_type)
 
        fw_bcdDevice = get_word(ub_fw->data, EEPROM_REGS_OFFSET);
 
-       /* FIXME: do we have any reason to perform the kludge that the vendor
-        * driver does when there is a version mismatch? (their driver uploads
-        * different firmwares and stuff)
-        */
        if (fw_bcdDevice != bcdDevice) {
                dev_info(&udev->dev,
-                       "firmware device id %#06x and actual device id "
-                       "%#06x differ, continuing anyway\n",
-                       fw_bcdDevice, bcdDevice);
+                       "firmware version %#06x and device bootcode version "
+                       "%#06x differ\n", fw_bcdDevice, bcdDevice);
+               if (bcdDevice <= 0x4313)
+                       dev_warn(&udev->dev, "device has old bootcode, please "
+                               "report success or failure\n");
+
+               r = handle_version_mismatch(udev, device_type, ub_fw);
+               if (r)
+                       goto error;
        } else {
                dev_dbg_f(&udev->dev,
                        "firmware device id %#06x is equal to the "