summaryrefslogtreecommitdiffstats
path: root/bsp_howto/console.rst
diff options
context:
space:
mode:
authorAmar Takhar <amar@rtems.org>2016-01-17 00:47:50 -0500
committerAmar Takhar <verm@darkbeer.org>2016-05-02 20:51:23 -0400
commitb35050917272ab536c8f4158e5c002f98a092796 (patch)
tree386dc0be827a10ff668e6d0b2b1ff52a1f49e9ed /bsp_howto/console.rst
parentFix warnings. (diff)
downloadrtems-docs-b35050917272ab536c8f4158e5c002f98a092796.tar.bz2
Split document into seperate files by section.
Diffstat (limited to 'bsp_howto/console.rst')
-rw-r--r--bsp_howto/console.rst552
1 files changed, 552 insertions, 0 deletions
diff --git a/bsp_howto/console.rst b/bsp_howto/console.rst
new file mode 100644
index 0000000..13f7a6c
--- /dev/null
+++ b/bsp_howto/console.rst
@@ -0,0 +1,552 @@
+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:: c
+
+ [...]
+ 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:: 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:: 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. Her is an example header file.
+.. code:: 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:: 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:: 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:: 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:: 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:: 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:: 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:: 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:: 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;
+ }
+
+.. COMMENT: COPYRIGHT (c) 1988-2002.
+
+.. COMMENT: On-Line Applications Research Corporation (OAR).
+
+.. COMMENT: All rights reserved.
+