Skip to content

Commit 38f0e6a

Browse files
committed
modem: cmux: Implement Modem Status Command
Implement Modem Status Command(MSC) and a per DLC flow control by using it. Send flow control signals when our input buffer don't fit full frame anymore. Stop TX if we have received from controls on MSC. Signed-off-by: Seppo Takalo <[email protected]>
1 parent 93674a6 commit 38f0e6a

File tree

5 files changed

+211
-22
lines changed

5 files changed

+211
-22
lines changed

include/zephyr/modem/cmux.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ struct modem_cmux_dlci {
120120
#if CONFIG_MODEM_STATS
121121
struct modem_stats_buffer receive_buf_stats;
122122
#endif
123+
/* Flow control */
124+
bool flow_control : 1;
125+
bool rx_full : 1;
126+
bool msc_sent : 1;
123127
};
124128

125129
struct modem_cmux_frame {

subsys/modem/modem_cmux.c

Lines changed: 139 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,30 @@ struct modem_cmux_command {
8383
uint8_t value[];
8484
};
8585

86+
struct modem_cmux_msc_signals {
87+
uint8_t ea: 1; /**< Last octet, always 1 */
88+
uint8_t fc: 1; /**< Flow Control */
89+
uint8_t rtc: 1; /**< Ready to Communicate */
90+
uint8_t rtr: 1; /**< Ready to Transmit */
91+
uint8_t res_0: 2; /**< Reserved, set to zero */
92+
uint8_t ic: 1; /**< Incoming call indicator */
93+
uint8_t dv: 1; /**< Data Valid */
94+
};
95+
struct modem_cmux_msc {
96+
uint8_t ea: 1; /**< Last octet, always 1 */
97+
uint8_t pad_one: 1; /**< Set to 1 */
98+
uint8_t dlci_address: 6; /**< DLCI channel address */
99+
struct modem_cmux_msc_signals signals; /**< Modem status signals */
100+
};
101+
102+
struct modem_cmux_command_msc {
103+
struct modem_cmux_command command;
104+
struct modem_cmux_msc msc;
105+
};
106+
107+
static struct modem_cmux_dlci *modem_cmux_find_dlci(struct modem_cmux *cmux, uint8_t dlci_address);
108+
static void modem_cmux_dlci_notify_transmit_idle(struct modem_cmux *cmux);
109+
86110
static int modem_cmux_wrap_command(struct modem_cmux_command **command, const uint8_t *data,
87111
uint16_t data_len)
88112
{
@@ -412,10 +436,89 @@ static void modem_cmux_acknowledge_received_frame(struct modem_cmux *cmux)
412436
}
413437
}
414438

439+
static void modem_cmux_send_msc(struct modem_cmux *cmux, struct modem_cmux_dlci *dlci)
440+
{
441+
if (cmux == NULL || dlci == NULL) {
442+
return;
443+
}
444+
445+
struct modem_cmux_command_msc cmd = {
446+
.command = {
447+
.type = {
448+
.ea = 1,
449+
.cr = 1,
450+
.value = MODEM_CMUX_COMMAND_MSC,
451+
},
452+
.length = {
453+
.ea = 1,
454+
.value = sizeof(struct modem_cmux_msc),
455+
},
456+
},
457+
.msc = {
458+
.ea = 1,
459+
.pad_one = 1,
460+
.dlci_address = dlci->dlci_address,
461+
.signals = {
462+
.ea = 1,
463+
.fc = dlci->rx_full ? 1 : 0,
464+
.rtc = dlci->state == MODEM_CMUX_DLCI_STATE_OPEN ? 1 : 0,
465+
.rtr = dlci->state == MODEM_CMUX_DLCI_STATE_OPEN ? 1 : 0,
466+
.dv = 1,
467+
},
468+
},
469+
};
470+
471+
struct modem_cmux_frame frame = {
472+
.dlci_address = 0,
473+
.cr = cmux->initiator,
474+
.pf = false,
475+
.type = MODEM_CMUX_FRAME_TYPE_UIH,
476+
.data = (void *)&cmd,
477+
.data_len = sizeof(cmd),
478+
};
479+
480+
LOG_DBG("Sending MSC command for DLCI %u, FC:%d RTR: %d DV: %d", cmd.msc.dlci_address,
481+
cmd.msc.signals.fc, cmd.msc.signals.rtr, cmd.msc.signals.dv);
482+
modem_cmux_transmit_cmd_frame(cmux, &frame);
483+
}
484+
415485
static void modem_cmux_on_msc_command(struct modem_cmux *cmux, struct modem_cmux_command *command)
416486
{
417-
if (command->type.cr) {
418-
modem_cmux_acknowledge_received_frame(cmux);
487+
if (!command->type.cr) {
488+
return;
489+
}
490+
491+
modem_cmux_acknowledge_received_frame(cmux);
492+
493+
uint8_t len = command->length.value;
494+
495+
if (len != 2 && len != 3) {
496+
LOG_WRN("Unexpected MSC command length %d", (int)len);
497+
return;
498+
}
499+
500+
struct modem_cmux_msc *msc = (struct modem_cmux_msc *)command->value;
501+
struct modem_cmux_dlci *dlci = modem_cmux_find_dlci(cmux, msc->dlci_address);
502+
503+
if (dlci) {
504+
LOG_DBG("MSC command received for DLCI %u", msc->dlci_address);
505+
bool fc_signal = msc->signals.fc || !msc->signals.rtr;
506+
507+
if (fc_signal != dlci->flow_control) {
508+
if (fc_signal) {
509+
dlci->flow_control = true;
510+
LOG_DBG("DLCI %u flow control ON", dlci->dlci_address);
511+
} else {
512+
dlci->flow_control = false;
513+
LOG_DBG("DLCI %u flow control OFF", dlci->dlci_address);
514+
modem_pipe_notify_transmit_idle(&dlci->pipe);
515+
}
516+
}
517+
/* As we have received MSC, send also our MSC */
518+
if (!dlci->msc_sent && dlci->state == MODEM_CMUX_DLCI_STATE_OPEN) {
519+
dlci->msc_sent = true;
520+
modem_cmux_send_msc(cmux, dlci);
521+
}
419522
}
420523
}
421524

@@ -425,6 +528,7 @@ static void modem_cmux_on_fcon_command(struct modem_cmux *cmux)
425528
cmux->flow_control_on = true;
426529
k_mutex_unlock(&cmux->transmit_rb_lock);
427530
modem_cmux_acknowledge_received_frame(cmux);
531+
modem_cmux_dlci_notify_transmit_idle(cmux);
428532
}
429533

