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:
2013-09-16
Revision:
60:a57e18c13f5e
Parent:
58:5f6eedbbbd5b
Child:
64:0225b609335e

File content as of revision 60:a57e18c13f5e:

/*********************************************************************
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"

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

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

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

static struct stack_endpoint *ep_accepting;
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)
{
  int retval = 0;
  uint16_t ev = ep->revents;
  uint32_t in_time = PICO_TIME_MS();
  
  if(lock) PicoTcpLock->unlock();
  while ((ep->events & ep->revents) == 0) {
    ep->queue->get(time);
    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;
    }
  }
  if(lock) PicoTcpLock->lock();
  return 1;
}

static void wakeup(uint16_t ev, struct pico_socket *s)
{
  struct stack_endpoint *ep = (struct stack_endpoint *)s->priv;
  if (!ep) {
    if (ep_accepting != NULL) {
        ptsock_dbg("Delivering %02x to accepting socket...\n", ev);
        ep = ep_accepting;
    } else {
        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 || !ep->s || !ep->queue)
  {
    ptsock_dbg("Endpoint null warning : ep=%p, ep->s=%p, ep->queue=%p\n",ep,ep->s,ep->queue);
    return;
  }

  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)) {
    ep->connected = 0;
    pico_socket_close(ep->s);
    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);
  ep_accepting = (struct stack_endpoint *) pico_zalloc(sizeof(struct stack_endpoint));
  ep_accepting->queue = new Queue<void,1>();
  if (!ep_accepting)
    ret = -1;
  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;
  __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;
  }
  PicoTcpLock->unlock();
  return retval;
}

struct stack_endpoint *picotcp_accept(struct stack_endpoint *ep, struct sockaddr *_cli_addr, socklen_t *len)
{
  int retval;
  struct stack_endpoint *aep = ep_accepting;
  struct sockaddr_in *cli_addr = (struct sockaddr_in *)_cli_addr;
  ep_accepting = (struct stack_endpoint *) pico_zalloc(sizeof(struct stack_endpoint));
  if (ep_accepting)
    ep_accepting->queue = new Queue<void,1>();
  
  
  if (!aep)
    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;
  }
  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)
{
  PicoTcpLock->lock();
  if(ep->state != SOCK_RESET_BY_PEER)
  {
    pico_socket_close(ep->s);
    ep->s->priv = NULL;
  }
  ptsock_dbg("Socket closed!\n");
  delete(ep->queue);
  pico_free(ep);
  PicoTcpLock->unlock();
}

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.addr);
  pico_string_to_ipv4(local,&mreq.mcast_link_addr.addr);
  ret = pico_socket_setoption(ep->s, PICO_IP_ADD_MEMBERSHIP, &mreq);
  PicoTcpLock->unlock();
  
  return ret; 
}



void pico_wrapper_loop(const void * arg)
{
  (void)arg;
  int ret = 0;
  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();
    Thread::wait(1); // added extra wait because of aggressive receiving that kills the buffer.
    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(); 
}

/****** 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 *************** 
void dns_cb(char *ip,void *arg)
{
  if(!arg)
    goto fail;
  // send the result back  
  ((Queue<void,1> *)arg)->put((void *)ip);
fail:
  if(ip) 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;
    
    pico_dns_client_getaddr(name,dns_cb,dnsResult);
 
    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;
}