blob: 5b588481c2ca26c9495e341535852cd5980ce473 [file] [log] [blame]
Harald Weltedc023cf2022-11-29 23:15:18 +01001/*! \file soft_uart.c
2 * Software UART implementation. */
3/*
4 * (C) 2022 by Harald Welte <laforge@gnumonks.org>
5 *
6 * All Rights Reserved
7 *
8 * SPDX-License-Identifier: GPL-2.0+
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 */
21
22#include <stdbool.h>
23#include <stdint.h>
24#include <errno.h>
25
26#include <osmocom/core/timer.h>
27#include <osmocom/core/soft_uart.h>
28
Vadim Yanitskiyb72625d2023-11-16 00:32:33 +070029/*! Internal state of a soft-UART */
Harald Weltedc023cf2022-11-29 23:15:18 +010030struct osmo_soft_uart {
31 struct osmo_soft_uart_cfg cfg;
32 const char *name;
33 struct {
34 bool running;
35 uint8_t bit_count;
36 uint8_t shift_reg;
37 struct msgb *msg;
38 ubit_t parity_bit;
39 unsigned int flags;
40 unsigned int status;
41 struct osmo_timer_list timer;
42 } rx;
43 struct {
44 bool running;
45 uint8_t bit_count;
46 uint8_t shift_reg;
47 struct msgb *msg;
48 struct llist_head queue;
49 } tx;
50};
51
Vadim Yanitskiyb72625d2023-11-16 00:32:33 +070052/*! Default soft-UART configuration (8-N-1) */
Vadim Yanitskiy82a1ae72023-11-16 15:34:50 +070053const struct osmo_soft_uart_cfg osmo_soft_uart_default_cfg = {
Harald Weltedc023cf2022-11-29 23:15:18 +010054 .num_data_bits = 8,
55 .num_stop_bits = 1,
56 .parity_mode = OSMO_SUART_PARITY_NONE,
57 .rx_buf_size = 1024,
58 .rx_timeout_ms = 100,
Harald Weltedc023cf2022-11-29 23:15:18 +010059};
60
61/*************************************************************************
62 * Receiver
63 *************************************************************************/
64
65/* flush the receive buffer + allocate new one, as needed */
66static void suart_flush_rx(struct osmo_soft_uart *suart)
67{
68 if ((suart->rx.msg && msgb_length(suart->rx.msg)) || suart->rx.flags) {
69 osmo_timer_del(&suart->rx.timer);
70 if (suart->cfg.rx_cb) {
71 suart->cfg.rx_cb(suart->cfg.priv, suart->rx.msg, suart->rx.flags);
72 /* call-back has taken ownership of msgb, no need to free() here */
73 suart->rx.msg = msgb_alloc_c(suart, suart->cfg.rx_buf_size, "soft_uart rx");
74 } else {
75 msgb_reset(suart->rx.msg);
76 }
77 }
78}
79
80/* one character was received; add to receive buffer and notify user, if needed */
81static void suart_rx_ch(struct osmo_soft_uart *suart, uint8_t ch)
82{
83 unsigned int msg_len;
84
85 OSMO_ASSERT(suart->rx.msg);
86 msgb_put_u8(suart->rx.msg, ch);
87 msg_len = msgb_length(suart->rx.msg);
88
89 /* first character in new message: start timer */
90 if (msg_len == 1) {
91 osmo_timer_schedule(&suart->rx.timer, suart->cfg.rx_timeout_ms / 1000,
92 (suart->cfg.rx_timeout_ms % 1000) * 1000);
93 } else if (msg_len >= suart->cfg.rx_buf_size || suart->rx.flags) {
94 suart_flush_rx(suart);
95 }
96}
97
98/* receive a single bit */
99static inline void osmo_uart_rx_bit(struct osmo_soft_uart *suart, const ubit_t bit)
100{
101 unsigned int num_parity_bits = 0;
102
103 if (!suart->rx.running)
104 return;
105
106 if (suart->rx.bit_count == 0) {
107 /* start bit is 0. Wait if there is none */
108 if (bit == 0) {
109 /* START bit */
110 suart->rx.flags = 0;
111 suart->rx.shift_reg = 0;
112 suart->rx.bit_count++;
113 }
114 return;
115 }
116
117 if (suart->cfg.parity_mode != OSMO_SUART_PARITY_NONE)
118 num_parity_bits = 1;
119
120 suart->rx.bit_count++;
121 if (suart->rx.bit_count <= 1 + suart->cfg.num_data_bits) {
122 /* DATA bit */
123 suart->rx.shift_reg = suart->rx.shift_reg >> 1;
124 if (bit)
125 suart->rx.shift_reg |= 0x80;
126 } else if (suart->cfg.parity_mode != OSMO_SUART_PARITY_NONE &&
127 suart->rx.bit_count == 1 + suart->cfg.num_data_bits + 1) {
128 /* PARITY bit */
129 suart->rx.parity_bit = bit;
130 /* TODO: verify parity */
131 //suart->rx.flags |= OSMO_SUART_F_PARITY_ERROR;
132 } else if (suart->rx.bit_count <=
133 1 + suart->cfg.num_data_bits + num_parity_bits + suart->cfg.num_stop_bits) {
134 /* STOP bit */
135 if (bit != 1) {
136 fprintf(stderr, "framing error: stop bit %u != 1\n", suart->rx.bit_count);
137 suart->rx.flags |= OSMO_SUART_F_FRAMING_ERROR;
138 }
139
140 if (suart->rx.bit_count == 1 + suart->cfg.num_data_bits + num_parity_bits + suart->cfg.num_stop_bits) {
141 //printf("Rx: 0x%02x %c\n", suart->rx.shift_reg, suart->rx.shift_reg);
142 suart_rx_ch(suart, suart->rx.shift_reg);
143 suart->rx.bit_count = 0;
144 }
145 }
146}
147
148/* receive timer expiration: flush rx-buffer to user call-back */
149static void suart_rx_timer_cb(void *data)
150{
151 struct osmo_soft_uart *suart = data;
152 suart_flush_rx(suart);
153}
154
Vadim Yanitskiyb72625d2023-11-16 00:32:33 +0700155/*! Feed a number of unpacked bits into the soft-UART receiver.
156 * \param[in] suart soft-UART instance to feed bits into.
157 * \param[in] ubits pointer to the unpacked bits.
158 * \param[in] n_ubits number of unpacked bits to be fed.
159 * \returns 0 on success; negative on error. */
Harald Weltedc023cf2022-11-29 23:15:18 +0100160int osmo_soft_uart_rx_ubits(struct osmo_soft_uart *suart, const ubit_t *ubits, size_t n_ubits)
161{
162 for (size_t i = 0; i < n_ubits; i++)
163 osmo_uart_rx_bit(suart, ubits[i]);
164 return 0;
165}
166
167/*************************************************************************
168 * Transmitter
169 *************************************************************************/
170
Vadim Yanitskiyb72625d2023-11-16 00:32:33 +0700171/*! Enqueue the given message buffer into the transmit queue of the soft-UART.
172 * \param[in] suart soft-UART instance for transmitting data.
173 * \param[in] tx_data message buffer containing to be transmitted data. */
Harald Weltedc023cf2022-11-29 23:15:18 +0100174void osmo_soft_uart_tx(struct osmo_soft_uart *suart, struct msgb *tx_data)
175{
176 if (!suart->tx.msg)
177 suart->tx.msg = tx_data;
178 else
179 msgb_enqueue(&suart->tx.queue, tx_data);
180}
181
182/* pull a single bit out of the UART transmitter */
183static inline ubit_t osmo_uart_tx_bit(struct osmo_soft_uart *suart)
184{
185 if (!suart->tx.running)
186 return 1;
187
188 if (suart->tx.bit_count == 0) {
189 /* do we have anything to transmit? */
190 /* FIXME */
191 }
192 /* FIXME */
193 return 1;
194}
195
Vadim Yanitskiyb72625d2023-11-16 00:32:33 +0700196/*! Pull a number of unpacked bits out of the soft-UART transmitter.
197 * \param[in] suart soft-UART instance to pull the bits from.
198 * \param[out] ubits pointer to a buffer where to store pulled bits.
199 * \param[in] n_ubits number of unpacked bits to be pulled.
200 * \returns number of unpacked bits pulled; negative on error. */
Harald Weltedc023cf2022-11-29 23:15:18 +0100201int osmo_soft_uart_tx_ubits(struct osmo_soft_uart *suart, ubit_t *ubits, size_t n_ubits)
202{
203 for (size_t i = 0; i < n_ubits; i++)
204 ubits[i] = osmo_uart_tx_bit(suart);
205 return n_ubits;
206}
207
Vadim Yanitskiyb72625d2023-11-16 00:32:33 +0700208/*! Set the modem status lines of the given soft-UART.
209 * \param[in] suart soft-UART instance to update the modem status.
210 * \param[in] status mask of osmo_soft_uart_status.
211 * \returns 0 on success; negative on error. */
Harald Weltedc023cf2022-11-29 23:15:18 +0100212int osmo_soft_uart_set_status(struct osmo_soft_uart *suart, unsigned int status)
213{
214 /* FIXME: Tx */
215 return 0;
216}
217
218
219/*************************************************************************
220 * Management / Initialization
221 *************************************************************************/
222
Vadim Yanitskiyb72625d2023-11-16 00:32:33 +0700223/*! Allocate a soft-UART instance.
224 * \param[in] ctx parent talloc context.
225 * \param[in] name name of the soft-UART instance.
Vadim Yanitskiy82a1ae72023-11-16 15:34:50 +0700226 * \param[in] cfg initial configuration of the soft-UART instance.
Vadim Yanitskiyb72625d2023-11-16 00:32:33 +0700227 * \returns pointer to allocated soft-UART instance; NULL on error. */
Vadim Yanitskiy82a1ae72023-11-16 15:34:50 +0700228struct osmo_soft_uart *osmo_soft_uart_alloc(void *ctx, const char *name,
229 const struct osmo_soft_uart_cfg *cfg)
Harald Weltedc023cf2022-11-29 23:15:18 +0100230{
231 struct osmo_soft_uart *suart = talloc_zero(ctx, struct osmo_soft_uart);
232 if (!suart)
233 return NULL;
234 suart->name = talloc_strdup(suart, name);
Vadim Yanitskiy82a1ae72023-11-16 15:34:50 +0700235
236 OSMO_ASSERT(cfg != NULL);
237 suart->cfg = *cfg;
Harald Weltedc023cf2022-11-29 23:15:18 +0100238
239 return suart;
240}
241
Vadim Yanitskiy877cfed2023-11-12 17:21:31 +0700242/*! Release memory taken by the given soft-UART.
243 * \param[in] suart soft-UART instance to be free()d. */
244void osmo_soft_uart_free(struct osmo_soft_uart *suart)
245{
246 if (suart == NULL)
247 return;
248
249 osmo_timer_del(&suart->rx.timer);
250 msgb_free(suart->rx.msg);
251
252 talloc_free((void *)suart->name);
253 talloc_free(suart);
254}
255
Vadim Yanitskiyb72625d2023-11-16 00:32:33 +0700256/*! Change soft-UART configuration to the user-provided config.
257 * \param[in] suart soft-UART instance to be re-configured.
258 * \param[in] cfg the user-provided config to be applied.
259 * \returns 0 on success; negative on error. */
Harald Weltedc023cf2022-11-29 23:15:18 +0100260int osmo_soft_uart_configure(struct osmo_soft_uart *suart, const struct osmo_soft_uart_cfg *cfg)
261{
262 /* consistency checks on the configuration */
263 if (cfg->num_data_bits > 8 || cfg->num_data_bits == 0)
264 return -EINVAL;
265 if (cfg->num_stop_bits == 0)
266 return -EINVAL;
267 if (cfg->parity_mode < 0 || cfg->parity_mode >= _OSMO_SUART_PARITY_NUM)
268 return -EINVAL;
269 if (cfg->rx_buf_size == 0)
270 return -EINVAL;
271
272 if (suart->cfg.rx_buf_size > cfg->rx_buf_size ||
273 suart->cfg.rx_timeout_ms > cfg->rx_timeout_ms) {
274 suart_flush_rx(suart);
275 }
276
277 suart->cfg = *cfg;
278
279 osmo_timer_setup(&suart->rx.timer, suart_rx_timer_cb, suart);
280 INIT_LLIST_HEAD(&suart->tx.queue);
281
282 return 0;
283}
284
Vadim Yanitskiycdde6712023-11-16 15:17:37 +0700285/*! Enable/disable receiver of the given soft-UART.
Vadim Yanitskiyb72625d2023-11-16 00:32:33 +0700286 * \param[in] suart soft-UART instance to be re-configured.
Vadim Yanitskiycdde6712023-11-16 15:17:37 +0700287 * \param[in] enable enable/disable state of the receiver.
Vadim Yanitskiyb72625d2023-11-16 00:32:33 +0700288 * \returns 0 on success; negative on error. */
Vadim Yanitskiycdde6712023-11-16 15:17:37 +0700289int osmo_soft_uart_set_rx(struct osmo_soft_uart *suart, bool enable)
Harald Weltedc023cf2022-11-29 23:15:18 +0100290{
Vadim Yanitskiycdde6712023-11-16 15:17:37 +0700291 if (!enable && suart->rx.running) {
Harald Weltedc023cf2022-11-29 23:15:18 +0100292 suart_flush_rx(suart);
293 suart->rx.running = false;
Vadim Yanitskiycdde6712023-11-16 15:17:37 +0700294 } else if (enable && !suart->rx.running) {
Harald Weltedc023cf2022-11-29 23:15:18 +0100295 if (!suart->rx.msg)
296 suart->rx.msg = msgb_alloc_c(suart, suart->cfg.rx_buf_size, "soft_uart rx");
297 suart->rx.running = true;
298 }
299
Vadim Yanitskiycdde6712023-11-16 15:17:37 +0700300 return 0;
301}
302
303/*! Enable/disable transmitter of the given soft-UART.
304 * \param[in] suart soft-UART instance to be re-configured.
305 * \param[in] enable enable/disable state of the transmitter.
306 * \returns 0 on success; negative on error. */
307int osmo_soft_uart_set_tx(struct osmo_soft_uart *suart, bool enable)
308{
309 if (!enable && suart->tx.running) {
Harald Weltedc023cf2022-11-29 23:15:18 +0100310 /* FIXME: Tx */
311 suart->tx.running = false;
Vadim Yanitskiycdde6712023-11-16 15:17:37 +0700312 } else if (enable && !suart->tx.running) {
Harald Weltedc023cf2022-11-29 23:15:18 +0100313 suart->tx.running = true;
314 }
315
316 return 0;
317}