1 #ifdef WL_EXT_GENL
2 #include <bcmendian.h>
3 #include <wl_android.h>
4 #include <dhd_config.h>
5 #include <net/genetlink.h>
6
7 #define AGENL_ERROR(name, arg1, args...) \
8 do { \
9 if (android_msg_level & ANDROID_ERROR_LEVEL) { \
10 printk(KERN_ERR DHD_LOG_PREFIX "[%s] AGENL-ERROR) %s : " arg1, \
11 name, __func__, ##args); \
12 } \
13 } while (0)
14 #define AGENL_TRACE(name, arg1, args...) \
15 do { \
16 if (android_msg_level & ANDROID_TRACE_LEVEL) { \
17 printk(KERN_INFO DHD_LOG_PREFIX "[%s] AGENL-TRACE) %s : " arg1, \
18 name, __func__, ##args); \
19 } \
20 } while (0)
21 #define AGENL_INFO(name, arg1, args...) \
22 do { \
23 if (android_msg_level & ANDROID_INFO_LEVEL) { \
24 printk(KERN_INFO DHD_LOG_PREFIX "[%s] AGENL-INFO) %s : " arg1, \
25 name, __func__, ##args); \
26 } \
27 } while (0)
28
29 #define htod32(i) i
30 #define htod16(i) i
31 #define dtoh32(i) i
32 #define dtoh16(i) i
33
34 #ifdef SENDPROB
35 #define MGMT_PROBE_REQ 0x40
36 #define MGMT_PROBE_RES 0x50
37 #endif
38
39 enum {
40 __GENL_CUSTOM_ATTR_INVALID,
41 GENL_CUSTOM_ATTR_MSG, /* message */
42 __GENL_CUSTOM_ATTR_MAX,
43 };
44
45 enum {
46 __GENLL_CUSTOM_COMMAND_INVALID,
47 GENL_CUSTOM_COMMAND_BIND, /* bind */
48 GENL_CUSTOM_COMMAND_SEND, /* user -> kernel */
49 GENL_CUSTOM_COMMAND_RECV, /* kernel -> user */
50 __GENL_CUSTOM_COMMAND_MAX,
51 };
52
53 #if defined(ALIBABA_ZEROCONFIG)
54 #define GENL_FAMILY_NAME "WIFI_NL_CUSTOM"
55 #define PROBE_RSP_DST_MAC_OFFSET 4
56 #define PROBE_RSP_VNDR_ID_OFFSET 55
57 #else
58 #define GENL_FAMILY_NAME "WLAN_NL_CUSTOM"
59 #define PROBE_RSP_DST_MAC_OFFSET 4
60 #define PROBE_RSP_VNDR_ID_OFFSET DOT11_MGMT_HDR_LEN
61 #endif
62 #define PROBE_RSP_VNDR_LEN_OFFSET (PROBE_RSP_VNDR_ID_OFFSET + 1)
63 #define PROBE_RSP_VNDR_OUI_OFFSET (PROBE_RSP_VNDR_ID_OFFSET + 2)
64 #define MAX_CUSTOM_PKT_LENGTH 2048
65 #define GENL_CUSTOM_ATTR_MAX (__GENL_CUSTOM_ATTR_MAX - 1)
66 #define GENLMSG_UNICAST_RETRY_LIMIT 5
67
68 typedef struct genl_params {
69 struct net_device *dev;
70 bool bind;
71 int pm;
72 int bind_pid;
73 int send_retry_cnt;
74 } genl_params_t;
75
76 struct genl_params *g_zconf = NULL;
77
78 static int wl_ext_genl_bind(struct sk_buff *skb, struct genl_info *info);
79 static int wl_ext_genl_recv(struct sk_buff *skb, struct genl_info *info);
80 static int wl_ext_genl_send(struct genl_params *zconf, struct net_device *dev,
81 char *buf, int buf_len);
82
83 static struct nla_policy wl_ext_genl_policy[GENL_CUSTOM_ATTR_MAX + 1] = {
84 [GENL_CUSTOM_ATTR_MSG] = {.type = NLA_NUL_STRING},
85 };
86
87 static struct genl_ops wl_ext_genl_ops[] = {
88 {
89 .cmd = GENL_CUSTOM_COMMAND_BIND,
90 .flags = 0,
91 .policy = wl_ext_genl_policy,
92 .doit = wl_ext_genl_bind,
93 .dumpit = NULL,
94 },
95 {
96 .cmd = GENL_CUSTOM_COMMAND_SEND,
97 .flags = 0,
98 .policy = wl_ext_genl_policy,
99 .doit = wl_ext_genl_recv,
100 .dumpit = NULL,
101 },
102 };
103
104 static struct genl_family wl_ext_genl_family = {
105 .id = GENL_ID_GENERATE,
106 .hdrsize = 0,
107 .name = GENL_FAMILY_NAME,
108 .version = 1,
109 .maxattr = GENL_CUSTOM_ATTR_MAX,
110 };
111
112 #ifdef SENDPROB
wl_ext_add_del_ie_hex(struct net_device * dev,uint pktflag,char * ie_data,int ie_len,const char * add_del_cmd)113 static int wl_ext_add_del_ie_hex(struct net_device *dev, uint pktflag,
114 char *ie_data, int ie_len,
115 const char *add_del_cmd)
116 {
117 vndr_ie_setbuf_t *vndr_ie = NULL;
118 char iovar_buf[WLC_IOCTL_SMLEN] = "\0";
119 int tot_len = 0, iecount;
120 int err = -1;
121
122 if (!ie_len) {
123 AGENL_ERROR(dev->name, "wrong ie_len %d\n", ie_len);
124 goto exit;
125 }
126
127 tot_len = (int)(sizeof(vndr_ie_setbuf_t) + (ie_len));
128 vndr_ie = (vndr_ie_setbuf_t *)kzalloc(tot_len, GFP_KERNEL);
129 if (!vndr_ie) {
130 AGENL_ERROR(dev->name, "IE memory alloc failed\n");
131 err = -ENOMEM;
132 goto exit;
133 }
134
135 /* Copy the vndr_ie SET command ("add"/"del") to the buffer */
136 strncpy(vndr_ie->cmd, add_del_cmd, VNDR_IE_CMD_LEN - 1);
137 vndr_ie->cmd[VNDR_IE_CMD_LEN - 1] = '\0';
138
139 /* Set the IE count - the buffer contains only 1 IE */
140 iecount = htod32(1);
141 memcpy((void *)&vndr_ie->vndr_ie_buffer.iecount, &iecount, sizeof(s32));
142
143 /* Set packet flag to indicate that BEACON's will contain this IE */
144 pktflag = htod32(pktflag);
145 memcpy((void *)&vndr_ie->vndr_ie_buffer.vndr_ie_list[0].pktflag, &pktflag,
146 sizeof(u32));
147
148 /* Set the IE ID */
149 vndr_ie->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.id =
150 (uchar)DOT11_MNG_VS_ID;
151
152 /* Set the IE LEN */
153 vndr_ie->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.len = ie_len;
154
155 /* Set the IE OUI and DATA */
156 memcpy((char *)vndr_ie->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.oui,
157 ie_data, ie_len);
158
159 err = wldev_iovar_setbuf(dev, "vndr_ie", vndr_ie, tot_len, iovar_buf,
160 sizeof(iovar_buf), NULL);
161 if (err != 0) {
162 AGENL_ERROR(dev->name, "vndr_ie, ret=%d\n", err);
163 }
164
165 exit:
166 if (vndr_ie) {
167 kfree(vndr_ie);
168 }
169 return err;
170 }
171
wl_ext_send_probersp(struct net_device * dev,char * buf,int buf_len)172 static int wl_ext_send_probersp(struct net_device *dev, char *buf, int buf_len)
173 {
174 char addr[ETHER_ADDR_LEN], *pVndrOUI;
175 char iovar_buf[WLC_IOCTL_SMLEN] = "\0";
176 int err = -1, ie_len;
177
178 if (buf == NULL || buf_len <= 0) {
179 AGENL_ERROR(dev->name, "buf is NULL or buf_len <= 0\n");
180 return -1;
181 }
182
183 AGENL_TRACE(dev->name, "Enter\n");
184
185 memcpy(addr, (buf + PROBE_RSP_DST_MAC_OFFSET), ETHER_ADDR_LEN);
186 pVndrOUI = (buf + PROBE_RSP_VNDR_OUI_OFFSET);
187 ie_len = *(buf + PROBE_RSP_VNDR_LEN_OFFSET);
188
189 if (ie_len > (buf_len - PROBE_RSP_VNDR_OUI_OFFSET)) {
190 AGENL_ERROR(dev->name, "wrong vendor ie len %d\n", ie_len);
191 return -1;
192 }
193
194 err = wl_ext_add_del_ie_hex(dev, VNDR_IE_PRBRSP_FLAG, pVndrOUI, ie_len,
195 "add");
196 if (err) {
197 goto exit;
198 }
199
200 err = wldev_iovar_setbuf(dev, "send_probresp", addr, ETHER_ADDR_LEN,
201 iovar_buf, sizeof(iovar_buf), NULL);
202 if (err != 0) {
203 AGENL_ERROR(dev->name, "vndr_ie, ret=%d\n", err);
204 }
205
206 OSL_SLEEP(0x64);
207 wl_ext_add_del_ie_hex(dev, VNDR_IE_PRBRSP_FLAG, pVndrOUI, ie_len, "del");
208
209 exit:
210 return err;
211 }
212
wl_ext_set_probreq(struct net_device * dev,bool set)213 static int wl_ext_set_probreq(struct net_device *dev, bool set)
214 {
215 int bytes_written = 0;
216 char recv_probreq[32];
217
218 AGENL_TRACE(dev->name, "Enter\n");
219
220 if (set) {
221 sprintf(recv_probreq, "wl recv_probreq 1");
222 wl_android_ext_priv_cmd(dev, recv_probreq, 0, &bytes_written);
223 } else {
224 sprintf(recv_probreq, "wl recv_probreq 0");
225 wl_android_ext_priv_cmd(dev, recv_probreq, 0, &bytes_written);
226 }
227
228 return 0;
229 }
230
wl_ext_probreq_event(struct net_device * dev,void * argu,const wl_event_msg_t * e,void * data)231 void wl_ext_probreq_event(struct net_device *dev, void *argu,
232 const wl_event_msg_t *e, void *data)
233 {
234 struct genl_params *zconf = (struct genl_params *)argu;
235 int i, ret = 0, num_ie = 0, totlen;
236 uint32 event_len = 0;
237 char *buf, *pbuf;
238 uint rem_len, buflen = MAX_CUSTOM_PKT_LENGTH;
239 uint32 event_id[] = {DOT11_MNG_VS_ID};
240 uint32 datalen = ntoh32(e->datalen);
241 bcm_tlv_t *ie;
242
243 AGENL_TRACE(dev->name, "Enter\n");
244
245 rem_len = buflen;
246 buf = kzalloc(MAX_CUSTOM_PKT_LENGTH, GFP_KERNEL);
247 if (unlikely(!buf)) {
248 AGENL_ERROR(dev->name, "Could not allocate buf\n");
249 return;
250 }
251
252 // copy mgmt header
253 pbuf = buf;
254 memcpy(pbuf, data, DOT11_MGMT_HDR_LEN);
255 rem_len -= (DOT11_MGMT_HDR_LEN + 1);
256 datalen -= DOT11_MGMT_HDR_LEN;
257 data += DOT11_MGMT_HDR_LEN;
258
259 // copy IEs
260 pbuf = buf + DOT11_MGMT_HDR_LEN;
261 #if 1 // non-sort by id
262 ie = (bcm_tlv_t *)data;
263 totlen = datalen;
264 while (ie && totlen >= TLV_HDR_LEN) {
265 int ie_id = -1;
266 int ie_len = ie->len + TLV_HDR_LEN;
267 for (i = 0; i < sizeof(event_id) / sizeof(event_id[0]); i++) {
268 if (ie->id == event_id[i]) {
269 ie_id = ie->id;
270 break;
271 }
272 }
273 if ((ie->id == ie_id) && (totlen >= ie_len) && (rem_len >= ie_len)) {
274 memcpy(pbuf, ie, ie_len);
275 pbuf += ie_len;
276 rem_len -= ie_len;
277 num_ie++;
278 }
279 ie = (bcm_tlv_t *)((uint8 *)ie + ie_len);
280 totlen -= ie_len;
281 }
282 #else // sort by id
283 for (i = 0; i < sizeof(event_id) / sizeof(event_id[0]); i++) {
284 void *pdata = data;
285 int data_len = datalen;
286 while (rem_len > 0) {
287 ie = bcm_parse_tlvs(pdata, data_len, event_id[i]);
288 if (!ie) {
289 break;
290 }
291 if (rem_len < (ie->len + TLV_HDR_LEN)) {
292 ANDROID_TRACE(("%s: buffer is not enough\n", __FUNCTION__));
293 break;
294 }
295 memcpy(pbuf, ie, min(ie->len + TLV_HDR_LEN, rem_len));
296 pbuf += (ie->len + TLV_HDR_LEN);
297 rem_len -= (ie->len + TLV_HDR_LEN);
298 data_len -= (((void *)ie - pdata) + (ie->len + TLV_HDR_LEN));
299 pdata = (char *)ie + (ie->len + TLV_HDR_LEN);
300 num_ie++;
301 }
302 }
303 #endif
304 if (num_ie) {
305 event_len = buflen - rem_len;
306 AGENL_INFO(dev->name, "num_ie=%d\n", num_ie);
307 if (android_msg_level & ANDROID_INFO_LEVEL) {
308 prhex("buf", buf, event_len);
309 }
310 ret = wl_ext_genl_send(zconf, dev, buf, event_len);
311 }
312
313 if (buf) {
314 kfree(buf);
315 }
316 return;
317 }
318 #endif
319
wl_ext_genl_recv(struct sk_buff * skb,struct genl_info * info)320 static int wl_ext_genl_recv(struct sk_buff *skb, struct genl_info *info)
321 {
322 struct genl_params *zconf = g_zconf;
323 struct net_device *dev;
324 struct nlattr *na;
325 char *pData = NULL;
326 int DataLen = 0;
327
328 if (info == NULL) {
329 AGENL_ERROR(dev->name, "genl_info is NULL\n");
330 return -1;
331 }
332
333 if (zconf == NULL) {
334 AGENL_ERROR("wlan", "g_zconf is NULL\n");
335 return -1;
336 }
337 dev = zconf->dev;
338
339 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0)
340 AGENL_TRACE(dev->name, "Enter snd_portid=%d\n", info->snd_portid);
341 #else
342 AGENL_TRACE(dev->name, "Enter\n");
343 #endif
344 na = info->attrs[GENL_CUSTOM_ATTR_MSG];
345
346 if (na) {
347 pData = (char *)nla_data(na);
348 DataLen = nla_len(na);
349 AGENL_INFO(dev->name, "nla_len(na) : %d\n", DataLen);
350 if (android_msg_level & ANDROID_INFO_LEVEL) {
351 prhex("nla_data(na)", pData, DataLen);
352 }
353 }
354
355 #ifdef SENDPROB
356 if (*pData == MGMT_PROBE_RES) {
357 wl_ext_send_probersp(dev, pData, DataLen);
358 } else if (*pData == MGMT_PROBE_REQ) {
359 AGENL_ERROR(dev->name, "probe req\n");
360 } else {
361 AGENL_ERROR(dev->name, "Unexpected pkt %d\n", *pData);
362 if (android_msg_level & ANDROID_INFO_LEVEL) {
363 prhex("nla_data(na)", pData, DataLen);
364 }
365 }
366 #endif
367
368 return 0;
369 }
370
wl_ext_genl_send(struct genl_params * zconf,struct net_device * dev,char * buf,int buf_len)371 static int wl_ext_genl_send(struct genl_params *zconf, struct net_device *dev,
372 char *buf, int buf_len)
373 {
374 struct sk_buff *skb = NULL;
375 char *msg_head = NULL;
376 int ret = -1;
377 int bytes_written = 0;
378 char recv_probreq[32];
379
380 if (zconf->bind_pid == -1) {
381 AGENL_ERROR(dev->name, "There is no binded process\n");
382 return -1;
383 }
384
385 if (buf == NULL || buf_len <= 0) {
386 AGENL_ERROR(dev->name, "buf is NULL or buf_len : %d\n", buf_len);
387 return -1;
388 }
389
390 skb = genlmsg_new(MAX_CUSTOM_PKT_LENGTH, GFP_KERNEL);
391 if (skb) {
392 msg_head = genlmsg_put(skb, 0, 0, &wl_ext_genl_family, 0,
393 GENL_CUSTOM_COMMAND_RECV);
394 if (msg_head == NULL) {
395 nlmsg_free(skb);
396 AGENL_ERROR(dev->name, "genlmsg_put fail\n");
397 return -1;
398 }
399
400 ret = nla_put(skb, GENL_CUSTOM_ATTR_MSG, buf_len, buf);
401 if (ret != 0) {
402 nlmsg_free(skb);
403 AGENL_ERROR(dev->name, "nla_put fail : %d\n", ret);
404 return ret;
405 }
406
407 genlmsg_end(skb, msg_head);
408
409 /* sending message */
410 AGENL_TRACE(dev->name, "send to process %d\n", zconf->bind_pid);
411 ret = genlmsg_unicast(&init_net, skb, zconf->bind_pid);
412 if (ret != 0) {
413 AGENL_ERROR(dev->name, "genlmsg_unicast fail : %d\n", ret);
414 zconf->send_retry_cnt++;
415 if (zconf->send_retry_cnt >= GENLMSG_UNICAST_RETRY_LIMIT) {
416 AGENL_ERROR(dev->name,
417 "Exceeding retry cnt %d, Unbind pid : %d\n",
418 zconf->send_retry_cnt, zconf->bind_pid);
419 zconf->bind_pid = -1;
420 sprintf(recv_probreq, "wl recv_probreq 0");
421 wl_android_ext_priv_cmd(dev, recv_probreq, 0, &bytes_written);
422 }
423 return ret;
424 }
425 } else {
426 AGENL_ERROR(dev->name, "genlmsg_new fail\n");
427 return -1;
428 }
429
430 zconf->send_retry_cnt = 0;
431
432 return 0;
433 }
434
wl_ext_genl_bind(struct sk_buff * skb,struct genl_info * info)435 static int wl_ext_genl_bind(struct sk_buff *skb, struct genl_info *info)
436 {
437 struct genl_params *zconf = g_zconf;
438 struct net_device *dev;
439 struct dhd_pub *dhd;
440 struct nlattr *na;
441 bool bind;
442 char *pData = NULL;
443 int DataLen = 0;
444
445 if (info == NULL) {
446 AGENL_ERROR("wlan", "genl_info is NULL\n");
447 return -1;
448 }
449
450 if (zconf == NULL) {
451 AGENL_ERROR("wlan", "zconf is NULL\n");
452 return -1;
453 }
454 dev = zconf->dev;
455 dhd = dhd_get_pub(dev);
456
457 AGENL_TRACE(dev->name, "Enter\n");
458
459 na = info->attrs[GENL_CUSTOM_ATTR_MSG];
460 if (na) {
461 pData = (char *)nla_data(na);
462 DataLen = nla_len(na);
463 AGENL_INFO(dev->name, "nla_len(na) : %d\n", DataLen);
464 if (android_msg_level & ANDROID_INFO_LEVEL) {
465 prhex("nla_data(na)", pData, DataLen);
466 }
467 }
468
469 if (strcmp(pData, "BIND") == 0) {
470 bind = TRUE;
471 } else if (strcmp(pData, "UNBIND") == 0) {
472 bind = FALSE;
473 } else {
474 AGENL_ERROR(dev->name, "Unknown cmd %s\n", pData);
475 return -1;
476 }
477
478 if (bind == zconf->bind) {
479 AGENL_TRACE(dev->name, "Already %s\n", bind ? "BIND" : "UNBIND");
480 return 0;
481 }
482
483 if (bind) {
484 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0)
485 zconf->bind_pid = info->snd_portid;
486 #endif
487 AGENL_TRACE(dev->name, "BIND pid = %d\n", zconf->bind_pid);
488 #ifdef SENDPROB
489 wl_ext_set_probreq(dev, TRUE);
490 #endif
491 zconf->bind = TRUE;
492 zconf->pm = dhd->conf->pm;
493 dhd->conf->pm = PM_OFF;
494 } else {
495 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0)
496 AGENL_TRACE(dev->name, "UNBIND snd_portid = %d\n", info->snd_portid);
497 #else
498 AGENL_TRACE(dev->name, "UNBIND pid = %d\n", zconf->bind_pid);
499 #endif
500 zconf->bind_pid = -1;
501 #ifdef SENDPROB
502 wl_ext_set_probreq(dev, FALSE);
503 #endif
504 dhd->conf->pm = zconf->pm;
505 zconf->bind = FALSE;
506 }
507
508 return 0;
509 }
510
wl_ext_genl_init(struct net_device * net)511 int wl_ext_genl_init(struct net_device *net)
512 {
513 struct dhd_pub *dhd = dhd_get_pub(net);
514 struct genl_params *zconf = dhd->zconf;
515 int ret = 0;
516
517 AGENL_TRACE(net->name, "Enter falimy name: \"%s\"\n",
518 wl_ext_genl_family.name);
519
520 zconf = kzalloc(sizeof(struct genl_params), GFP_KERNEL);
521 if (unlikely(!zconf)) {
522 AGENL_ERROR(net->name, "Could not allocate zconf\n");
523 return -ENOMEM;
524 }
525 dhd->zconf = (void *)zconf;
526
527 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
528 ret = genl_register_family(&wl_ext_genl_family);
529 // fix me: how to attach wl_ext_genl_ops
530 ret = -1;
531 #elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)
532 ret = genl_register_family_with_ops(&wl_ext_genl_family, wl_ext_genl_ops);
533 #else
534 ret = genl_register_family_with_ops(&wl_ext_genl_family, wl_ext_genl_ops,
535 ARRAY_SIZE(wl_ext_genl_ops));
536 #endif
537 if (ret != 0) {
538 AGENL_ERROR(net->name, "GE_NELINK family registration fail\n");
539 goto err;
540 }
541 zconf->bind_pid = -1;
542 #ifdef SENDPROB
543 ret = wl_ext_event_register(net, dhd, WLC_E_PROBREQ_MSG,
544 wl_ext_probreq_event, zconf, PRIO_EVENT_IAPSTA);
545 if (ret) {
546 goto err;
547 }
548 #endif
549 zconf->dev = net;
550 g_zconf = zconf;
551
552 return ret;
553 err:
554 if (zconf) {
555 kfree(zconf);
556 }
557 return ret;
558 }
559
wl_ext_genl_deinit(struct net_device * net)560 void wl_ext_genl_deinit(struct net_device *net)
561 {
562 struct dhd_pub *dhd = dhd_get_pub(net);
563 struct genl_params *zconf = dhd->zconf;
564
565 AGENL_TRACE(net->name, "Enter\n");
566
567 #ifdef SENDPROB
568 wl_ext_event_deregister(net, dhd, WLC_E_PROBREQ_MSG, wl_ext_probreq_event);
569 #endif
570
571 genl_unregister_family(&wl_ext_genl_family);
572 if (zconf != NULL) {
573 kfree(dhd->zconf);
574 dhd->zconf = NULL;
575 }
576 g_zconf = NULL;
577 }
578 #endif
579