#include"aicwf_tcp_ack.h"
//#include"rwnx_tx.h"
//#include "aicwf_tcp_ack.h"
#include"rwnx_defs.h"
extern int intf_tx(struct rwnx_hw *priv,struct msg_buf *msg);
struct msg_buf *intf_tcp_alloc_msg(struct msg_buf *msg)
{
	//printk("%s \n",__func__);
	int len=sizeof(struct msg_buf) ;
	msg = kzalloc(len , GFP_ATOMIC);
	if(!msg)
		printk("%s: alloc failed \n", __func__);
	memset(msg,0,len);
	return msg;
}
						
void intf_tcp_drop_msg(struct rwnx_hw *priv,
					    struct msg_buf *msg)
{
	//printk("%s \n",__func__);
	if (msg->skb)
		dev_kfree_skb_any(msg->skb);

	kfree(msg);
}

#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) 
void tcp_ack_timeout(unsigned long data)
#else
void tcp_ack_timeout(struct timer_list *t)
#endif
{
	//printk("%s \n",__func__);
	struct tcp_ack_info *ack_info;
	struct msg_buf *msg;
	struct tcp_ack_manage *ack_m = NULL;

#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) 
	ack_info = (struct tcp_ack_info *)data;
#else
	ack_info = container_of(t,struct tcp_ack_info,timer);
#endif

	ack_m = container_of(ack_info, struct tcp_ack_manage,
			     ack_info[ack_info->ack_info_num]);

	write_seqlock_bh(&ack_info->seqlock);
	msg = ack_info->msgbuf;
	if (ack_info->busy && msg && !ack_info->in_send_msg) {
		ack_info->msgbuf = NULL;
		ack_info->drop_cnt = 0;
		ack_info->in_send_msg = msg;
		write_sequnlock_bh(&ack_info->seqlock);
		intf_tx(ack_m->priv, msg);//send skb
		//ack_info->in_send_msg = NULL;//add by dwx
		//write_sequnlock_bh(&ack_info->seqlock);
		//intf_tx(ack_m->priv, msg);
		return;
	}
	write_sequnlock_bh(&ack_info->seqlock);
}

void tcp_ack_init(struct rwnx_hw *priv)
{
	int i;
	struct tcp_ack_info *ack_info;
	struct tcp_ack_manage *ack_m = &priv->ack_m;

	printk("========== tcp ack debug %s \n",__func__);
	memset(ack_m, 0, sizeof(struct tcp_ack_manage));
	ack_m->priv = priv;
	spin_lock_init(&ack_m->lock);
	atomic_set(&ack_m->max_drop_cnt, TCP_ACK_DROP_CNT);
	ack_m->last_time = jiffies;
	ack_m->timeout = msecs_to_jiffies(ACK_OLD_TIME);

	for (i = 0; i < TCP_ACK_NUM; i++) {
		ack_info = &ack_m->ack_info[i];
		ack_info->ack_info_num = i;
		seqlock_init(&ack_info->seqlock);
		ack_info->last_time = jiffies;
		ack_info->timeout = msecs_to_jiffies(ACK_OLD_TIME);

		#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) 
			setup_timer(&ack_info->timer, tcp_ack_timeout,
				    (unsigned long)ack_info);
		#else
			timer_setup(&ack_info->timer,tcp_ack_timeout,0);
		#endif
	}

	atomic_set(&ack_m->enable, 1);
	ack_m->ack_winsize = MIN_WIN;
}

void tcp_ack_deinit(struct rwnx_hw *priv)
{
	int i;
	struct tcp_ack_manage *ack_m = &priv->ack_m;
	struct msg_buf *drop_msg = NULL;

	printk("%s \n",__func__);
	atomic_set(&ack_m->enable, 0);

	for (i = 0; i < TCP_ACK_NUM; i++) {
		drop_msg = NULL;

		write_seqlock_bh(&ack_m->ack_info[i].seqlock);
		rwnx_timer_delete(&ack_m->ack_info[i].timer);
		drop_msg = ack_m->ack_info[i].msgbuf;
		ack_m->ack_info[i].msgbuf = NULL;
		write_sequnlock_bh(&ack_m->ack_info[i].seqlock);

		if (drop_msg)
			intf_tcp_drop_msg(priv, drop_msg);//drop skb
	}
}

