Skip to content

Commit 809c81b

Browse files
MarkusLassilaSeppoTakalo
authored andcommitted
applications: serial_lte_modem: Use TX buffer for CMUX
SLM TX buffering added to support incoming data during AT#XSLEEP=2. If PPP was active, the CMUX TX buffer would fill and the AT command data would be lost. CONFIG_SLM_CMUX_NOTIFICATION_TX_BUFFER_SIZE was renamed to CONFIG_SLM_CMUX_TX_BUFFER_SIZE. Signed-off-by: Markus Lassila <[email protected]>
1 parent 7a45d81 commit 809c81b

File tree

2 files changed

+102
-61
lines changed

2 files changed

+102
-61
lines changed

applications/serial_lte_modem/Kconfig

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -194,13 +194,15 @@ config SLM_CMUX_UART_BUFFER_SIZE
194194
Size of the buffer for data received in CMUX mode.
195195
Same buffer size is used for both RX and TX.
196196

197-
config SLM_CMUX_NOTIFICATION_TX_BUFFER_SIZE
198-
int "TX buffer size for unsolicited notifications in CMUX"
197+
config SLM_CMUX_TX_BUFFER_SIZE
198+
int "TX buffer size for CMUX"
199199
depends on SLM_CMUX
200200
default 4096
201201
help
202-
Unsolicited notifications need to be buffered with CMUX.
203-
Notifications longer than this size will get truncated.
202+
Size of the buffer for data sent in CMUX mode. The buffer has to be large enough
203+
to hold the largest unsolicited notification that can be sent by the modem.
204+
Notifications longer than this size will get dropped.
205+
204206
This can be reduced if your use cases do not require lengthy notifications.
205207
Note: %NCELLMEAS notifications can be nearly 4kB in size,
206208
which explains the default value.

applications/serial_lte_modem/src/slm_cmux.c

Lines changed: 96 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -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

201183
static 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

237277
static 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

Comments
 (0)