Commit 2f6adeaf authored by Johannes Berg's avatar Johannes Berg
Browse files

Merge tag 'ath-next-20251103' of...

Merge tag 'ath-next-20251103' of git://git.kernel.org/pub/scm/linux/kernel/git/ath/ath

 into wireless-next

Jeff Johnson says:
==================
ath.git patches for v6.19

Highlights for some specific drivers include:

ath10k:
Add support for Factory Test TLV commands

ath11k:
Add support for Tx Power insertion

ath12k:
Add support for BSS color change

And of course there is the usual set of cleanups and bug fixes across
the entire family of "ath" drivers.

We do expect to have one more pull request before the v6.19 merge
window to pull in the refactored ath12k driver from the ath12k-ng
branch.
==================

Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parents dcbc94c1 059ca8fd
Loading
Loading
Loading
Loading
+11 −17
Original line number Diff line number Diff line
@@ -3,7 +3,6 @@
 * Copyright (c) 2005-2011 Atheros Communications Inc.
 * Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
 * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
 * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
 * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
 */

@@ -1187,7 +1186,7 @@ static int ath10k_download_fw(struct ath10k *ar)
	u32 address, data_len;
	const void *data;
	int ret;
	struct pm_qos_request latency_qos;
	struct pm_qos_request latency_qos = {};

	address = ar->hw_params.patch_load_addr;

@@ -1221,7 +1220,6 @@ static int ath10k_download_fw(struct ath10k *ar)
			    ret);
	}

	memset(&latency_qos, 0, sizeof(latency_qos));
	cpu_latency_qos_add_request(&latency_qos, 0);

	ret = ath10k_bmi_fast_download(ar, address, data, data_len);
@@ -2493,8 +2491,9 @@ static int ath10k_init_hw_params(struct ath10k *ar)
	return 0;
}

static bool ath10k_core_needs_recovery(struct ath10k *ar)
static void ath10k_core_recovery_check_work(struct work_struct *work)
{
	struct ath10k *ar = container_of(work, struct ath10k, recovery_check_work);
	long time_left;

	/* Sometimes the recovery will fail and then the next all recovery fail,
@@ -2504,7 +2503,7 @@ static bool ath10k_core_needs_recovery(struct ath10k *ar)
		ath10k_err(ar, "consecutive fail %d times, will shutdown driver!",
			   atomic_read(&ar->fail_cont_count));
		ar->state = ATH10K_STATE_WEDGED;
		return false;
		return;
	}

	ath10k_dbg(ar, ATH10K_DBG_BOOT, "total recovery count: %d", ++ar->recovery_count);
@@ -2518,27 +2517,24 @@ static bool ath10k_core_needs_recovery(struct ath10k *ar)
							ATH10K_RECOVERY_TIMEOUT_HZ);
		if (time_left) {
			ath10k_warn(ar, "previous recovery succeeded, skip this!\n");
			return false;
			return;
		}

		/* Record the continuous recovery fail count when recovery failed. */
		atomic_inc(&ar->fail_cont_count);

		/* Avoid having multiple recoveries at the same time. */
		return false;
		return;
	}

	atomic_inc(&ar->pending_recovery);

	return true;
	queue_work(ar->workqueue, &ar->restart_work);
}

void ath10k_core_start_recovery(struct ath10k *ar)
{
	if (!ath10k_core_needs_recovery(ar))
		return;

	queue_work(ar->workqueue, &ar->restart_work);
	/* Use workqueue_aux to avoid blocking recovery tracking */
	queue_work(ar->workqueue_aux, &ar->recovery_check_work);
}
EXPORT_SYMBOL(ath10k_core_start_recovery);

@@ -3356,7 +3352,7 @@ EXPORT_SYMBOL(ath10k_core_stop);
 */
