123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850 |
- From b43310e44edc823a7f02af1e1e2b4e8a9abc7d91 Mon Sep 17 00:00:00 2001
- From: Govindaraj Saminathan <quic_gsaminat@quicinc.com>
- Date: Fri, 26 May 2023 12:41:07 +0300
- Subject: [PATCH 83/84] wifi: ath11k: factory test mode support
- Add support to process factory test mode commands (FTM) for calibration.
- By default firmware start with NORMAL 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.
- When start command ATH11K_TM_CMD_TESTMODE_START is received, ar->state
- is set to Test Mode. If the FTM command or event length is greater
- than 256 bytes, it will be broken down into multiple segments and
- encoded with TLV header if it is segmented commands, else it is sent
- to firmware as it is.
- On receiving UTF event from firmware, if it is segmented event, the driver
- will wait until it receives all the segments and notify the complete
- data to user application. In case the segmented sequence are missed or
- lost from the firmware, driver will skip the already received partial data.
- In case of unsegmented UTF event from firmware, driver notifies the
- data to the user application as it comes. Applications handles
- the data further.
- Command to boot in ftm mode:
- insmod ath11k ftm_mode=1
- Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1
- Signed-off-by: Govindaraj Saminathan <quic_gsaminat@quicinc.com>
- Co-developed-by: Sowmiya Sree Elavalagan <quic_ssreeela@quicinc.com>
- Signed-off-by: Sowmiya Sree Elavalagan <quic_ssreeela@quicinc.com>
- Signed-off-by: Raj Kumar Bhagat <quic_rajkbhag@quicinc.com>
- Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
- Link: https://lore.kernel.org/r/20230517135934.16408-4-quic_rajkbhag@quicinc.com
- ---
- drivers/net/wireless/ath/ath11k/ahb.c | 3 +-
- drivers/net/wireless/ath/ath11k/core.c | 21 +-
- drivers/net/wireless/ath/ath11k/core.h | 16 +-
- drivers/net/wireless/ath/ath11k/debug.h | 1 +
- drivers/net/wireless/ath/ath11k/mac.c | 11 +-
- drivers/net/wireless/ath/ath11k/pci.c | 3 +-
- drivers/net/wireless/ath/ath11k/testmode.c | 350 ++++++++++++++++++-
- drivers/net/wireless/ath/ath11k/testmode.h | 6 +
- drivers/net/wireless/ath/ath11k/testmode_i.h | 18 +-
- drivers/net/wireless/ath/ath11k/wmi.c | 11 +-
- drivers/net/wireless/ath/ath11k/wmi.h | 22 ++
- drivers/net/wireless/ath/ath11k/wow.c | 3 +-
- 12 files changed, 444 insertions(+), 21 deletions(-)
- --- a/drivers/net/wireless/ath/ath11k/ahb.c
- +++ b/drivers/net/wireless/ath/ath11k/ahb.c
- @@ -1,7 +1,7 @@
- // SPDX-License-Identifier: BSD-3-Clause-Clear
- /*
- * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
- + * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
- */
-
- #include <linux/module.h>
- @@ -1155,6 +1155,7 @@ static int ath11k_ahb_probe(struct platf
- ab->hif.ops = hif_ops;
- ab->pdev = pdev;
- ab->hw_rev = hw_rev;
- + ab->fw_mode = ATH11K_FIRMWARE_MODE_NORMAL;
- platform_set_drvdata(pdev, ab);
-
- ret = ath11k_pcic_register_pci_ops(ab, pci_ops);
- --- a/drivers/net/wireless/ath/ath11k/core.c
- +++ b/drivers/net/wireless/ath/ath11k/core.c
- @@ -1,7 +1,7 @@
- // SPDX-License-Identifier: BSD-3-Clause-Clear
- /*
- * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
- + * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
- */
-
- #include <linux/module.h>
- @@ -32,6 +32,10 @@ module_param_named(frame_mode, ath11k_fr
- MODULE_PARM_DESC(frame_mode,
- "Datapath frame mode (0: raw, 1: native wifi (default), 2: ethernet)");
-
- +bool ath11k_ftm_mode;
- +module_param_named(ftm_mode, ath11k_ftm_mode, bool, 0444);
- +MODULE_PARM_DESC(ftm_mode, "Boots up in factory test mode");
- +
- static const struct ath11k_hw_params ath11k_hw_params[] = {
- {
- .hw_rev = ATH11K_HW_IPQ8074,
- @@ -1381,6 +1385,11 @@ static int ath11k_core_soc_create(struct
- {
- int ret;
-
- + if (ath11k_ftm_mode) {
- + ab->fw_mode = ATH11K_FIRMWARE_MODE_FTM;
- + ath11k_info(ab, "Booting in factory test mode\n");
- + }
- +
- ret = ath11k_qmi_init_service(ab);
- if (ret) {
- ath11k_err(ab, "failed to initialize qmi :%d\n", ret);
- @@ -1607,7 +1616,7 @@ int ath11k_core_qmi_firmware_ready(struc
- {
- int ret;
-
- - ret = ath11k_core_start_firmware(ab, ATH11K_FIRMWARE_MODE_NORMAL);
- + ret = ath11k_core_start_firmware(ab, ab->fw_mode);
- if (ret) {
- ath11k_err(ab, "failed to start firmware: %d\n", ret);
- return ret;
- @@ -1772,7 +1781,8 @@ void ath11k_core_pre_reconfigure_recover
- for (i = 0; i < ab->num_radios; i++) {
- pdev = &ab->pdevs[i];
- ar = pdev->ar;
- - if (!ar || ar->state == ATH11K_STATE_OFF)
- + if (!ar || ar->state == ATH11K_STATE_OFF ||
- + ar->state == ATH11K_STATE_FTM)
- continue;
-
- ieee80211_stop_queues(ar->hw);
- @@ -1841,7 +1851,12 @@ static void ath11k_core_post_reconfigure
- ath11k_warn(ab,
- "device is wedged, will not restart radio %d\n", i);
- break;
- + case ATH11K_STATE_FTM:
- + ath11k_dbg(ab, ATH11K_DBG_TESTMODE,
- + "fw mode reset done radio %d\n", i);
- + break;
- }
- +
- mutex_unlock(&ar->conf_mutex);
- }
- complete(&ab->driver_recovery);
- --- a/drivers/net/wireless/ath/ath11k/core.h
- +++ b/drivers/net/wireless/ath/ath11k/core.h
- @@ -1,7 +1,7 @@
- /* SPDX-License-Identifier: BSD-3-Clause-Clear */
- /*
- * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
- + * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
- */
-
- #ifndef ATH11K_CORE_H
- @@ -52,6 +52,7 @@
- #define ATH11K_SMBIOS_BDF_EXT_MAGIC "BDF_"
-
- extern unsigned int ath11k_frame_mode;
- +extern bool ath11k_ftm_mode;
-
- #define ATH11K_SCAN_TIMEOUT_HZ (20 * HZ)
-
- @@ -277,6 +278,7 @@ enum ath11k_dev_flags {
- ATH11K_FLAG_FIXED_MEM_RGN,
- ATH11K_FLAG_DEVICE_INIT_DONE,
- ATH11K_FLAG_MULTI_MSI_VECTORS,
- + ATH11K_FLAG_FTM_SEGMENTED,
- };
-
- enum ath11k_monitor_flags {
- @@ -530,6 +532,7 @@ enum ath11k_state {
- ATH11K_STATE_RESTARTING,
- ATH11K_STATE_RESTARTED,
- ATH11K_STATE_WEDGED,
- + ATH11K_STATE_FTM,
- /* Add other states as required */
- };
-
- @@ -709,6 +712,8 @@ struct ath11k {
- u32 last_ppdu_id;
- u32 cached_ppdu_id;
- int monitor_vdev_id;
- + struct completion fw_mode_reset;
- + u8 ftm_msgref;
- #ifdef CPTCFG_ATH11K_DEBUGFS
- struct ath11k_debug debug;
- #endif
- @@ -838,6 +843,7 @@ struct ath11k_msi_config {
- /* Master structure to hold the hw data which may be used in core module */
- struct ath11k_base {
- enum ath11k_hw_rev hw_rev;
- + enum ath11k_firmware_mode fw_mode;
- struct platform_device *pdev;
- struct device *dev;
- struct ath11k_qmi qmi;
- @@ -978,6 +984,14 @@ struct ath11k_base {
- const struct ath11k_pci_ops *ops;
- } pci;
-
- +#ifdef CPTCFG_NL80211_TESTMODE
- + struct {
- + u32 data_pos;
- + u32 expected_seq;
- + u8 *eventdata;
- + } testmode;
- +#endif
- +
- /* must be last */
- u8 drv_priv[] __aligned(sizeof(void *));
- };
- --- a/drivers/net/wireless/ath/ath11k/debug.h
- +++ b/drivers/net/wireless/ath/ath11k/debug.h
- @@ -1,6 +1,7 @@
- /* SPDX-License-Identifier: BSD-3-Clause-Clear */
- /*
- * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
- */
-
- #ifndef _ATH11K_DEBUG_H_
- --- a/drivers/net/wireless/ath/ath11k/mac.c
- +++ b/drivers/net/wireless/ath/ath11k/mac.c
- @@ -643,7 +643,10 @@ struct ath11k *ath11k_mac_get_ar_by_pdev
- return NULL;
-
- for (i = 0; i < ab->num_radios; i++) {
- - pdev = rcu_dereference(ab->pdevs_active[i]);
- + if (ab->fw_mode == ATH11K_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);
- @@ -6271,6 +6274,11 @@ static int ath11k_mac_op_start(struct ie
- struct ath11k_pdev *pdev = ar->pdev;
- int ret;
-
- + if (ath11k_ftm_mode) {
- + ath11k_warn(ab, "mac operations not supported in factory test mode\n");
- + return -EOPNOTSUPP;
- + }
- +
- ath11k_mac_drain_tx(ar);
- mutex_lock(&ar->conf_mutex);
-
- @@ -6285,6 +6293,7 @@ static int ath11k_mac_op_start(struct ie
- case ATH11K_STATE_RESTARTED:
- case ATH11K_STATE_WEDGED:
- case ATH11K_STATE_ON:
- + case ATH11K_STATE_FTM:
- WARN_ON(1);
- ret = -EINVAL;
- goto err;
- --- a/drivers/net/wireless/ath/ath11k/pci.c
- +++ b/drivers/net/wireless/ath/ath11k/pci.c
- @@ -1,7 +1,7 @@
- // SPDX-License-Identifier: BSD-3-Clause-Clear
- /*
- * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
- - * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved.
- + * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
- */
-
- #include <linux/module.h>
- @@ -745,6 +745,7 @@ static int ath11k_pci_probe(struct pci_d
- ab_pci->ab = ab;
- ab_pci->pdev = pdev;
- ab->hif.ops = &ath11k_pci_hif_ops;
- + ab->fw_mode = ATH11K_FIRMWARE_MODE_NORMAL;
- pci_set_drvdata(pdev, ab);
- spin_lock_init(&ab_pci->window_lock);
-
- --- a/drivers/net/wireless/ath/ath11k/testmode.c
- +++ b/drivers/net/wireless/ath/ath11k/testmode.c
- @@ -12,6 +12,9 @@
- #include "core.h"
- #include "testmode_i.h"
-
- +#define ATH11K_FTM_SEGHDR_CURRENT_SEQ GENMASK(3, 0)
- +#define ATH11K_FTM_SEGHDR_TOTAL_SEGMENTS GENMASK(7, 4)
- +
- static const struct nla_policy ath11k_tm_policy[ATH11K_TM_ATTR_MAX + 1] = {
- [ATH11K_TM_ATTR_CMD] = { .type = NLA_U32 },
- [ATH11K_TM_ATTR_DATA] = { .type = NLA_BINARY,
- @@ -21,13 +24,217 @@ static const struct nla_policy ath11k_tm
- [ATH11K_TM_ATTR_VERSION_MINOR] = { .type = NLA_U32 },
- };
-
- +static struct ath11k *ath11k_tm_get_ar(struct ath11k_base *ab)
- +{
- + struct ath11k_pdev *pdev;
- + struct ath11k *ar = NULL;
- + int i;
- +
- + for (i = 0; i < ab->num_radios; i++) {
- + pdev = &ab->pdevs[i];
- + ar = pdev->ar;
- +
- + if (ar && ar->state == ATH11K_STATE_FTM)
- + break;
- + }
- +
- + return ar;
- +}
- +
- +/* This function handles unsegmented events. Data in various events are aggregated
- + * in application layer, this event is unsegmented from host perspective.
- + */
- +static void ath11k_tm_wmi_event_unsegmented(struct ath11k_base *ab, u32 cmd_id,
- + struct sk_buff *skb)
- +{
- + struct sk_buff *nl_skb;
- + struct ath11k *ar;
- +
- + ath11k_dbg(ab, ATH11K_DBG_TESTMODE,
- + "event wmi cmd_id %d skb length %d\n",
- + cmd_id, skb->len);
- + ath11k_dbg_dump(ab, ATH11K_DBG_TESTMODE, NULL, "", skb->data, skb->len);
- +
- + ar = ath11k_tm_get_ar(ab);
- + if (!ar) {
- + ath11k_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);
- + if (!nl_skb) {
- + ath11k_warn(ab,
- + "failed to allocate skb for unsegmented testmode wmi event\n");
- + goto out;
- + }
- +
- + if (nla_put_u32(nl_skb, ATH11K_TM_ATTR_CMD, ATH11K_TM_CMD_WMI) ||
- + nla_put_u32(nl_skb, ATH11K_TM_ATTR_WMI_CMDID, cmd_id) ||
- + nla_put(nl_skb, ATH11K_TM_ATTR_DATA, skb->len, skb->data)) {
- + ath11k_warn(ab, "failed to populate testmode unsegmented event\n");
- + kfree_skb(nl_skb);
- + goto out;
- + }
- +
- + cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
- + spin_unlock_bh(&ar->data_lock);
- + return;
- +
- +out:
- + spin_unlock_bh(&ar->data_lock);
- + ath11k_warn(ab, "Failed to send testmode event to higher layers\n");
- +}
- +
- +/* This function handles segmented events. Data of various events received
- + * from firmware is aggregated and sent to application layer
- + */
- +static int ath11k_tm_process_event(struct ath11k_base *ab, u32 cmd_id,
- + const struct wmi_ftm_event_msg *ftm_msg,
- + u16 length)
- +{
- + struct sk_buff *nl_skb;
- + int ret = 0;
- + struct ath11k *ar;
- + u8 const *buf_pos;
- + u16 datalen;
- + u8 total_segments, current_seq;
- + u32 data_pos;
- + u32 pdev_id;
- +
- + ath11k_dbg(ab, ATH11K_DBG_TESTMODE,
- + "event wmi cmd_id %d ftm event msg %pK datalen %d\n",
- + cmd_id, ftm_msg, length);
- + ath11k_dbg_dump(ab, ATH11K_DBG_TESTMODE, NULL, "", ftm_msg, length);
- + pdev_id = DP_HW2SW_MACID(ftm_msg->seg_hdr.pdev_id);
- +
- + if (pdev_id >= ab->num_radios) {
- + ath11k_warn(ab, "testmode event not handled due to invalid pdev id: %d\n",
- + pdev_id);
- + return -EINVAL;
- + }
- +
- + ar = ab->pdevs[pdev_id].ar;
- + if (!ar) {
- + ath11k_warn(ab, "testmode event not handled due to absence of pdev\n");
- + return -ENODEV;
- + }
- +
- + current_seq = FIELD_GET(ATH11K_FTM_SEGHDR_CURRENT_SEQ,
- + ftm_msg->seg_hdr.segmentinfo);
- + total_segments = FIELD_GET(ATH11K_FTM_SEGHDR_TOTAL_SEGMENTS,
- + ftm_msg->seg_hdr.segmentinfo);
- + datalen = length - (sizeof(struct wmi_ftm_seg_hdr));
- + buf_pos = ftm_msg->data;
- +
- + spin_lock_bh(&ar->data_lock);
- +
- + if (current_seq == 0) {
- + ab->testmode.expected_seq = 0;
- + ab->testmode.data_pos = 0;
- + }
- +
- + data_pos = ab->testmode.data_pos;
- +
- + if ((data_pos + datalen) > ATH11K_FTM_EVENT_MAX_BUF_LENGTH) {
- + ath11k_warn(ab, "Invalid ftm event length at %d: %d\n",
- + data_pos, datalen);
- + ret = -EINVAL;
- + goto out;
- + }
- +
- + memcpy(&ab->testmode.eventdata[data_pos], buf_pos, datalen);
- + data_pos += datalen;
- +
- + if (++ab->testmode.expected_seq != total_segments) {
- + ab->testmode.data_pos = data_pos;
- + ath11k_dbg(ab, ATH11K_DBG_TESTMODE,
- + "partial data received current_seq %d total_seg %d\n",
- + current_seq, total_segments);
- + goto out;
- + }
- +
- + ath11k_dbg(ab, ATH11K_DBG_TESTMODE,
- + "total data length pos %d len %d\n",
- + data_pos, ftm_msg->seg_hdr.len);
- + nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,
- + 2 * nla_total_size(sizeof(u32)) +
- + nla_total_size(data_pos),
- + GFP_ATOMIC);
- + if (!nl_skb) {
- + ath11k_warn(ab,
- + "failed to allocate skb for segmented testmode wmi event\n");
- + ret = -ENOMEM;
- + goto out;
- + }
- +
- + if (nla_put_u32(nl_skb, ATH11K_TM_ATTR_CMD,
- + ATH11K_TM_CMD_WMI_FTM) ||
- + nla_put_u32(nl_skb, ATH11K_TM_ATTR_WMI_CMDID, cmd_id) ||
- + nla_put(nl_skb, ATH11K_TM_ATTR_DATA, data_pos,
- + &ab->testmode.eventdata[0])) {
- + ath11k_warn(ab, "failed to populate segmented testmode event");
- + kfree_skb(nl_skb);
- + ret = -ENOBUFS;
- + goto out;
- + }
- +
- + cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
- +
- +out:
- + spin_unlock_bh(&ar->data_lock);
- + return ret;
- +}
- +
- +static void ath11k_tm_wmi_event_segmented(struct ath11k_base *ab, u32 cmd_id,
- + struct sk_buff *skb)
- +{
- + const void **tb;
- + const struct wmi_ftm_event_msg *ev;
- + u16 length;
- + int ret;
- +
- + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
- + if (IS_ERR(tb)) {
- + ret = PTR_ERR(tb);
- + ath11k_warn(ab, "failed to parse ftm event tlv: %d\n", ret);
- + return;
- + }
- +
- + ev = tb[WMI_TAG_ARRAY_BYTE];
- + if (!ev) {
- + ath11k_warn(ab, "failed to fetch ftm msg\n");
- + kfree(tb);
- + return;
- + }
- +
- + length = skb->len - TLV_HDR_SIZE;
- + ret = ath11k_tm_process_event(ab, cmd_id, ev, length);
- + if (ret)
- + ath11k_warn(ab, "Failed to process ftm event\n");
- +
- + kfree(tb);
- +}
- +
- +void ath11k_tm_wmi_event(struct ath11k_base *ab, u32 cmd_id, struct sk_buff *skb)
- +{
- + if (test_bit(ATH11K_FLAG_FTM_SEGMENTED, &ab->dev_flags))
- + ath11k_tm_wmi_event_segmented(ab, cmd_id, skb);
- + else
- + ath11k_tm_wmi_event_unsegmented(ab, cmd_id, skb);
- +}
- +
- static int ath11k_tm_cmd_get_version(struct ath11k *ar, struct nlattr *tb[])
- {
- struct sk_buff *skb;
- int ret;
-
- ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE,
- - "testmode cmd get version_major %d version_minor %d\n",
- + "cmd get version_major %d version_minor %d\n",
- ATH11K_TESTMODE_VERSION_MAJOR,
- ATH11K_TESTMODE_VERSION_MINOR);
-
- @@ -53,6 +260,43 @@ static int ath11k_tm_cmd_get_version(str
- return cfg80211_testmode_reply(skb);
- }
-
- +static int ath11k_tm_cmd_testmode_start(struct ath11k *ar, struct nlattr *tb[])
- +{
- + int ret;
- +
- + mutex_lock(&ar->conf_mutex);
- +
- + if (ar->state == ATH11K_STATE_FTM) {
- + ret = -EALREADY;
- + goto err;
- + }
- +
- + /* start utf only when the driver is not in use */
- + if (ar->state != ATH11K_STATE_OFF) {
- + ret = -EBUSY;
- + goto err;
- + }
- +
- + ar->ab->testmode.eventdata = kzalloc(ATH11K_FTM_EVENT_MAX_BUF_LENGTH,
- + GFP_KERNEL);
- + if (!ar->ab->testmode.eventdata) {
- + ret = -ENOMEM;
- + goto err;
- + }
- +
- + ar->state = ATH11K_STATE_FTM;
- + ar->ftm_msgref = 0;
- +
- + mutex_unlock(&ar->conf_mutex);
- +
- + ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, "cmd start\n");
- + return 0;
- +
- +err:
- + mutex_unlock(&ar->conf_mutex);
- + return ret;
- +}
- +
- static int ath11k_tm_cmd_wmi(struct ath11k *ar, struct nlattr *tb[])
- {
- struct ath11k_pdev_wmi *wmi = ar->wmi;
- @@ -63,11 +307,6 @@ static int ath11k_tm_cmd_wmi(struct ath1
-
- mutex_lock(&ar->conf_mutex);
-
- - if (ar->state != ATH11K_STATE_ON) {
- - ret = -ENETDOWN;
- - goto out;
- - }
- -
- if (!tb[ATH11K_TM_ATTR_DATA]) {
- ret = -EINVAL;
- goto out;
- @@ -80,11 +319,17 @@ static int ath11k_tm_cmd_wmi(struct ath1
-
- buf = nla_data(tb[ATH11K_TM_ATTR_DATA]);
- buf_len = nla_len(tb[ATH11K_TM_ATTR_DATA]);
- + if (!buf_len) {
- + ath11k_warn(ar->ab, "No data present in testmode wmi command\n");
- + ret = -EINVAL;
- + goto out;
- + }
- +
- cmd_id = nla_get_u32(tb[ATH11K_TM_ATTR_WMI_CMDID]);
-
- ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE,
- - "testmode cmd wmi cmd_id %d buf %pK buf_len %d\n",
- - cmd_id, buf, buf_len);
- + "cmd wmi cmd_id %d buf length %d\n",
- + cmd_id, buf_len);
-
- ath11k_dbg_dump(ar->ab, ATH11K_DBG_TESTMODE, NULL, "", buf, buf_len);
-
- @@ -111,6 +356,91 @@ out:
- return ret;
- }
-
- +static int ath11k_tm_cmd_wmi_ftm(struct ath11k *ar, struct nlattr *tb[])
- +{
- + struct ath11k_pdev_wmi *wmi = ar->wmi;
- + struct ath11k_base *ab = ar->ab;
- + struct sk_buff *skb;
- + u32 cmd_id, buf_len, hdr_info;
- + int ret;
- + void *buf;
- + u8 segnumber = 0, seginfo;
- + u16 chunk_len, total_bytes, num_segments;
- + u8 *bufpos;
- + struct wmi_ftm_cmd *ftm_cmd;
- +
- + set_bit(ATH11K_FLAG_FTM_SEGMENTED, &ab->dev_flags);
- +
- + mutex_lock(&ar->conf_mutex);
- +
- + if (ar->state != ATH11K_STATE_FTM) {
- + ret = -ENETDOWN;
- + goto out;
- + }
- +
- + if (!tb[ATH11K_TM_ATTR_DATA]) {
- + ret = -EINVAL;
- + goto out;
- + }
- +
- + buf = nla_data(tb[ATH11K_TM_ATTR_DATA]);
- + buf_len = nla_len(tb[ATH11K_TM_ATTR_DATA]);
- + cmd_id = WMI_PDEV_UTF_CMDID;
- +
- + ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE,
- + "cmd wmi ftm cmd_id %d buffer length %d\n",
- + cmd_id, buf_len);
- + ath11k_dbg_dump(ar->ab, ATH11K_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) {
- + chunk_len = min_t(u16, buf_len, MAX_WMI_UTF_LEN);
- +
- + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, (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_TAG_ARRAY_BYTE) |
- + FIELD_PREP(WMI_TLV_LEN, (chunk_len +
- + sizeof(struct wmi_ftm_seg_hdr)));
- + ftm_cmd->tlv_header = hdr_info;
- + ftm_cmd->seg_hdr.len = total_bytes;
- + ftm_cmd->seg_hdr.msgref = ar->ftm_msgref;
- + seginfo = FIELD_PREP(ATH11K_FTM_SEGHDR_TOTAL_SEGMENTS, num_segments) |
- + FIELD_PREP(ATH11K_FTM_SEGHDR_CURRENT_SEQ, segnumber);
- + ftm_cmd->seg_hdr.segmentinfo = seginfo;
- + segnumber++;
- +
- + memcpy(&ftm_cmd->data, bufpos, chunk_len);
- +
- + ret = ath11k_wmi_cmd_send(wmi, skb, cmd_id);
- + if (ret) {
- + ath11k_warn(ar->ab, "failed to send wmi ftm command: %d\n", ret);
- + goto out;
- + }
- +
- + buf_len -= chunk_len;
- + bufpos += chunk_len;
- + }
- +
- + ar->ftm_msgref++;
- + ret = 0;
- +
- +out:
- + mutex_unlock(&ar->conf_mutex);
- + return ret;
- +}
- +
- int ath11k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- void *data, int len)
- {
- @@ -131,6 +461,10 @@ int ath11k_tm_cmd(struct ieee80211_hw *h
- return ath11k_tm_cmd_get_version(ar, tb);
- case ATH11K_TM_CMD_WMI:
- return ath11k_tm_cmd_wmi(ar, tb);
- + case ATH11K_TM_CMD_TESTMODE_START:
- + return ath11k_tm_cmd_testmode_start(ar, tb);
- + case ATH11K_TM_CMD_WMI_FTM:
- + return ath11k_tm_cmd_wmi_ftm(ar, tb);
- default:
- return -EOPNOTSUPP;
- }
- --- a/drivers/net/wireless/ath/ath11k/testmode.h
- +++ b/drivers/net/wireless/ath/ath11k/testmode.h
- @@ -8,11 +8,17 @@
-
- #ifdef CPTCFG_NL80211_TESTMODE
-
- +void ath11k_tm_wmi_event(struct ath11k_base *ab, u32 cmd_id, struct sk_buff *skb);
- int ath11k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- void *data, int len);
-
- #else
-
- +static inline void ath11k_tm_wmi_event(struct ath11k_base *ab, u32 cmd_id,
- + struct sk_buff *skb)
- +{
- +}
- +
- static inline int ath11k_tm_cmd(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- void *data, int len)
- --- a/drivers/net/wireless/ath/ath11k/testmode_i.h
- +++ b/drivers/net/wireless/ath/ath11k/testmode_i.h
- @@ -1,6 +1,7 @@
- /* SPDX-License-Identifier: BSD-3-Clause-Clear */
- /*
- * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
- */
-
- /* "API" level of the ath11k testmode interface. Bump it after every
- @@ -11,9 +12,10 @@
- /* Bump this after every _compatible_ interface change, for example
- * addition of a new command or an attribute.
- */
- -#define ATH11K_TESTMODE_VERSION_MINOR 0
- +#define ATH11K_TESTMODE_VERSION_MINOR 1
-
- #define ATH11K_TM_DATA_MAX_LEN 5000
- +#define ATH11K_FTM_EVENT_MAX_BUF_LENGTH 2048
-
- enum ath11k_tm_attr {
- __ATH11K_TM_ATTR_INVALID = 0,
- @@ -47,4 +49,18 @@ enum ath11k_tm_cmd {
- * ATH11K_TM_ATTR_DATA.
- */
- ATH11K_TM_CMD_WMI = 1,
- +
- + /* Boots the UTF firmware, the netdev interface must be down at the
- + * time.
- + */
- + ATH11K_TM_CMD_TESTMODE_START = 2,
- +
- + /* The command used to transmit a FTM WMI command to the firmware
- + * and the event to receive WMI events from the firmware. The data
- + * received only contain the 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.
- + */
- + ATH11K_TM_CMD_WMI_FTM = 3,
- };
- --- a/drivers/net/wireless/ath/ath11k/wmi.c
- +++ b/drivers/net/wireless/ath/ath11k/wmi.c
- @@ -1,7 +1,7 @@
- // SPDX-License-Identifier: BSD-3-Clause-Clear
- /*
- * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- - * Copyright (c) 2021, Qualcomm Innovation Center, Inc. All rights reserved.
- + * Copyright (c) 2021, 2023 Qualcomm Innovation Center, Inc. All rights reserved.
- */
- #include <linux/skbuff.h>
- #include <linux/ctype.h>
- @@ -19,6 +19,7 @@
- #include "mac.h"
- #include "hw.h"
- #include "peer.h"
- +#include "testmode.h"
-
- struct wmi_tlv_policy {
- size_t min_len;
- @@ -237,9 +238,8 @@ static int ath11k_wmi_tlv_parse(struct a
- (void *)tb);
- }
-
- -static const void **
- -ath11k_wmi_tlv_parse_alloc(struct ath11k_base *ab, const void *ptr,
- - size_t len, gfp_t gfp)
- +const void **ath11k_wmi_tlv_parse_alloc(struct ath11k_base *ab, const void *ptr,
- + size_t len, gfp_t gfp)
- {
- const void **tb;
- int ret;
- @@ -8628,6 +8628,9 @@ static void ath11k_wmi_tlv_op_rx(struct
- case WMI_PDEV_CSA_SWITCH_COUNT_STATUS_EVENTID:
- ath11k_wmi_pdev_csa_switch_count_status_event(ab, skb);
- break;
- + case WMI_PDEV_UTF_EVENTID:
- + ath11k_tm_wmi_event(ab, id, skb);
- + break;
- case WMI_PDEV_TEMPERATURE_EVENTID:
- ath11k_wmi_pdev_temperature_event(ab, skb);
- break;
- --- a/drivers/net/wireless/ath/ath11k/wmi.h
- +++ b/drivers/net/wireless/ath/ath11k/wmi.h
- @@ -1,6 +1,7 @@
- /* SPDX-License-Identifier: BSD-3-Clause-Clear */
- /*
- * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
- */
-
- #ifndef ATH11K_WMI_H
- @@ -68,6 +69,7 @@ struct wmi_tlv {
-
- #define WMI_APPEND_TO_EXISTING_CHAN_LIST_FLAG 1
-
- +#define MAX_WMI_UTF_LEN 252
- #define WMI_BA_MODE_BUFFER_SIZE_256 3
- /*
- * HW mode config type replicated from FW header
- @@ -3564,6 +3566,24 @@ struct wmi_get_pdev_temperature_cmd {
- u32 pdev_id;
- } __packed;
-
- +struct wmi_ftm_seg_hdr {
- + u32 len;
- + u32 msgref;
- + u32 segmentinfo;
- + u32 pdev_id;
- +} __packed;
- +
- +struct wmi_ftm_cmd {
- + u32 tlv_header;
- + struct wmi_ftm_seg_hdr seg_hdr;
- + u8 data[];
- +} __packed;
- +
- +struct wmi_ftm_event_msg {
- + struct wmi_ftm_seg_hdr seg_hdr;
- + u8 data[];
- +} __packed;
- +
- #define WMI_BEACON_TX_BUFFER_SIZE 512
-
- #define WMI_EMA_TMPL_IDX_SHIFT 8
- @@ -6300,6 +6320,8 @@ enum wmi_sta_keepalive_method {
- #define WMI_STA_KEEPALIVE_INTERVAL_DEFAULT 30
- #define WMI_STA_KEEPALIVE_INTERVAL_DISABLE 0
-
- +const void **ath11k_wmi_tlv_parse_alloc(struct ath11k_base *ab, const void *ptr,
- + size_t len, gfp_t gfp);
- int ath11k_wmi_cmd_send(struct ath11k_pdev_wmi *wmi, struct sk_buff *skb,
- u32 cmd_id);
- struct sk_buff *ath11k_wmi_alloc_skb(struct ath11k_wmi_base *wmi_sc, u32 len);
- --- a/drivers/net/wireless/ath/ath11k/wow.c
- +++ b/drivers/net/wireless/ath/ath11k/wow.c
- @@ -1,7 +1,7 @@
- // SPDX-License-Identifier: BSD-3-Clause-Clear
- /*
- * Copyright (c) 2020 The Linux Foundation. All rights reserved.
- - * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
- + * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
- */
-
- #include <linux/delay.h>
- @@ -838,6 +838,7 @@ exit:
- case ATH11K_STATE_RESTARTING:
- case ATH11K_STATE_RESTARTED:
- case ATH11K_STATE_WEDGED:
- + case ATH11K_STATE_FTM:
- ath11k_warn(ar->ab, "encountered unexpected device state %d on resume, cannot recover\n",
- ar->state);
- ret = -EIO;
|