soft_uart: implement modem status lines and flow control
Change-Id: I26b93ce76f2f6b6fbf017f2684312007db3c6d48
Related: OS#4396
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 5750282..6cc8ab4 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_NONE,
};
/*************************************************************************
@@ -273,11 +275,25 @@
return tx_bit;
}
+/* pull pending bits out of the UART */
+static size_t suart_tx_pending(struct osmo_soft_uart *suart, ubit_t *ubits, size_t n_ubits)
+{
+ size_t i;
+
+ for (i = 0; i < n_ubits; i++) {
+ if (suart->tx.flow_state == SUART_FLOW_ST_IDLE)
+ break;
+ ubits[i] = suart_tx_bit(suart, NULL);
+ }
+
+ return i;
+}
+
/*! Pull a number of unpacked bits out of the soft-UART transmitter.
* \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 number of bits pulled; negative on error.
+ * \returns number of bits pulled (may be less than n_ubits); negative on error.
* -EAGAIN indicates that the transmitter is disabled. */
int osmo_soft_uart_tx_ubits(struct osmo_soft_uart *suart, ubit_t *ubits, size_t n_ubits)
{
@@ -291,6 +307,24 @@
if (!suart->tx.running)
return -EAGAIN;
+ switch (suart->cfg.flow_ctrl_mode) {
+ case OSMO_SUART_FLOW_CTRL_DTR_DSR:
+ /* if DSR is de-asserted, Tx pending bits and suspend */
+ if (~suart->status & OSMO_SUART_STATUS_F_DSR)
+ return suart_tx_pending(suart, ubits, n_ubits);
+ /* else: keep transmitting as usual */
+ break;
+ case OSMO_SUART_FLOW_CTRL_RTS_CTS:
+ /* if CTS is de-asserted, Tx pending bits and suspend */
+ if (~suart->status & OSMO_SUART_STATUS_F_CTS)
+ return suart_tx_pending(suart, ubits, n_ubits);
+ /* else: keep transmitting as usual */
+ break;
+ case OSMO_SUART_FLOW_CTRL_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)
@@ -321,16 +355,49 @@
return n_ubits;
}
-/*! 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)
+{
+ unsigned int status = suart->status;
+
+ if (active) /* assert the given line */
+ status |= line;
+ else /* de-assert the given line */
+ status &= ~line;
+
+ osmo_soft_uart_set_status(suart, status);
+}
+
/*************************************************************************
* Management / Initialization