static int ath10k_core_probe_fw(struct ath10k *ar)
{
	struct bmi_target_info target_info;
	struct bmi_target_info target_info = {};
	int ret = 0;

	ret = ath10k_hif_power_up(ar, ATH10K_FIRMWARE_MODE_NORMAL);
@@ -3367,7 +3363,6 @@ static int ath10k_core_probe_fw(struct ath10k *ar)

	switch (ar->hif.bus) {
	case ATH10K_BUS_SDIO:
		memset(&target_info, 0, sizeof(target_info));
		ret = ath10k_bmi_get_target_info_sdio(ar, &target_info);
		if (ret) {
			ath10k_err(ar, "could not get target info (%d)\n", ret);
@@ -3379,7 +3374,6 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
	case ATH10K_BUS_PCI:
	case ATH10K_BUS_AHB:
	case ATH10K_BUS_USB:
		memset(&target_info, 0, sizeof(target_info));
		ret = ath10k_bmi_get_target_info(ar, &target_info);
		if (ret) {
			ath10k_err(ar, "could not get target info (%d)\n", ret);
@@ -3389,7 +3383,6 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
		ar->hw->wiphy->hw_version = target_info.version;
		break;
	case ATH10K_BUS_SNOC:
		memset(&target_info, 0, sizeof(target_info));
		ret = ath10k_hif_get_target_info(ar, &target_info);
		if (ret) {
			ath10k_err(ar, "could not get target info (%d)\n", ret);
@@ -3734,6 +3727,7 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,

	INIT_WORK(&ar->register_work, ath10k_core_register_work);
	INIT_WORK(&ar->restart_work, ath10k_core_restart);
	INIT_WORK(&ar->recovery_check_work, ath10k_core_recovery_check_work);
	INIT_WORK(&ar->set_coverage_class_work,
		  ath10k_core_set_coverage_class_work);

+5 −1
Original line number Diff line number Diff line
@@ -3,7 +3,6 @@
 * Copyright (c) 2005-2011 Atheros Communications Inc.
 * Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
 * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
 * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
 * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
 */

@@ -1208,6 +1207,7 @@ struct ath10k {

	struct work_struct register_work;
	struct work_struct restart_work;
	struct work_struct recovery_check_work;
	struct work_struct bundle_tx_work;
	struct work_struct tx_complete_work;

@@ -1259,9 +1259,13 @@ struct ath10k {
	struct {
		/* protected by conf_mutex */
		struct ath10k_fw_components utf_mode_fw;
		u8 ftm_msgref;

		/* protected by data_lock */
		bool utf_monitor;
		u32 data_pos;
		u32 expected_seq;
		u8 *eventdata;
	} testmode;

	struct {
+1 −1
Original line number Diff line number Diff line
@@ -3,7 +3,6 @@
 * Copyright (c) 2005-2011 Atheros Communications Inc.
 * Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
 * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
 * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
 * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
 */

@@ -5428,6 +5427,7 @@ static void ath10k_stop(struct ieee80211_hw *hw, bool suspend)
	cancel_work_sync(&ar->set_coverage_class_work);
	cancel_delayed_work_sync(&ar->scan.timeout);
	cancel_work_sync(&ar->restart_work);
	cancel_work_sync(&ar->recovery_check_work);
}

static int ath10k_config_ps(struct ath10k *ar)
+223 −30
Original line number Diff line number Diff line
// SPDX-License-Identifier: ISC
/*
 * Copyright (c) 2014-2017 Qualcomm Atheros, Inc.
 * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
 */

#include "testmode.h"
@@ -10,12 +11,17 @@

#include "debug.h"
#include "wmi.h"
#include "wmi-tlv.h"
#include "hif.h"
#include "hw.h"
#include "core.h"

#include "testmode_i.h"

#define ATH10K_FTM_SEG_NONE			((u32)-1)
#define ATH10K_FTM_SEGHDR_CURRENT_SEQ		GENMASK(3, 0)
#define ATH10K_FTM_SEGHDR_TOTAL_SEGMENTS	GENMASK(7, 4)

static const struct nla_policy ath10k_tm_policy[ATH10K_TM_ATTR_MAX + 1] = {
	[ATH10K_TM_ATTR_CMD]		= { .type = NLA_U32 },
	[ATH10K_TM_ATTR_DATA]		= { .type = NLA_BINARY,
@@ -25,41 +31,19 @@ static const struct nla_policy ath10k_tm_policy[ATH10K_TM_ATTR_MAX + 1] = {
	[ATH10K_TM_ATTR_VERSION_MINOR]	= { .type = NLA_U32 },
};

/* Returns true if callee consumes the skb and the skb should be discarded.
 * Returns false if skb is not used. Does not sleep.
 */
bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)
static void ath10k_tm_event_unsegmented(struct ath10k *ar, u32 cmd_id,
					struct sk_buff *skb)
{
	struct sk_buff *nl_skb;
	bool consumed;
	int ret;

	ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
		   "testmode event wmi cmd_id %d skb %p skb->len %d\n",
		   cmd_id, skb, skb->len);

	ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", skb->data, skb->len);

	spin_lock_bh(&ar->data_lock);

	if (!ar->testmode.utf_monitor) {
		consumed = false;
		goto out;
	}

	/* Only testmode.c should be handling events from utf firmware,
	 * otherwise all sort of problems will arise as mac80211 operations
	 * are not initialised.
	 */
	consumed = true;

	nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,
						   2 * sizeof(u32) + skb->len,
						   GFP_ATOMIC);
	if (!nl_skb) {
		ath10k_warn(ar,
			    "failed to allocate skb for testmode wmi event\n");
		goto out;
		return;
	}

	ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_CMD, ATH10K_TM_CMD_WMI);
@@ -68,7 +52,7 @@ bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)
			    "failed to put testmode wmi event cmd attribute: %d\n",
			    ret);
		kfree_skb(nl_skb);
		goto out;
		return;
	}

	ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_WMI_CMDID, cmd_id);
@@ -77,7 +61,7 @@ bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)
			    "failed to put testmode wmi event cmd_id: %d\n",
			    ret);
		kfree_skb(nl_skb);
		goto out;
		return;
	}

	ret = nla_put(nl_skb, ATH10K_TM_ATTR_DATA, skb->len, skb->data);
