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