int tcp_check_quick_ack(unsigned char *buf,
				      struct tcp_ack_msg *msg)
{
	int ip_hdr_len;
	unsigned char *temp;
	struct ethhdr *ethhdr;
	struct iphdr *iphdr;
	struct tcphdr *tcphdr;

	ethhdr = (struct ethhdr *)buf;
	if (ethhdr->h_proto != htons(ETH_P_IP))
		return 0;
	iphdr = (struct iphdr *)(ethhdr + 1);
	if (iphdr->version != 4 || iphdr->protocol != IPPROTO_TCP)
		return 0;
	ip_hdr_len = iphdr->ihl * 4;
	temp = (unsigned char *)(iphdr) + ip_hdr_len;
	tcphdr = (struct tcphdr *)temp;
	/* TCP_FLAG_ACK */
	if (!(temp[13] & 0x10))
		return 0;

	if (temp[13] & 0x8) {
		msg->saddr = iphdr->daddr;
		msg->daddr = iphdr->saddr;
		msg->source = tcphdr->dest;
		msg->dest = tcphdr->source;
		msg->seq = ntohl(tcphdr->seq);
		return 1;
	}

	return 0;
}

int is_drop_tcp_ack(struct tcphdr *tcphdr, int tcp_tot_len,
				unsigned short *win_scale)
{
	//printk("%s \n",__func__);
	int drop = 1;
	int len = tcphdr->doff * 4;
	unsigned char *ptr;

	if(tcp_tot_len > len) {
		drop = 0;
	} else {
		len -= sizeof(struct tcphdr);
		ptr = (unsigned char *)(tcphdr + 1);

		while ((len > 0) && drop) {
			int opcode = *ptr++;
			int opsize;

			switch (opcode) {
			case TCPOPT_EOL:
				break;
			case TCPOPT_NOP:
				len--;
				continue;
			default:
				opsize = *ptr++;
				if (opsize < 2)
					break;
				if (opsize > len)
					break;

				switch (opcode) {
				/* TODO: Add other ignore opt */
				case TCPOPT_TIMESTAMP:
					break;
				case TCPOPT_WINDOW:
					if (*ptr < 15)
						*win_scale = (1 << (*ptr));
					//printk("%d\n",*win_scale);
					break;
				default:
					drop = 2;
				}

				ptr += opsize - 2;
				len -= opsize;
			}
		}
	}

	return drop;
}


/* flag:0 for not tcp ack
 *	1 for ack which can be drop
 *	2 for other ack whith more info
 */

int tcp_check_ack(unsigned char *buf,
				struct tcp_ack_msg *msg,
				unsigned short *win_scale)
{
	int ret;
	int ip_hdr_len;
	int tcp_tot_len;
	unsigned char *temp;
	struct ethhdr *ethhdr;
	struct iphdr *iphdr;
	struct tcphdr *tcphdr;

	ethhdr =(struct ethhdr *)buf;
	if (ethhdr->h_proto != htons(ETH_P_IP))
		return 0;

	iphdr = (struct iphdr *)(ethhdr + 1);
	if (iphdr->version != 4 || iphdr->protocol != IPPROTO_TCP)
		return 0;

	ip_hdr_len = iphdr->ihl * 4;
	temp = (unsigned char *)(iphdr) + ip_hdr_len;
	tcphdr = (struct tcphdr *)temp;
	/* TCP_FLAG_ACK */
	if (!(temp[13] & 0x10))
		return 0;

	tcp_tot_len = ntohs(iphdr->tot_len) - ip_hdr_len;// tcp total len
	ret = is_drop_tcp_ack(tcphdr, tcp_tot_len, win_scale);
	//printk("is drop:%d \n",ret);

	if (ret > 0) {
		msg->saddr = iphdr->saddr;
		msg->daddr = iphdr->daddr;
		msg->source = tcphdr->source;
		msg->dest = tcphdr->dest;
		msg->seq = ntohl(tcphdr->ack_seq);
		msg->win = ntohs(tcphdr->window);
	}
	
	return ret;
}

