xref: /OK3568_Linux_fs/external/rkwifibt/drivers/infineon/dhd_ip.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * IP Packet Parser Module.
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * Portions of this code are copyright (c) 2021 Cypress Semiconductor Corporation
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  * Copyright (C) 1999-2017, Broadcom Corporation
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  *      Unless you and Broadcom execute a separate written software license
9*4882a593Smuzhiyun  * agreement governing use of this software, this software is licensed to you
10*4882a593Smuzhiyun  * under the terms of the GNU General Public License version 2 (the "GPL"),
11*4882a593Smuzhiyun  * available at http://www.broadcom.com/licenses/GPLv2.php, with the
12*4882a593Smuzhiyun  * following added to such license:
13*4882a593Smuzhiyun  *
14*4882a593Smuzhiyun  *      As a special exception, the copyright holders of this software give you
15*4882a593Smuzhiyun  * permission to link this software with independent modules, and to copy and
16*4882a593Smuzhiyun  * distribute the resulting executable under terms of your choice, provided that
17*4882a593Smuzhiyun  * you also meet, for each linked independent module, the terms and conditions of
18*4882a593Smuzhiyun  * the license of that module.  An independent module is a module which is not
19*4882a593Smuzhiyun  * derived from this software.  The special exception does not apply to any
20*4882a593Smuzhiyun  * modifications of the software.
21*4882a593Smuzhiyun  *
22*4882a593Smuzhiyun  *      Notwithstanding the above, under no circumstances may you combine this
23*4882a593Smuzhiyun  * software in any way with any other Broadcom software provided under a license
24*4882a593Smuzhiyun  * other than the GPL, without Broadcom's express prior written consent.
25*4882a593Smuzhiyun  *
26*4882a593Smuzhiyun  *
27*4882a593Smuzhiyun  * <<Broadcom-WL-IPTag/Open:>>
28*4882a593Smuzhiyun  *
29*4882a593Smuzhiyun  * $Id: dhd_ip.c 700444 2017-05-19 06:38:00Z $
30*4882a593Smuzhiyun  */
31*4882a593Smuzhiyun #include <typedefs.h>
32*4882a593Smuzhiyun #include <osl.h>
33*4882a593Smuzhiyun 
34*4882a593Smuzhiyun #include <ethernet.h>
35*4882a593Smuzhiyun #include <vlan.h>
36*4882a593Smuzhiyun #include <802.3.h>
37*4882a593Smuzhiyun #include <bcmip.h>
38*4882a593Smuzhiyun #include <bcmendian.h>
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun #include <dhd_dbg.h>
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun #include <dhd_ip.h>
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun #if defined(DHDTCPACK_SUPPRESS) || defined(DHDTCPSYNC_FLOOD_BLK)
45*4882a593Smuzhiyun #include <dhd_bus.h>
46*4882a593Smuzhiyun #include <dhd_proto.h>
47*4882a593Smuzhiyun #include <bcmtcp.h>
48*4882a593Smuzhiyun #endif /* DHDTCPACK_SUPPRESS || DHDTCPSYNC_FLOOD_BLK */
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun /* special values */
51*4882a593Smuzhiyun /* 802.3 llc/snap header */
52*4882a593Smuzhiyun static const uint8 llc_snap_hdr[SNAP_HDR_LEN] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
53*4882a593Smuzhiyun 
pkt_frag_info(osl_t * osh,void * p)54*4882a593Smuzhiyun pkt_frag_t pkt_frag_info(osl_t *osh, void *p)
55*4882a593Smuzhiyun {
56*4882a593Smuzhiyun 	uint8 *frame;
57*4882a593Smuzhiyun 	int length;
58*4882a593Smuzhiyun 	uint8 *pt;			/* Pointer to type field */
59*4882a593Smuzhiyun 	uint16 ethertype;
60*4882a593Smuzhiyun 	struct ipv4_hdr *iph;		/* IP frame pointer */
61*4882a593Smuzhiyun 	int ipl;			/* IP frame length */
62*4882a593Smuzhiyun 	uint16 iph_frag;
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun 	ASSERT(osh && p);
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun 	frame = PKTDATA(osh, p);
67*4882a593Smuzhiyun 	length = PKTLEN(osh, p);
68*4882a593Smuzhiyun 
69*4882a593Smuzhiyun 	/* Process Ethernet II or SNAP-encapsulated 802.3 frames */
70*4882a593Smuzhiyun 	if (length < ETHER_HDR_LEN) {
71*4882a593Smuzhiyun 		DHD_INFO(("%s: short eth frame (%d)\n", __FUNCTION__, length));
72*4882a593Smuzhiyun 		return DHD_PKT_FRAG_NONE;
73*4882a593Smuzhiyun 	} else if (ntoh16(*(uint16 *)(frame + ETHER_TYPE_OFFSET)) >= ETHER_TYPE_MIN) {
74*4882a593Smuzhiyun 		/* Frame is Ethernet II */
75*4882a593Smuzhiyun 		pt = frame + ETHER_TYPE_OFFSET;
76*4882a593Smuzhiyun 	} else if (length >= ETHER_HDR_LEN + SNAP_HDR_LEN + ETHER_TYPE_LEN &&
77*4882a593Smuzhiyun 	           !bcmp(llc_snap_hdr, frame + ETHER_HDR_LEN, SNAP_HDR_LEN)) {
78*4882a593Smuzhiyun 		pt = frame + ETHER_HDR_LEN + SNAP_HDR_LEN;
79*4882a593Smuzhiyun 	} else {
80*4882a593Smuzhiyun 		DHD_INFO(("%s: non-SNAP 802.3 frame\n", __FUNCTION__));
81*4882a593Smuzhiyun 		return DHD_PKT_FRAG_NONE;
82*4882a593Smuzhiyun 	}
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun 	ethertype = ntoh16(*(uint16 *)pt);
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun 	/* Skip VLAN tag, if any */
87*4882a593Smuzhiyun 	if (ethertype == ETHER_TYPE_8021Q) {
88*4882a593Smuzhiyun 		pt += VLAN_TAG_LEN;
89*4882a593Smuzhiyun 
90*4882a593Smuzhiyun 		if (pt + ETHER_TYPE_LEN > frame + length) {
91*4882a593Smuzhiyun 			DHD_INFO(("%s: short VLAN frame (%d)\n", __FUNCTION__, length));
92*4882a593Smuzhiyun 			return DHD_PKT_FRAG_NONE;
93*4882a593Smuzhiyun 		}
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 		ethertype = ntoh16(*(uint16 *)pt);
96*4882a593Smuzhiyun 	}
97*4882a593Smuzhiyun 
98*4882a593Smuzhiyun 	if (ethertype != ETHER_TYPE_IP) {
99*4882a593Smuzhiyun 		DHD_INFO(("%s: non-IP frame (ethertype 0x%x, length %d)\n",
100*4882a593Smuzhiyun 			__FUNCTION__, ethertype, length));
101*4882a593Smuzhiyun 		return DHD_PKT_FRAG_NONE;
102*4882a593Smuzhiyun 	}
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun 	iph = (struct ipv4_hdr *)(pt + ETHER_TYPE_LEN);
105*4882a593Smuzhiyun 	ipl = (uint)(length - (pt + ETHER_TYPE_LEN - frame));
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun 	/* We support IPv4 only */
108*4882a593Smuzhiyun 	if ((ipl < IPV4_OPTIONS_OFFSET) || (IP_VER(iph) != IP_VER_4)) {
109*4882a593Smuzhiyun 		DHD_INFO(("%s: short frame (%d) or non-IPv4\n", __FUNCTION__, ipl));
110*4882a593Smuzhiyun 		return DHD_PKT_FRAG_NONE;
111*4882a593Smuzhiyun 	}
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun 	iph_frag = ntoh16(iph->frag);
114*4882a593Smuzhiyun 
115*4882a593Smuzhiyun 	if (iph_frag & IPV4_FRAG_DONT) {
116*4882a593Smuzhiyun 		return DHD_PKT_FRAG_NONE;
117*4882a593Smuzhiyun 	} else if ((iph_frag & IPV4_FRAG_MORE) == 0) {
118*4882a593Smuzhiyun 		return DHD_PKT_FRAG_LAST;
119*4882a593Smuzhiyun 	} else {
120*4882a593Smuzhiyun 		return (iph_frag & IPV4_FRAG_OFFSET_MASK)? DHD_PKT_FRAG_CONT : DHD_PKT_FRAG_FIRST;
121*4882a593Smuzhiyun 	}
122*4882a593Smuzhiyun }
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun #ifdef DHDTCPACK_SUPPRESS
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun typedef struct {
127*4882a593Smuzhiyun 	void *pkt_in_q;		/* TCP ACK packet that is already in txq or DelayQ */
128*4882a593Smuzhiyun 	void *pkt_ether_hdr;	/* Ethernet header pointer of pkt_in_q */
129*4882a593Smuzhiyun 	int ifidx;
130*4882a593Smuzhiyun 	uint8 supp_cnt;
131*4882a593Smuzhiyun 	dhd_pub_t *dhdp;
132*4882a593Smuzhiyun #ifndef TCPACK_SUPPRESS_HOLD_HRT
133*4882a593Smuzhiyun 	timer_list_compat_t timer;
134*4882a593Smuzhiyun #else
135*4882a593Smuzhiyun #if (LINUX_VERSION_CODE <= KERNEL_VERSION(5, 1, 21))
136*4882a593Smuzhiyun 	struct tasklet_hrtimer timer;
137*4882a593Smuzhiyun #else
138*4882a593Smuzhiyun 	struct hrtimer timer;
139*4882a593Smuzhiyun #endif /* LINUX_VERSION_CODE <= KERNEL_VERSION(5, 1, 21) */
140*4882a593Smuzhiyun #endif /* TCPACK_SUPPRESS_HOLD_HRT */
141*4882a593Smuzhiyun } tcpack_info_t;
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun typedef struct _tdata_psh_info_t {
144*4882a593Smuzhiyun 	uint32 end_seq;			/* end seq# of a received TCP PSH DATA pkt */
145*4882a593Smuzhiyun 	struct _tdata_psh_info_t *next;	/* next pointer of the link chain */
146*4882a593Smuzhiyun } tdata_psh_info_t;
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun typedef struct {
149*4882a593Smuzhiyun 	struct {
150*4882a593Smuzhiyun 		uint8 src[IPV4_ADDR_LEN];	/* SRC ip addrs of this TCP stream */
151*4882a593Smuzhiyun 		uint8 dst[IPV4_ADDR_LEN];	/* DST ip addrs of this TCP stream */
152*4882a593Smuzhiyun 	} ip_addr;
153*4882a593Smuzhiyun 	struct {
154*4882a593Smuzhiyun 		uint8 src[TCP_PORT_LEN];	/* SRC tcp ports of this TCP stream */
155*4882a593Smuzhiyun 		uint8 dst[TCP_PORT_LEN];	/* DST tcp ports of this TCP stream */
156*4882a593Smuzhiyun 	} tcp_port;
157*4882a593Smuzhiyun 	tdata_psh_info_t *tdata_psh_info_head;	/* Head of received TCP PSH DATA chain */
158*4882a593Smuzhiyun 	tdata_psh_info_t *tdata_psh_info_tail;	/* Tail of received TCP PSH DATA chain */
159*4882a593Smuzhiyun 	uint32 last_used_time;	/* The last time this tcpdata_info was used(in ms) */
160*4882a593Smuzhiyun } tcpdata_info_t;
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun /* TCPACK SUPPRESS module */
163*4882a593Smuzhiyun typedef struct {
164*4882a593Smuzhiyun 	int tcpack_info_cnt;
165*4882a593Smuzhiyun 	tcpack_info_t tcpack_info_tbl[TCPACK_INFO_MAXNUM];	/* Info of TCP ACK to send */
166*4882a593Smuzhiyun 	int tcpdata_info_cnt;
167*4882a593Smuzhiyun 	tcpdata_info_t tcpdata_info_tbl[TCPDATA_INFO_MAXNUM];	/* Info of received TCP DATA */
168*4882a593Smuzhiyun 	tdata_psh_info_t *tdata_psh_info_pool;	/* Pointer to tdata_psh_info elements pool */
169*4882a593Smuzhiyun 	tdata_psh_info_t *tdata_psh_info_free;	/* free tdata_psh_info elements chain in pool */
170*4882a593Smuzhiyun #ifdef DHDTCPACK_SUP_DBG
171*4882a593Smuzhiyun 	int psh_info_enq_num;	/* Number of free TCP PSH DATA info elements in pool */
172*4882a593Smuzhiyun #endif /* DHDTCPACK_SUP_DBG */
173*4882a593Smuzhiyun } tcpack_sup_module_t;
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun #if defined(DEBUG_COUNTER) && defined(DHDTCPACK_SUP_DBG)
176*4882a593Smuzhiyun counter_tbl_t tack_tbl = {"tcpACK", 0, 1000, 10, {0, }, 1};
177*4882a593Smuzhiyun #endif /* DEBUG_COUNTER && DHDTCPACK_SUP_DBG */
178*4882a593Smuzhiyun 
179*4882a593Smuzhiyun static void
_tdata_psh_info_pool_enq(tcpack_sup_module_t * tcpack_sup_mod,tdata_psh_info_t * tdata_psh_info)180*4882a593Smuzhiyun _tdata_psh_info_pool_enq(tcpack_sup_module_t *tcpack_sup_mod,
181*4882a593Smuzhiyun 	tdata_psh_info_t *tdata_psh_info)
182*4882a593Smuzhiyun {
183*4882a593Smuzhiyun 	if ((tcpack_sup_mod == NULL) || (tdata_psh_info == NULL)) {
184*4882a593Smuzhiyun 		DHD_ERROR(("%s %d: ERROR %p %p\n", __FUNCTION__, __LINE__,
185*4882a593Smuzhiyun 			tcpack_sup_mod, tdata_psh_info));
186*4882a593Smuzhiyun 		return;
187*4882a593Smuzhiyun 	}
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun 	ASSERT(tdata_psh_info->next == NULL);
190*4882a593Smuzhiyun 	tdata_psh_info->next = tcpack_sup_mod->tdata_psh_info_free;
191*4882a593Smuzhiyun 	tcpack_sup_mod->tdata_psh_info_free = tdata_psh_info;
192*4882a593Smuzhiyun #ifdef DHDTCPACK_SUP_DBG
193*4882a593Smuzhiyun 	tcpack_sup_mod->psh_info_enq_num++;
194*4882a593Smuzhiyun #endif // endif
195*4882a593Smuzhiyun }
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun static tdata_psh_info_t*
_tdata_psh_info_pool_deq(tcpack_sup_module_t * tcpack_sup_mod)198*4882a593Smuzhiyun _tdata_psh_info_pool_deq(tcpack_sup_module_t *tcpack_sup_mod)
199*4882a593Smuzhiyun {
200*4882a593Smuzhiyun 	tdata_psh_info_t *tdata_psh_info = NULL;
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun 	if (tcpack_sup_mod == NULL) {
203*4882a593Smuzhiyun 		DHD_ERROR(("%s %d: ERROR %p\n", __FUNCTION__, __LINE__,
204*4882a593Smuzhiyun 			tcpack_sup_mod));
205*4882a593Smuzhiyun 		return NULL;
206*4882a593Smuzhiyun 	}
207*4882a593Smuzhiyun 
208*4882a593Smuzhiyun 	tdata_psh_info = tcpack_sup_mod->tdata_psh_info_free;
209*4882a593Smuzhiyun 	if (tdata_psh_info == NULL)
210*4882a593Smuzhiyun 		DHD_ERROR(("%s %d: Out of tdata_disc_grp\n", __FUNCTION__, __LINE__));
211*4882a593Smuzhiyun 	else {
212*4882a593Smuzhiyun 		tcpack_sup_mod->tdata_psh_info_free = tdata_psh_info->next;
213*4882a593Smuzhiyun 		tdata_psh_info->next = NULL;
214*4882a593Smuzhiyun #ifdef DHDTCPACK_SUP_DBG
215*4882a593Smuzhiyun 		tcpack_sup_mod->psh_info_enq_num--;
216*4882a593Smuzhiyun #endif /* DHDTCPACK_SUP_DBG */
217*4882a593Smuzhiyun 	}
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	return tdata_psh_info;
220*4882a593Smuzhiyun }
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun #ifdef BCMSDIO
_tdata_psh_info_pool_init(dhd_pub_t * dhdp,tcpack_sup_module_t * tcpack_sup_mod)223*4882a593Smuzhiyun static int _tdata_psh_info_pool_init(dhd_pub_t *dhdp,
224*4882a593Smuzhiyun 	tcpack_sup_module_t *tcpack_sup_mod)
225*4882a593Smuzhiyun {
226*4882a593Smuzhiyun 	tdata_psh_info_t *tdata_psh_info_pool = NULL;
227*4882a593Smuzhiyun 	uint i;
228*4882a593Smuzhiyun 
229*4882a593Smuzhiyun 	DHD_TRACE(("%s %d: Enter\n", __FUNCTION__, __LINE__));
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun 	if (tcpack_sup_mod == NULL)
232*4882a593Smuzhiyun 		return BCME_ERROR;
233*4882a593Smuzhiyun 
234*4882a593Smuzhiyun 	ASSERT(tcpack_sup_mod->tdata_psh_info_pool == NULL);
235*4882a593Smuzhiyun 	ASSERT(tcpack_sup_mod->tdata_psh_info_free == NULL);
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 	tdata_psh_info_pool =
238*4882a593Smuzhiyun 		MALLOC(dhdp->osh, sizeof(tdata_psh_info_t) * TCPDATA_PSH_INFO_MAXNUM);
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun 	if (tdata_psh_info_pool == NULL)
241*4882a593Smuzhiyun 		return BCME_NOMEM;
242*4882a593Smuzhiyun 	bzero(tdata_psh_info_pool, sizeof(tdata_psh_info_t) * TCPDATA_PSH_INFO_MAXNUM);
243*4882a593Smuzhiyun #ifdef DHDTCPACK_SUP_DBG
244*4882a593Smuzhiyun 	tcpack_sup_mod->psh_info_enq_num = 0;
245*4882a593Smuzhiyun #endif /* DHDTCPACK_SUP_DBG */
246*4882a593Smuzhiyun 
247*4882a593Smuzhiyun 	/* Enqueue newly allocated tcpdata psh info elements to the pool */
248*4882a593Smuzhiyun 	for (i = 0; i < TCPDATA_PSH_INFO_MAXNUM; i++)
249*4882a593Smuzhiyun 		_tdata_psh_info_pool_enq(tcpack_sup_mod, &tdata_psh_info_pool[i]);
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun 	ASSERT(tcpack_sup_mod->tdata_psh_info_free != NULL);
252*4882a593Smuzhiyun 	tcpack_sup_mod->tdata_psh_info_pool = tdata_psh_info_pool;
253*4882a593Smuzhiyun 
254*4882a593Smuzhiyun 	return BCME_OK;
255*4882a593Smuzhiyun }
256*4882a593Smuzhiyun 
_tdata_psh_info_pool_deinit(dhd_pub_t * dhdp,tcpack_sup_module_t * tcpack_sup_mod)257*4882a593Smuzhiyun static void _tdata_psh_info_pool_deinit(dhd_pub_t *dhdp,
258*4882a593Smuzhiyun 	tcpack_sup_module_t *tcpack_sup_mod)
259*4882a593Smuzhiyun {
260*4882a593Smuzhiyun 	uint i;
261*4882a593Smuzhiyun 	tdata_psh_info_t *tdata_psh_info;
262*4882a593Smuzhiyun 
263*4882a593Smuzhiyun 	DHD_TRACE(("%s %d: Enter\n", __FUNCTION__, __LINE__));
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun 	if (tcpack_sup_mod == NULL) {
266*4882a593Smuzhiyun 		DHD_ERROR(("%s %d: ERROR tcpack_sup_mod NULL!\n",
267*4882a593Smuzhiyun 			__FUNCTION__, __LINE__));
268*4882a593Smuzhiyun 		return;
269*4882a593Smuzhiyun 	}
270*4882a593Smuzhiyun 
271*4882a593Smuzhiyun 	for (i = 0; i < tcpack_sup_mod->tcpdata_info_cnt; i++) {
272*4882a593Smuzhiyun 		tcpdata_info_t *tcpdata_info = &tcpack_sup_mod->tcpdata_info_tbl[i];
273*4882a593Smuzhiyun 		/* Return tdata_psh_info elements allocated to each tcpdata_info to the pool */
274*4882a593Smuzhiyun 		while ((tdata_psh_info = tcpdata_info->tdata_psh_info_head)) {
275*4882a593Smuzhiyun 			tcpdata_info->tdata_psh_info_head = tdata_psh_info->next;
276*4882a593Smuzhiyun 			tdata_psh_info->next = NULL;
277*4882a593Smuzhiyun 			_tdata_psh_info_pool_enq(tcpack_sup_mod, tdata_psh_info);
278*4882a593Smuzhiyun 		}
279*4882a593Smuzhiyun 		tcpdata_info->tdata_psh_info_tail = NULL;
280*4882a593Smuzhiyun 	}
281*4882a593Smuzhiyun #ifdef DHDTCPACK_SUP_DBG
282*4882a593Smuzhiyun 	DHD_ERROR(("%s %d: PSH INFO ENQ %d\n",
283*4882a593Smuzhiyun 		__FUNCTION__, __LINE__, tcpack_sup_mod->psh_info_enq_num));
284*4882a593Smuzhiyun #endif /* DHDTCPACK_SUP_DBG */
285*4882a593Smuzhiyun 
286*4882a593Smuzhiyun 	i = 0;
287*4882a593Smuzhiyun 	/* Be sure we recollected all tdata_psh_info elements */
288*4882a593Smuzhiyun 	while ((tdata_psh_info = tcpack_sup_mod->tdata_psh_info_free)) {
289*4882a593Smuzhiyun 		tcpack_sup_mod->tdata_psh_info_free = tdata_psh_info->next;
290*4882a593Smuzhiyun 		tdata_psh_info->next = NULL;
291*4882a593Smuzhiyun 		i++;
292*4882a593Smuzhiyun 	}
293*4882a593Smuzhiyun 	ASSERT(i == TCPDATA_PSH_INFO_MAXNUM);
294*4882a593Smuzhiyun 	MFREE(dhdp->osh, tcpack_sup_mod->tdata_psh_info_pool,
295*4882a593Smuzhiyun 		sizeof(tdata_psh_info_t) * TCPDATA_PSH_INFO_MAXNUM);
296*4882a593Smuzhiyun 	tcpack_sup_mod->tdata_psh_info_pool = NULL;
297*4882a593Smuzhiyun 
298*4882a593Smuzhiyun 	return;
299*4882a593Smuzhiyun }
300*4882a593Smuzhiyun #endif /* BCMSDIO */
301*4882a593Smuzhiyun 
302*4882a593Smuzhiyun #ifdef BCMPCIE
303*4882a593Smuzhiyun #ifndef TCPACK_SUPPRESS_HOLD_HRT
dhd_tcpack_send(ulong data)304*4882a593Smuzhiyun static void dhd_tcpack_send(ulong data)
305*4882a593Smuzhiyun #else
306*4882a593Smuzhiyun static enum hrtimer_restart dhd_tcpack_send(struct hrtimer *timer)
307*4882a593Smuzhiyun #endif /* TCPACK_SUPPRESS_HOLD_HRT */
308*4882a593Smuzhiyun {
309*4882a593Smuzhiyun 	tcpack_sup_module_t *tcpack_sup_mod;
310*4882a593Smuzhiyun 	tcpack_info_t *cur_tbl;
311*4882a593Smuzhiyun 	dhd_pub_t *dhdp;
312*4882a593Smuzhiyun 	int ifidx;
313*4882a593Smuzhiyun 	void* pkt;
314*4882a593Smuzhiyun 	unsigned long flags;
315*4882a593Smuzhiyun 
316*4882a593Smuzhiyun #ifndef TCPACK_SUPPRESS_HOLD_HRT
317*4882a593Smuzhiyun 	cur_tbl = (tcpack_info_t *)data;
318*4882a593Smuzhiyun #else
319*4882a593Smuzhiyun 	cur_tbl = container_of(timer, tcpack_info_t, timer.timer);
320*4882a593Smuzhiyun #endif /* TCPACK_SUPPRESS_HOLD_HRT */
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun 	if (!cur_tbl) {
323*4882a593Smuzhiyun 		goto done;
324*4882a593Smuzhiyun 	}
325*4882a593Smuzhiyun 
326*4882a593Smuzhiyun 	dhdp = cur_tbl->dhdp;
327*4882a593Smuzhiyun 	if (!dhdp) {
328*4882a593Smuzhiyun 		goto done;
329*4882a593Smuzhiyun 	}
330*4882a593Smuzhiyun 
331*4882a593Smuzhiyun 	flags = dhd_os_tcpacklock(dhdp);
332*4882a593Smuzhiyun 
333*4882a593Smuzhiyun 	if (unlikely(dhdp->tcpack_sup_mode != TCPACK_SUP_HOLD)) {
334*4882a593Smuzhiyun 		dhd_os_tcpackunlock(dhdp, flags);
335*4882a593Smuzhiyun 		goto done;
336*4882a593Smuzhiyun 	}
337*4882a593Smuzhiyun 
338*4882a593Smuzhiyun 	tcpack_sup_mod = dhdp->tcpack_sup_module;
339*4882a593Smuzhiyun 	if (!tcpack_sup_mod) {
340*4882a593Smuzhiyun 		DHD_ERROR(("%s %d: tcpack suppress module NULL!!\n",
341*4882a593Smuzhiyun 			__FUNCTION__, __LINE__));
342*4882a593Smuzhiyun 		dhd_os_tcpackunlock(dhdp, flags);
343*4882a593Smuzhiyun 		goto done;
344*4882a593Smuzhiyun 	}
345*4882a593Smuzhiyun 	pkt = cur_tbl->pkt_in_q;
346*4882a593Smuzhiyun 	ifidx = cur_tbl->ifidx;
347*4882a593Smuzhiyun 	if (!pkt) {
348*4882a593Smuzhiyun 		dhd_os_tcpackunlock(dhdp, flags);
349*4882a593Smuzhiyun 		goto done;
350*4882a593Smuzhiyun 	}
351*4882a593Smuzhiyun 	cur_tbl->pkt_in_q = NULL;
352*4882a593Smuzhiyun 	cur_tbl->pkt_ether_hdr = NULL;
353*4882a593Smuzhiyun 	cur_tbl->ifidx = 0;
354*4882a593Smuzhiyun 	cur_tbl->supp_cnt = 0;
355*4882a593Smuzhiyun 	if (--tcpack_sup_mod->tcpack_info_cnt < 0) {
356*4882a593Smuzhiyun 		DHD_ERROR(("%s %d: ERROR!!! tcp_ack_info_cnt %d\n",
357*4882a593Smuzhiyun 			__FUNCTION__, __LINE__, tcpack_sup_mod->tcpack_info_cnt));
358*4882a593Smuzhiyun 	}
359*4882a593Smuzhiyun 
360*4882a593Smuzhiyun 	dhd_os_tcpackunlock(dhdp, flags);
361*4882a593Smuzhiyun 
362*4882a593Smuzhiyun 	dhd_sendpkt(dhdp, ifidx, pkt);
363*4882a593Smuzhiyun 
364*4882a593Smuzhiyun done:
365*4882a593Smuzhiyun #ifndef TCPACK_SUPPRESS_HOLD_HRT
366*4882a593Smuzhiyun 	return;
367*4882a593Smuzhiyun #else
368*4882a593Smuzhiyun 	return HRTIMER_NORESTART;
369*4882a593Smuzhiyun #endif /* TCPACK_SUPPRESS_HOLD_HRT */
370*4882a593Smuzhiyun }
371*4882a593Smuzhiyun #endif /* BCMPCIE */
372*4882a593Smuzhiyun 
dhd_tcpack_suppress_set(dhd_pub_t * dhdp,uint8 mode)373*4882a593Smuzhiyun int dhd_tcpack_suppress_set(dhd_pub_t *dhdp, uint8 mode)
374*4882a593Smuzhiyun {
375*4882a593Smuzhiyun 	int ret = BCME_OK;
376*4882a593Smuzhiyun 	unsigned long flags;
377*4882a593Smuzhiyun 	tcpack_sup_module_t *tcpack_sup_module;
378*4882a593Smuzhiyun 	uint8 invalid_mode = FALSE;
379*4882a593Smuzhiyun 	int prev_mode;
380*4882a593Smuzhiyun 	int i = 0;
381*4882a593Smuzhiyun 
382*4882a593Smuzhiyun 	flags = dhd_os_tcpacklock(dhdp);
383*4882a593Smuzhiyun 	tcpack_sup_module = dhdp->tcpack_sup_module;
384*4882a593Smuzhiyun 	prev_mode = dhdp->tcpack_sup_mode;
385*4882a593Smuzhiyun 
386*4882a593Smuzhiyun 	if (prev_mode == mode) {
387*4882a593Smuzhiyun 		DHD_ERROR(("%s %d: already set to %d\n", __FUNCTION__, __LINE__, mode));
388*4882a593Smuzhiyun 		goto exit;
389*4882a593Smuzhiyun 	}
390*4882a593Smuzhiyun 
391*4882a593Smuzhiyun 	invalid_mode |= (mode >= TCPACK_SUP_LAST_MODE);
392*4882a593Smuzhiyun #ifdef BCMSDIO
393*4882a593Smuzhiyun 	invalid_mode |= (mode == TCPACK_SUP_HOLD);
394*4882a593Smuzhiyun #endif /* BCMSDIO */
395*4882a593Smuzhiyun #ifdef BCMPCIE
396*4882a593Smuzhiyun 	invalid_mode |= ((mode == TCPACK_SUP_REPLACE) || (mode == TCPACK_SUP_DELAYTX));
397*4882a593Smuzhiyun #endif /* BCMPCIE */
398*4882a593Smuzhiyun 
399*4882a593Smuzhiyun 	if (invalid_mode) {
400*4882a593Smuzhiyun 		DHD_ERROR(("%s %d: Invalid TCP ACK Suppress mode %d\n",
401*4882a593Smuzhiyun 			__FUNCTION__, __LINE__, mode));
402*4882a593Smuzhiyun 		ret = BCME_BADARG;
403*4882a593Smuzhiyun 		goto exit;
404*4882a593Smuzhiyun 	}
405*4882a593Smuzhiyun 
406*4882a593Smuzhiyun 	DHD_TRACE(("%s: TCP ACK Suppress mode %d -> mode %d\n",
407*4882a593Smuzhiyun 		__FUNCTION__, dhdp->tcpack_sup_mode, mode));
408*4882a593Smuzhiyun 
409*4882a593Smuzhiyun 	/* Pre-process routines to change a new mode as per previous mode */
410*4882a593Smuzhiyun 	switch (prev_mode) {
411*4882a593Smuzhiyun 		case TCPACK_SUP_OFF:
412*4882a593Smuzhiyun 			if (tcpack_sup_module == NULL) {
413*4882a593Smuzhiyun 				tcpack_sup_module = MALLOC(dhdp->osh, sizeof(tcpack_sup_module_t));
414*4882a593Smuzhiyun 				if (tcpack_sup_module == NULL) {
415*4882a593Smuzhiyun 					DHD_ERROR(("%s[%d]: Failed to allocate the new memory for "
416*4882a593Smuzhiyun 						"tcpack_sup_module\n", __FUNCTION__, __LINE__));
417*4882a593Smuzhiyun 					dhdp->tcpack_sup_mode = TCPACK_SUP_OFF;
418*4882a593Smuzhiyun 					ret = BCME_NOMEM;
419*4882a593Smuzhiyun 					goto exit;
420*4882a593Smuzhiyun 				}
421*4882a593Smuzhiyun 				dhdp->tcpack_sup_module = tcpack_sup_module;
422*4882a593Smuzhiyun 			}
423*4882a593Smuzhiyun 			bzero(tcpack_sup_module, sizeof(tcpack_sup_module_t));
424*4882a593Smuzhiyun 			break;
425*4882a593Smuzhiyun #ifdef BCMSDIO
426*4882a593Smuzhiyun 		case TCPACK_SUP_DELAYTX:
427*4882a593Smuzhiyun 			if (tcpack_sup_module) {
428*4882a593Smuzhiyun 				/* We won't need tdata_psh_info pool and
429*4882a593Smuzhiyun 				 * tcpddata_info_tbl anymore
430*4882a593Smuzhiyun 				 */
431*4882a593Smuzhiyun 				_tdata_psh_info_pool_deinit(dhdp, tcpack_sup_module);
432*4882a593Smuzhiyun 				tcpack_sup_module->tcpdata_info_cnt = 0;
433*4882a593Smuzhiyun 				bzero(tcpack_sup_module->tcpdata_info_tbl,
434*4882a593Smuzhiyun 					sizeof(tcpdata_info_t) * TCPDATA_INFO_MAXNUM);
435*4882a593Smuzhiyun 			}
436*4882a593Smuzhiyun 
437*4882a593Smuzhiyun 			/* For half duplex bus interface, tx precedes rx by default */
438*4882a593Smuzhiyun 			if (dhdp->bus) {
439*4882a593Smuzhiyun 				dhd_bus_set_dotxinrx(dhdp->bus, TRUE);
440*4882a593Smuzhiyun 			}
441*4882a593Smuzhiyun 
442*4882a593Smuzhiyun 			if (tcpack_sup_module == NULL) {
443*4882a593Smuzhiyun 				DHD_ERROR(("%s[%d]: tcpack_sup_module should not be NULL\n",
444*4882a593Smuzhiyun 					__FUNCTION__, __LINE__));
445*4882a593Smuzhiyun 				dhdp->tcpack_sup_mode = TCPACK_SUP_OFF;
446*4882a593Smuzhiyun 				goto exit;
447*4882a593Smuzhiyun 			}
448*4882a593Smuzhiyun 			break;
449*4882a593Smuzhiyun #endif /* BCMSDIO */
450*4882a593Smuzhiyun 	}
451*4882a593Smuzhiyun 
452*4882a593Smuzhiyun 	/* Update a new mode */
453*4882a593Smuzhiyun 	dhdp->tcpack_sup_mode = mode;
454*4882a593Smuzhiyun 
455*4882a593Smuzhiyun 	/* Process for a new mode */
456*4882a593Smuzhiyun 	switch (mode) {
457*4882a593Smuzhiyun 		case TCPACK_SUP_OFF:
458*4882a593Smuzhiyun 			ASSERT(tcpack_sup_module != NULL);
459*4882a593Smuzhiyun 			/* Clean up timer/data structure for
460*4882a593Smuzhiyun 			 * any remaining/pending packet or timer.
461*4882a593Smuzhiyun 			 */
462*4882a593Smuzhiyun 			if (tcpack_sup_module) {
463*4882a593Smuzhiyun 				/* Check if previous mode is TCAPACK_SUP_HOLD */
464*4882a593Smuzhiyun 				if (prev_mode == TCPACK_SUP_HOLD) {
465*4882a593Smuzhiyun 					for (i = 0; i < TCPACK_INFO_MAXNUM; i++) {
466*4882a593Smuzhiyun 						tcpack_info_t *tcpack_info_tbl =
467*4882a593Smuzhiyun 							&tcpack_sup_module->tcpack_info_tbl[i];
468*4882a593Smuzhiyun #ifndef TCPACK_SUPPRESS_HOLD_HRT
469*4882a593Smuzhiyun 						del_timer(&tcpack_info_tbl->timer);
470*4882a593Smuzhiyun #else
471*4882a593Smuzhiyun 						hrtimer_cancel(&tcpack_info_tbl->timer.timer);
472*4882a593Smuzhiyun #endif /* TCPACK_SUPPRESS_HOLD_HRT */
473*4882a593Smuzhiyun 						if (tcpack_info_tbl->pkt_in_q) {
474*4882a593Smuzhiyun 							PKTFREE(dhdp->osh,
475*4882a593Smuzhiyun 								tcpack_info_tbl->pkt_in_q, TRUE);
476*4882a593Smuzhiyun 							tcpack_info_tbl->pkt_in_q = NULL;
477*4882a593Smuzhiyun 						}
478*4882a593Smuzhiyun 					}
479*4882a593Smuzhiyun 				}
480*4882a593Smuzhiyun 				MFREE(dhdp->osh, tcpack_sup_module, sizeof(tcpack_sup_module_t));
481*4882a593Smuzhiyun 				dhdp->tcpack_sup_module = NULL;
482*4882a593Smuzhiyun 			} else {
483*4882a593Smuzhiyun 				DHD_ERROR(("%s[%d]: tcpack_sup_module should not be NULL\n",
484*4882a593Smuzhiyun 					__FUNCTION__, __LINE__));
485*4882a593Smuzhiyun 			}
486*4882a593Smuzhiyun 			break;
487*4882a593Smuzhiyun #ifdef BCMSDIO
488*4882a593Smuzhiyun 		case TCPACK_SUP_REPLACE:
489*4882a593Smuzhiyun 			/* There is nothing to configure for this mode */
490*4882a593Smuzhiyun 			break;
491*4882a593Smuzhiyun 		case TCPACK_SUP_DELAYTX:
492*4882a593Smuzhiyun 			ret = _tdata_psh_info_pool_init(dhdp, tcpack_sup_module);
493*4882a593Smuzhiyun 			if (ret != BCME_OK) {
494*4882a593Smuzhiyun 				DHD_ERROR(("%s %d: pool init fail with %d\n",
495*4882a593Smuzhiyun 					__FUNCTION__, __LINE__, ret));
496*4882a593Smuzhiyun 				break;
497*4882a593Smuzhiyun 			}
498*4882a593Smuzhiyun 			if (dhdp->bus) {
499*4882a593Smuzhiyun 				dhd_bus_set_dotxinrx(dhdp->bus, FALSE);
500*4882a593Smuzhiyun 			}
501*4882a593Smuzhiyun 			break;
502*4882a593Smuzhiyun #endif /* BCMSDIO */
503*4882a593Smuzhiyun #ifdef BCMPCIE
504*4882a593Smuzhiyun 		case TCPACK_SUP_HOLD:
505*4882a593Smuzhiyun 			dhdp->tcpack_sup_ratio = CUSTOM_TCPACK_SUPP_RATIO;
506*4882a593Smuzhiyun 			dhdp->tcpack_sup_delay = CUSTOM_TCPACK_DELAY_TIME;
507*4882a593Smuzhiyun 			for (i = 0; i < TCPACK_INFO_MAXNUM; i++) {
508*4882a593Smuzhiyun 				tcpack_info_t *tcpack_info_tbl =
509*4882a593Smuzhiyun 					&tcpack_sup_module->tcpack_info_tbl[i];
510*4882a593Smuzhiyun 				tcpack_info_tbl->dhdp = dhdp;
511*4882a593Smuzhiyun #ifndef TCPACK_SUPPRESS_HOLD_HRT
512*4882a593Smuzhiyun 				init_timer_compat(&tcpack_info_tbl->timer,
513*4882a593Smuzhiyun 					dhd_tcpack_send, tcpack_info_tbl);
514*4882a593Smuzhiyun #else
515*4882a593Smuzhiyun #if (LINUX_VERSION_CODE <= KERNEL_VERSION(5, 1, 21))
516*4882a593Smuzhiyun 				tasklet_hrtimer_init(&tcpack_info_tbl->timer,
517*4882a593Smuzhiyun 					dhd_tcpack_send, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
518*4882a593Smuzhiyun #else
519*4882a593Smuzhiyun 				hrtimer_init(&tcpack_info_tbl->timer, CLOCK_MONOTONIC,
520*4882a593Smuzhiyun 					HRTIMER_MODE_REL_SOFT);
521*4882a593Smuzhiyun #endif /* LINUX_VERSION_CODE <= KERNEL_VERSION(5, 1, 21) */
522*4882a593Smuzhiyun #endif /* TCPACK_SUPPRESS_HOLD_HRT */
523*4882a593Smuzhiyun 			}
524*4882a593Smuzhiyun 			break;
525*4882a593Smuzhiyun #endif /* BCMPCIE */
526*4882a593Smuzhiyun 	}
527*4882a593Smuzhiyun 
528*4882a593Smuzhiyun exit:
529*4882a593Smuzhiyun 	dhd_os_tcpackunlock(dhdp, flags);
530*4882a593Smuzhiyun 	return ret;
531*4882a593Smuzhiyun }
532*4882a593Smuzhiyun 
533*4882a593Smuzhiyun void
dhd_tcpack_info_tbl_clean(dhd_pub_t * dhdp)534*4882a593Smuzhiyun dhd_tcpack_info_tbl_clean(dhd_pub_t *dhdp)
535*4882a593Smuzhiyun {
536*4882a593Smuzhiyun 	tcpack_sup_module_t *tcpack_sup_mod = dhdp->tcpack_sup_module;
537*4882a593Smuzhiyun 	int i;
538*4882a593Smuzhiyun 	unsigned long flags;
539*4882a593Smuzhiyun 
540*4882a593Smuzhiyun 	if (dhdp->tcpack_sup_mode == TCPACK_SUP_OFF)
541*4882a593Smuzhiyun 		goto exit;
542*4882a593Smuzhiyun 
543*4882a593Smuzhiyun 	flags = dhd_os_tcpacklock(dhdp);
544*4882a593Smuzhiyun 
545*4882a593Smuzhiyun 	if (!tcpack_sup_mod) {
546*4882a593Smuzhiyun 		DHD_ERROR(("%s %d: tcpack suppress module NULL!!\n",
547*4882a593Smuzhiyun 			__FUNCTION__, __LINE__));
548*4882a593Smuzhiyun 		dhd_os_tcpackunlock(dhdp, flags);
549*4882a593Smuzhiyun 		goto exit;
550*4882a593Smuzhiyun 	}
551*4882a593Smuzhiyun 
552*4882a593Smuzhiyun 	if (dhdp->tcpack_sup_mode == TCPACK_SUP_HOLD) {
553*4882a593Smuzhiyun 		for (i = 0; i < TCPACK_INFO_MAXNUM; i++) {
554*4882a593Smuzhiyun 			if (tcpack_sup_mod->tcpack_info_tbl[i].pkt_in_q) {
555*4882a593Smuzhiyun 				PKTFREE(dhdp->osh, tcpack_sup_mod->tcpack_info_tbl[i].pkt_in_q,
556*4882a593Smuzhiyun 					TRUE);
557*4882a593Smuzhiyun 				tcpack_sup_mod->tcpack_info_tbl[i].pkt_in_q = NULL;
558*4882a593Smuzhiyun 				tcpack_sup_mod->tcpack_info_tbl[i].pkt_ether_hdr = NULL;
559*4882a593Smuzhiyun 				tcpack_sup_mod->tcpack_info_tbl[i].ifidx = 0;
560*4882a593Smuzhiyun 				tcpack_sup_mod->tcpack_info_tbl[i].supp_cnt = 0;
561*4882a593Smuzhiyun 			}
562*4882a593Smuzhiyun 		}
563*4882a593Smuzhiyun 	} else {
564*4882a593Smuzhiyun 		tcpack_sup_mod->tcpack_info_cnt = 0;
565*4882a593Smuzhiyun 		bzero(tcpack_sup_mod->tcpack_info_tbl, sizeof(tcpack_info_t) * TCPACK_INFO_MAXNUM);
566*4882a593Smuzhiyun 	}
567*4882a593Smuzhiyun 
568*4882a593Smuzhiyun 	dhd_os_tcpackunlock(dhdp, flags);
569*4882a593Smuzhiyun 
570*4882a593Smuzhiyun 	if (dhdp->tcpack_sup_mode == TCPACK_SUP_HOLD) {
571*4882a593Smuzhiyun 		for (i = 0; i < TCPACK_INFO_MAXNUM; i++) {
572*4882a593Smuzhiyun #ifndef TCPACK_SUPPRESS_HOLD_HRT
573*4882a593Smuzhiyun 			del_timer_sync(&tcpack_sup_mod->tcpack_info_tbl[i].timer);
574*4882a593Smuzhiyun #else
575*4882a593Smuzhiyun 			hrtimer_cancel(&tcpack_sup_mod->tcpack_info_tbl[i].timer.timer);
576*4882a593Smuzhiyun #endif /* TCPACK_SUPPRESS_HOLD_HRT */
577*4882a593Smuzhiyun 		}
578*4882a593Smuzhiyun 	}
579*4882a593Smuzhiyun 
580*4882a593Smuzhiyun exit:
581*4882a593Smuzhiyun 	return;
582*4882a593Smuzhiyun }
583*4882a593Smuzhiyun 
dhd_tcpack_check_xmit(dhd_pub_t * dhdp,void * pkt)584*4882a593Smuzhiyun inline int dhd_tcpack_check_xmit(dhd_pub_t *dhdp, void *pkt)
585*4882a593Smuzhiyun {
586*4882a593Smuzhiyun 	uint8 i;
587*4882a593Smuzhiyun 	tcpack_sup_module_t *tcpack_sup_mod;
588*4882a593Smuzhiyun 	tcpack_info_t *tcpack_info_tbl;
589*4882a593Smuzhiyun 	int tbl_cnt;
590*4882a593Smuzhiyun 	int ret = BCME_OK;
591*4882a593Smuzhiyun 	void *pdata;
592*4882a593Smuzhiyun 	uint32 pktlen;
593*4882a593Smuzhiyun 	unsigned long flags;
594*4882a593Smuzhiyun 
595*4882a593Smuzhiyun 	if (dhdp->tcpack_sup_mode == TCPACK_SUP_OFF)
596*4882a593Smuzhiyun 		goto exit;
597*4882a593Smuzhiyun 
598*4882a593Smuzhiyun 	pdata = PKTDATA(dhdp->osh, pkt);
599*4882a593Smuzhiyun 	pktlen = PKTLEN(dhdp->osh, pkt) - dhd_prot_hdrlen(dhdp, pdata);
600*4882a593Smuzhiyun 
601*4882a593Smuzhiyun 	if (pktlen < TCPACKSZMIN || pktlen > TCPACKSZMAX) {
602*4882a593Smuzhiyun 		DHD_TRACE(("%s %d: Too short or long length %d to be TCP ACK\n",
603*4882a593Smuzhiyun 			__FUNCTION__, __LINE__, pktlen));
604*4882a593Smuzhiyun 		goto exit;
605*4882a593Smuzhiyun 	}
606*4882a593Smuzhiyun 
607*4882a593Smuzhiyun 	flags = dhd_os_tcpacklock(dhdp);
608*4882a593Smuzhiyun 	tcpack_sup_mod = dhdp->tcpack_sup_module;
609*4882a593Smuzhiyun 
610*4882a593Smuzhiyun 	if (!tcpack_sup_mod) {
611*4882a593Smuzhiyun 		DHD_ERROR(("%s %d: tcpack suppress module NULL!!\n", __FUNCTION__, __LINE__));
612*4882a593Smuzhiyun 		ret = BCME_ERROR;
613*4882a593Smuzhiyun 		dhd_os_tcpackunlock(dhdp, flags);
614*4882a593Smuzhiyun 		goto exit;
615*4882a593Smuzhiyun 	}
616*4882a593Smuzhiyun 	tbl_cnt = tcpack_sup_mod->tcpack_info_cnt;
617*4882a593Smuzhiyun 	tcpack_info_tbl = tcpack_sup_mod->tcpack_info_tbl;
618*4882a593Smuzhiyun 
619*4882a593Smuzhiyun 	ASSERT(tbl_cnt <= TCPACK_INFO_MAXNUM);
620*4882a593Smuzhiyun 
621*4882a593Smuzhiyun 	for (i = 0; i < tbl_cnt; i++) {
622*4882a593Smuzhiyun 		if (tcpack_info_tbl[i].pkt_in_q == pkt) {
623*4882a593Smuzhiyun 			DHD_TRACE(("%s %d: pkt %p sent out. idx %d, tbl_cnt %d\n",
624*4882a593Smuzhiyun 				__FUNCTION__, __LINE__, pkt, i, tbl_cnt));
625*4882a593Smuzhiyun 			/* This pkt is being transmitted so remove the tcp_ack_info of it. */
626*4882a593Smuzhiyun 			if (i < tbl_cnt - 1) {
627*4882a593Smuzhiyun 				bcopy(&tcpack_info_tbl[tbl_cnt - 1],
628*4882a593Smuzhiyun 					&tcpack_info_tbl[i], sizeof(tcpack_info_t));
629*4882a593Smuzhiyun 			}
630*4882a593Smuzhiyun 			bzero(&tcpack_info_tbl[tbl_cnt - 1], sizeof(tcpack_info_t));
631*4882a593Smuzhiyun 			if (--tcpack_sup_mod->tcpack_info_cnt < 0) {
632*4882a593Smuzhiyun 				DHD_ERROR(("%s %d: ERROR!!! tcp_ack_info_cnt %d\n",
633*4882a593Smuzhiyun 					__FUNCTION__, __LINE__, tcpack_sup_mod->tcpack_info_cnt));
634*4882a593Smuzhiyun 				ret = BCME_ERROR;
635*4882a593Smuzhiyun 			}
636*4882a593Smuzhiyun 			break;
637*4882a593Smuzhiyun 		}
638*4882a593Smuzhiyun 	}
639*4882a593Smuzhiyun 	dhd_os_tcpackunlock(dhdp, flags);
640*4882a593Smuzhiyun 
641*4882a593Smuzhiyun exit:
642*4882a593Smuzhiyun 	return ret;
643*4882a593Smuzhiyun }
644*4882a593Smuzhiyun 
dhd_tcpdata_psh_acked(dhd_pub_t * dhdp,uint8 * ip_hdr,uint8 * tcp_hdr,uint32 tcp_ack_num)645*4882a593Smuzhiyun static INLINE bool dhd_tcpdata_psh_acked(dhd_pub_t *dhdp, uint8 *ip_hdr,
646*4882a593Smuzhiyun 	uint8 *tcp_hdr, uint32 tcp_ack_num)
647*4882a593Smuzhiyun {
648*4882a593Smuzhiyun 	tcpack_sup_module_t *tcpack_sup_mod;
649*4882a593Smuzhiyun 	int i;
650*4882a593Smuzhiyun 	tcpdata_info_t *tcpdata_info = NULL;
651*4882a593Smuzhiyun 	tdata_psh_info_t *tdata_psh_info = NULL;
652*4882a593Smuzhiyun 	bool ret = FALSE;
653*4882a593Smuzhiyun 
654*4882a593Smuzhiyun 	if (dhdp->tcpack_sup_mode != TCPACK_SUP_DELAYTX)
655*4882a593Smuzhiyun 		goto exit;
656*4882a593Smuzhiyun 
657*4882a593Smuzhiyun 	tcpack_sup_mod = dhdp->tcpack_sup_module;
658*4882a593Smuzhiyun 
659*4882a593Smuzhiyun 	if (!tcpack_sup_mod) {
660*4882a593Smuzhiyun 		DHD_ERROR(("%s %d: tcpack suppress module NULL!!\n", __FUNCTION__, __LINE__));
661*4882a593Smuzhiyun 		goto exit;
662*4882a593Smuzhiyun 	}
663*4882a593Smuzhiyun 
664*4882a593Smuzhiyun 	DHD_TRACE(("%s %d: IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR
665*4882a593Smuzhiyun 		" TCP port %d %d, ack %u\n", __FUNCTION__, __LINE__,
666*4882a593Smuzhiyun 		IPV4_ADDR_TO_STR(ntoh32_ua(&ip_hdr[IPV4_SRC_IP_OFFSET])),
667*4882a593Smuzhiyun 		IPV4_ADDR_TO_STR(ntoh32_ua(&ip_hdr[IPV4_DEST_IP_OFFSET])),
668*4882a593Smuzhiyun 		ntoh16_ua(&tcp_hdr[TCP_SRC_PORT_OFFSET]),
669*4882a593Smuzhiyun 		ntoh16_ua(&tcp_hdr[TCP_DEST_PORT_OFFSET]),
670*4882a593Smuzhiyun 		tcp_ack_num));
671*4882a593Smuzhiyun 
672*4882a593Smuzhiyun 	for (i = 0; i < tcpack_sup_mod->tcpdata_info_cnt; i++) {
673*4882a593Smuzhiyun 		tcpdata_info_t *tcpdata_info_tmp = &tcpack_sup_mod->tcpdata_info_tbl[i];
674*4882a593Smuzhiyun 		DHD_TRACE(("%s %d: data info[%d], IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR
675*4882a593Smuzhiyun 			" TCP port %d %d\n", __FUNCTION__, __LINE__, i,
676*4882a593Smuzhiyun 			IPV4_ADDR_TO_STR(ntoh32_ua(tcpdata_info_tmp->ip_addr.src)),
677*4882a593Smuzhiyun 			IPV4_ADDR_TO_STR(ntoh32_ua(tcpdata_info_tmp->ip_addr.dst)),
678*4882a593Smuzhiyun 			ntoh16_ua(tcpdata_info_tmp->tcp_port.src),
679*4882a593Smuzhiyun 			ntoh16_ua(tcpdata_info_tmp->tcp_port.dst)));
680*4882a593Smuzhiyun 
681*4882a593Smuzhiyun 		/* If either IP address or TCP port number does not match, skip. */
682*4882a593Smuzhiyun 		if (memcmp(&ip_hdr[IPV4_SRC_IP_OFFSET],
683*4882a593Smuzhiyun 			tcpdata_info_tmp->ip_addr.dst, IPV4_ADDR_LEN) == 0 &&
684*4882a593Smuzhiyun 			memcmp(&ip_hdr[IPV4_DEST_IP_OFFSET],
685*4882a593Smuzhiyun 			tcpdata_info_tmp->ip_addr.src, IPV4_ADDR_LEN) == 0 &&
686*4882a593Smuzhiyun 			memcmp(&tcp_hdr[TCP_SRC_PORT_OFFSET],
687*4882a593Smuzhiyun 			tcpdata_info_tmp->tcp_port.dst, TCP_PORT_LEN) == 0 &&
688*4882a593Smuzhiyun 			memcmp(&tcp_hdr[TCP_DEST_PORT_OFFSET],
689*4882a593Smuzhiyun 			tcpdata_info_tmp->tcp_port.src, TCP_PORT_LEN) == 0) {
690*4882a593Smuzhiyun 			tcpdata_info = tcpdata_info_tmp;
691*4882a593Smuzhiyun 			break;
692*4882a593Smuzhiyun 		}
693*4882a593Smuzhiyun 	}
694*4882a593Smuzhiyun 
695*4882a593Smuzhiyun 	if (tcpdata_info == NULL) {
696*4882a593Smuzhiyun 		DHD_TRACE(("%s %d: no tcpdata_info!\n", __FUNCTION__, __LINE__));
697*4882a593Smuzhiyun 		goto exit;
698*4882a593Smuzhiyun 	}
699*4882a593Smuzhiyun 
700*4882a593Smuzhiyun 	if (tcpdata_info->tdata_psh_info_head == NULL) {
701*4882a593Smuzhiyun 		DHD_TRACE(("%s %d: No PSH DATA to be acked!\n", __FUNCTION__, __LINE__));
702*4882a593Smuzhiyun 	}
703*4882a593Smuzhiyun 
704*4882a593Smuzhiyun 	while ((tdata_psh_info = tcpdata_info->tdata_psh_info_head)) {
705*4882a593Smuzhiyun 		if (IS_TCPSEQ_GE(tcp_ack_num, tdata_psh_info->end_seq)) {
706*4882a593Smuzhiyun 			DHD_TRACE(("%s %d: PSH ACKED! %u >= %u\n",
707*4882a593Smuzhiyun 				__FUNCTION__, __LINE__, tcp_ack_num, tdata_psh_info->end_seq));
708*4882a593Smuzhiyun 			tcpdata_info->tdata_psh_info_head = tdata_psh_info->next;
709*4882a593Smuzhiyun 			tdata_psh_info->next = NULL;
710*4882a593Smuzhiyun 			_tdata_psh_info_pool_enq(tcpack_sup_mod, tdata_psh_info);
711*4882a593Smuzhiyun 			ret = TRUE;
712*4882a593Smuzhiyun 		} else
713*4882a593Smuzhiyun 			break;
714*4882a593Smuzhiyun 	}
715*4882a593Smuzhiyun 	if (tdata_psh_info == NULL)
716*4882a593Smuzhiyun 		tcpdata_info->tdata_psh_info_tail = NULL;
717*4882a593Smuzhiyun 
718*4882a593Smuzhiyun #ifdef DHDTCPACK_SUP_DBG
719*4882a593Smuzhiyun 	DHD_TRACE(("%s %d: PSH INFO ENQ %d\n",
720*4882a593Smuzhiyun 		__FUNCTION__, __LINE__, tcpack_sup_mod->psh_info_enq_num));
721*4882a593Smuzhiyun #endif /* DHDTCPACK_SUP_DBG */
722*4882a593Smuzhiyun 
723*4882a593Smuzhiyun exit:
724*4882a593Smuzhiyun 	return ret;
725*4882a593Smuzhiyun }
726*4882a593Smuzhiyun 
727*4882a593Smuzhiyun bool
dhd_tcpack_suppress(dhd_pub_t * dhdp,void * pkt)728*4882a593Smuzhiyun dhd_tcpack_suppress(dhd_pub_t *dhdp, void *pkt)
729*4882a593Smuzhiyun {
730*4882a593Smuzhiyun 	uint8 *new_ether_hdr;	/* Ethernet header of the new packet */
731*4882a593Smuzhiyun 	uint16 new_ether_type;	/* Ethernet type of the new packet */
732*4882a593Smuzhiyun 	uint8 *new_ip_hdr;		/* IP header of the new packet */
733*4882a593Smuzhiyun 	uint8 *new_tcp_hdr;		/* TCP header of the new packet */
734*4882a593Smuzhiyun 	uint32 new_ip_hdr_len;	/* IP header length of the new packet */
735*4882a593Smuzhiyun 	uint32 cur_framelen;
736*4882a593Smuzhiyun 	uint32 new_tcp_ack_num;		/* TCP acknowledge number of the new packet */
737*4882a593Smuzhiyun 	uint16 new_ip_total_len;	/* Total length of IP packet for the new packet */
738*4882a593Smuzhiyun 	uint32 new_tcp_hdr_len;		/* TCP header length of the new packet */
739*4882a593Smuzhiyun 	tcpack_sup_module_t *tcpack_sup_mod;
740*4882a593Smuzhiyun 	tcpack_info_t *tcpack_info_tbl;
741*4882a593Smuzhiyun 	int i;
742*4882a593Smuzhiyun 	bool ret = FALSE;
743*4882a593Smuzhiyun 	bool set_dotxinrx = TRUE;
744*4882a593Smuzhiyun 	unsigned long flags;
745*4882a593Smuzhiyun 
746*4882a593Smuzhiyun 	if (dhdp->tcpack_sup_mode == TCPACK_SUP_OFF)
747*4882a593Smuzhiyun 		goto exit;
748*4882a593Smuzhiyun 
749*4882a593Smuzhiyun 	new_ether_hdr = PKTDATA(dhdp->osh, pkt);
750*4882a593Smuzhiyun 	cur_framelen = PKTLEN(dhdp->osh, pkt);
751*4882a593Smuzhiyun 
752*4882a593Smuzhiyun 	if (cur_framelen < TCPACKSZMIN || cur_framelen > TCPACKSZMAX) {
753*4882a593Smuzhiyun 		DHD_TRACE(("%s %d: Too short or long length %d to be TCP ACK\n",
754*4882a593Smuzhiyun 			__FUNCTION__, __LINE__, cur_framelen));
755*4882a593Smuzhiyun 		goto exit;
756*4882a593Smuzhiyun 	}
757*4882a593Smuzhiyun 
758*4882a593Smuzhiyun 	new_ether_type = new_ether_hdr[12] << 8 | new_ether_hdr[13];
759*4882a593Smuzhiyun 
760*4882a593Smuzhiyun 	if (new_ether_type != ETHER_TYPE_IP) {
761*4882a593Smuzhiyun 		DHD_TRACE(("%s %d: Not a IP packet 0x%x\n",
762*4882a593Smuzhiyun 			__FUNCTION__, __LINE__, new_ether_type));
763*4882a593Smuzhiyun 		goto exit;
764*4882a593Smuzhiyun 	}
765*4882a593Smuzhiyun 
766*4882a593Smuzhiyun 	DHD_TRACE(("%s %d: IP pkt! 0x%x\n", __FUNCTION__, __LINE__, new_ether_type));
767*4882a593Smuzhiyun 
768*4882a593Smuzhiyun 	new_ip_hdr = new_ether_hdr + ETHER_HDR_LEN;
769*4882a593Smuzhiyun 	cur_framelen -= ETHER_HDR_LEN;
770*4882a593Smuzhiyun 
771*4882a593Smuzhiyun 	ASSERT(cur_framelen >= IPV4_MIN_HEADER_LEN);
772*4882a593Smuzhiyun 
773*4882a593Smuzhiyun 	new_ip_hdr_len = IPV4_HLEN(new_ip_hdr);
774*4882a593Smuzhiyun 	if (IP_VER(new_ip_hdr) != IP_VER_4 || IPV4_PROT(new_ip_hdr) != IP_PROT_TCP) {
775*4882a593Smuzhiyun 		DHD_TRACE(("%s %d: Not IPv4 nor TCP! ip ver %d, prot %d\n",
776*4882a593Smuzhiyun 			__FUNCTION__, __LINE__, IP_VER(new_ip_hdr), IPV4_PROT(new_ip_hdr)));
777*4882a593Smuzhiyun 		goto exit;
778*4882a593Smuzhiyun 	}
779*4882a593Smuzhiyun 
780*4882a593Smuzhiyun 	new_tcp_hdr = new_ip_hdr + new_ip_hdr_len;
781*4882a593Smuzhiyun 	cur_framelen -= new_ip_hdr_len;
782*4882a593Smuzhiyun 
783*4882a593Smuzhiyun 	ASSERT(cur_framelen >= TCP_MIN_HEADER_LEN);
784*4882a593Smuzhiyun 
785*4882a593Smuzhiyun 	DHD_TRACE(("%s %d: TCP pkt!\n", __FUNCTION__, __LINE__));
786*4882a593Smuzhiyun 
787*4882a593Smuzhiyun 	/* is it an ack ? Allow only ACK flag, not to suppress others. */
788*4882a593Smuzhiyun 	if (new_tcp_hdr[TCP_FLAGS_OFFSET] != TCP_FLAG_ACK) {
789*4882a593Smuzhiyun 		DHD_TRACE(("%s %d: Do not touch TCP flag 0x%x\n",
790*4882a593Smuzhiyun 			__FUNCTION__, __LINE__, new_tcp_hdr[TCP_FLAGS_OFFSET]));
791*4882a593Smuzhiyun 		goto exit;
792*4882a593Smuzhiyun 	}
793*4882a593Smuzhiyun 
794*4882a593Smuzhiyun 	new_ip_total_len = ntoh16_ua(&new_ip_hdr[IPV4_PKTLEN_OFFSET]);
795*4882a593Smuzhiyun 	new_tcp_hdr_len = 4 * TCP_HDRLEN(new_tcp_hdr[TCP_HLEN_OFFSET]);
796*4882a593Smuzhiyun 
797*4882a593Smuzhiyun 	/* This packet has TCP data, so just send */
798*4882a593Smuzhiyun 	if (new_ip_total_len > new_ip_hdr_len + new_tcp_hdr_len) {
799*4882a593Smuzhiyun 		DHD_TRACE(("%s %d: Do nothing for TCP DATA\n", __FUNCTION__, __LINE__));
800*4882a593Smuzhiyun 		goto exit;
801*4882a593Smuzhiyun 	}
802*4882a593Smuzhiyun 
803*4882a593Smuzhiyun 	ASSERT(new_ip_total_len == new_ip_hdr_len + new_tcp_hdr_len);
804*4882a593Smuzhiyun 
805*4882a593Smuzhiyun 	new_tcp_ack_num = ntoh32_ua(&new_tcp_hdr[TCP_ACK_NUM_OFFSET]);
806*4882a593Smuzhiyun 
807*4882a593Smuzhiyun 	DHD_TRACE(("%s %d: TCP ACK with zero DATA length"
808*4882a593Smuzhiyun 		" IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR" TCP port %d %d\n",
809*4882a593Smuzhiyun 		__FUNCTION__, __LINE__,
810*4882a593Smuzhiyun 		IPV4_ADDR_TO_STR(ntoh32_ua(&new_ip_hdr[IPV4_SRC_IP_OFFSET])),
811*4882a593Smuzhiyun 		IPV4_ADDR_TO_STR(ntoh32_ua(&new_ip_hdr[IPV4_DEST_IP_OFFSET])),
812*4882a593Smuzhiyun 		ntoh16_ua(&new_tcp_hdr[TCP_SRC_PORT_OFFSET]),
813*4882a593Smuzhiyun 		ntoh16_ua(&new_tcp_hdr[TCP_DEST_PORT_OFFSET])));
814*4882a593Smuzhiyun 
815*4882a593Smuzhiyun 	/* Look for tcp_ack_info that has the same ip src/dst addrs and tcp src/dst ports */
816*4882a593Smuzhiyun 	flags = dhd_os_tcpacklock(dhdp);
817*4882a593Smuzhiyun #if defined(DEBUG_COUNTER) && defined(DHDTCPACK_SUP_DBG)
818*4882a593Smuzhiyun 	counter_printlog(&tack_tbl);
819*4882a593Smuzhiyun 	tack_tbl.cnt[0]++;
820*4882a593Smuzhiyun #endif /* DEBUG_COUNTER && DHDTCPACK_SUP_DBG */
821*4882a593Smuzhiyun 
822*4882a593Smuzhiyun 	tcpack_sup_mod = dhdp->tcpack_sup_module;
823*4882a593Smuzhiyun 	tcpack_info_tbl = tcpack_sup_mod->tcpack_info_tbl;
824*4882a593Smuzhiyun 
825*4882a593Smuzhiyun 	if (!tcpack_sup_mod) {
826*4882a593Smuzhiyun 		DHD_ERROR(("%s %d: tcpack suppress module NULL!!\n", __FUNCTION__, __LINE__));
827*4882a593Smuzhiyun 		ret = BCME_ERROR;
828*4882a593Smuzhiyun 		dhd_os_tcpackunlock(dhdp, flags);
829*4882a593Smuzhiyun 		goto exit;
830*4882a593Smuzhiyun 	}
831*4882a593Smuzhiyun 
832*4882a593Smuzhiyun 	if (dhd_tcpdata_psh_acked(dhdp, new_ip_hdr, new_tcp_hdr, new_tcp_ack_num)) {
833*4882a593Smuzhiyun 		/* This TCPACK is ACK to TCPDATA PSH pkt, so keep set_dotxinrx TRUE */
834*4882a593Smuzhiyun #if defined(DEBUG_COUNTER) && defined(DHDTCPACK_SUP_DBG)
835*4882a593Smuzhiyun 		tack_tbl.cnt[5]++;
836*4882a593Smuzhiyun #endif /* DEBUG_COUNTER && DHDTCPACK_SUP_DBG */
837*4882a593Smuzhiyun 	} else
838*4882a593Smuzhiyun 		set_dotxinrx = FALSE;
839*4882a593Smuzhiyun 
840*4882a593Smuzhiyun 	for (i = 0; i < tcpack_sup_mod->tcpack_info_cnt; i++) {
841*4882a593Smuzhiyun 		void *oldpkt;	/* TCPACK packet that is already in txq or DelayQ */
842*4882a593Smuzhiyun 		uint8 *old_ether_hdr, *old_ip_hdr, *old_tcp_hdr;
843*4882a593Smuzhiyun 		uint32 old_ip_hdr_len, old_tcp_hdr_len;
844*4882a593Smuzhiyun 		uint32 old_tcpack_num;	/* TCP ACK number of old TCPACK packet in Q */
845*4882a593Smuzhiyun 
846*4882a593Smuzhiyun 		if ((oldpkt = tcpack_info_tbl[i].pkt_in_q) == NULL) {
847*4882a593Smuzhiyun 			DHD_ERROR(("%s %d: Unexpected error!! cur idx %d, ttl cnt %d\n",
848*4882a593Smuzhiyun 				__FUNCTION__, __LINE__, i, tcpack_sup_mod->tcpack_info_cnt));
849*4882a593Smuzhiyun 			break;
850*4882a593Smuzhiyun 		}
851*4882a593Smuzhiyun 
852*4882a593Smuzhiyun 		if (PKTDATA(dhdp->osh, oldpkt) == NULL) {
853*4882a593Smuzhiyun 			DHD_ERROR(("%s %d: oldpkt data NULL!! cur idx %d, ttl cnt %d\n",
854*4882a593Smuzhiyun 				__FUNCTION__, __LINE__, i, tcpack_sup_mod->tcpack_info_cnt));
855*4882a593Smuzhiyun 			break;
856*4882a593Smuzhiyun 		}
857*4882a593Smuzhiyun 
858*4882a593Smuzhiyun 		old_ether_hdr = tcpack_info_tbl[i].pkt_ether_hdr;
859*4882a593Smuzhiyun 		old_ip_hdr = old_ether_hdr + ETHER_HDR_LEN;
860*4882a593Smuzhiyun 		old_ip_hdr_len = IPV4_HLEN(old_ip_hdr);
861*4882a593Smuzhiyun 		old_tcp_hdr = old_ip_hdr + old_ip_hdr_len;
862*4882a593Smuzhiyun 		old_tcp_hdr_len = 4 * TCP_HDRLEN(old_tcp_hdr[TCP_HLEN_OFFSET]);
863*4882a593Smuzhiyun 
864*4882a593Smuzhiyun 		DHD_TRACE(("%s %d: oldpkt %p[%d], IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR
865*4882a593Smuzhiyun 			" TCP port %d %d\n", __FUNCTION__, __LINE__, oldpkt, i,
866*4882a593Smuzhiyun 			IPV4_ADDR_TO_STR(ntoh32_ua(&old_ip_hdr[IPV4_SRC_IP_OFFSET])),
867*4882a593Smuzhiyun 			IPV4_ADDR_TO_STR(ntoh32_ua(&old_ip_hdr[IPV4_DEST_IP_OFFSET])),
868*4882a593Smuzhiyun 			ntoh16_ua(&old_tcp_hdr[TCP_SRC_PORT_OFFSET]),
869*4882a593Smuzhiyun 			ntoh16_ua(&old_tcp_hdr[TCP_DEST_PORT_OFFSET])));
870*4882a593Smuzhiyun 
871*4882a593Smuzhiyun 		/* If either of IP address or TCP port number does not match, skip.
872*4882a593Smuzhiyun 		 * Note that src/dst addr fields in ip header are contiguous being 8 bytes in total.
873*4882a593Smuzhiyun 		 * Also, src/dst port fields in TCP header are contiguous being 4 bytes in total.
874*4882a593Smuzhiyun 		 */
875*4882a593Smuzhiyun 		if (memcmp(&new_ip_hdr[IPV4_SRC_IP_OFFSET],
876*4882a593Smuzhiyun 			&old_ip_hdr[IPV4_SRC_IP_OFFSET], IPV4_ADDR_LEN * 2) ||
877*4882a593Smuzhiyun 			memcmp(&new_tcp_hdr[TCP_SRC_PORT_OFFSET],
878*4882a593Smuzhiyun 			&old_tcp_hdr[TCP_SRC_PORT_OFFSET], TCP_PORT_LEN * 2))
879*4882a593Smuzhiyun 			continue;
880*4882a593Smuzhiyun 
881*4882a593Smuzhiyun 		old_tcpack_num = ntoh32_ua(&old_tcp_hdr[TCP_ACK_NUM_OFFSET]);
882*4882a593Smuzhiyun 
883*4882a593Smuzhiyun 		if (IS_TCPSEQ_GT(new_tcp_ack_num, old_tcpack_num)) {
884*4882a593Smuzhiyun 			/* New packet has higher TCP ACK number, so it replaces the old packet */
885*4882a593Smuzhiyun 			if (new_ip_hdr_len == old_ip_hdr_len &&
886*4882a593Smuzhiyun 				new_tcp_hdr_len == old_tcp_hdr_len) {
887*4882a593Smuzhiyun 				ASSERT(memcmp(new_ether_hdr, old_ether_hdr, ETHER_HDR_LEN) == 0);
888*4882a593Smuzhiyun 				bcopy(new_ip_hdr, old_ip_hdr, new_ip_total_len);
889*4882a593Smuzhiyun 				PKTFREE(dhdp->osh, pkt, FALSE);
890*4882a593Smuzhiyun 				DHD_TRACE(("%s %d: TCP ACK replace %u -> %u\n",
891*4882a593Smuzhiyun 					__FUNCTION__, __LINE__, old_tcpack_num, new_tcp_ack_num));
892*4882a593Smuzhiyun #if defined(DEBUG_COUNTER) && defined(DHDTCPACK_SUP_DBG)
893*4882a593Smuzhiyun 				tack_tbl.cnt[2]++;
894*4882a593Smuzhiyun #endif /* DEBUG_COUNTER && DHDTCPACK_SUP_DBG */
895*4882a593Smuzhiyun 				ret = TRUE;
896*4882a593Smuzhiyun 			} else {
897*4882a593Smuzhiyun #if defined(DEBUG_COUNTER) && defined(DHDTCPACK_SUP_DBG)
898*4882a593Smuzhiyun 				tack_tbl.cnt[6]++;
899*4882a593Smuzhiyun #endif /* DEBUG_COUNTER && DHDTCPACK_SUP_DBG */
900*4882a593Smuzhiyun 				DHD_TRACE(("%s %d: lenth mismatch %d != %d || %d != %d"
901*4882a593Smuzhiyun 					" ACK %u -> %u\n", __FUNCTION__, __LINE__,
902*4882a593Smuzhiyun 					new_ip_hdr_len, old_ip_hdr_len,
903*4882a593Smuzhiyun 					new_tcp_hdr_len, old_tcp_hdr_len,
904*4882a593Smuzhiyun 					old_tcpack_num, new_tcp_ack_num));
905*4882a593Smuzhiyun 			}
906*4882a593Smuzhiyun 		} else if (new_tcp_ack_num == old_tcpack_num) {
907*4882a593Smuzhiyun 			set_dotxinrx = TRUE;
908*4882a593Smuzhiyun 			/* TCPACK retransmission */
909*4882a593Smuzhiyun #if defined(DEBUG_COUNTER) && defined(DHDTCPACK_SUP_DBG)
910*4882a593Smuzhiyun 			tack_tbl.cnt[3]++;
911*4882a593Smuzhiyun #endif /* DEBUG_COUNTER && DHDTCPACK_SUP_DBG */
912*4882a593Smuzhiyun 		} else {
913*4882a593Smuzhiyun 			DHD_TRACE(("%s %d: ACK number reverse old %u(0x%p) new %u(0x%p)\n",
914*4882a593Smuzhiyun 				__FUNCTION__, __LINE__, old_tcpack_num, oldpkt,
915*4882a593Smuzhiyun 				new_tcp_ack_num, pkt));
916*4882a593Smuzhiyun 		}
917*4882a593Smuzhiyun 		dhd_os_tcpackunlock(dhdp, flags);
918*4882a593Smuzhiyun 		goto exit;
919*4882a593Smuzhiyun 	}
920*4882a593Smuzhiyun 
921*4882a593Smuzhiyun 	if (i == tcpack_sup_mod->tcpack_info_cnt && i < TCPACK_INFO_MAXNUM) {
922*4882a593Smuzhiyun 		/* No TCPACK packet with the same IP addr and TCP port is found
923*4882a593Smuzhiyun 		 * in tcp_ack_info_tbl. So add this packet to the table.
924*4882a593Smuzhiyun 		 */
925*4882a593Smuzhiyun 		DHD_TRACE(("%s %d: Add pkt 0x%p(ether_hdr 0x%p) to tbl[%d]\n",
926*4882a593Smuzhiyun 			__FUNCTION__, __LINE__, pkt, new_ether_hdr,
927*4882a593Smuzhiyun 			tcpack_sup_mod->tcpack_info_cnt));
928*4882a593Smuzhiyun 
929*4882a593Smuzhiyun 		tcpack_info_tbl[tcpack_sup_mod->tcpack_info_cnt].pkt_in_q = pkt;
930*4882a593Smuzhiyun 		tcpack_info_tbl[tcpack_sup_mod->tcpack_info_cnt].pkt_ether_hdr = new_ether_hdr;
931*4882a593Smuzhiyun 		tcpack_sup_mod->tcpack_info_cnt++;
932*4882a593Smuzhiyun #if defined(DEBUG_COUNTER) && defined(DHDTCPACK_SUP_DBG)
933*4882a593Smuzhiyun 		tack_tbl.cnt[1]++;
934*4882a593Smuzhiyun #endif /* DEBUG_COUNTER && DHDTCPACK_SUP_DBG */
935*4882a593Smuzhiyun 	} else {
936*4882a593Smuzhiyun 		ASSERT(i == tcpack_sup_mod->tcpack_info_cnt);
937*4882a593Smuzhiyun 		DHD_TRACE(("%s %d: No empty tcp ack info tbl\n",
938*4882a593Smuzhiyun 			__FUNCTION__, __LINE__));
939*4882a593Smuzhiyun 	}
940*4882a593Smuzhiyun 	dhd_os_tcpackunlock(dhdp, flags);
941*4882a593Smuzhiyun 
942*4882a593Smuzhiyun exit:
943*4882a593Smuzhiyun 	/* Unless TCPACK_SUP_DELAYTX, dotxinrx is alwasy TRUE, so no need to set here */
944*4882a593Smuzhiyun 	if (dhdp->tcpack_sup_mode == TCPACK_SUP_DELAYTX && set_dotxinrx)
945*4882a593Smuzhiyun 		dhd_bus_set_dotxinrx(dhdp->bus, TRUE);
946*4882a593Smuzhiyun 
947*4882a593Smuzhiyun 	return ret;
948*4882a593Smuzhiyun }
949*4882a593Smuzhiyun 
950*4882a593Smuzhiyun bool
dhd_tcpdata_info_get(dhd_pub_t * dhdp,void * pkt)951*4882a593Smuzhiyun dhd_tcpdata_info_get(dhd_pub_t *dhdp, void *pkt)
952*4882a593Smuzhiyun {
953*4882a593Smuzhiyun 	uint8 *ether_hdr;	/* Ethernet header of the new packet */
954*4882a593Smuzhiyun 	uint16 ether_type;	/* Ethernet type of the new packet */
955*4882a593Smuzhiyun 	uint8 *ip_hdr;		/* IP header of the new packet */
956*4882a593Smuzhiyun 	uint8 *tcp_hdr;		/* TCP header of the new packet */
957*4882a593Smuzhiyun 	uint32 ip_hdr_len;	/* IP header length of the new packet */
958*4882a593Smuzhiyun 	uint32 cur_framelen;
959*4882a593Smuzhiyun 	uint16 ip_total_len;	/* Total length of IP packet for the new packet */
960*4882a593Smuzhiyun 	uint32 tcp_hdr_len;		/* TCP header length of the new packet */
961*4882a593Smuzhiyun 	uint32 tcp_seq_num;		/* TCP sequence number of the new packet */
962*4882a593Smuzhiyun 	uint16 tcp_data_len;	/* TCP DATA length that excludes IP and TCP headers */
963*4882a593Smuzhiyun 	uint32 end_tcp_seq_num;	/* TCP seq number of the last byte in the new packet */
964*4882a593Smuzhiyun 	tcpack_sup_module_t *tcpack_sup_mod;
965*4882a593Smuzhiyun 	tcpdata_info_t *tcpdata_info = NULL;
966*4882a593Smuzhiyun 	tdata_psh_info_t *tdata_psh_info;
967*4882a593Smuzhiyun 
968*4882a593Smuzhiyun 	int i;
969*4882a593Smuzhiyun 	bool ret = FALSE;
970*4882a593Smuzhiyun 	unsigned long flags;
971*4882a593Smuzhiyun 
972*4882a593Smuzhiyun 	if (dhdp->tcpack_sup_mode != TCPACK_SUP_DELAYTX)
973*4882a593Smuzhiyun 		goto exit;
974*4882a593Smuzhiyun 
975*4882a593Smuzhiyun 	ether_hdr = PKTDATA(dhdp->osh, pkt);
976*4882a593Smuzhiyun 	cur_framelen = PKTLEN(dhdp->osh, pkt);
977*4882a593Smuzhiyun 
978*4882a593Smuzhiyun 	ether_type = ether_hdr[12] << 8 | ether_hdr[13];
979*4882a593Smuzhiyun 
980*4882a593Smuzhiyun 	if (ether_type != ETHER_TYPE_IP) {
981*4882a593Smuzhiyun 		DHD_TRACE(("%s %d: Not a IP packet 0x%x\n",
982*4882a593Smuzhiyun 			__FUNCTION__, __LINE__, ether_type));
983*4882a593Smuzhiyun 		goto exit;
984*4882a593Smuzhiyun 	}
985*4882a593Smuzhiyun 
986*4882a593Smuzhiyun 	DHD_TRACE(("%s %d: IP pkt! 0x%x\n", __FUNCTION__, __LINE__, ether_type));
987*4882a593Smuzhiyun 
988*4882a593Smuzhiyun 	ip_hdr = ether_hdr + ETHER_HDR_LEN;
989*4882a593Smuzhiyun 	cur_framelen -= ETHER_HDR_LEN;
990*4882a593Smuzhiyun 
991*4882a593Smuzhiyun 	ASSERT(cur_framelen >= IPV4_MIN_HEADER_LEN);
992*4882a593Smuzhiyun 
993*4882a593Smuzhiyun 	ip_hdr_len = IPV4_HLEN(ip_hdr);
994*4882a593Smuzhiyun 	if (IP_VER(ip_hdr) != IP_VER_4 || IPV4_PROT(ip_hdr) != IP_PROT_TCP) {
995*4882a593Smuzhiyun 		DHD_TRACE(("%s %d: Not IPv4 nor TCP! ip ver %d, prot %d\n",
996*4882a593Smuzhiyun 			__FUNCTION__, __LINE__, IP_VER(ip_hdr), IPV4_PROT(ip_hdr)));
997*4882a593Smuzhiyun 		goto exit;
998*4882a593Smuzhiyun 	}
999*4882a593Smuzhiyun 
1000*4882a593Smuzhiyun 	tcp_hdr = ip_hdr + ip_hdr_len;
1001*4882a593Smuzhiyun 	cur_framelen -= ip_hdr_len;
1002*4882a593Smuzhiyun 
1003*4882a593Smuzhiyun 	ASSERT(cur_framelen >= TCP_MIN_HEADER_LEN);
1004*4882a593Smuzhiyun 
1005*4882a593Smuzhiyun 	DHD_TRACE(("%s %d: TCP pkt!\n", __FUNCTION__, __LINE__));
1006*4882a593Smuzhiyun 
1007*4882a593Smuzhiyun 	ip_total_len = ntoh16_ua(&ip_hdr[IPV4_PKTLEN_OFFSET]);
1008*4882a593Smuzhiyun 	tcp_hdr_len = 4 * TCP_HDRLEN(tcp_hdr[TCP_HLEN_OFFSET]);
1009*4882a593Smuzhiyun 
1010*4882a593Smuzhiyun 	/* This packet is mere TCP ACK, so do nothing */
1011*4882a593Smuzhiyun 	if (ip_total_len == ip_hdr_len + tcp_hdr_len) {
1012*4882a593Smuzhiyun 		DHD_TRACE(("%s %d: Do nothing for no data TCP ACK\n", __FUNCTION__, __LINE__));
1013*4882a593Smuzhiyun 		goto exit;
1014*4882a593Smuzhiyun 	}
1015*4882a593Smuzhiyun 
1016*4882a593Smuzhiyun 	ASSERT(ip_total_len > ip_hdr_len + tcp_hdr_len);
1017*4882a593Smuzhiyun 
1018*4882a593Smuzhiyun 	if ((tcp_hdr[TCP_FLAGS_OFFSET] & TCP_FLAG_PSH) == 0) {
1019*4882a593Smuzhiyun 		DHD_TRACE(("%s %d: Not interested TCP DATA packet\n", __FUNCTION__, __LINE__));
1020*4882a593Smuzhiyun 		goto exit;
1021*4882a593Smuzhiyun 	}
1022*4882a593Smuzhiyun 
1023*4882a593Smuzhiyun 	DHD_TRACE(("%s %d: TCP DATA with nonzero DATA length"
1024*4882a593Smuzhiyun 		" IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR" TCP port %d %d, flag 0x%x\n",
1025*4882a593Smuzhiyun 		__FUNCTION__, __LINE__,
1026*4882a593Smuzhiyun 		IPV4_ADDR_TO_STR(ntoh32_ua(&ip_hdr[IPV4_SRC_IP_OFFSET])),
1027*4882a593Smuzhiyun 		IPV4_ADDR_TO_STR(ntoh32_ua(&ip_hdr[IPV4_DEST_IP_OFFSET])),
1028*4882a593Smuzhiyun 		ntoh16_ua(&tcp_hdr[TCP_SRC_PORT_OFFSET]),
1029*4882a593Smuzhiyun 		ntoh16_ua(&tcp_hdr[TCP_DEST_PORT_OFFSET]),
1030*4882a593Smuzhiyun 		tcp_hdr[TCP_FLAGS_OFFSET]));
1031*4882a593Smuzhiyun 
1032*4882a593Smuzhiyun 	flags = dhd_os_tcpacklock(dhdp);
1033*4882a593Smuzhiyun 	tcpack_sup_mod = dhdp->tcpack_sup_module;
1034*4882a593Smuzhiyun 
1035*4882a593Smuzhiyun 	if (!tcpack_sup_mod) {
1036*4882a593Smuzhiyun 		DHD_ERROR(("%s %d: tcpack suppress module NULL!!\n", __FUNCTION__, __LINE__));
1037*4882a593Smuzhiyun 		ret = BCME_ERROR;
1038*4882a593Smuzhiyun 		dhd_os_tcpackunlock(dhdp, flags);
1039*4882a593Smuzhiyun 		goto exit;
1040*4882a593Smuzhiyun 	}
1041*4882a593Smuzhiyun 
1042*4882a593Smuzhiyun 	/* Look for tcpdata_info that has the same ip src/dst addrs and tcp src/dst ports */
1043*4882a593Smuzhiyun 	i = 0;
1044*4882a593Smuzhiyun 	while (i < tcpack_sup_mod->tcpdata_info_cnt) {
1045*4882a593Smuzhiyun 		tcpdata_info_t *tdata_info_tmp = &tcpack_sup_mod->tcpdata_info_tbl[i];
1046*4882a593Smuzhiyun 		uint32 now_in_ms = OSL_SYSUPTIME();
1047*4882a593Smuzhiyun 		DHD_TRACE(("%s %d: data info[%d], IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR
1048*4882a593Smuzhiyun 			" TCP port %d %d\n", __FUNCTION__, __LINE__, i,
1049*4882a593Smuzhiyun 			IPV4_ADDR_TO_STR(ntoh32_ua(tdata_info_tmp->ip_addr.src)),
1050*4882a593Smuzhiyun 			IPV4_ADDR_TO_STR(ntoh32_ua(tdata_info_tmp->ip_addr.dst)),
1051*4882a593Smuzhiyun 			ntoh16_ua(tdata_info_tmp->tcp_port.src),
1052*4882a593Smuzhiyun 			ntoh16_ua(tdata_info_tmp->tcp_port.dst)));
1053*4882a593Smuzhiyun 
1054*4882a593Smuzhiyun 		/* If both IP address and TCP port number match, we found it so break.
1055*4882a593Smuzhiyun 		 * Note that src/dst addr fields in ip header are contiguous being 8 bytes in total.
1056*4882a593Smuzhiyun 		 * Also, src/dst port fields in TCP header are contiguous being 4 bytes in total.
1057*4882a593Smuzhiyun 		 */
1058*4882a593Smuzhiyun 		if (memcmp(&ip_hdr[IPV4_SRC_IP_OFFSET],
1059*4882a593Smuzhiyun 			(void *)&tdata_info_tmp->ip_addr, IPV4_ADDR_LEN * 2) == 0 &&
1060*4882a593Smuzhiyun 			memcmp(&tcp_hdr[TCP_SRC_PORT_OFFSET],
1061*4882a593Smuzhiyun 			(void *)&tdata_info_tmp->tcp_port, TCP_PORT_LEN * 2) == 0) {
1062*4882a593Smuzhiyun 			tcpdata_info = tdata_info_tmp;
1063*4882a593Smuzhiyun 			tcpdata_info->last_used_time = now_in_ms;
1064*4882a593Smuzhiyun 			break;
1065*4882a593Smuzhiyun 		}
1066*4882a593Smuzhiyun 
1067*4882a593Smuzhiyun 		if (now_in_ms - tdata_info_tmp->last_used_time > TCPDATA_INFO_TIMEOUT) {
1068*4882a593Smuzhiyun 			tdata_psh_info_t *tdata_psh_info_tmp;
1069*4882a593Smuzhiyun 			tcpdata_info_t *last_tdata_info;
1070*4882a593Smuzhiyun 
1071*4882a593Smuzhiyun 			while ((tdata_psh_info_tmp = tdata_info_tmp->tdata_psh_info_head)) {
1072*4882a593Smuzhiyun 				tdata_info_tmp->tdata_psh_info_head = tdata_psh_info_tmp->next;
1073*4882a593Smuzhiyun 				tdata_psh_info_tmp->next = NULL;
1074*4882a593Smuzhiyun 				DHD_TRACE(("%s %d: Clean tdata_psh_info(end_seq %u)!\n",
1075*4882a593Smuzhiyun 					__FUNCTION__, __LINE__, tdata_psh_info_tmp->end_seq));
1076*4882a593Smuzhiyun 				_tdata_psh_info_pool_enq(tcpack_sup_mod, tdata_psh_info_tmp);
1077*4882a593Smuzhiyun 			}
1078*4882a593Smuzhiyun #ifdef DHDTCPACK_SUP_DBG
1079*4882a593Smuzhiyun 			DHD_ERROR(("%s %d: PSH INFO ENQ %d\n",
1080*4882a593Smuzhiyun 				__FUNCTION__, __LINE__, tcpack_sup_mod->psh_info_enq_num));
1081*4882a593Smuzhiyun #endif /* DHDTCPACK_SUP_DBG */
1082*4882a593Smuzhiyun 			tcpack_sup_mod->tcpdata_info_cnt--;
1083*4882a593Smuzhiyun 			ASSERT(tcpack_sup_mod->tcpdata_info_cnt >= 0);
1084*4882a593Smuzhiyun 
1085*4882a593Smuzhiyun 			last_tdata_info =
1086*4882a593Smuzhiyun 				&tcpack_sup_mod->tcpdata_info_tbl[tcpack_sup_mod->tcpdata_info_cnt];
1087*4882a593Smuzhiyun 			if (i < tcpack_sup_mod->tcpdata_info_cnt) {
1088*4882a593Smuzhiyun 				ASSERT(last_tdata_info != tdata_info_tmp);
1089*4882a593Smuzhiyun 				bcopy(last_tdata_info, tdata_info_tmp, sizeof(tcpdata_info_t));
1090*4882a593Smuzhiyun 			}
1091*4882a593Smuzhiyun 			bzero(last_tdata_info, sizeof(tcpdata_info_t));
1092*4882a593Smuzhiyun 			DHD_INFO(("%s %d: tcpdata_info(idx %d) is aged out. ttl cnt is now %d\n",
1093*4882a593Smuzhiyun 				__FUNCTION__, __LINE__, i, tcpack_sup_mod->tcpdata_info_cnt));
1094*4882a593Smuzhiyun 			/* Don't increase "i" here, so that the prev last tcpdata_info is checked */
1095*4882a593Smuzhiyun 		} else
1096*4882a593Smuzhiyun 			 i++;
1097*4882a593Smuzhiyun 	}
1098*4882a593Smuzhiyun 
1099*4882a593Smuzhiyun 	tcp_seq_num = ntoh32_ua(&tcp_hdr[TCP_SEQ_NUM_OFFSET]);
1100*4882a593Smuzhiyun 	tcp_data_len = ip_total_len - ip_hdr_len - tcp_hdr_len;
1101*4882a593Smuzhiyun 	end_tcp_seq_num = tcp_seq_num + tcp_data_len;
1102*4882a593Smuzhiyun 
1103*4882a593Smuzhiyun 	if (tcpdata_info == NULL) {
1104*4882a593Smuzhiyun 		ASSERT(i == tcpack_sup_mod->tcpdata_info_cnt);
1105*4882a593Smuzhiyun 		if (i >= TCPDATA_INFO_MAXNUM) {
1106*4882a593Smuzhiyun 			DHD_TRACE(("%s %d: tcp_data_info_tbl FULL! %d %d"
1107*4882a593Smuzhiyun 				" IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR" TCP port %d %d\n",
1108*4882a593Smuzhiyun 				__FUNCTION__, __LINE__, i, tcpack_sup_mod->tcpdata_info_cnt,
1109*4882a593Smuzhiyun 				IPV4_ADDR_TO_STR(ntoh32_ua(&ip_hdr[IPV4_SRC_IP_OFFSET])),
1110*4882a593Smuzhiyun 				IPV4_ADDR_TO_STR(ntoh32_ua(&ip_hdr[IPV4_DEST_IP_OFFSET])),
1111*4882a593Smuzhiyun 				ntoh16_ua(&tcp_hdr[TCP_SRC_PORT_OFFSET]),
1112*4882a593Smuzhiyun 				ntoh16_ua(&tcp_hdr[TCP_DEST_PORT_OFFSET])));
1113*4882a593Smuzhiyun 			dhd_os_tcpackunlock(dhdp, flags);
1114*4882a593Smuzhiyun 			goto exit;
1115*4882a593Smuzhiyun 		}
1116*4882a593Smuzhiyun 		tcpdata_info = &tcpack_sup_mod->tcpdata_info_tbl[i];
1117*4882a593Smuzhiyun 
1118*4882a593Smuzhiyun 		/* No TCP flow with the same IP addr and TCP port is found
1119*4882a593Smuzhiyun 		 * in tcp_data_info_tbl. So add this flow to the table.
1120*4882a593Smuzhiyun 		 */
1121*4882a593Smuzhiyun 		DHD_INFO(("%s %d: Add data info to tbl[%d]: IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR
1122*4882a593Smuzhiyun 			" TCP port %d %d\n",
1123*4882a593Smuzhiyun 			__FUNCTION__, __LINE__, tcpack_sup_mod->tcpdata_info_cnt,
1124*4882a593Smuzhiyun 			IPV4_ADDR_TO_STR(ntoh32_ua(&ip_hdr[IPV4_SRC_IP_OFFSET])),
1125*4882a593Smuzhiyun 			IPV4_ADDR_TO_STR(ntoh32_ua(&ip_hdr[IPV4_DEST_IP_OFFSET])),
1126*4882a593Smuzhiyun 			ntoh16_ua(&tcp_hdr[TCP_SRC_PORT_OFFSET]),
1127*4882a593Smuzhiyun 			ntoh16_ua(&tcp_hdr[TCP_DEST_PORT_OFFSET])));
1128*4882a593Smuzhiyun 		/* Note that src/dst addr fields in ip header are contiguous being 8 bytes in total.
1129*4882a593Smuzhiyun 		 * Also, src/dst port fields in TCP header are contiguous being 4 bytes in total.
1130*4882a593Smuzhiyun 		 */
1131*4882a593Smuzhiyun 		bcopy(&ip_hdr[IPV4_SRC_IP_OFFSET], (void *)&tcpdata_info->ip_addr,
1132*4882a593Smuzhiyun 			IPV4_ADDR_LEN * 2);
1133*4882a593Smuzhiyun 		bcopy(&tcp_hdr[TCP_SRC_PORT_OFFSET], (void *)&tcpdata_info->tcp_port,
1134*4882a593Smuzhiyun 			TCP_PORT_LEN * 2);
1135*4882a593Smuzhiyun 
1136*4882a593Smuzhiyun 		tcpdata_info->last_used_time = OSL_SYSUPTIME();
1137*4882a593Smuzhiyun 		tcpack_sup_mod->tcpdata_info_cnt++;
1138*4882a593Smuzhiyun 	}
1139*4882a593Smuzhiyun 
1140*4882a593Smuzhiyun 	ASSERT(tcpdata_info != NULL);
1141*4882a593Smuzhiyun 
1142*4882a593Smuzhiyun 	tdata_psh_info = _tdata_psh_info_pool_deq(tcpack_sup_mod);
1143*4882a593Smuzhiyun #ifdef DHDTCPACK_SUP_DBG
1144*4882a593Smuzhiyun 	DHD_TRACE(("%s %d: PSH INFO ENQ %d\n",
1145*4882a593Smuzhiyun 		__FUNCTION__, __LINE__, tcpack_sup_mod->psh_info_enq_num));
1146*4882a593Smuzhiyun #endif /* DHDTCPACK_SUP_DBG */
1147*4882a593Smuzhiyun 
1148*4882a593Smuzhiyun 	if (tdata_psh_info == NULL) {
1149*4882a593Smuzhiyun 		DHD_ERROR(("%s %d: No more free tdata_psh_info!!\n", __FUNCTION__, __LINE__));
1150*4882a593Smuzhiyun 		ret = BCME_ERROR;
1151*4882a593Smuzhiyun 		dhd_os_tcpackunlock(dhdp, flags);
1152*4882a593Smuzhiyun 		goto exit;
1153*4882a593Smuzhiyun 	}
1154*4882a593Smuzhiyun 	tdata_psh_info->end_seq = end_tcp_seq_num;
1155*4882a593Smuzhiyun 
1156*4882a593Smuzhiyun #if defined(DEBUG_COUNTER) && defined(DHDTCPACK_SUP_DBG)
1157*4882a593Smuzhiyun 	tack_tbl.cnt[4]++;
1158*4882a593Smuzhiyun #endif /* DEBUG_COUNTER && DHDTCPACK_SUP_DBG */
1159*4882a593Smuzhiyun 
1160*4882a593Smuzhiyun 	DHD_TRACE(("%s %d: TCP PSH DATA recvd! end seq %u\n",
1161*4882a593Smuzhiyun 		__FUNCTION__, __LINE__, tdata_psh_info->end_seq));
1162*4882a593Smuzhiyun 
1163*4882a593Smuzhiyun 	ASSERT(tdata_psh_info->next == NULL);
1164*4882a593Smuzhiyun 
1165*4882a593Smuzhiyun 	if (tcpdata_info->tdata_psh_info_head == NULL)
1166*4882a593Smuzhiyun 		tcpdata_info->tdata_psh_info_head = tdata_psh_info;
1167*4882a593Smuzhiyun 	else {
1168*4882a593Smuzhiyun 		ASSERT(tcpdata_info->tdata_psh_info_tail);
1169*4882a593Smuzhiyun 		tcpdata_info->tdata_psh_info_tail->next = tdata_psh_info;
1170*4882a593Smuzhiyun 	}
1171*4882a593Smuzhiyun 	tcpdata_info->tdata_psh_info_tail = tdata_psh_info;
1172*4882a593Smuzhiyun 
1173*4882a593Smuzhiyun 	dhd_os_tcpackunlock(dhdp, flags);
1174*4882a593Smuzhiyun 
1175*4882a593Smuzhiyun exit:
1176*4882a593Smuzhiyun 	return ret;
1177*4882a593Smuzhiyun }
1178*4882a593Smuzhiyun 
1179*4882a593Smuzhiyun bool
dhd_tcpack_hold(dhd_pub_t * dhdp,void * pkt,int ifidx)1180*4882a593Smuzhiyun dhd_tcpack_hold(dhd_pub_t *dhdp, void *pkt, int ifidx)
1181*4882a593Smuzhiyun {
1182*4882a593Smuzhiyun 	uint8 *new_ether_hdr;	/* Ethernet header of the new packet */
1183*4882a593Smuzhiyun 	uint16 new_ether_type;	/* Ethernet type of the new packet */
1184*4882a593Smuzhiyun 	uint8 *new_ip_hdr;		/* IP header of the new packet */
1185*4882a593Smuzhiyun 	uint8 *new_tcp_hdr;		/* TCP header of the new packet */
1186*4882a593Smuzhiyun 	uint32 new_ip_hdr_len;	/* IP header length of the new packet */
1187*4882a593Smuzhiyun 	uint32 cur_framelen;
1188*4882a593Smuzhiyun 	uint32 new_tcp_ack_num;		/* TCP acknowledge number of the new packet */
1189*4882a593Smuzhiyun 	uint16 new_ip_total_len;	/* Total length of IP packet for the new packet */
1190*4882a593Smuzhiyun 	uint32 new_tcp_hdr_len;		/* TCP header length of the new packet */
1191*4882a593Smuzhiyun 	tcpack_sup_module_t *tcpack_sup_mod;
1192*4882a593Smuzhiyun 	tcpack_info_t *tcpack_info_tbl;
1193*4882a593Smuzhiyun 	int i, free_slot = TCPACK_INFO_MAXNUM;
1194*4882a593Smuzhiyun 	bool hold = FALSE;
1195*4882a593Smuzhiyun 	unsigned long flags;
1196*4882a593Smuzhiyun 
1197*4882a593Smuzhiyun 	if (dhdp->tcpack_sup_mode != TCPACK_SUP_HOLD) {
1198*4882a593Smuzhiyun 		goto exit;
1199*4882a593Smuzhiyun 	}
1200*4882a593Smuzhiyun 
1201*4882a593Smuzhiyun 	if (dhdp->tcpack_sup_ratio == 1) {
1202*4882a593Smuzhiyun 		goto exit;
1203*4882a593Smuzhiyun 	}
1204*4882a593Smuzhiyun 
1205*4882a593Smuzhiyun 	new_ether_hdr = PKTDATA(dhdp->osh, pkt);
1206*4882a593Smuzhiyun 	cur_framelen = PKTLEN(dhdp->osh, pkt);
1207*4882a593Smuzhiyun 
1208*4882a593Smuzhiyun 	if (cur_framelen < TCPACKSZMIN || cur_framelen > TCPACKSZMAX) {
1209*4882a593Smuzhiyun 		DHD_TRACE(("%s %d: Too short or long length %d to be TCP ACK\n",
1210*4882a593Smuzhiyun 			__FUNCTION__, __LINE__, cur_framelen));
1211*4882a593Smuzhiyun 		goto exit;
1212*4882a593Smuzhiyun 	}
1213*4882a593Smuzhiyun 
1214*4882a593Smuzhiyun 	new_ether_type = new_ether_hdr[12] << 8 | new_ether_hdr[13];
1215*4882a593Smuzhiyun 
1216*4882a593Smuzhiyun 	if (new_ether_type != ETHER_TYPE_IP) {
1217*4882a593Smuzhiyun 		DHD_TRACE(("%s %d: Not a IP packet 0x%x\n",
1218*4882a593Smuzhiyun 			__FUNCTION__, __LINE__, new_ether_type));
1219*4882a593Smuzhiyun 		goto exit;
1220*4882a593Smuzhiyun 	}
1221*4882a593Smuzhiyun 
1222*4882a593Smuzhiyun 	DHD_TRACE(("%s %d: IP pkt! 0x%x\n", __FUNCTION__, __LINE__, new_ether_type));
1223*4882a593Smuzhiyun 
1224*4882a593Smuzhiyun 	new_ip_hdr = new_ether_hdr + ETHER_HDR_LEN;
1225*4882a593Smuzhiyun 	cur_framelen -= ETHER_HDR_LEN;
1226*4882a593Smuzhiyun 
1227*4882a593Smuzhiyun 	ASSERT(cur_framelen >= IPV4_MIN_HEADER_LEN);
1228*4882a593Smuzhiyun 
1229*4882a593Smuzhiyun 	new_ip_hdr_len = IPV4_HLEN(new_ip_hdr);
1230*4882a593Smuzhiyun 	if (IP_VER(new_ip_hdr) != IP_VER_4 || IPV4_PROT(new_ip_hdr) != IP_PROT_TCP) {
1231*4882a593Smuzhiyun 		DHD_TRACE(("%s %d: Not IPv4 nor TCP! ip ver %d, prot %d\n",
1232*4882a593Smuzhiyun 			__FUNCTION__, __LINE__, IP_VER(new_ip_hdr), IPV4_PROT(new_ip_hdr)));
1233*4882a593Smuzhiyun 		goto exit;
1234*4882a593Smuzhiyun 	}
1235*4882a593Smuzhiyun 
1236*4882a593Smuzhiyun 	new_tcp_hdr = new_ip_hdr + new_ip_hdr_len;
1237*4882a593Smuzhiyun 	cur_framelen -= new_ip_hdr_len;
1238*4882a593Smuzhiyun 
1239*4882a593Smuzhiyun 	ASSERT(cur_framelen >= TCP_MIN_HEADER_LEN);
1240*4882a593Smuzhiyun 
1241*4882a593Smuzhiyun 	DHD_TRACE(("%s %d: TCP pkt!\n", __FUNCTION__, __LINE__));
1242*4882a593Smuzhiyun 
1243*4882a593Smuzhiyun 	/* is it an ack ? Allow only ACK flag, not to suppress others. */
1244*4882a593Smuzhiyun 	if (new_tcp_hdr[TCP_FLAGS_OFFSET] != TCP_FLAG_ACK) {
1245*4882a593Smuzhiyun 		DHD_TRACE(("%s %d: Do not touch TCP flag 0x%x\n",
1246*4882a593Smuzhiyun 			__FUNCTION__, __LINE__, new_tcp_hdr[TCP_FLAGS_OFFSET]));
1247*4882a593Smuzhiyun 		goto exit;
1248*4882a593Smuzhiyun 	}
1249*4882a593Smuzhiyun 
1250*4882a593Smuzhiyun 	new_ip_total_len = ntoh16_ua(&new_ip_hdr[IPV4_PKTLEN_OFFSET]);
1251*4882a593Smuzhiyun 	new_tcp_hdr_len = 4 * TCP_HDRLEN(new_tcp_hdr[TCP_HLEN_OFFSET]);
1252*4882a593Smuzhiyun 
1253*4882a593Smuzhiyun 	/* This packet has TCP data, so just send */
1254*4882a593Smuzhiyun 	if (new_ip_total_len > new_ip_hdr_len + new_tcp_hdr_len) {
1255*4882a593Smuzhiyun 		DHD_TRACE(("%s %d: Do nothing for TCP DATA\n", __FUNCTION__, __LINE__));
1256*4882a593Smuzhiyun 		goto exit;
1257*4882a593Smuzhiyun 	}
1258*4882a593Smuzhiyun 
1259*4882a593Smuzhiyun 	ASSERT(new_ip_total_len == new_ip_hdr_len + new_tcp_hdr_len);
1260*4882a593Smuzhiyun 
1261*4882a593Smuzhiyun 	new_tcp_ack_num = ntoh32_ua(&new_tcp_hdr[TCP_ACK_NUM_OFFSET]);
1262*4882a593Smuzhiyun 
1263*4882a593Smuzhiyun 	DHD_TRACE(("%s %d: TCP ACK with zero DATA length"
1264*4882a593Smuzhiyun 		" IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR" TCP port %d %d\n",
1265*4882a593Smuzhiyun 		__FUNCTION__, __LINE__,
1266*4882a593Smuzhiyun 		IPV4_ADDR_TO_STR(ntoh32_ua(&new_ip_hdr[IPV4_SRC_IP_OFFSET])),
1267*4882a593Smuzhiyun 		IPV4_ADDR_TO_STR(ntoh32_ua(&new_ip_hdr[IPV4_DEST_IP_OFFSET])),
1268*4882a593Smuzhiyun 		ntoh16_ua(&new_tcp_hdr[TCP_SRC_PORT_OFFSET]),
1269*4882a593Smuzhiyun 		ntoh16_ua(&new_tcp_hdr[TCP_DEST_PORT_OFFSET])));
1270*4882a593Smuzhiyun 
1271*4882a593Smuzhiyun 	/* Look for tcp_ack_info that has the same ip src/dst addrs and tcp src/dst ports */
1272*4882a593Smuzhiyun 	flags = dhd_os_tcpacklock(dhdp);
1273*4882a593Smuzhiyun 
1274*4882a593Smuzhiyun 	tcpack_sup_mod = dhdp->tcpack_sup_module;
1275*4882a593Smuzhiyun 	tcpack_info_tbl = tcpack_sup_mod->tcpack_info_tbl;
1276*4882a593Smuzhiyun 
1277*4882a593Smuzhiyun 	if (!tcpack_sup_mod) {
1278*4882a593Smuzhiyun 		DHD_ERROR(("%s %d: tcpack suppress module NULL!!\n", __FUNCTION__, __LINE__));
1279*4882a593Smuzhiyun 		dhd_os_tcpackunlock(dhdp, flags);
1280*4882a593Smuzhiyun 		goto exit;
1281*4882a593Smuzhiyun 	}
1282*4882a593Smuzhiyun 
1283*4882a593Smuzhiyun 	hold = TRUE;
1284*4882a593Smuzhiyun 
1285*4882a593Smuzhiyun 	for (i = 0; i < TCPACK_INFO_MAXNUM; i++) {
1286*4882a593Smuzhiyun 		void *oldpkt;	/* TCPACK packet that is already in txq or DelayQ */
1287*4882a593Smuzhiyun 		uint8 *old_ether_hdr, *old_ip_hdr, *old_tcp_hdr;
1288*4882a593Smuzhiyun 		uint32 old_ip_hdr_len;
1289*4882a593Smuzhiyun 		uint32 old_tcpack_num;	/* TCP ACK number of old TCPACK packet in Q */
1290*4882a593Smuzhiyun 
1291*4882a593Smuzhiyun 		if ((oldpkt = tcpack_info_tbl[i].pkt_in_q) == NULL) {
1292*4882a593Smuzhiyun 			if (free_slot == TCPACK_INFO_MAXNUM) {
1293*4882a593Smuzhiyun 				free_slot = i;
1294*4882a593Smuzhiyun 			}
1295*4882a593Smuzhiyun 			continue;
1296*4882a593Smuzhiyun 		}
1297*4882a593Smuzhiyun 
1298*4882a593Smuzhiyun 		if (PKTDATA(dhdp->osh, oldpkt) == NULL) {
1299*4882a593Smuzhiyun 			DHD_ERROR(("%s %d: oldpkt data NULL!! cur idx %d\n",
1300*4882a593Smuzhiyun 				__FUNCTION__, __LINE__, i));
1301*4882a593Smuzhiyun 			hold = FALSE;
1302*4882a593Smuzhiyun 			dhd_os_tcpackunlock(dhdp, flags);
1303*4882a593Smuzhiyun 			goto exit;
1304*4882a593Smuzhiyun 		}
1305*4882a593Smuzhiyun 
1306*4882a593Smuzhiyun 		old_ether_hdr = tcpack_info_tbl[i].pkt_ether_hdr;
1307*4882a593Smuzhiyun 		old_ip_hdr = old_ether_hdr + ETHER_HDR_LEN;
1308*4882a593Smuzhiyun 		old_ip_hdr_len = IPV4_HLEN(old_ip_hdr);
1309*4882a593Smuzhiyun 		old_tcp_hdr = old_ip_hdr + old_ip_hdr_len;
1310*4882a593Smuzhiyun 
1311*4882a593Smuzhiyun 		DHD_TRACE(("%s %d: oldpkt %p[%d], IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR
1312*4882a593Smuzhiyun 			" TCP port %d %d\n", __FUNCTION__, __LINE__, oldpkt, i,
1313*4882a593Smuzhiyun 			IPV4_ADDR_TO_STR(ntoh32_ua(&old_ip_hdr[IPV4_SRC_IP_OFFSET])),
1314*4882a593Smuzhiyun 			IPV4_ADDR_TO_STR(ntoh32_ua(&old_ip_hdr[IPV4_DEST_IP_OFFSET])),
1315*4882a593Smuzhiyun 			ntoh16_ua(&old_tcp_hdr[TCP_SRC_PORT_OFFSET]),
1316*4882a593Smuzhiyun 			ntoh16_ua(&old_tcp_hdr[TCP_DEST_PORT_OFFSET])));
1317*4882a593Smuzhiyun 
1318*4882a593Smuzhiyun 		/* If either of IP address or TCP port number does not match, skip. */
1319*4882a593Smuzhiyun 		if (memcmp(&new_ip_hdr[IPV4_SRC_IP_OFFSET],
1320*4882a593Smuzhiyun 			&old_ip_hdr[IPV4_SRC_IP_OFFSET], IPV4_ADDR_LEN * 2) ||
1321*4882a593Smuzhiyun 			memcmp(&new_tcp_hdr[TCP_SRC_PORT_OFFSET],
1322*4882a593Smuzhiyun 			&old_tcp_hdr[TCP_SRC_PORT_OFFSET], TCP_PORT_LEN * 2)) {
1323*4882a593Smuzhiyun 			continue;
1324*4882a593Smuzhiyun 		}
1325*4882a593Smuzhiyun 
1326*4882a593Smuzhiyun 		old_tcpack_num = ntoh32_ua(&old_tcp_hdr[TCP_ACK_NUM_OFFSET]);
1327*4882a593Smuzhiyun 
1328*4882a593Smuzhiyun 		if (IS_TCPSEQ_GE(new_tcp_ack_num, old_tcpack_num)) {
1329*4882a593Smuzhiyun 			tcpack_info_tbl[i].supp_cnt++;
1330*4882a593Smuzhiyun 			if (tcpack_info_tbl[i].supp_cnt >= dhdp->tcpack_sup_ratio) {
1331*4882a593Smuzhiyun 				tcpack_info_tbl[i].pkt_in_q = NULL;
1332*4882a593Smuzhiyun 				tcpack_info_tbl[i].pkt_ether_hdr = NULL;
1333*4882a593Smuzhiyun 				tcpack_info_tbl[i].ifidx = 0;
1334*4882a593Smuzhiyun 				tcpack_info_tbl[i].supp_cnt = 0;
1335*4882a593Smuzhiyun 				hold = FALSE;
1336*4882a593Smuzhiyun 			} else {
1337*4882a593Smuzhiyun 				tcpack_info_tbl[i].pkt_in_q = pkt;
1338*4882a593Smuzhiyun 				tcpack_info_tbl[i].pkt_ether_hdr = new_ether_hdr;
1339*4882a593Smuzhiyun 				tcpack_info_tbl[i].ifidx = ifidx;
1340*4882a593Smuzhiyun 			}
1341*4882a593Smuzhiyun 			PKTFREE(dhdp->osh, oldpkt, TRUE);
1342*4882a593Smuzhiyun 		} else {
1343*4882a593Smuzhiyun 			PKTFREE(dhdp->osh, pkt, TRUE);
1344*4882a593Smuzhiyun 		}
1345*4882a593Smuzhiyun 		dhd_os_tcpackunlock(dhdp, flags);
1346*4882a593Smuzhiyun 
1347*4882a593Smuzhiyun 		if (!hold) {
1348*4882a593Smuzhiyun #ifndef TCPACK_SUPPRESS_HOLD_HRT
1349*4882a593Smuzhiyun 			del_timer_sync(&tcpack_info_tbl[i].timer);
1350*4882a593Smuzhiyun #else
1351*4882a593Smuzhiyun 			hrtimer_cancel(&tcpack_sup_mod->tcpack_info_tbl[i].timer.timer);
1352*4882a593Smuzhiyun #endif /* TCPACK_SUPPRESS_HOLD_HRT */
1353*4882a593Smuzhiyun 		}
1354*4882a593Smuzhiyun 		goto exit;
1355*4882a593Smuzhiyun 	}
1356*4882a593Smuzhiyun 
1357*4882a593Smuzhiyun 	if (free_slot < TCPACK_INFO_MAXNUM) {
1358*4882a593Smuzhiyun 		/* No TCPACK packet with the same IP addr and TCP port is found
1359*4882a593Smuzhiyun 		 * in tcp_ack_info_tbl. So add this packet to the table.
1360*4882a593Smuzhiyun 		 */
1361*4882a593Smuzhiyun 		DHD_TRACE(("%s %d: Add pkt 0x%p(ether_hdr 0x%p) to tbl[%d]\n",
1362*4882a593Smuzhiyun 			__FUNCTION__, __LINE__, pkt, new_ether_hdr,
1363*4882a593Smuzhiyun 			free_slot));
1364*4882a593Smuzhiyun 
1365*4882a593Smuzhiyun 		tcpack_info_tbl[free_slot].pkt_in_q = pkt;
1366*4882a593Smuzhiyun 		tcpack_info_tbl[free_slot].pkt_ether_hdr = new_ether_hdr;
1367*4882a593Smuzhiyun 		tcpack_info_tbl[free_slot].ifidx = ifidx;
1368*4882a593Smuzhiyun 		tcpack_info_tbl[free_slot].supp_cnt = 1;
1369*4882a593Smuzhiyun #ifndef TCPACK_SUPPRESS_HOLD_HRT
1370*4882a593Smuzhiyun 		mod_timer(&tcpack_sup_mod->tcpack_info_tbl[free_slot].timer,
1371*4882a593Smuzhiyun 			jiffies + msecs_to_jiffies(dhdp->tcpack_sup_delay));
1372*4882a593Smuzhiyun #else
1373*4882a593Smuzhiyun #if LINUX_VERSION_CODE <= KERNEL_VERSION(5, 1, 21)
1374*4882a593Smuzhiyun 		tasklet_hrtimer_start(&tcpack_sup_mod->tcpack_info_tbl[free_slot].timer,
1375*4882a593Smuzhiyun 			ktime_set(0, dhdp->tcpack_sup_delay*1000000), HRTIMER_MODE_REL);
1376*4882a593Smuzhiyun #else
1377*4882a593Smuzhiyun 		hrtimer_start(&tcpack_sup_mod->tcpack_info_tbl[free_slot].timer,
1378*4882a593Smuzhiyun 			ktime_set(0, dhdp->tcpack_sup_delay*1000000), HRTIMER_MODE_REL_SOFT);
1379*4882a593Smuzhiyun #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) */
1380*4882a593Smuzhiyun #endif /* TCPACK_SUPPRESS_HOLD_HRT */
1381*4882a593Smuzhiyun 		tcpack_sup_mod->tcpack_info_cnt++;
1382*4882a593Smuzhiyun 	} else {
1383*4882a593Smuzhiyun 		DHD_TRACE(("%s %d: No empty tcp ack info tbl\n",
1384*4882a593Smuzhiyun 			__FUNCTION__, __LINE__));
1385*4882a593Smuzhiyun 	}
1386*4882a593Smuzhiyun 	dhd_os_tcpackunlock(dhdp, flags);
1387*4882a593Smuzhiyun 
1388*4882a593Smuzhiyun exit:
1389*4882a593Smuzhiyun 	return hold;
1390*4882a593Smuzhiyun }
1391*4882a593Smuzhiyun #endif /* DHDTCPACK_SUPPRESS */
1392*4882a593Smuzhiyun 
1393*4882a593Smuzhiyun #ifdef DHDTCPSYNC_FLOOD_BLK
1394*4882a593Smuzhiyun tcp_hdr_flag_t
dhd_tcpdata_get_flag(dhd_pub_t * dhdp,void * pkt)1395*4882a593Smuzhiyun dhd_tcpdata_get_flag(dhd_pub_t *dhdp, void *pkt)
1396*4882a593Smuzhiyun {
1397*4882a593Smuzhiyun 	uint8 *ether_hdr;	/* Ethernet header of the new packet */
1398*4882a593Smuzhiyun 	uint16 ether_type;	/* Ethernet type of the new packet */
1399*4882a593Smuzhiyun 	uint8 *ip_hdr;		/* IP header of the new packet */
1400*4882a593Smuzhiyun 	uint8 *tcp_hdr;		/* TCP header of the new packet */
1401*4882a593Smuzhiyun 	uint32 ip_hdr_len;	/* IP header length of the new packet */
1402*4882a593Smuzhiyun 	uint32 cur_framelen;
1403*4882a593Smuzhiyun 	uint8 flags;
1404*4882a593Smuzhiyun 
1405*4882a593Smuzhiyun 	ether_hdr = PKTDATA(dhdp->osh, pkt);
1406*4882a593Smuzhiyun 	cur_framelen = PKTLEN(dhdp->osh, pkt);
1407*4882a593Smuzhiyun 
1408*4882a593Smuzhiyun 	ether_type = ether_hdr[12] << 8 | ether_hdr[13];
1409*4882a593Smuzhiyun 
1410*4882a593Smuzhiyun 	if (ether_type != ETHER_TYPE_IP) {
1411*4882a593Smuzhiyun 		DHD_TRACE(("%s %d: Not a IP packet 0x%x\n",
1412*4882a593Smuzhiyun 			__FUNCTION__, __LINE__, ether_type));
1413*4882a593Smuzhiyun 		return FLAG_OTHERS;
1414*4882a593Smuzhiyun 	}
1415*4882a593Smuzhiyun 
1416*4882a593Smuzhiyun 	ip_hdr = ether_hdr + ETHER_HDR_LEN;
1417*4882a593Smuzhiyun 	cur_framelen -= ETHER_HDR_LEN;
1418*4882a593Smuzhiyun 
1419*4882a593Smuzhiyun 	if (cur_framelen < IPV4_MIN_HEADER_LEN) {
1420*4882a593Smuzhiyun 		return FLAG_OTHERS;
1421*4882a593Smuzhiyun 	}
1422*4882a593Smuzhiyun 
1423*4882a593Smuzhiyun 	ip_hdr_len = IPV4_HLEN(ip_hdr);
1424*4882a593Smuzhiyun 	if (IP_VER(ip_hdr) != IP_VER_4 || IPV4_PROT(ip_hdr) != IP_PROT_TCP) {
1425*4882a593Smuzhiyun 		DHD_TRACE(("%s %d: Not IPv4 nor TCP! ip ver %d, prot %d\n",
1426*4882a593Smuzhiyun 			__FUNCTION__, __LINE__, IP_VER(ip_hdr), IPV4_PROT(ip_hdr)));
1427*4882a593Smuzhiyun 		return FLAG_OTHERS;
1428*4882a593Smuzhiyun 	}
1429*4882a593Smuzhiyun 
1430*4882a593Smuzhiyun 	tcp_hdr = ip_hdr + ip_hdr_len;
1431*4882a593Smuzhiyun 
1432*4882a593Smuzhiyun 	flags = (uint8)tcp_hdr[TCP_FLAGS_OFFSET];
1433*4882a593Smuzhiyun 
1434*4882a593Smuzhiyun 	if (flags & TCP_FLAG_SYN) {
1435*4882a593Smuzhiyun 		if (flags & TCP_FLAG_ACK) {
1436*4882a593Smuzhiyun 			return FLAG_SYNCACK;
1437*4882a593Smuzhiyun 		}
1438*4882a593Smuzhiyun 		return FLAG_SYNC;
1439*4882a593Smuzhiyun 	}
1440*4882a593Smuzhiyun 	return FLAG_OTHERS;
1441*4882a593Smuzhiyun }
1442*4882a593Smuzhiyun #endif /* DHDTCPSYNC_FLOOD_BLK */
1443