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_pub *drvr,
 106                                         struct brcmf_if *ifp,
 107                                         enum brcmf_fweh_event_code code,
 108                                         struct brcmf_event_msg *emsg,
 109                                         void *data)
 110{
 111        struct brcmf_fweh_info *fweh;
 112        int err = -EINVAL;
 113
 114        if (ifp) {
 115                fweh = &ifp->drvr->fweh;
 116
 117                /* handle the event if valid interface and handler */
 118                if (fweh->evt_handler[code])
 119                        err = fweh->evt_handler[code](ifp, emsg, data);
 120                else
 121                        bphy_err(drvr, "unhandled event %d ignored\n", code);
 122        } else {
 123                bphy_err(drvr, "no interface object\n");
 124        }
 125        return err;
 126}
 127
 128/**
 129 * brcmf_fweh_handle_if_event() - handle IF event.
 130 *
 131 * @drvr: driver information object.
 132 * @item: queue entry.
 133 * @ifpp: interface object (may change upon ADD action).
 134 */
 135static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr,
 136                                       struct brcmf_event_msg *emsg,
 137                                       void *data)
 138{
 139        struct brcmf_if_event *ifevent = data;
 140        struct brcmf_if *ifp;
 141        bool is_p2pdev;
 142        int err = 0;
 143
 144        brcmf_dbg(EVENT, "action: %u ifidx: %u bsscfgidx: %u flags: %u role: %u\n",
 145                  ifevent->action, ifevent->ifidx, ifevent->bsscfgidx,
 146                  ifevent->flags, ifevent->role);
 147
 148        /* The P2P Device interface event must not be ignored contrary to what
 149         * firmware tells us. Older firmware uses p2p noif, with sta role.
 150         * This should be accepted when p2pdev_setup is ongoing. TDLS setup will
 151         * use the same ifevent and should be ignored.
 152         */
 153        is_p2pdev = ((ifevent->flags & BRCMF_E_IF_FLAG_NOIF) &&
 154                     (ifevent->role == BRCMF_E_IF_ROLE_P2P_CLIENT ||
 155                      ((ifevent->role == BRCMF_E_IF_ROLE_STA) &&
 156                       (drvr->fweh.p2pdev_setup_ongoing))));
 157        if (!is_p2pdev && (ifevent->flags & BRCMF_E_IF_FLAG_NOIF)) {
 158                brcmf_dbg(EVENT, "event can be ignored\n");
 159                return;
 160        }
 161        if (ifevent->ifidx >= BRCMF_MAX_IFS) {
 162                bphy_err(drvr, "invalid interface index: %u\n", ifevent->ifidx);
 163                return;
 164        }
 165
 166        ifp = drvr->iflist[ifevent->bsscfgidx];
 167
 168        if (ifevent->action == BRCMF_E_IF_ADD) {
 169                brcmf_dbg(EVENT, "adding %s (%pM)\n", emsg->ifname,
 170                          emsg->addr);
 171                ifp = brcmf_add_if(drvr, ifevent->bsscfgidx, ifevent->ifidx,
 172                                   is_p2pdev, emsg->ifname, emsg->addr);
 173                if (IS_ERR(ifp))
 174                        return;
 175                if (!is_p2pdev)
 176                        brcmf_proto_add_if(drvr, ifp);
 177                if (!drvr->fweh.evt_handler[BRCMF_E_IF])
 178                        if (brcmf_net_attach(ifp, false) < 0)
 179                                return;
 180        }
 181
 182        if (ifp && ifevent->action == BRCMF_E_IF_CHANGE)
 183                brcmf_proto_reset_if(drvr, ifp);
 184
 185        err = brcmf_fweh_call_event_handler(drvr, ifp, emsg->event_code, emsg,
 186                                            data);
 187
 188        if (ifp && ifevent->action == BRCMF_E_IF_DEL) {
 189                bool armed = brcmf_cfg80211_vif_event_armed(drvr->config);
 190
 191                /* Default handling in case no-one waits for this event */
 192                if (!armed)
 193                        brcmf_remove_interface(ifp, false);
 194        }
 195}
 196
 197/**
 198 * brcmf_fweh_dequeue_event() - get event from the queue.
 199 *
 200 * @fweh: firmware event handling info.
 201 */
 202static struct brcmf_fweh_queue_item *
 203brcmf_fweh_dequeue_event(struct brcmf_fweh_info *fweh)
 204{
 205        struct brcmf_fweh_queue_item *event = NULL;
 206        ulong flags;
 207
 208        spin_lock_irqsave(&fweh->evt_q_lock, flags);
 209        if (!list_empty(&fweh->event_q)) {
 210                event = list_first_entry(&fweh->event_q,
 211                                         struct brcmf_fweh_queue_item, q);
 212                list_del(&event->q);
 213        }
 214        spin_unlock_irqrestore(&fweh->evt_q_lock, flags);
 215
 216        return event;
 217}
 218
 219/**
 220 * brcmf_fweh_event_worker() - firmware event worker.
 221 *
 222 * @work: worker object.
 223 */
 224static void brcmf_fweh_event_worker(struct work_struct *work)
 225{
 226        struct brcmf_pub *drvr;
 227        struct brcmf_if *ifp;
 228        struct brcmf_fweh_info *fweh;
 229        struct brcmf_fweh_queue_item *event;
 230        int err = 0;
 231        struct brcmf_event_msg_be *emsg_be;
 232        struct brcmf_event_msg emsg;
 233
 234        fweh = container_of(work, struct brcmf_fweh_info, event_work);
 235        drvr = container_of(fweh, struct brcmf_pub, fweh);
 236
 237        while ((event = brcmf_fweh_dequeue_event(fweh))) {
 238                brcmf_dbg(EVENT, "event %s (%u) ifidx %u bsscfg %u addr %pM\n",
 239                          brcmf_fweh_event_name(event->code), event->code,
 240                          event->emsg.ifidx, event->emsg.bsscfgidx,
 241                          event->emsg.addr);
 242
 243                /* convert event message */
 244                emsg_be = &event->emsg;
 245                emsg.version = be16_to_cpu(emsg_be->version);
 246                emsg.flags = be16_to_cpu(emsg_be->flags);
 247                emsg.event_code = event->code;
 248                emsg.status = be32_to_cpu(emsg_be->status);
 249                emsg.reason = be32_to_cpu(emsg_be->reason);
 250                emsg.auth_type = be32_to_cpu(emsg_be->auth_type);
 251                emsg.datalen = be32_to_cpu(emsg_be->datalen);
 252                memcpy(emsg.addr, emsg_be->addr, ETH_ALEN);
 253                memcpy(emsg.ifname, emsg_be->ifname, sizeof(emsg.ifname));
 254                emsg.ifidx = emsg_be->ifidx;
 255                emsg.bsscfgidx = emsg_be->bsscfgidx;
 256
 257                brcmf_dbg(EVENT, "  version %u flags %u status %u reason %u\n",
 258                          emsg.version, emsg.flags, emsg.status, emsg.reason);
 259                brcmf_dbg_hex_dump(BRCMF_EVENT_ON(), event->data,
 260                                   min_t(u32, emsg.datalen, 64),
 261                                   "event payload, len=%d\n", emsg.datalen);
 262
 263                /* special handling of interface event */
 264                if (event->code == BRCMF_E_IF) {
 265                        brcmf_fweh_handle_if_event(drvr, &emsg, event->data);
 266                        goto event_free;
 267                }
 268
 269                if (event->code == BRCMF_E_TDLS_PEER_EVENT)
 270                        ifp = drvr->iflist[0];
 271                else
 272                        ifp = drvr->iflist[emsg.bsscfgidx];
 273                err = brcmf_fweh_call_event_handler(drvr, ifp, event->code,
 274                                                    &emsg, event->data);
 275                if (err) {
 276                        bphy_err(drvr, "event handler failed (%d)\n",
 277                                 event->code);
 278                        err = 0;
 279                }
 280event_free:
 281                kfree(event);
 282        }
 283}
 284
 285/**
 286 * brcmf_fweh_p2pdev_setup() - P2P device setup ongoing (or not).
 287 *
 288 * @ifp: ifp on which setup is taking place or finished.
 289 * @ongoing: p2p device setup in progress (or not).
 290 */
 291void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing)
 292{
 293        ifp->drvr->fweh.p2pdev_setup_ongoing = ongoing;
 294}
 295
 296/**
 297 * brcmf_fweh_attach() - initialize firmware event handling.
 298 *
 299 * @drvr: driver information object.
 300 */
 301void brcmf_fweh_attach(struct brcmf_pub *drvr)
 302{
 303        struct brcmf_fweh_info *fweh = &drvr->fweh;
 304        INIT_WORK(&fweh->event_work, brcmf_fweh_event_worker);
 305        spin_lock_init(&fweh->evt_q_lock);
 306        INIT_LIST_HEAD(&fweh->event_q);
 307}
 308
 309/**
 310 * brcmf_fweh_detach() - cleanup firmware event handling.
 311 *
 312 * @drvr: driver information object.
 313 */
 314void brcmf_fweh_detach(struct brcmf_pub *drvr)
 315{
 316        struct brcmf_fweh_info *fweh = &drvr->fweh;
 317        struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
 318        s8 eventmask[BRCMF_EVENTING_MASK_LEN];
 319
 320        if (ifp) {
 321                /* clear all events */
 322                memset(eventmask, 0, BRCMF_EVENTING_MASK_LEN);
 323                (void)brcmf_fil_iovar_data_set(ifp, "event_msgs",
 324                                               eventmask,
 325                                               BRCMF_EVENTING_MASK_LEN);
 326        }
 327        /* cancel the worker */
 328        cancel_work_sync(&fweh->event_work);
 329        WARN_ON(!list_empty(&fweh->event_q));
 330        memset(fweh->evt_handler, 0, sizeof(fweh->evt_handler));
 331}
 332
 333/**
 334 * brcmf_fweh_register() - register handler for given event code.
 335 *
 336 * @drvr: driver information object.
 337 * @code: event code.
 338 * @handler: handler for the given event code.
 339 */
 340int brcmf_fweh_register(struct brcmf_pub *drvr, enum brcmf_fweh_event_code code,
 341                        brcmf_fweh_handler_t handler)
 342{
 343        if (drvr->fweh.evt_handler[code]) {
 344                bphy_err(drvr, "event code %d already registered\n", code);
 345                return -ENOSPC;
 346        }
 347        drvr->fweh.evt_handler[code] = handler;
 348        brcmf_dbg(TRACE, "event handler registered for %s\n",
 349                  brcmf_fweh_event_name(code));
 350        return 0;
 351}
 352
 353/**
 354 * brcmf_fweh_unregister() - remove handler for given code.
 355 *
 356 * @drvr: driver information object.
 357 * @code: event code.
 358 */
 359void brcmf_fweh_unregister(struct brcmf_pub *drvr,
 360                           enum brcmf_fweh_event_code code)
 361{
 362        brcmf_dbg(TRACE, "event handler cleared for %s\n",
 363                  brcmf_fweh_event_name(code));
 364        drvr->fweh.evt_handler[code] = NULL;
 365}
 366
 367/**
 368 * brcmf_fweh_activate_events() - enables firmware events registered.
 369 *
 370 * @ifp: primary interface object.
 371 */
 372int brcmf_fweh_activate_events(struct brcmf_if *ifp)
 373{
 374        struct brcmf_pub *drvr = ifp->drvr;
 375        int i, err;
 376        s8 eventmask[BRCMF_EVENTING_MASK_LEN];
 377
 378        memset(eventmask, 0, sizeof(eventmask));
 379        for (i = 0; i < BRCMF_E_LAST; i++) {
 380                if (ifp->drvr->fweh.evt_handler[i]) {
 381                        brcmf_dbg(EVENT, "enable event %s\n",
 382                                  brcmf_fweh_event_name(i));
 383                        setbit(eventmask, i);
 384                }
 385        }
 386
 387        /* want to handle IF event as well */
 388        brcmf_dbg(EVENT, "enable event IF\n");
 389        setbit(eventmask, BRCMF_E_IF);
 390
 391        err = brcmf_fil_iovar_data_set(ifp, "event_msgs",
 392                                       eventmask, BRCMF_EVENTING_MASK_LEN);
 393        if (err)
 394                bphy_err(drvr, "Set event_msgs error (%d)\n", err);
 395
 396        return err;
 397}
 398
 399/**
 400 * brcmf_fweh_process_event() - process skb as firmware event.
 401 *
 402 * @drvr: driver information object.
 403 * @event_packet: event packet to process.
 404 *
 405 * If the packet buffer contains a firmware event message it will
 406 * dispatch the event to a registered handler (using worker).
 407 */
 408void brcmf_fweh_process_event(struct brcmf_pub *drvr,
 409                              struct brcmf_event *event_packet,
 410                              u32 packet_len)
 411{
 412        enum brcmf_fweh_event_code code;
 413        struct brcmf_fweh_info *fweh = &drvr->fweh;
 414        struct brcmf_fweh_queue_item *event;
 415        gfp_t alloc_flag = GFP_KERNEL;
 416        void *data;
 417        u32 datalen;
 418
 419        /* get event info */
 420        code = get_unaligned_be32(&event_packet->msg.event_type);
 421        datalen = get_unaligned_be32(&event_packet->msg.datalen);
 422        data = &event_packet[1];
 423
 424        if (code >= BRCMF_E_LAST)
 425                return;
 426
 427        if (code != BRCMF_E_IF && !fweh->evt_handler[code])
 428                return;
 429
 430        if (datalen > BRCMF_DCMD_MAXLEN ||
 431            datalen + sizeof(*event_packet) > packet_len)
 432                return;
 433
 434        if (in_interrupt())
 435                alloc_flag = GFP_ATOMIC;
 436
 437        event = kzalloc(sizeof(*event) + datalen, alloc_flag);
 438        if (!event)
 439                return;
 440
 441        event->code = code;
 442        event->ifidx = event_packet->msg.ifidx;
 443
 444        /* use memcpy to get aligned event message */
 445        memcpy(&event->emsg, &event_packet->msg, sizeof(event->emsg));
 446        memcpy(event->data, data, datalen);
 447        event->datalen = datalen;
 448        memcpy(event->ifaddr, event_packet->eth.h_dest, ETH_ALEN);
 449
 450        brcmf_fweh_queue_event(fweh, event);
 451}
 452