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