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
12static void initrecvbuf(struct recv_buf *precvbuf, struct adapter *padapter)
13{
14	INIT_LIST_HEAD(&precvbuf->list);
15	spin_lock_init(&precvbuf->recvbuf_lock);
16
17	precvbuf->adapter = padapter;
18}
19
20static 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 */
75static 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(&padapter->mlmepriv);
104	rx_bssid = get_hdr_bssid(wlanhdr);
105	pkt_info.bssid_match = ((!IsFrameTypeCtrl(wlanhdr)) &&
106				!pattrib->icv_err && !pattrib->crc_err &&
107				ether_addr_equal(rx_bssid, my_bssid));
108
109	rx_ra = rtl8723bs_get_ra(wlanhdr);
110	my_hwaddr = myid(&padapter->eeprompriv);
111	pkt_info.to_self = pkt_info.bssid_match &&
112		ether_addr_equal(rx_ra, my_hwaddr);
113
114
115	pkt_info.is_beacon = pkt_info.bssid_match &&
116		(GetFrameSubType(wlanhdr) == WIFI_BEACON);
117
118	sa = get_ta(wlanhdr);
119
120	pkt_info.station_id = 0xFF;
121
122	pstapriv = &padapter->stapriv;
123	psta = rtw_get_stainfo(pstapriv, 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(&p_hal_data->odmpriv, p_phy_info,
132			   (u8 *)pphy_status, &(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(&padapter->mlmepriv, WIFI_AP_STATE) == true)
140	) {
141		if (psta) {
142			precvframe->u.hdr.psta = psta;
143			rtl8723b_process_phy_info(padapter, 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(&padapter->mlmepriv, adhoc_state))
148			if (psta)
149				precvframe->u.hdr.psta = psta;
150		rtl8723b_process_phy_info(padapter, precvframe);
151	}
152}
153
154static 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, tmp, length);
170
171	if (!res)
172		kfree(tmp);
173}
174
175static 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(&precvpriv->free_recv_queue);
181	if (!precvframe) {
182		rtw_enqueue_recvbuf_to_head(precvbuf,
183					    &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(&precvpriv->recv_tasklet);
188	}
189
190	return precvframe;
191}
192
193static 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, &precvpriv->free_recv_queue);
201		return true;
202	}
203
204	return false;
205}
206
207static 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, &precvpriv->free_recv_queue);
213		return true;
214	}
215
216	return false;
217}
218
219static 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(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						(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, ptr + pkt_offset,
266					     precvbuf->ptail, precvframe))
267				break;
268
269			if ((pattrib->crc_err) || (pattrib->icv_err)) {
270				rtw_free_recvframe(precvframe,
271						   &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, &precvpriv->free_recv_queue);
299					break;
300				}
301
302				pkt_copy->dev = padapter->pnetdev;
303				precvframe->u.hdr.pkt = pkt_copy;
304				skb_reserve(pkt_copy, 8 - ((SIZE_PTR)(pkt_copy->data) & 7));/* force pkt_copy->data at 8-byte alignment address */
305				skb_reserve(pkt_copy, 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(pkt_copy);
310
311				recvframe_put(precvframe, 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, (struct phy_stat *)ptr);
329
330					rtw_recv_entry(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_c2h, C2hEvent.CmdLen);
345					else
346						rtl8723bs_c2h_packet_handler(padapter, precvframe->u.hdr.rx_data, pattrib->pkt_len);
347
348					rtw_free_recvframe(precvframe, &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, &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 */
369s32 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(&precvpriv->free_recv_buf_queue.queue);
381	spin_lock_init(&precvpriv->free_recv_buf_queue.lock);
382	INIT_LIST_HEAD(&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(precvbuf->pskb, (RECVBUFF_ALIGN_SZ - alignment));
411			}
412		}
413
414		list_add_tail(&precvbuf->list, &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(&precvpriv->recv_tasklet, rtl8723bs_recv_tasklet);
425
426	goto exit;
427
428initbuferror:
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(&precvbuf->list);
435			rtw_os_recvbuf_resource_free(padapter, precvbuf);
436			precvbuf++;
437		}
438		precvpriv->precv_buf = NULL;
439	}
440
441	kfree(precvpriv->pallocated_recv_buf);
442	precvpriv->pallocated_recv_buf = NULL;
443
444exit:
445	return res;
446}
447
448/*
449 * Free recv private variable of hardware dependent
450 * 1. recv buf
451 * 2. recv tasklet
452 *
453 */
454void 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(&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(&precvbuf->list);
471			rtw_os_recvbuf_resource_free(padapter, precvbuf);
472			precvbuf++;
473		}
474		precvpriv->precv_buf = NULL;
475	}
476
477	kfree(precvpriv->pallocated_recv_buf);
478	precvpriv->pallocated_recv_buf = NULL;
479}
480