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