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