brcmfmac: firmware: Allow per-board firmware binaries
authorLinus Walleij <linus.walleij@linaro.org>
Sun, 11 Jul 2021 23:16:59 +0000 (01:16 +0200)
committerKalle Valo <kvalo@codeaurora.org>
Sun, 1 Aug 2021 10:26:59 +0000 (13:26 +0300)
After some crashes in the 3D engine (!) on the Samsung GT-I8530
it turns out that the main firmware file can be device dependent,
something that was previously only handled for the NVRAM
parameter file.

Rewrite the code a bit so we can a per-board suffixed firmware
binary as well, if this does not exist we fall back to the
canonical firmware name.

Example: a 4330 device with the OF board compatible is
"samsung,gavini". We will first try
"brcmfmac4330-sdio.samsung,gavini.bin" then "brcmfmac4330-sdio.bin"
if that does not work.

Cc: phone-devel@vger.kernel.org
Cc: newbyte@disroot.org
Cc: Stephan Gerhold <stephan@gerhold.net>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/20210711231659.255479-1-linus.walleij@linaro.org
drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c

index d40104b..adfdfc6 100644 (file)
@@ -594,28 +594,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);
 }
 
@@ -660,6 +679,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 +697,20 @@ 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);
+               kfree(alt_path);
+       }
+       /* Else try canonical path */
+       if (ret) {
+               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);