Skip to content

Commit 1f996d0

Browse files
SeppoTakalocfriedt
authored andcommitted
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 36a3a4c commit 1f996d0

File tree

5 files changed

+260
-22
lines changed

5 files changed

+260
-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: 188 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,29 @@ 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_addr {
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+
};
100+
101+
struct modem_cmux_command_msc {
102+
struct modem_cmux_command command;
103+
uint8_t value[2];
104+
};
105+
106+
static struct modem_cmux_dlci *modem_cmux_find_dlci(struct modem_cmux *cmux, uint8_t dlci_address);
107+
static void modem_cmux_dlci_notify_transmit_idle(struct modem_cmux *cmux);
108+
86109
static int modem_cmux_wrap_command(struct modem_cmux_command **command, const uint8_t *data,
87110
uint16_t data_len)
88111
{
@@ -124,6 +147,53 @@ static struct modem_cmux_command *modem_cmux_command_wrap(const uint8_t *data)
124147
return (struct modem_cmux_command *)data;
125148
}
126149

150+
static struct modem_cmux_msc_signals modem_cmux_msc_signals_decode(const uint8_t byte)
151+
{
152+
struct modem_cmux_msc_signals signals;
153+
154+
/* 3GPP TS 27.010 MSC signals octet:
155+
* |0 |1 |2 |3 |4 |5 |6 |7 |
156+
* |EA |FC|RTC|RTR|0 |0 |IC|DV|
157+
*/
158+
signals.ea = (byte & BIT(0)) ? 1 : 0;
159+
signals.fc = (byte & BIT(1)) ? 1 : 0;
160+
signals.rtc = (byte & BIT(2)) ? 1 : 0;
161+
signals.rtr = (byte & BIT(3)) ? 1 : 0;
162+
signals.ic = (byte & BIT(6)) ? 1 : 0;
163+
signals.dv = (byte & BIT(7)) ? 1 : 0;
164+
165+
return signals;
166+
}
167+
168+
static uint8_t modem_cmux_msc_signals_encode(const struct modem_cmux_msc_signals signals)
169+
{
170+
return (signals.ea ? BIT(0) : 0) | (signals.fc ? BIT(1) : 0) |
171+
(signals.rtc ? BIT(2) : 0) | (signals.rtr ? BIT(3) : 0) |
172+
(signals.ic ? BIT(6) : 0) | (signals.dv ? BIT(7) : 0);
173+
}
174+
175+
static struct modem_cmux_msc_addr modem_cmux_msc_addr_decode(const uint8_t byte)
176+
{
177+
struct modem_cmux_msc_addr addr;
178+
179+
/* 3GPP TS 27.010 MSC address octet:
180+
* |0 |1 |2 |3 |4 |5 |6 |7 |
181+
* |EA |1 | DLCI |
182+
*/
183+
addr.ea = (byte & BIT(0)) ? 1 : 0;
184+
addr.pad_one = 1;
185+
addr.dlci_address = (byte >> 2) & 0x3F;
186+
187+
return addr;
188+
}
189+
190+
static uint8_t modem_cmux_msc_addr_encode(const struct modem_cmux_msc_addr a)
191+
{
192+
return (a.ea ? BIT(0) : 0) | BIT(1) |
193+
((a.dlci_address & 0x3F) << 2);
194+
}
195+
196+
127197
#if CONFIG_MODEM_CMUX_LOG_FRAMES
128198
static const char *modem_cmux_frame_type_to_str(enum modem_cmux_frame_types frame_type)
129199
{
@@ -428,10 +498,92 @@ static void modem_cmux_acknowledge_received_frame(struct modem_cmux *cmux)
428498
}
429499
}
430500

