[PATCH 6/7] libchip/serial: Task driven mode for NS16550

Sebastian Huber sebastian.huber at embedded-brains.de
Tue Oct 7 14:36:30 UTC 2014


---
 c/src/libchip/serial/ns16550-context.c | 130 ++++++++++++++++++++++++++++++---
 c/src/libchip/serial/ns16550.h         |   2 +
 2 files changed, 120 insertions(+), 12 deletions(-)

diff --git a/c/src/libchip/serial/ns16550-context.c b/c/src/libchip/serial/ns16550-context.c
index 0b5d1b05..00ad89c 100644
--- a/c/src/libchip/serial/ns16550-context.c
+++ b/c/src/libchip/serial/ns16550-context.c
@@ -98,6 +98,26 @@ static void ns16550_enable_interrupts(
   (*ctx->set_reg)(ctx->port, NS16550_INTERRUPT_ENABLE, mask);
 }
 
+static void ns16550_clear_and_set_interrupts(
+  ns16550_context *ctx,
+  uint8_t          clear,
+  uint8_t          set
+)
+{
+  rtems_interrupt_lock_context lock_context;
+  ns16550_get_reg get_reg = ctx->get_reg;
+  ns16550_set_reg set_reg = ctx->set_reg;
+  uintptr_t port = ctx->port;
+  uint8_t val;
+
+  rtems_termios_device_lock_acquire(&ctx->base, &lock_context);
+  val = (*get_reg)(port, NS16550_INTERRUPT_ENABLE);
+  val &= ~clear;
+  val |= set;
+  (*set_reg)(port, NS16550_INTERRUPT_ENABLE, val);
+  rtems_termios_device_lock_release(&ctx->base, &lock_context);
+}
+
 /*
  *  ns16550_probe
  */
@@ -180,8 +200,6 @@ static size_t ns16550_write_to_fifo(
   return out;
 }
 
-#if defined(BSP_FEATURE_IRQ_EXTENSION) || defined(BSP_FEATURE_IRQ_LEGACY)
-
 /**
  * @brief Process interrupt.
  */
@@ -225,7 +243,58 @@ static void ns16550_isr(void *arg)
     }
   } while ((get( port, NS16550_INTERRUPT_ID) & SP_IID_0) == 0);
 }
