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 <juha.heiskanen@nordicsemi.no>
This commit is contained in:
Juha Heiskanen 2023-11-29 17:51:11 +02:00 committed by Fabio Baltieri
parent 72bb10dc84
commit 093efc444d

View File

@ -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;