/**************************************************************************
 * File Name                   : tls_netconn.c
 * Author                      :
 * Version                     :
 * Date                        :
 * Description                 :
 *
 * Copyright (c) 2014 Winner Microelectronics Co., Ltd. 
 * All rights reserved.
 *
 ***************************************************************************/
#include "wm_config.h"

#if TLS_CONFIG_SOCKET_RAW

#include "tls_netconn.h"
#include "wm_debug.h"
#include "wm_regs.h"
#include "lwip/sys.h"
#include "lwip/tcp.h"
#include "lwip/udp.h"
#include "lwip/tcpip.h"
#include <string.h>
#include "wm_sockets.h"

struct tls_netconn *p_net_conn[TLS_MAX_NETCONN_NUM];
#if CONN_SEM_NOT_FREE
sys_sem_t conn_op_completed[TLS_MAX_NETCONN_NUM] = {NULL};
#endif

//static err_t net_tcp_sent_cb(void *arg, struct tcp_pcb *pcb, u16_t len);
static void net_tcp_err_cb(void *arg, err_t err);
static err_t net_tcp_poll_cb(void *arg, struct tcp_pcb *pcb);
static void net_free_socket(int socketno);

//static err_t net_do_writemore(struct tls_netconn *conn);

#define BUF_CIRC_CNT_TO_END(head,tail,size) \
	({int end = (size) - (tail); \
	  int n = ((head) + end) & ((size)-1); \
	  n < end ? n : end;})


u32 current_src_ip = 0;
void tls_net_set_sourceip(u32 ipvalue)
{
	current_src_ip = ipvalue;
}

u32 tls_net_get_sourceip(void)
{
	return 	current_src_ip;
}

struct tls_netconn *get_server_conn(struct tls_netconn *conn)
{
    struct tls_netconn *server_conn = NULL;

    do{
        server_conn = dl_list_first(&conn->list, struct tls_netconn, list);
        conn = server_conn;
    }while((server_conn != NULL) && server_conn->client);

    return server_conn;
}

static struct tls_netconn *net_alloc_socket(struct tls_netconn *conn)
{
    int sock=-1, i=0, j=0;
    u32 cpu_sr;
	struct tls_netconn * conn_t = NULL;

    for (i = 0; i < TLS_MAX_NETCONN_NUM; i++)
    {
        if(p_net_conn[i] == NULL)
		{
			sock = i;
			break;
		}
    }

	if (i == TLS_MAX_NETCONN_NUM){
		TLS_DBGPRT_ERR("\nnet_alloc_socket error\n");
		return NULL;
	}

    if(conn != NULL)
    {
        j = dl_list_len(&conn->list);
        if(j>=4)
        {
            TLS_DBGPRT_ERR("list len > 4\n");
            sock = -1;
        }
    }
	if(sock < 0)
	{
	        TLS_DBGPRT_ERR("sock < 0\n");
		return NULL;
	}
	cpu_sr = tls_os_set_critical();
	conn_t = tls_mem_alloc(sizeof(struct tls_netconn));
	tls_os_release_critical(cpu_sr);
	if (NULL != conn_t) {
    	p_net_conn[sock] = conn_t;
    	memset(conn_t, 0, sizeof(struct tls_netconn));
    	conn_t->used = true;
    	conn_t->state = NETCONN_STATE_NONE;
    	conn_t->skt_num = sock + 1;//TLS_MAX_NETCONN_NUM + 
    	dl_list_init(&conn_t->list);
#if CONN_SEM_NOT_FREE
        TLS_DBGPRT_INFO("conn_op_completed[%d]=%x\n",sock, conn_op_completed[sock]);
        if(NULL == conn_op_completed[sock])
        {
            if(sys_sem_new(&conn_op_completed[sock], 0) != ERR_OK)
            {
                net_free_socket(conn_t->skt_num);
            }
        }
#else
    	if (sys_sem_new(&conn_t->op_completed, 0) != ERR_OK) {
			net_free_socket(conn_t->skt_num);
    	}
#endif		
	}
	TLS_DBGPRT_INFO("net_alloc_socket conn ptr = 0x%x\n", conn_t);
	return conn_t;
	
//    return NULL;
}

static void net_free_socket(int socketno)
{
	int index;
	u32 cpu_sr;
	struct tls_netconn *conn = NULL;

    conn = tls_net_get_socket(socketno);
	if(conn == NULL || TRUE != conn->used)
	{
		TLS_DBGPRT_ERR("\nconn=%x,used=%d\n",conn, conn->used);
		return;
	}
	TLS_DBGPRT_INFO("conn ptr = 0x%x\n", conn);
#if !CONN_SEM_NOT_FREE	
	if (NULL != conn->op_completed)
	{
		sys_sem_free(&conn->op_completed);
	}
#endif	
	conn->used = false;
	if(conn->client  && conn->list.prev != NULL && conn->list.prev != &conn->list)
	{
		TLS_DBGPRT_INFO("del from list.\n");
		cpu_sr = tls_os_set_critical();
		dl_list_del(&conn->list);
		tls_os_release_critical(cpu_sr);			
	}
	index = conn->skt_num - 1;//TLS_MAX_NETCONN_NUM - 
	if (conn->pcb.tcp)
	{
		tcp_close(conn->pcb.tcp);
		conn->pcb.tcp = NULL;
	}
	tls_mem_free(conn);
	cpu_sr = tls_os_set_critical();
	conn = NULL;
	p_net_conn[index] = NULL;
	tls_os_release_critical(cpu_sr);
}

static void net_send_event_to_hostif(struct tls_netconn *conn,
        int event)
{
    struct tls_socket_desc *skt_desc = conn->skd;
	TLS_DBGPRT_INFO("skt_desc->state_changed: 0x%x, event=%d\n", skt_desc->state_changed, event);
    if(skt_desc->state_changed)
    {
        skt_desc->state_changed(conn->skt_num, event, conn->state);
    }
}

static void net_tcp_close_connect(int socketno)
{
    err_t err;
	struct tls_netconn *conn = NULL;
    conn = tls_net_get_socket(socketno);
	if(conn == NULL || TRUE != conn->used)
	{
		TLS_DBGPRT_ERR("\nconn=%x,used=%d\n",conn, conn->used);
		return;
	}
	if (NULL == conn->pcb.tcp){
		TLS_DBGPRT_ERR("\ntcp=null\n");
		return;
	}
    /* Set back some callback pointers */
    tcp_arg(conn->pcb.tcp, NULL);
    if (conn->pcb.tcp->state == LISTEN) {
        tcp_accept(conn->pcb.tcp, NULL);
    } else {
        /* some callbacks have to be reset if tcp_close is not successful */
        tcp_recv(conn->pcb.tcp, NULL);
        tcp_accept(conn->pcb.tcp, NULL);
        tcp_sent(conn->pcb.tcp, NULL);
        tcp_poll(conn->pcb.tcp, NULL, 4);
        tcp_err(conn->pcb.tcp, NULL);
    }
    err = tcp_close(conn->pcb.tcp);
    if (err)
        err = tcp_shutdown(conn->pcb.tcp, 1, 1);
    if (err == ERR_OK) {
        /* Closing succeeded */
        TLS_DBGPRT_INFO("tcp %d closed\n", conn->skt_num);
        conn->state = NETCONN_STATE_NONE;
        /* Set back some callback pointers as conn is going away */
        conn->pcb.tcp = NULL;
        net_free_socket(socketno);
        /* Trigger select() in socket layer. Make sure everybody notices activity
           on the connection, error first! */
    } else {
        /* Closing failed, restore some of the callbacks */
        /* Closing of listen pcb will never fail! */
        LWIP_ASSERT("Closing a listen pcb may not fail!", (conn->pcb.tcp->state != LISTEN));
        //tcp_sent(conn->pcb.tcp, net_tcp_sent_cb);
        tcp_poll(conn->pcb.tcp, net_tcp_poll_cb, 4);
        tcp_err(conn->pcb.tcp, net_tcp_err_cb);
        tcp_arg(conn->pcb.tcp, (void *)socketno);
        /* don't restore recv callback: we don't want to receive any more data */
    }
}

