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