@@ -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+
86110static 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+
415485static 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
430534static 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 ,
0 commit comments