soft_uart: implement the transmitter
Change-Id: Ibcd9643227e5616efd8bbd7a1430feda6fcef45c
Related: OS#4396
diff --git a/include/osmocom/core/soft_uart.h b/include/osmocom/core/soft_uart.h
index 2b9d67f..df2faac 100644
--- a/include/osmocom/core/soft_uart.h
+++ b/include/osmocom/core/soft_uart.h
@@ -68,6 +68,11 @@
* 'flags' is a bit-mask of osmo_soft_uart_flags, */
void (*rx_cb)(void *priv, struct msgb *rx_data, unsigned int flags);
+ /*! transmit call-back. The implementation is expected to provide at most
+ * tx_data->data_len characters (the actual amount is detetmined by the
+ * number of requested bits and the effective UART configuration). */
+ void (*tx_cb)(void *priv, struct msgb *tx_data);
+
/*! modem status line change call-back. gets bitmask of osmo_soft_uart_status */
void (*status_change_cb)(void *priv, unsigned int status);
};
@@ -86,5 +91,4 @@
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);
int osmo_soft_uart_set_status(struct osmo_soft_uart *suart, unsigned int status);
diff --git a/src/core/libosmocore.map b/src/core/libosmocore.map
index a9d25fb..e6a1866 100644
--- a/src/core/libosmocore.map
+++ b/src/core/libosmocore.map
@@ -447,7 +447,6 @@
osmo_soft_uart_set_tx;
osmo_soft_uart_rx_ubits;
osmo_soft_uart_tx_ubits;
-osmo_soft_uart_tx;
osmo_soft_uart_set_status;
osmo_stat_item_dec;
osmo_stat_item_flush;
diff --git a/src/core/soft_uart.c b/src/core/soft_uart.c
index d8ce9d9..238425a 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
*
@@ -53,8 +54,8 @@
bool running;
uint8_t bit_count;
uint8_t shift_reg;
- struct msgb *msg;
- struct llist_head queue;
+ ubit_t parity_bit; /* 0 (even) / 1 (odd) */
+ enum suart_flow_state flow_state;
} tx;
};
@@ -79,7 +80,7 @@
if (suart->cfg.rx_cb) {
suart->cfg.rx_cb(suart->cfg.priv, suart->rx.msg, suart->rx.flags);
/* call-back has taken ownership of msgb, no need to free() here */
- suart->rx.msg = msgb_alloc_c(suart, suart->cfg.rx_buf_size, "soft_uart rx");
+ suart->rx.msg = msgb_alloc_c(suart, suart->cfg.rx_buf_size, "soft_uart_rx");
} else {
msgb_reset(suart->rx.msg);
}
@@ -191,29 +192,64 @@
* Transmitter
*************************************************************************/
-/*! Enqueue the given message buffer into the transmit queue 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)
-{
- if (!suart->tx.msg)
- suart->tx.msg = tx_data;
- else
- msgb_enqueue(&suart->tx.queue, tx_data);
-}
-
/* pull a single bit out of the UART transmitter */
-static inline ubit_t osmo_uart_tx_bit(struct osmo_soft_uart *suart)
+static inline ubit_t osmo_uart_tx_bit(struct osmo_soft_uart *suart, struct msgb *msg)
{
- if (!suart->tx.running)
- return 1;
+ ubit_t tx_bit = 1;
- if (suart->tx.bit_count == 0) {
- /* do we have anything to transmit? */
- /* FIXME */
+ if (!suart->tx.running)
+ return tx_bit;
+
+ switch (suart->tx.flow_state) {
+ case SUART_FLOW_ST_IDLE:
+ if (msgb_length(msg) > 0) { /* if we have pending data */
+ suart->tx.shift_reg = msgb_pull_u8(msg);
+ 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:
+ default: /* shall not happen */
+ OSMO_ASSERT(0);
+ }
+
+ suart->tx.flow_state = SUART_FLOW_ST_STOP;
+ break;
+ case SUART_FLOW_ST_STOP:
+ 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.
@@ -223,9 +259,28 @@
* \returns number of unpacked bits pulled; negative on error. */
int osmo_soft_uart_tx_ubits(struct osmo_soft_uart *suart, ubit_t *ubits, size_t n_ubits)
{
+ const struct osmo_soft_uart_cfg *cfg = &suart->cfg;
+ size_t n_frame_bits;
+ struct msgb *msg;
+
+ /* calculate UART frame size for the effective config */
+ n_frame_bits = 1 + cfg->num_data_bits + cfg->num_stop_bits;
+ if (cfg->parity_mode != OSMO_SUART_PARITY_NONE)
+ n_frame_bits += 1;
+
+ /* allocate a Tx buffer msgb */
+ msg = msgb_alloc_c(suart, n_ubits / n_frame_bits, "soft_uart_tx");
+ OSMO_ASSERT(msg != NULL);
+
+ /* call the .tx_cb() to populate the Tx buffer */
+ OSMO_ASSERT(cfg->tx_cb != NULL);
+ suart->cfg.tx_cb(cfg->priv, msg);
+
for (size_t i = 0; i < n_ubits; i++)
- ubits[i] = osmo_uart_tx_bit(suart);
- return n_ubits;
+ ubits[i] = osmo_uart_tx_bit(suart, msg);
+ msgb_free(msg);
+
+ return 0;
}
/*! Set the modem status lines of the given soft-UART.
@@ -300,7 +355,6 @@
suart->cfg = *cfg;
osmo_timer_setup(&suart->rx.timer, suart_rx_timer_cb, suart);
- INIT_LLIST_HEAD(&suart->tx.queue);
return 0;
}
@@ -332,10 +386,11 @@
int osmo_soft_uart_set_tx(struct osmo_soft_uart *suart, bool enable)
{
if (!enable && suart->tx.running) {
- /* FIXME: Tx */
suart->tx.running = false;
+ suart->tx.flow_state = SUART_FLOW_ST_IDLE;
} else if (enable && !suart->tx.running) {
suart->tx.running = true;
+ suart->tx.flow_state = SUART_FLOW_ST_IDLE;
}
return 0;