| /* |
| * Wl server for linux |
| * |
| * Broadcom Proprietary and Confidential. Copyright (C) 2017, |
| * All Rights Reserved. |
| * |
| * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom; |
| * the contents of this file may not be disclosed to third parties, copied |
| * or duplicated in any form, in whole or in part, without the prior |
| * written permission of Broadcom. |
| * |
| * |
| * <<Broadcom-WL-IPTag/Proprietary:>> |
| * |
| * $Id: wlu_server_linux.c 659779 2016-09-15 23:33:28Z $ |
| */ |
| |
| /* Revision History:Linux port of Remote wl server |
| * |
| * Date Author Description |
| * |
| * 27-Dec-2007 Suganthi Version 0.1 |
| * |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <ctype.h> |
| #include <malloc.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <netdb.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <sys/socket.h> |
| #include <netinet/in.h> |
| #include <arpa/inet.h> |
| #include <sys/wait.h> |
| #include <signal.h> |
| |
| #include <sys/ioctl.h> |
| #include <net/if.h> |
| #ifndef TARGETENV_android |
| #include <linux/types.h> |
| #endif |
| |
| #include <linux/sockios.h> |
| #include <linux/ethtool.h> |
| |
| #include <typedefs.h> |
| #include <wlioctl.h> |
| #include <dhdioctl.h> |
| #include <proto/ethernet.h> |
| #include <bcmendian.h> |
| #include <bcmutils.h> |
| #include <bcmcdc.h> |
| #include <proto/802.11.h> |
| #include "wlu_remote.h" |
| |
| |
| #define DEV_TYPE_LEN 3 /* length for devtype 'wl'/'et' */ |
| #define BUF_LENGTH 1000 |
| |
| #define dtoh32(i) i |
| |
| |
| #ifdef IL_BIGENDIAN |
| bool swap = FALSE; |
| #endif |
| const char *rwl_server_name; |
| void *g_wl_handle; |
| unsigned short defined_debug = DEBUG_ERR | DEBUG_INFO; |
| extern int remote_server_exec(int argc, char **argv, void *ifr); |
| |
| /* Global to have the PID of the current sync command |
| * This is required in case the sync command fails to respond, |
| * the alarm handler shall kill the PID upon a timeout |
| */ |
| int g_shellsync_pid; |
| unsigned char g_return_stat = 0; |
| |
| static void |
| syserr(char *s) |
| { |
| fprintf(stderr, "%s: ", rwl_server_name); |
| perror(s); |
| exit(errno); |
| } |
| /* The handler for DHD commands */ |
| int |
| dhd_ioctl(void *dhd, int cmd, void *buf, int len, bool set) |
| { |
| struct ifreq *ifr = (struct ifreq *) dhd; |
| dhd_ioctl_t ioc; |
| int ret = 0; |
| int s; |
| |
| /* open socket to kernel */ |
| if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) |
| syserr("socket"); |
| |
| /* do it */ |
| ioc.cmd = cmd; |
| ioc.buf = buf; |
| ioc.len = len; |
| ioc.set = set; |
| ioc.driver = DHD_IOCTL_MAGIC; |
| ifr->ifr_data = (caddr_t) &ioc; |
| if ((ret = ioctl(s, SIOCDEVPRIVATE, ifr)) < 0) { |
| if (cmd != DHD_GET_MAGIC) { |
| ret = BCME_IOCTL_ERROR; |
| } |
| } |
| /* cleanup */ |
| close(s); |
| return ret; |
| } |
| |
| int |
| wl_ioctl(void *wl, int cmd, void *buf, int len, bool set) |
| { |
| struct ifreq *ifr = (struct ifreq *) wl; |
| wl_ioctl_t ioc; |
| int ret = 0; |
| int s; |
| |
| /* open socket to kernel */ |
| if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) |
| syserr("socket"); |
| |
| /* do it */ |
| ioc.cmd = cmd; |
| ioc.buf = buf; |
| ioc.len = len; |
| ioc.set = set; |
| ifr->ifr_data = (caddr_t) &ioc; |
| if ((ret = ioctl(s, SIOCDEVPRIVATE, ifr)) < 0) { |
| if (cmd != WLC_GET_MAGIC) { |
| ret = BCME_IOCTL_ERROR; |
| } |
| } |
| /* cleanup */ |
| close(s); |
| return ret; |
| } |
| |
| /* Functions copied from wlu.c to check for the driver adapter in the server machine */ |
| int |
| wl_check(void *wl) |
| { |
| int ret; |
| int val; |
| |
| if ((ret = wl_ioctl(wl, WLC_GET_MAGIC, &val, sizeof(int), FALSE)) < 0) |
| return ret; |
| #ifdef IL_BIGENDIAN |
| /* Detect if IOCTL swapping is necessary */ |
| if (val == (int)bcmswap32(WLC_IOCTL_MAGIC)) |
| { |
| val = bcmswap32(val); |
| swap = TRUE; |
| } |
| #endif |
| if (val != WLC_IOCTL_MAGIC) |
| return -1; |
| if ((ret = wl_ioctl(wl, WLC_GET_VERSION, &val, sizeof(int), FALSE)) < 0) |
| return ret; |
| val = dtoh32(val); |
| |
| if (val > WLC_IOCTL_VERSION) { |
| fprintf(stderr, "Version mismatch, please upgrade\n"); |
| return -1; |
| } |
| return 0; |
| } |
| |
| static int |
| wl_get_dev_type(char *name, void *buf, int len) |
| { |
| int s; |
| int ret; |
| struct ifreq ifr; |
| struct ethtool_drvinfo info; |
| |
| /* open socket to kernel */ |
| if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) |
| syserr("socket"); |
| |
| /* get device type */ |
| memset(&info, 0, sizeof(info)); |
| info.cmd = ETHTOOL_GDRVINFO; |
| ifr.ifr_data = (caddr_t)&info; |
| strncpy(ifr.ifr_name, name, IFNAMSIZ); |
| if ((ret = ioctl(s, SIOCETHTOOL, &ifr)) < 0) { |
| /* print a good diagnostic if not superuser */ |
| if (errno == EPERM) |
| syserr("wl_get_dev_type"); |
| *(char *)buf = '\0'; |
| } else { |
| strncpy(buf, info.driver, len); |
| } |
| close(s); |
| return ret; |
| } |
| |
| static void |
| wl_find_adapter(struct ifreq *ifr) |
| { |
| char proc_net_dev[] = "/proc/net/dev"; |
| FILE *fp; |
| char buf[BUF_LENGTH], *c, *name; |
| char dev_type[DEV_TYPE_LEN]; |
| |
| ifr->ifr_name[0] = '\0'; |
| |
| if (!(fp = fopen(proc_net_dev, "r"))) |
| return; |
| |
| /* eat first two lines */ |
| if (!fgets(buf, sizeof(buf), fp) || |
| !fgets(buf, sizeof(buf), fp)) { |
| fclose(fp); |
| return; |
| } |
| |
| while (fgets(buf, sizeof(buf), fp)) { |
| c = buf; |
| while (isspace(*c)) |
| c++; |
| if (!(name = strsep(&c, ":"))) |
| continue; |
| strncpy(ifr->ifr_name, name, IFNAMSIZ-1); |
| ifr->ifr_name[IFNAMSIZ-1] = 0; |
| |
| /* |
| * If no -i interface, prioritize ethX, not say wl1.2 |
| * wlanX is Android |
| */ |
| if (!strncmp(name, "wl", 2) && strncmp(name, "wlan", 4)) { |
| continue; |
| } |
| |
| if (wl_get_dev_type(name, dev_type, DEV_TYPE_LEN) >= 0 && |
| !strncmp(dev_type, "wl", 2)) |
| if (wl_check((void *) ifr) == 0) |
| break; |
| ifr->ifr_name[0] = '\0'; |
| } |
| |
| fclose(fp); |
| } |
| |
| int |
| wl_get(void *wl, int cmd, void *buf, int len) |
| { |
| int error; |
| |
| error = wl_ioctl(wl, cmd, buf, len, FALSE); |
| |
| return error; |
| } |
| |
| int |
| wl_set(void *wl, int cmd, void *buf, int len) |
| { |
| int error; |
| |
| error = wl_ioctl(wl, cmd, buf, len, TRUE); |
| |
| return error; |
| } |
| |
| extern int set_ctrlc; |
| void handle_ctrlc(int unused) |
| { |
| UNUSED_PARAMETER(unused); |
| set_ctrlc = 1; |
| return; |
| } |
| |
| volatile sig_atomic_t g_sig_chld = 1; |
| void rwl_chld_handler(int num) |
| { |
| int child_status; |
| |
| UNUSED_PARAMETER(num); |
| /* g_return_stat is being set with the return status of sh commands */ |
| waitpid(g_shellsync_pid, &child_status, WNOHANG); |
| if (WIFEXITED(child_status)) |
| g_return_stat = WEXITSTATUS(child_status); |
| else if (g_rem_ptr->msg.flags == (unsigned)CTRLC_FLAG) |
| g_return_stat = 0; |
| else |
| g_return_stat = 1; |
| g_sig_chld = 0; |
| } |
| |
| /* Alarm handler called after SHELL_TIMEOUT value |
| * This handler kills the non-responsive shell process |
| * with the PID value g_shellsync_pid |
| */ |
| static void |
| sigalrm_handler(int s) |
| { |
| UNUSED_PARAMETER(s); |
| |
| if (g_shellsync_pid) { |
| kill(g_shellsync_pid, SIGINT); |
| } |
| #ifdef RWL_SOCKET |
| g_sig_chld = 0; |
| #endif |
| } |
| |
| static void |
| def_handler(int s) |
| { |
| UNUSED_PARAMETER(s); |
| kill(g_shellsync_pid, SIGKILL); |
| exit(0); |
| } |
| |
| static void |
| pipe_handler(int s) |
| { |
| UNUSED_PARAMETER(s); |
| kill(g_shellsync_pid, SIGKILL); |
| } |
| |
| int |
| main(int argc, char **argv) |
| { |
| int err = 0; |
| struct ifreq *ifr; |
| |
| rwl_server_name = argv[0]; |
| |
| if ((ifr = calloc(1, sizeof(*ifr))) == NULL) |
| { |
| DPRINT_ERR(ERR, "wl_server: Unable to allocate memory for handle\n"); |
| exit(1); |
| } |
| |
| memset(ifr, 0, sizeof(*ifr)); |
| #ifdef PCIE_MFGTEST |
| /* For router platform hardcode to eth1 */ |
| strcpy(ifr->ifr_name, "eth1"); |
| #endif /* PCIE_MFGTEST */ |
| |
| /* use default interface */ |
| if (!ifr->ifr_name[0]) |
| wl_find_adapter(ifr); |
| /* validate the interface */ |
| if (!ifr->ifr_name[0] || wl_check((void *)ifr)) { |
| DPRINT_INFO(OUTPUT, "wl_server: wl driver adapter not found\n"); |
| } |
| g_wl_handle = ifr; |
| |
| /* Register signal handlers */ |
| signal(SIGCHLD, rwl_chld_handler); |
| signal(SIGALRM, sigalrm_handler); |
| signal(SIGTERM, def_handler); |
| signal(SIGPIPE, pipe_handler); |
| signal(SIGABRT, def_handler); |
| #ifdef RWL_DONGLE |
| signal(SIGINT, handle_ctrlc); |
| #endif |
| /* Main server process for all transport types */ |
| err = remote_server_exec(argc, argv, ifr); |
| free(ifr); |
| return err; |
| } |
| |
| /* |
| * Funtion to store old interface. |
| */ |
| void |
| store_old_interface(void *wl, char *old_intf_name) |
| { |
| strcpy(old_intf_name, ((struct ifreq *)wl)->ifr_name); |
| } |
| |
| /* |
| * Function to set interface. |
| */ |
| int |
| set_interface(void *wl, char *intf_name) |
| { |
| struct ifreq *ifr = (struct ifreq *)wl; |
| |
| if (strlen(intf_name) != 0) { |
| strncpy(ifr->ifr_name, intf_name, strlen(intf_name) + 1); |
| return BCME_OK; |
| } |
| else { |
| DPRINT_DBG(OUTPUT, "Default Interface will be used ... \n"); |
| return BCME_ERROR; |
| } |
| } |