@@ -60,10 +60,9 @@ static struct {
6060 struct k_work rx_work ;
6161
6262 /* Outgoing data for AT DLCI. */
63- struct k_sem tx_sem ;
64- struct ring_buf notif_rb ;
65- uint8_t notif_buffer [CONFIG_SLM_CMUX_NOTIFICATION_TX_BUFFER_SIZE ];
66- struct k_mutex notif_rb_mutex ;
63+ struct ring_buf tx_rb ;
64+ uint8_t tx_buffer [CONFIG_SLM_CMUX_TX_BUFFER_SIZE ];
65+ struct k_mutex tx_rb_mutex ;
6766 struct k_work tx_work ;
6867
6968} cmux ;
@@ -111,17 +110,10 @@ static void dlci_pipe_event_handler(struct modem_pipe *pipe,
111110 */
112111 case MODEM_PIPE_EVENT_OPENED :
113112 LOG_INF ("DLCI %u%s opened." , dlci -> address , is_at ? " (AT)" : "" );
114- if (is_at ) {
115- k_sem_give (& cmux .tx_sem );
116- break ;
117- }
118113 break ;
119114
120115 case MODEM_PIPE_EVENT_CLOSED :
121116 LOG_INF ("DLCI %u%s closed." , dlci -> address , is_at ? " (AT)" : "" );
122- if (is_at ) {
123- k_sem_reset (& cmux .tx_sem );
124- }
125117 break ;
126118
127119 case MODEM_PIPE_EVENT_RECEIVE_READY :
@@ -132,8 +124,9 @@ static void dlci_pipe_event_handler(struct modem_pipe *pipe,
132124
133125 case MODEM_PIPE_EVENT_TRANSMIT_IDLE :
134126 if (is_at &&
135- cmux .dlcis [cmux .at_channel ].instance .state == MODEM_CMUX_DLCI_STATE_OPEN ) {
136- k_sem_give (& cmux .tx_sem );
127+ cmux .dlcis [cmux .at_channel ].instance .state == MODEM_CMUX_DLCI_STATE_OPEN &&
128+ !ring_buf_is_empty (& cmux .tx_rb )) {
129+ k_work_submit_to_queue (& slm_work_q , & cmux .tx_work );
137130 }
138131 break ;
139132 }
@@ -164,74 +157,121 @@ static void init_dlci(size_t dlci_idx, uint16_t recv_buf_size,
164157 modem_pipe_attach (dlci -> pipe , dlci_pipe_event_handler , dlci );
165158}
166159
167- static int cmux_write (struct modem_pipe * pipe , const uint8_t * data , size_t len )
160+ static size_t cmux_write (struct modem_pipe * pipe , const uint8_t * data , size_t len )
168161{
169- int ret ;
170162 size_t sent_len = 0 ;
163+ int ret = 0 ;
171164
172- if (cmux .dlcis [cmux .at_channel ].instance .state != MODEM_CMUX_DLCI_STATE_OPEN ) {
173- LOG_INF ("DLCI %u (AT) not open. Dropping %u bytes." ,
174- INDEX_TO_DLCI (cmux .at_channel ), len );
175- return 0 ;
176- }
177-
178- do {
179- ret = k_sem_take (& cmux .tx_sem , K_SECONDS (1 ));
180- if (ret ) {
181- LOG_WRN ("TX idle timeout. (%d)" , ret );
182- break ;
183- }
184-
165+ while (sent_len < len ) {
166+ /* Push data to CMUX TX buffer. */
185167 ret = modem_pipe_transmit (pipe , data , len - sent_len );
186- if (ret > 0 ) {
187- sent_len += ret ;
188- data += ret ;
168+ if (ret <= 0 ) {
169+ break ;
189170 }
190- } while (ret >= 0 && sent_len < len );
171+ sent_len += ret ;
172+ data += ret ;
173+ }
191174
192175 if (ret < 0 ) {
193- LOG_ERR ("DLCI %u (AT). Sent %u out of %u bytes. (%d)" ,
176+ LOG_DBG ("DLCI %u (AT). Sent %u out of %u bytes. (%d)" ,
194177 INDEX_TO_DLCI (cmux .at_channel ), sent_len , len , ret );
195- return ret ;
196178 }
197179
198- return 0 ;
180+ return sent_len ;
199181}
200182
201183static void tx_work_fn (struct k_work * work )
202184{
203185 uint8_t * data ;
204186 size_t len ;
205187
206- LOG_DBG ("tx_work_fn()" );
188+ k_mutex_lock (& cmux .tx_rb_mutex , K_FOREVER );
189+
207190 do {
208- /* Ignore errors when sending notification data. */
209- len = ring_buf_get_claim ( & cmux .notif_rb , & data ,
210- ring_buf_capacity_get (& cmux .notif_rb ) );
211- ( void ) cmux_write ( cmux . dlcis [ cmux . at_channel ]. pipe , data , len );
212- ring_buf_get_finish ( & cmux .notif_rb , len );
191+ len = ring_buf_get_claim ( & cmux . tx_rb , & data , ring_buf_capacity_get ( & cmux . tx_rb ));
192+ len = cmux_write ( cmux .dlcis [ cmux . at_channel ]. pipe , data , len );
193+ ring_buf_get_finish (& cmux .tx_rb , len );
194+
195+ } while (! ring_buf_is_empty ( & cmux .tx_rb ) && len != 0 );
213196
214- } while (!ring_buf_is_empty (& cmux .notif_rb ));
197+ k_mutex_unlock (& cmux .tx_rb_mutex );
198+
199+ if (!ring_buf_is_empty (& cmux .tx_rb )) {
200+ LOG_DBG ("Remaining bytes in TX buffer: %u." , ring_buf_size_get (& cmux .tx_rb ));
201+ }
215202}
216203
217- static int cmux_write_at_channel (const uint8_t * data , size_t len )
204+ static int cmux_write_at_channel_nonblock (const uint8_t * data , size_t len )
218205{
219- int ret ;
206+ int ret = 0 ;
220207
221- /* Send only from SLM work queue. */
222- if (k_current_get () != & slm_work_q .thread ) {
223- k_mutex_lock (& cmux .notif_rb_mutex , K_FOREVER );
224- ret = ring_buf_put (& cmux .notif_rb , data , len );
225- k_mutex_unlock (& cmux .notif_rb_mutex );
226- if (ret != len ) {
227- LOG_WRN ("CMUX notification buffer overflow. Dropping %u bytes." , len - ret );
208+ k_mutex_lock (& cmux .tx_rb_mutex , K_FOREVER );
209+
210+ if (ring_buf_space_get (& cmux .tx_rb ) >= len ) {
211+ ring_buf_put (& cmux .tx_rb , data , len );
212+ } else {
213+ LOG_WRN ("TX buf overflow, dropping %u bytes." , len );
214+ ret = - ENOBUFS ;
215+ }
216+
217+ k_mutex_unlock (& cmux .tx_rb_mutex );
218+
219+ return ret ;
220+ }
221+
222+ static int cmux_write_at_channel_block (const uint8_t * data , size_t len )
223+ {
224+ size_t sent = 0 ;
225+ size_t ret ;
226+ uint8_t * buf ;
227+
228+ k_mutex_lock (& cmux .tx_rb_mutex , K_FOREVER );
229+
230+ while (sent < len ) {
231+ ret = ring_buf_put (& cmux .tx_rb , data + sent , len - sent );
232+ sent += ret ;
233+ if (!ret ) {
234+ /* Buffer full, send partial data. */
235+ ret = ring_buf_get_claim (& cmux .tx_rb , & buf ,
236+ ring_buf_capacity_get (& cmux .tx_rb ));
237+ ret = cmux_write (cmux .dlcis [cmux .at_channel ].pipe , buf , ret );
238+ ring_buf_get_finish (& cmux .tx_rb , ret );
239+
240+ if (ret == 0 ) {
241+ /* Cannot send and buffers are full.
242+ * Data will be dropped.
243+ */
244+ break ;
245+ }
228246 }
229- k_work_submit_to_queue ( & slm_work_q , & cmux . tx_work );
247+ }
230248
231- return 0 ;
249+ k_mutex_unlock (& cmux .tx_rb_mutex );
250+
251+ if (sent < len ) {
252+ LOG_WRN ("TX buf overflow, dropping %u bytes." , len - sent );
253+ return - ENOBUFS ;
232254 }
233255
234- return cmux_write (cmux .dlcis [cmux .at_channel ].pipe , data , len );
256+ return 0 ;
257+ }
258+
259+ static int cmux_write_at_channel (const uint8_t * data , size_t len )
260+ {
261+ size_t ret ;
262+
263+ /* CMUX work queue needs to be able to run.
264+ * So, we will send only from SLM work queue.
265+ */
266+ if (k_current_get () != & slm_work_q .thread ) {
267+ ret = cmux_write_at_channel_nonblock (data , len );
268+ } else {
269+ ret = cmux_write_at_channel_block (data , len );
270+ }
271+
272+ k_work_submit_to_queue (& slm_work_q , & cmux .tx_work );
273+
274+ return ret ;
235275}
236276
237277static void close_pipe (struct modem_pipe * * pipe )
@@ -290,9 +330,8 @@ void slm_cmux_init(void)
290330 cmux .dlci_channel_rx = ATOMIC_INIT (0 );
291331 k_work_init (& cmux .rx_work , rx_work_fn );
292332
293- k_sem_init (& cmux .tx_sem , 0 , 1 );
294- ring_buf_init (& cmux .notif_rb , sizeof (cmux .notif_buffer ), cmux .notif_buffer );
295- k_mutex_init (& cmux .notif_rb_mutex );
333+ ring_buf_init (& cmux .tx_rb , sizeof (cmux .tx_buffer ), cmux .tx_buffer );
334+ k_mutex_init (& cmux .tx_rb_mutex );
296335 k_work_init (& cmux .tx_work , tx_work_fn );
297336}
298337
0 commit comments