1c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo/*
2c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo * This file is part of wl1271
3c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo *
4c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo * Copyright (C) 2010 Nokia Corporation
5c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo *
6c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo * Contact: Luciano Coelho <luciano.coelho@nokia.com>
7c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo *
8c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo * This program is free software; you can redistribute it and/or
9c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo * modify it under the terms of the GNU General Public License
10c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo * version 2 as published by the Free Software Foundation.
11c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo *
12c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo * This program is distributed in the hope that it will be useful, but
13c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo * WITHOUT ANY WARRANTY; without even the implied warranty of
14c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo * General Public License for more details.
16c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo *
17c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo * You should have received a copy of the GNU General Public License
18c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo * along with this program; if not, write to the Free Software
19c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo * 02110-1301 USA
21c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo *
22c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo */
2300d201001bd4e8a46e3d03c970abcb72256c368bShahar Levi#include "testmode.h"
24c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
255a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
26c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo#include <net/genetlink.h>
27c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
2800d201001bd4e8a46e3d03c970abcb72256c368bShahar Levi#include "wl12xx.h"
290f4e31222a2c0b93f25a87effd2033cb78c7a79cLuciano Coelho#include "debug.h"
3000d201001bd4e8a46e3d03c970abcb72256c368bShahar Levi#include "acx.h"
31bc765bf3b9a095b3e41c8cda80643901884c3dd4Shahar Levi#include "reg.h"
32abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller#include "ps.h"
3375f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho#include "io.h"
34c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
35c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo#define WL1271_TM_MAX_DATA_LENGTH 1024
36c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
37c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valoenum wl1271_tm_commands {
38c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	WL1271_TM_CMD_UNSPEC,
39c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	WL1271_TM_CMD_TEST,
40c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	WL1271_TM_CMD_INTERROGATE,
41c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	WL1271_TM_CMD_CONFIGURE,
423f1764945eaac532c20ab1f23afa352a40f797b2Pontus Fuchs	WL1271_TM_CMD_NVS_PUSH,		/* Not in use. Keep to not break ABI */
43c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	WL1271_TM_CMD_SET_PLT_MODE,
44e285a5250c0772c5596a9137041a96b2c1f744d6Eliad Peller	WL1271_TM_CMD_RECOVER,
4575f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho	WL1271_TM_CMD_GET_MAC,
46c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
47c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	__WL1271_TM_CMD_AFTER_LAST
48c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo};
49c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo#define WL1271_TM_CMD_MAX (__WL1271_TM_CMD_AFTER_LAST - 1)
50c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
51c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valoenum wl1271_tm_attrs {
52c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	WL1271_TM_ATTR_UNSPEC,
53c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	WL1271_TM_ATTR_CMD_ID,
54c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	WL1271_TM_ATTR_ANSWER,
55c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	WL1271_TM_ATTR_DATA,
56c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	WL1271_TM_ATTR_IE_ID,
57c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	WL1271_TM_ATTR_PLT_MODE,
58c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
59c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	__WL1271_TM_ATTR_AFTER_LAST
60c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo};
61c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo#define WL1271_TM_ATTR_MAX (__WL1271_TM_ATTR_AFTER_LAST - 1)
62c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
63c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valostatic struct nla_policy wl1271_tm_policy[WL1271_TM_ATTR_MAX + 1] = {
64c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	[WL1271_TM_ATTR_CMD_ID] =	{ .type = NLA_U32 },
65c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	[WL1271_TM_ATTR_ANSWER] =	{ .type = NLA_U8 },
66c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	[WL1271_TM_ATTR_DATA] =		{ .type = NLA_BINARY,
67c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo					  .len = WL1271_TM_MAX_DATA_LENGTH },
68c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	[WL1271_TM_ATTR_IE_ID] =	{ .type = NLA_U32 },
69c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	[WL1271_TM_ATTR_PLT_MODE] =	{ .type = NLA_U32 },
70c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo};
71c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
72c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
73c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valostatic int wl1271_tm_cmd_test(struct wl1271 *wl, struct nlattr *tb[])
74c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo{
75c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	int buf_len, ret, len;
76c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	struct sk_buff *skb;
77c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	void *buf;
78c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	u8 answer = 0;
79c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
80c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	wl1271_debug(DEBUG_TESTMODE, "testmode cmd test");
81c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
82c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	if (!tb[WL1271_TM_ATTR_DATA])
83c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo		return -EINVAL;
84c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
85c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	buf = nla_data(tb[WL1271_TM_ATTR_DATA]);
86c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	buf_len = nla_len(tb[WL1271_TM_ATTR_DATA]);
87c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
88c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	if (tb[WL1271_TM_ATTR_ANSWER])
89c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo		answer = nla_get_u8(tb[WL1271_TM_ATTR_ANSWER]);
90c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
91c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	if (buf_len > sizeof(struct wl1271_command))
92c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo		return -EMSGSIZE;
93c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
94c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	mutex_lock(&wl->mutex);
95c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
96abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller	if (wl->state == WL1271_STATE_OFF) {
97abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller		ret = -EINVAL;
98abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller		goto out;
99abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller	}
100abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller
101abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller	ret = wl1271_ps_elp_wakeup(wl);
102abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller	if (ret < 0)
103abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller		goto out;
104abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller
105abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller	ret = wl1271_cmd_test(wl, buf, buf_len, answer);
106c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	if (ret < 0) {
107c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo		wl1271_warning("testmode cmd test failed: %d", ret);
108abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller		goto out_sleep;
109c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	}
110c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
111c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	if (answer) {
112c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo		len = nla_total_size(buf_len);
113c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo		skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, len);
114abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller		if (!skb) {
115abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller			ret = -ENOMEM;
116abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller			goto out_sleep;
117abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller		}
118c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
119c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo		NLA_PUT(skb, WL1271_TM_ATTR_DATA, buf_len, buf);
120c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo		ret = cfg80211_testmode_reply(skb);
121c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo		if (ret < 0)
122abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller			goto out_sleep;
123c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	}
124c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
125abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Pellerout_sleep:
126abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller	wl1271_ps_elp_sleep(wl);
127abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Pellerout:
128abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller	mutex_unlock(&wl->mutex);
129abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller
130abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller	return ret;
131c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
132c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valonla_put_failure:
133c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	kfree_skb(skb);
134abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller	ret = -EMSGSIZE;
135abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller	goto out_sleep;
136c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo}
137c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
138c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valostatic int wl1271_tm_cmd_interrogate(struct wl1271 *wl, struct nlattr *tb[])
139c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo{
140c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	int ret;
141c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	struct wl1271_command *cmd;
142c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	struct sk_buff *skb;
143c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	u8 ie_id;
144c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
145c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	wl1271_debug(DEBUG_TESTMODE, "testmode cmd interrogate");
146c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
147c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	if (!tb[WL1271_TM_ATTR_IE_ID])
148c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo		return -EINVAL;
149c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
150c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	ie_id = nla_get_u8(tb[WL1271_TM_ATTR_IE_ID]);
151c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
152abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller	mutex_lock(&wl->mutex);
153abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller
154abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller	if (wl->state == WL1271_STATE_OFF) {
155abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller		ret = -EINVAL;
156abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller		goto out;
157abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller	}
158abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller
159abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller	ret = wl1271_ps_elp_wakeup(wl);
160abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller	if (ret < 0)
161abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller		goto out;
162abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller
163c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
164abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller	if (!cmd) {
165abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller		ret = -ENOMEM;
166abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller		goto out_sleep;
167abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller	}
168c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
169c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	ret = wl1271_cmd_interrogate(wl, ie_id, cmd, sizeof(*cmd));
170c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	if (ret < 0) {
171c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo		wl1271_warning("testmode cmd interrogate failed: %d", ret);
172abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller		goto out_free;
173c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	}
174c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
175c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, sizeof(*cmd));
176f8afdf481f0fef5e170c6c928cec42879d505654Julia Lawall	if (!skb) {
177abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller		ret = -ENOMEM;
178abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller		goto out_free;
179f8afdf481f0fef5e170c6c928cec42879d505654Julia Lawall	}
180c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
181c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	NLA_PUT(skb, WL1271_TM_ATTR_DATA, sizeof(*cmd), cmd);
1823dbb5846db1f5df3619b927cc2a7dcaf65a38f1eEliad Peller	ret = cfg80211_testmode_reply(skb);
1833dbb5846db1f5df3619b927cc2a7dcaf65a38f1eEliad Peller	if (ret < 0)
1843dbb5846db1f5df3619b927cc2a7dcaf65a38f1eEliad Peller		goto out_free;
185c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
186abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Pellerout_free:
187abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller	kfree(cmd);
188abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Pellerout_sleep:
189abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller	wl1271_ps_elp_sleep(wl);
190abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Pellerout:
191abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller	mutex_unlock(&wl->mutex);
192abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller
193abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller	return ret;
194c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
195c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valonla_put_failure:
196c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	kfree_skb(skb);
197abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller	ret = -EMSGSIZE;
198abc47470ef63cdde2efdf358ae373afb16f358c0Eliad Peller	goto out_free;
199c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo}
200c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
201c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valostatic int wl1271_tm_cmd_configure(struct wl1271 *wl, struct nlattr *tb[])
202c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo{
203c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	int buf_len, ret;
204c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	void *buf;
205c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	u8 ie_id;
206c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
207c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	wl1271_debug(DEBUG_TESTMODE, "testmode cmd configure");
208c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
209c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	if (!tb[WL1271_TM_ATTR_DATA])
210c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo		return -EINVAL;
211c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	if (!tb[WL1271_TM_ATTR_IE_ID])
212c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo		return -EINVAL;
213c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
214c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	ie_id = nla_get_u8(tb[WL1271_TM_ATTR_IE_ID]);
215c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	buf = nla_data(tb[WL1271_TM_ATTR_DATA]);
216c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	buf_len = nla_len(tb[WL1271_TM_ATTR_DATA]);
217c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
218c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	if (buf_len > sizeof(struct wl1271_command))
219c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo		return -EMSGSIZE;
220c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
221c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	mutex_lock(&wl->mutex);
222c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	ret = wl1271_cmd_configure(wl, ie_id, buf, buf_len);
223c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	mutex_unlock(&wl->mutex);
224c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
225c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	if (ret < 0) {
226c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo		wl1271_warning("testmode cmd configure failed: %d", ret);
227c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo		return ret;
228c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	}
229c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
230c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	return 0;
231c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo}
232c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
233c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valostatic int wl1271_tm_cmd_set_plt_mode(struct wl1271 *wl, struct nlattr *tb[])
234c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo{
235c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	u32 val;
236c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	int ret;
237c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
238c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	wl1271_debug(DEBUG_TESTMODE, "testmode cmd set plt mode");
239c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
240c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	if (!tb[WL1271_TM_ATTR_PLT_MODE])
241c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo		return -EINVAL;
242c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
243c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	val = nla_get_u32(tb[WL1271_TM_ATTR_PLT_MODE]);
244c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
245c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	switch (val) {
246c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	case 0:
247c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo		ret = wl1271_plt_stop(wl);
248c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo		break;
249c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	case 1:
250c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo		ret = wl1271_plt_start(wl);
251c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo		break;
252c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	default:
253c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo		ret = -EINVAL;
254c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo		break;
255c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	}
256c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
257c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	return ret;
258c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo}
259c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
260e285a5250c0772c5596a9137041a96b2c1f744d6Eliad Pellerstatic int wl1271_tm_cmd_recover(struct wl1271 *wl, struct nlattr *tb[])
261e285a5250c0772c5596a9137041a96b2c1f744d6Eliad Peller{
262e285a5250c0772c5596a9137041a96b2c1f744d6Eliad Peller	wl1271_debug(DEBUG_TESTMODE, "testmode cmd recover");
263e285a5250c0772c5596a9137041a96b2c1f744d6Eliad Peller
264baacb9aed020b890ddf6a57837a169092a25fc9bIdo Yariv	wl12xx_queue_recovery_work(wl);
265e285a5250c0772c5596a9137041a96b2c1f744d6Eliad Peller
266e285a5250c0772c5596a9137041a96b2c1f744d6Eliad Peller	return 0;
267e285a5250c0772c5596a9137041a96b2c1f744d6Eliad Peller}
268e285a5250c0772c5596a9137041a96b2c1f744d6Eliad Peller
26975f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelhostatic int wl12xx_tm_cmd_get_mac(struct wl1271 *wl, struct nlattr *tb[])
27075f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho{
27175f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho	struct sk_buff *skb;
27275f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho	u8 mac_addr[ETH_ALEN];
27375f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho	int ret = 0;
27475f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho
27575f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho	mutex_lock(&wl->mutex);
27675f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho
2773fcdab7066a31ae90ac2beba7d38e8e606374998Eliad Peller	if (!wl->plt) {
27875f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho		ret = -EINVAL;
27975f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho		goto out;
28075f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho	}
28175f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho
28275f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho	if (wl->fuse_oui_addr == 0 && wl->fuse_nic_addr == 0) {
28375f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho		ret = -EOPNOTSUPP;
28475f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho		goto out;
28575f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho	}
28675f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho
28775f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho	mac_addr[0] = (u8)(wl->fuse_oui_addr >> 16);
28875f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho	mac_addr[1] = (u8)(wl->fuse_oui_addr >> 8);
28975f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho	mac_addr[2] = (u8) wl->fuse_oui_addr;
29075f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho	mac_addr[3] = (u8)(wl->fuse_nic_addr >> 16);
29175f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho	mac_addr[4] = (u8)(wl->fuse_nic_addr >> 8);
29275f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho	mac_addr[5] = (u8) wl->fuse_nic_addr;
29375f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho
29475f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho	skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, ETH_ALEN);
29575f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho	if (!skb) {
29675f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho		ret = -ENOMEM;
29775f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho		goto out;
29875f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho	}
29975f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho
30075f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho	NLA_PUT(skb, WL1271_TM_ATTR_DATA, ETH_ALEN, mac_addr);
30175f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho	ret = cfg80211_testmode_reply(skb);
30275f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho	if (ret < 0)
30375f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho		goto out;
30475f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho
30575f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelhoout:
30675f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho	mutex_unlock(&wl->mutex);
30775f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho	return ret;
30875f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho
30975f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelhonla_put_failure:
31075f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho	kfree_skb(skb);
31175f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho	ret = -EMSGSIZE;
31275f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho	goto out;
31375f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho}
31475f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho
315c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valoint wl1271_tm_cmd(struct ieee80211_hw *hw, void *data, int len)
316c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo{
317c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	struct wl1271 *wl = hw->priv;
318c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	struct nlattr *tb[WL1271_TM_ATTR_MAX + 1];
319c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	int err;
320c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
321c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	err = nla_parse(tb, WL1271_TM_ATTR_MAX, data, len, wl1271_tm_policy);
322c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	if (err)
323c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo		return err;
324c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
325c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	if (!tb[WL1271_TM_ATTR_CMD_ID])
326c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo		return -EINVAL;
327c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo
328c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	switch (nla_get_u32(tb[WL1271_TM_ATTR_CMD_ID])) {
329c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	case WL1271_TM_CMD_TEST:
330c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo		return wl1271_tm_cmd_test(wl, tb);
331c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	case WL1271_TM_CMD_INTERROGATE:
332c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo		return wl1271_tm_cmd_interrogate(wl, tb);
333c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	case WL1271_TM_CMD_CONFIGURE:
334c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo		return wl1271_tm_cmd_configure(wl, tb);
335c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	case WL1271_TM_CMD_SET_PLT_MODE:
336c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo		return wl1271_tm_cmd_set_plt_mode(wl, tb);
337e285a5250c0772c5596a9137041a96b2c1f744d6Eliad Peller	case WL1271_TM_CMD_RECOVER:
338e285a5250c0772c5596a9137041a96b2c1f744d6Eliad Peller		return wl1271_tm_cmd_recover(wl, tb);
33975f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho	case WL1271_TM_CMD_GET_MAC:
34075f25548bea11d35b7cb0879eea595d3f86a0cc4Luciano Coelho		return wl12xx_tm_cmd_get_mac(wl, tb);
341c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	default:
342c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo		return -EOPNOTSUPP;
343c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo	}
344c8c90873520ef4c201cfd03b4892ca8bbf551e2eKalle Valo}
345