root/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. brcmf_fweh_event_name
  2. brcmf_fweh_event_name
  3. brcmf_fweh_queue_event
  4. brcmf_fweh_call_event_handler
  5. brcmf_fweh_handle_if_event
  6. brcmf_fweh_dequeue_event
  7. brcmf_fweh_event_worker
  8. brcmf_fweh_p2pdev_setup
  9. brcmf_fweh_attach
  10. brcmf_fweh_detach
  11. brcmf_fweh_register
  12. brcmf_fweh_unregister
  13. brcmf_fweh_activate_events
  14. brcmf_fweh_process_event

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

/* [<][>][^][v][top][bottom][index][help] */