diff --git a/drivers/wifi/eswifi/CMakeLists.txt b/drivers/wifi/eswifi/CMakeLists.txt index 9147380416f..071c204b792 100644 --- a/drivers/wifi/eswifi/CMakeLists.txt +++ b/drivers/wifi/eswifi/CMakeLists.txt @@ -12,5 +12,7 @@ if(CONFIG_WIFI_ESWIFI) eswifi_core.c eswifi_bus_spi.c eswifi_offload.c + eswifi_socket.c ) +zephyr_sources_ifdef(CONFIG_NET_SOCKETS_OFFLOAD eswifi_socket_offload.c) endif() diff --git a/drivers/wifi/eswifi/eswifi.h b/drivers/wifi/eswifi/eswifi.h index bf7c1f4d395..497aabe9a8d 100644 --- a/drivers/wifi/eswifi/eswifi.h +++ b/drivers/wifi/eswifi/eswifi.h @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -110,12 +111,37 @@ static inline void eswifi_unlock(struct eswifi_dev *eswifi) } } +int eswifi_at_cmd(struct eswifi_dev *eswifi, char *cmd); +static inline int __select_socket(struct eswifi_dev *eswifi, u8_t idx) +{ + snprintf(eswifi->buf, sizeof(eswifi->buf), "P0=%d\r", idx); + return eswifi_at_cmd(eswifi, eswifi->buf); +} + +static inline +struct eswifi_dev *eswifi_socket_to_dev(struct eswifi_off_socket *socket) +{ + return CONTAINER_OF(socket - socket->index, struct eswifi_dev, socket); +} + extern struct eswifi_bus_ops eswifi_bus_ops_spi; int eswifi_offload_init(struct eswifi_dev *eswifi); struct eswifi_dev *eswifi_by_iface_idx(u8_t iface); int eswifi_at_cmd_rsp(struct eswifi_dev *eswifi, char *cmd, char **rsp); -int eswifi_at_cmd(struct eswifi_dev *eswifi, char *cmd); void eswifi_async_msg(struct eswifi_dev *eswifi, char *msg, size_t len); void eswifi_offload_async_msg(struct eswifi_dev *eswifi, char *msg, size_t len); +int __eswifi_socket_free(struct eswifi_dev *eswifi, + struct eswifi_off_socket *socket); +int __eswifi_socket_new(struct eswifi_dev *eswifi, int family, int type, + int proto, void *context); +int __eswifi_off_start_client(struct eswifi_dev *eswifi, + struct eswifi_off_socket *socket); +int __eswifi_accept(struct eswifi_dev *eswifi, struct eswifi_off_socket *socket); +int __eswifi_bind(struct eswifi_dev *eswifi, struct eswifi_off_socket *socket, + const struct sockaddr *addr, socklen_t addrlen); +#if defined(CONFIG_NET_SOCKETS_OFFLOAD) +int eswifi_socket_offload_init(struct eswifi_dev *leswifi); +#endif + #endif diff --git a/drivers/wifi/eswifi/eswifi_core.c b/drivers/wifi/eswifi/eswifi_core.c index c3381fa6081..96795cddd18 100644 --- a/drivers/wifi/eswifi/eswifi_core.c +++ b/drivers/wifi/eswifi/eswifi_core.c @@ -401,6 +401,10 @@ static void eswifi_iface_init(struct net_if *iface) eswifi_unlock(eswifi); eswifi_offload_init(eswifi); +#if defined(CONFIG_NET_SOCKETS_OFFLOAD) + eswifi_socket_offload_init(eswifi); +#endif + } static int eswifi_mgmt_scan(struct device *dev, scan_result_cb_t cb) diff --git a/drivers/wifi/eswifi/eswifi_offload.c b/drivers/wifi/eswifi/eswifi_offload.c index e87b5a3907e..bcdb5c67609 100644 --- a/drivers/wifi/eswifi/eswifi_offload.c +++ b/drivers/wifi/eswifi/eswifi_offload.c @@ -20,124 +20,6 @@ LOG_MODULE_REGISTER(wifi_eswifi_offload); #include "eswifi.h" -static inline int __select_socket(struct eswifi_dev *eswifi, u8_t idx) -{ - snprintf(eswifi->buf, sizeof(eswifi->buf), "P0=%d\r", idx); - return eswifi_at_cmd(eswifi, eswifi->buf); -} - -static int __stop_socket(struct eswifi_dev *eswifi, - struct eswifi_off_socket *socket) -{ - char cmd_srv[] = "P5=0\r"; - char cmd_cli[] = "P6=0\r"; - - LOG_DBG("Stopping socket %d", socket->index); - - if (socket->state != ESWIFI_SOCKET_STATE_CONNECTED) { - return 0; - } - - socket->state = ESWIFI_SOCKET_STATE_NONE; - return eswifi_at_cmd(eswifi, socket->is_server ? cmd_srv : cmd_cli); -} - -static int __read_data(struct eswifi_dev *eswifi, size_t len, char **data) -{ - char cmd[] = "R0\r"; - char size[] = "R1=9999\r"; - char timeout[] = "R2=30000\r"; - int ret; - - /* Set max read size */ - snprintf(size, sizeof(size), "R1=%u\r", len); - ret = eswifi_at_cmd(eswifi, size); - if (ret < 0) { - LOG_ERR("Unable to set read size"); - return -EIO; - } - - /* Set timeout */ - snprintf(timeout, sizeof(timeout), "R2=%u\r", 30); /* 30 ms */ - ret = eswifi_at_cmd(eswifi, timeout); - if (ret < 0) { - LOG_ERR("Unable to set timeout"); - return -EIO; - } - - return eswifi_at_cmd_rsp(eswifi, cmd, data); -} - -static inline -struct eswifi_dev *eswifi_socket_to_dev(struct eswifi_off_socket *socket) -{ - return CONTAINER_OF(socket - socket->index, struct eswifi_dev, socket); -} - -static void eswifi_off_read_work(struct k_work *work) -{ - struct eswifi_off_socket *socket; - struct eswifi_dev *eswifi; - struct net_pkt *pkt; - int err, len; - char *data; - - LOG_DBG(""); - - socket = CONTAINER_OF(work, struct eswifi_off_socket, read_work); - eswifi = eswifi_socket_to_dev(socket); - - eswifi_lock(eswifi); - - if (socket->type == ESWIFI_TRANSPORT_TCP && - socket->state != ESWIFI_SOCKET_STATE_CONNECTED) { - goto done; - } - - __select_socket(eswifi, socket->index); - - len = __read_data(eswifi, 1460, &data); /* 1460 is max size */ - if (len < 0) { - __stop_socket(eswifi, socket); - goto done; - } else if (!len || !socket->recv_cb) { - goto done; - } - - LOG_DBG("payload sz = %d", len); - - pkt = net_pkt_rx_alloc_with_buffer(eswifi->iface, len, - AF_UNSPEC, 0, K_NO_WAIT); - if (!pkt) { - LOG_ERR("Cannot allocate rx packet"); - goto done; - } - - if (!net_pkt_write(pkt, data, len)) { - LOG_WRN("Incomplete buffer copy"); - } - - eswifi_unlock(eswifi); - - net_pkt_cursor_init(pkt); - socket->recv_cb(socket->context, pkt, - NULL, NULL, 0, socket->recv_data); - - eswifi_lock(eswifi); - - k_sem_give(&socket->read_sem); - k_yield(); - -done: - err = k_delayed_work_submit_to_queue(&eswifi->work_q, - &socket->read_work, - 500); - if (err) { - LOG_ERR("Rescheduling socket read error"); - } - - eswifi_unlock(eswifi); -} static int eswifi_off_bind(struct net_context *context, const struct sockaddr *addr, @@ -147,30 +29,12 @@ static int eswifi_off_bind(struct net_context *context, struct eswifi_dev *eswifi = eswifi_by_iface_idx(context->iface); int err; - if (addr->sa_family != AF_INET) { - LOG_ERR("Only AF_INET is supported!"); - return -EPFNOSUPPORT; - } - LOG_DBG(""); - eswifi_lock(eswifi); - - __select_socket(eswifi, socket->index); - socket->port = sys_be16_to_cpu(net_sin(addr)->sin_port); - - /* Set Local Port */ - snprintf(eswifi->buf, sizeof(eswifi->buf), "P2=%d\r", socket->port); - err = eswifi_at_cmd(eswifi, eswifi->buf); - if (err < 0) { - LOG_ERR("Unable to set local port"); - eswifi_unlock(eswifi); - return -EIO; - } - + err = __eswifi_bind(eswifi, socket, addr, addrlen); eswifi_unlock(eswifi); - return 0; + return err; } static int eswifi_off_listen(struct net_context *context, int backlog) @@ -200,48 +64,6 @@ static int eswifi_off_listen(struct net_context *context, int backlog) return err; } -static int __eswifi_off_start_client(struct eswifi_dev *eswifi, - struct eswifi_off_socket *socket) -{ - struct sockaddr *addr = &socket->peer_addr; - struct in_addr *sin_addr = &net_sin(addr)->sin_addr; - int err; - - LOG_DBG(""); - - __select_socket(eswifi, socket->index); - - /* Set Remote IP */ - snprintf(eswifi->buf, sizeof(eswifi->buf), "P3=%u.%u.%u.%u\r", - sin_addr->s4_addr[0], sin_addr->s4_addr[1], - sin_addr->s4_addr[2], sin_addr->s4_addr[3]); - - err = eswifi_at_cmd(eswifi, eswifi->buf); - if (err < 0) { - LOG_ERR("Unable to set remote ip"); - return -EIO; - } - - /* Set Remote Port */ - snprintf(eswifi->buf, sizeof(eswifi->buf), "P4=%d\r", - (u16_t)sys_be16_to_cpu(net_sin(addr)->sin_port)); - err = eswifi_at_cmd(eswifi, eswifi->buf); - if (err < 0) { - LOG_ERR("Unable to set remote port"); - return -EIO; - } - - /* Start TCP/UDP client */ - snprintf(eswifi->buf, sizeof(eswifi->buf), "P6=1\r"); - err = eswifi_at_cmd(eswifi, eswifi->buf); - if (err < 0) { - LOG_ERR("Unable to start TCP/UDP client"); - return -EIO; - } - - return 0; -} - static void eswifi_off_connect_work(struct k_work *work) { struct eswifi_off_socket *socket; @@ -333,28 +155,16 @@ static int eswifi_off_accept(struct net_context *context, { struct eswifi_off_socket *socket = context->offload_context; struct eswifi_dev *eswifi = eswifi_by_iface_idx(context->iface); - char cmd[] = "P5=1\r"; + int ret; eswifi_lock(eswifi); - if (socket->state != ESWIFI_SOCKET_STATE_NONE) { - /* we can only handle one connection at a time */ + ret = __eswifi_accept(eswifi, socket); + if (ret < 0) { eswifi_unlock(eswifi); - return -EBUSY; + return ret; } - __select_socket(eswifi, socket->index); - - /* Start TCP Server */ - if (eswifi_at_cmd(eswifi, cmd) < 0) { - LOG_ERR("Unable to start TCP server"); - eswifi_unlock(eswifi); - return -EIO; - } - - LOG_DBG("TCP Server started"); - - socket->state = ESWIFI_SOCKET_STATE_ACCEPTING; socket->accept_cb = cb; socket->accept_data = user_data; k_sem_reset(&socket->accept_sem); @@ -575,31 +385,23 @@ static int eswifi_off_put(struct net_context *context) { struct eswifi_off_socket *socket = context->offload_context; struct eswifi_dev *eswifi = eswifi_by_iface_idx(context->iface); + int ret; LOG_DBG(""); eswifi_lock(eswifi); - if (socket->type == ESWIFI_TRANSPORT_TCP && - socket->state != ESWIFI_SOCKET_STATE_CONNECTED) { - eswifi_unlock(eswifi); - return -ENOTCONN; + ret = __eswifi_socket_free(eswifi, socket); + if (ret) { + goto done; } - __select_socket(eswifi, socket->index); - - k_delayed_work_cancel(&socket->read_work); - - __select_socket(eswifi, socket->index); - __stop_socket(eswifi, socket); - if (--socket->usage <= 0) { memset(socket, 0, sizeof(*socket)); } - +done: eswifi_unlock(eswifi); - - return 0; + return ret; } static int eswifi_off_get(sa_family_t family, @@ -609,75 +411,33 @@ static int eswifi_off_get(sa_family_t family, { struct eswifi_dev *eswifi = eswifi_by_iface_idx((*context)->iface); struct eswifi_off_socket *socket = NULL; - int err, i; + int idx; LOG_DBG(""); - if (family != AF_INET) { - LOG_ERR("Only AF_INET is supported!"); - return -EPFNOSUPPORT; - } - eswifi_lock(eswifi); - /* pickup available socket */ - for (i = 0; i < ESWIFI_OFFLOAD_MAX_SOCKETS; i++) { - if (!eswifi->socket[i].context) { - socket = &eswifi->socket[i]; - socket->index = i; - socket->context = *context; - (*context)->offload_context = socket; - break; - } + idx = __eswifi_socket_new(eswifi, family, type, ip_proto, *context); + if (idx < 0) { + goto unlock; } - if (!socket) { - LOG_ERR("No socket resource available"); - eswifi_unlock(eswifi); - return -ENOMEM; - } - - err = __select_socket(eswifi, socket->index); - if (err < 0) { - LOG_ERR("Unable to select socket %u", socket->index); - eswifi_unlock(eswifi); - return -EIO; - } - - /* Set Transport Protocol */ - if (ip_proto == IPPROTO_TCP) { - socket->type = ESWIFI_TRANSPORT_TCP; - } else if (ip_proto == IPPROTO_UDP) { - socket->type = ESWIFI_TRANSPORT_UDP; - } else { - LOG_ERR("Only TCP & UDP is supported"); - eswifi_unlock(eswifi); - return -EPFNOSUPPORT; - } - - snprintf(eswifi->buf, sizeof(eswifi->buf), "P1=%d\r", socket->type); - - err = eswifi_at_cmd(eswifi, eswifi->buf); - if (err < 0) { - LOG_ERR("Unable to set transport protocol"); - eswifi_unlock(eswifi); - return -EIO; - } + socket = &eswifi->socket[idx]; + (*context)->offload_context = socket; LOG_DBG("Socket index %d", socket->index); k_work_init(&socket->connect_work, eswifi_off_connect_work); k_work_init(&socket->send_work, eswifi_off_send_work); - k_delayed_work_init(&socket->read_work, eswifi_off_read_work); k_sem_init(&socket->read_sem, 1, 1); k_sem_init(&socket->accept_sem, 1, 1); - socket->usage = 1; k_delayed_work_submit_to_queue(&eswifi->work_q, &socket->read_work, 500); - eswifi_unlock(eswifi); - return 0; +unlock: + eswifi_unlock(eswifi); + return idx; } void eswifi_offload_async_msg(struct eswifi_dev *eswifi, char *msg, size_t len) diff --git a/drivers/wifi/eswifi/eswifi_offload.h b/drivers/wifi/eswifi/eswifi_offload.h index 9e3a3aeca80..d477d16764a 100644 --- a/drivers/wifi/eswifi/eswifi_offload.h +++ b/drivers/wifi/eswifi/eswifi_offload.h @@ -8,7 +8,6 @@ #define ZEPHYR_DRIVERS_WIFI_ESWIFI_ESWIFI_OFFLOAD_H_ #include - #include "eswifi.h" #define ESWIFI_OFFLOAD_MAX_SOCKETS 4 @@ -50,6 +49,7 @@ struct eswifi_off_socket { u16_t port; bool is_server; int usage; + struct k_fifo fifo; + struct net_pkt *prev_pkt_rem; }; - #endif diff --git a/drivers/wifi/eswifi/eswifi_socket.c b/drivers/wifi/eswifi/eswifi_socket.c new file mode 100644 index 00000000000..226eb279298 --- /dev/null +++ b/drivers/wifi/eswifi/eswifi_socket.c @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2019 Linumiz + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +LOG_MODULE_REGISTER(wifi_eswifi, CONFIG_WIFI_LOG_LEVEL); + +#include +#include +#include +#include +#include +#include +#include + +#include "eswifi.h" +#include + +static int __stop_socket(struct eswifi_dev *eswifi, + struct eswifi_off_socket *socket) +{ + char cmd_srv[] = "P5=0\r"; + char cmd_cli[] = "P6=0\r"; + + LOG_DBG("Stopping socket %d", socket->index); + if (socket->state != ESWIFI_SOCKET_STATE_CONNECTED) { + return 0; + } + + socket->state = ESWIFI_SOCKET_STATE_NONE; + return eswifi_at_cmd(eswifi, socket->is_server ? cmd_srv : cmd_cli); +} + +static int __read_data(struct eswifi_dev *eswifi, size_t len, char **data) +{ + char cmd[] = "R0\r"; + char size[] = "R1=9999\r"; + char timeout[] = "R2=30000\r"; + int ret; + + /* Set max read size */ + snprintf(size, sizeof(size), "R1=%u\r", len); + ret = eswifi_at_cmd(eswifi, size); + if (ret < 0) { + LOG_ERR("Unable to set read size"); + return -EIO; + } + + /* Set timeout */ + snprintf(timeout, sizeof(timeout), "R2=%u\r", 30); /* 30 ms */ + ret = eswifi_at_cmd(eswifi, timeout); + if (ret < 0) { + LOG_ERR("Unable to set timeout"); + return -EIO; + } + + return eswifi_at_cmd_rsp(eswifi, cmd, data); +} + +int __eswifi_bind(struct eswifi_dev *eswifi, struct eswifi_off_socket *socket, + const struct sockaddr *addr, socklen_t addrlen) +{ + int err; + + if (addr->sa_family != AF_INET) { + LOG_ERR("Only AF_INET is supported!"); + return -EPFNOSUPPORT; + } + + __select_socket(eswifi, socket->index); + socket->port = sys_be16_to_cpu(net_sin(addr)->sin_port); + + /* Set Local Port */ + snprintf(eswifi->buf, sizeof(eswifi->buf), "P2=%d\r", socket->port); + err = eswifi_at_cmd(eswifi, eswifi->buf); + if (err < 0) { + LOG_ERR("Unable to set local port"); + return -EIO; + } + + return 0; +} + +static void eswifi_off_read_work(struct k_work *work) +{ + struct eswifi_off_socket *socket; + struct eswifi_dev *eswifi; + struct net_pkt *pkt; + int err, len; + char *data; + + LOG_DBG(""); + + socket = CONTAINER_OF(work, struct eswifi_off_socket, read_work); + eswifi = eswifi_socket_to_dev(socket); + + eswifi_lock(eswifi); + + if ((socket->type == ESWIFI_TRANSPORT_TCP || + socket->type == ESWIFI_TRANSPORT_TCP_SSL) && + socket->state != ESWIFI_SOCKET_STATE_CONNECTED) { + goto done; + } + + __select_socket(eswifi, socket->index); + + len = __read_data(eswifi, 1460, &data); /* 1460 is max size */ + if (len < 0) { + __stop_socket(eswifi, socket); + goto done; + } else if (!len || !socket->recv_cb) { + goto done; + } + + LOG_DBG("payload sz = %d", len); + + pkt = net_pkt_rx_alloc_with_buffer(eswifi->iface, len, + AF_UNSPEC, 0, K_NO_WAIT); + if (!pkt) { + LOG_ERR("Cannot allocate rx packet"); + goto done; + } + + if (!net_pkt_write(pkt, data, len)) { + LOG_WRN("Incomplete buffer copy"); + } + + eswifi_unlock(eswifi); + + net_pkt_cursor_init(pkt); + socket->recv_cb(socket->context, pkt, + NULL, NULL, 0, socket->recv_data); + + eswifi_lock(eswifi); + + k_sem_give(&socket->read_sem); + k_yield(); + +done: + err = k_delayed_work_submit_to_queue(&eswifi->work_q, + &socket->read_work, + 500); + if (err) { + LOG_ERR("Rescheduling socket read error"); + } + + eswifi_unlock(eswifi); +} + +int __eswifi_off_start_client(struct eswifi_dev *eswifi, + struct eswifi_off_socket *socket) +{ + struct sockaddr *addr = &socket->peer_addr; + struct in_addr *sin_addr = &net_sin(addr)->sin_addr; + int err; + + LOG_DBG(""); + + __select_socket(eswifi, socket->index); + + /* Set Remote IP */ + snprintf(eswifi->buf, sizeof(eswifi->buf), "P3=%u.%u.%u.%u\r", + sin_addr->s4_addr[0], sin_addr->s4_addr[1], + sin_addr->s4_addr[2], sin_addr->s4_addr[3]); + + err = eswifi_at_cmd(eswifi, eswifi->buf); + if (err < 0) { + LOG_ERR("Unable to set remote ip"); + return -EIO; + } + + /* Set Remote Port */ + snprintf(eswifi->buf, sizeof(eswifi->buf), "P4=%d\r", + (u16_t)sys_be16_to_cpu(net_sin(addr)->sin_port)); + err = eswifi_at_cmd(eswifi, eswifi->buf); + if (err < 0) { + LOG_ERR("Unable to set remote port"); + return -EIO; + } + + /* Start TCP/UDP client */ + snprintf(eswifi->buf, sizeof(eswifi->buf), "P6=1\r"); + err = eswifi_at_cmd(eswifi, eswifi->buf); + if (err < 0) { + LOG_ERR("Unable to start TCP/UDP client"); + return -EIO; + } + + return 0; +} + +int __eswifi_accept(struct eswifi_dev *eswifi, struct eswifi_off_socket *socket) +{ + char cmd[] = "P5=1\r"; + + if (socket->state != ESWIFI_SOCKET_STATE_NONE) { + /* we can only handle one connection at a time */ + return -EBUSY; + } + + __select_socket(eswifi, socket->index); + + /* Start TCP Server */ + if (eswifi_at_cmd(eswifi, cmd) < 0) { + LOG_ERR("Unable to start TCP server"); + return -EIO; + } + + LOG_DBG("TCP Server started"); + socket->state = ESWIFI_SOCKET_STATE_ACCEPTING; + + return 0; +} + +int __eswifi_socket_free(struct eswifi_dev *eswifi, + struct eswifi_off_socket *socket) +{ + if ((socket->type == ESWIFI_TRANSPORT_TCP || + socket->type == ESWIFI_TRANSPORT_TCP_SSL) && + socket->state != ESWIFI_SOCKET_STATE_CONNECTED) + return -ENOTCONN; + + __select_socket(eswifi, socket->index); + k_delayed_work_cancel(&socket->read_work); + + __select_socket(eswifi, socket->index); + __stop_socket(eswifi, socket); + + return 0; +} + +int __eswifi_socket_new(struct eswifi_dev *eswifi, int family, int type, + int proto, void *context) +{ + struct eswifi_off_socket *socket = NULL; + int err, i; + + LOG_DBG(""); + + if (family != AF_INET) { + LOG_ERR("Only AF_INET is supported!"); + return -EPFNOSUPPORT; + } + + /* pickup available socket */ + for (i = 0; i < ESWIFI_OFFLOAD_MAX_SOCKETS; i++) { + if (!eswifi->socket[i].context) { + socket = &eswifi->socket[i]; + socket->index = i; + socket->context = context; + break; + } + } + + if (!socket) { + LOG_ERR("No socket resource available"); + return -ENOMEM; + } + + if (IS_ENABLED(CONFIG_NET_SOCKETS_SOCKOPT_TLS) && + proto >= IPPROTO_TLS_1_0 && proto <= IPPROTO_TLS_1_2) { + socket->type = ESWIFI_TRANSPORT_TCP_SSL; + } else if (proto == IPPROTO_TCP) { + socket->type = ESWIFI_TRANSPORT_TCP; + } else if (proto == IPPROTO_UDP) { + socket->type = ESWIFI_TRANSPORT_UDP; + } else { + LOG_ERR("Only TCP & UDP is supported"); + return -EPFNOSUPPORT; + } + + err = __select_socket(eswifi, socket->index); + if (err < 0) { + LOG_ERR("Unable to select socket %u", socket->index); + return -EIO; + } + + snprintf(eswifi->buf, sizeof(eswifi->buf), "P1=%d\r", socket->type); + err = eswifi_at_cmd(eswifi, eswifi->buf); + if (err < 0) { + LOG_ERR("Unable to set transport protocol"); + return -EIO; + } + + k_delayed_work_init(&socket->read_work, eswifi_off_read_work); + socket->usage = 1; + LOG_DBG("Socket index %d", socket->index); + + return socket->index; +} diff --git a/drivers/wifi/eswifi/eswifi_socket_offload.c b/drivers/wifi/eswifi/eswifi_socket_offload.c new file mode 100644 index 00000000000..4829b7b5f41 --- /dev/null +++ b/drivers/wifi/eswifi/eswifi_socket_offload.c @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2019 Linumiz + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +LOG_MODULE_REGISTER(wifi_eswifi, CONFIG_WIFI_LOG_LEVEL); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) +#include "tls_internal.h" +#endif +#include "eswifi.h" +#include + +static struct eswifi_dev *eswifi; + +static void __process_received(struct net_context *context, + struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, + int status, + void *user_data) +{ + struct eswifi_off_socket *socket = user_data; + + eswifi_lock(eswifi); + k_fifo_put(&socket->fifo, pkt); + eswifi_unlock(eswifi); +} + +static int eswifi_socket_connect(int sock, const struct sockaddr *addr, + socklen_t addrlen) +{ + struct eswifi_off_socket *socket; + int ret; + + if ((addrlen == 0) || (addr == NULL) || + (sock > ESWIFI_OFFLOAD_MAX_SOCKETS)) { + return -EINVAL; + } + + if (addr->sa_family != AF_INET) { + LOG_ERR("Only AF_INET is supported!"); + return -EPFNOSUPPORT; + } + + eswifi_lock(eswifi); + socket = &eswifi->socket[sock]; + + if (socket->state != ESWIFI_SOCKET_STATE_NONE) { + eswifi_unlock(eswifi); + return -EBUSY; + } + + socket->peer_addr = *addr; + socket->state = ESWIFI_SOCKET_STATE_CONNECTING; + + ret = __eswifi_off_start_client(eswifi, socket); + if (!ret) { + socket->state = ESWIFI_SOCKET_STATE_CONNECTED; + } else { + socket->state = ESWIFI_SOCKET_STATE_NONE; + } + + eswifi_unlock(eswifi); + return ret; +} + +static int eswifi_socket_accept(int sock, struct sockaddr *addr, + socklen_t *addrlen) +{ + struct eswifi_off_socket *socket; + int ret; + + if ((addrlen == NULL) || (addr == NULL) || + (sock > ESWIFI_OFFLOAD_MAX_SOCKETS)) { + return -EINVAL; + } + + eswifi_lock(eswifi); + socket = &eswifi->socket[sock]; + + ret = __eswifi_accept(eswifi, socket); + eswifi_unlock(eswifi); + + return ret; +} + +#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) +static int map_credentials(int sd, const void *optval, socklen_t optlen) +{ + sec_tag_t *sec_tags = (sec_tag_t *)optval; + int ret = 0; + int tags_len; + sec_tag_t tag; + int id; + int i, bytes; + struct tls_credential *cert; + + if ((optlen % sizeof(sec_tag_t)) != 0 || (optlen == 0)) { + return -EINVAL; + } + + tags_len = optlen / sizeof(sec_tag_t); + /* For each tag, retrieve the credentials value and type: */ + for (i = 0; i < tags_len; i++) { + tag = sec_tags[i]; + cert = credential_next_get(tag, NULL); + while (cert != NULL) { + /* Map Zephyr cert types to Simplelink cert options: */ + switch (cert->type) { + case TLS_CREDENTIAL_CA_CERTIFICATE: + id = 0; + break; + case TLS_CREDENTIAL_SERVER_CERTIFICATE: + id = 1; + break; + case TLS_CREDENTIAL_PRIVATE_KEY: + id = 2; + break; + case TLS_CREDENTIAL_NONE: + case TLS_CREDENTIAL_PSK: + case TLS_CREDENTIAL_PSK_ID: + default: + /* Not handled */ + return -EINVAL; + } + + snprintf(eswifi->buf, sizeof(eswifi->buf), + "PG=%d,%d,%d\r", 0, id, cert->len); + bytes = strlen(eswifi->buf); + memcpy(&eswifi->buf[bytes], cert->buf, cert->len); + bytes += cert->len; + LOG_DBG("cert write len %d\n", cert->len); + ret = eswifi_request(eswifi, eswifi->buf, bytes + 1, + eswifi->buf, sizeof(eswifi->buf)); + LOG_DBG("cert write err %d\n", ret); + if (ret < 0) { + return ret; + } + + snprintf(eswifi->buf, sizeof(eswifi->buf), "PF=0,0\r"); + ret = eswifi_at_cmd(eswifi, eswifi->buf); + if (ret < 0) { + return ret; + } + + cert = credential_next_get(tag, cert); + } + } + + return 0; +} +#else +static int map_credentials(int sd, const void *optval, socklen_t optlen) +{ + return 0; +} +#endif + +static int eswifi_socket_setsockopt(int sd, int level, int optname, + const void *optval, socklen_t optlen) +{ + int ret; + + if (IS_ENABLED(CONFIG_NET_SOCKETS_SOCKOPT_TLS) && level == SOL_TLS) { + switch (optname) { + case TLS_SEC_TAG_LIST: + ret = map_credentials(sd, optval, optlen); + break; + case TLS_HOSTNAME: + case TLS_PEER_VERIFY: + ret = 0; + break; + default: + return -EINVAL; + } + } else { + return -EINVAL; + } + + return ret; +} + +static ssize_t eswifi_socket_send(int sock, const void *buf, size_t len, + int flags) +{ + struct eswifi_off_socket *socket; + int ret; + int offset; + + if (!buf) { + return -EINVAL; + } + + eswifi_lock(eswifi); + socket = &eswifi->socket[sock]; + + if (socket->state != ESWIFI_SOCKET_STATE_CONNECTED) { + eswifi_unlock(eswifi); + return -ENOTCONN; + } + + __select_socket(eswifi, socket->index); + + /* header */ + snprintf(eswifi->buf, sizeof(eswifi->buf), "S3=%u\r", len); + offset = strlen(eswifi->buf); + + /* copy payload */ + memcpy(&eswifi->buf[offset], buf, len); + offset += len; + + ret = eswifi_request(eswifi, eswifi->buf, offset + 1, eswifi->buf, + sizeof(eswifi->buf)); + if (ret < 0) { + LOG_DBG("Unable to send data"); + ret = -EIO; + } else { + ret = len; + } + + eswifi_unlock(eswifi); + return ret; +} + +static ssize_t eswifi_socket_recv(int sock, void *buf, size_t max_len, + int flags) +{ + struct eswifi_off_socket *socket; + int len = 0, ret = 0; + struct net_pkt *pkt; + + if ((max_len == 0) || (buf == NULL) || + (sock > ESWIFI_OFFLOAD_MAX_SOCKETS)) { + return -EINVAL; + } + + eswifi_lock(eswifi); + socket = &eswifi->socket[sock]; + + if (socket->prev_pkt_rem) { + pkt = socket->prev_pkt_rem; + goto skip_wait; + } + + pkt = k_fifo_get(&socket->fifo, K_NO_WAIT); + if (!pkt) { + errno = EAGAIN; + len = -EAGAIN; + goto done; + } + +skip_wait: + len = net_pkt_remaining_data(pkt); + if (len > max_len) { + len = max_len; + socket->prev_pkt_rem = pkt; + } else { + socket->prev_pkt_rem = NULL; + } + + ret = net_pkt_read(pkt, buf, len); + + if (!socket->prev_pkt_rem) { + net_pkt_unref(pkt); + } + +done: + LOG_DBG("read %d %d %p", len, ret, pkt); + eswifi_unlock(eswifi); + if (ret) { + len = 0; + } + + return len; +} + +static int eswifi_socket_close(int sock) +{ + struct eswifi_off_socket *socket; + struct net_pkt *pkt; + int ret; + + if (sock > ESWIFI_OFFLOAD_MAX_SOCKETS) { + return -EINVAL; + } + + eswifi_lock(eswifi); + + socket = &eswifi->socket[sock]; + ret = __eswifi_socket_free(eswifi, socket); + if (ret) { + goto done; + } + + /* consume all net pkt */ + while (1) { + pkt = k_fifo_get(&socket->fifo, K_NO_WAIT); + if (!pkt) { + break; + } + net_pkt_unref(pkt); + } + + if (--socket->usage <= 0) { + memset(socket, 0, sizeof(*socket)); + } + +done: + eswifi_unlock(eswifi); + return ret; +} + +static int eswifi_socket_open(int family, int type, int proto) +{ + struct eswifi_off_socket *socket = NULL; + int idx; + + eswifi_lock(eswifi); + + /* Assign dummy context SOCkEt(50CE) */ + idx = __eswifi_socket_new(eswifi, family, type, proto, 0x50CE); + if (idx < 0) { + goto unlock; + } + + socket = &eswifi->socket[idx]; + k_fifo_init(&socket->fifo); + k_sem_init(&socket->read_sem, 0, 200); + socket->prev_pkt_rem = NULL; + socket->recv_cb = __process_received; + socket->recv_data = socket; + + k_delayed_work_submit_to_queue(&eswifi->work_q, &socket->read_work, + 500); + +unlock: + eswifi_unlock(eswifi); + return idx; +} + +static int eswifi_socket_poll(struct pollfd *fds, int nfds, int msecs) +{ + struct eswifi_off_socket *socket; + int sock, ret; + + if (nfds > 1 || + fds[0].fd > ESWIFI_OFFLOAD_MAX_SOCKETS) { + return -EINVAL; + } + + eswifi_lock(eswifi); + sock = fds[0].fd; + socket = &eswifi->socket[sock]; + eswifi_unlock(eswifi); + if (socket->state != ESWIFI_SOCKET_STATE_CONNECTED) { + return -EINVAL; + } + + ret = k_sem_take(&socket->read_sem, msecs); + return ret; +} + +static int ewifi_socket_bind(int sock, const struct sockaddr *addr, + socklen_t addrlen) +{ + struct eswifi_off_socket *socket; + int ret; + + if ((addrlen == NULL) || (addr == NULL) || + (sock > ESWIFI_OFFLOAD_MAX_SOCKETS)) { + return -EINVAL; + } + + eswifi_lock(eswifi); + socket = &eswifi->socket[sock]; + ret = __eswifi_bind(eswifi, socket, addr, addrlen); + eswifi_unlock(eswifi); + + return ret; +} + +const struct socket_offload eswifi_socket_ops = { + /* POSIX Socket Functions: */ + .socket = eswifi_socket_open, + .close = eswifi_socket_close, + .accept = eswifi_socket_accept, + .bind = ewifi_socket_bind, + .connect = eswifi_socket_connect, + .setsockopt = eswifi_socket_setsockopt, + .recv = eswifi_socket_recv, + .send = eswifi_socket_send, + .poll = eswifi_socket_poll, +}; + +int eswifi_socket_offload_init(struct eswifi_dev *leswifi) +{ + eswifi = leswifi; + socket_offload_register(&eswifi_socket_ops); + return 0; +}