Skip to content

Commit

Permalink
applications: serial_lte_modem: integrate with Zephyr generic modem d…
Browse files Browse the repository at this point in the history
…river

This makes the changes necessary for SLM to be compatible
with Zephyr's generic modem driver.

The CMUX channels are now run-time configurable such that
both AT commands and PPP can be on any of the two DLCIs.
This is achieved by explicitly binding the AT channel to
a particular DLCI, and letting PPP take the other one.

Signed-off-by: Tomi Fontanilles <[email protected]>
  • Loading branch information
tomi-font authored and rlubos committed Feb 20, 2024
1 parent 24b073e commit 7a74dca
Show file tree
Hide file tree
Showing 9 changed files with 124 additions and 64 deletions.
12 changes: 12 additions & 0 deletions applications/serial_lte_modem/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,18 @@ config SLM_PPP
config SLM_CMUX
bool "CMUX support in SLM"

if SLM_CMUX && SLM_PPP

config SLM_CMUX_AUTOMATIC_FALLBACK_ON_PPP_STOPPAGE
bool "AT channel automatic fallback to DLCI 1 when PPP stops."
help
This is mainly intended for compatibility with Zephyr's generic modem driver.
If enabled, when PPP stops (which happens automatically when the LTE link goes down)
CMUX will make the AT channel be on DLCI 1. This only has an effect if it had been
configured to be elsewhere, e.g. with AT#XCMUX=2.

endif

rsource "src/ftp_c/Kconfig"
rsource "src/mqtt_c/Kconfig"
rsource "src/http_c/Kconfig"
Expand Down
2 changes: 1 addition & 1 deletion applications/serial_lte_modem/overlay-cmux.conf
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ CONFIG_MODEM_MODULES=y
CONFIG_MODEM_CMUX=y
CONFIG_MODEM_BACKEND_UART=y
# Allow large UART TXs to go through @115200.
CONFIG_MODEM_BACKEND_UART_ASYNC_TRANSMIT_TIMEOUT_MS=400
CONFIG_MODEM_BACKEND_UART_ASYNC_TRANSMIT_TIMEOUT_MS=200

# debug options
#CONFIG_MODEM_CMUX_LOG_LEVEL_DBG=y
Expand Down
2 changes: 1 addition & 1 deletion applications/serial_lte_modem/overlay-ppp.conf
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ CONFIG_MODEM_MODULES=y
CONFIG_MODEM_PPP=y
CONFIG_MODEM_BACKEND_UART=y
# Allow large UART TXs to go through @115200.
CONFIG_MODEM_BACKEND_UART_ASYNC_TRANSMIT_TIMEOUT_MS=400
CONFIG_MODEM_BACKEND_UART_ASYNC_TRANSMIT_TIMEOUT_MS=200

# L2 protocol
CONFIG_NET_L2_PPP_MGMT=y
Expand Down
4 changes: 3 additions & 1 deletion applications/serial_lte_modem/src/slm_at_host.c
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,9 @@ static void cmd_send(uint8_t *buf, size_t cmd_length, size_t buf_size)
const size_t cmd_name_len = cmd_name_toupper(at_cmd, cmd_length);

