1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
31
32
33
34
35
36
37
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
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
62static struct brcmf_fweh_event_name fweh_event_names[] = {
63 BRCMF_FWEH_EVENT_ENUM_DEFLIST
64};
65#undef BRCMF_ENUM_DEF
66
67
68
69
70
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
90
91
92
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
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
130
131
132
133
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
149
150
151
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
192 if (!armed)
193 brcmf_remove_interface(ifp, false);
194 }
195}
196
197
198
199
200
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
221
222
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
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
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
287
288
289
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
298
299
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
311
312
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
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
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
335
336
337
338
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
355
356
357
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
369
370
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
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
401
402
403
404
405
406
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
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
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