[PATCH] ipw2200: switch to the new ipw2200-fw-3.0 image format

This patch modifies the driver to support the ipw2200-fw-3.0 image format.

The 3.0 fw image does not add any new capabilities, but as a result of
image format changes, it should fix two problems experienced by users:

1) Race conditions with the request_firmware interface and udev/hotplug
are improved as only a single request_firmware call is now required to
load the firmware and microcode (vs. 3 separate calls previously)

2) The monitor mode firmware (sniffer) is now packaged with the correct
boot image so it can now function without frequent restarts.

Note: Once you apply this patch, you will also need to upgrade your
firmware image to the 3.0 version available from:

        http://ipw2200.sf.net/firmware.php

Signed-off-by: James Ketrenos <jketreno@linux.intel.com>
Signed-off-by: Zhu Yi <yi.zhu@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
James Ketrenos 2006-03-08 03:22:28 +08:00 committed by John W. Linville
parent 651be26f2d
commit 9006ea75cf
1 changed files with 65 additions and 102 deletions

View File

@ -2829,33 +2829,11 @@ static void ipw_arc_release(struct ipw_priv *priv)
mdelay(5); mdelay(5);
} }
struct fw_header {
u32 version;
u32 mode;
};
struct fw_chunk { struct fw_chunk {
u32 address; u32 address;
u32 length; u32 length;
}; };
#define IPW_FW_MAJOR_VERSION 2
#define IPW_FW_MINOR_VERSION 4
#define IPW_FW_MINOR(x) ((x & 0xff) >> 8)
#define IPW_FW_MAJOR(x) (x & 0xff)
#define IPW_FW_VERSION ((IPW_FW_MINOR_VERSION << 8) | IPW_FW_MAJOR_VERSION)
#define IPW_FW_PREFIX "ipw-" __stringify(IPW_FW_MAJOR_VERSION) \
"." __stringify(IPW_FW_MINOR_VERSION) "-"
#if IPW_FW_MAJOR_VERSION >= 2 && IPW_FW_MINOR_VERSION > 0
#define IPW_FW_NAME(x) IPW_FW_PREFIX "" x ".fw"
#else
#define IPW_FW_NAME(x) "ipw2200_" x ".fw"
#endif
static int ipw_load_ucode(struct ipw_priv *priv, u8 * data, size_t len) static int ipw_load_ucode(struct ipw_priv *priv, u8 * data, size_t len)
{ {
int rc = 0, i, addr; int rc = 0, i, addr;
@ -3124,33 +3102,47 @@ static int ipw_reset_nic(struct ipw_priv *priv)
return rc; return rc;
} }
struct ipw_fw {
u32 ver;
u32 boot_size;
u32 ucode_size;
u32 fw_size;
u8 data[0];
};
static int ipw_get_fw(struct ipw_priv *priv, static int ipw_get_fw(struct ipw_priv *priv,
const struct firmware **fw, const char *name) const struct firmware **raw, const char *name)
{ {
struct fw_header *header; struct ipw_fw *fw;
int rc; int rc;
/* ask firmware_class module to get the boot firmware off disk */ /* ask firmware_class module to get the boot firmware off disk */
rc = request_firmware(fw, name, &priv->pci_dev->dev); rc = request_firmware(raw, name, &priv->pci_dev->dev);
if (rc < 0) { if (rc < 0) {
IPW_ERROR("%s load failed: Reason %d\n", name, rc); IPW_ERROR("%s request_firmware failed: Reason %d\n", name, rc);
return rc; return rc;
} }
header = (struct fw_header *)(*fw)->data; if ((*raw)->size < sizeof(*fw)) {
if (IPW_FW_MAJOR(le32_to_cpu(header->version)) != IPW_FW_MAJOR_VERSION) { IPW_ERROR("%s is too small (%zd)\n", name, (*raw)->size);
IPW_ERROR("'%s' firmware version not compatible (%d != %d)\n",
name,
IPW_FW_MAJOR(le32_to_cpu(header->version)),
IPW_FW_MAJOR_VERSION);
return -EINVAL; return -EINVAL;
} }
IPW_DEBUG_INFO("Loading firmware '%s' file v%d.%d (%zd bytes)\n", fw = (void *)(*raw)->data;
if ((*raw)->size < sizeof(*fw) +
fw->boot_size + fw->ucode_size + fw->fw_size) {
IPW_ERROR("%s is too small or corrupt (%zd)\n",
name, (*raw)->size);
return -EINVAL;
}
IPW_DEBUG_INFO("Read firmware '%s' image v%d.%d (%zd bytes)\n",
name, name,
IPW_FW_MAJOR(le32_to_cpu(header->version)), le32_to_cpu(fw->ver) >> 16,
IPW_FW_MINOR(le32_to_cpu(header->version)), le32_to_cpu(fw->ver) & 0xff,
(*fw)->size - sizeof(struct fw_header)); (*raw)->size - sizeof(*fw));
return 0; return 0;
} }
@ -3190,17 +3182,13 @@ static void ipw_rx_queue_reset(struct ipw_priv *priv,
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int fw_loaded = 0; static int fw_loaded = 0;
static const struct firmware *bootfw = NULL; static const struct firmware *raw = NULL;
static const struct firmware *firmware = NULL;
static const struct firmware *ucode = NULL;
static void free_firmware(void) static void free_firmware(void)
{ {
if (fw_loaded) { if (fw_loaded) {
release_firmware(bootfw); release_firmware(raw);
release_firmware(ucode); raw = NULL;
release_firmware(firmware);
bootfw = ucode = firmware = NULL;
fw_loaded = 0; fw_loaded = 0;
} }
} }
@ -3211,33 +3199,47 @@ static void free_firmware(void)
static int ipw_load(struct ipw_priv *priv) static int ipw_load(struct ipw_priv *priv)
{ {
#ifndef CONFIG_PM #ifndef CONFIG_PM
const struct firmware *bootfw = NULL; const struct firmware *raw = NULL;
const struct firmware *firmware = NULL;
const struct firmware *ucode = NULL;
#endif #endif
char *ucode_name; struct ipw_fw *fw;
char *fw_name; u8 *boot_img, *ucode_img, *fw_img;
u8 *name = NULL;
int rc = 0, retries = 3; int rc = 0, retries = 3;
switch (priv->ieee->iw_mode) { switch (priv->ieee->iw_mode) {
case IW_MODE_ADHOC: case IW_MODE_ADHOC:
ucode_name = IPW_FW_NAME("ibss_ucode"); name = "ipw2200-ibss.fw";
fw_name = IPW_FW_NAME("ibss");
break; break;
#ifdef CONFIG_IPW2200_MONITOR #ifdef CONFIG_IPW2200_MONITOR
case IW_MODE_MONITOR: case IW_MODE_MONITOR:
ucode_name = IPW_FW_NAME("sniffer_ucode"); name = "ipw2200-sniffer.fw";
fw_name = IPW_FW_NAME("sniffer");
break; break;
#endif #endif
case IW_MODE_INFRA: case IW_MODE_INFRA:
ucode_name = IPW_FW_NAME("bss_ucode"); name = "ipw2200-bss.fw";
fw_name = IPW_FW_NAME("bss");
break; break;
default:
rc = -EINVAL;
} }
if (!name) {
rc = -EINVAL;
goto error;
}
#ifdef CONFIG_PM
if (!fw_loaded) {
#endif
rc = ipw_get_fw(priv, &raw, name);
if (rc < 0)
goto error;
#ifdef CONFIG_PM
}
#endif
fw = (void *)raw->data;
boot_img = &fw->data[0];
ucode_img = &fw->data[fw->boot_size];
fw_img = &fw->data[fw->boot_size + fw->ucode_size];
if (rc < 0) if (rc < 0)
goto error; goto error;
@ -3269,18 +3271,8 @@ static int ipw_load(struct ipw_priv *priv)
ipw_zero_memory(priv, IPW_NIC_SRAM_LOWER_BOUND, ipw_zero_memory(priv, IPW_NIC_SRAM_LOWER_BOUND,
IPW_NIC_SRAM_UPPER_BOUND - IPW_NIC_SRAM_LOWER_BOUND); IPW_NIC_SRAM_UPPER_BOUND - IPW_NIC_SRAM_LOWER_BOUND);
#ifdef CONFIG_PM
if (!fw_loaded) {
#endif
rc = ipw_get_fw(priv, &bootfw, IPW_FW_NAME("boot"));
if (rc < 0)
goto error;
#ifdef CONFIG_PM
}
#endif
/* DMA the initial boot firmware into the device */ /* DMA the initial boot firmware into the device */
rc = ipw_load_firmware(priv, bootfw->data + sizeof(struct fw_header), rc = ipw_load_firmware(priv, boot_img, fw->boot_size);
bootfw->size - sizeof(struct fw_header));
if (rc < 0) { if (rc < 0) {
IPW_ERROR("Unable to load boot firmware: %d\n", rc); IPW_ERROR("Unable to load boot firmware: %d\n", rc);
goto error; goto error;
@ -3301,19 +3293,8 @@ static int ipw_load(struct ipw_priv *priv)
/* ack fw init done interrupt */ /* ack fw init done interrupt */
ipw_write32(priv, IPW_INTA_RW, IPW_INTA_BIT_FW_INITIALIZATION_DONE); ipw_write32(priv, IPW_INTA_RW, IPW_INTA_BIT_FW_INITIALIZATION_DONE);
#ifdef CONFIG_PM
if (!fw_loaded) {
#endif
rc = ipw_get_fw(priv, &ucode, ucode_name);
if (rc < 0)
goto error;
#ifdef CONFIG_PM
}
#endif
/* DMA the ucode into the device */ /* DMA the ucode into the device */
rc = ipw_load_ucode(priv, ucode->data + sizeof(struct fw_header), rc = ipw_load_ucode(priv, ucode_img, fw->ucode_size);
ucode->size - sizeof(struct fw_header));
if (rc < 0) { if (rc < 0) {
IPW_ERROR("Unable to load ucode: %d\n", rc); IPW_ERROR("Unable to load ucode: %d\n", rc);
goto error; goto error;
@ -3322,20 +3303,8 @@ static int ipw_load(struct ipw_priv *priv)
/* stop nic */ /* stop nic */
ipw_stop_nic(priv); ipw_stop_nic(priv);
#ifdef CONFIG_PM
if (!fw_loaded) {
#endif
rc = ipw_get_fw(priv, &firmware, fw_name);
if (rc < 0)
goto error;
#ifdef CONFIG_PM
}
#endif
/* DMA bss firmware into the device */ /* DMA bss firmware into the device */
rc = ipw_load_firmware(priv, firmware->data + rc = ipw_load_firmware(priv, fw_img, fw->fw_size);
sizeof(struct fw_header),
firmware->size - sizeof(struct fw_header));
if (rc < 0) { if (rc < 0) {
IPW_ERROR("Unable to load firmware: %d\n", rc); IPW_ERROR("Unable to load firmware: %d\n", rc);
goto error; goto error;
@ -3400,9 +3369,7 @@ static int ipw_load(struct ipw_priv *priv)
ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL); ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL);
#ifndef CONFIG_PM #ifndef CONFIG_PM
release_firmware(bootfw); release_firmware(raw);
release_firmware(ucode);
release_firmware(firmware);
#endif #endif
return 0; return 0;
@ -3412,15 +3379,11 @@ static int ipw_load(struct ipw_priv *priv)
priv->rxq = NULL; priv->rxq = NULL;
} }
ipw_tx_queue_free(priv); ipw_tx_queue_free(priv);
if (bootfw) if (raw)
release_firmware(bootfw); release_firmware(raw);
if (ucode)
release_firmware(ucode);
if (firmware)
release_firmware(firmware);
#ifdef CONFIG_PM #ifdef CONFIG_PM
fw_loaded = 0; fw_loaded = 0;
bootfw = ucode = firmware = NULL; raw = NULL;
#endif #endif
return rc; return rc;