diff options
Diffstat (limited to 'drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c')
-rw-r--r-- | drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c | 69 |
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); |