@@ -60,10 +60,9 @@ static struct {
60
60
struct k_work rx_work ;
61
61
62
62
/* 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 ;
67
66
struct k_work tx_work ;
68
67
69
68
} cmux ;
@@ -111,17 +110,10 @@ static void dlci_pipe_event_handler(struct modem_pipe *pipe,
111
110
*/
112
111
case MODEM_PIPE_EVENT_OPENED :
113
112
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
- }
118
113
break ;
119
114
120
115
case MODEM_PIPE_EVENT_CLOSED :
121
116
LOG_INF ("DLCI %u%s closed." , dlci -> address , is_at ? " (AT)" : "" );
122
- if (is_at ) {
123
- k_sem_reset (& cmux .tx_sem );
124
- }
125
117
break ;
126
118
127
119
case MODEM_PIPE_EVENT_RECEIVE_READY :
@@ -132,8 +124,9 @@ static void dlci_pipe_event_handler(struct modem_pipe *pipe,
132
124
133
125
case MODEM_PIPE_EVENT_TRANSMIT_IDLE :
134
126
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 );
137
130
}
138
131
break ;
139
132
}
@@ -164,74 +157,121 @@ static void init_dlci(size_t dlci_idx, uint16_t recv_buf_size,
164
157
modem_pipe_attach (dlci -> pipe , dlci_pipe_event_handler , dlci );
165
158
}
166
159
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 )
168
161
{
169
- int ret ;
170
162
size_t sent_len = 0 ;
163
+ int ret = 0 ;
171
164
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. */
185
167
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 ;
189
170
}
190
- } while (ret >= 0 && sent_len < len );
171
+ sent_len += ret ;
172
+ data += ret ;
173
+ }
191
174
192
175
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)" ,
194
177
INDEX_TO_DLCI (cmux .at_channel ), sent_len , len , ret );
195
- return ret ;
196
178
}
197
179
198
- return 0 ;
180
+ return sent_len ;
199
181
}
200
182
201
183
static void tx_work_fn (struct k_work * work )
202
184
{
203
185
uint8_t * data ;
204
186
size_t len ;
205
187
206
- LOG_DBG ("tx_work_fn()" );
188
+ k_mutex_lock (& cmux .tx_rb_mutex , K_FOREVER );
189
+
207
190
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 );
213
196
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
+ }
215
202
}
216
203
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 )
218
205
{
219
- int ret ;
206
+ int ret = 0 ;
220
207
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
+ }
228
246
}
229
- k_work_submit_to_queue ( & slm_work_q , & cmux . tx_work );
247
+ }
230
248
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 ;
232
254
}
233
255
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 ;
235
275
}
236
276
237
277
static void close_pipe (struct modem_pipe * * pipe )
@@ -290,9 +330,8 @@ void slm_cmux_init(void)
290
330
cmux .dlci_channel_rx = ATOMIC_INIT (0 );
291
331
k_work_init (& cmux .rx_work , rx_work_fn );
292
332
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 );
296
335
k_work_init (& cmux .tx_work , tx_work_fn );
297
336
}
298
337
0 commit comments