b43: Fix firmware caching

We must also store the ID string (filename) for the cached firmware blobs
and verify that we really have the right firmware cached before using it.
If we don't have the right fw cached, we must free it and request the
correct blobs.

This fixes bandswitch on A/B/G multi-PHY devices.

Signed-off-by: Michael Buesch <mb@bu3sch.de>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Michael Buesch 2008-01-21 19:55:09 +01:00 committed by David S. Miller
parent 71ebb4aac8
commit 61cb5dd6d1
2 changed files with 134 additions and 110 deletions

View File

@ -665,16 +665,23 @@ struct b43_wl {
bool beacon1_uploaded; bool beacon1_uploaded;
}; };
/* In-memory representation of a cached microcode file. */
struct b43_firmware_file {
const char *filename;
const struct firmware *data;
};
/* Pointers to the firmware data and meta information about it. */ /* Pointers to the firmware data and meta information about it. */
struct b43_firmware { struct b43_firmware {
/* Microcode */ /* Microcode */
const struct firmware *ucode; struct b43_firmware_file ucode;
/* PCM code */ /* PCM code */
const struct firmware *pcm; struct b43_firmware_file pcm;
/* Initial MMIO values for the firmware */ /* Initial MMIO values for the firmware */
const struct firmware *initvals; struct b43_firmware_file initvals;
/* Initial MMIO values for the firmware, band-specific */ /* Initial MMIO values for the firmware, band-specific */
const struct firmware *initvals_band; struct b43_firmware_file initvals_band;
/* Firmware revision */ /* Firmware revision */
u16 rev; u16 rev;
/* Firmware patchlevel */ /* Firmware patchlevel */

View File

@ -1557,16 +1557,19 @@ static irqreturn_t b43_interrupt_handler(int irq, void *dev_id)
return ret; return ret;
} }
static void do_release_fw(struct b43_firmware_file *fw)
{
release_firmware(fw->data);
fw->data = NULL;
fw->filename = NULL;
}
static void b43_release_firmware(struct b43_wldev *dev) static void b43_release_firmware(struct b43_wldev *dev)
{ {
release_firmware(dev->fw.ucode); do_release_fw(&dev->fw.ucode);
dev->fw.ucode = NULL; do_release_fw(&dev->fw.pcm);
release_firmware(dev->fw.pcm); do_release_fw(&dev->fw.initvals);
dev->fw.pcm = NULL; do_release_fw(&dev->fw.initvals_band);
release_firmware(dev->fw.initvals);
dev->fw.initvals = NULL;
release_firmware(dev->fw.initvals_band);
dev->fw.initvals_band = NULL;
} }
static void b43_print_fw_helptext(struct b43_wl *wl, bool error) static void b43_print_fw_helptext(struct b43_wl *wl, bool error)
@ -1584,33 +1587,43 @@ static void b43_print_fw_helptext(struct b43_wl *wl, bool error)
static int do_request_fw(struct b43_wldev *dev, static int do_request_fw(struct b43_wldev *dev,
const char *name, const char *name,
const struct firmware **fw) struct b43_firmware_file *fw)
{ {
char path[sizeof(modparam_fwpostfix) + 32]; char path[sizeof(modparam_fwpostfix) + 32];
const struct firmware *blob;
struct b43_fw_header *hdr; struct b43_fw_header *hdr;
u32 size; u32 size;
int err; int err;
if (!name) if (!name) {
/* Don't fetch anything. Free possibly cached firmware. */
do_release_fw(fw);
return 0; return 0;
}
if (fw->filename) {
if (strcmp(fw->filename, name) == 0)
return 0; /* Already have this fw. */
/* Free the cached firmware first. */
do_release_fw(fw);
}
snprintf(path, ARRAY_SIZE(path), snprintf(path, ARRAY_SIZE(path),
"b43%s/%s.fw", "b43%s/%s.fw",
modparam_fwpostfix, name); modparam_fwpostfix, name);
err = request_firmware(fw, path, dev->dev->dev); err = request_firmware(&blob, path, dev->dev->dev);
if (err) { if (err) {
b43err(dev->wl, "Firmware file \"%s\" not found " b43err(dev->wl, "Firmware file \"%s\" not found "
"or load failed.\n", path); "or load failed.\n", path);
return err; return err;
} }
if ((*fw)->size < sizeof(struct b43_fw_header)) if (blob->size < sizeof(struct b43_fw_header))
goto err_format; goto err_format;
hdr = (struct b43_fw_header *)((*fw)->data); hdr = (struct b43_fw_header *)(blob->data);
switch (hdr->type) { switch (hdr->type) {
case B43_FW_TYPE_UCODE: case B43_FW_TYPE_UCODE:
case B43_FW_TYPE_PCM: case B43_FW_TYPE_PCM:
size = be32_to_cpu(hdr->size); size = be32_to_cpu(hdr->size);
if (size != (*fw)->size - sizeof(struct b43_fw_header)) if (size != blob->size - sizeof(struct b43_fw_header))
goto err_format; goto err_format;
/* fallthrough */ /* fallthrough */
case B43_FW_TYPE_IV: case B43_FW_TYPE_IV:
@ -1621,10 +1634,15 @@ static int do_request_fw(struct b43_wldev *dev,
goto err_format; goto err_format;
} }
return err; fw->data = blob;
fw->filename = name;
return 0;
err_format: err_format:
b43err(dev->wl, "Firmware file \"%s\" format error.\n", path); b43err(dev->wl, "Firmware file \"%s\" format error.\n", path);
release_firmware(blob);
return -EPROTO; return -EPROTO;
} }
@ -1636,8 +1654,8 @@ static int b43_request_firmware(struct b43_wldev *dev)
u32 tmshigh; u32 tmshigh;
int err; int err;
/* Get microcode */
tmshigh = ssb_read32(dev->dev, SSB_TMSHIGH); tmshigh = ssb_read32(dev->dev, SSB_TMSHIGH);
if (!fw->ucode) {
if ((rev >= 5) && (rev <= 10)) if ((rev >= 5) && (rev <= 10))
filename = "ucode5"; filename = "ucode5";
else if ((rev >= 11) && (rev <= 12)) else if ((rev >= 11) && (rev <= 12))
@ -1649,8 +1667,8 @@ static int b43_request_firmware(struct b43_wldev *dev)
err = do_request_fw(dev, filename, &fw->ucode); err = do_request_fw(dev, filename, &fw->ucode);
if (err) if (err)
goto err_load; goto err_load;
}
if (!fw->pcm) { /* Get PCM code */
if ((rev >= 5) && (rev <= 10)) if ((rev >= 5) && (rev <= 10))
filename = "pcm5"; filename = "pcm5";
else if (rev >= 11) else if (rev >= 11)
@ -1660,8 +1678,8 @@ static int b43_request_firmware(struct b43_wldev *dev)
err = do_request_fw(dev, filename, &fw->pcm); err = do_request_fw(dev, filename, &fw->pcm);
if (err) if (err)
goto err_load; goto err_load;
}
if (!fw->initvals) { /* Get initvals */
switch (dev->phy.type) { switch (dev->phy.type) {
case B43_PHYTYPE_A: case B43_PHYTYPE_A:
if ((rev >= 5) && (rev <= 10)) { if ((rev >= 5) && (rev <= 10)) {
@ -1692,8 +1710,8 @@ static int b43_request_firmware(struct b43_wldev *dev)
err = do_request_fw(dev, filename, &fw->initvals); err = do_request_fw(dev, filename, &fw->initvals);
if (err) if (err)
goto err_load; goto err_load;
}
if (!fw->initvals_band) { /* Get bandswitch initvals */
switch (dev->phy.type) { switch (dev->phy.type) {
case B43_PHYTYPE_A: case B43_PHYTYPE_A:
if ((rev >= 5) && (rev <= 10)) { if ((rev >= 5) && (rev <= 10)) {
@ -1726,7 +1744,6 @@ static int b43_request_firmware(struct b43_wldev *dev)
err = do_request_fw(dev, filename, &fw->initvals_band); err = do_request_fw(dev, filename, &fw->initvals_band);
if (err) if (err)
goto err_load; goto err_load;
}
return 0; return 0;
@ -1765,18 +1782,18 @@ static int b43_upload_microcode(struct b43_wldev *dev)
int err = 0; int err = 0;
/* Upload Microcode. */ /* Upload Microcode. */
data = (__be32 *) (dev->fw.ucode->data + hdr_len); data = (__be32 *) (dev->fw.ucode.data->data + hdr_len);
len = (dev->fw.ucode->size - hdr_len) / sizeof(__be32); len = (dev->fw.ucode.data->size - hdr_len) / sizeof(__be32);
b43_shm_control_word(dev, B43_SHM_UCODE | B43_SHM_AUTOINC_W, 0x0000); b43_shm_control_word(dev, B43_SHM_UCODE | B43_SHM_AUTOINC_W, 0x0000);
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
b43_write32(dev, B43_MMIO_SHM_DATA, be32_to_cpu(data[i])); b43_write32(dev, B43_MMIO_SHM_DATA, be32_to_cpu(data[i]));
udelay(10); udelay(10);
} }
if (dev->fw.pcm) { if (dev->fw.pcm.data) {
/* Upload PCM data. */ /* Upload PCM data. */
data = (__be32 *) (dev->fw.pcm->data + hdr_len); data = (__be32 *) (dev->fw.pcm.data->data + hdr_len);
len = (dev->fw.pcm->size - hdr_len) / sizeof(__be32); len = (dev->fw.pcm.data->size - hdr_len) / sizeof(__be32);
b43_shm_control_word(dev, B43_SHM_HW, 0x01EA); b43_shm_control_word(dev, B43_SHM_HW, 0x01EA);
b43_write32(dev, B43_MMIO_SHM_DATA, 0x00004000); b43_write32(dev, B43_MMIO_SHM_DATA, 0x00004000);
/* No need for autoinc bit in SHM_HW */ /* No need for autoinc bit in SHM_HW */
@ -1913,19 +1930,19 @@ static int b43_upload_initvals(struct b43_wldev *dev)
size_t count; size_t count;
int err; int err;
hdr = (const struct b43_fw_header *)(fw->initvals->data); hdr = (const struct b43_fw_header *)(fw->initvals.data->data);
ivals = (const struct b43_iv *)(fw->initvals->data + hdr_len); ivals = (const struct b43_iv *)(fw->initvals.data->data + hdr_len);
count = be32_to_cpu(hdr->size); count = be32_to_cpu(hdr->size);
err = b43_write_initvals(dev, ivals, count, err = b43_write_initvals(dev, ivals, count,
fw->initvals->size - hdr_len); fw->initvals.data->size - hdr_len);
if (err) if (err)
goto out; goto out;
if (fw->initvals_band) { if (fw->initvals_band.data) {
hdr = (const struct b43_fw_header *)(fw->initvals_band->data); hdr = (const struct b43_fw_header *)(fw->initvals_band.data->data);
ivals = (const struct b43_iv *)(fw->initvals_band->data + hdr_len); ivals = (const struct b43_iv *)(fw->initvals_band.data->data + hdr_len);
count = be32_to_cpu(hdr->size); count = be32_to_cpu(hdr->size);
err = b43_write_initvals(dev, ivals, count, err = b43_write_initvals(dev, ivals, count,
fw->initvals_band->size - hdr_len); fw->initvals_band.data->size - hdr_len);
if (err) if (err)
goto out; goto out;
} }