diff options
author | Daniel Drake <dsd@gentoo.org> | 2006-08-12 17:59:59 +0100 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2006-08-14 15:43:22 -0400 |
commit | d066c2190de86d75e17dc35beba48b920cb125ee (patch) | |
tree | 7920cc3c65f77122e1aa65ed4337580c80537257 | |
parent | 12f393089775ca33c53541eb67112fc04d799131 (diff) | |
download | op-kernel-dev-d066c2190de86d75e17dc35beba48b920cb125ee.zip op-kernel-dev-d066c2190de86d75e17dc35beba48b920cb125ee.tar.gz |
[PATCH] zd1211rw: Firmware version vs bootcode version mismatch handling
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>
-rw-r--r-- | drivers/net/wireless/zd1211rw/zd_chip.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/zd1211rw/zd_usb.c | 50 |
2 files changed, 44 insertions, 7 deletions
diff --git a/drivers/net/wireless/zd1211rw/zd_chip.h b/drivers/net/wireless/zd1211rw/zd_chip.h index 9f663f6..4b12508 100644 --- a/drivers/net/wireless/zd1211rw/zd_chip.h +++ b/drivers/net/wireless/zd1211rw/zd_chip.h @@ -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, }; diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c index 2e89c2e..06f72cb 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zd1211rw/zd_usb.c @@ -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 " |