-#endif
+
+static void ns16550_isr_task(void *arg)
+{
+  rtems_termios_tty *tty = arg;
+  ns16550_context *ctx = rtems_termios_get_device_context(tty);
+  uint8_t status = (*ctx->get_reg)(ctx->port, NS16550_LINE_STATUS);
+
+  if ((status & SP_LSR_RDY) != 0) {
+    ns16550_clear_and_set_interrupts(ctx, SP_INT_RX_ENABLE, 0);
+    rtems_termios_rxirq_occured(tty);
+  }
+
+  if (ctx->out_total > 0 && (status & SP_LSR_THOLD) != 0) {
+    size_t current = ctx->out_current;
+
+    ctx->out_buf += current;
+    ctx->out_remaining -= current;
+
+    if (ctx->out_remaining > 0) {
+      ctx->out_current =
+        ns16550_write_to_fifo(ctx, ctx->out_buf, ctx->out_remaining);
+    } else {
+      size_t done = ctx->out_total;
+
+      ctx->out_total = 0;
+      ns16550_clear_and_set_interrupts(ctx, SP_INT_TX_ENABLE, 0);
+      rtems_termios_dequeue_characters(tty, done);
+    }
+  }
+}
+
+static int ns16550_read_task(rtems_termios_device_context *base)
+{
+  ns16550_context *ctx = (ns16550_context *) base;
+  uint32_t port = ctx->port;
+  ns16550_get_reg get = ctx->get_reg;
+  char buf[SP_FIFO_SIZE];
+  int i;
+
+  for (i = 0; i < SP_FIFO_SIZE; ++i) {
+    if ((get(port, NS16550_LINE_STATUS) & SP_LSR_RDY) != 0) {
+      buf[i] = (char) get(port, NS16550_RECEIVE_BUFFER);
+    } else {
+      break;
+    }
+  }
+
+  rtems_termios_enqueue_raw_characters(ctx->tty, buf, i);
+  ns16550_clear_and_set_interrupts(ctx, 0, SP_INT_RX_ENABLE);
+
+  return -1;
+}
 
 /*
  *  ns16550_initialize_interrupts
@@ -234,7 +303,8 @@ static void ns16550_isr(void *arg)
  */
 static void ns16550_initialize_interrupts(
   struct rtems_termios_tty *tty,
-  ns16550_context          *ctx
+  ns16550_context          *ctx,
+  void                    (*isr)(void *)
 )
 {
   #ifdef BSP_FEATURE_IRQ_EXTENSION
@@ -244,7 +314,7 @@ static void ns16550_initialize_interrupts(
         ctx->irq,
         "NS16550",
         RTEMS_INTERRUPT_SHARED,
-        ns16550_isr,
+        isr,
         tty
       );
       if (sc != RTEMS_SUCCESSFUL) {
@@ -259,7 +329,7 @@ static void ns16550_initialize_interrupts(
       #ifdef BSP_FEATURE_IRQ_LEGACY_SHARED_HANDLER_SUPPORT
         rtems_irq_connect_data cd = {
           ctx->irq,
-          ns16550_isr,
+          isr,
           tty,
           NULL,
           NULL,
@@ -270,7 +340,7 @@ static void ns16550_initialize_interrupts(
       #else
         rtems_irq_connect_data cd = {
           ctx->irq,
-          ns16550_isr,
+          isr,
           tty,
           NULL,
           NULL,
@@ -300,11 +370,16 @@ static bool ns16550_open(
 {
   ns16550_context *ctx = (ns16550_context *) base;
 
+  ctx->tty = tty;
+
   /* Set initial baud */
   rtems_termios_set_initial_baud(tty, ctx->initial_baud);
 
   if (tty->handler.mode == TERMIOS_IRQ_DRIVEN) {
-    ns16550_initialize_interrupts(tty, ctx);
+    ns16550_initialize_interrupts(tty, ctx, ns16550_isr);
+    ns16550_enable_interrupts(ctx, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
+  } else if (tty->handler.mode == TERMIOS_TASK_DRIVEN) {
+    ns16550_initialize_interrupts(tty, ctx, ns16550_isr_task);
     ns16550_enable_interrupts(ctx, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
   }
 
@@ -313,14 +388,15 @@ static bool ns16550_open(
 
 static void ns16550_cleanup_interrupts(
   struct rtems_termios_tty *tty,
-  ns16550_context          *ctx
+  ns16550_context          *ctx,
+  void                    (*isr)(void *)
 )
 {
   #if defined(BSP_FEATURE_IRQ_EXTENSION)
     rtems_status_code sc = RTEMS_SUCCESSFUL;
     sc = rtems_interrupt_handler_remove(
       ctx->irq,
-      ns16550_isr,
+      isr,
       tty
     );
     if (sc != RTEMS_SUCCESSFUL) {
@@ -332,7 +408,7 @@ static void ns16550_cleanup_interrupts(
     int rv = 0;
     rtems_irq_connect_data cd = {
       .name = ctx->irq,
-      .hdl = ns16550_isr,
+      .hdl = isr,
       .handle = tty
     };
     rv = BSP_remove_rtems_irq_handler(&cd);
@@ -359,7 +435,9 @@ static void ns16550_close(
   ns16550_enable_interrupts(ctx, NS16550_DISABLE_ALL_INTR);
 
   if (tty->handler.mode == TERMIOS_IRQ_DRIVEN) {
-    ns16550_cleanup_interrupts(tty, ctx);
+    ns16550_cleanup_interrupts(tty, ctx, ns16550_isr);
+  } else if (tty->handler.mode == TERMIOS_TASK_DRIVEN) {
+    ns16550_cleanup_interrupts(tty, ctx, ns16550_isr_task);
   }
 }
 
@@ -614,6 +692,25 @@ static void ns16550_write_support_int(
   }
 }
 
+static void ns16550_write_support_task(
+  rtems_termios_device_context *base,
+  const char                   *buf,
+  size_t                        len
+)
+{
+  ns16550_context *ctx = (ns16550_context *) base;
+
+  ctx->out_total = len;
+
+  if (len > 0) {
+    ctx->out_remaining = len;
+    ctx->out_buf = buf;
+    ctx->out_current = ns16550_write_to_fifo(ctx, buf, len);
+
+    ns16550_clear_and_set_interrupts(ctx, 0, SP_INT_TX_ENABLE);
+  }
+}
+
 /*
  *  ns16550_write_support_polled
  *
@@ -694,3 +791,12 @@ const rtems_termios_device_handler ns16550_handler_polled = {
   .set_attributes = ns16550_set_attributes,
   .mode = TERMIOS_POLLED
 };
+
+const rtems_termios_device_handler ns16550_handler_task = {
+  .first_open = ns16550_open,
+  .last_close = ns16550_close,
+  .poll_read = ns16550_read_task,
+  .write = ns16550_write_support_task,
+  .set_attributes = ns16550_set_attributes,
+  .mode = TERMIOS_TASK_DRIVEN
+};
diff --git a/c/src/libchip/serial/ns16550.h b/c/src/libchip/serial/ns16550.h
index 0b05fe3..19ac3f1 100644
--- a/c/src/libchip/serial/ns16550.h
+++ b/c/src/libchip/serial/ns16550.h
@@ -74,10 +74,12 @@ typedef struct {
   size_t out_remaining;
   size_t out_current;
   const char *out_buf;
+  rtems_termios_tty *tty;
 } ns16550_context;
 
 extern const rtems_termios_device_handler ns16550_handler_interrupt;
 extern const rtems_termios_device_handler ns16550_handler_polled;
+extern const rtems_termios_device_handler ns16550_handler_task;
 
 extern const rtems_termios_device_flow ns16550_flow_rtscts;
 extern const rtems_termios_device_flow ns16550_flow_dtrcts;
-- 
1.8.4.5




More information about the devel mailing list