From b6b05d6e9b69d76053cc6997e1cedf2e4c9c24c0 Mon Sep 17 00:00:00 2001 From: Hari Chandrakanthan Date: Sun, 18 Apr 2021 16:51:56 +0530 Subject: [PATCH] debugfs-support-for-ani-parameters Signed-off-by: Hari Chandrakanthan --- drivers/net/wireless/ath/ath11k/core.h | 8 + drivers/net/wireless/ath/ath11k/debugfs.c | 308 ++++++++++++++++++++++++++++++ drivers/net/wireless/ath/ath11k/debugfs.h | 6 + drivers/net/wireless/ath/ath11k/mac.c | 3 + drivers/net/wireless/ath/ath11k/wmi.c | 74 +++++++ drivers/net/wireless/ath/ath11k/wmi.h | 18 ++ 6 files changed, 417 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h index 07a7175..c1dc737 100644 --- a/drivers/net/wireless/ath/ath11k/core.h +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -740,6 +740,7 @@ struct ath11k { struct ath11k_cfr cfr; #endif u8 cfr_enabled; + bool ani_enabled; }; struct ath11k_band_cap { @@ -988,6 +989,13 @@ struct ath11k_base { u32 rx_hash; bool stats_disable; + u32 ani_poll_period; + u32 ani_listen_period; + int ani_ofdm_level; + int ani_cck_level; + struct completion ani_ofdm_event; + struct completion ani_cck_event; + /* must be last */ u8 drv_priv[0] __aligned(sizeof(void *)); }; diff --git a/drivers/net/wireless/ath/ath11k/debugfs.c b/drivers/net/wireless/ath/ath11k/debugfs.c index 2636931..f04395a 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs.c +++ b/drivers/net/wireless/ath/ath11k/debugfs.c @@ -3692,6 +3692,302 @@ static const struct file_operations fops_disable_dyn_bw = { .open = simple_open }; +static ssize_t ath11k_read_ani_enable(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + int len = 0; + char buf[32]; + + len = scnprintf(buf, sizeof(buf) - len, "%d\n",ar->ani_enabled); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath11k_write_ani_enable(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + int ret; + u8 enable; + + if (kstrtou8_from_user(user_buf, count, 0, &enable)) + return -EINVAL; + + mutex_lock(&ar->conf_mutex); + + if (ar->ani_enabled == enable) { + ret = count; + goto exit; + } + + ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_ANI_ENABLE, + enable, ar->pdev->pdev_id); + if (ret) { + ath11k_warn(ar, "ani_enable failed from debugfs: %d\n", ret); + goto exit; + } + ar->ani_enabled = enable; + ret = count; + +exit: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static const struct file_operations fops_ani_enable = { + .read = ath11k_read_ani_enable, + .write = ath11k_write_ani_enable, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath11k_read_ani_poll_period(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + int len = 0; + char buf[32]; + + len = scnprintf(buf, sizeof(buf) - len, "%u\n", ar->ab->ani_poll_period); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath11k_write_ani_poll_period(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + int ret; + u32 ani_poll_period; + + if (kstrtou32_from_user(user_buf, count, 0, &ani_poll_period)) + return -EINVAL; + + if(ani_poll_period > ATH11K_ANI_POLL_PERIOD_MAX) + return -EINVAL; + + mutex_lock(&ar->conf_mutex); + + ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_ANI_POLL_PERIOD, + ani_poll_period, ar->pdev->pdev_id); + if (ret) { + ath11k_warn(ar, "ani poll period write failed in debugfs: %d\n", ret); + goto exit; + } + ar->ab->ani_poll_period = ani_poll_period; + ret = count; + +exit: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static const struct file_operations fops_ani_poll_period = { + .read = ath11k_read_ani_poll_period, + .write = ath11k_write_ani_poll_period, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath11k_read_ani_listen_period(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + int len = 0; + char buf[32]; + + len = scnprintf(buf, sizeof(buf) - len, "%u\n", ar->ab->ani_listen_period); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath11k_write_ani_listen_period(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + int ret; + u32 ani_listen_period = 0; + + if (kstrtou32_from_user(user_buf, count, 0, &ani_listen_period)) + return -EINVAL; + + if(ani_listen_period > ATH11K_ANI_LISTEN_PERIOD_MAX) + return -EINVAL; + + mutex_lock(&ar->conf_mutex); + + ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_ANI_LISTEN_PERIOD, + ani_listen_period, ar->pdev->pdev_id); + if (ret) { + ath11k_warn(ar, "ani listen period write failed in debugfs: %d\n", ret); + goto exit; + } + ar->ab->ani_listen_period = ani_listen_period; + ret = count; + +exit: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static const struct file_operations fops_ani_listen_period = { + .read = ath11k_read_ani_listen_period, + .write = ath11k_write_ani_listen_period, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int ath11k_debug_get_ani_level(struct ath11k *ar) +{ + unsigned long time_left; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + reinit_completion(&(ar->ab->ani_ofdm_event)); + + ret = ath11k_wmi_pdev_get_ani_level(ar, WMI_PDEV_GET_ANI_OFDM_CONFIG_CMDID, + ar->pdev->pdev_id); + if (ret) { + ath11k_warn(ar, "failed to request ofdm ani level: %d\n", ret); + return ret; + } + time_left = wait_for_completion_timeout(&ar->ab->ani_ofdm_event, 1 * HZ); + if (time_left == 0) + return -ETIMEDOUT; + + if (ar->ab->target_caps.phy_capability & WHAL_WLAN_11G_CAPABILITY) { + reinit_completion(&(ar->ab->ani_cck_event)); + ret = ath11k_wmi_pdev_get_ani_level(ar, WMI_PDEV_GET_ANI_CCK_CONFIG_CMDID, + ar->pdev->pdev_id); + if (ret) { + ath11k_warn(ar, "failed to request cck ani level: %d\n", ret); + return ret; + } + time_left = wait_for_completion_timeout(&ar->ab->ani_cck_event, 1 * HZ); + if (time_left == 0) + return -ETIMEDOUT; + } + + return 0; +} + +static ssize_t ath11k_read_ani_level(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + char buf[128]; + int ret, len = 0; + + mutex_lock(&ar->conf_mutex); + if (ar->state != ATH11K_STATE_ON) { + ret = -ENETDOWN; + goto unlock; + } + + if(!ar->ani_enabled) { + len += scnprintf(buf, sizeof(buf), "ANI is disabled\n"); + } else { + ret = ath11k_debug_get_ani_level(ar); + if (ret) { + ath11k_warn(ar, "failed to request ani level: %d\n", ret); + goto unlock; + } + len += scnprintf(buf, sizeof(buf), "ofdm level %d cck level %d\n", + ar->ab->ani_ofdm_level, ar->ab->ani_cck_level); + } + mutex_unlock(&ar->conf_mutex); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); + +unlock: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static ssize_t ath11k_write_ani_level(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + char buf[32] = {0}; + ssize_t rc; + u32 ofdm_param = 0, cck_param = 0; + int ofdm_level, cck_level; + int ret; + + rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); + if (rc < 0) + return rc; + + buf[*ppos - 1] = '\0'; + + ret = sscanf(buf, "%d %d", &ofdm_level, &cck_level); + + if (ret != 2) + return -EINVAL; + + mutex_lock(&ar->conf_mutex); + + if (ar->state != ATH11K_STATE_ON && ar->state != ATH11K_STATE_RESTARTED) { + ret = -ENETDOWN; + goto exit; + } + + if ((ofdm_level >= ATH11K_ANI_LEVEL_MIN && ofdm_level <= ATH11K_ANI_LEVEL_MAX) || + (ofdm_level == ATH11K_ANI_LEVEL_AUTO)) { + ofdm_param = WMI_PDEV_PARAM_ANI_OFDM_LEVEL; + } else { + ret = -EINVAL; + goto exit; + } + + if((ar->ab->target_caps.phy_capability & WHAL_WLAN_11G_CAPABILITY)) { + if ((cck_level >= ATH11K_ANI_LEVEL_MIN && + cck_level <= ATH11K_ANI_LEVEL_MAX) || + (cck_level == ATH11K_ANI_LEVEL_AUTO)) { + cck_param = WMI_PDEV_PARAM_ANI_CCK_LEVEL; + } else { + ret = -EINVAL; + goto exit; + } + } + + ret = ath11k_wmi_pdev_set_param(ar, ofdm_param, ofdm_level, ar->pdev->pdev_id); + if (ret) { + ath11k_warn(ar, "failed to set ANI ofdm level :%d\n", ret); + goto exit; + } + + if (cck_param) { + ret = ath11k_wmi_pdev_set_param(ar, cck_param, cck_level, + ar->pdev->pdev_id); + if (ret) { + ath11k_warn(ar, "failed to set ANI cck level :%d\n", ret); + goto exit; + } + } + + ret = count; +exit: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static const struct file_operations fops_ani_level = { + .write = ath11k_write_ani_level, + .read = ath11k_read_ani_level, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + int ath11k_debugfs_register(struct ath11k *ar) { struct ath11k_base *ab = ar->ab; @@ -3716,6 +4012,8 @@ int ath11k_debugfs_register(struct ath11k *ar) ath11k_debugfs_fw_stats_init(ar); ath11k_init_pktlog(ar); init_completion(&ar->tpc_complete); + init_completion(&ab->ani_ofdm_event); + init_completion(&ab->ani_cck_event); debugfs_create_file("ext_tx_stats", 0644, ar->debug.debugfs_pdev, ar, @@ -3781,6 +4079,16 @@ int ath11k_debugfs_register(struct ath11k *ar) debugfs_create_file("enable_dbr_debug", 0200, ar->debug.debugfs_pdev, ar, &fops_dbr_debug); + + debugfs_create_file("ani_enable", S_IRUSR | S_IWUSR, + ar->debug.debugfs_pdev, ar, &fops_ani_enable); + debugfs_create_file("ani_level", S_IRUSR | S_IWUSR, + ar->debug.debugfs_pdev, ar, &fops_ani_level); + debugfs_create_file("ani_poll_period", S_IRUSR | S_IWUSR, + ar->debug.debugfs_pdev, ar, &fops_ani_poll_period); + debugfs_create_file("ani_listen_period", S_IRUSR | S_IWUSR, + ar->debug.debugfs_pdev, ar, &fops_ani_listen_period); + return 0; } diff --git a/drivers/net/wireless/ath/ath11k/debugfs.h b/drivers/net/wireless/ath/ath11k/debugfs.h index e08034c..95a8372 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs.h +++ b/drivers/net/wireless/ath/ath11k/debugfs.h @@ -211,6 +211,12 @@ do { \ #define ATH11K_MEMORY_STATS_DEC(_struct, _field, _size) #endif +#define ATH11K_ANI_LEVEL_MAX 30 +#define ATH11K_ANI_LEVEL_MIN -5 +#define ATH11K_ANI_LEVEL_AUTO 0x80 +#define ATH11K_ANI_POLL_PERIOD_MAX 3000 +#define ATH11K_ANI_LISTEN_PERIOD_MAX 3000 + #ifdef CPTCFG_ATH11K_DEBUGFS int ath11k_debugfs_create(void); void ath11k_debugfs_destroy(void); diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 29cbc33..a68ab06 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -6153,6 +6153,9 @@ static int ath11k_mac_op_start(struct ieee80211_hw *hw) rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx], &ab->pdevs[ar->pdev_idx]); + /*ani is enabled by default*/ + ar->ani_enabled = true; + return 0; err: diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 173d217..1fbba1b 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -8370,6 +8370,74 @@ ath11k_wmi_pdev_csa_switch_count_status_event(struct ath11k_base *ab, kfree(tb); } +int ath11k_wmi_pdev_get_ani_level(struct ath11k *ar, u32 cmd_id, u8 pdev_id) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_pdev_get_ani_level_cmd *cmd = NULL; + struct sk_buff *skb; + int ret; + enum wmi_tlv_tag temp_tlv_tag; + + switch(cmd_id) { + case WMI_PDEV_GET_ANI_OFDM_CONFIG_CMDID: + temp_tlv_tag = WMI_TAG_PDEV_GET_ANI_OFDM_CONFIG_CMD; + break; + case WMI_PDEV_GET_ANI_CCK_CONFIG_CMDID: + temp_tlv_tag = WMI_TAG_PDEV_GET_ANI_CCK_CONFIG_CMD; + break; + default: + ath11k_warn(ar->ab, "Invalid cmd %d\n", cmd_id); + return -EINVAL; + } + + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_pdev_get_ani_level_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, temp_tlv_tag) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->pdev_id = pdev_id; + + ret = ath11k_wmi_cmd_send(wmi, skb, cmd_id); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_PDEV_SET_PARAM cmd\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "WMI pdev pdev id %d\n", pdev_id); + + return ret; +} + +static int ath11k_wmi_event_ani_ofdm_level(struct ath11k_base *ab, + struct sk_buff *skb) +{ + const struct wmi_pdev_ani_event *ev; + + ev = (struct wmi_pdev_ani_event *)skb->data; + if (WARN_ON(skb->len < sizeof(*ev))) + return -EPROTO; + + ab->ani_ofdm_level = ev->ani_level; + complete(&ab->ani_ofdm_event); + return 0; +} + +static int ath11k_wmi_event_ani_cck_level(struct ath11k_base *ab, + struct sk_buff *skb) +{ + const struct wmi_pdev_ani_event *ev; + + ev = (struct wmi_pdev_ani_event *)skb->data; + if (WARN_ON(skb->len < sizeof(*ev))) + return -EPROTO; + + ab->ani_cck_level = ev->ani_level; + complete(&(ab->ani_cck_event)); + return 0; +} + static void ath11k_wmi_pdev_dfs_radar_detected_event(struct ath11k_base *ab, struct sk_buff *skb) { @@ -9587,6 +9655,12 @@ static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb) case WMI_PEER_CFR_CAPTURE_EVENTID: ath11k_wmi_parse_cfr_capture_event(ab, skb); break; + case WMI_PDEV_ANI_CCK_LEVEL_EVENTID: + ath11k_wmi_event_ani_cck_level(ab, skb); + break; + case WMI_PDEV_ANI_OFDM_LEVEL_EVENTID: + ath11k_wmi_event_ani_ofdm_level(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 55f51fa..d74128f 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -117,6 +117,12 @@ enum { WMI_HOST_WLAN_2G_5G_CAP = 0x3, }; +enum wlan_mode_capability { + WHAL_WLAN_11A_CAPABILITY = 0x1, + WHAL_WLAN_11G_CAPABILITY = 0x2, + WHAL_WLAN_11AG_CAPABILITY = 0x3, +}; + /* Parameters used for WMI_VDEV_PARAM_AUTORATE_MISC_CFG command. * Used only for HE auto rate mode. */ @@ -2753,6 +2759,11 @@ struct wmi_p2p_noa_descriptor { u32 start_time; }; +struct wmi_pdev_ani_event { + u32 tlv_header; + u32 ani_level; +} __packed; + struct channel_param { u8 chan_id; u8 pwr; @@ -3148,6 +3159,12 @@ struct wmi_fwtest_set_param_cmd_param { u32 param_value; }; +struct wmi_pdev_get_ani_level_cmd { + u32 tlv_header; + u32 pdev_id; + u32 param_id; +} __packed; + struct wmi_pdev_set_param_cmd { u32 tlv_header; u32 pdev_id; @@ -6694,4 +6711,5 @@ int ath11k_wmi_dbglog_cfg(struct ath11k *ar, u32 param, u64 value); int ath11k_wmi_peer_set_cfr_capture_conf(struct ath11k *ar, u32 vdev_id, const u8 *mac, struct wmi_peer_cfr_capture_conf_arg *arg); +int ath11k_wmi_pdev_get_ani_level(struct ath11k *ar, u32 param_id, u8 pdev_id); #endif -- 2.7.4