Free (GPLv2) TCP/IP stack developed by TASS Belgium

Dependents:   lpc1768-picotcp-demo ZeroMQ_PicoTCP_Publisher_demo TCPSocket_HelloWorld_PicoTCP Pico_TCP_UDP_Test ... more

PicoTCP. Copyright (c) 2013 TASS Belgium NV.

Released under the GNU General Public License, version 2.

Different licensing models may exist, at the sole discretion of the Copyright holders.

Official homepage: http://www.picotcp.com

Bug tracker: https://github.com/tass-belgium/picotcp/issues

Development steps:

  • initial integration with mbed RTOS
  • generic mbed Ethernet driver
  • high performance NXP LPC1768 specific Ethernet driver
  • Multi-threading support for mbed RTOS
  • Berkeley sockets and integration with the New Socket API
  • Fork of the apps running on top of the New Socket API
  • Scheduling optimizations
  • Debugging/benchmarking/testing

Demo application (measuring TCP sender performance):

Import programlpc1768-picotcp-demo

A PicoTCP demo app testing the ethernet throughput on the lpc1768 mbed board.

Socket/bsd/stack_endpoint.cpp

Committer:
tass
Date:
2016-01-28
Revision:
155:a70f34550c34
Parent:
153:5e8a725dbd6e

File content as of revision 155:a70f34550c34:

/*********************************************************************
PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
See LICENSE and COPYING for usage.

Authors: Daniele Lacamera
*********************************************************************/

#include "wrapper.h"
#include "rtos.h"
#include "cmsis_os.h"
#include "mbed.h"
#include "Socket.h"
#include "Mutex.h"
//#include "PicoTerm.h"

extern "C"
{
    #include "pico_mbed.h"
    #include "pico_dns_client.h"
    #include "pico_dhcp_server.h"
}

//#define ptsock_dbg ptm_dbg
#define ptsock_dbg(...)
#define SCHEDULER_BASE  4u
#define SCHEDULER_MAX   10

int scheduler_timeout = 0;
int in_the_stack = 0;
Mutex *PicoTcpLock;
Queue<void,32> *PicoTcpEvents;

static Thread * serverThread = NULL;

/* Testing ng blocking mechanism */

/* 
 * backend of select function, used in blocking (like picotcp_read()...) 
 * calls. Sleeps on the message queue 
 * 
 *
 * WARNING: PicoTcpLock (big stack lock) must be acquired before entering this.
 */
void picotcp_async_clean(struct pico_device * dev);
static inline int __critical_select(struct stack_endpoint *ep, uint32_t time, uint8_t lock)
{
  uint32_t in_time = PICO_TIME_MS();
  ptsock_dbg("Critical_select on endpoint.\n");
  if(lock) PicoTcpLock->unlock();
  while ((ep->events & ep->revents) == 0) {
    ep->queue->get(time);
    ptsock_dbg("In while loop.\n");
    if ((time != osWaitForever) && (PICO_TIME_MS() > in_time + time)) {
        ptsock_dbg("TIMEOUT in critical select... (ev:%04x rev:%04x \n", ep->events, ep->revents);
        if(lock) PicoTcpLock->lock();
        return 0;
    }
  }
  ptsock_dbg("After while loop.\n");
  if(lock) PicoTcpLock->lock();
  ptsock_dbg("After while loop, returning now.\n");
  return 1;
}

static void wakeup(uint16_t ev, struct pico_socket *s)
{
  struct stack_endpoint *ep = (struct stack_endpoint *)s->priv;
  if (!ep) {
    ptsock_dbg("WAKEUP: socket not found! ev=%04x\n", ev);
    return;
  }
  //if (((ep->revents & PICO_SOCK_EV_RD) == 0) && (ev & PICO_SOCK_EV_RD))
  //  printf("Activating RD\n");
  if(!ep->s || !ep->queue)
  {
    ptsock_dbg("Endpoint null warning : ep=%p, ep->s=%p, ep->queue=%p\n",ep,ep->s,ep->queue);
    return;
  }

  ptsock_dbg("RECEIVED EVENT: %d\n",ev);

  ep->revents |= ev;
    
  if(ev & PICO_SOCK_EV_ERR)
  {
    if(pico_err == PICO_ERR_ECONNRESET)
    {   
      ptsock_dbg("Connection reset by peer...\n");
      ep->state = SOCK_RESET_BY_PEER;
      pico_socket_close(ep->s);
      ep->s->priv = NULL;
    }
  }
  if ((ev & PICO_SOCK_EV_CLOSE) || (ev & PICO_SOCK_EV_FIN)) {
    ptsock_dbg("GOT FIN or CLOSE event.\n");
    ep->connected = 0;
    pico_socket_close(ep->s);
    ep->s->priv = NULL;
    ep->state = SOCK_CLOSED;
  }
  if (ev & PICO_SOCK_EV_CONN) {
      ep->connected = 1;
      ep->state = SOCK_CONNECTED;
  }
  ep->queue->put((void *)0);
}