static err_t net_tcp_poll_cb(void *arg, struct tcp_pcb *pcb)
{
	err_t err_ret = ERR_OK;
    struct tls_netconn *conn = NULL;
    struct tls_netconn *server_conn = NULL; 
	int socketno = (int)arg;
	conn = tls_net_get_socket(socketno);
	if(conn == NULL || TRUE != conn->used || NULL == pcb)
	{
		TLS_DBGPRT_ERR("\nconn=%x,used=%d,pcb=%x\n",conn, conn->used, pcb);
		return ERR_ARG;
	}
    //TLS_DBGPRT_INFO("net_tcp_poll_cb start\n");
/*
    if (conn == NULL || pcb == NULL) {
	TLS_DBGPRT_ERR("\npcb=null\n");	
        return ERR_CLSD;
    }
*/
    if (conn->write_state && (conn->state == NETCONN_STATE_CONNECTED)) {
        //net_do_writemore(conn);
    } else if (conn->state == NETCONN_STATE_CLOSED) {
        net_tcp_close_connect(socketno); 
    } else {
        if ((pcb->state == CLOSE_WAIT) || (pcb->state == CLOSED)) {
            if (conn->pcb.tcp != NULL) {
                tcp_arg(conn->pcb.tcp, NULL);
                tcp_accept(conn->pcb.tcp, NULL);
                tcp_recv(conn->pcb.tcp, NULL);      
                tcp_sent(conn->pcb.tcp, NULL);
                tcp_poll(conn->pcb.tcp, NULL, 4);
                tcp_err(conn->pcb.tcp, NULL);
                tcp_close(conn->pcb.tcp);       
                conn->state = NETCONN_STATE_NONE;
                net_send_event_to_hostif(conn, NET_EVENT_TCP_DISCONNECT);
                conn->pcb.tcp = NULL;
                net_free_socket(socketno);
            }
        }
    }

	if (conn){
		if(conn->client && conn->idle_time > 0)
		{
			TLS_DBGPRT_INFO("conn->skt_num=%d, conn->client=%d\n", conn->skt_num, conn->client);
	//		server_conn = dl_list_first(&conn->list, struct tls_netconn, list);
	        server_conn = get_server_conn(conn);
			if(server_conn)
			{
				--conn->idle_time;
				if(conn->idle_time == 0)
				{
					tcp_abort(pcb);
					//tls_socket_close(conn->skt_num);
					TLS_DBGPRT_INFO("update conn->idle_time %d\n", conn->idle_time);
					return ERR_ABRT;
				}
			}
		}
		if((conn->skd) != NULL && (conn->skd->pollf) != NULL)
		{
			err_ret = conn->skd->pollf(conn->skt_num);
			if(err_ret == ERR_ABRT)
				tcp_abort(pcb);
			return err_ret;
		}
	}

    return ERR_OK;

}

static void net_tcp_err_cb(void *arg, err_t err)
{
    struct tls_netconn *conn = NULL;
    struct tcp_pcb *pcb = NULL;  
	int socketno = (int)arg;
    u8 event = NET_EVENT_TCP_CONNECT_FAILED;

	conn = tls_net_get_socket(socketno);
	if(conn == NULL || TRUE != conn->used)
	{
		TLS_DBGPRT_ERR("\nconn=%x,used=%d\n",conn, conn->used);
		return;
	}
	pcb = conn->pcb.tcp;  
    TLS_DBGPRT_INFO("tcp err = %d, pcb==%x, conn==%x, skt==%x\n", err, (char *)pcb, (char *)conn, conn->skd);
	
    if (pcb) {
        tcp_arg(pcb, NULL);
        tcp_sent(pcb, NULL);
        tcp_recv(pcb, NULL);
        tcp_err(pcb, NULL);
		if (!conn->client) {
		    tcp_accept(pcb, NULL);
		}
		if(err == ERR_OK)
		{
	      tcp_close(pcb);
		}
		if(conn->state != NETCONN_STATE_NONE)
		{
           conn->state = NETCONN_STATE_NONE;
           event = NET_EVENT_TCP_DISCONNECT;
		}

		net_send_event_to_hostif(conn, event);
		if(conn->skd->errf != NULL)
		{
			conn->skd->errf(conn->skt_num, err);
		}
		conn->pcb.tcp = NULL;
        net_free_socket(socketno);
		//printf("\nfree socket\n");
    }
}

#if (RAW_SOCKET_USE_CUSTOM_PBUF)
static struct raw_sk_pbuf_custom*
raw_sk_alloc_pbuf_custom(void)
{
  return (struct raw_sk_pbuf_custom*)mem_malloc(sizeof(struct raw_sk_pbuf_custom));
}


static void
raw_sk_free_pbuf_custom(struct raw_sk_pbuf_custom* p)
{
	if(p != NULL)
	{
		mem_free(p);
		p = NULL;
	}
}

static void
raw_sk_free_pbuf_custom_fn(struct pbuf *p)
{
	struct raw_sk_pbuf_custom *pcr = (struct raw_sk_pbuf_custom*)p;

	if(p != NULL)
	{
		//printf("\nraw_sk_free_pbuf_custom_fn,len=%d, custom pcb=%x, conn=%x, conn pcb=%x\n\n",p->tot_len, pcr->pcb, pcr->conn, ((struct tls_netconn *)pcr->conn)->pcb.tcp);
		if(TRUE == ((struct tls_netconn *)pcr->conn)->used && pcr->pcb == ((struct tls_netconn *)pcr->conn)->pcb.tcp)
		{
			tcp_recved((struct tcp_pcb *)pcr->pcb, p->tot_len);
		}

		if (pcr->original != NULL) {
	//		printf("\ngo to pbuf_free,original pbuf=%x\n",pcr->original);
			pbuf_free(pcr->original);
		}
		raw_sk_free_pbuf_custom(pcr);
	}
}
	
#endif


/**
 * Receive callback function for TCP connect.
 */
