Skip to content

Commit 3bf8f1d

Browse files
SeppoTakalorlubos
authored andcommitted
[nrf fromtree] 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]> (cherry picked from commit 1f996d0)
1 parent 996e312 commit 3bf8f1d

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
@@ -81,6 +81,29 @@ struct modem_cmux_command {
8181
uint8_t value[];
8282
};
8383

84+
struct modem_cmux_msc_signals {
85+
uint8_t ea: 1; /**< Last octet, always 1 */
86+
uint8_t fc: 1; /**< Flow Control */
87+
uint8_t rtc: 1; /**< Ready to Communicate */
88+
uint8_t rtr: 1; /**< Ready to Transmit */
89+
uint8_t res_0: 2; /**< Reserved, set to zero */
90+
uint8_t ic: 1; /**< Incoming call indicator */
91+
uint8_t dv: 1; /**< Data Valid */
92+
};
93+
struct modem_cmux_msc_addr {
94+
uint8_t ea: 1; /**< Last octet, always 1 */
95+
uint8_t pad_one: 1; /**< Set to 1 */
96+
uint8_t dlci_address: 6; /**< DLCI channel address */
97+
};
98+
99+
struct modem_cmux_command_msc {
100+
struct modem_cmux_command command;
101+
uint8_t value[2];
102+
};
103+
104+
static struct modem_cmux_dlci *modem_cmux_find_dlci(struct modem_cmux *cmux, uint8_t dlci_address);
105+
static void modem_cmux_dlci_notify_transmit_idle(struct modem_cmux *cmux);
106+
84107
static int modem_cmux_wrap_command(struct modem_cmux_command **command, const uint8_t *data,
85108
uint16_t data_len)
86109
{
@@ -122,6 +145,53 @@ static struct modem_cmux_command *modem_cmux_command_wrap(const uint8_t *data)
122145
return (struct modem_cmux_command *)data;
123146
}
124147

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

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

@@ -439,6 +591,7 @@ static void modem_cmux_on_fcon_command(struct modem_cmux *cmux)
439591
cmux->flow_control_on = true;
440592
k_mutex_unlock(&cmux->transmit_rb_lock);
441593
modem_cmux_acknowledge_received_frame(cmux);
594+
modem_cmux_dlci_notify_transmit_idle(cmux);
442595
}
443596

444597
static void modem_cmux_on_fcoff_command(struct modem_cmux *cmux)
@@ -670,15 +823,15 @@ static void modem_cmux_on_control_frame(struct modem_cmux *cmux)
670823
}
671824
}
672825

673-
static struct modem_cmux_dlci *modem_cmux_find_dlci(struct modem_cmux *cmux)
826+
static struct modem_cmux_dlci *modem_cmux_find_dlci(struct modem_cmux *cmux, uint8_t dlci_address)
674827
{
675828
sys_snode_t *node;
676829
struct modem_cmux_dlci *dlci;
677830

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

681-
if (dlci->dlci_address == cmux->frame.dlci_address) {
834+
if (dlci->dlci_address == dlci_address) {
682835
return dlci;
683836
}
684837
}
@@ -710,6 +863,12 @@ static void modem_cmux_on_dlci_frame_ua(struct modem_cmux_dlci *dlci)
710863
k_mutex_lock(&dlci->receive_rb_lock, K_FOREVER);
711864
ring_buf_reset(&dlci->receive_rb);
712865
k_mutex_unlock(&dlci->receive_rb_lock);
866+
if (dlci->cmux->initiator) {
867+
modem_cmux_send_msc(dlci->cmux, dlci);
868+
dlci->msc_sent = true;
869+
} else {
870+
dlci->msc_sent = false;
871+
}
713872
break;
714873

715874
case MODEM_CMUX_DLCI_STATE_CLOSING:
@@ -742,6 +901,12 @@ static void modem_cmux_on_dlci_frame_uih(struct modem_cmux_dlci *dlci)
742901
LOG_WRN("DLCI %u receive buffer overrun (dropped %u out of %u bytes)",
743902
dlci->dlci_address, cmux->frame.data_len - written, cmux->frame.data_len);
744903
}
904+
if (written < cmux->frame.data_len ||
905+
ring_buf_space_get(&dlci->receive_rb) < MODEM_CMUX_DATA_FRAME_SIZE_MAX) {
906+
LOG_WRN("DLCI %u receive buffer is full", dlci->dlci_address);
907+
dlci->rx_full = true;
908+
modem_cmux_send_msc(cmux, dlci);
909+
}
745910
modem_pipe_notify_receive_ready(&dlci->pipe);
746911
}
747912

@@ -758,6 +923,7 @@ static void modem_cmux_on_dlci_frame_sabm(struct modem_cmux_dlci *dlci)
758923

759924
LOG_DBG("DLCI %u SABM request accepted, DLCI opened", dlci->dlci_address);
760925
dlci->state = MODEM_CMUX_DLCI_STATE_OPEN;
926+
dlci->msc_sent = false;
761927
modem_pipe_notify_opened(&dlci->pipe);
762928
k_mutex_lock(&dlci->receive_rb_lock, K_FOREVER);
763929
ring_buf_reset(&dlci->receive_rb);
@@ -791,7 +957,7 @@ static void modem_cmux_on_dlci_frame(struct modem_cmux *cmux)
791957
return;
792958
}
793959

794-
dlci = modem_cmux_find_dlci(cmux);
960+
dlci = modem_cmux_find_dlci(cmux, cmux->frame.dlci_address);
795961
if (dlci == NULL) {
796962
LOG_WRN("Frame intended for unconfigured DLCI %u.",
797963
cmux->frame.dlci_address);
@@ -1066,7 +1232,9 @@ static void modem_cmux_dlci_notify_transmit_idle(struct modem_cmux *cmux)
10661232

10671233
SYS_SLIST_FOR_EACH_NODE(&cmux->dlcis, node) {
10681234
dlci = (struct modem_cmux_dlci *)node;
1069-
modem_pipe_notify_transmit_idle(&dlci->pipe);
1235+
if (!dlci->flow_control) {
1236+
modem_pipe_notify_transmit_idle(&dlci->pipe);
1237+
}
10701238
}
10711239
}
10721240

@@ -1241,6 +1409,10 @@ static int modem_cmux_dlci_pipe_api_transmit(void *data, const uint8_t *buf, siz
12411409
struct modem_cmux *cmux = dlci->cmux;
12421410
int ret = 0;
12431411

1412+
if (dlci->flow_control) {
1413+
return 0;
1414+
}
1415+
12441416
K_SPINLOCK(&cmux->work_lock) {
12451417
if (!cmux->attached) {
12461418
ret = -EPERM;
@@ -1275,6 +1447,15 @@ static int modem_cmux_dlci_pipe_api_receive(void *data, uint8_t *buf, size_t siz
12751447

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

@@ -1321,6 +1502,7 @@ static void modem_cmux_dlci_open_handler(struct k_work *item)
13211502
dlci = CONTAINER_OF(dwork, struct modem_cmux_dlci, open_work);
13221503

13231504
dlci->state = MODEM_CMUX_DLCI_STATE_OPENING;
1505+
dlci->msc_sent = false;
13241506

13251507
struct modem_cmux_frame frame = {
13261508
.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)