interrupt driven change from Eric Norum

This commit is contained in:
Joel Sherrill
1997-11-15 18:15:36 +00:00
parent a307f79f17
commit d24ceb38f7
8 changed files with 618 additions and 261 deletions

View File

@@ -139,7 +139,8 @@ rtems_status_code rtems_termios_open (
int (*deviceFirstOpen)(int major, int minor, void *arg), int (*deviceFirstOpen)(int major, int minor, void *arg),
int (*deviceLastClose)(int major, int minor, void *arg), int (*deviceLastClose)(int major, int minor, void *arg),
int (*deviceRead)(int minor), 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); rtems_status_code rtems_termios_close (void *arg);

View File

@@ -1,20 +1,13 @@
/* /*
* TERMIOS serial line support * TERMIOS serial line support
* *
* Authors: * Author:
* W. Eric Norum * W. Eric Norum
* Saskatchewan Accelerator Laboratory * Saskatchewan Accelerator Laboratory
* University of Saskatchewan * University of Saskatchewan
* Saskatoon, Saskatchewan, CANADA * Saskatoon, Saskatchewan, CANADA
* eric@skatter.usask.ca * 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 * The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at * found in the file LICENSE in this distribution or at
* http://www.OARcorp.com/rtems/license.html. * http://www.OARcorp.com/rtems/license.html.
@@ -37,9 +30,12 @@
#define CBUFSIZE 256 #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. * Variables associated with each termios instance.
@@ -89,16 +85,26 @@ struct rtems_termios_tty {
rtems_interval vtimeTicks; rtems_interval vtimeTicks;
/* /*
* Raw character buffer * Raw input character buffer
*/ */
volatile char rawBuf[RAW_BUFFER_SIZE]; volatile char rawInBuf[RAW_INPUT_BUFFER_SIZE];
volatile unsigned int rawBufHead; volatile unsigned int rawInBufHead;
volatile unsigned int rawBufTail; volatile unsigned int rawInBufTail;
rtems_id rawBufSemaphore; rtems_id rawInBufSemaphore;
rtems_unsigned32 rawBufSemaphoreOptions; rtems_unsigned32 rawInBufSemaphoreOptions;
rtems_interval rawBufSemaphoreTimeout; rtems_interval rawInBufSemaphoreTimeout;
rtems_interval rawBufSemaphoreFirstTimeout; rtems_interval rawInBufSemaphoreFirstTimeout;
unsigned int rawBufDropped; /* Statistics */ unsigned int rawInBufDropped; /* Statistics */
/*
* Raw output character buffer
*/
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 * Callbacks to device-specific routines
@@ -110,6 +116,29 @@ struct rtems_termios_tty {
static struct rtems_termios_tty *ttyHead, *ttyTail; static struct rtems_termios_tty *ttyHead, *ttyTail;
static rtems_id ttyMutex; 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 void
rtems_termios_initialize (void) rtems_termios_initialize (void)
{ {
@@ -141,7 +170,8 @@ rtems_termios_open (
int (*deviceFirstOpen)(int major, int minor, void *arg), int (*deviceFirstOpen)(int major, int minor, void *arg),
int (*deviceLastClose)(int major, int minor, void *arg), int (*deviceLastClose)(int major, int minor, void *arg),
int (*deviceRead)(int minor), 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; rtems_status_code sc;
@@ -194,6 +224,16 @@ rtems_termios_open (
&tty->osem); &tty->osem);
if (sc != RTEMS_SUCCESSFUL) if (sc != RTEMS_SUCCESSFUL)
rtems_fatal_error_occurred (sc); 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 * Set callbacks
@@ -206,11 +246,11 @@ rtems_termios_open (
0, 0,
RTEMS_COUNTING_SEMAPHORE | RTEMS_PRIORITY, RTEMS_COUNTING_SEMAPHORE | RTEMS_PRIORITY,
RTEMS_NO_PRIORITY, RTEMS_NO_PRIORITY,
&tty->rawBufSemaphore); &tty->rawInBufSemaphore);
if (sc != RTEMS_SUCCESSFUL) if (sc != RTEMS_SUCCESSFUL)
rtems_fatal_error_occurred (sc); rtems_fatal_error_occurred (sc);
tty->rawBufHead = 0; tty->rawInBufHead = 0;
tty->rawBufTail = 0; tty->rawInBufTail = 0;
} }
/* /*
@@ -218,6 +258,7 @@ rtems_termios_open (
*/ */
tty->column = 0; tty->column = 0;
tty->cindex = tty->ccount = 0; tty->cindex = tty->ccount = 0;
tty->outputUsesInterrupts = deviceOutputUsesInterrupts;
/* /*
* Set default parameters * Set default parameters
@@ -266,7 +307,6 @@ rtems_termios_close (void *arg)
struct rtems_termios_tty *tty = args->iop->data1; struct rtems_termios_tty *tty = args->iop->data1;
rtems_status_code sc; rtems_status_code sc;
args->ioctl_return = 0;
sc = rtems_semaphore_obtain (ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT); sc = rtems_semaphore_obtain (ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
if (sc != RTEMS_SUCCESSFUL) if (sc != RTEMS_SUCCESSFUL)
rtems_fatal_error_occurred (sc); rtems_fatal_error_occurred (sc);
@@ -283,8 +323,9 @@ rtems_termios_close (void *arg)
tty->back->forw = tty->forw; tty->back->forw = tty->forw;
rtems_semaphore_delete (tty->isem); rtems_semaphore_delete (tty->isem);
rtems_semaphore_delete (tty->osem); rtems_semaphore_delete (tty->osem);
rtems_semaphore_delete (tty->rawOutBufSemaphore);
if (tty->read == NULL) if (tty->read == NULL)
rtems_semaphore_delete (tty->rawBufSemaphore); rtems_semaphore_delete (tty->rawInBufSemaphore);
free (tty); free (tty);
} }
rtems_semaphore_release (ttyMutex); rtems_semaphore_release (ttyMutex);
@@ -298,6 +339,7 @@ rtems_termios_ioctl (void *arg)
struct rtems_termios_tty *tty = args->iop->data1; struct rtems_termios_tty *tty = args->iop->data1;
rtems_status_code sc; rtems_status_code sc;
args->ioctl_return = 0;
sc = rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT); sc = rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
if (sc != RTEMS_SUCCESSFUL) if (sc != RTEMS_SUCCESSFUL)
return sc; return sc;
@@ -313,30 +355,30 @@ rtems_termios_ioctl (void *arg)
case RTEMS_IO_SET_ATTRIBUTES: case RTEMS_IO_SET_ATTRIBUTES:
tty->termios = *(struct termios *)args->buffer; tty->termios = *(struct termios *)args->buffer;
if (tty->termios.c_lflag & ICANON) { if (tty->termios.c_lflag & ICANON) {
tty->rawBufSemaphoreOptions = RTEMS_WAIT; tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
tty->rawBufSemaphoreTimeout = RTEMS_NO_TIMEOUT; tty->rawInBufSemaphoreTimeout = RTEMS_NO_TIMEOUT;
tty->rawBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT; tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
} }
else { else {
rtems_interval ticksPerSecond; rtems_interval ticksPerSecond;
rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticksPerSecond); rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticksPerSecond);
tty->vtimeTicks = tty->termios.c_cc[VTIME] * ticksPerSecond / 10; tty->vtimeTicks = tty->termios.c_cc[VTIME] * ticksPerSecond / 10;
if (tty->termios.c_cc[VTIME]) { if (tty->termios.c_cc[VTIME]) {
tty->rawBufSemaphoreOptions = RTEMS_WAIT; tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
tty->rawBufSemap`oreTimeout = tty->vtimeTicks; tty->rawInBufSemaphoreTimeout = tty->vtimeTicks;
if (tty->termios.c_cc[VMIN]) if (tty->termios.c_cc[VMIN])
tty->rawBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT; tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
else else
tty->rawBufSemaphoreFirstTimeout = tty->vtimeTicks; tty->rawInBufSemaphoreFirstTimeout = tty->vtimeTicks;
} }
else { else {
if (tty->termios.c_cc[VMIN]) { if (tty->termios.c_cc[VMIN]) {
tty->rawBufSemaphoreOptions = RTEMS_WAIT; tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
tty->rawBufSemaphoreTimeout = RTEMS_NO_TIMEOUT; tty->rawInBufSemaphoreTimeout = RTEMS_NO_TIMEOUT;
tty->rawBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT; tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
} }
else { else {
tty->rawBufSemaphoreOptions = RTEMS_NO_WAIT; tty->rawInBufSemaphoreOptions = RTEMS_NO_WAIT;
} }
} }
} }
@@ -346,6 +388,60 @@ rtems_termios_ioctl (void *arg)
return sc; 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 * Handle output processing
*/ */
@@ -360,7 +456,7 @@ oproc (unsigned char c, struct rtems_termios_tty *tty)
if (tty->termios.c_oflag & ONLRET) if (tty->termios.c_oflag & ONLRET)
tty->column = 0; tty->column = 0;
if (tty->termios.c_oflag & ONLCR) { if (tty->termios.c_oflag & ONLCR) {
(*tty->write)(tty->minor, "\r", 1); osend ("\r", 1, tty);
tty->column = 0; tty->column = 0;
} }
break; break;
@@ -381,7 +477,7 @@ oproc (unsigned char c, struct rtems_termios_tty *tty)
i = 8 - (tty->column & 7); i = 8 - (tty->column & 7);
if ((tty->termios.c_oflag & TABDLY) == XTABS) { if ((tty->termios.c_oflag & TABDLY) == XTABS) {
tty->column += i; tty->column += i;
(*tty->write)(tty->minor, " ", i); osend ( " ", i, tty);
return; return;
} }
tty->column += i; tty->column += i;
@@ -400,7 +496,7 @@ oproc (unsigned char c, struct rtems_termios_tty *tty)
break; break;
} }
} }
(*tty->write)(tty->minor, &c, 1); osend (&c, 1, tty);
} }
rtems_status_code rtems_status_code
@@ -421,9 +517,7 @@ rtems_termios_write (void *arg)
args->bytes_moved = args->count; args->bytes_moved = args->count;
} }
else { else {
if ((*tty->write)(tty->minor, args->buffer, args->count) < 0) osend (args->buffer, args->count, tty);
sc = RTEMS_UNSATISFIED;
else
args->bytes_moved = args->count; args->bytes_moved = args->count;
} }
rtems_semaphore_release (tty->osem); rtems_semaphore_release (tty->osem);
@@ -441,7 +535,7 @@ echo (unsigned char c, struct rtems_termios_tty *tty)
echobuf[0] = '^'; echobuf[0] = '^';
echobuf[1] = c ^ 0x40; echobuf[1] = c ^ 0x40;
(*tty->write)(tty->minor, echobuf, 2); osend (echobuf, 2, tty);
tty->column += 2; tty->column += 2;
} }
else { else {
@@ -504,18 +598,18 @@ erase (struct rtems_termios_tty *tty, int lineFlag)
* Back up over the tab * Back up over the tab
*/ */
while (tty->column > col) { while (tty->column > col) {
(*tty->write)(tty->minor, "\b", 1); osend ("\b", 1, tty);
tty->column--; tty->column--;
} }
} }
else { else {
if (iscntrl (c) && (tty->termios.c_lflag & ECHOCTL)) { if (iscntrl (c) && (tty->termios.c_lflag & ECHOCTL)) {
(*tty->write)(tty->minor, "\b \b", 3); osend ("\b \b", 3, tty);
if (tty->column) if (tty->column)
tty->column--; tty->column--;
} }
if (!iscntrl (c) || (tty->termios.c_lflag & ECHOCTL)) { if (!iscntrl (c) || (tty->termios.c_lflag & ECHOCTL)) {
(*tty->write)(tty->minor, "\b \b", 3); osend ("\b \b", 3, tty);
if (tty->column) if (tty->column)
tty->column--; tty->column--;
} }
@@ -668,20 +762,20 @@ fillBufferPoll (struct rtems_termios_tty *tty)
static rtems_status_code static rtems_status_code
fillBufferQueue (struct rtems_termios_tty *tty) fillBufferQueue (struct rtems_termios_tty *tty)
{ {
rtems_interval timeout = tty->rawBufSemaphoreFirstTimeout; rtems_interval timeout = tty->rawInBufSemaphoreFirstTimeout;
rtems_status_code sc; rtems_status_code sc;
for (;;) { for (;;) {
/* /*
* Process characters read from raw queue * Process characters read from raw queue
*/ */
while (tty->rawBufHead != tty->rawBufTail) { while (tty->rawInBufHead != tty->rawInBufTail) {
unsigned char c; unsigned char c;
unsigned int newHead; unsigned int newHead;
newHead = (tty->rawBufHead + 1) % RAW_BUFFER_SIZE; newHead = (tty->rawInBufHead + 1) % RAW_INPUT_BUFFER_SIZE;
c = tty->rawBuf[newHead]; c = tty->rawInBuf[newHead];
tty->rawBufHead = newHead; tty->rawInBufHead = newHead;
if (tty->termios.c_lflag & ICANON) { if (tty->termios.c_lflag & ICANON) {
if (siproc (c, tty)) if (siproc (c, tty))
return RTEMS_SUCCESSFUL; return RTEMS_SUCCESSFUL;
@@ -691,14 +785,14 @@ fillBufferQueue (struct rtems_termios_tty *tty)
if (tty->ccount >= tty->termios.c_cc[VMIN]) if (tty->ccount >= tty->termios.c_cc[VMIN])
return RTEMS_SUCCESSFUL; return RTEMS_SUCCESSFUL;
} }
timeout = tty->rawBufSemaphoreTimeout; timeout = tty->rawInBufSemaphoreTimeout;
} }
/* /*
* Wait for characters * Wait for characters
*/ */
sc = rtems_semaphore_obtain (tty->rawBufSemaphore, sc = rtems_semaphore_obtain (tty->rawInBufSemaphore,
tty->rawBufSemaphoreOptions, tty->rawInBufSemaphoreOptions,
timeout); timeout);
if (sc != RTEMS_SUCCESSFUL) if (sc != RTEMS_SUCCESSFUL)
break; break;
@@ -739,7 +833,8 @@ rtems_termios_read (void *arg)
/* /*
* Place characters on raw queue. * 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 void
rtems_termios_enqueue_raw_characters (void *ttyp, char *buf, int len) 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; unsigned int newTail;
while (len) { while (len) {
newTail = (tty->rawBufTail + 1) % RAW_BUFFER_SIZE; newTail = (tty->rawInBufTail + 1) % RAW_INPUT_BUFFER_SIZE;
if (newTail == tty->rawBufHead) { if (newTail == tty->rawInBufHead) {
tty->rawBufDropped += len; tty->rawInBufDropped += len;
break; break;
} }
tty->rawBuf[newTail] = *buf++; tty->rawInBuf[newTail] = *buf++;
len--; 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
void rtems_termios_reserve_resources( rtems_termios_dequeue_characters (void *ttyp, int len)
rtems_configuration_table *configuration,
rtems_unsigned32 number_of_devices
)
{ {
static int first_time = 1; struct rtems_termios_tty *tty = ttyp;
rtems_api_configuration_table *rtems_config; unsigned int newTail;
int nToSend;
if (!configuration) if (tty->rawOutBufState == rob_wait)
rtems_fatal_error_occurred (0xFFF0F001); rtems_semaphore_release (tty->rawOutBufSemaphore);
newTail = (tty->rawOutBufTail + len) % RAW_OUTPUT_BUFFER_SIZE;
rtems_config = configuration->RTEMS_api_configuration; if (newTail == tty->rawOutBufHead) {
if (!rtems_config) /*
rtems_fatal_error_occurred (0xFFF0F002); * Buffer empty
*/
if (first_time) tty->rawOutBufState = rob_idle;
rtems_config->maximum_semaphores += 1; }
else {
first_time = 0; /*
rtems_config->maximum_semaphores += (3 * number_of_devices); * 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;
} }

View File

@@ -139,7 +139,8 @@ rtems_status_code rtems_termios_open (
int (*deviceFirstOpen)(int major, int minor, void *arg), int (*deviceFirstOpen)(int major, int minor, void *arg),
int (*deviceLastClose)(int major, int minor, void *arg), int (*deviceLastClose)(int major, int minor, void *arg),
int (*deviceRead)(int minor), 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); rtems_status_code rtems_termios_close (void *arg);

View File

@@ -40,7 +40,7 @@ static void *smc1ttyp;
/* /*
* I/O buffers and pointers to buffer descriptors * I/O buffers and pointers to buffer descriptors
*/ */
static volatile char rxBuf[RXBUFSIZE], txBuf; static volatile char rxBuf[RXBUFSIZE];
static volatile m360BufferDescriptor_t *smcRxBd, *smcTxBd; static volatile m360BufferDescriptor_t *smcRxBd, *smcTxBd;
/* /*
@@ -62,6 +62,15 @@ smc1InterruptHandler (rtems_vector_number v)
smcRxBd->status = M360_BD_EMPTY | M360_BD_WRAP | M360_BD_INTERRUPT; smcRxBd->status = M360_BD_EMPTY | M360_BD_WRAP | M360_BD_INTERRUPT;
} }
} }
/*
* Buffer transmitted?
*/
if (m360.smc1.smce & 0x2) {
m360.smc1.smce = 0x2;
if ((smcTxBd->status & M360_BD_READY) == 0)
rtems_termios_dequeue_characters (smc1ttyp, smcTxBd->length);
}
m360.cisr = 1UL << 4; /* Clear SMC1 interrupt-in-service bit */ m360.cisr = 1UL << 4; /* Clear SMC1 interrupt-in-service bit */
} }
@@ -122,8 +131,6 @@ smc1Initialize (void)
* Setup the Transmit Buffer Descriptor * Setup the Transmit Buffer Descriptor
*/ */
smcTxBd->status = M360_BD_WRAP; smcTxBd->status = M360_BD_WRAP;
smcTxBd->length = 1;
smcTxBd->buffer = &txBuf;
/* /*
* Set up SMC1 general and protocol-specific mode registers * Set up SMC1 general and protocol-specific mode registers
@@ -150,7 +157,7 @@ smc1Initialize (void)
sc = rtems_interrupt_catch (smc1InterruptHandler, sc = rtems_interrupt_catch (smc1InterruptHandler,
(m360.cicr & 0xE0) | 0x04, (m360.cicr & 0xE0) | 0x04,
&old_handler); &old_handler);
m360.smc1.smcm = 1; /* Enable SMC1 receiver interrupt */ m360.smc1.smcm = 3; /* Enable SMC1 TX and RX interrupts */
m360.cimr |= 1UL << 4; /* Enable SMC1 interrupts */ m360.cimr |= 1UL << 4; /* Enable SMC1 interrupts */
} }
#endif #endif
@@ -168,19 +175,32 @@ smc1Read (int minor)
return c; return c;
} }
/*
* Device-dependent write routine
* Interrupt-driven devices:
* Begin transmission of as many characters as possible (minimum is 1).
* Polling devices:
* Transmit all characters.
*/
static int static int
smc1Write (int minor, char *buf, int len) smc1Write (int minor, char *buf, int len)
{ {
int nwrite = 0; #if (defined (M360_SMC1_INTERRUPT))
smcTxBd->buffer = buf;
while (nwrite < len) { smcTxBd->length = len;
smcTxBd->status = M360_BD_READY | M360_BD_WRAP | M360_BD_INTERRUPT;
#else
while (len--) {
static char txBuf;
while (smcTxBd->status & M360_BD_READY) while (smcTxBd->status & M360_BD_READY)
continue; continue;
txBuf = *buf++; txBuf = *buf++;
smcTxBd->buffer = &txBuf;
smcTxBd->length = 1;
smcTxBd->status = M360_BD_READY | M360_BD_WRAP; smcTxBd->status = M360_BD_READY | M360_BD_WRAP;
nwrite++;
} }
return nwrite; #endif
return 0;
} }
/* /*
@@ -189,6 +209,16 @@ smc1Write (int minor, char *buf, int len)
*************** ***************
*/ */
/*
* Reserve resources consumed by this driver
*/
void console_reserve_resources(
rtems_configuration_table *configuration
)
{
rtems_termios_reserve_resources (configuration, 1);
}
/* /*
* Initialize and register the device * Initialize and register the device
*/ */
@@ -237,14 +267,16 @@ rtems_device_driver console_open(
NULL, NULL,
NULL, NULL,
NULL, NULL,
smc1Write); smc1Write,
1);
smc1ttyp = args->iop->data1; smc1ttyp = args->iop->data1;
#else #else
sc = rtems_termios_open (major, minor, arg, sc = rtems_termios_open (major, minor, arg,
NULL, NULL,
NULL, NULL,
smc1Read, smc1Read,
smc1Write); smc1Write,
0);
#endif #endif
return sc; return sc;
} }

