drivers: modem: introduce socket helper layer
Many modems implement socket-based APIs to manage data connections. This layer provides much of the groundwork for keeping track of these "sockets" throughout their lifecycle (from the initial offload API calls through the command handler call back layers): - structure for holding socket data like IP protocol, destination, source and incoming packet sizes - configuration to note modem starting socket id and number of sockets - methods to get/put socket structs from/to the pool - function to update the # and size of packets in the modem receive queue - prebuilt modem_socket_poll() method for socket offload poll() API Example modem driver setup code looks like this: /* socket data */ static struct modem_socket_config socket_config; static struct modem_socket sockets[MDM_MAX_SOCKETS]; static int modem_init(struct device *dev) { ... /* setup socket config */ socket_config.sockets = &sockets[0]; socket_config.sockets_len = ARRAY_SIZE(sockets); socket_config.base_socket_num = 0; ret = modem_socket_init(&socket_config); ... } Signed-off-by: Michael Scott <mike@foundries.io>
This commit is contained in:
parent
02abddccd6
commit
f5c45c2946
4 changed files with 396 additions and 0 deletions
|
@ -10,6 +10,7 @@ zephyr_sources_ifdef(CONFIG_MODEM_CONTEXT
|
|||
|
||||
zephyr_sources_ifdef(CONFIG_MODEM_IFACE_UART modem_iface_uart.c)
|
||||
zephyr_sources_ifdef(CONFIG_MODEM_CMD_HANDLER modem_cmd_handler.c)
|
||||
zephyr_sources_ifdef(CONFIG_MODEM_SOCKET modem_socket.c)
|
||||
|
||||
if(CONFIG_MODEM_UBLOX_SARA_R4)
|
||||
zephyr_library_include_directories(${ZEPHYR_BASE}/subsys/net/ip)
|
||||
|
|
|
@ -98,6 +98,25 @@ config MODEM_CMD_HANDLER_MAX_PARAM_COUNT
|
|||
of the match_buf (match_buf_len) field as it needs to be large
|
||||
enough to hold a single line of data (ending with /r).
|
||||
|
||||
config MODEM_SOCKET
|
||||
bool "Generic modem socket support layer"
|
||||
help
|
||||
This layer provides much of the groundwork for keeping track of
|
||||
modem "sockets" throughout their lifecycle (from the initial offload
|
||||
API calls through the command handler call back layers).
|
||||
To configure this layer for use, create a modem_socket_config
|
||||
object with your socket data and pass it's reference to
|
||||
modem_socket_init().
|
||||
|
||||
config MODEM_SOCKET_PACKET_COUNT
|
||||
int "Maximum number of stored packet sizes per socket"
|
||||
depends on MODEM_SOCKET
|
||||
default 6
|
||||
help
|
||||
As the modem indicates more data is available to be received,
|
||||
these values are organized into "packets". This setting limits
|
||||
the maximum number of packet sizes the socket can keep track of.
|
||||
|
||||
endif # MODEM_CONTEXT
|
||||
|
||||
config MODEM_SHELL
|
||||
|
|
302
drivers/modem/modem_socket.c
Normal file
302
drivers/modem/modem_socket.c
Normal file
|
@ -0,0 +1,302 @@
|
|||
/** @file
|
||||
* @brief Modem socket / packet size handler
|
||||
*
|
||||
* Generic modem socket and packet size implementation for modem context
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2019 Foundries.io
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <kernel.h>
|
||||
#include <net/socket_offload.h>
|
||||
#include <sys/fdtable.h>
|
||||
|
||||
#include "modem_socket.h"
|
||||
|
||||
/*
|
||||
* Packet Size Support Functions
|
||||
*/
|
||||
|
||||
static u16_t modem_socket_packet_get_total(struct modem_socket *sock)
|
||||
{
|
||||
int i;
|
||||
u16_t total = 0U;
|
||||
|
||||
if (!sock || !sock->packet_count) {
|
||||
return 0U;
|
||||
}
|
||||
|
||||
for (i = 0; i < sock->packet_count; i++) {
|
||||
total += sock->packet_sizes[i];
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
static int modem_socket_packet_drop_first(struct modem_socket *sock)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!sock || !sock->packet_count) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sock->packet_count--;
|
||||
for (i = 0; i < sock->packet_count; i++) {
|
||||
sock->packet_sizes[i] =
|
||||
sock->packet_sizes[i + 1];
|
||||
}
|
||||
|
||||
sock->packet_sizes[sock->packet_count] = 0U;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int modem_socket_packet_size_update(struct modem_socket_config *cfg,
|
||||
struct modem_socket *sock, int new_total)
|
||||
{
|
||||
u16_t old_total = 0U;
|
||||
|
||||
if (!sock) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (new_total < 0) {
|
||||
new_total += modem_socket_packet_get_total(sock);
|
||||
}
|
||||
|
||||
if (new_total <= 0) {
|
||||
/* reset outstanding value here */
|
||||
sock->packet_count = 0U;
|
||||
sock->packet_sizes[0] = 0U;
|
||||
return 0;
|
||||
}
|
||||
|
||||
old_total = modem_socket_packet_get_total(sock);
|
||||
if (new_total == old_total) {
|
||||
goto data_ready;
|
||||
}
|
||||
|
||||
/* remove sent packets */
|
||||
if (new_total < old_total) {
|
||||
/* remove packets that are not included in new_size */
|
||||
while (old_total > new_total && sock->packet_count > 0) {
|
||||
old_total -= sock->packet_sizes[0];
|
||||
modem_socket_packet_drop_first(sock);
|
||||
}
|
||||
|
||||
goto data_ready;
|
||||
}
|
||||
|
||||
/* new packet to add */
|
||||
if (sock->packet_count >= CONFIG_MODEM_SOCKET_PACKET_COUNT) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (new_total - old_total > 0) {
|
||||
sock->packet_sizes[sock->packet_count] = new_total - old_total;
|
||||
sock->packet_count++;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data_ready:
|
||||
return new_total;
|
||||
}
|
||||
|
||||
/*
|
||||
* VTable OPS
|
||||
*/
|
||||
|
||||
static ssize_t modem_socket_read_op(void *obj, void *buf, size_t sz)
|
||||
{
|
||||
/* TODO: NOT IMPLEMENTED */
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static ssize_t modem_socket_write_op(void *obj, const void *buf, size_t sz)
|
||||
{
|
||||
/* TODO: NOT IMPLEMENTED */
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static int modem_socket_ioctl_op(void *obj, unsigned int request, va_list args)
|
||||
{
|
||||
/* TODO: NOT IMPLEMENTED */
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static const struct fd_op_vtable modem_sock_fd_vtable = {
|
||||
.read = modem_socket_read_op,
|
||||
.write = modem_socket_write_op,
|
||||
.ioctl = modem_socket_ioctl_op,
|
||||
};
|
||||
|
||||
/*
|
||||
* Socket Support Functions
|
||||
*/
|
||||
|
||||
int modem_socket_get(struct modem_socket_config *cfg,
|
||||
int family, int type, int proto)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cfg->sockets_len; i++) {
|
||||
if (cfg->sockets[i].id < cfg->base_socket_num) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= cfg->sockets_len) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* FIXME: 4 fds max now due to POSIX_OS conflict */
|
||||
cfg->sockets[i].sock_fd = z_reserve_fd();
|
||||
if (cfg->sockets[i].sock_fd < 0) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
cfg->sockets[i].family = family;
|
||||
cfg->sockets[i].type = type;
|
||||
cfg->sockets[i].ip_proto = proto;
|
||||
/* socket # needs assigning */
|
||||
cfg->sockets[i].id = cfg->sockets_len + 1;
|
||||
z_finalize_fd(cfg->sockets[i].sock_fd, cfg, &modem_sock_fd_vtable);
|
||||
|
||||
return cfg->sockets[i].sock_fd;
|
||||
}
|
||||
|
||||
struct modem_socket *modem_socket_from_fd(struct modem_socket_config *cfg,
|
||||
int sock_fd)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cfg->sockets_len; i++) {
|
||||
if (cfg->sockets[i].sock_fd == sock_fd) {
|
||||
return &cfg->sockets[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct modem_socket *modem_socket_from_id(struct modem_socket_config *cfg,
|
||||
int id)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (id < cfg->base_socket_num) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < cfg->sockets_len; i++) {
|
||||
if (cfg->sockets[i].id == id) {
|
||||
return &cfg->sockets[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct modem_socket *modem_socket_from_newid(struct modem_socket_config *cfg)
|
||||
{
|
||||
return modem_socket_from_id(cfg, cfg->sockets_len + 1);
|
||||
}
|
||||
|
||||
void modem_socket_put(struct modem_socket_config *cfg, int sock_fd)
|
||||
{
|
||||
if (sock_fd < 0 || sock_fd >= cfg->sockets_len) {
|
||||
return;
|
||||
}
|
||||
|
||||
z_free_fd(sock_fd);
|
||||
cfg->sockets[sock_fd].id = cfg->base_socket_num - 1;
|
||||
cfg->sockets[sock_fd].sock_fd = -1;
|
||||
(void)memset(&cfg->sockets[sock_fd].src, 0, sizeof(struct sockaddr));
|
||||
(void)memset(&cfg->sockets[sock_fd].dst, 0, sizeof(struct sockaddr));
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic Poll Function
|
||||
*/
|
||||
|
||||
/*
|
||||
* FIXME: The design here makes the poll function non-reentrant. If two
|
||||
* different threads poll on two different sockets we'll end up with unexpected
|
||||
* behavior - the higher priority thread will be unblocked, regardless on which
|
||||
* socket it polled. I think we could live with such limitation though in the
|
||||
* initial implementation, but this should be improved in the future.
|
||||
*/
|
||||
int modem_socket_poll(struct modem_socket_config *cfg,
|
||||
struct pollfd *fds, int nfds, int msecs)
|
||||
{
|
||||
struct modem_socket *sock;
|
||||
int ret, i;
|
||||
u8_t found_count = 0;
|
||||
|
||||
if (!cfg) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < nfds; i++) {
|
||||
sock = modem_socket_from_fd(cfg, fds[i].fd);
|
||||
if (sock) {
|
||||
/*
|
||||
* Handle user check for POLLOUT events:
|
||||
* we consider the socket to always be writeable.
|
||||
*/
|
||||
if (fds[i].events & POLLOUT) {
|
||||
fds[i].revents |= POLLOUT;
|
||||
found_count++;
|
||||
} else if (fds[i].events & POLLIN) {
|
||||
sock->is_polled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* exit early if we've found rdy sockets */
|
||||
if (found_count) {
|
||||
errno = 0;
|
||||
return found_count;
|
||||
}
|
||||
|
||||
ret = k_sem_take(&cfg->sem_poll, msecs);
|
||||
for (i = 0; i < nfds; i++) {
|
||||
sock = modem_socket_from_fd(cfg, fds[i].fd);
|
||||
if (!sock) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fds[i].events & POLLIN && sock->packet_sizes[0] > 0U) {
|
||||
fds[i].revents |= POLLIN;
|
||||
found_count++;
|
||||
}
|
||||
|
||||
sock->is_polled = false;
|
||||
}
|
||||
|
||||
/* EBUSY, EAGAIN and ETIMEDOUT aren't true errors */
|
||||
if (ret < 0 && ret != -EBUSY && ret != -EAGAIN && ret != -ETIMEDOUT) {
|
||||
errno = ret;
|
||||
return -1;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
return found_count;
|
||||
}
|
||||
|
||||
int modem_socket_init(struct modem_socket_config *cfg)
|
||||
{
|
||||
int i;
|
||||
|
||||
k_sem_init(&cfg->sem_poll, 0, 1);
|
||||
for (i = 0; i < cfg->sockets_len; i++) {
|
||||
k_sem_init(&cfg->sockets[i].sem_data_ready, 0, 1);
|
||||
cfg->sockets[i].id = cfg->base_socket_num - 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
74
drivers/modem/modem_socket.h
Normal file
74
drivers/modem/modem_socket.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
/** @file
|
||||
* @brief Modem socket header file.
|
||||
*
|
||||
* Generic modem socket and packet size implementation for modem context
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2019 Foundries.io
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_DRIVERS_MODEM_MODEM_SOCKET_H_
|
||||
#define ZEPHYR_INCLUDE_DRIVERS_MODEM_MODEM_SOCKET_H_
|
||||
|
||||
#include <kernel.h>
|
||||
#include <net/net_ip.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct modem_socket {
|
||||
sa_family_t family;
|
||||
enum net_sock_type type;
|
||||
enum net_ip_protocol ip_proto;
|
||||
struct sockaddr src;
|
||||
struct sockaddr dst;
|
||||
int id;
|
||||
int sock_fd;
|
||||
|
||||
/** packet data */
|
||||
u16_t packet_sizes[CONFIG_MODEM_SOCKET_PACKET_COUNT];
|
||||
u16_t packet_count;
|
||||
|
||||
/** data ready semaphore */
|
||||
struct k_sem sem_data_ready;
|
||||
|
||||
/** socket state */
|
||||
bool is_polled;
|
||||
|
||||
/** temporary socket data */
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct modem_socket_config {
|
||||
struct modem_socket *sockets;
|
||||
size_t sockets_len;
|
||||
|
||||
/* beginning socket id (modems can set this to 0 or 1 as needed) */
|
||||
int base_socket_num;
|
||||
struct k_sem sem_poll;
|
||||
};
|
||||
|
||||
/* return total size of remaining packets */
|
||||
int modem_socket_packet_size_update(struct modem_socket_config *cfg,
|
||||
struct modem_socket *sock, int new_total);
|
||||
int modem_socket_get(struct modem_socket_config *cfg, int family, int type,
|
||||
int proto);
|
||||
struct modem_socket *modem_socket_from_fd(struct modem_socket_config *cfg,
|
||||
int sock_fd);
|
||||
struct modem_socket *modem_socket_from_id(struct modem_socket_config *cfg,
|
||||
int id);
|
||||
struct modem_socket *modem_socket_from_newid(struct modem_socket_config *cfg);
|
||||
void modem_socket_put(struct modem_socket_config *cfg, int sock_fd);
|
||||
int modem_socket_poll(struct modem_socket_config *cfg,
|
||||
struct pollfd *fds, int nfds, int msecs);
|
||||
int modem_socket_init(struct modem_socket_config *cfg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_DRIVERS_MODEM_MODEM_SOCKET_H_ */
|
Loading…
Add table
Add a link
Reference in a new issue