@@ -86,10 +70,122 @@ bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)
			    "failed to copy skb to testmode wmi event: %d\n",
			    ret);
		kfree_skb(nl_skb);
		goto out;
		return;
	}

	cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
}

static void ath10k_tm_event_segmented(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)
{
	struct wmi_ftm_cmd *ftm = (struct wmi_ftm_cmd *)skb->data;
	u8 total_segments, current_seq;
	struct sk_buff *nl_skb;
	u8 const *buf_pos;
	u16 datalen;
	u32 data_pos;
	int ret;

	if (skb->len < sizeof(*ftm)) {
		ath10k_warn(ar, "Invalid ftm event length: %d\n", skb->len);
		return;
	}

	current_seq = FIELD_GET(ATH10K_FTM_SEGHDR_CURRENT_SEQ,
				__le32_to_cpu(ftm->seg_hdr.segmentinfo));
	total_segments = FIELD_GET(ATH10K_FTM_SEGHDR_TOTAL_SEGMENTS,
				   __le32_to_cpu(ftm->seg_hdr.segmentinfo));
	datalen = skb->len - sizeof(*ftm);
	buf_pos = ftm->data;

	if (current_seq == 0) {
		ar->testmode.expected_seq = 0;
		ar->testmode.data_pos = 0;
	}

	data_pos = ar->testmode.data_pos;

	if ((data_pos + datalen) > ATH_FTM_EVENT_MAX_BUF_LENGTH) {
		ath10k_warn(ar, "Invalid ftm event length at %u: %u\n",
			    data_pos, datalen);
		ret = -EINVAL;
		return;
	}

	memcpy(&ar->testmode.eventdata[data_pos], buf_pos, datalen);
	data_pos += datalen;

	if (++ar->testmode.expected_seq != total_segments) {
		ar->testmode.data_pos = data_pos;
		ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "partial data received %u/%u\n",
			   current_seq + 1, total_segments);
		return;
	}

	ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "total data length %u\n", data_pos);

	nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,
						   2 * sizeof(u32) + data_pos,
						   GFP_ATOMIC);
	if (!nl_skb) {
		ath10k_warn(ar, "failed to allocate skb for testmode wmi event\n");
		return;
	}

	ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_CMD, ATH10K_TM_CMD_TLV);
	if (ret) {
		ath10k_warn(ar, "failed to put testmode wmi event attribute: %d\n", ret);
		kfree_skb(nl_skb);
		return;
	}

	ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_WMI_CMDID, cmd_id);
	if (ret) {
		ath10k_warn(ar, "failed to put testmode wmi event cmd_id: %d\n", ret);
		kfree_skb(nl_skb);
		return;
	}

	ret = nla_put(nl_skb, ATH10K_TM_ATTR_DATA, data_pos, &ar->testmode.eventdata[0]);
	if (ret) {
		ath10k_warn(ar, "failed to copy skb to testmode wmi event: %d\n", ret);
		kfree_skb(nl_skb);
		return;
	}

	cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
}

