Merge changes Idd374f80,Ife1863b0,I091a6b61,I3962487b,I3f86f231,I4220c551,Ia46fe269,I7afcf86a,I6990cc0e,Ic1084f0b into msm-next

* changes:
  wil6210: add ioctl interface
  wil6210: drop RX probe reponses with low SNR
  wil6210: add sysfs for setting connect SNR threshold
  wil6210: support AOA in FTM session
  wil6210: potential buffer overflow in wmi_evt_aoa_meas
  wil6210: send uevent when creating sysfs files
  wil6210: add option to ignore OTA regulatory hints
  wil6210: add sysfs file for enable/disable fst link loss
  wil6210: add sysfs for thermal throttling configuration
  wil6210: NOC time-out fixes
This commit is contained in:
Linux Build Service Account 2017-11-29 11:21:26 -08:00 committed by Gerrit - the friendly Code Review server
commit 8d4be3237f
13 changed files with 805 additions and 10 deletions

View File

@ -52,3 +52,14 @@ config WIL6210_DEBUGFS
option if you are interested in debugging the driver.
If unsure, say Y to make it easier to debug problems.
config WIL6210_WRITE_IOCTL
bool "wil6210 write ioctl to the device"
depends on WIL6210
default y
---help---
Say Y here to allow write-access from user-space to
the device memory through ioctl. This is useful for
debugging purposes only.
If unsure, say N.

View File

@ -12,6 +12,7 @@ wil6210-y += interrupt.o
wil6210-y += txrx.o
wil6210-y += debug.o
wil6210-y += rx_reorder.o
wil6210-y += ioctl.o
wil6210-y += fw.o
wil6210-y += pm.o
wil6210-y += pmc.o

View File

@ -33,6 +33,10 @@ static struct wiphy_wowlan_support wil_wowlan_support = {
};
#endif
static bool ignore_reg_hints = true;
module_param(ignore_reg_hints, bool, 0444);
MODULE_PARM_DESC(ignore_reg_hints, " Ignore OTA regulatory hints (Default: true)");
#define CHAN60G(_channel, _flags) { \
.band = NL80211_BAND_60GHZ, \
.center_freq = 56160 + (2160 * (_channel)), \
@ -1886,6 +1890,11 @@ static void wil_wiphy_init(struct wiphy *wiphy)
wiphy->vendor_events = wil_nl80211_vendor_events;
wiphy->n_vendor_events = ARRAY_SIZE(wil_nl80211_vendor_events);
if (ignore_reg_hints) {
wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS;
wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE;
}
#ifdef CONFIG_PM
wiphy->wowlan = &wil_wowlan_support;
#endif

View File

