123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746 |
- /**
- * Copyright (c) 2015 - 2019, Nordic Semiconductor ASA
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form, except as embedded into a Nordic
- * Semiconductor ASA integrated circuit in a product or a software update for
- * such product, must reproduce the above copyright notice, this list of
- * conditions and the following disclaimer in the documentation and/or other
- * materials provided with the distribution.
- *
- * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
- * contributors may be used to endorse or promote products derived from this
- * software without specific prior written permission.
- *
- * 4. This software, with or without modification, must only be used with a
- * Nordic Semiconductor ASA integrated circuit.
- *
- * 5. Any software provided in binary form under this license must not be reverse
- * engineered, decompiled, modified and/or disassembled.
- *
- * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
- * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
- * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
- #include "nordic_common.h"
- #include "sdk_common.h"
- #include "sdk_config.h"
- #include "nrf_sdm.h"
- #include "app_scheduler.h"
- #include "app_timer.h"
- #include "iot_common.h"
- #include "app_error.h"
- #include "socket_api.h"
- #include "socket_common.h"
- #include "socket_trace.h"
- #include "sdk_os.h"
- #include "transport_if.h"
- #include "portdb.h"
- #include "errno.h"
- #include "mem_manager.h"
- #include "ipv6_parse.h"
- #include "netinet/in.h"
- #include "unistd.h"
- #include "sdk_os.h"
- #include "nrf_log_ctrl.h"
- #include "nrf_log_default_backends.h"
- #ifndef SOCKET_ENABLE_API_PARAM_CHECK
- #define SOCKET_ENABLE_API_PARAM_CHECK 0
- #endif
- #include "socket_config.h"
- #if SOCKET_CONFIG_LOG_ENABLED == 1
- NRF_LOG_MODULE_REGISTER();
- #endif
- /**
- * @defgroup api_param_check API Parameters check macros.
- *
- * @details Macros that verify parameters passed to the module in the APIs. These macros
- * could be mapped to nothing in final versions of code to save execution and size.
- * SOCKET_ENABLE_API_PARAM_CHECK should be set to 0 to disable these checks.
- *
- * @{
- */
- #if SOCKET_ENABLE_API_PARAM_CHECK == 1
- /**@brief Macro to check is module is initialized before requesting one of the module procedures. */
- #define VERIFY_MODULE_IS_INITIALIZED() \
- do { \
- if (m_initialization_state == false) \
- { \
- return (SDK_ERR_MODULE_NOT_INITIALIZED | IOT_SOCKET_ERR_BASE);\
- } \
- } while (0)
- /**
- * @brief Verify NULL parameters are not passed to API by application.
- */
- #define NULL_PARAM_CHECK(PARAM) \
- do { \
- if ((PARAM) == NULL) \
- { \
- set_errno(EFAULT); \
- return -1; \
- } \
- } while (0)
- /**
- * @brief Verify socket id passed on the API by application is valid.
- */
- #define VERIFY_SOCKET_ID(ID) \
- do { \
- if (((ID) < 0) || ((ID) >= NUM_SOCKETS)) \
- { \
- set_errno(EBADF); \
- return -1; \
- } \
- } while (0)
- #else
- #define VERIFY_MODULE_IS_INITIALIZED()
- #define NULL_PARAM_CHECK(PARAM)
- #define VERIFY_SOCKET_ID(ID)
- #endif
- /** @} */
- #define SOCKET_MUTEX_INIT() SDK_MUTEX_INIT(m_socket_mtx);
- #define SOCKET_MUTEX_LOCK() SDK_MUTEX_LOCK(m_socket_mtx)
- #define SOCKET_MUTEX_UNLOCK() SDK_MUTEX_UNLOCK(m_socket_mtx)
- // note: one extra for configuration socket
- #define NUM_SOCKETS SOCKET_MAX_SOCKET_COUNT + 1
- SDK_MUTEX_DEFINE(m_socket_mtx) /**< Mutex for protecting m_socket_table (not individual entries). */
- #define SCHED_QUEUE_SIZE 16 /**< Maximum number of events in the scheduler queue. */
- #define SCHED_MAX_EVENT_DATA_SIZE 192 /**< Maximum size of scheduler events. */
- static bool m_initialization_state = false; /**< Variable to maintain module initialization state. */
- static volatile bool m_interface_up = false; /**< Interface state. */
- static socket_t m_socket_table[NUM_SOCKETS]; /**< Socket table. */
- const struct in6_addr in6addr_any = { {0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, /**< IPv6 anycast address. */
- 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u} };
- #if defined (NRF_LOG_ENABLED) && (NRF_LOG_ENABLED == 1)
- void log_init(void)
- {
- ret_code_t err_code = NRF_LOG_INIT(NULL);
- APP_ERROR_CHECK(err_code);
- NRF_LOG_DEFAULT_BACKENDS_INIT();
- }
- #else // defined (NRF_LOG_ENABLED) && (NRF_LOG_ENABLED == 1)
- void log_init(void)
- {
- ;
- }
- #endif // defined (NRF_LOG_ENABLED) && (NRF_LOG_ENABLED == 1)
- uint32_t socket_init(void)
- {
- memset(m_socket_table, 0, sizeof(m_socket_table));
- SOCKET_MUTEX_INIT();
- log_init();
- uint32_t err_code = nrf_mem_init();
- APP_ERROR_CHECK(err_code);
- APP_SCHED_INIT(SCHED_MAX_EVENT_DATA_SIZE, SCHED_QUEUE_SIZE);
- err_code = app_timer_init();
- APP_ERROR_CHECK(err_code);
- err_code = config_socket_init();
- APP_ERROR_CHECK(err_code);
- #if SOCKET_TRANSPORT_ENABLE == 1
- err_code = portdb_init(SOCKET_MAX_SOCKET_COUNT);
- APP_ERROR_CHECK(err_code);
- transport_handler_init();
- #endif
- config_socket_start();
- m_initialization_state = true;
- SOCKET_TRACE("Socket init complete");
- return NRF_SUCCESS;
- }
- /**
- * Finds a free entry in the socket table, marks it as used and returns it. Returns -1 if no entry
- * was found.
- */
- static int socket_allocate(socket_t ** pp_socket)
- {
- int ret_sock = -1;
- SOCKET_MUTEX_LOCK();
- for (int sock = 0; sock < NUM_SOCKETS; sock++)
- {
- SOCKET_TRACE("Looking at socket %d with state %d", (int)sock, m_socket_table[sock].so_state);
- if (m_socket_table[sock].so_state == STATE_CLOSED)
- {
- m_socket_table[sock].so_state = STATE_OPEN;
- ret_sock = sock;
- *pp_socket = &m_socket_table[sock];
- break;
- }
- }
- if (ret_sock < 0)
- {
- set_errno(EMFILE);
- }
- SOCKET_MUTEX_UNLOCK();
- return ret_sock;
- }
- static socket_t * socket_find(int sock)
- {
- SOCKET_MUTEX_LOCK();
- socket_t * p_socket = &m_socket_table[sock];
- SOCKET_MUTEX_UNLOCK();
- return p_socket;
- }
- static void socket_free(int sock)
- {
- SOCKET_TRACE("Freeing socket %d", (int)sock);
- SOCKET_MUTEX_LOCK();
- memset(&m_socket_table[sock], 0, sizeof(m_socket_table[sock]));
- m_socket_table[sock].so_state = STATE_CLOSED;
- SOCKET_MUTEX_UNLOCK();
- }
- #if SOCKET_TRANSPORT_ENABLE == 1
- void transport_interface_up(void)
- {
- m_interface_up = true;
- }
- void transport_interface_down(void)
- {
- m_interface_up = false;
- for (int sock = 0; sock < NUM_SOCKETS; sock++)
- {
- (void) close(sock);
- }
- }
- #endif
- int fcntl(int fd, int cmd, int flags)
- {
- VERIFY_MODULE_IS_INITIALIZED();
- VERIFY_SOCKET_ID(fd);
- if (!((cmd == F_SETFL) || (cmd == F_GETFL)))
- {
- set_errno(EINVAL);
- return -1;
- }
- socket_t * p_socket = socket_find(fd);
- if (cmd == F_SETFL)
- {
- p_socket->so_flags = flags;
- }
- else if (cmd == F_GETFL)
- {
- return p_socket->so_flags;
- }
- return 0;
- }
- static void socket_set_errno(uint32_t err_code)
- {
- switch (err_code) {
- case UDP_INTERFACE_NOT_READY: // fallthrough
- case SOCKET_INTERFACE_NOT_READY:
- set_errno(ENETDOWN);
- break;
- case SOCKET_WOULD_BLOCK:
- set_errno(EAGAIN);
- break;
- case SOCKET_NO_ROUTE:
- set_errno(ENETUNREACH);
- break;
- case NRF_ERROR_NO_MEM: // fallthrough
- case SOCKET_NO_MEM:
- set_errno(ENOMEM);
- break;
- case SOCKET_TIMEOUT:
- set_errno(ETIMEDOUT);
- break;
- case SOCKET_NO_AVAILABLE_PORTS:
- set_errno(EMFILE);
- break;
- case SOCKET_PORT_IN_USE: // fallthrough
- case SOCKET_ADDRESS_IN_USE:
- set_errno(EADDRINUSE);
- break;
- case SOCKET_INVALID_PARAM:
- set_errno(EINVAL);
- break;
- case SOCKET_UNSUPPORTED_PROTOCOL:
- set_errno(EPROTONOSUPPORT);
- break;
- case SOCKET_NOT_CONNECTED:
- set_errno(ENOTCONN);
- break;
- }
- }
- int socket(socket_family_t family, socket_type_t type, socket_protocol_t protocol)
- {
- if (m_initialization_state == false)
- {
- (void) socket_init();
- }
- VERIFY_MODULE_IS_INITIALIZED();
- int ret_sock = -1;
- socket_t * p_socket = NULL;
- int sock = socket_allocate(&p_socket);
- SOCKET_TRACE("Got value %d from allocate", (int)sock);
- if (sock >= 0)
- {
- p_socket->so_params.so_family = family;
- p_socket->so_params.so_protocol = protocol;
- p_socket->so_params.so_type = type;
- p_socket->so_transport = NULL;
- if (family == AF_INET6)
- {
- #if SOCKET_TRANSPORT_ENABLE == 1
- p_socket->so_transport = &transport_impl;
- #else
- set_errno(EAFNOSUPPORT);
- #endif
- }
- else if (family == AF_NRF_CFG || family == AF_NRF_CFG_INTERNAL)
- {
- p_socket->so_transport = &config_socket_transport;
- }
- else
- {
- set_errno(EAFNOSUPPORT);
- }
- if (p_socket->so_transport != NULL)
- {
- uint32_t err_code = p_socket->so_transport->open(p_socket);
- socket_set_errno(err_code);
- ret_sock = (err_code == NRF_SUCCESS) ? sock : ret_sock;
- }
- if (ret_sock < 0)
- {
- socket_free(sock);
- }
- }
- SOCKET_TRACE("Returning socket value %d", (int)ret_sock);
- return ret_sock;
- }
- static uint32_t wait_interface_up(void)
- {
- SOCKET_TRACE("Waiting for interface to come up");
- uint32_t err_code = NRF_SUCCESS;
- while (err_code == NRF_SUCCESS && m_interface_up == false)
- {
- err_code = socket_wait();
- }
- if (m_interface_up == true)
- {
- SOCKET_TRACE("Interface is up!");
- }
- return err_code;
- }
- static uint32_t socket_interface_up(bool is_blocking)
- {
- uint32_t err_code = NRF_SUCCESS;
- if (m_interface_up == false)
- {
- if (is_blocking)
- {
- (void) wait_interface_up();
- }
- }
- if (m_interface_up == false)
- {
- err_code = SOCKET_INTERFACE_NOT_READY;
- }
- return err_code;
- }
- int connect(int sock, const void * p_addr, socklen_t addrlen)
- {
- VERIFY_MODULE_IS_INITIALIZED();
- VERIFY_SOCKET_ID(sock);
- NULL_PARAM_CHECK(p_addr);
- socket_t * p_socket = socket_find(sock);
- bool is_blocking = ((p_socket->so_flags & O_NONBLOCK) == 0);
- int ret = -1;
- uint32_t err_code = socket_interface_up(is_blocking);
- if (err_code != NRF_SUCCESS)
- {
- socket_set_errno(err_code);
- }
- else if (p_socket->so_state == STATE_OPEN)
- {
- err_code = p_socket->so_transport->connect(p_socket, p_addr, addrlen);
- if (err_code == NRF_SUCCESS)
- {
- p_socket->so_state = STATE_CONNECTED;
- ret = 0;
- }
- socket_set_errno(err_code);
- }
- else if (p_socket->so_state == STATE_CONNECTED)
- {
- set_errno(EISCONN);
- }
- else if (p_socket->so_state == STATE_CLOSED)
- {
- set_errno(EBADF);
- }
- return ret;
- }
- ssize_t sendto(int sock,
- const void * p_buf,
- size_t buflen,
- int flags,
- const void * p_servaddr,
- socklen_t addrlen)
- {
- VERIFY_MODULE_IS_INITIALIZED();
- VERIFY_SOCKET_ID(sock);
- NULL_PARAM_CHECK(p_buf);
- socket_t * p_socket = socket_find(sock);
- if ((p_socket->so_flags & O_NONBLOCK) != 0 &&
- (flags & MSG_WAITALL) == 0)
- {
- flags |= MSG_DONTWAIT;
- }
- uint32_t err_code = socket_interface_up(((p_socket->so_flags & O_NONBLOCK) == 0) || ((flags & MSG_DONTWAIT) == 0));
- ssize_t ret = -1;
- if (err_code == NRF_SUCCESS)
- {
- err_code = p_socket->so_transport->send(p_socket, p_buf, buflen, flags, p_servaddr, addrlen);
- if (err_code == NRF_SUCCESS)
- {
- ret = (ssize_t) buflen;
- }
- }
- socket_set_errno(err_code);
- return ret;
- }
- ssize_t send(int sock, const void * p_buf, size_t buflen, int flags)
- {
- return sendto(sock, p_buf, buflen, flags, NULL, 0);
- }
- ssize_t write(int sock, const void * p_buf, size_t buflen)
- {
- return send(sock, p_buf, buflen, 0);
- }
- ssize_t recvfrom(int sock,
- void * p_buf,
- size_t buf_size,
- int flags,
- void * p_cliaddr,
- socklen_t * p_addrlen)
- {
- VERIFY_MODULE_IS_INITIALIZED();
- VERIFY_SOCKET_ID(sock);
- NULL_PARAM_CHECK(p_buf);
- socket_t * p_socket = socket_find(sock);
- ssize_t ret = -1;
- uint32_t recv_size = buf_size;
- uint32_t err_code = p_socket->so_transport->recv(p_socket,
- p_buf,
- &recv_size,
- flags,
- p_cliaddr,
- p_addrlen);
- if (err_code == NRF_SUCCESS)
- {
- ret = (ssize_t) recv_size;
- }
- socket_set_errno(err_code);
- return ret;
- }
- ssize_t recv(int sock, void * p_buf, size_t buf_size, int flags)
- {
- return recvfrom(sock, p_buf, buf_size, flags, NULL, NULL);
- }
- ssize_t read(int sock, void * p_buf, size_t buf_size)
- {
- return recv(sock, p_buf, buf_size, 0);
- }
- int setsockopt(int sock,
- socket_opt_lvl_t level,
- int optname,
- const void * p_optval,
- socklen_t optlen)
- {
- VERIFY_MODULE_IS_INITIALIZED();
- VERIFY_SOCKET_ID(sock);
- socket_t * p_socket = socket_find(sock);
- uint32_t err_code = p_socket->so_transport->setsockopt(p_socket,
- level,
- optname,
- p_optval,
- optlen);
- socket_set_errno(err_code);
- return (err_code == NRF_SUCCESS ? 0 : -1);
- }
- int getsockopt(int sock, socket_opt_lvl_t level, int optname, void * p_optval, socklen_t * p_optlen)
- {
- VERIFY_MODULE_IS_INITIALIZED();
- VERIFY_SOCKET_ID(sock);
- socket_t * p_socket = socket_find(sock);
- uint32_t err_code = p_socket->so_transport->getsockopt(p_socket,
- level,
- optname,
- p_optval,
- p_optlen);
- socket_set_errno(err_code);
- return (err_code == NRF_SUCCESS ? 0 : -1);
- }
- int bind(int sock, const void * p_addr, socklen_t addrlen)
- {
- VERIFY_MODULE_IS_INITIALIZED();
- VERIFY_SOCKET_ID(sock);
- NULL_PARAM_CHECK(p_addr);
- socket_t * p_socket = socket_find(sock);
- bool is_blocking = ((p_socket->so_flags & O_NONBLOCK) == 0);
- int ret = -1;
- uint32_t err_code = socket_interface_up(is_blocking);
- if (err_code == NRF_SUCCESS)
- {
- err_code = p_socket->so_transport->bind(p_socket, p_addr, addrlen);
- }
- if (err_code == NRF_SUCCESS)
- {
- ret = 0;
- }
- socket_set_errno(err_code);
- return ret;
- }
- int listen(int sock, int backlog)
- {
- VERIFY_MODULE_IS_INITIALIZED();
- VERIFY_SOCKET_ID(sock);
- socket_t * p_socket = socket_find(sock);
- uint32_t err_code = p_socket->so_transport->listen(p_socket, backlog);
- return (err_code == NRF_SUCCESS ? 0 : -1);
- }
- int accept(int sock, void * p_cliaddr, socklen_t * p_addrlen)
- {
- VERIFY_MODULE_IS_INITIALIZED();
- VERIFY_SOCKET_ID(sock);
- NULL_PARAM_CHECK(p_cliaddr);
- NULL_PARAM_CHECK(p_addrlen);
- socket_t * p_socket = socket_find(sock);
- int ret = -1;
- if (p_socket->so_params.so_type != SOCK_STREAM)
- {
- set_errno(EOPNOTSUPP);
- }
- else
- {
- uint32_t err_code = NRF_SUCCESS;
- socket_t * p_client = NULL;
- int sock_cli = socket_allocate(&p_client);
- if (sock_cli >= 0)
- {
- p_client->so_params = p_socket->so_params;
- p_client->so_state = STATE_CONNECTED;
- p_client->so_transport = p_socket->so_transport;
- err_code = p_socket->so_transport->accept(p_socket, p_client, p_cliaddr, p_addrlen);
- }
- if (err_code == NRF_SUCCESS)
- {
- ret = sock_cli;
- }
- else
- {
- socket_set_errno(err_code);
- socket_free(sock_cli);
- }
- }
- return ret;
- }
- int close(int sock)
- {
- VERIFY_MODULE_IS_INITIALIZED();
- VERIFY_SOCKET_ID(sock);
- socket_t * p_socket = socket_find(sock);
- int ret = 0;
- if (p_socket->so_state != STATE_CLOSED)
- {
- uint32_t err_code = p_socket->so_transport->close(p_socket);
- ret = (err_code == NRF_SUCCESS) ? 0 : -1;
- SOCKET_TRACE("Close socket %d: ret: %d", (int)sock, ret);
- socket_free(sock);
- }
- return ret;
- }
- int fd_set_cmp(fd_set * set_a, fd_set * set_b)
- {
- int ret = 0;
- if (set_a != NULL && set_b != NULL)
- {
- for (uint32_t i = 0; i < FD_SETSIZE; i++)
- {
- if (FD_ISSET(i, set_a) != FD_ISSET(i, set_b))
- {
- ret = 1;
- break;
- }
- }
- }
- return ret;
- }
- int select(int nfds,
- fd_set * p_readset,
- fd_set * p_writeset,
- fd_set * p_exceptset,
- const struct timeval * p_timeout)
- {
- VERIFY_SOCKET_ID(nfds - 1);
- // Approximately 10 ms sleep between each iteration
- uint32_t timestep = 10000;
- uint32_t endtime = 0;
- if (p_timeout != NULL)
- {
- endtime = (p_timeout->tv_sec * 1000000) + p_timeout->tv_usec;
- }
- fd_set readset;
- FD_ZERO(&readset);
- fd_set writeset;
- FD_ZERO(&writeset);
- fd_set exceptset;
- FD_ZERO(&exceptset);
- #define SELECT_CHECK_SET(in_set, out_set, evt_var) \
- if ((in_set) != NULL) \
- { \
- if (FD_ISSET(sock, (in_set)) && (evt_var) > 0) \
- { \
- FD_SET(sock, (out_set)); \
- num_ready++; \
- } \
- else \
- { \
- FD_CLR(sock, (out_set)); \
- } \
- }
- int num_ready = 0;
- uint32_t err_code = NRF_SUCCESS;
- while (err_code == NRF_SUCCESS)
- {
- for (int sock = 0; sock < nfds; sock++)
- {
- socket_t * p_socket = socket_find(sock);
- SELECT_CHECK_SET(p_readset, &readset, p_socket->so_read_evt);
- SELECT_CHECK_SET(p_writeset, &writeset, p_socket->so_write_evt);
- SELECT_CHECK_SET(p_exceptset, &exceptset, p_socket->so_except_evt);
- }
- // TODO: Check out how app events queue up while we checked the socket
- if (fd_set_cmp(p_readset, &readset) == 0 &&
- fd_set_cmp(p_writeset, &writeset) == 0 &&
- fd_set_cmp(p_exceptset, &exceptset) == 0)
- {
- break;
- }
- else
- {
- if (p_timeout == NULL)
- {
- err_code = socket_wait();
- }
- else if (endtime - timestep < endtime)
- {
- (void) usleep(timestep);
- endtime -= timestep;
- }
- else
- {
- break;
- }
- }
- }
- return num_ready;
- }
|