430534
static void modem_cmux_on_fcoff_command(struct modem_cmux *cmux)
@@ -644,15 +748,15 @@ static void modem_cmux_on_control_frame(struct modem_cmux *cmux)
644748
}
645749
}
646750

647-
static struct modem_cmux_dlci *modem_cmux_find_dlci(struct modem_cmux *cmux)
751+
static struct modem_cmux_dlci *modem_cmux_find_dlci(struct modem_cmux *cmux, uint8_t dlci_address)
648752
{
649753
sys_snode_t *node;
650754
struct modem_cmux_dlci *dlci;
651755

652756
SYS_SLIST_FOR_EACH_NODE(&cmux->dlcis, node) {
653757
dlci = (struct modem_cmux_dlci *)node;
654758

655-
if (dlci->dlci_address == cmux->frame.dlci_address) {
759+
if (dlci->dlci_address == dlci_address) {
656760
return dlci;
657761
}
658762
}
@@ -684,6 +788,12 @@ static void modem_cmux_on_dlci_frame_ua(struct modem_cmux_dlci *dlci)
684788
k_mutex_lock(&dlci->receive_rb_lock, K_FOREVER);
685789
ring_buf_reset(&dlci->receive_rb);
686790
k_mutex_unlock(&dlci->receive_rb_lock);
791+
if (dlci->cmux->initiator) {
792+
modem_cmux_send_msc(dlci->cmux, dlci);
793+
dlci->msc_sent = true;
794+
} else {
795+
dlci->msc_sent = false;
796+
}
687797
break;
688798

689799
case MODEM_CMUX_DLCI_STATE_CLOSING:
@@ -716,6 +826,12 @@ static void modem_cmux_on_dlci_frame_uih(struct modem_cmux_dlci *dlci)
716826
LOG_WRN("DLCI %u receive buffer overrun (dropped %u out of %u bytes)",
717827
dlci->dlci_address, cmux->frame.data_len - written, cmux->frame.data_len);
718828
}
829+
if (written < cmux->frame.data_len ||
830+
ring_buf_space_get(&dlci->receive_rb) < MODEM_CMUX_DATA_FRAME_SIZE_MAX) {
831+
LOG_WRN("DLCI %u receive buffer is full", dlci->dlci_address);
832+
dlci->rx_full = true;
833+
modem_cmux_send_msc(cmux, dlci);
834+
}
719835
modem_pipe_notify_receive_ready(&dlci->pipe);
720836
}
721837

@@ -732,6 +848,7 @@ static void modem_cmux_on_dlci_frame_sabm(struct modem_cmux_dlci *dlci)
732848

733849
LOG_DBG("DLCI %u SABM request accepted, DLCI opened", dlci->dlci_address);
734850
dlci->state = MODEM_CMUX_DLCI_STATE_OPEN;
851+
dlci->msc_sent = false;
735852
modem_pipe_notify_opened(&dlci->pipe);
736853
k_mutex_lock(&dlci->receive_rb_lock, K_FOREVER);
737854
ring_buf_reset(&dlci->receive_rb);
@@ -765,7 +882,7 @@ static void modem_cmux_on_dlci_frame(struct modem_cmux *cmux)
765882
return;
766883
}
767884

