/* * 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; rtems_chain_initialize_node( &buffer.node ); 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 ); }