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.c383
1 files changed, 350 insertions, 33 deletions
diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
index 465f877fc0fb..da85c28ec355 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.c
+++ b/drivers/net/wireless/ath/ath12k/wmi.c
@@ -201,10 +201,9 @@ static __le32 ath12k_wmi_tlv_cmd_hdr(u32 cmd, u32 len)
void ath12k_wmi_init_qcn9274(struct ath12k_base *ab,
struct ath12k_wmi_resource_config_arg *config)
{
- config->num_vdevs = ab->num_radios * TARGET_NUM_VDEVS;
+ config->num_vdevs = ab->num_radios * TARGET_NUM_VDEVS(ab);
config->num_peers = ab->num_radios *
ath12k_core_get_max_peers_per_radio(ab);
- config->num_tids = ath12k_core_get_max_num_tids(ab);
config->num_offload_peers = TARGET_NUM_OFFLD_PEERS;
config->num_offload_reorder_buffs = TARGET_NUM_OFFLD_REORDER_BUFFS;
config->num_peer_keys = TARGET_NUM_PEER_KEYS;
@@ -537,6 +536,10 @@ ath12k_pull_mac_phy_cap_svc_ready_ext(struct ath12k_wmi_pdev *wmi_handle,
pdev_cap->he_mcs = le32_to_cpu(mac_caps->he_supp_mcs_5g);
pdev_cap->tx_chain_mask = le32_to_cpu(mac_caps->tx_chain_mask_5g);
pdev_cap->rx_chain_mask = le32_to_cpu(mac_caps->rx_chain_mask_5g);
+ pdev_cap->nss_ratio_enabled =
+ WMI_NSS_RATIO_EN_DIS_GET(mac_caps->nss_ratio);
+ pdev_cap->nss_ratio_info =
+ WMI_NSS_RATIO_INFO_GET(mac_caps->nss_ratio);
} else {
return -EINVAL;
}
@@ -782,20 +785,46 @@ struct sk_buff *ath12k_wmi_alloc_skb(struct ath12k_wmi_base *wmi_ab, u32 len)
return skb;
}
-int ath12k_wmi_mgmt_send(struct ath12k *ar, u32 vdev_id, u32 buf_id,
+int ath12k_wmi_mgmt_send(struct ath12k_link_vif *arvif, u32 buf_id,
struct sk_buff *frame)
{
+ struct ath12k *ar = arvif->ar;
struct ath12k_wmi_pdev *wmi = ar->wmi;
struct wmi_mgmt_send_cmd *cmd;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(frame);
- struct wmi_tlv *frame_tlv;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)frame->data;
+ struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif);
+ int cmd_len = sizeof(struct ath12k_wmi_mgmt_send_tx_params);
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)hdr;
+ struct ath12k_wmi_mlo_mgmt_send_params *ml_params;
+ struct ath12k_base *ab = ar->ab;
+ struct wmi_tlv *frame_tlv, *tlv;
+ struct ath12k_skb_cb *skb_cb;
+ u32 buf_len, buf_len_aligned;
+ u32 vdev_id = arvif->vdev_id;
+ bool link_agnostic = false;
struct sk_buff *skb;
- u32 buf_len;
int ret, len;
+ void *ptr;
buf_len = min_t(int, frame->len, WMI_MGMT_SEND_DOWNLD_LEN);
- len = sizeof(*cmd) + sizeof(*frame_tlv) + roundup(buf_len, 4);
+ buf_len_aligned = roundup(buf_len, sizeof(u32));
+
+ len = sizeof(*cmd) + sizeof(*frame_tlv) + buf_len_aligned;
+
+ if (ieee80211_vif_is_mld(vif)) {
+ skb_cb = ATH12K_SKB_CB(frame);
+ if ((skb_cb->flags & ATH12K_SKB_MLO_STA) &&
+ ab->hw_params->hw_ops->is_frame_link_agnostic &&
+ ab->hw_params->hw_ops->is_frame_link_agnostic(arvif, mgmt)) {
+ len += cmd_len + TLV_HDR_SIZE + sizeof(*ml_params);
+ ath12k_generic_dbg(ATH12K_DBG_MGMT,
+ "Sending Mgmt Frame fc 0x%0x as link agnostic",
+ mgmt->frame_control);
+ link_agnostic = true;
+ }
+ }
skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len);
if (!skb)
@@ -818,6 +847,28 @@ int ath12k_wmi_mgmt_send(struct ath12k *ar, u32 vdev_id, u32 buf_id,
memcpy(frame_tlv->value, frame->data, buf_len);
+ if (!link_agnostic)
+ goto send;
+
+ ptr = skb->data + sizeof(*cmd) + sizeof(*frame_tlv) + buf_len_aligned;
+
+ tlv = ptr;
+
+ /* Tx params not used currently */
+ tlv->header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_TX_SEND_PARAMS, cmd_len);
+ ptr += cmd_len;
+
+ tlv = ptr;
+ tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, sizeof(*ml_params));
+ ptr += TLV_HDR_SIZE;
+
+ ml_params = ptr;
+ ml_params->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_MLO_TX_SEND_PARAMS,
+ sizeof(*ml_params));
+
+ ml_params->hw_link_id = cpu_to_le32(WMI_MGMT_LINK_AGNOSTIC_ID);
+
+send:
ret = ath12k_wmi_cmd_send(wmi, skb, WMI_MGMT_TX_SEND_CMDID);
if (ret) {
ath12k_warn(ar->ab,
@@ -1059,15 +1110,14 @@ static void ath12k_wmi_put_wmi_channel(struct ath12k_wmi_channel_params *chan,
chan->band_center_freq2 = cpu_to_le32(center_freq1);
- } else if (arg->mode == MODE_11BE_EHT160) {
+ } else if (arg->mode == MODE_11BE_EHT160 ||
+ arg->mode == MODE_11AX_HE160) {
if (arg->freq > center_freq1)
chan->band_center_freq1 = cpu_to_le32(center_freq1 + 40);
else
chan->band_center_freq1 = cpu_to_le32(center_freq1 - 40);
chan->band_center_freq2 = cpu_to_le32(center_freq1);
- } else if (arg->mode == MODE_11BE_EHT80_80) {
- chan->band_center_freq2 = cpu_to_le32(arg->band_center_freq2);
} else {
chan->band_center_freq2 = 0;
}
@@ -2013,6 +2063,9 @@ int ath12k_wmi_bcn_tmpl(struct ath12k_link_vif *arvif,
u32p_replace_bits(&ema_params, 1, WMI_EMA_BEACON_LAST);
cmd->ema_params = cpu_to_le32(ema_params);
}
+ cmd->feature_enable_bitmap =
+ cpu_to_le32(u32_encode_bits(arvif->beacon_prot,
+ WMI_BEACON_PROTECTION_EN_BIT));
ptr = skb->data + sizeof(*cmd);
@@ -2152,7 +2205,7 @@ static void ath12k_wmi_copy_peer_flags(struct wmi_peer_assoc_complete_cmd *cmd,
cmd->peer_flags |= cpu_to_le32(WMI_PEER_AUTH);
if (arg->need_ptk_4_way) {
cmd->peer_flags |= cpu_to_le32(WMI_PEER_NEED_PTK_4_WAY);
- if (!hw_crypto_disabled)
+ if (!hw_crypto_disabled && arg->is_assoc)
cmd->peer_flags &= cpu_to_le32(~WMI_PEER_AUTH);
}
if (arg->need_gtk_2_way)
@@ -3880,7 +3933,8 @@ ath12k_wmi_copy_resource_config(struct ath12k_base *ab,
wmi_cfg->max_bssid_rx_filters = cpu_to_le32(tg_cfg->max_bssid_rx_filters);
wmi_cfg->use_pdev_id = cpu_to_le32(tg_cfg->use_pdev_id);
wmi_cfg->flag1 = cpu_to_le32(tg_cfg->atf_config |
- WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64);
+ WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64 |
+ WMI_RSRC_CFG_FLAG1_ACK_RSSI);
wmi_cfg->peer_map_unmap_version = cpu_to_le32(tg_cfg->peer_map_unmap_version);
wmi_cfg->sched_params = cpu_to_le32(tg_cfg->sched_params);
wmi_cfg->twt_ap_pdev_count = cpu_to_le32(tg_cfg->twt_ap_pdev_count);
@@ -6116,7 +6170,7 @@ static int ath12k_pull_mgmt_rx_params_tlv(struct ath12k_base *ab,
}
static int wmi_process_mgmt_tx_comp(struct ath12k *ar, u32 desc_id,
- u32 status)
+ u32 status, u32 ack_rssi)
{
struct sk_buff *msdu;
struct ieee80211_tx_info *info;
@@ -6140,8 +6194,16 @@ static int wmi_process_mgmt_tx_comp(struct ath12k *ar, u32 desc_id,
dma_unmap_single(ar->ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
info = IEEE80211_SKB_CB(msdu);
- if ((!(info->flags & IEEE80211_TX_CTL_NO_ACK)) && !status)
+ memset(&info->status, 0, sizeof(info->status));
+
+ /* skip tx rate update from ieee80211_status*/
+ info->status.rates[0].idx = -1;
+
+ if ((!(info->flags & IEEE80211_TX_CTL_NO_ACK)) && !status) {
info->flags |= IEEE80211_TX_STAT_ACK;
+ info->status.ack_signal = ack_rssi;
+ info->status.flags |= IEEE80211_TX_STATUS_ACK_SIGNAL_VALID;
+ }
if ((info->flags & IEEE80211_TX_CTL_NO_ACK) && !status)
info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED;
@@ -6185,6 +6247,8 @@ static int ath12k_pull_mgmt_tx_compl_param_tlv(struct ath12k_base *ab,
param->pdev_id = ev->pdev_id;
param->desc_id = ev->desc_id;
param->status = ev->status;
+ param->ppdu_id = ev->ppdu_id;
+ param->ack_rssi = ev->ack_rssi;
kfree(tb);
return 0;
@@ -6445,9 +6509,16 @@ static int freq_to_idx(struct ath12k *ar, int freq)
if (!sband)
continue;
- for (ch = 0; ch < sband->n_channels; ch++, idx++)
+ for (ch = 0; ch < sband->n_channels; ch++, idx++) {
+ if (sband->channels[ch].center_freq <
+ KHZ_TO_MHZ(ar->freq_range.start_freq) ||
+ sband->channels[ch].center_freq >
+ KHZ_TO_MHZ(ar->freq_range.end_freq))
+ continue;
+
if (sband->channels[ch].center_freq == freq)
goto exit;
+ }
}
exit:
@@ -6677,7 +6748,8 @@ static void ath12k_wmi_htc_tx_complete(struct ath12k_base *ab,
static int ath12k_reg_chan_list_event(struct ath12k_base *ab, struct sk_buff *skb)
{
struct ath12k_reg_info *reg_info;
- u8 pdev_idx;
+ struct ath12k *ar = NULL;
+ u8 pdev_idx = 255;
int ret;
reg_info = kzalloc(sizeof(*reg_info), GFP_ATOMIC);
@@ -6732,7 +6804,7 @@ mem_free:
kfree(reg_info);
if (ret == ATH12K_REG_STATUS_VALID)
- return ret;
+ goto out;
fallback:
/* Fallback to older reg (by sending previous country setting
@@ -6746,6 +6818,18 @@ fallback:
WARN_ON(1);
out:
+ /* In some error cases, even a valid pdev_idx might not be available */
+ if (pdev_idx != 255)
+ ar = ab->pdevs[pdev_idx].ar;
+
+ /* During the boot-time update, 'ar' might not be allocated,
+ * so the completion cannot be marked at that point.
+ * This boot-time update is handled in ath12k_mac_hw_register()
+ * before registering the hardware.
+ */
+ if (ar)
+ complete_all(&ar->regd_update_completed);
+
return ret;
}
@@ -6955,12 +7039,13 @@ static void ath12k_vdev_stopped_event(struct ath12k_base *ab, struct sk_buff *sk
static void ath12k_mgmt_rx_event(struct ath12k_base *ab, struct sk_buff *skb)
{
- struct ath12k_wmi_mgmt_rx_arg rx_ev = {0};
+ struct ath12k_wmi_mgmt_rx_arg rx_ev = {};
struct ath12k *ar;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_hdr *hdr;
u16 fc;
struct ieee80211_supported_band *sband;
+ s32 noise_floor;
if (ath12k_pull_mgmt_rx_params_tlv(ab, skb, &rx_ev) != 0) {
ath12k_warn(ab, "failed to extract mgmt rx event");
@@ -7022,7 +7107,11 @@ static void ath12k_mgmt_rx_event(struct ath12k_base *ab, struct sk_buff *skb)
status->freq = ieee80211_channel_to_frequency(rx_ev.channel,
status->band);
- status->signal = rx_ev.snr + ATH12K_DEFAULT_NOISE_FLOOR;
+ spin_lock_bh(&ar->data_lock);
+ noise_floor = ath12k_pdev_get_noise_floor(ar);
+ spin_unlock_bh(&ar->data_lock);
+
+ status->signal = rx_ev.snr + noise_floor;
status->rate_idx = ath12k_mac_bitrate_to_idx(sband, rx_ev.rate / 100);
hdr = (struct ieee80211_hdr *)skb->data;
@@ -7069,7 +7158,7 @@ exit:
static void ath12k_mgmt_tx_compl_event(struct ath12k_base *ab, struct sk_buff *skb)
{
- struct wmi_mgmt_tx_compl_event tx_compl_param = {0};
+ struct wmi_mgmt_tx_compl_event tx_compl_param = {};
struct ath12k *ar;
if (ath12k_pull_mgmt_tx_compl_param_tlv(ab, skb, &tx_compl_param) != 0) {
@@ -7086,7 +7175,8 @@ static void ath12k_mgmt_tx_compl_event(struct ath12k_base *ab, struct sk_buff *s
}
wmi_process_mgmt_tx_comp(ar, le32_to_cpu(tx_compl_param.desc_id),
- le32_to_cpu(tx_compl_param.status));
+ le32_to_cpu(tx_compl_param.status),
+ le32_to_cpu(tx_compl_param.ack_rssi));
ath12k_dbg(ab, ATH12K_DBG_MGMT,
"mgmt tx compl ev pdev_id %d, desc_id %d, status %d",
@@ -7126,7 +7216,7 @@ static struct ath12k *ath12k_get_ar_on_scan_state(struct ath12k_base *ab,
static void ath12k_scan_event(struct ath12k_base *ab, struct sk_buff *skb)
{
struct ath12k *ar;
- struct wmi_scan_event scan_ev = {0};
+ struct wmi_scan_event scan_ev = {};
if (ath12k_pull_scan_ev(ab, skb, &scan_ev) != 0) {
ath12k_warn(ab, "failed to extract scan event");
@@ -7303,7 +7393,7 @@ static void ath12k_roam_event(struct ath12k_base *ab, struct sk_buff *skb)
static void ath12k_chan_info_event(struct ath12k_base *ab, struct sk_buff *skb)
{
- struct wmi_chan_info_event ch_info_ev = {0};
+ struct wmi_chan_info_event ch_info_ev = {};
struct ath12k *ar;
struct survey_info *survey;
int idx;
@@ -7451,7 +7541,7 @@ exit:
static void ath12k_vdev_install_key_compl_event(struct ath12k_base *ab,
struct sk_buff *skb)
{
- struct wmi_vdev_install_key_complete_arg install_key_compl = {0};
+ struct wmi_vdev_install_key_complete_arg install_key_compl = {};
struct ath12k *ar;
if (ath12k_pull_vdev_install_key_compl_ev(ab, skb, &install_key_compl) != 0) {
@@ -7491,7 +7581,8 @@ static int ath12k_wmi_tlv_services_parser(struct ath12k_base *ab,
void *data)
{
const struct wmi_service_available_event *ev;
- u32 *wmi_ext2_service_bitmap;
+ u16 wmi_ext2_service_words;
+ __le32 *wmi_ext2_service_bitmap;
int i, j;
u16 expected_len;
@@ -7523,21 +7614,21 @@ static int ath12k_wmi_tlv_services_parser(struct ath12k_base *ab,
ev->wmi_service_segment_bitmap[3]);
break;
case WMI_TAG_ARRAY_UINT32:
- wmi_ext2_service_bitmap = (u32 *)ptr;
+ wmi_ext2_service_bitmap = (__le32 *)ptr;
+ wmi_ext2_service_words = len / sizeof(u32);
for (i = 0, j = WMI_MAX_EXT_SERVICE;
- i < WMI_SERVICE_SEGMENT_BM_SIZE32 && j < WMI_MAX_EXT2_SERVICE;
+ i < wmi_ext2_service_words && j < WMI_MAX_EXT2_SERVICE;
i++) {
do {
- if (wmi_ext2_service_bitmap[i] &
+ if (__le32_to_cpu(wmi_ext2_service_bitmap[i]) &
BIT(j % WMI_AVAIL_SERVICE_BITS_IN_SIZE32))
set_bit(j, ab->wmi_ab.svc_map);
} while (++j % WMI_AVAIL_SERVICE_BITS_IN_SIZE32);
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
+ "wmi_ext2_service bitmap 0x%08x\n",
+ __le32_to_cpu(wmi_ext2_service_bitmap[i]));
}
- ath12k_dbg(ab, ATH12K_DBG_WMI,
- "wmi_ext2_service_bitmap 0x%04x 0x%04x 0x%04x 0x%04x",
- wmi_ext2_service_bitmap[0], wmi_ext2_service_bitmap[1],
- wmi_ext2_service_bitmap[2], wmi_ext2_service_bitmap[3]);
break;
}
return 0;
@@ -7555,7 +7646,7 @@ static int ath12k_service_available_event(struct ath12k_base *ab, struct sk_buff
static void ath12k_peer_assoc_conf_event(struct ath12k_base *ab, struct sk_buff *skb)
{
- struct wmi_peer_assoc_conf_arg peer_assoc_conf = {0};
+ struct wmi_peer_assoc_conf_arg peer_assoc_conf = {};
struct ath12k *ar;
if (ath12k_pull_peer_assoc_conf_ev(ab, skb, &peer_assoc_conf) != 0) {
@@ -8501,7 +8592,7 @@ ath12k_wmi_pdev_temperature_event(struct ath12k_base *ab,
struct sk_buff *skb)
{
struct ath12k *ar;
- struct wmi_pdev_temperature_event ev = {0};
+ struct wmi_pdev_temperature_event ev = {};
if (ath12k_pull_pdev_temp_ev(ab, skb, &ev) != 0) {
ath12k_warn(ab, "failed to extract pdev temperature event");
@@ -9299,6 +9390,229 @@ static void ath12k_wmi_process_tpc_stats(struct ath12k_base *ab,
}
#endif
+static int
+ath12k_wmi_rssi_dbm_conv_info_evt_subtlv_parser(struct ath12k_base *ab,
+ u16 tag, u16 len,
+ const void *ptr, void *data)
+{
+ const struct ath12k_wmi_rssi_dbm_conv_temp_info_params *temp_info;
+ const struct ath12k_wmi_rssi_dbm_conv_info_params *param_info;
+ struct ath12k_wmi_rssi_dbm_conv_info_arg *rssi_info = data;
+ struct ath12k_wmi_rssi_dbm_conv_param_arg param_arg;
+ s32 nf_hw_dbm[ATH12K_MAX_NUM_NF_HW_DBM];
+ u8 num_20mhz_segments;
+ s8 min_nf, *nf_ptr;
+ int i, j;
+
+ switch (tag) {
+ case WMI_TAG_RSSI_DBM_CONVERSION_PARAMS_INFO:
+ if (len < sizeof(*param_info)) {
+ ath12k_warn(ab,
+ "RSSI dbm conv subtlv 0x%x invalid len %d rcvd",
+ tag, len);
+ return -EINVAL;
+ }
+
+ param_info = ptr;
+
+ param_arg.curr_bw = le32_to_cpu(param_info->curr_bw);
+ param_arg.curr_rx_chainmask = le32_to_cpu(param_info->curr_rx_chainmask);
+
+ /* The received array is actually a 2D byte-array for per chain,
+ * per 20MHz subband. Convert to 2D byte-array
+ */
+ nf_ptr = &param_arg.nf_hw_dbm[0][0];
+
+ for (i = 0; i < ATH12K_MAX_NUM_NF_HW_DBM; i++) {
+ nf_hw_dbm[i] = a_sle32_to_cpu(param_info->nf_hw_dbm[i]);
+
+ for (j = 0; j < 4; j++) {
+ *nf_ptr = (nf_hw_dbm[i] >> (j * 8)) & 0xFF;
+ nf_ptr++;
+ }
+ }
+
+ switch (param_arg.curr_bw) {
+ case WMI_CHAN_WIDTH_20:
+ num_20mhz_segments = 1;
+ break;
+ case WMI_CHAN_WIDTH_40:
+ num_20mhz_segments = 2;
+ break;
+ case WMI_CHAN_WIDTH_80:
+ num_20mhz_segments = 4;
+ break;
+ case WMI_CHAN_WIDTH_160:
+ num_20mhz_segments = 8;
+ break;
+ case WMI_CHAN_WIDTH_320:
+ num_20mhz_segments = 16;
+ break;
+ default:
+ ath12k_warn(ab, "Invalid current bandwidth %d in RSSI dbm event",
+ param_arg.curr_bw);
+ /* In error case, still consider the primary 20 MHz segment since
+ * that would be much better than instead of dropping the whole
+ * event
+ */
+ num_20mhz_segments = 1;
+ }
+
+ min_nf = ATH12K_DEFAULT_NOISE_FLOOR;
+
+ for (i = 0; i < ATH12K_MAX_NUM_ANTENNA; i++) {
+ if (!(param_arg.curr_rx_chainmask & BIT(i)))
+ continue;
+
+ for (j = 0; j < num_20mhz_segments; j++) {
+ if (param_arg.nf_hw_dbm[i][j] < min_nf)
+ min_nf = param_arg.nf_hw_dbm[i][j];
+ }
+ }
+
+ rssi_info->min_nf_dbm = min_nf;
+ rssi_info->nf_dbm_present = true;
+ break;
+ case WMI_TAG_RSSI_DBM_CONVERSION_TEMP_OFFSET_INFO:
+ if (len < sizeof(*temp_info)) {
+ ath12k_warn(ab,
+ "RSSI dbm conv subtlv 0x%x invalid len %d rcvd",
+ tag, len);
+ return -EINVAL;
+ }
+
+ temp_info = ptr;
+ rssi_info->temp_offset = a_sle32_to_cpu(temp_info->offset);
+ rssi_info->temp_offset_present = true;
+ break;
+ default:
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
+ "Unknown subtlv 0x%x in RSSI dbm conversion event\n", tag);
+ }
+
+ return 0;
+}
+
+static int
+ath12k_wmi_rssi_dbm_conv_info_event_parser(struct ath12k_base *ab,
+ u16 tag, u16 len,
+ const void *ptr, void *data)
+{
+ int ret = 0;
+
+ switch (tag) {
+ case WMI_TAG_RSSI_DBM_CONVERSION_PARAMS_INFO_FIXED_PARAM:
+ /* Fixed param is already processed*/
+ break;
+ case WMI_TAG_ARRAY_STRUCT:
+ /* len 0 is expected for array of struct when there
+ * is no content of that type inside that tlv
+ */
+ if (len == 0)
+ return 0;
+
+ ret = ath12k_wmi_tlv_iter(ab, ptr, len,
+ ath12k_wmi_rssi_dbm_conv_info_evt_subtlv_parser,
+ data);
+ break;
+ default:
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
+ "Received invalid tag 0x%x for RSSI dbm conv info event\n",
+ tag);
+ break;
+ }
+
+ return ret;
+}
+
+static int
+ath12k_wmi_rssi_dbm_conv_info_process_fixed_param(struct ath12k_base *ab, u8 *ptr,
+ size_t len, int *pdev_id)
+{
+ struct ath12k_wmi_rssi_dbm_conv_info_fixed_params *fixed_param;
+ const struct wmi_tlv *tlv;
+ u16 tlv_tag;
+
+ if (len < (sizeof(*fixed_param) + TLV_HDR_SIZE)) {
+ ath12k_warn(ab, "invalid RSSI dbm conv event size %zu\n", len);
+ return -EINVAL;
+ }
+
+ tlv = (struct wmi_tlv *)ptr;
+ tlv_tag = le32_get_bits(tlv->header, WMI_TLV_TAG);
+ ptr += sizeof(*tlv);
+
+ if (tlv_tag != WMI_TAG_RSSI_DBM_CONVERSION_PARAMS_INFO_FIXED_PARAM) {
+ ath12k_warn(ab, "RSSI dbm conv event received without fixed param tlv\n");
+ return -EINVAL;
+ }
+
+ fixed_param = (struct ath12k_wmi_rssi_dbm_conv_info_fixed_params *)ptr;
+ *pdev_id = le32_to_cpu(fixed_param->pdev_id);
+
+ return 0;
+}
+
+static void
+ath12k_wmi_update_rssi_offsets(struct ath12k *ar,
+ struct ath12k_wmi_rssi_dbm_conv_info_arg *rssi_info)
+{
+ struct ath12k_pdev_rssi_offsets *info = &ar->rssi_info;
+
+ lockdep_assert_held(&ar->data_lock);
+
+ if (rssi_info->temp_offset_present)
+ info->temp_offset = rssi_info->temp_offset;
+
+ if (rssi_info->nf_dbm_present)
+ info->min_nf_dbm = rssi_info->min_nf_dbm;
+
+ info->noise_floor = info->min_nf_dbm + info->temp_offset;
+}
+
+static void
+ath12k_wmi_rssi_dbm_conversion_params_info_event(struct ath12k_base *ab,
+ struct sk_buff *skb)
+{
+ struct ath12k_wmi_rssi_dbm_conv_info_arg rssi_info;
+ struct ath12k *ar;
+ s32 noise_floor;
+ u32 pdev_id;
+ int ret;
+
+ ret = ath12k_wmi_rssi_dbm_conv_info_process_fixed_param(ab, skb->data, skb->len,
+ &pdev_id);
+ if (ret) {
+ ath12k_warn(ab, "failed to parse fixed param in RSSI dbm conv event: %d\n",
+ ret);
+ return;
+ }
+
+ rcu_read_lock();
+ ar = ath12k_mac_get_ar_by_pdev_id(ab, pdev_id);
+ /* If pdev is not active, ignore the event */
+ if (!ar)
+ goto out_unlock;
+
+ ret = ath12k_wmi_tlv_iter(ab, skb->data, skb->len,
+ ath12k_wmi_rssi_dbm_conv_info_event_parser,
+ &rssi_info);
+ if (ret) {
+ ath12k_warn(ab, "unable to parse RSSI dbm conversion event\n");
+ goto out_unlock;
+ }
+
+ spin_lock_bh(&ar->data_lock);
+ ath12k_wmi_update_rssi_offsets(ar, &rssi_info);
+ noise_floor = ath12k_pdev_get_noise_floor(ar);
+ spin_unlock_bh(&ar->data_lock);
+
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
+ "RSSI noise floor updated, new value is %d dbm\n", noise_floor);
+out_unlock:
+ rcu_read_unlock();
+}
+
static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb)
{
struct wmi_cmd_hdr *cmd_hdr;
@@ -9430,6 +9744,9 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb)
case WMI_11D_NEW_COUNTRY_EVENTID:
ath12k_reg_11d_new_cc_event(ab, skb);
break;
+ case WMI_PDEV_RSSI_DBM_CONVERSION_PARAMS_INFO_EVENTID:
+ ath12k_wmi_rssi_dbm_conversion_params_info_event(ab, skb);
+ break;
/* add Unsupported events (rare) here */
case WMI_TBTTOFFSET_EXT_UPDATE_EVENTID:
case WMI_PEER_OPER_MODE_CHANGE_EVENTID: