static int wifi_resume(struct platform_device *pdev)
{
	DHD_TRACE(("##> %s\n", __FUNCTION__));
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39)) && defined(OOB_INTR_ONLY) && 1
	if (dhd_os_check_if_up(bcmsdh_get_drvdata()))
		bcmsdh_oob_intr_set(1);
#endif /* (OOB_INTR_ONLY) */
	return 0;
}
static int wifi_plat_dev_drv_suspend(struct platform_device *pdev, pm_message_t state)
{
	DHD_TRACE(("##> %s\n", __FUNCTION__));
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39)) && defined(OOB_INTR_ONLY) && \
	defined(BCMSDIO)
	bcmsdh_oob_intr_set(0);
#endif /* (OOB_INTR_ONLY) */
	return 0;
}
Example #3
0
void *wifi_get_country_code(char *ccode)
{
	DHD_TRACE(("%s\n", __FUNCTION__));
	if (!ccode)
		return NULL;
	if (wifi_control_data && wifi_control_data->get_country_code) {
		return wifi_control_data->get_country_code(ccode);
	}
	return NULL;
}
/*
 *  channel list parsing from cscan tlv list
*/
int
wl_iw_parse_channel_list_tlv(char** list_str, uint16* channel_list,
                             int channel_num, int *bytes_left)
{
	char* str = *list_str;
	int idx = 0;

	if ((list_str == NULL) || (*list_str == NULL) ||(bytes_left == NULL) || (*bytes_left < 0)) {
		DHD_ERROR(("%s error paramters\n", __FUNCTION__));
		return -1;
	}

	while (*bytes_left > 0) {

		if (str[0] != CSCAN_TLV_TYPE_CHANNEL_IE) {
			*list_str = str;
			DHD_TRACE(("End channel=%d left_parse=%d %d\n", idx, *bytes_left, str[0]));
			return idx;
		}
		/* Get proper CSCAN_TLV_TYPE_CHANNEL_IE */
		*bytes_left -= 1;
		str += 1;

		if (str[0] == 0) {
			/* All channels */
			channel_list[idx] = 0x0;
		}
		else {
			channel_list[idx] = (uint16)str[0];
			DHD_TRACE(("%s channel=%d \n", __FUNCTION__,  channel_list[idx]));
		}
		*bytes_left -= 1;
		str += 1;

		if (idx++ > 255) {
			DHD_ERROR(("%s Too many channels \n", __FUNCTION__));
			return -1;
		}
	}

	*list_str = str;
	return idx;
}
Example #5
0
int dhd_check_module_mac(dhd_pub_t *dhd)
{
	int ret = -1;
	unsigned char cis_buf[250] = {0};
	unsigned char mac_buf[20] = {0};
	unsigned char otp_mac_buf[20] = {0};
	const char *macfilepath = MACINFO_EFS;

	/* Try reading out from CIS */
	cis_rw_t *cish = (cis_rw_t *)&cis_buf[8];
	struct file *fp_mac = NULL;

	cish->source = 0;
	cish->byteoff = 0;
	cish->nbytes = sizeof(cis_buf);

	strcpy(cis_buf, "cisdump");
	ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, cis_buf,
		sizeof(cis_buf), 0, 0);
	if (ret < 0) {
		DHD_TRACE(("%s: CIS reading failed, err=%d\n", __func__,
			ret));
		return ret;
	} else {
		unsigned char mac_id[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
#ifdef DUMP_CIS
		dump_cis(cis_buf, 48);
#endif
		mac_id[0] = cis_buf[CIS_MAC_OFFSET];
		mac_id[1] = cis_buf[CIS_MAC_OFFSET + 1];
		mac_id[2] = cis_buf[CIS_MAC_OFFSET + 2];
		mac_id[3] = cis_buf[CIS_MAC_OFFSET + 3];
		mac_id[4] = cis_buf[CIS_MAC_OFFSET + 4];
		mac_id[5] = cis_buf[CIS_MAC_OFFSET + 5];

		sprintf(otp_mac_buf, "%02X:%02X:%02X:%02X:%02X:%02X\n",
			mac_id[0], mac_id[1], mac_id[2], mac_id[3], mac_id[4],
			mac_id[5]);
		DHD_ERROR(("[WIFI]mac_id is setted from OTP \n"));
	}

	fp_mac = filp_open(macfilepath, O_RDONLY, 0);
	if (!IS_ERR(fp_mac)) {
		DHD_ERROR(("[WIFI]Check Mac address in .mac.info \n"));
		kernel_read(fp_mac, fp_mac->f_pos, mac_buf, sizeof(mac_buf));

		if (strncmp(mac_buf , otp_mac_buf , 17) != 0) {
			DHD_ERROR(("[WIFI]file MAC is wrong. Write OTP MAC in .mac.info \n"));
			dhd_write_mac_file(macfilepath, otp_mac_buf, sizeof(otp_mac_buf));
		}
	}

	return ret;
}
Example #6
0
static int wifi_add_dev(void)
{
	int ret;
	DHD_TRACE(("## Calling platform_driver_register\n"));
	ret = platform_driver_register(&wifi_device);
	if (ret)
		return ret;

	ret = platform_driver_register(&wifi_device_legacy);
	return ret;
}
static int wifi_suspend(struct platform_device *pdev, pm_message_t state)
{
	DHD_TRACE(("##> %s\n", __FUNCTION__));
#if defined(BCMHOST)
	if (dhd_os_check_wakelock(bcmsdh_get_drvdata()))
		return -EBUSY;
#endif
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39)) && defined(OOB_INTR_ONLY) && 1
	bcmsdh_oob_intr_set(0);
#endif /* (OOB_INTR_ONLY) */
	return 0;
}
Example #8
0
static int
dhd_iovar_op(dhd_pub_t *dhd_pub, const char *name,
	     void *params, int plen, void *arg, int len, bool set)
{
	int bcmerror = 0;
	int val_size;
	const bcm_iovar_t *vi = NULL;
	u32 actionid;

	DHD_TRACE(("%s: Enter\n", __func__));

	ASSERT(name);
	ASSERT(len >= 0);

	/* Get MUST have return space */
	ASSERT(set || (arg && len));

	/* Set does NOT take qualifiers */
	ASSERT(!set || (!params && !plen));

	vi = bcm_iovar_lookup(dhd_iovars, name);
	if (vi == NULL) {
		bcmerror = -ENOTSUPP;
		goto exit;
	}

	DHD_CTL(("%s: %s %s, len %d plen %d\n", __func__,
		 name, (set ? "set" : "get"), len, plen));

	/* set up 'params' pointer in case this is a set command so that
	 * the convenience int and bool code can be common to set and get
	 */
	if (params == NULL) {
		params = arg;
		plen = len;
	}

	if (vi->type == IOVT_VOID)
		val_size = 0;
	else if (vi->type == IOVT_BUFFER)
		val_size = len;
	else
		/* all other types are integer sized */
		val_size = sizeof(int);

	actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid);
	bcmerror =
	    dhd_doiovar(dhd_pub, vi, actionid, name, params, plen, arg, len,
			val_size);

exit:
	return bcmerror;
}
Example #9
0
int
dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len)
{
	dhd_prot_t *prot = dhd->prot;
	cdc_ioctl_t *msg = &prot->msg;
	int ret = 0;
	uint32 flags, id;

	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
	DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len));

	if (prot == 0) {
		DHD_ERROR(("dhdcdc_set_ioctl: dhd->prot NULL. idx=%d, cmd=0x%x", ifidx, cmd));
		goto done;
	}
	memset(msg, 0, sizeof(cdc_ioctl_t));

	msg->cmd = htol32(cmd);
	msg->len = htol32(len);
	msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT) | CDCF_IOC_SET;
	CDC_SET_IF_IDX(msg, ifidx);
	msg->flags |= htol32(msg->flags);

	if (buf)
		memcpy(prot->buf, buf, len);

	if ((ret = dhdcdc_msg(dhd)) < 0)
		goto done;

	if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0)
		goto done;

	flags = ltoh32(msg->flags);
	id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;

	if (id != prot->reqid) {
		DHD_ERROR(("%s: %s: unexpected request id %d (expected %d)\n",
		           dhd_ifname(dhd, ifidx), __FUNCTION__, id, prot->reqid));
		ret = -EINVAL;
		goto done;
	}

	/* Check the ERROR flag */
	if (flags & CDCF_IOC_ERROR)
	{
		ret = ltoh32(msg->status);
		/* Cache error from dongle */
		dhd->dongle_error = ret;
	}

done:
	return ret;
}
Example #10
0
void dhd_arp_offload_add_ip(dhd_pub_t *dhd, u32 ipaddr)
{
#ifdef ARP_OFFLOAD_SUPPORT
       int iov_len = 0;
       char iovbuf[32];
       int retcode;

       dhd_os_proto_block(dhd);

       iov_len = bcm_mkiovar("arp_hostip", (char *)&ipaddr, 4, iovbuf, sizeof(iovbuf));
       retcode = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, iov_len);

       dhd_os_proto_unblock(dhd);

       if (retcode)
               DHD_TRACE(("%s: ARP ip addr add failed, retcode = %d\n",
               __FUNCTION__, retcode));
       else
               DHD_TRACE(("%s: ARP ipaddr entry added\n",
               __FUNCTION__));
#endif /* ARP_OFFLOAD_SUPPORT */
}
Example #11
0
static int
dhd_iovar_op(dhd_pub_t *dhd_pub, const char *name,
             void *params, int plen, void *arg, int len, bool set)
{
	int bcmerror = 0;
	int val_size;
	const bcm_iovar_t *vi = NULL;
	uint32 actionid;

	DHD_TRACE(("%s: Enter\n", __FUNCTION__));

	ASSERT(name);
	ASSERT(len >= 0);

	
	ASSERT(set || (arg && len));

	
	ASSERT(!set || (!params && !plen));

	if ((vi = bcm_iovar_lookup(dhd_iovars, name)) == NULL)
	{
		bcmerror = BCME_UNSUPPORTED;
		goto exit;
	}

	DHD_CTL(("%s: %s %s, len %d plen %d\n", __FUNCTION__,
	         name, (set ? "set" : "get"), len, plen));

	
	if (params == NULL) {
		params = arg;
		plen = len;
	}

	if (vi->type == IOVT_VOID)
		val_size = 0;
	else if (vi->type == IOVT_BUFFER)
		val_size = len;
	else
		
		val_size = sizeof(int);

	actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid);
	bcmerror = dhd_doiovar(dhd_pub, vi, actionid, name, params, plen, arg, len, val_size);

