.. | .. |
---|
| 1 | +// SPDX-License-Identifier: ISC |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright (c) 2005-2011 Atheros Communications Inc. |
---|
3 | 4 | * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. |
---|
4 | | - * |
---|
5 | | - * Permission to use, copy, modify, and/or distribute this software for any |
---|
6 | | - * purpose with or without fee is hereby granted, provided that the above |
---|
7 | | - * copyright notice and this permission notice appear in all copies. |
---|
8 | | - * |
---|
9 | | - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
---|
10 | | - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
---|
11 | | - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
---|
12 | | - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
---|
13 | | - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
---|
14 | | - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
---|
15 | | - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
---|
16 | 5 | */ |
---|
17 | 6 | |
---|
18 | 7 | #include "core.h" |
---|
.. | .. |
---|
53 | 42 | { |
---|
54 | 43 | struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb); |
---|
55 | 44 | |
---|
56 | | - dma_unmap_single(htc->ar->dev, skb_cb->paddr, skb->len, DMA_TO_DEVICE); |
---|
| 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); |
---|
57 | 47 | skb_pull(skb, sizeof(struct ath10k_htc_hdr)); |
---|
58 | 48 | } |
---|
59 | 49 | |
---|
.. | .. |
---|
61 | 51 | struct sk_buff *skb) |
---|
62 | 52 | { |
---|
63 | 53 | struct ath10k *ar = ep->htc->ar; |
---|
| 54 | + struct ath10k_htc_hdr *hdr; |
---|
64 | 55 | |
---|
65 | 56 | ath10k_dbg(ar, ATH10K_DBG_HTC, "%s: ep %d skb %pK\n", __func__, |
---|
66 | 57 | ep->eid, skb); |
---|
67 | 58 | |
---|
| 59 | + hdr = (struct ath10k_htc_hdr *)skb->data; |
---|
68 | 60 | ath10k_htc_restore_tx_skb(ep->htc, skb); |
---|
69 | 61 | |
---|
70 | 62 | if (!ep->ep_ops.ep_tx_complete) { |
---|
71 | 63 | ath10k_warn(ar, "no tx handler for eid %d\n", ep->eid); |
---|
| 64 | + dev_kfree_skb_any(skb); |
---|
| 65 | + return; |
---|
| 66 | + } |
---|
| 67 | + |
---|
| 68 | + if (hdr->flags & ATH10K_HTC_FLAG_SEND_BUNDLE) { |
---|
72 | 69 | dev_kfree_skb_any(skb); |
---|
73 | 70 | return; |
---|
74 | 71 | } |
---|
.. | .. |
---|
83 | 80 | struct ath10k_htc_hdr *hdr; |
---|
84 | 81 | |
---|
85 | 82 | hdr = (struct ath10k_htc_hdr *)skb->data; |
---|
| 83 | + memset(hdr, 0, sizeof(struct ath10k_htc_hdr)); |
---|
86 | 84 | |
---|
87 | 85 | hdr->eid = ep->eid; |
---|
88 | 86 | hdr->len = __cpu_to_le16(skb->len - sizeof(*hdr)); |
---|
89 | 87 | hdr->flags = 0; |
---|
90 | | - hdr->flags |= ATH10K_HTC_FLAG_NEED_CREDIT_UPDATE; |
---|
| 88 | + if (ep->tx_credit_flow_enabled && !ep->bundle_tx) |
---|
| 89 | + hdr->flags |= ATH10K_HTC_FLAG_NEED_CREDIT_UPDATE; |
---|
91 | 90 | |
---|
92 | 91 | spin_lock_bh(&ep->htc->tx_lock); |
---|
93 | 92 | hdr->seq_no = ep->seq_no++; |
---|
94 | 93 | spin_unlock_bh(&ep->htc->tx_lock); |
---|
| 94 | +} |
---|
| 95 | + |
---|
| 96 | +static int ath10k_htc_consume_credit(struct ath10k_htc_ep *ep, |
---|
| 97 | + unsigned int len, |
---|
| 98 | + bool consume) |
---|
| 99 | +{ |
---|
| 100 | + struct ath10k_htc *htc = ep->htc; |
---|
| 101 | + struct ath10k *ar = htc->ar; |
---|
| 102 | + enum ath10k_htc_ep_id eid = ep->eid; |
---|
| 103 | + int credits, ret = 0; |
---|
| 104 | + |
---|
| 105 | + if (!ep->tx_credit_flow_enabled) |
---|
| 106 | + return 0; |
---|
| 107 | + |
---|
| 108 | + credits = DIV_ROUND_UP(len, ep->tx_credit_size); |
---|
| 109 | + spin_lock_bh(&htc->tx_lock); |
---|
| 110 | + |
---|
| 111 | + if (ep->tx_credits < credits) { |
---|
| 112 | + ath10k_dbg(ar, ATH10K_DBG_HTC, |
---|
| 113 | + "htc insufficient credits ep %d required %d available %d consume %d\n", |
---|
| 114 | + eid, credits, ep->tx_credits, consume); |
---|
| 115 | + ret = -EAGAIN; |
---|
| 116 | + goto unlock; |
---|
| 117 | + } |
---|
| 118 | + |
---|
| 119 | + if (consume) { |
---|
| 120 | + ep->tx_credits -= credits; |
---|
| 121 | + ath10k_dbg(ar, ATH10K_DBG_HTC, |
---|
| 122 | + "htc ep %d consumed %d credits total %d\n", |
---|
| 123 | + eid, credits, ep->tx_credits); |
---|
| 124 | + } |
---|
| 125 | + |
---|
| 126 | +unlock: |
---|
| 127 | + spin_unlock_bh(&htc->tx_lock); |
---|
| 128 | + return ret; |
---|
| 129 | +} |
---|
| 130 | + |
---|
| 131 | +static void ath10k_htc_release_credit(struct ath10k_htc_ep *ep, unsigned int len) |
---|
| 132 | +{ |
---|
| 133 | + struct ath10k_htc *htc = ep->htc; |
---|
| 134 | + struct ath10k *ar = htc->ar; |
---|
| 135 | + enum ath10k_htc_ep_id eid = ep->eid; |
---|
| 136 | + int credits; |
---|
| 137 | + |
---|
| 138 | + if (!ep->tx_credit_flow_enabled) |
---|
| 139 | + return; |
---|
| 140 | + |
---|
| 141 | + credits = DIV_ROUND_UP(len, ep->tx_credit_size); |
---|
| 142 | + spin_lock_bh(&htc->tx_lock); |
---|
| 143 | + ep->tx_credits += credits; |
---|
| 144 | + ath10k_dbg(ar, ATH10K_DBG_HTC, |
---|
| 145 | + "htc ep %d reverted %d credits back total %d\n", |
---|
| 146 | + eid, credits, ep->tx_credits); |
---|
| 147 | + spin_unlock_bh(&htc->tx_lock); |
---|
| 148 | + |
---|
| 149 | + if (ep->ep_ops.ep_tx_credits) |
---|
| 150 | + ep->ep_ops.ep_tx_credits(htc->ar); |
---|
95 | 151 | } |
---|
96 | 152 | |
---|
97 | 153 | int ath10k_htc_send(struct ath10k_htc *htc, |
---|
.. | .. |
---|
103 | 159 | struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb); |
---|
104 | 160 | struct ath10k_hif_sg_item sg_item; |
---|
105 | 161 | struct device *dev = htc->ar->dev; |
---|
106 | | - int credits = 0; |
---|
107 | 162 | int ret; |
---|
| 163 | + unsigned int skb_len; |
---|
108 | 164 | |
---|
109 | 165 | if (htc->ar->state == ATH10K_STATE_WEDGED) |
---|
110 | 166 | return -ECOMM; |
---|
.. | .. |
---|
116 | 172 | |
---|
117 | 173 | skb_push(skb, sizeof(struct ath10k_htc_hdr)); |
---|
118 | 174 | |
---|
119 | | - if (ep->tx_credit_flow_enabled) { |
---|
120 | | - credits = DIV_ROUND_UP(skb->len, htc->target_credit_size); |
---|
121 | | - spin_lock_bh(&htc->tx_lock); |
---|
122 | | - if (ep->tx_credits < credits) { |
---|
123 | | - ath10k_dbg(ar, ATH10K_DBG_HTC, |
---|
124 | | - "htc insufficient credits ep %d required %d available %d\n", |
---|
125 | | - eid, credits, ep->tx_credits); |
---|
126 | | - spin_unlock_bh(&htc->tx_lock); |
---|
127 | | - ret = -EAGAIN; |
---|
128 | | - goto err_pull; |
---|
129 | | - } |
---|
130 | | - ep->tx_credits -= credits; |
---|
131 | | - ath10k_dbg(ar, ATH10K_DBG_HTC, |
---|
132 | | - "htc ep %d consumed %d credits (total %d)\n", |
---|
133 | | - eid, credits, ep->tx_credits); |
---|
134 | | - spin_unlock_bh(&htc->tx_lock); |
---|
135 | | - } |
---|
| 175 | + skb_len = skb->len; |
---|
| 176 | + ret = ath10k_htc_consume_credit(ep, skb_len, true); |
---|
| 177 | + if (ret) |
---|
| 178 | + goto err_pull; |
---|
136 | 179 | |
---|
137 | 180 | ath10k_htc_prepare_tx_skb(ep, skb); |
---|
138 | 181 | |
---|
139 | 182 | skb_cb->eid = eid; |
---|
140 | | - skb_cb->paddr = dma_map_single(dev, skb->data, skb->len, DMA_TO_DEVICE); |
---|
141 | | - ret = dma_mapping_error(dev, skb_cb->paddr); |
---|
142 | | - if (ret) { |
---|
143 | | - ret = -EIO; |
---|
144 | | - goto err_credits; |
---|
| 183 | + if (ar->bus_param.dev_type != ATH10K_DEV_TYPE_HL) { |
---|
| 184 | + skb_cb->paddr = dma_map_single(dev, skb->data, skb->len, |
---|
| 185 | + DMA_TO_DEVICE); |
---|
| 186 | + ret = dma_mapping_error(dev, skb_cb->paddr); |
---|
| 187 | + if (ret) { |
---|
| 188 | + ret = -EIO; |
---|
| 189 | + goto err_credits; |
---|
| 190 | + } |
---|
145 | 191 | } |
---|
146 | 192 | |
---|
147 | 193 | sg_item.transfer_id = ep->eid; |
---|
.. | .. |
---|
157 | 203 | return 0; |
---|
158 | 204 | |
---|
159 | 205 | err_unmap: |
---|
160 | | - dma_unmap_single(dev, skb_cb->paddr, skb->len, DMA_TO_DEVICE); |
---|
| 206 | + if (ar->bus_param.dev_type != ATH10K_DEV_TYPE_HL) |
---|
| 207 | + dma_unmap_single(dev, skb_cb->paddr, skb->len, DMA_TO_DEVICE); |
---|
161 | 208 | err_credits: |
---|
162 | | - if (ep->tx_credit_flow_enabled) { |
---|
163 | | - spin_lock_bh(&htc->tx_lock); |
---|
164 | | - ep->tx_credits += credits; |
---|
165 | | - ath10k_dbg(ar, ATH10K_DBG_HTC, |
---|
166 | | - "htc ep %d reverted %d credits back (total %d)\n", |
---|
167 | | - eid, credits, ep->tx_credits); |
---|
168 | | - spin_unlock_bh(&htc->tx_lock); |
---|
169 | | - |
---|
170 | | - if (ep->ep_ops.ep_tx_credits) |
---|
171 | | - ep->ep_ops.ep_tx_credits(htc->ar); |
---|
172 | | - } |
---|
| 209 | + ath10k_htc_release_credit(ep, skb_len); |
---|
173 | 210 | err_pull: |
---|
174 | 211 | skb_pull(skb, sizeof(struct ath10k_htc_hdr)); |
---|
175 | 212 | return ret; |
---|
.. | .. |
---|
274 | 311 | struct ath10k *ar = htc->ar; |
---|
275 | 312 | int bundle_cnt = len / sizeof(*report); |
---|
276 | 313 | |
---|
277 | | - if (!bundle_cnt || (bundle_cnt > HTC_HOST_MAX_MSG_PER_RX_BUNDLE)) { |
---|
| 314 | + if (!bundle_cnt || (bundle_cnt > htc->max_msgs_per_htc_bundle)) { |
---|
278 | 315 | ath10k_warn(ar, "Invalid lookahead bundle count: %d\n", |
---|
279 | 316 | bundle_cnt); |
---|
280 | 317 | return -EINVAL; |
---|
.. | .. |
---|
585 | 622 | return allocation; |
---|
586 | 623 | } |
---|
587 | 624 | |
---|
| 625 | +static int ath10k_htc_send_bundle(struct ath10k_htc_ep *ep, |
---|
| 626 | + struct sk_buff *bundle_skb, |
---|
| 627 | + struct sk_buff_head *tx_save_head) |
---|
| 628 | +{ |
---|
| 629 | + struct ath10k_hif_sg_item sg_item; |
---|
| 630 | + struct ath10k_htc *htc = ep->htc; |
---|
| 631 | + struct ath10k *ar = htc->ar; |
---|
| 632 | + struct sk_buff *skb; |
---|
| 633 | + int ret, cn = 0; |
---|
| 634 | + unsigned int skb_len; |
---|
| 635 | + |
---|
| 636 | + ath10k_dbg(ar, ATH10K_DBG_HTC, "bundle skb len %d\n", bundle_skb->len); |
---|
| 637 | + skb_len = bundle_skb->len; |
---|
| 638 | + ret = ath10k_htc_consume_credit(ep, skb_len, true); |
---|
| 639 | + |
---|
| 640 | + if (!ret) { |
---|
| 641 | + sg_item.transfer_id = ep->eid; |
---|
| 642 | + sg_item.transfer_context = bundle_skb; |
---|
| 643 | + sg_item.vaddr = bundle_skb->data; |
---|
| 644 | + sg_item.len = bundle_skb->len; |
---|
| 645 | + |
---|
| 646 | + ret = ath10k_hif_tx_sg(htc->ar, ep->ul_pipe_id, &sg_item, 1); |
---|
| 647 | + if (ret) |
---|
| 648 | + ath10k_htc_release_credit(ep, skb_len); |
---|
| 649 | + } |
---|
| 650 | + |
---|
| 651 | + if (ret) |
---|
| 652 | + dev_kfree_skb_any(bundle_skb); |
---|
| 653 | + |
---|
| 654 | + for (cn = 0; (skb = skb_dequeue_tail(tx_save_head)); cn++) { |
---|
| 655 | + if (ret) { |
---|
| 656 | + skb_pull(skb, sizeof(struct ath10k_htc_hdr)); |
---|
| 657 | + skb_queue_head(&ep->tx_req_head, skb); |
---|
| 658 | + } else { |
---|
| 659 | + skb_queue_tail(&ep->tx_complete_head, skb); |
---|
| 660 | + } |
---|
| 661 | + } |
---|
| 662 | + |
---|
| 663 | + if (!ret) |
---|
| 664 | + queue_work(ar->workqueue_tx_complete, &ar->tx_complete_work); |
---|
| 665 | + |
---|
| 666 | + ath10k_dbg(ar, ATH10K_DBG_HTC, |
---|
| 667 | + "bundle tx status %d eid %d req count %d count %d len %d\n", |
---|
| 668 | + ret, ep->eid, skb_queue_len(&ep->tx_req_head), cn, skb_len); |
---|
| 669 | + return ret; |
---|
| 670 | +} |
---|
| 671 | + |
---|
| 672 | +static void ath10k_htc_send_one_skb(struct ath10k_htc_ep *ep, struct sk_buff *skb) |
---|
| 673 | +{ |
---|
| 674 | + struct ath10k_htc *htc = ep->htc; |
---|
| 675 | + struct ath10k *ar = htc->ar; |
---|
| 676 | + int ret; |
---|
| 677 | + |
---|
| 678 | + ret = ath10k_htc_send(htc, ep->eid, skb); |
---|
| 679 | + |
---|
| 680 | + if (ret) |
---|
| 681 | + skb_queue_head(&ep->tx_req_head, skb); |
---|
| 682 | + |
---|
| 683 | + ath10k_dbg(ar, ATH10K_DBG_HTC, "tx one status %d eid %d len %d pending count %d\n", |
---|
| 684 | + ret, ep->eid, skb->len, skb_queue_len(&ep->tx_req_head)); |
---|
| 685 | +} |
---|
| 686 | + |
---|
| 687 | +static int ath10k_htc_send_bundle_skbs(struct ath10k_htc_ep *ep) |
---|
| 688 | +{ |
---|
| 689 | + struct ath10k_htc *htc = ep->htc; |
---|
| 690 | + struct sk_buff *bundle_skb, *skb; |
---|
| 691 | + struct sk_buff_head tx_save_head; |
---|
| 692 | + struct ath10k_htc_hdr *hdr; |
---|
| 693 | + u8 *bundle_buf; |
---|
| 694 | + int ret = 0, credit_pad, credit_remainder, trans_len, bundles_left = 0; |
---|
| 695 | + |
---|
| 696 | + if (htc->ar->state == ATH10K_STATE_WEDGED) |
---|
| 697 | + return -ECOMM; |
---|
| 698 | + |
---|
| 699 | + if (ep->tx_credit_flow_enabled && |
---|
| 700 | + ep->tx_credits < ATH10K_MIN_CREDIT_PER_HTC_TX_BUNDLE) |
---|
| 701 | + return 0; |
---|
| 702 | + |
---|
| 703 | + bundles_left = ATH10K_MAX_MSG_PER_HTC_TX_BUNDLE * ep->tx_credit_size; |
---|
| 704 | + bundle_skb = dev_alloc_skb(bundles_left); |
---|
| 705 | + |
---|
| 706 | + if (!bundle_skb) |
---|
| 707 | + return -ENOMEM; |
---|
| 708 | + |
---|
| 709 | + bundle_buf = bundle_skb->data; |
---|
| 710 | + skb_queue_head_init(&tx_save_head); |
---|
| 711 | + |
---|
| 712 | + while (true) { |
---|
| 713 | + skb = skb_dequeue(&ep->tx_req_head); |
---|
| 714 | + if (!skb) |
---|
| 715 | + break; |
---|
| 716 | + |
---|
| 717 | + credit_pad = 0; |
---|
| 718 | + trans_len = skb->len + sizeof(*hdr); |
---|
| 719 | + credit_remainder = trans_len % ep->tx_credit_size; |
---|
| 720 | + |
---|
| 721 | + if (credit_remainder != 0) { |
---|
| 722 | + credit_pad = ep->tx_credit_size - credit_remainder; |
---|
| 723 | + trans_len += credit_pad; |
---|
| 724 | + } |
---|
| 725 | + |
---|
| 726 | + ret = ath10k_htc_consume_credit(ep, |
---|
| 727 | + bundle_buf + trans_len - bundle_skb->data, |
---|
| 728 | + false); |
---|
| 729 | + if (ret) { |
---|
| 730 | + skb_queue_head(&ep->tx_req_head, skb); |
---|
| 731 | + break; |
---|
| 732 | + } |
---|
| 733 | + |
---|
| 734 | + if (bundles_left < trans_len) { |
---|
| 735 | + bundle_skb->len = bundle_buf - bundle_skb->data; |
---|
| 736 | + ret = ath10k_htc_send_bundle(ep, bundle_skb, &tx_save_head); |
---|
| 737 | + |
---|
| 738 | + if (ret) { |
---|
| 739 | + skb_queue_head(&ep->tx_req_head, skb); |
---|
| 740 | + return ret; |
---|
| 741 | + } |
---|
| 742 | + |
---|
| 743 | + if (skb_queue_len(&ep->tx_req_head) == 0) { |
---|
| 744 | + ath10k_htc_send_one_skb(ep, skb); |
---|
| 745 | + return ret; |
---|
| 746 | + } |
---|
| 747 | + |
---|
| 748 | + if (ep->tx_credit_flow_enabled && |
---|
| 749 | + ep->tx_credits < ATH10K_MIN_CREDIT_PER_HTC_TX_BUNDLE) { |
---|
| 750 | + skb_queue_head(&ep->tx_req_head, skb); |
---|
| 751 | + return 0; |
---|
| 752 | + } |
---|
| 753 | + |
---|
| 754 | + bundles_left = |
---|
| 755 | + ATH10K_MAX_MSG_PER_HTC_TX_BUNDLE * ep->tx_credit_size; |
---|
| 756 | + bundle_skb = dev_alloc_skb(bundles_left); |
---|
| 757 | + |
---|
| 758 | + if (!bundle_skb) { |
---|
| 759 | + skb_queue_head(&ep->tx_req_head, skb); |
---|
| 760 | + return -ENOMEM; |
---|
| 761 | + } |
---|
| 762 | + bundle_buf = bundle_skb->data; |
---|
| 763 | + skb_queue_head_init(&tx_save_head); |
---|
| 764 | + } |
---|
| 765 | + |
---|
| 766 | + skb_push(skb, sizeof(struct ath10k_htc_hdr)); |
---|
| 767 | + ath10k_htc_prepare_tx_skb(ep, skb); |
---|
| 768 | + |
---|
| 769 | + memcpy(bundle_buf, skb->data, skb->len); |
---|
| 770 | + hdr = (struct ath10k_htc_hdr *)bundle_buf; |
---|
| 771 | + hdr->flags |= ATH10K_HTC_FLAG_SEND_BUNDLE; |
---|
| 772 | + hdr->pad_len = __cpu_to_le16(credit_pad); |
---|
| 773 | + bundle_buf += trans_len; |
---|
| 774 | + bundles_left -= trans_len; |
---|
| 775 | + skb_queue_tail(&tx_save_head, skb); |
---|
| 776 | + } |
---|
| 777 | + |
---|
| 778 | + if (bundle_buf != bundle_skb->data) { |
---|
| 779 | + bundle_skb->len = bundle_buf - bundle_skb->data; |
---|
| 780 | + ret = ath10k_htc_send_bundle(ep, bundle_skb, &tx_save_head); |
---|
| 781 | + } else { |
---|
| 782 | + dev_kfree_skb_any(bundle_skb); |
---|
| 783 | + } |
---|
| 784 | + |
---|
| 785 | + return ret; |
---|
| 786 | +} |
---|
| 787 | + |
---|
| 788 | +static void ath10k_htc_bundle_tx_work(struct work_struct *work) |
---|
| 789 | +{ |
---|
| 790 | + struct ath10k *ar = container_of(work, struct ath10k, bundle_tx_work); |
---|
| 791 | + struct ath10k_htc_ep *ep; |
---|
| 792 | + struct sk_buff *skb; |
---|
| 793 | + int i; |
---|
| 794 | + |
---|
| 795 | + for (i = 0; i < ARRAY_SIZE(ar->htc.endpoint); i++) { |
---|
| 796 | + ep = &ar->htc.endpoint[i]; |
---|
| 797 | + |
---|
| 798 | + if (!ep->bundle_tx) |
---|
| 799 | + continue; |
---|
| 800 | + |
---|
| 801 | + ath10k_dbg(ar, ATH10K_DBG_HTC, "bundle tx work eid %d count %d\n", |
---|
| 802 | + ep->eid, skb_queue_len(&ep->tx_req_head)); |
---|
| 803 | + |
---|
| 804 | + if (skb_queue_len(&ep->tx_req_head) >= |
---|
| 805 | + ATH10K_MIN_MSG_PER_HTC_TX_BUNDLE) { |
---|
| 806 | + ath10k_htc_send_bundle_skbs(ep); |
---|
| 807 | + } else { |
---|
| 808 | + skb = skb_dequeue(&ep->tx_req_head); |
---|
| 809 | + |
---|
| 810 | + if (!skb) |
---|
| 811 | + continue; |
---|
| 812 | + ath10k_htc_send_one_skb(ep, skb); |
---|
| 813 | + } |
---|
| 814 | + } |
---|
| 815 | +} |
---|
| 816 | + |
---|
| 817 | +static void ath10k_htc_tx_complete_work(struct work_struct *work) |
---|
| 818 | +{ |
---|
| 819 | + struct ath10k *ar = container_of(work, struct ath10k, tx_complete_work); |
---|
| 820 | + struct ath10k_htc_ep *ep; |
---|
| 821 | + enum ath10k_htc_ep_id eid; |
---|
| 822 | + struct sk_buff *skb; |
---|
| 823 | + int i; |
---|
| 824 | + |
---|
| 825 | + for (i = 0; i < ARRAY_SIZE(ar->htc.endpoint); i++) { |
---|
| 826 | + ep = &ar->htc.endpoint[i]; |
---|
| 827 | + eid = ep->eid; |
---|
| 828 | + if (ep->bundle_tx && eid == ar->htt.eid) { |
---|
| 829 | + ath10k_dbg(ar, ATH10K_DBG_HTC, "bundle tx complete eid %d pending complete count%d\n", |
---|
| 830 | + ep->eid, skb_queue_len(&ep->tx_complete_head)); |
---|
| 831 | + |
---|
| 832 | + while (true) { |
---|
| 833 | + skb = skb_dequeue(&ep->tx_complete_head); |
---|
| 834 | + if (!skb) |
---|
| 835 | + break; |
---|
| 836 | + ath10k_htc_notify_tx_completion(ep, skb); |
---|
| 837 | + } |
---|
| 838 | + } |
---|
| 839 | + } |
---|
| 840 | +} |
---|
| 841 | + |
---|
| 842 | +int ath10k_htc_send_hl(struct ath10k_htc *htc, |
---|
| 843 | + enum ath10k_htc_ep_id eid, |
---|
| 844 | + struct sk_buff *skb) |
---|
| 845 | +{ |
---|
| 846 | + struct ath10k_htc_ep *ep = &htc->endpoint[eid]; |
---|
| 847 | + struct ath10k *ar = htc->ar; |
---|
| 848 | + |
---|
| 849 | + if (sizeof(struct ath10k_htc_hdr) + skb->len > ep->tx_credit_size) { |
---|
| 850 | + ath10k_dbg(ar, ATH10K_DBG_HTC, "tx exceed max len %d\n", skb->len); |
---|
| 851 | + return -ENOMEM; |
---|
| 852 | + } |
---|
| 853 | + |
---|
| 854 | + ath10k_dbg(ar, ATH10K_DBG_HTC, "htc send hl eid %d bundle %d tx count %d len %d\n", |
---|
| 855 | + eid, ep->bundle_tx, skb_queue_len(&ep->tx_req_head), skb->len); |
---|
| 856 | + |
---|
| 857 | + if (ep->bundle_tx) { |
---|
| 858 | + skb_queue_tail(&ep->tx_req_head, skb); |
---|
| 859 | + queue_work(ar->workqueue, &ar->bundle_tx_work); |
---|
| 860 | + return 0; |
---|
| 861 | + } else { |
---|
| 862 | + return ath10k_htc_send(htc, eid, skb); |
---|
| 863 | + } |
---|
| 864 | +} |
---|
| 865 | + |
---|
| 866 | +void ath10k_htc_setup_tx_req(struct ath10k_htc_ep *ep) |
---|
| 867 | +{ |
---|
| 868 | + if (ep->htc->max_msgs_per_htc_bundle >= ATH10K_MIN_MSG_PER_HTC_TX_BUNDLE && |
---|
| 869 | + !ep->bundle_tx) { |
---|
| 870 | + ep->bundle_tx = true; |
---|
| 871 | + skb_queue_head_init(&ep->tx_req_head); |
---|
| 872 | + skb_queue_head_init(&ep->tx_complete_head); |
---|
| 873 | + } |
---|
| 874 | +} |
---|
| 875 | + |
---|
| 876 | +void ath10k_htc_stop_hl(struct ath10k *ar) |
---|
| 877 | +{ |
---|
| 878 | + struct ath10k_htc_ep *ep; |
---|
| 879 | + int i; |
---|
| 880 | + |
---|
| 881 | + cancel_work_sync(&ar->bundle_tx_work); |
---|
| 882 | + cancel_work_sync(&ar->tx_complete_work); |
---|
| 883 | + |
---|
| 884 | + for (i = 0; i < ARRAY_SIZE(ar->htc.endpoint); i++) { |
---|
| 885 | + ep = &ar->htc.endpoint[i]; |
---|
| 886 | + |
---|
| 887 | + if (!ep->bundle_tx) |
---|
| 888 | + continue; |
---|
| 889 | + |
---|
| 890 | + ath10k_dbg(ar, ATH10K_DBG_HTC, "stop tx work eid %d count %d\n", |
---|
| 891 | + ep->eid, skb_queue_len(&ep->tx_req_head)); |
---|
| 892 | + |
---|
| 893 | + skb_queue_purge(&ep->tx_req_head); |
---|
| 894 | + } |
---|
| 895 | +} |
---|
| 896 | + |
---|
588 | 897 | int ath10k_htc_wait_target(struct ath10k_htc *htc) |
---|
589 | 898 | { |
---|
590 | 899 | struct ath10k *ar = htc->ar; |
---|
.. | .. |
---|
653 | 962 | */ |
---|
654 | 963 | if (htc->control_resp_len >= |
---|
655 | 964 | sizeof(msg->hdr) + sizeof(msg->ready_ext)) { |
---|
| 965 | + htc->alt_data_credit_size = |
---|
| 966 | + __le16_to_cpu(msg->ready_ext.reserved) & |
---|
| 967 | + ATH10K_HTC_MSG_READY_EXT_ALT_DATA_MASK; |
---|
656 | 968 | htc->max_msgs_per_htc_bundle = |
---|
657 | 969 | min_t(u8, msg->ready_ext.max_msgs_per_htc_bundle, |
---|
658 | 970 | HTC_HOST_MAX_MSG_PER_RX_BUNDLE); |
---|
659 | 971 | ath10k_dbg(ar, ATH10K_DBG_HTC, |
---|
660 | | - "Extended ready message. RX bundle size: %d\n", |
---|
661 | | - htc->max_msgs_per_htc_bundle); |
---|
| 972 | + "Extended ready message RX bundle size %d alt size %d\n", |
---|
| 973 | + htc->max_msgs_per_htc_bundle, |
---|
| 974 | + htc->alt_data_credit_size); |
---|
662 | 975 | } |
---|
663 | 976 | |
---|
| 977 | + INIT_WORK(&ar->bundle_tx_work, ath10k_htc_bundle_tx_work); |
---|
| 978 | + INIT_WORK(&ar->tx_complete_work, ath10k_htc_tx_complete_work); |
---|
| 979 | + |
---|
664 | 980 | return 0; |
---|
| 981 | +} |
---|
| 982 | + |
---|
| 983 | +void ath10k_htc_change_tx_credit_flow(struct ath10k_htc *htc, |
---|
| 984 | + enum ath10k_htc_ep_id eid, |
---|
| 985 | + bool enable) |
---|
| 986 | +{ |
---|
| 987 | + struct ath10k *ar = htc->ar; |
---|
| 988 | + struct ath10k_htc_ep *ep = &ar->htc.endpoint[eid]; |
---|
| 989 | + |
---|
| 990 | + ep->tx_credit_flow_enabled = enable; |
---|
665 | 991 | } |
---|
666 | 992 | |
---|
667 | 993 | int ath10k_htc_connect_service(struct ath10k_htc *htc, |
---|
.. | .. |
---|
795 | 1121 | ep->max_tx_queue_depth = conn_req->max_send_queue_depth; |
---|
796 | 1122 | ep->max_ep_message_len = __le16_to_cpu(resp_msg->max_msg_size); |
---|
797 | 1123 | ep->tx_credits = tx_alloc; |
---|
| 1124 | + ep->tx_credit_size = htc->target_credit_size; |
---|
| 1125 | + |
---|
| 1126 | + if (conn_req->service_id == ATH10K_HTC_SVC_ID_HTT_DATA_MSG && |
---|
| 1127 | + htc->alt_data_credit_size != 0) |
---|
| 1128 | + ep->tx_credit_size = htc->alt_data_credit_size; |
---|
798 | 1129 | |
---|
799 | 1130 | /* copy all the callbacks */ |
---|
800 | 1131 | ep->ep_ops = conn_req->ep_ops; |
---|
.. | .. |
---|
803 | 1134 | ep->service_id, |
---|
804 | 1135 | &ep->ul_pipe_id, |
---|
805 | 1136 | &ep->dl_pipe_id); |
---|
806 | | - if (status) |
---|
| 1137 | + if (status) { |
---|
| 1138 | + ath10k_dbg(ar, ATH10K_DBG_BOOT, "unsupported HTC service id: %d\n", |
---|
| 1139 | + ep->service_id); |
---|
807 | 1140 | return status; |
---|
| 1141 | + } |
---|
808 | 1142 | |
---|
809 | 1143 | ath10k_dbg(ar, ATH10K_DBG_BOOT, |
---|
810 | 1144 | "boot htc service '%s' ul pipe %d dl pipe %d eid %d ready\n", |
---|
.. | .. |
---|
838 | 1172 | return skb; |
---|
839 | 1173 | } |
---|
840 | 1174 | |
---|
| 1175 | +static void ath10k_htc_pktlog_process_rx(struct ath10k *ar, struct sk_buff *skb) |
---|
| 1176 | +{ |
---|
| 1177 | + trace_ath10k_htt_pktlog(ar, skb->data, skb->len); |
---|
| 1178 | + dev_kfree_skb_any(skb); |
---|
| 1179 | +} |
---|
| 1180 | + |
---|
| 1181 | +static int ath10k_htc_pktlog_connect(struct ath10k *ar) |
---|
| 1182 | +{ |
---|
| 1183 | + struct ath10k_htc_svc_conn_resp conn_resp; |
---|
| 1184 | + struct ath10k_htc_svc_conn_req conn_req; |
---|
| 1185 | + int status; |
---|
| 1186 | + |
---|
| 1187 | + memset(&conn_req, 0, sizeof(conn_req)); |
---|
| 1188 | + memset(&conn_resp, 0, sizeof(conn_resp)); |
---|
| 1189 | + |
---|
| 1190 | + conn_req.ep_ops.ep_tx_complete = NULL; |
---|
| 1191 | + conn_req.ep_ops.ep_rx_complete = ath10k_htc_pktlog_process_rx; |
---|
| 1192 | + conn_req.ep_ops.ep_tx_credits = NULL; |
---|
| 1193 | + |
---|
| 1194 | + /* connect to control service */ |
---|
| 1195 | + conn_req.service_id = ATH10K_HTC_SVC_ID_HTT_LOG_MSG; |
---|
| 1196 | + status = ath10k_htc_connect_service(&ar->htc, &conn_req, &conn_resp); |
---|
| 1197 | + if (status) { |
---|
| 1198 | + ath10k_warn(ar, "failed to connect to PKTLOG service: %d\n", |
---|
| 1199 | + status); |
---|
| 1200 | + return status; |
---|
| 1201 | + } |
---|
| 1202 | + |
---|
| 1203 | + return 0; |
---|
| 1204 | +} |
---|
| 1205 | + |
---|
| 1206 | +static bool ath10k_htc_pktlog_svc_supported(struct ath10k *ar) |
---|
| 1207 | +{ |
---|
| 1208 | + u8 ul_pipe_id; |
---|
| 1209 | + u8 dl_pipe_id; |
---|
| 1210 | + int status; |
---|
| 1211 | + |
---|
| 1212 | + status = ath10k_hif_map_service_to_pipe(ar, ATH10K_HTC_SVC_ID_HTT_LOG_MSG, |
---|
| 1213 | + &ul_pipe_id, |
---|
| 1214 | + &dl_pipe_id); |
---|
| 1215 | + if (status) { |
---|
| 1216 | + ath10k_dbg(ar, ATH10K_DBG_BOOT, "unsupported HTC pktlog service id: %d\n", |
---|
| 1217 | + ATH10K_HTC_SVC_ID_HTT_LOG_MSG); |
---|
| 1218 | + |
---|
| 1219 | + return false; |
---|
| 1220 | + } |
---|
| 1221 | + |
---|
| 1222 | + return true; |
---|
| 1223 | +} |
---|
| 1224 | + |
---|
841 | 1225 | int ath10k_htc_start(struct ath10k_htc *htc) |
---|
842 | 1226 | { |
---|
843 | 1227 | struct ath10k *ar = htc->ar; |
---|
.. | .. |
---|
871 | 1255 | return status; |
---|
872 | 1256 | } |
---|
873 | 1257 | |
---|
| 1258 | + if (ath10k_htc_pktlog_svc_supported(ar)) { |
---|
| 1259 | + status = ath10k_htc_pktlog_connect(ar); |
---|
| 1260 | + if (status) { |
---|
| 1261 | + ath10k_err(ar, "failed to connect to pktlog: %d\n", status); |
---|
| 1262 | + return status; |
---|
| 1263 | + } |
---|
| 1264 | + } |
---|
| 1265 | + |
---|
874 | 1266 | return 0; |
---|
875 | 1267 | } |
---|
876 | 1268 | |
---|