xref: /linux/drivers/net/wireless/ath/ath9k/wmi.c (revision cc4589ebfae6f8dbb5cf880a0a67eedab3416492)
1 /*
2  * Copyright (c) 2010 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 "htc.h"
18 
19 static const char *wmi_cmd_to_name(enum wmi_cmd_id wmi_cmd)
20 {
21 	switch (wmi_cmd) {
22 	case WMI_ECHO_CMDID:
23 		return "WMI_ECHO_CMDID";
24 	case WMI_ACCESS_MEMORY_CMDID:
25 		return "WMI_ACCESS_MEMORY_CMDID";
26 	case WMI_DISABLE_INTR_CMDID:
27 		return "WMI_DISABLE_INTR_CMDID";
28 	case WMI_ENABLE_INTR_CMDID:
29 		return "WMI_ENABLE_INTR_CMDID";
30 	case WMI_RX_LINK_CMDID:
31 		return "WMI_RX_LINK_CMDID";
32 	case WMI_ATH_INIT_CMDID:
33 		return "WMI_ATH_INIT_CMDID";
34 	case WMI_ABORT_TXQ_CMDID:
35 		return "WMI_ABORT_TXQ_CMDID";
36 	case WMI_STOP_TX_DMA_CMDID:
37 		return "WMI_STOP_TX_DMA_CMDID";
38 	case WMI_STOP_DMA_RECV_CMDID:
39 		return "WMI_STOP_DMA_RECV_CMDID";
40 	case WMI_ABORT_TX_DMA_CMDID:
41 		return "WMI_ABORT_TX_DMA_CMDID";
42 	case WMI_DRAIN_TXQ_CMDID:
43 		return "WMI_DRAIN_TXQ_CMDID";
44 	case WMI_DRAIN_TXQ_ALL_CMDID:
45 		return "WMI_DRAIN_TXQ_ALL_CMDID";
46 	case WMI_START_RECV_CMDID:
47 		return "WMI_START_RECV_CMDID";
48 	case WMI_STOP_RECV_CMDID:
49 		return "WMI_STOP_RECV_CMDID";
50 	case WMI_FLUSH_RECV_CMDID:
51 		return "WMI_FLUSH_RECV_CMDID";
52 	case WMI_SET_MODE_CMDID:
53 		return "WMI_SET_MODE_CMDID";
54 	case WMI_RESET_CMDID:
55 		return "WMI_RESET_CMDID";
56 	case WMI_NODE_CREATE_CMDID:
57 		return "WMI_NODE_CREATE_CMDID";
58 	case WMI_NODE_REMOVE_CMDID:
59 		return "WMI_NODE_REMOVE_CMDID";
60 	case WMI_VAP_REMOVE_CMDID:
61 		return "WMI_VAP_REMOVE_CMDID";
62 	case WMI_VAP_CREATE_CMDID:
63 		return "WMI_VAP_CREATE_CMDID";
64 	case WMI_BEACON_UPDATE_CMDID:
65 		return "WMI_BEACON_UPDATE_CMDID";
66 	case WMI_REG_READ_CMDID:
67 		return "WMI_REG_READ_CMDID";
68 	case WMI_REG_WRITE_CMDID:
69 		return "WMI_REG_WRITE_CMDID";
70 	case WMI_RC_STATE_CHANGE_CMDID:
71 		return "WMI_RC_STATE_CHANGE_CMDID";
72 	case WMI_RC_RATE_UPDATE_CMDID:
73 		return "WMI_RC_RATE_UPDATE_CMDID";
74 	case WMI_DEBUG_INFO_CMDID:
75 		return "WMI_DEBUG_INFO_CMDID";
76 	case WMI_HOST_ATTACH:
77 		return "WMI_HOST_ATTACH";
78 	case WMI_TARGET_IC_UPDATE_CMDID:
79 		return "WMI_TARGET_IC_UPDATE_CMDID";
80 	case WMI_TGT_STATS_CMDID:
81 		return "WMI_TGT_STATS_CMDID";
82 	case WMI_TX_AGGR_ENABLE_CMDID:
83 		return "WMI_TX_AGGR_ENABLE_CMDID";
84 	case WMI_TGT_DETACH_CMDID:
85 		return "WMI_TGT_DETACH_CMDID";
86 	case WMI_TGT_TXQ_ENABLE_CMDID:
87 		return "WMI_TGT_TXQ_ENABLE_CMDID";
88 	}
89 
90 	return "Bogus";
91 }
92 
93 struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv)
94 {
95 	struct wmi *wmi;
96 
97 	wmi = kzalloc(sizeof(struct wmi), GFP_KERNEL);
98 	if (!wmi)
99 		return NULL;
100 
101 	wmi->drv_priv = priv;
102 	wmi->stopped = false;
103 	mutex_init(&wmi->op_mutex);
104 	mutex_init(&wmi->multi_write_mutex);
105 	init_completion(&wmi->cmd_wait);
106 
107 	return wmi;
108 }
109 
110 void ath9k_deinit_wmi(struct ath9k_htc_priv *priv)
111 {
112 	struct wmi *wmi = priv->wmi;
113 
114 	mutex_lock(&wmi->op_mutex);
115 	wmi->stopped = true;
116 	mutex_unlock(&wmi->op_mutex);
117 
118 	kfree(priv->wmi);
119 }
120 
121 void ath9k_wmi_tasklet(unsigned long data)
122 {
123 	struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)data;
124 	struct ath_common *common = ath9k_hw_common(priv->ah);
125 	struct wmi_cmd_hdr *hdr;
126 	struct wmi_swba *swba_hdr;
127 	enum wmi_event_id event;
128 	struct sk_buff *skb;
129 	void *wmi_event;
130 	unsigned long flags;
131 #ifdef CONFIG_ATH9K_HTC_DEBUGFS
132 	__be32 txrate;
133 #endif
134 
135 	spin_lock_irqsave(&priv->wmi->wmi_lock, flags);
136 	skb = priv->wmi->wmi_skb;
137 	spin_unlock_irqrestore(&priv->wmi->wmi_lock, flags);
138 
139 	hdr = (struct wmi_cmd_hdr *) skb->data;
140 	event = be16_to_cpu(hdr->command_id);
141 	wmi_event = skb_pull(skb, sizeof(struct wmi_cmd_hdr));
142 
143 	ath_print(common, ATH_DBG_WMI,
144 		  "WMI Event: 0x%x\n", event);
145 
146 	switch (event) {
147 	case WMI_TGT_RDY_EVENTID:
148 		break;
149 	case WMI_SWBA_EVENTID:
150 		swba_hdr = (struct wmi_swba *) wmi_event;
151 		ath9k_htc_swba(priv, swba_hdr->beacon_pending);
152 		break;
153 	case WMI_FATAL_EVENTID:
154 		break;
155 	case WMI_TXTO_EVENTID:
156 		break;
157 	case WMI_BMISS_EVENTID:
158 		break;
159 	case WMI_WLAN_TXCOMP_EVENTID:
160 		break;
161 	case WMI_DELBA_EVENTID:
162 		break;
163 	case WMI_TXRATE_EVENTID:
164 #ifdef CONFIG_ATH9K_HTC_DEBUGFS
165 		txrate = ((struct wmi_event_txrate *)wmi_event)->txrate;
166 		priv->debug.txrate = be32_to_cpu(txrate);
167 #endif
168 		break;
169 	default:
170 		break;
171 	}
172 
173 	kfree_skb(skb);
174 }
175 
176 static void ath9k_wmi_rsp_callback(struct wmi *wmi, struct sk_buff *skb)
177 {
178 	skb_pull(skb, sizeof(struct wmi_cmd_hdr));
179 
180 	if (wmi->cmd_rsp_buf != NULL && wmi->cmd_rsp_len != 0)
181 		memcpy(wmi->cmd_rsp_buf, skb->data, wmi->cmd_rsp_len);
182 
183 	complete(&wmi->cmd_wait);
184 }
185 
186 static void ath9k_wmi_ctrl_rx(void *priv, struct sk_buff *skb,
187 			      enum htc_endpoint_id epid)
188 {
189 	struct wmi *wmi = (struct wmi *) priv;
190 	struct wmi_cmd_hdr *hdr;
191 	u16 cmd_id;
192 
193 	if (unlikely(wmi->stopped))
194 		goto free_skb;
195 
196 	hdr = (struct wmi_cmd_hdr *) skb->data;
197 	cmd_id = be16_to_cpu(hdr->command_id);
198 
199 	if (cmd_id & 0x1000) {
200 		spin_lock(&wmi->wmi_lock);
201 		wmi->wmi_skb = skb;
202 		spin_unlock(&wmi->wmi_lock);
203 		tasklet_schedule(&wmi->drv_priv->wmi_tasklet);
204 		return;
205 	}
206 
207 	/* Check if there has been a timeout. */
208 	spin_lock(&wmi->wmi_lock);
209 	if (cmd_id != wmi->last_cmd_id) {
210 		spin_unlock(&wmi->wmi_lock);
211 		goto free_skb;
212 	}
213 	spin_unlock(&wmi->wmi_lock);
214 
215 	/* WMI command response */
216 	ath9k_wmi_rsp_callback(wmi, skb);
217 
218 free_skb:
219 	kfree_skb(skb);
220 }
221 
222 static void ath9k_wmi_ctrl_tx(void *priv, struct sk_buff *skb,
223 			      enum htc_endpoint_id epid, bool txok)
224 {
225 	kfree_skb(skb);
226 }
227 
228 int ath9k_wmi_connect(struct htc_target *htc, struct wmi *wmi,
229 		      enum htc_endpoint_id *wmi_ctrl_epid)
230 {
231 	struct htc_service_connreq connect;
232 	int ret;
233 
234 	wmi->htc = htc;
235 
236 	memset(&connect, 0, sizeof(connect));
237 
238 	connect.ep_callbacks.priv = wmi;
239 	connect.ep_callbacks.tx = ath9k_wmi_ctrl_tx;
240 	connect.ep_callbacks.rx = ath9k_wmi_ctrl_rx;
241 	connect.service_id = WMI_CONTROL_SVC;
242 
243 	ret = htc_connect_service(htc, &connect, &wmi->ctrl_epid);
244 	if (ret)
245 		return ret;
246 
247 	*wmi_ctrl_epid = wmi->ctrl_epid;
248 
249 	return 0;
250 }
251 
252 static int ath9k_wmi_cmd_issue(struct wmi *wmi,
253 			       struct sk_buff *skb,
254 			       enum wmi_cmd_id cmd, u16 len)
255 {
256 	struct wmi_cmd_hdr *hdr;
257 
258 	hdr = (struct wmi_cmd_hdr *) skb_push(skb, sizeof(struct wmi_cmd_hdr));
259 	hdr->command_id = cpu_to_be16(cmd);
260 	hdr->seq_no = cpu_to_be16(++wmi->tx_seq_id);
261 
262 	return htc_send(wmi->htc, skb, wmi->ctrl_epid, NULL);
263 }
264 
265 int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id,
266 		  u8 *cmd_buf, u32 cmd_len,
267 		  u8 *rsp_buf, u32 rsp_len,
268 		  u32 timeout)
269 {
270 	struct ath_hw *ah = wmi->drv_priv->ah;
271 	struct ath_common *common = ath9k_hw_common(ah);
272 	u16 headroom = sizeof(struct htc_frame_hdr) +
273 		       sizeof(struct wmi_cmd_hdr);
274 	struct sk_buff *skb;
275 	u8 *data;
276 	int time_left, ret = 0;
277 	unsigned long flags;
278 
279 	if (wmi->drv_priv->op_flags & OP_UNPLUGGED)
280 		return 0;
281 
282 	skb = alloc_skb(headroom + cmd_len, GFP_ATOMIC);
283 	if (!skb)
284 		return -ENOMEM;
285 
286 	skb_reserve(skb, headroom);
287 
288 	if (cmd_len != 0 && cmd_buf != NULL) {
289 		data = (u8 *) skb_put(skb, cmd_len);
290 		memcpy(data, cmd_buf, cmd_len);
291 	}
292 
293 	mutex_lock(&wmi->op_mutex);
294 
295 	/* check if wmi stopped flag is set */
296 	if (unlikely(wmi->stopped)) {
297 		ret = -EPROTO;
298 		goto out;
299 	}
300 
301 	/* record the rsp buffer and length */
302 	wmi->cmd_rsp_buf = rsp_buf;
303 	wmi->cmd_rsp_len = rsp_len;
304 
305 	spin_lock_irqsave(&wmi->wmi_lock, flags);
306 	wmi->last_cmd_id = cmd_id;
307 	spin_unlock_irqrestore(&wmi->wmi_lock, flags);
308 
309 	ret = ath9k_wmi_cmd_issue(wmi, skb, cmd_id, cmd_len);
310 	if (ret)
311 		goto out;
312 
313 	time_left = wait_for_completion_timeout(&wmi->cmd_wait, timeout);
314 	if (!time_left) {
315 		ath_print(common, ATH_DBG_WMI,
316 			  "Timeout waiting for WMI command: %s\n",
317 			  wmi_cmd_to_name(cmd_id));
318 		mutex_unlock(&wmi->op_mutex);
319 		return -ETIMEDOUT;
320 	}
321 
322 	mutex_unlock(&wmi->op_mutex);
323 
324 	return 0;
325 
326 out:
327 	ath_print(common, ATH_DBG_WMI,
328 		  "WMI failure for: %s\n", wmi_cmd_to_name(cmd_id));
329 	mutex_unlock(&wmi->op_mutex);
330 	kfree_skb(skb);
331 
332 	return ret;
333 }
334