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