aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorKalle Valo <kvalo@qca.qualcomm.com>2012-03-25 10:15:28 -0400
committerKalle Valo <kvalo@qca.qualcomm.com>2012-03-26 09:36:46 -0400
commit636f828844fad9421ea6e7df053bba995febdecf (patch)
tree48686f3155d6f7640ab3e975bf57612e7b02f52e /drivers/net
parente76ac2bf637defbe3b7fc644813be584b941ff0a (diff)
ath6kl: Add HTC pipe implementation
This is needed for USB. Based on code by Kevin Fang. Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/wireless/ath/ath6kl/Makefile1
-rw-r--r--drivers/net/wireless/ath/ath6kl/core.c15
-rw-r--r--drivers/net/wireless/ath/ath6kl/core.h4
-rw-r--r--drivers/net/wireless/ath/ath6kl/hif-ops.h34
-rw-r--r--drivers/net/wireless/ath/ath6kl/hif.h6
-rw-r--r--drivers/net/wireless/ath/ath6kl/htc.h35
-rw-r--r--drivers/net/wireless/ath/ath6kl/htc_pipe.c1713
7 files changed, 1808 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/ath6kl/Makefile b/drivers/net/wireless/ath/ath6kl/Makefile
index f4ac8174b24c..8cae8886f17d 100644
--- a/drivers/net/wireless/ath/ath6kl/Makefile
+++ b/drivers/net/wireless/ath/ath6kl/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_ATH6KL) += ath6kl_core.o
26ath6kl_core-y += debug.o 26ath6kl_core-y += debug.o
27ath6kl_core-y += hif.o 27ath6kl_core-y += hif.o
28ath6kl_core-y += htc_mbox.o 28ath6kl_core-y += htc_mbox.o
29ath6kl_core-y += htc_pipe.o
29ath6kl_core-y += bmi.o 30ath6kl_core-y += bmi.o
30ath6kl_core-y += cfg80211.o 31ath6kl_core-y += cfg80211.o
31ath6kl_core-y += init.o 32ath6kl_core-y += init.o
diff --git a/drivers/net/wireless/ath/ath6kl/core.c b/drivers/net/wireless/ath/ath6kl/core.c
index bb9fe381c3c6..5c20a043a470 100644
--- a/drivers/net/wireless/ath/ath6kl/core.c
+++ b/drivers/net/wireless/ath/ath6kl/core.c
@@ -40,6 +40,18 @@ module_param(uart_debug, uint, 0644);
40module_param(ath6kl_p2p, uint, 0644); 40module_param(ath6kl_p2p, uint, 0644);
41module_param(testmode, uint, 0644); 41module_param(testmode, uint, 0644);
42 42
43void ath6kl_core_tx_complete(struct ath6kl *ar, struct sk_buff *skb)
44{
45 ath6kl_htc_tx_complete(ar, skb);
46}
47EXPORT_SYMBOL(ath6kl_core_tx_complete);
48
49void ath6kl_core_rx_complete(struct ath6kl *ar, struct sk_buff *skb, u8 pipe)
50{
51 ath6kl_htc_rx_complete(ar, skb, pipe);
52}
53EXPORT_SYMBOL(ath6kl_core_rx_complete);
54
43int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type) 55int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type)
44{ 56{
45 struct ath6kl_bmi_target_info targ_info; 57 struct ath6kl_bmi_target_info targ_info;
@@ -50,6 +62,9 @@ int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type)
50 case ATH6KL_HTC_TYPE_MBOX: 62 case ATH6KL_HTC_TYPE_MBOX:
51 ath6kl_htc_mbox_attach(ar); 63 ath6kl_htc_mbox_attach(ar);
52 break; 64 break;
65 case ATH6KL_HTC_TYPE_PIPE:
66 ath6kl_htc_pipe_attach(ar);
67 break;
53 default: 68 default:
54 WARN_ON(1); 69 WARN_ON(1);
55 return -ENOMEM; 70 return -ENOMEM;
diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h
index f71c3a7a5e72..75b1d864090a 100644
--- a/drivers/net/wireless/ath/ath6kl/core.h
+++ b/drivers/net/wireless/ath/ath6kl/core.h
@@ -464,6 +464,7 @@ enum ath6kl_hif_type {
464 464
465enum ath6kl_htc_type { 465enum ath6kl_htc_type {
466 ATH6KL_HTC_TYPE_MBOX, 466 ATH6KL_HTC_TYPE_MBOX,
467 ATH6KL_HTC_TYPE_PIPE,
467}; 468};
468 469
469/* Max number of filters that hw supports */ 470/* Max number of filters that hw supports */
@@ -835,6 +836,9 @@ int ath6kl_init_hw_params(struct ath6kl *ar);
835 836
836void ath6kl_check_wow_status(struct ath6kl *ar); 837void ath6kl_check_wow_status(struct ath6kl *ar);
837 838
839void ath6kl_core_tx_complete(struct ath6kl *ar, struct sk_buff *skb);
840void ath6kl_core_rx_complete(struct ath6kl *ar, struct sk_buff *skb, u8 pipe);
841
838struct ath6kl *ath6kl_core_create(struct device *dev); 842struct ath6kl *ath6kl_core_create(struct device *dev);
839int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type); 843int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type);
840void ath6kl_core_cleanup(struct ath6kl *ar); 844void ath6kl_core_cleanup(struct ath6kl *ar);
diff --git a/drivers/net/wireless/ath/ath6kl/hif-ops.h b/drivers/net/wireless/ath/ath6kl/hif-ops.h
index fd84086638e3..8c9e72d5250d 100644
--- a/drivers/net/wireless/ath/ath6kl/hif-ops.h
+++ b/drivers/net/wireless/ath/ath6kl/hif-ops.h
@@ -150,4 +150,38 @@ static inline void ath6kl_hif_stop(struct ath6kl *ar)
150 ar->hif_ops->stop(ar); 150 ar->hif_ops->stop(ar);
151} 151}
152 152
153static inline int ath6kl_hif_pipe_send(struct ath6kl *ar,
154 u8 pipe, struct sk_buff *hdr_buf,
155 struct sk_buff *buf)
156{
157 ath6kl_dbg(ATH6KL_DBG_HIF, "hif pipe send\n");
158
159 return ar->hif_ops->pipe_send(ar, pipe, hdr_buf, buf);
160}
161
162static inline void ath6kl_hif_pipe_get_default(struct ath6kl *ar,
163 u8 *ul_pipe, u8 *dl_pipe)
164{
165 ath6kl_dbg(ATH6KL_DBG_HIF, "hif pipe get default\n");
166
167 ar->hif_ops->pipe_get_default(ar, ul_pipe, dl_pipe);
168}
169
170static inline int ath6kl_hif_pipe_map_service(struct ath6kl *ar,
171 u16 service_id, u8 *ul_pipe,
172 u8 *dl_pipe)
173{
174 ath6kl_dbg(ATH6KL_DBG_HIF, "hif pipe get default\n");
175
176 return ar->hif_ops->pipe_map_service(ar, service_id, ul_pipe, dl_pipe);
177}
178
179static inline u16 ath6kl_hif_pipe_get_free_queue_number(struct ath6kl *ar,
180 u8 pipe)
181{
182 ath6kl_dbg(ATH6KL_DBG_HIF, "hif pipe get free queue number\n");
183
184 return ar->hif_ops->pipe_get_free_queue_number(ar, pipe);
185}
186
153#endif 187#endif
diff --git a/drivers/net/wireless/ath/ath6kl/hif.h b/drivers/net/wireless/ath/ath6kl/hif.h
index 20ed6b73517b..61f6b21fb0ae 100644
--- a/drivers/net/wireless/ath/ath6kl/hif.h
+++ b/drivers/net/wireless/ath/ath6kl/hif.h
@@ -256,6 +256,12 @@ struct ath6kl_hif_ops {
256 int (*power_on)(struct ath6kl *ar); 256 int (*power_on)(struct ath6kl *ar);
257 int (*power_off)(struct ath6kl *ar); 257 int (*power_off)(struct ath6kl *ar);
258 void (*stop)(struct ath6kl *ar); 258 void (*stop)(struct ath6kl *ar);
259 int (*pipe_send)(struct ath6kl *ar, u8 pipe, struct sk_buff *hdr_buf,
260 struct sk_buff *buf);
261 void (*pipe_get_default)(struct ath6kl *ar, u8 *pipe_ul, u8 *pipe_dl);
262 int (*pipe_map_service)(struct ath6kl *ar, u16 service_id, u8 *pipe_ul,
263 u8 *pipe_dl);
264 u16 (*pipe_get_free_queue_number)(struct ath6kl *ar, u8 pipe);
259}; 265};
260 266
261int ath6kl_hif_setup(struct ath6kl_device *dev); 267int ath6kl_hif_setup(struct ath6kl_device *dev);
diff --git a/drivers/net/wireless/ath/ath6kl/htc.h b/drivers/net/wireless/ath/ath6kl/htc.h
index 43cb2cf270d6..a2c8ff809793 100644
--- a/drivers/net/wireless/ath/ath6kl/htc.h
+++ b/drivers/net/wireless/ath/ath6kl/htc.h
@@ -25,6 +25,7 @@
25/* send direction */ 25/* send direction */
26#define HTC_FLAGS_NEED_CREDIT_UPDATE (1 << 0) 26#define HTC_FLAGS_NEED_CREDIT_UPDATE (1 << 0)
27#define HTC_FLAGS_SEND_BUNDLE (1 << 1) 27#define HTC_FLAGS_SEND_BUNDLE (1 << 1)
28#define HTC_FLAGS_TX_FIXUP_NETBUF (1 << 2)
28 29
29/* receive direction */ 30/* receive direction */
30#define HTC_FLG_RX_UNUSED (1 << 0) 31#define HTC_FLG_RX_UNUSED (1 << 0)
@@ -56,6 +57,10 @@
56#define HTC_CONN_FLGS_THRESH_LVL_THREE_QUAT 0x2 57#define HTC_CONN_FLGS_THRESH_LVL_THREE_QUAT 0x2
57#define HTC_CONN_FLGS_REDUCE_CRED_DRIB 0x4 58#define HTC_CONN_FLGS_REDUCE_CRED_DRIB 0x4
58#define HTC_CONN_FLGS_THRESH_MASK 0x3 59#define HTC_CONN_FLGS_THRESH_MASK 0x3
60/* disable credit flow control on a specific service */
61#define HTC_CONN_FLGS_DISABLE_CRED_FLOW_CTRL (1 << 3)
62#define HTC_CONN_FLGS_SET_RECV_ALLOC_SHIFT 8
63#define HTC_CONN_FLGS_SET_RECV_ALLOC_MASK 0xFF00
59 64
60/* connect response status codes */ 65/* connect response status codes */
61#define HTC_SERVICE_SUCCESS 0 66#define HTC_SERVICE_SUCCESS 0
@@ -75,6 +80,7 @@
75#define HTC_RECORD_LOOKAHEAD_BUNDLE 3 80#define HTC_RECORD_LOOKAHEAD_BUNDLE 3
76 81
77#define HTC_SETUP_COMP_FLG_RX_BNDL_EN (1 << 0) 82#define HTC_SETUP_COMP_FLG_RX_BNDL_EN (1 << 0)
83#define HTC_SETUP_COMP_FLG_DISABLE_TX_CREDIT_FLOW (1 << 1)
78 84
79#define MAKE_SERVICE_ID(group, index) \ 85#define MAKE_SERVICE_ID(group, index) \
80 (int)(((int)group << 8) | (int)(index)) 86 (int)(((int)group << 8) | (int)(index))
@@ -109,6 +115,8 @@
109 115
110/* HTC operational parameters */ 116/* HTC operational parameters */
111#define HTC_TARGET_RESPONSE_TIMEOUT 2000 /* in ms */ 117#define HTC_TARGET_RESPONSE_TIMEOUT 2000 /* in ms */
118#define HTC_TARGET_RESPONSE_POLL_WAIT 10
119#define HTC_TARGET_RESPONSE_POLL_COUNT 200
112#define HTC_TARGET_DEBUG_INTR_MASK 0x01 120#define HTC_TARGET_DEBUG_INTR_MASK 0x01
113#define HTC_TARGET_CREDIT_INTR_MASK 0xF0 121#define HTC_TARGET_CREDIT_INTR_MASK 0xF0
114 122
@@ -128,6 +136,7 @@
128 136
129#define HTC_RECV_WAIT_BUFFERS (1 << 0) 137#define HTC_RECV_WAIT_BUFFERS (1 << 0)
130#define HTC_OP_STATE_STOPPING (1 << 0) 138#define HTC_OP_STATE_STOPPING (1 << 0)
139#define HTC_OP_STATE_SETUP_COMPLETE (1 << 1)
131 140
132/* 141/*
133 * The frame header length and message formats defined herein were selected 142 * The frame header length and message formats defined herein were selected
@@ -512,6 +521,13 @@ struct htc_endpoint {
512 u32 conn_flags; 521 u32 conn_flags;
513 struct htc_endpoint_stats ep_st; 522 struct htc_endpoint_stats ep_st;
514 u16 tx_drop_packet_threshold; 523 u16 tx_drop_packet_threshold;
524
525 struct {
526 u8 pipeid_ul;
527 u8 pipeid_dl;
528 struct list_head tx_lookup_queue;
529 bool tx_credit_flow_enabled;
530 } pipe;
515}; 531};
516 532
517struct htc_control_buffer { 533struct htc_control_buffer {
@@ -519,6 +535,16 @@ struct htc_control_buffer {
519 u8 *buf; 535 u8 *buf;
520}; 536};
521 537
538struct htc_pipe_txcredit_alloc {
539 u16 service_id;
540 u8 credit_alloc;
541};
542
543enum htc_send_queue_result {
544 HTC_SEND_QUEUE_OK = 0, /* packet was queued */
545 HTC_SEND_QUEUE_DROP = 1, /* this packet should be dropped */
546};
547
522struct ath6kl_htc_ops { 548struct ath6kl_htc_ops {
523 void* (*create)(struct ath6kl *ar); 549 void* (*create)(struct ath6kl *ar);
524 int (*wait_target)(struct htc_target *target); 550 int (*wait_target)(struct htc_target *target);
@@ -593,6 +619,14 @@ struct htc_target {
593 619
594 /* counts the number of Tx without bundling continously per AC */ 620 /* counts the number of Tx without bundling continously per AC */
595 u32 ac_tx_count[WMM_NUM_AC]; 621 u32 ac_tx_count[WMM_NUM_AC];
622
623 struct {
624 struct htc_packet *htc_packet_pool;
625 u8 ctrl_response_buf[HTC_MAX_CTRL_MSG_LEN];
626 int ctrl_response_len;
627 bool ctrl_response_valid;
628 struct htc_pipe_txcredit_alloc txcredit_alloc[ENDPOINT_MAX];
629 } pipe;
596}; 630};
597 631
598int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target, 632int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target,
@@ -637,6 +671,7 @@ static inline int get_queue_depth(struct list_head *queue)
637 return depth; 671 return depth;
638} 672}
639 673
674void ath6kl_htc_pipe_attach(struct ath6kl *ar);
640void ath6kl_htc_mbox_attach(struct ath6kl *ar); 675void ath6kl_htc_mbox_attach(struct ath6kl *ar);
641 676
642#endif 677#endif
diff --git a/drivers/net/wireless/ath/ath6kl/htc_pipe.c b/drivers/net/wireless/ath/ath6kl/htc_pipe.c
new file mode 100644
index 000000000000..b277b3446882
--- /dev/null
+++ b/drivers/net/wireless/ath/ath6kl/htc_pipe.c
@@ -0,0 +1,1713 @@
1/*
2 * Copyright (c) 2007-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#include "core.h"
18#include "debug.h"
19#include "hif-ops.h"
20
21#define HTC_PACKET_CONTAINER_ALLOCATION 32
22#define HTC_CONTROL_BUFFER_SIZE (HTC_MAX_CTRL_MSG_LEN + HTC_HDR_LENGTH)
23
24static int ath6kl_htc_pipe_tx(struct htc_target *handle,
25 struct htc_packet *packet);
26static void ath6kl_htc_pipe_cleanup(struct htc_target *handle);
27
28/* htc pipe tx path */
29static inline void restore_tx_packet(struct htc_packet *packet)
30{
31 if (packet->info.tx.flags & HTC_FLAGS_TX_FIXUP_NETBUF) {
32 skb_pull(packet->skb, sizeof(struct htc_frame_hdr));
33 packet->info.tx.flags &= ~HTC_FLAGS_TX_FIXUP_NETBUF;
34 }
35}
36
37static void do_send_completion(struct htc_endpoint *ep,
38 struct list_head *queue_to_indicate)
39{
40 struct htc_packet *packet;
41
42 if (list_empty(queue_to_indicate)) {
43 /* nothing to indicate */
44 return;
45 }
46
47 if (ep->ep_cb.tx_comp_multi != NULL) {
48 ath6kl_dbg(ATH6KL_DBG_HTC,
49 "%s: calling ep %d, send complete multiple callback (%d pkts)\n",
50 __func__, ep->eid,
51 get_queue_depth(queue_to_indicate));
52 /*
53 * a multiple send complete handler is being used,
54 * pass the queue to the handler
55 */
56 ep->ep_cb.tx_comp_multi(ep->target, queue_to_indicate);
57 /*
58 * all packets are now owned by the callback,
59 * reset queue to be safe
60 */
61 INIT_LIST_HEAD(queue_to_indicate);
62 } else {
63 /* using legacy EpTxComplete */
64 do {
65 packet = list_first_entry(queue_to_indicate,
66 struct htc_packet, list);
67
68 list_del(&packet->list);
69 ath6kl_dbg(ATH6KL_DBG_HTC,
70 "%s: calling ep %d send complete callback on packet 0x%p\n",
71 __func__, ep->eid, packet);
72 ep->ep_cb.tx_complete(ep->target, packet);
73 } while (!list_empty(queue_to_indicate));
74 }
75}
76
77static void send_packet_completion(struct htc_target *target,
78 struct htc_packet *packet)
79{
80 struct htc_endpoint *ep = &target->endpoint[packet->endpoint];
81 struct list_head container;
82
83 restore_tx_packet(packet);
84 INIT_LIST_HEAD(&container);
85 list_add_tail(&packet->list, &container);
86
87 /* do completion */
88 do_send_completion(ep, &container);
89}
90
91static void get_htc_packet_credit_based(struct htc_target *target,
92 struct htc_endpoint *ep,
93 struct list_head *queue)
94{
95 int credits_required;
96 int remainder;
97 u8 send_flags;
98 struct htc_packet *packet;
99 unsigned int transfer_len;
100
101 /* NOTE : the TX lock is held when this function is called */
102
103 /* loop until we can grab as many packets out of the queue as we can */
104 while (true) {
105 send_flags = 0;
106 if (list_empty(&ep->txq))
107 break;
108
109 /* get packet at head, but don't remove it */
110 packet = list_first_entry(&ep->txq, struct htc_packet, list);
111 if (packet == NULL)
112 break;
113
114 ath6kl_dbg(ATH6KL_DBG_HTC,
115 "%s: got head packet:0x%p , queue depth: %d\n",
116 __func__, packet, get_queue_depth(&ep->txq));
117
118 transfer_len = packet->act_len + HTC_HDR_LENGTH;
119
120 if (transfer_len <= target->tgt_cred_sz) {
121 credits_required = 1;
122 } else {
123 /* figure out how many credits this message requires */
124 credits_required = transfer_len / target->tgt_cred_sz;
125 remainder = transfer_len % target->tgt_cred_sz;
126
127 if (remainder)
128 credits_required++;
129 }
130
131 ath6kl_dbg(ATH6KL_DBG_HTC, "%s: creds required:%d got:%d\n",
132 __func__, credits_required, ep->cred_dist.credits);
133
134 if (ep->eid == ENDPOINT_0) {
135 /*
136 * endpoint 0 is special, it always has a credit and
137 * does not require credit based flow control
138 */
139 credits_required = 0;
140
141 } else {
142
143 if (ep->cred_dist.credits < credits_required)
144 break;
145
146 ep->cred_dist.credits -= credits_required;
147 ep->ep_st.cred_cosumd += credits_required;
148
149 /* check if we need credits back from the target */
150 if (ep->cred_dist.credits <
151 ep->cred_dist.cred_per_msg) {
152 /* tell the target we need credits ASAP! */
153 send_flags |= HTC_FLAGS_NEED_CREDIT_UPDATE;
154 ep->ep_st.cred_low_indicate += 1;
155 ath6kl_dbg(ATH6KL_DBG_HTC,
156 "%s: host needs credits\n",
157 __func__);
158 }
159 }
160
161 /* now we can fully dequeue */
162 packet = list_first_entry(&ep->txq, struct htc_packet, list);
163
164 list_del(&packet->list);
165 /* save the number of credits this packet consumed */
166 packet->info.tx.cred_used = credits_required;
167 /* save send flags */
168 packet->info.tx.flags = send_flags;
169 packet->info.tx.seqno = ep->seqno;
170 ep->seqno++;
171 /* queue this packet into the caller's queue */
172 list_add_tail(&packet->list, queue);
173 }
174
175}
176
177static void get_htc_packet(struct htc_target *target,
178 struct htc_endpoint *ep,
179 struct list_head *queue, int resources)
180{
181 struct htc_packet *packet;
182
183 /* NOTE : the TX lock is held when this function is called */
184
185 /* loop until we can grab as many packets out of the queue as we can */
186 while (resources) {
187 if (list_empty(&ep->txq))
188 break;
189
190 packet = list_first_entry(&ep->txq, struct htc_packet, list);
191 list_del(&packet->list);
192
193 ath6kl_dbg(ATH6KL_DBG_HTC,
194 "%s: got packet:0x%p , new queue depth: %d\n",
195 __func__, packet, get_queue_depth(&ep->txq));
196 packet->info.tx.seqno = ep->seqno;
197 packet->info.tx.flags = 0;
198 packet->info.tx.cred_used = 0;
199 ep->seqno++;
200
201 /* queue this packet into the caller's queue */
202 list_add_tail(&packet->list, queue);
203 resources--;
204 }
205}
206
207static int htc_issue_packets(struct htc_target *target,
208 struct htc_endpoint *ep,
209 struct list_head *pkt_queue)
210{
211 int status = 0;
212 u16 payload_len;
213 struct sk_buff *skb;
214 struct htc_frame_hdr *htc_hdr;
215 struct htc_packet *packet;
216
217 ath6kl_dbg(ATH6KL_DBG_HTC,
218 "%s: queue: 0x%p, pkts %d\n", __func__,
219 pkt_queue, get_queue_depth(pkt_queue));
220
221 while (!list_empty(pkt_queue)) {
222 packet = list_first_entry(pkt_queue, struct htc_packet, list);
223 list_del(&packet->list);
224
225 skb = packet->skb;
226 if (!skb) {
227 WARN_ON_ONCE(1);
228 status = -EINVAL;
229 break;
230 }
231
232 payload_len = packet->act_len;
233
234 /* setup HTC frame header */
235 htc_hdr = (struct htc_frame_hdr *) skb_push(skb,
236 sizeof(*htc_hdr));
237 if (!htc_hdr) {
238 WARN_ON_ONCE(1);
239 status = -EINVAL;
240 break;
241 }
242
243 packet->info.tx.flags |= HTC_FLAGS_TX_FIXUP_NETBUF;
244
245 /* Endianess? */
246 put_unaligned((u16) payload_len, &htc_hdr->payld_len);
247 htc_hdr->flags = packet->info.tx.flags;
248 htc_hdr->eid = (u8) packet->endpoint;
249 htc_hdr->ctrl[0] = 0;
250 htc_hdr->ctrl[1] = (u8) packet->info.tx.seqno;
251
252 spin_lock_bh(&target->tx_lock);
253
254 /* store in look up queue to match completions */
255 list_add_tail(&packet->list, &ep->pipe.tx_lookup_queue);
256 ep->ep_st.tx_issued += 1;
257 spin_unlock_bh(&target->tx_lock);
258
259 status = ath6kl_hif_pipe_send(target->dev->ar,
260 ep->pipe.pipeid_ul, NULL, skb);
261
262 if (status != 0) {
263 if (status != -ENOMEM) {
264 /* TODO: if more than 1 endpoint maps to the
265 * same PipeID, it is possible to run out of
266 * resources in the HIF layer.
267 * Don't emit the error
268 */
269 ath6kl_dbg(ATH6KL_DBG_HTC,
270 "%s: failed status:%d\n",
271 __func__, status);
272 }
273 spin_lock_bh(&target->tx_lock);
274 list_del(&packet->list);
275
276 /* reclaim credits */
277 ep->cred_dist.credits += packet->info.tx.cred_used;
278 spin_unlock_bh(&target->tx_lock);
279
280 /* put it back into the callers queue */
281 list_add(&packet->list, pkt_queue);
282 break;
283 }
284
285 }
286
287 if (status != 0) {
288 while (!list_empty(pkt_queue)) {
289 if (status != -ENOMEM) {
290 ath6kl_dbg(ATH6KL_DBG_HTC,
291 "%s: failed pkt:0x%p status:%d\n",
292 __func__, packet, status);
293 }
294
295 packet = list_first_entry(pkt_queue,
296 struct htc_packet, list);
297 list_del(&packet->list);
298 packet->status = status;
299 send_packet_completion(target, packet);
300 }
301 }
302
303 return status;
304}
305
306static enum htc_send_queue_result htc_try_send(struct htc_target *target,
307 struct htc_endpoint *ep,
308 struct list_head *txq)
309{
310 struct list_head send_queue; /* temp queue to hold packets */
311 struct htc_packet *packet, *tmp_pkt;
312 struct ath6kl *ar = target->dev->ar;
313 enum htc_send_full_action action;
314 int tx_resources, overflow, txqueue_depth, i, good_pkts;
315 u8 pipeid;
316
317 ath6kl_dbg(ATH6KL_DBG_HTC, "%s: (queue:0x%p depth:%d)\n",
318 __func__, txq,
319 (txq == NULL) ? 0 : get_queue_depth(txq));
320
321 /* init the local send queue */
322 INIT_LIST_HEAD(&send_queue);
323
324 /*
325 * txq equals to NULL means
326 * caller didn't provide a queue, just wants us to
327 * check queues and send
328 */
329 if (txq != NULL) {
330 if (list_empty(txq)) {
331 /* empty queue */
332 return HTC_SEND_QUEUE_DROP;
333 }
334
335 spin_lock_bh(&target->tx_lock);
336 txqueue_depth = get_queue_depth(&ep->txq);
337 spin_unlock_bh(&target->tx_lock);
338
339 if (txqueue_depth >= ep->max_txq_depth) {
340 /* we've already overflowed */
341 overflow = get_queue_depth(txq);
342 } else {
343 /* get how much we will overflow by */
344 overflow = txqueue_depth;
345 overflow += get_queue_depth(txq);
346 /* get how much we will overflow the TX queue by */
347 overflow -= ep->max_txq_depth;
348 }
349
350 /* if overflow is negative or zero, we are okay */
351 if (overflow > 0) {
352 ath6kl_dbg(ATH6KL_DBG_HTC,
353 "%s: Endpoint %d, TX queue will overflow :%d, Tx Depth:%d, Max:%d\n",
354 __func__, ep->eid, overflow, txqueue_depth,
355 ep->max_txq_depth);
356 }
357 if ((overflow <= 0) ||
358 (ep->ep_cb.tx_full == NULL)) {
359 /*
360 * all packets will fit or caller did not provide send
361 * full indication handler -- just move all of them
362 * to the local send_queue object
363 */
364 list_splice_tail_init(txq, &send_queue);
365 } else {
366 good_pkts = get_queue_depth(txq) - overflow;
367 if (good_pkts < 0) {
368 WARN_ON_ONCE(1);
369 return HTC_SEND_QUEUE_DROP;
370 }
371
372 /* we have overflowed, and a callback is provided */
373 /* dequeue all non-overflow packets to the sendqueue */
374 for (i = 0; i < good_pkts; i++) {
375 /* pop off caller's queue */
376 packet = list_first_entry(txq,
377 struct htc_packet,
378 list);
379 list_del(&packet->list);
380 /* insert into local queue */
381 list_add_tail(&packet->list, &send_queue);
382 }
383
384 /*
385 * the caller's queue has all the packets that won't fit
386 * walk through the caller's queue and indicate each to
387 * the send full handler
388 */
389 list_for_each_entry_safe(packet, tmp_pkt,
390 txq, list) {
391
392 ath6kl_dbg(ATH6KL_DBG_HTC,
393 "%s: Indicat overflowed TX pkts: %p\n",
394 __func__, packet);
395 action = ep->ep_cb.tx_full(ep->target, packet);
396 if (action == HTC_SEND_FULL_DROP) {
397 /* callback wants the packet dropped */
398 ep->ep_st.tx_dropped += 1;
399
400 /* leave this one in the caller's queue
401 * for cleanup */
402 } else {
403 /* callback wants to keep this packet,
404 * remove from caller's queue */
405 list_del(&packet->list);
406 /* put it in the send queue */
407 list_add_tail(&packet->list,
408 &send_queue);
409 }
410
411 }
412
413 if (list_empty(&send_queue)) {
414 /* no packets made it in, caller will cleanup */
415 return HTC_SEND_QUEUE_DROP;
416 }
417 }
418 }
419
420 if (!ep->pipe.tx_credit_flow_enabled) {
421 tx_resources =
422 ath6kl_hif_pipe_get_free_queue_number(ar,
423 ep->pipe.pipeid_ul);
424 } else {
425 tx_resources = 0;
426 }
427
428 spin_lock_bh(&target->tx_lock);
429 if (!list_empty(&send_queue)) {
430 /* transfer packets to tail */
431 list_splice_tail_init(&send_queue, &ep->txq);
432 if (!list_empty(&send_queue)) {
433 WARN_ON_ONCE(1);
434 spin_unlock_bh(&target->tx_lock);
435 return HTC_SEND_QUEUE_DROP;
436 }
437 INIT_LIST_HEAD(&send_queue);
438 }
439
440 /* increment tx processing count on entry */
441 ep->tx_proc_cnt++;
442
443 if (ep->tx_proc_cnt > 1) {
444 /*
445 * Another thread or task is draining the TX queues on this
446 * endpoint that thread will reset the tx processing count
447 * when the queue is drained.
448 */
449 ep->tx_proc_cnt--;
450 spin_unlock_bh(&target->tx_lock);
451 return HTC_SEND_QUEUE_OK;
452 }
453
454 /***** beyond this point only 1 thread may enter ******/
455
456 /*
457 * Now drain the endpoint TX queue for transmission as long as we have
458 * enough transmit resources.
459 */
460 while (true) {
461
462 if (get_queue_depth(&ep->txq) == 0)
463 break;
464
465 if (ep->pipe.tx_credit_flow_enabled) {
466 /*
467 * Credit based mechanism provides flow control
468 * based on target transmit resource availability,
469 * we assume that the HIF layer will always have
470 * bus resources greater than target transmit
471 * resources.
472 */
473 get_htc_packet_credit_based(target, ep, &send_queue);
474 } else {
475 /*
476 * Get all packets for this endpoint that we can
477 * for this pass.
478 */
479 get_htc_packet(target, ep, &send_queue, tx_resources);
480 }
481
482 if (get_queue_depth(&send_queue) == 0) {
483 /*
484 * Didn't get packets due to out of resources or TX
485 * queue was drained.
486 */
487 break;
488 }
489
490 spin_unlock_bh(&target->tx_lock);
491
492 /* send what we can */
493 htc_issue_packets(target, ep, &send_queue);
494
495 if (!ep->pipe.tx_credit_flow_enabled) {
496 pipeid = ep->pipe.pipeid_ul;
497 tx_resources =
498 ath6kl_hif_pipe_get_free_queue_number(ar, pipeid);
499 }
500
501 spin_lock_bh(&target->tx_lock);
502
503 }
504 /* done with this endpoint, we can clear the count */
505 ep->tx_proc_cnt = 0;
506 spin_unlock_bh(&target->tx_lock);
507
508 return HTC_SEND_QUEUE_OK;
509}
510
511/* htc control packet manipulation */
512static void destroy_htc_txctrl_packet(struct htc_packet *packet)
513{
514 struct sk_buff *skb;
515 skb = packet->skb;
516 if (skb != NULL)
517 dev_kfree_skb(skb);
518
519 kfree(packet);
520}
521
522static struct htc_packet *build_htc_txctrl_packet(void)
523{
524 struct htc_packet *packet = NULL;
525 struct sk_buff *skb;
526
527 packet = kzalloc(sizeof(struct htc_packet), GFP_KERNEL);
528 if (packet == NULL)
529 return NULL;
530
531 skb = __dev_alloc_skb(HTC_CONTROL_BUFFER_SIZE, GFP_KERNEL);
532
533 if (skb == NULL) {
534 kfree(packet);
535 return NULL;
536 }
537 packet->skb = skb;
538
539 return packet;
540}
541
542static void htc_free_txctrl_packet(struct htc_target *target,
543 struct htc_packet *packet)
544{
545 destroy_htc_txctrl_packet(packet);
546}
547
548static struct htc_packet *htc_alloc_txctrl_packet(struct htc_target *target)
549{
550 return build_htc_txctrl_packet();
551}
552
553static void htc_txctrl_complete(struct htc_target *target,
554 struct htc_packet *packet)
555{
556 htc_free_txctrl_packet(target, packet);
557}
558
559#define MAX_MESSAGE_SIZE 1536
560
561static int htc_setup_target_buffer_assignments(struct htc_target *target)
562{
563 int status, credits, credit_per_maxmsg, i;
564 struct htc_pipe_txcredit_alloc *entry;
565 unsigned int hif_usbaudioclass = 0;
566
567 credit_per_maxmsg = MAX_MESSAGE_SIZE / target->tgt_cred_sz;
568 if (MAX_MESSAGE_SIZE % target->tgt_cred_sz)
569 credit_per_maxmsg++;
570
571 /* TODO, this should be configured by the caller! */
572
573 credits = target->tgt_creds;
574 entry = &target->pipe.txcredit_alloc[0];
575
576 status = -ENOMEM;
577
578 /* FIXME: hif_usbaudioclass is always zero */
579 if (hif_usbaudioclass) {
580 ath6kl_dbg(ATH6KL_DBG_HTC,
581 "%s: For USB Audio Class- Total:%d\n",
582 __func__, credits);
583 entry++;
584 entry++;
585 /* Setup VO Service To have Max Credits */
586 entry->service_id = WMI_DATA_VO_SVC;
587 entry->credit_alloc = (credits - 6);
588 if (entry->credit_alloc == 0)
589 entry->credit_alloc++;
590
591 credits -= (int) entry->credit_alloc;
592 if (credits <= 0)
593 return status;
594
595 entry++;
596 entry->service_id = WMI_CONTROL_SVC;
597 entry->credit_alloc = credit_per_maxmsg;
598 credits -= (int) entry->credit_alloc;
599 if (credits <= 0)
600 return status;
601
602 /* leftovers go to best effort */
603 entry++;
604 entry++;
605 entry->service_id = WMI_DATA_BE_SVC;
606 entry->credit_alloc = (u8) credits;
607 status = 0;
608 } else {
609 entry++;
610 entry->service_id = WMI_DATA_VI_SVC;
611 entry->credit_alloc = credits / 4;
612 if (entry->credit_alloc == 0)
613 entry->credit_alloc++;
614
615 credits -= (int) entry->credit_alloc;
616 if (credits <= 0)
617 return status;
618
619 entry++;
620 entry->service_id = WMI_DATA_VO_SVC;
621 entry->credit_alloc = credits / 4;
622 if (entry->credit_alloc == 0)
623 entry->credit_alloc++;
624
625 credits -= (int) entry->credit_alloc;
626 if (credits <= 0)
627 return status;
628
629 entry++;
630 entry->service_id = WMI_CONTROL_SVC;
631 entry->credit_alloc = credit_per_maxmsg;
632 credits -= (int) entry->credit_alloc;
633 if (credits <= 0)
634 return status;
635
636 entry++;
637 entry->service_id = WMI_DATA_BK_SVC;
638 entry->credit_alloc = credit_per_maxmsg;
639 credits -= (int) entry->credit_alloc;
640 if (credits <= 0)
641 return status;
642
643 /* leftovers go to best effort */
644 entry++;
645 entry->service_id = WMI_DATA_BE_SVC;
646 entry->credit_alloc = (u8) credits;
647 status = 0;
648 }
649
650 if (status == 0) {
651 for (i = 0; i < ENDPOINT_MAX; i++) {
652 if (target->pipe.txcredit_alloc[i].service_id != 0) {
653 ath6kl_dbg(ATH6KL_DBG_HTC,
654 "HTC Service Index : %d TX : 0x%2.2X : alloc:%d\n",
655 i,
656 target->pipe.txcredit_alloc[i].
657 service_id,
658 target->pipe.txcredit_alloc[i].
659 credit_alloc);
660 }
661 }
662 }
663 return status;
664}
665
666/* process credit reports and call distribution function */
667static void htc_process_credit_report(struct htc_target *target,
668 struct htc_credit_report *rpt,
669 int num_entries,
670 enum htc_endpoint_id from_ep)
671{
672 int total_credits = 0, i;
673 struct htc_endpoint *ep;
674
675 /* lock out TX while we update credits */
676 spin_lock_bh(&target->tx_lock);
677
678 for (i = 0; i < num_entries; i++, rpt++) {
679 if (rpt->eid >= ENDPOINT_MAX) {
680 WARN_ON_ONCE(1);
681 spin_unlock_bh(&target->tx_lock);
682 return;
683 }
684
685 ep = &target->endpoint[rpt->eid];
686 ep->cred_dist.credits += rpt->credits;
687
688 if (ep->cred_dist.credits && get_queue_depth(&ep->txq)) {
689 spin_unlock_bh(&target->tx_lock);
690 htc_try_send(target, ep, NULL);
691 spin_lock_bh(&target->tx_lock);
692 }
693
694 total_credits += rpt->credits;
695 }
696 ath6kl_dbg(ATH6KL_DBG_HTC,
697 "Report indicated %d credits to distribute\n",
698 total_credits);
699
700 spin_unlock_bh(&target->tx_lock);
701}
702
703/* flush endpoint TX queue */
704static void htc_flush_tx_endpoint(struct htc_target *target,
705 struct htc_endpoint *ep, u16 tag)
706{
707 struct htc_packet *packet;
708
709 spin_lock_bh(&target->tx_lock);
710 while (get_queue_depth(&ep->txq)) {
711 packet = list_first_entry(&ep->txq, struct htc_packet, list);
712 list_del(&packet->list);
713 packet->status = 0;
714 send_packet_completion(target, packet);
715 }
716 spin_unlock_bh(&target->tx_lock);
717}
718
719/*
720 * In the adapted HIF layer, struct sk_buff * are passed between HIF and HTC,
721 * since upper layers expects struct htc_packet containers we use the completed
722 * skb and lookup it's corresponding HTC packet buffer from a lookup list.
723 * This is extra overhead that can be fixed by re-aligning HIF interfaces with
724 * HTC.
725 */
726static struct htc_packet *htc_lookup_tx_packet(struct htc_target *target,
727 struct htc_endpoint *ep,
728 struct sk_buff *skb)
729{
730 struct htc_packet *packet, *tmp_pkt, *found_packet = NULL;
731
732 spin_lock_bh(&target->tx_lock);
733
734 /*
735 * interate from the front of tx lookup queue
736 * this lookup should be fast since lower layers completes in-order and
737 * so the completed packet should be at the head of the list generally
738 */
739 list_for_each_entry_safe(packet, tmp_pkt, &ep->pipe.tx_lookup_queue,
740 list) {
741 /* check for removal */
742 if (skb == packet->skb) {
743 /* found it */
744 list_del(&packet->list);
745 found_packet = packet;
746 break;
747 }
748 }
749
750 spin_unlock_bh(&target->tx_lock);
751
752 return found_packet;
753}
754
755static int ath6kl_htc_pipe_tx_complete(struct ath6kl *ar, struct sk_buff *skb)
756{
757 struct htc_target *target = ar->htc_target;
758 struct htc_frame_hdr *htc_hdr;
759 struct htc_endpoint *ep;
760 struct htc_packet *packet;
761 u8 ep_id, *netdata;
762 u32 netlen;
763
764 netdata = skb->data;
765 netlen = skb->len;
766
767 htc_hdr = (struct htc_frame_hdr *) netdata;
768
769 ep_id = htc_hdr->eid;
770 ep = &target->endpoint[ep_id];
771
772 packet = htc_lookup_tx_packet(target, ep, skb);
773 if (packet == NULL) {
774 /* may have already been flushed and freed */
775 ath6kl_err("HTC TX lookup failed!\n");
776 } else {
777 /* will be giving this buffer back to upper layers */
778 packet->status = 0;
779 send_packet_completion(target, packet);
780 }
781 skb = NULL;
782
783 if (!ep->pipe.tx_credit_flow_enabled) {
784 /*
785 * note: when using TX credit flow, the re-checking of queues
786 * happens when credits flow back from the target. in the
787 * non-TX credit case, we recheck after the packet completes
788 */
789 htc_try_send(target, ep, NULL);
790 }
791
792 return 0;
793}
794
795static int htc_send_packets_multiple(struct htc_target *target,
796 struct list_head *pkt_queue)
797{
798 struct htc_endpoint *ep;
799 struct htc_packet *packet, *tmp_pkt;
800
801 if (list_empty(pkt_queue))
802 return -EINVAL;
803
804 /* get first packet to find out which ep the packets will go into */
805 packet = list_first_entry(pkt_queue, struct htc_packet, list);
806 if (packet == NULL)
807 return -EINVAL;
808
809 if (packet->endpoint >= ENDPOINT_MAX) {
810 WARN_ON_ONCE(1);
811 return -EINVAL;
812 }
813 ep = &target->endpoint[packet->endpoint];
814
815 htc_try_send(target, ep, pkt_queue);
816
817 /* do completion on any packets that couldn't get in */
818 if (!list_empty(pkt_queue)) {
819 list_for_each_entry_safe(packet, tmp_pkt, pkt_queue, list) {
820 packet->status = -ENOMEM;
821 }
822
823 do_send_completion(ep, pkt_queue);
824 }
825
826 return 0;
827}
828
829/* htc pipe rx path */
830static struct htc_packet *alloc_htc_packet_container(struct htc_target *target)
831{
832 struct htc_packet *packet;
833 spin_lock_bh(&target->rx_lock);
834
835 if (target->pipe.htc_packet_pool == NULL) {
836 spin_unlock_bh(&target->rx_lock);
837 return NULL;
838 }
839
840 packet = target->pipe.htc_packet_pool;
841 target->pipe.htc_packet_pool = (struct htc_packet *) packet->list.next;
842
843 spin_unlock_bh(&target->rx_lock);
844
845 packet->list.next = NULL;
846 return packet;
847}
848
849static void free_htc_packet_container(struct htc_target *target,
850 struct htc_packet *packet)
851{
852 struct list_head *lh;
853
854 spin_lock_bh(&target->rx_lock);
855
856 if (target->pipe.htc_packet_pool == NULL) {
857 target->pipe.htc_packet_pool = packet;
858 packet->list.next = NULL;
859 } else {
860 lh = (struct list_head *) target->pipe.htc_packet_pool;
861 packet->list.next = lh;
862 target->pipe.htc_packet_pool = packet;
863 }
864
865 spin_unlock_bh(&target->rx_lock);
866}
867
868static int htc_process_trailer(struct htc_target *target, u8 *buffer,
869 int len, enum htc_endpoint_id from_ep)
870{
871 struct htc_credit_report *report;
872 struct htc_record_hdr *record;
873 u8 *record_buf, *orig_buf;
874 int orig_len, status;
875
876 orig_buf = buffer;
877 orig_len = len;
878 status = 0;
879
880 while (len > 0) {
881 if (len < sizeof(struct htc_record_hdr)) {
882 status = -EINVAL;
883 break;
884 }
885
886 /* these are byte aligned structs */
887 record = (struct htc_record_hdr *) buffer;
888 len -= sizeof(struct htc_record_hdr);
889 buffer += sizeof(struct htc_record_hdr);
890
891 if (record->len > len) {
892 /* no room left in buffer for record */
893 ath6kl_dbg(ATH6KL_DBG_HTC,
894 "invalid length: %d (id:%d) buffer has: %d bytes left\n",
895 record->len, record->rec_id, len);
896 status = -EINVAL;
897 break;
898 }
899
900 /* start of record follows the header */
901 record_buf = buffer;
902
903 switch (record->rec_id) {
904 case HTC_RECORD_CREDITS:
905 if (record->len < sizeof(struct htc_credit_report)) {
906 WARN_ON_ONCE(1);
907 return -EINVAL;
908 }
909
910 report = (struct htc_credit_report *) record_buf;
911 htc_process_credit_report(target, report,
912 record->len / sizeof(*report),
913 from_ep);
914 break;
915 default:
916 ath6kl_dbg(ATH6KL_DBG_HTC,
917 "unhandled record: id:%d length:%d\n",
918 record->rec_id, record->len);
919 break;
920 }
921
922 if (status != 0)
923 break;
924
925 /* advance buffer past this record for next time around */
926 buffer += record->len;
927 len -= record->len;
928 }
929
930 return status;
931}
932
933static void do_recv_completion(struct htc_endpoint *ep,
934 struct list_head *queue_to_indicate)
935{
936 struct htc_packet *packet;
937
938 if (list_empty(queue_to_indicate)) {
939 /* nothing to indicate */
940 return;
941 }
942
943 /* using legacy EpRecv */
944 while (!list_empty(queue_to_indicate)) {
945 packet = list_first_entry(queue_to_indicate,
946 struct htc_packet, list);
947 list_del(&packet->list);
948 ep->ep_cb.rx(ep->target, packet);
949 }
950
951 return;
952}
953
954static void recv_packet_completion(struct htc_target *target,
955 struct htc_endpoint *ep,
956 struct htc_packet *packet)
957{
958 struct list_head container;
959 INIT_LIST_HEAD(&container);
960 list_add_tail(&packet->list, &container);
961
962 /* do completion */
963 do_recv_completion(ep, &container);
964}
965
966static int ath6kl_htc_pipe_rx_complete(struct ath6kl *ar, struct sk_buff *skb,
967 u8 pipeid)
968{
969 struct htc_target *target = ar->htc_target;
970 u8 *netdata, *trailer, hdr_info;
971 struct htc_frame_hdr *htc_hdr;
972 u32 netlen, trailerlen = 0;
973 struct htc_packet *packet;
974 struct htc_endpoint *ep;
975 u16 payload_len;
976 int status = 0;
977
978 netdata = skb->data;
979 netlen = skb->len;
980
981 htc_hdr = (struct htc_frame_hdr *) netdata;
982
983 ep = &target->endpoint[htc_hdr->eid];
984
985 if (htc_hdr->eid >= ENDPOINT_MAX) {
986 ath6kl_dbg(ATH6KL_DBG_HTC,
987 "HTC Rx: invalid EndpointID=%d\n",
988 htc_hdr->eid);
989 status = -EINVAL;
990 goto free_skb;
991 }
992
993 payload_len = le16_to_cpu(get_unaligned(&htc_hdr->payld_len));
994
995 if (netlen < (payload_len + HTC_HDR_LENGTH)) {
996 ath6kl_dbg(ATH6KL_DBG_HTC,
997 "HTC Rx: insufficient length, got:%d expected =%u\n",
998 netlen, payload_len + HTC_HDR_LENGTH);
999 status = -EINVAL;
1000 goto free_skb;
1001 }
1002
1003 /* get flags to check for trailer */
1004 hdr_info = htc_hdr->flags;
1005 if (hdr_info & HTC_FLG_RX_TRAILER) {
1006 /* extract the trailer length */
1007 hdr_info = htc_hdr->ctrl[0];
1008 if ((hdr_info < sizeof(struct htc_record_hdr)) ||
1009 (hdr_info > payload_len)) {
1010 ath6kl_dbg(ATH6KL_DBG_HTC,
1011 "invalid header: payloadlen should be %d, CB[0]: %d\n",
1012 payload_len, hdr_info);
1013 status = -EINVAL;
1014 goto free_skb;
1015 }
1016
1017 trailerlen = hdr_info;
1018 /* process trailer after hdr/apps payload */
1019 trailer = (u8 *) htc_hdr + HTC_HDR_LENGTH +
1020 payload_len - hdr_info;
1021 status = htc_process_trailer(target, trailer, hdr_info,
1022 htc_hdr->eid);
1023 if (status != 0)
1024 goto free_skb;
1025 }
1026
1027 if (((int) payload_len - (int) trailerlen) <= 0) {
1028 /* zero length packet with trailer, just drop these */
1029 goto free_skb;
1030 }
1031
1032 if (htc_hdr->eid == ENDPOINT_0) {
1033 /* handle HTC control message */
1034 if (target->htc_flags & HTC_OP_STATE_SETUP_COMPLETE) {
1035 /*
1036 * fatal: target should not send unsolicited
1037 * messageson the endpoint 0
1038 */
1039 ath6kl_dbg(ATH6KL_DBG_HTC,
1040 "HTC ignores Rx Ctrl after setup complete\n");
1041 status = -EINVAL;
1042 goto free_skb;
1043 }
1044
1045 /* remove HTC header */
1046 skb_pull(skb, HTC_HDR_LENGTH);
1047
1048 netdata = skb->data;
1049 netlen = skb->len;
1050
1051 spin_lock_bh(&target->rx_lock);
1052
1053 target->pipe.ctrl_response_valid = true;
1054 target->pipe.ctrl_response_len = min_t(int, netlen,
1055 HTC_MAX_CTRL_MSG_LEN);
1056 memcpy(target->pipe.ctrl_response_buf, netdata,
1057 target->pipe.ctrl_response_len);
1058
1059 spin_unlock_bh(&target->rx_lock);
1060
1061 dev_kfree_skb(skb);
1062 skb = NULL;
1063 goto free_skb;
1064 }
1065
1066 /*
1067 * TODO: the message based HIF architecture allocates net bufs
1068 * for recv packets since it bridges that HIF to upper layers,
1069 * which expects HTC packets, we form the packets here
1070 */
1071 packet = alloc_htc_packet_container(target);
1072 if (packet == NULL) {
1073 status = -ENOMEM;
1074 goto free_skb;
1075 }
1076
1077 packet->status = 0;
1078 packet->endpoint = htc_hdr->eid;
1079 packet->pkt_cntxt = skb;
1080
1081 /* TODO: for backwards compatibility */
1082 packet->buf = skb_push(skb, 0) + HTC_HDR_LENGTH;
1083 packet->act_len = netlen - HTC_HDR_LENGTH - trailerlen;
1084
1085 /*
1086 * TODO: this is a hack because the driver layer will set the
1087 * actual len of the skb again which will just double the len
1088 */
1089 skb_trim(skb, 0);
1090
1091 recv_packet_completion(target, ep, packet);
1092
1093 /* recover the packet container */
1094 free_htc_packet_container(target, packet);
1095 skb = NULL;
1096
1097free_skb:
1098 if (skb != NULL)
1099 dev_kfree_skb(skb);
1100
1101 return status;
1102
1103}
1104
1105static void htc_flush_rx_queue(struct htc_target *target,
1106 struct htc_endpoint *ep)
1107{
1108 struct list_head container;
1109 struct htc_packet *packet;
1110
1111 spin_lock_bh(&target->rx_lock);
1112
1113 while (1) {
1114 if (list_empty(&ep->rx_bufq))
1115 break;
1116
1117 packet = list_first_entry(&ep->rx_bufq,
1118 struct htc_packet, list);
1119 list_del(&packet->list);
1120
1121 spin_unlock_bh(&target->rx_lock);
1122 packet->status = -ECANCELED;
1123 packet->act_len = 0;
1124
1125 ath6kl_dbg(ATH6KL_DBG_HTC,
1126 "Flushing RX packet:0x%p, length:%d, ep:%d\n",
1127 packet, packet->buf_len,
1128 packet->endpoint);
1129
1130 INIT_LIST_HEAD(&container);
1131 list_add_tail(&packet->list, &container);
1132
1133 /* give the packet back */
1134 do_recv_completion(ep, &container);
1135 spin_lock_bh(&target->rx_lock);
1136 }
1137
1138 spin_unlock_bh(&target->rx_lock);
1139}
1140
1141/* polling routine to wait for a control packet to be received */
1142static int htc_wait_recv_ctrl_message(struct htc_target *target)
1143{
1144 int count = HTC_TARGET_RESPONSE_POLL_COUNT;
1145
1146 while (count > 0) {
1147 spin_lock_bh(&target->rx_lock);
1148
1149 if (target->pipe.ctrl_response_valid) {
1150 target->pipe.ctrl_response_valid = false;
1151 spin_unlock_bh(&target->rx_lock);
1152 break;
1153 }
1154
1155 spin_unlock_bh(&target->rx_lock);
1156
1157 count--;
1158
1159 msleep_interruptible(HTC_TARGET_RESPONSE_POLL_WAIT);
1160 }
1161
1162 if (count <= 0) {
1163 ath6kl_dbg(ATH6KL_DBG_HTC, "%s: Timeout!\n", __func__);
1164 return -ECOMM;
1165 }
1166
1167 return 0;
1168}
1169
1170static void htc_rxctrl_complete(struct htc_target *context,
1171 struct htc_packet *packet)
1172{
1173 /* TODO, can't really receive HTC control messages yet.... */
1174 ath6kl_dbg(ATH6KL_DBG_HTC, "%s: invalid call function\n", __func__);
1175}
1176
1177/* htc pipe initialization */
1178static void reset_endpoint_states(struct htc_target *target)
1179{
1180 struct htc_endpoint *ep;
1181 int i;
1182
1183 for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) {
1184 ep = &target->endpoint[i];
1185 ep->svc_id = 0;
1186 ep->len_max = 0;
1187 ep->max_txq_depth = 0;
1188 ep->eid = i;
1189 INIT_LIST_HEAD(&ep->txq);
1190 INIT_LIST_HEAD(&ep->pipe.tx_lookup_queue);
1191 INIT_LIST_HEAD(&ep->rx_bufq);
1192 ep->target = target;
1193 ep->pipe.tx_credit_flow_enabled = (bool) 1; /* FIXME */
1194 }
1195}
1196
1197/* start HTC, this is called after all services are connected */
1198static int htc_config_target_hif_pipe(struct htc_target *target)
1199{
1200 return 0;
1201}
1202
1203/* htc service functions */
1204static u8 htc_get_credit_alloc(struct htc_target *target, u16 service_id)
1205{
1206 u8 allocation = 0;
1207 int i;
1208
1209 for (i = 0; i < ENDPOINT_MAX; i++) {
1210 if (target->pipe.txcredit_alloc[i].service_id == service_id)
1211 allocation =
1212 target->pipe.txcredit_alloc[i].credit_alloc;
1213 }
1214
1215 if (allocation == 0) {
1216 ath6kl_dbg(ATH6KL_DBG_HTC,
1217 "HTC Service TX : 0x%2.2X : allocation is zero!\n",
1218 service_id);
1219 }
1220
1221 return allocation;
1222}
1223
1224static int ath6kl_htc_pipe_conn_service(struct htc_target *target,
1225 struct htc_service_connect_req *conn_req,
1226 struct htc_service_connect_resp *conn_resp)
1227{
1228 struct ath6kl *ar = target->dev->ar;
1229 struct htc_packet *packet = NULL;
1230 struct htc_conn_service_resp *resp_msg;
1231 struct htc_conn_service_msg *conn_msg;
1232 enum htc_endpoint_id assigned_epid = ENDPOINT_MAX;
1233 bool disable_credit_flowctrl = false;
1234 unsigned int max_msg_size = 0;
1235 struct htc_endpoint *ep;
1236 int length, status = 0;
1237 struct sk_buff *skb;
1238 u8 tx_alloc;
1239 u16 flags;
1240
1241 if (conn_req->svc_id == 0) {
1242 WARN_ON_ONCE(1);
1243 status = -EINVAL;
1244 goto free_packet;
1245 }
1246
1247 if (conn_req->svc_id == HTC_CTRL_RSVD_SVC) {
1248 /* special case for pseudo control service */
1249 assigned_epid = ENDPOINT_0;
1250 max_msg_size = HTC_MAX_CTRL_MSG_LEN;
1251 tx_alloc = 0;
1252
1253 } else {
1254
1255 tx_alloc = htc_get_credit_alloc(target, conn_req->svc_id);
1256 if (tx_alloc == 0) {
1257 status = -ENOMEM;
1258 goto free_packet;
1259 }
1260
1261 /* allocate a packet to send to the target */
1262 packet = htc_alloc_txctrl_packet(target);
1263
1264 if (packet == NULL) {
1265 WARN_ON_ONCE(1);
1266 status = -ENOMEM;
1267 goto free_packet;
1268 }
1269
1270 skb = packet->skb;
1271 length = sizeof(struct htc_conn_service_msg);
1272
1273 /* assemble connect service message */
1274 conn_msg = (struct htc_conn_service_msg *) skb_put(skb,
1275 length);
1276 if (conn_msg == NULL) {
1277 WARN_ON_ONCE(1);
1278 status = -EINVAL;
1279 goto free_packet;
1280 }
1281
1282 memset(conn_msg, 0,
1283 sizeof(struct htc_conn_service_msg));
1284 conn_msg->msg_id = cpu_to_le16(HTC_MSG_CONN_SVC_ID);
1285 conn_msg->svc_id = cpu_to_le16(conn_req->svc_id);
1286 conn_msg->conn_flags = cpu_to_le16(conn_req->conn_flags &
1287 ~HTC_CONN_FLGS_SET_RECV_ALLOC_MASK);
1288
1289 /* tell target desired recv alloc for this ep */
1290 flags = tx_alloc << HTC_CONN_FLGS_SET_RECV_ALLOC_SHIFT;
1291 conn_msg->conn_flags |= cpu_to_le16(flags);
1292
1293 if (conn_req->conn_flags &
1294 HTC_CONN_FLGS_DISABLE_CRED_FLOW_CTRL) {
1295 disable_credit_flowctrl = true;
1296 }
1297
1298 set_htc_pkt_info(packet, NULL, (u8 *) conn_msg,
1299 length,
1300 ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG);
1301
1302 status = ath6kl_htc_pipe_tx(target, packet);
1303
1304 /* we don't own it anymore */
1305 packet = NULL;
1306 if (status != 0)
1307 goto free_packet;
1308
1309 /* wait for response */
1310 status = htc_wait_recv_ctrl_message(target);
1311 if (status != 0)
1312 goto free_packet;
1313
1314 /* we controlled the buffer creation so it has to be
1315 * properly aligned
1316 */
1317 resp_msg = (struct htc_conn_service_resp *)
1318 target->pipe.ctrl_response_buf;
1319
1320 if (resp_msg->msg_id != cpu_to_le16(HTC_MSG_CONN_SVC_RESP_ID) ||
1321 (target->pipe.ctrl_response_len < sizeof(*resp_msg))) {
1322 /* this message is not valid */
1323 WARN_ON_ONCE(1);
1324 status = -EINVAL;
1325 goto free_packet;
1326 }
1327
1328 ath6kl_dbg(ATH6KL_DBG_TRC,
1329 "%s: service 0x%X conn resp: status: %d ep: %d\n",
1330 __func__, resp_msg->svc_id, resp_msg->status,
1331 resp_msg->eid);
1332
1333 conn_resp->resp_code = resp_msg->status;
1334 /* check response status */
1335 if (resp_msg->status != HTC_SERVICE_SUCCESS) {
1336 ath6kl_dbg(ATH6KL_DBG_HTC,
1337 "Target failed service 0x%X connect request (status:%d)\n",
1338 resp_msg->svc_id, resp_msg->status);
1339 status = -EINVAL;
1340 goto free_packet;
1341 }
1342
1343 assigned_epid = (enum htc_endpoint_id) resp_msg->eid;
1344 max_msg_size = le16_to_cpu(resp_msg->max_msg_sz);
1345 }
1346
1347 /* the rest are parameter checks so set the error status */
1348 status = -EINVAL;
1349
1350 if (assigned_epid >= ENDPOINT_MAX) {
1351 WARN_ON_ONCE(1);
1352 goto free_packet;
1353 }
1354
1355 if (max_msg_size == 0) {
1356 WARN_ON_ONCE(1);
1357 goto free_packet;
1358 }
1359
1360 ep = &target->endpoint[assigned_epid];
1361 ep->eid = assigned_epid;
1362 if (ep->svc_id != 0) {
1363 /* endpoint already in use! */
1364 WARN_ON_ONCE(1);
1365 goto free_packet;
1366 }
1367
1368 /* return assigned endpoint to caller */
1369 conn_resp->endpoint = assigned_epid;
1370 conn_resp->len_max = max_msg_size;
1371
1372 /* setup the endpoint */
1373 ep->svc_id = conn_req->svc_id; /* this marks ep in use */
1374 ep->max_txq_depth = conn_req->max_txq_depth;
1375 ep->len_max = max_msg_size;
1376 ep->cred_dist.credits = tx_alloc;
1377 ep->cred_dist.cred_sz = target->tgt_cred_sz;
1378 ep->cred_dist.cred_per_msg = max_msg_size / target->tgt_cred_sz;
1379 if (max_msg_size % target->tgt_cred_sz)
1380 ep->cred_dist.cred_per_msg++;
1381
1382 /* copy all the callbacks */
1383 ep->ep_cb = conn_req->ep_cb;
1384
1385 status = ath6kl_hif_pipe_map_service(ar, ep->svc_id,
1386 &ep->pipe.pipeid_ul,
1387 &ep->pipe.pipeid_dl);
1388 if (status != 0)
1389 goto free_packet;
1390
1391 ath6kl_dbg(ATH6KL_DBG_HTC,
1392 "SVC Ready: 0x%4.4X: ULpipe:%d DLpipe:%d id:%d\n",
1393 ep->svc_id, ep->pipe.pipeid_ul,
1394 ep->pipe.pipeid_dl, ep->eid);
1395
1396 if (disable_credit_flowctrl && ep->pipe.tx_credit_flow_enabled) {
1397 ep->pipe.tx_credit_flow_enabled = false;
1398 ath6kl_dbg(ATH6KL_DBG_HTC,
1399 "SVC: 0x%4.4X ep:%d TX flow control off\n",
1400 ep->svc_id, assigned_epid);
1401 }
1402
1403free_packet:
1404 if (packet != NULL)
1405 htc_free_txctrl_packet(target, packet);
1406 return status;
1407}
1408
1409/* htc export functions */
1410static void *ath6kl_htc_pipe_create(struct ath6kl *ar)
1411{
1412 int status = 0;
1413 struct htc_endpoint *ep = NULL;
1414 struct htc_target *target = NULL;
1415 struct htc_packet *packet;
1416 int i;
1417
1418 target = kzalloc(sizeof(struct htc_target), GFP_KERNEL);
1419 if (target == NULL) {
1420 ath6kl_err("htc create unable to allocate memory\n");
1421 status = -ENOMEM;
1422 goto fail_htc_create;
1423 }
1424
1425 spin_lock_init(&target->htc_lock);
1426 spin_lock_init(&target->rx_lock);
1427 spin_lock_init(&target->tx_lock);
1428
1429 reset_endpoint_states(target);
1430
1431 for (i = 0; i < HTC_PACKET_CONTAINER_ALLOCATION; i++) {
1432 packet = kzalloc(sizeof(struct htc_packet), GFP_KERNEL);
1433
1434 if (packet != NULL)
1435 free_htc_packet_container(target, packet);
1436 }
1437
1438 target->dev = kzalloc(sizeof(*target->dev), GFP_KERNEL);
1439 if (!target->dev) {
1440 ath6kl_err("unable to allocate memory\n");
1441 status = -ENOMEM;
1442 goto fail_htc_create;
1443 }
1444 target->dev->ar = ar;
1445 target->dev->htc_cnxt = target;
1446
1447 /* Get HIF default pipe for HTC message exchange */
1448 ep = &target->endpoint[ENDPOINT_0];
1449
1450 ath6kl_hif_pipe_get_default(ar, &ep->pipe.pipeid_ul,
1451 &ep->pipe.pipeid_dl);
1452
1453 return target;
1454
1455fail_htc_create:
1456 if (status != 0) {
1457 if (target != NULL)
1458 ath6kl_htc_pipe_cleanup(target);
1459
1460 target = NULL;
1461 }
1462 return target;
1463}
1464
1465/* cleanup the HTC instance */
1466static void ath6kl_htc_pipe_cleanup(struct htc_target *target)
1467{
1468 struct htc_packet *packet;
1469
1470 while (true) {
1471 packet = alloc_htc_packet_container(target);
1472 if (packet == NULL)
1473 break;
1474 kfree(packet);
1475 }
1476
1477 kfree(target->dev);
1478
1479 /* kfree our instance */
1480 kfree(target);
1481}
1482
1483static int ath6kl_htc_pipe_start(struct htc_target *target)
1484{
1485 struct sk_buff *skb;
1486 struct htc_setup_comp_ext_msg *setup;
1487 struct htc_packet *packet;
1488
1489 htc_config_target_hif_pipe(target);
1490
1491 /* allocate a buffer to send */
1492 packet = htc_alloc_txctrl_packet(target);
1493 if (packet == NULL) {
1494 WARN_ON_ONCE(1);
1495 return -ENOMEM;
1496 }
1497
1498 skb = packet->skb;
1499
1500 /* assemble setup complete message */
1501 setup = (struct htc_setup_comp_ext_msg *) skb_put(skb,
1502 sizeof(*setup));
1503 memset(setup, 0, sizeof(struct htc_setup_comp_ext_msg));
1504 setup->msg_id = cpu_to_le16(HTC_MSG_SETUP_COMPLETE_EX_ID);
1505
1506 ath6kl_dbg(ATH6KL_DBG_HTC, "HTC using TX credit flow control\n");
1507
1508 set_htc_pkt_info(packet, NULL, (u8 *) setup,
1509 sizeof(struct htc_setup_comp_ext_msg),
1510 ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG);
1511
1512 target->htc_flags |= HTC_OP_STATE_SETUP_COMPLETE;
1513
1514 return ath6kl_htc_pipe_tx(target, packet);
1515}
1516
1517static void ath6kl_htc_pipe_stop(struct htc_target *target)
1518{
1519 int i;
1520 struct htc_endpoint *ep;
1521
1522 /* cleanup endpoints */
1523 for (i = 0; i < ENDPOINT_MAX; i++) {
1524 ep = &target->endpoint[i];
1525 htc_flush_rx_queue(target, ep);
1526 htc_flush_tx_endpoint(target, ep, HTC_TX_PACKET_TAG_ALL);
1527 }
1528
1529 reset_endpoint_states(target);
1530 target->htc_flags &= ~HTC_OP_STATE_SETUP_COMPLETE;
1531}
1532
1533static int ath6kl_htc_pipe_get_rxbuf_num(struct htc_target *target,
1534 enum htc_endpoint_id endpoint)
1535{
1536 int num;
1537
1538 spin_lock_bh(&target->rx_lock);
1539 num = get_queue_depth(&(target->endpoint[endpoint].rx_bufq));
1540 spin_unlock_bh(&target->rx_lock);
1541
1542 return num;
1543}
1544
1545static int ath6kl_htc_pipe_tx(struct htc_target *target,
1546 struct htc_packet *packet)
1547{
1548 struct list_head queue;
1549
1550 ath6kl_dbg(ATH6KL_DBG_HTC,
1551 "%s: endPointId: %d, buffer: 0x%p, length: %d\n",
1552 __func__, packet->endpoint, packet->buf,
1553 packet->act_len);
1554
1555 INIT_LIST_HEAD(&queue);
1556 list_add_tail(&packet->list, &queue);
1557
1558 return htc_send_packets_multiple(target, &queue);
1559}
1560
1561static int ath6kl_htc_pipe_wait_target(struct htc_target *target)
1562{
1563 struct htc_ready_ext_msg *ready_msg;
1564 struct htc_service_connect_req connect;
1565 struct htc_service_connect_resp resp;
1566 int status = 0;
1567
1568 status = htc_wait_recv_ctrl_message(target);
1569
1570 if (status != 0)
1571 return status;
1572
1573 if (target->pipe.ctrl_response_len < sizeof(*ready_msg)) {
1574 ath6kl_dbg(ATH6KL_DBG_HTC, "invalid htc ready msg len:%d!\n",
1575 target->pipe.ctrl_response_len);
1576 return -ECOMM;
1577 }
1578
1579 ready_msg = (struct htc_ready_ext_msg *) target->pipe.ctrl_response_buf;
1580
1581 if (ready_msg->ver2_0_info.msg_id != cpu_to_le16(HTC_MSG_READY_ID)) {
1582 ath6kl_dbg(ATH6KL_DBG_HTC, "invalid htc ready msg : 0x%X !\n",
1583 ready_msg->ver2_0_info.msg_id);
1584 return -ECOMM;
1585 }
1586
1587 ath6kl_dbg(ATH6KL_DBG_HTC,
1588 "Target Ready! : transmit resources : %d size:%d\n",
1589 ready_msg->ver2_0_info.cred_cnt,
1590 ready_msg->ver2_0_info.cred_sz);
1591
1592 target->tgt_creds = le16_to_cpu(ready_msg->ver2_0_info.cred_cnt);
1593 target->tgt_cred_sz = le16_to_cpu(ready_msg->ver2_0_info.cred_sz);
1594
1595 if ((target->tgt_creds == 0) || (target->tgt_cred_sz == 0))
1596 return -ECOMM;
1597
1598 htc_setup_target_buffer_assignments(target);
1599
1600 /* setup our pseudo HTC control endpoint connection */
1601 memset(&connect, 0, sizeof(connect));
1602 memset(&resp, 0, sizeof(resp));
1603 connect.ep_cb.tx_complete = htc_txctrl_complete;
1604 connect.ep_cb.rx = htc_rxctrl_complete;
1605 connect.max_txq_depth = NUM_CONTROL_TX_BUFFERS;
1606 connect.svc_id = HTC_CTRL_RSVD_SVC;
1607
1608 /* connect fake service */
1609 status = ath6kl_htc_pipe_conn_service(target, &connect, &resp);
1610
1611 return status;
1612}
1613
1614static void ath6kl_htc_pipe_flush_txep(struct htc_target *target,
1615 enum htc_endpoint_id endpoint, u16 tag)
1616{
1617 struct htc_endpoint *ep = &target->endpoint[endpoint];
1618
1619 if (ep->svc_id == 0) {
1620 WARN_ON_ONCE(1);
1621 /* not in use.. */
1622 return;
1623 }
1624
1625 htc_flush_tx_endpoint(target, ep, tag);
1626}
1627
1628static int ath6kl_htc_pipe_add_rxbuf_multiple(struct htc_target *target,
1629 struct list_head *pkt_queue)
1630{
1631 struct htc_packet *packet, *tmp_pkt, *first;
1632 struct htc_endpoint *ep;
1633 int status = 0;
1634
1635 if (list_empty(pkt_queue))
1636 return -EINVAL;
1637
1638 first = list_first_entry(pkt_queue, struct htc_packet, list);
1639 if (first == NULL) {
1640 WARN_ON_ONCE(1);
1641 return -EINVAL;
1642 }
1643
1644 if (first->endpoint >= ENDPOINT_MAX) {
1645 WARN_ON_ONCE(1);
1646 return -EINVAL;
1647 }
1648
1649 ath6kl_dbg(ATH6KL_DBG_HTC, "%s: epid: %d, cnt:%d, len: %d\n",
1650 __func__, first->endpoint, get_queue_depth(pkt_queue),
1651 first->buf_len);
1652
1653 ep = &target->endpoint[first->endpoint];
1654
1655 spin_lock_bh(&target->rx_lock);
1656
1657 /* store receive packets */
1658 list_splice_tail_init(pkt_queue, &ep->rx_bufq);
1659
1660 spin_unlock_bh(&target->rx_lock);
1661
1662 if (status != 0) {
1663 /* walk through queue and mark each one canceled */
1664 list_for_each_entry_safe(packet, tmp_pkt, pkt_queue, list) {
1665 packet->status = -ECANCELED;
1666 }
1667
1668 do_recv_completion(ep, pkt_queue);
1669 }
1670
1671 return status;
1672}
1673
1674static void ath6kl_htc_pipe_activity_changed(struct htc_target *target,
1675 enum htc_endpoint_id ep,
1676 bool active)
1677{
1678 /* TODO */
1679}
1680
1681static void ath6kl_htc_pipe_flush_rx_buf(struct htc_target *target)
1682{
1683 /* TODO */
1684}
1685
1686static int ath6kl_htc_pipe_credit_setup(struct htc_target *target,
1687 struct ath6kl_htc_credit_info *info)
1688{
1689 return 0;
1690}
1691
1692static const struct ath6kl_htc_ops ath6kl_htc_pipe_ops = {
1693 .create = ath6kl_htc_pipe_create,
1694 .wait_target = ath6kl_htc_pipe_wait_target,
1695 .start = ath6kl_htc_pipe_start,
1696 .conn_service = ath6kl_htc_pipe_conn_service,
1697 .tx = ath6kl_htc_pipe_tx,
1698 .stop = ath6kl_htc_pipe_stop,
1699 .cleanup = ath6kl_htc_pipe_cleanup,
1700 .flush_txep = ath6kl_htc_pipe_flush_txep,
1701 .flush_rx_buf = ath6kl_htc_pipe_flush_rx_buf,
1702 .activity_changed = ath6kl_htc_pipe_activity_changed,
1703 .get_rxbuf_num = ath6kl_htc_pipe_get_rxbuf_num,
1704 .add_rxbuf_multiple = ath6kl_htc_pipe_add_rxbuf_multiple,
1705 .credit_setup = ath6kl_htc_pipe_credit_setup,
1706 .tx_complete = ath6kl_htc_pipe_tx_complete,
1707 .rx_complete = ath6kl_htc_pipe_rx_complete,
1708};
1709
1710void ath6kl_htc_pipe_attach(struct ath6kl *ar)
1711{
1712 ar->htc_ops = &ath6kl_htc_pipe_ops;
1713}