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.

Files at this revision

API Documentation at this revision

Comitter:
tass picotcp@tass.be
Date:
Tue Feb 11 14:48:37 2014 +0100
Parent:
139:1f7a4a8525ef
Parent:
138:0a7a449980e6
Child:
141:fb35b52d1c80
Child:
142:35da43068894
Commit message:
Added support for DHCP server

Changed in this revision

--- a/include/heap.h	Tue Feb 11 14:44:54 2014 +0100
+++ b/include/heap.h	Tue Feb 11 14:48:37 2014 +0100
@@ -73,9 +73,9 @@
         return p;     \
     } \
     /*static inline void heap_destroy(heap_ ## type * h) \
-    { \
-        pico_free(h->top);   \
-        pico_free(h);     \
-    } \*/
+       { \
+        pico_free(h->top); \
+        pico_free(h); \
+       } \*/
 
 
--- a/include/pico_constants.h	Tue Feb 11 14:44:54 2014 +0100
+++ b/include/pico_constants.h	Tue Feb 11 14:48:37 2014 +0100
@@ -133,7 +133,7 @@
 {
     uint32_t hash = 5381;
     uint32_t i;
-    const uint8_t *ptr= (const uint8_t *)buf;
+    const uint8_t *ptr = (const uint8_t *)buf;
     for(i = 0; i < size; i++)
         hash = ((hash << 5) + hash) + ptr[i]; /* hash * 33 + char */
     return hash;
--- a/include/pico_protocol.h	Tue Feb 11 14:44:54 2014 +0100
+++ b/include/pico_protocol.h	Tue Feb 11 14:48:37 2014 +0100
@@ -42,8 +42,10 @@
     PICO_ERR_EPROTONOSUPPORT = 93,
 
     /* ... */
+    PICO_ERR_EOPNOTSUPP = 95,
     PICO_ERR_EADDRINUSE = 98,
     PICO_ERR_EADDRNOTAVAIL,
+    PICO_ERR_ENETDOWN,
     PICO_ERR_ENETUNREACH,
 
     /* ... */
@@ -58,16 +60,13 @@
     PICO_ERR_ECONNREFUSED = 111,
     PICO_ERR_EHOSTDOWN,
     PICO_ERR_EHOSTUNREACH,
-    /* ... */
-    PICO_ERR_EOPNOTSUPP = 122,
-
 };
 
 typedef enum pico_err_e pico_err_t;
 extern volatile pico_err_t pico_err;
 
-#define IS_IPV6(f) ((((uint8_t *)(f->net_hdr))[0] & 0xf0) == 0x60)
-#define IS_IPV4(f) ((((uint8_t *)(f->net_hdr))[0] & 0xf0) == 0x40)
+#define IS_IPV6(f) (f && f->net_hdr && ((((uint8_t *)(f->net_hdr))[0] & 0xf0) == 0x60))
+#define IS_IPV4(f) (f && f->net_hdr && ((((uint8_t *)(f->net_hdr))[0] & 0xf0) == 0x40))
 
 #define MAX_PROTOCOL_NAME 16
 
@@ -79,9 +78,9 @@
     struct pico_queue *q_in;
     struct pico_queue *q_out;
     struct pico_frame *(*alloc)(struct pico_protocol *self, uint16_t size); /* Frame allocation. */
-    int (*push) (struct pico_protocol *self, struct pico_frame *p);   /* Push function, for active outgoing pkts from above */
-    int (*process_out) (struct pico_protocol *self, struct pico_frame *p); /* Send loop. */
-    int (*process_in) (struct pico_protocol *self, struct pico_frame *p); /* Recv loop. */
+    int (*push)(struct pico_protocol *self, struct pico_frame *p);    /* Push function, for active outgoing pkts from above */
+    int (*process_out)(struct pico_protocol *self, struct pico_frame *p);  /* Send loop. */
+    int (*process_in)(struct pico_protocol *self, struct pico_frame *p);  /* Recv loop. */
 };
 
 int pico_protocols_loop(int loop_score);
--- a/include/pico_socket.h	Tue Feb 11 14:44:54 2014 +0100
+++ b/include/pico_socket.h	Tue Feb 11 14:48:37 2014 +0100
@@ -11,7 +11,7 @@
 #include "pico_protocol.h"
 
 #ifdef __linux__
-    #define PICO_DEFAULT_SOCKETQ (128 * 1024) /* Linux host, so we want full throttle */
+    #define PICO_DEFAULT_SOCKETQ (16 * 1024) /* Linux host, so we want full throttle */
 #else
     #define PICO_DEFAULT_SOCKETQ (4 * 1024) /* seems like an acceptable default for small embedded systems */
 #endif
--- a/include/pico_stack.h	Tue Feb 11 14:44:54 2014 +0100
+++ b/include/pico_stack.h	Tue Feb 11 14:48:37 2014 +0100
@@ -8,7 +8,7 @@
 #include "pico_config.h"
 #include "pico_frame.h"
 
-#define PICO_MAX_TIMERS 20
+#define PICO_MAX_TIMERS 10
 
 #define PICO_ETH_MTU 1514
 #define PICO_IP_MTU 1500u
@@ -64,6 +64,7 @@
 void pico_store_network_origin(void *src, struct pico_frame *f);
 struct pico_timer *pico_timer_add(pico_time expire, void (*timer)(pico_time, void *), void *arg);
 void pico_timer_cancel(struct pico_timer *t);
+pico_time pico_timer_get_expire(struct pico_timer *t);
 uint32_t pico_rand(void);
 void pico_rand_feed(uint32_t feed);
 
--- a/modules/pico_dhcp_client.c	Tue Feb 11 14:44:54 2014 +0100
+++ b/modules/pico_dhcp_client.c	Tue Feb 11 14:48:37 2014 +0100
@@ -919,7 +919,7 @@
     hdr->htype = PICO_DHCP_HTYPE_ETH;
     hdr->hlen = PICO_SIZE_ETH;
     hdr->xid = dhcpc->xid;
-    hdr->flags = short_be(PICO_DHCP_FLAG_BROADCAST);
+    //hdr->flags = short_be(PICO_DHCP_FLAG_BROADCAST); /* Nope: see bug #96! */
     hdr->dhcp_magic = PICO_DHCPD_MAGIC_COOKIE;
     /* copy client hardware address */
     memcpy(hdr->hwaddr, &dhcpc->dev->eth->mac, PICO_SIZE_ETH);
--- a/modules/pico_dns_client.c	Tue Feb 11 14:44:54 2014 +0100
+++ b/modules/pico_dns_client.c	Tue Feb 11 14:48:37 2014 +0100
@@ -294,7 +294,7 @@
     if (!url)
         return 0;
 
-    for (len=0; len<0xFFFF; len++) {
+    for (len = 0; len < 0xFFFF; len++) {
         if (url[len] == 0)
             break;
     }
@@ -309,7 +309,6 @@
 
     while (*ptr != 0)
         ptr++;
-
     return ptr + 1;
 }
 
--- a/modules/pico_http_server.c	Tue Feb 11 14:44:54 2014 +0100
+++ b/modules/pico_http_server.c	Tue Feb 11 14:48:37 2014 +0100
@@ -587,7 +587,7 @@
     uint16_t length;
     while( client->bufferSent < client->bufferSize &&
            (length = (uint16_t)pico_socket_write(client->sck, (uint8_t *)client->buffer + client->bufferSent, \
-           client->bufferSize - client->bufferSent)) > 0 )
+                                                 client->bufferSize - client->bufferSent)) > 0 )
     {
         client->bufferSent = (uint16_t)(client->bufferSent + length);
         server.wakeup(EV_HTTP_PROGRESS, client->connectionID);
--- a/modules/pico_ipv4.c	Tue Feb 11 14:44:54 2014 +0100
+++ b/modules/pico_ipv4.c	Tue Feb 11 14:48:37 2014 +0100
@@ -163,6 +163,34 @@
     return 0;
 }
 