/* return val: -1 for not match, others for match */
int tcp_ack_match(struct tcp_ack_manage *ack_m,
				struct tcp_ack_msg *ack_msg)
{
	int i, ret = -1;
	unsigned start;
	struct tcp_ack_info *ack_info;
	struct tcp_ack_msg *ack;

	for (i = 0; ((ret < 0) && (i < TCP_ACK_NUM)); i++) {
		ack_info = &ack_m->ack_info[i];
		do {
			start = read_seqbegin(&ack_info->seqlock);
			ret = -1;

			ack = &ack_info->ack_msg;
			if (ack_info->busy &&
			    ack->dest == ack_msg->dest &&
			    ack->source == ack_msg->source &&
			    ack->saddr == ack_msg->saddr &&
			    ack->daddr == ack_msg->daddr)
				ret = i;
		} while(read_seqretry(&ack_info->seqlock, start));
	}

	return ret;
}


void tcp_ack_update(struct tcp_ack_manage *ack_m)
{
	int i;
	struct tcp_ack_info *ack_info;

	if (time_after(jiffies, ack_m->last_time + ack_m->timeout)) {
		spin_lock_bh(&ack_m->lock);
		ack_m->last_time = jiffies;
		for (i = TCP_ACK_NUM - 1; i >= 0; i--) {
			ack_info = &ack_m->ack_info[i];
			write_seqlock_bh(&ack_info->seqlock);
			if (ack_info->busy &&
			    time_after(jiffies, ack_info->last_time +
				       ack_info->timeout)) {
				ack_m->free_index = i;
				ack_m->max_num--;
				ack_info->busy = 0;
			}
			write_sequnlock_bh(&ack_info->seqlock);
		}
		spin_unlock_bh(&ack_m->lock);
	}
}

/* return val: -1 for no index, others for index */
int tcp_ack_alloc_index(struct tcp_ack_manage *ack_m)
{
	int i, ret = -1;
	struct tcp_ack_info *ack_info;
	unsigned start;

	spin_lock_bh(&ack_m->lock);
	if (ack_m->max_num == TCP_ACK_NUM) {
		spin_unlock_bh(&ack_m->lock);
		return -1;
	}

	if (ack_m->free_index >= 0) {
		i = ack_m->free_index;
		ack_m->free_index = -1;
		ack_m->max_num++;
		spin_unlock_bh(&ack_m->lock);
		return i;
	}

	for (i = 0; ((ret < 0) && (i < TCP_ACK_NUM)); i++) {
		ack_info = &ack_m->ack_info[i];
		do {
			start = read_seqbegin(&ack_info->seqlock);
			ret = -1;
			if (!ack_info->busy) {
				ack_m->free_index = -1;
				ack_m->max_num++;
				ret = i;
			}
		} while(read_seqretry(&ack_info->seqlock, start));
	}
	spin_unlock_bh(&ack_m->lock);

	return ret;
}


