wifi: brcmfmac: allow per-vendor event handling
authorArend van Spriel <arend.vanspriel@broadcom.com>
Sat, 6 Jan 2024 10:38:35 +0000 (11:38 +0100)
committerKalle Valo <kvalo@kernel.org>
Fri, 19 Jan 2024 17:30:19 +0000 (19:30 +0200)
The firmware interface also defines events generated by
firmware on the device. As the get/set primitives the
events are likely to diverge between the vendors so this
commit adds support for per-vendor handling. The number
of events may differ so we let the vendor-specific code
allocate the struct brcmf_fweh_info which contains array
of event handlers. The existing event enumeration will be
used by the higher layers and thus are common definitions.
The vendor-specific code can provide a mapping table for
converting the common definition to the vendor-specific
firmware event definition and vice-versa.

Signed-off-by: Arend van Spriel <arend.vanspriel@broadcom.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://msgid.link/20240106103835.269149-4-arend.vanspriel@broadcom.com
drivers/net/wireless/broadcom/brcm80211/brcmfmac/bca/core.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/core.c

index a963c242975ac96ee96bbba9dd3958e07c53eab2..f471c962104aac480d4ebc2c9013bf6c14ba746d 100644 (file)
 
 #include "vops.h"
 
+#define BRCMF_BCA_E_LAST               212
+
 static void brcmf_bca_feat_attach(struct brcmf_if *ifp)
 {
        /* SAE support not confirmed so disabling for now */
        ifp->drvr->feat_flags &= ~BIT(BRCMF_FEAT_SAE);
 }
 
+static int brcmf_bca_alloc_fweh_info(struct brcmf_pub *drvr)
+{
+       struct brcmf_fweh_info *fweh;
+
+       fweh = kzalloc(struct_size(fweh, evt_handler, BRCMF_BCA_E_LAST),
+                      GFP_KERNEL);
+       if (!fweh)
+               return -ENOMEM;
+
+       fweh->num_event_codes = BRCMF_BCA_E_LAST;
+       drvr->fweh = fweh;
+       return 0;
+}
+
 const struct brcmf_fwvid_ops brcmf_bca_ops = {
        .feat_attach = brcmf_bca_feat_attach,
+       .alloc_fweh_info = brcmf_bca_alloc_fweh_info,
 };
index b6d458e022fad30b6798aea0f4f2cabffa695603..b24faae35873dc0cfeec9fde148db6cf24a6a55e 100644 (file)
@@ -266,7 +266,7 @@ 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;
-       s8 eventmask[BRCMF_EVENTING_MASK_LEN];
+       struct brcmf_fweh_info *fweh = drvr->fweh;
        u8 buf[BRCMF_DCMD_SMLEN];
        struct brcmf_bus *bus;
        struct brcmf_rev_info_le revinfo;
@@ -413,15 +413,21 @@ 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", eventmask,
-                                      BRCMF_EVENTING_MASK_LEN);
+       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;
        }
-       setbit(eventmask, BRCMF_E_IF);
-       err = brcmf_fil_iovar_data_set(ifp, "event_msgs", eventmask,
-                                      BRCMF_EVENTING_MASK_LEN);
+       /*
+        * 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;
index a92f78026cfdac9b6b0ed32d134f220f667b2021..bf91b1e1368f03e0bf0035d99fffc6c488f95818 100644 (file)
@@ -1348,13 +1348,17 @@ int brcmf_attach(struct device *dev)
                goto fail;
        }
 
+       /* attach firmware event handler */
+       ret = brcmf_fweh_attach(drvr);
+       if (ret != 0) {
+               bphy_err(drvr, "brcmf_fweh_attach failed\n");
+               goto fail;
+       }
+
        /* Attach to events important for core code */
        brcmf_fweh_register(drvr, BRCMF_E_PSM_WATCHDOG,
                            brcmf_psm_watchdog_notify);
 
