diff mbox series

[v4,1/2] brcmfmac: Add support for getting nvram contents from EFI variables

Message ID 20181011095107.17073-1-hdegoede@redhat.com (mailing list archive)
State Accepted
Commit ce2e6db554fad444fa0b3904fc3015336e0ef765
Delegated to: Kalle Valo
Headers show
Series [v4,1/2] brcmfmac: Add support for getting nvram contents from EFI variables | expand

Commit Message

Hans de Goede Oct. 11, 2018, 9:51 a.m. UTC
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>
---
Changes in v4:
-Drop unused path argument from brcmf_fw_nvram_from_efi(), this makes its
 prototype the same again as the empty implementation which gets used when
 CONFIG_EFI is not defined, fixing compilation without CONFIG_EFI

Changes in v3:
-Drop ccode fixup code (to be re-added in a separate commit), so that we
 can get the main EFI nvram support merged while we figure out the ccode
 handling

Changes in v2:
-Stop testing for asus in the dmi sys_vendor string at least the Toshiba
 Encore uses the nvram efivar variable too
-Use a table mapping the firmare name to the correct ccode value (XV / X2) to
 use for the worldwide regulatory domain for that firmware, assuming the
 firmware from linux-firmware is used. This fixes the T200TA and T100HA not
 seeing 5GHz networks
-Not only fixup ccode=ALL, but also ccode=XV, this is necessary since the
 T100HA nvram efivar containts ccode=XV, but the firmware from Linux firmware
 needs ccode=X2 for proper operation
---
 .../broadcom/brcm80211/brcmfmac/firmware.c    | 63 +++++++++++++++++--
 1 file changed, 57 insertions(+), 6 deletions(-)

Comments

Kalle Valo Nov. 6, 2018, 4:52 p.m. UTC | #1
Hans de Goede <hdegoede@redhat.com> wrote:

> 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>

2 patches applied to wireless-drivers-next.git, thanks.

ce2e6db554fa brcmfmac: Add support for getting nvram contents from EFI variables
29ec3394f0bd brcmfmac: Fix ccode from EFI nvram when necessary
diff mbox series

Patch

diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
index b38c4b40b235..965ae5c24c2d 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
@@ -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;