Branch data Line data Source code
1 : : // SPDX-License-Identifier: ISC
2 : : /*
3 : : * Copyright (c) 2005-2011 Atheros Communications Inc.
4 : : * Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
5 : : */
6 : :
7 : : #include "core.h"
8 : : #include "hif.h"
9 : : #include "debug.h"
10 : :
11 : : /********/
12 : : /* Send */
13 : : /********/
14 : :
15 : 0 : static void ath10k_htc_control_tx_complete(struct ath10k *ar,
16 : : struct sk_buff *skb)
17 : : {
18 : 0 : kfree_skb(skb);
19 : 0 : }
20 : :
21 : 0 : static struct sk_buff *ath10k_htc_build_tx_ctrl_skb(void *ar)
22 : : {
23 : 0 : struct sk_buff *skb;
24 : 0 : struct ath10k_skb_cb *skb_cb;
25 : :
26 : 0 : skb = dev_alloc_skb(ATH10K_HTC_CONTROL_BUFFER_SIZE);
27 [ # # ]: 0 : if (!skb)
28 : : return NULL;
29 : :
30 [ # # ]: 0 : skb_reserve(skb, 20); /* FIXME: why 20 bytes? */
31 [ # # # # ]: 0 : WARN_ONCE((unsigned long)skb->data & 3, "unaligned skb");
32 : :
33 [ # # ]: 0 : skb_cb = ATH10K_SKB_CB(skb);
34 : 0 : memset(skb_cb, 0, sizeof(*skb_cb));
35 : :
36 [ # # ]: 0 : ath10k_dbg(ar, ATH10K_DBG_HTC, "%s: skb %pK\n", __func__, skb);
37 : : return skb;
38 : : }
39 : :
40 : : static inline void ath10k_htc_restore_tx_skb(struct ath10k_htc *htc,
41 : : struct sk_buff *skb)
42 : : {
43 : : struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
44 : :
45 : : if (htc->ar->bus_param.dev_type != ATH10K_DEV_TYPE_HL)
46 : : dma_unmap_single(htc->ar->dev, skb_cb->paddr, skb->len, DMA_TO_DEVICE);
47 : : skb_pull(skb, sizeof(struct ath10k_htc_hdr));
48 : : }
49 : :
50 : 0 : void ath10k_htc_notify_tx_completion(struct ath10k_htc_ep *ep,
51 : : struct sk_buff *skb)
52 : : {
53 : 0 : struct ath10k *ar = ep->htc->ar;
54 : :
55 [ # # ]: 0 : ath10k_dbg(ar, ATH10K_DBG_HTC, "%s: ep %d skb %pK\n", __func__,
56 : : ep->eid, skb);
57 : :
58 : 0 : ath10k_htc_restore_tx_skb(ep->htc, skb);
59 : :
60 [ # # ]: 0 : if (!ep->ep_ops.ep_tx_complete) {
61 : 0 : ath10k_warn(ar, "no tx handler for eid %d\n", ep->eid);
62 : 0 : dev_kfree_skb_any(skb);
63 : 0 : return;
64 : : }
65 : :
66 : 0 : ep->ep_ops.ep_tx_complete(ep->htc->ar, skb);
67 : : }
68 : : EXPORT_SYMBOL(ath10k_htc_notify_tx_completion);
69 : :
70 : : static void ath10k_htc_prepare_tx_skb(struct ath10k_htc_ep *ep,
71 : : struct sk_buff *skb)
72 : : {
73 : : struct ath10k_htc_hdr *hdr;
74 : :
75 : : hdr = (struct ath10k_htc_hdr *)skb->data;
76 : : memset(hdr, 0, sizeof(struct ath10k_htc_hdr));
77 : :
78 : : hdr->eid = ep->eid;
79 : : hdr->len = __cpu_to_le16(skb->len - sizeof(*hdr));
80 : : hdr->flags = 0;
81 : : if (ep->tx_credit_flow_enabled)
82 : : hdr->flags |= ATH10K_HTC_FLAG_NEED_CREDIT_UPDATE;
83 : :
84 : : spin_lock_bh(&ep->htc->tx_lock);
85 : : hdr->seq_no = ep->seq_no++;
86 : : spin_unlock_bh(&ep->htc->tx_lock);
87 : : }
88 : :
89 : 0 : int ath10k_htc_send(struct ath10k_htc *htc,
90 : : enum ath10k_htc_ep_id eid,
91 : : struct sk_buff *skb)
92 : : {
93 : 0 : struct ath10k *ar = htc->ar;
94 : 0 : struct ath10k_htc_ep *ep = &htc->endpoint[eid];
95 [ # # ]: 0 : struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
96 : 0 : struct ath10k_hif_sg_item sg_item;
97 : 0 : struct device *dev = htc->ar->dev;
98 : 0 : int credits = 0;
99 : 0 : int ret;
100 : :
101 [ # # ]: 0 : if (htc->ar->state == ATH10K_STATE_WEDGED)
102 : : return -ECOMM;
103 : :
104 [ # # ]: 0 : if (eid >= ATH10K_HTC_EP_COUNT) {
105 : 0 : ath10k_warn(ar, "Invalid endpoint id: %d\n", eid);
106 : 0 : return -ENOENT;
107 : : }
108 : :
109 : 0 : skb_push(skb, sizeof(struct ath10k_htc_hdr));
110 : :
111 [ # # ]: 0 : if (ep->tx_credit_flow_enabled) {
112 : 0 : credits = DIV_ROUND_UP(skb->len, htc->target_credit_size);
113 : 0 : spin_lock_bh(&htc->tx_lock);
114 [ # # ]: 0 : if (ep->tx_credits < credits) {
115 [ # # ]: 0 : ath10k_dbg(ar, ATH10K_DBG_HTC,
116 : : "htc insufficient credits ep %d required %d available %d\n",
117 : : eid, credits, ep->tx_credits);
118 : 0 : spin_unlock_bh(&htc->tx_lock);
119 : 0 : ret = -EAGAIN;
120 : 0 : goto err_pull;
121 : : }
122 : 0 : ep->tx_credits -= credits;
123 [ # # ]: 0 : ath10k_dbg(ar, ATH10K_DBG_HTC,
124 : : "htc ep %d consumed %d credits (total %d)\n",
125 : : eid, credits, ep->tx_credits);
126 : 0 : spin_unlock_bh(&htc->tx_lock);
127 : : }
128 : :
129 : 0 : ath10k_htc_prepare_tx_skb(ep, skb);
130 : :
131 : 0 : skb_cb->eid = eid;
132 [ # # ]: 0 : if (ar->bus_param.dev_type != ATH10K_DEV_TYPE_HL) {
133 : 0 : skb_cb->paddr = dma_map_single(dev, skb->data, skb->len,
134 : : DMA_TO_DEVICE);
135 : 0 : ret = dma_mapping_error(dev, skb_cb->paddr);
136 : 0 : if (ret) {
137 : 0 : ret = -EIO;
138 : 0 : goto err_credits;
139 : : }
140 : : }
141 : :
142 : 0 : sg_item.transfer_id = ep->eid;
143 : 0 : sg_item.transfer_context = skb;
144 : 0 : sg_item.vaddr = skb->data;
145 : 0 : sg_item.paddr = skb_cb->paddr;
146 : 0 : sg_item.len = skb->len;
147 : :
148 : 0 : ret = ath10k_hif_tx_sg(htc->ar, ep->ul_pipe_id, &sg_item, 1);
149 [ # # ]: 0 : if (ret)
150 : 0 : goto err_unmap;
151 : :
152 : : return 0;
153 : :
154 : : err_unmap:
155 [ # # ]: 0 : if (ar->bus_param.dev_type != ATH10K_DEV_TYPE_HL)
156 : 0 : dma_unmap_single(dev, skb_cb->paddr, skb->len, DMA_TO_DEVICE);
157 : 0 : err_credits:
158 [ # # ]: 0 : if (ep->tx_credit_flow_enabled) {
159 : 0 : spin_lock_bh(&htc->tx_lock);
160 : 0 : ep->tx_credits += credits;
161 [ # # ]: 0 : ath10k_dbg(ar, ATH10K_DBG_HTC,
162 : : "htc ep %d reverted %d credits back (total %d)\n",
163 : : eid, credits, ep->tx_credits);
164 : 0 : spin_unlock_bh(&htc->tx_lock);
165 : :
166 [ # # ]: 0 : if (ep->ep_ops.ep_tx_credits)
167 : 0 : ep->ep_ops.ep_tx_credits(htc->ar);
168 : : }
169 : 0 : err_pull:
170 : 0 : skb_pull(skb, sizeof(struct ath10k_htc_hdr));
171 : 0 : return ret;
172 : : }
173 : :
174 : 0 : void ath10k_htc_tx_completion_handler(struct ath10k *ar, struct sk_buff *skb)
175 : : {
176 : 0 : struct ath10k_htc *htc = &ar->htc;
177 : 0 : struct ath10k_skb_cb *skb_cb;
178 : 0 : struct ath10k_htc_ep *ep;
179 : :
180 [ # # # # ]: 0 : if (WARN_ON_ONCE(!skb))
181 : : return;
182 : :
183 : 0 : skb_cb = ATH10K_SKB_CB(skb);
184 : 0 : ep = &htc->endpoint[skb_cb->eid];
185 : :
186 : 0 : ath10k_htc_notify_tx_completion(ep, skb);
187 : : /* the skb now belongs to the completion handler */
188 : : }
189 : : EXPORT_SYMBOL(ath10k_htc_tx_completion_handler);
190 : :
191 : : /***********/
192 : : /* Receive */
193 : : /***********/
194 : :
195 : : static void
196 : : ath10k_htc_process_credit_report(struct ath10k_htc *htc,
197 : : const struct ath10k_htc_credit_report *report,
198 : : int len,
199 : : enum ath10k_htc_ep_id eid)
200 : : {
201 : : struct ath10k *ar = htc->ar;
202 : : struct ath10k_htc_ep *ep;
203 : : int i, n_reports;
204 : :
205 : : if (len % sizeof(*report))
206 : : ath10k_warn(ar, "Uneven credit report len %d", len);
207 : :
208 : : n_reports = len / sizeof(*report);
209 : :
210 : : spin_lock_bh(&htc->tx_lock);
211 : : for (i = 0; i < n_reports; i++, report++) {
212 : : if (report->eid >= ATH10K_HTC_EP_COUNT)
213 : : break;
214 : :
215 : : ep = &htc->endpoint[report->eid];
216 : : ep->tx_credits += report->credits;
217 : :
218 : : ath10k_dbg(ar, ATH10K_DBG_HTC, "htc ep %d got %d credits (total %d)\n",
219 : : report->eid, report->credits, ep->tx_credits);
220 : :
221 : : if (ep->ep_ops.ep_tx_credits) {
222 : : spin_unlock_bh(&htc->tx_lock);
223 : : ep->ep_ops.ep_tx_credits(htc->ar);
224 : : spin_lock_bh(&htc->tx_lock);
225 : : }
226 : : }
227 : : spin_unlock_bh(&htc->tx_lock);
228 : : }
229 : :
230 : : static int
231 : : ath10k_htc_process_lookahead(struct ath10k_htc *htc,
232 : : const struct ath10k_htc_lookahead_report *report,
233 : : int len,
234 : : enum ath10k_htc_ep_id eid,
235 : : void *next_lookaheads,
236 : : int *next_lookaheads_len)
237 : : {
238 : : struct ath10k *ar = htc->ar;
239 : :
240 : : /* Invalid lookahead flags are actually transmitted by
241 : : * the target in the HTC control message.
242 : : * Since this will happen at every boot we silently ignore
243 : : * the lookahead in this case
244 : : */
245 : : if (report->pre_valid != ((~report->post_valid) & 0xFF))
246 : : return 0;
247 : :
248 : : if (next_lookaheads && next_lookaheads_len) {
249 : : ath10k_dbg(ar, ATH10K_DBG_HTC,
250 : : "htc rx lookahead found pre_valid 0x%x post_valid 0x%x\n",
251 : : report->pre_valid, report->post_valid);
252 : :
253 : : /* look ahead bytes are valid, copy them over */
254 : : memcpy((u8 *)next_lookaheads, report->lookahead, 4);
255 : :
256 : : *next_lookaheads_len = 1;
257 : : }
258 : :
259 : : return 0;
260 : : }
261 : :
262 : : static int
263 : : ath10k_htc_process_lookahead_bundle(struct ath10k_htc *htc,
264 : : const struct ath10k_htc_lookahead_bundle *report,
265 : : int len,
266 : : enum ath10k_htc_ep_id eid,
267 : : void *next_lookaheads,
268 : : int *next_lookaheads_len)
269 : : {
270 : : struct ath10k *ar = htc->ar;
271 : : int bundle_cnt = len / sizeof(*report);
272 : :
273 : : if (!bundle_cnt || (bundle_cnt > htc->max_msgs_per_htc_bundle)) {
274 : : ath10k_warn(ar, "Invalid lookahead bundle count: %d\n",
275 : : bundle_cnt);
276 : : return -EINVAL;
277 : : }
278 : :
279 : : if (next_lookaheads && next_lookaheads_len) {
280 : : int i;
281 : :
282 : : for (i = 0; i < bundle_cnt; i++) {
283 : : memcpy(((u8 *)next_lookaheads) + 4 * i,
284 : : report->lookahead, 4);
285 : : report++;
286 : : }
287 : :
288 : : *next_lookaheads_len = bundle_cnt;
289 : : }
290 : :
291 : : return 0;
292 : : }
293 : :
294 : 0 : int ath10k_htc_process_trailer(struct ath10k_htc *htc,
295 : : u8 *buffer,
296 : : int length,
297 : : enum ath10k_htc_ep_id src_eid,
298 : : void *next_lookaheads,
299 : : int *next_lookaheads_len)
300 : : {
301 : 0 : struct ath10k_htc_lookahead_bundle *bundle;
302 : 0 : struct ath10k *ar = htc->ar;
303 : 0 : int status = 0;
304 : 0 : struct ath10k_htc_record *record;
305 : 0 : u8 *orig_buffer;
306 : 0 : int orig_length;
307 : 0 : size_t len;
308 : :
309 : 0 : orig_buffer = buffer;
310 : 0 : orig_length = length;
311 : :
312 [ # # ]: 0 : while (length > 0) {
313 : 0 : record = (struct ath10k_htc_record *)buffer;
314 : :
315 [ # # ]: 0 : if (length < sizeof(record->hdr)) {
316 : : status = -EINVAL;
317 : : break;
318 : : }
319 : :
320 [ # # ]: 0 : if (record->hdr.len > length) {
321 : : /* no room left in buffer for record */
322 : 0 : ath10k_warn(ar, "Invalid record length: %d\n",
323 : : record->hdr.len);
324 : 0 : status = -EINVAL;
325 : 0 : break;
326 : : }
327 : :
328 [ # # # # ]: 0 : switch (record->hdr.id) {
329 : 0 : case ATH10K_HTC_RECORD_CREDITS:
330 : 0 : len = sizeof(struct ath10k_htc_credit_report);
331 [ # # ]: 0 : if (record->hdr.len < len) {
332 : 0 : ath10k_warn(ar, "Credit report too long\n");
333 : 0 : status = -EINVAL;
334 : 0 : break;
335 : : }
336 : 0 : ath10k_htc_process_credit_report(htc,
337 : 0 : record->credit_report,
338 : : record->hdr.len,
339 : : src_eid);
340 : 0 : break;
341 : 0 : case ATH10K_HTC_RECORD_LOOKAHEAD:
342 : 0 : len = sizeof(struct ath10k_htc_lookahead_report);
343 [ # # ]: 0 : if (record->hdr.len < len) {
344 : 0 : ath10k_warn(ar, "Lookahead report too long\n");
345 : 0 : status = -EINVAL;
346 : 0 : break;
347 : : }
348 : 0 : status = ath10k_htc_process_lookahead(htc,
349 : 0 : record->lookahead_report,
350 : : record->hdr.len,
351 : : src_eid,
352 : : next_lookaheads,
353 : : next_lookaheads_len);
354 : 0 : break;
355 : 0 : case ATH10K_HTC_RECORD_LOOKAHEAD_BUNDLE:
356 : 0 : bundle = record->lookahead_bundle;
357 : 0 : status = ath10k_htc_process_lookahead_bundle(htc,
358 : : bundle,
359 : : record->hdr.len,
360 : : src_eid,
361 : : next_lookaheads,
362 : : next_lookaheads_len);
363 : 0 : break;
364 : 0 : default:
365 : 0 : ath10k_warn(ar, "Unhandled record: id:%d length:%d\n",
366 : : record->hdr.id, record->hdr.len);
367 : 0 : break;
368 : : }
369 : :
370 [ # # ]: 0 : if (status)
371 : : break;
372 : :
373 : : /* multiple records may be present in a trailer */
374 : 0 : buffer += sizeof(record->hdr) + record->hdr.len;
375 : 0 : length -= sizeof(record->hdr) + record->hdr.len;
376 : : }
377 : :
378 [ # # ]: 0 : if (status)
379 : 0 : ath10k_dbg_dump(ar, ATH10K_DBG_HTC, "htc rx bad trailer", "",
380 : : orig_buffer, orig_length);
381 : :
382 : 0 : return status;
383 : : }
384 : : EXPORT_SYMBOL(ath10k_htc_process_trailer);
385 : :
386 : 0 : void ath10k_htc_rx_completion_handler(struct ath10k *ar, struct sk_buff *skb)
387 : : {
388 : 0 : int status = 0;
389 : 0 : struct ath10k_htc *htc = &ar->htc;
390 : 0 : struct ath10k_htc_hdr *hdr;
391 : 0 : struct ath10k_htc_ep *ep;
392 : 0 : u16 payload_len;
393 : 0 : u32 trailer_len = 0;
394 : 0 : size_t min_len;
395 : 0 : u8 eid;
396 : 0 : bool trailer_present;
397 : :
398 : 0 : hdr = (struct ath10k_htc_hdr *)skb->data;
399 : 0 : skb_pull(skb, sizeof(*hdr));
400 : :
401 : 0 : eid = hdr->eid;
402 : :
403 [ # # ]: 0 : if (eid >= ATH10K_HTC_EP_COUNT) {
404 : 0 : ath10k_warn(ar, "HTC Rx: invalid eid %d\n", eid);
405 : 0 : ath10k_dbg_dump(ar, ATH10K_DBG_HTC, "htc bad header", "",
406 : : hdr, sizeof(*hdr));
407 : 0 : goto out;
408 : : }
409 : :
410 : 0 : ep = &htc->endpoint[eid];
411 : :
412 : 0 : payload_len = __le16_to_cpu(hdr->len);
413 : :
414 [ # # ]: 0 : if (payload_len + sizeof(*hdr) > ATH10K_HTC_MAX_LEN) {
415 : 0 : ath10k_warn(ar, "HTC rx frame too long, len: %zu\n",
416 : : payload_len + sizeof(*hdr));
417 : 0 : ath10k_dbg_dump(ar, ATH10K_DBG_HTC, "htc bad rx pkt len", "",
418 : : hdr, sizeof(*hdr));
419 : 0 : goto out;
420 : : }
421 : :
422 [ # # ]: 0 : if (skb->len < payload_len) {
423 [ # # ]: 0 : ath10k_dbg(ar, ATH10K_DBG_HTC,
424 : : "HTC Rx: insufficient length, got %d, expected %d\n",
425 : : skb->len, payload_len);
426 : 0 : ath10k_dbg_dump(ar, ATH10K_DBG_HTC, "htc bad rx pkt len",
427 : : "", hdr, sizeof(*hdr));
428 : 0 : goto out;
429 : : }
430 : :
431 : : /* get flags to check for trailer */
432 : 0 : trailer_present = hdr->flags & ATH10K_HTC_FLAG_TRAILER_PRESENT;
433 [ # # ]: 0 : if (trailer_present) {
434 : 0 : u8 *trailer;
435 : :
436 : 0 : trailer_len = hdr->trailer_len;
437 : 0 : min_len = sizeof(struct ath10k_ath10k_htc_record_hdr);
438 : :
439 [ # # # # ]: 0 : if ((trailer_len < min_len) ||
440 : : (trailer_len > payload_len)) {
441 : 0 : ath10k_warn(ar, "Invalid trailer length: %d\n",
442 : : trailer_len);
443 : 0 : goto out;
444 : : }
445 : :
446 : 0 : trailer = (u8 *)hdr;
447 : 0 : trailer += sizeof(*hdr);
448 : 0 : trailer += payload_len;
449 : 0 : trailer -= trailer_len;
450 : 0 : status = ath10k_htc_process_trailer(htc, trailer,
451 : : trailer_len, hdr->eid,
452 : : NULL, NULL);
453 [ # # ]: 0 : if (status)
454 : 0 : goto out;
455 : :
456 : 0 : skb_trim(skb, skb->len - trailer_len);
457 : : }
458 : :
459 [ # # ]: 0 : if (((int)payload_len - (int)trailer_len) <= 0)
460 : : /* zero length packet with trailer data, just drop these */
461 : 0 : goto out;
462 : :
463 [ # # ]: 0 : ath10k_dbg(ar, ATH10K_DBG_HTC, "htc rx completion ep %d skb %pK\n",
464 : : eid, skb);
465 : 0 : ep->ep_ops.ep_rx_complete(ar, skb);
466 : :
467 : : /* skb is now owned by the rx completion handler */
468 : 0 : skb = NULL;
469 : 0 : out:
470 : 0 : kfree_skb(skb);
471 : 0 : }
472 : : EXPORT_SYMBOL(ath10k_htc_rx_completion_handler);
473 : :
474 : 0 : static void ath10k_htc_control_rx_complete(struct ath10k *ar,
475 : : struct sk_buff *skb)
476 : : {
477 : 0 : struct ath10k_htc *htc = &ar->htc;
478 : 0 : struct ath10k_htc_msg *msg = (struct ath10k_htc_msg *)skb->data;
479 : :
480 [ # # # ]: 0 : switch (__le16_to_cpu(msg->hdr.message_id)) {
481 : 0 : case ATH10K_HTC_MSG_READY_ID:
482 : : case ATH10K_HTC_MSG_CONNECT_SERVICE_RESP_ID:
483 : : /* handle HTC control message */
484 [ # # ]: 0 : if (completion_done(&htc->ctl_resp)) {
485 : : /* this is a fatal error, target should not be
486 : : * sending unsolicited messages on the ep 0
487 : : */
488 : 0 : ath10k_warn(ar, "HTC rx ctrl still processing\n");
489 : 0 : complete(&htc->ctl_resp);
490 : 0 : goto out;
491 : : }
492 : :
493 : 0 : htc->control_resp_len =
494 : 0 : min_t(int, skb->len,
495 : : ATH10K_HTC_MAX_CTRL_MSG_LEN);
496 : :
497 : 0 : memcpy(htc->control_resp_buffer, skb->data,
498 : : htc->control_resp_len);
499 : :
500 : 0 : complete(&htc->ctl_resp);
501 : 0 : break;
502 : 0 : case ATH10K_HTC_MSG_SEND_SUSPEND_COMPLETE:
503 : 0 : htc->htc_ops.target_send_suspend_complete(ar);
504 : 0 : break;
505 : 0 : default:
506 : 0 : ath10k_warn(ar, "ignoring unsolicited htc ep0 event\n");
507 : 0 : break;
508 : : }
509 : :
510 : 0 : out:
511 : 0 : kfree_skb(skb);
512 : 0 : }
513 : :
514 : : /***************/
515 : : /* Init/Deinit */
516 : : /***************/
517 : :
518 : 0 : static const char *htc_service_name(enum ath10k_htc_svc_id id)
519 : : {
520 [ # # # # : 0 : switch (id) {
# # # # #
# # # # #
# ]
521 : : case ATH10K_HTC_SVC_ID_RESERVED:
522 : : return "Reserved";
523 : 0 : case ATH10K_HTC_SVC_ID_RSVD_CTRL:
524 : 0 : return "Control";
525 : 0 : case ATH10K_HTC_SVC_ID_WMI_CONTROL:
526 : 0 : return "WMI";
527 : 0 : case ATH10K_HTC_SVC_ID_WMI_DATA_BE:
528 : 0 : return "DATA BE";
529 : 0 : case ATH10K_HTC_SVC_ID_WMI_DATA_BK:
530 : 0 : return "DATA BK";
531 : 0 : case ATH10K_HTC_SVC_ID_WMI_DATA_VI:
532 : 0 : return "DATA VI";
533 : 0 : case ATH10K_HTC_SVC_ID_WMI_DATA_VO:
534 : 0 : return "DATA VO";
535 : 0 : case ATH10K_HTC_SVC_ID_NMI_CONTROL:
536 : 0 : return "NMI Control";
537 : 0 : case ATH10K_HTC_SVC_ID_NMI_DATA:
538 : 0 : return "NMI Data";
539 : 0 : case ATH10K_HTC_SVC_ID_HTT_DATA_MSG:
540 : 0 : return "HTT Data";
541 : 0 : case ATH10K_HTC_SVC_ID_HTT_DATA2_MSG:
542 : 0 : return "HTT Data";
543 : 0 : case ATH10K_HTC_SVC_ID_HTT_DATA3_MSG:
544 : 0 : return "HTT Data";
545 : 0 : case ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS:
546 : 0 : return "RAW";
547 : 0 : case ATH10K_HTC_SVC_ID_HTT_LOG_MSG:
548 : 0 : return "PKTLOG";
549 : : }
550 : :
551 : 0 : return "Unknown";
552 : : }
553 : :
554 : 0 : static void ath10k_htc_reset_endpoint_states(struct ath10k_htc *htc)
555 : : {
556 : : struct ath10k_htc_ep *ep;
557 : : int i;
558 : :
559 [ # # ]: 0 : for (i = ATH10K_HTC_EP_0; i < ATH10K_HTC_EP_COUNT; i++) {
560 : 0 : ep = &htc->endpoint[i];
561 : 0 : ep->service_id = ATH10K_HTC_SVC_ID_UNUSED;
562 : 0 : ep->max_ep_message_len = 0;
563 : 0 : ep->max_tx_queue_depth = 0;
564 : 0 : ep->eid = i;
565 : 0 : ep->htc = htc;
566 : 0 : ep->tx_credit_flow_enabled = true;
567 : : }
568 : : }
569 : :
570 : 0 : static u8 ath10k_htc_get_credit_allocation(struct ath10k_htc *htc,
571 : : u16 service_id)
572 : : {
573 : 0 : u8 allocation = 0;
574 : :
575 : : /* The WMI control service is the only service with flow control.
576 : : * Let it have all transmit credits.
577 : : */
578 : 0 : if (service_id == ATH10K_HTC_SVC_ID_WMI_CONTROL)
579 : 0 : allocation = htc->total_transmit_credits;
580 : :
581 : 0 : return allocation;
582 : : }
583 : :
584 : 0 : int ath10k_htc_wait_target(struct ath10k_htc *htc)
585 : : {
586 : 0 : struct ath10k *ar = htc->ar;
587 : 0 : int i, status = 0;
588 : 0 : unsigned long time_left;
589 : 0 : struct ath10k_htc_msg *msg;
590 : 0 : u16 message_id;
591 : :
592 : 0 : time_left = wait_for_completion_timeout(&htc->ctl_resp,
593 : : ATH10K_HTC_WAIT_TIMEOUT_HZ);
594 [ # # ]: 0 : if (!time_left) {
595 : : /* Workaround: In some cases the PCI HIF doesn't
596 : : * receive interrupt for the control response message
597 : : * even if the buffer was completed. It is suspected
598 : : * iomap writes unmasking PCI CE irqs aren't propagated
599 : : * properly in KVM PCI-passthrough sometimes.
600 : : */
601 : 0 : ath10k_warn(ar, "failed to receive control response completion, polling..\n");
602 : :
603 [ # # ]: 0 : for (i = 0; i < CE_COUNT; i++)
604 : 0 : ath10k_hif_send_complete_check(htc->ar, i, 1);
605 : :
606 : 0 : time_left =
607 : 0 : wait_for_completion_timeout(&htc->ctl_resp,
608 : : ATH10K_HTC_WAIT_TIMEOUT_HZ);
609 : :
610 [ # # ]: 0 : if (!time_left)
611 : 0 : status = -ETIMEDOUT;
612 : : }
613 : :
614 : 0 : if (status < 0) {
615 : 0 : ath10k_err(ar, "ctl_resp never came in (%d)\n", status);
616 : 0 : return status;
617 : : }
618 : :
619 [ # # ]: 0 : if (htc->control_resp_len < sizeof(msg->hdr) + sizeof(msg->ready)) {
620 : 0 : ath10k_err(ar, "Invalid HTC ready msg len:%d\n",
621 : : htc->control_resp_len);
622 : 0 : return -ECOMM;
623 : : }
624 : :
625 : 0 : msg = (struct ath10k_htc_msg *)htc->control_resp_buffer;
626 : 0 : message_id = __le16_to_cpu(msg->hdr.message_id);
627 : :
628 [ # # ]: 0 : if (message_id != ATH10K_HTC_MSG_READY_ID) {
629 : 0 : ath10k_err(ar, "Invalid HTC ready msg: 0x%x\n", message_id);
630 : 0 : return -ECOMM;
631 : : }
632 : :
633 : 0 : htc->total_transmit_credits = __le16_to_cpu(msg->ready.credit_count);
634 : 0 : htc->target_credit_size = __le16_to_cpu(msg->ready.credit_size);
635 : :
636 [ # # ]: 0 : ath10k_dbg(ar, ATH10K_DBG_HTC,
637 : : "Target ready! transmit resources: %d size:%d\n",
638 : : htc->total_transmit_credits,
639 : : htc->target_credit_size);
640 : :
641 [ # # ]: 0 : if ((htc->total_transmit_credits == 0) ||
642 [ # # ]: 0 : (htc->target_credit_size == 0)) {
643 : 0 : ath10k_err(ar, "Invalid credit size received\n");
644 : 0 : return -ECOMM;
645 : : }
646 : :
647 : : /* The only way to determine if the ready message is an extended
648 : : * message is from the size.
649 : : */
650 [ # # ]: 0 : if (htc->control_resp_len >=
651 : : sizeof(msg->hdr) + sizeof(msg->ready_ext)) {
652 : 0 : htc->max_msgs_per_htc_bundle =
653 : 0 : min_t(u8, msg->ready_ext.max_msgs_per_htc_bundle,
654 : : HTC_HOST_MAX_MSG_PER_RX_BUNDLE);
655 [ # # ]: 0 : ath10k_dbg(ar, ATH10K_DBG_HTC,
656 : : "Extended ready message. RX bundle size: %d\n",
657 : : htc->max_msgs_per_htc_bundle);
658 : : }
659 : :
660 : : return 0;
661 : : }
662 : :
663 : 0 : int ath10k_htc_connect_service(struct ath10k_htc *htc,
664 : : struct ath10k_htc_svc_conn_req *conn_req,
665 : : struct ath10k_htc_svc_conn_resp *conn_resp)
666 : : {
667 : 0 : struct ath10k *ar = htc->ar;
668 : 0 : struct ath10k_htc_msg *msg;
669 : 0 : struct ath10k_htc_conn_svc *req_msg;
670 : 0 : struct ath10k_htc_conn_svc_response resp_msg_dummy;
671 : 0 : struct ath10k_htc_conn_svc_response *resp_msg = &resp_msg_dummy;
672 : 0 : enum ath10k_htc_ep_id assigned_eid = ATH10K_HTC_EP_COUNT;
673 : 0 : struct ath10k_htc_ep *ep;
674 : 0 : struct sk_buff *skb;
675 : 0 : unsigned int max_msg_size = 0;
676 : 0 : int length, status;
677 : 0 : unsigned long time_left;
678 : 0 : bool disable_credit_flow_ctrl = false;
679 : 0 : u16 message_id, service_id, flags = 0;
680 : 0 : u8 tx_alloc = 0;
681 : :
682 : : /* special case for HTC pseudo control service */
683 [ # # ]: 0 : if (conn_req->service_id == ATH10K_HTC_SVC_ID_RSVD_CTRL) {
684 : 0 : disable_credit_flow_ctrl = true;
685 : 0 : assigned_eid = ATH10K_HTC_EP_0;
686 : 0 : max_msg_size = ATH10K_HTC_MAX_CTRL_MSG_LEN;
687 : 0 : memset(&resp_msg_dummy, 0, sizeof(resp_msg_dummy));
688 : 0 : goto setup;
689 : : }
690 : :
691 [ # # ]: 0 : tx_alloc = ath10k_htc_get_credit_allocation(htc,
692 : : conn_req->service_id);
693 [ # # ]: 0 : if (!tx_alloc)
694 [ # # ]: 0 : ath10k_dbg(ar, ATH10K_DBG_BOOT,
695 : : "boot htc service %s does not allocate target credits\n",
696 : : htc_service_name(conn_req->service_id));
697 : :
698 : 0 : skb = ath10k_htc_build_tx_ctrl_skb(htc->ar);
699 [ # # ]: 0 : if (!skb) {
700 : 0 : ath10k_err(ar, "Failed to allocate HTC packet\n");
701 : 0 : return -ENOMEM;
702 : : }
703 : :
704 : 0 : length = sizeof(msg->hdr) + sizeof(msg->connect_service);
705 : 0 : skb_put(skb, length);
706 : 0 : memset(skb->data, 0, length);
707 : :
708 : 0 : msg = (struct ath10k_htc_msg *)skb->data;
709 : 0 : msg->hdr.message_id =
710 : : __cpu_to_le16(ATH10K_HTC_MSG_CONNECT_SERVICE_ID);
711 : :
712 : 0 : flags |= SM(tx_alloc, ATH10K_HTC_CONN_FLAGS_RECV_ALLOC);
713 : :
714 : : /* Only enable credit flow control for WMI ctrl service */
715 [ # # ]: 0 : if (conn_req->service_id != ATH10K_HTC_SVC_ID_WMI_CONTROL) {
716 : 0 : flags |= ATH10K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL;
717 : 0 : disable_credit_flow_ctrl = true;
718 : : }
719 : :
720 : 0 : req_msg = &msg->connect_service;
721 : 0 : req_msg->flags = __cpu_to_le16(flags);
722 : 0 : req_msg->service_id = __cpu_to_le16(conn_req->service_id);
723 : :
724 : 0 : reinit_completion(&htc->ctl_resp);
725 : :
726 : 0 : status = ath10k_htc_send(htc, ATH10K_HTC_EP_0, skb);
727 [ # # ]: 0 : if (status) {
728 : 0 : kfree_skb(skb);
729 : 0 : return status;
730 : : }
731 : :
732 : : /* wait for response */
733 : 0 : time_left = wait_for_completion_timeout(&htc->ctl_resp,
734 : : ATH10K_HTC_CONN_SVC_TIMEOUT_HZ);
735 [ # # ]: 0 : if (!time_left) {
736 : 0 : ath10k_err(ar, "Service connect timeout\n");
737 : 0 : return -ETIMEDOUT;
738 : : }
739 : :
740 : : /* we controlled the buffer creation, it's aligned */
741 : 0 : msg = (struct ath10k_htc_msg *)htc->control_resp_buffer;
742 : 0 : resp_msg = &msg->connect_service_response;
743 : 0 : message_id = __le16_to_cpu(msg->hdr.message_id);
744 : 0 : service_id = __le16_to_cpu(resp_msg->service_id);
745 : :
746 [ # # ]: 0 : if ((message_id != ATH10K_HTC_MSG_CONNECT_SERVICE_RESP_ID) ||
747 [ # # ]: 0 : (htc->control_resp_len < sizeof(msg->hdr) +
748 : : sizeof(msg->connect_service_response))) {
749 : 0 : ath10k_err(ar, "Invalid resp message ID 0x%x", message_id);
750 : 0 : return -EPROTO;
751 : : }
752 : :
753 [ # # ]: 0 : ath10k_dbg(ar, ATH10K_DBG_HTC,
754 : : "HTC Service %s connect response: status: 0x%x, assigned ep: 0x%x\n",
755 : : htc_service_name(service_id),
756 : : resp_msg->status, resp_msg->eid);
757 : :
758 : 0 : conn_resp->connect_resp_code = resp_msg->status;
759 : :
760 : : /* check response status */
761 [ # # ]: 0 : if (resp_msg->status != ATH10K_HTC_CONN_SVC_STATUS_SUCCESS) {
762 : 0 : ath10k_err(ar, "HTC Service %s connect request failed: 0x%x)\n",
763 : : htc_service_name(service_id),
764 : : resp_msg->status);
765 : 0 : return -EPROTO;
766 : : }
767 : :
768 : 0 : assigned_eid = (enum ath10k_htc_ep_id)resp_msg->eid;
769 : 0 : max_msg_size = __le16_to_cpu(resp_msg->max_msg_size);
770 : :
771 : 0 : setup:
772 : :
773 [ # # ]: 0 : if (assigned_eid >= ATH10K_HTC_EP_COUNT)
774 : : return -EPROTO;
775 : :
776 [ # # ]: 0 : if (max_msg_size == 0)
777 : : return -EPROTO;
778 : :
779 : 0 : ep = &htc->endpoint[assigned_eid];
780 : 0 : ep->eid = assigned_eid;
781 : :
782 [ # # ]: 0 : if (ep->service_id != ATH10K_HTC_SVC_ID_UNUSED)
783 : : return -EPROTO;
784 : :
785 : : /* return assigned endpoint to caller */
786 : 0 : conn_resp->eid = assigned_eid;
787 : 0 : conn_resp->max_msg_len = __le16_to_cpu(resp_msg->max_msg_size);
788 : :
789 : : /* setup the endpoint */
790 : 0 : ep->service_id = conn_req->service_id;
791 : 0 : ep->max_tx_queue_depth = conn_req->max_send_queue_depth;
792 : 0 : ep->max_ep_message_len = __le16_to_cpu(resp_msg->max_msg_size);
793 : 0 : ep->tx_credits = tx_alloc;
794 : :
795 : : /* copy all the callbacks */
796 : 0 : ep->ep_ops = conn_req->ep_ops;
797 : :
798 : 0 : status = ath10k_hif_map_service_to_pipe(htc->ar,
799 : 0 : ep->service_id,
800 : : &ep->ul_pipe_id,
801 : : &ep->dl_pipe_id);
802 [ # # ]: 0 : if (status) {
803 [ # # ]: 0 : ath10k_dbg(ar, ATH10K_DBG_BOOT, "unsupported HTC service id: %d\n",
804 : : ep->service_id);
805 : 0 : return status;
806 : : }
807 : :
808 [ # # ]: 0 : ath10k_dbg(ar, ATH10K_DBG_BOOT,
809 : : "boot htc service '%s' ul pipe %d dl pipe %d eid %d ready\n",
810 : : htc_service_name(ep->service_id), ep->ul_pipe_id,
811 : : ep->dl_pipe_id, ep->eid);
812 : :
813 [ # # # # ]: 0 : if (disable_credit_flow_ctrl && ep->tx_credit_flow_enabled) {
814 : 0 : ep->tx_credit_flow_enabled = false;
815 [ # # ]: 0 : ath10k_dbg(ar, ATH10K_DBG_BOOT,
816 : : "boot htc service '%s' eid %d TX flow control disabled\n",
817 : : htc_service_name(ep->service_id), assigned_eid);
818 : : }
819 : :
820 : : return status;
821 : : }
822 : :
823 : 0 : struct sk_buff *ath10k_htc_alloc_skb(struct ath10k *ar, int size)
824 : : {
825 : 0 : struct sk_buff *skb;
826 : :
827 : 0 : skb = dev_alloc_skb(size + sizeof(struct ath10k_htc_hdr));
828 [ # # ]: 0 : if (!skb)
829 : : return NULL;
830 : :
831 [ # # ]: 0 : skb_reserve(skb, sizeof(struct ath10k_htc_hdr));
832 : :
833 : : /* FW/HTC requires 4-byte aligned streams */
834 [ # # ]: 0 : if (!IS_ALIGNED((unsigned long)skb->data, 4))
835 : 0 : ath10k_warn(ar, "Unaligned HTC tx skb\n");
836 : :
837 : : return skb;
838 : : }
839 : :
840 : 0 : static void ath10k_htc_pktlog_process_rx(struct ath10k *ar, struct sk_buff *skb)
841 : : {
842 : 0 : trace_ath10k_htt_pktlog(ar, skb->data, skb->len);
843 : 0 : dev_kfree_skb_any(skb);
844 : 0 : }
845 : :
846 : 0 : static int ath10k_htc_pktlog_connect(struct ath10k *ar)
847 : : {
848 : 0 : struct ath10k_htc_svc_conn_resp conn_resp;
849 : 0 : struct ath10k_htc_svc_conn_req conn_req;
850 : 0 : int status;
851 : :
852 : 0 : memset(&conn_req, 0, sizeof(conn_req));
853 : 0 : memset(&conn_resp, 0, sizeof(conn_resp));
854 : :
855 : 0 : conn_req.ep_ops.ep_tx_complete = NULL;
856 : 0 : conn_req.ep_ops.ep_rx_complete = ath10k_htc_pktlog_process_rx;
857 : 0 : conn_req.ep_ops.ep_tx_credits = NULL;
858 : :
859 : : /* connect to control service */
860 : 0 : conn_req.service_id = ATH10K_HTC_SVC_ID_HTT_LOG_MSG;
861 : 0 : status = ath10k_htc_connect_service(&ar->htc, &conn_req, &conn_resp);
862 [ # # ]: 0 : if (status) {
863 : 0 : ath10k_warn(ar, "failed to connect to PKTLOG service: %d\n",
864 : : status);
865 : 0 : return status;
866 : : }
867 : :
868 : : return 0;
869 : : }
870 : :
871 : 0 : static bool ath10k_htc_pktlog_svc_supported(struct ath10k *ar)
872 : : {
873 : 0 : u8 ul_pipe_id;
874 : 0 : u8 dl_pipe_id;
875 : 0 : int status;
876 : :
877 : 0 : status = ath10k_hif_map_service_to_pipe(ar, ATH10K_HTC_SVC_ID_HTT_LOG_MSG,
878 : : &ul_pipe_id,
879 : : &dl_pipe_id);
880 [ # # ]: 0 : if (status) {
881 [ # # ]: 0 : ath10k_dbg(ar, ATH10K_DBG_BOOT, "unsupported HTC pktlog service id: %d\n",
882 : : ATH10K_HTC_SVC_ID_HTT_LOG_MSG);
883 : :
884 : 0 : return false;
885 : : }
886 : :
887 : : return true;
888 : : }
889 : :
890 : 0 : int ath10k_htc_start(struct ath10k_htc *htc)
891 : : {
892 : 0 : struct ath10k *ar = htc->ar;
893 : 0 : struct sk_buff *skb;
894 : 0 : int status = 0;
895 : 0 : struct ath10k_htc_msg *msg;
896 : :
897 : 0 : skb = ath10k_htc_build_tx_ctrl_skb(htc->ar);
898 [ # # ]: 0 : if (!skb)
899 : : return -ENOMEM;
900 : :
901 : 0 : skb_put(skb, sizeof(msg->hdr) + sizeof(msg->setup_complete_ext));
902 : 0 : memset(skb->data, 0, skb->len);
903 : :
904 : 0 : msg = (struct ath10k_htc_msg *)skb->data;
905 : 0 : msg->hdr.message_id =
906 : : __cpu_to_le16(ATH10K_HTC_MSG_SETUP_COMPLETE_EX_ID);
907 : :
908 [ # # ]: 0 : if (ar->hif.bus == ATH10K_BUS_SDIO) {
909 : : /* Extra setup params used by SDIO */
910 : 0 : msg->setup_complete_ext.flags =
911 : : __cpu_to_le32(ATH10K_HTC_SETUP_COMPLETE_FLAGS_RX_BNDL_EN);
912 : 0 : msg->setup_complete_ext.max_msgs_per_bundled_recv =
913 : 0 : htc->max_msgs_per_htc_bundle;
914 : : }
915 [ # # ]: 0 : ath10k_dbg(ar, ATH10K_DBG_HTC, "HTC is using TX credit flow control\n");
916 : :
917 : 0 : status = ath10k_htc_send(htc, ATH10K_HTC_EP_0, skb);
918 [ # # ]: 0 : if (status) {
919 : 0 : kfree_skb(skb);
920 : 0 : return status;
921 : : }
922 : :
923 [ # # ]: 0 : if (ath10k_htc_pktlog_svc_supported(ar)) {
924 : 0 : status = ath10k_htc_pktlog_connect(ar);
925 [ # # ]: 0 : if (status) {
926 : 0 : ath10k_err(ar, "failed to connect to pktlog: %d\n", status);
927 : 0 : return status;
928 : : }
929 : : }
930 : :
931 : : return 0;
932 : : }
933 : :
934 : : /* registered target arrival callback from the HIF layer */
935 : 0 : int ath10k_htc_init(struct ath10k *ar)
936 : : {
937 : 0 : int status;
938 : 0 : struct ath10k_htc *htc = &ar->htc;
939 : 0 : struct ath10k_htc_svc_conn_req conn_req;
940 : 0 : struct ath10k_htc_svc_conn_resp conn_resp;
941 : :
942 : 0 : spin_lock_init(&htc->tx_lock);
943 : :
944 : 0 : ath10k_htc_reset_endpoint_states(htc);
945 : :
946 : 0 : htc->ar = ar;
947 : :
948 : : /* setup our pseudo HTC control endpoint connection */
949 : 0 : memset(&conn_req, 0, sizeof(conn_req));
950 : 0 : memset(&conn_resp, 0, sizeof(conn_resp));
951 : 0 : conn_req.ep_ops.ep_tx_complete = ath10k_htc_control_tx_complete;
952 : 0 : conn_req.ep_ops.ep_rx_complete = ath10k_htc_control_rx_complete;
953 : 0 : conn_req.max_send_queue_depth = ATH10K_NUM_CONTROL_TX_BUFFERS;
954 : 0 : conn_req.service_id = ATH10K_HTC_SVC_ID_RSVD_CTRL;
955 : :
956 : : /* connect fake service */
957 : 0 : status = ath10k_htc_connect_service(htc, &conn_req, &conn_resp);
958 [ # # ]: 0 : if (status) {
959 : 0 : ath10k_err(ar, "could not connect to htc service (%d)\n",
960 : : status);
961 : 0 : return status;
962 : : }
963 : :
964 : 0 : init_completion(&htc->ctl_resp);
965 : :
966 : 0 : return 0;
967 : : }
|