1/*
2 * Copyright (c) 2014 Qualcomm Atheros, Inc.
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include "wil6210.h"
18#include "txrx.h"
19
20#define SEQ_MODULO 0x1000
21#define SEQ_MASK   0xfff
22
23static inline int seq_less(u16 sq1, u16 sq2)
24{
25	return ((sq1 - sq2) & SEQ_MASK) > (SEQ_MODULO >> 1);
26}
27
28static inline u16 seq_inc(u16 sq)
29{
30	return (sq + 1) & SEQ_MASK;
31}
32
33static inline u16 seq_sub(u16 sq1, u16 sq2)
34{
35	return (sq1 - sq2) & SEQ_MASK;
36}
37
38static inline int reorder_index(struct wil_tid_ampdu_rx *r, u16 seq)
39{
40	return seq_sub(seq, r->ssn) % r->buf_size;
41}
42
43static void wil_release_reorder_frame(struct wil6210_priv *wil,
44				      struct wil_tid_ampdu_rx *r,
45				      int index)
46{
47	struct net_device *ndev = wil_to_ndev(wil);
48	struct sk_buff *skb = r->reorder_buf[index];
49
50	if (!skb)
51		goto no_frame;
52
53	/* release the frame from the reorder ring buffer */
54	r->stored_mpdu_num--;
55	r->reorder_buf[index] = NULL;
56	wil_netif_rx_any(skb, ndev);
57
58no_frame:
59	r->head_seq_num = seq_inc(r->head_seq_num);
60}
61
62static void wil_release_reorder_frames(struct wil6210_priv *wil,
63				       struct wil_tid_ampdu_rx *r,
64				       u16 hseq)
65{
66	int index;
67
68	/* note: this function is never called with
69	 * hseq preceding r->head_seq_num, i.e it is always true
70	 * !seq_less(hseq, r->head_seq_num)
71	 * and thus on loop exit it should be
72	 * r->head_seq_num == hseq
73	 */
74	while (seq_less(r->head_seq_num, hseq) && r->stored_mpdu_num) {
75		index = reorder_index(r, r->head_seq_num);
76		wil_release_reorder_frame(wil, r, index);
77	}
78	r->head_seq_num = hseq;
79}
80
81static void wil_reorder_release(struct wil6210_priv *wil,
82				struct wil_tid_ampdu_rx *r)
83{
84	int index = reorder_index(r, r->head_seq_num);
85
86	while (r->reorder_buf[index]) {
87		wil_release_reorder_frame(wil, r, index);
88		index = reorder_index(r, r->head_seq_num);
89	}
90}
91
92void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb)
93{
94	struct net_device *ndev = wil_to_ndev(wil);
95	struct vring_rx_desc *d = wil_skb_rxdesc(skb);
96	int tid = wil_rxdesc_tid(d);
97	int cid = wil_rxdesc_cid(d);
98	int mid = wil_rxdesc_mid(d);
99	u16 seq = wil_rxdesc_seq(d);
100	struct wil_sta_info *sta = &wil->sta[cid];
101	struct wil_tid_ampdu_rx *r;
102	u16 hseq;
103	int index;
104	unsigned long flags;
105
106	wil_dbg_txrx(wil, "MID %d CID %d TID %d Seq 0x%03x\n",
107		     mid, cid, tid, seq);
108
109	spin_lock_irqsave(&sta->tid_rx_lock, flags);
110
111	r = sta->tid_rx[tid];
112	if (!r) {
113		spin_unlock_irqrestore(&sta->tid_rx_lock, flags);
114		wil_netif_rx_any(skb, ndev);
115		return;
116	}
117
118	hseq = r->head_seq_num;
119
120	/** Due to the race between WMI events, where BACK establishment
121	 * reported, and data Rx, few packets may be pass up before reorder
122	 * buffer get allocated. Catch up by pretending SSN is what we
123	 * see in the 1-st Rx packet
124	 */
125	if (r->first_time) {
126		r->first_time = false;
127		if (seq != r->head_seq_num) {
128			wil_err(wil, "Error: 1-st frame with wrong sequence"
129				" %d, should be %d. Fixing...\n", seq,
130				r->head_seq_num);
131			r->head_seq_num = seq;
132			r->ssn = seq;
133		}
134	}
135
136	/* frame with out of date sequence number */
137	if (seq_less(seq, r->head_seq_num)) {
138		r->ssn_last_drop = seq;
139		dev_kfree_skb(skb);
140		goto out;
141	}
142
143	/*
144	 * If frame the sequence number exceeds our buffering window
145	 * size release some previous frames to make room for this one.
146	 */
147	if (!seq_less(seq, r->head_seq_num + r->buf_size)) {
148		hseq = seq_inc(seq_sub(seq, r->buf_size));
149		/* release stored frames up to new head to stack */
150		wil_release_reorder_frames(wil, r, hseq);
151	}
152
153	/* Now the new frame is always in the range of the reordering buffer */
154
155	index = reorder_index(r, seq);
156
157	/* check if we already stored this frame */
158	if (r->reorder_buf[index]) {
159		dev_kfree_skb(skb);
160		goto out;
161	}
162
163	/*
164	 * If the current MPDU is in the right order and nothing else
165	 * is stored we can process it directly, no need to buffer it.
166	 * If it is first but there's something stored, we may be able
167	 * to release frames after this one.
168	 */
169	if (seq == r->head_seq_num && r->stored_mpdu_num == 0) {
170		r->head_seq_num = seq_inc(r->head_seq_num);
171		wil_netif_rx_any(skb, ndev);
172		goto out;
173	}
174
175	/* put the frame in the reordering buffer */
176	r->reorder_buf[index] = skb;
177	r->reorder_time[index] = jiffies;
178	r->stored_mpdu_num++;
179	wil_reorder_release(wil, r);
180
181out:
182	spin_unlock_irqrestore(&sta->tid_rx_lock, flags);
183}
184
185struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil,
186						int size, u16 ssn)
187{
188	struct wil_tid_ampdu_rx *r = kzalloc(sizeof(*r), GFP_KERNEL);
189
190	if (!r)
191		return NULL;
192
193	r->reorder_buf =
194		kcalloc(size, sizeof(struct sk_buff *), GFP_KERNEL);
195	r->reorder_time =
196		kcalloc(size, sizeof(unsigned long), GFP_KERNEL);
197	if (!r->reorder_buf || !r->reorder_time) {
198		kfree(r->reorder_buf);
199		kfree(r->reorder_time);
200		kfree(r);
201		return NULL;
202	}
203
204	r->ssn = ssn;
205	r->head_seq_num = ssn;
206	r->buf_size = size;
207	r->stored_mpdu_num = 0;
208	r->first_time = true;
209	return r;
210}
211
212void wil_tid_ampdu_rx_free(struct wil6210_priv *wil,
213			   struct wil_tid_ampdu_rx *r)
214{
215	if (!r)
216		return;
217	wil_release_reorder_frames(wil, r, r->head_seq_num + r->buf_size);
218	kfree(r->reorder_buf);
219	kfree(r->reorder_time);
220	kfree(r);
221}
222