diff options
Diffstat (limited to 'bsp_howto/console.rst')
-rw-r--r-- | bsp_howto/console.rst | 579 |
1 files changed, 0 insertions, 579 deletions
diff --git a/bsp_howto/console.rst b/bsp_howto/console.rst deleted file mode 100644 index bcca519..0000000 --- a/bsp_howto/console.rst +++ /dev/null @@ -1,579 +0,0 @@ -.. 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; - } |