[PATCH] rtems_termios_puts: Copy and write more than one char at once

2014-08-07 Thread Kolja Waschk
Hi,

I'd like to send some work upstream that I've made for a Blackfin BF537 based 
device. The following patch was necessary for a DMA-based high speed UART
driver(*) for Blackfin - it allows to start transmission on serial interfaces 
with
more than just one char at a time.

Notes:

This already reworked patch obsoletes the one attached to
https://www.rtems.org/bugzilla/show_bug.cgi?id=2185

I'm unsure if the amount of bytes to sent, given as an argument to the final
call of (*tty->handler.write)(), is properly chosen. It was always fixed to one 
before. 

(*)The UART driver itself is not yet ready for upstream, because it lives still
in my BSP only and has some specific mechanisms which are not really suited
for general use (such as using an extra GPIO for end-of-transfer signalling).

Kolja


Kolja Waschk (1):
  rtems_termios_puts: Copy and write more than one char at once

 cpukit/libcsupport/src/termios.c | 74 +++-
 1 file changed, 51 insertions(+), 23 deletions(-)

-- 
1.9.1

___
devel mailing list
devel@rtems.org
http://lists.rtems.org/mailman/listinfo/devel


[PATCH] rtems_termios_puts: Copy and write more than one char at once

2014-08-07 Thread Kolja Waschk
---
 cpukit/libcsupport/src/termios.c | 74 +++-
 1 file changed, 51 insertions(+), 23 deletions(-)

