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