Skip to content

Commit

Permalink
b43: Fix firmware caching
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Michael Buesch authored and David S. Miller committed Jan 28, 2008
1 parent 71ebb4a commit 61cb5dd
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 110 deletions.
15 changes: 11 additions & 4 deletions drivers/net/wireless/b43/b43.h
Original file line number Diff line number Diff line change
Expand Up @@ -665,16 +665,23 @@ struct b43_wl {
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. */
struct b43_firmware {
/* Microcode */
const struct firmware *ucode;
struct b43_firmware_file ucode;
/* PCM code */
const struct firmware *pcm;
struct b43_firmware_file pcm;
/* Initial MMIO values for the firmware */
const struct firmware *initvals;
struct b43_firmware_file initvals;
/* Initial MMIO values for the firmware, band-specific */
const struct firmware *initvals_band;
struct b43_firmware_file initvals_band;

/* Firmware revision */
u16 rev;
/* Firmware patchlevel */
Expand Down
229 changes: 123 additions & 106 deletions drivers/net/wireless/b43/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -1557,16 +1557,19 @@ static irqreturn_t b43_interrupt_handler(int irq, void *dev_id)
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)
{
release_firmware(dev->fw.ucode);
dev->fw.ucode = NULL;
release_firmware(dev->fw.pcm);
dev->fw.pcm = NULL;
release_firmware(dev->fw.initvals);
dev->fw.initvals = NULL;
release_firmware(dev->fw.initvals_band);
dev->fw.initvals_band = NULL;
do_release_fw(&dev->fw.ucode);
do_release_fw(&dev->fw.pcm);
do_release_fw(&dev->fw.initvals);
do_release_fw(&dev->fw.initvals_band);
}

static void b43_print_fw_helptext(struct b43_wl *wl, bool error)
Expand All @@ -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,
const char *name,
const struct firmware **fw)
struct b43_firmware_file *fw)
{
char path[sizeof(modparam_fwpostfix) + 32];
const struct firmware *blob;
struct b43_fw_header *hdr;
u32 size;
int err;

if (!name)
if (!name) {
/* Don't fetch anything. Free possibly cached firmware. */
do_release_fw(fw);
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),
"b43%s/%s.fw",
modparam_fwpostfix, name);
err = request_firmware(fw, path, dev->dev->dev);
err = request_firmware(&blob, path, dev->dev->dev);
if (err) {
b43err(dev->wl, "Firmware file \"%s\" not found "
"or load failed.\n", path);
return err;
}
if ((*fw)->size < sizeof(struct b43_fw_header))
if (blob->size < sizeof(struct b43_fw_header))
goto err_format;
hdr = (struct b43_fw_header *)((*fw)->data);
hdr = (struct b43_fw_header *)(blob->data);
switch (hdr->type) {
case B43_FW_TYPE_UCODE:
case B43_FW_TYPE_PCM:
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;
/* fallthrough */
case B43_FW_TYPE_IV:
Expand All @@ -1621,10 +1634,15 @@ static int do_request_fw(struct b43_wldev *dev,
goto err_format;
}

return err;
fw->data = blob;
fw->filename = name;

return 0;

err_format:
b43err(dev->wl, "Firmware file \"%s\" format error.\n", path);
release_firmware(blob);

return -EPROTO;
}

Expand All @@ -1636,97 +1654,96 @@ static int b43_request_firmware(struct b43_wldev *dev)
u32 tmshigh;
int err;

