| /* |
| * Linux port of dhd command line utility. |
| * |
| * Copyright (C) 1999-2011, Broadcom Corporation |
| * |
| * Permission to use, copy, modify, and/or distribute this software for any |
| * purpose with or without fee is hereby granted, provided that the above |
| * copyright notice and this permission notice appear in all copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
| * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
| * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| * |
| * $Id: dhdu_linux.c,v 1.16.2.2 2010-12-16 08:12:11 Exp $ |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <ctype.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| #include <sys/socket.h> |
| #include <proto/ethernet.h> |
| #include <proto/bcmip.h> |
| #include <arpa/inet.h> |
| #include <sys/ioctl.h> |
| #include <net/if.h> |
| #include <fcntl.h> |
| #include <sys/ioctl.h> |
| #include <unistd.h> |
| |
| #ifndef TARGETENV_android |
| #include <error.h> |
| typedef u_int64_t u64; |
| typedef u_int32_t u32; |
| typedef u_int16_t u16; |
| typedef u_int8_t u8; |
| #endif /* TARGETENV_android */ |
| #include <linux/sockios.h> |
| #include <linux/types.h> |
| #include <linux/ethtool.h> |
| |
| #include <typedefs.h> |
| #include <signal.h> |
| #include <dhdioctl.h> |
| #include <wlioctl.h> |
| #include <bcmcdc.h> |
| #include <bcmutils.h> |
| #include "dhdu.h" |
| #include <netdb.h> |
| #include <dhdioctl.h> |
| #include "dhdu_common.h" |
| |
| extern int wl_get(void *wl, int cmd, void *buf, int len); |
| extern int wl_set(void *wl, int cmd, void *buf, int len); |
| |
| char *av0; |
| /* Search the dhd_cmds table for a matching command name. |
| * Return the matching command or NULL if no match found. |
| */ |
| static cmd_t * |
| dhd_find_cmd(char* name) |
| { |
| cmd_t *cmd = NULL; |
| /* search the dhd_cmds for a matching name */ |
| for (cmd = dhd_cmds; cmd->name && strcmp(cmd->name, name); cmd++); |
| if (cmd->name == NULL) |
| cmd = NULL; |
| return cmd; |
| } |
| |
| static void |
| syserr(char *s) |
| { |
| fprintf(stderr, "%s: ", dhdu_av0); |
| perror(s); |
| exit(errno); |
| } |
| |
| /* This function is called by ioctl_setinformation_fe or ioctl_queryinformation_fe |
| * for executing remote commands or local commands |
| */ |
| static 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; |
| /* By default try to execute wl commands */ |
| int driver_magic = DHD_IOCTL_MAGIC; |
| int get_magic = DHD_GET_MAGIC; |
| |
| /* 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 = driver_magic; |
| ifr->ifr_data = (caddr_t) &ioc; |
| |
| if ((ret = ioctl(s, SIOCDEVPRIVATE, ifr)) < 0) { |
| if (cmd != get_magic) { |
| ret = IOCTL_ERROR; |
| } |
| } |
| |
| /* cleanup */ |
| close(s); |
| return ret; |
| } |
| |
| /* This function is called in wlu_pipe.c remote_wifi_ser_init() to execute |
| * the initial set of wl commands for wifi transport (e.g slow_timer, fast_timer etc) |
| */ |
| int wl_ioctl(void *wl, int cmd, void *buf, int len, bool set) |
| { |
| return dhd_ioctl(wl, cmd, buf, len, set); /* Call actual wl_ioctl here: Shubhro */ |
| } |
| |
| /* Search if dhd adapter or wl adapter is present |
| * This is called by dhd_find to check if it supports wl or dhd |
| * The reason for checking wl adapter is that we can still send remote dhd commands over |
| * wifi transport. |
| */ |
| static int |
| dhd_get_dev_type(char *name, void *buf, char *type) |
| { |
| 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; |
| strcpy(info.driver, "?"); |
| strcat(info.driver, type); |
| 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("dhd_get_dev_type"); |
| |
| *(char *)buf = '\0'; |
| } |
| else |
| strcpy(buf, info.driver); |
| |
| close(s); |
| return ret; |
| } |
| |
| /* dhd_get/dhd_set is called by several functions in dhdu.c. This used to call dhd_ioctl |
| * directly. However now we need to execute the dhd commands remotely. |
| * So we make use of wl pipes to execute this. |
| * wl_get or wl_set functions also check if it is a local command hence they in turn |
| * call dhd_ioctl if required. Name wl_get/wl_set is retained because these functions are |
| * also called by wlu_pipe.c wlu_client_shared.c |
| */ |
| int |
| dhd_get(void *dhd, int cmd, void *buf, int len) |
| { |
| return wl_get(dhd, cmd, buf, len); |
| } |
| |
| /* |
| * To use /dev/node interface: |
| * 1. mknod /dev/hnd0 c 248 0 |
| * 2. chmod 777 /dev/hnd0 |
| */ |
| #define NODE "/dev/hnd0" |
| |
| int |
| dhd_set(void *dhd, int cmd, void *buf, int len) |
| { |
| static int dnode = -1; |
| |
| switch (cmd) { |
| case DHD_DLDN_ST: |
| if (dnode == -1) |
| dnode = open(NODE, O_RDWR); |
| else |
| fprintf(stderr, "devnode already opened!\n"); |
| |
| return dnode; |
| break; |
| case DHD_DLDN_WRITE: |
| if (dnode > 0) |
| return write(dnode, buf, len); |
| break; |
| case DHD_DLDN_END: |
| if (dnode > 0) |
| return close(dnode); |
| break; |
| default: |
| return wl_set(dhd, cmd, buf, len); |
| |
| } |
| |
| return -1; |
| } |
| |
| /* Verify the wl adapter found. |
| * This is called by dhd_find to check if it supports wl |
| * The reason for checking wl adapter is that we can still send remote dhd commands over |
| * wifi transport. The function is copied from wlu.c. |
| */ |
| int |
| wl_check(void *wl) |
| { |
| int ret; |
| int val = 0; |
| |
| if (!dhd_check (wl)) |
| return 0; |
| |
| /* |
| * If dhd_check() fails then go for a regular wl driver verification |
| */ |
| if ((ret = wl_get(wl, WLC_GET_MAGIC, &val, sizeof(int))) < 0) |
| return ret; |
| if (val != WLC_IOCTL_MAGIC) |
| return BCME_ERROR; |
| if ((ret = wl_get(wl, WLC_GET_VERSION, &val, sizeof(int))) < 0) |
| return ret; |
| if (val > WLC_IOCTL_VERSION) { |
| fprintf(stderr, "Version mismatch, please upgrade\n"); |
| return BCME_ERROR; |
| } |
| return 0; |
| } |
| /* Search and verify the request type of adapter (wl or dhd) |
| * This is called by main before executing local dhd commands |
| * or sending remote dhd commands over wifi transport |
| */ |
| void |
| dhd_find(struct ifreq *ifr, char *type) |
| { |
| char proc_net_dev[] = "/proc/net/dev"; |
| FILE *fp; |
| static char buf[400]; |
| char *c, *name; |
| char dev_type[32]; |
| |
| ifr->ifr_name[0] = '\0'; |
| /* eat first two lines */ |
| if (!(fp = fopen(proc_net_dev, "r")) || |
| !fgets(buf, sizeof(buf), fp) || |
| !fgets(buf, sizeof(buf), 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); |
| if (dhd_get_dev_type(name, dev_type, type) >= 0 && |
| !strncmp(dev_type, type, strlen(dev_type) - 1)) |
| { |
| if (!wl_check((void*)ifr)) |
| break; |
| } |
| ifr->ifr_name[0] = '\0'; |
| } |
| |
| fclose(fp); |
| } |
| /* This function is called by wl_get to execute either local dhd command |
| * or send a dhd command over wl transport |
| */ |
| static int |
| ioctl_queryinformation_fe(void *wl, int cmd, void* input_buf, int *input_len) |
| { |
| return dhd_ioctl(wl, cmd, input_buf, *input_len, FALSE); |
| } |
| |
| /* This function is called by wl_set to execute either local dhd command |
| * or send a dhd command over wl transport |
| */ |
| static int |
| ioctl_setinformation_fe(void *wl, int cmd, void* buf, int *len) |
| { |
| return dhd_ioctl(wl, cmd, buf, *len, TRUE); |
| } |
| |
| /* The function is replica of wl_get in wlu_linux.c. Optimize when we have some |
| * common code between wlu_linux.c and dhdu_linux.c |
| */ |
| int |
| wl_get(void *wl, int cmd, void *buf, int len) |
| { |
| int error = BCME_OK; |
| error = (int)ioctl_queryinformation_fe(wl, cmd, buf, &len); |
| |
| if (error != 0) |
| return IOCTL_ERROR; |
| |
| return error; |
| } |
| |
| /* The function is replica of wl_set in wlu_linux.c. Optimize when we have some |
| * common code between wlu_linux.c and dhdu_linux.c |
| */ |
| int |
| wl_set(void *wl, int cmd, void *buf, int len) |
| { |
| int error = BCME_OK; |
| |
| error = (int)ioctl_setinformation_fe(wl, cmd, buf, &len); |
| |
| if (error != 0) { |
| return IOCTL_ERROR; |
| } |
| return error; |
| } |
| /* Main client function |
| * The code is mostly from wlu_linux.c. This function takes care of executing remote dhd commands |
| * along with the local dhd commands now. |
| */ |
| int |
| main(int argc, char **argv) |
| { |
| struct ifreq ifr; |
| char *ifname = NULL; |
| int err = 0; |
| int help = 0; |
| int status = CMD_DHD; |
| UNUSED_PARAMETER(argc); |
| |
| av0 = dhdu_av0 = argv[0]; |
| memset(&ifr, 0, sizeof(ifr)); |
| argv++; |
| |
| if ((status = dhd_option(&argv, &ifname, &help)) == CMD_OPT) { |
| if (ifname) |
| strncpy(ifr.ifr_name, ifname, IFNAMSIZ); |
| } |
| |
| err = process_args(&ifr, argv); |
| |
| return err; |
| } |
| /* |
| * Function called for 'local' execution and for 'remote' non-interactive session |
| * (shell cmd, wl cmd) .The code is mostly from wlu_linux.c. This code can be |
| * common to wlu_linux.c and dhdu_linux.c |
| */ |
| static int |
| process_args(struct ifreq* ifr, char **argv) |
| { |
| char *ifname = NULL; |
| int help = 0; |
| int status = 0; |
| int err = BCME_OK; |
| cmd_t *cmd = NULL; |
| while (*argv) { |
| if ((status = dhd_option(&argv, &ifname, &help)) == CMD_OPT) { |
| if (help) |
| break; |
| if (ifname) |
| strncpy(ifr->ifr_name, ifname, IFNAMSIZ); |
| continue; |
| } |
| /* parse error */ |
| else if (status == CMD_ERR) |
| break; |
| |
| /* use default interface */ |
| if (!ifr->ifr_name[0]) |
| dhd_find(ifr, "dhd"); |
| /* validate the interface */ |
| if (!ifr->ifr_name[0] || dhd_check((void *)ifr)) { |
| if (strcmp("dldn", *argv) != 0) { |
| fprintf(stderr, "%s: dhd driver adapter not found\n", av0); |
| exit(BCME_ERROR); |
| } |
| } |
| |
| /* search for command */ |
| cmd = dhd_find_cmd(*argv); |
| /* if not found, use default set_var and get_var commands */ |
| if (!cmd) { |
| cmd = &dhd_varcmd; |
| } |
| |
| /* do command */ |
| err = (*cmd->func)((void *) ifr, cmd, argv); |
| break; |
| } /* while loop end */ |
| |
| /* provide for help on a particular command */ |
| if (help && *argv) { |
| cmd = dhd_find_cmd(*argv); |
| if (cmd) { |
| dhd_cmd_usage(cmd); |
| } else { |
| printf("%s: Unrecognized command \"%s\", type -h for help\n", |
| av0, *argv); |
| } |
| } else if (!cmd) |
| dhd_usage(NULL); |
| else if (err == USAGE_ERROR) |
| dhd_cmd_usage(cmd); |
| else if (err == IOCTL_ERROR) |
| dhd_printlasterror((void *) ifr); |
| |
| return err; |
| } |
| |
| int |
| rwl_shell_createproc(void *wl) |
| { |
| UNUSED_PARAMETER(wl); |
| return fork(); |
| } |
| |
| void |
| rwl_shell_killproc(int pid) |
| { |
| kill(pid, SIGKILL); |
| } |
| |