modem: modem_cellular: Configurable MTU for CMUX

Allow configuring MTU for CMUX.
Some AT manual and specification define this as a
frame size. Linux ldattach default to 127 bytes,
3GPP TS 27.010 defaults to 31.

We should limit our CMUX frames to a size that
remote end is capable of handling.
Linux silently drops oversized frames.

Also, remove MODEM_CELLULAR_CMUX_MAX_FRAME_SIZE as
this was only limiting a buffer sizes, and resulted
CMUX frames to be capped to same value.
Use MODEM_CMUX_WORK_BUFFER_SIZE and MODEM_CMUX_MTU instead.

Also rename CONFIG_MODEM_CELLULAR_CHAT_BUFFER_SIZES to
CONFIG_MODEM_CELLULAR_CHAT_BUFFER_SIZE as it is now
only used as a Chat module. DLCI pipes use
CONFIG_MODEM_CMUX_WORK_BUFFER_SIZE.

Signed-off-by: Seppo Takalo <seppo.takalo@nordicsemi.no>
This commit is contained in:
Seppo Takalo 2025-03-14 14:58:09 +02:00 committed by Benjamin Cabé
commit c78081e5cf
11 changed files with 75 additions and 23 deletions

View file

@ -42,15 +42,8 @@ config MODEM_CELLULAR_UART_BUFFER_SIZES
int "The UART receive and transmit buffer sizes in bytes." int "The UART receive and transmit buffer sizes in bytes."
default 512 default 512
config MODEM_CELLULAR_CMUX_MAX_FRAME_SIZE config MODEM_CELLULAR_CHAT_BUFFER_SIZE
int "The maximum CMUX frame size in bytes." int "The size of the buffer used for the chat scripts in bytes."
default 32 if DT_HAS_U_BLOX_LARA_R6_ENABLED
default 128
help
This value affects the size of buffers used to receive and transmit CMUX frames.
config MODEM_CELLULAR_CHAT_BUFFER_SIZES
int "The size of the buffers used for the chat scripts in bytes."
default 128 default 128
config MODEM_CELLULAR_USER_PIPE_BUFFER_SIZES config MODEM_CELLULAR_USER_PIPE_BUFFER_SIZES

View file

