diff options
Diffstat (limited to 'thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/transport/lwip/transport_handler.c')
-rw-r--r-- | thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/transport/lwip/transport_handler.c | 630 |
1 files changed, 630 insertions, 0 deletions
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/transport/lwip/transport_handler.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/transport/lwip/transport_handler.c new file mode 100644 index 0000000..4537053 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/transport/lwip/transport_handler.c @@ -0,0 +1,630 @@ +/** + * Copyright (c) 2015 - 2018, 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 "iot_defines.h" +#include "iot_errors.h" +#include "sdk_config.h" +#include "socket_common.h" +#include "socket_trace.h" +#include "transport_if.h" +#include "portdb.h" +#include "app_timer.h" +#include "mem_manager.h" +#include "nrf_fifo.h" +#include "mbuf.h" +#include "app_util.h" +#include "nrf_platform_port.h" +#include "app_util_platform.h" + +#include "lwip/opt.h" +#include "lwip/init.h" +#include "lwip/tcp.h" +#include "lwip/timers.h" + +/**@brief Verify that IPv6 address length is correct. */ +#define VERIFY_ADDRESS_LEN(len) \ + do { \ + if ((len) != sizeof(sockaddr_in6_t)) { \ + return NRF_ERROR_NULL | IOT_SOCKET_ERR_BASE; \ + } \ + } while (0) + +#define LWIP_SYS_TIMER_INTERVAL APP_TIMER_TICKS(100) /**< Interval for timer used as trigger to send. */ + +#define SOCKET_MAX_PENDING_PACKETS 32 /**< Maximum number of pending received packets. */ +#define SOCKET_MAX_PENDING_CONNECTIONS 10 /**< Maximum number of simultaneous connections. */ + +/**@brief TCP state */ +typedef enum +{ + TCP_STATE_IDLE, + TCP_STATE_REQUEST_CONNECTION, + TCP_STATE_CONNECTED, + TCP_STATE_DATA_TX_IN_PROGRESS, + TCP_STATE_TCP_SEND_PENDING, + TCP_STATE_DISCONNECTED +} tcp_state_t; + +/**@brief LwIP handle structure. */ +typedef struct { + struct tcp_pcb * p_pcb; /**< A pointer to LwIP TCP protocol control block. */ + socket_t * p_socket; /**< A pointer to corresponding socket. */ + nrf_fifo_t conn_queue; /**< Connections queue. */ + mbuf_t mbuf; /**< Memory management queue. */ + volatile tcp_state_t tcp_state; /**< TCP state. */ +} lwip_handle_t; + +static lwip_handle_t lwip_handles[SOCKET_MAX_SOCKET_COUNT]; /**< LwIP handle array. */ + +/**@brief Timer for LwIP. */ +APP_TIMER_DEF(m_lwip_timer_id); + +static err_t lwip_recv_callback(void * p_arg, + struct tcp_pcb * p_pcb, + struct pbuf * p_pbuf, + err_t err); + +static uint32_t lwip_buf_read(void * p_ctx, + uint32_t read_offset, + uint8_t * p_destbuf, + uint32_t destbuf_len) +{ + struct pbuf * p_pbuf = (struct pbuf *)p_ctx; + uint32_t copy_len = MIN(destbuf_len, (p_pbuf->len - read_offset)); + memcpy(p_destbuf, (void *)(((uint8_t *)p_pbuf->payload) + read_offset), copy_len); + return copy_len; +} + +static uint32_t lwip_buf_len(void * p_ctx) +{ + struct pbuf * p_pbuf = (struct pbuf *)p_ctx; + return p_pbuf->len; +} + +static void lwip_buf_free(void * p_ctx) +{ + nrf_free(((struct pbuf *)p_ctx)->payload); + (void) pbuf_free((struct pbuf *)p_ctx); +} + +static void lwip_timer_callback(void * p_ctx) +{ + (void) p_ctx; + sys_check_timeouts(); +} + +void nrf_driver_interface_up(iot_interface_t const * p_interface) +{ + UNUSED_PARAMETER(p_interface); + transport_interface_up(); +} + +void nrf_driver_interface_down(iot_interface_t const * p_interface) +{ + UNUSED_PARAMETER(p_interface); + transport_interface_down(); +} + +void transport_handler_init(void) +{ + lwip_init(); + uint32_t err_code = nrf_driver_init(); + APP_ERROR_CHECK(err_code); + for (uint32_t i = 0; i < SOCKET_MAX_SOCKET_COUNT; i++) + { + lwip_handles[i].p_pcb = NULL; + lwip_handles[i].p_socket = NULL; + lwip_handles[i].tcp_state = TCP_STATE_IDLE; + } + err_code = app_timer_create(&m_lwip_timer_id, + APP_TIMER_MODE_REPEATED, + lwip_timer_callback); + APP_ERROR_CHECK(err_code); + err_code = app_timer_start(m_lwip_timer_id, LWIP_SYS_TIMER_INTERVAL, NULL); + APP_ERROR_CHECK(err_code); + SOCKET_TRACE("Initialized LWIP transport handler\r\n"); +} + +static void lwip_error_handler(void * p_arg, err_t err) +{ + (void) p_arg; + SOCKET_TRACE("Error occured: %d\r\n", (int)err); + if(err == ERR_ABRT) + { + portdb_reset(); + } +} + +static err_t lwip_poll_handler(void * p_arg, struct tcp_pcb * p_pcb) +{ + (void) p_arg; + (void) p_pcb; + + return ERR_OK; +} + +static void lwip_drop_connection(void * p_ctx) +{ + struct tcp_pcb * p_pcb = (struct tcp_pcb *)p_ctx; + tcp_abort(p_pcb); +} + +static lwip_handle_t * lwip_handle_allocate(socket_t * p_socket, struct tcp_pcb * p_pcb) +{ + lwip_handle_t * p_handle = NULL; + for (uint32_t i = 0; i < SOCKET_MAX_SOCKET_COUNT; i++) + { + if (lwip_handles[i].p_pcb == NULL) + { + p_handle = &lwip_handles[i]; + (void)mbuf_init(&p_handle->mbuf, + lwip_buf_read, + lwip_buf_len, + lwip_buf_free, + SOCKET_MAX_PENDING_PACKETS); + (void) nrf_fifo_init(&p_handle->conn_queue, + SOCKET_MAX_PENDING_CONNECTIONS, + socket_wait, + lwip_drop_connection); + p_handle->p_socket = p_socket; + p_handle->tcp_state = TCP_STATE_IDLE; + if (p_pcb == NULL) + { + p_handle->p_pcb = tcp_new(); + } + else + { + p_handle->p_pcb = p_pcb; + } + tcp_arg(p_handle->p_pcb, p_handle); + tcp_setprio(p_handle->p_pcb, TCP_PRIO_MIN); + tcp_recv(p_handle->p_pcb, lwip_recv_callback); + tcp_err(p_handle->p_pcb, lwip_error_handler); + tcp_poll(p_handle->p_pcb, lwip_poll_handler, 0); + break; + } + } + SOCKET_TRACE("Allocated LWIP socket handle\r\n"); + return p_handle; +} + +static void lwip_handle_free(lwip_handle_t * p_handle) +{ + nrf_fifo_deinit(&p_handle->conn_queue); + mbuf_deinit(&p_handle->mbuf); + p_handle->p_pcb = NULL; + p_handle->p_socket = NULL; + p_handle->tcp_state = TCP_STATE_IDLE; + SOCKET_TRACE("Released LWIP socket handle\r\n"); +} + +static uint32_t lwip_transport_open(socket_t * p_socket) +{ + lwip_handle_t * p_handle = NULL; + uint32_t err_code = NRF_SUCCESS; + switch (p_socket->so_params.so_type) + { + case SOCK_STREAM: + p_handle = lwip_handle_allocate(p_socket, NULL); + if (p_handle == NULL) + { + err_code = NRF_ERROR_NULL; + } + else + { + p_socket->so_ctx = p_handle; + } + break; + default: + err_code = NRF_ERROR_NULL; + break; + } + return err_code; +} + +uint32_t lwip_error_convert(err_t lwip_err) +{ + uint32_t err_code = NRF_ERROR_NULL; + switch (lwip_err) + { + case ERR_OK: + err_code = NRF_SUCCESS; + break; + case ERR_MEM: + err_code = SOCKET_NO_MEM; + break; + case ERR_TIMEOUT: + err_code = SOCKET_TIMEOUT; + break; + case ERR_RTE: + err_code = SOCKET_NO_ROUTE; + break; + default: + err_code = NRF_ERROR_NULL; + break; + } + return err_code; +} + +static uint32_t lwip_transport_close(socket_t * p_socket) +{ + lwip_handle_t * p_handle = (lwip_handle_t *)p_socket->so_ctx; + portdb_free((uint16_t)p_handle->p_pcb->local_port); + err_t err_code = tcp_close(p_handle->p_pcb); + if (err_code == ERR_OK) + { + lwip_handle_free(p_handle); + } + return lwip_error_convert(err_code); +} + +static err_t lwip_connect_callback(void * p_arg, struct tcp_pcb * p_pcb, err_t err) +{ + lwip_handle_t * p_handle = (lwip_handle_t *)p_arg; + // TODO: Error check + SOCKET_TRACE("New connection\r\n"); + p_handle->tcp_state = TCP_STATE_CONNECTED; + return ERR_OK; +} + +static err_t lwip_recv_callback(void * p_arg, + struct tcp_pcb * p_pcb, + struct pbuf * p_pbuf, + err_t err) +{ + lwip_handle_t * p_handle = (lwip_handle_t *)p_arg; + socket_t * p_socket = p_handle->p_socket; + if (err == ERR_OK) + { + uint8_t * p_payload_cp = nrf_malloc(p_pbuf->len); + struct pbuf * p_pbuf_cp = pbuf_alloc(PBUF_RAW, p_pbuf->len, PBUF_REF); + + memcpy(p_payload_cp, p_pbuf->payload, p_pbuf->len); + p_pbuf_cp->payload = p_payload_cp; + p_pbuf_cp->len = p_pbuf->len; + p_pbuf_cp->tot_len = p_pbuf->tot_len; + + uint32_t err_code = mbuf_write(&p_handle->mbuf, p_pbuf_cp); + if (err_code == NRF_SUCCESS) + { + p_socket->so_read_evt++; + } + else + { + err = ERR_MEM; + } + } + return err; +} + +static uint32_t lwip_wait_for_state(lwip_handle_t * p_handle, tcp_state_t state) +{ + uint32_t err_code = NRF_SUCCESS; + while (err_code == NRF_SUCCESS && + p_handle->tcp_state != state && + p_handle->tcp_state != TCP_STATE_DISCONNECTED) + { + err_code = socket_wait(); + } + if (err_code == NRF_SUCCESS && p_handle->tcp_state != state) + { + err_code = NRF_ERROR_NULL; + } + return err_code; +} + +static void lwipaddr_to_sockaddr(const ip6_addr_t * p_lwip_addr, sockaddr_in6_t * p_addr_in6) +{ + struct in6_addr * addr = &p_addr_in6->sin6_addr; + addr->s6_addr[0] = p_lwip_addr->addr[0] & 0xFF; + addr->s6_addr[1] = (p_lwip_addr->addr[0] >> 8) & 0xFF; + addr->s6_addr[2] = (p_lwip_addr->addr[0] >> 16) & 0xFF; + addr->s6_addr[3] = (p_lwip_addr->addr[0] >> 24) & 0xFF; + + addr->s6_addr[4] = p_lwip_addr->addr[1] & 0xFF; + addr->s6_addr[5] = (p_lwip_addr->addr[1] >> 8) & 0xFF; + addr->s6_addr[6] = (p_lwip_addr->addr[1] >> 16) & 0xFF; + addr->s6_addr[7] = (p_lwip_addr->addr[1] >> 24) & 0xFF; + + addr->s6_addr[8] = p_lwip_addr->addr[2] & 0xFF; + addr->s6_addr[9] = (p_lwip_addr->addr[2] >> 8) & 0xFF; + addr->s6_addr[10] = (p_lwip_addr->addr[2] >> 16) & 0xFF; + addr->s6_addr[11] = (p_lwip_addr->addr[2] >> 24) & 0xFF; + + addr->s6_addr[12] = p_lwip_addr->addr[3] & 0xFF; + addr->s6_addr[13] = (p_lwip_addr->addr[3] >> 8) & 0xFF; + addr->s6_addr[14] = (p_lwip_addr->addr[3] >> 16) & 0xFF; + addr->s6_addr[15] = (p_lwip_addr->addr[3] >> 24) & 0xFF; +} + +static void sockaddr_to_lwipaddr(const sockaddr_in6_t * p_addr_in6, ip6_addr_t * p_lwip_addr) +{ + const struct in6_addr * addr = &p_addr_in6->sin6_addr; + IP6_ADDR_PART(p_lwip_addr, 0, addr->s6_addr[0], addr->s6_addr[1], addr->s6_addr[2], addr->s6_addr[3]); + IP6_ADDR_PART(p_lwip_addr, 1, addr->s6_addr[4], addr->s6_addr[5], addr->s6_addr[6], addr->s6_addr[7]); + IP6_ADDR_PART(p_lwip_addr, 2, addr->s6_addr[8], addr->s6_addr[9], addr->s6_addr[10], addr->s6_addr[11]); + IP6_ADDR_PART(p_lwip_addr, 3, addr->s6_addr[12], addr->s6_addr[13], addr->s6_addr[14], addr->s6_addr[15]); +} + +static uint32_t lwip_transport_bind(socket_t * p_socket, const void * p_addr, socklen_t addr_len) +{ + VERIFY_ADDRESS_LEN(addr_len); + const sockaddr_in6_t * p_addr_in6 = (const sockaddr_in6_t *)p_addr; + lwip_handle_t * p_handle = (lwip_handle_t *)p_socket->so_ctx; + + uint16_t port = HTONS(p_addr_in6->sin6_port); + uint32_t err_code = portdb_register(port); + if (err_code == NRF_SUCCESS) + { + ip6_addr_t lwip_addr; + sockaddr_to_lwipaddr(p_addr_in6, &lwip_addr); + err_t err = tcp_bind(p_handle->p_pcb, &lwip_addr, port); + err_code = lwip_error_convert(err); + } + return err_code; +} + +static uint32_t lwip_transport_connect(socket_t * p_socket, + const void * p_addr, + socklen_t addr_len) +{ + VERIFY_ADDRESS_LEN(addr_len); + + bool is_blocking = ((p_socket->so_flags & O_NONBLOCK) == 0); + lwip_handle_t * p_handle = (lwip_handle_t *)p_socket->so_ctx; + const sockaddr_in6_t * p_addr_in6 = (const sockaddr_in6_t *)p_addr; + uint16_t port = 0; + uint32_t err_code = portdb_alloc(&port); + + SOCKET_TRACE("Binding to port %d\r\n", (int)port); + if (err_code == NRF_SUCCESS) + { + ip6_addr_t any_addr; + ip6_addr_set_any(&any_addr); + err_t err = tcp_bind (p_handle->p_pcb, &any_addr, port); + SOCKET_TRACE("Err %d from bind\r\n", (int)err); + err_code = lwip_error_convert(err); + } + + if (err_code == NRF_SUCCESS) + { + ip6_addr_t remote_addr; + sockaddr_to_lwipaddr(p_addr_in6, &remote_addr); + + p_handle->tcp_state = TCP_STATE_REQUEST_CONNECTION; + err_t err = tcp_connect(p_handle->p_pcb, &remote_addr, HTONS(p_addr_in6->sin6_port), lwip_connect_callback); + SOCKET_TRACE("Err %d from connect\r\n", (int)err); + err_code = lwip_error_convert(err); + + if (err_code == NRF_SUCCESS && is_blocking) + { + err_code = lwip_wait_for_state(p_handle, TCP_STATE_CONNECTED); + } + } + if (err_code != NRF_SUCCESS) + { + SOCKET_TRACE("Error %d when connecting to socket\r\n", (int)err_code); + portdb_free(port); + } + else + { + SOCKET_TRACE("Successfully connected to remote host!\r\n"); + } + + return err_code; +} + +static err_t lwip_send_complete(void * p_arg, struct tcp_pcb * p_pcb, u16_t len) +{ + lwip_handle_t * p_handle = (lwip_handle_t *)p_arg; + if (p_handle->tcp_state == TCP_STATE_TCP_SEND_PENDING) + { + p_handle->tcp_state = TCP_STATE_DATA_TX_IN_PROGRESS; + } + return ERR_OK; +} + +static inline uint32_t lwip_check_connected(socket_t * p_socket) +{ + return (p_socket->so_state == STATE_CONNECTED || p_socket->so_params.so_type == SOCK_DGRAM) ? + NRF_SUCCESS : SOCKET_NOT_CONNECTED; +} + + +static uint32_t lwip_transport_send(socket_t * p_socket, + const void * p_buf, + uint32_t buf_len, + int flags, + const void * p_destaddr, + socklen_t destaddr_len) +{ + (void) p_destaddr; + (void) destaddr_len; + + lwip_handle_t * p_handle = (lwip_handle_t *)p_socket->so_ctx; + if ((p_socket->so_flags & O_NONBLOCK) != 0 && + (flags & MSG_WAITALL) == 0) + { + flags |= MSG_DONTWAIT; + } + + uint32_t err_code = lwip_check_connected(p_socket); + if (err_code == NRF_SUCCESS) + { + uint32_t len = tcp_sndbuf(p_handle->p_pcb); + if (len >= buf_len) + { + tcp_sent(p_handle->p_pcb, lwip_send_complete); + p_handle->tcp_state = TCP_STATE_TCP_SEND_PENDING; + err_t err = tcp_write(p_handle->p_pcb, p_buf, buf_len, 1); + err_code = lwip_error_convert(err); + if (err_code == NRF_SUCCESS && + (flags & MSG_DONTWAIT) == 0) + { + err_code = lwip_wait_for_state(p_handle, TCP_STATE_DATA_TX_IN_PROGRESS); + } + } + else + { + err_code = SOCKET_NO_MEM; + } + } + return err_code; +} + + +static uint32_t lwip_transport_recv(socket_t * p_socket, + void * p_buf, + uint32_t * p_buf_size, + int flags, + void * p_srcaddr, + socklen_t * p_srcaddr_len) +{ + lwip_handle_t * p_handle = (lwip_handle_t *)p_socket->so_ctx; + if ((p_socket->so_flags & O_NONBLOCK) != 0 && + (flags & MSG_WAITALL) == 0) + { + flags |= MSG_DONTWAIT; + } + uint32_t err_code = lwip_check_connected(p_socket); + if (err_code == NRF_SUCCESS) + { + if (mbuf_empty(&p_handle->mbuf) == true) + { + if ((flags & MSG_DONTWAIT) != 0) + { + err_code = SOCKET_WOULD_BLOCK; + } + else + { + while ((mbuf_empty(&p_handle->mbuf) == true) && (err_code == NRF_SUCCESS)) + { + err_code = socket_wait(); + } + } + } + } + + if (err_code == NRF_SUCCESS) + { + *p_buf_size = mbuf_read(&p_handle->mbuf, p_buf, *p_buf_size); + tcp_recved(p_handle->p_pcb, *p_buf_size); + p_socket->so_read_evt = 0; + } + return err_code; +} + +static err_t lwip_accept_callback(void * p_arg, struct tcp_pcb * p_pcb, err_t err) +{ + lwip_handle_t * p_handle = (lwip_handle_t *)p_arg; + socket_t * p_socket = p_handle->p_socket; + if (err == ERR_OK) + { + (void) nrf_fifo_enq(&p_handle->conn_queue, p_pcb, true); + // TODO: Error check + p_socket->so_read_evt++; + } + return err; +} + +static uint32_t lwip_transport_listen(socket_t * p_socket, int backlog) +{ + lwip_handle_t * p_handle = (lwip_handle_t *)p_socket->so_ctx; + uint32_t err_code = NRF_SUCCESS; + struct tcp_pcb * p_pcb = tcp_listen_with_backlog(p_handle->p_pcb, backlog); + if (p_pcb == NULL) + { + err_code = SOCKET_ADDRESS_IN_USE; + } + else + { + p_handle->p_pcb = p_pcb; + tcp_accept(p_handle->p_pcb, lwip_accept_callback); + } + return err_code; +} + +static uint32_t lwip_transport_accept(socket_t * p_socket, + socket_t * p_client, + void * p_cliaddr, + socklen_t * p_cliaddr_len) +{ + VERIFY_ADDRESS_LEN(*p_cliaddr_len); + struct tcp_pcb * p_client_pcb = NULL; + lwip_handle_t * p_lwip_handle = (lwip_handle_t *)p_socket->so_ctx; + uint32_t err_code = nrf_fifo_deq(&p_lwip_handle->conn_queue, + (void **)&p_client_pcb, + (p_socket->so_flags & O_NONBLOCK) == 0); + + if (err_code == NRF_SUCCESS) + { + lwip_handle_t * p_lwip_client = lwip_handle_allocate(p_client, p_client_pcb); + sockaddr_in6_t * p_sockaddr = (sockaddr_in6_t *)p_cliaddr; + + p_client->so_ctx = p_lwip_client; + + lwipaddr_to_sockaddr(&p_client_pcb->remote_ip, p_sockaddr); + p_sockaddr->sin6_port = p_client_pcb->remote_port; + p_sockaddr->sin6_len = *p_cliaddr_len; + p_sockaddr->sin6_family = AF_INET6; + p_sockaddr->sin6_flowinfo = 0; + p_sockaddr->sin6_scope_id = 0; + + /*lint -save -e548 */ + tcp_accepted(p_lwip_handle->p_pcb); + /*lint -restore */ + } + + return err_code; +} + +/** + * @brief Transport for LwIP socket. + */ +socket_transport_t transport_impl = +{ + .open = lwip_transport_open, + .bind = lwip_transport_bind, + .connect = lwip_transport_connect, + .send = lwip_transport_send, + .recv = lwip_transport_recv, + .listen = lwip_transport_listen, + .accept = lwip_transport_accept, + .close = lwip_transport_close +}; |