Merge ath-next from git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git

ath.git patches for 4.16. Major changes:

ath10k

* enable multiqueue support for all hw using mac80211 wake_tx_queue op

* new Kconfig option ATH10K_SPECTRAL to save RAM

* show tx stats on QCA9880

* new qcom,ath10k-calibration-variant DT entry

* WMI layer support for wcn3990

ath9k

* new Kconfig option ATH9K_COMMON_SPECTRAL to save RAM

wcn36xx

* hardware scan offload support

wil6210

* run-time PM support when interface is down
This commit is contained in:
Kalle Valo 2017-12-14 18:38:48 +02:00
commit 1dde35d0b8
42 changed files with 1664 additions and 344 deletions

View file

@ -41,6 +41,9 @@ Optional properties:
- qcom,msi_addr: MSI interrupt address.
- qcom,msi_base: Base value to add before writing MSI data into
MSI address register.
- qcom,ath10k-calibration-variant: string to search for in the board-2.bin
variant list with the same bus and device
specific ids
- qcom,ath10k-calibration-data : calibration data + board specific data
as an array, the length can vary between
hw versions.

View file

@ -47,12 +47,19 @@ config ATH10K_DEBUG
config ATH10K_DEBUGFS
bool "Atheros ath10k debugfs support"
depends on ATH10K && DEBUG_FS
select RELAY
---help---
Enabled debugfs support
If unsure, say Y to make it easier to debug problems.
config ATH10K_SPECTRAL
bool "Atheros ath10k spectral scan support"
depends on ATH10K_DEBUGFS
select RELAY
default n
---help---
Say Y to enable access to the FFT/spectral data via debugfs.
config ATH10K_TRACING
bool "Atheros ath10k tracing support"
depends on ATH10K

View file

@ -15,7 +15,7 @@ ath10k_core-y += mac.o \
p2p.o \
swap.o
ath10k_core-$(CONFIG_ATH10K_DEBUGFS) += spectral.o
ath10k_core-$(CONFIG_ATH10K_SPECTRAL) += spectral.o
ath10k_core-$(CONFIG_NL80211_TESTMODE) += testmode.o
ath10k_core-$(CONFIG_ATH10K_TRACING) += trace.o
ath10k_core-$(CONFIG_THERMAL) += thermal.o

View file

