diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index f3ec13b80b20..830b7fe466f3 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -1,6 +1,7 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. + * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -2439,7 +2440,7 @@ static int ath10k_core_probe_fw(struct ath10k *ar) ret = ath10k_hif_power_up(ar); if (ret) { - ath10k_err(ar, "could not start pci hif (%d)\n", ret); + ath10k_err(ar, "could not power on hif bus (%d)\n", ret); return ret; } diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index fe6b30356d3b..73712c830be7 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. + * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -324,6 +325,27 @@ struct ath10k_tpc_stats { struct ath10k_tpc_table tpc_table[WMI_TPC_FLAG]; }; +struct ath10k_tpc_table_final { + u32 pream_idx[WMI_TPC_FINAL_RATE_MAX]; + u8 rate_code[WMI_TPC_FINAL_RATE_MAX]; + char tpc_value[WMI_TPC_FINAL_RATE_MAX][WMI_TPC_TX_N_CHAIN * WMI_TPC_BUF_SIZE]; +}; + +struct ath10k_tpc_stats_final { + u32 reg_domain; + u32 chan_freq; + u32 phy_mode; + u32 twice_antenna_reduction; + u32 twice_max_rd_power; + s32 twice_antenna_gain; + u32 power_limit; + u32 num_tx_chain; + u32 ctl; + u32 rate_max; + u8 flag[WMI_TPC_FLAG]; + struct ath10k_tpc_table_final tpc_table_final[WMI_TPC_FLAG]; +}; + struct ath10k_dfs_stats { u32 phy_errors; u32 pulses_total; @@ -354,6 +376,45 @@ struct ath10k_txq { unsigned long num_push_allowed; }; +enum ath10k_pkt_rx_err { + ATH10K_PKT_RX_ERR_FCS, + ATH10K_PKT_RX_ERR_TKIP, + ATH10K_PKT_RX_ERR_CRYPT, + ATH10K_PKT_RX_ERR_PEER_IDX_INVAL, + ATH10K_PKT_RX_ERR_MAX, +}; + +enum ath10k_ampdu_subfrm_num { + ATH10K_AMPDU_SUBFRM_NUM_10, + ATH10K_AMPDU_SUBFRM_NUM_20, + ATH10K_AMPDU_SUBFRM_NUM_30, + ATH10K_AMPDU_SUBFRM_NUM_40, + ATH10K_AMPDU_SUBFRM_NUM_50, + ATH10K_AMPDU_SUBFRM_NUM_60, + ATH10K_AMPDU_SUBFRM_NUM_MORE, + ATH10K_AMPDU_SUBFRM_NUM_MAX, +}; + +enum ath10k_amsdu_subfrm_num { + ATH10K_AMSDU_SUBFRM_NUM_1, + ATH10K_AMSDU_SUBFRM_NUM_2, + ATH10K_AMSDU_SUBFRM_NUM_3, + ATH10K_AMSDU_SUBFRM_NUM_4, + ATH10K_AMSDU_SUBFRM_NUM_MORE, + ATH10K_AMSDU_SUBFRM_NUM_MAX, +}; + +struct ath10k_sta_tid_stats { + unsigned long int rx_pkt_from_fw; + unsigned long int rx_pkt_unchained; + unsigned long int rx_pkt_drop_chained; + unsigned long int rx_pkt_drop_filter; + unsigned long int rx_pkt_err[ATH10K_PKT_RX_ERR_MAX]; + unsigned long int rx_pkt_queued_for_mac; + unsigned long int rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_MAX]; + unsigned long int rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_MAX]; +}; + struct ath10k_sta { struct ath10k_vif *arvif; @@ -371,6 +432,9 @@ struct ath10k_sta { #ifdef CONFIG_MAC80211_DEBUGFS /* protected by conf_mutex */ bool aggr_mode; + + /* Protected with ar->data_lock */ + struct ath10k_sta_tid_stats tid_stats[IEEE80211_NUM_TIDS + 1]; #endif }; @@ -487,6 +551,7 @@ struct ath10k_debug { /* used for tpc-dump storage, protected by data-lock */ struct ath10k_tpc_stats *tpc_stats; + struct ath10k_tpc_stats_final *tpc_stats_final; struct completion tpc_complete; @@ -1019,6 +1084,8 @@ struct ath10k { void *ce_priv; + u32 sta_tid_stats_mask; + /* must be last */ u8 drv_priv[0] __aligned(sizeof(void *)); }; diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 554cd7856cb6..bac832ce1873 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -1480,6 +1480,19 @@ void ath10k_debug_tpc_stats_process(struct ath10k *ar, spin_unlock_bh(&ar->data_lock); } +void +ath10k_debug_tpc_stats_final_process(struct ath10k *ar, + struct ath10k_tpc_stats_final *tpc_stats) +{ + spin_lock_bh(&ar->data_lock); + + kfree(ar->debug.tpc_stats_final); + ar->debug.tpc_stats_final = tpc_stats; + complete(&ar->debug.tpc_complete); + + spin_unlock_bh(&ar->data_lock); +} + static void ath10k_tpc_stats_print(struct ath10k_tpc_stats *tpc_stats, unsigned int j, char *buf, size_t *len) { @@ -2143,6 +2156,137 @@ static const struct file_operations fops_fw_checksums = { .llseek = default_llseek, }; +static ssize_t ath10k_sta_tid_stats_mask_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + char buf[32]; + size_t len; + + len = scnprintf(buf, sizeof(buf), "0x%08x\n", ar->sta_tid_stats_mask); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath10k_sta_tid_stats_mask_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + char buf[32]; + ssize_t len; + u32 mask; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtoint(buf, 0, &mask)) + return -EINVAL; + + ar->sta_tid_stats_mask = mask; + + return len; +} + +static const struct file_operations fops_sta_tid_stats_mask = { + .read = ath10k_sta_tid_stats_mask_read, + .write = ath10k_sta_tid_stats_mask_write, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int ath10k_debug_tpc_stats_final_request(struct ath10k *ar) +{ + int ret; + unsigned long time_left; + + lockdep_assert_held(&ar->conf_mutex); + + reinit_completion(&ar->debug.tpc_complete); + + ret = ath10k_wmi_pdev_get_tpc_table_cmdid(ar, WMI_TPC_CONFIG_PARAM); + if (ret) { + ath10k_warn(ar, "failed to request tpc table cmdid: %d\n", ret); + return ret; + } + + time_left = wait_for_completion_timeout(&ar->debug.tpc_complete, + 1 * HZ); + if (time_left == 0) + return -ETIMEDOUT; + + return 0; +} + +static int ath10k_tpc_stats_final_open(struct inode *inode, struct file *file) +{ + struct ath10k *ar = inode->i_private; + void *buf; + int ret; + + mutex_lock(&ar->conf_mutex); + + if (ar->state != ATH10K_STATE_ON) { + ret = -ENETDOWN; + goto err_unlock; + } + + buf = vmalloc(ATH10K_TPC_CONFIG_BUF_SIZE); + if (!buf) { + ret = -ENOMEM; + goto err_unlock; + } + + ret = ath10k_debug_tpc_stats_final_request(ar); + if (ret) { + ath10k_warn(ar, "failed to request tpc stats final: %d\n", + ret); + goto err_free; + } + + ath10k_tpc_stats_fill(ar, ar->debug.tpc_stats, buf); + file->private_data = buf; + + mutex_unlock(&ar->conf_mutex); + return 0; + +err_free: + vfree(buf); + +err_unlock: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static int ath10k_tpc_stats_final_release(struct inode *inode, + struct file *file) +{ + vfree(file->private_data); + + return 0; +} + +static ssize_t ath10k_tpc_stats_final_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char *buf = file->private_data; + unsigned int len = strlen(buf); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_tpc_stats_final = { + .open = ath10k_tpc_stats_final_open, + .release = ath10k_tpc_stats_final_release, + .read = ath10k_tpc_stats_final_read, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + int ath10k_debug_create(struct ath10k *ar) { ar->debug.cal_data = vzalloc(ATH10K_DEBUG_CAL_DATA_LEN); @@ -2258,6 +2402,16 @@ int ath10k_debug_register(struct ath10k *ar) debugfs_create_file("fw_checksums", 0400, ar->debug.debugfs_phy, ar, &fops_fw_checksums); + if (IS_ENABLED(CONFIG_MAC80211_DEBUGFS)) + debugfs_create_file("sta_tid_stats_mask", 0600, + ar->debug.debugfs_phy, + ar, &fops_sta_tid_stats_mask); + + if (test_bit(WMI_SERVICE_TPC_STATS_FINAL, ar->wmi.svc_map)) + debugfs_create_file("tpc_stats_final", 0400, + ar->debug.debugfs_phy, ar, + &fops_tpc_stats_final); + return 0; } diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h index e54308889e59..0afca5c106b6 100644 --- a/drivers/net/wireless/ath/ath10k/debug.h +++ b/drivers/net/wireless/ath/ath10k/debug.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. + * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -101,6 +102,9 @@ void ath10k_debug_unregister(struct ath10k *ar); void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb); void ath10k_debug_tpc_stats_process(struct ath10k *ar, struct ath10k_tpc_stats *tpc_stats); +void +ath10k_debug_tpc_stats_final_process(struct ath10k *ar, + struct ath10k_tpc_stats_final *tpc_stats); void ath10k_debug_dbglog_add(struct ath10k *ar, u8 *buffer, int len); #define ATH10K_DFS_STAT_INC(ar, c) (ar->debug.dfs_stats.c++) @@ -164,6 +168,13 @@ static inline void ath10k_debug_tpc_stats_process(struct ath10k *ar, kfree(tpc_stats); } +static inline void +ath10k_debug_tpc_stats_final_process(struct ath10k *ar, + struct ath10k_tpc_stats_final *tpc_stats) +{ + kfree(tpc_stats); +} + static inline void ath10k_debug_dbglog_add(struct ath10k *ar, u8 *buffer, int len) { @@ -191,12 +202,42 @@ 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_update_rx_tid_stats(struct ath10k *ar, u8 *first_hdr, + unsigned long int num_msdus, + enum ath10k_pkt_rx_err err, + unsigned long int unchain_cnt, + unsigned long int drop_cnt, + unsigned long int drop_cnt_filter, + unsigned long int queued_msdus); +void ath10k_sta_update_rx_tid_stats_ampdu(struct ath10k *ar, + u16 peer_id, u8 tid, + struct htt_rx_indication_mpdu_range *ranges, + int num_ranges); #else static inline void ath10k_sta_update_rx_duration(struct ath10k *ar, struct ath10k_fw_stats *stats) { } + +static inline +void ath10k_sta_update_rx_tid_stats(struct ath10k *ar, u8 *first_hdr, + unsigned long int num_msdus, + enum ath10k_pkt_rx_err err, + unsigned long int unchain_cnt, + unsigned long int drop_cnt, + unsigned long int drop_cnt_filter, + unsigned long int queued_msdus) +{ +} + +static inline +void ath10k_sta_update_rx_tid_stats_ampdu(struct ath10k *ar, + u16 peer_id, u8 tid, + struct htt_rx_indication_mpdu_range *ranges, + int num_ranges) +{ +} #endif /* CONFIG_MAC80211_DEBUGFS */ #ifdef CONFIG_ATH10K_DEBUG diff --git a/drivers/net/wireless/ath/ath10k/debugfs_sta.c b/drivers/net/wireless/ath/ath10k/debugfs_sta.c index b260b09dd4d3..8f688f136c22 100644 --- a/drivers/net/wireless/ath/ath10k/debugfs_sta.c +++ b/drivers/net/wireless/ath/ath10k/debugfs_sta.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2014-2017 Qualcomm Atheros, Inc. + * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -16,8 +17,125 @@ #include "core.h" #include "wmi-ops.h" +#include "txrx.h" #include "debug.h" +static void ath10k_rx_stats_update_amsdu_subfrm(struct ath10k *ar, + struct ath10k_sta_tid_stats *stats, + u32 msdu_count) +{ + if (msdu_count == 1) + stats->rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_1]++; + else if (msdu_count == 2) + stats->rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_2]++; + else if (msdu_count == 3) + stats->rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_3]++; + else if (msdu_count == 4) + stats->rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_4]++; + else if (msdu_count > 4) + stats->rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_MORE]++; +} + +static void ath10k_rx_stats_update_ampdu_subfrm(struct ath10k *ar, + struct ath10k_sta_tid_stats *stats, + u32 mpdu_count) +{ + if (mpdu_count <= 10) + stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_10]++; + else if (mpdu_count <= 20) + stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_20]++; + else if (mpdu_count <= 30) + stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_30]++; + else if (mpdu_count <= 40) + stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_40]++; + else if (mpdu_count <= 50) + stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_50]++; + else if (mpdu_count <= 60) + stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_60]++; + else if (mpdu_count > 60) + stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_MORE]++; +} + +void ath10k_sta_update_rx_tid_stats_ampdu(struct ath10k *ar, u16 peer_id, u8 tid, + struct htt_rx_indication_mpdu_range *ranges, + int num_ranges) +{ + struct ath10k_sta *arsta; + struct ath10k_peer *peer; + int i; + + if (tid > IEEE80211_NUM_TIDS || !(ar->sta_tid_stats_mask & BIT(tid))) + return; + + rcu_read_lock(); + spin_lock_bh(&ar->data_lock); + + peer = ath10k_peer_find_by_id(ar, peer_id); + if (!peer) + goto out; + + arsta = (struct ath10k_sta *)peer->sta->drv_priv; + + for (i = 0; i < num_ranges; i++) + ath10k_rx_stats_update_ampdu_subfrm(ar, + &arsta->tid_stats[tid], + ranges[i].mpdu_count); + +out: + spin_unlock_bh(&ar->data_lock); + rcu_read_unlock(); +} + +void ath10k_sta_update_rx_tid_stats(struct ath10k *ar, u8 *first_hdr, + unsigned long int num_msdus, + enum ath10k_pkt_rx_err err, + unsigned long int unchain_cnt, + unsigned long int drop_cnt, + unsigned long int drop_cnt_filter, + unsigned long int queued_msdus) +{ + struct ieee80211_sta *sta; + struct ath10k_sta *arsta; + struct ieee80211_hdr *hdr; + struct ath10k_sta_tid_stats *stats; + u8 tid = IEEE80211_NUM_TIDS; + bool non_data_frm = false; + + hdr = (struct ieee80211_hdr *)first_hdr; + if (!ieee80211_is_data(hdr->frame_control)) + non_data_frm = true; + + if (ieee80211_is_data_qos(hdr->frame_control)) + tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; + + if (!(ar->sta_tid_stats_mask & BIT(tid)) || non_data_frm) + return; + + rcu_read_lock(); + + sta = ieee80211_find_sta_by_ifaddr(ar->hw, hdr->addr2, NULL); + if (!sta) + goto exit; + + arsta = (struct ath10k_sta *)sta->drv_priv; + + spin_lock_bh(&ar->data_lock); + stats = &arsta->tid_stats[tid]; + stats->rx_pkt_from_fw += num_msdus; + stats->rx_pkt_unchained += unchain_cnt; + stats->rx_pkt_drop_chained += drop_cnt; + stats->rx_pkt_drop_filter += drop_cnt_filter; + if (err != ATH10K_PKT_RX_ERR_MAX) + stats->rx_pkt_err[err] += queued_msdus; + stats->rx_pkt_queued_for_mac += queued_msdus; + ath10k_rx_stats_update_amsdu_subfrm(ar, &arsta->tid_stats[tid], + num_msdus); + spin_unlock_bh(&ar->data_lock); + +exit: + rcu_read_unlock(); +} + static void ath10k_sta_update_extd_stats_rx_duration(struct ath10k *ar, struct ath10k_fw_stats *stats) { @@ -342,6 +460,172 @@ static const struct file_operations fops_peer_debug_trigger = { .llseek = default_llseek, }; +static char *get_err_str(enum ath10k_pkt_rx_err i) +{ + switch (i) { + case ATH10K_PKT_RX_ERR_FCS: + return "fcs_err"; + case ATH10K_PKT_RX_ERR_TKIP: + return "tkip_err"; + case ATH10K_PKT_RX_ERR_CRYPT: + return "crypt_err"; + case ATH10K_PKT_RX_ERR_PEER_IDX_INVAL: + return "peer_idx_inval"; + case ATH10K_PKT_RX_ERR_MAX: + return "unknown"; + } + + return "unknown"; +} + +static char *get_num_ampdu_subfrm_str(enum ath10k_ampdu_subfrm_num i) +{ + switch (i) { + case ATH10K_AMPDU_SUBFRM_NUM_10: + return "upto 10"; + case ATH10K_AMPDU_SUBFRM_NUM_20: + return "11-20"; + case ATH10K_AMPDU_SUBFRM_NUM_30: + return "21-30"; + case ATH10K_AMPDU_SUBFRM_NUM_40: + return "31-40"; + case ATH10K_AMPDU_SUBFRM_NUM_50: + return "41-50"; + case ATH10K_AMPDU_SUBFRM_NUM_60: + return "51-60"; + case ATH10K_AMPDU_SUBFRM_NUM_MORE: + return ">60"; + case ATH10K_AMPDU_SUBFRM_NUM_MAX: + return "0"; + } + + return "0"; +} + +static char *get_num_amsdu_subfrm_str(enum ath10k_amsdu_subfrm_num i) +{ + switch (i) { + case ATH10K_AMSDU_SUBFRM_NUM_1: + return "1"; + case ATH10K_AMSDU_SUBFRM_NUM_2: + return "2"; + case ATH10K_AMSDU_SUBFRM_NUM_3: + return "3"; + case ATH10K_AMSDU_SUBFRM_NUM_4: + return "4"; + case ATH10K_AMSDU_SUBFRM_NUM_MORE: + return ">4"; + case ATH10K_AMSDU_SUBFRM_NUM_MAX: + return "0"; + } + + return "0"; +} + +#define PRINT_TID_STATS(_field, _tabs) \ + do { \ + int k = 0; \ + for (j = 0; j <= IEEE80211_NUM_TIDS; j++) { \ + if (ar->sta_tid_stats_mask & BIT(j)) { \ + len += scnprintf(buf + len, buf_len - len, \ + "[%02d] %-10lu ", \ + j, stats[j]._field); \ + k++; \ + if (k % 8 == 0) { \ + len += scnprintf(buf + len, \ + buf_len - len, "\n"); \ + len += scnprintf(buf + len, \ + buf_len - len, \ + _tabs); \ + } \ + } \ + } \ + len += scnprintf(buf + len, buf_len - len, "\n"); \ + } while (0) + +static ssize_t ath10k_dbg_sta_read_tid_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_sta *sta = file->private_data; + struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv; + struct ath10k *ar = arsta->arvif->ar; + struct ath10k_sta_tid_stats *stats = arsta->tid_stats; + size_t len = 0, buf_len = 1048 * IEEE80211_NUM_TIDS; + char *buf; + int i, j; + ssize_t ret; + + buf = kzalloc(buf_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mutex_lock(&ar->conf_mutex); + + spin_lock_bh(&ar->data_lock); + + len += scnprintf(buf + len, buf_len - len, + "\n\t\tDriver Rx pkt stats per tid, ([tid] count)\n"); + len += scnprintf(buf + len, buf_len - len, + "\t\t------------------------------------------\n"); + len += scnprintf(buf + len, buf_len - len, "MSDUs from FW\t\t\t"); + PRINT_TID_STATS(rx_pkt_from_fw, "\t\t\t\t"); + + len += scnprintf(buf + len, buf_len - len, "MSDUs unchained\t\t\t"); + PRINT_TID_STATS(rx_pkt_unchained, "\t\t\t\t"); + + len += scnprintf(buf + len, buf_len - len, + "MSDUs locally dropped:chained\t"); + PRINT_TID_STATS(rx_pkt_drop_chained, "\t\t\t\t"); + + len += scnprintf(buf + len, buf_len - len, + "MSDUs locally dropped:filtered\t"); + PRINT_TID_STATS(rx_pkt_drop_filter, "\t\t\t\t"); + + len += scnprintf(buf + len, buf_len - len, + "MSDUs queued for mac80211\t"); + PRINT_TID_STATS(rx_pkt_queued_for_mac, "\t\t\t\t"); + + for (i = 0; i < ATH10K_PKT_RX_ERR_MAX; i++) { + len += scnprintf(buf + len, buf_len - len, + "MSDUs with error:%s\t", get_err_str(i)); + PRINT_TID_STATS(rx_pkt_err[i], "\t\t\t\t"); + } + + len += scnprintf(buf + len, buf_len - len, "\n"); + for (i = 0; i < ATH10K_AMPDU_SUBFRM_NUM_MAX; i++) { + len += scnprintf(buf + len, buf_len - len, + "A-MPDU num subframes %s\t", + get_num_ampdu_subfrm_str(i)); + PRINT_TID_STATS(rx_pkt_ampdu[i], "\t\t\t\t"); + } + + len += scnprintf(buf + len, buf_len - len, "\n"); + for (i = 0; i < ATH10K_AMSDU_SUBFRM_NUM_MAX; i++) { + len += scnprintf(buf + len, buf_len - len, + "A-MSDU num subframes %s\t\t", + get_num_amsdu_subfrm_str(i)); + PRINT_TID_STATS(rx_pkt_amsdu[i], "\t\t\t\t"); + } + + spin_unlock_bh(&ar->data_lock); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + kfree(buf); + + mutex_unlock(&ar->conf_mutex); + + return ret; +} + +static const struct file_operations fops_tid_stats_dump = { + .open = simple_open, + .read = ath10k_dbg_sta_read_tid_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct dentry *dir) { @@ -351,4 +635,6 @@ void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, debugfs_create_file("delba", 0200, dir, sta, &fops_delba); debugfs_create_file("peer_debug_trigger", 0600, dir, sta, &fops_peer_debug_trigger); + debugfs_create_file("dump_tid_stats", 0400, dir, sta, + &fops_tid_stats_dump); } diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 6d96f9560950..64996aba1485 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -1,6 +1,7 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. + * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -1502,7 +1503,9 @@ static void ath10k_htt_rx_h_csum_offload(struct sk_buff *msdu) static void ath10k_htt_rx_h_mpdu(struct ath10k *ar, struct sk_buff_head *amsdu, struct ieee80211_rx_status *status, - bool fill_crypt_header) + bool fill_crypt_header, + u8 *rx_hdr, + enum ath10k_pkt_rx_err *err) { struct sk_buff *first; struct sk_buff *last; @@ -1538,6 +1541,9 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar, hdr = (void *)rxd->rx_hdr_status; memcpy(first_hdr, hdr, RX_HTT_HDR_STATUS_LEN); + if (rx_hdr) + memcpy(rx_hdr, hdr, RX_HTT_HDR_STATUS_LEN); + /* Each A-MSDU subframe will use the original header as the base and be * reported as a separate MSDU so strip the A-MSDU bit from QoS Ctl. */ @@ -1581,6 +1587,17 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar, if (has_tkip_err) status->flag |= RX_FLAG_MMIC_ERROR; + if (err) { + if (has_fcs_err) + *err = ATH10K_PKT_RX_ERR_FCS; + else if (has_tkip_err) + *err = ATH10K_PKT_RX_ERR_TKIP; + else if (has_crypto_err) + *err = ATH10K_PKT_RX_ERR_CRYPT; + else if (has_peer_idx_invalid) + *err = ATH10K_PKT_RX_ERR_PEER_IDX_INVAL; + } + /* Firmware reports all necessary management frames via WMI already. * They are not reported to monitor interfaces at all so pass the ones * coming via HTT to monitor interfaces instead. This simplifies @@ -1651,11 +1668,13 @@ static void ath10k_htt_rx_h_enqueue(struct ath10k *ar, } } -static int ath10k_unchain_msdu(struct sk_buff_head *amsdu) +static int ath10k_unchain_msdu(struct sk_buff_head *amsdu, + unsigned long int *unchain_cnt) { struct sk_buff *skb, *first; int space; int total_len = 0; + int amsdu_len = skb_queue_len(amsdu); /* TODO: Might could optimize this by using * skb_try_coalesce or similar method to @@ -1691,11 +1710,16 @@ static int ath10k_unchain_msdu(struct sk_buff_head *amsdu) } __skb_queue_head(amsdu, first); + + *unchain_cnt += amsdu_len - 1; + return 0; } static void ath10k_htt_rx_h_unchain(struct ath10k *ar, - struct sk_buff_head *amsdu) + struct sk_buff_head *amsdu, + unsigned long int *drop_cnt, + unsigned long int *unchain_cnt) { struct sk_buff *first; struct htt_rx_desc *rxd; @@ -1713,11 +1737,12 @@ static void ath10k_htt_rx_h_unchain(struct ath10k *ar, */ if (decap != RX_MSDU_DECAP_RAW || skb_queue_len(amsdu) != 1 + rxd->frag_info.ring2_more_count) { + *drop_cnt += skb_queue_len(amsdu); __skb_queue_purge(amsdu); return; } - ath10k_unchain_msdu(amsdu); + ath10k_unchain_msdu(amsdu, unchain_cnt); } static bool ath10k_htt_rx_amsdu_allowed(struct ath10k *ar, @@ -1743,7 +1768,8 @@ static bool ath10k_htt_rx_amsdu_allowed(struct ath10k *ar, static void ath10k_htt_rx_h_filter(struct ath10k *ar, struct sk_buff_head *amsdu, - struct ieee80211_rx_status *rx_status) + struct ieee80211_rx_status *rx_status, + unsigned long int *drop_cnt) { if (skb_queue_empty(amsdu)) return; @@ -1751,6 +1777,9 @@ static void ath10k_htt_rx_h_filter(struct ath10k *ar, if (ath10k_htt_rx_amsdu_allowed(ar, amsdu, rx_status)) return; + if (drop_cnt) + *drop_cnt += skb_queue_len(amsdu); + __skb_queue_purge(amsdu); } @@ -1760,6 +1789,12 @@ static int ath10k_htt_rx_handle_amsdu(struct ath10k_htt *htt) struct ieee80211_rx_status *rx_status = &htt->rx_status; struct sk_buff_head amsdu; int ret; + unsigned long int drop_cnt = 0; + unsigned long int unchain_cnt = 0; + unsigned long int drop_cnt_filter = 0; + unsigned long int msdus_to_queue, num_msdus; + enum ath10k_pkt_rx_err err = ATH10K_PKT_RX_ERR_MAX; + u8 first_hdr[RX_HTT_HDR_STATUS_LEN]; __skb_queue_head_init(&amsdu); @@ -1781,16 +1816,23 @@ 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 */ if (ret > 0) - ath10k_htt_rx_h_unchain(ar, &amsdu); + ath10k_htt_rx_h_unchain(ar, &amsdu, &drop_cnt, &unchain_cnt); - ath10k_htt_rx_h_filter(ar, &amsdu, rx_status); - ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status, true); + ath10k_htt_rx_h_filter(ar, &amsdu, rx_status, &drop_cnt_filter); + ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status, true, first_hdr, &err); + msdus_to_queue = skb_queue_len(&amsdu); ath10k_htt_rx_h_enqueue(ar, &amsdu, rx_status); + ath10k_sta_update_rx_tid_stats(ar, first_hdr, num_msdus, err, + unchain_cnt, drop_cnt, drop_cnt_filter, + msdus_to_queue); + return 0; } @@ -1801,9 +1843,14 @@ static void ath10k_htt_rx_proc_rx_ind(struct ath10k_htt *htt, struct htt_rx_indication_mpdu_range *mpdu_ranges; int num_mpdu_ranges; int i, mpdu_count = 0; + u16 peer_id; + u8 tid; num_mpdu_ranges = MS(__le32_to_cpu(rx->hdr.info1), HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES); + peer_id = __le16_to_cpu(rx->hdr.peer_id); + tid = MS(rx->hdr.info0, HTT_RX_INDICATION_INFO0_EXT_TID); + mpdu_ranges = htt_rx_ind_get_mpdu_ranges(rx); ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx ind: ", @@ -1815,6 +1862,9 @@ static void ath10k_htt_rx_proc_rx_ind(struct ath10k_htt *htt, mpdu_count += mpdu_ranges[i].mpdu_count; atomic_add(mpdu_count, &htt->num_mpdus_ready); + + ath10k_sta_update_rx_tid_stats_ampdu(ar, peer_id, tid, mpdu_ranges, + num_mpdu_ranges); } static void ath10k_htt_rx_tx_compl_ind(struct ath10k *ar, @@ -2124,8 +2174,9 @@ static int ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb) * should still give an idea about rx rate to the user. */ 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_filter(ar, &amsdu, status, NULL); + ath10k_htt_rx_h_mpdu(ar, &amsdu, status, false, NULL, + NULL); ath10k_htt_rx_h_enqueue(ar, &amsdu, status); break; case -EAGAIN: diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index ebb3f1b046f3..02674ee04435 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1,6 +1,7 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. + * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -3808,6 +3809,7 @@ void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work) { struct ath10k *ar = container_of(work, struct ath10k, wmi_mgmt_tx_work); struct sk_buff *skb; + dma_addr_t paddr; int ret; for (;;) { @@ -3815,11 +3817,27 @@ void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work) if (!skb) break; - ret = ath10k_wmi_mgmt_tx(ar, skb); - if (ret) { - ath10k_warn(ar, "failed to transmit management frame via WMI: %d\n", - ret); - ieee80211_free_txskb(ar->hw, skb); + if (test_bit(ATH10K_FW_FEATURE_MGMT_TX_BY_REF, + ar->running_fw->fw_file.fw_features)) { + paddr = dma_map_single(ar->dev, skb->data, + skb->len, DMA_TO_DEVICE); + if (!paddr) + continue; + ret = ath10k_wmi_mgmt_tx_send(ar, skb, paddr); + if (ret) { + ath10k_warn(ar, "failed to transmit management frame by ref via WMI: %d\n", + ret); + dma_unmap_single(ar->dev, paddr, skb->len, + DMA_FROM_DEVICE); + ieee80211_free_txskb(ar->hw, skb); + } + } else { + ret = ath10k_wmi_mgmt_tx(ar, skb); + if (ret) { + ath10k_warn(ar, "failed to transmit management frame via WMI: %d\n", + ret); + ieee80211_free_txskb(ar->hw, skb); + } } } } @@ -7085,10 +7103,20 @@ static void ath10k_sta_rc_update(struct ieee80211_hw *hw, { struct ath10k *ar = hw->priv; struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv; + struct ath10k_vif *arvif = (void *)vif->drv_priv; + struct ath10k_peer *peer; u32 bw, smps; spin_lock_bh(&ar->data_lock); + peer = ath10k_peer_find(ar, arvif->vdev_id, sta->addr); + if (!peer) { + spin_unlock_bh(&ar->data_lock); + ath10k_warn(ar, "mac sta rc update failed to find peer %pM on vdev %i\n", + sta->addr, arvif->vdev_id); + return; + } + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac sta rc update for %pM changed %08x bw %d nss %d smps %d\n", sta->addr, changed, sta->bandwidth, sta->rx_nss, diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 1b266cd0c2ec..808f3d67ba90 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -2221,7 +2221,7 @@ static int ath10k_pci_get_num_banks(struct ath10k *ar) } break; case QCA9377_1_0_DEVICE_ID: - return 4; + return 9; } ath10k_warn(ar, "unknown number of banks, assuming 1\n"); diff --git a/drivers/net/wireless/ath/ath10k/trace.h b/drivers/net/wireless/ath/ath10k/trace.h index e40edced1d82..7d2fac342150 100644 --- a/drivers/net/wireless/ath/ath10k/trace.h +++ b/drivers/net/wireless/ath/ath10k/trace.h @@ -152,10 +152,9 @@ TRACE_EVENT(ath10k_log_dbg_dump, ); TRACE_EVENT(ath10k_wmi_cmd, - TP_PROTO(struct ath10k *ar, int id, const void *buf, size_t buf_len, - int ret), + TP_PROTO(struct ath10k *ar, int id, const void *buf, size_t buf_len), - TP_ARGS(ar, id, buf, buf_len, ret), + TP_ARGS(ar, id, buf, buf_len), TP_STRUCT__entry( __string(device, dev_name(ar->dev)) @@ -163,7 +162,6 @@ TRACE_EVENT(ath10k_wmi_cmd, __field(unsigned int, id) __field(size_t, buf_len) __dynamic_array(u8, buf, buf_len) - __field(int, ret) ), TP_fast_assign( @@ -171,17 +169,15 @@ TRACE_EVENT(ath10k_wmi_cmd, __assign_str(driver, dev_driver_string(ar->dev)); __entry->id = id; __entry->buf_len = buf_len; - __entry->ret = ret; memcpy(__get_dynamic_array(buf), buf, buf_len); ), TP_printk( - "%s %s id %d len %zu ret %d", + "%s %s id %d len %zu", __get_str(driver), __get_str(device), __entry->id, - __entry->buf_len, - __entry->ret + __entry->buf_len ) ); diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c index 5b3b021526ab..70e23bbf7171 100644 --- a/drivers/net/wireless/ath/ath10k/txrx.c +++ b/drivers/net/wireless/ath/ath10k/txrx.c @@ -102,11 +102,6 @@ int ath10k_txrx_tx_unref(struct ath10k_htt *htt, memset(&info->status, 0, sizeof(info->status)); trace_ath10k_txrx_tx_unref(ar, tx_done->msdu_id); - if (tx_done->status == HTT_TX_COMPL_STATE_DISCARD) { - ieee80211_free_txskb(htt->ar->hw, msdu); - return 0; - } - if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) info->flags |= IEEE80211_TX_STAT_ACK; @@ -117,6 +112,13 @@ int ath10k_txrx_tx_unref(struct ath10k_htt *htt, (info->flags & IEEE80211_TX_CTL_NO_ACK)) info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; + if (tx_done->status == HTT_TX_COMPL_STATE_DISCARD) { + if (info->flags & IEEE80211_TX_CTL_NO_ACK) + info->flags &= ~IEEE80211_TX_STAT_NOACK_TRANSMITTED; + else + info->flags &= ~IEEE80211_TX_STAT_ACK; + } + ieee80211_tx_status(htt->ar->hw, msdu); /* we do not own the msdu anymore */ diff --git a/drivers/net/wireless/ath/ath10k/wmi-ops.h b/drivers/net/wireless/ath/ath10k/wmi-ops.h index 14093cfdc505..c35e45340b4f 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-ops.h +++ b/drivers/net/wireless/ath/ath10k/wmi-ops.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. + * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -125,6 +126,9 @@ struct wmi_ops { enum wmi_force_fw_hang_type type, u32 delay_ms); struct sk_buff *(*gen_mgmt_tx)(struct ath10k *ar, struct sk_buff *skb); + struct sk_buff *(*gen_mgmt_tx_send)(struct ath10k *ar, + struct sk_buff *skb, + dma_addr_t paddr); struct sk_buff *(*gen_dbglog_cfg)(struct ath10k *ar, u64 module_enable, u32 log_level); struct sk_buff *(*gen_pktlog_enable)(struct ath10k *ar, u32 filter); @@ -197,6 +201,9 @@ struct wmi_ops { (struct ath10k *ar, enum wmi_bss_survey_req_type type); struct sk_buff *(*gen_echo)(struct ath10k *ar, u32 value); + struct sk_buff *(*gen_pdev_get_tpc_table_cmdid)(struct ath10k *ar, + u32 param); + }; int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id); @@ -371,13 +378,34 @@ ath10k_wmi_get_txbf_conf_scheme(struct ath10k *ar) return ar->wmi.ops->get_txbf_conf_scheme(ar); } +static inline int +ath10k_wmi_mgmt_tx_send(struct ath10k *ar, struct sk_buff *msdu, + dma_addr_t paddr) +{ + struct sk_buff *skb; + int ret; + + if (!ar->wmi.ops->gen_mgmt_tx_send) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_mgmt_tx_send(ar, msdu, paddr); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + ret = ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->mgmt_tx_send_cmdid); + if (ret) + return ret; + + return 0; +} + static inline int 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; @@ -386,13 +414,8 @@ ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu) if (IS_ERR(skb)) return PTR_ERR(skb); - 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); + ret = ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->mgmt_tx_cmdid); if (ret) return ret; @@ -1425,4 +1448,21 @@ ath10k_wmi_echo(struct ath10k *ar, u32 value) return ath10k_wmi_cmd_send(ar, skb, wmi->cmd->echo_cmdid); } +static inline int +ath10k_wmi_pdev_get_tpc_table_cmdid(struct ath10k *ar, u32 param) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_pdev_get_tpc_table_cmdid) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_pdev_get_tpc_table_cmdid(ar, param); + + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->pdev_get_tpc_table_cmdid); +} + #endif diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index ae77a007ae07..523af3f8bb62 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -1,6 +1,7 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. + * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -2484,19 +2485,19 @@ ath10k_wmi_tlv_op_gen_request_stats(struct ath10k *ar, u32 stats_mask) } static struct sk_buff * -ath10k_wmi_tlv_op_gen_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu) +ath10k_wmi_tlv_op_gen_mgmt_tx_send(struct ath10k *ar, struct sk_buff *msdu, + dma_addr_t paddr) { 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 ath10k_vif *arvif; + u32 buf_len = msdu->len; + struct wmi_tlv *tlv; struct sk_buff *skb; + u32 vdev_id; void *ptr; int len; - u32 buf_len = msdu->len; - struct ath10k_vif *arvif; - dma_addr_t mgmt_frame_dma; - u32 vdev_id; if (!cb->vif) return ERR_PTR(-EINVAL); @@ -2537,12 +2538,7 @@ ath10k_wmi_tlv_op_gen_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu) 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); + cmd->paddr = __cpu_to_le64(paddr); ptr += sizeof(*tlv); ptr += sizeof(*cmd); @@ -3701,7 +3697,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_mgmt_tx_send = ath10k_wmi_tlv_op_gen_mgmt_tx_send, .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, diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 58dc2189ba49..9649bb752bbd 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -1,6 +1,7 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. + * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -196,6 +197,7 @@ static struct wmi_cmd_map wmi_cmd_map = { .mu_cal_start_cmdid = WMI_CMD_UNSUPPORTED, .set_cca_params_cmdid = WMI_CMD_UNSUPPORTED, .pdev_bss_chan_info_request_cmdid = WMI_CMD_UNSUPPORTED, + .pdev_get_tpc_table_cmdid = WMI_CMD_UNSUPPORTED, }; /* 10.X WMI cmd track */ @@ -362,6 +364,7 @@ static struct wmi_cmd_map wmi_10x_cmd_map = { .mu_cal_start_cmdid = WMI_CMD_UNSUPPORTED, .set_cca_params_cmdid = WMI_CMD_UNSUPPORTED, .pdev_bss_chan_info_request_cmdid = WMI_CMD_UNSUPPORTED, + .pdev_get_tpc_table_cmdid = WMI_CMD_UNSUPPORTED, }; /* 10.2.4 WMI cmd track */ @@ -528,6 +531,7 @@ static struct wmi_cmd_map wmi_10_2_4_cmd_map = { .set_cca_params_cmdid = WMI_CMD_UNSUPPORTED, .pdev_bss_chan_info_request_cmdid = WMI_10_2_PDEV_BSS_CHAN_INFO_REQUEST_CMDID, + .pdev_get_tpc_table_cmdid = WMI_CMD_UNSUPPORTED, }; /* 10.4 WMI cmd track */ @@ -1480,6 +1484,7 @@ static struct wmi_cmd_map wmi_10_2_cmd_map = { .pdev_get_ani_cck_config_cmdid = WMI_CMD_UNSUPPORTED, .pdev_get_ani_ofdm_config_cmdid = WMI_CMD_UNSUPPORTED, .pdev_reserve_ast_entry_cmdid = WMI_CMD_UNSUPPORTED, + .pdev_get_tpc_table_cmdid = WMI_CMD_UNSUPPORTED, }; static struct wmi_pdev_param_map wmi_10_4_pdev_param_map = { @@ -1742,8 +1747,8 @@ int ath10k_wmi_cmd_send_nowait(struct ath10k *ar, struct sk_buff *skb, cmd_hdr->cmd_id = __cpu_to_le32(cmd); memset(skb_cb, 0, sizeof(*skb_cb)); + trace_ath10k_wmi_cmd(ar, cmd_id, skb->data, skb->len); ret = ath10k_htc_send(&ar->htc, ar->wmi.eid, skb); - trace_ath10k_wmi_cmd(ar, cmd_id, skb->data, skb->len, ret); if (ret) goto err_pull; @@ -4313,19 +4318,11 @@ static void ath10k_tpc_config_disp_tables(struct ath10k *ar, } } -void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, struct sk_buff *skb) +void ath10k_wmi_tpc_config_get_rate_code(u8 *rate_code, u16 *pream_table, + u32 num_tx_chain) { - u32 i, j, pream_idx, num_tx_chain; - u8 rate_code[WMI_TPC_RATE_MAX], rate_idx; - u16 pream_table[WMI_TPC_PREAM_TABLE_MAX]; - struct wmi_pdev_tpc_config_event *ev; - struct ath10k_tpc_stats *tpc_stats; - - ev = (struct wmi_pdev_tpc_config_event *)skb->data; - - tpc_stats = kzalloc(sizeof(*tpc_stats), GFP_ATOMIC); - if (!tpc_stats) - return; + u32 i, j, pream_idx; + u8 rate_idx; /* Create the rate code table based on the chains supported */ rate_idx = 0; @@ -4349,8 +4346,6 @@ void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, struct sk_buff *skb) pream_table[pream_idx] = rate_idx; pream_idx++; - num_tx_chain = __le32_to_cpu(ev->num_tx_chain); - /* Fill HT20 rate code */ for (i = 0; i < num_tx_chain; i++) { for (j = 0; j < 8; j++) { @@ -4374,7 +4369,7 @@ void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, struct sk_buff *skb) pream_idx++; /* Fill VHT20 rate code */ - for (i = 0; i < __le32_to_cpu(ev->num_tx_chain); i++) { + for (i = 0; i < num_tx_chain; i++) { for (j = 0; j < 10; j++) { rate_code[rate_idx] = ATH10K_HW_RATECODE(j, i, WMI_RATE_PREAMBLE_VHT); @@ -4418,6 +4413,26 @@ void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, struct sk_buff *skb) ATH10K_HW_RATECODE(0, 0, WMI_RATE_PREAMBLE_OFDM); pream_table[pream_idx] = ATH10K_TPC_PREAM_TABLE_END; +} + +void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, struct sk_buff *skb) +{ + u32 num_tx_chain; + u8 rate_code[WMI_TPC_RATE_MAX]; + u16 pream_table[WMI_TPC_PREAM_TABLE_MAX]; + struct wmi_pdev_tpc_config_event *ev; + struct ath10k_tpc_stats *tpc_stats; + + ev = (struct wmi_pdev_tpc_config_event *)skb->data; + + tpc_stats = kzalloc(sizeof(*tpc_stats), GFP_ATOMIC); + if (!tpc_stats) + return; + + num_tx_chain = __le32_to_cpu(ev->num_tx_chain); + + ath10k_wmi_tpc_config_get_rate_code(rate_code, pream_table, + num_tx_chain); tpc_stats->chan_freq = __le32_to_cpu(ev->chan_freq); tpc_stats->phy_mode = __le32_to_cpu(ev->phy_mode); @@ -4457,6 +4472,246 @@ void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, struct sk_buff *skb) __le32_to_cpu(ev->rate_max)); } +static u8 +ath10k_wmi_tpc_final_get_rate(struct ath10k *ar, + struct wmi_pdev_tpc_final_table_event *ev, + u32 rate_idx, u32 num_chains, + u32 rate_code, u8 type, u32 pream_idx) +{ + u8 tpc, num_streams, preamble, ch, stm_idx; + s8 pow_agcdd, pow_agstbc, pow_agtxbf; + int pream; + + num_streams = ATH10K_HW_NSS(rate_code); + preamble = ATH10K_HW_PREAMBLE(rate_code); + ch = num_chains - 1; + stm_idx = num_streams - 1; + pream = -1; + + if (__le32_to_cpu(ev->chan_freq) <= 2483) { + switch (pream_idx) { + case WMI_TPC_PREAM_2GHZ_CCK: + pream = 0; + break; + case WMI_TPC_PREAM_2GHZ_OFDM: + pream = 1; + break; + case WMI_TPC_PREAM_2GHZ_HT20: + case WMI_TPC_PREAM_2GHZ_VHT20: + pream = 2; + break; + case WMI_TPC_PREAM_2GHZ_HT40: + case WMI_TPC_PREAM_2GHZ_VHT40: + pream = 3; + break; + case WMI_TPC_PREAM_2GHZ_VHT80: + pream = 4; + break; + default: + pream = -1; + break; + } + } + + if (__le32_to_cpu(ev->chan_freq) >= 5180) { + switch (pream_idx) { + case WMI_TPC_PREAM_5GHZ_OFDM: + pream = 0; + break; + case WMI_TPC_PREAM_5GHZ_HT20: + case WMI_TPC_PREAM_5GHZ_VHT20: + pream = 1; + break; + case WMI_TPC_PREAM_5GHZ_HT40: + case WMI_TPC_PREAM_5GHZ_VHT40: + pream = 2; + break; + case WMI_TPC_PREAM_5GHZ_VHT80: + pream = 3; + break; + case WMI_TPC_PREAM_5GHZ_HTCUP: + pream = 4; + break; + default: + pream = -1; + break; + } + } + + if (pream == 4) + tpc = min_t(u8, ev->rates_array[rate_idx], + ev->max_reg_allow_pow[ch]); + else + tpc = min_t(u8, min_t(u8, ev->rates_array[rate_idx], + ev->max_reg_allow_pow[ch]), + ev->ctl_power_table[0][pream][stm_idx]); + + if (__le32_to_cpu(ev->num_tx_chain) <= 1) + goto out; + + if (preamble == WMI_RATE_PREAMBLE_CCK) + goto out; + + if (num_chains <= num_streams) + goto out; + + switch (type) { + case WMI_TPC_TABLE_TYPE_STBC: + pow_agstbc = ev->max_reg_allow_pow_agstbc[ch - 1][stm_idx]; + if (pream == 4) + tpc = min_t(u8, tpc, pow_agstbc); + else + tpc = min_t(u8, min_t(u8, tpc, pow_agstbc), + ev->ctl_power_table[0][pream][stm_idx]); + break; + case WMI_TPC_TABLE_TYPE_TXBF: + pow_agtxbf = ev->max_reg_allow_pow_agtxbf[ch - 1][stm_idx]; + if (pream == 4) + tpc = min_t(u8, tpc, pow_agtxbf); + else + tpc = min_t(u8, min_t(u8, tpc, pow_agtxbf), + ev->ctl_power_table[1][pream][stm_idx]); + break; + case WMI_TPC_TABLE_TYPE_CDD: + pow_agcdd = ev->max_reg_allow_pow_agcdd[ch - 1][stm_idx]; + if (pream == 4) + tpc = min_t(u8, tpc, pow_agcdd); + else + tpc = min_t(u8, min_t(u8, tpc, pow_agcdd), + ev->ctl_power_table[0][pream][stm_idx]); + break; + default: + ath10k_warn(ar, "unknown wmi tpc final table type: %d\n", type); + tpc = 0; + break; + } + +out: + return tpc; +} + +static void +ath10k_wmi_tpc_stats_final_disp_tables(struct ath10k *ar, + struct wmi_pdev_tpc_final_table_event *ev, + struct ath10k_tpc_stats_final *tpc_stats, + u8 *rate_code, u16 *pream_table, u8 type) +{ + u32 i, j, pream_idx, flags; + u8 tpc[WMI_TPC_TX_N_CHAIN]; + char tpc_value[WMI_TPC_TX_N_CHAIN * WMI_TPC_BUF_SIZE]; + char buff[WMI_TPC_BUF_SIZE]; + + flags = __le32_to_cpu(ev->flags); + + switch (type) { + case WMI_TPC_TABLE_TYPE_CDD: + if (!(flags & WMI_TPC_CONFIG_EVENT_FLAG_TABLE_CDD)) { + ath10k_dbg(ar, ATH10K_DBG_WMI, "CDD not supported\n"); + tpc_stats->flag[type] = ATH10K_TPC_TABLE_TYPE_FLAG; + return; + } + break; + case WMI_TPC_TABLE_TYPE_STBC: + if (!(flags & WMI_TPC_CONFIG_EVENT_FLAG_TABLE_STBC)) { + ath10k_dbg(ar, ATH10K_DBG_WMI, "STBC not supported\n"); + tpc_stats->flag[type] = ATH10K_TPC_TABLE_TYPE_FLAG; + return; + } + break; + case WMI_TPC_TABLE_TYPE_TXBF: + if (!(flags & WMI_TPC_CONFIG_EVENT_FLAG_TABLE_TXBF)) { + ath10k_dbg(ar, ATH10K_DBG_WMI, "TXBF not supported\n"); + tpc_stats->flag[type] = ATH10K_TPC_TABLE_TYPE_FLAG; + return; + } + break; + default: + ath10k_dbg(ar, ATH10K_DBG_WMI, + "invalid table type in wmi tpc event: %d\n", type); + return; + } + + pream_idx = 0; + for (i = 0; i < __le32_to_cpu(ev->rate_max); i++) { + memset(tpc_value, 0, sizeof(tpc_value)); + memset(buff, 0, sizeof(buff)); + if (i == pream_table[pream_idx]) + pream_idx++; + + for (j = 0; j < WMI_TPC_TX_N_CHAIN; j++) { + if (j >= __le32_to_cpu(ev->num_tx_chain)) + break; + + tpc[j] = ath10k_wmi_tpc_final_get_rate(ar, ev, i, j + 1, + rate_code[i], + type, pream_idx); + snprintf(buff, sizeof(buff), "%8d ", tpc[j]); + strncat(tpc_value, buff, strlen(buff)); + } + tpc_stats->tpc_table_final[type].pream_idx[i] = pream_idx; + tpc_stats->tpc_table_final[type].rate_code[i] = rate_code[i]; + memcpy(tpc_stats->tpc_table_final[type].tpc_value[i], + tpc_value, sizeof(tpc_value)); + } +} + +void ath10k_wmi_event_tpc_final_table(struct ath10k *ar, struct sk_buff *skb) +{ + u32 num_tx_chain; + u8 rate_code[WMI_TPC_FINAL_RATE_MAX]; + u16 pream_table[WMI_TPC_PREAM_TABLE_MAX]; + struct wmi_pdev_tpc_final_table_event *ev; + struct ath10k_tpc_stats_final *tpc_stats; + + ev = (struct wmi_pdev_tpc_final_table_event *)skb->data; + + tpc_stats = kzalloc(sizeof(*tpc_stats), GFP_ATOMIC); + if (!tpc_stats) + return; + + num_tx_chain = __le32_to_cpu(ev->num_tx_chain); + + ath10k_wmi_tpc_config_get_rate_code(rate_code, pream_table, + num_tx_chain); + + tpc_stats->chan_freq = __le32_to_cpu(ev->chan_freq); + tpc_stats->phy_mode = __le32_to_cpu(ev->phy_mode); + tpc_stats->ctl = __le32_to_cpu(ev->ctl); + tpc_stats->reg_domain = __le32_to_cpu(ev->reg_domain); + tpc_stats->twice_antenna_gain = a_sle32_to_cpu(ev->twice_antenna_gain); + tpc_stats->twice_antenna_reduction = + __le32_to_cpu(ev->twice_antenna_reduction); + tpc_stats->power_limit = __le32_to_cpu(ev->power_limit); + tpc_stats->twice_max_rd_power = __le32_to_cpu(ev->twice_max_rd_power); + tpc_stats->num_tx_chain = __le32_to_cpu(ev->num_tx_chain); + tpc_stats->rate_max = __le32_to_cpu(ev->rate_max); + + ath10k_wmi_tpc_stats_final_disp_tables(ar, ev, tpc_stats, + rate_code, pream_table, + WMI_TPC_TABLE_TYPE_CDD); + ath10k_wmi_tpc_stats_final_disp_tables(ar, ev, tpc_stats, + rate_code, pream_table, + WMI_TPC_TABLE_TYPE_STBC); + ath10k_wmi_tpc_stats_final_disp_tables(ar, ev, tpc_stats, + rate_code, pream_table, + WMI_TPC_TABLE_TYPE_TXBF); + + ath10k_debug_tpc_stats_final_process(ar, tpc_stats); + + ath10k_dbg(ar, ATH10K_DBG_WMI, + "wmi event tpc final table channel %d mode %d ctl %d regd %d gain %d %d limit %d max_power %d tx_chanins %d rates %d\n", + __le32_to_cpu(ev->chan_freq), + __le32_to_cpu(ev->phy_mode), + __le32_to_cpu(ev->ctl), + __le32_to_cpu(ev->reg_domain), + a_sle32_to_cpu(ev->twice_antenna_gain), + __le32_to_cpu(ev->twice_antenna_reduction), + __le32_to_cpu(ev->power_limit), + __le32_to_cpu(ev->twice_max_rd_power) / 2, + __le32_to_cpu(ev->num_tx_chain), + __le32_to_cpu(ev->rate_max)); +} + static void ath10k_wmi_handle_tdls_peer_event(struct ath10k *ar, struct sk_buff *skb) { @@ -5549,6 +5804,9 @@ static void ath10k_wmi_10_4_op_rx(struct ath10k *ar, struct sk_buff *skb) case WMI_10_4_TDLS_PEER_EVENTID: ath10k_wmi_handle_tdls_peer_event(ar, skb); break; + case WMI_10_4_PDEV_TPC_TABLE_EVENTID: + ath10k_wmi_event_tpc_final_table(ar, skb); + break; default: ath10k_warn(ar, "Unknown eventid: %d\n", id); break; @@ -7989,6 +8247,24 @@ static u32 ath10k_wmi_prepare_peer_qos(u8 uapsd_queues, u8 sp) return peer_qos; } +static struct sk_buff * +ath10k_wmi_10_4_op_gen_pdev_get_tpc_table_cmdid(struct ath10k *ar, u32 param) +{ + struct wmi_pdev_get_tpc_table_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + cmd = (struct wmi_pdev_get_tpc_table_cmd *)skb->data; + cmd->param = __cpu_to_le32(param); + + ath10k_dbg(ar, ATH10K_DBG_WMI, + "wmi pdev get tpc table param:%d\n", param); + return skb; +} + static struct sk_buff * ath10k_wmi_10_4_gen_tdls_peer_update(struct ath10k *ar, const struct wmi_tdls_peer_update_cmd_arg *arg, @@ -8430,6 +8706,8 @@ static const struct wmi_ops wmi_10_4_ops = { .ext_resource_config = ath10k_wmi_10_4_ext_resource_config, .gen_update_fw_tdls_state = ath10k_wmi_10_4_gen_update_fw_tdls_state, .gen_tdls_peer_update = ath10k_wmi_10_4_gen_tdls_peer_update, + .gen_pdev_get_tpc_table_cmdid = + ath10k_wmi_10_4_op_gen_pdev_get_tpc_table_cmdid, /* shared with 10.2 */ .pull_echo_ev = ath10k_wmi_op_pull_echo_ev, diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index c7b30ed9015d..c8fc45d8090e 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. + * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -197,6 +198,9 @@ enum wmi_service { WMI_SERVICE_TDLS_EXPLICIT_MODE_ONLY, WMI_SERVICE_MGMT_TX_WMI, WMI_SERVICE_TDLS_WIDER_BANDWIDTH, + WMI_SERVICE_HTT_MGMT_TX_COMP_VALID_FLAGS, + WMI_SERVICE_HOST_DFS_CHECK_SUPPORT, + WMI_SERVICE_TPC_STATS_FINAL, /* keep last */ WMI_SERVICE_MAX, @@ -339,6 +343,9 @@ enum wmi_10_4_service { WMI_10_4_SERVICE_TDLS_CONN_TRACKER_IN_HOST_MODE, WMI_10_4_SERVICE_TDLS_EXPLICIT_MODE_ONLY, WMI_10_4_SERVICE_TDLS_WIDER_BANDWIDTH, + WMI_10_4_SERVICE_HTT_MGMT_TX_COMP_VALID_FLAGS, + WMI_10_4_SERVICE_HOST_DFS_CHECK_SUPPORT, + WMI_10_4_SERVICE_TPC_STATS_FINAL, }; static inline char *wmi_service_name(int service_id) @@ -448,6 +455,9 @@ static inline char *wmi_service_name(int service_id) SVCSTR(WMI_SERVICE_TDLS_CONN_TRACKER_IN_HOST_MODE); SVCSTR(WMI_SERVICE_TDLS_EXPLICIT_MODE_ONLY); SVCSTR(WMI_SERVICE_TDLS_WIDER_BANDWIDTH); + SVCSTR(WMI_SERVICE_HTT_MGMT_TX_COMP_VALID_FLAGS); + SVCSTR(WMI_SERVICE_HOST_DFS_CHECK_SUPPORT); + SVCSTR(WMI_SERVICE_TPC_STATS_FINAL); default: return NULL; } @@ -746,6 +756,12 @@ static inline void wmi_10_4_svc_map(const __le32 *in, unsigned long *out, WMI_SERVICE_TDLS_EXPLICIT_MODE_ONLY, len); SVCMAP(WMI_10_4_SERVICE_TDLS_WIDER_BANDWIDTH, WMI_SERVICE_TDLS_WIDER_BANDWIDTH, len); + SVCMAP(WMI_10_4_SERVICE_HTT_MGMT_TX_COMP_VALID_FLAGS, + WMI_SERVICE_HTT_MGMT_TX_COMP_VALID_FLAGS, len); + SVCMAP(WMI_10_4_SERVICE_HOST_DFS_CHECK_SUPPORT, + WMI_SERVICE_HOST_DFS_CHECK_SUPPORT, len); + SVCMAP(WMI_10_4_SERVICE_TPC_STATS_FINAL, + WMI_SERVICE_TPC_STATS_FINAL, len); } #undef SVCMAP @@ -3993,10 +4009,12 @@ struct wmi_pdev_get_tpc_config_cmd { #define WMI_TPC_CONFIG_PARAM 1 #define WMI_TPC_RATE_MAX 160 +#define WMI_TPC_FINAL_RATE_MAX 240 #define WMI_TPC_TX_N_CHAIN 4 #define WMI_TPC_PREAM_TABLE_MAX 10 #define WMI_TPC_FLAG 3 #define WMI_TPC_BUF_SIZE 10 +#define WMI_TPC_BEAMFORMING 2 enum wmi_tpc_table_type { WMI_TPC_TABLE_TYPE_CDD = 0, @@ -4039,6 +4057,51 @@ enum wmi_tp_scale { WMI_TP_SCALE_SIZE = 5, /* max num of enum */ }; +struct wmi_pdev_tpc_final_table_event { + __le32 reg_domain; + __le32 chan_freq; + __le32 phy_mode; + __le32 twice_antenna_reduction; + __le32 twice_max_rd_power; + a_sle32 twice_antenna_gain; + __le32 power_limit; + __le32 rate_max; + __le32 num_tx_chain; + __le32 ctl; + __le32 flags; + s8 max_reg_allow_pow[WMI_TPC_TX_N_CHAIN]; + s8 max_reg_allow_pow_agcdd[WMI_TPC_TX_N_CHAIN][WMI_TPC_TX_N_CHAIN]; + s8 max_reg_allow_pow_agstbc[WMI_TPC_TX_N_CHAIN][WMI_TPC_TX_N_CHAIN]; + s8 max_reg_allow_pow_agtxbf[WMI_TPC_TX_N_CHAIN][WMI_TPC_TX_N_CHAIN]; + u8 rates_array[WMI_TPC_FINAL_RATE_MAX]; + u8 ctl_power_table[WMI_TPC_BEAMFORMING][WMI_TPC_TX_N_CHAIN] + [WMI_TPC_TX_N_CHAIN]; +} __packed; + +struct wmi_pdev_get_tpc_table_cmd { + __le32 param; +} __packed; + +enum wmi_tpc_pream_2ghz { + WMI_TPC_PREAM_2GHZ_CCK = 0, + WMI_TPC_PREAM_2GHZ_OFDM, + WMI_TPC_PREAM_2GHZ_HT20, + WMI_TPC_PREAM_2GHZ_HT40, + WMI_TPC_PREAM_2GHZ_VHT20, + WMI_TPC_PREAM_2GHZ_VHT40, + WMI_TPC_PREAM_2GHZ_VHT80, +}; + +enum wmi_tpc_pream_5ghz { + WMI_TPC_PREAM_5GHZ_OFDM = 1, + WMI_TPC_PREAM_5GHZ_HT20, + WMI_TPC_PREAM_5GHZ_HT40, + WMI_TPC_PREAM_5GHZ_VHT20, + WMI_TPC_PREAM_5GHZ_VHT40, + WMI_TPC_PREAM_5GHZ_VHT80, + WMI_TPC_PREAM_5GHZ_HTCUP, +}; + struct wmi_pdev_chanlist_update_event { /* number of channels */ __le32 num_chan; @@ -6979,5 +7042,8 @@ void ath10k_wmi_10_4_op_fw_stats_fill(struct ath10k *ar, int ath10k_wmi_op_get_vdev_subtype(struct ath10k *ar, enum wmi_vdev_subtype subtype); int ath10k_wmi_barrier(struct ath10k *ar); +void ath10k_wmi_tpc_config_get_rate_code(u8 *rate_code, u16 *pream_table, + u32 num_tx_chain); +void ath10k_wmi_event_tpc_final_table(struct ath10k *ar, struct sk_buff *skb); #endif /* _WMI_H_ */ diff --git a/drivers/net/wireless/ath/ath5k/qcu.c b/drivers/net/wireless/ath/ath5k/qcu.c index beda11ce34a7..147947f632f7 100644 --- a/drivers/net/wireless/ath/ath5k/qcu.c +++ b/drivers/net/wireless/ath/ath5k/qcu.c @@ -327,8 +327,6 @@ ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue) AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); - tq = &ah->ah_txq[queue]; - /* Skip if queue inactive or if we are on AR5210 * that doesn't have QCU/DCU */ if ((ah->ah_version == AR5K_AR5210) || diff --git a/drivers/net/wireless/ath/ath9k/common-init.c b/drivers/net/wireless/ath/ath9k/common-init.c index 8b4f7fdabf58..82de0fadbc95 100644 --- a/drivers/net/wireless/ath/ath9k/common-init.c +++ b/drivers/net/wireless/ath/ath9k/common-init.c @@ -88,7 +88,7 @@ static const struct ieee80211_channel ath9k_5ghz_chantable[] = { CHAN5G(5825, 37), /* Channel 165 */ }; -/* Atheros hardware rate code addition for short premble */ +/* Atheros hardware rate code addition for short preamble */ #define SHPCHECK(__hw_rate, __flags) \ ((__flags & IEEE80211_RATE_SHORT_PREAMBLE) ? (__hw_rate | 0x04 ) : 0) diff --git a/drivers/net/wireless/ath/ath9k/common-spectral.c b/drivers/net/wireless/ath/ath9k/common-spectral.c index 5e77fe1f5b0d..a41bcbda1d9e 100644 --- a/drivers/net/wireless/ath/ath9k/common-spectral.c +++ b/drivers/net/wireless/ath/ath9k/common-spectral.c @@ -479,14 +479,16 @@ ath_cmn_is_fft_buf_full(struct ath_spec_scan_priv *spec_priv) { int i = 0; int ret = 0; + struct rchan_buf *buf; struct rchan *rc = spec_priv->rfs_chan_spec_scan; - for_each_online_cpu(i) - ret += relay_buf_full(*per_cpu_ptr(rc->buf, i)); + for_each_possible_cpu(i) { + if ((buf = *per_cpu_ptr(rc->buf, i))) { + ret += relay_buf_full(buf); + } + } - i = num_online_cpus(); - - if (ret == i) + if (ret) return 1; else return 0; diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index cd0f023ccf77..6b37036b2d36 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -184,7 +184,8 @@ u16 ath9k_hw_computetxtime(struct ath_hw *ah, break; case WLAN_RC_PHY_OFDM: if (ah->curchan && IS_CHAN_QUARTER_RATE(ah->curchan)) { - bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME_QUARTER) / 1000; + bitsPerSymbol = + ((kbps >> 2) * OFDM_SYMBOL_TIME_QUARTER) / 1000; numBits = OFDM_PLCP_BITS + (frameLen << 3); numSymbols = DIV_ROUND_UP(numBits, bitsPerSymbol); txTime = OFDM_SIFS_TIME_QUARTER @@ -192,7 +193,8 @@ u16 ath9k_hw_computetxtime(struct ath_hw *ah, + (numSymbols * OFDM_SYMBOL_TIME_QUARTER); } else if (ah->curchan && IS_CHAN_HALF_RATE(ah->curchan)) { - bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME_HALF) / 1000; + bitsPerSymbol = + ((kbps >> 1) * OFDM_SYMBOL_TIME_HALF) / 1000; numBits = OFDM_PLCP_BITS + (frameLen << 3); numSymbols = DIV_ROUND_UP(numBits, bitsPerSymbol); txTime = OFDM_SIFS_TIME_HALF + @@ -1036,7 +1038,7 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah) int acktimeout, ctstimeout, ack_offset = 0; int slottime; int sifstime; - int rx_lat = 0, tx_lat = 0, eifs = 0; + int rx_lat = 0, tx_lat = 0, eifs = 0, ack_shift = 0; u32 reg; ath_dbg(ath9k_hw_common(ah), RESET, "ah->misc_mode 0x%x\n", @@ -1068,6 +1070,7 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah) sifstime = 32; ack_offset = 16; + ack_shift = 3; slottime = 13; } else if (IS_CHAN_QUARTER_RATE(chan)) { eifs = 340; @@ -1078,6 +1081,7 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah) sifstime = 64; ack_offset = 32; + ack_shift = 1; slottime = 21; } else { if (AR_SREV_9287(ah) && AR_SREV_9287_13_OR_LATER(ah)) { @@ -1134,6 +1138,10 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah) SM(tx_lat, AR_USEC_TX_LAT), AR_USEC_TX_LAT | AR_USEC_RX_LAT | AR_USEC_USEC); + if (IS_CHAN_HALF_RATE(chan) || IS_CHAN_QUARTER_RATE(chan)) + REG_RMW(ah, AR_TXSIFS, + sifstime | SM(ack_shift, AR_TXSIFS_ACK_SHIFT), + (AR_TXSIFS_TIME | AR_TXSIFS_ACK_SHIFT)); } EXPORT_SYMBOL(ath9k_hw_init_global_settings); diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 396bf05c6bf6..d8b041f48ca8 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -2892,6 +2892,8 @@ void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an) struct ath_txq *txq; int tidno; + rcu_read_lock(); + for (tidno = 0; tidno < IEEE80211_NUM_TIDS; tidno++) { tid = ath_node_to_tid(an, tidno); txq = tid->txq; @@ -2909,6 +2911,8 @@ void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an) if (!an->sta) break; /* just one multicast ath_atx_tid */ } + + rcu_read_unlock(); } #ifdef CONFIG_ATH9K_TX99 diff --git a/drivers/net/wireless/ath/dfs_pattern_detector.c b/drivers/net/wireless/ath/dfs_pattern_detector.c index 4100ffd42a43..448b83eea810 100644 --- a/drivers/net/wireless/ath/dfs_pattern_detector.c +++ b/drivers/net/wireless/ath/dfs_pattern_detector.c @@ -115,7 +115,7 @@ static const struct radar_detector_specs jp_radar_ref_types[] = { JP_PATTERN(4, 0, 5, 150, 230, 1, 23, 50, false), JP_PATTERN(5, 6, 10, 200, 500, 1, 16, 50, false), JP_PATTERN(6, 11, 20, 200, 500, 1, 12, 50, false), - JP_PATTERN(7, 50, 100, 1000, 2000, 1, 3, 50, false), + JP_PATTERN(7, 50, 100, 1000, 2000, 1, 3, 50, true), JP_PATTERN(5, 0, 1, 333, 333, 1, 9, 50, false), }; diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.c b/drivers/net/wireless/ath/wcn36xx/dxe.c index a3f1f7d042a4..7d5ecaf02288 100644 --- a/drivers/net/wireless/ath/wcn36xx/dxe.c +++ b/drivers/net/wireless/ath/wcn36xx/dxe.c @@ -376,7 +376,7 @@ static void reap_tx_dxes(struct wcn36xx *wcn, struct wcn36xx_dxe_ch *ch) spin_lock_irqsave(&ch->lock, flags); ctl = ch->tail_blk_ctl; do { - if (ctl->desc->ctrl & WCN36XX_DXE_CTRL_VALID_MASK) + if (ctl->desc->ctrl & WCN36xx_DXE_CTRL_VLD) break; if (ctl->skb) { dma_unmap_single(wcn->dev, ctl->desc->src_addr_l, @@ -397,7 +397,7 @@ static void reap_tx_dxes(struct wcn36xx *wcn, struct wcn36xx_dxe_ch *ch) } ctl = ctl->next; } while (ctl != ch->head_blk_ctl && - !(ctl->desc->ctrl & WCN36XX_DXE_CTRL_VALID_MASK)); + !(ctl->desc->ctrl & WCN36xx_DXE_CTRL_VLD)); ch->tail_blk_ctl = ctl; spin_unlock_irqrestore(&ch->lock, flags); @@ -415,14 +415,31 @@ static irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev) WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_H, &int_reason); - /* TODO: Check int_reason */ - wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_CLR, WCN36XX_INT_MASK_CHAN_TX_H); - wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_ED_CLR, - WCN36XX_INT_MASK_CHAN_TX_H); + if (int_reason & WCN36XX_CH_STAT_INT_ERR_MASK ) { + wcn36xx_dxe_write_register(wcn, + WCN36XX_DXE_0_INT_ERR_CLR, + WCN36XX_INT_MASK_CHAN_TX_H); + + wcn36xx_err("DXE IRQ reported error: 0x%x in high TX channel\n", + int_src); + } + + if (int_reason & WCN36XX_CH_STAT_INT_DONE_MASK) { + wcn36xx_dxe_write_register(wcn, + WCN36XX_DXE_0_INT_DONE_CLR, + WCN36XX_INT_MASK_CHAN_TX_H); + } + + if (int_reason & WCN36XX_CH_STAT_INT_ED_MASK) { + wcn36xx_dxe_write_register(wcn, + WCN36XX_DXE_0_INT_ED_CLR, + WCN36XX_INT_MASK_CHAN_TX_H); + } + wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ready high\n"); reap_tx_dxes(wcn, &wcn->dxe_tx_h_ch); } @@ -431,14 +448,33 @@ static irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev) wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_L, &int_reason); - /* TODO: Check int_reason */ wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_CLR, WCN36XX_INT_MASK_CHAN_TX_L); - wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_ED_CLR, - WCN36XX_INT_MASK_CHAN_TX_L); + + if (int_reason & WCN36XX_CH_STAT_INT_ERR_MASK ) { + wcn36xx_dxe_write_register(wcn, + WCN36XX_DXE_0_INT_ERR_CLR, + WCN36XX_INT_MASK_CHAN_TX_L); + + wcn36xx_err("DXE IRQ reported error: 0x%x in low TX channel\n", + int_src); + } + + if (int_reason & WCN36XX_CH_STAT_INT_DONE_MASK) { + wcn36xx_dxe_write_register(wcn, + WCN36XX_DXE_0_INT_DONE_CLR, + WCN36XX_INT_MASK_CHAN_TX_L); + } + + if (int_reason & WCN36XX_CH_STAT_INT_ED_MASK) { + wcn36xx_dxe_write_register(wcn, + WCN36XX_DXE_0_INT_ED_CLR, + WCN36XX_INT_MASK_CHAN_TX_L); + } + wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ready low\n"); reap_tx_dxes(wcn, &wcn->dxe_tx_l_ch); } @@ -503,7 +539,7 @@ static int wcn36xx_rx_handle_packets(struct wcn36xx *wcn, int_mask = WCN36XX_DXE_INT_CH3_MASK; } - while (!(dxe->ctrl & WCN36XX_DXE_CTRL_VALID_MASK)) { + while (!(dxe->ctrl & WCN36xx_DXE_CTRL_VLD)) { skb = ctl->skb; dma_addr = dxe->dst_addr_l; ret = wcn36xx_dxe_fill_skb(wcn->dev, ctl); diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.h b/drivers/net/wireless/ath/wcn36xx/dxe.h index c012e807753b..2bc376c5391b 100644 --- a/drivers/net/wireless/ath/wcn36xx/dxe.h +++ b/drivers/net/wireless/ath/wcn36xx/dxe.h @@ -33,15 +33,106 @@ H2H_TEST_RX_TX = DMA2 #define WCN36XX_CCU_DXE_INT_SELECT_RIVA 0x310 #define WCN36XX_CCU_DXE_INT_SELECT_PRONTO 0x10dc -/* TODO This must calculated properly but not hardcoded */ -#define WCN36XX_DXE_CTRL_TX_L 0x328a44 -#define WCN36XX_DXE_CTRL_TX_H 0x32ce44 -#define WCN36XX_DXE_CTRL_RX_L 0x12ad2f -#define WCN36XX_DXE_CTRL_RX_H 0x12d12f -#define WCN36XX_DXE_CTRL_TX_H_BD 0x30ce45 -#define WCN36XX_DXE_CTRL_TX_H_SKB 0x32ce4d -#define WCN36XX_DXE_CTRL_TX_L_BD 0x308a45 -#define WCN36XX_DXE_CTRL_TX_L_SKB 0x328a4d +/* Descriptor valid */ +#define WCN36xx_DXE_CTRL_VLD BIT(0) +/* End of packet */ +#define WCN36xx_DXE_CTRL_EOP BIT(3) +/* BD handling bit */ +#define WCN36xx_DXE_CTRL_BDH BIT(4) +/* Source is a queue */ +#define WCN36xx_DXE_CTRL_SIQ BIT(5) +/* Destination is a queue */ +#define WCN36xx_DXE_CTRL_DIQ BIT(6) +/* Pointer address is a queue */ +#define WCN36xx_DXE_CTRL_PIQ BIT(7) +/* Release PDU when done */ +#define WCN36xx_DXE_CTRL_PDU_REL BIT(8) +/* STOP channel processing */ +#define WCN36xx_DXE_CTRL_STOP BIT(16) +/* INT on descriptor done */ +#define WCN36xx_DXE_CTRL_INT BIT(17) +/* Endian byte swap enable */ +#define WCN36xx_DXE_CTRL_SWAP BIT(20) +/* Master endianness */ +#define WCN36xx_DXE_CTRL_ENDIANNESS BIT(21) + +/* Transfer type */ +#define WCN36xx_DXE_CTRL_XTYPE_SHIFT 1 +#define WCN36xx_DXE_CTRL_XTYPE_MASK GENMASK(2, WCN36xx_DXE_CTRL_XTYPE_SHIFT) +#define WCN36xx_DXE_CTRL_XTYPE_SET(x) ((x) << WCN36xx_DXE_CTRL_XTYPE_SHIFT) + +/* BMU Threshold select */ +#define WCN36xx_DXE_CTRL_BTHLD_SEL_SHIFT 9 +#define WCN36xx_DXE_CTRL_BTHLD_SEL_MASK GENMASK(12, WCN36xx_DXE_CTRL_BTHLD_SEL_SHIFT) +#define WCN36xx_DXE_CTRL_BTHLD_SEL_SET(x) ((x) << WCN36xx_DXE_CTRL_BTHLD_SEL_SHIFT) + +/* Priority */ +#define WCN36xx_DXE_CTRL_PRIO_SHIFT 13 +#define WCN36xx_DXE_CTRL_PRIO_MASK GENMASK(15, WCN36xx_DXE_CTRL_PRIO_SHIFT) +#define WCN36xx_DXE_CTRL_PRIO_SET(x) ((x) << WCN36xx_DXE_CTRL_PRIO_SHIFT) + +/* BD Template index */ +#define WCN36xx_DXE_CTRL_BDT_IDX_SHIFT 18 +#define WCN36xx_DXE_CTRL_BDT_IDX_MASK GENMASK(19, WCN36xx_DXE_CTRL_BDT_IDX_SHIFT) +#define WCN36xx_DXE_CTRL_BDT_IDX_SET(x) ((x) << WCN36xx_DXE_CTRL_BDT_IDX_SHIFT) + +/* Transfer types: */ +/* Host to host */ +#define WCN36xx_DXE_XTYPE_H2H (0) +/* Host to BMU */ +#define WCN36xx_DXE_XTYPE_H2B (2) +/* BMU to host */ +#define WCN36xx_DXE_XTYPE_B2H (3) + +#define WCN36XX_DXE_CTRL_TX_L (WCN36xx_DXE_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_H2B) | \ + WCN36xx_DXE_CTRL_DIQ | WCN36xx_DXE_CTRL_BTHLD_SEL_SET(5) | \ + WCN36xx_DXE_CTRL_PRIO_SET(4) | WCN36xx_DXE_CTRL_INT | \ + WCN36xx_DXE_CTRL_SWAP | WCN36xx_DXE_CTRL_ENDIANNESS) + +#define WCN36XX_DXE_CTRL_TX_H (WCN36xx_DXE_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_H2B) | \ + WCN36xx_DXE_CTRL_DIQ | WCN36xx_DXE_CTRL_BTHLD_SEL_SET(7) | \ + WCN36xx_DXE_CTRL_PRIO_SET(6) | WCN36xx_DXE_CTRL_INT | \ + WCN36xx_DXE_CTRL_SWAP | WCN36xx_DXE_CTRL_ENDIANNESS) + +#define WCN36XX_DXE_CTRL_RX_L (WCN36xx_DXE_CTRL_VLD | \ + WCN36xx_DXE_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_B2H) | \ + WCN36xx_DXE_CTRL_EOP | WCN36xx_DXE_CTRL_SIQ | \ + WCN36xx_DXE_CTRL_PDU_REL | WCN36xx_DXE_CTRL_BTHLD_SEL_SET(6) | \ + WCN36xx_DXE_CTRL_PRIO_SET(5) | WCN36xx_DXE_CTRL_INT | \ + WCN36xx_DXE_CTRL_SWAP) + +#define WCN36XX_DXE_CTRL_RX_H (WCN36xx_DXE_CTRL_VLD | \ + WCN36xx_DXE_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_B2H) | \ + WCN36xx_DXE_CTRL_EOP | WCN36xx_DXE_CTRL_SIQ | \ + WCN36xx_DXE_CTRL_PDU_REL | WCN36xx_DXE_CTRL_BTHLD_SEL_SET(8) | \ + WCN36xx_DXE_CTRL_PRIO_SET(6) | WCN36xx_DXE_CTRL_INT | \ + WCN36xx_DXE_CTRL_SWAP) + +#define WCN36XX_DXE_CTRL_TX_H_BD (WCN36xx_DXE_CTRL_VLD | \ + WCN36xx_DXE_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_H2B) | \ + WCN36xx_DXE_CTRL_DIQ | WCN36xx_DXE_CTRL_BTHLD_SEL_SET(7) | \ + WCN36xx_DXE_CTRL_PRIO_SET(6) | WCN36xx_DXE_CTRL_SWAP | \ + WCN36xx_DXE_CTRL_ENDIANNESS) + +#define WCN36XX_DXE_CTRL_TX_H_SKB (WCN36xx_DXE_CTRL_VLD | \ + WCN36xx_DXE_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_H2B) | \ + WCN36xx_DXE_CTRL_EOP | WCN36xx_DXE_CTRL_DIQ | \ + WCN36xx_DXE_CTRL_BTHLD_SEL_SET(7) | WCN36xx_DXE_CTRL_PRIO_SET(6) | \ + WCN36xx_DXE_CTRL_INT | WCN36xx_DXE_CTRL_SWAP | \ + WCN36xx_DXE_CTRL_ENDIANNESS) + +#define WCN36XX_DXE_CTRL_TX_L_BD (WCN36xx_DXE_CTRL_VLD | \ + WCN36xx_DXE_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_H2B) | \ + WCN36xx_DXE_CTRL_DIQ | WCN36xx_DXE_CTRL_BTHLD_SEL_SET(5) | \ + WCN36xx_DXE_CTRL_PRIO_SET(4) | WCN36xx_DXE_CTRL_SWAP | \ + WCN36xx_DXE_CTRL_ENDIANNESS) + +#define WCN36XX_DXE_CTRL_TX_L_SKB (WCN36xx_DXE_CTRL_VLD | \ + WCN36xx_DXE_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_H2B) | \ + WCN36xx_DXE_CTRL_EOP | WCN36xx_DXE_CTRL_DIQ | \ + WCN36xx_DXE_CTRL_BTHLD_SEL_SET(5) | WCN36xx_DXE_CTRL_PRIO_SET(4) | \ + WCN36xx_DXE_CTRL_INT | WCN36xx_DXE_CTRL_SWAP | \ + WCN36xx_DXE_CTRL_ENDIANNESS) /* TODO This must calculated properly but not hardcoded */ #define WCN36XX_DXE_WQ_TX_L 0x17 @@ -49,15 +140,106 @@ H2H_TEST_RX_TX = DMA2 #define WCN36XX_DXE_WQ_RX_L 0xB #define WCN36XX_DXE_WQ_RX_H 0x4 -/* DXE descriptor control filed */ -#define WCN36XX_DXE_CTRL_VALID_MASK (0x00000001) +/* Channel enable or restart */ +#define WCN36xx_DXE_CH_CTRL_EN BIT(0) +/* End of packet bit */ +#define WCN36xx_DXE_CH_CTRL_EOP BIT(3) +/* BD Handling bit */ +#define WCN36xx_DXE_CH_CTRL_BDH BIT(4) +/* Source is queue */ +#define WCN36xx_DXE_CH_CTRL_SIQ BIT(5) +/* Destination is queue */ +#define WCN36xx_DXE_CH_CTRL_DIQ BIT(6) +/* Pointer descriptor is queue */ +#define WCN36xx_DXE_CH_CTRL_PIQ BIT(7) +/* Relase PDU when done */ +#define WCN36xx_DXE_CH_CTRL_PDU_REL BIT(8) +/* Stop channel processing */ +#define WCN36xx_DXE_CH_CTRL_STOP BIT(16) +/* Enable external descriptor interrupt */ +#define WCN36xx_DXE_CH_CTRL_INE_ED BIT(17) +/* Enable channel interrupt on errors */ +#define WCN36xx_DXE_CH_CTRL_INE_ERR BIT(18) +/* Enable Channel interrupt when done */ +#define WCN36xx_DXE_CH_CTRL_INE_DONE BIT(19) +/* External descriptor enable */ +#define WCN36xx_DXE_CH_CTRL_EDEN BIT(20) +/* Wait for valid bit */ +#define WCN36xx_DXE_CH_CTRL_EDVEN BIT(21) +/* Endianness is little endian*/ +#define WCN36xx_DXE_CH_CTRL_ENDIANNESS BIT(26) +/* Abort transfer */ +#define WCN36xx_DXE_CH_CTRL_ABORT BIT(27) +/* Long descriptor format */ +#define WCN36xx_DXE_CH_CTRL_DFMT BIT(28) +/* Endian byte swap enable */ +#define WCN36xx_DXE_CH_CTRL_SWAP BIT(31) + +/* Transfer type */ +#define WCN36xx_DXE_CH_CTRL_XTYPE_SHIFT 1 +#define WCN36xx_DXE_CH_CTRL_XTYPE_MASK GENMASK(2, WCN36xx_DXE_CH_CTRL_XTYPE_SHIFT) +#define WCN36xx_DXE_CH_CTRL_XTYPE_SET(x) ((x) << WCN36xx_DXE_CH_CTRL_XTYPE_SHIFT) + +/* Channel BMU Threshold select */ +#define WCN36xx_DXE_CH_CTRL_BTHLD_SEL_SHIFT 9 +#define WCN36xx_DXE_CH_CTRL_BTHLD_SEL_MASK GENMASK(12, WCN36xx_DXE_CH_CTRL_BTHLD_SEL_SHIFT) +#define WCN36xx_DXE_CH_CTRL_BTHLD_SEL_SET(x) ((x) << WCN36xx_DXE_CH_CTRL_BTHLD_SEL_SHIFT) + +/* Channel Priority */ +#define WCN36xx_DXE_CH_CTRL_PRIO_SHIFT 13 +#define WCN36xx_DXE_CH_CTRL_PRIO_MASK GENMASK(15, WCN36xx_DXE_CH_CTRL_PRIO_SHIFT) +#define WCN36xx_DXE_CH_CTRL_PRIO_SET(x) ((x) << WCN36xx_DXE_CH_CTRL_PRIO_SHIFT) + +/* Counter select */ +#define WCN36xx_DXE_CH_CTRL_SEL_SHIFT 22 +#define WCN36xx_DXE_CH_CTRL_SEL_MASK GENMASK(25, WCN36xx_DXE_CH_CTRL_SEL_SHIFT) +#define WCN36xx_DXE_CH_CTRL_SEL_SET(x) ((x) << WCN36xx_DXE_CH_CTRL_SEL_SHIFT) + +/* Channel BD template index */ +#define WCN36xx_DXE_CH_CTRL_BDT_IDX_SHIFT 29 +#define WCN36xx_DXE_CH_CTRL_BDT_IDX_MASK GENMASK(30, WCN36xx_DXE_CH_CTRL_BDT_IDX_SHIFT) +#define WCN36xx_DXE_CH_CTRL_BDT_IDX_SET(x) ((x) << WCN36xx_DXE_CH_CTRL_BDT_IDX_SHIFT) -/* TODO This must calculated properly but not hardcoded */ /* DXE default control register values */ -#define WCN36XX_DXE_CH_DEFAULT_CTL_RX_L 0x847EAD2F -#define WCN36XX_DXE_CH_DEFAULT_CTL_RX_H 0x84FED12F -#define WCN36XX_DXE_CH_DEFAULT_CTL_TX_H 0x853ECF4D -#define WCN36XX_DXE_CH_DEFAULT_CTL_TX_L 0x843e8b4d +#define WCN36XX_DXE_CH_DEFAULT_CTL_RX_L (WCN36xx_DXE_CH_CTRL_EN | \ + WCN36xx_DXE_CH_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_B2H) | \ + WCN36xx_DXE_CH_CTRL_EOP | WCN36xx_DXE_CH_CTRL_SIQ | \ + WCN36xx_DXE_CH_CTRL_PDU_REL | WCN36xx_DXE_CH_CTRL_BTHLD_SEL_SET(6) | \ + WCN36xx_DXE_CH_CTRL_PRIO_SET(5) | WCN36xx_DXE_CH_CTRL_INE_ED | \ + WCN36xx_DXE_CH_CTRL_INE_ERR | WCN36xx_DXE_CH_CTRL_INE_DONE | \ + WCN36xx_DXE_CH_CTRL_EDEN | WCN36xx_DXE_CH_CTRL_EDVEN | \ + WCN36xx_DXE_CH_CTRL_SEL_SET(1) | WCN36xx_DXE_CH_CTRL_ENDIANNESS | \ + WCN36xx_DXE_CH_CTRL_SWAP) + +#define WCN36XX_DXE_CH_DEFAULT_CTL_RX_H (WCN36xx_DXE_CH_CTRL_EN | \ + WCN36xx_DXE_CH_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_B2H) | \ + WCN36xx_DXE_CH_CTRL_EOP | WCN36xx_DXE_CH_CTRL_SIQ | \ + WCN36xx_DXE_CH_CTRL_PDU_REL | WCN36xx_DXE_CH_CTRL_BTHLD_SEL_SET(8) | \ + WCN36xx_DXE_CH_CTRL_PRIO_SET(6) | WCN36xx_DXE_CH_CTRL_INE_ED | \ + WCN36xx_DXE_CH_CTRL_INE_ERR | WCN36xx_DXE_CH_CTRL_INE_DONE | \ + WCN36xx_DXE_CH_CTRL_EDEN | WCN36xx_DXE_CH_CTRL_EDVEN | \ + WCN36xx_DXE_CH_CTRL_SEL_SET(3) | WCN36xx_DXE_CH_CTRL_ENDIANNESS | \ + WCN36xx_DXE_CH_CTRL_SWAP) + +#define WCN36XX_DXE_CH_DEFAULT_CTL_TX_H (WCN36xx_DXE_CH_CTRL_EN | \ + WCN36xx_DXE_CH_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_H2B) | \ + WCN36xx_DXE_CH_CTRL_EOP | WCN36xx_DXE_CH_CTRL_DIQ | \ + WCN36xx_DXE_CH_CTRL_PDU_REL | WCN36xx_DXE_CH_CTRL_BTHLD_SEL_SET(7) | \ + WCN36xx_DXE_CH_CTRL_PRIO_SET(6) | WCN36xx_DXE_CH_CTRL_INE_ED | \ + WCN36xx_DXE_CH_CTRL_INE_ERR | WCN36xx_DXE_CH_CTRL_INE_DONE | \ + WCN36xx_DXE_CH_CTRL_EDEN | WCN36xx_DXE_CH_CTRL_EDVEN | \ + WCN36xx_DXE_CH_CTRL_SEL_SET(4) | WCN36xx_DXE_CH_CTRL_ENDIANNESS | \ + WCN36xx_DXE_CH_CTRL_SWAP) + +#define WCN36XX_DXE_CH_DEFAULT_CTL_TX_L (WCN36xx_DXE_CH_CTRL_EN | \ + WCN36xx_DXE_CH_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_H2B) | \ + WCN36xx_DXE_CH_CTRL_EOP | WCN36xx_DXE_CH_CTRL_DIQ | \ + WCN36xx_DXE_CH_CTRL_PDU_REL | WCN36xx_DXE_CH_CTRL_BTHLD_SEL_SET(5) | \ + WCN36xx_DXE_CH_CTRL_PRIO_SET(4) | WCN36xx_DXE_CH_CTRL_INE_ED | \ + WCN36xx_DXE_CH_CTRL_INE_ERR | WCN36xx_DXE_CH_CTRL_INE_DONE | \ + WCN36xx_DXE_CH_CTRL_EDEN | WCN36xx_DXE_CH_CTRL_EDVEN | \ + WCN36xx_DXE_CH_CTRL_SEL_SET(0) | WCN36xx_DXE_CH_CTRL_ENDIANNESS | \ + WCN36xx_DXE_CH_CTRL_SWAP) /* Common DXE registers */ #define WCN36XX_DXE_MEM_CSR (WCN36XX_DXE_MEM_REG + 0x00) @@ -80,6 +262,10 @@ H2H_TEST_RX_TX = DMA2 #define WCN36XX_DXE_0_INT_DONE_CLR (WCN36XX_DXE_MEM_REG + 0x38) #define WCN36XX_DXE_0_INT_ERR_CLR (WCN36XX_DXE_MEM_REG + 0x3C) +#define WCN36XX_CH_STAT_INT_DONE_MASK 0x00008000 +#define WCN36XX_CH_STAT_INT_ERR_MASK 0x00004000 +#define WCN36XX_CH_STAT_INT_ED_MASK 0x00002000 + #define WCN36XX_DXE_0_CH0_STATUS (WCN36XX_DXE_MEM_REG + 0x404) #define WCN36XX_DXE_0_CH1_STATUS (WCN36XX_DXE_MEM_REG + 0x444) #define WCN36XX_DXE_0_CH2_STATUS (WCN36XX_DXE_MEM_REG + 0x484) diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index ab5be6d2c691..621e72b6ec99 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -261,7 +261,7 @@ static void wcn36xx_feat_caps_info(struct wcn36xx *wcn) for (i = 0; i < MAX_FEATURE_SUPPORTED; i++) { if (get_feat_caps(wcn->fw_feat_caps, i)) - wcn36xx_info("FW Cap %s\n", wcn36xx_get_cap_name(i)); + wcn36xx_dbg(WCN36XX_DBG_MAC, "FW Cap %s\n", wcn36xx_get_cap_name(i)); } } @@ -666,16 +666,13 @@ 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); + /* ieee80211_scan_completed will be called on FW scan indication */ + wcn36xx_smd_stop_hw_scan(wcn); + cancel_work_sync(&wcn->scan_work); } @@ -1283,6 +1280,7 @@ static int wcn36xx_probe(struct platform_device *pdev) wcn = hw->priv; wcn->hw = hw; wcn->dev = &pdev->dev; + wcn->first_boot = true; mutex_init(&wcn->conf_mutex); mutex_init(&wcn->hal_mutex); mutex_init(&wcn->scan_lock); diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c index 2a4871ca9c72..def6b23b777f 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.c +++ b/drivers/net/wireless/ath/wcn36xx/smd.c @@ -409,15 +409,17 @@ static int wcn36xx_smd_start_rsp(struct wcn36xx *wcn, void *buf, size_t len) wcn->fw_minor = rsp->start_rsp_params.version.minor; wcn->fw_major = rsp->start_rsp_params.version.major; - wcn36xx_info("firmware WLAN version '%s' and CRM version '%s'\n", - wcn->wlan_version, wcn->crm_version); - - wcn36xx_info("firmware API %u.%u.%u.%u, %u stations, %u bssids\n", - wcn->fw_major, wcn->fw_minor, - wcn->fw_version, wcn->fw_revision, - rsp->start_rsp_params.stations, - rsp->start_rsp_params.bssids); + if (wcn->first_boot) { + wcn->first_boot = false; + wcn36xx_info("firmware WLAN version '%s' and CRM version '%s'\n", + wcn->wlan_version, wcn->crm_version); + wcn36xx_info("firmware API %u.%u.%u.%u, %u stations, %u bssids\n", + wcn->fw_major, wcn->fw_minor, + wcn->fw_version, wcn->fw_revision, + rsp->start_rsp_params.stations, + rsp->start_rsp_params.bssids); + } return 0; } @@ -2138,6 +2140,8 @@ static int wcn36xx_smd_hw_scan_ind(struct wcn36xx *wcn, void *buf, size_t len) case WCN36XX_HAL_SCAN_IND_COMPLETED: mutex_lock(&wcn->scan_lock); wcn->scan_req = NULL; + if (wcn->scan_aborted) + scan_info.aborted = true; mutex_unlock(&wcn->scan_lock); ieee80211_scan_completed(wcn->hw, &scan_info); break; diff --git a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h index 81017e6703b4..5854adf43f3a 100644 --- a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h +++ b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h @@ -192,6 +192,8 @@ struct wcn36xx { u8 crm_version[WCN36XX_HAL_VERSION_LENGTH + 1]; u8 wlan_version[WCN36XX_HAL_VERSION_LENGTH + 1]; + bool first_boot; + /* IRQs */ int tx_irq; int rx_irq; diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index b799a5384abb..cdbb393863f3 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2012-2017 Qualcomm Atheros, Inc. + * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -17,8 +18,10 @@ #include #include #include +#include #include "wil6210.h" #include "wmi.h" +#include "fw.h" #define WIL_MAX_ROC_DURATION_MS 5000 @@ -258,9 +261,10 @@ int wil_iftype_nl2wmi(enum nl80211_iftype type) return -EOPNOTSUPP; } -int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid, +int wil_cid_fill_sinfo(struct wil6210_vif *vif, int cid, struct station_info *sinfo) { + struct wil6210_priv *wil = vif_to_wil(vif); struct wmi_notify_req_cmd cmd = { .cid = cid, .interval_usec = 0, @@ -272,17 +276,17 @@ int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid, struct wil_net_stats *stats = &wil->sta[cid].stats; int rc; - rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, &cmd, sizeof(cmd), + rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, vif->mid, &cmd, sizeof(cmd), WMI_NOTIFY_REQ_DONE_EVENTID, &reply, sizeof(reply), 20); if (rc) return rc; - wil_dbg_wmi(wil, "Link status for CID %d: {\n" + wil_dbg_wmi(wil, "Link status for CID %d MID %d: {\n" " MCS %d TSF 0x%016llx\n" " BF status 0x%08x RSSI %d SQI %d%%\n" " Tx Tpt %d goodput %d Rx goodput %d\n" " Sectors(rx:tx) my %d:%d peer %d:%d\n""}\n", - cid, le16_to_cpu(reply.evt.bf_mcs), + cid, vif->mid, le16_to_cpu(reply.evt.bf_mcs), le64_to_cpu(reply.evt.tsf), reply.evt.status, reply.evt.rssi, reply.evt.sqi, @@ -315,7 +319,7 @@ int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid, sinfo->tx_packets = stats->tx_packets; sinfo->tx_failed = stats->tx_errors; - if (test_bit(wil_status_fwconnected, wil->status)) { + if (test_bit(wil_vif_fwconnected, vif->status)) { sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); if (test_bit(WMI_FW_CAPABILITY_RSSI_REPORTING, wil->fw_capabilities)) @@ -331,30 +335,34 @@ static int wil_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, const u8 *mac, struct station_info *sinfo) { + struct wil6210_vif *vif = ndev_to_vif(ndev); struct wil6210_priv *wil = wiphy_to_wil(wiphy); int rc; - int cid = wil_find_cid(wil, mac); + int cid = wil_find_cid(wil, vif->mid, mac); - wil_dbg_misc(wil, "get_station: %pM CID %d\n", mac, cid); + wil_dbg_misc(wil, "get_station: %pM CID %d MID %d\n", mac, cid, + vif->mid); if (cid < 0) return cid; - rc = wil_cid_fill_sinfo(wil, cid, sinfo); + rc = wil_cid_fill_sinfo(vif, cid, sinfo); return rc; } /* - * Find @idx-th active STA for station dump. + * Find @idx-th active STA for specific MID for station dump. */ -static int wil_find_cid_by_idx(struct wil6210_priv *wil, int idx) +static int wil_find_cid_by_idx(struct wil6210_priv *wil, u8 mid, int idx) { int i; for (i = 0; i < ARRAY_SIZE(wil->sta); i++) { if (wil->sta[i].status == wil_sta_unused) continue; + if (wil->sta[i].mid != mid) + continue; if (idx == 0) return i; idx--; @@ -367,17 +375,19 @@ static int wil_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *dev, int idx, u8 *mac, struct station_info *sinfo) { + struct wil6210_vif *vif = ndev_to_vif(dev); struct wil6210_priv *wil = wiphy_to_wil(wiphy); int rc; - int cid = wil_find_cid_by_idx(wil, idx); + int cid = wil_find_cid_by_idx(wil, vif->mid, idx); if (cid < 0) return -ENOENT; ether_addr_copy(mac, wil->sta[cid].addr); - wil_dbg_misc(wil, "dump_station: %pM CID %d\n", mac, cid); + wil_dbg_misc(wil, "dump_station: %pM CID %d MID %d\n", mac, cid, + vif->mid); - rc = wil_cid_fill_sinfo(wil, cid, sinfo); + rc = wil_cid_fill_sinfo(vif, cid, sinfo); return rc; } @@ -388,7 +398,7 @@ static int wil_cfg80211_start_p2p_device(struct wiphy *wiphy, struct wil6210_priv *wil = wiphy_to_wil(wiphy); wil_dbg_misc(wil, "start_p2p_device: entered\n"); - wil->p2p.p2p_dev_started = 1; + wil->p2p_dev_started = 1; return 0; } @@ -396,20 +406,66 @@ static void wil_cfg80211_stop_p2p_device(struct wiphy *wiphy, struct wireless_dev *wdev) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); - struct wil_p2p_info *p2p = &wil->p2p; - if (!p2p->p2p_dev_started) + if (!wil->p2p_dev_started) return; wil_dbg_misc(wil, "stop_p2p_device: entered\n"); mutex_lock(&wil->mutex); - mutex_lock(&wil->p2p_wdev_mutex); + mutex_lock(&wil->vif_mutex); wil_p2p_stop_radio_operations(wil); - p2p->p2p_dev_started = 0; - mutex_unlock(&wil->p2p_wdev_mutex); + wil->p2p_dev_started = 0; + mutex_unlock(&wil->vif_mutex); mutex_unlock(&wil->mutex); } +static int wil_cfg80211_validate_add_iface(struct wil6210_priv *wil, + enum nl80211_iftype new_type) +{ + int i; + struct wireless_dev *wdev; + struct iface_combination_params params = { + .num_different_channels = 1, + }; + + for (i = 0; i < wil->max_vifs; i++) { + if (wil->vifs[i]) { + wdev = vif_to_wdev(wil->vifs[i]); + params.iftype_num[wdev->iftype]++; + } + } + params.iftype_num[new_type]++; + return cfg80211_check_combinations(wil->wiphy, ¶ms); +} + +static int wil_cfg80211_validate_change_iface(struct wil6210_priv *wil, + struct wil6210_vif *vif, + enum nl80211_iftype new_type) +{ + int i, ret = 0; + struct wireless_dev *wdev; + struct iface_combination_params params = { + .num_different_channels = 1, + }; + bool check_combos = false; + + for (i = 0; i < wil->max_vifs; i++) { + struct wil6210_vif *vif_pos = wil->vifs[i]; + + if (vif_pos && vif != vif_pos) { + wdev = vif_to_wdev(vif_pos); + params.iftype_num[wdev->iftype]++; + check_combos = true; + } + } + + if (check_combos) { + params.iftype_num[new_type]++; + ret = cfg80211_check_combinations(wil->wiphy, ¶ms); + } + return ret; +} + static struct wireless_dev * wil_cfg80211_add_iface(struct wiphy *wiphy, const char *name, unsigned char name_assign_type, @@ -417,51 +473,137 @@ wil_cfg80211_add_iface(struct wiphy *wiphy, const char *name, struct vif_params *params) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); - struct net_device *ndev = wil_to_ndev(wil); - struct wireless_dev *p2p_wdev; + struct net_device *ndev_main = wil->main_ndev, *ndev; + struct wil6210_vif *vif; + struct wireless_dev *p2p_wdev, *wdev; + int rc; - wil_dbg_misc(wil, "add_iface\n"); + wil_dbg_misc(wil, "add_iface, type %d\n", type); - if (type != NL80211_IFTYPE_P2P_DEVICE) { - wil_err(wil, "unsupported iftype %d\n", type); + /* P2P device is not a real virtual interface, it is a management-only + * interface that shares the main interface. + * Skip concurrency checks here. + */ + if (type == NL80211_IFTYPE_P2P_DEVICE) { + if (wil->p2p_wdev) { + wil_err(wil, "P2P_DEVICE interface already created\n"); + return ERR_PTR(-EINVAL); + } + + p2p_wdev = kzalloc(sizeof(*p2p_wdev), GFP_KERNEL); + if (!p2p_wdev) + return ERR_PTR(-ENOMEM); + + p2p_wdev->iftype = type; + p2p_wdev->wiphy = wiphy; + /* use our primary ethernet address */ + ether_addr_copy(p2p_wdev->address, ndev_main->perm_addr); + + wil->p2p_wdev = p2p_wdev; + + return p2p_wdev; + } + + if (!wil->wiphy->n_iface_combinations) { + wil_err(wil, "virtual interfaces not supported\n"); return ERR_PTR(-EINVAL); } - if (wil->p2p_wdev) { - wil_err(wil, "P2P_DEVICE interface already created\n"); - return ERR_PTR(-EINVAL); + rc = wil_cfg80211_validate_add_iface(wil, type); + if (rc) { + wil_err(wil, "iface validation failed, err=%d\n", rc); + return ERR_PTR(rc); } - p2p_wdev = kzalloc(sizeof(*p2p_wdev), GFP_KERNEL); - if (!p2p_wdev) - return ERR_PTR(-ENOMEM); + vif = wil_vif_alloc(wil, name, name_assign_type, type); + if (IS_ERR(vif)) + return ERR_CAST(vif); - p2p_wdev->iftype = type; - p2p_wdev->wiphy = wiphy; - /* use our primary ethernet address */ - ether_addr_copy(p2p_wdev->address, ndev->perm_addr); + ndev = vif_to_ndev(vif); + ether_addr_copy(ndev->perm_addr, ndev_main->perm_addr); + if (is_valid_ether_addr(params->macaddr)) { + ether_addr_copy(ndev->dev_addr, params->macaddr); + } else { + ether_addr_copy(ndev->dev_addr, ndev_main->perm_addr); + ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << vif->mid)) | + 0x2; /* locally administered */ + } + wdev = vif_to_wdev(vif); + ether_addr_copy(wdev->address, ndev->dev_addr); - wil->p2p_wdev = p2p_wdev; + rc = wil_vif_add(wil, vif); + if (rc) + goto out; - return p2p_wdev; + wil_info(wil, "added VIF, mid %d iftype %d MAC %pM\n", + vif->mid, type, wdev->address); + return wdev; +out: + wil_vif_free(vif); + return ERR_PTR(rc); +} + +int wil_vif_prepare_stop(struct wil6210_vif *vif) +{ + struct wil6210_priv *wil = vif_to_wil(vif); + struct wireless_dev *wdev = vif_to_wdev(vif); + struct net_device *ndev; + int rc; + + if (wdev->iftype != NL80211_IFTYPE_AP) + return 0; + + ndev = vif_to_ndev(vif); + if (netif_carrier_ok(ndev)) { + rc = wmi_pcp_stop(vif); + if (rc) { + wil_info(wil, "failed to stop AP, status %d\n", + rc); + /* continue */ + } + wil_bcast_fini(vif); + netif_carrier_off(ndev); + } + + return 0; } static int wil_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wil6210_vif *vif = wdev_to_vif(wil, wdev); + int rc; wil_dbg_misc(wil, "del_iface\n"); - if (wdev != wil->p2p_wdev) { - wil_err(wil, "delete of incorrect interface 0x%p\n", wdev); + if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE) { + if (wdev != wil->p2p_wdev) { + wil_err(wil, "delete of incorrect interface 0x%p\n", + wdev); + return -EINVAL; + } + + wil_cfg80211_stop_p2p_device(wiphy, wdev); + wil_p2p_wdev_free(wil); + return 0; + } + + if (vif->mid == 0) { + wil_err(wil, "cannot remove the main interface\n"); return -EINVAL; } - wil_cfg80211_stop_p2p_device(wiphy, wdev); - wil_p2p_wdev_free(wil); + rc = wil_vif_prepare_stop(vif); + if (rc) + goto out; - return 0; + wil_info(wil, "deleted VIF, mid %d iftype %d MAC %pM\n", + vif->mid, wdev->iftype, wdev->address); + + wil_vif_remove(wil, vif->mid); +out: + return rc; } static int wil_cfg80211_change_iface(struct wiphy *wiphy, @@ -470,12 +612,26 @@ static int wil_cfg80211_change_iface(struct wiphy *wiphy, struct vif_params *params) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); - struct wireless_dev *wdev = wil_to_wdev(wil); + struct wil6210_vif *vif = ndev_to_vif(ndev); + struct wireless_dev *wdev = vif_to_wdev(vif); int rc; + bool fw_reset = false; wil_dbg_misc(wil, "change_iface: type=%d\n", type); - if (netif_running(wil_to_ndev(wil)) && !wil_is_recovery_blocked(wil)) { + if (wiphy->n_iface_combinations) { + rc = wil_cfg80211_validate_change_iface(wil, vif, type); + if (rc) { + wil_err(wil, "iface validation failed, err=%d\n", rc); + return rc; + } + } + + /* do not reset FW when there are active VIFs, + * because it can cause significant disruption + */ + if (!wil_has_other_active_ifaces(wil, ndev, true, false) && + netif_running(ndev) && !wil_is_recovery_blocked(wil)) { wil_dbg_misc(wil, "interface is up. resetting...\n"); mutex_lock(&wil->mutex); __wil_down(wil); @@ -484,6 +640,7 @@ static int wil_cfg80211_change_iface(struct wiphy *wiphy, if (rc) return rc; + fw_reset = true; } switch (type) { @@ -500,8 +657,18 @@ static int wil_cfg80211_change_iface(struct wiphy *wiphy, return -EOPNOTSUPP; } - wdev->iftype = type; + if (vif->mid != 0 && wil_has_active_ifaces(wil, true, false)) { + if (!fw_reset) + wil_vif_prepare_stop(vif); + rc = wmi_port_delete(wil, vif->mid); + if (rc) + return rc; + rc = wmi_port_allocate(wil, vif->mid, ndev->dev_addr, type); + if (rc) + return rc; + } + wdev->iftype = type; return 0; } @@ -510,6 +677,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, { struct wil6210_priv *wil = wiphy_to_wil(wiphy); struct wireless_dev *wdev = request->wdev; + struct wil6210_vif *vif = wdev_to_vif(wil, wdev); struct { struct wmi_start_scan_cmd cmd; u16 chnl[4]; @@ -537,35 +705,38 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, mutex_lock(&wil->mutex); - mutex_lock(&wil->p2p_wdev_mutex); - if (wil->scan_request || wil->p2p.discovery_started) { + mutex_lock(&wil->vif_mutex); + if (vif->scan_request || vif->p2p.discovery_started) { wil_err(wil, "Already scanning\n"); - mutex_unlock(&wil->p2p_wdev_mutex); + mutex_unlock(&wil->vif_mutex); rc = -EAGAIN; goto out; } - mutex_unlock(&wil->p2p_wdev_mutex); + mutex_unlock(&wil->vif_mutex); if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE) { - if (!wil->p2p.p2p_dev_started) { + if (!wil->p2p_dev_started) { wil_err(wil, "P2P search requested on stopped P2P device\n"); rc = -EIO; goto out; } /* social scan on P2P_DEVICE is handled as p2p search */ if (wil_p2p_is_social_scan(request)) { - wil->scan_request = request; - wil->radio_wdev = wdev; - rc = wil_p2p_search(wil, request); + vif->scan_request = request; + if (vif->mid == 0) + wil->radio_wdev = wdev; + rc = wil_p2p_search(vif, request); if (rc) { - wil->radio_wdev = wil_to_wdev(wil); - wil->scan_request = NULL; + if (vif->mid == 0) + wil->radio_wdev = + wil->main_ndev->ieee80211_ptr; + vif->scan_request = NULL; } goto out; } } - (void)wil_p2p_stop_discovery(wil); + (void)wil_p2p_stop_discovery(vif); wil_dbg_misc(wil, "Start scan_request 0x%p\n", request); wil_dbg_misc(wil, "SSID count: %d", request->n_ssids); @@ -578,18 +749,18 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, } if (request->n_ssids) - rc = wmi_set_ssid(wil, request->ssids[0].ssid_len, + rc = wmi_set_ssid(vif, request->ssids[0].ssid_len, request->ssids[0].ssid); else - rc = wmi_set_ssid(wil, 0, NULL); + rc = wmi_set_ssid(vif, 0, NULL); if (rc) { wil_err(wil, "set SSID for scan request failed: %d\n", rc); goto out; } - wil->scan_request = request; - mod_timer(&wil->scan_timer, jiffies + WIL6210_SCAN_TO); + vif->scan_request = request; + mod_timer(&vif->scan_timer, jiffies + WIL6210_SCAN_TO); memset(&cmd, 0, sizeof(cmd)); cmd.cmd.scan_type = WMI_ACTIVE_SCAN; @@ -616,7 +787,8 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, else wil_dbg_misc(wil, "Scan has no IE's\n"); - rc = wmi_set_ie(wil, WMI_FRAME_PROBE_REQ, request->ie_len, request->ie); + rc = wmi_set_ie(vif, WMI_FRAME_PROBE_REQ, + request->ie_len, request->ie); if (rc) goto out_restore; @@ -625,15 +797,18 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, wil_dbg_misc(wil, "active scan with discovery_mode=1\n"); } - wil->radio_wdev = wdev; - rc = wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) + - cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0])); + if (vif->mid == 0) + wil->radio_wdev = wdev; + rc = wmi_send(wil, WMI_START_SCAN_CMDID, vif->mid, + &cmd, sizeof(cmd.cmd) + + cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0])); out_restore: if (rc) { - del_timer_sync(&wil->scan_timer); - wil->radio_wdev = wil_to_wdev(wil); - wil->scan_request = NULL; + del_timer_sync(&vif->scan_timer); + if (vif->mid == 0) + wil->radio_wdev = wil->main_ndev->ieee80211_ptr; + vif->scan_request = NULL; } out: mutex_unlock(&wil->mutex); @@ -644,27 +819,28 @@ static void wil_cfg80211_abort_scan(struct wiphy *wiphy, struct wireless_dev *wdev) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wil6210_vif *vif = wdev_to_vif(wil, wdev); wil_dbg_misc(wil, "wdev=0x%p iftype=%d\n", wdev, wdev->iftype); mutex_lock(&wil->mutex); - mutex_lock(&wil->p2p_wdev_mutex); + mutex_lock(&wil->vif_mutex); - if (!wil->scan_request) + if (!vif->scan_request) goto out; - if (wdev != wil->scan_request->wdev) { + if (wdev != vif->scan_request->wdev) { wil_dbg_misc(wil, "abort scan was called on the wrong iface\n"); goto out; } - if (wil->radio_wdev == wil->p2p_wdev) + if (wdev == wil->p2p_wdev && wil->radio_wdev == wil->p2p_wdev) wil_p2p_stop_radio_operations(wil); else - wil_abort_scan(wil, true); + wil_abort_scan(vif, true); out: - mutex_unlock(&wil->p2p_wdev_mutex); + mutex_unlock(&wil->vif_mutex); mutex_unlock(&wil->mutex); } @@ -715,6 +891,7 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, struct cfg80211_connect_params *sme) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wil6210_vif *vif = ndev_to_vif(ndev); struct cfg80211_bss *bss; struct wmi_connect_cmd conn; const u8 *ssid_eid; @@ -723,11 +900,11 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, int rc = 0; enum ieee80211_bss_type bss_type = IEEE80211_BSS_TYPE_ESS; - wil_dbg_misc(wil, "connect\n"); + wil_dbg_misc(wil, "connect, mid=%d\n", vif->mid); wil_print_connect_params(wil, sme); - if (test_bit(wil_status_fwconnecting, wil->status) || - test_bit(wil_status_fwconnected, wil->status)) + if (test_bit(wil_vif_fwconnecting, vif->status) || + test_bit(wil_vif_fwconnected, vif->status)) return -EALREADY; if (sme->ie_len > WMI_MAX_IE_LEN) { @@ -758,18 +935,18 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, rc = -ENOENT; goto out; } - wil->privacy = sme->privacy; - wil->pbss = sme->pbss; + vif->privacy = sme->privacy; + vif->pbss = sme->pbss; - if (wil->privacy) { + if (vif->privacy) { /* For secure assoc, remove old keys */ - rc = wmi_del_cipher_key(wil, 0, bss->bssid, + rc = wmi_del_cipher_key(vif, 0, bss->bssid, WMI_KEY_USE_PAIRWISE); if (rc) { wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD(PTK) failed\n"); goto out; } - rc = wmi_del_cipher_key(wil, 0, bss->bssid, + rc = wmi_del_cipher_key(vif, 0, bss->bssid, WMI_KEY_USE_RX_GROUP); if (rc) { wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD(GTK) failed\n"); @@ -781,7 +958,7 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, * elements. Send it also in case it's empty, to erase previously set * ies in FW. */ - rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_REQ, sme->ie_len, sme->ie); + rc = wmi_set_ie(vif, WMI_FRAME_ASSOC_REQ, sme->ie_len, sme->ie); if (rc) goto out; @@ -799,7 +976,7 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, bss->capability); goto out; } - if (wil->privacy) { + if (vif->privacy) { if (rsn_eid) { /* regular secure connection */ conn.dot11_auth_mode = WMI_AUTH11_SHARED; conn.auth_mode = WMI_AUTH_WPA2_PSK; @@ -831,18 +1008,19 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, ether_addr_copy(conn.bssid, bss->bssid); ether_addr_copy(conn.dst_mac, bss->bssid); - set_bit(wil_status_fwconnecting, wil->status); + set_bit(wil_vif_fwconnecting, vif->status); - rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn)); + rc = wmi_send(wil, WMI_CONNECT_CMDID, vif->mid, &conn, sizeof(conn)); if (rc == 0) { netif_carrier_on(ndev); - wil6210_bus_request(wil, WIL_MAX_BUS_REQUEST_KBPS); - wil->bss = bss; + if (!wil_has_other_active_ifaces(wil, ndev, false, true)) + wil6210_bus_request(wil, WIL_MAX_BUS_REQUEST_KBPS); + vif->bss = bss; /* Connect can take lots of time */ - mod_timer(&wil->connect_timer, + mod_timer(&vif->connect_timer, jiffies + msecs_to_jiffies(5000)); } else { - clear_bit(wil_status_fwconnecting, wil->status); + clear_bit(wil_vif_fwconnecting, vif->status); } out: @@ -857,17 +1035,19 @@ static int wil_cfg80211_disconnect(struct wiphy *wiphy, { int rc; struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wil6210_vif *vif = ndev_to_vif(ndev); - wil_dbg_misc(wil, "disconnect: reason=%d\n", reason_code); + wil_dbg_misc(wil, "disconnect: reason=%d, mid=%d\n", + reason_code, vif->mid); - if (!(test_bit(wil_status_fwconnecting, wil->status) || - test_bit(wil_status_fwconnected, wil->status))) { + if (!(test_bit(wil_vif_fwconnecting, vif->status) || + test_bit(wil_vif_fwconnected, vif->status))) { wil_err(wil, "Disconnect was called while disconnected\n"); return 0; } - wil->locally_generated_disc = true; - rc = wmi_call(wil, WMI_DISCONNECT_CMDID, NULL, 0, + vif->locally_generated_disc = true; + rc = wmi_call(wil, WMI_DISCONNECT_CMDID, vif->mid, NULL, 0, WMI_DISCONNECT_EVENTID, NULL, 0, WIL6210_DISCONNECT_TO_MS); if (rc) @@ -903,6 +1083,7 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, const u8 *buf = params->buf; size_t len = params->len, total; struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wil6210_vif *vif = wdev_to_vif(wil, wdev); int rc; bool tx_status = false; struct ieee80211_mgmt *mgmt_frame = (void *)buf; @@ -919,7 +1100,7 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, * different from currently "listened" channel and fail if it is. */ - wil_dbg_misc(wil, "mgmt_tx\n"); + wil_dbg_misc(wil, "mgmt_tx mid %d\n", vif->mid); wil_hex_dump_misc("mgmt tx frame ", DUMP_PREFIX_OFFSET, 16, 1, buf, len, true); @@ -940,7 +1121,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, total, + rc = wmi_call(wil, WMI_SW_TX_REQ_CMDID, vif->mid, cmd, total, WMI_SW_TX_COMPLETE_EVENTID, &evt, sizeof(evt), 2000); if (rc == 0) tx_status = !evt.evt.status; @@ -962,10 +1143,10 @@ static int wil_cfg80211_set_channel(struct wiphy *wiphy, return 0; } -static enum wmi_key_usage wil_detect_key_usage(struct wil6210_priv *wil, +static enum wmi_key_usage wil_detect_key_usage(struct wireless_dev *wdev, bool pairwise) { - struct wireless_dev *wdev = wil_to_wdev(wil); + struct wil6210_priv *wil = wdev_to_wil(wdev); enum wmi_key_usage rc; if (pairwise) { @@ -993,7 +1174,7 @@ static enum wmi_key_usage wil_detect_key_usage(struct wil6210_priv *wil, } static struct wil_sta_info * -wil_find_sta_by_key_usage(struct wil6210_priv *wil, +wil_find_sta_by_key_usage(struct wil6210_priv *wil, u8 mid, enum wmi_key_usage key_usage, const u8 *mac_addr) { int cid = -EINVAL; @@ -1003,9 +1184,9 @@ wil_find_sta_by_key_usage(struct wil6210_priv *wil, /* supplicant provides Rx group key in STA mode with NULL MAC address */ if (mac_addr) - cid = wil_find_cid(wil, mac_addr); + cid = wil_find_cid(wil, mid, mac_addr); else if (key_usage == WMI_KEY_USE_RX_GROUP) - cid = wil_find_cid_by_idx(wil, 0); + cid = wil_find_cid_by_idx(wil, mid, 0); if (cid < 0) { wil_err(wil, "No CID for %pM %s\n", mac_addr, key_usage_str[key_usage]); @@ -1082,9 +1263,12 @@ static int wil_cfg80211_add_key(struct wiphy *wiphy, struct key_params *params) { int rc; + struct wil6210_vif *vif = ndev_to_vif(ndev); struct wil6210_priv *wil = wiphy_to_wil(wiphy); - enum wmi_key_usage key_usage = wil_detect_key_usage(wil, pairwise); - struct wil_sta_info *cs = wil_find_sta_by_key_usage(wil, key_usage, + struct wireless_dev *wdev = vif_to_wdev(vif); + enum wmi_key_usage key_usage = wil_detect_key_usage(wdev, pairwise); + struct wil_sta_info *cs = wil_find_sta_by_key_usage(wil, vif->mid, + key_usage, mac_addr); if (!params) { @@ -1114,7 +1298,7 @@ static int wil_cfg80211_add_key(struct wiphy *wiphy, return -EINVAL; } - rc = wmi_add_cipher_key(wil, key_index, mac_addr, params->key_len, + rc = wmi_add_cipher_key(vif, key_index, mac_addr, params->key_len, params->key, key_usage); if (!rc) wil_set_crypto_rx(key_index, key_usage, cs, params); @@ -1127,9 +1311,12 @@ static int wil_cfg80211_del_key(struct wiphy *wiphy, u8 key_index, bool pairwise, const u8 *mac_addr) { + struct wil6210_vif *vif = ndev_to_vif(ndev); struct wil6210_priv *wil = wiphy_to_wil(wiphy); - enum wmi_key_usage key_usage = wil_detect_key_usage(wil, pairwise); - struct wil_sta_info *cs = wil_find_sta_by_key_usage(wil, key_usage, + struct wireless_dev *wdev = vif_to_wdev(vif); + enum wmi_key_usage key_usage = wil_detect_key_usage(wdev, pairwise); + struct wil_sta_info *cs = wil_find_sta_by_key_usage(wil, vif->mid, + key_usage, mac_addr); wil_dbg_misc(wil, "del_key: %pM %s[%d]\n", mac_addr, @@ -1142,7 +1329,7 @@ static int wil_cfg80211_del_key(struct wiphy *wiphy, if (!IS_ERR_OR_NULL(cs)) wil_del_rx_key(key_index, key_usage, cs); - return wmi_del_cipher_key(wil, key_index, mac_addr, key_usage); + return wmi_del_cipher_key(vif, key_index, mac_addr, key_usage); } /* Need to be present or wiphy_new() will WARN */ @@ -1179,10 +1366,11 @@ static int wil_cancel_remain_on_channel(struct wiphy *wiphy, u64 cookie) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wil6210_vif *vif = wdev_to_vif(wil, wdev); wil_dbg_misc(wil, "cancel_remain_on_channel\n"); - return wil_p2p_cancel_listen(wil, cookie); + return wil_p2p_cancel_listen(vif, cookie); } /** @@ -1275,11 +1463,10 @@ static void wil_print_bcon_data(struct cfg80211_beacon_data *b) } /* internal functions for device reset and starting AP */ -static int _wil_cfg80211_set_ies(struct wiphy *wiphy, +static int _wil_cfg80211_set_ies(struct wil6210_vif *vif, struct cfg80211_beacon_data *bcon) { int rc; - struct wil6210_priv *wil = wiphy_to_wil(wiphy); u16 len = 0, proberesp_len = 0; u8 *ies = NULL, *proberesp = NULL; @@ -1300,20 +1487,21 @@ static int _wil_cfg80211_set_ies(struct wiphy *wiphy, if (rc) goto out; - rc = wmi_set_ie(wil, WMI_FRAME_PROBE_RESP, len, ies); + rc = wmi_set_ie(vif, WMI_FRAME_PROBE_RESP, len, ies); if (rc) goto out; if (bcon->assocresp_ies) - rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP, + rc = wmi_set_ie(vif, WMI_FRAME_ASSOC_RESP, bcon->assocresp_ies_len, bcon->assocresp_ies); else - rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP, len, ies); + rc = wmi_set_ie(vif, WMI_FRAME_ASSOC_RESP, len, ies); #if 0 /* to use beacon IE's, remove this #if 0 */ if (rc) goto out; - rc = wmi_set_ie(wil, WMI_FRAME_BEACON, bcon->tail_len, bcon->tail); + rc = wmi_set_ie(vif, WMI_FRAME_BEACON, + bcon->tail_len, bcon->tail); #endif out: kfree(ies); @@ -1328,6 +1516,7 @@ static int _wil_cfg80211_start_ap(struct wiphy *wiphy, u8 hidden_ssid, u32 pbss) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wil6210_vif *vif = ndev_to_vif(ndev); int rc; struct wireless_dev *wdev = ndev->ieee80211_ptr; u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype); @@ -1336,7 +1525,7 @@ static int _wil_cfg80211_start_ap(struct wiphy *wiphy, if (pbss) wmi_nettype = WMI_NETTYPE_P2P; - wil_dbg_misc(wil, "start_ap: is_go=%d\n", is_go); + wil_dbg_misc(wil, "start_ap: mid=%d, is_go=%d\n", vif->mid, is_go); if (is_go && !pbss) { wil_err(wil, "P2P GO must be in PBSS\n"); return -ENOTSUPP; @@ -1346,42 +1535,46 @@ static int _wil_cfg80211_start_ap(struct wiphy *wiphy, mutex_lock(&wil->mutex); - __wil_down(wil); - rc = __wil_up(wil); + if (!wil_has_other_active_ifaces(wil, ndev, true, false)) { + __wil_down(wil); + rc = __wil_up(wil); + if (rc) + goto out; + } + + rc = wmi_set_ssid(vif, ssid_len, ssid); if (rc) goto out; - rc = wmi_set_ssid(wil, ssid_len, ssid); + rc = _wil_cfg80211_set_ies(vif, bcon); if (rc) goto out; - rc = _wil_cfg80211_set_ies(wiphy, bcon); - if (rc) - goto out; - - wil->privacy = privacy; - wil->channel = chan; - wil->hidden_ssid = hidden_ssid; - wil->pbss = pbss; + vif->privacy = privacy; + vif->channel = chan; + vif->hidden_ssid = hidden_ssid; + vif->pbss = pbss; netif_carrier_on(ndev); - wil6210_bus_request(wil, WIL_MAX_BUS_REQUEST_KBPS); + if (!wil_has_other_active_ifaces(wil, ndev, false, true)) + wil6210_bus_request(wil, WIL_MAX_BUS_REQUEST_KBPS); - rc = wmi_pcp_start(wil, bi, wmi_nettype, chan, hidden_ssid, is_go); + rc = wmi_pcp_start(vif, bi, wmi_nettype, chan, hidden_ssid, is_go); if (rc) goto err_pcp_start; - rc = wil_bcast_init(wil); + rc = wil_bcast_init(vif); if (rc) goto err_bcast; goto out; /* success */ err_bcast: - wmi_pcp_stop(wil); + wmi_pcp_stop(vif); err_pcp_start: netif_carrier_off(ndev); - wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS); + if (!wil_has_other_active_ifaces(wil, ndev, false, true)) + wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS); out: mutex_unlock(&wil->mutex); return rc; @@ -1392,10 +1585,11 @@ static int wil_cfg80211_change_beacon(struct wiphy *wiphy, struct cfg80211_beacon_data *bcon) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wil6210_vif *vif = ndev_to_vif(ndev); int rc; u32 privacy = 0; - wil_dbg_misc(wil, "change_beacon\n"); + wil_dbg_misc(wil, "change_beacon, mid=%d\n", vif->mid); wil_print_bcon_data(bcon); if (bcon->tail && @@ -1404,20 +1598,20 @@ static int wil_cfg80211_change_beacon(struct wiphy *wiphy, privacy = 1; /* in case privacy has changed, need to restart the AP */ - if (wil->privacy != privacy) { + if (vif->privacy != privacy) { struct wireless_dev *wdev = ndev->ieee80211_ptr; wil_dbg_misc(wil, "privacy changed %d=>%d. Restarting AP\n", - wil->privacy, privacy); + vif->privacy, privacy); rc = _wil_cfg80211_start_ap(wiphy, ndev, wdev->ssid, wdev->ssid_len, privacy, wdev->beacon_interval, - wil->channel, bcon, - wil->hidden_ssid, - wil->pbss); + vif->channel, bcon, + vif->hidden_ssid, + vif->pbss); } else { - rc = _wil_cfg80211_set_ies(wiphy, bcon); + rc = _wil_cfg80211_set_ies(vif, bcon); } return rc; @@ -1484,20 +1678,27 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wil6210_vif *vif = ndev_to_vif(ndev); + bool last; - wil_dbg_misc(wil, "stop_ap\n"); + wil_dbg_misc(wil, "stop_ap, mid=%d\n", vif->mid); netif_carrier_off(ndev); - wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS); - wil_set_recovery_state(wil, fw_recovery_idle); - - set_bit(wil_status_resetting, wil->status); + last = !wil_has_other_active_ifaces(wil, ndev, false, true); + if (last) { + wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS); + wil_set_recovery_state(wil, fw_recovery_idle); + set_bit(wil_status_resetting, wil->status); + } mutex_lock(&wil->mutex); - wmi_pcp_stop(wil); + wmi_pcp_stop(vif); - __wil_down(wil); + if (last) + __wil_down(wil); + else + wil_bcast_fini(vif); mutex_unlock(&wil->mutex); @@ -1509,9 +1710,11 @@ static int wil_cfg80211_add_station(struct wiphy *wiphy, const u8 *mac, struct station_parameters *params) { + struct wil6210_vif *vif = ndev_to_vif(dev); struct wil6210_priv *wil = wiphy_to_wil(wiphy); - wil_dbg_misc(wil, "add station %pM aid %d\n", mac, params->aid); + wil_dbg_misc(wil, "add station %pM aid %d mid %d\n", + mac, params->aid, vif->mid); if (!disable_ap_sme) { wil_err(wil, "not supported with AP SME enabled\n"); @@ -1523,20 +1726,21 @@ static int wil_cfg80211_add_station(struct wiphy *wiphy, return -EINVAL; } - return wmi_new_sta(wil, mac, params->aid); + return wmi_new_sta(vif, mac, params->aid); } static int wil_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, struct station_del_parameters *params) { + struct wil6210_vif *vif = ndev_to_vif(dev); struct wil6210_priv *wil = wiphy_to_wil(wiphy); - wil_dbg_misc(wil, "del_station: %pM, reason=%d\n", params->mac, - params->reason_code); + wil_dbg_misc(wil, "del_station: %pM, reason=%d mid=%d\n", + params->mac, params->reason_code, vif->mid); mutex_lock(&wil->mutex); - wil6210_disconnect(wil, params->mac, params->reason_code, false); + wil6210_disconnect(vif, params->mac, params->reason_code, false); mutex_unlock(&wil->mutex); return 0; @@ -1547,13 +1751,15 @@ static int wil_cfg80211_change_station(struct wiphy *wiphy, const u8 *mac, struct station_parameters *params) { + struct wil6210_vif *vif = ndev_to_vif(dev); struct wil6210_priv *wil = wiphy_to_wil(wiphy); int authorize; int cid, i; struct vring_tx_data *txdata = NULL; - wil_dbg_misc(wil, "change station %pM mask 0x%x set 0x%x\n", mac, - params->sta_flags_mask, params->sta_flags_set); + wil_dbg_misc(wil, "change station %pM mask 0x%x set 0x%x mid %d\n", + mac, params->sta_flags_mask, params->sta_flags_set, + vif->mid); if (!disable_ap_sme) { wil_dbg_misc(wil, "not supported with AP SME enabled\n"); @@ -1563,7 +1769,7 @@ static int wil_cfg80211_change_station(struct wiphy *wiphy, if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED))) return 0; - cid = wil_find_cid(wil, mac); + cid = wil_find_cid(wil, vif->mid, mac); if (cid < 0) { wil_err(wil, "station not found\n"); return -ENOLINK; @@ -1590,9 +1796,10 @@ static int wil_cfg80211_change_station(struct wiphy *wiphy, /* probe_client handling */ static void wil_probe_client_handle(struct wil6210_priv *wil, + struct wil6210_vif *vif, struct wil_probe_client_req *req) { - struct net_device *ndev = wil_to_ndev(wil); + struct net_device *ndev = vif_to_ndev(vif); struct wil_sta_info *sta = &wil->sta[req->cid]; /* assume STA is alive if it is still connected, * else FW will disconnect it @@ -1603,51 +1810,53 @@ static void wil_probe_client_handle(struct wil6210_priv *wil, 0, false, GFP_KERNEL); } -static struct list_head *next_probe_client(struct wil6210_priv *wil) +static struct list_head *next_probe_client(struct wil6210_vif *vif) { struct list_head *ret = NULL; - mutex_lock(&wil->probe_client_mutex); + mutex_lock(&vif->probe_client_mutex); - if (!list_empty(&wil->probe_client_pending)) { - ret = wil->probe_client_pending.next; + if (!list_empty(&vif->probe_client_pending)) { + ret = vif->probe_client_pending.next; list_del(ret); } - mutex_unlock(&wil->probe_client_mutex); + mutex_unlock(&vif->probe_client_mutex); return ret; } void wil_probe_client_worker(struct work_struct *work) { - struct wil6210_priv *wil = container_of(work, struct wil6210_priv, - probe_client_worker); + struct wil6210_vif *vif = container_of(work, struct wil6210_vif, + probe_client_worker); + struct wil6210_priv *wil = vif_to_wil(vif); struct wil_probe_client_req *req; struct list_head *lh; - while ((lh = next_probe_client(wil)) != NULL) { + while ((lh = next_probe_client(vif)) != NULL) { req = list_entry(lh, struct wil_probe_client_req, list); - wil_probe_client_handle(wil, req); + wil_probe_client_handle(wil, vif, req); kfree(req); } } -void wil_probe_client_flush(struct wil6210_priv *wil) +void wil_probe_client_flush(struct wil6210_vif *vif) { struct wil_probe_client_req *req, *t; + struct wil6210_priv *wil = vif_to_wil(vif); wil_dbg_misc(wil, "probe_client_flush\n"); - mutex_lock(&wil->probe_client_mutex); + mutex_lock(&vif->probe_client_mutex); - list_for_each_entry_safe(req, t, &wil->probe_client_pending, list) { + list_for_each_entry_safe(req, t, &vif->probe_client_pending, list) { list_del(&req->list); kfree(req); } - mutex_unlock(&wil->probe_client_mutex); + mutex_unlock(&vif->probe_client_mutex); } static int wil_cfg80211_probe_client(struct wiphy *wiphy, @@ -1655,10 +1864,12 @@ static int wil_cfg80211_probe_client(struct wiphy *wiphy, const u8 *peer, u64 *cookie) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wil6210_vif *vif = ndev_to_vif(dev); struct wil_probe_client_req *req; - int cid = wil_find_cid(wil, peer); + int cid = wil_find_cid(wil, vif->mid, peer); - wil_dbg_misc(wil, "probe_client: %pM => CID %d\n", peer, cid); + wil_dbg_misc(wil, "probe_client: %pM => CID %d MID %d\n", + peer, cid, vif->mid); if (cid < 0) return -ENOLINK; @@ -1670,12 +1881,12 @@ static int wil_cfg80211_probe_client(struct wiphy *wiphy, req->cid = cid; req->cookie = cid; - mutex_lock(&wil->probe_client_mutex); - list_add_tail(&req->list, &wil->probe_client_pending); - mutex_unlock(&wil->probe_client_mutex); + mutex_lock(&vif->probe_client_mutex); + list_add_tail(&req->list, &vif->probe_client_pending); + mutex_unlock(&vif->probe_client_mutex); *cookie = req->cookie; - queue_work(wil->wq_service, &wil->probe_client_worker); + queue_work(wil->wq_service, &vif->probe_client_worker); return 0; } @@ -1684,11 +1895,12 @@ static int wil_cfg80211_change_bss(struct wiphy *wiphy, struct bss_parameters *params) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wil6210_vif *vif = ndev_to_vif(dev); if (params->ap_isolate >= 0) { - wil_dbg_misc(wil, "change_bss: ap_isolate %d => %d\n", - wil->ap_isolate, params->ap_isolate); - wil->ap_isolate = params->ap_isolate; + wil_dbg_misc(wil, "change_bss: ap_isolate MID %d, %d => %d\n", + vif->mid, vif->ap_isolate, params->ap_isolate); + vif->ap_isolate = params->ap_isolate; } return 0; @@ -1732,10 +1944,10 @@ static int wil_cfg80211_suspend(struct wiphy *wiphy, wil_dbg_pm(wil, "suspending\n"); mutex_lock(&wil->mutex); - mutex_lock(&wil->p2p_wdev_mutex); + mutex_lock(&wil->vif_mutex); wil_p2p_stop_radio_operations(wil); - wil_abort_scan(wil, true); - mutex_unlock(&wil->p2p_wdev_mutex); + wil_abort_scan_all_vifs(wil, true); + mutex_unlock(&wil->vif_mutex); mutex_unlock(&wil->mutex); out: @@ -1757,8 +1969,12 @@ wil_cfg80211_sched_scan_start(struct wiphy *wiphy, struct cfg80211_sched_scan_request *request) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wil6210_vif *vif = ndev_to_vif(dev); int i, rc; + if (vif->mid != 0) + return -EOPNOTSUPP; + wil_dbg_misc(wil, "sched scan start: n_ssids %d, ie_len %zu, flags 0x%x\n", request->n_ssids, request->ie_len, request->flags); @@ -1792,7 +2008,8 @@ wil_cfg80211_sched_scan_start(struct wiphy *wiphy, i, sp->interval, sp->iterations); } - rc = wmi_set_ie(wil, WMI_FRAME_PROBE_REQ, request->ie_len, request->ie); + rc = wmi_set_ie(vif, WMI_FRAME_PROBE_REQ, + request->ie_len, request->ie); if (rc) return rc; return wmi_start_sched_scan(wil, request); @@ -1803,8 +2020,12 @@ wil_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev, u64 reqid) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wil6210_vif *vif = ndev_to_vif(dev); int rc; + if (vif->mid != 0) + return -EOPNOTSUPP; + rc = wmi_stop_sched_scan(wil); /* device would return error if it thinks PNO is already stopped. * ignore the return code so user space and driver gets back in-sync @@ -1893,57 +2114,132 @@ static void wil_wiphy_init(struct wiphy *wiphy) #endif } -struct wireless_dev *wil_cfg80211_init(struct device *dev) +int wil_cfg80211_iface_combinations_from_fw( + struct wil6210_priv *wil, const struct wil_fw_record_concurrency *conc) { - int rc = 0; - struct wireless_dev *wdev; + struct wiphy *wiphy = wil_to_wiphy(wil); + u32 total_limits = 0; + u16 n_combos; + const struct wil_fw_concurrency_combo *combo; + const struct wil_fw_concurrency_limit *limit; + struct ieee80211_iface_combination *iface_combinations; + struct ieee80211_iface_limit *iface_limit; + int i, j; + + if (wiphy->iface_combinations) { + wil_dbg_misc(wil, "iface_combinations already set, skipping\n"); + return 0; + } + + combo = conc->combos; + n_combos = le16_to_cpu(conc->n_combos); + for (i = 0; i < n_combos; i++) { + total_limits += combo->n_limits; + limit = combo->limits + combo->n_limits; + combo = (struct wil_fw_concurrency_combo *)limit; + } + + iface_combinations = + kzalloc(n_combos * sizeof(struct ieee80211_iface_combination) + + total_limits * sizeof(struct ieee80211_iface_limit), + GFP_KERNEL); + if (!iface_combinations) + return -ENOMEM; + iface_limit = (struct ieee80211_iface_limit *)(iface_combinations + + n_combos); + combo = conc->combos; + for (i = 0; i < n_combos; i++) { + iface_combinations[i].max_interfaces = combo->max_interfaces; + iface_combinations[i].num_different_channels = + combo->n_diff_channels; + iface_combinations[i].beacon_int_infra_match = + combo->same_bi; + iface_combinations[i].n_limits = combo->n_limits; + wil_dbg_misc(wil, + "iface_combination %d: max_if %d, num_ch %d, bi_match %d\n", + i, iface_combinations[i].max_interfaces, + iface_combinations[i].num_different_channels, + iface_combinations[i].beacon_int_infra_match); + limit = combo->limits; + for (j = 0; j < combo->n_limits; j++) { + iface_limit[j].max = le16_to_cpu(limit[j].max); + iface_limit[j].types = le16_to_cpu(limit[j].types); + wil_dbg_misc(wil, + "limit %d: max %d types 0x%x\n", j, + iface_limit[j].max, iface_limit[j].types); + } + iface_combinations[i].limits = iface_limit; + iface_limit += combo->n_limits; + limit += combo->n_limits; + combo = (struct wil_fw_concurrency_combo *)limit; + } + + wil_dbg_misc(wil, "multiple VIFs supported, n_mids %d\n", conc->n_mids); + wil->max_vifs = conc->n_mids + 1; /* including main interface */ + if (wil->max_vifs > WIL_MAX_VIFS) { + wil_info(wil, "limited number of VIFs supported(%d, FW %d)\n", + WIL_MAX_VIFS, wil->max_vifs); + wil->max_vifs = WIL_MAX_VIFS; + } + wiphy->n_iface_combinations = n_combos; + wiphy->iface_combinations = iface_combinations; + return 0; +} + +struct wil6210_priv *wil_cfg80211_init(struct device *dev) +{ + struct wiphy *wiphy; + struct wil6210_priv *wil; + struct ieee80211_channel *ch; dev_dbg(dev, "%s()\n", __func__); - wdev = kzalloc(sizeof(*wdev), GFP_KERNEL); - if (!wdev) + /* Note: the wireless_dev structure is no longer allocated here. + * Instead, it is allocated as part of the net_device structure + * for main interface and each VIF. + */ + wiphy = wiphy_new(&wil_cfg80211_ops, sizeof(struct wil6210_priv)); + if (!wiphy) return ERR_PTR(-ENOMEM); - wdev->wiphy = wiphy_new(&wil_cfg80211_ops, - sizeof(struct wil6210_priv)); - if (!wdev->wiphy) { - rc = -ENOMEM; - goto out; - } + set_wiphy_dev(wiphy, dev); + wil_wiphy_init(wiphy); - set_wiphy_dev(wdev->wiphy, dev); - wil_wiphy_init(wdev->wiphy); + wil = wiphy_to_wil(wiphy); + wil->wiphy = wiphy; - return wdev; + /* default monitor channel */ + ch = wiphy->bands[NL80211_BAND_60GHZ]->channels; + cfg80211_chandef_create(&wil->monitor_chandef, ch, NL80211_CHAN_NO_HT); -out: - kfree(wdev); - - return ERR_PTR(rc); + return wil; } -void wil_wdev_free(struct wil6210_priv *wil) +void wil_cfg80211_deinit(struct wil6210_priv *wil) { - struct wireless_dev *wdev = wil_to_wdev(wil); + struct wiphy *wiphy = wil_to_wiphy(wil); dev_dbg(wil_to_dev(wil), "%s()\n", __func__); - if (!wdev) + if (!wiphy) return; - wiphy_free(wdev->wiphy); - kfree(wdev); + kfree(wiphy->iface_combinations); + wiphy->iface_combinations = NULL; + + wiphy_free(wiphy); + /* do not access wil6210_priv after returning from here */ } void wil_p2p_wdev_free(struct wil6210_priv *wil) { struct wireless_dev *p2p_wdev; - mutex_lock(&wil->p2p_wdev_mutex); + mutex_lock(&wil->vif_mutex); p2p_wdev = wil->p2p_wdev; wil->p2p_wdev = NULL; - wil->radio_wdev = wil_to_wdev(wil); - mutex_unlock(&wil->p2p_wdev_mutex); + wil->radio_wdev = wil->main_ndev->ieee80211_ptr; + mutex_unlock(&wil->vif_mutex); if (p2p_wdev) { cfg80211_unregister_wdev(p2p_wdev); kfree(p2p_wdev); @@ -1971,6 +2267,7 @@ static int wil_rf_sector_get_cfg(struct wiphy *wiphy, const void *data, int data_len) { struct wil6210_priv *wil = wdev_to_wil(wdev); + struct wil6210_vif *vif = wdev_to_vif(wil, wdev); int rc; struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1]; u16 sector_index; @@ -2027,8 +2324,8 @@ static int wil_rf_sector_get_cfg(struct wiphy *wiphy, cmd.sector_type = sector_type; cmd.rf_modules_vec = rf_modules_vec & 0xFF; memset(&reply, 0, sizeof(reply)); - rc = wmi_call(wil, WMI_GET_RF_SECTOR_PARAMS_CMDID, &cmd, sizeof(cmd), - WMI_GET_RF_SECTOR_PARAMS_DONE_EVENTID, + rc = wmi_call(wil, WMI_GET_RF_SECTOR_PARAMS_CMDID, vif->mid, + &cmd, sizeof(cmd), WMI_GET_RF_SECTOR_PARAMS_DONE_EVENTID, &reply, sizeof(reply), 500); if (rc) @@ -2090,6 +2387,7 @@ static int wil_rf_sector_set_cfg(struct wiphy *wiphy, const void *data, int data_len) { struct wil6210_priv *wil = wdev_to_wil(wdev); + struct wil6210_vif *vif = wdev_to_vif(wil, wdev); int rc, tmp; struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1]; struct nlattr *tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MAX + 1]; @@ -2184,8 +2482,8 @@ static int wil_rf_sector_set_cfg(struct wiphy *wiphy, cmd.rf_modules_vec = rf_modules_vec & 0xFF; memset(&reply, 0, sizeof(reply)); - rc = wmi_call(wil, WMI_SET_RF_SECTOR_PARAMS_CMDID, &cmd, sizeof(cmd), - WMI_SET_RF_SECTOR_PARAMS_DONE_EVENTID, + rc = wmi_call(wil, WMI_SET_RF_SECTOR_PARAMS_CMDID, vif->mid, + &cmd, sizeof(cmd), WMI_SET_RF_SECTOR_PARAMS_DONE_EVENTID, &reply, sizeof(reply), 500); if (rc) @@ -2198,6 +2496,7 @@ static int wil_rf_sector_get_selected(struct wiphy *wiphy, const void *data, int data_len) { struct wil6210_priv *wil = wdev_to_wil(wdev); + struct wil6210_vif *vif = wdev_to_vif(wil, wdev); int rc; struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1]; u8 sector_type, mac_addr[ETH_ALEN]; @@ -2231,13 +2530,13 @@ static int wil_rf_sector_get_selected(struct wiphy *wiphy, if (tb[QCA_ATTR_MAC_ADDR]) { ether_addr_copy(mac_addr, nla_data(tb[QCA_ATTR_MAC_ADDR])); - cid = wil_find_cid(wil, mac_addr); + cid = wil_find_cid(wil, vif->mid, mac_addr); if (cid < 0) { wil_err(wil, "invalid MAC address %pM\n", mac_addr); return -ENOENT; } } else { - if (test_bit(wil_status_fwconnected, wil->status)) { + if (test_bit(wil_vif_fwconnected, vif->status)) { wil_err(wil, "must specify MAC address when connected\n"); return -EINVAL; } @@ -2247,7 +2546,7 @@ static int wil_rf_sector_get_selected(struct wiphy *wiphy, cmd.cid = (u8)cid; cmd.sector_type = sector_type; memset(&reply, 0, sizeof(reply)); - rc = wmi_call(wil, WMI_GET_SELECTED_RF_SECTOR_INDEX_CMDID, + rc = wmi_call(wil, WMI_GET_SELECTED_RF_SECTOR_INDEX_CMDID, vif->mid, &cmd, sizeof(cmd), WMI_GET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID, &reply, sizeof(reply), @@ -2280,7 +2579,7 @@ nla_put_failure: } static int wil_rf_sector_wmi_set_selected(struct wil6210_priv *wil, - u16 sector_index, + u8 mid, u16 sector_index, u8 sector_type, u8 cid) { struct wmi_set_selected_rf_sector_index_cmd cmd; @@ -2295,7 +2594,7 @@ static int wil_rf_sector_wmi_set_selected(struct wil6210_priv *wil, cmd.sector_type = sector_type; cmd.cid = (u8)cid; memset(&reply, 0, sizeof(reply)); - rc = wmi_call(wil, WMI_SET_SELECTED_RF_SECTOR_INDEX_CMDID, + rc = wmi_call(wil, WMI_SET_SELECTED_RF_SECTOR_INDEX_CMDID, mid, &cmd, sizeof(cmd), WMI_SET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID, &reply, sizeof(reply), @@ -2310,6 +2609,7 @@ static int wil_rf_sector_set_selected(struct wiphy *wiphy, const void *data, int data_len) { struct wil6210_priv *wil = wdev_to_wil(wdev); + struct wil6210_vif *vif = wdev_to_vif(wil, wdev); int rc; struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1]; u16 sector_index; @@ -2349,7 +2649,7 @@ static int wil_rf_sector_set_selected(struct wiphy *wiphy, if (tb[QCA_ATTR_MAC_ADDR]) { ether_addr_copy(mac_addr, nla_data(tb[QCA_ATTR_MAC_ADDR])); if (!is_broadcast_ether_addr(mac_addr)) { - cid = wil_find_cid(wil, mac_addr); + cid = wil_find_cid(wil, vif->mid, mac_addr); if (cid < 0) { wil_err(wil, "invalid MAC address %pM\n", mac_addr); @@ -2363,7 +2663,7 @@ static int wil_rf_sector_set_selected(struct wiphy *wiphy, cid = -1; } } else { - if (test_bit(wil_status_fwconnected, wil->status)) { + if (test_bit(wil_vif_fwconnected, vif->status)) { wil_err(wil, "must specify MAC address when connected\n"); return -EINVAL; } @@ -2371,17 +2671,20 @@ static int wil_rf_sector_set_selected(struct wiphy *wiphy, } if (cid >= 0) { - rc = wil_rf_sector_wmi_set_selected(wil, sector_index, + rc = wil_rf_sector_wmi_set_selected(wil, vif->mid, sector_index, sector_type, cid); } else { /* unlock all cids */ rc = wil_rf_sector_wmi_set_selected( - wil, WMI_INVALID_RF_SECTOR_INDEX, sector_type, - WIL_CID_ALL); + wil, vif->mid, WMI_INVALID_RF_SECTOR_INDEX, + sector_type, WIL_CID_ALL); if (rc == -EINVAL) { for (i = 0; i < WIL6210_MAX_CID; i++) { + if (wil->sta[i].mid != vif->mid) + continue; rc = wil_rf_sector_wmi_set_selected( - wil, WMI_INVALID_RF_SECTOR_INDEX, + wil, vif->mid, + WMI_INVALID_RF_SECTOR_INDEX, sector_type, i); /* the FW will silently ignore and return * success for unused cid, so abort the loop diff --git a/drivers/net/wireless/ath/wil6210/debug.c b/drivers/net/wireless/ath/wil6210/debug.c index 217a4591bde4..a9befb971cc4 100644 --- a/drivers/net/wireless/ath/wil6210/debug.c +++ b/drivers/net/wireless/ath/wil6210/debug.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2013,2016 Qualcomm Atheros, Inc. + * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -25,7 +26,7 @@ void __wil_err(struct wil6210_priv *wil, const char *fmt, ...) va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; - netdev_err(wil_to_ndev(wil), "%pV", &vaf); + netdev_err(wil->main_ndev, "%pV", &vaf); trace_wil6210_log_err(&vaf); va_end(args); } @@ -41,7 +42,7 @@ void __wil_err_ratelimited(struct wil6210_priv *wil, const char *fmt, ...) va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; - netdev_err(wil_to_ndev(wil), "%pV", &vaf); + netdev_err(wil->main_ndev, "%pV", &vaf); trace_wil6210_log_err(&vaf); va_end(args); } @@ -57,7 +58,7 @@ void wil_dbg_ratelimited(const struct wil6210_priv *wil, const char *fmt, ...) va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; - netdev_dbg(wil_to_ndev(wil), "%pV", &vaf); + netdev_dbg(wil->main_ndev, "%pV", &vaf); trace_wil6210_log_dbg(&vaf); va_end(args); } @@ -70,7 +71,7 @@ void __wil_info(struct wil6210_priv *wil, const char *fmt, ...) va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; - netdev_info(wil_to_ndev(wil), "%pV", &vaf); + netdev_info(wil->main_ndev, "%pV", &vaf); trace_wil6210_log_info(&vaf); va_end(args); } diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 4a4888246e8c..8c90b3111f0b 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2012-2017 Qualcomm Atheros, Inc. + * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -621,7 +622,7 @@ static ssize_t wil_write_file_reset(struct file *file, const char __user *buf, size_t len, loff_t *ppos) { struct wil6210_priv *wil = file->private_data; - struct net_device *ndev = wil_to_ndev(wil); + struct net_device *ndev = wil->main_ndev; /** * BUG: @@ -716,27 +717,44 @@ static ssize_t wil_write_back(struct file *file, const char __user *buf, if (rc < 2) return -EINVAL; - if (0 == strcmp(cmd, "add")) { - if (rc < 3) { - wil_err(wil, "BACK: add require at least 2 params\n"); + if ((strcmp(cmd, "add") == 0) || + (strcmp(cmd, "del_tx") == 0)) { + struct vring_tx_data *txdata; + + if (p1 < 0 || p1 >= WIL6210_MAX_TX_RINGS) { + wil_err(wil, "BACK: invalid ring id %d\n", p1); return -EINVAL; } - if (rc < 4) - p3 = 0; - wmi_addba(wil, p1, p2, p3); - } else if (0 == strcmp(cmd, "del_tx")) { - if (rc < 3) - p2 = WLAN_REASON_QSTA_LEAVE_QBSS; - wmi_delba_tx(wil, p1, p2); - } else if (0 == strcmp(cmd, "del_rx")) { + txdata = &wil->vring_tx_data[p1]; + if (strcmp(cmd, "add") == 0) { + if (rc < 3) { + wil_err(wil, "BACK: add require at least 2 params\n"); + return -EINVAL; + } + if (rc < 4) + p3 = 0; + wmi_addba(wil, txdata->mid, p1, p2, p3); + } else { + if (rc < 3) + p2 = WLAN_REASON_QSTA_LEAVE_QBSS; + wmi_delba_tx(wil, txdata->mid, p1, p2); + } + } else if (strcmp(cmd, "del_rx") == 0) { + struct wil_sta_info *sta; + if (rc < 3) { wil_err(wil, "BACK: del_rx require at least 2 params\n"); return -EINVAL; } + if (p1 < 0 || p1 >= WIL6210_MAX_CID) { + wil_err(wil, "BACK: invalid CID %d\n", p1); + return -EINVAL; + } if (rc < 4) p3 = WLAN_REASON_QSTA_LEAVE_QBSS; - wmi_delba_rx(wil, mk_cidxtid(p1, p2), p3); + sta = &wil->sta[p1]; + wmi_delba_rx(wil, sta->mid, mk_cidxtid(p1, p2), p3); } else { wil_err(wil, "BACK: Unrecognized command \"%s\"\n", cmd); return -EINVAL; @@ -855,7 +873,7 @@ static ssize_t wil_write_file_txmgmt(struct file *file, const char __user *buf, { struct wil6210_priv *wil = file->private_data; struct wiphy *wiphy = wil_to_wiphy(wil); - struct wireless_dev *wdev = wil_to_wdev(wil); + struct wireless_dev *wdev = wil->main_ndev->ieee80211_ptr; struct cfg80211_mgmt_tx_params params; int rc; void *frame; @@ -890,6 +908,7 @@ static ssize_t wil_write_file_wmi(struct file *file, const char __user *buf, size_t len, loff_t *ppos) { struct wil6210_priv *wil = file->private_data; + struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); struct wmi_cmd_hdr *wmi; void *cmd; int cmdlen = len - sizeof(struct wmi_cmd_hdr); @@ -912,7 +931,7 @@ static ssize_t wil_write_file_wmi(struct file *file, const char __user *buf, cmd = (cmdlen > 0) ? &wmi[1] : NULL; cmdid = le16_to_cpu(wmi->command_id); - rc1 = wmi_send(wil, cmdid, cmd, cmdlen); + rc1 = wmi_send(wil, cmdid, vif->mid, cmd, cmdlen); kfree(wmi); wil_info(wil, "0x%04x[%d] -> %d\n", cmdid, cmdlen, rc1); @@ -1050,6 +1069,7 @@ static int wil_bf_debugfs_show(struct seq_file *s, void *data) int rc; int i; struct wil6210_priv *wil = s->private; + struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); struct wmi_notify_req_cmd cmd = { .interval_usec = 0, }; @@ -1062,7 +1082,8 @@ static int wil_bf_debugfs_show(struct seq_file *s, void *data) u32 status; cmd.cid = i; - rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, &cmd, sizeof(cmd), + rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, vif->mid, + &cmd, sizeof(cmd), WMI_NOTIFY_REQ_DONE_EVENTID, &reply, sizeof(reply), 20); /* if reply is all-0, ignore this CID */ @@ -1155,7 +1176,7 @@ static const struct file_operations fops_temp = { static int wil_freq_debugfs_show(struct seq_file *s, void *data) { struct wil6210_priv *wil = s->private; - struct wireless_dev *wdev = wil_to_wdev(wil); + struct wireless_dev *wdev = wil->main_ndev->ieee80211_ptr; u16 freq = wdev->chandef.chan ? wdev->chandef.chan->center_freq : 0; seq_printf(s, "Freq = %d\n", freq); @@ -1185,6 +1206,8 @@ static int wil_link_debugfs_show(struct seq_file *s, void *data) for (i = 0; i < ARRAY_SIZE(wil->sta); i++) { struct wil_sta_info *p = &wil->sta[i]; char *status = "unknown"; + struct wil6210_vif *vif; + u8 mid; switch (p->status) { case wil_sta_unused: @@ -1197,16 +1220,24 @@ static int wil_link_debugfs_show(struct seq_file *s, void *data) status = "connected"; break; } - seq_printf(s, "[%d] %pM %s\n", i, p->addr, status); + mid = (p->status != wil_sta_unused) ? p->mid : U8_MAX; + seq_printf(s, "[%d][MID %d] %pM %s\n", + i, mid, p->addr, status); - if (p->status == wil_sta_connected) { - rc = wil_cid_fill_sinfo(wil, i, &sinfo); + if (p->status != wil_sta_connected) + continue; + + vif = (mid < wil->max_vifs) ? wil->vifs[mid] : NULL; + if (vif) { + rc = wil_cid_fill_sinfo(vif, i, &sinfo); if (rc) return rc; seq_printf(s, " Tx_mcs = %d\n", sinfo.txrate.mcs); seq_printf(s, " Rx_mcs = %d\n", sinfo.rxrate.mcs); seq_printf(s, " SQ = %d\n", sinfo.signal); + } else { + seq_puts(s, " INVALID MID\n"); } } @@ -1229,7 +1260,7 @@ static const struct file_operations fops_link = { static int wil_info_debugfs_show(struct seq_file *s, void *data) { struct wil6210_priv *wil = s->private; - struct net_device *ndev = wil_to_ndev(wil); + struct net_device *ndev = wil->main_ndev; int is_ac = power_supply_is_system_supplied(); int rx = atomic_xchg(&wil->isr_count_rx, 0); int tx = atomic_xchg(&wil->isr_count_tx, 0); @@ -1398,6 +1429,7 @@ __acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock) struct wil_sta_info *p = &wil->sta[i]; char *status = "unknown"; u8 aid = 0; + u8 mid; switch (p->status) { case wil_sta_unused: @@ -1411,7 +1443,9 @@ __acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock) aid = p->aid; break; } - seq_printf(s, "[%d] %pM %s AID %d\n", i, p->addr, status, aid); + mid = (p->status != wil_sta_unused) ? p->mid : U8_MAX; + seq_printf(s, "[%d] %pM %s MID %d AID %d\n", i, p->addr, status, + mid, aid); if (p->status == wil_sta_connected) { spin_lock_bh(&p->tid_rx_lock); @@ -1461,6 +1495,42 @@ static const struct file_operations fops_sta = { .llseek = seq_lseek, }; +static int wil_mids_debugfs_show(struct seq_file *s, void *data) +{ + struct wil6210_priv *wil = s->private; + struct wil6210_vif *vif; + struct net_device *ndev; + int i; + + mutex_lock(&wil->vif_mutex); + for (i = 0; i < wil->max_vifs; i++) { + vif = wil->vifs[i]; + + if (vif) { + ndev = vif_to_ndev(vif); + seq_printf(s, "[%d] %pM %s\n", i, ndev->dev_addr, + ndev->name); + } else { + seq_printf(s, "[%d] unused\n", i); + } + } + mutex_unlock(&wil->vif_mutex); + + return 0; +} + +static int wil_mids_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, wil_mids_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_mids = { + .open = wil_mids_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; + static ssize_t wil_read_file_led_cfg(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -1715,6 +1785,7 @@ static const struct { {"mbox", 0444, &fops_mbox}, {"vrings", 0444, &fops_vring}, {"stations", 0444, &fops_sta}, + {"mids", 0444, &fops_mids}, {"desc", 0444, &fops_txdesc}, {"bf", 0444, &fops_bf}, {"mem_val", 0644, &fops_memread}, @@ -1773,11 +1844,9 @@ static void wil6210_debugfs_init_isr(struct wil6210_priv *wil, /* fields in struct wil6210_priv */ static const struct dbg_off dbg_wil_off[] = { - WIL_FIELD(privacy, 0444, doff_u32), WIL_FIELD(status[0], 0644, doff_ulong), WIL_FIELD(hw_version, 0444, doff_x32), WIL_FIELD(recovery_count, 0444, doff_u32), - WIL_FIELD(ap_isolate, 0444, doff_u32), WIL_FIELD(discovery_mode, 0644, doff_u8), WIL_FIELD(chip_revision, 0444, doff_u8), WIL_FIELD(abft_len, 0644, doff_u8), diff --git a/drivers/net/wireless/ath/wil6210/ethtool.c b/drivers/net/wireless/ath/wil6210/ethtool.c index 66200f616a37..e7ff41e623d2 100644 --- a/drivers/net/wireless/ath/wil6210/ethtool.c +++ b/drivers/net/wireless/ath/wil6210/ethtool.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2014,2017 Qualcomm Atheros, Inc. + * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -74,12 +75,13 @@ static int wil_ethtoolops_set_coalesce(struct net_device *ndev, struct ethtool_coalesce *cp) { struct wil6210_priv *wil = ndev_to_wil(ndev); + struct wireless_dev *wdev = ndev->ieee80211_ptr; int ret; wil_dbg_misc(wil, "ethtoolops_set_coalesce: rx %d usec, tx %d usec\n", cp->rx_coalesce_usecs, cp->tx_coalesce_usecs); - if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) { + if (wdev->iftype == NL80211_IFTYPE_MONITOR) { wil_dbg_misc(wil, "No IRQ coalescing in monitor mode\n"); return -EINVAL; } diff --git a/drivers/net/wireless/ath/wil6210/fw.h b/drivers/net/wireless/ath/wil6210/fw.h index 2c7b24f61587..3e7a28045cab 100644 --- a/drivers/net/wireless/ath/wil6210/fw.h +++ b/drivers/net/wireless/ath/wil6210/fw.h @@ -14,6 +14,8 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#ifndef __WIL_FW_H__ +#define __WIL_FW_H__ #define WIL_FW_SIGNATURE (0x36323130) /* '0126' */ #define WIL_FW_FMT_VERSION (1) /* format version driver supports */ @@ -71,7 +73,39 @@ struct wil_fw_record_capabilities { /* type == wil_fw_type_comment */ struct wil_fw_record_comment_hdr hdr; /* capabilities (variable size), see enum wmi_fw_capability */ u8 capabilities[0]; -}; +} __packed; + +/* FW VIF concurrency encoded inside a comment record + * Format is similar to wiphy->iface_combinations + */ +#define WIL_FW_CONCURRENCY_MAGIC (0xfedccdef) +#define WIL_FW_CONCURRENCY_REC_VER 1 +struct wil_fw_concurrency_limit { + __le16 max; /* maximum number of interfaces of these types */ + __le16 types; /* interface types (bit mask of enum nl80211_iftype) */ +} __packed; + +struct wil_fw_concurrency_combo { + u8 n_limits; /* number of wil_fw_concurrency_limit entries */ + u8 max_interfaces; /* max number of concurrent interfaces allowed */ + u8 n_diff_channels; /* total number of different channels allowed */ + u8 same_bi; /* for APs, 1 if all APs must have same BI */ + /* keep last - concurrency limits, variable size by n_limits */ + struct wil_fw_concurrency_limit limits[0]; +} __packed; + +struct wil_fw_record_concurrency { /* type == wil_fw_type_comment */ + /* identifies concurrency record */ + __le32 magic; + /* structure version, currently always 1 */ + u8 version; + /* maximum number of supported MIDs _in addition_ to MID 0 */ + u8 n_mids; + /* number of concurrency combinations that follow */ + __le16 n_combos; + /* keep last - combinations, variable size by n_combos */ + struct wil_fw_concurrency_combo combos[0]; +} __packed; /* brd file info encoded inside a comment record */ #define WIL_BRD_FILE_MAGIC (0xabcddcbb) @@ -175,3 +209,5 @@ struct wil_fw_record_gateway_data4 { /* type == wil_fw_type_gateway_data4 */ __le32 command; struct wil_fw_data_gw4 data[0]; /* total size [data_size], see above */ } __packed; + +#endif /* __WIL_FW_H__ */ diff --git a/drivers/net/wireless/ath/wil6210/fw_inc.c b/drivers/net/wireless/ath/wil6210/fw_inc.c index 914c0106e94b..718161b829c2 100644 --- a/drivers/net/wireless/ath/wil6210/fw_inc.c +++ b/drivers/net/wireless/ath/wil6210/fw_inc.c @@ -136,8 +136,8 @@ fw_handle_capabilities(struct wil6210_priv *wil, const void *data, size_t capa_size; if (size < sizeof(*rec)) { - wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1, - data, size, true); + wil_err_fw(wil, "capabilities record too short: %zu\n", size); + /* let the FW load anyway */ return 0; } @@ -158,8 +158,7 @@ fw_handle_brd_file(struct wil6210_priv *wil, const void *data, const struct wil_fw_record_brd_file *rec = data; if (size < sizeof(*rec)) { - wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1, - data, size, true); + wil_err_fw(wil, "brd_file record too short: %zu\n", size); return 0; } @@ -172,6 +171,44 @@ fw_handle_brd_file(struct wil6210_priv *wil, const void *data, return 0; } +static int +fw_handle_concurrency(struct wil6210_priv *wil, const void *data, + size_t size) +{ + const struct wil_fw_record_concurrency *rec = data; + const struct wil_fw_concurrency_combo *combo; + const struct wil_fw_concurrency_limit *limit; + size_t remain, lsize; + int i, n_combos; + + if (size < sizeof(*rec)) { + wil_err_fw(wil, "concurrency record too short: %zu\n", size); + /* continue, let the FW load anyway */ + return 0; + } + + n_combos = le16_to_cpu(rec->n_combos); + remain = size - offsetof(struct wil_fw_record_concurrency, combos); + combo = rec->combos; + for (i = 0; i < n_combos; i++) { + if (remain < sizeof(*combo)) + goto out_short; + remain -= sizeof(*combo); + limit = combo->limits; + lsize = combo->n_limits * sizeof(*limit); + if (remain < lsize) + goto out_short; + remain -= lsize; + limit += combo->n_limits; + combo = (struct wil_fw_concurrency_combo *)limit; + } + + return wil_cfg80211_iface_combinations_from_fw(wil, rec); +out_short: + wil_err_fw(wil, "concurrency record truncated\n"); + return 0; +} + static int fw_handle_comment(struct wil6210_priv *wil, const void *data, size_t size) @@ -194,6 +231,13 @@ fw_handle_comment(struct wil6210_priv *wil, const void *data, wil_dbg_fw(wil, "magic is WIL_BRD_FILE_MAGIC\n"); rc = fw_handle_brd_file(wil, data, size); break; + case WIL_FW_CONCURRENCY_MAGIC: + wil_dbg_fw(wil, "magic is WIL_FW_CONCURRENCY_MAGIC\n"); + rc = fw_handle_concurrency(wil, data, size); + break; + default: + wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1, + data, size, true); } return rc; diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c index 1835187ea075..84e9840c1752 100644 --- a/drivers/net/wireless/ath/wil6210/interrupt.c +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -127,7 +127,7 @@ void wil6210_unmask_irq_tx(struct wil6210_priv *wil) void wil6210_unmask_irq_rx(struct wil6210_priv *wil) { - bool unmask_rx_htrsh = test_bit(wil_status_fwconnected, wil->status); + bool unmask_rx_htrsh = atomic_read(&wil->connected_vifs) > 0; wil_w(wil, RGF_DMA_EP_RX_ICR + offsetof(struct RGF_ICR, IMC), unmask_rx_htrsh ? WIL6210_IMC_RX : WIL6210_IMC_RX_NO_RX_HTRSH); @@ -188,12 +188,14 @@ void wil_unmask_irq(struct wil6210_priv *wil) void wil_configure_interrupt_moderation(struct wil6210_priv *wil) { + struct wireless_dev *wdev = wil->main_ndev->ieee80211_ptr; + wil_dbg_irq(wil, "configure_interrupt_moderation\n"); /* disable interrupt moderation for monitor * to get better timestamp precision */ - if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) + if (wdev->iftype == NL80211_IFTYPE_MONITOR) return; /* Disable and clear tx counter before (re)configuration */ @@ -340,7 +342,7 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie) static void wil_notify_fw_error(struct wil6210_priv *wil) { - struct device *dev = &wil_to_ndev(wil)->dev; + struct device *dev = &wil->main_ndev->dev; char *envp[3] = { [0] = "SOURCE=wil6210", [1] = "EVENT=FW_ERROR", diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 0c61a6c13991..a4b413e8d55a 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -160,24 +160,34 @@ void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, } } -static void wil_disconnect_cid(struct wil6210_priv *wil, int cid, +static void wil_disconnect_cid(struct wil6210_vif *vif, int cid, u16 reason_code, bool from_event) __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) { uint i; - struct net_device *ndev = wil_to_ndev(wil); - struct wireless_dev *wdev = wil->wdev; + struct wil6210_priv *wil = vif_to_wil(vif); + struct net_device *ndev = vif_to_ndev(vif); + struct wireless_dev *wdev = vif_to_wdev(vif); struct wil_sta_info *sta = &wil->sta[cid]; might_sleep(); - wil_dbg_misc(wil, "disconnect_cid: CID %d, status %d\n", - cid, sta->status); + wil_dbg_misc(wil, "disconnect_cid: CID %d, MID %d, status %d\n", + cid, sta->mid, sta->status); /* inform upper/lower layers */ if (sta->status != wil_sta_unused) { + if (vif->mid != sta->mid) { + wil_err(wil, "STA MID mismatch with VIF MID(%d)\n", + vif->mid); + /* let FW override sta->mid but be more strict with + * user space requests + */ + if (!from_event) + return; + } if (!from_event) { bool del_sta = (wdev->iftype == NL80211_IFTYPE_AP) ? disable_ap_sme : false; - wmi_disconnect_sta(wil, sta->addr, reason_code, + wmi_disconnect_sta(vif, sta->addr, reason_code, true, del_sta); } @@ -191,6 +201,7 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) break; } sta->status = wil_sta_unused; + sta->mid = U8_MAX; } /* reorder buffers */ for (i = 0; i < WIL_STA_TID_NUM; i++) { @@ -216,28 +227,33 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) memset(&sta->stats, 0, sizeof(sta->stats)); } -static bool wil_is_connected(struct wil6210_priv *wil) +static bool wil_vif_is_connected(struct wil6210_priv *wil, u8 mid) { int i; for (i = 0; i < ARRAY_SIZE(wil->sta); i++) { - if (wil->sta[i].status == wil_sta_connected) + if (wil->sta[i].mid == mid && + wil->sta[i].status == wil_sta_connected) return true; } return false; } -static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid, +static void _wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid, u16 reason_code, bool from_event) { + struct wil6210_priv *wil = vif_to_wil(vif); int cid = -ENOENT; - struct net_device *ndev = wil_to_ndev(wil); - struct wireless_dev *wdev = wil->wdev; + struct net_device *ndev; + struct wireless_dev *wdev; - if (unlikely(!ndev)) + if (unlikely(!vif)) return; + ndev = vif_to_ndev(vif); + wdev = vif_to_wdev(vif); + might_sleep(); wil_info(wil, "bssid=%pM, reason=%d, ev%s\n", bssid, reason_code, from_event ? "+" : "-"); @@ -254,48 +270,51 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid, */ if (bssid && !is_broadcast_ether_addr(bssid) && !ether_addr_equal_unaligned(ndev->dev_addr, bssid)) { - cid = wil_find_cid(wil, bssid); + cid = wil_find_cid(wil, vif->mid, bssid); wil_dbg_misc(wil, "Disconnect %pM, CID=%d, reason=%d\n", bssid, cid, reason_code); if (cid >= 0) /* disconnect 1 peer */ - wil_disconnect_cid(wil, cid, reason_code, from_event); + wil_disconnect_cid(vif, cid, reason_code, from_event); } else { /* all */ wil_dbg_misc(wil, "Disconnect all\n"); for (cid = 0; cid < WIL6210_MAX_CID; cid++) - wil_disconnect_cid(wil, cid, reason_code, from_event); + wil_disconnect_cid(vif, cid, reason_code, from_event); } /* link state */ switch (wdev->iftype) { case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: - wil_bcast_fini(wil); - wil_update_net_queues_bh(wil, NULL, true); + wil_bcast_fini(vif); + wil_update_net_queues_bh(wil, vif, NULL, true); netif_carrier_off(ndev); - wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS); + if (!wil_has_other_active_ifaces(wil, ndev, false, true)) + wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS); - if (test_bit(wil_status_fwconnected, wil->status)) { - clear_bit(wil_status_fwconnected, wil->status); + if (test_and_clear_bit(wil_vif_fwconnected, vif->status)) { + atomic_dec(&wil->connected_vifs); cfg80211_disconnected(ndev, reason_code, NULL, 0, - wil->locally_generated_disc, + vif->locally_generated_disc, GFP_KERNEL); - wil->locally_generated_disc = false; - } else if (test_bit(wil_status_fwconnecting, wil->status)) { + vif->locally_generated_disc = false; + } else if (test_bit(wil_vif_fwconnecting, vif->status)) { cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, GFP_KERNEL); - wil->bss = NULL; + vif->bss = NULL; } - clear_bit(wil_status_fwconnecting, wil->status); + clear_bit(wil_vif_fwconnecting, vif->status); break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: - if (!wil_is_connected(wil)) { - wil_update_net_queues_bh(wil, NULL, true); - clear_bit(wil_status_fwconnected, wil->status); + if (!wil_vif_is_connected(wil, vif->mid)) { + wil_update_net_queues_bh(wil, vif, NULL, true); + if (test_and_clear_bit(wil_vif_fwconnected, + vif->status)) + atomic_dec(&wil->connected_vifs); } else { - wil_update_net_queues_bh(wil, NULL, false); + wil_update_net_queues_bh(wil, vif, NULL, false); } break; default: @@ -303,26 +322,27 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid, } } -static void wil_disconnect_worker(struct work_struct *work) +void wil_disconnect_worker(struct work_struct *work) { - struct wil6210_priv *wil = container_of(work, - struct wil6210_priv, disconnect_worker); - struct net_device *ndev = wil_to_ndev(wil); + struct wil6210_vif *vif = container_of(work, + struct wil6210_vif, disconnect_worker); + struct wil6210_priv *wil = vif_to_wil(vif); + struct net_device *ndev = vif_to_ndev(vif); int rc; struct { struct wmi_cmd_hdr wmi; struct wmi_disconnect_event evt; } __packed reply; - if (test_bit(wil_status_fwconnected, wil->status)) + if (test_bit(wil_vif_fwconnected, vif->status)) /* connect succeeded after all */ return; - if (!test_bit(wil_status_fwconnecting, wil->status)) + if (!test_bit(wil_vif_fwconnecting, vif->status)) /* already disconnected */ return; - rc = wmi_call(wil, WMI_DISCONNECT_CMDID, NULL, 0, + rc = wmi_call(wil, WMI_DISCONNECT_CMDID, vif->mid, NULL, 0, WMI_DISCONNECT_EVENTID, &reply, sizeof(reply), WIL6210_DISCONNECT_TO_MS); if (rc) { @@ -330,35 +350,11 @@ static void wil_disconnect_worker(struct work_struct *work) return; } - wil_update_net_queues_bh(wil, NULL, true); + wil_update_net_queues_bh(wil, vif, NULL, true); netif_carrier_off(ndev); cfg80211_connect_result(ndev, NULL, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, GFP_KERNEL); - clear_bit(wil_status_fwconnecting, wil->status); -} - -static void wil_connect_timer_fn(struct timer_list *t) -{ - struct wil6210_priv *wil = from_timer(wil, t, connect_timer); - bool q; - - wil_err(wil, "Connect timeout detected, disconnect station\n"); - - /* reschedule to thread context - disconnect won't - * run from atomic context. - * queue on wmi_wq to prevent race with connect event. - */ - q = queue_work(wil->wmi_wq, &wil->disconnect_worker); - wil_dbg_wmi(wil, "queue_work of disconnect_worker -> %d\n", q); -} - -static void wil_scan_timer_fn(struct timer_list *t) -{ - struct wil6210_priv *wil = from_timer(wil, t, scan_timer); - - clear_bit(wil_status_fwready, wil->status); - wil_err(wil, "Scan timeout detected, start fw error recovery\n"); - wil_fw_error_recovery(wil); + clear_bit(wil_vif_fwconnecting, vif->status); } static int wil_wait_for_recovery(struct wil6210_priv *wil) @@ -394,12 +390,12 @@ static void wil_fw_error_worker(struct work_struct *work) { struct wil6210_priv *wil = container_of(work, struct wil6210_priv, fw_error_worker); - struct wireless_dev *wdev = wil->wdev; - struct net_device *ndev = wil_to_ndev(wil); + struct net_device *ndev = wil->main_ndev; + struct wireless_dev *wdev = ndev->ieee80211_ptr; wil_dbg_misc(wil, "fw error worker\n"); - if (!(ndev->flags & IFF_UP)) { + if (!ndev || !(ndev->flags & IFF_UP)) { wil_info(wil, "No recovery - interface is down\n"); return; } @@ -429,6 +425,10 @@ static void wil_fw_error_worker(struct work_struct *work) return; mutex_lock(&wil->mutex); + /* Needs adaptation for multiple VIFs + * need to go over all VIFs and consider the appropriate + * recovery. + */ switch (wdev->iftype) { case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: @@ -461,8 +461,9 @@ static int wil_find_free_vring(struct wil6210_priv *wil) return -EINVAL; } -int wil_tx_init(struct wil6210_priv *wil, int cid) +int wil_tx_init(struct wil6210_vif *vif, int cid) { + struct wil6210_priv *wil = vif_to_wil(vif); int rc = -EINVAL, ringid; if (cid < 0) { @@ -475,21 +476,22 @@ int wil_tx_init(struct wil6210_priv *wil, int cid) goto out; } - wil_dbg_wmi(wil, "Configure for connection CID %d vring %d\n", - cid, ringid); + wil_dbg_wmi(wil, "Configure for connection CID %d MID %d vring %d\n", + cid, vif->mid, ringid); - rc = wil_vring_init_tx(wil, ringid, 1 << tx_ring_order, cid, 0); + rc = wil_vring_init_tx(vif, ringid, 1 << tx_ring_order, cid, 0); if (rc) - wil_err(wil, "wil_vring_init_tx for CID %d vring %d failed\n", - cid, ringid); + wil_err(wil, "init TX for CID %d MID %d vring %d failed\n", + cid, vif->mid, ringid); out: return rc; } -int wil_bcast_init(struct wil6210_priv *wil) +int wil_bcast_init(struct wil6210_vif *vif) { - int ri = wil->bcast_vring, rc; + struct wil6210_priv *wil = vif_to_wil(vif); + int ri = vif->bcast_vring, rc; if ((ri >= 0) && wil->vring_tx[ri].va) return 0; @@ -498,25 +500,38 @@ int wil_bcast_init(struct wil6210_priv *wil) if (ri < 0) return ri; - wil->bcast_vring = ri; - rc = wil_vring_init_bcast(wil, ri, 1 << bcast_ring_order); + vif->bcast_vring = ri; + rc = wil_vring_init_bcast(vif, ri, 1 << bcast_ring_order); if (rc) - wil->bcast_vring = -1; + vif->bcast_vring = -1; return rc; } -void wil_bcast_fini(struct wil6210_priv *wil) +void wil_bcast_fini(struct wil6210_vif *vif) { - int ri = wil->bcast_vring; + struct wil6210_priv *wil = vif_to_wil(vif); + int ri = vif->bcast_vring; if (ri < 0) return; - wil->bcast_vring = -1; + vif->bcast_vring = -1; wil_vring_fini_tx(wil, ri); } +void wil_bcast_fini_all(struct wil6210_priv *wil) +{ + int i; + struct wil6210_vif *vif; + + for (i = 0; i < wil->max_vifs; i++) { + vif = wil->vifs[i]; + if (vif) + wil_bcast_fini(vif); + } +} + int wil_priv_init(struct wil6210_priv *wil) { uint i; @@ -524,38 +539,29 @@ int wil_priv_init(struct wil6210_priv *wil) wil_dbg_misc(wil, "priv_init\n"); memset(wil->sta, 0, sizeof(wil->sta)); - for (i = 0; i < WIL6210_MAX_CID; i++) + for (i = 0; i < WIL6210_MAX_CID; i++) { spin_lock_init(&wil->sta[i].tid_rx_lock); + wil->sta[i].mid = U8_MAX; + } for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) spin_lock_init(&wil->vring_tx_data[i].lock); mutex_init(&wil->mutex); + mutex_init(&wil->vif_mutex); mutex_init(&wil->wmi_mutex); - mutex_init(&wil->probe_client_mutex); - mutex_init(&wil->p2p_wdev_mutex); mutex_init(&wil->halp.lock); init_completion(&wil->wmi_ready); init_completion(&wil->wmi_call); init_completion(&wil->halp.comp); - wil->bcast_vring = -1; - timer_setup(&wil->connect_timer, wil_connect_timer_fn, 0); - timer_setup(&wil->scan_timer, wil_scan_timer_fn, 0); - timer_setup(&wil->p2p.discovery_timer, wil_p2p_discovery_timer_fn, 0); - - INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker); INIT_WORK(&wil->wmi_event_worker, wmi_event_worker); INIT_WORK(&wil->fw_error_worker, wil_fw_error_worker); - INIT_WORK(&wil->probe_client_worker, wil_probe_client_worker); - INIT_WORK(&wil->p2p.delayed_listen_work, wil_p2p_delayed_listen_work); INIT_LIST_HEAD(&wil->pending_wmi_ev); - INIT_LIST_HEAD(&wil->probe_client_pending); spin_lock_init(&wil->wmi_ev_lock); spin_lock_init(&wil->net_queue_lock); - wil->net_queue_stopped = 1; init_waitqueue_head(&wil->wq); wil->wmi_wq = create_singlethread_workqueue(WIL_NAME "_wmi"); @@ -582,6 +588,9 @@ int wil_priv_init(struct wil6210_priv *wil) memset(&wil->suspend_stats, 0, sizeof(wil->suspend_stats)); wil->vring_idle_trsh = 16; + wil->reply_mid = U8_MAX; + wil->max_vifs = 1; + return 0; out_wmi_wq: @@ -600,7 +609,7 @@ void wil6210_bus_request(struct wil6210_priv *wil, u32 kbps) /** * wil6210_disconnect - disconnect one connection - * @wil: driver context + * @vif: virtual interface context * @bssid: peer to disconnect, NULL to disconnect all * @reason_code: Reason code for the Disassociation frame * @from_event: whether is invoked from FW event handler @@ -608,13 +617,15 @@ void wil6210_bus_request(struct wil6210_priv *wil, u32 kbps) * Disconnect and release associated resources. If invoked not from the * FW event handler, issue WMI command(s) to trigger MAC disconnect. */ -void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid, +void wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid, u16 reason_code, bool from_event) { + struct wil6210_priv *wil = vif_to_wil(vif); + wil_dbg_misc(wil, "disconnect\n"); - del_timer_sync(&wil->connect_timer); - _wil6210_disconnect(wil, bssid, reason_code, from_event); + del_timer_sync(&vif->connect_timer); + _wil6210_disconnect(vif, bssid, reason_code, from_event); } void wil_priv_deinit(struct wil6210_priv *wil) @@ -622,18 +633,8 @@ void wil_priv_deinit(struct wil6210_priv *wil) wil_dbg_misc(wil, "priv_deinit\n"); wil_set_recovery_state(wil, fw_recovery_idle); - del_timer_sync(&wil->scan_timer); - del_timer_sync(&wil->p2p.discovery_timer); - cancel_work_sync(&wil->disconnect_worker); cancel_work_sync(&wil->fw_error_worker); - cancel_work_sync(&wil->p2p.discovery_expired_work); - cancel_work_sync(&wil->p2p.delayed_listen_work); - mutex_lock(&wil->mutex); - wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false); - mutex_unlock(&wil->mutex); wmi_event_flush(wil); - wil_probe_client_flush(wil); - cancel_work_sync(&wil->probe_client_worker); destroy_workqueue(wil->wq_service); destroy_workqueue(wil->wmi_wq); } @@ -715,7 +716,7 @@ static void wil_bl_prepare_halt(struct wil6210_priv *wil) offsetof(struct bl_dedicated_registers_v0, boot_loader_struct_version)); if (!tmp) { - wil_dbg_misc(wil, "old BL, skipping halt preperation\n"); + wil_dbg_misc(wil, "old BL, skipping halt preparation\n"); return; } @@ -943,7 +944,7 @@ void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r) static int wil_get_bl_info(struct wil6210_priv *wil) { - struct net_device *ndev = wil_to_ndev(wil); + struct net_device *ndev = wil->main_ndev; struct wiphy *wiphy = wil_to_wiphy(wil); union { struct bl_dedicated_registers_v0 bl0; @@ -1035,7 +1036,7 @@ static void wil_bl_crash_info(struct wil6210_priv *wil, bool is_err) static int wil_get_otp_info(struct wil6210_priv *wil) { - struct net_device *ndev = wil_to_ndev(wil); + struct net_device *ndev = wil->main_ndev; struct wiphy *wiphy = wil_to_wiphy(wil); u8 mac[8]; @@ -1069,31 +1070,46 @@ static int wil_wait_for_fw_ready(struct wil6210_priv *wil) return 0; } -void wil_abort_scan(struct wil6210_priv *wil, bool sync) +void wil_abort_scan(struct wil6210_vif *vif, bool sync) { + struct wil6210_priv *wil = vif_to_wil(vif); int rc; struct cfg80211_scan_info info = { .aborted = true, }; - lockdep_assert_held(&wil->p2p_wdev_mutex); + lockdep_assert_held(&wil->vif_mutex); - if (!wil->scan_request) + if (!vif->scan_request) return; - wil_dbg_misc(wil, "Abort scan_request 0x%p\n", wil->scan_request); - del_timer_sync(&wil->scan_timer); - mutex_unlock(&wil->p2p_wdev_mutex); - rc = wmi_abort_scan(wil); + wil_dbg_misc(wil, "Abort scan_request 0x%p\n", vif->scan_request); + del_timer_sync(&vif->scan_timer); + mutex_unlock(&wil->vif_mutex); + rc = wmi_abort_scan(vif); if (!rc && sync) - wait_event_interruptible_timeout(wil->wq, !wil->scan_request, + wait_event_interruptible_timeout(wil->wq, !vif->scan_request, msecs_to_jiffies( WAIT_FOR_SCAN_ABORT_MS)); - mutex_lock(&wil->p2p_wdev_mutex); - if (wil->scan_request) { - cfg80211_scan_done(wil->scan_request, &info); - wil->scan_request = NULL; + mutex_lock(&wil->vif_mutex); + if (vif->scan_request) { + cfg80211_scan_done(vif->scan_request, &info); + vif->scan_request = NULL; + } +} + +void wil_abort_scan_all_vifs(struct wil6210_priv *wil, bool sync) +{ + int i; + + lockdep_assert_held(&wil->vif_mutex); + + for (i = 0; i < wil->max_vifs; i++) { + struct wil6210_vif *vif = wil->vifs[i]; + + if (vif) + wil_abort_scan(vif, sync); } } @@ -1138,6 +1154,34 @@ static void wil_pre_fw_config(struct wil6210_priv *wil) } } +static int wil_restore_vifs(struct wil6210_priv *wil) +{ + struct wil6210_vif *vif; + struct net_device *ndev; + struct wireless_dev *wdev; + int i, rc; + + for (i = 0; i < wil->max_vifs; i++) { + vif = wil->vifs[i]; + if (!vif) + continue; + vif->ap_isolate = 0; + if (vif->mid) { + ndev = vif_to_ndev(vif); + wdev = vif_to_wdev(vif); + rc = wmi_port_allocate(wil, vif->mid, ndev->dev_addr, + wdev->iftype); + if (rc) { + wil_err(wil, "fail to restore VIF %d type %d, rc %d\n", + i, wdev->iftype, rc); + return rc; + } + } + } + + return 0; +} + /* * We reset all the structures, and we reset the UMAC. * After calling this routine, you're expected to reload @@ -1145,9 +1189,10 @@ static void wil_pre_fw_config(struct wil6210_priv *wil) */ int wil_reset(struct wil6210_priv *wil, bool load_fw) { - int rc; + int rc, i; unsigned long status_flags = BIT(wil_status_resetting); int no_flash; + struct wil6210_vif *vif; wil_dbg_misc(wil, "reset\n"); @@ -1158,7 +1203,7 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) static const u8 mac[ETH_ALEN] = { 0x00, 0xde, 0xad, 0x12, 0x34, 0x56, }; - struct net_device *ndev = wil_to_ndev(wil); + struct net_device *ndev = wil->main_ndev; ether_addr_copy(ndev->perm_addr, mac); ether_addr_copy(ndev->dev_addr, ndev->perm_addr); @@ -1196,17 +1241,23 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) goto out; } - cancel_work_sync(&wil->disconnect_worker); - wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false); - wil_bcast_fini(wil); + mutex_lock(&wil->vif_mutex); + wil_abort_scan_all_vifs(wil, false); + mutex_unlock(&wil->vif_mutex); + + for (i = 0; i < wil->max_vifs; i++) { + vif = wil->vifs[i]; + if (vif) { + cancel_work_sync(&vif->disconnect_worker); + wil6210_disconnect(vif, NULL, + WLAN_REASON_DEAUTH_LEAVING, false); + } + } + wil_bcast_fini_all(wil); /* Disable device led before reset*/ wmi_led_cfg(wil, false); - mutex_lock(&wil->p2p_wdev_mutex); - wil_abort_scan(wil, false); - mutex_unlock(&wil->p2p_wdev_mutex); - /* prevent NAPI from being scheduled and prevent wmi commands */ mutex_lock(&wil->wmi_mutex); if (test_bit(wil_status_suspending, wil->status)) @@ -1276,7 +1327,6 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) } /* init after reset */ - wil->ap_isolate = 0; reinit_completion(&wil->wmi_ready); reinit_completion(&wil->wmi_call); reinit_completion(&wil->halp.comp); @@ -1299,6 +1349,12 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) return rc; } + rc = wil_restore_vifs(wil); + if (rc) { + wil_err(wil, "failed to restore vifs, rc %d\n", rc); + return rc; + } + wil_collect_fw_info(wil); if (wil->ps_profile != WMI_PS_PROFILE_TYPE_DEFAULT) @@ -1337,8 +1393,8 @@ void wil_fw_error_recovery(struct wil6210_priv *wil) int __wil_up(struct wil6210_priv *wil) { - struct net_device *ndev = wil_to_ndev(wil); - struct wireless_dev *wdev = wil->wdev; + struct net_device *ndev = wil->main_ndev; + struct wireless_dev *wdev = ndev->ieee80211_ptr; int rc; WARN_ON(!mutex_is_locked(&wil->mutex)); @@ -1420,10 +1476,10 @@ int __wil_down(struct wil6210_priv *wil) } wil_enable_irq(wil); - mutex_lock(&wil->p2p_wdev_mutex); + mutex_lock(&wil->vif_mutex); wil_p2p_stop_radio_operations(wil); - wil_abort_scan(wil, false); - mutex_unlock(&wil->p2p_wdev_mutex); + wil_abort_scan_all_vifs(wil, false); + mutex_unlock(&wil->vif_mutex); return wil_reset(wil, false); } @@ -1442,13 +1498,14 @@ int wil_down(struct wil6210_priv *wil) return rc; } -int wil_find_cid(struct wil6210_priv *wil, const u8 *mac) +int wil_find_cid(struct wil6210_priv *wil, u8 mid, const u8 *mac) { int i; int rc = -ENOENT; for (i = 0; i < ARRAY_SIZE(wil->sta); i++) { - if ((wil->sta[i].status != wil_sta_unused) && + if (wil->sta[i].mid == mid && + wil->sta[i].status != wil_sta_unused && ether_addr_equal(wil->sta[i].addr, mac)) { rc = i; break; diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c index 7ba4e0af8f57..05e9408e7ea3 100644 --- a/drivers/net/wireless/ath/wil6210/netdev.c +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2012-2017 Qualcomm Atheros, Inc. + * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -15,13 +16,41 @@ */ #include +#include #include "wil6210.h" #include "txrx.h" +bool wil_has_other_active_ifaces(struct wil6210_priv *wil, + struct net_device *ndev, bool up, bool ok) +{ + int i; + struct wil6210_vif *vif; + struct net_device *ndev_i; + + for (i = 0; i < wil->max_vifs; i++) { + vif = wil->vifs[i]; + if (vif) { + ndev_i = vif_to_ndev(vif); + if (ndev_i != ndev) + if ((up && (ndev_i->flags & IFF_UP)) || + (ok && netif_carrier_ok(ndev_i))) + return true; + } + } + + return false; +} + +bool wil_has_active_ifaces(struct wil6210_priv *wil, bool up, bool ok) +{ + /* use NULL ndev argument to check all interfaces */ + return wil_has_other_active_ifaces(wil, NULL, up, ok); +} + static int wil_open(struct net_device *ndev) { struct wil6210_priv *wil = ndev_to_wil(ndev); - int rc; + int rc = 0; wil_dbg_misc(wil, "open\n"); @@ -31,13 +60,16 @@ static int wil_open(struct net_device *ndev) return -EINVAL; } - rc = wil_pm_runtime_get(wil); - if (rc < 0) - return rc; + if (!wil_has_other_active_ifaces(wil, ndev, true, false)) { + wil_dbg_misc(wil, "open, first iface\n"); + rc = wil_pm_runtime_get(wil); + if (rc < 0) + return rc; - rc = wil_up(wil); - if (rc) - wil_pm_runtime_put(wil); + rc = wil_up(wil); + if (rc) + wil_pm_runtime_put(wil); + } return rc; } @@ -45,13 +77,16 @@ static int wil_open(struct net_device *ndev) static int wil_stop(struct net_device *ndev) { struct wil6210_priv *wil = ndev_to_wil(ndev); - int rc; + int rc = 0; wil_dbg_misc(wil, "stop\n"); - rc = wil_down(wil); - if (!rc) - wil_pm_runtime_put(wil); + if (!wil_has_other_active_ifaces(wil, ndev, true, false)) { + wil_dbg_misc(wil, "stop, last iface\n"); + rc = wil_down(wil); + if (!rc) + wil_pm_runtime_put(wil); + } return rc; } @@ -96,11 +131,19 @@ static int wil6210_netdev_poll_tx(struct napi_struct *napi, int budget) for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { struct vring *vring = &wil->vring_tx[i]; struct vring_tx_data *txdata = &wil->vring_tx_data[i]; + struct wil6210_vif *vif; - if (!vring->va || !txdata->enabled) + if (!vring->va || !txdata->enabled || + txdata->mid >= wil->max_vifs) continue; - tx_done += wil_tx_complete(wil, i); + vif = wil->vifs[txdata->mid]; + if (unlikely(!vif)) { + wil_dbg_txrx(wil, "Invalid MID %d\n", txdata->mid); + continue; + } + + tx_done += wil_tx_complete(vif, i); } if (tx_done < budget) { @@ -121,43 +164,136 @@ static void wil_dev_setup(struct net_device *dev) dev->tx_queue_len = WIL_TX_Q_LEN_DEFAULT; } -void *wil_if_alloc(struct device *dev) +static void wil_vif_deinit(struct wil6210_vif *vif) +{ + del_timer_sync(&vif->scan_timer); + del_timer_sync(&vif->p2p.discovery_timer); + cancel_work_sync(&vif->disconnect_worker); + cancel_work_sync(&vif->p2p.discovery_expired_work); + cancel_work_sync(&vif->p2p.delayed_listen_work); + wil_probe_client_flush(vif); + cancel_work_sync(&vif->probe_client_worker); +} + +void wil_vif_free(struct wil6210_vif *vif) +{ + struct net_device *ndev = vif_to_ndev(vif); + + wil_vif_deinit(vif); + free_netdev(ndev); +} + +static void wil_ndev_destructor(struct net_device *ndev) +{ + struct wil6210_vif *vif = ndev_to_vif(ndev); + + wil_vif_deinit(vif); +} + +static void wil_connect_timer_fn(struct timer_list *t) +{ + struct wil6210_vif *vif = from_timer(vif, t, connect_timer); + struct wil6210_priv *wil = vif_to_wil(vif); + bool q; + + wil_err(wil, "Connect timeout detected, disconnect station\n"); + + /* reschedule to thread context - disconnect won't + * run from atomic context. + * queue on wmi_wq to prevent race with connect event. + */ + q = queue_work(wil->wmi_wq, &vif->disconnect_worker); + wil_dbg_wmi(wil, "queue_work of disconnect_worker -> %d\n", q); +} + +static void wil_scan_timer_fn(struct timer_list *t) +{ + struct wil6210_vif *vif = from_timer(vif, t, scan_timer); + struct wil6210_priv *wil = vif_to_wil(vif); + + clear_bit(wil_status_fwready, wil->status); + wil_err(wil, "Scan timeout detected, start fw error recovery\n"); + wil_fw_error_recovery(wil); +} + +static void wil_p2p_discovery_timer_fn(struct timer_list *t) +{ + struct wil6210_vif *vif = from_timer(vif, t, p2p.discovery_timer); + struct wil6210_priv *wil = vif_to_wil(vif); + + wil_dbg_misc(wil, "p2p_discovery_timer_fn\n"); + + schedule_work(&vif->p2p.discovery_expired_work); +} + +static void wil_vif_init(struct wil6210_vif *vif) +{ + vif->bcast_vring = -1; + + mutex_init(&vif->probe_client_mutex); + + timer_setup(&vif->connect_timer, wil_connect_timer_fn, 0); + timer_setup(&vif->scan_timer, wil_scan_timer_fn, 0); + timer_setup(&vif->p2p.discovery_timer, wil_p2p_discovery_timer_fn, 0); + + INIT_WORK(&vif->probe_client_worker, wil_probe_client_worker); + INIT_WORK(&vif->disconnect_worker, wil_disconnect_worker); + INIT_WORK(&vif->p2p.delayed_listen_work, wil_p2p_delayed_listen_work); + + INIT_LIST_HEAD(&vif->probe_client_pending); + + vif->net_queue_stopped = 1; +} + +static u8 wil_vif_find_free_mid(struct wil6210_priv *wil) +{ + u8 i; + + for (i = 0; i < wil->max_vifs; i++) { + if (!wil->vifs[i]) + return i; + } + + return U8_MAX; +} + +struct wil6210_vif * +wil_vif_alloc(struct wil6210_priv *wil, const char *name, + unsigned char name_assign_type, enum nl80211_iftype iftype) { struct net_device *ndev; struct wireless_dev *wdev; - struct wil6210_priv *wil; - struct ieee80211_channel *ch; - int rc = 0; + struct wil6210_vif *vif; + u8 mid; - wdev = wil_cfg80211_init(dev); - if (IS_ERR(wdev)) { - dev_err(dev, "wil_cfg80211_init failed\n"); - return wdev; + mid = wil_vif_find_free_mid(wil); + if (mid == U8_MAX) { + wil_err(wil, "no available virtual interface\n"); + return ERR_PTR(-EINVAL); } - wil = wdev_to_wil(wdev); - wil->wdev = wdev; - wil->radio_wdev = wdev; - - wil_dbg_misc(wil, "if_alloc\n"); - - rc = wil_priv_init(wil); - if (rc) { - dev_err(dev, "wil_priv_init failed\n"); - goto out_wdev; - } - - wdev->iftype = NL80211_IFTYPE_STATION; /* TODO */ - /* default monitor channel */ - ch = wdev->wiphy->bands[NL80211_BAND_60GHZ]->channels; - cfg80211_chandef_create(&wil->monitor_chandef, ch, NL80211_CHAN_NO_HT); - - ndev = alloc_netdev(0, "wlan%d", NET_NAME_UNKNOWN, wil_dev_setup); + ndev = alloc_netdev(sizeof(*vif), name, name_assign_type, + wil_dev_setup); if (!ndev) { - dev_err(dev, "alloc_netdev_mqs failed\n"); - rc = -ENOMEM; - goto out_priv; + dev_err(wil_to_dev(wil), "alloc_netdev failed\n"); + return ERR_PTR(-ENOMEM); } + if (mid == 0) { + wil->main_ndev = ndev; + } else { + ndev->priv_destructor = wil_ndev_destructor; + ndev->needs_free_netdev = true; + } + + vif = ndev_to_vif(ndev); + vif->ndev = ndev; + vif->wil = wil; + vif->mid = mid; + wil_vif_init(vif); + + wdev = &vif->wdev; + wdev->wiphy = wil->wiphy; + wdev->iftype = iftype; ndev->netdev_ops = &wil_netdev_ops; wil_set_ethtoolops(ndev); @@ -170,21 +306,53 @@ void *wil_if_alloc(struct device *dev) ndev->features |= ndev->hw_features; SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy)); wdev->netdev = ndev; + return vif; +} + +void *wil_if_alloc(struct device *dev) +{ + struct wil6210_priv *wil; + struct wil6210_vif *vif; + int rc = 0; + + wil = wil_cfg80211_init(dev); + if (IS_ERR(wil)) { + dev_err(dev, "wil_cfg80211_init failed\n"); + return wil; + } + + rc = wil_priv_init(wil); + if (rc) { + dev_err(dev, "wil_priv_init failed\n"); + goto out_cfg; + } + + wil_dbg_misc(wil, "if_alloc\n"); + + vif = wil_vif_alloc(wil, "wlan%d", NET_NAME_UNKNOWN, + NL80211_IFTYPE_STATION); + if (IS_ERR(vif)) { + dev_err(dev, "wil_vif_alloc failed\n"); + rc = -ENOMEM; + goto out_priv; + } + + wil->radio_wdev = vif_to_wdev(vif); return wil; - out_priv: +out_priv: wil_priv_deinit(wil); - out_wdev: - wil_wdev_free(wil); +out_cfg: + wil_cfg80211_deinit(wil); return ERR_PTR(rc); } void wil_if_free(struct wil6210_priv *wil) { - struct net_device *ndev = wil_to_ndev(wil); + struct net_device *ndev = wil->main_ndev; wil_dbg_misc(wil, "if_free\n"); @@ -193,17 +361,50 @@ void wil_if_free(struct wil6210_priv *wil) wil_priv_deinit(wil); - wil_to_ndev(wil) = NULL; + wil->main_ndev = NULL; + wil_ndev_destructor(ndev); free_netdev(ndev); - wil_wdev_free(wil); + wil_cfg80211_deinit(wil); +} + +int wil_vif_add(struct wil6210_priv *wil, struct wil6210_vif *vif) +{ + struct net_device *ndev = vif_to_ndev(vif); + struct wireless_dev *wdev = vif_to_wdev(vif); + bool any_active = wil_has_active_ifaces(wil, true, false); + int rc; + + ASSERT_RTNL(); + + if (wil->vifs[vif->mid]) { + dev_err(&ndev->dev, "VIF with mid %d already in use\n", + vif->mid); + return -EEXIST; + } + if (any_active && vif->mid != 0) { + rc = wmi_port_allocate(wil, vif->mid, ndev->dev_addr, + wdev->iftype); + if (rc) + return rc; + } + rc = register_netdevice(ndev); + if (rc < 0) { + dev_err(&ndev->dev, "Failed to register netdev: %d\n", rc); + if (any_active && vif->mid != 0) + wmi_port_delete(wil, vif->mid); + return rc; + } + + wil->vifs[vif->mid] = vif; + return 0; } int wil_if_add(struct wil6210_priv *wil) { - struct wireless_dev *wdev = wil_to_wdev(wil); - struct wiphy *wiphy = wdev->wiphy; - struct net_device *ndev = wil_to_ndev(wil); + struct wiphy *wiphy = wil->wiphy; + struct net_device *ndev = wil->main_ndev; + struct wil6210_vif *vif = ndev_to_vif(ndev); int rc; wil_dbg_misc(wil, "entered"); @@ -216,33 +417,94 @@ int wil_if_add(struct wil6210_priv *wil) return rc; } - netif_napi_add(ndev, &wil->napi_rx, wil6210_netdev_poll_rx, + init_dummy_netdev(&wil->napi_ndev); + netif_napi_add(&wil->napi_ndev, &wil->napi_rx, wil6210_netdev_poll_rx, WIL6210_NAPI_BUDGET); - netif_tx_napi_add(ndev, &wil->napi_tx, wil6210_netdev_poll_tx, + netif_tx_napi_add(&wil->napi_ndev, + &wil->napi_tx, wil6210_netdev_poll_tx, WIL6210_NAPI_BUDGET); - wil_update_net_queues_bh(wil, NULL, true); + wil_update_net_queues_bh(wil, vif, NULL, true); - rc = register_netdev(ndev); - if (rc < 0) { - dev_err(&ndev->dev, "Failed to register netdev: %d\n", rc); + rtnl_lock(); + rc = wil_vif_add(wil, vif); + rtnl_unlock(); + if (rc < 0) goto out_wiphy; - } return 0; out_wiphy: - wiphy_unregister(wdev->wiphy); + wiphy_unregister(wiphy); return rc; } +void wil_vif_remove(struct wil6210_priv *wil, u8 mid) +{ + struct wil6210_vif *vif; + struct net_device *ndev; + bool any_active = wil_has_active_ifaces(wil, true, false); + + ASSERT_RTNL(); + if (mid >= wil->max_vifs) { + wil_err(wil, "invalid MID: %d\n", mid); + return; + } + + vif = wil->vifs[mid]; + if (!vif) { + wil_err(wil, "MID %d not registered\n", mid); + return; + } + + ndev = vif_to_ndev(vif); + /* during unregister_netdevice cfg80211_leave may perform operations + * such as stop AP, disconnect, so we only clear the VIF afterwards + */ + unregister_netdevice(ndev); + + mutex_lock(&wil->mutex); + wil6210_disconnect(vif, NULL, WLAN_REASON_DEAUTH_LEAVING, false); + mutex_unlock(&wil->mutex); + + if (any_active && vif->mid != 0) + wmi_port_delete(wil, vif->mid); + + /* make sure no one is accessing the VIF before removing */ + mutex_lock(&wil->vif_mutex); + wil->vifs[mid] = NULL; + /* ensure NAPI code will see the NULL VIF */ + wmb(); + if (test_bit(wil_status_napi_en, wil->status)) { + napi_synchronize(&wil->napi_rx); + napi_synchronize(&wil->napi_tx); + } + mutex_unlock(&wil->vif_mutex); + + flush_work(&wil->wmi_event_worker); + del_timer_sync(&vif->connect_timer); + cancel_work_sync(&vif->disconnect_worker); + wil_probe_client_flush(vif); + cancel_work_sync(&vif->probe_client_worker); + /* for VIFs, ndev will be freed by destructor after RTNL is unlocked. + * the main interface will be freed in wil_if_free, we need to keep it + * a bit longer so logging macros will work. + */ +} + void wil_if_remove(struct wil6210_priv *wil) { - struct net_device *ndev = wil_to_ndev(wil); - struct wireless_dev *wdev = wil_to_wdev(wil); + struct net_device *ndev = wil->main_ndev; + struct wireless_dev *wdev = ndev->ieee80211_ptr; wil_dbg_misc(wil, "if_remove\n"); - unregister_netdev(ndev); + rtnl_lock(); + wil_vif_remove(wil, 0); + rtnl_unlock(); + + netif_napi_del(&wil->napi_tx); + netif_napi_del(&wil->napi_rx); + wiphy_unregister(wdev->wiphy); } diff --git a/drivers/net/wireless/ath/wil6210/p2p.c b/drivers/net/wireless/ath/wil6210/p2p.c index 7dbee2c3e482..db087ea58ddf 100644 --- a/drivers/net/wireless/ath/wil6210/p2p.c +++ b/drivers/net/wireless/ath/wil6210/p2p.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2014-2017 Qualcomm Atheros, Inc. + * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -22,27 +23,28 @@ #define P2P_SEARCH_DURATION_MS 500 #define P2P_DEFAULT_BI 100 -static int wil_p2p_start_listen(struct wil6210_priv *wil) +static int wil_p2p_start_listen(struct wil6210_vif *vif) { - struct wil_p2p_info *p2p = &wil->p2p; + struct wil6210_priv *wil = vif_to_wil(vif); + struct wil_p2p_info *p2p = &vif->p2p; u8 channel = p2p->listen_chan.hw_value; int rc; lockdep_assert_held(&wil->mutex); - rc = wmi_p2p_cfg(wil, channel, P2P_DEFAULT_BI); + rc = wmi_p2p_cfg(vif, channel, P2P_DEFAULT_BI); if (rc) { wil_err(wil, "wmi_p2p_cfg failed\n"); goto out; } - rc = wmi_set_ssid(wil, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID); + rc = wmi_set_ssid(vif, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID); if (rc) { wil_err(wil, "wmi_set_ssid failed\n"); goto out_stop; } - rc = wmi_start_listen(wil); + rc = wmi_start_listen(vif); if (rc) { wil_err(wil, "wmi_start_listen failed\n"); goto out_stop; @@ -53,7 +55,7 @@ static int wil_p2p_start_listen(struct wil6210_priv *wil) jiffies + msecs_to_jiffies(p2p->listen_duration)); out_stop: if (rc) - wmi_stop_discovery(wil); + wmi_stop_discovery(vif); out: return rc; @@ -65,20 +67,12 @@ bool wil_p2p_is_social_scan(struct cfg80211_scan_request *request) (request->channels[0]->hw_value == P2P_DMG_SOCIAL_CHANNEL); } -void wil_p2p_discovery_timer_fn(struct timer_list *t) -{ - struct wil6210_priv *wil = from_timer(wil, t, p2p.discovery_timer); - - wil_dbg_misc(wil, "p2p_discovery_timer_fn\n"); - - schedule_work(&wil->p2p.discovery_expired_work); -} - -int wil_p2p_search(struct wil6210_priv *wil, +int wil_p2p_search(struct wil6210_vif *vif, struct cfg80211_scan_request *request) { + struct wil6210_priv *wil = vif_to_wil(vif); int rc; - struct wil_p2p_info *p2p = &wil->p2p; + struct wil_p2p_info *p2p = &vif->p2p; wil_dbg_misc(wil, "p2p_search: channel %d\n", P2P_DMG_SOCIAL_CHANNEL); @@ -90,20 +84,20 @@ int wil_p2p_search(struct wil6210_priv *wil, goto out; } - rc = wmi_p2p_cfg(wil, P2P_DMG_SOCIAL_CHANNEL, P2P_DEFAULT_BI); + rc = wmi_p2p_cfg(vif, P2P_DMG_SOCIAL_CHANNEL, P2P_DEFAULT_BI); if (rc) { wil_err(wil, "wmi_p2p_cfg failed\n"); goto out; } - rc = wmi_set_ssid(wil, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID); + rc = wmi_set_ssid(vif, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID); if (rc) { wil_err(wil, "wmi_set_ssid failed\n"); goto out_stop; } /* Set application IE to probe request and probe response */ - rc = wmi_set_ie(wil, WMI_FRAME_PROBE_REQ, + rc = wmi_set_ie(vif, WMI_FRAME_PROBE_REQ, request->ie_len, request->ie); if (rc) { wil_err(wil, "wmi_set_ie(WMI_FRAME_PROBE_REQ) failed\n"); @@ -113,14 +107,14 @@ int wil_p2p_search(struct wil6210_priv *wil, /* supplicant doesn't provide Probe Response IEs. As a workaround - * re-use Probe Request IEs */ - rc = wmi_set_ie(wil, WMI_FRAME_PROBE_RESP, + rc = wmi_set_ie(vif, WMI_FRAME_PROBE_RESP, request->ie_len, request->ie); if (rc) { wil_err(wil, "wmi_set_ie(WMI_FRAME_PROBE_RESP) failed\n"); goto out_stop; } - rc = wmi_start_search(wil); + rc = wmi_start_search(vif); if (rc) { wil_err(wil, "wmi_start_search failed\n"); goto out_stop; @@ -133,7 +127,7 @@ int wil_p2p_search(struct wil6210_priv *wil, out_stop: if (rc) - wmi_stop_discovery(wil); + wmi_stop_discovery(vif); out: return rc; @@ -143,7 +137,8 @@ int wil_p2p_listen(struct wil6210_priv *wil, struct wireless_dev *wdev, unsigned int duration, struct ieee80211_channel *chan, u64 *cookie) { - struct wil_p2p_info *p2p = &wil->p2p; + struct wil6210_vif *vif = wdev_to_vif(wil, wdev); + struct wil_p2p_info *p2p = &vif->p2p; int rc; if (!chan) @@ -163,23 +158,24 @@ int wil_p2p_listen(struct wil6210_priv *wil, struct wireless_dev *wdev, *cookie = ++p2p->cookie; p2p->listen_duration = duration; - mutex_lock(&wil->p2p_wdev_mutex); - if (wil->scan_request) { + mutex_lock(&wil->vif_mutex); + if (vif->scan_request) { wil_dbg_misc(wil, "Delaying p2p listen until scan done\n"); p2p->pending_listen_wdev = wdev; p2p->discovery_started = 1; rc = 0; - mutex_unlock(&wil->p2p_wdev_mutex); + mutex_unlock(&wil->vif_mutex); goto out; } - mutex_unlock(&wil->p2p_wdev_mutex); + mutex_unlock(&wil->vif_mutex); - rc = wil_p2p_start_listen(wil); + rc = wil_p2p_start_listen(vif); if (rc) goto out; p2p->discovery_started = 1; - wil->radio_wdev = wdev; + if (vif->mid == 0) + wil->radio_wdev = wdev; cfg80211_ready_on_channel(wdev, *cookie, chan, duration, GFP_KERNEL); @@ -189,9 +185,9 @@ out: return rc; } -u8 wil_p2p_stop_discovery(struct wil6210_priv *wil) +u8 wil_p2p_stop_discovery(struct wil6210_vif *vif) { - struct wil_p2p_info *p2p = &wil->p2p; + struct wil_p2p_info *p2p = &vif->p2p; u8 started = p2p->discovery_started; if (p2p->discovery_started) { @@ -200,7 +196,7 @@ u8 wil_p2p_stop_discovery(struct wil6210_priv *wil) p2p->pending_listen_wdev = NULL; } else { del_timer_sync(&p2p->discovery_timer); - wmi_stop_discovery(wil); + wmi_stop_discovery(vif); } p2p->discovery_started = 0; } @@ -208,9 +204,10 @@ u8 wil_p2p_stop_discovery(struct wil6210_priv *wil) return started; } -int wil_p2p_cancel_listen(struct wil6210_priv *wil, u64 cookie) +int wil_p2p_cancel_listen(struct wil6210_vif *vif, u64 cookie) { - struct wil_p2p_info *p2p = &wil->p2p; + struct wil6210_priv *wil = vif_to_wil(vif); + struct wil_p2p_info *p2p = &vif->p2p; u8 started; mutex_lock(&wil->mutex); @@ -222,7 +219,7 @@ int wil_p2p_cancel_listen(struct wil6210_priv *wil, u64 cookie) return -ENOENT; } - started = wil_p2p_stop_discovery(wil); + started = wil_p2p_stop_discovery(vif); mutex_unlock(&wil->mutex); @@ -231,13 +228,14 @@ int wil_p2p_cancel_listen(struct wil6210_priv *wil, u64 cookie) return -ENOENT; } - mutex_lock(&wil->p2p_wdev_mutex); - cfg80211_remain_on_channel_expired(wil->radio_wdev, + mutex_lock(&wil->vif_mutex); + cfg80211_remain_on_channel_expired(vif_to_radio_wdev(wil, vif), p2p->cookie, &p2p->listen_chan, GFP_KERNEL); - wil->radio_wdev = wil->wdev; - mutex_unlock(&wil->p2p_wdev_mutex); + if (vif->mid == 0) + wil->radio_wdev = wil->main_ndev->ieee80211_ptr; + mutex_unlock(&wil->vif_mutex); return 0; } @@ -245,40 +243,43 @@ void wil_p2p_listen_expired(struct work_struct *work) { struct wil_p2p_info *p2p = container_of(work, struct wil_p2p_info, discovery_expired_work); - struct wil6210_priv *wil = container_of(p2p, - struct wil6210_priv, p2p); + struct wil6210_vif *vif = container_of(p2p, + struct wil6210_vif, p2p); + struct wil6210_priv *wil = vif_to_wil(vif); u8 started; wil_dbg_misc(wil, "p2p_listen_expired\n"); mutex_lock(&wil->mutex); - started = wil_p2p_stop_discovery(wil); + started = wil_p2p_stop_discovery(vif); mutex_unlock(&wil->mutex); - if (started) { - mutex_lock(&wil->p2p_wdev_mutex); - cfg80211_remain_on_channel_expired(wil->radio_wdev, - p2p->cookie, - &p2p->listen_chan, - GFP_KERNEL); - wil->radio_wdev = wil->wdev; - mutex_unlock(&wil->p2p_wdev_mutex); - } + if (!started) + return; + mutex_lock(&wil->vif_mutex); + cfg80211_remain_on_channel_expired(vif_to_radio_wdev(wil, vif), + p2p->cookie, + &p2p->listen_chan, + GFP_KERNEL); + if (vif->mid == 0) + wil->radio_wdev = wil->main_ndev->ieee80211_ptr; + mutex_unlock(&wil->vif_mutex); } void wil_p2p_search_expired(struct work_struct *work) { struct wil_p2p_info *p2p = container_of(work, struct wil_p2p_info, discovery_expired_work); - struct wil6210_priv *wil = container_of(p2p, - struct wil6210_priv, p2p); + struct wil6210_vif *vif = container_of(p2p, + struct wil6210_vif, p2p); + struct wil6210_priv *wil = vif_to_wil(vif); u8 started; wil_dbg_misc(wil, "p2p_search_expired\n"); mutex_lock(&wil->mutex); - started = wil_p2p_stop_discovery(wil); + started = wil_p2p_stop_discovery(vif); mutex_unlock(&wil->mutex); if (started) { @@ -286,13 +287,15 @@ void wil_p2p_search_expired(struct work_struct *work) .aborted = false, }; - mutex_lock(&wil->p2p_wdev_mutex); - if (wil->scan_request) { - cfg80211_scan_done(wil->scan_request, &info); - wil->scan_request = NULL; - wil->radio_wdev = wil->wdev; + mutex_lock(&wil->vif_mutex); + if (vif->scan_request) { + cfg80211_scan_done(vif->scan_request, &info); + vif->scan_request = NULL; + if (vif->mid == 0) + wil->radio_wdev = + wil->main_ndev->ieee80211_ptr; } - mutex_unlock(&wil->p2p_wdev_mutex); + mutex_unlock(&wil->vif_mutex); } } @@ -300,8 +303,9 @@ void wil_p2p_delayed_listen_work(struct work_struct *work) { struct wil_p2p_info *p2p = container_of(work, struct wil_p2p_info, delayed_listen_work); - struct wil6210_priv *wil = container_of(p2p, - struct wil6210_priv, p2p); + struct wil6210_vif *vif = container_of(p2p, + struct wil6210_vif, p2p); + struct wil6210_priv *wil = vif_to_wil(vif); int rc; mutex_lock(&wil->mutex); @@ -310,31 +314,33 @@ void wil_p2p_delayed_listen_work(struct work_struct *work) if (!p2p->discovery_started || !p2p->pending_listen_wdev) goto out; - mutex_lock(&wil->p2p_wdev_mutex); - if (wil->scan_request) { + mutex_lock(&wil->vif_mutex); + if (vif->scan_request) { /* another scan started, wait again... */ - mutex_unlock(&wil->p2p_wdev_mutex); + mutex_unlock(&wil->vif_mutex); goto out; } - mutex_unlock(&wil->p2p_wdev_mutex); + mutex_unlock(&wil->vif_mutex); - rc = wil_p2p_start_listen(wil); + rc = wil_p2p_start_listen(vif); - mutex_lock(&wil->p2p_wdev_mutex); + mutex_lock(&wil->vif_mutex); if (rc) { cfg80211_remain_on_channel_expired(p2p->pending_listen_wdev, p2p->cookie, &p2p->listen_chan, GFP_KERNEL); - wil->radio_wdev = wil->wdev; + if (vif->mid == 0) + wil->radio_wdev = wil->main_ndev->ieee80211_ptr; } else { cfg80211_ready_on_channel(p2p->pending_listen_wdev, p2p->cookie, &p2p->listen_chan, p2p->listen_duration, GFP_KERNEL); - wil->radio_wdev = p2p->pending_listen_wdev; + if (vif->mid == 0) + wil->radio_wdev = p2p->pending_listen_wdev; } p2p->pending_listen_wdev = NULL; - mutex_unlock(&wil->p2p_wdev_mutex); + mutex_unlock(&wil->vif_mutex); out: mutex_unlock(&wil->mutex); @@ -342,34 +348,35 @@ out: void wil_p2p_stop_radio_operations(struct wil6210_priv *wil) { - struct wil_p2p_info *p2p = &wil->p2p; + struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); + struct wil_p2p_info *p2p = &vif->p2p; struct cfg80211_scan_info info = { .aborted = true, }; lockdep_assert_held(&wil->mutex); - lockdep_assert_held(&wil->p2p_wdev_mutex); + lockdep_assert_held(&wil->vif_mutex); if (wil->radio_wdev != wil->p2p_wdev) goto out; if (!p2p->discovery_started) { /* Regular scan on the p2p device */ - if (wil->scan_request && - wil->scan_request->wdev == wil->p2p_wdev) - wil_abort_scan(wil, true); + if (vif->scan_request && + vif->scan_request->wdev == wil->p2p_wdev) + wil_abort_scan(vif, true); goto out; } /* Search or listen on p2p device */ - mutex_unlock(&wil->p2p_wdev_mutex); - wil_p2p_stop_discovery(wil); - mutex_lock(&wil->p2p_wdev_mutex); + mutex_unlock(&wil->vif_mutex); + wil_p2p_stop_discovery(vif); + mutex_lock(&wil->vif_mutex); - if (wil->scan_request) { + if (vif->scan_request) { /* search */ - cfg80211_scan_done(wil->scan_request, &info); - wil->scan_request = NULL; + cfg80211_scan_done(vif->scan_request, &info); + vif->scan_request = NULL; } else { /* listen */ cfg80211_remain_on_channel_expired(wil->radio_wdev, @@ -379,5 +386,5 @@ void wil_p2p_stop_radio_operations(struct wil6210_priv *wil) } out: - wil->radio_wdev = wil->wdev; + wil->radio_wdev = wil->main_ndev->ieee80211_ptr; } diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index 809092a49192..19cbc6add637 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -137,6 +137,20 @@ void wil_enable_irq(struct wil6210_priv *wil) enable_irq(wil->pdev->irq); } +static void wil_remove_all_additional_vifs(struct wil6210_priv *wil) +{ + struct wil6210_vif *vif; + int i; + + for (i = 1; i < wil->max_vifs; i++) { + vif = wil->vifs[i]; + if (vif) { + wil_vif_prepare_stop(vif); + wil_vif_remove(wil, vif->mid); + } + } +} + /* Bus ops */ static int wil_if_pcie_enable(struct wil6210_priv *wil) { @@ -148,10 +162,8 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil) */ int msi_only = pdev->msi_enabled; bool _use_msi = use_msi; - bool wmi_only = test_bit(WMI_FW_CAPABILITY_WMI_ONLY, - wil->fw_capabilities); - wil_dbg_misc(wil, "if_pcie_enable, wmi_only %d\n", wmi_only); + wil_dbg_misc(wil, "if_pcie_enable\n"); pci_set_master(pdev); @@ -172,11 +184,9 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil) if (rc) goto stop_master; - /* need reset here to obtain MAC or in case of WMI-only FW, full reset - * and fw loading takes place - */ + /* need reset here to obtain MAC */ mutex_lock(&wil->mutex); - rc = wil_reset(wil, wmi_only); + rc = wil_reset(wil, false); mutex_unlock(&wil->mutex); if (rc) goto release_irq; @@ -356,6 +366,18 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto bus_disable; } + /* in case of WMI-only FW, perform full reset and FW loading */ + if (test_bit(WMI_FW_CAPABILITY_WMI_ONLY, wil->fw_capabilities)) { + wil_dbg_misc(wil, "Loading WMI only FW\n"); + mutex_lock(&wil->mutex); + rc = wil_reset(wil, true); + mutex_unlock(&wil->mutex); + if (rc) { + wil_err(wil, "failed to load WMI only FW\n"); + goto if_remove; + } + } + if (IS_ENABLED(CONFIG_PM)) wil->pm_notify.notifier_call = wil6210_pm_notify; @@ -372,6 +394,8 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) return 0; +if_remove: + wil_if_remove(wil); bus_disable: wil_if_pcie_disable(wil); err_iounmap: @@ -402,6 +426,7 @@ static void wil_pcie_remove(struct pci_dev *pdev) wil6210_debugfs_remove(wil); rtnl_lock(); wil_p2p_wdev_free(wil); + wil_remove_all_additional_vifs(wil); rtnl_unlock(); wil_if_remove(wil); wil_if_pcie_disable(wil); @@ -425,12 +450,15 @@ 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; + bool keep_radio_on, active_ifaces; wil_dbg_pm(wil, "suspend: %s\n", is_runtime ? "runtime" : "system"); + mutex_lock(&wil->vif_mutex); + active_ifaces = wil_has_active_ifaces(wil, true, false); + mutex_unlock(&wil->vif_mutex); + keep_radio_on = active_ifaces && wil->keep_radio_on_during_sleep; + rc = wil_can_suspend(wil, is_runtime); if (rc) goto out; @@ -457,12 +485,15 @@ 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; + bool keep_radio_on, active_ifaces; wil_dbg_pm(wil, "resume: %s\n", is_runtime ? "runtime" : "system"); + mutex_lock(&wil->vif_mutex); + active_ifaces = wil_has_active_ifaces(wil, true, false); + mutex_unlock(&wil->vif_mutex); + keep_radio_on = active_ifaces && wil->keep_radio_on_during_sleep; + /* In case radio stays on, platform device will control * PCIe master */ diff --git a/drivers/net/wireless/ath/wil6210/pm.c b/drivers/net/wireless/ath/wil6210/pm.c index 0a96518a566f..ba81fb3ac96f 100644 --- a/drivers/net/wireless/ath/wil6210/pm.c +++ b/drivers/net/wireless/ath/wil6210/pm.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2014,2017 Qualcomm Atheros, Inc. + * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -20,13 +21,72 @@ #define WIL6210_AUTOSUSPEND_DELAY_MS (1000) +static void wil_pm_wake_connected_net_queues(struct wil6210_priv *wil) +{ + int i; + + mutex_lock(&wil->vif_mutex); + for (i = 0; i < wil->max_vifs; i++) { + struct wil6210_vif *vif = wil->vifs[i]; + + if (vif && test_bit(wil_vif_fwconnected, vif->status)) + wil_update_net_queues_bh(wil, vif, NULL, false); + } + mutex_unlock(&wil->vif_mutex); +} + +static void wil_pm_stop_all_net_queues(struct wil6210_priv *wil) +{ + int i; + + mutex_lock(&wil->vif_mutex); + for (i = 0; i < wil->max_vifs; i++) { + struct wil6210_vif *vif = wil->vifs[i]; + + if (vif) + wil_update_net_queues_bh(wil, vif, NULL, true); + } + mutex_unlock(&wil->vif_mutex); +} + +static bool +wil_can_suspend_vif(struct wil6210_priv *wil, struct wil6210_vif *vif, + bool is_runtime) +{ + struct wireless_dev *wdev = vif_to_wdev(vif); + + switch (wdev->iftype) { + case NL80211_IFTYPE_MONITOR: + wil_dbg_pm(wil, "Sniffer\n"); + return false; + + /* for STA-like interface, don't runtime suspend */ + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + if (test_bit(wil_vif_fwconnecting, vif->status)) { + wil_dbg_pm(wil, "Delay suspend when connecting\n"); + return false; + } + if (is_runtime) { + wil_dbg_pm(wil, "STA-like interface\n"); + return false; + } + break; + /* AP-like interface - can't suspend */ + default: + wil_dbg_pm(wil, "AP-like interface\n"); + return false; + } + + return true; +} + 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); + int rc = 0, i; bool wmi_only = test_bit(WMI_FW_CAPABILITY_WMI_ONLY, wil->fw_capabilities); + bool active_ifaces; wil_dbg_pm(wil, "can_suspend: %s\n", is_runtime ? "runtime" : "system"); @@ -40,7 +100,12 @@ int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime) rc = -EBUSY; goto out; } - if (!(ndev->flags & IFF_UP)) { + + mutex_lock(&wil->vif_mutex); + active_ifaces = wil_has_active_ifaces(wil, true, false); + mutex_unlock(&wil->vif_mutex); + + if (!active_ifaces) { /* can always sleep when down */ wil_dbg_pm(wil, "Interface is down\n"); goto out; @@ -57,32 +122,19 @@ 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)) { - wil_dbg_pm(wil, "Delay suspend when connecting\n"); + mutex_lock(&wil->vif_mutex); + for (i = 0; i < wil->max_vifs; i++) { + struct wil6210_vif *vif = wil->vifs[i]; + + if (!vif) + continue; + if (!wil_can_suspend_vif(wil, vif, is_runtime)) { rc = -EBUSY; + mutex_unlock(&wil->vif_mutex); 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: - wil_dbg_pm(wil, "AP-like interface\n"); - rc = -EBUSY; - break; } + mutex_unlock(&wil->vif_mutex); out: wil_dbg_pm(wil, "can_suspend: %s => %s (%d)\n", @@ -127,8 +179,7 @@ static int wil_resume_keep_radio_on(struct wil6210_priv *wil) } /* Wake all queues */ - if (test_bit(wil_status_fwconnected, wil->status)) - wil_update_net_queues_bh(wil, NULL, false); + wil_pm_wake_connected_net_queues(wil); out: if (rc) @@ -152,7 +203,7 @@ static int wil_suspend_keep_radio_on(struct wil6210_priv *wil) wil->suspend_stats.rejected_by_host++; return -EBUSY; } - wil_update_net_queues_bh(wil, NULL, true); + wil_pm_stop_all_net_queues(wil); if (!wil_is_tx_idle(wil)) { wil_dbg_pm(wil, "Pending TX data, reject suspend\n"); @@ -243,22 +294,20 @@ resume_after_fail: /* if resume succeeded, reject the suspend */ if (!rc) { rc = -EBUSY; - if (test_bit(wil_status_fwconnected, wil->status)) - wil_update_net_queues_bh(wil, NULL, false); + wil_pm_wake_connected_net_queues(wil); } return rc; reject_suspend: clear_bit(wil_status_suspending, wil->status); - if (test_bit(wil_status_fwconnected, wil->status)) - wil_update_net_queues_bh(wil, NULL, false); + wil_pm_wake_connected_net_queues(wil); return -EBUSY; } static int wil_suspend_radio_off(struct wil6210_priv *wil) { int rc = 0; - struct net_device *ndev = wil_to_ndev(wil); + bool active_ifaces; wil_dbg_pm(wil, "suspend radio off\n"); @@ -272,7 +321,11 @@ static int wil_suspend_radio_off(struct wil6210_priv *wil) } /* if netif up, hardware is alive, shut it down */ - if (ndev->flags & IFF_UP) { + mutex_lock(&wil->vif_mutex); + active_ifaces = wil_has_active_ifaces(wil, true, false); + mutex_unlock(&wil->vif_mutex); + + if (active_ifaces) { rc = wil_down(wil); if (rc) { wil_err(wil, "wil_down : %d\n", rc); @@ -306,16 +359,19 @@ out: static int wil_resume_radio_off(struct wil6210_priv *wil) { int rc = 0; - struct net_device *ndev = wil_to_ndev(wil); + bool active_ifaces; wil_dbg_pm(wil, "Enabling PCIe IRQ\n"); wil_enable_irq(wil); - /* if netif up, bring hardware up + /* if any netif up, bring hardware up * During open(), IFF_UP set after actual device method * invocation. This prevent recursive call to wil_up() * wil_status_suspended will be cleared in wil_reset */ - if (ndev->flags & IFF_UP) + mutex_lock(&wil->vif_mutex); + active_ifaces = wil_has_active_ifaces(wil, true, false); + mutex_unlock(&wil->vif_mutex); + if (active_ifaces) rc = wil_up(wil); else clear_bit(wil_status_suspended, wil->status); diff --git a/drivers/net/wireless/ath/wil6210/pmc.c b/drivers/net/wireless/ath/wil6210/pmc.c index 4ea27b0bd278..c49f7988369e 100644 --- a/drivers/net/wireless/ath/wil6210/pmc.c +++ b/drivers/net/wireless/ath/wil6210/pmc.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2012-2015,2017 Qualcomm Atheros, Inc. + * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -53,6 +54,7 @@ void wil_pmc_alloc(struct wil6210_priv *wil, u32 i; struct pmc_ctx *pmc = &wil->pmc; struct device *dev = wil_to_dev(wil); + struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); struct wmi_pmc_cmd pmc_cmd = {0}; int last_cmd_err = -ENOMEM; @@ -186,6 +188,7 @@ void wil_pmc_alloc(struct wil6210_priv *wil, wil_dbg_misc(wil, "pmc_alloc: send WMI_PMC_CMD with ALLOCATE op\n"); pmc->last_cmd_status = wmi_send(wil, WMI_PMC_CMDID, + vif->mid, &pmc_cmd, sizeof(pmc_cmd)); if (pmc->last_cmd_status) { @@ -236,6 +239,7 @@ void wil_pmc_free(struct wil6210_priv *wil, int send_pmc_cmd) { struct pmc_ctx *pmc = &wil->pmc; struct device *dev = wil_to_dev(wil); + struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); struct wmi_pmc_cmd pmc_cmd = {0}; mutex_lock(&pmc->lock); @@ -254,8 +258,8 @@ void wil_pmc_free(struct wil6210_priv *wil, int send_pmc_cmd) wil_dbg_misc(wil, "send WMI_PMC_CMD with RELEASE op\n"); pmc_cmd.op = WMI_PMC_RELEASE; pmc->last_cmd_status = - wmi_send(wil, WMI_PMC_CMDID, &pmc_cmd, - sizeof(pmc_cmd)); + wmi_send(wil, WMI_PMC_CMDID, vif->mid, + &pmc_cmd, sizeof(pmc_cmd)); if (pmc->last_cmd_status) { wil_err(wil, "WMI_PMC_CMD with RELEASE op failed, status %d", diff --git a/drivers/net/wireless/ath/wil6210/rx_reorder.c b/drivers/net/wireless/ath/wil6210/rx_reorder.c index a43cffcf1bbf..14dcb0698dee 100644 --- a/drivers/net/wireless/ath/wil6210/rx_reorder.c +++ b/drivers/net/wireless/ath/wil6210/rx_reorder.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2014-2017 Qualcomm Atheros, Inc. + * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -40,11 +41,10 @@ static inline int reorder_index(struct wil_tid_ampdu_rx *r, u16 seq) return seq_sub(seq, r->ssn) % r->buf_size; } -static void wil_release_reorder_frame(struct wil6210_priv *wil, +static void wil_release_reorder_frame(struct net_device *ndev, struct wil_tid_ampdu_rx *r, int index) { - struct net_device *ndev = wil_to_ndev(wil); struct sk_buff *skb = r->reorder_buf[index]; if (!skb) @@ -59,7 +59,7 @@ no_frame: r->head_seq_num = seq_inc(r->head_seq_num); } -static void wil_release_reorder_frames(struct wil6210_priv *wil, +static void wil_release_reorder_frames(struct net_device *ndev, struct wil_tid_ampdu_rx *r, u16 hseq) { @@ -73,18 +73,18 @@ static void wil_release_reorder_frames(struct wil6210_priv *wil, */ while (seq_less(r->head_seq_num, hseq) && r->stored_mpdu_num) { index = reorder_index(r, r->head_seq_num); - wil_release_reorder_frame(wil, r, index); + wil_release_reorder_frame(ndev, r, index); } r->head_seq_num = hseq; } -static void wil_reorder_release(struct wil6210_priv *wil, +static void wil_reorder_release(struct net_device *ndev, struct wil_tid_ampdu_rx *r) { int index = reorder_index(r, r->head_seq_num); while (r->reorder_buf[index]) { - wil_release_reorder_frame(wil, r, index); + wil_release_reorder_frame(ndev, r, index); index = reorder_index(r, r->head_seq_num); } } @@ -93,7 +93,8 @@ static void wil_reorder_release(struct wil6210_priv *wil, void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb) __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) { - struct net_device *ndev = wil_to_ndev(wil); + struct wil6210_vif *vif; + struct net_device *ndev; struct vring_rx_desc *d = wil_skb_rxdesc(skb); int tid = wil_rxdesc_tid(d); int cid = wil_rxdesc_cid(d); @@ -108,6 +109,14 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) wil_dbg_txrx(wil, "MID %d CID %d TID %d Seq 0x%03x mcast %01x\n", mid, cid, tid, seq, mcast); + vif = wil->vifs[mid]; + if (unlikely(!vif)) { + wil_dbg_txrx(wil, "invalid VIF, mid %d\n", mid); + dev_kfree_skb(skb); + return; + } + ndev = vif_to_ndev(vif); + if (unlikely(mcast)) { wil_netif_rx_any(skb, ndev); return; @@ -168,7 +177,7 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) if (!seq_less(seq, r->head_seq_num + r->buf_size)) { hseq = seq_inc(seq_sub(seq, r->buf_size)); /* release stored frames up to new head to stack */ - wil_release_reorder_frames(wil, r, hseq); + wil_release_reorder_frames(ndev, r, hseq); } /* Now the new frame is always in the range of the reordering buffer */ @@ -199,16 +208,18 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) r->reorder_buf[index] = skb; r->reorder_time[index] = jiffies; r->stored_mpdu_num++; - wil_reorder_release(wil, r); + wil_reorder_release(ndev, r); out: spin_unlock(&sta->tid_rx_lock); } /* process BAR frame, called in NAPI context */ -void wil_rx_bar(struct wil6210_priv *wil, u8 cid, u8 tid, u16 seq) +void wil_rx_bar(struct wil6210_priv *wil, struct wil6210_vif *vif, + u8 cid, u8 tid, u16 seq) { struct wil_sta_info *sta = &wil->sta[cid]; + struct net_device *ndev = vif_to_ndev(vif); struct wil_tid_ampdu_rx *r; spin_lock(&sta->tid_rx_lock); @@ -223,9 +234,9 @@ void wil_rx_bar(struct wil6210_priv *wil, u8 cid, u8 tid, u16 seq) seq, r->head_seq_num); goto out; } - wil_dbg_txrx(wil, "BAR: CID %d TID %d Seq 0x%03x head 0x%03x\n", - cid, tid, seq, r->head_seq_num); - wil_release_reorder_frames(wil, r, seq); + wil_dbg_txrx(wil, "BAR: CID %d MID %d TID %d Seq 0x%03x head 0x%03x\n", + cid, vif->mid, tid, seq, r->head_seq_num); + wil_release_reorder_frames(ndev, r, seq); out: spin_unlock(&sta->tid_rx_lock); @@ -292,8 +303,8 @@ static u16 wil_agg_size(struct wil6210_priv *wil, u16 req_agg_wsize) } /* Block Ack - Rx side (recipient) */ -int wil_addba_rx_request(struct wil6210_priv *wil, u8 cidxtid, - u8 dialog_token, __le16 ba_param_set, +int wil_addba_rx_request(struct wil6210_priv *wil, u8 mid, + u8 cidxtid, u8 dialog_token, __le16 ba_param_set, __le16 ba_timeout, __le16 ba_seq_ctrl) __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) { @@ -354,7 +365,7 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) } } - rc = wmi_addba_rx_resp(wil, cid, tid, dialog_token, status, + rc = wmi_addba_rx_resp(wil, mid, cid, tid, dialog_token, status, agg_amsdu, agg_wsize, agg_timeout); if (rc || (status != WLAN_STATUS_SUCCESS)) { wil_err(wil, "do not apply ba, rc(%d), status(%d)\n", rc, @@ -393,7 +404,7 @@ int wil_addba_tx_request(struct wil6210_priv *wil, u8 ringid, u16 wsize) goto out; } txdata->addba_in_progress = true; - rc = wmi_addba(wil, ringid, agg_wsize, agg_timeout); + rc = wmi_addba(wil, txdata->mid, ringid, agg_wsize, agg_timeout); if (rc) { wil_err(wil, "wmi_addba failed, rc (%d)", rc); txdata->addba_in_progress = false; diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 16b8a4e5201f..b60b9fcaaebd 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2012-2017 Qualcomm Atheros, Inc. + * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -474,7 +475,8 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, struct vring *vring) { struct device *dev = wil_to_dev(wil); - struct net_device *ndev = wil_to_ndev(wil); + struct wil6210_vif *vif; + struct net_device *ndev; volatile struct vring_rx_desc *_d; struct vring_rx_desc *d; struct sk_buff *skb; @@ -483,7 +485,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, unsigned int sz = wil->rx_buf_len + ETH_HLEN + snaplen; u16 dmalen; u8 ftype; - int cid; + int cid, mid; int i; struct wil_net_stats *stats; @@ -520,6 +522,16 @@ again: (const void *)d, sizeof(*d), false); cid = wil_rxdesc_cid(d); + mid = wil_rxdesc_mid(d); + vif = wil->vifs[mid]; + + if (unlikely(!vif)) { + wil_dbg_txrx(wil, "skipped RX descriptor with invalid mid %d", + mid); + kfree_skb(skb); + goto again; + } + ndev = vif_to_ndev(vif); stats = &wil->sta[cid].stats; if (unlikely(dmalen > sz)) { @@ -553,7 +565,6 @@ again: ftype = wil_rxdesc_ftype(d) << 2; if (unlikely(ftype != IEEE80211_FTYPE_DATA)) { u8 fc1 = wil_rxdesc_fc1(d); - int mid = wil_rxdesc_mid(d); int tid = wil_rxdesc_tid(d); u16 seq = wil_rxdesc_seq(d); @@ -565,7 +576,7 @@ again: wil_dbg_txrx(wil, "BAR: MID %d CID %d TID %d Seq 0x%03x\n", mid, cid, tid, seq); - wil_rx_bar(wil, cid, tid, seq); + wil_rx_bar(wil, vif, cid, tid, seq); } else { /* print again all info. One can enable only this * without overhead for printing every Rx frame @@ -621,10 +632,15 @@ again: /** * allocate and fill up to @count buffers in rx ring * buffers posted at @swtail + * Note: we have a single RX queue for servicing all VIFs, but we + * allocate skbs with headroom according to main interface only. This + * means it will not work with monitor interface together with other VIFs. + * Currently we only support monitor interface on its own without other VIFs, + * and we will need to fix this code once we add support. */ static int wil_rx_refill(struct wil6210_priv *wil, int count) { - struct net_device *ndev = wil_to_ndev(wil); + struct net_device *ndev = wil->main_ndev; struct vring *v = &wil->vring_rx; u32 next_tail; int rc = 0; @@ -713,8 +729,9 @@ static int wil_rx_crypto_check(struct wil6210_priv *wil, struct sk_buff *skb) void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) { gro_result_t rc = GRO_NORMAL; + struct wil6210_vif *vif = ndev_to_vif(ndev); struct wil6210_priv *wil = ndev_to_wil(ndev); - struct wireless_dev *wdev = wil_to_wdev(wil); + struct wireless_dev *wdev = vif_to_wdev(vif); unsigned int len = skb->len; struct vring_rx_desc *d = wil_skb_rxdesc(skb); int cid = wil_rxdesc_cid(d); /* always 0..7, no need to check */ @@ -751,14 +768,15 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) goto stats; } - if (wdev->iftype == NL80211_IFTYPE_AP && !wil->ap_isolate) { + if (wdev->iftype == NL80211_IFTYPE_AP && !vif->ap_isolate) { if (mcast) { /* send multicast frames both to higher layers in * local net stack and back to the wireless medium */ xmit_skb = skb_copy(skb, GFP_ATOMIC); } else { - int xmit_cid = wil_find_cid(wil, eth->h_dest); + int xmit_cid = wil_find_cid(wil, vif->mid, + eth->h_dest); if (xmit_cid >= 0) { /* The destination station is associated to @@ -786,8 +804,8 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) } if (skb) { /* deliver to local stack */ - skb->protocol = eth_type_trans(skb, ndev); + skb->dev = ndev; rc = napi_gro_receive(&wil->napi_rx, skb); wil_dbg_txrx(wil, "Rx complete %d bytes => %s\n", len, gro_res_str[rc]); @@ -815,7 +833,8 @@ stats: */ void wil_rx_handle(struct wil6210_priv *wil, int *quota) { - struct net_device *ndev = wil_to_ndev(wil); + struct net_device *ndev = wil->main_ndev; + struct wireless_dev *wdev = ndev->ieee80211_ptr; struct vring *v = &wil->vring_rx; struct sk_buff *skb; @@ -827,7 +846,8 @@ void wil_rx_handle(struct wil6210_priv *wil, int *quota) while ((*quota > 0) && (NULL != (skb = wil_vring_reap_rx(wil, v)))) { (*quota)--; - if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) { + /* monitor is currently supported on main interface only */ + if (wdev->iftype == NL80211_IFTYPE_MONITOR) { skb->dev = ndev; skb_reset_mac_header(skb); skb->ip_summed = CHECKSUM_UNNECESSARY; @@ -911,12 +931,14 @@ static inline void wil_tx_data_init(struct vring_tx_data *txdata) txdata->agg_timeout = 0; txdata->agg_amsdu = 0; txdata->addba_in_progress = false; + txdata->mid = U8_MAX; spin_unlock_bh(&txdata->lock); } -int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, +int wil_vring_init_tx(struct wil6210_vif *vif, int id, int size, int cid, int tid) { + struct wil6210_priv *wil = vif_to_wil(vif); int rc; struct wmi_vring_cfg_cmd cmd = { .action = cpu_to_le32(WMI_VRING_CMD_ADD), @@ -966,9 +988,9 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, cmd.vring_cfg.tx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa); - if (!wil->privacy) + if (!vif->privacy) txdata->dot1x_open = true; - rc = wmi_call(wil, WMI_VRING_CFG_CMDID, &cmd, sizeof(cmd), + rc = wmi_call(wil, WMI_VRING_CFG_CMDID, vif->mid, &cmd, sizeof(cmd), WMI_VRING_CFG_DONE_EVENTID, &reply, sizeof(reply), 100); if (rc) goto out_free; @@ -982,6 +1004,7 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, spin_lock_bh(&txdata->lock); vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr); + txdata->mid = vif->mid; txdata->enabled = 1; spin_unlock_bh(&txdata->lock); @@ -1003,8 +1026,9 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, return rc; } -int wil_vring_init_bcast(struct wil6210_priv *wil, int id, int size) +int wil_vring_init_bcast(struct wil6210_vif *vif, int id, int size) { + struct wil6210_priv *wil = vif_to_wil(vif); int rc; struct wmi_bcast_vring_cfg_cmd cmd = { .action = cpu_to_le32(WMI_VRING_CMD_ADD), @@ -1046,9 +1070,10 @@ int wil_vring_init_bcast(struct wil6210_priv *wil, int id, int size) cmd.vring_cfg.tx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa); - if (!wil->privacy) + if (!vif->privacy) txdata->dot1x_open = true; - rc = wmi_call(wil, WMI_BCAST_VRING_CFG_CMDID, &cmd, sizeof(cmd), + rc = wmi_call(wil, WMI_BCAST_VRING_CFG_CMDID, vif->mid, + &cmd, sizeof(cmd), WMI_VRING_CFG_DONE_EVENTID, &reply, sizeof(reply), 100); if (rc) goto out_free; @@ -1062,6 +1087,7 @@ int wil_vring_init_bcast(struct wil6210_priv *wil, int id, int size) spin_lock_bh(&txdata->lock); vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr); + txdata->mid = vif->mid; txdata->enabled = 1; spin_unlock_bh(&txdata->lock); @@ -1091,6 +1117,7 @@ void wil_vring_fini_tx(struct wil6210_priv *wil, int id) spin_lock_bh(&txdata->lock); txdata->dot1x_open = false; + txdata->mid = U8_MAX; txdata->enabled = 0; /* no Tx can be in progress or start anew */ spin_unlock_bh(&txdata->lock); /* napi_synchronize waits for completion of the current NAPI but will @@ -1108,11 +1135,12 @@ void wil_vring_fini_tx(struct wil6210_priv *wil, int id) } static struct vring *wil_find_tx_ucast(struct wil6210_priv *wil, + struct wil6210_vif *vif, struct sk_buff *skb) { int i; struct ethhdr *eth = (void *)skb->data; - int cid = wil_find_cid(wil, eth->h_dest); + int cid = wil_find_cid(wil, vif->mid, eth->h_dest); if (cid < 0) return NULL; @@ -1142,10 +1170,11 @@ static struct vring *wil_find_tx_ucast(struct wil6210_priv *wil, return NULL; } -static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, - struct sk_buff *skb); +static int wil_tx_vring(struct wil6210_priv *wil, struct wil6210_vif *vif, + struct vring *vring, struct sk_buff *skb); static struct vring *wil_find_tx_vring_sta(struct wil6210_priv *wil, + struct wil6210_vif *vif, struct sk_buff *skb) { struct vring *v; @@ -1160,7 +1189,7 @@ static struct vring *wil_find_tx_vring_sta(struct wil6210_priv *wil, for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { v = &wil->vring_tx[i]; txdata = &wil->vring_tx_data[i]; - if (!v->va || !txdata->enabled) + if (!v->va || !txdata->enabled || txdata->mid != vif->mid) continue; cid = wil->vring2cid_tid[i][0]; @@ -1193,11 +1222,12 @@ static struct vring *wil_find_tx_vring_sta(struct wil6210_priv *wil, * - for PBSS */ static struct vring *wil_find_tx_bcast_1(struct wil6210_priv *wil, + struct wil6210_vif *vif, struct sk_buff *skb) { struct vring *v; struct vring_tx_data *txdata; - int i = wil->bcast_vring; + int i = vif->bcast_vring; if (i < 0) return NULL; @@ -1222,6 +1252,7 @@ static void wil_set_da_for_vring(struct wil6210_priv *wil, } static struct vring *wil_find_tx_bcast_2(struct wil6210_priv *wil, + struct wil6210_vif *vif, struct sk_buff *skb) { struct vring *v, *v2; @@ -1230,13 +1261,13 @@ static struct vring *wil_find_tx_bcast_2(struct wil6210_priv *wil, u8 cid; struct ethhdr *eth = (void *)skb->data; char *src = eth->h_source; - struct vring_tx_data *txdata; + struct vring_tx_data *txdata, *txdata2; /* find 1-st vring eligible for data */ for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { v = &wil->vring_tx[i]; txdata = &wil->vring_tx_data[i]; - if (!v->va || !txdata->enabled) + if (!v->va || !txdata->enabled || txdata->mid != vif->mid) continue; cid = wil->vring2cid_tid[i][0]; @@ -1264,7 +1295,8 @@ found: /* find other active vrings and duplicate skb for each */ for (i++; i < WIL6210_MAX_TX_RINGS; i++) { v2 = &wil->vring_tx[i]; - if (!v2->va) + txdata2 = &wil->vring_tx_data[i]; + if (!v2->va || txdata2->mid != vif->mid) continue; cid = wil->vring2cid_tid[i][0]; if (cid >= WIL6210_MAX_CID) /* skip BCAST */ @@ -1280,7 +1312,7 @@ found: if (skb2) { wil_dbg_txrx(wil, "BCAST DUP -> ring %d\n", i); wil_set_da_for_vring(wil, skb2, i); - wil_tx_vring(wil, v2, skb2); + wil_tx_vring(wil, vif, v2, skb2); } else { wil_err(wil, "skb_copy failed\n"); } @@ -1417,8 +1449,8 @@ static inline void wil_set_tx_desc_last_tso(volatile struct vring_tx_desc *d) DMA_CFG_DESC_TX_0_SEGMENT_BUF_DETAILS_POS; } -static int __wil_tx_vring_tso(struct wil6210_priv *wil, struct vring *vring, - struct sk_buff *skb) +static int __wil_tx_vring_tso(struct wil6210_priv *wil, struct wil6210_vif *vif, + struct vring *vring, struct sk_buff *skb) { struct device *dev = wil_to_dev(wil); @@ -1710,8 +1742,8 @@ err_exit: return rc; } -static int __wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, - struct sk_buff *skb) +static int __wil_tx_vring(struct wil6210_priv *wil, struct wil6210_vif *vif, + struct vring *vring, struct sk_buff *skb) { struct device *dev = wil_to_dev(wil); struct vring_tx_desc dd, *d = ⅆ @@ -1725,7 +1757,7 @@ static int __wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, uint i = swhead; dma_addr_t pa; int used; - bool mcast = (vring_index == wil->bcast_vring); + bool mcast = (vring_index == vif->bcast_vring); uint len = skb_headlen(skb); wil_dbg_txrx(wil, "tx_vring: %d bytes to vring %d\n", skb->len, @@ -1860,8 +1892,8 @@ static int __wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, return -EINVAL; } -static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, - struct sk_buff *skb) +static int wil_tx_vring(struct wil6210_priv *wil, struct wil6210_vif *vif, + struct vring *vring, struct sk_buff *skb) { int vring_index = vring - wil->vring_tx; struct vring_tx_data *txdata = &wil->vring_tx_data[vring_index]; @@ -1879,7 +1911,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, } rc = (skb_is_gso(skb) ? __wil_tx_vring_tso : __wil_tx_vring) - (wil, vring, skb); + (wil, vif, vring, skb); spin_unlock(&txdata->lock); @@ -1888,6 +1920,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, /** * Check status of tx vrings and stop/wake net queues if needed + * It will start/stop net queues of a specific VIF net_device. * * This function does one of two checks: * In case check_stop is true, will check if net queues need to be stopped. If @@ -1903,28 +1936,32 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, * availability and modified vring has high descriptor availability. */ static inline void __wil_update_net_queues(struct wil6210_priv *wil, + struct wil6210_vif *vif, struct vring *vring, bool check_stop) { int i; - if (vring) - wil_dbg_txrx(wil, "vring %d, check_stop=%d, stopped=%d", - (int)(vring - wil->vring_tx), check_stop, - wil->net_queue_stopped); - else - wil_dbg_txrx(wil, "check_stop=%d, stopped=%d", - check_stop, wil->net_queue_stopped); + if (unlikely(!vif)) + return; - if (check_stop == wil->net_queue_stopped) + if (vring) + wil_dbg_txrx(wil, "vring %d, mid %d, check_stop=%d, stopped=%d", + (int)(vring - wil->vring_tx), vif->mid, check_stop, + vif->net_queue_stopped); + else + wil_dbg_txrx(wil, "check_stop=%d, mid=%d, stopped=%d", + check_stop, vif->mid, vif->net_queue_stopped); + + if (check_stop == vif->net_queue_stopped) /* net queues already in desired state */ return; if (check_stop) { if (!vring || unlikely(wil_vring_avail_low(vring))) { /* not enough room in the vring */ - netif_tx_stop_all_queues(wil_to_ndev(wil)); - wil->net_queue_stopped = true; + netif_tx_stop_all_queues(vif_to_ndev(vif)); + vif->net_queue_stopped = true; wil_dbg_txrx(wil, "netif_tx_stop called\n"); } return; @@ -1940,7 +1977,8 @@ static inline void __wil_update_net_queues(struct wil6210_priv *wil, struct vring *cur_vring = &wil->vring_tx[i]; struct vring_tx_data *txdata = &wil->vring_tx_data[i]; - if (!cur_vring->va || !txdata->enabled || cur_vring == vring) + if (txdata->mid != vif->mid || !cur_vring->va || + !txdata->enabled || cur_vring == vring) continue; if (wil_vring_avail_low(cur_vring)) { @@ -1953,30 +1991,31 @@ static inline void __wil_update_net_queues(struct wil6210_priv *wil, if (!vring || wil_vring_avail_high(vring)) { /* enough room in the vring */ wil_dbg_txrx(wil, "calling netif_tx_wake\n"); - netif_tx_wake_all_queues(wil_to_ndev(wil)); - wil->net_queue_stopped = false; + netif_tx_wake_all_queues(vif_to_ndev(vif)); + vif->net_queue_stopped = false; } } -void wil_update_net_queues(struct wil6210_priv *wil, struct vring *vring, - bool check_stop) +void wil_update_net_queues(struct wil6210_priv *wil, struct wil6210_vif *vif, + struct vring *vring, bool check_stop) { spin_lock(&wil->net_queue_lock); - __wil_update_net_queues(wil, vring, check_stop); + __wil_update_net_queues(wil, vif, vring, check_stop); spin_unlock(&wil->net_queue_lock); } -void wil_update_net_queues_bh(struct wil6210_priv *wil, struct vring *vring, - bool check_stop) +void wil_update_net_queues_bh(struct wil6210_priv *wil, struct wil6210_vif *vif, + struct vring *vring, bool check_stop) { spin_lock_bh(&wil->net_queue_lock); - __wil_update_net_queues(wil, vring, check_stop); + __wil_update_net_queues(wil, vif, vring, check_stop); spin_unlock_bh(&wil->net_queue_lock); } netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) { - struct wil6210_priv *wil = ndev_to_wil(ndev); + struct wil6210_vif *vif = ndev_to_vif(ndev); + struct wil6210_priv *wil = vif_to_wil(vif); struct ethhdr *eth = (void *)skb->data; bool bcast = is_multicast_ether_addr(eth->h_dest); struct vring *vring; @@ -1991,49 +2030,50 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) } goto drop; } - if (unlikely(!test_bit(wil_status_fwconnected, wil->status))) { - wil_dbg_ratelimited(wil, "FW not connected, packet dropped\n"); + if (unlikely(!test_bit(wil_vif_fwconnected, vif->status))) { + wil_dbg_ratelimited(wil, + "VIF not connected, packet dropped\n"); goto drop; } - if (unlikely(wil->wdev->iftype == NL80211_IFTYPE_MONITOR)) { + if (unlikely(vif->wdev.iftype == NL80211_IFTYPE_MONITOR)) { wil_err(wil, "Xmit in monitor mode not supported\n"); goto drop; } pr_once_fw = false; /* find vring */ - if (wil->wdev->iftype == NL80211_IFTYPE_STATION && !wil->pbss) { + if (vif->wdev.iftype == NL80211_IFTYPE_STATION && !vif->pbss) { /* in STA mode (ESS), all to same VRING (to AP) */ - vring = wil_find_tx_vring_sta(wil, skb); + vring = wil_find_tx_vring_sta(wil, vif, skb); } else if (bcast) { - if (wil->pbss) + if (vif->pbss) /* in pbss, no bcast VRING - duplicate skb in * all stations VRINGs */ - vring = wil_find_tx_bcast_2(wil, skb); - else if (wil->wdev->iftype == NL80211_IFTYPE_AP) + vring = wil_find_tx_bcast_2(wil, vif, skb); + else if (vif->wdev.iftype == NL80211_IFTYPE_AP) /* AP has a dedicated bcast VRING */ - vring = wil_find_tx_bcast_1(wil, skb); + vring = wil_find_tx_bcast_1(wil, vif, skb); else /* unexpected combination, fallback to duplicating * the skb in all stations VRINGs */ - vring = wil_find_tx_bcast_2(wil, skb); + vring = wil_find_tx_bcast_2(wil, vif, skb); } else { /* unicast, find specific VRING by dest. address */ - vring = wil_find_tx_ucast(wil, skb); + vring = wil_find_tx_ucast(wil, vif, skb); } if (unlikely(!vring)) { wil_dbg_txrx(wil, "No Tx VRING found for %pM\n", eth->h_dest); goto drop; } /* set up vring entry */ - rc = wil_tx_vring(wil, vring, skb); + rc = wil_tx_vring(wil, vif, vring, skb); switch (rc) { case 0: /* shall we stop net queues? */ - wil_update_net_queues_bh(wil, vring, true); + wil_update_net_queues_bh(wil, vif, vring, true); /* statistics will be updated on the tx_complete */ dev_kfree_skb_any(skb); return NETDEV_TX_OK; @@ -2072,9 +2112,10 @@ static inline void wil_consume_skb(struct sk_buff *skb, bool acked) * * Safe to call from IRQ */ -int wil_tx_complete(struct wil6210_priv *wil, int ringid) +int wil_tx_complete(struct wil6210_vif *vif, int ringid) { - struct net_device *ndev = wil_to_ndev(wil); + struct wil6210_priv *wil = vif_to_wil(vif); + struct net_device *ndev = vif_to_ndev(vif); struct device *dev = wil_to_dev(wil); struct vring *vring = &wil->vring_tx[ringid]; struct vring_tx_data *txdata = &wil->vring_tx_data[ringid]; @@ -2184,7 +2225,7 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid) /* shall we wake net queues? */ if (done) - wil_update_net_queues(wil, vring, false); + wil_update_net_queues(wil, vif, vring, false); return done; } diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h index fcdffaa8251b..5f07717acc2c 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.h +++ b/drivers/net/wireless/ath/wil6210/txrx.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2012-2016 Qualcomm Atheros, Inc. + * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -63,7 +64,9 @@ static inline void wil_desc_addr_set(struct vring_dma_addr *addr, * [dword 1] * bit 0.. 3 : pkt_mode:4 * bit 4 : pkt_mode_en:1 - * bit 5..14 : reserved0:10 + * bit 5 : mac_id_en:1 + * bit 6..7 : mac_id:2 + * bit 8..14 : reserved0:7 * bit 15 : ack_policy_en:1 * bit 16..19 : dst_index:4 * bit 20 : dst_index_en:1 @@ -132,6 +135,14 @@ struct vring_tx_mac { #define MAC_CFG_DESC_TX_1_PKT_MODE_EN_LEN 1 #define MAC_CFG_DESC_TX_1_PKT_MODE_EN_MSK 0x10 +#define MAC_CFG_DESC_TX_1_MAC_ID_EN_POS 5 +#define MAC_CFG_DESC_TX_1_MAC_ID_EN_LEN 1 +#define MAC_CFG_DESC_TX_1_MAC_ID_EN_MSK 0x20 + +#define MAC_CFG_DESC_TX_1_MAC_ID_POS 6 +#define MAC_CFG_DESC_TX_1_MAC_ID_LEN 2 +#define MAC_CFG_DESC_TX_1_MAC_ID_MSK 0xc0 + #define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_POS 15 #define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_LEN 1 #define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_MSK 0x8000 @@ -304,7 +315,7 @@ enum { * bit 0.. 3 : tid:4 The QoS (b3-0) TID Field * bit 4.. 6 : cid:3 The Source index that was found during parsing the TA. * This field is used to define the source of the packet - * bit 7 : reserved:1 + * bit 7 : MAC_id_valid:1, 1 if MAC virtual number is valid. * bit 8.. 9 : mid:2 The MAC virtual number * bit 10..11 : frame_type:2 : The FC (b3-2) - MPDU Type * (management, data, control and extension) @@ -395,6 +406,7 @@ struct vring_rx_mac { #define RX_DMA_D0_CMD_DMA_EOP BIT(8) #define RX_DMA_D0_CMD_DMA_RT BIT(9) /* always 1 */ #define RX_DMA_D0_CMD_DMA_IT BIT(10) /* interrupt */ +#define RX_MAC_D0_MAC_ID_VALID BIT(7) /* Error field */ #define RX_DMA_ERROR_FCS BIT(0) @@ -451,7 +463,8 @@ static inline int wil_rxdesc_cid(struct vring_rx_desc *d) static inline int wil_rxdesc_mid(struct vring_rx_desc *d) { - return WIL_GET_BITS(d->mac.d0, 8, 9); + return (d->mac.d0 & RX_MAC_D0_MAC_ID_VALID) ? + WIL_GET_BITS(d->mac.d0, 8, 9) : 0; } static inline int wil_rxdesc_ftype(struct vring_rx_desc *d) @@ -517,7 +530,8 @@ static inline struct vring_rx_desc *wil_skb_rxdesc(struct sk_buff *skb) void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev); void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb); -void wil_rx_bar(struct wil6210_priv *wil, u8 cid, u8 tid, u16 seq); +void wil_rx_bar(struct wil6210_priv *wil, struct wil6210_vif *vif, + u8 cid, u8 tid, u16 seq); struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil, int size, u16 ssn); void wil_tid_ampdu_rx_free(struct wil6210_priv *wil, diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 0df2aada6659..f9c5155025bc 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -26,6 +26,7 @@ #include #include "wmi.h" #include "wil_platform.h" +#include "fw.h" extern bool no_fw_recovery; extern unsigned int mtu_max; @@ -49,6 +50,11 @@ extern bool disable_ap_sme; #define WIL_DEFAULT_BUS_REQUEST_KBPS 128000 /* ~1Gbps */ #define WIL_MAX_BUS_REQUEST_KBPS 800000 /* ~6.1Gbps */ +/* maximum number of virtual interfaces the driver supports + * (including the main interface) + */ +#define WIL_MAX_VIFS 4 + /** * extract bits [@b0:@b1] (inclusive) from the value @x * it should be @b0 <= @b1, or result is incorrect @@ -463,13 +469,12 @@ struct vring_tx_data { u16 agg_timeout; u8 agg_amsdu; bool addba_in_progress; /* if set, agg_xxx is for request in progress */ + u8 mid; spinlock_t lock; }; enum { /* for wil6210_priv.status */ wil_status_fwready = 0, /* FW operational */ - wil_status_fwconnecting, - wil_status_fwconnected, wil_status_dontscan, wil_status_mbox_ready, /* MBOX structures ready */ wil_status_irqen, /* interrupts enabled - for debug */ @@ -541,7 +546,6 @@ struct wil_tid_crypto_rx { struct wil_p2p_info { struct ieee80211_channel listen_chan; u8 discovery_started; - u8 p2p_dev_started; u64 cookie; struct wireless_dev *pending_listen_wdev; unsigned int listen_duration; @@ -584,6 +588,7 @@ struct wil_net_stats { */ struct wil_sta_info { u8 addr[ETH_ALEN]; + u8 mid; enum wil_sta_status status; struct wil_net_stats stats; /* Rx BACK */ @@ -669,10 +674,44 @@ extern struct blink_on_off_time led_blink_time[WIL_LED_TIME_LAST]; extern u8 led_id; extern u8 led_polarity; +enum wil6210_vif_status { + wil_vif_fwconnecting, + wil_vif_fwconnected, + wil_vif_status_last /* keep last */ +}; + +struct wil6210_vif { + struct wireless_dev wdev; + struct net_device *ndev; + struct wil6210_priv *wil; + u8 mid; + DECLARE_BITMAP(status, wil_vif_status_last); + u32 privacy; /* secure connection? */ + u16 channel; /* relevant in AP mode */ + u8 hidden_ssid; /* relevant in AP mode */ + u32 ap_isolate; /* no intra-BSS communication */ + bool pbss; + int bcast_vring; + struct cfg80211_bss *bss; /* connected bss, relevant in STA mode */ + int locally_generated_disc; /* relevant in STA mode */ + struct timer_list connect_timer; + struct work_struct disconnect_worker; + /* scan */ + struct cfg80211_scan_request *scan_request; + struct timer_list scan_timer; /* detect scan timeout */ + struct wil_p2p_info p2p; + /* keep alive */ + struct list_head probe_client_pending; + struct mutex probe_client_mutex; /* protect @probe_client_pending */ + struct work_struct probe_client_worker; + int net_queue_stopped; /* netif_tx_stop_all_queues invoked */ +}; + struct wil6210_priv { struct pci_dev *pdev; u32 bar_size; - struct wireless_dev *wdev; + struct wiphy *wiphy; + struct net_device *main_ndev; void __iomem *csr; DECLARE_BITMAP(status, wil_status_last); u8 fw_version[ETHTOOL_FWVERS_LEN]; @@ -686,21 +725,18 @@ struct wil6210_priv { DECLARE_BITMAP(hw_capa, hw_capa_last); DECLARE_BITMAP(fw_capabilities, WMI_FW_CAPABILITY_MAX); DECLARE_BITMAP(platform_capa, WIL_PLATFORM_CAPA_MAX); - u8 n_mids; /* number of additional MIDs as reported by FW */ u32 recovery_count; /* num of FW recovery attempts in a short time */ u32 recovery_state; /* FW recovery state machine */ unsigned long last_fw_recovery; /* jiffies of last fw recovery */ wait_queue_head_t wq; /* for all wait_event() use */ + u8 max_vifs; /* maximum number of interfaces, including main */ + struct wil6210_vif *vifs[WIL_MAX_VIFS]; + struct mutex vif_mutex; /* protects access to VIF entries */ + atomic_t connected_vifs; /* profile */ struct cfg80211_chan_def monitor_chandef; u32 monitor_flags; - u32 privacy; /* secure connection? */ - u8 hidden_ssid; /* relevant in AP mode */ - u16 channel; /* relevant in AP mode */ int sinfo_gen; - u32 ap_isolate; /* no intra-BSS communication */ - struct cfg80211_bss *bss; /* connected bss, relevant in STA mode */ - int locally_generated_disc; /* relevant in STA mode */ /* interrupt moderation */ u32 tx_max_burst_duration; u32 tx_interframe_timeout; @@ -715,15 +751,13 @@ struct wil6210_priv { struct completion wmi_call; u16 wmi_seq; u16 reply_id; /**< wait for this WMI event */ + u8 reply_mid; void *reply_buf; u16 reply_size; struct workqueue_struct *wmi_wq; /* for deferred calls */ struct work_struct wmi_event_worker; struct workqueue_struct *wq_service; - struct work_struct disconnect_worker; struct work_struct fw_error_worker; /* for FW error recovery */ - struct timer_list connect_timer; - struct timer_list scan_timer; /* detect scan timeout */ struct list_head pending_wmi_ev; /* * protect pending_wmi_ev @@ -732,13 +766,10 @@ struct wil6210_priv { */ spinlock_t wmi_ev_lock; spinlock_t net_queue_lock; /* guarding stop/wake netif queue */ - int net_queue_stopped; /* netif_tx_stop_all_queues invoked */ struct napi_struct napi_rx; struct napi_struct napi_tx; - /* keep alive */ - struct list_head probe_client_pending; - struct mutex probe_client_mutex; /* protect @probe_client_pending */ - struct work_struct probe_client_worker; + struct net_device napi_ndev; /* dummy net_device serving all VIFs */ + /* DMA related */ struct vring vring_rx; unsigned int rx_buf_len; @@ -746,11 +777,8 @@ struct wil6210_priv { struct vring_tx_data vring_tx_data[WIL6210_MAX_TX_RINGS]; u8 vring2cid_tid[WIL6210_MAX_TX_RINGS][2]; /* [0] - CID, [1] - TID */ struct wil_sta_info sta[WIL6210_MAX_CID]; - int bcast_vring; u32 vring_idle_trsh; /* HW fetches up to 16 descriptors at once */ u32 dma_addr_size; /* indicates dma addr size */ - /* scan */ - struct cfg80211_scan_request *scan_request; struct mutex mutex; /* for wil6210_priv access in wil_{up|down} */ /* statistics */ @@ -770,13 +798,10 @@ struct wil6210_priv { struct pmc_ctx pmc; - bool pbss; - - struct wil_p2p_info p2p; + u8 p2p_dev_started; /* P2P_DEVICE vif */ struct wireless_dev *p2p_wdev; - struct mutex p2p_wdev_mutex; /* protect @p2p_wdev and @scan_request */ struct wireless_dev *radio_wdev; /* High Access Latency Policy voting */ @@ -798,13 +823,35 @@ struct wil6210_priv { u32 iccm_base; }; -#define wil_to_wiphy(i) (i->wdev->wiphy) +#define wil_to_wiphy(i) (i->wiphy) #define wil_to_dev(i) (wiphy_dev(wil_to_wiphy(i))) #define wiphy_to_wil(w) (struct wil6210_priv *)(wiphy_priv(w)) -#define wil_to_wdev(i) (i->wdev) #define wdev_to_wil(w) (struct wil6210_priv *)(wdev_priv(w)) -#define wil_to_ndev(i) (wil_to_wdev(i)->netdev) #define ndev_to_wil(n) (wdev_to_wil(n->ieee80211_ptr)) +#define ndev_to_vif(n) (struct wil6210_vif *)(netdev_priv(n)) +#define vif_to_wil(v) (v->wil) +#define vif_to_ndev(v) (v->ndev) +#define vif_to_wdev(v) (&v->wdev) + +static inline struct wil6210_vif *wdev_to_vif(struct wil6210_priv *wil, + struct wireless_dev *wdev) +{ + /* main interface is shared with P2P device */ + if (wdev == wil->p2p_wdev) + return ndev_to_vif(wil->main_ndev); + else + return container_of(wdev, struct wil6210_vif, wdev); +} + +static inline struct wireless_dev * +vif_to_radio_wdev(struct wil6210_priv *wil, struct wil6210_vif *vif) +{ + /* main interface is shared with P2P device */ + if (vif->mid) + return vif_to_wdev(vif); + else + return wil->radio_wdev; +} __printf(2, 3) void wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...); @@ -817,7 +864,7 @@ void __wil_info(struct wil6210_priv *wil, const char *fmt, ...); __printf(2, 3) void wil_dbg_ratelimited(const struct wil6210_priv *wil, const char *fmt, ...); #define wil_dbg(wil, fmt, arg...) do { \ - netdev_dbg(wil_to_ndev(wil), fmt, ##arg); \ + netdev_dbg(wil->main_ndev, fmt, ##arg); \ wil_dbg_trace(wil, fmt, ##arg); \ } while (0) @@ -900,9 +947,18 @@ void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src, void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, size_t count); +struct wil6210_vif * +wil_vif_alloc(struct wil6210_priv *wil, const char *name, + unsigned char name_assign_type, enum nl80211_iftype iftype); +void wil_vif_free(struct wil6210_vif *vif); void *wil_if_alloc(struct device *dev); +bool wil_has_other_active_ifaces(struct wil6210_priv *wil, + struct net_device *ndev, bool up, bool ok); +bool wil_has_active_ifaces(struct wil6210_priv *wil, bool up, bool ok); void wil_if_free(struct wil6210_priv *wil); +int wil_vif_add(struct wil6210_priv *wil, struct wil6210_vif *vif); int wil_if_add(struct wil6210_priv *wil); +void wil_vif_remove(struct wil6210_priv *wil, u8 mid); void wil_if_remove(struct wil6210_priv *wil); int wil_priv_init(struct wil6210_priv *wil); void wil_priv_deinit(struct wil6210_priv *wil); @@ -918,7 +974,7 @@ 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); +int wil_find_cid(struct wil6210_priv *wil, u8 mid, const u8 *mac); void wil_set_ethtoolops(struct net_device *ndev); struct fw_map *wil_find_fw_mapping(const char *section); @@ -927,40 +983,45 @@ 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, struct wil6210_mbox_hdr *hdr); -int wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len); +int wmi_send(struct wil6210_priv *wil, u16 cmdid, u8 mid, void *buf, u16 len); void wmi_recv_cmd(struct wil6210_priv *wil); -int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len, +int wmi_call(struct wil6210_priv *wil, u16 cmdid, u8 mid, void *buf, u16 len, u16 reply_id, void *reply, u8 reply_size, int to_msec); void wmi_event_worker(struct work_struct *work); void wmi_event_flush(struct wil6210_priv *wil); -int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid); -int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid); +int wmi_set_ssid(struct wil6210_vif *vif, u8 ssid_len, const void *ssid); +int wmi_get_ssid(struct wil6210_vif *vif, u8 *ssid_len, void *ssid); int wmi_set_channel(struct wil6210_priv *wil, int channel); int wmi_get_channel(struct wil6210_priv *wil, int *channel); -int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index, +int wmi_del_cipher_key(struct wil6210_vif *vif, u8 key_index, const void *mac_addr, int key_usage); -int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index, +int wmi_add_cipher_key(struct wil6210_vif *vif, u8 key_index, const void *mac_addr, int key_len, const void *key, int key_usage); int wmi_echo(struct wil6210_priv *wil); -int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie); +int wmi_set_ie(struct wil6210_vif *vif, u8 type, u16 ie_len, const void *ie); int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring); int wmi_rxon(struct wil6210_priv *wil, bool on); int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_m, u32 *t_r); -int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, +int wmi_disconnect_sta(struct wil6210_vif *vif, const u8 *mac, u16 reason, bool full_disconnect, bool del_sta); -int wmi_addba(struct wil6210_priv *wil, u8 ringid, u8 size, u16 timeout); -int wmi_delba_tx(struct wil6210_priv *wil, u8 ringid, u16 reason); -int wmi_delba_rx(struct wil6210_priv *wil, u8 cidxtid, u16 reason); -int wmi_addba_rx_resp(struct wil6210_priv *wil, u8 cid, u8 tid, u8 token, +int wmi_addba(struct wil6210_priv *wil, u8 mid, + u8 ringid, u8 size, u16 timeout); +int wmi_delba_tx(struct wil6210_priv *wil, u8 mid, u8 ringid, u16 reason); +int wmi_delba_rx(struct wil6210_priv *wil, u8 mid, u8 cidxtid, u16 reason); +int wmi_addba_rx_resp(struct wil6210_priv *wil, + u8 mid, u8 cid, u8 tid, u8 token, u16 status, bool amsdu, u16 agg_wsize, u16 timeout); int wmi_ps_dev_profile_cfg(struct wil6210_priv *wil, enum wmi_ps_profile_type ps_profile); int wmi_set_mgmt_retry(struct wil6210_priv *wil, u8 retry_short); int wmi_get_mgmt_retry(struct wil6210_priv *wil, u8 *retry_short); -int wmi_new_sta(struct wil6210_priv *wil, const u8 *mac, u8 aid); -int wil_addba_rx_request(struct wil6210_priv *wil, u8 cidxtid, - u8 dialog_token, __le16 ba_param_set, +int wmi_new_sta(struct wil6210_vif *vif, const u8 *mac, u8 aid); +int wmi_port_allocate(struct wil6210_priv *wil, u8 mid, + const u8 *mac, enum nl80211_iftype iftype); +int wmi_port_delete(struct wil6210_priv *wil, u8 mid); +int wil_addba_rx_request(struct wil6210_priv *wil, u8 mid, + u8 cidxtid, u8 dialog_token, __le16 ba_param_set, __le16 ba_timeout, __le16 ba_seq_ctrl); int wil_addba_tx_request(struct wil6210_priv *wil, u8 ringid, u16 wsize); @@ -976,28 +1037,31 @@ void wil6210_mask_halp(struct wil6210_priv *wil); /* P2P */ bool wil_p2p_is_social_scan(struct cfg80211_scan_request *request); -void wil_p2p_discovery_timer_fn(struct timer_list *t); -int wil_p2p_search(struct wil6210_priv *wil, +int wil_p2p_search(struct wil6210_vif *vif, struct cfg80211_scan_request *request); int wil_p2p_listen(struct wil6210_priv *wil, struct wireless_dev *wdev, unsigned int duration, struct ieee80211_channel *chan, u64 *cookie); -u8 wil_p2p_stop_discovery(struct wil6210_priv *wil); -int wil_p2p_cancel_listen(struct wil6210_priv *wil, u64 cookie); +u8 wil_p2p_stop_discovery(struct wil6210_vif *vif); +int wil_p2p_cancel_listen(struct wil6210_vif *vif, u64 cookie); void wil_p2p_listen_expired(struct work_struct *work); void wil_p2p_search_expired(struct work_struct *work); void wil_p2p_stop_radio_operations(struct wil6210_priv *wil); void wil_p2p_delayed_listen_work(struct work_struct *work); /* WMI for P2P */ -int wmi_p2p_cfg(struct wil6210_priv *wil, int channel, int bi); -int wmi_start_listen(struct wil6210_priv *wil); -int wmi_start_search(struct wil6210_priv *wil); -int wmi_stop_discovery(struct wil6210_priv *wil); +int wmi_p2p_cfg(struct wil6210_vif *vif, int channel, int bi); +int wmi_start_listen(struct wil6210_vif *vif); +int wmi_start_search(struct wil6210_vif *vif); +int wmi_stop_discovery(struct wil6210_vif *vif); int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, struct cfg80211_mgmt_tx_params *params, u64 *cookie); +int wil_cfg80211_iface_combinations_from_fw( + struct wil6210_priv *wil, + const struct wil_fw_record_concurrency *conc); +int wil_vif_prepare_stop(struct wil6210_vif *vif); #if defined(CONFIG_WIL6210_DEBUGFS) int wil6210_debugfs_init(struct wil6210_priv *wil); @@ -1007,44 +1071,47 @@ static inline int wil6210_debugfs_init(struct wil6210_priv *wil) { return 0; } static inline void wil6210_debugfs_remove(struct wil6210_priv *wil) {} #endif -int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid, +int wil_cid_fill_sinfo(struct wil6210_vif *vif, int cid, struct station_info *sinfo); -struct wireless_dev *wil_cfg80211_init(struct device *dev); -void wil_wdev_free(struct wil6210_priv *wil); +struct wil6210_priv *wil_cfg80211_init(struct device *dev); +void wil_cfg80211_deinit(struct wil6210_priv *wil); void wil_p2p_wdev_free(struct wil6210_priv *wil); int wmi_set_mac_address(struct wil6210_priv *wil, void *addr); -int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, - u8 chan, u8 hidden_ssid, u8 is_go); -int wmi_pcp_stop(struct wil6210_priv *wil); +int wmi_pcp_start(struct wil6210_vif *vif, int bi, u8 wmi_nettype, u8 chan, + u8 hidden_ssid, u8 is_go); +int wmi_pcp_stop(struct wil6210_vif *vif); int wmi_led_cfg(struct wil6210_priv *wil, bool enable); -int wmi_abort_scan(struct wil6210_priv *wil); -void wil_abort_scan(struct wil6210_priv *wil, bool sync); +int wmi_abort_scan(struct wil6210_vif *vif); +void wil_abort_scan(struct wil6210_vif *vif, bool sync); +void wil_abort_scan_all_vifs(struct wil6210_priv *wil, bool sync); void wil6210_bus_request(struct wil6210_priv *wil, u32 kbps); -void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid, +void wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid, u16 reason_code, bool from_event); -void wil_probe_client_flush(struct wil6210_priv *wil); +void wil_probe_client_flush(struct wil6210_vif *vif); void wil_probe_client_worker(struct work_struct *work); +void wil_disconnect_worker(struct work_struct *work); int wil_rx_init(struct wil6210_priv *wil, u16 size); void wil_rx_fini(struct wil6210_priv *wil); /* TX API */ -int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, +int wil_vring_init_tx(struct wil6210_vif *vif, int id, int size, int cid, int tid); void wil_vring_fini_tx(struct wil6210_priv *wil, int id); -int wil_tx_init(struct wil6210_priv *wil, int cid); -int wil_vring_init_bcast(struct wil6210_priv *wil, int id, int size); -int wil_bcast_init(struct wil6210_priv *wil); -void wil_bcast_fini(struct wil6210_priv *wil); +int wil_tx_init(struct wil6210_vif *vif, int cid); +int wil_vring_init_bcast(struct wil6210_vif *vif, int id, int size); +int wil_bcast_init(struct wil6210_vif *vif); +void wil_bcast_fini(struct wil6210_vif *vif); +void wil_bcast_fini_all(struct wil6210_priv *wil); -void wil_update_net_queues(struct wil6210_priv *wil, struct vring *vring, - bool should_stop); -void wil_update_net_queues_bh(struct wil6210_priv *wil, struct vring *vring, - bool check_stop); +void wil_update_net_queues(struct wil6210_priv *wil, struct wil6210_vif *vif, + struct vring *vring, bool should_stop); +void wil_update_net_queues_bh(struct wil6210_priv *wil, struct wil6210_vif *vif, + struct vring *vring, bool check_stop); netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev); -int wil_tx_complete(struct wil6210_priv *wil, int ringid); +int wil_tx_complete(struct wil6210_vif *vif, int ringid); void wil6210_unmask_irq_tx(struct wil6210_priv *wil); /* RX API */ diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index b31e2514f8c2..a3dda9a97c1f 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -341,6 +341,10 @@ static const char *cmdid2name(u16 cmdid) return "WMI_GET_PCP_CHANNEL_CMD"; case WMI_P2P_CFG_CMDID: return "WMI_P2P_CFG_CMD"; + case WMI_PORT_ALLOCATE_CMDID: + return "WMI_PORT_ALLOCATE_CMD"; + case WMI_PORT_DELETE_CMDID: + return "WMI_PORT_DELETE_CMD"; case WMI_START_LISTEN_CMDID: return "WMI_START_LISTEN_CMD"; case WMI_START_SEARCH_CMDID: @@ -479,6 +483,10 @@ static const char *eventid2name(u16 eventid) return "WMI_GET_PCP_CHANNEL_EVENT"; case WMI_P2P_CFG_DONE_EVENTID: return "WMI_P2P_CFG_DONE_EVENT"; + case WMI_PORT_ALLOCATED_EVENTID: + return "WMI_PORT_ALLOCATED_EVENT"; + case WMI_PORT_DELETED_EVENTID: + return "WMI_PORT_DELETED_EVENT"; case WMI_LISTEN_STARTED_EVENTID: return "WMI_LISTEN_STARTED_EVENT"; case WMI_SEARCH_STARTED_EVENTID: @@ -516,7 +524,8 @@ static const char *eventid2name(u16 eventid) } } -static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) +static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, u8 mid, + void *buf, u16 len) { struct { struct wil6210_mbox_hdr hdr; @@ -528,7 +537,7 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) .len = cpu_to_le16(sizeof(cmd.wmi) + len), }, .wmi = { - .mid = 0, + .mid = mid, .command_id = cpu_to_le16(cmdid), }, }; @@ -612,8 +621,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, "sending %s (0x%04x) [%d]\n", - cmdid2name(cmdid), cmdid, len); + wil_dbg_wmi(wil, "sending %s (0x%04x) [%d] mid %d\n", + cmdid2name(cmdid), cmdid, len, mid); 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, @@ -637,31 +646,34 @@ out: return rc; } -int wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) +int wmi_send(struct wil6210_priv *wil, u16 cmdid, u8 mid, void *buf, u16 len) { int rc; mutex_lock(&wil->wmi_mutex); - rc = __wmi_send(wil, cmdid, buf, len); + rc = __wmi_send(wil, cmdid, mid, buf, len); mutex_unlock(&wil->wmi_mutex); return rc; } /*=== Event handlers ===*/ -static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len) +static void wmi_evt_ready(struct wil6210_vif *vif, int id, void *d, int len) { - struct wireless_dev *wdev = wil->wdev; + struct wil6210_priv *wil = vif_to_wil(vif); + struct wiphy *wiphy = wil_to_wiphy(wil); struct wmi_ready_event *evt = d; - wil->n_mids = evt->numof_additional_mids; - wil_info(wil, "FW ver. %s(SW %d); MAC %pM; %d MID's\n", wil->fw_version, le32_to_cpu(evt->sw_version), - evt->mac, wil->n_mids); + evt->mac, evt->numof_additional_mids); + if (evt->numof_additional_mids + 1 < wil->max_vifs) { + wil_err(wil, "FW does not support enough MIDs (need %d)", + wil->max_vifs - 1); + return; /* FW load will fail after timeout */ + } /* ignore MAC address, we already have it from the boot loader */ - strlcpy(wdev->wiphy->fw_version, wil->fw_version, - sizeof(wdev->wiphy->fw_version)); + strlcpy(wiphy->fw_version, wil->fw_version, sizeof(wiphy->fw_version)); if (len > offsetof(struct wmi_ready_event, rfc_read_calib_result)) { wil_dbg_wmi(wil, "rfc calibration result %d\n", @@ -674,8 +686,9 @@ static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len) complete(&wil->wmi_ready); } -static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len) +static void wmi_evt_rx_mgmt(struct wil6210_vif *vif, int id, void *d, int len) { + struct wil6210_priv *wil = vif_to_wil(vif); struct wmi_rx_mgmt_packet_event *data = d; struct wiphy *wiphy = wil_to_wiphy(wil); struct ieee80211_mgmt *rx_mgmt_frame = @@ -753,14 +766,14 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len) wil_err(wil, "cfg80211_inform_bss_frame() failed\n"); } } else { - mutex_lock(&wil->p2p_wdev_mutex); - cfg80211_rx_mgmt(wil->radio_wdev, freq, signal, + mutex_lock(&wil->vif_mutex); + cfg80211_rx_mgmt(vif_to_radio_wdev(wil, vif), freq, signal, (void *)rx_mgmt_frame, d_len, 0); - mutex_unlock(&wil->p2p_wdev_mutex); + mutex_unlock(&wil->vif_mutex); } } -static void wmi_evt_tx_mgmt(struct wil6210_priv *wil, int id, void *d, int len) +static void wmi_evt_tx_mgmt(struct wil6210_vif *vif, int id, void *d, int len) { struct wmi_tx_mgmt_packet_event *data = d; struct ieee80211_mgmt *mgmt_frame = @@ -771,11 +784,13 @@ static void wmi_evt_tx_mgmt(struct wil6210_priv *wil, int id, void *d, int len) flen, true); } -static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id, +static void wmi_evt_scan_complete(struct wil6210_vif *vif, int id, void *d, int len) { - mutex_lock(&wil->p2p_wdev_mutex); - if (wil->scan_request) { + struct wil6210_priv *wil = vif_to_wil(vif); + + mutex_lock(&wil->vif_mutex); + if (vif->scan_request) { struct wmi_scan_complete_event *data = d; int status = le32_to_cpu(data->status); struct cfg80211_scan_info info = { @@ -785,26 +800,28 @@ static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id, wil_dbg_wmi(wil, "SCAN_COMPLETE(0x%08x)\n", status); wil_dbg_misc(wil, "Complete scan_request 0x%p aborted %d\n", - wil->scan_request, info.aborted); - del_timer_sync(&wil->scan_timer); - cfg80211_scan_done(wil->scan_request, &info); - wil->radio_wdev = wil->wdev; - wil->scan_request = NULL; + vif->scan_request, info.aborted); + del_timer_sync(&vif->scan_timer); + cfg80211_scan_done(vif->scan_request, &info); + if (vif->mid == 0) + wil->radio_wdev = wil->main_ndev->ieee80211_ptr; + vif->scan_request = NULL; wake_up_interruptible(&wil->wq); - if (wil->p2p.pending_listen_wdev) { + if (vif->p2p.pending_listen_wdev) { wil_dbg_misc(wil, "Scheduling delayed listen\n"); - schedule_work(&wil->p2p.delayed_listen_work); + schedule_work(&vif->p2p.delayed_listen_work); } } else { wil_err(wil, "SCAN_COMPLETE while not scanning\n"); } - mutex_unlock(&wil->p2p_wdev_mutex); + mutex_unlock(&wil->vif_mutex); } -static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) +static void wmi_evt_connect(struct wil6210_vif *vif, int id, void *d, int len) { - struct net_device *ndev = wil_to_ndev(wil); - struct wireless_dev *wdev = wil->wdev; + struct wil6210_priv *wil = vif_to_wil(vif); + struct net_device *ndev = vif_to_ndev(vif); + struct wireless_dev *wdev = vif_to_wdev(vif); struct wmi_connect_event *evt = d; int ch; /* channel number */ struct station_info sinfo; @@ -869,12 +886,12 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) if ((wdev->iftype == NL80211_IFTYPE_STATION) || (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) { - if (!test_bit(wil_status_fwconnecting, wil->status)) { + if (!test_bit(wil_vif_fwconnecting, vif->status)) { wil_err(wil, "Not in connecting state\n"); mutex_unlock(&wil->mutex); return; } - del_timer_sync(&wil->connect_timer); + del_timer_sync(&vif->connect_timer); } else if ((wdev->iftype == NL80211_IFTYPE_AP) || (wdev->iftype == NL80211_IFTYPE_P2P_GO)) { if (wil->sta[evt->cid].status != wil_sta_unused) { @@ -886,13 +903,14 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) } ether_addr_copy(wil->sta[evt->cid].addr, evt->bssid); + wil->sta[evt->cid].mid = vif->mid; wil->sta[evt->cid].status = wil_sta_conn_pending; - rc = wil_tx_init(wil, evt->cid); + rc = wil_tx_init(vif, evt->cid); if (rc) { wil_err(wil, "config tx vring failed for CID %d, rc (%d)\n", evt->cid, rc); - wmi_disconnect_sta(wil, wil->sta[evt->cid].addr, + wmi_disconnect_sta(vif, wil->sta[evt->cid].addr, WLAN_REASON_UNSPECIFIED, false, false); } else { wil_info(wil, "successful connection to CID %d\n", evt->cid); @@ -912,14 +930,14 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) } else { struct wiphy *wiphy = wil_to_wiphy(wil); - cfg80211_ref_bss(wiphy, wil->bss); - cfg80211_connect_bss(ndev, evt->bssid, wil->bss, + cfg80211_ref_bss(wiphy, vif->bss); + cfg80211_connect_bss(ndev, evt->bssid, vif->bss, assoc_req_ie, assoc_req_ielen, assoc_resp_ie, assoc_resp_ielen, WLAN_STATUS_SUCCESS, GFP_KERNEL, NL80211_TIMEOUT_UNSPECIFIED); } - wil->bss = NULL; + vif->bss = NULL; } else if ((wdev->iftype == NL80211_IFTYPE_AP) || (wdev->iftype == NL80211_IFTYPE_P2P_GO)) { if (rc) { @@ -947,19 +965,23 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) wil->sta[evt->cid].status = wil_sta_connected; wil->sta[evt->cid].aid = evt->aid; - set_bit(wil_status_fwconnected, wil->status); - wil_update_net_queues_bh(wil, NULL, false); + if (!test_and_set_bit(wil_vif_fwconnected, vif->status)) + atomic_inc(&wil->connected_vifs); + wil_update_net_queues_bh(wil, vif, NULL, false); out: - if (rc) + if (rc) { wil->sta[evt->cid].status = wil_sta_unused; - clear_bit(wil_status_fwconnecting, wil->status); + wil->sta[evt->cid].mid = U8_MAX; + } + clear_bit(wil_vif_fwconnecting, vif->status); mutex_unlock(&wil->mutex); } -static void wmi_evt_disconnect(struct wil6210_priv *wil, int id, +static void wmi_evt_disconnect(struct wil6210_vif *vif, int id, void *d, int len) { + struct wil6210_priv *wil = vif_to_wil(vif); struct wmi_disconnect_event *evt = d; u16 reason_code = le16_to_cpu(evt->protocol_reason_status); @@ -976,7 +998,7 @@ static void wmi_evt_disconnect(struct wil6210_priv *wil, int id, } mutex_lock(&wil->mutex); - wil6210_disconnect(wil, evt->bssid, reason_code, true); + wil6210_disconnect(vif, evt->bssid, reason_code, true); mutex_unlock(&wil->mutex); } @@ -984,10 +1006,10 @@ static void wmi_evt_disconnect(struct wil6210_priv *wil, int id, * Firmware reports EAPOL frame using WME event. * Reconstruct Ethernet frame and deliver it via normal Rx */ -static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id, - void *d, int len) +static void wmi_evt_eapol_rx(struct wil6210_vif *vif, int id, void *d, int len) { - struct net_device *ndev = wil_to_ndev(wil); + struct wil6210_priv *wil = vif_to_wil(vif); + struct net_device *ndev = vif_to_ndev(vif); struct wmi_eapol_rx_event *evt = d; u16 eapol_len = le16_to_cpu(evt->eapol_len); int sz = eapol_len + ETH_HLEN; @@ -996,10 +1018,10 @@ static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id, int cid; struct wil_net_stats *stats = NULL; - wil_dbg_wmi(wil, "EAPOL len %d from %pM\n", eapol_len, - evt->src_mac); + wil_dbg_wmi(wil, "EAPOL len %d from %pM MID %d\n", eapol_len, + evt->src_mac, vif->mid); - cid = wil_find_cid(wil, evt->src_mac); + cid = wil_find_cid(wil, vif->mid, evt->src_mac); if (cid >= 0) stats = &wil->sta[cid].stats; @@ -1034,13 +1056,14 @@ static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id, } } -static void wmi_evt_vring_en(struct wil6210_priv *wil, int id, void *d, int len) +static void wmi_evt_vring_en(struct wil6210_vif *vif, int id, void *d, int len) { + struct wil6210_priv *wil = vif_to_wil(vif); struct wmi_vring_en_event *evt = d; u8 vri = evt->vring_index; - struct wireless_dev *wdev = wil_to_wdev(wil); + struct wireless_dev *wdev = vif_to_wdev(vif); - wil_dbg_wmi(wil, "Enable vring %d\n", vri); + wil_dbg_wmi(wil, "Enable vring %d MID %d\n", vri, vif->mid); if (vri >= ARRAY_SIZE(wil->vring_tx)) { wil_err(wil, "Enable for invalid vring %d\n", vri); @@ -1052,15 +1075,16 @@ static void wmi_evt_vring_en(struct wil6210_priv *wil, int id, void *d, int len) * wil_cfg80211_change_station() */ wil->vring_tx_data[vri].dot1x_open = true; - if (vri == wil->bcast_vring) /* no BA for bcast */ + if (vri == vif->bcast_vring) /* no BA for bcast */ return; if (agg_wsize >= 0) wil_addba_tx_request(wil, vri, agg_wsize); } -static void wmi_evt_ba_status(struct wil6210_priv *wil, int id, void *d, - int len) +static void wmi_evt_ba_status(struct wil6210_vif *vif, int id, + void *d, int len) { + struct wil6210_priv *wil = vif_to_wil(vif); struct wmi_ba_status_event *evt = d; struct vring_tx_data *txdata; @@ -1089,19 +1113,21 @@ static void wmi_evt_ba_status(struct wil6210_priv *wil, int id, void *d, txdata->addba_in_progress = false; } -static void wmi_evt_addba_rx_req(struct wil6210_priv *wil, int id, void *d, - int len) +static void wmi_evt_addba_rx_req(struct wil6210_vif *vif, int id, + void *d, int len) { + struct wil6210_priv *wil = vif_to_wil(vif); struct wmi_rcp_addba_req_event *evt = d; - wil_addba_rx_request(wil, evt->cidxtid, evt->dialog_token, + wil_addba_rx_request(wil, vif->mid, evt->cidxtid, evt->dialog_token, evt->ba_param_set, evt->ba_timeout, evt->ba_seq_ctrl); } -static void wmi_evt_delba(struct wil6210_priv *wil, int id, void *d, int len) +static void wmi_evt_delba(struct wil6210_vif *vif, int id, void *d, int len) __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) { + struct wil6210_priv *wil = vif_to_wil(vif); struct wmi_delba_event *evt = d; u8 cid, tid; u16 reason = __le16_to_cpu(evt->reason); @@ -1110,8 +1136,8 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) might_sleep(); parse_cidxtid(evt->cidxtid, &cid, &tid); - wil_dbg_wmi(wil, "DELBA CID %d TID %d from %s reason %d\n", - cid, tid, + wil_dbg_wmi(wil, "DELBA MID %d CID %d TID %d from %s reason %d\n", + vif->mid, cid, tid, evt->from_initiator ? "originator" : "recipient", reason); if (!evt->from_initiator) { @@ -1148,8 +1174,9 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) } static void -wmi_evt_sched_scan_result(struct wil6210_priv *wil, int id, void *d, int len) +wmi_evt_sched_scan_result(struct wil6210_vif *vif, int id, void *d, int len) { + struct wil6210_priv *wil = vif_to_wil(vif); struct wmi_sched_scan_result_event *data = d; struct wiphy *wiphy = wil_to_wiphy(wil); struct ieee80211_mgmt *rx_mgmt_frame = @@ -1220,15 +1247,17 @@ wmi_evt_sched_scan_result(struct wil6210_priv *wil, int id, void *d, int len) * Some events are ignored for purpose; and need not be interpreted as * "unhandled events" */ -static void wmi_evt_ignore(struct wil6210_priv *wil, int id, void *d, int len) +static void wmi_evt_ignore(struct wil6210_vif *vif, int id, void *d, int len) { + struct wil6210_priv *wil = vif_to_wil(vif); + wil_dbg_wmi(wil, "Ignore event 0x%04x len %d\n", id, len); } static const struct { int eventid; - void (*handler)(struct wil6210_priv *wil, int eventid, - void *data, int data_len); + void (*handler)(struct wil6210_vif *vif, + int eventid, void *data, int data_len); } wmi_evt_handlers[] = { {WMI_READY_EVENTID, wmi_evt_ready}, {WMI_FW_READY_EVENTID, wmi_evt_ignore}, @@ -1325,6 +1354,7 @@ void wmi_recv_cmd(struct wil6210_priv *wil) (len >= sizeof(struct wmi_cmd_hdr))) { struct wmi_cmd_hdr *wmi = &evt->event.wmi; u16 id = le16_to_cpu(wmi->command_id); + u8 mid = wmi->mid; u32 tstamp = le32_to_cpu(wmi->fw_timestamp); if (test_bit(wil_status_resuming, wil->status)) { if (id == WMI_TRAFFIC_RESUME_EVENTID) @@ -1336,7 +1366,8 @@ void wmi_recv_cmd(struct wil6210_priv *wil) id); } spin_lock_irqsave(&wil->wmi_ev_lock, flags); - if (wil->reply_id && wil->reply_id == id) { + if (wil->reply_id && wil->reply_id == id && + wil->reply_mid == mid) { if (wil->reply_buf) { memcpy(wil->reply_buf, wmi, min(len, wil->reply_size)); @@ -1384,7 +1415,7 @@ void wmi_recv_cmd(struct wil6210_priv *wil) n - num_immed_reply, num_immed_reply); } -int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len, +int wmi_call(struct wil6210_priv *wil, u16 cmdid, u8 mid, void *buf, u16 len, u16 reply_id, void *reply, u8 reply_size, int to_msec) { int rc; @@ -1394,12 +1425,13 @@ int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len, spin_lock(&wil->wmi_ev_lock); wil->reply_id = reply_id; + wil->reply_mid = mid; wil->reply_buf = reply; wil->reply_size = reply_size; reinit_completion(&wil->wmi_call); spin_unlock(&wil->wmi_ev_lock); - rc = __wmi_send(wil, cmdid, buf, len); + rc = __wmi_send(wil, cmdid, mid, buf, len); if (rc) goto out; @@ -1419,6 +1451,7 @@ int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len, out: spin_lock(&wil->wmi_ev_lock); wil->reply_id = 0; + wil->reply_mid = U8_MAX; wil->reply_buf = NULL; wil->reply_size = 0; spin_unlock(&wil->wmi_ev_lock); @@ -1430,27 +1463,31 @@ out: int wmi_echo(struct wil6210_priv *wil) { + struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); struct wmi_echo_cmd cmd = { .value = cpu_to_le32(0x12345678), }; - return wmi_call(wil, WMI_ECHO_CMDID, &cmd, sizeof(cmd), + return wmi_call(wil, WMI_ECHO_CMDID, vif->mid, &cmd, sizeof(cmd), WMI_ECHO_RSP_EVENTID, NULL, 0, 50); } int wmi_set_mac_address(struct wil6210_priv *wil, void *addr) { + struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); struct wmi_set_mac_address_cmd cmd; ether_addr_copy(cmd.mac, addr); wil_dbg_wmi(wil, "Set MAC %pM\n", addr); - return wmi_send(wil, WMI_SET_MAC_ADDRESS_CMDID, &cmd, sizeof(cmd)); + return wmi_send(wil, WMI_SET_MAC_ADDRESS_CMDID, vif->mid, + &cmd, sizeof(cmd)); } int wmi_led_cfg(struct wil6210_priv *wil, bool enable) { + struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); int rc = 0; struct wmi_led_cfg_cmd cmd = { .led_mode = enable, @@ -1487,7 +1524,7 @@ int wmi_led_cfg(struct wil6210_priv *wil, bool enable) "%s led %d\n", enable ? "enabling" : "disabling", led_id); - rc = wmi_call(wil, WMI_LED_CFG_CMDID, &cmd, sizeof(cmd), + rc = wmi_call(wil, WMI_LED_CFG_CMDID, vif->mid, &cmd, sizeof(cmd), WMI_LED_CFG_DONE_EVENTID, &reply, sizeof(reply), 100); if (rc) @@ -1503,9 +1540,10 @@ out: return rc; } -int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, - u8 chan, u8 hidden_ssid, u8 is_go) +int wmi_pcp_start(struct wil6210_vif *vif, + int bi, u8 wmi_nettype, u8 chan, u8 hidden_ssid, u8 is_go) { + struct wil6210_priv *wil = vif_to_wil(vif); int rc; struct wmi_pcp_start_cmd cmd = { @@ -1524,7 +1562,7 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, struct wmi_pcp_started_event evt; } __packed reply; - if (!wil->privacy) + if (!vif->privacy) cmd.disable_sec = 1; if ((cmd.pcp_max_assoc_sta > WIL6210_MAX_CID) || @@ -1546,7 +1584,7 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, * Processing time may be huge, in case of secure AP it takes about * 3500ms for FW to start AP */ - rc = wmi_call(wil, WMI_PCP_START_CMDID, &cmd, sizeof(cmd), + rc = wmi_call(wil, WMI_PCP_START_CMDID, vif->mid, &cmd, sizeof(cmd), WMI_PCP_STARTED_EVENTID, &reply, sizeof(reply), 5000); if (rc) return rc; @@ -1561,20 +1599,22 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, return rc; } -int wmi_pcp_stop(struct wil6210_priv *wil) +int wmi_pcp_stop(struct wil6210_vif *vif) { + struct wil6210_priv *wil = vif_to_wil(vif); int rc; rc = wmi_led_cfg(wil, false); if (rc) return rc; - return wmi_call(wil, WMI_PCP_STOP_CMDID, NULL, 0, + return wmi_call(wil, WMI_PCP_STOP_CMDID, vif->mid, NULL, 0, WMI_PCP_STOPPED_EVENTID, NULL, 0, 20); } -int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid) +int wmi_set_ssid(struct wil6210_vif *vif, u8 ssid_len, const void *ssid) { + struct wil6210_priv *wil = vif_to_wil(vif); struct wmi_set_ssid_cmd cmd = { .ssid_len = cpu_to_le32(ssid_len), }; @@ -1584,11 +1624,12 @@ int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid) memcpy(cmd.ssid, ssid, ssid_len); - return wmi_send(wil, WMI_SET_SSID_CMDID, &cmd, sizeof(cmd)); + return wmi_send(wil, WMI_SET_SSID_CMDID, vif->mid, &cmd, sizeof(cmd)); } -int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid) +int wmi_get_ssid(struct wil6210_vif *vif, u8 *ssid_len, void *ssid) { + struct wil6210_priv *wil = vif_to_wil(vif); int rc; struct { struct wmi_cmd_hdr wmi; @@ -1596,8 +1637,8 @@ int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid) } __packed reply; int len; /* reply.cmd.ssid_len in CPU order */ - rc = wmi_call(wil, WMI_GET_SSID_CMDID, NULL, 0, WMI_GET_SSID_EVENTID, - &reply, sizeof(reply), 20); + rc = wmi_call(wil, WMI_GET_SSID_CMDID, vif->mid, NULL, 0, + WMI_GET_SSID_EVENTID, &reply, sizeof(reply), 20); if (rc) return rc; @@ -1613,22 +1654,25 @@ int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid) int wmi_set_channel(struct wil6210_priv *wil, int channel) { + struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); struct wmi_set_pcp_channel_cmd cmd = { .channel = channel - 1, }; - return wmi_send(wil, WMI_SET_PCP_CHANNEL_CMDID, &cmd, sizeof(cmd)); + return wmi_send(wil, WMI_SET_PCP_CHANNEL_CMDID, vif->mid, + &cmd, sizeof(cmd)); } int wmi_get_channel(struct wil6210_priv *wil, int *channel) { + struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); int rc; struct { struct wmi_cmd_hdr wmi; struct wmi_set_pcp_channel_cmd cmd; } __packed reply; - rc = wmi_call(wil, WMI_GET_PCP_CHANNEL_CMDID, NULL, 0, + rc = wmi_call(wil, WMI_GET_PCP_CHANNEL_CMDID, vif->mid, NULL, 0, WMI_GET_PCP_CHANNEL_EVENTID, &reply, sizeof(reply), 20); if (rc) return rc; @@ -1641,8 +1685,9 @@ int wmi_get_channel(struct wil6210_priv *wil, int *channel) return 0; } -int wmi_p2p_cfg(struct wil6210_priv *wil, int channel, int bi) +int wmi_p2p_cfg(struct wil6210_vif *vif, int channel, int bi) { + struct wil6210_priv *wil = vif_to_wil(vif); int rc; struct wmi_p2p_cfg_cmd cmd = { .discovery_mode = WMI_DISCOVERY_MODE_PEER2PEER, @@ -1656,7 +1701,7 @@ int wmi_p2p_cfg(struct wil6210_priv *wil, int channel, int bi) wil_dbg_wmi(wil, "sending WMI_P2P_CFG_CMDID\n"); - rc = wmi_call(wil, WMI_P2P_CFG_CMDID, &cmd, sizeof(cmd), + rc = wmi_call(wil, WMI_P2P_CFG_CMDID, vif->mid, &cmd, sizeof(cmd), WMI_P2P_CFG_DONE_EVENTID, &reply, sizeof(reply), 300); if (!rc && reply.evt.status != WMI_FW_STATUS_SUCCESS) { wil_err(wil, "P2P_CFG failed. status %d\n", reply.evt.status); @@ -1666,8 +1711,9 @@ int wmi_p2p_cfg(struct wil6210_priv *wil, int channel, int bi) return rc; } -int wmi_start_listen(struct wil6210_priv *wil) +int wmi_start_listen(struct wil6210_vif *vif) { + struct wil6210_priv *wil = vif_to_wil(vif); int rc; struct { struct wmi_cmd_hdr wmi; @@ -1676,7 +1722,7 @@ int wmi_start_listen(struct wil6210_priv *wil) wil_dbg_wmi(wil, "sending WMI_START_LISTEN_CMDID\n"); - rc = wmi_call(wil, WMI_START_LISTEN_CMDID, NULL, 0, + rc = wmi_call(wil, WMI_START_LISTEN_CMDID, vif->mid, NULL, 0, WMI_LISTEN_STARTED_EVENTID, &reply, sizeof(reply), 300); if (!rc && reply.evt.status != WMI_FW_STATUS_SUCCESS) { wil_err(wil, "device failed to start listen. status %d\n", @@ -1687,8 +1733,9 @@ int wmi_start_listen(struct wil6210_priv *wil) return rc; } -int wmi_start_search(struct wil6210_priv *wil) +int wmi_start_search(struct wil6210_vif *vif) { + struct wil6210_priv *wil = vif_to_wil(vif); int rc; struct { struct wmi_cmd_hdr wmi; @@ -1697,7 +1744,7 @@ int wmi_start_search(struct wil6210_priv *wil) wil_dbg_wmi(wil, "sending WMI_START_SEARCH_CMDID\n"); - rc = wmi_call(wil, WMI_START_SEARCH_CMDID, NULL, 0, + rc = wmi_call(wil, WMI_START_SEARCH_CMDID, vif->mid, NULL, 0, WMI_SEARCH_STARTED_EVENTID, &reply, sizeof(reply), 300); if (!rc && reply.evt.status != WMI_FW_STATUS_SUCCESS) { wil_err(wil, "device failed to start search. status %d\n", @@ -1708,13 +1755,14 @@ int wmi_start_search(struct wil6210_priv *wil) return rc; } -int wmi_stop_discovery(struct wil6210_priv *wil) +int wmi_stop_discovery(struct wil6210_vif *vif) { + struct wil6210_priv *wil = vif_to_wil(vif); int rc; wil_dbg_wmi(wil, "sending WMI_DISCOVERY_STOP_CMDID\n"); - rc = wmi_call(wil, WMI_DISCOVERY_STOP_CMDID, NULL, 0, + rc = wmi_call(wil, WMI_DISCOVERY_STOP_CMDID, vif->mid, NULL, 0, WMI_DISCOVERY_STOPPED_EVENTID, NULL, 0, 100); if (rc) @@ -1723,9 +1771,10 @@ int wmi_stop_discovery(struct wil6210_priv *wil) return rc; } -int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index, +int wmi_del_cipher_key(struct wil6210_vif *vif, u8 key_index, const void *mac_addr, int key_usage) { + struct wil6210_priv *wil = vif_to_wil(vif); struct wmi_delete_cipher_key_cmd cmd = { .key_index = key_index, }; @@ -1733,13 +1782,15 @@ int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index, if (mac_addr) memcpy(cmd.mac, mac_addr, WMI_MAC_LEN); - return wmi_send(wil, WMI_DELETE_CIPHER_KEY_CMDID, &cmd, sizeof(cmd)); + return wmi_send(wil, WMI_DELETE_CIPHER_KEY_CMDID, vif->mid, + &cmd, sizeof(cmd)); } -int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index, +int wmi_add_cipher_key(struct wil6210_vif *vif, u8 key_index, const void *mac_addr, int key_len, const void *key, int key_usage) { + struct wil6210_priv *wil = vif_to_wil(vif); struct wmi_add_cipher_key_cmd cmd = { .key_index = key_index, .key_usage = key_usage, @@ -1753,11 +1804,13 @@ int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index, if (mac_addr) memcpy(cmd.mac, mac_addr, WMI_MAC_LEN); - return wmi_send(wil, WMI_ADD_CIPHER_KEY_CMDID, &cmd, sizeof(cmd)); + return wmi_send(wil, WMI_ADD_CIPHER_KEY_CMDID, vif->mid, + &cmd, sizeof(cmd)); } -int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie) +int wmi_set_ie(struct wil6210_vif *vif, u8 type, u16 ie_len, const void *ie) { + struct wil6210_priv *wil = vif_to_wil(vif); static const char *const names[] = { [WMI_FRAME_BEACON] = "BEACON", [WMI_FRAME_PROBE_REQ] = "PROBE_REQ", @@ -1786,7 +1839,7 @@ int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie) /* BUG: FW API define ieLen as u8. Will fix FW */ cmd->ie_len = cpu_to_le16(ie_len); memcpy(cmd->ie_info, ie, ie_len); - rc = wmi_send(wil, WMI_SET_APPIE_CMDID, cmd, len); + rc = wmi_send(wil, WMI_SET_APPIE_CMDID, vif->mid, cmd, len); kfree(cmd); out: if (rc) { @@ -1808,6 +1861,7 @@ out: */ int wmi_rxon(struct wil6210_priv *wil, bool on) { + struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); int rc; struct { struct wmi_cmd_hdr wmi; @@ -1817,13 +1871,13 @@ int wmi_rxon(struct wil6210_priv *wil, bool on) wil_info(wil, "(%s)\n", on ? "on" : "off"); if (on) { - rc = wmi_call(wil, WMI_START_LISTEN_CMDID, NULL, 0, + rc = wmi_call(wil, WMI_START_LISTEN_CMDID, vif->mid, NULL, 0, WMI_LISTEN_STARTED_EVENTID, &reply, sizeof(reply), 100); if ((rc == 0) && (reply.evt.status != WMI_FW_STATUS_SUCCESS)) rc = -EINVAL; } else { - rc = wmi_call(wil, WMI_DISCOVERY_STOP_CMDID, NULL, 0, + rc = wmi_call(wil, WMI_DISCOVERY_STOP_CMDID, vif->mid, NULL, 0, WMI_DISCOVERY_STOPPED_EVENTID, NULL, 0, 20); } @@ -1832,8 +1886,9 @@ int wmi_rxon(struct wil6210_priv *wil, bool on) int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring) { - struct wireless_dev *wdev = wil->wdev; - struct net_device *ndev = wil_to_ndev(wil); + struct net_device *ndev = wil->main_ndev; + struct wireless_dev *wdev = ndev->ieee80211_ptr; + struct wil6210_vif *vif = ndev_to_vif(ndev); struct wmi_cfg_rx_chain_cmd cmd = { .action = WMI_RX_CHAIN_ADD, .rx_sw_ring = { @@ -1877,7 +1932,7 @@ int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring) L2_802_3_OFFLOAD_CTRL_SNAP_KEEP_MSK; /* typical time for secure PCP is 840ms */ - rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd), + rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, vif->mid, &cmd, sizeof(cmd), WMI_CFG_RX_CHAIN_DONE_EVENTID, &evt, sizeof(evt), 2000); if (rc) return rc; @@ -1895,6 +1950,7 @@ int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring) int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_bb, u32 *t_rf) { + struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); int rc; struct wmi_temp_sense_cmd cmd = { .measure_baseband_en = cpu_to_le32(!!t_bb), @@ -1906,7 +1962,7 @@ int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_bb, u32 *t_rf) struct wmi_temp_sense_done_event evt; } __packed reply; - rc = wmi_call(wil, WMI_TEMP_SENSE_CMDID, &cmd, sizeof(cmd), + rc = wmi_call(wil, WMI_TEMP_SENSE_CMDID, vif->mid, &cmd, sizeof(cmd), WMI_TEMP_SENSE_DONE_EVENTID, &reply, sizeof(reply), 100); if (rc) return rc; @@ -1919,9 +1975,10 @@ int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_bb, u32 *t_rf) return 0; } -int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, +int wmi_disconnect_sta(struct wil6210_vif *vif, const u8 *mac, u16 reason, bool full_disconnect, bool del_sta) { + struct wil6210_priv *wil = vif_to_wil(vif); int rc; u16 reason_code; struct wmi_disconnect_sta_cmd disc_sta_cmd = { @@ -1937,16 +1994,17 @@ int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, wil_dbg_wmi(wil, "disconnect_sta: (%pM, reason %d)\n", mac, reason); - wil->locally_generated_disc = true; + vif->locally_generated_disc = true; if (del_sta) { ether_addr_copy(del_sta_cmd.dst_mac, mac); - rc = wmi_call(wil, WMI_DEL_STA_CMDID, &del_sta_cmd, + rc = wmi_call(wil, WMI_DEL_STA_CMDID, vif->mid, &del_sta_cmd, sizeof(del_sta_cmd), WMI_DISCONNECT_EVENTID, &reply, sizeof(reply), 1000); } else { ether_addr_copy(disc_sta_cmd.dst_mac, mac); - rc = wmi_call(wil, WMI_DISCONNECT_STA_CMDID, &disc_sta_cmd, - sizeof(disc_sta_cmd), WMI_DISCONNECT_EVENTID, + rc = wmi_call(wil, WMI_DISCONNECT_STA_CMDID, vif->mid, + &disc_sta_cmd, sizeof(disc_sta_cmd), + WMI_DISCONNECT_EVENTID, &reply, sizeof(reply), 1000); } /* failure to disconnect in reasonable time treated as FW error */ @@ -1967,12 +2025,13 @@ int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, reply.evt.disconnect_reason); wil->sinfo_gen++; - wil6210_disconnect(wil, reply.evt.bssid, reason_code, true); + wil6210_disconnect(vif, reply.evt.bssid, reason_code, true); } return 0; } -int wmi_addba(struct wil6210_priv *wil, u8 ringid, u8 size, u16 timeout) +int wmi_addba(struct wil6210_priv *wil, u8 mid, + u8 ringid, u8 size, u16 timeout) { struct wmi_vring_ba_en_cmd cmd = { .ringid = ringid, @@ -1984,10 +2043,10 @@ int wmi_addba(struct wil6210_priv *wil, u8 ringid, u8 size, u16 timeout) wil_dbg_wmi(wil, "addba: (ring %d size %d timeout %d)\n", ringid, size, timeout); - return wmi_send(wil, WMI_VRING_BA_EN_CMDID, &cmd, sizeof(cmd)); + return wmi_send(wil, WMI_VRING_BA_EN_CMDID, mid, &cmd, sizeof(cmd)); } -int wmi_delba_tx(struct wil6210_priv *wil, u8 ringid, u16 reason) +int wmi_delba_tx(struct wil6210_priv *wil, u8 mid, u8 ringid, u16 reason) { struct wmi_vring_ba_dis_cmd cmd = { .ringid = ringid, @@ -1996,10 +2055,10 @@ int wmi_delba_tx(struct wil6210_priv *wil, u8 ringid, u16 reason) wil_dbg_wmi(wil, "delba_tx: (ring %d reason %d)\n", ringid, reason); - return wmi_send(wil, WMI_VRING_BA_DIS_CMDID, &cmd, sizeof(cmd)); + return wmi_send(wil, WMI_VRING_BA_DIS_CMDID, mid, &cmd, sizeof(cmd)); } -int wmi_delba_rx(struct wil6210_priv *wil, u8 cidxtid, u16 reason) +int wmi_delba_rx(struct wil6210_priv *wil, u8 mid, u8 cidxtid, u16 reason) { struct wmi_rcp_delba_cmd cmd = { .cidxtid = cidxtid, @@ -2009,10 +2068,11 @@ int wmi_delba_rx(struct wil6210_priv *wil, u8 cidxtid, u16 reason) wil_dbg_wmi(wil, "delba_rx: (CID %d TID %d reason %d)\n", cidxtid & 0xf, (cidxtid >> 4) & 0xf, reason); - return wmi_send(wil, WMI_RCP_DELBA_CMDID, &cmd, sizeof(cmd)); + return wmi_send(wil, WMI_RCP_DELBA_CMDID, mid, &cmd, sizeof(cmd)); } -int wmi_addba_rx_resp(struct wil6210_priv *wil, u8 cid, u8 tid, u8 token, +int wmi_addba_rx_resp(struct wil6210_priv *wil, + u8 mid, u8 cid, u8 tid, u8 token, u16 status, bool amsdu, u16 agg_wsize, u16 timeout) { int rc; @@ -2035,10 +2095,11 @@ int wmi_addba_rx_resp(struct wil6210_priv *wil, u8 cid, u8 tid, u8 token, } __packed reply; wil_dbg_wmi(wil, - "ADDBA response for CID %d TID %d size %d timeout %d status %d AMSDU%s\n", - cid, tid, agg_wsize, timeout, status, amsdu ? "+" : "-"); + "ADDBA response for MID %d CID %d TID %d size %d timeout %d status %d AMSDU%s\n", + mid, cid, tid, agg_wsize, + timeout, status, amsdu ? "+" : "-"); - rc = wmi_call(wil, WMI_RCP_ADDBA_RESP_CMDID, &cmd, sizeof(cmd), + rc = wmi_call(wil, WMI_RCP_ADDBA_RESP_CMDID, mid, &cmd, sizeof(cmd), WMI_RCP_ADDBA_RESP_SENT_EVENTID, &reply, sizeof(reply), 100); if (rc) @@ -2056,6 +2117,7 @@ int wmi_addba_rx_resp(struct wil6210_priv *wil, u8 cid, u8 tid, u8 token, int wmi_ps_dev_profile_cfg(struct wil6210_priv *wil, enum wmi_ps_profile_type ps_profile) { + struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); int rc; struct wmi_ps_dev_profile_cfg_cmd cmd = { .ps_profile = ps_profile, @@ -2070,7 +2132,8 @@ int wmi_ps_dev_profile_cfg(struct wil6210_priv *wil, reply.evt.status = cpu_to_le32(WMI_PS_CFG_CMD_STATUS_ERROR); - rc = wmi_call(wil, WMI_PS_DEV_PROFILE_CFG_CMDID, &cmd, sizeof(cmd), + rc = wmi_call(wil, WMI_PS_DEV_PROFILE_CFG_CMDID, vif->mid, + &cmd, sizeof(cmd), WMI_PS_DEV_PROFILE_CFG_EVENTID, &reply, sizeof(reply), 100); if (rc) @@ -2089,6 +2152,7 @@ int wmi_ps_dev_profile_cfg(struct wil6210_priv *wil, int wmi_set_mgmt_retry(struct wil6210_priv *wil, u8 retry_short) { + struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); int rc; struct wmi_set_mgmt_retry_limit_cmd cmd = { .mgmt_retry_limit = retry_short, @@ -2105,7 +2169,8 @@ int wmi_set_mgmt_retry(struct wil6210_priv *wil, u8 retry_short) reply.evt.status = WMI_FW_STATUS_FAILURE; - rc = wmi_call(wil, WMI_SET_MGMT_RETRY_LIMIT_CMDID, &cmd, sizeof(cmd), + rc = wmi_call(wil, WMI_SET_MGMT_RETRY_LIMIT_CMDID, vif->mid, + &cmd, sizeof(cmd), WMI_SET_MGMT_RETRY_LIMIT_EVENTID, &reply, sizeof(reply), 100); if (rc) @@ -2122,6 +2187,7 @@ int wmi_set_mgmt_retry(struct wil6210_priv *wil, u8 retry_short) int wmi_get_mgmt_retry(struct wil6210_priv *wil, u8 *retry_short) { + struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); int rc; struct { struct wmi_cmd_hdr wmi; @@ -2134,7 +2200,7 @@ int wmi_get_mgmt_retry(struct wil6210_priv *wil, u8 *retry_short) return -ENOTSUPP; reply.evt.mgmt_retry_limit = 0; - rc = wmi_call(wil, WMI_GET_MGMT_RETRY_LIMIT_CMDID, NULL, 0, + rc = wmi_call(wil, WMI_GET_MGMT_RETRY_LIMIT_CMDID, vif->mid, NULL, 0, WMI_GET_MGMT_RETRY_LIMIT_EVENTID, &reply, sizeof(reply), 100); if (rc) @@ -2146,21 +2212,23 @@ int wmi_get_mgmt_retry(struct wil6210_priv *wil, u8 *retry_short) return 0; } -int wmi_abort_scan(struct wil6210_priv *wil) +int wmi_abort_scan(struct wil6210_vif *vif) { + struct wil6210_priv *wil = vif_to_wil(vif); int rc; wil_dbg_wmi(wil, "sending WMI_ABORT_SCAN_CMDID\n"); - rc = wmi_send(wil, WMI_ABORT_SCAN_CMDID, NULL, 0); + rc = wmi_send(wil, WMI_ABORT_SCAN_CMDID, vif->mid, NULL, 0); if (rc) wil_err(wil, "Failed to abort scan (%d)\n", rc); return rc; } -int wmi_new_sta(struct wil6210_priv *wil, const u8 *mac, u8 aid) +int wmi_new_sta(struct wil6210_vif *vif, const u8 *mac, u8 aid) { + struct wil6210_priv *wil = vif_to_wil(vif); int rc; struct wmi_new_sta_cmd cmd = { .aid = aid, @@ -2170,7 +2238,7 @@ int wmi_new_sta(struct wil6210_priv *wil, const u8 *mac, u8 aid) ether_addr_copy(cmd.dst_mac, mac); - rc = wmi_send(wil, WMI_NEW_STA_CMDID, &cmd, sizeof(cmd)); + rc = wmi_send(wil, WMI_NEW_STA_CMDID, vif->mid, &cmd, sizeof(cmd)); if (rc) wil_err(wil, "Failed to send new sta (%d)\n", rc); @@ -2206,6 +2274,7 @@ static const char *suspend_status2name(u8 status) int wmi_suspend(struct wil6210_priv *wil) { + struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); int rc; struct wmi_traffic_suspend_cmd cmd = { .wakeup_trigger = wil->wakeup_trigger, @@ -2221,7 +2290,8 @@ int wmi_suspend(struct wil6210_priv *wil) reply.evt.status = WMI_TRAFFIC_SUSPEND_REJECTED_LINK_NOT_IDLE; - rc = wmi_call(wil, WMI_TRAFFIC_SUSPEND_CMDID, &cmd, sizeof(cmd), + rc = wmi_call(wil, WMI_TRAFFIC_SUSPEND_CMDID, vif->mid, + &cmd, sizeof(cmd), WMI_TRAFFIC_SUSPEND_EVENTID, &reply, sizeof(reply), suspend_to); if (rc) { @@ -2289,6 +2359,7 @@ static void resume_triggers2string(u32 triggers, char *string, int str_size) int wmi_resume(struct wil6210_priv *wil) { + struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); int rc; char string[100]; struct { @@ -2299,7 +2370,7 @@ int wmi_resume(struct wil6210_priv *wil) 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, + rc = wmi_call(wil, WMI_TRAFFIC_RESUME_CMDID, vif->mid, NULL, 0, WMI_TRAFFIC_RESUME_EVENTID, &reply, sizeof(reply), WIL_WAIT_FOR_SUSPEND_RESUME_COMP); if (rc) @@ -2313,14 +2384,100 @@ int wmi_resume(struct wil6210_priv *wil) return reply.evt.status; } -static bool wmi_evt_call_handler(struct wil6210_priv *wil, int id, +int wmi_port_allocate(struct wil6210_priv *wil, u8 mid, + const u8 *mac, enum nl80211_iftype iftype) +{ + int rc; + struct wmi_port_allocate_cmd cmd = { + .mid = mid, + }; + struct { + struct wmi_cmd_hdr wmi; + struct wmi_port_allocated_event evt; + } __packed reply; + + wil_dbg_misc(wil, "port allocate, mid %d iftype %d, mac %pM\n", + mid, iftype, mac); + + ether_addr_copy(cmd.mac, mac); + switch (iftype) { + case NL80211_IFTYPE_STATION: + cmd.port_role = WMI_PORT_STA; + break; + case NL80211_IFTYPE_AP: + cmd.port_role = WMI_PORT_AP; + break; + case NL80211_IFTYPE_P2P_CLIENT: + cmd.port_role = WMI_PORT_P2P_CLIENT; + break; + case NL80211_IFTYPE_P2P_GO: + cmd.port_role = WMI_PORT_P2P_GO; + break; + /* what about monitor??? */ + default: + wil_err(wil, "unsupported iftype: %d\n", iftype); + return -EINVAL; + } + + reply.evt.status = WMI_FW_STATUS_FAILURE; + + rc = wmi_call(wil, WMI_PORT_ALLOCATE_CMDID, mid, + &cmd, sizeof(cmd), + WMI_PORT_ALLOCATED_EVENTID, &reply, + sizeof(reply), 300); + if (rc) { + wil_err(wil, "failed to allocate port, status %d\n", rc); + return rc; + } + if (reply.evt.status != WMI_FW_STATUS_SUCCESS) { + wil_err(wil, "WMI_PORT_ALLOCATE returned status %d\n", + reply.evt.status); + return -EINVAL; + } + + return 0; +} + +int wmi_port_delete(struct wil6210_priv *wil, u8 mid) +{ + int rc; + struct wmi_port_delete_cmd cmd = { + .mid = mid, + }; + struct { + struct wmi_cmd_hdr wmi; + struct wmi_port_deleted_event evt; + } __packed reply; + + wil_dbg_misc(wil, "port delete, mid %d\n", mid); + + reply.evt.status = WMI_FW_STATUS_FAILURE; + + rc = wmi_call(wil, WMI_PORT_DELETE_CMDID, mid, + &cmd, sizeof(cmd), + WMI_PORT_DELETED_EVENTID, &reply, + sizeof(reply), 2000); + if (rc) { + wil_err(wil, "failed to delete port, status %d\n", rc); + return rc; + } + if (reply.evt.status != WMI_FW_STATUS_SUCCESS) { + wil_err(wil, "WMI_PORT_DELETE returned status %d\n", + reply.evt.status); + return -EINVAL; + } + + return 0; +} + +static bool wmi_evt_call_handler(struct wil6210_vif *vif, int id, void *d, int len) { uint i; for (i = 0; i < ARRAY_SIZE(wmi_evt_handlers); i++) { if (wmi_evt_handlers[i].eventid == id) { - wmi_evt_handlers[i].handler(wil, id, d, len); + wmi_evt_handlers[i].handler(vif, id, d, len); return true; } } @@ -2332,19 +2489,39 @@ static void wmi_event_handle(struct wil6210_priv *wil, struct wil6210_mbox_hdr *hdr) { u16 len = le16_to_cpu(hdr->len); + struct wil6210_vif *vif; if ((hdr->type == WIL_MBOX_HDR_TYPE_WMI) && (len >= sizeof(struct wmi_cmd_hdr))) { struct wmi_cmd_hdr *wmi = (void *)(&hdr[1]); void *evt_data = (void *)(&wmi[1]); u16 id = le16_to_cpu(wmi->command_id); + u8 mid = wmi->mid; + + wil_dbg_wmi(wil, "Handle %s (0x%04x) (reply_id 0x%04x,%d)\n", + eventid2name(id), id, wil->reply_id, + wil->reply_mid); + + if (mid == MID_BROADCAST) + mid = 0; + if (mid >= wil->max_vifs) { + wil_dbg_wmi(wil, "invalid mid %d, event skipped\n", + mid); + return; + } + vif = wil->vifs[mid]; + if (!vif) { + wil_dbg_wmi(wil, "event for empty VIF(%d), skipped\n", + mid); + return; + } - 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) { + if (wil->reply_id && wil->reply_id == id && + wil->reply_mid == mid) { WARN_ON(wil->reply_buf); - wmi_evt_call_handler(wil, id, evt_data, + + wmi_evt_call_handler(vif, id, evt_data, len - sizeof(*wmi)); wil_dbg_wmi(wil, "event_handle: Complete WMI 0x%04x\n", id); @@ -2353,7 +2530,7 @@ static void wmi_event_handle(struct wil6210_priv *wil, } /* unsolicited event */ /* search for handler */ - if (!wmi_evt_call_handler(wil, id, evt_data, + if (!wmi_evt_call_handler(vif, id, evt_data, len - sizeof(*wmi))) { wil_info(wil, "Unhandled event 0x%04x\n", id); } @@ -2523,6 +2700,7 @@ wmi_sched_scan_set_plans(struct wil6210_priv *wil, int wmi_start_sched_scan(struct wil6210_priv *wil, struct cfg80211_sched_scan_request *request) { + struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); int rc; struct wmi_start_sched_scan_cmd cmd = { .min_rssi_threshold = S8_MIN, @@ -2549,7 +2727,8 @@ int wmi_start_sched_scan(struct wil6210_priv *wil, reply.evt.result = WMI_PNO_REJECT; - rc = wmi_call(wil, WMI_START_SCHED_SCAN_CMDID, &cmd, sizeof(cmd), + rc = wmi_call(wil, WMI_START_SCHED_SCAN_CMDID, vif->mid, + &cmd, sizeof(cmd), WMI_START_SCHED_SCAN_EVENTID, &reply, sizeof(reply), WIL_WMI_CALL_GENERAL_TO_MS); if (rc) @@ -2566,6 +2745,7 @@ int wmi_start_sched_scan(struct wil6210_priv *wil, int wmi_stop_sched_scan(struct wil6210_priv *wil) { + struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); int rc; struct { struct wmi_cmd_hdr wmi; @@ -2577,7 +2757,7 @@ int wmi_stop_sched_scan(struct wil6210_priv *wil) reply.evt.result = WMI_PNO_REJECT; - rc = wmi_call(wil, WMI_STOP_SCHED_SCAN_CMDID, NULL, 0, + rc = wmi_call(wil, WMI_STOP_SCHED_SCAN_CMDID, vif->mid, NULL, 0, WMI_STOP_SCHED_SCAN_EVENTID, &reply, sizeof(reply), WIL_WMI_CALL_GENERAL_TO_MS); if (rc)