| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529 |
- /*
- * Common function shared by Linux WEXT, cfg80211 and p2p drivers
- *
- * Portions of this code are copyright (c) 2020 Cypress Semiconductor Corporation
- *
- * Copyright (C) 1999-2020, Broadcom Corporation
- *
- * Unless you and Broadcom execute a separate written software license
- * agreement governing use of this software, this software is licensed to you
- * under the terms of the GNU General Public License version 2 (the "GPL"),
- * available at http://www.broadcom.com/licenses/GPLv2.php, with the
- * following added to such license:
- *
- * As a special exception, the copyright holders of this software give you
- * permission to link this software with independent modules, and to copy and
- * distribute the resulting executable under terms of your choice, provided that
- * you also meet, for each linked independent module, the terms and conditions of
- * the license of that module. An independent module is a module which is not
- * derived from this software. The special exception does not apply to any
- * modifications of the software.
- *
- * Notwithstanding the above, under no circumstances may you combine this
- * software in any way with any other Broadcom software provided under a license
- * other than the GPL, without Broadcom's express prior written consent.
- *
- *
- * <<Broadcom-WL-IPTag/Open:>>
- *
- * $Id: wldev_common.c 698236 2017-05-08 19:41:09Z $
- */
- #include <osl.h>
- #include <linux/kernel.h>
- #include <linux/kthread.h>
- #include <linux/netdevice.h>
- #include <wldev_common.h>
- #include <bcmutils.h>
- #ifdef WL_CFG80211
- #include <wl_cfg80211.h>
- #include <wl_cfgscan.h>
- #endif /* WL_CFG80211 */
- #define htod32(i) (i)
- #define htod16(i) (i)
- #define dtoh32(i) (i)
- #define dtoh16(i) (i)
- #define htodchanspec(i) (i)
- #define dtohchanspec(i) (i)
- #define WLDEV_ERROR(args) \
- do { \
- printk(KERN_ERR "WLDEV-ERROR) "); \
- printk args; \
- } while (0)
- #define WLDEV_INFO(args) \
- do { \
- printk(KERN_INFO "WLDEV-INFO) "); \
- printk args; \
- } while (0)
- extern int dhd_ioctl_entry_local(struct net_device *net, wl_ioctl_t *ioc, int cmd);
- static s32 wldev_ioctl(
- struct net_device *dev, u32 cmd, void *arg, u32 len, u32 set)
- {
- s32 ret = 0;
- struct wl_ioctl ioc;
- memset(&ioc, 0, sizeof(ioc));
- ioc.cmd = cmd;
- ioc.buf = arg;
- ioc.len = len;
- ioc.set = set;
- ret = dhd_ioctl_entry_local(dev, (wl_ioctl_t *)&ioc, cmd);
- return ret;
- }
- /*
- SET commands :
- cast buffer to non-const and call the GET function
- */
- s32 wldev_ioctl_set(
- struct net_device *dev, u32 cmd, const void *arg, u32 len)
- {
- #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wcast-qual"
- #endif // endif
- return wldev_ioctl(dev, cmd, (void *)arg, len, 1);
- #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
- #pragma GCC diagnostic pop
- #endif // endif
- }
- s32 wldev_ioctl_get(
- struct net_device *dev, u32 cmd, void *arg, u32 len)
- {
- return wldev_ioctl(dev, cmd, (void *)arg, len, 0);
- }
- /* Format a iovar buffer, not bsscfg indexed. The bsscfg index will be
- * taken care of in dhd_ioctl_entry. Internal use only, not exposed to
- * wl_iw, wl_cfg80211 and wl_cfgp2p
- */
- static s32 wldev_mkiovar(
- const s8 *iovar_name, const s8 *param, s32 paramlen,
- s8 *iovar_buf, u32 buflen)
- {
- s32 iolen = 0;
- iolen = bcm_mkiovar(iovar_name, param, paramlen, iovar_buf, buflen);
- return iolen;
- }
- s32 wldev_iovar_getbuf(
- struct net_device *dev, s8 *iovar_name,
- const void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync)
- {
- s32 ret = 0;
- if (buf_sync) {
- mutex_lock(buf_sync);
- }
- if (buf && (buflen > 0)) {
- /* initialize the response buffer */
- memset(buf, 0, buflen);
- } else {
- ret = BCME_BADARG;
- goto exit;
- }
- ret = wldev_mkiovar(iovar_name, param, paramlen, buf, buflen);
- if (!ret) {
- ret = BCME_BUFTOOSHORT;
- goto exit;
- }
- ret = wldev_ioctl_get(dev, WLC_GET_VAR, buf, buflen);
- exit:
- if (buf_sync)
- mutex_unlock(buf_sync);
- return ret;
- }
- s32 wldev_iovar_setbuf(
- struct net_device *dev, s8 *iovar_name,
- const void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync)
- {
- s32 ret = 0;
- s32 iovar_len;
- if (buf_sync) {
- mutex_lock(buf_sync);
- }
- iovar_len = wldev_mkiovar(iovar_name, param, paramlen, buf, buflen);
- if (iovar_len > 0)
- ret = wldev_ioctl_set(dev, WLC_SET_VAR, buf, iovar_len);
- else
- ret = BCME_BUFTOOSHORT;
- if (buf_sync)
- mutex_unlock(buf_sync);
- return ret;
- }
- s32 wldev_iovar_setint(
- struct net_device *dev, s8 *iovar, s32 val)
- {
- s8 iovar_buf[WLC_IOCTL_SMLEN];
- val = htod32(val);
- memset(iovar_buf, 0, sizeof(iovar_buf));
- return wldev_iovar_setbuf(dev, iovar, &val, sizeof(val), iovar_buf,
- sizeof(iovar_buf), NULL);
- }
- s32 wldev_iovar_getint(
- struct net_device *dev, s8 *iovar, s32 *pval)
- {
- s8 iovar_buf[WLC_IOCTL_SMLEN];
- s32 err;
- memset(iovar_buf, 0, sizeof(iovar_buf));
- err = wldev_iovar_getbuf(dev, iovar, pval, sizeof(*pval), iovar_buf,
- sizeof(iovar_buf), NULL);
- if (err == 0)
- {
- memcpy(pval, iovar_buf, sizeof(*pval));
- *pval = dtoh32(*pval);
- }
- return err;
- }
- /** Format a bsscfg indexed iovar buffer. The bsscfg index will be
- * taken care of in dhd_ioctl_entry. Internal use only, not exposed to
- * wl_iw, wl_cfg80211 and wl_cfgp2p
- */
- s32 wldev_mkiovar_bsscfg(
- const s8 *iovar_name, const s8 *param, s32 paramlen,
- s8 *iovar_buf, s32 buflen, s32 bssidx)
- {
- const s8 *prefix = "bsscfg:";
- s8 *p;
- u32 prefixlen;
- u32 namelen;
- u32 iolen;
- /* initialize buffer */
- if (!iovar_buf || buflen <= 0)
- return BCME_BADARG;
- memset(iovar_buf, 0, buflen);
- if (bssidx == 0) {
- return wldev_mkiovar(iovar_name, param, paramlen,
- iovar_buf, buflen);
- }
- prefixlen = (u32) strlen(prefix); /* lengh of bsscfg prefix */
- namelen = (u32) strlen(iovar_name) + 1; /* lengh of iovar name + null */
- iolen = prefixlen + namelen + sizeof(u32) + paramlen;
- if (iolen > (u32)buflen) {
- WLDEV_ERROR(("%s: buffer is too short\n", __FUNCTION__));
- return BCME_BUFTOOSHORT;
- }
- p = (s8 *)iovar_buf;
- /* copy prefix, no null */
- memcpy(p, prefix, prefixlen);
- p += prefixlen;
- /* copy iovar name including null */
- memcpy(p, iovar_name, namelen);
- p += namelen;
- /* bss config index as first param */
- bssidx = htod32(bssidx);
- memcpy(p, &bssidx, sizeof(u32));
- p += sizeof(u32);
- /* parameter buffer follows */
- if (paramlen)
- memcpy(p, param, paramlen);
- return iolen;
- }
- s32 wldev_iovar_getbuf_bsscfg(
- struct net_device *dev, s8 *iovar_name,
- void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync)
- {
- s32 ret = 0;
- if (buf_sync) {
- mutex_lock(buf_sync);
- }
- wldev_mkiovar_bsscfg(iovar_name, param, paramlen, buf, buflen, bsscfg_idx);
- ret = wldev_ioctl_get(dev, WLC_GET_VAR, buf, buflen);
- if (buf_sync) {
- mutex_unlock(buf_sync);
- }
- return ret;
- }
- s32 wldev_iovar_setbuf_bsscfg(
- struct net_device *dev, const s8 *iovar_name,
- const void *param, s32 paramlen,
- void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync)
- {
- s32 ret = 0;
- s32 iovar_len;
- if (buf_sync) {
- mutex_lock(buf_sync);
- }
- iovar_len = wldev_mkiovar_bsscfg(iovar_name, param, paramlen, buf, buflen, bsscfg_idx);
- if (iovar_len > 0)
- ret = wldev_ioctl_set(dev, WLC_SET_VAR, buf, iovar_len);
- else {
- ret = BCME_BUFTOOSHORT;
- }
- if (buf_sync) {
- mutex_unlock(buf_sync);
- }
- return ret;
- }
- s32 wldev_iovar_setint_bsscfg(
- struct net_device *dev, s8 *iovar, s32 val, s32 bssidx)
- {
- s8 iovar_buf[WLC_IOCTL_SMLEN];
- val = htod32(val);
- memset(iovar_buf, 0, sizeof(iovar_buf));
- return wldev_iovar_setbuf_bsscfg(dev, iovar, &val, sizeof(val), iovar_buf,
- sizeof(iovar_buf), bssidx, NULL);
- }
- s32 wldev_iovar_getint_bsscfg(
- struct net_device *dev, s8 *iovar, s32 *pval, s32 bssidx)
- {
- s8 iovar_buf[WLC_IOCTL_SMLEN];
- s32 err;
- memset(iovar_buf, 0, sizeof(iovar_buf));
- err = wldev_iovar_getbuf_bsscfg(dev, iovar, pval, sizeof(*pval), iovar_buf,
- sizeof(iovar_buf), bssidx, NULL);
- if (err == 0)
- {
- memcpy(pval, iovar_buf, sizeof(*pval));
- *pval = dtoh32(*pval);
- }
- return err;
- }
- int wldev_get_link_speed(
- struct net_device *dev, int *plink_speed)
- {
- int error;
- if (!plink_speed)
- return -ENOMEM;
- *plink_speed = 0;
- error = wldev_ioctl_get(dev, WLC_GET_RATE, plink_speed, sizeof(int));
- if (unlikely(error))
- return error;
- /* Convert internal 500Kbps to Kbps */
- *plink_speed *= 500;
- return error;
- }
- int wldev_get_rssi(
- struct net_device *dev, scb_val_t *scb_val)
- {
- int error;
- if (!scb_val)
- return -ENOMEM;
- error = wldev_ioctl_get(dev, WLC_GET_RSSI, scb_val, sizeof(scb_val_t));
- if (unlikely(error))
- return error;
- return error;
- }
- int wldev_get_ssid(
- struct net_device *dev, wlc_ssid_t *pssid)
- {
- int error;
- if (!pssid)
- return -ENOMEM;
- memset(pssid, 0, sizeof(wlc_ssid_t));
- error = wldev_ioctl_get(dev, WLC_GET_SSID, pssid, sizeof(wlc_ssid_t));
- if (unlikely(error))
- return error;
- pssid->SSID_len = dtoh32(pssid->SSID_len);
- return error;
- }
- int wldev_get_band(
- struct net_device *dev, uint *pband)
- {
- int error;
- *pband = 0;
- error = wldev_ioctl_get(dev, WLC_GET_BAND, pband, sizeof(uint));
- return error;
- }
- int wldev_set_band(
- struct net_device *dev, uint band)
- {
- int error = -1;
- if ((band == WLC_BAND_AUTO) || (band == WLC_BAND_5G) || (band == WLC_BAND_2G)) {
- error = wldev_ioctl_set(dev, WLC_SET_BAND, &band, sizeof(band));
- if (!error)
- dhd_bus_band_set(dev, band);
- }
- return error;
- }
- int wldev_get_datarate(struct net_device *dev, int *datarate)
- {
- int error = 0;
- error = wldev_ioctl_get(dev, WLC_GET_RATE, datarate, sizeof(int));
- if (error) {
- return -1;
- } else {
- *datarate = dtoh32(*datarate);
- }
- return error;
- }
- extern chanspec_t
- wl_chspec_driver_to_host(chanspec_t chanspec);
- #define WL_EXTRA_BUF_MAX 2048
- int wldev_get_mode(
- struct net_device *dev, uint8 *cap, uint8 caplen)
- {
- int error = 0;
- int chanspec = 0;
- uint16 band = 0;
- uint16 bandwidth = 0;
- wl_bss_info_t *bss = NULL;
- char* buf = NULL;
- buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
- if (!buf) {
- WLDEV_ERROR(("%s:ENOMEM\n", __FUNCTION__));
- return -ENOMEM;
- }
- *(u32*) buf = htod32(WL_EXTRA_BUF_MAX);
- error = wldev_ioctl_get(dev, WLC_GET_BSS_INFO, (void*)buf, WL_EXTRA_BUF_MAX);
- if (error) {
- WLDEV_ERROR(("%s:failed:%d\n", __FUNCTION__, error));
- kfree(buf);
- buf = NULL;
- return error;
- }
- bss = (wl_bss_info_t*)(buf + 4);
- chanspec = wl_chspec_driver_to_host(bss->chanspec);
- band = chanspec & WL_CHANSPEC_BAND_MASK;
- bandwidth = chanspec & WL_CHANSPEC_BW_MASK;
- if (band == WL_CHANSPEC_BAND_2G) {
- if (bss->n_cap)
- strncpy(cap, "n", caplen);
- else
- strncpy(cap, "bg", caplen);
- } else if (band == WL_CHANSPEC_BAND_5G) {
- if (bandwidth == WL_CHANSPEC_BW_80)
- strncpy(cap, "ac", caplen);
- else if ((bandwidth == WL_CHANSPEC_BW_40) || (bandwidth == WL_CHANSPEC_BW_20)) {
- if ((bss->nbss_cap & 0xf00) && (bss->n_cap))
- strncpy(cap, "n|ac", caplen);
- else if (bss->n_cap)
- strncpy(cap, "n", caplen);
- else if (bss->vht_cap)
- strncpy(cap, "ac", caplen);
- else
- strncpy(cap, "a", caplen);
- } else {
- WLDEV_ERROR(("%s:Mode get failed\n", __FUNCTION__));
- error = BCME_ERROR;
- }
- }
- kfree(buf);
- buf = NULL;
- return error;
- }
- int wldev_set_country(
- struct net_device *dev, char *country_code, bool notify, bool user_enforced, int revinfo)
- {
- int error = -1;
- wl_country_t cspec = {{0}, 0, {0}};
- scb_val_t scbval;
- char smbuf[WLC_IOCTL_SMLEN];
- #ifdef WL_CFG80211
- struct wireless_dev *wdev = ndev_to_wdev(dev);
- struct wiphy *wiphy = wdev->wiphy;
- struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
- #endif /* WL_CFG80211 */
- if (!country_code)
- return error;
- bzero(&scbval, sizeof(scb_val_t));
- error = wldev_iovar_getbuf(dev, "country", NULL, 0, &cspec, sizeof(cspec), NULL);
- if (error < 0) {
- WLDEV_ERROR(("%s: get country failed = %d\n", __FUNCTION__, error));
- return error;
- }
- if ((error < 0) ||
- #ifdef OEM_ANDROID
- dhd_force_country_change(dev) ||
- #endif /* OEM_ANDROID */
- (strncmp(country_code, cspec.ccode, WLC_CNTRY_BUF_SZ) != 0)) {
- #ifdef WL_CFG80211
- if ((user_enforced) && (wl_get_drv_status(cfg, CONNECTED, dev))) {
- #else
- if (user_enforced) {
- #endif /* WL_CFG80211 */
- bzero(&scbval, sizeof(scb_val_t));
- error = wldev_ioctl_set(dev, WLC_DISASSOC,
- &scbval, sizeof(scb_val_t));
- if (error < 0) {
- WLDEV_ERROR(("%s: set country failed due to Disassoc error %d\n",
- __FUNCTION__, error));
- return error;
- }
- }
- wl_cfg80211_scan_abort(cfg);
- cspec.rev = revinfo;
- strlcpy(cspec.country_abbrev, country_code, WLC_CNTRY_BUF_SZ);
- strlcpy(cspec.ccode, country_code, WLC_CNTRY_BUF_SZ);
- dhd_get_customized_country_code(dev, (char *)&cspec.country_abbrev, &cspec);
- error = wldev_iovar_setbuf(dev, "country", &cspec, sizeof(cspec),
- smbuf, sizeof(smbuf), NULL);
- if (error < 0) {
- WLDEV_ERROR(("%s: set country for %s as %s rev %d failed\n",
- __FUNCTION__, country_code, cspec.ccode, cspec.rev));
- return error;
- }
- dhd_bus_country_set(dev, &cspec, notify);
- WLDEV_INFO(("%s: set country for %s as %s rev %d\n",
- __FUNCTION__, country_code, cspec.ccode, cspec.rev));
- }
- return 0;
- }
|