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