exit:
	return bcmerror;
}
Example #12
0
int
dhd_prot_hdrpull(dhd_pub_t *dhd, int *ifidx, void *pktbuf)
{
#ifdef BDC
	struct bdc_header *h;
#endif
	uint8 bdc_ver;

	DHD_TRACE(("%s: Enter\n", __FUNCTION__));

#ifdef BDC
	/* Pop BDC header used to convey priority for buses that don't */

	if (PKTLEN(dhd->osh, pktbuf) < BDC_HEADER_LEN) {
		DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__,
		           PKTLEN(dhd->osh, pktbuf), BDC_HEADER_LEN));
		return BCME_ERROR;
	}

	h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);

	if ((*ifidx = BDC_GET_IF_IDX(h)) >= DHD_MAX_IFS) {
		DHD_ERROR(("%s: rx data ifnum out of range (%d)\n",
		           __FUNCTION__, *ifidx));
		return BCME_ERROR;
	}

	bdc_ver = (h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT;

	if ((bdc_ver != BDC_PROTO_VER) && (bdc_ver != 2)) {
		DHD_ERROR(("%s: non-BDC packet received, flags 0x%x, BDC %d\n",
		           dhd_ifname(dhd, *ifidx), h->flags, bdc_ver));
		return BCME_ERROR;
	}

	if (h->flags & BDC_FLAG_SUM_GOOD) {
		DHD_INFO(("%s: BDC packet received with good rx-csum, flags 0x%x\n",
		          dhd_ifname(dhd, *ifidx), h->flags));
		PKTSETSUMGOOD(pktbuf, TRUE);
	}

	PKTSETPRIO(pktbuf, (h->priority & BDC_PRIORITY_MASK));

	PKTPULL(dhd->osh, pktbuf, BDC_HEADER_LEN);
#endif /* BDC */

	return 0;
}
Example #13
0
static int
dhdcdc_cmplt(dhd_pub_t *dhd, uint32 id, uint32 len)
{
	int ret;
	dhd_prot_t *prot = dhd->prot;

	DHD_TRACE(("%s: Enter\n", __FUNCTION__));

	do {
		ret = dhd_bus_rxctl(dhd->bus, (uchar*)&prot->msg, len+sizeof(cdc_ioctl_t));
		if (ret < 0)
			break;
	} while (CDC_IOC_ID(ltoh32(prot->msg.flags)) != id);

	return ret;
}
/*
 *  data parsing from ComboScan tlv list
*/
int
wl_iw_parse_data_tlv(char** list_str, void *dst, int dst_size, const char token,
                     int input_size, int *bytes_left)
{
	char* str = *list_str;
	uint16 short_temp;
	uint32 int_temp;

	if ((list_str == NULL) || (*list_str == NULL) ||(bytes_left == NULL) || (*bytes_left < 0)) {
		DHD_ERROR(("%s error paramters\n", __FUNCTION__));
		return -1;
	}

	/* Clean all dest bytes */
	memset(dst, 0, dst_size);
	while (*bytes_left > 0) {

		if (str[0] != token) {
			DHD_TRACE(("%s NOT Type=%d get=%d left_parse=%d \n",
				__FUNCTION__, token, str[0], *bytes_left));
			return -1;
		}

		*bytes_left -= 1;
		str += 1;

		if (input_size == 1) {
			memcpy(dst, str, input_size);
		}
		else if (input_size == 2) {
			memcpy(dst, (char *)htod16(memcpy(&short_temp, str, input_size)),
				input_size);
		}
		else if (input_size == 4) {
			memcpy(dst, (char *)htod32(memcpy(&int_temp, str, input_size)),
				input_size);
		}

		*bytes_left -= input_size;
		str += input_size;
		*list_str = str;
		return 1;
	}
	return 1;
}
Example #15
0
static int
dhdcdc_msg(dhd_pub_t *dhd)
{
	dhd_prot_t *prot = dhd->prot;
	int len = ltoh32(prot->msg.len) + sizeof(cdc_ioctl_t);

	DHD_TRACE(("%s: Enter\n", __FUNCTION__));

	/* NOTE : cdc->msg.len holds the desired length of the buffer to be
	 *        returned. Only up to CDC_MAX_MSG_SIZE of this buffer area
	 *	  is actually sent to the dongle
	 */
	if (len > CDC_MAX_MSG_SIZE)
		len = CDC_MAX_MSG_SIZE;

	/* Send request */
	return dhd_bus_txctl(dhd->bus, (uchar*)&prot->msg, len);
}
Example #16
0
void *wifi_platform_get_country_code(wifi_adapter_info_t *adapter, char *ccode)
{
	/* get_country_code was added after 2.6.39 */
#if	(LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39))
	struct wifi_platform_data *plat_data;

	if (!ccode || !adapter || !adapter->wifi_plat_data)
		return NULL;
	plat_data = adapter->wifi_plat_data;

	DHD_TRACE(("%s\n", __FUNCTION__));
	if (plat_data->get_country_code) {
		return plat_data->get_country_code(ccode);
	}
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) */

	return NULL;
}
Example #17
0
wifi_adapter_info_t* dhd_wifi_platform_get_adapter(uint32 bus_type, uint32 bus_num, uint32 slot_num)
{
	int i;

	if (dhd_wifi_platdata == NULL)
		return NULL;

	for (i = 0; i < dhd_wifi_platdata->num_adapters; i++) {
		wifi_adapter_info_t *adapter = &dhd_wifi_platdata->adapters[i];
		if ((adapter->bus_type == -1 || adapter->bus_type == bus_type) &&
			(adapter->bus_num == -1 || adapter->bus_num == bus_num) &&
			(adapter->slot_num == -1 || adapter->slot_num == slot_num)) {
			DHD_TRACE(("found adapter info '%s'\n", adapter->name));
			return adapter;
		}
	}
	return NULL;
}
static void _tdata_psh_info_pool_deinit(dhd_pub_t *dhdp,
	tcpack_sup_module_t *tcpack_sup_mod)
{
	uint i;
	tdata_psh_info_t *tdata_psh_info;

	DHD_TRACE(("%s %d: Enter\n", __FUNCTION__, __LINE__));

	if (tcpack_sup_mod == NULL) {
		DHD_ERROR(("%s %d: ERROR tcpack_sup_mod NULL!\n",
			__FUNCTION__, __LINE__));
		return;
	}

	for (i = 0; i < tcpack_sup_mod->tcpdata_info_cnt; i++) {
		tcpdata_info_t *tcpdata_info = &tcpack_sup_mod->tcpdata_info_tbl[i];
		/* Return tdata_psh_info elements allocated to each tcpdata_info to the pool */
		while ((tdata_psh_info = tcpdata_info->tdata_psh_info_head)) {
			tcpdata_info->tdata_psh_info_head = tdata_psh_info->next;
			tdata_psh_info->next = NULL;
			_tdata_psh_info_pool_enq(tcpack_sup_mod, tdata_psh_info);
		}
		tcpdata_info->tdata_psh_info_tail = NULL;
	}
#ifdef DHDTCPACK_SUP_DBG
	DHD_ERROR(("%s %d: PSH INFO ENQ %d\n",
		__FUNCTION__, __LINE__, tcpack_sup_mod->psh_info_enq_num));
#endif /* DHDTCPACK_SUP_DBG */

	i = 0;
	/* Be sure we recollected all tdata_psh_info elements */
	while ((tdata_psh_info = tcpack_sup_mod->tdata_psh_info_free)) {
		tcpack_sup_mod->tdata_psh_info_free = tdata_psh_info->next;
		tdata_psh_info->next = NULL;
		i++;
	}
	ASSERT(i == TCPDATA_PSH_INFO_MAXNUM);
	MFREE(dhdp->osh, tcpack_sup_mod->tdata_psh_info_pool,
		sizeof(tdata_psh_info_t) * TCPDATA_PSH_INFO_MAXNUM);
	tcpack_sup_mod->tdata_psh_info_pool = NULL;

	return;
}
void
dhd_iscan_free_buf(void *dhdp, iscan_buf_t *iscan_delete)
{
	iscan_buf_t *iscanbuf_free = 0;
	iscan_buf_t *iscanbuf_prv = 0;
	iscan_buf_t *iscanbuf_cur;
	dhd_pub_t *dhd = dhd_bus_pub(dhdp);
	DHD_TRACE(("%s: Entered\n", __FUNCTION__));

	dhd_iscan_lock();

	iscanbuf_cur = iscan_chain;

	/* If iscan_delete is null then delete the entire 
	 * chain or else delete specific one provided
	 */
	if (!iscan_delete) {
		while (iscanbuf_cur) {
			iscanbuf_free = iscanbuf_cur;
			iscanbuf_cur = iscanbuf_cur->next;
			iscanbuf_free->next = 0;
			MFREE(dhd->osh, iscanbuf_free, sizeof(iscan_buf_t));
		}
		iscan_chain = 0;
	} else {
		while (iscanbuf_cur) {
			if (iscanbuf_cur == iscan_delete)
				break;
			iscanbuf_prv = iscanbuf_cur;
			iscanbuf_cur = iscanbuf_cur->next;
		}
		if (iscanbuf_prv)
			iscanbuf_prv->next = iscan_delete->next;

		iscan_delete->next = 0;
		MFREE(dhd->osh, iscan_delete, sizeof(iscan_buf_t));

		if (!iscanbuf_prv)
			iscan_chain = 0;
	}
	dhd_iscan_unlock();
}
Example #20
0
int wl_android_wifi_off(struct net_device *dev)
{
	int ret = 0;

	if (!dev) {
		DHD_TRACE(("%s: dev is null\n", __FUNCTION__));
		return -EINVAL;
	}

	dhd_net_if_lock(dev);
	if (g_wifi_on) {
		dhd_dev_reset(dev, 1);
		sdioh_stop(NULL);
		dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF);
		g_wifi_on = 0;
	}
	dhd_net_if_unlock(dev);

	return ret;
}
/*
 * module init
*/
int sdio_function_init(void)
{
	int error = 0;
	sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__));

	gInstance = kzalloc(sizeof(BCMSDH_SDMMC_INSTANCE), GFP_KERNEL);
	if (!gInstance)
		return -ENOMEM;

	bzero(&sdmmc_dev, sizeof(sdmmc_dev));
	error = sdio_register_driver(&bcmsdh_sdmmc_driver);

#if defined(CONFIG_BRCM_LGE_WL_HOSTWAKEUP)
	if (!error) {
		dhd_register_early_suspend();
		DHD_TRACE(("%s: registered with Android PM\n", __FUNCTION__));
	}
#endif	/* defined(CONFIG_BRCM_LGE_WL_HOSTWAKEUP) */

	return error;
}
void dhd_late_resume(struct early_suspend *h)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
            dhd_mmc_suspend = FALSE;
#endif

	printk(KERN_ERR "%s: enter\n", __FUNCTION__);
	DHD_TRACE(("%s: enter\n", __FUNCTION__));

	if(dhd_suspend_context == TRUE ){
	disable_irq(dhd_wifi_sleep->host_wake_irq);
	/*Do the resume operations*/
	dhd_resume();
	}else 
		printk("%s: Do not dhd_suspend mode setting.\n",__FUNCTION__);

//#if defined(CONFIG_LGE_BCM432X_PATCH) && defined(PKT_FILTER_SUPPORT)
#if 0	//hyeok-multi
 	dhd_pkt_enable_filter(0, dhd_early_suspend_ctrl.bus);
