mirror of
https://git.rtems.org/rtems-docs/
synced 2025-05-15 00:17:26 +08:00
parent
3b42c96764
commit
5bb51a43d1
@ -7,240 +7,171 @@
|
|||||||
Console Driver
|
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
|
Introduction
|
||||||
============
|
============
|
||||||
|
|
||||||
This chapter describes the operation of a console driver using the RTEMS POSIX
|
This chapter describes the operation of a console driver using the RTEMS POSIX
|
||||||
Termios support. Traditionally RTEMS has referred to all serial device drivers
|
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
|
as console drivers.
|
||||||
processing in addition to the "normal" standard input and output device
|
`Termios <http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap11.html>`_
|
||||||
functions required of a console.
|
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
|
There are the following software layers.
|
||||||
``printf`` or ``scanf`` or directly via the``read`` or ``write`` system calls.
|
|
||||||
There are two main functioning modes:
|
|
||||||
|
|
||||||
- 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
|
Build System and Files
|
||||||
data, but Termios permits having only one driver.
|
======================
|
||||||
|
|
||||||
Termios
|
A new serial device driver should consist of three parts.
|
||||||
=======
|
|
||||||
|
|
||||||
Termios is a standard for terminal management, included in the POSIX 1003.1b
|
- A section in the BSPs Makefile.am:
|
||||||
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:
|
|
||||||
|
|
||||||
.. code-block:: makefile
|
.. code-block:: makefile
|
||||||
|
|
||||||
[...]
|
[...]
|
||||||
libbsp_a_SOURCES += ../../shared/console-termios.c
|
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
|
- A general serial device specific low-level driver providing the handler table
|
||||||
for the Termios ``rtems_termios_device_install()`` function. This low-level
|
and the device context specialization for the Termios
|
||||||
driver could be used for more than one BSP.
|
: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
|
- A BSP-specific initialization routine :c:func:`console_initialize()`, that calls
|
||||||
``rtems_termios_device_install()`` providing a low-level driver context for
|
:c:func:`rtems_termios_device_install()` providing a low-level driver context for
|
||||||
each installed device.
|
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
|
The low-level driver should provide a specialization of the Termios device
|
||||||
interface. The functions are described later in this chapter. The first open
|
context. The initialization routine must provide a context for each installed
|
||||||
and set attributes handler return a boolean status to indicate success (true)
|
device via :c:func:`rtems_termios_device_install()`. Here is an example header
|
||||||
or failure (false). The polled read function returns an unsigned character in
|
file for a low-level serial device driver.
|
||||||
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.
|
|
||||||
|
|
||||||
.. code-block:: c
|
.. code-block:: c
|
||||||
|
|
||||||
#ifndef MY_DRIVER_H
|
#ifndef MY_DRIVER_H
|
||||||
#define MY_DRIVER_H
|
#define MY_DRIVER_H
|
||||||
|
|
||||||
#include <rtems/termiostypes.h>
|
#include <some-chip/serial.h>
|
||||||
#include <some-chip-header.h>
|
|
||||||
|
|
||||||
/* Low-level driver specific data structure */
|
#include <rtems/termiostypes.h>
|
||||||
|
|
||||||
|
/* My low-level driver specialization of Termios device context */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
rtems_termios_device_context base;
|
rtems_termios_device_context base;
|
||||||
const char *device_name;
|
const char *device_name;
|
||||||
volatile module_register_block *regs;
|
volatile some_chip_registers *regs;
|
||||||
/* More stuff */
|
/* More stuff */
|
||||||
} my_driver_context;
|
} my_driver_context;
|
||||||
|
|
||||||
extern const rtems_termios_handler my_driver_handler_polled;
|
extern const rtems_termios_device_handler my_driver_handler_polled;
|
||||||
extern const rtems_termios_handler my_driver_handler_interrupt;
|
|
||||||
|
extern const rtems_termios_device_handler my_driver_handler_interrupt;
|
||||||
|
|
||||||
#endif /* MY_DRIVER_H */
|
#endif /* MY_DRIVER_H */
|
||||||
|
|
||||||
Termios and Polled IO
|
Driver Functioning Modes
|
||||||
---------------------
|
========================
|
||||||
|
|
||||||
The following handler are provided by the low-level driver and invoked by
|
There are four main functioning modes for a Termios serial device driver. The
|
||||||
Termios for simple character IO.
|
mode must be set during device creation and cannot be changed afterwards.
|
||||||
|
|
||||||
The ``my_driver_poll_write()`` routine is responsible for writing ``n``
|
Polled Mode (`TERMIOS_POLLED`)
|
||||||
characters from ``buf`` to the serial device specified by ``tty``.
|
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
|
.. code-block:: c
|
||||||
|
|
||||||
@ -250,72 +181,94 @@ characters from ``buf`` to the serial device specified by ``tty``.
|
|||||||
size_t n
|
size_t n
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
my_driver_context *ctx = (my_driver_context *) base;
|
my_driver_context *ctx;
|
||||||
size_t i;
|
|
||||||
/* Write */
|
ctx = (my_driver_context *) base;
|
||||||
for (i = 0; i < n; ++i) {
|
|
||||||
my_driver_write_char(ctx, buf[i]);
|
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
|
The :c:func:`my_driver_poll_read` routine is responsible for reading a single
|
||||||
character from the serial device specified by ``tty``. If no character is
|
character from the serial device specified by ``base``. If no character is
|
||||||
available, then the routine should return minus one.
|
available, then the routine should immediately return minus one.
|
||||||
|
|
||||||
.. code-block:: c
|
.. code-block:: c
|
||||||
|
|
||||||
static int my_driver_poll_read(rtems_termios_device_context *base)
|
static int my_driver_poll_read( rtems_termios_device_context *base )
|
||||||
{
|
{
|
||||||
my_driver_context *ctx = (my_driver_context *) base;
|
my_driver_context *ctx;
|
||||||
/* Check if a character is available */
|
size_t i;
|
||||||
if (my_driver_can_read_char(ctx)) {
|
|
||||||
/* Return the character */
|
ctx = (my_driver_context *) base;
|
||||||
return my_driver_read_char(ctx);
|
|
||||||
|
if ( my_driver_can_read_char( ctx ) ) {
|
||||||
|
/* Return the character (must be unsigned) */
|
||||||
|
return my_driver_read_char( ctx );
|
||||||
} else {
|
} else {
|
||||||
/* Return an error status */
|
/* Return -1 to indicate that no character is available */
|
||||||
return -1;
|
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
|
The handler table for the interrupt driven mode should look like the following.
|
||||||
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:
|
|
||||||
|
|
||||||
.. code-block:: c
|
.. code-block:: c
|
||||||
|
|
||||||
static void my_driver_interrupt_handler(
|
const rtems_termios_device_handler my_driver_handler_interrupt = {
|
||||||
rtems_vector_number vector,
|
.first_open = my_driver_first_open,
|
||||||
void *arg
|
.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;
|
rtems_termios_tty *tty;
|
||||||
my_driver_context *ctx = rtems_termios_get_device_context(tty);
|
my_driver_context *ctx;
|
||||||
char buf[N];
|
char buf[N];
|
||||||
size_t n;
|
size_t n;
|
||||||
|
|
||||||
|
tty = arg;
|
||||||
|
ctx = rtems_termios_get_device_context( tty );
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if we have received something. The function reads the
|
* Check if we have received something. The function reads the
|
||||||
* received characters from the device and stores them in the
|
* received characters from the device and stores them in the
|
||||||
* buffer. It returns the number of read characters.
|
* buffer. It returns the number of read characters.
|
||||||
*/
|
*/
|
||||||
n = my_driver_read_received_chars(ctx, buf, N);
|
n = my_driver_read_received_chars( ctx, buf, N );
|
||||||
if (n > 0) {
|
if ( n > 0 ) {
|
||||||
/* Hand the data over to the Termios infrastructure */
|
/* Hand the data over to the Termios infrastructure */
|
||||||
rtems_termios_enqueue_raw_characters(tty, buf, n);
|
rtems_termios_enqueue_raw_characters( tty, buf, n );
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -323,19 +276,19 @@ this:
|
|||||||
* the number of transmitted characters since the last write to the
|
* the number of transmitted characters since the last write to the
|
||||||
* device.
|
* device.
|
||||||
*/
|
*/
|
||||||
n = my_driver_transmitted_chars(ctx);
|
n = my_driver_transmitted_chars( ctx );
|
||||||
if (n > 0) {
|
if ( n > 0 ) {
|
||||||
/*
|
/*
|
||||||
* Notify Termios that we have transmitted some characters. It
|
* Notify Termios that we have transmitted some characters. It
|
||||||
* will call now the interrupt write function if more characters
|
* will call now the interrupt write function if more characters
|
||||||
* are ready for transmission.
|
* are ready for transmission.
|
||||||
*/
|
*/
|
||||||
rtems_termios_dequeue_characters(tty, n);
|
rtems_termios_dequeue_characters( tty, n );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
The ``my_driver_interrupt_write()`` function is responsible for telling the
|
The :c:func:`my_driver_interrupt_write()` handler is responsible for telling
|
||||||
device that the ``n`` characters at ``buf`` are to be transmitted. It the
|
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
|
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
|
driver can disable the transmit interrupts now. This routine is invoked either
|
||||||
from task context with disabled interrupts to start a new transmission process
|
from task context with disabled interrupts to start a new transmission process
|
||||||
@ -354,102 +307,34 @@ character.
|
|||||||
size_t n
|
size_t n
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
my_driver_context *ctx = (my_driver_context *) base;
|
my_driver_context *ctx;
|
||||||
|
|
||||||
/*
|
ctx = (my_driver_context *) base;
|
||||||
* Tell the device to transmit some characters from buf (less than
|
|
||||||
* or equal to n). When the device is finished it should raise an
|
|
||||||
* interrupt. The interrupt handler will notify Termios that these
|
|
||||||
* characters have been transmitted and this may trigger this write
|
|
||||||
* function again. You may have to store the number of outstanding
|
|
||||||
* characters in the device data structure.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
* 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];
|
|
||||||
|
|
||||||
|
if ( n > 0 ) {
|
||||||
/*
|
/*
|
||||||
* Install this device in the file system and Termios. In order
|
* Tell the device to transmit some characters from buf (less than
|
||||||
* to use the console (i.e. being able to do printf, scanf etc.
|
* or equal to n). When the device is finished it should raise an
|
||||||
* on stdin, stdout and stderr), one device must be registered as
|
* interrupt. The interrupt handler will notify Termios that these
|
||||||
* "/dev/console" (CONSOLE_DEVICE_NAME).
|
* characters have been transmitted and this may trigger this write
|
||||||
|
* 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.
|
||||||
*/
|
*/
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Opening a serial device
|
First Open
|
||||||
-----------------------
|
==========
|
||||||
|
|
||||||
The ``console_open()`` function provided by :file:`console-termios.c` is called
|
Upon first open of the device, the :c:func:`my_driver_first_open` handler is
|
||||||
whenever a serial device is opened. The device registered as
|
called by Termios. The device registered as :file:`/dev/console` (or
|
||||||
``"/dev/console"`` (``CONSOLE_DEVICE_NAME``) is opened automatically during
|
``CONSOLE_DEVICE_NAME``) is opened automatically during RTEMS initialization.
|
||||||
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.
|
|
||||||
|
|
||||||
.. code-block:: c
|
.. code-block:: c
|
||||||
|
|
||||||
@ -460,9 +345,11 @@ During the first open of the device Termios will call the
|
|||||||
rtems_libio_open_close_args_t *args
|
rtems_libio_open_close_args_t *args
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
my_driver_context *ctx = (my_driver_context *) base;
|
my_driver_context *ctx;
|
||||||
rtems_status_code sc;
|
rtems_status_code sc;
|
||||||
bool ok;
|
bool ok;
|
||||||
|
|
||||||
|
ctx = (my_driver_context *) base;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* You may add some initialization code here.
|
* You may add some initialization code here.
|
||||||
@ -473,22 +360,22 @@ During the first open of the device Termios will call the
|
|||||||
* the boot loader. This function accepts only exact Termios baud
|
* the boot loader. This function accepts only exact Termios baud
|
||||||
* values.
|
* values.
|
||||||
*/
|
*/
|
||||||
sc = rtems_termios_set_initial_baud(tty, MY_DRIVER_BAUD_RATE);
|
sc = rtems_termios_set_initial_baud( tty, MY_DRIVER_BAUD_RATE );
|
||||||
if (sc != RTEMS_SUCCESSFUL) {
|
if ( sc != RTEMS_SUCCESSFUL ) {
|
||||||
/* Not a valid Termios baud */
|
/* Not a valid Termios baud */
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Alternatively you can set the best baud.
|
* Alternatively you can set the best baud.
|
||||||
*/
|
*/
|
||||||
rtems_termios_set_best_baud(term, MY_DRIVER_BAUD_RATE);
|
rtems_termios_set_best_baud( term, MY_DRIVER_BAUD_RATE );
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* To propagate the initial Termios attributes to the device use
|
* To propagate the initial Termios attributes to the device use
|
||||||
* this.
|
* this.
|
||||||
*/
|
*/
|
||||||
ok = my_driver_set_attributes(base, term);
|
ok = my_driver_set_attributes( base, term );
|
||||||
if (!ok) {
|
if ( !ok ) {
|
||||||
/* This is bad */
|
/* This is bad */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -499,14 +386,10 @@ During the first open of the device Termios will call the
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Closing a Serial Device
|
Last Close
|
||||||
-----------------------
|
==========
|
||||||
|
|
||||||
The ``console_close()`` provided by :file:`console-termios.c` is invoked when
|
Termios will call the :c:func:`my_driver_last_close` handler if the last close
|
||||||
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
|
|
||||||
happens on the device.
|
happens on the device.
|
||||||
|
|
||||||
.. code-block:: c
|
.. code-block:: c
|
||||||
@ -517,44 +400,21 @@ happens on the device.
|
|||||||
rtems_libio_open_close_args_t *args
|
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.
|
* 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
|
Termios will call the :c:func:`my_driver_set_attributes` handler if a serial
|
||||||
the serial device is to be read from. This entry point corresponds to the
|
line configuration parameter changed, e.g. baud, character size, number of stop
|
||||||
device driver read entry point.
|
bits, parity, etc.
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
.. code-block:: c
|
.. code-block:: c
|
||||||
|
|
||||||
@ -563,7 +423,9 @@ handler.
|
|||||||
const struct termios *term
|
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
|
* Inspect the termios data structure and configure the device
|
||||||
@ -571,9 +433,111 @@ handler.
|
|||||||
* parts of the structure that specify hardware setting for the
|
* parts of the structure that specify hardware setting for the
|
||||||
* communications channel such as baud, character size, etc.
|
* communications channel such as baud, character size, etc.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return true to indicate a successful set attributes, and false
|
* Return true to indicate a successful set attributes, and false
|
||||||
* otherwise.
|
* otherwise.
|
||||||
*/
|
*/
|
||||||
return true;
|
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;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user