Commit | Line | Data |
---|---|---|
fb9987d0 | 1 | /* |
5b68138e | 2 | * Copyright (c) 2010-2011 Atheros Communications Inc. |
fb9987d0 S |
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 int htc_issue_send(struct htc_target *target, struct sk_buff* skb, | |
40dc9e4b SM |
20 | u16 len, u8 flags, u8 epid) |
21 | ||
fb9987d0 S |
22 | { |
23 | struct htc_frame_hdr *hdr; | |
24 | struct htc_endpoint *endpoint = &target->endpoint[epid]; | |
25 | int status; | |
26 | ||
27 | hdr = (struct htc_frame_hdr *) | |
28 | skb_push(skb, sizeof(struct htc_frame_hdr)); | |
29 | hdr->endpoint_id = epid; | |
30 | hdr->flags = flags; | |
31 | hdr->payload_len = cpu_to_be16(len); | |
32 | ||
40dc9e4b SM |
33 | status = target->hif->send(target->hif_dev, endpoint->ul_pipeid, skb); |
34 | ||
fb9987d0 S |
35 | return status; |
36 | } | |
37 | ||
38 | static struct htc_endpoint *get_next_avail_ep(struct htc_endpoint *endpoint) | |
39 | { | |
40 | enum htc_endpoint_id avail_epid; | |
41 | ||
8116daf2 | 42 | for (avail_epid = (ENDPOINT_MAX - 1); avail_epid > ENDPOINT0; avail_epid--) |
fb9987d0 S |
43 | if (endpoint[avail_epid].service_id == 0) |
44 | return &endpoint[avail_epid]; | |
45 | return NULL; | |
46 | } | |
47 | ||
48 | static u8 service_to_ulpipe(u16 service_id) | |
49 | { | |
50 | switch (service_id) { | |
51 | case WMI_CONTROL_SVC: | |
52 | return 4; | |
53 | case WMI_BEACON_SVC: | |
54 | case WMI_CAB_SVC: | |
55 | case WMI_UAPSD_SVC: | |
56 | case WMI_MGMT_SVC: | |
57 | case WMI_DATA_VO_SVC: | |
58 | case WMI_DATA_VI_SVC: | |
59 | case WMI_DATA_BE_SVC: | |
60 | case WMI_DATA_BK_SVC: | |
61 | return 1; | |
62 | default: | |
63 | return 0; | |
64 | } | |
65 | } | |
66 | ||
67 | static u8 service_to_dlpipe(u16 service_id) | |
68 | { | |
69 | switch (service_id) { | |
70 | case WMI_CONTROL_SVC: | |
71 | return 3; | |
72 | case WMI_BEACON_SVC: | |
73 | case WMI_CAB_SVC: | |
74 | case WMI_UAPSD_SVC: | |
75 | case WMI_MGMT_SVC: | |
76 | case WMI_DATA_VO_SVC: | |
77 | case WMI_DATA_VI_SVC: | |
78 | case WMI_DATA_BE_SVC: | |
79 | case WMI_DATA_BK_SVC: | |
80 | return 2; | |
81 | default: | |
82 | return 0; | |
83 | } | |
84 | } | |
85 | ||
86 | static void htc_process_target_rdy(struct htc_target *target, | |
87 | void *buf) | |
88 | { | |
89 | struct htc_endpoint *endpoint; | |
90 | struct htc_ready_msg *htc_ready_msg = (struct htc_ready_msg *) buf; | |
91 | ||
fb9987d0 S |
92 | target->credit_size = be16_to_cpu(htc_ready_msg->credit_size); |
93 | ||
94 | endpoint = &target->endpoint[ENDPOINT0]; | |
95 | endpoint->service_id = HTC_CTRL_RSVD_SVC; | |
96 | endpoint->max_msglen = HTC_MAX_CONTROL_MESSAGE_LENGTH; | |
d8c49ffb | 97 | atomic_inc(&target->tgt_ready); |
fb9987d0 S |
98 | complete(&target->target_wait); |
99 | } | |
100 | ||
101 | static void htc_process_conn_rsp(struct htc_target *target, | |
102 | struct htc_frame_hdr *htc_hdr) | |
103 | { | |
104 | struct htc_conn_svc_rspmsg *svc_rspmsg; | |
105 | struct htc_endpoint *endpoint, *tmp_endpoint = NULL; | |
106 | u16 service_id; | |
107 | u16 max_msglen; | |
108 | enum htc_endpoint_id epid, tepid; | |
109 | ||
110 | svc_rspmsg = (struct htc_conn_svc_rspmsg *) | |
111 | ((void *) htc_hdr + sizeof(struct htc_frame_hdr)); | |
112 | ||
113 | if (svc_rspmsg->status == HTC_SERVICE_SUCCESS) { | |
114 | epid = svc_rspmsg->endpoint_id; | |
115 | service_id = be16_to_cpu(svc_rspmsg->service_id); | |
116 | max_msglen = be16_to_cpu(svc_rspmsg->max_msg_len); | |
117 | endpoint = &target->endpoint[epid]; | |
118 | ||
8116daf2 | 119 | for (tepid = (ENDPOINT_MAX - 1); tepid > ENDPOINT0; tepid--) { |
fb9987d0 S |
120 | tmp_endpoint = &target->endpoint[tepid]; |
121 | if (tmp_endpoint->service_id == service_id) { | |
122 | tmp_endpoint->service_id = 0; | |
123 | break; | |
124 | } | |
125 | } | |
126 | ||
8116daf2 | 127 | if (tepid == ENDPOINT0) |
fb9987d0 S |
128 | return; |
129 | ||
130 | endpoint->service_id = service_id; | |
131 | endpoint->max_txqdepth = tmp_endpoint->max_txqdepth; | |
132 | endpoint->ep_callbacks = tmp_endpoint->ep_callbacks; | |
133 | endpoint->ul_pipeid = tmp_endpoint->ul_pipeid; | |
134 | endpoint->dl_pipeid = tmp_endpoint->dl_pipeid; | |
135 | endpoint->max_msglen = max_msglen; | |
136 | target->conn_rsp_epid = epid; | |
137 | complete(&target->cmd_wait); | |
138 | } else { | |
139 | target->conn_rsp_epid = ENDPOINT_UNUSED; | |
140 | } | |
141 | } | |
142 | ||
143 | static int htc_config_pipe_credits(struct htc_target *target) | |
144 | { | |
145 | struct sk_buff *skb; | |
146 | struct htc_config_pipe_msg *cp_msg; | |
147 | int ret, time_left; | |
148 | ||
0fa35a58 | 149 | skb = alloc_skb(50 + sizeof(struct htc_frame_hdr), GFP_ATOMIC); |
fb9987d0 S |
150 | if (!skb) { |
151 | dev_err(target->dev, "failed to allocate send buffer\n"); | |
152 | return -ENOMEM; | |
153 | } | |
154 | skb_reserve(skb, sizeof(struct htc_frame_hdr)); | |
155 | ||
156 | cp_msg = (struct htc_config_pipe_msg *) | |
157 | skb_put(skb, sizeof(struct htc_config_pipe_msg)); | |
158 | ||
159 | cp_msg->message_id = cpu_to_be16(HTC_MSG_CONFIG_PIPE_ID); | |
160 | cp_msg->pipe_id = USB_WLAN_TX_PIPE; | |
6267dc70 | 161 | cp_msg->credits = target->credits; |
fb9987d0 S |
162 | |
163 | target->htc_flags |= HTC_OP_CONFIG_PIPE_CREDITS; | |
164 | ||
40dc9e4b | 165 | ret = htc_issue_send(target, skb, skb->len, 0, ENDPOINT0); |
fb9987d0 S |
166 | if (ret) |
167 | goto err; | |
168 | ||
169 | time_left = wait_for_completion_timeout(&target->cmd_wait, HZ); | |
170 | if (!time_left) { | |
171 | dev_err(target->dev, "HTC credit config timeout\n"); | |
172 | return -ETIMEDOUT; | |
173 | } | |
174 | ||
175 | return 0; | |
176 | err: | |
0fa35a58 | 177 | kfree_skb(skb); |
fb9987d0 S |
178 | return -EINVAL; |
179 | } | |
180 | ||
181 | static int htc_setup_complete(struct htc_target *target) | |
182 | { | |
183 | struct sk_buff *skb; | |
184 | struct htc_comp_msg *comp_msg; | |
185 | int ret = 0, time_left; | |
186 | ||
0fa35a58 | 187 | skb = alloc_skb(50 + sizeof(struct htc_frame_hdr), GFP_ATOMIC); |
fb9987d0 S |
188 | if (!skb) { |
189 | dev_err(target->dev, "failed to allocate send buffer\n"); | |
190 | return -ENOMEM; | |
191 | } | |
192 | skb_reserve(skb, sizeof(struct htc_frame_hdr)); | |
193 | ||
194 | comp_msg = (struct htc_comp_msg *) | |
195 | skb_put(skb, sizeof(struct htc_comp_msg)); | |
196 | comp_msg->msg_id = cpu_to_be16(HTC_MSG_SETUP_COMPLETE_ID); | |
197 | ||
198 | target->htc_flags |= HTC_OP_START_WAIT; | |
199 | ||
40dc9e4b | 200 | ret = htc_issue_send(target, skb, skb->len, 0, ENDPOINT0); |
fb9987d0 S |
201 | if (ret) |
202 | goto err; | |
203 | ||
204 | time_left = wait_for_completion_timeout(&target->cmd_wait, HZ); | |
205 | if (!time_left) { | |
206 | dev_err(target->dev, "HTC start timeout\n"); | |
207 | return -ETIMEDOUT; | |
208 | } | |
209 | ||
210 | return 0; | |
211 | ||
212 | err: | |
0fa35a58 | 213 | kfree_skb(skb); |
fb9987d0 S |
214 | return -EINVAL; |
215 | } | |
216 | ||
217 | /* HTC APIs */ | |
218 | ||
219 | int htc_init(struct htc_target *target) | |
220 | { | |
221 | int ret; | |
222 | ||
223 | ret = htc_config_pipe_credits(target); | |
224 | if (ret) | |
225 | return ret; | |
226 | ||
227 | return htc_setup_complete(target); | |
228 | } | |
229 | ||
230 | int htc_connect_service(struct htc_target *target, | |
231 | struct htc_service_connreq *service_connreq, | |
232 | enum htc_endpoint_id *conn_rsp_epid) | |
233 | { | |
234 | struct sk_buff *skb; | |
235 | struct htc_endpoint *endpoint; | |
236 | struct htc_conn_svc_msg *conn_msg; | |
237 | int ret, time_left; | |
238 | ||
239 | /* Find an available endpoint */ | |
240 | endpoint = get_next_avail_ep(target->endpoint); | |
241 | if (!endpoint) { | |
242 | dev_err(target->dev, "Endpoint is not available for" | |
243 | "service %d\n", service_connreq->service_id); | |
244 | return -EINVAL; | |
245 | } | |
246 | ||
247 | endpoint->service_id = service_connreq->service_id; | |
248 | endpoint->max_txqdepth = service_connreq->max_send_qdepth; | |
249 | endpoint->ul_pipeid = service_to_ulpipe(service_connreq->service_id); | |
250 | endpoint->dl_pipeid = service_to_dlpipe(service_connreq->service_id); | |
251 | endpoint->ep_callbacks = service_connreq->ep_callbacks; | |
252 | ||
0fa35a58 ML |
253 | skb = alloc_skb(sizeof(struct htc_conn_svc_msg) + |
254 | sizeof(struct htc_frame_hdr), GFP_ATOMIC); | |
fb9987d0 S |
255 | if (!skb) { |
256 | dev_err(target->dev, "Failed to allocate buf to send" | |
257 | "service connect req\n"); | |
258 | return -ENOMEM; | |
259 | } | |
260 | ||
261 | skb_reserve(skb, sizeof(struct htc_frame_hdr)); | |
262 | ||
263 | conn_msg = (struct htc_conn_svc_msg *) | |
264 | skb_put(skb, sizeof(struct htc_conn_svc_msg)); | |
265 | conn_msg->service_id = cpu_to_be16(service_connreq->service_id); | |
266 | conn_msg->msg_id = cpu_to_be16(HTC_MSG_CONNECT_SERVICE_ID); | |
267 | conn_msg->con_flags = cpu_to_be16(service_connreq->con_flags); | |
268 | conn_msg->dl_pipeid = endpoint->dl_pipeid; | |
269 | conn_msg->ul_pipeid = endpoint->ul_pipeid; | |
270 | ||
40dc9e4b | 271 | ret = htc_issue_send(target, skb, skb->len, 0, ENDPOINT0); |
fb9987d0 S |
272 | if (ret) |
273 | goto err; | |
274 | ||
275 | time_left = wait_for_completion_timeout(&target->cmd_wait, HZ); | |
276 | if (!time_left) { | |
277 | dev_err(target->dev, "Service connection timeout for: %d\n", | |
278 | service_connreq->service_id); | |
279 | return -ETIMEDOUT; | |
280 | } | |
281 | ||
282 | *conn_rsp_epid = target->conn_rsp_epid; | |
283 | return 0; | |
284 | err: | |
0fa35a58 | 285 | kfree_skb(skb); |
fb9987d0 S |
286 | return ret; |
287 | } | |
288 | ||
d67ee533 | 289 | int htc_send(struct htc_target *target, struct sk_buff *skb) |
fb9987d0 | 290 | { |
d67ee533 SM |
291 | struct ath9k_htc_tx_ctl *tx_ctl; |
292 | ||
293 | tx_ctl = HTC_SKB_CB(skb); | |
294 | return htc_issue_send(target, skb, skb->len, 0, tx_ctl->epid); | |
fb9987d0 S |
295 | } |
296 | ||
d67ee533 SM |
297 | int htc_send_epid(struct htc_target *target, struct sk_buff *skb, |
298 | enum htc_endpoint_id epid) | |
fb9987d0 | 299 | { |
40dc9e4b | 300 | return htc_issue_send(target, skb, skb->len, 0, epid); |
fb9987d0 | 301 | } |
fb9987d0 | 302 | |
fb9987d0 S |
303 | void htc_stop(struct htc_target *target) |
304 | { | |
e1fe7c38 | 305 | target->hif->stop(target->hif_dev); |
fb9987d0 S |
306 | } |
307 | ||
308 | void htc_start(struct htc_target *target) | |
309 | { | |
e1fe7c38 | 310 | target->hif->start(target->hif_dev); |
fb9987d0 | 311 | } |
fb9987d0 | 312 | |
84c9e164 SM |
313 | void htc_sta_drain(struct htc_target *target, u8 idx) |
314 | { | |
315 | target->hif->sta_drain(target->hif_dev, idx); | |
fb9987d0 S |
316 | } |
317 | ||
318 | void ath9k_htc_txcompletion_cb(struct htc_target *htc_handle, | |
319 | struct sk_buff *skb, bool txok) | |
320 | { | |
321 | struct htc_endpoint *endpoint; | |
0fa35a58 | 322 | struct htc_frame_hdr *htc_hdr = NULL; |
fb9987d0 S |
323 | |
324 | if (htc_handle->htc_flags & HTC_OP_CONFIG_PIPE_CREDITS) { | |
325 | complete(&htc_handle->cmd_wait); | |
326 | htc_handle->htc_flags &= ~HTC_OP_CONFIG_PIPE_CREDITS; | |
f984d94c | 327 | goto ret; |
fb9987d0 S |
328 | } |
329 | ||
330 | if (htc_handle->htc_flags & HTC_OP_START_WAIT) { | |
331 | complete(&htc_handle->cmd_wait); | |
332 | htc_handle->htc_flags &= ~HTC_OP_START_WAIT; | |
f984d94c | 333 | goto ret; |
fb9987d0 S |
334 | } |
335 | ||
336 | if (skb) { | |
337 | htc_hdr = (struct htc_frame_hdr *) skb->data; | |
338 | endpoint = &htc_handle->endpoint[htc_hdr->endpoint_id]; | |
339 | skb_pull(skb, sizeof(struct htc_frame_hdr)); | |
340 | ||
341 | if (endpoint->ep_callbacks.tx) { | |
f6689072 S |
342 | endpoint->ep_callbacks.tx(endpoint->ep_callbacks.priv, |
343 | skb, htc_hdr->endpoint_id, | |
344 | txok); | |
fb9987d0 S |
345 | } |
346 | } | |
f984d94c S |
347 | |
348 | return; | |
349 | ret: | |
350 | /* HTC-generated packets are freed here. */ | |
0fa35a58 ML |
351 | if (htc_hdr && htc_hdr->endpoint_id != ENDPOINT0) |
352 | dev_kfree_skb_any(skb); | |
353 | else | |
354 | kfree_skb(skb); | |
fb9987d0 S |
355 | } |
356 | ||
357 | /* | |
358 | * HTC Messages are handled directly here and the obtained SKB | |
359 | * is freed. | |
360 | * | |
25985edc | 361 | * Service messages (Data, WMI) passed to the corresponding |
fb9987d0 S |
362 | * endpoint RX handlers, which have to free the SKB. |
363 | */ | |
364 | void ath9k_htc_rx_msg(struct htc_target *htc_handle, | |
365 | struct sk_buff *skb, u32 len, u8 pipe_id) | |
366 | { | |
367 | struct htc_frame_hdr *htc_hdr; | |
368 | enum htc_endpoint_id epid; | |
369 | struct htc_endpoint *endpoint; | |
7f1f5a00 | 370 | __be16 *msg_id; |
fb9987d0 S |
371 | |
372 | if (!htc_handle || !skb) | |
373 | return; | |
374 | ||
375 | htc_hdr = (struct htc_frame_hdr *) skb->data; | |
376 | epid = htc_hdr->endpoint_id; | |
377 | ||
378 | if (epid >= ENDPOINT_MAX) { | |
e6c6d33c ML |
379 | if (pipe_id != USB_REG_IN_PIPE) |
380 | dev_kfree_skb_any(skb); | |
381 | else | |
382 | kfree_skb(skb); | |
fb9987d0 S |
383 | return; |
384 | } | |
385 | ||
386 | if (epid == ENDPOINT0) { | |
387 | ||
388 | /* Handle trailer */ | |
389 | if (htc_hdr->flags & HTC_FLAGS_RECV_TRAILER) { | |
7f1f5a00 | 390 | if (be32_to_cpu(*(__be32 *) skb->data) == 0x00C60000) |
fb9987d0 | 391 | /* Move past the Watchdog pattern */ |
d5a4c5e3 | 392 | htc_hdr = (struct htc_frame_hdr *)(skb->data + 4); |
fb9987d0 S |
393 | } |
394 | ||
395 | /* Get the message ID */ | |
7f1f5a00 S |
396 | msg_id = (__be16 *) ((void *) htc_hdr + |
397 | sizeof(struct htc_frame_hdr)); | |
fb9987d0 S |
398 | |
399 | /* Now process HTC messages */ | |
400 | switch (be16_to_cpu(*msg_id)) { | |
401 | case HTC_MSG_READY_ID: | |
402 | htc_process_target_rdy(htc_handle, htc_hdr); | |
403 | break; | |
404 | case HTC_MSG_CONNECT_SERVICE_RESPONSE_ID: | |
405 | htc_process_conn_rsp(htc_handle, htc_hdr); | |
406 | break; | |
407 | default: | |
408 | break; | |
409 | } | |
410 | ||
e6c6d33c | 411 | kfree_skb(skb); |
fb9987d0 S |
412 | |
413 | } else { | |
414 | if (htc_hdr->flags & HTC_FLAGS_RECV_TRAILER) | |
415 | skb_trim(skb, len - htc_hdr->control[0]); | |
416 | ||
417 | skb_pull(skb, sizeof(struct htc_frame_hdr)); | |
418 | ||
419 | endpoint = &htc_handle->endpoint[epid]; | |
420 | if (endpoint->ep_callbacks.rx) | |
421 | endpoint->ep_callbacks.rx(endpoint->ep_callbacks.priv, | |
422 | skb, epid); | |
423 | } | |
424 | } | |
425 | ||
47fce026 SM |
426 | struct htc_target *ath9k_htc_hw_alloc(void *hif_handle, |
427 | struct ath9k_htc_hif *hif, | |
428 | struct device *dev) | |
fb9987d0 | 429 | { |
47fce026 | 430 | struct htc_endpoint *endpoint; |
fb9987d0 S |
431 | struct htc_target *target; |
432 | ||
433 | target = kzalloc(sizeof(struct htc_target), GFP_KERNEL); | |
e404decb | 434 | if (!target) |
47fce026 | 435 | return NULL; |
fb9987d0 S |
436 | |
437 | init_completion(&target->target_wait); | |
438 | init_completion(&target->cmd_wait); | |
439 | ||
440 | target->hif = hif; | |
441 | target->hif_dev = hif_handle; | |
442 | target->dev = dev; | |
443 | ||
444 | /* Assign control endpoint pipe IDs */ | |
445 | endpoint = &target->endpoint[ENDPOINT0]; | |
446 | endpoint->ul_pipeid = hif->control_ul_pipe; | |
447 | endpoint->dl_pipeid = hif->control_dl_pipe; | |
448 | ||
d8c49ffb SM |
449 | atomic_set(&target->tgt_ready, 0); |
450 | ||
47fce026 SM |
451 | return target; |
452 | } | |
453 | ||
454 | void ath9k_htc_hw_free(struct htc_target *htc) | |
455 | { | |
456 | kfree(htc); | |
457 | } | |
458 | ||
459 | int ath9k_htc_hw_init(struct htc_target *target, | |
fa6e15e0 RM |
460 | struct device *dev, u16 devid, |
461 | char *product, u32 drv_info) | |
47fce026 | 462 | { |
fa6e15e0 | 463 | if (ath9k_htc_probe_device(target, dev, devid, product, drv_info)) { |
fb9987d0 S |
464 | printk(KERN_ERR "Failed to initialize the device\n"); |
465 | return -ENODEV; | |
466 | } | |
467 | ||
468 | return 0; | |
469 | } | |
470 | ||
471 | void ath9k_htc_hw_deinit(struct htc_target *target, bool hot_unplug) | |
472 | { | |
473 | if (target) | |
474 | ath9k_htc_disconnect_device(target, hot_unplug); | |
475 | } |