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