static err_t net_tcp_recv_cb(void *arg, 
        struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{
    struct tls_netconn *conn;
    struct tls_netconn *server_conn = NULL;
    err_t err_ret = ERR_OK;
	u16 datalen = 0;
#if (RAW_SOCKET_USE_CUSTOM_PBUF)
	struct raw_sk_pbuf_custom *pcr = NULL;
	struct pbuf *newpbuf = NULL;
#endif
	struct pbuf *p_tmp,*p_next;
	int socketno = -1;

 //   TLS_DBGPRT_INFO("net_tcp_recv_cb err=%d\n", err);

    LWIP_UNUSED_ARG(pcb);
    LWIP_ASSERT("must have a pcb argument", pcb != NULL);
    LWIP_ASSERT("must have an argument", arg < 0);
	socketno = (int)arg;	
    conn = tls_net_get_socket(socketno);
	if(conn == NULL || TRUE != conn->used)
	{
		TLS_DBGPRT_ERR("\nconn=%x,used=%d\n",conn, conn->used);
		return ERR_ARG;
	}
    LWIP_ASSERT("tcp recv for wrong pcb!", conn->pcb.tcp == pcb);

    if (err) { 
        /* tcp is disconnect */
        TLS_DBGPRT_INFO("err code = %d\n", err);
		err_ret = err;
#if 0
        if (conn->pcb.tcp != NULL) {
            tcp_arg(conn->pcb.tcp, NULL);
            tcp_accept(conn->pcb.tcp, NULL);
            tcp_recv(conn->pcb.tcp, NULL);      
            tcp_sent(conn->pcb.tcp, NULL);
            tcp_poll(conn->pcb.tcp, NULL, 4);
            tcp_err(conn->pcb.tcp, NULL);
            tcp_close(conn->pcb.tcp);     
            conn->state = NETCONN_STATE_NONE;         
            net_send_event_to_hostif(conn, NET_EVENT_TCP_DISCONNECT);
            conn->pcb.tcp = NULL;
            net_free_socket(conn);
        }
#endif
		if(conn->skd != NULL && conn->skd->recvf != NULL)
		{
			TLS_DBGPRT_INFO("conn->skd->recvf to call.");
			err_ret = conn->skd->recvf(conn->skt_num, p, err);
			if(err_ret == ERR_ABRT)
				tcp_abort(pcb);
			p = NULL;
		}

	    if (p){
	        pbuf_free(p);
			p = NULL;
	    }
	    return err_ret;
    }

    if (pcb == NULL ||conn == NULL) {//p == NULL ||  
        TLS_DBGPRT_INFO("pcb = 0x%x, p = 0x%x\n", pcb ,p);
        return ERR_VAL;
    }

    if(p == NULL)
    {
        TLS_DBGPRT_ERR("received 0\n");
        net_tcp_err_cb((void *)socketno, ERR_OK);
        return ERR_OK;
    }
#if 0//(RAW_SOCKET_USE_CUSTOM_PBUF)
	pcr = raw_sk_alloc_pbuf_custom();
	if(pcr == NULL)
	{
		return ERR_MEM;
	}
	//newpbuf = pbuf_alloced_custom(PBUF_RAW, p->tot_len, PBUF_REF, &pcr->pc, p->payload, p->tot_len);

	newpbuf = &(pcr->pc);
	newpbuf->len = 0;
	newpbuf->payload = NULL;
	newpbuf->tot_len = p->tot_len;
	newpbuf->next = p;
	newpbuf->flags = PBUF_FLAG_IS_CUSTOM;
	newpbuf->type = PBUF_REF;
	newpbuf->ref =1;

      if (newpbuf == NULL) {
		raw_sk_free_pbuf_custom(pcr);
		return ERR_MEM;
      }
	pcr->original = p;
	pcr->param = pcb;
	pcr->pc.custom_free_function = raw_sk_free_pbuf_custom_fn;
#endif	
#if !RAW_SOCKET_USE_CUSTOM_PBUF
    tcp_recved(pcb, p->tot_len);
#endif
	
	if(conn->client && conn->idle_time > 0)
	{
		TLS_DBGPRT_INFO("conn->skt_num=%d, conn->client=%d\n", conn->skt_num, conn->client);
//		server_conn = dl_list_first(&conn->list, struct tls_netconn, list);
        server_conn = get_server_conn(conn);
		TLS_DBGPRT_INFO("server_conn=%p\n", server_conn);
		if(server_conn)
		{
			conn->idle_time = server_conn->idle_time;
			TLS_DBGPRT_INFO("update conn->idle_time %d\n", conn->idle_time);
		}
	}
    if(conn->skd->recvf != NULL)
    {
    	tls_net_set_sourceip(ip_addr_get_ip4_u32(&pcb->remote_ip));
	datalen = p->tot_len;
	if (conn->skd->recvwithipf != NULL){
		//conn->skd->recvwithipf(conn->skt_num, datalen, (u8 *)(&(pcb->remote_ip.addr)), pcb->remote_port, ERR_OK);
		err_ret = conn->skd->recvwithipf(conn->skt_num, datalen, (u8 *)(&(ip_2_ip4(&pcb->remote_ip)->addr)), pcb->remote_port, ERR_OK);
		if(err_ret == ERR_ABRT)
			tcp_abort(pcb);
	}

	p_next = p;
	for(p_tmp = p; p_tmp != NULL; )
	{
		p_next = p_tmp->next;
		p_tmp->next = NULL;
		if(err_ret == ERR_ABRT)
		{
			pbuf_free(p_tmp);
		}
		else if(err_ret == ERR_OK)
		{
			if(p_next != NULL)
				printf("\npbufcat p next=%x\n",(u32)p_next);
#if (RAW_SOCKET_USE_CUSTOM_PBUF)
			pcr = raw_sk_alloc_pbuf_custom();
			if(pcr == NULL)
			{
				return ERR_MEM;
			}
			
			newpbuf = pbuf_alloced_custom(PBUF_RAW, p_tmp->len, PBUF_REF, &pcr->pc, p_tmp->payload, p_tmp->len);
		      if (newpbuf == NULL) {
				raw_sk_free_pbuf_custom(pcr);
				return ERR_MEM;
	      		}
			pcr->original = p_tmp;
			pcr->conn = conn;
			pcr->pcb = pcb;
			pcr->pc.custom_free_function = raw_sk_free_pbuf_custom_fn;

			err_ret = conn->skd->recvf(conn->skt_num, newpbuf, ERR_OK);
#else
			err_ret = conn->skd->recvf(conn->skt_num, p_tmp, ERR_OK);
#endif
			if(err_ret == ERR_ABRT)
				tcp_abort(pcb);
		}
		p_tmp = p_next;
	}
		
    }
    else
    {
#if 0//(RAW_SOCKET_USE_CUSTOM_PBUF)
	raw_sk_free_pbuf_custom(pcr);
#endif
        if (p)
            pbuf_free(p);
    }

    return err_ret;
}

#if 0
static err_t net_tcp_sent_cb(void *arg, struct tcp_pcb *pcb, u16_t len)
{
    struct tls_netconn *conn = (struct tls_netconn *)arg;    

    //TLS_DBGPRT_INFO("s\n");
    if (conn->write_state && (conn->state == NETCONN_STATE_CONNECTED)) {
        net_do_writemore(conn);
    } else if (conn->state == NETCONN_STATE_CLOSED) {
        net_tcp_close_connect(conn);
    }

    return ERR_OK;
}
#endif

/**
 * tcp connnect callback
 */
static err_t net_tcp_connect_cb(void *arg, struct tcp_pcb *pcb, err_t err)
{
    struct tls_netconn *conn;
	err_t  err_ret = ERR_OK;
	int socketno = -1;

	socketno = (int)arg;
    conn = tls_net_get_socket(socketno);
	if(conn == NULL || conn->used != TRUE){
		TLS_DBGPRT_ERR("\nconn=%x,used=%d\n",conn, conn->used);
		tcp_abort(pcb);
        return ERR_ABRT;
	}
	//pcb = conn->pcb.tcp;
    if ((conn->proto == TLS_NETCONN_TCP) && (err == ERR_OK)) {
		TLS_DBGPRT_INFO("net_tcp_connect_cb =====> state : %d\n", pcb->state);

		conn->state = NETCONN_STATE_CONNECTED;
		net_send_event_to_hostif(conn, NET_EVENT_TCP_CONNECTED);
    } else {
        TLS_DBGPRT_INFO("the err is =%d\n", err);
    } 

	if(conn->skd != NULL && conn->skd->connf != NULL)
	{
		err_ret = conn->skd->connf(conn->skt_num, err);
		if(err_ret == ERR_ABRT)
			tcp_abort(pcb);
		return err_ret;
	}

    return err;
}

/**
 * Accept callback function for TCP netconns.
 */
static err_t net_tcp_accept_cb(void *arg, 
        struct tcp_pcb *newpcb, err_t err)
{
    err_t err_ret = ERR_OK;
    struct tls_netconn *conn = NULL;
    struct tls_netconn *newconn;
    struct tcp_pcb *pcb;
    u32 cpu_sr;
	int socketno = -1;

    //TLS_DBGPRT_INFO("=====>\n");
#if 0
    if (conn->tcp_connect_cnt >= TLS_MAX_SOCKET_NUM) {
        return ERR_ABRT;
    }
#endif
	socketno = (int)arg;
    conn = tls_net_get_socket(socketno);
	if(conn == NULL || conn->used != TRUE){
		TLS_DBGPRT_ERR("\nconn=%x,used=%d\n",conn, conn->used);
		tcp_abort(newpcb);
        return ERR_ABRT;
	}

	if (conn){
		tcp_accepted(conn->pcb.tcp);
	}


    newconn = net_alloc_socket(conn);
	//printf("\nnew conn==%x, new pcb==%x\n",newconn,newpcb);
    if (!newconn) {
        /* connection aborted */
        /* not happen */
		tcp_abort(newpcb);
		//printf("\r\nnet_tcp_accept_cb err\r\n");
        return ERR_ABRT;
    }

    //conn->tcp_connect_cnt++;

    newconn->used = true;
    newconn->pcb.tcp = newpcb;
    newconn->client = true;
    newconn->idle_time = conn->idle_time;
    newconn->proto = TLS_NETCONN_TCP;
    newconn->state = NETCONN_STATE_CONNECTED;
    pcb = newconn->pcb.tcp;
    ip_set_option(newconn->pcb.tcp, SOF_KEEPALIVE);
    newconn->skd = conn->skd;
    tcp_arg(pcb, (void *)(newconn->skt_num));
    tcp_recv(pcb, net_tcp_recv_cb);
    //tcp_sent(pcb, net_tcp_sent_cb);
    tcp_poll(pcb, net_tcp_poll_cb, 2);
    tcp_err(pcb, net_tcp_err_cb);
	cpu_sr = tls_os_set_critical();
	dl_list_add_tail(&conn->list, &newconn->list);
	tls_os_release_critical(cpu_sr);
    newconn->port = host_to_le16(pcb->remote_port);
    newconn->localport = conn->localport;
 //   ip_addr_set(&newconn->addr, &pcb->remote_ip);
    ip_addr_copy(newconn->addr, pcb->remote_ip);
    net_send_event_to_hostif(newconn, NET_EVENT_TCP_JOINED);
    if(conn->skd->acceptf != NULL)
	{
		err_ret =conn->skd->acceptf(newconn->skt_num, err);
		if(err_ret == ERR_ABRT)
		{
			tcp_abort(pcb);
		}
	} 
    //conn->state = NETCONN_STATE_CONNECTED;

    return err_ret;
}

/**
 * Receive callback function for UDP netconns.
 */
static void net_udp_recv_cb(void *arg, struct udp_pcb *pcb, 
        struct pbuf *p, const ip_addr_t *srcaddr, u16_t port)
{
    struct tls_netconn *conn;
	u32 datalen = 0;
	int socketno = -1;

    //TLS_DBGPRT_INFO("=====>\n");
    LWIP_UNUSED_ARG(pcb); /* only used for asserts... */
    LWIP_ASSERT("recv_udp must have a pcb argument", pcb != NULL);
    LWIP_ASSERT("recv_udp must have an argument", arg < 0);
	socketno = (int)arg;
    conn = tls_net_get_socket(socketno);
	if(conn == NULL || conn->used != TRUE || NULL == pcb){
		TLS_DBGPRT_ERR("\nconn=%x,used=%d\n",conn, conn->used);
		if (p != NULL)
	            pbuf_free(p);
		return;
	}
    LWIP_ASSERT("recv_udp: recv for wrong pcb!", conn->pcb.udp == pcb);
/*
    if (conn == NULL || pcb == NULL) {
	TLS_DBGPRT_INFO("if (conn == NULL || pcb == NULL) \n");
        if (p != NULL)
            pbuf_free(p);
        return;
    }
*/
    if(conn->skd->recvf != NULL)
    {
    	datalen = p->tot_len;
		/*if Address is broadcast, update source IP according to source address and source port according to sender*/
		
//		if ((conn->addr.addr == IPADDR_BROADCAST)||((conn->addr.addr & 0xFF) == 0xFF))
                if((ip_addr_get_ip4_u32(&conn->addr) == IPADDR_BROADCAST) || ((ip_addr_get_ip4_u32(&conn->addr) & 0xFF) == 0xFF))
		{
			tls_net_set_sourceip(ip_addr_get_ip4_u32(srcaddr));
			conn->port = port;
		}else{
			tls_net_set_sourceip(ip_addr_get_ip4_u32(&conn->addr));
		}


		if (conn->skd->recvwithipf != NULL){
			conn->skd->recvwithipf(conn->skt_num, datalen, (u8 *)(&(ip_2_ip4(srcaddr)->addr)), port, ERR_OK);
		}

        conn->skd->recvf(conn->skt_num, p, ERR_OK);
    }
    else
    {
        if (p){
            pbuf_free(p);
			p = NULL;
        }
    }

    return; 
}


/**
 * Start create TCP connection
 */
static err_t net_tcp_start(struct tls_netconn *conn)
{
    struct tcp_pcb *pcb;
    err_t  err;
    u16 localportnum = conn->localport;

	
    //TLS_DBGPRT_INFO("=====>\n");
    conn->pcb.tcp = tcp_new();
    pcb = conn->pcb.tcp;

    if (pcb == NULL) {
        TLS_DBGPRT_INFO("could not allocate tcp pcb\n");
        return ERR_VAL;
    }

    tcp_arg(pcb, (void *)(conn->skt_num));

    if (conn->client) {
        TLS_DBGPRT_INFO("pcb = 0x%x, conn->addr = 0x%x, port = %d, localport=%d\n",
                pcb, ip_addr_get_ip4_u32(&conn->addr), conn->port, conn->localport);
        tcp_err(pcb, net_tcp_err_cb);
        tcp_recv(pcb, net_tcp_recv_cb);
        //tcp_nagle_disable(pcb);
		if (pcb->recv != NULL) {
			TLS_DBGPRT_INFO("pcb->recv != NULL\n");
		}
	    //tcp_sent(pcb, net_tcp_sent_cb);
	    tcp_poll(pcb, net_tcp_poll_cb, 4);
		ip_set_option(pcb, SOF_KEEPALIVE);
		if(localportnum > 0 && localportnum <= 0xFFFF)
		{
			err = tcp_bind(pcb, IP_ADDR_ANY, localportnum);
			if (err != ERR_OK) 
			{
				TLS_DBGPRT_INFO("tcp bind failed %d\n", err);
	            return err;
			}
		}
        err = tcp_connect(pcb, &conn->addr, conn->port, net_tcp_connect_cb);
        if (err != ERR_OK) {
            TLS_DBGPRT_INFO("tcp connect failed %d\n", err);
            return err;
        }
		if (conn->localport == 0){
			conn->localport = pcb->local_port;
		}
    } else {
        TLS_DBGPRT_INFO("pcb ptr = 0x%x, conn->port = %d\n",
                pcb, conn->port);
		ip_set_option(pcb, SOF_REUSEADDR);
        err = tcp_bind(pcb, IP_ADDR_ANY, conn->port);
	 	if (err != ERR_OK) {
            TLS_DBGPRT_INFO("tcp bind failed %d\n", err);
            return err;
        }
        conn->pcb.tcp = tcp_listen(pcb);
        //ip_set_option(conn->pcb.tcp, SOF_KEEPALIVE);
        if (conn->pcb.tcp == NULL) {
			tcp_close(pcb);
            /* create tcp sever failed */
            TLS_DBGPRT_INFO("tcp listen failed\n");
            return ERR_VAL;
        }
		tcp_arg(conn->pcb.tcp, (void *)(conn->skt_num));
        tcp_accept(conn->pcb.tcp, net_tcp_accept_cb);
    }

    return ERR_OK;
}

/**
 * Start create UDP connection
 */
static err_t net_udp_start(struct tls_netconn *conn)
{
	struct udp_pcb *udppcb;
	u16 localportnum = conn->localport;

	udppcb = udp_new();

    if(udppcb == NULL) {
        return ERR_MEM;
    }	
	conn->pcb.udp = udppcb;
    if (udp_bind(udppcb, IP_ADDR_ANY, localportnum)!= ERR_OK){
        TLS_DBGPRT_INFO("udp connect failed\n");
        return ERR_CONN;
    }	

	if (conn->localport == 0){
		conn->localport = udppcb->local_port;
	}
	
    /* Set a receive callback for the upcb */
    udp_recv(udppcb, net_udp_recv_cb, (void *)(conn->skt_num));

    return ERR_OK;
}
#if 0
static err_t net_do_writemore(struct tls_netconn *conn)
{
    err_t err = ERR_OK;
    void *dataptr;
    u16 len, available;
    u8 write_finished = 0;
    u32 diff;
    u8 apiflags = TCP_WRITE_FLAG_COPY;
    u8 dontblock = 0;    

    dataptr = (u8_t*)conn->current_msg->dataptr + conn->current_msg->write_offset;
    diff = conn->current_msg->len - conn->current_msg->write_offset;
    if (diff > 0xffffUL) { /* max_u16_t */
        len = 0xffff;
        apiflags |= TCP_WRITE_FLAG_MORE;
    } else {
        len = (u16_t)diff;
    }
    available = tcp_sndbuf(conn->pcb.tcp);
    if (available < len) {
        /* don't try to write more than sendbuf */
        len = available;
        if (dontblock){ 
            if (!len) {
                err = ERR_WOULDBLOCK;
                goto err_mem;
            }
        } else {
            apiflags |= TCP_WRITE_FLAG_MORE;
        }
    }
    err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags);

err_mem:
    if (err == ERR_OK) {
        conn->current_msg->write_offset += len;
        if ((conn->current_msg->write_offset == conn->current_msg->len) || dontblock) {
            /* everything was written */
            write_finished = 1;
            conn->current_msg->write_offset = 0;
        }
        tcp_output(conn->pcb.tcp);
    } else if ((err == ERR_MEM) && !dontblock) {
        /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called
           we do NOT return to the application thread, since ERR_MEM is
           only a temporary error! */

        /* tcp_write returned ERR_MEM, try tcp_output anyway */
        tcp_output(conn->pcb.tcp);

    } else {
        /* On errors != ERR_MEM, we don't try writing any more but return
           the error to the application thread. */
        write_finished = 1;
        conn->current_msg->len = 0;
    }
    if (write_finished) {
        /* everything was written: set back connection state
           and back to application task */
        conn->current_msg->err = err;
        conn->write_state = false;

        tls_mem_free(conn->current_msg);
        conn->current_msg = NULL;
    }
    return ERR_OK;
}
#endif 

