[Feature][ZXW-237]merge P54U03 version

Only Configure: No
Affected branch: master
Affected module: unknow
Is it affected on both ZXIC and MTK: only ZXIC
Self-test: Yes
Doc Update: No

Change-Id: Id39ef8b992af691eab09c01d4ea26da89e5f4049
diff --git a/ap/os/linux/linux-3.4.x/drivers/cpko/cpko_main.c b/ap/os/linux/linux-3.4.x/drivers/cpko/cpko_main.c
index 5a45243..7a9c22c 100755
--- a/ap/os/linux/linux-3.4.x/drivers/cpko/cpko_main.c
+++ b/ap/os/linux/linux-3.4.x/drivers/cpko/cpko_main.c
@@ -83,30 +83,30 @@
 unsigned int __comm_modem_text_start;unsigned int modem_text_end;unsigned int 
 cpko_data_start;unsigned int cpko_bss_start;unsigned int cpko_text_offset;}
 cpko_section_layout;cpko_section_layout cpko_ps_section;int raise(int signo){
-return(0x316+6712-0x1d4e);}extern unsigned int SysEntry(void);static int 
+return(0x404+8970-0x270e);}extern unsigned int SysEntry(void);static int 
 ko_Main_Thread(void*data){struct sched_param param={.sched_priority=
-MAX_USER_RT_PRIO/(0xe6b+2120-0x16b1)-(0x1975+1578-0x1f9c)};int ret=
-(0x3a0+6131-0x1b93);sched_setscheduler(current,SCHED_FIFO,&param);ret=SysEntry()
-;if(ret!=(0x1419+3950-0x2387))panic("Main_Thread\n");param.sched_priority=
-MAX_USER_RT_PRIO-(0x10ed+82-0x1111);sched_setscheduler(kthreadd_task,SCHED_FIFO,
-&param);return(0x227+8075-0x21b2);}int zte_modem_ko_start(void){kthread_run(
+MAX_USER_RT_PRIO/(0xafb+4511-0x1c98)-(0x1633+3815-0x2517)};int ret=
+(0x1f43+402-0x20d5);sched_setscheduler(current,SCHED_FIFO,&param);ret=SysEntry()
+;if(ret!=(0x10b8+3966-0x2036))panic("Main_Thread\n");param.sched_priority=
+MAX_USER_RT_PRIO-(0xfb3+2852-0x1aa9);sched_setscheduler(kthreadd_task,SCHED_FIFO
+,&param);return(0xaa6+6401-0x23a7);}int zte_modem_ko_start(void){kthread_run(
 ko_Main_Thread,NULL,"\x5a\x54\x45\x4d\x61\x69\x6e\x54\x68\x72\x65\x61\x64");
-return(0x890+7748-0x26d4);}static void cpko_sectioninfo_set(void){int ret;struct
- file*fp;mm_segment_t old_fs;loff_t cpko_pos=(0x1570+4436-0x26c4);struct 
+return(0x8b9+1955-0x105c);}static void cpko_sectioninfo_set(void){int ret;struct
+ file*fp;mm_segment_t old_fs;loff_t cpko_pos=(0x6ed+5503-0x1c6c);struct 
 cpps_globalModem globalVar;fp=filp_open(
 "\x2f\x6c\x69\x62\x2f\x63\x70\x6b\x6f\x2f\x63\x70\x6b\x6f\x5f\x73\x65\x63\x69\x6e\x66\x6f\x2e\x62\x69\x6e"
-,(0x294+1512-0x87c),(0xe06+3271-0x1acd));if(IS_ERR(fp)||fp==NULL)panic(
+,(0xf28+2571-0x1933),(0xa59+1171-0xeec));if(IS_ERR(fp)||fp==NULL)panic(
 "\x6f\x70\x65\x6e\x20\x66\x69\x6c\x65\x20\x65\x72\x72\x6f\x72" "\n");old_fs=
 get_fs();set_fs(KERNEL_DS);ret=vfs_read(fp,(char*)&cpko_ps_section,sizeof(
-cpko_section_layout),&cpko_pos);if(ret<=(0x3ca+7257-0x2023))panic(
+cpko_section_layout),&cpko_pos);if(ret<=(0x304+3756-0x11b0))panic(
 "\x72\x65\x61\x64\x20\x66\x69\x6c\x65\x20\x65\x72\x72\x6f\x72" "\n");filp_close(
 fp,NULL);
 #ifdef CONFIG_MODEM_CODE_IS_MAPPING
 fp=filp_open(
 "\x2f\x6c\x69\x62\x2f\x63\x70\x6b\x6f\x2f\x63\x70\x6b\x6f\x2e\x6b\x6f",
-(0x9ca+4617-0x1bd3),(0x659+7219-0x228c));if(IS_ERR(fp)||fp==NULL)panic(
+(0x1019+4563-0x21ec),(0x1cf2+1837-0x241f));if(IS_ERR(fp)||fp==NULL)panic(
 "\x6f\x70\x65\x6e\x20\x66\x69\x6c\x65\x20\x65\x72\x72\x6f\x72" "\n");fp->f_ra.