#endif	/* CONFIG_LGE_BCM432X_PATCH */
	return;
}
int wl_android_wifi_off(struct net_device *dev)
{
	int ret = 0;

	DHD_ERROR(("%s in\n", __FUNCTION__));
	if (!dev) {
		DHD_TRACE(("%s: dev is null\n", __FUNCTION__));
		return -EINVAL;
	}

	dhd_net_if_lock(dev);
	if (g_wifi_on) {
		dhd_dev_reset(dev, 1);
		if (dhd_download_fw_on_driverload)
			msleep(100);
		sdioh_stop(NULL);
		dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF);
		g_wifi_on = FALSE;
	}
	dhd_net_if_unlock(dev);

	return ret;
}
int wl_android_wifi_off(struct net_device *dev)
{
	int ret = 0;

	printk("%s in\n", __FUNCTION__);
	if (!dev) {
		DHD_TRACE(("%s: dev is null\n", __FUNCTION__));
		return -EINVAL;
	}

	dhd_net_if_lock(dev);
	if (g_wifi_on) {
#ifdef PROP_TXSTATUS
		dhd_wlfc_deinit(bcmsdh_get_drvdata());
#endif
		dhd_dev_reset(dev, 1);
		sdioh_stop(NULL);
		dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF);
		g_wifi_on = FALSE;
	}
	dhd_net_if_unlock(dev);

	return ret;
}
Example #25
0
int
wl_host_event(struct dhd_info *dhd, int *ifidx, void *pktdata,
              wl_event_msg_t *event, void **data_ptr)
{
	/* check whether packet is a BRCM event pkt */
	bcm_event_t *pvt_data = (bcm_event_t *)pktdata;
	char *event_data;
	uint32 type, status;
	uint16 flags;
	int evlen;


	if (bcmp(BRCM_OUI, &pvt_data->bcm_hdr.oui[0], DOT11_OUI_LEN))
		return (BCME_ERROR);

	/* BRCM event pkt may be unaligned - use xxx_ua to load user_subtype. */
	if (ntoh16_ua((void *)&pvt_data->bcm_hdr.usr_subtype) != BCMILCP_BCM_SUBTYPE_EVENT)
		return (BCME_ERROR);

	*data_ptr = &pvt_data[1];
	event_data = *data_ptr;

	/* memcpy since BRCM event pkt may be unaligned. */
	memcpy(event, &pvt_data->event, sizeof(wl_event_msg_t));

	type = ntoh32_ua((void *)&event->event_type);
	flags = ntoh16_ua((void *)&event->flags);
	status = ntoh32_ua((void *)&event->status);
	evlen = ntoh32_ua((void *)&event->datalen) + sizeof(bcm_event_t);

	switch (type) {
		case WLC_E_IF:
			{
				dhd_if_event_t *ifevent = (dhd_if_event_t *)event_data;

				if (ifevent->ifidx > 0 && ifevent->ifidx < DHD_MAX_IFS)
				{
					if (ifevent->action == WLC_E_IF_ADD)
						dhd_add_if(dhd, ifevent->ifidx,
							NULL, event->ifname,
							pvt_data->eth.ether_dhost,
							ifevent->flags, ifevent->bssidx);
					else
						dhd_del_if(dhd, ifevent->ifidx);
				} else {
					DHD_ERROR(("%s: Invalid ifidx %d for %s\n",
						__FUNCTION__, ifevent->ifidx, event->ifname));
				}
			}
			/* send up the if event: btamp user needs it */
			*ifidx = dhd_ifname2idx(dhd, event->ifname);
			/* push up to external supp/auth */
			dhd_event(dhd, (char *)pvt_data, evlen, *ifidx);
			break;


		/* fall through */
		/* These are what external supplicant/authenticator wants */
		case WLC_E_LINK:
		case WLC_E_ASSOC_IND:
		case WLC_E_REASSOC_IND:
		case WLC_E_DISASSOC_IND:
		case WLC_E_MIC_ERROR:
		default:
		/* Fall through: this should get _everything_  */

			*ifidx = dhd_ifname2idx(dhd, event->ifname);
			/* push up to external supp/auth */
			dhd_event(dhd, (char *)pvt_data, evlen, *ifidx);
			DHD_TRACE(("%s: MAC event %d, flags %x, status %x\n",
			           __FUNCTION__, type, flags, status));

			/* put it back to WLC_E_NDIS_LINK */
			if (type == WLC_E_NDIS_LINK) {
				uint32 temp;

				temp = ntoh32_ua((void *)&event->event_type);
				DHD_TRACE(("Converted to WLC_E_LINK type %d\n", temp));

				temp = ntoh32(WLC_E_NDIS_LINK);
				memcpy((void *)(&pvt_data->event.event_type), &temp,
					sizeof(pvt_data->event.event_type));
			}
			break;
	}

#ifdef SHOW_EVENTS
	wl_show_host_event(event, event_data);
#endif /* SHOW_EVENTS */

	return (BCME_OK);
}
Example #26
0
int
dhd_ioctl(dhd_pub_t * dhd_pub, dhd_ioctl_t *ioc, void * buf, uint buflen)
{
	int bcmerror = 0;

	DHD_TRACE(("%s: Enter\n", __FUNCTION__));

	switch (ioc->cmd) {
	case DHD_GET_MAGIC:
		if (buflen < sizeof(int))
			bcmerror = BCME_BUFTOOSHORT;
		else
			*(int*)buf = DHD_IOCTL_MAGIC;
		break;

	case DHD_GET_VERSION:
		if (buflen < sizeof(int))
			bcmerror = -BCME_BUFTOOSHORT;
		else
			*(int*)buf = DHD_IOCTL_VERSION;
		break;

	case DHD_GET_VAR:
	case DHD_SET_VAR: {
		char *arg;
		uint arglen;

		/* scan past the name to any arguments */
		for (arg = buf, arglen = buflen; *arg && arglen; arg++, arglen--);

		if (*arg) {
			bcmerror = BCME_BUFTOOSHORT;
			break;
		}

		/* account for the NUL terminator */
		arg++, arglen--;

		/* call with the appropriate arguments */
		if (ioc->cmd == DHD_GET_VAR)
			bcmerror = dhd_iovar_op(dhd_pub, buf, arg, arglen,
			buf, buflen, IOV_GET);
		else
			bcmerror = dhd_iovar_op(dhd_pub, buf, NULL, 0, arg, arglen, IOV_SET);
		if (bcmerror != BCME_UNSUPPORTED)
			break;

		/* not in generic table, try protocol module */
		if (ioc->cmd == DHD_GET_VAR)
			bcmerror = dhd_prot_iovar_op(dhd_pub, buf, arg,
				arglen, buf, buflen, IOV_GET);
		else
			bcmerror = dhd_prot_iovar_op(dhd_pub, buf,
				NULL, 0, arg, arglen, IOV_SET);
		if (bcmerror != BCME_UNSUPPORTED)
			break;

		/* if still not found, try bus module */
		if (ioc->cmd == DHD_GET_VAR)
			bcmerror = dhd_bus_iovar_op(dhd_pub, buf,
				arg, arglen, buf, buflen, IOV_GET);
		else
			bcmerror = dhd_bus_iovar_op(dhd_pub, buf,
				NULL, 0, arg, arglen, IOV_SET);

		break;
	}

	default:
		bcmerror = BCME_UNSUPPORTED;
	}

	return bcmerror;
}
Example #27
0
static int
dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, uint32 actionid,
	const char *name, void *params, int plen, void *arg, int len, int val_size)
{
	int bcmerror = 0;
	int32 int_val = 0;

	DHD_TRACE(("%s: Enter\n", __FUNCTION__));

	if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, IOV_ISSET(actionid))) != 0)
		goto exit;

	if (plen >= (int)sizeof(int_val))
		bcopy(params, &int_val, sizeof(int_val));

	switch (actionid) {
	case IOV_GVAL(IOV_VERSION):
		/* Need to have checked buffer length */
		strncpy((char*)arg, dhd_version, len);
		break;

	case IOV_GVAL(IOV_MSGLEVEL):
		int_val = (int32)dhd_msg_level;
		bcopy(&int_val, arg, val_size);
		break;

	case IOV_SVAL(IOV_MSGLEVEL):
		dhd_msg_level = int_val;
		break;

	case IOV_GVAL(IOV_BCMERRORSTR):
		strncpy((char *)arg, bcmerrorstr(dhd_pub->bcmerror), BCME_STRLEN);
		((char *)arg)[BCME_STRLEN - 1] = 0x00;
		break;

	case IOV_GVAL(IOV_BCMERROR):
		int_val = (int32)dhd_pub->bcmerror;
		bcopy(&int_val, arg, val_size);
		break;

	case IOV_GVAL(IOV_WDTICK):
		int_val = (int32)dhd_watchdog_ms;
		bcopy(&int_val, arg, val_size);
		break;

	case IOV_SVAL(IOV_WDTICK):
		if (!dhd_pub->up) {
			bcmerror = BCME_NOTUP;
			break;
		}
		dhd_os_wd_timer(dhd_pub, (uint)int_val);
		break;

	case IOV_GVAL(IOV_DUMP):
		bcmerror = dhd_dump(dhd_pub, arg, len);
		break;

	case IOV_SVAL(IOV_CLEARCOUNTS):
		dhd_pub->tx_packets = dhd_pub->rx_packets = 0;
		dhd_pub->tx_errors = dhd_pub->rx_errors = 0;
		dhd_pub->tx_ctlpkts = dhd_pub->rx_ctlpkts = 0;
		dhd_pub->tx_ctlerrs = dhd_pub->rx_ctlerrs = 0;
		dhd_pub->rx_dropped = 0;
		dhd_pub->rx_readahead_cnt = 0;
		dhd_pub->tx_realloc = 0;
		dhd_pub->rx_flushed = 0;
		memset(&dhd_pub->dstats, 0, sizeof(dhd_pub->dstats));
		dhd_bus_clearcounts(dhd_pub);
		break;


	case IOV_GVAL(IOV_IOCTLTIMEOUT):
	{
		int_val = (int32)dhd_os_get_ioctl_resp_timeout();
		bcopy(&int_val, arg, sizeof(int_val));
		break;
	}

	case IOV_SVAL(IOV_IOCTLTIMEOUT):
	{
		if (int_val <= 0)
			bcmerror = BCME_BADARG;
		else
			dhd_os_set_ioctl_resp_timeout((unsigned int)int_val);
		break;
	}


	default:
		bcmerror = BCME_UNSUPPORTED;
		break;
	}

