fixeria has uploaded this change for review. ( https://gerrit.osmocom.org/c/libosmocore/+/35044?usp=email )
Change subject: soft_uart: implement the transmitter ......................................................................
soft_uart: implement the transmitter
Change-Id: Ibcd9643227e5616efd8bbd7a1430feda6fcef45c Related: OS#4396 --- M include/osmocom/core/soft_uart.h M src/core/soft_uart.c 2 files changed, 104 insertions(+), 17 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/44/35044/1
diff --git a/include/osmocom/core/soft_uart.h b/include/osmocom/core/soft_uart.h index df14648..6088922 100644 --- a/include/osmocom/core/soft_uart.h +++ b/include/osmocom/core/soft_uart.h @@ -53,6 +53,8 @@ uint8_t num_stop_bits; /*! parity mode (none, even, odd) */ enum osmo_soft_uart_parity_mode parity_mode; + /*! size of transmit buffer */ + unsigned int tx_buf_size; /*! size of receive buffer; UART will buffer up to that number of characters * before calling the receive call-back */ unsigned int rx_buf_size; @@ -85,5 +87,5 @@ int osmo_soft_uart_rx_ubits(struct osmo_soft_uart *suart, const ubit_t *ubits, size_t n_ubits); int osmo_soft_uart_tx_ubits(struct osmo_soft_uart *suart, ubit_t *ubits, size_t n_ubits);
-void osmo_soft_uart_tx(struct osmo_soft_uart *suart, struct msgb *tx_data); +size_t osmo_soft_uart_tx(struct osmo_soft_uart *suart, const uint8_t *data, size_t data_len); int osmo_soft_uart_set_status(struct osmo_soft_uart *suart, unsigned int status); diff --git a/src/core/soft_uart.c b/src/core/soft_uart.c index d92b8bc..9b6ca05 100644 --- a/src/core/soft_uart.c +++ b/src/core/soft_uart.c @@ -2,6 +2,7 @@ * Software UART implementation. */ /* * (C) 2022 by Harald Welte laforge@gnumonks.org + * (C) 2023 by sysmocom - s.f.m.c. GmbH info@sysmocom.de * * All Rights Reserved * @@ -52,8 +53,14 @@ bool running; uint8_t bit_count; uint8_t shift_reg; - struct msgb *msg; - struct llist_head queue; + struct { + uint8_t *buf; + unsigned int rpos; + unsigned int wpos; + unsigned int pending; + } rb; + ubit_t parity_bit; /* 0 (even) / 1 (odd) */ + enum suart_flow_state flow_state; } tx; };
@@ -62,6 +69,7 @@ .num_data_bits = 8, .num_stop_bits = 1, .parity_mode = OSMO_SUART_PARITY_NONE, + .tx_buf_size = 1024, .rx_buf_size = 1024, .rx_timeout_ms = 100, .priv = NULL, @@ -194,29 +202,90 @@ * Transmitter *************************************************************************/
-/*! Enqueue the given message buffer into the transmit queue of the soft-UART. +/*! Append data to the transmit buffer of the soft-UART. * \param[in] suart soft-UART instance for transmitting data. - * \param[in] tx_data message buffer containing to be transmitted data. */ -void osmo_soft_uart_tx(struct osmo_soft_uart *suart, struct msgb *tx_data) + * \param[in] data data to be transmitted. + * \param[in] data_len number of characters to be transmitted. + * \returns Number of characters appended, equal to or less than data_len. */ +size_t osmo_soft_uart_tx(struct osmo_soft_uart *suart, + const uint8_t *data, size_t data_len) { - if (!suart->tx.msg) - suart->tx.msg = tx_data; - else - msgb_enqueue(&suart->tx.queue, tx_data); + size_t len; + + len = OSMO_MIN(data_len, suart->cfg.tx_buf_size - suart->tx.rb.pending); + for (size_t i = 0; i < len; i++) { + if (suart->tx.rb.wpos >= suart->cfg.tx_buf_size) + suart->tx.rb.wpos = 0; + suart->tx.rb.buf[suart->tx.rb.wpos++] = data[i]; + } + + suart->tx.rb.pending += len; + return len; }
/* pull a single bit out of the UART transmitter */ static inline ubit_t osmo_uart_tx_bit(struct osmo_soft_uart *suart) { + ubit_t tx_bit; + if (!suart->tx.running) return 1;
- if (suart->tx.bit_count == 0) { - /* do we have anything to transmit? */ - /* FIXME */ + switch (suart->tx.flow_state) { + case SUART_FLOW_ST_IDLE: + tx_bit = 1; + if (suart->tx.rb.pending) { + if (suart->tx.rb.rpos >= suart->cfg.tx_buf_size) + suart->tx.rb.rpos = 0; + suart->tx.shift_reg = suart->tx.rb.buf[suart->tx.rb.rpos++]; + suart->tx.rb.pending--; + suart->tx.flow_state = SUART_FLOW_ST_DATA; + suart->tx.bit_count = 0; + suart->tx.parity_bit = 0; + tx_bit = 0; + } + break; + case SUART_FLOW_ST_DATA: + tx_bit = suart->tx.shift_reg & 1; + suart->tx.parity_bit ^= tx_bit; + suart->tx.shift_reg >>= 1; + suart->tx.bit_count++; + if (suart->tx.bit_count >= suart->cfg.num_data_bits) { + /* we have transmitted all data bits */ + if (suart->cfg.parity_mode != OSMO_SUART_PARITY_NONE) + suart->tx.flow_state = SUART_FLOW_ST_PARITY; + else + suart->tx.flow_state = SUART_FLOW_ST_STOP; + } + break; + case SUART_FLOW_ST_PARITY: + switch (suart->cfg.parity_mode) { + case OSMO_SUART_PARITY_EVEN: + /* number of 1-bits (in both data and parity) shall be even */ + tx_bit = suart->tx.parity_bit; + break; + case OSMO_SUART_PARITY_ODD: + /* number of 1-bits (in both data and parity) shall be odd */ + tx_bit = !suart->tx.parity_bit; + break; + case OSMO_SUART_PARITY_NONE: /* shall not happen */ + default: + OSMO_ASSERT(0); + } + + suart->tx.flow_state = SUART_FLOW_ST_STOP; + break; + case SUART_FLOW_ST_STOP: + tx_bit = 1; + suart->tx.bit_count++; + if (suart->tx.bit_count >= (suart->cfg.num_data_bits + suart->cfg.num_stop_bits)) { + /* we have transmitted all stop bits, we're done */ + suart->tx.flow_state = SUART_FLOW_ST_IDLE; + } + break; } - /* FIXME */ - return 1; + + return tx_bit; }
/*! Pull a number of unpacked bits out of the soft-UART transmitter. @@ -297,7 +366,6 @@ suart->cfg = *cfg;
osmo_timer_setup(&suart->rx.timer, suart_rx_timer_cb, suart); - INIT_LLIST_HEAD(&suart->tx.queue);
return 0; } @@ -329,10 +397,17 @@ int osmo_soft_uart_set_tx(struct osmo_soft_uart *suart, bool enable) { if (!enable && suart->tx.running) { - /* FIXME: Tx */ + suart->tx.rb.wpos = 0; + suart->tx.rb.rpos = 0; + suart->tx.rb.pending = 0; suart->tx.running = false; + suart->tx.flow_state = SUART_FLOW_ST_IDLE; } else if (enable && !suart->tx.running) { + if (suart->tx.rb.buf == NULL) + suart->tx.rb.buf = talloc_size(suart, suart->cfg.tx_buf_size); + OSMO_ASSERT(suart->tx.rb.buf != NULL); suart->tx.running = true; + suart->tx.flow_state = SUART_FLOW_ST_IDLE; }
return 0;