|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH v8 11/26] emul/ns16x50: implement THR register
From: Denis Mukhin <dmukhin@xxxxxxxx>
Add THR register emulation to the I/O port handlder.
Add TX FIFO management code since THR depends on TX FIFO.
TX FIFOs is emulated by using xencons_interface which conveniently
provides primitives for buffer management and later can be used for
inter-domain communication similarly to vpl011.
Account for DLL == 0: in this case, disable transmitter.
Add UART_IIR_THR interrupt reason handling since it depends on THR
register access.
Signed-off-by: Denis Mukhin <dmukhin@xxxxxxxx>
---
xen/common/emul/vuart/ns16x50.c | 121 +++++++++++++++++++++++++++++++-
1 file changed, 120 insertions(+), 1 deletion(-)
diff --git a/xen/common/emul/vuart/ns16x50.c b/xen/common/emul/vuart/ns16x50.c
index d04caf2baf2c..8d3845a8a4cd 100644
--- a/xen/common/emul/vuart/ns16x50.c
+++ b/xen/common/emul/vuart/ns16x50.c
@@ -98,6 +98,7 @@ struct vuart_ns16x50 {
spinlock_t lock; /* Protection */
struct xencons_interface cons; /* Emulated RX/TX FIFOs */
bool irq_asserted; /* IRQ line status */
+ bool thr_pending; /* THR interrupt reason status */
};
static inline void ns16x50_lock_init(struct vuart_ns16x50 *vdev)
@@ -185,6 +186,72 @@ static int ns16x50_fifo_rx_putchar(struct vuart_ns16x50
*vdev, char c)
return rc;
}
+static bool ns16x50_fifo_tx_full(const struct vuart_ns16x50 *vdev)
+{
+ const struct xencons_interface *cons = &vdev->cons;
+
+ return cons->out_prod - cons->out_cons == ARRAY_SIZE(cons->out);
+}
+
+static void ns16x50_fifo_tx_reset(struct vuart_ns16x50 *vdev)
+{
+ struct xencons_interface *cons = &vdev->cons;
+
+ cons->out_cons = cons->out_prod;
+}
+
+/*
+ * Flush cached output to Xen console.
+ */
+static void ns16x50_fifo_tx_flush(struct vuart_ns16x50 *vdev)
+{
+ struct xencons_interface *cons = &vdev->cons;
+ struct domain *d = vdev->owner;
+ XENCONS_RING_IDX i, j, n, m, len = cons->out_prod - cons->out_cons;
+
+ ASSERT(len <= ARRAY_SIZE(cons->out));
+ if ( !len )
+ return;
+
+ /* Tail of the FIFO */
+ i = MASK_XENCONS_IDX(cons->out_cons, cons->out);
+ n = min_t(XENCONS_RING_IDX, len, ARRAY_SIZE(cons->out) - i);
+
+ /* Head of the FIFO */
+ j = 0;
+ m = len - n;
+
+ /* Linearize the FIFO */
+ if ( m )
+ guest_printk(d, guest_prefix "%.*s%.*s",
+ n, &cons->out[i],
+ m, &cons->out[j]);
+ else
+ guest_printk(d, guest_prefix "%.*s", n, &cons->out[i]);
+
+ cons->out_cons += len;
+}
+
+/*
+ * Accumulate guest OS output before sending to Xen console.
+ */
+static void ns16x50_fifo_tx_putchar(struct vuart_ns16x50 *vdev, char ch)
+{
+ struct xencons_interface *cons = &vdev->cons;
+
+ if ( !is_console_printable(ch) )
+ return;
+
+ if ( !ns16x50_fifo_tx_full(vdev) )
+ {
+ cons->out[MASK_XENCONS_IDX(cons->out_prod, cons->out)] = ch;
+ cons->out_prod++;
+ }
+
+ if ( ch == '\n' || ch == '\0' || ns16x50_fifo_tx_full(vdev) )
+ ns16x50_fifo_tx_flush(vdev);
+}
+
static bool ns16x50_is_tranceiver_enabled(const struct vuart_ns16x50 *vdev)
{
BUG_ON(vdev->regs[UART_LCR] & UART_LCR_DLAB);
@@ -205,7 +272,7 @@ static bool cf_check ns16x50_iir_check_rda(const struct
vuart_ns16x50 *vdev)
static bool cf_check ns16x50_iir_check_thr(const struct vuart_ns16x50 *vdev)
{
- return false;
+ return vdev->thr_pending;
}
static bool cf_check ns16x50_iir_check_msi(const struct vuart_ns16x50 *vdev)
@@ -316,6 +383,38 @@ static int ns16x50_io_write8(
}
/* DLAB=0 */
+ if ( !ns16x50_is_tranceiver_enabled(vdev) )
+ break;
+
+ /*
+ * THR is no longer empty.
+ */
+ vdev->thr_pending = false;
+ regs[UART_LSR] &= ~(UART_LSR_THRE | UART_LSR_TEMT);
+
+ /*
+ * Drop the IRQ line before completing this immediate
+ * transmission, if THRE was its only reason.
+ */
+ ns16x50_irq_check(vdev);
+
+ if ( regs[UART_MCR] & UART_MCR_LOOP )
+ {
+ if ( ns16x50_fifo_rx_putchar(vdev, val) )
+ regs[UART_LSR] |= UART_LSR_OE;
+
+ regs[UART_LSR] |= UART_LSR_DR;
+ }
+ else
+ ns16x50_fifo_tx_putchar(vdev, val);
+
+ /*
+ * Backend transmission is synchronous in this emulator,
+ * so THR is immediately empty again.
+ */
+ regs[UART_LSR] |= UART_LSR_THRE | UART_LSR_TEMT;
+ vdev->thr_pending = true;
+
break;
case UART_IER: /* DLAB=0 --> UART_IER, DLAB=1 --> UART_DLM */
@@ -327,7 +426,13 @@ static int ns16x50_io_write8(
/* DLAB=0 */
else
{
+ uint8_t old = regs[UART_IER];
+
regs[UART_IER] = val & UART_IER_ALL_INTR;
+
+ if ( (old ^ regs[UART_IER]) & UART_IER_ETHREI )
+ vdev->thr_pending = ( (regs[UART_IER] & UART_IER_ETHREI) &&
+ (regs[UART_LSR] & UART_LSR_THRE) );
}
break;
@@ -497,6 +602,14 @@ static int ns16x50_io_read8(
case UART_IIR: /* RO */
val = ns16x50_iir_get(vdev);
+
+ /*
+ * Reading IIR when THRE is the reported reason clears the
+ * THRE interrupt latch.
+ */
+ if ( (val & UART_IIR_IMA) == UART_IIR_THR )
+ vdev->thr_pending = false;
+
break;
case UART_LCR:
@@ -679,6 +792,7 @@ static void ns16x50_reset(struct vuart_ns16x50 *vdev)
memset(vdev->regs, 0, sizeof(vdev->regs));
vdev->irq_asserted = false;
+ vdev->thr_pending = false;
vdev->regs[UART_LSR] = UART_LSR_THRE | UART_LSR_TEMT;
vdev->regs[UART_MSR] = UART_MSR_DCD |
@@ -722,8 +836,13 @@ static int ns16x50_init(void *arg)
static void cf_check ns16x50_deinit(void *arg)
{
struct vuart_ns16x50 *vdev = arg;
+ unsigned long flags;
ASSERT(vdev);
+
+ ns16x50_lock(vdev, &flags);
+ ns16x50_fifo_tx_flush(vdev);
+ ns16x50_unlock(vdev, &flags);
}
static void * cf_check ns16x50_alloc(struct domain *d, const struct vuart_info
*info)
--
2.56.0
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |