diff --git a/app/overlay-cmux.conf b/app/overlay-cmux.conf index fe227930..9389efb9 100644 --- a/app/overlay-cmux.conf +++ b/app/overlay-cmux.conf @@ -11,18 +11,6 @@ CONFIG_SM_SKIP_READY_MSG=y CONFIG_MODEM_MODULES=y CONFIG_MODEM_CMUX=y -# Enable Serial Modem UART backend -CONFIG_MODEM_BACKEND_UART=n -CONFIG_MODEM_BACKEND_UART_ASYNC=n -CONFIG_MODEM_BACKEND_UART_SLM=y -CONFIG_MODEM_BACKEND_UART_SLM_TRANSMIT_TIMEOUT_MS=1000 - -# These buffers are unused after AT#CMUX is enabled -# so use minimal buffer size -CONFIG_SM_UART_RX_BUF_COUNT=2 -CONFIG_SM_UART_RX_BUF_SIZE=128 -CONFIG_SM_UART_TX_BUF_SIZE=128 - # debug options #CONFIG_MODEM_CMUX_LOG_LEVEL_DBG=y #CONFIG_MODEM_MODULES_LOG_LEVEL_DBG=y diff --git a/app/overlay-ppp-cmux-linux.conf b/app/overlay-ppp-cmux-linux.conf index dda3afed..1809fd09 100644 --- a/app/overlay-ppp-cmux-linux.conf +++ b/app/overlay-ppp-cmux-linux.conf @@ -11,25 +11,12 @@ CONFIG_SM_CR_TERMINATION=y CONFIG_MODEM_CMUX_MTU=127 CONFIG_MODEM_CMUX_WORK_BUFFER_SIZE=536 -CONFIG_SM_CMUX_UART_BUFFER_SIZE=600 -# Enable Serial Modem UART backend -CONFIG_MODEM_BACKEND_UART=n -CONFIG_MODEM_BACKEND_UART_ASYNC=n -CONFIG_MODEM_BACKEND_UART_SLM=y - -# For sending full 600 bytes at 115200 baudrate -# 600 * 10 / 115200 = 52.1 ms -CONFIG_MODEM_BACKEND_UART_SLM_TRANSMIT_TIMEOUT_MS=53 -# Assume at least baudrate 115200 for UART -# so CMUX frame can be received in 12 ms (134*10/115200) -CONFIG_MODEM_BACKEND_UART_SLM_RECEIVE_IDLE_TIMEOUT_MS=12 - -# These buffers are unused after AT#CMUX is enabled -# so use minimal buffer size -CONFIG_SM_UART_RX_BUF_COUNT=2 -CONFIG_SM_UART_RX_BUF_SIZE=128 -CONFIG_SM_UART_TX_BUF_SIZE=128 +# With CMUX, the UART buffers should be at least the size of the +# CONFIG_MODEM_CMUX_WORK_BUFFER_SIZE +CONFIG_SM_UART_RX_BUF_COUNT=3 +CONFIG_SM_UART_RX_BUF_SIZE=256 +CONFIG_SM_UART_TX_BUF_SIZE=768 # When using PPP, disable commands of IP-based protocols to save flash space. CONFIG_SM_FTPC=n diff --git a/app/overlay-ppp-without-cmux.conf b/app/overlay-ppp-without-cmux.conf new file mode 100644 index 00000000..b5bcec14 --- /dev/null +++ b/app/overlay-ppp-without-cmux.conf @@ -0,0 +1,8 @@ +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# Enable modem UART backend +CONFIG_MODEM_BACKEND_UART=y +CONFIG_MODEM_BACKEND_UART_ASYNC=y diff --git a/app/overlay-ppp.conf b/app/overlay-ppp.conf index 34ef2661..a0a86e5b 100644 --- a/app/overlay-ppp.conf +++ b/app/overlay-ppp.conf @@ -24,12 +24,6 @@ CONFIG_NET_L2_PPP=y CONFIG_MODEM_MODULES=y CONFIG_MODEM_PPP=y -# Enable Serial Modem UART backend -CONFIG_MODEM_BACKEND_UART=n -CONFIG_MODEM_BACKEND_UART_ASYNC=n -CONFIG_MODEM_BACKEND_UART_SLM=y -CONFIG_MODEM_BACKEND_UART_SLM_TRANSMIT_TIMEOUT_MS=1000 - # L2 protocol CONFIG_NET_L2_PPP_MGMT=y CONFIG_NET_L2_PPP_OPTION_MRU=y diff --git a/app/prj.conf b/app/prj.conf index acb08057..5599f9ed 100644 --- a/app/prj.conf +++ b/app/prj.conf @@ -133,6 +133,7 @@ CONFIG_SM_EXTERNAL_XTAL=n #CONFIG_SM_LOG_LEVEL_DBG=y #CONFIG_LOG_PRINTK=n #CONFIG_LOG_MODE_IMMEDIATE=y +#CONFIG_DEBUG_OPTIMIZATIONS=y # For using external GNSS antenna #CONFIG_MODEM_ANTENNA=y diff --git a/app/sample.yaml b/app/sample.yaml index 7ae3564c..92b99ad7 100644 --- a/app/sample.yaml +++ b/app/sample.yaml @@ -85,6 +85,7 @@ tests: build_only: true extra_args: - EXTRA_CONF_FILE="overlay-ppp.conf" + - EXTRA_CONF_FILE="overlay-ppp-without-cmux.conf" - EXTRA_DTC_OVERLAY_FILE="overlay-ppp-without-cmux.overlay" platform_allow: - nrf9160dk/nrf9160/ns @@ -104,6 +105,7 @@ tests: build_only: true extra_args: - EXTRA_CONF_FILE="overlay-ppp.conf" + - EXTRA_CONF_FILE="overlay-ppp-without-cmux.conf" - EXTRA_DTC_OVERLAY_FILE="overlay-ppp-without-cmux.overlay" extra_configs: - CONFIG_SM_POWER_PIN=31 diff --git a/app/src/sm_at_commands.c b/app/src/sm_at_commands.c index 53c93fe3..36e422f3 100644 --- a/app/src/sm_at_commands.c +++ b/app/src/sm_at_commands.c @@ -383,34 +383,34 @@ int sm_at_init(void) err = sm_at_tcp_proxy_init(); if (err) { - LOG_ERR("TCP Server could not be initialized: %d", err); + LOG_ERR("%s initialization failed (%d).", "TCP Server", err); return -EFAULT; } err = sm_at_udp_proxy_init(); if (err) { - LOG_ERR("UDP Server could not be initialized: %d", err); + LOG_ERR("%s initialization failed (%d).", "UDP Server", err); return -EFAULT; } err = sm_at_socket_init(); if (err) { - LOG_ERR("TCPIP could not be initialized: %d", err); + LOG_ERR("%s initialization failed (%d).", "Socket", err); return -EFAULT; } err = sm_at_icmp_init(); if (err) { - LOG_ERR("ICMP could not be initialized: %d", err); + LOG_ERR("%s initialization failed (%d).", "ICMP", err); return -EFAULT; } #if defined(CONFIG_SM_SMS) err = sm_at_sms_init(); if (err) { - LOG_ERR("SMS could not be initialized: %d", err); + LOG_ERR("%s initialization failed (%d).", "SMS", err); return -EFAULT; } #endif err = sm_at_fota_init(); if (err) { - LOG_ERR("FOTA could not be initialized: %d", err); + LOG_ERR("%s initialization failed (%d).", "FOTA", err); return -EFAULT; } #if defined(CONFIG_SM_NRF_CLOUD) @@ -419,63 +419,63 @@ int sm_at_init(void) /* Allow nRF Cloud initialization to fail as sometimes JWT is missing * especially during development. */ - LOG_ERR("nRF Cloud could not be initialized: %d", err); + LOG_ERR("%s initialization failed (%d).", "nRF Cloud", err); err = 0; } #endif #if defined(CONFIG_SM_GNSS) err = sm_at_gnss_init(); if (err) { - LOG_ERR("GNSS could not be initialized: %d", err); + LOG_ERR("%s initialization failed (%d).", "GNSS", err); return -EFAULT; } #endif #if defined(CONFIG_SM_FTPC) err = sm_at_ftp_init(); if (err) { - LOG_ERR("FTP could not be initialized: %d", err); + LOG_ERR("%s initialization failed (%d).", "FTP", err); return -EFAULT; } #endif #if defined(CONFIG_SM_MQTTC) err = sm_at_mqtt_init(); if (err) { - LOG_ERR("MQTT could not be initialized: %d", err); + LOG_ERR("%s initialization failed (%d).", "MQTT", err); return -EFAULT; } #endif #if defined(CONFIG_SM_HTTPC) err = sm_at_httpc_init(); if (err) { - LOG_ERR("HTTP could not be initialized: %d", err); + LOG_ERR("%s initialization failed (%d).", "HTTP", err); return -EFAULT; } #endif #if defined(CONFIG_SM_GPIO) err = sm_at_gpio_init(); if (err) { - LOG_ERR("GPIO could not be initialized: %d", err); + LOG_ERR("%s initialization failed (%d).", "GPIO", err); return -EFAULT; } #endif #if defined(CONFIG_SM_TWI) err = sm_at_twi_init(); if (err) { - LOG_ERR("TWI could not be initialized: %d", err); + LOG_ERR("%s initialization failed (%d).", "TWI", err); return -EFAULT; } #endif #if defined(CONFIG_SM_CARRIER) err = sm_at_carrier_init(); if (err) { - LOG_ERR("LwM2M carrier could not be initialized: %d", err); + LOG_ERR("%s initialization failed (%d).", "LwM2M carrier", err); return -EFAULT; } #endif #if defined(CONFIG_LWM2M_CARRIER_SETTINGS) err = sm_at_carrier_cfg_init(); if (err) { - LOG_ERR("LwM2M carrier could not be initialized: %d", err); + LOG_ERR("%s initialization failed (%d).", "LwM2M carrier", err); return -EFAULT; } #endif @@ -485,7 +485,7 @@ int sm_at_init(void) #if defined(CONFIG_SM_PPP) err = sm_ppp_init(); if (err) { - LOG_ERR("PPP initialization failed. (%d)", err); + LOG_ERR("%s initialization failed (%d).", "PPP", err); return err; } #endif @@ -498,76 +498,79 @@ void sm_at_uninit(void) err = sm_at_tcp_proxy_uninit(); if (err) { - LOG_WRN("TCP Server could not be uninitialized: %d", err); + LOG_WRN("%s uninitialization failed (%d).", "TCP Server", err); } err = sm_at_udp_proxy_uninit(); if (err) { - LOG_WRN("UDP Server could not be uninitialized: %d", err); + LOG_WRN("%s uninitialization failed (%d).", "UDP Server", err); } err = sm_at_socket_uninit(); if (err) { - LOG_WRN("TCPIP could not be uninitialized: %d", err); + LOG_WRN("%s uninitialization failed (%d).", "Socket", err); } err = sm_at_icmp_uninit(); if (err) { - LOG_WRN("ICMP could not be uninitialized: %d", err); + LOG_WRN("%s uninitialization failed (%d).", "ICMP", err); } #if defined(CONFIG_SM_SMS) err = sm_at_sms_uninit(); if (err) { - LOG_WRN("SMS could not be uninitialized: %d", err); + LOG_WRN("%s uninitialization failed (%d).", "SMS", err); } #endif err = sm_at_fota_uninit(); if (err) { - LOG_WRN("FOTA could not be uninitialized: %d", err); + LOG_WRN("%s uninitialization failed (%d).", "FOTA", err); } #if defined(CONFIG_SM_NRF_CLOUD) err = sm_at_nrfcloud_uninit(); if (err) { - LOG_WRN("nRF Cloud could not be uninitialized: %d", err); + LOG_WRN("%s uninitialization failed (%d).", "nRF Cloud", err); } #endif #if defined(CONFIG_SM_GNSS) err = sm_at_gnss_uninit(); if (err) { - LOG_WRN("GNSS could not be uninitialized: %d", err); + LOG_WRN("%s uninitialization failed (%d).", "GNSS", err); } #endif #if defined(CONFIG_SM_FTPC) err = sm_at_ftp_uninit(); if (err) { - LOG_WRN("FTP could not be uninitialized: %d", err); + LOG_WRN("%s uninitialization failed (%d).", "FTP", err); } #endif #if defined(CONFIG_SM_MQTTC) err = sm_at_mqtt_uninit(); if (err) { - LOG_WRN("MQTT could not be uninitialized: %d", err); + LOG_WRN("%s uninitialization failed (%d).", "MQTT", err); } #endif #if defined(CONFIG_SM_HTTPC) err = sm_at_httpc_uninit(); if (err) { - LOG_WRN("HTTP could not be uninitialized: %d", err); + LOG_WRN("%s uninitialization failed (%d).", "HTTP", err); } #endif #if defined(CONFIG_SM_TWI) err = sm_at_twi_uninit(); if (err) { - LOG_ERR("TWI could not be uninit: %d", err); + LOG_WRN("%s uninitialization failed (%d).", "TWI", err); } #endif #if defined(CONFIG_SM_GPIO) err = sm_at_gpio_uninit(); if (err) { - LOG_ERR("GPIO could not be uninit: %d", err); + LOG_WRN("%s uninitialization failed (%d).", "GPIO", err); } #endif #if defined(CONFIG_SM_CARRIER) err = sm_at_carrier_uninit(); if (err) { - LOG_ERR("LwM2M carrier could not be uninitialized: %d", err); + LOG_WRN("%s uninitialization failed (%d).", "LwM2M carrier", err); } #endif +#if defined(CONFIG_SM_CMUX) + sm_cmux_uninit(); +#endif } diff --git a/app/src/sm_at_host.c b/app/src/sm_at_host.c index 2afcce48..e4e8d5d1 100644 --- a/app/src/sm_at_host.c +++ b/app/src/sm_at_host.c @@ -38,7 +38,6 @@ enum sm_operation_mode { SM_DATA_MODE, /* Raw data sending */ SM_NULL_MODE /* Discard incoming until next command */ }; -static struct sm_at_backend at_backend; static enum sm_operation_mode at_mode; static sm_datamode_handler_t datamode_handler; static int datamode_handler_result; @@ -445,52 +444,6 @@ static void format_final_result(char *buf, size_t buf_len, size_t buf_max_len) } } } -static void restore_at_backend(void) -{ - const int err = at_backend.start(); - - if (err) { - LOG_ERR("Failed to restore AT backend. (%d) Resetting.", err); - sm_reset(); - } -} - -static int stop_at_backend(void) -{ - const int err = at_backend.stop(); - - if (!err) { - /* Wait for UART disabling to complete. */ - k_sleep(K_MSEC(100)); - } - return err; -} - -int sm_at_set_backend(const struct sm_at_backend new_backend) -{ - const struct sm_at_backend old_backend = at_backend; - int ret; - - if (old_backend.start) { - ret = stop_at_backend(); - if (ret) { - LOG_ERR("Failed to stop previous AT backend. (%d)", ret); - return ret; - } - } - - at_backend = new_backend; - ret = new_backend.start(); - if (ret) { - LOG_ERR("Failed to start AT backend. (%d)", ret); - stop_at_backend(); - - at_backend = old_backend; - restore_at_backend(); - } - - return ret; -} static int sm_at_send_indicate(const uint8_t *data, size_t len, bool print_full_debug, bool indicate) @@ -500,9 +453,6 @@ static int sm_at_send_indicate(const uint8_t *data, size_t len, if (k_is_in_isr()) { LOG_ERR("FIXME: Attempt to send AT response (of size %u) in ISR.", len); return -EINTR; - } else if (at_backend.send == NULL) { - LOG_ERR("Attempt to send via an uninitialized AT backend"); - return -EFAULT; } if (indicate) { @@ -514,7 +464,7 @@ static int sm_at_send_indicate(const uint8_t *data, size_t len, } } - ret = at_backend.send(data, len); + ret = sm_tx_write(data, len); if (!ret) { LOG_HEXDUMP_DBG(data, print_full_debug ? len : MIN(HEXDUMP_LIMIT, len), "TX"); } @@ -531,7 +481,7 @@ int sm_at_send_str(const char *str) return sm_at_send(str, strlen(str)); } -static void cmd_send(uint8_t *buf, size_t cmd_length, size_t buf_size) +static void cmd_send(uint8_t *buf, size_t cmd_length, size_t buf_size, bool *stop_at_receive) { int err; size_t offset = 0; @@ -563,6 +513,10 @@ static void cmd_send(uint8_t *buf, size_t cmd_length, size_t buf_size) err = nrf_modem_at_cmd(buf + strlen(CRLF_STR), buf_size - strlen(CRLF_STR), "%s", at_cmd); if (err == -SILENT_AT_COMMAND_RET) { return; + } else if (err == -SILENT_AT_CMUX_COMMAND_RET) { + /* Stop processing AT commands until CMUX pipe is established. */ + *stop_at_receive = true; + return; } else if (err < 0) { LOG_ERR("AT command failed: %d", err); rsp_send_error(); @@ -586,7 +540,7 @@ static void cmd_send(uint8_t *buf, size_t cmd_length, size_t buf_size) } } -static size_t cmd_rx_handler(const uint8_t *buf, const size_t len) +static size_t cmd_rx_handler(const uint8_t *buf, const size_t len, bool *stop_at_receive) { size_t processed; static bool inside_quotes; @@ -661,7 +615,7 @@ static size_t cmd_rx_handler(const uint8_t *buf, const size_t len) rsp_send_error(); } else if (at_cmd_len > 0) { sm_at_buf[at_cmd_len] = '\0'; - cmd_send(sm_at_buf, at_cmd_len, sizeof(sm_at_buf)); + cmd_send(sm_at_buf, at_cmd_len, sizeof(sm_at_buf), stop_at_receive); } else { /* Ignore 0 size command. */ } @@ -712,42 +666,45 @@ static size_t null_handler(const uint8_t *buf, const size_t len) return processed; } -void sm_at_receive(const uint8_t *buf, size_t len) +size_t sm_at_receive(const uint8_t *buf, size_t len, bool *stop_at_receive) { size_t ret = 0; k_timer_stop(&inactivity_timer); - while (len > 0) { + while (ret < len) { switch (get_sm_mode()) { case SM_AT_COMMAND_MODE: - ret = cmd_rx_handler(buf, len); + ret += cmd_rx_handler(buf + ret, len - ret, stop_at_receive); + if (*stop_at_receive) { + return ret; + } break; case SM_DATA_MODE: - ret = raw_rx_handler(buf, len); + ret += raw_rx_handler(buf + ret, len - ret); break; case SM_NULL_MODE: - ret = null_handler(buf, len); + ret += null_handler(buf + ret, len - ret); break; } assert(ret <= len); - buf += ret; - len -= ret; } /* start inactivity timer in datamode */ if (get_sm_mode() == SM_DATA_MODE) { k_timer_start(&inactivity_timer, K_MSEC(sm_datamode_time_limit), K_NO_WAIT); } + + return ret; } AT_MONITOR(at_notify, ANY, notification_handler); static void notification_handler(const char *notification) { - if (get_sm_mode() == SM_AT_COMMAND_MODE && at_backend.send != NULL) { + if (get_sm_mode() == SM_AT_COMMAND_MODE) { #if defined(CONFIG_SM_PPP) if (!sm_fwd_cgev_notifs @@ -1001,9 +958,11 @@ int sm_at_host_init(void) static int at_host_power_off(bool shutting_down) { - int err = stop_at_backend(); + int err = sm_uart_handler_disable(); if (!err || shutting_down) { + /* Wait for UART disabling to complete. */ + k_sleep(K_MSEC(100)); /* Power off UART module */ err = pm_device_action_run(sm_uart_dev, PM_DEVICE_ACTION_SUSPEND); @@ -1012,9 +971,6 @@ static int at_host_power_off(bool shutting_down) } if (err) { LOG_WRN("Failed to suspend UART. (%d)", err); - if (!shutting_down) { - restore_at_backend(); - } } } @@ -1042,8 +998,8 @@ int sm_at_host_power_on(void) /* Wait for UART enabling to complete. */ k_sleep(K_MSEC(100)); + sm_uart_handler_enable(); - restore_at_backend(); return 0; } diff --git a/app/src/sm_at_host.h b/app/src/sm_at_host.h index 688d732d..dc880ae6 100644 --- a/app/src/sm_at_host.h +++ b/app/src/sm_at_host.h @@ -47,15 +47,6 @@ enum sm_datamode_operation { */ typedef int (*sm_datamode_handler_t)(uint8_t op, const uint8_t *data, int len, uint8_t flags); -/* All the AT backend API functions return 0 on success. */ -struct sm_at_backend { - int (*start)(void); - int (*send)(const uint8_t *data, size_t len); - int (*stop)(void); -}; -/** @retval 0 on success (the new backend is successfully started). */ -int sm_at_set_backend(struct sm_at_backend backend); - /** * @brief Sends the given data via the current AT backend. * @@ -66,8 +57,17 @@ int sm_at_send(const uint8_t *data, size_t len); /** @brief Identical to sm_at_send(str, strlen(str)). */ int sm_at_send_str(const char *str); -/** @brief Processes received AT bytes. */ -void sm_at_receive(const uint8_t *data, size_t len); +/** + * @brief Processes received AT bytes. + * + * @param data AT command bytes received. + * @param len Length of AT command bytes received. + * @param stop_at_receive Pointer to a boolean variable that will be set to true + * if the reception should be stopped. + * + * @retval Number of bytes processed. + */ +size_t sm_at_receive(const uint8_t *data, size_t len, bool *stop_at_receive); /** * @brief Initialize AT host for Serial Modem @@ -78,7 +78,7 @@ void sm_at_receive(const uint8_t *data, size_t len); int sm_at_host_init(void); /** - * @brief Turns the current AT backend and UART power off. + * @brief Powers the UART down. * * @retval 0 on success, or a (negative) error code. */ diff --git a/app/src/sm_cmux.c b/app/src/sm_cmux.c index bfdc8422..eb64e5da 100644 --- a/app/src/sm_cmux.c +++ b/app/src/sm_cmux.c @@ -9,8 +9,8 @@ #include "sm_ppp.h" #endif #include "sm_util.h" +#include "sm_uart_handler.h" #include -#include #include #include #include @@ -35,10 +35,6 @@ static struct { /* UART backend */ struct modem_pipe *uart_pipe; bool uart_pipe_open; - struct modem_backend_uart_slm uart_backend; - uint8_t uart_backend_receive_buf[CONFIG_SM_CMUX_UART_BUFFER_SIZE] - __aligned(sizeof(void *)); - uint8_t uart_backend_transmit_buf[CONFIG_SM_CMUX_UART_BUFFER_SIZE]; /* CMUX */ struct modem_cmux instance; @@ -73,6 +69,7 @@ static void rx_work_fn(struct k_work *work) static uint8_t recv_buf[RECV_BUF_LEN]; int ret; bool is_at; + bool ignored; for (int i = 0; i < ARRAY_SIZE(cmux.dlcis); ++i) { if (atomic_test_and_clear_bit(&cmux.dlci_channel_rx, i)) { @@ -93,7 +90,7 @@ static void rx_work_fn(struct k_work *work) } LOG_DBG("DLCI %u (AT) received %u bytes of data.", INDEX_TO_DLCI(i), ret); - sm_at_receive(recv_buf, ret); + sm_at_receive(recv_buf, ret, &ignored); } } } @@ -290,29 +287,6 @@ static void close_pipe(struct modem_pipe **pipe) } } -static int cmux_stop(void) -{ - if (cmux.uart_pipe && cmux.uart_pipe_open) { - /* CMUX is running. Just close the UART pipe to pause and be able to resume. - * This allows AT data to be cached by the CMUX module while the UART is off. - */ - modem_pipe_close_async(cmux.uart_pipe); - cmux.uart_pipe_open = false; - return 0; - } - - modem_cmux_release(&cmux.instance); - - close_pipe(&cmux.uart_pipe); - cmux.uart_pipe_open = false; - - for (size_t i = 0; i != ARRAY_SIZE(cmux.dlcis); ++i) { - close_pipe(&cmux.dlcis[i].pipe); - } - - return 0; -} - static bool cmux_is_started(void) { return (cmux.uart_pipe != NULL); @@ -344,6 +318,20 @@ void sm_cmux_init(void) cmux.requested_at_channel = UINT_MAX; } +void sm_cmux_uninit(void) +{ + if (cmux_is_started()) { + modem_cmux_release(&cmux.instance); + + close_pipe(&cmux.uart_pipe); + cmux.uart_pipe_open = false; + + for (size_t i = 0; i != ARRAY_SIZE(cmux.dlcis); ++i) { + close_pipe(&cmux.dlcis[i].pipe); + } + } +} + static struct cmux_dlci *cmux_get_dlci(enum cmux_channel channel) { #if defined(CONFIG_SM_PPP) @@ -396,16 +384,7 @@ static int cmux_start(void) } { - const struct modem_backend_uart_slm_config uart_backend_config = { - .uart = DEVICE_DT_GET(DT_CHOSEN(ncs_sm_uart)), - .receive_buf = cmux.uart_backend_receive_buf, - .receive_buf_size = sizeof(cmux.uart_backend_receive_buf), - .transmit_buf = cmux.uart_backend_transmit_buf, - .transmit_buf_size = sizeof(cmux.uart_backend_transmit_buf), - }; - - cmux.uart_pipe = - modem_backend_uart_slm_init(&cmux.uart_backend, &uart_backend_config); + cmux.uart_pipe = sm_uart_pipe_init(cmux_write_at_channel); if (!cmux.uart_pipe) { return -ENODEV; } @@ -423,26 +402,10 @@ static int cmux_start(void) return ret; } -static void cmux_starter(struct k_work *) -{ - const int ret = sm_at_set_backend((struct sm_at_backend) { - .start = cmux_start, - .send = cmux_write_at_channel, - .stop = cmux_stop - }); - - if (ret) { - LOG_ERR("Failed to start CMUX. (%d)", ret); - } else { - LOG_INF("CMUX started."); - } -} - SM_AT_CMD_CUSTOM(xcmux, "AT#XCMUX", handle_at_cmux); static int handle_at_cmux(enum at_parser_cmd_type cmd_type, struct at_parser *parser, uint32_t param_count) { - static struct k_work_delayable cmux_start_work; unsigned int at_dlci; int ret; @@ -480,12 +443,13 @@ static int handle_at_cmux(enum at_parser_cmd_type cmd_type, struct at_parser *pa cmux.at_channel = at_channel; } - k_work_init_delayable(&cmux_start_work, cmux_starter); - ret = k_work_schedule(&cmux_start_work, SM_UART_RESPONSE_DELAY); - if (ret == 1) { - ret = 0; - } else if (ret == 0) { - ret = -EAGAIN; + /* Respond before starting CMUX. */ + rsp_send_ok(); + ret = cmux_start(); + if (ret) { + LOG_ERR("Failed to start CMUX. (%d)", ret); + } else { + ret = -SILENT_AT_CMUX_COMMAND_RET; } return ret; } diff --git a/app/src/sm_cmux.h b/app/src/sm_cmux.h index 8e334fd3..4a6e02d9 100644 --- a/app/src/sm_cmux.h +++ b/app/src/sm_cmux.h @@ -10,8 +10,12 @@ struct modem_pipe; +/** @brief Initialize the CMUX subsystem. */ void sm_cmux_init(void); +/** @brief Uninitialize the CMUX subsystem. */ +void sm_cmux_uninit(void); + /* CMUX channels that are used by other modules. */ enum cmux_channel { #if defined(CONFIG_SM_PPP) diff --git a/app/src/sm_defines.h b/app/src/sm_defines.h index 709d3e20..6731784f 100644 --- a/app/src/sm_defines.h +++ b/app/src/sm_defines.h @@ -17,6 +17,8 @@ enum { /* The command ran successfully and doesn't want the automatic response to be sent. */ SILENT_AT_COMMAND_RET = __ELASTERROR, + /* The AT to CMUX change command ran successfully. */ + SILENT_AT_CMUX_COMMAND_RET, }; /** The maximum allowed length of an AT command/response passed through the Serial Modem */ diff --git a/app/src/sm_ppp.c b/app/src/sm_ppp.c index 5d919e2a..ea6a4b85 100644 --- a/app/src/sm_ppp.c +++ b/app/src/sm_ppp.c @@ -14,7 +14,9 @@ #include #include #include -#include +#if !defined(CONFIG_SM_CMUX) +#include +#endif #include #include #include @@ -616,12 +618,12 @@ int sm_ppp_init(void) } { - static struct modem_backend_uart_slm ppp_uart_backend; + static struct modem_backend_uart ppp_uart_backend; static uint8_t ppp_uart_backend_receive_buf[sizeof(ppp_data_buf)] __aligned(sizeof(void *)); static uint8_t ppp_uart_backend_transmit_buf[sizeof(ppp_data_buf)]; - const struct modem_backend_uart_slm_config uart_backend_config = { + const struct modem_backend_uart_config uart_backend_config = { .uart = ppp_uart_dev, .receive_buf = ppp_uart_backend_receive_buf, .receive_buf_size = sizeof(ppp_uart_backend_receive_buf), @@ -629,7 +631,7 @@ int sm_ppp_init(void) .transmit_buf_size = sizeof(ppp_uart_backend_transmit_buf), }; - ppp_pipe = modem_backend_uart_slm_init(&ppp_uart_backend, &uart_backend_config); + ppp_pipe = modem_backend_uart_init(&ppp_uart_backend, &uart_backend_config); if (!ppp_pipe) { return -ENOSYS; } diff --git a/app/src/sm_uart_handler.c b/app/src/sm_uart_handler.c index 0b19f8e0..798282a3 100644 --- a/app/src/sm_uart_handler.c +++ b/app/src/sm_uart_handler.c @@ -11,12 +11,15 @@ #include #include #include +#include #include "sm_uart_handler.h" #include "sm_at_host.h" #include LOG_MODULE_REGISTER(sm_uart_handler, CONFIG_SM_LOG_LEVEL); +#define SM_PIPE CONFIG_SM_CMUX + #define UART_RX_TIMEOUT_US 2000 #define UART_ERROR_DELAY_MS 500 @@ -49,12 +52,30 @@ K_MSGQ_DEFINE(rx_event_queue, sizeof(struct rx_event_t), UART_RX_EVENT_COUNT, 4) RING_BUF_DECLARE(tx_buf, CONFIG_SM_UART_TX_BUF_SIZE); K_MUTEX_DEFINE(mutex_tx_put); /* Protects the tx_buf from multiple writes. */ -enum uart_recovery_state { - RECOVERY_IDLE, - RECOVERY_ONGOING, - RECOVERY_DISABLED +enum sm_uart_state { + SM_UART_STATE_TX_ENABLED_BIT, + SM_UART_STATE_RX_ENABLED_BIT, + SM_UART_STATE_RX_RECOVERY_BIT, + SM_UART_STATE_RX_RECOVERY_DISABLED_BIT +}; +static atomic_t uart_state; + +#if SM_PIPE + +enum sm_pipe_state { + SM_PIPE_STATE_INIT_BIT, + SM_PIPE_STATE_OPEN_BIT, }; -static atomic_t recovery_state; +static struct { + struct modem_pipe pipe; + sm_pipe_tx_t tx_cb; + atomic_t state; + + struct k_work notify_transmit_idle; + struct k_work notify_closed; +} sm_pipe; + +#endif K_SEM_DEFINE(tx_done_sem, 0, 1); @@ -112,6 +133,10 @@ static int rx_enable(void) struct rx_buf_t *buf; int ret; + if (atomic_test_bit(&uart_state, SM_UART_STATE_RX_ENABLED_BIT)) { + return 0; + } + buf = rx_buf_alloc(); if (!buf) { LOG_ERR("UART RX failed to allocate buffer"); @@ -121,25 +146,28 @@ static int rx_enable(void) ret = uart_rx_enable(sm_uart_dev, buf->buf, sizeof(buf->buf), UART_RX_TIMEOUT_US); if (ret) { LOG_ERR("UART RX enable failed: %d", ret); + rx_buf_unref(buf); return ret; } + atomic_set_bit(&uart_state, SM_UART_STATE_RX_ENABLED_BIT); return 0; } -static int sm_uart_rx_disable(void) +static int rx_disable(void) { int err; - /* Wait for possible rx_enable to complete. */ - if (atomic_set(&recovery_state, RECOVERY_DISABLED) == RECOVERY_ONGOING) { + atomic_set_bit(&uart_state, SM_UART_STATE_RX_RECOVERY_DISABLED_BIT); + + while (atomic_test_bit(&uart_state, SM_UART_STATE_RX_RECOVERY_BIT)) { + /* Wait until possible recovery is complete. */ k_sleep(K_MSEC(10)); } err = uart_rx_disable(sm_uart_dev); if (err) { LOG_ERR("UART RX disable failed: %d", err); - atomic_set(&recovery_state, RECOVERY_IDLE); return err; } @@ -150,31 +178,91 @@ static void rx_recovery(void) { int err; - if (atomic_get(&recovery_state) != RECOVERY_ONGOING) { + if (atomic_test_bit(&uart_state, SM_UART_STATE_RX_RECOVERY_DISABLED_BIT)) { return; } + atomic_set_bit(&uart_state, SM_UART_STATE_RX_RECOVERY_BIT); + err = rx_enable(); if (err) { k_work_schedule(&rx_process_work, K_MSEC(UART_RX_MARGIN_MS)); - return; } - atomic_cas(&recovery_state, RECOVERY_ONGOING, RECOVERY_IDLE); + atomic_clear_bit(&uart_state, SM_UART_STATE_RX_RECOVERY_BIT); } static void rx_process(struct k_work *work) { +#if SM_PIPE + /* With pipe, CMUX layer is notified and it requests the data. */ + if (atomic_test_bit(&sm_pipe.state, SM_PIPE_STATE_INIT_BIT)) { + if (atomic_test_bit(&sm_pipe.state, SM_PIPE_STATE_OPEN_BIT)) { + modem_pipe_notify_receive_ready(&sm_pipe.pipe); + } + return; + } +#endif + /* Without pipe, we push the data immediately. */ struct rx_event_t rx_event; + size_t processed; + bool stop_at_receive = false; + int err; while (k_msgq_get(&rx_event_queue, &rx_event, K_NO_WAIT) == 0) { - sm_at_receive(rx_event.buf, rx_event.len); - rx_buf_unref(rx_event.buf); + processed = sm_at_receive(rx_event.buf, rx_event.len, &stop_at_receive); + + if (processed == rx_event.len) { + /* All data processed, release the buffer. */ + rx_buf_unref(rx_event.buf); + } else { + rx_event.len -= processed; + rx_event.buf += processed; + err = k_msgq_put_front(&rx_event_queue, &rx_event, K_NO_WAIT); + if (err) { + LOG_ERR("RX event queue full, dropped %zu bytes", rx_event.len); + rx_buf_unref(rx_event.buf); + } + } + + if (stop_at_receive) { + break; + } } rx_recovery(); } +static void tx_enable(void) +{ + if (!atomic_test_and_set_bit(&uart_state, SM_UART_STATE_TX_ENABLED_BIT)) { + k_sem_give(&tx_done_sem); + } +} + +static int tx_disable(k_timeout_t timeout) +{ + int err; + + if (!atomic_test_and_clear_bit(&uart_state, SM_UART_STATE_TX_ENABLED_BIT)) { + return 0; + } + + if (k_sem_take(&tx_done_sem, timeout) == 0) { + return 0; + } + + err = uart_tx_abort(sm_uart_dev); + if (!err) { + LOG_INF("TX aborted"); + } else if (err != -EFAULT) { + LOG_ERR("uart_tx_abort failed (%d).", err); + return err; + } + + return 0; +} + static int tx_start(void) { uint8_t *buf; @@ -182,6 +270,10 @@ static int tx_start(void) int err; enum pm_device_state state = PM_DEVICE_STATE_OFF; + if (!atomic_test_bit(&uart_state, SM_UART_STATE_TX_ENABLED_BIT)) { + return -EAGAIN; + } + pm_device_state_get(sm_uart_dev, &state); if (state != PM_DEVICE_STATE_ACTIVE) { return 1; @@ -198,6 +290,28 @@ static int tx_start(void) return 0; } +static inline void uart_callback_notify_pipe_transmit_idle(void) +{ +#if SM_PIPE + if (atomic_test_bit(&sm_pipe.state, SM_PIPE_STATE_OPEN_BIT)) { + k_work_submit(&sm_pipe.notify_transmit_idle); + } +#endif +} + +static inline void uart_callback_notify_pipe_closure(void) +{ +#if SM_PIPE + if (atomic_test_bit(&sm_pipe.state, SM_PIPE_STATE_INIT_BIT) && + !atomic_test_bit(&sm_pipe.state, SM_PIPE_STATE_OPEN_BIT) && + !atomic_test_bit(&uart_state, SM_UART_STATE_RX_ENABLED_BIT) && + !atomic_test_bit(&uart_state, SM_UART_STATE_TX_ENABLED_BIT)) { + /* Pipe is closed, RX and TX are idle, notify the closure */ + k_work_submit(&sm_pipe.notify_closed); + } +#endif +} + static void uart_callback(const struct device *dev, struct uart_event *evt, void *user_data) { struct rx_buf_t *buf; @@ -215,10 +329,11 @@ static void uart_callback(const struct device *dev, struct uart_event *evt, void LOG_ERR("UART_TX_%s failure: %d", (evt->type == UART_TX_DONE) ? "DONE" : "ABORTED", err); } - if (ring_buf_is_empty(&tx_buf) == false) { + if (ring_buf_is_empty(&tx_buf) == false && evt->type == UART_TX_DONE) { tx_start(); } else { k_sem_give(&tx_done_sem); + uart_callback_notify_pipe_transmit_idle(); } break; case UART_RX_RDY: @@ -227,7 +342,7 @@ static void uart_callback(const struct device *dev, struct uart_event *evt, void rx_event.len = evt->data.rx.len; err = k_msgq_put(&rx_event_queue, &rx_event, K_NO_WAIT); if (err) { - LOG_ERR("UART_RX_RDY failure: %d, dropped: %d", err, evt->data.rx.len); + LOG_ERR("RX event queue full, dropped %zu bytes", evt->data.rx.len); rx_buf_unref(evt->data.rx.buf); break; } @@ -255,16 +370,19 @@ static void uart_callback(const struct device *dev, struct uart_event *evt, void } break; case UART_RX_DISABLED: - if (atomic_cas(&recovery_state, RECOVERY_IDLE, RECOVERY_ONGOING)) { - k_work_submit((struct k_work *)&rx_process_work); - } + atomic_clear_bit(&uart_state, SM_UART_STATE_RX_ENABLED_BIT); + k_work_submit((struct k_work *)&rx_process_work); break; default: break; } + + uart_callback_notify_pipe_closure(); } -/* Write the data to tx_buffer and trigger sending. */ +/* Write the data to tx_buffer and trigger sending. Repeat until everything is sent. + * Returns 0 on success or a negative error code. + */ static int sm_uart_tx_write(const uint8_t *data, size_t len) { size_t ret; @@ -272,33 +390,40 @@ static int sm_uart_tx_write(const uint8_t *data, size_t len) int err; k_mutex_lock(&mutex_tx_put, K_FOREVER); + while (sent < len) { ret = ring_buf_put(&tx_buf, data + sent, len - sent); if (ret) { sent += ret; - } else { - /* Buffer full, block and start TX. */ - k_sem_take(&tx_done_sem, K_FOREVER); - err = tx_start(); - if (err) { - LOG_ERR("TX buf overflow, %d dropped. Unable to send: %d", - len - sent, - err); - k_sem_give(&tx_done_sem); - k_mutex_unlock(&mutex_tx_put); - return err; - } + continue; + } + + /* Buffer full, block and start TX. */ + err = k_sem_take(&tx_done_sem, K_FOREVER); + if (err) { + LOG_ERR("TX %s failed (%d). TX buf overflow, %zu dropped.", + "semaphore take", err, len - sent); + k_mutex_unlock(&mutex_tx_put); + return err; + } + err = tx_start(); + if (err) { + LOG_ERR("TX %s failed (%d). TX buf overflow, %zu dropped.", "start", err, + len - sent); + k_sem_give(&tx_done_sem); + k_mutex_unlock(&mutex_tx_put); + return err; } } k_mutex_unlock(&mutex_tx_put); if (k_sem_take(&tx_done_sem, K_NO_WAIT) == 0) { err = tx_start(); - if (err == 1) { + if (err == 1 || err == -EAGAIN) { k_sem_give(&tx_done_sem); return 0; } else if (err) { - LOG_ERR("TX start failed: %d", err); + LOG_ERR("TX %s failed (%d).", "start", err); k_sem_give(&tx_done_sem); return err; } @@ -309,7 +434,17 @@ static int sm_uart_tx_write(const uint8_t *data, size_t len) return 0; } -static int sm_uart_handler_init(void) +int sm_tx_write(const uint8_t *data, size_t len) +{ +#if SM_PIPE + if (atomic_test_bit(&sm_pipe.state, SM_PIPE_STATE_INIT_BIT) && sm_pipe.tx_cb != NULL) { + return sm_pipe.tx_cb(data, len); + } +#endif + return sm_uart_tx_write(data, len); +} + +int sm_uart_handler_enable(void) { int err; uint32_t start_time; @@ -350,7 +485,9 @@ static int sm_uart_handler_init(void) LOG_ERR("Cannot set callback: %d", err); return -EFAULT; } - (void)atomic_set(&recovery_state, RECOVERY_IDLE); + + atomic_clear(&uart_state); + tx_enable(); err = rx_enable(); if (err) { return -EFAULT; @@ -358,19 +495,220 @@ static int sm_uart_handler_init(void) k_work_init_delayable(&rx_process_work, rx_process); - k_sem_give(&tx_done_sem); - /* Flush possibly pending data in case Serial Modem was idle. */ tx_start(); return 0; } -int sm_uart_handler_enable(void) +int sm_uart_handler_disable(void) { - return sm_at_set_backend((struct sm_at_backend) { - .start = sm_uart_handler_init, - .send = sm_uart_tx_write, - .stop = sm_uart_rx_disable - }); + int err; + + err = tx_disable(K_MSEC(50)); + if (err) { + LOG_ERR("TX disable failed (%d).", err); + return err; + } + + err = rx_disable(); + if (err) { + LOG_ERR("RX disable failed (%d).", err); + return err; + } + + return 0; } + +#if SM_PIPE + +static int pipe_open(void *data) +{ + int ret; + + ARG_UNUSED(data); + + if (!atomic_test_bit(&sm_pipe.state, SM_PIPE_STATE_INIT_BIT)) { + return -EINVAL; + } + + if (atomic_test_bit(&sm_pipe.state, SM_PIPE_STATE_OPEN_BIT)) { + return -EALREADY; + } + + atomic_clear_bit(&uart_state, SM_UART_STATE_RX_RECOVERY_DISABLED_BIT); + ret = rx_enable(); + if (ret) { + return ret; + } + + tx_enable(); + + atomic_set_bit(&sm_pipe.state, SM_PIPE_STATE_OPEN_BIT); + modem_pipe_notify_opened(&sm_pipe.pipe); + + return 0; +} + +/* Returns the number of bytes written or a negative error code. */ +static int pipe_transmit(void *data, const uint8_t *buf, size_t size) +{ + size_t ret; + size_t sent = 0; + + ARG_UNUSED(data); + + if (!atomic_test_bit(&sm_pipe.state, SM_PIPE_STATE_OPEN_BIT)) { + return -EPERM; + } + + if (!buf || size == 0) { + return -EINVAL; + } + + while (sent < size) { + ret = ring_buf_put(&tx_buf, buf + sent, size - sent); + if (ret) { + sent += ret; + } else { + /* Buffer full. */ + break; + } + } + + if (k_sem_take(&tx_done_sem, K_NO_WAIT) == 0) { + int err = tx_start(); + + if (err == 1 || err == -EAGAIN) { + k_sem_give(&tx_done_sem); + return (int)sent; + } else if (err) { + LOG_ERR("TX %s failed (%d).", "start", err); + k_sem_give(&tx_done_sem); + return err; + } + } + + return (int)sent; +} + +static int pipe_receive(void *data, uint8_t *buf, size_t size) +{ + struct rx_event_t rx_event; + size_t received = 0; + size_t copy_size; + int err; + + ARG_UNUSED(data); + + if (!buf || size == 0) { + return 0; + } + + while (size > received) { + if (k_msgq_get(&rx_event_queue, &rx_event, K_NO_WAIT)) { + break; + } + copy_size = MIN(size - received, rx_event.len); + memcpy(buf, rx_event.buf, copy_size); + received += copy_size; + buf += copy_size; + + if (rx_event.len == copy_size) { + rx_buf_unref(rx_event.buf); + } else { + rx_event.len -= copy_size; + rx_event.buf += copy_size; + err = k_msgq_put_front(&rx_event_queue, &rx_event, K_NO_WAIT); + if (err) { + LOG_ERR("RX event queue full, dropped %zu bytes", rx_event.len); + rx_buf_unref(rx_event.buf); + } + } + } + if (k_msgq_num_used_get(&rx_event_queue) == 0) { + /* Try to recover RX, in case it was disabled. */ + rx_recovery(); + } + + return (int)received; +} + +static int pipe_close(void *data) +{ + int err; + + ARG_UNUSED(data); + + if (!atomic_test_bit(&sm_pipe.state, SM_PIPE_STATE_OPEN_BIT)) { + return -EALREADY; + } + + atomic_clear_bit(&sm_pipe.state, SM_PIPE_STATE_OPEN_BIT); + + err = tx_disable(K_MSEC(50)); + if (err) { + LOG_WRN("%s disable failed (%d).", "TX", err); + } + + err = rx_disable(); + if (err) { + LOG_ERR("%s disable failed (%d).", "RX", err); + } + + return 0; +} + +static const struct modem_pipe_api modem_pipe_api = { + .open = pipe_open, + .transmit = pipe_transmit, + .receive = pipe_receive, + .close = pipe_close, +}; + +static void notify_transmit_idle_fn(struct k_work *work) +{ + ARG_UNUSED(work); + modem_pipe_notify_transmit_idle(&sm_pipe.pipe); +} +static void notify_closed_fn(struct k_work *work) +{ + ARG_UNUSED(work); + modem_pipe_notify_closed(&sm_pipe.pipe); +} + +static void at_to_cmux_switch(void) +{ + /* TX handling when moving from AT to CMUX: + * - Complete (OK message) TX transmission through regular UART. + */ + tx_disable(K_MSEC(10)); + + /* Possible TX handling improvements: + * - Callers in sm_uart_tx_write need to abort when CMUX change is done. There may be + * multiple callers and they may be blocked. + * - TX buffer must be flushed or the data must be routed to CMUX. + */ + + /* RX handling when moving from AT to CMUX: + * - RX and RX buffers are retained. + * - Data in RX buffers is routed to CMUX AT channel. + */ +} + +struct modem_pipe *sm_uart_pipe_init(sm_pipe_tx_t pipe_tx_cb) +{ + k_work_init(&sm_pipe.notify_transmit_idle, notify_transmit_idle_fn); + k_work_init(&sm_pipe.notify_closed, notify_closed_fn); + + sm_pipe.tx_cb = pipe_tx_cb; + atomic_set_bit(&sm_pipe.state, SM_PIPE_STATE_INIT_BIT); + + modem_pipe_init(&sm_pipe.pipe, &sm_pipe, &modem_pipe_api); + + at_to_cmux_switch(); + + return &sm_pipe.pipe; +} + +#endif /* SM_PIPE */ diff --git a/app/src/sm_uart_handler.h b/app/src/sm_uart_handler.h index 54baa04e..b139d36a 100644 --- a/app/src/sm_uart_handler.h +++ b/app/src/sm_uart_handler.h @@ -14,15 +14,52 @@ */ #include "sm_trap_macros.h" #include +#include #define UART_RX_MARGIN_MS 10 extern const struct device *const sm_uart_dev; extern uint32_t sm_uart_baudrate; -/** @retval 0 on success. Otherwise, the error code is returned. */ +/** + * @brief UART pipe transmit callback type. + * + * @retval Amount of bytes written on success, otherwise a negative error code. + */ +typedef int (*sm_pipe_tx_t)(const uint8_t *data, size_t len); + +/** + * @brief Enable the UART handler. + * + * @retval 0 on success. Otherwise, a negative error code. + */ int sm_uart_handler_enable(void); +/** @brief Disable the UART handler. + * + * @retval 0 on success. Otherwise, a negative error code. + */ +int sm_uart_handler_disable(void); + +/** + * @brief Write data to UART or to a modem pipe. + * + * @param data Data to write. + * @param len Length of data to write. + * + * @retval 0 on success. Otherwise, a negative error code. + */ +int sm_tx_write(const uint8_t *data, size_t len); + +/** + * @brief Initialize UART pipe for Serial Modem. + * + * @param pipe_tx Transmit callback for the pipe. + * + * @retval Pointer to the initialized pipe on success, NULL otherwise. + */ +struct modem_pipe *sm_uart_pipe_init(sm_pipe_tx_t pipe_tx); + /** @} */ #endif /* SM_UART_HANDLER_ */ diff --git a/doc/app/sm_description.rst b/doc/app/sm_description.rst index 880e75fe..1f89618f 100644 --- a/doc/app/sm_description.rst +++ b/doc/app/sm_description.rst @@ -340,8 +340,11 @@ The following configuration files are provided: This disables most of the IP-based protocols available through AT commands (such as FTP and MQTT) as it is expected that the controlling chip's own IP stack is used instead. See :ref:`CONFIG_SM_PPP ` and :ref:`SM_AT_PPP` for more information. -* :file:`overlay-ppp-without-cmux.overlay` - Devicetree overlay that configures the UART to be used by PPP. - This configuration file should be included when building Serial Modem with PPP and without CMUX, in addition to :file:`overlay-ppp.conf`. +* :file:`overlay-ppp-without-cmux.conf` - Configuration file that enables support for the second UART to be used by PPP. + This configuration file should be included when building Serial Modem with PPP and without CMUX, in addition to :file:`overlay-ppp.conf` and :file:`overlay-ppp-without-cmux.overlay`. + +* :file:`overlay-ppp-without-cmux.overlay` - Devicetree overlay that configures the second UART to be used by PPP. + This configuration file should be included when building Serial Modem with PPP and without CMUX, in addition to :file:`overlay-ppp.conf` and :file:`overlay-ppp-without-cmux.conf`. It can be customized to fit your configuration, such as UART settings, baud rate, and flow control. By default, it sets the baud rate of the PPP UART to 1 000 000.