exit:
	return bcmerror;
}
bool
dhd_tcpdata_info_get(dhd_pub_t *dhdp, void *pkt)
{
	uint8 *ether_hdr;	/* Ethernet header of the new packet */
	uint16 ether_type;	/* Ethernet type of the new packet */
	uint8 *ip_hdr;		/* IP header of the new packet */
	uint8 *tcp_hdr;		/* TCP header of the new packet */
	uint32 ip_hdr_len;	/* IP header length of the new packet */
	uint32 cur_framelen;
	uint16 ip_total_len;	/* Total length of IP packet for the new packet */
	uint32 tcp_hdr_len;		/* TCP header length of the new packet */
	uint32 tcp_seq_num;		/* TCP sequence number of the new packet */
	uint16 tcp_data_len;	/* TCP DATA length that excludes IP and TCP headers */
	uint32 end_tcp_seq_num;	/* TCP seq number of the last byte in the new packet */
	tcpack_sup_module_t *tcpack_sup_mod;
	tcpdata_info_t *tcpdata_info = NULL;
	tdata_psh_info_t *tdata_psh_info;

	int i;
	bool ret = FALSE;

	if (dhdp->tcpack_sup_mode != TCPACK_SUP_DELAYTX)
		goto exit;

	ether_hdr = PKTDATA(dhdp->osh, pkt);
	cur_framelen = PKTLEN(dhdp->osh, pkt);

	ether_type = ether_hdr[12] << 8 | ether_hdr[13];

	if (ether_type != ETHER_TYPE_IP) {
		DHD_TRACE(("%s %d: Not a IP packet 0x%x\n",
			__FUNCTION__, __LINE__, ether_type));
		goto exit;
	}

	DHD_TRACE(("%s %d: IP pkt! 0x%x\n", __FUNCTION__, __LINE__, ether_type));

	ip_hdr = ether_hdr + ETHER_HDR_LEN;
	cur_framelen -= ETHER_HDR_LEN;

	ASSERT(cur_framelen >= IPV4_MIN_HEADER_LEN);

	ip_hdr_len = IPV4_HLEN(ip_hdr);
	if (IP_VER(ip_hdr) != IP_VER_4 || IPV4_PROT(ip_hdr) != IP_PROT_TCP) {
		DHD_TRACE(("%s %d: Not IPv4 nor TCP! ip ver %d, prot %d\n",
			__FUNCTION__, __LINE__, IP_VER(ip_hdr), IPV4_PROT(ip_hdr)));
		goto exit;
	}

	tcp_hdr = ip_hdr + ip_hdr_len;
	cur_framelen -= ip_hdr_len;

	ASSERT(cur_framelen >= TCP_MIN_HEADER_LEN);

	DHD_TRACE(("%s %d: TCP pkt!\n", __FUNCTION__, __LINE__));

	ip_total_len = ntoh16_ua(&ip_hdr[IPV4_PKTLEN_OFFSET]);
	tcp_hdr_len = 4 * TCP_HDRLEN(tcp_hdr[TCP_HLEN_OFFSET]);

	/* This packet is mere TCP ACK, so do nothing */
	if (ip_total_len == ip_hdr_len + tcp_hdr_len) {
		DHD_TRACE(("%s %d: Do nothing for no data TCP ACK\n", __FUNCTION__, __LINE__));
		goto exit;
	}

	ASSERT(ip_total_len > ip_hdr_len + tcp_hdr_len);

	if ((tcp_hdr[TCP_FLAGS_OFFSET] & TCP_FLAG_PSH) == 0) {
		DHD_TRACE(("%s %d: Not interested TCP DATA packet\n", __FUNCTION__, __LINE__));
		goto exit;
	}

	DHD_TRACE(("%s %d: TCP DATA with nonzero DATA length"
		" IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR" TCP port %d %d, flag 0x%x\n",
		__FUNCTION__, __LINE__,
		IPV4_ADDR_TO_STR(ntoh32_ua(&ip_hdr[IPV4_SRC_IP_OFFSET])),
		IPV4_ADDR_TO_STR(ntoh32_ua(&ip_hdr[IPV4_DEST_IP_OFFSET])),
		ntoh16_ua(&tcp_hdr[TCP_SRC_PORT_OFFSET]),
		ntoh16_ua(&tcp_hdr[TCP_DEST_PORT_OFFSET]),
		tcp_hdr[TCP_FLAGS_OFFSET]));

	dhd_os_tcpacklock(dhdp);
	tcpack_sup_mod = dhdp->tcpack_sup_module;

	if (!tcpack_sup_mod) {
		DHD_ERROR(("%s %d: tcpack suppress module NULL!!\n", __FUNCTION__, __LINE__));
		ret = BCME_ERROR;
		dhd_os_tcpackunlock(dhdp);
		goto exit;
	}

	/* Look for tcpdata_info that has the same ip src/dst addrs and tcp src/dst ports */
	i = 0;
	while (i < tcpack_sup_mod->tcpdata_info_cnt) {
		tcpdata_info_t *tdata_info_tmp = &tcpack_sup_mod->tcpdata_info_tbl[i];
		uint32 now_in_ms = OSL_SYSUPTIME();
		DHD_TRACE(("%s %d: data info[%d], IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR
			" TCP port %d %d\n", __FUNCTION__, __LINE__, i,
			IPV4_ADDR_TO_STR(ntoh32_ua(tdata_info_tmp->src_ip_addr)),
			IPV4_ADDR_TO_STR(ntoh32_ua(tdata_info_tmp->dst_ip_addr)),
			ntoh16_ua(tdata_info_tmp->src_tcp_port),
			ntoh16_ua(tdata_info_tmp->dst_tcp_port)));

		/* If both IP address and TCP port number match, we found it so break. */
		if (memcmp(&ip_hdr[IPV4_SRC_IP_OFFSET],
			tdata_info_tmp->src_ip_addr, IPV4_ADDR_LEN * 2) == 0 &&
			memcmp(&tcp_hdr[TCP_SRC_PORT_OFFSET],
			tdata_info_tmp->src_tcp_port, TCP_PORT_LEN * 2) == 0) {
			tcpdata_info = tdata_info_tmp;
			tcpdata_info->last_used_time = now_in_ms;
			break;
		}

		if (now_in_ms - tdata_info_tmp->last_used_time > TCPDATA_INFO_TIMEOUT) {
			tdata_psh_info_t *tdata_psh_info_tmp;
			tcpdata_info_t *last_tdata_info;

			while ((tdata_psh_info_tmp = tdata_info_tmp->tdata_psh_info_head)) {
				tdata_info_tmp->tdata_psh_info_head = tdata_psh_info_tmp->next;
				tdata_psh_info_tmp->next = NULL;
				DHD_TRACE(("%s %d: Clean tdata_psh_info(end_seq %u)!\n",
					__FUNCTION__, __LINE__, tdata_psh_info_tmp->end_seq));
				_tdata_psh_info_pool_enq(tcpack_sup_mod, tdata_psh_info_tmp);
			}
#ifdef DHDTCPACK_SUP_DBG
			DHD_ERROR(("%s %d: PSH INFO ENQ %d\n",
				__FUNCTION__, __LINE__, tcpack_sup_mod->psh_info_enq_num));
#endif /* DHDTCPACK_SUP_DBG */
			tcpack_sup_mod->tcpdata_info_cnt--;
			ASSERT(tcpack_sup_mod->tcpdata_info_cnt >= 0);

			last_tdata_info =
				&tcpack_sup_mod->tcpdata_info_tbl[tcpack_sup_mod->tcpdata_info_cnt];
			if (i < tcpack_sup_mod->tcpdata_info_cnt) {
				ASSERT(last_tdata_info != tdata_info_tmp);
				bcopy(last_tdata_info, tdata_info_tmp, sizeof(tcpdata_info_t));
			}
			bzero(last_tdata_info, sizeof(tcpdata_info_t));
			DHD_TRACE(("%s %d: tcpdata_info(idx %d) is aged out. ttl cnt is now %d\n",
				__FUNCTION__, __LINE__, i, tcpack_sup_mod->tcpdata_info_cnt));
			/* Don't increase "i" here, so that the prev last tcpdata_info is checked */
		} else
			 i++;
	}

	tcp_seq_num = ntoh32_ua(&tcp_hdr[TCP_SEQ_NUM_OFFSET]);
	tcp_data_len = ip_total_len - ip_hdr_len - tcp_hdr_len;
	end_tcp_seq_num = tcp_seq_num + tcp_data_len;

	if (tcpdata_info == NULL) {
		ASSERT(i == tcpack_sup_mod->tcpdata_info_cnt);
		if (i >= TCPDATA_INFO_MAXNUM) {
			DHD_TRACE(("%s %d: tcp_data_info_tbl FULL! %d %d"
				" IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR" TCP port %d %d\n",
				__FUNCTION__, __LINE__, i, tcpack_sup_mod->tcpdata_info_cnt,
				IPV4_ADDR_TO_STR(ntoh32_ua(&ip_hdr[IPV4_SRC_IP_OFFSET])),
				IPV4_ADDR_TO_STR(ntoh32_ua(&ip_hdr[IPV4_DEST_IP_OFFSET])),
				ntoh16_ua(&tcp_hdr[TCP_SRC_PORT_OFFSET]),
				ntoh16_ua(&tcp_hdr[TCP_DEST_PORT_OFFSET])));
			dhd_os_tcpackunlock(dhdp);
			goto exit;
		}
		tcpdata_info = &tcpack_sup_mod->tcpdata_info_tbl[i];

		/* No TCP flow with the same IP addr and TCP port is found
		 * in tcp_data_info_tbl. So add this flow to the table.
		 */
		DHD_TRACE(("%s %d: Add data info to tbl[%d]: IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR
			" TCP port %d %d\n",
			__FUNCTION__, __LINE__, tcpack_sup_mod->tcpdata_info_cnt,
			IPV4_ADDR_TO_STR(ntoh32_ua(&ip_hdr[IPV4_SRC_IP_OFFSET])),
			IPV4_ADDR_TO_STR(ntoh32_ua(&ip_hdr[IPV4_DEST_IP_OFFSET])),
			ntoh16_ua(&tcp_hdr[TCP_SRC_PORT_OFFSET]),
			ntoh16_ua(&tcp_hdr[TCP_DEST_PORT_OFFSET])));

		bcopy(&ip_hdr[IPV4_SRC_IP_OFFSET], tcpdata_info->src_ip_addr,
			IPV4_ADDR_LEN * 2);
		bcopy(&tcp_hdr[TCP_SRC_PORT_OFFSET], tcpdata_info->src_tcp_port,
			TCP_PORT_LEN * 2);

		tcpdata_info->last_used_time = OSL_SYSUPTIME();
		tcpack_sup_mod->tcpdata_info_cnt++;
	}

	ASSERT(tcpdata_info != NULL);

	tdata_psh_info = _tdata_psh_info_pool_deq(tcpack_sup_mod);
#ifdef DHDTCPACK_SUP_DBG
	DHD_TRACE(("%s %d: PSH INFO ENQ %d\n",
		__FUNCTION__, __LINE__, tcpack_sup_mod->psh_info_enq_num));
#endif /* DHDTCPACK_SUP_DBG */

	if (tdata_psh_info == NULL) {
		DHD_ERROR(("%s %d: No more free tdata_psh_info!!\n", __FUNCTION__, __LINE__));
		ret = BCME_ERROR;
		dhd_os_tcpackunlock(dhdp);
		goto exit;
	}
	tdata_psh_info->end_seq = end_tcp_seq_num;

#if defined(DEBUG_COUNTER) && defined(DHDTCPACK_SUP_DBG)
	tack_tbl.cnt[4]++;
#endif /* DEBUG_COUNTER && DHDTCPACK_SUP_DBG */

	DHD_TRACE(("%s %d: TCP PSH DATA recvd! end seq %u\n",
		__FUNCTION__, __LINE__, tdata_psh_info->end_seq));

	ASSERT(tdata_psh_info->next == NULL);

	if (tcpdata_info->tdata_psh_info_head == NULL)
		tcpdata_info->tdata_psh_info_head = tdata_psh_info;
	else {
		ASSERT(tcpdata_info->tdata_psh_info_tail);
		tcpdata_info->tdata_psh_info_tail->next = tdata_psh_info;
	}
	tcpdata_info->tdata_psh_info_tail = tdata_psh_info;

	dhd_os_tcpackunlock(dhdp);

exit:
	return ret;
}
bool
dhd_tcpack_suppress(dhd_pub_t *dhdp, void *pkt)
{
	uint8 *new_ether_hdr;	/* Ethernet header of the new packet */
	uint16 new_ether_type;	/* Ethernet type of the new packet */
	uint8 *new_ip_hdr;		/* IP header of the new packet */
	uint8 *new_tcp_hdr;		/* TCP header of the new packet */
	uint32 new_ip_hdr_len;	/* IP header length of the new packet */
	uint32 cur_framelen;
	uint32 new_tcp_ack_num;		/* TCP acknowledge number of the new packet */
	uint16 new_ip_total_len;	/* Total length of IP packet for the new packet */
	uint32 new_tcp_hdr_len;		/* TCP header length of the new packet */
	tcpack_sup_module_t *tcpack_sup_mod;
	tcpack_info_t *tcpack_info_tbl;
	int i;
	bool ret = FALSE;
	bool set_dotxinrx = TRUE;

	if (dhdp->tcpack_sup_mode == TCPACK_SUP_OFF)
		goto exit;

	new_ether_hdr = PKTDATA(dhdp->osh, pkt);
	cur_framelen = PKTLEN(dhdp->osh, pkt);

	if (cur_framelen < TCPACKSZMIN || cur_framelen > TCPACKSZMAX) {
		DHD_TRACE(("%s %d: Too short or long length %d to be TCP ACK\n",
			__FUNCTION__, __LINE__, cur_framelen));
		goto exit;
	}

	new_ether_type = new_ether_hdr[12] << 8 | new_ether_hdr[13];

	if (new_ether_type != ETHER_TYPE_IP) {
		DHD_TRACE(("%s %d: Not a IP packet 0x%x\n",
			__FUNCTION__, __LINE__, new_ether_type));
		goto exit;
	}

	DHD_TRACE(("%s %d: IP pkt! 0x%x\n", __FUNCTION__, __LINE__, new_ether_type));

	new_ip_hdr = new_ether_hdr + ETHER_HDR_LEN;
	cur_framelen -= ETHER_HDR_LEN;

	ASSERT(cur_framelen >= IPV4_MIN_HEADER_LEN);

	new_ip_hdr_len = IPV4_HLEN(new_ip_hdr);
	if (IP_VER(new_ip_hdr) != IP_VER_4 || IPV4_PROT(new_ip_hdr) != IP_PROT_TCP) {
		DHD_TRACE(("%s %d: Not IPv4 nor TCP! ip ver %d, prot %d\n",
			__FUNCTION__, __LINE__, IP_VER(new_ip_hdr), IPV4_PROT(new_ip_hdr)));
		goto exit;
	}

	new_tcp_hdr = new_ip_hdr + new_ip_hdr_len;
	cur_framelen -= new_ip_hdr_len;

	ASSERT(cur_framelen >= TCP_MIN_HEADER_LEN);

	DHD_TRACE(("%s %d: TCP pkt!\n", __FUNCTION__, __LINE__));

	/* is it an ack ? Allow only ACK flag, not to suppress others. */
	if (new_tcp_hdr[TCP_FLAGS_OFFSET] != TCP_FLAG_ACK) {
		DHD_TRACE(("%s %d: Do not touch TCP flag 0x%x\n",
			__FUNCTION__, __LINE__, new_tcp_hdr[TCP_FLAGS_OFFSET]));
		goto exit;
	}

	new_ip_total_len = ntoh16_ua(&new_ip_hdr[IPV4_PKTLEN_OFFSET]);
	new_tcp_hdr_len = 4 * TCP_HDRLEN(new_tcp_hdr[TCP_HLEN_OFFSET]);

	/* This packet has TCP data, so just send */
	if (new_ip_total_len > new_ip_hdr_len + new_tcp_hdr_len) {
		DHD_TRACE(("%s %d: Do nothing for TCP DATA\n", __FUNCTION__, __LINE__));
		goto exit;
	}

	ASSERT(new_ip_total_len == new_ip_hdr_len + new_tcp_hdr_len);

	new_tcp_ack_num = ntoh32_ua(&new_tcp_hdr[TCP_ACK_NUM_OFFSET]);

	DHD_TRACE(("%s %d: TCP ACK with zero DATA length"
		" IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR" TCP port %d %d\n",
		__FUNCTION__, __LINE__,
		IPV4_ADDR_TO_STR(ntoh32_ua(&new_ip_hdr[IPV4_SRC_IP_OFFSET])),
		IPV4_ADDR_TO_STR(ntoh32_ua(&new_ip_hdr[IPV4_DEST_IP_OFFSET])),
		ntoh16_ua(&new_tcp_hdr[TCP_SRC_PORT_OFFSET]),
		ntoh16_ua(&new_tcp_hdr[TCP_DEST_PORT_OFFSET])));

	/* Look for tcp_ack_info that has the same ip src/dst addrs and tcp src/dst ports */
	dhd_os_tcpacklock(dhdp);
#if defined(DEBUG_COUNTER) && defined(DHDTCPACK_SUP_DBG)
	counter_printlog(&tack_tbl);
	tack_tbl.cnt[0]++;
#endif /* DEBUG_COUNTER && DHDTCPACK_SUP_DBG */

	tcpack_sup_mod = dhdp->tcpack_sup_module;
	tcpack_info_tbl = tcpack_sup_mod->tcpack_info_tbl;

	if (!tcpack_sup_mod) {
		DHD_ERROR(("%s %d: tcpack suppress module NULL!!\n", __FUNCTION__, __LINE__));
		ret = BCME_ERROR;
		dhd_os_tcpackunlock(dhdp);
		goto exit;
	}

	if (dhd_tcpdata_psh_acked(dhdp, new_ip_hdr, new_tcp_hdr, new_tcp_ack_num)) {
		/* This TCPACK is ACK to TCPDATA PSH pkt, so keep set_dotxinrx TRUE */
#if defined(DEBUG_COUNTER) && defined(DHDTCPACK_SUP_DBG)
		tack_tbl.cnt[5]++;
#endif /* DEBUG_COUNTER && DHDTCPACK_SUP_DBG */
	} else
		set_dotxinrx = FALSE;

	for (i = 0; i < tcpack_sup_mod->tcpack_info_cnt; i++) {
		void *oldpkt;	/* TCPACK packet that is already in txq or DelayQ */
		uint8 *old_ether_hdr, *old_ip_hdr, *old_tcp_hdr;
		uint32 old_ip_hdr_len, old_tcp_hdr_len;
		uint32 old_tcpack_num;	/* TCP ACK number of old TCPACK packet in Q */

		if ((oldpkt = tcpack_info_tbl[i].pkt_in_q) == NULL) {
			DHD_ERROR(("%s %d: Unexpected error!! cur idx %d, ttl cnt %d\n",
				__FUNCTION__, __LINE__, i, tcpack_sup_mod->tcpack_info_cnt));
			break;
		}

		if (PKTDATA(dhdp->osh, oldpkt) == NULL) {
			DHD_ERROR(("%s %d: oldpkt data NULL!! cur idx %d, ttl cnt %d\n",
				__FUNCTION__, __LINE__, i, tcpack_sup_mod->tcpack_info_cnt));
			break;
		}

		old_ether_hdr = tcpack_info_tbl[i].pkt_ether_hdr;
		old_ip_hdr = old_ether_hdr + ETHER_HDR_LEN;
		old_ip_hdr_len = IPV4_HLEN(old_ip_hdr);
		old_tcp_hdr = old_ip_hdr + old_ip_hdr_len;
		old_tcp_hdr_len = 4 * TCP_HDRLEN(old_tcp_hdr[TCP_HLEN_OFFSET]);

		DHD_TRACE(("%s %d: oldpkt %p[%d], IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR
			" TCP port %d %d\n", __FUNCTION__, __LINE__, oldpkt, i,
			IPV4_ADDR_TO_STR(ntoh32_ua(&old_ip_hdr[IPV4_SRC_IP_OFFSET])),
			IPV4_ADDR_TO_STR(ntoh32_ua(&old_ip_hdr[IPV4_DEST_IP_OFFSET])),
			ntoh16_ua(&old_tcp_hdr[TCP_SRC_PORT_OFFSET]),
			ntoh16_ua(&old_tcp_hdr[TCP_DEST_PORT_OFFSET])));

		/* If either of IP address or TCP port number does not match, skip. */
		if (memcmp(&new_ip_hdr[IPV4_SRC_IP_OFFSET],
			&old_ip_hdr[IPV4_SRC_IP_OFFSET], IPV4_ADDR_LEN * 2) ||
			memcmp(&new_tcp_hdr[TCP_SRC_PORT_OFFSET],
			&old_tcp_hdr[TCP_SRC_PORT_OFFSET], TCP_PORT_LEN * 2))
			continue;

		old_tcpack_num = ntoh32_ua(&old_tcp_hdr[TCP_ACK_NUM_OFFSET]);

		if (IS_TCPSEQ_GT(new_tcp_ack_num, old_tcpack_num)) {
			/* New packet has higher TCP ACK number, so it replaces the old packet */
			if (new_ip_hdr_len == old_ip_hdr_len &&
				new_tcp_hdr_len == old_tcp_hdr_len) {
				ASSERT(memcmp(new_ether_hdr, old_ether_hdr, ETHER_HDR_LEN) == 0);
				bcopy(new_ip_hdr, old_ip_hdr, new_ip_total_len);
				PKTFREE(dhdp->osh, pkt, FALSE);
				DHD_TRACE(("%s %d: TCP ACK replace %u -> %u\n",
					__FUNCTION__, __LINE__, old_tcpack_num, new_tcp_ack_num));
#if defined(DEBUG_COUNTER) && defined(DHDTCPACK_SUP_DBG)
				tack_tbl.cnt[2]++;
#endif /* DEBUG_COUNTER && DHDTCPACK_SUP_DBG */
				ret = TRUE;
			} else {
#if defined(DEBUG_COUNTER) && defined(DHDTCPACK_SUP_DBG)
				tack_tbl.cnt[6]++;
#endif /* DEBUG_COUNTER && DHDTCPACK_SUP_DBG */
				DHD_TRACE(("%s %d: lenth mismatch %d != %d || %d != %d"
					" ACK %u -> %u\n", __FUNCTION__, __LINE__,
					new_ip_hdr_len, old_ip_hdr_len,
					new_tcp_hdr_len, old_tcp_hdr_len,
					old_tcpack_num, new_tcp_ack_num));
			}
		} else if (new_tcp_ack_num == old_tcpack_num) {
			set_dotxinrx = TRUE;
			/* TCPACK retransmission */
#if defined(DEBUG_COUNTER) && defined(DHDTCPACK_SUP_DBG)
			tack_tbl.cnt[3]++;
#endif /* DEBUG_COUNTER && DHDTCPACK_SUP_DBG */
		} else {
			DHD_TRACE(("%s %d: ACK number reverse old %u(0x%p) new %u(0x%p)\n",
				__FUNCTION__, __LINE__, old_tcpack_num, oldpkt,
				new_tcp_ack_num, pkt));
		}
		dhd_os_tcpackunlock(dhdp);
		goto exit;
	}

	if (i == tcpack_sup_mod->tcpack_info_cnt && i < TCPACK_INFO_MAXNUM) {
		/* No TCPACK packet with the same IP addr and TCP port is found
		 * in tcp_ack_info_tbl. So add this packet to the table.
		 */
		DHD_TRACE(("%s %d: Add pkt 0x%p(ether_hdr 0x%p) to tbl[%d]\n",
			__FUNCTION__, __LINE__, pkt, new_ether_hdr,
			tcpack_sup_mod->tcpack_info_cnt));

		tcpack_info_tbl[tcpack_sup_mod->tcpack_info_cnt].pkt_in_q = pkt;
		tcpack_info_tbl[tcpack_sup_mod->tcpack_info_cnt].pkt_ether_hdr = new_ether_hdr;
		tcpack_sup_mod->tcpack_info_cnt++;
#if defined(DEBUG_COUNTER) && defined(DHDTCPACK_SUP_DBG)
		tack_tbl.cnt[1]++;
#endif /* DEBUG_COUNTER && DHDTCPACK_SUP_DBG */
	} else {
		ASSERT(i == tcpack_sup_mod->tcpack_info_cnt);
		DHD_TRACE(("%s %d: No empty tcp ack info tbl\n",
			__FUNCTION__, __LINE__));
	}
	dhd_os_tcpackunlock(dhdp);

exit:
	/* Unless TCPACK_SUP_DELAYTX, dotxinrx is alwasy TRUE, so no need to set here */
	if (dhdp->tcpack_sup_mode == TCPACK_SUP_DELAYTX && set_dotxinrx)
		dhd_bus_set_dotxinrx(dhdp->bus, TRUE);

	return ret;
}
static INLINE bool dhd_tcpdata_psh_acked(dhd_pub_t *dhdp, uint8 *ip_hdr,
	uint8 *tcp_hdr, uint32 tcp_ack_num)
{
	tcpack_sup_module_t *tcpack_sup_mod;
	int i;
	tcpdata_info_t *tcpdata_info = NULL;
	tdata_psh_info_t *tdata_psh_info = NULL;
	bool ret = FALSE;

	if (dhdp->tcpack_sup_mode != TCPACK_SUP_DELAYTX)
		goto exit;

	tcpack_sup_mod = dhdp->tcpack_sup_module;

	if (!tcpack_sup_mod) {
		DHD_ERROR(("%s %d: tcpack suppress module NULL!!\n", __FUNCTION__, __LINE__));
		goto exit;
	}

	DHD_TRACE(("%s %d: IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR
		" TCP port %d %d, ack %u\n", __FUNCTION__, __LINE__,
		IPV4_ADDR_TO_STR(ntoh32_ua(&ip_hdr[IPV4_SRC_IP_OFFSET])),
		IPV4_ADDR_TO_STR(ntoh32_ua(&ip_hdr[IPV4_DEST_IP_OFFSET])),
		ntoh16_ua(&tcp_hdr[TCP_SRC_PORT_OFFSET]),
		ntoh16_ua(&tcp_hdr[TCP_DEST_PORT_OFFSET]),
		tcp_ack_num));

	for (i = 0; i < tcpack_sup_mod->tcpdata_info_cnt; i++) {
		tcpdata_info_t *tcpdata_info_tmp = &tcpack_sup_mod->tcpdata_info_tbl[i];
		DHD_TRACE(("%s %d: data info[%d], IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR
			" TCP port %d %d\n", __FUNCTION__, __LINE__, i,
			IPV4_ADDR_TO_STR(ntoh32_ua(tcpdata_info_tmp->src_ip_addr)),
			IPV4_ADDR_TO_STR(ntoh32_ua(tcpdata_info_tmp->dst_ip_addr)),
			ntoh16_ua(tcpdata_info_tmp->src_tcp_port),
			ntoh16_ua(tcpdata_info_tmp->dst_tcp_port)));

		/* If either IP address or TCP port number does not match, skip. */
		if (memcmp(&ip_hdr[IPV4_SRC_IP_OFFSET],
			tcpdata_info_tmp->dst_ip_addr, IPV4_ADDR_LEN) == 0 &&
			memcmp(&ip_hdr[IPV4_DEST_IP_OFFSET],
			tcpdata_info_tmp->src_ip_addr, IPV4_ADDR_LEN) == 0 &&
			memcmp(&tcp_hdr[TCP_SRC_PORT_OFFSET],
			tcpdata_info_tmp->dst_tcp_port, TCP_PORT_LEN) == 0 &&
			memcmp(&tcp_hdr[TCP_DEST_PORT_OFFSET],
			tcpdata_info_tmp->src_tcp_port, TCP_PORT_LEN) == 0) {
			tcpdata_info = tcpdata_info_tmp;
			break;
		}
	}

	if (tcpdata_info == NULL) {
		DHD_TRACE(("%s %d: no tcpdata_info!\n", __FUNCTION__, __LINE__));
		goto exit;
	}

	if (tcpdata_info->tdata_psh_info_head == NULL) {
		DHD_TRACE(("%s %d: No PSH DATA to be acked!\n", __FUNCTION__, __LINE__));
	}

	while ((tdata_psh_info = tcpdata_info->tdata_psh_info_head)) {
		if (IS_TCPSEQ_GE(tcp_ack_num, tdata_psh_info->end_seq)) {
			DHD_TRACE(("%s %d: PSH ACKED! %u >= %u\n",
				__FUNCTION__, __LINE__, tcp_ack_num, tdata_psh_info->end_seq));
			tcpdata_info->tdata_psh_info_head = tdata_psh_info->next;
			tdata_psh_info->next = NULL;
			_tdata_psh_info_pool_enq(tcpack_sup_mod, tdata_psh_info);
			ret = TRUE;
		} else
			break;
	}
	if (tdata_psh_info == NULL)
		tcpdata_info->tdata_psh_info_tail = NULL;

#ifdef DHDTCPACK_SUP_DBG
	DHD_TRACE(("%s %d: PSH INFO ENQ %d\n",
		__FUNCTION__, __LINE__, tcpack_sup_mod->psh_info_enq_num));
#endif /* DHDTCPACK_SUP_DBG */

exit:
	return ret;
}