[PATCH 1/3] bsp/leon3: Fix interrupt-driven console driver
Sebastian Huber
sebastian.huber at embedded-brains.de
Tue Nov 26 08:19:34 UTC 2013
---
c/src/lib/libbsp/sparc/leon3/console/console.c | 170 ++++++++++++++----------
1 files changed, 98 insertions(+), 72 deletions(-)
diff --git a/c/src/lib/libbsp/sparc/leon3/console/console.c b/c/src/lib/libbsp/sparc/leon3/console/console.c
index 1b1882d..0b30369 100644
--- a/c/src/lib/libbsp/sparc/leon3/console/console.c
+++ b/c/src/lib/libbsp/sparc/leon3/console/console.c
@@ -85,7 +85,7 @@ static int uarts = 0;
#if CONSOLE_USE_INTERRUPTS
/* Handle UART interrupts */
-void console_isr(void *arg)
+static void leon3_console_isr(void *arg)
{
struct apbuart_priv *uart = arg;
unsigned int status;
@@ -100,13 +100,10 @@ void console_isr(void *arg)
rtems_termios_enqueue_raw_characters(uart->cookie, &data, 1);
}
- if (status & LEON_REG_UART_STATUS_THE) {
- /* Sent the one char, we disable TX interrupts */
- uart->regs->ctrl &= ~LEON_REG_UART_CTRL_TI;
-
- /* Tell close that we sent everything */
- uart->sending = 0;
-
+ if (
+ (status & LEON_REG_UART_STATUS_THE)
+ && (uart->regs->ctrl & LEON_REG_UART_CTRL_TI) != 0
+ ) {
/* write_interrupt will get called from this function */
rtems_termios_dequeue_characters(uart->cookie, 1);
}
@@ -116,27 +113,34 @@ void console_isr(void *arg)
* Console Termios Write-Buffer Support Entry Point
*
*/
-int console_write_interrupt(int minor, const char *buf, int len)
+static int leon3_console_write_support(int minor, const char *buf, size_t len)
{
- if (len > 0) {
- struct apbuart_priv *uart;
-
- if (minor == 0)
- uart = &apbuarts[syscon_uart_index];
- else
- uart = &apbuarts[minor - 1];
+ struct apbuart_priv *uart;
+ int sending;
- /* Remember what position in buffer */
+ if (minor == 0)
+ uart = &apbuarts[syscon_uart_index];
+ else
+ uart = &apbuarts[minor - 1];
- /* Enable TX interrupt */
+ if (len > 0) {
+ /* Enable TX interrupt (interrupt is edge-triggered) */
uart->regs->ctrl |= LEON_REG_UART_CTRL_TI;
/* start UART TX, this will result in an interrupt when done */
uart->regs->data = *buf;
- uart->sending = 1;
+ sending = 1;
+ } else {
+ /* No more to send, disable TX interrupts */
+ uart->regs->ctrl &= ~LEON_REG_UART_CTRL_TI;
+
+ /* Tell close that we sent everything */
+ sending = 0;
}
+ uart->sending = sending;
+
return 0;
}
@@ -212,6 +216,11 @@ int console_set_attributes(int minor, const struct termios *t)
else
uart = &apbuarts[minor - 1];
+ /*
+ * FIXME: This read-modify-write sequence is broken since interrupts may
+ * interfere.
+ */
+
/* Read out current value */
ctrl = uart->regs->ctrl;
@@ -351,49 +360,19 @@ rtems_device_driver console_initialize(
return RTEMS_SUCCESSFUL;
}
-rtems_device_driver console_open(
- rtems_device_major_number major,
- rtems_device_minor_number minor,
- void * arg
+#if CONSOLE_USE_INTERRUPTS
+static struct rtems_termios_tty *leon3_console_get_tty(
+ rtems_libio_open_close_args_t *args
)
{
- rtems_status_code sc;
- struct apbuart_priv *uart;
-#if CONSOLE_USE_INTERRUPTS
- rtems_libio_open_close_args_t *priv = arg;
-
- /* Interrupt mode routines */
- static const rtems_termios_callbacks Callbacks = {
- NULL, /* firstOpen */
- NULL, /* lastClose */
- NULL, /* pollRead */
- console_write_interrupt, /* write */
- console_set_attributes, /* setAttributes */
- NULL, /* stopRemoteTx */
- NULL, /* startRemoteTx */
- 1 /* outputUsesInterrupts */
- };
-#else
- /* Polling mode routines */
- static const rtems_termios_callbacks Callbacks = {
- NULL, /* firstOpen */
- NULL, /* lastClose */
- console_pollRead, /* pollRead */
- console_write_polled, /* write */
- console_set_attributes, /* setAttributes */
- NULL, /* stopRemoteTx */
- NULL, /* startRemoteTx */
- 0 /* outputUsesInterrupts */
- };
+ return args->iop->data1;
+}
#endif
- assert(minor <= uarts);
- if (minor > uarts || minor == (syscon_uart_index + 1))
- return RTEMS_INVALID_NUMBER;
-
- sc = rtems_termios_open(major, minor, arg, &Callbacks);
- if (sc != RTEMS_SUCCESSFUL)
- return sc;
+static int leon3_console_first_open(int major, int minor, void *arg)
+{
+ struct apbuart_priv *uart;
+ rtems_status_code sc;
if (minor == 0)
uart = &apbuarts[syscon_uart_index];
@@ -401,17 +380,15 @@ rtems_device_driver console_open(
uart = &apbuarts[minor - 1];
#if CONSOLE_USE_INTERRUPTS
- if (priv && priv->iop)
- uart->cookie = priv->iop->data1;
- else
- uart->cookie = NULL;
+ uart->cookie = leon3_console_get_tty(arg);
/* Register Interrupt handler */
sc = rtems_interrupt_handler_install(uart->irq, "console",
- RTEMS_INTERRUPT_SHARED, console_isr,
+ RTEMS_INTERRUPT_SHARED,
+ leon3_console_isr,
uart);
if (sc != RTEMS_SUCCESSFUL)
- return sc;
+ return -1;
uart->sending = 0;
/* Enable Receiver and transmitter and Turn on RX interrupts */
@@ -423,17 +400,15 @@ rtems_device_driver console_open(
#endif
uart->regs->status = 0;
- return RTEMS_SUCCESSFUL;
+ return 0;
}
-rtems_device_driver console_close(
- rtems_device_major_number major,
- rtems_device_minor_number minor,
- void * arg
-)
-{
#if CONSOLE_USE_INTERRUPTS
+static int leon3_console_last_close(int major, int minor, void *arg)
+{
+ struct rtems_termios_tty *tty = leon3_console_get_tty(arg);
struct apbuart_priv *uart;
+ rtems_interrupt_level level;
if (minor == 0)
uart = &apbuarts[syscon_uart_index];
@@ -441,7 +416,9 @@ rtems_device_driver console_close(
uart = &apbuarts[minor - 1];
/* Turn off RX interrupts */
+ rtems_termios_interrupt_lock_acquire(tty, level);
uart->regs->ctrl &= ~(LEON_REG_UART_CTRL_RI);
+ rtems_termios_interrupt_lock_release(tty, level);
/**** Flush device ****/
while (uart->sending) {
@@ -449,8 +426,57 @@ rtems_device_driver console_close(
}
/* uninstall ISR */
- rtems_interrupt_handler_remove(uart->irq, console_isr, uart);
+ rtems_interrupt_handler_remove(uart->irq, leon3_console_isr, uart);
+
+ return 0;
+}
#endif
+
+rtems_device_driver console_open(
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void * arg
+)
+{
+#if CONSOLE_USE_INTERRUPTS
+ /* Interrupt mode routines */
+ static const rtems_termios_callbacks Callbacks = {
+ leon3_console_first_open, /* firstOpen */
+ leon3_console_last_close, /* lastClose */
+ NULL, /* pollRead */
+ leon3_console_write_support, /* write */
+ console_set_attributes, /* setAttributes */
+ NULL, /* stopRemoteTx */
+ NULL, /* startRemoteTx */
+ 1 /* outputUsesInterrupts */
+ };
+#else
+ /* Polling mode routines */
+ static const rtems_termios_callbacks Callbacks = {
+ leon3_console_first_open, /* firstOpen */
+ NULL, /* lastClose */
+ console_pollRead, /* pollRead */
+ console_write_polled, /* write */
+ console_set_attributes, /* setAttributes */
+ NULL, /* stopRemoteTx */
+ NULL, /* startRemoteTx */
+ 0 /* outputUsesInterrupts */
+ };
+#endif
+
+ assert(minor <= uarts);
+ if (minor > uarts || minor == (syscon_uart_index + 1))
+ return RTEMS_INVALID_NUMBER;
+
+ return rtems_termios_open(major, minor, arg, &Callbacks);
+}
+
+rtems_device_driver console_close(
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void * arg
+)
+{
return rtems_termios_close(arg);
}
--
1.7.7
More information about the devel
mailing list