aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/wireless/ath/wil6210
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/ath/wil6210')
-rw-r--r--drivers/net/wireless/ath/wil6210/Kconfig12
-rw-r--r--drivers/net/wireless/ath/wil6210/Makefile23
-rw-r--r--drivers/net/wireless/ath/wil6210/cfg80211.c322
-rw-r--r--drivers/net/wireless/ath/wil6210/dbg_hexdump.h30
-rw-r--r--drivers/net/wireless/ath/wil6210/debug.c69
-rw-r--r--drivers/net/wireless/ath/wil6210/debugfs.c245
-rw-r--r--drivers/net/wireless/ath/wil6210/interrupt.c141
-rw-r--r--drivers/net/wireless/ath/wil6210/main.c380
-rw-r--r--drivers/net/wireless/ath/wil6210/netdev.c91
-rw-r--r--drivers/net/wireless/ath/wil6210/pcie_bus.c41
-rw-r--r--drivers/net/wireless/ath/wil6210/rx_reorder.c201
-rw-r--r--drivers/net/wireless/ath/wil6210/trace.c20
-rw-r--r--drivers/net/wireless/ath/wil6210/trace.h239
-rw-r--r--drivers/net/wireless/ath/wil6210/txrx.c714
-rw-r--r--drivers/net/wireless/ath/wil6210/txrx.h112
-rw-r--r--drivers/net/wireless/ath/wil6210/wil6210.h266
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.c475
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.h401
18 files changed, 2945 insertions, 837 deletions
diff --git a/drivers/net/wireless/ath/wil6210/Kconfig b/drivers/net/wireless/ath/wil6210/Kconfig
index bac3d98a0cf..ce8c0381825 100644
--- a/drivers/net/wireless/ath/wil6210/Kconfig
+++ b/drivers/net/wireless/ath/wil6210/Kconfig
@@ -27,3 +27,15 @@ config WIL6210_ISR_COR
self-clear when accessed for debug purposes, it makes
such monitoring impossible.
Say y unless you debug interrupts
+
+config WIL6210_TRACING
+ bool "wil6210 tracing support"
+ depends on WIL6210
+ depends on EVENT_TRACING
+ default y
+ ---help---
+ Say Y here to enable tracepoints for the wil6210 driver
+ using the kernel tracing infrastructure. Select this
+ option if you are interested in debugging the driver.
+
+ If unsure, say Y to make it easier to debug problems.
diff --git a/drivers/net/wireless/ath/wil6210/Makefile b/drivers/net/wireless/ath/wil6210/Makefile
index 9396dc9fe3c..c7a3465fd02 100644
--- a/drivers/net/wireless/ath/wil6210/Makefile
+++ b/drivers/net/wireless/ath/wil6210/Makefile
@@ -1,13 +1,18 @@
obj-$(CONFIG_WIL6210) += wil6210.o
-wil6210-objs := main.o
-wil6210-objs += netdev.o
-wil6210-objs += cfg80211.o
-wil6210-objs += pcie_bus.o
-wil6210-objs += debugfs.o
-wil6210-objs += wmi.o
-wil6210-objs += interrupt.o
-wil6210-objs += txrx.o
+wil6210-y := main.o
+wil6210-y += netdev.o
+wil6210-y += cfg80211.o
+wil6210-y += pcie_bus.o
+wil6210-y += debugfs.o
+wil6210-y += wmi.o
+wil6210-y += interrupt.o
+wil6210-y += txrx.o
+wil6210-y += debug.o
+wil6210-y += rx_reorder.o
+wil6210-$(CONFIG_WIL6210_TRACING) += trace.o
+
+# for tracing framework to find trace.h
+CFLAGS_trace.o := -I$(src)
-subdir-ccflags-y += -Werror
subdir-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index 116f4e807ae..820d4ebd932 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -14,16 +14,6 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include <linux/kernel.h>
-#include <linux/netdevice.h>
-#include <linux/sched.h>
-#include <linux/etherdevice.h>
-#include <linux/wireless.h>
-#include <linux/ieee80211.h>
-#include <linux/slab.h>
-#include <linux/version.h>
-#include <net/cfg80211.h>
-
#include "wil6210.h"
#include "wmi.h"
@@ -114,41 +104,125 @@ int wil_iftype_nl2wmi(enum nl80211_iftype type)
return -EOPNOTSUPP;
}
-static int wil_cfg80211_get_station(struct wiphy *wiphy,
- struct net_device *ndev,
- u8 *mac, struct station_info *sinfo)
+static int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid,
+ struct station_info *sinfo)
{
- struct wil6210_priv *wil = wiphy_to_wil(wiphy);
- int rc;
struct wmi_notify_req_cmd cmd = {
- .cid = 0,
+ .cid = cid,
.interval_usec = 0,
};
+ struct {
+ struct wil6210_mbox_hdr_wmi wmi;
+ struct wmi_notify_req_done_event evt;
+ } __packed reply;
+ struct wil_net_stats *stats = &wil->sta[cid].stats;
+ int rc;
- if (memcmp(mac, wil->dst_addr[0], ETH_ALEN))
- return -ENOENT;
-
- /* WMI_NOTIFY_REQ_DONE_EVENTID handler fills wil->stats.bf_mcs */
rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, &cmd, sizeof(cmd),
- WMI_NOTIFY_REQ_DONE_EVENTID, NULL, 0, 20);
+ WMI_NOTIFY_REQ_DONE_EVENTID, &reply, sizeof(reply), 20);
if (rc)
return rc;
+ wil_dbg_wmi(wil, "Link status for CID %d: {\n"
+ " MCS %d TSF 0x%016llx\n"
+ " BF status 0x%08x SNR 0x%08x SQI %d%%\n"
+ " Tx Tpt %d goodput %d Rx goodput %d\n"
+ " Sectors(rx:tx) my %d:%d peer %d:%d\n""}\n",
+ cid, le16_to_cpu(reply.evt.bf_mcs),
+ le64_to_cpu(reply.evt.tsf), reply.evt.status,
+ le32_to_cpu(reply.evt.snr_val),
+ reply.evt.sqi,
+ le32_to_cpu(reply.evt.tx_tpt),
+ le32_to_cpu(reply.evt.tx_goodput),
+ le32_to_cpu(reply.evt.rx_goodput),
+ le16_to_cpu(reply.evt.my_rx_sector),
+ le16_to_cpu(reply.evt.my_tx_sector),
+ le16_to_cpu(reply.evt.other_rx_sector),
+ le16_to_cpu(reply.evt.other_tx_sector));
+
sinfo->generation = wil->sinfo_gen;
- sinfo->filled |= STATION_INFO_TX_BITRATE;
+ sinfo->filled = STATION_INFO_RX_BYTES |
+ STATION_INFO_TX_BYTES |
+ STATION_INFO_RX_PACKETS |
+ STATION_INFO_TX_PACKETS |
+ STATION_INFO_RX_BITRATE |
+ STATION_INFO_TX_BITRATE |
+ STATION_INFO_RX_DROP_MISC |
+ STATION_INFO_TX_FAILED;
+
sinfo->txrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G;
- sinfo->txrate.mcs = wil->stats.bf_mcs;
- sinfo->filled |= STATION_INFO_RX_BITRATE;
+ sinfo->txrate.mcs = le16_to_cpu(reply.evt.bf_mcs);
sinfo->rxrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G;
- sinfo->rxrate.mcs = wil->stats.last_mcs_rx;
+ sinfo->rxrate.mcs = stats->last_mcs_rx;
+ sinfo->rx_bytes = stats->rx_bytes;
+ sinfo->rx_packets = stats->rx_packets;
+ sinfo->rx_dropped_misc = stats->rx_dropped;
+ sinfo->tx_bytes = stats->tx_bytes;
+ sinfo->tx_packets = stats->tx_packets;
+ sinfo->tx_failed = stats->tx_errors;
if (test_bit(wil_status_fwconnected, &wil->status)) {
sinfo->filled |= STATION_INFO_SIGNAL;
- sinfo->signal = 12; /* TODO: provide real value */
+ sinfo->signal = reply.evt.sqi;
}
- return 0;
+ return rc;
+}
+
+static int wil_cfg80211_get_station(struct wiphy *wiphy,
+ struct net_device *ndev,
+ const u8 *mac, struct station_info *sinfo)
+{
+ struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ int rc;
+
+ int cid = wil_find_cid(wil, mac);
+
+ wil_dbg_misc(wil, "%s(%pM) CID %d\n", __func__, mac, cid);
+ if (cid < 0)
+ return cid;
+
+ rc = wil_cid_fill_sinfo(wil, cid, sinfo);
+
+ return rc;
+}
+
+/*
+ * Find @idx-th active STA for station dump.
+ */
+static int wil_find_cid_by_idx(struct wil6210_priv *wil, int idx)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
+ if (wil->sta[i].status == wil_sta_unused)
+ continue;
+ if (idx == 0)
+ return i;
+ idx--;
+ }
+
+ return -ENOENT;
+}
+
+static int wil_cfg80211_dump_station(struct wiphy *wiphy,
+ struct net_device *dev, int idx,
+ u8 *mac, struct station_info *sinfo)
+{
+ struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ int rc;
+ int cid = wil_find_cid_by_idx(wil, idx);
+
+ if (cid < 0)
+ return -ENOENT;
+
+ memcpy(mac, wil->sta[cid].addr, ETH_ALEN);
+ wil_dbg_misc(wil, "%s(%pM) CID %d\n", __func__, mac, cid);
+
+ rc = wil_cid_fill_sinfo(wil, cid, sinfo);
+
+ return rc;
}
static int wil_cfg80211_change_iface(struct wiphy *wiphy,
@@ -191,6 +265,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
u16 chnl[4];
} __packed cmd;
uint i, n;
+ int rc;
if (wil->scan_request) {
wil_err(wil, "Already scanning\n");
@@ -204,16 +279,16 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
break;
default:
return -EOPNOTSUPP;
-
}
/* FW don't support scan after connection attempt */
if (test_bit(wil_status_dontscan, &wil->status)) {
- wil_err(wil, "Scan after connect attempt not supported\n");
+ wil_err(wil, "Can't scan now\n");
return -EBUSY;
}
wil->scan_request = request;
+ mod_timer(&wil->scan_timer, jiffies + WIL6210_SCAN_TO);
memset(&cmd, 0, sizeof(cmd));
cmd.cmd.num_channels = 0;
@@ -228,12 +303,17 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
}
/* 0-based channel indexes */
cmd.cmd.channel_list[cmd.cmd.num_channels++].channel = ch - 1;
- wil_dbg(wil, "Scan for ch %d : %d MHz\n", ch,
- request->channels[i]->center_freq);
+ wil_dbg_misc(wil, "Scan for ch %d : %d MHz\n", ch,
+ request->channels[i]->center_freq);
}
- return wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) +
+ rc = wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) +
cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0]));
+
+ if (rc)
+ wil->scan_request = NULL;
+
+ return rc;
}
static int wil_cfg80211_connect(struct wiphy *wiphy,
@@ -248,6 +328,10 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
int ch;
int rc = 0;
+ if (test_bit(wil_status_fwconnecting, &wil->status) ||
+ test_bit(wil_status_fwconnected, &wil->status))
+ return -EALREADY;
+
bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid,
sme->ssid, sme->ssid_len,
WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
@@ -293,7 +377,7 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
/* WMI_CONNECT_CMD */
memset(&conn, 0, sizeof(conn));
- switch (bss->capability & 0x03) {
+ switch (bss->capability & WLAN_CAPABILITY_DMG_TYPE_MASK) {
case WLAN_CAPABILITY_DMG_TYPE_AP:
conn.network_type = WMI_NETTYPE_INFRA;
break;
@@ -327,22 +411,22 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
}
conn.channel = ch - 1;
- memcpy(conn.bssid, bss->bssid, 6);
- memcpy(conn.dst_mac, bss->bssid, 6);
- /*
- * FW don't support scan after connection attempt
- */
- set_bit(wil_status_dontscan, &wil->status);
+ memcpy(conn.bssid, bss->bssid, ETH_ALEN);
+ memcpy(conn.dst_mac, bss->bssid, ETH_ALEN);
+
+ set_bit(wil_status_fwconnecting, &wil->status);
rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn));
if (rc == 0) {
/* Connect can take lots of time */
mod_timer(&wil->connect_timer,
jiffies + msecs_to_jiffies(2000));
+ } else {
+ clear_bit(wil_status_fwconnecting, &wil->status);
}
out:
- cfg80211_put_bss(bss);
+ cfg80211_put_bss(wiphy, bss);
return rc;
}
@@ -359,6 +443,40 @@ static int wil_cfg80211_disconnect(struct wiphy *wiphy,
return rc;
}
+static int wil_cfg80211_mgmt_tx(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct cfg80211_mgmt_tx_params *params,
+ u64 *cookie)
+{
+ const u8 *buf = params->buf;
+ size_t len = params->len;
+ struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ int rc;
+ struct ieee80211_mgmt *mgmt_frame = (void *)buf;
+ struct wmi_sw_tx_req_cmd *cmd;
+ struct {
+ struct wil6210_mbox_hdr_wmi wmi;
+ struct wmi_sw_tx_complete_event evt;
+ } __packed evt;
+
+ cmd = kmalloc(sizeof(*cmd) + len, GFP_KERNEL);
+ if (!cmd)
+ return -ENOMEM;
+
+ memcpy(cmd->dst_mac, mgmt_frame->da, WMI_MAC_LEN);
+ cmd->len = cpu_to_le16(len);
+ memcpy(cmd->payload, buf, len);
+
+ rc = wmi_call(wil, WMI_SW_TX_REQ_CMDID, cmd, sizeof(*cmd) + len,
+ WMI_SW_TX_COMPLETE_EVENTID, &evt, sizeof(evt), 2000);
+ if (rc == 0)
+ rc = evt.evt.status;
+
+ kfree(cmd);
+
+ return rc;
+}
+
static int wil_cfg80211_set_channel(struct wiphy *wiphy,
struct cfg80211_chan_def *chandef)
{
@@ -409,6 +527,65 @@ static int wil_cfg80211_set_default_key(struct wiphy *wiphy,
return 0;
}
+static int wil_remain_on_channel(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct ieee80211_channel *chan,
+ unsigned int duration,
+ u64 *cookie)
+{
+ struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ int rc;
+
+ /* TODO: handle duration */
+ wil_info(wil, "%s(%d, %d ms)\n", __func__, chan->center_freq, duration);
+
+ rc = wmi_set_channel(wil, chan->hw_value);
+ if (rc)
+ return rc;
+
+ rc = wmi_rxon(wil, true);
+
+ return rc;
+}
+
+static int wil_cancel_remain_on_channel(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ u64 cookie)
+{
+ struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ int rc;
+
+ wil_info(wil, "%s()\n", __func__);
+
+ rc = wmi_rxon(wil, false);
+
+ return rc;
+}
+
+static int wil_fix_bcon(struct wil6210_priv *wil,
+ struct cfg80211_beacon_data *bcon)
+{
+ struct ieee80211_mgmt *f = (struct ieee80211_mgmt *)bcon->probe_resp;
+ size_t hlen = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+ int rc = 0;
+
+ if (bcon->probe_resp_len <= hlen)
+ return 0;
+
+ if (!bcon->proberesp_ies) {
+ bcon->proberesp_ies = f->u.probe_resp.variable;
+ bcon->proberesp_ies_len = bcon->probe_resp_len - hlen;
+ rc = 1;
+ }
+ if (!bcon->assocresp_ies) {
+ bcon->assocresp_ies = f->u.probe_resp.variable;
+ bcon->assocresp_ies_len = bcon->probe_resp_len - hlen;
+ rc = 1;
+ }
+
+ return rc;
+}
+
static int wil_cfg80211_start_ap(struct wiphy *wiphy,
struct net_device *ndev,
struct cfg80211_ap_settings *info)
@@ -425,30 +602,41 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
return -EINVAL;
}
- wil_dbg(wil, "AP on Channel %d %d MHz, %s\n", channel->hw_value,
- channel->center_freq, info->privacy ? "secure" : "open");
+ wil_dbg_misc(wil, "AP on Channel %d %d MHz, %s\n", channel->hw_value,
+ channel->center_freq, info->privacy ? "secure" : "open");
print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET,
info->ssid, info->ssid_len);
+ if (wil_fix_bcon(wil, bcon))
+ wil_dbg_misc(wil, "Fixed bcon\n");
+
+ mutex_lock(&wil->mutex);
+
rc = wil_reset(wil);
if (rc)
- return rc;
+ goto out;
- rc = wmi_set_ssid(wil, info->ssid_len, info->ssid);
+ /* Rx VRING. */
+ rc = wil_rx_init(wil);
if (rc)
- return rc;
+ goto out;
- rc = wmi_set_channel(wil, channel->hw_value);
+ rc = wmi_set_ssid(wil, info->ssid_len, info->ssid);
if (rc)
- return rc;
+ goto out;
/* MAC address - pre-requisite for other commands */
wmi_set_mac_address(wil, ndev->dev_addr);
/* IE's */
/* bcon 'head IE's are not relevant for 60g band */
- wmi_set_ie(wil, WMI_FRAME_BEACON, bcon->beacon_ies_len,
- bcon->beacon_ies);
+ /*
+ * FW do not form regular beacon, so bcon IE's are not set
+ * For the DMG bcon, when it will be supported, bcon IE's will
+ * be reused; add something like:
+ * wmi_set_ie(wil, WMI_FRAME_BEACON, bcon->beacon_ies_len,
+ * bcon->beacon_ies);
+ */
wmi_set_ie(wil, WMI_FRAME_PROBE_RESP, bcon->proberesp_ies_len,
bcon->proberesp_ies);
wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP, bcon->assocresp_ies_len,
@@ -456,15 +644,16 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
wil->secure_pcp = info->privacy;
- rc = wmi_set_bcon(wil, info->beacon_interval, wmi_nettype);
+ rc = wmi_pcp_start(wil, info->beacon_interval, wmi_nettype,
+ channel->hw_value);
if (rc)
- return rc;
+ goto out;
- /* Rx VRING. After MAC and beacon */
- rc = wil_rx_init(wil);
netif_carrier_on(ndev);
+out:
+ mutex_unlock(&wil->mutex);
return rc;
}
@@ -473,21 +662,37 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
{
int rc = 0;
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
- struct wireless_dev *wdev = ndev->ieee80211_ptr;
- u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype);
- /* To stop beaconing, set BI to 0 */
- rc = wmi_set_bcon(wil, 0, wmi_nettype);
+ mutex_lock(&wil->mutex);
+
+ rc = wmi_pcp_stop(wil);
+ mutex_unlock(&wil->mutex);
return rc;
}
+static int wil_cfg80211_del_station(struct wiphy *wiphy,
+ struct net_device *dev, const u8 *mac)
+{
+ struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+
+ mutex_lock(&wil->mutex);
+ wil6210_disconnect(wil, mac);
+ mutex_unlock(&wil->mutex);
+
+ return 0;
+}
+
static struct cfg80211_ops wil_cfg80211_ops = {
.scan = wil_cfg80211_scan,
.connect = wil_cfg80211_connect,
.disconnect = wil_cfg80211_disconnect,
.change_virtual_intf = wil_cfg80211_change_iface,
.get_station = wil_cfg80211_get_station,
+ .dump_station = wil_cfg80211_dump_station,
+ .remain_on_channel = wil_remain_on_channel,
+ .cancel_remain_on_channel = wil_cancel_remain_on_channel,
+ .mgmt_tx = wil_cfg80211_mgmt_tx,
.set_monitor_channel = wil_cfg80211_set_channel,
.add_key = wil_cfg80211_add_key,
.del_key = wil_cfg80211_del_key,
@@ -495,6 +700,7 @@ static struct cfg80211_ops wil_cfg80211_ops = {
/* AP mode */
.start_ap = wil_cfg80211_start_ap,
.stop_ap = wil_cfg80211_stop_ap,
+ .del_station = wil_cfg80211_del_station,
};
static void wil_wiphy_init(struct wiphy *wiphy)
@@ -520,7 +726,7 @@ static void wil_wiphy_init(struct wiphy *wiphy)
wiphy->bands[IEEE80211_BAND_60GHZ] = &wil_band_60ghz;
/* TODO: figure this out */
- wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+ wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC;
wiphy->cipher_suites = wil_cipher_suites;
wiphy->n_cipher_suites = ARRAY_SIZE(wil_cipher_suites);
diff --git a/drivers/net/wireless/ath/wil6210/dbg_hexdump.h b/drivers/net/wireless/ath/wil6210/dbg_hexdump.h
deleted file mode 100644
index 6a315ba5aa7..00000000000
--- a/drivers/net/wireless/ath/wil6210/dbg_hexdump.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#ifndef WIL_DBG_HEXDUMP_H_
-#define WIL_DBG_HEXDUMP_H_
-
-#if defined(CONFIG_DYNAMIC_DEBUG)
-#define wil_dynamic_hex_dump(prefix_str, prefix_type, rowsize, \
- groupsize, buf, len, ascii) \
-do { \
- DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, \
- __builtin_constant_p(prefix_str) ? prefix_str : "hexdump");\
- if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT)) \
- print_hex_dump(KERN_DEBUG, prefix_str, \
- prefix_type, rowsize, groupsize, \
- buf, len, ascii); \
-} while (0)
-
-#define wil_print_hex_dump_debug(prefix_str, prefix_type, rowsize, \
- groupsize, buf, len, ascii) \
- wil_dynamic_hex_dump(prefix_str, prefix_type, rowsize, \
- groupsize, buf, len, ascii)
-
-#define print_hex_dump_bytes(prefix_str, prefix_type, buf, len) \
- wil_dynamic_hex_dump(prefix_str, prefix_type, 16, 1, buf, len, true)
-#else /* defined(CONFIG_DYNAMIC_DEBUG) */
-#define wil_print_hex_dump_debug(prefix_str, prefix_type, rowsize, \
- groupsize, buf, len, ascii) \
- print_hex_dump(KERN_DEBUG, prefix_str, prefix_type, rowsize, \
- groupsize, buf, len, ascii)
-#endif /* defined(CONFIG_DYNAMIC_DEBUG) */
-
-#endif /* WIL_DBG_HEXDUMP_H_ */
diff --git a/drivers/net/wireless/ath/wil6210/debug.c b/drivers/net/wireless/ath/wil6210/debug.c
new file mode 100644
index 00000000000..9eeabf4a587
--- /dev/null
+++ b/drivers/net/wireless/ath/wil6210/debug.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2013 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 "wil6210.h"
+#include "trace.h"
+
+int wil_err(struct wil6210_priv *wil, const char *fmt, ...)
+{
+ struct net_device *ndev = wil_to_ndev(wil);
+ struct va_format vaf = {
+ .fmt = fmt,
+ };
+ va_list args;
+ int ret;
+
+ va_start(args, fmt);
+ vaf.va = &args;
+ ret = netdev_err(ndev, "%pV", &vaf);
+ trace_wil6210_log_err(&vaf);
+ va_end(args);
+
+ return ret;
+}
+
+int wil_info(struct wil6210_priv *wil, const char *fmt, ...)
+{
+ struct net_device *ndev = wil_to_ndev(wil);
+ struct va_format vaf = {
+ .fmt = fmt,
+ };
+ va_list args;
+ int ret;
+
+ va_start(args, fmt);
+ vaf.va = &args;
+ ret = netdev_info(ndev, "%pV", &vaf);
+ trace_wil6210_log_info(&vaf);
+ va_end(args);
+
+ return ret;
+}
+
+int wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...)
+{
+ struct va_format vaf = {
+ .fmt = fmt,
+ };
+ va_list args;
+
+ va_start(args, fmt);
+ vaf.va = &args;
+ trace_wil6210_log_dbg(&vaf);
+ va_end(args);
+
+ return 0;
+}
diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c
index 65fc9683bfd..8d4bc4bfb66 100644
--- a/drivers/net/wireless/ath/wil6210/debugfs.c
+++ b/drivers/net/wireless/ath/wil6210/debugfs.c
@@ -26,14 +26,16 @@
/* Nasty hack. Better have per device instances */
static u32 mem_addr;
static u32 dbg_txdesc_index;
+static u32 dbg_vring_index; /* 24+ for Rx, 0..23 for Tx */
static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil,
- const char *name, struct vring *vring)
+ const char *name, struct vring *vring,
+ char _s, char _h)
{
void __iomem *x = wmi_addr(wil, vring->hwtail);
seq_printf(s, "VRING %s = {\n", name);
- seq_printf(s, " pa = 0x%016llx\n", (unsigned long long)vring->pa);
+ seq_printf(s, " pa = %pad\n", &vring->pa);
seq_printf(s, " va = 0x%p\n", vring->va);
seq_printf(s, " size = %d\n", vring->size);
seq_printf(s, " swtail = %d\n", vring->swtail);
@@ -50,8 +52,8 @@ static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil,
volatile struct vring_tx_desc *d = &vring->va[i].tx;
if ((i % 64) == 0 && (i != 0))
seq_printf(s, "\n");
- seq_printf(s, "%s", (d->dma.status & BIT(0)) ?
- "S" : (vring->ctx[i] ? "H" : "h"));
+ seq_printf(s, "%c", (d->dma.status & BIT(0)) ?
+ _s : (vring->ctx[i].skb ? _h : 'h'));
}
seq_printf(s, "\n");
}
@@ -63,14 +65,19 @@ static int wil_vring_debugfs_show(struct seq_file *s, void *data)
uint i;
struct wil6210_priv *wil = s->private;
- wil_print_vring(s, wil, "rx", &wil->vring_rx);
+ wil_print_vring(s, wil, "rx", &wil->vring_rx, 'S', '_');
for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) {
struct vring *vring = &(wil->vring_tx[i]);
if (vring->va) {
+ int cid = wil->vring2cid_tid[i][0];
+ int tid = wil->vring2cid_tid[i][1];
char name[10];
snprintf(name, sizeof(name), "tx_%2d", i);
- wil_print_vring(s, wil, name, vring);
+
+ seq_printf(s, "\n%pM CID %d TID %d\n",
+ wil->sta[cid].addr, cid, tid);
+ wil_print_vring(s, wil, name, vring, '_', 'H');
}
}
@@ -145,7 +152,7 @@ static void wil_print_ring(struct seq_file *s, const char *prefix,
le16_to_cpu(hdr.type), hdr.flags);
if (len <= MAX_MBOXITEM_SIZE) {
int n = 0;
- unsigned char printbuf[16 * 3 + 2];
+ char printbuf[16 * 3 + 2];
unsigned char databuf[MAX_MBOXITEM_SIZE];
void __iomem *src = wmi_buffer(wil, d.addr) +
sizeof(struct wil6210_mbox_hdr);
@@ -216,7 +223,7 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x32, wil_debugfs_iomem_x32_get,
wil_debugfs_iomem_x32_set, "0x%08llx\n");
static struct dentry *wil_debugfs_create_iomem_x32(const char *name,
- mode_t mode,
+ umode_t mode,
struct dentry *parent,
void __iomem *value)
{
@@ -312,14 +319,6 @@ static const struct file_operations fops_memread = {
.llseek = seq_lseek,
};
-static int wil_default_open(struct inode *inode, struct file *file)
-{
- if (inode->i_private)
- file->private_data = inode->i_private;
-
- return 0;
-}
-
static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
@@ -361,13 +360,13 @@ static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf,
static const struct file_operations fops_ioblob = {
.read = wil_read_file_ioblob,
- .open = wil_default_open,
+ .open = simple_open,
.llseek = default_llseek,
};
static
struct dentry *wil_debugfs_create_ioblob(const char *name,
- mode_t mode,
+ umode_t mode,
struct dentry *parent,
struct debugfs_blob_wrapper *blob)
{
@@ -396,53 +395,100 @@ static ssize_t wil_write_file_reset(struct file *file, const char __user *buf,
static const struct file_operations fops_reset = {
.write = wil_write_file_reset,
- .open = wil_default_open,
+ .open = simple_open,
};
-/*---------Tx descriptor------------*/
+static void wil_seq_hexdump(struct seq_file *s, void *p, int len,
+ const char *prefix)
+{
+ char printbuf[16 * 3 + 2];
+ int i = 0;
+ while (i < len) {
+ int l = min(len - i, 16);
+ hex_dump_to_buffer(p + i, l, 16, 1, printbuf,
+ sizeof(printbuf), false);
+ seq_printf(s, "%s%s\n", prefix, printbuf);
+ i += l;
+ }
+}
+
+static void wil_seq_print_skb(struct seq_file *s, struct sk_buff *skb)
+{
+ int i = 0;
+ int len = skb_headlen(skb);
+ void *p = skb->data;
+ int nr_frags = skb_shinfo(skb)->nr_frags;
+
+ seq_printf(s, " len = %d\n", len);
+ wil_seq_hexdump(s, p, len, " : ");
+
+ if (nr_frags) {
+ seq_printf(s, " nr_frags = %d\n", nr_frags);
+ for (i = 0; i < nr_frags; i++) {
+ const struct skb_frag_struct *frag =
+ &skb_shinfo(skb)->frags[i];
+
+ len = skb_frag_size(frag);
+ p = skb_frag_address_safe(frag);
+ seq_printf(s, " [%2d] : len = %d\n", i, len);
+ wil_seq_hexdump(s, p, len, " : ");
+ }
+ }
+}
+
+/*---------Tx/Rx descriptor------------*/
static int wil_txdesc_debugfs_show(struct seq_file *s, void *data)
{
struct wil6210_priv *wil = s->private;
- struct vring *vring = &(wil->vring_tx[0]);
+ struct vring *vring;
+ bool tx = (dbg_vring_index < WIL6210_MAX_TX_RINGS);
+ if (tx)
+ vring = &(wil->vring_tx[dbg_vring_index]);
+ else
+ vring = &wil->vring_rx;
if (!vring->va) {
- seq_printf(s, "No Tx VRING\n");
+ if (tx)
+ seq_printf(s, "No Tx[%2d] VRING\n", dbg_vring_index);
+ else
+ seq_puts(s, "No Rx VRING\n");
return 0;
}
if (dbg_txdesc_index < vring->size) {
+ /* use struct vring_tx_desc for Rx as well,
+ * only field used, .dma.length, is the same
+ */
volatile struct vring_tx_desc *d =
&(vring->va[dbg_txdesc_index].tx);
volatile u32 *u = (volatile u32 *)d;
- struct sk_buff *skb = vring->ctx[dbg_txdesc_index];
+ struct sk_buff *skb = vring->ctx[dbg_txdesc_index].skb;
- seq_printf(s, "Tx[%3d] = {\n", dbg_txdesc_index);
+ if (tx)
+ seq_printf(s, "Tx[%2d][%3d] = {\n", dbg_vring_index,
+ dbg_txdesc_index);
+ else
+ seq_printf(s, "Rx[%3d] = {\n", dbg_txdesc_index);
seq_printf(s, " MAC = 0x%08x 0x%08x 0x%08x 0x%08x\n",
u[0], u[1], u[2], u[3]);
seq_printf(s, " DMA = 0x%08x 0x%08x 0x%08x 0x%08x\n",
u[4], u[5], u[6], u[7]);
- seq_printf(s, " SKB = %p\n", skb);
+ seq_printf(s, " SKB = 0x%p\n", skb);
if (skb) {
- unsigned char printbuf[16 * 3 + 2];
- int i = 0;
- int len = skb_headlen(skb);
- void *p = skb->data;
-
- seq_printf(s, " len = %d\n", len);
-
- while (i < len) {
- int l = min(len - i, 16);
- hex_dump_to_buffer(p + i, l, 16, 1, printbuf,
- sizeof(printbuf), false);
- seq_printf(s, " : %s\n", printbuf);
- i += l;
- }
+ skb_get(skb);
+ wil_seq_print_skb(s, skb);
+ kfree_skb(skb);
}
seq_printf(s, "}\n");
} else {
- seq_printf(s, "TxDesc index (%d) >= size (%d)\n",
- dbg_txdesc_index, vring->size);
+ if (tx)
+ seq_printf(s, "[%2d] TxDesc index (%d) >= size (%d)\n",
+ dbg_vring_index, dbg_txdesc_index,
+ vring->size);
+ else
+ seq_printf(s, "RxDesc index (%d) >= size (%d)\n",
+ dbg_txdesc_index, vring->size);
}
return 0;
@@ -526,7 +572,113 @@ static ssize_t wil_write_file_ssid(struct file *file, const char __user *buf,
static const struct file_operations fops_ssid = {
.read = wil_read_file_ssid,
.write = wil_write_file_ssid,
- .open = wil_default_open,
+ .open = simple_open,
+};
+
+/*---------temp------------*/
+static void print_temp(struct seq_file *s, const char *prefix, u32 t)
+{
+ switch (t) {
+ case 0:
+ case ~(u32)0:
+ seq_printf(s, "%s N/A\n", prefix);
+ break;
+ default:
+ seq_printf(s, "%s %d.%03d\n", prefix, t / 1000, t % 1000);
+ break;
+ }
+}
+
+static int wil_temp_debugfs_show(struct seq_file *s, void *data)
+{
+ struct wil6210_priv *wil = s->private;
+ u32 t_m, t_r;
+
+ int rc = wmi_get_temperature(wil, &t_m, &t_r);
+ if (rc) {
+ seq_printf(s, "Failed\n");
+ return 0;
+ }
+
+ print_temp(s, "MAC temperature :", t_m);
+ print_temp(s, "Radio temperature :", t_r);
+
+ return 0;
+}
+
+static int wil_temp_seq_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, wil_temp_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations fops_temp = {
+ .open = wil_temp_seq_open,
+ .release = single_release,
+ .read = seq_read,
+ .llseek = seq_lseek,
+};
+
+/*---------Station matrix------------*/
+static void wil_print_rxtid(struct seq_file *s, struct wil_tid_ampdu_rx *r)
+{
+ int i;
+ u16 index = ((r->head_seq_num - r->ssn) & 0xfff) % r->buf_size;
+ seq_printf(s, "0x%03x [", r->head_seq_num);
+ for (i = 0; i < r->buf_size; i++) {
+ if (i == index)
+ seq_printf(s, "%c", r->reorder_buf[i] ? 'O' : '|');
+ else
+ seq_printf(s, "%c", r->reorder_buf[i] ? '*' : '_');
+ }
+ seq_puts(s, "]\n");
+}
+
+static int wil_sta_debugfs_show(struct seq_file *s, void *data)
+{
+ struct wil6210_priv *wil = s->private;
+ int i, tid;
+
+ for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
+ struct wil_sta_info *p = &wil->sta[i];
+ char *status = "unknown";
+ switch (p->status) {
+ case wil_sta_unused:
+ status = "unused ";
+ break;
+ case wil_sta_conn_pending:
+ status = "pending ";
+ break;
+ case wil_sta_connected:
+ status = "connected";
+ break;
+ }
+ seq_printf(s, "[%d] %pM %s%s\n", i, p->addr, status,
+ (p->data_port_open ? " data_port_open" : ""));
+
+ if (p->status == wil_sta_connected) {
+ for (tid = 0; tid < WIL_STA_TID_NUM; tid++) {
+ struct wil_tid_ampdu_rx *r = p->tid_rx[tid];
+ if (r) {
+ seq_printf(s, "[%2d] ", tid);
+ wil_print_rxtid(s, r);
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int wil_sta_seq_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, wil_sta_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations fops_sta = {
+ .open = wil_sta_seq_open,
+ .release = single_release,
+ .read = seq_read,
+ .llseek = seq_lseek,
};
/*----------------*/
@@ -540,9 +692,13 @@ int wil6210_debugfs_init(struct wil6210_priv *wil)
debugfs_create_file("mbox", S_IRUGO, dbg, wil, &fops_mbox);
debugfs_create_file("vrings", S_IRUGO, dbg, wil, &fops_vring);
- debugfs_create_file("txdesc", S_IRUGO, dbg, wil, &fops_txdesc);
- debugfs_create_u32("txdesc_index", S_IRUGO | S_IWUSR, dbg,
+ debugfs_create_file("stations", S_IRUGO, dbg, wil, &fops_sta);
+ debugfs_create_file("desc", S_IRUGO, dbg, wil, &fops_txdesc);
+ debugfs_create_u32("desc_index", S_IRUGO | S_IWUSR, dbg,
&dbg_txdesc_index);
+ debugfs_create_u32("vring_index", S_IRUGO | S_IWUSR, dbg,
+ &dbg_vring_index);
+
debugfs_create_file("bf", S_IRUGO, dbg, wil, &fops_bf);
debugfs_create_file("ssid", S_IRUGO | S_IWUSR, dbg, wil, &fops_ssid);
debugfs_create_u32("secure_pcp", S_IRUGO | S_IWUSR, dbg,
@@ -563,6 +719,7 @@ int wil6210_debugfs_init(struct wil6210_priv *wil)
debugfs_create_file("mem_val", S_IRUGO, dbg, wil, &fops_memread);
debugfs_create_file("reset", S_IWUSR, dbg, wil, &fops_reset);
+ debugfs_create_file("temp", S_IRUGO, dbg, wil, &fops_temp);
wil->rgf_blob.data = (void * __force)wil->csr + 0;
wil->rgf_blob.size = 0xa000;
diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c
index 38049da7104..73593aa3cd9 100644
--- a/drivers/net/wireless/ath/wil6210/interrupt.c
+++ b/drivers/net/wireless/ath/wil6210/interrupt.c
@@ -17,6 +17,7 @@
#include <linux/interrupt.h>
#include "wil6210.h"
+#include "trace.h"
/**
* Theory of operation:
@@ -38,7 +39,9 @@
#define WIL6210_IMC_RX BIT_DMA_EP_RX_ICR_RX_DONE
#define WIL6210_IMC_TX (BIT_DMA_EP_TX_ICR_TX_DONE | \
BIT_DMA_EP_TX_ICR_TX_DONE_N(0))
-#define WIL6210_IMC_MISC (ISR_MISC_FW_READY | ISR_MISC_MBOX_EVT)
+#define WIL6210_IMC_MISC (ISR_MISC_FW_READY | \
+ ISR_MISC_MBOX_EVT | \
+ ISR_MISC_FW_ERROR)
#define WIL6210_IRQ_PSEUDO_MASK (u32)(~(BIT_DMA_PSEUDO_CAUSE_RX | \
BIT_DMA_PSEUDO_CAUSE_TX | \
@@ -50,7 +53,6 @@
static inline void wil_icr_clear(u32 x, void __iomem *addr)
{
-
}
#else /* defined(CONFIG_WIL6210_ISR_COR) */
/* configure to Write-1-to-Clear mode */
@@ -94,7 +96,7 @@ static void wil6210_mask_irq_misc(struct wil6210_priv *wil)
static void wil6210_mask_irq_pseudo(struct wil6210_priv *wil)
{
- wil_dbg_IRQ(wil, "%s()\n", __func__);
+ wil_dbg_irq(wil, "%s()\n", __func__);
iowrite32(WIL6210_IRQ_DISABLE, wil->csr +
HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW));
@@ -102,14 +104,14 @@ static void wil6210_mask_irq_pseudo(struct wil6210_priv *wil)
clear_bit(wil_status_irqen, &wil->status);
}
-static void wil6210_unmask_irq_tx(struct wil6210_priv *wil)
+void wil6210_unmask_irq_tx(struct wil6210_priv *wil)
{
iowrite32(WIL6210_IMC_TX, wil->csr +
HOSTADDR(RGF_DMA_EP_TX_ICR) +
offsetof(struct RGF_ICR, IMC));
}
-static void wil6210_unmask_irq_rx(struct wil6210_priv *wil)
+void wil6210_unmask_irq_rx(struct wil6210_priv *wil)
{
iowrite32(WIL6210_IMC_RX, wil->csr +
HOSTADDR(RGF_DMA_EP_RX_ICR) +
@@ -125,7 +127,7 @@ static void wil6210_unmask_irq_misc(struct wil6210_priv *wil)
static void wil6210_unmask_irq_pseudo(struct wil6210_priv *wil)
{
- wil_dbg_IRQ(wil, "%s()\n", __func__);
+ wil_dbg_irq(wil, "%s()\n", __func__);
set_bit(wil_status_irqen, &wil->status);
@@ -135,7 +137,7 @@ static void wil6210_unmask_irq_pseudo(struct wil6210_priv *wil)
void wil6210_disable_irq(struct wil6210_priv *wil)
{
- wil_dbg_IRQ(wil, "%s()\n", __func__);
+ wil_dbg_irq(wil, "%s()\n", __func__);
wil6210_mask_irq_tx(wil);
wil6210_mask_irq_rx(wil);
@@ -145,7 +147,7 @@ void wil6210_disable_irq(struct wil6210_priv *wil)
void wil6210_enable_irq(struct wil6210_priv *wil)
{
- wil_dbg_IRQ(wil, "%s()\n", __func__);
+ wil_dbg_irq(wil, "%s()\n", __func__);
iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_RX_ICR) +
offsetof(struct RGF_ICR, ICC));
@@ -154,6 +156,19 @@ void wil6210_enable_irq(struct wil6210_priv *wil)
iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) +
offsetof(struct RGF_ICR, ICC));
+ /* interrupt moderation parameters */
+ if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) {
+ /* disable interrupt moderation for monitor
+ * to get better timestamp precision
+ */
+ iowrite32(0, wil->csr + HOSTADDR(RGF_DMA_ITR_CNT_CRL));
+ } else {
+ iowrite32(WIL6210_ITR_TRSH,
+ wil->csr + HOSTADDR(RGF_DMA_ITR_CNT_TRSH));
+ iowrite32(BIT_DMA_ITR_CNT_CRL_EN,
+ wil->csr + HOSTADDR(RGF_DMA_ITR_CNT_CRL));
+ }
+
wil6210_unmask_irq_pseudo(wil);
wil6210_unmask_irq_tx(wil);
wil6210_unmask_irq_rx(wil);
@@ -167,7 +182,8 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
HOSTADDR(RGF_DMA_EP_RX_ICR) +
offsetof(struct RGF_ICR, ICR));
- wil_dbg_IRQ(wil, "ISR RX 0x%08x\n", isr);
+ trace_wil6210_irq_rx(isr);
+ wil_dbg_irq(wil, "ISR RX 0x%08x\n", isr);
if (!isr) {
wil_err(wil, "spurious IRQ: RX\n");
@@ -177,15 +193,20 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
wil6210_mask_irq_rx(wil);
if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) {
- wil_dbg_IRQ(wil, "RX done\n");
+ wil_dbg_irq(wil, "RX done\n");
isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE;
- wil_rx_handle(wil);
+ if (test_bit(wil_status_reset_done, &wil->status)) {
+ wil_dbg_txrx(wil, "NAPI(Rx) schedule\n");
+ napi_schedule(&wil->napi_rx);
+ } else {
+ wil_err(wil, "Got Rx interrupt while in reset\n");
+ }
}
if (isr)
wil_err(wil, "un-handled RX ISR bits 0x%08x\n", isr);
- wil6210_unmask_irq_rx(wil);
+ /* Rx IRQ will be enabled when NAPI processing finished */
return IRQ_HANDLED;
}
@@ -197,7 +218,8 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
HOSTADDR(RGF_DMA_EP_TX_ICR) +
offsetof(struct RGF_ICR, ICR));
- wil_dbg_IRQ(wil, "ISR TX 0x%08x\n", isr);
+ trace_wil6210_irq_tx(isr);
+ wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr);
if (!isr) {
wil_err(wil, "spurious IRQ: TX\n");
@@ -207,27 +229,46 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
wil6210_mask_irq_tx(wil);
if (isr & BIT_DMA_EP_TX_ICR_TX_DONE) {
- uint i;
- wil_dbg_IRQ(wil, "TX done\n");
+ wil_dbg_irq(wil, "TX done\n");
isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE;
- for (i = 0; i < 24; i++) {
- u32 mask = BIT_DMA_EP_TX_ICR_TX_DONE_N(i);
- if (isr & mask) {
- isr &= ~mask;
- wil_dbg_IRQ(wil, "TX done(%i)\n", i);
- wil_tx_complete(wil, i);
- }
+ /* clear also all VRING interrupts */
+ isr &= ~(BIT(25) - 1UL);
+ if (test_bit(wil_status_reset_done, &wil->status)) {
+ wil_dbg_txrx(wil, "NAPI(Tx) schedule\n");
+ napi_schedule(&wil->napi_tx);
+ } else {
+ wil_err(wil, "Got Tx interrupt while in reset\n");
}
}
if (isr)
wil_err(wil, "un-handled TX ISR bits 0x%08x\n", isr);
- wil6210_unmask_irq_tx(wil);
+ /* Tx IRQ will be enabled when NAPI processing finished */
return IRQ_HANDLED;
}
+static void wil_notify_fw_error(struct wil6210_priv *wil)
+{
+ struct device *dev = &wil_to_ndev(wil)->dev;
+ char *envp[3] = {
+ [0] = "SOURCE=wil6210",
+ [1] = "EVENT=FW_ERROR",
+ [2] = NULL,
+ };
+ kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
+}
+
+static void wil_cache_mbox_regs(struct wil6210_priv *wil)
+{
+ /* make shadow copy of registers that should not change on run time */
+ wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX,
+ sizeof(struct wil6210_mbox_ctl));
+ wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx);
+ wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx);
+}
+
static irqreturn_t wil6210_irq_misc(int irq, void *cookie)
{
struct wil6210_priv *wil = cookie;
@@ -235,7 +276,8 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie)
HOSTADDR(RGF_DMA_EP_MISC_ICR) +
offsetof(struct RGF_ICR, ICR));
- wil_dbg_IRQ(wil, "ISR MISC 0x%08x\n", isr);
+ trace_wil6210_irq_misc(isr);
+ wil_dbg_irq(wil, "ISR MISC 0x%08x\n", isr);
if (!isr) {
wil_err(wil, "spurious IRQ: MISC\n");
@@ -244,8 +286,20 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie)
wil6210_mask_irq_misc(wil);
+ if (isr & ISR_MISC_FW_ERROR) {
+ wil_err(wil, "Firmware error detected\n");
+ clear_bit(wil_status_fwready, &wil->status);
+ /*
+ * do not clear @isr here - we do 2-nd part in thread
+ * there, user space get notified, and it should be done
+ * in non-atomic context
+ */
+ }
+
if (isr & ISR_MISC_FW_READY) {
- wil_dbg_IRQ(wil, "IRQ: FW ready\n");
+ wil_dbg_irq(wil, "IRQ: FW ready\n");
+ wil_cache_mbox_regs(wil);
+ set_bit(wil_status_reset_done, &wil->status);
/**
* Actual FW ready indicated by the
* WMI_FW_READY_EVENTID
@@ -268,16 +322,23 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie)
struct wil6210_priv *wil = cookie;
u32 isr = wil->isr_misc;
- wil_dbg_IRQ(wil, "Thread ISR MISC 0x%08x\n", isr);
+ trace_wil6210_irq_misc_thread(isr);
+ wil_dbg_irq(wil, "Thread ISR MISC 0x%08x\n", isr);
+
+ if (isr & ISR_MISC_FW_ERROR) {
+ wil_notify_fw_error(wil);
+ isr &= ~ISR_MISC_FW_ERROR;
+ wil_fw_error_recovery(wil);
+ }
if (isr & ISR_MISC_MBOX_EVT) {
- wil_dbg_IRQ(wil, "MBOX event\n");
+ wil_dbg_irq(wil, "MBOX event\n");
wmi_recv_cmd(wil);
isr &= ~ISR_MISC_MBOX_EVT;
}
if (isr)
- wil_err(wil, "un-handled MISC ISR bits 0x%08x\n", isr);
+ wil_dbg_irq(wil, "un-handled MISC ISR bits 0x%08x\n", isr);
wil->isr_misc = 0;
@@ -293,7 +354,7 @@ static irqreturn_t wil6210_thread_irq(int irq, void *cookie)
{
struct wil6210_priv *wil = cookie;
- wil_dbg_IRQ(wil, "Thread IRQ\n");
+ wil_dbg_irq(wil, "Thread IRQ\n");
/* Discover real IRQ cause */
if (wil->isr_misc)
wil6210_irq_misc_thread(irq, cookie);
@@ -370,6 +431,9 @@ static irqreturn_t wil6210_hardirq(int irq, void *cookie)
if (wil6210_debug_irq_mask(wil, pseudo_cause))
return IRQ_NONE;
+ trace_wil6210_irq_pseudo(pseudo_cause);
+ wil_dbg_irq(wil, "Pseudo IRQ 0x%08x\n", pseudo_cause);
+
wil6210_mask_irq_pseudo(wil);
/* Discover real IRQ cause
@@ -401,8 +465,6 @@ static irqreturn_t wil6210_hardirq(int irq, void *cookie)
if (rc != IRQ_WAKE_THREAD)
wil6210_unmask_irq_pseudo(wil);
- wil_dbg_IRQ(wil, "Hard IRQ 0x%08x\n", pseudo_cause);
-
return rc;
}
@@ -441,6 +503,23 @@ free0:
return rc;
}
+/* can't use wil_ioread32_and_clear because ICC value is not ser yet */
+static inline void wil_clear32(void __iomem *addr)
+{
+ u32 x = ioread32(addr);
+
+ iowrite32(x, addr);
+}
+
+void wil6210_clear_irq(struct wil6210_priv *wil)
+{
+ wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_RX_ICR) +
+ offsetof(struct RGF_ICR, ICR));
+ wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_TX_ICR) +
+ offsetof(struct RGF_ICR, ICR));
+ wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) +
+ offsetof(struct RGF_ICR, ICR));
+}
int wil6210_init_irq(struct wil6210_priv *wil, int irq)
{
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 95fcd361322..11e6d9d22ea 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -14,16 +14,16 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include <linux/kernel.h>
-#include <linux/netdevice.h>
-#include <linux/sched.h>
-#include <linux/ieee80211.h>
-#include <linux/wireless.h>
-#include <linux/slab.h>
#include <linux/moduleparam.h>
#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
#include "wil6210.h"
+#include "txrx.h"
+
+static bool no_fw_recovery;
+module_param(no_fw_recovery, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(no_fw_recovery, " disable FW error recovery");
/*
* Due to a hardware issue,
@@ -58,33 +58,74 @@ void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src,
__raw_writel(*s++, d++);
}
-static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid)
+static void wil_disconnect_cid(struct wil6210_priv *wil, int cid)
{
uint i;
+ struct wil_sta_info *sta = &wil->sta[cid];
+
+ sta->data_port_open = false;
+ if (sta->status != wil_sta_unused) {
+ wmi_disconnect_sta(wil, sta->addr, WLAN_REASON_DEAUTH_LEAVING);
+ sta->status = wil_sta_unused;
+ }
+
+ for (i = 0; i < WIL_STA_TID_NUM; i++) {
+ struct wil_tid_ampdu_rx *r = sta->tid_rx[i];
+ sta->tid_rx[i] = NULL;
+ wil_tid_ampdu_rx_free(wil, r);
+ }
+ for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) {
+ if (wil->vring2cid_tid[i][0] == cid)
+ wil_vring_fini_tx(wil, i);
+ }
+ memset(&sta->stats, 0, sizeof(sta->stats));
+}
+
+static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid)
+{
+ int cid = -ENOENT;
struct net_device *ndev = wil_to_ndev(wil);
struct wireless_dev *wdev = wil->wdev;
- wil_dbg(wil, "%s()\n", __func__);
+ might_sleep();
+ if (bssid) {
+ cid = wil_find_cid(wil, bssid);
+ wil_dbg_misc(wil, "%s(%pM, CID %d)\n", __func__, bssid, cid);
+ } else {
+ wil_dbg_misc(wil, "%s(all)\n", __func__);
+ }
- wil_link_off(wil);
- clear_bit(wil_status_fwconnected, &wil->status);
+ if (cid >= 0) /* disconnect 1 peer */
+ wil_disconnect_cid(wil, cid);
+ else /* disconnect all */
+ for (cid = 0; cid < WIL6210_MAX_CID; cid++)
+ wil_disconnect_cid(wil, cid);
- switch (wdev->sme_state) {
- case CFG80211_SME_CONNECTED:
- cfg80211_disconnected(ndev, WLAN_STATUS_UNSPECIFIED_FAILURE,
- NULL, 0, GFP_KERNEL);
- break;
- case CFG80211_SME_CONNECTING:
- cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0,
- WLAN_STATUS_UNSPECIFIED_FAILURE,
- GFP_KERNEL);
+ /* link state */
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ wil_link_off(wil);
+ if (test_bit(wil_status_fwconnected, &wil->status)) {
+ clear_bit(wil_status_fwconnected, &wil->status);
+ cfg80211_disconnected(ndev,
+ WLAN_STATUS_UNSPECIFIED_FAILURE,
+ NULL, 0, GFP_KERNEL);
+ } else if (test_bit(wil_status_fwconnecting, &wil->status)) {
+ cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0,
+ WLAN_STATUS_UNSPECIFIED_FAILURE,
+ GFP_KERNEL);
+ }
+ clear_bit(wil_status_fwconnecting, &wil->status);
break;
default:
- ;
+ /* AP-like interface and monitor:
+ * never scan, always connected
+ */
+ if (bssid)
+ cfg80211_del_sta(ndev, bssid, GFP_KERNEL);
+ break;
}
-
- for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++)
- wil_vring_fini_tx(wil, i);
}
static void wil_disconnect_worker(struct work_struct *work)
@@ -92,14 +133,16 @@ static void wil_disconnect_worker(struct work_struct *work)
struct wil6210_priv *wil = container_of(work,
struct wil6210_priv, disconnect_worker);
+ mutex_lock(&wil->mutex);
_wil6210_disconnect(wil, NULL);
+ mutex_unlock(&wil->mutex);
}
static void wil_connect_timer_fn(ulong x)
{
struct wil6210_priv *wil = (void *)x;
- wil_dbg(wil, "Connect timeout\n");
+ wil_dbg_misc(wil, "Connect timeout\n");
/* reschedule to thread context - disconnect won't
* run from atomic context
@@ -107,9 +150,105 @@ static void wil_connect_timer_fn(ulong x)
schedule_work(&wil->disconnect_worker);
}
+static void wil_scan_timer_fn(ulong x)
+{
+ struct wil6210_priv *wil = (void *)x;
+
+ clear_bit(wil_status_fwready, &wil->status);
+ wil_err(wil, "Scan timeout detected, start fw error recovery\n");
+ schedule_work(&wil->fw_error_worker);
+}
+
+static void wil_fw_error_worker(struct work_struct *work)
+{
+ struct wil6210_priv *wil = container_of(work,
+ struct wil6210_priv, fw_error_worker);
+ struct wireless_dev *wdev = wil->wdev;
+
+ wil_dbg_misc(wil, "fw error worker\n");
+
+ if (no_fw_recovery)
+ return;
+
+ /* increment @recovery_count if less then WIL6210_FW_RECOVERY_TO
+ * passed since last recovery attempt
+ */
+ if (time_is_after_jiffies(wil->last_fw_recovery +
+ WIL6210_FW_RECOVERY_TO))
+ wil->recovery_count++;
+ else
+ wil->recovery_count = 1; /* fw was alive for a long time */
+
+ if (wil->recovery_count > WIL6210_FW_RECOVERY_RETRIES) {
+ wil_err(wil, "too many recovery attempts (%d), giving up\n",
+ wil->recovery_count);
+ return;
+ }
+
+ wil->last_fw_recovery = jiffies;
+
+ mutex_lock(&wil->mutex);
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_MONITOR:
+ wil_info(wil, "fw error recovery started (try %d)...\n",
+ wil->recovery_count);
+ wil_reset(wil);
+
+ /* need to re-allocate Rx ring after reset */
+ wil_rx_init(wil);
+ break;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ /* recovery in these modes is done by upper layers */
+ break;
+ default:
+ break;
+ }
+ mutex_unlock(&wil->mutex);
+}
+
+static int wil_find_free_vring(struct wil6210_priv *wil)
+{
+ int i;
+ for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
+ if (!wil->vring_tx[i].va)
+ return i;
+ }
+ return -EINVAL;
+}
+
+static void wil_connect_worker(struct work_struct *work)
+{
+ int rc;
+ struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
+ connect_worker);
+ int cid = wil->pending_connect_cid;
+ int ringid = wil_find_free_vring(wil);
+
+ if (cid < 0) {
+ wil_err(wil, "No connection pending\n");
+ return;
+ }
+
+ wil_dbg_wmi(wil, "Configure for connection CID %d\n", cid);
+
+ rc = wil_vring_init_tx(wil, ringid, WIL6210_TX_RING_SIZE, cid, 0);
+ wil->pending_connect_cid = -1;
+ if (rc == 0) {
+ wil->sta[cid].status = wil_sta_connected;
+ wil_link_on(wil);
+ } else {
+ wil->sta[cid].status = wil_sta_unused;
+ }
+}
+
int wil_priv_init(struct wil6210_priv *wil)
{
- wil_dbg(wil, "%s()\n", __func__);
+ wil_dbg_misc(wil, "%s()\n", __func__);
+
+ memset(wil->sta, 0, sizeof(wil->sta));
mutex_init(&wil->mutex);
mutex_init(&wil->wmi_mutex);
@@ -118,10 +257,12 @@ int wil_priv_init(struct wil6210_priv *wil)
wil->pending_connect_cid = -1;
setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil);
+ setup_timer(&wil->scan_timer, wil_scan_timer_fn, (ulong)wil);
- INIT_WORK(&wil->wmi_connect_worker, wmi_connect_worker);
+ INIT_WORK(&wil->connect_worker, wil_connect_worker);
INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker);
INIT_WORK(&wil->wmi_event_worker, wmi_event_worker);
+ INIT_WORK(&wil->fw_error_worker, wil_fw_error_worker);
INIT_LIST_HEAD(&wil->pending_wmi_ev);
spin_lock_init(&wil->wmi_ev_lock);
@@ -136,16 +277,12 @@ int wil_priv_init(struct wil6210_priv *wil)
return -EAGAIN;
}
- /* make shadow copy of registers that should not change on run time */
- wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX,
- sizeof(struct wil6210_mbox_ctl));
- wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx);
- wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx);
+ wil->last_fw_recovery = jiffies;
return 0;
}
-void wil6210_disconnect(struct wil6210_priv *wil, void *bssid)
+void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid)
{
del_timer_sync(&wil->connect_timer);
_wil6210_disconnect(wil, bssid);
@@ -153,8 +290,12 @@ void wil6210_disconnect(struct wil6210_priv *wil, void *bssid)
void wil_priv_deinit(struct wil6210_priv *wil)
{
+ del_timer_sync(&wil->scan_timer);
cancel_work_sync(&wil->disconnect_worker);
+ cancel_work_sync(&wil->fw_error_worker);
+ mutex_lock(&wil->mutex);
wil6210_disconnect(wil, NULL);
+ mutex_unlock(&wil->mutex);
wmi_event_flush(wil);
destroy_workqueue(wil->wmi_wq_conn);
destroy_workqueue(wil->wmi_wq);
@@ -162,50 +303,78 @@ void wil_priv_deinit(struct wil6210_priv *wil)
static void wil_target_reset(struct wil6210_priv *wil)
{
- wil_dbg(wil, "Resetting...\n");
+ int delay = 0;
+ u32 hw_state;
+ u32 rev_id;
+ wil_dbg_misc(wil, "Resetting...\n");
+
+ /* register read */
+#define R(a) ioread32(wil->csr + HOSTADDR(a))
/* register write */
#define W(a, v) iowrite32(v, wil->csr + HOSTADDR(a))
/* register set = read, OR, write */
-#define S(a, v) iowrite32(ioread32(wil->csr + HOSTADDR(a)) | v, \
- wil->csr + HOSTADDR(a))
+#define S(a, v) W(a, R(a) | v)
+ /* register clear = read, AND with inverted, write */
+#define C(a, v) W(a, R(a) & ~v)
+ wil->hw_version = R(RGF_USER_FW_REV_ID);
+ rev_id = wil->hw_version & 0xff;
/* hpal_perst_from_pad_src_n_mask */
S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(6));
/* car_perst_rst_src_n_mask */
S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(7));
+ wmb(); /* order is important here */
W(RGF_USER_MAC_CPU_0, BIT(1)); /* mac_cpu_man_rst */
W(RGF_USER_USER_CPU_0, BIT(1)); /* user_cpu_man_rst */
-
- msleep(100);
+ wmb(); /* order is important here */
W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000170);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FC00);
-
- msleep(100);
+ wmb(); /* order is important here */
W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
+ wmb(); /* order is important here */
W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001);
- W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080);
+ if (rev_id == 1) {
+ W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080);
+ } else {
+ W(RGF_PCIE_LOS_COUNTER_CTL, BIT(6) | BIT(8));
+ W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000);
+ }
W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
+ wmb(); /* order is important here */
+
+ /* wait until device ready */
+ do {
+ msleep(1);
+ hw_state = R(RGF_USER_HW_MACHINE_STATE);
+ if (delay++ > 100) {
+ wil_err(wil, "Reset not completed, hw_state 0x%08x\n",
+ hw_state);
+ return;
+ }
+ } while (hw_state != HW_MACHINE_BOOT_DONE);
- msleep(2000);
-
- W(RGF_USER_USER_CPU_0, BIT(0)); /* user_cpu_man_de_rst */
+ if (rev_id == 2)
+ W(RGF_PCIE_LOS_COUNTER_CTL, BIT(8));
- msleep(2000);
+ C(RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
+ wmb(); /* order is important here */
- wil_dbg(wil, "Reset completed\n");
+ wil_dbg_misc(wil, "Reset completed in %d ms\n", delay);
+#undef R
#undef W
#undef S
+#undef C
}
void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r)
@@ -225,8 +394,8 @@ static int wil_wait_for_fw_ready(struct wil6210_priv *wil)
wil_err(wil, "Firmware not ready\n");
return -ETIME;
} else {
- wil_dbg(wil, "FW ready after %d ms\n",
- jiffies_to_msecs(to-left));
+ wil_info(wil, "FW ready after %d ms. HW version 0x%08x\n",
+ jiffies_to_msecs(to-left), wil->hw_version);
}
return 0;
}
@@ -240,29 +409,39 @@ int wil_reset(struct wil6210_priv *wil)
{
int rc;
+ WARN_ON(!mutex_is_locked(&wil->mutex));
+
cancel_work_sync(&wil->disconnect_worker);
wil6210_disconnect(wil, NULL);
- wmi_event_flush(wil);
+ wil->status = 0; /* prevent NAPI from being scheduled */
+ if (test_bit(wil_status_napi_en, &wil->status)) {
+ napi_synchronize(&wil->napi_rx);
+ }
- flush_workqueue(wil->wmi_wq);
- flush_workqueue(wil->wmi_wq_conn);
+ if (wil->scan_request) {
+ wil_dbg_misc(wil, "Abort scan_request 0x%p\n",
+ wil->scan_request);
+ del_timer_sync(&wil->scan_timer);
+ cfg80211_scan_done(wil->scan_request, true);
+ wil->scan_request = NULL;
+ }
wil6210_disable_irq(wil);
- wil->status = 0;
+
+ wmi_event_flush(wil);
+
+ flush_workqueue(wil->wmi_wq_conn);
+ flush_workqueue(wil->wmi_wq);
/* TODO: put MAC in reset */
wil_target_reset(wil);
+ wil_rx_fini(wil);
+
/* init after reset */
wil->pending_connect_cid = -1;
- INIT_COMPLETION(wil->wmi_ready);
-
- /* make shadow copy of registers that should not change on run time */
- wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX,
- sizeof(struct wil6210_mbox_ctl));
- wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx);
- wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx);
+ reinit_completion(&wil->wmi_ready);
/* TODO: release MAC reset */
wil6210_enable_irq(wil);
@@ -273,12 +452,17 @@ int wil_reset(struct wil6210_priv *wil)
return rc;
}
+void wil_fw_error_recovery(struct wil6210_priv *wil)
+{
+ wil_dbg_misc(wil, "starting fw error recovery\n");
+ schedule_work(&wil->fw_error_worker);
+}
void wil_link_on(struct wil6210_priv *wil)
{
struct net_device *ndev = wil_to_ndev(wil);
- wil_dbg(wil, "%s()\n", __func__);
+ wil_dbg_misc(wil, "%s()\n", __func__);
netif_carrier_on(ndev);
netif_tx_wake_all_queues(ndev);
@@ -288,7 +472,7 @@ void wil_link_off(struct wil6210_priv *wil)
{
struct net_device *ndev = wil_to_ndev(wil);
- wil_dbg(wil, "%s()\n", __func__);
+ wil_dbg_misc(wil, "%s()\n", __func__);
netif_tx_stop_all_queues(ndev);
netif_carrier_off(ndev);
@@ -298,41 +482,38 @@ static int __wil_up(struct wil6210_priv *wil)
{
struct net_device *ndev = wil_to_ndev(wil);
struct wireless_dev *wdev = wil->wdev;
- struct ieee80211_channel *channel = wdev->preset_chandef.chan;
int rc;
- int bi;
- u16 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype);
+
+ WARN_ON(!mutex_is_locked(&wil->mutex));
rc = wil_reset(wil);
if (rc)
return rc;
- /* FIXME Firmware works now in PBSS mode(ToDS=0, FromDS=0) */
- wmi_nettype = wil_iftype_nl2wmi(NL80211_IFTYPE_ADHOC);
+ /* Rx VRING. After MAC and beacon */
+ rc = wil_rx_init(wil);
+ if (rc)
+ return rc;
+
switch (wdev->iftype) {
case NL80211_IFTYPE_STATION:
- wil_dbg(wil, "type: STATION\n");
- bi = 0;
+ wil_dbg_misc(wil, "type: STATION\n");
ndev->type = ARPHRD_ETHER;
break;
case NL80211_IFTYPE_AP:
- wil_dbg(wil, "type: AP\n");
- bi = 100;
+ wil_dbg_misc(wil, "type: AP\n");
ndev->type = ARPHRD_ETHER;
break;
case NL80211_IFTYPE_P2P_CLIENT:
- wil_dbg(wil, "type: P2P_CLIENT\n");
- bi = 0;
+ wil_dbg_misc(wil, "type: P2P_CLIENT\n");
ndev->type = ARPHRD_ETHER;
break;
case NL80211_IFTYPE_P2P_GO:
- wil_dbg(wil, "type: P2P_GO\n");
- bi = 100;
+ wil_dbg_misc(wil, "type: P2P_GO\n");
ndev->type = ARPHRD_ETHER;
break;
case NL80211_IFTYPE_MONITOR:
- wil_dbg(wil, "type: Monitor\n");
- bi = 0;
+ wil_dbg_misc(wil, "type: Monitor\n");
ndev->type = ARPHRD_IEEE80211_RADIOTAP;
/* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */
break;
@@ -340,33 +521,13 @@ static int __wil_up(struct wil6210_priv *wil)
return -EOPNOTSUPP;
}
- /* Apply profile in the following order: */
- /* SSID and channel for the AP */
- switch (wdev->iftype) {
- case NL80211_IFTYPE_AP:
- case NL80211_IFTYPE_P2P_GO:
- if (wdev->ssid_len == 0) {
- wil_err(wil, "SSID not set\n");
- return -EINVAL;
- }
- wmi_set_ssid(wil, wdev->ssid_len, wdev->ssid);
- if (channel)
- wmi_set_channel(wil, channel->hw_value);
- break;
- default:
- ;
- }
-
/* MAC address - pre-requisite for other commands */
wmi_set_mac_address(wil, ndev->dev_addr);
- /* Set up beaconing if required. */
- rc = wmi_set_bcon(wil, bi, wmi_nettype);
- if (rc)
- return rc;
- /* Rx VRING. After MAC and beacon */
- wil_rx_init(wil);
+ napi_enable(&wil->napi_rx);
+ napi_enable(&wil->napi_tx);
+ set_bit(wil_status_napi_en, &wil->status);
return 0;
}
@@ -384,7 +545,14 @@ int wil_up(struct wil6210_priv *wil)
static int __wil_down(struct wil6210_priv *wil)
{
+ WARN_ON(!mutex_is_locked(&wil->mutex));
+
+ clear_bit(wil_status_napi_en, &wil->status);
+ napi_disable(&wil->napi_rx);
+ napi_disable(&wil->napi_tx);
+
if (wil->scan_request) {
+ del_timer_sync(&wil->scan_timer);
cfg80211_scan_done(wil->scan_request, true);
wil->scan_request = NULL;
}
@@ -405,3 +573,19 @@ int wil_down(struct wil6210_priv *wil)
return rc;
}
+
+int wil_find_cid(struct wil6210_priv *wil, const u8 *mac)
+{
+ int i;
+ int rc = -ENOENT;
+
+ for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
+ if ((wil->sta[i].status != wil_sta_unused) &&
+ ether_addr_equal(wil->sta[i].addr, mac)) {
+ rc = i;
+ break;
+ }
+ }
+
+ return rc;
+}
diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c
index 3068b5cb53a..106b6dcb773 100644
--- a/drivers/net/wireless/ath/wil6210/netdev.c
+++ b/drivers/net/wireless/ath/wil6210/netdev.c
@@ -14,10 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include <linux/module.h>
-#include <linux/netdevice.h>
#include <linux/etherdevice.h>
-#include <linux/slab.h>
#include "wil6210.h"
@@ -35,39 +32,77 @@ static int wil_stop(struct net_device *ndev)
return wil_down(wil);
}
-/*
- * AC to queue mapping
- *
- * AC_VO -> queue 3
- * AC_VI -> queue 2
- * AC_BE -> queue 1
- * AC_BK -> queue 0
- */
-static u16 wil_select_queue(struct net_device *ndev, struct sk_buff *skb)
+static int wil_change_mtu(struct net_device *ndev, int new_mtu)
{
- static const u16 wil_1d_to_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 };
struct wil6210_priv *wil = ndev_to_wil(ndev);
- u16 rc;
- skb->priority = cfg80211_classify8021d(skb);
+ if (new_mtu < 68 || new_mtu > IEEE80211_MAX_DATA_LEN_DMG)
+ return -EINVAL;
- rc = wil_1d_to_queue[skb->priority];
+ wil_dbg_misc(wil, "change MTU %d -> %d\n", ndev->mtu, new_mtu);
+ ndev->mtu = new_mtu;
- wil_dbg_TXRX(wil, "%s() %d -> %d\n", __func__, (int)skb->priority,
- (int)rc);
-
- return rc;
+ return 0;
}
static const struct net_device_ops wil_netdev_ops = {
.ndo_open = wil_open,
.ndo_stop = wil_stop,
.ndo_start_xmit = wil_start_xmit,
- .ndo_select_queue = wil_select_queue,
- .ndo_set_mac_address = eth_mac_addr,
- .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = wil_change_mtu,
};
+static int wil6210_netdev_poll_rx(struct napi_struct *napi, int budget)
+{
+ struct wil6210_priv *wil = container_of(napi, struct wil6210_priv,
+ napi_rx);
+ int quota = budget;
+ int done;
+
+ wil_rx_handle(wil, &quota);
+ done = budget - quota;
+
+ if (done <= 1) { /* burst ends - only one packet processed */
+ napi_complete(napi);
+ wil6210_unmask_irq_rx(wil);
+ wil_dbg_txrx(wil, "NAPI RX complete\n");
+ }
+
+ wil_dbg_txrx(wil, "NAPI RX poll(%d) done %d\n", budget, done);
+
+ return done;
+}
+
+static int wil6210_netdev_poll_tx(struct napi_struct *napi, int budget)
+{
+ struct wil6210_priv *wil = container_of(napi, struct wil6210_priv,
+ napi_tx);
+ int tx_done = 0;
+ uint i;
+
+ /* always process ALL Tx complete, regardless budget - it is fast */
+ for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
+ struct vring *vring = &wil->vring_tx[i];
+
+ if (!vring->va)
+ continue;
+
+ tx_done += wil_tx_complete(wil, i);
+ }
+
+ if (tx_done <= 1) { /* burst ends - only one packet processed */
+ napi_complete(napi);
+ wil6210_unmask_irq_tx(wil);
+ wil_dbg_txrx(wil, "NAPI TX complete\n");
+ }
+
+ wil_dbg_txrx(wil, "NAPI TX poll(%d) done %d\n", budget, tx_done);
+
+ return min(tx_done, budget);
+}
+
void *wil_if_alloc(struct device *dev, void __iomem *csr)
{
struct net_device *ndev;
@@ -97,7 +132,7 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr)
ch = wdev->wiphy->bands[IEEE80211_BAND_60GHZ]->channels;
cfg80211_chandef_create(&wdev->preset_chandef, ch, NL80211_CHAN_NO_HT);
- ndev = alloc_netdev_mqs(0, "wlan%d", ether_setup, WIL6210_TX_QUEUES, 1);
+ ndev = alloc_netdev(0, "wlan%d", ether_setup);
if (!ndev) {
dev_err(dev, "alloc_netdev_mqs failed\n");
rc = -ENOMEM;
@@ -106,9 +141,17 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr)
ndev->netdev_ops = &wil_netdev_ops;
ndev->ieee80211_ptr = wdev;
+ ndev->hw_features = NETIF_F_HW_CSUM | NETIF_F_RXCSUM |
+ NETIF_F_SG | NETIF_F_GRO;
+ ndev->features |= ndev->hw_features;
SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
wdev->netdev = ndev;
+ netif_napi_add(ndev, &wil->napi_rx, wil6210_netdev_poll_rx,
+ WIL6210_NAPI_BUDGET);
+ netif_napi_add(ndev, &wil->napi_tx, wil6210_netdev_poll_tx,
+ WIL6210_NAPI_BUDGET);
+
wil_link_off(wil);
return wil;
diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
index 0fc83edd6ba..1e2e07b9d13 100644
--- a/drivers/net/wireless/ath/wil6210/pcie_bus.c
+++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
@@ -14,10 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/netdevice.h>
#include <linux/debugfs.h>
#include <linux/pci.h>
#include <linux/moduleparam.h>
@@ -44,36 +41,36 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil)
switch (use_msi) {
case 3:
case 1:
+ wil_dbg_misc(wil, "Setup %d MSI interrupts\n", use_msi);
+ break;
case 0:
+ wil_dbg_misc(wil, "MSI interrupts disabled, use INTx\n");
break;
default:
- wil_err(wil, "Invalid use_msi=%d, default to 1\n",
- use_msi);
+ wil_err(wil, "Invalid use_msi=%d, default to 1\n", use_msi);
use_msi = 1;
}
- wil->n_msi = use_msi;
- if (wil->n_msi) {
- wil_dbg(wil, "Setup %d MSI interrupts\n", use_msi);
- rc = pci_enable_msi_block(pdev, wil->n_msi);
- if (rc && (wil->n_msi == 3)) {
- wil_err(wil, "3 MSI mode failed, try 1 MSI\n");
- wil->n_msi = 1;
- rc = pci_enable_msi_block(pdev, wil->n_msi);
- }
- if (rc) {
- wil_err(wil, "pci_enable_msi failed, use INTx\n");
- wil->n_msi = 0;
- }
- } else {
- wil_dbg(wil, "MSI interrupts disabled, use INTx\n");
+
+ if (use_msi == 3 && pci_enable_msi_range(pdev, 3, 3) < 0) {
+ wil_err(wil, "3 MSI mode failed, try 1 MSI\n");
+ use_msi = 1;
}
+ if (use_msi == 1 && pci_enable_msi(pdev)) {
+ wil_err(wil, "pci_enable_msi failed, use INTx\n");
+ use_msi = 0;
+ }
+
+ wil->n_msi = use_msi;
+
rc = wil6210_init_irq(wil, pdev->irq);
if (rc)
goto stop_master;
/* need reset here to obtain MAC */
+ mutex_lock(&wil->mutex);
rc = wil_reset(wil);
+ mutex_unlock(&wil->mutex);
if (rc)
goto release_irq;
@@ -141,7 +138,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto err_release_reg;
}
/* rollback to err_iounmap */
- dev_info(&pdev->dev, "CSR at %pR -> %p\n", &pdev->resource[0], csr);
+ dev_info(&pdev->dev, "CSR at %pR -> 0x%p\n", &pdev->resource[0], csr);
wil = wil_if_alloc(dev, csr);
if (IS_ERR(wil)) {
@@ -154,6 +151,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
pci_set_drvdata(pdev, wil);
wil->pdev = pdev;
+ wil6210_clear_irq(wil);
/* FW should raise IRQ when ready */
rc = wil_if_pcie_enable(wil);
if (rc) {
@@ -200,7 +198,6 @@ static void wil_pcie_remove(struct pci_dev *pdev)
pci_iounmap(pdev, wil->csr);
pci_release_region(pdev, 0);
pci_disable_device(pdev);
- pci_set_drvdata(pdev, NULL);
}
static DEFINE_PCI_DEVICE_TABLE(wil6210_pcie_ids) = {
diff --git a/drivers/net/wireless/ath/wil6210/rx_reorder.c b/drivers/net/wireless/ath/wil6210/rx_reorder.c
new file mode 100644
index 00000000000..747ae127587
--- /dev/null
+++ b/drivers/net/wireless/ath/wil6210/rx_reorder.c
@@ -0,0 +1,201 @@
+#include "wil6210.h"
+#include "txrx.h"
+
+#define SEQ_MODULO 0x1000
+#define SEQ_MASK 0xfff
+
+static inline int seq_less(u16 sq1, u16 sq2)
+{
+ return ((sq1 - sq2) & SEQ_MASK) > (SEQ_MODULO >> 1);
+}
+
+static inline u16 seq_inc(u16 sq)
+{
+ return (sq + 1) & SEQ_MASK;
+}
+
+static inline u16 seq_sub(u16 sq1, u16 sq2)
+{
+ return (sq1 - sq2) & SEQ_MASK;
+}
+
+static inline int reorder_index(struct wil_tid_ampdu_rx *r, u16 seq)
+{
+ return seq_sub(seq, r->ssn) % r->buf_size;
+}
+
+static void wil_release_reorder_frame(struct wil6210_priv *wil,
+ struct wil_tid_ampdu_rx *r,
+ int index)
+{
+ struct net_device *ndev = wil_to_ndev(wil);
+ struct sk_buff *skb = r->reorder_buf[index];
+
+ if (!skb)
+ goto no_frame;
+
+ /* release the frame from the reorder ring buffer */
+ r->stored_mpdu_num--;
+ r->reorder_buf[index] = NULL;
+ wil_netif_rx_any(skb, ndev);
+
+no_frame:
+ r->head_seq_num = seq_inc(r->head_seq_num);
+}
+
+static void wil_release_reorder_frames(struct wil6210_priv *wil,
+ struct wil_tid_ampdu_rx *r,
+ u16 hseq)
+{
+ int index;
+
+ /* note: this function is never called with
+ * hseq preceding r->head_seq_num, i.e it is always true
+ * !seq_less(hseq, r->head_seq_num)
+ * and thus on loop exit it should be
+ * r->head_seq_num == hseq
+ */
+ while (seq_less(r->head_seq_num, hseq) && r->stored_mpdu_num) {
+ index = reorder_index(r, r->head_seq_num);
+ wil_release_reorder_frame(wil, r, index);
+ }
+ r->head_seq_num = hseq;
+}
+
+static void wil_reorder_release(struct wil6210_priv *wil,
+ struct wil_tid_ampdu_rx *r)
+{
+ int index = reorder_index(r, r->head_seq_num);
+
+ while (r->reorder_buf[index]) {
+ wil_release_reorder_frame(wil, r, index);
+ index = reorder_index(r, r->head_seq_num);
+ }
+}
+
+void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb)
+{
+ struct net_device *ndev = wil_to_ndev(wil);
+ struct vring_rx_desc *d = wil_skb_rxdesc(skb);
+ int tid = wil_rxdesc_tid(d);
+ int cid = wil_rxdesc_cid(d);
+ int mid = wil_rxdesc_mid(d);
+ u16 seq = wil_rxdesc_seq(d);
+ struct wil_sta_info *sta = &wil->sta[cid];
+ struct wil_tid_ampdu_rx *r = sta->tid_rx[tid];
+ u16 hseq;
+ int index;
+
+ wil_dbg_txrx(wil, "MID %d CID %d TID %d Seq 0x%03x\n",
+ mid, cid, tid, seq);
+
+ if (!r) {
+ wil_netif_rx_any(skb, ndev);
+ return;
+ }
+
+ hseq = r->head_seq_num;
+
+ spin_lock(&r->reorder_lock);
+
+ /** Due to the race between WMI events, where BACK establishment
+ * reported, and data Rx, few packets may be pass up before reorder
+ * buffer get allocated. Catch up by pretending SSN is what we
+ * see in the 1-st Rx packet
+ */
+ if (r->first_time) {
+ r->first_time = false;
+ if (seq != r->head_seq_num) {
+ wil_err(wil, "Error: 1-st frame with wrong sequence"
+ " %d, should be %d. Fixing...\n", seq,
+ r->head_seq_num);
+ r->head_seq_num = seq;
+ r->ssn = seq;
+ }
+ }
+
+ /* frame with out of date sequence number */
+ if (seq_less(seq, r->head_seq_num)) {
+ dev_kfree_skb(skb);
+ goto out;
+ }
+
+ /*
+ * If frame the sequence number exceeds our buffering window
+ * size release some previous frames to make room for this one.
+ */
+ if (!seq_less(seq, r->head_seq_num + r->buf_size)) {
+ hseq = seq_inc(seq_sub(seq, r->buf_size));
+ /* release stored frames up to new head to stack */
+ wil_release_reorder_frames(wil, r, hseq);
+ }
+
+ /* Now the new frame is always in the range of the reordering buffer */
+
+ index = reorder_index(r, seq);
+
+ /* check if we already stored this frame */
+ if (r->reorder_buf[index]) {
+ dev_kfree_skb(skb);
+ goto out;
+ }
+
+ /*
+ * If the current MPDU is in the right order and nothing else
+ * is stored we can process it directly, no need to buffer it.
+ * If it is first but there's something stored, we may be able
+ * to release frames after this one.
+ */
+ if (seq == r->head_seq_num && r->stored_mpdu_num == 0) {
+ r->head_seq_num = seq_inc(r->head_seq_num);
+ wil_netif_rx_any(skb, ndev);
+ goto out;
+ }
+
+ /* put the frame in the reordering buffer */
+ r->reorder_buf[index] = skb;
+ r->reorder_time[index] = jiffies;
+ r->stored_mpdu_num++;
+ wil_reorder_release(wil, r);
+
+out:
+ spin_unlock(&r->reorder_lock);
+}
+
+struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil,
+ int size, u16 ssn)
+{
+ struct wil_tid_ampdu_rx *r = kzalloc(sizeof(*r), GFP_KERNEL);
+ if (!r)
+ return NULL;
+
+ r->reorder_buf =
+ kcalloc(size, sizeof(struct sk_buff *), GFP_KERNEL);
+ r->reorder_time =
+ kcalloc(size, sizeof(unsigned long), GFP_KERNEL);
+ if (!r->reorder_buf || !r->reorder_time) {
+ kfree(r->reorder_buf);
+ kfree(r->reorder_time);
+ kfree(r);
+ return NULL;
+ }
+
+ spin_lock_init(&r->reorder_lock);
+ r->ssn = ssn;
+ r->head_seq_num = ssn;
+ r->buf_size = size;
+ r->stored_mpdu_num = 0;
+ r->first_time = true;
+ return r;
+}
+
+void wil_tid_ampdu_rx_free(struct wil6210_priv *wil,
+ struct wil_tid_ampdu_rx *r)
+{
+ if (!r)
+ return;
+ wil_release_reorder_frames(wil, r, r->head_seq_num + r->buf_size);
+ kfree(r->reorder_buf);
+ kfree(r->reorder_time);
+ kfree(r);
+}
diff --git a/drivers/net/wireless/ath/wil6210/trace.c b/drivers/net/wireless/ath/wil6210/trace.c
new file mode 100644
index 00000000000..cd2534b9c5a
--- /dev/null
+++ b/drivers/net/wireless/ath/wil6210/trace.c
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2013 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/module.h>
+
+#define CREATE_TRACE_POINTS
+#include "trace.h"
diff --git a/drivers/net/wireless/ath/wil6210/trace.h b/drivers/net/wireless/ath/wil6210/trace.h
new file mode 100644
index 00000000000..e59239d22b9
--- /dev/null
+++ b/drivers/net/wireless/ath/wil6210/trace.h
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2013 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.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM wil6210
+#if !defined(WIL6210_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define WIL6210_TRACE_H
+
+#include <linux/tracepoint.h>
+#include "wil6210.h"
+#include "txrx.h"
+
+/* create empty functions when tracing is disabled */
+#if !defined(CONFIG_WIL6210_TRACING) || defined(__CHECKER__)
+
+#undef TRACE_EVENT
+#define TRACE_EVENT(name, proto, ...) \
+static inline void trace_ ## name(proto) {}
+#undef DECLARE_EVENT_CLASS
+#define DECLARE_EVENT_CLASS(...)
+#undef DEFINE_EVENT
+#define DEFINE_EVENT(evt_class, name, proto, ...) \
+static inline void trace_ ## name(proto) {}
+#endif /* !CONFIG_WIL6210_TRACING || defined(__CHECKER__) */
+
+DECLARE_EVENT_CLASS(wil6210_wmi,
+ TP_PROTO(struct wil6210_mbox_hdr_wmi *wmi, void *buf, u16 buf_len),
+
+ TP_ARGS(wmi, buf, buf_len),
+
+ TP_STRUCT__entry(
+ __field(u8, mid)
+ __field(u16, id)
+ __field(u32, timestamp)
+ __field(u16, buf_len)
+ __dynamic_array(u8, buf, buf_len)
+ ),
+
+ TP_fast_assign(
+ __entry->mid = wmi->mid;
+ __entry->id = le16_to_cpu(wmi->id);
+ __entry->timestamp = le32_to_cpu(wmi->timestamp);
+ __entry->buf_len = buf_len;
+ memcpy(__get_dynamic_array(buf), buf, buf_len);
+ ),
+
+ TP_printk(
+ "MID %d id 0x%04x len %d timestamp %d",
+ __entry->mid, __entry->id, __entry->buf_len, __entry->timestamp
+ )
+);
+
+DEFINE_EVENT(wil6210_wmi, wil6210_wmi_cmd,
+ TP_PROTO(struct wil6210_mbox_hdr_wmi *wmi, void *buf, u16 buf_len),
+ TP_ARGS(wmi, buf, buf_len)
+);
+
+DEFINE_EVENT(wil6210_wmi, wil6210_wmi_event,
+ TP_PROTO(struct wil6210_mbox_hdr_wmi *wmi, void *buf, u16 buf_len),
+ TP_ARGS(wmi, buf, buf_len)
+);
+
+#define WIL6210_MSG_MAX (200)
+
+DECLARE_EVENT_CLASS(wil6210_log_event,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf),
+ TP_STRUCT__entry(
+ __dynamic_array(char, msg, WIL6210_MSG_MAX)
+ ),
+ TP_fast_assign(
+ WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
+ WIL6210_MSG_MAX,
+ vaf->fmt,
+ *vaf->va) >= WIL6210_MSG_MAX);
+ ),
+ TP_printk("%s", __get_str(msg))
+);
+
+DEFINE_EVENT(wil6210_log_event, wil6210_log_err,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(wil6210_log_event, wil6210_log_info,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(wil6210_log_event, wil6210_log_dbg,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
+#define wil_pseudo_irq_cause(x) __print_flags(x, "|", \
+ {BIT_DMA_PSEUDO_CAUSE_RX, "Rx" }, \
+ {BIT_DMA_PSEUDO_CAUSE_TX, "Tx" }, \
+ {BIT_DMA_PSEUDO_CAUSE_MISC, "Misc" })
+
+TRACE_EVENT(wil6210_irq_pseudo,
+ TP_PROTO(u32 x),
+ TP_ARGS(x),
+ TP_STRUCT__entry(
+ __field(u32, x)
+ ),
+ TP_fast_assign(
+ __entry->x = x;
+ ),
+ TP_printk("cause 0x%08x : %s", __entry->x,
+ wil_pseudo_irq_cause(__entry->x))
+);
+
+DECLARE_EVENT_CLASS(wil6210_irq,
+ TP_PROTO(u32 x),
+ TP_ARGS(x),
+ TP_STRUCT__entry(
+ __field(u32, x)
+ ),
+ TP_fast_assign(
+ __entry->x = x;
+ ),
+ TP_printk("cause 0x%08x", __entry->x)
+);
+
+DEFINE_EVENT(wil6210_irq, wil6210_irq_rx,
+ TP_PROTO(u32 x),
+ TP_ARGS(x)
+);
+
+DEFINE_EVENT(wil6210_irq, wil6210_irq_tx,
+ TP_PROTO(u32 x),
+ TP_ARGS(x)
+);
+
+DEFINE_EVENT(wil6210_irq, wil6210_irq_misc,
+ TP_PROTO(u32 x),
+ TP_ARGS(x)
+);
+
+DEFINE_EVENT(wil6210_irq, wil6210_irq_misc_thread,
+ TP_PROTO(u32 x),
+ TP_ARGS(x)
+);
+
+TRACE_EVENT(wil6210_rx,
+ TP_PROTO(u16 index, struct vring_rx_desc *d),
+ TP_ARGS(index, d),
+ TP_STRUCT__entry(
+ __field(u16, index)
+ __field(unsigned int, len)
+ __field(u8, mid)
+ __field(u8, cid)
+ __field(u8, tid)
+ __field(u8, type)
+ __field(u8, subtype)
+ __field(u16, seq)
+ __field(u8, mcs)
+ ),
+ TP_fast_assign(
+ __entry->index = index;
+ __entry->len = d->dma.length;
+ __entry->mid = wil_rxdesc_mid(d);
+ __entry->cid = wil_rxdesc_cid(d);
+ __entry->tid = wil_rxdesc_tid(d);
+ __entry->type = wil_rxdesc_ftype(d);
+ __entry->subtype = wil_rxdesc_subtype(d);
+ __entry->seq = wil_rxdesc_seq(d);
+ __entry->mcs = wil_rxdesc_mcs(d);
+ ),
+ TP_printk("index %d len %d mid %d cid %d tid %d mcs %d seq 0x%03x"
+ " type 0x%1x subtype 0x%1x", __entry->index, __entry->len,
+ __entry->mid, __entry->cid, __entry->tid, __entry->mcs,
+ __entry->seq, __entry->type, __entry->subtype)
+);
+
+TRACE_EVENT(wil6210_tx,
+ TP_PROTO(u8 vring, u16 index, unsigned int len, u8 frags),
+ TP_ARGS(vring, index, len, frags),
+ TP_STRUCT__entry(
+ __field(u8, vring)
+ __field(u8, frags)
+ __field(u16, index)
+ __field(unsigned int, len)
+ ),
+ TP_fast_assign(
+ __entry->vring = vring;
+ __entry->frags = frags;
+ __entry->index = index;
+ __entry->len = len;
+ ),
+ TP_printk("vring %d index %d len %d frags %d",
+ __entry->vring, __entry->index, __entry->len, __entry->frags)
+);
+
+TRACE_EVENT(wil6210_tx_done,
+ TP_PROTO(u8 vring, u16 index, unsigned int len, u8 err),
+ TP_ARGS(vring, index, len, err),
+ TP_STRUCT__entry(
+ __field(u8, vring)
+ __field(u8, err)
+ __field(u16, index)
+ __field(unsigned int, len)
+ ),
+ TP_fast_assign(
+ __entry->vring = vring;
+ __entry->index = index;
+ __entry->len = len;
+ __entry->err = err;
+ ),
+ TP_printk("vring %d index %d len %d err 0x%02x",
+ __entry->vring, __entry->index, __entry->len,
+ __entry->err)
+);
+
+#endif /* WIL6210_TRACE_H || TRACE_HEADER_MULTI_READ*/
+
+#if defined(CONFIG_WIL6210_TRACING) && !defined(__CHECKER__)
+/* we don't want to use include/trace/events */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE trace
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
+#endif /* defined(CONFIG_WIL6210_TRACING) && !defined(__CHECKER__) */
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index f29c294413c..0784ef3d4ce 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -14,17 +14,19 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include <linux/kernel.h>
-#include <linux/netdevice.h>
#include <linux/etherdevice.h>
-#include <linux/hardirq.h>
#include <net/ieee80211_radiotap.h>
#include <linux/if_arp.h>
#include <linux/moduleparam.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <net/ipv6.h>
+#include <linux/prefetch.h>
#include "wil6210.h"
#include "wmi.h"
#include "txrx.h"
+#include "trace.h"
static bool rtap_include_phy_info;
module_param(rtap_include_phy_info, bool, S_IRUGO);
@@ -62,6 +64,22 @@ static inline int wil_vring_avail_tx(struct vring *vring)
return vring->size - used - 1;
}
+/**
+ * wil_vring_wmark_low - low watermark for available descriptor space
+ */
+static inline int wil_vring_wmark_low(struct vring *vring)
+{
+ return vring->size/8;
+}
+
+/**
+ * wil_vring_wmark_high - high watermark for available descriptor space
+ */
+static inline int wil_vring_wmark_high(struct vring *vring)
+{
+ return vring->size/4;
+}
+
static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)
{
struct device *dev = wil_to_dev(wil);
@@ -72,10 +90,8 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)
vring->swhead = 0;
vring->swtail = 0;
- vring->ctx = kzalloc(vring->size * sizeof(vring->ctx[0]), GFP_KERNEL);
+ vring->ctx = kcalloc(vring->size, sizeof(vring->ctx[0]), GFP_KERNEL);
if (!vring->ctx) {
- wil_err(wil, "vring_alloc [%d] failed to alloc ctx mem\n",
- vring->size);
vring->va = NULL;
return -ENOMEM;
}
@@ -85,8 +101,6 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)
*/
vring->va = dma_alloc_coherent(dev, sz, &vring->pa, GFP_KERNEL);
if (!vring->va) {
- wil_err(wil, "vring_alloc [%d] failed to alloc DMA mem\n",
- vring->size);
kfree(vring->ctx);
vring->ctx = NULL;
return -ENOMEM;
@@ -96,16 +110,33 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)
* we can use any
*/
for (i = 0; i < vring->size; i++) {
- volatile struct vring_tx_desc *d = &(vring->va[i].tx);
- d->dma.status = TX_DMA_STATUS_DU;
+ volatile struct vring_tx_desc *_d = &(vring->va[i].tx);
+ _d->dma.status = TX_DMA_STATUS_DU;
}
- wil_dbg(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size,
- vring->va, (unsigned long long)vring->pa, vring->ctx);
+ wil_dbg_misc(wil, "vring[%d] 0x%p:%pad 0x%p\n", vring->size,
+ vring->va, &vring->pa, vring->ctx);
return 0;
}
+static void wil_txdesc_unmap(struct device *dev, struct vring_tx_desc *d,
+ struct wil_ctx *ctx)
+{
+ dma_addr_t pa = wil_desc_addr(&d->dma.addr);
+ u16 dmalen = le16_to_cpu(d->dma.length);
+ switch (ctx->mapped_as) {
+ case wil_mapped_as_single:
+ dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
+ break;
+ case wil_mapped_as_page:
+ dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE);
+ break;
+ default:
+ break;
+ }
+}
+
static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring,
int tx)
{
@@ -113,31 +144,32 @@ static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring,
size_t sz = vring->size * sizeof(vring->va[0]);
while (!wil_vring_is_empty(vring)) {
+ dma_addr_t pa;
+ u16 dmalen;
+ struct wil_ctx *ctx;
+
if (tx) {
- volatile struct vring_tx_desc *d =
+ struct vring_tx_desc dd, *d = &dd;
+ volatile struct vring_tx_desc *_d =
&vring->va[vring->swtail].tx;
- dma_addr_t pa = d->dma.addr_low |
- ((u64)d->dma.addr_high << 32);
- struct sk_buff *skb = vring->ctx[vring->swtail];
- if (skb) {
- dma_unmap_single(dev, pa, d->dma.length,
- DMA_TO_DEVICE);
- dev_kfree_skb_any(skb);
- vring->ctx[vring->swtail] = NULL;
- } else {
- dma_unmap_page(dev, pa, d->dma.length,
- DMA_TO_DEVICE);
- }
+
+ ctx = &vring->ctx[vring->swtail];
+ *d = *_d;
+ wil_txdesc_unmap(dev, d, ctx);
+ if (ctx->skb)
+ dev_kfree_skb_any(ctx->skb);
vring->swtail = wil_vring_next_tail(vring);
} else { /* rx */
- volatile struct vring_rx_desc *d =
- &vring->va[vring->swtail].rx;
- dma_addr_t pa = d->dma.addr_low |
- ((u64)d->dma.addr_high << 32);
- struct sk_buff *skb = vring->ctx[vring->swhead];
- dma_unmap_single(dev, pa, d->dma.length,
- DMA_FROM_DEVICE);
- kfree_skb(skb);
+ struct vring_rx_desc dd, *d = &dd;
+ volatile struct vring_rx_desc *_d =
+ &vring->va[vring->swhead].rx;
+
+ ctx = &vring->ctx[vring->swhead];
+ *d = *_d;
+ pa = wil_desc_addr(&d->dma.addr);
+ dmalen = le16_to_cpu(d->dma.length);
+ dma_unmap_single(dev, pa, dmalen, DMA_FROM_DEVICE);
+ kfree_skb(ctx->skb);
wil_vring_advance_head(vring, 1);
}
}
@@ -158,7 +190,8 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring,
{
struct device *dev = wil_to_dev(wil);
unsigned int sz = RX_BUF_LEN;
- volatile struct vring_rx_desc *d = &(vring->va[i].rx);
+ struct vring_rx_desc dd, *d = &dd;
+ volatile struct vring_rx_desc *_d = &(vring->va[i].rx);
dma_addr_t pa;
/* TODO align */
@@ -176,14 +209,14 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring,
}
d->dma.d0 = BIT(9) | RX_DMA_D0_CMD_DMA_IT;
- d->dma.addr_low = lower_32_bits(pa);
- d->dma.addr_high = (u16)upper_32_bits(pa);
+ wil_desc_addr_set(&d->dma.addr, pa);
/* ip_length don't care */
/* b11 don't care */
/* error don't care */
d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */
- d->dma.length = sz;
- vring->ctx[i] = skb;
+ d->dma.length = cpu_to_le16(sz);
+ *_d = *d;
+ vring->ctx[i].skb = skb;
return 0;
}
@@ -198,8 +231,7 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring,
* - Phy info
*/
static void wil_rx_add_radiotap_header(struct wil6210_priv *wil,
- struct sk_buff *skb,
- volatile struct vring_rx_desc *d)
+ struct sk_buff *skb)
{
struct wireless_dev *wdev = wil->wdev;
struct wil6210_rtap {
@@ -223,6 +255,7 @@ static void wil_rx_add_radiotap_header(struct wil6210_priv *wil,
__le16 vendor_skip;
u8 vendor_data[0];
} __packed;
+ struct vring_rx_desc *d = wil_skb_rxdesc(skb);
struct wil6210_rtap_vendor *rtap_vendor;
int rtap_len = sizeof(struct wil6210_rtap);
int phy_length = 0; /* phy info header size, bytes */
@@ -319,6 +352,8 @@ static void wil_swap_ethaddr(void *data)
/**
* reap 1 frame from @swhead
*
+ * Rx descriptor copied to skb->cb
+ *
* Safe to call from IRQ
*/
static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
@@ -326,38 +361,64 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
{
struct device *dev = wil_to_dev(wil);
struct net_device *ndev = wil_to_ndev(wil);
- volatile struct vring_rx_desc *d;
+ volatile struct vring_rx_desc *_d;
+ struct vring_rx_desc *d;
struct sk_buff *skb;
dma_addr_t pa;
unsigned int sz = RX_BUF_LEN;
+ u16 dmalen;
u8 ftype;
u8 ds_bits;
+ int cid;
+ struct wil_net_stats *stats;
+
+
+ BUILD_BUG_ON(sizeof(struct vring_rx_desc) > sizeof(skb->cb));
if (wil_vring_is_empty(vring))
return NULL;
- d = &(vring->va[vring->swhead].rx);
- if (!(d->dma.status & RX_DMA_STATUS_DU)) {
+ _d = &(vring->va[vring->swhead].rx);
+ if (!(_d->dma.status & RX_DMA_STATUS_DU)) {
/* it is not error, we just reached end of Rx done area */
return NULL;
}
- pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32);
- skb = vring->ctx[vring->swhead];
+ skb = vring->ctx[vring->swhead].skb;
+ d = wil_skb_rxdesc(skb);
+ *d = *_d;
+ pa = wil_desc_addr(&d->dma.addr);
+ vring->ctx[vring->swhead].skb = NULL;
+ wil_vring_advance_head(vring, 1);
+
dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE);
- skb_trim(skb, d->dma.length);
+ dmalen = le16_to_cpu(d->dma.length);
- wil->stats.last_mcs_rx = wil_rxdesc_mcs(d);
+ trace_wil6210_rx(vring->swhead, d);
+ wil_dbg_txrx(wil, "Rx[%3d] : %d bytes\n", vring->swhead, dmalen);
+ wil_hex_dump_txrx("Rx ", DUMP_PREFIX_NONE, 32, 4,
+ (const void *)d, sizeof(*d), false);
- /* use radiotap header only if required */
- if (ndev->type == ARPHRD_IEEE80211_RADIOTAP)
- wil_rx_add_radiotap_header(wil, skb, d);
+ if (dmalen > sz) {
+ wil_err(wil, "Rx size too large: %d bytes!\n", dmalen);
+ kfree_skb(skb);
+ return NULL;
+ }
+ skb_trim(skb, dmalen);
- wil_dbg_TXRX(wil, "Rx[%3d] : %d bytes\n", vring->swhead, d->dma.length);
- wil_hex_dump_TXRX("Rx ", DUMP_PREFIX_NONE, 32, 4,
- (const void *)d, sizeof(*d), false);
+ prefetch(skb->data);
- wil_vring_advance_head(vring, 1);
+ wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1,
+ skb->data, skb_headlen(skb), false);
+
+ cid = wil_rxdesc_cid(d);
+ stats = &wil->sta[cid].stats;
+ stats->last_mcs_rx = wil_rxdesc_mcs(d);
+ wil->stats.last_mcs_rx = stats->last_mcs_rx;
+
+ /* use radiotap header only if required */
+ if (ndev->type == ARPHRD_IEEE80211_RADIOTAP)
+ wil_rx_add_radiotap_header(wil, skb);
/* no extra checks if in sniffer mode */
if (ndev->type != ARPHRD_ETHER)
@@ -369,7 +430,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
*/
ftype = wil_rxdesc_ftype(d) << 2;
if (ftype != IEEE80211_FTYPE_DATA) {
- wil_dbg_TXRX(wil, "Non-data frame ftype 0x%08x\n", ftype);
+ wil_dbg_txrx(wil, "Non-data frame ftype 0x%08x\n", ftype);
/* TODO: process it */
kfree_skb(skb);
return NULL;
@@ -382,6 +443,21 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
return NULL;
}
+ /* L4 IDENT is on when HW calculated checksum, check status
+ * and in case of error drop the packet
+ * higher stack layers will handle retransmission (if required)
+ */
+ if (d->dma.status & RX_DMA_STATUS_L4_IDENT) {
+ /* L4 protocol identified, csum calculated */
+ if ((d->dma.error & RX_DMA_ERROR_L4_ERR) == 0)
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ /* If HW reports bad checksum, let IP stack re-check it
+ * For example, HW don't understand Microsoft IP stack that
+ * mis-calculates TCP checksum - if it should be 0x0,
+ * it writes 0xffff in violation of RFC 1624
+ */
+ }
+
ds_bits = wil_rxdesc_ds_bits(d);
if (ds_bits == 1) {
/*
@@ -424,32 +500,39 @@ static int wil_rx_refill(struct wil6210_priv *wil, int count)
/*
* Pass Rx packet to the netif. Update statistics.
+ * Called in softirq context (NAPI poll).
*/
-static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
+void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
{
- int rc;
+ gro_result_t rc;
+ struct wil6210_priv *wil = ndev_to_wil(ndev);
unsigned int len = skb->len;
+ struct vring_rx_desc *d = wil_skb_rxdesc(skb);
+ int cid = wil_rxdesc_cid(d);
+ struct wil_net_stats *stats = &wil->sta[cid].stats;
- if (in_interrupt())
- rc = netif_rx(skb);
- else
- rc = netif_rx_ni(skb);
+ skb_orphan(skb);
- if (likely(rc == NET_RX_SUCCESS)) {
- ndev->stats.rx_packets++;
- ndev->stats.rx_bytes += len;
+ rc = napi_gro_receive(&wil->napi_rx, skb);
- } else {
+ if (unlikely(rc == GRO_DROP)) {
ndev->stats.rx_dropped++;
+ stats->rx_dropped++;
+ wil_dbg_txrx(wil, "Rx drop %d bytes\n", len);
+ } else {
+ ndev->stats.rx_packets++;
+ stats->rx_packets++;
+ ndev->stats.rx_bytes += len;
+ stats->rx_bytes += len;
}
}
/**
* Proceed all completed skb's from Rx VRING
*
- * Safe to call from IRQ
+ * Safe to call from NAPI poll, i.e. softirq with interrupts enabled
*/
-void wil_rx_handle(struct wil6210_priv *wil)
+void wil_rx_handle(struct wil6210_priv *wil, int *quota)
{
struct net_device *ndev = wil_to_ndev(wil);
struct vring *v = &wil->vring_rx;
@@ -459,12 +542,9 @@ void wil_rx_handle(struct wil6210_priv *wil)
wil_err(wil, "Rx IRQ while Rx not yet initialized\n");
return;
}
- wil_dbg_TXRX(wil, "%s()\n", __func__);
- while (NULL != (skb = wil_vring_reap_rx(wil, v))) {
- wil_hex_dump_TXRX("Rx ", DUMP_PREFIX_OFFSET, 16, 1,
- skb->data, skb_headlen(skb), false);
-
- skb_orphan(skb);
+ wil_dbg_txrx(wil, "%s()\n", __func__);
+ while ((*quota > 0) && (NULL != (skb = wil_vring_reap_rx(wil, v)))) {
+ (*quota)--;
if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) {
skb->dev = ndev;
@@ -472,65 +552,41 @@ void wil_rx_handle(struct wil6210_priv *wil)
skb->ip_summed = CHECKSUM_UNNECESSARY;
skb->pkt_type = PACKET_OTHERHOST;
skb->protocol = htons(ETH_P_802_2);
-
+ wil_netif_rx_any(skb, ndev);
} else {
+ struct ethhdr *eth = (void *)skb->data;
+
skb->protocol = eth_type_trans(skb, ndev);
+
+ if (is_unicast_ether_addr(eth->h_dest))
+ wil_rx_reorder(wil, skb);
+ else
+ wil_netif_rx_any(skb, ndev);
}
- wil_netif_rx_any(skb, ndev);
}
wil_rx_refill(wil, v->size);
}
int wil_rx_init(struct wil6210_priv *wil)
{
- struct net_device *ndev = wil_to_ndev(wil);
- struct wireless_dev *wdev = wil->wdev;
struct vring *vring = &wil->vring_rx;
int rc;
- struct wmi_cfg_rx_chain_cmd cmd = {
- .action = WMI_RX_CHAIN_ADD,
- .rx_sw_ring = {
- .max_mpdu_size = cpu_to_le16(RX_BUF_LEN),
- },
- .mid = 0, /* TODO - what is it? */
- .decap_trans_type = WMI_DECAP_TYPE_802_3,
- };
- struct {
- struct wil6210_mbox_hdr_wmi wmi;
- struct wmi_cfg_rx_chain_done_event evt;
- } __packed evt;
+
+ if (vring->va) {
+ wil_err(wil, "Rx ring already allocated\n");
+ return -EINVAL;
+ }
vring->size = WIL6210_RX_RING_SIZE;
rc = wil_vring_alloc(wil, vring);
if (rc)
return rc;
- cmd.rx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa);
- cmd.rx_sw_ring.ring_size = cpu_to_le16(vring->size);
- if (wdev->iftype == NL80211_IFTYPE_MONITOR) {
- struct ieee80211_channel *ch = wdev->preset_chandef.chan;
-
- cmd.sniffer_cfg.mode = cpu_to_le32(WMI_SNIFFER_ON);
- if (ch)
- cmd.sniffer_cfg.channel = ch->hw_value - 1;
- cmd.sniffer_cfg.phy_info_mode =
- cpu_to_le32(ndev->type == ARPHRD_IEEE80211_RADIOTAP);
- cmd.sniffer_cfg.phy_support =
- cpu_to_le32((wil->monitor_flags & MONITOR_FLAG_CONTROL)
- ? WMI_SNIFFER_CP : WMI_SNIFFER_DP);
- }
- /* typical time for secure PCP is 840ms */
- rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd),
- WMI_CFG_RX_CHAIN_DONE_EVENTID, &evt, sizeof(evt), 2000);
+ rc = wmi_rx_chain_add(wil, vring);
if (rc)
goto err_free;
- vring->hwtail = le32_to_cpu(evt.evt.rx_ring_tail_ptr);
-
- wil_dbg(wil, "Rx init: status %d tail 0x%08x\n",
- le32_to_cpu(evt.evt.status), vring->hwtail);
-
rc = wil_rx_refill(wil, vring->size);
if (rc)
goto err_free;
@@ -546,25 +602,8 @@ void wil_rx_fini(struct wil6210_priv *wil)
{
struct vring *vring = &wil->vring_rx;
- if (vring->va) {
- int rc;
- struct wmi_cfg_rx_chain_cmd cmd = {
- .action = cpu_to_le32(WMI_RX_CHAIN_DEL),
- .rx_sw_ring = {
- .max_mpdu_size = cpu_to_le16(RX_BUF_LEN),
- },
- };
- struct {
- struct wil6210_mbox_hdr_wmi wmi;
- struct wmi_cfg_rx_chain_done_event cfg;
- } __packed wmi_rx_cfg_reply;
-
- rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd),
- WMI_CFG_RX_CHAIN_DONE_EVENTID,
- &wmi_rx_cfg_reply, sizeof(wmi_rx_cfg_reply),
- 100);
+ if (vring->va)
wil_vring_free(wil, vring, 0);
- }
}
int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
@@ -576,9 +615,10 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
.vring_cfg = {
.tx_sw_ring = {
.max_mpdu_size = cpu_to_le16(TX_BUF_LEN),
+ .ring_size = cpu_to_le16(size),
},
.ringid = id,
- .cidxtid = (cid & 0xf) | ((tid & 0xf) << 4),
+ .cidxtid = mk_cidxtid(cid, tid),
.encap_trans_type = WMI_VRING_ENC_TYPE_802_3,
.mac_ctrl = 0,
.to_resolution = 0,
@@ -594,6 +634,7 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
struct wmi_vring_cfg_done_event cmd;
} __packed reply;
struct vring *vring = &wil->vring_tx[id];
+ struct vring_tx_data *txdata = &wil->vring_tx_data[id];
if (vring->va) {
wil_err(wil, "Tx ring [%d] already allocated\n", id);
@@ -601,26 +642,32 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
goto out;
}
+ memset(txdata, 0, sizeof(*txdata));
vring->size = size;
rc = wil_vring_alloc(wil, vring);
if (rc)
goto out;
+ wil->vring2cid_tid[id][0] = cid;
+ wil->vring2cid_tid[id][1] = tid;
+
cmd.vring_cfg.tx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa);
- cmd.vring_cfg.tx_sw_ring.ring_size = cpu_to_le16(vring->size);
rc = wmi_call(wil, WMI_VRING_CFG_CMDID, &cmd, sizeof(cmd),
WMI_VRING_CFG_DONE_EVENTID, &reply, sizeof(reply), 100);
if (rc)
goto out_free;
- if (reply.cmd.status != WMI_VRING_CFG_SUCCESS) {
+ if (reply.cmd.status != WMI_FW_STATUS_SUCCESS) {
wil_err(wil, "Tx config failed, status 0x%02x\n",
reply.cmd.status);
+ rc = -EINVAL;
goto out_free;
}
vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr);
+ txdata->enabled = 1;
+
return 0;
out_free:
wil_vring_free(wil, vring, 1);
@@ -633,35 +680,127 @@ void wil_vring_fini_tx(struct wil6210_priv *wil, int id)
{
struct vring *vring = &wil->vring_tx[id];
+ WARN_ON(!mutex_is_locked(&wil->mutex));
+
if (!vring->va)
return;
+ /* make sure NAPI won't touch this vring */
+ wil->vring_tx_data[id].enabled = 0;
+ if (test_bit(wil_status_napi_en, &wil->status))
+ napi_synchronize(&wil->napi_tx);
+
wil_vring_free(wil, vring, 1);
}
static struct vring *wil_find_tx_vring(struct wil6210_priv *wil,
struct sk_buff *skb)
{
- struct vring *v = &wil->vring_tx[0];
+ int i;
+ struct ethhdr *eth = (void *)skb->data;
+ int cid = wil_find_cid(wil, eth->h_dest);
+
+ if (cid < 0)
+ return NULL;
+
+ if (!wil->sta[cid].data_port_open &&
+ (skb->protocol != cpu_to_be16(ETH_P_PAE)))
+ return NULL;
+
+ /* TODO: fix for multiple TID */
+ for (i = 0; i < ARRAY_SIZE(wil->vring2cid_tid); i++) {
+ if (wil->vring2cid_tid[i][0] == cid) {
+ struct vring *v = &wil->vring_tx[i];
+ wil_dbg_txrx(wil, "%s(%pM) -> [%d]\n",
+ __func__, eth->h_dest, i);
+ if (v->va) {
+ return v;
+ } else {
+ wil_dbg_txrx(wil, "vring[%d] not valid\n", i);
+ return NULL;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static void wil_set_da_for_vring(struct wil6210_priv *wil,
+ struct sk_buff *skb, int vring_index)
+{
+ struct ethhdr *eth = (void *)skb->data;
+ int cid = wil->vring2cid_tid[vring_index][0];
+ memcpy(eth->h_dest, wil->sta[cid].addr, ETH_ALEN);
+}
+
+static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
+ struct sk_buff *skb);
+/*
+ * Find 1-st vring and return it; set dest address for this vring in skb
+ * duplicate skb and send it to other active vrings
+ */
+static struct vring *wil_tx_bcast(struct wil6210_priv *wil,
+ struct sk_buff *skb)
+{
+ struct vring *v, *v2;
+ struct sk_buff *skb2;
+ int i;
+ u8 cid;
+
+ /* find 1-st vring eligible for data */
+ for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
+ v = &wil->vring_tx[i];
+ if (!v->va)
+ continue;
+
+ cid = wil->vring2cid_tid[i][0];
+ if (!wil->sta[cid].data_port_open)
+ continue;
+
+ goto found;
+ }
- if (v->va)
- return v;
+ wil_err(wil, "Tx while no vrings active?\n");
return NULL;
+
+found:
+ wil_dbg_txrx(wil, "BCAST -> ring %d\n", i);
+ wil_set_da_for_vring(wil, skb, i);
+
+ /* find other active vrings and duplicate skb for each */
+ for (i++; i < WIL6210_MAX_TX_RINGS; i++) {
+ v2 = &wil->vring_tx[i];
+ if (!v2->va)
+ continue;
+ cid = wil->vring2cid_tid[i][0];
+ if (!wil->sta[cid].data_port_open)
+ continue;
+
+ skb2 = skb_copy(skb, GFP_ATOMIC);
+ if (skb2) {
+ wil_dbg_txrx(wil, "BCAST DUP -> ring %d\n", i);
+ wil_set_da_for_vring(wil, skb2, i);
+ wil_tx_vring(wil, v2, skb2);
+ } else {
+ wil_err(wil, "skb_copy failed\n");
+ }
+ }
+
+ return v;
}
-static int wil_tx_desc_map(volatile struct vring_tx_desc *d,
- dma_addr_t pa, u32 len)
+static int wil_tx_desc_map(struct vring_tx_desc *d, dma_addr_t pa, u32 len,
+ int vring_index)
{
- d->dma.addr_low = lower_32_bits(pa);
- d->dma.addr_high = (u16)upper_32_bits(pa);
+ wil_desc_addr_set(&d->dma.addr, pa);
d->dma.ip_length = 0;
/* 0..6: mac_length; 7:ip_version 0-IP6 1-IP4*/
d->dma.b11 = 0/*14 | BIT(7)*/;
d->dma.error = 0;
d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */
- d->dma.length = len;
- d->dma.d0 = 0;
+ d->dma.length = cpu_to_le16((u16)len);
+ d->dma.d0 = (vring_index << DMA_CFG_DESC_TX_0_QID_POS);
d->mac.d[0] = 0;
d->mac.d[1] = 0;
d->mac.d[2] = 0;
@@ -676,93 +815,168 @@ static int wil_tx_desc_map(volatile struct vring_tx_desc *d,
return 0;
}
+static inline
+void wil_tx_desc_set_nr_frags(struct vring_tx_desc *d, int nr_frags)
+{
+ d->mac.d[2] |= ((nr_frags + 1) <<
+ MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS);
+}
+
+static int wil_tx_desc_offload_cksum_set(struct wil6210_priv *wil,
+ struct vring_tx_desc *d,
+ struct sk_buff *skb)
+{
+ int protocol;
+
+ if (skb->ip_summed != CHECKSUM_PARTIAL)
+ return 0;
+
+ d->dma.b11 = ETH_HLEN; /* MAC header length */
+
+ switch (skb->protocol) {
+ case cpu_to_be16(ETH_P_IP):
+ protocol = ip_hdr(skb)->protocol;
+ d->dma.b11 |= BIT(DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_POS);
+ break;
+ case cpu_to_be16(ETH_P_IPV6):
+ protocol = ipv6_hdr(skb)->nexthdr;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (protocol) {
+ case IPPROTO_TCP:
+ d->dma.d0 |= (2 << DMA_CFG_DESC_TX_0_L4_TYPE_POS);
+ /* L4 header len: TCP header length */
+ d->dma.d0 |=
+ (tcp_hdrlen(skb) & DMA_CFG_DESC_TX_0_L4_LENGTH_MSK);
+ break;
+ case IPPROTO_UDP:
+ /* L4 header len: UDP header length */
+ d->dma.d0 |=
+ (sizeof(struct udphdr) & DMA_CFG_DESC_TX_0_L4_LENGTH_MSK);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ d->dma.ip_length = skb_network_header_len(skb);
+ /* Enable TCP/UDP checksum */
+ d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_POS);
+ /* Calculate pseudo-header */
+ d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_POS);
+
+ return 0;
+}
+
static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
struct sk_buff *skb)
{
struct device *dev = wil_to_dev(wil);
- volatile struct vring_tx_desc *d;
+ struct vring_tx_desc dd, *d = &dd;
+ volatile struct vring_tx_desc *_d;
u32 swhead = vring->swhead;
int avail = wil_vring_avail_tx(vring);
int nr_frags = skb_shinfo(skb)->nr_frags;
- uint f;
+ uint f = 0;
int vring_index = vring - wil->vring_tx;
uint i = swhead;
dma_addr_t pa;
- wil_dbg_TXRX(wil, "%s()\n", __func__);
+ wil_dbg_txrx(wil, "%s()\n", __func__);
- if (avail < vring->size/8)
- netif_tx_stop_all_queues(wil_to_ndev(wil));
if (avail < 1 + nr_frags) {
wil_err(wil, "Tx ring full. No space for %d fragments\n",
1 + nr_frags);
return -ENOMEM;
}
- d = &(vring->va[i].tx);
-
- /* FIXME FW can accept only unicast frames for the peer */
- memcpy(skb->data, wil->dst_addr[vring_index], ETH_ALEN);
+ _d = &(vring->va[i].tx);
pa = dma_map_single(dev, skb->data,
skb_headlen(skb), DMA_TO_DEVICE);
- wil_dbg_TXRX(wil, "Tx skb %d bytes %p -> %#08llx\n", skb_headlen(skb),
- skb->data, (unsigned long long)pa);
- wil_hex_dump_TXRX("Tx ", DUMP_PREFIX_OFFSET, 16, 1,
+ wil_dbg_txrx(wil, "Tx skb %d bytes 0x%p -> %pad\n", skb_headlen(skb),
+ skb->data, &pa);
+ wil_hex_dump_txrx("Tx ", DUMP_PREFIX_OFFSET, 16, 1,
skb->data, skb_headlen(skb), false);
if (unlikely(dma_mapping_error(dev, pa)))
return -EINVAL;
+ vring->ctx[i].mapped_as = wil_mapped_as_single;
/* 1-st segment */
- wil_tx_desc_map(d, pa, skb_headlen(skb));
- d->mac.d[2] |= ((nr_frags + 1) <<
- MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS);
+ wil_tx_desc_map(d, pa, skb_headlen(skb), vring_index);
+ /* Process TCP/UDP checksum offloading */
+ if (wil_tx_desc_offload_cksum_set(wil, d, skb)) {
+ wil_err(wil, "VRING #%d Failed to set cksum, drop packet\n",
+ vring_index);
+ goto dma_error;
+ }
+
+ vring->ctx[i].nr_frags = nr_frags;
+ wil_tx_desc_set_nr_frags(d, nr_frags);
+ if (nr_frags)
+ *_d = *d;
+
/* middle segments */
- for (f = 0; f < nr_frags; f++) {
+ for (; f < nr_frags; f++) {
const struct skb_frag_struct *frag =
&skb_shinfo(skb)->frags[f];
int len = skb_frag_size(frag);
i = (swhead + f + 1) % vring->size;
- d = &(vring->va[i].tx);
+ _d = &(vring->va[i].tx);
pa = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag),
DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(dev, pa)))
goto dma_error;
- wil_tx_desc_map(d, pa, len);
- vring->ctx[i] = NULL;
+ vring->ctx[i].mapped_as = wil_mapped_as_page;
+ wil_tx_desc_map(d, pa, len, vring_index);
+ /* no need to check return code -
+ * if it succeeded for 1-st descriptor,
+ * it will succeed here too
+ */
+ wil_tx_desc_offload_cksum_set(wil, d, skb);
+ *_d = *d;
}
/* for the last seg only */
d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_EOP_POS);
- d->dma.d0 |= BIT(9); /* BUG: undocumented bit */
+ d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_MARK_WB_POS);
d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS);
- d->dma.d0 |= (vring_index << DMA_CFG_DESC_TX_0_QID_POS);
+ *_d = *d;
+
+ /* hold reference to skb
+ * to prevent skb release before accounting
+ * in case of immediate "tx done"
+ */
+ vring->ctx[i].skb = skb_get(skb);
- wil_hex_dump_TXRX("Tx ", DUMP_PREFIX_NONE, 32, 4,
+ wil_hex_dump_txrx("Tx ", DUMP_PREFIX_NONE, 32, 4,
(const void *)d, sizeof(*d), false);
/* advance swhead */
wil_vring_advance_head(vring, nr_frags + 1);
- wil_dbg_TXRX(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead);
+ wil_dbg_txrx(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead);
+ trace_wil6210_tx(vring_index, swhead, skb->len, nr_frags);
iowrite32(vring->swhead, wil->csr + HOSTADDR(vring->hwtail));
- /* hold reference to skb
- * to prevent skb release before accounting
- * in case of immediate "tx done"
- */
- vring->ctx[i] = skb_get(skb);
return 0;
dma_error:
/* unmap what we have mapped */
- /* Note: increment @f to operate with positive index */
- for (f++; f > 0; f--) {
+ nr_frags = f + 1; /* frags mapped + one for skb head */
+ for (f = 0; f < nr_frags; f++) {
+ struct wil_ctx *ctx;
+
i = (swhead + f) % vring->size;
- d = &(vring->va[i].tx);
- d->dma.status = TX_DMA_STATUS_DU;
- pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32);
- if (vring->ctx[i])
- dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE);
- else
- dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE);
+ ctx = &vring->ctx[i];
+ _d = &(vring->va[i].tx);
+ *d = *_d;
+ _d->dma.status = TX_DMA_STATUS_DU;
+ wil_txdesc_unmap(dev, d, ctx);
+
+ if (ctx->skb)
+ dev_kfree_skb_any(ctx->skb);
+
+ memset(ctx, 0, sizeof(*ctx));
}
return -EINVAL;
@@ -772,12 +986,17 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
struct wil6210_priv *wil = ndev_to_wil(ndev);
+ struct ethhdr *eth = (void *)skb->data;
struct vring *vring;
+ static bool pr_once_fw;
int rc;
- wil_dbg_TXRX(wil, "%s()\n", __func__);
+ wil_dbg_txrx(wil, "%s()\n", __func__);
if (!test_bit(wil_status_fwready, &wil->status)) {
- wil_err(wil, "FW not ready\n");
+ if (!pr_once_fw) {
+ wil_err(wil, "FW not ready\n");
+ pr_once_fw = true;
+ }
goto drop;
}
if (!test_bit(wil_status_fwconnected, &wil->status)) {
@@ -788,32 +1007,36 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
wil_err(wil, "Xmit in monitor mode not supported\n");
goto drop;
}
- if (skb->protocol == cpu_to_be16(ETH_P_PAE)) {
- rc = wmi_tx_eapol(wil, skb);
- } else {
- /* find vring */
+ pr_once_fw = false;
+
+ /* find vring */
+ if (is_unicast_ether_addr(eth->h_dest)) {
vring = wil_find_tx_vring(wil, skb);
- if (!vring) {
- wil_err(wil, "No Tx VRING available\n");
- goto drop;
- }
- /* set up vring entry */
- rc = wil_tx_vring(wil, vring, skb);
+ } else {
+ vring = wil_tx_bcast(wil, skb);
+ }
+ if (!vring) {
+ wil_err(wil, "No Tx VRING found for %pM\n", eth->h_dest);
+ goto drop;
}
+ /* set up vring entry */
+ rc = wil_tx_vring(wil, vring, skb);
+
+ /* do we still have enough room in the vring? */
+ if (wil_vring_avail_tx(vring) < wil_vring_wmark_low(vring))
+ netif_tx_stop_all_queues(wil_to_ndev(wil));
+
switch (rc) {
case 0:
- ndev->stats.tx_packets++;
- ndev->stats.tx_bytes += skb->len;
+ /* statistics will be updated on the tx_complete */
dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
case -ENOMEM:
return NETDEV_TX_BUSY;
default:
- ; /* goto drop; */
- break;
+ break; /* goto drop; */
}
drop:
- netif_tx_stop_all_queues(ndev);
ndev->stats.tx_dropped++;
dev_kfree_skb_any(skb);
@@ -823,49 +1046,94 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
/**
* Clean up transmitted skb's from the Tx VRING
*
+ * Return number of descriptors cleared
+ *
* Safe to call from IRQ
*/
-void wil_tx_complete(struct wil6210_priv *wil, int ringid)
+int wil_tx_complete(struct wil6210_priv *wil, int ringid)
{
+ struct net_device *ndev = wil_to_ndev(wil);
struct device *dev = wil_to_dev(wil);
struct vring *vring = &wil->vring_tx[ringid];
+ struct vring_tx_data *txdata = &wil->vring_tx_data[ringid];
+ int done = 0;
+ int cid = wil->vring2cid_tid[ringid][0];
+ struct wil_net_stats *stats = &wil->sta[cid].stats;
+ volatile struct vring_tx_desc *_d;
if (!vring->va) {
wil_err(wil, "Tx irq[%d]: vring not initialized\n", ringid);
- return;
+ return 0;
+ }
+
+ if (!txdata->enabled) {
+ wil_info(wil, "Tx irq[%d]: vring disabled\n", ringid);
+ return 0;
}
- wil_dbg_TXRX(wil, "%s(%d)\n", __func__, ringid);
+ wil_dbg_txrx(wil, "%s(%d)\n", __func__, ringid);
while (!wil_vring_is_empty(vring)) {
- volatile struct vring_tx_desc *d = &vring->va[vring->swtail].tx;
- dma_addr_t pa;
- struct sk_buff *skb;
- if (!(d->dma.status & TX_DMA_STATUS_DU))
+ int new_swtail;
+ struct wil_ctx *ctx = &vring->ctx[vring->swtail];
+ /**
+ * For the fragmented skb, HW will set DU bit only for the
+ * last fragment. look for it
+ */
+ int lf = (vring->swtail + ctx->nr_frags) % vring->size;
+ /* TODO: check we are not past head */
+
+ _d = &vring->va[lf].tx;
+ if (!(_d->dma.status & TX_DMA_STATUS_DU))
break;
- wil_dbg_TXRX(wil,
- "Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n",
- vring->swtail, d->dma.length, d->dma.status,
- d->dma.error);
- wil_hex_dump_TXRX("TxC ", DUMP_PREFIX_NONE, 32, 4,
- (const void *)d, sizeof(*d), false);
-
- pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32);
- skb = vring->ctx[vring->swtail];
- if (skb) {
- dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE);
- dev_kfree_skb_any(skb);
- vring->ctx[vring->swtail] = NULL;
- } else {
- dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE);
+ new_swtail = (lf + 1) % vring->size;
+ while (vring->swtail != new_swtail) {
+ struct vring_tx_desc dd, *d = &dd;
+ u16 dmalen;
+ struct wil_ctx *ctx = &vring->ctx[vring->swtail];
+ struct sk_buff *skb = ctx->skb;
+ _d = &vring->va[vring->swtail].tx;
+
+ *d = *_d;
+
+ dmalen = le16_to_cpu(d->dma.length);
+ trace_wil6210_tx_done(ringid, vring->swtail, dmalen,
+ d->dma.error);
+ wil_dbg_txrx(wil,
+ "Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n",
+ vring->swtail, dmalen, d->dma.status,
+ d->dma.error);
+ wil_hex_dump_txrx("TxC ", DUMP_PREFIX_NONE, 32, 4,
+ (const void *)d, sizeof(*d), false);
+
+ wil_txdesc_unmap(dev, d, ctx);
+
+ if (skb) {
+ if (d->dma.error == 0) {
+ ndev->stats.tx_packets++;
+ stats->tx_packets++;
+ ndev->stats.tx_bytes += skb->len;
+ stats->tx_bytes += skb->len;
+ } else {
+ ndev->stats.tx_errors++;
+ stats->tx_errors++;
+ }
+
+ dev_kfree_skb_any(skb);
+ }
+ memset(ctx, 0, sizeof(*ctx));
+ /* There is no need to touch HW descriptor:
+ * - ststus bit TX_DMA_STATUS_DU is set by design,
+ * so hardware will not try to process this desc.,
+ * - rest of descriptor will be initialized on Tx.
+ */
+ vring->swtail = wil_vring_next_tail(vring);
+ done++;
}
- d->dma.addr_low = 0;
- d->dma.addr_high = 0;
- d->dma.length = 0;
- d->dma.status = TX_DMA_STATUS_DU;
- vring->swtail = wil_vring_next_tail(vring);
}
- if (wil_vring_avail_tx(vring) > vring->size/4)
+ if (wil_vring_avail_tx(vring) > wil_vring_wmark_high(vring))
netif_tx_wake_all_queues(wil_to_ndev(wil));
+
+ return done;
}
diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h
index 45a61f597c5..bc5706a4f00 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.h
+++ b/drivers/net/wireless/ath/wil6210/txrx.h
@@ -27,6 +27,28 @@
#define WIL6210_RTAP_SIZE (128)
/* Tx/Rx path */
+
+/*
+ * Common representation of physical address in Vring
+ */
+struct vring_dma_addr {
+ __le32 addr_low;
+ __le16 addr_high;
+} __packed;
+
+static inline dma_addr_t wil_desc_addr(struct vring_dma_addr *addr)
+{
+ return le32_to_cpu(addr->addr_low) |
+ ((u64)le16_to_cpu(addr->addr_high) << 32);
+}
+
+static inline void wil_desc_addr_set(struct vring_dma_addr *addr,
+ dma_addr_t pa)
+{
+ addr->addr_low = cpu_to_le32(lower_32_bits(pa));
+ addr->addr_high = cpu_to_le16((u16)upper_32_bits(pa));
+}
+
/*
* Tx descriptor - MAC part
* [dword 0]
@@ -179,6 +201,10 @@ struct vring_tx_mac {
#define DMA_CFG_DESC_TX_0_CMD_EOP_LEN 1
#define DMA_CFG_DESC_TX_0_CMD_EOP_MSK 0x100
+#define DMA_CFG_DESC_TX_0_CMD_MARK_WB_POS 9
+#define DMA_CFG_DESC_TX_0_CMD_MARK_WB_LEN 1
+#define DMA_CFG_DESC_TX_0_CMD_MARK_WB_MSK 0x200
+
#define DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS 10
#define DMA_CFG_DESC_TX_0_CMD_DMA_IT_LEN 1
#define DMA_CFG_DESC_TX_0_CMD_DMA_IT_MSK 0x400
@@ -209,20 +235,28 @@ struct vring_tx_mac {
#define DMA_CFG_DESC_TX_0_L4_TYPE_POS 30
#define DMA_CFG_DESC_TX_0_L4_TYPE_LEN 2
-#define DMA_CFG_DESC_TX_0_L4_TYPE_MSK 0xC0000000
+#define DMA_CFG_DESC_TX_0_L4_TYPE_MSK 0xC0000000 /* L4 type: 0-UDP, 2-TCP */
+
+
+#define DMA_CFG_DESC_TX_OFFLOAD_CFG_MAC_LEN_POS 0
+#define DMA_CFG_DESC_TX_OFFLOAD_CFG_MAC_LEN_LEN 7
+#define DMA_CFG_DESC_TX_OFFLOAD_CFG_MAC_LEN_MSK 0x7F /* MAC hdr len */
+
+#define DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_POS 7
+#define DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_LEN 1
+#define DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_MSK 0x80 /* 1-IPv4, 0-IPv6 */
#define TX_DMA_STATUS_DU BIT(0)
struct vring_tx_dma {
u32 d0;
- u32 addr_low;
- u16 addr_high;
+ struct vring_dma_addr addr;
u8 ip_length;
u8 b11; /* 0..6: mac_length; 7:ip_version */
u8 error; /* 0..2: err; 3..7: reserved; */
u8 status; /* 0: used; 1..7; reserved */
- u16 length;
+ __le16 length;
} __packed;
/*
@@ -309,19 +343,27 @@ struct vring_rx_mac {
#define RX_DMA_D0_CMD_DMA_IT BIT(10)
+/* Error field, offload bits */
+#define RX_DMA_ERROR_L3_ERR BIT(4)
+#define RX_DMA_ERROR_L4_ERR BIT(5)
+
+
+/* Status field */
#define RX_DMA_STATUS_DU BIT(0)
#define RX_DMA_STATUS_ERROR BIT(2)
+
+#define RX_DMA_STATUS_L3_IDENT BIT(4)
+#define RX_DMA_STATUS_L4_IDENT BIT(5)
#define RX_DMA_STATUS_PHY_INFO BIT(6)
struct vring_rx_dma {
u32 d0;
- u32 addr_low;
- u16 addr_high;
+ struct vring_dma_addr addr;
u8 ip_length;
u8 b11;
u8 error;
u8 status;
- u16 length;
+ __le16 length;
} __packed;
struct vring_tx_desc {
@@ -339,24 +381,66 @@ union vring_desc {
struct vring_rx_desc rx;
} __packed;
-static inline int wil_rxdesc_phy_length(volatile struct vring_rx_desc *d)
+static inline int wil_rxdesc_tid(struct vring_rx_desc *d)
{
- return WIL_GET_BITS(d->dma.d0, 16, 29);
+ return WIL_GET_BITS(d->mac.d0, 0, 3);
}
-static inline int wil_rxdesc_mcs(volatile struct vring_rx_desc *d)
+static inline int wil_rxdesc_cid(struct vring_rx_desc *d)
{
- return WIL_GET_BITS(d->mac.d1, 21, 24);
+ return WIL_GET_BITS(d->mac.d0, 4, 6);
}
-static inline int wil_rxdesc_ds_bits(volatile struct vring_rx_desc *d)
+static inline int wil_rxdesc_mid(struct vring_rx_desc *d)
{
- return WIL_GET_BITS(d->mac.d1, 8, 9);
+ return WIL_GET_BITS(d->mac.d0, 8, 9);
}
-static inline int wil_rxdesc_ftype(volatile struct vring_rx_desc *d)
+static inline int wil_rxdesc_ftype(struct vring_rx_desc *d)
{
return WIL_GET_BITS(d->mac.d0, 10, 11);
}
+static inline int wil_rxdesc_subtype(struct vring_rx_desc *d)
+{
+ return WIL_GET_BITS(d->mac.d0, 12, 15);
+}
+
+static inline int wil_rxdesc_seq(struct vring_rx_desc *d)
+{
+ return WIL_GET_BITS(d->mac.d0, 16, 27);
+}
+
+static inline int wil_rxdesc_ext_subtype(struct vring_rx_desc *d)
+{
+ return WIL_GET_BITS(d->mac.d0, 28, 31);
+}
+
+static inline int wil_rxdesc_ds_bits(struct vring_rx_desc *d)
+{
+ return WIL_GET_BITS(d->mac.d1, 8, 9);
+}
+
+static inline int wil_rxdesc_mcs(struct vring_rx_desc *d)
+{
+ return WIL_GET_BITS(d->mac.d1, 21, 24);
+}
+
+static inline int wil_rxdesc_phy_length(struct vring_rx_desc *d)
+{
+ return WIL_GET_BITS(d->dma.d0, 16, 29);
+}
+
+static inline struct vring_rx_desc *wil_skb_rxdesc(struct sk_buff *skb)
+{
+ return (void *)skb->cb;
+}
+
+void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev);
+void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb);
+struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil,
+ int size, u16 ssn);
+void wil_tid_ampdu_rx_free(struct wil6210_priv *wil,
+ struct wil_tid_ampdu_rx *r);
+
#endif /* WIL6210_TXRX_H */
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 9bcfffa4006..e25edc52398 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -21,8 +21,6 @@
#include <linux/wireless.h>
#include <net/cfg80211.h>
-#include "dbg_hexdump.h"
-
#define WIL_NAME "wil6210"
/**
@@ -36,11 +34,15 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1)
#define WIL6210_MEM_SIZE (2*1024*1024UL)
-#define WIL6210_TX_QUEUES (4)
-
-#define WIL6210_RX_RING_SIZE (128)
-#define WIL6210_TX_RING_SIZE (128)
-#define WIL6210_MAX_TX_RINGS (24)
+#define WIL6210_RX_RING_SIZE (128)
+#define WIL6210_TX_RING_SIZE (512)
+#define WIL6210_MAX_TX_RINGS (24) /* HW limit */
+#define WIL6210_MAX_CID (8) /* HW limit */
+#define WIL6210_NAPI_BUDGET (16) /* arbitrary */
+#define WIL6210_ITR_TRSH (10000) /* arbitrary - about 15 IRQs/msec */
+#define WIL6210_FW_RECOVERY_RETRIES (5) /* try to recover this many times */
+#define WIL6210_FW_RECOVERY_TO msecs_to_jiffies(5000)
+#define WIL6210_SCAN_TO msecs_to_jiffies(10000)
/* Hardware definitions begin */
@@ -75,23 +77,21 @@ struct RGF_ICR {
} __packed;
/* registers - FW addresses */
-#define RGF_USER_USER_SCRATCH_PAD (0x8802bc)
-#define RGF_USER_USER_ICR (0x880b4c) /* struct RGF_ICR */
- #define BIT_USER_USER_ICR_SW_INT_2 BIT(18)
-#define RGF_USER_CLKS_CTL_SW_RST_MASK_0 (0x880b14)
-#define RGF_USER_MAC_CPU_0 (0x8801fc)
+#define RGF_USER_HW_MACHINE_STATE (0x8801dc)
+ #define HW_MACHINE_BOOT_DONE (0x3fffffd)
#define RGF_USER_USER_CPU_0 (0x8801e0)
+#define RGF_USER_MAC_CPU_0 (0x8801fc)
+#define RGF_USER_USER_SCRATCH_PAD (0x8802bc)
+#define RGF_USER_FW_REV_ID (0x880a8c) /* chip revision */
+#define RGF_USER_CLKS_CTL_0 (0x880abc)
+ #define BIT_USER_CLKS_RST_PWGD BIT(11) /* reset on "power good" */
#define RGF_USER_CLKS_CTL_SW_RST_VEC_0 (0x880b04)
#define RGF_USER_CLKS_CTL_SW_RST_VEC_1 (0x880b08)
#define RGF_USER_CLKS_CTL_SW_RST_VEC_2 (0x880b0c)
#define RGF_USER_CLKS_CTL_SW_RST_VEC_3 (0x880b10)
-
-#define RGF_DMA_PSEUDO_CAUSE (0x881c68)
-#define RGF_DMA_PSEUDO_CAUSE_MASK_SW (0x881c6c)
-#define RGF_DMA_PSEUDO_CAUSE_MASK_FW (0x881c70)
- #define BIT_DMA_PSEUDO_CAUSE_RX BIT(0)
- #define BIT_DMA_PSEUDO_CAUSE_TX BIT(1)
- #define BIT_DMA_PSEUDO_CAUSE_MISC BIT(2)
+#define RGF_USER_CLKS_CTL_SW_RST_MASK_0 (0x880b14)
+#define RGF_USER_USER_ICR (0x880b4c) /* struct RGF_ICR */
+ #define BIT_USER_USER_ICR_SW_INT_2 BIT(18)
#define RGF_DMA_EP_TX_ICR (0x881bb4) /* struct RGF_ICR */
#define BIT_DMA_EP_TX_ICR_TX_DONE BIT(0)
@@ -101,19 +101,27 @@ struct RGF_ICR {
#define RGF_DMA_EP_MISC_ICR (0x881bec) /* struct RGF_ICR */
#define BIT_DMA_EP_MISC_ICR_RX_HTRSH BIT(0)
#define BIT_DMA_EP_MISC_ICR_TX_NO_ACT BIT(1)
- #define BIT_DMA_EP_MISC_ICR_FW_INT0 BIT(28)
- #define BIT_DMA_EP_MISC_ICR_FW_INT1 BIT(29)
+ #define BIT_DMA_EP_MISC_ICR_FW_INT(n) BIT(28+n) /* n = [0..3] */
/* Interrupt moderation control */
#define RGF_DMA_ITR_CNT_TRSH (0x881c5c)
#define RGF_DMA_ITR_CNT_DATA (0x881c60)
-#define RGF_DMA_ITR_CNT_CRL (0x881C64)
+#define RGF_DMA_ITR_CNT_CRL (0x881c64)
#define BIT_DMA_ITR_CNT_CRL_EN BIT(0)
#define BIT_DMA_ITR_CNT_CRL_EXT_TICK BIT(1)
#define BIT_DMA_ITR_CNT_CRL_FOREVER BIT(2)
#define BIT_DMA_ITR_CNT_CRL_CLR BIT(3)
#define BIT_DMA_ITR_CNT_CRL_REACH_TRSH BIT(4)
+#define RGF_DMA_PSEUDO_CAUSE (0x881c68)
+#define RGF_DMA_PSEUDO_CAUSE_MASK_SW (0x881c6c)
+#define RGF_DMA_PSEUDO_CAUSE_MASK_FW (0x881c70)
+ #define BIT_DMA_PSEUDO_CAUSE_RX BIT(0)
+ #define BIT_DMA_PSEUDO_CAUSE_TX BIT(1)
+ #define BIT_DMA_PSEUDO_CAUSE_MISC BIT(2)
+
+#define RGF_PCIE_LOS_COUNTER_CTL (0x882dc4)
+
/* popular locations */
#define HOST_MBOX HOSTADDR(RGF_USER_USER_SCRATCH_PAD)
#define HOST_SW_INT (HOSTADDR(RGF_USER_USER_ICR) + \
@@ -121,11 +129,37 @@ struct RGF_ICR {
#define SW_INT_MBOX BIT_USER_USER_ICR_SW_INT_2
/* ISR register bits */
-#define ISR_MISC_FW_READY BIT_DMA_EP_MISC_ICR_FW_INT0
-#define ISR_MISC_MBOX_EVT BIT_DMA_EP_MISC_ICR_FW_INT1
+#define ISR_MISC_FW_READY BIT_DMA_EP_MISC_ICR_FW_INT(0)
+#define ISR_MISC_MBOX_EVT BIT_DMA_EP_MISC_ICR_FW_INT(1)
+#define ISR_MISC_FW_ERROR BIT_DMA_EP_MISC_ICR_FW_INT(3)
/* Hardware definitions end */
+/**
+ * mk_cidxtid - construct @cidxtid field
+ * @cid: CID value
+ * @tid: TID value
+ *
+ * @cidxtid field encoded as bits 0..3 - CID; 4..7 - TID
+ */
+static inline u8 mk_cidxtid(u8 cid, u8 tid)
+{
+ return ((tid & 0xf) << 4) | (cid & 0xf);
+}
+
+/**
+ * parse_cidxtid - parse @cidxtid field
+ * @cid: store CID value here
+ * @tid: store TID value here
+ *
+ * @cidxtid field encoded as bits 0..3 - CID; 4..7 - TID
+ */
+static inline void parse_cidxtid(u8 cidxtid, u8 *cid, u8 *tid)
+{
+ *cid = cidxtid & 0xf;
+ *tid = (cidxtid >> 4) & 0xf;
+}
+
struct wil6210_mbox_ring {
u32 base;
u16 entry_size; /* max. size of mbox entry, incl. all headers */
@@ -158,11 +192,22 @@ struct wil6210_mbox_hdr {
/* max. value for wil6210_mbox_hdr.len */
#define MAX_MBOXITEM_SIZE (240)
+/**
+ * struct wil6210_mbox_hdr_wmi - WMI header
+ *
+ * @mid: MAC ID
+ * 00 - default, created by FW
+ * 01..0f - WiFi ports, driver to create
+ * 10..fe - debug
+ * ff - broadcast
+ * @id: command/event ID
+ * @timestamp: FW fills for events, free-running msec timer
+ */
struct wil6210_mbox_hdr_wmi {
- u8 reserved0[2];
+ u8 mid;
+ u8 reserved;
__le16 id;
- __le16 info1; /* bits [0..3] - device_id, rest - unused */
- u8 reserved1[2];
+ __le32 timestamp;
} __packed;
struct pending_wmi_event {
@@ -174,6 +219,21 @@ struct pending_wmi_event {
} __packed event;
};
+enum { /* for wil_ctx.mapped_as */
+ wil_mapped_as_none = 0,
+ wil_mapped_as_single = 1,
+ wil_mapped_as_page = 2,
+};
+
+/**
+ * struct wil_ctx - software context for Vring descriptor
+ */
+struct wil_ctx {
+ struct sk_buff *skb;
+ u8 nr_frags;
+ u8 mapped_as;
+};
+
union vring_desc;
struct vring {
@@ -183,18 +243,70 @@ struct vring {
u32 swtail;
u32 swhead;
u32 hwtail; /* write here to inform hw */
- void **ctx; /* void *ctx[size] - software context */
+ struct wil_ctx *ctx; /* ctx[size] - software context */
+};
+
+/**
+ * Additional data for Tx Vring
+ */
+struct vring_tx_data {
+ int enabled;
+
};
enum { /* for wil6210_priv.status */
wil_status_fwready = 0,
+ wil_status_fwconnecting,
wil_status_fwconnected,
wil_status_dontscan,
+ wil_status_reset_done,
wil_status_irqen, /* FIXME: interrupts enabled - for debug */
+ wil_status_napi_en, /* NAPI enabled protected by wil->mutex */
};
struct pci_dev;
+/**
+ * struct tid_ampdu_rx - TID aggregation information (Rx).
+ *
+ * @reorder_buf: buffer to reorder incoming aggregated MPDUs
+ * @reorder_time: jiffies when skb was added
+ * @session_timer: check if peer keeps Tx-ing on the TID (by timeout value)
+ * @reorder_timer: releases expired frames from the reorder buffer.
+ * @last_rx: jiffies of last rx activity
+ * @head_seq_num: head sequence number in reordering buffer.
+ * @stored_mpdu_num: number of MPDUs in reordering buffer
+ * @ssn: Starting Sequence Number expected to be aggregated.
+ * @buf_size: buffer size for incoming A-MPDUs
+ * @timeout: reset timer value (in TUs).
+ * @dialog_token: dialog token for aggregation session
+ * @rcu_head: RCU head used for freeing this struct
+ * @reorder_lock: serializes access to reorder buffer, see below.
+ *
+ * This structure's lifetime is managed by RCU, assignments to
+ * the array holding it must hold the aggregation mutex.
+ *
+ * The @reorder_lock is used to protect the members of this
+ * struct, except for @timeout, @buf_size and @dialog_token,
+ * which are constant across the lifetime of the struct (the
+ * dialog token being used only for debugging).
+ */
+struct wil_tid_ampdu_rx {
+ spinlock_t reorder_lock; /* see above */
+ struct sk_buff **reorder_buf;
+ unsigned long *reorder_time;
+ struct timer_list session_timer;
+ struct timer_list reorder_timer;
+ unsigned long last_rx;
+ u16 head_seq_num;
+ u16 stored_mpdu_num;
+ u16 ssn;
+ u16 buf_size;
+ u16 timeout;
+ u8 dialog_token;
+ bool first_time; /* is it 1-st time this buffer used? */
+};
+
struct wil6210_stats {
u64 tsf;
u32 snr;
@@ -206,12 +318,54 @@ struct wil6210_stats {
u16 peer_tx_sector;
};
+enum wil_sta_status {
+ wil_sta_unused = 0,
+ wil_sta_conn_pending = 1,
+ wil_sta_connected = 2,
+};
+
+#define WIL_STA_TID_NUM (16)
+
+struct wil_net_stats {
+ unsigned long rx_packets;
+ unsigned long tx_packets;
+ unsigned long rx_bytes;
+ unsigned long tx_bytes;
+ unsigned long tx_errors;
+ unsigned long rx_dropped;
+ u16 last_mcs_rx;
+};
+
+/**
+ * struct wil_sta_info - data for peer
+ *
+ * Peer identified by its CID (connection ID)
+ * NIC performs beam forming for each peer;
+ * if no beam forming done, frame exchange is not
+ * possible.
+ */
+struct wil_sta_info {
+ u8 addr[ETH_ALEN];
+ enum wil_sta_status status;
+ struct wil_net_stats stats;
+ bool data_port_open; /* can send any data, not only EAPOL */
+ /* Rx BACK */
+ struct wil_tid_ampdu_rx *tid_rx[WIL_STA_TID_NUM];
+ unsigned long tid_rx_timer_expired[BITS_TO_LONGS(WIL_STA_TID_NUM)];
+ unsigned long tid_rx_stop_requested[BITS_TO_LONGS(WIL_STA_TID_NUM)];
+};
+
struct wil6210_priv {
struct pci_dev *pdev;
int n_msi;
struct wireless_dev *wdev;
void __iomem *csr;
ulong status;
+ u32 fw_version;
+ u32 hw_version;
+ u8 n_mids; /* number of additional MIDs as reported by FW */
+ int recovery_count; /* num of FW recovery attempts in a short time */
+ unsigned long last_fw_recovery; /* jiffies of last fw recovery */
/* profile */
u32 monitor_flags;
u32 secure_pcp; /* create secure PCP? */
@@ -229,9 +383,11 @@ struct wil6210_priv {
struct workqueue_struct *wmi_wq; /* for deferred calls */
struct work_struct wmi_event_worker;
struct workqueue_struct *wmi_wq_conn; /* for connect worker */
- struct work_struct wmi_connect_worker;
+ struct work_struct connect_worker;
struct work_struct disconnect_worker;
+ struct work_struct fw_error_worker; /* for FW error recovery */
struct timer_list connect_timer;
+ struct timer_list scan_timer; /* detect scan timeout */
int pending_connect_cid;
struct list_head pending_wmi_ev;
/*
@@ -240,10 +396,14 @@ struct wil6210_priv {
* - consumed in thread by wmi_event_worker
*/
spinlock_t wmi_ev_lock;
+ struct napi_struct napi_rx;
+ struct napi_struct napi_tx;
/* DMA related */
struct vring vring_rx;
struct vring vring_tx[WIL6210_MAX_TX_RINGS];
- u8 dst_addr[WIL6210_MAX_TX_RINGS][ETH_ALEN];
+ struct vring_tx_data vring_tx_data[WIL6210_MAX_TX_RINGS];
+ u8 vring2cid_tid[WIL6210_MAX_TX_RINGS][2]; /* [0] - CID, [1] - TID */
+ struct wil_sta_info sta[WIL6210_MAX_CID];
/* scan */
struct cfg80211_scan_request *scan_request;
@@ -268,23 +428,28 @@ struct wil6210_priv {
#define wil_to_ndev(i) (wil_to_wdev(i)->netdev)
#define ndev_to_wil(n) (wdev_to_wil(n->ieee80211_ptr))
-#define wil_dbg(wil, fmt, arg...) netdev_dbg(wil_to_ndev(wil), fmt, ##arg)
-#define wil_info(wil, fmt, arg...) netdev_info(wil_to_ndev(wil), fmt, ##arg)
-#define wil_err(wil, fmt, arg...) netdev_err(wil_to_ndev(wil), fmt, ##arg)
+int wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...);
+int wil_err(struct wil6210_priv *wil, const char *fmt, ...);
+int wil_info(struct wil6210_priv *wil, const char *fmt, ...);
+#define wil_dbg(wil, fmt, arg...) do { \
+ netdev_dbg(wil_to_ndev(wil), fmt, ##arg); \
+ wil_dbg_trace(wil, fmt, ##arg); \
+} while (0)
-#define wil_dbg_IRQ(wil, fmt, arg...) wil_dbg(wil, "DBG[ IRQ]" fmt, ##arg)
-#define wil_dbg_TXRX(wil, fmt, arg...) wil_dbg(wil, "DBG[TXRX]" fmt, ##arg)
-#define wil_dbg_WMI(wil, fmt, arg...) wil_dbg(wil, "DBG[ WMI]" fmt, ##arg)
+#define wil_dbg_irq(wil, fmt, arg...) wil_dbg(wil, "DBG[ IRQ]" fmt, ##arg)
+#define wil_dbg_txrx(wil, fmt, arg...) wil_dbg(wil, "DBG[TXRX]" fmt, ##arg)
+#define wil_dbg_wmi(wil, fmt, arg...) wil_dbg(wil, "DBG[ WMI]" fmt, ##arg)
+#define wil_dbg_misc(wil, fmt, arg...) wil_dbg(wil, "DBG[MISC]" fmt, ##arg)
-#define wil_hex_dump_TXRX(prefix_str, prefix_type, rowsize, \
+#define wil_hex_dump_txrx(prefix_str, prefix_type, rowsize, \
groupsize, buf, len, ascii) \
- wil_print_hex_dump_debug("DBG[TXRX]" prefix_str,\
+ print_hex_dump_debug("DBG[TXRX]" prefix_str,\
prefix_type, rowsize, \
groupsize, buf, len, ascii)
-#define wil_hex_dump_WMI(prefix_str, prefix_type, rowsize, \
+#define wil_hex_dump_wmi(prefix_str, prefix_type, rowsize, \
groupsize, buf, len, ascii) \
- wil_print_hex_dump_debug("DBG[ WMI]" prefix_str,\
+ print_hex_dump_debug("DBG[ WMI]" prefix_str,\
prefix_type, rowsize, \
groupsize, buf, len, ascii)
@@ -300,11 +465,13 @@ void wil_if_remove(struct wil6210_priv *wil);
int wil_priv_init(struct wil6210_priv *wil);
void wil_priv_deinit(struct wil6210_priv *wil);
int wil_reset(struct wil6210_priv *wil);
+void wil_fw_error_recovery(struct wil6210_priv *wil);
void wil_link_on(struct wil6210_priv *wil);
void wil_link_off(struct wil6210_priv *wil);
int wil_up(struct wil6210_priv *wil);
int wil_down(struct wil6210_priv *wil);
void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r);
+int wil_find_cid(struct wil6210_priv *wil, const u8 *mac);
void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr);
void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr);
@@ -314,21 +481,25 @@ int wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len);
void wmi_recv_cmd(struct wil6210_priv *wil);
int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len,
u16 reply_id, void *reply, u8 reply_size, int to_msec);
-void wmi_connect_worker(struct work_struct *work);
void wmi_event_worker(struct work_struct *work);
void wmi_event_flush(struct wil6210_priv *wil);
int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid);
int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid);
int wmi_set_channel(struct wil6210_priv *wil, int channel);
int wmi_get_channel(struct wil6210_priv *wil, int *channel);
-int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb);
int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index,
const void *mac_addr);
int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index,
const void *mac_addr, int key_len, const void *key);
int wmi_echo(struct wil6210_priv *wil);
int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie);
+int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring);
+int wmi_p2p_cfg(struct wil6210_priv *wil, int channel);
+int wmi_rxon(struct wil6210_priv *wil, bool on);
+int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_m, u32 *t_r);
+int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, u16 reason);
+void wil6210_clear_irq(struct wil6210_priv *wil);
int wil6210_init_irq(struct wil6210_priv *wil, int irq);
void wil6210_fini_irq(struct wil6210_priv *wil, int irq);
void wil6210_disable_irq(struct wil6210_priv *wil);
@@ -341,8 +512,9 @@ struct wireless_dev *wil_cfg80211_init(struct device *dev);
void wil_wdev_free(struct wil6210_priv *wil);
int wmi_set_mac_address(struct wil6210_priv *wil, void *addr);
-int wmi_set_bcon(struct wil6210_priv *wil, int bi, u8 wmi_nettype);
-void wil6210_disconnect(struct wil6210_priv *wil, void *bssid);
+int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan);
+int wmi_pcp_stop(struct wil6210_priv *wil);
+void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid);
int wil_rx_init(struct wil6210_priv *wil);
void wil_rx_fini(struct wil6210_priv *wil);
@@ -353,10 +525,12 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
void wil_vring_fini_tx(struct wil6210_priv *wil, int id);
netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev);
-void wil_tx_complete(struct wil6210_priv *wil, int ringid);
+int wil_tx_complete(struct wil6210_priv *wil, int ringid);
+void wil6210_unmask_irq_tx(struct wil6210_priv *wil);
/* RX API */
-void wil_rx_handle(struct wil6210_priv *wil);
+void wil_rx_handle(struct wil6210_priv *wil, int *quota);
+void wil6210_unmask_irq_rx(struct wil6210_priv *wil);
int wil_iftype_nl2wmi(enum nl80211_iftype type);
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 12915f6e761..6cc0e182cc7 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -14,13 +14,13 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include <linux/pci.h>
-#include <linux/io.h>
-#include <linux/list.h>
#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
#include "wil6210.h"
+#include "txrx.h"
#include "wmi.h"
+#include "trace.h"
/**
* WMI event receiving - theory of operations
@@ -75,10 +75,11 @@ static const struct {
{0x800000, 0x808000, 0x900000}, /* FW data RAM 32k */
{0x840000, 0x860000, 0x908000}, /* peripheral data RAM 128k/96k used */
{0x880000, 0x88a000, 0x880000}, /* various RGF */
- {0x8c0000, 0x932000, 0x8c0000}, /* trivial mapping for upper area */
+ {0x8c0000, 0x949000, 0x8c0000}, /* trivial mapping for upper area */
/*
* 920000..930000 ucode code RAM
* 930000..932000 ucode data RAM
+ * 932000..949000 back-door debug data
*/
};
@@ -171,8 +172,8 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
.len = cpu_to_le16(sizeof(cmd.wmi) + len),
},
.wmi = {
+ .mid = 0,
.id = cpu_to_le16(cmdid),
- .info1 = 0,
},
};
struct wil6210_mbox_ring *r = &wil->mbox_ctl.tx;
@@ -186,13 +187,12 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
wil_err(wil, "WMI size too large: %d bytes, max is %d\n",
(int)(sizeof(cmd) + len), r->entry_size);
return -ERANGE;
-
}
might_sleep();
if (!test_bit(wil_status_fwready, &wil->status)) {
- wil_err(wil, "FW not ready\n");
+ wil_err(wil, "WMI: cannot send command while FW not ready\n");
return -EAGAIN;
}
@@ -213,7 +213,7 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
}
/* next head */
next_head = r->base + ((r->head - r->base + sizeof(d_head)) % r->size);
- wil_dbg_WMI(wil, "Head 0x%08x -> 0x%08x\n", r->head, next_head);
+ wil_dbg_wmi(wil, "Head 0x%08x -> 0x%08x\n", r->head, next_head);
/* wait till FW finish with previous command */
for (retry = 5; retry > 0; retry--) {
r->tail = ioread32(wil->csr + HOST_MBOX +
@@ -234,10 +234,10 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
}
cmd.hdr.seq = cpu_to_le16(++wil->wmi_seq);
/* set command */
- wil_dbg_WMI(wil, "WMI command 0x%04x [%d]\n", cmdid, len);
- wil_hex_dump_WMI("Cmd ", DUMP_PREFIX_OFFSET, 16, 1, &cmd,
+ wil_dbg_wmi(wil, "WMI command 0x%04x [%d]\n", cmdid, len);
+ wil_hex_dump_wmi("Cmd ", DUMP_PREFIX_OFFSET, 16, 1, &cmd,
sizeof(cmd), true);
- wil_hex_dump_WMI("cmd ", DUMP_PREFIX_OFFSET, 16, 1, buf,
+ wil_hex_dump_wmi("cmd ", DUMP_PREFIX_OFFSET, 16, 1, buf,
len, true);
wil_memcpy_toio_32(dst, &cmd, sizeof(cmd));
wil_memcpy_toio_32(dst + sizeof(cmd), buf, len);
@@ -248,6 +248,8 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
iowrite32(r->head = next_head, wil->csr + HOST_MBOX +
offsetof(struct wil6210_mbox_ctl, tx.head));
+ trace_wil6210_wmi_cmd(&cmd.wmi, buf, len);
+
/* interrupt to FW */
iowrite32(SW_INT_MBOX, wil->csr + HOST_SW_INT);
@@ -271,22 +273,24 @@ static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len)
struct net_device *ndev = wil_to_ndev(wil);
struct wireless_dev *wdev = wil->wdev;
struct wmi_ready_event *evt = d;
- u32 ver = le32_to_cpu(evt->sw_version);
+ wil->fw_version = le32_to_cpu(evt->sw_version);
+ wil->n_mids = evt->numof_additional_mids;
- wil_dbg_WMI(wil, "FW ver. %d; MAC %pM\n", ver, evt->mac);
+ wil_info(wil, "FW ver. %d; MAC %pM; %d MID's\n", wil->fw_version,
+ evt->mac, wil->n_mids);
if (!is_valid_ether_addr(ndev->dev_addr)) {
memcpy(ndev->dev_addr, evt->mac, ETH_ALEN);
memcpy(ndev->perm_addr, evt->mac, ETH_ALEN);
}
snprintf(wdev->wiphy->fw_version, sizeof(wdev->wiphy->fw_version),
- "%d", ver);
+ "%d", wil->fw_version);
}
static void wmi_evt_fw_ready(struct wil6210_priv *wil, int id, void *d,
int len)
{
- wil_dbg_WMI(wil, "WMI: FW ready\n");
+ wil_dbg_wmi(wil, "WMI: got FW ready event\n");
set_bit(wil_status_fwready, &wil->status);
/* reuse wmi_ready for the firmware ready indication */
@@ -303,17 +307,17 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
u32 freq = ieee80211_channel_to_frequency(ch_no,
IEEE80211_BAND_60GHZ);
struct ieee80211_channel *channel = ieee80211_get_channel(wiphy, freq);
- /* TODO convert LE to CPU */
- s32 signal = 0; /* TODO */
+ s32 signal = data->info.sqi;
__le16 fc = rx_mgmt_frame->frame_control;
u32 d_len = le32_to_cpu(data->info.len);
u16 d_status = le16_to_cpu(data->info.status);
- wil_dbg_WMI(wil, "MGMT: channel %d MCS %d SNR %d\n",
- data->info.channel, data->info.mcs, data->info.snr);
- wil_dbg_WMI(wil, "status 0x%04x len %d stype %04x\n", d_status, d_len,
- le16_to_cpu(data->info.stype));
- wil_dbg_WMI(wil, "qid %d mid %d cid %d\n",
+ wil_dbg_wmi(wil, "MGMT: channel %d MCS %d SNR %d SQI %d%%\n",
+ data->info.channel, data->info.mcs, data->info.snr,
+ data->info.sqi);
+ wil_dbg_wmi(wil, "status 0x%04x len %d fc 0x%04x\n", d_status, 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);
if (!channel) {
@@ -323,24 +327,19 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
if (ieee80211_is_beacon(fc) || ieee80211_is_probe_resp(fc)) {
struct cfg80211_bss *bss;
- u64 tsf = le64_to_cpu(rx_mgmt_frame->u.beacon.timestamp);
- u16 cap = le16_to_cpu(rx_mgmt_frame->u.beacon.capab_info);
- u16 bi = le16_to_cpu(rx_mgmt_frame->u.beacon.beacon_int);
- const u8 *ie_buf = rx_mgmt_frame->u.beacon.variable;
- size_t ie_len = d_len - offsetof(struct ieee80211_mgmt,
- u.beacon.variable);
- wil_dbg_WMI(wil, "Capability info : 0x%04x\n", cap);
-
- bss = cfg80211_inform_bss(wiphy, channel, rx_mgmt_frame->bssid,
- tsf, cap, bi, ie_buf, ie_len,
- signal, GFP_KERNEL);
+
+ bss = cfg80211_inform_bss_frame(wiphy, channel, rx_mgmt_frame,
+ d_len, signal, GFP_KERNEL);
if (bss) {
- wil_dbg_WMI(wil, "Added BSS %pM\n",
+ wil_dbg_wmi(wil, "Added BSS %pM\n",
rx_mgmt_frame->bssid);
- cfg80211_put_bss(bss);
+ cfg80211_put_bss(wiphy, bss);
} else {
wil_err(wil, "cfg80211_inform_bss() failed\n");
}
+ } else {
+ cfg80211_rx_mgmt(wil->wdev, freq, signal,
+ (void *)rx_mgmt_frame, d_len, 0, GFP_KERNEL);
}
}
@@ -349,9 +348,10 @@ static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id,
{
if (wil->scan_request) {
struct wmi_scan_complete_event *data = d;
- bool aborted = (data->status != 0);
+ bool aborted = (data->status != WMI_SCAN_SUCCESS);
- wil_dbg_WMI(wil, "SCAN_COMPLETE(0x%08x)\n", data->status);
+ wil_dbg_wmi(wil, "SCAN_COMPLETE(0x%08x)\n", data->status);
+ del_timer_sync(&wil->scan_timer);
cfg80211_scan_done(wil->scan_request, aborted);
wil->scan_request = NULL;
} else {
@@ -385,10 +385,15 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
evt->assoc_req_len, evt->assoc_resp_len);
return;
}
+ if (evt->cid >= WIL6210_MAX_CID) {
+ wil_err(wil, "Connect CID invalid : %d\n", evt->cid);
+ return;
+ }
+
ch = evt->channel + 1;
- wil_dbg_WMI(wil, "Connect %pM channel [%d] cid %d\n",
+ wil_dbg_wmi(wil, "Connect %pM channel [%d] cid %d\n",
evt->bssid, ch, evt->cid);
- wil_hex_dump_WMI("connect AI : ", DUMP_PREFIX_OFFSET, 16, 1,
+ wil_hex_dump_wmi("connect AI : ", DUMP_PREFIX_OFFSET, 16, 1,
evt->assoc_info, len - sizeof(*evt), true);
/* figure out IE's */
@@ -411,7 +416,7 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
if ((wdev->iftype == NL80211_IFTYPE_STATION) ||
(wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) {
- if (wdev->sme_state != CFG80211_SME_CONNECTING) {
+ if (!test_bit(wil_status_fwconnecting, &wil->status)) {
wil_err(wil, "Not in connecting state\n");
return;
}
@@ -435,14 +440,16 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
cfg80211_new_sta(ndev, evt->bssid, &sinfo, GFP_KERNEL);
}
+ clear_bit(wil_status_fwconnecting, &wil->status);
set_bit(wil_status_fwconnected, &wil->status);
/* FIXME FW can transmit only ucast frames to peer */
/* FIXME real ring_id instead of hard coded 0 */
- memcpy(wil->dst_addr[0], evt->bssid, ETH_ALEN);
+ memcpy(wil->sta[evt->cid].addr, evt->bssid, ETH_ALEN);
+ wil->sta[evt->cid].status = wil_sta_conn_pending;
wil->pending_connect_cid = evt->cid;
- queue_work(wil->wmi_wq_conn, &wil->wmi_connect_worker);
+ queue_work(wil->wmi_wq_conn, &wil->connect_worker);
}
static void wmi_evt_disconnect(struct wil6210_priv *wil, int id,
@@ -450,14 +457,15 @@ static void wmi_evt_disconnect(struct wil6210_priv *wil, int id,
{
struct wmi_disconnect_event *evt = d;
- wil_dbg_WMI(wil, "Disconnect %pM reason %d proto %d wmi\n",
+ wil_dbg_wmi(wil, "Disconnect %pM reason %d proto %d wmi\n",
evt->bssid,
evt->protocol_reason_status, evt->disconnect_reason);
wil->sinfo_gen++;
+ mutex_lock(&wil->mutex);
wil6210_disconnect(wil, evt->bssid);
- clear_bit(wil_status_dontscan, &wil->status);
+ mutex_unlock(&wil->mutex);
}
static void wmi_evt_notify(struct wil6210_priv *wil, int id, void *d, int len)
@@ -476,12 +484,12 @@ static void wmi_evt_notify(struct wil6210_priv *wil, int id, void *d, int len)
wil->stats.my_tx_sector = le16_to_cpu(evt->my_tx_sector);
wil->stats.peer_rx_sector = le16_to_cpu(evt->other_rx_sector);
wil->stats.peer_tx_sector = le16_to_cpu(evt->other_tx_sector);
- wil_dbg_WMI(wil, "Link status, MCS %d TSF 0x%016llx\n"
- "BF status 0x%08x SNR 0x%08x\n"
+ wil_dbg_wmi(wil, "Link status, MCS %d TSF 0x%016llx\n"
+ "BF status 0x%08x SNR 0x%08x SQI %d%%\n"
"Tx Tpt %d goodput %d Rx goodput %d\n"
"Sectors(rx:tx) my %d:%d peer %d:%d\n",
wil->stats.bf_mcs, wil->stats.tsf, evt->status,
- wil->stats.snr, le32_to_cpu(evt->tx_tpt),
+ wil->stats.snr, evt->sqi, le32_to_cpu(evt->tx_tpt),
le32_to_cpu(evt->tx_goodput), le32_to_cpu(evt->rx_goodput),
wil->stats.my_rx_sector, wil->stats.my_tx_sector,
wil->stats.peer_rx_sector, wil->stats.peer_tx_sector);
@@ -500,10 +508,16 @@ static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id,
int sz = eapol_len + ETH_HLEN;
struct sk_buff *skb;
struct ethhdr *eth;
+ int cid;
+ struct wil_net_stats *stats = NULL;
- wil_dbg_WMI(wil, "EAPOL len %d from %pM\n", eapol_len,
+ wil_dbg_wmi(wil, "EAPOL len %d from %pM\n", eapol_len,
evt->src_mac);
+ cid = wil_find_cid(wil, evt->src_mac);
+ if (cid >= 0)
+ stats = &wil->sta[cid].stats;
+
if (eapol_len > 196) { /* TODO: revisit size limit */
wil_err(wil, "EAPOL too large\n");
return;
@@ -514,6 +528,7 @@ static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id,
wil_err(wil, "Failed to allocate skb\n");
return;
}
+
eth = (struct ethhdr *)skb_put(skb, ETH_HLEN);
memcpy(eth->h_dest, ndev->dev_addr, ETH_ALEN);
memcpy(eth->h_source, evt->src_mac, ETH_ALEN);
@@ -522,9 +537,92 @@ static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id,
skb->protocol = eth_type_trans(skb, ndev);
if (likely(netif_rx_ni(skb) == NET_RX_SUCCESS)) {
ndev->stats.rx_packets++;
- ndev->stats.rx_bytes += skb->len;
+ ndev->stats.rx_bytes += sz;
+ if (stats) {
+ stats->rx_packets++;
+ stats->rx_bytes += sz;
+ }
} else {
ndev->stats.rx_dropped++;
+ if (stats)
+ stats->rx_dropped++;
+ }
+}
+
+static void wmi_evt_linkup(struct wil6210_priv *wil, int id, void *d, int len)
+{
+ struct net_device *ndev = wil_to_ndev(wil);
+ struct wmi_data_port_open_event *evt = d;
+ u8 cid = evt->cid;
+
+ wil_dbg_wmi(wil, "Link UP for CID %d\n", cid);
+
+ if (cid >= ARRAY_SIZE(wil->sta)) {
+ wil_err(wil, "Link UP for invalid CID %d\n", cid);
+ return;
+ }
+
+ wil->sta[cid].data_port_open = true;
+ netif_carrier_on(ndev);
+}
+
+static void wmi_evt_linkdown(struct wil6210_priv *wil, int id, void *d, int len)
+{
+ struct net_device *ndev = wil_to_ndev(wil);
+ struct wmi_wbe_link_down_event *evt = d;
+ u8 cid = evt->cid;
+
+ wil_dbg_wmi(wil, "Link DOWN for CID %d, reason %d\n",
+ cid, le32_to_cpu(evt->reason));
+
+ if (cid >= ARRAY_SIZE(wil->sta)) {
+ wil_err(wil, "Link DOWN for invalid CID %d\n", cid);
+ return;
+ }
+
+ wil->sta[cid].data_port_open = false;
+ netif_carrier_off(ndev);
+}
+
+static void wmi_evt_ba_status(struct wil6210_priv *wil, int id, void *d,
+ int len)
+{
+ struct wmi_vring_ba_status_event *evt = d;
+ struct wil_sta_info *sta;
+ uint i, cid;
+
+ /* TODO: use Rx BA status, not Tx one */
+
+ wil_dbg_wmi(wil, "BACK[%d] %s {%d} timeout %d\n",
+ evt->ringid,
+ evt->status == WMI_BA_AGREED ? "OK" : "N/A",
+ evt->agg_wsize, __le16_to_cpu(evt->ba_timeout));
+
+ if (evt->ringid >= WIL6210_MAX_TX_RINGS) {
+ wil_err(wil, "invalid ring id %d\n", evt->ringid);
+ return;
+ }
+
+ cid = wil->vring2cid_tid[evt->ringid][0];
+ if (cid >= WIL6210_MAX_CID) {
+ wil_err(wil, "invalid CID %d for vring %d\n", cid, evt->ringid);
+ return;
+ }
+
+ sta = &wil->sta[cid];
+ if (sta->status == wil_sta_unused) {
+ wil_err(wil, "CID %d unused\n", cid);
+ return;
+ }
+
+ wil_dbg_wmi(wil, "BACK for CID %d %pM\n", cid, sta->addr);
+ for (i = 0; i < WIL_STA_TID_NUM; i++) {
+ struct wil_tid_ampdu_rx *r = sta->tid_rx[i];
+ sta->tid_rx[i] = NULL;
+ wil_tid_ampdu_rx_free(wil, r);
+ if ((evt->status == WMI_BA_AGREED) && evt->agg_wsize)
+ sta->tid_rx[i] = wil_tid_ampdu_rx_alloc(wil,
+ evt->agg_wsize, 0);
}
}
@@ -541,6 +639,9 @@ static const struct {
{WMI_DISCONNECT_EVENTID, wmi_evt_disconnect},
{WMI_NOTIFY_REQ_DONE_EVENTID, wmi_evt_notify},
{WMI_EAPOL_RX_EVENTID, wmi_evt_eapol_rx},
+ {WMI_DATA_PORT_OPEN_EVENTID, wmi_evt_linkup},
+ {WMI_WBE_LINKDOWN_EVENTID, wmi_evt_linkdown},
+ {WMI_BA_STATUS_EVENTID, wmi_evt_ba_status},
};
/*
@@ -558,16 +659,27 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
u8 *cmd;
void __iomem *src;
ulong flags;
+ unsigned n;
- for (;;) {
+ if (!test_bit(wil_status_reset_done, &wil->status)) {
+ wil_err(wil, "Reset not completed\n");
+ return;
+ }
+
+ for (n = 0;; n++) {
u16 len;
r->head = ioread32(wil->csr + HOST_MBOX +
offsetof(struct wil6210_mbox_ctl, rx.head));
- if (r->tail == r->head)
+ if (r->tail == r->head) {
+ if (n == 0)
+ wil_dbg_wmi(wil, "No events?\n");
return;
+ }
- /* read cmd from tail */
+ wil_dbg_wmi(wil, "Mbox head %08x tail %08x\n",
+ r->head, r->tail);
+ /* read cmd descriptor from tail */
wil_memcpy_fromio_32(&d_tail, wil->csr + HOSTADDR(r->tail),
sizeof(struct wil6210_mbox_ring_desc));
if (d_tail.sync == 0) {
@@ -575,23 +687,26 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
return;
}
+ /* read cmd header from descriptor */
if (0 != wmi_read_hdr(wil, d_tail.addr, &hdr)) {
wil_err(wil, "Mbox evt at 0x%08x?\n",
le32_to_cpu(d_tail.addr));
return;
}
-
len = le16_to_cpu(hdr.len);
+ wil_dbg_wmi(wil, "Mbox evt %04x %04x %04x %02x\n",
+ le16_to_cpu(hdr.seq), len, le16_to_cpu(hdr.type),
+ hdr.flags);
+
+ /* read cmd buffer from descriptor */
src = wmi_buffer(wil, d_tail.addr) +
sizeof(struct wil6210_mbox_hdr);
evt = kmalloc(ALIGN(offsetof(struct pending_wmi_event,
event.wmi) + len, 4),
GFP_KERNEL);
- if (!evt) {
- wil_err(wil, "kmalloc for WMI event (%d) failed\n",
- len);
+ if (!evt)
return;
- }
+
evt->event.hdr = hdr;
cmd = (void *)&evt->event.wmi;
wil_memcpy_fromio_32(cmd, src, len);
@@ -599,15 +714,17 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
iowrite32(0, wil->csr + HOSTADDR(r->tail) +
offsetof(struct wil6210_mbox_ring_desc, sync));
/* indicate */
- wil_dbg_WMI(wil, "Mbox evt %04x %04x %04x %02x\n",
- le16_to_cpu(hdr.seq), len, le16_to_cpu(hdr.type),
- hdr.flags);
if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) &&
(len >= sizeof(struct wil6210_mbox_hdr_wmi))) {
- wil_dbg_WMI(wil, "WMI event 0x%04x\n",
- evt->event.wmi.id);
+ struct wil6210_mbox_hdr_wmi *wmi = &evt->event.wmi;
+ u16 id = le16_to_cpu(wmi->id);
+ u32 tstamp = le32_to_cpu(wmi->timestamp);
+ wil_dbg_wmi(wil, "WMI event 0x%04x MID %d @%d msec\n",
+ id, wmi->mid, tstamp);
+ trace_wil6210_wmi_event(wmi, &wmi[1],
+ len - sizeof(*wmi));
}
- wil_hex_dump_WMI("evt ", DUMP_PREFIX_OFFSET, 16, 1,
+ wil_hex_dump_wmi("evt ", DUMP_PREFIX_OFFSET, 16, 1,
&evt->event.hdr, sizeof(hdr) + len, true);
/* advance tail */
@@ -623,9 +740,11 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
{
int q = queue_work(wil->wmi_wq,
&wil->wmi_event_worker);
- wil_dbg_WMI(wil, "queue_work -> %d\n", q);
+ wil_dbg_wmi(wil, "queue_work -> %d\n", q);
}
}
+ if (n > 1)
+ wil_dbg_wmi(wil, "%s -> %d events processed\n", __func__, n);
}
int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len,
@@ -650,7 +769,7 @@ int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len,
cmdid, reply_id, to_msec);
rc = -ETIME;
} else {
- wil_dbg_WMI(wil,
+ wil_dbg_wmi(wil,
"wmi_call(0x%04x->0x%04x) completed in %d msec\n",
cmdid, reply_id,
to_msec - jiffies_to_msecs(remain));
@@ -680,23 +799,49 @@ int wmi_set_mac_address(struct wil6210_priv *wil, void *addr)
memcpy(cmd.mac, addr, ETH_ALEN);
- wil_dbg_WMI(wil, "Set MAC %pM\n", addr);
+ wil_dbg_wmi(wil, "Set MAC %pM\n", addr);
return wmi_send(wil, WMI_SET_MAC_ADDRESS_CMDID, &cmd, sizeof(cmd));
}
-int wmi_set_bcon(struct wil6210_priv *wil, int bi, u8 wmi_nettype)
+int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan)
{
- struct wmi_bcon_ctrl_cmd cmd = {
+ int rc;
+
+ struct wmi_pcp_start_cmd cmd = {
.bcon_interval = cpu_to_le16(bi),
.network_type = wmi_nettype,
.disable_sec_offload = 1,
+ .channel = chan - 1,
+ .pcp_max_assoc_sta = WIL6210_MAX_CID,
};
+ struct {
+ struct wil6210_mbox_hdr_wmi wmi;
+ struct wmi_pcp_started_event evt;
+ } __packed reply;
if (!wil->secure_pcp)
cmd.disable_sec = 1;
- return wmi_send(wil, WMI_BCON_CTRL_CMDID, &cmd, sizeof(cmd));
+ /*
+ * Processing time may be huge, in case of secure AP it takes about
+ * 3500ms for FW to start AP
+ */
+ rc = wmi_call(wil, WMI_PCP_START_CMDID, &cmd, sizeof(cmd),
+ WMI_PCP_STARTED_EVENTID, &reply, sizeof(reply), 5000);
+ if (rc)
+ return rc;
+
+ if (reply.evt.status != WMI_FW_STATUS_SUCCESS)
+ rc = -EINVAL;
+
+ return rc;
+}
+
+int wmi_pcp_stop(struct wil6210_priv *wil)
+{
+ return wmi_call(wil, WMI_PCP_STOP_CMDID, NULL, 0,
+ WMI_PCP_STOPPED_EVENTID, NULL, 0, 20);
}
int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid)
@@ -767,38 +912,14 @@ int wmi_get_channel(struct wil6210_priv *wil, int *channel)
return 0;
}
-int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb)
+int wmi_p2p_cfg(struct wil6210_priv *wil, int channel)
{
- struct wmi_eapol_tx_cmd *cmd;
- struct ethhdr *eth;
- u16 eapol_len = skb->len - ETH_HLEN;
- void *eapol = skb->data + ETH_HLEN;
- uint i;
- int rc;
-
- skb_set_mac_header(skb, 0);
- eth = eth_hdr(skb);
- wil_dbg_WMI(wil, "EAPOL %d bytes to %pM\n", eapol_len, eth->h_dest);
- for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) {
- if (memcmp(wil->dst_addr[i], eth->h_dest, ETH_ALEN) == 0)
- goto found_dest;
- }
-
- return -EINVAL;
-
- found_dest:
- /* find out eapol data & len */
- cmd = kzalloc(sizeof(*cmd) + eapol_len, GFP_KERNEL);
- if (!cmd)
- return -EINVAL;
-
- memcpy(cmd->dst_mac, eth->h_dest, ETH_ALEN);
- cmd->eapol_len = cpu_to_le16(eapol_len);
- memcpy(cmd->eapol, eapol, eapol_len);
- rc = wmi_send(wil, WMI_EAPOL_TX_CMDID, cmd, sizeof(*cmd) + eapol_len);
- kfree(cmd);
+ struct wmi_p2p_cfg_cmd cmd = {
+ .discovery_mode = WMI_DISCOVERY_MODE_NON_OFFLOAD,
+ .channel = channel - 1,
+ };
- return rc;
+ return wmi_send(wil, WMI_P2P_CFG_CMDID, &cmd, sizeof(cmd));
}
int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index,
@@ -838,26 +959,149 @@ int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie)
int rc;
u16 len = sizeof(struct wmi_set_appie_cmd) + ie_len;
struct wmi_set_appie_cmd *cmd = kzalloc(len, GFP_KERNEL);
- if (!cmd) {
- wil_err(wil, "kmalloc(%d) failed\n", len);
+ if (!cmd)
return -ENOMEM;
- }
cmd->mgmt_frm_type = type;
/* BUG: FW API define ieLen as u8. Will fix FW */
cmd->ie_len = cpu_to_le16(ie_len);
memcpy(cmd->ie_info, ie, ie_len);
- rc = wmi_send(wil, WMI_SET_APPIE_CMDID, &cmd, len);
+ rc = wmi_send(wil, WMI_SET_APPIE_CMDID, cmd, len);
kfree(cmd);
return rc;
}
+/**
+ * wmi_rxon - turn radio on/off
+ * @on: turn on if true, off otherwise
+ *
+ * Only switch radio. Channel should be set separately.
+ * No timeout for rxon - radio turned on forever unless some other call
+ * turns it off
+ */
+int wmi_rxon(struct wil6210_priv *wil, bool on)
+{
+ int rc;
+ struct {
+ struct wil6210_mbox_hdr_wmi wmi;
+ struct wmi_listen_started_event evt;
+ } __packed reply;
+
+ wil_info(wil, "%s(%s)\n", __func__, on ? "on" : "off");
+
+ if (on) {
+ rc = wmi_call(wil, WMI_START_LISTEN_CMDID, NULL, 0,
+ WMI_LISTEN_STARTED_EVENTID,
+ &reply, sizeof(reply), 100);
+ if ((rc == 0) && (reply.evt.status != WMI_FW_STATUS_SUCCESS))
+ rc = -EINVAL;
+ } else {
+ rc = wmi_call(wil, WMI_DISCOVERY_STOP_CMDID, NULL, 0,
+ WMI_DISCOVERY_STOPPED_EVENTID, NULL, 0, 20);
+ }
+
+ return rc;
+}
+
+int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring)
+{
+ struct wireless_dev *wdev = wil->wdev;
+ struct net_device *ndev = wil_to_ndev(wil);
+ struct wmi_cfg_rx_chain_cmd cmd = {
+ .action = WMI_RX_CHAIN_ADD,
+ .rx_sw_ring = {
+ .max_mpdu_size = cpu_to_le16(RX_BUF_LEN),
+ .ring_mem_base = cpu_to_le64(vring->pa),
+ .ring_size = cpu_to_le16(vring->size),
+ },
+ .mid = 0, /* TODO - what is it? */
+ .decap_trans_type = WMI_DECAP_TYPE_802_3,
+ .reorder_type = WMI_RX_SW_REORDER,
+ };
+ struct {
+ struct wil6210_mbox_hdr_wmi wmi;
+ struct wmi_cfg_rx_chain_done_event evt;
+ } __packed evt;
+ int rc;
+
+ if (wdev->iftype == NL80211_IFTYPE_MONITOR) {
+ struct ieee80211_channel *ch = wdev->preset_chandef.chan;
+
+ cmd.sniffer_cfg.mode = cpu_to_le32(WMI_SNIFFER_ON);
+ if (ch)
+ cmd.sniffer_cfg.channel = ch->hw_value - 1;
+ cmd.sniffer_cfg.phy_info_mode =
+ cpu_to_le32(ndev->type == ARPHRD_IEEE80211_RADIOTAP);
+ cmd.sniffer_cfg.phy_support =
+ cpu_to_le32((wil->monitor_flags & MONITOR_FLAG_CONTROL)
+ ? WMI_SNIFFER_CP : WMI_SNIFFER_DP);
+ } else {
+ /* Initialize offload (in non-sniffer mode).
+ * Linux IP stack always calculates IP checksum
+ * HW always calculate TCP/UDP checksum
+ */
+ cmd.l3_l4_ctrl |= (1 << L3_L4_CTRL_TCPIP_CHECKSUM_EN_POS);
+ }
+ /* typical time for secure PCP is 840ms */
+ rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd),
+ WMI_CFG_RX_CHAIN_DONE_EVENTID, &evt, sizeof(evt), 2000);
+ if (rc)
+ return rc;
+
+ vring->hwtail = le32_to_cpu(evt.evt.rx_ring_tail_ptr);
+
+ wil_dbg_misc(wil, "Rx init: status %d tail 0x%08x\n",
+ le32_to_cpu(evt.evt.status), vring->hwtail);
+
+ if (le32_to_cpu(evt.evt.status) != WMI_CFG_RX_CHAIN_SUCCESS)
+ rc = -EINVAL;
+
+ return rc;
+}
+
+int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_m, u32 *t_r)
+{
+ int rc;
+ struct wmi_temp_sense_cmd cmd = {
+ .measure_marlon_m_en = cpu_to_le32(!!t_m),
+ .measure_marlon_r_en = cpu_to_le32(!!t_r),
+ };
+ struct {
+ struct wil6210_mbox_hdr_wmi wmi;
+ struct wmi_temp_sense_done_event evt;
+ } __packed reply;
+
+ rc = wmi_call(wil, WMI_TEMP_SENSE_CMDID, &cmd, sizeof(cmd),
+ WMI_TEMP_SENSE_DONE_EVENTID, &reply, sizeof(reply), 100);
+ if (rc)
+ return rc;
+
+ if (t_m)
+ *t_m = le32_to_cpu(reply.evt.marlon_m_t1000);
+ if (t_r)
+ *t_r = le32_to_cpu(reply.evt.marlon_r_t1000);
+
+ return 0;
+}
+
+int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, u16 reason)
+{
+ struct wmi_disconnect_sta_cmd cmd = {
+ .disconnect_reason = cpu_to_le16(reason),
+ };
+ memcpy(cmd.dst_mac, mac, ETH_ALEN);
+
+ wil_dbg_wmi(wil, "%s(%pM, reason %d)\n", __func__, mac, reason);
+
+ return wmi_send(wil, WMI_DISCONNECT_STA_CMDID, &cmd, sizeof(cmd));
+}
+
void wmi_event_flush(struct wil6210_priv *wil)
{
struct pending_wmi_event *evt, *t;
- wil_dbg_WMI(wil, "%s()\n", __func__);
+ wil_dbg_wmi(wil, "%s()\n", __func__);
list_for_each_entry_safe(evt, t, &wil->pending_wmi_ev, list) {
list_del(&evt->list);
@@ -899,7 +1143,7 @@ static void wmi_event_handle(struct wil6210_priv *wil,
wmi_evt_call_handler(wil, id, evt_data,
len - sizeof(*wmi));
}
- wil_dbg_WMI(wil, "Complete WMI 0x%04x\n", id);
+ wil_dbg_wmi(wil, "Complete WMI 0x%04x\n", id);
complete(&wil->wmi_ready);
return;
}
@@ -952,24 +1196,3 @@ void wmi_event_worker(struct work_struct *work)
kfree(evt);
}
}
-
-void wmi_connect_worker(struct work_struct *work)
-{
- int rc;
- struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
- wmi_connect_worker);
-
- if (wil->pending_connect_cid < 0) {
- wil_err(wil, "No connection pending\n");
- return;
- }
-
- wil_dbg_WMI(wil, "Configure for connection CID %d\n",
- wil->pending_connect_cid);
-
- rc = wil_vring_init_tx(wil, 0, WIL6210_TX_RING_SIZE,
- wil->pending_connect_cid, 0);
- wil->pending_connect_cid = -1;
- if (rc == 0)
- wil_link_on(wil);
-}
diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h
index 3bbf87572b0..17334c85286 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.h
+++ b/drivers/net/wireless/ath/wil6210/wmi.h
@@ -28,7 +28,7 @@
#define __WILOCITY_WMI_H__
/* General */
-
+#define WILOCITY_MAX_ASSOC_STA (8)
#define WMI_MAC_LEN (6)
#define WMI_PROX_RANGE_NUM (3)
@@ -36,6 +36,7 @@
enum wmi_command_id {
WMI_CONNECT_CMDID = 0x0001,
WMI_DISCONNECT_CMDID = 0x0003,
+ WMI_DISCONNECT_STA_CMDID = 0x0004,
WMI_START_SCAN_CMDID = 0x0007,
WMI_SET_BSS_FILTER_CMDID = 0x0009,
WMI_SET_PROBED_SSID_CMDID = 0x000a,
@@ -44,7 +45,6 @@ enum wmi_command_id {
WMI_ADD_CIPHER_KEY_CMDID = 0x0016,
WMI_DELETE_CIPHER_KEY_CMDID = 0x0017,
WMI_SET_APPIE_CMDID = 0x003f,
- WMI_GET_APPIE_CMDID = 0x0040,
WMI_SET_WSC_STATUS_CMDID = 0x0041,
WMI_PXMT_RANGE_CFG_CMDID = 0x0042,
WMI_PXMT_SNR2_RANGE_CFG_CMDID = 0x0043,
@@ -55,11 +55,11 @@ enum wmi_command_id {
WMI_DEEP_ECHO_CMDID = 0x0804,
WMI_CONFIG_MAC_CMDID = 0x0805,
WMI_CONFIG_PHY_DEBUG_CMDID = 0x0806,
- WMI_ADD_STATION_CMDID = 0x0807,
WMI_ADD_DEBUG_TX_PCKT_CMDID = 0x0808,
WMI_PHY_GET_STATISTICS_CMDID = 0x0809,
WMI_FS_TUNE_CMDID = 0x080a,
WMI_CORR_MEASURE_CMDID = 0x080b,
+ WMI_READ_RSSI_CMDID = 0x080c,
WMI_TEMP_SENSE_CMDID = 0x080e,
WMI_DC_CALIB_CMDID = 0x080f,
WMI_SEND_TONE_CMDID = 0x0810,
@@ -75,9 +75,9 @@ enum wmi_command_id {
MAC_IO_STATIC_PARAMS_CMDID = 0x081b,
MAC_IO_DYNAMIC_PARAMS_CMDID = 0x081c,
WMI_SILENT_RSSI_CALIB_CMDID = 0x081d,
+ WMI_RF_RX_TEST_CMDID = 0x081e,
WMI_CFG_RX_CHAIN_CMDID = 0x0820,
WMI_VRING_CFG_CMDID = 0x0821,
- WMI_RX_ON_CMDID = 0x0822,
WMI_VRING_BA_EN_CMDID = 0x0823,
WMI_VRING_BA_DIS_CMDID = 0x0824,
WMI_RCP_ADDBA_RESP_CMDID = 0x0825,
@@ -87,7 +87,6 @@ enum wmi_command_id {
WMI_SET_PCP_CHANNEL_CMDID = 0x0829,
WMI_GET_PCP_CHANNEL_CMDID = 0x082a,
WMI_SW_TX_REQ_CMDID = 0x082b,
- WMI_RX_OFF_CMDID = 0x082c,
WMI_READ_MAC_RXQ_CMDID = 0x0830,
WMI_READ_MAC_TXQ_CMDID = 0x0831,
WMI_WRITE_MAC_RXQ_CMDID = 0x0832,
@@ -112,6 +111,18 @@ enum wmi_command_id {
WMI_FLASH_READ_CMDID = 0x0902,
WMI_FLASH_WRITE_CMDID = 0x0903,
WMI_SECURITY_UNIT_TEST_CMDID = 0x0904,
+ /*P2P*/
+ WMI_P2P_CFG_CMDID = 0x0910,
+ WMI_PORT_ALLOCATE_CMDID = 0x0911,
+ WMI_PORT_DELETE_CMDID = 0x0912,
+ WMI_POWER_MGMT_CFG_CMDID = 0x0913,
+ WMI_START_LISTEN_CMDID = 0x0914,
+ WMI_START_SEARCH_CMDID = 0x0915,
+ WMI_DISCOVERY_START_CMDID = 0x0916,
+ WMI_DISCOVERY_STOP_CMDID = 0x0917,
+ WMI_PCP_START_CMDID = 0x0918,
+ WMI_PCP_STOP_CMDID = 0x0919,
+ WMI_GET_PCP_FACTOR_CMDID = 0x091b,
WMI_SET_MAC_ADDRESS_CMDID = 0xf003,
WMI_ABORT_SCAN_CMDID = 0xf007,
@@ -132,18 +143,6 @@ enum wmi_command_id {
*/
/*
- * Frame Types
- */
-enum wmi_mgmt_frame_type {
- WMI_FRAME_BEACON = 0,
- WMI_FRAME_PROBE_REQ = 1,
- WMI_FRAME_PROBE_RESP = 2,
- WMI_FRAME_ASSOC_REQ = 3,
- WMI_FRAME_ASSOC_RESP = 4,
- WMI_NUM_MGMT_FRAME,
-};
-
-/*
* WMI_CONNECT_CMDID
*/
enum wmi_network_type {
@@ -184,7 +183,7 @@ enum wmi_crypto_type {
enum wmi_connect_ctrl_flag_bits {
WMI_CONNECT_ASSOC_POLICY_USER = 0x0001,
WMI_CONNECT_SEND_REASSOC = 0x0002,
- WMI_CONNECT_IGNORE_WPAx_GROUP_CIPHER = 0x0004,
+ WMI_CONNECT_IGNORE_WPA_GROUP_CIPHER = 0x0004,
WMI_CONNECT_PROFILE_MATCH_DONE = 0x0008,
WMI_CONNECT_IGNORE_AAC_BEACON = 0x0010,
WMI_CONNECT_CSA_FOLLOW_BSS = 0x0020,
@@ -212,14 +211,12 @@ struct wmi_connect_cmd {
u8 reserved1[2];
} __packed;
-
/*
- * WMI_RECONNECT_CMDID
+ * WMI_DISCONNECT_STA_CMDID
*/
-struct wmi_reconnect_cmd {
- u8 channel; /* hint */
- u8 reserved;
- u8 bssid[WMI_MAC_LEN]; /* mandatory if set */
+struct wmi_disconnect_sta_cmd {
+ u8 dst_mac[WMI_MAC_LEN];
+ __le16 disconnect_reason;
} __packed;
@@ -289,10 +286,14 @@ struct wmi_delete_cipher_key_cmd {
enum wmi_scan_type {
WMI_LONG_SCAN = 0,
WMI_SHORT_SCAN = 1,
+ WMI_PBC_SCAN = 2,
+ WMI_ACTIVE_SCAN = 3,
+ WMI_DIRECT_SCAN = 4,
};
struct wmi_start_scan_cmd {
- u8 reserved[8];
+ u8 direct_scan_mac_addr[6];
+ u8 reserved[2];
__le32 home_dwell_time; /* Max duration in the home channel(ms) */
__le32 force_scan_interval; /* Time interval between scans (ms)*/
u8 scan_type; /* wmi_scan_type */
@@ -309,7 +310,7 @@ struct wmi_start_scan_cmd {
/*
* WMI_SET_PROBED_SSID_CMDID
*/
-#define MAX_PROBED_SSID_INDEX (15)
+#define MAX_PROBED_SSID_INDEX (3)
enum wmi_ssid_flag {
WMI_SSID_FLAG_DISABLE = 0, /* disables entry */
@@ -324,10 +325,25 @@ struct wmi_probed_ssid_cmd {
u8 ssid[WMI_MAX_SSID_LEN];
} __packed;
+
/*
* WMI_SET_APPIE_CMDID
* Add Application specified IE to a management frame
*/
+#define WMI_MAX_IE_LEN (1024)
+
+/*
+ * Frame Types
+ */
+enum wmi_mgmt_frame_type {
+ WMI_FRAME_BEACON = 0,
+ WMI_FRAME_PROBE_REQ = 1,
+ WMI_FRAME_PROBE_RESP = 2,
+ WMI_FRAME_ASSOC_REQ = 3,
+ WMI_FRAME_ASSOC_RESP = 4,
+ WMI_NUM_MGMT_FRAME,
+};
+
struct wmi_set_appie_cmd {
u8 mgmt_frm_type; /* enum wmi_mgmt_frame_type */
u8 reserved;
@@ -335,13 +351,18 @@ struct wmi_set_appie_cmd {
u8 ie_info[0];
} __packed;
-#define WMI_MAX_IE_LEN (1024)
+/*
+ * WMI_PXMT_RANGE_CFG_CMDID
+ */
struct wmi_pxmt_range_cfg_cmd {
u8 dst_mac[WMI_MAC_LEN];
__le16 range;
} __packed;
+/*
+ * WMI_PXMT_SNR2_RANGE_CFG_CMDID
+ */
struct wmi_pxmt_snr2_range_cfg_cmd {
s8 snr2range_arr[WMI_PROX_RANGE_NUM-1];
} __packed;
@@ -359,6 +380,23 @@ struct wmi_rf_mgmt_cmd {
__le32 rf_mgmt_type;
} __packed;
+
+/*
+ * WMI_RF_RX_TEST_CMDID
+ */
+struct wmi_rf_rx_test_cmd {
+ __le32 sector;
+} __packed;
+
+/*
+ * WMI_CORR_MEASURE_CMDID
+ */
+struct wmi_corr_measure_cmd {
+ s32 freq_mhz;
+ __le32 length_samples;
+ __le32 iterations;
+} __packed;
+
/*
* WMI_SET_SSID_CMDID
*/
@@ -383,7 +421,77 @@ struct wmi_bcon_ctrl_cmd {
__le16 frag_num;
__le64 ss_mask;
u8 network_type;
- u8 reserved;
+ u8 pcp_max_assoc_sta;
+ u8 disable_sec_offload;
+ u8 disable_sec;
+} __packed;
+
+
+/******* P2P ***********/
+
+/*
+ * WMI_PORT_ALLOCATE_CMDID
+ */
+enum wmi_port_role {
+ WMI_PORT_STA = 0,
+ WMI_PORT_PCP = 1,
+ WMI_PORT_AP = 2,
+ WMI_PORT_P2P_DEV = 3,
+ WMI_PORT_P2P_CLIENT = 4,
+ WMI_PORT_P2P_GO = 5,
+};
+
+struct wmi_port_allocate_cmd {
+ u8 mac[WMI_MAC_LEN];
+ u8 port_role;
+ u8 mid;
+} __packed;
+
+/*
+ * WMI_PORT_DELETE_CMDID
+ */
+struct wmi_delete_port_cmd {
+ u8 mid;
+ u8 reserved[3];
+} __packed;
+
+/*
+ * WMI_P2P_CFG_CMDID
+ */
+enum wmi_discovery_mode {
+ WMI_DISCOVERY_MODE_NON_OFFLOAD = 0,
+ WMI_DISCOVERY_MODE_OFFLOAD = 1,
+ WMI_DISCOVERY_MODE_PEER2PEER = 2,
+};
+
+struct wmi_p2p_cfg_cmd {
+ u8 discovery_mode; /* wmi_discovery_mode */
+ u8 channel;
+ __le16 bcon_interval; /* base to listen/search duration calculation */
+} __packed;
+
+/*
+ * WMI_POWER_MGMT_CFG_CMDID
+ */
+enum wmi_power_source_type {
+ WMI_POWER_SOURCE_BATTERY = 0,
+ WMI_POWER_SOURCE_OTHER = 1,
+};
+
+struct wmi_power_mgmt_cfg_cmd {
+ u8 power_source; /* wmi_power_source_type */
+ u8 reserved[3];
+} __packed;
+
+/*
+ * WMI_PCP_START_CMDID
+ */
+struct wmi_pcp_start_cmd {
+ __le16 bcon_interval;
+ u8 pcp_max_assoc_sta;
+ u8 reserved0[9];
+ u8 network_type;
+ u8 channel;
u8 disable_sec_offload;
u8 disable_sec;
} __packed;
@@ -435,16 +543,17 @@ enum wmi_vring_cfg_schd_params_priority {
WMI_SCH_PRIO_HIGH = 1,
};
+#define CIDXTID_CID_POS (0)
+#define CIDXTID_CID_LEN (4)
+#define CIDXTID_CID_MSK (0xF)
+#define CIDXTID_TID_POS (4)
+#define CIDXTID_TID_LEN (4)
+#define CIDXTID_TID_MSK (0xF0)
+
struct wmi_vring_cfg {
struct wmi_sw_ring_cfg tx_sw_ring;
u8 ringid; /* 0-23 vrings */
- #define CIDXTID_CID_POS (0)
- #define CIDXTID_CID_LEN (4)
- #define CIDXTID_CID_MSK (0xF)
- #define CIDXTID_TID_POS (4)
- #define CIDXTID_TID_LEN (4)
- #define CIDXTID_TID_MSK (0xF0)
u8 cidxtid;
u8 encap_trans_type;
@@ -501,8 +610,14 @@ struct wmi_vring_ba_dis_cmd {
*/
struct wmi_notify_req_cmd {
u8 cid;
- u8 reserved[3];
+ u8 year;
+ u8 month;
+ u8 day;
__le32 interval_usec;
+ u8 hour;
+ u8 minute;
+ u8 second;
+ u8 miliseconds;
} __packed;
/*
@@ -548,6 +663,11 @@ enum wmi_cfg_rx_chain_cmd_nwifi_ds_trans_type {
WMI_NWIFI_RX_TRANS_MODE_PBSS2STA = 2,
};
+enum wmi_cfg_rx_chain_cmd_reorder_type {
+ WMI_RX_HW_REORDER = 0,
+ WMI_RX_SW_REORDER = 1,
+};
+
struct wmi_cfg_rx_chain_cmd {
__le32 action;
struct wmi_sw_ring_cfg rx_sw_ring;
@@ -596,7 +716,8 @@ struct wmi_cfg_rx_chain_cmd {
__le16 wb_thrsh;
__le32 itr_value;
__le16 host_thrsh;
- u8 reserved[2];
+ u8 reorder_type;
+ u8 reserved;
struct wmi_sniffer_cfg sniffer_cfg;
} __packed;
@@ -604,15 +725,7 @@ struct wmi_cfg_rx_chain_cmd {
* WMI_RCP_ADDBA_RESP_CMDID
*/
struct wmi_rcp_addba_resp_cmd {
-
- #define CIDXTID_CID_POS (0)
- #define CIDXTID_CID_LEN (4)
- #define CIDXTID_CID_MSK (0xF)
- #define CIDXTID_TID_POS (4)
- #define CIDXTID_TID_LEN (4)
- #define CIDXTID_TID_MSK (0xF0)
u8 cidxtid;
-
u8 dialog_token;
__le16 status_code;
__le16 ba_param_set; /* ieee80211_ba_parameterset field to send */
@@ -623,15 +736,7 @@ struct wmi_rcp_addba_resp_cmd {
* WMI_RCP_DELBA_CMDID
*/
struct wmi_rcp_delba_cmd {
-
- #define CIDXTID_CID_POS (0)
- #define CIDXTID_CID_LEN (4)
- #define CIDXTID_CID_MSK (0xF)
- #define CIDXTID_TID_POS (4)
- #define CIDXTID_TID_LEN (4)
- #define CIDXTID_TID_MSK (0xF0)
u8 cidxtid;
-
u8 reserved;
__le16 reason;
} __packed;
@@ -640,15 +745,7 @@ struct wmi_rcp_delba_cmd {
* WMI_RCP_ADDBA_REQ_CMDID
*/
struct wmi_rcp_addba_req_cmd {
-
- #define CIDXTID_CID_POS (0)
- #define CIDXTID_CID_LEN (4)
- #define CIDXTID_CID_MSK (0xF)
- #define CIDXTID_TID_POS (4)
- #define CIDXTID_TID_LEN (4)
- #define CIDXTID_TID_MSK (0xF0)
u8 cidxtid;
-
u8 dialog_token;
/* ieee80211_ba_parameterset field as it received */
__le16 ba_param_set;
@@ -665,7 +762,6 @@ struct wmi_set_mac_address_cmd {
u8 reserved[2];
} __packed;
-
/*
* WMI_EAPOL_TX_CMDID
*/
@@ -692,6 +788,17 @@ struct wmi_echo_cmd {
} __packed;
/*
+ * WMI_TEMP_SENSE_CMDID
+ *
+ * Measure MAC and radio temperatures
+ */
+struct wmi_temp_sense_cmd {
+ __le32 measure_marlon_m_en;
+ __le32 measure_marlon_r_en;
+} __packed;
+
+
+/*
* WMI Events
*/
@@ -699,7 +806,6 @@ struct wmi_echo_cmd {
* List of Events (target to host)
*/
enum wmi_event_id {
- WMI_IMM_RSP_EVENTID = 0x0000,
WMI_READY_EVENTID = 0x1001,
WMI_CONNECT_EVENTID = 0x1002,
WMI_DISCONNECT_EVENTID = 0x1003,
@@ -709,13 +815,9 @@ enum wmi_event_id {
WMI_FW_READY_EVENTID = 0x1801,
WMI_EXIT_FAST_MEM_ACC_MODE_EVENTID = 0x0200,
WMI_ECHO_RSP_EVENTID = 0x1803,
- WMI_CONFIG_MAC_DONE_EVENTID = 0x1805,
- WMI_CONFIG_PHY_DEBUG_DONE_EVENTID = 0x1806,
- WMI_ADD_STATION_DONE_EVENTID = 0x1807,
- WMI_ADD_DEBUG_TX_PCKT_DONE_EVENTID = 0x1808,
- WMI_PHY_GET_STATISTICS_EVENTID = 0x1809,
WMI_FS_TUNE_DONE_EVENTID = 0x180a,
- WMI_CORR_MEASURE_DONE_EVENTID = 0x180b,
+ WMI_CORR_MEASURE_EVENTID = 0x180b,
+ WMI_READ_RSSI_EVENTID = 0x180c,
WMI_TEMP_SENSE_DONE_EVENTID = 0x180e,
WMI_DC_CALIB_DONE_EVENTID = 0x180f,
WMI_IQ_TX_CALIB_DONE_EVENTID = 0x1811,
@@ -727,10 +829,9 @@ enum wmi_event_id {
WMI_MARLON_R_WRITE_DONE_EVENTID = 0x1819,
WMI_MARLON_R_TXRX_SEL_DONE_EVENTID = 0x181a,
WMI_SILENT_RSSI_CALIB_DONE_EVENTID = 0x181d,
-
+ WMI_RF_RX_TEST_DONE_EVENTID = 0x181e,
WMI_CFG_RX_CHAIN_DONE_EVENTID = 0x1820,
WMI_VRING_CFG_DONE_EVENTID = 0x1821,
- WMI_RX_ON_DONE_EVENTID = 0x1822,
WMI_BA_STATUS_EVENTID = 0x1823,
WMI_RCP_ADDBA_REQ_EVENTID = 0x1824,
WMI_ADDBA_RESP_SENT_EVENTID = 0x1825,
@@ -738,7 +839,6 @@ enum wmi_event_id {
WMI_GET_SSID_EVENTID = 0x1828,
WMI_GET_PCP_CHANNEL_EVENTID = 0x182a,
WMI_SW_TX_COMPLETE_EVENTID = 0x182b,
- WMI_RX_OFF_DONE_EVENTID = 0x182c,
WMI_READ_MAC_RXQ_EVENTID = 0x1830,
WMI_READ_MAC_TXQ_EVENTID = 0x1831,
@@ -753,6 +853,7 @@ enum wmi_event_id {
WMI_RF_MGMT_STATUS_EVENTID = 0x1853,
WMI_BF_SM_MGMT_DONE_EVENTID = 0x1838,
WMI_RX_MGMT_PACKET_EVENTID = 0x1840,
+ WMI_TX_MGMT_PACKET_EVENTID = 0x1841,
/* Performance monitoring events */
WMI_DATA_PORT_OPEN_EVENTID = 0x1860,
@@ -765,7 +866,16 @@ enum wmi_event_id {
WMI_UNIT_TEST_EVENTID = 0x1900,
WMI_FLASH_READ_DONE_EVENTID = 0x1902,
WMI_FLASH_WRITE_DONE_EVENTID = 0x1903,
-
+ /*P2P*/
+ WMI_PORT_ALLOCATED_EVENTID = 0x1911,
+ WMI_PORT_DELETED_EVENTID = 0x1912,
+ WMI_LISTEN_STARTED_EVENTID = 0x1914,
+ WMI_SEARCH_STARTED_EVENTID = 0x1915,
+ WMI_DISCOVERY_STARTED_EVENTID = 0x1916,
+ WMI_DISCOVERY_STOPPED_EVENTID = 0x1917,
+ WMI_PCP_STARTED_EVENTID = 0x1918,
+ WMI_PCP_STOPPED_EVENTID = 0x1919,
+ WMI_PCP_FACTOR_EVENTID = 0x191a,
WMI_SET_CHANNEL_EVENTID = 0x9000,
WMI_ASSOC_REQ_EVENTID = 0x9001,
WMI_EAPOL_RX_EVENTID = 0x9002,
@@ -777,6 +887,12 @@ enum wmi_event_id {
* Events data structures
*/
+
+enum wmi_fw_status {
+ WMI_FW_STATUS_SUCCESS,
+ WMI_FW_STATUS_FAILURE,
+};
+
/*
* WMI_RF_MGMT_STATUS_EVENTID
*/
@@ -857,7 +973,7 @@ struct wmi_ready_event {
__le32 abi_version;
u8 mac[WMI_MAC_LEN];
u8 phy_capability; /* enum wmi_phy_capability */
- u8 reserved;
+ u8 numof_additional_mids;
} __packed;
/*
@@ -876,6 +992,8 @@ struct wmi_notify_req_done_event {
__le16 other_rx_sector;
__le16 other_tx_sector;
__le16 range;
+ u8 sqi;
+ u8 reserved[3];
} __packed;
/*
@@ -919,16 +1037,23 @@ enum wmi_disconnect_reason {
struct wmi_disconnect_event {
__le16 protocol_reason_status; /* reason code, see 802.11 spec. */
u8 bssid[WMI_MAC_LEN]; /* set if known */
- u8 disconnect_reason; /* see wmi_disconnect_reason_e */
- u8 assoc_resp_len;
- u8 assoc_info[0];
+ u8 disconnect_reason; /* see wmi_disconnect_reason */
+ u8 assoc_resp_len; /* not in use */
+ u8 assoc_info[0]; /* not in use */
} __packed;
/*
* WMI_SCAN_COMPLETE_EVENTID
*/
+enum scan_status {
+ WMI_SCAN_SUCCESS = 0,
+ WMI_SCAN_FAILED = 1,
+ WMI_SCAN_ABORTED = 2,
+ WMI_SCAN_REJECTED = 3,
+};
+
struct wmi_scan_complete_event {
- __le32 status;
+ __le32 status; /* scan_status */
} __packed;
/*
@@ -951,27 +1076,15 @@ struct wmi_vring_ba_status_event {
* WMI_DELBA_EVENTID
*/
struct wmi_delba_event {
-
- #define CIDXTID_CID_POS (0)
- #define CIDXTID_CID_LEN (4)
- #define CIDXTID_CID_MSK (0xF)
- #define CIDXTID_TID_POS (4)
- #define CIDXTID_TID_LEN (4)
- #define CIDXTID_TID_MSK (0xF0)
u8 cidxtid;
-
u8 from_initiator;
__le16 reason;
} __packed;
+
/*
* WMI_VRING_CFG_DONE_EVENTID
*/
-enum wmi_vring_cfg_done_event_status {
- WMI_VRING_CFG_SUCCESS = 0,
- WMI_VRING_CFG_FAILURE = 1,
-};
-
struct wmi_vring_cfg_done_event {
u8 ringid;
u8 status;
@@ -982,21 +1095,8 @@ struct wmi_vring_cfg_done_event {
/*
* WMI_ADDBA_RESP_SENT_EVENTID
*/
-enum wmi_rcp_addba_resp_sent_event_status {
- WMI_ADDBA_SUCCESS = 0,
- WMI_ADDBA_FAIL = 1,
-};
-
struct wmi_rcp_addba_resp_sent_event {
-
- #define CIDXTID_CID_POS (0)
- #define CIDXTID_CID_LEN (4)
- #define CIDXTID_CID_MSK (0xF)
- #define CIDXTID_TID_POS (4)
- #define CIDXTID_TID_LEN (4)
- #define CIDXTID_TID_MSK (0xF0)
u8 cidxtid;
-
u8 reserved;
__le16 status;
} __packed;
@@ -1005,15 +1105,7 @@ struct wmi_rcp_addba_resp_sent_event {
* WMI_RCP_ADDBA_REQ_EVENTID
*/
struct wmi_rcp_addba_req_event {
-
- #define CIDXTID_CID_POS (0)
- #define CIDXTID_CID_LEN (4)
- #define CIDXTID_CID_MSK (0xF)
- #define CIDXTID_TID_POS (4)
- #define CIDXTID_TID_LEN (4)
- #define CIDXTID_TID_MSK (0xF0)
u8 cidxtid;
-
u8 dialog_token;
__le16 ba_param_set; /* ieee80211_ba_parameterset as it received */
__le16 ba_timeout;
@@ -1055,6 +1147,7 @@ struct wmi_data_port_open_event {
u8 reserved[3];
} __packed;
+
/*
* WMI_GET_PCP_CHANNEL_EVENTID
*/
@@ -1063,6 +1156,54 @@ struct wmi_get_pcp_channel_event {
u8 reserved[3];
} __packed;
+
+/*
+* WMI_PORT_ALLOCATED_EVENTID
+*/
+struct wmi_port_allocated_event {
+ u8 status; /* wmi_fw_status */
+ u8 reserved[3];
+} __packed;
+
+/*
+* WMI_PORT_DELETED_EVENTID
+*/
+struct wmi_port_deleted_event {
+ u8 status; /* wmi_fw_status */
+ u8 reserved[3];
+} __packed;
+
+/*
+ * WMI_LISTEN_STARTED_EVENTID
+ */
+struct wmi_listen_started_event {
+ u8 status; /* wmi_fw_status */
+ u8 reserved[3];
+} __packed;
+
+/*
+ * WMI_SEARCH_STARTED_EVENTID
+ */
+struct wmi_search_started_event {
+ u8 status; /* wmi_fw_status */
+ u8 reserved[3];
+} __packed;
+
+/*
+ * WMI_PCP_STARTED_EVENTID
+ */
+struct wmi_pcp_started_event {
+ u8 status; /* wmi_fw_status */
+ u8 reserved[3];
+} __packed;
+
+/*
+ * WMI_PCP_FACTOR_EVENTID
+ */
+struct wmi_pcp_factor_event {
+ __le32 pcp_factor;
+} __packed;
+
/*
* WMI_SW_TX_COMPLETE_EVENTID
*/
@@ -1078,6 +1219,23 @@ struct wmi_sw_tx_complete_event {
} __packed;
/*
+ * WMI_CORR_MEASURE_EVENTID
+ */
+struct wmi_corr_measure_event {
+ s32 i;
+ s32 q;
+ s32 image_i;
+ s32 image_q;
+} __packed;
+
+/*
+ * WMI_READ_RSSI_EVENTID
+ */
+struct wmi_read_rssi_event {
+ __le32 ina_rssi_adc_dbm;
+} __packed;
+
+/*
* WMI_GET_SSID_EVENTID
*/
struct wmi_get_ssid_event {
@@ -1091,7 +1249,8 @@ struct wmi_get_ssid_event {
struct wmi_rx_mgmt_info {
u8 mcs;
s8 snr;
- __le16 range;
+ u8 range;
+ u8 sqi;
__le16 stype;
__le16 status;
__le32 len;
@@ -1101,6 +1260,14 @@ struct wmi_rx_mgmt_info {
u8 channel; /* From Radio MNGR */
} __packed;
+
+/*
+ * WMI_TX_MGMT_PACKET_EVENTID
+ */
+struct wmi_tx_mgmt_packet_event {
+ u8 payload[0];
+} __packed;
+
struct wmi_rx_mgmt_packet_event {
struct wmi_rx_mgmt_info info;
u8 payload[0];
@@ -1113,4 +1280,14 @@ struct wmi_echo_event {
__le32 echoed_value;
} __packed;
+/*
+ * WMI_TEMP_SENSE_DONE_EVENTID
+ *
+ * Measure MAC and radio temperatures
+ */
+struct wmi_temp_sense_done_event {
+ __le32 marlon_m_t1000;
+ __le32 marlon_r_t1000;
+} __packed;
+
#endif /* __WILOCITY_WMI_H__ */