static err_t net_skt_tcp_send(struct tls_net_msg *net_msg)
{
	struct tcp_pcb *pcb = NULL;
	struct tls_netconn *conn;
	err_t err;
    //TLS_DBGPRT_INFO("=====>\n");
	conn = tls_net_get_socket(net_msg->skt_no);
	if(conn == NULL || TRUE != conn->used)
	{
		TLS_DBGPRT_ERR("conn =%x,used=%d\n", conn, conn->used);
		return ERR_ARG;
	}
	pcb = conn->pcb.tcp;
	/* 
		When tcp error occured, lwip will delete the pcb and sometimes GSKT.
		This function maybe registered by GSKT_TimerSend, so we must check if GSKT has been delted!!! 
	*/
	err = tcp_write(pcb, net_msg->dataptr, net_msg->len, TCP_WRITE_FLAG_COPY);
	if (err == ERR_OK){
		//sys_sem_signal(&net_msg->conn->op_completed);
		tcp_output(pcb);
	}
	else
	{
	//	TLS_DBGPRT_INFO("err:%d\n", err);
	}
	return err;
}


/**
 * Send data on a UDP pcb 
 */
static void net_do_send(void *ctx)
{
    struct tls_net_msg *msg = (struct tls_net_msg *)ctx;
    struct tls_netconn *conn = NULL;
    struct pbuf *p;
	int socketno = msg->skt_no;	

    //TLS_DBGPRT_INFO("=====>\n"); 

    p = msg->p;
	conn = tls_net_get_socket(socketno);
	if(conn == NULL || TRUE != conn->used)
	{
		TLS_DBGPRT_ERR("conn =%x,used=%d\n", conn, conn->used);
		if(p)
			pbuf_free(p);
#if CONN_SEM_NOT_FREE
                sys_sem_signal(&conn_op_completed[socketno - 1]);
#endif
			
		return;
	}
#if LWIP_CHECKSUM_ON_COPY
    if (ip_addr_isany(&msg->addr)) {
        msg->err = udp_send_chksum(conn->pcb.udp, p,
                0, 0);
    } else {
        msg->err = udp_sendto_chksum(conn->pcb.udp, p,
                &msg->addr, msg->port,
                0, 0);
    }
#else /* LWIP_CHECKSUM_ON_COPY */
    if (ip_addr_isany(&msg->addr)) {
        msg->err = udp_send(conn->pcb.udp, p);
    } else {
        msg->err = udp_sendto(conn->pcb.udp, p, 
                &msg->addr, msg->port);
    }
#endif /* LWIP_CHECKSUM_ON_COPY */

    pbuf_free(p);
#if CONN_SEM_NOT_FREE
        sys_sem_signal(&conn_op_completed[socketno - 1]);
#else
	conn = tls_net_get_socket(socketno);
	if(conn && TRUE == conn->used)
	{
		sys_sem_signal(&conn->op_completed);
	}
#endif
}

