diff options
author | Kalle Valo <kvalo@kernel.org> | 2021-12-22 20:46:02 +0300 |
---|---|---|
committer | Kalle Valo <kvalo@kernel.org> | 2021-12-22 20:46:02 +0300 |
commit | 68b930ad46b6615a3ef3fb05ac229a7b17df6c9c (patch) | |
tree | 333915041beaf5f5529c760f95ca159486d58211 | |
parent | 8704d0befb59304eed60204e5524d5051de1d171 (diff) | |
parent | 71c748b5e01e3e28838a8e26a8966fb5adb03df7 (diff) | |
download | linux-68b930ad46b6615a3ef3fb05ac229a7b17df6c9c.tar.xz |
Merge ath-next from git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git
ath.git patches for v5.17. Major changes:
ath11k
* qca6390/wcn6855: report signal and tx bitrate
* qca6390: rfkill support
* qca6390/wcn6855: regdb.bin support
ath5k
* switch to rate table based lookup
27 files changed, 1055 insertions, 195 deletions
diff --git a/drivers/net/wireless/ath/ath10k/coredump.c b/drivers/net/wireless/ath/ath10k/coredump.c index 55e7e11d06d9..fe6b6f97a916 100644 --- a/drivers/net/wireless/ath/ath10k/coredump.c +++ b/drivers/net/wireless/ath/ath10k/coredump.c @@ -1522,7 +1522,7 @@ static struct ath10k_dump_file_data *ath10k_coredump_build(struct ath10k *ar) mutex_lock(&ar->dump_mutex); dump_data = (struct ath10k_dump_file_data *)(buf); - strlcpy(dump_data->df_magic, "ATH10K-FW-DUMP", + strscpy(dump_data->df_magic, "ATH10K-FW-DUMP", sizeof(dump_data->df_magic)); dump_data->len = cpu_to_le32(len); @@ -1543,11 +1543,11 @@ static struct ath10k_dump_file_data *ath10k_coredump_build(struct ath10k *ar) dump_data->vht_cap_info = cpu_to_le32(ar->vht_cap_info); dump_data->num_rf_chains = cpu_to_le32(ar->num_rf_chains); - strlcpy(dump_data->fw_ver, ar->hw->wiphy->fw_version, + strscpy(dump_data->fw_ver, ar->hw->wiphy->fw_version, sizeof(dump_data->fw_ver)); dump_data->kernel_ver_code = 0; - strlcpy(dump_data->kernel_ver, init_utsname()->release, + strscpy(dump_data->kernel_ver, init_utsname()->release, sizeof(dump_data->kernel_ver)); dump_data->tv_sec = cpu_to_le64(crash_data->timestamp.tv_sec); diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c index 096c502cce38..3fb0aa000825 100644 --- a/drivers/net/wireless/ath/ath11k/ahb.c +++ b/drivers/net/wireless/ath/ath11k/ahb.c @@ -175,8 +175,11 @@ static void __ath11k_ahb_ext_irq_disable(struct ath11k_base *ab) ath11k_ahb_ext_grp_disable(irq_grp); - napi_synchronize(&irq_grp->napi); - napi_disable(&irq_grp->napi); + if (irq_grp->napi_enabled) { + napi_synchronize(&irq_grp->napi); + napi_disable(&irq_grp->napi); + irq_grp->napi_enabled = false; + } } } @@ -300,7 +303,10 @@ static void ath11k_ahb_ext_irq_enable(struct ath11k_base *ab) for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; - napi_enable(&irq_grp->napi); + if (!irq_grp->napi_enabled) { + napi_enable(&irq_grp->napi); + irq_grp->napi_enabled = true; + } ath11k_ahb_ext_grp_enable(irq_grp); } } diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index 606e867c36ec..293563b3f784 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -52,6 +52,9 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .target_ce_count = 11, .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_ipq8074, .svc_to_ce_map_len = 21, + .rfkill_pin = 0, + .rfkill_cfg = 0, + .rfkill_on_level = 0, .single_pdev_only = false, .rxdma1_enable = true, .num_rxmda_per_pdev = 1, @@ -84,6 +87,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .num_peers = 512, .supports_suspend = false, .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), + .supports_regdb = false, .fix_l1ss = true, .credit_flow = false, .max_tx_ring = DP_TCL_NUM_RING_MAX, @@ -91,6 +95,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .supports_dynamic_smps_6ghz = false, .alloc_cacheable_memory = true, .wakeup_mhi = false, + .supports_rssi_stats = false, + .fw_wmi_diag_event = false, }, { .hw_rev = ATH11K_HW_IPQ6018_HW10, @@ -113,6 +119,9 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .target_ce_count = 11, .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_ipq6018, .svc_to_ce_map_len = 19, + .rfkill_pin = 0, + .rfkill_cfg = 0, + .rfkill_on_level = 0, .single_pdev_only = false, .rxdma1_enable = true, .num_rxmda_per_pdev = 1, @@ -142,6 +151,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .num_peers = 512, .supports_suspend = false, .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), + .supports_regdb = false, .fix_l1ss = true, .credit_flow = false, .max_tx_ring = DP_TCL_NUM_RING_MAX, @@ -149,6 +159,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .supports_dynamic_smps_6ghz = false, .alloc_cacheable_memory = true, .wakeup_mhi = false, + .supports_rssi_stats = false, + .fw_wmi_diag_event = false, }, { .name = "qca6390 hw2.0", @@ -171,6 +183,9 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .target_ce_count = 9, .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_qca6390, .svc_to_ce_map_len = 14, + .rfkill_pin = 48, + .rfkill_cfg = 0, + .rfkill_on_level = 1, .single_pdev_only = true, .rxdma1_enable = false, .num_rxmda_per_pdev = 2, @@ -199,6 +214,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .num_peers = 512, .supports_suspend = true, .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), + .supports_regdb = true, .fix_l1ss = true, .credit_flow = true, .max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390, @@ -206,6 +222,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .supports_dynamic_smps_6ghz = false, .alloc_cacheable_memory = false, .wakeup_mhi = true, + .supports_rssi_stats = true, + .fw_wmi_diag_event = true, }, { .name = "qcn9074 hw1.0", @@ -228,6 +246,9 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .target_ce_count = 9, .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_qcn9074, .svc_to_ce_map_len = 18, + .rfkill_pin = 0, + .rfkill_cfg = 0, + .rfkill_on_level = 0, .rxdma1_enable = true, .num_rxmda_per_pdev = 1, .rx_mac_buf_ring = false, @@ -256,6 +277,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .num_peers = 128, .supports_suspend = false, .hal_desc_sz = sizeof(struct hal_rx_desc_qcn9074), + .supports_regdb = false, .fix_l1ss = true, .credit_flow = false, .max_tx_ring = DP_TCL_NUM_RING_MAX, @@ -263,6 +285,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .supports_dynamic_smps_6ghz = true, .alloc_cacheable_memory = true, .wakeup_mhi = false, + .supports_rssi_stats = false, + .fw_wmi_diag_event = false, }, { .name = "wcn6855 hw2.0", @@ -285,6 +309,9 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .target_ce_count = 9, .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_qca6390, .svc_to_ce_map_len = 14, + .rfkill_pin = 0, + .rfkill_cfg = 0, + .rfkill_on_level = 0, .single_pdev_only = true, .rxdma1_enable = false, .num_rxmda_per_pdev = 2, @@ -313,6 +340,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .num_peers = 512, .supports_suspend = true, .hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855), + .supports_regdb = true, .fix_l1ss = false, .credit_flow = true, .max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390, @@ -320,6 +348,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .supports_dynamic_smps_6ghz = false, .alloc_cacheable_memory = false, .wakeup_mhi = true, + .supports_rssi_stats = true, + .fw_wmi_diag_event = true, }, { .name = "wcn6855 hw2.1", @@ -342,6 +372,9 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .target_ce_count = 9, .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_qca6390, .svc_to_ce_map_len = 14, + .rfkill_pin = 0, + .rfkill_cfg = 0, + .rfkill_on_level = 0, .single_pdev_only = true, .rxdma1_enable = false, .num_rxmda_per_pdev = 2, @@ -369,6 +402,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .num_peers = 512, .supports_suspend = true, .hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855), + .supports_regdb = true, .fix_l1ss = false, .credit_flow = true, .max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390, @@ -376,6 +410,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .supports_dynamic_smps_6ghz = false, .alloc_cacheable_memory = false, .wakeup_mhi = true, + .supports_rssi_stats = true, + .fw_wmi_diag_event = true, }, }; @@ -736,10 +772,12 @@ err: return ret; } -static int ath11k_core_fetch_board_data_api_1(struct ath11k_base *ab, - struct ath11k_board_data *bd) +int ath11k_core_fetch_board_data_api_1(struct ath11k_base *ab, + struct ath11k_board_data *bd, + const char *name) { - bd->fw = ath11k_core_firmware_request(ab, ATH11K_DEFAULT_BOARD_FILE); + bd->fw = ath11k_core_firmware_request(ab, name); + if (IS_ERR(bd->fw)) return PTR_ERR(bd->fw); @@ -767,7 +805,7 @@ int ath11k_core_fetch_bdf(struct ath11k_base *ab, struct ath11k_board_data *bd) goto success; ab->bd_api = 1; - ret = ath11k_core_fetch_board_data_api_1(ab, bd); + ret = ath11k_core_fetch_board_data_api_1(ab, bd, ATH11K_DEFAULT_BOARD_FILE); if (ret) { ath11k_err(ab, "failed to fetch board-2.bin or board.bin from %s\n", ab->hw_params.fw.dir); @@ -779,6 +817,18 @@ success: return 0; } +int ath11k_core_fetch_regdb(struct ath11k_base *ab, struct ath11k_board_data *bd) +{ + int ret; + + ret = ath11k_core_fetch_board_data_api_1(ab, bd, ATH11K_REGDB_FILE_NAME); + if (ret) + ath11k_dbg(ab, ATH11K_DBG_BOOT, "failed to fetch %s from %s\n", + ATH11K_REGDB_FILE_NAME, ab->hw_params.fw.dir); + + return ret; +} + static void ath11k_core_stop(struct ath11k_base *ab) { if (!test_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags)) @@ -1009,6 +1059,27 @@ err_firmware_stop: return ret; } +static int ath11k_core_rfkill_config(struct ath11k_base *ab) +{ + struct ath11k *ar; + int ret = 0, i; + + if (!(ab->target_caps.sys_cap_info & WMI_SYS_CAP_INFO_RFKILL)) + return 0; + + for (i = 0; i < ab->num_radios; i++) { + ar = ab->pdevs[i].ar; + + ret = ath11k_mac_rfkill_config(ar); + if (ret && ret != -EOPNOTSUPP) { + ath11k_warn(ab, "failed to configure rfkill: %d", ret); + return ret; + } + } + + return ret; +} + int ath11k_core_qmi_firmware_ready(struct ath11k_base *ab) { int ret; @@ -1055,6 +1126,13 @@ int ath11k_core_qmi_firmware_ready(struct ath11k_base *ab) goto err_core_stop; } ath11k_hif_irq_enable(ab); + + ret = ath11k_core_rfkill_config(ab); + if (ret && ret != -EOPNOTSUPP) { + ath11k_err(ab, "failed to config rfkill: %d\n", ret); + goto err_core_stop; + } + mutex_unlock(&ab->core_lock); return 0; @@ -1120,6 +1198,7 @@ void ath11k_core_halt(struct ath11k *ar) cancel_delayed_work_sync(&ar->scan.timeout); cancel_work_sync(&ar->regd_update_work); cancel_work_sync(&ab->update_11d_work); + cancel_work_sync(&ab->rfkill_work); rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx], NULL); synchronize_rcu(); @@ -1127,6 +1206,28 @@ void ath11k_core_halt(struct ath11k *ar) idr_init(&ar->txmgmt_idr); } +static void ath11k_rfkill_work(struct work_struct *work) +{ + struct ath11k_base *ab = container_of(work, struct ath11k_base, rfkill_work); + struct ath11k *ar; + bool rfkill_radio_on; + int i; + + spin_lock_bh(&ab->base_lock); + rfkill_radio_on = ab->rfkill_radio_on; + spin_unlock_bh(&ab->base_lock); + + for (i = 0; i < ab->num_radios; i++) { + ar = ab->pdevs[i].ar; + if (!ar) + continue; + + /* notify cfg80211 radio state change */ + ath11k_mac_rfkill_enable_radio(ar, rfkill_radio_on); + wiphy_rfkill_set_hw_state(ar->hw->wiphy, !rfkill_radio_on); + } +} + static void ath11k_update_11d(struct work_struct *work) { struct ath11k_base *ab = container_of(work, struct ath11k_base, update_11d_work); @@ -1333,6 +1434,7 @@ struct ath11k_base *ath11k_core_alloc(struct device *dev, size_t priv_size, init_waitqueue_head(&ab->qmi.cold_boot_waitq); INIT_WORK(&ab->restart_work, ath11k_core_restart); INIT_WORK(&ab->update_11d_work, ath11k_update_11d); + INIT_WORK(&ab->rfkill_work, ath11k_rfkill_work); timer_setup(&ab->rx_replenish_retry, ath11k_ce_rx_replenish_retry, 0); init_completion(&ab->htc_suspend); init_completion(&ab->wow.wakeup_completed); diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h index bab3878a41a7..9e88ccca5ca7 100644 --- a/drivers/net/wireless/ath/ath11k/core.h +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -142,6 +142,7 @@ struct ath11k_ext_irq_grp { u32 num_irq; u32 grp_id; u64 timestamp; + bool napi_enabled; struct napi_struct napi; struct net_device napi_ndev; }; @@ -382,10 +383,13 @@ struct ath11k_sta { struct work_struct update_wk; struct work_struct set_4addr_wk; struct rate_info txrate; + u32 peer_nss; struct rate_info last_txrate; u64 rx_duration; u64 tx_duration; u8 rssi_comb; + s8 rssi_beacon; + s8 chain_signal[IEEE80211_MAX_CHAINS]; struct ath11k_htt_tx_stats *tx_stats; struct ath11k_rx_peer_stats *rx_stats; @@ -416,6 +420,10 @@ enum ath11k_state { /* Antenna noise floor */ #define ATH11K_DEFAULT_NOISE_FLOOR -95 +#define ATH11K_INVALID_RSSI_FULL -1 + +#define ATH11K_INVALID_RSSI_EMPTY -128 + struct ath11k_fw_stats { struct dentry *debugfs_fwstats; u32 pdev_id; @@ -736,7 +744,6 @@ struct ath11k_base { u32 wlan_init_status; int irq_num[ATH11K_IRQ_NUM_MAX]; struct ath11k_ext_irq_grp ext_irq_grp[ATH11K_EXT_IRQ_GRP_NUM_MAX]; - struct napi_struct *napi; struct ath11k_targ_cap target_caps; u32 ext_service_bitmap[WMI_SERVICE_EXT_BM_SIZE]; bool pdevs_macaddr_valid; @@ -779,6 +786,10 @@ struct ath11k_base { struct ath11k_dbring_cap *db_caps; u32 num_db_cap; + struct work_struct rfkill_work; + + /* true means radio is on */ + bool rfkill_radio_on; /* To synchronize 11d scan vdev id */ struct mutex vdev_id_11d_lock; @@ -969,6 +980,10 @@ struct ath11k_base *ath11k_core_alloc(struct device *dev, size_t priv_size, void ath11k_core_free(struct ath11k_base *ath11k); int ath11k_core_fetch_bdf(struct ath11k_base *ath11k, struct ath11k_board_data *bd); +int ath11k_core_fetch_regdb(struct ath11k_base *ab, struct ath11k_board_data *bd); +int ath11k_core_fetch_board_data_api_1(struct ath11k_base *ab, + struct ath11k_board_data *bd, + const char *name); void ath11k_core_free_bdf(struct ath11k_base *ab, struct ath11k_board_data *bd); int ath11k_core_check_dt(struct ath11k_base *ath11k); diff --git a/drivers/net/wireless/ath/ath11k/debugfs.c b/drivers/net/wireless/ath/ath11k/debugfs.c index dba055d085be..ca68cf63dd27 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs.c +++ b/drivers/net/wireless/ath/ath11k/debugfs.c @@ -126,6 +126,11 @@ void ath11k_debugfs_fw_stats_process(struct ath11k_base *ab, struct sk_buff *skb goto complete; } + if (stats.stats_id == WMI_REQUEST_RSSI_PER_CHAIN_STAT) { + ar->debug.fw_stats_done = true; + goto complete; + } + if (stats.stats_id == WMI_REQUEST_VDEV_STAT) { if (list_empty(&stats.vdevs)) { ath11k_warn(ab, "empty vdev stats"); @@ -229,6 +234,38 @@ static int ath11k_debugfs_fw_stats_request(struct ath11k *ar, return 0; } +int ath11k_debugfs_get_fw_stats(struct ath11k *ar, u32 pdev_id, + u32 vdev_id, u32 stats_id) +{ + struct ath11k_base *ab = ar->ab; + struct stats_request_params req_param; + int ret; + + mutex_lock(&ar->conf_mutex); + + if (ar->state != ATH11K_STATE_ON) { + ret = -ENETDOWN; + goto err_unlock; + } + + req_param.pdev_id = pdev_id; + req_param.vdev_id = vdev_id; + req_param.stats_id = stats_id; + + ret = ath11k_debugfs_fw_stats_request(ar, &req_param); + if (ret) + ath11k_warn(ab, "failed to request fw stats: %d\n", ret); + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "debug get fw stat pdev id %d vdev id %d stats id 0x%x\n", + pdev_id, vdev_id, stats_id); + +err_unlock: + mutex_unlock(&ar->conf_mutex); + + return ret; +} + static int ath11k_open_pdev_stats(struct inode *inode, struct file *file) { struct ath11k *ar = inode->i_private; diff --git a/drivers/net/wireless/ath/ath11k/debugfs.h b/drivers/net/wireless/ath/ath11k/debugfs.h index ec743a015dc7..4c0740394c95 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs.h +++ b/drivers/net/wireless/ath/ath11k/debugfs.h @@ -117,6 +117,8 @@ void ath11k_debugfs_unregister(struct ath11k *ar); void ath11k_debugfs_fw_stats_process(struct ath11k_base *ab, struct sk_buff *skb); void ath11k_debugfs_fw_stats_init(struct ath11k *ar); +int ath11k_debugfs_get_fw_stats(struct ath11k *ar, u32 pdev_id, + u32 vdev_id, u32 stats_id); static inline bool ath11k_debugfs_is_pktlog_lite_mode_enabled(struct ath11k *ar) { @@ -216,6 +218,12 @@ static inline int ath11k_debugfs_rx_filter(struct ath11k *ar) return 0; } +static inline int ath11k_debugfs_get_fw_stats(struct ath11k *ar, + u32 pdev_id, u32 vdev_id, u32 stats_id) +{ + return 0; +} + #endif /* CONFIG_MAC80211_DEBUGFS*/ #endif /* _ATH11K_DEBUGFS_H_ */ diff --git a/drivers/net/wireless/ath/ath11k/debugfs_sta.c b/drivers/net/wireless/ath/ath11k/debugfs_sta.c index fecd9718f5ce..1b1acbdf837a 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs_sta.c +++ b/drivers/net/wireless/ath/ath11k/debugfs_sta.c @@ -126,85 +126,9 @@ void ath11k_debugfs_sta_add_tx_stats(struct ath11k_sta *arsta, } void ath11k_debugfs_sta_update_txcompl(struct ath11k *ar, - struct sk_buff *msdu, struct hal_tx_status *ts) { - struct ath11k_base *ab = ar->ab; - struct ath11k_per_peer_tx_stats *peer_stats = &ar->cached_stats; - enum hal_tx_rate_stats_pkt_type pkt_type; - enum hal_tx_rate_stats_sgi sgi; - enum hal_tx_rate_stats_bw bw; - struct ath11k_peer *peer; - struct ath11k_sta *arsta; - struct ieee80211_sta *sta; - u16 rate; - u8 rate_idx = 0; - int ret; - u8 mcs; - - rcu_read_lock(); - spin_lock_bh(&ab->base_lock); - peer = ath11k_peer_find_by_id(ab, ts->peer_id); - if (!peer || !peer->sta) { - ath11k_warn(ab, "failed to find the peer\n"); - spin_unlock_bh(&ab->base_lock); - rcu_read_unlock(); - return; - } - - sta = peer->sta; - arsta = (struct ath11k_sta *)sta->drv_priv; - - memset(&arsta->txrate, 0, sizeof(arsta->txrate)); - pkt_type = FIELD_GET(HAL_TX_RATE_STATS_INFO0_PKT_TYPE, - ts->rate_stats); - mcs = FIELD_GET(HAL_TX_RATE_STATS_INFO0_MCS, - ts->rate_stats); - sgi = FIELD_GET(HAL_TX_RATE_STATS_INFO0_SGI, - ts->rate_stats); - bw = FIELD_GET(HAL_TX_RATE_STATS_INFO0_BW, ts->rate_stats); - - if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11A || - pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11B) { - ret = ath11k_mac_hw_ratecode_to_legacy_rate(mcs, - pkt_type, - &rate_idx, - &rate); - if (ret < 0) - goto err_out; - arsta->txrate.legacy = rate; - } else if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11N) { - if (mcs > 7) { - ath11k_warn(ab, "Invalid HT mcs index %d\n", mcs); - goto err_out; - } - - arsta->txrate.mcs = mcs + 8 * (arsta->last_txrate.nss - 1); - arsta->txrate.flags = RATE_INFO_FLAGS_MCS; - if (sgi) - arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; - } else if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11AC) { - if (mcs > 9) { - ath11k_warn(ab, "Invalid VHT mcs index %d\n", mcs); - goto err_out; - } - - arsta->txrate.mcs = mcs; - arsta->txrate.flags = RATE_INFO_FLAGS_VHT_MCS; - if (sgi) - arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; - } else if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11AX) { - /* TODO */ - } - - arsta->txrate.nss = arsta->last_txrate.nss; - arsta->txrate.bw = ath11k_mac_bw_to_mac80211_bw(bw); - - ath11k_debugfs_sta_add_tx_stats(arsta, peer_stats, rate_idx); - -err_out: - spin_unlock_bh(&ab->base_lock); - rcu_read_unlock(); + ath11k_dp_tx_update_txcompl(ar, ts); } static ssize_t ath11k_dbg_sta_dump_tx_stats(struct file *file, diff --git a/drivers/net/wireless/ath/ath11k/debugfs_sta.h b/drivers/net/wireless/ath/ath11k/debugfs_sta.h index 18dc65d9edcf..e6c11b3a40aa 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs_sta.h +++ b/drivers/net/wireless/ath/ath11k/debugfs_sta.h @@ -19,7 +19,6 @@ void ath11k_debugfs_sta_add_tx_stats(struct ath11k_sta *arsta, struct ath11k_per_peer_tx_stats *peer_stats, u8 legacy_rate_idx); void ath11k_debugfs_sta_update_txcompl(struct ath11k *ar, - struct sk_buff *msdu, struct hal_tx_status *ts); #else /* CONFIG_ATH11K_DEBUGFS */ @@ -34,7 +33,6 @@ ath11k_debugfs_sta_add_tx_stats(struct ath11k_sta *arsta, } static inline void ath11k_debugfs_sta_update_txcompl(struct ath11k *ar, - struct sk_buff *msdu, struct hal_tx_status *ts) { } diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 38eeb6e7c6b6..c212a789421e 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -1360,25 +1360,6 @@ int ath11k_dp_htt_tlv_iter(struct ath11k_base *ab, const void *ptr, size_t len, return 0; } -static inline u32 ath11k_he_gi_to_nl80211_he_gi(u8 sgi) -{ - u32 ret = 0; - - switch (sgi) { - case RX_MSDU_START_SGI_0_8_US: - ret = NL80211_RATE_INFO_HE_GI_0_8; - break; - case RX_MSDU_START_SGI_1_6_US: - ret = NL80211_RATE_INFO_HE_GI_1_6; - break; - case RX_MSDU_START_SGI_3_2_US: - ret = NL80211_RATE_INFO_HE_GI_3_2; - break; - } - - return ret; -} - static void ath11k_update_per_peer_tx_stats(struct ath11k *ar, struct htt_ppdu_stats *ppdu_stats, u8 user) @@ -1497,14 +1478,15 @@ ath11k_update_per_peer_tx_stats(struct ath11k *ar, arsta->txrate.mcs = mcs; arsta->txrate.flags = RATE_INFO_FLAGS_HE_MCS; arsta->txrate.he_dcm = dcm; - arsta->txrate.he_gi = ath11k_he_gi_to_nl80211_he_gi(sgi); - arsta->txrate.he_ru_alloc = ath11k_he_ru_tones_to_nl80211_he_ru_alloc( - (user_rate->ru_end - + arsta->txrate.he_gi = ath11k_mac_he_gi_to_nl80211_he_gi(sgi); + arsta->txrate.he_ru_alloc = ath11k_mac_phy_he_ru_to_nl80211_he_ru_alloc + ((user_rate->ru_end - user_rate->ru_start) + 1); break; } arsta->txrate.nss = nss; + arsta->txrate.bw = ath11k_mac_bw_to_mac80211_bw(bw); arsta->tx_duration += tx_duration; memcpy(&arsta->last_txrate, &arsta->txrate, sizeof(struct rate_info)); @@ -2384,7 +2366,7 @@ static void ath11k_dp_rx_h_rate(struct ath11k *ar, struct hal_rx_desc *rx_desc, } rx_status->encoding = RX_ENC_HE; rx_status->nss = nss; - rx_status->he_gi = ath11k_he_gi_to_nl80211_he_gi(sgi); + rx_status->he_gi = ath11k_mac_he_gi_to_nl80211_he_gi(sgi); rx_status->bw = ath11k_mac_bw_to_mac80211_bw(bw); break; } @@ -2768,6 +2750,7 @@ static void ath11k_dp_rx_update_peer_stats(struct ath11k_sta *arsta, { struct ath11k_rx_peer_stats *rx_stats = arsta->rx_stats; u32 num_msdu; + int i; if (!rx_stats) return; @@ -2829,6 +2812,13 @@ static void ath11k_dp_rx_update_peer_stats(struct ath11k_sta *arsta, rx_stats->ru_alloc_cnt[ppdu_info->ru_alloc] += num_msdu; arsta->rssi_comb = ppdu_info->rssi_comb; + + BUILD_BUG_ON(ARRAY_SIZE(arsta->chain_signal) > + ARRAY_SIZE(ppdu_info->rssi_chain_pri20)); + + for (i = 0; i < ARRAY_SIZE(arsta->chain_signal); i++) + arsta->chain_signal[i] = ppdu_info->rssi_chain_pri20[i]; + rx_stats->rx_duration += ppdu_info->rx_duration; arsta->rx_duration = rx_stats->rx_duration; } @@ -3847,7 +3837,7 @@ int ath11k_dp_process_rx_err(struct ath11k_base *ab, struct napi_struct *napi, ath11k_hal_rx_msdu_link_info_get(link_desc_va, &num_msdus, msdu_cookies, &rbm); if (rbm != HAL_RX_BUF_RBM_WBM_IDLE_DESC_LIST && - rbm != ab->hw_params.hal_params->rx_buf_rbm) { + rbm != HAL_RX_BUF_RBM_SW3_BM) { ab->soc_stats.invalid_rbm++; ath11k_warn(ab, "invalid return buffer manager %d\n", rbm); ath11k_dp_rx_link_desc_return(ab, desc, diff --git a/drivers/net/wireless/ath/ath11k/dp_tx.c b/drivers/net/wireless/ath/ath11k/dp_tx.c index 8560f3b33812..91d6244b6543 100644 --- a/drivers/net/wireless/ath/ath11k/dp_tx.c +++ b/drivers/net/wireless/ath/ath11k/dp_tx.c @@ -415,6 +415,105 @@ static void ath11k_dp_tx_cache_peer_stats(struct ath11k *ar, } } +void ath11k_dp_tx_update_txcompl(struct ath11k *ar, struct hal_tx_status *ts) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_per_peer_tx_stats *peer_stats = &ar->cached_stats; + enum hal_tx_rate_stats_pkt_type pkt_type; + enum hal_tx_rate_stats_sgi sgi; + enum hal_tx_rate_stats_bw bw; + struct ath11k_peer *peer; + struct ath11k_sta *arsta; + struct ieee80211_sta *sta; + u16 rate, ru_tones; + u8 mcs, rate_idx, ofdma; + int ret; + + spin_lock_bh(&ab->base_lock); + peer = ath11k_peer_find_by_id(ab, ts->peer_id); + if (!peer || !peer->sta) { + ath11k_dbg(ab, ATH11K_DBG_DP_TX, + "failed to find the peer by id %u\n", ts->peer_id); + goto err_out; + } + + sta = peer->sta; + arsta = (struct ath11k_sta *)sta->drv_priv; + + memset(&arsta->txrate, 0, sizeof(arsta->txrate)); + pkt_type = FIELD_GET(HAL_TX_RATE_STATS_INFO0_PKT_TYPE, + ts->rate_stats); + mcs = FIELD_GET(HAL_TX_RATE_STATS_INFO0_MCS, + ts->rate_stats); + sgi = FIELD_GET(HAL_TX_RATE_STATS_INFO0_SGI, + ts->rate_stats); + bw = FIELD_GET(HAL_TX_RATE_STATS_INFO0_BW, ts->rate_stats); + ru_tones = FIELD_GET(HAL_TX_RATE_STATS_INFO0_TONES_IN_RU, ts->rate_stats); + ofdma = FIELD_GET(HAL_TX_RATE_STATS_INFO0_OFDMA_TX, ts->rate_stats); + + /* This is to prefer choose the real NSS value arsta->last_txrate.nss, + * if it is invalid, then choose the NSS value while assoc. + */ + if (arsta->last_txrate.nss) + arsta->txrate.nss = arsta->last_txrate.nss; + else + arsta->txrate.nss = arsta->peer_nss; + + if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11A || + pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11B) { + ret = ath11k_mac_hw_ratecode_to_legacy_rate(mcs, + pkt_type, + &rate_idx, + &rate); + if (ret < 0) + goto err_out; + arsta->txrate.legacy = rate; + } else if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11N) { + if (mcs > 7) { + ath11k_warn(ab, "Invalid HT mcs index %d\n", mcs); + goto err_out; + } + + if (arsta->txrate.nss != 0) + arsta->txrate.mcs = mcs + 8 * (arsta->txrate.nss - 1); + arsta->txrate.flags = RATE_INFO_FLAGS_MCS; + if (sgi) + arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + } else if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11AC) { + if (mcs > 9) { + ath11k_warn(ab, "Invalid VHT mcs index %d\n", mcs); + goto err_out; + } + + arsta->txrate.mcs = mcs; + arsta->txrate.flags = RATE_INFO_FLAGS_VHT_MCS; + if (sgi) + arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + } else if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11AX) { + if (mcs > 11) { + ath11k_warn(ab, "Invalid HE mcs index %d\n", mcs); + goto err_out; + } + + arsta->txrate.mcs = mcs; + arsta->txrate.flags = RATE_INFO_FLAGS_HE_MCS; + arsta->txrate.he_gi = ath11k_mac_he_gi_to_nl80211_he_gi(sgi); + } + + arsta->txrate.bw = ath11k_mac_bw_to_mac80211_bw(bw); + if (ofdma && pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11AX) { + arsta->txrate.bw = RATE_INFO_BW_HE_RU; + arsta->txrate.he_ru_alloc = + ath11k_mac_he_ru_tones_to_nl80211_he_ru_alloc(ru_tones); + } + + if (ath11k_debugfs_is_extd_tx_stats_enabled(ar)) + ath11k_debugfs_sta_add_tx_stats(arsta, peer_stats, rate_idx); + +err_out: + spin_unlock_bh(&ab->base_lock); +} + static void ath11k_dp_tx_complete_msdu(struct ath11k *ar, struct sk_buff *msdu, struct hal_tx_status *ts) @@ -460,7 +559,8 @@ static void ath11k_dp_tx_complete_msdu(struct ath11k *ar, (info->flags & IEEE80211_TX_CTL_NO_ACK)) info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; - if (unlikely(ath11k_debugfs_is_extd_tx_stats_enabled(ar))) { + if (unlikely(ath11k_debugfs_is_extd_tx_stats_enabled(ar)) || + ab->hw_params.single_pdev_only) { if (ts->flags & HAL_TX_STATUS_FLAGS_FIRST_MSDU) { if (ar->last_ppdu_id == 0) { ar->last_ppdu_id = ts->ppdu_id; @@ -468,12 +568,12 @@ static void ath11k_dp_tx_complete_msdu(struct ath11k *ar, ar->cached_ppdu_id == ar->last_ppdu_id) { ar->cached_ppdu_id = ar->last_ppdu_id; ar->cached_stats.is_ampdu = true; - ath11k_debugfs_sta_update_txcompl(ar, msdu, ts); + ath11k_dp_tx_update_txcompl(ar, ts); memset(&ar->cached_stats, 0, sizeof(struct ath11k_per_peer_tx_stats)); } else { ar->cached_stats.is_ampdu = false; - ath11k_debugfs_sta_update_txcompl(ar, msdu, ts); + ath11k_dp_tx_update_txcompl(ar, ts); memset(&ar->cached_stats, 0, sizeof(struct ath11k_per_peer_tx_stats)); } diff --git a/drivers/net/wireless/ath/ath11k/dp_tx.h b/drivers/net/wireless/ath/ath11k/dp_tx.h index e373dbccf417..e87d65bfbf06 100644 --- a/drivers/net/wireless/ath/ath11k/dp_tx.h +++ b/drivers/net/wireless/ath/ath11k/dp_tx.h @@ -15,6 +15,7 @@ struct ath11k_dp_htt_wbm_tx_status { int ack_rssi; }; +void ath11k_dp_tx_update_txcompl(struct ath11k *ar, struct hal_tx_status *ts); int ath11k_dp_tx_htt_h2t_ver_req_msg(struct ath11k_base *ab); int ath11k_dp_tx(struct ath11k *ar, struct ath11k_vif *arvif, struct ath11k_sta *arsta, struct sk_buff *skb); diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.c b/drivers/net/wireless/ath/ath11k/hal_rx.c index 1e279e99baa8..a3b353a4b5f7 100644 --- a/drivers/net/wireless/ath/ath11k/hal_rx.c +++ b/drivers/net/wireless/ath/ath11k/hal_rx.c @@ -371,7 +371,7 @@ int ath11k_hal_wbm_desc_parse_err(struct ath11k_base *ab, void *desc, ret_buf_mgr = FIELD_GET(BUFFER_ADDR_INFO1_RET_BUF_MGR, wbm_desc->buf_addr_info.info1); - if (ret_buf_mgr != ab->hw_params.hal_params->rx_buf_rbm) { + if (ret_buf_mgr != HAL_RX_BUF_RBM_SW3_BM) { ab->soc_stats.invalid_rbm++; return -EINVAL; } @@ -1038,7 +1038,9 @@ ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab, ru_tones = FIELD_GET(HAL_RX_HE_SIG_B1_MU_INFO_INFO0_RU_ALLOCATION, info0); - ppdu_info->ru_alloc = ath11k_he_ru_tones_to_nl80211_he_ru_alloc(ru_tones); + ppdu_info->ru_alloc = + ath11k_mac_phy_he_ru_to_nl80211_he_ru_alloc(ru_tones); + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_MIMO; break; } @@ -1079,6 +1081,9 @@ ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab, break; } case HAL_PHYRX_RSSI_LEGACY: { + int i; + bool db2dbm = test_bit(WMI_TLV_SERVICE_HW_DB2DBM_CONVERSION_SUPPORT, + ab->wmi_ab.svc_map); struct hal_rx_phyrx_rssi_legacy_info *rssi = (struct hal_rx_phyrx_rssi_legacy_info *)tlv_data; @@ -1089,6 +1094,14 @@ ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab, ppdu_info->rssi_comb = FIELD_GET(HAL_RX_PHYRX_RSSI_LEGACY_INFO_INFO1_RSSI_COMB, __le32_to_cpu(rssi->info0)); + + if (db2dbm) { + for (i = 0; i < ARRAY_SIZE(rssi->preamble); i++) { + ppdu_info->rssi_chain_pri20[i] = + le32_get_bits(rssi->preamble[i].rssi_2040, + HAL_RX_PHYRX_RSSI_PREAMBLE_PRI20); + } + } break; } case HAL_RX_MPDU_START: { diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.h b/drivers/net/wireless/ath/ath11k/hal_rx.h index 8db420ef6351..571054c6d7f8 100644 --- a/drivers/net/wireless/ath/ath11k/hal_rx.h +++ b/drivers/net/wireless/ath/ath11k/hal_rx.h @@ -112,6 +112,7 @@ struct hal_rx_mon_ppdu_info { u8 ldpc; u8 beamformed; u8 rssi_comb; + u8 rssi_chain_pri20[HAL_RX_MAX_NSS]; u8 tid; u8 dcm; u8 ru_alloc; @@ -262,8 +263,17 @@ struct hal_rx_he_sig_b2_ofdma_info { #define HAL_RX_PHYRX_RSSI_LEGACY_INFO_INFO1_RSSI_COMB GENMASK(15, 8) +#define HAL_RX_PHYRX_RSSI_PREAMBLE_PRI20 GENMASK(7, 0) + +struct hal_rx_phyrx_chain_rssi { + __le32 rssi_2040; + __le32 rssi_80; +} __packed; + struct hal_rx_phyrx_rssi_legacy_info { - __le32 rsvd[35]; + __le32 rsvd[3]; + struct hal_rx_phyrx_chain_rssi pre_rssi[HAL_RX_MAX_NSS]; + struct hal_rx_phyrx_chain_rssi preamble[HAL_RX_MAX_NSS]; __le32 info0; } __packed; @@ -353,33 +363,6 @@ ath11k_hal_rx_parse_mon_status(struct ath11k_base *ab, struct hal_rx_mon_ppdu_info *ppdu_info, struct sk_buff *skb); -static inline u32 ath11k_he_ru_tones_to_nl80211_he_ru_alloc(u16 ru_tones) -{ - u32 ret = 0; - - switch (ru_tones) { - case RU_26: - ret = NL80211_RATE_INFO_HE_RU_ALLOC_26; - break; - case RU_52: - ret = NL80211_RATE_INFO_HE_RU_ALLOC_52; - break; - case RU_106: - ret = NL80211_RATE_INFO_HE_RU_ALLOC_106; - break; - case RU_242: - ret = NL80211_RATE_INFO_HE_RU_ALLOC_242; - break; - case RU_484: - ret = NL80211_RATE_INFO_HE_RU_ALLOC_484; - break; - case RU_996: - ret = NL80211_RATE_INFO_HE_RU_ALLOC_996; - break; - } - return ret; -} - #define REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_0 0xDDBEEF #define REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_1 0xADBEEF #define REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_2 0xBDBEEF diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h index 23f3ce741636..29934b36c14e 100644 --- a/drivers/net/wireless/ath/ath11k/hw.h +++ b/drivers/net/wireless/ath/ath11k/hw.h @@ -77,6 +77,7 @@ #define ATH11K_DEFAULT_CAL_FILE "caldata.bin" #define ATH11K_AMSS_FILE "amss.bin" #define ATH11K_M3_FILE "m3.bin" +#define ATH11K_REGDB_FILE_NAME "regdb.bin" enum ath11k_hw_rate_cck { ATH11K_HW_RATE_CCK_LP_11M = 0, @@ -151,6 +152,9 @@ struct ath11k_hw_params { u32 svc_to_ce_map_len; bool single_pdev_only; + u32 rfkill_pin; + u32 rfkill_cfg; + u32 rfkill_on_level; bool rxdma1_enable; int num_rxmda_per_pdev; @@ -178,6 +182,7 @@ struct ath11k_hw_params { u32 num_peers; bool supports_suspend; u32 hal_desc_sz; + bool supports_regdb; bool fix_l1ss; bool credit_flow; u8 max_tx_ring; @@ -185,6 +190,8 @@ struct ath11k_hw_params { bool supports_dynamic_smps_6ghz; bool alloc_cacheable_memory; bool wakeup_mhi; + bool supports_rssi_stats; + bool fw_wmi_diag_event; }; struct ath11k_hw_ops { diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 2bc93456b34e..07f499d5ec92 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -246,6 +246,93 @@ static const u32 ath11k_smps_map[] = { static int ath11k_start_vdev_delay(struct ieee80211_hw *hw, struct ieee80211_vif *vif); +enum nl80211_he_ru_alloc ath11k_mac_phy_he_ru_to_nl80211_he_ru_alloc(u16 ru_phy) +{ + enum nl80211_he_ru_alloc ret; + + switch (ru_phy) { + case RU_26: + ret = NL80211_RATE_INFO_HE_RU_ALLOC_26; + break; + case RU_52: + ret = NL80211_RATE_INFO_HE_RU_ALLOC_52; + break; + case RU_106: + ret = NL80211_RATE_INFO_HE_RU_ALLOC_106; + break; + case RU_242: + ret = NL80211_RATE_INFO_HE_RU_ALLOC_242; + break; + case RU_484: + ret = NL80211_RATE_INFO_HE_RU_ALLOC_484; + break; + case RU_996: + ret = NL80211_RATE_INFO_HE_RU_ALLOC_996; + break; + default: + ret = NL80211_RATE_INFO_HE_RU_ALLOC_26; + break; + } + + return ret; +} + +enum nl80211_he_ru_alloc ath11k_mac_he_ru_tones_to_nl80211_he_ru_alloc(u16 ru_tones) +{ + enum nl80211_he_ru_alloc ret; + + switch (ru_tones) { + case 26: + ret = NL80211_RATE_INFO_HE_RU_ALLOC_26; + break; + case 52: + ret = NL80211_RATE_INFO_HE_RU_ALLOC_52; + break; + case 106: + ret = NL80211_RATE_INFO_HE_RU_ALLOC_106; + break; + case 242: + ret = NL80211_RATE_INFO_HE_RU_ALLOC_242; + break; + case 484: + ret = NL80211_RATE_INFO_HE_RU_ALLOC_484; + break; + case 996: + ret = NL80211_RATE_INFO_HE_RU_ALLOC_996; + break; + case (996 * 2): + ret = NL80211_RATE_INFO_HE_RU_ALLOC_2x996; + break; + default: + ret = NL80211_RATE_INFO_HE_RU_ALLOC_26; + break; + } + + return ret; +} + +enum nl80211_he_gi ath11k_mac_he_gi_to_nl80211_he_gi(u8 sgi) +{ + enum nl80211_he_gi ret; + + switch (sgi) { + case RX_MSDU_START_SGI_0_8_US: + ret = NL80211_RATE_INFO_HE_GI_0_8; + break; + case RX_MSDU_START_SGI_1_6_US: + ret = NL80211_RATE_INFO_HE_GI_1_6; + break; + case RX_MSDU_START_SGI_3_2_US: + ret = NL80211_RATE_INFO_HE_GI_3_2; + break; + default: + ret = NL80211_RATE_INFO_HE_GI_0_8; + break; + } + + return ret; +} + u8 ath11k_mac_bw_to_mac80211_bw(u8 bw) { u8 ret = 0; @@ -2541,8 +2628,12 @@ static void ath11k_peer_assoc_prepare(struct ath11k *ar, struct peer_assoc_params *arg, bool reassoc) { + struct ath11k_sta *arsta; + lockdep_assert_held(&ar->conf_mutex); + arsta = (struct ath11k_sta *)sta->drv_priv; + memset(arg, 0, sizeof(*arg)); reinit_completion(&ar->peer_assoc_done); @@ -2559,6 +2650,8 @@ static void ath11k_peer_assoc_prepare(struct ath11k *ar, ath11k_peer_assoc_h_qos(ar, vif, sta, arg); ath11k_peer_assoc_h_smps(sta, arg); + arsta->peer_nss = arg->peer_nss; + /* TODO: amsdu_disable req? */ } @@ -5478,6 +5571,63 @@ static int ath11k_mac_mgmt_tx(struct ath11k *ar, struct sk_buff *skb, return 0; } +int ath11k_mac_rfkill_config(struct ath11k *ar) +{ + struct ath11k_base *ab = ar->ab; + u32 param; + int ret; + + if (ab->hw_params.rfkill_pin == 0) + return -EOPNOTSUPP; + + ath11k_dbg(ab, ATH11K_DBG_MAC, + "mac rfkill_pin %d rfkill_cfg %d rfkill_on_level %d", + ab->hw_params.rfkill_pin, ab->hw_params.rfkill_cfg, + ab->hw_params.rfkill_on_level); + + param = FIELD_PREP(WMI_RFKILL_CFG_RADIO_LEVEL, + ab->hw_params.rfkill_on_level) | + FIELD_PREP(WMI_RFKILL_CFG_GPIO_PIN_NUM, + ab->hw_params.rfkill_pin) | + FIELD_PREP(WMI_RFKILL_CFG_PIN_AS_GPIO, + ab->hw_params.rfkill_cfg); + + ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_HW_RFKILL_CONFIG, + param, ar->pdev->pdev_id); + if (ret) { + ath11k_warn(ab, + "failed to set rfkill config 0x%x: %d\n", + param, ret); + return ret; + } + + return 0; +} + +int ath11k_mac_rfkill_enable_radio(struct ath11k *ar, bool enable) +{ + enum wmi_rfkill_enable_radio param; + int ret; + + if (enable) + param = WMI_RFKILL_ENABLE_RADIO_ON; + else + param = WMI_RFKILL_ENABLE_RADIO_OFF; + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac %d rfkill enable %d", + ar->pdev_idx, param); + + ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_RFKILL_ENABLE, + param, ar->pdev->pdev_id); + if (ret) { + ath11k_warn(ar->ab, "failed to set rfkill enable param %d: %d\n", + param, ret); + return ret; + } + + return 0; +} + static void ath11k_mac_op_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb) @@ -5710,6 +5860,7 @@ static void ath11k_mac_op_stop(struct ieee80211_hw *hw) cancel_delayed_work_sync(&ar->scan.timeout); cancel_work_sync(&ar->regd_update_work); cancel_work_sync(&ar->ab->update_11d_work); + cancel_work_sync(&ar->ab->rfkill_work); spin_lock_bh(&ar->data_lock); list_for_each_entry_safe(ppdu_stats, tmp, &ar->ppdu_stats_info, list) { @@ -7775,12 +7926,45 @@ exit: return ret; } +static void ath11k_mac_put_chain_rssi(struct station_info *sinfo, + struct ath11k_sta *arsta, + char *pre, + bool clear) +{ + struct ath11k *ar = arsta->arvif->ar; + int i; + s8 rssi; + + for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) { + sinfo->chains &= ~BIT(i); + rssi = arsta->chain_signal[i]; + if (clear) + arsta->chain_signal[i] = ATH11K_INVALID_RSSI_FULL; + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, + "mac sta statistics %s rssi[%d] %d\n", pre, i, rssi); + + if (rssi != ATH11K_DEFAULT_NOISE_FLOOR && + rssi != ATH11K_INVALID_RSSI_FULL && + rssi != ATH11K_INVALID_RSSI_EMPTY && + rssi != 0) { + sinfo->chain_signal[i] = rssi; + sinfo->chains |= BIT(i); + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL); + } + } +} + static void ath11k_mac_op_sta_statistics(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct station_info *sinfo) { struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; + struct ath11k *ar = arsta->arvif->ar; + s8 signal; + bool db2dbm = test_bit(WMI_TLV_SERVICE_HW_DB2DBM_CONVERSION_SUPPORT, + ar->ab->wmi_ab.svc_map); sinfo->rx_duration = arsta->rx_duration; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_DURATION); @@ -7803,9 +7987,32 @@ static void ath11k_mac_op_sta_statistics(struct ieee80211_hw *hw, sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); } - /* TODO: Use real NF instead of default one. */ - sinfo->signal = arsta->rssi_comb + ATH11K_DEFAULT_NOISE_FLOOR; - sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); + ath11k_mac_put_chain_rssi(sinfo, arsta, "ppdu", false); + + if (!(sinfo->filled & BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL)) && + arsta->arvif->vdev_type == WMI_VDEV_TYPE_STA && + ar->ab->hw_params.supports_rssi_stats && + !ath11k_debugfs_get_fw_stats(ar, ar->pdev->pdev_id, 0, + WMI_REQUEST_RSSI_PER_CHAIN_STAT)) { + ath11k_mac_put_chain_rssi(sinfo, arsta, "fw stats", true); + } + + signal = arsta->rssi_comb; + if (!signal && + arsta->arvif->vdev_type == WMI_VDEV_TYPE_STA && + ar->ab->hw_params.supports_rssi_stats && + !(ath11k_debugfs_get_fw_stats(ar, ar->pdev->pdev_id, 0, + WMI_REQUEST_VDEV_STAT))) + signal = arsta->rssi_beacon; + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, + "mac sta statistics db2dbm %u rssi comb %d rssi beacon %d\n", + db2dbm, arsta->rssi_comb, arsta->rssi_beacon); + + if (signal) { + sinfo->signal = db2dbm ? signal : signal + ATH11K_DEFAULT_NOISE_FLOOR; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); + } } static const struct ieee80211_ops ath11k_ops = { diff --git a/drivers/net/wireless/ath/ath11k/mac.h b/drivers/net/wireless/ath/ath11k/mac.h index 035d6738c20b..0e6c870b09c8 100644 --- a/drivers/net/wireless/ath/ath11k/mac.h +++ b/drivers/net/wireless/ath/ath11k/mac.h @@ -147,6 +147,8 @@ u8 ath11k_mac_hw_rate_to_idx(const struct ieee80211_supported_band *sband, void __ath11k_mac_scan_finish(struct ath11k *ar); void ath11k_mac_scan_finish(struct ath11k *ar); +int ath11k_mac_rfkill_enable_radio(struct ath11k *ar, bool enable); +int ath11k_mac_rfkill_config(struct ath11k *ar); struct ath11k_vif *ath11k_mac_get_arvif(struct ath11k *ar, u32 vdev_id); struct ath11k_vif *ath11k_mac_get_arvif_by_vdev_id(struct ath11k_base *ab, @@ -162,6 +164,9 @@ void ath11k_mac_drain_tx(struct ath11k *ar); void ath11k_mac_peer_cleanup_all(struct ath11k *ar); int ath11k_mac_tx_mgmt_pending_free(int buf_id, void *skb, void *ctx); u8 ath11k_mac_bw_to_mac80211_bw(u8 bw); +u32 ath11k_mac_he_gi_to_nl80211_he_gi(u8 sgi); +enum nl80211_he_ru_alloc ath11k_mac_phy_he_ru_to_nl80211_he_ru_alloc(u16 ru_phy); +enum nl80211_he_ru_alloc ath11k_mac_he_ru_tones_to_nl80211_he_ru_alloc(u16 ru_tones); enum ath11k_supported_bw ath11k_mac_mac80211_bw_to_ath11k_bw(enum rate_info_bw bw); enum hal_encrypt_type ath11k_dp_tx_get_encrypt_type(u32 cipher); void ath11k_mac_handle_beacon(struct ath11k *ar, struct sk_buff *skb); diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c index 8c09d61138a1..d73b522a0081 100644 --- a/drivers/net/wireless/ath/ath11k/pci.c +++ b/drivers/net/wireless/ath/ath11k/pci.c @@ -683,8 +683,11 @@ static void __ath11k_pci_ext_irq_disable(struct ath11k_base *sc) ath11k_pci_ext_grp_disable(irq_grp); - napi_synchronize(&irq_grp->napi); - napi_disable(&irq_grp->napi); + if (irq_grp->napi_enabled) { + napi_synchronize(&irq_grp->napi); + napi_disable(&irq_grp->napi); + irq_grp->napi_enabled = false; + } } } @@ -712,7 +715,10 @@ static void ath11k_pci_ext_irq_enable(struct ath11k_base *ab) for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; - napi_enable(&irq_grp->napi); + if (!irq_grp->napi_enabled) { + napi_enable(&irq_grp->napi); + irq_grp->napi_enabled = true; + } ath11k_pci_ext_grp_enable(irq_grp); } } diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c index 3b9ba0e03a66..65d3c6ba35ae 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.c +++ b/drivers/net/wireless/ath/ath11k/qmi.c @@ -1584,6 +1584,50 @@ static struct qmi_elem_info qmi_wlanfw_cold_boot_cal_done_ind_msg_v01_ei[] = { }, }; +static struct qmi_elem_info qmi_wlanfw_wlan_ini_req_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_wlan_ini_req_msg_v01, + enablefwlog_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_wlan_ini_req_msg_v01, + enablefwlog), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_wlan_ini_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_wlan_ini_resp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + static int ath11k_qmi_host_cap_send(struct ath11k_base *ab) { struct qmi_wlanfw_host_cap_req_msg_v01 req; @@ -2161,7 +2205,8 @@ err_free_req: return ret; } -static int ath11k_qmi_load_bdf_qmi(struct ath11k_base *ab) +static int ath11k_qmi_load_bdf_qmi(struct ath11k_base *ab, + bool regdb) { struct device *dev = ab->dev; char filename[ATH11K_QMI_MAX_BDF_FILE_NAME_SIZE]; @@ -2172,13 +2217,21 @@ static int ath11k_qmi_load_bdf_qmi(struct ath11k_base *ab) const u8 *tmp; memset(&bd, 0, sizeof(bd)); - ret = ath11k_core_fetch_bdf(ab, &bd); - if (ret) { - ath11k_warn(ab, "qmi failed to fetch board file: %d\n", ret); - goto out; + + if (regdb) { + ret = ath11k_core_fetch_regdb(ab, &bd); + } else { + ret = ath11k_core_fetch_bdf(ab, &bd); + if (ret) + ath11k_warn(ab, "qmi failed to fetch board file: %d\n", ret); } - if (bd.len >= SELFMAG && memcmp(bd.data, ELFMAG, SELFMAG) == 0) + if (ret) + goto out; + + if (regdb) + bdf_type = ATH11K_QMI_BDF_TYPE_REGDB; + else if (bd.len >= SELFMAG && memcmp(bd.data, ELFMAG, SELFMAG) == 0) bdf_type = ATH11K_QMI_BDF_TYPE_ELF; else bdf_type = ATH11K_QMI_BDF_TYPE_BIN; @@ -2193,8 +2246,8 @@ static int ath11k_qmi_load_bdf_qmi(struct ath11k_base *ab) goto out; } - /* QCA6390 does not support cal data, skip it */ - if (bdf_type == ATH11K_QMI_BDF_TYPE_ELF) + /* QCA6390/WCN6855 does not support cal data, skip it */ + if (bdf_type == ATH11K_QMI_BDF_TYPE_ELF || bdf_type == ATH11K_QMI_BDF_TYPE_REGDB) goto out; if (ab->qmi.target.eeprom_caldata) { @@ -2495,6 +2548,48 @@ out: return ret; } +static int ath11k_qmi_wlanfw_wlan_ini_send(struct ath11k_base *ab, bool enable) +{ + int ret; + struct qmi_txn txn; + struct qmi_wlanfw_wlan_ini_req_msg_v01 req = {}; + struct qmi_wlanfw_wlan_ini_resp_msg_v01 resp = {}; + + req.enablefwlog_valid = true; + req.enablefwlog = enable ? 1 : 0; + + ret = qmi_txn_init(&ab->qmi.handle, &txn, + qmi_wlanfw_wlan_ini_resp_msg_v01_ei, &resp); + if (ret < 0) + goto out; + + ret = qmi_send_request(&ab->qmi.handle, NULL, &txn, + QMI_WLANFW_WLAN_INI_REQ_V01, + QMI_WLANFW_WLAN_INI_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_wlan_ini_req_msg_v01_ei, &req); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to send wlan ini request, err = %d\n", + ret); + qmi_txn_cancel(&txn); + goto out; + } + + ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH11K_QMI_WLANFW_TIMEOUT_MS)); + if (ret < 0) { + ath11k_warn(ab, "qmi failed wlan ini request, err = %d\n", ret); + goto out; + } + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + ath11k_warn(ab, "qmi wlan ini request failed, result: %d, err: %d\n", + resp.resp.result, resp.resp.error); + ret = -EINVAL; + } + +out: + return ret; +} + void ath11k_qmi_firmware_stop(struct ath11k_base *ab) { int ret; @@ -2515,6 +2610,14 @@ int ath11k_qmi_firmware_start(struct ath11k_base *ab, ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi firmware start\n"); + if (ab->hw_params.fw_wmi_diag_event) { + ret = ath11k_qmi_wlanfw_wlan_ini_send(ab, true); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to send wlan fw ini:%d\n", ret); + return ret; + } + } + ret = ath11k_qmi_wlanfw_wlan_cfg_send(ab); if (ret < 0) { ath11k_warn(ab, "qmi failed to send wlan cfg: %d\n", ret); @@ -2626,7 +2729,10 @@ static int ath11k_qmi_event_load_bdf(struct ath11k_qmi *qmi) return ret; } - ret = ath11k_qmi_load_bdf_qmi(ab); + if (ab->hw_params.supports_regdb) + ath11k_qmi_load_bdf_qmi(ab, true); + + ret = ath11k_qmi_load_bdf_qmi(ab, false); if (ret < 0) { ath11k_warn(ab, "failed to load board data file: %d\n", ret); return ret; diff --git a/drivers/net/wireless/ath/ath11k/qmi.h b/drivers/net/wireless/ath/ath11k/qmi.h index a6fb7d47ee82..ba2eff4d59cb 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.h +++ b/drivers/net/wireless/ath/ath11k/qmi.h @@ -48,6 +48,7 @@ enum ath11k_qmi_file_type { enum ath11k_qmi_bdf_type { ATH11K_QMI_BDF_TYPE_BIN = 0, ATH11K_QMI_BDF_TYPE_ELF = 1, + ATH11K_QMI_BDF_TYPE_REGDB = 4, }; enum ath11k_qmi_event_type { @@ -427,10 +428,12 @@ struct qmi_wlanfw_m3_info_resp_msg_v01 { #define QMI_WLANFW_WLAN_MODE_RESP_MSG_V01_MAX_LEN 7 #define QMI_WLANFW_WLAN_CFG_REQ_MSG_V01_MAX_LEN 803 #define QMI_WLANFW_WLAN_CFG_RESP_MSG_V01_MAX_LEN 7 +#define QMI_WLANFW_WLAN_INI_REQ_MSG_V01_MAX_LEN 4 #define QMI_WLANFW_WLAN_MODE_REQ_V01 0x0022 #define QMI_WLANFW_WLAN_MODE_RESP_V01 0x0022 #define QMI_WLANFW_WLAN_CFG_REQ_V01 0x0023 #define QMI_WLANFW_WLAN_CFG_RESP_V01 0x0023 +#define QMI_WLANFW_WLAN_INI_REQ_V01 0x002F #define QMI_WLANFW_MAX_STR_LEN_V01 16 #define QMI_WLANFW_MAX_NUM_CE_V01 12 #define QMI_WLANFW_MAX_NUM_SVC_V01 24 @@ -472,6 +475,16 @@ struct qmi_wlanfw_wlan_cfg_resp_msg_v01 { struct qmi_response_type_v01 resp; }; +struct qmi_wlanfw_wlan_ini_req_msg_v01 { + /* Must be set to true if enablefwlog is being passed */ + u8 enablefwlog_valid; + u8 enablefwlog; +}; + +struct qmi_wlanfw_wlan_ini_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + int ath11k_qmi_firmware_start(struct ath11k_base *ab, u32 mode); void ath11k_qmi_firmware_stop(struct ath11k_base *ab); diff --git a/drivers/net/wireless/ath/ath11k/trace.h b/drivers/net/wireless/ath/ath11k/trace.h index 02003dc4207d..a02e54735e88 100644 --- a/drivers/net/wireless/ath/ath11k/trace.h +++ b/drivers/net/wireless/ath/ath11k/trace.h @@ -280,6 +280,34 @@ TRACE_EVENT(ath11k_log_dbg_dump, __get_str(msg) ) ); + +TRACE_EVENT(ath11k_wmi_diag, + TP_PROTO(struct ath11k_base *ab, const void *data, size_t len), + + TP_ARGS(ab, data, len), + + TP_STRUCT__entry( + __string(device, dev_name(ab->dev)) + __string(driver, dev_driver_string(ab->dev)) + __field(u16, len) + __dynamic_array(u8, data, len) + ), + + TP_fast_assign( + __assign_str(device, dev_name(ab->dev)); + __assign_str(driver, dev_driver_string(ab->dev)); + __entry->len = len; + memcpy(__get_dynamic_array(data), data, len); + ), + + TP_printk( + "%s %s tlv diag len %d", + __get_str(driver), + __get_str(device), + __entry->len + ) +); + #endif /* _TRACE_H_ || TRACE_HEADER_MULTI_READ*/ /* we don't want to use include/trace/events */ diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 2b4d27d807ab..6b68ccf65e39 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -73,6 +73,14 @@ struct wmi_tlv_dma_buf_release_parse { bool meta_data_done; }; +struct wmi_tlv_fw_stats_parse { + const struct wmi_stats_event *ev; + const struct wmi_per_chain_rssi_stats *rssi; + struct ath11k_fw_stats *stats; + int rssi_num; + bool chain_rssi_done; +}; + static const struct wmi_tlv_policy wmi_tlv_policies[] = { [WMI_TAG_ARRAY_BYTE] = { .min_len = 0 }, @@ -120,6 +128,8 @@ static const struct wmi_tlv_policy wmi_tlv_policies[] = { = { .min_len = sizeof(struct wmi_peer_assoc_conf_event) }, [WMI_TAG_STATS_EVENT] = { .min_len = sizeof(struct wmi_stats_event) }, + [WMI_TAG_RFKILL_EVENT] = { + .min_len = sizeof(struct wmi_rfkill_state_change_ev) }, [WMI_TAG_PDEV_CTL_FAILSAFE_CHECK_EVENT] = { .min_len = sizeof(struct wmi_pdev_ctl_failsafe_chk_event) }, [WMI_TAG_HOST_SWFDA_EVENT] = { @@ -132,6 +142,8 @@ static const struct wmi_tlv_policy wmi_tlv_policies[] = { .min_len = sizeof(struct wmi_obss_color_collision_event) }, [WMI_TAG_11D_NEW_COUNTRY_EVENT] = { .min_len = sizeof(struct wmi_11d_new_cc_ev) }, + [WMI_TAG_PER_CHAIN_RSSI_STATS] = { + .min_len = sizeof(struct wmi_per_chain_rssi_stats) }, }; #define PRIMAP(_hw_mode_) \ @@ -514,6 +526,8 @@ static int ath11k_pull_service_ready_tlv(struct ath11k_base *ab, cap->default_dbs_hw_mode_index = ev->default_dbs_hw_mode_index; cap->num_msdu_desc = ev->num_msdu_desc; + ath11k_dbg(ab, ATH11K_DBG_WMI, "wmi sys cap info 0x%x\n", cap->sys_cap_info); + return 0; } @@ -3595,6 +3609,8 @@ ath11k_wmi_obss_color_collision_event(struct ath11k_base *ab, struct sk_buff *sk return; } + rcu_read_lock(); + ev = tb[WMI_TAG_OBSS_COLOR_COLLISION_EVT]; if (!ev) { ath11k_warn(ab, "failed to fetch obss color collision ev"); @@ -3625,6 +3641,7 @@ ath11k_wmi_obss_color_collision_event(struct ath11k_base *ab, struct sk_buff *sk exit: kfree(tb); + rcu_read_unlock(); } static void @@ -5554,47 +5571,107 @@ ath11k_wmi_pull_bcn_stats(const struct wmi_bcn_stats *src, dst->tx_bcn_outage_cnt = src->tx_bcn_outage_cnt; } -int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb, - struct ath11k_fw_stats *stats) +static int ath11k_wmi_tlv_rssi_chain_parse(struct ath11k_base *ab, + u16 tag, u16 len, + const void *ptr, void *data) { - const void **tb; - const struct wmi_stats_event *ev; - const void *data; - int i, ret; - u32 len = skb->len; + struct wmi_tlv_fw_stats_parse *parse = data; + const struct wmi_stats_event *ev = parse->ev; + struct ath11k_fw_stats *stats = parse->stats; + struct ath11k *ar; + struct ath11k_vif *arvif; + struct ieee80211_sta *sta; + struct ath11k_sta *arsta; + const struct wmi_rssi_stats *stats_rssi = (const struct wmi_rssi_stats *)ptr; + int j, ret = 0; - tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, len, GFP_ATOMIC); - if (IS_ERR(tb)) { - ret = PTR_ERR(tb); - ath11k_warn(ab, "failed to parse tlv: %d\n", ret); - return ret; + if (tag != WMI_TAG_RSSI_STATS) + return -EPROTO; + + rcu_read_lock(); + + ar = ath11k_mac_get_ar_by_pdev_id(ab, ev->pdev_id); + stats->stats_id = WMI_REQUEST_RSSI_PER_CHAIN_STAT; + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "wmi stats vdev id %d mac %pM\n", + stats_rssi->vdev_id, stats_rssi->peer_macaddr.addr); + + arvif = ath11k_mac_get_arvif(ar, stats_rssi->vdev_id); + if (!arvif) { + ath11k_warn(ab, "not found vif for vdev id %d\n", + stats_rssi->vdev_id); + ret = -EPROTO; + goto exit; } - ev = tb[WMI_TAG_STATS_EVENT]; - data = tb[WMI_TAG_ARRAY_BYTE]; - if (!ev || !data) { + ath11k_dbg(ab, ATH11K_DBG_WMI, + "wmi stats bssid %pM vif %pK\n", + arvif->bssid, arvif->vif); + + sta = ieee80211_find_sta_by_ifaddr(ar->hw, + arvif->bssid, + NULL); + if (!sta) { + ath11k_warn(ab, "not found station for bssid %pM\n", + arvif->bssid); + ret = -EPROTO; + goto exit; + } + + arsta = (struct ath11k_sta *)sta->drv_priv; + + BUILD_BUG_ON(ARRAY_SIZE(arsta->chain_signal) > + ARRAY_SIZE(stats_rssi->rssi_avg_beacon)); + + for (j = 0; j < ARRAY_SIZE(arsta->chain_signal); j++) { + arsta->chain_signal[j] = stats_rssi->rssi_avg_beacon[j]; + ath11k_dbg(ab, ATH11K_DBG_WMI, + "wmi stats beacon rssi[%d] %d data rssi[%d] %d\n", + j, + stats_rssi->rssi_avg_beacon[j], + j, + stats_rssi->rssi_avg_data[j]); + } + +exit: + rcu_read_unlock(); + return ret; +} + +static int ath11k_wmi_tlv_fw_stats_data_parse(struct ath11k_base *ab, + struct wmi_tlv_fw_stats_parse *parse, + const void *ptr, + u16 len) +{ + struct ath11k_fw_stats *stats = parse->stats; + const struct wmi_stats_event *ev = parse->ev; + struct ath11k *ar; + struct ath11k_vif *arvif; + struct ieee80211_sta *sta; + struct ath11k_sta *arsta; + int i, ret = 0; + const void *data = ptr; + + if (!ev) { ath11k_warn(ab, "failed to fetch update stats ev"); - kfree(tb); return -EPROTO; } - ath11k_dbg(ab, ATH11K_DBG_WMI, - "wmi stats update ev pdev_id %d pdev %i vdev %i bcn %i\n", - ev->pdev_id, - ev->num_pdev_stats, ev->num_vdev_stats, - ev->num_bcn_stats); - - stats->pdev_id = ev->pdev_id; stats->stats_id = 0; + rcu_read_lock(); + + ar = ath11k_mac_get_ar_by_pdev_id(ab, ev->pdev_id); + for (i = 0; i < ev->num_pdev_stats; i++) { const struct wmi_pdev_stats *src; struct ath11k_fw_stats_pdev *dst; src = data; if (len < sizeof(*src)) { - kfree(tb); - return -EPROTO; + ret = -EPROTO; + goto exit; } stats->stats_id = WMI_REQUEST_PDEV_STAT; @@ -5618,12 +5695,29 @@ int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb, src = data; if (len < sizeof(*src)) { - kfree(tb); - return -EPROTO; + ret = -EPROTO; + goto exit; } stats->stats_id = WMI_REQUEST_VDEV_STAT; + arvif = ath11k_mac_get_arvif(ar, src->vdev_id); + if (arvif) { + sta = ieee80211_find_sta_by_ifaddr(ar->hw, + arvif->bssid, + NULL); + if (sta) { + arsta = (struct ath11k_sta *)sta->drv_priv; + arsta->rssi_beacon = src->beacon_snr; + ath11k_dbg(ab, ATH11K_DBG_WMI, + "wmi stats vdev id %d snr %d\n", + src->vdev_id, src->beacon_snr); + } else { + ath11k_warn(ab, "not found station for bssid %pM\n", + arvif->bssid); + } + } + data += sizeof(*src); len -= sizeof(*src); @@ -5641,8 +5735,8 @@ int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb, src = data; if (len < sizeof(*src)) { - kfree(tb); - return -EPROTO; + ret = -EPROTO; + goto exit; } stats->stats_id = WMI_REQUEST_BCN_STAT; @@ -5658,8 +5752,67 @@ int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb, list_add_tail(&dst->list, &stats->bcn); } - kfree(tb); - return 0; +exit: + rcu_read_unlock(); + return ret; +} + +static int ath11k_wmi_tlv_fw_stats_parse(struct ath11k_base *ab, + u16 tag, u16 len, + const void *ptr, void *data) +{ + struct wmi_tlv_fw_stats_parse *parse = data; + int ret = 0; + + switch (tag) { + case WMI_TAG_STATS_EVENT: + parse->ev = (struct wmi_stats_event *)ptr; + parse->stats->pdev_id = parse->ev->pdev_id; + break; + case WMI_TAG_ARRAY_BYTE: + ret = ath11k_wmi_tlv_fw_stats_data_parse(ab, parse, ptr, len); + break; + case WMI_TAG_PER_CHAIN_RSSI_STATS: + parse->rssi = (struct wmi_per_chain_rssi_stats *)ptr; + + if (parse->ev->stats_id & WMI_REQUEST_RSSI_PER_CHAIN_STAT) + parse->rssi_num = parse->rssi->num_per_chain_rssi_stats; + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "wmi stats id 0x%x num chain %d\n", + parse->ev->stats_id, + parse->rssi_num); + break; + case WMI_TAG_ARRAY_STRUCT: + if (parse->rssi_num && !parse->chain_rssi_done) { + ret = ath11k_wmi_tlv_iter(ab, ptr, len, + ath11k_wmi_tlv_rssi_chain_parse, + parse); + if (ret) { + ath11k_warn(ab, "failed to parse rssi chain %d\n", + ret); + return ret; + } + parse->chain_rssi_done = true; + } + break; + default: + break; + } + return ret; +} + +int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb, + struct ath11k_fw_stats *stats) +{ + struct wmi_tlv_fw_stats_parse parse = { }; + + stats->stats_id = 0; + parse.stats = stats; + + return ath11k_wmi_tlv_iter(ab, skb->data, skb->len, + ath11k_wmi_tlv_fw_stats_parse, + &parse); } size_t ath11k_wmi_fw_stats_num_vdevs(struct list_head *head) @@ -6395,13 +6548,16 @@ static void ath11k_bcn_tx_status_event(struct ath11k_base *ab, struct sk_buff *s return; } + rcu_read_lock(); arvif = ath11k_mac_get_arvif_by_vdev_id(ab, vdev_id); if (!arvif) { ath11k_warn(ab, "invalid vdev id %d in bcn_tx_status", vdev_id); + rcu_read_unlock(); return; } ath11k_mac_bcn_tx_event(arvif); + rcu_read_unlock(); } static void ath11k_vdev_stopped_event(struct ath11k_base *ab, struct sk_buff *skb) @@ -7182,6 +7338,40 @@ exit: kfree(tb); } +static void ath11k_rfkill_state_change_event(struct ath11k_base *ab, + struct sk_buff *skb) +{ + const struct wmi_rfkill_state_change_ev *ev; + const void **tb; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath11k_warn(ab, "failed to parse tlv: %d\n", ret); + return; + } + + ev = tb[WMI_TAG_RFKILL_EVENT]; + if (!ev) { + kfree(tb); + return; + } + + ath11k_dbg(ab, ATH11K_DBG_MAC, + "wmi tlv rfkill state change gpio %d type %d radio_state %d\n", + ev->gpio_pin_num, + ev->int_type, + ev->radio_state); + + spin_lock_bh(&ab->base_lock); + ab->rfkill_radio_on = (ev->radio_state == WMI_RFKILL_RADIO_STATE_ON); + spin_unlock_bh(&ab->base_lock); + + queue_work(ab->workqueue, &ab->rfkill_work); + kfree(tb); +} + static void ath11k_wmi_pdev_temperature_event(struct ath11k_base *ab, struct sk_buff *skb) @@ -7335,6 +7525,13 @@ static void ath11k_wmi_event_wow_wakeup_host(struct ath11k_base *ab, struct sk_b complete(&ab->wow.wakeup_completed); } +static void +ath11k_wmi_diag_event(struct ath11k_base *ab, + struct sk_buff *skb) +{ + trace_ath11k_wmi_diag(ab, skb->data, skb->len); +} + static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb) { struct wmi_cmd_hdr *cmd_hdr; @@ -7454,6 +7651,12 @@ static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb) case WMI_11D_NEW_COUNTRY_EVENTID: ath11k_reg_11d_new_cc_event(ab, skb); break; + case WMI_RFKILL_STATE_CHANGE_EVENTID: + ath11k_rfkill_state_change_event(ab, skb); + break; + case WMI_DIAG_EVENTID: + ath11k_wmi_diag_event(ab, skb); + break; /* TODO: Add remaining events */ default: ath11k_dbg(ab, ATH11K_DBG_WMI, "Unknown eventid: 0x%x\n", id); diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index d79eb1b7d9c2..2f26ec1a8aa3 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -4439,6 +4439,17 @@ struct wmi_stats_event { u32 num_peer_extd2_stats; } __packed; +struct wmi_rssi_stats { + u32 vdev_id; + u32 rssi_avg_beacon[WMI_MAX_CHAINS]; + u32 rssi_avg_data[WMI_MAX_CHAINS]; + struct wmi_mac_addr peer_macaddr; +} __packed; + +struct wmi_per_chain_rssi_stats { + u32 num_per_chain_rssi_stats; +} __packed; + struct wmi_pdev_ctl_failsafe_chk_event { u32 pdev_id; u32 ctl_failsafe_status; @@ -5204,6 +5215,31 @@ struct target_resource_config { u32 twt_ap_sta_count; }; +enum wmi_sys_cap_info_flags { + WMI_SYS_CAP_INFO_RXTX_LED = BIT(0), + WMI_SYS_CAP_INFO_RFKILL = BIT(1), +}; + +#define WMI_RFKILL_CFG_GPIO_PIN_NUM GENMASK(5, 0) +#define WMI_RFKILL_CFG_RADIO_LEVEL BIT(6) +#define WMI_RFKILL_CFG_PIN_AS_GPIO GENMASK(10, 7) + +enum wmi_rfkill_enable_radio { + WMI_RFKILL_ENABLE_RADIO_ON = 0, + WMI_RFKILL_ENABLE_RADIO_OFF = 1, +}; + +enum wmi_rfkill_radio_state { + WMI_RFKILL_RADIO_STATE_OFF = 1, + WMI_RFKILL_RADIO_STATE_ON = 2, +}; + +struct wmi_rfkill_state_change_ev { + u32 gpio_pin_num; + u32 int_type; + u32 radio_state; +} __packed; + #define WMI_MAX_MEM_REQS 32 #define MAX_RADIOS 3 diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index cef17f33c69e..66d123f48085 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -727,6 +727,43 @@ ath5k_get_rate_hw_value(const struct ieee80211_hw *hw, return hw_rate; } +static bool ath5k_merge_ratetbl(struct ieee80211_sta *sta, + struct ath5k_buf *bf, + struct ieee80211_tx_info *tx_info) +{ + struct ieee80211_sta_rates *ratetbl; + u8 i; + + if (!sta) + return false; + + ratetbl = rcu_dereference(sta->rates); + if (!ratetbl) + return false; + + if (tx_info->control.rates[0].idx < 0 || + tx_info->control.rates[0].count == 0) + { + i = 0; + } else { + bf->rates[0] = tx_info->control.rates[0]; + i = 1; + } + + for ( ; i < IEEE80211_TX_MAX_RATES; i++) { + bf->rates[i].idx = ratetbl->rate[i].idx; + bf->rates[i].flags = ratetbl->rate[i].flags; + if (tx_info->control.use_rts) + bf->rates[i].count = ratetbl->rate[i].count_rts; + else if (tx_info->control.use_cts_prot) + bf->rates[i].count = ratetbl->rate[i].count_cts; + else + bf->rates[i].count = ratetbl->rate[i].count; + } + + return true; +} + static int ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf, struct ath5k_txq *txq, int padsize, @@ -737,6 +774,7 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); unsigned int pktlen, flags, keyidx = AR5K_TXKEYIX_INVALID; struct ieee80211_rate *rate; + struct ieee80211_sta *sta; unsigned int mrr_rate[3], mrr_tries[3]; int i, ret; u16 hw_rate; @@ -753,8 +791,16 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf, if (dma_mapping_error(ah->dev, bf->skbaddr)) return -ENOSPC; - ieee80211_get_tx_rates(info->control.vif, (control) ? control->sta : NULL, skb, bf->rates, - ARRAY_SIZE(bf->rates)); + if (control) + sta = control->sta; + else + sta = NULL; + + if (!ath5k_merge_ratetbl(sta, bf, info)) { + ieee80211_get_tx_rates(info->control.vif, + sta, skb, bf->rates, + ARRAY_SIZE(bf->rates)); + } rate = ath5k_get_rate(ah->hw, info, bf, 0); diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index 860da13bfb6a..f06eec99de68 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -590,6 +590,13 @@ static void ath9k_hif_usb_rx_stream(struct hif_device_usb *hif_dev, return; } + if (pkt_len > 2 * MAX_RX_BUF_SIZE) { + dev_err(&hif_dev->udev->dev, + "ath9k_htc: invalid pkt_len (%x)\n", pkt_len); + RX_STAT_INC(skb_dropped); + return; + } + pad_len = 4 - (pkt_len & 0x3); if (pad_len == 4) pad_len = 0; diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h index 0a1634238e67..6b45e63fae4b 100644 --- a/drivers/net/wireless/ath/ath9k/htc.h +++ b/drivers/net/wireless/ath/ath9k/htc.h @@ -281,6 +281,7 @@ struct ath9k_htc_rxbuf { struct ath9k_htc_rx { struct list_head rxbuf; spinlock_t rxbuflock; + bool initialized; }; #define ATH9K_HTC_TX_CLEANUP_INTERVAL 50 /* ms */ @@ -305,6 +306,7 @@ struct ath9k_htc_tx { DECLARE_BITMAP(tx_slot, MAX_TX_BUF_NUM); struct timer_list cleanup_timer; spinlock_t tx_lock; + bool initialized; }; struct ath9k_htc_tx_ctl { diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c index 8e69e8989f6d..6a850a0bfa8a 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c @@ -813,6 +813,11 @@ int ath9k_tx_init(struct ath9k_htc_priv *priv) skb_queue_head_init(&priv->tx.data_vi_queue); skb_queue_head_init(&priv->tx.data_vo_queue); skb_queue_head_init(&priv->tx.tx_failed); + + /* Allow ath9k_wmi_event_tasklet(WMI_TXSTATUS_EVENTID) to operate. */ + smp_wmb(); + priv->tx.initialized = true; + return 0; } @@ -1130,6 +1135,10 @@ void ath9k_htc_rxep(void *drv_priv, struct sk_buff *skb, struct ath9k_htc_rxbuf *rxbuf = NULL, *tmp_buf = NULL; unsigned long flags; + /* Check if ath9k_rx_init() completed. */ + if (!data_race(priv->rx.initialized)) + goto err; + spin_lock_irqsave(&priv->rx.rxbuflock, flags); list_for_each_entry(tmp_buf, &priv->rx.rxbuf, list) { if (!tmp_buf->in_process) { @@ -1185,6 +1194,10 @@ int ath9k_rx_init(struct ath9k_htc_priv *priv) list_add_tail(&rxbuf->list, &priv->rx.rxbuf); } + /* Allow ath9k_htc_rxep() to operate. */ + smp_wmb(); + priv->rx.initialized = true; + return 0; err: diff --git a/drivers/net/wireless/ath/ath9k/wmi.c b/drivers/net/wireless/ath/ath9k/wmi.c index fe29ad4b9023..f315c54bd3ac 100644 --- a/drivers/net/wireless/ath/ath9k/wmi.c +++ b/drivers/net/wireless/ath/ath9k/wmi.c @@ -169,6 +169,10 @@ void ath9k_wmi_event_tasklet(struct tasklet_struct *t) &wmi->drv_priv->fatal_work); break; case WMI_TXSTATUS_EVENTID: + /* Check if ath9k_tx_init() completed. */ + if (!data_race(priv->tx.initialized)) + break; + spin_lock_bh(&priv->tx.tx_lock); if (priv->tx.flags & ATH9K_HTC_OP_TX_DRAIN) { spin_unlock_bh(&priv->tx.tx_lock); |