18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: ISC 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2014-2017 Qualcomm Atheros, Inc. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include "testmode.h" 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <net/netlink.h> 98c2ecf20Sopenharmony_ci#include <linux/firmware.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include "debug.h" 128c2ecf20Sopenharmony_ci#include "wmi.h" 138c2ecf20Sopenharmony_ci#include "hif.h" 148c2ecf20Sopenharmony_ci#include "hw.h" 158c2ecf20Sopenharmony_ci#include "core.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "testmode_i.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic const struct nla_policy ath10k_tm_policy[ATH10K_TM_ATTR_MAX + 1] = { 208c2ecf20Sopenharmony_ci [ATH10K_TM_ATTR_CMD] = { .type = NLA_U32 }, 218c2ecf20Sopenharmony_ci [ATH10K_TM_ATTR_DATA] = { .type = NLA_BINARY, 228c2ecf20Sopenharmony_ci .len = ATH10K_TM_DATA_MAX_LEN }, 238c2ecf20Sopenharmony_ci [ATH10K_TM_ATTR_WMI_CMDID] = { .type = NLA_U32 }, 248c2ecf20Sopenharmony_ci [ATH10K_TM_ATTR_VERSION_MAJOR] = { .type = NLA_U32 }, 258c2ecf20Sopenharmony_ci [ATH10K_TM_ATTR_VERSION_MINOR] = { .type = NLA_U32 }, 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* Returns true if callee consumes the skb and the skb should be discarded. 298c2ecf20Sopenharmony_ci * Returns false if skb is not used. Does not sleep. 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_cibool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci struct sk_buff *nl_skb; 348c2ecf20Sopenharmony_ci bool consumed; 358c2ecf20Sopenharmony_ci int ret; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_TESTMODE, 388c2ecf20Sopenharmony_ci "testmode event wmi cmd_id %d skb %pK skb->len %d\n", 398c2ecf20Sopenharmony_ci cmd_id, skb, skb->len); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", skb->data, skb->len); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci spin_lock_bh(&ar->data_lock); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci if (!ar->testmode.utf_monitor) { 468c2ecf20Sopenharmony_ci consumed = false; 478c2ecf20Sopenharmony_ci goto out; 488c2ecf20Sopenharmony_ci } 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci /* Only testmode.c should be handling events from utf firmware, 518c2ecf20Sopenharmony_ci * otherwise all sort of problems will arise as mac80211 operations 528c2ecf20Sopenharmony_ci * are not initialised. 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_ci consumed = true; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy, 578c2ecf20Sopenharmony_ci 2 * sizeof(u32) + skb->len, 588c2ecf20Sopenharmony_ci GFP_ATOMIC); 598c2ecf20Sopenharmony_ci if (!nl_skb) { 608c2ecf20Sopenharmony_ci ath10k_warn(ar, 618c2ecf20Sopenharmony_ci "failed to allocate skb for testmode wmi event\n"); 628c2ecf20Sopenharmony_ci goto out; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_CMD, ATH10K_TM_CMD_WMI); 668c2ecf20Sopenharmony_ci if (ret) { 678c2ecf20Sopenharmony_ci ath10k_warn(ar, 688c2ecf20Sopenharmony_ci "failed to put testmode wmi event cmd attribute: %d\n", 698c2ecf20Sopenharmony_ci ret); 708c2ecf20Sopenharmony_ci kfree_skb(nl_skb); 718c2ecf20Sopenharmony_ci goto out; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_WMI_CMDID, cmd_id); 758c2ecf20Sopenharmony_ci if (ret) { 768c2ecf20Sopenharmony_ci ath10k_warn(ar, 778c2ecf20Sopenharmony_ci "failed to put testmode wmi event cmd_id: %d\n", 788c2ecf20Sopenharmony_ci ret); 798c2ecf20Sopenharmony_ci kfree_skb(nl_skb); 808c2ecf20Sopenharmony_ci goto out; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci ret = nla_put(nl_skb, ATH10K_TM_ATTR_DATA, skb->len, skb->data); 848c2ecf20Sopenharmony_ci if (ret) { 858c2ecf20Sopenharmony_ci ath10k_warn(ar, 868c2ecf20Sopenharmony_ci "failed to copy skb to testmode wmi event: %d\n", 878c2ecf20Sopenharmony_ci ret); 888c2ecf20Sopenharmony_ci kfree_skb(nl_skb); 898c2ecf20Sopenharmony_ci goto out; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci cfg80211_testmode_event(nl_skb, GFP_ATOMIC); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ciout: 958c2ecf20Sopenharmony_ci spin_unlock_bh(&ar->data_lock); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci return consumed; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic int ath10k_tm_cmd_get_version(struct ath10k *ar, struct nlattr *tb[]) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct sk_buff *skb; 1038c2ecf20Sopenharmony_ci int ret; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_TESTMODE, 1068c2ecf20Sopenharmony_ci "testmode cmd get version_major %d version_minor %d\n", 1078c2ecf20Sopenharmony_ci ATH10K_TESTMODE_VERSION_MAJOR, 1088c2ecf20Sopenharmony_ci ATH10K_TESTMODE_VERSION_MINOR); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci skb = cfg80211_testmode_alloc_reply_skb(ar->hw->wiphy, 1118c2ecf20Sopenharmony_ci nla_total_size(sizeof(u32))); 1128c2ecf20Sopenharmony_ci if (!skb) 1138c2ecf20Sopenharmony_ci return -ENOMEM; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci ret = nla_put_u32(skb, ATH10K_TM_ATTR_VERSION_MAJOR, 1168c2ecf20Sopenharmony_ci ATH10K_TESTMODE_VERSION_MAJOR); 1178c2ecf20Sopenharmony_ci if (ret) { 1188c2ecf20Sopenharmony_ci kfree_skb(skb); 1198c2ecf20Sopenharmony_ci return ret; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci ret = nla_put_u32(skb, ATH10K_TM_ATTR_VERSION_MINOR, 1238c2ecf20Sopenharmony_ci ATH10K_TESTMODE_VERSION_MINOR); 1248c2ecf20Sopenharmony_ci if (ret) { 1258c2ecf20Sopenharmony_ci kfree_skb(skb); 1268c2ecf20Sopenharmony_ci return ret; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci ret = nla_put_u32(skb, ATH10K_TM_ATTR_WMI_OP_VERSION, 1308c2ecf20Sopenharmony_ci ar->normal_mode_fw.fw_file.wmi_op_version); 1318c2ecf20Sopenharmony_ci if (ret) { 1328c2ecf20Sopenharmony_ci kfree_skb(skb); 1338c2ecf20Sopenharmony_ci return ret; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci return cfg80211_testmode_reply(skb); 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic int ath10k_tm_fetch_utf_firmware_api_1(struct ath10k *ar, 1408c2ecf20Sopenharmony_ci struct ath10k_fw_file *fw_file) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci char filename[100]; 1438c2ecf20Sopenharmony_ci int ret; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci snprintf(filename, sizeof(filename), "%s/%s", 1468c2ecf20Sopenharmony_ci ar->hw_params.fw.dir, ATH10K_FW_UTF_FILE); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* load utf firmware image */ 1498c2ecf20Sopenharmony_ci ret = firmware_request_nowarn(&fw_file->firmware, filename, ar->dev); 1508c2ecf20Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode fw request '%s': %d\n", 1518c2ecf20Sopenharmony_ci filename, ret); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (ret) { 1548c2ecf20Sopenharmony_ci ath10k_warn(ar, "failed to retrieve utf firmware '%s': %d\n", 1558c2ecf20Sopenharmony_ci filename, ret); 1568c2ecf20Sopenharmony_ci return ret; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* We didn't find FW UTF API 1 ("utf.bin") does not advertise 1608c2ecf20Sopenharmony_ci * firmware features. Do an ugly hack where we force the firmware 1618c2ecf20Sopenharmony_ci * features to match with 10.1 branch so that wmi.c will use the 1628c2ecf20Sopenharmony_ci * correct WMI interface. 1638c2ecf20Sopenharmony_ci */ 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci fw_file->wmi_op_version = ATH10K_FW_WMI_OP_VERSION_10_1; 1668c2ecf20Sopenharmony_ci fw_file->htt_op_version = ATH10K_FW_HTT_OP_VERSION_10_1; 1678c2ecf20Sopenharmony_ci fw_file->firmware_data = fw_file->firmware->data; 1688c2ecf20Sopenharmony_ci fw_file->firmware_len = fw_file->firmware->size; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci return 0; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic int ath10k_tm_fetch_firmware(struct ath10k *ar) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct ath10k_fw_components *utf_mode_fw; 1768c2ecf20Sopenharmony_ci int ret; 1778c2ecf20Sopenharmony_ci char fw_name[100]; 1788c2ecf20Sopenharmony_ci int fw_api2 = 2; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci switch (ar->hif.bus) { 1818c2ecf20Sopenharmony_ci case ATH10K_BUS_SDIO: 1828c2ecf20Sopenharmony_ci case ATH10K_BUS_USB: 1838c2ecf20Sopenharmony_ci scnprintf(fw_name, sizeof(fw_name), "%s-%s-%d.bin", 1848c2ecf20Sopenharmony_ci ATH10K_FW_UTF_FILE_BASE, ath10k_bus_str(ar->hif.bus), 1858c2ecf20Sopenharmony_ci fw_api2); 1868c2ecf20Sopenharmony_ci break; 1878c2ecf20Sopenharmony_ci default: 1888c2ecf20Sopenharmony_ci scnprintf(fw_name, sizeof(fw_name), "%s-%d.bin", 1898c2ecf20Sopenharmony_ci ATH10K_FW_UTF_FILE_BASE, fw_api2); 1908c2ecf20Sopenharmony_ci break; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci ret = ath10k_core_fetch_firmware_api_n(ar, fw_name, 1948c2ecf20Sopenharmony_ci &ar->testmode.utf_mode_fw.fw_file); 1958c2ecf20Sopenharmony_ci if (ret == 0) { 1968c2ecf20Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode using fw utf api 2"); 1978c2ecf20Sopenharmony_ci goto out; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci ret = ath10k_tm_fetch_utf_firmware_api_1(ar, &ar->testmode.utf_mode_fw.fw_file); 2018c2ecf20Sopenharmony_ci if (ret) { 2028c2ecf20Sopenharmony_ci ath10k_err(ar, "failed to fetch utf firmware binary: %d", ret); 2038c2ecf20Sopenharmony_ci return ret; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode using utf api 1"); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ciout: 2098c2ecf20Sopenharmony_ci utf_mode_fw = &ar->testmode.utf_mode_fw; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci /* Use the same board data file as the normal firmware uses (but 2128c2ecf20Sopenharmony_ci * it's still "owned" by normal_mode_fw so we shouldn't free it. 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_ci utf_mode_fw->board_data = ar->normal_mode_fw.board_data; 2158c2ecf20Sopenharmony_ci utf_mode_fw->board_len = ar->normal_mode_fw.board_len; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (!utf_mode_fw->fw_file.otp_data) { 2188c2ecf20Sopenharmony_ci ath10k_info(ar, "utf.bin didn't contain otp binary, taking it from the normal mode firmware"); 2198c2ecf20Sopenharmony_ci utf_mode_fw->fw_file.otp_data = ar->normal_mode_fw.fw_file.otp_data; 2208c2ecf20Sopenharmony_ci utf_mode_fw->fw_file.otp_len = ar->normal_mode_fw.fw_file.otp_len; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci return 0; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic int ath10k_tm_cmd_utf_start(struct ath10k *ar, struct nlattr *tb[]) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci const char *ver; 2298c2ecf20Sopenharmony_ci int ret; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode cmd utf start\n"); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci mutex_lock(&ar->conf_mutex); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (ar->state == ATH10K_STATE_UTF) { 2368c2ecf20Sopenharmony_ci ret = -EALREADY; 2378c2ecf20Sopenharmony_ci goto err; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci /* start utf only when the driver is not in use */ 2418c2ecf20Sopenharmony_ci if (ar->state != ATH10K_STATE_OFF) { 2428c2ecf20Sopenharmony_ci ret = -EBUSY; 2438c2ecf20Sopenharmony_ci goto err; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (WARN_ON(ar->testmode.utf_mode_fw.fw_file.firmware != NULL)) { 2478c2ecf20Sopenharmony_ci /* utf image is already downloaded, it shouldn't be */ 2488c2ecf20Sopenharmony_ci ret = -EEXIST; 2498c2ecf20Sopenharmony_ci goto err; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci ret = ath10k_tm_fetch_firmware(ar); 2538c2ecf20Sopenharmony_ci if (ret) { 2548c2ecf20Sopenharmony_ci ath10k_err(ar, "failed to fetch UTF firmware: %d", ret); 2558c2ecf20Sopenharmony_ci goto err; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (ar->testmode.utf_mode_fw.fw_file.codeswap_data && 2598c2ecf20Sopenharmony_ci ar->testmode.utf_mode_fw.fw_file.codeswap_len) { 2608c2ecf20Sopenharmony_ci ret = ath10k_swap_code_seg_init(ar, 2618c2ecf20Sopenharmony_ci &ar->testmode.utf_mode_fw.fw_file); 2628c2ecf20Sopenharmony_ci if (ret) { 2638c2ecf20Sopenharmony_ci ath10k_warn(ar, 2648c2ecf20Sopenharmony_ci "failed to init utf code swap segment: %d\n", 2658c2ecf20Sopenharmony_ci ret); 2668c2ecf20Sopenharmony_ci goto err_release_utf_mode_fw; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci spin_lock_bh(&ar->data_lock); 2718c2ecf20Sopenharmony_ci ar->testmode.utf_monitor = true; 2728c2ecf20Sopenharmony_ci spin_unlock_bh(&ar->data_lock); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode wmi version %d\n", 2758c2ecf20Sopenharmony_ci ar->testmode.utf_mode_fw.fw_file.wmi_op_version); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci ret = ath10k_hif_power_up(ar, ATH10K_FIRMWARE_MODE_UTF); 2788c2ecf20Sopenharmony_ci if (ret) { 2798c2ecf20Sopenharmony_ci ath10k_err(ar, "failed to power up hif (testmode): %d\n", ret); 2808c2ecf20Sopenharmony_ci ar->state = ATH10K_STATE_OFF; 2818c2ecf20Sopenharmony_ci goto err_release_utf_mode_fw; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci ret = ath10k_core_start(ar, ATH10K_FIRMWARE_MODE_UTF, 2858c2ecf20Sopenharmony_ci &ar->testmode.utf_mode_fw); 2868c2ecf20Sopenharmony_ci if (ret) { 2878c2ecf20Sopenharmony_ci ath10k_err(ar, "failed to start core (testmode): %d\n", ret); 2888c2ecf20Sopenharmony_ci ar->state = ATH10K_STATE_OFF; 2898c2ecf20Sopenharmony_ci goto err_power_down; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci ar->state = ATH10K_STATE_UTF; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (strlen(ar->testmode.utf_mode_fw.fw_file.fw_version) > 0) 2958c2ecf20Sopenharmony_ci ver = ar->testmode.utf_mode_fw.fw_file.fw_version; 2968c2ecf20Sopenharmony_ci else 2978c2ecf20Sopenharmony_ci ver = "API 1"; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci ath10k_info(ar, "UTF firmware %s started\n", ver); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci mutex_unlock(&ar->conf_mutex); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci return 0; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cierr_power_down: 3068c2ecf20Sopenharmony_ci ath10k_hif_power_down(ar); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cierr_release_utf_mode_fw: 3098c2ecf20Sopenharmony_ci if (ar->testmode.utf_mode_fw.fw_file.codeswap_data && 3108c2ecf20Sopenharmony_ci ar->testmode.utf_mode_fw.fw_file.codeswap_len) 3118c2ecf20Sopenharmony_ci ath10k_swap_code_seg_release(ar, 3128c2ecf20Sopenharmony_ci &ar->testmode.utf_mode_fw.fw_file); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci release_firmware(ar->testmode.utf_mode_fw.fw_file.firmware); 3158c2ecf20Sopenharmony_ci ar->testmode.utf_mode_fw.fw_file.firmware = NULL; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cierr: 3188c2ecf20Sopenharmony_ci mutex_unlock(&ar->conf_mutex); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci return ret; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic void __ath10k_tm_cmd_utf_stop(struct ath10k *ar) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci lockdep_assert_held(&ar->conf_mutex); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci ath10k_core_stop(ar); 3288c2ecf20Sopenharmony_ci ath10k_hif_power_down(ar); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci spin_lock_bh(&ar->data_lock); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci ar->testmode.utf_monitor = false; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci spin_unlock_bh(&ar->data_lock); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (ar->testmode.utf_mode_fw.fw_file.codeswap_data && 3378c2ecf20Sopenharmony_ci ar->testmode.utf_mode_fw.fw_file.codeswap_len) 3388c2ecf20Sopenharmony_ci ath10k_swap_code_seg_release(ar, 3398c2ecf20Sopenharmony_ci &ar->testmode.utf_mode_fw.fw_file); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci release_firmware(ar->testmode.utf_mode_fw.fw_file.firmware); 3428c2ecf20Sopenharmony_ci ar->testmode.utf_mode_fw.fw_file.firmware = NULL; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci ar->state = ATH10K_STATE_OFF; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic int ath10k_tm_cmd_utf_stop(struct ath10k *ar, struct nlattr *tb[]) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci int ret; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode cmd utf stop\n"); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci mutex_lock(&ar->conf_mutex); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (ar->state != ATH10K_STATE_UTF) { 3568c2ecf20Sopenharmony_ci ret = -ENETDOWN; 3578c2ecf20Sopenharmony_ci goto out; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci __ath10k_tm_cmd_utf_stop(ar); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci ret = 0; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci ath10k_info(ar, "UTF firmware stopped\n"); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ciout: 3678c2ecf20Sopenharmony_ci mutex_unlock(&ar->conf_mutex); 3688c2ecf20Sopenharmony_ci return ret; 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic int ath10k_tm_cmd_wmi(struct ath10k *ar, struct nlattr *tb[]) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct sk_buff *skb; 3748c2ecf20Sopenharmony_ci int ret, buf_len; 3758c2ecf20Sopenharmony_ci u32 cmd_id; 3768c2ecf20Sopenharmony_ci void *buf; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci mutex_lock(&ar->conf_mutex); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (ar->state != ATH10K_STATE_UTF) { 3818c2ecf20Sopenharmony_ci ret = -ENETDOWN; 3828c2ecf20Sopenharmony_ci goto out; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if (!tb[ATH10K_TM_ATTR_DATA]) { 3868c2ecf20Sopenharmony_ci ret = -EINVAL; 3878c2ecf20Sopenharmony_ci goto out; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (!tb[ATH10K_TM_ATTR_WMI_CMDID]) { 3918c2ecf20Sopenharmony_ci ret = -EINVAL; 3928c2ecf20Sopenharmony_ci goto out; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci buf = nla_data(tb[ATH10K_TM_ATTR_DATA]); 3968c2ecf20Sopenharmony_ci buf_len = nla_len(tb[ATH10K_TM_ATTR_DATA]); 3978c2ecf20Sopenharmony_ci cmd_id = nla_get_u32(tb[ATH10K_TM_ATTR_WMI_CMDID]); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_TESTMODE, 4008c2ecf20Sopenharmony_ci "testmode cmd wmi cmd_id %d buf %pK buf_len %d\n", 4018c2ecf20Sopenharmony_ci cmd_id, buf, buf_len); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", buf, buf_len); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci skb = ath10k_wmi_alloc_skb(ar, buf_len); 4068c2ecf20Sopenharmony_ci if (!skb) { 4078c2ecf20Sopenharmony_ci ret = -ENOMEM; 4088c2ecf20Sopenharmony_ci goto out; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci memcpy(skb->data, buf, buf_len); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci ret = ath10k_wmi_cmd_send(ar, skb, cmd_id); 4148c2ecf20Sopenharmony_ci if (ret) { 4158c2ecf20Sopenharmony_ci ath10k_warn(ar, "failed to transmit wmi command (testmode): %d\n", 4168c2ecf20Sopenharmony_ci ret); 4178c2ecf20Sopenharmony_ci goto out; 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci ret = 0; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ciout: 4238c2ecf20Sopenharmony_ci mutex_unlock(&ar->conf_mutex); 4248c2ecf20Sopenharmony_ci return ret; 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ciint ath10k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 4288c2ecf20Sopenharmony_ci void *data, int len) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci struct ath10k *ar = hw->priv; 4318c2ecf20Sopenharmony_ci struct nlattr *tb[ATH10K_TM_ATTR_MAX + 1]; 4328c2ecf20Sopenharmony_ci int ret; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci ret = nla_parse_deprecated(tb, ATH10K_TM_ATTR_MAX, data, len, 4358c2ecf20Sopenharmony_ci ath10k_tm_policy, NULL); 4368c2ecf20Sopenharmony_ci if (ret) 4378c2ecf20Sopenharmony_ci return ret; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci if (!tb[ATH10K_TM_ATTR_CMD]) 4408c2ecf20Sopenharmony_ci return -EINVAL; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci switch (nla_get_u32(tb[ATH10K_TM_ATTR_CMD])) { 4438c2ecf20Sopenharmony_ci case ATH10K_TM_CMD_GET_VERSION: 4448c2ecf20Sopenharmony_ci return ath10k_tm_cmd_get_version(ar, tb); 4458c2ecf20Sopenharmony_ci case ATH10K_TM_CMD_UTF_START: 4468c2ecf20Sopenharmony_ci return ath10k_tm_cmd_utf_start(ar, tb); 4478c2ecf20Sopenharmony_ci case ATH10K_TM_CMD_UTF_STOP: 4488c2ecf20Sopenharmony_ci return ath10k_tm_cmd_utf_stop(ar, tb); 4498c2ecf20Sopenharmony_ci case ATH10K_TM_CMD_WMI: 4508c2ecf20Sopenharmony_ci return ath10k_tm_cmd_wmi(ar, tb); 4518c2ecf20Sopenharmony_ci default: 4528c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_civoid ath10k_testmode_destroy(struct ath10k *ar) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci mutex_lock(&ar->conf_mutex); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if (ar->state != ATH10K_STATE_UTF) { 4618c2ecf20Sopenharmony_ci /* utf firmware is not running, nothing to do */ 4628c2ecf20Sopenharmony_ci goto out; 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci __ath10k_tm_cmd_utf_stop(ar); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ciout: 4688c2ecf20Sopenharmony_ci mutex_unlock(&ar->conf_mutex); 4698c2ecf20Sopenharmony_ci} 470