Path: blob/main/sys/contrib/dev/athk/ath10k/testmode.c
105585 views
// SPDX-License-Identifier: ISC1/*2* Copyright (c) 2014-2017 Qualcomm Atheros, Inc.3* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.4*/56#include "testmode.h"78#include <net/netlink.h>9#include <linux/firmware.h>1011#include "debug.h"12#include "wmi.h"13#include "wmi-tlv.h"14#include "hif.h"15#include "hw.h"16#include "core.h"1718#include "testmode_i.h"1920#define ATH10K_FTM_SEG_NONE ((u32)-1)21#define ATH10K_FTM_SEGHDR_CURRENT_SEQ GENMASK(3, 0)22#define ATH10K_FTM_SEGHDR_TOTAL_SEGMENTS GENMASK(7, 4)2324static const struct nla_policy ath10k_tm_policy[ATH10K_TM_ATTR_MAX + 1] = {25[ATH10K_TM_ATTR_CMD] = { .type = NLA_U32 },26[ATH10K_TM_ATTR_DATA] = { .type = NLA_BINARY,27.len = ATH10K_TM_DATA_MAX_LEN },28[ATH10K_TM_ATTR_WMI_CMDID] = { .type = NLA_U32 },29[ATH10K_TM_ATTR_VERSION_MAJOR] = { .type = NLA_U32 },30[ATH10K_TM_ATTR_VERSION_MINOR] = { .type = NLA_U32 },31};3233static void ath10k_tm_event_unsegmented(struct ath10k *ar, u32 cmd_id,34struct sk_buff *skb)35{36struct sk_buff *nl_skb;37int ret;3839nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,402 * sizeof(u32) + skb->len,41GFP_ATOMIC);42if (!nl_skb) {43ath10k_warn(ar,44"failed to allocate skb for testmode wmi event\n");45return;46}4748ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_CMD, ATH10K_TM_CMD_WMI);49if (ret) {50ath10k_warn(ar,51"failed to put testmode wmi event cmd attribute: %d\n",52ret);53kfree_skb(nl_skb);54return;55}5657ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_WMI_CMDID, cmd_id);58if (ret) {59ath10k_warn(ar,60"failed to put testmode wmi event cmd_id: %d\n",61ret);62kfree_skb(nl_skb);63return;64}6566ret = nla_put(nl_skb, ATH10K_TM_ATTR_DATA, skb->len, skb->data);67if (ret) {68ath10k_warn(ar,69"failed to copy skb to testmode wmi event: %d\n",70ret);71kfree_skb(nl_skb);72return;73}7475cfg80211_testmode_event(nl_skb, GFP_ATOMIC);76}7778static void ath10k_tm_event_segmented(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)79{80struct wmi_ftm_cmd *ftm = (struct wmi_ftm_cmd *)skb->data;81u8 total_segments, current_seq;82struct sk_buff *nl_skb;83u8 const *buf_pos;84u16 datalen;85u32 data_pos;86int ret;8788if (skb->len < sizeof(*ftm)) {89ath10k_warn(ar, "Invalid ftm event length: %d\n", skb->len);90return;91}9293current_seq = FIELD_GET(ATH10K_FTM_SEGHDR_CURRENT_SEQ,94__le32_to_cpu(ftm->seg_hdr.segmentinfo));95total_segments = FIELD_GET(ATH10K_FTM_SEGHDR_TOTAL_SEGMENTS,96__le32_to_cpu(ftm->seg_hdr.segmentinfo));97datalen = skb->len - sizeof(*ftm);98buf_pos = ftm->data;99100if (current_seq == 0) {101ar->testmode.expected_seq = 0;102ar->testmode.data_pos = 0;103}104105data_pos = ar->testmode.data_pos;106107if ((data_pos + datalen) > ATH_FTM_EVENT_MAX_BUF_LENGTH) {108ath10k_warn(ar, "Invalid ftm event length at %u: %u\n",109data_pos, datalen);110ret = -EINVAL;111return;112}113114memcpy(&ar->testmode.eventdata[data_pos], buf_pos, datalen);115data_pos += datalen;116117if (++ar->testmode.expected_seq != total_segments) {118ar->testmode.data_pos = data_pos;119ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "partial data received %u/%u\n",120current_seq + 1, total_segments);121return;122}123124ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "total data length %u\n", data_pos);125126nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,1272 * sizeof(u32) + data_pos,128GFP_ATOMIC);129if (!nl_skb) {130ath10k_warn(ar, "failed to allocate skb for testmode wmi event\n");131return;132}133134ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_CMD, ATH10K_TM_CMD_TLV);135if (ret) {136ath10k_warn(ar, "failed to put testmode wmi event attribute: %d\n", ret);137kfree_skb(nl_skb);138return;139}140141ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_WMI_CMDID, cmd_id);142if (ret) {143ath10k_warn(ar, "failed to put testmode wmi event cmd_id: %d\n", ret);144kfree_skb(nl_skb);145return;146}147148ret = nla_put(nl_skb, ATH10K_TM_ATTR_DATA, data_pos, &ar->testmode.eventdata[0]);149if (ret) {150ath10k_warn(ar, "failed to copy skb to testmode wmi event: %d\n", ret);151kfree_skb(nl_skb);152return;153}154155cfg80211_testmode_event(nl_skb, GFP_ATOMIC);156}157158/* Returns true if callee consumes the skb and the skb should be discarded.159* Returns false if skb is not used. Does not sleep.160*/161bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)162{163bool consumed;164165ath10k_dbg(ar, ATH10K_DBG_TESTMODE,166"testmode event wmi cmd_id %d skb %p skb->len %d\n",167cmd_id, skb, skb->len);168169ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", skb->data, skb->len);170171spin_lock_bh(&ar->data_lock);172173if (!ar->testmode.utf_monitor) {174consumed = false;175goto out;176}177178/* Only testmode.c should be handling events from utf firmware,179* otherwise all sort of problems will arise as mac80211 operations180* are not initialised.181*/182consumed = true;183184if (ar->testmode.expected_seq != ATH10K_FTM_SEG_NONE)185ath10k_tm_event_segmented(ar, cmd_id, skb);186else187ath10k_tm_event_unsegmented(ar, cmd_id, skb);188189out:190spin_unlock_bh(&ar->data_lock);191192return consumed;193}194195static int ath10k_tm_cmd_get_version(struct ath10k *ar, struct nlattr *tb[])196{197struct sk_buff *skb;198int ret;199200ath10k_dbg(ar, ATH10K_DBG_TESTMODE,201"testmode cmd get version_major %d version_minor %d\n",202ATH10K_TESTMODE_VERSION_MAJOR,203ATH10K_TESTMODE_VERSION_MINOR);204205skb = cfg80211_testmode_alloc_reply_skb(ar->hw->wiphy,206nla_total_size(sizeof(u32)));207if (!skb)208return -ENOMEM;209210ret = nla_put_u32(skb, ATH10K_TM_ATTR_VERSION_MAJOR,211ATH10K_TESTMODE_VERSION_MAJOR);212if (ret) {213kfree_skb(skb);214return ret;215}216217ret = nla_put_u32(skb, ATH10K_TM_ATTR_VERSION_MINOR,218ATH10K_TESTMODE_VERSION_MINOR);219if (ret) {220kfree_skb(skb);221return ret;222}223224ret = nla_put_u32(skb, ATH10K_TM_ATTR_WMI_OP_VERSION,225ar->normal_mode_fw.fw_file.wmi_op_version);226if (ret) {227kfree_skb(skb);228return ret;229}230231return cfg80211_testmode_reply(skb);232}233234static int ath10k_tm_fetch_utf_firmware_api_1(struct ath10k *ar,235struct ath10k_fw_file *fw_file)236{237char filename[100];238int ret;239240snprintf(filename, sizeof(filename), "%s/%s",241ar->hw_params.fw.dir, ATH10K_FW_UTF_FILE);242243/* load utf firmware image */244ret = firmware_request_nowarn(&fw_file->firmware, filename, ar->dev);245ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode fw request '%s': %d\n",246filename, ret);247248if (ret) {249ath10k_warn(ar, "failed to retrieve utf firmware '%s': %d\n",250filename, ret);251return ret;252}253254/* We didn't find FW UTF API 1 ("utf.bin") does not advertise255* firmware features. Do an ugly hack where we force the firmware256* features to match with 10.1 branch so that wmi.c will use the257* correct WMI interface.258*/259260fw_file->wmi_op_version = ATH10K_FW_WMI_OP_VERSION_10_1;261fw_file->htt_op_version = ATH10K_FW_HTT_OP_VERSION_10_1;262fw_file->firmware_data = fw_file->firmware->data;263fw_file->firmware_len = fw_file->firmware->size;264265return 0;266}267268static int ath10k_tm_fetch_firmware(struct ath10k *ar)269{270struct ath10k_fw_components *utf_mode_fw;271int ret;272char fw_name[100];273int fw_api2 = 2;274275switch (ar->hif.bus) {276case ATH10K_BUS_SDIO:277case ATH10K_BUS_USB:278scnprintf(fw_name, sizeof(fw_name), "%s-%s-%d.bin",279ATH10K_FW_UTF_FILE_BASE, ath10k_bus_str(ar->hif.bus),280fw_api2);281break;282default:283scnprintf(fw_name, sizeof(fw_name), "%s-%d.bin",284ATH10K_FW_UTF_FILE_BASE, fw_api2);285break;286}287288ret = ath10k_core_fetch_firmware_api_n(ar, fw_name,289&ar->testmode.utf_mode_fw.fw_file);290if (ret == 0) {291ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode using fw utf api 2");292goto out;293}294295ret = ath10k_tm_fetch_utf_firmware_api_1(ar, &ar->testmode.utf_mode_fw.fw_file);296if (ret) {297ath10k_err(ar, "failed to fetch utf firmware binary: %d", ret);298return ret;299}300301ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode using utf api 1");302303out:304utf_mode_fw = &ar->testmode.utf_mode_fw;305306/* Use the same board data file as the normal firmware uses (but307* it's still "owned" by normal_mode_fw so we shouldn't free it.308*/309utf_mode_fw->board_data = ar->normal_mode_fw.board_data;310utf_mode_fw->board_len = ar->normal_mode_fw.board_len;311312if (!utf_mode_fw->fw_file.otp_data) {313ath10k_info(ar, "utf.bin didn't contain otp binary, taking it from the normal mode firmware");314utf_mode_fw->fw_file.otp_data = ar->normal_mode_fw.fw_file.otp_data;315utf_mode_fw->fw_file.otp_len = ar->normal_mode_fw.fw_file.otp_len;316}317318return 0;319}320321static int ath10k_tm_cmd_utf_start(struct ath10k *ar, struct nlattr *tb[])322{323const char *ver;324int ret;325326ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode cmd utf start\n");327328mutex_lock(&ar->conf_mutex);329330if (ar->state == ATH10K_STATE_UTF) {331ret = -EALREADY;332goto err;333}334335/* start utf only when the driver is not in use */336if (ar->state != ATH10K_STATE_OFF) {337ret = -EBUSY;338goto err;339}340341if (WARN_ON(ar->testmode.utf_mode_fw.fw_file.firmware != NULL)) {342/* utf image is already downloaded, it shouldn't be */343ret = -EEXIST;344goto err;345}346347ret = ath10k_tm_fetch_firmware(ar);348if (ret) {349ath10k_err(ar, "failed to fetch UTF firmware: %d", ret);350goto err;351}352353if (ar->testmode.utf_mode_fw.fw_file.codeswap_data &&354ar->testmode.utf_mode_fw.fw_file.codeswap_len) {355ret = ath10k_swap_code_seg_init(ar,356&ar->testmode.utf_mode_fw.fw_file);357if (ret) {358ath10k_warn(ar,359"failed to init utf code swap segment: %d\n",360ret);361goto err_release_utf_mode_fw;362}363}364365spin_lock_bh(&ar->data_lock);366ar->testmode.utf_monitor = true;367spin_unlock_bh(&ar->data_lock);368369ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode wmi version %d\n",370ar->testmode.utf_mode_fw.fw_file.wmi_op_version);371372ret = ath10k_hif_power_up(ar, ATH10K_FIRMWARE_MODE_UTF);373if (ret) {374ath10k_err(ar, "failed to power up hif (testmode): %d\n", ret);375ar->state = ATH10K_STATE_OFF;376goto err_release_utf_mode_fw;377}378379ar->testmode.eventdata = kzalloc(ATH_FTM_EVENT_MAX_BUF_LENGTH, GFP_KERNEL);380if (!ar->testmode.eventdata) {381ret = -ENOMEM;382goto err_power_down;383}384385ret = ath10k_core_start(ar, ATH10K_FIRMWARE_MODE_UTF,386&ar->testmode.utf_mode_fw);387if (ret) {388ath10k_err(ar, "failed to start core (testmode): %d\n", ret);389ar->state = ATH10K_STATE_OFF;390goto err_release_eventdata;391}392393ar->state = ATH10K_STATE_UTF;394395if (strlen(ar->testmode.utf_mode_fw.fw_file.fw_version) > 0)396ver = ar->testmode.utf_mode_fw.fw_file.fw_version;397else398ver = "API 1";399400ath10k_info(ar, "UTF firmware %s started\n", ver);401402mutex_unlock(&ar->conf_mutex);403404return 0;405406err_release_eventdata:407kfree(ar->testmode.eventdata);408ar->testmode.eventdata = NULL;409410err_power_down:411ath10k_hif_power_down(ar);412413err_release_utf_mode_fw:414if (ar->testmode.utf_mode_fw.fw_file.codeswap_data &&415ar->testmode.utf_mode_fw.fw_file.codeswap_len)416ath10k_swap_code_seg_release(ar,417&ar->testmode.utf_mode_fw.fw_file);418419release_firmware(ar->testmode.utf_mode_fw.fw_file.firmware);420ar->testmode.utf_mode_fw.fw_file.firmware = NULL;421422err:423mutex_unlock(&ar->conf_mutex);424425return ret;426}427428static void __ath10k_tm_cmd_utf_stop(struct ath10k *ar)429{430lockdep_assert_held(&ar->conf_mutex);431432ath10k_core_stop(ar);433ath10k_hif_power_down(ar);434435spin_lock_bh(&ar->data_lock);436437ar->testmode.utf_monitor = false;438439spin_unlock_bh(&ar->data_lock);440441if (ar->testmode.utf_mode_fw.fw_file.codeswap_data &&442ar->testmode.utf_mode_fw.fw_file.codeswap_len)443ath10k_swap_code_seg_release(ar,444&ar->testmode.utf_mode_fw.fw_file);445446release_firmware(ar->testmode.utf_mode_fw.fw_file.firmware);447ar->testmode.utf_mode_fw.fw_file.firmware = NULL;448449kfree(ar->testmode.eventdata);450ar->testmode.eventdata = NULL;451452ar->state = ATH10K_STATE_OFF;453}454455static int ath10k_tm_cmd_utf_stop(struct ath10k *ar, struct nlattr *tb[])456{457int ret;458459ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode cmd utf stop\n");460461mutex_lock(&ar->conf_mutex);462463if (ar->state != ATH10K_STATE_UTF) {464ret = -ENETDOWN;465goto out;466}467468__ath10k_tm_cmd_utf_stop(ar);469470ret = 0;471472ath10k_info(ar, "UTF firmware stopped\n");473474out:475mutex_unlock(&ar->conf_mutex);476return ret;477}478479static int ath10k_tm_cmd_wmi(struct ath10k *ar, struct nlattr *tb[])480{481struct sk_buff *skb;482int ret, buf_len;483u32 cmd_id;484void *buf;485486mutex_lock(&ar->conf_mutex);487488if (ar->state != ATH10K_STATE_UTF) {489ret = -ENETDOWN;490goto out;491}492493if (!tb[ATH10K_TM_ATTR_DATA]) {494ret = -EINVAL;495goto out;496}497498if (!tb[ATH10K_TM_ATTR_WMI_CMDID]) {499ret = -EINVAL;500goto out;501}502503buf = nla_data(tb[ATH10K_TM_ATTR_DATA]);504buf_len = nla_len(tb[ATH10K_TM_ATTR_DATA]);505cmd_id = nla_get_u32(tb[ATH10K_TM_ATTR_WMI_CMDID]);506507ath10k_dbg(ar, ATH10K_DBG_TESTMODE,508"testmode cmd wmi cmd_id %d buf %p buf_len %d\n",509cmd_id, buf, buf_len);510511ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", buf, buf_len);512513skb = ath10k_wmi_alloc_skb(ar, buf_len);514if (!skb) {515ret = -ENOMEM;516goto out;517}518519memcpy(skb->data, buf, buf_len);520521ret = ath10k_wmi_cmd_send(ar, skb, cmd_id);522if (ret) {523ath10k_warn(ar, "failed to transmit wmi command (testmode): %d\n",524ret);525goto out;526}527528ret = 0;529530out:531mutex_unlock(&ar->conf_mutex);532return ret;533}534535static int ath10k_tm_cmd_tlv(struct ath10k *ar, struct nlattr *tb[])536{537u16 total_bytes, num_segments;538u32 cmd_id, buf_len;539u8 segnumber = 0;540u8 *bufpos;541void *buf;542int ret;543544mutex_lock(&ar->conf_mutex);545546if (ar->state != ATH10K_STATE_UTF) {547ret = -ENETDOWN;548goto out;549}550551buf = nla_data(tb[ATH10K_TM_ATTR_DATA]);552buf_len = nla_len(tb[ATH10K_TM_ATTR_DATA]);553cmd_id = WMI_PDEV_UTF_CMDID;554555ath10k_dbg(ar, ATH10K_DBG_TESTMODE,556"cmd wmi ftm cmd_id %d buffer length %d\n",557cmd_id, buf_len);558ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", buf, buf_len);559560bufpos = buf;561total_bytes = buf_len;562num_segments = total_bytes / MAX_WMI_UTF_LEN;563ar->testmode.expected_seq = 0;564565if (buf_len - (num_segments * MAX_WMI_UTF_LEN))566num_segments++;567568while (buf_len) {569u16 chunk_len = min_t(u16, buf_len, MAX_WMI_UTF_LEN);570struct wmi_ftm_cmd *ftm_cmd;571struct sk_buff *skb;572u32 hdr_info;573u8 seginfo;574575skb = ath10k_wmi_alloc_skb(ar, (chunk_len +576sizeof(struct wmi_ftm_cmd)));577if (!skb) {578ret = -ENOMEM;579goto out;580}581582ftm_cmd = (struct wmi_ftm_cmd *)skb->data;583hdr_info = FIELD_PREP(WMI_TLV_TAG, WMI_TLV_TAG_ARRAY_BYTE) |584FIELD_PREP(WMI_TLV_LEN, (chunk_len +585sizeof(struct wmi_ftm_seg_hdr)));586ftm_cmd->tlv_header = __cpu_to_le32(hdr_info);587ftm_cmd->seg_hdr.len = __cpu_to_le32(total_bytes);588ftm_cmd->seg_hdr.msgref = __cpu_to_le32(ar->testmode.ftm_msgref);589seginfo = FIELD_PREP(ATH10K_FTM_SEGHDR_TOTAL_SEGMENTS, num_segments) |590FIELD_PREP(ATH10K_FTM_SEGHDR_CURRENT_SEQ, segnumber);591ftm_cmd->seg_hdr.segmentinfo = __cpu_to_le32(seginfo);592segnumber++;593594memcpy(&ftm_cmd->data, bufpos, chunk_len);595596ret = ath10k_wmi_cmd_send(ar, skb, cmd_id);597if (ret) {598ath10k_warn(ar, "failed to send wmi ftm command: %d\n", ret);599goto out;600}601602buf_len -= chunk_len;603bufpos += chunk_len;604}605606ar->testmode.ftm_msgref++;607ret = 0;608609out:610mutex_unlock(&ar->conf_mutex);611return ret;612}613614int ath10k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,615void *data, int len)616{617struct ath10k *ar = hw->priv;618struct nlattr *tb[ATH10K_TM_ATTR_MAX + 1];619int ret;620621ret = nla_parse_deprecated(tb, ATH10K_TM_ATTR_MAX, data, len,622ath10k_tm_policy, NULL);623if (ret)624return ret;625626if (!tb[ATH10K_TM_ATTR_CMD])627return -EINVAL;628629ar->testmode.expected_seq = ATH10K_FTM_SEG_NONE;630631switch (nla_get_u32(tb[ATH10K_TM_ATTR_CMD])) {632case ATH10K_TM_CMD_GET_VERSION:633if (!tb[ATH10K_TM_ATTR_DATA])634return ath10k_tm_cmd_get_version(ar, tb);635else /* ATH10K_TM_CMD_TLV */636return ath10k_tm_cmd_tlv(ar, tb);637case ATH10K_TM_CMD_UTF_START:638return ath10k_tm_cmd_utf_start(ar, tb);639case ATH10K_TM_CMD_UTF_STOP:640return ath10k_tm_cmd_utf_stop(ar, tb);641case ATH10K_TM_CMD_WMI:642return ath10k_tm_cmd_wmi(ar, tb);643default:644return -EOPNOTSUPP;645}646}647648void ath10k_testmode_destroy(struct ath10k *ar)649{650mutex_lock(&ar->conf_mutex);651652if (ar->state != ATH10K_STATE_UTF) {653/* utf firmware is not running, nothing to do */654goto out;655}656657__ath10k_tm_cmd_utf_stop(ar);658659out:660mutex_unlock(&ar->conf_mutex);661}662663664