summaryrefslogtreecommitdiffstats
path: root/bsp-howto/console.rst
diff options
context:
space:
mode:
authorChris Johns <chrisj@rtems.org>2016-11-03 16:58:08 +1100
committerChris Johns <chrisj@rtems.org>2016-11-03 16:58:08 +1100
commit72a62ad88f82fe1ffee50024db4dd0f3fa5806f7 (patch)
tree6b0e527e67141f8126ba56b8a3c1eb90aeed5849 /bsp-howto/console.rst
parentwaf: Use separate doctrees so avoid sphinx clashes. (diff)
downloadrtems-docs-72a62ad88f82fe1ffee50024db4dd0f3fa5806f7.tar.bz2
Rename all manuals with an _ to have a -. It helps released naming of files.
Diffstat (limited to 'bsp-howto/console.rst')
-rw-r--r--bsp-howto/console.rst579
1 files changed, 579 insertions, 0 deletions
diff --git a/bsp-howto/console.rst b/bsp-howto/console.rst
new file mode 100644
index 0000000..bcca519
--- /dev/null
+++ b/bsp-howto/console.rst
@@ -0,0 +1,579 @@
+.. comment SPDX-License-Identifier: CC-BY-SA-4.0
+
+.. COMMENT: COPYRIGHT (c) 1988-2002.
+.. COMMENT: On-Line Applications Research Corporation (OAR).
+.. COMMENT: All rights reserved.
+
+Console Driver
+##############
+
+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.
+
+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:
+
+- console: formatted input/output, with special characters (end of line,
+ tabulations, etc.) recognition and processing,
+
+- raw: permits raw data processing.
+
+One may think that two serial drivers are needed to handle these two types of
+data, but Termios permits having only one driver.
+
+Termios
+=======
+
+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:
+
+.. code-block:: makefile
+
+ [...]
+ libbsp_a_SOURCES += ../../shared/console-termios.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 BSP specific initialization routine ``console_initialize()``, that calls
+ ``rtems_termios_device_install()`` providing a low-level driver context for
+ each installed device.
+
+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.
+
+.. code-block:: c
+
+ #ifndef MY_DRIVER_H
+ #define MY_DRIVER_H
+
+ #include <rtems/termiostypes.h>
+ #include <some-chip-header.h>
+
+ /* Low-level driver specific data structure */
+ typedef struct {
+ rtems_termios_device_context base;
+ const char *device_name;
+ volatile module_register_block *regs;
+ /* More stuff */
+ } my_driver_context;
+
+ extern const rtems_termios_handler my_driver_handler_polled;
+ extern const rtems_termios_handler my_driver_handler_interrupt;
+
+ #endif /* MY_DRIVER_H */
+
+Termios and Polled IO
+---------------------
+
+The following handler are provided by the low-level driver and invoked by
+Termios for simple character IO.
+
+The ``my_driver_poll_write()`` routine is responsible for writing ``n``
+characters from ``buf`` to the serial device specified by ``tty``.
+
+.. code-block:: c
+
+ static void my_driver_poll_write(
+ rtems_termios_device_context *base,
+ const char *buf,
+ size_t n
+ )
+ {
+ my_driver_context *ctx = (my_driver_context *) base;
+ size_t i;
+ /* Write */
+ 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.
+
+.. 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 */
+ if (my_driver_can_read_char(ctx)) {
+ /* Return the character */
+ return my_driver_read_char(ctx);
+ } else {
+ /* Return an error status */
+ return -1;
+ }
+ }
+
+Termios and Interrupt Driven IO
+-------------------------------
+
+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:
+
+.. code-block:: c
+
+ static void my_driver_interrupt_handler(
+ rtems_vector_number vector,
+ void *arg
+ )
+ {
+ rtems_termios_tty *tty = arg;
+ my_driver_context *ctx = rtems_termios_get_device_context(tty);
+ char buf[N];
+ size_t n;
+
+ /*
+ * Check if we have received something. The function reads the
+ * received characters from the device and stores them in the
+ * buffer. It returns the number of read characters.
+ */
+ n = my_driver_read_received_chars(ctx, buf, N);
+ if (n > 0) {
+ /* Hand the data over to the Termios infrastructure */
+ rtems_termios_enqueue_raw_characters(tty, buf, n);
+ }
+
+ /*
+ * Check if we have something transmitted. The functions returns
+ * the number of transmitted characters since the last write to the
+ * device.
+ */
+ n = my_driver_transmitted_chars(ctx);
+ if (n > 0) {
+ /*
+ * Notify Termios that we have transmitted some characters. It
+ * will call now the interrupt write function if more characters
+ * are ready for transmission.
+ */
+ rtems_termios_dequeue_characters(tty, n);
+ }
+ }
+
+The ``my_driver_interrupt_write()`` function 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
+with exactly one character in case of an idle output state or from the
+interrupt handler to refill the transmitter. If the routine is invoked to
+start the transmit process the output state will become busy and Termios starts
+to fill the output buffer. If the transmit interrupt arises before Termios was
+able to fill the transmit buffer you will end up with one interrupt per
+character.
+
+.. code-block:: c
+
+ static void my_driver_interrupt_write(
+ rtems_termios_device_context *base,
+ const char *buf,
+ size_t n
+ )
+ {
+ my_driver_context *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];
+
+ /*
+ * 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;
+ }
+
+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.
+
+.. code-block:: c
+
+ static bool my_driver_first_open(
+ rtems_termios_tty *tty,
+ rtems_termios_device_context *base,
+ struct termios *term,
+ rtems_libio_open_close_args_t *args
+ )
+ {
+ my_driver_context *ctx = (my_driver_context *) base;
+ rtems_status_code sc;
+ bool ok;
+
+ /*
+ * You may add some initialization code here.
+ */
+
+ /*
+ * Sets the initial baud rate. This should be set to the value of
+ * the boot loader. This function accepts only exact Termios baud
+ * values.
+ */
+ sc = rtems_termios_set_initial_baud(tty, MY_DRIVER_BAUD_RATE);
+ if (sc != RTEMS_SUCCESSFUL) {
+ /* Not a valid Termios baud */
+ }
+
+ /*
+ * Alternatively you can set the best baud.
+ */
+ rtems_termios_set_best_baud(term, MY_DRIVER_BAUD_RATE);
+
+ /*
+ * To propagate the initial Termios attributes to the device use
+ * this.
+ */
+ ok = my_driver_set_attributes(base, term);
+ if (!ok) {
+ /* This is bad */
+ }
+
+ /*
+ * Return true to indicate a successful set attributes, and false
+ * otherwise.
+ */
+ return true;
+ }
+
+Closing a Serial Device
+-----------------------
+
+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
+happens on the device.
+
+.. code-block:: c
+
+ static void my_driver_last_close(
+ rtems_termios_tty *tty,
+ rtems_termios_device_context *base,
+ rtems_libio_open_close_args_t *args
+ )
+ {
+ my_driver_context *ctx = (my_driver_context *) base;
+
+ /*
+ * The driver may do some cleanup here.
+ */
+ }
+
+Reading Characters from a Serial Device
+---------------------------------------
+
+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.
+
+.. code-block:: c
+
+ static bool my_driver_set_attributes(
+ rtems_termios_device_context *base,
+ const struct termios *term
+ )
+ {
+ my_driver_context *ctx = (my_driver_context *) base;
+
+ /*
+ * Inspect the termios data structure and configure the device
+ * appropriately. The driver should only be concerned with the
+ * 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;
+ }