-       /* attach firmware event handler */
-       brcmf_fweh_attach(drvr);
-
        ret = brcmf_bus_started(drvr, drvr->ops);
        if (ret != 0) {
                bphy_err(drvr, "dongle is not responding: err=%d\n", ret);
index e4f911dd414b6c4749f1081e2580e6c5235759ff..ea76b8d334019a98bd85c70929edfb3ac6571d46 100644 (file)
@@ -122,7 +122,7 @@ struct brcmf_pub {
        struct mutex proto_block;
        unsigned char proto_buf[BRCMF_DCMD_MAXLEN];
 
-       struct brcmf_fweh_info fweh;
+       struct brcmf_fweh_info *fweh;
 
        struct brcmf_ampdu_rx_reorder
                *reorder_flows[BRCMF_AMPDU_RX_REORDER_MAXFLOWS];
index bec5748310b9cd6134b9c8afddcfbc051b50c72a..9a48378814866781820936c19ea147989d888122 100644 (file)
@@ -11,6 +11,8 @@
 
 #include "vops.h"
 
+#define BRCMF_CYW_E_LAST               197
+
 static int brcmf_cyw_set_sae_pwd(struct brcmf_if *ifp,
                                 struct cfg80211_crypto_settings *crypto)
 {
@@ -37,6 +39,21 @@ static int brcmf_cyw_set_sae_pwd(struct brcmf_if *ifp,
        return err;
 }
 
+static int brcmf_cyw_alloc_fweh_info(struct brcmf_pub *drvr)
+{
+       struct brcmf_fweh_info *fweh;
+
+       fweh = kzalloc(struct_size(fweh, evt_handler, BRCMF_CYW_E_LAST),
+                      GFP_KERNEL);
+       if (!fweh)
+               return -ENOMEM;
+
+       fweh->num_event_codes = BRCMF_CYW_E_LAST;
+       drvr->fweh = fweh;
+       return 0;
+}
+
 const struct brcmf_fwvid_ops brcmf_cyw_ops = {
        .set_sae_password = brcmf_cyw_set_sae_pwd,
+       .alloc_fweh_info = brcmf_cyw_alloc_fweh_info,
 };
index 68960ae98987134cb2f87c505320021eea7fd96e..0774f6c59226eeb33e2a86c71e18f970637cb5ce 100644 (file)
@@ -14,7 +14,8 @@
 #include "fweh.h"
 #include "fwil.h"
 #include "proto.h"
-
+#include "bus.h"
+#include "fwvid.h"
 /**
  * struct brcmf_fweh_queue_item - event item on event queue.
  *
@@ -28,7 +29,7 @@
  */
 struct brcmf_fweh_queue_item {
        struct list_head q;
-       enum brcmf_fweh_event_code code;
+       u32 code;
        u8 ifidx;
        u8 ifaddr[ETH_ALEN];
        struct brcmf_event_msg_be emsg;
@@ -94,7 +95,7 @@ static void brcmf_fweh_queue_event(struct brcmf_fweh_info *fweh,
 
 static int brcmf_fweh_call_event_handler(struct brcmf_pub *drvr,
                                         struct brcmf_if *ifp,
-                                        enum brcmf_fweh_event_code code,
+                                        u32 fwcode,
                                         struct brcmf_event_msg *emsg,
                                         void *data)
 {
@@ -102,13 +103,13 @@ static int brcmf_fweh_call_event_handler(struct brcmf_pub *drvr,
        int err = -EINVAL;
 
        if (ifp) {
-               fweh = &ifp->drvr->fweh;
+               fweh = ifp->drvr->fweh;
 
                /* handle the event if valid interface and handler */
-               if (fweh->evt_handler[code])
-                       err = fweh->evt_handler[code](ifp, emsg, data);
+               if (fweh->evt_handler[fwcode])
+                       err = fweh->evt_handler[fwcode](ifp, emsg, data);
                else
-                       bphy_err(drvr, "unhandled event %d ignored\n", code);
+                       bphy_err(drvr, "unhandled fwevt %d ignored\n", fwcode);
        } else {
                bphy_err(drvr, "no interface object\n");
        }
@@ -142,7 +143,7 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr,
        is_p2pdev = ((ifevent->flags & BRCMF_E_IF_FLAG_NOIF) &&
                     (ifevent->role == BRCMF_E_IF_ROLE_P2P_CLIENT ||
                      ((ifevent->role == BRCMF_E_IF_ROLE_STA) &&
-                      (drvr->fweh.p2pdev_setup_ongoing))));
+                      (drvr->fweh->p2pdev_setup_ongoing))));
        if (!is_p2pdev && (ifevent->flags & BRCMF_E_IF_FLAG_NOIF)) {
                brcmf_dbg(EVENT, "event can be ignored\n");
                return;
@@ -163,7 +164,7 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr,
                        return;
                if (!is_p2pdev)
                        brcmf_proto_add_if(drvr, ifp);
-               if (!drvr->fweh.evt_handler[BRCMF_E_IF])
+               if (!drvr->fweh->evt_handler[BRCMF_E_IF])
                        if (brcmf_net_attach(ifp, false) < 0)
                                return;
        }
@@ -183,6 +184,45 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr,
        }
 }
 
+static void brcmf_fweh_map_event_code(struct brcmf_fweh_info *fweh,
+                                     enum brcmf_fweh_event_code code,
+                                     u32 *fw_code)
+{
+       int i;
+
+       if (WARN_ON(!fw_code))
+               return;
+
+       *fw_code = code;
+       if (fweh->event_map) {
+               for (i = 0; i < fweh->event_map->n_items; i++) {
+                       if (fweh->event_map->items[i].code == code) {
+                               *fw_code = fweh->event_map->items[i].fwevt_code;
+                               break;
+                       }
+               }
+       }
+}
+
+static void brcmf_fweh_map_fwevt_code(struct brcmf_fweh_info *fweh, u32 fw_code,
+                                     enum brcmf_fweh_event_code *code)
+{
+       int i;
+
+       if (WARN_ON(!code))
+               return;
+
+       *code = fw_code;
+       if (fweh->event_map) {
+               for (i = 0; i < fweh->event_map->n_items; i++) {
+                       if (fweh->event_map->items[i].fwevt_code == fw_code) {
+                               *code = fweh->event_map->items[i].code;
+                               break;
+                       }
+               }
+       }
+}
+
 /**
  * brcmf_fweh_dequeue_event() - get event from the queue.
  *
@@ -221,15 +261,19 @@ static void brcmf_fweh_event_worker(struct work_struct *work)
        struct brcmf_event_msg emsg;
 
        fweh = container_of(work, struct brcmf_fweh_info, event_work);
-       drvr = container_of(fweh, struct brcmf_pub, fweh);
+       drvr = fweh->drvr;
 
        while ((event = brcmf_fweh_dequeue_event(fweh))) {
-               brcmf_dbg(EVENT, "event %s (%u) ifidx %u bsscfg %u addr %pM\n",
-                         brcmf_fweh_event_name(event->code), event->code,
+               enum brcmf_fweh_event_code code;
+
+               brcmf_fweh_map_fwevt_code(fweh, event->code, &code);
+               brcmf_dbg(EVENT, "event %s (%u:%u) ifidx %u bsscfg %u addr %pM\n",
+                         brcmf_fweh_event_name(code), code, event->code,
                          event->emsg.ifidx, event->emsg.bsscfgidx,
                          event->emsg.addr);
                if (event->emsg.bsscfgidx >= BRCMF_MAX_IFS) {
-                       bphy_err(drvr, "invalid bsscfg index: %u\n", event->emsg.bsscfgidx);
+                       bphy_err(drvr, "invalid bsscfg index: %u\n",
+                                event->emsg.bsscfgidx);
                        goto event_free;
                }
 
@@ -237,7 +281,7 @@ static void brcmf_fweh_event_worker(struct work_struct *work)
                emsg_be = &event->emsg;
                emsg.version = be16_to_cpu(emsg_be->version);
                emsg.flags = be16_to_cpu(emsg_be->flags);
-               emsg.event_code = event->code;
+               emsg.event_code = code;
                emsg.status = be32_to_cpu(emsg_be->status);
                emsg.reason = be32_to_cpu(emsg_be->reason);
                emsg.auth_type = be32_to_cpu(emsg_be->auth_type);
@@ -283,7 +327,7 @@ event_free:
  */
 void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing)
 {
-       ifp->drvr->fweh.p2pdev_setup_ongoing = ongoing;
+       ifp->drvr->fweh->p2pdev_setup_ongoing = ongoing;
 }
 
 /**
@@ -291,12 +335,27 @@ void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing)
  *
  * @drvr: driver information object.
  */
-void brcmf_fweh_attach(struct brcmf_pub *drvr)
+int brcmf_fweh_attach(struct brcmf_pub *drvr)
 {
-       struct brcmf_fweh_info *fweh = &drvr->fweh;
+       struct brcmf_fweh_info *fweh;
+       int err;
+
+       err = brcmf_fwvid_alloc_fweh_info(drvr);
+       if (err < 0)
+               return err;
+
+       fweh = drvr->fweh;
+       fweh->drvr = drvr;
+
+       fweh->event_mask_len = DIV_ROUND_UP(fweh->num_event_codes, 8);
+       fweh->event_mask = kzalloc(fweh->event_mask_len, GFP_KERNEL);
+       if (!fweh->event_mask)
+               return -ENOMEM;
+
        INIT_WORK(&fweh->event_work, brcmf_fweh_event_worker);
        spin_lock_init(&fweh->evt_q_lock);
        INIT_LIST_HEAD(&fweh->event_q);
+       return 0;
 }
 
 /**
@@ -306,14 +365,19 @@ void brcmf_fweh_attach(struct brcmf_pub *drvr)
  */
 void brcmf_fweh_detach(struct brcmf_pub *drvr)
 {
-       struct brcmf_fweh_info *fweh = &drvr->fweh;
+       struct brcmf_fweh_info *fweh = drvr->fweh;
+
+       if (!fweh)
+               return;
 
        /* cancel the worker if initialized */
        if (fweh->event_work.func) {
                cancel_work_sync(&fweh->event_work);
                WARN_ON(!list_empty(&fweh->event_q));
-               memset(fweh->evt_handler, 0, sizeof(fweh->evt_handler));
        }
+       drvr->fweh = NULL;
+       kfree(fweh->event_mask);
+       kfree(fweh);
 }
 
 /**
@@ -326,11 +390,17 @@ void brcmf_fweh_detach(struct brcmf_pub *drvr)
 int brcmf_fweh_register(struct brcmf_pub *drvr, enum brcmf_fweh_event_code code,
                        brcmf_fweh_handler_t handler)
 {
-       if (drvr->fweh.evt_handler[code]) {
+       struct brcmf_fweh_info *fweh = drvr->fweh;
+       u32 evt_handler_idx;
+
+       brcmf_fweh_map_event_code(fweh, code, &evt_handler_idx);
+
+       if (fweh->evt_handler[evt_handler_idx]) {
                bphy_err(drvr, "event code %d already registered\n", code);
                return -ENOSPC;
        }
-       drvr->fweh.evt_handler[code] = handler;
+
+       fweh->evt_handler[evt_handler_idx] = handler;
        brcmf_dbg(TRACE, "event handler registered for %s\n",
                  brcmf_fweh_event_name(code));
        return 0;
@@ -345,9 +415,12 @@ int brcmf_fweh_register(struct brcmf_pub *drvr, enum brcmf_fweh_event_code code,
 void brcmf_fweh_unregister(struct brcmf_pub *drvr,
                           enum brcmf_fweh_event_code code)
 {
+       u32 evt_handler_idx;
+
        brcmf_dbg(TRACE, "event handler cleared for %s\n",
                  brcmf_fweh_event_name(code));
-       drvr->fweh.evt_handler[code] = NULL;
+       brcmf_fweh_map_event_code(drvr->fweh, code, &evt_handler_idx);
+       drvr->fweh->evt_handler[evt_handler_idx] = NULL;
 }
 
 /**
@@ -357,27 +430,28 @@ void brcmf_fweh_unregister(struct brcmf_pub *drvr,
  */
 int brcmf_fweh_activate_events(struct brcmf_if *ifp)
 {
-       struct brcmf_pub *drvr = ifp->drvr;
+       struct brcmf_fweh_info *fweh = ifp->drvr->fweh;
+       enum brcmf_fweh_event_code code;
        int i, err;
-       s8 eventmask[BRCMF_EVENTING_MASK_LEN];
 
-       memset(eventmask, 0, sizeof(eventmask));
-       for (i = 0; i < BRCMF_E_LAST; i++) {
-               if (ifp->drvr->fweh.evt_handler[i]) {
+       memset(fweh->event_mask, 0, fweh->event_mask_len);
+       for (i = 0; i < fweh->num_event_codes; i++) {
+               if (fweh->evt_handler[i]) {
+                       brcmf_fweh_map_fwevt_code(fweh, i, &code);
                        brcmf_dbg(EVENT, "enable event %s\n",
-                                 brcmf_fweh_event_name(i));
-                       setbit(eventmask, i);
+                                 brcmf_fweh_event_name(code));
+                       setbit(fweh->event_mask, i);
                }
        }
 
        /* want to handle IF event as well */
        brcmf_dbg(EVENT, "enable event IF\n");
-       setbit(eventmask, BRCMF_E_IF);
+       setbit(fweh->event_mask, BRCMF_E_IF);
 
-       err = brcmf_fil_iovar_data_set(ifp, "event_msgs",
-                                      eventmask, BRCMF_EVENTING_MASK_LEN);
+       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);
+               bphy_err(fweh->drvr, "Set event_msgs error (%d)\n", err);
 
        return err;
 }
@@ -397,21 +471,21 @@ void brcmf_fweh_process_event(struct brcmf_pub *drvr,
                              struct brcmf_event *event_packet,
                              u32 packet_len, gfp_t gfp)
 {
-       enum brcmf_fweh_event_code code;
-       struct brcmf_fweh_info *fweh = &drvr->fweh;
+       u32 fwevt_idx;
+       struct brcmf_fweh_info *fweh = drvr->fweh;
        struct brcmf_fweh_queue_item *event;
        void *data;
        u32 datalen;
 
        /* get event info */
-       code = get_unaligned_be32(&event_packet->msg.event_type);
+       fwevt_idx = get_unaligned_be32(&event_packet->msg.event_type);
        datalen = get_unaligned_be32(&event_packet->msg.datalen);
        data = &event_packet[1];
 
-       if (code >= BRCMF_E_LAST)
+       if (fwevt_idx >= fweh->num_event_codes)
                return;
 
-       if (code != BRCMF_E_IF && !fweh->evt_handler[code])
+       if (fwevt_idx != BRCMF_E_IF && !fweh->evt_handler[fwevt_idx])
                return;
 
        if (datalen > BRCMF_DCMD_MAXLEN ||
@@ -422,13 +496,13 @@ void brcmf_fweh_process_event(struct brcmf_pub *drvr,
        if (!event)
                return;
 
-       event->datalen = datalen;
-       event->code = code;
+       event->code = fwevt_idx;
        event->ifidx = event_packet->msg.ifidx;
 
        /* use memcpy to get aligned event message */
        memcpy(&event->emsg, &event_packet->msg, sizeof(event->emsg));
        memcpy(event->data, data, datalen);
+       event->datalen = datalen;
        memcpy(event->ifaddr, event_packet->eth.h_dest, ETH_ALEN);
 
        brcmf_fweh_queue_event(fweh, event);
index 48414e8b93890dde49298c6c11c83786e9fe4cef..9ca1b2aadcb537c47cb4f76de85ecf508ad3f4bc 100644 (file)
@@ -17,6 +17,10 @@ struct brcmf_pub;
 struct brcmf_if;
 struct brcmf_cfg80211_info;
 
+#define BRCMF_ABSTRACT_EVENT_BIT       BIT(31)
+#define BRCMF_ABSTRACT_ENUM_DEF(_id, _val) \
+       BRCMF_ENUM_DEF(_id, (BRCMF_ABSTRACT_EVENT_BIT | (_val)))
+
 /* list of firmware events */
 #define BRCMF_FWEH_EVENT_ENUM_DEFLIST \
        BRCMF_ENUM_DEF(SET_SSID, 0) \
@@ -98,16 +102,9 @@ struct brcmf_cfg80211_info;
 /* firmware event codes sent by the dongle */
 enum brcmf_fweh_event_code {
        BRCMF_FWEH_EVENT_ENUM_DEFLIST
-       /* this determines event mask length which must match
-        * minimum length check in device firmware so it is
-        * hard-coded here.
-        */
-       BRCMF_E_LAST = 139
 };
 #undef BRCMF_ENUM_DEF
 
-#define BRCMF_EVENTING_MASK_LEN                DIV_ROUND_UP(BRCMF_E_LAST, 8)
-
 /* flags field values in struct brcmf_event_msg */
 #define BRCMF_EVENT_MSG_LINK           0x01
 #define BRCMF_EVENT_MSG_FLUSHTXQ       0x02
@@ -287,6 +284,33 @@ typedef int (*brcmf_fweh_handler_t)(struct brcmf_if *ifp,
                                    const struct brcmf_event_msg *evtmsg,
                                    void *data);
 
+/**
+ * struct brcmf_fweh_event_map_item - fweh event and firmware event pair.
+ *
+ * @code: fweh event code as used by higher layers.
+ * @fwevt_code: firmware event code as used by firmware.
+ *
+ * This mapping is needed when a functionally identical event has a
+ * different numerical definition between vendors. When such mapping
+ * is needed the higher layer event code should not collide with the
+ * firmware event.
+ */
+struct brcmf_fweh_event_map_item {
+       enum brcmf_fweh_event_code code;
+       u32 fwevt_code;
+};
+
+/**
+ * struct brcmf_fweh_event_map - mapping between firmware event and fweh event.
+ *
+ * @n_items: number of mapping items.
+ * @items: array of fweh event and firmware event pairs.
+ */
+struct brcmf_fweh_event_map {
+       u32 n_items;
+       const struct brcmf_fweh_event_map_item items[] __counted_by(n_items);
+};
+
 /**
  * struct brcmf_fweh_info - firmware event handling information.
  *
@@ -294,21 +318,33 @@ typedef int (*brcmf_fweh_handler_t)(struct brcmf_if *ifp,
  * @event_work: event worker.
  * @evt_q_lock: lock for event queue protection.
  * @event_q: event queue.
- * @evt_handler: registered event handlers.
+ * @event_mask_len: length of @event_mask used to enable firmware events.
+ * @event_mask: byte array used in 'event_msgs' iovar command.
+ * @event_map: mapping between fweh event and firmware event which
+ *     may be provided by vendor-specific module for events that need
+ *     mapping.
+ * @num_event_codes: number of firmware events supported by firmware which
+ *     does a minimum length check for the @event_mask. This value is to
+ *     be provided by vendor-specific module determining @event_mask_len
+ *     and consequently the allocation size for @event_mask.
+ * @evt_handler: event handler registry indexed by firmware event code.
  */
 struct brcmf_fweh_info {
+       struct brcmf_pub *drvr;
        bool p2pdev_setup_ongoing;
        struct work_struct event_work;
        spinlock_t evt_q_lock;
        struct list_head event_q;
-       int (*evt_handler[BRCMF_E_LAST])(struct brcmf_if *ifp,
-                                        const struct brcmf_event_msg *evtmsg,
-                                        void *data);
+       uint event_mask_len;
+       u8 *event_mask;
+       struct brcmf_fweh_event_map *event_map;
+       uint num_event_codes;
+       brcmf_fweh_handler_t evt_handler[] __counted_by(num_event_codes);
 };
 
 const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code);
 
-void brcmf_fweh_attach(struct brcmf_pub *drvr);
+int brcmf_fweh_attach(struct brcmf_pub *drvr);
 void brcmf_fweh_detach(struct brcmf_pub *drvr);
 int brcmf_fweh_register(struct brcmf_pub *drvr, enum brcmf_fweh_event_code code,
                        int (*handler)(struct brcmf_if *ifp,
index b427782554b5904da695e805a9a18a5adda26472..41eafcda77f7e0c9b374f841612b13b8aaebc854 100644 (file)
@@ -89,7 +89,8 @@ int brcmf_fwvid_register_vendor(enum brcmf_fwvendor fwvid, struct module *vmod,
        if (fwvid >= BRCMF_FWVENDOR_NUM)
                return -ERANGE;
 
-       if (WARN_ON(!vmod) || WARN_ON(!vops))
+       if (WARN_ON(!vmod) || WARN_ON(!vops) ||
+           WARN_ON(!vops->alloc_fweh_info))
                return -EINVAL;
 
        if (WARN_ON(fwvid_list[fwvid].vmod))
index dac22534d0334e3490b9148e219d644b03e0638d..e6ac9fc341bceca725cb6632feae8b3df3332e0e 100644 (file)
@@ -14,6 +14,7 @@ struct brcmf_if;
 struct brcmf_fwvid_ops {
        void (*feat_attach)(struct brcmf_if *ifp);
        int (*set_sae_password)(struct brcmf_if *ifp, struct cfg80211_crypto_settings *crypto);
+       int (*alloc_fweh_info)(struct brcmf_pub *drvr);
 };
 
 /* exported functions */
@@ -47,4 +48,12 @@ static inline int brcmf_fwvid_set_sae_password(struct brcmf_if *ifp,
        return vops->set_sae_password(ifp, crypto);
 }
 
+static inline int brcmf_fwvid_alloc_fweh_info(struct brcmf_pub *drvr)
+{
+       if (!drvr->vops)
+               return -EIO;
+
+       return drvr->vops->alloc_fweh_info(drvr);
+}
+
 #endif /* FWVID_H_ */
index fd593b93ad404c16ca86d4a4dc8c32e7754d555c..05d7c2a4fba51c7c3875bf3d568c7d129ec3c9a8 100644 (file)
@@ -11,6 +11,8 @@
 
 #include "vops.h"
 
+#define BRCMF_WCC_E_LAST               213
+
 static int brcmf_wcc_set_sae_pwd(struct brcmf_if *ifp,
                                 struct cfg80211_crypto_settings *crypto)
 {
@@ -18,6 +20,21 @@ static int brcmf_wcc_set_sae_pwd(struct brcmf_if *ifp,
                              BRCMF_WSEC_PASSPHRASE);
 }
 
+static int brcmf_wcc_alloc_fweh_info(struct brcmf_pub *drvr)
+{
+       struct brcmf_fweh_info *fweh;
+
+       fweh = kzalloc(struct_size(fweh, evt_handler, BRCMF_WCC_E_LAST),
+                      GFP_KERNEL);
+       if (!fweh)
+               return -ENOMEM;
+
+       fweh->num_event_codes = BRCMF_WCC_E_LAST;
+       drvr->fweh = fweh;
+       return 0;
+}
+
 const struct brcmf_fwvid_ops brcmf_wcc_ops = {
        .set_sae_password = brcmf_wcc_set_sae_pwd,
+       .alloc_fweh_info = brcmf_wcc_alloc_fweh_info,
 };