/* Returns true if callee consumes the skb and the skb should be discarded.
 * Returns false if skb is not used. Does not sleep.
 */
bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)
{
	bool consumed;

	ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
		   "testmode event wmi cmd_id %d skb %p skb->len %d\n",
		   cmd_id, skb, skb->len);

	ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", skb->data, skb->len);

	spin_lock_bh(&ar->data_lock);

	if (!ar->testmode.utf_monitor) {
		consumed = false;
		goto out;
	}

	/* Only testmode.c should be handling events from utf firmware,
	 * otherwise all sort of problems will arise as mac80211 operations
	 * are not initialised.
	 */
	consumed = true;

	if (ar->testmode.expected_seq != ATH10K_FTM_SEG_NONE)
		ath10k_tm_event_segmented(ar, cmd_id, skb);
	else
		ath10k_tm_event_unsegmented(ar, cmd_id, skb);

out:
	spin_unlock_bh(&ar->data_lock);
@@ -281,12 +377,18 @@ static int ath10k_tm_cmd_utf_start(struct ath10k *ar, struct nlattr *tb[])
		goto err_release_utf_mode_fw;
	}

	ar->testmode.eventdata = kzalloc(ATH_FTM_EVENT_MAX_BUF_LENGTH, GFP_KERNEL);
	if (!ar->testmode.eventdata) {
		ret = -ENOMEM;
		goto err_power_down;
	}

	ret = ath10k_core_start(ar, ATH10K_FIRMWARE_MODE_UTF,
				&ar->testmode.utf_mode_fw);
	if (ret) {
		ath10k_err(ar, "failed to start core (testmode): %d\n", ret);
		ar->state = ATH10K_STATE_OFF;
		goto err_power_down;
		goto err_release_eventdata;
	}

	ar->state = ATH10K_STATE_UTF;
@@ -302,6 +404,10 @@ static int ath10k_tm_cmd_utf_start(struct ath10k *ar, struct nlattr *tb[])

	return 0;

err_release_eventdata:
	kfree(ar->testmode.eventdata);
	ar->testmode.eventdata = NULL;

err_power_down:
	ath10k_hif_power_down(ar);

@@ -341,6 +447,9 @@ static void __ath10k_tm_cmd_utf_stop(struct ath10k *ar)
	release_firmware(ar->testmode.utf_mode_fw.fw_file.firmware);
	ar->testmode.utf_mode_fw.fw_file.firmware = NULL;

	kfree(ar->testmode.eventdata);
	ar->testmode.eventdata = NULL;

	ar->state = ATH10K_STATE_OFF;
}

@@ -424,6 +533,85 @@ static int ath10k_tm_cmd_wmi(struct ath10k *ar, struct nlattr *tb[])
	return ret;
}

