zte's code,first commit

Change-Id: I9a04da59e459a9bc0d67f101f700d9d7dc8d681b
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/psnet/Kconfig b/ap/os/linux/linux-3.4.x/drivers/net/psnet/Kconfig
new file mode 100644
index 0000000..b058eae
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/psnet/Kconfig
@@ -0,0 +1,7 @@
+#
+# Appletalk driver configuration
+#
+config PS_NET
+	tristate "ZTE AP LAN AND WAN"
+	default y
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/psnet/Makefile b/ap/os/linux/linux-3.4.x/drivers/net/psnet/Makefile
new file mode 100644
index 0000000..53c6333
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/psnet/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the Linux Wireless network device drivers.
+#
+
+obj-y += psnet_dev.o psnet_io.o
+
+ccflags-y += -I$(TOPDIR)/cp/ps/modem/com/inc
\ No newline at end of file
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/psnet/psnet.h b/ap/os/linux/linux-3.4.x/drivers/net/psnet/psnet.h
new file mode 100644
index 0000000..98bcfb7
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/psnet/psnet.h
@@ -0,0 +1,97 @@
+#ifndef _PSNET_H_
+#define _PSNET_H_
+
+#include <linux/interrupt.h>
+
+#include <linux/netdevice.h>
+
+#include <linux/kthread.h>
+
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/semaphore.h>
+//#include "psnet.h"
+
+#ifdef dbg
+#undef dbg
+#endif
+#if PSNET_DEBUG
+#define dbg(format, arg...) printk(KERN_DEBUG " psnet.c<%s>: " format "\n" , \
+	__func__ , ## arg)
+#else
+#define dbg(format, arg...) do {} while (0)
+#endif
+
+#ifdef err
+#undef err
+#endif
+#define err(format, arg...) printk(KERN_ERR " psnet.c<%s>: " format "\n" , \
+	__func__ , ## arg)
+
+#ifdef warn
+#undef warn
+#endif
+#define warn(format, arg...) printk(KERN_WARNING " psnet.c<%s>: " format "\n" , \
+	__func__ , ## arg)
+
+#ifdef info
+#undef info
+#endif
+#define info(format, arg...) printk(KERN_ERR " psnet.c<%s>: " format "\n" , \
+	__func__ , ## arg)
+
+
+#define NET_LAN  	0x10
+#define NET_WAN 	0x11
+typedef (*functpye1)(unsigned int);
+typedef (*functpye2)(unsigned int, void*, unsigned int);
+
+struct psnet_msg
+{
+    void  *buf;
+    unsigned int len;
+    u32 protocol; 
+};
+
+struct psnet {
+    struct net_device  *net;
+	//void*  IOhandle;
+	int initflag;
+    /*lan0 or wan0*/
+    unsigned char lan;
+	unsigned int index;
+    //struct sk_buff_head txq;
+    struct sk_buff_head rxq;
+	//struct semaphore  skb_que_sem;
+    //struct tasklet_struct rx_bh;
+	struct task_struct* rcv_thread;
+	//struct completion rcv_completion;
+	//wait_queue_head_t waitqueue;
+	void (*channelOpen)(unsigned int chid);
+	void (*channelClose)(unsigned int chid);
+	int (*transdataOut)(unsigned int channel, void* buff, unsigned int length);
+};
+
+enum{
+	DDR_DEV_WAN1 = 0,
+	DDR_DEV_WAN2,
+	DDR_DEV_WAN3,
+	DDR_DEV_WAN4,
+	DDR_DEV_WAN5,
+	DDR_DEV_WAN6,
+	DDR_DEV_WAN7,
+	DDR_DEV_WAN8,
+	DDR_DEV_MAX,
+	
+};
+void psnet_recv_notify(unsigned int index, const void *buffer, unsigned int length);
+int psnet_registerOpsCallback(unsigned int chID, functpye1* funcOpen, functpye1* funcClose, functpye2* funcOut);
+
+#if 0
+#ifdef USE_PSNET_PACKET
+s32 psnet_skb_xmit_thread(void * __dev);
+#endif
+int psnet_rx_packet(struct net_device *net, struct psnet_msg *psMsg);
+#endif
+#endif  /*  _PSNET_H_  */
+
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
new file mode 100755
index 0000000..ad98163
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/psnet/psnet_dev.c
@@ -0,0 +1,487 @@
+#include <linux/module.h>
+#include <linux/etherdevice.h>
+
+#include <net/SI/ext_mem.h>
+
+#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*/
+
+static unsigned int ddr_net_collect[DDR_DEV_MAX][2]= {
+		{DDR_DEV_WAN1, NET_WAN},
+		{DDR_DEV_WAN2, NET_WAN},
+		{DDR_DEV_WAN3, NET_WAN},
+		{DDR_DEV_WAN4, NET_WAN},
+		{DDR_DEV_WAN5, NET_WAN},
+		{DDR_DEV_WAN6, NET_WAN},
+		{DDR_DEV_WAN7, NET_WAN},
+		{DDR_DEV_WAN8, NET_WAN}	
+};
+
+//int *vir_addr_ddrnet = ZX_DDR_PSBUF_BASE;
+int *vir_addr_ddrnet = 0;
+EXPORT_SYMBOL(vir_addr_ddrnet);
+
+static struct net_device *psnet_dev[DDR_DEV_MAX];
+
+struct psnet *global_psnet[DDR_DEV_MAX];
+
+extern int (*fast_from_driver)(struct sk_buff *skb, struct net_device* dev);
+
+static psnet_receive_thread(unsigned long param)
+{
+    struct psnet *dev = (struct psnet *)param;
+    struct sk_buff    *skb;
+	int status;
+
+	struct sched_param sch_param = { .sched_priority = 1 };
+	sch_param.sched_priority = 37;
+	sched_setscheduler(current, SCHED_FIFO, &sch_param);
+	
+    while(1) {
+
+		if (dev->rxq.qlen == 0) 
+		{
+			// avoid to continue calling wake_up_process() when cmd_thread is NULL
+			//set_bit(WAKE_EVENT_CMD, &pshare->wake_event);
+			//DECLARE_WAITQUEUE(wait, current);
+			//add_wait_queue(&dev->waitqueue, &wait);
+			set_current_state(TASK_KILLABLE);			
+			//printk("[%s] is closed (%d)\n",dev->net->name, dev->bOpen);
+			schedule();
+			set_current_state(TASK_RUNNING);	
+			//remove_wait_queue(&dev->waitqueue, &wait);
+			continue;
+		}		
+		skb = skb_dequeue(&dev->rxq);
+		//TO DO read data from ps and trans to skb data
+		
+        if (skb && skb->len)
+    	{
+	    	skb->protocol = eth_type_trans (skb, dev->net);
+
+		    status = netif_rx (skb);
+		    if (status == NET_RX_SUCCESS) {
+		        dev->net->stats.rx_packets++;
+		        dev->net->stats.rx_bytes += skb->len;
+		    } else {
+		        dev->net->stats.rx_errors++;
+		        err("netif_rx status %d.", status);
+		    }
+    	}
+        else {
+            dev->net->stats.rx_errors++;
+            
+            dev_kfree_skb (skb);
+            err("drop!!!psnet_bh skb len == 0.");
+        }
+    }
+}
+
+static int psnet_open(struct net_device *net)
+{
+	//int retval;
+	struct psnet *dev = netdev_priv(net);
+	if(dev->initflag != 1)
+		return 0;
+	info("psnet_open %s", dev->net->name);
+	
+	if(net->flags & IFF_UP) {
+		dbg("the %s has been opened!", dev->net->name);
+		return -EBUSY;
+	}
+	if(dev->rcv_thread == NULL)
+    	dev->rcv_thread = kthread_run(psnet_receive_thread, dev, "psnet-thread/%s", dev->net->name);
+	else
+		wake_up_process(dev->rcv_thread);
+	netif_start_queue (net);
+	dev->channelOpen(dev->index);
+	dev->initflag = 2;
+	return 0;
+}
+
+static int psnet_close(struct net_device *net)
+{
+    struct psnet *dev = netdev_priv(net);
+	if(dev->initflag != 2)
+		return 0;
+    info("psnet_close");
+    netif_stop_queue(net);
+	dev->channelClose(dev->index);
+    //tasklet_kill (&dev->rx_bh);
+	dev->initflag = 1;
+	return 0;
+}
+extern unsigned long skb_max_panic;
+extern atomic_t  skb_used;
+extern atomic_t  skb_tops;
+extern atomic_t  skb_fromps;
+/* Called by the kernel in order to transmit a packet. */
+static netdev_tx_t psnet_xmit(struct sk_buff *skb, struct net_device *net)
+{
+	unsigned int length;
+	unsigned char *psbuff;
+	struct psnet *dev = netdev_priv(net);
+	int retval = 0;
+
+	if (!skb) {
+		panic("skb  err!!!!");
+	}
+	if(skb_max_panic && (atomic_read(&skb_tops) > skb_max_panic))
+	{
+		kfree_skb(skb);
+		//printk("skb=%d tops fail tops=%d fromps=%d\n",atomic_read(&skb_used),atomic_read(&skb_tops),atomic_read(&skb_fromps));
+		net->stats.tx_dropped++;
+		return NET_XMIT_SUCCESS;		  
+	}
+	//Æ«ÒÆÖÁipÍ·
+	skb_pull(skb, ETH_HLEN);
+
+	//Ë¢ÐÂcache
+	skb = flush_skbuf(skb);
+	if (!skb) {
+		panic("skb  err!!!!");
+		return NET_XMIT_SUCCESS;		  
+	}
+	insert_toext_mem(skb);
+
+	//TODO convert data to psbuf, wirte data to ps
+	length = skb->len;
+	retval = dev->transdataOut(dev->index, skb, length);
+	if (retval == 0) {
+		net->trans_start = jiffies;
+		net->stats.tx_packets++;
+		net->stats.tx_bytes += length;
+		return NET_XMIT_SUCCESS;		  
+	} else {
+		printk("write err, ret : %d!!!!", retval);
+//		dev_kfree_skb(skb);
+		free_toext_mem(skb);
+		net->stats.tx_errors++;
+		return NET_XMIT_SUCCESS;		  
+	}
+
+}
+#if 0
+static void psnet_rx_bh (unsigned long param)
+{
+    struct psnet *dev = (struct psnet *)param;
+    struct sk_buff    *skb;
+	int status;
+    while(skb = skb_dequeue(&dev->rxq)) {
+        if (skb->len)
+    	{
+	    	skb->protocol = eth_type_trans (skb, dev->net);
+
+		    status = netif_rx (skb);
+		    if (status == NET_RX_SUCCESS) {
+		        dev->net->stats.rx_packets++;
+		        dev->net->stats.rx_bytes += skb->len;
+		    } else {
+		        dev->net->stats.rx_errors++;
+		        err("netif_rx status %d.", status);
+		    }
+    	}
+        else {
+            dev->net->stats.rx_errors++;
+            
+            dev_kfree_skb (skb);
+            err("drop!!!psnet_bh skb len == 0.");
+        }
+    }
+}
+#endif 
+
+/* Called by the kernel when transmit times out */
+static void psnet_tx_timeout(struct net_device *net)
+{
+    err("sent timeout!");
+    net->stats.tx_errors++;
+    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)
+{
+	struct sk_buff    *skb;
+	struct  psnet *dev = (struct  psnet *)global_psnet[index];
+	struct T_FromExt_mem psData = { 0 };
+
+	psData.pbuf_data = (void *)buffer;
+#if 0
+#ifdef USE_CPPS_KO
+	psData.pbuf_head = (void *)cpps_callbacks.zGetpsbufferHead(buffer); 
+#else
+	psData.pbuf_head = (void *)zGetpsbufferHead(buffer); 
+#endif
+#else
+    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) 
+	{
+		//err("psnet_recv_notify can not allocate skb. drop the package");
+		dev->net->stats.rx_dropped++;
+	} 
+	else 
+	{
+		skb_push(skb, ETH_HLEN);
+
+		if (set_protocol_by_version(skb) < 0)
+			return;
+
+		if (fast_from_driver && fast_from_driver(skb, dev->net))
+		{
+			return;
+		}
+
+            memcpy(skb->data, dev->net->dev_addr, ETH_ALEN);
+            memcpy(skb->data + ETH_ALEN, dev->net->dev_addr, ETH_ALEN);
+			
+		skb_queue_tail(&dev->rxq, skb);
+		wake_up_process(dev->rcv_thread);
+	}
+
+}
+
+static struct net_device_stats *psnet_get_stats(struct net_device *net)
+{
+//    dbg("%s: request for stats\n", net->name);
+    return &net->stats;
+}
+
+const struct net_device_ops psnet_netdev_ops = {
+    .ndo_open    = psnet_open,
+    .ndo_stop    = psnet_close,
+    .ndo_start_xmit = psnet_xmit,
+    .ndo_tx_timeout = psnet_tx_timeout,
+    .ndo_get_stats = psnet_get_stats,
+};
+int psnet_registerOpsCallback(unsigned int chID, functpye1* funcOpen, functpye1* funcClose, functpye2* funcOut)
+{
+	int nRet = -1;
+	struct  psnet *dev = global_psnet[chID];
+	if(funcOpen && funcClose && funcOut)
+	{
+		dev->channelOpen = funcOpen;
+		dev->channelClose = funcClose;
+		//rpmsg_channel->transdatatoRpmsg = funcIn;
+		dev->transdataOut = funcOut;
+		dev->initflag = 1;
+		nRet = 0;
+	}
+	return nRet;
+}
+
+static inline void psnet_init_netdev(struct net_device *net)
+{
+    //memset(net->dev_addr, 0xfe, ETH_ALEN);
+#if 0
+	u8 mac[ETH_ALEN] = {0x0, 0xa0, 0xc6, 0x11, 0x22, 0x33};
+	memcpy(net->dev_addr, mac, ETH_ALEN);
+#else
+	// randomly generated ethernet address
+	static u8 node_id [ETH_ALEN];
+	random_ether_addr(node_id);
+	memcpy (net->dev_addr, node_id, sizeof node_id);
+#endif
+    net->netdev_ops = &psnet_netdev_ops;
+    net->watchdog_timeo = WATCHDOG_TIMEO;
+    net->flags |= IFF_NOARP;
+}
+static inline void psnet_init_lan(struct net_device *net, unsigned int index)
+{
+    struct psnet *dev;
+    dev = netdev_priv(net);
+    dev->net = net;
+    //strcpy(net->name, "lan%d");
+    sprintf(net->name, "ps%d", index+1);
+    dev->lan = 1;
+    skb_queue_head_init (&dev->rxq);
+}
+
+static inline void psnet_init_wan(struct net_device *net, unsigned int index)
+{
+    struct psnet *dev;
+	//unsigned wan_index;
+    dev = netdev_priv(net);
+    dev->net = net;
+	sprintf(net->name, "wan%d", index+1);// WAN1, WAN2,....
+    dev->lan = 0;
+    skb_queue_head_init (&dev->rxq);
+}
+
+
+static int __init psnet_init(void)
+{
+    int i;
+    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;
+    for (i = 0; i < DDR_DEV_MAX; i++){
+    //for (i = 0; i < 1; i++) {
+        net = alloc_etherdev(sizeof(struct psnet));
+        if (!net) {
+            printk(KERN_ERR "could not allocate device.\n");
+            return err;
+        }
+
+        //È·±£ËùÓз¢Íùcp²àµÄÊý¾ÝÍ·²¿Ô¤Áô×ã¹»£¬±ÜÃâË¢cacheµÄʱºòÖØÐÂÉêÇëskb
+        net->needed_headroom += NET_SKB_PAD - ETH_HLEN;
+        
+        dev = netdev_priv(net);
+        global_psnet[i] = dev;
+
+        if(ddr_net_collect[i][1] == NET_LAN)
+        {
+        	printk("NET_LAN :%d\n",i);
+        	psnet_init_lan(net, i);
+        }
+        else if (ddr_net_collect[i][1] == NET_WAN)
+        {
+        	psnet_init_wan(net, i);
+		printk("NET_WAN :%d\n",i);
+        }
+        else
+        {
+        	printk("NET_error :%d\n",i);
+        	goto out_free_netdev;
+        }
+	dev->index = i;
+	dev->net = net;
+        //dev->rx_bh.func = psnet_rx_bh;
+        //dev->rx_bh.data = (unsigned long) dev;
+		//sema_init(&dev->skb_que_sem,1);
+		//init_waitqueue_head(&dev->waitqueue);
+		//init_completion(&dev->rcv_completion);
+        psnet_init_netdev(net);
+        err = register_netdev(net);
+	printk("register_netdev error:%d :%d\n",err,i);
+        if (err)
+            goto out_free_netdev;
+        psnet_dev[i] = net;
+//		psnet_start(net);
+    }
+
+    return 0;
+
+out_free_netdev:
+    free_netdev(net);
+    return err;
+}
+late_initcall(psnet_init);
+
+static void __exit psnet_exit(void)
+{
+    int i;
+    struct net_device *net;
+
+    for (i = 0; i < DDR_DEV_MAX; i++) {
+        if ((net = psnet_dev[i])) {
+            unregister_netdev(net);
+            free_netdev(net);
+            psnet_dev[i] = NULL;
+        }
+    }
+}
+module_exit(psnet_exit);
+
+MODULE_AUTHOR("P96G");
+MODULE_DESCRIPTION("7510 AP Lan Net Device");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/psnet/psnet_io.c b/ap/os/linux/linux-3.4.x/drivers/net/psnet/psnet_io.c
new file mode 100755
index 0000000..bef6240
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/psnet/psnet_io.c
@@ -0,0 +1,400 @@
+#define NEW_PS_BUF
+#include <linux/module.h>
+#include "psnet.h"
+#include "psnet_io.h"
+#include <linux/cp_types.h>
+#include <linux/soc/zte/rpm/rpmsg_sim.h>
+#include "zpsi_api.h"
+#include <linux/if_addr.h>
+#include <linux/if_arp.h>
+#include <linux/ipv6.h>
+#include <linux/icmpv6.h>
+#include <net/ipv6.h>
+#include <net/protocol.h>
+#include <net/ndisc.h>
+
+extern struct psnet *global_psnet[DDR_DEV_MAX];
+
+#define ZATI2_DATA_CHANNEL_ID_NUM_START		(CHANNEL_AP2PS_MAXID)//+1
+#define PSNET_IO_SUCCESS	0
+#define PSNET_IO_ERROR		1
+//×Ö½Ú¶ÔÆë¶ÏÑÔ
+#define MAC4BYTE_ASSERT 	4
+#define MAC2BYTE_ASSERT 	2
+
+#define  ZATI2_SUCCESS	0
+#define  ZATI2_ERROR	1
+
+typedef struct _PS_CHID_INFO{
+	unsigned int chid;
+	unsigned int  stat; 
+} PS_CHID_INFO;
+
+static PS_CHID_INFO s_pschid_info[DDR_DEV_MAX] = {
+		{ZATI2_DATA_CHANNEL_ID_NUM_START + 0, 0},
+		{ZATI2_DATA_CHANNEL_ID_NUM_START + 1, 0},
+		{ZATI2_DATA_CHANNEL_ID_NUM_START + 2, 0},
+		{ZATI2_DATA_CHANNEL_ID_NUM_START + 3, 0},
+		{ZATI2_DATA_CHANNEL_ID_NUM_START + 4, 0},
+		{ZATI2_DATA_CHANNEL_ID_NUM_START + 5, 0},
+		{ZATI2_DATA_CHANNEL_ID_NUM_START + 6, 0},
+		{ZATI2_DATA_CHANNEL_ID_NUM_START + 7, 0}
+};
+extern PBYTE zPutSkb2Psbuf(PBYTE pbIpData, WORD wIpDataLen, PBYTE pbPdcpPduAddr, PBYTE pbSkbAddr);
+extern WORD zGetUpLinkSduSize(VOID);
+enum ip_ver {
+	IP_V4,
+	IP_V6,
+	MAX_IP_VER
+};
+
+extern VOID zFreeDlBuf(PBYTE pBuf);
+
+struct nd_opt_prefix_info     /* prefix information */
+{
+	uint8_t   nd_opt_pi_type;
+	uint8_t   nd_opt_pi_len;
+	uint8_t   nd_opt_pi_prefix_len;
+	uint8_t   nd_opt_pi_flags_reserved;
+	uint32_t  nd_opt_pi_valid_time;
+	uint32_t  nd_opt_pi_preferred_time;
+	uint32_t  nd_opt_pi_reserved2;
+	struct in6_addr nd_opt_pi_prefix;
+};
+
+struct psnet_RA_info
+{
+	unsigned int flag;
+	struct in6_addr prefix;
+	unsigned char prefix_len;
+	unsigned char ext_cid;
+	struct semaphore sem;
+};
+typedef void (*set_pdp_state_CB)(unsigned char cid, unsigned int state);
+typedef int (*get_ipv6_prefix_CB)(unsigned char cid, int len, unsigned char *prefix, unsigned char *prefix_len);
+extern set_pdp_state_CB g_psnet_set_RA_state;
+extern get_ipv6_prefix_CB g_psnet_get_prefix;
+extern int zAti2_IsExCid(unsigned char cid, unsigned char *pcid);
+struct psnet_RA_info g_psnet_ipv6_prefix[DDR_DEV_MAX] = {0};
+
+void psnet_parse_RA_pkt(unsigned char cid, unsigned char* pkt, int len)
+{
+	//warn("cid=%d flag=%d\n", cid, g_psnet_ipv6_prefix[cid-1].flag);
+	if(likely(g_psnet_ipv6_prefix[cid-1].flag))
+		return;
+
+	if ((pkt[0] & 0xF0) == 0x60 && len >= (sizeof(struct ipv6hdr) + sizeof(struct ra_msg) + sizeof(struct nd_opt_prefix_info))){
+		struct ipv6hdr *ip6h = (struct ipv6hdr *)pkt;
+		unsigned char nexthdr = ip6h->nexthdr;
+		unsigned char *hp = pkt + sizeof(struct ipv6hdr);
+		warn("cid=%d nexthdr=%d data=%d\n", cid,nexthdr,(*hp));
+		while((nexthdr == NEXTHDR_HOP)||(nexthdr == NEXTHDR_ROUTING)|| (nexthdr == NEXTHDR_DEST)){
+			nexthdr = *hp;
+			hp = hp + ipv6_optlen((struct ipv6_opt_hdr *)hp);
+		}
+		if(nexthdr == IPPROTO_ICMPV6 && (*hp) == NDISC_ROUTER_ADVERTISEMENT){
+			struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(hp + sizeof(struct ra_msg));
+			int opt_len = len - sizeof(struct ipv6hdr) - sizeof(struct ra_msg);
+			while (opt_len) {
+				int l;
+				if (opt_len < sizeof(struct nd_opt_hdr))
+					return;
+				l = nd_opt->nd_opt_len << 3;
+				if (opt_len < l || l == 0)
+					return;
+				if((nd_opt->nd_opt_type) == ND_OPT_PREFIX_INFO){
+					struct nd_opt_prefix_info *pinfo = (struct nd_opt_prefix_info *)nd_opt;
+					unsigned char ext_cid = 0;
+#ifdef USE_CPPS_KO
+					cpps_callbacks.zAti2_IsExCid(cid, &ext_cid);
+#else
+					zAti2_IsExCid(cid, &ext_cid);
+#endif
+					if(ext_cid){
+						g_psnet_ipv6_prefix[cid-1].flag = 2;
+						g_psnet_ipv6_prefix[cid-1].ext_cid = ext_cid;
+						g_psnet_ipv6_prefix[ext_cid-1].ext_cid = cid;
+					}else{
+						ext_cid = cid;
+					}
+					memcpy(&g_psnet_ipv6_prefix[ext_cid-1].prefix, &pinfo->nd_opt_pi_prefix, sizeof(struct in6_addr));
+					g_psnet_ipv6_prefix[ext_cid-1].prefix_len = pinfo->nd_opt_pi_prefix_len;
+					g_psnet_ipv6_prefix[ext_cid-1].flag = 1;
+					up(&g_psnet_ipv6_prefix[ext_cid-1].sem);
+					warn("cid=%d prefix_len=%d\n", ext_cid, pinfo->nd_opt_pi_prefix_len);
+					return;
+				}
+				opt_len -= l;
+				nd_opt = ((void *)nd_opt) + l;
+			}
+		}
+	}
+}
+
+void psnet_set_RA_state(unsigned char cid, unsigned int flag)
+{
+	if(cid > 0 && cid <= DDR_DEV_MAX){
+		unsigned char ext_cid = g_psnet_ipv6_prefix[cid-1].ext_cid;
+		warn("cid=%d flag=%d cur_flg=%d ext=%d\n", cid, flag, g_psnet_ipv6_prefix[cid-1].flag, ext_cid);
+		if(flag == 0 && ext_cid > 0 && ext_cid <= DDR_DEV_MAX){
+			warn("ext cid=%d cur_flg=%d ext=%d\n", ext_cid, 
+				g_psnet_ipv6_prefix[ext_cid-1].flag, g_psnet_ipv6_prefix[ext_cid-1].ext_cid);
+			if(g_psnet_ipv6_prefix[cid-1].flag == 1){
+				g_psnet_ipv6_prefix[ext_cid-1].flag = 0;
+			}
+			g_psnet_ipv6_prefix[ext_cid-1].ext_cid = 0;
+			g_psnet_ipv6_prefix[cid-1].ext_cid = 0;
+		}
+		g_psnet_ipv6_prefix[cid-1].flag = flag;
+		if(flag == 0){
+			int ret = down_trylock(&g_psnet_ipv6_prefix[cid-1].sem);
+			if(ret != 0)//cov
+				warn("cid=%d down_trylock ret=%d\n", cid, ret);
+		}
+		return;
+	}
+	err("err cid=%d flag=%d \n", cid, flag);
+}
+
+int psnet_get_prefix(unsigned char cid, int len, unsigned char *prefix, unsigned char *prefix_len)
+{
+	if(cid > 0 && cid <= DDR_DEV_MAX){
+		if(g_psnet_ipv6_prefix[cid-1].flag == 0){
+			int ret = down_timeout(&g_psnet_ipv6_prefix[cid-1].sem, 3*HZ);
+			if(ret != 0)//cov
+				err("err ipv6_prefix down_timeout ret=%d\n", ret);
+		}
+		if(g_psnet_ipv6_prefix[cid-1].flag == 1 
+			&& len > (((g_psnet_ipv6_prefix[cid-1].prefix_len/8+1)/2) * 5)){
+			int i = 0;
+			unsigned char* prefix_str = prefix;
+			int prefix_temp = g_psnet_ipv6_prefix[cid-1].prefix_len/8;
+			
+			for (i = 0; i < prefix_temp; i++) {
+				sprintf(prefix_str + strlen(prefix_str), "%02x", g_psnet_ipv6_prefix[cid-1].prefix.s6_addr[i]);
+				if (i % 2 == 1)
+					sprintf(prefix_str + strlen(prefix_str), "%s", ":");
+			}
+			warn("cid=%d len=%d plen=%d prefix=%s\n", cid, len, g_psnet_ipv6_prefix[cid-1].prefix_len, prefix);
+			*prefix_len = g_psnet_ipv6_prefix[cid-1].prefix_len;
+			return strlen(prefix)+1;
+		}
+		err("err flag=%d pflen=%d \n", g_psnet_ipv6_prefix[cid-1].flag, g_psnet_ipv6_prefix[cid-1].prefix_len);
+	}
+	err("err cid=%d len=%d \n", cid, len);
+	return 0;
+}
+
+void psnet_freepsbuf(void *head)
+{
+#ifdef USE_CPPS_KO
+		cpps_callbacks.zFreeDlBuf(head);
+#else
+    	zFreeDlBuf(head);	
+#endif
+}
+
+unsigned int psnet_get_pschid_stat(unsigned int chid)
+{
+	unsigned int index = chid - 	ZATI2_DATA_CHANNEL_ID_NUM_START;
+	return s_pschid_info[index].stat;
+}
+void psnet_set_pschid_stat(unsigned int chid, unsigned int newstat)
+{
+	unsigned int index = chid - 	ZATI2_DATA_CHANNEL_ID_NUM_START;
+	s_pschid_info[index].stat = newstat;
+}
+
+static int psnet_write(struct sk_buff  *skb, unsigned long data_len, unsigned char ip_type, int ch_id)
+{
+	int ret  = 1;
+	unsigned char * pdata = skb->data;
+    //µ¥ºËÖÐEL2Ïà¹ØDDR½øÐÐѹËõ£¬UL EPDCP NODEÓÉEL2½øÐйÜÀí£¬
+    //AP²»ÔÙ½øÐÐÉêÇ룬¸³ÖµºÍÊͷŹÜÀí
+    //unsigned char * pdcpDataAddr = skb->head;
+       unsigned char * pdcpDataAddr = NULL;
+#ifdef USE_CPPS_KO
+	PBYTE p_to_ps_packet = (PBYTE)cpps_callbacks.zPutSkb2Psbuf(pdata, data_len, pdcpDataAddr, skb);
+#else
+	PBYTE p_to_ps_packet = (PBYTE)zPutSkb2Psbuf(pdata, data_len, pdcpDataAddr, skb);
+#endif
+	if(p_to_ps_packet == NULL)
+		panic("get ulbufaddr error ");
+		
+	//p_to_ps_packet->wIpPacketLen    = (UINT16)(data_len);
+	//p_to_ps_packet->wIpPacketOffSet = (UINT16)(pdata - (UINT8 *)p_to_ps_packet->Data);        
+
+	//printk("[ZJT] IPType =%d , skb->len = %d, ch_id = %d \r\n", ip_type, data_len, ch_id);
+#ifdef USE_CPPS_KO
+	WORD SduSize = cpps_callbacks.zGetUpLinkSduSize();
+#else
+	WORD SduSize = zGetUpLinkSduSize();
+#endif
+	if(0 == ip_type)   	
+#ifdef USE_CPPS_KO
+		ret = cpps_callbacks.zAti2_Send(ch_id, (UINT8*)p_to_ps_packet, SduSize, ZATI2_CHIND_PSD);
+#else
+		ret = zAti2_Send(ch_id, (UINT8*)p_to_ps_packet, SduSize, ZATI2_CHIND_PSD);
+#endif
+	else if(1 == ip_type)	
+#ifdef USE_CPPS_KO
+		ret = cpps_callbacks.zAti2_Send(ch_id, (UINT8*)p_to_ps_packet, SduSize, ZATI2_CHIND_PSDV6);
+#else
+		ret = zAti2_Send(ch_id, (UINT8*)p_to_ps_packet, SduSize, ZATI2_CHIND_PSDV6);
+#endif
+	else
+		panic("send data with wrong ip_type ");
+
+	return (0-ret);
+}
+int open_flag[DDR_DEV_MAX] = {0};
+int psnet_IOOpen(unsigned int index)
+{
+	int chid;
+	int ret;
+	char pStr[20] = {0};
+	
+	if(index >= DDR_DEV_MAX)
+	{
+		err("%s: chid [%d] is illegal \n", __func__, index);
+		return PSNET_IO_ERROR;
+	}
+
+	chid = s_pschid_info[index].chid;
+	if(0 == open_flag[index])
+	{
+#ifdef USE_CPPS_KO
+		ret = cpps_callbacks.zAti2_Open(chid);
+#else
+		ret = zAti2_Open(chid);
+#endif
+		if(ret != ZATI2_SUCCESS)
+		{
+			panic("open chid failed");
+		}
+		open_flag[index]=1;
+	}
+
+	//·¢ËÍZGACT£¬½«µ±Ç°Í¨µÀÇÐΪÊý¾Ý̬£¬ºóÐøÒ»Ö±¶¼ÎªÊý¾Ý̬	
+	sprintf((char *)pStr, "\r\nAT+ZGACT=1,%d\r\n",index+1);	
+	//printk("[zjt] chid= %d, pStr =%s \n", chid, pStr);
+#ifdef USE_CPPS_KO
+	ret = cpps_callbacks.zAti2_Send(chid, pStr, strlen(pStr), ZATI2_CHIND_AT); 
+#else
+	ret = zAti2_Send(chid, pStr, strlen(pStr), ZATI2_CHIND_AT);	
+#endif
+	if(ret != 0)	
+	{		
+		err("send %s to %d  failed!!!", pStr, chid);
+	}
+	return PSNET_IO_SUCCESS;
+}
+
+int psnet_IOClose(unsigned int index)
+{
+	int chid;
+
+	if(index >= DDR_DEV_MAX)
+	{
+		err("%s: dev index [%d] is illegal \n", __func__, index);
+		return PSNET_IO_ERROR;
+	}	
+
+	chid = s_pschid_info[index].chid;
+	if(s_pschid_info[index].stat != 1)
+	{
+		err("%s: chid [%d] is already deact, please check !!! \n", __func__, chid);
+		return PSNET_IO_ERROR;
+	}
+
+	//·¢ËÍZGACT£¬½«µ±Ç°Í¨µÀÇÐΪAT̬£¬
+#ifdef USE_CPPS_KO
+	cpps_callbacks.zAti2_Send(chid, NULL, 0, ZATI2_CHIND_TURN_AT);
+#else
+	zAti2_Send(chid, NULL, 0, ZATI2_CHIND_TURN_AT);
+#endif
+	s_pschid_info[index].stat = 0;
+	
+	return 0;
+}
+
+int psnet_transIPdataToPS(unsigned int index, struct sk_buff * skb, unsigned int length)
+{
+
+    	unsigned long flags;
+	int chid;		
+	int ret = 0;
+
+	unsigned char * buffer  = skb->data;
+	if(index >= DDR_DEV_MAX)
+	{
+		err("%s: chid [%d] is illegal \n", __func__, index);
+		return PSNET_IO_ERROR;
+	}	
+
+	if(buffer == NULL)//cov_2 || length < 0)
+	{
+		err("%s:  Input data is not valid \n", __func__);
+		return PSNET_IO_ERROR;
+	}
+
+	chid = s_pschid_info[index].chid;
+
+	//if(!((unsigned long)buffer%MAC4BYTE_ASSERT != 0) || !((unsigned long)buffer % MAC2BYTE_ASSERT == 0))
+	//	panic("wrong skb data");
+
+	if ((((unsigned)buffer[0]) & 0xF0) == 0x40 && (s_pschid_info[index].stat == 1))
+	{
+		ret = psnet_write(skb, length, IP_V4, chid); 
+	}
+	else if ((((unsigned)buffer[0]) & 0xF0) == 0x60 && (s_pschid_info[index].stat == 1))
+	{
+		ret = psnet_write(skb, length, IP_V6, chid); 
+	}
+	else
+	{
+		//err("%s:: IP type of data is wrong!!!", __func__);
+		return PSNET_IO_ERROR;
+	}
+	
+	if(ret < 0)
+	{
+		//err("%s:: direct psnet_transIPdataToPS failed!!!\n", __func__);
+		return PSNET_IO_ERROR;
+	}
+
+	return 0;
+}
+int transdataToTCPIP(unsigned int chid, void *buffer, unsigned int length)
+{
+	int index = chid - ZATI2_DATA_CHANNEL_ID_NUM_START;
+	//truct psnet *dev = global_psnet[index];
+	psnet_recv_notify(index,buffer,length);
+    	return 0;
+}
+
+int psnet_IOInit(void)
+{
+	int index;
+	for (index = 0; index < DDR_DEV_MAX; index++)
+	{
+		psnet_registerOpsCallback(index, psnet_IOOpen, psnet_IOClose, psnet_transIPdataToPS);
+		sema_init(&g_psnet_ipv6_prefix[index].sem, 0);
+	}	
+	g_psnet_set_RA_state = psnet_set_RA_state;
+	g_psnet_get_prefix = psnet_get_prefix;
+	return 0;
+}
+
+int psnet_IOExit(void)
+{
+	return 0;
+}
+
+late_initcall(psnet_IOInit);
+module_exit(psnet_IOExit);
+
+MODULE_AUTHOR("ZTE");
+MODULE_DESCRIPTION("ZTE Lan Net Device");
+MODULE_LICENSE("GPL");
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/psnet/psnet_io.h b/ap/os/linux/linux-3.4.x/drivers/net/psnet/psnet_io.h
new file mode 100644
index 0000000..992c80b
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/psnet/psnet_io.h
@@ -0,0 +1,15 @@
+
+#ifndef PSNETIO_H
+#define PSNETIO_H
+#include <net/SI/ext_mem.h>
+//#include <linux/module.h>
+
+
+int psnet_IOOpen(unsigned int index);
+int psnet_IOClose(unsigned int index);
+int psnet_transIPdataToPS(unsigned int index, struct sk_buff *skb, unsigned int length);
+int transdataToTCPIP(unsigned int index, void *buffer, unsigned int length);
+
+
+#endif //PSNETIO_H
+