@ -38,6 +38,9 @@
/* initial token to use on non-secure FTM measurement */
#define WIL_TOF_FTM_DEFAULT_INITIAL_TOKEN 2
/* maximum AOA burst period, limited by FW */
#define WIL_AOA_MAX_BURST_PERIOD 255
#define WIL_TOF_FTM_MAX_LCI_LENGTH (240)
#define WIL_TOF_FTM_MAX_LCR_LENGTH (240)
@ -62,6 +65,7 @@ nla_policy wil_nl80211_ftm_peer_policy[
[QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAGS] = { .type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_PARAMS] = { .type = NLA_NESTED },
[QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID] = { .type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD] = { .type = NLA_U16 },
[QCA_WLAN_VENDOR_ATTR_FTM_PEER_FREQ] = { .type = NLA_U32 },
};
@ -315,8 +319,8 @@ wil_ftm_cfg80211_start_session(struct wil6210_priv *wil,
struct wmi_tof_session_start_cmd *cmd;
mutex_lock(&wil->ftm.lock);
if (wil->ftm.session_started) {
wil_err(wil, "FTM session already running\n");
if (wil->ftm.session_started || wil->ftm.aoa_started) {
wil_err(wil, "FTM or AOA session already running\n");
rc = -EAGAIN;
goto out;
}
@ -360,6 +364,7 @@ wil_ftm_cfg80211_start_session(struct wil6210_priv *wil,
}
cmd->session_id = cpu_to_le32(WIL_FTM_FW_SESSION_ID);
cmd->aoa_type = request->aoa_type;
cmd->num_of_dest = cpu_to_le16(request->n_peers);
for (i = 0; i < request->n_peers; i++) {
ether_addr_copy(cmd->ftm_dest_info[i].dst_mac,
@ -402,6 +407,8 @@ wil_ftm_cfg80211_start_session(struct wil6210_priv *wil,
request->peers[i].params.burst_duration;
cmd->ftm_dest_info[i].burst_period =
cpu_to_le16(request->peers[i].params.burst_period);
cmd->ftm_dest_info[i].num_burst_per_aoa_meas =
request->peers[i].aoa_burst_period;
}
rc = wmi_send(wil, WMI_TOF_SESSION_START_CMDID, cmd, cmd_len);
@ -487,8 +494,8 @@ wil_aoa_cfg80211_start_measurement(struct wil6210_priv *wil,
mutex_lock(&wil->ftm.lock);
if (wil->ftm.aoa_started) {
wil_err(wil, "AOA measurement already running\n");
if (wil->ftm.aoa_started || wil->ftm.session_started) {
wil_err(wil, "AOA or FTM measurement already running\n");
rc = -EAGAIN;
goto out;
}
@ -529,8 +536,8 @@ void wil_aoa_cfg80211_meas_result(struct wil6210_priv *wil,
mutex_lock(&wil->ftm.lock);
if (!wil->ftm.aoa_started) {
wil_info(wil, "AOA not started, not sending result\n");
if (!wil->ftm.aoa_started && !wil->ftm.session_started) {
wil_info(wil, "AOA/FTM not started, not sending result\n");
goto out;
}
@ -683,6 +690,10 @@ void wil_aoa_evt_meas(struct wil6210_priv *wil,
int data_len = len - offsetof(struct wmi_aoa_meas_event, meas_data);
struct wil_aoa_meas_result *res;
if (data_len < 0) {
wil_err(wil, "AOA event too short (%d)\n", len);
return;
}
data_len = min_t(int, le16_to_cpu(evt->length), data_len);
res = kmalloc(sizeof(*res) + data_len, GFP_KERNEL);
@ -754,6 +765,7 @@ int wil_ftm_start_session(struct wiphy *wiphy, struct wireless_dev *wdev,
struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAX + 1];
struct nlattr *peer;
int rc, n_peers = 0, index = 0, tmp;
u32 aoa_type = 0;
if (!test_bit(WMI_FW_CAPABILITY_FTM, wil->fw_capabilities))
return -ENOTSUPP;
@ -775,6 +787,14 @@ int wil_ftm_start_session(struct wiphy *wiphy, struct wireless_dev *wdev,
return -EINVAL;
}
if (tb[QCA_WLAN_VENDOR_ATTR_AOA_TYPE]) {
aoa_type = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_AOA_TYPE]);
if (aoa_type >= QCA_WLAN_VENDOR_ATTR_AOA_TYPE_MAX) {
wil_err(wil, "invalid AOA type: %d\n", aoa_type);
return -EINVAL;
}
}
nla_for_each_nested(peer, tb[QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEERS],
tmp)
n_peers++;
@ -798,6 +818,7 @@ int wil_ftm_start_session(struct wiphy *wiphy, struct wireless_dev *wdev,
request->session_cookie =
nla_get_u64(tb[QCA_WLAN_VENDOR_ATTR_FTM_SESSION_COOKIE]);
request->aoa_type = aoa_type;
request->n_peers = n_peers;
nla_for_each_nested(peer, tb[QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEERS],
tmp) {
@ -826,6 +847,18 @@ int wil_ftm_start_session(struct wiphy *wiphy, struct wireless_dev *wdev,
if (tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID])
request->peers[index].secure_token_id = nla_get_u8(
tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID]);
if (tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD]) {
request->peers[index].aoa_burst_period = nla_get_u16(
tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD]);
if (request->peers[index].aoa_burst_period >
WIL_AOA_MAX_BURST_PERIOD) {
wil_err(wil, "Invalid AOA burst period at index: %d\n",
index);
rc = -EINVAL;
goto out;
}
}
rc = wil_ftm_parse_meas_params(
wil,
tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_PARAMS],

View File

@ -437,12 +437,14 @@ struct wil_ftm_meas_peer_info {
u32 flags; /* enum qca_wlan_vendor_attr_ftm_peer_meas_flags */
struct wil_ftm_meas_params params;
u8 secure_token_id;
u16 aoa_burst_period; /* 0 if no AOA, >0 every <value> bursts */
};
/* session request, passed to wil_ftm_cfg80211_start_session */
struct wil_ftm_session_request {
u64 session_cookie;
u32 n_peers;
u32 aoa_type; /* enum qca_wlan_vendor_attr_aoa_type */
/* keep last, variable size according to n_peers */
struct wil_ftm_meas_peer_info peers[0];
};

View File

@ -0,0 +1,243 @@
/*
* Copyright (c) 2014,2017 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/uaccess.h>
#include "wil6210.h"
#include <uapi/linux/wil6210_uapi.h>
#define wil_hex_dump_ioctl(prefix_str, buf, len) \
print_hex_dump_debug("DBG[IOC ]" prefix_str, \
DUMP_PREFIX_OFFSET, 16, 1, buf, len, true)
#define wil_dbg_ioctl(wil, fmt, arg...) wil_dbg(wil, "DBG[IOC ]" fmt, ##arg)
#define WIL_PRIV_DATA_MAX_LEN 8192
#define CMD_SET_AP_WPS_P2P_IE "SET_AP_WPS_P2P_IE"
struct wil_android_priv_data {
char *buf;
int used_len;
int total_len;
};
static void __iomem *wil_ioc_addr(struct wil6210_priv *wil, u32 addr,
u32 size, enum wil_memio_op op)
{
void __iomem *a;
u32 off;
switch (op & wil_mmio_addr_mask) {
case wil_mmio_addr_linker:
a = wmi_buffer(wil, cpu_to_le32(addr));
break;
case wil_mmio_addr_ahb:
a = wmi_addr(wil, addr);
break;
case wil_mmio_addr_bar:
a = wmi_addr(wil, addr + WIL6210_FW_HOST_OFF);
break;
default:
wil_err(wil, "Unsupported address mode, op = 0x%08x\n", op);
return NULL;
}
off = a - wil->csr;
if (size >= wil->bar_size - off) {
wil_err(wil,
"Invalid requested block: off(0x%08x) size(0x%08x)\n",
off, size);
return NULL;
}
return a;
}
static int wil_ioc_memio_dword(struct wil6210_priv *wil, void __user *data)
{
struct wil_memio io;
void __iomem *a;
bool need_copy = false;
if (copy_from_user(&io, data, sizeof(io)))
return -EFAULT;
wil_dbg_ioctl(wil, "IO: addr = 0x%08x val = 0x%08x op = 0x%08x\n",
io.addr, io.val, io.op);
a = wil_ioc_addr(wil, io.addr, sizeof(u32), io.op);
if (!a) {
wil_err(wil, "invalid address 0x%08x, op = 0x%08x\n", io.addr,
io.op);
return -EINVAL;
}
/* operation */
switch (io.op & wil_mmio_op_mask) {
case wil_mmio_read:
io.val = readl_relaxed(a);
need_copy = true;
break;
#if defined(CONFIG_WIL6210_WRITE_IOCTL)
case wil_mmio_write:
writel_relaxed(io.val, a);
wmb(); /* make sure write propagated to HW */
break;
#endif
default:
wil_err(wil, "Unsupported operation, op = 0x%08x\n", io.op);
return -EINVAL;
}
if (need_copy) {
wil_dbg_ioctl(wil,
"IO done: addr(0x%08x) val(0x%08x) op(0x%08x)\n",
io.addr, io.val, io.op);
if (copy_to_user(data, &io, sizeof(io)))
return -EFAULT;
}
return 0;
}
static int wil_ioc_memio_block(struct wil6210_priv *wil, void __user *data)
{
struct wil_memio_block io;
void *block;
void __iomem *a;
int rc = 0;
if (copy_from_user(&io, data, sizeof(io)))
return -EFAULT;
wil_dbg_ioctl(wil, "IO: addr = 0x%08x size = 0x%08x op = 0x%08x\n",
io.addr, io.size, io.op);
/* size */
if (io.size % 4) {
wil_err(wil, "size is not multiple of 4: 0x%08x\n", io.size);
return -EINVAL;
}
a = wil_ioc_addr(wil, io.addr, io.size, io.op);
if (!a) {
wil_err(wil, "invalid address 0x%08x, op = 0x%08x\n", io.addr,
io.op);
return -EINVAL;
}
block = kmalloc(io.size, GFP_USER);
if (!block)
return -ENOMEM;
/* operation */
switch (io.op & wil_mmio_op_mask) {
case wil_mmio_read:
wil_memcpy_fromio_32(block, a, io.size);
wil_hex_dump_ioctl("Read ", block, io.size);
if (copy_to_user(io.block, block, io.size)) {
rc = -EFAULT;
goto out_free;
}
break;
#if defined(CONFIG_WIL6210_WRITE_IOCTL)
case wil_mmio_write:
if (copy_from_user(block, io.block, io.size)) {
rc = -EFAULT;
goto out_free;
}
wil_memcpy_toio_32(a, block, io.size);
wmb(); /* make sure write propagated to HW */
wil_hex_dump_ioctl("Write ", block, io.size);
break;
#endif
default:
wil_err(wil, "Unsupported operation, op = 0x%08x\n", io.op);
rc = -EINVAL;
break;
}
out_free:
kfree(block);
return rc;
}
static int wil_ioc_android(struct wil6210_priv *wil, void __user *data)
{
int rc = 0;
char *command;
struct wil_android_priv_data priv_data;
wil_dbg_ioctl(wil, "ioc_android\n");
if (copy_from_user(&priv_data, data, sizeof(priv_data)))
return -EFAULT;
if (priv_data.total_len <= 0 ||
priv_data.total_len >= WIL_PRIV_DATA_MAX_LEN) {
wil_err(wil, "invalid data len %d\n", priv_data.total_len);
return -EINVAL;
}
command = kmalloc(priv_data.total_len + 1, GFP_KERNEL);
if (!command)
return -ENOMEM;
if (copy_from_user(command, priv_data.buf, priv_data.total_len)) {
rc = -EFAULT;
goto out_free;
}
/* Make sure the command is NUL-terminated */
command[priv_data.total_len] = '\0';
wil_dbg_ioctl(wil, "ioc_android: command = %s\n", command);
/* P2P not supported, but WPS is (in AP mode).
* Ignore those in order not to block WPS functionality
* in non-P2P mode.
*/
if (strncasecmp(command, CMD_SET_AP_WPS_P2P_IE,
strlen(CMD_SET_AP_WPS_P2P_IE)) == 0)
rc = 0;
else
rc = -ENOIOCTLCMD;
out_free:
kfree(command);
return rc;
}
int wil_ioctl(struct wil6210_priv *wil, void __user *data, int cmd)
{
int ret;
switch (cmd) {
case WIL_IOCTL_MEMIO:
ret = wil_ioc_memio_dword(wil, data);
break;
case WIL_IOCTL_MEMIO_BLOCK:
ret = wil_ioc_memio_block(wil, data);
break;
case (SIOCDEVPRIVATE + 1):
ret = wil_ioc_android(wil, data);
break;
default:
wil_dbg_ioctl(wil, "Unsupported IOCTL 0x%04x\n", cmd);
return -ENOIOCTLCMD;
}
wil_dbg_ioctl(wil, "ioctl(0x%04x) -> %d\n", cmd, ret);
return ret;
}

View File

@ -190,6 +190,7 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
break;
}
sta->status = wil_sta_unused;
sta->fst_link_loss = false;
}
/* reorder buffers */
for (i = 0; i < WIL_STA_TID_NUM; i++) {
@ -984,6 +985,10 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
if (wil->hw_version == HW_VER_UNKNOWN)
return -ENODEV;
wil_dbg_misc(wil, "Prevent DS in BL & mark FW to set T_POWER_ON=0\n");
wil_s(wil, RGF_USER_USAGE_8, BIT_USER_PREVENT_DEEP_SLEEP |
BIT_USER_SUPPORT_T_POWER_ON_0);
if (wil->platform_ops.notify) {
rc = wil->platform_ops.notify(wil->platform_handle,
WIL_PLATFORM_EVT_PRE_RESET);
@ -1078,8 +1083,15 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
if (wil->ps_profile != WMI_PS_PROFILE_TYPE_DEFAULT)
wil_ps_update(wil, wil->ps_profile);
if (wil->tt_data_set)
wmi_set_tt_cfg(wil, &wil->tt_data);
wil_collect_fw_info(wil);
if (wil->snr_thresh.enabled)
wmi_set_snr_thresh(wil, wil->snr_thresh.omni,
wil->snr_thresh.direct);
if (wil->platform_ops.notify) {
rc = wil->platform_ops.notify(wil->platform_handle,
WIL_PLATFORM_EVT_FW_RDY);

View File

@ -47,12 +47,20 @@ static int wil_stop(struct net_device *ndev)
return wil_down(wil);
}
static int wil_do_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd)
{
struct wil6210_priv *wil = ndev_to_wil(ndev);
return wil_ioctl(wil, ifr->ifr_data, cmd);
}
static const struct net_device_ops wil_netdev_ops = {
.ndo_open = wil_open,
.ndo_stop = wil_stop,
.ndo_start_xmit = wil_start_xmit,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
.ndo_do_ioctl = wil_do_ioctl,
};
static int wil6210_netdev_poll_rx(struct napi_struct *napi, int budget)

View File

@ -94,8 +94,223 @@ static DEVICE_ATTR(ftm_txrx_offset, 0644,
wil_ftm_txrx_offset_sysfs_show,
wil_ftm_txrx_offset_sysfs_store);
static ssize_t
wil_tt_sysfs_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct wil6210_priv *wil = dev_get_drvdata(dev);
ssize_t len;
struct wmi_tt_data tt_data;
int i, rc;
rc = wmi_get_tt_cfg(wil, &tt_data);
if (rc)
return rc;
len = snprintf(buf, PAGE_SIZE, " high max critical\n");
len += snprintf(buf + len, PAGE_SIZE - len, "bb: ");
if (tt_data.bb_enabled)
for (i = 0; i < WMI_NUM_OF_TT_ZONES; ++i)
len += snprintf(buf + len, PAGE_SIZE - len,
"%03d-%03d ",
tt_data.bb_zones[i].temperature_high,
tt_data.bb_zones[i].temperature_low);
else
len += snprintf(buf + len, PAGE_SIZE - len, "* disabled *");
len += snprintf(buf + len, PAGE_SIZE - len, "\nrf: ");
if (tt_data.rf_enabled)
for (i = 0; i < WMI_NUM_OF_TT_ZONES; ++i)
len += snprintf(buf + len, PAGE_SIZE - len,
"%03d-%03d ",
tt_data.rf_zones[i].temperature_high,
tt_data.rf_zones[i].temperature_low);
else
len += snprintf(buf + len, PAGE_SIZE - len, "* disabled *");
len += snprintf(buf + len, PAGE_SIZE - len, "\n");
return len;
}
static ssize_t
wil_tt_sysfs_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct wil6210_priv *wil = dev_get_drvdata(dev);
int i, rc = -EINVAL;
char *token, *dupbuf, *tmp;
struct wmi_tt_data tt_data = {
.bb_enabled = 0,
.rf_enabled = 0,
};
tmp = kmemdup(buf, count + 1, GFP_KERNEL);
if (!tmp)
return -ENOMEM;
tmp[count] = '\0';
dupbuf = tmp;
/* Format for writing is 12 unsigned bytes separated by spaces:
* <bb_z1_h> <bb_z1_l> <bb_z2_h> <bb_z2_l> <bb_z3_h> <bb_z3_l> \
* <rf_z1_h> <rf_z1_l> <rf_z2_h> <rf_z2_l> <rf_z3_h> <rf_z3_l>
* To disable thermal throttling for bb or for rf, use 0 for all
* its six set points.
*/
/* bb */
for (i = 0; i < WMI_NUM_OF_TT_ZONES; ++i) {
token = strsep(&dupbuf, " ");
if (!token)
goto out;
if (kstrtou8(token, 0, &tt_data.bb_zones[i].temperature_high))
goto out;
token = strsep(&dupbuf, " ");
if (!token)
goto out;
if (kstrtou8(token, 0, &tt_data.bb_zones[i].temperature_low))
goto out;
if (tt_data.bb_zones[i].temperature_high > 0 ||
tt_data.bb_zones[i].temperature_low > 0)
tt_data.bb_enabled = 1;
}
/* rf */
for (i = 0; i < WMI_NUM_OF_TT_ZONES; ++i) {
token = strsep(&dupbuf, " ");
if (!token)
goto out;
if (kstrtou8(token, 0, &tt_data.rf_zones[i].temperature_high))
goto out;
token = strsep(&dupbuf, " ");
if (!token)
goto out;
if (kstrtou8(token, 0, &tt_data.rf_zones[i].temperature_low))
goto out;
if (tt_data.rf_zones[i].temperature_high > 0 ||
tt_data.rf_zones[i].temperature_low > 0)
tt_data.rf_enabled = 1;
}
rc = wmi_set_tt_cfg(wil, &tt_data);
if (rc)
goto out;
rc = count;
out:
kfree(tmp);
return rc;
}
static DEVICE_ATTR(thermal_throttling, 0644,
wil_tt_sysfs_show, wil_tt_sysfs_store);
static ssize_t
wil_fst_link_loss_sysfs_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct wil6210_priv *wil = dev_get_drvdata(dev);
ssize_t len = 0;
int i;
for (i = 0; i < ARRAY_SIZE(wil->sta); i++)
if (wil->sta[i].status == wil_sta_connected)
len += snprintf(buf + len, PAGE_SIZE - len,
"[%d] %pM %s\n", i, wil->sta[i].addr,
wil->sta[i].fst_link_loss ?
"On" : "Off");
return len;
}
static ssize_t
wil_fst_link_loss_sysfs_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct wil6210_priv *wil = dev_get_drvdata(dev);
u8 addr[ETH_ALEN];
char *token, *dupbuf, *tmp;
int rc = -EINVAL;
bool fst_link_loss;
tmp = kmemdup(buf, count + 1, GFP_KERNEL);
if (!tmp)
return -ENOMEM;
tmp[count] = '\0';
dupbuf = tmp;
token = strsep(&dupbuf, " ");
if (!token)
goto out;
/* mac address */
if (sscanf(token, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
&addr[0], &addr[1], &addr[2],
&addr[3], &addr[4], &addr[5]) != 6)
goto out;
/* On/Off */
if (strtobool(dupbuf, &fst_link_loss))
goto out;
wil_dbg_misc(wil, "set [%pM] with %d\n", addr, fst_link_loss);
rc = wmi_link_maintain_cfg_write(wil, addr, fst_link_loss);
if (!rc)
rc = count;
out:
kfree(tmp);
return rc;
}
static DEVICE_ATTR(fst_link_loss, 0644,
wil_fst_link_loss_sysfs_show,
wil_fst_link_loss_sysfs_store);
static ssize_t
wil_snr_thresh_sysfs_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct wil6210_priv *wil = dev_get_drvdata(dev);
ssize_t len = 0;
if (wil->snr_thresh.enabled)
len = snprintf(buf, PAGE_SIZE, "omni=%d, direct=%d\n",
wil->snr_thresh.omni, wil->snr_thresh.direct);
return len;
}
static ssize_t
wil_snr_thresh_sysfs_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct wil6210_priv *wil = dev_get_drvdata(dev);
int rc;
short omni, direct;
/* to disable snr threshold, set both omni and direct to 0 */
if (sscanf(buf, "%hd %hd", &omni, &direct) != 2)
return -EINVAL;
rc = wmi_set_snr_thresh(wil, omni, direct);
if (!rc)
rc = count;
return rc;
}
static DEVICE_ATTR(snr_thresh, 0644,
wil_snr_thresh_sysfs_show,
wil_snr_thresh_sysfs_store);
static struct attribute *wil6210_sysfs_entries[] = {
&dev_attr_ftm_txrx_offset.attr,
&dev_attr_thermal_throttling.attr,
&dev_attr_fst_link_loss.attr,
&dev_attr_snr_thresh.attr,
NULL
};
@ -115,6 +330,8 @@ int wil6210_sysfs_init(struct wil6210_priv *wil)
return err;
}
kobject_uevent(&dev->kobj, KOBJ_CHANGE);
return 0;
}
@ -123,4 +340,5 @@ void wil6210_sysfs_remove(struct wil6210_priv *wil)
struct device *dev = wil_to_dev(wil);
sysfs_remove_group(&dev->kobj, &wil6210_attribute_group);
kobject_uevent(&dev->kobj, KOBJ_CHANGE);
}

