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

Sebastian Huber sebastian.huber at embedded-brains.de
Fri Aug 8 13:17:29 UTC 2014


On 08/07/2014 09:38 PM, Kolja Waschk wrote:
> ---
>   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;
> +    }

Maybe we should add helper functions for the buffer consecutive free and 
available chars, e.g.

static unsigned int
bufConsecutiveAvailable(
   unsigned int head,
   unsigned int tail,
   unsigned int size
)
{
   if (tail > head)
     return size - tail;
   else
     return head - tail;
}

static unsigned int
bufConsecutiveFree(
   unsigned int head,
   unsigned int tail,
   unsigned int size
)
{
   if (tail > head)
     return tail - head - 1;
   else
     return size - head;
}

since this is used on several places.

> +
> +    /* 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);

It should be possible to use the same logic as in 
rtems_termios_refill_transmitter() here or even introduce a common 
helper function for this block:

     /* check, whether output should stop due to received XOFF */
     else 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;
       tty->rawOutBufState = rob_busy; /*apm*/
       (*tty->handler.write) (tty, NULL, 0);
       nToSend = 0;
     } else {
       /*
        * Buffer not empty, start tranmitter
        */
       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; /*apm*/
       (*tty->handler.write)(
         tty, &tty->rawOutBuf.theBuf[newTail], nToSend);
     }
     tty->rawOutBuf.Tail = newTail; /*apm*/
   }

   rtems_termios_interrupt_lock_release (tty, &lock_context);

> +      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;
>     }
>   }
>   


-- 
Sebastian Huber, embedded brains GmbH

Address : Dornierstr. 4, D-82178 Puchheim, Germany
Phone   : +49 89 189 47 41-16
Fax     : +49 89 189 47 41-09
E-Mail  : sebastian.huber at embedded-brains.de
PGP     : Public key available on request.

Diese Nachricht ist keine geschäftliche Mitteilung im Sinne des EHUG.



More information about the devel mailing list