mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-09-03 07:21:56 +00:00
In the conversion of net_pkt_read_new to net_pkt_read, we missed changing the function in the eth_smsc911x and eswifi_offload. Signed-off-by: Kumar Gala <kumar.gala@linaro.org>
637 lines
13 KiB
C
637 lines
13 KiB
C
/**
|
|
* Copyright (c) 2018 Linaro
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
#define LOG_LEVEL CONFIG_WIFI_LOG_LEVEL
|
|
#include <logging/log.h>
|
|
LOG_MODULE_REGISTER(wifi_eswifi_offload);
|
|
|
|
#include <zephyr.h>
|
|
#include <kernel.h>
|
|
#include <device.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
|
|
#include <net/net_pkt.h>
|
|
#include <net/net_if.h>
|
|
|
|
#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 __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->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 || !socket->recv_cb) {
|
|
goto done;
|
|
}
|
|
|
|
LOG_ERR("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");
|
|
}
|
|
|
|
socket->recv_cb(socket->context, pkt,
|
|
NULL, NULL, 0, socket->user_data);
|
|
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,
|
|
socklen_t addrlen)
|
|
{
|
|
struct eswifi_off_socket *socket = context->offload_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);
|
|
|
|
/* Set Local Port */
|
|
snprintf(eswifi->buf, sizeof(eswifi->buf), "P2=%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 local port");
|
|
eswifi_unlock(eswifi);
|
|
return -EIO;
|
|
}
|
|
|
|
eswifi_unlock(eswifi);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int eswifi_off_listen(struct net_context *context, int backlog)
|
|
{
|
|
struct eswifi_off_socket *socket = context->offload_context;
|
|
struct eswifi_dev *eswifi = eswifi_by_iface_idx(context->iface);
|
|
char cmd[] = "P5=1\r";
|
|
int err;
|
|
|
|
/* TODO */
|
|
LOG_ERR("");
|
|
|
|
eswifi_lock(eswifi);
|
|
|
|
__select_socket(eswifi, socket->index);
|
|
|
|
/* Start TCP Server */
|
|
err = eswifi_at_cmd(eswifi, cmd);
|
|
if (err < 0) {
|
|
LOG_ERR("Unable to start TCP server");
|
|
}
|
|
|
|
eswifi_unlock(eswifi);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int __eswifi_off_connect(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 client */
|
|
snprintf(eswifi->buf, sizeof(eswifi->buf), "P6=1\r");
|
|
err = eswifi_at_cmd(eswifi, eswifi->buf);
|
|
if (err < 0) {
|
|
LOG_ERR("Unable to connect");
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void eswifi_off_connect_work(struct k_work *work)
|
|
{
|
|
struct eswifi_off_socket *socket;
|
|
net_context_connect_cb_t cb;
|
|
struct net_context *context;
|
|
struct eswifi_dev *eswifi;
|
|
void *user_data;
|
|
int err;
|
|
|
|
socket = CONTAINER_OF(work, struct eswifi_off_socket, connect_work);
|
|
eswifi = eswifi_socket_to_dev(socket);
|
|
|
|
eswifi_lock(eswifi);
|
|
|
|
cb = socket->conn_cb;
|
|
context = socket->context;
|
|
user_data = socket->user_data;
|
|
|
|
err = __eswifi_off_connect(eswifi, socket);
|
|
if (!err) {
|
|
socket->state = ESWIFI_SOCKET_STATE_CONNECTED;
|
|
} else {
|
|
socket->state = ESWIFI_SOCKET_STATE_NONE;
|
|
}
|
|
|
|
eswifi_unlock(eswifi);
|
|
|
|
if (cb) {
|
|
cb(context, err, user_data);
|
|
}
|
|
}
|
|
|
|
static int eswifi_off_connect(struct net_context *context,
|
|
const struct sockaddr *addr,
|
|
socklen_t addrlen,
|
|
net_context_connect_cb_t cb,
|
|
s32_t timeout,
|
|
void *user_data)
|
|
{
|
|
struct eswifi_off_socket *socket = context->offload_context;
|
|
struct eswifi_dev *eswifi = eswifi_by_iface_idx(context->iface);
|
|
int err;
|
|
|
|
LOG_DBG("timeout=%d", timeout);
|
|
|
|
if (addr->sa_family != AF_INET) {
|
|
LOG_ERR("Only AF_INET is supported!");
|
|
return -EPFNOSUPPORT;
|
|
}
|
|
|
|
eswifi_lock(eswifi);
|
|
|
|
if (socket->state != ESWIFI_SOCKET_STATE_NONE) {
|
|
eswifi_unlock(eswifi);
|
|
return -EBUSY;
|
|
}
|
|
|
|
socket->peer_addr = *addr;
|
|
socket->user_data = user_data;
|
|
socket->state = ESWIFI_SOCKET_STATE_CONNECTING;
|
|
|
|
if (timeout == K_NO_WAIT) {
|
|
/* async */
|
|
k_work_submit_to_queue(&eswifi->work_q, &socket->connect_work);
|
|
eswifi_unlock(eswifi);
|
|
return 0;
|
|
}
|
|
|
|
err = __eswifi_off_connect(eswifi, socket);
|
|
if (!err) {
|
|
socket->state = ESWIFI_SOCKET_STATE_CONNECTED;
|
|
} else {
|
|
socket->state = ESWIFI_SOCKET_STATE_NONE;
|
|
}
|
|
|
|
eswifi_unlock(eswifi);
|
|
|
|
if (cb) {
|
|
cb(context, err, user_data);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int eswifi_off_accept(struct net_context *context,
|
|
net_tcp_accept_cb_t cb, s32_t timeout,
|
|
void *user_data)
|
|
{
|
|
/* TODO */
|
|
LOG_DBG("");
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
static int __eswifi_off_send_pkt(struct eswifi_dev *eswifi,
|
|
struct eswifi_off_socket *socket)
|
|
{
|
|
struct net_pkt *pkt = socket->tx_pkt;
|
|
unsigned int bytes;
|
|
int err, offset;
|
|
|
|
LOG_DBG("");
|
|
|
|
if (!pkt) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
bytes = net_pkt_get_len(pkt);
|
|
|
|
__select_socket(eswifi, socket->index);
|
|
|
|
/* header */
|
|
snprintf(eswifi->buf, sizeof(eswifi->buf), "S3=%u\r", bytes);
|
|
offset = strlen(eswifi->buf);
|
|
|
|
/* copy payload */
|
|
if (net_pkt_read(pkt, &eswifi->buf[offset], bytes)) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
offset += bytes;
|
|
|
|
err = eswifi_request(eswifi, eswifi->buf, offset + 1,
|
|
eswifi->buf, sizeof(eswifi->buf));
|
|
if (err < 0) {
|
|
LOG_ERR("Unable to send data");
|
|
return -EIO;
|
|
}
|
|
|
|
net_pkt_unref(pkt);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void eswifi_off_send_work(struct k_work *work)
|
|
{
|
|
struct eswifi_off_socket *socket;
|
|
net_context_send_cb_t cb;
|
|
struct net_context *context;
|
|
struct eswifi_dev *eswifi;
|
|
void *user_data;
|
|
int err;
|
|
|
|
socket = CONTAINER_OF(work, struct eswifi_off_socket, connect_work);
|
|
eswifi = eswifi_socket_to_dev(socket);
|
|
|
|
eswifi_lock(eswifi);
|
|
|
|
user_data = socket->user_data;
|
|
cb = socket->send_cb;
|
|
context = socket->context;
|
|
|
|
err = __eswifi_off_send_pkt(eswifi, socket);
|
|
socket->tx_pkt = NULL;
|
|
|
|
eswifi_unlock(eswifi);
|
|
|
|
if (cb) {
|
|
cb(context, err, user_data);
|
|
}
|
|
}
|
|
|
|
static int eswifi_off_send(struct net_pkt *pkt,
|
|
net_context_send_cb_t cb,
|
|
s32_t timeout,
|
|
void *user_data)
|
|
{
|
|
struct eswifi_off_socket *socket = pkt->context->offload_context;
|
|
struct eswifi_dev *eswifi = eswifi_by_iface_idx(socket->context->iface);
|
|
int err;
|
|
|
|
LOG_DBG("timeout=%d", timeout);
|
|
|
|
eswifi_lock(eswifi);
|
|
|
|
if (socket->state != ESWIFI_SOCKET_STATE_CONNECTED) {
|
|
eswifi_unlock(eswifi);
|
|
return -ENOTCONN;
|
|
}
|
|
|
|
if (socket->tx_pkt) {
|
|
eswifi_unlock(eswifi);
|
|
return -EBUSY;
|
|
}
|
|
socket->tx_pkt = pkt;
|
|
|
|
if (timeout == K_NO_WAIT) {
|
|
socket->user_data = user_data;
|
|
socket->send_cb = cb;
|
|
|
|
k_work_submit_to_queue(&eswifi->work_q, &socket->send_work);
|
|
|
|
eswifi_unlock(eswifi);
|
|
|
|
return 0;
|
|
}
|
|
|
|
err = __eswifi_off_send_pkt(eswifi, socket);
|
|
socket->tx_pkt = NULL;
|
|
|
|
eswifi_unlock(eswifi);
|
|
|
|
if (cb) {
|
|
cb(socket->context, err, user_data);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int eswifi_off_sendto(struct net_pkt *pkt,
|
|
const struct sockaddr *dst_addr,
|
|
socklen_t addrlen,
|
|
net_context_send_cb_t cb,
|
|
s32_t timeout,
|
|
void *user_data)
|
|
{
|
|
/* TODO */
|
|
LOG_DBG("");
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
static int eswifi_off_recv(struct net_context *context,
|
|
net_context_recv_cb_t cb,
|
|
s32_t timeout,
|
|
void *user_data)
|
|
{
|
|
struct eswifi_off_socket *socket = context->offload_context;
|
|
struct eswifi_dev *eswifi = eswifi_by_iface_idx(context->iface);
|
|
int err;
|
|
|
|
|
|
LOG_DBG("");
|
|
|
|
eswifi_lock(eswifi);
|
|
socket->recv_cb = cb;
|
|
socket->user_data = user_data;
|
|
k_sem_reset(&socket->read_sem);
|
|
eswifi_unlock(eswifi);
|
|
|
|
if (timeout == K_NO_WAIT)
|
|
return 0;
|
|
|
|
err = k_sem_take(&socket->read_sem, timeout);
|
|
|
|
/* Unregister cakkback */
|
|
eswifi_lock(eswifi);
|
|
socket->recv_cb = NULL;
|
|
eswifi_unlock(eswifi);
|
|
|
|
return err;
|
|
}
|
|
|
|
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 err;
|
|
|
|
LOG_DBG("");
|
|
|
|
eswifi_lock(eswifi);
|
|
|
|
if (socket->state != ESWIFI_SOCKET_STATE_CONNECTED) {
|
|
eswifi_unlock(eswifi);
|
|
return -ENOTCONN;
|
|
}
|
|
|
|
__select_socket(eswifi, socket->index);
|
|
|
|
k_delayed_work_cancel(&socket->read_work);
|
|
|
|
socket->context = NULL;
|
|
socket->state = ESWIFI_SOCKET_STATE_NONE;
|
|
|
|
/* Stop TCP client */
|
|
snprintf(eswifi->buf, sizeof(eswifi->buf), "P6=0\r");
|
|
err = eswifi_at_cmd(eswifi, eswifi->buf);
|
|
if (err < 0) {
|
|
err = -EIO;
|
|
LOG_ERR("Unable to disconnect");
|
|
}
|
|
|
|
eswifi_unlock(eswifi);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int eswifi_off_get(sa_family_t family,
|
|
enum net_sock_type type,
|
|
enum net_ip_protocol ip_proto,
|
|
struct net_context **context)
|
|
{
|
|
struct eswifi_dev *eswifi = eswifi_by_iface_idx((*context)->iface);
|
|
struct eswifi_off_socket *socket = NULL;
|
|
int err, i;
|
|
|
|
LOG_DBG("");
|
|
|
|
if (family != AF_INET) {
|
|
LOG_ERR("Only AF_INET is supported!");
|
|
return -EPFNOSUPPORT;
|
|
}
|
|
|
|
if (ip_proto != IPPROTO_TCP) {
|
|
/* TODO: add UDP */
|
|
LOG_ERR("Only TCP supported");
|
|
return -EPROTONOSUPPORT;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (!socket) {
|
|
LOG_ERR("No socket resource available");
|
|
eswifi_unlock(eswifi);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
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);
|
|
|
|
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 */
|
|
snprintf(eswifi->buf, sizeof(eswifi->buf), "P1=%d\r",
|
|
ESWIFI_TRANSPORT_TCP);
|
|
err = eswifi_at_cmd(eswifi, eswifi->buf);
|
|
if (err < 0) {
|
|
LOG_ERR("Unable to set transport protocol");
|
|
eswifi_unlock(eswifi);
|
|
return -EIO;
|
|
}
|
|
|
|
k_delayed_work_submit_to_queue(&eswifi->work_q, &socket->read_work,
|
|
500);
|
|
|
|
eswifi_unlock(eswifi);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct net_offload eswifi_offload = {
|
|
.get = eswifi_off_get,
|
|
.bind = eswifi_off_bind,
|
|
.listen = eswifi_off_listen,
|
|
.connect = eswifi_off_connect,
|
|
.accept = eswifi_off_accept,
|
|
.send = eswifi_off_send,
|
|
.sendto = eswifi_off_sendto,
|
|
.recv = eswifi_off_recv,
|
|
.put = eswifi_off_put,
|
|
};
|
|
|
|
static int eswifi_off_enable_dhcp(struct eswifi_dev *eswifi)
|
|
{
|
|
char cmd[] = "C4=1\r";
|
|
int err;
|
|
|
|
LOG_DBG("");
|
|
|
|
eswifi_lock(eswifi);
|
|
|
|
err = eswifi_at_cmd(eswifi, cmd);
|
|
|
|
eswifi_unlock(eswifi);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int eswifi_off_disable_bypass(struct eswifi_dev *eswifi)
|
|
{
|
|
char cmd[] = "PR=0\r";
|
|
int err;
|
|
|
|
LOG_DBG("");
|
|
|
|
eswifi_lock(eswifi);
|
|
|
|
err = eswifi_at_cmd(eswifi, cmd);
|
|
|
|
eswifi_unlock(eswifi);
|
|
|
|
return err;
|
|
}
|
|
|
|
int eswifi_offload_init(struct eswifi_dev *eswifi)
|
|
{
|
|
eswifi->iface->if_dev->offload = &eswifi_offload;
|
|
int err;
|
|
|
|
err = eswifi_off_enable_dhcp(eswifi);
|
|
if (err < 0) {
|
|
LOG_ERR("Unable to configure dhcp");
|
|
return err;
|
|
}
|
|
|
|
err = eswifi_off_disable_bypass(eswifi);
|
|
if (err < 0) {
|
|
LOG_ERR("Unable to disable bypass mode");
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|