brcmfmac: Add support for getting nvram contents from EFI variables
authorHans de Goede <hdegoede@redhat.com>
Thu, 11 Oct 2018 09:51:06 +0000 (11:51 +0200)
committerKalle Valo <kvalo@codeaurora.org>
Tue, 6 Nov 2018 16:51:40 +0000 (18:51 +0200)
Various X86 laptops with a SDIO attached brcmfmac wifi chip, store the
nvram contents in a special EFI variable. This commit adds support for
getting nvram directly from this EFI variable, without the user needing
to manually copy it.

This makes Wifi / Bluetooth work out of the box on these devices instead of
requiring manual setup.

This has been tested on the following models: Acer Iconia Tab8 w1-810,
Acer One 10, Asus T100CHI, Asus T100HA, Asus T100TA, Asus T200TA and a
Lenovo Mixx 2 8.

Tested-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c

index b38c4b4..965ae5c 100644 (file)
@@ -14,6 +14,7 @@
  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <linux/efi.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/device.h>
@@ -445,6 +446,51 @@ struct brcmf_fw {
 
 static void brcmf_fw_request_done(const struct firmware *fw, void *ctx);
 
+#ifdef CONFIG_EFI
+static u8 *brcmf_fw_nvram_from_efi(size_t *data_len_ret)
+{
+       const u16 name[] = { 'n', 'v', 'r', 'a', 'm', 0 };
+       struct efivar_entry *nvram_efivar;
+       unsigned long data_len = 0;
+       u8 *data = NULL;
+       int err;
+
+       nvram_efivar = kzalloc(sizeof(*nvram_efivar), GFP_KERNEL);
+       if (!nvram_efivar)
+               return NULL;
+
+       memcpy(&nvram_efivar->var.VariableName, name, sizeof(name));
+       nvram_efivar->var.VendorGuid = EFI_GUID(0x74b00bd9, 0x805a, 0x4d61,
+                                               0xb5, 0x1f, 0x43, 0x26,
+                                               0x81, 0x23, 0xd1, 0x13);
+
+       err = efivar_entry_size(nvram_efivar, &data_len);
+       if (err)
+               goto fail;
+
+       data = kmalloc(data_len, GFP_KERNEL);
+       if (!data)
+               goto fail;
+
+       err = efivar_entry_get(nvram_efivar, NULL, &data_len, data);
+       if (err)
+               goto fail;
+
+       brcmf_info("Using nvram EFI variable\n");
+
+       kfree(nvram_efivar);
+       *data_len_ret = data_len;
+       return data;
+
+fail:
+       kfree(data);
+       kfree(nvram_efivar);
+       return NULL;
+}
+#else
+static u8 *brcmf_fw_nvram_from_efi(size_t *data_len) { return NULL; }
+#endif
+
 static void brcmf_fw_free_request(struct brcmf_fw_request *req)
 {
        struct brcmf_fw_item *item;
@@ -463,11 +509,12 @@ static int brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx)
 {
        struct brcmf_fw *fwctx = ctx;
        struct brcmf_fw_item *cur;
+       bool free_bcm47xx_nvram = false;
+       bool kfree_nvram = false;
        u32 nvram_length = 0;
        void *nvram = NULL;
        u8 *data = NULL;
        size_t data_len;
-       bool raw_nvram;
 
        brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev));
 
@@ -476,12 +523,13 @@ static int brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx)
        if (fw && fw->data) {
                data = (u8 *)fw->data;
                data_len = fw->size;
-               raw_nvram = false;
        } else {
-               data = bcm47xx_nvram_get_contents(&data_len);
-               if (!data && !(cur->flags & BRCMF_FW_REQF_OPTIONAL))
+               if ((data = bcm47xx_nvram_get_contents(&data_len)))
+                       free_bcm47xx_nvram = true;
+               else if ((data = brcmf_fw_nvram_from_efi(&data_len)))
+                       kfree_nvram = true;
+               else if (!(cur->flags & BRCMF_FW_REQF_OPTIONAL))
                        goto fail;
-               raw_nvram = true;
        }
 
        if (data)
@@ -489,8 +537,11 @@ static int brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx)
                                             fwctx->req->domain_nr,
                                             fwctx->req->bus_nr);
 
-       if (raw_nvram)
+       if (free_bcm47xx_nvram)
                bcm47xx_nvram_release_contents(data);
+       if (kfree_nvram)
+               kfree(data);
+
        release_firmware(fw);
        if (!nvram && !(cur->flags & BRCMF_FW_REQF_OPTIONAL))
                goto fail;