1*b1906ceaSLachlan Hodges // SPDX-License-Identifier: GPL-2.0-only
2*b1906ceaSLachlan Hodges /*
3*b1906ceaSLachlan Hodges * Copyright (c) 2017-2026 Morse Micro
4*b1906ceaSLachlan Hodges */
5*b1906ceaSLachlan Hodges #include <linux/types.h>
6*b1906ceaSLachlan Hodges #include <linux/slab.h>
7*b1906ceaSLachlan Hodges #include <linux/workqueue.h>
8*b1906ceaSLachlan Hodges #include <linux/ktime.h>
9*b1906ceaSLachlan Hodges #include <linux/skbuff.h>
10*b1906ceaSLachlan Hodges #include <linux/jiffies.h>
11*b1906ceaSLachlan Hodges #include "hif.h"
12*b1906ceaSLachlan Hodges #include "skbq.h"
13*b1906ceaSLachlan Hodges #include "mac.h"
14*b1906ceaSLachlan Hodges #include "command.h"
15*b1906ceaSLachlan Hodges #include "bus.h"
16*b1906ceaSLachlan Hodges
17*b1906ceaSLachlan Hodges /* Returns number of bytes needed to word align */
18*b1906ceaSLachlan Hodges #define BYTES_NEEDED_TO_WORD_ALIGN(bytes) \
19*b1906ceaSLachlan Hodges ((bytes) & 0x3 ? (4 - ((bytes) & 0x3)) : 0)
20*b1906ceaSLachlan Hodges
21*b1906ceaSLachlan Hodges /* Rounds down to the nearest word boundary */
22*b1906ceaSLachlan Hodges #define ROUND_DOWN_TO_WORD(bytes) \
23*b1906ceaSLachlan Hodges (BYTES_NEEDED_TO_WORD_ALIGN(bytes) ? \
24*b1906ceaSLachlan Hodges bytes - (4 - BYTES_NEEDED_TO_WORD_ALIGN(bytes)) : \
25*b1906ceaSLachlan Hodges bytes)
26*b1906ceaSLachlan Hodges
27*b1906ceaSLachlan Hodges #define MM81X_SKBQ_MAX_TXQ_LEN 32
28*b1906ceaSLachlan Hodges #define MM81X_SKBQ_TX_QUEUED_LIFETIME_MS 1000
29*b1906ceaSLachlan Hodges #define MM81X_SKBQ_TX_STATUS_LIFETIME_MS (15 * 1000)
30*b1906ceaSLachlan Hodges
31*b1906ceaSLachlan Hodges /* Returns padding needed to align x up to a 4-byte boundary */
32*b1906ceaSLachlan Hodges #define MM81X_PAD4(x) (((x) & 0x3) ? (4 - ((x) & 0x3)) : 0)
33*b1906ceaSLachlan Hodges
34*b1906ceaSLachlan Hodges struct mm81x_tx_status_priv {
35*b1906ceaSLachlan Hodges /*
36*b1906ceaSLachlan Hodges * Time (jiffies) at which this packet has spent too long the pending
37*b1906ceaSLachlan Hodges * queue, waiting for status notification from the firmware, and
38*b1906ceaSLachlan Hodges * should be considered lost.
39*b1906ceaSLachlan Hodges */
40*b1906ceaSLachlan Hodges unsigned long tx_status_expiry;
41*b1906ceaSLachlan Hodges };
42*b1906ceaSLachlan Hodges
43*b1906ceaSLachlan Hodges static struct mm81x_tx_status_priv *
__mm81x_skbq_tx_status_priv(struct sk_buff * skb)44*b1906ceaSLachlan Hodges __mm81x_skbq_tx_status_priv(struct sk_buff *skb)
45*b1906ceaSLachlan Hodges {
46*b1906ceaSLachlan Hodges struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
47*b1906ceaSLachlan Hodges
48*b1906ceaSLachlan Hodges BUILD_BUG_ON(sizeof(struct mm81x_tx_status_priv) >
49*b1906ceaSLachlan Hodges sizeof(tx_info->status.status_driver_data));
50*b1906ceaSLachlan Hodges return (struct mm81x_tx_status_priv *)&tx_info->status
51*b1906ceaSLachlan Hodges .status_driver_data[0];
52*b1906ceaSLachlan Hodges }
53*b1906ceaSLachlan Hodges
__mm81x_skbq_has_pending_tx_skb_timed_out(struct sk_buff * skb)54*b1906ceaSLachlan Hodges static bool __mm81x_skbq_has_pending_tx_skb_timed_out(struct sk_buff *skb)
55*b1906ceaSLachlan Hodges {
56*b1906ceaSLachlan Hodges struct mm81x_tx_status_priv *info = __mm81x_skbq_tx_status_priv(skb);
57*b1906ceaSLachlan Hodges
58*b1906ceaSLachlan Hodges /* If our timestamp value is in the past then we have timed out. */
59*b1906ceaSLachlan Hodges return time_is_before_jiffies(info->tx_status_expiry);
60*b1906ceaSLachlan Hodges }
61*b1906ceaSLachlan Hodges
__mm81x_skbq_size(const struct mm81x_skbq * mq)62*b1906ceaSLachlan Hodges static u32 __mm81x_skbq_size(const struct mm81x_skbq *mq)
63*b1906ceaSLachlan Hodges {
64*b1906ceaSLachlan Hodges return mq->skbq_size;
65*b1906ceaSLachlan Hodges }
66*b1906ceaSLachlan Hodges
__mm81x_skbq_space(const struct mm81x_skbq * mq)67*b1906ceaSLachlan Hodges static u32 __mm81x_skbq_space(const struct mm81x_skbq *mq)
68*b1906ceaSLachlan Hodges {
69*b1906ceaSLachlan Hodges return MM81X_SKBQ_SIZE - __mm81x_skbq_size(mq);
70*b1906ceaSLachlan Hodges }
71*b1906ceaSLachlan Hodges
__mm81x_skbq_over_threshold(struct mm81x_skbq * mq)72*b1906ceaSLachlan Hodges static bool __mm81x_skbq_over_threshold(struct mm81x_skbq *mq)
73*b1906ceaSLachlan Hodges {
74*b1906ceaSLachlan Hodges return skb_queue_len(&mq->skbq) >= MM81X_SKBQ_MAX_TXQ_LEN;
75*b1906ceaSLachlan Hodges }
76*b1906ceaSLachlan Hodges
__mm81x_skbq_under_threshold(struct mm81x_skbq * mq)77*b1906ceaSLachlan Hodges static bool __mm81x_skbq_under_threshold(struct mm81x_skbq *mq)
78*b1906ceaSLachlan Hodges {
79*b1906ceaSLachlan Hodges return skb_queue_len(&mq->skbq) < (MM81X_SKBQ_MAX_TXQ_LEN - 2);
80*b1906ceaSLachlan Hodges }
81*b1906ceaSLachlan Hodges
__mm81x_skbq_unlink(struct mm81x_skbq * mq,struct sk_buff_head * queue,struct sk_buff * skb)82*b1906ceaSLachlan Hodges static void __mm81x_skbq_unlink(struct mm81x_skbq *mq,
83*b1906ceaSLachlan Hodges struct sk_buff_head *queue, struct sk_buff *skb)
84*b1906ceaSLachlan Hodges {
85*b1906ceaSLachlan Hodges if (queue == &mq->skbq) {
86*b1906ceaSLachlan Hodges WARN_ON(skb->len > mq->skbq_size);
87*b1906ceaSLachlan Hodges mq->skbq_size -= min(skb->len, mq->skbq_size);
88*b1906ceaSLachlan Hodges }
89*b1906ceaSLachlan Hodges
90*b1906ceaSLachlan Hodges __skb_unlink(skb, queue);
91*b1906ceaSLachlan Hodges }
92*b1906ceaSLachlan Hodges
__mm81x_skbq_put(struct mm81x_skbq * mq,struct sk_buff_head * queue,struct sk_buff * skb,bool queue_at_head,struct sk_buff * queue_before)93*b1906ceaSLachlan Hodges static int __mm81x_skbq_put(struct mm81x_skbq *mq, struct sk_buff_head *queue,
94*b1906ceaSLachlan Hodges struct sk_buff *skb, bool queue_at_head,
95*b1906ceaSLachlan Hodges struct sk_buff *queue_before)
96*b1906ceaSLachlan Hodges {
97*b1906ceaSLachlan Hodges /* Limit the size of the Tx queue, but not the pending queue */
98*b1906ceaSLachlan Hodges if (queue == &mq->skbq) {
99*b1906ceaSLachlan Hodges if (skb->len > __mm81x_skbq_space(mq))
100*b1906ceaSLachlan Hodges return -ENOMEM;
101*b1906ceaSLachlan Hodges
102*b1906ceaSLachlan Hodges mq->skbq_size += skb->len;
103*b1906ceaSLachlan Hodges }
104*b1906ceaSLachlan Hodges
105*b1906ceaSLachlan Hodges if (queue_before)
106*b1906ceaSLachlan Hodges __skb_queue_before(queue, queue_before, skb);
107*b1906ceaSLachlan Hodges else if (queue_at_head)
108*b1906ceaSLachlan Hodges __skb_queue_head(queue, skb);
109*b1906ceaSLachlan Hodges else
110*b1906ceaSLachlan Hodges __skb_queue_tail(queue, skb);
111*b1906ceaSLachlan Hodges
112*b1906ceaSLachlan Hodges return 0;
113*b1906ceaSLachlan Hodges }
114*b1906ceaSLachlan Hodges
__mm81x_skbq_pkt_id(struct mm81x_skbq * mq,struct sk_buff * skb)115*b1906ceaSLachlan Hodges static void __mm81x_skbq_pkt_id(struct mm81x_skbq *mq, struct sk_buff *skb)
116*b1906ceaSLachlan Hodges {
117*b1906ceaSLachlan Hodges struct mm81x_skb_hdr *hdr = (struct mm81x_skb_hdr *)skb->data;
118*b1906ceaSLachlan Hodges
119*b1906ceaSLachlan Hodges hdr->tx_info.pkt_id = cpu_to_le32(mq->pkt_seq++);
120*b1906ceaSLachlan Hodges }
121*b1906ceaSLachlan Hodges
122*b1906ceaSLachlan Hodges static struct mm81x_skbq *
__mm81x_skbq_tx_status_to_skbq(struct mm81x * mors,const struct mm81x_skb_tx_status * tx_sts)123*b1906ceaSLachlan Hodges __mm81x_skbq_tx_status_to_skbq(struct mm81x *mors,
124*b1906ceaSLachlan Hodges const struct mm81x_skb_tx_status *tx_sts)
125*b1906ceaSLachlan Hodges {
126*b1906ceaSLachlan Hodges int aci;
127*b1906ceaSLachlan Hodges struct mm81x_skbq *mq = NULL;
128*b1906ceaSLachlan Hodges
129*b1906ceaSLachlan Hodges switch (tx_sts->channel) {
130*b1906ceaSLachlan Hodges case MM81X_SKB_CHAN_DATA:
131*b1906ceaSLachlan Hodges case MM81X_SKB_CHAN_DATA_NOACK:
132*b1906ceaSLachlan Hodges aci = dot11_tid_to_ac(tx_sts->tid);
133*b1906ceaSLachlan Hodges mq = mm81x_hif_get_tx_data_queue(mors, aci);
134*b1906ceaSLachlan Hodges break;
135*b1906ceaSLachlan Hodges case MM81X_SKB_CHAN_MGMT:
136*b1906ceaSLachlan Hodges mq = mm81x_hif_get_tx_mgmt_queue(mors);
137*b1906ceaSLachlan Hodges break;
138*b1906ceaSLachlan Hodges case MM81X_SKB_CHAN_BEACON:
139*b1906ceaSLachlan Hodges mq = mm81x_hif_get_tx_beacon_queue(mors);
140*b1906ceaSLachlan Hodges break;
141*b1906ceaSLachlan Hodges default:
142*b1906ceaSLachlan Hodges dev_err(mors->dev,
143*b1906ceaSLachlan Hodges "unexpected channel on reported tx status [%d]",
144*b1906ceaSLachlan Hodges tx_sts->channel);
145*b1906ceaSLachlan Hodges }
146*b1906ceaSLachlan Hodges
147*b1906ceaSLachlan Hodges return mq;
148*b1906ceaSLachlan Hodges }
149*b1906ceaSLachlan Hodges
mm81x_skbq_pull_hdr_post_tx(struct sk_buff * skb)150*b1906ceaSLachlan Hodges void mm81x_skbq_pull_hdr_post_tx(struct sk_buff *skb)
151*b1906ceaSLachlan Hodges {
152*b1906ceaSLachlan Hodges skb_pull(skb, sizeof(struct mm81x_skb_hdr) +
153*b1906ceaSLachlan Hodges ((struct mm81x_skb_hdr *)skb->data)->offset);
154*b1906ceaSLachlan Hodges }
155*b1906ceaSLachlan Hodges
mm81x_skbq_insert_pending(struct mm81x_skbq * mq,struct sk_buff * skb,__le32 insertion_id)156*b1906ceaSLachlan Hodges static void mm81x_skbq_insert_pending(struct mm81x_skbq *mq,
157*b1906ceaSLachlan Hodges struct sk_buff *skb, __le32 insertion_id)
158*b1906ceaSLachlan Hodges {
159*b1906ceaSLachlan Hodges struct sk_buff *pfirst, *pnext;
160*b1906ceaSLachlan Hodges struct mm81x_skb_hdr *mhdr;
161*b1906ceaSLachlan Hodges struct sk_buff *tail = skb_peek_tail(&mq->skbq);
162*b1906ceaSLachlan Hodges
163*b1906ceaSLachlan Hodges __mm81x_skbq_unlink(mq, &mq->pending, skb);
164*b1906ceaSLachlan Hodges
165*b1906ceaSLachlan Hodges if (!tail) {
166*b1906ceaSLachlan Hodges __mm81x_skbq_put(mq, &mq->skbq, skb, false, NULL);
167*b1906ceaSLachlan Hodges return;
168*b1906ceaSLachlan Hodges }
169*b1906ceaSLachlan Hodges
170*b1906ceaSLachlan Hodges /* Check if it should just be inserted on to the end */
171*b1906ceaSLachlan Hodges mhdr = (struct mm81x_skb_hdr *)tail->data;
172*b1906ceaSLachlan Hodges WARN_ON(insertion_id == mhdr->tx_info.pkt_id);
173*b1906ceaSLachlan Hodges if (le32_to_cpu(insertion_id) >= le32_to_cpu(mhdr->tx_info.pkt_id)) {
174*b1906ceaSLachlan Hodges __mm81x_skbq_put(mq, &mq->skbq, skb, false, NULL);
175*b1906ceaSLachlan Hodges return;
176*b1906ceaSLachlan Hodges }
177*b1906ceaSLachlan Hodges
178*b1906ceaSLachlan Hodges /* Otherwise, re-insert to correct spot in skbq */
179*b1906ceaSLachlan Hodges skb_queue_walk_safe(&mq->skbq, pfirst, pnext) {
180*b1906ceaSLachlan Hodges mhdr = (struct mm81x_skb_hdr *)pfirst->data;
181*b1906ceaSLachlan Hodges
182*b1906ceaSLachlan Hodges WARN_ON(insertion_id == mhdr->tx_info.pkt_id);
183*b1906ceaSLachlan Hodges if (le32_to_cpu(insertion_id) <=
184*b1906ceaSLachlan Hodges le32_to_cpu(mhdr->tx_info.pkt_id)) {
185*b1906ceaSLachlan Hodges __mm81x_skbq_put(mq, &mq->skbq, skb, false, pfirst);
186*b1906ceaSLachlan Hodges return;
187*b1906ceaSLachlan Hodges }
188*b1906ceaSLachlan Hodges }
189*b1906ceaSLachlan Hodges
190*b1906ceaSLachlan Hodges WARN_ON_ONCE(1);
191*b1906ceaSLachlan Hodges }
192*b1906ceaSLachlan Hodges
mm81x_skbq_sta_eosp(struct mm81x * mors,struct sk_buff * skb)193*b1906ceaSLachlan Hodges static void mm81x_skbq_sta_eosp(struct mm81x *mors, struct sk_buff *skb)
194*b1906ceaSLachlan Hodges {
195*b1906ceaSLachlan Hodges struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb);
196*b1906ceaSLachlan Hodges struct ieee80211_vif *vif = txi->control.vif;
197*b1906ceaSLachlan Hodges
198*b1906ceaSLachlan Hodges mm81x_skbq_pull_hdr_post_tx(skb);
199*b1906ceaSLachlan Hodges
200*b1906ceaSLachlan Hodges /*
201*b1906ceaSLachlan Hodges * If this frame is the last frame in a PS-Poll or u-APSD SP,
202*b1906ceaSLachlan Hodges * then mac80211 must be informed that the SP is now over.
203*b1906ceaSLachlan Hodges */
204*b1906ceaSLachlan Hodges if (txi->flags & IEEE80211_TX_STATUS_EOSP) {
205*b1906ceaSLachlan Hodges struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
206*b1906ceaSLachlan Hodges struct ieee80211_sta *sta;
207*b1906ceaSLachlan Hodges
208*b1906ceaSLachlan Hodges scoped_guard(rcu) {
209*b1906ceaSLachlan Hodges sta = ieee80211_find_sta(vif, hdr->addr1);
210*b1906ceaSLachlan Hodges if (sta)
211*b1906ceaSLachlan Hodges ieee80211_sta_eosp(sta);
212*b1906ceaSLachlan Hodges }
213*b1906ceaSLachlan Hodges }
214*b1906ceaSLachlan Hodges }
215*b1906ceaSLachlan Hodges
__mm81x_skbq_drop_pending_skb(struct mm81x_skbq * mq,struct sk_buff * skb)216*b1906ceaSLachlan Hodges static void __mm81x_skbq_drop_pending_skb(struct mm81x_skbq *mq,
217*b1906ceaSLachlan Hodges struct sk_buff *skb)
218*b1906ceaSLachlan Hodges {
219*b1906ceaSLachlan Hodges __mm81x_skbq_unlink(mq, &mq->pending, skb);
220*b1906ceaSLachlan Hodges mm81x_skbq_sta_eosp(mq->mors, skb);
221*b1906ceaSLachlan Hodges ieee80211_free_txskb(mq->mors->hw, skb);
222*b1906ceaSLachlan Hodges }
223*b1906ceaSLachlan Hodges
mm81x_tx_h_is_ps_filtered(struct mm81x_skbq * mq,struct sk_buff * skb,struct mm81x_skb_tx_status * tx_sts)224*b1906ceaSLachlan Hodges static bool mm81x_tx_h_is_ps_filtered(struct mm81x_skbq *mq,
225*b1906ceaSLachlan Hodges struct sk_buff *skb,
226*b1906ceaSLachlan Hodges struct mm81x_skb_tx_status *tx_sts)
227*b1906ceaSLachlan Hodges {
228*b1906ceaSLachlan Hodges struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb);
229*b1906ceaSLachlan Hodges struct ieee80211_vif *vif = txi->control.vif;
230*b1906ceaSLachlan Hodges
231*b1906ceaSLachlan Hodges WARN_ON_ONCE(!(le32_to_cpu(tx_sts->flags) &
232*b1906ceaSLachlan Hodges MM81X_TX_STATUS_FLAGS_PS_FILTERED));
233*b1906ceaSLachlan Hodges
234*b1906ceaSLachlan Hodges if (vif->type == NL80211_IFTYPE_AP) {
235*b1906ceaSLachlan Hodges __mm81x_skbq_drop_pending_skb(mq, skb);
236*b1906ceaSLachlan Hodges return true;
237*b1906ceaSLachlan Hodges }
238*b1906ceaSLachlan Hodges
239*b1906ceaSLachlan Hodges if (vif->type == NL80211_IFTYPE_STATION) {
240*b1906ceaSLachlan Hodges mm81x_skbq_insert_pending(mq, skb, tx_sts->pkt_id);
241*b1906ceaSLachlan Hodges return true;
242*b1906ceaSLachlan Hodges }
243*b1906ceaSLachlan Hodges
244*b1906ceaSLachlan Hodges return false;
245*b1906ceaSLachlan Hodges }
246*b1906ceaSLachlan Hodges
247*b1906ceaSLachlan Hodges /*
248*b1906ceaSLachlan Hodges * Get a pending frame by its ID. This will also drop frames with
249*b1906ceaSLachlan Hodges * older packet ids that are in the list
250*b1906ceaSLachlan Hodges */
__mm81x_skbq_get_pending_by_id(struct mm81x * mors,struct mm81x_skbq * mq,u32 pkt_id)251*b1906ceaSLachlan Hodges static struct sk_buff *__mm81x_skbq_get_pending_by_id(struct mm81x *mors,
252*b1906ceaSLachlan Hodges struct mm81x_skbq *mq,
253*b1906ceaSLachlan Hodges u32 pkt_id)
254*b1906ceaSLachlan Hodges {
255*b1906ceaSLachlan Hodges struct sk_buff *pfirst, *pnext;
256*b1906ceaSLachlan Hodges struct sk_buff *ret = NULL;
257*b1906ceaSLachlan Hodges
258*b1906ceaSLachlan Hodges /* Move sent packets to pending list waiting for feedback */
259*b1906ceaSLachlan Hodges skb_queue_walk_safe(&mq->pending, pfirst, pnext) {
260*b1906ceaSLachlan Hodges struct mm81x_skb_hdr *hdr =
261*b1906ceaSLachlan Hodges (struct mm81x_skb_hdr *)pfirst->data;
262*b1906ceaSLachlan Hodges
263*b1906ceaSLachlan Hodges if (le32_to_cpu(hdr->tx_info.pkt_id) == pkt_id) {
264*b1906ceaSLachlan Hodges ret = pfirst;
265*b1906ceaSLachlan Hodges break;
266*b1906ceaSLachlan Hodges
267*b1906ceaSLachlan Hodges } else if (le32_to_cpu(hdr->tx_info.pkt_id) < pkt_id &&
268*b1906ceaSLachlan Hodges __mm81x_skbq_has_pending_tx_skb_timed_out(pfirst)) {
269*b1906ceaSLachlan Hodges __mm81x_skbq_drop_pending_skb(mq, pfirst);
270*b1906ceaSLachlan Hodges }
271*b1906ceaSLachlan Hodges }
272*b1906ceaSLachlan Hodges
273*b1906ceaSLachlan Hodges return ret;
274*b1906ceaSLachlan Hodges }
275*b1906ceaSLachlan Hodges
mm81x_skbq_check_tx_empty(struct mm81x * mors,struct mm81x_skbq * mq)276*b1906ceaSLachlan Hodges static void mm81x_skbq_check_tx_empty(struct mm81x *mors, struct mm81x_skbq *mq)
277*b1906ceaSLachlan Hodges {
278*b1906ceaSLachlan Hodges lockdep_assert_held(&mq->lock);
279*b1906ceaSLachlan Hodges
280*b1906ceaSLachlan Hodges if (mq->flags & MM81X_HIF_FLAGS_BEACON)
281*b1906ceaSLachlan Hodges return;
282*b1906ceaSLachlan Hodges
283*b1906ceaSLachlan Hodges if (skb_queue_len(&mq->skbq) + skb_queue_len(&mq->pending) == 0)
284*b1906ceaSLachlan Hodges wake_up(&mq->mors->tx_empty_waitq);
285*b1906ceaSLachlan Hodges }
286*b1906ceaSLachlan Hodges
__mm81x_skbq_tx_status_process(struct mm81x * mors,struct mm81x_skbq * mq,struct mm81x_skb_tx_status * tx_sts)287*b1906ceaSLachlan Hodges static void __mm81x_skbq_tx_status_process(struct mm81x *mors,
288*b1906ceaSLachlan Hodges struct mm81x_skbq *mq,
289*b1906ceaSLachlan Hodges struct mm81x_skb_tx_status *tx_sts)
290*b1906ceaSLachlan Hodges {
291*b1906ceaSLachlan Hodges struct sk_buff *skb;
292*b1906ceaSLachlan Hodges
293*b1906ceaSLachlan Hodges lockdep_assert_held(&mq->lock);
294*b1906ceaSLachlan Hodges
295*b1906ceaSLachlan Hodges skb = __mm81x_skbq_get_pending_by_id(mors, mq,
296*b1906ceaSLachlan Hodges le32_to_cpu(tx_sts->pkt_id));
297*b1906ceaSLachlan Hodges if (!skb) {
298*b1906ceaSLachlan Hodges dev_dbg(mors->dev,
299*b1906ceaSLachlan Hodges "No pending pkt match found [pktid:%d chan:%d]",
300*b1906ceaSLachlan Hodges tx_sts->pkt_id, tx_sts->channel);
301*b1906ceaSLachlan Hodges goto out;
302*b1906ceaSLachlan Hodges }
303*b1906ceaSLachlan Hodges
304*b1906ceaSLachlan Hodges if (le32_to_cpu(tx_sts->flags) & MM81X_TX_STATUS_PAGE_INVALID) {
305*b1906ceaSLachlan Hodges __mm81x_skbq_drop_pending_skb(mq, skb);
306*b1906ceaSLachlan Hodges goto out;
307*b1906ceaSLachlan Hodges }
308*b1906ceaSLachlan Hodges
309*b1906ceaSLachlan Hodges if (le32_to_cpu(tx_sts->flags) & MM81X_TX_STATUS_FLAGS_PS_FILTERED &&
310*b1906ceaSLachlan Hodges mm81x_tx_h_is_ps_filtered(mq, skb, tx_sts))
311*b1906ceaSLachlan Hodges /* Has been consumed by mm81x_tx_h_is_ps_filtered */
312*b1906ceaSLachlan Hodges goto out;
313*b1906ceaSLachlan Hodges
314*b1906ceaSLachlan Hodges mm81x_skbq_pull_hdr_post_tx(skb);
315*b1906ceaSLachlan Hodges mm81x_skbq_skb_finish(mq, skb, tx_sts);
316*b1906ceaSLachlan Hodges
317*b1906ceaSLachlan Hodges out:
318*b1906ceaSLachlan Hodges mm81x_skbq_check_tx_empty(mors, mq);
319*b1906ceaSLachlan Hodges }
320*b1906ceaSLachlan Hodges
mm81x_skbq_tx_status_process(struct mm81x * mors,struct sk_buff * skb)321*b1906ceaSLachlan Hodges static void mm81x_skbq_tx_status_process(struct mm81x *mors,
322*b1906ceaSLachlan Hodges struct sk_buff *skb)
323*b1906ceaSLachlan Hodges {
324*b1906ceaSLachlan Hodges int i;
325*b1906ceaSLachlan Hodges struct mm81x_skb_tx_status *tx_sts =
326*b1906ceaSLachlan Hodges (struct mm81x_skb_tx_status *)skb->data;
327*b1906ceaSLachlan Hodges int count = skb->len / sizeof(*tx_sts);
328*b1906ceaSLachlan Hodges
329*b1906ceaSLachlan Hodges for (i = 0; i < count; tx_sts++, i++) {
330*b1906ceaSLachlan Hodges struct mm81x_skbq *mq =
331*b1906ceaSLachlan Hodges __mm81x_skbq_tx_status_to_skbq(mors, tx_sts);
332*b1906ceaSLachlan Hodges
333*b1906ceaSLachlan Hodges if (mq) {
334*b1906ceaSLachlan Hodges spin_lock_bh(&mq->lock);
335*b1906ceaSLachlan Hodges __mm81x_skbq_tx_status_process(mors, mq, tx_sts);
336*b1906ceaSLachlan Hodges spin_unlock_bh(&mq->lock);
337*b1906ceaSLachlan Hodges }
338*b1906ceaSLachlan Hodges }
339*b1906ceaSLachlan Hodges
340*b1906ceaSLachlan Hodges if (mors->ps.enable && !mors->ps.suspended &&
341*b1906ceaSLachlan Hodges (mm81x_hif_get_tx_buffered_count(mors) == 0)) {
342*b1906ceaSLachlan Hodges /* Evaluate ps, check if it was gated on a pending tx status */
343*b1906ceaSLachlan Hodges queue_delayed_work(mors->chip_wq, &mors->ps.delayed_eval_work,
344*b1906ceaSLachlan Hodges 0);
345*b1906ceaSLachlan Hodges }
346*b1906ceaSLachlan Hodges }
347*b1906ceaSLachlan Hodges
mm81x_skbq_dispatch_work(struct work_struct * dispatch_work)348*b1906ceaSLachlan Hodges static void mm81x_skbq_dispatch_work(struct work_struct *dispatch_work)
349*b1906ceaSLachlan Hodges {
350*b1906ceaSLachlan Hodges struct mm81x_skbq *mq =
351*b1906ceaSLachlan Hodges container_of(dispatch_work, struct mm81x_skbq, dispatch_work);
352*b1906ceaSLachlan Hodges struct mm81x *mors = mq->mors;
353*b1906ceaSLachlan Hodges struct mm81x_skb_hdr *hdr;
354*b1906ceaSLachlan Hodges struct sk_buff_head skbq;
355*b1906ceaSLachlan Hodges struct sk_buff *pfirst, *pnext;
356*b1906ceaSLachlan Hodges u8 channel;
357*b1906ceaSLachlan Hodges
358*b1906ceaSLachlan Hodges __skb_queue_head_init(&skbq);
359*b1906ceaSLachlan Hodges
360*b1906ceaSLachlan Hodges mm81x_skbq_deq_num_skb(mq, &skbq, mm81x_skbq_count(mq));
361*b1906ceaSLachlan Hodges
362*b1906ceaSLachlan Hodges skb_queue_walk_safe(&skbq, pfirst, pnext) {
363*b1906ceaSLachlan Hodges __skb_unlink(pfirst, &skbq);
364*b1906ceaSLachlan Hodges /* Header endianness has already be adjusted */
365*b1906ceaSLachlan Hodges hdr = (struct mm81x_skb_hdr *)pfirst->data;
366*b1906ceaSLachlan Hodges channel = hdr->channel;
367*b1906ceaSLachlan Hodges /* Remove mm81x header and padding */
368*b1906ceaSLachlan Hodges __skb_pull(pfirst, sizeof(*hdr) + hdr->offset);
369*b1906ceaSLachlan Hodges
370*b1906ceaSLachlan Hodges switch (channel) {
371*b1906ceaSLachlan Hodges case MM81X_SKB_CHAN_COMMAND:
372*b1906ceaSLachlan Hodges mm81x_cmd_resp_process(mors, pfirst);
373*b1906ceaSLachlan Hodges break;
374*b1906ceaSLachlan Hodges case MM81X_SKB_CHAN_TX_STATUS:
375*b1906ceaSLachlan Hodges mm81x_skbq_tx_status_process(mors, pfirst);
376*b1906ceaSLachlan Hodges dev_kfree_skb_any(pfirst);
377*b1906ceaSLachlan Hodges break;
378*b1906ceaSLachlan Hodges default:
379*b1906ceaSLachlan Hodges mm81x_mac_rx_skb(mors, pfirst, &hdr->rx_status);
380*b1906ceaSLachlan Hodges break;
381*b1906ceaSLachlan Hodges }
382*b1906ceaSLachlan Hodges }
383*b1906ceaSLachlan Hodges
384*b1906ceaSLachlan Hodges if (mm81x_skbq_count(mq))
385*b1906ceaSLachlan Hodges queue_work(mors->net_wq, &mq->dispatch_work);
386*b1906ceaSLachlan Hodges }
387*b1906ceaSLachlan Hodges
mm81x_skbq_put(struct mm81x_skbq * mq,struct sk_buff * skb)388*b1906ceaSLachlan Hodges int mm81x_skbq_put(struct mm81x_skbq *mq, struct sk_buff *skb)
389*b1906ceaSLachlan Hodges {
390*b1906ceaSLachlan Hodges int ret;
391*b1906ceaSLachlan Hodges
392*b1906ceaSLachlan Hodges spin_lock_bh(&mq->lock);
393*b1906ceaSLachlan Hodges ret = __mm81x_skbq_put(mq, &mq->skbq, skb, false, NULL);
394*b1906ceaSLachlan Hodges spin_unlock_bh(&mq->lock);
395*b1906ceaSLachlan Hodges return ret;
396*b1906ceaSLachlan Hodges }
397*b1906ceaSLachlan Hodges
mm81x_skbq_set_queued_tx_skb_expiry(struct sk_buff * skb)398*b1906ceaSLachlan Hodges static void mm81x_skbq_set_queued_tx_skb_expiry(struct sk_buff *skb)
399*b1906ceaSLachlan Hodges {
400*b1906ceaSLachlan Hodges struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
401*b1906ceaSLachlan Hodges struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb);
402*b1906ceaSLachlan Hodges
403*b1906ceaSLachlan Hodges if (ieee80211_is_probe_req(hdr->frame_control) ||
404*b1906ceaSLachlan Hodges ieee80211_is_probe_resp(hdr->frame_control) ||
405*b1906ceaSLachlan Hodges ieee80211_is_auth(hdr->frame_control)) {
406*b1906ceaSLachlan Hodges txi->control.enqueue_time = (u32)jiffies;
407*b1906ceaSLachlan Hodges } else {
408*b1906ceaSLachlan Hodges txi->control.enqueue_time = 0;
409*b1906ceaSLachlan Hodges }
410*b1906ceaSLachlan Hodges }
411*b1906ceaSLachlan Hodges
mm81x_skbq_has_queued_tx_skb_expired(struct sk_buff * skb)412*b1906ceaSLachlan Hodges static bool mm81x_skbq_has_queued_tx_skb_expired(struct sk_buff *skb)
413*b1906ceaSLachlan Hodges {
414*b1906ceaSLachlan Hodges struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb);
415*b1906ceaSLachlan Hodges
416*b1906ceaSLachlan Hodges if (txi->control.enqueue_time > 0) {
417*b1906ceaSLachlan Hodges u32 expiry_time =
418*b1906ceaSLachlan Hodges txi->control.enqueue_time +
419*b1906ceaSLachlan Hodges msecs_to_jiffies(MM81X_SKBQ_TX_QUEUED_LIFETIME_MS);
420*b1906ceaSLachlan Hodges
421*b1906ceaSLachlan Hodges return (s32)((u32)jiffies - expiry_time) > 0;
422*b1906ceaSLachlan Hodges }
423*b1906ceaSLachlan Hodges
424*b1906ceaSLachlan Hodges return false;
425*b1906ceaSLachlan Hodges }
426*b1906ceaSLachlan Hodges
427*b1906ceaSLachlan Hodges /*
428*b1906ceaSLachlan Hodges * Drop selected frames (those with an expiry time set) that could not
429*b1906ceaSLachlan Hodges * be sent within a reasonable timeframe due to congestion. These would
430*b1906ceaSLachlan Hodges * only be rejected or ignored by the peer, so are only contributing to
431*b1906ceaSLachlan Hodges * the problem.
432*b1906ceaSLachlan Hodges */
mm81x_skbq_purge_aged(struct mm81x * mors,struct mm81x_skbq * mq)433*b1906ceaSLachlan Hodges void mm81x_skbq_purge_aged(struct mm81x *mors, struct mm81x_skbq *mq)
434*b1906ceaSLachlan Hodges {
435*b1906ceaSLachlan Hodges struct sk_buff *pfirst;
436*b1906ceaSLachlan Hodges struct sk_buff *pnext;
437*b1906ceaSLachlan Hodges
438*b1906ceaSLachlan Hodges spin_lock_bh(&mq->lock);
439*b1906ceaSLachlan Hodges skb_queue_walk_safe(&mq->skbq, pfirst, pnext) {
440*b1906ceaSLachlan Hodges if (!mm81x_skbq_has_queued_tx_skb_expired(pfirst))
441*b1906ceaSLachlan Hodges break;
442*b1906ceaSLachlan Hodges __mm81x_skbq_unlink(mq, &mq->skbq, pfirst);
443*b1906ceaSLachlan Hodges ieee80211_free_txskb(mors->hw, pfirst);
444*b1906ceaSLachlan Hodges }
445*b1906ceaSLachlan Hodges
446*b1906ceaSLachlan Hodges spin_unlock_bh(&mq->lock);
447*b1906ceaSLachlan Hodges }
448*b1906ceaSLachlan Hodges
mm81x_skbq_purge(struct mm81x_skbq * mq,struct sk_buff_head * skbq)449*b1906ceaSLachlan Hodges void mm81x_skbq_purge(struct mm81x_skbq *mq, struct sk_buff_head *skbq)
450*b1906ceaSLachlan Hodges {
451*b1906ceaSLachlan Hodges struct sk_buff *skb;
452*b1906ceaSLachlan Hodges
453*b1906ceaSLachlan Hodges spin_lock_bh(&mq->lock);
454*b1906ceaSLachlan Hodges while ((skb = __skb_dequeue(skbq)))
455*b1906ceaSLachlan Hodges dev_kfree_skb_any(skb);
456*b1906ceaSLachlan Hodges spin_unlock_bh(&mq->lock);
457*b1906ceaSLachlan Hodges }
458*b1906ceaSLachlan Hodges
mm81x_skbq_enq(struct mm81x_skbq * mq,struct sk_buff_head * skbq)459*b1906ceaSLachlan Hodges void mm81x_skbq_enq(struct mm81x_skbq *mq, struct sk_buff_head *skbq)
460*b1906ceaSLachlan Hodges {
461*b1906ceaSLachlan Hodges int size;
462*b1906ceaSLachlan Hodges struct sk_buff *pfirst, *pnext;
463*b1906ceaSLachlan Hodges
464*b1906ceaSLachlan Hodges spin_lock_bh(&mq->lock);
465*b1906ceaSLachlan Hodges size = __mm81x_skbq_space(mq);
466*b1906ceaSLachlan Hodges skb_queue_walk_safe(skbq, pfirst, pnext) {
467*b1906ceaSLachlan Hodges if (pfirst->len > size)
468*b1906ceaSLachlan Hodges break;
469*b1906ceaSLachlan Hodges __skb_unlink(pfirst, skbq);
470*b1906ceaSLachlan Hodges __mm81x_skbq_put(mq, &mq->skbq, pfirst, false, NULL);
471*b1906ceaSLachlan Hodges size -= pfirst->len;
472*b1906ceaSLachlan Hodges }
473*b1906ceaSLachlan Hodges
474*b1906ceaSLachlan Hodges spin_unlock_bh(&mq->lock);
475*b1906ceaSLachlan Hodges }
476*b1906ceaSLachlan Hodges
mm81x_skbq_deq_num_skb(struct mm81x_skbq * mq,struct sk_buff_head * skbq,int num_skb)477*b1906ceaSLachlan Hodges int mm81x_skbq_deq_num_skb(struct mm81x_skbq *mq, struct sk_buff_head *skbq,
478*b1906ceaSLachlan Hodges int num_skb)
479*b1906ceaSLachlan Hodges {
480*b1906ceaSLachlan Hodges int count = 0;
481*b1906ceaSLachlan Hodges struct sk_buff *pfirst, *pnext;
482*b1906ceaSLachlan Hodges
483*b1906ceaSLachlan Hodges spin_lock_bh(&mq->lock);
484*b1906ceaSLachlan Hodges skb_queue_walk_safe(&mq->skbq, pfirst, pnext) {
485*b1906ceaSLachlan Hodges if (count >= num_skb)
486*b1906ceaSLachlan Hodges break;
487*b1906ceaSLachlan Hodges __mm81x_skbq_unlink(mq, &mq->skbq, pfirst);
488*b1906ceaSLachlan Hodges __skb_queue_tail(skbq, pfirst);
489*b1906ceaSLachlan Hodges ++count;
490*b1906ceaSLachlan Hodges }
491*b1906ceaSLachlan Hodges
492*b1906ceaSLachlan Hodges spin_unlock_bh(&mq->lock);
493*b1906ceaSLachlan Hodges return count;
494*b1906ceaSLachlan Hodges }
495*b1906ceaSLachlan Hodges
mm81x_skbq_enq_prepend(struct mm81x_skbq * mq,struct sk_buff_head * skbq)496*b1906ceaSLachlan Hodges void mm81x_skbq_enq_prepend(struct mm81x_skbq *mq, struct sk_buff_head *skbq)
497*b1906ceaSLachlan Hodges {
498*b1906ceaSLachlan Hodges int size;
499*b1906ceaSLachlan Hodges struct sk_buff *pfirst, *pnext;
500*b1906ceaSLachlan Hodges
501*b1906ceaSLachlan Hodges spin_lock_bh(&mq->lock);
502*b1906ceaSLachlan Hodges size = __mm81x_skbq_space(mq);
503*b1906ceaSLachlan Hodges
504*b1906ceaSLachlan Hodges /*
505*b1906ceaSLachlan Hodges * We are doing a reverse walk here to ensure the order remains the
506*b1906ceaSLachlan Hodges * same. This means the last member of the queue goes in, on top of
507*b1906ceaSLachlan Hodges * the queue first and gets pushed down as more members get added to
508*b1906ceaSLachlan Hodges * the top of the queue.
509*b1906ceaSLachlan Hodges */
510*b1906ceaSLachlan Hodges skb_queue_reverse_walk_safe(skbq, pfirst, pnext) {
511*b1906ceaSLachlan Hodges if (pfirst->len > size)
512*b1906ceaSLachlan Hodges break;
513*b1906ceaSLachlan Hodges __skb_unlink(pfirst, skbq);
514*b1906ceaSLachlan Hodges __mm81x_skbq_put(mq, &mq->skbq, pfirst, true, NULL);
515*b1906ceaSLachlan Hodges size -= pfirst->len;
516*b1906ceaSLachlan Hodges }
517*b1906ceaSLachlan Hodges
518*b1906ceaSLachlan Hodges spin_unlock_bh(&mq->lock);
519*b1906ceaSLachlan Hodges }
520*b1906ceaSLachlan Hodges
mm81x_skbq_stop_tx_queues(struct mm81x * mors)521*b1906ceaSLachlan Hodges static void mm81x_skbq_stop_tx_queues(struct mm81x *mors)
522*b1906ceaSLachlan Hodges {
523*b1906ceaSLachlan Hodges int queue;
524*b1906ceaSLachlan Hodges
525*b1906ceaSLachlan Hodges if (!mors->started)
526*b1906ceaSLachlan Hodges return;
527*b1906ceaSLachlan Hodges for (queue = IEEE80211_AC_VO; queue <= IEEE80211_AC_BK; queue++)
528*b1906ceaSLachlan Hodges ieee80211_stop_queue(mors->hw, queue);
529*b1906ceaSLachlan Hodges
530*b1906ceaSLachlan Hodges set_bit(MM81X_STATE_DATA_QS_STOPPED, &mors->state_flags);
531*b1906ceaSLachlan Hodges }
532*b1906ceaSLachlan Hodges
533*b1906ceaSLachlan Hodges /* Wake all Tx queues if all queues are below threshold */
mm81x_skbq_may_wake_tx_queues(struct mm81x * mors)534*b1906ceaSLachlan Hodges void mm81x_skbq_may_wake_tx_queues(struct mm81x *mors)
535*b1906ceaSLachlan Hodges {
536*b1906ceaSLachlan Hodges int queue;
537*b1906ceaSLachlan Hodges struct mm81x_skbq *qs;
538*b1906ceaSLachlan Hodges int num_qs;
539*b1906ceaSLachlan Hodges bool could_wake;
540*b1906ceaSLachlan Hodges
541*b1906ceaSLachlan Hodges if (!mors->started)
542*b1906ceaSLachlan Hodges return;
543*b1906ceaSLachlan Hodges
544*b1906ceaSLachlan Hodges could_wake = true;
545*b1906ceaSLachlan Hodges mm81x_hif_skbq_get_tx_qs(mors, &qs, &num_qs);
546*b1906ceaSLachlan Hodges for (queue = 0; queue < num_qs; queue++) {
547*b1906ceaSLachlan Hodges struct mm81x_skbq *mq = &qs[queue];
548*b1906ceaSLachlan Hodges
549*b1906ceaSLachlan Hodges if (!could_wake)
550*b1906ceaSLachlan Hodges break;
551*b1906ceaSLachlan Hodges
552*b1906ceaSLachlan Hodges spin_lock_bh(&mq->lock);
553*b1906ceaSLachlan Hodges could_wake &= (__mm81x_skbq_under_threshold(mq));
554*b1906ceaSLachlan Hodges spin_unlock_bh(&mq->lock);
555*b1906ceaSLachlan Hodges }
556*b1906ceaSLachlan Hodges
557*b1906ceaSLachlan Hodges if (!could_wake)
558*b1906ceaSLachlan Hodges return;
559*b1906ceaSLachlan Hodges
560*b1906ceaSLachlan Hodges for (queue = IEEE80211_AC_VO; queue <= IEEE80211_AC_BK; queue++)
561*b1906ceaSLachlan Hodges ieee80211_wake_queue(mors->hw, queue);
562*b1906ceaSLachlan Hodges
563*b1906ceaSLachlan Hodges clear_bit(MM81X_STATE_DATA_QS_STOPPED, &mors->state_flags);
564*b1906ceaSLachlan Hodges }
565*b1906ceaSLachlan Hodges
mm81x_skbq_tx(struct mm81x_skbq * mq,struct sk_buff * skb,u8 channel)566*b1906ceaSLachlan Hodges static int mm81x_skbq_tx(struct mm81x_skbq *mq, struct sk_buff *skb, u8 channel)
567*b1906ceaSLachlan Hodges {
568*b1906ceaSLachlan Hodges int rc;
569*b1906ceaSLachlan Hodges bool mq_over_threshold;
570*b1906ceaSLachlan Hodges struct mm81x *mors = mq->mors;
571*b1906ceaSLachlan Hodges
572*b1906ceaSLachlan Hodges spin_lock_bh(&mq->lock);
573*b1906ceaSLachlan Hodges rc = __mm81x_skbq_put(mq, &mq->skbq, skb, false, NULL);
574*b1906ceaSLachlan Hodges if (rc) {
575*b1906ceaSLachlan Hodges dev_err(mors->dev, "skb put chan %d failed (%d)", channel, rc);
576*b1906ceaSLachlan Hodges if (channel == MM81X_SKB_CHAN_DATA) {
577*b1906ceaSLachlan Hodges u16 queue = skb_get_queue_mapping(skb);
578*b1906ceaSLachlan Hodges
579*b1906ceaSLachlan Hodges dev_err(mors->dev, "skb put queue %d status %d", queue,
580*b1906ceaSLachlan Hodges ieee80211_queue_stopped(mors->hw, queue));
581*b1906ceaSLachlan Hodges }
582*b1906ceaSLachlan Hodges }
583*b1906ceaSLachlan Hodges
584*b1906ceaSLachlan Hodges /* Fill packet ID in TX info */
585*b1906ceaSLachlan Hodges __mm81x_skbq_pkt_id(mq, skb);
586*b1906ceaSLachlan Hodges
587*b1906ceaSLachlan Hodges mq_over_threshold = __mm81x_skbq_over_threshold(mq);
588*b1906ceaSLachlan Hodges spin_unlock_bh(&mq->lock);
589*b1906ceaSLachlan Hodges
590*b1906ceaSLachlan Hodges /* For data packets stop queues */
591*b1906ceaSLachlan Hodges if (channel == MM81X_SKB_CHAN_DATA && mq_over_threshold)
592*b1906ceaSLachlan Hodges mm81x_skbq_stop_tx_queues(mors);
593*b1906ceaSLachlan Hodges
594*b1906ceaSLachlan Hodges switch (channel) {
595*b1906ceaSLachlan Hodges case MM81X_SKB_CHAN_DATA:
596*b1906ceaSLachlan Hodges case MM81X_SKB_CHAN_DATA_NOACK:
597*b1906ceaSLachlan Hodges if (mm81x_is_data_tx_allowed(mors)) {
598*b1906ceaSLachlan Hodges set_bit(MM81X_HIF_EVT_TX_DATA_PEND,
599*b1906ceaSLachlan Hodges &mors->hif.event_flags);
600*b1906ceaSLachlan Hodges queue_work(mors->chip_wq, &mors->hif_work);
601*b1906ceaSLachlan Hodges }
602*b1906ceaSLachlan Hodges break;
603*b1906ceaSLachlan Hodges case MM81X_SKB_CHAN_MGMT:
604*b1906ceaSLachlan Hodges set_bit(MM81X_HIF_EVT_TX_MGMT_PEND, &mors->hif.event_flags);
605*b1906ceaSLachlan Hodges queue_work(mors->chip_wq, &mors->hif_work);
606*b1906ceaSLachlan Hodges break;
607*b1906ceaSLachlan Hodges case MM81X_SKB_CHAN_BEACON:
608*b1906ceaSLachlan Hodges set_bit(MM81X_HIF_EVT_TX_BEACON_PEND, &mors->hif.event_flags);
609*b1906ceaSLachlan Hodges queue_work(mors->chip_wq, &mors->hif_work);
610*b1906ceaSLachlan Hodges break;
611*b1906ceaSLachlan Hodges case MM81X_SKB_CHAN_COMMAND:
612*b1906ceaSLachlan Hodges set_bit(MM81X_HIF_EVT_TX_COMMAND_PEND, &mors->hif.event_flags);
613*b1906ceaSLachlan Hodges queue_work(mors->chip_wq, &mors->hif_work);
614*b1906ceaSLachlan Hodges break;
615*b1906ceaSLachlan Hodges default:
616*b1906ceaSLachlan Hodges dev_err(mors->dev, "Invalid skb channel: %d", channel);
617*b1906ceaSLachlan Hodges break;
618*b1906ceaSLachlan Hodges }
619*b1906ceaSLachlan Hodges
620*b1906ceaSLachlan Hodges return rc;
621*b1906ceaSLachlan Hodges }
622*b1906ceaSLachlan Hodges
__mm81x_skbq_tx_move_to_pending(struct mm81x_skbq * mq,struct sk_buff * skb)623*b1906ceaSLachlan Hodges static void __mm81x_skbq_tx_move_to_pending(struct mm81x_skbq *mq,
624*b1906ceaSLachlan Hodges struct sk_buff *skb)
625*b1906ceaSLachlan Hodges {
626*b1906ceaSLachlan Hodges struct mm81x_tx_status_priv *pend_info =
627*b1906ceaSLachlan Hodges __mm81x_skbq_tx_status_priv(skb);
628*b1906ceaSLachlan Hodges
629*b1906ceaSLachlan Hodges pend_info->tx_status_expiry =
630*b1906ceaSLachlan Hodges jiffies + msecs_to_jiffies(MM81X_SKBQ_TX_STATUS_LIFETIME_MS);
631*b1906ceaSLachlan Hodges __mm81x_skbq_put(mq, &mq->pending, skb, false, NULL);
632*b1906ceaSLachlan Hodges }
633*b1906ceaSLachlan Hodges
mm81x_skbq_tx_complete(struct mm81x_skbq * mq,struct sk_buff_head * skbq)634*b1906ceaSLachlan Hodges void mm81x_skbq_tx_complete(struct mm81x_skbq *mq, struct sk_buff_head *skbq)
635*b1906ceaSLachlan Hodges {
636*b1906ceaSLachlan Hodges bool skb_awaits_tx_status = false;
637*b1906ceaSLachlan Hodges struct mm81x *mors = mq->mors;
638*b1906ceaSLachlan Hodges struct sk_buff *pfirst, *pnext;
639*b1906ceaSLachlan Hodges struct sk_buff *peek = skb_peek(skbq);
640*b1906ceaSLachlan Hodges struct mm81x_skb_hdr *hdr;
641*b1906ceaSLachlan Hodges const bool fw_reports_bcn_tx_status =
642*b1906ceaSLachlan Hodges mors->fw_flags & MM81X_FW_FLAGS_REPORTS_TX_BEACON_COMPLETION;
643*b1906ceaSLachlan Hodges
644*b1906ceaSLachlan Hodges if (!peek)
645*b1906ceaSLachlan Hodges return;
646*b1906ceaSLachlan Hodges
647*b1906ceaSLachlan Hodges /* Move sent packets to pending list waiting for feedback */
648*b1906ceaSLachlan Hodges spin_lock_bh(&mq->lock);
649*b1906ceaSLachlan Hodges skb_queue_walk_safe(skbq, pfirst, pnext) {
650*b1906ceaSLachlan Hodges __skb_unlink(pfirst, skbq);
651*b1906ceaSLachlan Hodges hdr = (struct mm81x_skb_hdr *)pfirst->data;
652*b1906ceaSLachlan Hodges /*
653*b1906ceaSLachlan Hodges * If firmware doesn't give status on beacons just free
654*b1906ceaSLachlan Hodges * them, otherwise queue and wait for response.
655*b1906ceaSLachlan Hodges */
656*b1906ceaSLachlan Hodges switch (hdr->channel) {
657*b1906ceaSLachlan Hodges case MM81X_SKB_CHAN_BEACON:
658*b1906ceaSLachlan Hodges if (fw_reports_bcn_tx_status) {
659*b1906ceaSLachlan Hodges __mm81x_skbq_tx_move_to_pending(mq, pfirst);
660*b1906ceaSLachlan Hodges skb_awaits_tx_status = true;
661*b1906ceaSLachlan Hodges break;
662*b1906ceaSLachlan Hodges }
663*b1906ceaSLachlan Hodges /*
664*b1906ceaSLachlan Hodges * If the FW doesn't give statuses on beacon's,
665*b1906ceaSLachlan Hodges * then mark them as done.
666*b1906ceaSLachlan Hodges */
667*b1906ceaSLachlan Hodges mm81x_skbq_pull_hdr_post_tx(pfirst);
668*b1906ceaSLachlan Hodges dev_kfree_skb_any(pfirst);
669*b1906ceaSLachlan Hodges break;
670*b1906ceaSLachlan Hodges default:
671*b1906ceaSLachlan Hodges if (le32_to_cpu(hdr->tx_info.flags) &
672*b1906ceaSLachlan Hodges MM81X_TX_STATUS_FLAGS_NO_REPORT) {
673*b1906ceaSLachlan Hodges dev_kfree_skb_any(pfirst);
674*b1906ceaSLachlan Hodges } else {
675*b1906ceaSLachlan Hodges /*
676*b1906ceaSLachlan Hodges * skb has been given to the chip. Store the
677*b1906ceaSLachlan Hodges * time and queue the skb onto the pending
678*b1906ceaSLachlan Hodges * queue while we wait for the tx_status.
679*b1906ceaSLachlan Hodges */
680*b1906ceaSLachlan Hodges __mm81x_skbq_tx_move_to_pending(mq, pfirst);
681*b1906ceaSLachlan Hodges skb_awaits_tx_status = true;
682*b1906ceaSLachlan Hodges }
683*b1906ceaSLachlan Hodges break;
684*b1906ceaSLachlan Hodges }
685*b1906ceaSLachlan Hodges }
686*b1906ceaSLachlan Hodges spin_unlock_bh(&mq->lock);
687*b1906ceaSLachlan Hodges
688*b1906ceaSLachlan Hodges if (skb_awaits_tx_status) {
689*b1906ceaSLachlan Hodges spin_lock_bh(&mors->stale_status.lock);
690*b1906ceaSLachlan Hodges mod_timer(&mors->stale_status.timer,
691*b1906ceaSLachlan Hodges jiffies + msecs_to_jiffies(
692*b1906ceaSLachlan Hodges MM81X_SKBQ_TX_STATUS_LIFETIME_MS));
693*b1906ceaSLachlan Hodges spin_unlock_bh(&mors->stale_status.lock);
694*b1906ceaSLachlan Hodges }
695*b1906ceaSLachlan Hodges }
696*b1906ceaSLachlan Hodges
697*b1906ceaSLachlan Hodges /* Returns the first skb in the pending list. */
mm81x_skbq_tx_pending(struct mm81x_skbq * mq)698*b1906ceaSLachlan Hodges struct sk_buff *mm81x_skbq_tx_pending(struct mm81x_skbq *mq)
699*b1906ceaSLachlan Hodges {
700*b1906ceaSLachlan Hodges struct sk_buff *pfirst;
701*b1906ceaSLachlan Hodges
702*b1906ceaSLachlan Hodges spin_lock_bh(&mq->lock);
703*b1906ceaSLachlan Hodges pfirst = skb_peek(&mq->pending);
704*b1906ceaSLachlan Hodges spin_unlock_bh(&mq->lock);
705*b1906ceaSLachlan Hodges return pfirst;
706*b1906ceaSLachlan Hodges }
707*b1906ceaSLachlan Hodges
mm81x_skbq_check_for_stale_tx(struct mm81x * mors,struct mm81x_skbq * mq)708*b1906ceaSLachlan Hodges int mm81x_skbq_check_for_stale_tx(struct mm81x *mors, struct mm81x_skbq *mq)
709*b1906ceaSLachlan Hodges {
710*b1906ceaSLachlan Hodges int flushed = 0;
711*b1906ceaSLachlan Hodges struct sk_buff *pfirst;
712*b1906ceaSLachlan Hodges struct sk_buff *pnext;
713*b1906ceaSLachlan Hodges
714*b1906ceaSLachlan Hodges if (!skb_queue_len(&mq->pending))
715*b1906ceaSLachlan Hodges return 0;
716*b1906ceaSLachlan Hodges
717*b1906ceaSLachlan Hodges /* Move sent packets to pending list waiting for feedback */
718*b1906ceaSLachlan Hodges spin_lock_bh(&mq->lock);
719*b1906ceaSLachlan Hodges skb_queue_walk_safe(&mq->pending, pfirst, pnext) {
720*b1906ceaSLachlan Hodges struct mm81x_skb_hdr *hdr =
721*b1906ceaSLachlan Hodges (struct mm81x_skb_hdr *)pfirst->data;
722*b1906ceaSLachlan Hodges
723*b1906ceaSLachlan Hodges if (__mm81x_skbq_has_pending_tx_skb_timed_out(pfirst)) {
724*b1906ceaSLachlan Hodges dev_dbg(mors->dev, "TX skb timed out [id:%d,chan:%d]",
725*b1906ceaSLachlan Hodges hdr->tx_info.pkt_id, hdr->channel);
726*b1906ceaSLachlan Hodges
727*b1906ceaSLachlan Hodges __mm81x_skbq_drop_pending_skb(mq, pfirst);
728*b1906ceaSLachlan Hodges flushed++;
729*b1906ceaSLachlan Hodges }
730*b1906ceaSLachlan Hodges }
731*b1906ceaSLachlan Hodges
732*b1906ceaSLachlan Hodges if (flushed)
733*b1906ceaSLachlan Hodges mm81x_skbq_check_tx_empty(mors, mq);
734*b1906ceaSLachlan Hodges
735*b1906ceaSLachlan Hodges spin_unlock_bh(&mq->lock);
736*b1906ceaSLachlan Hodges return flushed;
737*b1906ceaSLachlan Hodges }
738*b1906ceaSLachlan Hodges
739*b1906ceaSLachlan Hodges /* Remove commands from pending (or skbq if not sent) */
__skbq_cmd_finish(struct mm81x_skbq * mq,struct sk_buff * skb)740*b1906ceaSLachlan Hodges static void __skbq_cmd_finish(struct mm81x_skbq *mq, struct sk_buff *skb)
741*b1906ceaSLachlan Hodges {
742*b1906ceaSLachlan Hodges struct mm81x *mors = mq->mors;
743*b1906ceaSLachlan Hodges
744*b1906ceaSLachlan Hodges if (skb_queue_len(&mq->pending)) {
745*b1906ceaSLachlan Hodges __mm81x_skbq_unlink(mq, &mq->pending, skb);
746*b1906ceaSLachlan Hodges dev_kfree_skb(skb);
747*b1906ceaSLachlan Hodges } else if (skb_queue_len(&mq->skbq)) {
748*b1906ceaSLachlan Hodges /* Command was probably timed out before being sent */
749*b1906ceaSLachlan Hodges dev_dbg(mors->dev,
750*b1906ceaSLachlan Hodges "Command pending queue empty. Removing from SKBQ.");
751*b1906ceaSLachlan Hodges __mm81x_skbq_unlink(mq, &mq->skbq, skb);
752*b1906ceaSLachlan Hodges dev_kfree_skb(skb);
753*b1906ceaSLachlan Hodges } else {
754*b1906ceaSLachlan Hodges dev_dbg(mors->dev, "Command Q not found");
755*b1906ceaSLachlan Hodges }
756*b1906ceaSLachlan Hodges }
757*b1906ceaSLachlan Hodges
758*b1906ceaSLachlan Hodges struct mm81x_update_sta_iter_data {
759*b1906ceaSLachlan Hodges struct mm81x *mors;
760*b1906ceaSLachlan Hodges struct sk_buff *skb;
761*b1906ceaSLachlan Hodges struct mm81x_skb_tx_status *tx_sts;
762*b1906ceaSLachlan Hodges int tx_attempts;
763*b1906ceaSLachlan Hodges bool updated;
764*b1906ceaSLachlan Hodges };
765*b1906ceaSLachlan Hodges
mm81x_tx_h_update_sta_iter(void * data,u8 * mac,struct ieee80211_vif * vif)766*b1906ceaSLachlan Hodges static void mm81x_tx_h_update_sta_iter(void *data, u8 *mac,
767*b1906ceaSLachlan Hodges struct ieee80211_vif *vif)
768*b1906ceaSLachlan Hodges {
769*b1906ceaSLachlan Hodges struct mm81x_update_sta_iter_data *iter = data;
770*b1906ceaSLachlan Hodges struct ieee80211_hdr *hdr;
771*b1906ceaSLachlan Hodges struct ieee80211_sta *sta;
772*b1906ceaSLachlan Hodges
773*b1906ceaSLachlan Hodges if (iter->updated || !iter->skb || !iter->skb->data)
774*b1906ceaSLachlan Hodges return;
775*b1906ceaSLachlan Hodges
776*b1906ceaSLachlan Hodges hdr = (struct ieee80211_hdr *)iter->skb->data;
777*b1906ceaSLachlan Hodges
778*b1906ceaSLachlan Hodges /*
779*b1906ceaSLachlan Hodges * Note that each iteration via
780*b1906ceaSLachlan Hodges * ieee80211_iterate_active_interfaces_atomic is under an RCU critical
781*b1906ceaSLachlan Hodges * section so there is no need for a local critical section within here
782*b1906ceaSLachlan Hodges * when looking up the station.
783*b1906ceaSLachlan Hodges */
784*b1906ceaSLachlan Hodges sta = ieee80211_find_sta(vif, hdr->addr1);
785*b1906ceaSLachlan Hodges if (!sta)
786*b1906ceaSLachlan Hodges return;
787*b1906ceaSLachlan Hodges
788*b1906ceaSLachlan Hodges mm81x_rc_sta_feedback_rates(iter->mors, iter->skb, sta, iter->tx_sts,
789*b1906ceaSLachlan Hodges iter->tx_attempts);
790*b1906ceaSLachlan Hodges mm81x_tx_h_check_aggr(sta, iter->skb);
791*b1906ceaSLachlan Hodges
792*b1906ceaSLachlan Hodges /*
793*b1906ceaSLachlan Hodges * In situations with multiple virtual interfaces, finish iteration
794*b1906ceaSLachlan Hodges * once we have found our STA to prevent further iteration.
795*b1906ceaSLachlan Hodges */
796*b1906ceaSLachlan Hodges iter->updated = true;
797*b1906ceaSLachlan Hodges }
798*b1906ceaSLachlan Hodges
799*b1906ceaSLachlan Hodges /* TX status/Response received remove packet from pending TX finish */
__skbq_data_tx_finish(struct mm81x_skbq * mq,struct sk_buff * skb,struct mm81x_skb_tx_status * tx_sts)800*b1906ceaSLachlan Hodges static void __skbq_data_tx_finish(struct mm81x_skbq *mq, struct sk_buff *skb,
801*b1906ceaSLachlan Hodges struct mm81x_skb_tx_status *tx_sts)
802*b1906ceaSLachlan Hodges {
803*b1906ceaSLachlan Hodges struct mm81x *mors = mq->mors;
804*b1906ceaSLachlan Hodges struct mm81x_update_sta_iter_data iter = {};
805*b1906ceaSLachlan Hodges
806*b1906ceaSLachlan Hodges __mm81x_skbq_unlink(mq, &mq->pending, skb);
807*b1906ceaSLachlan Hodges iter.mors = mors;
808*b1906ceaSLachlan Hodges iter.skb = skb;
809*b1906ceaSLachlan Hodges iter.tx_sts = tx_sts;
810*b1906ceaSLachlan Hodges iter.tx_attempts = mm81x_tx_h_get_attempts(mors, tx_sts);
811*b1906ceaSLachlan Hodges
812*b1906ceaSLachlan Hodges ieee80211_iterate_active_interfaces_atomic(mors->hw,
813*b1906ceaSLachlan Hodges IEEE80211_IFACE_ITER_NORMAL,
814*b1906ceaSLachlan Hodges mm81x_tx_h_update_sta_iter,
815*b1906ceaSLachlan Hodges &iter);
816*b1906ceaSLachlan Hodges
817*b1906ceaSLachlan Hodges ieee80211_tx_status_skb(mors->hw, skb);
818*b1906ceaSLachlan Hodges }
819*b1906ceaSLachlan Hodges
mm81x_skbq_skb_finish(struct mm81x_skbq * mq,struct sk_buff * skb,struct mm81x_skb_tx_status * tx_sts)820*b1906ceaSLachlan Hodges void mm81x_skbq_skb_finish(struct mm81x_skbq *mq, struct sk_buff *skb,
821*b1906ceaSLachlan Hodges struct mm81x_skb_tx_status *tx_sts)
822*b1906ceaSLachlan Hodges {
823*b1906ceaSLachlan Hodges if (mq->flags & MM81X_HIF_FLAGS_COMMAND)
824*b1906ceaSLachlan Hodges __skbq_cmd_finish(mq, skb);
825*b1906ceaSLachlan Hodges else
826*b1906ceaSLachlan Hodges __skbq_data_tx_finish(mq, skb, tx_sts);
827*b1906ceaSLachlan Hodges }
828*b1906ceaSLachlan Hodges
mm81x_skbq_tx_flush(struct mm81x_skbq * mq)829*b1906ceaSLachlan Hodges void mm81x_skbq_tx_flush(struct mm81x_skbq *mq)
830*b1906ceaSLachlan Hodges {
831*b1906ceaSLachlan Hodges struct sk_buff *pfirst, *pnext;
832*b1906ceaSLachlan Hodges
833*b1906ceaSLachlan Hodges spin_lock_bh(&mq->lock);
834*b1906ceaSLachlan Hodges skb_queue_walk_safe(&mq->pending, pfirst, pnext) {
835*b1906ceaSLachlan Hodges __mm81x_skbq_unlink(mq, &mq->pending, pfirst);
836*b1906ceaSLachlan Hodges ieee80211_free_txskb(mq->mors->hw, pfirst);
837*b1906ceaSLachlan Hodges }
838*b1906ceaSLachlan Hodges
839*b1906ceaSLachlan Hodges skb_queue_walk_safe(&mq->skbq, pfirst, pnext) {
840*b1906ceaSLachlan Hodges __mm81x_skbq_unlink(mq, &mq->skbq, pfirst);
841*b1906ceaSLachlan Hodges ieee80211_free_txskb(mq->mors->hw, pfirst);
842*b1906ceaSLachlan Hodges }
843*b1906ceaSLachlan Hodges spin_unlock_bh(&mq->lock);
844*b1906ceaSLachlan Hodges }
845*b1906ceaSLachlan Hodges
mm81x_skbq_init(struct mm81x * mors,struct mm81x_skbq * mq,u16 flags)846*b1906ceaSLachlan Hodges void mm81x_skbq_init(struct mm81x *mors, struct mm81x_skbq *mq, u16 flags)
847*b1906ceaSLachlan Hodges {
848*b1906ceaSLachlan Hodges spin_lock_init(&mq->lock);
849*b1906ceaSLachlan Hodges __skb_queue_head_init(&mq->skbq);
850*b1906ceaSLachlan Hodges __skb_queue_head_init(&mq->pending);
851*b1906ceaSLachlan Hodges mq->mors = mors;
852*b1906ceaSLachlan Hodges mq->skbq_size = 0;
853*b1906ceaSLachlan Hodges mq->flags = flags;
854*b1906ceaSLachlan Hodges mq->pkt_seq = 0;
855*b1906ceaSLachlan Hodges if (flags & MM81X_HIF_FLAGS_DIR_TO_HOST)
856*b1906ceaSLachlan Hodges INIT_WORK(&mq->dispatch_work, mm81x_skbq_dispatch_work);
857*b1906ceaSLachlan Hodges }
858*b1906ceaSLachlan Hodges
mm81x_skbq_finish(struct mm81x_skbq * mq)859*b1906ceaSLachlan Hodges void mm81x_skbq_finish(struct mm81x_skbq *mq)
860*b1906ceaSLachlan Hodges {
861*b1906ceaSLachlan Hodges if (mq->skbq_size > 0)
862*b1906ceaSLachlan Hodges dev_dbg(mq->mors->dev,
863*b1906ceaSLachlan Hodges "Purging a non empty MorseQ. Dropping data!");
864*b1906ceaSLachlan Hodges
865*b1906ceaSLachlan Hodges /* Clean up link to hif */
866*b1906ceaSLachlan Hodges if (mq->flags & MM81X_HIF_FLAGS_DIR_TO_HOST)
867*b1906ceaSLachlan Hodges cancel_work_sync(&mq->dispatch_work);
868*b1906ceaSLachlan Hodges mm81x_skbq_purge(mq, &mq->skbq);
869*b1906ceaSLachlan Hodges mm81x_skbq_purge(mq, &mq->pending);
870*b1906ceaSLachlan Hodges mq->skbq_size = 0;
871*b1906ceaSLachlan Hodges }
872*b1906ceaSLachlan Hodges
mm81x_skbq_size(struct mm81x_skbq * mq)873*b1906ceaSLachlan Hodges u32 mm81x_skbq_size(struct mm81x_skbq *mq)
874*b1906ceaSLachlan Hodges {
875*b1906ceaSLachlan Hodges u32 count;
876*b1906ceaSLachlan Hodges
877*b1906ceaSLachlan Hodges spin_lock_bh(&mq->lock);
878*b1906ceaSLachlan Hodges count = __mm81x_skbq_size(mq);
879*b1906ceaSLachlan Hodges spin_unlock_bh(&mq->lock);
880*b1906ceaSLachlan Hodges return count;
881*b1906ceaSLachlan Hodges }
882*b1906ceaSLachlan Hodges
mm81x_skbq_count(struct mm81x_skbq * mq)883*b1906ceaSLachlan Hodges u32 mm81x_skbq_count(struct mm81x_skbq *mq)
884*b1906ceaSLachlan Hodges {
885*b1906ceaSLachlan Hodges u32 count = 0;
886*b1906ceaSLachlan Hodges
887*b1906ceaSLachlan Hodges spin_lock_bh(&mq->lock);
888*b1906ceaSLachlan Hodges count += skb_queue_len(&mq->skbq);
889*b1906ceaSLachlan Hodges spin_unlock_bh(&mq->lock);
890*b1906ceaSLachlan Hodges return count;
891*b1906ceaSLachlan Hodges }
892*b1906ceaSLachlan Hodges
mm81x_skbq_pending_count(struct mm81x_skbq * mq)893*b1906ceaSLachlan Hodges u32 mm81x_skbq_pending_count(struct mm81x_skbq *mq)
894*b1906ceaSLachlan Hodges {
895*b1906ceaSLachlan Hodges u32 count;
896*b1906ceaSLachlan Hodges
897*b1906ceaSLachlan Hodges spin_lock_bh(&mq->lock);
898*b1906ceaSLachlan Hodges count = skb_queue_len(&mq->pending);
899*b1906ceaSLachlan Hodges spin_unlock_bh(&mq->lock);
900*b1906ceaSLachlan Hodges return count;
901*b1906ceaSLachlan Hodges }
902*b1906ceaSLachlan Hodges
mm81x_skbq_count_tx_ready(struct mm81x_skbq * mq)903*b1906ceaSLachlan Hodges u32 mm81x_skbq_count_tx_ready(struct mm81x_skbq *mq)
904*b1906ceaSLachlan Hodges {
905*b1906ceaSLachlan Hodges struct mm81x *mors = mq->mors;
906*b1906ceaSLachlan Hodges
907*b1906ceaSLachlan Hodges if (!mm81x_is_data_tx_allowed(mors))
908*b1906ceaSLachlan Hodges return 0;
909*b1906ceaSLachlan Hodges
910*b1906ceaSLachlan Hodges return mm81x_skbq_count(mq);
911*b1906ceaSLachlan Hodges }
912*b1906ceaSLachlan Hodges
mm81x_skbq_space(struct mm81x_skbq * mq)913*b1906ceaSLachlan Hodges u32 mm81x_skbq_space(struct mm81x_skbq *mq)
914*b1906ceaSLachlan Hodges {
915*b1906ceaSLachlan Hodges u32 space;
916*b1906ceaSLachlan Hodges
917*b1906ceaSLachlan Hodges spin_lock_bh(&mq->lock);
918*b1906ceaSLachlan Hodges space = __mm81x_skbq_space(mq);
919*b1906ceaSLachlan Hodges spin_unlock_bh(&mq->lock);
920*b1906ceaSLachlan Hodges
921*b1906ceaSLachlan Hodges return space;
922*b1906ceaSLachlan Hodges }
923*b1906ceaSLachlan Hodges
mm81x_skbq_alloc_skb(struct mm81x_skbq * mq,unsigned int length)924*b1906ceaSLachlan Hodges struct sk_buff *mm81x_skbq_alloc_skb(struct mm81x_skbq *mq, unsigned int length)
925*b1906ceaSLachlan Hodges {
926*b1906ceaSLachlan Hodges struct sk_buff *skb;
927*b1906ceaSLachlan Hodges int tx_headroom = sizeof(struct mm81x_skb_hdr) +
928*b1906ceaSLachlan Hodges mm81x_bus_get_alignment(mq->mors);
929*b1906ceaSLachlan Hodges int skb_len = tx_headroom + length + MM81X_PAD4(length);
930*b1906ceaSLachlan Hodges
931*b1906ceaSLachlan Hodges skb = dev_alloc_skb(skb_len);
932*b1906ceaSLachlan Hodges if (!skb)
933*b1906ceaSLachlan Hodges return NULL;
934*b1906ceaSLachlan Hodges
935*b1906ceaSLachlan Hodges skb_reserve(skb, tx_headroom);
936*b1906ceaSLachlan Hodges skb_put(skb, length);
937*b1906ceaSLachlan Hodges return skb;
938*b1906ceaSLachlan Hodges }
939*b1906ceaSLachlan Hodges
mm81x_skb_tx_h_validate_channel(const struct mm81x * mors,u8 channel)940*b1906ceaSLachlan Hodges static int mm81x_skb_tx_h_validate_channel(const struct mm81x *mors, u8 channel)
941*b1906ceaSLachlan Hodges {
942*b1906ceaSLachlan Hodges if (channel == MM81X_SKB_CHAN_COMMAND) {
943*b1906ceaSLachlan Hodges if (test_bit(MM81X_STATE_HOST_TO_CHIP_CMD_BLOCKED,
944*b1906ceaSLachlan Hodges &mors->state_flags))
945*b1906ceaSLachlan Hodges return -EPERM;
946*b1906ceaSLachlan Hodges } else {
947*b1906ceaSLachlan Hodges if (test_bit(MM81X_STATE_HOST_TO_CHIP_TX_BLOCKED,
948*b1906ceaSLachlan Hodges &mors->state_flags))
949*b1906ceaSLachlan Hodges return -EPERM;
950*b1906ceaSLachlan Hodges }
951*b1906ceaSLachlan Hodges
952*b1906ceaSLachlan Hodges return 0;
953*b1906ceaSLachlan Hodges }
954*b1906ceaSLachlan Hodges
mm81x_skbq_skb_tx(struct mm81x_skbq * mq,struct sk_buff ** skb_orig,struct mm81x_skb_tx_info * tx_info,u8 channel)955*b1906ceaSLachlan Hodges int mm81x_skbq_skb_tx(struct mm81x_skbq *mq, struct sk_buff **skb_orig,
956*b1906ceaSLachlan Hodges struct mm81x_skb_tx_info *tx_info, u8 channel)
957*b1906ceaSLachlan Hodges {
958*b1906ceaSLachlan Hodges int ret;
959*b1906ceaSLachlan Hodges struct mm81x_skb_hdr hdr;
960*b1906ceaSLachlan Hodges struct mm81x *mors = mq->mors;
961*b1906ceaSLachlan Hodges size_t end_of_skb_pad;
962*b1906ceaSLachlan Hodges struct sk_buff *skb = *skb_orig;
963*b1906ceaSLachlan Hodges u8 *aligned_head, *data;
964*b1906ceaSLachlan Hodges
965*b1906ceaSLachlan Hodges if (test_bit(MM81X_STATE_CHIP_UNRESPONSIVE, &mors->state_flags)) {
966*b1906ceaSLachlan Hodges dev_kfree_skb_any(skb);
967*b1906ceaSLachlan Hodges return -ENODEV;
968*b1906ceaSLachlan Hodges }
969*b1906ceaSLachlan Hodges
970*b1906ceaSLachlan Hodges ret = mm81x_skb_tx_h_validate_channel(mors, channel);
971*b1906ceaSLachlan Hodges if (ret) {
972*b1906ceaSLachlan Hodges dev_kfree_skb_any(skb);
973*b1906ceaSLachlan Hodges return ret;
974*b1906ceaSLachlan Hodges }
975*b1906ceaSLachlan Hodges
976*b1906ceaSLachlan Hodges mm81x_skbq_set_queued_tx_skb_expiry(skb);
977*b1906ceaSLachlan Hodges
978*b1906ceaSLachlan Hodges data = skb->data;
979*b1906ceaSLachlan Hodges aligned_head = PTR_ALIGN_DOWN((data - sizeof(hdr)),
980*b1906ceaSLachlan Hodges mm81x_bus_get_alignment(mors));
981*b1906ceaSLachlan Hodges hdr.sync = MM81X_SKB_HEADER_SYNC;
982*b1906ceaSLachlan Hodges hdr.channel = channel;
983*b1906ceaSLachlan Hodges hdr.len = cpu_to_le16(skb->len);
984*b1906ceaSLachlan Hodges hdr.offset = data - (aligned_head + sizeof(hdr));
985*b1906ceaSLachlan Hodges hdr.checksum_upper = 0;
986*b1906ceaSLachlan Hodges hdr.checksum_lower = 0;
987*b1906ceaSLachlan Hodges if (tx_info)
988*b1906ceaSLachlan Hodges memcpy(&hdr.tx_info, tx_info, sizeof(*tx_info));
989*b1906ceaSLachlan Hodges else
990*b1906ceaSLachlan Hodges memset(&hdr.tx_info, 0, sizeof(hdr.tx_info));
991*b1906ceaSLachlan Hodges
992*b1906ceaSLachlan Hodges skb_push(skb, data - aligned_head);
993*b1906ceaSLachlan Hodges memcpy(skb->data, &hdr, sizeof(hdr));
994*b1906ceaSLachlan Hodges
995*b1906ceaSLachlan Hodges end_of_skb_pad = MM81X_PAD4(skb->len);
996*b1906ceaSLachlan Hodges if (end_of_skb_pad && skb_pad(skb, end_of_skb_pad))
997*b1906ceaSLachlan Hodges return -EINVAL;
998*b1906ceaSLachlan Hodges
999*b1906ceaSLachlan Hodges ret = mm81x_skbq_tx(mq, skb, channel);
1000*b1906ceaSLachlan Hodges if (ret) {
1001*b1906ceaSLachlan Hodges dev_err(mors->dev, "mm81x_skbq_tx fail: %d", ret);
1002*b1906ceaSLachlan Hodges dev_kfree_skb_any(skb);
1003*b1906ceaSLachlan Hodges }
1004*b1906ceaSLachlan Hodges
1005*b1906ceaSLachlan Hodges return ret;
1006*b1906ceaSLachlan Hodges }
1007*b1906ceaSLachlan Hodges
mm81x_skbq_data_traffic_pause(struct mm81x * mors)1008*b1906ceaSLachlan Hodges void mm81x_skbq_data_traffic_pause(struct mm81x *mors)
1009*b1906ceaSLachlan Hodges {
1010*b1906ceaSLachlan Hodges set_bit(MM81X_STATE_DATA_TX_STOPPED, &mors->state_flags);
1011*b1906ceaSLachlan Hodges /* power-save requirements will be re-evaluated by the caller */
1012*b1906ceaSLachlan Hodges }
1013*b1906ceaSLachlan Hodges
mm81x_skbq_data_traffic_resume(struct mm81x * mors)1014*b1906ceaSLachlan Hodges void mm81x_skbq_data_traffic_resume(struct mm81x *mors)
1015*b1906ceaSLachlan Hodges {
1016*b1906ceaSLachlan Hodges clear_bit(MM81X_STATE_DATA_TX_STOPPED, &mors->state_flags);
1017*b1906ceaSLachlan Hodges
1018*b1906ceaSLachlan Hodges /* Set the TX_DATA_PEND bit. This will kick the transmission path to
1019*b1906ceaSLachlan Hodges * send any frames pending in the TX buffers, and wake the mac80211
1020*b1906ceaSLachlan Hodges * data Qs if they were previously stopped.
1021*b1906ceaSLachlan Hodges */
1022*b1906ceaSLachlan Hodges set_bit(MM81X_HIF_EVT_TX_DATA_PEND, &mors->hif.event_flags);
1023*b1906ceaSLachlan Hodges }
1024*b1906ceaSLachlan Hodges
mm81x_skbq_validate_checksum(u8 * data)1025*b1906ceaSLachlan Hodges bool mm81x_skbq_validate_checksum(u8 *data)
1026*b1906ceaSLachlan Hodges {
1027*b1906ceaSLachlan Hodges int i;
1028*b1906ceaSLachlan Hodges u32 xor = 0;
1029*b1906ceaSLachlan Hodges struct mm81x_skb_hdr *skb_hdr = (struct mm81x_skb_hdr *)data;
1030*b1906ceaSLachlan Hodges struct ieee80211_hdr *hdr =
1031*b1906ceaSLachlan Hodges (struct ieee80211_hdr *)(data + sizeof(*skb_hdr));
1032*b1906ceaSLachlan Hodges u16 len = le16_to_cpu(skb_hdr->len) + sizeof(*skb_hdr);
1033*b1906ceaSLachlan Hodges u32 *data_to_xor = (u32 *)data;
1034*b1906ceaSLachlan Hodges u32 header_xor = (le16_to_cpu(skb_hdr->checksum_upper) << 8) |
1035*b1906ceaSLachlan Hodges (skb_hdr->checksum_lower);
1036*b1906ceaSLachlan Hodges
1037*b1906ceaSLachlan Hodges /*
1038*b1906ceaSLachlan Hodges * For data frames the calculate the xor for skb header, mac header
1039*b1906ceaSLachlan Hodges * and ccmp header. For all other channel the xor is calculated for
1040*b1906ceaSLachlan Hodges * the full skb.
1041*b1906ceaSLachlan Hodges */
1042*b1906ceaSLachlan Hodges if (skb_hdr->channel == MM81X_SKB_CHAN_DATA &&
1043*b1906ceaSLachlan Hodges (ieee80211_is_data(hdr->frame_control) ||
1044*b1906ceaSLachlan Hodges ieee80211_is_data_qos(hdr->frame_control))) {
1045*b1906ceaSLachlan Hodges u16 data_len = sizeof(*skb_hdr) +
1046*b1906ceaSLachlan Hodges sizeof(struct ieee80211_qos_hdr) +
1047*b1906ceaSLachlan Hodges IEEE80211_CCMP_HDR_LEN;
1048*b1906ceaSLachlan Hodges
1049*b1906ceaSLachlan Hodges len = min(len, data_len);
1050*b1906ceaSLachlan Hodges len = ROUND_DOWN_TO_WORD(len);
1051*b1906ceaSLachlan Hodges }
1052*b1906ceaSLachlan Hodges
1053*b1906ceaSLachlan Hodges skb_hdr->checksum_upper = 0;
1054*b1906ceaSLachlan Hodges skb_hdr->checksum_lower = 0;
1055*b1906ceaSLachlan Hodges
1056*b1906ceaSLachlan Hodges for (i = 0; i < len; i += 4) {
1057*b1906ceaSLachlan Hodges xor ^= *data_to_xor;
1058*b1906ceaSLachlan Hodges data_to_xor++;
1059*b1906ceaSLachlan Hodges }
1060*b1906ceaSLachlan Hodges
1061*b1906ceaSLachlan Hodges xor &= 0x00FFFFFF;
1062*b1906ceaSLachlan Hodges
1063*b1906ceaSLachlan Hodges return xor == header_xor;
1064*b1906ceaSLachlan Hodges }
1065