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