xref: /freebsd/sys/contrib/dev/broadcom/brcm80211/brcmfmac/fweh.c (revision b4c3e9b5b09c829b4135aff738bd2893ed052377)
1*b4c3e9b5SBjoern A. Zeeb // SPDX-License-Identifier: ISC
2*b4c3e9b5SBjoern A. Zeeb /*
3*b4c3e9b5SBjoern A. Zeeb  * Copyright (c) 2012 Broadcom Corporation
4*b4c3e9b5SBjoern A. Zeeb  */
5*b4c3e9b5SBjoern A. Zeeb #include <linux/netdevice.h>
6*b4c3e9b5SBjoern A. Zeeb 
7*b4c3e9b5SBjoern A. Zeeb #include "brcmu_wifi.h"
8*b4c3e9b5SBjoern A. Zeeb #include "brcmu_utils.h"
9*b4c3e9b5SBjoern A. Zeeb 
10*b4c3e9b5SBjoern A. Zeeb #include "cfg80211.h"
11*b4c3e9b5SBjoern A. Zeeb #include "core.h"
12*b4c3e9b5SBjoern A. Zeeb #include "debug.h"
13*b4c3e9b5SBjoern A. Zeeb #include "tracepoint.h"
14*b4c3e9b5SBjoern A. Zeeb #include "fweh.h"
15*b4c3e9b5SBjoern A. Zeeb #include "fwil.h"
16*b4c3e9b5SBjoern A. Zeeb #include "proto.h"
17*b4c3e9b5SBjoern A. Zeeb #include "bus.h"
18*b4c3e9b5SBjoern A. Zeeb #include "fwvid.h"
19*b4c3e9b5SBjoern A. Zeeb /**
20*b4c3e9b5SBjoern A. Zeeb  * struct brcmf_fweh_queue_item - event item on event queue.
21*b4c3e9b5SBjoern A. Zeeb  *
22*b4c3e9b5SBjoern A. Zeeb  * @q: list element for queuing.
23*b4c3e9b5SBjoern A. Zeeb  * @code: event code.
24*b4c3e9b5SBjoern A. Zeeb  * @ifidx: interface index related to this event.
25*b4c3e9b5SBjoern A. Zeeb  * @ifaddr: ethernet address for interface.
26*b4c3e9b5SBjoern A. Zeeb  * @emsg: common parameters of the firmware event message.
27*b4c3e9b5SBjoern A. Zeeb  * @datalen: length of the data array
28*b4c3e9b5SBjoern A. Zeeb  * @data: event specific data part of the firmware event.
29*b4c3e9b5SBjoern A. Zeeb  */
30*b4c3e9b5SBjoern A. Zeeb struct brcmf_fweh_queue_item {
31*b4c3e9b5SBjoern A. Zeeb 	struct list_head q;
32*b4c3e9b5SBjoern A. Zeeb 	u32 code;
33*b4c3e9b5SBjoern A. Zeeb 	u8 ifidx;
34*b4c3e9b5SBjoern A. Zeeb 	u8 ifaddr[ETH_ALEN];
35*b4c3e9b5SBjoern A. Zeeb 	struct brcmf_event_msg_be emsg;
36*b4c3e9b5SBjoern A. Zeeb 	u32 datalen;
37*b4c3e9b5SBjoern A. Zeeb 	u8 data[] __counted_by(datalen);
38*b4c3e9b5SBjoern A. Zeeb };
39*b4c3e9b5SBjoern A. Zeeb 
40*b4c3e9b5SBjoern A. Zeeb /*
41*b4c3e9b5SBjoern A. Zeeb  * struct brcmf_fweh_event_name - code, name mapping entry.
42*b4c3e9b5SBjoern A. Zeeb  */
43*b4c3e9b5SBjoern A. Zeeb struct brcmf_fweh_event_name {
44*b4c3e9b5SBjoern A. Zeeb 	enum brcmf_fweh_event_code code;
45*b4c3e9b5SBjoern A. Zeeb 	const char *name;
46*b4c3e9b5SBjoern A. Zeeb };
47*b4c3e9b5SBjoern A. Zeeb 
48*b4c3e9b5SBjoern A. Zeeb #ifdef DEBUG
49*b4c3e9b5SBjoern A. Zeeb #define BRCMF_ENUM_DEF(id, val) \
50*b4c3e9b5SBjoern A. Zeeb 	{ val, #id },
51*b4c3e9b5SBjoern A. Zeeb 
52*b4c3e9b5SBjoern A. Zeeb /* array for mapping code to event name */
53*b4c3e9b5SBjoern A. Zeeb static struct brcmf_fweh_event_name fweh_event_names[] = {
54*b4c3e9b5SBjoern A. Zeeb 	BRCMF_FWEH_EVENT_ENUM_DEFLIST
55*b4c3e9b5SBjoern A. Zeeb };
56*b4c3e9b5SBjoern A. Zeeb #undef BRCMF_ENUM_DEF
57*b4c3e9b5SBjoern A. Zeeb 
58*b4c3e9b5SBjoern A. Zeeb /**
59*b4c3e9b5SBjoern A. Zeeb  * brcmf_fweh_event_name() - returns name for given event code.
60*b4c3e9b5SBjoern A. Zeeb  *
61*b4c3e9b5SBjoern A. Zeeb  * @code: code to lookup.
62*b4c3e9b5SBjoern A. Zeeb  */
brcmf_fweh_event_name(enum brcmf_fweh_event_code code)63*b4c3e9b5SBjoern A. Zeeb const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code)
64*b4c3e9b5SBjoern A. Zeeb {
65*b4c3e9b5SBjoern A. Zeeb 	int i;
66*b4c3e9b5SBjoern A. Zeeb 	for (i = 0; i < ARRAY_SIZE(fweh_event_names); i++) {
67*b4c3e9b5SBjoern A. Zeeb 		if (fweh_event_names[i].code == code)
68*b4c3e9b5SBjoern A. Zeeb 			return fweh_event_names[i].name;
69*b4c3e9b5SBjoern A. Zeeb 	}
70*b4c3e9b5SBjoern A. Zeeb 	return "unknown";
71*b4c3e9b5SBjoern A. Zeeb }
72*b4c3e9b5SBjoern A. Zeeb #else
brcmf_fweh_event_name(enum brcmf_fweh_event_code code)73*b4c3e9b5SBjoern A. Zeeb const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code)
74*b4c3e9b5SBjoern A. Zeeb {
75*b4c3e9b5SBjoern A. Zeeb 	return "nodebug";
76*b4c3e9b5SBjoern A. Zeeb }
77*b4c3e9b5SBjoern A. Zeeb #endif
78*b4c3e9b5SBjoern A. Zeeb BRCMF_EXPORT_SYMBOL_GPL(brcmf_fweh_event_name);
79*b4c3e9b5SBjoern A. Zeeb 
80*b4c3e9b5SBjoern A. Zeeb /**
81*b4c3e9b5SBjoern A. Zeeb  * brcmf_fweh_queue_event() - create and queue event.
82*b4c3e9b5SBjoern A. Zeeb  *
83*b4c3e9b5SBjoern A. Zeeb  * @fweh: firmware event handling info.
84*b4c3e9b5SBjoern A. Zeeb  * @event: event queue entry.
85*b4c3e9b5SBjoern A. Zeeb  */
brcmf_fweh_queue_event(struct brcmf_fweh_info * fweh,struct brcmf_fweh_queue_item * event)86*b4c3e9b5SBjoern A. Zeeb static void brcmf_fweh_queue_event(struct brcmf_fweh_info *fweh,
87*b4c3e9b5SBjoern A. Zeeb 				   struct brcmf_fweh_queue_item *event)
88*b4c3e9b5SBjoern A. Zeeb {
89*b4c3e9b5SBjoern A. Zeeb 	ulong flags;
90*b4c3e9b5SBjoern A. Zeeb 
91*b4c3e9b5SBjoern A. Zeeb 	spin_lock_irqsave(&fweh->evt_q_lock, flags);
92*b4c3e9b5SBjoern A. Zeeb 	list_add_tail(&event->q, &fweh->event_q);
93*b4c3e9b5SBjoern A. Zeeb 	spin_unlock_irqrestore(&fweh->evt_q_lock, flags);
94*b4c3e9b5SBjoern A. Zeeb 	schedule_work(&fweh->event_work);
95*b4c3e9b5SBjoern A. Zeeb }
96*b4c3e9b5SBjoern A. Zeeb 
brcmf_fweh_call_event_handler(struct brcmf_pub * drvr,struct brcmf_if * ifp,u32 fwcode,struct brcmf_event_msg * emsg,void * data)97*b4c3e9b5SBjoern A. Zeeb static int brcmf_fweh_call_event_handler(struct brcmf_pub *drvr,
98*b4c3e9b5SBjoern A. Zeeb 					 struct brcmf_if *ifp,
99*b4c3e9b5SBjoern A. Zeeb 					 u32 fwcode,
100*b4c3e9b5SBjoern A. Zeeb 					 struct brcmf_event_msg *emsg,
101*b4c3e9b5SBjoern A. Zeeb 					 void *data)
102*b4c3e9b5SBjoern A. Zeeb {
103*b4c3e9b5SBjoern A. Zeeb 	struct brcmf_fweh_info *fweh;
104*b4c3e9b5SBjoern A. Zeeb 	int err = -EINVAL;
105*b4c3e9b5SBjoern A. Zeeb 
106*b4c3e9b5SBjoern A. Zeeb 	if (ifp) {
107*b4c3e9b5SBjoern A. Zeeb 		fweh = ifp->drvr->fweh;
108*b4c3e9b5SBjoern A. Zeeb 
109*b4c3e9b5SBjoern A. Zeeb 		/* handle the event if valid interface and handler */
110*b4c3e9b5SBjoern A. Zeeb 		if (fweh->evt_handler[fwcode])
111*b4c3e9b5SBjoern A. Zeeb 			err = fweh->evt_handler[fwcode](ifp, emsg, data);
112*b4c3e9b5SBjoern A. Zeeb 		else
113*b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "unhandled fwevt %d ignored\n", fwcode);
114*b4c3e9b5SBjoern A. Zeeb 	} else {
115*b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "no interface object\n");
116*b4c3e9b5SBjoern A. Zeeb 	}
117*b4c3e9b5SBjoern A. Zeeb 	return err;
118*b4c3e9b5SBjoern A. Zeeb }
119*b4c3e9b5SBjoern A. Zeeb 
120*b4c3e9b5SBjoern A. Zeeb /**
121*b4c3e9b5SBjoern A. Zeeb  * brcmf_fweh_handle_if_event() - handle IF event.
122*b4c3e9b5SBjoern A. Zeeb  *
123*b4c3e9b5SBjoern A. Zeeb  * @drvr: driver information object.
124*b4c3e9b5SBjoern A. Zeeb  * @emsg: event message object.
125*b4c3e9b5SBjoern A. Zeeb  * @data: event object.
126*b4c3e9b5SBjoern A. Zeeb  */
brcmf_fweh_handle_if_event(struct brcmf_pub * drvr,struct brcmf_event_msg * emsg,void * data)127*b4c3e9b5SBjoern A. Zeeb static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr,
128*b4c3e9b5SBjoern A. Zeeb 				       struct brcmf_event_msg *emsg,
129*b4c3e9b5SBjoern A. Zeeb 				       void *data)
130*b4c3e9b5SBjoern A. Zeeb {
131*b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if_event *ifevent = data;
132*b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp;
133*b4c3e9b5SBjoern A. Zeeb 	bool is_p2pdev;
134*b4c3e9b5SBjoern A. Zeeb 
135*b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(EVENT, "action: %u ifidx: %u bsscfgidx: %u flags: %u role: %u\n",
136*b4c3e9b5SBjoern A. Zeeb 		  ifevent->action, ifevent->ifidx, ifevent->bsscfgidx,
137*b4c3e9b5SBjoern A. Zeeb 		  ifevent->flags, ifevent->role);
138*b4c3e9b5SBjoern A. Zeeb 
139*b4c3e9b5SBjoern A. Zeeb 	/* The P2P Device interface event must not be ignored contrary to what
140*b4c3e9b5SBjoern A. Zeeb 	 * firmware tells us. Older firmware uses p2p noif, with sta role.
141*b4c3e9b5SBjoern A. Zeeb 	 * This should be accepted when p2pdev_setup is ongoing. TDLS setup will
142*b4c3e9b5SBjoern A. Zeeb 	 * use the same ifevent and should be ignored.
143*b4c3e9b5SBjoern A. Zeeb 	 */
144*b4c3e9b5SBjoern A. Zeeb 	is_p2pdev = ((ifevent->flags & BRCMF_E_IF_FLAG_NOIF) &&
145*b4c3e9b5SBjoern A. Zeeb 		     (ifevent->role == BRCMF_E_IF_ROLE_P2P_CLIENT ||
146*b4c3e9b5SBjoern A. Zeeb 		      ((ifevent->role == BRCMF_E_IF_ROLE_STA) &&
147*b4c3e9b5SBjoern A. Zeeb 		       (drvr->fweh->p2pdev_setup_ongoing))));
148*b4c3e9b5SBjoern A. Zeeb 	if (!is_p2pdev && (ifevent->flags & BRCMF_E_IF_FLAG_NOIF)) {
149*b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(EVENT, "event can be ignored\n");
150*b4c3e9b5SBjoern A. Zeeb 		return;
151*b4c3e9b5SBjoern A. Zeeb 	}
152*b4c3e9b5SBjoern A. Zeeb 	if (ifevent->ifidx >= BRCMF_MAX_IFS) {
153*b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "invalid interface index: %u\n", ifevent->ifidx);
154*b4c3e9b5SBjoern A. Zeeb 		return;
155*b4c3e9b5SBjoern A. Zeeb 	}
156*b4c3e9b5SBjoern A. Zeeb 
157*b4c3e9b5SBjoern A. Zeeb 	ifp = drvr->iflist[ifevent->bsscfgidx];
158*b4c3e9b5SBjoern A. Zeeb 
159*b4c3e9b5SBjoern A. Zeeb 	if (ifevent->action == BRCMF_E_IF_ADD) {
160*b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(EVENT, "adding %s (%pM)\n", emsg->ifname,
161*b4c3e9b5SBjoern A. Zeeb 			  emsg->addr);
162*b4c3e9b5SBjoern A. Zeeb 		ifp = brcmf_add_if(drvr, ifevent->bsscfgidx, ifevent->ifidx,
163*b4c3e9b5SBjoern A. Zeeb 				   is_p2pdev, emsg->ifname, emsg->addr);
164*b4c3e9b5SBjoern A. Zeeb 		if (IS_ERR(ifp))
165*b4c3e9b5SBjoern A. Zeeb 			return;
166*b4c3e9b5SBjoern A. Zeeb 		if (!is_p2pdev)
167*b4c3e9b5SBjoern A. Zeeb 			brcmf_proto_add_if(drvr, ifp);
168*b4c3e9b5SBjoern A. Zeeb 		if (!drvr->fweh->evt_handler[BRCMF_E_IF])
169*b4c3e9b5SBjoern A. Zeeb 			if (brcmf_net_attach(ifp, false) < 0)
170*b4c3e9b5SBjoern A. Zeeb 				return;
171*b4c3e9b5SBjoern A. Zeeb 	}
172*b4c3e9b5SBjoern A. Zeeb 
173*b4c3e9b5SBjoern A. Zeeb 	if (ifp && ifevent->action == BRCMF_E_IF_CHANGE)
174*b4c3e9b5SBjoern A. Zeeb 		brcmf_proto_reset_if(drvr, ifp);
175*b4c3e9b5SBjoern A. Zeeb 
176*b4c3e9b5SBjoern A. Zeeb 	brcmf_fweh_call_event_handler(drvr, ifp, emsg->event_code, emsg,
177*b4c3e9b5SBjoern A. Zeeb 				      data);
178*b4c3e9b5SBjoern A. Zeeb 
179*b4c3e9b5SBjoern A. Zeeb 	if (ifp && ifevent->action == BRCMF_E_IF_DEL) {
180*b4c3e9b5SBjoern A. Zeeb 		bool armed = brcmf_cfg80211_vif_event_armed(drvr->config);
181*b4c3e9b5SBjoern A. Zeeb 
182*b4c3e9b5SBjoern A. Zeeb 		/* Default handling in case no-one waits for this event */
183*b4c3e9b5SBjoern A. Zeeb 		if (!armed)
184*b4c3e9b5SBjoern A. Zeeb 			brcmf_remove_interface(ifp, false);
185*b4c3e9b5SBjoern A. Zeeb 	}
186*b4c3e9b5SBjoern A. Zeeb }
187*b4c3e9b5SBjoern A. Zeeb 
brcmf_fweh_map_event_code(struct brcmf_fweh_info * fweh,enum brcmf_fweh_event_code code,u32 * fw_code)188*b4c3e9b5SBjoern A. Zeeb static void brcmf_fweh_map_event_code(struct brcmf_fweh_info *fweh,
189*b4c3e9b5SBjoern A. Zeeb 				      enum brcmf_fweh_event_code code,
190*b4c3e9b5SBjoern A. Zeeb 				      u32 *fw_code)
191*b4c3e9b5SBjoern A. Zeeb {
192*b4c3e9b5SBjoern A. Zeeb 	int i;
193*b4c3e9b5SBjoern A. Zeeb 
194*b4c3e9b5SBjoern A. Zeeb 	if (WARN_ON(!fw_code))
195*b4c3e9b5SBjoern A. Zeeb 		return;
196*b4c3e9b5SBjoern A. Zeeb 
197*b4c3e9b5SBjoern A. Zeeb 	*fw_code = code;
198*b4c3e9b5SBjoern A. Zeeb 	if (fweh->event_map) {
199*b4c3e9b5SBjoern A. Zeeb 		for (i = 0; i < fweh->event_map->n_items; i++) {
200*b4c3e9b5SBjoern A. Zeeb 			if (fweh->event_map->items[i].code == code) {
201*b4c3e9b5SBjoern A. Zeeb 				*fw_code = fweh->event_map->items[i].fwevt_code;
202*b4c3e9b5SBjoern A. Zeeb 				break;
203*b4c3e9b5SBjoern A. Zeeb 			}
204*b4c3e9b5SBjoern A. Zeeb 		}
205*b4c3e9b5SBjoern A. Zeeb 	}
206*b4c3e9b5SBjoern A. Zeeb }
207*b4c3e9b5SBjoern A. Zeeb 
brcmf_fweh_map_fwevt_code(struct brcmf_fweh_info * fweh,u32 fw_code,enum brcmf_fweh_event_code * code)208*b4c3e9b5SBjoern A. Zeeb static void brcmf_fweh_map_fwevt_code(struct brcmf_fweh_info *fweh, u32 fw_code,
209*b4c3e9b5SBjoern A. Zeeb 				      enum brcmf_fweh_event_code *code)
210*b4c3e9b5SBjoern A. Zeeb {
211*b4c3e9b5SBjoern A. Zeeb 	int i;
212*b4c3e9b5SBjoern A. Zeeb 
213*b4c3e9b5SBjoern A. Zeeb 	if (WARN_ON(!code))
214*b4c3e9b5SBjoern A. Zeeb 		return;
215*b4c3e9b5SBjoern A. Zeeb 
216*b4c3e9b5SBjoern A. Zeeb 	*code = fw_code;
217*b4c3e9b5SBjoern A. Zeeb 	if (fweh->event_map) {
218*b4c3e9b5SBjoern A. Zeeb 		for (i = 0; i < fweh->event_map->n_items; i++) {
219*b4c3e9b5SBjoern A. Zeeb 			if (fweh->event_map->items[i].fwevt_code == fw_code) {
220*b4c3e9b5SBjoern A. Zeeb 				*code = fweh->event_map->items[i].code;
221*b4c3e9b5SBjoern A. Zeeb 				break;
222*b4c3e9b5SBjoern A. Zeeb 			}
223*b4c3e9b5SBjoern A. Zeeb 		}
224*b4c3e9b5SBjoern A. Zeeb 	}
225*b4c3e9b5SBjoern A. Zeeb }
226*b4c3e9b5SBjoern A. Zeeb 
227*b4c3e9b5SBjoern A. Zeeb /**
228*b4c3e9b5SBjoern A. Zeeb  * brcmf_fweh_dequeue_event() - get event from the queue.
229*b4c3e9b5SBjoern A. Zeeb  *
230*b4c3e9b5SBjoern A. Zeeb  * @fweh: firmware event handling info.
231*b4c3e9b5SBjoern A. Zeeb  */
232*b4c3e9b5SBjoern A. Zeeb static struct brcmf_fweh_queue_item *
brcmf_fweh_dequeue_event(struct brcmf_fweh_info * fweh)233*b4c3e9b5SBjoern A. Zeeb brcmf_fweh_dequeue_event(struct brcmf_fweh_info *fweh)
234*b4c3e9b5SBjoern A. Zeeb {
235*b4c3e9b5SBjoern A. Zeeb 	struct brcmf_fweh_queue_item *event = NULL;
236*b4c3e9b5SBjoern A. Zeeb 	ulong flags;
237*b4c3e9b5SBjoern A. Zeeb 
238*b4c3e9b5SBjoern A. Zeeb 	spin_lock_irqsave(&fweh->evt_q_lock, flags);
239*b4c3e9b5SBjoern A. Zeeb 	if (!list_empty(&fweh->event_q)) {
240*b4c3e9b5SBjoern A. Zeeb 		event = list_first_entry(&fweh->event_q,
241*b4c3e9b5SBjoern A. Zeeb 					 struct brcmf_fweh_queue_item, q);
242*b4c3e9b5SBjoern A. Zeeb 		list_del(&event->q);
243*b4c3e9b5SBjoern A. Zeeb 	}
244*b4c3e9b5SBjoern A. Zeeb 	spin_unlock_irqrestore(&fweh->evt_q_lock, flags);
245*b4c3e9b5SBjoern A. Zeeb 
246*b4c3e9b5SBjoern A. Zeeb 	return event;
247*b4c3e9b5SBjoern A. Zeeb }
248*b4c3e9b5SBjoern A. Zeeb 
249*b4c3e9b5SBjoern A. Zeeb /**
250*b4c3e9b5SBjoern A. Zeeb  * brcmf_fweh_event_worker() - firmware event worker.
251*b4c3e9b5SBjoern A. Zeeb  *
252*b4c3e9b5SBjoern A. Zeeb  * @work: worker object.
253*b4c3e9b5SBjoern A. Zeeb  */
brcmf_fweh_event_worker(struct work_struct * work)254*b4c3e9b5SBjoern A. Zeeb static void brcmf_fweh_event_worker(struct work_struct *work)
255*b4c3e9b5SBjoern A. Zeeb {
256*b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr;
257*b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp;
258*b4c3e9b5SBjoern A. Zeeb 	struct brcmf_fweh_info *fweh;
259*b4c3e9b5SBjoern A. Zeeb 	struct brcmf_fweh_queue_item *event;
260*b4c3e9b5SBjoern A. Zeeb 	int err = 0;
261*b4c3e9b5SBjoern A. Zeeb 	struct brcmf_event_msg_be *emsg_be;
262*b4c3e9b5SBjoern A. Zeeb 	struct brcmf_event_msg emsg;
263*b4c3e9b5SBjoern A. Zeeb 
264*b4c3e9b5SBjoern A. Zeeb 	fweh = container_of(work, struct brcmf_fweh_info, event_work);
265*b4c3e9b5SBjoern A. Zeeb 	drvr = fweh->drvr;
266*b4c3e9b5SBjoern A. Zeeb 
267*b4c3e9b5SBjoern A. Zeeb 	while ((event = brcmf_fweh_dequeue_event(fweh))) {
268*b4c3e9b5SBjoern A. Zeeb 		enum brcmf_fweh_event_code code;
269*b4c3e9b5SBjoern A. Zeeb 
270*b4c3e9b5SBjoern A. Zeeb 		brcmf_fweh_map_fwevt_code(fweh, event->code, &code);
271*b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(EVENT, "event %s (%u:%u) ifidx %u bsscfg %u addr %pM\n",
272*b4c3e9b5SBjoern A. Zeeb 			  brcmf_fweh_event_name(code), code, event->code,
273*b4c3e9b5SBjoern A. Zeeb 			  event->emsg.ifidx, event->emsg.bsscfgidx,
274*b4c3e9b5SBjoern A. Zeeb 			  event->emsg.addr);
275*b4c3e9b5SBjoern A. Zeeb 		if (event->emsg.bsscfgidx >= BRCMF_MAX_IFS) {
276*b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "invalid bsscfg index: %u\n",
277*b4c3e9b5SBjoern A. Zeeb 				 event->emsg.bsscfgidx);
278*b4c3e9b5SBjoern A. Zeeb 			goto event_free;
279*b4c3e9b5SBjoern A. Zeeb 		}
280*b4c3e9b5SBjoern A. Zeeb 
281*b4c3e9b5SBjoern A. Zeeb 		/* convert event message */
282*b4c3e9b5SBjoern A. Zeeb 		emsg_be = &event->emsg;
283*b4c3e9b5SBjoern A. Zeeb 		emsg.version = be16_to_cpu(emsg_be->version);
284*b4c3e9b5SBjoern A. Zeeb 		emsg.flags = be16_to_cpu(emsg_be->flags);
285*b4c3e9b5SBjoern A. Zeeb 		emsg.event_code = code;
286*b4c3e9b5SBjoern A. Zeeb 		emsg.status = be32_to_cpu(emsg_be->status);
287*b4c3e9b5SBjoern A. Zeeb 		emsg.reason = be32_to_cpu(emsg_be->reason);
288*b4c3e9b5SBjoern A. Zeeb 		emsg.auth_type = be32_to_cpu(emsg_be->auth_type);
289*b4c3e9b5SBjoern A. Zeeb 		emsg.datalen = be32_to_cpu(emsg_be->datalen);
290*b4c3e9b5SBjoern A. Zeeb 		memcpy(emsg.addr, emsg_be->addr, ETH_ALEN);
291*b4c3e9b5SBjoern A. Zeeb 		memcpy(emsg.ifname, emsg_be->ifname, sizeof(emsg.ifname));
292*b4c3e9b5SBjoern A. Zeeb 		emsg.ifidx = emsg_be->ifidx;
293*b4c3e9b5SBjoern A. Zeeb 		emsg.bsscfgidx = emsg_be->bsscfgidx;
294*b4c3e9b5SBjoern A. Zeeb 
295*b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(EVENT, "  version %u flags %u status %u reason %u\n",
296*b4c3e9b5SBjoern A. Zeeb 			  emsg.version, emsg.flags, emsg.status, emsg.reason);
297*b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg_hex_dump(BRCMF_EVENT_ON(), event->data,
298*b4c3e9b5SBjoern A. Zeeb 				   min_t(u32, emsg.datalen, 64),
299*b4c3e9b5SBjoern A. Zeeb 				   "event payload, len=%d\n", emsg.datalen);
300*b4c3e9b5SBjoern A. Zeeb 
301*b4c3e9b5SBjoern A. Zeeb 		/* special handling of interface event */
302*b4c3e9b5SBjoern A. Zeeb 		if (event->code == BRCMF_E_IF) {
303*b4c3e9b5SBjoern A. Zeeb 			brcmf_fweh_handle_if_event(drvr, &emsg, event->data);
304*b4c3e9b5SBjoern A. Zeeb 			goto event_free;
305*b4c3e9b5SBjoern A. Zeeb 		}
306*b4c3e9b5SBjoern A. Zeeb 
307*b4c3e9b5SBjoern A. Zeeb 		if (event->code == BRCMF_E_TDLS_PEER_EVENT)
308*b4c3e9b5SBjoern A. Zeeb 			ifp = drvr->iflist[0];
309*b4c3e9b5SBjoern A. Zeeb 		else
310*b4c3e9b5SBjoern A. Zeeb 			ifp = drvr->iflist[emsg.bsscfgidx];
311*b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fweh_call_event_handler(drvr, ifp, event->code,
312*b4c3e9b5SBjoern A. Zeeb 						    &emsg, event->data);
313*b4c3e9b5SBjoern A. Zeeb 		if (err) {
314*b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "event handler failed (%d)\n",
315*b4c3e9b5SBjoern A. Zeeb 				 event->code);
316*b4c3e9b5SBjoern A. Zeeb 			err = 0;
317*b4c3e9b5SBjoern A. Zeeb 		}
318*b4c3e9b5SBjoern A. Zeeb event_free:
319*b4c3e9b5SBjoern A. Zeeb 		kfree(event);
320*b4c3e9b5SBjoern A. Zeeb 	}
321*b4c3e9b5SBjoern A. Zeeb }
322*b4c3e9b5SBjoern A. Zeeb 
323*b4c3e9b5SBjoern A. Zeeb /**
324*b4c3e9b5SBjoern A. Zeeb  * brcmf_fweh_p2pdev_setup() - P2P device setup ongoing (or not).
325*b4c3e9b5SBjoern A. Zeeb  *
326*b4c3e9b5SBjoern A. Zeeb  * @ifp: ifp on which setup is taking place or finished.
327*b4c3e9b5SBjoern A. Zeeb  * @ongoing: p2p device setup in progress (or not).
328*b4c3e9b5SBjoern A. Zeeb  */
brcmf_fweh_p2pdev_setup(struct brcmf_if * ifp,bool ongoing)329*b4c3e9b5SBjoern A. Zeeb void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing)
330*b4c3e9b5SBjoern A. Zeeb {
331*b4c3e9b5SBjoern A. Zeeb 	ifp->drvr->fweh->p2pdev_setup_ongoing = ongoing;
332*b4c3e9b5SBjoern A. Zeeb }
333*b4c3e9b5SBjoern A. Zeeb 
334*b4c3e9b5SBjoern A. Zeeb /**
335*b4c3e9b5SBjoern A. Zeeb  * brcmf_fweh_attach() - initialize firmware event handling.
336*b4c3e9b5SBjoern A. Zeeb  *
337*b4c3e9b5SBjoern A. Zeeb  * @drvr: driver information object.
338*b4c3e9b5SBjoern A. Zeeb  */
brcmf_fweh_attach(struct brcmf_pub * drvr)339*b4c3e9b5SBjoern A. Zeeb int brcmf_fweh_attach(struct brcmf_pub *drvr)
340*b4c3e9b5SBjoern A. Zeeb {
341*b4c3e9b5SBjoern A. Zeeb 	struct brcmf_fweh_info *fweh;
342*b4c3e9b5SBjoern A. Zeeb 	int err;
343*b4c3e9b5SBjoern A. Zeeb 
344*b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fwvid_alloc_fweh_info(drvr);
345*b4c3e9b5SBjoern A. Zeeb 	if (err < 0)
346*b4c3e9b5SBjoern A. Zeeb 		return err;
347*b4c3e9b5SBjoern A. Zeeb 
348*b4c3e9b5SBjoern A. Zeeb 	fweh = drvr->fweh;
349*b4c3e9b5SBjoern A. Zeeb 	fweh->drvr = drvr;
350*b4c3e9b5SBjoern A. Zeeb 
351*b4c3e9b5SBjoern A. Zeeb 	fweh->event_mask_len = DIV_ROUND_UP(fweh->num_event_codes, 8);
352*b4c3e9b5SBjoern A. Zeeb 	fweh->event_mask = kzalloc(fweh->event_mask_len, GFP_KERNEL);
353*b4c3e9b5SBjoern A. Zeeb 	if (!fweh->event_mask)
354*b4c3e9b5SBjoern A. Zeeb 		return -ENOMEM;
355*b4c3e9b5SBjoern A. Zeeb 
356*b4c3e9b5SBjoern A. Zeeb 	INIT_WORK(&fweh->event_work, brcmf_fweh_event_worker);
357*b4c3e9b5SBjoern A. Zeeb 	spin_lock_init(&fweh->evt_q_lock);
358*b4c3e9b5SBjoern A. Zeeb 	INIT_LIST_HEAD(&fweh->event_q);
359*b4c3e9b5SBjoern A. Zeeb 	return 0;
360*b4c3e9b5SBjoern A. Zeeb }
361*b4c3e9b5SBjoern A. Zeeb 
362*b4c3e9b5SBjoern A. Zeeb /**
363*b4c3e9b5SBjoern A. Zeeb  * brcmf_fweh_detach() - cleanup firmware event handling.
364*b4c3e9b5SBjoern A. Zeeb  *
365*b4c3e9b5SBjoern A. Zeeb  * @drvr: driver information object.
366*b4c3e9b5SBjoern A. Zeeb  */
brcmf_fweh_detach(struct brcmf_pub * drvr)367*b4c3e9b5SBjoern A. Zeeb void brcmf_fweh_detach(struct brcmf_pub *drvr)
368*b4c3e9b5SBjoern A. Zeeb {
369*b4c3e9b5SBjoern A. Zeeb 	struct brcmf_fweh_info *fweh = drvr->fweh;
370*b4c3e9b5SBjoern A. Zeeb 
371*b4c3e9b5SBjoern A. Zeeb 	if (!fweh)
372*b4c3e9b5SBjoern A. Zeeb 		return;
373*b4c3e9b5SBjoern A. Zeeb 
374*b4c3e9b5SBjoern A. Zeeb 	/* cancel the worker if initialized */
375*b4c3e9b5SBjoern A. Zeeb 	if (fweh->event_work.func) {
376*b4c3e9b5SBjoern A. Zeeb 		cancel_work_sync(&fweh->event_work);
377*b4c3e9b5SBjoern A. Zeeb 		WARN_ON(!list_empty(&fweh->event_q));
378*b4c3e9b5SBjoern A. Zeeb 	}
379*b4c3e9b5SBjoern A. Zeeb 	drvr->fweh = NULL;
380*b4c3e9b5SBjoern A. Zeeb 	kfree(fweh->event_mask);
381*b4c3e9b5SBjoern A. Zeeb 	kfree(fweh);
382*b4c3e9b5SBjoern A. Zeeb }
383*b4c3e9b5SBjoern A. Zeeb 
384*b4c3e9b5SBjoern A. Zeeb /**
385*b4c3e9b5SBjoern A. Zeeb  * brcmf_fweh_register() - register handler for given event code.
386*b4c3e9b5SBjoern A. Zeeb  *
387*b4c3e9b5SBjoern A. Zeeb  * @drvr: driver information object.
388*b4c3e9b5SBjoern A. Zeeb  * @code: event code.
389*b4c3e9b5SBjoern A. Zeeb  * @handler: handler for the given event code.
390*b4c3e9b5SBjoern A. Zeeb  */
brcmf_fweh_register(struct brcmf_pub * drvr,enum brcmf_fweh_event_code code,brcmf_fweh_handler_t handler)391*b4c3e9b5SBjoern A. Zeeb int brcmf_fweh_register(struct brcmf_pub *drvr, enum brcmf_fweh_event_code code,
392*b4c3e9b5SBjoern A. Zeeb 			brcmf_fweh_handler_t handler)
393*b4c3e9b5SBjoern A. Zeeb {
394*b4c3e9b5SBjoern A. Zeeb 	struct brcmf_fweh_info *fweh = drvr->fweh;
395*b4c3e9b5SBjoern A. Zeeb 	u32 evt_handler_idx;
396*b4c3e9b5SBjoern A. Zeeb 
397*b4c3e9b5SBjoern A. Zeeb 	brcmf_fweh_map_event_code(fweh, code, &evt_handler_idx);
398*b4c3e9b5SBjoern A. Zeeb 
399*b4c3e9b5SBjoern A. Zeeb 	if (fweh->evt_handler[evt_handler_idx]) {
400*b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "event code %d already registered\n", code);
401*b4c3e9b5SBjoern A. Zeeb 		return -ENOSPC;
402*b4c3e9b5SBjoern A. Zeeb 	}
403*b4c3e9b5SBjoern A. Zeeb 
404*b4c3e9b5SBjoern A. Zeeb 	fweh->evt_handler[evt_handler_idx] = handler;
405*b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "event handler registered for %s\n",
406*b4c3e9b5SBjoern A. Zeeb 		  brcmf_fweh_event_name(code));
407*b4c3e9b5SBjoern A. Zeeb 	return 0;
408*b4c3e9b5SBjoern A. Zeeb }
409*b4c3e9b5SBjoern A. Zeeb BRCMF_EXPORT_SYMBOL_GPL(brcmf_fweh_register);
410*b4c3e9b5SBjoern A. Zeeb 
411*b4c3e9b5SBjoern A. Zeeb /**
412*b4c3e9b5SBjoern A. Zeeb  * brcmf_fweh_unregister() - remove handler for given code.
413*b4c3e9b5SBjoern A. Zeeb  *
414*b4c3e9b5SBjoern A. Zeeb  * @drvr: driver information object.
415*b4c3e9b5SBjoern A. Zeeb  * @code: event code.
416*b4c3e9b5SBjoern A. Zeeb  */
brcmf_fweh_unregister(struct brcmf_pub * drvr,enum brcmf_fweh_event_code code)417*b4c3e9b5SBjoern A. Zeeb void brcmf_fweh_unregister(struct brcmf_pub *drvr,
418*b4c3e9b5SBjoern A. Zeeb 			   enum brcmf_fweh_event_code code)
419*b4c3e9b5SBjoern A. Zeeb {
420*b4c3e9b5SBjoern A. Zeeb 	u32 evt_handler_idx;
421*b4c3e9b5SBjoern A. Zeeb 
422*b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "event handler cleared for %s\n",
423*b4c3e9b5SBjoern A. Zeeb 		  brcmf_fweh_event_name(code));
424*b4c3e9b5SBjoern A. Zeeb 	brcmf_fweh_map_event_code(drvr->fweh, code, &evt_handler_idx);
425*b4c3e9b5SBjoern A. Zeeb 	drvr->fweh->evt_handler[evt_handler_idx] = NULL;
426*b4c3e9b5SBjoern A. Zeeb }
427*b4c3e9b5SBjoern A. Zeeb 
428*b4c3e9b5SBjoern A. Zeeb /**
429*b4c3e9b5SBjoern A. Zeeb  * brcmf_fweh_activate_events() - enables firmware events registered.
430*b4c3e9b5SBjoern A. Zeeb  *
431*b4c3e9b5SBjoern A. Zeeb  * @ifp: primary interface object.
432*b4c3e9b5SBjoern A. Zeeb  */
brcmf_fweh_activate_events(struct brcmf_if * ifp)433*b4c3e9b5SBjoern A. Zeeb int brcmf_fweh_activate_events(struct brcmf_if *ifp)
434*b4c3e9b5SBjoern A. Zeeb {
435*b4c3e9b5SBjoern A. Zeeb 	struct brcmf_fweh_info *fweh = ifp->drvr->fweh;
436*b4c3e9b5SBjoern A. Zeeb 	enum brcmf_fweh_event_code code;
437*b4c3e9b5SBjoern A. Zeeb 	int i, err;
438*b4c3e9b5SBjoern A. Zeeb 
439*b4c3e9b5SBjoern A. Zeeb 	memset(fweh->event_mask, 0, fweh->event_mask_len);
440*b4c3e9b5SBjoern A. Zeeb 	for (i = 0; i < fweh->num_event_codes; i++) {
441*b4c3e9b5SBjoern A. Zeeb 		if (fweh->evt_handler[i]) {
442*b4c3e9b5SBjoern A. Zeeb 			brcmf_fweh_map_fwevt_code(fweh, i, &code);
443*b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(EVENT, "enable event %s\n",
444*b4c3e9b5SBjoern A. Zeeb 				  brcmf_fweh_event_name(code));
445*b4c3e9b5SBjoern A. Zeeb 			setbit(fweh->event_mask, i);
446*b4c3e9b5SBjoern A. Zeeb 		}
447*b4c3e9b5SBjoern A. Zeeb 	}
448*b4c3e9b5SBjoern A. Zeeb 
449*b4c3e9b5SBjoern A. Zeeb 	/* want to handle IF event as well */
450*b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(EVENT, "enable event IF\n");
451*b4c3e9b5SBjoern A. Zeeb 	setbit(fweh->event_mask, BRCMF_E_IF);
452*b4c3e9b5SBjoern A. Zeeb 
453*b4c3e9b5SBjoern A. Zeeb 	/* allow per-vendor method to activate firmware events */
454*b4c3e9b5SBjoern A. Zeeb 	if (!brcmf_fwvid_activate_events(ifp))
455*b4c3e9b5SBjoern A. Zeeb 		return 0;
456*b4c3e9b5SBjoern A. Zeeb 
457*b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_data_set(ifp, "event_msgs", fweh->event_mask,
458*b4c3e9b5SBjoern A. Zeeb 				       fweh->event_mask_len);
459*b4c3e9b5SBjoern A. Zeeb 	if (err)
460*b4c3e9b5SBjoern A. Zeeb 		bphy_err(fweh->drvr, "Set event_msgs error (%d)\n", err);
461*b4c3e9b5SBjoern A. Zeeb 	return err;
462*b4c3e9b5SBjoern A. Zeeb }
463*b4c3e9b5SBjoern A. Zeeb 
464*b4c3e9b5SBjoern A. Zeeb /**
465*b4c3e9b5SBjoern A. Zeeb  * brcmf_fweh_process_event() - process skb as firmware event.
466*b4c3e9b5SBjoern A. Zeeb  *
467*b4c3e9b5SBjoern A. Zeeb  * @drvr: driver information object.
468*b4c3e9b5SBjoern A. Zeeb  * @event_packet: event packet to process.
469*b4c3e9b5SBjoern A. Zeeb  * @packet_len: length of the packet
470*b4c3e9b5SBjoern A. Zeeb  * @gfp: memory allocation flags.
471*b4c3e9b5SBjoern A. Zeeb  *
472*b4c3e9b5SBjoern A. Zeeb  * If the packet buffer contains a firmware event message it will
473*b4c3e9b5SBjoern A. Zeeb  * dispatch the event to a registered handler (using worker).
474*b4c3e9b5SBjoern A. Zeeb  */
brcmf_fweh_process_event(struct brcmf_pub * drvr,struct brcmf_event * event_packet,u32 packet_len,gfp_t gfp)475*b4c3e9b5SBjoern A. Zeeb void brcmf_fweh_process_event(struct brcmf_pub *drvr,
476*b4c3e9b5SBjoern A. Zeeb 			      struct brcmf_event *event_packet,
477*b4c3e9b5SBjoern A. Zeeb 			      u32 packet_len, gfp_t gfp)
478*b4c3e9b5SBjoern A. Zeeb {
479*b4c3e9b5SBjoern A. Zeeb 	u32 fwevt_idx;
480*b4c3e9b5SBjoern A. Zeeb 	struct brcmf_fweh_info *fweh = drvr->fweh;
481*b4c3e9b5SBjoern A. Zeeb 	struct brcmf_fweh_queue_item *event;
482*b4c3e9b5SBjoern A. Zeeb 	void *data;
483*b4c3e9b5SBjoern A. Zeeb 	u32 datalen;
484*b4c3e9b5SBjoern A. Zeeb 
485*b4c3e9b5SBjoern A. Zeeb 	/* get event info */
486*b4c3e9b5SBjoern A. Zeeb 	fwevt_idx = get_unaligned_be32(&event_packet->msg.event_type);
487*b4c3e9b5SBjoern A. Zeeb 	datalen = get_unaligned_be32(&event_packet->msg.datalen);
488*b4c3e9b5SBjoern A. Zeeb 	data = &event_packet[1];
489*b4c3e9b5SBjoern A. Zeeb 
490*b4c3e9b5SBjoern A. Zeeb 	if (fwevt_idx >= fweh->num_event_codes)
491*b4c3e9b5SBjoern A. Zeeb 		return;
492*b4c3e9b5SBjoern A. Zeeb 
493*b4c3e9b5SBjoern A. Zeeb 	if (fwevt_idx != BRCMF_E_IF && !fweh->evt_handler[fwevt_idx])
494*b4c3e9b5SBjoern A. Zeeb 		return;
495*b4c3e9b5SBjoern A. Zeeb 
496*b4c3e9b5SBjoern A. Zeeb 	if (datalen > BRCMF_DCMD_MAXLEN ||
497*b4c3e9b5SBjoern A. Zeeb 	    datalen + sizeof(*event_packet) > packet_len)
498*b4c3e9b5SBjoern A. Zeeb 		return;
499*b4c3e9b5SBjoern A. Zeeb 
500*b4c3e9b5SBjoern A. Zeeb 	event = kzalloc(struct_size(event, data, datalen), gfp);
501*b4c3e9b5SBjoern A. Zeeb 	if (!event)
502*b4c3e9b5SBjoern A. Zeeb 		return;
503*b4c3e9b5SBjoern A. Zeeb 
504*b4c3e9b5SBjoern A. Zeeb 	event->code = fwevt_idx;
505*b4c3e9b5SBjoern A. Zeeb 	event->datalen = datalen;
506*b4c3e9b5SBjoern A. Zeeb 	event->ifidx = event_packet->msg.ifidx;
507*b4c3e9b5SBjoern A. Zeeb 
508*b4c3e9b5SBjoern A. Zeeb 	/* use memcpy to get aligned event message */
509*b4c3e9b5SBjoern A. Zeeb 	memcpy(&event->emsg, &event_packet->msg, sizeof(event->emsg));
510*b4c3e9b5SBjoern A. Zeeb 	memcpy(event->data, data, datalen);
511*b4c3e9b5SBjoern A. Zeeb 	memcpy(event->ifaddr, event_packet->eth.h_dest, ETH_ALEN);
512*b4c3e9b5SBjoern A. Zeeb 
513*b4c3e9b5SBjoern A. Zeeb 	brcmf_fweh_queue_event(fweh, event);
514*b4c3e9b5SBjoern A. Zeeb }
515