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 = NULL; 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 if ((status = dhd_option(&argv, &ifname, &help)) == CMD_OPT) { 459 if (ifname) 460 strncpy(ifr.ifr_name, ifname, IFNAMSIZ); 461 } 462 /* Linux client looking for a Win32 server */ 463 if (*argv && strncmp (*argv, "--wince", strlen(*argv)) == 0) { 464 rwl_os_type = WIN32_OS; 465 argv++; 466 } 467 468 /* RWL socket transport Usage: --socket ipaddr [port num] */ 469 if (*argv && strncmp (*argv, "--socket", strlen(*argv)) == 0) { 470 argv++; 471 472 remote_type = REMOTE_SOCKET; 473 #ifdef RWL_SOCKET 474 if (!(*argv)) { 475 rwl_usage(remote_type); 476 return err; 477 } 478 479 if (!dhd_atoip(*argv, &temp)) { 480 rwl_usage(remote_type); 481 return err; 482 } 483 g_rwl_servIP = *argv; 484 argv++; 485 486 g_rwl_servport = DEFAULT_SERVER_PORT; 487 if ((*argv) && isdigit(**argv)) { 488 g_rwl_servport = atoi(*argv); 489 argv++; 490 } 491 #endif /* RWL_SOCKET */ 492 } 493 494 /* RWL from system serial port on client to uart dongle port on server */ 495 /* Usage: --dongle /dev/ttyS0 */ 496 if (*argv && strncmp (*argv, "--dongle", strlen(*argv)) == 0) { 497 argv++; 498 remote_type = REMOTE_DONGLE; 499 } 500 501 /* RWL over wifi. Usage: --wifi mac_address */ 502 if (*argv && strncmp (*argv, "--wifi", strlen(*argv)) == 0) { 503 argv++; 504 #ifdef RWL_WIFI 505 remote_type = NO_REMOTE; 506 if (!ifr.ifr_name[0]) 507 { 508 dhd_find(&ifr, "wl"); 509 } 510 /* validate the interface */ 511 if (!ifr.ifr_name[0] || wl_check((void*)&ifr)) { 512 fprintf(stderr, "%s: wl driver adapter not found\n", av0); 513 exit(1); 514 } 515 remote_type = REMOTE_WIFI; 516 517 if (argc < 4) { 518 rwl_usage(remote_type); 519 return err; 520 } 521 /* copy server mac address to local buffer for later use by findserver cmd */ 522 if (!dhd_ether_atoe(*argv, (struct ether_addr *)g_rwl_buf_mac)) { 523 fprintf(stderr, 524 "could not parse as an ethernet MAC address\n"); 525 return FAIL; 526 } 527 argv++; 528 #else /* RWL_WIFI */ 529 remote_type = REMOTE_WIFI; 530 #endif /* RWL_WIFI */ 531 } 532 533 /* Process for local dhd */ 534 if (remote_type == NO_REMOTE) { 535 err = process_args(&ifr, argv); 536 return err; 537 } 538 539 #ifdef RWL_ENABLE 540 if (*argv) { 541 err = process_args(&ifr, argv); 542 if ((err == BCME_SERIAL_PORT_ERR) && (remote_type == REMOTE_DONGLE)) { 543 DPRINT_ERR(ERR, "\n Retry again\n"); 544 err = process_args((struct ifreq*)&ifr, argv); 545 } 546 return err; 547 } 548 rwl_usage(remote_type); 549 #endif /* RWL_ENABLE */ 550 551 return err; 552 } 553 /* 554 * Function called for 'local' execution and for 'remote' non-interactive session 555 * (shell cmd, wl cmd) .The code is mostly from wlu_linux.c. This code can be 556 * common to wlu_linux.c and dhdu_linux.c 557 */ 558 static int 559 process_args(struct ifreq* ifr, char **argv) 560 { 561 char *ifname = NULL; 562 int help = 0; 563 int status = 0; 564 int err = BCME_OK; 565 cmd_t *cmd = NULL; 566 while (*argv) { 567 #ifdef RWL_ENABLE 568 if ((strcmp (*argv, "sh") == 0) && (remote_type != NO_REMOTE)) { 569 argv++; /* Get the shell command */ 570 if (*argv) { 571 /* Register handler in case of shell command only */ 572 signal(SIGINT, ctrlc_handler); 573 err = rwl_shell_cmd_proc((void*)ifr, argv, SHELL_CMD); 574 } else { 575 DPRINT_ERR(ERR, 576 "Enter the shell command (e.g ls(Linux) or dir(Win CE) \n"); 577 err = BCME_ERROR; 578 } 579 return err; 580 } 581 #endif /* RWL_ENABLE */ 582 if ((status = dhd_option(&argv, &ifname, &help)) == CMD_OPT) { 583 if (help) 584 break; 585 if (ifname) 586 strncpy(ifr->ifr_name, ifname, IFNAMSIZ); 587 continue; 588 } 589 /* parse error */ 590 else if (status == CMD_ERR) 591 break; 592 593 if (remote_type == NO_REMOTE) { 594 int ret; 595 596 /* use default interface */ 597 if (!ifr->ifr_name[0]) 598 dhd_find(ifr, "dhd"); 599 /* validate the interface */ 600 if (!ifr->ifr_name[0]) { 601 if (strcmp("dldn", *argv) != 0) { 602 exit(ENXIO); 603 syserr("interface"); 604 } 605 } 606 if ((ret = dhd_check((void *)ifr)) != 0) { 607 if (strcmp("dldn", *argv) != 0) { 608 errno = -ret; 609 syserr("dhd_check"); 610 } 611 } 612 } 613 /* search for command */ 614 cmd = dhd_find_cmd(*argv); 615 /* if not found, use default set_var and get_var commands */ 616 if (!cmd) { 617 cmd = &dhd_varcmd; 618 } 619 620 /* do command */ 621 err = (*cmd->func)((void *) ifr, cmd, argv); 622 break; 623 } /* while loop end */ 624 625 /* provide for help on a particular command */ 626 if (help && *argv) { 627 cmd = dhd_find_cmd(*argv); 628 if (cmd) { 629 dhd_cmd_usage(cmd); 630 } else { 631 DPRINT_ERR(ERR, "%s: Unrecognized command \"%s\", type -h for help\n", 632 av0, *argv); 633 } 634 } else if (!cmd) 635 dhd_usage(NULL); 636 else if (err == BCME_USAGE_ERROR) 637 dhd_cmd_usage(cmd); 638 else if (err == BCME_IOCTL_ERROR) 639 dhd_printlasterror((void *) ifr); 640 641 return err; 642 } 643 644 int 645 rwl_shell_createproc(void *wl) 646 { 647 UNUSED_PARAMETER(wl); 648 return fork(); 649 } 650 651 void 652 rwl_shell_killproc(int pid) 653 { 654 kill(pid, SIGKILL); 655 } 656 657 #ifdef RWL_SOCKET 658 /* validate hostname/ip given by the client */ 659 int 660 validate_server_address() 661 { 662 struct hostent *he; 663 struct ipv4_addr temp; 664 665 if (!dhd_atoip(g_rwl_servIP, &temp)) { 666 /* Wrong IP address format check for hostname */ 667 if ((he = gethostbyname(g_rwl_servIP)) != NULL) { 668 if (!dhd_atoip(*he->h_addr_list, &temp)) { 669 g_rwl_servIP = inet_ntoa(*(struct in_addr *)*he->h_addr_list); 670 if (g_rwl_servIP == NULL) { 671 DPRINT_ERR(ERR, "Error at inet_ntoa \n"); 672 return FAIL; 673 } 674 } else { 675 DPRINT_ERR(ERR, "Error in IP address \n"); 676 return FAIL; 677 } 678 } else { 679 DPRINT_ERR(ERR, "Enter correct IP address/hostname format\n"); 680 return FAIL; 681 } 682 } 683 return SUCCESS; 684 } 685 #endif /* RWL_SOCKET */ 686