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