398c54a4e80c700f96ea8a8872c25efc066abf09
2 * Common function shared by Linux WEXT, cfg80211 and p2p drivers
4 * Copyright (C) 1999-2018, Broadcom.
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 754283 2018-03-27 02:51:46Z $
31 #include <linux/kernel.h>
32 #include <linux/kthread.h>
33 #include <linux/netdevice.h>
35 #include <wldev_common.h>
38 #include <wl_cfg80211.h>
39 #endif /* WL_CFG80211 */
40 #include <dhd_config.h>
46 #define htodchanspec(i) (i)
47 #define dtohchanspec(i) (i)
49 #define WLDEV_ERROR(args) \
51 printk(KERN_ERR "WLDEV-ERROR) "); \
55 #define WLDEV_INFO(args) \
57 printk(KERN_INFO "WLDEV-INFO) "); \
61 extern int dhd_ioctl_entry_local(struct net_device
*net
, wl_ioctl_t
*ioc
, int cmd
);
64 struct net_device
*dev
, u32 cmd
, void *arg
, u32 len
, u32 set
)
69 memset(&ioc
, 0, sizeof(ioc
));
74 ret
= dhd_ioctl_entry_local(dev
, (wl_ioctl_t
*)&ioc
, cmd
);
81 cast buffer to non-const and call the GET function
85 struct net_device
*dev
, u32 cmd
, const void *arg
, u32 len
)
88 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
89 #pragma GCC diagnostic push
90 #pragma GCC diagnostic ignored "-Wcast-qual"
92 return wldev_ioctl(dev
, cmd
, (void *)arg
, len
, 1);
93 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
94 #pragma GCC diagnostic pop
100 struct net_device
*dev
, u32 cmd
, void *arg
, u32 len
)
102 return wldev_ioctl(dev
, cmd
, (void *)arg
, len
, 0);
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
109 static s32
wldev_mkiovar(
110 const s8
*iovar_name
, const s8
*param
, s32 paramlen
,
111 s8
*iovar_buf
, u32 buflen
)
115 iolen
= bcm_mkiovar(iovar_name
, param
, paramlen
, iovar_buf
, buflen
);
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
)
125 mutex_lock(buf_sync
);
128 if (buf
&& (buflen
> 0)) {
129 /* initialize the response buffer */
130 memset(buf
, 0, buflen
);
136 ret
= wldev_mkiovar(iovar_name
, param
, paramlen
, buf
, buflen
);
139 ret
= BCME_BUFTOOSHORT
;
142 ret
= wldev_ioctl_get(dev
, WLC_GET_VAR
, buf
, buflen
);
145 mutex_unlock(buf_sync
);
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
)
156 mutex_lock(buf_sync
);
158 iovar_len
= wldev_mkiovar(iovar_name
, param
, paramlen
, buf
, buflen
);
160 ret
= wldev_ioctl_set(dev
, WLC_SET_VAR
, buf
, iovar_len
);
162 ret
= BCME_BUFTOOSHORT
;
165 mutex_unlock(buf_sync
);
169 s32
wldev_iovar_setint(
170 struct net_device
*dev
, s8
*iovar
, s32 val
)
172 s8 iovar_buf
[WLC_IOCTL_SMLEN
];
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
);
180 s32
wldev_iovar_getint(
181 struct net_device
*dev
, s8
*iovar
, s32
*pval
)
183 s8 iovar_buf
[WLC_IOCTL_SMLEN
];
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
);
191 memcpy(pval
, iovar_buf
, sizeof(*pval
));
192 *pval
= dtoh32(*pval
);
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
201 s32
wldev_mkiovar_bsscfg(
202 const s8
*iovar_name
, const s8
*param
, s32 paramlen
,
203 s8
*iovar_buf
, s32 buflen
, s32 bssidx
)
205 const s8
*prefix
= "bsscfg:";
211 /* initialize buffer */
212 if (!iovar_buf
|| buflen
<= 0)
214 memset(iovar_buf
, 0, buflen
);
217 return wldev_mkiovar(iovar_name
, param
, paramlen
,
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
;
225 if (iolen
> (u32
)buflen
) {
226 WLDEV_ERROR(("%s: buffer is too short\n", __FUNCTION__
));
227 return BCME_BUFTOOSHORT
;
232 /* copy prefix, no null */
233 memcpy(p
, prefix
, prefixlen
);
236 /* copy iovar name including null */
237 memcpy(p
, iovar_name
, namelen
);
240 /* bss config index as first param */
241 bssidx
= htod32(bssidx
);
242 memcpy(p
, &bssidx
, sizeof(u32
));
245 /* parameter buffer follows */
247 memcpy(p
, param
, paramlen
);
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
)
259 mutex_lock(buf_sync
);
262 wldev_mkiovar_bsscfg(iovar_name
, param
, paramlen
, buf
, buflen
, bsscfg_idx
);
263 ret
= wldev_ioctl_get(dev
, WLC_GET_VAR
, buf
, buflen
);
265 mutex_unlock(buf_sync
);
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
)
279 mutex_lock(buf_sync
);
281 iovar_len
= wldev_mkiovar_bsscfg(iovar_name
, param
, paramlen
, buf
, buflen
, bsscfg_idx
);
283 ret
= wldev_ioctl_set(dev
, WLC_SET_VAR
, buf
, iovar_len
);
285 ret
= BCME_BUFTOOSHORT
;
289 mutex_unlock(buf_sync
);
294 s32
wldev_iovar_setint_bsscfg(
295 struct net_device
*dev
, s8
*iovar
, s32 val
, s32 bssidx
)
297 s8 iovar_buf
[WLC_IOCTL_SMLEN
];
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
);
305 s32
wldev_iovar_getint_bsscfg(
306 struct net_device
*dev
, s8
*iovar
, s32
*pval
, s32 bssidx
)
308 s8 iovar_buf
[WLC_IOCTL_SMLEN
];
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
);
316 memcpy(pval
, iovar_buf
, sizeof(*pval
));
317 *pval
= dtoh32(*pval
);
322 int wldev_get_link_speed(
323 struct net_device
*dev
, int *plink_speed
)
330 error
= wldev_ioctl_get(dev
, WLC_GET_RATE
, plink_speed
, sizeof(int));
334 /* Convert internal 500Kbps to Kbps */
340 struct net_device
*dev
, scb_val_t
*scb_val
)
346 memset(scb_val
, 0, sizeof(scb_val_t
));
347 error
= wldev_ioctl_get(dev
, WLC_GET_RSSI
, scb_val
, sizeof(scb_val_t
));
355 struct net_device
*dev
, wlc_ssid_t
*pssid
)
361 memset(pssid
, 0, sizeof(wlc_ssid_t
));
362 error
= wldev_ioctl_get(dev
, WLC_GET_SSID
, pssid
, sizeof(wlc_ssid_t
));
365 pssid
->SSID_len
= dtoh32(pssid
->SSID_len
);
370 struct net_device
*dev
, uint
*pband
)
375 error
= wldev_ioctl_get(dev
, WLC_GET_BAND
, pband
, sizeof(uint
));
380 struct net_device
*dev
, uint band
)
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
));
387 dhd_bus_band_set(dev
, band
);
391 int wldev_get_datarate(struct net_device
*dev
, int *datarate
)
395 error
= wldev_ioctl_get(dev
, WLC_GET_RATE
, datarate
, sizeof(int));
399 *datarate
= dtoh32(*datarate
);
407 wl_chspec_driver_to_host(chanspec_t chanspec
);
408 #define WL_EXTRA_BUF_MAX 2048
410 struct net_device
*dev
, uint8
*cap
, uint8 caplen
)
415 uint16 bandwidth
= 0;
416 wl_bss_info_t
*bss
= NULL
;
419 buf
= kzalloc(WL_EXTRA_BUF_MAX
, GFP_KERNEL
);
421 WLDEV_ERROR(("%s:ENOMEM\n", __FUNCTION__
));
425 *(u32
*) buf
= htod32(WL_EXTRA_BUF_MAX
);
426 error
= wldev_ioctl_get(dev
, WLC_GET_BSS_INFO
, (void*)buf
, WL_EXTRA_BUF_MAX
);
428 WLDEV_ERROR(("%s:failed:%d\n", __FUNCTION__
, error
));
433 bss
= (wl_bss_info_t
*)(buf
+ 4);
434 chanspec
= wl_chspec_driver_to_host(bss
->chanspec
);
436 band
= chanspec
& WL_CHANSPEC_BAND_MASK
;
437 bandwidth
= chanspec
& WL_CHANSPEC_BW_MASK
;
439 if (band
== WL_CHANSPEC_BAND_2G
) {
441 strncpy(cap
, "n", caplen
);
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
);
451 strncpy(cap
, "n", caplen
);
452 else if (bss
->vht_cap
)
453 strncpy(cap
, "ac", caplen
);
455 strncpy(cap
, "a", caplen
);
457 WLDEV_ERROR(("%s:Mode get failed\n", __FUNCTION__
));
468 int wldev_set_country(
469 struct net_device
*dev
, char *country_code
, bool notify
, bool user_enforced
, int revinfo
)
472 wl_country_t cspec
= {{0}, 0, {0}};
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 */
483 bzero(&scbval
, sizeof(scb_val_t
));
484 error
= wldev_iovar_getbuf(dev
, "country", NULL
, 0, &cspec
, sizeof(cspec
), NULL
);
486 WLDEV_ERROR(("%s: get country failed = %d\n", __FUNCTION__
, error
));
491 dhd_force_country_change(dev
) ||
492 (strncmp(country_code
, cspec
.ccode
, WLC_CNTRY_BUF_SZ
) != 0)) {
495 if ((user_enforced
) && (wl_get_drv_status(cfg
, CONNECTED
, dev
)))
498 #endif /* WL_CFG80211 */
500 bzero(&scbval
, sizeof(scb_val_t
));
501 error
= wldev_ioctl_set(dev
, WLC_DISASSOC
,
502 &scbval
, sizeof(scb_val_t
));
504 WLDEV_ERROR(("%s: set country failed due to Disassoc error %d\n",
505 __FUNCTION__
, error
));
511 wl_cfg80211_scan_abort(cfg
);
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
);
519 dhd_get_customized_country_code(dev
, (char *)&cspec
.country_abbrev
, &cspec
);
520 error
= dhd_conf_set_country(dhd_get_pub(dev
), &cspec
);
522 WLDEV_ERROR(("%s: set country for %s as %s rev %d failed\n",
523 __FUNCTION__
, country_code
, cspec
.ccode
, cspec
.rev
));
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
);