err = slm_at_parse(at_cmd, cmd_name_len);
if (err == 0) {
if (err == SILENT_AT_COMMAND_RET) {
return;
} else if (err == 0) {
rsp_send_ok();
return;
} else if (err != UNKNOWN_AT_COMMAND_RET) {
Expand Down
94 changes: 67 additions & 27 deletions applications/serial_lte_modem/src/slm_cmux.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
*/
#include "slm_cmux.h"
#include "slm_at_host.h"
#if defined(CONFIG_SLM_PPP)
#include "slm_ppp.h"
#endif
#include <zephyr/logging/log.h>
#include <zephyr/modem/backend/uart.h>
#include <zephyr/modem/cmux.h>
Expand All @@ -14,13 +17,7 @@
/* This makes use of part of the Zephyr modem subsystem which has a CMUX module. */
LOG_MODULE_REGISTER(slm_cmux, CONFIG_SLM_LOG_LEVEL);

enum cmux_channel {
AT_CHANNEL,
#if defined(CONFIG_SLM_PPP)
PPP_CHANNEL,
#endif
CHANNEL_COUNT
};
#define CHANNEL_COUNT (1 + IS_ENABLED(CONFIG_SLM_PPP))

#define RECV_BUF_LEN SLM_AT_MAX_CMD_LEN
/* The CMUX module reserves some spare buffer bytes. To achieve a maximum
Expand Down Expand Up @@ -49,6 +46,8 @@ static struct {
uint8_t address;
uint8_t receive_buf[RECV_BUF_LEN];
} dlcis[CHANNEL_COUNT];
/* Index of the DLCI used for AT communication; defaults to 0. */
unsigned int at_channel;
} cmux;

static void dlci_pipe_event_handler(struct modem_pipe *pipe,
Expand All @@ -59,6 +58,7 @@ static void dlci_pipe_event_handler(struct modem_pipe *pipe,
switch (event) {
case MODEM_PIPE_EVENT_OPENED:
case MODEM_PIPE_EVENT_CLOSED:
/* The PPP DLCI events are received here only when PPP isn't started. */
LOG_INF("DLCI %u %s.", dlci->address,
(event == MODEM_PIPE_EVENT_OPENED) ? "opened" : "closed");
break;
Expand All @@ -67,11 +67,14 @@ static void dlci_pipe_event_handler(struct modem_pipe *pipe,
const int recv_len = modem_pipe_receive(pipe, recv_buf, sizeof(recv_buf));

if (recv_len > 0) {
if (ARRAY_INDEX(cmux.dlcis, dlci) == AT_CHANNEL) {
if (ARRAY_INDEX(cmux.dlcis, dlci) == cmux.at_channel) {
slm_at_receive(recv_buf, recv_len);
} else {
LOG_WRN("Discarding %u byte%s received on non-AT DLCI %u.",
recv_len, (recv_len > 1) ? "s" : "", dlci->address);
}
} else {
LOG_ERR("Failed to receive data from DLCI %u. (%d)",
LOG_ERR("Failed to receive data on DLCI %u. (%d)",
dlci->address, recv_len);
}
break;
Expand Down Expand Up @@ -105,8 +108,7 @@ static void init_dlci(size_t dlci_idx, uint16_t recv_buf_size,

static int cmux_write_at_channel(const uint8_t *data, size_t len)
{
struct modem_pipe *const at_dlci_pipe = cmux.dlcis[AT_CHANNEL].pipe;
int ret = modem_pipe_transmit(at_dlci_pipe, data, len);
int ret = modem_pipe_transmit(cmux.dlcis[cmux.at_channel].pipe, data, len);

if (ret != len) {
const int sent_len = MAX(0, ret);
Expand Down Expand Up @@ -150,7 +152,7 @@ static int cmux_stop(void)
return 0;
}

static bool cmux_is_initialized(void)
static bool cmux_is_started(void)
{
return (cmux.uart_pipe != NULL);
}
Expand All @@ -173,17 +175,41 @@ void slm_cmux_init(void)
}

#if defined(CONFIG_SLM_PPP)
void *slm_cmux_get_ppp_channel_pipe(void)

static struct cmux_dlci *cmux_ppp_dlci(void)
{
return cmux.dlcis[PPP_CHANNEL].pipe;
BUILD_ASSERT(ARRAY_SIZE(cmux.dlcis) == 2);
/* The DLCI that is not the AT channel's is PPP's. */
return &cmux.dlcis[!cmux.at_channel];
}

struct modem_pipe *slm_cmux_reserve_ppp_channel(void)
{
/* Return the PPP channel's pipe. PPP will attach to it, after which
* this pipe's events and data will not be received here anymore
* until the pipe is released (below) and we attach back to it.
*/
return cmux_ppp_dlci()->pipe;
}

void slm_cmux_release_ppp_channel(void)
{
struct cmux_dlci *ppp_dlci = cmux_ppp_dlci();

#if CONFIG_SLM_CMUX_AUTOMATIC_FALLBACK_ON_PPP_STOPPAGE
cmux.at_channel = 0;
#endif

modem_pipe_attach(ppp_dlci->pipe, dlci_pipe_event_handler, ppp_dlci);
}

#endif /* CONFIG_SLM_PPP */

static int cmux_start(void)
{
int ret;

if (cmux_is_initialized()) {
if (cmux_is_started()) {
ret = modem_pipe_open(cmux.uart_pipe);
if (!ret) {
LOG_INF("CMUX resumed.");
Expand Down Expand Up @@ -234,26 +260,40 @@ static void cmux_starter(struct k_work *)
int handle_at_cmux(enum at_cmd_type cmd_type)
{
static struct k_work_delayable cmux_start_work;
const unsigned int param_count = at_params_valid_count_get(&slm_at_param_list);
unsigned int at_dlci;
int ret;
unsigned int op;
enum {
OP_START,
};

if (cmd_type != AT_CMD_TYPE_SET_COMMAND
|| at_params_valid_count_get(&slm_at_param_list) != 2) {
if (cmd_type != AT_CMD_TYPE_SET_COMMAND || param_count > 2) {
return -EINVAL;
}

ret = at_params_unsigned_int_get(&slm_at_param_list, 1, &op);
if (ret) {
return ret;
} else if (op != OP_START) {
return -EINVAL;
} else if (cmux_is_initialized()) {
if (param_count == 1 && cmux_is_started()) {
return -EALREADY;
}

if (param_count == 2) {
ret = at_params_unsigned_int_get(&slm_at_param_list, 1, &at_dlci);
if (ret || at_dlci < 1 || at_dlci > ARRAY_SIZE(cmux.dlcis)) {
return -EINVAL;
}
const unsigned int at_channel = at_dlci - 1;

#if defined(CONFIG_SLM_PPP)
if (slm_ppp_is_running() && at_channel != cmux.at_channel) {
/* The AT channel cannot be changed when PPP is running. */
return -ENOTSUP;
}
#endif
if (cmux_is_started()) {
/* Just update the AT channel, first answering "OK" on the current DLCI. */
rsp_send_ok();
cmux.at_channel = at_channel;
return SILENT_AT_COMMAND_RET;
}
cmux.at_channel = at_channel;
}

k_work_init_delayable(&cmux_start_work, cmux_starter);
ret = k_work_schedule(&cmux_start_work, SLM_UART_RESPONSE_DELAY);
if (ret == 1) {
Expand Down
9 changes: 6 additions & 3 deletions applications/serial_lte_modem/src/slm_cmux.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@

void slm_cmux_init(void);

int handle_at_cmux(enum at_cmd_type cmd_type);

#if defined(CONFIG_SLM_PPP)
void *slm_cmux_get_ppp_channel_pipe(void);
#endif
struct modem_pipe;
struct modem_pipe *slm_cmux_reserve_ppp_channel(void);

int handle_at_cmux(enum at_cmd_type cmd_type);
void slm_cmux_release_ppp_channel(void);
#endif /* CONFIG_SLM_PPP */

#endif
7 changes: 6 additions & 1 deletion applications/serial_lte_modem/src/slm_defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@
#define INVALID_ROLE -1
#define INVALID_DTLS_CID -1

#define UNKNOWN_AT_COMMAND_RET __ELASTERROR
enum {
/* The command is not a known (or activated) SLM-proprietary AT command. */
UNKNOWN_AT_COMMAND_RET = __ELASTERROR,
/* The command ran successfully and doesn't want the automatic response to be sent. */
SILENT_AT_COMMAND_RET,
};

/** The maximum allowed length of an AT command/response passed through the SLM */
#define SLM_AT_MAX_CMD_LEN 4096
Expand Down
Loading

0 comments on commit 7a74dca

Please sign in to comment.