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