Update Console Driver chapter

Close #2785.
Close #2838.
This commit is contained in:
Sebastian Huber 2016-12-23 14:38:35 +01:00
parent 3b42c96764
commit 5bb51a43d1

View File

@ -7,240 +7,171 @@
Console Driver
**************
.. warning::
The low-level driver API changed between RTEMS 4.10 and RTEMS 4.11. The
legacy callback API is still supported, but its use is discouraged. The
following functions are deprecated:
- :c:func:`rtems_termios_open()`
- :c:func:`rtems_termios_close()`
This manual describes the new API.
Introduction
============
This chapter describes the operation of a console driver using the RTEMS POSIX
Termios support. Traditionally RTEMS has referred to all serial device drivers
as console device drivers. A console driver can be used to do raw data
processing in addition to the "normal" standard input and output device
functions required of a console.
Termios support. Traditionally, RTEMS has referred to all serial device drivers
as console drivers.
`Termios <http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap11.html>`_
is defined by IEEE Std 1003.1-2008 (POSIX.1-2008). It supports various modes
of operations at application level. This chapter focuses on the low-level
serial device driver. Additional Termios information can be found in the
`Linux TERMIOS(3) <http://man7.org/linux/man-pages/man3/termios.3.html>`_
manpage or the
`FreeBSD TERMIOS(4) <https://www.freebsd.org/cgi/man.cgi?query=termios&sektion=4>`_
manpage.
The serial driver may be called as the consequence of a C Library call such as
``printf`` or ``scanf`` or directly via the``read`` or ``write`` system calls.
There are two main functioning modes:
There are the following software layers.
- console: formatted input/output, with special characters (end of line,
tabulations, etc.) recognition and processing,
+-------------------------+
| Application |
+-------------------------+
| Termios |
+-------------------------+
| Low-Level Device Driver |
+-------------------------+
- raw: permits raw data processing.
In the default application configuration RTEMS opens during system
initialization a :file:`/dev/console` device file to create the file
descriptors 0, 1 and 2 used for standard input, output and error, respectively.
The corresponding device driver is usually a Termios serial device driver
described here. The standard file descriptors are used by standard C library
calls such as :c:func:`printf` or :c:func:`scanf` or directly via the
:c:func:`read` or :c:func:`write` system calls.
One may think that two serial drivers are needed to handle these two types of
data, but Termios permits having only one driver.
Build System and Files
======================
Termios
=======
A new serial device driver should consist of three parts.
Termios is a standard for terminal management, included in the POSIX 1003.1b
standard. As part of the POSIX and Open Group Single UNIX Specification, is
commonly provided on UNIX implementations. The Open Group has the termios
portion of the POSIX standard online at
http://opengroup.org/onlinepubs/007908775/xbd/termios.html. The requirements
for the ``<termios.h>`` file are also provided and are at
http://opengroup.org/onlinepubs/007908775/xsh/termios.h.html.
Having RTEMS support for Termios is beneficial because:
- from the user's side because it provides standard primitive operations to
access the terminal and change configuration settings. These operations are
the same under UNIX and RTEMS.
- from the BSP developer's side because it frees the developer from dealing
with buffer states and mutual exclusions on them. Early RTEMS console device
drivers also did their own special character processing.
- it is part of an internationally recognized standard.
- it makes porting code from other environments easier.
Termios support includes:
- raw and console handling,
- blocking or non-blocking characters receive, with or without Timeout.
At this time, RTEMS documentation does not include a thorough discussion of the
Termios functionality. For more information on Termios, type ``man termios``
on a Unix box or point a web browser athttp://www.freebsd.org/cgi/man.cgi.
Driver Functioning Modes
========================
There are generally three main functioning modes for an UART (Universal
Asynchronous Receiver-Transmitter, i.e. the serial chip):
- polled mode
- interrupt driven mode
- task driven mode
In polled mode, the processor blocks on sending/receiving characters. This
mode is not the most efficient way to utilize the UART. But polled mode is
usually necessary when one wants to print an error message in the event of a
fatal error such as a fatal error in the BSP. This is also the simplest mode
to program. Polled mode is generally preferred if the serial port is to be
used primarily as a debug console. In a simple polled driver, the software
will continuously check the status of the UART when it is reading or writing to
the UART. Termios improves on this by delaying the caller for 1 clock tick
between successive checks of the UART on a read operation.
In interrupt driven mode, the processor does not block on sending/receiving
characters. Data is buffered between the interrupt service routine and
application code. Two buffers are used to insulate the application from the
relative slowness of the serial device. One of the buffers is used for
incoming characters, while the other is used for outgoing characters.
An interrupt is raised when a character is received by the UART. The interrupt
subroutine places the incoming character at the end of the input buffer. When
an application asks for input, the characters at the front of the buffer are
returned.
When the application prints to the serial device, the outgoing characters are
placed at the end of the output buffer. The driver will place one or more
characters in the UART (the exact number depends on the UART) An interrupt will
be raised when all the characters have been transmitted. The interrupt service
routine has to send the characters remaining in the output buffer the same way.
When the transmitting side of the UART is idle, it is typically necessary to
prime the transmitter before the first interrupt will occur.
The task driven mode is similar to interrupt driven mode, but the actual data
processing is done in dedicated tasks instead of interrupt routines.
Serial Driver Functioning Overview
==================================
The following Figure shows how a Termios driven serial driver works: Figure not
included in ASCII version
The following list describes the basic flow.
- the application programmer uses standard C library call (printf, scanf, read,
write, etc.),
- C library (ctx.g. RedHat (formerly Cygnus) Newlib) calls the RTEMS system
call interface. This code can be found in the:file:`cpukit/libcsupport/src`
directory.
- Glue code calls the serial driver entry routines.
Basics
------
The low-level driver API changed between RTEMS 4.10 and RTEMS 4.11. The legacy
callback API is still supported, but its use is discouraged. The following
functions are deprecated:
- ``rtems_termios_open()`` - use ``rtems_termios_device_open()`` in combination
with ``rtems_termios_device_install()`` instead.
- ``rtems_termios_close()`` - use ``rtems_termios_device_close()`` instead.
This manual describes the new API. A new console driver should consist of
three parts.
- The basic console driver functions using the Termios support. Add this the
BSPs Makefile.am:
- A section in the BSPs Makefile.am:
.. code-block:: makefile
[...]
libbsp_a_SOURCES += ../../shared/console-termios.c
libbsp_a_SOURCES += console/console.c
[...]
- A general serial module specific low-level driver providing the handler table
for the Termios ``rtems_termios_device_install()`` function. This low-level
driver could be used for more than one BSP.
- A general serial device specific low-level driver providing the handler table
and the device context specialization for the Termios
:c:func:`rtems_termios_device_install()` function. This low-level driver
could be used for more than one BSP.
- A BSP specific initialization routine ``console_initialize()``, that calls
``rtems_termios_device_install()`` providing a low-level driver context for
each installed device.
- A BSP-specific initialization routine :c:func:`console_initialize()`, that calls
:c:func:`rtems_termios_device_install()` providing a low-level driver context for
each installed device. This is usually defined in the file
:file:`console/console.c` relative to the BSP base directory.
You need to provide a device handler structure for the Termios device
interface. The functions are described later in this chapter. The first open
and set attributes handler return a boolean status to indicate success (true)
or failure (false). The polled read function returns an unsigned character in
case one is available or minus one otherwise.
If you want to use polled IO it should look like the following. Termios must
be told the addresses of the handler that are to be used for simple character
IO, i.e. pointers to the ``my_driver_poll_read()`` and
``my_driver_poll_write()`` functions described later in `Termios and Polled
IO`_.
.. code-block:: c
const rtems_termios_handler my_driver_handler_polled = {
.first_open = my_driver_first_open,
.last_close = my_driver_last_close,
.poll_read = my_driver_poll_read,
.write = my_driver_poll_write,
.set_attributes = my_driver_set_attributes,
.stop_remote_tx = NULL,
.start_remote_tx = NULL,
.mode = TERMIOS_POLLED
}
For an interrupt driven implementation you need the following. The driver
functioning is quite different in this mode. There is no device driver read
handler to be passed to Termios. Indeed a ``console_read()`` call returns the
contents of Termios input buffer. This buffer is filled in the driver
interrupt subroutine, see also `Termios and Interrupt Driven IO`_. The driver
is responsible for providing a pointer to the``my_driver_interrupt_write()``
function.
.. code-block:: c
const rtems_termios_handler my_driver_handler_interrupt = {
.first_open = my_driver_first_open,
.last_close = my_driver_last_close,
.poll_read = NULL,
.write = my_driver_interrupt_write,
.set_attributes = my_driver_set_attributes,
.stopRemoteTx = NULL,
.stop_remote_tx = NULL,
.start_remote_tx = NULL,
.mode = TERMIOS_IRQ_DRIVEN
};
You can also provide hander for remote transmission control. This is not
covered in this manual, so they are set to ``NULL`` in the above examples.
The low-level driver should provide a data structure for its device context.
The initialization routine must provide a context for each installed device via
``rtems_termios_device_install()``. For simplicity of the console
initialization example the device name is also present. Here is an example
header file.
The low-level driver should provide a specialization of the Termios device
context. The initialization routine must provide a context for each installed
device via :c:func:`rtems_termios_device_install()`. Here is an example header
file for a low-level serial device driver.
.. code-block:: c
#ifndef MY_DRIVER_H
#define MY_DRIVER_H
#include <rtems/termiostypes.h>
#include <some-chip-header.h>
#include <some-chip/serial.h>
/* Low-level driver specific data structure */
#include <rtems/termiostypes.h>
/* My low-level driver specialization of Termios device context */
typedef struct {
rtems_termios_device_context base;
const char *device_name;
volatile module_register_block *regs;
volatile some_chip_registers *regs;
/* More stuff */
} my_driver_context;
extern const rtems_termios_handler my_driver_handler_polled;
extern const rtems_termios_handler my_driver_handler_interrupt;
extern const rtems_termios_device_handler my_driver_handler_polled;
extern const rtems_termios_device_handler my_driver_handler_interrupt;
#endif /* MY_DRIVER_H */
Termios and Polled IO
---------------------
Driver Functioning Modes
========================
The following handler are provided by the low-level driver and invoked by
Termios for simple character IO.
There are four main functioning modes for a Termios serial device driver. The
mode must be set during device creation and cannot be changed afterwards.
The ``my_driver_poll_write()`` routine is responsible for writing ``n``
characters from ``buf`` to the serial device specified by ``tty``.
Polled Mode (`TERMIOS_POLLED`)
In polled mode, the processor blocks on sending/receiving characters. This
mode is not the most efficient way to utilize the serial device. But polled
mode is usually necessary when one wants to print an error message in the
event of a fatal error such as a fatal error in the BSP. This is also the
simplest mode to program. Polled mode is generally preferred if the serial
device is to be used primarily as a debug console. In a simple polled
driver, the software will continuously check the status of the serial
device when it is reading or writing to the serial device. Termios
improves on this by delaying the caller for one clock tick between
successive checks of the serial device on a read operation.
Interrupt Driven Mode (`TERMIOS_IRQ_DRIVEN`)
In interrupt driven mode, the processor does not block on sending/receiving
characters. Data is buffered between the interrupt service routine and
application code. Two buffers are used to insulate the application from
the relative slowness of the serial device. One of the buffers is used for
incoming characters, while the other is used for outgoing characters.
An interrupt is raised when a character is received by the serial device.
The interrupt routine places the incoming character at the end of the input
buffer. When an application asks for input, the characters at the front of
the buffer are returned.
When the application prints to the serial device, the outgoing characters
are placed at the end of the output buffer. The driver will place one or
more characters in the serial device (the exact number depends on the
serial device) An interrupt will be raised when all the characters have
been transmitted. The interrupt service routine has to send the characters
remaining in the output buffer the same way. When the transmitting side of
the serial device is idle, it is typically necessary to prime the
transmitter before the first interrupt will occur.
Interrupt Server Driven Mode (`TERMIOS_IRQ_SERVER_DRIVEN`)
The interrupt server driven mode is identical to the interrupt driven mode,
except that a mutex is used to protect the low-level device state instead
of an interrupt lock (disabled interrupts). Use this mode in case the
serial device is connected via I2C or SPI and the I2C or SPI framework is
used.
Task Driven Mode (`TERMIOS_TASK_DRIVEN`)
The task driven mode is similar to interrupt driven mode, but the actual
data processing is done in dedicated tasks instead of interrupt routines.
Polled Mode
===========
The handler table for the polled mode should look like the following.
.. code-block:: c
const rtems_termios_device_handler my_driver_handler_polled = {
.first_open = my_driver_first_open,
.last_close = my_driver_last_close,
.poll_read = my_driver_poll_read,
.write = my_driver_poll_write,
.set_attributes = my_driver_set_attributes,
.ioctl = my_driver_ioctl, /* optional, may be NULL */
.mode = TERMIOS_POLLED
}
The :c:func:`my_driver_poll_write()` routine is responsible for writing ``n``
characters from ``buf`` to the serial device specified by ``base``.
.. code-block:: c
@ -250,63 +181,85 @@ characters from ``buf`` to the serial device specified by ``tty``.
size_t n
)
{
my_driver_context *ctx = (my_driver_context *) base;
size_t i;
/* Write */
my_driver_context *ctx;
ctx = (my_driver_context *) base;
for ( i = 0 ; i < n ; ++i ) {
my_driver_write_char( ctx, buf[ i ] );
}
}
The ``my_driver_poll_read`` routine is responsible for reading a single
character from the serial device specified by ``tty``. If no character is
available, then the routine should return minus one.
The :c:func:`my_driver_poll_read` routine is responsible for reading a single
character from the serial device specified by ``base``. If no character is
available, then the routine should immediately return minus one.
.. code-block:: c
static int my_driver_poll_read( rtems_termios_device_context *base )
{
my_driver_context *ctx = (my_driver_context *) base;
/* Check if a character is available */
my_driver_context *ctx;
size_t i;
ctx = (my_driver_context *) base;
if ( my_driver_can_read_char( ctx ) ) {
/* Return the character */
/* Return the character (must be unsigned) */
return my_driver_read_char( ctx );
} else {
/* Return an error status */
/* Return -1 to indicate that no character is available */
return -1;
}
}
Termios and Interrupt Driven IO
-------------------------------
Interrupt Driven Mode
=====================
The UART generally generates interrupts when it is ready to accept or to emit a
number of characters. In this mode, the interrupt subroutine is the core of
the driver.
The ``my_driver_interrupt_handler()`` is responsible for processing
asynchronous interrupts from the UART. There may be multiple interrupt
handlers for a single UART. Some UARTs can generate a unique interrupt vector
for each interrupt source such as a character has been received or the
transmitter is ready for another character.
In the simplest case, the ``my_driver_interrupt_handler()`` will have to check
the status of the UART and determine what caused the interrupt. The following
describes the operation of an ``my_driver_interrupt_handler`` which has to do
this:
The handler table for the interrupt driven mode should look like the following.
.. code-block:: c
static void my_driver_interrupt_handler(
rtems_vector_number vector,
void *arg
)
const rtems_termios_device_handler my_driver_handler_interrupt = {
.first_open = my_driver_first_open,
.last_close = my_driver_last_close,
.poll_read = NULL,
.write = my_driver_interrupt_write,
.set_attributes = my_driver_set_attributes,
.ioctl = my_driver_ioctl, /* optional, may be NULL */
.mode = TERMIOS_IRQ_DRIVEN
};
There is no device driver read handler to be passed to Termios. Indeed a
:c:func:`read()` call returns the contents of Termios input buffer. This
buffer is filled in the driver interrupt routine.
A serial device generally generates interrupts when it is ready to accept or to
emit a number of characters. In this mode, the interrupt routine is the core
of the driver.
The :c:func:`my_driver_interrupt_handler` is responsible for processing
asynchronous interrupts from the serial device. There may be multiple
interrupt handlers for a single serial device. Some serial devices can
generate a unique interrupt vector for each interrupt source such as a
character has been received or the transmitter is ready for another character.
In the simplest case, the :c:func:`my_driver_interrupt_handler` will have to
check the status of the serial device and determine what caused the interrupt.
The following describes the operation of an
:c:func:`my_driver_interrupt_handler` which has to do this:
.. code-block:: c
static void my_driver_interrupt_handler( void *arg )
{
rtems_termios_tty *tty = arg;
my_driver_context *ctx = rtems_termios_get_device_context(tty);
rtems_termios_tty *tty;
my_driver_context *ctx;
char buf[N];
size_t n;
tty = arg;
ctx = rtems_termios_get_device_context( tty );
/*
* Check if we have received something. The function reads the
* received characters from the device and stores them in the
@ -334,8 +287,8 @@ this:
}
}
The ``my_driver_interrupt_write()`` function is responsible for telling the
device that the ``n`` characters at ``buf`` are to be transmitted. It the
The :c:func:`my_driver_interrupt_write()` handler is responsible for telling
the device that the ``n`` characters at ``buf`` are to be transmitted. It the
value ``n`` is zero to indicate that no more characters are to send. The
driver can disable the transmit interrupts now. This routine is invoked either
from task context with disabled interrupts to start a new transmission process
@ -354,8 +307,11 @@ character.
size_t n
)
{
my_driver_context *ctx = (my_driver_context *) base;
my_driver_context *ctx;
ctx = (my_driver_context *) base;
if ( n > 0 ) {
/*
* Tell the device to transmit some characters from buf (less than
* or equal to n). When the device is finished it should raise an
@ -364,92 +320,21 @@ character.
* function again. You may have to store the number of outstanding
* characters in the device data structure.
*/
} else {
/*
* Termios will set n to zero to indicate that the transmitter is
* now inactive. The output buffer is empty in this case. The
* driver may disable the transmit interrupts now.
*/
}
Initialization
--------------
The BSP specific driver initialization is called once during the RTEMS
initialization process.
The ``console_initialize()`` function may look like this:
.. code-block:: c
#include <my-driver.h>
#include <rtems/console.h>
#include <bsp.h>
#include <bsp/fatal.h>
static my_driver_context driver_context_table[M] = { /* Some values */ };
rtems_device_driver console_initialize(
rtems_device_major_number major,
rtems_device_minor_number minor,
void *arg
)
{
rtems_status_code sc;
#ifdef SOME_BSP_USE_INTERRUPTS
const rtems_termios_handler *handler = &my_driver_handler_interrupt;
#else
const rtems_termios_handler *handler = &my_driver_handler_polled;
#endif
/*
* Initialize the Termios infrastructure. If Termios has already
* been initialized by another device driver, then this call will
* have no effect.
*/
rtems_termios_initialize();
/* Initialize each device */
for (
minor = 0;
minor < RTEMS_ARRAY_SIZE(driver_context_table);
++minor
) {
my_driver_context *ctx = &driver_context_table[minor];
/*
* Install this device in the file system and Termios. In order
* to use the console (i.e. being able to do printf, scanf etc.
* on stdin, stdout and stderr), one device must be registered as
* "/dev/console" (CONSOLE_DEVICE_NAME).
*/
sc = rtems_termios_device_install(
ctx->device_name,
major,
minor,
handler,
NULL,
ctx
);
if (sc != RTEMS_SUCCESSFUL) {
bsp_fatal(SOME_BSP_FATAL_CONSOLE_DEVICE_INSTALL);
}
}
return RTEMS_SUCCESSFUL;
}
First Open
==========
Opening a serial device
-----------------------
The ``console_open()`` function provided by :file:`console-termios.c` is called
whenever a serial device is opened. The device registered as
``"/dev/console"`` (``CONSOLE_DEVICE_NAME``) is opened automatically during
RTEMS initialization. For instance, if UART channel 2 is registered as
``"/dev/tty1"``, the ``console_open()`` entry point will be called as the
result of an ``fopen("/dev/tty1", mode)`` in the application.
During the first open of the device Termios will call the
``my_driver_first_open()`` handler.
Upon first open of the device, the :c:func:`my_driver_first_open` handler is
called by Termios. The device registered as :file:`/dev/console` (or
``CONSOLE_DEVICE_NAME``) is opened automatically during RTEMS initialization.
.. code-block:: c
@ -460,10 +345,12 @@ During the first open of the device Termios will call the
rtems_libio_open_close_args_t *args
)
{
my_driver_context *ctx = (my_driver_context *) base;
my_driver_context *ctx;
rtems_status_code sc;
bool ok;
ctx = (my_driver_context *) base;
/*
* You may add some initialization code here.
*/
@ -499,14 +386,10 @@ During the first open of the device Termios will call the
return true;
}
Closing a Serial Device
-----------------------
Last Close
==========
The ``console_close()`` provided by :file:`console-termios.c` is invoked when
the serial device is to be closed. This entry point corresponds to the device
driver close entry point.
Termios will call the ``my_driver_last_close()`` handler if the last close
Termios will call the :c:func:`my_driver_last_close` handler if the last close
happens on the device.
.. code-block:: c
@ -517,44 +400,21 @@ happens on the device.
rtems_libio_open_close_args_t *args
)
{
my_driver_context *ctx = (my_driver_context *) base;
my_driver_context *ctx;
ctx = (my_driver_context *) base;
/*
* The driver may do some cleanup here.
*/
}
Reading Characters from a Serial Device
---------------------------------------
Set Attributes
==============
The ``console_read()`` provided by :file:`console-termios.c` is invoked when
the serial device is to be read from. This entry point corresponds to the
device driver read entry point.
Writing Characters to a Serial Device
-------------------------------------
The ``console_write()`` provided by :file:`console-termios.c` is invoked when
the serial device is to be written to. This entry point corresponds to the
device driver write entry point.
Changing Serial Line Parameters
-------------------------------
The ``console_control()`` provided by :file:`console-termios.c` is invoked when
the line parameters for a particular serial device are to be changed. This
entry point corresponds to the device driver IO control entry point.
The application writer is able to control the serial line configuration with
Termios calls (such as the ``ioctl()`` command, see the Termios documentation
for more details). If the driver is to support dynamic configuration, then it
must have the ``console_control()`` piece of code. Basically ``ioctl()``
commands call ``console_control()`` with the serial line configuration in a
Termios defined data structure.
The driver is responsible for reinitializing the device with the correct
settings. For this purpose Termios calls the ``my_driver_set_attributes()``
handler.
Termios will call the :c:func:`my_driver_set_attributes` handler if a serial
line configuration parameter changed, e.g. baud, character size, number of stop
bits, parity, etc.
.. code-block:: c
@ -563,7 +423,9 @@ handler.
const struct termios *term
)
{
my_driver_context *ctx = (my_driver_context *) base;
my_driver_context *ctx;
ctx = (my_driver_context *) base;
/*
* Inspect the termios data structure and configure the device
@ -571,9 +433,111 @@ handler.
* parts of the structure that specify hardware setting for the
* communications channel such as baud, character size, etc.
*/
/*
* Return true to indicate a successful set attributes, and false
* otherwise.
*/
return true;
}
IO Control
==========
Optionally, the :c:func:`my_driver_ioctl()` routine may be provided for
arbitrary device-specific functions.
.. code-block:: c
static int my_driver_ioctl(
rtems_termios_device_context *base,
ioctl_command_t request,
void *buffer
)
{
my_driver_context *ctx;
ctx = (my_driver_context *) base;
switch ( request ) {
case MY_DRIVER_DO_XYZ:
my_driver_do_xyz(ctx, buffer);
break;
default:
rtems_set_errno_and_return_minus_one( EINVAL );
}
return 0;
}
Flow Control
============
You can also provide handler for remote transmission control. This is not
covered in this manual.
General Initialization
======================
The BSP-specific driver initialization is called once during the RTEMS
initialization process.
The :c:func:`console_initialize()` function may look like this:
.. code-block:: c
#include <my-driver.h>
#include <rtems/console.h>
#include <bsp.h>
#include <bsp/fatal.h>
static my_driver_context driver_context_table[] = {
{ /* Some values for device 0 */ },
{ /* Some values for device 1 */ }
};
rtems_device_driver console_initialize(
rtems_device_major_number major,
rtems_device_minor_number minor,
void *arg
)
{
const rtems_termios_device_handler *handler;
rtems_status_code sc;
size_t i;
#ifdef SOME_BSP_USE_INTERRUPTS
handler = &my_driver_handler_interrupt;
#else
handler = &my_driver_handler_polled;
#endif
/*
* Initialize the Termios infrastructure. If Termios has already
* been initialized by another device driver, then this call will
* have no effect.
*/
rtems_termios_initialize();
/* Initialize each device */
for ( i = 0; i < RTEMS_ARRAY_SIZE( driver_context_table ) ; ++i ) {
my_driver_context *ctx;
ctx = &driver_context_table[ i ];
/*
* Install this device in the file system and Termios. In order
* to use the console (i.e. being able to do printf, scanf etc.
* on stdin, stdout and stderr), one device must be registered as
* "/dev/console" (CONSOLE_DEVICE_NAME).
*/
sc = rtems_termios_device_install( ctx->device_name, handler, NULL, ctx );
if ( sc != RTEMS_SUCCESSFUL ) {
bsp_fatal( SOME_BSP_FATAL_CONSOLE_DEVICE_INSTALL );
}
}
return RTEMS_SUCCESSFUL;
}