static int ath10k_tm_cmd_tlv(struct ath10k *ar, struct nlattr *tb[])
{
	u16 total_bytes, num_segments;
	u32 cmd_id, buf_len;
	u8 segnumber = 0;
	u8 *bufpos;
	void *buf;
	int ret;

	mutex_lock(&ar->conf_mutex);

	if (ar->state != ATH10K_STATE_UTF) {
		ret = -ENETDOWN;
		goto out;
	}

	buf = nla_data(tb[ATH10K_TM_ATTR_DATA]);
	buf_len = nla_len(tb[ATH10K_TM_ATTR_DATA]);
	cmd_id = WMI_PDEV_UTF_CMDID;

	ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
		   "cmd wmi ftm cmd_id %d buffer length %d\n",
		   cmd_id, buf_len);
	ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", buf, buf_len);

	bufpos = buf;
	total_bytes = buf_len;
	num_segments = total_bytes / MAX_WMI_UTF_LEN;
	ar->testmode.expected_seq = 0;

	if (buf_len - (num_segments * MAX_WMI_UTF_LEN))
		num_segments++;

	while (buf_len) {
		u16 chunk_len = min_t(u16, buf_len, MAX_WMI_UTF_LEN);
		struct wmi_ftm_cmd *ftm_cmd;
		struct sk_buff *skb;
		u32 hdr_info;
		u8 seginfo;

		skb = ath10k_wmi_alloc_skb(ar, (chunk_len +
					   sizeof(struct wmi_ftm_cmd)));
		if (!skb) {
			ret = -ENOMEM;
			goto out;
		}

		ftm_cmd = (struct wmi_ftm_cmd *)skb->data;
		hdr_info = FIELD_PREP(WMI_TLV_TAG, WMI_TLV_TAG_ARRAY_BYTE) |
			   FIELD_PREP(WMI_TLV_LEN, (chunk_len +
				      sizeof(struct wmi_ftm_seg_hdr)));
		ftm_cmd->tlv_header = __cpu_to_le32(hdr_info);
		ftm_cmd->seg_hdr.len = __cpu_to_le32(total_bytes);
		ftm_cmd->seg_hdr.msgref = __cpu_to_le32(ar->testmode.ftm_msgref);
		seginfo = FIELD_PREP(ATH10K_FTM_SEGHDR_TOTAL_SEGMENTS, num_segments) |
			  FIELD_PREP(ATH10K_FTM_SEGHDR_CURRENT_SEQ, segnumber);
		ftm_cmd->seg_hdr.segmentinfo = __cpu_to_le32(seginfo);
		segnumber++;

		memcpy(&ftm_cmd->data, bufpos, chunk_len);

		ret = ath10k_wmi_cmd_send(ar, skb, cmd_id);
		if (ret) {
			ath10k_warn(ar, "failed to send wmi ftm command: %d\n", ret);
			goto out;
		}

		buf_len -= chunk_len;
		bufpos += chunk_len;
	}

	ar->testmode.ftm_msgref++;
	ret = 0;

out:
	mutex_unlock(&ar->conf_mutex);
	return ret;
}

int ath10k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
		  void *data, int len)
{
@@ -439,9 +627,14 @@ int ath10k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
	if (!tb[ATH10K_TM_ATTR_CMD])
		return -EINVAL;

	ar->testmode.expected_seq = ATH10K_FTM_SEG_NONE;

	switch (nla_get_u32(tb[ATH10K_TM_ATTR_CMD])) {
	case ATH10K_TM_CMD_GET_VERSION:
		if (!tb[ATH10K_TM_ATTR_DATA])
			return ath10k_tm_cmd_get_version(ar, tb);
		else /* ATH10K_TM_CMD_TLV */
			return ath10k_tm_cmd_tlv(ar, tb);
	case ATH10K_TM_CMD_UTF_START:
		return ath10k_tm_cmd_utf_start(ar, tb);
	case ATH10K_TM_CMD_UTF_STOP:
+15 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: ISC */
/*
 * Copyright (c) 2014,2017 Qualcomm Atheros, Inc.
 * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
 */

/* "API" level of the ath10k testmode interface. Bump it after every
@@ -14,6 +15,7 @@
#define ATH10K_TESTMODE_VERSION_MINOR 0

#define ATH10K_TM_DATA_MAX_LEN		5000
#define ATH_FTM_EVENT_MAX_BUF_LENGTH	2048

enum ath10k_tm_attr {
	__ATH10K_TM_ATTR_INVALID	= 0,
@@ -57,4 +59,17 @@ enum ath10k_tm_cmd {
	 * ATH10K_TM_ATTR_DATA.
	 */
	ATH10K_TM_CMD_WMI = 3,

	/* The command used to transmit a test command to the firmware
	 * and the event to receive test events from the firmware. The data
	 * received only contain the TLV payload, need to add the tlv header
	 * and send the cmd to firmware with command id WMI_PDEV_UTF_CMDID.
	 * The data payload size could be large and the driver needs to
	 * send segmented data to firmware.
	 *
	 * This legacy testmode command shares the same value as the get-version
	 * command. To distinguish between them, we check whether the data attribute
	 * is present.
	 */
	ATH10K_TM_CMD_TLV = ATH10K_TM_CMD_GET_VERSION,
};
Loading