xref: /OK3568_Linux_fs/external/rkwifibt/drivers/bcmdhd/wldev_common.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /*
2  * Common function shared by Linux WEXT, cfg80211 and p2p drivers
3  *
4  * Copyright (C) 2020, Broadcom.
5  *
6  *      Unless you and Broadcom execute a separate written software license
7  * agreement governing use of this software, this software is licensed to you
8  * under the terms of the GNU General Public License version 2 (the "GPL"),
9  * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10  * following added to such license:
11  *
12  *      As a special exception, the copyright holders of this software give you
13  * permission to link this software with independent modules, and to copy and
14  * distribute the resulting executable under terms of your choice, provided that
15  * you also meet, for each linked independent module, the terms and conditions of
16  * the license of that module.  An independent module is a module which is not
17  * derived from this software.  The special exception does not apply to any
18  * modifications of the software.
19  *
20  *
21  * <<Broadcom-WL-IPTag/Dual:>>
22  */
23 
24 #include <osl.h>
25 #include <linux/kernel.h>
26 #include <linux/kthread.h>
27 #include <linux/netdevice.h>
28 
29 #include <wldev_common.h>
30 #include <bcmutils.h>
31 #ifdef WL_CFG80211
32 #include <wl_cfg80211.h>
33 #include <wl_cfgscan.h>
34 #endif /* WL_CFG80211 */
35 #include <dhd_config.h>
36 
37 #if defined(IL_BIGENDIAN)
38 #include <bcmendian.h>
39 #define htod32(i) (bcmswap32(i))
40 #define htod16(i) (bcmswap16(i))
41 #define dtoh32(i) (bcmswap32(i))
42 #define dtoh16(i) (bcmswap16(i))
43 #define htodchanspec(i) htod16(i)
44 #define dtohchanspec(i) dtoh16(i)
45 #else
46 #define htod32(i) (i)
47 #define htod16(i) (i)
48 #define dtoh32(i) (i)
49 #define dtoh16(i) (i)
50 #define htodchanspec(i) (i)
51 #define dtohchanspec(i) (i)
52 #endif
53 
54 #if defined(CUSTOMER_DBG_PREFIX_ENABLE)
55 #define USER_PREFIX_WLDEV		"[wldev][wlan] "
56 #define WLDEV_ERROR_TEXT		USER_PREFIX_WLDEV
57 #define WLDEV_INFO_TEXT			USER_PREFIX_WLDEV
58 #else
59 #define WLDEV_ERROR_TEXT		"WLDEV-ERROR) "
60 #define WLDEV_INFO_TEXT			"WLDEV-INFO) "
61 #endif /* defined(CUSTOMER_DBG_PREFIX_ENABLE) */
62 
63 #define	WLDEV_ERROR_MSG(x, args...)						\
64 	do {										\
65 		printf(WLDEV_ERROR_TEXT x, ## args);	\
66 	} while (0)
67 #define WLDEV_ERROR(x) WLDEV_ERROR_MSG x
68 
69 #define	WLDEV_INFO_MSG(x, args...)						\
70 	do {										\
71 		printf(WLDEV_INFO_TEXT x, ## args);	\
72 	} while (0)
73 #define WLDEV_INFO(x) WLDEV_INFO_MSG x
74 
75 extern int dhd_ioctl_entry_local(struct net_device *net, wl_ioctl_t *ioc, int cmd);
76 
wldev_ioctl(struct net_device * dev,u32 cmd,void * arg,u32 len,u32 set)77 s32 wldev_ioctl(
78 	struct net_device *dev, u32 cmd, void *arg, u32 len, u32 set)
79 {
80 	s32 ret = 0;
81 	struct wl_ioctl  ioc;
82 
83 #if defined(BCMDONGLEHOST)
84 
85 	bzero(&ioc, sizeof(ioc));
86 	ioc.cmd = cmd;
87 	ioc.buf = arg;
88 	ioc.len = len;
89 	ioc.set = set;
90 	ret = dhd_ioctl_entry_local(dev, (wl_ioctl_t *)&ioc, cmd);
91 #else
92 	struct ifreq ifr;
93 	mm_segment_t fs;
94 
95 	bzero(&ioc, sizeof(ioc));
96 	ioc.cmd = cmd;
97 	ioc.buf = arg;
98 	ioc.len = len;
99 	ioc.set = set;
100 
101 	strlcpy(ifr.ifr_name, dev->name, sizeof(ifr.ifr_name));
102 	ifr.ifr_data = (caddr_t)&ioc;
103 
104 	fs = get_fs();
105 	set_fs(get_ds());
106 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
107 	ret = dev->do_ioctl(dev, &ifr, SIOCDEVPRIVATE);
108 #else
109 	ret = dev->netdev_ops->ndo_do_ioctl(dev, &ifr, SIOCDEVPRIVATE);
110 #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) */
111 	set_fs(fs);
112 
113 	ret = 0;
114 #endif /* defined(BCMDONGLEHOST) */
115 
116 	return ret;
117 }
118 
119 /*
120 SET commands :
121 cast buffer to non-const  and call the GET function
122 */
123 
wldev_ioctl_set(struct net_device * dev,u32 cmd,const void * arg,u32 len)124 s32 wldev_ioctl_set(
125 	struct net_device *dev, u32 cmd, const void *arg, u32 len)
126 {
127 
128 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
129 #pragma GCC diagnostic push
130 #pragma GCC diagnostic ignored "-Wcast-qual"
131 #endif
132 	return wldev_ioctl(dev, cmd, (void *)arg, len, 1);
133 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
134 #pragma GCC diagnostic pop
135 #endif
136 
137 }
138 
wldev_ioctl_get(struct net_device * dev,u32 cmd,void * arg,u32 len)139 s32 wldev_ioctl_get(
140 	struct net_device *dev, u32 cmd, void *arg, u32 len)
141 {
142 	return wldev_ioctl(dev, cmd, (void *)arg, len, 0);
143 }
144 
145 /* Format a iovar buffer, not bsscfg indexed. The bsscfg index will be
146  * taken care of in dhd_ioctl_entry. Internal use only, not exposed to
147  * wl_iw, wl_cfg80211 and wl_cfgp2p
148  */
wldev_mkiovar(const s8 * iovar_name,const s8 * param,u32 paramlen,s8 * iovar_buf,u32 buflen)149 static s32 wldev_mkiovar(
150 	const s8 *iovar_name, const s8 *param, u32 paramlen,
151 	s8 *iovar_buf, u32 buflen)
152 {
153 	s32 iolen = 0;
154 
155 	iolen = bcm_mkiovar(iovar_name, param, paramlen, iovar_buf, buflen);
156 	return iolen;
157 }
158 
wldev_iovar_getbuf(struct net_device * dev,s8 * iovar_name,const void * param,u32 paramlen,void * buf,u32 buflen,struct mutex * buf_sync)159 s32 wldev_iovar_getbuf(
160 	struct net_device *dev, s8 *iovar_name,
161 	const void *param, u32 paramlen, void *buf, u32 buflen, struct mutex* buf_sync)
162 {
163 	s32 ret = 0;
164 	if (buf_sync) {
165 		mutex_lock(buf_sync);
166 	}
167 
168 	if (buf && (buflen > 0)) {
169 		/* initialize the response buffer */
170 		bzero(buf, buflen);
171 	} else {
172 		ret = BCME_BADARG;
173 		goto exit;
174 	}
175 
176 	ret = wldev_mkiovar(iovar_name, param, paramlen, buf, buflen);
177 
178 	if (!ret) {
179 		ret = BCME_BUFTOOSHORT;
180 		goto exit;
181 	}
182 	ret = wldev_ioctl_get(dev, WLC_GET_VAR, buf, buflen);
183 exit:
184 	if (buf_sync)
185 		mutex_unlock(buf_sync);
186 	return ret;
187 }
188 
wldev_iovar_setbuf(struct net_device * dev,s8 * iovar_name,const void * param,s32 paramlen,void * buf,s32 buflen,struct mutex * buf_sync)189 s32 wldev_iovar_setbuf(
190 	struct net_device *dev, s8 *iovar_name,
191 	const void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync)
192 {
193 	s32 ret = 0;
194 	s32 iovar_len;
195 	if (buf_sync) {
196 		mutex_lock(buf_sync);
197 	}
198 	iovar_len = wldev_mkiovar(iovar_name, param, paramlen, buf, buflen);
199 	if (iovar_len > 0)
200 		ret = wldev_ioctl_set(dev, WLC_SET_VAR, buf, iovar_len);
201 	else
202 		ret = BCME_BUFTOOSHORT;
203 
204 	if (buf_sync)
205 		mutex_unlock(buf_sync);
206 	return ret;
207 }
208 
wldev_iovar_setint(struct net_device * dev,s8 * iovar,s32 val)209 s32 wldev_iovar_setint(
210 	struct net_device *dev, s8 *iovar, s32 val)
211 {
212 	s8 iovar_buf[WLC_IOCTL_SMLEN];
213 
214 	val = htod32(val);
215 	bzero(iovar_buf, sizeof(iovar_buf));
216 	return wldev_iovar_setbuf(dev, iovar, &val, sizeof(val), iovar_buf,
217 		sizeof(iovar_buf), NULL);
218 }
219 
wldev_iovar_getint(struct net_device * dev,s8 * iovar,s32 * pval)220 s32 wldev_iovar_getint(
221 	struct net_device *dev, s8 *iovar, s32 *pval)
222 {
223 	s8 iovar_buf[WLC_IOCTL_SMLEN];
224 	s32 err;
225 
226 	bzero(iovar_buf, sizeof(iovar_buf));
227 	err = wldev_iovar_getbuf(dev, iovar, pval, sizeof(*pval), iovar_buf,
228 		sizeof(iovar_buf), NULL);
229 	if (err == 0)
230 	{
231 		memcpy(pval, iovar_buf, sizeof(*pval));
232 		*pval = dtoh32(*pval);
233 	}
234 	return err;
235 }
236 
237 /** Format a bsscfg indexed iovar buffer. The bsscfg index will be
238  *  taken care of in dhd_ioctl_entry. Internal use only, not exposed to
239  *  wl_iw, wl_cfg80211 and wl_cfgp2p
240  */
wldev_mkiovar_bsscfg(const s8 * iovar_name,const s8 * param,s32 paramlen,s8 * iovar_buf,s32 buflen,s32 bssidx)241 s32 wldev_mkiovar_bsscfg(
242 	const s8 *iovar_name, const s8 *param, s32 paramlen,
243 	s8 *iovar_buf, s32 buflen, s32 bssidx)
244 {
245 	const s8 *prefix = "bsscfg:";
246 	s8 *p;
247 	u32 prefixlen;
248 	u32 namelen;
249 	u32 iolen;
250 
251 	/* initialize buffer */
252 	if (!iovar_buf || buflen <= 0)
253 		return BCME_BADARG;
254 	bzero(iovar_buf, buflen);
255 
256 	if (bssidx == 0) {
257 		return wldev_mkiovar(iovar_name, param, paramlen,
258 			iovar_buf, buflen);
259 	}
260 
261 	prefixlen = (u32) strlen(prefix); /* lengh of bsscfg prefix */
262 	namelen = (u32) strlen(iovar_name) + 1; /* lengh of iovar  name + null */
263 	iolen = prefixlen + namelen + sizeof(u32) + paramlen;
264 
265 	if (iolen > (u32)buflen) {
266 		WLDEV_ERROR(("wldev_mkiovar_bsscfg: buffer is too short\n"));
267 		return BCME_BUFTOOSHORT;
268 	}
269 
270 	p = (s8 *)iovar_buf;
271 
272 	/* copy prefix, no null */
273 	memcpy(p, prefix, prefixlen);
274 	p += prefixlen;
275 
276 	/* copy iovar name including null */
277 	memcpy(p, iovar_name, namelen);
278 	p += namelen;
279 
280 	/* bss config index as first param */
281 	bssidx = htod32(bssidx);
282 	memcpy(p, &bssidx, sizeof(u32));
283 	p += sizeof(u32);
284 
285 	/* parameter buffer follows */
286 	if (paramlen)
287 		memcpy(p, param, paramlen);
288 
289 	return iolen;
290 
291 }
292 
wldev_iovar_getbuf_bsscfg(struct net_device * dev,s8 * iovar_name,void * param,s32 paramlen,void * buf,s32 buflen,s32 bsscfg_idx,struct mutex * buf_sync)293 s32 wldev_iovar_getbuf_bsscfg(
294 	struct net_device *dev, s8 *iovar_name,
295 	void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync)
296 {
297 	s32 ret = 0;
298 	if (buf_sync) {
299 		mutex_lock(buf_sync);
300 	}
301 
302 	wldev_mkiovar_bsscfg(iovar_name, param, paramlen, buf, buflen, bsscfg_idx);
303 	ret = wldev_ioctl_get(dev, WLC_GET_VAR, buf, buflen);
304 	if (buf_sync) {
305 		mutex_unlock(buf_sync);
306 	}
307 	return ret;
308 
309 }
310 
wldev_iovar_setbuf_bsscfg(struct net_device * dev,const s8 * iovar_name,const void * param,s32 paramlen,void * buf,s32 buflen,s32 bsscfg_idx,struct mutex * buf_sync)311 s32 wldev_iovar_setbuf_bsscfg(
312 	struct net_device *dev, const s8 *iovar_name,
313 	const void *param, s32 paramlen,
314 	void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync)
315 {
316 	s32 ret = 0;
317 	s32 iovar_len;
318 	if (buf_sync) {
319 		mutex_lock(buf_sync);
320 	}
321 	iovar_len = wldev_mkiovar_bsscfg(iovar_name, param, paramlen, buf, buflen, bsscfg_idx);
322 	if (iovar_len > 0)
323 		ret = wldev_ioctl_set(dev, WLC_SET_VAR, buf, iovar_len);
324 	else {
325 		ret = BCME_BUFTOOSHORT;
326 	}
327 
328 	if (buf_sync) {
329 		mutex_unlock(buf_sync);
330 	}
331 	return ret;
332 }
333 
wldev_iovar_setint_bsscfg(struct net_device * dev,s8 * iovar,s32 val,s32 bssidx)334 s32 wldev_iovar_setint_bsscfg(
335 	struct net_device *dev, s8 *iovar, s32 val, s32 bssidx)
336 {
337 	s8 iovar_buf[WLC_IOCTL_SMLEN];
338 
339 	val = htod32(val);
340 	bzero(iovar_buf, sizeof(iovar_buf));
341 	return wldev_iovar_setbuf_bsscfg(dev, iovar, &val, sizeof(val), iovar_buf,
342 		sizeof(iovar_buf), bssidx, NULL);
343 }
344 
wldev_iovar_getint_bsscfg(struct net_device * dev,s8 * iovar,s32 * pval,s32 bssidx)345 s32 wldev_iovar_getint_bsscfg(
346 	struct net_device *dev, s8 *iovar, s32 *pval, s32 bssidx)
347 {
348 	s8 iovar_buf[WLC_IOCTL_SMLEN];
349 	s32 err;
350 
351 	bzero(iovar_buf, sizeof(iovar_buf));
352 	err = wldev_iovar_getbuf_bsscfg(dev, iovar, pval, sizeof(*pval), iovar_buf,
353 		sizeof(iovar_buf), bssidx, NULL);
354 	if (err == 0)
355 	{
356 		memcpy(pval, iovar_buf, sizeof(*pval));
357 		*pval = dtoh32(*pval);
358 	}
359 	return err;
360 }
361 
wldev_get_link_speed(struct net_device * dev,int * plink_speed)362 int wldev_get_link_speed(
363 	struct net_device *dev, int *plink_speed)
364 {
365 	int error;
366 
367 	if (!plink_speed)
368 		return -ENOMEM;
369 	*plink_speed = 0;
370 	error = wldev_ioctl_get(dev, WLC_GET_RATE, plink_speed, sizeof(int));
371 	if (unlikely(error))
372 		return error;
373 
374 	/* Convert internal 500Kbps to Kbps */
375 	*plink_speed *= 500;
376 	return error;
377 }
378 
wldev_get_rssi(struct net_device * dev,scb_val_t * scb_val)379 int wldev_get_rssi(
380 	struct net_device *dev, scb_val_t *scb_val)
381 {
382 	int error;
383 
384 	if (!scb_val)
385 		return -ENOMEM;
386 	bzero(scb_val, sizeof(scb_val_t));
387 	error = wldev_ioctl_get(dev, WLC_GET_RSSI, scb_val, sizeof(scb_val_t));
388 	if (unlikely(error))
389 		return error;
390 
391 	return error;
392 }
393 
wldev_get_ssid(struct net_device * dev,wlc_ssid_t * pssid)394 int wldev_get_ssid(
395 	struct net_device *dev, wlc_ssid_t *pssid)
396 {
397 	int error;
398 
399 	if (!pssid)
400 		return -ENOMEM;
401 	bzero(pssid, sizeof(wlc_ssid_t));
402 	error = wldev_ioctl_get(dev, WLC_GET_SSID, pssid, sizeof(wlc_ssid_t));
403 	if (unlikely(error))
404 		return error;
405 	pssid->SSID_len = dtoh32(pssid->SSID_len);
406 	return error;
407 }
408 
wldev_get_band(struct net_device * dev,uint * pband)409 int wldev_get_band(
410 	struct net_device *dev, uint *pband)
411 {
412 	int error;
413 
414 	*pband = 0;
415 	error = wldev_ioctl_get(dev, WLC_GET_BAND, pband, sizeof(uint));
416 	return error;
417 }
418 
wldev_set_band(struct net_device * dev,uint band)419 int wldev_set_band(
420 	struct net_device *dev, uint band)
421 {
422 	int error = -1;
423 
424 	if ((band == WLC_BAND_AUTO) || (band == WLC_BAND_5G) || (band == WLC_BAND_2G)) {
425 		error = wldev_ioctl_set(dev, WLC_SET_BAND, &band, sizeof(band));
426 		if (!error)
427 			dhd_bus_band_set(dev, band);
428 	}
429 	return error;
430 }
wldev_get_datarate(struct net_device * dev,int * datarate)431 int wldev_get_datarate(struct net_device *dev, int *datarate)
432 {
433 	int error = 0;
434 
435 	error = wldev_ioctl_get(dev, WLC_GET_RATE, datarate, sizeof(int));
436 	if (error) {
437 		return -1;
438 	} else {
439 		*datarate = dtoh32(*datarate);
440 	}
441 
442 	return error;
443 }
444 
445 #ifdef WL_CFG80211
446 extern chanspec_t
447 wl_chspec_driver_to_host(chanspec_t chanspec);
448 #define WL_EXTRA_BUF_MAX 2048
wldev_get_mode(struct net_device * dev,uint8 * cap,uint8 caplen)449 int wldev_get_mode(
450 	struct net_device *dev, uint8 *cap, uint8 caplen)
451 {
452 	int error = 0;
453 	int chanspec = 0;
454 	uint16 band = 0;
455 	uint16 bandwidth = 0;
456 	wl_bss_info_t *bss = NULL;
457 	char* buf = NULL;
458 
459 	buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
460 	if (!buf) {
461 		WLDEV_ERROR(("%s:ENOMEM\n", __FUNCTION__));
462 		return -ENOMEM;
463 	}
464 
465 	*(u32*) buf = htod32(WL_EXTRA_BUF_MAX);
466 	error = wldev_ioctl_get(dev, WLC_GET_BSS_INFO, (void*)buf, WL_EXTRA_BUF_MAX);
467 	if (error) {
468 		WLDEV_ERROR(("%s:failed:%d\n", __FUNCTION__, error));
469 		kfree(buf);
470 		buf = NULL;
471 		return error;
472 	}
473 	bss = (wl_bss_info_t*)(buf + 4);
474 	chanspec = wl_chspec_driver_to_host(bss->chanspec);
475 
476 	band = chanspec & WL_CHANSPEC_BAND_MASK;
477 	bandwidth = chanspec & WL_CHANSPEC_BW_MASK;
478 
479 	if (band == WL_CHANSPEC_BAND_2G) {
480 		if (bss->n_cap)
481 			strlcpy(cap, "n", caplen);
482 		else
483 			strlcpy(cap, "bg", caplen);
484 	} else if (band == WL_CHANSPEC_BAND_5G) {
485 		if (bandwidth == WL_CHANSPEC_BW_80)
486 			strlcpy(cap, "ac", caplen);
487 		else if ((bandwidth == WL_CHANSPEC_BW_40) || (bandwidth == WL_CHANSPEC_BW_20)) {
488 			if ((bss->nbss_cap & 0xf00) && (bss->n_cap))
489 				strlcpy(cap, "n|ac", caplen);
490 			else if (bss->n_cap)
491 				strlcpy(cap, "n", caplen);
492 			else if (bss->vht_cap)
493 				strlcpy(cap, "ac", caplen);
494 			else
495 				strlcpy(cap, "a", caplen);
496 		} else {
497 			WLDEV_ERROR(("wldev_get_mode: Mode get failed\n"));
498 			error = BCME_ERROR;
499 		}
500 
501 	}
502 	kfree(buf);
503 	buf = NULL;
504 	return error;
505 }
506 #endif
507 
wldev_set_country(struct net_device * dev,char * country_code,bool notify,int revinfo)508 int wldev_set_country(
509 	struct net_device *dev, char *country_code, bool notify, int revinfo)
510 {
511 	int error = 0;
512 #if defined(BCMDONGLEHOST)
513 	struct dhd_pub *dhd = dhd_get_pub(dev);
514 
515 	if (!country_code)
516 		return -1;
517 	error = dhd_conf_country(dhd, "country", country_code);
518 	dhd_bus_country_set(dev, &dhd->dhd_cspec, notify);
519 	printf("%s: set country for %s as %s rev %d\n",
520 		__FUNCTION__, country_code, dhd->dhd_cspec.ccode, dhd->dhd_cspec.rev);
521 #endif /* defined(BCMDONGLEHOST) */
522 	return error;
523 }
524