/**
 * Send data on a TCP pcb 
 */
static void net_do_write(void *ctx)
{
    struct tls_net_msg *net_msg = (struct tls_net_msg *)ctx;
    struct tls_netconn *conn = NULL;
	struct tls_netconn *server_conn = NULL;
	
	int socketno = -1;
    //TLS_DBGPRT_INFO("=====>\n");
    socketno = net_msg->skt_no;
	conn = tls_net_get_socket(socketno);
	if(conn == NULL ||TRUE != conn->used)
	{
		TLS_DBGPRT_ERR("\n conn=%x,used=%d\n",conn, conn->used);
#if CONN_SEM_NOT_FREE
        sys_sem_signal(&conn_op_completed[socketno - 1]);
#endif		
		return;
	}

    //TLS_DBGPRT_INFO("s=%d,p=0x%x\n", conn->state, conn->pcb.tcp);

#if 0
    if (ERR_IS_FATAL(conn->last_err)) {
        net_msg->err = conn->last_err;
    } 
#endif
    if (conn->proto == TLS_NETCONN_TCP) {
#if LWIP_TCP
        if (conn->state != NETCONN_STATE_CONNECTED) {
            /* netconn is connecting, closing or in blocking write */
            net_msg->err = ERR_INPROGRESS;
        } else if (conn->pcb.tcp != NULL) {
            conn->write_state = true;
            //conn->write_offset = 0;
            net_msg->err = net_skt_tcp_send(net_msg);
            /* for both cases: if do_writemore was called, don't ACK the APIMSG
               since do_writemore ACKs it! */
        } else {
            net_msg->err = ERR_CONN;
            TLS_DBGPRT_INFO("==>err=%d\n", net_msg->err);
        }
	if(conn->client && conn->idle_time > 0)
	{
		TLS_DBGPRT_INFO("conn->skt_num=%d, conn->client=%d\n", conn->skt_num, conn->client);
//		server_conn = dl_list_first(&conn->list, struct tls_netconn, list);
        server_conn = get_server_conn(conn);
		TLS_DBGPRT_INFO("server_conn=%p\n", server_conn);
		if(server_conn)
		{
			conn->idle_time = server_conn->idle_time;
			TLS_DBGPRT_INFO("update conn->idle_time %d\n", conn->idle_time);
		}
	}
#else /* LWIP_TCP */
        net_msg->err = ERR_VAL;
#endif /* LWIP_TCP */
#if (LWIP_UDP || LWIP_RAW)
    } else {
        net_msg->err = ERR_VAL;
#endif /* (LWIP_UDP || LWIP_RAW) */
    }
        //tls_mem_free(net_msg->dataptr);
    //if(net_msg->err != ERR_OK)
    {
        //TLS_DBGPRT_INFO("conn->proto=%d, err=%d\n", conn->proto, net_msg->err);
        //TLS_DBGPRT_INFO("free net_msg->dataptr=%p\n", net_msg->dataptr);

#if CONN_SEM_NOT_FREE
        sys_sem_signal(&conn_op_completed[socketno - 1]);
#else
	conn = tls_net_get_socket(socketno);
	if(conn && TRUE == conn->used)
	{
		sys_sem_signal(&conn->op_completed);
	}
#endif
    }
#if 0
    tls_mem_free(net_msg);
#endif
}

