1/*
2 * Wireless network adapter utilities
3 *
4 * Copyright (C) 2014, Broadcom Corporation. All Rights Reserved.
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: wl.c 470880 2014-04-16 22:00:58Z $
19 */
20#include <typedefs.h>
21#include <string.h>
22#include <stdio.h>
23#include <unistd.h>
24#include <errno.h>
25#include <sys/ioctl.h>
26#if	defined(__ECOS)
27#include <sys/socket.h>
28#endif
29#include <net/if.h>
30
31#include <bcmutils.h>
32#include <wlutils.h>
33#include <bcmconfig.h>
34
35int
36wl_probe(char *name)
37{
38	int ret, val;
39
40#if defined(linux) || defined(__ECOS)
41	char buf[DEV_TYPE_LEN];
42	if ((ret = wl_get_dev_type(name, buf, DEV_TYPE_LEN)) < 0)
43		return ret;
44	/* Check interface */
45	if (strncmp(buf, "wl", 2))
46		return -1;
47#else
48	/* Check interface */
49	if ((ret = wl_ioctl(name, WLC_GET_MAGIC, &val, sizeof(val))))
50		return ret;
51#endif
52	if ((ret = wl_ioctl(name, WLC_GET_VERSION, &val, sizeof(val))))
53		return ret;
54	if (val > WLC_IOCTL_VERSION)
55		return -1;
56
57	return ret;
58}
59
60#ifdef __CONFIG_DHDAP__
61#include <dhdioctl.h>
62/*
63 * Probe the specified interface.
64 * @param	name	interface name
65 * @return	0       if using dhd driver
66 *          <0      otherwise
67 */
68int
69dhd_probe(char *name)
70{
71	int ret, val;
72	val = 0;
73	/* Check interface */
74	ret = dhd_ioctl(name, DHD_GET_MAGIC, &val, sizeof(val));
75	if (val == WLC_IOCTL_MAGIC) {
76		ret = 1; /* is_dhd = !dhd_probe(), so ret 1 for WL */
77	} else if (val == DHD_IOCTL_MAGIC) {
78		ret = 0;
79	} else {
80		if (ret < 0) {
81			perror("dhd_ioctl");
82		}
83		ret = 1; /* default: WL mode */
84	}
85	return ret;
86}
87#endif /* __CONFIG_DHDAP__ */
88
89int
90wl_iovar_getbuf(char *ifname, char *iovar, void *param, int paramlen, void *bufptr, int buflen)
91{
92	int err;
93	uint namelen;
94	uint iolen;
95	uint wlc_cmd = WLC_GET_VAR;
96
97	namelen = strlen(iovar) + 1;	 /* length of iovar name plus null */
98	iolen = namelen + paramlen;
99
100	/* check for overflow */
101	if (iolen > buflen)
102		return (BCME_BUFTOOSHORT);
103
104	memcpy(bufptr, iovar, namelen);	/* copy iovar name including null */
105	memcpy((int8*)bufptr + namelen, param, paramlen);
106
107	err = wl_ioctl(ifname, wlc_cmd, bufptr, buflen);
108
109	return (err);
110}
111
112#ifdef __CONFIG_DHDAP__
113int
114dhd_iovar_setbuf(char *ifname, char *iovar, void *param, int paramlen, void *bufptr, int buflen)
115{
116	uint namelen;
117	uint iolen;
118
119	namelen = strlen(iovar) + 1;	 /* length of iovar name plus null */
120	iolen = namelen + paramlen;
121
122	/* check for overflow */
123	if (iolen > buflen)
124		return (BCME_BUFTOOSHORT);
125
126	memcpy(bufptr, iovar, namelen);	/* copy iovar name including null */
127	memcpy((int8*)bufptr + namelen, param, paramlen);
128
129	return dhd_ioctl(ifname, WLC_SET_VAR, bufptr, iolen);
130}
131#endif /* __CONFIG_DHDAP__ */
132
133int
134wl_iovar_setbuf(char *ifname, char *iovar, void *param, int paramlen, void *bufptr, int buflen)
135{
136	uint namelen;
137	uint iolen;
138
139	namelen = strlen(iovar) + 1;	 /* length of iovar name plus null */
140	iolen = namelen + paramlen;
141
142	/* check for overflow */
143	if (iolen > buflen)
144		return (BCME_BUFTOOSHORT);
145
146	memcpy(bufptr, iovar, namelen);	/* copy iovar name including null */
147	memcpy((int8*)bufptr + namelen, param, paramlen);
148
149	return wl_ioctl(ifname, WLC_SET_VAR, bufptr, iolen);
150}
151
152#ifdef __CONFIG_DHDAP__
153int
154dhd_iovar_set(char *ifname, char *iovar, void *param, int paramlen)
155{
156	char smbuf[WLC_IOCTL_SMLEN];
157
158	return dhd_iovar_setbuf(ifname, iovar, param, paramlen, smbuf, sizeof(smbuf));
159}
160#endif /* __CONFIG_DHDAP__ */
161
162int
163wl_iovar_set(char *ifname, char *iovar, void *param, int paramlen)
164{
165	char smbuf[WLC_IOCTL_SMLEN];
166
167	return wl_iovar_setbuf(ifname, iovar, param, paramlen, smbuf, sizeof(smbuf));
168}
169
170int
171wl_iovar_get(char *ifname, char *iovar, void *bufptr, int buflen)
172{
173	char smbuf[WLC_IOCTL_SMLEN];
174	int ret;
175
176	/* use the return buffer if it is bigger than what we have on the stack */
177	if (buflen > sizeof(smbuf)) {
178		ret = wl_iovar_getbuf(ifname, iovar, NULL, 0, bufptr, buflen);
179	} else {
180		ret = wl_iovar_getbuf(ifname, iovar, NULL, 0, smbuf, sizeof(smbuf));
181		if (ret == 0)
182			memcpy(bufptr, smbuf, buflen);
183	}
184
185	return ret;
186}
187
188#ifdef __CONFIG_DHDAP__
189/*
190 * set named driver variable to int value
191 * calling example: dhd_iovar_setint(ifname, "arate", rate)
192*/
193int
194dhd_iovar_setint(char *ifname, char *iovar, int val)
195{
196	return dhd_iovar_set(ifname, iovar, &val, sizeof(val));
197}
198#endif /* __CONFIG_DHDAP__ */
199
200/*
201 * set named driver variable to int value
202 * calling example: wl_iovar_setint(ifname, "arate", rate)
203*/
204int
205wl_iovar_setint(char *ifname, char *iovar, int val)
206{
207	return wl_iovar_set(ifname, iovar, &val, sizeof(val));
208}
209
210/*
211 * get named driver variable to int value and return error indication
212 * calling example: wl_iovar_getint(ifname, "arate", &rate)
213 */
214int
215wl_iovar_getint(char *ifname, char *iovar, int *val)
216{
217	return wl_iovar_get(ifname, iovar, val, sizeof(int));
218}
219
220/*
221 * format a bsscfg indexed iovar buffer
222 */
223static int
224wl_bssiovar_mkbuf(char *iovar, int bssidx, void *param, int paramlen, void *bufptr, int buflen,
225                  int *plen)
226{
227	char *prefix = "bsscfg:";
228	int8* p;
229	uint prefixlen;
230	uint namelen;
231	uint iolen;
232
233	prefixlen = strlen(prefix);	/* length of bsscfg prefix */
234	namelen = strlen(iovar) + 1;	/* length of iovar name + null */
235	iolen = prefixlen + namelen + sizeof(int) + paramlen;
236
237	/* check for overflow */
238	if (buflen < 0 || iolen > (uint)buflen) {
239		*plen = 0;
240		return BCME_BUFTOOSHORT;
241	}
242
243	p = (int8*)bufptr;
244
245	/* copy prefix, no null */
246	memcpy(p, prefix, prefixlen);
247	p += prefixlen;
248
249	/* copy iovar name including null */
250	memcpy(p, iovar, namelen);
251	p += namelen;
252
253	/* bss config index as first param */
254	memcpy(p, &bssidx, sizeof(int32));
255	p += sizeof(int32);
256
257	/* parameter buffer follows */
258	if (paramlen)
259		memcpy(p, param, paramlen);
260
261	*plen = iolen;
262	return 0;
263}
264
265/*
266 * set named & bss indexed driver variable to buffer value
267 */
268int
269wl_bssiovar_setbuf(char *ifname, char *iovar, int bssidx, void *param, int paramlen, void *bufptr,
270                   int buflen)
271{
272	int err;
273	int iolen;
274
275	err = wl_bssiovar_mkbuf(iovar, bssidx, param, paramlen, bufptr, buflen, &iolen);
276	if (err)
277		return err;
278
279	return wl_ioctl(ifname, WLC_SET_VAR, bufptr, iolen);
280}
281
282#ifdef __CONFIG_DHDAP__
283int
284dhd_ioctl(char *name, int cmd, void *buf, int len)
285{
286	struct ifreq ifr;
287	dhd_ioctl_t ioc;
288	int ret = 0;
289	int s;
290	char buffer[WLC_IOCTL_SMLEN];
291
292	/* open socket to kernel */
293	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
294		perror("socket");
295		return -1;
296	}
297
298	/* do it */
299	if (cmd == WLC_SET_VAR) {
300		cmd = DHD_SET_VAR;
301	} else if (cmd == WLC_GET_VAR) {
302		cmd = DHD_GET_VAR;
303	}
304
305	ioc.cmd = cmd;
306	ioc.buf = buf;
307	ioc.len = len;
308	ioc.set = FALSE;
309	ioc.driver = DHD_IOCTL_MAGIC;
310	ioc.used = 0;
311	ioc.needed = 0;
312
313	strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name) - 1);
314	ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0';
315
316	ifr.ifr_data = (caddr_t) &ioc;
317	if ((ret = ioctl(s, SIOCDEVPRIVATE, &ifr)) < 0)
318		if (cmd != WLC_GET_MAGIC && cmd != WLC_GET_BSSID) {
319			if ((cmd == WLC_GET_VAR) || (cmd == WLC_SET_VAR)) {
320				snprintf(buffer, sizeof(buffer), "%s: WLC_%s_VAR(%s)", name,
321				         cmd == WLC_GET_VAR ? "GET" : "SET", (char *)buf);
322			} else {
323				snprintf(buffer, sizeof(buffer), "%s: cmd=%d", name, cmd);
324			}
325			perror(buffer);
326		}
327	/* cleanup */
328	close(s);
329	return ret;
330}
331
332/*
333 * set named & bss indexed driver variable to buffer value
334 */
335int
336dhd_bssiovar_setbuf(char *ifname, char *iovar, int bssidx, void *param, int paramlen, void *bufptr,
337                   int buflen)
338{
339	int err;
340	int iolen;
341
342	err = wl_bssiovar_mkbuf(iovar, bssidx, param, paramlen, bufptr, buflen, &iolen);
343	if (err)
344		return err;
345
346	return dhd_ioctl(ifname, WLC_SET_VAR, bufptr, iolen);
347}
348#endif /* __CONFIG_DHDAP__ */
349
350/*
351 * get named & bss indexed driver variable buffer value
352 */
353int
354wl_bssiovar_getbuf(char *ifname, char *iovar, int bssidx, void *param, int paramlen, void *bufptr,
355                   int buflen)
356{
357	int err;
358	int iolen;
359
360	err = wl_bssiovar_mkbuf(iovar, bssidx, param, paramlen, bufptr, buflen, &iolen);
361	if (err)
362		return err;
363
364	return wl_ioctl(ifname, WLC_GET_VAR, bufptr, buflen);
365}
366
367/*
368 * set named & bss indexed driver variable to buffer value
369 */
370int
371wl_bssiovar_set(char *ifname, char *iovar, int bssidx, void *param, int paramlen)
372{
373	char smbuf[WLC_IOCTL_SMLEN];
374
375	return wl_bssiovar_setbuf(ifname, iovar, bssidx, param, paramlen, smbuf, sizeof(smbuf));
376}
377
378#ifdef __CONFIG_DHDAP__
379/*
380 * set named & bss indexed driver variable to buffer value
381 */
382int
383dhd_bssiovar_set(char *ifname, char *iovar, int bssidx, void *param, int paramlen)
384{
385	char smbuf[WLC_IOCTL_SMLEN];
386
387	return dhd_bssiovar_setbuf(ifname, iovar, bssidx, param, paramlen, smbuf, sizeof(smbuf));
388}
389#endif
390
391/*
392 * get named & bss indexed driver variable buffer value
393 */
394int
395wl_bssiovar_get(char *ifname, char *iovar, int bssidx, void *outbuf, int len)
396{
397	char smbuf[WLC_IOCTL_SMLEN];
398	int err;
399
400	/* use the return buffer if it is bigger than what we have on the stack */
401	if (len > (int)sizeof(smbuf)) {
402		err = wl_bssiovar_getbuf(ifname, iovar, bssidx, NULL, 0, outbuf, len);
403	} else {
404		memset(smbuf, 0, sizeof(smbuf));
405		err = wl_bssiovar_getbuf(ifname, iovar, bssidx, NULL, 0, smbuf, sizeof(smbuf));
406		if (err == 0)
407			memcpy(outbuf, smbuf, len);
408	}
409
410	return err;
411}
412
413/*
414 * set named & bss indexed driver variable to int value
415 */
416int
417wl_bssiovar_setint(char *ifname, char *iovar, int bssidx, int val)
418{
419	return wl_bssiovar_set(ifname, iovar, bssidx, &val, sizeof(int));
420}
421
422#ifdef __CONFIG_DHDAP__
423/*
424 * set named & bss indexed driver variable to int value
425 */
426int
427dhd_bssiovar_setint(char *ifname, char *iovar, int bssidx, int val)
428{
429	return dhd_bssiovar_set(ifname, iovar, bssidx, &val, sizeof(int));
430}
431#endif
432
433/*
434void
435wl_printlasterror(char *name)
436{
437	char err_buf[WLC_IOCTL_SMLEN];
438	strcpy(err_buf, "bcmerrstr");
439
440	fprintf(stderr, "Error: ");
441	if ( wl_ioctl(name, WLC_GET_VAR, err_buf, sizeof (err_buf)) != 0)
442		fprintf(stderr, "Error getting the Errorstring from driver\n");
443	else
444		fprintf(stderr, err_buf);
445}
446*/
447