[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