static void do_create_connect(void *ctx)
{
    struct tls_net_msg *net_msg = (struct tls_net_msg *)ctx;
    struct tls_netconn *conn;
	err_t  err;
	int socketno = -1;
    //TLS_DBGPRT_INFO("=====>\n");
    socketno = net_msg->skt_no;
	conn = tls_net_get_socket(socketno);
	if(conn == NULL || TRUE != conn->used)
	{
		tls_mem_free(net_msg);
		TLS_DBGPRT_ERR("\nconn=%x,usde=%d\n",conn, conn->used);
		return;
	}
    TLS_DBGPRT_INFO("conn ptr = 0x%x, conn->skt_num=%d, conn->client=%d\n", conn, conn->skt_num, conn->client);

    switch (conn->proto) {
        case TLS_NETCONN_UDP:
            err = net_udp_start(conn);
			if (err != ERR_OK){
				conn->state = NETCONN_STATE_NONE;
				net_send_event_to_hostif(conn, NET_EVENT_UDP_START_FAILED);
				net_free_socket(socketno);				
			}else{
				conn->state = NETCONN_STATE_CONNECTED;
				net_send_event_to_hostif(conn, NET_EVENT_UDP_START); 
			}
            break;
        case TLS_NETCONN_TCP:
            err = net_tcp_start(conn);
			if (err != ERR_OK){
				conn->state = NETCONN_STATE_NONE;
				net_send_event_to_hostif(conn, NET_EVENT_TCP_CONNECT_FAILED);
				net_free_socket(socketno);
			}else{
				if (!conn->client){
					net_send_event_to_hostif(conn, NET_EVENT_TCP_CONNECTED);
				}
				//conn->state = NETCONN_STATE_CONNECTED;
			}
            break;
        default:
            /* Unsupported netconn type, e.g. protocol disabled */
            break;
    }
    tls_mem_free(net_msg);
    return;
}

static void do_close_connect(void *ctx)
{
    struct tls_net_msg *net_msg = (struct tls_net_msg *)ctx;
    struct tls_netconn *conn = NULL;
    struct tls_netconn *client_conn;
	int socketno = net_msg->skt_no;
    int i;
    int sktNums[TLS_MAX_SOCKET_NUM] = {-1};// 2*TLS_MAX_NETCONN_NUM

    conn = tls_net_get_socket(socketno);
	if(conn == NULL || TRUE != conn->used)
	{
		TLS_DBGPRT_ERR("conn==%x,used=%d\n",conn, conn->used);	
		tls_mem_free(net_msg);
        return;
	}

    switch (conn->proto) {
        case TLS_NETCONN_UDP:
            if (conn->pcb.udp != NULL) {
                conn->pcb.udp->recv_arg = NULL;
                udp_remove(conn->pcb.udp);
                conn->pcb.udp = NULL;
            }
	     	conn->state = NETCONN_STATE_CLOSED;
            break;
        case TLS_NETCONN_TCP:
            if (!conn->client) {
                /* it's a server, close connected client */
				i=0;
				dl_list_for_each(client_conn, &conn->list, struct tls_netconn, list){
                    if (client_conn->used) {
                        client_conn->state = NETCONN_STATE_CLOSED;
						sktNums[i++] = client_conn->skt_num;
                    }
		        }

				while(i-->0)
				{
				 	//client_conn = tls_net_get_socket(sktNums[i]);
					net_tcp_close_connect(sktNums[i]); 
				}
            }

            conn->state = NETCONN_STATE_CLOSED;
            net_tcp_close_connect(socketno);
            break;
        default:
            break;
    }
    net_free_socket(socketno);
    tls_mem_free(net_msg);
}

int tls_socket_create(struct tls_socket_desc * skd)
{
    struct tls_netconn *conn = NULL;
    struct tls_net_msg *net_msg;
    err_t err = ERR_OK;

    //TLS_DBGPRT_INFO("=====>socket_type=%d, skd->protocol=%d\n", socket_type, skd->protocol);

    if ((skd->protocol == SOCKET_PROTO_TCP) || (skd->protocol == SOCKET_PROTO_UDP)) {
        conn = net_alloc_socket(NULL);

        if (conn) {
            conn->client = (skd->cs_mode == SOCKET_CS_MODE_CLIENT);
            conn->proto = skd->protocol;
            if (conn->client){
                IP_SET_TYPE(&skd->ip_addr, IPADDR_TYPE_V4);
                ip_addr_copy(conn->addr, skd->ip_addr);
                conn->state = NETCONN_STATE_NONE;
            }
            else {
                if(conn->proto == 1)
                    ip_addr_set(&conn->addr, IP_ADDR_BROADCAST);
                else
                    ip_addr_set(&conn->addr, IP_ADDR_ANY);  
				conn->state = NETCONN_STATE_WAITING;
            }
            conn->port = skd->port;
	     	conn->localport = skd->localport;
            conn->idle_time = skd->timeout;
	     	conn->skd = skd;
            //conn->cmd_mode = cmd_mode;

            net_msg = tls_mem_alloc(sizeof(*net_msg));
            if (net_msg == NULL) {
                net_free_socket(conn->skt_num);
                return -1;
            }
            memset(net_msg, 0, sizeof(*net_msg));
            net_msg->skt_no = conn->skt_num;
            err = tcpip_callback_with_block(do_create_connect, net_msg, 0);
            if (err) {
                conn->state = NETCONN_STATE_NONE;
                net_free_socket(conn->skt_num);
                tls_mem_free(net_msg);
            }
        } else {
            return 0;
        }
    } else {
        err = ERR_VAL;
    }

    if (conn)
        return conn->skt_num; 
    else 
        return -1;
}

