• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 *
dhd_find_cmd(char * name)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
syserr(char * s)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
dhd_ioctl(void * dhd,int cmd,void * buf,int len,bool set)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  */
wl_ioctl(void * wl,int cmd,void * buf,int len,bool set)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
dhd_get_dev_type(char * name,void * buf,char * type)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
dhd_get(void * dhd,int cmd,void * buf,int len)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
dhd_set(void * dhd,int cmd,void * buf,int len)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
wl_check(void * wl)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
dhd_find(struct ifreq * ifr,char * type)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
ioctl_queryinformation_fe(void * wl,int cmd,void * input_buf,int * input_len)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
ioctl_setinformation_fe(void * wl,int cmd,void * buf,int * len)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
wl_get(void * wl,int cmd,void * buf,int len)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
wl_set(void * wl,int cmd,void * buf,int len)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
main(int argc,char ** argv)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
process_args(struct ifreq * ifr,char ** argv)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
rwl_shell_createproc(void * wl)431 rwl_shell_createproc(void *wl)
432 {
433 	UNUSED_PARAMETER(wl);
434 	return fork();
435 }
436 
437 void
rwl_shell_killproc(int pid)438 rwl_shell_killproc(int pid)
439 {
440 	kill(pid, SIGKILL);
441 }
442 
443