This repository has been archived on 2025-09-14. You can view files and clone it, but cannot push or open issues or pull requests.
Files
zhangyang-libzt/ext/pico_bsd/pico_bsd_sockets.c

1586 lines
45 KiB
C
Raw Normal View History

/*********************************************************************
PicoTCP. Copyright (c) 2013 TASS Belgium NV. Some rights reserved.
See LICENSE and COPYING for usage.
Do not redistribute without a written permission by the Copyright
holders.
Author: Maxime Vincent, Daniele Lacamera
*********************************************************************/
#include "pico_defines.h"
#include "pico_config.h" /* for zalloc and free */
#include "pico_bsd_sockets.h"
#include "pico_osal.h"
#ifdef PICO_SUPPORT_SNTP_CLIENT
#include "pico_sntp_client.h"
#endif
#include <errno.h> /* should be there in C99 */
#define SOCK_OPEN 0
#define SOCK_BOUND 1
#define SOCK_LISTEN 2
#define SOCK_CONNECTED 3
#define SOCK_ERROR 4
#define SOCK_RESET_BY_PEER 5
#define SOCK_CLOSED 100
#define bsd_dbg(...) do {} while(0)
#define bsd_dbg_select(...) do {} while(0)
/* Global signal sent on any event (for select) */
void * picoLock = NULL; /* pico stack lock */
void * pico_signal_select = NULL; /* pico global signal for select */
void * pico_signal_tick = NULL; /* pico tick signal, e.g. coming from a driver ISR */
struct pico_bsd_endpoint {
struct pico_socket *s;
int socket_fd;
int posix_fd; /* TODO: ifdef... */
uint8_t in_use;
uint8_t state; /* for pico_state */
uint8_t nonblocking; /* The non-blocking flag, for non-blocking socket operations */
uint16_t events; /* events that we filter for */
uint16_t revents; /* received events */
uint16_t proto;
void * mutex_lock; /* mutex for clearing revents */
void * signal; /* signals new events */
uint32_t timeout; /* this is used for timeout sockets */
int error; /* used for SO_ERROR sockopt after connect() */
};
/* MACRO's */
#define VALIDATE_NULL(param) \
if(!param) \
{ \
return -1; \
}
#define VALIDATE_ONE(param,value) \
if(param != value) { \
pico_err = PICO_ERR_EINVAL; \
errno = pico_err; \
return -1; \
}
#define VALIDATE_TWO(param,value1,value2) \
if(param != value1 && param != value2) { \
pico_err = PICO_ERR_EINVAL; \
errno = pico_err; \
return -1; \
}
/* Private function prototypes */
static void pico_event_clear(struct pico_bsd_endpoint *ep, uint16_t events);
static uint16_t pico_bsd_wait(struct pico_bsd_endpoint * ep, int read, int write, int close);
static void pico_socket_event(uint16_t ev, struct pico_socket *s);
/************************/
/* Public API functions */
/************************/
void pico_bsd_init(void)
{
pico_signal_select = pico_signal_init();
pico_signal_tick = pico_signal_init();
picoLock = pico_mutex_init();
}
void pico_bsd_deinit(void)
{
pico_mutex_deinit(picoLock);
}
/* just ticks the stack twice */
void pico_bsd_stack_tick(void)
{
pico_mutex_lock(picoLock);
pico_stack_tick();
pico_stack_tick();
pico_mutex_unlock(picoLock);
}
/* ticks the stack, but wait for a signal with a timeout (e.g. from the driver interrupt) */
void pico_bsd_stack_tick_timeout(int timeout_ms)
{
pico_signal_wait_timeout(pico_signal_tick, timeout_ms);
pico_bsd_stack_tick();
}
/** Declarations of helper functions **/
static struct pico_bsd_endpoint *pico_bsd_create_socket(void);
static int get_free_sd(struct pico_bsd_endpoint *ep);
static int new_sd(struct pico_bsd_endpoint *ep);
static void free_up_ep(struct pico_bsd_endpoint *ep);
static struct pico_bsd_endpoint *get_endpoint(int sd, int set_err);
static int bsd_to_pico_addr(union pico_address *addr, struct sockaddr *_saddr, socklen_t socklen);
static uint16_t bsd_to_pico_port(struct sockaddr *_saddr, socklen_t socklen);
static int pico_addr_to_bsd(struct sockaddr *_saddr, socklen_t socklen, union pico_address *addr, uint16_t net);
static int pico_port_to_bsd(struct sockaddr *_saddr, socklen_t socklen, uint16_t port);
/** Global Sockets descriptors array **/
static struct pico_bsd_endpoint **PicoSockets = NULL;
static int PicoSocket_max = 0;
/*** Public socket functions ***/
/* Socket interface. */
int pico_newsocket(int domain, int type, int proto)
{
struct pico_bsd_endpoint * ep = NULL;
(void)proto;
#ifdef PICO_SUPPORT_IPV6
VALIDATE_TWO(domain,AF_INET, AF_INET6);
#else
VALIDATE_ONE(domain, AF_INET);
#endif
VALIDATE_TWO(type,SOCK_STREAM,SOCK_DGRAM);
if (AF_INET6 != PICO_PROTO_IPV6) {
if (domain == AF_INET6)
domain = PICO_PROTO_IPV6;
else
domain = PICO_PROTO_IPV4;
}
if (SOCK_STREAM != PICO_PROTO_TCP) {
if (type == SOCK_STREAM)
type = PICO_PROTO_TCP;
else
type = PICO_PROTO_UDP;
}
pico_mutex_lock(picoLock);
ep = pico_bsd_create_socket();
VALIDATE_NULL(ep);
ep->error = PICO_ERR_NOERR;
ep->proto = type;
ep->s = pico_socket_open(domain, type,&pico_socket_event);
if (!ep->s)
{
PICO_FREE(ep);
pico_mutex_unlock(picoLock);
return -1;
}
ep->s->priv = ep; /* let priv point to the endpoint struct */
/* open picotcp endpoint */
ep->state = SOCK_OPEN;
ep->mutex_lock = pico_mutex_init();
ep->signal = pico_signal_init();
ep->error = pico_err;
pico_mutex_unlock(picoLock);
return ep->socket_fd;
}
int pico_bind(int sd, struct sockaddr * local_addr, socklen_t socklen)
{
union pico_address addr = { .ip4 = { 0 } };
uint16_t port;
struct pico_bsd_endpoint *ep = get_endpoint(sd, 1);
VALIDATE_NULL(ep);
ep->error = PICO_ERR_NOERR;
VALIDATE_NULL(local_addr);
VALIDATE_TWO(socklen, SOCKSIZE, SOCKSIZE6);
if (bsd_to_pico_addr(&addr, local_addr, socklen) < 0)
{
ep->error = PICO_ERR_EINVAL;
errno = pico_err;
return -1;
}
port = bsd_to_pico_port(local_addr, socklen);
pico_mutex_lock(picoLock);
if(pico_socket_bind(ep->s, &addr, &port) < 0)
{
ep->error = pico_err;
errno = pico_err;
pico_mutex_unlock(picoLock);
return -1;
}
ep->state = SOCK_BOUND;
pico_mutex_unlock(picoLock);
return 0;
}
int pico_getsockname(int sd, struct sockaddr * local_addr, socklen_t *socklen)
{
union pico_address addr;
uint16_t port, proto;
struct pico_bsd_endpoint *ep = get_endpoint(sd, 1);
VALIDATE_NULL(ep);
ep->error = PICO_ERR_NOERR;
VALIDATE_NULL(local_addr);
VALIDATE_NULL(socklen);
pico_mutex_lock(picoLock);
if(pico_socket_getname(ep->s, &addr, &port, &proto) < 0)
{
ep->error = pico_err;
errno = pico_err;
pico_mutex_unlock(picoLock);
return -1;
}
if (proto == PICO_PROTO_IPV6)
*socklen = SOCKSIZE6;
else
*socklen = SOCKSIZE;
if (pico_addr_to_bsd(local_addr, *socklen, &addr, proto) < 0) {
ep->error = pico_err;
errno = pico_err;
pico_mutex_unlock(picoLock);
return -1;
}
pico_mutex_unlock(picoLock);
pico_port_to_bsd(local_addr, *socklen, port);
ep->error = pico_err;
return 0;
}
int pico_getpeername(int sd, struct sockaddr * remote_addr, socklen_t *socklen)
{
union pico_address addr;
uint16_t port, proto;
struct pico_bsd_endpoint *ep = get_endpoint(sd, 1);
VALIDATE_NULL(ep);
ep->error = PICO_ERR_NOERR;
VALIDATE_NULL(remote_addr);
VALIDATE_NULL(socklen);
pico_mutex_lock(picoLock);
if(pico_socket_getpeername(ep->s, &addr, &port, &proto) < 0)
{
pico_mutex_unlock(picoLock);
return -1;
}
if (proto == PICO_PROTO_IPV6)
*socklen = SOCKSIZE6;
else
*socklen = SOCKSIZE;
if (pico_addr_to_bsd(remote_addr, *socklen, &addr, proto) < 0) {
pico_mutex_unlock(picoLock);
return -1;
}
pico_mutex_unlock(picoLock);
pico_port_to_bsd(remote_addr, *socklen, port);
return 0;
}
int pico_listen(int sd, int backlog)
{
struct pico_bsd_endpoint *ep = get_endpoint(sd, 1);
VALIDATE_NULL(ep);
ep->error = PICO_ERR_NOERR;
VALIDATE_NULL(ep->s);
VALIDATE_ONE(ep->state, SOCK_BOUND);
pico_mutex_lock(picoLock);
if(pico_socket_listen(ep->s, backlog) < 0)
{
ep->error = pico_err;
errno = pico_err;
pico_mutex_unlock(picoLock);
return -1;
}
ep->state = SOCK_LISTEN;
ep->error = pico_err;
pico_mutex_unlock(picoLock);
return 0;
}
int pico_connect(int sd, struct sockaddr *_saddr, socklen_t socklen)
{
struct pico_bsd_endpoint *ep = get_endpoint(sd, 1);
union pico_address addr;
uint16_t port;
uint16_t ev = 0;
int ret;
VALIDATE_NULL(ep);
ep->error = PICO_ERR_NOERR;
VALIDATE_NULL(_saddr);
if (bsd_to_pico_addr(&addr, _saddr, socklen) < 0)
{
ep->error = PICO_ERR_EINVAL;
errno = pico_err;
return -1;
}
port = bsd_to_pico_port(_saddr, socklen);
pico_mutex_lock(picoLock);
ret = pico_socket_connect(ep->s, &addr, port);
pico_mutex_unlock(picoLock);
if (ret < 0) {
ep->error = pico_err;
return -1;
}
if (ep->nonblocking) {
pico_err = PICO_ERR_EAGAIN;
ep->error = pico_err;
} else {
/* wait for event */
ev = pico_bsd_wait(ep, 0, 0, 0); /* wait for ERR, FIN and CONN */
}
if(ev & PICO_SOCK_EV_CONN)
{
/* clear the EV_CONN event */
pico_event_clear(ep, PICO_SOCK_EV_CONN);
ep->error = pico_err;
return 0;
} else {
if (!(ep->nonblocking))
pico_socket_close(ep->s);
}
ep->error = pico_err;
errno = pico_err;
return -1;
}
int pico_isconnected(int sd) {
struct pico_bsd_endpoint *ep = NULL;
int state = 0;
ep = get_endpoint(sd, 1);
VALIDATE_NULL(ep);
ep->error = PICO_ERR_NOERR;
pico_mutex_lock(picoLock);
if(ep->state == SOCK_CONNECTED) {
state = 1;
}
pico_mutex_unlock(picoLock);
return state;
}
int pico_accept(int sd, struct sockaddr *_orig, socklen_t *socklen)
{
struct pico_bsd_endpoint *ep, * client_ep = NULL;
uint16_t events;
union pico_address picoaddr;
uint16_t port;
ep = get_endpoint(sd, 1);
VALIDATE_NULL(ep);
ep->error = PICO_ERR_NOERR;
VALIDATE_ONE(ep->state, SOCK_LISTEN);
if (ep->nonblocking)
events = PICO_SOCK_EV_CONN;
else
events = pico_bsd_wait(ep, 0, 0, 0); /* Wait for CONN, FIN and ERR */
if(events & PICO_SOCK_EV_CONN)
{
struct pico_socket *s;
pico_mutex_lock(picoLock);
s = pico_socket_accept(ep->s,&picoaddr,&port);
if (!s)
{
ep->error = pico_err;
errno = pico_err;
pico_mutex_unlock(picoLock);
return -1;
}
/* Create a new client EP, only after the accept returned succesfully */
client_ep = pico_bsd_create_socket();
if (!client_ep)
{
ep->error = pico_err;
errno = pico_err;
pico_mutex_unlock(picoLock);
return -1;
}
client_ep->s = s;
client_ep->state = SOCK_OPEN;
client_ep->mutex_lock = pico_mutex_init();
client_ep->signal = pico_signal_init();
client_ep->s->priv = client_ep;
pico_event_clear(ep, PICO_SOCK_EV_CONN); /* clear the CONN event the listening socket */
if (client_ep->s->net->proto_number == PICO_PROTO_IPV4)
*socklen = SOCKSIZE;
else
*socklen = SOCKSIZE6;
client_ep->state = SOCK_CONNECTED;
if (pico_addr_to_bsd(_orig, *socklen, &picoaddr, client_ep->s->net->proto_number) < 0) {
client_ep->in_use = 0;
pico_mutex_unlock(picoLock);
return -1;
}
pico_port_to_bsd(_orig, *socklen, port);
client_ep->in_use = 1;
pico_mutex_unlock(picoLock);
ep->error = pico_err;
return client_ep->socket_fd;
}
client_ep->in_use = 0;
ep->error = pico_err;
errno = pico_err;
return -1;
}
int pico_sendto(int sd, void * buf, int len, int flags, struct sockaddr *_dst, socklen_t socklen)
{
int retval = 0;
int tot_len = 0;
uint16_t port;
union pico_address picoaddr;
struct pico_bsd_endpoint *ep = get_endpoint(sd, 1);
VALIDATE_NULL(ep);
ep->error = PICO_ERR_NOERR;
if (!buf || (len <= 0)) {
pico_err = PICO_ERR_EINVAL;
errno = pico_err;
ep->error = pico_err;
return -1;
}
while (tot_len < len) {
/* Write to the pico socket */
pico_mutex_lock(picoLock);
if (_dst == NULL) {
retval = pico_socket_send(ep->s, ((uint8_t *)buf) + tot_len, len - tot_len);
} else {
if (bsd_to_pico_addr(&picoaddr, _dst, socklen) < 0) {
ep->error = PICO_ERR_EINVAL;
errno = pico_err;
pico_mutex_unlock(picoLock);
return -1;
}
port = bsd_to_pico_port(_dst, socklen);
retval = pico_socket_sendto(ep->s, ((uint8_t *)buf) + tot_len, len - tot_len, &picoaddr, port);
}
pico_event_clear(ep, PICO_SOCK_EV_WR);
pico_mutex_unlock(picoLock);
/* If sending failed, return an error */
if (retval < 0)
{
ep->error = pico_err;
errno = pico_err;
pico_event_clear(ep, PICO_SOCK_EV_WR);
return -1;
}
if (retval > 0)
{
tot_len += retval;
break;
}
if (ep->nonblocking)
break;
/* If sent bytes (retval) < len-tot_len: socket full, we need to wait for a new WR event */
if (retval < (len - tot_len))
{
uint16_t ev = 0;
/* wait for a new WR or CLOSE event */
ev = pico_bsd_wait(ep, 0, 1, 1);
if (ev & (PICO_SOCK_EV_ERR | PICO_SOCK_EV_FIN | PICO_SOCK_EV_CLOSE))
{
ep->error = pico_err;
errno = pico_err;
pico_event_clear(ep, PICO_SOCK_EV_WR);
/* closing and freeing the socket is done in the event handler */
return -1;
}
}
tot_len += retval;
}
ep->error = pico_err;
return tot_len;
}
int pico_fcntl(int sd, int cmd, int arg)
{
struct pico_bsd_endpoint *ep = get_endpoint(sd, 1);
if (!ep) {
pico_err = PICO_ERR_EINVAL;
errno = pico_err;
return -1;
}
if (cmd == F_SETFL) {
if ((arg & O_NONBLOCK) != 0) {
ep->nonblocking = 1;
} else {
ep->nonblocking = 0;
}
ep->error = PICO_ERR_NOERR;
return 0;
}
if (cmd == F_GETFL) {
(void)arg; /* F_GETFL: arg is ignored */
ep->error = PICO_ERR_NOERR;
if (ep->nonblocking)
return O_NONBLOCK;
else
return 0;
}
if (cmd == F_SETFD) {
(void)arg;
ep->error = PICO_ERR_NOERR;
return 0;
}
pico_err = PICO_ERR_EINVAL;
errno = pico_err;
ep->error = pico_err;
return -1;
}
/*
* RETURN VALUE
* Upon successful completion, recv_from() shall return the length of the
* message in bytes. If no messages are available to be received and the
* peer has performed an orderly shutdown, recv() shall return 0. Otherwise,
* 1 shall be returned and errno set to indicate the error.
*/
int pico_recvfrom(int sd, void * _buf, int len, int flags, struct sockaddr *_addr, socklen_t *socklen)
{
int retval = 0;
int tot_len = 0;
struct pico_bsd_endpoint *ep = get_endpoint(sd, 1);
union pico_address picoaddr;
uint16_t port;
unsigned char *buf = (unsigned char *)_buf;
bsd_dbg("Recvfrom called \n");
VALIDATE_NULL(ep);
ep->error = PICO_ERR_NOERR;
if (ep->state == SOCK_RESET_BY_PEER) {
/* not much to do here. Peer has nothing to say. */
return 0;
}
if (!buf || (len <= 0)) {
pico_err = PICO_ERR_EINVAL;
errno = pico_err;
ep->error = pico_err;
return -1;
}
while (tot_len < len) {
pico_mutex_lock(picoLock);
retval = pico_socket_recvfrom(ep->s, buf + tot_len , len - tot_len, &picoaddr, &port);
pico_mutex_unlock(picoLock);
bsd_dbg("pico_socket_recvfrom returns %d, first bytes are %c-%c-%c-%c\n", retval, buf[0], buf[1], buf[2], buf[3]);
/* pico_socket_recvfrom failed */
if (retval < 0) {
/* data was received */
if (tot_len > 0)
{
bsd_dbg("Recvfrom returning %d\n", tot_len);
ep->error = pico_err;
return tot_len;
}
/* no data was received yet */
ep->error = pico_err;
if (pico_err == PICO_ERR_ESHUTDOWN) /* If no messages are available to be received and the peer has performed an orderly shutdown, recvfrom() shall return 0. */
{
return 0;
}
else /* Otherwise, the function shall return 1 and set errno to indicate the error. */
{
return -1;
}
}
/* If received 0 bytes, return -1 or amount of bytes received */
if (retval == 0) {
pico_event_clear(ep, PICO_SOCK_EV_RD);
if (tot_len > 0) {
bsd_dbg("Recvfrom returning %d\n", tot_len);
ep->error = pico_err;
return tot_len;
}
}
if (retval > 0) {
if (ep->proto == PICO_PROTO_UDP) {
if (_addr && socklen > 0)
{
if (pico_addr_to_bsd(_addr, *socklen, &picoaddr, ep->s->net->proto_number) < 0) {
pico_err = PICO_ERR_EINVAL;
errno = pico_err;
ep->error = pico_err;
return -1;
}
pico_port_to_bsd(_addr, *socklen, port);
}
/* If in a recvfrom call, for UDP we should return immediately after the first dgram */
ep->error = pico_err;
return retval + tot_len;
} else {
/* TCP: continue until recvfrom = 0, socket buffer empty */
tot_len += retval;
continue;
}
}
if (ep->nonblocking)
{
if (retval == 0)
{
pico_err = PICO_ERR_EAGAIN; /* or EWOULDBLOCK */
errno = pico_err;
retval = -1; /* BSD-speak: -1 == 0 bytes received */
}
break;
}
/* If recv bytes (retval) < len-tot_len: socket empty, we need to wait for a new RD event */
if (retval < (len - tot_len))
{
uint16_t ev = 0;
/* wait for a new RD event -- also wait for CLOSE event */
ev = pico_bsd_wait(ep, 1, 0, 1);
if (ev & (PICO_SOCK_EV_ERR | PICO_SOCK_EV_FIN | PICO_SOCK_EV_CLOSE))
{
/* closing and freeing the socket is done in the event handler */
pico_event_clear(ep, PICO_SOCK_EV_RD);
ep->error = pico_err;
return 0; /* return 0 on a properly closed socket */
}
}
tot_len += retval;
}
pico_event_clear(ep, PICO_SOCK_EV_RD); // What if still space available and we clear it here??
bsd_dbg("Recvfrom returning %d (full block)\n", tot_len);
ep->error = pico_err;
if (tot_len == 0)
{
errno = pico_err;
tot_len = -1; /* BSD-speak: -1 == 0 bytes received */
}
return tot_len;
}
int pico_write(int sd, void * buf, int len)
{
return pico_sendto(sd, buf, len, 0, NULL, 0);
}
int pico_send(int sd, void * buf, int len, int flags)
{
return pico_sendto(sd, buf, len, flags, NULL, 0);
}
int pico_read(int sd, void * buf, int len)
{
return pico_recvfrom(sd, buf, len, 0, NULL, 0);
}
int pico_recv(int sd, void * buf, int len, int flags)
{
return pico_recvfrom(sd, buf, len, flags, NULL, 0);
}
int pico_close(int sd)
{
struct pico_bsd_endpoint *ep = get_endpoint(sd, 1);
VALIDATE_NULL(ep);
ep->error = PICO_ERR_NOERR;
if (ep->s && ep->in_use) /* valid socket, try to close it */
{
pico_mutex_lock(picoLock);
pico_socket_close(ep->s);
ep->s->priv = NULL;
pico_mutex_unlock(picoLock);
}
ep->in_use = 0;
ep->error = pico_err;
return 0;
}
int pico_shutdown(int sd, int how)
{
struct pico_bsd_endpoint *ep = get_endpoint(sd, 1);
VALIDATE_NULL(ep);
ep->error = PICO_ERR_NOERR;
if(ep->s) /* valid socket, try to close it */
{
pico_mutex_lock(picoLock);
pico_socket_shutdown(ep->s, how);
ep->error = pico_err;
pico_mutex_unlock(picoLock);
} else {
ep->error = PICO_ERR_EINVAL;
errno = pico_err;
}
return 0;
}
int pico_join_multicast_group(int sd, const char *address, const char *local) {
int ret;
struct pico_ip_mreq mreq={};
pico_string_to_ipv4(address, &mreq.mcast_group_addr.ip4.addr);
pico_string_to_ipv4(local, &mreq.mcast_link_addr.ip4.addr);
ret = pico_setsockopt(sd, SOL_SOCKET, PICO_IP_ADD_MEMBERSHIP, &mreq, sizeof(struct pico_ip_mreq));
return ret;
}
/*** Helper functions ***/
static int bsd_to_pico_addr(union pico_address *addr, struct sockaddr *_saddr, socklen_t socklen)
{
VALIDATE_TWO(socklen, SOCKSIZE, SOCKSIZE6);
if (socklen == SOCKSIZE6) {
struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)_saddr;
memcpy(&addr->ip6.addr, &saddr->sin6_addr.s6_addr, 16);
saddr->sin6_family = AF_INET6;
} else {
struct sockaddr_in *saddr = (struct sockaddr_in *)_saddr;
addr->ip4.addr = saddr->sin_addr.s_addr;
saddr->sin_family = AF_INET;
}
return 0;
}
static uint16_t bsd_to_pico_port(struct sockaddr *_saddr, socklen_t socklen)
{
VALIDATE_TWO(socklen, SOCKSIZE, SOCKSIZE6);
if (socklen == SOCKSIZE6) {
struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)_saddr;
return saddr->sin6_port;
} else {
struct sockaddr_in *saddr = (struct sockaddr_in *)_saddr;
return saddr->sin_port;
}
}
static int pico_port_to_bsd(struct sockaddr *_saddr, socklen_t socklen, uint16_t port)
{
if (socklen == SOCKSIZE6) {
struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)_saddr;
saddr->sin6_port = port;
return 0;
} else {
struct sockaddr_in *saddr = (struct sockaddr_in *)_saddr;
saddr->sin_port = port;
return 0;
}
pico_err = PICO_ERR_EINVAL;
errno = pico_err;
return -1;
}
static int pico_addr_to_bsd(struct sockaddr *_saddr, socklen_t socklen, union pico_address *addr, uint16_t net)
{
VALIDATE_TWO(socklen, SOCKSIZE, SOCKSIZE6);
if ((socklen == SOCKSIZE6) && (net == PICO_PROTO_IPV6)) {
struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)_saddr;
memcpy(&saddr->sin6_addr.s6_addr, &addr->ip6.addr, 16);
saddr->sin6_family = AF_INET6;
} else if ((socklen == SOCKSIZE) && (net == PICO_PROTO_IPV4)) {
struct sockaddr_in *saddr = (struct sockaddr_in *)_saddr;
saddr->sin_addr.s_addr = addr->ip4.addr;
saddr->sin_family = AF_INET;
}
return 0;
}
static void free_up_ep(struct pico_bsd_endpoint *ep)
{
if (ep->signal)
pico_signal_deinit(ep->signal);
if (ep->mutex_lock)
pico_mutex_deinit(ep->mutex_lock);
PICO_FREE(ep);
}
static int get_free_sd(struct pico_bsd_endpoint *ep)
{
int i;
for (i = 0; i < PicoSocket_max; i++) {
if (!PicoSockets[i]->in_use) {
free_up_ep(PicoSockets[i]);
PicoSockets[i] = ep;
return i;
}
}
return -1;
}
/* DLA TODO: make a GC for freeing up the last socket descriptor periodically if not in use */
static int new_sd(struct pico_bsd_endpoint *ep)
{
int sd = PicoSocket_max;
struct pico_bsd_endpoint **new;
new = PICO_ZALLOC(sizeof(void *) * ++PicoSocket_max);
if (!new) {
PicoSocket_max--;
pico_err = PICO_ERR_ENOMEM;
errno = pico_err;
return -1;
}
if (sd > 0) {
memcpy(new, PicoSockets, sd * sizeof(void *));
PICO_FREE(PicoSockets);
}
PicoSockets = new;
new[sd] = ep;
return sd;
}
/* picoLock must be taken already ! */
static struct pico_bsd_endpoint *pico_bsd_create_socket(void)
{
struct pico_bsd_endpoint *ep = PICO_ZALLOC(sizeof(struct pico_bsd_endpoint));
if (!ep) {
pico_err = PICO_ERR_ENOMEM;
errno = pico_err;
}
ep->in_use = 1;
ep->socket_fd = get_free_sd(ep);
if (ep->socket_fd < 0) {
ep->socket_fd = new_sd(ep);
}
return ep;
}
#ifndef PICO_EBADFD
# define PICO_EBADFD 77 /* File descriptor in bad state */
#endif
static struct pico_bsd_endpoint *get_endpoint(int sd, int set_err)
{
if ((sd > PicoSocket_max) || (sd < 0) ||
(PicoSockets[sd]->in_use == 0)) {
if (set_err)
{
pico_err = PICO_EBADFD;
errno = pico_err;
}
return NULL;
}
return PicoSockets[sd];
}
/* wait for one of the selected events, return any of those that occurred */
uint16_t pico_bsd_select(struct pico_bsd_endpoint *ep)
{
uint16_t events = ep->events & ep->revents; /* maybe an event we are waiting for, was already queued ? */
/* wait for one of the selected events... */
while (!events)
{
pico_signal_wait(ep->signal);
events = (ep->revents & ep->events); /* filter for the events we were waiting for */
}
/* the event we were waiting for happened, now report it */
return events; /* return any event(s) that occurred, that we were waiting for */
}
/****************************/
/* Private helper functions */
/****************************/
static uint16_t pico_bsd_wait(struct pico_bsd_endpoint * ep, int read, int write, int close)
{
pico_mutex_lock(ep->mutex_lock);
ep->events = PICO_SOCK_EV_ERR;
ep->events |= PICO_SOCK_EV_FIN;
ep->events |= PICO_SOCK_EV_CONN;
if (close)
ep->events |= PICO_SOCK_EV_CLOSE;
if (read)
ep->events |= PICO_SOCK_EV_RD;
if (write)
ep->events |= PICO_SOCK_EV_WR;
pico_mutex_unlock(ep->mutex_lock);
return pico_bsd_select(ep);
}
static void pico_event_clear(struct pico_bsd_endpoint *ep, uint16_t events)
{
pico_mutex_lock(ep->mutex_lock);
ep->revents &= ~events; /* clear those events */
pico_mutex_unlock(ep->mutex_lock);
}
/* NOTE: __NO__ picoLock'ing here !! */
/* this is called from pico_stack_tick, so picoLock is already locked */
static void pico_socket_event(uint16_t ev, struct pico_socket *s)
{
struct pico_bsd_endpoint * ep = (struct pico_bsd_endpoint *)(s->priv);
if (!s)
return;
if(!ep || !ep->s || !ep->mutex_lock || !ep->signal )
{
/* DLA: do not call close upon SOCK_CLOSE, we might still write. */
if(ev & (PICO_SOCK_EV_FIN | PICO_SOCK_EV_ERR) )
{
pico_signal_send(pico_signal_select); /* Signal this event globally (e.g. for select()) */
pico_socket_close(s);
}
if (ev & PICO_SOCK_EV_CLOSE)
pico_signal_send(pico_signal_select);
/* endpoint not initialized yet! */
return;
}
if(ep->in_use != 1)
return;
pico_mutex_lock(ep->mutex_lock); /* lock over the complete body is needed,
as the event might get cleared in another process.. */
ep->revents |= ev; /* set those events */
if(ev & PICO_SOCK_EV_CONN)
{
if(ep->state != SOCK_LISTEN)
{
ep->state = SOCK_CONNECTED;
}
}
if(ev & PICO_SOCK_EV_ERR)
{
ep->state = SOCK_RESET_BY_PEER;
}
if (ev & PICO_SOCK_EV_CLOSE) {
ep->state = SOCK_RESET_BY_PEER;
/* DO NOT close: we might still write! */
}
if (ev & PICO_SOCK_EV_FIN) {
/* DO NOT set ep->s = NULL, we might still be transmitting stuff! */
ep->state = SOCK_CLOSED;
}
pico_signal_send(pico_signal_select); /* Signal this event globally (e.g. for select()) */
pico_signal_send(ep->signal); /* Signal the endpoint that was blocking on this event */
pico_mutex_unlock(ep->mutex_lock);
}
#define DNSQUERY_OK 1
#define DNSQUERY_FAIL 0xFF
struct dnsquery_cookie
{
struct addrinfo **res;
void *signal;
uint8_t block;
uint8_t revents;
};
static struct dnsquery_cookie *dnsquery_cookie_create(struct addrinfo **res, uint8_t block)
{
struct dnsquery_cookie *ck = PICO_ZALLOC(sizeof(struct dnsquery_cookie));
if (!ck) {
pico_err = PICO_ERR_ENOMEM;
errno = pico_err;
return NULL;
}
ck->signal = pico_signal_init();
ck->res = res;
ck->block = block;
return ck;
}
static int dnsquery_cookie_delete(struct dnsquery_cookie *ck)
{
if (!ck) {
pico_err = PICO_ERR_EINVAL;
errno = pico_err;
return -1;
}
if (ck->signal)
{
pico_signal_deinit(ck->signal);
ck->signal = NULL;
}
PICO_FREE(ck);
return 0;
}
#ifdef PICO_SUPPORT_IPV6
static void dns_ip6_cb(char *ip, void *arg)
{
struct dnsquery_cookie *ck = (struct dnsquery_cookie *)arg;
struct addrinfo *new;
if (ip) {
new = PICO_ZALLOC(sizeof(struct addrinfo));
if (!new) {
ck->revents = DNSQUERY_FAIL;
if (ck->block)
pico_signal_send(ck->signal);
return;
}
new->ai_family = AF_INET6;
new->ai_addr = PICO_ZALLOC(sizeof(struct sockaddr_in6));
if (!new->ai_addr) {
PICO_FREE(new);
ck->revents = DNSQUERY_FAIL;
if (ck->block)
pico_signal_send(ck->signal);
return;
}
new->ai_addrlen = sizeof(struct sockaddr_in6);
pico_string_to_ipv6(ip, (((struct sockaddr_in6*)(new->ai_addr))->sin6_addr.s6_addr));
((struct sockaddr_in6*)(new->ai_addr))->sin6_family = AF_INET6;
new->ai_next = *ck->res;
*ck->res = new;
ck->revents = DNSQUERY_OK;
} else {
/* No ip given, but still callback was called: timeout! */
ck->revents = DNSQUERY_FAIL;
}
if (ck->block)
pico_signal_send(ck->signal);
}
#endif
static void dns_ip4_cb(char *ip, void *arg)
{
struct dnsquery_cookie *ck = (struct dnsquery_cookie *)arg;
struct addrinfo *new;
if (ip) {
new = PICO_ZALLOC(sizeof(struct addrinfo));
if (!new) {
ck->revents = DNSQUERY_FAIL;
if (ck->block)
pico_signal_send(ck->signal);
return;
}
new->ai_family = AF_INET;
new->ai_addr = PICO_ZALLOC(sizeof(struct sockaddr_in));
if (!new->ai_addr) {
PICO_FREE(new);
ck->revents = DNSQUERY_FAIL;
if (ck->block)
pico_signal_send(ck->signal);
return;
}
new->ai_addrlen = sizeof(struct sockaddr_in);
pico_string_to_ipv4(ip, &(((struct sockaddr_in*)new->ai_addr)->sin_addr.s_addr));
((struct sockaddr_in*)(new->ai_addr))->sin_family = AF_INET;
new->ai_next = *ck->res;
*ck->res = new;
ck->revents = DNSQUERY_OK;
} else {
/* No ip given, but still callback was called: timeout! */
ck->revents = DNSQUERY_FAIL;
}
if (ck->block)
pico_signal_send(ck->signal);
}
#ifdef PICO_SUPPORT_DNS_CLIENT
int pico_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res)
{
struct dnsquery_cookie *ck4 = NULL;
struct dnsquery_cookie *ck6 = NULL;
struct sockaddr_in sa4;
*res = NULL;
(void)service;
bsd_dbg("Called pico_getaddrinfo, looking for %s\n", node);
#ifdef PICO_SUPPORT_IPV6
struct sockaddr_in6 sa6;
if (pico_string_to_ipv6(node, sa6.sin6_addr.s6_addr) == 0) {
ck6 = dnsquery_cookie_create(res, 0);
dns_ip6_cb((char *)node, ck6);
dnsquery_cookie_delete(ck6);
return 0;
}
#endif
if (pico_string_to_ipv4(node, &sa4.sin_addr.s_addr) == 0) {
ck4 = dnsquery_cookie_create(res, 0);
dns_ip4_cb((char*)node, ck4);
dnsquery_cookie_delete(ck4);
return 0;
}
#ifdef PICO_SUPPORT_IPV6
{
if (!hints || (hints->ai_family == AF_INET6)) {
ck6 = dnsquery_cookie_create(res, 1);
if (!ck6)
return -1;
pico_mutex_lock(picoLock);
if (pico_dns_client_getaddr6(node, dns_ip6_cb, ck6) < 0)
{
bsd_dbg("Error resolving AAAA record %s\n", node);
dnsquery_cookie_delete(ck6);
pico_mutex_unlock(picoLock);
return -1;
}
bsd_dbg("Resolving AAAA record %s\n", node);
pico_mutex_unlock(picoLock);
}
}
#endif /* PICO_SUPPORT_IPV6 */
if (!hints || (hints->ai_family == AF_INET)) {
ck4 = dnsquery_cookie_create(res, 1);
pico_mutex_lock(picoLock);
if (pico_dns_client_getaddr(node, dns_ip4_cb, ck4) < 0)
{
bsd_dbg("Error resolving A record %s\n", node);
dnsquery_cookie_delete(ck4);
pico_mutex_unlock(picoLock);
return -1;
}
bsd_dbg("Resolving A record %s\n", node);
pico_mutex_unlock(picoLock);
}
#ifdef PICO_SUPPORT_IPV6
if (ck6) {
/* Signal is always sent; either dns resolved, or timeout/failure */
pico_signal_wait(ck6->signal);
dnsquery_cookie_delete(ck6);
}
#endif /* PICO_SUPPORT_IPV6 */
if (ck4) {
/* Signal is always sent; either dns resolved, or timeout/failure */
pico_signal_wait(ck4->signal);
dnsquery_cookie_delete(ck4);
}
if (*res)
return 0;
return -1;
}
void pico_freeaddrinfo(struct addrinfo *res)
{
struct addrinfo *cur = res;
struct addrinfo *nxt;
while(cur) {
if (cur->ai_addr)
PICO_FREE(cur->ai_addr);
nxt = cur->ai_next;
PICO_FREE(cur);
cur = nxt;
}
}
/* Legacy gethostbyname call implementation */
static struct hostent PRIV_HOSTENT = { };
struct hostent *pico_gethostbyname(const char *name)
{
struct addrinfo *res;
struct addrinfo hint = {.ai_family = AF_INET};
int ret;
if (!PRIV_HOSTENT.h_addr_list) {
/* Done only once: reserve space for 2 entries */
PRIV_HOSTENT.h_addr_list = PICO_ZALLOC(2 * sizeof(void*));
PRIV_HOSTENT.h_addr_list[1] = NULL;
}
ret = pico_getaddrinfo(name, NULL, &hint, &res);
if (ret == 0) {
if (PRIV_HOSTENT.h_name != NULL) {
PICO_FREE(PRIV_HOSTENT.h_name);
PRIV_HOSTENT.h_name = NULL;
}
if (PRIV_HOSTENT.h_addr_list[0] != NULL) {
PICO_FREE(PRIV_HOSTENT.h_addr_list[0]);
PRIV_HOSTENT.h_addr_list[0] = NULL;
}
PRIV_HOSTENT.h_name = PICO_ZALLOC(strlen(name));
if (!PRIV_HOSTENT.h_name) {
pico_freeaddrinfo(res);
return NULL;
}
strcpy(PRIV_HOSTENT.h_name, name);
PRIV_HOSTENT.h_addrtype = res->ai_addr->sa_family;
if (PRIV_HOSTENT.h_addrtype == AF_INET) {
PRIV_HOSTENT.h_length = 4;
PRIV_HOSTENT.h_addr_list[0] = PICO_ZALLOC(4);
if (!PRIV_HOSTENT.h_addr_list[0]) {
pico_freeaddrinfo(res);
return NULL;
}
memcpy (PRIV_HOSTENT.h_addr_list[0], &(((struct sockaddr_in *)res->ai_addr)->sin_addr.s_addr), 4);
} else {
/* Only IPv4 supported by this ancient call. */
pico_freeaddrinfo(res);
return NULL;
}
pico_freeaddrinfo(res);
return &PRIV_HOSTENT;
}
return NULL;
}
#endif
int pico_getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen)
{
struct pico_bsd_endpoint *ep = get_endpoint(sockfd, 1);
int ret;
bsd_dbg("called getsockopt\n");
VALIDATE_NULL(ep);
if (level != SOL_SOCKET) {
pico_err = PICO_ERR_EPROTONOSUPPORT;
errno = pico_err;
return -1;
}
if (!ep) {
pico_err = PICO_ERR_EINVAL;
errno = pico_err;
return -1;
}
if (!optval) {
pico_err = PICO_ERR_EFAULT;
errno = pico_err;
return -1;
}
if (optname == SO_ERROR)
{
*((int*)optval) = ep->error;
ep->error = 0;
return 0;
}
pico_mutex_lock(ep->mutex_lock);
ret = pico_socket_getoption(ep->s, sockopt_get_name(optname), optval);
pico_mutex_unlock(ep->mutex_lock);
return ret;
}
int pico_setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)
{
struct pico_bsd_endpoint *ep = get_endpoint(sockfd, 1);
int ret;
VALIDATE_NULL(ep);
ep->error = PICO_ERR_NOERR;
bsd_dbg("called setsockopt\n");
if (level != SOL_SOCKET) {
pico_err = PICO_ERR_EPROTONOSUPPORT;
errno = pico_err;
return -1;
}
if (!ep) {
pico_err = PICO_ERR_EINVAL;
errno = pico_err;
return -1;
}
if (!optval) {
pico_err = PICO_ERR_EFAULT;
errno = pico_err;
return -1;
}
if ((optname == SO_REUSEADDR) || (optname == SO_REUSEPORT))
return 0; /* Pretend it was OK. */
if (optname == SO_ERROR)
return PICO_ERR_ENOPROTOOPT;
pico_mutex_lock(ep->mutex_lock);
ret = pico_socket_setoption(ep->s, sockopt_get_name(optname), (void *)optval);
pico_mutex_unlock(ep->mutex_lock);
return ret;
}
#ifdef PICO_SUPPORT_SNTP_CLIENT
int pico_gettimeofday(struct timeval *tv, struct timezone *tz)
{
int ret;
(void)tz;
struct pico_timeval ptv;
ret= pico_sntp_gettimeofday(&ptv);
tv->tv_sec = ptv.tv_sec;
tv->tv_usec= ptv.tv_msec * 1000; /* pico_timeval uses milliseconds instead of microseconds */
return ret;
}
/* dummy function */
int pico_settimeofday(struct timeval *tv, struct timezone *tz)
{
(void)tz;
(void)tv;
return 0;
}
#else
static struct pico_timeval ptv = {0u,0u};
int pico_gettimeofday(struct timeval *tv, struct timezone *tz)
{
int ret;
(void)tz;
tv->tv_sec = ptv.tv_sec;
tv->tv_usec= ptv.tv_msec * 1000; /* pico_timeval uses milliseconds instead of microseconds */
return 0;
}
int pico_settimeofday(struct timeval *tv, struct timezone *tz)
{
int ret;
(void)tz;
ptv.tv_sec = tv->tv_sec;
ptv.tv_msec= tv->tv_usec / 1000; /* pico_timeval uses milliseconds instead of microseconds */
return 0;
}
#endif
long XTIME(void) {
struct timeval t;
pico_gettimeofday(&t, NULL);
return (long)t.tv_sec;
}
const char *pico_inet_ntop(int af, const void *src, char *dst, socklen_t size)
{
if ((!dst) || (!src))
return NULL;
switch (af)
{
case AF_INET:
if (size < INET_ADDRSTRLEN)
return NULL;
pico_ipv4_to_string(dst, *((const uint32_t *)src));
break;
#ifdef PICO_SUPPORT_IPV6
case AF_INET6:
if (size < INET6_ADDRSTRLEN)
return NULL;
pico_ipv6_to_string(dst, ((struct in6_addr *)src)->s6_addr);
break;
#endif
default:
dst = NULL;
break;
}
return dst;
}
char *pico_inet_ntoa(struct in_addr in)
{
static char ipbuf[INET_ADDRSTRLEN];
pico_ipv4_to_string(ipbuf, (uint32_t)in.s_addr);
return ipbuf;
}
int pico_pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask)
{
/*
* EV_READ: sets the readfds
* EV_WRITE: sets the writefds
* EV_CONN: sets the readfds (a.k.a. someone connects to your listening socket)
* EV_CLOSE: sets the readfds, then next recv() returns 0;
* EV_FIN: sets the readfds, then next recv() returns 0;
* EV_ERR: sets the exceptfds
*/
int i = 0; /* socket fds */
int nfds_out = 0; /* amount of changed sockets */
(void) sigmask;
bsd_dbg_select("=== IN: PICO SELECT === readfds[0]: 0x%x -- writefds[0]: 0x%x\n", readfds?(*(uint8_t *)readfds):0, writefds?(*(uint8_t *)writefds):0);
pico_fd_set readfds_out = {};
pico_fd_set writefds_out = {};
pico_fd_set exceptfds_out = {};
/* First, loop over all possible file descriptors, check if one has an event pending that we're waiting for */
while (nfds_out == 0)
{
for (i = 0; i < nfds; i++)
{
struct pico_bsd_endpoint *ep = get_endpoint(i, 0);
bsd_dbg_select("\t~~~ SELECT: fds %d - ep:%p ", i, ep);
if (ep)
{
/* Is this endpoint still valid? */
if (!ep->in_use)
{
bsd_dbg_select(" ep->in_use = 0\n");
break;
}
/* READ event needed and available? */
if (readfds && PICO_FD_ISSET(i,readfds) && (ep->revents & (PICO_SOCK_EV_CONN | PICO_SOCK_EV_CLOSE | PICO_SOCK_EV_RD)))
{
bsd_dbg_select("- READ_EV - ");
nfds_out++;
PICO_FD_SET(i, &readfds_out);
}
/* Force write events on empty udp sockets */
if ((ep->proto == PICO_PROTO_UDP) && (ep->s->q_out.size < ep->s->q_out.max_size))
ep->revents |= PICO_SOCK_EV_WR;
/* WRITE event needed? and available? */
if (writefds && PICO_FD_ISSET(i,writefds) && (ep->revents & (PICO_SOCK_EV_WR)))
{
bsd_dbg_select("- WRITE_EV - ");
nfds_out++;
PICO_FD_SET(i, &writefds_out);
}
/* EXCEPTION event needed and available? */
if (exceptfds && PICO_FD_ISSET(i,exceptfds) && (ep->revents & (PICO_SOCK_EV_ERR)))
{
bsd_dbg_select("- EXCEPT_EV - ");
nfds_out++;
PICO_FD_SET(i, &exceptfds_out);
}
}
if (ep)
bsd_dbg_select("- s:%p - ev:%x", ep->s, ep->revents);
bsd_dbg_select("\n");
}
/* If there was a hit, break out of the loop */
if (nfds_out)
break;
/* If not, wait for a semaphore signaling an event from the stack */
if (pico_signal_wait_timeout(pico_signal_select, (timeout->tv_sec * 1000) + ((timeout->tv_nsec) / 1000000)) == -1)
{
/* On timeout, break out of the loop */
bsd_dbg_select("\t~~~ SELECT: TIMEOUT\n");
break;
} else {
/* Process the received event -> re-iterate */
bsd_dbg_select("\t~~~ SELECT: Socket event, re-iterating fds\n");
}
}
/* Copy back result only if descriptor was valid */
if (readfds)
memcpy(readfds, &readfds_out, sizeof(pico_fd_set));
if (writefds)
memcpy(writefds, &writefds_out, sizeof(pico_fd_set));
if (exceptfds)
memcpy(exceptfds, &exceptfds_out, sizeof(pico_fd_set));
bsd_dbg_select("=== OUT: PICO SELECT === fds changed: %d\n", nfds_out);
return nfds_out;
}
int pico_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
{
struct timespec ts;
if (timeout) {
ts.tv_sec = timeout->tv_sec;
ts.tv_nsec = timeout->tv_usec * 1000;
return pico_pselect(nfds, readfds, writefds, exceptfds, &ts, NULL);
} else
return pico_pselect(nfds, readfds, writefds, exceptfds, NULL, NULL);
}
int pico_ppoll(struct pollfd *pfd, nfds_t npfd, const struct timespec *timeout, const sigset_t *sigmask) {
int i;
int ret = 0;
(void) sigmask;
while (ret == 0) {
for (i = 0; i < npfd; i++) {
struct pico_bsd_endpoint *ep = get_endpoint(pfd[i].fd, 0);
pfd[i].revents = 0u;
/* Always polled events */
if (!ep) {
pfd[i].revents |= POLLNVAL;
}
if (!ep->in_use) {
pfd[i].revents |= POLLNVAL;
}
if (ep->revents & (PICO_SOCK_EV_FIN | PICO_SOCK_EV_ERR)) {
pfd[i].revents |= POLLERR;
ret++;
}
if (ep->revents & PICO_SOCK_EV_CLOSE)
pfd[i].revents |= POLLHUP; /* XXX: I am sure we mean POLLRDHUP ! see man 2 poll */
/* Checking POLLIN */
if ((pfd[i].events & POLLIN) && (ep->revents & (PICO_SOCK_EV_RD | PICO_SOCK_EV_CONN))) {
pfd[i].revents |= POLLIN;
if (pfd[i].events & POLLRDNORM)
pfd[i].revents |= POLLRDNORM;
}
/* Checking POLLOUT */
if ((pfd[i].events & POLLOUT) && (ep->revents & (PICO_SOCK_EV_WR))) {
pfd[i].revents |= POLLOUT;
if (pfd[i].events & POLLWRNORM)
pfd[i].revents |= POLLWRNORM;
}
if (pfd[i].revents != 0)
ret++;
} /* End for loop */
if ((ret == 0) && timeout && (pico_signal_wait_timeout(pico_signal_select, (timeout->tv_sec * 1000) + ((timeout->tv_nsec) / 1000000)) == -1))
return 0; /* Timeout */
} /* End while loop */
return ret;
}
int pico_poll(struct pollfd *pfd, nfds_t npfd, int timeout)
{
struct timespec ts = {0U, 0U};
if (timeout >= 0) {
ts.tv_sec = timeout / 1000;
ts.tv_nsec = (timeout % 1000) * 1000000;
return pico_ppoll(pfd, npfd, &ts, NULL);
} else {
return pico_ppoll(pfd, npfd, NULL, NULL);
}
}