int tls_socket_get_status(u8 socket, u8 *buf, u32 bufsize)
{
    struct tls_skt_status_t *skt_status;
    struct tls_skt_status_ext_t *skts_ext;
    struct tls_netconn *conn;
    struct tls_netconn *client_conn;
    struct tls_netconn *server_conn;
    int remain_len;

    skt_status = (struct tls_skt_status_t *)buf;

    if (bufsize < sizeof(struct tls_skt_status_t))
    	{
    		TLS_DBGPRT_ERR("\nbufsize=%d\n",bufsize);
        return ERR_VAL; 
    	}

    if (socket < 1 || socket > 20)
    	{
	    	TLS_DBGPRT_ERR("\nskt num=%d\n",socket);
        return ERR_VAL;
    	}

    memset(buf, 0, bufsize);

    conn = tls_net_get_socket(socket);
	if(conn == NULL || TRUE != conn->used)
	{
		TLS_DBGPRT_ERR("conn==%x,used=%d\n",conn, conn->used);	
        	return ERR_VAL;
	}
    remain_len = bufsize - sizeof(u32);
    skts_ext = (struct tls_skt_status_ext_t *)(buf + sizeof(u32));
	if(conn == NULL){
		skt_status->socket_cnt = 1;
		skts_ext->protocol = SOCKET_PROTO_TCP;
		skts_ext->status = 0;
		skts_ext->socket = socket;
		skts_ext->host_ipaddr[0] = 0;
		skts_ext->host_ipaddr[1] = 0;
		skts_ext->host_ipaddr[2] = 0;
		skts_ext->host_ipaddr[3] = 0;
		return 0;
	}
    skts_ext->protocol = (enum tls_socket_protocol)conn->proto;
//TLS_DBGPRT_INFO("conn->client=%d, conn->state=%d, conn->used=%d\n", conn->client, conn->state,conn->used);
    if (conn->client) {
        skt_status->socket_cnt = 1;
        if (conn->used) {
            MEMCPY(skts_ext->host_ipaddr, (u8 *)ip_2_ip4(&conn->addr), 4);
//            server_conn = dl_list_first(&conn->list, struct tls_netconn, list);
            server_conn = get_server_conn(conn);
            if(server_conn != NULL){
           //     TLS_DBGPRT_INFO("server_conn->skt_num = %d\n", server_conn->skt_num);
                skts_ext->local_port = server_conn->localport;
            }
            else
                skts_ext->local_port = conn->localport;
            skts_ext->remote_port = conn->port; 
            skts_ext->status = conn->state; /* connected */
            skts_ext->socket = conn->skt_num; 
        } else {
            skts_ext->status = conn->state;   /* disconnect */ 
            skts_ext->socket = conn->skt_num;
        }
//TLS_DBGPRT_INFO("skts_ext->socket=%d, skts_ext->status=%d, conn->state=%d\n", skts_ext->socket, skts_ext->status, conn->state);
    } else {
    	struct tls_ethif * ethif;
		ethif = tls_netif_get_ethif();
        skt_status->socket_cnt = 1;
        if (conn->used) {
            skts_ext->status = conn->state; /* listen */
            //MEMCPY(skts_ext->host_ipaddr, &conn->addr.addr, 4);
            //MEMCPY(skts_ext->host_ipaddr, (char *)&sys->ethif->nif->ip_addr.addr, 4);
            //MEMCPY(&conn->addr.addr, (char *)&sys->ethif->nif->ip_addr.addr, 4);
            //MEMCPY(skts_ext->host_ipaddr, (char *)&ethif->ip_addr.addr, 4);
            MEMCPY(skts_ext->host_ipaddr, (char *)(ip_2_ip4(&ethif->ip_addr)), 4);
            if(conn->proto == 1){
                skts_ext->remote_port = conn->port;
                skts_ext->local_port = conn->localport;
                if(skts_ext->local_port==0)
                    skts_ext->local_port = skts_ext->remote_port;
                MEMCPY(skts_ext->host_ipaddr, IP_ADDR_BROADCAST, 4);
            }else{
                skts_ext->local_port = conn->port;
                skts_ext->remote_port = 0;
            }
            skts_ext->socket = conn->skt_num;

            remain_len -= sizeof(struct tls_skt_status_ext_t);
            
	     	dl_list_for_each(client_conn, &conn->list, struct tls_netconn, list){
                if (remain_len < sizeof(struct tls_skt_status_ext_t))
                    break;
                if (client_conn->used) {
                    skts_ext++;
                    skts_ext->status = client_conn->state; /* connect */
                    skt_status->socket_cnt++;
                    MEMCPY(skts_ext->host_ipaddr, (char *)ip_2_ip4(&client_conn->addr), 4);
                    skts_ext->local_port = conn->port;
                    skts_ext->remote_port = client_conn->port; 
                    skts_ext->socket = client_conn->skt_num;
                    skts_ext->protocol = (enum tls_socket_protocol)client_conn->proto;
					remain_len -= sizeof(struct tls_skt_status_ext_t);
                }
            }
        } else {
            skts_ext->status = conn->state;
            skts_ext->socket = conn->skt_num;
        }
    }
    return 0;
}

struct tls_netconn * tls_net_get_socket(u8 socket)
{
    struct tls_netconn *conn = NULL;

    if (socket < 1 || socket > TLS_MAX_NETCONN_NUM )//* 2
    	{
    		TLS_DBGPRT_ERR("skt num=%d\n", socket);	
        return NULL;
    	}

    conn = p_net_conn[socket - 1];
	//TLS_DBGPRT_INFO("conn->skt_num = %d\n", conn->skt_num);
    return conn;
}

int tls_socket_close(u8 socket)
{
    struct tls_net_msg *net_msg;
    struct tls_netconn *conn;
    err_t err;
//TLS_DBGPRT_INFO("socket=%d.\n", socket);
    if (socket < 1 || socket > TLS_MAX_NETCONN_NUM)//*2
    	{
    		TLS_DBGPRT_ERR("skt num=%d\n", socket);	
        	return -1;
    	}

    conn = tls_net_get_socket(socket);

	if(conn == NULL || TRUE != conn->used)
	{
		TLS_DBGPRT_ERR("conn==%x,used=%d\n",conn, conn->used);	
		return -1;
	}
    net_msg = tls_mem_alloc(sizeof(*net_msg));
    if (net_msg == NULL) {
	TLS_DBGPRT_ERR("\nmem err\n");
        return -1;
    }
    memset(net_msg, 0, sizeof(*net_msg));
    net_msg->skt_no = socket;
    err = tcpip_callback_with_block(do_close_connect, net_msg, 0);
    if (err) {
        tls_mem_free(net_msg);
    }

    return err;

}