struct stack_endpoint *picotcp_socket(uint16_t net, uint16_t proto, uint16_t timeout)
{
  struct stack_endpoint *ep = (struct stack_endpoint *)pico_zalloc(sizeof(struct stack_endpoint));
  uint16_t p_net = ((net == AF_INET6)?PICO_PROTO_IPV6:PICO_PROTO_IPV4);
  uint16_t p_proto = ((proto == SOCK_DGRAM)?PICO_PROTO_UDP:PICO_PROTO_TCP);
  PicoTcpLock->lock();
  ep->s = pico_socket_open( p_net, p_proto, &wakeup );
  if (ep->s == NULL) {
    delete(ep->queue);
    pico_free(ep);
    ep = NULL;
    ptsock_dbg("Error opening socket!\n");
  } else {
    ep->s->priv = ep;
    ptsock_dbg("Added socket (open)\n");
    ep->state = SOCK_OPEN;
    ep->queue = new Queue<void,1>();
  }
  PicoTcpLock->unlock();
  return ep;
}


int picotcp_state(struct stack_endpoint *ep)
{
    return ep->state;
}

int picotcp_bind(struct stack_endpoint *ep, struct sockaddr *_local_addr, socklen_t len)
{
  int ret;
  struct sockaddr_in *local_addr;
  local_addr = (struct sockaddr_in *)_local_addr;
  
  PicoTcpLock->lock();
  ret = pico_socket_bind(ep->s, (struct pico_ip4 *)(&local_addr->sin_addr.s_addr), &local_addr->sin_port);
  if(ret == 0)
    ep->state = SOCK_BOUND;
  PicoTcpLock->unlock();
  return ret;
}

int picotcp_listen(struct stack_endpoint *ep, int queue)
{
  int ret;
  PicoTcpLock->lock();
  ret = pico_socket_listen(ep->s, queue);
  
  if(ret == 0)
    ep->state = SOCK_LISTEN;
  PicoTcpLock->unlock();
  return ret;
}

int picotcp_connect(struct stack_endpoint *ep, struct sockaddr *_srv_addr, socklen_t len)
{
  int retval;
  struct sockaddr_in *srv_addr;
  srv_addr = (struct sockaddr_in *)_srv_addr;
  PicoTcpLock->lock();
  pico_socket_connect(ep->s, (struct pico_ip4 *)(&srv_addr->sin_addr.s_addr), srv_addr->sin_port);
  ep->events = PICO_SOCK_EV_CONN | PICO_SOCK_EV_ERR | PICO_SOCK_EV_FIN;
  ptsock_dbg("Going to perform cirital_select on endpoint.\n");
  __critical_select(ep, osWaitForever, 1);
  if ((ep->revents & PICO_SOCK_EV_CONN) && ep->connected) {
    ep->revents &= (~PICO_SOCK_EV_CONN);
    ep->revents |= PICO_SOCK_EV_WR;
    ptsock_dbg("Established. sock state: %x\n", ep->s->state);
    ep->state = SOCK_CONNECTED;
    retval = 0;
  } else {
    retval = -1;
  }
  ptsock_dbg("Returning %d from picotcp_connect.\n", retval);
  PicoTcpLock->unlock();
  return retval;
}

struct stack_endpoint *picotcp_accept(struct stack_endpoint *ep, struct sockaddr *_cli_addr, socklen_t *len)
{
  struct sockaddr_in *cli_addr = (struct sockaddr_in *)_cli_addr;
  struct stack_endpoint *aep = (struct stack_endpoint *) pico_zalloc(sizeof(struct stack_endpoint));
  if (aep)
    aep->queue = new Queue<void,1>();
  else
    return aep;
  
