* [PATCH 01/13] wifi: brcmfmac: pcie: Report the detected chip ID to the bus
2026-09-25 13:43 [PATCH 00/13] wifi: brcmfmac: Add BCM4388 support Michael Reeves via B4 Relay
@ 2026-09-25 13:43 ` Michael Reeves via B4 Relay
2026-09-25 13:43 ` [PATCH 02/13] wifi: brcmfmac: Match complete firmware capability names Michael Reeves via B4 Relay
` (11 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Michael Reeves via B4 Relay @ 2026-09-25 13:43 UTC (permalink / raw)
To: Arend van Spriel, Pieter-Paul Giesberts, Franky Lin (Zhenhui),
Mathy Vanhoef, Kalle Valo, Kees Cook, Gustavo A. R. Silva
Cc: linux-wireless, brcm80211, brcm80211-dev-list.pdl, asahi,
Hector Martin, Daniel Berlin, Janne Grunau, linux-kernel,
Hante Meuleman, linux-hardening, Michael Reeves
From: Michael Reeves <michael.reeves077@gmail.com>
The devinfo->coreid field is never assigned, so bus->chip remains zero
and chip-specific feature checks cannot match. Assign bus->chip from
devinfo->ci->chip, which contains the detected chip ID.
Signed-off-by: Michael Reeves <michael.reeves077@gmail.com>
---
drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
index 55f4d7b970..5d84526c96 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
@@ -342,7 +342,6 @@ struct brcmf_pciedev_info {
u32 ram_base;
u32 ram_size;
struct brcmf_chip *ci;
- u32 coreid;
struct brcmf_pcie_shared_info shared;
wait_queue_head_t mbdata_resp_wait;
bool mbdata_completed;
@@ -2518,7 +2517,7 @@ brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
bus->bus_priv.pcie = pcie_bus_dev;
bus->ops = &brcmf_pcie_bus_ops;
bus->proto_type = BRCMF_PROTO_MSGBUF;
- bus->chip = devinfo->coreid;
+ bus->chip = devinfo->ci->chip;
bus->wowl_supported = pci_pme_capable(pdev, PCI_D3hot);
bus->fwvid = drvdata[id->driver_data].vendor;
devinfo->fwseed = drvdata[id->driver_data].fw_seed;
--
2.55.0
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH 02/13] wifi: brcmfmac: Match complete firmware capability names
2026-09-25 13:43 [PATCH 00/13] wifi: brcmfmac: Add BCM4388 support Michael Reeves via B4 Relay
2026-09-25 13:43 ` [PATCH 01/13] wifi: brcmfmac: pcie: Report the detected chip ID to the bus Michael Reeves via B4 Relay
@ 2026-09-25 13:43 ` Michael Reeves via B4 Relay
2026-09-25 13:43 ` [PATCH 03/13] wifi: brcmfmac: Fall back to RSSI-only default join preferences Michael Reeves via B4 Relay
` (10 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Michael Reeves via B4 Relay @ 2026-09-25 13:43 UTC (permalink / raw)
To: Arend van Spriel, Pieter-Paul Giesberts, Franky Lin (Zhenhui),
Mathy Vanhoef, Kalle Valo, Kees Cook, Gustavo A. R. Silva
Cc: linux-wireless, brcm80211, brcm80211-dev-list.pdl, asahi,
Hector Martin, Daniel Berlin, Janne Grunau, linux-kernel,
Hante Meuleman, linux-hardening, Michael Reeves
From: Michael Reeves <michael.reeves077@gmail.com>
BCM4388 firmware advertises "hp2p" without advertising "p2p". Substring
matching incorrectly treats "hp2p" as P2P support, causing userspace to
attempt Wi-Fi Direct interface creation that the firmware rejects.
Match complete whitespace-separated capability names instead. Remove the
trailing-space workarounds for SAE and FBT and reserve a terminating
byte outside the firmware response buffer.
Fixes: ec64241c9fd2 ("brcmfmac: obtain feature info using 'cap' firmware command")
Signed-off-by: Michael Reeves <michael.reeves077@gmail.com>
---
.../wireless/broadcom/brcm80211/brcmfmac/feature.c | 20 +++++++++++++-------
1 file changed, 13 insertions(+), 7 deletions(-)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
index cc19e5e7eb..a3c221117d 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
@@ -42,10 +42,10 @@ static const struct brcmf_feat_fwcap brcmf_fwcap_map[] = {
{ BRCMF_FEAT_MONITOR_FLAG, "rtap" },
{ BRCMF_FEAT_MONITOR_FMT_RADIOTAP, "rtap" },
{ BRCMF_FEAT_DOT11H, "802.11h" },
- { BRCMF_FEAT_SAE, "sae " },
+ { BRCMF_FEAT_SAE, "sae" },
{ BRCMF_FEAT_FWAUTH, "idauth" },
{ BRCMF_FEAT_SAE_EXT, "sae_ext" },
- { BRCMF_FEAT_FBT, "fbt " },
+ { BRCMF_FEAT_FBT, "fbt" },
{ BRCMF_FEAT_OKC, "okc" },
};
@@ -230,11 +230,12 @@ static void brcmf_feat_iovar_data_set(struct brcmf_if *ifp,
static void brcmf_feat_firmware_capabilities(struct brcmf_if *ifp)
{
struct brcmf_pub *drvr = ifp->drvr;
- char caps[MAX_CAPS_BUFFER_SIZE];
+ char caps[MAX_CAPS_BUFFER_SIZE + 1] = {};
+ char *cap, *next = caps;
enum brcmf_feat_id id;
int i, err;
- err = brcmf_fil_iovar_data_get(ifp, "cap", caps, sizeof(caps));
+ err = brcmf_fil_iovar_data_get(ifp, "cap", caps, sizeof(caps) - 1);
if (err) {
bphy_err(drvr, "could not get firmware cap (%d)\n", err);
return;
@@ -242,12 +243,17 @@ static void brcmf_feat_firmware_capabilities(struct brcmf_if *ifp)
brcmf_dbg(INFO, "[ %s]\n", caps);
- for (i = 0; i < ARRAY_SIZE(brcmf_fwcap_map); i++) {
- if (strnstr(caps, brcmf_fwcap_map[i].fwcap_id, sizeof(caps))) {
+ while ((cap = strsep(&next, " \t\r\n")) != NULL) {
+ if (!*cap)
+ continue;
+
+ for (i = 0; i < ARRAY_SIZE(brcmf_fwcap_map); i++) {
+ if (strcmp(cap, brcmf_fwcap_map[i].fwcap_id))
+ continue;
id = brcmf_fwcap_map[i].feature;
brcmf_dbg(INFO, "enabling feature: %s\n",
brcmf_feat_names[id]);
- ifp->drvr->feat_flags |= BIT(id);
+ drvr->feat_flags |= BIT(id);
}
}
}
--
2.55.0
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH 03/13] wifi: brcmfmac: Fall back to RSSI-only default join preferences
2026-09-25 13:43 [PATCH 00/13] wifi: brcmfmac: Add BCM4388 support Michael Reeves via B4 Relay
2026-09-25 13:43 ` [PATCH 01/13] wifi: brcmfmac: pcie: Report the detected chip ID to the bus Michael Reeves via B4 Relay
2026-09-25 13:43 ` [PATCH 02/13] wifi: brcmfmac: Match complete firmware capability names Michael Reeves via B4 Relay
@ 2026-09-25 13:43 ` Michael Reeves via B4 Relay
2026-09-25 13:43 ` [PATCH 04/13] wifi: brcmfmac: Support newer PCIe core startup and interrupts Michael Reeves via B4 Relay
` (9 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Michael Reeves via B4 Relay @ 2026-09-25 13:43 UTC (permalink / raw)
To: Arend van Spriel, Pieter-Paul Giesberts, Franky Lin (Zhenhui),
Mathy Vanhoef, Kalle Valo, Kees Cook, Gustavo A. R. Silva
Cc: linux-wireless, brcm80211, brcm80211-dev-list.pdl, asahi,
Hector Martin, Daniel Berlin, Janne Grunau, linux-kernel,
Hante Meuleman, linux-hardening, Michael Reeves
From: Michael Reeves <michael.reeves077@gmail.com>
BCM4388 firmware rejects the default join preference containing a 5 GHz
RSSI boost with -EBADE, but accepts an RSSI-only preference.
Retry with RSSI-only selection only after -EBADE. Log other errors
without retrying.
Signed-off-by: Michael Reeves <michael.reeves077@gmail.com>
---
drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
index b6027d83e6..2a37b42ca9 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
@@ -95,6 +95,10 @@ void brcmf_c_set_joinpref_default(struct brcmf_if *ifp)
join_pref_params[1].band = 0;
err = brcmf_fil_iovar_data_set(ifp, "join_pref", join_pref_params,
sizeof(join_pref_params));
+ if (err == -EBADE)
+ err = brcmf_fil_iovar_data_set(ifp, "join_pref",
+ &join_pref_params[1],
+ sizeof(join_pref_params[1]));
if (err)
bphy_err(drvr, "Set join_pref error (%d)\n", err);
}
--
2.55.0
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH 04/13] wifi: brcmfmac: Support newer PCIe core startup and interrupts
2026-09-25 13:43 [PATCH 00/13] wifi: brcmfmac: Add BCM4388 support Michael Reeves via B4 Relay
` (2 preceding siblings ...)
2026-09-25 13:43 ` [PATCH 03/13] wifi: brcmfmac: Fall back to RSSI-only default join preferences Michael Reeves via B4 Relay
@ 2026-09-25 13:43 ` Michael Reeves via B4 Relay
2026-09-25 13:43 ` [PATCH 05/13] wifi: brcmfmac: Negotiate PCIe mailbox transport with firmware Michael Reeves via B4 Relay
` (8 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Michael Reeves via B4 Relay @ 2026-09-25 13:43 UTC (permalink / raw)
To: Arend van Spriel, Pieter-Paul Giesberts, Franky Lin (Zhenhui),
Mathy Vanhoef, Kalle Valo, Kees Cook, Gustavo A. R. Silva
Cc: linux-wireless, brcm80211, brcm80211-dev-list.pdl, asahi,
Hector Martin, Daniel Berlin, Janne Grunau, linux-kernel,
Hante Meuleman, linux-hardening, Michael Reeves
From: Hector Martin <marcan@marcan.st>
Newer Broadcom chips, such as BCM4388, require changes to PCIe register
access, watchdog reset, and RAM size detection. PCIe core revision 64
and higher uses a different BAR0 register window. ChipCommon revision 65
and higher requires a different watchdog reset sequence.
Update register access and reset for these revisions. Decode the SYSMEM
bank counts according to the core revision and exclude ROM banks from
RAM size calculation. Disable all detected D11 cores during CA7 setup
and select H2D doorbells based on the firmware DAR flag.
In MSI mode, firmware can clear the mailbox interrupt status before the
host reads it. Requiring a nonzero status can therefore discard valid
interrupts. Process ring events without that requirement when MSI is
enabled. Use release/acquire ordering on irq_ready to publish the
initialized rings before the interrupt thread accesses them.
Signed-off-by: Hector Martin <marcan@marcan.st>
Co-developed-by: Michael Reeves <michael.reeves077@gmail.com>
Signed-off-by: Michael Reeves <michael.reeves077@gmail.com>
---
.../wireless/broadcom/brcm80211/brcmfmac/chip.c | 35 +++-
.../wireless/broadcom/brcm80211/brcmfmac/pcie.c | 197 ++++++++++++++++-----
.../broadcom/brcm80211/include/chipcommon.h | 5 +
3 files changed, 183 insertions(+), 54 deletions(-)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
index 4adc0d0e42..253ab45c33 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
@@ -162,6 +162,15 @@ struct sbconfig {
#define SRCI_SRBSZ_SHIFT 0
#define SR_BSZ_BASE 14
+#define SYSMEM_SRCI_ROMNB_MASK 0x3e0
+#define SYSMEM_SRCI_ROMNB_SHIFT 5
+#define SYSMEM_SRCI_SRNB_MASK 0x1f
+#define SYSMEM_SRCI_SRNB_SHIFT 0
+#define SYSMEM_SRCI_NEW_ROMNB_MASK 0xff000000
+#define SYSMEM_SRCI_NEW_ROMNB_SHIFT 24
+#define SYSMEM_SRCI_NEW_SRNB_MASK 0x00ff0000
+#define SYSMEM_SRCI_NEW_SRNB_SHIFT 16
+
struct sbsocramregs {
u32 coreinfo;
u32 bwalloc;
@@ -659,6 +668,7 @@ static u32 brcmf_chip_sysmem_ramsize(struct brcmf_core_priv *sysmem)
u32 memsize = 0;
u32 coreinfo;
u32 idx;
+ u32 nrb;
u32 nb;
u32 banksize;
@@ -666,10 +676,20 @@ static u32 brcmf_chip_sysmem_ramsize(struct brcmf_core_priv *sysmem)
brcmf_chip_resetcore(&sysmem->pub, 0, 0, 0);
coreinfo = brcmf_chip_core_read32(sysmem, SYSMEMREGOFFS(coreinfo));
- nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT;
+ if (sysmem->pub.rev >= 12) {
+ nrb = (coreinfo & SYSMEM_SRCI_NEW_ROMNB_MASK) >>
+ SYSMEM_SRCI_NEW_ROMNB_SHIFT;
+ nb = (coreinfo & SYSMEM_SRCI_NEW_SRNB_MASK) >>
+ SYSMEM_SRCI_NEW_SRNB_SHIFT;
+ } else {
+ nrb = (coreinfo & SYSMEM_SRCI_ROMNB_MASK) >>
+ SYSMEM_SRCI_ROMNB_SHIFT;
+ nb = (coreinfo & SYSMEM_SRCI_SRNB_MASK) >>
+ SYSMEM_SRCI_SRNB_SHIFT;
+ }
for (idx = 0; idx < nb; idx++) {
- brcmf_chip_socram_banksize(sysmem, idx, &banksize);
+ brcmf_chip_socram_banksize(sysmem, idx + nrb, &banksize);
memsize += banksize;
}
@@ -1353,14 +1373,15 @@ static inline void
brcmf_chip_ca7_set_passive(struct brcmf_chip_priv *chip)
{
struct brcmf_core *core;
+ int i;
brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CA7);
- core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211);
- brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET |
- D11_BCMA_IOCTL_PHYCLOCKEN,
- D11_BCMA_IOCTL_PHYCLOCKEN,
- D11_BCMA_IOCTL_PHYCLOCKEN);
+ /* Leave all D11 cores disabled for the firmware to enable. */
+ for (i = 0; (core = brcmf_chip_get_d11core(&chip->pub, i)); i++)
+ brcmf_chip_coredisable(core, D11_BCMA_IOCTL_PHYRESET |
+ D11_BCMA_IOCTL_PHYCLOCKEN,
+ D11_BCMA_IOCTL_PHYCLOCKEN);
}
static bool brcmf_chip_ca7_set_active(struct brcmf_chip_priv *chip, u32 rstvec)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
index 5d84526c96..5917ee5fd7 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
@@ -220,6 +220,8 @@ static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = {
#define BRCMF_PCIE_SHARED_DMA_INDEX 0x10000
#define BRCMF_PCIE_SHARED_DMA_2B_IDX 0x100000
#define BRCMF_PCIE_SHARED_HOSTRDY_DB1 0x10000000
+#define BRCMF_PCIE_SHARED_DAR 0x80000000
+
#define BRCMF_PCIE_FLAGS_HTOD_SPLIT 0x4000
#define BRCMF_PCIE_FLAGS_DTOH_SPLIT 0x8000
@@ -280,6 +282,7 @@ static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = {
#define BRCMF_PCIE_CFGREG_PML1_SUB_CTRL1 0x248
#define BRCMF_PCIE_CFGREG_REG_BAR2_CONFIG 0x4E0
#define BRCMF_PCIE_CFGREG_REG_BAR3_CONFIG 0x4F4
+#define BRCMF_PCIE_CFGREG_TLCNTRL_5 0x814
#define BRCMF_PCIE_LINK_STATUS_CTRL_ASPM_ENAB 3
/* Magic number at a magic location to find RAM size */
@@ -346,6 +349,8 @@ struct brcmf_pciedev_info {
wait_queue_head_t mbdata_resp_wait;
bool mbdata_completed;
bool irq_allocated;
+ bool irq_ready;
+ bool have_msi;
bool wowl_enabled;
u8 dma_idx_sz;
void *idxbuf;
@@ -432,8 +437,6 @@ struct brcmf_pcie_reginfo {
u32 intmask;
u32 mailboxint;
u32 mailboxmask;
- u32 h2d_mailbox_0;
- u32 h2d_mailbox_1;
u32 int_d2h_db;
u32 int_fn0;
};
@@ -442,8 +445,6 @@ static const struct brcmf_pcie_reginfo brcmf_reginfo_default = {
.intmask = BRCMF_PCIE_PCIE2REG_INTMASK,
.mailboxint = BRCMF_PCIE_PCIE2REG_MAILBOXINT,
.mailboxmask = BRCMF_PCIE_PCIE2REG_MAILBOXMASK,
- .h2d_mailbox_0 = BRCMF_PCIE_PCIE2REG_H2D_MAILBOX_0,
- .h2d_mailbox_1 = BRCMF_PCIE_PCIE2REG_H2D_MAILBOX_1,
.int_d2h_db = BRCMF_PCIE_MB_INT_D2H_DB,
.int_fn0 = BRCMF_PCIE_MB_INT_FN0,
};
@@ -452,8 +453,6 @@ static const struct brcmf_pcie_reginfo brcmf_reginfo_64 = {
.intmask = BRCMF_PCIE_64_PCIE2REG_INTMASK,
.mailboxint = BRCMF_PCIE_64_PCIE2REG_MAILBOXINT,
.mailboxmask = BRCMF_PCIE_64_PCIE2REG_MAILBOXMASK,
- .h2d_mailbox_0 = BRCMF_PCIE_64_PCIE2REG_H2D_MAILBOX_0,
- .h2d_mailbox_1 = BRCMF_PCIE_64_PCIE2REG_H2D_MAILBOX_1,
.int_d2h_db = BRCMF_PCIE_64_MB_INT_D2H_DB,
.int_fn0 = 0,
};
@@ -492,6 +491,46 @@ brcmf_pcie_write_reg32(struct brcmf_pciedev_info *devinfo, u32 reg_offset,
iowrite32(value, address);
}
+static bool
+brcmf_pcie_use_pcie2_bar0_window(struct brcmf_pciedev_info *devinfo)
+{
+ struct brcmf_core *core;
+
+ if (!devinfo->ci)
+ return false;
+
+ core = brcmf_chip_get_core(devinfo->ci, BCMA_CORE_PCIE2);
+ return core && core->rev >= 64;
+}
+
+static u32
+brcmf_pcie_pcie2_reg_offset(struct brcmf_pciedev_info *devinfo,
+ u32 reg_offset)
+{
+ if (brcmf_pcie_use_pcie2_bar0_window(devinfo))
+ return BRCMF_PCIE_BARO_PCIE_ENUM_OFFSET + reg_offset;
+
+ return reg_offset;
+}
+
+static u32
+brcmf_pcie_read_pcie32(struct brcmf_pciedev_info *devinfo, u32 reg_offset)
+{
+ return brcmf_pcie_read_reg32(devinfo,
+ brcmf_pcie_pcie2_reg_offset(devinfo,
+ reg_offset));
+}
+
+static void
+brcmf_pcie_write_pcie32(struct brcmf_pciedev_info *devinfo, u32 reg_offset,
+ u32 value)
+{
+ brcmf_pcie_write_reg32(devinfo,
+ brcmf_pcie_pcie2_reg_offset(devinfo,
+ reg_offset),
+ value);
+}
+
static u8
brcmf_pcie_read_tcm8(struct brcmf_pciedev_info *devinfo, u32 mem_offset)
@@ -685,8 +724,29 @@ static void brcmf_pcie_reset_device(struct brcmf_pciedev_info *devinfo)
/* Watchdog reset */
brcmf_pcie_select_core(devinfo, BCMA_CORE_CHIPCOMMON);
- WRITECC32(devinfo, watchdog, 4);
- msleep(100);
+ core = brcmf_chip_get_chipcommon(devinfo->ci);
+ if (core->rev >= 65) {
+ u32 mask = CC_WD_SSRESET_PCIE_F0_EN;
+
+ core = brcmf_chip_get_core(devinfo->ci, BCMA_CORE_PCIE2);
+ if (core->rev < 66)
+ mask |= CC_WD_SSRESET_PCIE_ALL_FN_EN;
+
+ val = READCC32(devinfo, watchdog);
+ val &= ~CC_WD_ENABLE_MASK;
+ val |= mask;
+ WRITECC32(devinfo, watchdog, val);
+ val &= ~CC_WD_COUNTER_MASK;
+ val |= 4;
+ WRITECC32(devinfo, watchdog, val);
+ fsleep(10000);
+ val = READCC32(devinfo, intstatus);
+ val |= mask;
+ WRITECC32(devinfo, intstatus, val);
+ } else {
+ WRITECC32(devinfo, watchdog, 4);
+ msleep(100);
+ }
/* Restore ASPM */
brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
@@ -696,16 +756,13 @@ static void brcmf_pcie_reset_device(struct brcmf_pciedev_info *devinfo)
core = brcmf_chip_get_core(devinfo->ci, BCMA_CORE_PCIE2);
if (core->rev <= 13) {
for (i = 0; i < ARRAY_SIZE(cfg_offset); i++) {
- brcmf_pcie_write_reg32(devinfo,
- BRCMF_PCIE_PCIE2REG_CONFIGADDR,
- cfg_offset[i]);
- val = brcmf_pcie_read_reg32(devinfo,
- BRCMF_PCIE_PCIE2REG_CONFIGDATA);
+ brcmf_pcie_write_pcie32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR,
+ cfg_offset[i]);
+ val = brcmf_pcie_read_pcie32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA);
brcmf_dbg(PCIE, "config offset 0x%04x, value 0x%04x\n",
cfg_offset[i], val);
- brcmf_pcie_write_reg32(devinfo,
- BRCMF_PCIE_PCIE2REG_CONFIGDATA,
- val);
+ brcmf_pcie_write_pcie32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA,
+ val);
}
}
}
@@ -717,9 +774,12 @@ static void brcmf_pcie_attach(struct brcmf_pciedev_info *devinfo)
/* BAR1 window may not be sized properly */
brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR, 0x4e0);
- config = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA);
- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA, config);
+ brcmf_pcie_write_pcie32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR,
+ 0x4e0);
+ config = brcmf_pcie_read_pcie32(devinfo,
+ BRCMF_PCIE_PCIE2REG_CONFIGDATA);
+ brcmf_pcie_write_pcie32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA,
+ config);
device_wakeup_enable(&devinfo->pdev->dev);
}
@@ -738,6 +798,21 @@ static int brcmf_pcie_enter_download_state(struct brcmf_pciedev_info *devinfo)
brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKPDA,
0);
}
+ if (brcmf_pcie_use_pcie2_bar0_window(devinfo)) {
+ brcmf_pcie_write_pcie32(devinfo,
+ devinfo->reginfo->mailboxmask, 0);
+ brcmf_pcie_write_pcie32(devinfo,
+ devinfo->reginfo->mailboxint,
+ 0xffffffff);
+ pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_INTMASK,
+ 0);
+ brcmf_pcie_write_pcie32(devinfo,
+ BRCMF_PCIE_PCIE2REG_CONFIGADDR,
+ BRCMF_PCIE_CFGREG_TLCNTRL_5);
+ brcmf_pcie_write_pcie32(devinfo,
+ BRCMF_PCIE_PCIE2REG_CONFIGDATA,
+ 0xffffffff);
+ }
return 0;
}
@@ -906,33 +981,48 @@ static void brcmf_pcie_bus_console_read(struct brcmf_pciedev_info *devinfo,
static void brcmf_pcie_intr_disable(struct brcmf_pciedev_info *devinfo)
{
- brcmf_pcie_write_reg32(devinfo, devinfo->reginfo->mailboxmask, 0);
+ WRITE_ONCE(devinfo->irq_ready, false);
+ brcmf_pcie_write_pcie32(devinfo, devinfo->reginfo->mailboxmask, 0);
}
static void brcmf_pcie_intr_enable(struct brcmf_pciedev_info *devinfo)
{
- brcmf_pcie_write_reg32(devinfo, devinfo->reginfo->mailboxmask,
- devinfo->reginfo->int_d2h_db |
- devinfo->reginfo->int_fn0);
+ u32 mask = devinfo->reginfo->int_d2h_db | devinfo->reginfo->int_fn0;
+
+ /* Publish initialized rings before accepting firmware MSIs. */
+ smp_store_release(&devinfo->irq_ready, true);
+ brcmf_pcie_write_pcie32(devinfo, devinfo->reginfo->mailboxmask, mask);
}
static void brcmf_pcie_hostready(struct brcmf_pciedev_info *devinfo)
{
- if (devinfo->shared.flags & BRCMF_PCIE_SHARED_HOSTRDY_DB1)
- brcmf_pcie_write_reg32(devinfo,
- devinfo->reginfo->h2d_mailbox_1, 1);
+ u32 reg;
+
+ if (!(devinfo->shared.flags & BRCMF_PCIE_SHARED_HOSTRDY_DB1))
+ return;
+
+ reg = devinfo->shared.flags & BRCMF_PCIE_SHARED_DAR ?
+ BRCMF_PCIE_64_PCIE2REG_H2D_MAILBOX_1 :
+ BRCMF_PCIE_PCIE2REG_H2D_MAILBOX_1;
+ brcmf_pcie_write_pcie32(devinfo, reg, 1);
}
static irqreturn_t brcmf_pcie_quick_check_isr(int irq, void *arg)
{
struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg;
- if (brcmf_pcie_read_reg32(devinfo, devinfo->reginfo->mailboxint)) {
- brcmf_pcie_intr_disable(devinfo);
+ if (brcmf_pcie_read_pcie32(devinfo, devinfo->reginfo->mailboxint)) {
+ brcmf_pcie_write_pcie32(devinfo,
+ devinfo->reginfo->mailboxmask, 0);
brcmf_dbg(PCIE, "Enter\n");
return IRQ_WAKE_THREAD;
}
+
+ /* mailboxint is cleared by the firmware in MSI mode */
+ if (devinfo->have_msi)
+ return IRQ_WAKE_THREAD;
+
return IRQ_NONE;
}
@@ -942,23 +1032,28 @@ static irqreturn_t brcmf_pcie_isr_thread(int irq, void *arg)
struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg;
u32 status;
+ /* Pair with ring publication in brcmf_pcie_intr_enable(). */
+ if (!smp_load_acquire(&devinfo->irq_ready))
+ return IRQ_HANDLED;
+
devinfo->in_irq = true;
- status = brcmf_pcie_read_reg32(devinfo, devinfo->reginfo->mailboxint);
+ status = brcmf_pcie_read_pcie32(devinfo, devinfo->reginfo->mailboxint);
brcmf_dbg(PCIE, "Enter %x\n", status);
if (status) {
- brcmf_pcie_write_reg32(devinfo, devinfo->reginfo->mailboxint,
- status);
+ brcmf_pcie_write_pcie32(devinfo, devinfo->reginfo->mailboxint, status);
if (status & devinfo->reginfo->int_fn0)
brcmf_pcie_handle_mb_data(devinfo);
- if (status & devinfo->reginfo->int_d2h_db) {
- if (devinfo->state == BRCMFMAC_PCIE_STATE_UP)
- brcmf_proto_msgbuf_rx_trigger(
- &devinfo->pdev->dev);
- }
+ }
+ if (devinfo->have_msi || status & devinfo->reginfo->int_d2h_db) {
+ if (devinfo->state == BRCMFMAC_PCIE_STATE_UP)
+ brcmf_proto_msgbuf_rx_trigger(&devinfo->pdev->dev);
}
brcmf_pcie_bus_console_read(devinfo, false);
- if (devinfo->state == BRCMFMAC_PCIE_STATE_UP)
- brcmf_pcie_intr_enable(devinfo);
+ if (devinfo->state == BRCMFMAC_PCIE_STATE_UP &&
+ READ_ONCE(devinfo->irq_ready))
+ brcmf_pcie_write_pcie32(devinfo, devinfo->reginfo->mailboxmask,
+ devinfo->reginfo->int_d2h_db |
+ devinfo->reginfo->int_fn0);
devinfo->in_irq = false;
return IRQ_HANDLED;
}
@@ -973,11 +1068,13 @@ static int brcmf_pcie_request_irq(struct brcmf_pciedev_info *devinfo)
brcmf_dbg(PCIE, "Enter\n");
- pci_enable_msi(pdev);
+ devinfo->have_msi = pci_enable_msi(pdev) >= 0;
if (request_threaded_irq(pdev->irq, brcmf_pcie_quick_check_isr,
brcmf_pcie_isr_thread, IRQF_SHARED,
"brcmf_pcie_intr", devinfo)) {
- pci_disable_msi(pdev);
+ if (devinfo->have_msi)
+ pci_disable_msi(pdev);
+ devinfo->have_msi = false;
brcmf_err(bus, "Failed to request IRQ %d\n", pdev->irq);
return -EIO;
}
@@ -998,7 +1095,9 @@ static void brcmf_pcie_release_irq(struct brcmf_pciedev_info *devinfo)
brcmf_pcie_intr_disable(devinfo);
free_irq(pdev->irq, devinfo);
- pci_disable_msi(pdev);
+ if (devinfo->have_msi)
+ pci_disable_msi(pdev);
+ devinfo->have_msi = false;
msleep(50);
count = 0;
@@ -1009,8 +1108,8 @@ static void brcmf_pcie_release_irq(struct brcmf_pciedev_info *devinfo)
if (devinfo->in_irq)
brcmf_err(bus, "Still in IRQ (processing) !!!\n");
- status = brcmf_pcie_read_reg32(devinfo, devinfo->reginfo->mailboxint);
- brcmf_pcie_write_reg32(devinfo, devinfo->reginfo->mailboxint, status);
+ status = brcmf_pcie_read_pcie32(devinfo, devinfo->reginfo->mailboxint);
+ brcmf_pcie_write_pcie32(devinfo, devinfo->reginfo->mailboxint, status);
devinfo->irq_allocated = false;
}
@@ -1056,13 +1155,17 @@ static int brcmf_pcie_ring_mb_ring_bell(void *ctx)
{
struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx;
struct brcmf_pciedev_info *devinfo = ring->devinfo;
+ u32 reg;
if (devinfo->state != BRCMFMAC_PCIE_STATE_UP)
return -EIO;
brcmf_dbg(PCIE, "RING !\n");
+ reg = devinfo->shared.flags & BRCMF_PCIE_SHARED_DAR ?
+ BRCMF_PCIE_64_PCIE2REG_H2D_MAILBOX_0 :
+ BRCMF_PCIE_PCIE2REG_H2D_MAILBOX_0;
/* Any arbitrary value will do, lets use 1 */
- brcmf_pcie_write_reg32(devinfo, devinfo->reginfo->h2d_mailbox_0, 1);
+ brcmf_pcie_write_pcie32(devinfo, reg, 1);
return 0;
}
@@ -1892,9 +1995,9 @@ static int brcmf_pcie_buscore_reset(void *ctx, struct brcmf_chip *chip)
else
reg = BRCMF_PCIE_PCIE2REG_MAILBOXINT;
- val = brcmf_pcie_read_reg32(devinfo, reg);
+ val = brcmf_pcie_read_pcie32(devinfo, reg);
if (val != 0xffffffff)
- brcmf_pcie_write_reg32(devinfo, reg, val);
+ brcmf_pcie_write_pcie32(devinfo, reg, val);
return 0;
}
@@ -2676,7 +2779,7 @@ static int brcmf_pcie_pm_leave_D3(struct device *dev)
brcmf_dbg(PCIE, "Enter, dev=%p, bus=%p\n", dev, bus);
/* Check if device is still up and running, if so we are ready */
- if (brcmf_pcie_read_reg32(devinfo, devinfo->reginfo->intmask) != 0) {
+ if (brcmf_pcie_read_pcie32(devinfo, devinfo->reginfo->intmask) != 0) {
brcmf_dbg(PCIE, "Try to wakeup device....\n");
if (brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_D0_INFORM))
goto cleanup;
diff --git a/drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h b/drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h
index 0340bba968..641eff6b19 100644
--- a/drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h
+++ b/drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h
@@ -302,6 +302,11 @@ struct chipcregs {
#define PMU_RCTL_LOGIC_DISABLE_MASK (1 << 27)
+#define CC_WD_SSRESET_PCIE_F0_EN 0x10000000
+#define CC_WD_SSRESET_PCIE_ALL_FN_EN 0x80000000
+#define CC_WD_COUNTER_MASK 0x0fffffff
+#define CC_WD_ENABLE_MASK 0xf0000000
+
/*
* Maximum delay for the PMU state transition in us.
* This is an upper bound intended for spinwaits etc.
--
2.55.0
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH 05/13] wifi: brcmfmac: Negotiate PCIe mailbox transport with firmware
2026-09-25 13:43 [PATCH 00/13] wifi: brcmfmac: Add BCM4388 support Michael Reeves via B4 Relay
` (3 preceding siblings ...)
2026-09-25 13:43 ` [PATCH 04/13] wifi: brcmfmac: Support newer PCIe core startup and interrupts Michael Reeves via B4 Relay
@ 2026-09-25 13:43 ` Michael Reeves via B4 Relay
2026-09-25 13:43 ` [PATCH 06/13] wifi: brcmfmac: Load signed PCIe firmware images Michael Reeves via B4 Relay
` (7 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Michael Reeves via B4 Relay @ 2026-09-25 13:43 UTC (permalink / raw)
To: Arend van Spriel, Pieter-Paul Giesberts, Franky Lin (Zhenhui),
Mathy Vanhoef, Kalle Valo, Kees Cook, Gustavo A. R. Silva
Cc: linux-wireless, brcm80211, brcm80211-dev-list.pdl, asahi,
Hector Martin, Daniel Berlin, Janne Grunau, linux-kernel,
Hante Meuleman, linux-hardening, Michael Reeves
From: Hector Martin <marcan@marcan.st>
Shared protocol version 6 and later allows mailbox messages over the
control ring as an alternative to the shared-memory mailbox.
Advertise host capabilities using the negotiated shared protocol
version. Select the control-ring transport when firmware does not set
BRCMF_PCIE_SHARED_USE_MAILBOX, and dispatch received mailbox data
through bus operations.
Signed-off-by: Hector Martin <marcan@marcan.st>
Co-developed-by: Michael Reeves <michael.reeves077@gmail.com>
Signed-off-by: Michael Reeves <michael.reeves077@gmail.com>
---
.../net/wireless/broadcom/brcm80211/brcmfmac/bus.h | 9 +++
.../wireless/broadcom/brcm80211/brcmfmac/msgbuf.c | 57 +++++++++++++++
.../wireless/broadcom/brcm80211/brcmfmac/msgbuf.h | 5 ++
.../wireless/broadcom/brcm80211/brcmfmac/pcie.c | 83 ++++++++++++++++++----
4 files changed, 139 insertions(+), 15 deletions(-)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
index 9371c14899..421039759b 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
@@ -86,6 +86,8 @@ struct brcmf_bus_dcmd {
* @get_ramsize: obtain size of device memory.
* @get_memdump: obtain device memory dump in provided buffer.
* @get_blob: obtain a firmware blob.
+ * @d2h_mb_rx: handle device-to-host mailbox data received through a transport
+ * specific side channel.
* @remove: initiate unbind of the device.
*
* This structure provides an abstract interface towards the
@@ -108,6 +110,7 @@ struct brcmf_bus_ops {
void (*debugfs_create)(struct device *dev);
int (*reset)(struct device *dev);
void (*remove)(struct device *dev);
+ void (*d2h_mb_rx)(struct device *dev, u32 data);
};
@@ -292,6 +295,12 @@ static inline void brcmf_bus_remove(struct brcmf_bus *bus)
bus->ops->remove(bus->dev);
}
+static inline void brcmf_bus_d2h_mb_rx(struct brcmf_bus *bus, u32 data)
+{
+ if (bus->ops->d2h_mb_rx)
+ bus->ops->d2h_mb_rx(bus->dev, data);
+}
+
/*
* interface functions from common layer
*/
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
index 069ba70166..8e5c697e7f 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
@@ -47,6 +47,8 @@
#define MSGBUF_TYPE_RX_CMPLT 0x12
#define MSGBUF_TYPE_LPBK_DMAXFER 0x13
#define MSGBUF_TYPE_LPBK_DMAXFER_CMPLT 0x14
+#define MSGBUF_TYPE_H2D_MAILBOX_DATA 0x23
+#define MSGBUF_TYPE_D2H_MAILBOX_DATA 0x24
#define NR_TX_PKTIDS 2048
#define NR_RX_PKTIDS 1024
@@ -218,6 +220,19 @@ struct msgbuf_flowring_flush_resp {
__le32 rsvd0[3];
};
+struct msgbuf_h2d_mailbox_data {
+ struct msgbuf_common_hdr msg;
+ __le32 data;
+ __le32 rsvd0[7];
+};
+
+struct msgbuf_d2h_mailbox_data {
+ struct msgbuf_common_hdr msg;
+ struct msgbuf_completion_hdr compl_hdr;
+ __le32 data;
+ __le32 rsvd0[2];
+};
+
struct brcmf_msgbuf_work_item {
struct list_head queue;
u32 flowid;
@@ -1319,6 +1334,15 @@ brcmf_msgbuf_process_flow_ring_delete_response(struct brcmf_msgbuf *msgbuf,
brcmf_msgbuf_remove_flowring(msgbuf, flowid);
}
+static void brcmf_msgbuf_process_d2h_mailbox_data(struct brcmf_msgbuf *msgbuf,
+ void *buf)
+{
+ struct msgbuf_d2h_mailbox_data *d2h_mb_data = buf;
+
+ brcmf_bus_d2h_mb_rx(msgbuf->drvr->bus_if,
+ le32_to_cpu(d2h_mb_data->data));
+}
+
static void brcmf_msgbuf_process_msgtype(struct brcmf_msgbuf *msgbuf, void *buf)
{
@@ -1362,6 +1386,10 @@ static void brcmf_msgbuf_process_msgtype(struct brcmf_msgbuf *msgbuf, void *buf)
brcmf_dbg(MSGBUF, "MSGBUF_TYPE_RX_CMPLT\n");
brcmf_msgbuf_process_rx_complete(msgbuf, buf);
break;
+ case MSGBUF_TYPE_D2H_MAILBOX_DATA:
+ brcmf_dbg(MSGBUF, "MSGBUF_TYPE_D2H_MAILBOX_DATA\n");
+ brcmf_msgbuf_process_d2h_mailbox_data(msgbuf, buf);
+ break;
default:
bphy_err(drvr, "Unsupported msgtype %d\n", msg->msgtype);
break;
@@ -1500,6 +1528,35 @@ void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u16 flowid)
}
}
+int brcmf_msgbuf_h2d_mb_write(struct brcmf_pub *drvr, u32 data)
+{
+ struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
+ struct brcmf_commonring *commonring;
+ struct msgbuf_h2d_mailbox_data *request;
+ void *ret_ptr;
+ int err;
+
+ commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT];
+ brcmf_commonring_lock(commonring);
+ ret_ptr = brcmf_commonring_reserve_for_write(commonring);
+ if (!ret_ptr) {
+ bphy_err(drvr, "Failed to reserve space in commonring\n");
+ brcmf_commonring_unlock(commonring);
+ return -ENOMEM;
+ }
+
+ request = (struct msgbuf_h2d_mailbox_data *)ret_ptr;
+ memset(request, 0, sizeof(*request));
+ request->msg.msgtype = MSGBUF_TYPE_H2D_MAILBOX_DATA;
+ request->msg.ifidx = 0xff;
+ request->data = cpu_to_le32(data);
+
+ err = brcmf_commonring_write_complete(commonring);
+ brcmf_commonring_unlock(commonring);
+
+ return err;
+}
+
#ifdef DEBUG
static int brcmf_msgbuf_stats_read(struct seq_file *seq, void *data)
{
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h
index 6a849f4a94..18b56d0789 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h
@@ -32,12 +32,17 @@ int brcmf_proto_msgbuf_rx_trigger(struct device *dev);
void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u16 flowid);
int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr);
void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr);
+int brcmf_msgbuf_h2d_mb_write(struct brcmf_pub *drvr, u32 data);
#else
static inline int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr)
{
return 0;
}
static inline void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr) {}
+static inline int brcmf_msgbuf_h2d_mb_write(struct brcmf_pub *drvr, u32 data)
+{
+ return -EOPNOTSUPP;
+}
#endif
#endif /* BRCMFMAC_MSGBUF_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
index 5917ee5fd7..ad8567e2b9 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
@@ -219,9 +219,13 @@ static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = {
#define BRCMF_PCIE_SHARED_VERSION_MASK 0x00FF
#define BRCMF_PCIE_SHARED_DMA_INDEX 0x10000
#define BRCMF_PCIE_SHARED_DMA_2B_IDX 0x100000
+#define BRCMF_PCIE_SHARED_USE_MAILBOX 0x2000000
#define BRCMF_PCIE_SHARED_HOSTRDY_DB1 0x10000000
#define BRCMF_PCIE_SHARED_DAR 0x80000000
+#define BRCMF_HOSTCAP_H2D_ENABLE_HOSTRDY 0x400
+#define BRCMF_HOSTCAP_DS_NO_OOB_DW 0x1000
+#define BRCMF_HOSTCAP_H2D_DAR 0x10000
#define BRCMF_PCIE_FLAGS_HTOD_SPLIT 0x4000
#define BRCMF_PCIE_FLAGS_DTOH_SPLIT 0x8000
@@ -237,6 +241,8 @@ static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = {
#define BRCMF_SHARED_DMA_SCRATCH_ADDR_OFFSET 56
#define BRCMF_SHARED_DMA_RINGUPD_LEN_OFFSET 64
#define BRCMF_SHARED_DMA_RINGUPD_ADDR_OFFSET 68
+#define BRCMF_SHARED_HOST_CAP_OFFSET 84
+#define BRCMF_SHARED_HOST_CAP2_OFFSET 112
#define BRCMF_RING_H2D_RING_COUNT_OFFSET 0
#define BRCMF_RING_D2H_RING_COUNT_OFFSET 1
@@ -318,6 +324,7 @@ struct brcmf_pcie_shared_info {
void *ringupd;
dma_addr_t ringupd_dmahandle;
u8 version;
+ bool mb_via_ctl;
};
#define BRCMF_OTP_MAX_PARAM_LEN 16
@@ -843,6 +850,18 @@ brcmf_pcie_send_mb_data(struct brcmf_pciedev_info *devinfo, u32 htod_mb_data)
u32 i;
shared = &devinfo->shared;
+ if (shared->mb_via_ctl) {
+ struct pci_dev *pdev = devinfo->pdev;
+ struct brcmf_bus *bus = dev_get_drvdata(&pdev->dev);
+ int ret;
+
+ ret = brcmf_msgbuf_h2d_mb_write(bus->drvr, htod_mb_data);
+ if (ret < 0)
+ brcmf_err(bus, "Failed to send H2D mailbox data (%d)\n",
+ ret);
+ return ret;
+ }
+
addr = shared->htod_mb_data_addr;
cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr);
@@ -871,21 +890,9 @@ brcmf_pcie_send_mb_data(struct brcmf_pciedev_info *devinfo, u32 htod_mb_data)
}
-static void brcmf_pcie_handle_mb_data(struct brcmf_pciedev_info *devinfo)
+static void brcmf_pcie_handle_mb_data(struct brcmf_pciedev_info *devinfo,
+ u32 dtoh_mb_data)
{
- struct brcmf_pcie_shared_info *shared;
- u32 addr;
- u32 dtoh_mb_data;
-
- shared = &devinfo->shared;
- addr = shared->dtoh_mb_data_addr;
- dtoh_mb_data = brcmf_pcie_read_tcm32(devinfo, addr);
-
- if (!dtoh_mb_data)
- return;
-
- brcmf_pcie_write_tcm32(devinfo, addr, 0);
-
brcmf_dbg(PCIE, "D2H_MB_DATA: 0x%04x\n", dtoh_mb_data);
if (dtoh_mb_data & BRCMF_D2H_DEV_DS_ENTER_REQ) {
brcmf_dbg(PCIE, "D2H_MB_DATA: DEEP SLEEP REQ\n");
@@ -906,6 +913,32 @@ static void brcmf_pcie_handle_mb_data(struct brcmf_pciedev_info *devinfo)
}
+static void brcmf_pcie_poll_mb_data(struct brcmf_pciedev_info *devinfo)
+{
+ struct brcmf_pcie_shared_info *shared;
+ u32 addr;
+ u32 dtoh_mb_data;
+
+ shared = &devinfo->shared;
+ addr = shared->dtoh_mb_data_addr;
+ dtoh_mb_data = brcmf_pcie_read_tcm32(devinfo, addr);
+
+ if (!dtoh_mb_data)
+ return;
+
+ brcmf_pcie_write_tcm32(devinfo, addr, 0);
+
+ brcmf_pcie_handle_mb_data(devinfo, dtoh_mb_data);
+}
+
+static void brcmf_pcie_d2h_mb_rx(struct device *dev, u32 data)
+{
+ struct brcmf_bus *bus = dev_get_drvdata(dev);
+ struct brcmf_pciedev *buspub = bus->bus_priv.pcie;
+
+ brcmf_pcie_handle_mb_data(buspub->devinfo, data);
+}
+
static void brcmf_pcie_bus_console_init(struct brcmf_pciedev_info *devinfo)
{
struct brcmf_pcie_shared_info *shared;
@@ -1042,7 +1075,7 @@ static irqreturn_t brcmf_pcie_isr_thread(int irq, void *arg)
if (status) {
brcmf_pcie_write_pcie32(devinfo, devinfo->reginfo->mailboxint, status);
if (status & devinfo->reginfo->int_fn0)
- brcmf_pcie_handle_mb_data(devinfo);
+ brcmf_pcie_poll_mb_data(devinfo);
}
if (devinfo->have_msi || status & devinfo->reginfo->int_d2h_db) {
if (devinfo->state == BRCMFMAC_PCIE_STATE_UP)
@@ -1695,6 +1728,7 @@ static const struct brcmf_bus_ops brcmf_pcie_bus_ops = {
.get_blob = brcmf_pcie_get_blob,
.reset = brcmf_pcie_reset,
.debugfs_create = brcmf_pcie_debugfs_create,
+ .d2h_mb_rx = brcmf_pcie_d2h_mb_rx,
};
@@ -1726,6 +1760,7 @@ brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo,
{
struct brcmf_bus *bus = dev_get_drvdata(&devinfo->pdev->dev);
struct brcmf_pcie_shared_info *shared;
+ u32 host_cap;
u32 addr;
shared = &devinfo->shared;
@@ -1766,6 +1801,24 @@ brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo,
addr = sharedram_addr + BRCMF_SHARED_RING_INFO_ADDR_OFFSET;
shared->ring_info_addr = brcmf_pcie_read_tcm32(devinfo, addr);
+ if (shared->version >= 6) {
+ shared->mb_via_ctl =
+ !(shared->flags & BRCMF_PCIE_SHARED_USE_MAILBOX);
+
+ host_cap = shared->version;
+ if (shared->flags & BRCMF_PCIE_SHARED_HOSTRDY_DB1)
+ host_cap |= BRCMF_HOSTCAP_H2D_ENABLE_HOSTRDY;
+ if (shared->flags & BRCMF_PCIE_SHARED_DAR)
+ host_cap |= BRCMF_HOSTCAP_H2D_DAR;
+ host_cap |= BRCMF_HOSTCAP_DS_NO_OOB_DW;
+
+ brcmf_pcie_write_tcm32(devinfo, sharedram_addr +
+ BRCMF_SHARED_HOST_CAP_OFFSET,
+ host_cap);
+ brcmf_pcie_write_tcm32(devinfo, sharedram_addr +
+ BRCMF_SHARED_HOST_CAP2_OFFSET, 0);
+ }
+
brcmf_dbg(PCIE, "max rx buf post %d, rx dataoffset %d\n",
shared->max_rxbufpost, shared->rx_dataoffset);
--
2.55.0
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH 06/13] wifi: brcmfmac: Load signed PCIe firmware images
2026-09-25 13:43 [PATCH 00/13] wifi: brcmfmac: Add BCM4388 support Michael Reeves via B4 Relay
` (4 preceding siblings ...)
2026-09-25 13:43 ` [PATCH 05/13] wifi: brcmfmac: Negotiate PCIe mailbox transport with firmware Michael Reeves via B4 Relay
@ 2026-09-25 13:43 ` Michael Reeves via B4 Relay
2026-09-25 13:43 ` [PATCH 07/13] wifi: brcmfmac: Use event_msgs_ext for event masks Michael Reeves via B4 Relay
` (6 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Michael Reeves via B4 Relay @ 2026-09-25 13:43 UTC (permalink / raw)
To: Arend van Spriel, Pieter-Paul Giesberts, Franky Lin (Zhenhui),
Mathy Vanhoef, Kalle Valo, Kees Cook, Gustavo A. R. Silva
Cc: linux-wireless, brcm80211, brcm80211-dev-list.pdl, asahi,
Hector Martin, Daniel Berlin, Janne Grunau, linux-kernel,
Hante Meuleman, linux-hardening, Michael Reeves
From: Hector Martin <marcan@marcan.st>
Signed PCIe firmware needs a signature and a memory map describing the
image, signature, verification status, and bootloader heap.
Request an optional .sig file and write the signature and reverse-TLV
metadata to device memory. Describe an aligned heap after the firmware
image, keeping the metadata and end footer outside it. Require NVRAM for
signed images and preserve their reset vector.
Request the IRQ before starting firmware on chips that use signatures,
while keeping ring processing disabled until initialization completes.
Signed-off-by: Hector Martin <marcan@marcan.st>
Co-developed-by: Daniel Berlin <dberlin@dberlin.org>
Signed-off-by: Daniel Berlin <dberlin@dberlin.org>
Co-developed-by: Michael Reeves <michael.reeves077@gmail.com>
Signed-off-by: Michael Reeves <michael.reeves077@gmail.com>
---
.../wireless/broadcom/brcm80211/brcmfmac/pcie.c | 267 ++++++++++++++++++---
1 file changed, 233 insertions(+), 34 deletions(-)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
index ad8567e2b9..48402eb2ef 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
@@ -80,6 +80,7 @@ MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH "brcmfmac*-pcie.*.txt");
/* per-board firmware binaries */
MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH "brcmfmac*-pcie.*.bin");
MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH "brcmfmac*-pcie.*.clm_blob");
+MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH "brcmfmac*-pcie.*.sig");
MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH "brcmfmac*-pcie.*.txcap_blob");
static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = {
@@ -369,6 +370,9 @@ struct brcmf_pciedev_info {
struct brcmf_mp_device *settings;
struct brcmf_otp_params otp;
bool fwseed;
+ char sig_name[BRCMF_FW_NAME_LEN];
+ u32 fw_size;
+ bool skip_reset_vector;
#ifdef DEBUG
u32 console_interval;
bool console_active;
@@ -468,6 +472,7 @@ static void brcmf_pcie_setup(struct device *dev, int ret,
struct brcmf_fw_request *fwreq);
static struct brcmf_fw_request *
brcmf_pcie_prepare_fw_request(struct brcmf_pciedev_info *devinfo);
+static bool brcmf_pcie_request_fw_signature(struct brcmf_pciedev_info *devinfo);
static void
brcmf_pcie_fwcon_timer(struct brcmf_pciedev_info *devinfo, bool active);
static void brcmf_pcie_debugfs_create(struct device *dev);
@@ -1828,26 +1833,174 @@ brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo,
return 0;
}
-struct brcmf_random_seed_footer {
+struct brcmf_rtlv_footer {
__le32 length;
__le32 magic;
};
+struct brcmf_fw_memmap_region {
+ __le32 start;
+ __le32 end;
+};
+
+struct brcmf_fw_memmap {
+ struct brcmf_fw_memmap_region reset_vec;
+ struct brcmf_fw_memmap_region int_vec;
+ struct brcmf_fw_memmap_region rom;
+ struct brcmf_fw_memmap_region mmap;
+ struct brcmf_fw_memmap_region vstatus;
+ struct brcmf_fw_memmap_region fw;
+ struct brcmf_fw_memmap_region sig;
+ struct brcmf_fw_memmap_region heap;
+ struct brcmf_fw_memmap_region stack;
+ struct brcmf_fw_memmap_region prng;
+ struct brcmf_fw_memmap_region nvram;
+};
+
+#define BRCMF_BL_HEAP_START_GAP 0x1000
+#define BRCMF_BL_HEAP_SIZE 0x10000
#define BRCMF_RANDOM_SEED_MAGIC 0xfeedc0de
#define BRCMF_RANDOM_SEED_LENGTH 0x100
+#define BRCMF_FW_SIG_MAGIC 0xfeedfe51
+#define BRCMF_MEMMAP_MAGIC 0xfeedfe53
+#define BRCMF_VSTATUS_MAGIC 0xfeedfe54
+#define BRCMF_VSTATUS_SIZE 0x28
+#define BRCMF_END_MAGIC 0xfeed0e2d
+
+static int brcmf_pcie_alloc_rtlv(struct brcmf_pciedev_info *devinfo,
+ u32 *address, u32 type, size_t length)
+{
+ struct brcmf_bus *bus = dev_get_drvdata(&devinfo->pdev->dev);
+ u32 fw_top = devinfo->ci->rambase + devinfo->fw_size;
+ u32 start_addr;
+ u32 length_field;
+ struct brcmf_rtlv_footer footer = {
+ .magic = cpu_to_le32(type),
+ };
+
+ if (length > 0xfffc)
+ return -E2BIG;
+
+ length = ALIGN(length, 4);
+ if (*address < length + sizeof(footer))
+ return -ENOMEM;
+
+ start_addr = *address - length - sizeof(footer);
+ if (start_addr < fw_top) {
+ brcmf_err(bus, "failed to allocate rTLV type 0x%x len 0x%zx\n",
+ type, length);
+ return -ENOMEM;
+ }
+
+ if (type == BRCMF_RANDOM_SEED_MAGIC)
+ length_field = length;
+ else
+ length_field = length | ((length ^ 0xffff) << 16);
+
+ footer.length = cpu_to_le32(length_field);
+ memcpy_toio(devinfo->tcm + *address - sizeof(footer), &footer,
+ sizeof(footer));
+ *address = start_addr;
+
+ return 0;
+}
-static noinline_for_stack void
-brcmf_pcie_provide_random_bytes(struct brcmf_pciedev_info *devinfo, u32 address)
+static noinline_for_stack int
+brcmf_pcie_add_random_seed(struct brcmf_pciedev_info *devinfo, u32 *address)
{
u8 randbuf[BRCMF_RANDOM_SEED_LENGTH];
+ int err;
+
+ err = brcmf_pcie_alloc_rtlv(devinfo, address,
+ BRCMF_RANDOM_SEED_MAGIC,
+ BRCMF_RANDOM_SEED_LENGTH);
+ if (err)
+ return err;
+ brcmf_dbg(PCIE, "Download random seed\n");
get_random_bytes(randbuf, BRCMF_RANDOM_SEED_LENGTH);
- memcpy_toio(devinfo->tcm + address, randbuf, BRCMF_RANDOM_SEED_LENGTH);
+ memcpy_toio(devinfo->tcm + *address, randbuf, BRCMF_RANDOM_SEED_LENGTH);
+
+ return 0;
+}
+
+static int brcmf_pcie_add_signature(struct brcmf_pciedev_info *devinfo,
+ u32 *address,
+ const struct firmware *fwsig)
+{
+ struct brcmf_fw_memmap memmap = {};
+ u32 sig_start, vstatus_start, fw_end, heap_start, heap_end;
+ int err;
+
+ brcmf_dbg(PCIE, "Download firmware signature\n");
+
+ memmap.sig.end = cpu_to_le32(*address);
+ err = brcmf_pcie_alloc_rtlv(devinfo, address, BRCMF_FW_SIG_MAGIC,
+ fwsig->size);
+ if (err)
+ return err;
+ sig_start = *address;
+ memmap.sig.start = cpu_to_le32(sig_start);
+
+ memmap.vstatus.end = cpu_to_le32(*address);
+ err = brcmf_pcie_alloc_rtlv(devinfo, address, BRCMF_VSTATUS_MAGIC,
+ BRCMF_VSTATUS_SIZE);
+ if (err)
+ return err;
+ vstatus_start = *address;
+ memmap.vstatus.start = cpu_to_le32(vstatus_start);
+
+ err = brcmf_pcie_alloc_rtlv(devinfo, address, BRCMF_MEMMAP_MAGIC,
+ sizeof(memmap));
+ if (err)
+ return err;
+
+ fw_end = devinfo->ci->rambase + devinfo->fw_size;
+ heap_start = ALIGN(fw_end + BRCMF_BL_HEAP_START_GAP, 4);
+ heap_end = heap_start + BRCMF_BL_HEAP_SIZE;
+ if (heap_end > *address ||
+ *address - heap_end < sizeof(struct brcmf_rtlv_footer))
+ return -ENOMEM;
+
+ memmap.fw.start = cpu_to_le32(devinfo->ci->rambase);
+ memmap.fw.end = cpu_to_le32(fw_end);
+ memmap.heap.start = cpu_to_le32(heap_start);
+ memmap.heap.end = cpu_to_le32(heap_end);
+
+ memcpy_toio(devinfo->tcm + sig_start, fwsig->data, fwsig->size);
+ memset_io(devinfo->tcm + vstatus_start, 0, BRCMF_VSTATUS_SIZE);
+ memcpy_toio(devinfo->tcm + *address, &memmap, sizeof(memmap));
+
+ err = brcmf_pcie_alloc_rtlv(devinfo, address, BRCMF_END_MAGIC, 0);
+ if (err)
+ return err;
+
+ devinfo->skip_reset_vector = true;
+ return 0;
+}
+
+static int brcmf_pcie_populate_footers(struct brcmf_pciedev_info *devinfo,
+ u32 *address,
+ const struct firmware *fwsig)
+{
+ int err;
+
+ if (devinfo->fwseed) {
+ err = brcmf_pcie_add_random_seed(devinfo, address);
+ if (err)
+ return err;
+ }
+
+ if (fwsig)
+ return brcmf_pcie_add_signature(devinfo, address, fwsig);
+
+ return 0;
}
static int brcmf_pcie_download_fw_nvram(struct brcmf_pciedev_info *devinfo,
- const struct firmware *fw, void *nvram,
- u32 nvram_len)
+ const struct firmware *fw,
+ const struct firmware *fwsig,
+ void *nvram, u32 nvram_len)
{
struct brcmf_bus *bus = dev_get_drvdata(&devinfo->pdev->dev);
u32 sharedram_addr;
@@ -1855,19 +2008,30 @@ static int brcmf_pcie_download_fw_nvram(struct brcmf_pciedev_info *devinfo,
u32 loop_counter;
int err;
u32 address;
+ u32 free_start;
u32 resetintr;
+ devinfo->skip_reset_vector = false;
+
+ if (fwsig && !nvram) {
+ brcmf_err(bus, "NVRAM required for signed firmware\n");
+ err = -ENOENT;
+ goto fail;
+ }
+
brcmf_dbg(PCIE, "Halt ARM.\n");
err = brcmf_pcie_enter_download_state(devinfo);
if (err)
- return err;
+ goto fail;
brcmf_dbg(PCIE, "Download FW %s\n", devinfo->fw_name);
memcpy_toio(devinfo->tcm + devinfo->ci->rambase,
(void *)fw->data, fw->size);
resetintr = get_unaligned_le32(fw->data);
+ devinfo->fw_size = fw->size;
release_firmware(fw);
+ fw = NULL;
/* reset last 4 bytes of RAM address. to be used for shared
* area. This identifies when FW is running
@@ -1880,30 +2044,22 @@ static int brcmf_pcie_download_fw_nvram(struct brcmf_pciedev_info *devinfo,
nvram_len;
memcpy_toio(devinfo->tcm + address, nvram, nvram_len);
brcmf_fw_nvram_free(nvram);
+ nvram = NULL;
- if (devinfo->fwseed) {
- size_t rand_len = BRCMF_RANDOM_SEED_LENGTH;
- struct brcmf_random_seed_footer footer = {
- .length = cpu_to_le32(rand_len),
- .magic = cpu_to_le32(BRCMF_RANDOM_SEED_MAGIC),
- };
-
- /* Some chips/firmwares expect a buffer of random
- * data to be present before NVRAM
- */
- brcmf_dbg(PCIE, "Download random seed\n");
-
- address -= sizeof(footer);
- memcpy_toio(devinfo->tcm + address, &footer,
- sizeof(footer));
+ err = brcmf_pcie_populate_footers(devinfo, &address, fwsig);
+ if (err)
+ goto fail;
- address -= rand_len;
- brcmf_pcie_provide_random_bytes(devinfo, address);
- }
+ free_start = devinfo->ci->rambase + devinfo->fw_size;
+ if ((devinfo->fwseed || fwsig) && address > free_start)
+ memset_io(devinfo->tcm + free_start, 0,
+ address - free_start);
} else {
brcmf_dbg(PCIE, "No matching NVRAM file found %s\n",
devinfo->nvram_name);
}
+ release_firmware(fwsig);
+ fwsig = NULL;
sharedram_addr_written = brcmf_pcie_read_ram32(devinfo,
devinfo->ci->ramsize -
@@ -1911,7 +2067,7 @@ static int brcmf_pcie_download_fw_nvram(struct brcmf_pciedev_info *devinfo,
brcmf_dbg(PCIE, "Bring ARM in running state\n");
err = brcmf_pcie_exit_download_state(devinfo, resetintr);
if (err)
- return err;
+ goto fail;
brcmf_dbg(PCIE, "Wait for FW init\n");
sharedram_addr = sharedram_addr_written;
@@ -1925,17 +2081,25 @@ static int brcmf_pcie_download_fw_nvram(struct brcmf_pciedev_info *devinfo,
}
if (sharedram_addr == sharedram_addr_written) {
brcmf_err(bus, "FW failed to initialize\n");
- return -ENODEV;
+ err = -ENODEV;
+ goto fail;
}
if (sharedram_addr < devinfo->ci->rambase ||
sharedram_addr >= devinfo->ci->rambase + devinfo->ci->ramsize) {
brcmf_err(bus, "Invalid shared RAM address 0x%08x\n",
sharedram_addr);
- return -ENODEV;
+ err = -ENODEV;
+ goto fail;
}
brcmf_dbg(PCIE, "Shared RAM addr: 0x%08x\n", sharedram_addr);
return (brcmf_pcie_init_share_ram_info(devinfo, sharedram_addr));
+
+fail:
+ release_firmware(fw);
+ release_firmware(fwsig);
+ brcmf_fw_nvram_free(nvram);
+ return err;
}
@@ -2061,7 +2225,8 @@ static void brcmf_pcie_buscore_activate(void *ctx, struct brcmf_chip *chip,
{
struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx;
- brcmf_pcie_write_tcm32(devinfo, 0, rstvec);
+ if (!devinfo->skip_reset_vector)
+ brcmf_pcie_write_tcm32(devinfo, 0, rstvec);
}
@@ -2290,11 +2455,18 @@ static int brcmf_pcie_read_otp(struct brcmf_pciedev_info *devinfo)
#define BRCMF_PCIE_FW_NVRAM 1
#define BRCMF_PCIE_FW_CLM 2
#define BRCMF_PCIE_FW_TXCAP 3
+#define BRCMF_PCIE_FW_SIG 4
+
+static bool brcmf_pcie_request_fw_signature(struct brcmf_pciedev_info *devinfo)
+{
+ return false;
+}
static void brcmf_pcie_setup(struct device *dev, int ret,
struct brcmf_fw_request *fwreq)
{
const struct firmware *fw;
+ const struct firmware *fwsig = NULL;
void *nvram;
struct brcmf_bus *bus;
struct brcmf_pciedev *pcie_bus_dev;
@@ -2313,6 +2485,8 @@ static void brcmf_pcie_setup(struct device *dev, int ret,
brcmf_pcie_attach(devinfo);
fw = fwreq->items[BRCMF_PCIE_FW_CODE].binary;
+ if (fwreq->n_items > BRCMF_PCIE_FW_SIG)
+ fwsig = fwreq->items[BRCMF_PCIE_FW_SIG].binary;
nvram = fwreq->items[BRCMF_PCIE_FW_NVRAM].nv_data.data;
nvram_len = fwreq->items[BRCMF_PCIE_FW_NVRAM].nv_data.len;
devinfo->clm_fw = fwreq->items[BRCMF_PCIE_FW_CLM].binary;
@@ -2323,6 +2497,7 @@ static void brcmf_pcie_setup(struct device *dev, int ret,
if (ret) {
brcmf_err(bus, "Failed to get RAM info\n");
release_firmware(fw);
+ release_firmware(fwsig);
brcmf_fw_nvram_free(nvram);
goto fail;
}
@@ -2334,7 +2509,19 @@ static void brcmf_pcie_setup(struct device *dev, int ret,
*/
brcmf_pcie_adjust_ramsize(devinfo, (u8 *)fw->data, fw->size);
- ret = brcmf_pcie_download_fw_nvram(devinfo, fw, nvram, nvram_len);
+ if (brcmf_pcie_request_fw_signature(devinfo)) {
+ brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
+ ret = brcmf_pcie_request_irq(devinfo);
+ if (ret) {
+ release_firmware(fw);
+ release_firmware(fwsig);
+ brcmf_fw_nvram_free(nvram);
+ goto fail;
+ }
+ }
+
+ ret = brcmf_pcie_download_fw_nvram(devinfo, fw, fwsig, nvram,
+ nvram_len);
if (ret)
goto fail;
@@ -2349,9 +2536,11 @@ static void brcmf_pcie_setup(struct device *dev, int ret,
goto fail;
brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
- ret = brcmf_pcie_request_irq(devinfo);
- if (ret)
- goto fail;
+ if (!devinfo->irq_allocated) {
+ ret = brcmf_pcie_request_irq(devinfo);
+ if (ret)
+ goto fail;
+ }
/* hook the commonrings in the bus structure. */
for (i = 0; i < BRCMF_NROF_COMMON_MSGRINGS; i++)
@@ -2393,17 +2582,23 @@ static struct brcmf_fw_request *
brcmf_pcie_prepare_fw_request(struct brcmf_pciedev_info *devinfo)
{
struct brcmf_fw_request *fwreq;
+ u32 n_fwnames;
struct brcmf_fw_name fwnames[] = {
{ ".bin", devinfo->fw_name },
{ ".txt", devinfo->nvram_name },
{ ".clm_blob", devinfo->clm_name },
{ ".txcap_blob", devinfo->txcap_name },
+ { ".sig", devinfo->sig_name },
};
+ n_fwnames = ARRAY_SIZE(fwnames);
+ if (!brcmf_pcie_request_fw_signature(devinfo))
+ n_fwnames--;
+
fwreq = brcmf_fw_alloc_request(devinfo->ci->chip, devinfo->ci->chiprev,
brcmf_pcie_fwnames,
ARRAY_SIZE(brcmf_pcie_fwnames),
- fwnames, ARRAY_SIZE(fwnames));
+ fwnames, n_fwnames);
if (!fwreq)
return NULL;
@@ -2414,6 +2609,10 @@ brcmf_pcie_prepare_fw_request(struct brcmf_pciedev_info *devinfo)
fwreq->items[BRCMF_PCIE_FW_CLM].flags = BRCMF_FW_REQF_OPTIONAL;
fwreq->items[BRCMF_PCIE_FW_TXCAP].type = BRCMF_FW_TYPE_BINARY;
fwreq->items[BRCMF_PCIE_FW_TXCAP].flags = BRCMF_FW_REQF_OPTIONAL;
+ if (fwreq->n_items > BRCMF_PCIE_FW_SIG) {
+ fwreq->items[BRCMF_PCIE_FW_SIG].type = BRCMF_FW_TYPE_BINARY;
+ fwreq->items[BRCMF_PCIE_FW_SIG].flags = BRCMF_FW_REQF_OPTIONAL;
+ }
/* NVRAM reserves PCI domain 0 for Broadcom's SDK faked bus */
fwreq->domain_nr = pci_domain_nr(devinfo->pdev->bus) + 1;
fwreq->bus_nr = devinfo->pdev->bus->number;
--
2.55.0
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH 07/13] wifi: brcmfmac: Use event_msgs_ext for event masks
2026-09-25 13:43 [PATCH 00/13] wifi: brcmfmac: Add BCM4388 support Michael Reeves via B4 Relay
` (5 preceding siblings ...)
2026-09-25 13:43 ` [PATCH 06/13] wifi: brcmfmac: Load signed PCIe firmware images Michael Reeves via B4 Relay
@ 2026-09-25 13:43 ` Michael Reeves via B4 Relay
2026-09-25 13:43 ` [PATCH 08/13] wifi: brcmfmac: Handle larger firmware band lists Michael Reeves via B4 Relay
` (5 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Michael Reeves via B4 Relay @ 2026-09-25 13:43 UTC (permalink / raw)
To: Arend van Spriel, Pieter-Paul Giesberts, Franky Lin (Zhenhui),
Mathy Vanhoef, Kalle Valo, Kees Cook, Gustavo A. R. Silva
Cc: linux-wireless, brcm80211, brcm80211-dev-list.pdl, asahi,
Hector Martin, Daniel Berlin, Janne Grunau, linux-kernel,
Hante Meuleman, linux-hardening, Michael Reeves
From: Michael Reeves <michael.reeves077@gmail.com>
BCM4388 firmware rejects the legacy event_msgs query during preinit but
supports the structured event_msgs_ext command.
Probe event_msgs_ext during feature detection and initialize the event
mask after feature flags are set. Use the extended command when
supported and retain the legacy path otherwise.
Co-developed-by: Hector Martin <marcan@marcan.st>
Signed-off-by: Hector Martin <marcan@marcan.st>
Signed-off-by: Michael Reeves <michael.reeves077@gmail.com>
---
.../wireless/broadcom/brcm80211/brcmfmac/common.c | 23 ----
.../wireless/broadcom/brcm80211/brcmfmac/core.c | 5 +-
.../wireless/broadcom/brcm80211/brcmfmac/feature.c | 39 +++++++
.../wireless/broadcom/brcm80211/brcmfmac/feature.h | 2 +
.../wireless/broadcom/brcm80211/brcmfmac/fweh.c | 130 ++++++++++++++++++++-
.../wireless/broadcom/brcm80211/brcmfmac/fweh.h | 1 +
.../broadcom/brcm80211/brcmfmac/fwil_types.h | 26 +++++
7 files changed, 199 insertions(+), 27 deletions(-)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
index 2a37b42ca9..93458c2fbc 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
@@ -269,7 +269,6 @@ static int brcmf_c_process_cal_blob(struct brcmf_if *ifp)
int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
{
struct brcmf_pub *drvr = ifp->drvr;
- struct brcmf_fweh_info *fweh = drvr->fweh;
u8 buf[BRCMF_DCMD_SMLEN];
struct brcmf_bus *bus;
struct brcmf_rev_info_le revinfo;
@@ -415,27 +414,6 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
brcmf_c_set_joinpref_default(ifp);
- /* Setup event_msgs, enable E_IF */
- err = brcmf_fil_iovar_data_get(ifp, "event_msgs", fweh->event_mask,
- fweh->event_mask_len);
- if (err) {
- bphy_err(drvr, "Get event_msgs error (%d)\n", err);
- goto done;
- }
- /*
- * BRCMF_E_IF can safely be used to set the appropriate bit
- * in the event_mask as the firmware event code is guaranteed
- * to match the value of BRCMF_E_IF because it is old cruft
- * that all vendors have.
- */
- setbit(fweh->event_mask, BRCMF_E_IF);
- err = brcmf_fil_iovar_data_set(ifp, "event_msgs", fweh->event_mask,
- fweh->event_mask_len);
- if (err) {
- bphy_err(drvr, "Set event_msgs error (%d)\n", err);
- goto done;
- }
-
/* Setup default scan channel time */
err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
BRCMF_DEFAULT_SCAN_CHANNEL_TIME);
@@ -637,4 +615,3 @@ static void __exit brcmfmac_module_exit(void)
module_init(brcmfmac_module_init);
module_exit(brcmfmac_module_exit);
-
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
index d2ae679856..bb9a0aeb06 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
@@ -1260,6 +1260,10 @@ static int brcmf_bus_started(struct brcmf_pub *drvr, struct cfg80211_ops *ops)
brcmf_feat_attach(drvr);
+ ret = brcmf_fweh_init_events(ifp);
+ if (ret < 0)
+ goto fail;
+
ret = brcmf_proto_init_done(drvr);
if (ret < 0)
goto fail;
@@ -1613,4 +1617,3 @@ void __exit brcmf_core_exit(void)
brcmf_usb_exit();
brcmf_pcie_exit();
}
-
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
index a3c221117d..d289451644 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
@@ -5,6 +5,7 @@
#include <linux/netdevice.h>
#include <linux/module.h>
+#include <linux/slab.h>
#include <brcm_hw_ids.h>
#include <brcmu_wifi.h>
@@ -226,6 +227,43 @@ static void brcmf_feat_iovar_data_set(struct brcmf_if *ifp,
ifp->fwil_fwerr = false;
}
+static void brcmf_feat_event_msgs_ext(struct brcmf_if *ifp)
+{
+ struct brcmf_pub *drvr = ifp->drvr;
+ struct brcmf_fweh_info *fweh = drvr->fweh;
+ struct brcmf_eventmsgs_ext_le *eventmsgs;
+ size_t size;
+ int err;
+
+ if (!fweh || fweh->event_mask_len > 0xff)
+ return;
+
+ size = struct_size(eventmsgs, mask, fweh->event_mask_len);
+ eventmsgs = kzalloc(size, GFP_KERNEL);
+ if (!eventmsgs)
+ return;
+
+ eventmsgs->version = EVENTMSGS_VER;
+ eventmsgs->command = EVENTMSGS_NONE;
+ eventmsgs->len = fweh->event_mask_len;
+ eventmsgs->maxgetsize = fweh->event_mask_len;
+
+ ifp->fwil_fwerr = true;
+ err = brcmf_fil_iovar_data_get(ifp, "event_msgs_ext", eventmsgs, size);
+ ifp->fwil_fwerr = false;
+
+ if (!err) {
+ brcmf_dbg(INFO, "enabling feature: %s\n",
+ brcmf_feat_names[BRCMF_FEAT_EVENT_MSGS_EXT]);
+ drvr->feat_flags |= BIT(BRCMF_FEAT_EVENT_MSGS_EXT);
+ } else {
+ brcmf_dbg(TRACE, "%s feature check failed: %d\n",
+ brcmf_feat_names[BRCMF_FEAT_EVENT_MSGS_EXT], err);
+ }
+
+ kfree(eventmsgs);
+}
+
#define MAX_CAPS_BUFFER_SIZE 768
static void brcmf_feat_firmware_capabilities(struct brcmf_if *ifp)
{
@@ -348,6 +386,7 @@ void brcmf_feat_attach(struct brcmf_pub *drvr)
brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_FWSUP, "sup_wpa");
brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_SCAN_V2, "scan_ver");
+ brcmf_feat_event_msgs_ext(ifp);
brcmf_feat_wlc_version_overrides(drvr);
brcmf_feat_firmware_overrides(drvr);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
index 8165a286b6..45a05642bb 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
@@ -31,6 +31,7 @@
* FWAUTH: Firmware authenticator
* DUMP_OBSS: Firmware has capable to dump obss info to support ACS
* SCAN_V2: Version 2 scan params
+ * EVENT_MSGS_EXT: Firmware supports event_msgs_ext for event masks
* SAE_EXT: SAE authentication handled by user-space supplicant
*/
#define BRCMF_FEAT_LIST \
@@ -59,6 +60,7 @@
BRCMF_FEAT_DEF(SCAN_V2) \
BRCMF_FEAT_DEF(PMKID_V2) \
BRCMF_FEAT_DEF(PMKID_V3) \
+ BRCMF_FEAT_DEF(EVENT_MSGS_EXT) \
BRCMF_FEAT_DEF(SAE_EXT) \
BRCMF_FEAT_DEF(FBT) \
BRCMF_FEAT_DEF(OKC)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
index 1cff4ba769..034f90ad6c 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
@@ -12,7 +12,9 @@
#include "debug.h"
#include "tracepoint.h"
#include "fweh.h"
+#include "feature.h"
#include "fwil.h"
+#include "fwil_types.h"
#include "proto.h"
#include "bus.h"
#include "fwvid.h"
@@ -430,6 +432,128 @@ void brcmf_fweh_unregister(struct brcmf_pub *drvr,
drvr->fweh->evt_handler[evt_handler_idx] = NULL;
}
+static bool brcmf_fweh_use_event_msgs_ext(struct brcmf_if *ifp)
+{
+ return brcmf_feat_is_enabled(ifp, BRCMF_FEAT_EVENT_MSGS_EXT);
+}
+
+static const char *brcmf_fweh_event_mask_iovar(struct brcmf_if *ifp)
+{
+ return brcmf_fweh_use_event_msgs_ext(ifp) ? "event_msgs_ext" :
+ "event_msgs";
+}
+
+static int brcmf_fweh_get_event_mask(struct brcmf_if *ifp)
+{
+ struct brcmf_pub *drvr = ifp->drvr;
+ struct brcmf_fweh_info *fweh = drvr->fweh;
+ struct brcmf_eventmsgs_ext_le *eventmsgs;
+ size_t size;
+ u8 fw_len;
+ int err;
+
+ memset(fweh->event_mask, 0, fweh->event_mask_len);
+
+ if (!brcmf_fweh_use_event_msgs_ext(ifp))
+ return brcmf_fil_iovar_data_get(ifp, "event_msgs",
+ fweh->event_mask,
+ fweh->event_mask_len);
+
+ if (fweh->event_mask_len > 0xff)
+ return -E2BIG;
+
+ size = struct_size(eventmsgs, mask, fweh->event_mask_len);
+ eventmsgs = kzalloc(size, GFP_KERNEL);
+ if (!eventmsgs)
+ return -ENOMEM;
+
+ eventmsgs->version = EVENTMSGS_VER;
+ eventmsgs->command = EVENTMSGS_NONE;
+ eventmsgs->len = fweh->event_mask_len;
+ eventmsgs->maxgetsize = fweh->event_mask_len;
+
+ err = brcmf_fil_iovar_data_get(ifp, "event_msgs_ext", eventmsgs, size);
+ if (!err) {
+ fw_len = eventmsgs->len;
+ memcpy(fweh->event_mask, eventmsgs->mask,
+ min_t(u8, fw_len, fweh->event_mask_len));
+ if (fw_len != fweh->event_mask_len)
+ brcmf_dbg(EVENT, "event_msgs_ext returned mask len=%u, driver len=%u\n",
+ fw_len, fweh->event_mask_len);
+ }
+
+ kfree(eventmsgs);
+ return err;
+}
+
+static int brcmf_fweh_set_event_mask(struct brcmf_if *ifp)
+{
+ struct brcmf_pub *drvr = ifp->drvr;
+ struct brcmf_fweh_info *fweh = drvr->fweh;
+ struct brcmf_eventmsgs_ext_le *eventmsgs;
+ size_t size;
+ int err;
+
+ if (!brcmf_fweh_use_event_msgs_ext(ifp))
+ return brcmf_fil_iovar_data_set(ifp, "event_msgs",
+ fweh->event_mask,
+ fweh->event_mask_len);
+
+ if (fweh->event_mask_len > 0xff)
+ return -E2BIG;
+
+ size = struct_size(eventmsgs, mask, fweh->event_mask_len);
+ eventmsgs = kzalloc(size, GFP_KERNEL);
+ if (!eventmsgs)
+ return -ENOMEM;
+
+ eventmsgs->version = EVENTMSGS_VER;
+ eventmsgs->command = EVENTMSGS_SET_MASK;
+ eventmsgs->len = fweh->event_mask_len;
+ memcpy(eventmsgs->mask, fweh->event_mask, fweh->event_mask_len);
+
+ err = brcmf_fil_iovar_data_set(ifp, "event_msgs_ext", eventmsgs, size);
+
+ kfree(eventmsgs);
+ return err;
+}
+
+/**
+ * brcmf_fweh_init_events() - initialize firmware event mask.
+ *
+ * @ifp: primary interface object.
+ */
+int brcmf_fweh_init_events(struct brcmf_if *ifp)
+{
+ struct brcmf_pub *drvr = ifp->drvr;
+ struct brcmf_fweh_info *fweh = drvr->fweh;
+ const char *iovar = brcmf_fweh_event_mask_iovar(ifp);
+ int err;
+
+ brcmf_dbg(EVENT, "using %s for firmware event masks (len=%u)\n", iovar,
+ fweh->event_mask_len);
+
+ err = brcmf_fweh_get_event_mask(ifp);
+ if (err) {
+ bphy_err(drvr, "Get %s error (%d)\n", iovar, err);
+ return err;
+ }
+
+ /*
+ * BRCMF_E_IF can safely be used to set the appropriate bit in the
+ * event mask as the firmware event code is guaranteed to match the
+ * value of BRCMF_E_IF because it is old cruft that all vendors have.
+ */
+ brcmf_dbg(EVENT, "enable event IF\n");
+ setbit(fweh->event_mask, BRCMF_E_IF);
+
+ err = brcmf_fweh_set_event_mask(ifp);
+ if (err)
+ bphy_err(drvr, "Set %s error (%d)\n", iovar, err);
+
+ return err;
+}
+
/**
* brcmf_fweh_activate_events() - enables firmware events registered.
*
@@ -439,6 +563,7 @@ int brcmf_fweh_activate_events(struct brcmf_if *ifp)
{
struct brcmf_fweh_info *fweh = ifp->drvr->fweh;
enum brcmf_fweh_event_code code;
+ const char *iovar = brcmf_fweh_event_mask_iovar(ifp);
int i, err;
memset(fweh->event_mask, 0, fweh->event_mask_len);
@@ -459,10 +584,9 @@ int brcmf_fweh_activate_events(struct brcmf_if *ifp)
if (!brcmf_fwvid_activate_events(ifp))
return 0;
- err = brcmf_fil_iovar_data_set(ifp, "event_msgs", fweh->event_mask,
- fweh->event_mask_len);
+ err = brcmf_fweh_set_event_mask(ifp);
if (err)
- bphy_err(fweh->drvr, "Set event_msgs error (%d)\n", err);
+ bphy_err(fweh->drvr, "Set %s error (%d)\n", iovar, err);
return err;
}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h
index e327dd58d2..53c4b58e63 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h
@@ -356,6 +356,7 @@ int brcmf_fweh_register(struct brcmf_pub *drvr, enum brcmf_fweh_event_code code,
void *data));
void brcmf_fweh_unregister(struct brcmf_pub *drvr,
enum brcmf_fweh_event_code code);
+int brcmf_fweh_init_events(struct brcmf_if *ifp);
int brcmf_fweh_activate_events(struct brcmf_if *ifp);
void brcmf_fweh_process_event(struct brcmf_pub *drvr,
struct brcmf_event *event_packet,
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
index e74a23e118..d591f17960 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
@@ -1236,4 +1236,30 @@ struct brcmf_mkeep_alive_pkt_le {
u8 data[];
} __packed;
+enum event_msgs_ext_command {
+ EVENTMSGS_NONE = 0,
+ EVENTMSGS_SET_BIT = 1,
+ EVENTMSGS_RESET_BIT = 2,
+ EVENTMSGS_SET_MASK = 3,
+};
+
+#define EVENTMSGS_VER 1
+
+/**
+ * struct brcmf_eventmsgs_ext_le - event_msgs_ext iovar data
+ *
+ * @version: structure version.
+ * @command: requested operation.
+ * @len: mask size for set, actual firmware mask size for get.
+ * @maxgetsize: maximum mask size that may be returned by firmware.
+ * @mask: event mask.
+ */
+struct brcmf_eventmsgs_ext_le {
+ u8 version;
+ u8 command;
+ u8 len;
+ u8 maxgetsize;
+ u8 mask[] __counted_by(len);
+};
+
#endif /* FWIL_TYPES_H_ */
--
2.55.0
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH 08/13] wifi: brcmfmac: Handle larger firmware band lists
2026-09-25 13:43 [PATCH 00/13] wifi: brcmfmac: Add BCM4388 support Michael Reeves via B4 Relay
` (6 preceding siblings ...)
2026-09-25 13:43 ` [PATCH 07/13] wifi: brcmfmac: Use event_msgs_ext for event masks Michael Reeves via B4 Relay
@ 2026-09-25 13:43 ` Michael Reeves via B4 Relay
2026-09-25 13:43 ` [PATCH 09/13] wifi: brcmfmac: Accept newer BSS information versions Michael Reeves via B4 Relay
` (4 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Michael Reeves via B4 Relay @ 2026-09-25 13:43 UTC (permalink / raw)
To: Arend van Spriel, Pieter-Paul Giesberts, Franky Lin (Zhenhui),
Mathy Vanhoef, Kalle Valo, Kees Cook, Gustavo A. R. Silva
Cc: linux-wireless, brcm80211, brcm80211-dev-list.pdl, asahi,
Hector Martin, Daniel Berlin, Janne Grunau, linux-kernel,
Hante Meuleman, linux-hardening, Michael Reeves
From: Michael Reeves <michael.reeves077@gmail.com>
Some firmware returns more than two entries from BRCMF_C_GET_BANDLIST.
The fixed two-band response buffer cannot accommodate these lists.
Use a larger bounded buffer and register only the 2.4 GHz and 5 GHz
bands handled by this driver path. Ignore duplicate or unknown entries
so they do not prevent wiphy setup. Skip unsupported bands in chanspec
lists and scan results before decoding channels.
Co-developed-by: Hector Martin <marcan@marcan.st>
Signed-off-by: Hector Martin <marcan@marcan.st>
Signed-off-by: Michael Reeves <michael.reeves077@gmail.com>
---
.../broadcom/brcm80211/brcmfmac/cfg80211.c | 48 +++++++++++++++++++---
1 file changed, 43 insertions(+), 5 deletions(-)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index 9d8ce7bb04..5d99708007 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -105,6 +105,8 @@
#define BRCMF_MAX_CHANSPEC_LIST \
(BRCMF_DCMD_MEDLEN / sizeof(__le32) - 1)
+#define BRCMF_MAX_BANDLIST_ENTRIES 16
+
struct brcmf_dump_survey {
u32 obss;
u32 ibss;
@@ -3385,6 +3387,17 @@ brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
return err;
}
+static bool brcmf_chanspec_supported(struct brcmf_cfg80211_info *cfg, u16 chanspec)
+{
+ u16 band = chanspec & BRCMU_CHSPEC_D11AC_BND_MASK;
+
+ if (cfg->d11inf.io_type != BRCMU_D11AC_IOTYPE)
+ return true;
+
+ return band == BRCMU_CHSPEC_D11AC_BND_2G ||
+ band == BRCMU_CHSPEC_D11AC_BND_5G;
+}
+
static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
struct brcmf_bss_info_le *bi)
{
@@ -3406,6 +3419,9 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
return -EINVAL;
}
+ if (!brcmf_chanspec_supported(cfg, le16_to_cpu(bi->chanspec)))
+ return 0;
+
if (!bi->ctl_ch) {
ch.chspec = le16_to_cpu(bi->chanspec);
cfg->d11inf.decchspec(&ch);
@@ -7163,6 +7179,8 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
for (i = 0; i < total; i++) {
ch.chspec = (u16)le32_to_cpu(list->element[i]);
+ if (!brcmf_chanspec_supported(cfg, ch.chspec))
+ continue;
cfg->d11inf.decchspec(&ch);
if (ch.band == BRCMU_CHAN_BAND_2G) {
@@ -7764,7 +7782,7 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
struct ieee80211_supported_band *band;
u16 max_interfaces = 0;
bool gscan;
- __le32 bandlist[3];
+ __le32 bandlist[BRCMF_MAX_BANDLIST_ENTRIES];
u32 n_bands;
int err, i;
@@ -7856,8 +7874,20 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
}
/* first entry in bandlist is number of bands */
n_bands = le32_to_cpu(bandlist[0]);
- for (i = 1; i <= n_bands && i < ARRAY_SIZE(bandlist); i++) {
- if (bandlist[i] == cpu_to_le32(WLC_BAND_2G)) {
+ if (n_bands >= ARRAY_SIZE(bandlist)) {
+ brcmf_dbg(INFO, "bandlist count %u exceeds buffer entries %zu\n",
+ n_bands, ARRAY_SIZE(bandlist) - 1);
+ n_bands = ARRAY_SIZE(bandlist) - 1;
+ }
+
+ for (i = 1; i <= n_bands; i++) {
+ u32 band_id = le32_to_cpu(bandlist[i]);
+
+ switch (band_id) {
+ case WLC_BAND_2G:
+ if (wiphy->bands[NL80211_BAND_2GHZ])
+ break;
+
band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz),
GFP_KERNEL);
if (!band)
@@ -7873,8 +7903,11 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
band->n_channels = ARRAY_SIZE(__wl_2ghz_channels);
wiphy->bands[NL80211_BAND_2GHZ] = band;
- }
- if (bandlist[i] == cpu_to_le32(WLC_BAND_5G)) {
+ break;
+ case WLC_BAND_5G:
+ if (wiphy->bands[NL80211_BAND_5GHZ])
+ break;
+
band = kmemdup(&__wl_band_5ghz, sizeof(__wl_band_5ghz),
GFP_KERNEL);
if (!band)
@@ -7890,6 +7923,11 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
band->n_channels = ARRAY_SIZE(__wl_5ghz_channels);
wiphy->bands[NL80211_BAND_5GHZ] = band;
+ break;
+ default:
+ brcmf_dbg(INFO, "ignoring unsupported band %u\n",
+ band_id);
+ break;
}
}
--
2.55.0
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH 09/13] wifi: brcmfmac: Accept newer BSS information versions
2026-09-25 13:43 [PATCH 00/13] wifi: brcmfmac: Add BCM4388 support Michael Reeves via B4 Relay
` (7 preceding siblings ...)
2026-09-25 13:43 ` [PATCH 08/13] wifi: brcmfmac: Handle larger firmware band lists Michael Reeves via B4 Relay
@ 2026-09-25 13:43 ` Michael Reeves via B4 Relay
2026-09-25 13:43 ` [PATCH 10/13] wifi: brcmfmac: Support versioned scan parameter layouts Michael Reeves via B4 Relay
` (3 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Michael Reeves via B4 Relay @ 2026-09-25 13:43 UTC (permalink / raw)
To: Arend van Spriel, Pieter-Paul Giesberts, Franky Lin (Zhenhui),
Mathy Vanhoef, Kalle Valo, Kees Cook, Gustavo A. R. Silva
Cc: linux-wireless, brcm80211, brcm80211-dev-list.pdl, asahi,
Hector Martin, Daniel Berlin, Janne Grunau, linux-kernel,
Hante Meuleman, linux-hardening, Michael Reeves
From: Hector Martin <marcan@marcan.st>
The driver accepts only BSS information version 109, rejecting scan
results with newer versions even though the common fields used for
reporting remain compatible.
Accept versions 109 through 112 when reporting scan results.
Signed-off-by: Hector Martin <marcan@marcan.st>
Signed-off-by: Michael Reeves <michael.reeves077@gmail.com>
---
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 5 +++--
drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h | 3 ++-
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index 5d99708007..4f1768c8bb 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -3484,8 +3484,9 @@ static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg)
bss_list = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
if (bss_list->count != 0 &&
- bss_list->version != BRCMF_BSS_INFO_VERSION) {
- bphy_err(drvr, "Version %d != WL_BSS_INFO_VERSION\n",
+ (bss_list->version < BRCMF_BSS_INFO_MIN_VERSION ||
+ bss_list->version > BRCMF_BSS_INFO_MAX_VERSION)) {
+ bphy_err(drvr, "BSS info version %d unsupported\n",
bss_list->version);
return -EOPNOTSUPP;
}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
index d591f17960..c786da4336 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
@@ -18,7 +18,8 @@
#define BRCMF_ARP_OL_HOST_AUTO_REPLY 0x00000004
#define BRCMF_ARP_OL_PEER_AUTO_REPLY 0x00000008
-#define BRCMF_BSS_INFO_VERSION 109 /* curr ver of brcmf_bss_info_le struct */
+#define BRCMF_BSS_INFO_MIN_VERSION 109
+#define BRCMF_BSS_INFO_MAX_VERSION 112
#define BRCMF_BSS_RSSI_ON_CHANNEL 0x0004
#define BRCMF_STA_BRCM 0x00000001 /* Running a Broadcom driver */
--
2.55.0
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH 10/13] wifi: brcmfmac: Support versioned scan parameter layouts
2026-09-25 13:43 [PATCH 00/13] wifi: brcmfmac: Add BCM4388 support Michael Reeves via B4 Relay
` (8 preceding siblings ...)
2026-09-25 13:43 ` [PATCH 09/13] wifi: brcmfmac: Accept newer BSS information versions Michael Reeves via B4 Relay
@ 2026-09-25 13:43 ` Michael Reeves via B4 Relay
2026-09-25 13:43 ` [PATCH 11/13] wifi: brcmfmac: Support versioned join requests Michael Reeves via B4 Relay
` (2 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Michael Reeves via B4 Relay @ 2026-09-25 13:43 UTC (permalink / raw)
To: Arend van Spriel, Pieter-Paul Giesberts, Franky Lin (Zhenhui),
Mathy Vanhoef, Kalle Valo, Kees Cook, Gustavo A. R. Silva
Cc: linux-wireless, brcm80211, brcm80211-dev-list.pdl, asahi,
Hector Martin, Daniel Berlin, Janne Grunau, linux-kernel,
Hante Meuleman, linux-hardening, Michael Reeves
From: Daniel Berlin <dberlin@dberlin.org>
Newer firmware uses scan parameter layouts with version headers, wider
scan flags, and additional fields. Requests must match the layout
expected by firmware.
Select builders for versions 1 through 4 from the scan_ver response and
use the selected layout for both scan requests and aborts. Fall back to
version 1 when version discovery is unavailable.
Signed-off-by: Daniel Berlin <dberlin@dberlin.org>
Co-developed-by: Hector Martin <marcan@marcan.st>
Signed-off-by: Hector Martin <marcan@marcan.st>
Co-developed-by: Michael Reeves <michael.reeves077@gmail.com>
Signed-off-by: Michael Reeves <michael.reeves077@gmail.com>
---
.../wireless/broadcom/brcm80211/brcmfmac/Makefile | 1 +
.../broadcom/brcm80211/brcmfmac/cfg80211.c | 203 ++--------
.../wireless/broadcom/brcm80211/brcmfmac/core.h | 17 +
.../wireless/broadcom/brcm80211/brcmfmac/feature.c | 46 ++-
.../broadcom/brcm80211/brcmfmac/fwil_types.h | 65 ++++
.../broadcom/brcm80211/brcmfmac/scan_param.c | 416 +++++++++++++++++++++
.../broadcom/brcm80211/brcmfmac/scan_param.h | 13 +
7 files changed, 594 insertions(+), 167 deletions(-)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile
index e5ca0f5118..5281922867 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile
@@ -25,6 +25,7 @@ brcmfmac-objs += \
btcoex.o \
vendor.o \
pno.o \
+ scan_param.o \
xtlv.o
brcmfmac-$(CONFIG_BRCMFMAC_PROTO_BCDC) += \
bcdc.o \
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index 4f1768c8bb..2ce7041be6 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -1061,134 +1061,11 @@ bool brcmf_is_apmode_operating(struct wiphy *wiphy)
return ret;
}
-static void brcmf_scan_params_v2_to_v1(struct brcmf_scan_params_v2_le *params_v2_le,
- struct brcmf_scan_params_le *params_le)
-{
- size_t params_size;
- u32 ch;
- int n_channels, n_ssids;
-
- memcpy(¶ms_le->ssid_le, ¶ms_v2_le->ssid_le,
- sizeof(params_le->ssid_le));
- memcpy(¶ms_le->bssid, ¶ms_v2_le->bssid,
- sizeof(params_le->bssid));
-
- params_le->bss_type = params_v2_le->bss_type;
- params_le->scan_type = le32_to_cpu(params_v2_le->scan_type);
- params_le->nprobes = params_v2_le->nprobes;
- params_le->active_time = params_v2_le->active_time;
- params_le->passive_time = params_v2_le->passive_time;
- params_le->home_time = params_v2_le->home_time;
- params_le->channel_num = params_v2_le->channel_num;
-
- ch = le32_to_cpu(params_v2_le->channel_num);
- n_channels = ch & BRCMF_SCAN_PARAMS_COUNT_MASK;
- n_ssids = ch >> BRCMF_SCAN_PARAMS_NSSID_SHIFT;
-
- params_size = sizeof(u16) * n_channels;
- if (n_ssids > 0) {
- params_size = roundup(params_size, sizeof(u32));
- params_size += sizeof(struct brcmf_ssid_le) * n_ssids;
- }
-
- memcpy(¶ms_le->channel_list[0],
- ¶ms_v2_le->channel_list[0], params_size);
-}
-
-static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg,
- struct brcmf_scan_params_v2_le *params_le,
- struct cfg80211_scan_request *request)
-{
- u32 n_ssids;
- u32 n_channels;
- s32 i;
- s32 offset;
- u16 chanspec;
- char *ptr;
- int length;
- struct brcmf_ssid_le ssid_le;
-
- eth_broadcast_addr(params_le->bssid);
-
- length = BRCMF_SCAN_PARAMS_V2_FIXED_SIZE;
-
- params_le->version = cpu_to_le16(BRCMF_SCAN_PARAMS_VERSION_V2);
- params_le->bss_type = DOT11_BSSTYPE_ANY;
- params_le->scan_type = cpu_to_le32(BRCMF_SCANTYPE_ACTIVE);
- params_le->channel_num = 0;
- params_le->nprobes = cpu_to_le32(-1);
- params_le->active_time = cpu_to_le32(-1);
- params_le->passive_time = cpu_to_le32(-1);
- params_le->home_time = cpu_to_le32(-1);
- memset(¶ms_le->ssid_le, 0, sizeof(params_le->ssid_le));
-
- /* Scan abort */
- if (!request) {
- length += sizeof(u16);
- params_le->channel_num = cpu_to_le32(1);
- params_le->channel_list[0] = cpu_to_le16(-1);
- params_le->length = cpu_to_le16(length);
- return;
- }
-
- n_ssids = request->n_ssids;
- n_channels = request->n_channels;
-
- /* Copy channel array if applicable */
- brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n",
- n_channels);
- if (n_channels > 0) {
- length += roundup(sizeof(u16) * n_channels, sizeof(u32));
- for (i = 0; i < n_channels; i++) {
- chanspec = channel_to_chanspec(&cfg->d11inf,
- request->channels[i]);
- brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n",
- request->channels[i]->hw_value, chanspec);
- params_le->channel_list[i] = cpu_to_le16(chanspec);
- }
- } else {
- brcmf_dbg(SCAN, "Scanning all channels\n");
- }
-
- /* Copy ssid array if applicable */
- brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids);
- if (n_ssids > 0) {
- offset = offsetof(struct brcmf_scan_params_v2_le, channel_list) +
- n_channels * sizeof(u16);
- offset = roundup(offset, sizeof(u32));
- length += sizeof(ssid_le) * n_ssids;
- ptr = (char *)params_le + offset;
- for (i = 0; i < n_ssids; i++) {
- memset(&ssid_le, 0, sizeof(ssid_le));
- ssid_le.SSID_len =
- cpu_to_le32(request->ssids[i].ssid_len);
- memcpy(ssid_le.SSID, request->ssids[i].ssid,
- request->ssids[i].ssid_len);
- if (!ssid_le.SSID_len)
- brcmf_dbg(SCAN, "%d: Broadcast scan\n", i);
- else
- brcmf_dbg(SCAN, "%d: scan for %.32s size=%d\n",
- i, ssid_le.SSID, ssid_le.SSID_len);
- memcpy(ptr, &ssid_le, sizeof(ssid_le));
- ptr += sizeof(ssid_le);
- }
- } else {
- brcmf_dbg(SCAN, "Performing passive scan\n");
- params_le->scan_type = cpu_to_le32(BRCMF_SCANTYPE_PASSIVE);
- }
- params_le->length = cpu_to_le16(length);
- /* Adding mask to channel numbers */
- params_le->channel_num =
- cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
- (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
-}
-
s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
struct brcmf_if *ifp, bool aborted,
bool fw_abort)
{
struct brcmf_pub *drvr = cfg->pub;
- struct brcmf_scan_params_v2_le params_v2_le;
struct cfg80211_scan_request *scan_request;
u64 reqid;
u32 bucket;
@@ -1204,29 +1081,29 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
timer_delete_sync(&cfg->escan_timeout);
if (fw_abort) {
+ void *params;
+ u32 params_size;
+
/* Do a scan abort to stop the driver's scan engine */
brcmf_dbg(SCAN, "ABORT scan in firmware\n");
- brcmf_escan_prep(cfg, ¶ms_v2_le, NULL);
-
- /* E-Scan (or anyother type) can be aborted by SCAN */
- if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_V2)) {
- err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
- ¶ms_v2_le,
- sizeof(params_v2_le));
- } else {
- struct brcmf_scan_params_le params_le;
-
- brcmf_scan_params_v2_to_v1(¶ms_v2_le, ¶ms_le);
- err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
- ¶ms_le,
- sizeof(params_le));
+ params = drvr->scan_param_handler.get_struct_for_request(cfg,
+ ¶ms_size,
+ NULL);
+ if (!params) {
+ bphy_err(drvr, "Scan abort failed to prepare params\n");
+ goto scan_done;
}
+ /* E-Scan (or any other type) can be aborted by SCAN */
+ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN, params,
+ params_size);
+ kfree(params);
if (err)
bphy_err(drvr, "Scan abort failed\n");
}
+scan_done:
brcmf_scan_config_mpc(ifp, 1);
/*
@@ -1446,46 +1323,38 @@ brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
struct cfg80211_scan_request *request)
{
struct brcmf_pub *drvr = cfg->pub;
- s32 params_size = BRCMF_SCAN_PARAMS_V2_FIXED_SIZE +
- offsetof(struct brcmf_escan_params_le, params_v2_le);
struct brcmf_escan_params_le *params;
+ void *scan_params;
+ u32 scan_params_size;
+ u32 params_size;
s32 err = 0;
brcmf_dbg(SCAN, "E-SCAN START\n");
- if (request != NULL) {
- /* Allocate space for populating ssids in struct */
- params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
-
- /* Allocate space for populating ssids in struct */
- params_size += sizeof(struct brcmf_ssid_le) * request->n_ssids;
+ scan_params = drvr->scan_param_handler.get_struct_for_request(cfg,
+ &scan_params_size,
+ request);
+ if (!scan_params) {
+ err = -ENOMEM;
+ goto exit;
}
+ params_size = offsetof(struct brcmf_escan_params_le, params_v4_le) +
+ scan_params_size;
+ if (params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN) {
+ err = -E2BIG;
+ goto exit_scan_params;
+ }
params = kzalloc(params_size, GFP_KERNEL);
if (!params) {
err = -ENOMEM;
- goto exit;
+ goto exit_scan_params;
}
- BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
- brcmf_escan_prep(cfg, ¶ms->params_v2_le, request);
-
- params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION_V2);
+ unsafe_memcpy(¶ms->params_v4_le, scan_params, scan_params_size,
+ /* composite flex-array sized by allocation above */);
+ kfree(scan_params);
- if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_V2)) {
- struct brcmf_escan_params_le *params_v1;
-
- params_size -= BRCMF_SCAN_PARAMS_V2_FIXED_SIZE;
- params_size += BRCMF_SCAN_PARAMS_FIXED_SIZE;
- params_v1 = kzalloc(params_size, GFP_KERNEL);
- if (!params_v1) {
- err = -ENOMEM;
- goto exit_params;
- }
- params_v1->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
- brcmf_scan_params_v2_to_v1(¶ms->params_v2_le, ¶ms_v1->params_le);
- kfree(params);
- params = params_v1;
- }
+ params->version = cpu_to_le32(drvr->scan_param_handler.version);
params->action = cpu_to_le16(WL_ESCAN_ACTION_START);
params->sync_id = cpu_to_le16(0x1234);
@@ -1498,8 +1367,10 @@ brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
bphy_err(drvr, "error (%d)\n", err);
}
-exit_params:
kfree(params);
+ return err;
+exit_scan_params:
+ kfree(scan_params);
exit:
return err;
}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
index 399b6810e3..fe909f7add 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
@@ -97,6 +97,22 @@ struct brcmf_rev_info {
u32 nvramrev;
};
+struct brcmf_cfg80211_info;
+struct cfg80211_scan_request;
+
+/**
+ * struct scan_param_struct_handler - firmware scan parameter operations.
+ *
+ * @version: scan parameter version used by firmware.
+ * @get_struct_for_request: build a scan parameter structure for @request.
+ */
+struct scan_param_struct_handler {
+ u8 version;
+ void *(*get_struct_for_request)(struct brcmf_cfg80211_info *cfg,
+ u32 *struct_size,
+ struct cfg80211_scan_request *request);
+};
+
/* Common structure for module and instance linkage */
struct brcmf_pub {
/* Linkage ponters */
@@ -145,6 +161,7 @@ struct brcmf_pub {
u8 sta_mac_idx;
const struct brcmf_fwvid_ops *vops;
void *vdata;
+ struct scan_param_struct_handler scan_param_handler;
};
/* forward declarations */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
index d289451644..44c0082e21 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
@@ -17,6 +17,7 @@
#include "fwvid.h"
#include "feature.h"
#include "common.h"
+#include "scan_param.h"
#define BRCMF_FW_UNSUPPORTED 23
@@ -296,6 +297,46 @@ static void brcmf_feat_firmware_capabilities(struct brcmf_if *ifp)
}
}
+static void brcmf_feat_scan_params(struct brcmf_if *ifp)
+{
+ struct brcmf_pub *drvr = ifp->drvr;
+ struct brcmf_scan_version_le scan_ver = {};
+ u32 version = 1;
+ u32 scalar_version;
+ int err;
+
+ err = brcmf_fil_iovar_data_get(ifp, "scan_ver", &scan_ver,
+ sizeof(scan_ver));
+ if (!err) {
+ version = le16_to_cpu(scan_ver.scan_ver_major);
+ if (!version)
+ version = le16_to_cpu(scan_ver.version);
+ if (version >= BRCMF_SCAN_PARAMS_VERSION_V2)
+ drvr->feat_flags |= BIT(BRCMF_FEAT_SCAN_V2);
+ } else {
+ err = brcmf_fil_iovar_int_get(ifp, "scan_ver",
+ &scalar_version);
+ if (!err) {
+ version = scalar_version;
+ if (version >= BRCMF_SCAN_PARAMS_VERSION_V2)
+ drvr->feat_flags |= BIT(BRCMF_FEAT_SCAN_V2);
+ } else {
+ brcmf_dbg(TRACE, "scan_ver unavailable (%d), using v1 scan params\n",
+ err);
+ }
+ }
+
+ err = brcmf_scan_param_setup_for_version(drvr, version);
+ if (!err) {
+ brcmf_dbg(INFO, "using scan params v%u\n", version);
+ return;
+ }
+
+ bphy_err(drvr, "unsupported scan params v%u, using v1\n", version);
+ drvr->feat_flags &= ~BIT(BRCMF_FEAT_SCAN_V2);
+ brcmf_scan_param_setup_for_version(drvr, 1);
+}
+
/**
* brcmf_feat_fwcap_debugfs_read() - expose firmware capabilities to debugfs.
*
@@ -385,7 +426,7 @@ void brcmf_feat_attach(struct brcmf_pub *drvr)
ifp->drvr->feat_flags |= BIT(BRCMF_FEAT_SCAN_RANDOM_MAC);
brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_FWSUP, "sup_wpa");
- brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_SCAN_V2, "scan_ver");
+ brcmf_feat_scan_params(ifp);
brcmf_feat_event_msgs_ext(ifp);
brcmf_feat_wlc_version_overrides(drvr);
@@ -400,6 +441,9 @@ void brcmf_feat_attach(struct brcmf_pub *drvr)
ifp->drvr->feat_flags &= ~drvr->settings->feature_disable;
}
+ if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_V2))
+ brcmf_scan_param_setup_for_version(drvr, 1);
+
/* set chip related quirks */
switch (drvr->bus_if->chip) {
case BRCM_CC_43236_CHIP_ID:
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
index c786da4336..dbba883bba 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
@@ -53,6 +53,8 @@
/* version of brcmf_scan_params structure */
#define BRCMF_SCAN_PARAMS_VERSION_V2 2
+#define BRCMF_SCAN_PARAMS_VERSION_V3 3
+#define BRCMF_SCAN_PARAMS_VERSION_V4 4
/* masks for channel and ssid count */
#define BRCMF_SCAN_PARAMS_COUNT_MASK 0x0000ffff
@@ -63,6 +65,13 @@
#define BRCMF_SCANTYPE_ACTIVE 0
#define BRCMF_SCANTYPE_PASSIVE 1
+#define BRCMF_SCANFLAGS_LOW_PRIO BIT(1)
+#define BRCMF_SCANFLAGS_LOW_POWER BIT(12)
+#define BRCMF_SCANFLAGS_HIGH_ACCURACY BIT(13)
+#define BRCMF_SCANFLAGS_LOW_SPAN BIT(14)
+
+#define BRCMF_SCANSSID_INC_RNR BIT(1)
+
#define BRCMF_WSEC_MAX_PSK_LEN 32
#define BRCMF_WSEC_PASSPHRASE BIT(0)
@@ -451,6 +460,45 @@ struct brcmf_scan_params_v2_le {
};
};
+struct brcmf_scan_params_v3_le {
+ __le16 version;
+ __le16 length;
+ struct brcmf_ssid_le ssid_le;
+ u8 bssid[ETH_ALEN];
+ s8 bss_type;
+ u8 ssid_type;
+ __le32 scan_type;
+ __le32 nprobes;
+ __le32 active_time;
+ __le32 passive_time;
+ __le32 home_time;
+ __le32 channel_num;
+ union {
+ __le16 padding;
+ DECLARE_FLEX_ARRAY(__le16, channel_list);
+ };
+};
+
+struct brcmf_scan_params_v4_le {
+ __le16 version;
+ __le16 length;
+ struct brcmf_ssid_le ssid_le;
+ u8 bssid[ETH_ALEN];
+ s8 bss_type;
+ u8 ssid_type;
+ __le32 scan_type;
+ __le32 scan_type_ext;
+ __le32 nprobes;
+ __le32 active_time;
+ __le32 passive_time;
+ __le32 home_time;
+ __le32 channel_num;
+ union {
+ __le16 padding;
+ DECLARE_FLEX_ARRAY(__le16, channel_list);
+ };
+};
+
struct brcmf_scan_results {
u32 buflen;
u32 version;
@@ -465,6 +513,8 @@ struct brcmf_escan_params_le {
union {
struct brcmf_scan_params_le params_le;
struct brcmf_scan_params_v2_le params_v2_le;
+ struct brcmf_scan_params_v3_le params_v3_le;
+ struct brcmf_scan_params_v4_le params_v4_le;
};
};
@@ -1083,6 +1133,21 @@ struct brcmf_pno_scanresults_v2_le {
__le32 scan_ch_bucket;
};
+/**
+ * struct brcmf_scan_version_le - scan interface version.
+ *
+ * @version: version of this structure.
+ * @length: length of this structure.
+ * @scan_ver_major: scan parameter version supported by firmware.
+ */
+struct brcmf_scan_version_le {
+ __le16 version;
+ __le16 length;
+ __le16 scan_ver_major;
+};
+
+#define BRCMF_SCAN_VERSION_VERSION 1
+
/**
* struct brcmf_pno_macaddr_le - to configure PNO macaddr randomization.
*
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/scan_param.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/scan_param.c
new file mode 100644
index 0000000000..43e0fd16f1
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/scan_param.c
@@ -0,0 +1,416 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (c) 2023 Daniel Berlin
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <net/cfg80211.h>
+
+#include "cfg80211.h"
+#include "core.h"
+#include "debug.h"
+#include "fwil_types.h"
+#include "scan_param.h"
+
+static void brcmf_scan_param_set_defaults(u8 *bssid,
+ s8 *bss_type, __le32 *channel_num,
+ __le32 *nprobes,
+ __le32 *active_time,
+ __le32 *passive_time,
+ __le32 *home_time)
+{
+ eth_broadcast_addr(bssid);
+ *bss_type = DOT11_BSSTYPE_ANY;
+ *channel_num = 0;
+ *nprobes = cpu_to_le32(-1);
+ *active_time = cpu_to_le32(-1);
+ *passive_time = cpu_to_le32(-1);
+ *home_time = cpu_to_le32(-1);
+}
+
+static void brcmf_scan_param_copy_chanspecs(struct brcmf_cfg80211_info *cfg,
+ __le16 *dest_channels,
+ struct ieee80211_channel **channels,
+ u32 n_channels)
+{
+ u32 chanspec;
+ int i;
+
+ for (i = 0; i < n_channels; i++) {
+ chanspec = channel_to_chanspec(&cfg->d11inf, channels[i]);
+ brcmf_dbg(SCAN, "Chan: %d, channel spec: %x\n",
+ channels[i]->hw_value, chanspec);
+ dest_channels[i] = cpu_to_le16(chanspec);
+ }
+}
+
+static void brcmf_scan_param_copy_ssids(char *dest_ssids,
+ struct cfg80211_ssid *ssids,
+ u32 n_ssids)
+{
+ struct brcmf_ssid_le ssid_le;
+ int i;
+
+ for (i = 0; i < n_ssids; i++) {
+ memset(&ssid_le, 0, sizeof(ssid_le));
+ ssid_le.SSID_len = cpu_to_le32(ssids[i].ssid_len);
+ memcpy(ssid_le.SSID, ssids[i].ssid, ssids[i].ssid_len);
+ memcpy(dest_ssids, &ssid_le, sizeof(ssid_le));
+ dest_ssids += sizeof(ssid_le);
+ }
+}
+
+static u32
+brcmf_scan_param_tail_size(const struct cfg80211_scan_request *request,
+ u32 params_size)
+{
+ if (!request)
+ return params_size + sizeof(u16);
+
+ params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
+ params_size += sizeof(struct brcmf_ssid_le) * request->n_ssids;
+
+ return params_size;
+}
+
+static u32 brcmf_scan_param_scan_flags(u32 nl80211_flags)
+{
+ u32 scan_flags = 0;
+
+ if (nl80211_flags & NL80211_SCAN_FLAG_LOW_SPAN)
+ scan_flags |= BRCMF_SCANFLAGS_LOW_SPAN;
+ if (nl80211_flags & NL80211_SCAN_FLAG_HIGH_ACCURACY)
+ scan_flags |= BRCMF_SCANFLAGS_HIGH_ACCURACY;
+ if (nl80211_flags & NL80211_SCAN_FLAG_LOW_POWER)
+ scan_flags |= BRCMF_SCANFLAGS_LOW_POWER;
+ if (nl80211_flags & NL80211_SCAN_FLAG_LOW_PRIORITY)
+ scan_flags |= BRCMF_SCANFLAGS_LOW_PRIO;
+
+ return scan_flags;
+}
+
+static void *
+brcmf_scan_param_get_prepped_struct_v1(struct brcmf_cfg80211_info *cfg,
+ u32 *struct_size,
+ struct cfg80211_scan_request *request)
+{
+ struct brcmf_scan_params_le *params_le;
+ u32 params_size;
+ u32 n_channels;
+ u32 n_ssids;
+ u32 length;
+ u8 scan_type = BRCMF_SCANTYPE_ACTIVE;
+
+ length = offsetof(struct brcmf_scan_params_le, channel_list);
+ params_size = brcmf_scan_param_tail_size(request, length);
+ params_le = kzalloc(params_size, GFP_KERNEL);
+ if (!params_le)
+ return NULL;
+
+ brcmf_scan_param_set_defaults(params_le->bssid,
+ ¶ms_le->bss_type,
+ ¶ms_le->channel_num,
+ ¶ms_le->nprobes,
+ ¶ms_le->active_time,
+ ¶ms_le->passive_time,
+ ¶ms_le->home_time);
+
+ if (!request) {
+ length += sizeof(u16);
+ params_le->channel_num = cpu_to_le32(1);
+ params_le->channel_list[0] = cpu_to_le16(-1);
+ goto done;
+ }
+
+ n_ssids = request->n_ssids;
+ n_channels = request->n_channels;
+
+ if (n_channels > 0) {
+ length += roundup(sizeof(u16) * n_channels, sizeof(u32));
+ brcmf_scan_param_copy_chanspecs(cfg, params_le->channel_list,
+ request->channels, n_channels);
+ } else {
+ brcmf_dbg(SCAN, "Scanning all channels\n");
+ }
+
+ if (n_ssids > 0) {
+ char *ptr;
+ u32 offset;
+
+ offset = offsetof(struct brcmf_scan_params_le, channel_list) +
+ n_channels * sizeof(u16);
+ offset = roundup(offset, sizeof(u32));
+ length += sizeof(struct brcmf_ssid_le) * n_ssids;
+ ptr = (char *)params_le + offset;
+ brcmf_scan_param_copy_ssids(ptr, request->ssids, n_ssids);
+ } else {
+ brcmf_dbg(SCAN, "Performing passive scan\n");
+ scan_type = BRCMF_SCANTYPE_PASSIVE;
+ }
+
+ if (request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY)
+ scan_type |= BRCMF_SCANFLAGS_LOW_PRIO;
+ params_le->scan_type = scan_type;
+ params_le->channel_num =
+ cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
+ (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
+
+done:
+ *struct_size = length;
+ return params_le;
+}
+
+static void *
+brcmf_scan_param_get_prepped_struct_v2(struct brcmf_cfg80211_info *cfg,
+ u32 *struct_size,
+ struct cfg80211_scan_request *request)
+{
+ struct brcmf_scan_params_v2_le *params_le;
+ u32 params_size;
+ u32 n_channels;
+ u32 n_ssids;
+ u32 length;
+ u32 scan_type = BRCMF_SCANTYPE_ACTIVE;
+
+ length = offsetof(struct brcmf_scan_params_v2_le, channel_list);
+ params_size = brcmf_scan_param_tail_size(request, length);
+ params_le = kzalloc(params_size, GFP_KERNEL);
+ if (!params_le)
+ return NULL;
+
+ params_le->version = cpu_to_le16(BRCMF_SCAN_PARAMS_VERSION_V2);
+ brcmf_scan_param_set_defaults(params_le->bssid,
+ ¶ms_le->bss_type,
+ ¶ms_le->channel_num,
+ ¶ms_le->nprobes,
+ ¶ms_le->active_time,
+ ¶ms_le->passive_time,
+ ¶ms_le->home_time);
+
+ if (!request) {
+ length += sizeof(u16);
+ params_le->channel_num = cpu_to_le32(1);
+ params_le->channel_list[0] = cpu_to_le16(-1);
+ params_le->length = cpu_to_le16(length);
+ goto done;
+ }
+
+ n_ssids = request->n_ssids;
+ n_channels = request->n_channels;
+
+ if (n_channels > 0) {
+ length += roundup(sizeof(u16) * n_channels, sizeof(u32));
+ brcmf_scan_param_copy_chanspecs(cfg, params_le->channel_list,
+ request->channels, n_channels);
+ } else {
+ brcmf_dbg(SCAN, "Scanning all channels\n");
+ }
+
+ if (n_ssids > 0) {
+ char *ptr;
+ u32 offset;
+
+ offset = offsetof(struct brcmf_scan_params_v2_le,
+ channel_list) + n_channels * sizeof(u16);
+ offset = roundup(offset, sizeof(u32));
+ length += sizeof(struct brcmf_ssid_le) * n_ssids;
+ ptr = (char *)params_le + offset;
+ brcmf_scan_param_copy_ssids(ptr, request->ssids, n_ssids);
+ } else {
+ brcmf_dbg(SCAN, "Performing passive scan\n");
+ scan_type = BRCMF_SCANTYPE_PASSIVE;
+ }
+
+ scan_type |= brcmf_scan_param_scan_flags(request->flags);
+ params_le->scan_type = cpu_to_le32(scan_type);
+ params_le->length = cpu_to_le16(length);
+ params_le->channel_num =
+ cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
+ (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
+
+done:
+ *struct_size = length;
+ return params_le;
+}
+
+static void *
+brcmf_scan_param_get_prepped_struct_v3(struct brcmf_cfg80211_info *cfg,
+ u32 *struct_size,
+ struct cfg80211_scan_request *request)
+{
+ struct brcmf_scan_params_v3_le *params_le;
+ u32 params_size;
+ u32 n_channels;
+ u32 n_ssids;
+ u32 length;
+ u32 scan_type = BRCMF_SCANTYPE_ACTIVE;
+
+ length = offsetof(struct brcmf_scan_params_v3_le, channel_list);
+ params_size = brcmf_scan_param_tail_size(request, length);
+ params_le = kzalloc(params_size, GFP_KERNEL);
+ if (!params_le)
+ return NULL;
+
+ params_le->version = cpu_to_le16(BRCMF_SCAN_PARAMS_VERSION_V3);
+ brcmf_scan_param_set_defaults(params_le->bssid,
+ ¶ms_le->bss_type,
+ ¶ms_le->channel_num,
+ ¶ms_le->nprobes,
+ ¶ms_le->active_time,
+ ¶ms_le->passive_time,
+ ¶ms_le->home_time);
+
+ if (!request) {
+ length += sizeof(u16);
+ params_le->channel_num = cpu_to_le32(1);
+ params_le->channel_list[0] = cpu_to_le16(-1);
+ params_le->length = cpu_to_le16(length);
+ goto done;
+ }
+
+ n_ssids = request->n_ssids;
+ n_channels = request->n_channels;
+
+ if (n_channels > 0) {
+ length += roundup(sizeof(u16) * n_channels, sizeof(u32));
+ brcmf_scan_param_copy_chanspecs(cfg, params_le->channel_list,
+ request->channels, n_channels);
+ } else {
+ brcmf_dbg(SCAN, "Scanning all channels\n");
+ }
+
+ if (n_ssids > 0) {
+ char *ptr;
+ u32 offset;
+
+ offset = offsetof(struct brcmf_scan_params_v3_le,
+ channel_list) + n_channels * sizeof(u16);
+ offset = roundup(offset, sizeof(u32));
+ length += sizeof(struct brcmf_ssid_le) * n_ssids;
+ ptr = (char *)params_le + offset;
+ brcmf_scan_param_copy_ssids(ptr, request->ssids, n_ssids);
+ } else {
+ brcmf_dbg(SCAN, "Performing passive scan\n");
+ scan_type = BRCMF_SCANTYPE_PASSIVE;
+ }
+
+ scan_type |= brcmf_scan_param_scan_flags(request->flags);
+ params_le->scan_type = cpu_to_le32(scan_type);
+ params_le->length = cpu_to_le16(length);
+ params_le->channel_num =
+ cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
+ (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
+ if (request->flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ)
+ params_le->ssid_type |= BRCMF_SCANSSID_INC_RNR;
+
+done:
+ *struct_size = length;
+ return params_le;
+}
+
+static void *
+brcmf_scan_param_get_prepped_struct_v4(struct brcmf_cfg80211_info *cfg,
+ u32 *struct_size,
+ struct cfg80211_scan_request *request)
+{
+ struct brcmf_scan_params_v4_le *params_le;
+ u32 params_size;
+ u32 n_channels;
+ u32 n_ssids;
+ u32 length;
+ u32 scan_type = BRCMF_SCANTYPE_ACTIVE;
+
+ length = offsetof(struct brcmf_scan_params_v4_le, channel_list);
+ params_size = brcmf_scan_param_tail_size(request, length);
+ params_le = kzalloc(params_size, GFP_KERNEL);
+ if (!params_le)
+ return NULL;
+
+ params_le->version = cpu_to_le16(BRCMF_SCAN_PARAMS_VERSION_V4);
+ brcmf_scan_param_set_defaults(params_le->bssid,
+ ¶ms_le->bss_type,
+ ¶ms_le->channel_num,
+ ¶ms_le->nprobes,
+ ¶ms_le->active_time,
+ ¶ms_le->passive_time,
+ ¶ms_le->home_time);
+
+ if (!request) {
+ length += sizeof(u16);
+ params_le->channel_num = cpu_to_le32(1);
+ params_le->channel_list[0] = cpu_to_le16(-1);
+ params_le->length = cpu_to_le16(length);
+ goto done;
+ }
+
+ n_ssids = request->n_ssids;
+ n_channels = request->n_channels;
+
+ if (n_channels > 0) {
+ length += roundup(sizeof(u16) * n_channels, sizeof(u32));
+ brcmf_scan_param_copy_chanspecs(cfg, params_le->channel_list,
+ request->channels, n_channels);
+ } else {
+ brcmf_dbg(SCAN, "Scanning all channels\n");
+ }
+
+ if (n_ssids > 0) {
+ char *ptr;
+ u32 offset;
+
+ offset = offsetof(struct brcmf_scan_params_v4_le,
+ channel_list) + n_channels * sizeof(u16);
+ offset = roundup(offset, sizeof(u32));
+ length += sizeof(struct brcmf_ssid_le) * n_ssids;
+ ptr = (char *)params_le + offset;
+ brcmf_scan_param_copy_ssids(ptr, request->ssids, n_ssids);
+ } else {
+ brcmf_dbg(SCAN, "Performing passive scan\n");
+ scan_type = BRCMF_SCANTYPE_PASSIVE;
+ }
+
+ scan_type |= brcmf_scan_param_scan_flags(request->flags);
+ params_le->scan_type = cpu_to_le32(scan_type);
+ params_le->length = cpu_to_le16(length);
+ params_le->channel_num =
+ cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
+ (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
+ if (request->flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ)
+ params_le->ssid_type |= BRCMF_SCANSSID_INC_RNR;
+
+done:
+ *struct_size = length;
+ return params_le;
+}
+
+int brcmf_scan_param_setup_for_version(struct brcmf_pub *drvr, u32 version)
+{
+ switch (version) {
+ case 1:
+ drvr->scan_param_handler.get_struct_for_request =
+ brcmf_scan_param_get_prepped_struct_v1;
+ break;
+ case 2:
+ drvr->scan_param_handler.get_struct_for_request =
+ brcmf_scan_param_get_prepped_struct_v2;
+ break;
+ case 3:
+ drvr->scan_param_handler.get_struct_for_request =
+ brcmf_scan_param_get_prepped_struct_v3;
+ break;
+ case 4:
+ drvr->scan_param_handler.get_struct_for_request =
+ brcmf_scan_param_get_prepped_struct_v4;
+ break;
+ default:
+ drvr->scan_param_handler.version = 0;
+ drvr->scan_param_handler.get_struct_for_request = NULL;
+ return -EINVAL;
+ }
+
+ drvr->scan_param_handler.version = version;
+
+ return 0;
+}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/scan_param.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/scan_param.h
new file mode 100644
index 0000000000..a30ad26d9c
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/scan_param.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: ISC */
+/*
+ * Copyright (c) 2023 Daniel Berlin
+ */
+
+#ifndef _BRCMF_SCAN_PARAM_H
+#define _BRCMF_SCAN_PARAM_H
+
+struct brcmf_pub;
+
+int brcmf_scan_param_setup_for_version(struct brcmf_pub *drvr, u32 version);
+
+#endif /* _BRCMF_SCAN_PARAM_H */
--
2.55.0
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH 11/13] wifi: brcmfmac: Support versioned join requests
2026-09-25 13:43 [PATCH 00/13] wifi: brcmfmac: Add BCM4388 support Michael Reeves via B4 Relay
` (9 preceding siblings ...)
2026-09-25 13:43 ` [PATCH 10/13] wifi: brcmfmac: Support versioned scan parameter layouts Michael Reeves via B4 Relay
@ 2026-09-25 13:43 ` Michael Reeves via B4 Relay
2026-09-25 13:43 ` [PATCH 12/13] wifi: brcmfmac: Handle extended PMK command payloads Michael Reeves via B4 Relay
2026-09-25 13:43 ` [PATCH 13/13] wifi: brcmfmac: Add BCM4388 support Michael Reeves via B4 Relay
12 siblings, 0 replies; 14+ messages in thread
From: Michael Reeves via B4 Relay @ 2026-09-25 13:43 UTC (permalink / raw)
To: Arend van Spriel, Pieter-Paul Giesberts, Franky Lin (Zhenhui),
Mathy Vanhoef, Kalle Valo, Kees Cook, Gustavo A. R. Silva
Cc: linux-wireless, brcm80211, brcm80211-dev-list.pdl, asahi,
Hector Martin, Daniel Berlin, Janne Grunau, linux-kernel,
Hante Meuleman, linux-hardening, Michael Reeves
From: Hector Martin <marcan@marcan.st>
Newer firmware uses version 1 join and association structures with
explicit version headers.
Query join_ver and build version 1 requests when required. Retain legacy
layouts for older firmware and fall back to BRCMF_C_SET_SSID when the
join iovar returns -EBADE. Use the selected association layout for IBSS
joins too. Report unsupported join versions rather than guessing their
request layout.
Signed-off-by: Hector Martin <marcan@marcan.st>
Signed-off-by: Daniel Berlin <dberlin@dberlin.org>
Co-developed-by: Michael Reeves <michael.reeves077@gmail.com>
Signed-off-by: Michael Reeves <michael.reeves077@gmail.com>
---
.../broadcom/brcm80211/brcmfmac/cfg80211.c | 73 +++++++++++++++++++---
.../wireless/broadcom/brcm80211/brcmfmac/core.h | 1 +
.../wireless/broadcom/brcm80211/brcmfmac/feature.c | 19 ++++++
.../broadcom/brcm80211/brcmfmac/fwil_types.h | 29 +++++++++
4 files changed, 113 insertions(+), 9 deletions(-)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index 2ce7041be6..2a742ae8cc 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -1653,6 +1653,36 @@ static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason,
brcmf_dbg(TRACE, "Exit\n");
}
+static int brcmf_set_ssid(struct brcmf_if *ifp,
+ const struct brcmf_join_params *legacy, size_t legacy_size)
+{
+ struct brcmf_join_params_v1_le *params;
+ u32 count = le32_to_cpu(legacy->params_le.chanspec_num);
+ size_t size;
+ int err;
+
+ if (!ifp->drvr->join_version)
+ return brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
+ (void *)legacy, legacy_size);
+ if (ifp->drvr->join_version != 1)
+ return -EOPNOTSUPP;
+
+ size = struct_size(params, assoc_le.chanspec_list, count);
+ params = kzalloc(size, GFP_KERNEL);
+ if (!params)
+ return -ENOMEM;
+
+ params->ssid_le = legacy->ssid_le;
+ params->assoc_le.version = cpu_to_le16(1);
+ ether_addr_copy(params->assoc_le.bssid, legacy->params_le.bssid);
+ params->assoc_le.chanspec_num = legacy->params_le.chanspec_num;
+ memcpy(params->assoc_le.chanspec_list, legacy->params_le.chanspec_list,
+ count * sizeof(__le16));
+ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID, params, size);
+ kfree(params);
+ return err;
+}
+
static s32
brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
struct cfg80211_ibss_params *params)
@@ -1791,8 +1821,7 @@ brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
cfg->ibss_starter = false;
- err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
- &join_params, join_params_size);
+ err = brcmf_set_ssid(ifp, &join_params, join_params_size);
if (err) {
bphy_err(drvr, "WLC_SET_SSID failed (%d)\n", err);
goto done;
@@ -2281,6 +2310,30 @@ static void brcmf_set_join_pref(struct brcmf_if *ifp,
bphy_err(drvr, "Set join_pref error (%d)\n", err);
}
+static int brcmf_join_v1(struct brcmf_if *ifp,
+ const struct brcmf_ext_join_params_le *legacy)
+{
+ struct brcmf_ext_join_params_v1_le *params;
+ u32 count = le32_to_cpu(legacy->assoc_le.chanspec_num);
+ size_t size = struct_size(params, assoc_le.chanspec_list, count);
+ int err;
+
+ params = kzalloc(size, GFP_KERNEL);
+ if (!params)
+ return -ENOMEM;
+ params->version = cpu_to_le16(1);
+ params->ssid_le = legacy->ssid_le;
+ params->scan_le = legacy->scan_le;
+ params->assoc_le.version = cpu_to_le16(1);
+ ether_addr_copy(params->assoc_le.bssid, legacy->assoc_le.bssid);
+ params->assoc_le.chanspec_num = legacy->assoc_le.chanspec_num;
+ memcpy(params->assoc_le.chanspec_list, legacy->assoc_le.chanspec_list,
+ count * sizeof(__le16));
+ err = brcmf_fil_bsscfg_data_set(ifp, "join", params, size);
+ kfree(params);
+ return err;
+}
+
static s32
brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
struct cfg80211_connect_params *sme)
@@ -2302,6 +2355,8 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
u32 ssid_len;
brcmf_dbg(TRACE, "Enter\n");
+ if (drvr->join_version > 1)
+ return -EOPNOTSUPP;
if (!check_vif_up(ifp->vif))
return -EIO;
@@ -2492,14 +2547,15 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
brcmf_set_join_pref(ifp, &sme->bss_select);
- err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params,
- join_params_size);
+ if (ifp->drvr->join_version == 1)
+ err = brcmf_join_v1(ifp, ext_join_params);
+ else
+ err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params,
+ join_params_size);
kfree(ext_join_params);
- if (!err)
- /* This is it. join command worked, we are done */
+ if (err != -EBADE)
goto done;
- /* join command failed, fallback to set ssid */
memset(&join_params, 0, sizeof(join_params));
join_params_size = sizeof(join_params.ssid_le);
@@ -2516,8 +2572,7 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
join_params.params_le.chanspec_num = cpu_to_le32(1);
join_params_size += sizeof(join_params.params_le);
}
- err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
- &join_params, join_params_size);
+ err = brcmf_set_ssid(ifp, &join_params, join_params_size);
if (err)
bphy_err(drvr, "BRCMF_C_SET_SSID failed (%d)\n", err);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
index fe909f7add..320473970d 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
@@ -162,6 +162,7 @@ struct brcmf_pub {
const struct brcmf_fwvid_ops *vops;
void *vdata;
struct scan_param_struct_handler scan_param_handler;
+ u16 join_version;
};
/* forward declarations */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
index 44c0082e21..27fa3a7941 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
@@ -160,6 +160,10 @@ static void brcmf_feat_wlc_version_overrides(struct brcmf_pub *drv)
brcmf_dbg(INFO, "WLC version: %d.%d\n", major, minor);
+ if (major >= 17 || (major == 16 && minor >= 1) ||
+ (major == 14 && (minor == 2 || minor == 4)))
+ drv->join_version = 1;
+
for (i = 0; i < ARRAY_SIZE(brcmf_feat_wlcfeat_map); i++) {
e = &brcmf_feat_wlcfeat_map[i];
if (major > e->min_ver_major ||
@@ -297,6 +301,20 @@ static void brcmf_feat_firmware_capabilities(struct brcmf_if *ifp)
}
}
+static void brcmf_feat_join_params(struct brcmf_if *ifp)
+{
+ struct brcmf_join_version_le version = {};
+ int err;
+
+ err = brcmf_fil_iovar_data_get(ifp, "join_ver", &version,
+ sizeof(version));
+ if (!err)
+ ifp->drvr->join_version = le16_to_cpu(version.major);
+ if (ifp->drvr->join_version > 1)
+ bphy_err(ifp->drvr, "unsupported join params v%u\n",
+ ifp->drvr->join_version);
+}
+
static void brcmf_feat_scan_params(struct brcmf_if *ifp)
{
struct brcmf_pub *drvr = ifp->drvr;
@@ -430,6 +448,7 @@ void brcmf_feat_attach(struct brcmf_pub *drvr)
brcmf_feat_event_msgs_ext(ifp);
brcmf_feat_wlc_version_overrides(drvr);
+ brcmf_feat_join_params(ifp);
brcmf_feat_firmware_overrides(drvr);
brcmf_fwvid_feat_attach(ifp);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
index dbba883bba..917966ad70 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
@@ -584,6 +584,35 @@ struct brcmf_ext_join_params_le {
struct brcmf_assoc_params_le assoc_le;
};
+struct brcmf_assoc_params_v1_le {
+ __le16 version;
+ __le16 flags;
+ u8 bssid[ETH_ALEN];
+ __le16 bssid_cnt;
+ __le32 chanspec_num;
+ __le16 chanspec_list[];
+};
+
+struct brcmf_join_params_v1_le {
+ struct brcmf_ssid_le ssid_le;
+ struct brcmf_assoc_params_v1_le assoc_le;
+};
+
+struct brcmf_join_version_le {
+ __le16 version;
+ __le16 length;
+ __le16 major;
+ u8 pad[2];
+};
+
+struct brcmf_ext_join_params_v1_le {
+ __le16 version;
+ __le16 pad;
+ struct brcmf_ssid_le ssid_le;
+ struct brcmf_join_scan_params_le scan_le;
+ struct brcmf_assoc_params_v1_le assoc_le;
+};
+
struct brcmf_wsec_key {
u32 index; /* key index */
u32 len; /* key length */
--
2.55.0
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH 12/13] wifi: brcmfmac: Handle extended PMK command payloads
2026-09-25 13:43 [PATCH 00/13] wifi: brcmfmac: Add BCM4388 support Michael Reeves via B4 Relay
` (10 preceding siblings ...)
2026-09-25 13:43 ` [PATCH 11/13] wifi: brcmfmac: Support versioned join requests Michael Reeves via B4 Relay
@ 2026-09-25 13:43 ` Michael Reeves via B4 Relay
2026-09-25 13:43 ` [PATCH 13/13] wifi: brcmfmac: Add BCM4388 support Michael Reeves via B4 Relay
12 siblings, 0 replies; 14+ messages in thread
From: Michael Reeves via B4 Relay @ 2026-09-25 13:43 UTC (permalink / raw)
To: Arend van Spriel, Pieter-Paul Giesberts, Franky Lin (Zhenhui),
Mathy Vanhoef, Kalle Valo, Kees Cook, Gustavo A. R. Silva
Cc: linux-wireless, brcm80211, brcm80211-dev-list.pdl, asahi,
Hector Martin, Daniel Berlin, Janne Grunau, linux-kernel,
Hante Meuleman, linux-hardening, Michael Reeves
From: Michael Reeves <michael.reeves077@gmail.com>
Some firmware rejects the legacy BRCMF_C_SET_WSEC_PMK payload despite
advertising firmware supplicant support.
Add the extended PMK layout. Try the legacy payload first when the key
fits, and retry with the extended layout only after -EBADE. Use the
extended layout directly for larger keys and reject keys that exceed its
capacity.
Co-developed-by: Hector Martin <marcan@marcan.st>
Signed-off-by: Hector Martin <marcan@marcan.st>
Signed-off-by: Michael Reeves <michael.reeves077@gmail.com>
---
.../broadcom/brcm80211/brcmfmac/cfg80211.c | 39 ++++++++++++++++------
.../broadcom/brcm80211/brcmfmac/fwil_types.h | 18 ++++++++++
2 files changed, 46 insertions(+), 11 deletions(-)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index 2a742ae8cc..c9cc6e0ccd 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -1584,28 +1584,45 @@ int brcmf_set_wsec(struct brcmf_if *ifp, const u8 *key, u16 key_len, u16 flags)
{
struct brcmf_pub *drvr = ifp->drvr;
struct brcmf_wsec_pmk_le pmk;
+ struct brcmf_wsec_pmk_ext_le pmk_ext;
int err;
- if (key_len > sizeof(pmk.key)) {
+ if (key_len > sizeof(pmk_ext.key)) {
bphy_err(drvr, "key must be less than %zu bytes\n",
- sizeof(pmk.key));
+ sizeof(pmk_ext.key));
return -EINVAL;
}
- memset(&pmk, 0, sizeof(pmk));
+ if (key_len <= sizeof(pmk.key)) {
+ memset(&pmk, 0, sizeof(pmk));
- /* pass key material directly */
- pmk.key_len = cpu_to_le16(key_len);
- pmk.flags = cpu_to_le16(flags);
- memcpy(pmk.key, key, key_len);
+ pmk.key_len = cpu_to_le16(key_len);
+ pmk.flags = cpu_to_le16(flags);
+ memcpy(pmk.key, key, key_len);
+
+ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_WSEC_PMK,
+ &pmk, sizeof(pmk));
+ if (!err)
+ return 0;
+ if (err != -EBADE)
+ goto fail;
+ }
+
+ memset(&pmk_ext, 0, sizeof(pmk_ext));
+ pmk_ext.key_len = cpu_to_le16(key_len);
+ pmk_ext.flags = cpu_to_le16(flags);
+ memcpy(pmk_ext.key, key, key_len);
- /* store key material in firmware */
err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_WSEC_PMK,
- &pmk, sizeof(pmk));
+ &pmk_ext, sizeof(pmk_ext));
if (err < 0)
- bphy_err(drvr, "failed to change PSK in firmware (len=%u)\n",
- key_len);
+ goto fail;
+
+ return 0;
+fail:
+ bphy_err(drvr, "failed to change PSK in firmware (len=%u, err=%d)\n",
+ key_len, err);
return err;
}
BRCMF_EXPORT_SYMBOL_GPL(brcmf_set_wsec);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
index 917966ad70..37d9414546 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
@@ -75,6 +75,7 @@
#define BRCMF_WSEC_MAX_PSK_LEN 32
#define BRCMF_WSEC_PASSPHRASE BIT(0)
+#define BRCMF_WSEC_MAX_PMK_LEN 256
#define BRCMF_WSEC_MAX_SAE_PASSWORD_LEN 128
/* primary (ie tx) key */
@@ -667,6 +668,23 @@ struct brcmf_wsec_pmk_le {
u8 key[BRCMF_WSEC_MAX_SAE_PASSWORD_LEN];
};
+/**
+ * struct brcmf_wsec_pmk_ext_le - extended firmware pmk material.
+ *
+ * @key_len: number of octets in key material.
+ * @flags: key handling qualifiers.
+ * @key: PMK key material.
+ * @opt_len: optional field length.
+ * @opt_tlvs: optional fields in TLV format.
+ */
+struct brcmf_wsec_pmk_ext_le {
+ __le16 key_len;
+ __le16 flags;
+ u8 key[BRCMF_WSEC_MAX_PMK_LEN];
+ __le16 opt_len;
+ u8 opt_tlvs[];
+};
+
/**
* struct brcmf_wsec_sae_pwd_le - firmware SAE password material.
*
--
2.55.0
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH 13/13] wifi: brcmfmac: Add BCM4388 support
2026-09-25 13:43 [PATCH 00/13] wifi: brcmfmac: Add BCM4388 support Michael Reeves via B4 Relay
` (11 preceding siblings ...)
2026-09-25 13:43 ` [PATCH 12/13] wifi: brcmfmac: Handle extended PMK command payloads Michael Reeves via B4 Relay
@ 2026-09-25 13:43 ` Michael Reeves via B4 Relay
12 siblings, 0 replies; 14+ messages in thread
From: Michael Reeves via B4 Relay @ 2026-09-25 13:43 UTC (permalink / raw)
To: Arend van Spriel, Pieter-Paul Giesberts, Franky Lin (Zhenhui),
Mathy Vanhoef, Kalle Valo, Kees Cook, Gustavo A. R. Silva
Cc: linux-wireless, brcm80211, brcm80211-dev-list.pdl, asahi,
Hector Martin, Daniel Berlin, Janne Grunau, linux-kernel,
Hante Meuleman, linux-hardening, Michael Reeves
From: Hector Martin <marcan@marcan.st>
Add BCM4388 firmware mappings, RAM base and OTP handling, and increase
the RX posting and completion ring capacities to 2048 items.
On BCM4388, DMA_FROM_DEVICE mappings for posted receive buffers cause
DART read-permission faults followed by firmware DMA failure. Map these
buffers with DMA_BIDIRECTIONAL to allow device reads as well as writes.
Keep the existing mapping direction for other chips.
Signed-off-by: Hector Martin <marcan@marcan.st>
Co-developed-by: Michael Reeves <michael.reeves077@gmail.com>
Signed-off-by: Michael Reeves <michael.reeves077@gmail.com>
---
drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c | 1 +
drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c | 8 +++++---
drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h | 4 ++--
drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | 12 +++++++++++-
.../net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h | 2 ++
5 files changed, 21 insertions(+), 6 deletions(-)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
index 253ab45c33..411188010f 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
@@ -751,6 +751,7 @@ static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci)
case BRCM_CC_4366_CHIP_ID:
case BRCM_CC_43664_CHIP_ID:
case BRCM_CC_43666_CHIP_ID:
+ case BRCM_CC_4388_CHIP_ID:
return 0x200000;
case BRCM_CC_4355_CHIP_ID:
case BRCM_CC_4359_CHIP_ID:
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
index 8e5c697e7f..5ad46d7ec6 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
@@ -12,6 +12,7 @@
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
+#include <brcm_hw_ids.h>
#include <brcmu_utils.h>
#include <brcmu_wifi.h>
@@ -51,7 +52,7 @@
#define MSGBUF_TYPE_D2H_MAILBOX_DATA 0x24
#define NR_TX_PKTIDS 2048
-#define NR_RX_PKTIDS 1024
+#define NR_RX_PKTIDS 2048
#define BRCMF_IOCTL_REQ_PKTID 0xFFFE
@@ -330,7 +331,6 @@ brcmf_msgbuf_init_pktids(u32 nr_array_entries,
return pktids;
}
-
static int
brcmf_msgbuf_alloc_pktid(struct device *dev,
struct brcmf_msgbuf_pktids *pktids,
@@ -1695,8 +1695,10 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr)
DMA_TO_DEVICE);
if (!msgbuf->tx_pktids)
goto fail;
+ /* BCM4388 firmware also reads from posted RX buffers. */
msgbuf->rx_pktids = brcmf_msgbuf_init_pktids(NR_RX_PKTIDS,
- DMA_FROM_DEVICE);
+ drvr->bus_if->chip == BRCM_CC_4388_CHIP_ID ?
+ DMA_BIDIRECTIONAL : DMA_FROM_DEVICE);
if (!msgbuf->rx_pktids)
goto fail;
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h
index 18b56d0789..1d0233c619 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h
@@ -8,10 +8,10 @@
#ifdef CONFIG_BRCMFMAC_PROTO_MSGBUF
#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_MAX_ITEM 64
-#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT_MAX_ITEM 1024
+#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT_MAX_ITEM 2048
#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE_MAX_ITEM 64
#define BRCMF_D2H_MSGRING_TX_COMPLETE_MAX_ITEM 1024
-#define BRCMF_D2H_MSGRING_RX_COMPLETE_MAX_ITEM 1024
+#define BRCMF_D2H_MSGRING_RX_COMPLETE_MAX_ITEM 2048
#define BRCMF_H2D_TXFLOWRING_MAX_ITEM 512
#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_ITEMSIZE 40
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
index 48402eb2ef..32c95b8f51 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
@@ -71,6 +71,8 @@ BRCMF_FW_CLM_DEF(4377B3, "brcmfmac4377b3-pcie");
BRCMF_FW_CLM_DEF(4378B1, "brcmfmac4378b1-pcie");
BRCMF_FW_CLM_DEF(4378B3, "brcmfmac4378b3-pcie");
BRCMF_FW_CLM_DEF(4387C2, "brcmfmac4387c2-pcie");
+BRCMF_FW_CLM_DEF(4388B0, "brcmfmac4388b0-pcie");
+BRCMF_FW_CLM_DEF(4388C0, "brcmfmac4388c0-pcie");
BRCMF_FW_CLM_DEF(54591, "brcmfmac54591-pcie");
/* firmware config files */
@@ -113,6 +115,8 @@ static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = {
BRCMF_FW_ENTRY(BRCM_CC_4378_CHIP_ID, 0x0000000F, 4378B1), /* revision ID 3 */
BRCMF_FW_ENTRY(BRCM_CC_4378_CHIP_ID, 0xFFFFFFE0, 4378B3), /* revision ID 5 */
BRCMF_FW_ENTRY(BRCM_CC_4387_CHIP_ID, 0xFFFFFFFF, 4387C2), /* revision ID 7 */
+ BRCMF_FW_ENTRY(BRCM_CC_4388_CHIP_ID, 0x0000000F, 4388B0),
+ BRCMF_FW_ENTRY(BRCM_CC_4388_CHIP_ID, 0xFFFFFFF0, 4388C0), /* revision ID 4 */
};
#define BRCMF_PCIE_FW_UP_TIMEOUT 5000 /* msec */
@@ -2397,6 +2401,11 @@ static int brcmf_pcie_read_otp(struct brcmf_pciedev_info *devinfo)
base = 0x113c;
words = 0x170;
break;
+ case BRCM_CC_4388_CHIP_ID:
+ coreid = BCMA_CORE_GCI;
+ base = 0x115c;
+ words = 0x150;
+ break;
default:
/* OTP not supported on this chip */
return 0;
@@ -2459,7 +2468,7 @@ static int brcmf_pcie_read_otp(struct brcmf_pciedev_info *devinfo)
static bool brcmf_pcie_request_fw_signature(struct brcmf_pciedev_info *devinfo)
{
- return false;
+ return devinfo->ci->chip == BRCM_CC_4388_CHIP_ID;
}
static void brcmf_pcie_setup(struct device *dev, int ret,
@@ -3114,6 +3123,7 @@ static const struct pci_device_id brcmf_pcie_devid_table[] = {
BRCMF_PCIE_DEVICE(BRCM_PCIE_4377_DEVICE_ID, WCC_SEED),
BRCMF_PCIE_DEVICE(BRCM_PCIE_4378_DEVICE_ID, WCC_SEED),
BRCMF_PCIE_DEVICE(BRCM_PCIE_4387_DEVICE_ID, WCC_SEED),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_4388_DEVICE_ID, WCC_SEED),
BRCMF_PCIE_DEVICE(BRCM_PCIE_43752_DEVICE_ID, WCC_SEED),
BRCMF_PCIE_DEVICE(CY_PCIE_54591_DEVICE_ID, CYW),
{ /* end: all zeroes */ }
diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h
index df3b67ba4d..f749337a06 100644
--- a/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h
+++ b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h
@@ -57,6 +57,7 @@
#define BRCM_CC_4377_CHIP_ID 0x4377
#define BRCM_CC_4378_CHIP_ID 0x4378
#define BRCM_CC_4387_CHIP_ID 0x4387
+#define BRCM_CC_4388_CHIP_ID 0x4388
#define CY_CC_4373_CHIP_ID 0x4373
#define CY_CC_43012_CHIP_ID 43012
#define CY_CC_43439_CHIP_ID 43439
@@ -99,6 +100,7 @@
#define BRCM_PCIE_4377_DEVICE_ID 0x4488
#define BRCM_PCIE_4378_DEVICE_ID 0x4425
#define BRCM_PCIE_4387_DEVICE_ID 0x4433
+#define BRCM_PCIE_4388_DEVICE_ID 0x4434
#define CY_PCIE_54591_DEVICE_ID 0x4417
/* brcmsmac IDs */
--
2.55.0
^ permalink raw reply [flat|nested] 14+ messages in thread