Path: blob/main/sys/contrib/dev/athk/ath10k/testmode.c
48375 views
// SPDX-License-Identifier: ISC1/*2* Copyright (c) 2014-2017 Qualcomm Atheros, Inc.3*/45#include "testmode.h"67#include <net/netlink.h>8#include <linux/firmware.h>910#include "debug.h"11#include "wmi.h"12#include "hif.h"13#include "hw.h"14#include "core.h"1516#include "testmode_i.h"1718static const struct nla_policy ath10k_tm_policy[ATH10K_TM_ATTR_MAX + 1] = {19[ATH10K_TM_ATTR_CMD] = { .type = NLA_U32 },20[ATH10K_TM_ATTR_DATA] = { .type = NLA_BINARY,21.len = ATH10K_TM_DATA_MAX_LEN },22[ATH10K_TM_ATTR_WMI_CMDID] = { .type = NLA_U32 },23[ATH10K_TM_ATTR_VERSION_MAJOR] = { .type = NLA_U32 },24[ATH10K_TM_ATTR_VERSION_MINOR] = { .type = NLA_U32 },25};2627/* Returns true if callee consumes the skb and the skb should be discarded.28* Returns false if skb is not used. Does not sleep.29*/30bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)31{32struct sk_buff *nl_skb;33bool consumed;34int ret;3536ath10k_dbg(ar, ATH10K_DBG_TESTMODE,37"testmode event wmi cmd_id %d skb %pK skb->len %d\n",38cmd_id, skb, skb->len);3940ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", skb->data, skb->len);4142spin_lock_bh(&ar->data_lock);4344if (!ar->testmode.utf_monitor) {45consumed = false;46goto out;47}4849/* Only testmode.c should be handling events from utf firmware,50* otherwise all sort of problems will arise as mac80211 operations51* are not initialised.52*/53consumed = true;5455nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,562 * sizeof(u32) + skb->len,57GFP_ATOMIC);58if (!nl_skb) {59ath10k_warn(ar,60"failed to allocate skb for testmode wmi event\n");61goto out;62}6364ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_CMD, ATH10K_TM_CMD_WMI);65if (ret) {66ath10k_warn(ar,67"failed to put testmode wmi event cmd attribute: %d\n",68ret);69kfree_skb(nl_skb);70goto out;71}7273ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_WMI_CMDID, cmd_id);74if (ret) {75ath10k_warn(ar,76"failed to put testmode wmi event cmd_id: %d\n",77ret);78kfree_skb(nl_skb);79goto out;80}8182ret = nla_put(nl_skb, ATH10K_TM_ATTR_DATA, skb->len, skb->data);83if (ret) {84ath10k_warn(ar,85"failed to copy skb to testmode wmi event: %d\n",86ret);87kfree_skb(nl_skb);88goto out;89}9091cfg80211_testmode_event(nl_skb, GFP_ATOMIC);9293out:94spin_unlock_bh(&ar->data_lock);9596return consumed;97}9899static int ath10k_tm_cmd_get_version(struct ath10k *ar, struct nlattr *tb[])100{101struct sk_buff *skb;102int ret;103104ath10k_dbg(ar, ATH10K_DBG_TESTMODE,105"testmode cmd get version_major %d version_minor %d\n",106ATH10K_TESTMODE_VERSION_MAJOR,107ATH10K_TESTMODE_VERSION_MINOR);108109skb = cfg80211_testmode_alloc_reply_skb(ar->hw->wiphy,110nla_total_size(sizeof(u32)));111if (!skb)112return -ENOMEM;113114ret = nla_put_u32(skb, ATH10K_TM_ATTR_VERSION_MAJOR,115ATH10K_TESTMODE_VERSION_MAJOR);116if (ret) {117kfree_skb(skb);118return ret;119}120121ret = nla_put_u32(skb, ATH10K_TM_ATTR_VERSION_MINOR,122ATH10K_TESTMODE_VERSION_MINOR);123if (ret) {124kfree_skb(skb);125return ret;126}127128ret = nla_put_u32(skb, ATH10K_TM_ATTR_WMI_OP_VERSION,129ar->normal_mode_fw.fw_file.wmi_op_version);130if (ret) {131kfree_skb(skb);132return ret;133}134135return cfg80211_testmode_reply(skb);136}137138static int ath10k_tm_fetch_utf_firmware_api_1(struct ath10k *ar,139struct ath10k_fw_file *fw_file)140{141char filename[100];142int ret;143144snprintf(filename, sizeof(filename), "%s/%s",145ar->hw_params.fw.dir, ATH10K_FW_UTF_FILE);146147/* load utf firmware image */148ret = firmware_request_nowarn(&fw_file->firmware, filename, ar->dev);149ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode fw request '%s': %d\n",150filename, ret);151152if (ret) {153ath10k_warn(ar, "failed to retrieve utf firmware '%s': %d\n",154filename, ret);155return ret;156}157158/* We didn't find FW UTF API 1 ("utf.bin") does not advertise159* firmware features. Do an ugly hack where we force the firmware160* features to match with 10.1 branch so that wmi.c will use the161* correct WMI interface.162*/163164fw_file->wmi_op_version = ATH10K_FW_WMI_OP_VERSION_10_1;165fw_file->htt_op_version = ATH10K_FW_HTT_OP_VERSION_10_1;166fw_file->firmware_data = fw_file->firmware->data;167fw_file->firmware_len = fw_file->firmware->size;168169return 0;170}171172static int ath10k_tm_fetch_firmware(struct ath10k *ar)173{174struct ath10k_fw_components *utf_mode_fw;175int ret;176char fw_name[100];177int fw_api2 = 2;178179switch (ar->hif.bus) {180case ATH10K_BUS_SDIO:181case ATH10K_BUS_USB:182scnprintf(fw_name, sizeof(fw_name), "%s-%s-%d.bin",183ATH10K_FW_UTF_FILE_BASE, ath10k_bus_str(ar->hif.bus),184fw_api2);185break;186default:187scnprintf(fw_name, sizeof(fw_name), "%s-%d.bin",188ATH10K_FW_UTF_FILE_BASE, fw_api2);189break;190}191192ret = ath10k_core_fetch_firmware_api_n(ar, fw_name,193&ar->testmode.utf_mode_fw.fw_file);194if (ret == 0) {195ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode using fw utf api 2");196goto out;197}198199ret = ath10k_tm_fetch_utf_firmware_api_1(ar, &ar->testmode.utf_mode_fw.fw_file);200if (ret) {201ath10k_err(ar, "failed to fetch utf firmware binary: %d", ret);202return ret;203}204205ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode using utf api 1");206207out:208utf_mode_fw = &ar->testmode.utf_mode_fw;209210/* Use the same board data file as the normal firmware uses (but211* it's still "owned" by normal_mode_fw so we shouldn't free it.212*/213utf_mode_fw->board_data = ar->normal_mode_fw.board_data;214utf_mode_fw->board_len = ar->normal_mode_fw.board_len;215216if (!utf_mode_fw->fw_file.otp_data) {217ath10k_info(ar, "utf.bin didn't contain otp binary, taking it from the normal mode firmware");218utf_mode_fw->fw_file.otp_data = ar->normal_mode_fw.fw_file.otp_data;219utf_mode_fw->fw_file.otp_len = ar->normal_mode_fw.fw_file.otp_len;220}221222return 0;223}224225static int ath10k_tm_cmd_utf_start(struct ath10k *ar, struct nlattr *tb[])226{227const char *ver;228int ret;229230ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode cmd utf start\n");231232mutex_lock(&ar->conf_mutex);233234if (ar->state == ATH10K_STATE_UTF) {235ret = -EALREADY;236goto err;237}238239/* start utf only when the driver is not in use */240if (ar->state != ATH10K_STATE_OFF) {241ret = -EBUSY;242goto err;243}244245if (WARN_ON(ar->testmode.utf_mode_fw.fw_file.firmware != NULL)) {246/* utf image is already downloaded, it shouldn't be */247ret = -EEXIST;248goto err;249}250251ret = ath10k_tm_fetch_firmware(ar);252if (ret) {253ath10k_err(ar, "failed to fetch UTF firmware: %d", ret);254goto err;255}256257if (ar->testmode.utf_mode_fw.fw_file.codeswap_data &&258ar->testmode.utf_mode_fw.fw_file.codeswap_len) {259ret = ath10k_swap_code_seg_init(ar,260&ar->testmode.utf_mode_fw.fw_file);261if (ret) {262ath10k_warn(ar,263"failed to init utf code swap segment: %d\n",264ret);265goto err_release_utf_mode_fw;266}267}268269spin_lock_bh(&ar->data_lock);270ar->testmode.utf_monitor = true;271spin_unlock_bh(&ar->data_lock);272273ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode wmi version %d\n",274ar->testmode.utf_mode_fw.fw_file.wmi_op_version);275276ret = ath10k_hif_power_up(ar, ATH10K_FIRMWARE_MODE_UTF);277if (ret) {278ath10k_err(ar, "failed to power up hif (testmode): %d\n", ret);279ar->state = ATH10K_STATE_OFF;280goto err_release_utf_mode_fw;281}282283ret = ath10k_core_start(ar, ATH10K_FIRMWARE_MODE_UTF,284&ar->testmode.utf_mode_fw);285if (ret) {286ath10k_err(ar, "failed to start core (testmode): %d\n", ret);287ar->state = ATH10K_STATE_OFF;288goto err_power_down;289}290291ar->state = ATH10K_STATE_UTF;292293if (strlen(ar->testmode.utf_mode_fw.fw_file.fw_version) > 0)294ver = ar->testmode.utf_mode_fw.fw_file.fw_version;295else296ver = "API 1";297298ath10k_info(ar, "UTF firmware %s started\n", ver);299300mutex_unlock(&ar->conf_mutex);301302return 0;303304err_power_down:305ath10k_hif_power_down(ar);306307err_release_utf_mode_fw:308if (ar->testmode.utf_mode_fw.fw_file.codeswap_data &&309ar->testmode.utf_mode_fw.fw_file.codeswap_len)310ath10k_swap_code_seg_release(ar,311&ar->testmode.utf_mode_fw.fw_file);312313release_firmware(ar->testmode.utf_mode_fw.fw_file.firmware);314ar->testmode.utf_mode_fw.fw_file.firmware = NULL;315316err:317mutex_unlock(&ar->conf_mutex);318319return ret;320}321322static void __ath10k_tm_cmd_utf_stop(struct ath10k *ar)323{324lockdep_assert_held(&ar->conf_mutex);325326ath10k_core_stop(ar);327ath10k_hif_power_down(ar);328329spin_lock_bh(&ar->data_lock);330331ar->testmode.utf_monitor = false;332333spin_unlock_bh(&ar->data_lock);334335if (ar->testmode.utf_mode_fw.fw_file.codeswap_data &&336ar->testmode.utf_mode_fw.fw_file.codeswap_len)337ath10k_swap_code_seg_release(ar,338&ar->testmode.utf_mode_fw.fw_file);339340release_firmware(ar->testmode.utf_mode_fw.fw_file.firmware);341ar->testmode.utf_mode_fw.fw_file.firmware = NULL;342343ar->state = ATH10K_STATE_OFF;344}345346static int ath10k_tm_cmd_utf_stop(struct ath10k *ar, struct nlattr *tb[])347{348int ret;349350ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode cmd utf stop\n");351352mutex_lock(&ar->conf_mutex);353354if (ar->state != ATH10K_STATE_UTF) {355ret = -ENETDOWN;356goto out;357}358359__ath10k_tm_cmd_utf_stop(ar);360361ret = 0;362363ath10k_info(ar, "UTF firmware stopped\n");364365out:366mutex_unlock(&ar->conf_mutex);367return ret;368}369370static int ath10k_tm_cmd_wmi(struct ath10k *ar, struct nlattr *tb[])371{372struct sk_buff *skb;373int ret, buf_len;374u32 cmd_id;375void *buf;376377mutex_lock(&ar->conf_mutex);378379if (ar->state != ATH10K_STATE_UTF) {380ret = -ENETDOWN;381goto out;382}383384if (!tb[ATH10K_TM_ATTR_DATA]) {385ret = -EINVAL;386goto out;387}388389if (!tb[ATH10K_TM_ATTR_WMI_CMDID]) {390ret = -EINVAL;391goto out;392}393394buf = nla_data(tb[ATH10K_TM_ATTR_DATA]);395buf_len = nla_len(tb[ATH10K_TM_ATTR_DATA]);396cmd_id = nla_get_u32(tb[ATH10K_TM_ATTR_WMI_CMDID]);397398ath10k_dbg(ar, ATH10K_DBG_TESTMODE,399"testmode cmd wmi cmd_id %d buf %pK buf_len %d\n",400cmd_id, buf, buf_len);401402ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", buf, buf_len);403404skb = ath10k_wmi_alloc_skb(ar, buf_len);405if (!skb) {406ret = -ENOMEM;407goto out;408}409410memcpy(skb->data, buf, buf_len);411412ret = ath10k_wmi_cmd_send(ar, skb, cmd_id);413if (ret) {414ath10k_warn(ar, "failed to transmit wmi command (testmode): %d\n",415ret);416goto out;417}418419ret = 0;420421out:422mutex_unlock(&ar->conf_mutex);423return ret;424}425426int ath10k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,427void *data, int len)428{429struct ath10k *ar = hw->priv;430struct nlattr *tb[ATH10K_TM_ATTR_MAX + 1];431int ret;432433ret = nla_parse_deprecated(tb, ATH10K_TM_ATTR_MAX, data, len,434ath10k_tm_policy, NULL);435if (ret)436return ret;437438if (!tb[ATH10K_TM_ATTR_CMD])439return -EINVAL;440441switch (nla_get_u32(tb[ATH10K_TM_ATTR_CMD])) {442case ATH10K_TM_CMD_GET_VERSION:443return ath10k_tm_cmd_get_version(ar, tb);444case ATH10K_TM_CMD_UTF_START:445return ath10k_tm_cmd_utf_start(ar, tb);446case ATH10K_TM_CMD_UTF_STOP:447return ath10k_tm_cmd_utf_stop(ar, tb);448case ATH10K_TM_CMD_WMI:449return ath10k_tm_cmd_wmi(ar, tb);450default:451return -EOPNOTSUPP;452}453}454455void ath10k_testmode_destroy(struct ath10k *ar)456{457mutex_lock(&ar->conf_mutex);458459if (ar->state != ATH10K_STATE_UTF) {460/* utf firmware is not running, nothing to do */461goto out;462}463464__ath10k_tm_cmd_utf_stop(ar);465466out:467mutex_unlock(&ar->conf_mutex);468}469470471