From 093efc444d65a6477688b5884915be5151fc62f1 Mon Sep 17 00:00:00 2001 From: Juha Heiskanen Date: Wed, 29 Nov 2023 17:51:11 +0200 Subject: [PATCH] modem: modem_cmux: Support modem CMUX DCE role Added missing command and message handling for use existing modem cmux for DCE role. DCE CMUX connection can be now initialized from DTE side. Signed-off-by: Juha Heiskanen --- subsys/modem/modem_cmux.c | 108 ++++++++++++++++++++++++++++++++++---- 1 file changed, 99 insertions(+), 9 deletions(-) diff --git a/subsys/modem/modem_cmux.c b/subsys/modem/modem_cmux.c index 9c95e124fc2..29ff41c1ca7 100644 --- a/subsys/modem/modem_cmux.c +++ b/subsys/modem/modem_cmux.c @@ -340,9 +340,11 @@ static void modem_cmux_acknowledge_received_frame(struct modem_cmux *cmux) } } -static void modem_cmux_on_msc_command(struct modem_cmux *cmux) +static void modem_cmux_on_msc_command(struct modem_cmux *cmux, struct modem_cmux_command *command) { - modem_cmux_acknowledge_received_frame(cmux); + if (command->type.cr) { + modem_cmux_acknowledge_received_frame(cmux); + } } static void modem_cmux_on_fcon_command(struct modem_cmux *cmux) @@ -361,17 +363,27 @@ static void modem_cmux_on_fcoff_command(struct modem_cmux *cmux) modem_cmux_acknowledge_received_frame(cmux); } -static void modem_cmux_on_cld_command(struct modem_cmux *cmux) +static void modem_cmux_on_cld_command(struct modem_cmux *cmux, struct modem_cmux_command *command) { - if (cmux->state != MODEM_CMUX_STATE_DISCONNECTING) { + if (command->type.cr) { + modem_cmux_acknowledge_received_frame(cmux); + } + + if (cmux->state != MODEM_CMUX_STATE_DISCONNECTING && + cmux->state != MODEM_CMUX_STATE_CONNECTED) { LOG_WRN("Unexpected close down"); + return; + } + + if (cmux->state == MODEM_CMUX_STATE_DISCONNECTING) { + k_work_cancel_delayable(&cmux->disconnect_work); } cmux->state = MODEM_CMUX_STATE_DISCONNECTED; k_mutex_lock(&cmux->transmit_rb_lock, K_FOREVER); cmux->flow_control_on = false; k_mutex_unlock(&cmux->transmit_rb_lock); - k_work_cancel_delayable(&cmux->disconnect_work); + modem_cmux_raise_event(cmux, MODEM_CMUX_EVENT_DISCONNECTED); k_event_clear(&cmux->event, MODEM_CMUX_EVENT_CONNECTED_BIT); k_event_post(&cmux->event, MODEM_CMUX_EVENT_DISCONNECTED_BIT); @@ -381,7 +393,6 @@ static void modem_cmux_on_control_frame_ua(struct modem_cmux *cmux) { if (cmux->state != MODEM_CMUX_STATE_CONNECTING) { LOG_DBG("Unexpected UA frame"); - return; } @@ -414,11 +425,11 @@ static void modem_cmux_on_control_frame_uih(struct modem_cmux *cmux) switch (command->type.value) { case MODEM_CMUX_COMMAND_CLD: - modem_cmux_on_cld_command(cmux); + modem_cmux_on_cld_command(cmux, command); break; case MODEM_CMUX_COMMAND_MSC: - modem_cmux_on_msc_command(cmux); + modem_cmux_on_msc_command(cmux, command); break; case MODEM_CMUX_COMMAND_FCON: @@ -435,6 +446,40 @@ static void modem_cmux_on_control_frame_uih(struct modem_cmux *cmux) } } +static void modem_cmux_connect_response_transmit(struct modem_cmux *cmux) +{ + struct modem_cmux_frame frame = { + .dlci_address = cmux->frame.dlci_address, + .cr = cmux->frame.cr, + .pf = cmux->frame.pf, + .type = MODEM_CMUX_FRAME_TYPE_UA, + .data = NULL, + .data_len = 0, + }; + + LOG_DBG("SABM/DISC request state send ack"); + modem_cmux_transmit_cmd_frame(cmux, &frame); +} + +static void modem_cmux_on_control_frame_sabm(struct modem_cmux *cmux) +{ + modem_cmux_connect_response_transmit(cmux); + + if ((cmux->state == MODEM_CMUX_STATE_CONNECTED) || + (cmux->state == MODEM_CMUX_STATE_DISCONNECTING)) { + LOG_DBG("Connect request not accepted"); + return; + } + + cmux->state = MODEM_CMUX_STATE_CONNECTED; + k_mutex_lock(&cmux->transmit_rb_lock, K_FOREVER); + cmux->flow_control_on = true; + k_mutex_unlock(&cmux->transmit_rb_lock); + modem_cmux_raise_event(cmux, MODEM_CMUX_EVENT_CONNECTED); + k_event_clear(&cmux->event, MODEM_CMUX_EVENT_DISCONNECTED_BIT); + k_event_post(&cmux->event, MODEM_CMUX_EVENT_CONNECTED_BIT); +} + static void modem_cmux_on_control_frame(struct modem_cmux *cmux) { modem_cmux_log_received_frame(&cmux->frame); @@ -448,6 +493,10 @@ static void modem_cmux_on_control_frame(struct modem_cmux *cmux) modem_cmux_on_control_frame_uih(cmux); break; + case MODEM_CMUX_FRAME_TYPE_SABM: + modem_cmux_on_control_frame_sabm(cmux); + break; + default: LOG_WRN("Unknown %s frame type", "control"); break; @@ -509,6 +558,39 @@ static void modem_cmux_on_dlci_frame_uih(struct modem_cmux_dlci *dlci) modem_pipe_notify_receive_ready(&dlci->pipe); } +static void modem_cmux_on_dlci_frame_sabm(struct modem_cmux_dlci *dlci) +{ + struct modem_cmux *cmux = dlci->cmux; + + modem_cmux_connect_response_transmit(cmux); + + if (dlci->state == MODEM_CMUX_DLCI_STATE_OPEN) { + LOG_DBG("Unexpected SABM frame"); + return; + } + + dlci->state = MODEM_CMUX_DLCI_STATE_OPEN; + modem_pipe_notify_opened(&dlci->pipe); + k_mutex_lock(&dlci->receive_rb_lock, K_FOREVER); + ring_buf_reset(&dlci->receive_rb); + k_mutex_unlock(&dlci->receive_rb_lock); +} + +static void modem_cmux_on_dlci_frame_disc(struct modem_cmux_dlci *dlci) +{ + struct modem_cmux *cmux = dlci->cmux; + + modem_cmux_connect_response_transmit(cmux); + + if (dlci->state != MODEM_CMUX_DLCI_STATE_OPEN) { + LOG_DBG("Unexpected Disc frame"); + return; + } + + dlci->state = MODEM_CMUX_DLCI_STATE_CLOSED; + modem_pipe_notify_closed(&dlci->pipe); +} + static void modem_cmux_on_dlci_frame(struct modem_cmux *cmux) { struct modem_cmux_dlci *dlci; @@ -532,6 +614,14 @@ static void modem_cmux_on_dlci_frame(struct modem_cmux *cmux) modem_cmux_on_dlci_frame_uih(dlci); break; + case MODEM_CMUX_FRAME_TYPE_SABM: + modem_cmux_on_dlci_frame_sabm(dlci); + break; + + case MODEM_CMUX_FRAME_TYPE_DISC: + modem_cmux_on_dlci_frame_disc(dlci); + break; + default: LOG_WRN("Unknown %s frame type", "DLCI"); break; @@ -996,6 +1086,7 @@ void modem_cmux_init(struct modem_cmux *cmux, const struct modem_cmux_config *co k_work_init_delayable(&cmux->connect_work, modem_cmux_connect_handler); k_work_init_delayable(&cmux->disconnect_work, modem_cmux_disconnect_handler); k_event_init(&cmux->event); + k_event_clear(&cmux->event, MODEM_CMUX_EVENT_CONNECTED_BIT); k_event_post(&cmux->event, MODEM_CMUX_EVENT_DISCONNECTED_BIT); } @@ -1071,7 +1162,6 @@ int modem_cmux_disconnect(struct modem_cmux *cmux) if (ret < 0) { return ret; } - if (k_event_wait(&cmux->event, MODEM_CMUX_EVENT_DISCONNECTED_BIT, false, MODEM_CMUX_T2_TIMEOUT) == 0) { return -EAGAIN;