501+
static void modem_cmux_send_msc(struct modem_cmux *cmux, struct modem_cmux_dlci *dlci)
502+
{
503+
if (cmux == NULL || dlci == NULL) {
504+
return;
505+
}
506+
507+
struct modem_cmux_msc_addr addr = {
508+
.ea = 1,
509+
.pad_one = 1,
510+
.dlci_address = dlci->dlci_address,
511+
};
512+
struct modem_cmux_msc_signals signals = {
513+
.ea = 1,
514+
.fc = dlci->rx_full ? 1 : 0,
515+
.rtc = dlci->state == MODEM_CMUX_DLCI_STATE_OPEN ? 1 : 0,
516+
.rtr = dlci->state == MODEM_CMUX_DLCI_STATE_OPEN ? 1 : 0,
517+
.dv = 1,
518+
};
519+
struct modem_cmux_command_msc cmd = {
520+
.command = {
521+
.type = {
522+
.ea = 1,
523+
.cr = 1,
524+
.value = MODEM_CMUX_COMMAND_MSC,
525+
},
526+
.length = {
527+
.ea = 1,
528+
.value = sizeof(cmd.value),
529+
},
530+
},
531+
.value[0] = modem_cmux_msc_addr_encode(addr),
532+
.value[1] = modem_cmux_msc_signals_encode(signals),
533+
};
534+
535+
struct modem_cmux_frame frame = {
536+
.dlci_address = 0,
537+
.cr = cmux->initiator,
538+
.pf = false,
539+
.type = MODEM_CMUX_FRAME_TYPE_UIH,
540+
.data = (void *)&cmd,
541+
.data_len = sizeof(cmd),
542+
};
543+
544+
LOG_DBG("Sending MSC command for DLCI %u, FC:%d RTR: %d DV: %d", addr.dlci_address,
545+
signals.fc, signals.rtr, signals.dv);
546+
modem_cmux_transmit_cmd_frame(cmux, &frame);
547+
}
548+
431549
static void modem_cmux_on_msc_command(struct modem_cmux *cmux, struct modem_cmux_command *command)
432550
{
433-
if (command->type.cr) {
434-
modem_cmux_acknowledge_received_frame(cmux);
551+
if (!command->type.cr) {
552+
return;
553+
}
554+
555+
modem_cmux_acknowledge_received_frame(cmux);
556+
557+
uint8_t len = command->length.value;
558+
559+
if (len != 2 && len != 3) {
560+
LOG_WRN("Unexpected MSC command length %d", (int)len);
561+
return;
562+
}
563+
564+
struct modem_cmux_msc_addr msc = modem_cmux_msc_addr_decode(command->value[0]);
565+
struct modem_cmux_msc_signals signals = modem_cmux_msc_signals_decode(command->value[1]);
566+
struct modem_cmux_dlci *dlci = modem_cmux_find_dlci(cmux, msc.dlci_address);
567+
568+
if (dlci) {
569+
LOG_DBG("MSC command received for DLCI %u", msc.dlci_address);
570+
bool fc_signal = signals.fc || !signals.rtr;
571+
572+
if (fc_signal != dlci->flow_control) {
573+
if (fc_signal) {
574+
dlci->flow_control = true;
575+
LOG_DBG("DLCI %u flow control ON", dlci->dlci_address);
576+
} else {
577+
dlci->flow_control = false;
578+
LOG_DBG("DLCI %u flow control OFF", dlci->dlci_address);
579+
modem_pipe_notify_transmit_idle(&dlci->pipe);
580+
}
581+
}
582+
/* As we have received MSC, send also our MSC */
583+
if (!dlci->msc_sent && dlci->state == MODEM_CMUX_DLCI_STATE_OPEN) {
584+
dlci->msc_sent = true;
585+
modem_cmux_send_msc(cmux, dlci);
586+
}
435587
}
436588
}
437589

@@ -441,6 +593,7 @@ static void modem_cmux_on_fcon_command(struct modem_cmux *cmux)
441593
cmux->flow_control_on = true;
442594
k_mutex_unlock(&cmux->transmit_rb_lock);
443595
modem_cmux_acknowledge_received_frame(cmux);
596+
modem_cmux_dlci_notify_transmit_idle(cmux);
444597
}
445598

446599
static void modem_cmux_on_fcoff_command(struct modem_cmux *cmux)
@@ -672,15 +825,15 @@ static void modem_cmux_on_control_frame(struct modem_cmux *cmux)
672825
}
673826
}
674827

