@@ -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+
86109static 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
128198static 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+
431549static 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
446599static 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 ,
0 commit comments