/* Get microcode */
tmshigh = ssb_read32(dev->dev, SSB_TMSHIGH);
if (!fw->ucode) {
if ((rev >= 5) && (rev <= 10))
filename = "ucode5";
else if ((rev >= 11) && (rev <= 12))
filename = "ucode11";
else if (rev >= 13)
filename = "ucode13";
else
goto err_no_ucode;
err = do_request_fw(dev, filename, &fw->ucode);
if (err)
goto err_load;

/* Get PCM code */
if ((rev >= 5) && (rev <= 10))
filename = "pcm5";
else if (rev >= 11)
filename = NULL;
else
goto err_no_pcm;
err = do_request_fw(dev, filename, &fw->pcm);
if (err)
goto err_load;

/* Get initvals */
switch (dev->phy.type) {
case B43_PHYTYPE_A:
if ((rev >= 5) && (rev <= 10)) {
if (tmshigh & B43_TMSHIGH_HAVE_2GHZ_PHY)
filename = "a0g1initvals5";
else
filename = "a0g0initvals5";
} else
goto err_no_initvals;
break;
case B43_PHYTYPE_G:
if ((rev >= 5) && (rev <= 10))
filename = "ucode5";
else if ((rev >= 11) && (rev <= 12))
filename = "ucode11";
filename = "b0g0initvals5";
else if (rev >= 13)
filename = "ucode13";
filename = "lp0initvals13";
else
goto err_no_ucode;
err = do_request_fw(dev, filename, &fw->ucode);
if (err)
goto err_load;
goto err_no_initvals;
break;
case B43_PHYTYPE_N:
if ((rev >= 11) && (rev <= 12))
filename = "n0initvals11";
else
goto err_no_initvals;
break;
default:
goto err_no_initvals;
}
if (!fw->pcm) {
err = do_request_fw(dev, filename, &fw->initvals);
if (err)
goto err_load;

/* Get bandswitch initvals */
switch (dev->phy.type) {
case B43_PHYTYPE_A:
if ((rev >= 5) && (rev <= 10)) {
if (tmshigh & B43_TMSHIGH_HAVE_2GHZ_PHY)
filename = "a0g1bsinitvals5";
else
filename = "a0g0bsinitvals5";
} else if (rev >= 11)
filename = NULL;
else
goto err_no_initvals;
break;
case B43_PHYTYPE_G:
if ((rev >= 5) && (rev <= 10))
filename = "pcm5";
filename = "b0g0bsinitvals5";
else if (rev >= 11)
filename = NULL;
else
goto err_no_pcm;
err = do_request_fw(dev, filename, &fw->pcm);
if (err)
goto err_load;
}
if (!fw->initvals) {
switch (dev->phy.type) {
case B43_PHYTYPE_A:
if ((rev >= 5) && (rev <= 10)) {
if (tmshigh & B43_TMSHIGH_HAVE_2GHZ_PHY)
filename = "a0g1initvals5";
else
filename = "a0g0initvals5";
} else
goto err_no_initvals;
break;
case B43_PHYTYPE_G:
if ((rev >= 5) && (rev <= 10))
filename = "b0g0initvals5";
else if (rev >= 13)
filename = "lp0initvals13";
else
goto err_no_initvals;
break;
case B43_PHYTYPE_N:
if ((rev >= 11) && (rev <= 12))
filename = "n0initvals11";
else
goto err_no_initvals;
break;
default:
goto err_no_initvals;
}
err = do_request_fw(dev, filename, &fw->initvals);
if (err)
goto err_load;
}
if (!fw->initvals_band) {
switch (dev->phy.type) {
case B43_PHYTYPE_A:
if ((rev >= 5) && (rev <= 10)) {
if (tmshigh & B43_TMSHIGH_HAVE_2GHZ_PHY)
filename = "a0g1bsinitvals5";
else
filename = "a0g0bsinitvals5";
} else if (rev >= 11)
filename = NULL;
else
goto err_no_initvals;
break;
case B43_PHYTYPE_G:
if ((rev >= 5) && (rev <= 10))
filename = "b0g0bsinitvals5";
else if (rev >= 11)
filename = NULL;
else
goto err_no_initvals;
break;
case B43_PHYTYPE_N:
if ((rev >= 11) && (rev <= 12))
filename = "n0bsinitvals11";
else
goto err_no_initvals;
break;
default:
break;
case B43_PHYTYPE_N:
if ((rev >= 11) && (rev <= 12))
filename = "n0bsinitvals11";
else
goto err_no_initvals;
}
err = do_request_fw(dev, filename, &fw->initvals_band);
if (err)
goto err_load;
break;
default:
goto err_no_initvals;
}
err = do_request_fw(dev, filename, &fw->initvals_band);
if (err)
goto err_load;

return 0;

Expand Down Expand Up @@ -1765,18 +1782,18 @@ static int b43_upload_microcode(struct b43_wldev *dev)
int err = 0;

/* Upload Microcode. */
data = (__be32 *) (dev->fw.ucode->data + hdr_len);
len = (dev->fw.ucode->size - hdr_len) / sizeof(__be32);
data = (__be32 *) (dev->fw.ucode.data->data + hdr_len);
len = (dev->fw.ucode.data->size - hdr_len) / sizeof(__be32);
b43_shm_control_word(dev, B43_SHM_UCODE | B43_SHM_AUTOINC_W, 0x0000);
for (i = 0; i < len; i++) {
b43_write32(dev, B43_MMIO_SHM_DATA, be32_to_cpu(data[i]));
udelay(10);
}

if (dev->fw.pcm) {
if (dev->fw.pcm.data) {
/* Upload PCM data. */
data = (__be32 *) (dev->fw.pcm->data + hdr_len);
len = (dev->fw.pcm->size - hdr_len) / sizeof(__be32);
data = (__be32 *) (dev->fw.pcm.data->data + hdr_len);
len = (dev->fw.pcm.data->size - hdr_len) / sizeof(__be32);
b43_shm_control_word(dev, B43_SHM_HW, 0x01EA);
b43_write32(dev, B43_MMIO_SHM_DATA, 0x00004000);
/* No need for autoinc bit in SHM_HW */
Expand Down Expand Up @@ -1913,19 +1930,19 @@ static int b43_upload_initvals(struct b43_wldev *dev)
size_t count;
int err;

hdr = (const struct b43_fw_header *)(fw->initvals->data);
ivals = (const struct b43_iv *)(fw->initvals->data + hdr_len);
hdr = (const struct b43_fw_header *)(fw->initvals.data->data);
ivals = (const struct b43_iv *)(fw->initvals.data->data + hdr_len);
count = be32_to_cpu(hdr->size);
err = b43_write_initvals(dev, ivals, count,
fw->initvals->size - hdr_len);
fw->initvals.data->size - hdr_len);
if (err)
goto out;
if (fw->initvals_band) {
hdr = (const struct b43_fw_header *)(fw->initvals_band->data);
ivals = (const struct b43_iv *)(fw->initvals_band->data + hdr_len);
if (fw->initvals_band.data) {
hdr = (const struct b43_fw_header *)(fw->initvals_band.data->data);
ivals = (const struct b43_iv *)(fw->initvals_band.data->data + hdr_len);
count = be32_to_cpu(hdr->size);
err = b43_write_initvals(dev, ivals, count,
fw->initvals_band->size - hdr_len);
fw->initvals_band.data->size - hdr_len);
if (err)
goto out;
}
Expand Down

0 comments on commit 61cb5dd

Please sign in to comment.