diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath6kl/testmode.c')
-rw-r--r-- | drivers/net/wireless/ath/ath6kl/testmode.c | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/ath6kl/testmode.c b/drivers/net/wireless/ath/ath6kl/testmode.c new file mode 100644 index 000000000000..381eb66a605f --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/testmode.c | |||
@@ -0,0 +1,167 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2010-2011 Atheros Communications Inc. | ||
3 | * | ||
4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
5 | * purpose with or without fee is hereby granted, provided that the above | ||
6 | * copyright notice and this permission notice appear in all copies. | ||
7 | * | ||
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
15 | */ | ||
16 | |||
17 | #include "testmode.h" | ||
18 | |||
19 | #include <net/netlink.h> | ||
20 | |||
21 | enum ath6kl_tm_attr { | ||
22 | __ATH6KL_TM_ATTR_INVALID = 0, | ||
23 | ATH6KL_TM_ATTR_CMD = 1, | ||
24 | ATH6KL_TM_ATTR_DATA = 2, | ||
25 | |||
26 | /* keep last */ | ||
27 | __ATH6KL_TM_ATTR_AFTER_LAST, | ||
28 | ATH6KL_TM_ATTR_MAX = __ATH6KL_TM_ATTR_AFTER_LAST - 1, | ||
29 | }; | ||
30 | |||
31 | enum ath6kl_tm_cmd { | ||
32 | ATH6KL_TM_CMD_TCMD = 0, | ||
33 | ATH6KL_TM_CMD_RX_REPORT = 1, | ||
34 | }; | ||
35 | |||
36 | #define ATH6KL_TM_DATA_MAX_LEN 5000 | ||
37 | |||
38 | static const struct nla_policy ath6kl_tm_policy[ATH6KL_TM_ATTR_MAX + 1] = { | ||
39 | [ATH6KL_TM_ATTR_CMD] = { .type = NLA_U32 }, | ||
40 | [ATH6KL_TM_ATTR_DATA] = { .type = NLA_BINARY, | ||
41 | .len = ATH6KL_TM_DATA_MAX_LEN }, | ||
42 | }; | ||
43 | |||
44 | void ath6kl_tm_rx_report_event(struct ath6kl *ar, void *buf, size_t buf_len) | ||
45 | { | ||
46 | if (down_interruptible(&ar->sem)) | ||
47 | return; | ||
48 | |||
49 | kfree(ar->tm.rx_report); | ||
50 | |||
51 | ar->tm.rx_report = kmemdup(buf, buf_len, GFP_KERNEL); | ||
52 | ar->tm.rx_report_len = buf_len; | ||
53 | |||
54 | up(&ar->sem); | ||
55 | |||
56 | wake_up(&ar->event_wq); | ||
57 | } | ||
58 | |||
59 | static int ath6kl_tm_rx_report(struct ath6kl *ar, void *buf, size_t buf_len, | ||
60 | struct sk_buff *skb) | ||
61 | { | ||
62 | int ret = 0; | ||
63 | long left; | ||
64 | |||
65 | if (down_interruptible(&ar->sem)) | ||
66 | return -ERESTARTSYS; | ||
67 | |||
68 | if (!test_bit(WMI_READY, &ar->flag)) { | ||
69 | ret = -EIO; | ||
70 | goto out; | ||
71 | } | ||
72 | |||
73 | if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) { | ||
74 | ret = -EBUSY; | ||
75 | goto out; | ||
76 | } | ||
77 | |||
78 | if (ath6kl_wmi_test_cmd(ar->wmi, buf, buf_len) < 0) { | ||
79 | up(&ar->sem); | ||
80 | return -EIO; | ||
81 | } | ||
82 | |||
83 | left = wait_event_interruptible_timeout(ar->event_wq, | ||
84 | ar->tm.rx_report != NULL, | ||
85 | WMI_TIMEOUT); | ||
86 | |||
87 | if (left == 0) { | ||
88 | ret = -ETIMEDOUT; | ||
89 | goto out; | ||
90 | } else if (left < 0) { | ||
91 | ret = left; | ||
92 | goto out; | ||
93 | } | ||
94 | |||
95 | if (ar->tm.rx_report == NULL || ar->tm.rx_report_len == 0) { | ||
96 | ret = -EINVAL; | ||
97 | goto out; | ||
98 | } | ||
99 | |||
100 | NLA_PUT(skb, ATH6KL_TM_ATTR_DATA, ar->tm.rx_report_len, | ||
101 | ar->tm.rx_report); | ||
102 | |||
103 | kfree(ar->tm.rx_report); | ||
104 | ar->tm.rx_report = NULL; | ||
105 | |||
106 | out: | ||
107 | up(&ar->sem); | ||
108 | |||
109 | return ret; | ||
110 | |||
111 | nla_put_failure: | ||
112 | ret = -ENOBUFS; | ||
113 | goto out; | ||
114 | } | ||
115 | |||
116 | int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len) | ||
117 | { | ||
118 | struct ath6kl *ar = wiphy_priv(wiphy); | ||
119 | struct nlattr *tb[ATH6KL_TM_ATTR_MAX + 1]; | ||
120 | int err, buf_len, reply_len; | ||
121 | struct sk_buff *skb; | ||
122 | void *buf; | ||
123 | |||
124 | err = nla_parse(tb, ATH6KL_TM_ATTR_MAX, data, len, | ||
125 | ath6kl_tm_policy); | ||
126 | if (err) | ||
127 | return err; | ||
128 | |||
129 | if (!tb[ATH6KL_TM_ATTR_CMD]) | ||
130 | return -EINVAL; | ||
131 | |||
132 | switch (nla_get_u32(tb[ATH6KL_TM_ATTR_CMD])) { | ||
133 | case ATH6KL_TM_CMD_TCMD: | ||
134 | if (!tb[ATH6KL_TM_ATTR_DATA]) | ||
135 | return -EINVAL; | ||
136 | |||
137 | buf = nla_data(tb[ATH6KL_TM_ATTR_DATA]); | ||
138 | buf_len = nla_len(tb[ATH6KL_TM_ATTR_DATA]); | ||
139 | |||
140 | ath6kl_wmi_test_cmd(ar->wmi, buf, buf_len); | ||
141 | |||
142 | return 0; | ||
143 | |||
144 | break; | ||
145 | case ATH6KL_TM_CMD_RX_REPORT: | ||
146 | if (!tb[ATH6KL_TM_ATTR_DATA]) | ||
147 | return -EINVAL; | ||
148 | |||
149 | buf = nla_data(tb[ATH6KL_TM_ATTR_DATA]); | ||
150 | buf_len = nla_len(tb[ATH6KL_TM_ATTR_DATA]); | ||
151 | |||
152 | reply_len = nla_total_size(ATH6KL_TM_DATA_MAX_LEN); | ||
153 | skb = cfg80211_testmode_alloc_reply_skb(wiphy, reply_len); | ||
154 | if (!skb) | ||
155 | return -ENOMEM; | ||
156 | |||
157 | err = ath6kl_tm_rx_report(ar, buf, buf_len, skb); | ||
158 | if (err < 0) { | ||
159 | kfree_skb(skb); | ||
160 | return err; | ||
161 | } | ||
162 | |||
163 | return cfg80211_testmode_reply(skb); | ||
164 | default: | ||
165 | return -EOPNOTSUPP; | ||
166 | } | ||
167 | } | ||