[PATCH v2 3/4] wifi: ath12k: add factory test mode support

Jeff Johnson quic_jjohnson at quicinc.com
Fri Nov 3 06:41:08 PDT 2023


On 11/3/2023 2:37 AM, Aaradhana Sahu wrote:
> Add support to process factory test mode commands(FTM) for calibration.
> By default firmware start with MISSION mode and to process the FTM commands
> firmware needs to be restarted in FTM mode using module parameter ftm_mode.
> The pre-request is all the radios should be down before starting the test.
> 
> All ath12k test mode interface related commands specified in enum
> ath_tm_cmd.
> 
> When start command ATH_TM_CMD_TESTMODE_START is received, ar state
> is set to test Mode and FTM daemon sends test mode command to wifi
> driver via cfg80211. Wifi driver sends these command to firmware as
> wmi events. If it is segmented commands it will be broken down into
> multiple segments and encoded with TLV header else it is sent to
> firmware as it is.
> 
> Firmware response via UTF events, wifi driver creates skb and send
> to cfg80211, cfg80211 sends firmware response to FTM daemon via
> netlink message.
> 
> Command to boot in ftm mode
> insmod ath12k ftm_mode=1
> 
> Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.1.1-00126-QCAHKSWPL_SILICONZ-1
> 
> Signed-off-by: Aaradhana Sahu <quic_aarasahu at quicinc.com>
> ---
>   drivers/net/wireless/ath/ath12k/Makefile   |   1 +
>   drivers/net/wireless/ath/ath12k/core.c     |  19 +-
>   drivers/net/wireless/ath/ath12k/core.h     |  13 +
>   drivers/net/wireless/ath/ath12k/debug.h    |   1 +
>   drivers/net/wireless/ath/ath12k/dp.c       |   3 +
>   drivers/net/wireless/ath/ath12k/mac.c      |  13 +-
>   drivers/net/wireless/ath/ath12k/pci.c      |   1 +
>   drivers/net/wireless/ath/ath12k/testmode.c | 406 +++++++++++++++++++++
>   drivers/net/wireless/ath/ath12k/testmode.h |  42 +++
>   drivers/net/wireless/ath/ath12k/wmi.c      |  34 ++
>   drivers/net/wireless/ath/ath12k/wmi.h      |  20 +
>   11 files changed, 549 insertions(+), 4 deletions(-)
>   create mode 100644 drivers/net/wireless/ath/ath12k/testmode.c
>   create mode 100644 drivers/net/wireless/ath/ath12k/testmode.h
> 
> diff --git a/drivers/net/wireless/ath/ath12k/Makefile b/drivers/net/wireless/ath/ath12k/Makefile
> index 62c52e733b5e..20bfa824370e 100644
> --- a/drivers/net/wireless/ath/ath12k/Makefile
> +++ b/drivers/net/wireless/ath/ath12k/Makefile
> @@ -22,6 +22,7 @@ ath12k-y += core.o \
>   	    dp_mon.o
>   
>   ath12k-$(CONFIG_ATH12K_TRACING) += trace.o
> +ath12k-$(CONFIG_NL80211_TESTMODE) += testmode.o
>   
>   # for tracing framework to find trace.h
>   CFLAGS_trace.o := -I$(src)
> diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c
> index b936760b5140..9351b55a65fb 100644
> --- a/drivers/net/wireless/ath/ath12k/core.c
> +++ b/drivers/net/wireless/ath/ath12k/core.c
> @@ -40,6 +40,10 @@ static int ath12k_core_rfkill_config(struct ath12k_base *ab)
>   	return ret;
>   }
>   
> +bool ath12k_ftm_mode;
> +module_param_named(ftm_mode, ath12k_ftm_mode, bool, 0444);
> +MODULE_PARM_DESC(ftm_mode, "Boots up in factory test mode");
> +
>   int ath12k_core_suspend(struct ath12k_base *ab)
>   {
>   	int ret;
> @@ -472,6 +476,11 @@ static int ath12k_core_soc_create(struct ath12k_base *ab)
>   {
>   	int ret;
>   
> +	if (ath12k_ftm_mode) {
> +		ab->fw_mode = ATH12K_FIRMWARE_MODE_FTM;
> +		ath12k_info(ab, "Booting in ftm mode\n");
> +	}
> +
>   	ret = ath12k_qmi_init_service(ab);
>   	if (ret) {
>   		ath12k_err(ab, "failed to initialize qmi :%d\n", ret);
> @@ -663,7 +672,7 @@ int ath12k_core_qmi_firmware_ready(struct ath12k_base *ab)
>   {
>   	int ret;
>   
> -	ret = ath12k_core_start_firmware(ab, ATH12K_FIRMWARE_MODE_NORMAL);
> +	ret = ath12k_core_start_firmware(ab, ab->fw_mode);
>   	if (ret) {
>   		ath12k_err(ab, "failed to start firmware: %d\n", ret);
>   		return ret;
> @@ -682,7 +691,7 @@ int ath12k_core_qmi_firmware_ready(struct ath12k_base *ab)
>   	}
>   
>   	mutex_lock(&ab->core_lock);
> -	ret = ath12k_core_start(ab, ATH12K_FIRMWARE_MODE_NORMAL);
> +	ret = ath12k_core_start(ab, ab->fw_mode);

suggest you drop the 2nd param and just let ath12k_core_start() retrieve 
ab->fw_mode

>   	if (ret) {
>   		ath12k_err(ab, "failed to start core: %d\n", ret);
>   		goto err_dp_free;
> @@ -811,7 +820,8 @@ static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab)
>   	for (i = 0; i < ab->num_radios; i++) {
>   		pdev = &ab->pdevs[i];
>   		ar = pdev->ar;
> -		if (!ar || ar->state == ATH12K_STATE_OFF)
> +		if (!ar || ar->state == ATH12K_STATE_OFF ||
> +		    ar->state == ATH12K_STATE_TM)
>   			continue;
>   
>   		ieee80211_stop_queues(ar->hw);
> @@ -870,6 +880,9 @@ static void ath12k_core_post_reconfigure_recovery(struct ath12k_base *ab)
>   			ath12k_warn(ab,
>   				    "device is wedged, will not restart radio %d\n", i);
>   			break;
> +		case ATH12K_STATE_TM:
> +			ath12k_warn(ab, "fw mode reset done radio %d\n", i);
> +			break;
>   		}
>   		mutex_unlock(&ar->conf_mutex);
>   	}
> diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h
> index 68c42ca44fcb..3e5472556aff 100644
> --- a/drivers/net/wireless/ath/ath12k/core.h
> +++ b/drivers/net/wireless/ath/ath12k/core.h
> @@ -199,6 +199,7 @@ enum ath12k_dev_flags {
>   	ATH12K_FLAG_REGISTERED,
>   	ATH12K_FLAG_QMI_FAIL,
>   	ATH12K_FLAG_HTC_SUSPEND_COMPLETE,
> +	ATH12K_FLAG_FTM_SEGMENTED,
>   };
>   
>   enum ath12k_monitor_flags {
> @@ -429,12 +430,19 @@ enum ath12k_state {
>   	ATH12K_STATE_RESTARTING,
>   	ATH12K_STATE_RESTARTED,
>   	ATH12K_STATE_WEDGED,
> +	ATH12K_STATE_TM,
>   	/* Add other states as required */
>   };
>   
>   /* Antenna noise floor */
>   #define ATH12K_DEFAULT_NOISE_FLOOR -95
>   
> +struct ath12k_ftm_event_obj {
> +	u32 data_pos;
> +	u32 expected_seq;
> +	u8 *eventdata;
> +};
> +
>   struct ath12k_fw_stats {
>   	u32 pdev_id;
>   	u32 stats_id;
> @@ -588,6 +596,8 @@ struct ath12k {
>   	bool monitor_vdev_created;
>   	bool monitor_started;
>   	int monitor_vdev_id;
> +	struct completion fw_mode_reset;
> +	u8 ftm_msgref;
>   };
>   
>   struct ath12k_band_cap {
> @@ -674,6 +684,7 @@ struct ath12k_soc_dp_stats {
>   /* Master structure to hold the hw data which may be used in core module */
>   struct ath12k_base {
>   	enum ath12k_hw_rev hw_rev;
> +	enum ath12k_firmware_mode fw_mode;
>   	struct platform_device *pdev;
>   	struct device *dev;
>   	struct ath12k_qmi qmi;
> @@ -776,6 +787,8 @@ struct ath12k_base {
>   		/* protected by data_lock */
>   		u32 fw_crash_counter;
>   	} stats;
> +	bool ftm_segment_handler;
> +	struct ath12k_ftm_event_obj ftm_event_obj;
>   	u32 pktlog_defs_checksum;
>   
>   	struct ath12k_dbring_cap *db_caps;
> diff --git a/drivers/net/wireless/ath/ath12k/debug.h b/drivers/net/wireless/ath/ath12k/debug.h
> index aa685295f8a4..11139793547d 100644
> --- a/drivers/net/wireless/ath/ath12k/debug.h
> +++ b/drivers/net/wireless/ath/ath12k/debug.h
> @@ -33,6 +33,7 @@ __printf(2, 3) void ath12k_err(struct ath12k_base *ab, const char *fmt, ...);
>   __printf(2, 3) void ath12k_warn(struct ath12k_base *ab, const char *fmt, ...);
>   
>   extern unsigned int ath12k_debug_mask;
> +extern bool ath12k_ftm_mode;
>   
>   #ifdef CONFIG_ATH12K_DEBUG
>   __printf(3, 4) void __ath12k_dbg(struct ath12k_base *ab,
> diff --git a/drivers/net/wireless/ath/ath12k/dp.c b/drivers/net/wireless/ath/ath12k/dp.c
> index 6893466f61f0..b7827b95ded6 100644
> --- a/drivers/net/wireless/ath/ath12k/dp.c
> +++ b/drivers/net/wireless/ath/ath12k/dp.c
> @@ -1255,6 +1255,9 @@ void ath12k_dp_cc_config(struct ath12k_base *ab)
>   	u32 wbm_base = HAL_SEQ_WCSS_UMAC_WBM_REG;
>   	u32 val = 0;
>   
> +	if (ath12k_ftm_mode)
> +		return;
> +
>   	ath12k_hif_write32(ab, reo_base + HAL_REO1_SW_COOKIE_CFG0(ab), cmem_base);
>   
>   	val |= u32_encode_bits(ATH12K_CMEM_ADDR_MSB,
> diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
> index fc0d14ea328e..de57a57afde0 100644
> --- a/drivers/net/wireless/ath/ath12k/mac.c
> +++ b/drivers/net/wireless/ath/ath12k/mac.c
> @@ -13,6 +13,7 @@
>   #include "hw.h"
>   #include "dp_tx.h"
>   #include "dp_rx.h"
> +#include "testmode.h"
>   #include "peer.h"
>   
>   #define CHAN2G(_channel, _freq, _flags) { \
> @@ -598,7 +599,10 @@ struct ath12k *ath12k_mac_get_ar_by_pdev_id(struct ath12k_base *ab, u32 pdev_id)
>   		return NULL;
>   
>   	for (i = 0; i < ab->num_radios; i++) {
> -		pdev = rcu_dereference(ab->pdevs_active[i]);
> +		if (ab->fw_mode == ATH12K_FIRMWARE_MODE_FTM)
> +			pdev = &ab->pdevs[i];
> +		else
> +			pdev = rcu_dereference(ab->pdevs_active[i]);
>   
>   		if (pdev && pdev->pdev_id == pdev_id)
>   			return (pdev->ar ? pdev->ar : NULL);
> @@ -5043,6 +5047,11 @@ static int ath12k_mac_op_start(struct ieee80211_hw *hw)
>   	struct ath12k_pdev *pdev = ar->pdev;
>   	int ret;
>   
> +	if (ath12k_ftm_mode) {
> +		ath12k_err(ab, "fail to start mac operations in ftm mode\n");
> +		return -EWOULDBLOCK;
> +	}
> +
>   	ath12k_mac_drain_tx(ar);
>   	mutex_lock(&ar->conf_mutex);
>   
> @@ -5057,6 +5066,7 @@ static int ath12k_mac_op_start(struct ieee80211_hw *hw)
>   	case ATH12K_STATE_RESTARTED:
>   	case ATH12K_STATE_WEDGED:
>   	case ATH12K_STATE_ON:
> +	case ATH12K_STATE_TM:
>   		WARN_ON(1);
>   		ret = -EINVAL;
>   		goto err;
> @@ -7114,6 +7124,7 @@ static const struct ieee80211_ops ath12k_ops = {
>   	.get_survey			= ath12k_mac_op_get_survey,
>   	.flush				= ath12k_mac_op_flush,
>   	.sta_statistics			= ath12k_mac_op_sta_statistics,
> +	CFG80211_TESTMODE_CMD(ath12k_tm_cmd)
>   };
>   
>   static void ath12k_mac_update_ch_list(struct ath12k *ar,
> diff --git a/drivers/net/wireless/ath/ath12k/pci.c b/drivers/net/wireless/ath/ath12k/pci.c
> index 3006cd3fbe11..9d30c7ea235f 100644
> --- a/drivers/net/wireless/ath/ath12k/pci.c
> +++ b/drivers/net/wireless/ath/ath12k/pci.c
> @@ -1195,6 +1195,7 @@ static int ath12k_pci_probe(struct pci_dev *pdev,
>   	ab_pci->ab = ab;
>   	ab_pci->pdev = pdev;
>   	ab->hif.ops = &ath12k_pci_hif_ops;
> +	ab->fw_mode = ATH12K_FIRMWARE_MODE_NORMAL;
>   	pci_set_drvdata(pdev, ab);
>   	spin_lock_init(&ab_pci->window_lock);
>   
> diff --git a/drivers/net/wireless/ath/ath12k/testmode.c b/drivers/net/wireless/ath/ath12k/testmode.c
> new file mode 100644
> index 000000000000..cc5e482049a5
> --- /dev/null
> +++ b/drivers/net/wireless/ath/ath12k/testmode.c
> @@ -0,0 +1,406 @@
> +// SPDX-License-Identifier: BSD-3-Clause-Clear
> +/*
> + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
> + * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
> + */
> +
> +#include "testmode.h"
> +#include <net/netlink.h>
> +#include "debug.h"
> +#include "wmi.h"
> +#include "hw.h"
> +#include "core.h"
> +#include "hif.h"
> +#include "../testmode_i.h"
> +
> +#define ATH12K_FTM_SEGHDR_CURRENT_SEQ		GENMASK(3, 0)
> +#define ATH12K_FTM_SEGHDR_TOTAL_SEGMENTS	GENMASK(7, 4)
> +
> +static const struct nla_policy ath12k_tm_policy[ATH_TM_ATTR_MAX + 1] = {
> +	[ATH_TM_ATTR_CMD]		= { .type = NLA_U32 },
> +	[ATH_TM_ATTR_DATA]		= { .type = NLA_BINARY,
> +					    .len = ATH_TM_DATA_MAX_LEN },
> +	[ATH_TM_ATTR_WMI_CMDID]		= { .type = NLA_U32 },
> +	[ATH_TM_ATTR_VERSION_MAJOR]	= { .type = NLA_U32 },
> +	[ATH_TM_ATTR_VERSION_MINOR]	= { .type = NLA_U32 },
> +};
> +
> +static struct ath12k *ath12k_tm_get_ar(struct ath12k_base *ab)
> +{
> +	struct ath12k_pdev *pdev;
> +	struct ath12k *ar;
> +	int i;
> +
> +	for (i = 0; i < ab->num_radios; i++) {
> +		pdev = &ab->pdevs[i];
> +		ar = pdev->ar;
> +
> +		if (ar && ar->state == ATH12K_STATE_TM)
> +			return ar;
> +	}
> +
> +	return NULL;
> +}
> +
> +void ath12k_tm_wmi_event_unsegmented(struct ath12k_base *ab, u32 cmd_id,
> +				     struct sk_buff *skb)
> +{
> +	struct sk_buff *nl_skb;
> +	struct ath12k *ar;
> +
> +	ath12k_dbg(ab, ATH12K_DBG_TESTMODE,
> +		   "testmode event wmi cmd_id %d skb length %d\n",
> +		   cmd_id, skb->len);
> +
> +	ath12k_dbg_dump(ab, ATH12K_DBG_TESTMODE, NULL, "", skb->data, skb->len);
> +
> +	ar = ath12k_tm_get_ar(ab);
> +	if (!ar) {
> +		ath12k_warn(ab, "testmode event not handled due to invalid pdev\n");
> +		return;
> +	}
> +
> +	spin_lock_bh(&ar->data_lock);
> +
> +	nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,
> +						   2 * nla_total_size(sizeof(u32)) +
> +						   nla_total_size(skb->len),
> +						   GFP_ATOMIC);
> +	spin_unlock_bh(&ar->data_lock);
> +
> +	if (!nl_skb) {
> +		ath12k_warn(ab,
> +			    "failed to allocate skb for unsegmented testmode wmi event\n");
> +		return;
> +	}
> +
> +	if (nla_put_u32(nl_skb, ATH_TM_ATTR_CMD, ATH_TM_CMD_WMI) ||
> +	    nla_put_u32(nl_skb, ATH_TM_ATTR_WMI_CMDID, cmd_id) ||
> +	    nla_put(nl_skb, ATH_TM_ATTR_DATA, skb->len, skb->data)) {
> +		ath12k_warn(ab, "failed to populate testmode unsegmented event\n");
> +		kfree_skb(nl_skb);
> +		return;
> +	}
> +
> +	cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
> +}
> +
> +void ath12k_tm_process_event(struct ath12k_base *ab, u32 cmd_id,
> +			     const struct ath12k_wmi_ftm_event *ftm_msg,
> +			     u16 length)
> +{
> +	struct sk_buff *nl_skb;
> +	struct ath12k *ar;
> +	u32 data_pos, pdev_id;
> +	u16 datalen;
> +	u8 total_segments, current_seq;
> +	u8 const *buf_pos;
> +
> +	ath12k_dbg(ab, ATH12K_DBG_TESTMODE,
> +		   "testmode event wmi cmd_id %d ftm event msg %pK datalen %d\n",
> +		   cmd_id, ftm_msg, length);
> +	ath12k_dbg_dump(ab, ATH12K_DBG_TESTMODE, NULL, "", ftm_msg, length);
> +	pdev_id = DP_HW2SW_MACID(le32_to_cpu(ftm_msg->seg_hdr.pdev_id));
> +
> +	if (pdev_id >= ab->num_radios) {
> +		ath12k_warn(ab, "testmode event not handled due to invalid pdev id\n");
> +		return;
> +	}
> +
> +	ar = ab->pdevs[pdev_id].ar;
> +
> +	if (!ar) {
> +		ath12k_warn(ab, "testmode event not handled due to absence of pdev\n");
> +		return;
> +	}
> +
> +	current_seq = le32_get_bits(ftm_msg->seg_hdr.segmentinfo,
> +				    ATH12K_FTM_SEGHDR_CURRENT_SEQ);
> +	total_segments = le32_get_bits(ftm_msg->seg_hdr.segmentinfo,
> +				       ATH12K_FTM_SEGHDR_TOTAL_SEGMENTS);
> +	datalen = length - (sizeof(struct ath12k_wmi_ftm_seg_hdr_params));
> +	buf_pos = ftm_msg->data;
> +
> +	if (current_seq == 0) {
> +		ab->ftm_event_obj.expected_seq = 0;
> +		ab->ftm_event_obj.data_pos = 0;
> +	}
> +
> +	data_pos = ab->ftm_event_obj.data_pos;
> +
> +	if ((data_pos + datalen) > ATH_FTM_EVENT_MAX_BUF_LENGTH) {
> +		ath12k_warn(ab,
> +			    "Invalid event length date_pos[%d] datalen[%d]\n",
> +			    data_pos, datalen);
> +		return;
> +	}
> +
> +	memcpy(&ab->ftm_event_obj.eventdata[data_pos], buf_pos, datalen);
> +	data_pos += datalen;
> +
> +	if (++ab->ftm_event_obj.expected_seq != total_segments) {
> +		ab->ftm_event_obj.data_pos = data_pos;
> +		ath12k_dbg(ab, ATH12K_DBG_TESTMODE,
> +			   "partial data received current_seq[%d], total_seg[%d]\n",
> +			    current_seq, total_segments);
> +		return;
> +	}
> +
> +	ath12k_dbg(ab, ATH12K_DBG_TESTMODE,
> +		   "total data length[%d] = [%d]\n",
> +		   data_pos, ftm_msg->seg_hdr.len);
> +
> +	spin_lock_bh(&ar->data_lock);
> +	nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,
> +						   2 * nla_total_size(sizeof(u32)) +
> +						   nla_total_size(data_pos),
> +						   GFP_ATOMIC);
> +	spin_unlock_bh(&ar->data_lock);
> +
> +	if (!nl_skb) {
> +		ath12k_warn(ab,
> +			    "failed to allocate skb for testmode wmi event\n");
> +		return;
> +	}
> +
> +	if (nla_put_u32(nl_skb, ATH_TM_ATTR_CMD,
> +			ATH_TM_CMD_WMI_FTM) ||
> +	    nla_put_u32(nl_skb, ATH_TM_ATTR_WMI_CMDID, cmd_id) ||
> +	    nla_put(nl_skb, ATH_TM_ATTR_DATA, data_pos,
> +		    &ab->ftm_event_obj.eventdata[0])) {
> +		ath12k_warn(ab, "failed to populate testmode event");
> +		kfree_skb(nl_skb);
> +		return;
> +	}
> +
> +	cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
> +}
> +
> +static int ath12k_tm_cmd_get_version(struct ath12k *ar, struct nlattr *tb[])
> +{
> +	struct sk_buff *skb;
> +
> +	ath12k_dbg(ar->ab, ATH12K_DBG_TESTMODE,
> +		   "testmode cmd get version_major %d version_minor %d\n",
> +		   ATH_TESTMODE_VERSION_MAJOR,
> +		   ATH_TESTMODE_VERSION_MINOR);
> +
> +	spin_lock_bh(&ar->data_lock);
> +	skb = cfg80211_testmode_alloc_reply_skb(ar->hw->wiphy,
> +						2 * nla_total_size(sizeof(u32)));
> +	spin_unlock_bh(&ar->data_lock);
> +
> +	if (!skb)
> +		return -ENOMEM;
> +
> +	if (nla_put_u32(skb, ATH_TM_ATTR_VERSION_MAJOR,
> +			ATH_TESTMODE_VERSION_MAJOR) ||
> +	    nla_put_u32(skb, ATH_TM_ATTR_VERSION_MINOR,
> +			ATH_TESTMODE_VERSION_MINOR)) {
> +		kfree_skb(skb);
> +		return -ENOBUFS;
> +	}
> +
> +	return cfg80211_testmode_reply(skb);
> +}
> +
> +static int ath12k_tm_cmd_process_ftm(struct ath12k *ar, struct nlattr *tb[])
> +{
> +	struct ath12k_wmi_pdev *wmi = ar->wmi;
> +	struct sk_buff *skb;
> +	struct ath12k_wmi_ftm_cmd *ftm_cmd;
> +	int ret = 0;
> +	void *buf;
> +	size_t aligned_len;
> +	u32 cmd_id, buf_len;
> +	u16 chunk_len, total_bytes, num_segments;
> +	u8 segnumber = 0, *bufpos;
> +
> +	mutex_lock(&ar->conf_mutex);
> +	ath12k_dbg(ar->ab, ATH12K_DBG_TESTMODE, "ar->state  %d\n", ar->state);
> +	if (ar->state != ATH12K_STATE_TM) {
> +		ret = -ENETDOWN;
> +		goto out;
> +	}
> +
> +	if (!tb[ATH_TM_ATTR_DATA]) {
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +
> +	buf = nla_data(tb[ATH_TM_ATTR_DATA]);
> +	buf_len = nla_len(tb[ATH_TM_ATTR_DATA]);
> +	cmd_id = WMI_PDEV_UTF_CMDID;
> +	ath12k_dbg(ar->ab, ATH12K_DBG_TESTMODE,
> +		   "testmode cmd wmi cmd_id %d buf %pK buf_len %d\n",
> +		   cmd_id, buf, buf_len);
> +	ath12k_dbg_dump(ar->ab, ATH12K_DBG_TESTMODE, NULL, "", buf, buf_len);
> +	bufpos = buf;
> +	total_bytes = buf_len;
> +	num_segments = total_bytes / MAX_WMI_UTF_LEN;
> +
> +	if (buf_len - (num_segments * MAX_WMI_UTF_LEN))
> +		num_segments++;
> +
> +	while (buf_len) {
> +		if (buf_len > MAX_WMI_UTF_LEN)
> +			chunk_len = MAX_WMI_UTF_LEN;    /* MAX message */
> +		else
> +			chunk_len = buf_len;
> +
> +		skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, (chunk_len +
> +					sizeof(struct ath12k_wmi_ftm_cmd)));
> +
> +		if (!skb) {
> +			ret = -ENOMEM;
> +			goto out;
> +		}
> +
> +		ftm_cmd = (struct ath12k_wmi_ftm_cmd *)skb->data;
> +		aligned_len  = chunk_len + sizeof(struct ath12k_wmi_ftm_seg_hdr_params);
> +		ftm_cmd->tlv_header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_BYTE, aligned_len);
> +		ftm_cmd->seg_hdr.len = cpu_to_le32(total_bytes);
> +		ftm_cmd->seg_hdr.msgref = cpu_to_le32(ar->ftm_msgref);
> +		ftm_cmd->seg_hdr.segmentinfo = le32_encode_bits(num_segments, ATH12K_FTM_SEGHDR_TOTAL_SEGMENTS) |
> +					       le32_encode_bits(segnumber, ATH12K_FTM_SEGHDR_CURRENT_SEQ);

drivers/net/wireless/ath/ath12k/testmode.c:264: line length of 113 
exceeds 90 columns
drivers/net/wireless/ath/ath12k/testmode.c:265: line length of 106 
exceeds 90 columns

> +		segnumber++;
> +		memcpy(&ftm_cmd->data, bufpos, chunk_len);
> +		ret = ath12k_wmi_cmd_send(wmi, skb, cmd_id);
> +
> +		if (ret) {
> +			ath12k_warn(ar->ab, "ftm wmi command fail: %d\n", ret);
> +			kfree_skb(skb);
> +			goto out;
> +		}
> +
> +		buf_len -= chunk_len;
> +		bufpos += chunk_len;
> +	}
> +
> +	++ar->ftm_msgref;
> +out:
> +	mutex_unlock(&ar->conf_mutex);
> +	return ret;
> +}
> +
> +static int ath12k_tm_cmd_testmode_start(struct ath12k *ar, struct nlattr *tb[])
> +{
> +	int ret = 0;
> +
> +	mutex_lock(&ar->conf_mutex);
> +
> +	if (ar->state == ATH12K_STATE_TM) {
> +		ret = -EALREADY;
> +		goto out;
> +	}
> +
> +	if (ar->state != ATH12K_STATE_OFF) {
> +		ret = -EBUSY;
> +		goto out;
> +	}
> +
> +	ar->ab->ftm_event_obj.eventdata = kzalloc(ATH_FTM_EVENT_MAX_BUF_LENGTH,
> +						  GFP_KERNEL);
> +
> +	if (!ar->ab->ftm_event_obj.eventdata) {
> +		ret = -ENOMEM;
> +		goto out;
> +	}
> +
> +	ar->state = ATH12K_STATE_TM;
> +	ar->ftm_msgref = 0;
> +out:
> +	mutex_unlock(&ar->conf_mutex);
> +	return ret;
> +}
> +
> +static int ath12k_tm_cmd_wmi(struct ath12k *ar, struct nlattr *tb[])
> +{
> +	struct ath12k_wmi_pdev *wmi = ar->wmi;
> +	struct sk_buff *skb;
> +	u32 cmd_id, buf_len;
> +	int ret;
> +	void *buf;
> +
> +	mutex_lock(&ar->conf_mutex);
> +
> +	if (!tb[ATH_TM_ATTR_DATA]) {
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +
> +	if (!tb[ATH_TM_ATTR_WMI_CMDID]) {
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +
> +	buf = nla_data(tb[ATH_TM_ATTR_DATA]);
> +	buf_len = nla_len(tb[ATH_TM_ATTR_DATA]);
> +
> +	if (!buf_len) {
> +		ath12k_warn(ar->ab, "No data present in testmode command\n");
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +
> +	cmd_id = nla_get_u32(tb[ATH_TM_ATTR_WMI_CMDID]);
> +
> +	ath12k_dbg(ar->ab, ATH12K_DBG_TESTMODE,
> +		   "testmode cmd wmi cmd_id %d  buf length %d\n",
> +		   cmd_id, buf_len);
> +
> +	ath12k_dbg_dump(ar->ab, ATH12K_DBG_TESTMODE, NULL, "", buf, buf_len);
> +
> +	skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, buf_len);
> +
> +	if (!skb) {
> +		ret = -ENOMEM;
> +		goto out;
> +	}
> +
> +	memcpy(skb->data, buf, buf_len);
> +
> +	ret = ath12k_wmi_cmd_send(wmi, skb, cmd_id);
> +	if (ret) {
> +		dev_kfree_skb(skb);
> +		ath12k_warn(ar->ab, "failed to transmit wmi command (testmode): %d\n",
> +			    ret);
> +		goto out;
> +	}
> +
> +out:
> +	mutex_unlock(&ar->conf_mutex);
> +	return ret;
> +}
> +
> +int ath12k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
> +		  void *data, int len)
> +{
> +	struct ath12k *ar = hw->priv;
> +	struct nlattr *tb[ATH_TM_ATTR_MAX + 1];
> +	struct ath12k_base *ab;
> +	int ret;
> +
> +	ret = nla_parse(tb, ATH_TM_ATTR_MAX, data, len, ath12k_tm_policy,
> +			NULL);
> +	if (ret)
> +		return ret;
> +
> +	if (!tb[ATH_TM_ATTR_CMD])
> +		return -EINVAL;
> +
> +	ab = ar->ab;
> +	switch (nla_get_u32(tb[ATH_TM_ATTR_CMD])) {
> +	case ATH_TM_CMD_WMI:
> +		return ath12k_tm_cmd_wmi(ar, tb);
> +	case ATH_TM_CMD_TESTMODE_START:
> +		return ath12k_tm_cmd_testmode_start(ar, tb);
> +	case ATH_TM_CMD_GET_VERSION:
> +		return ath12k_tm_cmd_get_version(ar, tb);
> +	case ATH_TM_CMD_WMI_FTM:
> +		set_bit(ATH12K_FLAG_FTM_SEGMENTED, &ab->dev_flags);
> +		return ath12k_tm_cmd_process_ftm(ar, tb);
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +}
> diff --git a/drivers/net/wireless/ath/ath12k/testmode.h b/drivers/net/wireless/ath/ath12k/testmode.h
> new file mode 100644
> index 000000000000..5f68a2f44789
> --- /dev/null
> +++ b/drivers/net/wireless/ath/ath12k/testmode.h
> @@ -0,0 +1,42 @@
> +/* SPDX-License-Identifier: BSD-3-Clause-Clear */
> +/*
> + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
> + * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
> + */
> +
> +#include "core.h"
> +#include "hif.h"
> +
> +#ifdef CONFIG_NL80211_TESTMODE
> +
> +void ath12k_tm_wmi_event_unsegmented(struct ath12k_base *ab, u32 cmd_id,
> +				     struct sk_buff *skb);
> +void ath12k_tm_process_event(struct ath12k_base *ab, u32 cmd_id,
> +			     const struct ath12k_wmi_ftm_event *ftm_msg,
> +			     u16 length);
> +int ath12k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
> +		  void *data, int len);
> +
> +#else
> +
> +static inline void ath12k_tm_wmi_event_unsegmented(struct ath12k_base *ab, u32 cmd_id,
> +						   struct sk_buff *skb)
> +{
> +	return;

Please run ath12k-check on your patches
drivers/net/wireless/ath/ath12k/testmode.h:26: void function return 
statements are not generally useful

> +}
> +
> +static inline void ath12k_tm_process_event(struct ath12k_base *ab, u32 cmd_id,
> +					   const struct ath12k_wmi_ftm_event *msg,
> +					   u16 length)
> +{
> +	return;

drivers/net/wireless/ath/ath12k/testmode.h:26: void function return 
statements are not generally useful

> +}
> +
> +static inline int ath12k_tm_cmd(struct ieee80211_hw *hw,
> +				struct ieee80211_vif *vif,
> +				void *data, int len)
> +{
> +	return 0;
> +}
> +
> +#endif
> diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
> index f10e0cfca1ef..da7ed0b6a15f 100644
> --- a/drivers/net/wireless/ath/ath12k/wmi.c
> +++ b/drivers/net/wireless/ath/ath12k/wmi.c
> @@ -19,6 +19,7 @@
>   #include "mac.h"
>   #include "hw.h"
>   #include "peer.h"
> +#include "testmode.h"
>   
>   struct ath12k_wmi_svc_ready_parse {
>   	bool wmi_svc_bitmap_done;
> @@ -6539,6 +6540,33 @@ ath12k_wmi_pdev_dfs_radar_detected_event(struct ath12k_base *ab, struct sk_buff
>   	kfree(tb);
>   }
>   
> +static void ath12k_tm_wmi_event_segmented(struct ath12k_base *ab, u32 cmd_id,
> +					  struct sk_buff *skb)
> +{
> +	const struct ath12k_wmi_ftm_event *ev;
> +	const void **tb;
> +	int ret;
> +	u16 length;
> +
> +	tb = ath12k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
> +
> +	if (IS_ERR(tb)) {
> +		ret = PTR_ERR(tb);
> +		ath12k_warn(ab, "failed to parse ftm event tlv: %d\n", ret);
> +		return;
> +	}
> +
> +	ev = tb[WMI_TAG_ARRAY_BYTE];
> +	if (!ev) {
> +		ath12k_warn(ab, "failed to fetch ftm msg\n");
> +		kfree(tb);
> +		return;
> +	}
> +
> +	length = skb->len - TLV_HDR_SIZE;
> +	ath12k_tm_process_event(ab, cmd_id, ev, length);
> +	kfree(tb);
> +}

drivers/net/wireless/ath/ath12k/wmi.c:6570: Please use a blank line 
after function/struct/union/enum declarations

>   static void
>   ath12k_wmi_pdev_temperature_event(struct ath12k_base *ab,
>   				  struct sk_buff *skb)
> @@ -6772,6 +6800,12 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb)
>   	case WMI_VDEV_DELETE_RESP_EVENTID:
>   		ath12k_vdev_delete_resp_event(ab, skb);
>   		break;
> +	case WMI_PDEV_UTF_EVENTID:
> +		if (test_bit(ATH12K_FLAG_FTM_SEGMENTED, &ab->dev_flags))
> +			ath12k_tm_wmi_event_segmented(ab, id, skb);
> +		else
> +			ath12k_tm_wmi_event_unsegmented(ab, id, skb);
> +		break;
>   	/* TODO: Add remaining events */

since you have to respin this patch, can you please remove this 
unnecessary comment? Adding specific event handling is just part of 
normal feature development, not a TODO

>   	default:
>   		ath12k_dbg(ab, ATH12K_DBG_WMI, "Unknown eventid: 0x%x\n", id);
> diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h
> index dcbae5258b35..567ede091d48 100644
> --- a/drivers/net/wireless/ath/ath12k/wmi.h
> +++ b/drivers/net/wireless/ath/ath12k/wmi.h
> @@ -3486,6 +3486,26 @@ struct wmi_get_pdev_temperature_cmd {
>   	__le32 pdev_id;
>   } __packed;
>   
> +#define MAX_WMI_UTF_LEN 252
> +
> +struct ath12k_wmi_ftm_seg_hdr_params {
> +	__le32 len;
> +	__le32 msgref;
> +	__le32 segmentinfo;
> +	__le32 pdev_id;
> +} __packed;
> +
> +struct ath12k_wmi_ftm_cmd {
> +	__le32 tlv_header;
> +	struct ath12k_wmi_ftm_seg_hdr_params seg_hdr;
> +	u8 data[];
> +} __packed;
> +
> +struct ath12k_wmi_ftm_event {
> +	struct ath12k_wmi_ftm_seg_hdr_params seg_hdr;
> +	u8 data[];
> +} __packed;
> +
>   #define WMI_BEACON_TX_BUFFER_SIZE	512
>   
>   struct wmi_bcn_tmpl_cmd {




More information about the ath12k mailing list