1/*
2 * Linux port of dhd command line utility, hacked from wl utility.
3 *
4 * Copyright (C) 1999-2013, 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 378962 2013-01-15 13:18:28Z $
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>
41typedef u_int64_t u64;
42typedef u_int32_t u32;
43typedef u_int16_t u16;
44typedef 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
57#if defined(RWL_WIFI) || defined(RWL_SOCKET) ||defined(RWL_SERIAL)
58#define RWL_ENABLE
59#endif
60
61#include "dhdu.h"
62#ifdef RWL_ENABLE
63#include "wlu_remote.h"
64#include "wlu_client_shared.h"
65#include "wlu_pipe.h"
66#endif /* RWL_ENABLE */
67#include <netdb.h>
68#include <netinet/in.h>
69#include <dhdioctl.h>
70#include "dhdu_common.h"
71#include "dhdu_nl80211.h"
72
73char *av0;
74static int rwl_os_type = LINUX_OS;
75/* Search the dhd_cmds table for a matching command name.
76 * Return the matching command or NULL if no match found.
77 */
78static cmd_t *
79dhd_find_cmd(char* name)
80{
81	cmd_t *cmd = NULL;
82	/* search the dhd_cmds for a matching name */
83	for (cmd = dhd_cmds; cmd->name && strcmp(cmd->name, name); cmd++);
84	if (cmd->name == NULL)
85		cmd = NULL;
86	return cmd;
87}
88
89static void
90syserr(const char *s)
91{
92	fprintf(stderr, "%s: ", av0);
93	perror(s);
94	exit(errno);
95}
96
97#ifdef NL80211
98static int __dhd_driver_io(void *dhd, dhd_ioctl_t *ioc)
99{
100	struct dhd_netlink_info dhd_nli;
101	struct ifreq *ifr = (struct ifreq *)dhd;
102	int ret = 0;
103
104	dhd_nli.ifidx = if_nametoindex(ifr->ifr_name);
105	if (!dhd_nli.ifidx) {
106		fprintf(stderr, "invalid device %s\n", ifr->ifr_name);
107		return BCME_IOCTL_ERROR;
108	}
109
110	if (dhd_nl_sock_connect(&dhd_nli) < 0)
111		syserr("socket");
112
113	ret = dhd_nl_do_testmode(&dhd_nli, ioc);
114	dhd_nl_sock_disconnect(&dhd_nli);
115	return ret;
116}
117#else
118static int __dhd_driver_io(void *dhd, dhd_ioctl_t *ioc)
119{
120	struct ifreq *ifr = (struct ifreq *)dhd;
121	int s;
122	int ret = 0;
123
124	/* pass ioctl data */
125	ifr->ifr_data = (caddr_t)ioc;
126
127	/* open socket to kernel */
128	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
129		syserr("socket");
130
131	ret = ioctl(s, SIOCDEVPRIVATE, ifr);
132	if (ret < 0 && errno != EAGAIN)
133		syserr(__FUNCTION__);
134
135	/* cleanup */
136	close(s);
137	return ret;
138}
139#endif /* NL80211 */
140
141/* This function is called by ioctl_setinformation_fe or ioctl_queryinformation_fe
142 * for executing  remote commands or local commands
143 */
144static int
145dhd_ioctl(void *dhd, int cmd, void *buf, int len, bool set)
146{
147	dhd_ioctl_t ioc;
148	int ret = 0;
149
150	/* By default try to execute wl commands */
151	int driver_magic = WLC_IOCTL_MAGIC;
152	int get_magic = WLC_GET_MAGIC;
153
154	/* For local dhd commands execute dhd. For wifi transport we still
155	 * execute wl commands.
156	 */
157	if (remote_type == NO_REMOTE && strncmp (buf, RWL_WIFI_ACTION_CMD,
158		strlen(RWL_WIFI_ACTION_CMD)) && strncmp(buf, RWL_WIFI_GET_ACTION_CMD,
159		strlen(RWL_WIFI_GET_ACTION_CMD))) {
160		driver_magic = DHD_IOCTL_MAGIC;
161		get_magic = DHD_GET_MAGIC;
162	}
163
164	/* do it */
165	ioc.cmd = cmd;
166	ioc.buf = buf;
167	ioc.len = len;
168	ioc.set = set;
169	ioc.driver = driver_magic;
170
171	ret = __dhd_driver_io(dhd, &ioc);
172	if (ret < 0 && cmd != get_magic)
173		ret = BCME_IOCTL_ERROR;
174	return ret;
175}
176
177/* This function is called in wlu_pipe.c remote_wifi_ser_init() to execute
178 * the initial set of wl commands for wifi transport (e.g slow_timer, fast_timer etc)
179 */
180int wl_ioctl(void *wl, int cmd, void *buf, int len, bool set)
181{
182	return dhd_ioctl(wl, cmd, buf, len, set); /* Call actual wl_ioctl here: Shubhro */
183}
184
185/* Search if dhd adapter or wl adapter is present
186 * This is called by dhd_find to check if it supports wl or dhd
187 * The reason for checking wl adapter is that we can still send remote dhd commands over
188 * wifi transport.
189 */
190static int
191dhd_get_dev_type(char *name, void *buf, char *type)
192{
193	int s;
194	int ret;
195	struct ifreq ifr;
196	struct ethtool_drvinfo info;
197
198	/* open socket to kernel */
199	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
200		syserr("socket");
201
202	/* get device type */
203	memset(&info, 0, sizeof(info));
204	info.cmd = ETHTOOL_GDRVINFO;
205	strcpy(info.driver, "?");
206	strcat(info.driver, type);
207	ifr.ifr_data = (caddr_t)&info;
208	strncpy(ifr.ifr_name, name, IFNAMSIZ);
209	if ((ret = ioctl(s, SIOCETHTOOL, &ifr)) < 0) {
210
211		if (errno != EAGAIN)
212			syserr(__FUNCTION__);
213
214		*(char *)buf = '\0';
215	}
216	else
217		strcpy(buf, info.driver);
218
219	close(s);
220	return ret;
221}
222
223/* dhd_get/dhd_set is called by several functions in dhdu.c. This used to call dhd_ioctl
224 * directly. However now we need to execute the dhd commands remotely.
225 * So we make use of wl pipes to execute this.
226 * wl_get or wl_set functions also check if it is a local command hence they in turn
227 * call dhd_ioctl if required. Name wl_get/wl_set is retained because these functions are
228 * also called by wlu_pipe.c wlu_client_shared.c
229 */
230int
231dhd_get(void *dhd, int cmd, void *buf, int len)
232{
233	return wl_get(dhd, cmd, buf, len);
234}
235
236/*
237 * To use /dev/node interface:
238 *   1.  mknod /dev/hnd0 c 248 0
239 *   2.  chmod 777 /dev/hnd0
240 */
241#define NODE "/dev/hnd0"
242
243int
244dhd_set(void *dhd, int cmd, void *buf, int len)
245{
246	static int dnode = -1;
247
248	switch (cmd) {
249	case DHD_DLDN_ST:
250		if (dnode == -1)
251			dnode = open(NODE, O_RDWR);
252		else
253			fprintf(stderr, "devnode already opened!\n");
254
255		return dnode;
256		break;
257	case DHD_DLDN_WRITE:
258		if (dnode > 0)
259			return write(dnode, buf, len);
260		break;
261	case DHD_DLDN_END:
262		if (dnode > 0)
263			return close(dnode);
264		break;
265	default:
266		return wl_set(dhd, cmd, buf, len);
267
268	}
269
270	return -1;
271}
272
273/* Verify the wl adapter found.
274 * This is called by dhd_find to check if it supports wl
275 * The reason for checking wl adapter is that we can still send remote dhd commands over
276 * wifi transport. The function is copied from wlu.c.
277 */
278int
279wl_check(void *wl)
280{
281	int ret;
282	int val = 0;
283
284	if (!dhd_check (wl))
285		return 0;
286
287	/*
288	 *  If dhd_check() fails then go for a regular wl driver verification
289	 */
290	if ((ret = wl_get(wl, WLC_GET_MAGIC, &val, sizeof(int))) < 0)
291		return ret;
292	if (val != WLC_IOCTL_MAGIC)
293		return BCME_ERROR;
294	if ((ret = wl_get(wl, WLC_GET_VERSION, &val, sizeof(int))) < 0)
295		return ret;
296	if (val > WLC_IOCTL_VERSION) {
297		fprintf(stderr, "Version mismatch, please upgrade\n");
298		return BCME_ERROR;
299	}
300	return 0;
301}
302/* Search and verify the request type of adapter (wl or dhd)
303 * This is called by main before executing local dhd commands
304 * or sending remote dhd commands over wifi transport
305 */
306void
307dhd_find(struct ifreq *ifr, char *type)
308{
309	char proc_net_dev[] = "/proc/net/dev";
310	FILE *fp;
311	static char buf[400];
312	char *c, *name;
313	char dev_type[32];
314
315	ifr->ifr_name[0] = '\0';
316	/* eat first two lines */
317	if (!(fp = fopen(proc_net_dev, "r")) ||
318	    !fgets(buf, sizeof(buf), fp) ||
319	    !fgets(buf, sizeof(buf), fp))
320		return;
321
322	while (fgets(buf, sizeof(buf), fp)) {
323		c = buf;
324		while (isspace(*c))
325			c++;
326		if (!(name = strsep(&c, ":")))
327			continue;
328		strncpy(ifr->ifr_name, name, IFNAMSIZ);
329		if (dhd_get_dev_type(name, dev_type, type) >= 0 &&
330			!strncmp(dev_type, type, strlen(dev_type) - 1))
331		{
332			if (!wl_check((void*)ifr))
333				break;
334		}
335		ifr->ifr_name[0] = '\0';
336	}
337
338	fclose(fp);
339}
340/* This function is called by wl_get to execute either local dhd command
341 * or send a dhd command over wl transport
342 */
343static int
344ioctl_queryinformation_fe(void *wl, int cmd, void* input_buf, int *input_len)
345{
346	if (remote_type == NO_REMOTE) {
347		return dhd_ioctl(wl, cmd, input_buf, *input_len, FALSE);
348	}
349#ifdef RWL_ENABLE
350	else {
351		return rwl_queryinformation_fe(wl, cmd, input_buf,
352			(unsigned long*)input_len, 0, RDHD_GET_IOCTL);
353	}
354#else /* RWL_ENABLE */
355	return BCME_IOCTL_ERROR;
356#endif /* RWL_ENABLE */
357}
358
359/* This function is called by wl_set to execute either local dhd command
360 * or send a dhd command over wl transport
361 */
362static int
363ioctl_setinformation_fe(void *wl, int cmd, void* buf, int *len)
364{
365	if (remote_type == NO_REMOTE) {
366		return dhd_ioctl(wl,  cmd, buf, *len, TRUE);
367	}
368#ifdef RWL_ENABLE
369	else {
370		return rwl_setinformation_fe(wl, cmd, buf, (unsigned long*)len, 0, RDHD_SET_IOCTL);
371
372	}
373#else /* RWL_ENABLE */
374	return BCME_IOCTL_ERROR;
375#endif /* RWL_ENABLE */
376}
377
378/* The function is replica of wl_get in wlu_linux.c. Optimize when we have some
379 * common code between wlu_linux.c and dhdu_linux.c
380 */
381int
382wl_get(void *wl, int cmd, void *buf, int len)
383{
384	int error = BCME_OK;
385	/* For RWL: When interfacing to a Windows client, need t add in OID_BASE */
386	if ((rwl_os_type == WIN32_OS) && (remote_type != NO_REMOTE)) {
387		error = (int)ioctl_queryinformation_fe(wl, WL_OID_BASE + cmd, buf, &len);
388	} else {
389		error = (int)ioctl_queryinformation_fe(wl, cmd, buf, &len);
390	}
391	if (error == BCME_SERIAL_PORT_ERR)
392		return BCME_SERIAL_PORT_ERR;
393
394	if (error != 0)
395		return BCME_IOCTL_ERROR;
396
397	return error;
398}
399
400/* The function is replica of wl_set in wlu_linux.c. Optimize when we have some
401 * common code between wlu_linux.c and dhdu_linux.c
402 */
403int
404wl_set(void *wl, int cmd, void *buf, int len)
405{
406	int error = BCME_OK;
407
408	/* For RWL: When interfacing to a Windows client, need t add in OID_BASE */
409	if ((rwl_os_type == WIN32_OS) && (remote_type != NO_REMOTE)) {
410		error = (int)ioctl_setinformation_fe(wl, WL_OID_BASE + cmd, buf, &len);
411	} else {
412		error = (int)ioctl_setinformation_fe(wl, cmd, buf, &len);
413	}
414
415	if (error == BCME_SERIAL_PORT_ERR)
416		return BCME_SERIAL_PORT_ERR;
417
418	if (error != 0) {
419		return BCME_IOCTL_ERROR;
420	}
421	return error;
422}
423
424int
425wl_validatedev(void *dev_handle)
426{
427	int retval = 1;
428	struct ifreq *ifr = (struct ifreq *)dev_handle;
429	/* validate the interface */
430	if (!ifr->ifr_name || wl_check((void *)ifr)) {
431		retval = 0;
432	}
433	return retval;
434}
435
436/* Main client function
437 * The code is mostly from wlu_linux.c. This function takes care of executing remote dhd commands
438 * along with the local dhd commands now.
439 */
440int
441main(int argc, char **argv)
442{
443	struct ifreq ifr;
444	char *ifname = NULL;
445	int err = 0;
446	int help = 0;
447	int status = CMD_DHD;
448#ifdef RWL_SOCKET
449	struct ipv4_addr temp;
450#endif /* RWL_SOCKET */
451
452	UNUSED_PARAMETER(argc);
453
454	av0 = argv[0];
455	memset(&ifr, 0, sizeof(ifr));
456	argv++;
457
458	if ((status = dhd_option(&argv, &ifname, &help)) == CMD_OPT) {
459		if (ifname)
460			strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
461	}
462	/* Linux client looking for a Win32 server */
463	if (*argv && strncmp (*argv, "--wince", strlen(*argv)) == 0) {
464		rwl_os_type = WIN32_OS;
465		argv++;
466	}
467
468	/* RWL socket transport Usage: --socket ipaddr [port num] */
469	if (*argv && strncmp (*argv, "--socket", strlen(*argv)) == 0) {
470		argv++;
471
472		remote_type = REMOTE_SOCKET;
473#ifdef RWL_SOCKET
474		if (!(*argv)) {
475			rwl_usage(remote_type);
476			return err;
477		}
478
479		if (!dhd_atoip(*argv, &temp)) {
480			rwl_usage(remote_type);
481			return err;
482		}
483		g_rwl_servIP = *argv;
484		argv++;
485
486		g_rwl_servport = DEFAULT_SERVER_PORT;
487		if ((*argv) && isdigit(**argv)) {
488			g_rwl_servport = atoi(*argv);
489			argv++;
490		}
491#endif /* RWL_SOCKET */
492	}
493
494	/* RWL from system serial port on client to uart dongle port on server */
495	/* Usage: --dongle /dev/ttyS0 */
496	if (*argv && strncmp (*argv, "--dongle", strlen(*argv)) == 0) {
497		argv++;
498		remote_type = REMOTE_DONGLE;
499	}
500
501	/* RWL over wifi.  Usage: --wifi mac_address */
502	if (*argv && strncmp (*argv, "--wifi", strlen(*argv)) == 0) {
503		argv++;
504#ifdef RWL_WIFI
505		remote_type = NO_REMOTE;
506		if (!ifr.ifr_name[0])
507		{
508			dhd_find(&ifr, "wl");
509		}
510		/* validate the interface */
511		if (!ifr.ifr_name[0] || wl_check((void*)&ifr)) {
512			fprintf(stderr, "%s: wl driver adapter not found\n", av0);
513			exit(1);
514		}
515		remote_type = REMOTE_WIFI;
516
517		if (argc < 4) {
518			rwl_usage(remote_type);
519			return err;
520		}
521		/* copy server mac address to local buffer for later use by findserver cmd */
522		if (!dhd_ether_atoe(*argv, (struct ether_addr *)g_rwl_buf_mac)) {
523			fprintf(stderr,
524			        "could not parse as an ethernet MAC address\n");
525			return FAIL;
526		}
527		argv++;
528#else /* RWL_WIFI */
529		remote_type = REMOTE_WIFI;
530#endif /* RWL_WIFI */
531	}
532
533	/* Process for local dhd */
534	if (remote_type == NO_REMOTE) {
535		err = process_args(&ifr, argv);
536		return err;
537	}
538
539#ifdef RWL_ENABLE
540	if (*argv) {
541		err = process_args(&ifr, argv);
542		if ((err == BCME_SERIAL_PORT_ERR) && (remote_type == REMOTE_DONGLE)) {
543			DPRINT_ERR(ERR, "\n Retry again\n");
544			err = process_args((struct ifreq*)&ifr, argv);
545		}
546		return err;
547	}
548	rwl_usage(remote_type);
549#endif /* RWL_ENABLE */
550
551	return err;
552}
553/*
554 * Function called for  'local' execution and for 'remote' non-interactive session
555 * (shell cmd, wl cmd) .The code is mostly from wlu_linux.c. This code can be
556 * common to wlu_linux.c and dhdu_linux.c
557 */
558static int
559process_args(struct ifreq* ifr, char **argv)
560{
561	char *ifname = NULL;
562	int help = 0;
563	int status = 0;
564	int err = BCME_OK;
565	cmd_t *cmd = NULL;
566	while (*argv) {
567#ifdef RWL_ENABLE
568		if ((strcmp (*argv, "sh") == 0) && (remote_type != NO_REMOTE)) {
569			argv++; /* Get the shell command */
570			if (*argv) {
571				/* Register handler in case of shell command only */
572				signal(SIGINT, ctrlc_handler);
573				err = rwl_shell_cmd_proc((void*)ifr, argv, SHELL_CMD);
574			} else {
575				DPRINT_ERR(ERR,
576				"Enter the shell command (e.g ls(Linux) or dir(Win CE) \n");
577				err = BCME_ERROR;
578			}
579			return err;
580		}
581#endif /* RWL_ENABLE */
582		if ((status = dhd_option(&argv, &ifname, &help)) == CMD_OPT) {
583			if (help)
584				break;
585			if (ifname)
586				strncpy(ifr->ifr_name, ifname, IFNAMSIZ);
587			continue;
588		}
589		/* parse error */
590		else if (status == CMD_ERR)
591		    break;
592
593		if (remote_type == NO_REMOTE) {
594			int ret;
595
596			/* use default interface */
597			if (!ifr->ifr_name[0])
598				dhd_find(ifr, "dhd");
599			/* validate the interface */
600			if (!ifr->ifr_name[0]) {
601				if (strcmp("dldn", *argv) != 0) {
602					exit(ENXIO);
603					syserr("interface");
604				}
605			}
606			if ((ret = dhd_check((void *)ifr)) != 0) {
607				if (strcmp("dldn", *argv) != 0) {
608					errno = -ret;
609					syserr("dhd_check");
610				}
611			}
612		}
613		/* search for command */
614		cmd = dhd_find_cmd(*argv);
615		/* if not found, use default set_var and get_var commands */
616		if (!cmd) {
617			cmd = &dhd_varcmd;
618		}
619
620		/* do command */
621		err = (*cmd->func)((void *) ifr, cmd, argv);
622		break;
623	} /* while loop end */
624
625	/* provide for help on a particular command */
626	if (help && *argv) {
627		cmd = dhd_find_cmd(*argv);
628		if (cmd) {
629			dhd_cmd_usage(cmd);
630		} else {
631			DPRINT_ERR(ERR, "%s: Unrecognized command \"%s\", type -h for help\n",
632			           av0, *argv);
633		}
634	} else if (!cmd)
635		dhd_usage(NULL);
636	else if (err == BCME_USAGE_ERROR)
637		dhd_cmd_usage(cmd);
638	else if (err == BCME_IOCTL_ERROR)
639		dhd_printlasterror((void *) ifr);
640
641	return err;
642}
643
644int
645rwl_shell_createproc(void *wl)
646{
647	UNUSED_PARAMETER(wl);
648	return fork();
649}
650
651void
652rwl_shell_killproc(int pid)
653{
654	kill(pid, SIGKILL);
655}
656
657#ifdef RWL_SOCKET
658/* validate hostname/ip given by the client */
659int
660validate_server_address()
661{
662	struct hostent *he;
663	struct ipv4_addr temp;
664
665	if (!dhd_atoip(g_rwl_servIP, &temp)) {
666	/* Wrong IP address format check for hostname */
667		if ((he = gethostbyname(g_rwl_servIP)) != NULL) {
668			if (!dhd_atoip(*he->h_addr_list, &temp)) {
669				g_rwl_servIP = inet_ntoa(*(struct in_addr *)*he->h_addr_list);
670				if (g_rwl_servIP == NULL) {
671					DPRINT_ERR(ERR, "Error at inet_ntoa \n");
672					return FAIL;
673				}
674			} else {
675				DPRINT_ERR(ERR, "Error in IP address \n");
676				return FAIL;
677			}
678		} else {
679			DPRINT_ERR(ERR, "Enter correct IP address/hostname format\n");
680			return FAIL;
681		}
682	}
683	return SUCCESS;
684}
685#endif /* RWL_SOCKET */
686