/* * Copyright (c) 2017 embedded brains GmbH & Co. KG * * 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 #include #include #include #include #include #include #include #include #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