diff --git a/cpukit/libcsupport/src/termios.c b/cpukit/libcsupport/src/termios.c
index 2448ea1..d32ab74 100644
--- a/cpukit/libcsupport/src/termios.c
+++ b/cpukit/libcsupport/src/termios.c
@@ -879,7 +879,7 @@ rtems_termios_ioctl (void *arg)
   tty->rawInBufSemaphoreTimeout = RTEMS_NO_TIMEOUT;
   tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
 } else {
-  tty->vtimeTicks = tty->termios.c_cc[VTIME] * 
+  tty->vtimeTicks = tty->termios.c_cc[VTIME] *
 rtems_clock_get_ticks_per_second() / 10;
   if (tty->termios.c_cc[VTIME]) {
 tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
@@ -984,26 +984,19 @@ rtems_termios_puts (
   unsigned int newHead;
   rtems_interrupt_lock_context lock_context;
   rtems_status_code sc;
+  size_t partlen;
 
   if (tty->handler.mode == TERMIOS_POLLED) {
 (*tty->handler.write)(tty, buf, len);
 return;
   }
-  newHead = tty->rawOutBuf.Head;
+
   while (len) {
-/*
- * Performance improvement could be made here.
- * Copy multiple bytes to raw buffer:
- * if (len > 1) && (space to buffer end, or tail > 1)
- *  ncopy = MIN (len, space to buffer end or tail)
- *  memcpy (raw buffer, buf, ncopy)
- *  buf += ncopy
- *  len -= ncopy
- *
- * To minimize latency, the memcpy should be done
- * with interrupts enabled.
- */
-newHead = (newHead + 1) % tty->rawOutBuf.Size;
+/* Check space for at least one char */
+newHead = tty->rawOutBuf.Head + 1;
+if (newHead >= tty->rawOutBuf.Size)
+  newHead -= tty->rawOutBuf.Size;
+
 rtems_termios_interrupt_lock_acquire (tty, &lock_context);
 while (newHead == tty->rawOutBuf.Tail) {
   tty->rawOutBufState = rob_wait;
@@ -1014,21 +1007,56 @@ rtems_termios_puts (
 rtems_fatal_error_occurred (sc);
   rtems_termios_interrupt_lock_acquire (tty, &lock_context);
 }
-tty->rawOutBuf.theBuf[tty->rawOutBuf.Head] = *buf++;
+
+/* Copy as much chars as fit until current tail or end of ring buffer */
+partlen = len;
+if (tty->rawOutBuf.Tail > tty->rawOutBuf.Head) {
+  /* Available space is contiguous from Head to Tail */
+  size_t available = tty->rawOutBuf.Tail - tty->rawOutBuf.Head - 1;
+  if (partlen > available)
+partlen = available;
+} else {
+  /* Available space wraps at buffer end. To keep code simple, utilize
+ only the part from Head to end during this iteration */
+  size_t available = tty->rawOutBuf.Size - tty->rawOutBuf.Head;
+  if (partlen > available)
+partlen = available;
+}
+
+/* To minimize latency, the memcpy should be done
+ * with interrupts enabled (TBD) */
+memcpy(&tty->rawOutBuf.theBuf[tty->rawOutBuf.Head], buf, partlen);
+newHead = tty->rawOutBuf.Head + partlen;
+if (newHead >= tty->rawOutBuf.Size)
+  newHead -= tty->rawOutBuf.Size;
 tty->rawOutBuf.Head = newHead;
+buf += partlen;
+
 if (tty->rawOutBufState == rob_idle) {
-  /* check, whether XOFF has been received */
-  if (!(tty->flow_ctrl & FL_ORCVXOF)) {
-(*tty->handler.write)(
-  tty, &tty->rawOutBuf.theBuf[tty->rawOutBuf.Tail],1);
+  if (tty->flow_ctrl & FL_MDXOF) {
+/* Write only one byte at a time if using XON/XOFF flow ctrl */
+/* check, whether XOFF has been received */
+if (!(tty->flow_ctrl & FL_ORCVXOF)) {
+  (*tty->handler.write)(
+tty, &tty->rawOutBuf.theBuf[tty->rawOutBuf.Tail], 1);
+} else {
+  /* remember that output has been stopped due to flow ctrl*/
+  tty->flow_ctrl |= FL_OSTOP;
+}
   } else {
-/* remember that output has been stopped due to flow ctrl*/
-tty->flow_ctrl |= FL_OSTOP;
+size_t nwaiting;
+if (tty->rawOutBuf.Head > tty->rawOutBuf.Tail)
+  nwaiting = tty->rawOutBuf.Head - tty->rawOutBuf.Tail;
+else
+  nwaiting = tty->rawOutBuf.Size - tty->rawOutBuf.Tail;
+
+(*tty->handler.write)(
+  tty, &tty->rawOutBuf.theBuf[tty->rawOutBuf.Tail], nwaiting);
   }
   tty->rawOutBufState = rob_busy;
 }
 rtems_termios_interrupt_lock_release (tty, &lock_context);
-len--;
+len -= partlen;
   }
 }
 
-- 
1.9.1

___
devel mailing list
devel@rtems.org
http://lists.rtems.org/mailman/listinfo/devel


Re: [PATCH] rtems_termios_puts: Copy and write more than one char at once

2014-08-14 Thread Kolja Waschk
Beside copying more than one char at once, rtems_termios_refill_transmitter()
and rtems_termios_puts() now use a common helper that (after checking ORCVXOF)
calls write() with as much chars as are available consecutively in rawOutBuf.
Integer types used in new rtems_termios_start_xmit might not be ideal but
resemble what is used in existing rtems_refill_transmitter code.

---
 cpukit/libcsupport/src/termios.c | 133 ---
 1 file changed, 83 insertions(+), 50 deletions(-)

diff --git a/cpukit/libcsupport/src/termios.c b/cpukit/libcsupport/src/termios.c
index 2448ea1..f14f138 100644
--- a/cpukit/libcsupport/src/termios.c
+++ b/cpukit/libcsupport/src/termios.c
@@ -974,6 +974,47 @@ rtems_termios_ioctl (void *arg)
 }
 
 /*
+ * Send as many chars at once as possible to device-specific code.
+ * If stopXmit==true then explicitly write(0) to stop transmitter.
+ */
+static int
+rtems_termios_start_xmit (
+  struct rtems_termios_tty *tty, unsigned int newTail, bool stopXmit)
+{
+  int nToSend;
+
+  /* if XOFF was received, do not start output resp. stop */
+  if ((tty->flow_ctrl & (FL_MDXON | FL_ORCVXOF))
+  ==(FL_MDXON | FL_ORCVXOF)) {
+/* Buffer not empty, but output stops due to XOFF */
+/* set flag, that output has been stopped */
+tty->flow_ctrl |= FL_OSTOP;
+nToSend = 0;
+tty->rawOutBufState = rob_busy;
+if (stopXmit) {
+  (*tty->handler.write) (tty, NULL, 0);
+}
+  } else {
+if (newTail > tty->rawOutBuf.Head)
+  nToSend = tty->rawOutBuf.Size - newTail;
+else
+  nToSend = tty->rawOutBuf.Head - newTail;
+/* when flow control XON or XOF, don't send blocks of data */
+/* to allow fast reaction on incoming flow ctrl and low latency*/
+/* for outgoing flow control   */
+if (tty->flow_ctrl & (FL_MDXON | FL_MDXOF)) {
+  nToSend = 1;
+}
+
+tty->rawOutBufState = rob_busy;
+(*tty->handler.write)(
+tty, &tty->rawOutBuf.theBuf[newTail], nToSend);
+  }
+
+  return nToSend;
+}
+
+/*
  * Send characters to device-specific code
  */
 void
@@ -989,21 +1030,16 @@ rtems_termios_puts (
 (*tty->handler.write)(tty, buf, len);
 return;
   }
-  newHead = tty->rawOutBuf.Head;
+
   while (len) {
-/*
- * Performance improvement could be made here.
- * Copy multiple bytes to raw buffer:
- * if (len > 1) && (space to buffer end, or tail > 1)
- *  ncopy = MIN (len, space to buffer end or tail)
- *  memcpy (raw buffer, buf, ncopy)
- *  buf += ncopy
- *  len -= ncopy
- *
- * To minimize latency, the memcpy should be done
- * with interrupts enabled.
- */
-newHead = (newHead + 1) % tty->rawOutBuf.Size;
+size_t nToCopy;
+size_t nAvail;
+
+/* Check space for at least one char */
+newHead = tty->rawOutBuf.Head + 1;
+if (newHead >= tty->rawOutBuf.Size)
+  newHead -= tty->rawOutBuf.Size;
+
 rtems_termios_interrupt_lock_acquire (tty, &lock_context);
 while (newHead == tty->rawOutBuf.Tail) {
   tty->rawOutBufState = rob_wait;
@@ -1014,21 +1050,41 @@ rtems_termios_puts (
 rtems_fatal_error_occurred (sc);
   rtems_termios_interrupt_lock_acquire (tty, &lock_context);
 }
-tty->rawOutBuf.theBuf[tty->rawOutBuf.Head] = *buf++;
+
+/* Determine free space up to current tail or end of ring buffer */
+nToCopy = len;
+if (tty->rawOutBuf.Tail > tty->rawOutBuf.Head) {
+  /* Available space is contiguous from Head to Tail */
+  nAvail = tty->rawOutBuf.Tail - tty->rawOutBuf.Head - 1;
+} else {
+  /* Available space wraps at buffer end. To keep code simple, utilize
+ only the part from Head to end during this iteration */
+  nAvail = tty->rawOutBuf.Size - tty->rawOutBuf.Head;
+  /* Head may not touch Tail after wraparound */
+  if (tty->rawOutBuf.Tail == 0)
+nAvail--;
+}
+if (nToCopy > nAvail)
+  nToCopy = nAvail;
+
+/* To minimize latency, the memcpy could be done
+ * with interrupts enabled or with limit on nToCopy (TBD)
+ */
+memcpy(&tty->rawOutBuf.theBuf[tty->rawOutBuf.Head], buf, nToCopy);
+
+newHead = tty->rawOutBuf.Head + nToCopy;
+if (newHead >= tty->rawOutBuf.Size)
+  newHead -= tty->rawOutBuf.Size;
 tty->rawOutBuf.Head = newHead;
+
 if (tty->rawOutBufState == rob_idle) {
-  /* check, whether XOFF has been received */
-  if (!(tty->flow_ctrl & FL_ORCVXOF)) {
-(*tty->handler.write)(
-  tty, &tty->rawOutBuf.theBuf[tty->rawOutBuf.Tail],1);
-  } else {
-/* remember that output has been stopped due to flow ctrl*/
-tty->flow_ctrl |= FL_OSTOP;
-  }
-  tty->rawOutBufState = rob_busy;
+  (void)rtems_termios_start_xmit (tty, tty->rawOutBuf.Tail, false);
 }
+
 rtems_termios_interrupt_lock_release (tty, &lock_context);
-len--;
+
+buf += nToCopy;
+len -= nToCopy;
   }
 }
 
@@ -1678,35 +1734,12 @@ rtems_te

Re: [PATCH] rtems_termios_puts: Copy and write more than one char at once

2014-08-14 Thread Kolja Waschk

Hi Sebastian,


+  int nToSend;

Integer type is wrong.


Yes, just what the original refill_transmitter uses and expects: nToSend 
is passed as return value to rtems_refill_transmitter, which also 
returns it to its caller as an int...



+ (void)rtems_termios_start_xmit (tty, tty->rawOutBuf.Tail, false);

Please don't use this (void).


Only one of the two callers (rtems_refill_transmitter) is interested in 
that information. Is it preferred to omit the "(void)" in the other or 
rather do not pass the info as a return value, but e.g. into a variable 
pointed to by a further argument?


http://stackoverflow.com/questions/11888594/ignoring-return-values-in-c

Thanks,
Kolja

--
mr.k.waschk - ixo.de - hamburg, germany

___
devel mailing list
devel@rtems.org
http://lists.rtems.org/mailman/listinfo/devel


Console driver that needs extra TIMER

2014-08-14 Thread Kolja Waschk

Hi,

I'm developing a DMA-based UART driver for Blackfin. It requires an 
extra timer to trigger data processing after a transmission ended. This 
increases the amount of timers required by every application that uses 
it for the console.


E.g. most of the tests won't even start without modifications. So 
effectively I have to fall back to a polling driver for console if I 
want to run the tests unmodified, or is there any mechanism to generally 
increment the amount of timers for all tests?


Thanks,
Kolja





___
devel mailing list
devel@rtems.org
http://lists.rtems.org/mailman/listinfo/devel


Re: [PATCH] rtems_termios_puts: Copy and write more than one char at once

2014-08-14 Thread Kolja Waschk
Renamed startXmit(), nToSend is unsigned, just check FL_ORCVXOF, no (void) cast 
anymore, compute nToSend in single if/else if/else.


---
 cpukit/libcsupport/src/termios.c | 135 ---
 1 file changed, 85 insertions(+), 50 deletions(-)

diff --git a/cpukit/libcsupport/src/termios.c b/cpukit/libcsupport/src/termios.c
index 2448ea1..216834c 100644
--- a/cpukit/libcsupport/src/termios.c
+++ b/cpukit/libcsupport/src/termios.c
@@ -974,6 +974,49 @@ rtems_termios_ioctl (void *arg)
 }
 
 /*
+ * Send as many chars at once as possible to device-specific code.
+ * If transmitting==true then assume transmission is already running and
+ * an explicit write(0) is needed if output has to stop for flow control.
+ */
+static unsigned int
+startXmit (
+  struct rtems_termios_tty *tty,
+  unsigned int newTail,
+  bool transmitting
+)
+{
+  unsigned int nToSend;
+
+  tty->rawOutBufState = rob_busy;
+
+  /* if XOFF was received, do not (re)start output */
+  if (tty->flow_ctrl & FL_ORCVXOF) {
+/* set flag, that output has been stopped */
+tty->flow_ctrl |= FL_OSTOP;
+nToSend = 0;
+/* stop transmitter */
+if (transmitting) {
+  (*tty->handler.write) (tty, NULL, 0);
+}
+  } else {
+/* when flow control XON or XOF, don't send blocks of data */
+/* to allow fast reaction on incoming flow ctrl and low latency*/
+/* for outgoing flow control   */
+if (tty->flow_ctrl & (FL_MDXON | FL_MDXOF))
+  nToSend = 1;
+else if (newTail > tty->rawOutBuf.Head)
+  nToSend = tty->rawOutBuf.Size - newTail;
+else
+  nToSend = tty->rawOutBuf.Head - newTail;
+
+(*tty->handler.write)(
+tty, &tty->rawOutBuf.theBuf[newTail], nToSend);
+  }
+
+  return nToSend;
+}
+
+/*
  * Send characters to device-specific code
  */
 void
@@ -989,21 +1032,16 @@ rtems_termios_puts (
 (*tty->handler.write)(tty, buf, len);
 return;
   }
-  newHead = tty->rawOutBuf.Head;
+
   while (len) {
-/*
- * Performance improvement could be made here.
- * Copy multiple bytes to raw buffer:
- * if (len > 1) && (space to buffer end, or tail > 1)
- *  ncopy = MIN (len, space to buffer end or tail)
- *  memcpy (raw buffer, buf, ncopy)
- *  buf += ncopy
- *  len -= ncopy
- *
- * To minimize latency, the memcpy should be done
- * with interrupts enabled.
- */
-newHead = (newHead + 1) % tty->rawOutBuf.Size;
+size_t nToCopy;
+size_t nAvail;
+
+/* Check space for at least one char */
+newHead = tty->rawOutBuf.Head + 1;
+if (newHead >= tty->rawOutBuf.Size)
+  newHead -= tty->rawOutBuf.Size;
+
 rtems_termios_interrupt_lock_acquire (tty, &lock_context);
 while (newHead == tty->rawOutBuf.Tail) {
   tty->rawOutBufState = rob_wait;
@@ -1014,21 +1052,41 @@ rtems_termios_puts (
 rtems_fatal_error_occurred (sc);
   rtems_termios_interrupt_lock_acquire (tty, &lock_context);
 }
-tty->rawOutBuf.theBuf[tty->rawOutBuf.Head] = *buf++;
+
+/* Determine free space up to current tail or end of ring buffer */
+nToCopy = len;
+if (tty->rawOutBuf.Tail > tty->rawOutBuf.Head) {
+  /* Available space is contiguous from Head to Tail */
+  nAvail = tty->rawOutBuf.Tail - tty->rawOutBuf.Head - 1;
+} else {
+  /* Available space wraps at buffer end. To keep it simple, utilize
+ only the free space from Head to end during this iteration */
+  nAvail = tty->rawOutBuf.Size - tty->rawOutBuf.Head;
+  /* Head may not touch Tail after wraparound */
+  if (tty->rawOutBuf.Tail == 0)
+nAvail--;
+}
+if (nToCopy > nAvail)
+  nToCopy = nAvail;
+
+/* To minimize latency, the memcpy could be done
+ * with interrupts enabled or with limit on nToCopy (TBD)
+ */
+memcpy(&tty->rawOutBuf.theBuf[tty->rawOutBuf.Head], buf, nToCopy);
+
+newHead = tty->rawOutBuf.Head + nToCopy;
+if (newHead >= tty->rawOutBuf.Size)
+  newHead -= tty->rawOutBuf.Size;
 tty->rawOutBuf.Head = newHead;
+
 if (tty->rawOutBufState == rob_idle) {
-  /* check, whether XOFF has been received */
-  if (!(tty->flow_ctrl & FL_ORCVXOF)) {
-(*tty->handler.write)(
-  tty, &tty->rawOutBuf.theBuf[tty->rawOutBuf.Tail],1);
-  } else {
-/* remember that output has been stopped due to flow ctrl*/
-tty->flow_ctrl |= FL_OSTOP;
-  }
-  tty->rawOutBufState = rob_busy;
+  startXmit (tty, tty->rawOutBuf.Tail, false);
 }
+
 rtems_termios_interrupt_lock_release (tty, &lock_context);
-len--;
+
+buf += nToCopy;
+len -= nToCopy;
   }
 }
 
@@ -1678,35 +1736,12 @@ rtems_termios_refill_transmitter (struct 
rtems_termios_tty *tty)
   if ( tty->tty_snd.sw_pfn != NULL) {
 (*tty->tty_snd.sw_pfn)(&tty->termios, tty->tty_snd.sw_arg);
   }
-}
-/* check, whether output should stop due to received XOFF */
-else if ((tty->flow_ctrl & (FL_MDXON | FL_

Re: Console driver that needs extra TIMER

2014-08-14 Thread Kolja Waschk

[...] DMA-based UART driver for Blackfin. It requires an
extra timer to trigger data processing after a transmission ended. [...]



Don't you get an end of DMA TX interrupt?


No, it's about RX - the driver doesn't know beforehand how many bytes are 
expected
and thus can configure DMA only to continously receive into a ring buffer.
There are buffer-complete interrupts after every so many bytes, but not related
to the actual received data content.

As an alternative to the timer, to avoid the latency it introduces, the driver
can generate/interpret RTS/CTS signals as frame sync signals with interrupts,
but these aren't always connected and usually not supported by the communication
partner.

Kolja



___
devel mailing list
devel@rtems.org
http://lists.rtems.org/mailman/listinfo/devel


Re: Console driver that needs extra TIMER

2014-08-14 Thread Kolja Waschk

If you are working with rtems.git, you could probably just add another
BSP setting in confdefs.h for this and push the patch out for


Thanks for that hint. There already seems to be such mechanism in place for 
SHARED_MEMORY_DRIVER:

confdefs.h
...
rtems_api_configuration_table Configuration_RTEMS_API = {
  CONFIGURE_TASKS,
  CONFIGURE_NOTEPADS_ENABLED,
  CONFIGURE_MAXIMUM_TIMERS + CONFIGURE_TIMER_FOR_SHARED_MEMORY_DRIVER,
  CONFIGURE_SEMAPHORES,
...

At least confdefs allows to add something to CONFIGURE_MAXIMUM_TIMERS so I can
patch it for running the tests, and soon make it properly dependent on some
BSP setting.

Thanks,
Kolja



___
devel mailing list
devel@rtems.org
http://lists.rtems.org/mailman/listinfo/devel