summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/ath/ath12k/wmi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/ath/ath12k/wmi.c')
-rw-r--r--drivers/net/wireless/ath/ath12k/wmi.c161
1 files changed, 144 insertions, 17 deletions
diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
index da85c28ec355..ff6b3d4ea820 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.c
+++ b/drivers/net/wireless/ath/ath12k/wmi.c
@@ -30,6 +30,9 @@ struct ath12k_wmi_svc_ready_parse {
struct wmi_tlv_fw_stats_parse {
const struct wmi_stats_event *ev;
struct ath12k_fw_stats *stats;
+ const struct wmi_per_chain_rssi_stat_params *rssi;
+ int rssi_num;
+ bool chain_rssi_done;
};
struct ath12k_wmi_dma_ring_caps_parse {
@@ -185,6 +188,8 @@ static const struct ath12k_wmi_tlv_policy ath12k_wmi_tlv_policies[] = {
.min_len = sizeof(struct wmi_p2p_noa_event) },
[WMI_TAG_11D_NEW_COUNTRY_EVENT] = {
.min_len = sizeof(struct wmi_11d_new_cc_event) },
+ [WMI_TAG_PER_CHAIN_RSSI_STATS] = {
+ .min_len = sizeof(struct wmi_per_chain_rssi_stat_params) },
};
__le32 ath12k_wmi_tlv_hdr(u32 cmd, u32 len)
@@ -843,7 +848,7 @@ int ath12k_wmi_mgmt_send(struct ath12k_link_vif *arvif, u32 buf_id,
cmd->tx_params_valid = 0;
frame_tlv = (struct wmi_tlv *)(skb->data + sizeof(*cmd));
- frame_tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_BYTE, buf_len);
+ frame_tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_BYTE, buf_len_aligned);
memcpy(frame_tlv->value, frame->data, buf_len);
@@ -2423,6 +2428,7 @@ int ath12k_wmi_send_peer_assoc_cmd(struct ath12k *ar,
eml_cap = arg->ml.eml_cap;
if (u16_get_bits(eml_cap, IEEE80211_EML_CAP_EMLSR_SUPP)) {
+ ml_params->flags |= cpu_to_le32(ATH12K_WMI_FLAG_MLO_EMLSR_SUPPORT);
/* Padding delay */
eml_pad_delay = ieee80211_emlsr_pad_delay_in_us(eml_cap);
ml_params->emlsr_padding_delay_us = cpu_to_le32(eml_pad_delay);
@@ -6461,6 +6467,8 @@ static int ath12k_pull_peer_sta_kickout_ev(struct ath12k_base *ab, struct sk_buf
}
arg->mac_addr = ev->peer_macaddr.addr;
+ arg->reason = le32_to_cpu(ev->reason);
+ arg->rssi = le32_to_cpu(ev->rssi);
kfree(tb);
return 0;
@@ -7297,8 +7305,10 @@ static void ath12k_scan_event(struct ath12k_base *ab, struct sk_buff *skb)
static void ath12k_peer_sta_kickout_event(struct ath12k_base *ab, struct sk_buff *skb)
{
struct wmi_peer_sta_kickout_arg arg = {};
+ struct ath12k_link_vif *arvif;
struct ieee80211_sta *sta;
struct ath12k_peer *peer;
+ unsigned int link_id;
struct ath12k *ar;
if (ath12k_pull_peer_sta_kickout_ev(ab, skb, &arg) != 0) {
@@ -7318,25 +7328,49 @@ static void ath12k_peer_sta_kickout_event(struct ath12k_base *ab, struct sk_buff
goto exit;
}
- ar = ath12k_mac_get_ar_by_vdev_id(ab, peer->vdev_id);
- if (!ar) {
+ arvif = ath12k_mac_get_arvif_by_vdev_id(ab, peer->vdev_id);
+ if (!arvif) {
ath12k_warn(ab, "invalid vdev id in peer sta kickout ev %d",
peer->vdev_id);
goto exit;
}
- sta = ieee80211_find_sta_by_ifaddr(ath12k_ar_to_hw(ar),
- arg.mac_addr, NULL);
+ ar = arvif->ar;
+
+ if (peer->mlo) {
+ sta = ieee80211_find_sta_by_link_addrs(ath12k_ar_to_hw(ar),
+ arg.mac_addr,
+ NULL, &link_id);
+ if (peer->link_id != link_id) {
+ ath12k_warn(ab,
+ "Spurious quick kickout for MLO STA %pM with invalid link_id, peer: %d, sta: %d\n",
+ arg.mac_addr, peer->link_id, link_id);
+ goto exit;
+ }
+ } else {
+ sta = ieee80211_find_sta_by_ifaddr(ath12k_ar_to_hw(ar),
+ arg.mac_addr, NULL);
+ }
if (!sta) {
- ath12k_warn(ab, "Spurious quick kickout for STA %pM\n",
- arg.mac_addr);
+ ath12k_warn(ab, "Spurious quick kickout for %sSTA %pM\n",
+ peer->mlo ? "MLO " : "", arg.mac_addr);
goto exit;
}
- ath12k_dbg(ab, ATH12K_DBG_WMI, "peer sta kickout event %pM",
- arg.mac_addr);
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
+ "peer sta kickout event %pM reason: %d rssi: %d\n",
+ arg.mac_addr, arg.reason, arg.rssi);
- ieee80211_report_low_ack(sta, 10);
+ switch (arg.reason) {
+ case WMI_PEER_STA_KICKOUT_REASON_INACTIVITY:
+ if (arvif->ahvif->vif->type == NL80211_IFTYPE_STATION) {
+ ath12k_mac_handle_beacon_miss(ar, arvif);
+ break;
+ }
+ fallthrough;
+ default:
+ ieee80211_report_low_ack(sta, 10);
+ }
exit:
spin_unlock_bh(&ab->base_lock);
@@ -7345,6 +7379,7 @@ exit:
static void ath12k_roam_event(struct ath12k_base *ab, struct sk_buff *skb)
{
+ struct ath12k_link_vif *arvif;
struct wmi_roam_event roam_ev = {};
struct ath12k *ar;
u32 vdev_id;
@@ -7363,21 +7398,22 @@ static void ath12k_roam_event(struct ath12k_base *ab, struct sk_buff *skb)
"wmi roam event vdev %u reason %d rssi %d\n",
vdev_id, roam_reason, roam_ev.rssi);
- rcu_read_lock();
- ar = ath12k_mac_get_ar_by_vdev_id(ab, vdev_id);
- if (!ar) {
+ guard(rcu)();
+ arvif = ath12k_mac_get_arvif_by_vdev_id(ab, vdev_id);
+ if (!arvif) {
ath12k_warn(ab, "invalid vdev id in roam ev %d", vdev_id);
- rcu_read_unlock();
return;
}
+ ar = arvif->ar;
+
if (roam_reason >= WMI_ROAM_REASON_MAX)
ath12k_warn(ab, "ignoring unknown roam event reason %d on vdev %i\n",
roam_reason, vdev_id);
switch (roam_reason) {
case WMI_ROAM_REASON_BEACON_MISS:
- ath12k_mac_handle_beacon_miss(ar, vdev_id);
+ ath12k_mac_handle_beacon_miss(ar, arvif);
break;
case WMI_ROAM_REASON_BETTER_AP:
case WMI_ROAM_REASON_LOW_RSSI:
@@ -7387,8 +7423,6 @@ static void ath12k_roam_event(struct ath12k_base *ab, struct sk_buff *skb)
roam_reason, vdev_id);
break;
}
-
- rcu_read_unlock();
}
static void ath12k_chan_info_event(struct ath12k_base *ab, struct sk_buff *skb)
@@ -8218,6 +8252,77 @@ exit:
return ret;
}
+static int ath12k_wmi_tlv_rssi_chain_parse(struct ath12k_base *ab,
+ u16 tag, u16 len,
+ const void *ptr, void *data)
+{
+ const struct wmi_rssi_stat_params *stats_rssi = ptr;
+ struct wmi_tlv_fw_stats_parse *parse = data;
+ const struct wmi_stats_event *ev = parse->ev;
+ struct ath12k_fw_stats *stats = parse->stats;
+ struct ath12k_link_vif *arvif;
+ struct ath12k_link_sta *arsta;
+ struct ieee80211_sta *sta;
+ struct ath12k_sta *ahsta;
+ struct ath12k *ar;
+ int vdev_id;
+ int j;
+
+ if (!ev) {
+ ath12k_warn(ab, "failed to fetch update stats ev");
+ return -EPROTO;
+ }
+
+ if (tag != WMI_TAG_RSSI_STATS)
+ return -EPROTO;
+
+ if (!stats)
+ return -EINVAL;
+
+ stats->pdev_id = le32_to_cpu(ev->pdev_id);
+ vdev_id = le32_to_cpu(stats_rssi->vdev_id);
+ guard(rcu)();
+ ar = ath12k_mac_get_ar_by_pdev_id(ab, stats->pdev_id);
+ if (!ar) {
+ ath12k_warn(ab, "invalid pdev id %d in rssi chain parse\n",
+ stats->pdev_id);
+ return -EPROTO;
+ }
+
+ arvif = ath12k_mac_get_arvif(ar, vdev_id);
+ if (!arvif) {
+ ath12k_warn(ab, "not found vif for vdev id %d\n", vdev_id);
+ return -EPROTO;
+ }
+
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
+ "stats bssid %pM vif %p\n",
+ arvif->bssid, arvif->ahvif->vif);
+
+ sta = ieee80211_find_sta_by_ifaddr(ath12k_ar_to_hw(ar),
+ arvif->bssid,
+ NULL);
+ if (!sta) {
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
+ "not found station of bssid %pM for rssi chain\n",
+ arvif->bssid);
+ return -EPROTO;
+ }
+
+ ahsta = ath12k_sta_to_ahsta(sta);
+ arsta = &ahsta->deflink;
+
+ 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] = le32_to_cpu(stats_rssi->rssi_avg_beacon[j]);
+
+ stats->stats_id = WMI_REQUEST_RSSI_PER_CHAIN_STAT;
+
+ return 0;
+}
+
static int ath12k_wmi_tlv_fw_stats_parse(struct ath12k_base *ab,
u16 tag, u16 len,
const void *ptr, void *data)
@@ -8232,6 +8337,22 @@ static int ath12k_wmi_tlv_fw_stats_parse(struct ath12k_base *ab,
case WMI_TAG_ARRAY_BYTE:
ret = ath12k_wmi_tlv_fw_stats_data_parse(ab, parse, ptr, len);
break;
+ case WMI_TAG_PER_CHAIN_RSSI_STATS:
+ parse->rssi = ptr;
+ if (le32_to_cpu(parse->ev->stats_id) & WMI_REQUEST_RSSI_PER_CHAIN_STAT)
+ parse->rssi_num = le32_to_cpu(parse->rssi->num_per_chain_rssi);
+ break;
+ case WMI_TAG_ARRAY_STRUCT:
+ if (parse->rssi_num && !parse->chain_rssi_done) {
+ ret = ath12k_wmi_tlv_iter(ab, ptr, len,
+ ath12k_wmi_tlv_rssi_chain_parse,
+ parse);
+ if (ret)
+ return ret;
+
+ parse->chain_rssi_done = true;
+ }
+ break;
default:
break;
}
@@ -8345,6 +8466,12 @@ static void ath12k_update_stats_event(struct ath12k_base *ab, struct sk_buff *sk
goto complete;
}
+ /* Handle WMI_REQUEST_RSSI_PER_CHAIN_STAT status update */
+ if (stats.stats_id == WMI_REQUEST_RSSI_PER_CHAIN_STAT) {
+ complete(&ar->fw_stats_done);
+ goto complete;
+ }
+
/* Handle WMI_REQUEST_VDEV_STAT and WMI_REQUEST_BCN_STAT updates. */
ath12k_wmi_fw_stats_process(ar, &stats);