  PicoTcpLock->lock();
  ep->events = PICO_SOCK_EV_CONN | PICO_SOCK_EV_ERR;
  __critical_select(ep, osWaitForever, 1);
  if (ep->revents & PICO_SOCK_EV_CONN) {
    ptsock_dbg("Calling Accept\n");
    aep->s = pico_socket_accept(ep->s, (struct pico_ip4 *)(&cli_addr->sin_addr.s_addr), &cli_addr->sin_port);
    ptsock_dbg("Accept returned\n");
    aep->s->priv = aep;
    ep->revents &= (~PICO_SOCK_EV_CONN);
    aep->revents = 0; // set this to 0 to allow seq connections
    aep->revents |= PICO_SOCK_EV_WR;
    aep->state = SOCK_CONNECTED;
    ptsock_dbg("Added socket (accept)\n");
    
    *len = sizeof(struct sockaddr_in);
    ptsock_dbg("Established. sock state: %x\n", aep->s->state);
  } else {
    pico_free(aep);
    aep = NULL;
  }
  PicoTcpLock->unlock();
  return aep;
}

int picotcp_select(struct stack_endpoint *ep, struct timeval *timeout, int read, int write)
{
  int ret;
  ep->timeout |= timeout->tv_sec * 1000 + timeout->tv_usec / 1000;
  ep->events = PICO_SOCK_EV_ERR;
  ep->events |= PICO_SOCK_EV_FIN;
  ep->events |= PICO_SOCK_EV_CLOSE;
  ep->events |= PICO_SOCK_EV_CONN;
  if (read) {
    ep->events |= PICO_SOCK_EV_RD;
  }
  if (write)
    ep->events |= PICO_SOCK_EV_WR;
  ret = __critical_select(ep, ep->timeout, 0);
  return ret;
}

int picotcp_send(struct stack_endpoint *ep,void * buf, int len, int flags)
{
  int retval = 0;
  int tot_len = 0;
  if (!buf || (len <= 0))
     return  0;
  PicoTcpLock->lock();
  while (tot_len < len) {
    retval = pico_socket_send(ep->s, ((uint8_t *)buf) + tot_len ,  len - tot_len);
    if (retval == 0) {
        if (tot_len < len)
            ep->revents &= ~PICO_SOCK_EV_WR;
        break;
    }
    if (retval < 0) {
       if(tot_len == 0) tot_len = -1;
       break;
    }
    tot_len += retval;
  }
  PicoTcpLock->unlock();
  picotcp_async_interrupt(NULL);
  return tot_len; 
}

int picotcp_recv(struct stack_endpoint *ep,void * buf, int len, int flags)
{
  int retval = 0;
  int tot_len = 0;
  if (!buf || (len <= 0))
     return  0;
  PicoTcpLock->lock();
  while (tot_len < len) {
    retval = pico_socket_recv(ep->s, ((uint8_t *)buf) + tot_len ,  len - tot_len);
    if (retval == 0) {
        if (tot_len < len)
            ep->revents &= ~PICO_SOCK_EV_RD;
        break;
    }
    if (retval < 0) {
       if(tot_len == 0) tot_len = -1;
       break;
    }
    tot_len += retval;
  }
  PicoTcpLock->unlock();
  picotcp_async_interrupt(NULL);
  return tot_len;
}

int picotcp_sendto(struct stack_endpoint * ep,void * buf, int len, struct sockaddr* a,socklen_t size)
{
  int retval = 0;
  int tot_len = 0;
  struct sockaddr_in * in = (struct sockaddr_in *)a;
  if (!buf || (len <= 0))
     return  0;
  if(!ep->broadcast && pico_ipv4_is_broadcast(in->sin_addr.s_addr))
     return -1;
     
  PicoTcpLock->lock();
  while (tot_len < len) {
    retval = pico_socket_sendto(ep->s, ((uint8_t *)buf) + tot_len ,  len - tot_len,(struct pico_ip4 *)&in->sin_addr.s_addr, in->sin_port);
    
    if (retval == 0)
        break;
    
    if (retval < 0) {
       if(tot_len == 0) tot_len = -1;
       break;
    }
    tot_len += retval;
  }
  PicoTcpLock->unlock();
  picotcp_async_interrupt(NULL);
  return tot_len; 
}

