From: Michael Reeves <[email protected]> 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 <[email protected]> Signed-off-by: Hector Martin <[email protected]> Signed-off-by: Michael Reeves <[email protected]> --- .../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

