| /* |
| * Copyright (c) 2019 Linumiz |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include "eswifi_log.h" |
| LOG_MODULE_DECLARE(LOG_MODULE_NAME); |
| |
| #include <zephyr/kernel.h> |
| #include <zephyr/device.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <errno.h> |
| |
| #include "eswifi.h" |
| #include <zephyr/net/net_pkt.h> |
| |
| int eswifi_socket_type_from_zephyr(int proto, enum eswifi_transport_type *type) |
| { |
| if (IS_ENABLED(CONFIG_NET_SOCKETS_SOCKOPT_TLS) && |
| proto >= IPPROTO_TLS_1_0 && proto <= IPPROTO_TLS_1_2) { |
| *type = ESWIFI_TRANSPORT_TCP_SSL; |
| } else if (proto == IPPROTO_TCP) { |
| *type = ESWIFI_TRANSPORT_TCP; |
| } else if (proto == IPPROTO_UDP) { |
| *type = ESWIFI_TRANSPORT_UDP; |
| } else { |
| return -EPFNOSUPPORT; |
| } |
| |
| return 0; |
| } |
| |
| 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 */ |
| snprintk(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 */ |
| snprintk(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 */ |
| snprintk(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; |
| } |
| |
| if (socket->type == ESWIFI_TRANSPORT_UDP) { |
| /* No listen or accept, so start UDP server now */ |
| snprintk(eswifi->buf, sizeof(eswifi->buf), "P5=1\r"); |
| err = eswifi_at_cmd(eswifi, eswifi->buf); |
| if (err < 0) { |
| LOG_ERR("Unable to start UDP server"); |
| 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 = NULL; |
| int next_timeout_ms = 100; |
| int err, len; |
| char *data; |
| struct k_work_delayable *dwork = k_work_delayable_from_work(work); |
| |
| LOG_DBG(""); |
| |
| socket = CONTAINER_OF(dwork, 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); |
| |
| /* Verify if we can allocate a rx packet before reading data to prevent leaks */ |
| pkt = net_pkt_rx_alloc_with_buffer(eswifi->iface, 1460, |
| AF_UNSPEC, 0, K_NO_WAIT); |
| if (!pkt) { |
| LOG_ERR("Cannot allocate rx packet"); |
| goto done; |
| } |
| |
| len = __read_data(eswifi, 1460, &data); /* 1460 is max size */ |
| if (len < 0) { |
| __stop_socket(eswifi, socket); |
| |
| if (socket->recv_cb) { |
| /* send EOF (null pkt) */ |
| net_pkt_unref(pkt); |
| pkt = NULL; |
| goto do_recv_cb; |
| } |
| } |
| |
| if (!len || !socket->recv_cb) { |
| net_pkt_unref(pkt); |
| goto done; |
| } |
| |
| LOG_DBG("payload sz = %d", len); |
| |
| if (net_pkt_write(pkt, data, len) < 0) { |
| LOG_WRN("Incomplete buffer copy"); |
| } |
| |
| /* Resize the packet */ |
| net_pkt_trim_buffer(pkt); |
| |
| net_pkt_cursor_init(pkt); |
| |
| do_recv_cb: |
| socket->recv_cb(socket->context, pkt, |
| NULL, NULL, 0, socket->recv_data); |
| |
| if (!socket->context) { |
| /* something destroyed the socket in the recv path */ |
| eswifi_unlock(eswifi); |
| return; |
| } |
| |
| k_sem_give(&socket->read_sem); |
| next_timeout_ms = 0; |
| |
| done: |
| err = k_work_reschedule_for_queue(&eswifi->work_q, &socket->read_work, |
| K_MSEC(next_timeout_ms)); |
| if (err < 0) { |
| 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); |
| |
| /* Stop any running client */ |
| snprintk(eswifi->buf, sizeof(eswifi->buf), "P6=0\r"); |
| err = eswifi_at_cmd(eswifi, eswifi->buf); |
| if (err < 0) { |
| LOG_ERR("Unable to stop running client"); |
| return -EIO; |
| } |
| |
| /* Stop any running server */ |
| snprintk(eswifi->buf, sizeof(eswifi->buf), "P5=0\r"); |
| err = eswifi_at_cmd(eswifi, eswifi->buf); |
| if (err < 0) { |
| LOG_ERR("Unable to stop running client"); |
| return -EIO; |
| } |
| |
| /* Clear local port */ |
| snprintk(eswifi->buf, sizeof(eswifi->buf), "P2=0\r"); |
| err = eswifi_at_cmd(eswifi, eswifi->buf); |
| if (err < 0) { |
| LOG_ERR("Unable to stop running client"); |
| return -EIO; |
| } |
| |
| /* Set Remote IP */ |
| snprintk(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 */ |
| snprintk(eswifi->buf, sizeof(eswifi->buf), "P4=%d\r", |
| (uint16_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 */ |
| snprintk(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; |
| } |
| |
| #if !defined(CONFIG_NET_SOCKETS_OFFLOAD) |
| net_context_set_state(socket->context, NET_CONTEXT_CONNECTED); |
| #endif |
| |
| return 0; |
| } |
| |
| int __eswifi_listen(struct eswifi_dev *eswifi, struct eswifi_off_socket *socket, int backlog) |
| { |
| int err; |
| |
| __select_socket(eswifi, socket->index); |
| |
| /* Set backlog */ |
| snprintk(eswifi->buf, sizeof(eswifi->buf), "P8=%d\r", backlog); |
| err = eswifi_at_cmd(eswifi, eswifi->buf); |
| if (err < 0) { |
| LOG_ERR("Unable to start set listen backlog"); |
| err = -EIO; |
| } |
| |
| socket->is_server = true; |
| |
| 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) |
| { |
| __select_socket(eswifi, socket->index); |
| k_work_cancel_delayable(&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; |
| } |
| |
| err = eswifi_socket_type_from_zephyr(proto, &socket->type); |
| if (err) { |
| LOG_ERR("Only TCP & UDP is supported"); |
| return err; |
| } |
| |
| err = __select_socket(eswifi, socket->index); |
| if (err < 0) { |
| LOG_ERR("Unable to select socket %u", socket->index); |
| return -EIO; |
| } |
| |
| snprintk(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_work_init_delayable(&socket->read_work, eswifi_off_read_work); |
| socket->usage = 1; |
| LOG_DBG("Socket index %d", socket->index); |
| |
| return socket->index; |
| } |