summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c')
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c69
1 files changed, 56 insertions, 13 deletions
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
index d40104b8df55..0eb13e5df517 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
@@ -431,8 +431,6 @@ struct brcmf_fw {
void (*done)(struct device *dev, int err, struct brcmf_fw_request *req);
};
-static void brcmf_fw_request_done(const struct firmware *fw, void *ctx);
-
#ifdef CONFIG_EFI
/* In some cases the EFI-var stored nvram contains "ccode=ALL" or "ccode=XV"
* to specify "worldwide" compatible settings, but these 2 ccode-s do not work
@@ -594,28 +592,47 @@ static int brcmf_fw_complete_request(const struct firmware *fw,
return (cur->flags & BRCMF_FW_REQF_OPTIONAL) ? 0 : ret;
}
+static char *brcm_alt_fw_path(const char *path, const char *board_type)
+{
+ char alt_path[BRCMF_FW_NAME_LEN];
+ char suffix[5];
+
+ strscpy(alt_path, path, BRCMF_FW_NAME_LEN);
+ /* At least one character + suffix */
+ if (strlen(alt_path) < 5)
+ return NULL;
+
+ /* strip .txt or .bin at the end */
+ strscpy(suffix, alt_path + strlen(alt_path) - 4, 5);
+ alt_path[strlen(alt_path) - 4] = 0;
+ strlcat(alt_path, ".", BRCMF_FW_NAME_LEN);
+ strlcat(alt_path, board_type, BRCMF_FW_NAME_LEN);
+ strlcat(alt_path, suffix, BRCMF_FW_NAME_LEN);
+
+ return kstrdup(alt_path, GFP_KERNEL);
+}
+
static int brcmf_fw_request_firmware(const struct firmware **fw,
struct brcmf_fw *fwctx)
{
struct brcmf_fw_item *cur = &fwctx->req->items[fwctx->curpos];
int ret;
- /* nvram files are board-specific, first try a board-specific path */
+ /* Files can be board-specific, first try a board-specific path */
if (cur->type == BRCMF_FW_TYPE_NVRAM && fwctx->req->board_type) {
- char alt_path[BRCMF_FW_NAME_LEN];
+ char *alt_path;
- strlcpy(alt_path, cur->path, BRCMF_FW_NAME_LEN);
- /* strip .txt at the end */
- alt_path[strlen(alt_path) - 4] = 0;
- strlcat(alt_path, ".", BRCMF_FW_NAME_LEN);
- strlcat(alt_path, fwctx->req->board_type, BRCMF_FW_NAME_LEN);
- strlcat(alt_path, ".txt", BRCMF_FW_NAME_LEN);
+ alt_path = brcm_alt_fw_path(cur->path, fwctx->req->board_type);
+ if (!alt_path)
+ goto fallback;
ret = request_firmware(fw, alt_path, fwctx->dev);
+ kfree(alt_path);
if (ret == 0)
return ret;
}
+fallback:
return request_firmware(fw, cur->path, fwctx->dev);
}
@@ -639,6 +656,22 @@ static void brcmf_fw_request_done(const struct firmware *fw, void *ctx)
kfree(fwctx);
}
+static void brcmf_fw_request_done_alt_path(const struct firmware *fw, void *ctx)
+{
+ struct brcmf_fw *fwctx = ctx;
+ struct brcmf_fw_item *first = &fwctx->req->items[0];
+ int ret = 0;
+
+ /* Fall back to canonical path if board firmware not found */
+ if (!fw)
+ ret = request_firmware_nowait(THIS_MODULE, true, first->path,
+ fwctx->dev, GFP_KERNEL, fwctx,
+ brcmf_fw_request_done);
+
+ if (fw || ret < 0)
+ brcmf_fw_request_done(fw, ctx);
+}
+
static bool brcmf_fw_request_is_valid(struct brcmf_fw_request *req)
{
struct brcmf_fw_item *item;
@@ -660,6 +693,7 @@ int brcmf_fw_get_firmwares(struct device *dev, struct brcmf_fw_request *req,
{
struct brcmf_fw_item *first = &req->items[0];
struct brcmf_fw *fwctx;
+ char *alt_path;
int ret;
brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(dev));
@@ -677,9 +711,18 @@ int brcmf_fw_get_firmwares(struct device *dev, struct brcmf_fw_request *req,
fwctx->req = req;
fwctx->done = fw_cb;
- ret = request_firmware_nowait(THIS_MODULE, true, first->path,
- fwctx->dev, GFP_KERNEL, fwctx,
- brcmf_fw_request_done);
+ /* First try alternative board-specific path if any */
+ alt_path = brcm_alt_fw_path(first->path, fwctx->req->board_type);
+ if (alt_path) {
+ ret = request_firmware_nowait(THIS_MODULE, true, alt_path,
+ fwctx->dev, GFP_KERNEL, fwctx,
+ brcmf_fw_request_done_alt_path);
+ kfree(alt_path);
+ } else {
+ ret = request_firmware_nowait(THIS_MODULE, true, first->path,
+ fwctx->dev, GFP_KERNEL, fwctx,
+ brcmf_fw_request_done);
+ }
if (ret < 0)
brcmf_fw_request_done(NULL, fwctx);