675-
static struct modem_cmux_dlci *modem_cmux_find_dlci(struct modem_cmux *cmux)
828+
static struct modem_cmux_dlci *modem_cmux_find_dlci(struct modem_cmux *cmux, uint8_t dlci_address)
676829
{
677830
sys_snode_t *node;
678831
struct modem_cmux_dlci *dlci;
679832

680833
SYS_SLIST_FOR_EACH_NODE(&cmux->dlcis, node) {
681834
dlci = (struct modem_cmux_dlci *)node;
682835

683-
if (dlci->dlci_address == cmux->frame.dlci_address) {
836+
if (dlci->dlci_address == dlci_address) {
684837
return dlci;
685838
}
686839
}
@@ -712,6 +865,12 @@ static void modem_cmux_on_dlci_frame_ua(struct modem_cmux_dlci *dlci)
712865
k_mutex_lock(&dlci->receive_rb_lock, K_FOREVER);
713866
ring_buf_reset(&dlci->receive_rb);
714867
k_mutex_unlock(&dlci->receive_rb_lock);
868+
if (dlci->cmux->initiator) {
869+
modem_cmux_send_msc(dlci->cmux, dlci);
870+
dlci->msc_sent = true;
871+
} else {
872+
dlci->msc_sent = false;
873+
}
715874
break;
716875

717876
case MODEM_CMUX_DLCI_STATE_CLOSING:
@@ -744,6 +903,12 @@ static void modem_cmux_on_dlci_frame_uih(struct modem_cmux_dlci *dlci)
744903
LOG_WRN("DLCI %u receive buffer overrun (dropped %u out of %u bytes)",
745904
dlci->dlci_address, cmux->frame.data_len - written, cmux->frame.data_len);
746905
}
906+
if (written < cmux->frame.data_len ||
907+
ring_buf_space_get(&dlci->receive_rb) < MODEM_CMUX_DATA_FRAME_SIZE_MAX) {
908+
LOG_WRN("DLCI %u receive buffer is full", dlci->dlci_address);
909+
dlci->rx_full = true;
910+
modem_cmux_send_msc(cmux, dlci);
911+
}
747912
modem_pipe_notify_receive_ready(&dlci->pipe);
748913
}
749914

@@ -760,6 +925,7 @@ static void modem_cmux_on_dlci_frame_sabm(struct modem_cmux_dlci *dlci)
760925

761926
LOG_DBG("DLCI %u SABM request accepted, DLCI opened", dlci->dlci_address);
762927
dlci->state = MODEM_CMUX_DLCI_STATE_OPEN;
928+
dlci->msc_sent = false;
763929
modem_pipe_notify_opened(&dlci->pipe);
764930
k_mutex_lock(&dlci->receive_rb_lock, K_FOREVER);
765931
ring_buf_reset(&dlci->receive_rb);
@@ -793,7 +959,7 @@ static void modem_cmux_on_dlci_frame(struct modem_cmux *cmux)
793959
return;
794960
}
795961

796-
dlci = modem_cmux_find_dlci(cmux);
962+
dlci = modem_cmux_find_dlci(cmux, cmux->frame.dlci_address);
797963
if (dlci == NULL) {
798964
LOG_WRN("Frame intended for unconfigured DLCI %u.",
799965
cmux->frame.dlci_address);
@@ -1068,7 +1234,9 @@ static void modem_cmux_dlci_notify_transmit_idle(struct modem_cmux *cmux)
10681234

10691235
SYS_SLIST_FOR_EACH_NODE(&cmux->dlcis, node) {
10701236
dlci = (struct modem_cmux_dlci *)node;
1071-
modem_pipe_notify_transmit_idle(&dlci->pipe);
1237+
if (!dlci->flow_control) {
1238+
modem_pipe_notify_transmit_idle(&dlci->pipe);
1239+
}
10721240
}
10731241
}
10741242

@@ -1243,6 +1411,10 @@ static int modem_cmux_dlci_pipe_api_transmit(void *data, const uint8_t *buf, siz
12431411
struct modem_cmux *cmux = dlci->cmux;
12441412
int ret = 0;
12451413

1414+
if (dlci->flow_control) {
1415+
return 0;
1416+
}
1417+
12461418
K_SPINLOCK(&cmux->work_lock) {
12471419
if (!cmux->attached) {
12481420
ret = -EPERM;
@@ -1277,6 +1449,15 @@ static int modem_cmux_dlci_pipe_api_receive(void *data, uint8_t *buf, size_t siz
12771449

12781450
ret = ring_buf_get(&dlci->receive_rb, buf, size);
12791451
k_mutex_unlock(&dlci->receive_rb_lock);
1452+
1453+
/* Release FC if set */
1454+
if (dlci->rx_full &&
1455+
ring_buf_space_get(&dlci->receive_rb) >= MODEM_CMUX_DATA_FRAME_SIZE_MAX) {
1456+
LOG_DBG("DLCI %u receive buffer is no longer full", dlci->dlci_address);
1457+
dlci->rx_full = false;
1458+
modem_cmux_send_msc(dlci->cmux, dlci);
1459+
}
1460+
12801461
return ret;
12811462
}
12821463

@@ -1323,6 +1504,7 @@ static void modem_cmux_dlci_open_handler(struct k_work *item)
13231504
dlci = CONTAINER_OF(dwork, struct modem_cmux_dlci, open_work);
13241505

13251506
dlci->state = MODEM_CMUX_DLCI_STATE_OPENING;
1507+
dlci->msc_sent = false;
13261508

13271509
struct modem_cmux_frame frame = {
13281510
.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)