768-
dlci = modem_cmux_find_dlci(cmux);
885+
dlci = modem_cmux_find_dlci(cmux, cmux->frame.dlci_address);
769886
if (dlci == NULL) {
770887
LOG_WRN("Frame intended for unconfigured DLCI %u.",
771888
cmux->frame.dlci_address);
@@ -1040,7 +1157,9 @@ static void modem_cmux_dlci_notify_transmit_idle(struct modem_cmux *cmux)
10401157

10411158
SYS_SLIST_FOR_EACH_NODE(&cmux->dlcis, node) {
10421159
dlci = (struct modem_cmux_dlci *)node;
1043-
modem_pipe_notify_transmit_idle(&dlci->pipe);
1160+
if (!dlci->flow_control) {
1161+
modem_pipe_notify_transmit_idle(&dlci->pipe);
1162+
}
10441163
}
10451164
}
10461165

@@ -1210,6 +1329,10 @@ static int modem_cmux_dlci_pipe_api_transmit(void *data, const uint8_t *buf, siz
12101329
struct modem_cmux *cmux = dlci->cmux;
12111330
int ret = 0;
12121331

1332+
if (dlci->flow_control) {
1333+
return 0;
1334+
}
1335+
12131336
K_SPINLOCK(&cmux->work_lock) {
12141337
if (!cmux->attached) {
12151338
ret = -EPERM;
@@ -1244,6 +1367,15 @@ static int modem_cmux_dlci_pipe_api_receive(void *data, uint8_t *buf, size_t siz
12441367

12451368
ret = ring_buf_get(&dlci->receive_rb, buf, size);
12461369
k_mutex_unlock(&dlci->receive_rb_lock);
1370+
1371+
/* Release FC if set */
1372+
if (dlci->rx_full &&
1373+
ring_buf_space_get(&dlci->receive_rb) >= MODEM_CMUX_DATA_FRAME_SIZE_MAX) {
1374+
LOG_DBG("DLCI %u receive buffer is no longer full", dlci->dlci_address);
1375+
dlci->rx_full = false;
1376+
modem_cmux_send_msc(dlci->cmux, dlci);
1377+
}
1378+
12471379
return ret;
12481380
}
12491381

@@ -1290,6 +1422,7 @@ static void modem_cmux_dlci_open_handler(struct k_work *item)
12901422
dlci = CONTAINER_OF(dwork, struct modem_cmux_dlci, open_work);
12911423

12921424
dlci->state = MODEM_CMUX_DLCI_STATE_OPENING;
1425+
dlci->msc_sent = false;
12931426

12941427
struct modem_cmux_frame frame = {
12951428
.dlci_address = dlci->dlci_address,

tests/subsys/modem/mock/modem_backend_mock.c

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,15 @@ static int modem_backend_mock_transmit(void *data, const uint8_t *buf, size_t si
5252
return ret;
5353
}
5454

55-
ret = ring_buf_put(&mock->tx_rb, buf, size);
5655
if (modem_backend_mock_update(mock, buf, size)) {
56+
/* Skip ringbuffer if transaction consumes bytes */
57+
ret = size;
5758
modem_backend_mock_put(mock, mock->transaction->put,
5859
mock->transaction->put_size);
5960

60-
mock->transaction = NULL;
61+
modem_backend_mock_prime(mock, mock->transaction->next);
62+
} else {
63+
ret = ring_buf_put(&mock->tx_rb, buf, size);
6164
}
6265

6366
k_work_submit(&mock->transmit_idle_work);
@@ -137,6 +140,10 @@ int modem_backend_mock_get(struct modem_backend_mock *mock, uint8_t *buf, size_t
137140

138141
void modem_backend_mock_put(struct modem_backend_mock *mock, const uint8_t *buf, size_t size)
139142
{
143+
if (size == 0) {
144+
return;
145+
}
146+
140147
__ASSERT(ring_buf_put(&mock->rx_rb, buf, size) == size,
141148
"Mock buffer capacity exceeded");
142149

@@ -155,3 +162,10 @@ void modem_backend_mock_bridge(struct modem_backend_mock *mock_a, struct modem_b
155162
mock_a->bridge = mock_b;
156163
mock_b->bridge = mock_a;
157164
}
165+
166+
void modem_backend_mock_wait_for_transaction(struct modem_backend_mock *mock)
167+
{
168+
while (mock->transaction) {
169+
k_msleep(1);
170+
}
171+
}

tests/subsys/modem/mock/modem_backend_mock.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ struct modem_backend_mock_transaction {
1919
/* Data which will be put in response to get data */
2020
const uint8_t *put;
2121
size_t put_size;
22+
23+
/* Next transaction in chain */
24+
const struct modem_backend_mock_transaction *next;
2225
};
2326

2427
struct modem_backend_mock {
@@ -62,4 +65,6 @@ void modem_backend_mock_prime(struct modem_backend_mock *mock,
6265
void modem_backend_mock_bridge(struct modem_backend_mock *mock_a,
6366
struct modem_backend_mock *mock_b);
6467

68+
void modem_backend_mock_wait_for_transaction(struct modem_backend_mock *mock);
69+
6570
#endif /* ZEPHYR_DRIVERS_MODEM_MODEM_PIPE_MOCK */

0 commit comments

Comments
 (0)