xref: /OK3568_Linux_fs/external/rkwifibt/drivers/rtl8852bs/phl/phl_rx_agg.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /******************************************************************************
2*4882a593Smuzhiyun  *
3*4882a593Smuzhiyun  * Copyright(c) 2019 Realtek Corporation.
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * This program is free software; you can redistribute it and/or modify it
6*4882a593Smuzhiyun  * under the terms of version 2 of the GNU General Public License as
7*4882a593Smuzhiyun  * published by the Free Software Foundation.
8*4882a593Smuzhiyun  *
9*4882a593Smuzhiyun  * This program is distributed in the hope that it will be useful, but WITHOUT
10*4882a593Smuzhiyun  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11*4882a593Smuzhiyun  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12*4882a593Smuzhiyun  * more details.
13*4882a593Smuzhiyun  *
14*4882a593Smuzhiyun  *****************************************************************************/
15*4882a593Smuzhiyun #define _PHL_RX_AGG_C_
16*4882a593Smuzhiyun #include "phl_headers.h"
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun /**
19*4882a593Smuzhiyun  * phl_tid_ampdu_rx_free() - Free the tid ampdu rx entry specified by @r.
20*4882a593Smuzhiyun  *
21*4882a593Smuzhiyun  * The caller has to take care of potential race condition:
22*4882a593Smuzhiyun  *  - if it is in @rtw_phl_stainfo_t.tid_rx, should be called with
23*4882a593Smuzhiyun  *    @rtw_phl_stainfo_t.tid_rx_lock held
24*4882a593Smuzhiyun  *  - if it is not in @rtw_phl_stainfo_t.tid_rx, it can be called freely since
25*4882a593Smuzhiyun  *    other part of the code can't see it
26*4882a593Smuzhiyun  *
27*4882a593Smuzhiyun  * @phl_tid_ampdu_rx.removed can be set to make sure that the reorder_time will
28*4882a593Smuzhiyun  * not be set again.  This is usful when canceling the timer synchronously
29*4882a593Smuzhiyun  * before releasing it.
30*4882a593Smuzhiyun  *
31*4882a593Smuzhiyun  * TODO: On macos, _os_kmem_free() should not be called with lock held since it
32*4882a593Smuzhiyun  * may block.
33*4882a593Smuzhiyun  */
phl_tid_ampdu_rx_free(struct phl_tid_ampdu_rx * r)34*4882a593Smuzhiyun void phl_tid_ampdu_rx_free(struct phl_tid_ampdu_rx *r)
35*4882a593Smuzhiyun {
36*4882a593Smuzhiyun 	/* ref wil_tid_ampdu_rx_free() and ieee80211_free_tid_rx() */
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun 	u16 buf_size;
39*4882a593Smuzhiyun 	void *drv_priv;
40*4882a593Smuzhiyun 	int i;
41*4882a593Smuzhiyun 	struct rtw_phl_rx_pkt *pkt = NULL;
42*4882a593Smuzhiyun 	struct phl_hci_trx_ops *hci_trx_ops = NULL;
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun 	if (!r)
45*4882a593Smuzhiyun 		return;
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun 	buf_size = r->buf_size;
48*4882a593Smuzhiyun 	drv_priv = r->drv_priv;
49*4882a593Smuzhiyun 	hci_trx_ops = r->phl_info->hci_trx_ops;
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun 	for (i = 0; i < r->buf_size; i++) {
52*4882a593Smuzhiyun 		pkt = r->reorder_buf[i];
53*4882a593Smuzhiyun 		if (NULL != pkt)
54*4882a593Smuzhiyun 			hci_trx_ops->recycle_rx_pkt(r->phl_info, pkt);
55*4882a593Smuzhiyun 	}
56*4882a593Smuzhiyun 	_os_kmem_free(drv_priv, r->reorder_time,
57*4882a593Smuzhiyun 	              buf_size * sizeof(u32));
58*4882a593Smuzhiyun 	_os_kmem_free(drv_priv, r->reorder_buf,
59*4882a593Smuzhiyun 	              buf_size * sizeof(struct rtw_phl_rx_pkt *));
60*4882a593Smuzhiyun 	_os_kmem_free(drv_priv, r, sizeof(*r));
61*4882a593Smuzhiyun }
62*4882a593Smuzhiyun 
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun static inline void
_phl_cancel_rx_reorder_timer(struct phl_info_t * phl_info,struct rtw_phl_stainfo_t * sta)65*4882a593Smuzhiyun _phl_cancel_rx_reorder_timer(struct phl_info_t *phl_info,
66*4882a593Smuzhiyun                          struct rtw_phl_stainfo_t *sta)
67*4882a593Smuzhiyun {
68*4882a593Smuzhiyun 	void *drv = phl_to_drvpriv(phl_info);
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun #ifdef PHL_PLATFORM_WINDOWS
71*4882a593Smuzhiyun 	/* Cancel the reorder_timer of the stainfo synchronously.
72*4882a593Smuzhiyun 	 * Note that on Windows, _os_cancel_timer() does not guarantee that
73*4882a593Smuzhiyun 	 * after the cancel, no timer callback will ever be called.  Therefore
74*4882a593Smuzhiyun 	 * @comp_sync is used for waiting this residual timer callback on
75*4882a593Smuzhiyun 	 * Windows.*/
76*4882a593Smuzhiyun 	_os_event_reset(drv, &sta->comp_sync);
77*4882a593Smuzhiyun 	_os_cancel_timer(drv, &sta->reorder_timer);
78*4882a593Smuzhiyun 	_os_event_wait(drv, &sta->comp_sync, PHL_REORDER_TIMER_SYNC_TO_MS);
79*4882a593Smuzhiyun #else /*PHL_PLATFORM_LINUX && PHL_PLATFORM_AP*/
80*4882a593Smuzhiyun 	_os_cancel_timer(drv, &sta->reorder_timer);/*or _os_cancel_timer_async*/
81*4882a593Smuzhiyun #endif
82*4882a593Smuzhiyun }
83*4882a593Smuzhiyun 
phl_free_rx_reorder(struct phl_info_t * phl_info,struct rtw_phl_stainfo_t * sta)84*4882a593Smuzhiyun void phl_free_rx_reorder(struct phl_info_t *phl_info,
85*4882a593Smuzhiyun                          struct rtw_phl_stainfo_t *sta)
86*4882a593Smuzhiyun {
87*4882a593Smuzhiyun 	void *drv = phl_to_drvpriv(phl_info);
88*4882a593Smuzhiyun 	u8 i = 0;
89*4882a593Smuzhiyun 
90*4882a593Smuzhiyun 	/* Mark the enrties as removed to make sure that reorder_timer of the
91*4882a593Smuzhiyun 	 * stainfo will not be set again after the timer is canceled. */
92*4882a593Smuzhiyun 	_os_spinlock(drv, &sta->tid_rx_lock, _bh, NULL);
93*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(sta->tid_rx); i++)
94*4882a593Smuzhiyun 		if (sta->tid_rx[i])
95*4882a593Smuzhiyun 			sta->tid_rx[i]->removed = true;
96*4882a593Smuzhiyun 	_os_spinunlock(drv, &sta->tid_rx_lock, _bh, NULL);
97*4882a593Smuzhiyun 
98*4882a593Smuzhiyun 	_phl_cancel_rx_reorder_timer(phl_info, sta);
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun 	/* Free the tid ampdu rx entry */
101*4882a593Smuzhiyun 	_os_spinlock(drv, &sta->tid_rx_lock, _bh, NULL);
102*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(sta->tid_rx); i++) {
103*4882a593Smuzhiyun 		/* ref wil_disconnect_cid() */
104*4882a593Smuzhiyun 		if (!sta->tid_rx[i])
105*4882a593Smuzhiyun 			continue;
106*4882a593Smuzhiyun 		phl_tid_ampdu_rx_free(sta->tid_rx[i]);
107*4882a593Smuzhiyun 		sta->tid_rx[i] = NULL;
108*4882a593Smuzhiyun 	}
109*4882a593Smuzhiyun 	_os_spinunlock(drv, &sta->tid_rx_lock, _bh, NULL);
110*4882a593Smuzhiyun }
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun 
rtw_phl_stop_rx_ba_session(void * phl,struct rtw_phl_stainfo_t * sta,u16 tid)113*4882a593Smuzhiyun void rtw_phl_stop_rx_ba_session(void *phl, struct rtw_phl_stainfo_t *sta,
114*4882a593Smuzhiyun                                 u16 tid)
115*4882a593Smuzhiyun {
116*4882a593Smuzhiyun 	/* ref wmi_evt_delba() and ___ieee80211_stop_rx_ba_session() */
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 	struct phl_info_t *phl_info = (struct phl_info_t *)phl;
119*4882a593Smuzhiyun 	void *drv_priv = phl_to_drvpriv(phl_info);
120*4882a593Smuzhiyun 	struct phl_tid_ampdu_rx *r;
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun 	if (NULL == sta) {
123*4882a593Smuzhiyun 		PHL_TRACE(COMP_PHL_RECV, _PHL_WARNING_,
124*4882a593Smuzhiyun 		          "rtw_phl_stop_rx_ba_session: station info is NULL!\n");
125*4882a593Smuzhiyun 		return;
126*4882a593Smuzhiyun 	}
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun 	PHL_INFO("Stop rx BA session for sta=0x%p, tid=%u\n", sta, tid);
129*4882a593Smuzhiyun 	rtw_hal_stop_ba_session(phl_info->hal, sta, tid);
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun 	if (tid >= ARRAY_SIZE(sta->tid_rx))
132*4882a593Smuzhiyun 		return;
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 	_os_spinlock(drv_priv, &sta->tid_rx_lock, _bh, NULL);
135*4882a593Smuzhiyun 	if (!sta->tid_rx[tid]) {
136*4882a593Smuzhiyun 		PHL_INFO("No active session found for the specified sta tid pair\n");
137*4882a593Smuzhiyun 		_os_spinunlock(drv_priv, &sta->tid_rx_lock, _bh, NULL);
138*4882a593Smuzhiyun 		return;
139*4882a593Smuzhiyun 	}
140*4882a593Smuzhiyun 	r = sta->tid_rx[tid];
141*4882a593Smuzhiyun 	sta->tid_rx[tid] = NULL;
142*4882a593Smuzhiyun 	r->removed = true;
143*4882a593Smuzhiyun 
144*4882a593Smuzhiyun 	/* Note that it is safe to free the tid ampdu rx here.  If reorder_timer
145*4882a593Smuzhiyun 	 * callback is invoked, the tid_rx_lock is held and it does not do
146*4882a593Smuzhiyun 	 * anything to the entry if it is NULL. */
147*4882a593Smuzhiyun 	phl_tid_ampdu_rx_free(r);
148*4882a593Smuzhiyun 	_os_spinunlock(drv_priv, &sta->tid_rx_lock, _bh, NULL);
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	PHL_INFO("Rx BA session for sta=0x%p, tid=%u freed\n", sta, tid);
151*4882a593Smuzhiyun }
152*4882a593Smuzhiyun 
phl_tid_ampdu_rx_alloc(struct phl_info_t * phl_info,struct rtw_phl_stainfo_t * sta,u16 timeout,u16 ssn,u16 tid,u16 buf_size)153*4882a593Smuzhiyun struct phl_tid_ampdu_rx *phl_tid_ampdu_rx_alloc(struct phl_info_t *phl_info,
154*4882a593Smuzhiyun                                                 struct rtw_phl_stainfo_t *sta,
155*4882a593Smuzhiyun                                                 u16 timeout, u16 ssn, u16 tid,
156*4882a593Smuzhiyun                                                 u16 buf_size)
157*4882a593Smuzhiyun {
158*4882a593Smuzhiyun 	/* ref wil_tid_ampdu_rx_alloc() */
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun 	void *drv_priv = phl_to_drvpriv(phl_info);
161*4882a593Smuzhiyun 	struct phl_tid_ampdu_rx *r;
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun 	/* allocate r */
164*4882a593Smuzhiyun 	r = _os_kmem_alloc(drv_priv, sizeof(*r));
165*4882a593Smuzhiyun 	if (!r)
166*4882a593Smuzhiyun 		return NULL;
167*4882a593Smuzhiyun 	_os_mem_set(drv_priv, r, 0, sizeof(*r));
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	/* allocate reorder_buf */
170*4882a593Smuzhiyun 	r->reorder_buf =
171*4882a593Smuzhiyun 		_os_kmem_alloc(drv_priv,
172*4882a593Smuzhiyun 		               buf_size * sizeof(struct rtw_phl_rx_pkt *));
173*4882a593Smuzhiyun 	if (!r->reorder_buf) {
174*4882a593Smuzhiyun 		_os_kmem_free(drv_priv, r, sizeof(*r));
175*4882a593Smuzhiyun 		return NULL;
176*4882a593Smuzhiyun 	}
177*4882a593Smuzhiyun 	_os_mem_set(drv_priv, r->reorder_buf, 0,
178*4882a593Smuzhiyun 	            buf_size * sizeof(struct rtw_phl_rx_pkt *));
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun 	/* allocate reorder_time */
181*4882a593Smuzhiyun 	r->reorder_time =
182*4882a593Smuzhiyun 		_os_kmem_alloc(drv_priv, buf_size * sizeof(u32));
183*4882a593Smuzhiyun 	if (!r->reorder_time) {
184*4882a593Smuzhiyun 		_os_kmem_free(drv_priv, r->reorder_buf,
185*4882a593Smuzhiyun 		              buf_size * sizeof(struct rtw_phl_rx_pkt *));
186*4882a593Smuzhiyun 		_os_kmem_free(drv_priv, r, sizeof(*r));
187*4882a593Smuzhiyun 		return NULL;
188*4882a593Smuzhiyun 	}
189*4882a593Smuzhiyun 	_os_mem_set(drv_priv, r->reorder_time, 0,
190*4882a593Smuzhiyun 	            buf_size * sizeof(u32));
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun 	/* init other fields */
193*4882a593Smuzhiyun 	r->sta = sta;
194*4882a593Smuzhiyun 	r->ssn = ssn;
195*4882a593Smuzhiyun 	r->head_seq_num = ssn;
196*4882a593Smuzhiyun 	r->buf_size = buf_size;
197*4882a593Smuzhiyun 	r->stored_mpdu_num = 0;
198*4882a593Smuzhiyun 	r->tid = tid;
199*4882a593Smuzhiyun 	r->started = false;
200*4882a593Smuzhiyun 	r->drv_priv = drv_priv;
201*4882a593Smuzhiyun 	r->phl_info = phl_info;
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	return r;
204*4882a593Smuzhiyun }
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun enum rtw_phl_status
rtw_phl_start_rx_ba_session(void * phl,struct rtw_phl_stainfo_t * sta,u8 dialog_token,u16 timeout,u16 start_seq_num,u16 ba_policy,u16 tid,u16 buf_size)207*4882a593Smuzhiyun rtw_phl_start_rx_ba_session(void *phl, struct rtw_phl_stainfo_t *sta,
208*4882a593Smuzhiyun                             u8 dialog_token, u16 timeout, u16 start_seq_num,
209*4882a593Smuzhiyun                             u16 ba_policy, u16 tid, u16 buf_size)
210*4882a593Smuzhiyun {
211*4882a593Smuzhiyun 	/* ref wil_addba_rx_request() and ___ieee80211_start_rx_ba_session() */
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun 	struct phl_info_t *phl_info = (struct phl_info_t *)phl;
214*4882a593Smuzhiyun 	enum rtw_hal_status hal_sts = RTW_HAL_STATUS_FAILURE;
215*4882a593Smuzhiyun 	void *drv_priv = phl_to_drvpriv(phl_info);
216*4882a593Smuzhiyun 	struct phl_tid_ampdu_rx *r;
217*4882a593Smuzhiyun 
218*4882a593Smuzhiyun 	hal_sts = rtw_hal_start_ba_session(phl_info->hal, sta, dialog_token,
219*4882a593Smuzhiyun 	                                   timeout, start_seq_num, ba_policy,
220*4882a593Smuzhiyun 	                                   tid, buf_size);
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun 	/* TODO: sta status */
223*4882a593Smuzhiyun 
224*4882a593Smuzhiyun 	/* TODO: check sta capability */
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun 	PHL_INFO("Start rx BA session for sta=0x%p, tid=%u, buf_size=%u, timeout=%u\n",
227*4882a593Smuzhiyun 	         sta, tid, buf_size, timeout);
228*4882a593Smuzhiyun 
229*4882a593Smuzhiyun 	/* apply policies */
230*4882a593Smuzhiyun 	if (ba_policy) {
231*4882a593Smuzhiyun 		PHL_ERR("BACK requested unsupported ba_policy == 1\n");
232*4882a593Smuzhiyun 		return RTW_PHL_STATUS_FAILURE;
233*4882a593Smuzhiyun 	}
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun 	/* apply buf_size */
236*4882a593Smuzhiyun 	if (buf_size == 0) {
237*4882a593Smuzhiyun 		PHL_INFO("Suggest BACK wsize %d\n", PHL_MAX_AGG_WSIZE);
238*4882a593Smuzhiyun 		buf_size = PHL_MAX_AGG_WSIZE;
239*4882a593Smuzhiyun 	}
240*4882a593Smuzhiyun 
241*4882a593Smuzhiyun 	/* allocate tid ampdu rx */
242*4882a593Smuzhiyun 	r = phl_tid_ampdu_rx_alloc(phl_info, sta, timeout, start_seq_num, tid,
243*4882a593Smuzhiyun 	                           buf_size);
244*4882a593Smuzhiyun 	if (!r) {
245*4882a593Smuzhiyun 		PHL_ERR("Failed to alloc tid ampdu rx\n");
246*4882a593Smuzhiyun 		return RTW_PHL_STATUS_RESOURCE;
247*4882a593Smuzhiyun 	}
248*4882a593Smuzhiyun 
249*4882a593Smuzhiyun 	/* apply */
250*4882a593Smuzhiyun 	_os_spinlock(drv_priv, &sta->tid_rx_lock, _bh, NULL);
251*4882a593Smuzhiyun 	if (sta->tid_rx[tid])
252*4882a593Smuzhiyun 		phl_tid_ampdu_rx_free(sta->tid_rx[tid]);
253*4882a593Smuzhiyun 	sta->tid_rx[tid] = r;
254*4882a593Smuzhiyun 	_os_spinunlock(drv_priv, &sta->tid_rx_lock, _bh, NULL);
255*4882a593Smuzhiyun 
256*4882a593Smuzhiyun 	return RTW_PHL_STATUS_SUCCESS;
257*4882a593Smuzhiyun }
258*4882a593Smuzhiyun 
phl_notify_reorder_sleep(void * phl,struct rtw_phl_stainfo_t * sta)259*4882a593Smuzhiyun void phl_notify_reorder_sleep(void *phl, struct rtw_phl_stainfo_t *sta)
260*4882a593Smuzhiyun {
261*4882a593Smuzhiyun 	struct phl_info_t *phl_info = (struct phl_info_t *)phl;
262*4882a593Smuzhiyun 	void *drv = phl_to_drvpriv(phl_info);
263*4882a593Smuzhiyun 	struct phl_tid_ampdu_rx *r;
264*4882a593Smuzhiyun 	u16 tid = 0;
265*4882a593Smuzhiyun 
266*4882a593Smuzhiyun 	if (NULL == sta) {
267*4882a593Smuzhiyun 		PHL_TRACE(COMP_PHL_RECV, _PHL_WARNING_,
268*4882a593Smuzhiyun 		          "rtw_phl_stop_rx_ba_session: station info is NULL!\n");
269*4882a593Smuzhiyun 		return;
270*4882a593Smuzhiyun 	}
271*4882a593Smuzhiyun 
272*4882a593Smuzhiyun 	_os_spinlock(drv, &sta->tid_rx_lock, _bh, NULL);
273*4882a593Smuzhiyun 	for (tid = 0; tid < ARRAY_SIZE(sta->tid_rx); tid++) {
274*4882a593Smuzhiyun 		if (!sta->tid_rx[tid])
275*4882a593Smuzhiyun 			continue;
276*4882a593Smuzhiyun 
277*4882a593Smuzhiyun 		PHL_INFO("Notify rx BA session sleep for sta=0x%p, tid=%u\n",
278*4882a593Smuzhiyun 		         sta, tid);
279*4882a593Smuzhiyun 		r = sta->tid_rx[tid];
280*4882a593Smuzhiyun 		r->sleep = true;
281*4882a593Smuzhiyun 		PHL_INFO("Rx BA session for sta=0x%p, tid=%u sleep\n", sta, tid);
282*4882a593Smuzhiyun 	}
283*4882a593Smuzhiyun 	_os_spinunlock(drv, &sta->tid_rx_lock, _bh, NULL);
284*4882a593Smuzhiyun }
285