View File

@ -162,6 +162,9 @@ struct RGF_ICR {
#define RGF_USER_USAGE_6 (0x880018)
#define BIT_USER_OOB_MODE BIT(31)
#define BIT_USER_OOB_R2_MODE BIT(30)
#define RGF_USER_USAGE_8 (0x880020)
#define BIT_USER_PREVENT_DEEP_SLEEP BIT(0)
#define BIT_USER_SUPPORT_T_POWER_ON_0 BIT(1)
#define RGF_USER_HW_MACHINE_STATE (0x8801dc)
#define HW_MACHINE_BOOT_DONE (0x3fffffd)
#define RGF_USER_USER_CPU_0 (0x8801e0)
@ -557,6 +560,7 @@ struct wil_sta_info {
struct wil_tid_crypto_rx tid_crypto_rx[WIL_STA_TID_NUM];
struct wil_tid_crypto_rx group_crypto_rx;
u8 aid; /* 1-254; 0 if unknown/not reported */
bool fst_link_loss;
};
enum {
@ -733,6 +737,13 @@ struct wil6210_priv {
int fw_calib_result;
struct wil_ftm_priv ftm;
bool tt_data_set;
struct wmi_tt_data tt_data;
struct {
bool enabled;
short omni;
short direct;
} snr_thresh;
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
@ -904,6 +915,8 @@ int wmi_ps_dev_profile_cfg(struct wil6210_priv *wil,
int wmi_set_mgmt_retry(struct wil6210_priv *wil, u8 retry_short);
int wmi_get_mgmt_retry(struct wil6210_priv *wil, u8 *retry_short);
int wmi_new_sta(struct wil6210_priv *wil, const u8 *mac, u8 aid);
int wmi_set_tt_cfg(struct wil6210_priv *wil, struct wmi_tt_data *tt_data);
int wmi_get_tt_cfg(struct wil6210_priv *wil, struct wmi_tt_data *tt_data);
int wil_addba_rx_request(struct wil6210_priv *wil, u8 cidxtid,
u8 dialog_token, __le16 ba_param_set,
__le16 ba_timeout, __le16 ba_seq_ctrl);
@ -1003,6 +1016,7 @@ void wil6210_unmask_irq_rx(struct wil6210_priv *wil);
int wil_iftype_nl2wmi(enum nl80211_iftype type);
int wil_ioctl(struct wil6210_priv *wil, void __user *data, int cmd);
int wil_request_firmware(struct wil6210_priv *wil, const char *name,
bool load);
bool wil_fw_verify_file_exists(struct wil6210_priv *wil, const char *name);
@ -1037,5 +1051,11 @@ void wil_ftm_evt_per_dest_res(struct wil6210_priv *wil,
void wil_aoa_evt_meas(struct wil6210_priv *wil,
struct wmi_aoa_meas_event *evt,
int len);
/* link loss */
int wmi_link_maintain_cfg_write(struct wil6210_priv *wil,
const u8 *addr,
bool fst_link_loss);
int wmi_set_snr_thresh(struct wil6210_priv *wil, short omni, short direct);
#endif /* __WIL6210_H__ */

View File

@ -369,7 +369,7 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
s32 signal;
__le16 fc;
u32 d_len;
u16 d_status;
s16 snr;
if (flen < 0) {
wil_err(wil, "MGMT Rx: short event, len %d\n", len);
@ -391,13 +391,13 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
signal = 100 * data->info.rssi;
else
signal = data->info.sqi;
d_status = le16_to_cpu(data->info.status);
snr = le16_to_cpu(data->info.snr); /* 1/4 dB units */
fc = rx_mgmt_frame->frame_control;
wil_dbg_wmi(wil, "MGMT Rx: channel %d MCS %d RSSI %d SQI %d%%\n",
data->info.channel, data->info.mcs, data->info.rssi,
data->info.sqi);
wil_dbg_wmi(wil, "status 0x%04x len %d fc 0x%04x\n", d_status, d_len,
wil_dbg_wmi(wil, "snr %ddB len %d fc 0x%04x\n", snr / 4, d_len,
le16_to_cpu(fc));
wil_dbg_wmi(wil, "qid %d mid %d cid %d\n",
data->info.qid, data->info.mid, data->info.cid);
@ -425,6 +425,11 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
wil_dbg_wmi(wil, "Capability info : 0x%04x\n", cap);
if (wil->snr_thresh.enabled && snr < wil->snr_thresh.omni) {
wil_dbg_wmi(wil, "snr below threshold. dropping\n");
return;
}
bss = cfg80211_inform_bss_frame(wiphy, channel, rx_mgmt_frame,
d_len, signal, GFP_KERNEL);
if (bss) {
@ -1816,6 +1821,67 @@ int wmi_new_sta(struct wil6210_priv *wil, const u8 *mac, u8 aid)
return rc;
}
int wmi_set_tt_cfg(struct wil6210_priv *wil, struct wmi_tt_data *tt_data)
{
int rc;
struct wmi_set_thermal_throttling_cfg_cmd cmd = {
.tt_data = *tt_data,
};
struct {
struct wmi_cmd_hdr wmi;
struct wmi_set_thermal_throttling_cfg_event evt;
} __packed reply;
if (!test_bit(WMI_FW_CAPABILITY_THERMAL_THROTTLING,
wil->fw_capabilities))
return -EOPNOTSUPP;
memset(&reply, 0, sizeof(reply));
rc = wmi_call(wil, WMI_SET_THERMAL_THROTTLING_CFG_CMDID, &cmd,
sizeof(cmd), WMI_SET_THERMAL_THROTTLING_CFG_EVENTID,
&reply, sizeof(reply), 100);
if (rc) {
wil_err(wil, "failed to set thermal throttling\n");
return rc;
}
if (reply.evt.status) {
wil_err(wil, "set thermal throttling failed, error %d\n",
reply.evt.status);
return -EIO;
}
wil->tt_data = *tt_data;
wil->tt_data_set = true;
return 0;
}
int wmi_get_tt_cfg(struct wil6210_priv *wil, struct wmi_tt_data *tt_data)
{
int rc;
struct {
struct wmi_cmd_hdr wmi;
struct wmi_get_thermal_throttling_cfg_event evt;
} __packed reply;
if (!test_bit(WMI_FW_CAPABILITY_THERMAL_THROTTLING,
wil->fw_capabilities))
return -EOPNOTSUPP;
rc = wmi_call(wil, WMI_GET_THERMAL_THROTTLING_CFG_CMDID, NULL, 0,
WMI_GET_THERMAL_THROTTLING_CFG_EVENTID, &reply,
sizeof(reply), 100);
if (rc) {
wil_err(wil, "failed to get thermal throttling\n");
return rc;
}
if (tt_data)
*tt_data = reply.evt.tt_data;
return 0;
}
void wmi_event_flush(struct wil6210_priv *wil)
{
ulong flags;
@ -1912,6 +1978,61 @@ int wmi_resume(struct wil6210_priv *wil)
return reply.evt.status;
}
int wmi_link_maintain_cfg_write(struct wil6210_priv *wil,
const u8 *addr,
bool fst_link_loss)
{
int rc;
int cid = wil_find_cid(wil, addr);
u32 cfg_type;
struct wmi_link_maintain_cfg_write_cmd cmd;
struct {
struct wmi_cmd_hdr wmi;
struct wmi_link_maintain_cfg_write_done_event evt;
} __packed reply;
if (cid < 0)
return cid;
switch (wil->wdev->iftype) {
case NL80211_IFTYPE_STATION:
cfg_type = fst_link_loss ?
WMI_LINK_MAINTAIN_CFG_TYPE_DEFAULT_FST_STA :
WMI_LINK_MAINTAIN_CFG_TYPE_DEFAULT_NORMAL_STA;
break;
case NL80211_IFTYPE_AP:
cfg_type = fst_link_loss ?
WMI_LINK_MAINTAIN_CFG_TYPE_DEFAULT_FST_AP :
WMI_LINK_MAINTAIN_CFG_TYPE_DEFAULT_NORMAL_AP;
break;
default:
wil_err(wil, "Unsupported for iftype %d", wil->wdev->iftype);
return -EINVAL;
}
wil_dbg_misc(wil, "Setting cid:%d with cfg_type:%d\n", cid, cfg_type);
cmd.cfg_type = cpu_to_le32(cfg_type);
cmd.cid = cpu_to_le32(cid);
reply.evt.status = cpu_to_le32(WMI_FW_STATUS_FAILURE);
rc = wmi_call(wil, WMI_LINK_MAINTAIN_CFG_WRITE_CMDID, &cmd, sizeof(cmd),
WMI_LINK_MAINTAIN_CFG_WRITE_DONE_EVENTID, &reply,
sizeof(reply), 250);
if (rc) {
wil_err(wil, "Failed to %s FST link loss",
fst_link_loss ? "enable" : "disable");
} else if (reply.evt.status == WMI_FW_STATUS_SUCCESS) {
wil->sta[cid].fst_link_loss = fst_link_loss;
} else {
wil_err(wil, "WMI_LINK_MAINTAIN_CFG_WRITE_CMDID returned status %d",
reply.evt.status);
rc = -EINVAL;
}
return rc;
}
static bool wmi_evt_call_handler(struct wil6210_priv *wil, int id,
void *d, int len)
{
@ -2034,3 +2155,32 @@ out:
spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
return rc;
}
int wmi_set_snr_thresh(struct wil6210_priv *wil, short omni, short direct)
{
int rc;
struct wmi_set_connect_snr_thr_cmd cmd = {
.enable = true,
.omni_snr_thr = cpu_to_le16(omni),
.direct_snr_thr = cpu_to_le16(direct),
};
if (!test_bit(WMI_FW_CAPABILITY_CONNECT_SNR_THR, wil->fw_capabilities))
return -ENOTSUPP;
if (omni == 0 && direct == 0)
cmd.enable = false;
wil_dbg_wmi(wil, "%s snr thresh omni=%d, direct=%d (1/4 dB units)\n",
cmd.enable ? "enable" : "disable", omni, direct);
rc = wmi_send(wil, WMI_SET_CONNECT_SNR_THR_CMDID, &cmd, sizeof(cmd));
if (rc)
return rc;
wil->snr_thresh.enabled = cmd.enable;
wil->snr_thresh.omni = omni;
wil->snr_thresh.direct = direct;
return 0;
}

View File

@ -71,6 +71,7 @@ enum wmi_fw_capability {
WMI_FW_CAPABILITY_RSSI_REPORTING = 12,
WMI_FW_CAPABILITY_SET_SILENT_RSSI_TABLE = 13,
WMI_FW_CAPABILITY_LO_POWER_CALIB_FROM_OTP = 14,
WMI_FW_CAPABILITY_CONNECT_SNR_THR = 16,
WMI_FW_CAPABILITY_MAX,
};
@ -1821,7 +1822,7 @@ struct wmi_rx_mgmt_info {
u8 range;
u8 sqi;
__le16 stype;
__le16 status;
__le16 snr;
__le32 len;
/* Not resolved when == 0xFFFFFFFF == > Broadcast to all MIDS */
u8 qid;

View File

@ -0,0 +1,87 @@
/*
* Copyright (c) 2014 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef __WIL6210_UAPI_H__
#define __WIL6210_UAPI_H__
#if !defined(__KERNEL__)
#define __user
#endif
#include <linux/sockios.h>
/* Numbers SIOCDEVPRIVATE and SIOCDEVPRIVATE + 1
* are used by Android devices to implement PNO (preferred network offload).
* Albeit it is temporary solution, use different numbers to avoid conflicts
*/
/**
* Perform 32-bit I/O operation to the card memory
*
* User code should arrange data in memory like this:
*
* struct wil_memio io;
* struct ifreq ifr = {
* .ifr_data = &io,
* };
*/
#define WIL_IOCTL_MEMIO (SIOCDEVPRIVATE + 2)
/**
* Perform block I/O operation to the card memory
*
* User code should arrange data in memory like this:
*
* void *buf;
* struct wil_memio_block io = {
* .block = buf,
* };
* struct ifreq ifr = {
* .ifr_data = &io,
* };
*/
#define WIL_IOCTL_MEMIO_BLOCK (SIOCDEVPRIVATE + 3)
/**
* operation to perform
*
* @wil_mmio_op_mask - bits defining operation,
* @wil_mmio_addr_mask - bits defining addressing mode
*/
enum wil_memio_op {
wil_mmio_read = 0,
wil_mmio_write = 1,
wil_mmio_op_mask = 0xff,
wil_mmio_addr_linker = 0 << 8,
wil_mmio_addr_ahb = 1 << 8,
wil_mmio_addr_bar = 2 << 8,
wil_mmio_addr_mask = 0xff00,
};
struct wil_memio {
uint32_t op; /* enum wil_memio_op */
uint32_t addr; /* should be 32-bit aligned */
uint32_t val;
};
struct wil_memio_block {
uint32_t op; /* enum wil_memio_op */
uint32_t addr; /* should be 32-bit aligned */
uint32_t size; /* should be multiple of 4 */
void __user *block; /* block address */
};
#endif /* __WIL6210_UAPI_H__ */