summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2017-02-23 15:43:41 +0100
committerSebastian Huber <sebastian.huber@embedded-brains.de>2017-02-28 09:09:19 +0100
commita165a9607abd4263fa1a572bb227bd9fb537053b (patch)
treecfe1593cacf1a76cbd3a95fb367eb7c8cdbf3101
parenttermios: Introduce doTransmit() (diff)
downloadrtems-a165a9607abd4263fa1a572bb227bd9fb537053b.tar.bz2
termios: Make write POSIX compatible
Currently only blocking read/write operations are implemented. A blocking write must transfer at least one character. It should not wait for the device for the second character and so on. Close #2917.
-rw-r--r--cpukit/libcsupport/src/termios.c115
-rw-r--r--testsuites/libtests/termios09/init.c138
2 files changed, 217 insertions, 36 deletions
diff --git a/cpukit/libcsupport/src/termios.c b/cpukit/libcsupport/src/termios.c
index 376f176623..e59f97799c 100644
--- a/cpukit/libcsupport/src/termios.c
+++ b/cpukit/libcsupport/src/termios.c
@@ -1020,20 +1020,24 @@ startXmit (
/*
* Send characters to device-specific code
*/
-static void
-doTransmit (const char *buf, size_t len, rtems_termios_tty *tty)
+static size_t
+doTransmit (const char *buf, size_t len, rtems_termios_tty *tty,
+ bool wait, bool nextWait)
{
unsigned int newHead;
rtems_termios_device_context *ctx = tty->device_context;
rtems_interrupt_lock_context lock_context;
rtems_status_code sc;
+ size_t todo;
if (tty->handler.mode == TERMIOS_POLLED) {
(*tty->handler.write)(ctx, buf, len);
- return;
+ return len;
}
- while (len) {
+ todo = len;
+
+ while (todo > 0) {
size_t nToCopy;
size_t nAvail;
@@ -1043,18 +1047,25 @@ doTransmit (const char *buf, size_t len, rtems_termios_tty *tty)
newHead -= tty->rawOutBuf.Size;
rtems_termios_device_lock_acquire (ctx, &lock_context);
- while (newHead == tty->rawOutBuf.Tail) {
- tty->rawOutBufState = rob_wait;
- rtems_termios_device_lock_release (ctx, &lock_context);
- sc = rtems_semaphore_obtain(
- tty->rawOutBuf.Semaphore, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
- if (sc != RTEMS_SUCCESSFUL)
- rtems_fatal_error_occurred (sc);
- rtems_termios_device_lock_acquire (ctx, &lock_context);
+ if (newHead == tty->rawOutBuf.Tail) {
+ if (wait) {
+ do {
+ tty->rawOutBufState = rob_wait;
+ rtems_termios_device_lock_release (ctx, &lock_context);
+ sc = rtems_semaphore_obtain(
+ tty->rawOutBuf.Semaphore, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ if (sc != RTEMS_SUCCESSFUL)
+ rtems_fatal_error_occurred (sc);
+ rtems_termios_device_lock_acquire (ctx, &lock_context);
+ } while (newHead == tty->rawOutBuf.Tail);
+ } else {
+ rtems_termios_device_lock_release (ctx, &lock_context);
+ return len - todo;
+ }
}
/* Determine free space up to current tail or end of ring buffer */
- nToCopy = len;
+ nToCopy = todo;
if (tty->rawOutBuf.Tail > tty->rawOutBuf.Head) {
/* Available space is contiguous from Head to Tail */
nAvail = tty->rawOutBuf.Tail - tty->rawOutBuf.Head - 1;
@@ -1086,22 +1097,44 @@ doTransmit (const char *buf, size_t len, rtems_termios_tty *tty)
rtems_termios_device_lock_release (ctx, &lock_context);
buf += nToCopy;
- len -= nToCopy;
+ todo -= nToCopy;
+ wait = nextWait;
}
+
+ return len;
}
void
rtems_termios_puts (
const void *_buf, size_t len, struct rtems_termios_tty *tty)
{
- doTransmit (_buf, len, tty);
+ doTransmit (_buf, len, tty, true, true);
+}
+
+static bool
+canTransmit (rtems_termios_tty *tty, bool wait, size_t len)
+{
+ rtems_termios_device_context *ctx;
+ rtems_interrupt_lock_context lock_context;
+ unsigned int capacity;
+
+ if (wait || tty->handler.mode == TERMIOS_POLLED) {
+ return true;
+ }
+
+ ctx = tty->device_context;
+ rtems_termios_device_lock_acquire (ctx, &lock_context);
+ capacity = (tty->rawOutBuf.Tail - tty->rawOutBuf.Head - 1) %
+ tty->rawOutBuf.Size;
+ rtems_termios_device_lock_release (ctx, &lock_context);
+ return capacity >= len;
}
/*
* Handle output processing
*/
-static void
-oproc (unsigned char c, struct rtems_termios_tty *tty)
+static bool
+oproc (unsigned char c, rtems_termios_tty *tty, bool wait)
{
char buf[8];
size_t len;
@@ -1118,16 +1151,21 @@ oproc (unsigned char c, struct rtems_termios_tty *tty)
if (tty->termios.c_oflag & ONLRET)
columnAdj = -oldColumn;
if (tty->termios.c_oflag & ONLCR) {
+ len = 2;
+
+ if (!canTransmit (tty, wait, len)) {
+ return false;
+ }
+
columnAdj = -oldColumn;
buf[0] = '\r';
buf[1] = c;
- len = 2;
}
break;
case '\r':
if ((tty->termios.c_oflag & ONOCR) && (oldColumn == 0))
- return;
+ return true;
if (tty->termios.c_oflag & OCRNL) {
buf[0] = '\n';
if (tty->termios.c_oflag & ONLRET)
@@ -1144,6 +1182,10 @@ oproc (unsigned char c, struct rtems_termios_tty *tty)
len = (size_t) columnAdj;
+ if (!canTransmit (tty, wait, len)) {
+ return false;
+ }
+
for (i = 0; i < columnAdj; ++i) {
buf[i] = ' ';
}
@@ -1168,26 +1210,31 @@ oproc (unsigned char c, struct rtems_termios_tty *tty)
tty->column = oldColumn + columnAdj;
}
- doTransmit (buf, len, tty);
+ return doTransmit (buf, len, tty, wait, true) > 0;
}
static uint32_t
-rtems_termios_write_tty (struct rtems_termios_tty *tty, const char *buffer,
- uint32_t initial_count)
+rtems_termios_write_tty (rtems_termios_tty *tty, const char *buf, uint32_t len)
{
+ bool wait = true;
if (tty->termios.c_oflag & OPOST) {
- uint32_t count;
+ uint32_t todo = len;
- count = initial_count;
+ while (todo > 0) {
+ if (!oproc (*buf, tty, wait)) {
+ break;
+ }
- while (count--)
- oproc (*buffer++, tty);
+ ++buf;
+ --todo;
+ wait = false;
+ }
+
+ return len - todo;
} else {
- doTransmit (buffer, initial_count, tty);
+ return doTransmit (buf, len, tty, wait, false);
}
-
- return initial_count;
}
rtems_status_code
@@ -1222,10 +1269,10 @@ echo (unsigned char c, struct rtems_termios_tty *tty)
echobuf[0] = '^';
echobuf[1] = c ^ 0x40;
- doTransmit (echobuf, 2, tty);
+ doTransmit (echobuf, 2, tty, true, true);
tty->column += 2;
} else {
- oproc (c, tty);
+ oproc (c, tty, true);
}
}
@@ -1282,18 +1329,18 @@ erase (struct rtems_termios_tty *tty, int lineFlag)
* Back up over the tab
*/
while (tty->column > col) {
- doTransmit ("\b", 1, tty);
+ doTransmit ("\b", 1, tty, true, true);
tty->column--;
}
}
else {
if (iscntrl (c) && (tty->termios.c_lflag & ECHOCTL)) {
- doTransmit ("\b \b", 3, tty);
+ doTransmit ("\b \b", 3, tty, true, true);
if (tty->column)
tty->column--;
}
if (!iscntrl (c) || (tty->termios.c_lflag & ECHOCTL)) {
- doTransmit ("\b \b", 3, tty);
+ doTransmit ("\b \b", 3, tty, true, true);
if (tty->column)
tty->column--;
}
diff --git a/testsuites/libtests/termios09/init.c b/testsuites/libtests/termios09/init.c
index 58a23ea2c4..648d1bf7ed 100644
--- a/testsuites/libtests/termios09/init.c
+++ b/testsuites/libtests/termios09/init.c
@@ -55,6 +55,8 @@ typedef struct {
device_context devices[DEVICE_COUNT];
int fds[DEVICE_COUNT];
struct termios term[DEVICE_COUNT];
+ int context_switch_counter;
+ rtems_id flush_task_id;
} test_context;
static test_context test_instance = {
@@ -910,6 +912,128 @@ static void test_olcuc(test_context *ctx)
}
}
+static void
+set_self_prio(rtems_task_priority prio)
+{
+ rtems_status_code sc;
+
+ sc = rtems_task_set_priority(RTEMS_SELF, prio, &prio);
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+}
+
+static void flush_task(rtems_task_argument arg)
+{
+ test_context *ctx = (test_context *) arg;
+
+ while (true) {
+ set_self_prio(1);
+ flush_output(ctx, INTERRUPT);
+ set_self_prio(2);
+ }
+}
+
+static void test_write(test_context *ctx)
+{
+ tcflag_t oflags = OPOST | ONLCR | XTABS;
+ rtems_status_code sc;
+ size_t i = INTERRUPT;
+ device_context *dev = &ctx->devices[i];
+ char buf[OUTPUT_BUFFER_SIZE];
+ ssize_t n;
+
+ ctx->context_switch_counter = 0;
+
+ sc = rtems_task_create(
+ rtems_build_name('F', 'L', 'S', 'H'),
+ 2,
+ RTEMS_MINIMUM_STACK_SIZE,
+ RTEMS_DEFAULT_MODES,
+ RTEMS_DEFAULT_ATTRIBUTES,
+ &ctx->flush_task_id
+ );
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+ sc = rtems_task_start(
+ ctx->flush_task_id,
+ flush_task,
+ (rtems_task_argument) ctx
+ );
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+ clear_output(ctx, i);
+ memset(buf, 'a', OUTPUT_BUFFER_SIZE);
+
+ n = write(ctx->fds[i], &buf[0], OUTPUT_BUFFER_SIZE);
+ rtems_test_assert(n == OUTPUT_BUFFER_SIZE - 1);
+
+ rtems_test_assert(ctx->context_switch_counter == 0);
+
+ n = write(ctx->fds[i], &buf[OUTPUT_BUFFER_SIZE - 1], 1);
+ rtems_test_assert(n == 1);
+
+ rtems_test_assert(ctx->context_switch_counter == 2);
+ rtems_test_assert(dev->output_count == OUTPUT_BUFFER_SIZE);
+ rtems_test_assert(memcmp(dev->output_buf, buf, OUTPUT_BUFFER_SIZE) == 0);
+
+ clear_set_oflag(ctx, i, 0, oflags);
+
+ /* Ensure that ONLCR output expansion is taken into account */
+
+ dev->tty->column = 0;
+ clear_output(ctx, i);
+ memset(buf, 'b', OUTPUT_BUFFER_SIZE - 1);
+ buf[OUTPUT_BUFFER_SIZE - 2] = '\n';
+
+ n = write(ctx->fds[i], &buf[0], OUTPUT_BUFFER_SIZE - 3);
+ rtems_test_assert(n == OUTPUT_BUFFER_SIZE - 3);
+
+ rtems_test_assert(ctx->context_switch_counter == 2);
+
+ n = write(ctx->fds[i], &buf[OUTPUT_BUFFER_SIZE - 3], 2);
+ rtems_test_assert(n == 1);
+
+ rtems_test_assert(ctx->context_switch_counter == 2);
+
+ n = write(ctx->fds[i], &buf[OUTPUT_BUFFER_SIZE - 2], 1);
+ rtems_test_assert(n == 1);
+
+ rtems_test_assert(ctx->context_switch_counter == 4);
+ rtems_test_assert(dev->output_count == OUTPUT_BUFFER_SIZE);
+ buf[OUTPUT_BUFFER_SIZE - 2] = '\r';
+ buf[OUTPUT_BUFFER_SIZE - 1] = '\n';
+ rtems_test_assert(memcmp(dev->output_buf, buf, OUTPUT_BUFFER_SIZE) == 0);
+
+ /* Ensure that XTABS output expansion is taken into account */
+
+ dev->tty->column = 0;
+ clear_output(ctx, i);
+ memset(buf, 'c', OUTPUT_BUFFER_SIZE - 8);
+ buf[OUTPUT_BUFFER_SIZE - 8] = '\t';
+
+ n = write(ctx->fds[i], &buf[0], OUTPUT_BUFFER_SIZE - 9);
+ rtems_test_assert(n == OUTPUT_BUFFER_SIZE - 9);
+
+ rtems_test_assert(ctx->context_switch_counter == 4);
+
+ n = write(ctx->fds[i], &buf[OUTPUT_BUFFER_SIZE - 9], 2);
+ rtems_test_assert(n == 1);
+
+ rtems_test_assert(ctx->context_switch_counter == 4);
+
+ n = write(ctx->fds[i], &buf[OUTPUT_BUFFER_SIZE - 8], 1);
+ rtems_test_assert(n == 1);
+
+ rtems_test_assert(ctx->context_switch_counter == 6);
+ rtems_test_assert(dev->output_count == OUTPUT_BUFFER_SIZE);
+ memset(&buf[OUTPUT_BUFFER_SIZE - 8], ' ', 8);
+ rtems_test_assert(memcmp(dev->output_buf, buf, OUTPUT_BUFFER_SIZE) == 0);
+
+ clear_set_oflag(ctx, i, oflags, 0);
+
+ sc = rtems_task_delete(ctx->flush_task_id);
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+}
+
static void Init(rtems_task_argument arg)
{
test_context *ctx = &test_instance;
@@ -932,21 +1056,31 @@ static void Init(rtems_task_argument arg)
test_opost(ctx);
test_xtabs(ctx);
test_olcuc(ctx);
+ test_write(ctx);
TEST_END();
rtems_test_exit(0);
}
+static void switch_extension(Thread_Control *executing, Thread_Control *heir)
+{
+ test_context *ctx = &test_instance;
+
+ ++ctx->context_switch_counter;
+}
+
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
#define CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS 5
-#define CONFIGURE_MAXIMUM_TASKS 1
+#define CONFIGURE_MAXIMUM_TASKS 2
#define CONFIGURE_MAXIMUM_SEMAPHORES 7
-#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
+#define CONFIGURE_INITIAL_EXTENSIONS \
+ { .thread_switch = switch_extension }, \
+ RTEMS_TEST_INITIAL_EXTENSION
#define CONFIGURE_RTEMS_INIT_TASKS_TABLE