socket_error_t lwipv4_socket_send_to(struct socket *socket, const void * buf, const size_t len, const struct socket_addr *addr, const uint16_t port)
{
    ip_addr_t a;
    err_t err = ERR_VAL;
    switch(socket->family) {
    case SOCKET_DGRAM: {
        struct pbuf *pb = pbuf_alloc(PBUF_TRANSPORT,len,PBUF_RAM);
        socket_event_t e;
        socket_api_handler_t handler = socket->handler;
        err = pbuf_take(pb, buf, len);
        if (err != ERR_OK)
        	break;
        a.addr = socket_addr_get_ipv4_addr(addr);
        err = udp_sendto(socket->impl, pb, &a, port);
        pbuf_free(pb);
        if (err != ERR_OK)
        	break;
        e.event = SOCKET_EVENT_TX_DONE;
        e.sock = socket;
        e.i.t.sentbytes = len;
        socket->event = &e;
        handler();
        socket->event = NULL;
        break;
    }
    case SOCKET_STREAM:
        err = ERR_USE;
        break;
    }
    return lwipv4_socket_error_remap(err);

}
err_t irqTCPRecv(void * arg, struct tcp_pcb * tpcb, struct pbuf * p, err_t err)
{
    (void) tpcb;
    struct socket_event e;
    struct socket *s = (struct socket *) arg;

    if(err != ERR_OK) {
        e.event = SOCKET_EVENT_ERROR;
        e.i.e = lwipv4_socket_error_remap(err);
        s->event = &e;
        ((socket_api_handler_t)(s->handler))();
        s->event = NULL;
        return ERR_OK;
    }
    /* Check for a disconnect */
    if (p == NULL) {
        e.event = SOCKET_EVENT_DISCONNECT;
        s->event = &e;
        ((socket_api_handler_t) (s->handler))();
        s->event = NULL;
        /* Zero the impl, since a disconnect will cause a free */
        s->impl = NULL;
        return ERR_OK;
    }

    rx_core(s, p);

    e.event = SOCKET_EVENT_RX_DONE;
    s->event = &e;
    ((socket_api_handler_t)(s->handler))();
    s->event = NULL;
    return ERR_OK;
}
socket_error_t lwipv4_socket_send(struct socket *socket, const void * buf, const size_t len)
{
    err_t err = ERR_VAL;
    switch(socket->family) {
    case SOCKET_DGRAM: {
        struct pbuf *pb = pbuf_alloc(PBUF_TRANSPORT,len,PBUF_RAM);
        socket_event_t e;
        socket_api_handler_t handler = socket->handler;
        err = pbuf_take(pb, buf, len);
        if (err != ERR_OK)
            break;
        err = udp_send(socket->impl, pb);
        pbuf_free(pb);
        if (err != ERR_OK)
            break;
        //Notify the application that the transfer is queued at the MAC layer
        e.event = SOCKET_EVENT_TX_DONE;
        e.sock = socket;
        e.i.t.sentbytes = len;
        socket->event = &e;
        handler();
        socket->event = NULL;
        break;
    }
    case SOCKET_STREAM:
        err = tcp_write(socket->impl,buf,len,TCP_WRITE_FLAG_COPY);
        tcp_output(socket->impl); // TODO use opt instead with SOCKET_OPT_NAGGLE
        break;
    }
    return lwipv4_socket_error_remap(err);
}
static err_t irqAccept (void * arg, struct tcp_pcb * newpcb, err_t err)
{
    struct socket * s = (struct socket *)arg;
    struct socket_event e;
    socket_api_handler_t handler = s->handler;
    e.sock = s;
    if (err != ERR_OK) {
        e.event = SOCKET_EVENT_ERROR;
        e.i.e = lwipv4_socket_error_remap(err);
        err = ERR_OK;
        s->event = &e;
        handler();
        s->event = NULL;
    } else {
        e.event = SOCKET_EVENT_ACCEPT;
        e.i.a.newimpl = newpcb;
        e.i.a.reject = 0;
        s->event = &e;
        handler();
        s->event = NULL;
        if (e.i.a.reject) {
            err = ERR_ABRT;
            tcp_abort(newpcb);
        }
    }
    return err;
}
static void tcp_error_handler(void *arg, err_t err)
{
    struct socket *sock = (struct socket *) arg;
    struct socket_event e;
    socket_api_handler_t h = sock->handler;
    e.event = SOCKET_EVENT_ERROR;
    e.i.e = lwipv4_socket_error_remap(err);
    sock->event = &e;
    h();
    sock->event = NULL;
}
static socket_error_t lwipv4_socket_resolve(struct socket *sock, const char *address)
{
    struct ip_addr ia;
    // attempt to resolve with DNS or convert to ip addr
    err_t err = dns_gethostbyname(address, &ia, dnscb, sock);
    if (err == ERR_OK) {
        dnscb(address, &ia, sock);
    }
    if (err == ERR_INPROGRESS)
        err = ERR_OK;
    return lwipv4_socket_error_remap(err);
}
static socket_error_t lwipv4_socket_close(struct socket *sock)
{
    err_t err = ERR_OK;
    if (sock == NULL || sock->impl == NULL)
        return SOCKET_ERROR_NULL_PTR;
    switch (sock->family) {
    case SOCKET_DGRAM:
        udp_disconnect((struct udp_pcb *)sock->impl);
        break;
    case SOCKET_STREAM:
        err = tcp_close((struct tcp_pcb *)sock->impl);
        break;
    default:
        return SOCKET_ERROR_BAD_FAMILY;
    }
    return lwipv4_socket_error_remap(err);
}
static socket_error_t lwipv4_socket_bind(struct socket *sock, const struct socket_addr *address, const uint16_t port)
{
    err_t err = ERR_OK;
    ip_addr_t a;
    switch (sock->family){
    case SOCKET_DGRAM:
        a.addr = socket_addr_get_ipv4_addr(address);
        err = udp_bind((struct udp_pcb *)sock->impl, &a, port);
        break;
    case SOCKET_STREAM:
    a.addr = socket_addr_get_ipv4_addr(address);
        err = tcp_bind((struct tcp_pcb *)sock->impl, &a, port);
        break;
    default:
        return SOCKET_ERROR_BAD_FAMILY;
    }
    return lwipv4_socket_error_remap(err);
}
static socket_error_t lwipv4_socket_connect(struct socket *sock, const struct socket_addr *address, const uint16_t port)
{
    err_t err = ERR_OK;
    if (!socket_addr_is_ipv4(address)) {
        return SOCKET_ERROR_BAD_ADDRESS;
    }
    switch (sock->family){
    case SOCKET_DGRAM:
        err = udp_connect((struct udp_pcb *)sock->impl, (void*)socket_addr_get_ipv4_addrp(address), port);
        break;
    case SOCKET_STREAM:
        err = tcp_connect((struct tcp_pcb *)sock->impl, (void*)socket_addr_get_ipv4_addrp(address), port, irqConnect);
        break;
    default:
        return SOCKET_ERROR_BAD_FAMILY;
    }
    return lwipv4_socket_error_remap(err);
}
socket_error_t lwipv4_socket_send(struct socket *socket, const void * buf, const size_t len)
{
    err_t err = ERR_VAL;
    switch(socket->family) {
    case SOCKET_DGRAM: {
        struct pbuf *pb = pbuf_alloc(PBUF_TRANSPORT,len,PBUF_RAM);
        socket_event_t e;
        socket_api_handler_t handler = socket->handler;
        err = pbuf_take(pb, buf, len);
        if (err != ERR_OK)
            break;
        err = udp_send(socket->impl, pb);
        pbuf_free(pb);
        if (err != ERR_OK)
            break;
        //Notify the application that the transfer is queued at the MAC layer
        e.event = SOCKET_EVENT_TX_DONE;
        e.sock = socket;
        e.i.t.sentbytes = len;
        socket->event = &e;
        handler();
        socket->event = NULL;
        break;
    }
    case SOCKET_STREAM:{
            struct tcp_pcb* pcb = socket->impl;
            err = tcp_write(pcb,buf,len,TCP_WRITE_FLAG_COPY);
            if (tcp_nagle_disabled(pcb)) {

// TODO: Remove when sal-driver-lwip-k64f-eth/#8 is fixed
#ifdef YOTTA_SAL_DRIVER_LWIP_K64F_ETH_VERSION_STRING
                emac_tcp_push_pcb = (struct pbuf * volatile)pcb;
                vIRQ_SetPendingIRQ(ENET_Receive_IRQn);
#else
                tcp_output(pcb);
#endif
            }
            break;
        }
    }
    return lwipv4_socket_error_remap(err);
}
err_t irqTCPRecv(void * arg, struct tcp_pcb * tpcb, struct pbuf * p, err_t err)
{
    struct socket_event e;
    struct socket *s = (struct socket *) arg;

    if(err != ERR_OK) {
        e.event = SOCKET_EVENT_ERROR;
        e.i.e = lwipv4_socket_error_remap(err);
        s->event = &e;
        ((socket_api_handler_t)(s->handler))();
        s->event = NULL;
        return ERR_OK;
    }
    /* Check for a disconnect */
    if (p == NULL) {
        e.event = SOCKET_EVENT_DISCONNECT;
        s->event = &e;
        ((socket_api_handler_t) (s->handler))();
        s->event = NULL;
        /* if close has been called, we have to remove impl, since it could be freed */
        switch (tpcb->state) {
            case FIN_WAIT_1:
            case FIN_WAIT_2:
            case TIME_WAIT:
                /* Zero the impl, since a disconnect will cause a free */
                s->impl = NULL;
                break;
            default:
                break;
        }
        return ERR_OK;
    }

    rx_core(s, p);

    e.event = SOCKET_EVENT_RX_DONE;
    s->event = &e;
    ((socket_api_handler_t)(s->handler))();
    s->event = NULL;
    return ERR_OK;
}
static err_t irqConnect(void * arg, struct tcp_pcb * tpcb, err_t err)
{
    struct socket *sock = (struct socket *) arg;
    socket_api_handler_t handler = (socket_api_handler_t) sock->handler;
    socket_event_t e;
    tcp_sent(tpcb,irqTCPSent);
    tcp_recv(tpcb, irqTCPRecv);

    if (err != ERR_OK)
    {
        e.event = SOCKET_EVENT_ERROR;
        e.i.e = lwipv4_socket_error_remap(err);
    }
    else
    {
        e.event = SOCKET_EVENT_CONNECT;
    }
    sock->event = &e;
    handler();
    sock->event = NULL;
    return ERR_OK;
}