-ra_pages=(0xac6+6080-0x2286);
+ra_pages=(0x53f+4485-0x16c4);
 #endif
 if(cpko_ps_section.cpko_text_start){globalVar.cpko_text_start=(unsigned long)
 cpko_ps_section.cpko_text_start;globalVar.cpko_rodata_start=(unsigned long)
@@ -126,7 +126,7 @@
 vfree_modem_section(globalVar.cpko_text_start,globalVar.modem_text_end);
 #endif
 }else panic("\x66\x69\x6c\x65\x20\x65\x72\x72\x6f\x72" "\n");}static int 
-cpko_start(void){struct cpps_callbacks callback={(0x5b7+4236-0x1643)};callback.
+cpko_start(void){struct cpps_callbacks callback={(0x913+4588-0x1aff)};callback.
 zOss_ResetNVFactory=zOss_ResetNVFactory;callback.zOss_NvramFlush=zOss_NvramFlush
 ;callback.zOss_NvItemWrite=zOss_NvItemWrite;callback.zOss_NvItemWriteFactory=
 zOss_NvItemWriteFactory;callback.zOss_NvItemRead=zOss_NvItemRead;callback.
@@ -198,5 +198,5 @@
 psm_GetModemSleepFlagStatus=psm_GetModemSleepFlagStatus;
 #endif
 cpps_callbacks_register(&callback);cpko_sectioninfo_set();zte_modem_ko_start();
-return(0x1997+168-0x1a3f);}static int cpko_stop(void){return(0x636+5784-0x1cce);
+return(0xb43+4520-0x1ceb);}static int cpko_stop(void){return(0x20cd+460-0x2299);
 }module_init(cpko_start);module_exit(cpko_stop);
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/psnet/psnet_dev.c b/ap/os/linux/linux-3.4.x/drivers/net/psnet/psnet_dev.c
index 5df877d..09a1b2b 100755
--- a/ap/os/linux/linux-3.4.x/drivers/net/psnet/psnet_dev.c
+++ b/ap/os/linux/linux-3.4.x/drivers/net/psnet/psnet_dev.c
@@ -6,6 +6,14 @@
 #include "psnet.h"
 #include "psnet_io.h"
 #include <mach/iomap.h>
+
+#include <linux/if_addr.h>
+#include <linux/ip.h>
+#include <net/ip.h>
+#include <linux/ipv6.h>
+#include <net/ipv6.h>
+#include <net/protocol.h>
+
 #define WATCHDOG_TIMEO (5*HZ)
 
 /* NET NUMBER;  channel id;  lan/wan*/
@@ -248,6 +256,49 @@
     netif_wake_queue(net);
 }
 
+typedef int (*set_xlat_CB)(unsigned char *ip_info, unsigned char *dev_name);
+extern set_xlat_CB g_set_xlat_cb;
+extern struct in6_addr g_plat_subnet;
+extern struct in6_addr g_ipv6_local_subnet;
+extern struct in_addr g_ipv4_local_subnet;
+extern struct net_device *g_xlat_dev;
+int psnet_set_xlat(unsigned char *ip_info, unsigned char *dev_name)
+{
+	unsigned char *buff = ip_info;
+	memcpy(&g_plat_subnet, buff, sizeof(struct in6_addr));
+	buff = buff + sizeof(struct in6_addr);
+	memcpy(&g_ipv6_local_subnet, buff, sizeof(struct in6_addr));
+	buff = buff + sizeof(struct in6_addr);
+	memcpy(&g_ipv4_local_subnet, buff, sizeof(struct in_addr));
+	g_xlat_dev = dev_get_by_name(&init_net, dev_name);
+}
+
+int fill_ip_header(struct iphdr *ip, const struct ipv6hdr *old_header) {
+	if(memcmp(&old_header->saddr, &g_plat_subnet, 12)){
+		return 0;
+	}
+	if(memcmp(&old_header->daddr, &g_ipv6_local_subnet, 16)){
+		return 0;
+	}
+	ip->saddr = old_header->saddr.s6_addr32[3];
+	ip->daddr = g_ipv4_local_subnet.s_addr;
+	ip->ihl = 5;
+	ip->version = 4;
+	ip->tos = 0;
+	ip->tot_len = htons(sizeof(struct iphdr) + ntohs(old_header->payload_len));
+	ip->id = 0;
+	ip->frag_off = htons(IP_DF);
+	ip->ttl = old_header->hop_limit;
+	ip->protocol = old_header->nexthdr;
+	ip->check = 0;
+	return 1;
+}
+extern int fast_nat_check(unsigned char *data);
+extern uint32_t ipv6_pseudo_header_checksum(const struct ipv6hdr *ip6, uint16_t len, uint8_t protocol);
+extern uint32_t ipv4_pseudo_header_checksum(const struct iphdr *ip, uint16_t len);
+extern uint16_t ip_checksum(const void *data, int len);
+extern uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum);
+
 #define PSBUFF_DL_HEADER_OFFSET 64 //80
 //extern void * zGetpsbufferHead(void * data);
 void psnet_recv_notify(unsigned int index, const void *buffer, unsigned int length)//(T_ZDrvRpMsg_Msg rpmsg)
