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