zephyr/drivers/wifi/eswifi/eswifi_socket.c
Loic Poulain c8f48681ed wifi: eswifi: Fix socket client initialization
It has been observed that sockets can be in bad state after
boot. Be sure to correctly reset local port and any 'server'
mode before configuring client mode.

Signed-off-by: Loic Poulain <loic.poulain@linaro.org>
2023-04-07 13:27:12 +02:00

365 lines
8.2 KiB
C

/*
* 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);
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) */
goto do_recv_cb;
}
}
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) < 0) {
LOG_WRN("Incomplete buffer copy");
}
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;
}