Home | History | Annotate | Download | only in dhdutil
      1 /*
      2  * Linux port of dhd command line utility.
      3  *
      4  * Copyright (C) 1999-2011, 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,v 1.16.2.2 2010-12-16 08:12:11 Exp $
     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 #include "dhdu.h"
     57 #include <netdb.h>
     58 #include <dhdioctl.h>
     59 #include "dhdu_common.h"
     60 
     61 extern int wl_get(void *wl, int cmd, void *buf, int len);
     62 extern int wl_set(void *wl, int cmd, void *buf, int len);
     63 
     64 char *av0;
     65 /* Search the dhd_cmds table for a matching command name.
     66  * Return the matching command or NULL if no match found.
     67  */
     68 static cmd_t *
     69 dhd_find_cmd(char* name)
     70 {
     71 	cmd_t *cmd = NULL;
     72 	/* search the dhd_cmds for a matching name */
     73 	for (cmd = dhd_cmds; cmd->name && strcmp(cmd->name, name); cmd++);
     74 	if (cmd->name == NULL)
     75 		cmd = NULL;
     76 	return cmd;
     77 }
     78 
     79 static void
     80 syserr(char *s)
     81 {
     82 	fprintf(stderr, "%s: ", dhdu_av0);
     83 	perror(s);
     84 	exit(errno);
     85 }
     86 
     87 /* This function is called by ioctl_setinformation_fe or ioctl_queryinformation_fe
     88  * for executing  remote commands or local commands
     89  */
     90 static int
     91 dhd_ioctl(void *dhd, int cmd, void *buf, int len, bool set)
     92 {
     93 	struct ifreq *ifr = (struct ifreq *)dhd;
     94 	dhd_ioctl_t ioc;
     95 	int ret = 0;
     96 	int s;
     97 	/* By default try to execute wl commands */
     98 	int driver_magic = DHD_IOCTL_MAGIC;
     99 	int get_magic = DHD_GET_MAGIC;
    100 
    101 	/* open socket to kernel */
    102 	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    103 		syserr("socket");
    104 
    105 	/* do it */
    106 	ioc.cmd = cmd;
    107 	ioc.buf = buf;
    108 	ioc.len = len;
    109 	ioc.set = set;
    110 	ioc.driver = driver_magic;
    111 	ifr->ifr_data = (caddr_t) &ioc;
    112 
    113 	if ((ret = ioctl(s, SIOCDEVPRIVATE, ifr)) < 0) {
    114 		if (cmd != get_magic) {
    115 			ret = IOCTL_ERROR;
    116 		}
    117 	}
    118 
    119 	/* cleanup */
    120 	close(s);
    121 	return ret;
    122 }
    123 
    124 /* This function is called in wlu_pipe.c remote_wifi_ser_init() to execute
    125  * the initial set of wl commands for wifi transport (e.g slow_timer, fast_timer etc)
    126  */
    127 int wl_ioctl(void *wl, int cmd, void *buf, int len, bool set)
    128 {
    129 	return dhd_ioctl(wl, cmd, buf, len, set); /* Call actual wl_ioctl here: Shubhro */
    130 }
    131 
    132 /* Search if dhd adapter or wl adapter is present
    133  * This is called by dhd_find to check if it supports wl or dhd
    134  * The reason for checking wl adapter is that we can still send remote dhd commands over
    135  * wifi transport.
    136  */
    137 static int
    138 dhd_get_dev_type(char *name, void *buf, char *type)
    139 {
    140 	int s;
    141 	int ret;
    142 	struct ifreq ifr;
    143 	struct ethtool_drvinfo info;
    144 
    145 	/* open socket to kernel */
    146 	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    147 		syserr("socket");
    148 
    149 	/* get device type */
    150 	memset(&info, 0, sizeof(info));
    151 	info.cmd = ETHTOOL_GDRVINFO;
    152 	strcpy(info.driver, "?");
    153 	strcat(info.driver, type);
    154 	ifr.ifr_data = (caddr_t)&info;
    155 	strncpy(ifr.ifr_name, name, IFNAMSIZ);
    156 	if ((ret = ioctl(s, SIOCETHTOOL, &ifr)) < 0) {
    157 
    158 		/* print a good diagnostic if not superuser */
    159 		if (errno == EPERM)
    160 			syserr("dhd_get_dev_type");
    161 
    162 		*(char *)buf = '\0';
    163 	}
    164 	else
    165 		strcpy(buf, info.driver);
    166 
    167 	close(s);
    168 	return ret;
    169 }
    170 
    171 /* dhd_get/dhd_set is called by several functions in dhdu.c. This used to call dhd_ioctl
    172  * directly. However now we need to execute the dhd commands remotely.
    173  * So we make use of wl pipes to execute this.
    174  * wl_get or wl_set functions also check if it is a local command hence they in turn
    175  * call dhd_ioctl if required. Name wl_get/wl_set is retained because these functions are
    176  * also called by wlu_pipe.c wlu_client_shared.c
    177  */
    178 int
    179 dhd_get(void *dhd, int cmd, void *buf, int len)
    180 {
    181 	return wl_get(dhd, cmd, buf, len);
    182 }
    183 
    184 /*
    185  * To use /dev/node interface:
    186  *   1.  mknod /dev/hnd0 c 248 0
    187  *   2.  chmod 777 /dev/hnd0
    188  */
    189 #define NODE "/dev/hnd0"
    190 
    191 int
    192 dhd_set(void *dhd, int cmd, void *buf, int len)
    193 {
    194 	static int dnode = -1;
    195 
    196 	switch (cmd) {
    197 	case DHD_DLDN_ST:
    198 		if (dnode == -1)
    199 			dnode = open(NODE, O_RDWR);
    200 		else
    201 			fprintf(stderr, "devnode already opened!\n");
    202 
    203 		return dnode;
    204 		break;
    205 	case DHD_DLDN_WRITE:
    206 		if (dnode > 0)
    207 			return write(dnode, buf, len);
    208 		break;
    209 	case DHD_DLDN_END:
    210 		if (dnode > 0)
    211 			return close(dnode);
    212 		break;
    213 	default:
    214 		return wl_set(dhd, cmd, buf, len);
    215 
    216 	}
    217 
    218 	return -1;
    219 }
    220 
    221 /* Verify the wl adapter found.
    222  * This is called by dhd_find to check if it supports wl
    223  * The reason for checking wl adapter is that we can still send remote dhd commands over
    224  * wifi transport. The function is copied from wlu.c.
    225  */
    226 int
    227 wl_check(void *wl)
    228 {
    229 	int ret;
    230 	int val = 0;
    231 
    232 	if (!dhd_check (wl))
    233 		return 0;
    234 
    235 	/*
    236 	 *  If dhd_check() fails then go for a regular wl driver verification
    237 	 */
    238 	if ((ret = wl_get(wl, WLC_GET_MAGIC, &val, sizeof(int))) < 0)
    239 		return ret;
    240 	if (val != WLC_IOCTL_MAGIC)
    241 		return BCME_ERROR;
    242 	if ((ret = wl_get(wl, WLC_GET_VERSION, &val, sizeof(int))) < 0)
    243 		return ret;
    244 	if (val > WLC_IOCTL_VERSION) {
    245 		fprintf(stderr, "Version mismatch, please upgrade\n");
    246 		return BCME_ERROR;
    247 	}
    248 	return 0;
    249 }
    250 /* Search and verify the request type of adapter (wl or dhd)
    251  * This is called by main before executing local dhd commands
    252  * or sending remote dhd commands over wifi transport
    253  */
    254 void
    255 dhd_find(struct ifreq *ifr, char *type)
    256 {
    257 	char proc_net_dev[] = "/proc/net/dev";
    258 	FILE *fp;
    259 	static char buf[400];
    260 	char *c, *name;
    261 	char dev_type[32];
    262 
    263 	ifr->ifr_name[0] = '\0';
    264 	/* eat first two lines */
    265 	if (!(fp = fopen(proc_net_dev, "r")) ||
    266 	    !fgets(buf, sizeof(buf), fp) ||
    267 	    !fgets(buf, sizeof(buf), fp))
    268 		return;
    269 
    270 	while (fgets(buf, sizeof(buf), fp)) {
    271 		c = buf;
    272 		while (isspace(*c))
    273 			c++;
    274 		if (!(name = strsep(&c, ":")))
    275 			continue;
    276 		strncpy(ifr->ifr_name, name, IFNAMSIZ);
    277 		if (dhd_get_dev_type(name, dev_type, type) >= 0 &&
    278 			!strncmp(dev_type, type, strlen(dev_type) - 1))
    279 		{
    280 			if (!wl_check((void*)ifr))
    281 				break;
    282 		}
    283 		ifr->ifr_name[0] = '\0';
    284 	}
    285 
    286 	fclose(fp);
    287 }
    288 /* This function is called by wl_get to execute either local dhd command
    289  * or send a dhd command over wl transport
    290  */
    291 static int
    292 ioctl_queryinformation_fe(void *wl, int cmd, void* input_buf, int *input_len)
    293 {
    294 	return dhd_ioctl(wl, cmd, input_buf, *input_len, FALSE);
    295 }
    296 
    297 /* This function is called by wl_set to execute either local dhd command
    298  * or send a dhd command over wl transport
    299  */
    300 static int
    301 ioctl_setinformation_fe(void *wl, int cmd, void* buf, int *len)
    302 {
    303 	return dhd_ioctl(wl,  cmd, buf, *len, TRUE);
    304 }
    305 
    306 /* The function is replica of wl_get in wlu_linux.c. Optimize when we have some
    307  * common code between wlu_linux.c and dhdu_linux.c
    308  */
    309 int
    310 wl_get(void *wl, int cmd, void *buf, int len)
    311 {
    312 	int error = BCME_OK;
    313 	error = (int)ioctl_queryinformation_fe(wl, cmd, buf, &len);
    314 
    315 	if (error != 0)
    316 		return IOCTL_ERROR;
    317 
    318 	return error;
    319 }
    320 
    321 /* The function is replica of wl_set in wlu_linux.c. Optimize when we have some
    322  * common code between wlu_linux.c and dhdu_linux.c
    323  */
    324 int
    325 wl_set(void *wl, int cmd, void *buf, int len)
    326 {
    327 	int error = BCME_OK;
    328 
    329 	error = (int)ioctl_setinformation_fe(wl, cmd, buf, &len);
    330 
    331 	if (error != 0) {
    332 		return IOCTL_ERROR;
    333 	}
    334 	return error;
    335 }
    336 /* Main client function
    337  * The code is mostly from wlu_linux.c. This function takes care of executing remote dhd commands
    338  * along with the local dhd commands now.
    339  */
    340 int
    341 main(int argc, char **argv)
    342 {
    343 	struct ifreq ifr;
    344 	char *ifname = NULL;
    345 	int err = 0;
    346 	int help = 0;
    347 	int status = CMD_DHD;
    348 	UNUSED_PARAMETER(argc);
    349 
    350 	av0 = dhdu_av0 = argv[0];
    351 	memset(&ifr, 0, sizeof(ifr));
    352 	argv++;
    353 
    354 	if ((status = dhd_option(&argv, &ifname, &help)) == CMD_OPT) {
    355 		if (ifname)
    356 			strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
    357 	}
    358 
    359 	err = process_args(&ifr, argv);
    360 
    361 	return err;
    362 }
    363 /*
    364  * Function called for  'local' execution and for 'remote' non-interactive session
    365  * (shell cmd, wl cmd) .The code is mostly from wlu_linux.c. This code can be
    366  * common to wlu_linux.c and dhdu_linux.c
    367  */
    368 static int
    369 process_args(struct ifreq* ifr, char **argv)
    370 {
    371 	char *ifname = NULL;
    372 	int help = 0;
    373 	int status = 0;
    374 	int err = BCME_OK;
    375 	cmd_t *cmd = NULL;
    376 	while (*argv) {
    377 		if ((status = dhd_option(&argv, &ifname, &help)) == CMD_OPT) {
    378 			if (help)
    379 				break;
    380 			if (ifname)
    381 				strncpy(ifr->ifr_name, ifname, IFNAMSIZ);
    382 			continue;
    383 		}
    384 		/* parse error */
    385 		else if (status == CMD_ERR)
    386 		    break;
    387 
    388 		/* use default interface */
    389 		if (!ifr->ifr_name[0])
    390 			dhd_find(ifr, "dhd");
    391 		/* validate the interface */
    392 		if (!ifr->ifr_name[0] || dhd_check((void *)ifr)) {
    393 		if (strcmp("dldn", *argv) != 0) {
    394 			fprintf(stderr, "%s: dhd driver adapter not found\n", av0);
    395 			exit(BCME_ERROR);
    396 			}
    397 		}
    398 
    399 		/* search for command */
    400 		cmd = dhd_find_cmd(*argv);
    401 		/* if not found, use default set_var and get_var commands */
    402 		if (!cmd) {
    403 			cmd = &dhd_varcmd;
    404 		}
    405 
    406 		/* do command */
    407 		err = (*cmd->func)((void *) ifr, cmd, argv);
    408 		break;
    409 	} /* while loop end */
    410 
    411 	/* provide for help on a particular command */
    412 	if (help && *argv) {
    413 		cmd = dhd_find_cmd(*argv);
    414 		if (cmd) {
    415 			dhd_cmd_usage(cmd);
    416 		} else {
    417 			printf("%s: Unrecognized command \"%s\", type -h for help\n",
    418 			           av0, *argv);
    419 		}
    420 	} else if (!cmd)
    421 		dhd_usage(NULL);
    422 	else if (err == USAGE_ERROR)
    423 		dhd_cmd_usage(cmd);
    424 	else if (err == IOCTL_ERROR)
    425 		dhd_printlasterror((void *) ifr);
    426 
    427 	return err;
    428 }
    429 
    430 int
    431 rwl_shell_createproc(void *wl)
    432 {
    433 	UNUSED_PARAMETER(wl);
    434 	return fork();
    435 }
    436 
    437 void
    438 rwl_shell_killproc(int pid)
    439 {
    440 	kill(pid, SIGKILL);
    441 }
    442 
    443