2 * Common function shared by Linux WEXT, cfg80211 and p2p drivers
4 * Copyright (C) 1999-2016, Broadcom Corporation
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:
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.
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.
25 * <<Broadcom-WL-IPTag/Open:>>
27 * $Id: wldev_common.c 556083 2015-05-12 14:03:00Z $
31 #include <linux/kernel.h>
32 #include <linux/kthread.h>
33 #include <linux/netdevice.h>
35 #include <wldev_common.h>
37 #include <dhd_config.h>
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)
52 #define htodchanspec(i) (i)
53 #define dtohchanspec(i) (i)
56 #define WLDEV_ERROR(args) \
58 printk(KERN_ERR "WLDEV-ERROR) %s : ", __func__); \
62 extern int dhd_ioctl_entry_local(struct net_device
*net
, wl_ioctl_t
*ioc
, int cmd
);
65 struct net_device
*dev
, u32 cmd
, void *arg
, u32 len
, u32 set
)
71 memset(&ioc
, 0, sizeof(ioc
));
77 ret
= dhd_ioctl_entry_local(dev
, &ioc
, cmd
);
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
86 static s32
wldev_mkiovar(
87 const s8
*iovar_name
, s8
*param
, s32 paramlen
,
88 s8
*iovar_buf
, u32 buflen
)
92 iolen
= bcm_mkiovar(iovar_name
, param
, paramlen
, iovar_buf
, buflen
);
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
)
102 mutex_lock(buf_sync
);
104 wldev_mkiovar(iovar_name
, param
, paramlen
, buf
, buflen
);
105 ret
= wldev_ioctl(dev
, WLC_GET_VAR
, buf
, buflen
, FALSE
);
107 mutex_unlock(buf_sync
);
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
)
119 mutex_lock(buf_sync
);
121 iovar_len
= wldev_mkiovar(iovar_name
, param
, paramlen
, buf
, buflen
);
123 ret
= wldev_ioctl(dev
, WLC_SET_VAR
, buf
, iovar_len
, TRUE
);
125 ret
= BCME_BUFTOOSHORT
;
128 mutex_unlock(buf_sync
);
132 s32
wldev_iovar_setint(
133 struct net_device
*dev
, s8
*iovar
, s32 val
)
135 s8 iovar_buf
[WLC_IOCTL_SMLEN
];
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
);
144 s32
wldev_iovar_getint(
145 struct net_device
*dev
, s8
*iovar
, s32
*pval
)
147 s8 iovar_buf
[WLC_IOCTL_SMLEN
];
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
);
155 memcpy(pval
, iovar_buf
, sizeof(*pval
));
156 *pval
= dtoh32(*pval
);
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
165 s32
wldev_mkiovar_bsscfg(
166 const s8
*iovar_name
, s8
*param
, s32 paramlen
,
167 s8
*iovar_buf
, s32 buflen
, s32 bssidx
)
169 const s8
*prefix
= "bsscfg:";
176 return wldev_mkiovar(iovar_name
, param
, paramlen
,
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
;
184 if (buflen
< 0 || iolen
> (u32
)buflen
)
186 WLDEV_ERROR(("%s: buffer is too short\n", __FUNCTION__
));
187 return BCME_BUFTOOSHORT
;
192 /* copy prefix, no null */
193 memcpy(p
, prefix
, prefixlen
);
196 /* copy iovar name including null */
197 memcpy(p
, iovar_name
, namelen
);
200 /* bss config index as first param */
201 bssidx
= htod32(bssidx
);
202 memcpy(p
, &bssidx
, sizeof(u32
));
205 /* parameter buffer follows */
207 memcpy(p
, param
, paramlen
);
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
)
219 mutex_lock(buf_sync
);
222 wldev_mkiovar_bsscfg(iovar_name
, param
, paramlen
, buf
, buflen
, bsscfg_idx
);
223 ret
= wldev_ioctl(dev
, WLC_GET_VAR
, buf
, buflen
, FALSE
);
225 mutex_unlock(buf_sync
);
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
)
238 mutex_lock(buf_sync
);
240 iovar_len
= wldev_mkiovar_bsscfg(iovar_name
, param
, paramlen
, buf
, buflen
, bsscfg_idx
);
242 ret
= wldev_ioctl(dev
, WLC_SET_VAR
, buf
, iovar_len
, TRUE
);
244 ret
= BCME_BUFTOOSHORT
;
248 mutex_unlock(buf_sync
);
253 s32
wldev_iovar_setint_bsscfg(
254 struct net_device
*dev
, s8
*iovar
, s32 val
, s32 bssidx
)
256 s8 iovar_buf
[WLC_IOCTL_SMLEN
];
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
);
265 s32
wldev_iovar_getint_bsscfg(
266 struct net_device
*dev
, s8
*iovar
, s32
*pval
, s32 bssidx
)
268 s8 iovar_buf
[WLC_IOCTL_SMLEN
];
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
);
276 memcpy(pval
, iovar_buf
, sizeof(*pval
));
277 *pval
= dtoh32(*pval
);
282 int wldev_get_link_speed(
283 struct net_device
*dev
, int *plink_speed
)
289 error
= wldev_ioctl(dev
, WLC_GET_RATE
, plink_speed
, sizeof(int), 0);
293 /* Convert internal 500Kbps to Kbps */
299 struct net_device
*dev
, scb_val_t
*scb_val
)
306 error
= wldev_ioctl(dev
, WLC_GET_RSSI
, scb_val
, sizeof(scb_val_t
), 0);
314 struct net_device
*dev
, wlc_ssid_t
*pssid
)
320 error
= wldev_ioctl(dev
, WLC_GET_SSID
, pssid
, sizeof(wlc_ssid_t
), 0);
323 pssid
->SSID_len
= dtoh32(pssid
->SSID_len
);
328 struct net_device
*dev
, uint
*pband
)
332 error
= wldev_ioctl(dev
, WLC_GET_BAND
, pband
, sizeof(uint
), 0);
337 struct net_device
*dev
, uint band
)
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);
344 dhd_bus_band_set(dev
, band
);
349 int wldev_get_datarate(struct net_device
*dev
, int *datarate
)
353 error
= wldev_ioctl(dev
, WLC_GET_RATE
, datarate
, sizeof(int), false);
357 *datarate
= dtoh32(*datarate
);
365 wl_chspec_driver_to_host(chanspec_t chanspec
);
366 #define WL_EXTRA_BUF_MAX 2048
368 struct net_device
*dev
, uint8
*cap
)
373 uint16 bandwidth
= 0;
374 wl_bss_info_t
*bss
= NULL
;
377 buf
= kmalloc(WL_EXTRA_BUF_MAX
, GFP_KERNEL
);
379 WLDEV_ERROR(("%s:NOMEM\n", __FUNCTION__
));
383 *(u32
*) buf
= htod32(WL_EXTRA_BUF_MAX
);
384 error
= wldev_ioctl(dev
, WLC_GET_BSS_INFO
, (void*)buf
, WL_EXTRA_BUF_MAX
, false);
386 WLDEV_ERROR(("%s:failed:%d\n", __FUNCTION__
, error
));
391 bss
= (struct wl_bss_info
*)(buf
+ 4);
392 chanspec
= wl_chspec_driver_to_host(bss
->chanspec
);
394 band
= chanspec
& WL_CHANSPEC_BAND_MASK
;
395 bandwidth
= chanspec
& WL_CHANSPEC_BW_MASK
;
397 if (band
== WL_CHANSPEC_BAND_2G
) {
402 } else if (band
== WL_CHANSPEC_BAND_5G
) {
403 if (bandwidth
== WL_CHANSPEC_BW_80
)
405 else if ((bandwidth
== WL_CHANSPEC_BW_40
) || (bandwidth
== WL_CHANSPEC_BW_20
)) {
406 if ((bss
->nbss_cap
& 0xf00) && (bss
->n_cap
))
410 else if (bss
->vht_cap
)
415 WLDEV_ERROR(("%s:Mode get failed\n", __FUNCTION__
));
424 #endif /* WL_CFG80211 */
426 int wldev_set_country(
427 struct net_device
*dev
, char *country_code
, bool notify
, bool user_enforced
, int revinfo
)
430 wl_country_t cspec
= {{0}, 0, {0}};
432 char smbuf
[WLC_IOCTL_SMLEN
];
437 bzero(&scbval
, sizeof(scb_val_t
));
438 error
= wldev_iovar_getbuf(dev
, "country", NULL
, 0, &cspec
, sizeof(cspec
), NULL
);
440 WLDEV_ERROR(("%s: get country failed = %d\n", __FUNCTION__
, error
));
446 dhd_force_country_change(dev
) ||
447 #endif /* OEM_ANDROID */
448 (strncmp(country_code
, cspec
.country_abbrev
, WLC_CNTRY_BUF_SZ
) != 0)) {
451 bzero(&scbval
, sizeof(scb_val_t
));
452 error
= wldev_ioctl(dev
, WLC_DISASSOC
, &scbval
, sizeof(scb_val_t
), true);
454 WLDEV_ERROR(("%s: set country failed due to Disassoc error %d\n",
455 __FUNCTION__
, error
));
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
);
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
);
469 WLDEV_ERROR(("%s: set country for %s as %s rev %d failed\n",
470 __FUNCTION__
, country_code
, cspec
.ccode
, cspec
.rev
));
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
);