@@ -267,6 +318,41 @@
     psData.pbuf_head = buffer - PSBUFF_DL_HEADER_OFFSET;
 #endif
 	psData.datalen = length;
+	
+	unsigned char header[40] = {0};
+	if(unlikely(g_xlat_dev) && g_xlat_dev == dev->net && ((*(uint8_t *)psData.pbuf_data)&0xf0) == 0x60 && (g_xlat_dev->flags & IFF_UP)){
+		struct iphdr *ip_h = header;
+		struct ipv6hdr *ip6_h = (struct ipv6hdr *)psData.pbuf_data;
+		memcpy(header + sizeof(struct iphdr), psData.pbuf_data + sizeof(struct ipv6hdr), 4);
+		if(fill_ip_header(ip_h, ip6_h) && fast_nat_check(ip_h)){//
+			uint32_t old_sum, new_sum;
+			uint16_t checksum, len_left;
+			uint8_t protocol = ip6_h->nexthdr;
+			
+			len_left = ntohs(ip6_h->payload_len);
+			old_sum = ipv6_pseudo_header_checksum(psData.pbuf_data, len_left, protocol);
+			new_sum = ipv4_pseudo_header_checksum(ip_h, len_left);
+			ip_h->check = ip_checksum(ip_h, sizeof(struct iphdr));
+			if (protocol == IPPROTO_TCP) {
+				struct tcphdr *tcp_targ = (struct tcphdr *)(psData.pbuf_data + sizeof(struct ipv6hdr));
+				checksum = ip_checksum_adjust(tcp_targ->check, old_sum, new_sum);
+				tcp_targ->check = checksum;
+				psData.pbuf_data = psData.pbuf_data + sizeof(struct ipv6hdr) - sizeof(struct iphdr);
+				memcpy(psData.pbuf_data, ip_h, sizeof(struct iphdr));
+				psData.datalen = psData.datalen - sizeof(struct ipv6hdr) + sizeof(struct iphdr);
+			}else if (protocol == IPPROTO_UDP) {
+				struct udphdr *udp_targ = (struct udphdr *)(psData.pbuf_data + sizeof(struct ipv6hdr));
+				if(udp_targ->check){
+					checksum = ip_checksum_adjust(udp_targ->check, old_sum, new_sum);
+					udp_targ->check = checksum;
+				}
+				psData.pbuf_data = psData.pbuf_data + sizeof(struct ipv6hdr) - sizeof(struct iphdr);
+				memcpy(psData.pbuf_data, ip_h, sizeof(struct iphdr));
+				psData.datalen = psData.datalen - sizeof(struct ipv6hdr) + sizeof(struct iphdr);
+			}
+		}
+	}
+
       skb = skb_build_psbuf(&psData);
 
 	if (!skb) 
@@ -369,7 +455,7 @@
     int err;
     struct psnet *dev;
     struct net_device *net;
-	
+	g_set_xlat_cb = psnet_set_xlat;
     dbg("ddr_ap_net_lan_init.\n");
     
     err = -ENOMEM;
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/tun.c b/ap/os/linux/linux-3.4.x/drivers/net/tun.c
old mode 100644
new mode 100755
index 8e8b74e..3309fc9
--- a/ap/os/linux/linux-3.4.x/drivers/net/tun.c
+++ b/ap/os/linux/linux-3.4.x/drivers/net/tun.c
@@ -603,7 +603,7 @@
 
 	return skb;
 }
-
+extern int (*fast_from_softirq) (struct sk_buff *skb);
 /* Get packet from user space buffer */
 static ssize_t tun_get_user(struct tun_struct *tun,
 			    const struct iovec *iv, size_t count,
@@ -733,7 +733,12 @@
 		skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY;
 		skb_shinfo(skb)->gso_segs = 0;
 	}
-
+	if (fast_from_softirq && fast_from_softirq(skb))
+	{
+		tun->dev->stats.rx_packets++;
+		tun->dev->stats.rx_bytes += len;
+		return count;  
+	}
 	netif_rx_ni(skb);
 
 	tun->dev->stats.rx_packets++;