From 22c3a54b8ba033cd4cfc6aa871b4259719a01a5f Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Tue, 21 Jun 2016 06:59:38 +0200 Subject: Add printer task --- cpukit/include/rtems/printer.h | 106 ++++++++++++++++++ cpukit/libcsupport/Makefile.am | 1 + cpukit/libcsupport/src/printertask.c | 203 +++++++++++++++++++++++++++++++++++ 3 files changed, 310 insertions(+) create mode 100644 cpukit/libcsupport/src/printertask.c diff --git a/cpukit/include/rtems/printer.h b/cpukit/include/rtems/printer.h index 2ed6b6ac3f..28b0b259f5 100644 --- a/cpukit/include/rtems/printer.h +++ b/cpukit/include/rtems/printer.h @@ -19,6 +19,9 @@ #define _RTEMS_PRINTER_H #include +#include +#include +#include #include @@ -110,6 +113,109 @@ extern void rtems_print_printer_printf(rtems_printer *printer); */ extern void rtems_print_printer_fprintf(rtems_printer *printer, FILE *file); +typedef struct { + rtems_id task; + RTEMS_INTERRUPT_LOCK_MEMBER( lock ) + rtems_chain_control free_buffers; + rtems_chain_control todo_buffers; + size_t task_stack_size; + rtems_task_priority task_priority; + int fd; + void *buffer_table; + size_t buffer_count; + size_t buffer_size; +} rtems_printer_task_context; + +static inline void rtems_printer_task_initialize( + rtems_printer_task_context *context +) +{ + memset( context, 0, sizeof( *context ) ); +} + +static inline void rtems_printer_task_set_stack_size( + rtems_printer_task_context *context, + size_t stack_size +) +{ + context->task_stack_size = stack_size; +} + +static inline void rtems_printer_task_set_priority( + rtems_printer_task_context *context, + rtems_task_priority priority +) +{ + context->task_priority = priority; +} + +static inline void rtems_printer_task_set_file_descriptor( + rtems_printer_task_context *context, + int fd +) +{ + context->fd = fd; +} + +static inline void rtems_printer_task_set_buffer_table( + rtems_printer_task_context *context, + void *buffer_table +) +{ + context->buffer_table = buffer_table; +} + +static inline void rtems_printer_task_set_buffer_count( + rtems_printer_task_context *context, + size_t buffer_count +) +{ + context->buffer_count = buffer_count; +} + +static inline void rtems_printer_task_set_buffer_size( + rtems_printer_task_context *context, + size_t buffer_size +) +{ + context->buffer_size = buffer_size; +} + +/** + * @brief Creates a printer task. + * + * Print requests via rtems_printf() or rtems_vprintf() using a printer task + * printer are output to a buffer and then placed on a work queue in FIFO + * order. The work queue is emptied by the printer task. The printer task + * writes the buffer content to the file descriptor specified by the context. + * Buffers are allocated from a pool of buffers as specified by the context. + * + * @param[in] printer Pointer to the printer structure. + * @param[in] context The initialized printer task context. + * + * @retval 0 Successful operation. + * @retval EINVAL Invalid context parameters. + * @retval ENOMEM Not enough resources. + */ +int rtems_print_printer_task( + rtems_printer *printer, + rtems_printer_task_context *context +); + +/** + * @brief Drains the work queue of the printer task. + * + * Waits until all output buffers in the work queue at the time of this + * function call are written to the file descriptor and an fsync() completed. + * + * The printer task must be successfully started via rtems_print_printer_task() + * before this function can be used. Otherwise, the behaviour is undefined. + * + * @param[in] context The printer task context of a successfully started + * printer task. + */ +void rtems_printer_task_drain(rtems_printer_task_context *context); + /** @} */ #ifdef __cplusplus diff --git a/cpukit/libcsupport/Makefile.am b/cpukit/libcsupport/Makefile.am index f047cfc40f..99dc2e18a6 100644 --- a/cpukit/libcsupport/Makefile.am +++ b/cpukit/libcsupport/Makefile.am @@ -128,6 +128,7 @@ libcsupport_a_SOURCES = src/gxx_wrappers.c src/getchark.c src/printk.c \ src/resource_snapshot.c \ $(BSD_LIBC_C_FILES) $(BASE_FS_C_FILES) $(MALLOC_C_FILES) \ $(ERROR_C_FILES) $(ASSOCIATION_C_FILES) +libcsupport_a_SOURCES += src/printertask.c libcsupport_a_SOURCES += $(LIBC_GLUE_C_FILES) $(PASSWORD_GROUP_C_FILES) \ $(TERMINAL_IDENTIFICATION_C_FILES) $(SYSTEM_CALL_C_FILES) \ diff --git a/cpukit/libcsupport/src/printertask.c b/cpukit/libcsupport/src/printertask.c new file mode 100644 index 0000000000..f358f32bb1 --- /dev/null +++ b/cpukit/libcsupport/src/printertask.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2016 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Obere Lagerstr. 30 + * 82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include + +#include + +#define PRINT_TASK_WAKE_UP RTEMS_EVENT_0 + +typedef struct { + rtems_chain_node node; + + enum { + ACTION_WRITE, + ACTION_DRAIN + } action_kind; + + union { + size_t size; + rtems_id task; + } action_data; + + char data[ RTEMS_ZERO_LENGTH_ARRAY ]; +} printer_task_buffer; + +static void printer_task_acquire( + rtems_printer_task_context *ctx, + rtems_interrupt_lock_context *lock_context +) +{ + rtems_interrupt_lock_acquire( &ctx->lock, lock_context ); +} + +static void printer_task_release( + rtems_printer_task_context *ctx, + rtems_interrupt_lock_context *lock_context +) +{ + rtems_interrupt_lock_release( &ctx->lock, lock_context ); +} + +static printer_task_buffer *printer_task_get_buffer( + rtems_printer_task_context *ctx, + rtems_chain_control *chain +) +{ + rtems_interrupt_lock_context lock_context; + printer_task_buffer *buffer; + + printer_task_acquire( ctx, &lock_context ); + buffer = (printer_task_buffer *) rtems_chain_get_unprotected( chain ); + printer_task_release( ctx, &lock_context ); + + return buffer; +} + +static void printer_task_append_buffer( + rtems_printer_task_context *ctx, + rtems_chain_control *chain, + printer_task_buffer *buffer +) +{ + rtems_interrupt_lock_context lock_context; + + printer_task_acquire( ctx, &lock_context ); + rtems_chain_append_unprotected( chain, &buffer->node ); + printer_task_release( ctx, &lock_context ); +} + +static int printer_task_printer( void *context, const char *fmt, va_list ap ) +{ + rtems_printer_task_context *ctx; + printer_task_buffer *buffer; + int n; + + ctx = context; + buffer = printer_task_get_buffer( ctx, &ctx->free_buffers ); + + if ( buffer == NULL ) { + rtems_set_errno_and_return_minus_one( ENOMEM ); + } + + n = vsnprintf( &buffer->data[ 0 ], ctx->buffer_size, fmt, ap ); + + if ( n >= (int) ctx->buffer_size ) { + printer_task_append_buffer( ctx, &ctx->free_buffers, buffer ); + rtems_set_errno_and_return_minus_one( EINVAL ); + } + + buffer->action_kind = ACTION_WRITE; + buffer->action_data.size = (size_t) n; + printer_task_append_buffer( ctx, &ctx->todo_buffers, buffer ); + rtems_event_send( ctx->task, PRINT_TASK_WAKE_UP ); + + return n; +} + +static void printer_task( rtems_task_argument arg ) +{ + rtems_printer_task_context *ctx; + int fd; + + ctx = (rtems_printer_task_context *) arg; + fd = ctx->fd; + + while ( true ) { + rtems_event_set unused; + printer_task_buffer *buffer; + + rtems_event_receive( + PRINT_TASK_WAKE_UP, + RTEMS_EVENT_ALL | RTEMS_WAIT, + RTEMS_NO_TIMEOUT, + &unused + ); + + while ( + ( buffer = printer_task_get_buffer( ctx, &ctx->todo_buffers ) ) != NULL + ) { + switch ( buffer->action_kind ) { + case ACTION_WRITE: + write( fd, &buffer->data[ 0 ], buffer->action_data.size ); + printer_task_append_buffer( ctx, &ctx->free_buffers, buffer ); + break; + case ACTION_DRAIN: + fsync(fd); + rtems_event_transient_send( buffer->action_data.task ); + break; + } + } + } +} + +int rtems_print_printer_task( + rtems_printer *printer, + rtems_printer_task_context *ctx +) +{ + rtems_status_code sc; + + if ( ctx->buffer_size < sizeof( printer_task_buffer ) ) { + return EINVAL; + } + + sc = rtems_task_create( + rtems_build_name( 'P', 'R', 'N', 'T'), + ctx->task_priority, + ctx->task_stack_size, + RTEMS_DEFAULT_MODES, + RTEMS_DEFAULT_ATTRIBUTES, + &ctx->task + ); + + if ( sc != RTEMS_SUCCESSFUL ) { + return ENOMEM; + } + + rtems_chain_initialize_empty( &ctx->todo_buffers ); + rtems_chain_initialize( + &ctx->free_buffers, + ctx->buffer_table, + ctx->buffer_count, + ctx->buffer_size + ); + ctx->buffer_size -= sizeof( printer_task_buffer ); + + printer->context = ctx; + printer->printer = printer_task_printer; + + rtems_task_start( ctx->task, printer_task, (rtems_task_argument) ctx ); + + return 0; +} + +void rtems_printer_task_drain( rtems_printer_task_context *ctx ) +{ + printer_task_buffer buffer; + + buffer.action_kind = ACTION_DRAIN; + buffer.action_data.task = rtems_task_self(); + + printer_task_append_buffer( ctx, &ctx->todo_buffers, &buffer ); + rtems_event_send( ctx->task, PRINT_TASK_WAKE_UP ); + rtems_event_transient_receive( RTEMS_WAIT, RTEMS_NO_TIMEOUT ); +} -- cgit v1.2.3