View File

@@ -139,7 +139,8 @@ rtems_status_code rtems_termios_open (
int (*deviceFirstOpen)(int major, int minor, void *arg), int (*deviceFirstOpen)(int major, int minor, void *arg),
int (*deviceLastClose)(int major, int minor, void *arg), int (*deviceLastClose)(int major, int minor, void *arg),
int (*deviceRead)(int minor), 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); rtems_status_code rtems_termios_close (void *arg);

View File

@@ -1,20 +1,13 @@
/* /*
* TERMIOS serial line support * TERMIOS serial line support
* *
* Authors: * Author:
* W. Eric Norum * W. Eric Norum
* Saskatchewan Accelerator Laboratory * Saskatchewan Accelerator Laboratory
* University of Saskatchewan * University of Saskatchewan
* Saskatoon, Saskatchewan, CANADA * Saskatoon, Saskatchewan, CANADA
* eric@skatter.usask.ca * 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 * The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at * found in the file LICENSE in this distribution or at
* http://www.OARcorp.com/rtems/license.html. * http://www.OARcorp.com/rtems/license.html.
@@ -37,9 +30,12 @@
#define CBUFSIZE 256 #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. * Variables associated with each termios instance.
@@ -89,16 +85,26 @@ struct rtems_termios_tty {
rtems_interval vtimeTicks; rtems_interval vtimeTicks;
/* /*
* Raw character buffer * Raw input character buffer
*/ */
volatile char rawBuf[RAW_BUFFER_SIZE]; volatile char rawInBuf[RAW_INPUT_BUFFER_SIZE];
volatile unsigned int rawBufHead; volatile unsigned int rawInBufHead;
volatile unsigned int rawBufTail; volatile unsigned int rawInBufTail;
rtems_id rawBufSemaphore; rtems_id rawInBufSemaphore;
rtems_unsigned32 rawBufSemaphoreOptions; rtems_unsigned32 rawInBufSemaphoreOptions;
rtems_interval rawBufSemaphoreTimeout; rtems_interval rawInBufSemaphoreTimeout;
rtems_interval rawBufSemaphoreFirstTimeout; rtems_interval rawInBufSemaphoreFirstTimeout;
unsigned int rawBufDropped; /* Statistics */ unsigned int rawInBufDropped; /* Statistics */
/*
* Raw output character buffer
*/
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 * Callbacks to device-specific routines
@@ -110,6 +116,29 @@ struct rtems_termios_tty {
static struct rtems_termios_tty *ttyHead, *ttyTail; static struct rtems_termios_tty *ttyHead, *ttyTail;
static rtems_id ttyMutex; 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 void
rtems_termios_initialize (void) rtems_termios_initialize (void)
{ {
@@ -141,7 +170,8 @@ rtems_termios_open (
int (*deviceFirstOpen)(int major, int minor, void *arg), int (*deviceFirstOpen)(int major, int minor, void *arg),
int (*deviceLastClose)(int major, int minor, void *arg), int (*deviceLastClose)(int major, int minor, void *arg),
int (*deviceRead)(int minor), 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; rtems_status_code sc;
@@ -194,6 +224,16 @@ rtems_termios_open (
&tty->osem); &tty->osem);
if (sc != RTEMS_SUCCESSFUL) if (sc != RTEMS_SUCCESSFUL)
rtems_fatal_error_occurred (sc); 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 * Set callbacks
@@ -206,11 +246,11 @@ rtems_termios_open (
0, 0,
RTEMS_COUNTING_SEMAPHORE | RTEMS_PRIORITY, RTEMS_COUNTING_SEMAPHORE | RTEMS_PRIORITY,
RTEMS_NO_PRIORITY, RTEMS_NO_PRIORITY,
&tty->rawBufSemaphore); &tty->rawInBufSemaphore);
if (sc != RTEMS_SUCCESSFUL) if (sc != RTEMS_SUCCESSFUL)
rtems_fatal_error_occurred (sc); rtems_fatal_error_occurred (sc);
tty->rawBufHead = 0; tty->rawInBufHead = 0;
tty->rawBufTail = 0; tty->rawInBufTail = 0;
} }
/* /*
@@ -218,6 +258,7 @@ rtems_termios_open (
*/ */
tty->column = 0; tty->column = 0;
tty->cindex = tty->ccount = 0; tty->cindex = tty->ccount = 0;
tty->outputUsesInterrupts = deviceOutputUsesInterrupts;
/* /*
* Set default parameters * Set default parameters
@@ -266,7 +307,6 @@ rtems_termios_close (void *arg)
struct rtems_termios_tty *tty = args->iop->data1; struct rtems_termios_tty *tty = args->iop->data1;
rtems_status_code sc; rtems_status_code sc;
args->ioctl_return = 0;
sc = rtems_semaphore_obtain (ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT); sc = rtems_semaphore_obtain (ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
if (sc != RTEMS_SUCCESSFUL) if (sc != RTEMS_SUCCESSFUL)
rtems_fatal_error_occurred (sc); rtems_fatal_error_occurred (sc);
@@ -283,8 +323,9 @@ rtems_termios_close (void *arg)
tty->back->forw = tty->forw; tty->back->forw = tty->forw;
rtems_semaphore_delete (tty->isem); rtems_semaphore_delete (tty->isem);
rtems_semaphore_delete (tty->osem); rtems_semaphore_delete (tty->osem);
rtems_semaphore_delete (tty->rawOutBufSemaphore);
if (tty->read == NULL) if (tty->read == NULL)
rtems_semaphore_delete (tty->rawBufSemaphore); rtems_semaphore_delete (tty->rawInBufSemaphore);
free (tty); free (tty);
} }
rtems_semaphore_release (ttyMutex); rtems_semaphore_release (ttyMutex);
@@ -298,6 +339,7 @@ rtems_termios_ioctl (void *arg)
struct rtems_termios_tty *tty = args->iop->data1; struct rtems_termios_tty *tty = args->iop->data1;
rtems_status_code sc; rtems_status_code sc;
args->ioctl_return = 0;
sc = rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT); sc = rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
if (sc != RTEMS_SUCCESSFUL) if (sc != RTEMS_SUCCESSFUL)
return sc; return sc;
@@ -313,30 +355,30 @@ rtems_termios_ioctl (void *arg)
case RTEMS_IO_SET_ATTRIBUTES: case RTEMS_IO_SET_ATTRIBUTES:
tty->termios = *(struct termios *)args->buffer; tty->termios = *(struct termios *)args->buffer;
if (tty->termios.c_lflag & ICANON) { if (tty->termios.c_lflag & ICANON) {
tty->rawBufSemaphoreOptions = RTEMS_WAIT; tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
tty->rawBufSemaphoreTimeout = RTEMS_NO_TIMEOUT; tty->rawInBufSemaphoreTimeout = RTEMS_NO_TIMEOUT;
tty->rawBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT; tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
} }
else { else {
rtems_interval ticksPerSecond; rtems_interval ticksPerSecond;
rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticksPerSecond); rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticksPerSecond);
tty->vtimeTicks = tty->termios.c_cc[VTIME] * ticksPerSecond / 10; tty->vtimeTicks = tty->termios.c_cc[VTIME] * ticksPerSecond / 10;
if (tty->termios.c_cc[VTIME]) { if (tty->termios.c_cc[VTIME]) {
tty->rawBufSemaphoreOptions = RTEMS_WAIT; tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
tty->rawBufSemap`oreTimeout = tty->vtimeTicks; tty->rawInBufSemaphoreTimeout = tty->vtimeTicks;
if (tty->termios.c_cc[VMIN]) if (tty->termios.c_cc[VMIN])
tty->rawBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT; tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
else else
tty->rawBufSemaphoreFirstTimeout = tty->vtimeTicks; tty->rawInBufSemaphoreFirstTimeout = tty->vtimeTicks;
} }
else { else {
if (tty->termios.c_cc[VMIN]) { if (tty->termios.c_cc[VMIN]) {
tty->rawBufSemaphoreOptions = RTEMS_WAIT; tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
tty->rawBufSemaphoreTimeout = RTEMS_NO_TIMEOUT; tty->rawInBufSemaphoreTimeout = RTEMS_NO_TIMEOUT;
tty->rawBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT; tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
} }
else { else {
tty->rawBufSemaphoreOptions = RTEMS_NO_WAIT; tty->rawInBufSemaphoreOptions = RTEMS_NO_WAIT;
} }
} }
} }
@@ -346,6 +388,60 @@ rtems_termios_ioctl (void *arg)
return sc; 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 * Handle output processing
*/ */
@@ -360,7 +456,7 @@ oproc (unsigned char c, struct rtems_termios_tty *tty)
if (tty->termios.c_oflag & ONLRET) if (tty->termios.c_oflag & ONLRET)
tty->column = 0; tty->column = 0;
if (tty->termios.c_oflag & ONLCR) { if (tty->termios.c_oflag & ONLCR) {
(*tty->write)(tty->minor, "\r", 1); osend ("\r", 1, tty);
tty->column = 0; tty->column = 0;
} }
break; break;
@@ -381,7 +477,7 @@ oproc (unsigned char c, struct rtems_termios_tty *tty)
i = 8 - (tty->column & 7); i = 8 - (tty->column & 7);
if ((tty->termios.c_oflag & TABDLY) == XTABS) { if ((tty->termios.c_oflag & TABDLY) == XTABS) {
tty->column += i; tty->column += i;
(*tty->write)(tty->minor, " ", i); osend ( " ", i, tty);
return; return;
} }
tty->column += i; tty->column += i;
@@ -400,7 +496,7 @@ oproc (unsigned char c, struct rtems_termios_tty *tty)
break; break;
} }
} }
(*tty->write)(tty->minor, &c, 1); osend (&c, 1, tty);
} }
rtems_status_code rtems_status_code
@@ -421,9 +517,7 @@ rtems_termios_write (void *arg)
args->bytes_moved = args->count; args->bytes_moved = args->count;
} }
else { else {
if ((*tty->write)(tty->minor, args->buffer, args->count) < 0) osend (args->buffer, args->count, tty);
sc = RTEMS_UNSATISFIED;
else
args->bytes_moved = args->count; args->bytes_moved = args->count;
} }
rtems_semaphore_release (tty->osem); rtems_semaphore_release (tty->osem);
@@ -441,7 +535,7 @@ echo (unsigned char c, struct rtems_termios_tty *tty)
echobuf[0] = '^'; echobuf[0] = '^';
echobuf[1] = c ^ 0x40; echobuf[1] = c ^ 0x40;
(*tty->write)(tty->minor, echobuf, 2); osend (echobuf, 2, tty);
tty->column += 2; tty->column += 2;
} }
else { else {
@@ -504,18 +598,18 @@ erase (struct rtems_termios_tty *tty, int lineFlag)
* Back up over the tab * Back up over the tab
*/ */
while (tty->column > col) { while (tty->column > col) {
(*tty->write)(tty->minor, "\b", 1); osend ("\b", 1, tty);
tty->column--; tty->column--;
} }
} }
else { else {
if (iscntrl (c) && (tty->termios.c_lflag & ECHOCTL)) { if (iscntrl (c) && (tty->termios.c_lflag & ECHOCTL)) {
(*tty->write)(tty->minor, "\b \b", 3); osend ("\b \b", 3, tty);
if (tty->column) if (tty->column)
tty->column--; tty->column--;
} }
if (!iscntrl (c) || (tty->termios.c_lflag & ECHOCTL)) { if (!iscntrl (c) || (tty->termios.c_lflag & ECHOCTL)) {
(*tty->write)(tty->minor, "\b \b", 3); osend ("\b \b", 3, tty);
if (tty->column) if (tty->column)
tty->column--; tty->column--;
} }
@@ -668,20 +762,20 @@ fillBufferPoll (struct rtems_termios_tty *tty)
static rtems_status_code static rtems_status_code
fillBufferQueue (struct rtems_termios_tty *tty) fillBufferQueue (struct rtems_termios_tty *tty)
{ {
rtems_interval timeout = tty->rawBufSemaphoreFirstTimeout; rtems_interval timeout = tty->rawInBufSemaphoreFirstTimeout;
rtems_status_code sc; rtems_status_code sc;
for (;;) { for (;;) {
/* /*
* Process characters read from raw queue * Process characters read from raw queue
*/ */
while (tty->rawBufHead != tty->rawBufTail) { while (tty->rawInBufHead != tty->rawInBufTail) {
unsigned char c; unsigned char c;
unsigned int newHead; unsigned int newHead;
newHead = (tty->rawBufHead + 1) % RAW_BUFFER_SIZE; newHead = (tty->rawInBufHead + 1) % RAW_INPUT_BUFFER_SIZE;
c = tty->rawBuf[newHead]; c = tty->rawInBuf[newHead];
tty->rawBufHead = newHead; tty->rawInBufHead = newHead;
if (tty->termios.c_lflag & ICANON) { if (tty->termios.c_lflag & ICANON) {
if (siproc (c, tty)) if (siproc (c, tty))
return RTEMS_SUCCESSFUL; return RTEMS_SUCCESSFUL;
@@ -691,14 +785,14 @@ fillBufferQueue (struct rtems_termios_tty *tty)
if (tty->ccount >= tty->termios.c_cc[VMIN]) if (tty->ccount >= tty->termios.c_cc[VMIN])
return RTEMS_SUCCESSFUL; return RTEMS_SUCCESSFUL;
} }
timeout = tty->rawBufSemaphoreTimeout; timeout = tty->rawInBufSemaphoreTimeout;
} }
/* /*
* Wait for characters * Wait for characters
*/ */
sc = rtems_semaphore_obtain (tty->rawBufSemaphore, sc = rtems_semaphore_obtain (tty->rawInBufSemaphore,
tty->rawBufSemaphoreOptions, tty->rawInBufSemaphoreOptions,
timeout); timeout);
if (sc != RTEMS_SUCCESSFUL) if (sc != RTEMS_SUCCESSFUL)
break; break;
@@ -739,7 +833,8 @@ rtems_termios_read (void *arg)
/* /*
* Place characters on raw queue. * 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 void
rtems_termios_enqueue_raw_characters (void *ttyp, char *buf, int len) 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; unsigned int newTail;
while (len) { while (len) {
newTail = (tty->rawBufTail + 1) % RAW_BUFFER_SIZE; newTail = (tty->rawInBufTail + 1) % RAW_INPUT_BUFFER_SIZE;
if (newTail == tty->rawBufHead) { if (newTail == tty->rawInBufHead) {
tty->rawBufDropped += len; tty->rawInBufDropped += len;
break; break;
} }
tty->rawBuf[newTail] = *buf++; tty->rawInBuf[newTail] = *buf++;
len--; 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
void rtems_termios_reserve_resources( rtems_termios_dequeue_characters (void *ttyp, int len)
rtems_configuration_table *configuration,
rtems_unsigned32 number_of_devices
)
{ {
static int first_time = 1; struct rtems_termios_tty *tty = ttyp;
rtems_api_configuration_table *rtems_config; unsigned int newTail;
int nToSend;
if (!configuration) if (tty->rawOutBufState == rob_wait)
rtems_fatal_error_occurred (0xFFF0F001); rtems_semaphore_release (tty->rawOutBufSemaphore);
newTail = (tty->rawOutBufTail + len) % RAW_OUTPUT_BUFFER_SIZE;
rtems_config = configuration->RTEMS_api_configuration; if (newTail == tty->rawOutBufHead) {
if (!rtems_config) /*
rtems_fatal_error_occurred (0xFFF0F002); * Buffer empty
*/
if (first_time) tty->rawOutBufState = rob_idle;
rtems_config->maximum_semaphores += 1; }
else {
first_time = 0; /*
rtems_config->maximum_semaphores += (3 * number_of_devices); * 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;
} }

View File

@@ -139,7 +139,8 @@ rtems_status_code rtems_termios_open (
int (*deviceFirstOpen)(int major, int minor, void *arg), int (*deviceFirstOpen)(int major, int minor, void *arg),
int (*deviceLastClose)(int major, int minor, void *arg), int (*deviceLastClose)(int major, int minor, void *arg),
int (*deviceRead)(int minor), 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); rtems_status_code rtems_termios_close (void *arg);

View File

@@ -1,20 +1,13 @@
/* /*
* TERMIOS serial line support * TERMIOS serial line support
* *
* Authors: * Author:
* W. Eric Norum * W. Eric Norum
* Saskatchewan Accelerator Laboratory * Saskatchewan Accelerator Laboratory
* University of Saskatchewan * University of Saskatchewan
* Saskatoon, Saskatchewan, CANADA * Saskatoon, Saskatchewan, CANADA
* eric@skatter.usask.ca * 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 * The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at * found in the file LICENSE in this distribution or at
* http://www.OARcorp.com/rtems/license.html. * http://www.OARcorp.com/rtems/license.html.
@@ -37,9 +30,12 @@
#define CBUFSIZE 256 #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. * Variables associated with each termios instance.
@@ -89,16 +85,26 @@ struct rtems_termios_tty {
rtems_interval vtimeTicks; rtems_interval vtimeTicks;
/* /*
* Raw character buffer * Raw input character buffer
*/ */
volatile char rawBuf[RAW_BUFFER_SIZE]; volatile char rawInBuf[RAW_INPUT_BUFFER_SIZE];
volatile unsigned int rawBufHead; volatile unsigned int rawInBufHead;
volatile unsigned int rawBufTail; volatile unsigned int rawInBufTail;
rtems_id rawBufSemaphore; rtems_id rawInBufSemaphore;
rtems_unsigned32 rawBufSemaphoreOptions; rtems_unsigned32 rawInBufSemaphoreOptions;
rtems_interval rawBufSemaphoreTimeout; rtems_interval rawInBufSemaphoreTimeout;
rtems_interval rawBufSemaphoreFirstTimeout; rtems_interval rawInBufSemaphoreFirstTimeout;
unsigned int rawBufDropped; /* Statistics */ unsigned int rawInBufDropped; /* Statistics */
/*
* Raw output character buffer
*/
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 * Callbacks to device-specific routines
@@ -110,6 +116,29 @@ struct rtems_termios_tty {
static struct rtems_termios_tty *ttyHead, *ttyTail; static struct rtems_termios_tty *ttyHead, *ttyTail;
static rtems_id ttyMutex; 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 void
rtems_termios_initialize (void) rtems_termios_initialize (void)
{ {
@@ -141,7 +170,8 @@ rtems_termios_open (
int (*deviceFirstOpen)(int major, int minor, void *arg), int (*deviceFirstOpen)(int major, int minor, void *arg),
int (*deviceLastClose)(int major, int minor, void *arg), int (*deviceLastClose)(int major, int minor, void *arg),
int (*deviceRead)(int minor), 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; rtems_status_code sc;
@@ -194,6 +224,16 @@ rtems_termios_open (
&tty->osem); &tty->osem);
if (sc != RTEMS_SUCCESSFUL) if (sc != RTEMS_SUCCESSFUL)
rtems_fatal_error_occurred (sc); 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 * Set callbacks
@@ -206,11 +246,11 @@ rtems_termios_open (
0, 0,
RTEMS_COUNTING_SEMAPHORE | RTEMS_PRIORITY, RTEMS_COUNTING_SEMAPHORE | RTEMS_PRIORITY,
RTEMS_NO_PRIORITY, RTEMS_NO_PRIORITY,
&tty->rawBufSemaphore); &tty->rawInBufSemaphore);
if (sc != RTEMS_SUCCESSFUL) if (sc != RTEMS_SUCCESSFUL)
rtems_fatal_error_occurred (sc); rtems_fatal_error_occurred (sc);
tty->rawBufHead = 0; tty->rawInBufHead = 0;
tty->rawBufTail = 0; tty->rawInBufTail = 0;
} }
/* /*
@@ -218,6 +258,7 @@ rtems_termios_open (
*/ */
tty->column = 0; tty->column = 0;
tty->cindex = tty->ccount = 0; tty->cindex = tty->ccount = 0;
tty->outputUsesInterrupts = deviceOutputUsesInterrupts;
/* /*
* Set default parameters * Set default parameters
@@ -266,7 +307,6 @@ rtems_termios_close (void *arg)
struct rtems_termios_tty *tty = args->iop->data1; struct rtems_termios_tty *tty = args->iop->data1;
rtems_status_code sc; rtems_status_code sc;
args->ioctl_return = 0;
sc = rtems_semaphore_obtain (ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT); sc = rtems_semaphore_obtain (ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
if (sc != RTEMS_SUCCESSFUL) if (sc != RTEMS_SUCCESSFUL)
rtems_fatal_error_occurred (sc); rtems_fatal_error_occurred (sc);
@@ -283,8 +323,9 @@ rtems_termios_close (void *arg)
tty->back->forw = tty->forw; tty->back->forw = tty->forw;
rtems_semaphore_delete (tty->isem); rtems_semaphore_delete (tty->isem);
rtems_semaphore_delete (tty->osem); rtems_semaphore_delete (tty->osem);
rtems_semaphore_delete (tty->rawOutBufSemaphore);
if (tty->read == NULL) if (tty->read == NULL)
rtems_semaphore_delete (tty->rawBufSemaphore); rtems_semaphore_delete (tty->rawInBufSemaphore);
free (tty); free (tty);
} }
rtems_semaphore_release (ttyMutex); rtems_semaphore_release (ttyMutex);
@@ -298,6 +339,7 @@ rtems_termios_ioctl (void *arg)
struct rtems_termios_tty *tty = args->iop->data1; struct rtems_termios_tty *tty = args->iop->data1;
rtems_status_code sc; rtems_status_code sc;
args->ioctl_return = 0;
sc = rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT); sc = rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
if (sc != RTEMS_SUCCESSFUL) if (sc != RTEMS_SUCCESSFUL)
return sc; return sc;
@@ -313,30 +355,30 @@ rtems_termios_ioctl (void *arg)
case RTEMS_IO_SET_ATTRIBUTES: case RTEMS_IO_SET_ATTRIBUTES:
tty->termios = *(struct termios *)args->buffer; tty->termios = *(struct termios *)args->buffer;
if (tty->termios.c_lflag & ICANON) { if (tty->termios.c_lflag & ICANON) {
tty->rawBufSemaphoreOptions = RTEMS_WAIT; tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
tty->rawBufSemaphoreTimeout = RTEMS_NO_TIMEOUT; tty->rawInBufSemaphoreTimeout = RTEMS_NO_TIMEOUT;
tty->rawBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT; tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
} }
else { else {
rtems_interval ticksPerSecond; rtems_interval ticksPerSecond;
rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticksPerSecond); rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticksPerSecond);
tty->vtimeTicks = tty->termios.c_cc[VTIME] * ticksPerSecond / 10; tty->vtimeTicks = tty->termios.c_cc[VTIME] * ticksPerSecond / 10;
if (tty->termios.c_cc[VTIME]) { if (tty->termios.c_cc[VTIME]) {
tty->rawBufSemaphoreOptions = RTEMS_WAIT; tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
tty->rawBufSemap`oreTimeout = tty->vtimeTicks; tty->rawInBufSemaphoreTimeout = tty->vtimeTicks;
if (tty->termios.c_cc[VMIN]) if (tty->termios.c_cc[VMIN])
tty->rawBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT; tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
else else
tty->rawBufSemaphoreFirstTimeout = tty->vtimeTicks; tty->rawInBufSemaphoreFirstTimeout = tty->vtimeTicks;
} }
else { else {
if (tty->termios.c_cc[VMIN]) { if (tty->termios.c_cc[VMIN]) {
tty->rawBufSemaphoreOptions = RTEMS_WAIT; tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
tty->rawBufSemaphoreTimeout = RTEMS_NO_TIMEOUT; tty->rawInBufSemaphoreTimeout = RTEMS_NO_TIMEOUT;
tty->rawBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT; tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
} }
else { else {
tty->rawBufSemaphoreOptions = RTEMS_NO_WAIT; tty->rawInBufSemaphoreOptions = RTEMS_NO_WAIT;
} }
} }
} }
@@ -346,6 +388,60 @@ rtems_termios_ioctl (void *arg)
return sc; 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 * Handle output processing
*/ */
@@ -360,7 +456,7 @@ oproc (unsigned char c, struct rtems_termios_tty *tty)
if (tty->termios.c_oflag & ONLRET) if (tty->termios.c_oflag & ONLRET)
tty->column = 0; tty->column = 0;
if (tty->termios.c_oflag & ONLCR) { if (tty->termios.c_oflag & ONLCR) {
(*tty->write)(tty->minor, "\r", 1); osend ("\r", 1, tty);
tty->column = 0; tty->column = 0;
} }
break; break;
@@ -381,7 +477,7 @@ oproc (unsigned char c, struct rtems_termios_tty *tty)
i = 8 - (tty->column & 7); i = 8 - (tty->column & 7);
if ((tty->termios.c_oflag & TABDLY) == XTABS) { if ((tty->termios.c_oflag & TABDLY) == XTABS) {
tty->column += i; tty->column += i;
(*tty->write)(tty->minor, " ", i); osend ( " ", i, tty);
return; return;
} }
tty->column += i; tty->column += i;
@@ -400,7 +496,7 @@ oproc (unsigned char c, struct rtems_termios_tty *tty)
break; break;
} }
} }
(*tty->write)(tty->minor, &c, 1); osend (&c, 1, tty);
} }
rtems_status_code rtems_status_code
@@ -421,9 +517,7 @@ rtems_termios_write (void *arg)
args->bytes_moved = args->count; args->bytes_moved = args->count;
} }
else { else {
if ((*tty->write)(tty->minor, args->buffer, args->count) < 0) osend (args->buffer, args->count, tty);
sc = RTEMS_UNSATISFIED;
else
args->bytes_moved = args->count; args->bytes_moved = args->count;
} }
rtems_semaphore_release (tty->osem); rtems_semaphore_release (tty->osem);
@@ -441,7 +535,7 @@ echo (unsigned char c, struct rtems_termios_tty *tty)
echobuf[0] = '^'; echobuf[0] = '^';
echobuf[1] = c ^ 0x40; echobuf[1] = c ^ 0x40;
(*tty->write)(tty->minor, echobuf, 2); osend (echobuf, 2, tty);
tty->column += 2; tty->column += 2;
} }
else { else {
@@ -504,18 +598,18 @@ erase (struct rtems_termios_tty *tty, int lineFlag)
* Back up over the tab * Back up over the tab
*/ */
while (tty->column > col) { while (tty->column > col) {
(*tty->write)(tty->minor, "\b", 1); osend ("\b", 1, tty);
tty->column--; tty->column--;
} }
} }
else { else {
if (iscntrl (c) && (tty->termios.c_lflag & ECHOCTL)) { if (iscntrl (c) && (tty->termios.c_lflag & ECHOCTL)) {
(*tty->write)(tty->minor, "\b \b", 3); osend ("\b \b", 3, tty);
if (tty->column) if (tty->column)
tty->column--; tty->column--;
} }
if (!iscntrl (c) || (tty->termios.c_lflag & ECHOCTL)) { if (!iscntrl (c) || (tty->termios.c_lflag & ECHOCTL)) {
(*tty->write)(tty->minor, "\b \b", 3); osend ("\b \b", 3, tty);
if (tty->column) if (tty->column)
tty->column--; tty->column--;
} }
@@ -668,20 +762,20 @@ fillBufferPoll (struct rtems_termios_tty *tty)
static rtems_status_code static rtems_status_code
fillBufferQueue (struct rtems_termios_tty *tty) fillBufferQueue (struct rtems_termios_tty *tty)
{ {
rtems_interval timeout = tty->rawBufSemaphoreFirstTimeout; rtems_interval timeout = tty->rawInBufSemaphoreFirstTimeout;
rtems_status_code sc; rtems_status_code sc;
for (;;) { for (;;) {
/* /*
* Process characters read from raw queue * Process characters read from raw queue
*/ */
while (tty->rawBufHead != tty->rawBufTail) { while (tty->rawInBufHead != tty->rawInBufTail) {
unsigned char c; unsigned char c;
unsigned int newHead; unsigned int newHead;
newHead = (tty->rawBufHead + 1) % RAW_BUFFER_SIZE; newHead = (tty->rawInBufHead + 1) % RAW_INPUT_BUFFER_SIZE;
c = tty->rawBuf[newHead]; c = tty->rawInBuf[newHead];
tty->rawBufHead = newHead; tty->rawInBufHead = newHead;
if (tty->termios.c_lflag & ICANON) { if (tty->termios.c_lflag & ICANON) {
if (siproc (c, tty)) if (siproc (c, tty))
return RTEMS_SUCCESSFUL; return RTEMS_SUCCESSFUL;
@@ -691,14 +785,14 @@ fillBufferQueue (struct rtems_termios_tty *tty)
if (tty->ccount >= tty->termios.c_cc[VMIN]) if (tty->ccount >= tty->termios.c_cc[VMIN])
return RTEMS_SUCCESSFUL; return RTEMS_SUCCESSFUL;
} }
timeout = tty->rawBufSemaphoreTimeout; timeout = tty->rawInBufSemaphoreTimeout;
} }
/* /*
* Wait for characters * Wait for characters
*/ */
sc = rtems_semaphore_obtain (tty->rawBufSemaphore, sc = rtems_semaphore_obtain (tty->rawInBufSemaphore,
tty->rawBufSemaphoreOptions, tty->rawInBufSemaphoreOptions,
timeout); timeout);
if (sc != RTEMS_SUCCESSFUL) if (sc != RTEMS_SUCCESSFUL)
break; break;
@@ -739,7 +833,8 @@ rtems_termios_read (void *arg)
/* /*
* Place characters on raw queue. * 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 void
rtems_termios_enqueue_raw_characters (void *ttyp, char *buf, int len) 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; unsigned int newTail;
while (len) { while (len) {
newTail = (tty->rawBufTail + 1) % RAW_BUFFER_SIZE; newTail = (tty->rawInBufTail + 1) % RAW_INPUT_BUFFER_SIZE;
if (newTail == tty->rawBufHead) { if (newTail == tty->rawInBufHead) {
tty->rawBufDropped += len; tty->rawInBufDropped += len;
break; break;
} }
tty->rawBuf[newTail] = *buf++; tty->rawInBuf[newTail] = *buf++;
len--; 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
void rtems_termios_reserve_resources( rtems_termios_dequeue_characters (void *ttyp, int len)
rtems_configuration_table *configuration,
rtems_unsigned32 number_of_devices
)
{ {
static int first_time = 1; struct rtems_termios_tty *tty = ttyp;
rtems_api_configuration_table *rtems_config; unsigned int newTail;
int nToSend;
if (!configuration) if (tty->rawOutBufState == rob_wait)
rtems_fatal_error_occurred (0xFFF0F001); rtems_semaphore_release (tty->rawOutBufSemaphore);
newTail = (tty->rawOutBufTail + len) % RAW_OUTPUT_BUFFER_SIZE;
rtems_config = configuration->RTEMS_api_configuration; if (newTail == tty->rawOutBufHead) {
if (!rtems_config) /*
rtems_fatal_error_occurred (0xFFF0F002); * Buffer empty
*/
if (first_time) tty->rawOutBufState = rob_idle;
rtems_config->maximum_semaphores += 1; }
else {
first_time = 0; /*
rtems_config->maximum_semaphores += (3 * number_of_devices); * 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;
} }