nexmon – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /*
2 * Linux port of dhd command line utility, hacked from wl utility.
3 *
4 * Copyright (C) 1999-2013, Broadcom Corporation
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 * $Id: dhdu_linux.c 378962 2013-01-15 13:18:28Z $
19 */
20  
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <ctype.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <sys/types.h>
28 #include <sys/wait.h>
29 #include <sys/socket.h>
30 #include <proto/ethernet.h>
31 #include <proto/bcmip.h>
32 #include <arpa/inet.h>
33 #include <sys/ioctl.h>
34 #include <net/if.h>
35 #include <fcntl.h>
36 #include <sys/ioctl.h>
37 #include <unistd.h>
38  
39 #ifndef TARGETENV_android
40 #include <error.h>
41 typedef u_int64_t u64;
42 typedef u_int32_t u32;
43 typedef u_int16_t u16;
44 typedef u_int8_t u8;
45 #endif /* TARGETENV_android */
46 #include <linux/sockios.h>
47 #include <linux/types.h>
48 #include <linux/ethtool.h>
49  
50 #include <typedefs.h>
51 #include <signal.h>
52 #include <dhdioctl.h>
53 #include <wlioctl.h>
54 #include <bcmcdc.h>
55 #include <bcmutils.h>
56  
57 #if defined(RWL_WIFI) || defined(RWL_SOCKET) ||defined(RWL_SERIAL)
58 #define RWL_ENABLE
59 #endif
60  
61 #include "dhdu.h"
62 #ifdef RWL_ENABLE
63 #include "wlu_remote.h"
64 #include "wlu_client_shared.h"
65 #include "wlu_pipe.h"
66 #endif /* RWL_ENABLE */
67 #include <netdb.h>
68 #include <netinet/in.h>
69 #include <dhdioctl.h>
70 #include "dhdu_common.h"
71 #include "dhdu_nl80211.h"
72  
73 char *av0;
74 static int rwl_os_type = LINUX_OS;
75 /* Search the dhd_cmds table for a matching command name.
76 * Return the matching command or NULL if no match found.
77 */
78 static cmd_t *
79 dhd_find_cmd(char* name)
80 {
81 cmd_t *cmd = NULL;
82 /* search the dhd_cmds for a matching name */
83 for (cmd = dhd_cmds; cmd->name && strcmp(cmd->name, name); cmd++);
84 if (cmd->name == NULL)
85 cmd = NULL;
86 return cmd;
87 }
88  
89 static void
90 syserr(const char *s)
91 {
92 fprintf(stderr, "%s: ", av0);
93 perror(s);
94 exit(errno);
95 }
96  
97 #ifdef NL80211
98 static int __dhd_driver_io(void *dhd, dhd_ioctl_t *ioc)
99 {
100 struct dhd_netlink_info dhd_nli;
101 struct ifreq *ifr = (struct ifreq *)dhd;
102 int ret = 0;
103  
104 dhd_nli.ifidx = if_nametoindex(ifr->ifr_name);
105 if (!dhd_nli.ifidx) {
106 fprintf(stderr, "invalid device %s\n", ifr->ifr_name);
107 return BCME_IOCTL_ERROR;
108 }
109  
110 if (dhd_nl_sock_connect(&dhd_nli) < 0)
111 syserr("socket");
112  
113 ret = dhd_nl_do_testmode(&dhd_nli, ioc);
114 dhd_nl_sock_disconnect(&dhd_nli);
115 return ret;
116 }
117 #else
118 static int __dhd_driver_io(void *dhd, dhd_ioctl_t *ioc)
119 {
120 struct ifreq *ifr = (struct ifreq *)dhd;
121 int s;
122 int ret = 0;
123  
124 /* pass ioctl data */
125 ifr->ifr_data = (caddr_t)ioc;
126  
127 /* open socket to kernel */
128 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
129 syserr("socket");
130  
131 ret = ioctl(s, SIOCDEVPRIVATE, ifr);
132 if (ret < 0 && errno != EAGAIN)
133 syserr(__FUNCTION__);
134  
135 /* cleanup */
136 close(s);
137 return ret;
138 }
139 #endif /* NL80211 */
140  
141 /* This function is called by ioctl_setinformation_fe or ioctl_queryinformation_fe
142 * for executing remote commands or local commands
143 */
144 static int
145 dhd_ioctl(void *dhd, int cmd, void *buf, int len, bool set)
146 {
147 dhd_ioctl_t ioc;
148 int ret = 0;
149  
150 /* By default try to execute wl commands */
151 int driver_magic = WLC_IOCTL_MAGIC;
152 int get_magic = WLC_GET_MAGIC;
153  
154 /* For local dhd commands execute dhd. For wifi transport we still
155 * execute wl commands.
156 */
157 if (remote_type == NO_REMOTE && strncmp (buf, RWL_WIFI_ACTION_CMD,
158 strlen(RWL_WIFI_ACTION_CMD)) && strncmp(buf, RWL_WIFI_GET_ACTION_CMD,
159 strlen(RWL_WIFI_GET_ACTION_CMD))) {
160 driver_magic = DHD_IOCTL_MAGIC;
161 get_magic = DHD_GET_MAGIC;
162 }
163  
164 /* do it */
165 ioc.cmd = cmd;
166 ioc.buf = buf;
167 ioc.len = len;
168 ioc.set = set;
169 ioc.driver = driver_magic;
170  
171 ret = __dhd_driver_io(dhd, &ioc);
172 if (ret < 0 && cmd != get_magic)
173 ret = BCME_IOCTL_ERROR;
174 return ret;
175 }
176  
177 /* This function is called in wlu_pipe.c remote_wifi_ser_init() to execute
178 * the initial set of wl commands for wifi transport (e.g slow_timer, fast_timer etc)
179 */
180 int wl_ioctl(void *wl, int cmd, void *buf, int len, bool set)
181 {
182 return dhd_ioctl(wl, cmd, buf, len, set); /* Call actual wl_ioctl here: Shubhro */
183 }
184  
185 /* Search if dhd adapter or wl adapter is present
186 * This is called by dhd_find to check if it supports wl or dhd
187 * The reason for checking wl adapter is that we can still send remote dhd commands over
188 * wifi transport.
189 */
190 static int
191 dhd_get_dev_type(char *name, void *buf, char *type)
192 {
193 int s;
194 int ret;
195 struct ifreq ifr;
196 struct ethtool_drvinfo info;
197  
198 /* open socket to kernel */
199 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
200 syserr("socket");
201  
202 /* get device type */
203 memset(&info, 0, sizeof(info));
204 info.cmd = ETHTOOL_GDRVINFO;
205 strcpy(info.driver, "?");
206 strcat(info.driver, type);
207 ifr.ifr_data = (caddr_t)&info;
208 strncpy(ifr.ifr_name, name, IFNAMSIZ);
209 if ((ret = ioctl(s, SIOCETHTOOL, &ifr)) < 0) {
210  
211 if (errno != EAGAIN)
212 syserr(__FUNCTION__);
213  
214 *(char *)buf = '\0';
215 }
216 else
217 strcpy(buf, info.driver);
218  
219 close(s);
220 return ret;
221 }
222  
223 /* dhd_get/dhd_set is called by several functions in dhdu.c. This used to call dhd_ioctl
224 * directly. However now we need to execute the dhd commands remotely.
225 * So we make use of wl pipes to execute this.
226 * wl_get or wl_set functions also check if it is a local command hence they in turn
227 * call dhd_ioctl if required. Name wl_get/wl_set is retained because these functions are
228 * also called by wlu_pipe.c wlu_client_shared.c
229 */
230 int
231 dhd_get(void *dhd, int cmd, void *buf, int len)
232 {
233 return wl_get(dhd, cmd, buf, len);
234 }
235  
236 /*
237 * To use /dev/node interface:
238 * 1. mknod /dev/hnd0 c 248 0
239 * 2. chmod 777 /dev/hnd0
240 */
241 #define NODE "/dev/hnd0"
242  
243 int
244 dhd_set(void *dhd, int cmd, void *buf, int len)
245 {
246 static int dnode = -1;
247  
248 switch (cmd) {
249 case DHD_DLDN_ST:
250 if (dnode == -1)
251 dnode = open(NODE, O_RDWR);
252 else
253 fprintf(stderr, "devnode already opened!\n");
254  
255 return dnode;
256 break;
257 case DHD_DLDN_WRITE:
258 if (dnode > 0)
259 return write(dnode, buf, len);
260 break;
261 case DHD_DLDN_END:
262 if (dnode > 0)
263 return close(dnode);
264 break;
265 default:
266 return wl_set(dhd, cmd, buf, len);
267  
268 }
269  
270 return -1;
271 }
272  
273 /* Verify the wl adapter found.
274 * This is called by dhd_find to check if it supports wl
275 * The reason for checking wl adapter is that we can still send remote dhd commands over
276 * wifi transport. The function is copied from wlu.c.
277 */
278 int
279 wl_check(void *wl)
280 {
281 int ret;
282 int val = 0;
283  
284 if (!dhd_check (wl))
285 return 0;
286  
287 /*
288 * If dhd_check() fails then go for a regular wl driver verification
289 */
290 if ((ret = wl_get(wl, WLC_GET_MAGIC, &val, sizeof(int))) < 0)
291 return ret;
292 if (val != WLC_IOCTL_MAGIC)
293 return BCME_ERROR;
294 if ((ret = wl_get(wl, WLC_GET_VERSION, &val, sizeof(int))) < 0)
295 return ret;
296 if (val > WLC_IOCTL_VERSION) {
297 fprintf(stderr, "Version mismatch, please upgrade\n");
298 return BCME_ERROR;
299 }
300 return 0;
301 }
302 /* Search and verify the request type of adapter (wl or dhd)
303 * This is called by main before executing local dhd commands
304 * or sending remote dhd commands over wifi transport
305 */
306 void
307 dhd_find(struct ifreq *ifr, char *type)
308 {
309 char proc_net_dev[] = "/proc/net/dev";
310 FILE *fp;
311 static char buf[400];
312 char *c, *name;
313 char dev_type[32];
314  
315 ifr->ifr_name[0] = '\0';
316 /* eat first two lines */
317 if (!(fp = fopen(proc_net_dev, "r")) ||
318 !fgets(buf, sizeof(buf), fp) ||
319 !fgets(buf, sizeof(buf), fp))
320 return;
321  
322 while (fgets(buf, sizeof(buf), fp)) {
323 c = buf;
324 while (isspace(*c))
325 c++;
326 if (!(name = strsep(&c, ":")))
327 continue;
328 strncpy(ifr->ifr_name, name, IFNAMSIZ);
329 if (dhd_get_dev_type(name, dev_type, type) >= 0 &&
330 !strncmp(dev_type, type, strlen(dev_type) - 1))
331 {
332 if (!wl_check((void*)ifr))
333 break;
334 }
335 ifr->ifr_name[0] = '\0';
336 }
337  
338 fclose(fp);
339 }
340 /* This function is called by wl_get to execute either local dhd command
341 * or send a dhd command over wl transport
342 */
343 static int
344 ioctl_queryinformation_fe(void *wl, int cmd, void* input_buf, int *input_len)
345 {
346 if (remote_type == NO_REMOTE) {
347 return dhd_ioctl(wl, cmd, input_buf, *input_len, FALSE);
348 }
349 #ifdef RWL_ENABLE
350 else {
351 return rwl_queryinformation_fe(wl, cmd, input_buf,
352 (unsigned long*)input_len, 0, RDHD_GET_IOCTL);
353 }
354 #else /* RWL_ENABLE */
355 return BCME_IOCTL_ERROR;
356 #endif /* RWL_ENABLE */
357 }
358  
359 /* This function is called by wl_set to execute either local dhd command
360 * or send a dhd command over wl transport
361 */
362 static int
363 ioctl_setinformation_fe(void *wl, int cmd, void* buf, int *len)
364 {
365 if (remote_type == NO_REMOTE) {
366 return dhd_ioctl(wl, cmd, buf, *len, TRUE);
367 }
368 #ifdef RWL_ENABLE
369 else {
370 return rwl_setinformation_fe(wl, cmd, buf, (unsigned long*)len, 0, RDHD_SET_IOCTL);
371  
372 }
373 #else /* RWL_ENABLE */
374 return BCME_IOCTL_ERROR;
375 #endif /* RWL_ENABLE */
376 }
377  
378 /* The function is replica of wl_get in wlu_linux.c. Optimize when we have some
379 * common code between wlu_linux.c and dhdu_linux.c
380 */
381 int
382 wl_get(void *wl, int cmd, void *buf, int len)
383 {
384 int error = BCME_OK;
385 /* For RWL: When interfacing to a Windows client, need t add in OID_BASE */
386 if ((rwl_os_type == WIN32_OS) && (remote_type != NO_REMOTE)) {
387 error = (int)ioctl_queryinformation_fe(wl, WL_OID_BASE + cmd, buf, &len);
388 } else {
389 error = (int)ioctl_queryinformation_fe(wl, cmd, buf, &len);
390 }
391 if (error == BCME_SERIAL_PORT_ERR)
392 return BCME_SERIAL_PORT_ERR;
393  
394 if (error != 0)
395 return BCME_IOCTL_ERROR;
396  
397 return error;
398 }
399  
400 /* The function is replica of wl_set in wlu_linux.c. Optimize when we have some
401 * common code between wlu_linux.c and dhdu_linux.c
402 */
403 int
404 wl_set(void *wl, int cmd, void *buf, int len)
405 {
406 int error = BCME_OK;
407  
408 /* For RWL: When interfacing to a Windows client, need t add in OID_BASE */
409 if ((rwl_os_type == WIN32_OS) && (remote_type != NO_REMOTE)) {
410 error = (int)ioctl_setinformation_fe(wl, WL_OID_BASE + cmd, buf, &len);
411 } else {
412 error = (int)ioctl_setinformation_fe(wl, cmd, buf, &len);
413 }
414  
415 if (error == BCME_SERIAL_PORT_ERR)
416 return BCME_SERIAL_PORT_ERR;
417  
418 if (error != 0) {
419 return BCME_IOCTL_ERROR;
420 }
421 return error;
422 }
423  
424 int
425 wl_validatedev(void *dev_handle)
426 {
427 int retval = 1;
428 struct ifreq *ifr = (struct ifreq *)dev_handle;
429 /* validate the interface */
430 if (!ifr->ifr_name || wl_check((void *)ifr)) {
431 retval = 0;
432 }
433 return retval;
434 }
435  
436 /* Main client function
437 * The code is mostly from wlu_linux.c. This function takes care of executing remote dhd commands
438 * along with the local dhd commands now.
439 */
440 int
441 main(int argc, char **argv)
442 {
443 struct ifreq ifr;
444 char *ifname = "wlan0";
445 int err = 0;
446 int help = 0;
447 int status = CMD_DHD;
448 #ifdef RWL_SOCKET
449 struct ipv4_addr temp;
450 #endif /* RWL_SOCKET */
451  
452 UNUSED_PARAMETER(argc);
453  
454 av0 = argv[0];
455 memset(&ifr, 0, sizeof(ifr));
456 argv++;
457  
458 strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
459 if ((status = dhd_option(&argv, &ifname, &help)) == CMD_OPT) {
460 if (ifname)
461 strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
462 }
463 /* Linux client looking for a Win32 server */
464 if (*argv && strncmp (*argv, "--wince", strlen(*argv)) == 0) {
465 rwl_os_type = WIN32_OS;
466 argv++;
467 }
468  
469 /* RWL socket transport Usage: --socket ipaddr [port num] */
470 if (*argv && strncmp (*argv, "--socket", strlen(*argv)) == 0) {
471 argv++;
472  
473 remote_type = REMOTE_SOCKET;
474 #ifdef RWL_SOCKET
475 if (!(*argv)) {
476 rwl_usage(remote_type);
477 return err;
478 }
479  
480 if (!dhd_atoip(*argv, &temp)) {
481 rwl_usage(remote_type);
482 return err;
483 }
484 g_rwl_servIP = *argv;
485 argv++;
486  
487 g_rwl_servport = DEFAULT_SERVER_PORT;
488 if ((*argv) && isdigit(**argv)) {
489 g_rwl_servport = atoi(*argv);
490 argv++;
491 }
492 #endif /* RWL_SOCKET */
493 }
494  
495 /* RWL from system serial port on client to uart dongle port on server */
496 /* Usage: --dongle /dev/ttyS0 */
497 if (*argv && strncmp (*argv, "--dongle", strlen(*argv)) == 0) {
498 argv++;
499 remote_type = REMOTE_DONGLE;
500 }
501  
502 /* RWL over wifi. Usage: --wifi mac_address */
503 if (*argv && strncmp (*argv, "--wifi", strlen(*argv)) == 0) {
504 argv++;
505 #ifdef RWL_WIFI
506 remote_type = NO_REMOTE;
507 if (!ifr.ifr_name[0])
508 {
509 dhd_find(&ifr, "wl");
510 }
511 /* validate the interface */
512 if (!ifr.ifr_name[0] || wl_check((void*)&ifr)) {
513 fprintf(stderr, "%s: wl driver adapter not found\n", av0);
514 exit(1);
515 }
516 remote_type = REMOTE_WIFI;
517  
518 if (argc < 4) {
519 rwl_usage(remote_type);
520 return err;
521 }
522 /* copy server mac address to local buffer for later use by findserver cmd */
523 if (!dhd_ether_atoe(*argv, (struct ether_addr *)g_rwl_buf_mac)) {
524 fprintf(stderr,
525 "could not parse as an ethernet MAC address\n");
526 return FAIL;
527 }
528 argv++;
529 #else /* RWL_WIFI */
530 remote_type = REMOTE_WIFI;
531 #endif /* RWL_WIFI */
532 }
533  
534 /* Process for local dhd */
535 if (remote_type == NO_REMOTE) {
536 err = process_args(&ifr, argv);
537 return err;
538 }
539  
540 #ifdef RWL_ENABLE
541 if (*argv) {
542 err = process_args(&ifr, argv);
543 if ((err == BCME_SERIAL_PORT_ERR) && (remote_type == REMOTE_DONGLE)) {
544 DPRINT_ERR(ERR, "\n Retry again\n");
545 err = process_args((struct ifreq*)&ifr, argv);
546 }
547 return err;
548 }
549 rwl_usage(remote_type);
550 #endif /* RWL_ENABLE */
551  
552 return err;
553 }
554 /*
555 * Function called for 'local' execution and for 'remote' non-interactive session
556 * (shell cmd, wl cmd) .The code is mostly from wlu_linux.c. This code can be
557 * common to wlu_linux.c and dhdu_linux.c
558 */
559 static int
560 process_args(struct ifreq* ifr, char **argv)
561 {
562 char *ifname = NULL;
563 int help = 0;
564 int status = 0;
565 int err = BCME_OK;
566 cmd_t *cmd = NULL;
567 while (*argv) {
568 #ifdef RWL_ENABLE
569 if ((strcmp (*argv, "sh") == 0) && (remote_type != NO_REMOTE)) {
570 argv++; /* Get the shell command */
571 if (*argv) {
572 /* Register handler in case of shell command only */
573 signal(SIGINT, ctrlc_handler);
574 err = rwl_shell_cmd_proc((void*)ifr, argv, SHELL_CMD);
575 } else {
576 DPRINT_ERR(ERR,
577 "Enter the shell command (e.g ls(Linux) or dir(Win CE) \n");
578 err = BCME_ERROR;
579 }
580 return err;
581 }
582 #endif /* RWL_ENABLE */
583 if ((status = dhd_option(&argv, &ifname, &help)) == CMD_OPT) {
584 if (help)
585 break;
586 if (ifname)
587 strncpy(ifr->ifr_name, ifname, IFNAMSIZ);
588 continue;
589 }
590 /* parse error */
591 else if (status == CMD_ERR)
592 break;
593  
594 if (remote_type == NO_REMOTE) {
595 int ret;
596  
597 /* use default interface */
598 if (!ifr->ifr_name[0])
599 dhd_find(ifr, "dhd");
600 /* validate the interface */
601 if (!ifr->ifr_name[0]) {
602 if (strcmp("dldn", *argv) != 0) {
603 exit(ENXIO);
604 syserr("interface");
605 }
606 }
607 if ((ret = dhd_check((void *)ifr)) != 0) {
608 if (strcmp("dldn", *argv) != 0) {
609 errno = -ret;
610 syserr("dhd_check");
611 }
612 }
613 }
614 /* search for command */
615 cmd = dhd_find_cmd(*argv);
616 /* if not found, use default set_var and get_var commands */
617 if (!cmd) {
618 cmd = &dhd_varcmd;
619 }
620  
621 /* do command */
622 err = (*cmd->func)((void *) ifr, cmd, argv);
623 break;
624 } /* while loop end */
625  
626 /* provide for help on a particular command */
627 if (help && *argv) {
628 cmd = dhd_find_cmd(*argv);
629 if (cmd) {
630 dhd_cmd_usage(cmd);
631 } else {
632 DPRINT_ERR(ERR, "%s: Unrecognized command \"%s\", type -h for help\n",
633 av0, *argv);
634 }
635 } else if (!cmd)
636 dhd_usage(NULL);
637 else if (err == BCME_USAGE_ERROR)
638 dhd_cmd_usage(cmd);
639 else if (err == BCME_IOCTL_ERROR)
640 dhd_printlasterror((void *) ifr);
641  
642 return err;
643 }
644  
645 int
646 rwl_shell_createproc(void *wl)
647 {
648 UNUSED_PARAMETER(wl);
649 return fork();
650 }
651  
652 void
653 rwl_shell_killproc(int pid)
654 {
655 kill(pid, SIGKILL);
656 }
657  
658 #ifdef RWL_SOCKET
659 /* validate hostname/ip given by the client */
660 int
661 validate_server_address()
662 {
663 struct hostent *he;
664 struct ipv4_addr temp;
665  
666 if (!dhd_atoip(g_rwl_servIP, &temp)) {
667 /* Wrong IP address format check for hostname */
668 if ((he = gethostbyname(g_rwl_servIP)) != NULL) {
669 if (!dhd_atoip(*he->h_addr_list, &temp)) {
670 g_rwl_servIP = inet_ntoa(*(struct in_addr *)*he->h_addr_list);
671 if (g_rwl_servIP == NULL) {
672 DPRINT_ERR(ERR, "Error at inet_ntoa \n");
673 return FAIL;
674 }
675 } else {
676 DPRINT_ERR(ERR, "Error in IP address \n");
677 return FAIL;
678 }
679 } else {
680 DPRINT_ERR(ERR, "Enter correct IP address/hostname format\n");
681 return FAIL;
682 }
683 }
684 return SUCCESS;
685 }
686 #endif /* RWL_SOCKET */