brintos

brintos / linux-shallow public Read only

0
0
Text · 10.9 KiB · 7a9b9bb Raw
470 lines · c
1// SPDX-License-Identifier: ISC2/*3 * Copyright (c) 2014-2017 Qualcomm Atheros, Inc.4 */5 6#include "testmode.h"7 8#include <net/netlink.h>9#include <linux/firmware.h>10 11#include "debug.h"12#include "wmi.h"13#include "hif.h"14#include "hw.h"15#include "core.h"16 17#include "testmode_i.h"18 19static const struct nla_policy ath10k_tm_policy[ATH10K_TM_ATTR_MAX + 1] = {20	[ATH10K_TM_ATTR_CMD]		= { .type = NLA_U32 },21	[ATH10K_TM_ATTR_DATA]		= { .type = NLA_BINARY,22					    .len = ATH10K_TM_DATA_MAX_LEN },23	[ATH10K_TM_ATTR_WMI_CMDID]	= { .type = NLA_U32 },24	[ATH10K_TM_ATTR_VERSION_MAJOR]	= { .type = NLA_U32 },25	[ATH10K_TM_ATTR_VERSION_MINOR]	= { .type = NLA_U32 },26};27 28/* Returns true if callee consumes the skb and the skb should be discarded.29 * Returns false if skb is not used. Does not sleep.30 */31bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)32{33	struct sk_buff *nl_skb;34	bool consumed;35	int ret;36 37	ath10k_dbg(ar, ATH10K_DBG_TESTMODE,38		   "testmode event wmi cmd_id %d skb %pK skb->len %d\n",39		   cmd_id, skb, skb->len);40 41	ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", skb->data, skb->len);42 43	spin_lock_bh(&ar->data_lock);44 45	if (!ar->testmode.utf_monitor) {46		consumed = false;47		goto out;48	}49 50	/* Only testmode.c should be handling events from utf firmware,51	 * otherwise all sort of problems will arise as mac80211 operations52	 * are not initialised.53	 */54	consumed = true;55 56	nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,57						   2 * sizeof(u32) + skb->len,58						   GFP_ATOMIC);59	if (!nl_skb) {60		ath10k_warn(ar,61			    "failed to allocate skb for testmode wmi event\n");62		goto out;63	}64 65	ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_CMD, ATH10K_TM_CMD_WMI);66	if (ret) {67		ath10k_warn(ar,68			    "failed to put testmode wmi event cmd attribute: %d\n",69			    ret);70		kfree_skb(nl_skb);71		goto out;72	}73 74	ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_WMI_CMDID, cmd_id);75	if (ret) {76		ath10k_warn(ar,77			    "failed to put testmode wmi event cmd_id: %d\n",78			    ret);79		kfree_skb(nl_skb);80		goto out;81	}82 83	ret = nla_put(nl_skb, ATH10K_TM_ATTR_DATA, skb->len, skb->data);84	if (ret) {85		ath10k_warn(ar,86			    "failed to copy skb to testmode wmi event: %d\n",87			    ret);88		kfree_skb(nl_skb);89		goto out;90	}91 92	cfg80211_testmode_event(nl_skb, GFP_ATOMIC);93 94out:95	spin_unlock_bh(&ar->data_lock);96 97	return consumed;98}99 100static int ath10k_tm_cmd_get_version(struct ath10k *ar, struct nlattr *tb[])101{102	struct sk_buff *skb;103	int ret;104 105	ath10k_dbg(ar, ATH10K_DBG_TESTMODE,106		   "testmode cmd get version_major %d version_minor %d\n",107		   ATH10K_TESTMODE_VERSION_MAJOR,108		   ATH10K_TESTMODE_VERSION_MINOR);109 110	skb = cfg80211_testmode_alloc_reply_skb(ar->hw->wiphy,111						nla_total_size(sizeof(u32)));112	if (!skb)113		return -ENOMEM;114 115	ret = nla_put_u32(skb, ATH10K_TM_ATTR_VERSION_MAJOR,116			  ATH10K_TESTMODE_VERSION_MAJOR);117	if (ret) {118		kfree_skb(skb);119		return ret;120	}121 122	ret = nla_put_u32(skb, ATH10K_TM_ATTR_VERSION_MINOR,123			  ATH10K_TESTMODE_VERSION_MINOR);124	if (ret) {125		kfree_skb(skb);126		return ret;127	}128 129	ret = nla_put_u32(skb, ATH10K_TM_ATTR_WMI_OP_VERSION,130			  ar->normal_mode_fw.fw_file.wmi_op_version);131	if (ret) {132		kfree_skb(skb);133		return ret;134	}135 136	return cfg80211_testmode_reply(skb);137}138 139static int ath10k_tm_fetch_utf_firmware_api_1(struct ath10k *ar,140					      struct ath10k_fw_file *fw_file)141{142	char filename[100];143	int ret;144 145	snprintf(filename, sizeof(filename), "%s/%s",146		 ar->hw_params.fw.dir, ATH10K_FW_UTF_FILE);147 148	/* load utf firmware image */149	ret = firmware_request_nowarn(&fw_file->firmware, filename, ar->dev);150	ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode fw request '%s': %d\n",151		   filename, ret);152 153	if (ret) {154		ath10k_warn(ar, "failed to retrieve utf firmware '%s': %d\n",155			    filename, ret);156		return ret;157	}158 159	/* We didn't find FW UTF API 1 ("utf.bin") does not advertise160	 * firmware features. Do an ugly hack where we force the firmware161	 * features to match with 10.1 branch so that wmi.c will use the162	 * correct WMI interface.163	 */164 165	fw_file->wmi_op_version = ATH10K_FW_WMI_OP_VERSION_10_1;166	fw_file->htt_op_version = ATH10K_FW_HTT_OP_VERSION_10_1;167	fw_file->firmware_data = fw_file->firmware->data;168	fw_file->firmware_len = fw_file->firmware->size;169 170	return 0;171}172 173static int ath10k_tm_fetch_firmware(struct ath10k *ar)174{175	struct ath10k_fw_components *utf_mode_fw;176	int ret;177	char fw_name[100];178	int fw_api2 = 2;179 180	switch (ar->hif.bus) {181	case ATH10K_BUS_SDIO:182	case ATH10K_BUS_USB:183		scnprintf(fw_name, sizeof(fw_name), "%s-%s-%d.bin",184			  ATH10K_FW_UTF_FILE_BASE, ath10k_bus_str(ar->hif.bus),185			  fw_api2);186		break;187	default:188		scnprintf(fw_name, sizeof(fw_name), "%s-%d.bin",189			  ATH10K_FW_UTF_FILE_BASE, fw_api2);190		break;191	}192 193	ret = ath10k_core_fetch_firmware_api_n(ar, fw_name,194					       &ar->testmode.utf_mode_fw.fw_file);195	if (ret == 0) {196		ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode using fw utf api 2");197		goto out;198	}199 200	ret = ath10k_tm_fetch_utf_firmware_api_1(ar, &ar->testmode.utf_mode_fw.fw_file);201	if (ret) {202		ath10k_err(ar, "failed to fetch utf firmware binary: %d", ret);203		return ret;204	}205 206	ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode using utf api 1");207 208out:209	utf_mode_fw = &ar->testmode.utf_mode_fw;210 211	/* Use the same board data file as the normal firmware uses (but212	 * it's still "owned" by normal_mode_fw so we shouldn't free it.213	 */214	utf_mode_fw->board_data = ar->normal_mode_fw.board_data;215	utf_mode_fw->board_len = ar->normal_mode_fw.board_len;216 217	if (!utf_mode_fw->fw_file.otp_data) {218		ath10k_info(ar, "utf.bin didn't contain otp binary, taking it from the normal mode firmware");219		utf_mode_fw->fw_file.otp_data = ar->normal_mode_fw.fw_file.otp_data;220		utf_mode_fw->fw_file.otp_len = ar->normal_mode_fw.fw_file.otp_len;221	}222 223	return 0;224}225 226static int ath10k_tm_cmd_utf_start(struct ath10k *ar, struct nlattr *tb[])227{228	const char *ver;229	int ret;230 231	ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode cmd utf start\n");232 233	mutex_lock(&ar->conf_mutex);234 235	if (ar->state == ATH10K_STATE_UTF) {236		ret = -EALREADY;237		goto err;238	}239 240	/* start utf only when the driver is not in use  */241	if (ar->state != ATH10K_STATE_OFF) {242		ret = -EBUSY;243		goto err;244	}245 246	if (WARN_ON(ar->testmode.utf_mode_fw.fw_file.firmware != NULL)) {247		/* utf image is already downloaded, it shouldn't be */248		ret = -EEXIST;249		goto err;250	}251 252	ret = ath10k_tm_fetch_firmware(ar);253	if (ret) {254		ath10k_err(ar, "failed to fetch UTF firmware: %d", ret);255		goto err;256	}257 258	if (ar->testmode.utf_mode_fw.fw_file.codeswap_data &&259	    ar->testmode.utf_mode_fw.fw_file.codeswap_len) {260		ret = ath10k_swap_code_seg_init(ar,261						&ar->testmode.utf_mode_fw.fw_file);262		if (ret) {263			ath10k_warn(ar,264				    "failed to init utf code swap segment: %d\n",265				    ret);266			goto err_release_utf_mode_fw;267		}268	}269 270	spin_lock_bh(&ar->data_lock);271	ar->testmode.utf_monitor = true;272	spin_unlock_bh(&ar->data_lock);273 274	ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode wmi version %d\n",275		   ar->testmode.utf_mode_fw.fw_file.wmi_op_version);276 277	ret = ath10k_hif_power_up(ar, ATH10K_FIRMWARE_MODE_UTF);278	if (ret) {279		ath10k_err(ar, "failed to power up hif (testmode): %d\n", ret);280		ar->state = ATH10K_STATE_OFF;281		goto err_release_utf_mode_fw;282	}283 284	ret = ath10k_core_start(ar, ATH10K_FIRMWARE_MODE_UTF,285				&ar->testmode.utf_mode_fw);286	if (ret) {287		ath10k_err(ar, "failed to start core (testmode): %d\n", ret);288		ar->state = ATH10K_STATE_OFF;289		goto err_power_down;290	}291 292	ar->state = ATH10K_STATE_UTF;293 294	if (strlen(ar->testmode.utf_mode_fw.fw_file.fw_version) > 0)295		ver = ar->testmode.utf_mode_fw.fw_file.fw_version;296	else297		ver = "API 1";298 299	ath10k_info(ar, "UTF firmware %s started\n", ver);300 301	mutex_unlock(&ar->conf_mutex);302 303	return 0;304 305err_power_down:306	ath10k_hif_power_down(ar);307 308err_release_utf_mode_fw:309	if (ar->testmode.utf_mode_fw.fw_file.codeswap_data &&310	    ar->testmode.utf_mode_fw.fw_file.codeswap_len)311		ath10k_swap_code_seg_release(ar,312					     &ar->testmode.utf_mode_fw.fw_file);313 314	release_firmware(ar->testmode.utf_mode_fw.fw_file.firmware);315	ar->testmode.utf_mode_fw.fw_file.firmware = NULL;316 317err:318	mutex_unlock(&ar->conf_mutex);319 320	return ret;321}322 323static void __ath10k_tm_cmd_utf_stop(struct ath10k *ar)324{325	lockdep_assert_held(&ar->conf_mutex);326 327	ath10k_core_stop(ar);328	ath10k_hif_power_down(ar);329 330	spin_lock_bh(&ar->data_lock);331 332	ar->testmode.utf_monitor = false;333 334	spin_unlock_bh(&ar->data_lock);335 336	if (ar->testmode.utf_mode_fw.fw_file.codeswap_data &&337	    ar->testmode.utf_mode_fw.fw_file.codeswap_len)338		ath10k_swap_code_seg_release(ar,339					     &ar->testmode.utf_mode_fw.fw_file);340 341	release_firmware(ar->testmode.utf_mode_fw.fw_file.firmware);342	ar->testmode.utf_mode_fw.fw_file.firmware = NULL;343 344	ar->state = ATH10K_STATE_OFF;345}346 347static int ath10k_tm_cmd_utf_stop(struct ath10k *ar, struct nlattr *tb[])348{349	int ret;350 351	ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode cmd utf stop\n");352 353	mutex_lock(&ar->conf_mutex);354 355	if (ar->state != ATH10K_STATE_UTF) {356		ret = -ENETDOWN;357		goto out;358	}359 360	__ath10k_tm_cmd_utf_stop(ar);361 362	ret = 0;363 364	ath10k_info(ar, "UTF firmware stopped\n");365 366out:367	mutex_unlock(&ar->conf_mutex);368	return ret;369}370 371static int ath10k_tm_cmd_wmi(struct ath10k *ar, struct nlattr *tb[])372{373	struct sk_buff *skb;374	int ret, buf_len;375	u32 cmd_id;376	void *buf;377 378	mutex_lock(&ar->conf_mutex);379 380	if (ar->state != ATH10K_STATE_UTF) {381		ret = -ENETDOWN;382		goto out;383	}384 385	if (!tb[ATH10K_TM_ATTR_DATA]) {386		ret = -EINVAL;387		goto out;388	}389 390	if (!tb[ATH10K_TM_ATTR_WMI_CMDID]) {391		ret = -EINVAL;392		goto out;393	}394 395	buf = nla_data(tb[ATH10K_TM_ATTR_DATA]);396	buf_len = nla_len(tb[ATH10K_TM_ATTR_DATA]);397	cmd_id = nla_get_u32(tb[ATH10K_TM_ATTR_WMI_CMDID]);398 399	ath10k_dbg(ar, ATH10K_DBG_TESTMODE,400		   "testmode cmd wmi cmd_id %d buf %pK buf_len %d\n",401		   cmd_id, buf, buf_len);402 403	ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", buf, buf_len);404 405	skb = ath10k_wmi_alloc_skb(ar, buf_len);406	if (!skb) {407		ret = -ENOMEM;408		goto out;409	}410 411	memcpy(skb->data, buf, buf_len);412 413	ret = ath10k_wmi_cmd_send(ar, skb, cmd_id);414	if (ret) {415		ath10k_warn(ar, "failed to transmit wmi command (testmode): %d\n",416			    ret);417		goto out;418	}419 420	ret = 0;421 422out:423	mutex_unlock(&ar->conf_mutex);424	return ret;425}426 427int ath10k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,428		  void *data, int len)429{430	struct ath10k *ar = hw->priv;431	struct nlattr *tb[ATH10K_TM_ATTR_MAX + 1];432	int ret;433 434	ret = nla_parse_deprecated(tb, ATH10K_TM_ATTR_MAX, data, len,435				   ath10k_tm_policy, NULL);436	if (ret)437		return ret;438 439	if (!tb[ATH10K_TM_ATTR_CMD])440		return -EINVAL;441 442	switch (nla_get_u32(tb[ATH10K_TM_ATTR_CMD])) {443	case ATH10K_TM_CMD_GET_VERSION:444		return ath10k_tm_cmd_get_version(ar, tb);445	case ATH10K_TM_CMD_UTF_START:446		return ath10k_tm_cmd_utf_start(ar, tb);447	case ATH10K_TM_CMD_UTF_STOP:448		return ath10k_tm_cmd_utf_stop(ar, tb);449	case ATH10K_TM_CMD_WMI:450		return ath10k_tm_cmd_wmi(ar, tb);451	default:452		return -EOPNOTSUPP;453	}454}455 456void ath10k_testmode_destroy(struct ath10k *ar)457{458	mutex_lock(&ar->conf_mutex);459 460	if (ar->state != ATH10K_STATE_UTF) {461		/* utf firmware is not running, nothing to do */462		goto out;463	}464 465	__ath10k_tm_cmd_utf_stop(ar);466 467out:468	mutex_unlock(&ar->conf_mutex);469}470