b43: Automatically probe for opensource firmware
First probe for proprietary firmware and then probe for opensource firmware.
This way around it's a win-win situation.
1) If proprietary fw is available, it will work.
2) If opensource firmware is available, but no proprietary (Distros can only ship open fw)
it might work.
3) If both open and proprietary are available, it will work, because it selects
the proprietary. We currently don't prefer the open fw in this case, because it doesn't
work on all devices. It would introduce a regression otherwise.
The remaining FIXMEs in this patch are harmless, because they only matter on multiband
devices, which are not implemented yet anyway.
Signed-off-by: Michael Buesch <mb@bu3sch.de>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c
index 675a73a..cbb3d45 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -1934,7 +1934,7 @@
return ret;
}
-static void do_release_fw(struct b43_firmware_file *fw)
+void b43_do_release_fw(struct b43_firmware_file *fw)
{
release_firmware(fw->data);
fw->data = NULL;
@@ -1943,10 +1943,10 @@
static void b43_release_firmware(struct b43_wldev *dev)
{
- 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);
+ b43_do_release_fw(&dev->fw.ucode);
+ b43_do_release_fw(&dev->fw.pcm);
+ b43_do_release_fw(&dev->fw.initvals);
+ b43_do_release_fw(&dev->fw.initvals_band);
}
static void b43_print_fw_helptext(struct b43_wl *wl, bool error)
@@ -1963,12 +1963,10 @@
b43warn(wl, text);
}
-static int do_request_fw(struct b43_wldev *dev,
- const char *name,
- struct b43_firmware_file *fw,
- bool silent)
+int b43_do_request_fw(struct b43_request_fw_context *ctx,
+ const char *name,
+ struct b43_firmware_file *fw)
{
- char path[sizeof(modparam_fwpostfix) + 32];
const struct firmware *blob;
struct b43_fw_header *hdr;
u32 size;
@@ -1976,29 +1974,49 @@
if (!name) {
/* Don't fetch anything. Free possibly cached firmware. */
- do_release_fw(fw);
+ /* FIXME: We should probably keep it anyway, to save some headache
+ * on suspend/resume with multiband devices. */
+ b43_do_release_fw(fw);
return 0;
}
if (fw->filename) {
- if (strcmp(fw->filename, name) == 0)
+ if ((fw->type == ctx->req_type) &&
+ (strcmp(fw->filename, name) == 0))
return 0; /* Already have this fw. */
/* Free the cached firmware first. */
- do_release_fw(fw);
+ /* FIXME: We should probably do this later after we successfully
+ * got the new fw. This could reduce headache with multiband devices.
+ * We could also redesign this to cache the firmware for all possible
+ * bands all the time. */
+ b43_do_release_fw(fw);
}
- snprintf(path, ARRAY_SIZE(path),
- "b43%s/%s.fw",
- modparam_fwpostfix, name);
- err = request_firmware(&blob, path, dev->dev->dev);
+ switch (ctx->req_type) {
+ case B43_FWTYPE_PROPRIETARY:
+ snprintf(ctx->fwname, sizeof(ctx->fwname),
+ "b43%s/%s.fw",
+ modparam_fwpostfix, name);
+ break;
+ case B43_FWTYPE_OPENSOURCE:
+ snprintf(ctx->fwname, sizeof(ctx->fwname),
+ "b43-open%s/%s.fw",
+ modparam_fwpostfix, name);
+ break;
+ default:
+ B43_WARN_ON(1);
+ return -ENOSYS;
+ }
+ err = request_firmware(&blob, ctx->fwname, ctx->dev->dev->dev);
if (err == -ENOENT) {
- if (!silent) {
- b43err(dev->wl, "Firmware file \"%s\" not found\n",
- path);
- }
+ snprintf(ctx->errors[ctx->req_type],
+ sizeof(ctx->errors[ctx->req_type]),
+ "Firmware file \"%s\" not found\n", ctx->fwname);
return err;
} else if (err) {
- b43err(dev->wl, "Firmware file \"%s\" request failed (err=%d)\n",
- path, err);
+ snprintf(ctx->errors[ctx->req_type],
+ sizeof(ctx->errors[ctx->req_type]),
+ "Firmware file \"%s\" request failed (err=%d)\n",
+ ctx->fwname, err);
return err;
}
if (blob->size < sizeof(struct b43_fw_header))
@@ -2021,20 +2039,24 @@
fw->data = blob;
fw->filename = name;
+ fw->type = ctx->req_type;
return 0;
err_format:
- b43err(dev->wl, "Firmware file \"%s\" format error.\n", path);
+ snprintf(ctx->errors[ctx->req_type],
+ sizeof(ctx->errors[ctx->req_type]),
+ "Firmware file \"%s\" format error.\n", ctx->fwname);
release_firmware(blob);
return -EPROTO;
}
-static int b43_request_firmware(struct b43_wldev *dev)
+static int b43_try_request_fw(struct b43_request_fw_context *ctx)
{
- struct b43_firmware *fw = &dev->fw;
- const u8 rev = dev->dev->id.revision;
+ struct b43_wldev *dev = ctx->dev;
+ struct b43_firmware *fw = &ctx->dev->fw;
+ const u8 rev = ctx->dev->dev->id.revision;
const char *filename;
u32 tmshigh;
int err;
@@ -2049,7 +2071,7 @@
filename = "ucode13";
else
goto err_no_ucode;
- err = do_request_fw(dev, filename, &fw->ucode, 0);
+ err = b43_do_request_fw(ctx, filename, &fw->ucode);
if (err)
goto err_load;
@@ -2061,7 +2083,7 @@
else
goto err_no_pcm;
fw->pcm_request_failed = 0;
- err = do_request_fw(dev, filename, &fw->pcm, 1);
+ err = b43_do_request_fw(ctx, filename, &fw->pcm);
if (err == -ENOENT) {
/* We did not find a PCM file? Not fatal, but
* core rev <= 10 must do without hwcrypto then. */
@@ -2097,7 +2119,7 @@
default:
goto err_no_initvals;
}
- err = do_request_fw(dev, filename, &fw->initvals, 0);
+ err = b43_do_request_fw(ctx, filename, &fw->initvals);
if (err)
goto err_load;
@@ -2131,30 +2153,34 @@
default:
goto err_no_initvals;
}
- err = do_request_fw(dev, filename, &fw->initvals_band, 0);
+ err = b43_do_request_fw(ctx, filename, &fw->initvals_band);
if (err)
goto err_load;
return 0;
-err_load:
- b43_print_fw_helptext(dev->wl, 1);
- goto error;
-
err_no_ucode:
- err = -ENODEV;
- b43err(dev->wl, "No microcode available for core rev %u\n", rev);
+ err = ctx->fatal_failure = -EOPNOTSUPP;
+ b43err(dev->wl, "The driver does not know which firmware (ucode) "
+ "is required for your device (wl-core rev %u)\n", rev);
goto error;
err_no_pcm:
- err = -ENODEV;
- b43err(dev->wl, "No PCM available for core rev %u\n", rev);
+ err = ctx->fatal_failure = -EOPNOTSUPP;
+ b43err(dev->wl, "The driver does not know which firmware (PCM) "
+ "is required for your device (wl-core rev %u)\n", rev);
goto error;
err_no_initvals:
- err = -ENODEV;
- b43err(dev->wl, "No Initial Values firmware file for PHY %u, "
- "core rev %u\n", dev->phy.type, rev);
+ err = ctx->fatal_failure = -EOPNOTSUPP;
+ b43err(dev->wl, "The driver does not know which firmware (initvals) "
+ "is required for your device (wl-core rev %u)\n", rev);
+ goto error;
+
+err_load:
+ /* We failed to load this firmware image. The error message
+ * already is in ctx->errors. Return and let our caller decide
+ * what to do. */
goto error;
error:
@@ -2162,6 +2188,48 @@
return err;
}
+static int b43_request_firmware(struct b43_wldev *dev)
+{
+ struct b43_request_fw_context *ctx;
+ unsigned int i;
+ int err;
+ const char *errmsg;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ ctx->dev = dev;
+
+ ctx->req_type = B43_FWTYPE_PROPRIETARY;
+ err = b43_try_request_fw(ctx);
+ if (!err)
+ goto out; /* Successfully loaded it. */
+ err = ctx->fatal_failure;
+ if (err)
+ goto out;
+
+ ctx->req_type = B43_FWTYPE_OPENSOURCE;
+ err = b43_try_request_fw(ctx);
+ if (!err)
+ goto out; /* Successfully loaded it. */
+ err = ctx->fatal_failure;
+ if (err)
+ goto out;
+
+ /* Could not find a usable firmware. Print the errors. */
+ for (i = 0; i < B43_NR_FWTYPES; i++) {
+ errmsg = ctx->errors[i];
+ if (strlen(errmsg))
+ b43err(dev->wl, errmsg);
+ }
+ b43_print_fw_helptext(dev->wl, 1);
+ err = -ENOENT;
+
+out:
+ kfree(ctx);
+ return err;
+}
+
static int b43_upload_microcode(struct b43_wldev *dev)
{
const size_t hdr_len = sizeof(struct b43_fw_header);