Path: blob/main/sys/contrib/dev/broadcom/brcm80211/brcmfmac/fweh.c
178665 views
// SPDX-License-Identifier: ISC1/*2* Copyright (c) 2012 Broadcom Corporation3*/4#include <linux/netdevice.h>56#include "brcmu_wifi.h"7#include "brcmu_utils.h"89#include "cfg80211.h"10#include "core.h"11#include "debug.h"12#include "tracepoint.h"13#include "fweh.h"14#include "fwil.h"15#include "proto.h"16#include "bus.h"17#include "fwvid.h"18/**19* struct brcmf_fweh_queue_item - event item on event queue.20*21* @q: list element for queuing.22* @code: event code.23* @ifidx: interface index related to this event.24* @ifaddr: ethernet address for interface.25* @emsg: common parameters of the firmware event message.26* @datalen: length of the data array27* @data: event specific data part of the firmware event.28*/29struct brcmf_fweh_queue_item {30struct list_head q;31u32 code;32u8 ifidx;33u8 ifaddr[ETH_ALEN];34struct brcmf_event_msg_be emsg;35u32 datalen;36u8 data[] __counted_by(datalen);37};3839/*40* struct brcmf_fweh_event_name - code, name mapping entry.41*/42struct brcmf_fweh_event_name {43enum brcmf_fweh_event_code code;44const char *name;45};4647#ifdef DEBUG48#define BRCMF_ENUM_DEF(id, val) \49{ val, #id },5051/* array for mapping code to event name */52static struct brcmf_fweh_event_name fweh_event_names[] = {53BRCMF_FWEH_EVENT_ENUM_DEFLIST54};55#undef BRCMF_ENUM_DEF5657/**58* brcmf_fweh_event_name() - returns name for given event code.59*60* @code: code to lookup.61*/62const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code)63{64int i;65for (i = 0; i < ARRAY_SIZE(fweh_event_names); i++) {66if (fweh_event_names[i].code == code)67return fweh_event_names[i].name;68}69return "unknown";70}71#else72const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code)73{74return "nodebug";75}76#endif77BRCMF_EXPORT_SYMBOL_GPL(brcmf_fweh_event_name);7879/**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,86struct brcmf_fweh_queue_item *event)87{88ulong flags;8990spin_lock_irqsave(&fweh->evt_q_lock, flags);91list_add_tail(&event->q, &fweh->event_q);92spin_unlock_irqrestore(&fweh->evt_q_lock, flags);93schedule_work(&fweh->event_work);94}9596static int brcmf_fweh_call_event_handler(struct brcmf_pub *drvr,97struct brcmf_if *ifp,98u32 fwcode,99struct brcmf_event_msg *emsg,100void *data)101{102struct brcmf_fweh_info *fweh;103int err = -EINVAL;104105if (ifp) {106fweh = ifp->drvr->fweh;107108/* handle the event if valid interface and handler */109if (fweh->evt_handler[fwcode])110err = fweh->evt_handler[fwcode](ifp, emsg, data);111else112bphy_err(drvr, "unhandled fwevt %d ignored\n", fwcode);113} else {114bphy_err(drvr, "no interface object\n");115}116return err;117}118119/**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,127struct brcmf_event_msg *emsg,128void *data)129{130struct brcmf_if_event *ifevent = data;131struct brcmf_if *ifp;132bool is_p2pdev;133134brcmf_dbg(EVENT, "action: %u ifidx: %u bsscfgidx: %u flags: %u role: %u\n",135ifevent->action, ifevent->ifidx, ifevent->bsscfgidx,136ifevent->flags, ifevent->role);137138/* 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*/143is_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))));147if (!is_p2pdev && (ifevent->flags & BRCMF_E_IF_FLAG_NOIF)) {148brcmf_dbg(EVENT, "event can be ignored\n");149return;150}151if (ifevent->ifidx >= BRCMF_MAX_IFS) {152bphy_err(drvr, "invalid interface index: %u\n", ifevent->ifidx);153return;154}155156ifp = drvr->iflist[ifevent->bsscfgidx];157158if (ifevent->action == BRCMF_E_IF_ADD) {159brcmf_dbg(EVENT, "adding %s (%pM)\n", emsg->ifname,160emsg->addr);161ifp = brcmf_add_if(drvr, ifevent->bsscfgidx, ifevent->ifidx,162is_p2pdev, emsg->ifname, emsg->addr);163if (IS_ERR(ifp))164return;165if (!is_p2pdev)166brcmf_proto_add_if(drvr, ifp);167if (!drvr->fweh->evt_handler[BRCMF_E_IF])168if (brcmf_net_attach(ifp, false) < 0)169return;170}171172if (ifp && ifevent->action == BRCMF_E_IF_CHANGE)173brcmf_proto_reset_if(drvr, ifp);174175brcmf_fweh_call_event_handler(drvr, ifp, emsg->event_code, emsg,176data);177178if (ifp && ifevent->action == BRCMF_E_IF_DEL) {179bool armed = brcmf_cfg80211_vif_event_armed(drvr->config);180181/* Default handling in case no-one waits for this event */182if (!armed)183brcmf_remove_interface(ifp, false);184}185}186187static void brcmf_fweh_map_event_code(struct brcmf_fweh_info *fweh,188enum brcmf_fweh_event_code code,189u32 *fw_code)190{191int i;192193if (WARN_ON(!fw_code))194return;195196*fw_code = code;197if (fweh->event_map) {198for (i = 0; i < fweh->event_map->n_items; i++) {199if (fweh->event_map->items[i].code == code) {200*fw_code = fweh->event_map->items[i].fwevt_code;201break;202}203}204}205}206207static void brcmf_fweh_map_fwevt_code(struct brcmf_fweh_info *fweh, u32 fw_code,208enum brcmf_fweh_event_code *code)209{210int i;211212if (WARN_ON(!code))213return;214215*code = fw_code;216if (fweh->event_map) {217for (i = 0; i < fweh->event_map->n_items; i++) {218if (fweh->event_map->items[i].fwevt_code == fw_code) {219*code = fweh->event_map->items[i].code;220break;221}222}223}224}225226/**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{234struct brcmf_fweh_queue_item *event = NULL;235ulong flags;236237spin_lock_irqsave(&fweh->evt_q_lock, flags);238if (!list_empty(&fweh->event_q)) {239event = list_first_entry(&fweh->event_q,240struct brcmf_fweh_queue_item, q);241list_del(&event->q);242}243spin_unlock_irqrestore(&fweh->evt_q_lock, flags);244245return event;246}247248/**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{255struct brcmf_pub *drvr;256struct brcmf_if *ifp;257struct brcmf_fweh_info *fweh;258struct brcmf_fweh_queue_item *event;259int err = 0;260struct brcmf_event_msg_be *emsg_be;261struct brcmf_event_msg emsg;262263fweh = container_of(work, struct brcmf_fweh_info, event_work);264drvr = fweh->drvr;265266while ((event = brcmf_fweh_dequeue_event(fweh))) {267enum brcmf_fweh_event_code code;268269brcmf_fweh_map_fwevt_code(fweh, event->code, &code);270brcmf_dbg(EVENT, "event %s (%u:%u) ifidx %u bsscfg %u addr %pM\n",271brcmf_fweh_event_name(code), code, event->code,272event->emsg.ifidx, event->emsg.bsscfgidx,273event->emsg.addr);274if (event->emsg.bsscfgidx >= BRCMF_MAX_IFS) {275bphy_err(drvr, "invalid bsscfg index: %u\n",276event->emsg.bsscfgidx);277goto event_free;278}279280/* convert event message */281emsg_be = &event->emsg;282emsg.version = be16_to_cpu(emsg_be->version);283emsg.flags = be16_to_cpu(emsg_be->flags);284emsg.event_code = code;285emsg.status = be32_to_cpu(emsg_be->status);286emsg.reason = be32_to_cpu(emsg_be->reason);287emsg.auth_type = be32_to_cpu(emsg_be->auth_type);288emsg.datalen = be32_to_cpu(emsg_be->datalen);289memcpy(emsg.addr, emsg_be->addr, ETH_ALEN);290memcpy(emsg.ifname, emsg_be->ifname, sizeof(emsg.ifname));291emsg.ifidx = emsg_be->ifidx;292emsg.bsscfgidx = emsg_be->bsscfgidx;293294brcmf_dbg(EVENT, " version %u flags %u status %u reason %u\n",295emsg.version, emsg.flags, emsg.status, emsg.reason);296brcmf_dbg_hex_dump(BRCMF_EVENT_ON(), event->data,297min_t(u32, emsg.datalen, 64),298"event payload, len=%d\n", emsg.datalen);299300/* special handling of interface event */301if (event->code == BRCMF_E_IF) {302brcmf_fweh_handle_if_event(drvr, &emsg, event->data);303goto event_free;304}305306if (event->code == BRCMF_E_TDLS_PEER_EVENT)307ifp = drvr->iflist[0];308else309ifp = drvr->iflist[emsg.bsscfgidx];310err = brcmf_fweh_call_event_handler(drvr, ifp, event->code,311&emsg, event->data);312if (err) {313bphy_err(drvr, "event handler failed (%d)\n",314event->code);315err = 0;316}317event_free:318kfree(event);319}320}321322/**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{330ifp->drvr->fweh->p2pdev_setup_ongoing = ongoing;331}332333/**334* brcmf_fweh_attach() - initialize firmware event handling.335*336* @drvr: driver information object.337*/338int brcmf_fweh_attach(struct brcmf_pub *drvr)339{340struct brcmf_fweh_info *fweh;341int err;342343err = brcmf_fwvid_alloc_fweh_info(drvr);344if (err < 0)345return err;346347fweh = drvr->fweh;348fweh->drvr = drvr;349350fweh->event_mask_len = DIV_ROUND_UP(fweh->num_event_codes, 8);351fweh->event_mask = kzalloc(fweh->event_mask_len, GFP_KERNEL);352if (!fweh->event_mask)353return -ENOMEM;354355INIT_WORK(&fweh->event_work, brcmf_fweh_event_worker);356spin_lock_init(&fweh->evt_q_lock);357INIT_LIST_HEAD(&fweh->event_q);358return 0;359}360361/**362* brcmf_fweh_detach() - cleanup firmware event handling.363*364* @drvr: driver information object.365*/366void brcmf_fweh_detach(struct brcmf_pub *drvr)367{368struct brcmf_fweh_info *fweh = drvr->fweh;369370if (!fweh)371return;372373/* cancel the worker if initialized */374if (fweh->event_work.func) {375cancel_work_sync(&fweh->event_work);376WARN_ON(!list_empty(&fweh->event_q));377}378drvr->fweh = NULL;379kfree(fweh->event_mask);380kfree(fweh);381}382383/**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,391brcmf_fweh_handler_t handler)392{393struct brcmf_fweh_info *fweh = drvr->fweh;394u32 evt_handler_idx;395396brcmf_fweh_map_event_code(fweh, code, &evt_handler_idx);397398if (fweh->evt_handler[evt_handler_idx]) {399bphy_err(drvr, "event code %d already registered\n", code);400return -ENOSPC;401}402403fweh->evt_handler[evt_handler_idx] = handler;404brcmf_dbg(TRACE, "event handler registered for %s\n",405brcmf_fweh_event_name(code));406return 0;407}408BRCMF_EXPORT_SYMBOL_GPL(brcmf_fweh_register);409410/**411* brcmf_fweh_unregister() - remove handler for given code.412*413* @drvr: driver information object.414* @code: event code.415*/416void brcmf_fweh_unregister(struct brcmf_pub *drvr,417enum brcmf_fweh_event_code code)418{419u32 evt_handler_idx;420421brcmf_dbg(TRACE, "event handler cleared for %s\n",422brcmf_fweh_event_name(code));423brcmf_fweh_map_event_code(drvr->fweh, code, &evt_handler_idx);424drvr->fweh->evt_handler[evt_handler_idx] = NULL;425}426427/**428* brcmf_fweh_activate_events() - enables firmware events registered.429*430* @ifp: primary interface object.431*/432int brcmf_fweh_activate_events(struct brcmf_if *ifp)433{434struct brcmf_fweh_info *fweh = ifp->drvr->fweh;435enum brcmf_fweh_event_code code;436int i, err;437438memset(fweh->event_mask, 0, fweh->event_mask_len);439for (i = 0; i < fweh->num_event_codes; i++) {440if (fweh->evt_handler[i]) {441brcmf_fweh_map_fwevt_code(fweh, i, &code);442brcmf_dbg(EVENT, "enable event %s\n",443brcmf_fweh_event_name(code));444setbit(fweh->event_mask, i);445}446}447448/* want to handle IF event as well */449brcmf_dbg(EVENT, "enable event IF\n");450setbit(fweh->event_mask, BRCMF_E_IF);451452/* allow per-vendor method to activate firmware events */453if (!brcmf_fwvid_activate_events(ifp))454return 0;455456err = brcmf_fil_iovar_data_set(ifp, "event_msgs", fweh->event_mask,457fweh->event_mask_len);458if (err)459bphy_err(fweh->drvr, "Set event_msgs error (%d)\n", err);460return err;461}462463/**464* brcmf_fweh_process_event() - process skb as firmware event.465*466* @drvr: driver information object.467* @event_packet: event packet to process.468* @packet_len: length of the packet469* @gfp: memory allocation flags.470*471* If the packet buffer contains a firmware event message it will472* dispatch the event to a registered handler (using worker).473*/474void brcmf_fweh_process_event(struct brcmf_pub *drvr,475struct brcmf_event *event_packet,476u32 packet_len, gfp_t gfp)477{478u32 fwevt_idx;479struct brcmf_fweh_info *fweh = drvr->fweh;480struct brcmf_fweh_queue_item *event;481void *data;482u32 datalen;483484/* get event info */485fwevt_idx = get_unaligned_be32(&event_packet->msg.event_type);486datalen = get_unaligned_be32(&event_packet->msg.datalen);487data = &event_packet[1];488489if (fwevt_idx >= fweh->num_event_codes)490return;491492if (fwevt_idx != BRCMF_E_IF && !fweh->evt_handler[fwevt_idx])493return;494495if (datalen > BRCMF_DCMD_MAXLEN ||496datalen + sizeof(*event_packet) > packet_len)497return;498499event = kzalloc(struct_size(event, data, datalen), gfp);500if (!event)501return;502503event->code = fwevt_idx;504event->datalen = datalen;505event->ifidx = event_packet->msg.ifidx;506507/* use memcpy to get aligned event message */508memcpy(&event->emsg, &event_packet->msg, sizeof(event->emsg));509memcpy(event->data, data, datalen);510memcpy(event->ifaddr, event_packet->eth.h_dest, ETH_ALEN);511512brcmf_fweh_queue_event(fweh, event);513}514515516