brintos

brintos / linux-shallow public Read only

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