@ -75,6 +75,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.vht160_mcs_rx_highest = 0,
.vht160_mcs_tx_highest = 0,
.n_cipher_suites = 8,
.num_peers = TARGET_TLV_NUM_PEERS,
.ast_skid_limit = 0x10,
.num_wds_entries = 0x20,
},
{
.id = QCA9887_HW_1_0_VERSION,
@ -99,6 +102,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.vht160_mcs_rx_highest = 0,
.vht160_mcs_tx_highest = 0,
.n_cipher_suites = 8,
.num_peers = TARGET_TLV_NUM_PEERS,
.ast_skid_limit = 0x10,
.num_wds_entries = 0x20,
},
{
.id = QCA6174_HW_2_1_VERSION,
@ -122,6 +128,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.vht160_mcs_rx_highest = 0,
.vht160_mcs_tx_highest = 0,
.n_cipher_suites = 8,
.num_peers = TARGET_TLV_NUM_PEERS,
.ast_skid_limit = 0x10,
.num_wds_entries = 0x20,
},
{
.id = QCA6174_HW_2_1_VERSION,
@ -145,6 +154,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.vht160_mcs_rx_highest = 0,
.vht160_mcs_tx_highest = 0,
.n_cipher_suites = 8,
.num_peers = TARGET_TLV_NUM_PEERS,
.ast_skid_limit = 0x10,
.num_wds_entries = 0x20,
},
{
.id = QCA6174_HW_3_0_VERSION,
@ -168,6 +180,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.vht160_mcs_rx_highest = 0,
.vht160_mcs_tx_highest = 0,
.n_cipher_suites = 8,
.num_peers = TARGET_TLV_NUM_PEERS,
.ast_skid_limit = 0x10,
.num_wds_entries = 0x20,
},
{
.id = QCA6174_HW_3_2_VERSION,
@ -194,6 +209,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.vht160_mcs_rx_highest = 0,
.vht160_mcs_tx_highest = 0,
.n_cipher_suites = 8,
.num_peers = TARGET_TLV_NUM_PEERS,
.ast_skid_limit = 0x10,
.num_wds_entries = 0x20,
},
{
.id = QCA99X0_HW_2_0_DEV_VERSION,
@ -223,6 +241,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.vht160_mcs_rx_highest = 0,
.vht160_mcs_tx_highest = 0,
.n_cipher_suites = 11,
.num_peers = TARGET_TLV_NUM_PEERS,
.ast_skid_limit = 0x10,
.num_wds_entries = 0x20,
},
{
.id = QCA9984_HW_1_0_DEV_VERSION,
@ -257,6 +278,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.vht160_mcs_rx_highest = 1560,
.vht160_mcs_tx_highest = 1560,
.n_cipher_suites = 11,
.num_peers = TARGET_TLV_NUM_PEERS,
.ast_skid_limit = 0x10,
.num_wds_entries = 0x20,
},
{
.id = QCA9888_HW_2_0_DEV_VERSION,
@ -290,6 +314,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.vht160_mcs_rx_highest = 780,
.vht160_mcs_tx_highest = 780,
.n_cipher_suites = 11,
.num_peers = TARGET_TLV_NUM_PEERS,
.ast_skid_limit = 0x10,
.num_wds_entries = 0x20,
},
{
.id = QCA9377_HW_1_0_DEV_VERSION,
@ -313,6 +340,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.vht160_mcs_rx_highest = 0,
.vht160_mcs_tx_highest = 0,
.n_cipher_suites = 8,
.num_peers = TARGET_TLV_NUM_PEERS,
.ast_skid_limit = 0x10,
.num_wds_entries = 0x20,
},
{
.id = QCA9377_HW_1_1_DEV_VERSION,
@ -338,6 +368,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.vht160_mcs_rx_highest = 0,
.vht160_mcs_tx_highest = 0,
.n_cipher_suites = 8,
.num_peers = TARGET_TLV_NUM_PEERS,
.ast_skid_limit = 0x10,
.num_wds_entries = 0x20,
},
{
.id = QCA4019_HW_1_0_DEV_VERSION,
@ -368,6 +401,27 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.vht160_mcs_rx_highest = 0,
.vht160_mcs_tx_highest = 0,
.n_cipher_suites = 11,
.num_peers = TARGET_TLV_NUM_PEERS,
.ast_skid_limit = 0x10,
.num_wds_entries = 0x20,
},
{
.id = WCN3990_HW_1_0_DEV_VERSION,
.dev_id = 0,
.name = "wcn3990 hw1.0",
.continuous_frag_desc = true,
.tx_chain_mask = 0x7,
.rx_chain_mask = 0x7,
.max_spatial_stream = 4,
.fw = {
.dir = WCN3990_HW_1_0_FW_DIR,
},
.sw_decrypt_mcast_mgmt = true,
.hw_ops = &wcn3990_ops,
.decap_align_bytes = 1,
.num_peers = TARGET_HL_10_TLV_NUM_PEERS,
.ast_skid_limit = TARGET_HL_10_TLV_AST_SKID_LIMIT,
.num_wds_entries = TARGET_HL_10_TLV_NUM_WDS_ENTRIES,
},
};
@ -390,6 +444,7 @@ static const char *const ath10k_core_fw_feature_str[] = {
[ATH10K_FW_FEATURE_SKIP_NULL_FUNC_WAR] = "skip-null-func-war",
[ATH10K_FW_FEATURE_ALLOWS_MESH_BCAST] = "allows-mesh-bcast",
[ATH10K_FW_FEATURE_NO_PS] = "no-ps",
[ATH10K_FW_FEATURE_MGMT_TX_BY_REF] = "mgmt-tx-by-reference",
};
static unsigned int ath10k_core_get_fw_feature_str(char *buf,
@ -860,6 +915,28 @@ static int ath10k_core_check_smbios(struct ath10k *ar)
return 0;
}
static int ath10k_core_check_dt(struct ath10k *ar)
{
struct device_node *node;
const char *variant = NULL;
node = ar->dev->of_node;
if (!node)
return -ENOENT;
of_property_read_string(node, "qcom,ath10k-calibration-variant",
&variant);
if (!variant)
return -ENODATA;
if (strscpy(ar->id.bdf_ext, variant, sizeof(ar->id.bdf_ext)) < 0)
ath10k_dbg(ar, ATH10K_DBG_BOOT,
"bdf variant string is longer than the buffer can accommodate (variant: %s)\n",
variant);
return 0;
}
static int ath10k_download_and_run_otp(struct ath10k *ar)
{
u32 result, address = ar->hw_params.patch_load_addr;
@ -1231,19 +1308,19 @@ static int ath10k_core_create_board_name(struct ath10k *ar, char *name,
/* strlen(',variant=') + strlen(ar->id.bdf_ext) */
char variant[9 + ATH10K_SMBIOS_BDF_EXT_STR_LENGTH] = { 0 };
if (ar->id.bmi_ids_valid) {
scnprintf(name, name_len,
"bus=%s,bmi-chip-id=%d,bmi-board-id=%d",
ath10k_bus_str(ar->hif.bus),
ar->id.bmi_chip_id,
ar->id.bmi_board_id);
goto out;
}
if (ar->id.bdf_ext[0] != '\0')
scnprintf(variant, sizeof(variant), ",variant=%s",
ar->id.bdf_ext);
if (ar->id.bmi_ids_valid) {
scnprintf(name, name_len,
"bus=%s,bmi-chip-id=%d,bmi-board-id=%d%s",
ath10k_bus_str(ar->hif.bus),
ar->id.bmi_chip_id,
ar->id.bmi_board_id, variant);
goto out;
}
scnprintf(name, name_len,
"bus=%s,vendor=%04x,device=%04x,subsystem-vendor=%04x,subsystem-device=%04x%s",
ath10k_bus_str(ar->hif.bus),
@ -2343,7 +2420,11 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
ret = ath10k_core_check_smbios(ar);
if (ret)
ath10k_dbg(ar, ATH10K_DBG_BOOT, "bdf variant name not set.\n");
ath10k_dbg(ar, ATH10K_DBG_BOOT, "SMBIOS bdf variant name not set.\n");
ret = ath10k_core_check_dt(ar);
if (ret)
ath10k_dbg(ar, ATH10K_DBG_BOOT, "DT bdf variant name not set.\n");
ret = ath10k_core_fetch_board_file(ar);
if (ret) {

View file

@ -67,7 +67,6 @@
/* NAPI poll budget */
#define ATH10K_NAPI_BUDGET 64
#define ATH10K_NAPI_QUOTA_LIMIT 60
/* SMBIOS type containing Board Data File Name Extension */
#define ATH10K_SMBIOS_BDF_EXT_TYPE 0xF8
@ -364,11 +363,11 @@ struct ath10k_sta {
struct rate_info txrate;
struct work_struct update_wk;
u64 rx_duration;
#ifdef CONFIG_MAC80211_DEBUGFS
/* protected by conf_mutex */
bool aggr_mode;
u64 rx_duration;
#endif
};
@ -463,7 +462,7 @@ struct ath10k_fw_crash_data {
bool crashed_since_read;
guid_t guid;
struct timespec timestamp;
struct timespec64 timestamp;
__le32 registers[REG_DUMP_COUNT_QCA988X];
struct ath10k_ce_crash_data ce_crash_data[CE_COUNT_MAX];
};
@ -488,7 +487,6 @@ struct ath10k_debug {
/* protected by conf_mutex */
u64 fw_dbglog_mask;
u32 fw_dbglog_level;
u32 pktlog_filter;
u32 reg_addr;
u32 nf_cal_period;
void *cal_data;
@ -615,6 +613,9 @@ enum ath10k_fw_features {
/* Firmware does not support power save in station mode. */
ATH10K_FW_FEATURE_NO_PS = 17,
/* Firmware allows management tx by reference instead of by value. */
ATH10K_FW_FEATURE_MGMT_TX_BY_REF = 18,
/* keep last */
ATH10K_FW_FEATURE_COUNT,
};
@ -963,6 +964,7 @@ struct ath10k {
} spectral;
#endif
u32 pktlog_filter;
struct {
/* protected by conf_mutex */
struct ath10k_fw_components utf_mode_fw;

View file

@ -720,7 +720,7 @@ ath10k_debug_get_new_fw_crash_data(struct ath10k *ar)
crash_data->crashed_since_read = true;
guid_gen(&crash_data->guid);
getnstimeofday(&crash_data->timestamp);
ktime_get_real_ts64(&crash_data->timestamp);
return crash_data;
}
@ -1950,14 +1950,14 @@ int ath10k_debug_start(struct ath10k *ar)
ret);
}
if (ar->debug.pktlog_filter) {
if (ar->pktlog_filter) {
ret = ath10k_wmi_pdev_pktlog_enable(ar,
ar->debug.pktlog_filter);
ar->pktlog_filter);
if (ret)
/* not serious */
ath10k_warn(ar,
"failed to enable pktlog filter %x: %d\n",
ar->debug.pktlog_filter, ret);
ar->pktlog_filter, ret);
} else {
ret = ath10k_wmi_pdev_pktlog_disable(ar);
if (ret)
@ -2097,12 +2097,12 @@ static ssize_t ath10k_write_pktlog_filter(struct file *file,
mutex_lock(&ar->conf_mutex);
if (ar->state != ATH10K_STATE_ON) {
ar->debug.pktlog_filter = filter;
ar->pktlog_filter = filter;
ret = count;
goto out;
}
if (filter == ar->debug.pktlog_filter) {
if (filter == ar->pktlog_filter) {
ret = count;
goto out;
}
@ -2111,7 +2111,7 @@ static ssize_t ath10k_write_pktlog_filter(struct file *file,
ret = ath10k_wmi_pdev_pktlog_enable(ar, filter);
if (ret) {
ath10k_warn(ar, "failed to enable pktlog filter %x: %d\n",
ar->debug.pktlog_filter, ret);
ar->pktlog_filter, ret);
goto out;
}
} else {
@ -2122,7 +2122,7 @@ static ssize_t ath10k_write_pktlog_filter(struct file *file,
}
}
ar->debug.pktlog_filter = filter;
ar->pktlog_filter = filter;
ret = count;
out:
@ -2139,7 +2139,7 @@ static ssize_t ath10k_read_pktlog_filter(struct file *file, char __user *ubuf,
mutex_lock(&ar->conf_mutex);
len = scnprintf(buf, sizeof(buf) - len, "%08x\n",
ar->debug.pktlog_filter);
ar->pktlog_filter);
mutex_unlock(&ar->conf_mutex);
return simple_read_from_buffer(ubuf, count, ppos, buf, len);

View file

@ -51,7 +51,8 @@ enum ath10k_pktlog_filter {
ATH10K_PKTLOG_RCFIND = 0x000000004,
ATH10K_PKTLOG_RCUPDATE = 0x000000008,
ATH10K_PKTLOG_DBG_PRINT = 0x000000010,
ATH10K_PKTLOG_ANY = 0x00000001f,
ATH10K_PKTLOG_PEER_STATS = 0x000000040,
ATH10K_PKTLOG_ANY = 0x00000005f,
};
enum ath10k_dbg_aggr_mode {
@ -60,6 +61,21 @@ enum ath10k_dbg_aggr_mode {
ATH10K_DBG_AGGR_MODE_MAX,
};
/* Types of packet log events */
enum ath_pktlog_type {
ATH_PKTLOG_TYPE_TX_CTRL = 1,
ATH_PKTLOG_TYPE_TX_STAT,
};
struct ath10k_pktlog_hdr {
__le16 flags;
__le16 missed_cnt;
__le16 log_type; /* Type of log information foll this header */
__le16 size; /* Size of variable length log information in bytes */
__le32 timestamp;
u8 payload[0];
} __packed;
/* FIXME: How to calculate the buffer size sanely? */
#define ATH10K_FW_STATS_BUF_SIZE (1024 * 1024)
@ -190,9 +206,6 @@ void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, struct dentry *dir);
void ath10k_sta_update_rx_duration(struct ath10k *ar,
struct ath10k_fw_stats *stats);
void ath10k_sta_statistics(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct station_info *sinfo);
#else
static inline
void ath10k_sta_update_rx_duration(struct ath10k *ar,

View file

@ -65,33 +65,6 @@ void ath10k_sta_update_rx_duration(struct ath10k *ar,
ath10k_sta_update_stats_rx_duration(ar, stats);
}
void ath10k_sta_statistics(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct station_info *sinfo)
{
struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
struct ath10k *ar = arsta->arvif->ar;
if (!ath10k_peer_stats_enabled(ar))
return;
sinfo->rx_duration = arsta->rx_duration;
sinfo->filled |= 1ULL << NL80211_STA_INFO_RX_DURATION;
if (!arsta->txrate.legacy && !arsta->txrate.nss)
return;
if (arsta->txrate.legacy) {
sinfo->txrate.legacy = arsta->txrate.legacy;
} else {
sinfo->txrate.mcs = arsta->txrate.mcs;
sinfo->txrate.nss = arsta->txrate.nss;
sinfo->txrate.bw = arsta->txrate.bw;
}
sinfo->txrate.flags = arsta->txrate.flags;
sinfo->filled |= 1ULL << NL80211_STA_INFO_TX_BITRATE;
}
static ssize_t ath10k_dbg_sta_read_aggr_mode(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)

View file

@ -1497,6 +1497,23 @@ struct htt_peer_tx_stats {
u8 payload[0];
} __packed;
#define ATH10K_10_2_TX_STATS_OFFSET 136
#define PEER_STATS_FOR_NO_OF_PPDUS 4
struct ath10k_10_2_peer_tx_stats {
u8 ratecode[PEER_STATS_FOR_NO_OF_PPDUS];
u8 success_pkts[PEER_STATS_FOR_NO_OF_PPDUS];
__le16 success_bytes[PEER_STATS_FOR_NO_OF_PPDUS];
u8 retry_pkts[PEER_STATS_FOR_NO_OF_PPDUS];
__le16 retry_bytes[PEER_STATS_FOR_NO_OF_PPDUS];
u8 failed_pkts[PEER_STATS_FOR_NO_OF_PPDUS];
__le16 failed_bytes[PEER_STATS_FOR_NO_OF_PPDUS];
u8 flags[PEER_STATS_FOR_NO_OF_PPDUS];
__le32 tx_duration;
u8 tx_ppdu_cnt;
u8 peer_id;
} __packed;
union htt_rx_pn_t {
/* WEP: 24-bit PN */
u32 pn24;
@ -1695,7 +1712,7 @@ struct ath10k_htt {
/* This is used to group tx/rx completions separately and process them
* in batches to reduce cache stalls
*/
struct sk_buff_head rx_compl_q;
struct sk_buff_head rx_msdus_q;
struct sk_buff_head rx_in_ord_compl_q;
struct sk_buff_head tx_fetch_ind_q;

View file

@ -227,7 +227,7 @@ void ath10k_htt_rx_free(struct ath10k_htt *htt)
{
del_timer_sync(&htt->rx_ring.refill_retry_timer);
skb_queue_purge(&htt->rx_compl_q);
skb_queue_purge(&htt->rx_msdus_q);
skb_queue_purge(&htt->rx_in_ord_compl_q);
skb_queue_purge(&htt->tx_fetch_ind_q);
@ -515,7 +515,7 @@ int ath10k_htt_rx_alloc(struct ath10k_htt *htt)
htt->rx_ring.sw_rd_idx.msdu_payld = 0;
hash_init(htt->rx_ring.skb_table);
skb_queue_head_init(&htt->rx_compl_q);
skb_queue_head_init(&htt->rx_msdus_q);
skb_queue_head_init(&htt->rx_in_ord_compl_q);
skb_queue_head_init(&htt->tx_fetch_ind_q);
atomic_set(&htt->num_mpdus_ready, 0);
@ -974,16 +974,25 @@ static char *ath10k_get_tid(struct ieee80211_hdr *hdr, char *out, size_t size)
return out;
}
static void ath10k_process_rx(struct ath10k *ar,
struct ieee80211_rx_status *rx_status,
struct sk_buff *skb)
static void ath10k_htt_rx_h_queue_msdu(struct ath10k *ar,
struct ieee80211_rx_status *rx_status,
struct sk_buff *skb)
{
struct ieee80211_rx_status *status;
status = IEEE80211_SKB_RXCB(skb);
*status = *rx_status;
__skb_queue_tail(&ar->htt.rx_msdus_q, skb);
}
static void ath10k_process_rx(struct ath10k *ar, struct sk_buff *skb)
{
struct ieee80211_rx_status *status;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
char tid[32];
status = IEEE80211_SKB_RXCB(skb);
*status = *rx_status;
ath10k_dbg(ar, ATH10K_DBG_DATA,
"rx skb %pK len %u peer %pM %s %s sn %u %s%s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n",
@ -1517,7 +1526,7 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
}
}
static void ath10k_htt_rx_h_deliver(struct ath10k *ar,
static void ath10k_htt_rx_h_enqueue(struct ath10k *ar,
struct sk_buff_head *amsdu,
struct ieee80211_rx_status *status)
{
@ -1540,7 +1549,7 @@ static void ath10k_htt_rx_h_deliver(struct ath10k *ar,
status->flag |= RX_FLAG_ALLOW_SAME_PN;
}
ath10k_process_rx(ar, status, msdu);
ath10k_htt_rx_h_queue_msdu(ar, status, msdu);
}
}
@ -1652,7 +1661,7 @@ static int ath10k_htt_rx_handle_amsdu(struct ath10k_htt *htt)
struct ath10k *ar = htt->ar;
struct ieee80211_rx_status *rx_status = &htt->rx_status;
struct sk_buff_head amsdu;
int ret, num_msdus;
int ret;
__skb_queue_head_init(&amsdu);
@ -1674,7 +1683,6 @@ static int ath10k_htt_rx_handle_amsdu(struct ath10k_htt *htt)
return ret;
}
num_msdus = skb_queue_len(&amsdu);
ath10k_htt_rx_h_ppdu(ar, &amsdu, rx_status, 0xffff);
/* only for ret = 1 indicates chained msdus */
@ -1683,9 +1691,9 @@ static int ath10k_htt_rx_handle_amsdu(struct ath10k_htt *htt)
ath10k_htt_rx_h_filter(ar, &amsdu, rx_status);
ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status, true);
ath10k_htt_rx_h_deliver(ar, &amsdu, rx_status);
ath10k_htt_rx_h_enqueue(ar, &amsdu, rx_status);
return num_msdus;
return 0;
}
static void ath10k_htt_rx_proc_rx_ind(struct ath10k_htt *htt,
@ -1893,15 +1901,14 @@ static void ath10k_htt_rx_h_rx_offload_prot(struct ieee80211_rx_status *status,
RX_FLAG_MMIC_STRIPPED;
}
static int ath10k_htt_rx_h_rx_offload(struct ath10k *ar,
struct sk_buff_head *list)
static void ath10k_htt_rx_h_rx_offload(struct ath10k *ar,
struct sk_buff_head *list)
{
struct ath10k_htt *htt = &ar->htt;
struct ieee80211_rx_status *status = &htt->rx_status;
struct htt_rx_offload_msdu *rx;
struct sk_buff *msdu;
size_t offset;
int num_msdu = 0;
while ((msdu = __skb_dequeue(list))) {
/* Offloaded frames don't have Rx descriptor. Instead they have
@ -1940,10 +1947,8 @@ static int ath10k_htt_rx_h_rx_offload(struct ath10k *ar,
ath10k_htt_rx_h_rx_offload_prot(status, msdu);
ath10k_htt_rx_h_channel(ar, status, NULL, rx->vdev_id);
ath10k_process_rx(ar, status, msdu);
num_msdu++;
ath10k_htt_rx_h_queue_msdu(ar, status, msdu);
}
return num_msdu;
}
static int ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb)
@ -1959,7 +1964,7 @@ static int ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb)
u8 tid;
bool offload;
bool frag;
int ret, num_msdus = 0;
int ret;
lockdep_assert_held(&htt->rx_ring.lock);
@ -2001,7 +2006,7 @@ static int ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb)
* separately.
*/
if (offload)
num_msdus = ath10k_htt_rx_h_rx_offload(ar, &list);
ath10k_htt_rx_h_rx_offload(ar, &list);
while (!skb_queue_empty(&list)) {
__skb_queue_head_init(&amsdu);
@ -2014,11 +2019,10 @@ static int ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb)
* better to report something than nothing though. This
* should still give an idea about rx rate to the user.
*/
num_msdus += skb_queue_len(&amsdu);
ath10k_htt_rx_h_ppdu(ar, &amsdu, status, vdev_id);
ath10k_htt_rx_h_filter(ar, &amsdu, status);
ath10k_htt_rx_h_mpdu(ar, &amsdu, status, false);
ath10k_htt_rx_h_deliver(ar, &amsdu, status);
ath10k_htt_rx_h_enqueue(ar, &amsdu, status);
break;
case -EAGAIN:
/* fall through */
@ -2030,7 +2034,7 @@ static int ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb)
return -EIO;
}
}
return num_msdus;
return ret;
}
static void ath10k_htt_rx_tx_fetch_resp_id_confirm(struct ath10k *ar,
@ -2449,6 +2453,62 @@ static void ath10k_htt_fetch_peer_stats(struct ath10k *ar,
rcu_read_unlock();
}
static void ath10k_fetch_10_2_tx_stats(struct ath10k *ar, u8 *data)
{
struct ath10k_pktlog_hdr *hdr = (struct ath10k_pktlog_hdr *)data;
struct ath10k_per_peer_tx_stats *p_tx_stats = &ar->peer_tx_stats;
struct ath10k_10_2_peer_tx_stats *tx_stats;
struct ieee80211_sta *sta;
struct ath10k_peer *peer;
u16 log_type = __le16_to_cpu(hdr->log_type);
u32 peer_id = 0, i;
if (log_type != ATH_PKTLOG_TYPE_TX_STAT)
return;
tx_stats = (struct ath10k_10_2_peer_tx_stats *)((hdr->payload) +
ATH10K_10_2_TX_STATS_OFFSET);
if (!tx_stats->tx_ppdu_cnt)
return;
peer_id = tx_stats->peer_id;
rcu_read_lock();
spin_lock_bh(&ar->data_lock);
peer = ath10k_peer_find_by_id(ar, peer_id);
if (!peer) {
ath10k_warn(ar, "Invalid peer id %d in peer stats buffer\n",
peer_id);
goto out;
}
sta = peer->sta;
for (i = 0; i < tx_stats->tx_ppdu_cnt; i++) {
p_tx_stats->succ_bytes =
__le16_to_cpu(tx_stats->success_bytes[i]);
p_tx_stats->retry_bytes =
__le16_to_cpu(tx_stats->retry_bytes[i]);
p_tx_stats->failed_bytes =
__le16_to_cpu(tx_stats->failed_bytes[i]);
p_tx_stats->ratecode = tx_stats->ratecode[i];
p_tx_stats->flags = tx_stats->flags[i];
p_tx_stats->succ_pkts = tx_stats->success_pkts[i];
p_tx_stats->retry_pkts = tx_stats->retry_pkts[i];
p_tx_stats->failed_pkts = tx_stats->failed_pkts[i];
ath10k_update_per_peer_tx_stats(ar, sta, p_tx_stats);
}
spin_unlock_bh(&ar->data_lock);
rcu_read_unlock();
return;
out:
spin_unlock_bh(&ar->data_lock);
rcu_read_unlock();
}
bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
{
struct ath10k_htt *htt = &ar->htt;
@ -2566,6 +2626,10 @@ bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
skb->len -
offsetof(struct htt_resp,
pktlog_msg.payload));
if (ath10k_peer_stats_enabled(ar))
ath10k_fetch_10_2_tx_stats(ar,
resp->pktlog_msg.payload);
break;
}
case HTT_T2H_MSG_TYPE_RX_FLUSH: {
@ -2631,6 +2695,24 @@ void ath10k_htt_rx_pktlog_completion_handler(struct ath10k *ar,
}
EXPORT_SYMBOL(ath10k_htt_rx_pktlog_completion_handler);
static int ath10k_htt_rx_deliver_msdu(struct ath10k *ar, int quota, int budget)
{
struct sk_buff *skb;
while (quota < budget) {
if (skb_queue_empty(&ar->htt.rx_msdus_q))
break;
skb = __skb_dequeue(&ar->htt.rx_msdus_q);
if (!skb)
break;
ath10k_process_rx(ar, skb);
quota++;
}
return quota;
}
int ath10k_htt_txrx_compl_task(struct ath10k *ar, int budget)
{
struct ath10k_htt *htt = &ar->htt;
@ -2638,63 +2720,44 @@ int ath10k_htt_txrx_compl_task(struct ath10k *ar, int budget)
struct sk_buff_head tx_ind_q;
struct sk_buff *skb;
unsigned long flags;
int quota = 0, done, num_rx_msdus;
int quota = 0, done, ret;
bool resched_napi = false;
__skb_queue_head_init(&tx_ind_q);
/* Since in-ord-ind can deliver more than 1 A-MSDU in single event,
* process it first to utilize full available quota.
/* Process pending frames before dequeuing more data
* from hardware.
*/
while (quota < budget) {
if (skb_queue_empty(&htt->rx_in_ord_compl_q))
break;
skb = __skb_dequeue(&htt->rx_in_ord_compl_q);
if (!skb) {
resched_napi = true;
goto exit;
}
quota = ath10k_htt_rx_deliver_msdu(ar, quota, budget);
if (quota == budget) {
resched_napi = true;
goto exit;
}
while ((skb = __skb_dequeue(&htt->rx_in_ord_compl_q))) {
spin_lock_bh(&htt->rx_ring.lock);
num_rx_msdus = ath10k_htt_rx_in_ord_ind(ar, skb);
ret = ath10k_htt_rx_in_ord_ind(ar, skb);
spin_unlock_bh(&htt->rx_ring.lock);
if (num_rx_msdus < 0) {
resched_napi = true;
goto exit;
}
dev_kfree_skb_any(skb);
if (num_rx_msdus > 0)
quota += num_rx_msdus;
if ((quota > ATH10K_NAPI_QUOTA_LIMIT) &&
!skb_queue_empty(&htt->rx_in_ord_compl_q)) {
if (ret == -EIO) {
resched_napi = true;
goto exit;
}
}
while (quota < budget) {
/* no more data to receive */
if (!atomic_read(&htt->num_mpdus_ready))
break;
num_rx_msdus = ath10k_htt_rx_handle_amsdu(htt);
if (num_rx_msdus < 0) {
while (atomic_read(&htt->num_mpdus_ready)) {
ret = ath10k_htt_rx_handle_amsdu(htt);
if (ret == -EIO) {
resched_napi = true;
goto exit;
}
quota += num_rx_msdus;
atomic_dec(&htt->num_mpdus_ready);
if ((quota > ATH10K_NAPI_QUOTA_LIMIT) &&
atomic_read(&htt->num_mpdus_ready)) {
resched_napi = true;
goto exit;
}
}
/* Deliver received data after processing data from hardware */
quota = ath10k_htt_rx_deliver_msdu(ar, quota, budget);
/* From NAPI documentation:
* The napi poll() function may also process TX completions, in which
* case if it processes the entire TX ring then it should count that

View file

@ -931,3 +931,5 @@ const struct ath10k_hw_ops qca6174_ops = {
.set_coverage_class = ath10k_hw_qca988x_set_coverage_class,
.enable_pll_clk = ath10k_hw_qca6174_enable_pll_clock,
};
const struct ath10k_hw_ops wcn3990_ops = {};

View file

@ -128,6 +128,10 @@ enum qca9377_chip_id_rev {
#define QCA4019_HW_1_0_BOARD_DATA_FILE "board.bin"
#define QCA4019_HW_1_0_PATCH_LOAD_ADDR 0x1234
/* WCN3990 1.0 definitions */
#define WCN3990_HW_1_0_DEV_VERSION ATH10K_HW_WCN3990
#define WCN3990_HW_1_0_FW_DIR ATH10K_FW_DIR "/WCN3990/hw3.0"
#define ATH10K_FW_FILE_BASE "firmware"
#define ATH10K_FW_API_MAX 6
#define ATH10K_FW_API_MIN 2
@ -553,6 +557,10 @@ struct ath10k_hw_params {
/* Number of ciphers supported (i.e First N) in cipher_suites array */
int n_cipher_suites;
u32 num_peers;
u32 ast_skid_limit;
u32 num_wds_entries;
};
struct htt_rx_desc;
@ -567,6 +575,7 @@ struct ath10k_hw_ops {
extern const struct ath10k_hw_ops qca988x_ops;
extern const struct ath10k_hw_ops qca99x0_ops;
extern const struct ath10k_hw_ops qca6174_ops;
extern const struct ath10k_hw_ops wcn3990_ops;
extern const struct ath10k_hw_clk_params qca6174_clk[];
@ -663,6 +672,11 @@ ath10k_rx_desc_get_l3_pad_bytes(struct ath10k_hw_params *hw,
#define TARGET_TLV_NUM_MSDU_DESC (1024 + 32)
#define TARGET_TLV_NUM_WOW_PATTERNS 22
/* Target specific defines for WMI-HL-1.0 firmware */
#define TARGET_HL_10_TLV_NUM_PEERS 14
#define TARGET_HL_10_TLV_AST_SKID_LIMIT 6
#define TARGET_HL_10_TLV_NUM_WDS_ENTRIES 2
/* Diagnostic Window */
#define CE_DIAG_PIPE 7

View file

@ -2563,7 +2563,7 @@ static void ath10k_peer_assoc_h_qos(struct ath10k *ar,
}
break;
case WMI_VDEV_TYPE_STA:
if (vif->bss_conf.qos)
if (sta->wme)
arg->peer_flags |= arvif->ar->wmi.peer_flags->qos;
break;
case WMI_VDEV_TYPE_IBSS:
@ -3574,7 +3574,9 @@ ath10k_mac_tx_h_get_txpath(struct ath10k *ar,
return ATH10K_MAC_TX_HTT;
case ATH10K_HW_TXRX_MGMT:
if (test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX,
ar->running_fw->fw_file.fw_features))
ar->running_fw->fw_file.fw_features) ||
test_bit(WMI_SERVICE_MGMT_TX_WMI,
ar->wmi.svc_map))
return ATH10K_MAC_TX_WMI_MGMT;
else if (ar->htt.target_version_major >= 3)
return ATH10K_MAC_TX_HTT;
@ -6201,6 +6203,16 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
"mac vdev %d peer delete %pM sta %pK (sta gone)\n",
arvif->vdev_id, sta->addr, sta);
if (sta->tdls) {
ret = ath10k_mac_tdls_peer_update(ar, arvif->vdev_id,
sta,
WMI_TDLS_PEER_STATE_TEARDOWN);
if (ret)
ath10k_warn(ar, "failed to update tdls peer state for %pM state %d: %i\n",
sta->addr,
WMI_TDLS_PEER_STATE_TEARDOWN, ret);
}
ret = ath10k_peer_delete(ar, arvif->vdev_id, sta->addr);
if (ret)
ath10k_warn(ar, "failed to delete peer %pM for vdev %d: %i\n",
@ -7536,6 +7548,16 @@ ath10k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw,
arvif->vdev_id, ret);
}
if (ath10k_peer_stats_enabled(ar)) {
ar->pktlog_filter |= ATH10K_PKTLOG_PEER_STATS;
ret = ath10k_wmi_pdev_pktlog_enable(ar,
ar->pktlog_filter);
if (ret) {
ath10k_warn(ar, "failed to enable pktlog %d\n", ret);
goto err_stop;
}
}
mutex_unlock(&ar->conf_mutex);
return 0;
@ -7620,6 +7642,34 @@ static void ath10k_mac_op_sta_pre_rcu_remove(struct ieee80211_hw *hw,
peer->removed = true;
}
static void ath10k_sta_statistics(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct station_info *sinfo)
{
struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
struct ath10k *ar = arsta->arvif->ar;
if (!ath10k_peer_stats_enabled(ar))
return;
sinfo->rx_duration = arsta->rx_duration;
sinfo->filled |= 1ULL << NL80211_STA_INFO_RX_DURATION;
if (!arsta->txrate.legacy && !arsta->txrate.nss)
return;
if (arsta->txrate.legacy) {
sinfo->txrate.legacy = arsta->txrate.legacy;
} else {
sinfo->txrate.mcs = arsta->txrate.mcs;
sinfo->txrate.nss = arsta->txrate.nss;
sinfo->txrate.bw = arsta->txrate.bw;
}
sinfo->txrate.flags = arsta->txrate.flags;
sinfo->filled |= 1ULL << NL80211_STA_INFO_TX_BITRATE;
}
static const struct ieee80211_ops ath10k_ops = {
.tx = ath10k_mac_op_tx,
.wake_tx_queue = ath10k_mac_op_wake_tx_queue,
@ -7661,6 +7711,7 @@ static const struct ieee80211_ops ath10k_ops = {
.unassign_vif_chanctx = ath10k_mac_op_unassign_vif_chanctx,
.switch_vif_chanctx = ath10k_mac_op_switch_vif_chanctx,
.sta_pre_rcu_remove = ath10k_mac_op_sta_pre_rcu_remove,
.sta_statistics = ath10k_sta_statistics,
CFG80211_TESTMODE_CMD(ath10k_tm_cmd)
@ -7671,7 +7722,6 @@ static const struct ieee80211_ops ath10k_ops = {
#endif
#ifdef CONFIG_MAC80211_DEBUGFS
.sta_add_debugfs = ath10k_sta_add_debugfs,
.sta_statistics = ath10k_sta_statistics,
#endif
};
@ -8329,15 +8379,6 @@ int ath10k_mac_register(struct ath10k *ar)
ath10k_warn(ar, "failed to initialise DFS pattern detector\n");
}
/* Current wake_tx_queue implementation imposes a significant
* performance penalty in some setups. The tx scheduling code needs
* more work anyway so disable the wake_tx_queue unless firmware
* supports the pull-push mechanism.
*/
if (!test_bit(ATH10K_FW_FEATURE_PEER_FLOW_CONTROL,
ar->running_fw->fw_file.fw_features))
ar->ops->wake_tx_queue = NULL;
ret = ath10k_mac_init_rd(ar);
if (ret) {
ath10k_err(ar, "failed to derive regdom: %d\n", ret);

View file

@ -44,7 +44,7 @@ enum ath10k_spectral_mode {
SPECTRAL_MANUAL,
};
#ifdef CONFIG_ATH10K_DEBUGFS
#ifdef CONFIG_ATH10K_SPECTRAL
int ath10k_spectral_process_fft(struct ath10k *ar,
struct wmi_phyerr_ev_arg *phyerr,
@ -85,6 +85,6 @@ static inline void ath10k_spectral_destroy(struct ath10k *ar)
{
}
#endif /* CONFIG_ATH10K_DEBUGFS */
#endif /* CONFIG_ATH10K_SPECTRAL */
#endif /* SPECTRAL_H */

View file

@ -377,6 +377,7 @@ ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu)
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(msdu);
struct sk_buff *skb;
int ret;
u32 mgmt_tx_cmdid;
if (!ar->wmi.ops->gen_mgmt_tx)
return -EOPNOTSUPP;
@ -385,7 +386,13 @@ ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu)
if (IS_ERR(skb))
return PTR_ERR(skb);
ret = ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->mgmt_tx_cmdid);
if (test_bit(ATH10K_FW_FEATURE_MGMT_TX_BY_REF,
ar->running_fw->fw_file.fw_features))
mgmt_tx_cmdid = ar->wmi.cmd->mgmt_tx_send_cmdid;
else
mgmt_tx_cmdid = ar->wmi.cmd->mgmt_tx_cmdid;
ret = ath10k_wmi_cmd_send(ar, skb, mgmt_tx_cmdid);
if (ret)
return ret;

View file

@ -917,33 +917,69 @@ ath10k_wmi_tlv_parse_mem_reqs(struct ath10k *ar, u16 tag, u16 len,
return -ENOMEM;
}
static int ath10k_wmi_tlv_op_pull_svc_rdy_ev(struct ath10k *ar,
struct sk_buff *skb,
struct wmi_svc_rdy_ev_arg *arg)
{
const void **tb;
struct wmi_tlv_svc_rdy_parse {
const struct hal_reg_capabilities *reg;
const struct wmi_tlv_svc_rdy_ev *ev;
const __le32 *svc_bmap;
const struct wlan_host_mem_req *mem_reqs;
bool svc_bmap_done;
bool dbs_hw_mode_done;
};
static int ath10k_wmi_tlv_svc_rdy_parse(struct ath10k *ar, u16 tag, u16 len,
const void *ptr, void *data)
{
struct wmi_tlv_svc_rdy_parse *svc_rdy = data;
switch (tag) {
case WMI_TLV_TAG_STRUCT_SERVICE_READY_EVENT:
svc_rdy->ev = ptr;
break;
case WMI_TLV_TAG_STRUCT_HAL_REG_CAPABILITIES:
svc_rdy->reg = ptr;
break;
case WMI_TLV_TAG_ARRAY_STRUCT:
svc_rdy->mem_reqs = ptr;
break;
case WMI_TLV_TAG_ARRAY_UINT32:
if (!svc_rdy->svc_bmap_done) {
svc_rdy->svc_bmap_done = true;
svc_rdy->svc_bmap = ptr;
} else if (!svc_rdy->dbs_hw_mode_done) {
svc_rdy->dbs_hw_mode_done = true;
}
break;
default:
break;
}
return 0;
}
static int ath10k_wmi_tlv_op_pull_svc_rdy_ev(struct ath10k *ar,
struct sk_buff *skb,
struct wmi_svc_rdy_ev_arg *arg)
{
const struct hal_reg_capabilities *reg;
const struct wmi_tlv_svc_rdy_ev *ev;
const __le32 *svc_bmap;
const struct wlan_host_mem_req *mem_reqs;
struct wmi_tlv_svc_rdy_parse svc_rdy = { };
int ret;
tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ret = ath10k_wmi_tlv_iter(ar, skb->data, skb->len,
ath10k_wmi_tlv_svc_rdy_parse, &svc_rdy);
if (ret) {
ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
return ret;
}
ev = tb[WMI_TLV_TAG_STRUCT_SERVICE_READY_EVENT];
reg = tb[WMI_TLV_TAG_STRUCT_HAL_REG_CAPABILITIES];
svc_bmap = tb[WMI_TLV_TAG_ARRAY_UINT32];
mem_reqs = tb[WMI_TLV_TAG_ARRAY_STRUCT];
ev = svc_rdy.ev;
reg = svc_rdy.reg;
svc_bmap = svc_rdy.svc_bmap;
mem_reqs = svc_rdy.mem_reqs;
if (!ev || !reg || !svc_bmap || !mem_reqs) {
kfree(tb);
if (!ev || !reg || !svc_bmap || !mem_reqs)
return -EPROTO;
}
/* This is an internal ABI compatibility check for WMI TLV so check it
* here instead of the generic WMI code.
@ -961,7 +997,6 @@ static int ath10k_wmi_tlv_op_pull_svc_rdy_ev(struct ath10k *ar,
__le32_to_cpu(ev->abi.abi_ver_ns1) != WMI_TLV_ABI_VER_NS1 ||
__le32_to_cpu(ev->abi.abi_ver_ns2) != WMI_TLV_ABI_VER_NS2 ||
__le32_to_cpu(ev->abi.abi_ver_ns3) != WMI_TLV_ABI_VER_NS3) {
kfree(tb);
return -ENOTSUPP;
}
@ -982,12 +1017,10 @@ static int ath10k_wmi_tlv_op_pull_svc_rdy_ev(struct ath10k *ar,
ret = ath10k_wmi_tlv_iter(ar, mem_reqs, ath10k_wmi_tlv_len(mem_reqs),
ath10k_wmi_tlv_parse_mem_reqs, arg);
if (ret) {
kfree(tb);
ath10k_warn(ar, "failed to parse mem_reqs tlv: %d\n", ret);
return ret;
}
kfree(tb);
return 0;
}
@ -1406,7 +1439,10 @@ static struct sk_buff *ath10k_wmi_tlv_op_gen_init(struct ath10k *ar)
cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks);
cfg->num_vdevs = __cpu_to_le32(TARGET_TLV_NUM_VDEVS);
cfg->num_peers = __cpu_to_le32(TARGET_TLV_NUM_PEERS);
cfg->num_peers = __cpu_to_le32(ar->hw_params.num_peers);
cfg->ast_skid_limit = __cpu_to_le32(ar->hw_params.ast_skid_limit);
cfg->num_wds_entries = __cpu_to_le32(ar->hw_params.num_wds_entries);
if (test_bit(WMI_SERVICE_RX_FULL_REORDER, ar->wmi.svc_map)) {
cfg->num_offload_peers = __cpu_to_le32(TARGET_TLV_NUM_VDEVS);
@ -1418,7 +1454,6 @@ static struct sk_buff *ath10k_wmi_tlv_op_gen_init(struct ath10k *ar)
cfg->num_peer_keys = __cpu_to_le32(2);
cfg->num_tids = __cpu_to_le32(TARGET_TLV_NUM_TIDS);
cfg->ast_skid_limit = __cpu_to_le32(0x10);
cfg->tx_chain_mask = __cpu_to_le32(0x7);
cfg->rx_chain_mask = __cpu_to_le32(0x7);
cfg->rx_timeout_pri[0] = __cpu_to_le32(0x64);
@ -1434,7 +1469,6 @@ static struct sk_buff *ath10k_wmi_tlv_op_gen_init(struct ath10k *ar)
cfg->num_mcast_table_elems = __cpu_to_le32(0);
cfg->mcast2ucast_mode = __cpu_to_le32(0);
cfg->tx_dbg_log_size = __cpu_to_le32(0x400);
cfg->num_wds_entries = __cpu_to_le32(0x20);
cfg->dma_burst_size = __cpu_to_le32(0);
cfg->mac_aggr_delim = __cpu_to_le32(0);
cfg->rx_skip_defrag_timeout_dup_detection_check = __cpu_to_le32(0);
@ -2449,6 +2483,82 @@ ath10k_wmi_tlv_op_gen_request_stats(struct ath10k *ar, u32 stats_mask)
return skb;
}
static struct sk_buff *
ath10k_wmi_tlv_op_gen_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu)
{
struct ath10k_skb_cb *cb = ATH10K_SKB_CB(msdu);
struct wmi_tlv_mgmt_tx_cmd *cmd;
struct wmi_tlv *tlv;
struct ieee80211_hdr *hdr;
struct sk_buff *skb;
void *ptr;
int len;
u32 buf_len = msdu->len;
u16 fc;
struct ath10k_vif *arvif;
dma_addr_t mgmt_frame_dma;
u32 vdev_id;
if (!cb->vif)
return ERR_PTR(-EINVAL);
hdr = (struct ieee80211_hdr *)msdu->data;
fc = le16_to_cpu(hdr->frame_control);
arvif = (void *)cb->vif->drv_priv;
vdev_id = arvif->vdev_id;
if (WARN_ON_ONCE(!ieee80211_is_mgmt(hdr->frame_control)))
return ERR_PTR(-EINVAL);
len = sizeof(*cmd) + 2 * sizeof(*tlv);
if ((ieee80211_is_action(hdr->frame_control) ||
ieee80211_is_deauth(hdr->frame_control) ||
ieee80211_is_disassoc(hdr->frame_control)) &&
ieee80211_has_protected(hdr->frame_control)) {
len += IEEE80211_CCMP_MIC_LEN;
buf_len += IEEE80211_CCMP_MIC_LEN;
}
buf_len = min_t(u32, buf_len, WMI_TLV_MGMT_TX_FRAME_MAX_LEN);
buf_len = round_up(buf_len, 4);
len += buf_len;
len = round_up(len, 4);
skb = ath10k_wmi_alloc_skb(ar, len);
if (!skb)
return ERR_PTR(-ENOMEM);
ptr = (void *)skb->data;
tlv = ptr;
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_MGMT_TX_CMD);
tlv->len = __cpu_to_le16(sizeof(*cmd));
cmd = (void *)tlv->value;
cmd->vdev_id = __cpu_to_le32(vdev_id);
cmd->desc_id = 0;
cmd->chanfreq = 0;
cmd->buf_len = __cpu_to_le32(buf_len);
cmd->frame_len = __cpu_to_le32(msdu->len);
mgmt_frame_dma = dma_map_single(arvif->ar->dev, msdu->data,
msdu->len, DMA_TO_DEVICE);
if (!mgmt_frame_dma)
return ERR_PTR(-ENOMEM);
cmd->paddr = __cpu_to_le64(mgmt_frame_dma);
ptr += sizeof(*tlv);
ptr += sizeof(*cmd);
tlv = ptr;
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_BYTE);
tlv->len = __cpu_to_le16(buf_len);
ptr += sizeof(*tlv);
memcpy(ptr, msdu->data, buf_len);
return skb;
}
static struct sk_buff *
ath10k_wmi_tlv_op_gen_force_fw_hang(struct ath10k *ar,
enum wmi_force_fw_hang_type type,
@ -3258,6 +3368,7 @@ static struct wmi_cmd_map wmi_tlv_cmd_map = {
.bcn_filter_rx_cmdid = WMI_TLV_BCN_FILTER_RX_CMDID,
.prb_req_filter_rx_cmdid = WMI_TLV_PRB_REQ_FILTER_RX_CMDID,
.mgmt_tx_cmdid = WMI_TLV_MGMT_TX_CMDID,
.mgmt_tx_send_cmdid = WMI_TLV_MGMT_TX_SEND_CMD,
.prb_tmpl_cmdid = WMI_TLV_PRB_TMPL_CMDID,
.addba_clear_resp_cmdid = WMI_TLV_ADDBA_CLEAR_RESP_CMDID,
.addba_send_cmdid = WMI_TLV_ADDBA_SEND_CMDID,
@ -3592,6 +3703,7 @@ static const struct wmi_ops wmi_tlv_ops = {
.gen_request_stats = ath10k_wmi_tlv_op_gen_request_stats,
.gen_force_fw_hang = ath10k_wmi_tlv_op_gen_force_fw_hang,
/* .gen_mgmt_tx = not implemented; HTT is used */
.gen_mgmt_tx = ath10k_wmi_tlv_op_gen_mgmt_tx,
.gen_dbglog_cfg = ath10k_wmi_tlv_op_gen_dbglog_cfg,
.gen_pktlog_enable = ath10k_wmi_tlv_op_gen_pktlog_enable,
.gen_pktlog_disable = ath10k_wmi_tlv_op_gen_pktlog_disable,

View file

@ -22,6 +22,7 @@
#define WMI_TLV_CMD_UNSUPPORTED 0
#define WMI_TLV_PDEV_PARAM_UNSUPPORTED 0
#define WMI_TLV_VDEV_PARAM_UNSUPPORTED 0
#define WMI_TLV_MGMT_TX_FRAME_MAX_LEN 64
enum wmi_tlv_grp_id {
WMI_TLV_GRP_START = 0x3,
@ -132,6 +133,7 @@ enum wmi_tlv_cmd_id {
WMI_TLV_PRB_REQ_FILTER_RX_CMDID,
WMI_TLV_MGMT_TX_CMDID,
WMI_TLV_PRB_TMPL_CMDID,
WMI_TLV_MGMT_TX_SEND_CMD,
WMI_TLV_ADDBA_CLEAR_RESP_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_BA_NEG),
WMI_TLV_ADDBA_SEND_CMDID,
WMI_TLV_ADDBA_STATUS_CMDID,
@ -890,6 +892,63 @@ enum wmi_tlv_tag {
WMI_TLV_TAG_STRUCT_SAP_OFL_DEL_STA_EVENT,
WMI_TLV_TAG_STRUCT_APFIND_CMD_PARAM,
WMI_TLV_TAG_STRUCT_APFIND_EVENT_HDR,
WMI_TLV_TAG_STRUCT_OCB_SET_SCHED_CMD,
WMI_TLV_TAG_STRUCT_OCB_SET_SCHED_EVENT,
WMI_TLV_TAG_STRUCT_OCB_SET_CONFIG_CMD,
WMI_TLV_TAG_STRUCT_OCB_SET_CONFIG_RESP_EVENT,
WMI_TLV_TAG_STRUCT_OCB_SET_UTC_TIME_CMD,
WMI_TLV_TAG_STRUCT_OCB_START_TIMING_ADVERT_CMD,
WMI_TLV_TAG_STRUCT_OCB_STOP_TIMING_ADVERT_CMD,
WMI_TLV_TAG_STRUCT_OCB_GET_TSF_TIMER_CMD,
WMI_TLV_TAG_STRUCT_OCB_GET_TSF_TIMER_RESP_EVENT,
WMI_TLV_TAG_STRUCT_DCC_GET_STATS_CMD,
WMI_TLV_TAG_STRUCT_DCC_CHANNEL_STATS_REQUEST,
WMI_TLV_TAG_STRUCT_DCC_GET_STATS_RESP_EVENT,
WMI_TLV_TAG_STRUCT_DCC_CLEAR_STATS_CMD,
WMI_TLV_TAG_STRUCT_DCC_UPDATE_NDL_CMD,
WMI_TLV_TAG_STRUCT_DCC_UPDATE_NDL_RESP_EVENT,
WMI_TLV_TAG_STRUCT_DCC_STATS_EVENT,
WMI_TLV_TAG_STRUCT_OCB_CHANNEL,
WMI_TLV_TAG_STRUCT_OCB_SCHEDULE_ELEMENT,
WMI_TLV_TAG_STRUCT_DCC_NDL_STATS_PER_CHANNEL,
WMI_TLV_TAG_STRUCT_DCC_NDL_CHAN,
WMI_TLV_TAG_STRUCT_QOS_PARAMETER,
WMI_TLV_TAG_STRUCT_DCC_NDL_ACTIVE_STATE_CONFIG,
WMI_TLV_TAG_STRUCT_ROAM_SCAN_EXTENDED_THRESHOLD_PARAM,
WMI_TLV_TAG_STRUCT_ROAM_FILTER_FIXED_PARAM,
WMI_TLV_TAG_STRUCT_PASSPOINT_CONFIG_CMD,
WMI_TLV_TAG_STRUCT_PASSPOINT_EVENT_HDR,
WMI_TLV_TAG_STRUCT_EXTSCAN_CONFIGURE_HOTLIST_SSID_MONITOR_CMD,
WMI_TLV_TAG_STRUCT_EXTSCAN_HOTLIST_SSID_MATCH_EVENT,
WMI_TLV_TAG_STRUCT_VDEV_TSF_TSTAMP_ACTION_CMD,
WMI_TLV_TAG_STRUCT_VDEV_TSF_REPORT_EVENT,
WMI_TLV_TAG_STRUCT_GET_FW_MEM_DUMP,
WMI_TLV_TAG_STRUCT_UPDATE_FW_MEM_DUMP,
WMI_TLV_TAG_STRUCT_FW_MEM_DUMP_PARAMS,
WMI_TLV_TAG_STRUCT_DEBUG_MESG_FLUSH,
WMI_TLV_TAG_STRUCT_DEBUG_MESG_FLUSH_COMPLETE,
WMI_TLV_TAG_STRUCT_PEER_SET_RATE_REPORT_CONDITION,
WMI_TLV_TAG_STRUCT_ROAM_SUBNET_CHANGE_CONFIG,
WMI_TLV_TAG_STRUCT_VDEV_SET_IE_CMD,
WMI_TLV_TAG_STRUCT_RSSI_BREACH_MONITOR_CONFIG,
WMI_TLV_TAG_STRUCT_RSSI_BREACH_EVENT,
WMI_TLV_TAG_STRUCT_EVENT_INITIAL_WAKEUP,
WMI_TLV_TAG_STRUCT_SOC_SET_PCL_CMD,
WMI_TLV_TAG_STRUCT_SOC_SET_HW_MODE_CMD,
WMI_TLV_TAG_STRUCT_SOC_SET_HW_MODE_RESPONSE_EVENT,
WMI_TLV_TAG_STRUCT_SOC_HW_MODE_TRANSITION_EVENT,
WMI_TLV_TAG_STRUCT_VDEV_TXRX_STREAMS,
WMI_TLV_TAG_STRUCT_SOC_SET_HW_MODE_RESPONSE_VDEV_MAC_ENTRY,
WMI_TLV_TAG_STRUCT_SOC_SET_DUAL_MAC_CONFIG_CMD,
WMI_TLV_TAG_STRUCT_SOC_SET_DUAL_MAC_CONFIG_RESPONSE_EVENT,
WMI_TLV_TAG_STRUCT_IOAC_SOCK_PATTERN_T,
WMI_TLV_TAG_STRUCT_WOW_ENABLE_ICMPV6_NA_FLT_CMD,
WMI_TLV_TAG_STRUCT_DIAG_EVENT_LOG_CONFIG,
WMI_TLV_TAG_STRUCT_DIAG_EVENT_LOG_SUPPORTED_EVENT,
WMI_TLV_TAG_STRUCT_PACKET_FILTER_CONFIG,
WMI_TLV_TAG_STRUCT_PACKET_FILTER_ENABLE,
WMI_TLV_TAG_STRUCT_SAP_SET_BLACKLIST_PARAM_CMD,
WMI_TLV_TAG_STRUCT_MGMT_TX_CMD,
WMI_TLV_TAG_MAX
};
@ -965,6 +1024,50 @@ enum wmi_tlv_service {
WMI_TLV_SERVICE_STA_RX_IPA_OFFLOAD_SUPPORT,
WMI_TLV_SERVICE_MDNS_OFFLOAD,
WMI_TLV_SERVICE_SAP_AUTH_OFFLOAD,
WMI_TLV_SERVICE_DUAL_BAND_SIMULTANEOUS_SUPPORT,
WMI_TLV_SERVICE_OCB,
WMI_TLV_SERVICE_AP_ARPNS_OFFLOAD,
WMI_TLV_SERVICE_PER_BAND_CHAINMASK_SUPPORT,
WMI_TLV_SERVICE_PACKET_FILTER_OFFLOAD,
WMI_TLV_SERVICE_MGMT_TX_HTT,
WMI_TLV_SERVICE_MGMT_TX_WMI,
WMI_TLV_SERVICE_EXT_MSG,
WMI_TLV_SERVICE_MAWC,
WMI_TLV_SERVICE_PEER_ASSOC_CONF,
WMI_TLV_SERVICE_EGAP,
WMI_TLV_SERVICE_STA_PMF_OFFLOAD,
WMI_TLV_SERVICE_UNIFIED_WOW_CAPABILITY,
WMI_TLV_SERVICE_ENHANCED_PROXY_STA,
WMI_TLV_SERVICE_ATF,
WMI_TLV_SERVICE_COEX_GPIO,
WMI_TLV_SERVICE_AUX_SPECTRAL_INTF,
WMI_TLV_SERVICE_AUX_CHAN_LOAD_INTF,
WMI_TLV_SERVICE_BSS_CHANNEL_INFO_64,
WMI_TLV_SERVICE_ENTERPRISE_MESH,
WMI_TLV_SERVICE_RESTRT_CHNL_SUPPORT,
WMI_TLV_SERVICE_BPF_OFFLOAD,
WMI_TLV_SERVICE_SYNC_DELETE_CMDS,
WMI_TLV_SERVICE_SMART_ANTENNA_SW_SUPPORT,
WMI_TLV_SERVICE_SMART_ANTENNA_HW_SUPPORT,
WMI_TLV_SERVICE_RATECTRL_LIMIT_MAX_MIN_RATES,
WMI_TLV_SERVICE_NAN_DATA,
WMI_TLV_SERVICE_NAN_RTT,
WMI_TLV_SERVICE_11AX,
WMI_TLV_SERVICE_DEPRECATED_REPLACE,
WMI_TLV_SERVICE_TDLS_CONN_TRACKER_IN_HOST_MODE,
WMI_TLV_SERVICE_ENHANCED_MCAST_FILTER,
WMI_TLV_SERVICE_PERIODIC_CHAN_STAT_SUPPORT,
WMI_TLV_SERVICE_MESH_11S,
WMI_TLV_SERVICE_HALF_RATE_QUARTER_RATE_SUPPORT,
WMI_TLV_SERVICE_VDEV_RX_FILTER,
WMI_TLV_SERVICE_P2P_LISTEN_OFFLOAD_SUPPORT,
WMI_TLV_SERVICE_MARK_FIRST_WAKEUP_PACKET,
WMI_TLV_SERVICE_MULTIPLE_MCAST_FILTER_SET,
WMI_TLV_SERVICE_HOST_MANAGED_RX_REORDER,
WMI_TLV_SERVICE_FLASH_RDWR_SUPPORT,
WMI_TLV_SERVICE_WLAN_STATS_REPORT,
WMI_TLV_SERVICE_TX_MSDU_ID_NEW_PARTITION_SUPPORT,
WMI_TLV_SERVICE_DFS_PHYERR_OFFLOAD,
};
#define WMI_SERVICE_IS_ENABLED(wmi_svc_bmap, svc_id, len) \
@ -1121,6 +1224,8 @@ wmi_tlv_svc_map(const __le32 *in, unsigned long *out, size_t len)
WMI_SERVICE_MDNS_OFFLOAD, len);
SVCMAP(WMI_TLV_SERVICE_SAP_AUTH_OFFLOAD,
WMI_SERVICE_SAP_AUTH_OFFLOAD, len);
SVCMAP(WMI_TLV_SERVICE_MGMT_TX_WMI,
WMI_SERVICE_MGMT_TX_WMI, len);
}
#undef SVCMAP
@ -1643,4 +1748,12 @@ struct wmi_tlv_tx_pause_ev {
void ath10k_wmi_tlv_attach(struct ath10k *ar);
struct wmi_tlv_mgmt_tx_cmd {
__le32 vdev_id;
__le32 desc_id;
__le32 chanfreq;
__le64 paddr;
__le32 frame_len;
__le32 buf_len;
} __packed;
#endif

View file

@ -29,6 +29,7 @@
#include "p2p.h"
#include "hw.h"
#include "hif.h"
#include "txrx.h"
#define ATH10K_WMI_BARRIER_ECHO_ID 0xBA991E9
#define ATH10K_WMI_BARRIER_TIMEOUT_HZ (3 * HZ)
@ -4456,6 +4457,74 @@ void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, struct sk_buff *skb)
__le32_to_cpu(ev->rate_max));
}
static void
ath10k_wmi_handle_tdls_peer_event(struct ath10k *ar, struct sk_buff *skb)
{
struct wmi_tdls_peer_event *ev;
struct ath10k_peer *peer;
struct ath10k_vif *arvif;
int vdev_id;
int peer_status;
int peer_reason;
u8 reason;
if (skb->len < sizeof(*ev)) {
ath10k_err(ar, "received tdls peer event with invalid size (%d bytes)\n",
skb->len);
return;
}
ev = (struct wmi_tdls_peer_event *)skb->data;
vdev_id = __le32_to_cpu(ev->vdev_id);
peer_status = __le32_to_cpu(ev->peer_status);
peer_reason = __le32_to_cpu(ev->peer_reason);
spin_lock_bh(&ar->data_lock);
peer = ath10k_peer_find(ar, vdev_id, ev->peer_macaddr.addr);
spin_unlock_bh(&ar->data_lock);
if (!peer) {
ath10k_warn(ar, "failed to find peer entry for %pM\n",
ev->peer_macaddr.addr);
return;
}
switch (peer_status) {
case WMI_TDLS_SHOULD_TEARDOWN:
switch (peer_reason) {
case WMI_TDLS_TEARDOWN_REASON_PTR_TIMEOUT:
case WMI_TDLS_TEARDOWN_REASON_NO_RESPONSE:
case WMI_TDLS_TEARDOWN_REASON_RSSI:
reason = WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE;
break;
default:
reason = WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED;
break;
}
arvif = ath10k_get_arvif(ar, vdev_id);
if (!arvif) {
ath10k_warn(ar, "received tdls peer event for invalid vdev id %u\n",
vdev_id);
return;
}
ieee80211_tdls_oper_request(arvif->vif, ev->peer_macaddr.addr,
NL80211_TDLS_TEARDOWN, reason,
GFP_ATOMIC);
ath10k_dbg(ar, ATH10K_DBG_WMI,
"received tdls teardown event for peer %pM reason %u\n",
ev->peer_macaddr.addr, peer_reason);
break;
default:
ath10k_dbg(ar, ATH10K_DBG_WMI,
"received unknown tdls peer event %u\n",
peer_status);
break;
}
}
void ath10k_wmi_event_pdev_ftm_intg(struct ath10k *ar, struct sk_buff *skb)
{
ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_PDEV_FTM_INTG_EVENTID\n");
@ -5477,6 +5546,9 @@ static void ath10k_wmi_10_4_op_rx(struct ath10k *ar, struct sk_buff *skb)
case WMI_10_4_PDEV_TPC_CONFIG_EVENTID:
ath10k_wmi_event_pdev_tpc_config(ar, skb);
break;
case WMI_10_4_TDLS_PEER_EVENTID:
ath10k_wmi_handle_tdls_peer_event(ar, skb);
break;
default:
ath10k_warn(ar, "Unknown eventid: %d\n", id);
break;

View file

@ -195,6 +195,7 @@ enum wmi_service {
WMI_SERVICE_SMART_LOGGING_SUPPORT,
WMI_SERVICE_TDLS_CONN_TRACKER_IN_HOST_MODE,
WMI_SERVICE_TDLS_EXPLICIT_MODE_ONLY,
WMI_SERVICE_MGMT_TX_WMI,
/* keep last */
WMI_SERVICE_MAX,
@ -797,6 +798,7 @@ struct wmi_cmd_map {
u32 bcn_filter_rx_cmdid;
u32 prb_req_filter_rx_cmdid;
u32 mgmt_tx_cmdid;
u32 mgmt_tx_send_cmdid;
u32 prb_tmpl_cmdid;
u32 addba_clear_resp_cmdid;
u32 addba_send_cmdid;
@ -5236,7 +5238,8 @@ enum wmi_10_4_vdev_param {
#define WMI_VDEV_PARAM_TXBF_MU_TX_BFER BIT(3)
#define WMI_TXBF_STS_CAP_OFFSET_LSB 4
#define WMI_TXBF_STS_CAP_OFFSET_MASK 0xf0
#define WMI_TXBF_STS_CAP_OFFSET_MASK 0x70
#define WMI_TXBF_CONF_IMPLICIT_BF BIT(7)
#define WMI_BF_SOUND_DIM_OFFSET_LSB 8
#define WMI_BF_SOUND_DIM_OFFSET_MASK 0xf00

View file

@ -2766,7 +2766,6 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_mgmt *mgmt;
bool hidden = false;
u8 *ies;
int ies_len;
struct wmi_connect_cmd p;
int res;
int i, ret;
@ -2804,7 +2803,6 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
ies = mgmt->u.beacon.variable;
if (ies > info->beacon.head + info->beacon.head_len)
return -EINVAL;
ies_len = info->beacon.head + info->beacon.head_len - ies;
if (info->ssid == NULL)
return -EINVAL;

View file

@ -1001,7 +1001,7 @@ static void aggr_slice_amsdu(struct aggr_info *p_aggr,
while (amsdu_len > mac_hdr_len) {
hdr = (struct ethhdr *) framep;
payload_8023_len = ntohs(hdr->h_proto);
payload_8023_len = be16_to_cpu(hdr->h_proto);
if (payload_8023_len < MIN_MSDU_SUBFRAME_PAYLOAD_LEN ||
payload_8023_len > MAX_MSDU_SUBFRAME_PAYLOAD_LEN) {

View file

@ -61,13 +61,12 @@ config ATH9K_DEBUGFS
depends on ATH9K && DEBUG_FS
select MAC80211_DEBUGFS
select ATH9K_COMMON_DEBUG
select RELAY
---help---
Say Y, if you need access to ath9k's statistics for
interrupts, rate control, etc.
Also required for changing debug message flags at run time.
As well as access to the FFT/spectral data and TX99.
Also required for changing debug message flags at run time and for
TX99.
config ATH9K_STATION_STATISTICS
bool "Detailed station statistics"
@ -177,7 +176,6 @@ config ATH9K_HTC_DEBUGFS
bool "Atheros ath9k_htc debugging"
depends on ATH9K_HTC && DEBUG_FS
select ATH9K_COMMON_DEBUG
select RELAY
---help---
Say Y, if you need access to ath9k_htc's statistics.
As well as access to the FFT/spectral data.
@ -192,3 +190,11 @@ config ATH9K_HWRNG
Say Y, feeds the entropy directly from the WiFi driver to the input
pool.
config ATH9K_COMMON_SPECTRAL
bool "Atheros ath9k/ath9k_htc spectral scan support"
depends on ATH9K_DEBUGFS || ATH9K_HTC_DEBUGFS
select RELAY
default n
---help---
Say Y to enable access to the FFT/spectral data via debugfs.

View file

@ -62,8 +62,8 @@ ath9k_common-y:= common.o \
common-init.o \
common-beacon.o \
ath9k_common-$(CONFIG_ATH9K_COMMON_DEBUG) += common-debug.o \
common-spectral.o
ath9k_common-$(CONFIG_ATH9K_COMMON_DEBUG) += common-debug.o
ath9k_common-$(CONFIG_ATH9K_COMMON_SPECTRAL) += common-spectral.o
ath9k_htc-y += htc_hst.o \
hif_usb.o \

View file

@ -151,7 +151,7 @@ static inline u8 spectral_bitmap_weight(u8 *bins)
return bins[0] & 0x3f;
}
#ifdef CONFIG_ATH9K_COMMON_DEBUG
#ifdef CONFIG_ATH9K_COMMON_SPECTRAL
void ath9k_cmn_spectral_init_debug(struct ath_spec_scan_priv *spec_priv, struct dentry *debugfs_phy);
void ath9k_cmn_spectral_deinit_debug(struct ath_spec_scan_priv *spec_priv);
@ -183,6 +183,6 @@ static inline int ath_cmn_process_fft(struct ath_spec_scan_priv *spec_priv,
{
return 0;
}
#endif /* CONFIG_ATH9K_COMMON_DEBUG */
#endif /* CONFIG_ATH9K_COMMON_SPECTRAL */
#endif /* SPECTRAL_H */

View file

@ -123,11 +123,9 @@ static bool ath9k_check_chirping(struct ath_softc *sc, u8 *data,
fft = (struct ath9k_dfs_fft_40 *) (data + 2);
ath_dbg(common, DFS, "fixing datalen by 2\n");
}
if (IS_CHAN_HT40MINUS(ah->curchan)) {
int temp = is_ctl;
is_ctl = is_ext;
is_ext = temp;
}
if (IS_CHAN_HT40MINUS(ah->curchan))
swap(is_ctl, is_ext);
for (i = 0; i < FFT_NUM_SAMPLES; i++)
max_bin[i] = ath9k_get_max_index_ht40(fft + i, is_ctl,
is_ext);

View file

@ -1683,6 +1683,10 @@ static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break;
case IEEE80211_AMPDU_TX_OPERATIONAL:
if (tid >= ATH9K_HTC_MAX_TID) {
ret = -EINVAL;
break;
}
ista = (struct ath9k_htc_sta *) sta->drv_priv;
spin_lock_bh(&priv->tx.tx_lock);
ista->tid_state[tid] = AGGR_OPERATIONAL;

View file

@ -348,6 +348,13 @@ enum wcn36xx_hal_host_msg_type {
WCN36XX_HAL_DHCP_START_IND = 189,
WCN36XX_HAL_DHCP_STOP_IND = 190,
/* Scan Offload(hw) APIs */
WCN36XX_HAL_START_SCAN_OFFLOAD_REQ = 204,
WCN36XX_HAL_START_SCAN_OFFLOAD_RSP = 205,
WCN36XX_HAL_STOP_SCAN_OFFLOAD_REQ = 206,
WCN36XX_HAL_STOP_SCAN_OFFLOAD_RSP = 207,
WCN36XX_HAL_SCAN_OFFLOAD_IND = 210,
WCN36XX_HAL_AVOID_FREQ_RANGE_IND = 233,
WCN36XX_HAL_PRINT_REG_INFO_IND = 259,
@ -1115,6 +1122,101 @@ struct wcn36xx_hal_finish_scan_rsp_msg {
} __packed;
enum wcn36xx_hal_scan_type {
WCN36XX_HAL_SCAN_TYPE_PASSIVE = 0x00,
WCN36XX_HAL_SCAN_TYPE_ACTIVE = WCN36XX_HAL_MAX_ENUM_SIZE
};
struct wcn36xx_hal_mac_ssid {
u8 length;
u8 ssid[32];
} __packed;
struct wcn36xx_hal_start_scan_offload_req_msg {
struct wcn36xx_hal_msg_header header;
/* BSSIDs hot list */
u8 num_bssid;
u8 bssids[4][ETH_ALEN];
/* Directed probe-requests will be sent for listed SSIDs (max 10)*/
u8 num_ssid;
struct wcn36xx_hal_mac_ssid ssids[10];
/* Report AP with hidden ssid */
u8 scan_hidden;
/* Self MAC address */
u8 mac[ETH_ALEN];
/* BSS type */
enum wcn36xx_hal_bss_type bss_type;
/* Scan type */
enum wcn36xx_hal_scan_type scan_type;
/* Minimum scanning time on each channel (ms) */
u32 min_ch_time;
/* Maximum scanning time on each channel */
u32 max_ch_time;
/* Is a p2p search */
u8 p2p_search;
/* Channels to scan */
u8 num_channel;
u8 channels[80];
/* IE field */
u16 ie_len;
u8 ie[0];
} __packed;
struct wcn36xx_hal_start_scan_offload_rsp_msg {
struct wcn36xx_hal_msg_header header;
/* success or failure */
u32 status;
} __packed;
enum wcn36xx_hal_scan_offload_ind_type {
/* Scan has been started */
WCN36XX_HAL_SCAN_IND_STARTED = 0x01,
/* Scan has been completed */
WCN36XX_HAL_SCAN_IND_COMPLETED = 0x02,
/* Moved to foreign channel */
WCN36XX_HAL_SCAN_IND_FOREIGN_CHANNEL = 0x08,
/* scan request has been dequeued */
WCN36XX_HAL_SCAN_IND_DEQUEUED = 0x10,
/* preempted by other high priority scan */
WCN36XX_HAL_SCAN_IND_PREEMPTED = 0x20,
/* scan start failed */
WCN36XX_HAL_SCAN_IND_FAILED = 0x40,
/*scan restarted */
WCN36XX_HAL_SCAN_IND_RESTARTED = 0x80,
WCN36XX_HAL_SCAN_IND_MAX = WCN36XX_HAL_MAX_ENUM_SIZE
};
struct wcn36xx_hal_scan_offload_ind {
struct wcn36xx_hal_msg_header header;
u32 type;
u32 channel_mhz;
u32 scan_id;
} __packed;
struct wcn36xx_hal_stop_scan_offload_req_msg {
struct wcn36xx_hal_msg_header header;
} __packed;
struct wcn36xx_hal_stop_scan_offload_rsp_msg {
struct wcn36xx_hal_msg_header header;
/* success or failure */
u32 status;
} __packed;
enum wcn36xx_hal_rate_index {
HW_RATE_INDEX_1MBPS = 0x82,
HW_RATE_INDEX_2MBPS = 0x84,
@ -1507,11 +1609,6 @@ struct wcn36xx_hal_edca_param_record {
u16 txop_limit;
} __packed;
struct wcn36xx_hal_mac_ssid {
u8 length;
u8 ssid[32];
} __packed;
/* Concurrency role. These are generic IDs that identify the various roles
* in the software system. */
enum wcn36xx_hal_con_mode {

View file

@ -629,7 +629,6 @@ static int wcn36xx_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_scan_request *hw_req)
{
struct wcn36xx *wcn = hw->priv;
mutex_lock(&wcn->scan_lock);
if (wcn->scan_req) {
mutex_unlock(&wcn->scan_lock);
@ -638,11 +637,16 @@ static int wcn36xx_hw_scan(struct ieee80211_hw *hw,
wcn->scan_aborted = false;
wcn->scan_req = &hw_req->req;
mutex_unlock(&wcn->scan_lock);
schedule_work(&wcn->scan_work);
if (!get_feat_caps(wcn->fw_feat_caps, SCAN_OFFLOAD)) {
/* legacy manual/sw scan */
schedule_work(&wcn->scan_work);
return 0;
}
return 0;
return wcn36xx_smd_start_hw_scan(wcn, vif, &hw_req->req);
}
static void wcn36xx_cancel_hw_scan(struct ieee80211_hw *hw,
@ -650,6 +654,12 @@ static void wcn36xx_cancel_hw_scan(struct ieee80211_hw *hw,
{
struct wcn36xx *wcn = hw->priv;
if (!wcn36xx_smd_stop_hw_scan(wcn)) {
struct cfg80211_scan_info scan_info = { .aborted = true };
ieee80211_scan_completed(wcn->hw, &scan_info);
}
mutex_lock(&wcn->scan_lock);
wcn->scan_aborted = true;
mutex_unlock(&wcn->scan_lock);

View file

@ -73,6 +73,8 @@ static struct wcn36xx_cfg_val wcn36xx_cfg_vals[] = {
WCN36XX_CFG_VAL(TX_PWR_CTRL_ENABLE, 1),
WCN36XX_CFG_VAL(ENABLE_CLOSE_LOOP, 1),
WCN36XX_CFG_VAL(ENABLE_LPWR_IMG_TRANSITION, 0),
WCN36XX_CFG_VAL(BTC_STATIC_LEN_LE_BT, 120000),
WCN36XX_CFG_VAL(BTC_STATIC_LEN_LE_WLAN, 30000),
WCN36XX_CFG_VAL(MAX_ASSOC_LIMIT, 10),
WCN36XX_CFG_VAL(ENABLE_MCC_ADAPTIVE_SCHEDULER, 0),
};
@ -613,6 +615,85 @@ int wcn36xx_smd_finish_scan(struct wcn36xx *wcn,
return ret;
}
int wcn36xx_smd_start_hw_scan(struct wcn36xx *wcn, struct ieee80211_vif *vif,
struct cfg80211_scan_request *req)
{
struct wcn36xx_hal_start_scan_offload_req_msg msg_body;
int ret, i;
mutex_lock(&wcn->hal_mutex);
INIT_HAL_MSG(msg_body, WCN36XX_HAL_START_SCAN_OFFLOAD_REQ);
msg_body.scan_type = WCN36XX_HAL_SCAN_TYPE_ACTIVE;
msg_body.min_ch_time = 30;
msg_body.min_ch_time = 100;
msg_body.scan_hidden = 1;
memcpy(msg_body.mac, vif->addr, ETH_ALEN);
msg_body.p2p_search = vif->p2p;
msg_body.num_ssid = min_t(u8, req->n_ssids, ARRAY_SIZE(msg_body.ssids));
for (i = 0; i < msg_body.num_ssid; i++) {
msg_body.ssids[i].length = min_t(u8, req->ssids[i].ssid_len,
sizeof(msg_body.ssids[i].ssid));
memcpy(msg_body.ssids[i].ssid, req->ssids[i].ssid,
msg_body.ssids[i].length);
}
msg_body.num_channel = min_t(u8, req->n_channels,
sizeof(msg_body.channels));
for (i = 0; i < msg_body.num_channel; i++)
msg_body.channels[i] = req->channels[i]->hw_value;
PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
wcn36xx_dbg(WCN36XX_DBG_HAL,
"hal start hw-scan (channels: %u; ssids: %u; p2p: %s)\n",
msg_body.num_channel, msg_body.num_ssid,
msg_body.p2p_search ? "yes" : "no");
ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
if (ret) {
wcn36xx_err("Sending hal_start_scan_offload failed\n");
goto out;
}
ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
if (ret) {
wcn36xx_err("hal_start_scan_offload response failed err=%d\n",
ret);
goto out;
}
out:
mutex_unlock(&wcn->hal_mutex);
return ret;
}
int wcn36xx_smd_stop_hw_scan(struct wcn36xx *wcn)
{
struct wcn36xx_hal_stop_scan_offload_req_msg msg_body;
int ret;
mutex_lock(&wcn->hal_mutex);
INIT_HAL_MSG(msg_body, WCN36XX_HAL_STOP_SCAN_OFFLOAD_REQ);
PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
wcn36xx_dbg(WCN36XX_DBG_HAL, "hal stop hw-scan\n");
ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
if (ret) {
wcn36xx_err("Sending hal_stop_scan_offload failed\n");
goto out;
}
ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
if (ret) {
wcn36xx_err("hal_stop_scan_offload response failed err=%d\n",
ret);
goto out;
}
out:
mutex_unlock(&wcn->hal_mutex);
return ret;
}
static int wcn36xx_smd_switch_channel_rsp(void *buf, size_t len)
{
struct wcn36xx_hal_switch_channel_rsp_msg *rsp;
@ -2039,6 +2120,40 @@ static int wcn36xx_smd_tx_compl_ind(struct wcn36xx *wcn, void *buf, size_t len)
return 0;
}
static int wcn36xx_smd_hw_scan_ind(struct wcn36xx *wcn, void *buf, size_t len)
{
struct wcn36xx_hal_scan_offload_ind *rsp = buf;
struct cfg80211_scan_info scan_info = {};
if (len != sizeof(*rsp)) {
wcn36xx_warn("Corrupted delete scan indication\n");
return -EIO;
}
wcn36xx_dbg(WCN36XX_DBG_HAL, "scan indication (type %x)", rsp->type);
switch (rsp->type) {
case WCN36XX_HAL_SCAN_IND_FAILED:
scan_info.aborted = true;
case WCN36XX_HAL_SCAN_IND_COMPLETED:
mutex_lock(&wcn->scan_lock);
wcn->scan_req = NULL;
mutex_unlock(&wcn->scan_lock);
ieee80211_scan_completed(wcn->hw, &scan_info);
break;
case WCN36XX_HAL_SCAN_IND_STARTED:
case WCN36XX_HAL_SCAN_IND_FOREIGN_CHANNEL:
case WCN36XX_HAL_SCAN_IND_DEQUEUED:
case WCN36XX_HAL_SCAN_IND_PREEMPTED:
case WCN36XX_HAL_SCAN_IND_RESTARTED:
break;
default:
wcn36xx_warn("Unknown scan indication type %x\n", rsp->type);
}
return 0;
}
static int wcn36xx_smd_missed_beacon_ind(struct wcn36xx *wcn,
void *buf,
size_t len)
@ -2250,6 +2365,8 @@ int wcn36xx_smd_rsp_process(struct rpmsg_device *rpdev,
case WCN36XX_HAL_CH_SWITCH_RSP:
case WCN36XX_HAL_FEATURE_CAPS_EXCHANGE_RSP:
case WCN36XX_HAL_8023_MULTICAST_LIST_RSP:
case WCN36XX_HAL_START_SCAN_OFFLOAD_RSP:
case WCN36XX_HAL_STOP_SCAN_OFFLOAD_RSP:
memcpy(wcn->hal_buf, buf, len);
wcn->hal_rsp_len = len;
complete(&wcn->hal_rsp_compl);
@ -2262,6 +2379,7 @@ int wcn36xx_smd_rsp_process(struct rpmsg_device *rpdev,
case WCN36XX_HAL_MISSED_BEACON_IND:
case WCN36XX_HAL_DELETE_STA_CONTEXT_IND:
case WCN36XX_HAL_PRINT_REG_INFO_IND:
case WCN36XX_HAL_SCAN_OFFLOAD_IND:
msg_ind = kmalloc(sizeof(*msg_ind) + len, GFP_ATOMIC);
if (!msg_ind) {
wcn36xx_err("Run out of memory while handling SMD_EVENT (%d)\n",
@ -2298,6 +2416,8 @@ static void wcn36xx_ind_smd_work(struct work_struct *work)
hal_ind_msg = list_first_entry(&wcn->hal_ind_queue,
struct wcn36xx_hal_ind_msg,
list);
list_del(wcn->hal_ind_queue.next);
spin_unlock_irqrestore(&wcn->hal_ind_lock, flags);
msg_header = (struct wcn36xx_hal_msg_header *)hal_ind_msg->msg;
@ -2326,12 +2446,14 @@ static void wcn36xx_ind_smd_work(struct work_struct *work)
hal_ind_msg->msg,
hal_ind_msg->msg_len);
break;
case WCN36XX_HAL_SCAN_OFFLOAD_IND:
wcn36xx_smd_hw_scan_ind(wcn, hal_ind_msg->msg,
hal_ind_msg->msg_len);
break;
default:
wcn36xx_err("SMD_EVENT (%d) not supported\n",
msg_header->msg_type);
}
list_del(wcn->hal_ind_queue.next);
spin_unlock_irqrestore(&wcn->hal_ind_lock, flags);
kfree(hal_ind_msg);
}
int wcn36xx_smd_open(struct wcn36xx *wcn)

View file

@ -65,6 +65,9 @@ int wcn36xx_smd_end_scan(struct wcn36xx *wcn, u8 scan_channel);
int wcn36xx_smd_finish_scan(struct wcn36xx *wcn,
enum wcn36xx_hal_sys_mode mode);
int wcn36xx_smd_update_scan_params(struct wcn36xx *wcn, u8 *channels, size_t channel_count);
int wcn36xx_smd_start_hw_scan(struct wcn36xx *wcn, struct ieee80211_vif *vif,
struct cfg80211_scan_request *req);
int wcn36xx_smd_stop_hw_scan(struct wcn36xx *wcn);
int wcn36xx_smd_add_sta_self(struct wcn36xx *wcn, struct ieee80211_vif *vif);
int wcn36xx_smd_delete_sta_self(struct wcn36xx *wcn, u8 *addr);
int wcn36xx_smd_delete_sta(struct wcn36xx *wcn, u8 sta_index);

View file

@ -901,7 +901,7 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
u64 *cookie)
{
const u8 *buf = params->buf;
size_t len = params->len;
size_t len = params->len, total;
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
int rc;
bool tx_status = false;
@ -926,7 +926,11 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
if (len < sizeof(struct ieee80211_hdr_3addr))
return -EINVAL;
cmd = kmalloc(sizeof(*cmd) + len, GFP_KERNEL);
total = sizeof(*cmd) + len;
if (total < len)
return -EINVAL;
cmd = kmalloc(total, GFP_KERNEL);
if (!cmd) {
rc = -ENOMEM;
goto out;
@ -936,7 +940,7 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
cmd->len = cpu_to_le16(len);
memcpy(cmd->payload, buf, len);
rc = wmi_call(wil, WMI_SW_TX_REQ_CMDID, cmd, sizeof(*cmd) + len,
rc = wmi_call(wil, WMI_SW_TX_REQ_CMDID, cmd, total,
WMI_SW_TX_COMPLETE_EVENTID, &evt, sizeof(evt), 2000);
if (rc == 0)
tx_status = !evt.evt.status;
@ -1727,9 +1731,12 @@ static int wil_cfg80211_suspend(struct wiphy *wiphy,
wil_dbg_pm(wil, "suspending\n");
wil_p2p_stop_discovery(wil);
mutex_lock(&wil->mutex);
mutex_lock(&wil->p2p_wdev_mutex);
wil_p2p_stop_radio_operations(wil);
wil_abort_scan(wil, true);
mutex_unlock(&wil->p2p_wdev_mutex);
mutex_unlock(&wil->mutex);
out:
return rc;

View file

@ -242,12 +242,19 @@ static void wil_print_ring(struct seq_file *s, const char *prefix,
static int wil_mbox_debugfs_show(struct seq_file *s, void *data)
{
struct wil6210_priv *wil = s->private;
int ret;
ret = wil_pm_runtime_get(wil);
if (ret < 0)
return ret;
wil_print_ring(s, "tx", wil->csr + HOST_MBOX +
offsetof(struct wil6210_mbox_ctl, tx));
wil_print_ring(s, "rx", wil->csr + HOST_MBOX +
offsetof(struct wil6210_mbox_ctl, rx));
wil_pm_runtime_put(wil);
return 0;
}
@ -265,15 +272,37 @@ static const struct file_operations fops_mbox = {
static int wil_debugfs_iomem_x32_set(void *data, u64 val)
{
writel(val, (void __iomem *)data);
struct wil_debugfs_iomem_data *d = (struct
wil_debugfs_iomem_data *)data;
struct wil6210_priv *wil = d->wil;
int ret;
ret = wil_pm_runtime_get(wil);
if (ret < 0)
return ret;
writel(val, (void __iomem *)d->offset);
wmb(); /* make sure write propagated to HW */
wil_pm_runtime_put(wil);
return 0;
}
static int wil_debugfs_iomem_x32_get(void *data, u64 *val)
{
*val = readl((void __iomem *)data);
struct wil_debugfs_iomem_data *d = (struct
wil_debugfs_iomem_data *)data;
struct wil6210_priv *wil = d->wil;
int ret;
ret = wil_pm_runtime_get(wil);
if (ret < 0)
return ret;
*val = readl((void __iomem *)d->offset);
wil_pm_runtime_put(wil);
return 0;
}
@ -284,10 +313,21 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x32, wil_debugfs_iomem_x32_get,
static struct dentry *wil_debugfs_create_iomem_x32(const char *name,
umode_t mode,
struct dentry *parent,
void *value)
void *value,
struct wil6210_priv *wil)
{
return debugfs_create_file(name, mode, parent, value,
&fops_iomem_x32);
struct dentry *file;
struct wil_debugfs_iomem_data *data = &wil->dbg_data.data_arr[
wil->dbg_data.iomem_data_count];
data->wil = wil;
data->offset = value;
file = debugfs_create_file(name, mode, parent, data, &fops_iomem_x32);
if (!IS_ERR_OR_NULL(file))
wil->dbg_data.iomem_data_count++;
return file;
}
static int wil_debugfs_ulong_set(void *data, u64 val)
@ -346,7 +386,8 @@ static void wil6210_debugfs_init_offset(struct wil6210_priv *wil,
case doff_io32:
f = wil_debugfs_create_iomem_x32(tbl[i].name,
tbl[i].mode, dbg,
base + tbl[i].off);
base + tbl[i].off,
wil);
break;
case doff_u8:
f = debugfs_create_u8(tbl[i].name, tbl[i].mode, dbg,
@ -475,13 +516,22 @@ static int wil6210_debugfs_create_ITR_CNT(struct wil6210_priv *wil,
static int wil_memread_debugfs_show(struct seq_file *s, void *data)
{
struct wil6210_priv *wil = s->private;
void __iomem *a = wmi_buffer(wil, cpu_to_le32(mem_addr));
void __iomem *a;
int ret;
ret = wil_pm_runtime_get(wil);
if (ret < 0)
return ret;
a = wmi_buffer(wil, cpu_to_le32(mem_addr));
if (a)
seq_printf(s, "[0x%08x] = 0x%08x\n", mem_addr, readl(a));
else
seq_printf(s, "[0x%08x] = INVALID\n", mem_addr);
wil_pm_runtime_put(wil);
return 0;
}
@ -502,10 +552,12 @@ static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf,
{
enum { max_count = 4096 };
struct wil_blob_wrapper *wil_blob = file->private_data;
struct wil6210_priv *wil = wil_blob->wil;
loff_t pos = *ppos;
size_t available = wil_blob->blob.size;
void *buf;
size_t ret;
int rc;
if (test_bit(wil_status_suspending, wil_blob->wil->status) ||
test_bit(wil_status_suspended, wil_blob->wil->status))
@ -526,10 +578,19 @@ static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf,
if (!buf)
return -ENOMEM;
rc = wil_pm_runtime_get(wil);
if (rc < 0) {
kfree(buf);
return rc;
}
wil_memcpy_fromio_32(buf, (const void __iomem *)
wil_blob->blob.data + pos, count);
ret = copy_to_user(user_buf, buf, count);
wil_pm_runtime_put(wil);
kfree(buf);
if (ret == count)
return -EFAULT;
@ -1571,8 +1632,6 @@ static ssize_t wil_write_suspend_stats(struct file *file,
struct wil6210_priv *wil = file->private_data;
memset(&wil->suspend_stats, 0, sizeof(wil->suspend_stats));
wil->suspend_stats.min_suspend_time = ULONG_MAX;
wil->suspend_stats.collection_start = ktime_get();
return len;
}
@ -1582,33 +1641,41 @@ static ssize_t wil_read_suspend_stats(struct file *file,
size_t count, loff_t *ppos)
{
struct wil6210_priv *wil = file->private_data;
static char text[400];
int n;
unsigned long long stats_collection_time =
ktime_to_us(ktime_sub(ktime_get(),
wil->suspend_stats.collection_start));
char *text;
int n, ret, text_size = 500;
n = snprintf(text, sizeof(text),
"Suspend statistics:\n"
text = kmalloc(text_size, GFP_KERNEL);
if (!text)
return -ENOMEM;
n = snprintf(text, text_size,
"Radio on suspend statistics:\n"
"successful suspends:%ld failed suspends:%ld\n"
"successful resumes:%ld failed resumes:%ld\n"
"rejected by host:%ld rejected by device:%ld\n"
"total suspend time:%lld min suspend time:%lld\n"
"max suspend time:%lld stats collection time: %lld\n",
wil->suspend_stats.successful_suspends,
wil->suspend_stats.failed_suspends,
wil->suspend_stats.successful_resumes,
wil->suspend_stats.failed_resumes,
wil->suspend_stats.rejected_by_host,
"rejected by device:%ld\n"
"Radio off suspend statistics:\n"
"successful suspends:%ld failed suspends:%ld\n"
"successful resumes:%ld failed resumes:%ld\n"
"General statistics:\n"
"rejected by host:%ld\n",
wil->suspend_stats.r_on.successful_suspends,
wil->suspend_stats.r_on.failed_suspends,
wil->suspend_stats.r_on.successful_resumes,
wil->suspend_stats.r_on.failed_resumes,
wil->suspend_stats.rejected_by_device,
wil->suspend_stats.total_suspend_time,
wil->suspend_stats.min_suspend_time,
wil->suspend_stats.max_suspend_time,
stats_collection_time);
wil->suspend_stats.r_off.successful_suspends,
wil->suspend_stats.r_off.failed_suspends,
wil->suspend_stats.r_off.successful_resumes,
wil->suspend_stats.r_off.failed_resumes,
wil->suspend_stats.rejected_by_host);
n = min_t(int, n, sizeof(text));
n = min_t(int, n, text_size);
return simple_read_from_buffer(user_buf, count, ppos, text, n);
ret = simple_read_from_buffer(user_buf, count, ppos, text, n);
kfree(text);
return ret;
}
static const struct file_operations fops_suspend_stats = {
@ -1736,14 +1803,31 @@ static const struct dbg_off dbg_statics[] = {
{},
};
static const int dbg_off_count = 4 * (ARRAY_SIZE(isr_off) - 1) +
ARRAY_SIZE(dbg_wil_regs) - 1 +
ARRAY_SIZE(pseudo_isr_off) - 1 +
ARRAY_SIZE(lgc_itr_cnt_off) - 1 +
ARRAY_SIZE(tx_itr_cnt_off) - 1 +
ARRAY_SIZE(rx_itr_cnt_off) - 1;
int wil6210_debugfs_init(struct wil6210_priv *wil)
{
struct dentry *dbg = wil->debug = debugfs_create_dir(WIL_NAME,
wil_to_wiphy(wil)->debugfsdir);
if (IS_ERR_OR_NULL(dbg))
return -ENODEV;
wil->dbg_data.data_arr = kcalloc(dbg_off_count,
sizeof(struct wil_debugfs_iomem_data),
GFP_KERNEL);
if (!wil->dbg_data.data_arr) {
debugfs_remove_recursive(dbg);
wil->debug = NULL;
return -ENOMEM;
}
wil->dbg_data.iomem_data_count = 0;
wil_pmc_init(wil);
wil6210_debugfs_init_files(wil, dbg);
@ -1758,8 +1842,6 @@ int wil6210_debugfs_init(struct wil6210_priv *wil)
wil6210_debugfs_create_ITR_CNT(wil, dbg);
wil->suspend_stats.collection_start = ktime_get();
return 0;
}
@ -1768,6 +1850,8 @@ void wil6210_debugfs_remove(struct wil6210_priv *wil)
debugfs_remove_recursive(wil->debug);
wil->debug = NULL;
kfree(wil->dbg_data.data_arr);
/* free pmc memory without sending command to fw, as it will
* be reset on the way down anyway
*/

View file

@ -47,9 +47,14 @@ static int wil_ethtoolops_get_coalesce(struct net_device *ndev,
struct wil6210_priv *wil = ndev_to_wil(ndev);
u32 tx_itr_en, tx_itr_val = 0;
u32 rx_itr_en, rx_itr_val = 0;
int ret;
wil_dbg_misc(wil, "ethtoolops_get_coalesce\n");
ret = wil_pm_runtime_get(wil);
if (ret < 0)
return ret;
tx_itr_en = wil_r(wil, RGF_DMA_ITR_TX_CNT_CTL);
if (tx_itr_en & BIT_DMA_ITR_TX_CNT_CTL_EN)
tx_itr_val = wil_r(wil, RGF_DMA_ITR_TX_CNT_TRSH);
@ -58,6 +63,8 @@ static int wil_ethtoolops_get_coalesce(struct net_device *ndev,
if (rx_itr_en & BIT_DMA_ITR_RX_CNT_CTL_EN)
rx_itr_val = wil_r(wil, RGF_DMA_ITR_RX_CNT_TRSH);
wil_pm_runtime_put(wil);
cp->tx_coalesce_usecs = tx_itr_val;
cp->rx_coalesce_usecs = rx_itr_val;
return 0;
@ -67,6 +74,7 @@ static int wil_ethtoolops_set_coalesce(struct net_device *ndev,
struct ethtool_coalesce *cp)
{
struct wil6210_priv *wil = ndev_to_wil(ndev);
int ret;
wil_dbg_misc(wil, "ethtoolops_set_coalesce: rx %d usec, tx %d usec\n",
cp->rx_coalesce_usecs, cp->tx_coalesce_usecs);
@ -86,8 +94,15 @@ static int wil_ethtoolops_set_coalesce(struct net_device *ndev,
wil->tx_max_burst_duration = cp->tx_coalesce_usecs;
wil->rx_max_burst_duration = cp->rx_coalesce_usecs;
ret = wil_pm_runtime_get(wil);
if (ret < 0)
return ret;
wil_configure_interrupt_moderation(wil);
wil_pm_runtime_put(wil);
return 0;
out_bad:

View file

@ -26,14 +26,17 @@
prefix_type, rowsize, \
groupsize, buf, len, ascii)
#define FW_ADDR_CHECK(ioaddr, val, msg) do { \
ioaddr = wmi_buffer(wil, val); \
if (!ioaddr) { \
wil_err_fw(wil, "bad " msg ": 0x%08x\n", \
le32_to_cpu(val)); \
return -EINVAL; \
} \
} while (0)
static bool wil_fw_addr_check(struct wil6210_priv *wil,
void __iomem **ioaddr, __le32 val,
u32 size, const char *msg)
{
*ioaddr = wmi_buffer_block(wil, val, size);
if (!(*ioaddr)) {
wil_err_fw(wil, "bad %s: 0x%08x\n", msg, le32_to_cpu(val));
return false;
}
return true;
}
/**
* wil_fw_verify - verify firmware file validity
@ -124,24 +127,19 @@ static int fw_ignore_section(struct wil6210_priv *wil, const void *data,
return 0;
}
static int fw_handle_comment(struct wil6210_priv *wil, const void *data,
size_t size)
{
wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1, data, size, true);
return 0;
}
static int
fw_handle_capabilities(struct wil6210_priv *wil, const void *data,
size_t size)
fw_handle_comment(struct wil6210_priv *wil, const void *data,
size_t size)
{
const struct wil_fw_record_capabilities *rec = data;
size_t capa_size;
if (size < sizeof(*rec) ||
le32_to_cpu(rec->magic) != WIL_FW_CAPABILITIES_MAGIC)
le32_to_cpu(rec->magic) != WIL_FW_CAPABILITIES_MAGIC) {
wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1,
data, size, true);
return 0;
}
capa_size = size - offsetof(struct wil_fw_record_capabilities,
capabilities);
@ -165,7 +163,8 @@ static int fw_handle_data(struct wil6210_priv *wil, const void *data,
return -EINVAL;
}
FW_ADDR_CHECK(dst, d->addr, "address");
if (!wil_fw_addr_check(wil, &dst, d->addr, s, "address"))
return -EINVAL;
wil_dbg_fw(wil, "write [0x%08x] <== %zu bytes\n", le32_to_cpu(d->addr),
s);
wil_memcpy_toio_32(dst, d->data, s);
@ -197,7 +196,8 @@ static int fw_handle_fill(struct wil6210_priv *wil, const void *data,
return -EINVAL;
}
FW_ADDR_CHECK(dst, d->addr, "address");
if (!wil_fw_addr_check(wil, &dst, d->addr, s, "address"))
return -EINVAL;
v = le32_to_cpu(d->value);
wil_dbg_fw(wil, "fill [0x%08x] <== 0x%08x, %zu bytes\n",
@ -253,7 +253,8 @@ static int fw_handle_direct_write(struct wil6210_priv *wil, const void *data,
u32 v = le32_to_cpu(block[i].value);
u32 x, y;
FW_ADDR_CHECK(dst, block[i].addr, "address");
if (!wil_fw_addr_check(wil, &dst, block[i].addr, 0, "address"))
return -EINVAL;
x = readl(dst);
y = (x & m) | (v & ~m);
@ -319,10 +320,15 @@ static int fw_handle_gateway_data(struct wil6210_priv *wil, const void *data,
wil_dbg_fw(wil, "gw write record [%3d] blocks, cmd 0x%08x\n",
n, gw_cmd);
FW_ADDR_CHECK(gwa_addr, d->gateway_addr_addr, "gateway_addr_addr");
FW_ADDR_CHECK(gwa_val, d->gateway_value_addr, "gateway_value_addr");
FW_ADDR_CHECK(gwa_cmd, d->gateway_cmd_addr, "gateway_cmd_addr");
FW_ADDR_CHECK(gwa_ctl, d->gateway_ctrl_address, "gateway_ctrl_address");
if (!wil_fw_addr_check(wil, &gwa_addr, d->gateway_addr_addr, 0,
"gateway_addr_addr") ||
!wil_fw_addr_check(wil, &gwa_val, d->gateway_value_addr, 0,
"gateway_value_addr") ||
!wil_fw_addr_check(wil, &gwa_cmd, d->gateway_cmd_addr, 0,
"gateway_cmd_addr") ||
!wil_fw_addr_check(wil, &gwa_ctl, d->gateway_ctrl_address, 0,
"gateway_ctrl_address"))
return -EINVAL;
wil_dbg_fw(wil, "gw addresses: addr 0x%08x val 0x%08x"
" cmd 0x%08x ctl 0x%08x\n",
@ -378,12 +384,19 @@ static int fw_handle_gateway_data4(struct wil6210_priv *wil, const void *data,
wil_dbg_fw(wil, "gw4 write record [%3d] blocks, cmd 0x%08x\n",
n, gw_cmd);
FW_ADDR_CHECK(gwa_addr, d->gateway_addr_addr, "gateway_addr_addr");
if (!wil_fw_addr_check(wil, &gwa_addr, d->gateway_addr_addr, 0,
"gateway_addr_addr"))
return -EINVAL;
for (k = 0; k < ARRAY_SIZE(block->value); k++)
FW_ADDR_CHECK(gwa_val[k], d->gateway_value_addr[k],
"gateway_value_addr");
FW_ADDR_CHECK(gwa_cmd, d->gateway_cmd_addr, "gateway_cmd_addr");
FW_ADDR_CHECK(gwa_ctl, d->gateway_ctrl_address, "gateway_ctrl_address");
if (!wil_fw_addr_check(wil, &gwa_val[k],
d->gateway_value_addr[k],
0, "gateway_value_addr"))
return -EINVAL;
if (!wil_fw_addr_check(wil, &gwa_cmd, d->gateway_cmd_addr, 0,
"gateway_cmd_addr") ||
!wil_fw_addr_check(wil, &gwa_ctl, d->gateway_ctrl_address, 0,
"gateway_ctrl_address"))
return -EINVAL;
wil_dbg_fw(wil, "gw4 addresses: addr 0x%08x cmd 0x%08x ctl 0x%08x\n",
le32_to_cpu(d->gateway_addr_addr),
@ -422,7 +435,7 @@ static const struct {
int (*parse_handler)(struct wil6210_priv *wil, const void *data,
size_t size);
} wil_fw_handlers[] = {
{wil_fw_type_comment, fw_handle_comment, fw_handle_capabilities},
{wil_fw_type_comment, fw_handle_comment, fw_handle_comment},
{wil_fw_type_data, fw_handle_data, fw_ignore_section},
{wil_fw_type_fill, fw_handle_fill, fw_ignore_section},
/* wil_fw_type_action */
@ -517,7 +530,7 @@ int wil_request_firmware(struct wil6210_priv *wil, const char *name,
rc = request_firmware(&fw, name, wil_to_dev(wil));
if (rc) {
wil_err_fw(wil, "Failed to load firmware %s\n", name);
wil_err_fw(wil, "Failed to load firmware %s rc %d\n", name, rc);
return rc;
}
wil_dbg_fw(wil, "Loading <%s>, %zu bytes\n", name, fw->size);

View file

@ -358,6 +358,25 @@ static void wil_cache_mbox_regs(struct wil6210_priv *wil)
wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx);
}
static bool wil_validate_mbox_regs(struct wil6210_priv *wil)
{
size_t min_size = sizeof(struct wil6210_mbox_hdr) +
sizeof(struct wmi_cmd_hdr);
if (wil->mbox_ctl.rx.entry_size < min_size) {
wil_err(wil, "rx mbox entry too small (%d)\n",
wil->mbox_ctl.rx.entry_size);
return false;
}
if (wil->mbox_ctl.tx.entry_size < min_size) {
wil_err(wil, "tx mbox entry too small (%d)\n",
wil->mbox_ctl.tx.entry_size);
return false;
}
return true;
}
static irqreturn_t wil6210_irq_misc(int irq, void *cookie)
{
struct wil6210_priv *wil = cookie;
@ -393,7 +412,8 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie)
if (isr & ISR_MISC_FW_READY) {
wil_dbg_irq(wil, "IRQ: FW ready\n");
wil_cache_mbox_regs(wil);
set_bit(wil_status_mbox_ready, wil->status);
if (wil_validate_mbox_regs(wil))
set_bit(wil_status_mbox_ready, wil->status);
/**
* Actual FW ready indicated by the
* WMI_FW_READY_EVENTID

View file

@ -579,7 +579,6 @@ int wil_priv_init(struct wil6210_priv *wil)
wil->wakeup_trigger = WMI_WAKEUP_TRIGGER_UCAST |
WMI_WAKEUP_TRIGGER_BCAST;
memset(&wil->suspend_stats, 0, sizeof(wil->suspend_stats));
wil->suspend_stats.min_suspend_time = ULONG_MAX;
wil->vring_idle_trsh = 16;
return 0;
@ -760,6 +759,8 @@ static void wil_collect_fw_info(struct wil6210_priv *wil)
u8 retry_short;
int rc;
wil_refresh_fw_capabilities(wil);
rc = wmi_get_mgmt_retry(wil, &retry_short);
if (!rc) {
wiphy->retry_short = retry_short;
@ -767,6 +768,25 @@ static void wil_collect_fw_info(struct wil6210_priv *wil)
}
}
void wil_refresh_fw_capabilities(struct wil6210_priv *wil)
{
struct wiphy *wiphy = wil_to_wiphy(wil);
wil->keep_radio_on_during_sleep =
wil->platform_ops.keep_radio_on_during_sleep &&
wil->platform_ops.keep_radio_on_during_sleep(
wil->platform_handle) &&
test_bit(WMI_FW_CAPABILITY_D3_SUSPEND, wil->fw_capabilities);
wil_info(wil, "keep_radio_on_during_sleep (%d)\n",
wil->keep_radio_on_during_sleep);
if (test_bit(WMI_FW_CAPABILITY_RSSI_REPORTING, wil->fw_capabilities))
wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
else
wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC;
}
void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r)
{
le32_to_cpus(&r->base);
@ -1071,11 +1091,11 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
return rc;
}
wil_collect_fw_info(wil);
if (wil->ps_profile != WMI_PS_PROFILE_TYPE_DEFAULT)
wil_ps_update(wil, wil->ps_profile);
wil_collect_fw_info(wil);
if (wil->platform_ops.notify) {
rc = wil->platform_ops.notify(wil->platform_handle,
WIL_PLATFORM_EVT_FW_RDY);

View file

@ -21,6 +21,7 @@
static int wil_open(struct net_device *ndev)
{
struct wil6210_priv *wil = ndev_to_wil(ndev);
int rc;
wil_dbg_misc(wil, "open\n");
@ -30,16 +31,29 @@ static int wil_open(struct net_device *ndev)
return -EINVAL;
}
return wil_up(wil);
rc = wil_pm_runtime_get(wil);
if (rc < 0)
return rc;
rc = wil_up(wil);
if (rc)
wil_pm_runtime_put(wil);
return rc;
}
static int wil_stop(struct net_device *ndev)
{
struct wil6210_priv *wil = ndev_to_wil(ndev);
int rc;
wil_dbg_misc(wil, "stop\n");
return wil_down(wil);
rc = wil_down(wil);
if (!rc)
wil_pm_runtime_put(wil);
return rc;
}
static const struct net_device_ops wil_netdev_ops = {

View file

@ -21,6 +21,7 @@
#include <linux/suspend.h>
#include "wil6210.h"
#include <linux/rtnetlink.h>
#include <linux/pm_runtime.h>
static bool use_msi = true;
module_param(use_msi, bool, 0444);
@ -31,10 +32,8 @@ module_param(ftm_mode, bool, 0444);
MODULE_PARM_DESC(ftm_mode, " Set factory test mode, default - false");
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
static int wil6210_pm_notify(struct notifier_block *notify_block,
unsigned long mode, void *unused);
#endif /* CONFIG_PM_SLEEP */
#endif /* CONFIG_PM */
static
@ -84,9 +83,7 @@ void wil_set_capabilities(struct wil6210_priv *wil)
/* extract FW capabilities from file without loading the FW */
wil_request_firmware(wil, wil->wil_fw_name, false);
if (test_bit(WMI_FW_CAPABILITY_RSSI_REPORTING, wil->fw_capabilities))
wil_to_wiphy(wil)->signal_type = CFG80211_SIGNAL_TYPE_MBM;
wil_refresh_fw_capabilities(wil);
}
void wil_disable_irq(struct wil6210_priv *wil)
@ -296,15 +293,6 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
wil_set_capabilities(wil);
wil6210_clear_irq(wil);
wil->keep_radio_on_during_sleep =
wil->platform_ops.keep_radio_on_during_sleep &&
wil->platform_ops.keep_radio_on_during_sleep(
wil->platform_handle) &&
test_bit(WMI_FW_CAPABILITY_D3_SUSPEND, wil->fw_capabilities);
wil_info(wil, "keep_radio_on_during_sleep (%d)\n",
wil->keep_radio_on_during_sleep);
/* FW should raise IRQ when ready */
rc = wil_if_pcie_enable(wil);
if (rc) {
@ -320,7 +308,6 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
}
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
wil->pm_notify.notifier_call = wil6210_pm_notify;
rc = register_pm_notifier(&wil->pm_notify);
if (rc)
@ -328,11 +315,11 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
* be prevented in a later phase if needed
*/
wil_err(wil, "register_pm_notifier failed: %d\n", rc);
#endif /* CONFIG_PM_SLEEP */
#endif /* CONFIG_PM */
wil6210_debugfs_init(wil);
wil_pm_runtime_allow(wil);
return 0;
@ -360,11 +347,11 @@ static void wil_pcie_remove(struct pci_dev *pdev)
wil_dbg_misc(wil, "pcie_remove\n");
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
unregister_pm_notifier(&wil->pm_notify);
#endif /* CONFIG_PM_SLEEP */
#endif /* CONFIG_PM */
wil_pm_runtime_forbid(wil);
wil6210_debugfs_remove(wil);
rtnl_lock();
wil_p2p_wdev_free(wil);
@ -386,13 +373,15 @@ static const struct pci_device_id wil6210_pcie_ids[] = {
MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids);
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
static int wil6210_suspend(struct device *dev, bool is_runtime)
{
int rc = 0;
struct pci_dev *pdev = to_pci_dev(dev);
struct wil6210_priv *wil = pci_get_drvdata(pdev);
struct net_device *ndev = wil_to_ndev(wil);
bool keep_radio_on = ndev->flags & IFF_UP &&
wil->keep_radio_on_during_sleep;
wil_dbg_pm(wil, "suspend: %s\n", is_runtime ? "runtime" : "system");
@ -400,16 +389,18 @@ static int wil6210_suspend(struct device *dev, bool is_runtime)
if (rc)
goto out;
rc = wil_suspend(wil, is_runtime);
rc = wil_suspend(wil, is_runtime, keep_radio_on);
if (!rc) {
wil->suspend_stats.successful_suspends++;
/* If platform device supports keep_radio_on_during_sleep
* it will control PCIe master
/* In case radio stays on, platform device will control
* PCIe master
*/
if (!wil->keep_radio_on_during_sleep)
if (!keep_radio_on) {
/* disable bus mastering */
pci_clear_master(pdev);
wil->suspend_stats.r_off.successful_suspends++;
} else {
wil->suspend_stats.r_on.successful_suspends++;
}
}
out:
return rc;
@ -420,23 +411,32 @@ static int wil6210_resume(struct device *dev, bool is_runtime)
int rc = 0;
struct pci_dev *pdev = to_pci_dev(dev);
struct wil6210_priv *wil = pci_get_drvdata(pdev);
struct net_device *ndev = wil_to_ndev(wil);
bool keep_radio_on = ndev->flags & IFF_UP &&
wil->keep_radio_on_during_sleep;
wil_dbg_pm(wil, "resume: %s\n", is_runtime ? "runtime" : "system");
/* If platform device supports keep_radio_on_during_sleep it will
* control PCIe master
/* In case radio stays on, platform device will control
* PCIe master
*/
if (!wil->keep_radio_on_during_sleep)
if (!keep_radio_on)
/* allow master */
pci_set_master(pdev);
rc = wil_resume(wil, is_runtime);
rc = wil_resume(wil, is_runtime, keep_radio_on);
if (rc) {
wil_err(wil, "device failed to resume (%d)\n", rc);
wil->suspend_stats.failed_resumes++;
if (!wil->keep_radio_on_during_sleep)
if (!keep_radio_on) {
pci_clear_master(pdev);
wil->suspend_stats.r_off.failed_resumes++;
} else {
wil->suspend_stats.r_on.failed_resumes++;
}
} else {
wil->suspend_stats.successful_resumes++;
if (keep_radio_on)
wil->suspend_stats.r_on.successful_resumes++;
else
wil->suspend_stats.r_off.successful_resumes++;
}
return rc;
@ -490,12 +490,43 @@ static int wil6210_pm_resume(struct device *dev)
{
return wil6210_resume(dev, false);
}
#endif /* CONFIG_PM_SLEEP */
static int wil6210_pm_runtime_idle(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct wil6210_priv *wil = pci_get_drvdata(pdev);
wil_dbg_pm(wil, "Runtime idle\n");
return wil_can_suspend(wil, true);
}
static int wil6210_pm_runtime_resume(struct device *dev)
{
return wil6210_resume(dev, true);
}
static int wil6210_pm_runtime_suspend(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct wil6210_priv *wil = pci_get_drvdata(pdev);
if (test_bit(wil_status_suspended, wil->status)) {
wil_dbg_pm(wil, "trying to suspend while suspended\n");
return 1;
}
return wil6210_suspend(dev, true);
}
#endif /* CONFIG_PM */
static const struct dev_pm_ops wil6210_pm_ops = {
#ifdef CONFIG_PM
SET_SYSTEM_SLEEP_PM_OPS(wil6210_pm_suspend, wil6210_pm_resume)
SET_RUNTIME_PM_OPS(wil6210_pm_runtime_suspend,
wil6210_pm_runtime_resume,
wil6210_pm_runtime_idle)
#endif /* CONFIG_PM */
};
static struct pci_driver wil6210_driver = {

View file

@ -16,15 +16,30 @@
#include "wil6210.h"
#include <linux/jiffies.h>
#include <linux/pm_runtime.h>
#define WIL6210_AUTOSUSPEND_DELAY_MS (1000)
int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime)
{
int rc = 0;
struct wireless_dev *wdev = wil->wdev;
struct net_device *ndev = wil_to_ndev(wil);
bool wmi_only = test_bit(WMI_FW_CAPABILITY_WMI_ONLY,
wil->fw_capabilities);
wil_dbg_pm(wil, "can_suspend: %s\n", is_runtime ? "runtime" : "system");
if (wmi_only || debug_fw) {
wil_dbg_pm(wil, "Deny any suspend - %s mode\n",
wmi_only ? "wmi_only" : "debug_fw");
rc = -EBUSY;
goto out;
}
if (is_runtime && !wil->platform_ops.suspend) {
rc = -EBUSY;
goto out;
}
if (!(ndev->flags & IFF_UP)) {
/* can always sleep when down */
wil_dbg_pm(wil, "Interface is down\n");
@ -44,6 +59,10 @@ int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime)
/* interface is running */
switch (wdev->iftype) {
case NL80211_IFTYPE_MONITOR:
wil_dbg_pm(wil, "Sniffer\n");
rc = -EBUSY;
goto out;
/* for STA-like interface, don't runtime suspend */
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_P2P_CLIENT:
if (test_bit(wil_status_fwconnecting, wil->status)) {
@ -51,6 +70,12 @@ int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime)
rc = -EBUSY;
goto out;
}
/* Runtime pm not supported in case the interface is up */
if (is_runtime) {
wil_dbg_pm(wil, "STA-like interface\n");
rc = -EBUSY;
goto out;
}
break;
/* AP-like interface - can't suspend */
default:
@ -158,7 +183,7 @@ static int wil_suspend_keep_radio_on(struct wil6210_priv *wil)
break;
wil_err(wil,
"TO waiting for idle RX, suspend failed\n");
wil->suspend_stats.failed_suspends++;
wil->suspend_stats.r_on.failed_suspends++;
goto resume_after_fail;
}
wil_dbg_ratelimited(wil, "rx vring is not empty -> NAPI\n");
@ -174,7 +199,7 @@ static int wil_suspend_keep_radio_on(struct wil6210_priv *wil)
*/
if (!wil_is_wmi_idle(wil)) {
wil_err(wil, "suspend failed due to pending WMI events\n");
wil->suspend_stats.failed_suspends++;
wil->suspend_stats.r_on.failed_suspends++;
goto resume_after_fail;
}
@ -188,7 +213,7 @@ static int wil_suspend_keep_radio_on(struct wil6210_priv *wil)
if (rc) {
wil_err(wil, "platform device failed to suspend (%d)\n",
rc);
wil->suspend_stats.failed_suspends++;
wil->suspend_stats.r_on.failed_suspends++;
wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
wil_unmask_irq(wil);
goto resume_after_fail;
@ -235,6 +260,7 @@ static int wil_suspend_radio_off(struct wil6210_priv *wil)
rc = wil_down(wil);
if (rc) {
wil_err(wil, "wil_down : %d\n", rc);
wil->suspend_stats.r_off.failed_suspends++;
goto out;
}
}
@ -247,6 +273,7 @@ static int wil_suspend_radio_off(struct wil6210_priv *wil)
rc = wil->platform_ops.suspend(wil->platform_handle, false);
if (rc) {
wil_enable_irq(wil);
wil->suspend_stats.r_off.failed_suspends++;
goto out;
}
}
@ -279,12 +306,9 @@ static int wil_resume_radio_off(struct wil6210_priv *wil)
return rc;
}
int wil_suspend(struct wil6210_priv *wil, bool is_runtime)
int wil_suspend(struct wil6210_priv *wil, bool is_runtime, bool keep_radio_on)
{
int rc = 0;
struct net_device *ndev = wil_to_ndev(wil);
bool keep_radio_on = ndev->flags & IFF_UP &&
wil->keep_radio_on_during_sleep;
wil_dbg_pm(wil, "suspend: %s\n", is_runtime ? "runtime" : "system");
@ -301,19 +325,12 @@ int wil_suspend(struct wil6210_priv *wil, bool is_runtime)
wil_dbg_pm(wil, "suspend: %s => %d\n",
is_runtime ? "runtime" : "system", rc);
if (!rc)
wil->suspend_stats.suspend_start_time = ktime_get();
return rc;
}
int wil_resume(struct wil6210_priv *wil, bool is_runtime)
int wil_resume(struct wil6210_priv *wil, bool is_runtime, bool keep_radio_on)
{
int rc = 0;
struct net_device *ndev = wil_to_ndev(wil);
bool keep_radio_on = ndev->flags & IFF_UP &&
wil->keep_radio_on_during_sleep;
unsigned long long suspend_time_usec = 0;
wil_dbg_pm(wil, "resume: %s\n", is_runtime ? "runtime" : "system");
@ -331,20 +348,49 @@ int wil_resume(struct wil6210_priv *wil, bool is_runtime)
else
rc = wil_resume_radio_off(wil);
if (rc)
goto out;
suspend_time_usec =
ktime_to_us(ktime_sub(ktime_get(),
wil->suspend_stats.suspend_start_time));
wil->suspend_stats.total_suspend_time += suspend_time_usec;
if (suspend_time_usec < wil->suspend_stats.min_suspend_time)
wil->suspend_stats.min_suspend_time = suspend_time_usec;
if (suspend_time_usec > wil->suspend_stats.max_suspend_time)
wil->suspend_stats.max_suspend_time = suspend_time_usec;
out:
wil_dbg_pm(wil, "resume: %s => %d, suspend time %lld usec\n",
is_runtime ? "runtime" : "system", rc, suspend_time_usec);
wil_dbg_pm(wil, "resume: %s => %d\n", is_runtime ? "runtime" : "system",
rc);
return rc;
}
void wil_pm_runtime_allow(struct wil6210_priv *wil)
{
struct device *dev = wil_to_dev(wil);
pm_runtime_put_noidle(dev);
pm_runtime_set_autosuspend_delay(dev, WIL6210_AUTOSUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(dev);
pm_runtime_allow(dev);
}
void wil_pm_runtime_forbid(struct wil6210_priv *wil)
{
struct device *dev = wil_to_dev(wil);
pm_runtime_forbid(dev);
pm_runtime_get_noresume(dev);
}
int wil_pm_runtime_get(struct wil6210_priv *wil)
{
int rc;
struct device *dev = wil_to_dev(wil);
rc = pm_runtime_get_sync(dev);
if (rc < 0) {
wil_err(wil, "pm_runtime_get_sync() failed, rc = %d\n", rc);
pm_runtime_put_noidle(dev);
return rc;
}
return 0;
}
void wil_pm_runtime_put(struct wil6210_priv *wil)
{
struct device *dev = wil_to_dev(wil);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
}

View file

@ -82,18 +82,18 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1)
*/
#define WIL_MAX_MPDU_OVERHEAD (62)
struct wil_suspend_stats {
struct wil_suspend_count_stats {
unsigned long successful_suspends;
unsigned long failed_suspends;
unsigned long successful_resumes;
unsigned long failed_suspends;
unsigned long failed_resumes;
unsigned long rejected_by_device;
};
struct wil_suspend_stats {
struct wil_suspend_count_stats r_off;
struct wil_suspend_count_stats r_on;
unsigned long rejected_by_device; /* only radio on */
unsigned long rejected_by_host;
unsigned long long total_suspend_time;
unsigned long long min_suspend_time;
unsigned long long max_suspend_time;
ktime_t collection_start;
ktime_t suspend_start_time;
};
/* Calculate MAC buffer size for the firmware. It includes all overhead,
@ -616,6 +616,16 @@ struct blink_on_off_time {
u32 off_ms;
};
struct wil_debugfs_iomem_data {
void *offset;
struct wil6210_priv *wil;
};
struct wil_debugfs_data {
struct wil_debugfs_iomem_data *data_arr;
int iomem_data_count;
};
extern struct blink_on_off_time led_blink_time[WIL_LED_TIME_LAST];
extern u8 led_id;
extern u8 led_polarity;
@ -708,6 +718,7 @@ struct wil6210_priv {
u8 abft_len;
u8 wakeup_trigger;
struct wil_suspend_stats suspend_stats;
struct wil_debugfs_data dbg_data;
void *platform_handle;
struct wil_platform_ops platform_ops;
@ -732,9 +743,7 @@ struct wil6210_priv {
int fw_calib_result;
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
struct notifier_block pm_notify;
#endif /* CONFIG_PM_SLEEP */
#endif /* CONFIG_PM */
bool suspend_resp_rcvd;
@ -861,10 +870,12 @@ int wil_up(struct wil6210_priv *wil);
int __wil_up(struct wil6210_priv *wil);
int wil_down(struct wil6210_priv *wil);
int __wil_down(struct wil6210_priv *wil);
void wil_refresh_fw_capabilities(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 wil_set_ethtoolops(struct net_device *ndev);
void __iomem *wmi_buffer_block(struct wil6210_priv *wil, __le32 ptr, u32 size);
void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr);
void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr);
int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr,
@ -999,9 +1010,14 @@ int wil_request_firmware(struct wil6210_priv *wil, const char *name,
bool load);
bool wil_fw_verify_file_exists(struct wil6210_priv *wil, const char *name);
void wil_pm_runtime_allow(struct wil6210_priv *wil);
void wil_pm_runtime_forbid(struct wil6210_priv *wil);
int wil_pm_runtime_get(struct wil6210_priv *wil);
void wil_pm_runtime_put(struct wil6210_priv *wil);
int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime);
int wil_suspend(struct wil6210_priv *wil, bool is_runtime);
int wil_resume(struct wil6210_priv *wil, bool is_runtime);
int wil_suspend(struct wil6210_priv *wil, bool is_runtime, bool keep_radio_on);
int wil_resume(struct wil6210_priv *wil, bool is_runtime, bool keep_radio_on);
bool wil_is_wmi_idle(struct wil6210_priv *wil);
int wmi_resume(struct wil6210_priv *wil);
int wmi_suspend(struct wil6210_priv *wil);

View file

@ -140,13 +140,15 @@ static u32 wmi_addr_remap(u32 x)
/**
* Check address validity for WMI buffer; remap if needed
* @ptr - internal (linker) fw/ucode address
* @size - if non zero, validate the block does not
* exceed the device memory (bar)
*
* Valid buffer should be DWORD aligned
*
* return address for accessing buffer from the host;
* if buffer is not valid, return NULL.
*/
void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_)
void __iomem *wmi_buffer_block(struct wil6210_priv *wil, __le32 ptr_, u32 size)
{
u32 off;
u32 ptr = le32_to_cpu(ptr_);
@ -161,10 +163,17 @@ void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_)
off = HOSTADDR(ptr);
if (off > wil->bar_size - 4)
return NULL;
if (size && ((off + size > wil->bar_size) || (off + size < off)))
return NULL;
return wil->csr + off;
}
void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_)
{
return wmi_buffer_block(wil, ptr_, 0);
}
/**
* Check address validity
*/
@ -198,6 +207,232 @@ int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr,
return 0;
}
static const char *cmdid2name(u16 cmdid)
{
switch (cmdid) {
case WMI_NOTIFY_REQ_CMDID:
return "WMI_NOTIFY_REQ_CMD";
case WMI_START_SCAN_CMDID:
return "WMI_START_SCAN_CMD";
case WMI_CONNECT_CMDID:
return "WMI_CONNECT_CMD";
case WMI_DISCONNECT_CMDID:
return "WMI_DISCONNECT_CMD";
case WMI_SW_TX_REQ_CMDID:
return "WMI_SW_TX_REQ_CMD";
case WMI_GET_RF_SECTOR_PARAMS_CMDID:
return "WMI_GET_RF_SECTOR_PARAMS_CMD";
case WMI_SET_RF_SECTOR_PARAMS_CMDID:
return "WMI_SET_RF_SECTOR_PARAMS_CMD";
case WMI_GET_SELECTED_RF_SECTOR_INDEX_CMDID:
return "WMI_GET_SELECTED_RF_SECTOR_INDEX_CMD";
case WMI_SET_SELECTED_RF_SECTOR_INDEX_CMDID:
return "WMI_SET_SELECTED_RF_SECTOR_INDEX_CMD";
case WMI_BRP_SET_ANT_LIMIT_CMDID:
return "WMI_BRP_SET_ANT_LIMIT_CMD";
case WMI_TOF_SESSION_START_CMDID:
return "WMI_TOF_SESSION_START_CMD";
case WMI_AOA_MEAS_CMDID:
return "WMI_AOA_MEAS_CMD";
case WMI_PMC_CMDID:
return "WMI_PMC_CMD";
case WMI_TOF_GET_TX_RX_OFFSET_CMDID:
return "WMI_TOF_GET_TX_RX_OFFSET_CMD";
case WMI_TOF_SET_TX_RX_OFFSET_CMDID:
return "WMI_TOF_SET_TX_RX_OFFSET_CMD";
case WMI_VRING_CFG_CMDID:
return "WMI_VRING_CFG_CMD";
case WMI_BCAST_VRING_CFG_CMDID:
return "WMI_BCAST_VRING_CFG_CMD";
case WMI_TRAFFIC_SUSPEND_CMDID:
return "WMI_TRAFFIC_SUSPEND_CMD";
case WMI_TRAFFIC_RESUME_CMDID:
return "WMI_TRAFFIC_RESUME_CMD";
case WMI_ECHO_CMDID:
return "WMI_ECHO_CMD";
case WMI_SET_MAC_ADDRESS_CMDID:
return "WMI_SET_MAC_ADDRESS_CMD";
case WMI_LED_CFG_CMDID:
return "WMI_LED_CFG_CMD";
case WMI_PCP_START_CMDID:
return "WMI_PCP_START_CMD";
case WMI_PCP_STOP_CMDID:
return "WMI_PCP_STOP_CMD";
case WMI_SET_SSID_CMDID:
return "WMI_SET_SSID_CMD";
case WMI_GET_SSID_CMDID:
return "WMI_GET_SSID_CMD";
case WMI_SET_PCP_CHANNEL_CMDID:
return "WMI_SET_PCP_CHANNEL_CMD";
case WMI_GET_PCP_CHANNEL_CMDID:
return "WMI_GET_PCP_CHANNEL_CMD";
case WMI_P2P_CFG_CMDID:
return "WMI_P2P_CFG_CMD";
case WMI_START_LISTEN_CMDID:
return "WMI_START_LISTEN_CMD";
case WMI_START_SEARCH_CMDID:
return "WMI_START_SEARCH_CMD";
case WMI_DISCOVERY_STOP_CMDID:
return "WMI_DISCOVERY_STOP_CMD";
case WMI_DELETE_CIPHER_KEY_CMDID:
return "WMI_DELETE_CIPHER_KEY_CMD";
case WMI_ADD_CIPHER_KEY_CMDID:
return "WMI_ADD_CIPHER_KEY_CMD";
case WMI_SET_APPIE_CMDID:
return "WMI_SET_APPIE_CMD";
case WMI_CFG_RX_CHAIN_CMDID:
return "WMI_CFG_RX_CHAIN_CMD";
case WMI_TEMP_SENSE_CMDID:
return "WMI_TEMP_SENSE_CMD";
case WMI_DEL_STA_CMDID:
return "WMI_DEL_STA_CMD";
case WMI_DISCONNECT_STA_CMDID:
return "WMI_DISCONNECT_STA_CMD";
case WMI_VRING_BA_EN_CMDID:
return "WMI_VRING_BA_EN_CMD";
case WMI_VRING_BA_DIS_CMDID:
return "WMI_VRING_BA_DIS_CMD";
case WMI_RCP_DELBA_CMDID:
return "WMI_RCP_DELBA_CMD";
case WMI_RCP_ADDBA_RESP_CMDID:
return "WMI_RCP_ADDBA_RESP_CMD";
case WMI_PS_DEV_PROFILE_CFG_CMDID:
return "WMI_PS_DEV_PROFILE_CFG_CMD";
case WMI_SET_MGMT_RETRY_LIMIT_CMDID:
return "WMI_SET_MGMT_RETRY_LIMIT_CMD";
case WMI_GET_MGMT_RETRY_LIMIT_CMDID:
return "WMI_GET_MGMT_RETRY_LIMIT_CMD";
case WMI_ABORT_SCAN_CMDID:
return "WMI_ABORT_SCAN_CMD";
case WMI_NEW_STA_CMDID:
return "WMI_NEW_STA_CMD";
case WMI_SET_THERMAL_THROTTLING_CFG_CMDID:
return "WMI_SET_THERMAL_THROTTLING_CFG_CMD";
case WMI_GET_THERMAL_THROTTLING_CFG_CMDID:
return "WMI_GET_THERMAL_THROTTLING_CFG_CMD";
case WMI_LINK_MAINTAIN_CFG_WRITE_CMDID:
return "WMI_LINK_MAINTAIN_CFG_WRITE_CMD";
case WMI_LO_POWER_CALIB_FROM_OTP_CMDID:
return "WMI_LO_POWER_CALIB_FROM_OTP_CMD";
default:
return "Untracked CMD";
}
}
static const char *eventid2name(u16 eventid)
{
switch (eventid) {
case WMI_NOTIFY_REQ_DONE_EVENTID:
return "WMI_NOTIFY_REQ_DONE_EVENT";
case WMI_DISCONNECT_EVENTID:
return "WMI_DISCONNECT_EVENT";
case WMI_SW_TX_COMPLETE_EVENTID:
return "WMI_SW_TX_COMPLETE_EVENT";
case WMI_GET_RF_SECTOR_PARAMS_DONE_EVENTID:
return "WMI_GET_RF_SECTOR_PARAMS_DONE_EVENT";
case WMI_SET_RF_SECTOR_PARAMS_DONE_EVENTID:
return "WMI_SET_RF_SECTOR_PARAMS_DONE_EVENT";
case WMI_GET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID:
return "WMI_GET_SELECTED_RF_SECTOR_INDEX_DONE_EVENT";
case WMI_SET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID:
return "WMI_SET_SELECTED_RF_SECTOR_INDEX_DONE_EVENT";
case WMI_BRP_SET_ANT_LIMIT_EVENTID:
return "WMI_BRP_SET_ANT_LIMIT_EVENT";
case WMI_FW_READY_EVENTID:
return "WMI_FW_READY_EVENT";
case WMI_TRAFFIC_RESUME_EVENTID:
return "WMI_TRAFFIC_RESUME_EVENT";
case WMI_TOF_GET_TX_RX_OFFSET_EVENTID:
return "WMI_TOF_GET_TX_RX_OFFSET_EVENT";
case WMI_TOF_SET_TX_RX_OFFSET_EVENTID:
return "WMI_TOF_SET_TX_RX_OFFSET_EVENT";
case WMI_VRING_CFG_DONE_EVENTID:
return "WMI_VRING_CFG_DONE_EVENT";
case WMI_READY_EVENTID:
return "WMI_READY_EVENT";
case WMI_RX_MGMT_PACKET_EVENTID:
return "WMI_RX_MGMT_PACKET_EVENT";
case WMI_TX_MGMT_PACKET_EVENTID:
return "WMI_TX_MGMT_PACKET_EVENT";
case WMI_SCAN_COMPLETE_EVENTID:
return "WMI_SCAN_COMPLETE_EVENT";
case WMI_ACS_PASSIVE_SCAN_COMPLETE_EVENTID:
return "WMI_ACS_PASSIVE_SCAN_COMPLETE_EVENT";
case WMI_CONNECT_EVENTID:
return "WMI_CONNECT_EVENT";
case WMI_EAPOL_RX_EVENTID:
return "WMI_EAPOL_RX_EVENT";
case WMI_BA_STATUS_EVENTID:
return "WMI_BA_STATUS_EVENT";
case WMI_RCP_ADDBA_REQ_EVENTID:
return "WMI_RCP_ADDBA_REQ_EVENT";
case WMI_DELBA_EVENTID:
return "WMI_DELBA_EVENT";
case WMI_VRING_EN_EVENTID:
return "WMI_VRING_EN_EVENT";
case WMI_DATA_PORT_OPEN_EVENTID:
return "WMI_DATA_PORT_OPEN_EVENT";
case WMI_AOA_MEAS_EVENTID:
return "WMI_AOA_MEAS_EVENT";
case WMI_TOF_SESSION_END_EVENTID:
return "WMI_TOF_SESSION_END_EVENT";
case WMI_TOF_GET_CAPABILITIES_EVENTID:
return "WMI_TOF_GET_CAPABILITIES_EVENT";
case WMI_TOF_SET_LCR_EVENTID:
return "WMI_TOF_SET_LCR_EVENT";
case WMI_TOF_SET_LCI_EVENTID:
return "WMI_TOF_SET_LCI_EVENT";
case WMI_TOF_FTM_PER_DEST_RES_EVENTID:
return "WMI_TOF_FTM_PER_DEST_RES_EVENT";
case WMI_TOF_CHANNEL_INFO_EVENTID:
return "WMI_TOF_CHANNEL_INFO_EVENT";
case WMI_TRAFFIC_SUSPEND_EVENTID:
return "WMI_TRAFFIC_SUSPEND_EVENT";
case WMI_ECHO_RSP_EVENTID:
return "WMI_ECHO_RSP_EVENT";
case WMI_LED_CFG_DONE_EVENTID:
return "WMI_LED_CFG_DONE_EVENT";
case WMI_PCP_STARTED_EVENTID:
return "WMI_PCP_STARTED_EVENT";
case WMI_PCP_STOPPED_EVENTID:
return "WMI_PCP_STOPPED_EVENT";
case WMI_GET_SSID_EVENTID:
return "WMI_GET_SSID_EVENT";
case WMI_GET_PCP_CHANNEL_EVENTID:
return "WMI_GET_PCP_CHANNEL_EVENT";
case WMI_P2P_CFG_DONE_EVENTID:
return "WMI_P2P_CFG_DONE_EVENT";
case WMI_LISTEN_STARTED_EVENTID:
return "WMI_LISTEN_STARTED_EVENT";
case WMI_SEARCH_STARTED_EVENTID:
return "WMI_SEARCH_STARTED_EVENT";
case WMI_DISCOVERY_STOPPED_EVENTID:
return "WMI_DISCOVERY_STOPPED_EVENT";
case WMI_CFG_RX_CHAIN_DONE_EVENTID:
return "WMI_CFG_RX_CHAIN_DONE_EVENT";
case WMI_TEMP_SENSE_DONE_EVENTID:
return "WMI_TEMP_SENSE_DONE_EVENT";
case WMI_RCP_ADDBA_RESP_SENT_EVENTID:
return "WMI_RCP_ADDBA_RESP_SENT_EVENT";
case WMI_PS_DEV_PROFILE_CFG_EVENTID:
return "WMI_PS_DEV_PROFILE_CFG_EVENT";
case WMI_SET_MGMT_RETRY_LIMIT_EVENTID:
return "WMI_SET_MGMT_RETRY_LIMIT_EVENT";
case WMI_GET_MGMT_RETRY_LIMIT_EVENTID:
return "WMI_GET_MGMT_RETRY_LIMIT_EVENT";
case WMI_SET_THERMAL_THROTTLING_CFG_EVENTID:
return "WMI_SET_THERMAL_THROTTLING_CFG_EVENT";
case WMI_GET_THERMAL_THROTTLING_CFG_EVENTID:
return "WMI_GET_THERMAL_THROTTLING_CFG_EVENT";
case WMI_LINK_MAINTAIN_CFG_WRITE_DONE_EVENTID:
return "WMI_LINK_MAINTAIN_CFG_WRITE_DONE_EVENT";
case WMI_LO_POWER_CALIB_FROM_OTP_EVENTID:
return "WMI_LO_POWER_CALIB_FROM_OTP_EVENT";
default:
return "Untracked EVENT";
}
}
static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
{
struct {
@ -222,7 +457,7 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
uint retry;
int rc = 0;
if (sizeof(cmd) + len > r->entry_size) {
if (len > r->entry_size - sizeof(cmd)) {
wil_err(wil, "WMI size too large: %d bytes, max is %d\n",
(int)(sizeof(cmd) + len), r->entry_size);
return -ERANGE;
@ -294,7 +529,8 @@ 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_dbg_wmi(wil, "sending %s (0x%04x) [%d]\n",
cmdid2name(cmdid), 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,
@ -963,8 +1199,8 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
}
spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
wil_dbg_wmi(wil, "WMI event 0x%04x MID %d @%d msec\n",
id, wmi->mid, tstamp);
wil_dbg_wmi(wil, "recv %s (0x%04x) MID %d @%d msec\n",
eventid2name(id), id, wmi->mid, tstamp);
trace_wil6210_wmi_event(wmi, &wmi[1],
len - sizeof(*wmi));
}
@ -1380,8 +1616,14 @@ 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);
struct wmi_set_appie_cmd *cmd;
if (len < ie_len) {
rc = -EINVAL;
goto out;
}
cmd = kzalloc(len, GFP_KERNEL);
if (!cmd) {
rc = -ENOMEM;
goto out;
@ -1801,6 +2043,16 @@ void wmi_event_flush(struct wil6210_priv *wil)
spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
}
static const char *suspend_status2name(u8 status)
{
switch (status) {
case WMI_TRAFFIC_SUSPEND_REJECTED_LINK_NOT_IDLE:
return "LINK_NOT_IDLE";
default:
return "Untracked status";
}
}
int wmi_suspend(struct wil6210_priv *wil)
{
int rc;
@ -1816,7 +2068,7 @@ int wmi_suspend(struct wil6210_priv *wil)
wil->suspend_resp_rcvd = false;
wil->suspend_resp_comp = false;
reply.evt.status = WMI_TRAFFIC_SUSPEND_REJECTED;
reply.evt.status = WMI_TRAFFIC_SUSPEND_REJECTED_LINK_NOT_IDLE;
rc = wmi_call(wil, WMI_TRAFFIC_SUSPEND_CMDID, &cmd, sizeof(cmd),
WMI_TRAFFIC_SUSPEND_EVENTID, &reply, sizeof(reply),
@ -1848,8 +2100,9 @@ int wmi_suspend(struct wil6210_priv *wil)
}
wil_dbg_wmi(wil, "suspend_response_completed rcvd\n");
if (reply.evt.status == WMI_TRAFFIC_SUSPEND_REJECTED) {
wil_dbg_pm(wil, "device rejected the suspend\n");
if (reply.evt.status != WMI_TRAFFIC_SUSPEND_APPROVED) {
wil_dbg_pm(wil, "device rejected the suspend, %s\n",
suspend_status2name(reply.evt.status));
wil->suspend_stats.rejected_by_device++;
}
rc = reply.evt.status;
@ -1861,21 +2114,50 @@ int wmi_suspend(struct wil6210_priv *wil)
return rc;
}
static void resume_triggers2string(u32 triggers, char *string, int str_size)
{
string[0] = '\0';
if (!triggers) {
strlcat(string, " UNKNOWN", str_size);
return;
}
if (triggers & WMI_RESUME_TRIGGER_HOST)
strlcat(string, " HOST", str_size);
if (triggers & WMI_RESUME_TRIGGER_UCAST_RX)
strlcat(string, " UCAST_RX", str_size);
if (triggers & WMI_RESUME_TRIGGER_BCAST_RX)
strlcat(string, " BCAST_RX", str_size);
if (triggers & WMI_RESUME_TRIGGER_WMI_EVT)
strlcat(string, " WMI_EVT", str_size);
}
int wmi_resume(struct wil6210_priv *wil)
{
int rc;
char string[100];
struct {
struct wmi_cmd_hdr wmi;
struct wmi_traffic_resume_event evt;
} __packed reply;
reply.evt.status = WMI_TRAFFIC_RESUME_FAILED;
reply.evt.resume_triggers = WMI_RESUME_TRIGGER_UNKNOWN;
rc = wmi_call(wil, WMI_TRAFFIC_RESUME_CMDID, NULL, 0,
WMI_TRAFFIC_RESUME_EVENTID, &reply, sizeof(reply),
WIL_WAIT_FOR_SUSPEND_RESUME_COMP);
if (rc)
return rc;
resume_triggers2string(le32_to_cpu(reply.evt.resume_triggers), string,
sizeof(string));
wil_dbg_pm(wil, "device resume %s, resume triggers:%s (0x%x)\n",
reply.evt.status ? "failed" : "passed", string,
le32_to_cpu(reply.evt.resume_triggers));
return reply.evt.status;
}
@ -1906,8 +2188,8 @@ static void wmi_event_handle(struct wil6210_priv *wil,
void *evt_data = (void *)(&wmi[1]);
u16 id = le16_to_cpu(wmi->command_id);
wil_dbg_wmi(wil, "Handle WMI 0x%04x (reply_id 0x%04x)\n",
id, wil->reply_id);
wil_dbg_wmi(wil, "Handle %s (0x%04x) (reply_id 0x%04x)\n",
eventid2name(id), id, wil->reply_id);
/* check if someone waits for this event */
if (wil->reply_id && wil->reply_id == id) {
WARN_ON(wil->reply_buf);

View file

@ -2267,8 +2267,8 @@ struct wmi_link_maintain_cfg_read_done_event {
} __packed;
enum wmi_traffic_suspend_status {
WMI_TRAFFIC_SUSPEND_APPROVED = 0x0,
WMI_TRAFFIC_SUSPEND_REJECTED = 0x1,
WMI_TRAFFIC_SUSPEND_APPROVED = 0x0,
WMI_TRAFFIC_SUSPEND_REJECTED_LINK_NOT_IDLE = 0x1,
};
/* WMI_TRAFFIC_SUSPEND_EVENTID */
@ -2282,10 +2282,21 @@ enum wmi_traffic_resume_status {
WMI_TRAFFIC_RESUME_FAILED = 0x1,
};
enum wmi_resume_trigger {
WMI_RESUME_TRIGGER_UNKNOWN = 0x0,
WMI_RESUME_TRIGGER_HOST = 0x1,
WMI_RESUME_TRIGGER_UCAST_RX = 0x2,
WMI_RESUME_TRIGGER_BCAST_RX = 0x4,
WMI_RESUME_TRIGGER_WMI_EVT = 0x8,
};
/* WMI_TRAFFIC_RESUME_EVENTID */
struct wmi_traffic_resume_event {
/* enum wmi_traffic_resume_status_e */
/* enum wmi_traffic_resume_status */
u8 status;
u8 reserved[3];
/* enum wmi_resume_trigger bitmap */
__le32 resume_triggers;
} __packed;
/* Power Save command completion status codes */