static err_t netconn_msg(tcpip_callback_fn function, struct tls_net_msg *net_msg, u8_t block)
{
	err_t err = ERR_OK;
	struct tls_netconn *conn = NULL;
	conn = tls_net_get_socket(net_msg->skt_no);
	if(conn == NULL || TRUE != conn->used)
	{
		TLS_DBGPRT_ERR("netconn_msg conn=%x,used=%d\n",conn, conn->used);
		return ERR_ARG;
	}
#if 1
	err = tcpip_callback_with_block(function, net_msg, block);
	if(err)
	{
		//TLS_DBGPRT_INFO("free net_msg->dataptr=%p\n", net_msg->dataptr);
		//tls_mem_free(net_msg->dataptr);
		TLS_DBGPRT_INFO("callback end, err=%d\n", err);
	//	tls_mem_free(net_msg);
		return err;
	}
#if CONN_SEM_NOT_FREE	
    sys_arch_sem_wait(&conn_op_completed[net_msg->skt_no - 1], 0);
#else
	conn = tls_net_get_socket(net_msg->skt_no);
    if(conn !=  NULL && conn->op_completed != NULL)
    {
    	sys_arch_sem_wait(&conn->op_completed, 0);
    	conn = tls_net_get_socket(net_msg->skt_no);
    	if(conn == NULL || TRUE != conn->used)
    	{
    		printf("wait error, conn is freeed\n");
    	}
    }
#endif        
	err = net_msg->err;
	return err;
#else
	struct tls_net_msg *net_msg_item;
	if(conn->current_msg == NULL)
	{
		conn->current_msg = net_msg;
	}
	else
	{
		dl_list_add_tail(&conn->current_msg->list, &net_msg->list);
	}
	//TLS_DBGPRT_INFO("callback\n");
	err = tcpip_callback_with_block(function, conn->current_msg, block);
	if(err)
	{
		TLS_DBGPRT_INFO("callback end, err=%d\n", err);
		return err;
	}
	sys_arch_sem_wait(&conn->op_completed, 0);
	//TLS_DBGPRT_INFO("callback end\n");
	if(conn->current_msg->err)
		return conn->current_msg->err;
		
	dl_list_for_each(net_msg_item, &conn->current_msg->list, struct tls_net_msg, list){
		err = tcpip_callback_with_block(function, net_msg_item, block);
		if(err)
			break;
		sys_arch_sem_wait(&conn->op_completed, 0);
		if(net_msg_item->err)
			break;
		dl_list_del(&net_msg_item->list);
		tls_mem_free(net_msg_item);
	}
	if(dl_list_empty(&conn->current_msg->list))
	{
		tls_mem_free(conn->current_msg);
		conn->current_msg = NULL;
	}
	else
	{
		tls_mem_free(conn->current_msg);
		conn->current_msg = net_msg_item;
	}
	return ERR_OK;
#endif
}


int tls_socket_udp_sendto(u16 localport, u8  *ip_addr, u16 port, void *pdata, u16 len)
{
    struct tls_netconn *conn;
    struct tls_net_msg *net_msg = NULL;
    struct pbuf *p;
    int i;
    int err = 0;
    bool found = false;

	/* find match socket */
	for (i = 0; i<TLS_MAX_NETCONN_NUM; i++) {// * 2
	    conn = tls_net_get_socket(i);
	    if (conn != NULL 
			&& conn->used 
			&& (localport== conn->port) 
			&& (conn->proto == TLS_NETCONN_UDP)) {
	        found = true;
	        break;
	    }
	}
	if (!found) {
		TLS_DBGPRT_ERR("\nfound=0\n");
	    err = -1;
	    goto out;
	}

	net_msg = tls_mem_alloc(sizeof(struct tls_net_msg));
	if(!net_msg)
		return ERR_MEM;

	net_msg->len = len;
	net_msg->dataptr = pdata;

    /* udp */
    while (1) {
        p = pbuf_alloc(PBUF_TRANSPORT, net_msg->len, PBUF_REF);
        if (p != NULL) {
            break;
        } else {
            /* delay 1 ticks */
            tls_os_time_delay(1);
        } 
    }
    p->payload = (void*)net_msg->dataptr;
    p->len = p->tot_len = net_msg->len;

    net_msg->err = ERR_VAL;/* for debug : catch not set err */
    net_msg->p = p;
    net_msg->port = port;
    net_msg->skt_no = conn->skt_num;
    MEMCPY((char *)&(ip_2_ip4(&net_msg->addr)->addr), ip_addr, 4);
    IP_SET_TYPE(&net_msg->addr, IPADDR_TYPE_V4);
    err = netconn_msg(net_do_send, net_msg, 0);
out:
    if( net_msg)	/*Who allocates free it*/
        tls_mem_free(net_msg);
    return err;
}


int tls_socket_send(u8 skt_num, void *pdata, u16 len)
{
    struct tls_net_msg net_msg[1] = {0};
    struct tls_netconn *conn;
    struct tls_netconn *client_conn;
    struct pbuf *p;
    err_t err = ERR_OK;
    u32 socket;
    u32 errornum = 0;	

    socket = skt_num;

	dl_list_init(&net_msg->list);
	net_msg->len = len;
	net_msg->dataptr = pdata;
    if (socket < 1 || socket > TLS_MAX_NETCONN_NUM)//*2
    {
        TLS_DBGPRT_ERR("\nskt num=%d\n",socket);
        return ERR_VAL;
    }
	
    conn = tls_net_get_socket(socket);
    if (conn == NULL || TRUE != conn->used)
    {
        TLS_DBGPRT_ERR("\nconn=%x,used=%d\n",conn, conn->used);
        return ERR_VAL;
    }
    if (conn->proto == TLS_NETCONN_UDP) 
    {
        net_msg->skt_no = skt_num;
        net_msg->err = ERR_VAL;/* for debug : catch not set err */
        do{
            p = pbuf_alloc(PBUF_TRANSPORT, net_msg->len, PBUF_REF);
            if (p != NULL) {
                break;
            } else {
                TLS_DBGPRT_INFO("pbuf_alloc err.\n");
                return ERR_VAL;
            } 
        }while (0);
        p->payload = (void*)net_msg->dataptr;
        p->len = p->tot_len = net_msg->len;

        net_msg->p = p;
        net_msg->port = conn->port;
    //    ip_addr_set(&net_msg->addr, &conn->addr);
		ip_addr_copy(net_msg->addr, conn->addr);

        err = netconn_msg(net_do_send, net_msg, 0);		
    }
    else if (conn->proto == TLS_NETCONN_TCP) 
    {
    	if (conn->client)
        {
            net_msg->skt_no = skt_num;
	        net_msg->err = ERR_VAL;/* for debug : catch not set err */
            err = netconn_msg(net_do_write, net_msg, 0);
    	}
        else
        {
    		dl_list_for_each(client_conn, &conn->list, struct tls_netconn, list)
            {
    			//printf("\nclient conn=%x,used==%d, sock num=%d, net conn=%x\n",client_conn, client_conn->used, client_conn->skt_num, p_net_conn[client_conn->skt_num - 1]);
    			if (client_conn && TRUE == client_conn->used && client_conn == p_net_conn[client_conn->skt_num - 1])
                {
    				net_msg->skt_no = client_conn->skt_num;
	        		net_msg->err = ERR_VAL;/* for debug : catch not set err */
	 	           	err = netconn_msg(net_do_write, net_msg, 0);
    				if(err)
    				{
    			//		TLS_DBGPRT_ERR("\nerr:%d\n",err);
    				}
    			}
    			else
    			{
    				struct tls_netconn *conn_tmp;
    				conn_tmp = tls_net_get_socket(socket);
    				errornum ++;					
    				if(NULL == conn_tmp || errornum >= 4)
    				{
    					TLS_DBGPRT_ERR("\nconn=%x, errnum=%d\n", conn_tmp, errornum);
    					err = ERR_VAL;
    					break;
    				}
    			}
    		}
			
    	}
    } 
    else 
    {
        err = -1;
    }
    return err;
}

int tls_net_init()
{
    //int i;
    memset(p_net_conn, 0, sizeof(struct tls_netconn *) * TLS_MAX_NETCONN_NUM);
    return 0;
}

#endif //TLS_CONFIG_SOCKET_RAW

