xref: /linux/drivers/net/wireless/morsemicro/mm81x/skbq.c (revision 91ec2035134982b98fab0609a9fd8480e8217dc1)
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