From d24ceb38f71e2b8a0b0822815de35cc0658c2d4e Mon Sep 17 00:00:00 2001 From: Joel Sherrill Date: Sat, 15 Nov 1997 18:15:36 +0000 Subject: interrupt driven change from Eric Norum --- c/src/lib/libc/libio.h | 3 +- c/src/lib/libc/termios.c | 271 +++++++++++++++++++++++++++++++++-------------- 2 files changed, 191 insertions(+), 83 deletions(-) (limited to 'c/src/lib/libc') diff --git a/c/src/lib/libc/libio.h b/c/src/lib/libc/libio.h index 15c04a2b79..bf9fbf68ea 100644 --- a/c/src/lib/libc/libio.h +++ b/c/src/lib/libc/libio.h @@ -139,7 +139,8 @@ rtems_status_code rtems_termios_open ( int (*deviceFirstOpen)(int major, int minor, void *arg), int (*deviceLastClose)(int major, int minor, void *arg), int (*deviceRead)(int minor), - int (*deviceWrite)(int minor, char *buf, int len) + int (*deviceWrite)(int minor, char *buf, int len), + int deviceOutputUsesInterrupts ); rtems_status_code rtems_termios_close (void *arg); diff --git a/c/src/lib/libc/termios.c b/c/src/lib/libc/termios.c index 113157f65f..5eb8997b15 100644 --- a/c/src/lib/libc/termios.c +++ b/c/src/lib/libc/termios.c @@ -1,20 +1,13 @@ /* * TERMIOS serial line support * - * Authors: + * Author: * W. Eric Norum * Saskatchewan Accelerator Laboratory * University of Saskatchewan * Saskatoon, Saskatchewan, CANADA * eric@skatter.usask.ca * - * AND - * - * Katsutoshi Shibuya - * BU Denken Co.,Ltd. - * Sapporo, JAPAN - * shibuya@mxb.meshnet.or.jp - * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.OARcorp.com/rtems/license.html. @@ -37,9 +30,12 @@ #define CBUFSIZE 256 /* - * The size of the raw input message queue + * The sizes of the raw message buffers. + * On most architectures it is quite a bit more + * efficient if these are powers of two. */ -#define RAW_BUFFER_SIZE 128 +#define RAW_INPUT_BUFFER_SIZE 128 +#define RAW_OUTPUT_BUFFER_SIZE 64 /* * Variables associated with each termios instance. @@ -89,16 +85,26 @@ struct rtems_termios_tty { rtems_interval vtimeTicks; /* - * Raw character buffer + * Raw input character buffer + */ + volatile char rawInBuf[RAW_INPUT_BUFFER_SIZE]; + volatile unsigned int rawInBufHead; + volatile unsigned int rawInBufTail; + rtems_id rawInBufSemaphore; + rtems_unsigned32 rawInBufSemaphoreOptions; + rtems_interval rawInBufSemaphoreTimeout; + rtems_interval rawInBufSemaphoreFirstTimeout; + unsigned int rawInBufDropped; /* Statistics */ + + /* + * Raw output character buffer */ - volatile char rawBuf[RAW_BUFFER_SIZE]; - volatile unsigned int rawBufHead; - volatile unsigned int rawBufTail; - rtems_id rawBufSemaphore; - rtems_unsigned32 rawBufSemaphoreOptions; - rtems_interval rawBufSemaphoreTimeout; - rtems_interval rawBufSemaphoreFirstTimeout; - unsigned int rawBufDropped; /* Statistics */ + char outputUsesInterrupts; + volatile char rawOutBuf[RAW_OUTPUT_BUFFER_SIZE]; + volatile unsigned int rawOutBufHead; + volatile unsigned int rawOutBufTail; + rtems_id rawOutBufSemaphore; + enum {rob_idle, rob_busy, rob_wait } rawOutBufState; /* * Callbacks to device-specific routines @@ -110,6 +116,29 @@ struct rtems_termios_tty { static struct rtems_termios_tty *ttyHead, *ttyTail; static rtems_id ttyMutex; +/* + * Reserve enough resources to open every physical device once. + */ +void +rtems_termios_reserve_resources ( + rtems_configuration_table *configuration, + rtems_unsigned32 number_of_devices + ) +{ + static int first_time = 1; + rtems_api_configuration_table *rtems_config; + + if (!configuration) + rtems_fatal_error_occurred (0xFFF0F001); + rtems_config = configuration->RTEMS_api_configuration; + if (!rtems_config) + rtems_fatal_error_occurred (0xFFF0F002); + if (first_time) + rtems_config->maximum_semaphores += 1; + first_time = 0; + rtems_config->maximum_semaphores += (4 * number_of_devices); +} + void rtems_termios_initialize (void) { @@ -141,7 +170,8 @@ rtems_termios_open ( int (*deviceFirstOpen)(int major, int minor, void *arg), int (*deviceLastClose)(int major, int minor, void *arg), int (*deviceRead)(int minor), - int (*deviceWrite)(int minor, char *buf, int len) + int (*deviceWrite)(int minor, char *buf, int len), + int deviceOutputUsesInterrupts ) { rtems_status_code sc; @@ -194,6 +224,16 @@ rtems_termios_open ( &tty->osem); if (sc != RTEMS_SUCCESSFUL) rtems_fatal_error_occurred (sc); + sc = rtems_semaphore_create ( + rtems_build_name ('T', 'R', 'x', c), + 0, + RTEMS_COUNTING_SEMAPHORE | RTEMS_PRIORITY, + RTEMS_NO_PRIORITY, + &tty->rawOutBufSemaphore); + if (sc != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (sc); + tty->rawOutBufHead = 0; + tty->rawOutBufTail = 0; /* * Set callbacks @@ -206,11 +246,11 @@ rtems_termios_open ( 0, RTEMS_COUNTING_SEMAPHORE | RTEMS_PRIORITY, RTEMS_NO_PRIORITY, - &tty->rawBufSemaphore); + &tty->rawInBufSemaphore); if (sc != RTEMS_SUCCESSFUL) rtems_fatal_error_occurred (sc); - tty->rawBufHead = 0; - tty->rawBufTail = 0; + tty->rawInBufHead = 0; + tty->rawInBufTail = 0; } /* @@ -218,6 +258,7 @@ rtems_termios_open ( */ tty->column = 0; tty->cindex = tty->ccount = 0; + tty->outputUsesInterrupts = deviceOutputUsesInterrupts; /* * Set default parameters @@ -266,7 +307,6 @@ rtems_termios_close (void *arg) struct rtems_termios_tty *tty = args->iop->data1; rtems_status_code sc; - args->ioctl_return = 0; sc = rtems_semaphore_obtain (ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT); if (sc != RTEMS_SUCCESSFUL) rtems_fatal_error_occurred (sc); @@ -283,8 +323,9 @@ rtems_termios_close (void *arg) tty->back->forw = tty->forw; rtems_semaphore_delete (tty->isem); rtems_semaphore_delete (tty->osem); + rtems_semaphore_delete (tty->rawOutBufSemaphore); if (tty->read == NULL) - rtems_semaphore_delete (tty->rawBufSemaphore); + rtems_semaphore_delete (tty->rawInBufSemaphore); free (tty); } rtems_semaphore_release (ttyMutex); @@ -298,6 +339,7 @@ rtems_termios_ioctl (void *arg) struct rtems_termios_tty *tty = args->iop->data1; rtems_status_code sc; + args->ioctl_return = 0; sc = rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT); if (sc != RTEMS_SUCCESSFUL) return sc; @@ -313,30 +355,30 @@ rtems_termios_ioctl (void *arg) case RTEMS_IO_SET_ATTRIBUTES: tty->termios = *(struct termios *)args->buffer; if (tty->termios.c_lflag & ICANON) { - tty->rawBufSemaphoreOptions = RTEMS_WAIT; - tty->rawBufSemaphoreTimeout = RTEMS_NO_TIMEOUT; - tty->rawBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT; + tty->rawInBufSemaphoreOptions = RTEMS_WAIT; + tty->rawInBufSemaphoreTimeout = RTEMS_NO_TIMEOUT; + tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT; } else { rtems_interval ticksPerSecond; rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticksPerSecond); tty->vtimeTicks = tty->termios.c_cc[VTIME] * ticksPerSecond / 10; if (tty->termios.c_cc[VTIME]) { - tty->rawBufSemaphoreOptions = RTEMS_WAIT; - tty->rawBufSemap`oreTimeout = tty->vtimeTicks; + tty->rawInBufSemaphoreOptions = RTEMS_WAIT; + tty->rawInBufSemaphoreTimeout = tty->vtimeTicks; if (tty->termios.c_cc[VMIN]) - tty->rawBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT; + tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT; else - tty->rawBufSemaphoreFirstTimeout = tty->vtimeTicks; + tty->rawInBufSemaphoreFirstTimeout = tty->vtimeTicks; } else { if (tty->termios.c_cc[VMIN]) { - tty->rawBufSemaphoreOptions = RTEMS_WAIT; - tty->rawBufSemaphoreTimeout = RTEMS_NO_TIMEOUT; - tty->rawBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT; + tty->rawInBufSemaphoreOptions = RTEMS_WAIT; + tty->rawInBufSemaphoreTimeout = RTEMS_NO_TIMEOUT; + tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT; } else { - tty->rawBufSemaphoreOptions = RTEMS_NO_WAIT; + tty->rawInBufSemaphoreOptions = RTEMS_NO_WAIT; } } } @@ -346,6 +388,60 @@ rtems_termios_ioctl (void *arg) return sc; } +/* + * Send characters to device-specific code + */ +static void +osend (const char *buf, int len, struct rtems_termios_tty *tty) +{ + unsigned int newHead; + rtems_interrupt_level level; + rtems_status_code sc; + + if (!tty->outputUsesInterrupts) { + (*tty->write)(tty->minor, buf, len); + return; + } + newHead = tty->rawOutBufHead; + 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) % RAW_OUTPUT_BUFFER_SIZE; + rtems_interrupt_disable (level); + while (newHead == tty->rawOutBufTail) { + tty->rawOutBufState = rob_wait; + rtems_interrupt_enable (level); + sc = rtems_semaphore_obtain (tty->rawOutBufSemaphore, + RTEMS_WAIT, + RTEMS_NO_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (sc); + rtems_interrupt_disable (level); + } + tty->rawOutBuf[tty->rawOutBufHead] = *buf++; + tty->rawOutBufHead = newHead; + if (tty->rawOutBufState == rob_idle) { + rtems_interrupt_enable (level); + tty->rawOutBufState = rob_busy; + (*tty->write)(tty->minor, (char *)&tty->rawOutBuf[tty->rawOutBufTail], 1); + } + else { + rtems_interrupt_enable (level); + } + len--; + } +} + /* * Handle output processing */ @@ -360,7 +456,7 @@ oproc (unsigned char c, struct rtems_termios_tty *tty) if (tty->termios.c_oflag & ONLRET) tty->column = 0; if (tty->termios.c_oflag & ONLCR) { - (*tty->write)(tty->minor, "\r", 1); + osend ("\r", 1, tty); tty->column = 0; } break; @@ -381,7 +477,7 @@ oproc (unsigned char c, struct rtems_termios_tty *tty) i = 8 - (tty->column & 7); if ((tty->termios.c_oflag & TABDLY) == XTABS) { tty->column += i; - (*tty->write)(tty->minor, " ", i); + osend ( " ", i, tty); return; } tty->column += i; @@ -400,7 +496,7 @@ oproc (unsigned char c, struct rtems_termios_tty *tty) break; } } - (*tty->write)(tty->minor, &c, 1); + osend (&c, 1, tty); } rtems_status_code @@ -421,10 +517,8 @@ rtems_termios_write (void *arg) args->bytes_moved = args->count; } else { - if ((*tty->write)(tty->minor, args->buffer, args->count) < 0) - sc = RTEMS_UNSATISFIED; - else - args->bytes_moved = args->count; + osend (args->buffer, args->count, tty); + args->bytes_moved = args->count; } rtems_semaphore_release (tty->osem); return sc; @@ -441,7 +535,7 @@ echo (unsigned char c, struct rtems_termios_tty *tty) echobuf[0] = '^'; echobuf[1] = c ^ 0x40; - (*tty->write)(tty->minor, echobuf, 2); + osend (echobuf, 2, tty); tty->column += 2; } else { @@ -504,18 +598,18 @@ erase (struct rtems_termios_tty *tty, int lineFlag) * Back up over the tab */ while (tty->column > col) { - (*tty->write)(tty->minor, "\b", 1); + osend ("\b", 1, tty); tty->column--; } } else { if (iscntrl (c) && (tty->termios.c_lflag & ECHOCTL)) { - (*tty->write)(tty->minor, "\b \b", 3); + osend ("\b \b", 3, tty); if (tty->column) tty->column--; } if (!iscntrl (c) || (tty->termios.c_lflag & ECHOCTL)) { - (*tty->write)(tty->minor, "\b \b", 3); + osend ("\b \b", 3, tty); if (tty->column) tty->column--; } @@ -668,20 +762,20 @@ fillBufferPoll (struct rtems_termios_tty *tty) static rtems_status_code fillBufferQueue (struct rtems_termios_tty *tty) { - rtems_interval timeout = tty->rawBufSemaphoreFirstTimeout; + rtems_interval timeout = tty->rawInBufSemaphoreFirstTimeout; rtems_status_code sc; for (;;) { /* * Process characters read from raw queue */ - while (tty->rawBufHead != tty->rawBufTail) { + while (tty->rawInBufHead != tty->rawInBufTail) { unsigned char c; unsigned int newHead; - newHead = (tty->rawBufHead + 1) % RAW_BUFFER_SIZE; - c = tty->rawBuf[newHead]; - tty->rawBufHead = newHead; + newHead = (tty->rawInBufHead + 1) % RAW_INPUT_BUFFER_SIZE; + c = tty->rawInBuf[newHead]; + tty->rawInBufHead = newHead; if (tty->termios.c_lflag & ICANON) { if (siproc (c, tty)) return RTEMS_SUCCESSFUL; @@ -691,14 +785,14 @@ fillBufferQueue (struct rtems_termios_tty *tty) if (tty->ccount >= tty->termios.c_cc[VMIN]) return RTEMS_SUCCESSFUL; } - timeout = tty->rawBufSemaphoreTimeout; + timeout = tty->rawInBufSemaphoreTimeout; } /* * Wait for characters */ - sc = rtems_semaphore_obtain (tty->rawBufSemaphore, - tty->rawBufSemaphoreOptions, + sc = rtems_semaphore_obtain (tty->rawInBufSemaphore, + tty->rawInBufSemaphoreOptions, timeout); if (sc != RTEMS_SUCCESSFUL) break; @@ -739,7 +833,8 @@ rtems_termios_read (void *arg) /* * Place characters on raw queue. - * NOTE: This routine runs in the context of the device interrupt handler. + * NOTE: This routine runs in the context of the + * device receive interrupt handler. */ void rtems_termios_enqueue_raw_characters (void *ttyp, char *buf, int len) @@ -748,40 +843,52 @@ rtems_termios_enqueue_raw_characters (void *ttyp, char *buf, int len) unsigned int newTail; while (len) { - newTail = (tty->rawBufTail + 1) % RAW_BUFFER_SIZE; - if (newTail == tty->rawBufHead) { - tty->rawBufDropped += len; + newTail = (tty->rawInBufTail + 1) % RAW_INPUT_BUFFER_SIZE; + if (newTail == tty->rawInBufHead) { + tty->rawInBufDropped += len; break; } - tty->rawBuf[newTail] = *buf++; + tty->rawInBuf[newTail] = *buf++; len--; - tty->rawBufTail = newTail; + tty->rawInBufTail = newTail; } - rtems_semaphore_release (tty->rawBufSemaphore); + rtems_semaphore_release (tty->rawInBufSemaphore); } /* - * Reserve enough resources to open every physical device once. + * Characters have been transmitted + * NOTE: This routine runs in the context of the + * device transmit interrupt handler. + * The second argument is the number of characters transmitted so far. + * This value will always be 1 for devices which generate an interrupt + * for each transmitted character. */ - -void rtems_termios_reserve_resources( - rtems_configuration_table *configuration, - rtems_unsigned32 number_of_devices -) +void +rtems_termios_dequeue_characters (void *ttyp, int len) { - static int first_time = 1; - rtems_api_configuration_table *rtems_config; - - if (!configuration) - rtems_fatal_error_occurred (0xFFF0F001); - - rtems_config = configuration->RTEMS_api_configuration; - if (!rtems_config) - rtems_fatal_error_occurred (0xFFF0F002); - - if (first_time) - rtems_config->maximum_semaphores += 1; + struct rtems_termios_tty *tty = ttyp; + unsigned int newTail; + int nToSend; - first_time = 0; - rtems_config->maximum_semaphores += (3 * number_of_devices); + if (tty->rawOutBufState == rob_wait) + rtems_semaphore_release (tty->rawOutBufSemaphore); + newTail = (tty->rawOutBufTail + len) % RAW_OUTPUT_BUFFER_SIZE; + if (newTail == tty->rawOutBufHead) { + /* + * Buffer empty + */ + tty->rawOutBufState = rob_idle; + } + else { + /* + * Buffer not empty, start tranmitter + */ + tty->rawOutBufState = rob_busy; + if (newTail > tty->rawOutBufHead) + nToSend = RAW_OUTPUT_BUFFER_SIZE - newTail; + else + nToSend = tty->rawOutBufHead - newTail; + (*tty->write)(tty->minor, (char *)&tty->rawOutBuf[newTail], nToSend); + } + tty->rawOutBufTail = newTail; } -- cgit v1.2.3