File Index
Files and directories
Sign in
FILE

fweh.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c C
  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 */
 30struct 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 */
 43struct 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 */
 53static 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 */
 63const 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
 73const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code)
 74{
 75	return "nodebug";
 76}
 77#endif
 78BRCMF_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 */
 86static 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
 97static 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 */
127static 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
193static 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
213static 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 */
237static struct brcmf_fweh_queue_item *
238brcmf_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 */
259static 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		}
323event_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 */
334void 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 */
344int 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 */
372void 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 */
396int 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}
414BRCMF_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 */
422void 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 */
438int 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 */
480void 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(struct_size(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}
~/ Read-only file preview.