Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/dev/broadcom/brcm80211/brcmfmac/fweh.c
178665 views
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
*/
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
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
*/
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
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
*/
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
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
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 *
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
516