xref: /linux/drivers/net/wireless/ath/ath11k/testmode.c (revision 74c12ee02af109adcde36ec184fa59c0afb0edaa)
1d5c65159SKalle Valo // SPDX-License-Identifier: BSD-3-Clause-Clear
2d5c65159SKalle Valo /*
3d5c65159SKalle Valo  * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
4d5c65159SKalle Valo  */
5d5c65159SKalle Valo 
6d5c65159SKalle Valo #include "testmode.h"
7d5c65159SKalle Valo #include <net/netlink.h>
8d5c65159SKalle Valo #include "debug.h"
9d5c65159SKalle Valo #include "wmi.h"
10d5c65159SKalle Valo #include "hw.h"
11d5c65159SKalle Valo #include "core.h"
12d5c65159SKalle Valo #include "testmode_i.h"
13d5c65159SKalle Valo 
14d5c65159SKalle Valo static const struct nla_policy ath11k_tm_policy[ATH11K_TM_ATTR_MAX + 1] = {
15d5c65159SKalle Valo 	[ATH11K_TM_ATTR_CMD]		= { .type = NLA_U32 },
16d5c65159SKalle Valo 	[ATH11K_TM_ATTR_DATA]		= { .type = NLA_BINARY,
17d5c65159SKalle Valo 					    .len = ATH11K_TM_DATA_MAX_LEN },
18d5c65159SKalle Valo 	[ATH11K_TM_ATTR_WMI_CMDID]	= { .type = NLA_U32 },
19d5c65159SKalle Valo 	[ATH11K_TM_ATTR_VERSION_MAJOR]	= { .type = NLA_U32 },
20d5c65159SKalle Valo 	[ATH11K_TM_ATTR_VERSION_MINOR]	= { .type = NLA_U32 },
21d5c65159SKalle Valo };
22d5c65159SKalle Valo 
23d5c65159SKalle Valo /* Returns true if callee consumes the skb and the skb should be discarded.
24d5c65159SKalle Valo  * Returns false if skb is not used. Does not sleep.
25d5c65159SKalle Valo  */
26d5c65159SKalle Valo bool ath11k_tm_event_wmi(struct ath11k *ar, u32 cmd_id, struct sk_buff *skb)
27d5c65159SKalle Valo {
28d5c65159SKalle Valo 	struct sk_buff *nl_skb;
29d5c65159SKalle Valo 	bool consumed;
30d5c65159SKalle Valo 	int ret;
31d5c65159SKalle Valo 
32d5c65159SKalle Valo 	ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE,
33d5c65159SKalle Valo 		   "testmode event wmi cmd_id %d skb %pK skb->len %d\n",
34d5c65159SKalle Valo 		   cmd_id, skb, skb->len);
35d5c65159SKalle Valo 
36d5c65159SKalle Valo 	ath11k_dbg_dump(ar->ab, ATH11K_DBG_TESTMODE, NULL, "", skb->data, skb->len);
37d5c65159SKalle Valo 
38d5c65159SKalle Valo 	spin_lock_bh(&ar->data_lock);
39d5c65159SKalle Valo 
40d5c65159SKalle Valo 	consumed = true;
41d5c65159SKalle Valo 
42d5c65159SKalle Valo 	nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,
43d5c65159SKalle Valo 						   2 * sizeof(u32) + skb->len,
44d5c65159SKalle Valo 						   GFP_ATOMIC);
45d5c65159SKalle Valo 	if (!nl_skb) {
46d5c65159SKalle Valo 		ath11k_warn(ar->ab,
47d5c65159SKalle Valo 			    "failed to allocate skb for testmode wmi event\n");
48d5c65159SKalle Valo 		goto out;
49d5c65159SKalle Valo 	}
50d5c65159SKalle Valo 
51d5c65159SKalle Valo 	ret = nla_put_u32(nl_skb, ATH11K_TM_ATTR_CMD, ATH11K_TM_CMD_WMI);
52d5c65159SKalle Valo 	if (ret) {
53d5c65159SKalle Valo 		ath11k_warn(ar->ab,
54d5c65159SKalle Valo 			    "failed to to put testmode wmi event cmd attribute: %d\n",
55d5c65159SKalle Valo 			    ret);
56d5c65159SKalle Valo 		kfree_skb(nl_skb);
57d5c65159SKalle Valo 		goto out;
58d5c65159SKalle Valo 	}
59d5c65159SKalle Valo 
60d5c65159SKalle Valo 	ret = nla_put_u32(nl_skb, ATH11K_TM_ATTR_WMI_CMDID, cmd_id);
61d5c65159SKalle Valo 	if (ret) {
62d5c65159SKalle Valo 		ath11k_warn(ar->ab,
63d5c65159SKalle Valo 			    "failed to to put testmode wmi even cmd_id: %d\n",
64d5c65159SKalle Valo 			    ret);
65d5c65159SKalle Valo 		kfree_skb(nl_skb);
66d5c65159SKalle Valo 		goto out;
67d5c65159SKalle Valo 	}
68d5c65159SKalle Valo 
69d5c65159SKalle Valo 	ret = nla_put(nl_skb, ATH11K_TM_ATTR_DATA, skb->len, skb->data);
70d5c65159SKalle Valo 	if (ret) {
71d5c65159SKalle Valo 		ath11k_warn(ar->ab,
72d5c65159SKalle Valo 			    "failed to copy skb to testmode wmi event: %d\n",
73d5c65159SKalle Valo 			    ret);
74d5c65159SKalle Valo 		kfree_skb(nl_skb);
75d5c65159SKalle Valo 		goto out;
76d5c65159SKalle Valo 	}
77d5c65159SKalle Valo 
78d5c65159SKalle Valo 	cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
79d5c65159SKalle Valo 
80d5c65159SKalle Valo out:
81d5c65159SKalle Valo 	spin_unlock_bh(&ar->data_lock);
82d5c65159SKalle Valo 
83d5c65159SKalle Valo 	return consumed;
84d5c65159SKalle Valo }
85d5c65159SKalle Valo 
86d5c65159SKalle Valo static int ath11k_tm_cmd_get_version(struct ath11k *ar, struct nlattr *tb[])
87d5c65159SKalle Valo {
88d5c65159SKalle Valo 	struct sk_buff *skb;
89d5c65159SKalle Valo 	int ret;
90d5c65159SKalle Valo 
91d5c65159SKalle Valo 	ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE,
92d5c65159SKalle Valo 		   "testmode cmd get version_major %d version_minor %d\n",
93d5c65159SKalle Valo 		   ATH11K_TESTMODE_VERSION_MAJOR,
94d5c65159SKalle Valo 		   ATH11K_TESTMODE_VERSION_MINOR);
95d5c65159SKalle Valo 
96d5c65159SKalle Valo 	skb = cfg80211_testmode_alloc_reply_skb(ar->hw->wiphy,
97d5c65159SKalle Valo 						nla_total_size(sizeof(u32)));
98d5c65159SKalle Valo 	if (!skb)
99d5c65159SKalle Valo 		return -ENOMEM;
100d5c65159SKalle Valo 
101d5c65159SKalle Valo 	ret = nla_put_u32(skb, ATH11K_TM_ATTR_VERSION_MAJOR,
102d5c65159SKalle Valo 			  ATH11K_TESTMODE_VERSION_MAJOR);
103d5c65159SKalle Valo 	if (ret) {
104d5c65159SKalle Valo 		kfree_skb(skb);
105d5c65159SKalle Valo 		return ret;
106d5c65159SKalle Valo 	}
107d5c65159SKalle Valo 
108d5c65159SKalle Valo 	ret = nla_put_u32(skb, ATH11K_TM_ATTR_VERSION_MINOR,
109d5c65159SKalle Valo 			  ATH11K_TESTMODE_VERSION_MINOR);
110d5c65159SKalle Valo 	if (ret) {
111d5c65159SKalle Valo 		kfree_skb(skb);
112d5c65159SKalle Valo 		return ret;
113d5c65159SKalle Valo 	}
114d5c65159SKalle Valo 
115d5c65159SKalle Valo 	return cfg80211_testmode_reply(skb);
116d5c65159SKalle Valo }
117d5c65159SKalle Valo 
118d5c65159SKalle Valo static int ath11k_tm_cmd_wmi(struct ath11k *ar, struct nlattr *tb[])
119d5c65159SKalle Valo {
120d5c65159SKalle Valo 	struct ath11k_pdev_wmi *wmi = ar->wmi;
121d5c65159SKalle Valo 	struct sk_buff *skb;
122d5c65159SKalle Valo 	u32 cmd_id, buf_len;
123d5c65159SKalle Valo 	int ret;
124d5c65159SKalle Valo 	void *buf;
125d5c65159SKalle Valo 
126d5c65159SKalle Valo 	mutex_lock(&ar->conf_mutex);
127d5c65159SKalle Valo 
128d5c65159SKalle Valo 	if (ar->state != ATH11K_STATE_ON) {
129d5c65159SKalle Valo 		ret = -ENETDOWN;
130d5c65159SKalle Valo 		goto out;
131d5c65159SKalle Valo 	}
132d5c65159SKalle Valo 
133d5c65159SKalle Valo 	if (!tb[ATH11K_TM_ATTR_DATA]) {
134d5c65159SKalle Valo 		ret = -EINVAL;
135d5c65159SKalle Valo 		goto out;
136d5c65159SKalle Valo 	}
137d5c65159SKalle Valo 
138d5c65159SKalle Valo 	if (!tb[ATH11K_TM_ATTR_WMI_CMDID]) {
139d5c65159SKalle Valo 		ret = -EINVAL;
140d5c65159SKalle Valo 		goto out;
141d5c65159SKalle Valo 	}
142d5c65159SKalle Valo 
143d5c65159SKalle Valo 	buf = nla_data(tb[ATH11K_TM_ATTR_DATA]);
144d5c65159SKalle Valo 	buf_len = nla_len(tb[ATH11K_TM_ATTR_DATA]);
145d5c65159SKalle Valo 	cmd_id = nla_get_u32(tb[ATH11K_TM_ATTR_WMI_CMDID]);
146d5c65159SKalle Valo 
147d5c65159SKalle Valo 	ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE,
148d5c65159SKalle Valo 		   "testmode cmd wmi cmd_id %d buf %pK buf_len %d\n",
149d5c65159SKalle Valo 		   cmd_id, buf, buf_len);
150d5c65159SKalle Valo 
151d5c65159SKalle Valo 	ath11k_dbg_dump(ar->ab, ATH11K_DBG_TESTMODE, NULL, "", buf, buf_len);
152d5c65159SKalle Valo 
153*6bc9d6f7SJohn Crispin 	skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, buf_len);
154d5c65159SKalle Valo 	if (!skb) {
155d5c65159SKalle Valo 		ret = -ENOMEM;
156d5c65159SKalle Valo 		goto out;
157d5c65159SKalle Valo 	}
158d5c65159SKalle Valo 
159d5c65159SKalle Valo 	memcpy(skb->data, buf, buf_len);
160d5c65159SKalle Valo 
161d5c65159SKalle Valo 	ret = ath11k_wmi_cmd_send(wmi, skb, cmd_id);
162d5c65159SKalle Valo 	if (ret) {
163d5c65159SKalle Valo 		dev_kfree_skb(skb);
164d5c65159SKalle Valo 		ath11k_warn(ar->ab, "failed to transmit wmi command (testmode): %d\n",
165d5c65159SKalle Valo 			    ret);
166d5c65159SKalle Valo 		goto out;
167d5c65159SKalle Valo 	}
168d5c65159SKalle Valo 
169d5c65159SKalle Valo 	ret = 0;
170d5c65159SKalle Valo 
171d5c65159SKalle Valo out:
172d5c65159SKalle Valo 	mutex_unlock(&ar->conf_mutex);
173d5c65159SKalle Valo 	return ret;
174d5c65159SKalle Valo }
175d5c65159SKalle Valo 
176d5c65159SKalle Valo int ath11k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
177d5c65159SKalle Valo 		  void *data, int len)
178d5c65159SKalle Valo {
179d5c65159SKalle Valo 	struct ath11k *ar = hw->priv;
180d5c65159SKalle Valo 	struct nlattr *tb[ATH11K_TM_ATTR_MAX + 1];
181d5c65159SKalle Valo 	int ret;
182d5c65159SKalle Valo 
183d5c65159SKalle Valo 	ret = nla_parse(tb, ATH11K_TM_ATTR_MAX, data, len, ath11k_tm_policy,
184d5c65159SKalle Valo 			NULL);
185d5c65159SKalle Valo 	if (ret)
186d5c65159SKalle Valo 		return ret;
187d5c65159SKalle Valo 
188d5c65159SKalle Valo 	if (!tb[ATH11K_TM_ATTR_CMD])
189d5c65159SKalle Valo 		return -EINVAL;
190d5c65159SKalle Valo 
191d5c65159SKalle Valo 	switch (nla_get_u32(tb[ATH11K_TM_ATTR_CMD])) {
192d5c65159SKalle Valo 	case ATH11K_TM_CMD_GET_VERSION:
193d5c65159SKalle Valo 		return ath11k_tm_cmd_get_version(ar, tb);
194d5c65159SKalle Valo 	case ATH11K_TM_CMD_WMI:
195d5c65159SKalle Valo 		return ath11k_tm_cmd_wmi(ar, tb);
196d5c65159SKalle Valo 	default:
197d5c65159SKalle Valo 		return -EOPNOTSUPP;
198d5c65159SKalle Valo 	}
199d5c65159SKalle Valo }
200