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 Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*********************************************************************
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);
}
}