subsys/modem: Add modem modules
This PR adds the following modem modules to the subsys/modem folder: - chat: Light implementation of the Linux chat program, used to send and receive text based commands statically created scripts. - cmux: Implementation of the CMUX protocol - pipe: Thread-safe async data-in/data-out binding layer between modem modules. - ppp: Implementation of the PPP protocol, binding the Zephyr PPP L2 stack with the data-in/data-out pipe. These modules use the abstract pipes to communicate between each other. To bind them with the hardware, the following backends are provided: - TTY: modem pipe <-> POSIX TTY file - UART: modem pipe <-> UART, async and ISR APIs supported The backends are used to abstract away the physical layer, UART, TTY, IPC, I2C, SPI etc, to a modem modules friendly pipe. Signed-off-by: Bjarki Arge Andreasen <baa@trackunit.com>
This commit is contained in:
parent
b7a172fa1a
commit
b4cf54b8c3
45
include/zephyr/modem/backend/tty.h
Normal file
45
include/zephyr/modem/backend/tty.h
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Trackunit Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/types.h>
|
||||||
|
#include <zephyr/device.h>
|
||||||
|
#include <zephyr/sys/ring_buffer.h>
|
||||||
|
#include <zephyr/sys/atomic.h>
|
||||||
|
|
||||||
|
#include <zephyr/modem/pipe.h>
|
||||||
|
|
||||||
|
#ifndef ZEPHYR_MODEM_BACKEND_TTY_
|
||||||
|
#define ZEPHYR_MODEM_BACKEND_TTY_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct modem_backend_tty {
|
||||||
|
const char *tty_path;
|
||||||
|
int tty_fd;
|
||||||
|
struct modem_pipe pipe;
|
||||||
|
struct k_thread thread;
|
||||||
|
k_thread_stack_t *stack;
|
||||||
|
size_t stack_size;
|
||||||
|
atomic_t state;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct modem_backend_tty_config {
|
||||||
|
const char *tty_path;
|
||||||
|
k_thread_stack_t *stack;
|
||||||
|
size_t stack_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct modem_pipe *modem_backend_tty_init(struct modem_backend_tty *backend,
|
||||||
|
const struct modem_backend_tty_config *config);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* ZEPHYR_MODEM_BACKEND_TTY_ */
|
66
include/zephyr/modem/backend/uart.h
Normal file
66
include/zephyr/modem/backend/uart.h
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Trackunit Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/types.h>
|
||||||
|
#include <zephyr/device.h>
|
||||||
|
#include <zephyr/drivers/uart.h>
|
||||||
|
#include <zephyr/sys/ring_buffer.h>
|
||||||
|
#include <zephyr/sys/atomic.h>
|
||||||
|
|
||||||
|
#include <zephyr/modem/pipe.h>
|
||||||
|
|
||||||
|
#ifndef ZEPHYR_MODEM_BACKEND_UART_
|
||||||
|
#define ZEPHYR_MODEM_BACKEND_UART_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct modem_backend_uart_isr {
|
||||||
|
struct ring_buf receive_rdb[2];
|
||||||
|
struct ring_buf transmit_rb;
|
||||||
|
atomic_t transmit_buf_len;
|
||||||
|
uint8_t receive_rdb_used;
|
||||||
|
uint32_t transmit_buf_put_limit;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct modem_backend_uart_async {
|
||||||
|
uint8_t *receive_bufs[2];
|
||||||
|
uint32_t receive_buf_size;
|
||||||
|
struct ring_buf receive_rdb[2];
|
||||||
|
uint8_t *transmit_buf;
|
||||||
|
uint32_t transmit_buf_size;
|
||||||
|
atomic_t state;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct modem_backend_uart {
|
||||||
|
const struct device *uart;
|
||||||
|
struct modem_pipe pipe;
|
||||||
|
struct k_work receive_ready_work;
|
||||||
|
|
||||||
|
union {
|
||||||
|
struct modem_backend_uart_isr isr;
|
||||||
|
struct modem_backend_uart_async async;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct modem_backend_uart_config {
|
||||||
|
const struct device *uart;
|
||||||
|
uint8_t *receive_buf;
|
||||||
|
uint32_t receive_buf_size;
|
||||||
|
uint8_t *transmit_buf;
|
||||||
|
uint32_t transmit_buf_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct modem_pipe *modem_backend_uart_init(struct modem_backend_uart *backend,
|
||||||
|
const struct modem_backend_uart_config *config);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* ZEPHYR_MODEM_BACKEND_UART_ */
|
310
include/zephyr/modem/chat.h
Normal file
310
include/zephyr/modem/chat.h
Normal file
|
@ -0,0 +1,310 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Trackunit Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/types.h>
|
||||||
|
#include <zephyr/device.h>
|
||||||
|
#include <zephyr/sys/ring_buffer.h>
|
||||||
|
|
||||||
|
#include <zephyr/modem/pipe.h>
|
||||||
|
|
||||||
|
#ifndef ZEPHYR_MODEM_CHAT_
|
||||||
|
#define ZEPHYR_MODEM_CHAT_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct modem_chat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Callback called when matching chat is received
|
||||||
|
*
|
||||||
|
* @param chat Pointer to chat instance instance
|
||||||
|
* @param argv Pointer to array of parsed arguments
|
||||||
|
* @param argc Number of parsed arguments, arg 0 holds the exact match
|
||||||
|
* @param user_data Free to use user data set during modem_chat_init()
|
||||||
|
*/
|
||||||
|
typedef void (*modem_chat_match_callback)(struct modem_chat *chat, char **argv, uint16_t argc,
|
||||||
|
void *user_data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Modem chat match
|
||||||
|
*/
|
||||||
|
struct modem_chat_match {
|
||||||
|
/* Match array */
|
||||||
|
const uint8_t *match;
|
||||||
|
const uint8_t match_size;
|
||||||
|
|
||||||
|
/* Separators array */
|
||||||
|
const uint8_t *separators;
|
||||||
|
const uint8_t separators_size;
|
||||||
|
|
||||||
|
/* Set if modem chat instance shall use wildcards when matching */
|
||||||
|
const bool wildcards;
|
||||||
|
|
||||||
|
/* Type of modem chat instance */
|
||||||
|
const modem_chat_match_callback callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MODEM_CHAT_MATCH(_match, _separators, _callback) \
|
||||||
|
{ \
|
||||||
|
.match = (uint8_t *)(_match), .match_size = (uint8_t)(sizeof(_match) - 1), \
|
||||||
|
.separators = (uint8_t *)(_separators), \
|
||||||
|
.separators_size = (uint8_t)(sizeof(_separators) - 1), .wildcards = false, \
|
||||||
|
.callback = _callback, \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MODEM_CHAT_MATCH_WILDCARD(_match, _separators, _callback) \
|
||||||
|
{ \
|
||||||
|
.match = (uint8_t *)(_match), .match_size = (uint8_t)(sizeof(_match) - 1), \
|
||||||
|
.separators = (uint8_t *)(_separators), \
|
||||||
|
.separators_size = (uint8_t)(sizeof(_separators) - 1), .wildcards = true, \
|
||||||
|
.callback = _callback, \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MODEM_CHAT_MATCH_DEFINE(_sym, _match, _separators, _callback) \
|
||||||
|
const static struct modem_chat_match _sym = MODEM_CHAT_MATCH(_match, _separators, _callback)
|
||||||
|
|
||||||
|
#define MODEM_CHAT_MATCHES_DEFINE(_sym, ...) \
|
||||||
|
const static struct modem_chat_match _sym[] = {__VA_ARGS__}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Modem chat script chat
|
||||||
|
*/
|
||||||
|
struct modem_chat_script_chat {
|
||||||
|
/** Request to send to modem formatted as char string */
|
||||||
|
const char *request;
|
||||||
|
/** Expected responses to request */
|
||||||
|
const struct modem_chat_match *const response_matches;
|
||||||
|
/** Number of elements in expected responses */
|
||||||
|
const uint16_t response_matches_size;
|
||||||
|
/** Timeout before chat script may continue to next step in milliseconds */
|
||||||
|
uint16_t timeout;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MODEM_CHAT_SCRIPT_CMD_RESP(_request, _response_match) \
|
||||||
|
{ \
|
||||||
|
.request = _request, .response_matches = &_response_match, \
|
||||||
|
.response_matches_size = 1, .timeout = 0, \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MODEM_CHAT_SCRIPT_CMD_RESP_MULT(_request, _response_matches) \
|
||||||
|
{ \
|
||||||
|
.request = _request, .response_matches = _response_matches, \
|
||||||
|
.response_matches_size = ARRAY_SIZE(_response_matches), .timeout = 0, \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MODEM_CHAT_SCRIPT_CMD_RESP_NONE(_request, _timeout) \
|
||||||
|
{ \
|
||||||
|
.request = _request, .response_matches = NULL, .response_matches_size = 0, \
|
||||||
|
.timeout = _timeout, \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MODEM_CHAT_SCRIPT_CMDS_DEFINE(_sym, ...) \
|
||||||
|
const static struct modem_chat_script_chat _sym[] = {__VA_ARGS__}
|
||||||
|
|
||||||
|
enum modem_chat_script_result {
|
||||||
|
MODEM_CHAT_SCRIPT_RESULT_SUCCESS,
|
||||||
|
MODEM_CHAT_SCRIPT_RESULT_ABORT,
|
||||||
|
MODEM_CHAT_SCRIPT_RESULT_TIMEOUT
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Callback called when script chat is received
|
||||||
|
*
|
||||||
|
* @param chat Pointer to chat instance instance
|
||||||
|
* @param result Result of script execution
|
||||||
|
* @param user_data Free to use user data set during modem_chat_init()
|
||||||
|
*/
|
||||||
|
typedef void (*modem_chat_script_callback)(struct modem_chat *chat,
|
||||||
|
enum modem_chat_script_result result, void *user_data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Modem chat script
|
||||||
|
*/
|
||||||
|
struct modem_chat_script {
|
||||||
|
/** Name of script */
|
||||||
|
const char *name;
|
||||||
|
/** Array of script chats */
|
||||||
|
const struct modem_chat_script_chat *script_chats;
|
||||||
|
/** Elements in array of script chats */
|
||||||
|
const uint16_t script_chats_size;
|
||||||
|
/** Array of abort matches */
|
||||||
|
const struct modem_chat_match *const abort_matches;
|
||||||
|
/** Number of elements in array of abort matches */
|
||||||
|
const uint16_t abort_matches_size;
|
||||||
|
/** Callback called when script execution terminates */
|
||||||
|
modem_chat_script_callback callback;
|
||||||
|
/** Timeout in seconds within which the script execution must terminate */
|
||||||
|
const uint32_t timeout;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MODEM_CHAT_SCRIPT_DEFINE(_sym, _script_chats, _abort_matches, _callback, _timeout) \
|
||||||
|
static struct modem_chat_script _sym = { \
|
||||||
|
.name = #_sym, \
|
||||||
|
.script_chats = _script_chats, \
|
||||||
|
.script_chats_size = ARRAY_SIZE(_script_chats), \
|
||||||
|
.abort_matches = _abort_matches, \
|
||||||
|
.abort_matches_size = ARRAY_SIZE(_abort_matches), \
|
||||||
|
.callback = _callback, \
|
||||||
|
.timeout = _timeout, \
|
||||||
|
}
|
||||||
|
|
||||||
|
enum modem_chat_script_send_state {
|
||||||
|
/* No data to send */
|
||||||
|
MODEM_CHAT_SCRIPT_SEND_STATE_IDLE,
|
||||||
|
/* Sending request */
|
||||||
|
MODEM_CHAT_SCRIPT_SEND_STATE_REQUEST,
|
||||||
|
/* Sending delimiter */
|
||||||
|
MODEM_CHAT_SCRIPT_SEND_STATE_DELIMITER,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Chat instance internal context
|
||||||
|
* @warning Do not modify any members of this struct directly
|
||||||
|
*/
|
||||||
|
struct modem_chat {
|
||||||
|
/* Pipe used to send and receive data */
|
||||||
|
struct modem_pipe *pipe;
|
||||||
|
|
||||||
|
/* User data passed with match callbacks */
|
||||||
|
void *user_data;
|
||||||
|
|
||||||
|
/* Receive buffer */
|
||||||
|
uint8_t *receive_buf;
|
||||||
|
uint16_t receive_buf_size;
|
||||||
|
uint16_t receive_buf_len;
|
||||||
|
|
||||||
|
/* Work buffer */
|
||||||
|
uint8_t work_buf[32];
|
||||||
|
uint16_t work_buf_len;
|
||||||
|
|
||||||
|
/* Chat delimiter */
|
||||||
|
uint8_t *delimiter;
|
||||||
|
uint16_t delimiter_size;
|
||||||
|
uint16_t delimiter_match_len;
|
||||||
|
|
||||||
|
/* Array of bytes which are discarded out by parser */
|
||||||
|
uint8_t *filter;
|
||||||
|
uint16_t filter_size;
|
||||||
|
|
||||||
|
/* Parsed arguments */
|
||||||
|
uint8_t **argv;
|
||||||
|
uint16_t argv_size;
|
||||||
|
uint16_t argc;
|
||||||
|
|
||||||
|
/* Matches
|
||||||
|
* Index 0 -> Response matches
|
||||||
|
* Index 1 -> Abort matches
|
||||||
|
* Index 2 -> Unsolicited matches
|
||||||
|
*/
|
||||||
|
const struct modem_chat_match *matches[3];
|
||||||
|
uint16_t matches_size[3];
|
||||||
|
|
||||||
|
/* Script execution */
|
||||||
|
const struct modem_chat_script *script;
|
||||||
|
const struct modem_chat_script *pending_script;
|
||||||
|
struct k_work script_run_work;
|
||||||
|
struct k_work_delayable script_timeout_work;
|
||||||
|
struct k_work script_abort_work;
|
||||||
|
uint16_t script_chat_it;
|
||||||
|
atomic_t script_state;
|
||||||
|
|
||||||
|
/* Script sending */
|
||||||
|
uint16_t script_send_request_pos;
|
||||||
|
uint16_t script_send_delimiter_pos;
|
||||||
|
struct k_work_delayable script_send_work;
|
||||||
|
struct k_work_delayable script_send_timeout_work;
|
||||||
|
|
||||||
|
/* Match parsing */
|
||||||
|
const struct modem_chat_match *parse_match;
|
||||||
|
uint16_t parse_match_len;
|
||||||
|
uint16_t parse_arg_len;
|
||||||
|
uint16_t parse_match_type;
|
||||||
|
|
||||||
|
/* Process received data */
|
||||||
|
struct k_work_delayable process_work;
|
||||||
|
k_timeout_t process_timeout;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Chat configuration
|
||||||
|
*/
|
||||||
|
struct modem_chat_config {
|
||||||
|
/** Free to use user data passed with modem match callbacks */
|
||||||
|
void *user_data;
|
||||||
|
/** Receive buffer used to store parsed arguments */
|
||||||
|
uint8_t *receive_buf;
|
||||||
|
/** Size of receive buffer should be longest line + longest match */
|
||||||
|
uint16_t receive_buf_size;
|
||||||
|
/** Delimiter */
|
||||||
|
uint8_t *delimiter;
|
||||||
|
/** Size of delimiter */
|
||||||
|
uint8_t delimiter_size;
|
||||||
|
/** Bytes which are discarded by parser */
|
||||||
|
uint8_t *filter;
|
||||||
|
/** Size of filter */
|
||||||
|
uint8_t filter_size;
|
||||||
|
/** Array of pointers used to point to parsed arguments */
|
||||||
|
uint8_t **argv;
|
||||||
|
/** Elements in array of pointers */
|
||||||
|
uint16_t argv_size;
|
||||||
|
/** Array of unsolicited matches */
|
||||||
|
const struct modem_chat_match *unsol_matches;
|
||||||
|
/** Elements in array of unsolicited matches */
|
||||||
|
uint16_t unsol_matches_size;
|
||||||
|
/** Delay from receive ready event to pipe receive occurs */
|
||||||
|
k_timeout_t process_timeout;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize modem pipe chat instance
|
||||||
|
* @param chat Chat instance
|
||||||
|
* @param config Configuration which shall be applied to Chat instance
|
||||||
|
* @note Chat instance must be attached to pipe
|
||||||
|
*/
|
||||||
|
int modem_chat_init(struct modem_chat *chat, const struct modem_chat_config *config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Attach modem chat instance to pipe
|
||||||
|
* @param chat Chat instance
|
||||||
|
* @param pipe Pipe instance to attach Chat instance to
|
||||||
|
* @returns 0 if successful
|
||||||
|
* @returns negative errno code if failure
|
||||||
|
* @note Chat instance is enabled if successful
|
||||||
|
*/
|
||||||
|
int modem_chat_attach(struct modem_chat *chat, struct modem_pipe *pipe);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Run script
|
||||||
|
* @param chat Chat instance
|
||||||
|
* @param script Script to run
|
||||||
|
* @returns 0 if successful
|
||||||
|
* @returns -EBUSY if a script is currently running
|
||||||
|
* @returns -EPERM if modem pipe is not attached
|
||||||
|
* @returns -EINVAL if arguments or script is invalid
|
||||||
|
* @note Script runs asynchronously until complete or aborted.
|
||||||
|
*/
|
||||||
|
int modem_chat_script_run(struct modem_chat *chat, const struct modem_chat_script *script);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Abort script
|
||||||
|
* @param chat Chat instance
|
||||||
|
*/
|
||||||
|
void modem_chat_script_abort(struct modem_chat *chat);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Release pipe from chat instance
|
||||||
|
* @param chat Chat instance
|
||||||
|
*/
|
||||||
|
void modem_chat_release(struct modem_chat *chat);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* ZEPHYR_MODEM_CHAT_ */
|
267
include/zephyr/modem/cmux.h
Normal file
267
include/zephyr/modem/cmux.h
Normal file
|
@ -0,0 +1,267 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Trackunit Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This library uses CMUX to create multiple data channels, called DLCIs, on a single serial bus.
|
||||||
|
* Each DLCI has an address from 1 to 63. DLCI address 0 is reserved for control commands.
|
||||||
|
*
|
||||||
|
* Design overview:
|
||||||
|
*
|
||||||
|
* DLCI1 <-----------+ +-------> DLCI1
|
||||||
|
* v v
|
||||||
|
* DLCI2 <---> CMUX instance <--> Serial bus <--> Client <--> DLCI2
|
||||||
|
* ^ ^
|
||||||
|
* DLCI3 <-----------+ +-------> DLCI3
|
||||||
|
*
|
||||||
|
* Writing to and from the CMUX instances is done using the modem_pipe API.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/types.h>
|
||||||
|
#include <zephyr/sys/ring_buffer.h>
|
||||||
|
#include <zephyr/sys/atomic.h>
|
||||||
|
|
||||||
|
#include <zephyr/modem/pipe.h>
|
||||||
|
|
||||||
|
#ifndef ZEPHYR_MODEM_CMUX_
|
||||||
|
#define ZEPHYR_MODEM_CMUX_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct modem_cmux;
|
||||||
|
|
||||||
|
enum modem_cmux_state {
|
||||||
|
MODEM_CMUX_STATE_DISCONNECTED = 0,
|
||||||
|
MODEM_CMUX_STATE_CONNECTING,
|
||||||
|
MODEM_CMUX_STATE_CONNECTED,
|
||||||
|
MODEM_CMUX_STATE_DISCONNECTING,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum modem_cmux_event {
|
||||||
|
MODEM_CMUX_EVENT_CONNECTED = 0,
|
||||||
|
MODEM_CMUX_EVENT_DISCONNECTED,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void (*modem_cmux_callback)(struct modem_cmux *cmux, enum modem_cmux_event event,
|
||||||
|
void *user_data);
|
||||||
|
|
||||||
|
enum modem_cmux_receive_state {
|
||||||
|
MODEM_CMUX_RECEIVE_STATE_SOF = 0,
|
||||||
|
MODEM_CMUX_RECEIVE_STATE_RESYNC_0,
|
||||||
|
MODEM_CMUX_RECEIVE_STATE_RESYNC_1,
|
||||||
|
MODEM_CMUX_RECEIVE_STATE_RESYNC_2,
|
||||||
|
MODEM_CMUX_RECEIVE_STATE_RESYNC_3,
|
||||||
|
MODEM_CMUX_RECEIVE_STATE_ADDRESS,
|
||||||
|
MODEM_CMUX_RECEIVE_STATE_ADDRESS_CONT,
|
||||||
|
MODEM_CMUX_RECEIVE_STATE_CONTROL,
|
||||||
|
MODEM_CMUX_RECEIVE_STATE_LENGTH,
|
||||||
|
MODEM_CMUX_RECEIVE_STATE_LENGTH_CONT,
|
||||||
|
MODEM_CMUX_RECEIVE_STATE_DATA,
|
||||||
|
MODEM_CMUX_RECEIVE_STATE_FCS,
|
||||||
|
MODEM_CMUX_RECEIVE_STATE_DROP,
|
||||||
|
MODEM_CMUX_RECEIVE_STATE_EOF,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum modem_cmux_dlci_state {
|
||||||
|
MODEM_CMUX_DLCI_STATE_CLOSED,
|
||||||
|
MODEM_CMUX_DLCI_STATE_OPENING,
|
||||||
|
MODEM_CMUX_DLCI_STATE_OPEN,
|
||||||
|
MODEM_CMUX_DLCI_STATE_CLOSING,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum modem_cmux_dlci_event {
|
||||||
|
MODEM_CMUX_DLCI_EVENT_OPENED,
|
||||||
|
MODEM_CMUX_DLCI_EVENT_CLOSED,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct modem_cmux_dlci {
|
||||||
|
sys_snode_t node;
|
||||||
|
|
||||||
|
/* Pipe */
|
||||||
|
struct modem_pipe pipe;
|
||||||
|
|
||||||
|
/* Context */
|
||||||
|
uint16_t dlci_address;
|
||||||
|
struct modem_cmux *cmux;
|
||||||
|
|
||||||
|
/* Receive buffer */
|
||||||
|
struct ring_buf receive_rb;
|
||||||
|
struct k_mutex receive_rb_lock;
|
||||||
|
|
||||||
|
/* Work */
|
||||||
|
struct k_work_delayable open_work;
|
||||||
|
struct k_work_delayable close_work;
|
||||||
|
|
||||||
|
/* State */
|
||||||
|
enum modem_cmux_dlci_state state;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct modem_cmux_frame {
|
||||||
|
uint16_t dlci_address;
|
||||||
|
bool cr;
|
||||||
|
bool pf;
|
||||||
|
uint8_t type;
|
||||||
|
const uint8_t *data;
|
||||||
|
uint16_t data_len;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct modem_cmux_work {
|
||||||
|
struct k_work_delayable dwork;
|
||||||
|
struct modem_cmux *cmux;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct modem_cmux {
|
||||||
|
/* Bus pipe */
|
||||||
|
struct modem_pipe *pipe;
|
||||||
|
|
||||||
|
/* Event handler */
|
||||||
|
modem_cmux_callback callback;
|
||||||
|
void *user_data;
|
||||||
|
|
||||||
|
/* DLCI channel contexts */
|
||||||
|
sys_slist_t dlcis;
|
||||||
|
|
||||||
|
/* State */
|
||||||
|
enum modem_cmux_state state;
|
||||||
|
bool flow_control_on;
|
||||||
|
|
||||||
|
/* Receive state*/
|
||||||
|
enum modem_cmux_receive_state receive_state;
|
||||||
|
|
||||||
|
/* Receive buffer */
|
||||||
|
uint8_t *receive_buf;
|
||||||
|
uint16_t receive_buf_size;
|
||||||
|
uint16_t receive_buf_len;
|
||||||
|
|
||||||
|
/* Transmit buffer */
|
||||||
|
struct ring_buf transmit_rb;
|
||||||
|
struct k_mutex transmit_rb_lock;
|
||||||
|
|
||||||
|
/* Received frame */
|
||||||
|
struct modem_cmux_frame frame;
|
||||||
|
uint8_t frame_header[5];
|
||||||
|
uint16_t frame_header_len;
|
||||||
|
|
||||||
|
/* Work */
|
||||||
|
struct k_work_delayable receive_work;
|
||||||
|
struct k_work_delayable transmit_work;
|
||||||
|
struct k_work_delayable connect_work;
|
||||||
|
struct k_work_delayable disconnect_work;
|
||||||
|
|
||||||
|
/* Synchronize actions */
|
||||||
|
struct k_event event;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Contains CMUX instance configuration data
|
||||||
|
*/
|
||||||
|
struct modem_cmux_config {
|
||||||
|
/** Invoked when event occurs */
|
||||||
|
modem_cmux_callback callback;
|
||||||
|
/** Free to use pointer passed to event handler when invoked */
|
||||||
|
void *user_data;
|
||||||
|
/** Receive buffer */
|
||||||
|
uint8_t *receive_buf;
|
||||||
|
/** Size of receive buffer in bytes [127, ...] */
|
||||||
|
uint16_t receive_buf_size;
|
||||||
|
/** Transmit buffer */
|
||||||
|
uint8_t *transmit_buf;
|
||||||
|
/** Size of transmit buffer in bytes [149, ...] */
|
||||||
|
uint16_t transmit_buf_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize CMUX instance
|
||||||
|
* @param cmux CMUX instance
|
||||||
|
* @param config Configuration to apply to CMUX instance
|
||||||
|
*/
|
||||||
|
void modem_cmux_init(struct modem_cmux *cmux, const struct modem_cmux_config *config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief CMUX DLCI configuration
|
||||||
|
*/
|
||||||
|
struct modem_cmux_dlci_config {
|
||||||
|
/** DLCI channel address */
|
||||||
|
uint8_t dlci_address;
|
||||||
|
/** Receive buffer used by pipe */
|
||||||
|
uint8_t *receive_buf;
|
||||||
|
/** Size of receive buffer used by pipe [127, ...] */
|
||||||
|
uint16_t receive_buf_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize DLCI instance and register it with CMUX instance
|
||||||
|
*
|
||||||
|
* @param cmux CMUX instance which the DLCI will be registered to
|
||||||
|
* @param dlci DLCI instance which will be registered and configured
|
||||||
|
* @param config Configuration to apply to DLCI instance
|
||||||
|
*/
|
||||||
|
struct modem_pipe *modem_cmux_dlci_init(struct modem_cmux *cmux, struct modem_cmux_dlci *dlci,
|
||||||
|
const struct modem_cmux_dlci_config *config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize CMUX instance
|
||||||
|
*
|
||||||
|
* @param cmux CMUX instance
|
||||||
|
* @param pipe Pipe instance to attach CMUX instance to
|
||||||
|
*/
|
||||||
|
int modem_cmux_attach(struct modem_cmux *cmux, struct modem_pipe *pipe);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Connect CMUX instance
|
||||||
|
*
|
||||||
|
* @details This will send a CMUX connect request to target on the serial bus. If successful,
|
||||||
|
* DLCI channels can be now be opened using modem_pipe_open()
|
||||||
|
*
|
||||||
|
* @param cmux CMUX instance
|
||||||
|
*
|
||||||
|
* @note When connected, the bus pipe must not be used directly
|
||||||
|
*/
|
||||||
|
int modem_cmux_connect(struct modem_cmux *cmux);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Connect CMUX instance asynchronously
|
||||||
|
*
|
||||||
|
* @details This will send a CMUX connect request to target on the serial bus. If successful,
|
||||||
|
* DLCI channels can be now be opened using modem_pipe_open().
|
||||||
|
*
|
||||||
|
* @param cmux CMUX instance
|
||||||
|
*
|
||||||
|
* @note When connected, the bus pipe must not be used directly
|
||||||
|
*/
|
||||||
|
int modem_cmux_connect_async(struct modem_cmux *cmux);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Close down and disconnect CMUX instance
|
||||||
|
*
|
||||||
|
* @details This will close all open DLCI channels, and close down the CMUX connection.
|
||||||
|
*
|
||||||
|
* @param cmux CMUX instance
|
||||||
|
*
|
||||||
|
* @note When disconnected, the bus pipe can be used directly again
|
||||||
|
*/
|
||||||
|
int modem_cmux_disconnect(struct modem_cmux *cmux);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Close down and disconnect CMUX instance asynchronously
|
||||||
|
*
|
||||||
|
* @details This will close all open DLCI channels, and close down the CMUX connection.
|
||||||
|
*
|
||||||
|
* @param cmux CMUX instance
|
||||||
|
*
|
||||||
|
* @note When disconnected, the bus pipe can be used directly again
|
||||||
|
*/
|
||||||
|
int modem_cmux_disconnect_async(struct modem_cmux *cmux);
|
||||||
|
|
||||||
|
void modem_cmux_release(struct modem_cmux *cmux);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* ZEPHYR_MODEM_CMUX_ */
|
170
include/zephyr/modem/pipe.h
Normal file
170
include/zephyr/modem/pipe.h
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Trackunit Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/types.h>
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
|
||||||
|
#ifndef ZEPHYR_MODEM_PIPE_
|
||||||
|
#define ZEPHYR_MODEM_PIPE_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct modem_pipe;
|
||||||
|
|
||||||
|
typedef int (*modem_pipe_api_open)(void *data);
|
||||||
|
|
||||||
|
typedef int (*modem_pipe_api_transmit)(void *data, const uint8_t *buf, size_t size);
|
||||||
|
|
||||||
|
typedef int (*modem_pipe_api_receive)(void *data, uint8_t *buf, size_t size);
|
||||||
|
|
||||||
|
typedef int (*modem_pipe_api_close)(void *data);
|
||||||
|
|
||||||
|
struct modem_pipe_api {
|
||||||
|
modem_pipe_api_open open;
|
||||||
|
modem_pipe_api_transmit transmit;
|
||||||
|
modem_pipe_api_receive receive;
|
||||||
|
modem_pipe_api_close close;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum modem_pipe_state {
|
||||||
|
MODEM_PIPE_STATE_CLOSED = 0,
|
||||||
|
MODEM_PIPE_STATE_OPEN,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum modem_pipe_event {
|
||||||
|
MODEM_PIPE_EVENT_OPENED = 0,
|
||||||
|
MODEM_PIPE_EVENT_RECEIVE_READY,
|
||||||
|
MODEM_PIPE_EVENT_CLOSED,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void (*modem_pipe_api_callback)(struct modem_pipe *pipe, enum modem_pipe_event event,
|
||||||
|
void *user_data);
|
||||||
|
|
||||||
|
struct modem_pipe {
|
||||||
|
void *data;
|
||||||
|
struct modem_pipe_api *api;
|
||||||
|
modem_pipe_api_callback callback;
|
||||||
|
void *user_data;
|
||||||
|
enum modem_pipe_state state;
|
||||||
|
struct k_mutex lock;
|
||||||
|
struct k_condvar condvar;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize a modem pipe
|
||||||
|
*
|
||||||
|
* @param pipe Pipe instance to initialize
|
||||||
|
* @param data Pipe data to bind to pipe instance
|
||||||
|
* @param api Pipe API implementation to bind to pipe instance
|
||||||
|
*/
|
||||||
|
void modem_pipe_init(struct modem_pipe *pipe, void *data, struct modem_pipe_api *api);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Open pipe
|
||||||
|
*
|
||||||
|
* @param pipe Pipe instance
|
||||||
|
*/
|
||||||
|
int modem_pipe_open(struct modem_pipe *pipe);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Open pipe asynchronously
|
||||||
|
*
|
||||||
|
* @param pipe Pipe instance
|
||||||
|
*/
|
||||||
|
int modem_pipe_open_async(struct modem_pipe *pipe);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Attach pipe to callback
|
||||||
|
*
|
||||||
|
* @param pipe Pipe instance
|
||||||
|
* @param callback Callback called when pipe event occurs
|
||||||
|
* @param user_data Free to use user data passed with callback
|
||||||
|
*/
|
||||||
|
void modem_pipe_attach(struct modem_pipe *pipe, modem_pipe_api_callback callback, void *user_data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Transmit data through pipe
|
||||||
|
*
|
||||||
|
* @param pipe Pipe to transmit through
|
||||||
|
* @param buf Destination for reveived data
|
||||||
|
* @param size Capacity of destination for recevied data
|
||||||
|
*
|
||||||
|
* @return Number of bytes placed in pipe
|
||||||
|
*
|
||||||
|
* @warning This call must be non-blocking
|
||||||
|
*/
|
||||||
|
int modem_pipe_transmit(struct modem_pipe *pipe, const uint8_t *buf, size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reveive data through pipe
|
||||||
|
*
|
||||||
|
* @param pipe Pipe to receive from
|
||||||
|
* @param buf Destination for reveived data
|
||||||
|
* @param size Capacity of destination for recevied data
|
||||||
|
*
|
||||||
|
* @return Number of bytes received from pipe if any
|
||||||
|
* @return -EPERM if pipe is closed
|
||||||
|
* @return -errno code on error
|
||||||
|
*
|
||||||
|
* @warning This call must be non-blocking
|
||||||
|
*/
|
||||||
|
int modem_pipe_receive(struct modem_pipe *pipe, uint8_t *buf, size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clear callback
|
||||||
|
*
|
||||||
|
* @param pipe Pipe instance
|
||||||
|
*/
|
||||||
|
void modem_pipe_release(struct modem_pipe *pipe);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Close pipe
|
||||||
|
*
|
||||||
|
* @param pipe Pipe instance
|
||||||
|
*/
|
||||||
|
int modem_pipe_close(struct modem_pipe *pipe);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Close pipe asynchronously
|
||||||
|
*
|
||||||
|
* @param pipe Pipe instance
|
||||||
|
*/
|
||||||
|
int modem_pipe_close_async(struct modem_pipe *pipe);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Notify user of pipe that it has opened
|
||||||
|
*
|
||||||
|
* @param pipe Pipe instance
|
||||||
|
*
|
||||||
|
* @note Invoked from instance which initialized the pipe instance
|
||||||
|
*/
|
||||||
|
void modem_pipe_notify_opened(struct modem_pipe *pipe);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Notify user of pipe that it has closed
|
||||||
|
*
|
||||||
|
* @param pipe Pipe instance
|
||||||
|
*
|
||||||
|
* @note Invoked from instance which initialized the pipe instance
|
||||||
|
*/
|
||||||
|
void modem_pipe_notify_closed(struct modem_pipe *pipe);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Notify user of pipe that data is ready to be received
|
||||||
|
*
|
||||||
|
* @param pipe Pipe instance
|
||||||
|
*
|
||||||
|
* @note Invoked from instance which initialized the pipe instance
|
||||||
|
*/
|
||||||
|
void modem_pipe_notify_receive_ready(struct modem_pipe *pipe);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* ZEPHYR_MODEM_PIPE_ */
|
167
include/zephyr/modem/ppp.h
Normal file
167
include/zephyr/modem/ppp.h
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Trackunit Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/types.h>
|
||||||
|
#include <zephyr/net/net_if.h>
|
||||||
|
#include <zephyr/net/net_pkt.h>
|
||||||
|
#include <zephyr/sys/ring_buffer.h>
|
||||||
|
#include <zephyr/sys/atomic.h>
|
||||||
|
|
||||||
|
#include <zephyr/modem/pipe.h>
|
||||||
|
|
||||||
|
#ifndef ZEPHYR_MODEM_PPP_
|
||||||
|
#define ZEPHYR_MODEM_PPP_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum modem_ppp_receive_state {
|
||||||
|
/* Searching for start of frame and header */
|
||||||
|
MODEM_PPP_RECEIVE_STATE_HDR_SOF = 0,
|
||||||
|
MODEM_PPP_RECEIVE_STATE_HDR_FF,
|
||||||
|
MODEM_PPP_RECEIVE_STATE_HDR_7D,
|
||||||
|
MODEM_PPP_RECEIVE_STATE_HDR_23,
|
||||||
|
/* Writing bytes to network packet */
|
||||||
|
MODEM_PPP_RECEIVE_STATE_WRITING,
|
||||||
|
/* Unescaping next byte before writing to network packet */
|
||||||
|
MODEM_PPP_RECEIVE_STATE_UNESCAPING,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum modem_ppp_transmit_state {
|
||||||
|
/* Idle */
|
||||||
|
MODEM_PPP_TRANSMIT_STATE_IDLE = 0,
|
||||||
|
/* Writing header */
|
||||||
|
MODEM_PPP_TRANSMIT_STATE_SOF,
|
||||||
|
MODEM_PPP_TRANSMIT_STATE_HDR_FF,
|
||||||
|
MODEM_PPP_TRANSMIT_STATE_HDR_7D,
|
||||||
|
MODEM_PPP_TRANSMIT_STATE_HDR_23,
|
||||||
|
/* Writing protocol */
|
||||||
|
MODEM_PPP_TRANSMIT_STATE_PROTOCOL_HIGH,
|
||||||
|
MODEM_PPP_TRANSMIT_STATE_ESCAPING_PROTOCOL_HIGH,
|
||||||
|
MODEM_PPP_TRANSMIT_STATE_PROTOCOL_LOW,
|
||||||
|
MODEM_PPP_TRANSMIT_STATE_ESCAPING_PROTOCOL_LOW,
|
||||||
|
/* Writing data */
|
||||||
|
MODEM_PPP_TRANSMIT_STATE_DATA,
|
||||||
|
MODEM_PPP_TRANSMIT_STATE_ESCAPING_DATA,
|
||||||
|
/* Writing FCS */
|
||||||
|
MODEM_PPP_TRANSMIT_STATE_FCS_LOW,
|
||||||
|
MODEM_PPP_TRANSMIT_STATE_ESCAPING_FCS_LOW,
|
||||||
|
MODEM_PPP_TRANSMIT_STATE_FCS_HIGH,
|
||||||
|
MODEM_PPP_TRANSMIT_STATE_ESCAPING_FCS_HIGH,
|
||||||
|
/* Writing end of frame */
|
||||||
|
MODEM_PPP_TRANSMIT_STATE_EOF,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void (*modem_ppp_init_iface)(struct net_if *iface);
|
||||||
|
|
||||||
|
struct modem_ppp {
|
||||||
|
/* Network interface instance is bound to */
|
||||||
|
struct net_if *iface;
|
||||||
|
|
||||||
|
/* Hook for PPP L2 network interface initialization */
|
||||||
|
modem_ppp_init_iface init_iface;
|
||||||
|
|
||||||
|
atomic_t state;
|
||||||
|
|
||||||
|
/* Buffers used for processing partial frames */
|
||||||
|
uint8_t *receive_buf;
|
||||||
|
uint8_t *transmit_buf;
|
||||||
|
uint16_t buf_size;
|
||||||
|
|
||||||
|
/* Wrapped PPP frames are sent and received through this pipe */
|
||||||
|
struct modem_pipe *pipe;
|
||||||
|
|
||||||
|
/* Receive PPP frame state */
|
||||||
|
enum modem_ppp_receive_state receive_state;
|
||||||
|
|
||||||
|
/* Allocated network packet being created */
|
||||||
|
struct net_pkt *rx_pkt;
|
||||||
|
|
||||||
|
/* Packet being sent */
|
||||||
|
enum modem_ppp_transmit_state transmit_state;
|
||||||
|
struct net_pkt *tx_pkt;
|
||||||
|
uint8_t tx_pkt_escaped;
|
||||||
|
uint16_t tx_pkt_protocol;
|
||||||
|
uint16_t tx_pkt_fcs;
|
||||||
|
|
||||||
|
/* Ring buffer used for transmitting partial PPP frame */
|
||||||
|
struct ring_buf transmit_rb;
|
||||||
|
|
||||||
|
struct k_fifo tx_pkt_fifo;
|
||||||
|
|
||||||
|
/* Work */
|
||||||
|
struct k_work send_work;
|
||||||
|
struct k_work process_work;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Attach pipe to instance and connect
|
||||||
|
*
|
||||||
|
* @param ppp Modem PPP instance
|
||||||
|
* @param pipe Pipe to attach to modem PPP instance
|
||||||
|
*/
|
||||||
|
int modem_ppp_attach(struct modem_ppp *ppp, struct modem_pipe *pipe);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get network interface modem PPP instance is bound to
|
||||||
|
*
|
||||||
|
* @param ppp Modem PPP instance
|
||||||
|
* @returns Pointer to network interface modem PPP instance is bound to
|
||||||
|
*/
|
||||||
|
struct net_if *modem_ppp_get_iface(struct modem_ppp *ppp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Release pipe from instance
|
||||||
|
*
|
||||||
|
* @param ppp Modem PPP instance
|
||||||
|
*/
|
||||||
|
void modem_ppp_release(struct modem_ppp *ppp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize modem PPP instance device
|
||||||
|
* @param dev Device instance associated with network interface
|
||||||
|
* @warning Should not be used directly
|
||||||
|
*/
|
||||||
|
int modem_ppp_init_internal(const struct device *dev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Define a modem PPP module and bind it to a network interface
|
||||||
|
*
|
||||||
|
* @details This macro defines the modem_ppp instance, initializes a PPP L2
|
||||||
|
* network device instance, and binds the modem_ppp instance to the PPP L2
|
||||||
|
* instance.
|
||||||
|
*
|
||||||
|
* @param _name Name of the statically defined modem_ppp instance
|
||||||
|
* @param _init_iface Hook for the PPP L2 network interface init function
|
||||||
|
* @param _prio Initialization priority of the PPP L2 net iface
|
||||||
|
* @param _mtu Max size of net_pkt data sent and received on PPP L2 net iface
|
||||||
|
* @param _buf_size Size of partial PPP frame transmit and receive buffers
|
||||||
|
*/
|
||||||
|
#define MODEM_PPP_DEFINE(_name, _init_iface, _prio, _mtu, _buf_size) \
|
||||||
|
extern const struct ppp_api modem_ppp_ppp_api; \
|
||||||
|
\
|
||||||
|
static uint8_t _CONCAT(_name, _receive_buf)[_buf_size]; \
|
||||||
|
static uint8_t _CONCAT(_name, _transmit_buf)[_buf_size]; \
|
||||||
|
\
|
||||||
|
static struct modem_ppp _name = { \
|
||||||
|
.init_iface = _init_iface, \
|
||||||
|
.receive_buf = _CONCAT(_name, _receive_buf), \
|
||||||
|
.transmit_buf = _CONCAT(_name, _transmit_buf), \
|
||||||
|
.buf_size = _buf_size, \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
NET_DEVICE_INIT(_CONCAT(ppp_net_dev_, _name), "modem_ppp_" # _name, \
|
||||||
|
modem_ppp_init_internal, NULL, &_name, NULL, _prio, &modem_ppp_ppp_api, \
|
||||||
|
PPP_L2, NET_L2_GET_CTX_TYPE(PPP_L2), _mtu)
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* ZEPHYR_MODEM_PPP_ */
|
|
@ -40,6 +40,7 @@ add_subdirectory_ifdef(CONFIG_EMUL emul)
|
||||||
add_subdirectory_ifdef(CONFIG_IMG_MANAGER dfu)
|
add_subdirectory_ifdef(CONFIG_IMG_MANAGER dfu)
|
||||||
add_subdirectory_ifdef(CONFIG_INPUT input)
|
add_subdirectory_ifdef(CONFIG_INPUT input)
|
||||||
add_subdirectory_ifdef(CONFIG_JWT jwt)
|
add_subdirectory_ifdef(CONFIG_JWT jwt)
|
||||||
|
add_subdirectory_ifdef(CONFIG_MODEM_MODULES modem)
|
||||||
add_subdirectory_ifdef(CONFIG_NET_BUF net)
|
add_subdirectory_ifdef(CONFIG_NET_BUF net)
|
||||||
add_subdirectory_ifdef(CONFIG_RETENTION retention)
|
add_subdirectory_ifdef(CONFIG_RETENTION retention)
|
||||||
add_subdirectory_ifdef(CONFIG_SENSING sensing)
|
add_subdirectory_ifdef(CONFIG_SENSING sensing)
|
||||||
|
|
|
@ -24,6 +24,7 @@ source "subsys/logging/Kconfig"
|
||||||
source "subsys/lorawan/Kconfig"
|
source "subsys/lorawan/Kconfig"
|
||||||
source "subsys/mgmt/Kconfig"
|
source "subsys/mgmt/Kconfig"
|
||||||
source "subsys/modbus/Kconfig"
|
source "subsys/modbus/Kconfig"
|
||||||
|
source "subsys/modem/Kconfig"
|
||||||
source "subsys/net/Kconfig"
|
source "subsys/net/Kconfig"
|
||||||
source "subsys/pm/Kconfig"
|
source "subsys/pm/Kconfig"
|
||||||
source "subsys/portability/Kconfig"
|
source "subsys/portability/Kconfig"
|
||||||
|
|
15
subsys/modem/CMakeLists.txt
Normal file
15
subsys/modem/CMakeLists.txt
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# Copyright (c) 2023 Trackunit Corporation
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
if(CONFIG_MODEM_MODULES)
|
||||||
|
|
||||||
|
zephyr_library()
|
||||||
|
|
||||||
|
zephyr_library_sources_ifdef(CONFIG_MODEM_CHAT modem_chat.c)
|
||||||
|
zephyr_library_sources_ifdef(CONFIG_MODEM_CMUX modem_cmux.c)
|
||||||
|
zephyr_library_sources_ifdef(CONFIG_MODEM_PIPE modem_pipe.c)
|
||||||
|
zephyr_library_sources_ifdef(CONFIG_MODEM_PPP modem_ppp.c)
|
||||||
|
|
||||||
|
add_subdirectory(backends)
|
||||||
|
|
||||||
|
endif()
|
54
subsys/modem/Kconfig
Normal file
54
subsys/modem/Kconfig
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
# Copyright (c) 2023 Trackunit Corporation
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
menuconfig MODEM_MODULES
|
||||||
|
bool "Modem modules"
|
||||||
|
|
||||||
|
if MODEM_MODULES
|
||||||
|
|
||||||
|
config MODEM_CHAT
|
||||||
|
bool "Modem chat module"
|
||||||
|
select RING_BUFFER
|
||||||
|
select MODEM_PIPE
|
||||||
|
|
||||||
|
if MODEM_CHAT
|
||||||
|
|
||||||
|
config MODEM_CHAT_LOG_BUFFER
|
||||||
|
int "Modem chat log buffer size"
|
||||||
|
default 128
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
config MODEM_CMUX
|
||||||
|
bool "Modem CMUX module"
|
||||||
|
select MODEM_PIPE
|
||||||
|
select RING_BUFFER
|
||||||
|
select EVENTS
|
||||||
|
select CRC
|
||||||
|
|
||||||
|
config MODEM_PIPE
|
||||||
|
bool "Modem pipe module"
|
||||||
|
|
||||||
|
config MODEM_PPP
|
||||||
|
bool "Modem PPP module"
|
||||||
|
depends on NET_L2_PPP
|
||||||
|
select MODEM_PIPE
|
||||||
|
select RING_BUFFER
|
||||||
|
select CRC
|
||||||
|
|
||||||
|
if MODEM_PPP
|
||||||
|
|
||||||
|
config MODEM_PPP_NET_BUF_FRAG_SIZE
|
||||||
|
int "Network buffer fragment size"
|
||||||
|
default NET_BUF_DATA_SIZE if NET_BUF_FIXED_DATA_SIZE
|
||||||
|
default 128
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
module = MODEM_MODULES
|
||||||
|
module-str = modem_modules
|
||||||
|
source "subsys/logging/Kconfig.template.log_config"
|
||||||
|
|
||||||
|
rsource "backends/Kconfig"
|
||||||
|
|
||||||
|
endif
|
9
subsys/modem/backends/CMakeLists.txt
Normal file
9
subsys/modem/backends/CMakeLists.txt
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# Copyright (c) 2023 Trackunit Corporation
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
zephyr_library()
|
||||||
|
|
||||||
|
zephyr_library_sources_ifdef(CONFIG_MODEM_BACKEND_TTY modem_backend_tty.c)
|
||||||
|
zephyr_library_sources_ifdef(CONFIG_MODEM_BACKEND_UART modem_backend_uart.c)
|
||||||
|
zephyr_library_sources_ifdef(CONFIG_MODEM_BACKEND_UART_ISR modem_backend_uart_isr.c)
|
||||||
|
zephyr_library_sources_ifdef(CONFIG_MODEM_BACKEND_UART_ASYNC modem_backend_uart_async.c)
|
24
subsys/modem/backends/Kconfig
Normal file
24
subsys/modem/backends/Kconfig
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# Copyright (c) 2023 Trackunit Corporation
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
config MODEM_BACKEND_TTY
|
||||||
|
bool "Modem TTY backend module"
|
||||||
|
select MODEM_PIPE
|
||||||
|
depends on ARCH_POSIX
|
||||||
|
|
||||||
|
config MODEM_BACKEND_UART
|
||||||
|
bool "Modem UART backend module"
|
||||||
|
select MODEM_PIPE
|
||||||
|
depends on UART_INTERRUPT_DRIVEN || UART_ASYNC_API
|
||||||
|
|
||||||
|
if MODEM_BACKEND_UART
|
||||||
|
|
||||||
|
config MODEM_BACKEND_UART_ISR
|
||||||
|
bool "Modem UART backend module interrupt driven implementation"
|
||||||
|
default y if UART_INTERRUPT_DRIVEN
|
||||||
|
|
||||||
|
config MODEM_BACKEND_UART_ASYNC
|
||||||
|
bool "Modem UART backend module async implementation"
|
||||||
|
default y if UART_ASYNC_API
|
||||||
|
|
||||||
|
endif # MODEM_BACKEND_UART
|
124
subsys/modem/backends/modem_backend_tty.c
Normal file
124
subsys/modem/backends/modem_backend_tty.c
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Trackunit Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/modem/backend/tty.h>
|
||||||
|
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(modem_backend_tty);
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define MODEM_BACKEND_TTY_THREAD_PRIO (10)
|
||||||
|
#define MODEM_BACKEND_TTY_THREAD_RUN_PERIOD_MS (1000)
|
||||||
|
#define MODEM_BACKEND_TTY_THREAD_POLL_DELAY (100)
|
||||||
|
|
||||||
|
#define MODEM_BACKEND_TTY_STATE_RUN_BIT (1)
|
||||||
|
|
||||||
|
static void modem_backend_tty_routine(void *p1, void *p2, void *p3)
|
||||||
|
{
|
||||||
|
struct modem_backend_tty *backend = (struct modem_backend_tty *)p1;
|
||||||
|
struct pollfd pd;
|
||||||
|
|
||||||
|
ARG_UNUSED(p2);
|
||||||
|
ARG_UNUSED(p3);
|
||||||
|
|
||||||
|
pd.fd = backend->tty_fd;
|
||||||
|
pd.events = POLLIN;
|
||||||
|
|
||||||
|
/* Run until run flag is cleared. Check every MODEM_BACKEND_TTY_THREAD_RUN_PERIOD_MS */
|
||||||
|
while (atomic_test_bit(&backend->state, MODEM_BACKEND_TTY_STATE_RUN_BIT)) {
|
||||||
|
/* Clear events */
|
||||||
|
pd.revents = 0;
|
||||||
|
|
||||||
|
if (poll(&pd, 1, MODEM_BACKEND_TTY_THREAD_RUN_PERIOD_MS) < 0) {
|
||||||
|
LOG_ERR("Poll operation failed");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pd.revents & POLLIN) {
|
||||||
|
modem_pipe_notify_receive_ready(&backend->pipe);
|
||||||
|
}
|
||||||
|
|
||||||
|
k_sleep(K_MSEC(MODEM_BACKEND_TTY_THREAD_POLL_DELAY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int modem_backend_tty_open(void *data)
|
||||||
|
{
|
||||||
|
struct modem_backend_tty *backend = (struct modem_backend_tty *)data;
|
||||||
|
|
||||||
|
if (atomic_test_and_set_bit(&backend->state, MODEM_BACKEND_TTY_STATE_RUN_BIT)) {
|
||||||
|
return -EALREADY;
|
||||||
|
}
|
||||||
|
|
||||||
|
backend->tty_fd = open(backend->tty_path, (O_RDWR | O_NONBLOCK), 0644);
|
||||||
|
if (backend->tty_fd < 0) {
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
k_thread_create(&backend->thread, backend->stack, backend->stack_size,
|
||||||
|
modem_backend_tty_routine, backend, NULL, NULL,
|
||||||
|
MODEM_BACKEND_TTY_THREAD_PRIO, 0, K_NO_WAIT);
|
||||||
|
|
||||||
|
modem_pipe_notify_opened(&backend->pipe);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int modem_backend_tty_transmit(void *data, const uint8_t *buf, size_t size)
|
||||||
|
{
|
||||||
|
struct modem_backend_tty *backend = (struct modem_backend_tty *)data;
|
||||||
|
|
||||||
|
return write(backend->tty_fd, buf, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int modem_backend_tty_receive(void *data, uint8_t *buf, size_t size)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct modem_backend_tty *backend = (struct modem_backend_tty *)data;
|
||||||
|
|
||||||
|
ret = read(backend->tty_fd, buf, size);
|
||||||
|
return (ret < 0) ? 0 : ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int modem_backend_tty_close(void *data)
|
||||||
|
{
|
||||||
|
struct modem_backend_tty *backend = (struct modem_backend_tty *)data;
|
||||||
|
|
||||||
|
if (!atomic_test_and_clear_bit(&backend->state, MODEM_BACKEND_TTY_STATE_RUN_BIT)) {
|
||||||
|
return -EALREADY;
|
||||||
|
}
|
||||||
|
|
||||||
|
k_thread_join(&backend->thread, K_MSEC(MODEM_BACKEND_TTY_THREAD_RUN_PERIOD_MS * 2));
|
||||||
|
close(backend->tty_fd);
|
||||||
|
modem_pipe_notify_closed(&backend->pipe);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct modem_pipe_api modem_backend_tty_api = {
|
||||||
|
.open = modem_backend_tty_open,
|
||||||
|
.transmit = modem_backend_tty_transmit,
|
||||||
|
.receive = modem_backend_tty_receive,
|
||||||
|
.close = modem_backend_tty_close,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct modem_pipe *modem_backend_tty_init(struct modem_backend_tty *backend,
|
||||||
|
const struct modem_backend_tty_config *config)
|
||||||
|
{
|
||||||
|
__ASSERT_NO_MSG(backend != NULL);
|
||||||
|
__ASSERT_NO_MSG(config != NULL);
|
||||||
|
__ASSERT_NO_MSG(config->tty_path != NULL);
|
||||||
|
|
||||||
|
memset(backend, 0x00, sizeof(*backend));
|
||||||
|
backend->tty_path = config->tty_path;
|
||||||
|
backend->stack = config->stack;
|
||||||
|
backend->stack_size = config->stack_size;
|
||||||
|
atomic_set(&backend->state, 0);
|
||||||
|
modem_pipe_init(&backend->pipe, backend, &modem_backend_tty_api);
|
||||||
|
return &backend->pipe;
|
||||||
|
}
|
55
subsys/modem/backends/modem_backend_uart.c
Normal file
55
subsys/modem/backends/modem_backend_uart.c
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Trackunit Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "modem_backend_uart_isr.h"
|
||||||
|
#include "modem_backend_uart_async.h"
|
||||||
|
|
||||||
|
#include <zephyr/modem/backend/uart.h>
|
||||||
|
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(modem_backend_uart);
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static void modem_backend_uart_receive_ready_handler(struct k_work *item)
|
||||||
|
{
|
||||||
|
struct modem_backend_uart *backend =
|
||||||
|
CONTAINER_OF(item, struct modem_backend_uart, receive_ready_work);
|
||||||
|
|
||||||
|
modem_pipe_notify_receive_ready(&backend->pipe);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct modem_pipe *modem_backend_uart_init(struct modem_backend_uart *backend,
|
||||||
|
const struct modem_backend_uart_config *config)
|
||||||
|
{
|
||||||
|
__ASSERT_NO_MSG(config->uart != NULL);
|
||||||
|
__ASSERT_NO_MSG(config->receive_buf != NULL);
|
||||||
|
__ASSERT_NO_MSG(config->receive_buf_size > 1);
|
||||||
|
__ASSERT_NO_MSG((config->receive_buf_size % 2) == 0);
|
||||||
|
__ASSERT_NO_MSG(config->transmit_buf != NULL);
|
||||||
|
__ASSERT_NO_MSG(config->transmit_buf_size > 0);
|
||||||
|
|
||||||
|
memset(backend, 0x00, sizeof(*backend));
|
||||||
|
backend->uart = config->uart;
|
||||||
|
k_work_init(&backend->receive_ready_work, modem_backend_uart_receive_ready_handler);
|
||||||
|
|
||||||
|
#ifdef CONFIG_UART_ASYNC_API
|
||||||
|
if (modem_backend_uart_async_is_supported(backend)) {
|
||||||
|
modem_backend_uart_async_init(backend, config);
|
||||||
|
return &backend->pipe;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_UART_ASYNC_API */
|
||||||
|
|
||||||
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||||
|
modem_backend_uart_isr_init(backend, config);
|
||||||
|
|
||||||
|
return &backend->pipe;
|
||||||
|
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
||||||
|
|
||||||
|
__ASSERT(0, "No supported UART API");
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
260
subsys/modem/backends/modem_backend_uart_async.c
Normal file
260
subsys/modem/backends/modem_backend_uart_async.c
Normal file
|
@ -0,0 +1,260 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Trackunit Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "modem_backend_uart_async.h"
|
||||||
|
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
LOG_MODULE_DECLARE(modem_backend_uart);
|
||||||
|
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define MODEM_BACKEND_UART_ASYNC_STATE_TRANSMITTING_BIT (0)
|
||||||
|
#define MODEM_BACKEND_UART_ASYNC_STATE_RX_BUF0_USED_BIT (1)
|
||||||
|
#define MODEM_BACKEND_UART_ASYNC_STATE_RX_BUF1_USED_BIT (2)
|
||||||
|
#define MODEM_BACKEND_UART_ASYNC_STATE_RX_RBUF_USED_INDEX_BIT (3)
|
||||||
|
|
||||||
|
#define MODEM_BACKEND_UART_ASYNC_BLOCK_MIN_SIZE (8)
|
||||||
|
|
||||||
|
static void modem_backend_uart_async_flush(struct modem_backend_uart *backend)
|
||||||
|
{
|
||||||
|
uint8_t c;
|
||||||
|
|
||||||
|
while (uart_fifo_read(backend->uart, &c, 1) > 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t modem_backend_uart_async_rx_rbuf_used_index(struct modem_backend_uart *backend)
|
||||||
|
{
|
||||||
|
return atomic_test_bit(&backend->async.state,
|
||||||
|
MODEM_BACKEND_UART_ASYNC_STATE_RX_RBUF_USED_INDEX_BIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void modem_backend_uart_async_rx_rbuf_used_swap(struct modem_backend_uart *backend)
|
||||||
|
{
|
||||||
|
uint8_t rx_rbuf_index = modem_backend_uart_async_rx_rbuf_used_index(backend);
|
||||||
|
|
||||||
|
if (rx_rbuf_index) {
|
||||||
|
atomic_clear_bit(&backend->async.state,
|
||||||
|
MODEM_BACKEND_UART_ASYNC_STATE_RX_RBUF_USED_INDEX_BIT);
|
||||||
|
} else {
|
||||||
|
atomic_set_bit(&backend->async.state,
|
||||||
|
MODEM_BACKEND_UART_ASYNC_STATE_RX_RBUF_USED_INDEX_BIT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void modem_backend_uart_async_event_handler(const struct device *dev,
|
||||||
|
struct uart_event *evt, void *user_data)
|
||||||
|
{
|
||||||
|
struct modem_backend_uart *backend = (struct modem_backend_uart *) user_data;
|
||||||
|
|
||||||
|
uint8_t receive_rb_used_index;
|
||||||
|
uint32_t received;
|
||||||
|
|
||||||
|
switch (evt->type) {
|
||||||
|
case UART_TX_DONE:
|
||||||
|
atomic_clear_bit(&backend->async.state,
|
||||||
|
MODEM_BACKEND_UART_ASYNC_STATE_TRANSMITTING_BIT);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UART_RX_BUF_REQUEST:
|
||||||
|
if (!atomic_test_and_set_bit(&backend->async.state,
|
||||||
|
MODEM_BACKEND_UART_ASYNC_STATE_RX_BUF0_USED_BIT)) {
|
||||||
|
uart_rx_buf_rsp(backend->uart, backend->async.receive_bufs[0],
|
||||||
|
backend->async.receive_buf_size);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!atomic_test_and_set_bit(&backend->async.state,
|
||||||
|
MODEM_BACKEND_UART_ASYNC_STATE_RX_BUF1_USED_BIT)) {
|
||||||
|
uart_rx_buf_rsp(backend->uart, backend->async.receive_bufs[1],
|
||||||
|
backend->async.receive_buf_size);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_WRN("No receive buffer available");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UART_RX_BUF_RELEASED:
|
||||||
|
if (evt->data.rx_buf.buf == backend->async.receive_bufs[0]) {
|
||||||
|
atomic_clear_bit(&backend->async.state,
|
||||||
|
MODEM_BACKEND_UART_ASYNC_STATE_RX_BUF0_USED_BIT);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (evt->data.rx_buf.buf == backend->async.receive_bufs[1]) {
|
||||||
|
atomic_clear_bit(&backend->async.state,
|
||||||
|
MODEM_BACKEND_UART_ASYNC_STATE_RX_BUF1_USED_BIT);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_WRN("Unknown receive buffer released");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UART_RX_RDY:
|
||||||
|
receive_rb_used_index = modem_backend_uart_async_rx_rbuf_used_index(backend);
|
||||||
|
|
||||||
|
received = ring_buf_put(&backend->async.receive_rdb[receive_rb_used_index],
|
||||||
|
&evt->data.rx.buf[evt->data.rx.offset],
|
||||||
|
evt->data.rx.len);
|
||||||
|
|
||||||
|
if (received < evt->data.rx.len) {
|
||||||
|
ring_buf_reset(&backend->async.receive_rdb[receive_rb_used_index]);
|
||||||
|
LOG_WRN("Receive buffer overrun");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
k_work_submit(&backend->receive_ready_work);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UART_TX_ABORTED:
|
||||||
|
LOG_WRN("Transmit aborted");
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int modem_backend_uart_async_open(void *data)
|
||||||
|
{
|
||||||
|
struct modem_backend_uart *backend = (struct modem_backend_uart *)data;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
atomic_set(&backend->async.state, 0);
|
||||||
|
modem_backend_uart_async_flush(backend);
|
||||||
|
ring_buf_reset(&backend->async.receive_rdb[0]);
|
||||||
|
ring_buf_reset(&backend->async.receive_rdb[1]);
|
||||||
|
|
||||||
|
/* Reserve receive buffer 0 */
|
||||||
|
atomic_set_bit(&backend->async.state,
|
||||||
|
MODEM_BACKEND_UART_ASYNC_STATE_RX_BUF0_USED_BIT);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Receive buffer 0 is used internally by UART, receive ring buffer 0 is
|
||||||
|
* used to store received data.
|
||||||
|
*/
|
||||||
|
ret = uart_rx_enable(backend->uart, backend->async.receive_bufs[0],
|
||||||
|
backend->async.receive_buf_size, 3000);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
modem_pipe_notify_opened(&backend->pipe);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int modem_backend_uart_async_transmit(void *data, const uint8_t *buf, size_t size)
|
||||||
|
{
|
||||||
|
struct modem_backend_uart *backend = (struct modem_backend_uart *)data;
|
||||||
|
bool transmitting;
|
||||||
|
uint32_t bytes_to_transmit;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
transmitting = atomic_test_and_set_bit(&backend->async.state,
|
||||||
|
MODEM_BACKEND_UART_ASYNC_STATE_TRANSMITTING_BIT);
|
||||||
|
|
||||||
|
if (transmitting) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Determine amount of bytes to transmit */
|
||||||
|
bytes_to_transmit = (size < backend->async.transmit_buf_size)
|
||||||
|
? size
|
||||||
|
: backend->async.transmit_buf_size;
|
||||||
|
|
||||||
|
/* Copy buf to transmit buffer which is passed to UART */
|
||||||
|
memcpy(backend->async.transmit_buf, buf, bytes_to_transmit);
|
||||||
|
|
||||||
|
ret = uart_tx(backend->uart, backend->async.transmit_buf, bytes_to_transmit,
|
||||||
|
SYS_FOREVER_US);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
LOG_WRN("Failed to start async transmit");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)bytes_to_transmit;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int modem_backend_uart_async_receive(void *data, uint8_t *buf, size_t size)
|
||||||
|
{
|
||||||
|
struct modem_backend_uart *backend = (struct modem_backend_uart *)data;
|
||||||
|
|
||||||
|
uint32_t received;
|
||||||
|
uint8_t receive_rdb_unused;
|
||||||
|
|
||||||
|
received = 0;
|
||||||
|
receive_rdb_unused = modem_backend_uart_async_rx_rbuf_used_index(backend) ? 0 : 1;
|
||||||
|
|
||||||
|
/* Read data from unused ring double buffer first */
|
||||||
|
received += ring_buf_get(&backend->async.receive_rdb[receive_rdb_unused], buf, size);
|
||||||
|
|
||||||
|
if (ring_buf_is_empty(&backend->async.receive_rdb[receive_rdb_unused]) == false) {
|
||||||
|
return (int)received;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Swap receive ring double buffer */
|
||||||
|
modem_backend_uart_async_rx_rbuf_used_swap(backend);
|
||||||
|
|
||||||
|
/* Read data from previously used buffer */
|
||||||
|
receive_rdb_unused = modem_backend_uart_async_rx_rbuf_used_index(backend) ? 0 : 1;
|
||||||
|
|
||||||
|
received += ring_buf_get(&backend->async.receive_rdb[receive_rdb_unused],
|
||||||
|
&buf[received], (size - received));
|
||||||
|
|
||||||
|
return (int)received;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int modem_backend_uart_async_close(void *data)
|
||||||
|
{
|
||||||
|
struct modem_backend_uart *backend = (struct modem_backend_uart *)data;
|
||||||
|
|
||||||
|
uart_rx_disable(backend->uart);
|
||||||
|
modem_pipe_notify_closed(&backend->pipe);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct modem_pipe_api modem_backend_uart_async_api = {
|
||||||
|
.open = modem_backend_uart_async_open,
|
||||||
|
.transmit = modem_backend_uart_async_transmit,
|
||||||
|
.receive = modem_backend_uart_async_receive,
|
||||||
|
.close = modem_backend_uart_async_close,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool modem_backend_uart_async_is_supported(struct modem_backend_uart *backend)
|
||||||
|
{
|
||||||
|
return uart_callback_set(backend->uart, modem_backend_uart_async_event_handler,
|
||||||
|
backend) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void modem_backend_uart_async_init(struct modem_backend_uart *backend,
|
||||||
|
const struct modem_backend_uart_config *config)
|
||||||
|
{
|
||||||
|
uint32_t receive_buf_size_quarter = config->receive_buf_size / 4;
|
||||||
|
|
||||||
|
/* Split receive buffer into 4 buffers, use 2 parts for UART receive double buffer */
|
||||||
|
backend->async.receive_buf_size = receive_buf_size_quarter;
|
||||||
|
backend->async.receive_bufs[0] = &config->receive_buf[0];
|
||||||
|
backend->async.receive_bufs[1] = &config->receive_buf[receive_buf_size_quarter];
|
||||||
|
|
||||||
|
/* Use remaining 2 parts for receive double ring buffer */
|
||||||
|
ring_buf_init(&backend->async.receive_rdb[0], receive_buf_size_quarter,
|
||||||
|
&config->receive_buf[receive_buf_size_quarter * 2]);
|
||||||
|
|
||||||
|
ring_buf_init(&backend->async.receive_rdb[1], receive_buf_size_quarter,
|
||||||
|
&config->receive_buf[receive_buf_size_quarter * 3]);
|
||||||
|
|
||||||
|
backend->async.transmit_buf = config->transmit_buf;
|
||||||
|
backend->async.transmit_buf_size = config->transmit_buf_size;
|
||||||
|
modem_pipe_init(&backend->pipe, backend, &modem_backend_uart_async_api);
|
||||||
|
}
|
25
subsys/modem/backends/modem_backend_uart_async.h
Normal file
25
subsys/modem/backends/modem_backend_uart_async.h
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Trackunit Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/modem/backend/uart.h>
|
||||||
|
|
||||||
|
#ifndef ZEPHYR_MODEM_BACKEND_UART_ASYNC_
|
||||||
|
#define ZEPHYR_MODEM_BACKEND_UART_ASYNC_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool modem_backend_uart_async_is_supported(struct modem_backend_uart *backend);
|
||||||
|
|
||||||
|
void modem_backend_uart_async_init(struct modem_backend_uart *backend,
|
||||||
|
const struct modem_backend_uart_config *config);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* ZEPHYR_MODEM_BACKEND_UART_ASYNC_ */
|
203
subsys/modem/backends/modem_backend_uart_isr.c
Normal file
203
subsys/modem/backends/modem_backend_uart_isr.c
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Trackunit Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "modem_backend_uart_isr.h"
|
||||||
|
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
LOG_MODULE_DECLARE(modem_backend_uart);
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static void modem_backend_uart_isr_flush(struct modem_backend_uart *backend)
|
||||||
|
{
|
||||||
|
uint8_t c;
|
||||||
|
|
||||||
|
while (uart_fifo_read(backend->uart, &c, 1) > 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void modem_backend_uart_isr_irq_handler_receive_ready(struct modem_backend_uart *backend)
|
||||||
|
{
|
||||||
|
uint32_t size;
|
||||||
|
struct ring_buf *receive_rb;
|
||||||
|
uint8_t *buffer;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
receive_rb = &backend->isr.receive_rdb[backend->isr.receive_rdb_used];
|
||||||
|
size = ring_buf_put_claim(receive_rb, &buffer, UINT32_MAX);
|
||||||
|
if (size == 0) {
|
||||||
|
LOG_WRN("Receive buffer overrun");
|
||||||
|
ring_buf_put_finish(receive_rb, 0);
|
||||||
|
ring_buf_reset(receive_rb);
|
||||||
|
size = ring_buf_put_claim(receive_rb, &buffer, UINT32_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = uart_fifo_read(backend->uart, buffer, size);
|
||||||
|
if (ret < 0) {
|
||||||
|
ring_buf_put_finish(receive_rb, 0);
|
||||||
|
} else {
|
||||||
|
ring_buf_put_finish(receive_rb, (uint32_t)ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret > 0) {
|
||||||
|
k_work_submit(&backend->receive_ready_work);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void modem_backend_uart_isr_irq_handler_transmit_ready(struct modem_backend_uart *backend)
|
||||||
|
{
|
||||||
|
uint32_t size;
|
||||||
|
uint8_t *buffer;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (ring_buf_is_empty(&backend->isr.transmit_rb) == true) {
|
||||||
|
uart_irq_tx_disable(backend->uart);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size = ring_buf_get_claim(&backend->isr.transmit_rb, &buffer, UINT32_MAX);
|
||||||
|
ret = uart_fifo_fill(backend->uart, buffer, size);
|
||||||
|
if (ret < 0) {
|
||||||
|
ring_buf_get_finish(&backend->isr.transmit_rb, 0);
|
||||||
|
} else {
|
||||||
|
ring_buf_get_finish(&backend->isr.transmit_rb, (uint32_t)ret);
|
||||||
|
|
||||||
|
/* Update transmit buf capacity tracker */
|
||||||
|
atomic_sub(&backend->isr.transmit_buf_len, (uint32_t)ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void modem_backend_uart_isr_irq_handler(const struct device *uart, void *user_data)
|
||||||
|
{
|
||||||
|
struct modem_backend_uart *backend = (struct modem_backend_uart *)user_data;
|
||||||
|
|
||||||
|
if (uart_irq_update(uart) < 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uart_irq_rx_ready(uart)) {
|
||||||
|
modem_backend_uart_isr_irq_handler_receive_ready(backend);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uart_irq_tx_ready(uart)) {
|
||||||
|
modem_backend_uart_isr_irq_handler_transmit_ready(backend);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int modem_backend_uart_isr_open(void *data)
|
||||||
|
{
|
||||||
|
struct modem_backend_uart *backend = (struct modem_backend_uart *)data;
|
||||||
|
|
||||||
|
ring_buf_reset(&backend->isr.receive_rdb[0]);
|
||||||
|
ring_buf_reset(&backend->isr.receive_rdb[1]);
|
||||||
|
ring_buf_reset(&backend->isr.transmit_rb);
|
||||||
|
atomic_set(&backend->isr.transmit_buf_len, 0);
|
||||||
|
modem_backend_uart_isr_flush(backend);
|
||||||
|
uart_irq_rx_enable(backend->uart);
|
||||||
|
uart_irq_tx_enable(backend->uart);
|
||||||
|
modem_pipe_notify_opened(&backend->pipe);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool modem_backend_uart_isr_transmit_buf_above_limit(struct modem_backend_uart *backend)
|
||||||
|
{
|
||||||
|
return backend->isr.transmit_buf_put_limit < atomic_get(&backend->isr.transmit_buf_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int modem_backend_uart_isr_transmit(void *data, const uint8_t *buf, size_t size)
|
||||||
|
{
|
||||||
|
struct modem_backend_uart *backend = (struct modem_backend_uart *)data;
|
||||||
|
int written;
|
||||||
|
|
||||||
|
if (modem_backend_uart_isr_transmit_buf_above_limit(backend) == true) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uart_irq_tx_disable(backend->uart);
|
||||||
|
written = ring_buf_put(&backend->isr.transmit_rb, buf, size);
|
||||||
|
uart_irq_tx_enable(backend->uart);
|
||||||
|
|
||||||
|
/* Update transmit buf capacity tracker */
|
||||||
|
atomic_add(&backend->isr.transmit_buf_len, written);
|
||||||
|
return written;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int modem_backend_uart_isr_receive(void *data, uint8_t *buf, size_t size)
|
||||||
|
{
|
||||||
|
struct modem_backend_uart *backend = (struct modem_backend_uart *)data;
|
||||||
|
|
||||||
|
uint32_t read_bytes;
|
||||||
|
uint8_t receive_rdb_unused;
|
||||||
|
|
||||||
|
read_bytes = 0;
|
||||||
|
receive_rdb_unused = (backend->isr.receive_rdb_used == 1) ? 0 : 1;
|
||||||
|
|
||||||
|
/* Read data from unused ring double buffer first */
|
||||||
|
read_bytes += ring_buf_get(&backend->isr.receive_rdb[receive_rdb_unused], buf, size);
|
||||||
|
|
||||||
|
if (ring_buf_is_empty(&backend->isr.receive_rdb[receive_rdb_unused]) == false) {
|
||||||
|
return (int)read_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Swap receive ring double buffer */
|
||||||
|
uart_irq_rx_disable(backend->uart);
|
||||||
|
backend->isr.receive_rdb_used = receive_rdb_unused;
|
||||||
|
uart_irq_rx_enable(backend->uart);
|
||||||
|
|
||||||
|
/* Read data from previously used buffer */
|
||||||
|
receive_rdb_unused = (backend->isr.receive_rdb_used == 1) ? 0 : 1;
|
||||||
|
|
||||||
|
read_bytes += ring_buf_get(&backend->isr.receive_rdb[receive_rdb_unused],
|
||||||
|
&buf[read_bytes], (size - read_bytes));
|
||||||
|
|
||||||
|
return (int)read_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int modem_backend_uart_isr_close(void *data)
|
||||||
|
{
|
||||||
|
struct modem_backend_uart *backend = (struct modem_backend_uart *)data;
|
||||||
|
|
||||||
|
uart_irq_rx_disable(backend->uart);
|
||||||
|
uart_irq_tx_disable(backend->uart);
|
||||||
|
modem_pipe_notify_closed(&backend->pipe);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct modem_pipe_api modem_backend_uart_isr_api = {
|
||||||
|
.open = modem_backend_uart_isr_open,
|
||||||
|
.transmit = modem_backend_uart_isr_transmit,
|
||||||
|
.receive = modem_backend_uart_isr_receive,
|
||||||
|
.close = modem_backend_uart_isr_close,
|
||||||
|
};
|
||||||
|
|
||||||
|
void modem_backend_uart_isr_init(struct modem_backend_uart *backend,
|
||||||
|
const struct modem_backend_uart_config *config)
|
||||||
|
{
|
||||||
|
uint32_t receive_double_buf_size;
|
||||||
|
|
||||||
|
backend->isr.transmit_buf_put_limit =
|
||||||
|
config->transmit_buf_size - (config->transmit_buf_size / 4);
|
||||||
|
|
||||||
|
receive_double_buf_size = config->receive_buf_size / 2;
|
||||||
|
|
||||||
|
ring_buf_init(&backend->isr.receive_rdb[0], receive_double_buf_size,
|
||||||
|
&config->receive_buf[0]);
|
||||||
|
|
||||||
|
ring_buf_init(&backend->isr.receive_rdb[1], receive_double_buf_size,
|
||||||
|
&config->receive_buf[receive_double_buf_size]);
|
||||||
|
|
||||||
|
ring_buf_init(&backend->isr.transmit_rb, config->transmit_buf_size,
|
||||||
|
config->transmit_buf);
|
||||||
|
|
||||||
|
atomic_set(&backend->isr.transmit_buf_len, 0);
|
||||||
|
uart_irq_rx_disable(backend->uart);
|
||||||
|
uart_irq_tx_disable(backend->uart);
|
||||||
|
uart_irq_callback_user_data_set(backend->uart, modem_backend_uart_isr_irq_handler,
|
||||||
|
backend);
|
||||||
|
|
||||||
|
modem_pipe_init(&backend->pipe, backend, &modem_backend_uart_isr_api);
|
||||||
|
}
|
23
subsys/modem/backends/modem_backend_uart_isr.h
Normal file
23
subsys/modem/backends/modem_backend_uart_isr.h
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Trackunit Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/modem/backend/uart.h>
|
||||||
|
|
||||||
|
#ifndef ZEPHYR_MODEM_BACKEND_UART_ISR_
|
||||||
|
#define ZEPHYR_MODEM_BACKEND_UART_ISR_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void modem_backend_uart_isr_init(struct modem_backend_uart *backend,
|
||||||
|
const struct modem_backend_uart_config *config);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* ZEPHYR_MODEM_BACKEND_UART_ISR_ */
|
788
subsys/modem/modem_chat.c
Normal file
788
subsys/modem/modem_chat.c
Normal file
|
@ -0,0 +1,788 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Trackunit Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(modem_chat, CONFIG_MODEM_MODULES_LOG_LEVEL);
|
||||||
|
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <zephyr/modem/chat.h>
|
||||||
|
|
||||||
|
#define MODEM_CHAT_MATCHES_INDEX_RESPONSE (0)
|
||||||
|
#define MODEM_CHAT_MATCHES_INDEX_ABORT (1)
|
||||||
|
#define MODEM_CHAT_MATCHES_INDEX_UNSOL (2)
|
||||||
|
|
||||||
|
#define MODEM_CHAT_SCRIPT_STATE_RUNNING_BIT (0)
|
||||||
|
|
||||||
|
#if defined(CONFIG_LOG) && (CONFIG_MODEM_MODULES_LOG_LEVEL == LOG_LEVEL_DBG)
|
||||||
|
|
||||||
|
static char log_buffer[CONFIG_MODEM_CHAT_LOG_BUFFER];
|
||||||
|
|
||||||
|
static void modem_chat_log_received_command(struct modem_chat *chat)
|
||||||
|
{
|
||||||
|
uint16_t log_buffer_pos = 0;
|
||||||
|
uint16_t argv_len;
|
||||||
|
|
||||||
|
for (uint16_t i = 0; i < chat->argc; i++) {
|
||||||
|
argv_len = (uint16_t)strlen(chat->argv[i]);
|
||||||
|
|
||||||
|
/* Validate argument fits in log buffer including termination */
|
||||||
|
if (sizeof(log_buffer) < (log_buffer_pos + argv_len + 1)) {
|
||||||
|
LOG_WRN("log buffer overrun");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy argument and append space */
|
||||||
|
memcpy(&log_buffer[log_buffer_pos], chat->argv[i], argv_len);
|
||||||
|
log_buffer_pos += argv_len;
|
||||||
|
log_buffer[log_buffer_pos] = ' ';
|
||||||
|
log_buffer_pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Terminate line after last argument, overwriting trailing space */
|
||||||
|
log_buffer_pos = log_buffer_pos == 0 ? log_buffer_pos : log_buffer_pos - 1;
|
||||||
|
log_buffer[log_buffer_pos] = '\0';
|
||||||
|
|
||||||
|
LOG_DBG("%s", log_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
static void modem_chat_log_received_command(struct modem_chat *chat)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void modem_chat_script_stop(struct modem_chat *chat, enum modem_chat_script_result result)
|
||||||
|
{
|
||||||
|
/* Handle result */
|
||||||
|
if (result == MODEM_CHAT_SCRIPT_RESULT_SUCCESS) {
|
||||||
|
LOG_DBG("%s: complete", chat->script->name);
|
||||||
|
} else if (result == MODEM_CHAT_SCRIPT_RESULT_ABORT) {
|
||||||
|
LOG_WRN("%s: aborted", chat->script->name);
|
||||||
|
} else {
|
||||||
|
LOG_WRN("%s: timed out", chat->script->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear script running state */
|
||||||
|
atomic_clear_bit(&chat->script_state, MODEM_CHAT_SCRIPT_STATE_RUNNING_BIT);
|
||||||
|
|
||||||
|
/* Call back with result */
|
||||||
|
if (chat->script->callback != NULL) {
|
||||||
|
chat->script->callback(chat, result, chat->user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear reference to script */
|
||||||
|
chat->script = NULL;
|
||||||
|
|
||||||
|
/* Clear response and abort commands */
|
||||||
|
chat->matches[MODEM_CHAT_MATCHES_INDEX_ABORT] = NULL;
|
||||||
|
chat->matches_size[MODEM_CHAT_MATCHES_INDEX_ABORT] = 0;
|
||||||
|
chat->matches[MODEM_CHAT_MATCHES_INDEX_RESPONSE] = NULL;
|
||||||
|
chat->matches_size[MODEM_CHAT_MATCHES_INDEX_RESPONSE] = 0;
|
||||||
|
|
||||||
|
/* Cancel timeout work */
|
||||||
|
k_work_cancel_delayable(&chat->script_timeout_work);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void modem_chat_script_send(struct modem_chat *chat)
|
||||||
|
{
|
||||||
|
/* Initialize script send work */
|
||||||
|
chat->script_send_request_pos = 0;
|
||||||
|
chat->script_send_delimiter_pos = 0;
|
||||||
|
|
||||||
|
/* Schedule script send work */
|
||||||
|
k_work_schedule(&chat->script_send_work, K_NO_WAIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void modem_chat_script_next(struct modem_chat *chat, bool initial)
|
||||||
|
{
|
||||||
|
const struct modem_chat_script_chat *script_chat;
|
||||||
|
|
||||||
|
/* Advance iterator if not initial */
|
||||||
|
if (initial == true) {
|
||||||
|
/* Reset iterator */
|
||||||
|
chat->script_chat_it = 0;
|
||||||
|
} else {
|
||||||
|
/* Advance iterator */
|
||||||
|
chat->script_chat_it++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if end of script reached */
|
||||||
|
if (chat->script_chat_it == chat->script->script_chats_size) {
|
||||||
|
modem_chat_script_stop(chat, MODEM_CHAT_SCRIPT_RESULT_SUCCESS);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("%s: step: %u", chat->script->name, chat->script_chat_it);
|
||||||
|
|
||||||
|
script_chat = &chat->script->script_chats[chat->script_chat_it];
|
||||||
|
|
||||||
|
/* Set response command handlers */
|
||||||
|
chat->matches[MODEM_CHAT_MATCHES_INDEX_RESPONSE] = script_chat->response_matches;
|
||||||
|
chat->matches_size[MODEM_CHAT_MATCHES_INDEX_RESPONSE] = script_chat->response_matches_size;
|
||||||
|
|
||||||
|
/* Check if work must be sent */
|
||||||
|
if (strlen(script_chat->request) > 0) {
|
||||||
|
LOG_DBG("sending: %s", script_chat->request);
|
||||||
|
modem_chat_script_send(chat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void modem_chat_script_start(struct modem_chat *chat, const struct modem_chat_script *script)
|
||||||
|
{
|
||||||
|
/* Save script */
|
||||||
|
chat->script = script;
|
||||||
|
|
||||||
|
/* Set abort matches */
|
||||||
|
chat->matches[MODEM_CHAT_MATCHES_INDEX_ABORT] = script->abort_matches;
|
||||||
|
chat->matches_size[MODEM_CHAT_MATCHES_INDEX_ABORT] = script->abort_matches_size;
|
||||||
|
|
||||||
|
LOG_DBG("running script: %s", chat->script->name);
|
||||||
|
|
||||||
|
/* Set first script command */
|
||||||
|
modem_chat_script_next(chat, true);
|
||||||
|
|
||||||
|
/* Start timeout work if script started */
|
||||||
|
if (chat->script != NULL) {
|
||||||
|
k_work_schedule(&chat->script_timeout_work, K_SECONDS(chat->script->timeout));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void modem_chat_script_run_handler(struct k_work *item)
|
||||||
|
{
|
||||||
|
struct modem_chat *chat = CONTAINER_OF(item, struct modem_chat, script_run_work);
|
||||||
|
|
||||||
|
/* Start script */
|
||||||
|
modem_chat_script_start(chat, chat->pending_script);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void modem_chat_script_timeout_handler(struct k_work *item)
|
||||||
|
{
|
||||||
|
struct modem_chat *chat = CONTAINER_OF(item, struct modem_chat, script_timeout_work);
|
||||||
|
|
||||||
|
/* Abort script */
|
||||||
|
modem_chat_script_stop(chat, MODEM_CHAT_SCRIPT_RESULT_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void modem_chat_script_abort_handler(struct k_work *item)
|
||||||
|
{
|
||||||
|
struct modem_chat *chat = CONTAINER_OF(item, struct modem_chat, script_abort_work);
|
||||||
|
|
||||||
|
/* Validate script is currently running */
|
||||||
|
if (chat->script == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Abort script */
|
||||||
|
modem_chat_script_stop(chat, MODEM_CHAT_SCRIPT_RESULT_ABORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool modem_chat_script_send_request(struct modem_chat *chat)
|
||||||
|
{
|
||||||
|
const struct modem_chat_script_chat *script_chat =
|
||||||
|
&chat->script->script_chats[chat->script_chat_it];
|
||||||
|
|
||||||
|
uint16_t script_chat_request_size = strlen(script_chat->request);
|
||||||
|
uint8_t *script_chat_request_start;
|
||||||
|
uint16_t script_chat_request_remaining;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Validate data to send */
|
||||||
|
if (script_chat_request_size == chat->script_send_request_pos) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
script_chat_request_start = (uint8_t *)&script_chat->request[chat->script_send_request_pos];
|
||||||
|
script_chat_request_remaining = script_chat_request_size - chat->script_send_request_pos;
|
||||||
|
|
||||||
|
/* Send data through pipe */
|
||||||
|
ret = modem_pipe_transmit(chat->pipe, script_chat_request_start,
|
||||||
|
script_chat_request_remaining);
|
||||||
|
|
||||||
|
/* Validate transmit successful */
|
||||||
|
if (ret < 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update script send position */
|
||||||
|
chat->script_send_request_pos += (uint16_t)ret;
|
||||||
|
|
||||||
|
/* Check if data remains */
|
||||||
|
if (chat->script_send_request_pos < script_chat_request_size) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool modem_chat_script_send_delimiter(struct modem_chat *chat)
|
||||||
|
{
|
||||||
|
uint8_t *script_chat_delimiter_start;
|
||||||
|
uint8_t script_chat_delimiter_remaining;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Validate data to send */
|
||||||
|
if (chat->delimiter_size == chat->script_send_delimiter_pos) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
script_chat_delimiter_start = (uint8_t *)&chat->delimiter[chat->script_send_delimiter_pos];
|
||||||
|
script_chat_delimiter_remaining = chat->delimiter_size - chat->script_send_delimiter_pos;
|
||||||
|
|
||||||
|
/* Send data through pipe */
|
||||||
|
ret = modem_pipe_transmit(chat->pipe, script_chat_delimiter_start,
|
||||||
|
script_chat_delimiter_remaining);
|
||||||
|
|
||||||
|
/* Validate transmit successful */
|
||||||
|
if (ret < 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update script send position */
|
||||||
|
chat->script_send_delimiter_pos += (uint8_t)ret;
|
||||||
|
|
||||||
|
/* Check if data remains */
|
||||||
|
if (chat->script_send_delimiter_pos < chat->delimiter_size) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool modem_chat_script_chat_is_no_response(struct modem_chat *chat)
|
||||||
|
{
|
||||||
|
const struct modem_chat_script_chat *script_chat =
|
||||||
|
&chat->script->script_chats[chat->script_chat_it];
|
||||||
|
|
||||||
|
return (script_chat->response_matches_size == 0) ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t modem_chat_script_chat_get_send_timeout(struct modem_chat *chat)
|
||||||
|
{
|
||||||
|
const struct modem_chat_script_chat *script_chat =
|
||||||
|
&chat->script->script_chats[chat->script_chat_it];
|
||||||
|
|
||||||
|
return script_chat->timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void modem_chat_script_send_handler(struct k_work *item)
|
||||||
|
{
|
||||||
|
struct modem_chat *chat = CONTAINER_OF(item, struct modem_chat, script_send_work);
|
||||||
|
uint16_t timeout;
|
||||||
|
|
||||||
|
/* Validate script running */
|
||||||
|
if (chat->script == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send request */
|
||||||
|
if (modem_chat_script_send_request(chat) == false) {
|
||||||
|
k_work_schedule(&chat->script_send_work, chat->process_timeout);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send delimiter */
|
||||||
|
if (modem_chat_script_send_delimiter(chat) == false) {
|
||||||
|
k_work_schedule(&chat->script_send_work, chat->process_timeout);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if script command is no response */
|
||||||
|
if (modem_chat_script_chat_is_no_response(chat)) {
|
||||||
|
timeout = modem_chat_script_chat_get_send_timeout(chat);
|
||||||
|
|
||||||
|
if (timeout == 0) {
|
||||||
|
modem_chat_script_next(chat, false);
|
||||||
|
} else {
|
||||||
|
k_work_schedule(&chat->script_send_timeout_work, K_MSEC(timeout));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void modem_chat_script_send_timeout_handler(struct k_work *item)
|
||||||
|
{
|
||||||
|
struct modem_chat *chat = CONTAINER_OF(item, struct modem_chat, script_send_timeout_work);
|
||||||
|
|
||||||
|
/* Validate script is currently running */
|
||||||
|
if (chat->script == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
modem_chat_script_next(chat, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void modem_chat_parse_reset(struct modem_chat *chat)
|
||||||
|
{
|
||||||
|
/* Reset parameters used for parsing */
|
||||||
|
chat->receive_buf_len = 0;
|
||||||
|
chat->delimiter_match_len = 0;
|
||||||
|
chat->argc = 0;
|
||||||
|
chat->parse_match = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Exact match is stored at end of receive buffer */
|
||||||
|
static void modem_chat_parse_save_match(struct modem_chat *chat)
|
||||||
|
{
|
||||||
|
uint8_t *argv;
|
||||||
|
|
||||||
|
/* Store length of match including NULL to avoid overwriting it if buffer overruns */
|
||||||
|
chat->parse_match_len = chat->receive_buf_len + 1;
|
||||||
|
|
||||||
|
/* Copy match to end of receive buffer */
|
||||||
|
argv = &chat->receive_buf[chat->receive_buf_size - chat->parse_match_len];
|
||||||
|
|
||||||
|
/* Copy match to end of receive buffer (excluding NULL) */
|
||||||
|
memcpy(argv, &chat->receive_buf[0], chat->parse_match_len - 1);
|
||||||
|
|
||||||
|
/* Save match */
|
||||||
|
chat->argv[chat->argc] = argv;
|
||||||
|
|
||||||
|
/* Terminate match */
|
||||||
|
chat->receive_buf[chat->receive_buf_size - 1] = '\0';
|
||||||
|
|
||||||
|
/* Increment argument count */
|
||||||
|
chat->argc++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool modem_chat_match_matches_received(struct modem_chat *chat,
|
||||||
|
const struct modem_chat_match *match)
|
||||||
|
{
|
||||||
|
for (uint16_t i = 0; i < match->match_size; i++) {
|
||||||
|
if ((match->match[i] == chat->receive_buf[i]) ||
|
||||||
|
(match->wildcards == true && match->match[i] == '?')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool modem_chat_parse_find_match(struct modem_chat *chat)
|
||||||
|
{
|
||||||
|
/* Find in all matches types */
|
||||||
|
for (uint16_t i = 0; i < ARRAY_SIZE(chat->matches); i++) {
|
||||||
|
/* Find in all matches of matches type */
|
||||||
|
for (uint16_t u = 0; u < chat->matches_size[i]; u++) {
|
||||||
|
/* Validate match size matches received data length */
|
||||||
|
if (chat->matches[i][u].match_size != chat->receive_buf_len) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate match */
|
||||||
|
if (modem_chat_match_matches_received(chat, &chat->matches[i][u]) ==
|
||||||
|
false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Complete match found */
|
||||||
|
chat->parse_match = &chat->matches[i][u];
|
||||||
|
chat->parse_match_type = i;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool modem_chat_parse_is_separator(struct modem_chat *chat)
|
||||||
|
{
|
||||||
|
for (uint16_t i = 0; i < chat->parse_match->separators_size; i++) {
|
||||||
|
if ((chat->parse_match->separators[i]) ==
|
||||||
|
(chat->receive_buf[chat->receive_buf_len - 1])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool modem_chat_parse_end_del_start(struct modem_chat *chat)
|
||||||
|
{
|
||||||
|
for (uint8_t i = 0; i < chat->delimiter_size; i++) {
|
||||||
|
if (chat->receive_buf[chat->receive_buf_len - 1] == chat->delimiter[i]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool modem_chat_parse_end_del_complete(struct modem_chat *chat)
|
||||||
|
{
|
||||||
|
/* Validate length of end delimiter */
|
||||||
|
if (chat->receive_buf_len < chat->delimiter_size) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compare end delimiter with receive buffer content */
|
||||||
|
return (memcmp(&chat->receive_buf[chat->receive_buf_len - chat->delimiter_size],
|
||||||
|
chat->delimiter, chat->delimiter_size) == 0)
|
||||||
|
? true
|
||||||
|
: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void modem_chat_on_command_received_unsol(struct modem_chat *chat)
|
||||||
|
{
|
||||||
|
/* Callback */
|
||||||
|
if (chat->parse_match->callback != NULL) {
|
||||||
|
chat->parse_match->callback(chat, (char **)chat->argv, chat->argc, chat->user_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void modem_chat_on_command_received_abort(struct modem_chat *chat)
|
||||||
|
{
|
||||||
|
/* Callback */
|
||||||
|
if (chat->parse_match->callback != NULL) {
|
||||||
|
chat->parse_match->callback(chat, (char **)chat->argv, chat->argc, chat->user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Abort script */
|
||||||
|
modem_chat_script_stop(chat, MODEM_CHAT_SCRIPT_RESULT_ABORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void modem_chat_on_command_received_resp(struct modem_chat *chat)
|
||||||
|
{
|
||||||
|
/* Callback */
|
||||||
|
if (chat->parse_match->callback != NULL) {
|
||||||
|
chat->parse_match->callback(chat, (char **)chat->argv, chat->argc, chat->user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Advance script */
|
||||||
|
modem_chat_script_next(chat, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool modem_chat_parse_find_catch_all_match(struct modem_chat *chat)
|
||||||
|
{
|
||||||
|
/* Find in all matches types */
|
||||||
|
for (uint16_t i = 0; i < ARRAY_SIZE(chat->matches); i++) {
|
||||||
|
/* Find in all matches of matches type */
|
||||||
|
for (uint16_t u = 0; u < chat->matches_size[i]; u++) {
|
||||||
|
/* Validate match config is matching previous bytes */
|
||||||
|
if (chat->matches[i][u].match_size == 0) {
|
||||||
|
chat->parse_match = &chat->matches[i][u];
|
||||||
|
chat->parse_match_type = i;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void modem_chat_on_command_received(struct modem_chat *chat)
|
||||||
|
{
|
||||||
|
modem_chat_log_received_command(chat);
|
||||||
|
|
||||||
|
switch (chat->parse_match_type) {
|
||||||
|
case MODEM_CHAT_MATCHES_INDEX_UNSOL:
|
||||||
|
modem_chat_on_command_received_unsol(chat);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MODEM_CHAT_MATCHES_INDEX_ABORT:
|
||||||
|
modem_chat_on_command_received_abort(chat);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MODEM_CHAT_MATCHES_INDEX_RESPONSE:
|
||||||
|
modem_chat_on_command_received_resp(chat);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void modem_chat_on_unknown_command_received(struct modem_chat *chat)
|
||||||
|
{
|
||||||
|
/* Terminate received command */
|
||||||
|
chat->receive_buf[chat->receive_buf_len - chat->delimiter_size] = '\0';
|
||||||
|
|
||||||
|
/* Try to find catch all match */
|
||||||
|
if (modem_chat_parse_find_catch_all_match(chat) == false) {
|
||||||
|
LOG_DBG("%s", chat->receive_buf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse command */
|
||||||
|
chat->argv[0] = "";
|
||||||
|
chat->argv[1] = chat->receive_buf;
|
||||||
|
chat->argc = 2;
|
||||||
|
|
||||||
|
modem_chat_on_command_received(chat);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void modem_chat_process_byte(struct modem_chat *chat, uint8_t byte)
|
||||||
|
{
|
||||||
|
/* Validate receive buffer not overrun */
|
||||||
|
if (chat->receive_buf_size == chat->receive_buf_len) {
|
||||||
|
LOG_WRN("receive buffer overrun");
|
||||||
|
modem_chat_parse_reset(chat);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate argv buffer not overrun */
|
||||||
|
if (chat->argc == chat->argv_size) {
|
||||||
|
LOG_WRN("argv buffer overrun");
|
||||||
|
modem_chat_parse_reset(chat);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy byte to receive buffer */
|
||||||
|
chat->receive_buf[chat->receive_buf_len] = byte;
|
||||||
|
chat->receive_buf_len++;
|
||||||
|
|
||||||
|
/* Validate end delimiter not complete */
|
||||||
|
if (modem_chat_parse_end_del_complete(chat) == true) {
|
||||||
|
/* Filter out empty lines */
|
||||||
|
if (chat->receive_buf_len == chat->delimiter_size) {
|
||||||
|
/* Reset parser */
|
||||||
|
modem_chat_parse_reset(chat);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if match exists */
|
||||||
|
if (chat->parse_match == NULL) {
|
||||||
|
/* Handle unknown command */
|
||||||
|
modem_chat_on_unknown_command_received(chat);
|
||||||
|
|
||||||
|
/* Reset parser */
|
||||||
|
modem_chat_parse_reset(chat);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if trailing argument exists */
|
||||||
|
if (chat->parse_arg_len > 0) {
|
||||||
|
chat->argv[chat->argc] =
|
||||||
|
&chat->receive_buf[chat->receive_buf_len - chat->delimiter_size -
|
||||||
|
chat->parse_arg_len];
|
||||||
|
chat->receive_buf[chat->receive_buf_len - chat->delimiter_size] = '\0';
|
||||||
|
chat->argc++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle received command */
|
||||||
|
modem_chat_on_command_received(chat);
|
||||||
|
|
||||||
|
/* Reset parser */
|
||||||
|
modem_chat_parse_reset(chat);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate end delimiter not started */
|
||||||
|
if (modem_chat_parse_end_del_start(chat) == true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find matching command if missing */
|
||||||
|
if (chat->parse_match == NULL) {
|
||||||
|
/* Find matching command */
|
||||||
|
if (modem_chat_parse_find_match(chat) == false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Save match */
|
||||||
|
modem_chat_parse_save_match(chat);
|
||||||
|
|
||||||
|
/* Prepare argument parser */
|
||||||
|
chat->parse_arg_len = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if separator reached */
|
||||||
|
if (modem_chat_parse_is_separator(chat) == true) {
|
||||||
|
/* Check if argument is empty */
|
||||||
|
if (chat->parse_arg_len == 0) {
|
||||||
|
/* Save empty argument */
|
||||||
|
chat->argv[chat->argc] = "";
|
||||||
|
} else {
|
||||||
|
/* Save pointer to start of argument */
|
||||||
|
chat->argv[chat->argc] =
|
||||||
|
&chat->receive_buf[chat->receive_buf_len - chat->parse_arg_len - 1];
|
||||||
|
|
||||||
|
/* Replace separator with string terminator */
|
||||||
|
chat->receive_buf[chat->receive_buf_len - 1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Increment argument count */
|
||||||
|
chat->argc++;
|
||||||
|
|
||||||
|
/* Reset parse argument length */
|
||||||
|
chat->parse_arg_len = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Increment argument length */
|
||||||
|
chat->parse_arg_len++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool modem_chat_discard_byte(struct modem_chat *chat, uint8_t byte)
|
||||||
|
{
|
||||||
|
for (uint8_t i = 0; i < chat->filter_size; i++) {
|
||||||
|
if (byte == chat->filter[i]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process chunk of received bytes */
|
||||||
|
static void modem_chat_process_bytes(struct modem_chat *chat)
|
||||||
|
{
|
||||||
|
for (uint16_t i = 0; i < chat->work_buf_len; i++) {
|
||||||
|
if (modem_chat_discard_byte(chat, chat->work_buf[i])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
modem_chat_process_byte(chat, chat->work_buf[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void modem_chat_process_handler(struct k_work *item)
|
||||||
|
{
|
||||||
|
struct modem_chat *chat = CONTAINER_OF(item, struct modem_chat, process_work);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Fill work buffer */
|
||||||
|
ret = modem_pipe_receive(chat->pipe, chat->work_buf, sizeof(chat->work_buf));
|
||||||
|
if (ret < 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Save received data length */
|
||||||
|
chat->work_buf_len = (size_t)ret;
|
||||||
|
|
||||||
|
/* Process data */
|
||||||
|
modem_chat_process_bytes(chat);
|
||||||
|
k_work_schedule(&chat->process_work, K_NO_WAIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void modem_chat_pipe_callback(struct modem_pipe *pipe, enum modem_pipe_event event,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
struct modem_chat *chat = (struct modem_chat *)user_data;
|
||||||
|
|
||||||
|
if (event == MODEM_PIPE_EVENT_RECEIVE_READY) {
|
||||||
|
k_work_schedule(&chat->process_work, chat->process_timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************************
|
||||||
|
* GLOBAL FUNCTIONS
|
||||||
|
*********************************************************/
|
||||||
|
int modem_chat_init(struct modem_chat *chat, const struct modem_chat_config *config)
|
||||||
|
{
|
||||||
|
__ASSERT_NO_MSG(chat != NULL);
|
||||||
|
__ASSERT_NO_MSG(config != NULL);
|
||||||
|
__ASSERT_NO_MSG(config->receive_buf != NULL);
|
||||||
|
__ASSERT_NO_MSG(config->receive_buf_size > 0);
|
||||||
|
__ASSERT_NO_MSG(config->argv != NULL);
|
||||||
|
__ASSERT_NO_MSG(config->argv_size > 0);
|
||||||
|
__ASSERT_NO_MSG(config->delimiter != NULL);
|
||||||
|
__ASSERT_NO_MSG(config->delimiter_size > 0);
|
||||||
|
__ASSERT_NO_MSG(!((config->filter == NULL) && (config->filter > 0)));
|
||||||
|
__ASSERT_NO_MSG(!((config->unsol_matches == NULL) && (config->unsol_matches_size > 0)));
|
||||||
|
|
||||||
|
memset(chat, 0x00, sizeof(*chat));
|
||||||
|
chat->pipe = NULL;
|
||||||
|
chat->user_data = config->user_data;
|
||||||
|
chat->receive_buf = config->receive_buf;
|
||||||
|
chat->receive_buf_size = config->receive_buf_size;
|
||||||
|
chat->argv = config->argv;
|
||||||
|
chat->argv_size = config->argv_size;
|
||||||
|
chat->delimiter = config->delimiter;
|
||||||
|
chat->delimiter_size = config->delimiter_size;
|
||||||
|
chat->filter = config->filter;
|
||||||
|
chat->filter_size = config->filter_size;
|
||||||
|
chat->matches[MODEM_CHAT_MATCHES_INDEX_UNSOL] = config->unsol_matches;
|
||||||
|
chat->matches_size[MODEM_CHAT_MATCHES_INDEX_UNSOL] = config->unsol_matches_size;
|
||||||
|
chat->process_timeout = config->process_timeout;
|
||||||
|
atomic_set(&chat->script_state, 0);
|
||||||
|
k_work_init_delayable(&chat->process_work, modem_chat_process_handler);
|
||||||
|
k_work_init(&chat->script_run_work, modem_chat_script_run_handler);
|
||||||
|
k_work_init_delayable(&chat->script_timeout_work, modem_chat_script_timeout_handler);
|
||||||
|
k_work_init(&chat->script_abort_work, modem_chat_script_abort_handler);
|
||||||
|
k_work_init_delayable(&chat->script_send_work, modem_chat_script_send_handler);
|
||||||
|
k_work_init_delayable(&chat->script_send_timeout_work,
|
||||||
|
modem_chat_script_send_timeout_handler);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int modem_chat_attach(struct modem_chat *chat, struct modem_pipe *pipe)
|
||||||
|
{
|
||||||
|
chat->pipe = pipe;
|
||||||
|
modem_chat_parse_reset(chat);
|
||||||
|
modem_pipe_attach(chat->pipe, modem_chat_pipe_callback, chat);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int modem_chat_script_run(struct modem_chat *chat, const struct modem_chat_script *script)
|
||||||
|
{
|
||||||
|
bool script_is_running;
|
||||||
|
|
||||||
|
if (chat->pipe == NULL) {
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate script */
|
||||||
|
if ((script->script_chats == NULL) || (script->script_chats_size == 0) ||
|
||||||
|
((script->abort_matches != NULL) && (script->abort_matches_size == 0))) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate script commands */
|
||||||
|
for (uint16_t i = 0; i < script->script_chats_size; i++) {
|
||||||
|
if ((strlen(script->script_chats[i].request) == 0) &&
|
||||||
|
(script->script_chats[i].response_matches_size == 0)) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
script_is_running =
|
||||||
|
atomic_test_and_set_bit(&chat->script_state, MODEM_CHAT_SCRIPT_STATE_RUNNING_BIT);
|
||||||
|
|
||||||
|
if (script_is_running == true) {
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
chat->pending_script = script;
|
||||||
|
k_work_submit(&chat->script_run_work);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void modem_chat_script_abort(struct modem_chat *chat)
|
||||||
|
{
|
||||||
|
k_work_submit(&chat->script_abort_work);
|
||||||
|
}
|
||||||
|
|
||||||
|
void modem_chat_release(struct modem_chat *chat)
|
||||||
|
{
|
||||||
|
struct k_work_sync sync;
|
||||||
|
|
||||||
|
if (chat->pipe) {
|
||||||
|
modem_pipe_release(chat->pipe);
|
||||||
|
}
|
||||||
|
|
||||||
|
k_work_cancel_sync(&chat->script_run_work, &sync);
|
||||||
|
k_work_cancel_sync(&chat->script_abort_work, &sync);
|
||||||
|
k_work_cancel_delayable_sync(&chat->process_work, &sync);
|
||||||
|
k_work_cancel_delayable_sync(&chat->script_send_work, &sync);
|
||||||
|
|
||||||
|
chat->pipe = NULL;
|
||||||
|
chat->receive_buf_len = 0;
|
||||||
|
chat->work_buf_len = 0;
|
||||||
|
chat->argc = 0;
|
||||||
|
chat->script = NULL;
|
||||||
|
chat->script_chat_it = 0;
|
||||||
|
atomic_set(&chat->script_state, 0);
|
||||||
|
chat->script_send_request_pos = 0;
|
||||||
|
chat->script_send_delimiter_pos = 0;
|
||||||
|
chat->parse_match = NULL;
|
||||||
|
chat->parse_match_len = 0;
|
||||||
|
chat->parse_arg_len = 0;
|
||||||
|
}
|
1016
subsys/modem/modem_cmux.c
Normal file
1016
subsys/modem/modem_cmux.c
Normal file
File diff suppressed because it is too large
Load diff
176
subsys/modem/modem_pipe.c
Normal file
176
subsys/modem/modem_pipe.c
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Trackunit Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/modem/pipe.h>
|
||||||
|
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(modem_pipe, CONFIG_MODEM_MODULES_LOG_LEVEL);
|
||||||
|
|
||||||
|
void modem_pipe_init(struct modem_pipe *pipe, void *data, struct modem_pipe_api *api)
|
||||||
|
{
|
||||||
|
__ASSERT_NO_MSG(pipe != NULL);
|
||||||
|
__ASSERT_NO_MSG(data != NULL);
|
||||||
|
__ASSERT_NO_MSG(api != NULL);
|
||||||
|
|
||||||
|
pipe->data = data;
|
||||||
|
pipe->api = api;
|
||||||
|
pipe->callback = NULL;
|
||||||
|
pipe->user_data = NULL;
|
||||||
|
pipe->state = MODEM_PIPE_STATE_CLOSED;
|
||||||
|
|
||||||
|
k_mutex_init(&pipe->lock);
|
||||||
|
k_condvar_init(&pipe->condvar);
|
||||||
|
}
|
||||||
|
|
||||||
|
int modem_pipe_open(struct modem_pipe *pipe)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
k_mutex_lock(&pipe->lock, K_FOREVER);
|
||||||
|
ret = pipe->api->open(pipe->data);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
k_mutex_unlock(&pipe->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pipe->state == MODEM_PIPE_STATE_OPEN) {
|
||||||
|
k_mutex_unlock(&pipe->lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
k_condvar_wait(&pipe->condvar, &pipe->lock, K_MSEC(10000));
|
||||||
|
ret = (pipe->state == MODEM_PIPE_STATE_OPEN) ? 0 : -EAGAIN;
|
||||||
|
k_mutex_unlock(&pipe->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int modem_pipe_open_async(struct modem_pipe *pipe)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
k_mutex_lock(&pipe->lock, K_FOREVER);
|
||||||
|
ret = pipe->api->open(pipe->data);
|
||||||
|
k_mutex_unlock(&pipe->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void modem_pipe_attach(struct modem_pipe *pipe, modem_pipe_api_callback callback, void *user_data)
|
||||||
|
{
|
||||||
|
k_mutex_lock(&pipe->lock, K_FOREVER);
|
||||||
|
pipe->callback = callback;
|
||||||
|
pipe->user_data = user_data;
|
||||||
|
k_mutex_unlock(&pipe->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
int modem_pipe_transmit(struct modem_pipe *pipe, const uint8_t *buf, size_t size)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
k_mutex_lock(&pipe->lock, K_FOREVER);
|
||||||
|
|
||||||
|
if (pipe->state == MODEM_PIPE_STATE_CLOSED) {
|
||||||
|
k_mutex_unlock(&pipe->lock);
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = pipe->api->transmit(pipe->data, buf, size);
|
||||||
|
k_mutex_unlock(&pipe->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int modem_pipe_receive(struct modem_pipe *pipe, uint8_t *buf, size_t size)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
k_mutex_lock(&pipe->lock, K_FOREVER);
|
||||||
|
|
||||||
|
if (pipe->state == MODEM_PIPE_STATE_CLOSED) {
|
||||||
|
k_mutex_unlock(&pipe->lock);
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = pipe->api->receive(pipe->data, buf, size);
|
||||||
|
k_mutex_unlock(&pipe->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void modem_pipe_release(struct modem_pipe *pipe)
|
||||||
|
{
|
||||||
|
k_mutex_lock(&pipe->lock, K_FOREVER);
|
||||||
|
pipe->callback = NULL;
|
||||||
|
pipe->user_data = NULL;
|
||||||
|
k_mutex_unlock(&pipe->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
int modem_pipe_close(struct modem_pipe *pipe)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
k_mutex_lock(&pipe->lock, K_FOREVER);
|
||||||
|
ret = pipe->api->close(pipe->data);
|
||||||
|
if (ret < 0) {
|
||||||
|
k_mutex_unlock(&pipe->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pipe->state == MODEM_PIPE_STATE_CLOSED) {
|
||||||
|
k_mutex_unlock(&pipe->lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
k_condvar_wait(&pipe->condvar, &pipe->lock, K_MSEC(10000));
|
||||||
|
ret = (pipe->state == MODEM_PIPE_STATE_CLOSED) ? 0 : -EAGAIN;
|
||||||
|
k_mutex_unlock(&pipe->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int modem_pipe_close_async(struct modem_pipe *pipe)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
k_mutex_lock(&pipe->lock, K_FOREVER);
|
||||||
|
ret = pipe->api->close(pipe->data);
|
||||||
|
k_mutex_unlock(&pipe->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void modem_pipe_notify_opened(struct modem_pipe *pipe)
|
||||||
|
{
|
||||||
|
k_mutex_lock(&pipe->lock, K_FOREVER);
|
||||||
|
pipe->state = MODEM_PIPE_STATE_OPEN;
|
||||||
|
|
||||||
|
if (pipe->callback != NULL) {
|
||||||
|
pipe->callback(pipe, MODEM_PIPE_EVENT_OPENED, pipe->user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
k_condvar_signal(&pipe->condvar);
|
||||||
|
k_mutex_unlock(&pipe->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void modem_pipe_notify_closed(struct modem_pipe *pipe)
|
||||||
|
{
|
||||||
|
k_mutex_lock(&pipe->lock, K_FOREVER);
|
||||||
|
pipe->state = MODEM_PIPE_STATE_CLOSED;
|
||||||
|
|
||||||
|
if (pipe->callback != NULL) {
|
||||||
|
pipe->callback(pipe, MODEM_PIPE_EVENT_CLOSED, pipe->user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
k_condvar_signal(&pipe->condvar);
|
||||||
|
k_mutex_unlock(&pipe->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void modem_pipe_notify_receive_ready(struct modem_pipe *pipe)
|
||||||
|
{
|
||||||
|
k_mutex_lock(&pipe->lock, K_FOREVER);
|
||||||
|
|
||||||
|
if (pipe->callback != NULL) {
|
||||||
|
pipe->callback(pipe, MODEM_PIPE_EVENT_RECEIVE_READY, pipe->user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
k_mutex_unlock(&pipe->lock);
|
||||||
|
}
|
507
subsys/modem/modem_ppp.c
Normal file
507
subsys/modem/modem_ppp.c
Normal file
|
@ -0,0 +1,507 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Trackunit Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/net/ppp.h>
|
||||||
|
#include <zephyr/sys/crc.h>
|
||||||
|
#include <zephyr/modem/ppp.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(modem_ppp, CONFIG_MODEM_MODULES_LOG_LEVEL);
|
||||||
|
|
||||||
|
#define MODEM_PPP_STATE_ATTACHED_BIT (0)
|
||||||
|
#define MODEM_PPP_FRAME_TAIL_SIZE (2)
|
||||||
|
|
||||||
|
#define MODEM_PPP_CODE_DELIMITER (0x7E)
|
||||||
|
#define MODEM_PPP_CODE_ESCAPE (0x7D)
|
||||||
|
#define MODEM_PPP_VALUE_ESCAPE (0x20)
|
||||||
|
|
||||||
|
static uint16_t modem_ppp_fcs_init(uint8_t byte)
|
||||||
|
{
|
||||||
|
return crc16_ccitt(0xFFFF, &byte, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t modem_ppp_fcs_update(uint16_t fcs, uint8_t byte)
|
||||||
|
{
|
||||||
|
return crc16_ccitt(fcs, &byte, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t modem_ppp_fcs_final(uint16_t fcs)
|
||||||
|
{
|
||||||
|
return fcs ^ 0xFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t modem_ppp_ppp_protocol(struct net_pkt *pkt)
|
||||||
|
{
|
||||||
|
if (net_pkt_family(pkt) == AF_INET) {
|
||||||
|
return PPP_IP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (net_pkt_family(pkt) == AF_INET6) {
|
||||||
|
return PPP_IPV6;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_WRN("Unsupported protocol");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t modem_ppp_wrap_net_pkt_byte(struct modem_ppp *ppp)
|
||||||
|
{
|
||||||
|
uint8_t byte;
|
||||||
|
|
||||||
|
switch (ppp->transmit_state) {
|
||||||
|
case MODEM_PPP_TRANSMIT_STATE_IDLE:
|
||||||
|
LOG_WRN("Invalid transmit state");
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Writing header */
|
||||||
|
case MODEM_PPP_TRANSMIT_STATE_SOF:
|
||||||
|
ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_HDR_FF;
|
||||||
|
return MODEM_PPP_CODE_DELIMITER;
|
||||||
|
|
||||||
|
case MODEM_PPP_TRANSMIT_STATE_HDR_FF:
|
||||||
|
net_pkt_cursor_init(ppp->tx_pkt);
|
||||||
|
ppp->tx_pkt_fcs = modem_ppp_fcs_init(0xFF);
|
||||||
|
ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_HDR_7D;
|
||||||
|
return 0xFF;
|
||||||
|
|
||||||
|
case MODEM_PPP_TRANSMIT_STATE_HDR_7D:
|
||||||
|
ppp->tx_pkt_fcs = modem_ppp_fcs_update(ppp->tx_pkt_fcs, 0x03);
|
||||||
|
ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_HDR_23;
|
||||||
|
return MODEM_PPP_CODE_ESCAPE;
|
||||||
|
|
||||||
|
case MODEM_PPP_TRANSMIT_STATE_HDR_23:
|
||||||
|
if (net_pkt_is_ppp(ppp->tx_pkt) == true) {
|
||||||
|
ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_DATA;
|
||||||
|
} else {
|
||||||
|
ppp->tx_pkt_protocol = modem_ppp_ppp_protocol(ppp->tx_pkt);
|
||||||
|
ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_PROTOCOL_HIGH;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0x23;
|
||||||
|
|
||||||
|
/* Writing protocol */
|
||||||
|
case MODEM_PPP_TRANSMIT_STATE_PROTOCOL_HIGH:
|
||||||
|
byte = (ppp->tx_pkt_protocol >> 8) & 0xFF;
|
||||||
|
ppp->tx_pkt_fcs = modem_ppp_fcs_update(ppp->tx_pkt_fcs, byte);
|
||||||
|
|
||||||
|
if ((byte == MODEM_PPP_CODE_DELIMITER) || (byte == MODEM_PPP_CODE_ESCAPE) ||
|
||||||
|
(byte < MODEM_PPP_VALUE_ESCAPE)) {
|
||||||
|
ppp->tx_pkt_escaped = byte ^ MODEM_PPP_VALUE_ESCAPE;
|
||||||
|
ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_ESCAPING_PROTOCOL_HIGH;
|
||||||
|
return MODEM_PPP_CODE_ESCAPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_PROTOCOL_LOW;
|
||||||
|
return byte;
|
||||||
|
|
||||||
|
case MODEM_PPP_TRANSMIT_STATE_ESCAPING_PROTOCOL_HIGH:
|
||||||
|
ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_PROTOCOL_LOW;
|
||||||
|
return ppp->tx_pkt_escaped;
|
||||||
|
|
||||||
|
case MODEM_PPP_TRANSMIT_STATE_PROTOCOL_LOW:
|
||||||
|
byte = ppp->tx_pkt_protocol & 0xFF;
|
||||||
|
ppp->tx_pkt_fcs = modem_ppp_fcs_update(ppp->tx_pkt_fcs, byte);
|
||||||
|
|
||||||
|
if ((byte == MODEM_PPP_CODE_DELIMITER) || (byte == MODEM_PPP_CODE_ESCAPE) ||
|
||||||
|
(byte < MODEM_PPP_VALUE_ESCAPE)) {
|
||||||
|
ppp->tx_pkt_escaped = byte ^ MODEM_PPP_VALUE_ESCAPE;
|
||||||
|
ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_ESCAPING_PROTOCOL_LOW;
|
||||||
|
return MODEM_PPP_CODE_ESCAPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_DATA;
|
||||||
|
return byte;
|
||||||
|
|
||||||
|
case MODEM_PPP_TRANSMIT_STATE_ESCAPING_PROTOCOL_LOW:
|
||||||
|
ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_DATA;
|
||||||
|
return ppp->tx_pkt_escaped;
|
||||||
|
|
||||||
|
/* Writing data */
|
||||||
|
case MODEM_PPP_TRANSMIT_STATE_DATA:
|
||||||
|
net_pkt_read_u8(ppp->tx_pkt, &byte);
|
||||||
|
ppp->tx_pkt_fcs = modem_ppp_fcs_update(ppp->tx_pkt_fcs, byte);
|
||||||
|
|
||||||
|
if ((byte == MODEM_PPP_CODE_DELIMITER) || (byte == MODEM_PPP_CODE_ESCAPE) ||
|
||||||
|
(byte < MODEM_PPP_VALUE_ESCAPE)) {
|
||||||
|
ppp->tx_pkt_escaped = byte ^ MODEM_PPP_VALUE_ESCAPE;
|
||||||
|
ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_ESCAPING_DATA;
|
||||||
|
return MODEM_PPP_CODE_ESCAPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (net_pkt_remaining_data(ppp->tx_pkt) == 0) {
|
||||||
|
ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_FCS_LOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
return byte;
|
||||||
|
|
||||||
|
case MODEM_PPP_TRANSMIT_STATE_ESCAPING_DATA:
|
||||||
|
if (net_pkt_remaining_data(ppp->tx_pkt) == 0) {
|
||||||
|
ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_FCS_LOW;
|
||||||
|
} else {
|
||||||
|
ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ppp->tx_pkt_escaped;
|
||||||
|
|
||||||
|
/* Writing FCS */
|
||||||
|
case MODEM_PPP_TRANSMIT_STATE_FCS_LOW:
|
||||||
|
ppp->tx_pkt_fcs = modem_ppp_fcs_final(ppp->tx_pkt_fcs);
|
||||||
|
byte = ppp->tx_pkt_fcs & 0xFF;
|
||||||
|
|
||||||
|
if ((byte == MODEM_PPP_CODE_DELIMITER) || (byte == MODEM_PPP_CODE_ESCAPE) ||
|
||||||
|
(byte < MODEM_PPP_VALUE_ESCAPE)) {
|
||||||
|
ppp->tx_pkt_escaped = byte ^ MODEM_PPP_VALUE_ESCAPE;
|
||||||
|
ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_ESCAPING_FCS_LOW;
|
||||||
|
return MODEM_PPP_CODE_ESCAPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_FCS_HIGH;
|
||||||
|
return byte;
|
||||||
|
|
||||||
|
case MODEM_PPP_TRANSMIT_STATE_ESCAPING_FCS_LOW:
|
||||||
|
ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_FCS_HIGH;
|
||||||
|
return ppp->tx_pkt_escaped;
|
||||||
|
|
||||||
|
case MODEM_PPP_TRANSMIT_STATE_FCS_HIGH:
|
||||||
|
byte = (ppp->tx_pkt_fcs >> 8) & 0xFF;
|
||||||
|
|
||||||
|
if ((byte == MODEM_PPP_CODE_DELIMITER) || (byte == MODEM_PPP_CODE_ESCAPE) ||
|
||||||
|
(byte < MODEM_PPP_VALUE_ESCAPE)) {
|
||||||
|
ppp->tx_pkt_escaped = byte ^ MODEM_PPP_VALUE_ESCAPE;
|
||||||
|
ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_ESCAPING_FCS_HIGH;
|
||||||
|
return MODEM_PPP_CODE_ESCAPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_EOF;
|
||||||
|
return byte;
|
||||||
|
|
||||||
|
case MODEM_PPP_TRANSMIT_STATE_ESCAPING_FCS_HIGH:
|
||||||
|
ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_EOF;
|
||||||
|
return ppp->tx_pkt_escaped;
|
||||||
|
|
||||||
|
/* Writing end of frame */
|
||||||
|
case MODEM_PPP_TRANSMIT_STATE_EOF:
|
||||||
|
ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_IDLE;
|
||||||
|
return MODEM_PPP_CODE_DELIMITER;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void modem_ppp_process_received_byte(struct modem_ppp *ppp, uint8_t byte)
|
||||||
|
{
|
||||||
|
switch (ppp->receive_state) {
|
||||||
|
case MODEM_PPP_RECEIVE_STATE_HDR_SOF:
|
||||||
|
if (byte == MODEM_PPP_CODE_DELIMITER) {
|
||||||
|
ppp->receive_state = MODEM_PPP_RECEIVE_STATE_HDR_FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MODEM_PPP_RECEIVE_STATE_HDR_FF:
|
||||||
|
if (byte == MODEM_PPP_CODE_DELIMITER) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (byte == 0xFF) {
|
||||||
|
ppp->receive_state = MODEM_PPP_RECEIVE_STATE_HDR_7D;
|
||||||
|
} else {
|
||||||
|
ppp->receive_state = MODEM_PPP_RECEIVE_STATE_HDR_SOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MODEM_PPP_RECEIVE_STATE_HDR_7D:
|
||||||
|
if (byte == MODEM_PPP_CODE_ESCAPE) {
|
||||||
|
ppp->receive_state = MODEM_PPP_RECEIVE_STATE_HDR_23;
|
||||||
|
} else {
|
||||||
|
ppp->receive_state = MODEM_PPP_RECEIVE_STATE_HDR_SOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MODEM_PPP_RECEIVE_STATE_HDR_23:
|
||||||
|
if (byte == 0x23) {
|
||||||
|
ppp->rx_pkt = net_pkt_rx_alloc_with_buffer(ppp->iface,
|
||||||
|
CONFIG_MODEM_PPP_NET_BUF_FRAG_SIZE, AF_UNSPEC, 0, K_NO_WAIT);
|
||||||
|
|
||||||
|
if (ppp->rx_pkt == NULL) {
|
||||||
|
LOG_WRN("Dropped frame, no net_pkt available");
|
||||||
|
ppp->receive_state = MODEM_PPP_RECEIVE_STATE_HDR_SOF;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("Receiving PPP frame");
|
||||||
|
ppp->receive_state = MODEM_PPP_RECEIVE_STATE_WRITING;
|
||||||
|
net_pkt_cursor_init(ppp->rx_pkt);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ppp->receive_state = MODEM_PPP_RECEIVE_STATE_HDR_SOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MODEM_PPP_RECEIVE_STATE_WRITING:
|
||||||
|
if (byte == MODEM_PPP_CODE_DELIMITER) {
|
||||||
|
LOG_DBG("Received PPP frame");
|
||||||
|
|
||||||
|
/* Remove FCS */
|
||||||
|
net_pkt_remove_tail(ppp->rx_pkt, MODEM_PPP_FRAME_TAIL_SIZE);
|
||||||
|
net_pkt_cursor_init(ppp->rx_pkt);
|
||||||
|
net_pkt_set_ppp(ppp->rx_pkt, true);
|
||||||
|
|
||||||
|
if (net_recv_data(ppp->iface, ppp->rx_pkt) < 0) {
|
||||||
|
LOG_WRN("Net pkt could not be processed");
|
||||||
|
net_pkt_unref(ppp->rx_pkt);
|
||||||
|
}
|
||||||
|
|
||||||
|
ppp->rx_pkt = NULL;
|
||||||
|
ppp->receive_state = MODEM_PPP_RECEIVE_STATE_HDR_SOF;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (net_pkt_available_buffer(ppp->rx_pkt) == 1) {
|
||||||
|
if (net_pkt_alloc_buffer(ppp->rx_pkt, CONFIG_MODEM_PPP_NET_BUF_FRAG_SIZE,
|
||||||
|
AF_INET, K_NO_WAIT) < 0) {
|
||||||
|
LOG_WRN("Failed to alloc buffer");
|
||||||
|
net_pkt_unref(ppp->rx_pkt);
|
||||||
|
ppp->rx_pkt = NULL;
|
||||||
|
ppp->receive_state = MODEM_PPP_RECEIVE_STATE_HDR_SOF;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (byte == MODEM_PPP_CODE_ESCAPE) {
|
||||||
|
ppp->receive_state = MODEM_PPP_RECEIVE_STATE_UNESCAPING;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (net_pkt_write_u8(ppp->rx_pkt, byte) < 0) {
|
||||||
|
LOG_WRN("Dropped PPP frame");
|
||||||
|
net_pkt_unref(ppp->rx_pkt);
|
||||||
|
ppp->rx_pkt = NULL;
|
||||||
|
ppp->receive_state = MODEM_PPP_RECEIVE_STATE_HDR_SOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MODEM_PPP_RECEIVE_STATE_UNESCAPING:
|
||||||
|
if (net_pkt_write_u8(ppp->rx_pkt, (byte ^ MODEM_PPP_VALUE_ESCAPE)) < 0) {
|
||||||
|
LOG_WRN("Dropped PPP frame");
|
||||||
|
net_pkt_unref(ppp->rx_pkt);
|
||||||
|
ppp->rx_pkt = NULL;
|
||||||
|
ppp->receive_state = MODEM_PPP_RECEIVE_STATE_HDR_SOF;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ppp->receive_state = MODEM_PPP_RECEIVE_STATE_WRITING;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void modem_ppp_pipe_callback(struct modem_pipe *pipe, enum modem_pipe_event event,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
struct modem_ppp *ppp = (struct modem_ppp *)user_data;
|
||||||
|
|
||||||
|
if (event == MODEM_PIPE_EVENT_RECEIVE_READY) {
|
||||||
|
k_work_submit(&ppp->process_work);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void modem_ppp_send_handler(struct k_work *item)
|
||||||
|
{
|
||||||
|
struct modem_ppp *ppp = CONTAINER_OF(item, struct modem_ppp, send_work);
|
||||||
|
uint8_t byte;
|
||||||
|
uint8_t *reserved;
|
||||||
|
uint32_t reserved_size;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (ppp->tx_pkt == NULL) {
|
||||||
|
ppp->tx_pkt = k_fifo_get(&ppp->tx_pkt_fifo, K_NO_WAIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ppp->tx_pkt != NULL) {
|
||||||
|
/* Initialize wrap */
|
||||||
|
if (ppp->transmit_state == MODEM_PPP_TRANSMIT_STATE_IDLE) {
|
||||||
|
ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_SOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill transmit ring buffer */
|
||||||
|
while (ring_buf_space_get(&ppp->transmit_rb) > 0) {
|
||||||
|
byte = modem_ppp_wrap_net_pkt_byte(ppp);
|
||||||
|
|
||||||
|
ring_buf_put(&ppp->transmit_rb, &byte, 1);
|
||||||
|
|
||||||
|
if (ppp->transmit_state == MODEM_PPP_TRANSMIT_STATE_IDLE) {
|
||||||
|
net_pkt_unref(ppp->tx_pkt);
|
||||||
|
ppp->tx_pkt = k_fifo_get(&ppp->tx_pkt_fifo, K_NO_WAIT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reserved_size = ring_buf_get_claim(&ppp->transmit_rb, &reserved, UINT32_MAX);
|
||||||
|
if (reserved_size == 0) {
|
||||||
|
ring_buf_get_finish(&ppp->transmit_rb, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = modem_pipe_transmit(ppp->pipe, reserved, reserved_size);
|
||||||
|
if (ret < 0) {
|
||||||
|
ring_buf_get_finish(&ppp->transmit_rb, 0);
|
||||||
|
} else {
|
||||||
|
ring_buf_get_finish(&ppp->transmit_rb, (uint32_t)ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Resubmit send work if data remains */
|
||||||
|
if ((ring_buf_is_empty(&ppp->transmit_rb) == false) || (ppp->tx_pkt != NULL)) {
|
||||||
|
k_work_submit(&ppp->send_work);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void modem_ppp_process_handler(struct k_work *item)
|
||||||
|
{
|
||||||
|
struct modem_ppp *ppp = CONTAINER_OF(item, struct modem_ppp, process_work);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = modem_pipe_receive(ppp->pipe, ppp->receive_buf, ppp->buf_size);
|
||||||
|
if (ret < 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < ret; i++) {
|
||||||
|
modem_ppp_process_received_byte(ppp, ppp->receive_buf[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
k_work_submit(&ppp->process_work);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void modem_ppp_ppp_api_init(struct net_if *iface)
|
||||||
|
{
|
||||||
|
const struct device *dev = net_if_get_device(iface);
|
||||||
|
struct modem_ppp *ppp = (struct modem_ppp *)dev->data;
|
||||||
|
|
||||||
|
net_ppp_init(iface);
|
||||||
|
net_if_flag_set(iface, NET_IF_NO_AUTO_START);
|
||||||
|
net_if_carrier_off(iface);
|
||||||
|
|
||||||
|
if (ppp->init_iface != NULL) {
|
||||||
|
ppp->init_iface(iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
ppp->iface = iface;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int modem_ppp_ppp_api_start(const struct device *dev)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int modem_ppp_ppp_api_stop(const struct device *dev)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int modem_ppp_ppp_api_send(const struct device *dev, struct net_pkt *pkt)
|
||||||
|
{
|
||||||
|
struct modem_ppp *ppp = (struct modem_ppp *)dev->data;
|
||||||
|
|
||||||
|
if (atomic_test_bit(&ppp->state, MODEM_PPP_STATE_ATTACHED_BIT) == false) {
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate packet protocol */
|
||||||
|
if ((net_pkt_is_ppp(pkt) == false) && (net_pkt_family(pkt) != AF_INET) &&
|
||||||
|
(net_pkt_family(pkt) != AF_INET6)) {
|
||||||
|
return -EPROTONOSUPPORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate packet data length */
|
||||||
|
if (((net_pkt_get_len(pkt) < 2) && (net_pkt_is_ppp(pkt) == true)) ||
|
||||||
|
((net_pkt_get_len(pkt) < 1))) {
|
||||||
|
return -ENODATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
net_pkt_ref(pkt);
|
||||||
|
k_fifo_put(&ppp->tx_pkt_fifo, pkt);
|
||||||
|
k_work_submit(&ppp->send_work);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct ppp_api modem_ppp_ppp_api = {
|
||||||
|
.iface_api.init = modem_ppp_ppp_api_init,
|
||||||
|
.start = modem_ppp_ppp_api_start,
|
||||||
|
.stop = modem_ppp_ppp_api_stop,
|
||||||
|
.send = modem_ppp_ppp_api_send,
|
||||||
|
};
|
||||||
|
|
||||||
|
int modem_ppp_attach(struct modem_ppp *ppp, struct modem_pipe *pipe)
|
||||||
|
{
|
||||||
|
if (atomic_test_and_set_bit(&ppp->state, MODEM_PPP_STATE_ATTACHED_BIT) == true) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
modem_pipe_attach(pipe, modem_ppp_pipe_callback, ppp);
|
||||||
|
ppp->pipe = pipe;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct net_if *modem_ppp_get_iface(struct modem_ppp *ppp)
|
||||||
|
{
|
||||||
|
return ppp->iface;
|
||||||
|
}
|
||||||
|
|
||||||
|
void modem_ppp_release(struct modem_ppp *ppp)
|
||||||
|
{
|
||||||
|
struct k_work_sync sync;
|
||||||
|
struct net_pkt *pkt;
|
||||||
|
|
||||||
|
if (atomic_test_and_clear_bit(&ppp->state, MODEM_PPP_STATE_ATTACHED_BIT) == false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
modem_pipe_release(ppp->pipe);
|
||||||
|
k_work_cancel_sync(&ppp->send_work, &sync);
|
||||||
|
k_work_cancel_sync(&ppp->process_work, &sync);
|
||||||
|
ppp->pipe = NULL;
|
||||||
|
ppp->receive_state = MODEM_PPP_RECEIVE_STATE_HDR_SOF;
|
||||||
|
|
||||||
|
if (ppp->rx_pkt != NULL) {
|
||||||
|
net_pkt_unref(ppp->rx_pkt);
|
||||||
|
ppp->rx_pkt = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_IDLE;
|
||||||
|
|
||||||
|
if (ppp->tx_pkt != NULL) {
|
||||||
|
net_pkt_unref(ppp->tx_pkt);
|
||||||
|
ppp->tx_pkt = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
pkt = k_fifo_get(&ppp->tx_pkt_fifo, K_NO_WAIT);
|
||||||
|
if (pkt == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
net_pkt_unref(pkt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int modem_ppp_init_internal(const struct device *dev)
|
||||||
|
{
|
||||||
|
struct modem_ppp *ppp = (struct modem_ppp *)dev->data;
|
||||||
|
|
||||||
|
atomic_set(&ppp->state, 0);
|
||||||
|
ring_buf_init(&ppp->transmit_rb, ppp->buf_size, ppp->transmit_buf);
|
||||||
|
k_work_init(&ppp->send_work, modem_ppp_send_handler);
|
||||||
|
k_work_init(&ppp->process_work, modem_ppp_process_handler);
|
||||||
|
k_fifo_init(&ppp->tx_pkt_fifo);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in a new issue