1daeccac2SArend van Spriel // SPDX-License-Identifier: ISC
205491d2cSKalle Valo /*
305491d2cSKalle Valo * Copyright (c) 2012 Broadcom Corporation
405491d2cSKalle Valo */
505491d2cSKalle Valo #include <linux/netdevice.h>
605491d2cSKalle Valo
705491d2cSKalle Valo #include "brcmu_wifi.h"
805491d2cSKalle Valo #include "brcmu_utils.h"
905491d2cSKalle Valo
10a63b0987SRafał Miłecki #include "cfg80211.h"
1105491d2cSKalle Valo #include "core.h"
1205491d2cSKalle Valo #include "debug.h"
1305491d2cSKalle Valo #include "tracepoint.h"
1405491d2cSKalle Valo #include "fweh.h"
1505491d2cSKalle Valo #include "fwil.h"
169fdc64bbSFranky Lin #include "proto.h"
1705491d2cSKalle Valo
1805491d2cSKalle Valo /**
1905491d2cSKalle Valo * struct brcmf_fweh_queue_item - event item on event queue.
2005491d2cSKalle Valo *
2105491d2cSKalle Valo * @q: list element for queuing.
2205491d2cSKalle Valo * @code: event code.
2305491d2cSKalle Valo * @ifidx: interface index related to this event.
2405491d2cSKalle Valo * @ifaddr: ethernet address for interface.
2505491d2cSKalle Valo * @emsg: common parameters of the firmware event message.
264e124e1fSLee Jones * @datalen: length of the data array
2705491d2cSKalle Valo * @data: event specific data part of the firmware event.
2805491d2cSKalle Valo */
2905491d2cSKalle Valo struct brcmf_fweh_queue_item {
3005491d2cSKalle Valo struct list_head q;
3105491d2cSKalle Valo enum brcmf_fweh_event_code code;
3205491d2cSKalle Valo u8 ifidx;
3305491d2cSKalle Valo u8 ifaddr[ETH_ALEN];
3405491d2cSKalle Valo struct brcmf_event_msg_be emsg;
350aedbcafSHante Meuleman u32 datalen;
36232c897eSGustavo A. R. Silva u8 data[];
3705491d2cSKalle Valo };
3805491d2cSKalle Valo
394e124e1fSLee Jones /*
4005491d2cSKalle Valo * struct brcmf_fweh_event_name - code, name mapping entry.
4105491d2cSKalle Valo */
4205491d2cSKalle Valo struct brcmf_fweh_event_name {
4305491d2cSKalle Valo enum brcmf_fweh_event_code code;
4405491d2cSKalle Valo const char *name;
4505491d2cSKalle Valo };
4605491d2cSKalle Valo
4705491d2cSKalle Valo #ifdef DEBUG
4805491d2cSKalle Valo #define BRCMF_ENUM_DEF(id, val) \
4905491d2cSKalle Valo { val, #id },
5005491d2cSKalle Valo
5105491d2cSKalle Valo /* array for mapping code to event name */
5205491d2cSKalle Valo static struct brcmf_fweh_event_name fweh_event_names[] = {
5305491d2cSKalle Valo BRCMF_FWEH_EVENT_ENUM_DEFLIST
5405491d2cSKalle Valo };
5505491d2cSKalle Valo #undef BRCMF_ENUM_DEF
5605491d2cSKalle Valo
5705491d2cSKalle Valo /**
5805491d2cSKalle Valo * brcmf_fweh_event_name() - returns name for given event code.
5905491d2cSKalle Valo *
6005491d2cSKalle Valo * @code: code to lookup.
6105491d2cSKalle Valo */
brcmf_fweh_event_name(enum brcmf_fweh_event_code code)62e1c122d5SRafał Miłecki const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code)
6305491d2cSKalle Valo {
6405491d2cSKalle Valo int i;
6505491d2cSKalle Valo for (i = 0; i < ARRAY_SIZE(fweh_event_names); i++) {
6605491d2cSKalle Valo if (fweh_event_names[i].code == code)
6705491d2cSKalle Valo return fweh_event_names[i].name;
6805491d2cSKalle Valo }
6905491d2cSKalle Valo return "unknown";
7005491d2cSKalle Valo }
7105491d2cSKalle Valo #else
brcmf_fweh_event_name(enum brcmf_fweh_event_code code)72e1c122d5SRafał Miłecki const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code)
7305491d2cSKalle Valo {
7405491d2cSKalle Valo return "nodebug";
7505491d2cSKalle Valo }
7605491d2cSKalle Valo #endif
7705491d2cSKalle Valo
7805491d2cSKalle Valo /**
7905491d2cSKalle Valo * brcmf_fweh_queue_event() - create and queue event.
8005491d2cSKalle Valo *
8105491d2cSKalle Valo * @fweh: firmware event handling info.
8205491d2cSKalle Valo * @event: event queue entry.
8305491d2cSKalle Valo */
brcmf_fweh_queue_event(struct brcmf_fweh_info * fweh,struct brcmf_fweh_queue_item * event)8405491d2cSKalle Valo static void brcmf_fweh_queue_event(struct brcmf_fweh_info *fweh,
8505491d2cSKalle Valo struct brcmf_fweh_queue_item *event)
8605491d2cSKalle Valo {
8705491d2cSKalle Valo ulong flags;
8805491d2cSKalle Valo
8905491d2cSKalle Valo spin_lock_irqsave(&fweh->evt_q_lock, flags);
9005491d2cSKalle Valo list_add_tail(&event->q, &fweh->event_q);
9105491d2cSKalle Valo spin_unlock_irqrestore(&fweh->evt_q_lock, flags);
9205491d2cSKalle Valo schedule_work(&fweh->event_work);
9305491d2cSKalle Valo }
9405491d2cSKalle Valo
brcmf_fweh_call_event_handler(struct brcmf_pub * drvr,struct brcmf_if * ifp,enum brcmf_fweh_event_code code,struct brcmf_event_msg * emsg,void * data)95dcb1471bSRafał Miłecki static int brcmf_fweh_call_event_handler(struct brcmf_pub *drvr,
96dcb1471bSRafał Miłecki struct brcmf_if *ifp,
9705491d2cSKalle Valo enum brcmf_fweh_event_code code,
9805491d2cSKalle Valo struct brcmf_event_msg *emsg,
9905491d2cSKalle Valo void *data)
10005491d2cSKalle Valo {
10105491d2cSKalle Valo struct brcmf_fweh_info *fweh;
10205491d2cSKalle Valo int err = -EINVAL;
10305491d2cSKalle Valo
10405491d2cSKalle Valo if (ifp) {
10505491d2cSKalle Valo fweh = &ifp->drvr->fweh;
10605491d2cSKalle Valo
10705491d2cSKalle Valo /* handle the event if valid interface and handler */
10805491d2cSKalle Valo if (fweh->evt_handler[code])
10905491d2cSKalle Valo err = fweh->evt_handler[code](ifp, emsg, data);
11005491d2cSKalle Valo else
111dcb1471bSRafał Miłecki bphy_err(drvr, "unhandled event %d ignored\n", code);
11205491d2cSKalle Valo } else {
113dcb1471bSRafał Miłecki bphy_err(drvr, "no interface object\n");
11405491d2cSKalle Valo }
11505491d2cSKalle Valo return err;
11605491d2cSKalle Valo }
11705491d2cSKalle Valo
11805491d2cSKalle Valo /**
11905491d2cSKalle Valo * brcmf_fweh_handle_if_event() - handle IF event.
12005491d2cSKalle Valo *
12105491d2cSKalle Valo * @drvr: driver information object.
1224e124e1fSLee Jones * @emsg: event message object.
1234e124e1fSLee Jones * @data: event object.
12405491d2cSKalle Valo */
brcmf_fweh_handle_if_event(struct brcmf_pub * drvr,struct brcmf_event_msg * emsg,void * data)12505491d2cSKalle Valo static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr,
12605491d2cSKalle Valo struct brcmf_event_msg *emsg,
12705491d2cSKalle Valo void *data)
12805491d2cSKalle Valo {
12905491d2cSKalle Valo struct brcmf_if_event *ifevent = data;
13005491d2cSKalle Valo struct brcmf_if *ifp;
13105491d2cSKalle Valo bool is_p2pdev;
13205491d2cSKalle Valo
13337a869ecSHante Meuleman brcmf_dbg(EVENT, "action: %u ifidx: %u bsscfgidx: %u flags: %u role: %u\n",
13437a869ecSHante Meuleman ifevent->action, ifevent->ifidx, ifevent->bsscfgidx,
13505491d2cSKalle Valo ifevent->flags, ifevent->role);
13605491d2cSKalle Valo
13705491d2cSKalle Valo /* The P2P Device interface event must not be ignored contrary to what
13805491d2cSKalle Valo * firmware tells us. Older firmware uses p2p noif, with sta role.
13905491d2cSKalle Valo * This should be accepted when p2pdev_setup is ongoing. TDLS setup will
14005491d2cSKalle Valo * use the same ifevent and should be ignored.
14105491d2cSKalle Valo */
14205491d2cSKalle Valo is_p2pdev = ((ifevent->flags & BRCMF_E_IF_FLAG_NOIF) &&
14305491d2cSKalle Valo (ifevent->role == BRCMF_E_IF_ROLE_P2P_CLIENT ||
14405491d2cSKalle Valo ((ifevent->role == BRCMF_E_IF_ROLE_STA) &&
14505491d2cSKalle Valo (drvr->fweh.p2pdev_setup_ongoing))));
14605491d2cSKalle Valo if (!is_p2pdev && (ifevent->flags & BRCMF_E_IF_FLAG_NOIF)) {
14705491d2cSKalle Valo brcmf_dbg(EVENT, "event can be ignored\n");
14805491d2cSKalle Valo return;
14905491d2cSKalle Valo }
15005491d2cSKalle Valo if (ifevent->ifidx >= BRCMF_MAX_IFS) {
151dcb1471bSRafał Miłecki bphy_err(drvr, "invalid interface index: %u\n", ifevent->ifidx);
15205491d2cSKalle Valo return;
15305491d2cSKalle Valo }
15405491d2cSKalle Valo
15537a869ecSHante Meuleman ifp = drvr->iflist[ifevent->bsscfgidx];
15605491d2cSKalle Valo
15705491d2cSKalle Valo if (ifevent->action == BRCMF_E_IF_ADD) {
15805491d2cSKalle Valo brcmf_dbg(EVENT, "adding %s (%pM)\n", emsg->ifname,
15905491d2cSKalle Valo emsg->addr);
16037a869ecSHante Meuleman ifp = brcmf_add_if(drvr, ifevent->bsscfgidx, ifevent->ifidx,
16105491d2cSKalle Valo is_p2pdev, emsg->ifname, emsg->addr);
16205491d2cSKalle Valo if (IS_ERR(ifp))
16305491d2cSKalle Valo return;
16405491d2cSKalle Valo if (!is_p2pdev)
1659fdc64bbSFranky Lin brcmf_proto_add_if(drvr, ifp);
16605491d2cSKalle Valo if (!drvr->fweh.evt_handler[BRCMF_E_IF])
16705491d2cSKalle Valo if (brcmf_net_attach(ifp, false) < 0)
16805491d2cSKalle Valo return;
16905491d2cSKalle Valo }
17005491d2cSKalle Valo
17105491d2cSKalle Valo if (ifp && ifevent->action == BRCMF_E_IF_CHANGE)
17266ded1f8SFranky Lin brcmf_proto_reset_if(drvr, ifp);
17305491d2cSKalle Valo
17410c3ba7dSLee Jones brcmf_fweh_call_event_handler(drvr, ifp, emsg->event_code, emsg,
175dcb1471bSRafał Miłecki data);
17605491d2cSKalle Valo
177a63b0987SRafał Miłecki if (ifp && ifevent->action == BRCMF_E_IF_DEL) {
178a63b0987SRafał Miłecki bool armed = brcmf_cfg80211_vif_event_armed(drvr->config);
179a63b0987SRafał Miłecki
180a63b0987SRafał Miłecki /* Default handling in case no-one waits for this event */
181a63b0987SRafał Miłecki if (!armed)
182b50ddfa8SRafał Miłecki brcmf_remove_interface(ifp, false);
18305491d2cSKalle Valo }
184a63b0987SRafał Miłecki }
18505491d2cSKalle Valo
18605491d2cSKalle Valo /**
18705491d2cSKalle Valo * brcmf_fweh_dequeue_event() - get event from the queue.
18805491d2cSKalle Valo *
18905491d2cSKalle Valo * @fweh: firmware event handling info.
19005491d2cSKalle Valo */
19105491d2cSKalle Valo static struct brcmf_fweh_queue_item *
brcmf_fweh_dequeue_event(struct brcmf_fweh_info * fweh)19205491d2cSKalle Valo brcmf_fweh_dequeue_event(struct brcmf_fweh_info *fweh)
19305491d2cSKalle Valo {
19405491d2cSKalle Valo struct brcmf_fweh_queue_item *event = NULL;
19505491d2cSKalle Valo ulong flags;
19605491d2cSKalle Valo
19705491d2cSKalle Valo spin_lock_irqsave(&fweh->evt_q_lock, flags);
19805491d2cSKalle Valo if (!list_empty(&fweh->event_q)) {
19905491d2cSKalle Valo event = list_first_entry(&fweh->event_q,
20005491d2cSKalle Valo struct brcmf_fweh_queue_item, q);
20105491d2cSKalle Valo list_del(&event->q);
20205491d2cSKalle Valo }
20305491d2cSKalle Valo spin_unlock_irqrestore(&fweh->evt_q_lock, flags);
20405491d2cSKalle Valo
20505491d2cSKalle Valo return event;
20605491d2cSKalle Valo }
20705491d2cSKalle Valo
20805491d2cSKalle Valo /**
20905491d2cSKalle Valo * brcmf_fweh_event_worker() - firmware event worker.
21005491d2cSKalle Valo *
21105491d2cSKalle Valo * @work: worker object.
21205491d2cSKalle Valo */
brcmf_fweh_event_worker(struct work_struct * work)21305491d2cSKalle Valo static void brcmf_fweh_event_worker(struct work_struct *work)
21405491d2cSKalle Valo {
21505491d2cSKalle Valo struct brcmf_pub *drvr;
21605491d2cSKalle Valo struct brcmf_if *ifp;
21705491d2cSKalle Valo struct brcmf_fweh_info *fweh;
21805491d2cSKalle Valo struct brcmf_fweh_queue_item *event;
21905491d2cSKalle Valo int err = 0;
22005491d2cSKalle Valo struct brcmf_event_msg_be *emsg_be;
22105491d2cSKalle Valo struct brcmf_event_msg emsg;
22205491d2cSKalle Valo
22305491d2cSKalle Valo fweh = container_of(work, struct brcmf_fweh_info, event_work);
22405491d2cSKalle Valo drvr = container_of(fweh, struct brcmf_pub, fweh);
22505491d2cSKalle Valo
22605491d2cSKalle Valo while ((event = brcmf_fweh_dequeue_event(fweh))) {
22705491d2cSKalle Valo brcmf_dbg(EVENT, "event %s (%u) ifidx %u bsscfg %u addr %pM\n",
22805491d2cSKalle Valo brcmf_fweh_event_name(event->code), event->code,
22905491d2cSKalle Valo event->emsg.ifidx, event->emsg.bsscfgidx,
23005491d2cSKalle Valo event->emsg.addr);
231*6788ba8aSDokyung Song if (event->emsg.bsscfgidx >= BRCMF_MAX_IFS) {
232*6788ba8aSDokyung Song bphy_err(drvr, "invalid bsscfg index: %u\n", event->emsg.bsscfgidx);
233*6788ba8aSDokyung Song goto event_free;
234*6788ba8aSDokyung Song }
23505491d2cSKalle Valo
23605491d2cSKalle Valo /* convert event message */
23705491d2cSKalle Valo emsg_be = &event->emsg;
23805491d2cSKalle Valo emsg.version = be16_to_cpu(emsg_be->version);
23905491d2cSKalle Valo emsg.flags = be16_to_cpu(emsg_be->flags);
24005491d2cSKalle Valo emsg.event_code = event->code;
24105491d2cSKalle Valo emsg.status = be32_to_cpu(emsg_be->status);
24205491d2cSKalle Valo emsg.reason = be32_to_cpu(emsg_be->reason);
24305491d2cSKalle Valo emsg.auth_type = be32_to_cpu(emsg_be->auth_type);
24405491d2cSKalle Valo emsg.datalen = be32_to_cpu(emsg_be->datalen);
24505491d2cSKalle Valo memcpy(emsg.addr, emsg_be->addr, ETH_ALEN);
24605491d2cSKalle Valo memcpy(emsg.ifname, emsg_be->ifname, sizeof(emsg.ifname));
24705491d2cSKalle Valo emsg.ifidx = emsg_be->ifidx;
24805491d2cSKalle Valo emsg.bsscfgidx = emsg_be->bsscfgidx;
24905491d2cSKalle Valo
25005491d2cSKalle Valo brcmf_dbg(EVENT, " version %u flags %u status %u reason %u\n",
25105491d2cSKalle Valo emsg.version, emsg.flags, emsg.status, emsg.reason);
25205491d2cSKalle Valo brcmf_dbg_hex_dump(BRCMF_EVENT_ON(), event->data,
25305491d2cSKalle Valo min_t(u32, emsg.datalen, 64),
25405491d2cSKalle Valo "event payload, len=%d\n", emsg.datalen);
25505491d2cSKalle Valo
25605491d2cSKalle Valo /* special handling of interface event */
25705491d2cSKalle Valo if (event->code == BRCMF_E_IF) {
25805491d2cSKalle Valo brcmf_fweh_handle_if_event(drvr, &emsg, event->data);
25905491d2cSKalle Valo goto event_free;
26005491d2cSKalle Valo }
26105491d2cSKalle Valo
26205491d2cSKalle Valo if (event->code == BRCMF_E_TDLS_PEER_EVENT)
26305491d2cSKalle Valo ifp = drvr->iflist[0];
26405491d2cSKalle Valo else
26505491d2cSKalle Valo ifp = drvr->iflist[emsg.bsscfgidx];
266dcb1471bSRafał Miłecki err = brcmf_fweh_call_event_handler(drvr, ifp, event->code,
267dcb1471bSRafał Miłecki &emsg, event->data);
26805491d2cSKalle Valo if (err) {
269dcb1471bSRafał Miłecki bphy_err(drvr, "event handler failed (%d)\n",
27005491d2cSKalle Valo event->code);
27105491d2cSKalle Valo err = 0;
27205491d2cSKalle Valo }
27305491d2cSKalle Valo event_free:
27405491d2cSKalle Valo kfree(event);
27505491d2cSKalle Valo }
27605491d2cSKalle Valo }
27705491d2cSKalle Valo
27805491d2cSKalle Valo /**
27905491d2cSKalle Valo * brcmf_fweh_p2pdev_setup() - P2P device setup ongoing (or not).
28005491d2cSKalle Valo *
28105491d2cSKalle Valo * @ifp: ifp on which setup is taking place or finished.
28205491d2cSKalle Valo * @ongoing: p2p device setup in progress (or not).
28305491d2cSKalle Valo */
brcmf_fweh_p2pdev_setup(struct brcmf_if * ifp,bool ongoing)28405491d2cSKalle Valo void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing)
28505491d2cSKalle Valo {
28605491d2cSKalle Valo ifp->drvr->fweh.p2pdev_setup_ongoing = ongoing;
28705491d2cSKalle Valo }
28805491d2cSKalle Valo
28905491d2cSKalle Valo /**
29005491d2cSKalle Valo * brcmf_fweh_attach() - initialize firmware event handling.
29105491d2cSKalle Valo *
29205491d2cSKalle Valo * @drvr: driver information object.
29305491d2cSKalle Valo */
brcmf_fweh_attach(struct brcmf_pub * drvr)29405491d2cSKalle Valo void brcmf_fweh_attach(struct brcmf_pub *drvr)
29505491d2cSKalle Valo {
29605491d2cSKalle Valo struct brcmf_fweh_info *fweh = &drvr->fweh;
29705491d2cSKalle Valo INIT_WORK(&fweh->event_work, brcmf_fweh_event_worker);
29805491d2cSKalle Valo spin_lock_init(&fweh->evt_q_lock);
29905491d2cSKalle Valo INIT_LIST_HEAD(&fweh->event_q);
30005491d2cSKalle Valo }
30105491d2cSKalle Valo
30205491d2cSKalle Valo /**
30305491d2cSKalle Valo * brcmf_fweh_detach() - cleanup firmware event handling.
30405491d2cSKalle Valo *
30505491d2cSKalle Valo * @drvr: driver information object.
30605491d2cSKalle Valo */
brcmf_fweh_detach(struct brcmf_pub * drvr)30705491d2cSKalle Valo void brcmf_fweh_detach(struct brcmf_pub *drvr)
30805491d2cSKalle Valo {
30905491d2cSKalle Valo struct brcmf_fweh_info *fweh = &drvr->fweh;
31005491d2cSKalle Valo
3116aa5a83aSWright Feng /* cancel the worker if initialized */
3126aa5a83aSWright Feng if (fweh->event_work.func) {
31305491d2cSKalle Valo cancel_work_sync(&fweh->event_work);
31405491d2cSKalle Valo WARN_ON(!list_empty(&fweh->event_q));
31505491d2cSKalle Valo memset(fweh->evt_handler, 0, sizeof(fweh->evt_handler));
31605491d2cSKalle Valo }
3176aa5a83aSWright Feng }
31805491d2cSKalle Valo
31905491d2cSKalle Valo /**
32005491d2cSKalle Valo * brcmf_fweh_register() - register handler for given event code.
32105491d2cSKalle Valo *
32205491d2cSKalle Valo * @drvr: driver information object.
32305491d2cSKalle Valo * @code: event code.
32405491d2cSKalle Valo * @handler: handler for the given event code.
32505491d2cSKalle Valo */
brcmf_fweh_register(struct brcmf_pub * drvr,enum brcmf_fweh_event_code code,brcmf_fweh_handler_t handler)32605491d2cSKalle Valo int brcmf_fweh_register(struct brcmf_pub *drvr, enum brcmf_fweh_event_code code,
32705491d2cSKalle Valo brcmf_fweh_handler_t handler)
32805491d2cSKalle Valo {
32905491d2cSKalle Valo if (drvr->fweh.evt_handler[code]) {
330dcb1471bSRafał Miłecki bphy_err(drvr, "event code %d already registered\n", code);
33105491d2cSKalle Valo return -ENOSPC;
33205491d2cSKalle Valo }
33305491d2cSKalle Valo drvr->fweh.evt_handler[code] = handler;
33405491d2cSKalle Valo brcmf_dbg(TRACE, "event handler registered for %s\n",
33505491d2cSKalle Valo brcmf_fweh_event_name(code));
33605491d2cSKalle Valo return 0;
33705491d2cSKalle Valo }
33805491d2cSKalle Valo
33905491d2cSKalle Valo /**
34005491d2cSKalle Valo * brcmf_fweh_unregister() - remove handler for given code.
34105491d2cSKalle Valo *
34205491d2cSKalle Valo * @drvr: driver information object.
34305491d2cSKalle Valo * @code: event code.
34405491d2cSKalle Valo */
brcmf_fweh_unregister(struct brcmf_pub * drvr,enum brcmf_fweh_event_code code)34505491d2cSKalle Valo void brcmf_fweh_unregister(struct brcmf_pub *drvr,
34605491d2cSKalle Valo enum brcmf_fweh_event_code code)
34705491d2cSKalle Valo {
34805491d2cSKalle Valo brcmf_dbg(TRACE, "event handler cleared for %s\n",
34905491d2cSKalle Valo brcmf_fweh_event_name(code));
35005491d2cSKalle Valo drvr->fweh.evt_handler[code] = NULL;
35105491d2cSKalle Valo }
35205491d2cSKalle Valo
35305491d2cSKalle Valo /**
35405491d2cSKalle Valo * brcmf_fweh_activate_events() - enables firmware events registered.
35505491d2cSKalle Valo *
35605491d2cSKalle Valo * @ifp: primary interface object.
35705491d2cSKalle Valo */
brcmf_fweh_activate_events(struct brcmf_if * ifp)35805491d2cSKalle Valo int brcmf_fweh_activate_events(struct brcmf_if *ifp)
35905491d2cSKalle Valo {
360dcb1471bSRafał Miłecki struct brcmf_pub *drvr = ifp->drvr;
36105491d2cSKalle Valo int i, err;
36205491d2cSKalle Valo s8 eventmask[BRCMF_EVENTING_MASK_LEN];
36305491d2cSKalle Valo
3647fde010dSHante Meuleman memset(eventmask, 0, sizeof(eventmask));
36505491d2cSKalle Valo for (i = 0; i < BRCMF_E_LAST; i++) {
36605491d2cSKalle Valo if (ifp->drvr->fweh.evt_handler[i]) {
36705491d2cSKalle Valo brcmf_dbg(EVENT, "enable event %s\n",
36805491d2cSKalle Valo brcmf_fweh_event_name(i));
36905491d2cSKalle Valo setbit(eventmask, i);
37005491d2cSKalle Valo }
37105491d2cSKalle Valo }
37205491d2cSKalle Valo
37305491d2cSKalle Valo /* want to handle IF event as well */
37405491d2cSKalle Valo brcmf_dbg(EVENT, "enable event IF\n");
37505491d2cSKalle Valo setbit(eventmask, BRCMF_E_IF);
37605491d2cSKalle Valo
37705491d2cSKalle Valo err = brcmf_fil_iovar_data_set(ifp, "event_msgs",
37805491d2cSKalle Valo eventmask, BRCMF_EVENTING_MASK_LEN);
37905491d2cSKalle Valo if (err)
380dcb1471bSRafał Miłecki bphy_err(drvr, "Set event_msgs error (%d)\n", err);
38105491d2cSKalle Valo
38205491d2cSKalle Valo return err;
38305491d2cSKalle Valo }
38405491d2cSKalle Valo
38505491d2cSKalle Valo /**
38605491d2cSKalle Valo * brcmf_fweh_process_event() - process skb as firmware event.
38705491d2cSKalle Valo *
38805491d2cSKalle Valo * @drvr: driver information object.
38905491d2cSKalle Valo * @event_packet: event packet to process.
3904e124e1fSLee Jones * @packet_len: length of the packet
3919bd28c66SLee Jones * @gfp: memory allocation flags.
39205491d2cSKalle Valo *
39305491d2cSKalle Valo * If the packet buffer contains a firmware event message it will
39405491d2cSKalle Valo * dispatch the event to a registered handler (using worker).
39505491d2cSKalle Valo */
brcmf_fweh_process_event(struct brcmf_pub * drvr,struct brcmf_event * event_packet,u32 packet_len,gfp_t gfp)39605491d2cSKalle Valo void brcmf_fweh_process_event(struct brcmf_pub *drvr,
3970aedbcafSHante Meuleman struct brcmf_event *event_packet,
398c597ede4SSebastian Andrzej Siewior u32 packet_len, gfp_t gfp)
39905491d2cSKalle Valo {
40005491d2cSKalle Valo enum brcmf_fweh_event_code code;
40105491d2cSKalle Valo struct brcmf_fweh_info *fweh = &drvr->fweh;
40205491d2cSKalle Valo struct brcmf_fweh_queue_item *event;
40305491d2cSKalle Valo void *data;
40405491d2cSKalle Valo u32 datalen;
40505491d2cSKalle Valo
40605491d2cSKalle Valo /* get event info */
40705491d2cSKalle Valo code = get_unaligned_be32(&event_packet->msg.event_type);
40805491d2cSKalle Valo datalen = get_unaligned_be32(&event_packet->msg.datalen);
40905491d2cSKalle Valo data = &event_packet[1];
41005491d2cSKalle Valo
41105491d2cSKalle Valo if (code >= BRCMF_E_LAST)
41205491d2cSKalle Valo return;
41305491d2cSKalle Valo
41405491d2cSKalle Valo if (code != BRCMF_E_IF && !fweh->evt_handler[code])
41505491d2cSKalle Valo return;
41605491d2cSKalle Valo
417dd234912SKevin Cernekee if (datalen > BRCMF_DCMD_MAXLEN ||
418dd234912SKevin Cernekee datalen + sizeof(*event_packet) > packet_len)
4190aedbcafSHante Meuleman return;
4200aedbcafSHante Meuleman
421c597ede4SSebastian Andrzej Siewior event = kzalloc(sizeof(*event) + datalen, gfp);
42205491d2cSKalle Valo if (!event)
42305491d2cSKalle Valo return;
42405491d2cSKalle Valo
42505491d2cSKalle Valo event->code = code;
42605491d2cSKalle Valo event->ifidx = event_packet->msg.ifidx;
42705491d2cSKalle Valo
42805491d2cSKalle Valo /* use memcpy to get aligned event message */
42905491d2cSKalle Valo memcpy(&event->emsg, &event_packet->msg, sizeof(event->emsg));
43005491d2cSKalle Valo memcpy(event->data, data, datalen);
4310aedbcafSHante Meuleman event->datalen = datalen;
43205491d2cSKalle Valo memcpy(event->ifaddr, event_packet->eth.h_dest, ETH_ALEN);
43305491d2cSKalle Valo
43405491d2cSKalle Valo brcmf_fweh_queue_event(fweh, event);
43505491d2cSKalle Valo }
436