/*
* Copyright (c) 2017 embedded brains GmbH. All rights reserved.
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.com/license/LICENSE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/param.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <math.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <rtems.h>
#include <rtems/console.h>
#include "tmacros.h"
const char rtems_test_name[] = "SPCONSOLE 1";
#define LINE_LEN 79
static const char n_line[LINE_LEN + 1] =
"nNnNnNnNnNnNnNnNnNnNnNnNnNnNnNnNnNnNnNnNnNnNnNnNnNnNnNnNnNnNnNnNnNnNnNnNnNnNnNn";
static const char b_line[LINE_LEN + 1] =
"bBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBb";
static const char e_line[LINE_LEN + 1] =
"eEeEeEeEeEeEeEeEeEeEeEeEeEeEeEeEeEeEeEeEeEeEeEeEeEeEeEeEeEeEeEeEeEeEeEeEeEeEeEe";
typedef struct {
speed_t speed;
uint32_t value;
const char *msg;
} speed_desc;
static const speed_desc speeds[] = {
{ .speed = B9600, .value = 9600, .msg = "9600 8N1" },
{ .speed = B19200, .value = 19200, .msg = "19200 8N1" },
{ .speed = B38400, .value = 38400, .msg = "38400 8N1" },
{ .speed = B57600, .value = 57600, .msg = "57600 8N1" },
{ .speed = B115200, .value = 115200, .msg = "115200 8N1" }
};
#define BITS_PER_CHAR 10
typedef struct {
int fd;
struct termios saved_term;
struct termios term;
char buf[9600 / BITS_PER_CHAR];
rtems_id done;
} test_context;
static test_context test_instance;
static void drain(test_context *ctx)
{
int rv;
rv = tcdrain(ctx->fd);
rtems_test_assert(rv == 0);
/* Ensure that a hardware transmit FIFO of reasonable size is empty */
rv = usleep(200000);
rtems_test_assert(rv == 0);
}
static void do_begin(test_context *ctx, const char *msg)
{
puts(n_line);
puts(msg);
puts(b_line);
drain(ctx);
}
static void do_end(test_context *ctx)
{
int rv;
drain(ctx);
rv = cfsetspeed(&ctx->term, cfgetospeed(&ctx->saved_term));
rtems_test_assert(rv == 0);
rv = tcsetattr(ctx->fd, TCSANOW, &ctx->term);
rtems_test_assert(rv == 0);
puts("");
puts(e_line);
}
static void test_steps_one_task(test_context *ctx, char c)
{
char buf[128];
size_t i;
for (i = 0; i < RTEMS_ARRAY_SIZE(buf); ++i) {
size_t r;
char *p;
buf[i] = '\n';
r = i;
p = buf;
while (r > 0) {
ssize_t m;
m = write(ctx->fd, p, r);
rtems_test_assert(m > 0);
r -= (size_t) m;
p += (size_t) m;
}
if (i > 1) {
buf[i - 2] = c;
}
if (i > 0) {
buf[i - 1] = '\r';
}
}
}
static void test_speeds(test_context *ctx)
{
size_t i;
for (i = 0; i < RTEMS_ARRAY_SIZE(speeds); ++i) {
uint64_t t0;
uint64_t t1;
size_t n;
size_t r;
int rv;
do_begin(ctx, speeds[i].msg);
rv = cfsetspeed(&ctx->term, speeds[i].speed);
rtems_test_assert(rv == 0);
rv = tcsetattr(ctx->fd, TCSANOW, &ctx->term);
rtems_test_assert(rv == 0);
n = speeds[i].value / BITS_PER_CHAR;
r = n;
t0 = rtems_clock_get_uptime_nanoseconds();
while (r > 0) {
ssize_t m;
m = write(ctx->fd, ctx->buf, MIN(sizeof(ctx->buf), r));
rtems_test_assert(m > 0);
r -= (size_t) m;
}
rv = tcdrain(ctx->fd);
rtems_test_assert(rv == 0);
t1 = rtems_clock_get_uptime_nanoseconds();
do_end(ctx);
printf("transmitted chars: %zu\n", n);
printf(
"transmitted bits (1 start, 8 data, 1 stop): %zu\n",
n * BITS_PER_CHAR
);
printf("actual transmit time: %10" PRIu64 "ns\n", t1 - t0);
printf("expected transmit time: 1000000000ns\n");
}
}
static void task(test_context *ctx, char c)
{
rtems_status_code sc;
test_steps_one_task(ctx, c);
sc = rtems_semaphore_release(ctx->done);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
rtems_task_suspend(RTEMS_SELF);
}
static void task_y(rtems_task_argument arg)
{
test_context *ctx;
ctx = (test_context *) arg;
task(ctx, 'Y');
}
static void task_z(rtems_task_argument arg)
{
test_context *ctx;
ctx = (test_context *) arg;
task(ctx, 'z');
}
static void test(test_context *ctx)
{
int rv;
rtems_status_code sc;
rtems_id y;
rtems_id z;
sc = rtems_semaphore_create(
rtems_build_name('D', 'O', 'N', 'E'),
0,
RTEMS_COUNTING_SEMAPHORE,
0,
&ctx->done
);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
ctx->fd = open(CONSOLE_DEVICE_NAME, O_RDWR);
rtems_test_assert(ctx->fd >= 0);
rv = tcgetattr(ctx->fd, &ctx->saved_term);
rtems_test_assert(rv == 0);
ctx->term = ctx->saved_term;
cfmakeraw(&ctx->term);
rv = tcsetattr(ctx->fd, TCSANOW, &ctx->term);
rtems_test_assert(rv == 0);
memset(&ctx->buf, 'd', sizeof(ctx->buf));
do_begin(ctx, "steps one task");
test_steps_one_task(ctx, 'x');
do_end(ctx);
do_begin(ctx, "steps three tasks");
sc = rtems_task_create(
rtems_build_name(' ', ' ', ' ', 'Y'),
1,
RTEMS_MINIMUM_STACK_SIZE,
RTEMS_DEFAULT_MODES,
RTEMS_DEFAULT_ATTRIBUTES,
&y
);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
sc = rtems_task_start(y, task_y, (rtems_task_argument) ctx);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
sc = rtems_task_create(
rtems_build_name(' ', ' ', ' ', 'Z'),
1,
RTEMS_MINIMUM_STACK_SIZE,
RTEMS_DEFAULT_MODES,
RTEMS_DEFAULT_ATTRIBUTES,
&z
);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
sc = rtems_task_start(z, task_z, (rtems_task_argument) ctx);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
test_steps_one_task(ctx, 'x');
sc = rtems_semaphore_obtain(ctx->done, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
sc = rtems_semaphore_obtain(ctx->done, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
do_end(ctx);
test_speeds(ctx);
rv = close(ctx->fd);
rtems_test_assert(rv == 0);
sc = rtems_task_delete(y);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
sc = rtems_task_delete(z);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
sc = rtems_semaphore_delete(ctx->done);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
}
static void Init(rtems_task_argument arg)
{
TEST_BEGIN();
rtems_print_printer_fprintf_putc(&rtems_test_printer);
test(&test_instance);
TEST_END();
rtems_test_exit(0);
}
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
#define CONFIGURE_MAXIMUM_FILE_DESCRIPTORS 4
#define CONFIGURE_MAXIMUM_TASKS 3
#define CONFIGURE_MAXIMUM_SEMAPHORES 1
#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
#define CONFIGURE_INIT_TASK_INITIAL_MODES RTEMS_DEFAULT_MODES
#define CONFIGURE_INIT_TASK_ATTRIBUTES RTEMS_FLOATING_POINT
#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
#define CONFIGURE_INIT
#include <rtems/confdefs.h>