int picotcp_recvfrom(struct stack_endpoint *ep,void * buf, int len, struct sockaddr *a, socklen_t * size)
{
  (void)len;
  int retval = 0;
  int tot_len = 0;
  struct sockaddr_in * in = (struct sockaddr_in *)a;
  if (!buf || (len <= 0))
     return  0;
  PicoTcpLock->lock();
  while (tot_len < len) {
    retval = pico_socket_recvfrom(ep->s, ((uint8_t *)buf) + tot_len ,  len - tot_len,(struct pico_ip4 *)&(in->sin_addr.s_addr),&in->sin_port);
    if (retval == 0) {
        if (tot_len < len)
            ep->revents &= ~PICO_SOCK_EV_RD;
        break;
    }
    if (retval < 0) {
       if(tot_len == 0) tot_len = -1;
       break;
    }
    tot_len += retval;
    break;
  }
  PicoTcpLock->unlock();
  picotcp_async_interrupt(NULL);
  return tot_len;
}

int picotcp_read(struct stack_endpoint *ep,void *buf, int len)
{
  int retval = 0;
  int tot_len = 0;
  if (!buf || (len <= 0))
     return  0;
  PicoTcpLock->lock();
  while (tot_len < len) {
    retval = pico_socket_read(ep->s, ((uint8_t *)buf) + tot_len ,  len - tot_len);
    if (retval == 0) {
        if (tot_len < len)
            ep->revents &= ~PICO_SOCK_EV_RD;
        break;
    }
    if (retval < 0) {
       if(tot_len == 0) tot_len = -1;
       break;
    }
    tot_len += retval;
  }
  PicoTcpLock->unlock();
  picotcp_async_interrupt(NULL);
  return tot_len;
}

int picotcp_write(struct stack_endpoint *ep,void *buf, int len)
{
  int retval = 0;
  int tot_len = 0;
  if (!buf || (len <= 0))
     return  0;
  PicoTcpLock->lock();
  while (tot_len < len) {
    retval = pico_socket_write(ep->s, ((uint8_t *)buf) + tot_len ,  len - tot_len);
    
    if (retval == 0) {
        if (tot_len < len)
            ep->revents &= ~PICO_SOCK_EV_WR;
        break;
    }
    if (retval < 0) {
       if(tot_len == 0) tot_len = -1;
       break;
    }
    tot_len += retval;
  }
  PicoTcpLock->unlock();
  picotcp_async_interrupt(NULL);
  return tot_len; 
}


int picotcp_setsockopt(struct stack_endpoint *ep, int option, void *value)
{
  int ret;
  
  PicoTcpLock->lock();
  if(option == SO_BROADCAST)
  {
    ep->broadcast = *(int *)value;
    ret = 0;
  }
  else
    ret = pico_socket_setoption(ep->s,option,value);
  PicoTcpLock->unlock();
  
  return ret; 
}

int picotcp_getsockopt(struct stack_endpoint *ep, int option, void *value)
{
  int ret;
  
  PicoTcpLock->lock();
  ret = pico_socket_getoption(ep->s,option,value);
  PicoTcpLock->unlock();
  
  return ret;
}

int picotcp_close(struct stack_endpoint *ep)
{
  int ret = 0;
  PicoTcpLock->lock();
  if(ep->state != SOCK_RESET_BY_PEER && ep->state != SOCK_CLOSED)
  {
    pico_socket_close(ep->s);
    // wait for fin, but timeout
    if( !(ep->revents & PICO_SOCK_EV_FIN) && !(ep->revents & PICO_SOCK_EV_CLOSE))
    {
        ep->events = (PICO_SOCK_EV_FIN | PICO_SOCK_EV_CLOSE | PICO_SOCK_EV_ERR);
        __critical_select(ep,3000u,1);// wait for 3 seconds
        ret = ((ep->revents & PICO_SOCK_EV_FIN) || (ep->revents & PICO_SOCK_EV_CLOSE) ? 0:-1);
    }
    ep->s->priv = NULL;
  }
  ptsock_dbg("Socket closed!\n");
  delete(ep->queue);
  pico_free(ep);
  PicoTcpLock->unlock();
  return ret;
}

int picotcp_join_multicast(struct stack_endpoint *ep,const char* address,const char* local)
{
  int ret;
  struct pico_ip_mreq mreq={};
  
  PicoTcpLock->lock();
  pico_string_to_ipv4(address,&mreq.mcast_group_addr.ip4.addr);
  pico_string_to_ipv4(local,&mreq.mcast_link_addr.ip4.addr);
  ret = pico_socket_setoption(ep->s, PICO_IP_ADD_MEMBERSHIP, &mreq);
  PicoTcpLock->unlock();
  
  return ret; 
}

