fixeria has uploaded this change for review. (
https://gerrit.osmocom.org/c/libosmocore/+/35172?usp=email )
Change subject: soft_uart: implement modem status lines and flow control
......................................................................
soft_uart: implement modem status lines and flow control
Change-Id: I26b93ce76f2f6b6fbf017f2684312007db3c6d48
Related: OS#4396
---
M include/osmocom/core/soft_uart.h
M src/core/libosmocore.map
M src/core/soft_uart.c
M tests/soft_uart/soft_uart_test.c
M tests/soft_uart/soft_uart_test.ok
5 files changed, 341 insertions(+), 12 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/72/35172/1
diff --git a/include/osmocom/core/soft_uart.h b/include/osmocom/core/soft_uart.h
index 7b7c394..a2435a8 100644
--- a/include/osmocom/core/soft_uart.h
+++ b/include/osmocom/core/soft_uart.h
@@ -4,6 +4,7 @@
* Software UART implementation. */
/*
* (C) 2022 by Harald Welte <laforge(a)gnumonks.org>
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH <info(a)sysmocom.de>
*
* All Rights Reserved
*
@@ -22,6 +23,8 @@
*/
#include <stdint.h>
+#include <stdbool.h>
+
#include <osmocom/core/bits.h>
#include <osmocom/core/msgb.h>
@@ -40,12 +43,31 @@
OSMO_SUART_F_BREAK = (1 << 2),
};
-#if 0
+/*! Modem status "line" flags.
+ *
https://en.wikipedia.org/wiki/RS-232#Data_and_control_signals */
enum osmo_soft_uart_status {
- /* RTS, CTS, ... */
- _fixme,
+ OSMO_SUART_STATUS_F_DTR = (1 << 0), /*!< Data Terminal Ready */
+ OSMO_SUART_STATUS_F_DCD = (1 << 1), /*!< Data Carrier Detect */
+ OSMO_SUART_STATUS_F_DSR = (1 << 2), /*!< Data Set Ready */
+ OSMO_SUART_STATUS_F_RI = (1 << 3), /*!< Ring Indicator */
+ OSMO_SUART_STATUS_F_RTS_RTR = (1 << 4), /*!< Request To Send or Ready To
Receive */
+ OSMO_SUART_STATUS_F_CTS = (1 << 5), /*!< Clear To Send */
};
-#endif
+
+/*! Flow control mode.
+ *
https://en.wikipedia.org/wiki/Flow_control_(data)#Hardware_flow_control */
+enum osmo_soft_uart_flow_ctrl_mode {
+ /*! No flow control */
+ OSMO_SUART_FLOW_CTRL_M_NONE,
+ /*! DTR/DSR flow control: Tx if DSR is active and drop DTR if cannot Rx anymore. */
+ OSMO_SUART_FLOW_CTRL_M_DTR_DSR,
+ /*! RTS/CTS flow control: Tx if CTS is active and drop RTS if cannot Rx anymore.
+ * The technically correct name would be RTR/CTS, because the RTS signal actually
+ * indicates readiness to *receive* data (Ready To Receive), and not really used
+ * to request a transmission (Request To Send) nowadays. Alternatively, the RTS
+ * signal can be interpreted as "Request To Send to me". */
+ OSMO_SUART_FLOW_CTRL_M_RTS_CTS,
+};
/* configuration for a soft-uart */
struct osmo_soft_uart_cfg {
@@ -75,8 +97,11 @@
* 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 */
+ /*! modem status line change call-back. gets bitmask of OSMO_SUART_STATUS_F_* */
void (*status_change_cb)(void *priv, unsigned int status);
+
+ /*! "hardware" flow control mode */
+ enum osmo_soft_uart_flow_ctrl_mode flow_ctrl_mode;
};
extern const struct osmo_soft_uart_cfg osmo_soft_uart_default_cfg;
@@ -97,5 +122,10 @@
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);
+unsigned int osmo_soft_uart_get_status(const struct osmo_soft_uart *suart);
int osmo_soft_uart_set_status(struct osmo_soft_uart *suart, unsigned int status);
+void osmo_soft_uart_set_status_line(struct osmo_soft_uart *suart,
+ enum osmo_soft_uart_status line,
+ bool active);
+
void osmo_soft_uart_flush_rx(struct osmo_soft_uart *suart);
diff --git a/src/core/libosmocore.map b/src/core/libosmocore.map
index 4ae5108..fc81650 100644
--- a/src/core/libosmocore.map
+++ b/src/core/libosmocore.map
@@ -450,7 +450,9 @@
osmo_soft_uart_set_tx;
osmo_soft_uart_rx_ubits;
osmo_soft_uart_tx_ubits;
+osmo_soft_uart_get_status;
osmo_soft_uart_set_status;
+osmo_soft_uart_set_status_line;
osmo_soft_uart_flush_rx;
osmo_stat_item_dec;
osmo_stat_item_flush;
diff --git a/src/core/soft_uart.c b/src/core/soft_uart.c
index 1d9984b..e3e3b0a 100644
--- a/src/core/soft_uart.c
+++ b/src/core/soft_uart.c
@@ -40,6 +40,8 @@
struct osmo_soft_uart {
struct osmo_soft_uart_cfg cfg;
const char *name;
+ /* modem status (bitmask of OSMO_SUART_STATUS_F_*) */
+ unsigned int status;
struct {
bool running;
uint8_t bit_count;
@@ -47,7 +49,6 @@
struct msgb *msg;
ubit_t parity_bit; /* 0 (even) / 1 (odd) */
unsigned int flags;
- unsigned int status;
struct osmo_timer_list timer;
enum suart_flow_state flow_state;
} rx;
@@ -67,6 +68,7 @@
.parity_mode = OSMO_SUART_PARITY_NONE,
.rx_buf_size = 1024,
.rx_timeout_ms = 100,
+ .flow_ctrl_mode = OSMO_SUART_FLOW_CTRL_M_NONE,
};
/*************************************************************************
@@ -193,13 +195,33 @@
* \param[in] suart soft-UART instance to feed bits into.
* \param[in] ubits pointer to the unpacked bits.
* \param[in] n_ubits number of unpacked bits to be fed.
- * \returns 0 on success; negative on error. */
+ * \returns 0 on success; negative on error.
+ * -EAGAIN indicates that the receiver is either disabled
+ * or suspended by the flow control (if enabled). */
int osmo_soft_uart_rx_ubits(struct osmo_soft_uart *suart, const ubit_t *ubits, size_t
n_ubits)
{
if (!suart->rx.running)
return -EAGAIN;
+
+ switch (suart->cfg.flow_ctrl_mode) {
+ case OSMO_SUART_FLOW_CTRL_M_DTR_DSR:
+ /* cannot Rx anymore if DTR is de-asserted */
+ if (~suart->status & OSMO_SUART_STATUS_F_DTR)
+ return -EAGAIN;
+ break;
+ case OSMO_SUART_FLOW_CTRL_M_RTS_CTS:
+ /* cannot Rx anymore if RTS/RTR is de-asserted */
+ if (~suart->status & OSMO_SUART_STATUS_F_RTS_RTR)
+ return -EAGAIN;
+ break;
+ case OSMO_SUART_FLOW_CTRL_M_NONE:
+ default:
+ break;
+ }
+
for (size_t i = 0; i < n_ubits; i++)
osmo_uart_rx_bit(suart, ubits[i]);
+
return 0;
}
@@ -276,7 +298,9 @@
* \param[in] suart soft-UART instance to pull the bits from.
* \param[out] ubits pointer to a buffer where to store pulled bits.
* \param[in] n_ubits number of unpacked bits to be pulled.
- * \returns 0 on success; negative on error. */
+ * \returns 0 on success; negative on error.
+ * -EAGAIN indicates that the transmitter is either disabled
+ * or suspended by the flow control (if enabled). */
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;
@@ -289,6 +313,22 @@
if (!suart->tx.running)
return -EAGAIN;
+ switch (suart->cfg.flow_ctrl_mode) {
+ case OSMO_SUART_FLOW_CTRL_M_DTR_DSR:
+ /* cannot Tx if DSR is de-asserted */
+ if (~suart->status & OSMO_SUART_STATUS_F_DSR)
+ return -EAGAIN;
+ break;
+ case OSMO_SUART_FLOW_CTRL_M_RTS_CTS:
+ /* cannot Tx if CTS is de-asserted */
+ if (~suart->status & OSMO_SUART_STATUS_F_CTS)
+ return -EAGAIN;
+ break;
+ case OSMO_SUART_FLOW_CTRL_M_NONE:
+ default:
+ break;
+ }
+
/* 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)
@@ -319,16 +359,55 @@
return 0;
}
-/*! Set the modem status lines of the given soft-UART.
- * \param[in] suart soft-UART instance to update the modem status.
- * \param[in] status mask of osmo_soft_uart_status.
+/*! Get the modem status bitmask of the given soft-UART.
+ * \param[in] suart soft-UART instance to get the modem status.
+ * \returns bitmask of OSMO_SUART_STATUS_F_*. */
+unsigned int osmo_soft_uart_get_status(const struct osmo_soft_uart *suart)
+{
+ return suart->status;
+}
+
+/*! Set the modem status bitmask of the given soft-UART.
+ * \param[in] suart soft-UART instance to set the modem status.
+ * \param[in] status bitmask of OSMO_SUART_STATUS_F_*.
* \returns 0 on success; negative on error. */
int osmo_soft_uart_set_status(struct osmo_soft_uart *suart, unsigned int status)
{
- /* FIXME: Tx */
+ const struct osmo_soft_uart_cfg *cfg = &suart->cfg;
+
+ if (cfg->status_change_cb != NULL) {
+ if (suart->status != status)
+ cfg->status_change_cb(cfg->priv, status);
+ }
+
+ suart->status = status;
return 0;
}
+/*! Activate/deactivate a modem status line of the given soft-UART.
+ * \param[in] suart soft-UART instance to update the modem status.
+ * \param[in] line a modem status line, one of OSMO_SUART_STATUS_F_*.
+ * \param[in] active activate (true) or deactivate (false) the line. */
+void osmo_soft_uart_set_status_line(struct osmo_soft_uart *suart,
+ enum osmo_soft_uart_status line,
+ bool active)
+{
+ const struct osmo_soft_uart_cfg *cfg = &suart->cfg;
+ unsigned int status = suart->status;
+
+ if (active) /* assert the given line */
+ status |= line;
+ else /* de-assert the given line */
+ status &= ~line;
+
+ if (cfg->status_change_cb != NULL) {
+ if (suart->status != status)
+ cfg->status_change_cb(cfg->priv, status);
+ }
+
+ suart->status = status;
+}
+
/*************************************************************************
* Management / Initialization
diff --git a/tests/soft_uart/soft_uart_test.c b/tests/soft_uart/soft_uart_test.c
index f1b0b25..3631db7 100644
--- a/tests/soft_uart/soft_uart_test.c
+++ b/tests/soft_uart/soft_uart_test.c
@@ -16,6 +16,7 @@
*
*/
+#include <errno.h>
#include <stdio.h>
#include <stdint.h>
@@ -47,6 +48,11 @@
__func__, msg->len, msg->data_len, msgb_hexdump(msg));
}
+static void suart_status_change_cb(void *priv, unsigned int status)
+{
+ fprintf(stdout, "%s(status=0x%08x)\n", __func__, status);
+}
+
static const struct osmo_soft_uart_cfg suart_test_default_cfg = {
.num_data_bits = 8,
.num_stop_bits = 1,
@@ -54,6 +60,7 @@
.rx_buf_size = 128,
.rx_cb = &suart_rx_cb,
.tx_cb = &suart_tx_cb,
+ .status_change_cb = &suart_status_change_cb,
};
static void test_rx_exec(struct osmo_soft_uart *suart,
@@ -343,6 +350,161 @@
osmo_soft_uart_free(suart);
}
+static void test_modem_status(void)
+{
+ struct osmo_soft_uart *suart;
+ unsigned int status;
+
+ suart = osmo_soft_uart_alloc(NULL, __func__, &suart_test_default_cfg);
+ OSMO_ASSERT(suart != NULL);
+
+ printf("======== %s(): initial status=0x%08x\n",
+ __func__, osmo_soft_uart_get_status(suart));
+
+ printf("de-asserting DCD, which was not asserted\n");
+ osmo_soft_uart_set_status_line(suart, OSMO_SUART_STATUS_F_DCD, false);
+ OSMO_ASSERT(osmo_soft_uart_get_status(suart) == 0x00); /* no change */
+
+ printf("asserting both RI and DCD, expecting the callback to be called
twice\n");
+ osmo_soft_uart_set_status_line(suart, OSMO_SUART_STATUS_F_RI, true);
+ osmo_soft_uart_set_status_line(suart, OSMO_SUART_STATUS_F_DCD, true);
+ status = osmo_soft_uart_get_status(suart);
+ OSMO_ASSERT(status == (OSMO_SUART_STATUS_F_RI | OSMO_SUART_STATUS_F_DCD));
+
+ printf("de-asserting RI, expecting the callback to be called\n");
+ osmo_soft_uart_set_status_line(suart, OSMO_SUART_STATUS_F_RI, false);
+ status = osmo_soft_uart_get_status(suart);
+ OSMO_ASSERT(status == (OSMO_SUART_STATUS_F_DCD));
+
+ printf("resetting to 0x00, expecting the callback to be called\n");
+ osmo_soft_uart_set_status(suart, 0x00);
+ OSMO_ASSERT(osmo_soft_uart_get_status(suart) == 0x00);
+
+ osmo_soft_uart_free(suart);
+}
+
+static void test_flow_control_dtr_dsr(void)
+{
+ struct osmo_soft_uart_cfg cfg;
+ struct osmo_soft_uart *suart;
+ ubit_t tx_buf[32];
+ int rc;
+
+ g_tx_cb_cfg.data = (void *)"\x42\x42\x42";
+ g_tx_cb_cfg.data_len = 3;
+
+ cfg = suart_test_default_cfg;
+ cfg.flow_ctrl_mode = OSMO_SUART_FLOW_CTRL_M_DTR_DSR;
+
+ suart = osmo_soft_uart_alloc(NULL, __func__, &cfg);
+ OSMO_ASSERT(suart != NULL);
+
+ osmo_soft_uart_set_tx(suart, true);
+ osmo_soft_uart_set_rx(suart, true);
+
+ /* a) expect the initial status to be 0 (all lines de-asserted) */
+ printf("======== %s(): initial status=0x%08x\n",
+ __func__, osmo_soft_uart_get_status(suart));
+
+ printf("expecting osmo_soft_uart_tx_ubits() to yeild nothing\n");
+ rc = osmo_soft_uart_tx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == -EAGAIN);
+
+ printf("expecting osmo_soft_uart_rx_ubits() to consume nothing\n");
+ rc = osmo_soft_uart_rx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == -EAGAIN);
+
+ /* b) DSR is asserted, expect only the transmitter to work */
+ printf("======== %s(): asserting DSR\n", __func__);
+ osmo_soft_uart_set_status_line(suart, OSMO_SUART_STATUS_F_DSR, true);
+
+ printf("expecting osmo_soft_uart_tx_ubits() to yeild some data\n");
+ rc = osmo_soft_uart_tx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == 0);
+ printf("%s\n", osmo_ubit_dump(&tx_buf[0], sizeof(tx_buf)));
+
+ printf("expecting osmo_soft_uart_rx_ubits() to consume nothing\n");
+ rc = osmo_soft_uart_rx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == -EAGAIN);
+
+ /* c) both DTR and DSR are asserted, expect both Rx and Tx to work */
+ printf("======== %s(): asserting DTR\n", __func__);
+ osmo_soft_uart_set_status_line(suart, OSMO_SUART_STATUS_F_DTR, true);
+
+ printf("expecting osmo_soft_uart_tx_ubits() to yeild some data\n");
+ rc = osmo_soft_uart_tx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == 0);
+ printf("%s\n", osmo_ubit_dump(&tx_buf[0], sizeof(tx_buf)));
+
+ printf("expecting osmo_soft_uart_rx_ubits() to consume some data\n");
+ rc = osmo_soft_uart_rx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == 0);
+ osmo_soft_uart_flush_rx(suart);
+
+ osmo_soft_uart_free(suart);
+}
+
+static void test_flow_control_rts_cts(void)
+{
+ struct osmo_soft_uart_cfg cfg;
+ struct osmo_soft_uart *suart;
+ ubit_t tx_buf[32];
+ int rc;
+
+ g_tx_cb_cfg.data = (void *)"\x42\x42\x42";
+ g_tx_cb_cfg.data_len = 3;
+
+ cfg = suart_test_default_cfg;
+ cfg.flow_ctrl_mode = OSMO_SUART_FLOW_CTRL_M_RTS_CTS;
+
+ suart = osmo_soft_uart_alloc(NULL, __func__, &cfg);
+ OSMO_ASSERT(suart != NULL);
+
+ osmo_soft_uart_set_tx(suart, true);
+ osmo_soft_uart_set_rx(suart, true);
+
+ /* a) expect the initial status to be 0 (all lines de-asserted) */
+ printf("======== %s(): initial status=0x%08x\n",
+ __func__, osmo_soft_uart_get_status(suart));
+
+ printf("expecting osmo_soft_uart_tx_ubits() to yeild nothing\n");
+ rc = osmo_soft_uart_tx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == -EAGAIN);
+
+ printf("expecting osmo_soft_uart_rx_ubits() to consume nothing\n");
+ rc = osmo_soft_uart_rx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == -EAGAIN);
+
+ /* b) CTS is asserted, expect only the transmitter to work */
+ printf("======== %s(): asserting CTS\n", __func__);
+ osmo_soft_uart_set_status_line(suart, OSMO_SUART_STATUS_F_CTS, true);
+
+ printf("expecting osmo_soft_uart_tx_ubits() to yeild some data\n");
+ rc = osmo_soft_uart_tx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == 0);
+ printf("%s\n", osmo_ubit_dump(&tx_buf[0], sizeof(tx_buf)));
+
+ printf("expecting osmo_soft_uart_rx_ubits() to consume nothing\n");
+ rc = osmo_soft_uart_rx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == -EAGAIN);
+
+ /* c) both RTS/RTR and CTS are asserted, expect both Rx and Tx to work */
+ printf("======== %s(): asserting RTS/RTR\n", __func__);
+ osmo_soft_uart_set_status_line(suart, OSMO_SUART_STATUS_F_RTS_RTR, true);
+
+ printf("expecting osmo_soft_uart_tx_ubits() to yeild some data\n");
+ rc = osmo_soft_uart_tx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == 0);
+ printf("%s\n", osmo_ubit_dump(&tx_buf[0], sizeof(tx_buf)));
+
+ printf("expecting osmo_soft_uart_rx_ubits() to consume some data\n");
+ rc = osmo_soft_uart_rx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == 0);
+ osmo_soft_uart_flush_rx(suart);
+
+ osmo_soft_uart_free(suart);
+}
+
int main(int argc, char **argv)
{
test_rx();
@@ -354,5 +516,10 @@
test_tx_rx_pull_n(4);
test_tx_rx_pull_n(8);
+ /* test flow control */
+ test_modem_status();
+ test_flow_control_dtr_dsr();
+ test_flow_control_rts_cts();
+
return 0;
}
diff --git a/tests/soft_uart/soft_uart_test.ok b/tests/soft_uart/soft_uart_test.ok
index 9ce3bc8..37c342f 100644
--- a/tests/soft_uart/soft_uart_test.ok
+++ b/tests/soft_uart/soft_uart_test.ok
@@ -200,3 +200,44 @@
01010101011111110101010101111111
======== test_tx_rx_pull_n(): feeding 32 bits into the receiver
suart_rx_cb(flags=00): 55 55
+======== test_modem_status(): initial status=0x00000000
+de-asserting DCD, which was not asserted
+asserting both RI and DCD, expecting the callback to be called twice
+suart_status_change_cb(status=0x00000008)
+suart_status_change_cb(status=0x0000000a)
+de-asserting RI, expecting the callback to be called
+suart_status_change_cb(status=0x00000002)
+resetting to 0x00, expecting the callback to be called
+suart_status_change_cb(status=0x00000000)
+======== test_flow_control_dtr_dsr(): initial status=0x00000000
+expecting osmo_soft_uart_tx_ubits() to yeild nothing
+expecting osmo_soft_uart_rx_ubits() to consume nothing
+======== test_flow_control_dtr_dsr(): asserting DSR
+suart_status_change_cb(status=0x00000004)
+expecting osmo_soft_uart_tx_ubits() to yeild some data
+suart_tx_cb(len=3/3): 42 42 42
+00100001010010000101001000010111
+expecting osmo_soft_uart_rx_ubits() to consume nothing
+======== test_flow_control_dtr_dsr(): asserting DTR
+suart_status_change_cb(status=0x00000005)
+expecting osmo_soft_uart_tx_ubits() to yeild some data
+suart_tx_cb(len=3/3): 42 42 42
+00100001010010000101001000010111
+expecting osmo_soft_uart_rx_ubits() to consume some data
+suart_rx_cb(flags=00): 42 42 42
+======== test_flow_control_rts_cts(): initial status=0x00000000
+expecting osmo_soft_uart_tx_ubits() to yeild nothing
+expecting osmo_soft_uart_rx_ubits() to consume nothing
+======== test_flow_control_rts_cts(): asserting CTS
+suart_status_change_cb(status=0x00000020)
+expecting osmo_soft_uart_tx_ubits() to yeild some data
+suart_tx_cb(len=3/3): 42 42 42
+00100001010010000101001000010111
+expecting osmo_soft_uart_rx_ubits() to consume nothing
+======== test_flow_control_rts_cts(): asserting RTS/RTR
+suart_status_change_cb(status=0x00000030)
+expecting osmo_soft_uart_tx_ubits() to yeild some data
+suart_tx_cb(len=3/3): 42 42 42
+00100001010010000101001000010111
+expecting osmo_soft_uart_rx_ubits() to consume some data
+suart_rx_cb(flags=00): 42 42 42
--
To view, visit
https://gerrit.osmocom.org/c/libosmocore/+/35172?usp=email
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings
Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-Change-Id: I26b93ce76f2f6b6fbf017f2684312007db3c6d48
Gerrit-Change-Number: 35172
Gerrit-PatchSet: 1
Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de>
Gerrit-MessageType: newchange