summaryrefslogblamecommitdiffstats
path: root/cpukit/libcsupport/src/printertask.c
blob: 31d569f3211ef48dec2226e84bed2749064b69f4 (plain) (tree)






















































































































                                                                             

                                  























                                                                             



                                                                      









































                                                           

                                                                              







                                                                
                                              






                                                                 
/*
 * Copyright (c) 2016 embedded brains GmbH.  All rights reserved.
 *
 *  embedded brains GmbH
 *  Obere Lagerstr. 30
 *  82178 Puchheim
 *  Germany
 *  <rtems@embedded-brains.de>
 *
 * 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 <rtems/printer.h>

#include <rtems.h>
#include <rtems/seterr.h>

#include <unistd.h>

#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;
  int                         err;
  rtems_status_code           sc;

  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:
          err = fsync(fd);
          _Assert_Unused_variable_equals(err, 0);
          sc = rtems_event_transient_send( buffer->action_data.task );
          _Assert_Unused_variable_equals(sc, RTEMS_SUCCESSFUL);
          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;

  sc = rtems_task_start( ctx->task, printer_task, (rtems_task_argument) ctx );
  _Assert_Unused_variable_equals(sc, RTEMS_SUCCESSFUL);

  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 );
}