/* return val: 0 for not handle tx, 1 for handle tx */
int tcp_ack_handle(struct msg_buf *new_msgbuf,
			  struct tcp_ack_manage *ack_m,
			  struct tcp_ack_info *ack_info,
			  struct tcp_ack_msg *ack_msg,
			  int type)
{
	int quick_ack = 0;
	struct tcp_ack_msg *ack;
	int ret = 0;
	struct msg_buf *drop_msg = NULL;

	//printk("%s %d",__func__,type);
	write_seqlock_bh(&ack_info->seqlock);

	ack_info->last_time = jiffies;
	ack = &ack_info->ack_msg;

	if (type == 2) {
		if (U32_BEFORE(ack->seq, ack_msg->seq)) {
			ack->seq = ack_msg->seq;
			if (ack_info->psh_flag &&
			    !U32_BEFORE(ack_msg->seq,
					       ack_info->psh_seq)) {
				ack_info->psh_flag = 0;
			}

			if (ack_info->msgbuf) {
				//printk("%lx \n",ack_info->msgbuf);
				drop_msg = ack_info->msgbuf;
				ack_info->msgbuf = NULL;
				rwnx_timer_delete(&ack_info->timer);
			}else{
				//printk("msgbuf is NULL \n");
			}

			ack_info->in_send_msg = NULL;
			ack_info->drop_cnt = atomic_read(&ack_m->max_drop_cnt);
		} else {
			printk("%s before abnormal ack: %d, %d\n",
			       __func__, ack->seq, ack_msg->seq);
			drop_msg = new_msgbuf;
			ret = 1;
		}
	} else if (U32_BEFORE(ack->seq, ack_msg->seq)) {
		if (ack_info->msgbuf) {
			drop_msg = ack_info->msgbuf;
			ack_info->msgbuf = NULL;
		}

		if (ack_info->psh_flag &&
		    !U32_BEFORE(ack_msg->seq, ack_info->psh_seq)) {
			ack_info->psh_flag = 0;
			quick_ack = 1;
		} else {
			ack_info->drop_cnt++;
		}

		ack->seq = ack_msg->seq;

		if (quick_ack || (!ack_info->in_send_msg &&
				  (ack_info->drop_cnt >=
				   atomic_read(&ack_m->max_drop_cnt)))) {
			ack_info->drop_cnt = 0;
			ack_info->in_send_msg = new_msgbuf;
			rwnx_timer_delete(&ack_info->timer);
		} else {
			ret = 1;
			ack_info->msgbuf = new_msgbuf;
			if (!timer_pending(&ack_info->timer))
				mod_timer(&ack_info->timer,
					  (jiffies + msecs_to_jiffies(5)));
		}
	} else {
		printk("%s before ack: %d, %d\n",
		       __func__, ack->seq, ack_msg->seq);
		drop_msg = new_msgbuf;
		ret = 1;
	}

	write_sequnlock_bh(&ack_info->seqlock);

	if (drop_msg)
		intf_tcp_drop_msg(ack_m->priv, drop_msg);// drop skb

	return ret;
}

int tcp_ack_handle_new(struct msg_buf *new_msgbuf,
			  struct tcp_ack_manage *ack_m,
			  struct tcp_ack_info *ack_info,
			  struct tcp_ack_msg *ack_msg,
			  int type)
{
	int quick_ack = 0;
	struct tcp_ack_msg *ack;
	int ret = 0;
	struct msg_buf *drop_msg = NULL;
	//struct msg_buf * send_msg = NULL;
	//printk("",);
	write_seqlock_bh(&ack_info->seqlock);

        ack_info->last_time = jiffies;
        ack = &ack_info->ack_msg;

	if(U32_BEFORE(ack->seq, ack_msg->seq)){
		if (ack_info->msgbuf) {
			drop_msg = ack_info->msgbuf;
			ack_info->msgbuf = NULL;
			//ack_info->drop_cnt++;
		}

		if (ack_info->psh_flag &&
		    !U32_BEFORE(ack_msg->seq, ack_info->psh_seq)) {
			ack_info->psh_flag = 0;
			quick_ack = 1;
		} else {
			ack_info->drop_cnt++;
		}

		ack->seq = ack_msg->seq;

		if(quick_ack || (!ack_info->in_send_msg &&
				  (ack_info->drop_cnt >=
				   atomic_read(&ack_m->max_drop_cnt)))){
			ack_info->drop_cnt = 0;
			//send_msg = new_msgbuf;
			ack_info->in_send_msg = new_msgbuf;
			rwnx_timer_delete(&ack_info->timer);
		}else{
			ret = 1;
			ack_info->msgbuf = new_msgbuf;
			if (!timer_pending(&ack_info->timer))
				mod_timer(&ack_info->timer,
					  (jiffies + msecs_to_jiffies(5)));
		}
		
		//ret = 1;
	}else {
		printk("%s before ack: %d, %d\n",
		       __func__, ack->seq, ack_msg->seq);
		drop_msg = new_msgbuf;
		ret = 1;
	}

	/*if(send_msg){
		intf_tx(ack_m->priv,send_msg);
		ack_info->in_send_msg=NULL;
	}*/

	//ack_info->in_send_msg=NULL;
	
	write_sequnlock_bh(&ack_info->seqlock);

    	/*if(send_msg){
            intf_tx(ack_m->priv,send_msg);
            //ack_info->in_send_msg=NULL;
    	}*/

	if (drop_msg)
		intf_tcp_drop_msg(ack_m->priv, drop_msg);// drop skb

	return ret;

}