+int pico_ipv4_is_loopback(uint32_t address)
+{
+    const unsigned char *addr = (unsigned char *) &address;
+    if(addr[0] == 0x7f)
+        return 1;
+
+    return 0;
+}
+
+int pico_ipv4_is_valid_src(uint32_t address)
+{
+    if (pico_ipv4_is_broadcast(address)) {
+        dbg("Source is a broadcast address, discard packet\n");
+        return 0;
+    }
+    else if( pico_ipv4_is_multicast(address)) {
+        dbg("Source is a multicast address, discard packet\n");
+        return 0;
+    }
+    else if (pico_ipv4_is_loopback(address)) {
+        dbg("Source is a loopback address, discard packet\n");
+        return 0;
+    }
+    else {
+        return 1;
+    }
+}
+
 static int pico_ipv4_checksum(struct pico_frame *f)
 {
     struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
@@ -518,15 +546,12 @@
     if (ret < 1)
         return ret;
 
-#ifdef PICO_SUPPORT_MCAST
-    /* Multicast address in source, discard quietly */
-    if (pico_ipv4_is_multicast(hdr->src.addr)) {
-        ip_mcast_dbg("MCAST: ERROR multicast address %08X in source address\n", hdr->src.addr);
+    /* Validate source IP address. Discard quietly if invalid */
+    if (!pico_ipv4_is_valid_src(hdr->src.addr)) {
         pico_frame_discard(f);
         return 0;
     }
 
-#endif
     if (hdr->frag & 0x80) {
         pico_frame_discard(f); /* RFC 3514 */
         return 0;
@@ -1506,9 +1531,6 @@
 {
     struct pico_ipv4_link *link;
     struct pico_tree_node *index;
-    if (addr == PICO_IP4_ANY)
-        return 1;
-
     if (addr == PICO_IP4_BCAST)
         return 1;
 
--- a/modules/pico_ipv4.h	Tue Feb 11 14:44:54 2014 +0100
+++ b/modules/pico_ipv4.h	Tue Feb 11 14:48:37 2014 +0100
@@ -79,6 +79,8 @@
 int pico_ipv4_is_unicast(uint32_t address);
 int pico_ipv4_is_multicast(uint32_t address);
 int pico_ipv4_is_broadcast(uint32_t addr);
+int pico_ipv4_is_loopback(uint32_t addr);
+int pico_ipv4_is_valid_src(uint32_t addr);
 
 int pico_ipv4_link_add(struct pico_device *dev, struct pico_ip4 address, struct pico_ip4 netmask);
 int pico_ipv4_link_del(struct pico_device *dev, struct pico_ip4 address);
--- a/modules/pico_tcp.c	Tue Feb 11 14:44:54 2014 +0100
+++ b/modules/pico_tcp.c	Tue Feb 11 14:48:37 2014 +0100
@@ -21,6 +21,8 @@
 #define SEQN(f) ((f) ? (long_be(((struct pico_tcp_hdr *)((f)->transport_hdr))->seq)) : 0)
 #define ACKN(f) ((f) ? (long_be(((struct pico_tcp_hdr *)((f)->transport_hdr))->ack)) : 0)
 
+#define TCP_TIME (PICO_TIME_MS())
+
 #define PICO_TCP_RTO_MIN 50
 #define PICO_TCP_RTO_MAX 120000
 #define PICO_TCP_IW          2
@@ -277,9 +279,8 @@
     uint32_t rttvar;
     uint32_t rto;
     uint32_t in_flight;
-    uint8_t timer_running;
     struct pico_timer *retrans_tmr;
-    uint8_t keepalive_timer_running;
+    pico_time retrans_tmr_due;
     uint16_t cwnd_counter;
     uint16_t cwnd;
     uint16_t ssthresh;
@@ -418,7 +419,7 @@
     struct pico_socket_tcp *t = (struct pico_socket_tcp *)f->sock;
     IGNORE_PARAMETER(self);
     hdr = (struct pico_tcp_hdr *)f->transport_hdr;
-    f->sock->timestamp = PICO_TIME_MS();
+    f->sock->timestamp = TCP_TIME;
     if (f->payload_len > 0) {
         tcp_dbg("Process out: sending %p (%d bytes)\n", f, f->payload_len);
     } else {
@@ -440,7 +441,7 @@
     return 0;
 }
 
-int32_t pico_tcp_push(struct pico_protocol *self, struct pico_frame *data);
+int pico_tcp_push(struct pico_protocol *self, struct pico_frame *data);
 
 /* Interface: protocol definition */
 struct pico_protocol pico_proto_tcp = {
@@ -463,7 +464,7 @@
 
 static void tcp_add_options(struct pico_socket_tcp *ts, struct pico_frame *f, uint16_t flags, uint16_t optsiz)
 {
-    uint32_t tsval = long_be((uint32_t)pico_tick);
+    uint32_t tsval = long_be((uint32_t)TCP_TIME);
     uint32_t tsecr = long_be(ts->ts_nxt);
     uint32_t i = 0;
     f->start = f->transport_hdr + PICO_SIZE_TCPHDR;
@@ -531,7 +532,7 @@
 
 static void tcp_add_options_frame(struct pico_socket_tcp *ts, struct pico_frame *f)
 {
-    uint32_t tsval = long_be((uint32_t)pico_tick);
+    uint32_t tsval = long_be((uint32_t)TCP_TIME);
     uint32_t tsecr = long_be(ts->ts_nxt);
     uint32_t i = 0;
     uint16_t optsiz = tcp_options_size_frame(f);
@@ -677,10 +678,10 @@
 inline static void tcp_add_header(struct pico_socket_tcp *t, struct pico_frame *f)
 {
     struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *)f->transport_hdr;
-    f->timestamp = pico_tick;
+    f->timestamp = TCP_TIME;
     tcp_add_options(t, f, 0, (uint16_t)(f->transport_len - f->payload_len - (uint16_t)PICO_SIZE_TCPHDR));
     hdr->rwnd = short_be(t->wnd);
-    hdr->flags |= PICO_TCP_PSH;
+    hdr->flags |= PICO_TCP_PSH | PICO_TCP_ACK;
     hdr->ack = long_be(t->rcv_nxt);
     hdr->crc = 0;
     hdr->crc = short_be(pico_tcp_checksum_ipv4(f));
@@ -818,7 +819,7 @@
         hdr->flags |= PICO_TCP_PSH | PICO_TCP_ACK;
         hdr->ack = long_be(ts->rcv_nxt);
         ts->rcv_ackd = ts->rcv_nxt;
-        ts->keepalive_timer_running = 2; /* XXX TODO check fix: added 1 to counter to postpone sending keepalive, ACK is in data segments */
+        /* XXX pico_keepalive_reschedule(ts); */
     }
 
     f->start = f->transport_hdr + PICO_SIZE_TCPHDR;
@@ -849,8 +850,8 @@
 static void sock_stats(uint32_t when, void *arg)
 {
     struct pico_socket_tcp *t = (struct pico_socket_tcp *)arg;
-    tcp_dbg("STATISTIC> [%lu] socket state: %02x --> local port:%d remote port: %d queue size: %d snd_una: %08x snd_nxt: %08x timer: %d cwnd: %d\n",
-            when, t->sock.state, short_be(t->sock.local_port), short_be(t->sock.remote_port), t->tcpq_out.size, SEQN(first_segment(&t->tcpq_out)), t->snd_nxt, t->timer_running, t->cwnd);
+    tcp_dbg("STATISTIC> [%lu] socket state: %02x --> local port:%d remote port: %d queue size: %d snd_una: %08x snd_nxt: %08x cwnd: %d\n",
+            when, t->sock.state, short_be(t->sock.local_port), short_be(t->sock.remote_port), t->tcpq_out.size, SEQN((struct pico_frame *)first_segment(&t->tcpq_out)), t->snd_nxt, t->cwnd);
     pico_timer_add(2000, sock_stats, t);
 }
 #endif
@@ -861,7 +862,7 @@
     if (!t)
         return NULL;
 
-    t->sock.timestamp = PICO_TIME_MS();
+    t->sock.timestamp = TCP_TIME;
     t->mss = PICO_TCP_DEFAULT_MSS;
 
     t->tcpq_in.pool.root = t->tcpq_hold.pool.root = t->tcpq_out.pool.root = &LEAF;
@@ -873,9 +874,9 @@
     t->tcpq_in.overhead = (sizeof(struct tcp_input_segment) + sizeof(struct pico_tree_node));
     t->tcpq_out.overhead = t->tcpq_hold.overhead = sizeof(struct pico_frame) + sizeof(struct pico_tree_node);
     /* disable Nagle by default */
-    /* t->sock.opt_flags |= (1 << PICO_SOCKET_OPT_TCPNODELAY); */
+    t->sock.opt_flags |= (1 << PICO_SOCKET_OPT_TCPNODELAY);
     /* Nagle is enabled by default */
-    t->sock.opt_flags &= (uint16_t) ~(1 << PICO_SOCKET_OPT_TCPNODELAY);
+    /* t->sock.opt_flags &= (uint16_t) ~(1 << PICO_SOCKET_OPT_TCPNODELAY); */
 
 #ifdef PICO_TCP_SUPPORT_SOCKET_STATS
     pico_timer_add(2000, sock_stats, t);
@@ -896,10 +897,8 @@
         /* To be sure we don't have garbage at the beginning */
         release_until(&t->tcpq_in, t->rcv_processed);
         f = first_segment(&t->tcpq_in);
-        if (!f) {
-            tcp_set_space(t);
+        if (!f)
             goto out;
-        }
 
         /* Hole at the beginning of data, awaiting retransmissions. */
         if (seq_compare(t->rcv_processed, f->seq) < 0) {
@@ -1025,13 +1024,13 @@
     tcp_set_space(ts);
     tcp_add_options(ts, synack, hdr->flags, opt_len);
     synack->payload_len = 0;
-    synack->timestamp = pico_tick;
+    synack->timestamp = TCP_TIME;
     tcp_send(ts, synack);
     pico_frame_discard(synack);
     return 0;
 }
 
-static void tcp_send_empty(struct pico_socket_tcp *t, uint16_t flags)
+static void tcp_send_empty(struct pico_socket_tcp *t, uint16_t flags, int is_keepalive)
 {
     struct pico_frame *f;
     struct pico_tcp_hdr *hdr;
@@ -1054,6 +1053,9 @@
     if ((flags & PICO_TCP_ACK) != 0)
         hdr->ack = long_be(t->rcv_nxt);
 
+    if (is_keepalive)
+        hdr->seq = long_be(t->snd_nxt - 1);
+
     t->rcv_ackd = t->rcv_nxt;
 
     f->start = f->transport_hdr + PICO_SIZE_TCPHDR;
@@ -1067,7 +1069,13 @@
 
 static void tcp_send_ack(struct pico_socket_tcp *t)
 {
-    return tcp_send_empty(t, PICO_TCP_ACK);
+    return tcp_send_empty(t, PICO_TCP_ACK, 0);
+}
+
+static void tcp_send_probe(struct pico_socket_tcp *t)
+{
+    /* tcp_dbg("Sending probe\n"); */
+    return tcp_send_empty(t, PICO_TCP_PSH, 0);
 }
 
 static int tcp_send_rst(struct pico_socket *s, struct pico_frame *fr)
@@ -1340,11 +1348,18 @@
 {
     struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
     struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) f->transport_hdr;
+    uint16_t payload_len = (uint16_t)(f->transport_len - ((hdr->len & 0xf0) >> 2));
+
+    if (payload_len == 0 && (hdr->flags & PICO_TCP_PSH)) {
+        tcp_send_ack(t);
+        return 0;
+    }
+
 
     if (((hdr->len & 0xf0) >> 2) <= f->transport_len) {
         tcp_parse_options(f);
         f->payload = f->transport_hdr + ((hdr->len & 0xf0) >> 2);
-        f->payload_len = (uint16_t)(f->transport_len - ((hdr->len & 0xf0) >> 2));
+        f->payload_len = payload_len;
         tcp_dbg("TCP> Received segment. (exp: %x got: %x)\n", t->rcv_nxt, SEQN(f));
 
         if (seq_compare(SEQN(f), t->rcv_nxt) <= 0) {
@@ -1402,8 +1417,9 @@
 static int tcp_ack_advance_una(struct pico_socket_tcp *t, struct pico_frame *f, pico_time *timestamp)
 {
     int ret =  release_all_until(&t->tcpq_out, ACKN(f), timestamp);
-    if (ret > 0)
+    if (ret > 0) {
         t->sock.ev_pending |= PICO_SOCK_EV_WR;
+    }
 
     return ret;
 }
@@ -1482,99 +1498,142 @@
         }
     }
 
-    tcp_dbg("TCP_CWND, %lu, %u, %u, %u\n", pico_tick, t->cwnd, t->ssthresh, t->in_flight);
+    tcp_dbg("TCP_CWND, %lu, %u, %u, %u\n", TCP_TIME, t->cwnd, t->ssthresh, t->in_flight);
 }
 
 static void add_retransmission_timer(struct pico_socket_tcp *t, pico_time next_ts);
+
+
+/* Retransmission time out (RTO). */
+
+static void tcp_first_timeout(struct pico_socket_tcp *t)
+{
+    t->x_mode = PICO_TCP_BLACKOUT;
+    t->cwnd = PICO_TCP_IW;
+    t->in_flight = 0;
+}
+
+static int tcp_rto_xmit(struct pico_socket_tcp *t, struct pico_frame *f)
+{
+    struct pico_frame *cpy;
+    /* TCP: ENQUEUE to PROTO ( retransmit )*/
+    cpy = pico_frame_copy(f);
+    if (pico_enqueue(&tcp_out, cpy) > 0) {
+        t->snd_last_out = SEQN(cpy);
+        add_retransmission_timer(t, (t->rto << (++t->backoff)) + TCP_TIME);
+        tcp_dbg("TCP_CWND, %lu, %u, %u, %u\n", TCP_TIME, t->cwnd, t->ssthresh, t->in_flight);
+        tcp_dbg("Sending RTO!\n");
+        return 1;
+    } else {
+        add_retransmission_timer(t, (t->rto << t->backoff) + TCP_TIME);
+        pico_frame_discard(cpy);
+        return 0;
+    }
+}
+
+static void tcp_next_zerowindow_probe(struct pico_socket_tcp *t)
+{
+    tcp_dbg("Sending probe!\n");
+    tcp_send_probe(t);
+    add_retransmission_timer(t, (t->rto << ++t->backoff) + TCP_TIME);
+}
+
+static int tcp_is_allowed_to_send(struct pico_socket_tcp *t)
+{
+    return t->sock.net &&
+           (
+               ((t->sock.state & 0xFF00) == PICO_SOCKET_STATE_TCP_ESTABLISHED) ||
+               ((t->sock.state & 0xFF00) == PICO_SOCKET_STATE_TCP_CLOSE_WAIT)
+           ) &&
+           ((t->backoff < PICO_TCP_MAX_RETRANS));
+}
+
 static void tcp_retrans_timeout(pico_time val, void *sock)
 {
     struct pico_socket_tcp *t = (struct pico_socket_tcp *) sock;
     struct pico_frame *f = NULL;
-    pico_time limit = val - t->rto;
-    if( t->sock.net && ((t->sock.state & 0xFF00) == PICO_SOCKET_STATE_TCP_ESTABLISHED
-                        || (t->sock.state & 0xFF00) == PICO_SOCKET_STATE_TCP_CLOSE_WAIT) && t->backoff < PICO_TCP_MAX_RETRANS)
-    {
-        tcp_dbg("TIMEOUT! backoff = %d\n", t->backoff);
-        /* was timer cancelled? */
-        if (t->timer_running == 0) {
-            add_retransmission_timer(t, 0);
-            return;
-        }
-
-        t->timer_running--;
-
+
+    t->retrans_tmr = NULL;
+
+    if (t->retrans_tmr_due == 0ull)
+        return;
+
+    if (t->retrans_tmr_due > val) {
+        /* Timer was postponed... */
+        add_retransmission_timer(t, (t->rto << (t->backoff)) + TCP_TIME);
+        return;
+    }
+
+    tcp_dbg("TIMEOUT! backoff = %d, rto: %d\n", t->backoff, t->rto);
+    tcp_dbg("TIMEOUT! backoff = %d, rto: %d\n", t->backoff, t->rto);
+    t->retrans_tmr_due = 0ull;
+
+    if (tcp_is_allowed_to_send(t)) {
         f = first_segment(&t->tcpq_out);
         while (f) {
-            if ((t->x_mode == PICO_TCP_WINDOW_FULL) ||
-                ((f->timestamp != 0) && (f->timestamp <= limit))) {
-                struct pico_frame *cpy;
+            if (t->x_mode == PICO_TCP_WINDOW_FULL) {
+                tcp_dbg("TCP BLACKOUT> TIMED OUT (output) frame %08x, len= %d rto=%d Win full: %d frame flags: %04x\n", SEQN(f), f->payload_len, t->rto, t->x_mode == PICO_TCP_WINDOW_FULL, f->flags);
                 tcp_dbg("TCP BLACKOUT> TIMED OUT (output) frame %08x, len= %d rto=%d Win full: %d frame flags: %04x\n", SEQN(f), f->payload_len, t->rto, t->x_mode == PICO_TCP_WINDOW_FULL, f->flags);
-                if ((t->x_mode != PICO_TCP_WINDOW_FULL)) {
-                    t->x_mode = PICO_TCP_BLACKOUT;
-                    tcp_dbg("Mode: Blackout.\n");
-                    t->cwnd = PICO_TCP_IW;
-                    t->in_flight = 0;
-                }
-
-                tcp_add_header(t, f);
-                /* TCP: ENQUEUE to PROTO ( retransmit )*/
-                cpy = pico_frame_copy(f);
-                if (pico_enqueue(&tcp_out, cpy) > 0) {
-                    t->backoff++;
-                    t->snd_last_out = SEQN(cpy);
-                    add_retransmission_timer(t, (t->rto << t->backoff) + pico_tick);
-                    tcp_dbg("TCP_CWND, %lu, %u, %u, %u\n", pico_tick, t->cwnd, t->ssthresh, t->in_flight);
-                    return;
-                } else {
-                    add_retransmission_timer(t, (t->rto << t->backoff) + pico_tick);
-                    pico_frame_discard(cpy);
-                }
+                tcp_next_zerowindow_probe(t);
+                return;
             }
 
+            if (t->x_mode != PICO_TCP_BLACKOUT)
+                tcp_first_timeout(t);
+
+            tcp_add_header(t, f);
+            if (tcp_rto_xmit(t, f) > 0) /* A segment has been rexmit'd */
+                return;
+
             f = next_segment(&t->tcpq_out, f);
         }
-        t->backoff = 0;
-        add_retransmission_timer(t, 0);
         if (t->tcpq_out.size < t->tcpq_out.max_size)
             t->sock.ev_pending |= PICO_SOCK_EV_WR;
-
-        return;
     }
     else if(t->backoff >= PICO_TCP_MAX_RETRANS && (t->sock.state & 0xFF00) == PICO_SOCKET_STATE_TCP_ESTABLISHED )
     {
-        /* the retransmission timer, failed to get an ack for a frame, giving up on the connection */
+        dbg("Connection timeout!\n");
+        /* the retransmission timer, failed to get an ack for a frame, gives up on the connection */
         tcp_discard_all_segments(&t->tcpq_out);
         if(t->sock.wakeup)
             t->sock.wakeup(PICO_SOCK_EV_FIN, &t->sock);
+
+        return;
+    } else {
+        tcp_dbg("Retransmission not allowed, rescheduling\n");
     }
 }
 
 static void add_retransmission_timer(struct pico_socket_tcp *t, pico_time next_ts)
 {
     struct pico_tree_node *index;
-
-    if (t->timer_running > 0)
-        return;
+    pico_time val = 0;
 
     if (next_ts == 0) {
         struct pico_frame *f;
 
         pico_tree_foreach(index, &t->tcpq_out.pool){
             f = index->keyValue;
-            if (((next_ts == 0) || (f->timestamp < next_ts)) && (f->timestamp > 0)) {
+            if ((next_ts == 0) || ((f->timestamp < next_ts) && (f->timestamp > 0))) {
                 next_ts = f->timestamp;
+                val = next_ts + (t->rto << t->backoff);
             }
         }
+    } else {
+        val = next_ts;
+    }
+
+    if (val > 0) {
+        if (val > TCP_TIME) {
+            t->retrans_tmr_due = val;
+        } else {
+            t->retrans_tmr_due = TCP_TIME + 1;
+        }
     }
 
-    if (next_ts > 0) {
-        if ((next_ts + t->rto) > pico_tick) {
-            t->retrans_tmr = pico_timer_add(next_ts + t->rto - pico_tick, tcp_retrans_timeout, t);
-        } else {
-            t->retrans_tmr = pico_timer_add(1, tcp_retrans_timeout, t);
-        }
-
-        t->timer_running++;
+    if (!t->retrans_tmr) {
+        t->retrans_tmr = pico_timer_add(t->retrans_tmr_due - TCP_TIME, tcp_retrans_timeout, t);
+    } else {
     }
 }
 
@@ -1589,11 +1648,11 @@
         if (pico_enqueue(&tcp_out, cpy) > 0) {
             t->in_flight++;
             t->snd_last_out = SEQN(cpy);
-            add_retransmission_timer(t, pico_tick + t->rto);
         } else {
             pico_frame_discard(cpy);
         }
 
+        add_retransmission_timer(t, TCP_TIME + t->rto);
         return(f->payload_len);
     }
 
@@ -1654,13 +1713,12 @@
 
 static int tcp_ack(struct pico_socket *s, struct pico_frame *f)
 {
-    struct pico_frame *f_new; /* use with Nagle to push to out queue */
+    struct pico_frame *f_new;              /* use with Nagle to push to out queue */
     struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
     struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) f->transport_hdr;
     uint32_t rtt = 0;
     uint16_t acked = 0;
     pico_time acked_timestamp = 0;
-    uint8_t restart_tmr = 0;
 
     struct pico_frame *una = NULL;
     if ((hdr->flags & PICO_TCP_ACK) == 0)
@@ -1687,9 +1745,9 @@
         {
             t->snd_nxt = SEQN(una);
             /* restart the retrans timer */
-            pico_timer_cancel(t->retrans_tmr);
-            t->timer_running = 0;
-            restart_tmr = 1u;
+            if (t->retrans_tmr) {
+                t->retrans_tmr_due = 0ull;
+            }
         }
     }
 
@@ -1705,12 +1763,12 @@
         /* Do rtt/rttvar/rto calculations */
         /* First, try with timestamps, using the value from options */
         if(f && (f->timestamp != 0)) {
-            rtt = time_diff(pico_tick, f->timestamp);
+            rtt = time_diff(TCP_TIME, f->timestamp);
             if (rtt)
                 tcp_rtt(t, rtt);
         } else if(acked_timestamp) {
             /* If no timestamps are there, use conservatve estimation on the una */
-            rtt = time_diff(pico_tick, acked_timestamp);
+            rtt = time_diff(TCP_TIME, acked_timestamp);
             if (rtt)
                 tcp_rtt(t, rtt);
         }
@@ -1722,17 +1780,17 @@
         } else
             t->in_flight -= (acked);
 
-    } else if ((t->snd_old_ack == ACKN(f)) && /* We've just seen this ack, and... */
+    } else if ((t->snd_old_ack == ACKN(f)) &&              /* We've just seen this ack, and... */
                ((0 == (hdr->flags & (PICO_TCP_PSH | PICO_TCP_SYN))) &&
-                (f->payload_len == 0)) && /* This is a pure ack, and... */
-               (ACKN(f) != t->snd_nxt)) /* There is something in flight awaiting to be acked... */
+                (f->payload_len == 0)) &&              /* This is a pure ack, and... */
+               (ACKN(f) != t->snd_nxt))              /* There is something in flight awaiting to be acked... */
     {
         /* Process incoming duplicate ack. */
         if (t->x_mode < PICO_TCP_RECOVER) {
             t->x_mode++;
             tcp_dbg("Mode: DUPACK %d, due to PURE ACK %0x, len = %d\n", t->x_mode, SEQN(f), f->payload_len);
             tcp_dbg("ACK: %x - QUEUE: %x\n", ACKN(f), SEQN(first_segment(&t->tcpq_out)));
-            if (t->x_mode == PICO_TCP_RECOVER) { /* Switching mode */
+            if (t->x_mode == PICO_TCP_RECOVER) {              /* Switching mode */
                 t->snd_retry = SEQN((struct pico_frame *)first_segment(&t->tcpq_out));
                 if (t->ssthresh > t->cwnd)
                     t->ssthresh >>= 2;
@@ -1779,7 +1837,7 @@
             tcp_dbg("DUPACK in mode %d \n", t->x_mode);
 
         }
-    } /* End case duplicate ack detection */
+    }              /* End case duplicate ack detection */
 
     /* Do congestion control */
     tcp_congestion_control(t);
@@ -1796,7 +1854,7 @@
             tcp_dbg_nagle("TCP_ACK - NAGLE add new segment\n");
             f_new = pico_hold_segment_make(t);
             if (f_new == NULL)
-                break;    /* XXX corrupt !!! (or no memory) */
+                break;              /* XXX corrupt !!! (or no memory) */
 
             if (pico_enqueue_segment(&t->tcpq_out, f_new) <= 0)
                 /* handle error */
@@ -1805,18 +1863,14 @@
     }
 
     /* If some space was created, put a few segments out. */
-    tcp_dbg("TCP_CWND, %lu, %u, %u, %u\n", pico_tick, t->cwnd, t->ssthresh, t->in_flight);
+    tcp_dbg("TCP_CWND, %lu, %u, %u, %u\n", TCP_TIME, t->cwnd, t->ssthresh, t->in_flight);
     if (t->x_mode ==  PICO_TCP_LOOKAHEAD) {
         if ((t->cwnd >= t->in_flight) && (t->snd_nxt > t->snd_last_out)) {
             pico_tcp_output(&t->sock, (int)t->cwnd - (int)t->in_flight);
         }
     }
 
-    if(restart_tmr)
-    {
-        add_retransmission_timer(t, pico_tick + t->rto);
-    }
-
+    add_retransmission_timer(t, 0);
     t->snd_old_ack = ACKN(f);
     return 0;
 }
@@ -1873,7 +1927,7 @@
     if (s->wakeup)
         s->wakeup(PICO_SOCK_EV_CLOSE, s);
 
-    if (f->payload_len > 0) /* needed?? */
+    if (f->payload_len > 0)              /* needed?? */
         tcp_data_in(s, f);
 
     /* send ACK */
@@ -2001,7 +2055,7 @@
 
         t->rcv_nxt++;
         t->snd_nxt++;
-        tcp_send_ack(t); /* return ACK */
+        tcp_send_ack(t);              /* return ACK */
 
         return 0;
 
@@ -2022,7 +2076,7 @@
         s->state &= 0x00FFU;
         s->state |= PICO_SOCKET_STATE_TCP_ESTABLISHED;
         tcp_dbg("TCP: Established. State now: %04x\n", s->state);
-        if( !s->parent && s->wakeup) { /* If the socket has no parent, -> sending socket that has a sim_open */
+        if( !s->parent && s->wakeup) {              /* If the socket has no parent, -> sending socket that has a sim_open */
             tcp_dbg("FIRST ACK - No parent found -> sending socket\n");
             s->wakeup(PICO_SOCK_EV_CONN,  s);
         }
@@ -2072,7 +2126,7 @@
                 s->wakeup(PICO_SOCK_EV_CLOSE, s);
         }
     } else {
-        tcp_send_ack(t); /* return ACK */
+        tcp_send_ack(t);              /* return ACK */
     }
 
     return 0;
@@ -2145,16 +2199,16 @@
     tcp_dbg("TCP >>>>>>>>>>>>>> received RST <<<<<<<<<<<<<<<<<<<<\n");
     if ((s->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_SYN_SENT) {
         /* the RST is acceptable if the ACK field acknowledges the SYN */
-        if ((t->snd_nxt + 1) == ACKN(f)) { /* valid, got to closed state */
+        if ((t->snd_nxt + 1) == ACKN(f)) {              /* valid, got to closed state */
             tcp_force_closed(s);
             pico_err = PICO_ERR_ECONNRESET;
             tcp_wakeup_pending(s, PICO_SOCK_EV_ERR);
-            pico_socket_del(&t->sock); /* delete socket */
+            pico_socket_del(&t->sock);              /* delete socket */
         } else {                  /* not valid, ignore */
             tcp_dbg("TCP RST> IGNORE\n");
             return 0;
         }
-    } else { /* all other states */
+    } else {              /* all other states */
         /* all reset (RST) segments are validated by checking their SEQ-fields,
            a reset is valid if its sequence number is in the window */
         if ((long_be(hdr->seq) >= t->rcv_ackd) && (long_be(hdr->seq) <= ((uint32_t)(short_be(hdr->rwnd) << (t->wnd_scale)) + t->rcv_ackd))) {
@@ -2162,7 +2216,7 @@
                 tcp_force_closed(s);
                 pico_err = PICO_ERR_ECONNRESET;
                 tcp_wakeup_pending(s, PICO_SOCK_EV_ERR);
-                pico_socket_del(&t->sock); /* delete socket */
+                pico_socket_del(&t->sock);              /* delete socket */
                 tcp_dbg("TCP RST> SOCKET BACK TO LISTEN\n");
                 /*   pico_socket_del(s); */
             } else {
@@ -2170,7 +2224,7 @@
                 tcp_wakeup_pending(s, PICO_SOCK_EV_FIN);
                 pico_err = PICO_ERR_ECONNRESET;
                 tcp_wakeup_pending(s, PICO_SOCK_EV_ERR);
-                pico_socket_del(&t->sock); /* delete socket */
+                pico_socket_del(&t->sock);              /* delete socket */
             }
         } else {                  /* not valid, ignore */
             tcp_dbg("TCP RST> IGNORE\n");
@@ -2274,12 +2328,12 @@
     f->payload = (f->transport_hdr + ((hdr->len & 0xf0) >> 2));
     f->payload_len = (uint16_t)(f->transport_len - ((hdr->len & 0xf0) >> 2));
 
-    tcp_dbg("[%lu] TCP> [tcp input] socket: %p state: %d <-- local port:%d remote port: %d seq: %08x ack: %08x flags: %02x = t_len: %d, hdr: %u payload: %d\n", pico_tick,
+    tcp_dbg("[%lu] TCP> [tcp input] socket: %p state: %d <-- local port:%d remote port: %d seq: %08x ack: %08x flags: %02x = t_len: %d, hdr: %u payload: %d\n", TCP_TIME,
             s, s->state >> 8, short_be(hdr->trans.dport), short_be(hdr->trans.sport), SEQN(f), ACKN(f), hdr->flags, f->transport_len, (hdr->len & 0xf0) >> 2, f->payload_len );
 
     /* This copy of the frame has the current socket as owner */
     f->sock = s;
-    s->timestamp = PICO_TIME_MS();
+    s->timestamp = TCP_TIME;
     /* Those are not supported at this time. */
     /* flags &= (uint8_t) ~(PICO_TCP_CWR | PICO_TCP_URG | PICO_TCP_ECN); */
     if(invalid_flags(s, flags))
@@ -2297,7 +2351,8 @@
             }
         }
 
-        if (f->payload_len > 0 && !(s->state & PICO_SOCKET_STATE_CLOSED) && !TCP_IS_STATE(s, PICO_SOCKET_STATE_TCP_LISTEN))
+        if ((f->payload_len > 0 || (flags & PICO_TCP_PSH)) &&
+            !(s->state & PICO_SOCKET_STATE_CLOSED) && !TCP_IS_STATE(s, PICO_SOCKET_STATE_TCP_LISTEN))
         {
             ret = f->payload_len;
             if (action->data)
@@ -2325,26 +2380,6 @@
     return ret;
 }
 
-static void tcp_send_keepalive(pico_time when, void *_t)
-{
-    struct pico_socket_tcp *t = (struct pico_socket_tcp *)_t;
-    IGNORE_PARAMETER(when);
-    tcp_dbg("Sending keepalive (%d), [State = %d]...\n", t->backoff, t->sock.state );
-    if( t->sock.net && ((t->sock.state & 0xFF00) == PICO_SOCKET_STATE_TCP_ESTABLISHED))
-    {
-        tcp_send_ack(t);
-
-        if (t->keepalive_timer_running > 0) {
-            t->keepalive_timer_running--;
-        }
-
-        if (t->keepalive_timer_running == 0) {
-            t->keepalive_timer_running++;
-            tcp_dbg("[Self] Adding timer(retransmit keepalive)\n");
-            pico_timer_add(t->rto << (++t->backoff), tcp_send_keepalive, t);
-        }
-    }
-}
 
 inline static int checkLocalClosing(struct pico_socket *s);
 inline static int checkRemoteClosing(struct pico_socket *s);
@@ -2355,12 +2390,13 @@
     struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
     struct pico_frame *f, *una;
     int sent = 0;
+    int data_sent = 0;
 
     una = first_segment(&t->tcpq_out);
     f = peek_segment(&t->tcpq_out, t->snd_nxt);
 
     while((f) && (t->cwnd >= t->in_flight)) {
-        f->timestamp = pico_tick;
+        f->timestamp = TCP_TIME;
         tcp_add_options_frame(t, f);
         if (seq_compare((SEQN(f) + f->payload_len), (SEQN(una) + (uint32_t)(t->recv_wnd << t->recv_wnd_scale))) > 0) {
             t->cwnd = (uint16_t)t->in_flight;
@@ -2370,8 +2406,8 @@
             if (t->x_mode != PICO_TCP_WINDOW_FULL) {
                 tcp_dbg("TCP> RIGHT SIZING (rwnd: %d, frame len: %d\n", t->recv_wnd << t->recv_wnd_scale, f->payload_len);
                 tcp_dbg("In window full...\n");
-                t->snd_nxt = SEQN(una); /* XXX prevent out-of-order-packets ! */ /*DLA re-enabled.*/
-                t->snd_retry = SEQN(una); /* XXX replace by retry pointer? */
+                t->snd_nxt = SEQN(una);              /* XXX prevent out-of-order-packets ! */ /*DLA re-enabled.*/
+                t->snd_retry = SEQN(una);              /* XXX replace by retry pointer? */
 
                 /* Alternative to the line above:  (better performance, but seems to lock anyway with larger buffers)
                    if (seq_compare(t->snd_nxt, SEQN(una)) > 0)
@@ -2379,10 +2415,6 @@
                  */
 
                 t->x_mode = PICO_TCP_WINDOW_FULL;
-                if (t->keepalive_timer_running == 0) {
-                    tcp_dbg("[Window full] Adding timer(send keepalive)\n");
-                    tcp_send_keepalive(0, t);
-                }
             }
 
             break;
@@ -2397,26 +2429,23 @@
             break;
 
         if (f->payload_len > 0) {
+            data_sent++;
             f = next_segment(&t->tcpq_out, f);
         } else {
             f = NULL;
         }
     }
-    if (sent > 0) {
+    if ((sent > 0 && data_sent > 0)) {
         if (t->rto < PICO_TCP_RTO_MIN)
             t->rto = PICO_TCP_RTO_MIN;
-
-        /* if (s->wakeup) */
-        /*  t->sock.wakeup(PICO_SOCK_EV_WR, &t->sock); */
-        add_retransmission_timer(t, pico_tick + t->rto);
     } else {
         /* Nothing to transmit. */
     }
 
-    if ((t->tcpq_out.frames == 0) && (s->state & PICO_SOCKET_STATE_SHUT_LOCAL)) {  /* if no more packets in queue, XXX replacled !f by tcpq check */
-        if(!checkLocalClosing(&t->sock)) /* check if local closing started and send fin */
+    if ((t->tcpq_out.frames == 0) && (s->state & PICO_SOCKET_STATE_SHUT_LOCAL)) {              /* if no more packets in queue, XXX replacled !f by tcpq check */
+        if(!checkLocalClosing(&t->sock))              /* check if local closing started and send fin */
         {
-            checkRemoteClosing(&t->sock); /* check if remote closing started and send fin */
+            checkRemoteClosing(&t->sock);              /* check if remote closing started and send fin */
         }
     }
 
@@ -2460,7 +2489,7 @@
     f_new->sock = s;
 
     f_temp = first_segment(&t->tcpq_hold);
-    hdr->seq = ((struct pico_tcp_hdr *)(f_temp->transport_hdr))->seq; /* get sequence number of first frame */
+    hdr->seq = ((struct pico_tcp_hdr *)(f_temp->transport_hdr))->seq;              /* get sequence number of first frame */
     hdr->trans.sport = t->sock.local_port;
     hdr->trans.dport = t->sock.remote_port;
 
@@ -2482,7 +2511,7 @@
 
 /* original behavior kept when Nagle disabled;
    Nagle algorithm added here, keeping hold frame queue instead of eg linked list of data */
-int32_t pico_tcp_push(struct pico_protocol *self, struct pico_frame *f)
+int pico_tcp_push(struct pico_protocol *self, struct pico_frame *f)
 {
     struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *)f->transport_hdr;
     struct pico_socket_tcp *t = (struct pico_socket_tcp *) f->sock;
@@ -2514,7 +2543,7 @@
     /***************************************************************************/
     else {
         /* Nagle's algorithm enabled, check if ready to send, or put frame in hold queue */
-        if (IS_TCP_IDLE(t) && IS_TCP_HOLDQ_EMPTY(t)) { /* opt 1. send frame */
+        if (IS_TCP_IDLE(t) && IS_TCP_HOLDQ_EMPTY(t)) {              /* opt 1. send frame */
             if (pico_enqueue_segment(&t->tcpq_out, f) > 0) {
                 tcp_dbg_nagle("TCP_PUSH - NAGLE - Pushing segment %08x, len %08x to socket %p\n", t->snd_last + 1, f->payload_len, t);
                 t->snd_last += f->payload_len;
@@ -2525,12 +2554,12 @@
             }
         } else {                                    /* opt 2. hold data back */
             total_len = f->payload_len + t->tcpq_hold.size;
-            if ((total_len >= PICO_TCP_DEFAULT_MSS) && ((t->tcpq_out.max_size - t->tcpq_out.size) >= PICO_TCP_DEFAULT_MSS)) { /* TODO check mss socket */
+            if ((total_len >= PICO_TCP_DEFAULT_MSS) && ((t->tcpq_out.max_size - t->tcpq_out.size) >= PICO_TCP_DEFAULT_MSS)) {              /* TODO check mss socket */
                 /* IF enough data in hold (>mss) AND space in out queue (>mss) */
                 /* add current frame in hold and make new segment */
                 if (pico_enqueue_segment(&t->tcpq_hold, f) > 0 ) {
                     tcp_dbg_nagle("TCP_PUSH - NAGLE - Pushed into hold, make new (enqueued frames out %d)\n", t->tcpq_out.frames);
-                    t->snd_last += f->payload_len; /* XXX  WATCH OUT */
+                    t->snd_last += f->payload_len;              /* XXX  WATCH OUT */
                     f_new = pico_hold_segment_make(t);
                 } else {
                     tcp_dbg_nagle("TCP_PUSH - NAGLE - enqueue hold failed 1\n");
@@ -2548,7 +2577,7 @@
                 /* ELSE put frame in hold queue */
                 if (pico_enqueue_segment(&t->tcpq_hold, f) > 0) {
                     tcp_dbg_nagle("TCP_PUSH - NAGLE - Pushed into hold (enqueued frames out %d)\n", t->tcpq_out.frames);
-                    t->snd_last += f->payload_len; /* XXX  WATCH OUT */
+                    t->snd_last += f->payload_len;              /* XXX  WATCH OUT */
                     return f->payload_len;
                 } else {
                     pico_err = PICO_ERR_EAGAIN;
@@ -2590,8 +2619,10 @@
 void pico_tcp_cleanup_queues(struct pico_socket *sck)
 {
     struct pico_socket_tcp *tcp = (struct pico_socket_tcp *)sck;
-    if(tcp->retrans_tmr)
+    if(tcp->retrans_tmr) {
         pico_timer_cancel(tcp->retrans_tmr);
+        tcp->retrans_tmr = NULL;
+    }
 
     tcp_discard_all_segments(&tcp->tcpq_in);
     tcp_discard_all_segments(&tcp->tcpq_out);
--- a/stack/pico_arp.c	Tue Feb 11 14:44:54 2014 +0100
+++ b/stack/pico_arp.c	Tue Feb 11 14:48:37 2014 +0100
@@ -31,7 +31,7 @@
 static int pending_timer_on = 0;
 static int max_arp_reqs = PICO_ARP_MAX_RATE;
 
-void check_pending(pico_time now, void *_unused)
+static void check_pending(pico_time now, void *_unused)
 {
     struct pico_frame *f = pico_dequeue(&pending);
     IGNORE_PARAMETER(now);
@@ -41,8 +41,7 @@
         return;
     }
 
-    if(pico_ethernet_send(f) > 0)
-        pico_frame_discard(f);
+    pico_ethernet_send(f);
 
     pico_timer_add(PICO_ARP_RETRY, &check_pending, NULL);
 }
@@ -154,6 +153,10 @@
     struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
     struct pico_ipv4_link *l;
 
+#ifndef PICO_SUPPORT_IPV4
+    return NULL;
+#endif
+
     l = pico_ipv4_link_get(&hdr->dst);
     if(l) {
         /* address belongs to ourself */
@@ -169,7 +172,7 @@
 
     if (!a4) {
         if (++f->failure_count < 4) {
-            dbg ("================= ARP REQUIRED: %d =============\n\n", f->failure_count);
+            arp_dbg ("================= ARP REQUIRED: %d =============\n\n", f->failure_count);
             /* check if dst is local (gateway = 0), or if to use gateway */
             if (gateway.addr != 0)
                 pico_arp_request(f->dev, &gateway, PICO_ARP_QUERY); /* arp to gateway */
@@ -204,17 +207,21 @@
 }
 #endif
 
-void arp_expire(pico_time now, void *_stale)
+static void arp_expire(pico_time now, void *_stale)
 {
     struct pico_arp *stale = (struct pico_arp *) _stale;
-    IGNORE_PARAMETER(now);
-    stale->arp_status = PICO_ARP_STATUS_STALE;
-    arp_dbg("ARP: Setting arp_status to STALE\n");
-    pico_arp_request(stale->dev, &stale->ipv4, PICO_ARP_QUERY);
-
+    if (now >= (stale->timestamp + PICO_ARP_TIMEOUT)) {
+        stale->arp_status = PICO_ARP_STATUS_STALE;
+        arp_dbg("ARP: Setting arp_status to STALE\n");
+        pico_arp_request(stale->dev, &stale->ipv4, PICO_ARP_QUERY);
+    } else {
+        /* Timer must be rescheduled, ARP entry has been renewed lately.
+         * No action required to refresh the entry, will check on the next timeout */
+        pico_timer_add(PICO_ARP_TIMEOUT + stale->timestamp - now, arp_expire, stale);
+    }
 }
 
-void pico_arp_add_entry(struct pico_arp *entry)
+static void pico_arp_add_entry(struct pico_arp *entry)
 {
     entry->arp_status = PICO_ARP_STATUS_REACHABLE;
     entry->timestamp  = PICO_TIME();
@@ -250,12 +257,17 @@
     struct pico_eth_hdr *eh;
     int ret = -1;
     hdr = (struct pico_arp_hdr *) f->net_hdr;
-    me.addr = hdr->dst.addr;
     eh = (struct pico_eth_hdr *)f->datalink_hdr;
 
     if (!hdr)
         goto end;
 
+#ifndef PICO_SUPPORT_IPV4
+    goto end;
+#endif
+
+    me.addr = hdr->dst.addr;
+
     /* Validate the incoming arp packet */
 
     /* Check the hardware type and protocol */
@@ -293,15 +305,13 @@
             new = found;
 
             pico_tree_delete(&arp_tree, new);
-        }
-        else {
+        } else {
             /* Update mac address */
             memcpy(found->eth.addr, hdr->s_mac, PICO_SIZE_ETH);
 
-            /* Refresh timeout & update timestamp*/
-            pico_timer_cancel(found->timer);
-            found->timer = pico_timer_add(PICO_ARP_TIMEOUT, arp_expire, found);
+            /* Refresh timestamp, this will force a reschedule on the next timeout*/ 
             found->timestamp = PICO_TIME();
+            new = NULL; /* Avoid re-inserting the entry in the table */
         }
     }
 
@@ -355,21 +365,27 @@
     struct pico_frame *q = pico_frame_alloc(PICO_SIZE_ETHHDR + PICO_SIZE_ARPHDR);
     struct pico_eth_hdr *eh;
     struct pico_arp_hdr *ah;
-    struct pico_ip4 *src=NULL;
+    struct pico_ip4 *src = NULL;
     int ret;
 
+    if (!q)
+        return -1;
+
+#ifndef PICO_SUPPORT_IPV4
+    return -1;
+#endif
+
     if (type == PICO_ARP_QUERY)
     {
         src = pico_ipv4_source_find(dst);
-        if (!src)
+        if (!src) {
+            pico_frame_discard(q);
             return -1;
+        }
     }
 
     arp_dbg("QUERY: %08x\n", dst->addr);
 
-    if (!q)
-        return -1;
-
     eh = (struct pico_eth_hdr *)q->start;
     ah = (struct pico_arp_hdr *) (q->start + PICO_SIZE_ETHHDR);
 
--- a/stack/pico_device.c	Tue Feb 11 14:44:54 2014 +0100
+++ b/stack/pico_device.c	Tue Feb 11 14:48:37 2014 +0100
@@ -14,6 +14,13 @@
 #include "pico_protocol.h"
 #include "pico_tree.h"
 
+struct pico_devices_rr_info {
+    struct pico_tree_node *node_in, *node_out;
+};
+
+static struct pico_devices_rr_info Devices_rr_info = {
+    NULL, NULL
+};
 
 static int pico_dev_cmp(void *ka, void *kb)
 {
@@ -39,6 +46,8 @@
     dev->hash = pico_hash(dev->name, len);
 
     pico_tree_insert(&Device_tree, dev);
+    Devices_rr_info.node_in  = NULL;
+    Devices_rr_info.node_out = NULL;
     dev->q_in = pico_zalloc(sizeof(struct pico_queue));
     dev->q_out = pico_zalloc(sizeof(struct pico_queue));
 
@@ -55,144 +64,181 @@
     return 0;
 }
 
+static void pico_queue_destroy(struct pico_queue *q)
+{
+    if (q) {
+        pico_queue_empty(q);
+        pico_free(q);
+    }
+}
+
 void pico_device_destroy(struct pico_device *dev)
 {
     if (dev->destroy)
         dev->destroy(dev);
 
-    if (dev->q_in) {
-        pico_queue_empty(dev->q_in);
-        pico_free(dev->q_in);
-    }
-
-    if (dev->q_out) {
-        pico_queue_empty(dev->q_out);
-        pico_free(dev->q_out);
-    }
+    pico_queue_destroy(dev->q_in);
+    pico_queue_destroy(dev->q_out);
 
     if (dev->eth)
         pico_free(dev->eth);
 
     pico_tree_delete(&Device_tree, dev);
+    Devices_rr_info.node_in  = NULL;
+    Devices_rr_info.node_out = NULL;
     pico_free(dev);
 }
 
-static int devloop(struct pico_device *dev, int loop_score, int direction)
+static int check_dev_serve_interrupt(struct pico_device *dev, int loop_score)
 {
-    struct pico_frame *f;
-
-    /* If device supports interrupts, read the value of the condition and trigger the dsr */
     if ((dev->__serving_interrupt) && (dev->dsr)) {
         /* call dsr routine */
         loop_score = dev->dsr(dev, loop_score);
     }
 
-    /* If device supports polling, give control. Loop score is managed internally,
-     * remaining loop points are returned. */
+    return loop_score;
+}
+
+static int check_dev_serve_polling(struct pico_device *dev, int loop_score)
+{
     if (dev->poll) {
         loop_score = dev->poll(dev, loop_score);
     }
 
-    if (direction == PICO_LOOP_DIR_OUT) {
-
-        while(loop_score > 0) {
-            if (dev->q_out->frames <= 0)
-                break;
-
-            /* Device dequeue + send */
-            f = pico_dequeue(dev->q_out);
-            if (f) {
-                if (dev->eth) {
-                    int ret = pico_ethernet_send(f);
-                    if (0 == ret) {
-                        loop_score--;
-                        continue;
-                    }
-
-                    if (ret < 0) {
-                        if (!pico_source_is_local(f)) {
-                            dbg("Destination unreachable -------> SEND ICMP\n");
-                            pico_notify_dest_unreachable(f);
-                        } else {
-                            dbg("Destination unreachable -------> LOCAL\n");
-                        }
-
-                        pico_frame_discard(f);
-                        continue;
-                    }
-                } else {
-                    dev->send(dev, f->start, (int)f->len);
-                }
-
-                pico_frame_discard(f);
-                loop_score--;
-            }
-        }
-    } else if (direction == PICO_LOOP_DIR_IN) {
-
-        while(loop_score > 0) {
-            if (dev->q_in->frames <= 0)
-                break;
-
-            /* Receive */
-            f = pico_dequeue(dev->q_in);
-            if (f) {
-                if (dev->eth) {
-                    f->datalink_hdr = f->buffer;
-                    pico_ethernet_receive(f);
-                } else {
-                    f->net_hdr = f->buffer;
-                    pico_network_receive(f);
-                }
-
-                loop_score--;
-            }
-        }
-    }
-
     return loop_score;
 }
 
+static int devloop_in(struct pico_device *dev, int loop_score)
+{
+    struct pico_frame *f;
+    while(loop_score > 0) {
+        if (dev->q_in->frames <= 0)
+            break;
+
+        /* Receive */
+        f = pico_dequeue(dev->q_in);
+        if (f) {
+            if (dev->eth) {
+                f->datalink_hdr = f->buffer;
+                pico_ethernet_receive(f);
+            } else {
+                f->net_hdr = f->buffer;
+                pico_network_receive(f);
+            }
+
+            loop_score--;
+        }
+    }
+    return loop_score;
+}
+
+static int devloop_sendto_dev(struct pico_device *dev, struct pico_frame *f)
+{
+
+    int ret;
+    if (dev->eth) {
+        ret = pico_ethernet_send(f);
+        if (0 <= ret) {
+            return -1;
+        } else {
+            if (!pico_source_is_local(f)) {
+                dbg("Destination unreachable -------> SEND ICMP\n");
+                pico_notify_dest_unreachable(f);
+            } else {
+                dbg("Destination unreachable -------> LOCAL\n");
+            }
+
+            pico_frame_discard(f);
+            return 1;
+        }
+    } else {
+        /* non-ethernet */
+        if (dev->send(dev, f->start, (int)f->len) <= 0)
+            return -1;
+
+        pico_frame_discard(f);
+        return 1;
+    }
+}
+
+static int devloop_out(struct pico_device *dev, int loop_score)
+{
+    struct pico_frame *f;
+    while(loop_score > 0) {
+        if (dev->q_out->frames <= 0)
+            break;
+
+        /* Device dequeue + send */
+        f = pico_dequeue(dev->q_out);
+        if (!f)
+            break;
+
+        if (devloop_sendto_dev(dev, f) < 0)
+            break;
+
+        loop_score--;
+    }
+    return loop_score;
+}
+
+static int devloop(struct pico_device *dev, int loop_score, int direction)
+{
+    /* If device supports interrupts, read the value of the condition and trigger the dsr */
+    loop_score = check_dev_serve_interrupt(dev, loop_score);
+
+    /* If device supports polling, give control. Loop score is managed internally,
+     * remaining loop points are returned. */
+    loop_score = check_dev_serve_polling(dev, loop_score);
+
+    if (direction == PICO_LOOP_DIR_OUT)
+        loop_score = devloop_out(dev, loop_score);
+    else
+        loop_score = devloop_in(dev, loop_score);
+
+    return loop_score;
+}
+
+
+static struct pico_tree_node *pico_dev_roundrobin_start(int direction)
+{
+    if (Devices_rr_info.node_in == NULL)
+        Devices_rr_info.node_in = pico_tree_firstNode(Device_tree.root);
+
+    if (Devices_rr_info.node_out == NULL)
+        Devices_rr_info.node_out = pico_tree_firstNode(Device_tree.root);
+
+    if (direction == PICO_LOOP_DIR_IN)
+        return Devices_rr_info.node_in;
+    else
+        return Devices_rr_info.node_out;
+}
+
+static void pico_dev_roundrobin_end(int direction, struct pico_tree_node *last)
+{
+    if (direction == PICO_LOOP_DIR_IN)
+        Devices_rr_info.node_in = last;
+    else
+        Devices_rr_info.node_out = last;
+}
 
 #define DEV_LOOP_MIN  16
 
 int pico_devices_loop(int loop_score, int direction)
 {
-    struct pico_device *start;
-    static struct pico_device *next = NULL, *next_in = NULL, *next_out = NULL;
-    static struct pico_tree_node *next_node, *in_node, *out_node;
-
-    if (next_in == NULL) {
-        in_node = pico_tree_firstNode(Device_tree.root);
-        next_in = in_node->keyValue;
-    }
+    struct pico_device *start, *next;
+    struct pico_tree_node *next_node  = pico_dev_roundrobin_start(direction);
 
-    if (next_out == NULL) {
-        out_node = pico_tree_firstNode(Device_tree.root);
-        next_out = out_node->keyValue;
-    }
+    if (!next_node)
+        return loop_score;
 
-    if (direction == PICO_LOOP_DIR_IN)
-    {
-        next_node = in_node;
-        next = next_in;
-    }
-    else if (direction == PICO_LOOP_DIR_OUT)
-    {
-        next_node = out_node;
-        next = next_out;
-    }
-
-    /* init start node */
+    next = next_node->keyValue;
     start = next;
 
     /* round-robin all devices, break if traversed all devices */
-    while (loop_score > DEV_LOOP_MIN && next != NULL) {
+    while ((loop_score > DEV_LOOP_MIN) && (next != NULL)) {
         loop_score = devloop(next, loop_score, direction);
-
         next_node = pico_tree_next(next_node);
         next = next_node->keyValue;
-
         if (next == NULL)
         {
             next_node = pico_tree_firstNode(Device_tree.root);
@@ -202,21 +248,11 @@
         if (next == start)
             break;
     }
-    if (direction == PICO_LOOP_DIR_IN)
-    {
-        in_node = next_node;
-        next_in = next;
-    }
-    else if (direction == PICO_LOOP_DIR_OUT)
-    {
-        out_node = next_node;
-        next_out = next;
-    }
-
+    pico_dev_roundrobin_end(direction, next_node);
     return loop_score;
 }
 
-struct pico_device*pico_get_device(const char*name)
+struct pico_device *pico_get_device(const char*name)
 {
     struct pico_device *dev;
     struct pico_tree_node *index;
@@ -249,7 +285,7 @@
         }
         else
         {
-            ret = (int32_t)f->dev->send(f->dev, f->start, (int)f->len);
+            ret = f->dev->send(f->dev, f->start, (int)f->len);
         }
     }
 
--- a/stack/pico_frame.c	Tue Feb 11 14:44:54 2014 +0100
+++ b/stack/pico_frame.c	Tue Feb 11 14:48:37 2014 +0100
@@ -181,26 +181,3 @@
     return (uint16_t) (~sum);
 }
 
-uint16_t pico_dualbuffer_checksum_broken(void *inbuf1, uint16_t len1, void *inbuf2, uint16_t len2)
-{
-    uint16_t *b1 = (uint16_t *) inbuf1;
-    uint16_t *b2 = (uint16_t *) inbuf2;
-    uint32_t sum = 0;
-    int i = 0, j = 0;
-    for(i = 0; i < (len1 >> 1); i++) {
-        sum += short_be(b1[i]);
-        j++;
-    }
-    for(i = 0; i < (len2 >> 1); i++) {
-        sum += short_be(b2[i]);
-        j++;
-    }
-    sum = (sum & 0xFFFF) + (sum >> 16);
-    sum += (sum >> 16);
-
-    /* Take the bitwise complement of sum */
-    sum = ~sum;
-    return (uint16_t) (sum);
-}
-
-
--- a/stack/pico_protocol.c	Tue Feb 11 14:44:54 2014 +0100
+++ b/stack/pico_protocol.c	Tue Feb 11 14:48:37 2014 +0100
@@ -11,6 +11,13 @@
 #include "pico_protocol.h"
 #include "pico_tree.h"
 
+struct pico_proto_rr
+{
+    struct pico_tree *t;
+    struct pico_tree_node *node_in, *node_out;
+};
+
+
 static int pico_proto_cmp(void *ka, void *kb)
 {
     struct pico_protocol *a = ka, *b = kb;
@@ -28,295 +35,136 @@
 PICO_TREE_DECLARE(Transport_proto_tree, pico_proto_cmp);
 PICO_TREE_DECLARE(Socket_proto_tree, pico_proto_cmp);
 
-static int proto_loop(struct pico_protocol *proto, int loop_score, int direction)
+/* Static variables to keep track of the round robin loop */
+static struct pico_proto_rr proto_rr_datalink   = {
+    &Datalink_proto_tree,     NULL, NULL
+};
+static struct pico_proto_rr proto_rr_network    = {
+    &Network_proto_tree,      NULL, NULL
+};
+static struct pico_proto_rr proto_rr_transport  = {
+    &Transport_proto_tree,    NULL, NULL
+};
+static struct pico_proto_rr proto_rr_socket     = {
+    &Socket_proto_tree,       NULL, NULL
+};
+
+static int proto_loop_in(struct pico_protocol *proto, int loop_score)
 {
     struct pico_frame *f;
-
-    if (direction == PICO_LOOP_DIR_IN) {
-
-        while(loop_score > 0) {
-            if (proto->q_in->frames <= 0)
-                break;
+    while(loop_score > 0) {
+        if (proto->q_in->frames <= 0)
+            break;
 
-            f = pico_dequeue(proto->q_in);
-            if ((f) && (proto->process_in(proto, f) > 0)) {
-                loop_score--;
-            }
-        }
-    } else if (direction == PICO_LOOP_DIR_OUT) {
-
-        while(loop_score > 0) {
-            if (proto->q_out->frames <= 0)
-                break;
-
-            f = pico_dequeue(proto->q_out);
-            if ((f) && (proto->process_out(proto, f) > 0)) {
-                loop_score--;
-            }
+        f = pico_dequeue(proto->q_in);
+        if ((f) && (proto->process_in(proto, f) > 0)) {
+            loop_score--;
         }
     }
+    return loop_score;
+}
+
+static int proto_loop_out(struct pico_protocol *proto, int loop_score)
+{
+    struct pico_frame *f;
+    while(loop_score > 0) {
+        if (proto->q_out->frames <= 0)
+            break;
+
+        f = pico_dequeue(proto->q_out);
+        if ((f) && (proto->process_out(proto, f) > 0)) {
+            loop_score--;
+        }
+    }
+    return loop_score;
+}
+
+static int proto_loop(struct pico_protocol *proto, int loop_score, int direction)
+{
+
+    if (direction == PICO_LOOP_DIR_IN)
+        loop_score = proto_loop_in(proto, loop_score);
+    else if (direction == PICO_LOOP_DIR_OUT)
+        loop_score = proto_loop_out(proto, loop_score);
 
     return loop_score;
 }
 
-#define DL_LOOP_MIN 1
-
-int pico_protocol_datalink_loop(int loop_score, int direction)
+static struct pico_tree_node *roundrobin_init(struct pico_proto_rr *rr, int direction)
 {
-    struct pico_protocol *start;
-    static struct pico_protocol *next = NULL, *next_in = NULL, *next_out = NULL;
-    static struct pico_tree_node *next_node, *in_node, *out_node;
+    struct pico_tree_node *next_node = NULL;
+    /* Initialization (takes place only once) */
+    if (rr->node_in == NULL)
+        rr->node_in = pico_tree_firstNode(rr->t->root);
 
-    if (next_in == NULL) {
-        in_node = pico_tree_firstNode(Datalink_proto_tree.root);
-        if (in_node)
-            next_in = in_node->keyValue;
-    }
-
-    if (next_out == NULL) {
-        out_node = pico_tree_firstNode(Datalink_proto_tree.root);
-        if (out_node)
-            next_out = out_node->keyValue;
-    }
+    if (rr->node_out == NULL)
+        rr->node_out = pico_tree_firstNode(rr->t->root);
 
     if (direction == PICO_LOOP_DIR_IN)
-    {
-        next_node = in_node;
-        next = next_in;
-    }
-    else if (direction == PICO_LOOP_DIR_OUT)
-    {
-        next_node = out_node;
-        next = next_out;
-    }
+        next_node = rr->node_in;
+    else
+        next_node = rr->node_out;
+
+    return next_node;
+}
+
+static void roundrobin_end(struct pico_proto_rr *rr, int direction, struct pico_tree_node *last)
+{
+    if (direction == PICO_LOOP_DIR_IN)
+        rr->node_in = last;
+    else
+        rr->node_out = last;
+}
+
+static int pico_protocol_generic_loop(struct pico_proto_rr *rr, int loop_score, int direction)
+{
+    struct pico_protocol *start, *next;
+    struct pico_tree_node *next_node = roundrobin_init(rr, direction);
+
+    if (!next_node)
+        return loop_score;
+    else
+        next = next_node->keyValue;
 
     /* init start node */
     start = next;
 
-    /* round-robin all datalink protocols, break if traversed all protocols */
-    while (loop_score > DL_LOOP_MIN && next != NULL) {
+    /* round-robin all layer protocols, break if traversed all protocols */
+    while (loop_score > 1 && next != NULL) {
         loop_score = proto_loop(next, loop_score, direction);
-
-        /* next = RB_NEXT(pico_protocol_tree, &Datalink_proto_tree, next); */
         next_node = pico_tree_next(next_node);
         next = next_node->keyValue;
-
         if (next == NULL)
         {
-            next_node = pico_tree_firstNode(Datalink_proto_tree.root);
+            next_node = pico_tree_firstNode(rr->t->root);
             next = next_node->keyValue;
         }
 
         if (next == start)
             break;
     }
-    if (direction == PICO_LOOP_DIR_IN)
-    {
-        in_node = next_node;
-        next_in = next;
-    }
-    else if (direction == PICO_LOOP_DIR_OUT)
-    {
-        out_node = next_node;
-        next_out = next;
-    }
-
+    roundrobin_end(rr, direction, next_node);
     return loop_score;
 }
 
-
-#define NW_LOOP_MIN 1
+int pico_protocol_datalink_loop(int loop_score, int direction)
+{
+    return pico_protocol_generic_loop(&proto_rr_datalink, loop_score, direction);
+}
 
 int pico_protocol_network_loop(int loop_score, int direction)
 {
-    struct pico_protocol *start;
-    static struct pico_protocol *next = NULL, *next_in = NULL, *next_out = NULL;
-    static struct pico_tree_node *next_node, *in_node, *out_node;
-
-    if (next_in == NULL) {
-        in_node = pico_tree_firstNode(Network_proto_tree.root);
-        if (in_node)
-            next_in = in_node->keyValue;
-    }
-
-    if (next_out == NULL) {
-        out_node = pico_tree_firstNode(Network_proto_tree.root);
-        if (out_node)
-            next_out = out_node->keyValue;
-    }
-
-    if (direction == PICO_LOOP_DIR_IN)
-    {
-        next_node = in_node;
-        next = next_in;
-    }
-    else if (direction == PICO_LOOP_DIR_OUT)
-    {
-        next_node = out_node;
-        next = next_out;
-    }
-
-    /* init start node */
-    start = next;
-
-    /* round-robin all network protocols, break if traversed all protocols */
-    while (loop_score > NW_LOOP_MIN && next != NULL) {
-        loop_score = proto_loop(next, loop_score, direction);
-
-        next_node = pico_tree_next(next_node);
-        next = next_node->keyValue;
-
-        if (next == NULL)
-        {
-            next_node = pico_tree_firstNode(Network_proto_tree.root);
-            next = next_node->keyValue;
-        }
-
-        if (next == start)
-            break;
-    }
-    if (direction == PICO_LOOP_DIR_IN)
-    {
-        in_node = next_node;
-        next_in = next;
-    }
-    else if (direction == PICO_LOOP_DIR_OUT)
-    {
-        out_node = next_node;
-        next_out = next;
-    }
-
-    return loop_score;
+    return pico_protocol_generic_loop(&proto_rr_network, loop_score, direction);
 }
 
-#define TP_LOOP_MIN 1
-
 int pico_protocol_transport_loop(int loop_score, int direction)
 {
-    struct pico_protocol *start;
-    static struct pico_protocol *next = NULL, *next_in = NULL, *next_out = NULL;
-    static struct pico_tree_node *next_node, *in_node, *out_node;
-
-    if (next_in == NULL) {
-        in_node = pico_tree_firstNode(Transport_proto_tree.root);
-        if (in_node)
-            next_in = in_node->keyValue;
-    }
-
-    if (next_out == NULL) {
-        out_node = pico_tree_firstNode(Transport_proto_tree.root);
-        if (out_node)
-            next_out = out_node->keyValue;
-    }
-
-    if (direction == PICO_LOOP_DIR_IN)
-    {
-        next_node = in_node;
-        next = next_in;
-    }
-    else if (direction == PICO_LOOP_DIR_OUT)
-    {
-        next_node = out_node;
-        next = next_out;
-    }
-
-    /* init start node */
-    start = next;
-
-    /* round-robin all transport protocols, break if traversed all protocols */
-    while (loop_score > DL_LOOP_MIN && next != NULL) {
-        loop_score = proto_loop(next, loop_score, direction);
-
-        /* next = RB_NEXT(pico_protocol_tree, &Transport_proto_tree, next); */
-        next_node = pico_tree_next(next_node);
-        next = next_node->keyValue;
-
-        if (next == NULL)
-        {
-            next_node = pico_tree_firstNode(Transport_proto_tree.root);
-            next = next_node->keyValue;
-        }
-
-        if (next == start)
-            break;
-    }
-    if (direction == PICO_LOOP_DIR_IN)
-    {
-        in_node = next_node;
-        next_in = next;
-    }
-    else if (direction == PICO_LOOP_DIR_OUT)
-    {
-        out_node = next_node;
-        next_out = next;
-    }
-
-    return loop_score;
+    return pico_protocol_generic_loop(&proto_rr_transport, loop_score, direction);
 }
 
-
-#define SOCK_LOOP_MIN 1
-
 int pico_protocol_socket_loop(int loop_score, int direction)
 {
-    struct pico_protocol *start;
-    static struct pico_protocol *next = NULL, *next_in = NULL, *next_out = NULL;
-    static struct pico_tree_node *next_node, *in_node, *out_node;
-
-    if (next_in == NULL) {
-        in_node = pico_tree_firstNode(Socket_proto_tree.root);
-        if(in_node)
-            next_in = in_node->keyValue;
-    }
-
-    if (next_out == NULL) {
-        out_node = pico_tree_firstNode(Socket_proto_tree.root);
-        if(out_node)
-            next_out = out_node->keyValue;
-    }
-
-    if (direction == PICO_LOOP_DIR_IN)
-    {
-        next_node = in_node;
-        next = next_in;
-    }
-    else if (direction == PICO_LOOP_DIR_OUT)
-    {
-        next_node = out_node;
-        next = next_out;
-    }
-
-    /* init start node */
-    start = next;
-
-    /* round-robin all transport protocols, break if traversed all protocols */
-    while (loop_score > SOCK_LOOP_MIN && next != NULL) {
-        loop_score = proto_loop(next, loop_score, direction);
-
-        next_node = pico_tree_next(next_node);
-        next = next_node->keyValue;
-
-        if (next == NULL)
-        {
-            next_node = pico_tree_firstNode(next_node);
-            next = next_node->keyValue;
-        }
-
-        if (next == start)
-            break;
-    }
-    if (direction == PICO_LOOP_DIR_IN)
-    {
-        in_node = next_node;
-        next_in = next;
-    }
-    else if (direction == PICO_LOOP_DIR_OUT)
-    {
-        out_node = next_node;
-        next_out = next;
-    }
-
-    return loop_score;
+    return pico_protocol_generic_loop(&proto_rr_socket, loop_score, direction);
 }
 
 int pico_protocols_loop(int loop_score)
@@ -330,6 +178,12 @@
     return loop_score;
 }
 
+static void proto_layer_rr_reset(struct pico_proto_rr *rr)
+{
+    rr->node_in = NULL;
+    rr->node_out = NULL;
+}
+
 void pico_protocol_init(struct pico_protocol *p)
 {
     if (!p)
@@ -339,15 +193,19 @@
     switch (p->layer) {
     case PICO_LAYER_DATALINK:
         pico_tree_insert(&Datalink_proto_tree, p);
+        proto_layer_rr_reset(&proto_rr_datalink);
         break;
     case PICO_LAYER_NETWORK:
         pico_tree_insert(&Network_proto_tree, p);
+        proto_layer_rr_reset(&proto_rr_network);
         break;
     case PICO_LAYER_TRANSPORT:
         pico_tree_insert(&Transport_proto_tree, p);
+        proto_layer_rr_reset(&proto_rr_transport);
         break;
     case PICO_LAYER_SOCKET:
         pico_tree_insert(&Socket_proto_tree, p);
+        proto_layer_rr_reset(&proto_rr_socket);
         break;
     }
     dbg("Protocol %s registered (layer: %d).\n", p->name, p->layer);
--- a/stack/pico_socket.c	Tue Feb 11 14:44:54 2014 +0100
+++ b/stack/pico_socket.c	Tue Feb 11 14:48:37 2014 +0100
@@ -702,6 +702,7 @@
 #ifdef PICO_SUPPORT_TCP
     if(s->parent)
         s->parent->number_of_pending_conn--;
+
 #endif
 
     s->state = PICO_SOCKET_STATE_CLOSED;
@@ -896,8 +897,10 @@
         s = pico_tcp_open();
         s->proto = &pico_proto_tcp;
         /*check if Nagle enabled */
+        /* 
         if (!IS_NAGLE_ENABLED(s))
             dbg("ERROR Nagle should be enabled here\n\n");
+        */
     }
 
 #endif
@@ -2230,12 +2233,16 @@
             return -1;
 
         /* if no activity, force the socket into closing state */
+        /* DLA: Why? This seems off-specs. */
+#ifdef TCP_OFF_SPECS
         if( TCP_STATE(s) == PICO_SOCKET_STATE_TCP_ESTABLISHED )
         {
             s->wakeup(PICO_SOCK_EV_CLOSE, s);
             pico_socket_close(s);
             s->timestamp = PICO_TIME_MS();
         }
+
+#endif
     }
 
     return 0;
@@ -2413,22 +2420,29 @@
                 if (s->wakeup) {
                     /* dbg("SOCKET ERROR FROM ICMP NOTIFICATION. (icmp code= %d)\n\n", code); */
                     switch(code) {
+                    case PICO_ICMP_UNREACH_NET:
+                        pico_err = PICO_ERR_ENETUNREACH;
+                        break;
+
+                    case PICO_ICMP_UNREACH_HOST:
+                        pico_err = PICO_ERR_EHOSTUNREACH;
+                        break;
+
                     case PICO_ICMP_UNREACH_PROTOCOL:
-                        pico_err = PICO_ERR_EPROTO;
+                        pico_err = PICO_ERR_ENOPROTOOPT;
                         break;
 
                     case PICO_ICMP_UNREACH_PORT:
                         pico_err = PICO_ERR_ECONNREFUSED;
                         break;
 
-                    case PICO_ICMP_UNREACH_NET:
                     case PICO_ICMP_UNREACH_NET_PROHIB:
                     case PICO_ICMP_UNREACH_NET_UNKNOWN:
                         pico_err = PICO_ERR_ENETUNREACH;
                         break;
 
                     default:
-                        pico_err = PICO_ERR_EHOSTUNREACH;
+                        pico_err = PICO_ERR_EOPNOTSUPP;
                     }
                     s->state |= PICO_SOCKET_STATE_SHUT_REMOTE;
                     s->wakeup(PICO_SOCK_EV_ERR, s);
--- a/stack/pico_stack.c	Tue Feb 11 14:44:54 2014 +0100
+++ b/stack/pico_stack.c	Tue Feb 11 14:48:37 2014 +0100
@@ -29,12 +29,10 @@
 
 #define IS_LIMITED_BCAST(f) (((struct pico_ipv4_hdr *) f->net_hdr)->dst.addr == PICO_IP4_BCAST)
 
-#ifdef PICO_SUPPORT_MCAST
 # define PICO_SIZE_MCAST 3
 const uint8_t PICO_ETHADDR_MCAST[6] = {
     0x01, 0x00, 0x5e, 0x00, 0x00, 0x00
 };
-#endif
 
 volatile pico_time pico_tick;
 volatile pico_err_t pico_err;
@@ -265,30 +263,50 @@
  * those devices supporting ETH in order to push packets up
  * into the stack.
  */
+
+static int32_t pico_ll_receive(struct pico_frame *f)
+{
+    struct pico_eth_hdr *hdr = (struct pico_eth_hdr *) f->datalink_hdr;
+    f->net_hdr = f->datalink_hdr + sizeof(struct pico_eth_hdr);
+    if (hdr->proto == PICO_IDETH_ARP)
+        return pico_arp_receive(f);
+    else if ((hdr->proto == PICO_IDETH_IPV4) || (hdr->proto == PICO_IDETH_IPV6))
+        return pico_network_receive(f);
+    else {
+        pico_frame_discard(f);
+        return -1;
+    }
+}
+
+static void pico_ll_check_bcast(struct pico_frame *f)
+{
+    struct pico_eth_hdr *hdr = (struct pico_eth_hdr *) f->datalink_hdr;
+    /* Indicate a link layer broadcast packet */
+    if (memcmp(hdr->daddr, PICO_ETHADDR_ALL, PICO_SIZE_ETH) == 0)
+        f->flags |= PICO_FRAME_FLAG_BCAST;
+}
+
 int32_t pico_ethernet_receive(struct pico_frame *f)
 {
     struct pico_eth_hdr *hdr;
     if (!f || !f->dev || !f->datalink_hdr)
-        goto discard;
+    {
+        pico_frame_discard(f);
+        return -1;
+    }
 
     hdr = (struct pico_eth_hdr *) f->datalink_hdr;
     if ((memcmp(hdr->daddr, f->dev->eth->mac.addr, PICO_SIZE_ETH) != 0) &&
-#ifdef PICO_SUPPORT_MCAST
         (memcmp(hdr->daddr, PICO_ETHADDR_MCAST, PICO_SIZE_MCAST) != 0) &&
-#endif
         (memcmp(hdr->daddr, PICO_ETHADDR_ALL, PICO_SIZE_ETH) != 0))
-        goto discard;
+    {
+        pico_frame_discard(f);
+        return -1;
+    }
 
-    f->net_hdr = f->datalink_hdr + sizeof(struct pico_eth_hdr);
-    if (hdr->proto == PICO_IDETH_ARP)
-        return pico_arp_receive(f);
+    pico_ll_check_bcast(f);
 
-    if ((hdr->proto == PICO_IDETH_IPV4) || (hdr->proto == PICO_IDETH_IPV6))
-        return pico_network_receive(f);
-
-discard:
-    pico_frame_discard(f);
-    return -1;
+    return pico_ll_receive(f);
 }
 
 static int destination_is_bcast(struct pico_frame *f)
@@ -309,7 +327,6 @@
 #endif
 }
 
-#ifdef PICO_SUPPORT_MCAST
 static int destination_is_mcast(struct pico_frame *f)
 {
     if (!f)
@@ -341,7 +358,16 @@
 }
 
 
-#endif /* PICO_SUPPORT_MCAST */
+
+
+int32_t pico_ethernet_send_ipv6(struct pico_frame *f)
+{
+    (void)f;
+    /*TODO: Neighbor solicitation */
+    return -1;
+}
+
+
 
 /* This is called by dev loop in order to ensure correct ethernet addressing.
  * Returns 0 if the destination is unknown, and -1 if the packet is not deliverable
@@ -350,64 +376,82 @@
  * Only IP packets must pass by this. ARP will always use direct dev->send() function, so
  * we assume IP is used.
  */
+
+static int32_t pico_ethsend_local(struct pico_frame *f, struct pico_eth_hdr *hdr, int *ret)
+{
+    /* Check own mac */
+    if(!memcmp(hdr->daddr, hdr->saddr, PICO_SIZE_ETH)) {
+        dbg("sending out packet destined for our own mac\n");
+        *ret = (int32_t)pico_ethernet_receive(f);
+        return 1;
+    }
+
+    return 0;
+}
+
+static int32_t pico_ethsend_bcast(struct pico_frame *f, int *ret)
+{
+    if (IS_LIMITED_BCAST(f)) {
+        *ret = pico_device_broadcast(f);
+        return 1;
+    }
+
+    return 0;
+}
+
+static int32_t pico_ethsend_dispatch(struct pico_frame *f, int *ret)
+{
+    *ret = f->dev->send(f->dev, f->start, (int) f->len);
+    if (*ret <= 0)
+        return 0;
+    else {
+        pico_frame_discard(f);
+        return 1;
+    }
+}
+
 int32_t pico_ethernet_send(struct pico_frame *f)
 {
     const struct pico_eth *dstmac = NULL;
     int32_t ret = -1;
 
-    if (IS_IPV6(f)) {
-        /*TODO: Neighbor solicitation */
-        dstmac = NULL;
+    if (IS_IPV6(f))
+        return pico_ethernet_send_ipv6(f);
+
+    if (IS_BCAST(f) || destination_is_bcast(f))
+        dstmac = (const struct pico_eth *) PICO_ETHADDR_ALL;
+
+    else if (destination_is_mcast(f)) {
+        uint8_t pico_mcast_mac[6] = {
+            0x01, 0x00, 0x5e, 0x00, 0x00, 0x00
+        };
+        dstmac = pico_ethernet_mcast_translate(f, pico_mcast_mac);
+    }
+    else {
+        dstmac = pico_arp_get(f);
+        if (!dstmac)
+            return 0;
     }
 
-    else if (IS_IPV4(f)) {
-        if (IS_BCAST(f) || destination_is_bcast(f)) {
-            dstmac = (const struct pico_eth *) PICO_ETHADDR_ALL;
-        }
+    /* This sets destination and source address, then pushes the packet to the device. */
+    if (dstmac && (f->start > f->buffer) && ((f->start - f->buffer) >= PICO_SIZE_ETHHDR)) {
+        struct pico_eth_hdr *hdr;
+        f->start -= PICO_SIZE_ETHHDR;
+        f->len += PICO_SIZE_ETHHDR;
+        f->datalink_hdr = f->start;
+        hdr = (struct pico_eth_hdr *) f->datalink_hdr;
+        memcpy(hdr->saddr, f->dev->eth->mac.addr, PICO_SIZE_ETH);
+        memcpy(hdr->daddr, dstmac, PICO_SIZE_ETH);
+        hdr->proto = PICO_IDETH_IPV4;
 
-#ifdef PICO_SUPPORT_MCAST
-        else if (destination_is_mcast(f)) {
-            uint8_t pico_mcast_mac[6] = {
-                0x01, 0x00, 0x5e, 0x00, 0x00, 0x00
-            };
-            dstmac = pico_ethernet_mcast_translate(f, pico_mcast_mac);
-        }
-#endif
-        else {
-            dstmac = pico_arp_get(f);
-            if (!dstmac)
-                return 0;
-        }
-        /* This sets destination and source address, then pushes the packet to the device. */
-        if (dstmac && (f->start > f->buffer) && ((f->start - f->buffer) >= PICO_SIZE_ETHHDR)) {
-            struct pico_eth_hdr *hdr;
-            f->start -= PICO_SIZE_ETHHDR;
-            f->len += PICO_SIZE_ETHHDR;
-            f->datalink_hdr = f->start;
-            hdr = (struct pico_eth_hdr *) f->datalink_hdr;
-            memcpy(hdr->saddr, f->dev->eth->mac.addr, PICO_SIZE_ETH);
-            memcpy(hdr->daddr, dstmac, PICO_SIZE_ETH);
-            hdr->proto = PICO_IDETH_IPV4;
-            if(!memcmp(hdr->daddr, hdr->saddr, PICO_SIZE_ETH)) {
-                dbg("sending out packet destined for our own mac\n");
-                return pico_ethernet_receive(f);
-            }else if(IS_LIMITED_BCAST(f)) {
-                ret = pico_device_broadcast(f);
-            }else {
-                ret = (int32_t)f->dev->send(f->dev, f->start, (int) f->len);
-                /* Frame is discarded after this return by the caller */
-            }
-
-            if(!ret) pico_frame_discard(f);
-
+        if (pico_ethsend_local(f, hdr, &ret) || pico_ethsend_bcast(f, &ret) || pico_ethsend_dispatch(f, &ret)) {
             return ret;
         } else {
             return -1;
         }
-    } /* End IPV4 ethernet addressing */
+    }
 
     return -1;
-
 }
 
 void pico_store_network_origin(void *src, struct pico_frame *f)
@@ -454,12 +498,15 @@
 
     f = pico_frame_alloc(len);
     if (!f)
+    {
+        dbg("Cannot alloc incoming frame!\n");
         return -1;
+    }
 
     /* Association to the device that just received the frame. */
     f->dev = dev;
 
-    /* Setup the start pointer, lenght. */
+    /* Setup the start pointer, length. */
     f->start = f->buffer;
     f->len = f->buffer_len;
     if (f->len > 8) {
@@ -495,7 +542,6 @@
 
 struct pico_timer
 {
-    uint32_t expire;
     void *arg;
     void (*timer)(pico_time timestamp, void *arg);
 };
@@ -503,6 +549,9 @@
 struct pico_timer_ref
 {
     pico_time expire;
+#ifdef JENKINS_DEBUG
+    void *caller;
+#endif
     struct pico_timer *tmr;
 };
 
@@ -547,7 +596,6 @@
     }
 }
 
-
 #define PROTO_DEF_NR      11
 #define PROTO_DEF_AVG_NR  4
 #define PROTO_DEF_SCORE   32
@@ -760,9 +808,24 @@
     t->arg = arg;
     t->timer = timer;
     tref.tmr = t;
+    #ifdef JENKINS_DEBUG
+    //jenkins_dbg("pico_timer_add: now have %d \t caller: %p\n", Timers->n, __builtin_return_address(0));
+    tref.caller = __builtin_return_address(0);
+    #endif
     heap_insert(Timers, &tref);
     if (Timers->n > PICO_MAX_TIMERS) {
-        dbg("Warning: I have %d timers\n", Timers->n);
+        /* dbg("Warning: I have %d timers\n", Timers->n); */
+        #ifdef JENKINS_DEBUG
+        {
+            struct pico_timer_ref *trf = heap_first(Timers);
+            int timer_it = 1; 
+            for (timer_it = 1; timer_it <= Timers->n; timer_it++) 
+            {
+                trf = &Timers->top[timer_it];
+                jenkins_dbg("timer %d [%p] - cb:%p\n",Timers->n - timer_it, trf->tmr, trf->caller,(trf->tmr->timer));    
+            }
+        }
+        #endif
     }
 
     return t;