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