void filter_rx_tcp_ack(struct rwnx_hw *priv,
			      unsigned char *buf, unsigned plen)
{
	int index;
	struct tcp_ack_msg ack_msg;
	struct tcp_ack_info *ack_info;
	struct tcp_ack_manage *ack_m = &priv->ack_m;

	if (!atomic_read(&ack_m->enable))
		return;

	if ((plen > MAX_TCP_ACK) ||
	    !tcp_check_quick_ack(buf, &ack_msg))
		return;

	index = tcp_ack_match(ack_m, &ack_msg);
	if (index >= 0) {
		ack_info = ack_m->ack_info + index;
		write_seqlock_bh(&ack_info->seqlock);
		ack_info->psh_flag = 1;
		ack_info->psh_seq = ack_msg.seq;
		write_sequnlock_bh(&ack_info->seqlock);
	}
}

/* return val: 0 for not filter, 1 for filter */
int filter_send_tcp_ack(struct rwnx_hw *priv,
			       struct msg_buf *msgbuf,
			       unsigned char *buf, unsigned int plen)
{
//	printk("%s \n",__func__);
	int ret = 0;
	int index, drop;
	unsigned short win_scale = 0;
	unsigned int win = 0;
	struct tcp_ack_msg ack_msg;
	struct tcp_ack_msg *ack;
	struct tcp_ack_info *ack_info;
	struct tcp_ack_manage *ack_m = &priv->ack_m;

	tcp_ack_update(ack_m);
	drop = tcp_check_ack(buf, &ack_msg, &win_scale);
//	printk("drop:%d win_scale:%d",drop,win_scale);
	if (!drop && (0 == win_scale))
		return 0;

	index = tcp_ack_match(ack_m, &ack_msg);
	if (index >= 0) {
		ack_info = ack_m->ack_info + index;
		if ((0 != win_scale) &&
			(ack_info->win_scale != win_scale)) {
			write_seqlock_bh(&ack_info->seqlock);
			ack_info->win_scale = win_scale;
			write_sequnlock_bh(&ack_info->seqlock);
		}

		if (drop > 0 && atomic_read(&ack_m->enable)) {
			win = ack_info->win_scale * ack_msg.win;
			if ((win_scale!=0) && (win < (ack_m->ack_winsize * SIZE_KB)))
			{	
				drop = 2;
				printk("%d %d %d",win_scale,win,(ack_m->ack_winsize * SIZE_KB));
			}
			ret = tcp_ack_handle_new(msgbuf, ack_m, ack_info,
						&ack_msg, drop);
		}

		goto out;
	}

	index = tcp_ack_alloc_index(ack_m);
	if (index >= 0) {
		write_seqlock_bh(&ack_m->ack_info[index].seqlock);
		ack_m->ack_info[index].busy = 1;
		ack_m->ack_info[index].psh_flag = 0;
		ack_m->ack_info[index].last_time = jiffies;
		ack_m->ack_info[index].drop_cnt =
			atomic_read(&ack_m->max_drop_cnt);
		ack_m->ack_info[index].win_scale =
			(win_scale != 0) ? win_scale : 1;
		
		//ack_m->ack_info[index].msgbuf = NULL;
		//ack_m->ack_info[index].in_send_msg = NULL;
		ack = &ack_m->ack_info[index].ack_msg;
		ack->dest = ack_msg.dest;
		ack->source = ack_msg.source;
		ack->saddr = ack_msg.saddr;
		ack->daddr = ack_msg.daddr;
		ack->seq = ack_msg.seq;
		write_sequnlock_bh(&ack_m->ack_info[index].seqlock);
	}

out:
	return ret;
}

void move_tcpack_msg(struct rwnx_hw *priv,
			    struct msg_buf *msg)
{
	struct tcp_ack_info *ack_info;
	struct tcp_ack_manage *ack_m = &priv->ack_m;
	int i = 0;

	if (!atomic_read(&ack_m->enable))
		return;

	//if (msg->len > MAX_TCP_ACK)
	//	return;

	for (i = 0; i < TCP_ACK_NUM; i++) {
		ack_info = &ack_m->ack_info[i];
		write_seqlock_bh(&ack_info->seqlock);
		if (ack_info->busy && (ack_info->in_send_msg == msg))
			ack_info->in_send_msg = NULL;
		write_sequnlock_bh(&ack_info->seqlock);
	}
}