void pico_wrapper_loop(const void * arg)
{
#ifdef PICO_MEASURE_STACK
    stack_fill_pattern((void *)&arg);
#else
  (void)arg;
#endif  

  struct pico_device *dev;
  while(1) {
    
    osEvent evt = PicoTcpEvents->get(SCHEDULER_BASE<<scheduler_timeout);
    
    if (evt.status == osEventMessage) {
        dev = (struct pico_device *)evt.value.p;
        scheduler_timeout = 0;
    } else {
        dev = NULL;
        if(scheduler_timeout < SCHEDULER_MAX) 
            scheduler_timeout++;
    }
    PicoTcpLock->lock();
    if (dev && dev->dsr)
      dev->dsr(dev, 5);
    picotcp_async_clean(dev);
    pico_stack_tick();
    pico_stack_tick();
#ifdef PICO_MEASURE_STACK
    stack_count_free_words((void *)&arg);
#endif
    PicoTcpLock->unlock();
  }
}

void picotcp_start(void)
{
  if (serverThread == NULL) {
    PicoTcpLock = new Mutex();
    PicoTcpEvents = new Queue<void,32>();
    ptsock_dbg (" *** PicoTCP initialized *** \n");
    serverThread = new Thread(pico_wrapper_loop,NULL,osPriorityNormal,4096);
    serverThread->set_priority(osPriorityIdle);
  }
}

void picotcp_init(void)
{
  picotcp_start(); 
}

int picotcp_dhcp_server_start(struct pico_dhcp_server_setting *setting)
{
  int ret;

  PicoTcpLock->lock();
  ret = pico_dhcp_server_initiate(setting);
  PicoTcpLock->unlock();
  picotcp_async_interrupt(NULL);

  return ret;
}

/****** Asynchronous event handling ******/
static int appEvents = 0;
static Mutex asyncMutex;
void picotcp_async_interrupt(void *arg)
{
    asyncMutex.lock();
    if(!arg)
    {
        if(!appEvents) // add NULL to queue
            PicoTcpEvents->put(NULL);
        appEvents++;
    }
    else
    {
        struct pico_device * dev = (struct pico_device *)arg;
        if(!dev->eventCnt) // put arg to queue
            PicoTcpEvents->put(dev);
        dev->eventCnt++;
    }
    asyncMutex.unlock();
}

void picotcp_async_clean(struct pico_device * dev)
{
    asyncMutex.lock();
    if(!dev)
        appEvents = 0;
    else
        dev->eventCnt=0;
    asyncMutex.unlock();
}

/*******************************/


// *************** DNS part *************** 

int picotcp_dns_client_nameserver(const char *ip, int flag)
{
    int ret;
    struct pico_ip4 addr;
    pico_string_to_ipv4(ip, &addr.addr);

    PicoTcpLock->lock();
    ret = pico_dns_client_nameserver(&addr, flag);
    PicoTcpLock->unlock();

    return ret;
}

void dns_cb(char *ip,void *arg)
{
  if(!arg)
    goto fail;
    // send the result back  
    ((Queue<void,1> *)arg)->put((void *)ip);
    return;
    
fail:
    if(ip) pico_free(ip);    
}

// dns get host
struct hostent *picotcp_gethostbyname(const char *name)
{
    Queue<void,1> *dnsResult = new Queue<void,1>();
    struct hostent * hostdata = NULL;
    char *ip;
    
    if(!dnsResult)
        return NULL;
    PicoTcpLock->lock();
    pico_dns_client_getaddr(name,dns_cb,dnsResult);
    PicoTcpLock->unlock();
    picotcp_async_interrupt(NULL);
    
    osEvent evt = dnsResult->get(7000);// 7 seconds timeout
    if (evt.status == osEventMessage){
        ip = (char *)evt.value.p;
        hostdata = new struct hostent;
        hostdata->h_name = new char[strlen(name)+1];
        strcpy(hostdata->h_name,name);
        hostdata->h_aliases=NULL;
        hostdata->h_addrtype = AF_INET;
        hostdata->h_length = strlen(ip);
        hostdata->h_addr_list = new char*[2];
        hostdata->h_addr_list[0] = ip;
        hostdata->h_addr_list[1] = NULL;
    }
    free(dnsResult);
    return hostdata;
}