1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /****************************************************************************** |
3 | * |
4 | * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved. |
5 | * |
6 | ******************************************************************************/ |
7 | |
8 | #include <drv_types.h> |
9 | #include <rtw_debug.h> |
10 | #include <rtl8723b_hal.h> |
11 | |
12 | static void initrecvbuf(struct recv_buf *precvbuf, struct adapter *padapter) |
13 | { |
14 | INIT_LIST_HEAD(list: &precvbuf->list); |
15 | spin_lock_init(&precvbuf->recvbuf_lock); |
16 | |
17 | precvbuf->adapter = padapter; |
18 | } |
19 | |
20 | static void update_recvframe_attrib(struct adapter *padapter, |
21 | union recv_frame *precvframe, |
22 | struct recv_stat *prxstat) |
23 | { |
24 | struct rx_pkt_attrib *pattrib; |
25 | struct recv_stat report; |
26 | struct rxreport_8723b *prxreport = (struct rxreport_8723b *)&report; |
27 | |
28 | report.rxdw0 = prxstat->rxdw0; |
29 | report.rxdw1 = prxstat->rxdw1; |
30 | report.rxdw2 = prxstat->rxdw2; |
31 | report.rxdw3 = prxstat->rxdw3; |
32 | report.rxdw4 = prxstat->rxdw4; |
33 | report.rxdw5 = prxstat->rxdw5; |
34 | |
35 | pattrib = &precvframe->u.hdr.attrib; |
36 | memset(pattrib, 0, sizeof(struct rx_pkt_attrib)); |
37 | |
38 | /* update rx report to recv_frame attribute */ |
39 | pattrib->pkt_rpt_type = prxreport->c2h_ind ? C2H_PACKET : NORMAL_RX; |
40 | |
41 | if (pattrib->pkt_rpt_type == NORMAL_RX) { |
42 | /* Normal rx packet */ |
43 | /* update rx report to recv_frame attribute */ |
44 | pattrib->pkt_len = (u16)prxreport->pktlen; |
45 | pattrib->drvinfo_sz = (u8)(prxreport->drvinfosize << 3); |
46 | pattrib->physt = (u8)prxreport->physt; |
47 | |
48 | pattrib->crc_err = (u8)prxreport->crc32; |
49 | pattrib->icv_err = (u8)prxreport->icverr; |
50 | |
51 | pattrib->bdecrypted = (u8)(prxreport->swdec ? 0 : 1); |
52 | pattrib->encrypt = (u8)prxreport->security; |
53 | |
54 | pattrib->qos = (u8)prxreport->qos; |
55 | pattrib->priority = (u8)prxreport->tid; |
56 | |
57 | pattrib->amsdu = (u8)prxreport->amsdu; |
58 | |
59 | pattrib->seq_num = (u16)prxreport->seq; |
60 | pattrib->frag_num = (u8)prxreport->frag; |
61 | pattrib->mfrag = (u8)prxreport->mf; |
62 | pattrib->mdata = (u8)prxreport->md; |
63 | |
64 | pattrib->data_rate = (u8)prxreport->rx_rate; |
65 | } else { |
66 | pattrib->pkt_len = (u16)prxreport->pktlen; |
67 | } |
68 | } |
69 | |
70 | /* |
71 | * Notice: |
72 | *Before calling this function, |
73 | *precvframe->u.hdr.rx_data should be ready! |
74 | */ |
75 | static void update_recvframe_phyinfo(union recv_frame *precvframe, |
76 | struct phy_stat *pphy_status) |
77 | { |
78 | struct adapter *padapter = precvframe->u.hdr.adapter; |
79 | struct rx_pkt_attrib *pattrib = &precvframe->u.hdr.attrib; |
80 | struct hal_com_data *p_hal_data = GET_HAL_DATA(padapter); |
81 | struct odm_phy_info *p_phy_info = |
82 | (struct odm_phy_info *)(&pattrib->phy_info); |
83 | |
84 | u8 *wlanhdr = precvframe->u.hdr.rx_data; |
85 | u8 *my_bssid; |
86 | u8 *rx_bssid; |
87 | u8 *rx_ra; |
88 | u8 *my_hwaddr; |
89 | u8 *sa = NULL; |
90 | |
91 | struct odm_packet_info pkt_info = { |
92 | .data_rate = 0x00, |
93 | .station_id = 0x00, |
94 | .bssid_match = false, |
95 | .to_self = false, |
96 | .is_beacon = false, |
97 | }; |
98 | |
99 | /* unsigned long irqL; */ |
100 | struct sta_priv *pstapriv; |
101 | struct sta_info *psta; |
102 | |
103 | my_bssid = get_bssid(pmlmepriv: &padapter->mlmepriv); |
104 | rx_bssid = get_hdr_bssid(pframe: wlanhdr); |
105 | pkt_info.bssid_match = ((!IsFrameTypeCtrl(pframe: wlanhdr)) && |
106 | !pattrib->icv_err && !pattrib->crc_err && |
107 | ether_addr_equal(addr1: rx_bssid, addr2: my_bssid)); |
108 | |
109 | rx_ra = rtl8723bs_get_ra(pframe: wlanhdr); |
110 | my_hwaddr = myid(peepriv: &padapter->eeprompriv); |
111 | pkt_info.to_self = pkt_info.bssid_match && |
112 | ether_addr_equal(addr1: rx_ra, addr2: my_hwaddr); |
113 | |
114 | |
115 | pkt_info.is_beacon = pkt_info.bssid_match && |
116 | (GetFrameSubType(wlanhdr) == WIFI_BEACON); |
117 | |
118 | sa = get_ta(pframe: wlanhdr); |
119 | |
120 | pkt_info.station_id = 0xFF; |
121 | |
122 | pstapriv = &padapter->stapriv; |
123 | psta = rtw_get_stainfo(pstapriv, hwaddr: sa); |
124 | if (psta) |
125 | pkt_info.station_id = psta->mac_id; |
126 | |
127 | pkt_info.data_rate = pattrib->data_rate; |
128 | |
129 | /* rtl8723b_query_rx_phy_status(precvframe, pphy_status); */ |
130 | /* spin_lock_bh(&p_hal_data->odm_stainfo_lock); */ |
131 | odm_phy_status_query(dm_odm: &p_hal_data->odmpriv, phy_info: p_phy_info, |
132 | phy_status: (u8 *)pphy_status, pkt_info: &(pkt_info)); |
133 | if (psta) |
134 | psta->rssi = pattrib->phy_info.RecvSignalPower; |
135 | /* spin_unlock_bh(&p_hal_data->odm_stainfo_lock); */ |
136 | precvframe->u.hdr.psta = NULL; |
137 | if ( |
138 | pkt_info.bssid_match && |
139 | (check_fwstate(pmlmepriv: &padapter->mlmepriv, WIFI_AP_STATE) == true) |
140 | ) { |
141 | if (psta) { |
142 | precvframe->u.hdr.psta = psta; |
143 | rtl8723b_process_phy_info(padapter, prframe: precvframe); |
144 | } |
145 | } else if (pkt_info.to_self || pkt_info.is_beacon) { |
146 | u32 adhoc_state = WIFI_ADHOC_STATE | WIFI_ADHOC_MASTER_STATE; |
147 | if (check_fwstate(pmlmepriv: &padapter->mlmepriv, state: adhoc_state)) |
148 | if (psta) |
149 | precvframe->u.hdr.psta = psta; |
150 | rtl8723b_process_phy_info(padapter, prframe: precvframe); |
151 | } |
152 | } |
153 | |
154 | static void rtl8723bs_c2h_packet_handler(struct adapter *padapter, |
155 | u8 *pbuf, u16 length) |
156 | { |
157 | u8 *tmp = NULL; |
158 | u8 res = false; |
159 | |
160 | if (length == 0) |
161 | return; |
162 | |
163 | tmp = rtw_zmalloc(length); |
164 | if (!tmp) |
165 | return; |
166 | |
167 | memcpy(tmp, pbuf, length); |
168 | |
169 | res = rtw_c2h_packet_wk_cmd(padapter, pbuf: tmp, length); |
170 | |
171 | if (!res) |
172 | kfree(objp: tmp); |
173 | } |
174 | |
175 | static inline union recv_frame *try_alloc_recvframe(struct recv_priv *precvpriv, |
176 | struct recv_buf *precvbuf) |
177 | { |
178 | union recv_frame *precvframe; |
179 | |
180 | precvframe = rtw_alloc_recvframe(pfree_recv_queue: &precvpriv->free_recv_queue); |
181 | if (!precvframe) { |
182 | rtw_enqueue_recvbuf_to_head(precvbuf, |
183 | queue: &precvpriv->recv_buf_pending_queue); |
184 | |
185 | /* The case of can't allocate recvframe should be temporary, */ |
186 | /* schedule again and hope recvframe is available next time. */ |
187 | tasklet_schedule(t: &precvpriv->recv_tasklet); |
188 | } |
189 | |
190 | return precvframe; |
191 | } |
192 | |
193 | static inline bool rx_crc_err(struct recv_priv *precvpriv, |
194 | struct hal_com_data *p_hal_data, |
195 | struct rx_pkt_attrib *pattrib, |
196 | union recv_frame *precvframe) |
197 | { |
198 | /* fix Hardware RX data error, drop whole recv_buffer */ |
199 | if ((!(p_hal_data->ReceiveConfig & RCR_ACRC32)) && pattrib->crc_err) { |
200 | rtw_free_recvframe(precvframe, pfree_recv_queue: &precvpriv->free_recv_queue); |
201 | return true; |
202 | } |
203 | |
204 | return false; |
205 | } |
206 | |
207 | static inline bool pkt_exceeds_tail(struct recv_priv *precvpriv, |
208 | u8 *end, u8 *tail, |
209 | union recv_frame *precvframe) |
210 | { |
211 | if (end > tail) { |
212 | rtw_free_recvframe(precvframe, pfree_recv_queue: &precvpriv->free_recv_queue); |
213 | return true; |
214 | } |
215 | |
216 | return false; |
217 | } |
218 | |
219 | static void rtl8723bs_recv_tasklet(struct tasklet_struct *t) |
220 | { |
221 | struct adapter *padapter = from_tasklet(padapter, t, |
222 | recvpriv.recv_tasklet); |
223 | struct hal_com_data *p_hal_data; |
224 | struct recv_priv *precvpriv; |
225 | struct recv_buf *precvbuf; |
226 | union recv_frame *precvframe; |
227 | struct rx_pkt_attrib *pattrib; |
228 | struct __queue *recv_buf_queue; |
229 | u8 *ptr; |
230 | u32 pkt_offset, skb_len, alloc_sz; |
231 | struct sk_buff *pkt_copy = NULL; |
232 | u8 shift_sz = 0, rx_report_sz = 0; |
233 | |
234 | p_hal_data = GET_HAL_DATA(padapter); |
235 | precvpriv = &padapter->recvpriv; |
236 | recv_buf_queue = &precvpriv->recv_buf_pending_queue; |
237 | |
238 | do { |
239 | precvbuf = rtw_dequeue_recvbuf(queue: recv_buf_queue); |
240 | if (!precvbuf) |
241 | break; |
242 | |
243 | ptr = precvbuf->pdata; |
244 | |
245 | while (ptr < precvbuf->ptail) { |
246 | precvframe = try_alloc_recvframe(precvpriv, precvbuf); |
247 | if (!precvframe) |
248 | return; |
249 | |
250 | /* rx desc parsing */ |
251 | update_recvframe_attrib(padapter, precvframe, |
252 | prxstat: (struct recv_stat *)ptr); |
253 | |
254 | pattrib = &precvframe->u.hdr.attrib; |
255 | |
256 | if (rx_crc_err(precvpriv, p_hal_data, |
257 | pattrib, precvframe)) |
258 | break; |
259 | |
260 | rx_report_sz = RXDESC_SIZE + pattrib->drvinfo_sz; |
261 | pkt_offset = rx_report_sz + |
262 | pattrib->shift_sz + |
263 | pattrib->pkt_len; |
264 | |
265 | if (pkt_exceeds_tail(precvpriv, end: ptr + pkt_offset, |
266 | tail: precvbuf->ptail, precvframe)) |
267 | break; |
268 | |
269 | if ((pattrib->crc_err) || (pattrib->icv_err)) { |
270 | rtw_free_recvframe(precvframe, |
271 | pfree_recv_queue: &precvpriv->free_recv_queue); |
272 | } else { |
273 | /* Modified by Albert 20101213 */ |
274 | /* For 8 bytes IP header alignment. */ |
275 | if (pattrib->qos) /* Qos data, wireless lan header length is 26 */ |
276 | shift_sz = 6; |
277 | else |
278 | shift_sz = 0; |
279 | |
280 | skb_len = pattrib->pkt_len; |
281 | |
282 | /* for first fragment packet, driver need allocate 1536+drvinfo_sz+RXDESC_SIZE to defrag packet. */ |
283 | /* modify alloc_sz for recvive crc error packet by thomas 2011-06-02 */ |
284 | if ((pattrib->mfrag == 1) && (pattrib->frag_num == 0)) { |
285 | if (skb_len <= 1650) |
286 | alloc_sz = 1664; |
287 | else |
288 | alloc_sz = skb_len + 14; |
289 | } else { |
290 | alloc_sz = skb_len; |
291 | /* 6 is for IP header 8 bytes alignment in QoS packet case. */ |
292 | /* 8 is for skb->data 4 bytes alignment. */ |
293 | alloc_sz += 14; |
294 | } |
295 | |
296 | pkt_copy = rtw_skb_alloc(alloc_sz); |
297 | if (!pkt_copy) { |
298 | rtw_free_recvframe(precvframe, pfree_recv_queue: &precvpriv->free_recv_queue); |
299 | break; |
300 | } |
301 | |
302 | pkt_copy->dev = padapter->pnetdev; |
303 | precvframe->u.hdr.pkt = pkt_copy; |
304 | skb_reserve(skb: pkt_copy, len: 8 - ((SIZE_PTR)(pkt_copy->data) & 7));/* force pkt_copy->data at 8-byte alignment address */ |
305 | skb_reserve(skb: pkt_copy, len: shift_sz);/* force ip_hdr at 8-byte alignment address according to shift_sz. */ |
306 | memcpy(pkt_copy->data, (ptr + rx_report_sz + pattrib->shift_sz), skb_len); |
307 | precvframe->u.hdr.rx_head = pkt_copy->head; |
308 | precvframe->u.hdr.rx_data = precvframe->u.hdr.rx_tail = pkt_copy->data; |
309 | precvframe->u.hdr.rx_end = skb_end_pointer(skb: pkt_copy); |
310 | |
311 | recvframe_put(precvframe, sz: skb_len); |
312 | /* recvframe_pull(precvframe, drvinfo_sz + RXDESC_SIZE); */ |
313 | |
314 | if (p_hal_data->ReceiveConfig & RCR_APPFCS) |
315 | recvframe_pull_tail(precvframe, IEEE80211_FCS_LEN); |
316 | |
317 | /* move to drv info position */ |
318 | ptr += RXDESC_SIZE; |
319 | |
320 | /* update drv info */ |
321 | if (p_hal_data->ReceiveConfig & RCR_APP_BA_SSN) { |
322 | /* rtl8723s_update_bassn(padapter, pdrvinfo); */ |
323 | ptr += 4; |
324 | } |
325 | |
326 | if (pattrib->pkt_rpt_type == NORMAL_RX) { /* Normal rx packet */ |
327 | if (pattrib->physt) |
328 | update_recvframe_phyinfo(precvframe, pphy_status: (struct phy_stat *)ptr); |
329 | |
330 | rtw_recv_entry(precv_frame: precvframe); |
331 | } else if (pattrib->pkt_rpt_type == C2H_PACKET) { |
332 | struct c2h_evt_hdr_t C2hEvent; |
333 | |
334 | u16 len_c2h = pattrib->pkt_len; |
335 | u8 *pbuf_c2h = precvframe->u.hdr.rx_data; |
336 | u8 *pdata_c2h; |
337 | |
338 | C2hEvent.CmdID = pbuf_c2h[0]; |
339 | C2hEvent.CmdSeq = pbuf_c2h[1]; |
340 | C2hEvent.CmdLen = (len_c2h-2); |
341 | pdata_c2h = pbuf_c2h+2; |
342 | |
343 | if (C2hEvent.CmdID == C2H_CCX_TX_RPT) |
344 | CCX_FwC2HTxRpt_8723b(padapter, pdata: pdata_c2h, len: C2hEvent.CmdLen); |
345 | else |
346 | rtl8723bs_c2h_packet_handler(padapter, pbuf: precvframe->u.hdr.rx_data, length: pattrib->pkt_len); |
347 | |
348 | rtw_free_recvframe(precvframe, pfree_recv_queue: &precvpriv->free_recv_queue); |
349 | } |
350 | } |
351 | |
352 | pkt_offset = round_up(pkt_offset, 8); |
353 | precvbuf->pdata += pkt_offset; |
354 | ptr = precvbuf->pdata; |
355 | precvframe = NULL; |
356 | pkt_copy = NULL; |
357 | } |
358 | |
359 | rtw_enqueue_recvbuf(precvbuf, queue: &precvpriv->free_recv_buf_queue); |
360 | } while (1); |
361 | } |
362 | |
363 | /* |
364 | * Initialize recv private variable for hardware dependent |
365 | * 1. recv buf |
366 | * 2. recv tasklet |
367 | * |
368 | */ |
369 | s32 rtl8723bs_init_recv_priv(struct adapter *padapter) |
370 | { |
371 | s32 res; |
372 | u32 i, n; |
373 | struct recv_priv *precvpriv; |
374 | struct recv_buf *precvbuf; |
375 | |
376 | res = _SUCCESS; |
377 | precvpriv = &padapter->recvpriv; |
378 | |
379 | /* 3 1. init recv buffer */ |
380 | INIT_LIST_HEAD(list: &precvpriv->free_recv_buf_queue.queue); |
381 | spin_lock_init(&precvpriv->free_recv_buf_queue.lock); |
382 | INIT_LIST_HEAD(list: &precvpriv->recv_buf_pending_queue.queue); |
383 | spin_lock_init(&precvpriv->recv_buf_pending_queue.lock); |
384 | |
385 | n = NR_RECVBUFF * sizeof(struct recv_buf) + 4; |
386 | precvpriv->pallocated_recv_buf = rtw_zmalloc(n); |
387 | if (!precvpriv->pallocated_recv_buf) { |
388 | res = _FAIL; |
389 | goto exit; |
390 | } |
391 | |
392 | precvpriv->precv_buf = (u8 *)N_BYTE_ALIGMENT((SIZE_PTR)(precvpriv->pallocated_recv_buf), 4); |
393 | |
394 | /* init each recv buffer */ |
395 | precvbuf = (struct recv_buf *)precvpriv->precv_buf; |
396 | for (i = 0; i < NR_RECVBUFF; i++) { |
397 | initrecvbuf(precvbuf, padapter); |
398 | |
399 | if (!precvbuf->pskb) { |
400 | SIZE_PTR tmpaddr = 0; |
401 | SIZE_PTR alignment = 0; |
402 | |
403 | precvbuf->pskb = rtw_skb_alloc(MAX_RECVBUF_SZ + RECVBUFF_ALIGN_SZ); |
404 | |
405 | if (precvbuf->pskb) { |
406 | precvbuf->pskb->dev = padapter->pnetdev; |
407 | |
408 | tmpaddr = (SIZE_PTR)precvbuf->pskb->data; |
409 | alignment = tmpaddr & (RECVBUFF_ALIGN_SZ-1); |
410 | skb_reserve(skb: precvbuf->pskb, len: (RECVBUFF_ALIGN_SZ - alignment)); |
411 | } |
412 | } |
413 | |
414 | list_add_tail(new: &precvbuf->list, head: &precvpriv->free_recv_buf_queue.queue); |
415 | |
416 | precvbuf++; |
417 | } |
418 | precvpriv->free_recv_buf_queue_cnt = i; |
419 | |
420 | if (res == _FAIL) |
421 | goto initbuferror; |
422 | |
423 | /* 3 2. init tasklet */ |
424 | tasklet_setup(t: &precvpriv->recv_tasklet, callback: rtl8723bs_recv_tasklet); |
425 | |
426 | goto exit; |
427 | |
428 | initbuferror: |
429 | precvbuf = (struct recv_buf *)precvpriv->precv_buf; |
430 | if (precvbuf) { |
431 | n = precvpriv->free_recv_buf_queue_cnt; |
432 | precvpriv->free_recv_buf_queue_cnt = 0; |
433 | for (i = 0; i < n ; i++) { |
434 | list_del_init(entry: &precvbuf->list); |
435 | rtw_os_recvbuf_resource_free(padapter, precvbuf); |
436 | precvbuf++; |
437 | } |
438 | precvpriv->precv_buf = NULL; |
439 | } |
440 | |
441 | kfree(objp: precvpriv->pallocated_recv_buf); |
442 | precvpriv->pallocated_recv_buf = NULL; |
443 | |
444 | exit: |
445 | return res; |
446 | } |
447 | |
448 | /* |
449 | * Free recv private variable of hardware dependent |
450 | * 1. recv buf |
451 | * 2. recv tasklet |
452 | * |
453 | */ |
454 | void rtl8723bs_free_recv_priv(struct adapter *padapter) |
455 | { |
456 | u32 i; |
457 | struct recv_priv *precvpriv; |
458 | struct recv_buf *precvbuf; |
459 | |
460 | precvpriv = &padapter->recvpriv; |
461 | |
462 | /* 3 1. kill tasklet */ |
463 | tasklet_kill(t: &precvpriv->recv_tasklet); |
464 | |
465 | /* 3 2. free all recv buffers */ |
466 | precvbuf = (struct recv_buf *)precvpriv->precv_buf; |
467 | if (precvbuf) { |
468 | precvpriv->free_recv_buf_queue_cnt = 0; |
469 | for (i = 0; i < NR_RECVBUFF; i++) { |
470 | list_del_init(entry: &precvbuf->list); |
471 | rtw_os_recvbuf_resource_free(padapter, precvbuf); |
472 | precvbuf++; |
473 | } |
474 | precvpriv->precv_buf = NULL; |
475 | } |
476 | |
477 | kfree(objp: precvpriv->pallocated_recv_buf); |
478 | precvpriv->pallocated_recv_buf = NULL; |
479 | } |
480 | |