@ -90,8 +90,8 @@ struct modem_cellular_data {
/* CMUX */ /* CMUX */
struct modem_cmux cmux; struct modem_cmux cmux;
uint8_t cmux_receive_buf[CONFIG_MODEM_CELLULAR_CMUX_MAX_FRAME_SIZE]; uint8_t cmux_receive_buf[CONFIG_MODEM_CMUX_WORK_BUFFER_SIZE];
uint8_t cmux_transmit_buf[2 * CONFIG_MODEM_CELLULAR_CMUX_MAX_FRAME_SIZE]; uint8_t cmux_transmit_buf[CONFIG_MODEM_CMUX_WORK_BUFFER_SIZE];
struct modem_cmux_dlci dlci1; struct modem_cmux_dlci dlci1;
struct modem_cmux_dlci dlci2; struct modem_cmux_dlci dlci2;
@ -99,13 +99,13 @@ struct modem_cellular_data {
struct modem_pipe *dlci2_pipe; struct modem_pipe *dlci2_pipe;
/* Points to dlci2_pipe or NULL. Used for shutdown script if not NULL */ /* Points to dlci2_pipe or NULL. Used for shutdown script if not NULL */
struct modem_pipe *cmd_pipe; struct modem_pipe *cmd_pipe;
uint8_t dlci1_receive_buf[CONFIG_MODEM_CELLULAR_CMUX_MAX_FRAME_SIZE]; uint8_t dlci1_receive_buf[CONFIG_MODEM_CMUX_WORK_BUFFER_SIZE];
/* DLCI 2 is only used for chat scripts. */ /* DLCI 2 is only used for chat scripts. */
uint8_t dlci2_receive_buf[CONFIG_MODEM_CELLULAR_CHAT_BUFFER_SIZES]; uint8_t dlci2_receive_buf[CONFIG_MODEM_CMUX_WORK_BUFFER_SIZE];
/* Modem chat */ /* Modem chat */
struct modem_chat chat; struct modem_chat chat;
uint8_t chat_receive_buf[CONFIG_MODEM_CELLULAR_CHAT_BUFFER_SIZES]; uint8_t chat_receive_buf[CONFIG_MODEM_CELLULAR_CHAT_BUFFER_SIZE];
uint8_t *chat_delimiter; uint8_t *chat_delimiter;
uint8_t *chat_filter; uint8_t *chat_filter;
uint8_t *chat_argv[32]; uint8_t *chat_argv[32];

View file

@ -1,3 +1,2 @@
CONFIG_MODEM_HL7800=n CONFIG_MODEM_HL7800=n
CONFIG_MODEM_CELLULAR_CMUX_MAX_FRAME_SIZE=255
CONFIG_NET_BUF_TX_COUNT=32 CONFIG_NET_BUF_TX_COUNT=32

View file

@ -3,7 +3,6 @@ CONFIG_UART_1_ASYNC=y
CONFIG_UART_1_INTERRUPT_DRIVEN=n CONFIG_UART_1_INTERRUPT_DRIVEN=n
# Align with the Serial LTE Modem (SLM) application. # Align with the Serial LTE Modem (SLM) application.
CONFIG_MODEM_CELLULAR_CMUX_MAX_FRAME_SIZE=1500
CONFIG_MODEM_CELLULAR_UART_BUFFER_SIZES=6000 CONFIG_MODEM_CELLULAR_UART_BUFFER_SIZES=6000
# Print logs and printk() output on uart0. # Print logs and printk() output on uart0.

View file

@ -3,7 +3,6 @@ CONFIG_UART_1_ASYNC=y
CONFIG_UART_1_INTERRUPT_DRIVEN=n CONFIG_UART_1_INTERRUPT_DRIVEN=n
# Align with the Serial LTE Modem (SLM) application. # Align with the Serial LTE Modem (SLM) application.
CONFIG_MODEM_CELLULAR_CMUX_MAX_FRAME_SIZE=1500
CONFIG_MODEM_CELLULAR_UART_BUFFER_SIZES=6000 CONFIG_MODEM_CELLULAR_UART_BUFFER_SIZES=6000
# Prevent sockets getting offloaded to the modem. # Prevent sockets getting offloaded to the modem.

View file

@ -1,3 +1,2 @@
CONFIG_MODEM_HL7800=n CONFIG_MODEM_HL7800=n
CONFIG_MODEM_CELLULAR_CMUX_MAX_FRAME_SIZE=255
CONFIG_NET_BUF_TX_COUNT=32 CONFIG_NET_BUF_TX_COUNT=32

View file

@ -29,10 +29,34 @@ config MODEM_CMUX
if MODEM_CMUX if MODEM_CMUX
config MODEM_CMUX_DEFAULT_MTU_127
bool
default y if (DT_HAS_QUECTEL_BG95_ENABLED || DT_HAS_QUECTEL_EG25_G_ENABLED || \
DT_HAS_SIMCOM_SIM7080_ENABLED || DT_HAS_U_BLOX_SARA_R4_ENABLED || \
DT_HAS_U_BLOX_SARA_R5_ENABLED || DT_HAS_SWIR_HL7800_ENABLED || \
DT_HAS_TELIT_ME910G1_ENABLED || DT_HAS_TELIT_ME310G1_ENABLED || \
DT_HAS_SQN_GM02S_ENABLED)
help
Use the default MTU size of 127 bytes for the CMUX module on certain modems.
This must match the AT+CMUX commands in the modem_cellular driver.
config MODEM_CMUX_MTU
int "CMUX MTU size in bytes"
range 16 1500
default 127 if MODEM_CMUX_DEFAULT_MTU_127
default 31
help
Maximum Transmission Unit (MTU) size for the CMUX module.
Linux ldattach defaults to 127 bytes, 3GPP TS 27.010 to 31.
config MODEM_CMUX_WORK_BUFFER_SIZE config MODEM_CMUX_WORK_BUFFER_SIZE
int "CMUX module work buffer size in bytes" int "CMUX module work buffer size in bytes"
range 16 1500 range 23 1507
default 64 default 134 if MODEM_CMUX_DEFAULT_MTU_127
default 38
help
Size of the work buffer used by the CMUX module.
Recommended size is MODEM_CMUX_MTU + 7 (CMUX header size).
module = MODEM_CMUX module = MODEM_CMUX
module-str = modem_cmux module-str = modem_cmux

View file

@ -18,7 +18,7 @@ LOG_MODULE_REGISTER(modem_cmux, CONFIG_MODEM_CMUX_LOG_LEVEL);
#define MODEM_CMUX_EA (0x01) #define MODEM_CMUX_EA (0x01)
#define MODEM_CMUX_CR (0x02) #define MODEM_CMUX_CR (0x02)
#define MODEM_CMUX_PF (0x10) #define MODEM_CMUX_PF (0x10)
#define MODEM_CMUX_FRAME_SIZE_MAX (0x08) #define MODEM_CMUX_FRAME_SIZE_MAX (0x07)
#define MODEM_CMUX_DATA_SIZE_MIN (0x08) #define MODEM_CMUX_DATA_SIZE_MIN (0x08)
#define MODEM_CMUX_DATA_FRAME_SIZE_MIN (MODEM_CMUX_FRAME_SIZE_MAX + \ #define MODEM_CMUX_DATA_FRAME_SIZE_MIN (MODEM_CMUX_FRAME_SIZE_MAX + \
MODEM_CMUX_DATA_SIZE_MIN) MODEM_CMUX_DATA_SIZE_MIN)
@ -274,6 +274,7 @@ static uint16_t modem_cmux_transmit_frame(struct modem_cmux *cmux,
space = ring_buf_space_get(&cmux->transmit_rb) - MODEM_CMUX_FRAME_SIZE_MAX; space = ring_buf_space_get(&cmux->transmit_rb) - MODEM_CMUX_FRAME_SIZE_MAX;
data_len = MIN(space, frame->data_len); data_len = MIN(space, frame->data_len);
data_len = MIN(data_len, CONFIG_MODEM_CMUX_MTU);
/* SOF */ /* SOF */
buf[0] = 0xF9; buf[0] = 0xF9;
@ -797,6 +798,12 @@ static void modem_cmux_process_received_byte(struct modem_cmux *cmux, uint8_t by
break; break;
} }
if (cmux->frame.data_len > CONFIG_MODEM_CMUX_MTU) {
LOG_ERR("Too large frame");
cmux->receive_state = MODEM_CMUX_RECEIVE_STATE_DROP;
break;
}
/* Check if no data field */ /* Check if no data field */
if (cmux->frame.data_len == 0) { if (cmux->frame.data_len == 0) {
/* Await FCS */ /* Await FCS */
@ -816,6 +823,12 @@ static void modem_cmux_process_received_byte(struct modem_cmux *cmux, uint8_t by
/* Get last 8 bits of data length */ /* Get last 8 bits of data length */
cmux->frame.data_len |= ((uint16_t)byte) << 7; cmux->frame.data_len |= ((uint16_t)byte) << 7;
if (cmux->frame.data_len > CONFIG_MODEM_CMUX_MTU) {
LOG_ERR("Too large frame");
cmux->receive_state = MODEM_CMUX_RECEIVE_STATE_DROP;
break;
}
if (cmux->frame.data_len > cmux->receive_buf_size) { if (cmux->frame.data_len > cmux->receive_buf_size) {
LOG_ERR("Indicated frame data length %u exceeds receive buffer size %u", LOG_ERR("Indicated frame data length %u exceeds receive buffer size %u",
cmux->frame.data_len, cmux->receive_buf_size); cmux->frame.data_len, cmux->receive_buf_size);
@ -1239,9 +1252,11 @@ void modem_cmux_init(struct modem_cmux *cmux, const struct modem_cmux_config *co
__ASSERT_NO_MSG(cmux != NULL); __ASSERT_NO_MSG(cmux != NULL);
__ASSERT_NO_MSG(config != NULL); __ASSERT_NO_MSG(config != NULL);
__ASSERT_NO_MSG(config->receive_buf != NULL); __ASSERT_NO_MSG(config->receive_buf != NULL);
__ASSERT_NO_MSG(config->receive_buf_size >= 126); __ASSERT_NO_MSG(config->receive_buf_size >=
(CONFIG_MODEM_CMUX_MTU + MODEM_CMUX_FRAME_SIZE_MAX));
__ASSERT_NO_MSG(config->transmit_buf != NULL); __ASSERT_NO_MSG(config->transmit_buf != NULL);
__ASSERT_NO_MSG(config->transmit_buf_size >= 148); __ASSERT_NO_MSG(config->transmit_buf_size >=
(CONFIG_MODEM_CMUX_MTU + MODEM_CMUX_FRAME_SIZE_MAX));
memset(cmux, 0x00, sizeof(*cmux)); memset(cmux, 0x00, sizeof(*cmux));
cmux->callback = config->callback; cmux->callback = config->callback;

View file

@ -5,5 +5,6 @@ CONFIG_NO_OPTIMIZATIONS=y
CONFIG_MODEM_MODULES=y CONFIG_MODEM_MODULES=y
CONFIG_MODEM_CMUX=y CONFIG_MODEM_CMUX=y
CONFIG_MODEM_CMUX_MTU=64
CONFIG_ZTEST=y CONFIG_ZTEST=y

View file

@ -27,6 +27,8 @@
#define EVENT_CMUX_DLCI1_CLOSED BIT(7) #define EVENT_CMUX_DLCI1_CLOSED BIT(7)
#define EVENT_CMUX_DLCI2_CLOSED BIT(8) #define EVENT_CMUX_DLCI2_CLOSED BIT(8)
#define EVENT_CMUX_DISCONNECTED BIT(9) #define EVENT_CMUX_DISCONNECTED BIT(9)
#define CMUX_BASIC_HRD_SMALL_SIZE 6
#define CMUX_BASIC_HRD_LARGE_SIZE 7
/*************************************************************************************************/ /*************************************************************************************************/
/* Instances */ /* Instances */
@ -195,6 +197,8 @@ static uint8_t cmux_frame_data_dlci2_ppp_18[] = {0x7E, 0xFF, 0x7D, 0x23, 0xC0, 0
0x7D, 0x22, 0x7D, 0x21, 0x7D, 0x20, 0x7D, 0x22, 0x7D, 0x21, 0x7D, 0x20,
0x7D, 0x24, 0x7D, 0x3C, 0x90, 0x7E}; 0x7D, 0x24, 0x7D, 0x3C, 0x90, 0x7E};
static uint8_t cmux_frame_data_large[127] = { [0 ... 126] = 0xAA };
const static struct modem_backend_mock_transaction transaction_control_cld = { const static struct modem_backend_mock_transaction transaction_control_cld = {
.get = cmux_frame_control_cld_cmd, .get = cmux_frame_control_cld_cmd,
.get_size = sizeof(cmux_frame_control_cld_cmd), .get_size = sizeof(cmux_frame_control_cld_cmd),
@ -864,4 +868,22 @@ ZTEST(modem_cmux, test_modem_drop_frames_with_invalid_length)
"Incorrect data received"); "Incorrect data received");
} }
ZTEST(modem_cmux, test_modem_cmux_split_large_data)
{
int ret;
uint32_t events;
ret = modem_pipe_transmit(dlci2_pipe, cmux_frame_data_large,
sizeof(cmux_frame_data_large));
zassert_true(ret == CONFIG_MODEM_CMUX_MTU, "Failed to split large data %d", ret);
events = k_event_wait(&cmux_event, EVENT_CMUX_DLCI2_TRANSMIT_IDLE, false, K_MSEC(200));
zassert_equal(events, EVENT_CMUX_DLCI2_TRANSMIT_IDLE,
"Transmit idle event not received for DLCI2 pipe");
ret = modem_backend_mock_get(&bus_mock, buffer2, sizeof(buffer2));
zassert_true(ret == CONFIG_MODEM_CMUX_MTU + CMUX_BASIC_HRD_SMALL_SIZE,
"Incorrect number of bytes transmitted %d", ret);
}
ZTEST_SUITE(modem_cmux, NULL, test_modem_cmux_setup, test_modem_cmux_before, NULL, NULL); ZTEST_SUITE(modem_cmux, NULL, test_modem_cmux_setup, test_modem_cmux_before, NULL, NULL);

View file

@ -5,5 +5,6 @@ CONFIG_NO_OPTIMIZATIONS=y
CONFIG_MODEM_MODULES=y CONFIG_MODEM_MODULES=y
CONFIG_MODEM_CMUX=y CONFIG_MODEM_CMUX=y
CONFIG_MODEM_CMUX_MTU=64
CONFIG_ZTEST=y CONFIG_ZTEST=y