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> 41typedef u_int64_t u64; 42typedef u_int32_t u32; 43typedef u_int16_t u16; 44typedef 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 73char *av0; 74static 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 */ 78static cmd_t * 79dhd_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 89static void 90syserr(const char *s) 91{ 92 fprintf(stderr, "%s: ", av0); 93 perror(s); 94 exit(errno); 95} 96 97#ifdef NL80211 98static 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 118static 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 */ 144static int 145dhd_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 */ 180int 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 */ 190static int 191dhd_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 */ 230int 231dhd_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 243int 244dhd_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 */ 278int 279wl_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 */ 306void 307dhd_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 */ 343static int 344ioctl_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 */ 362static int 363ioctl_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 */ 381int 382wl_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 */ 403int 404wl_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 424int 425wl_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 */ 440int 441main(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 */ 558static int 559process_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 644int 645rwl_shell_createproc(void *wl) 646{ 647 UNUSED_PARAMETER(wl); 648 return fork(); 649} 650 651void 652rwl_shell_killproc(int pid) 653{ 654 kill(pid, SIGKILL); 655} 656 657#ifdef RWL_SOCKET 658/* validate hostname/ip given by the client */ 659int 660validate_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