source: rtems-docs/bsp_howto/console.rst @ 6d7a4d2

4.115
Last change on this file since 6d7a4d2 was 6d7a4d2, checked in by Chris Johns <chrisj@…>, on 06/17/16 at 05:05:41

Update the BSP howto.

Closes #2590.

  • Property mode set to 100644
File size: 20.1 KB
Line 
1.. comment SPDX-License-Identifier: CC-BY-SA-4.0
2
3.. COMMENT: COPYRIGHT (c) 1988-2002.
4.. COMMENT: On-Line Applications Research Corporation (OAR).
5.. COMMENT: All rights reserved.
6
7Console Driver
8##############
9
10Introduction
11============
12
13This chapter describes the operation of a console driver using the RTEMS POSIX
14Termios support.  Traditionally RTEMS has referred to all serial device drivers
15as console device drivers.  A console driver can be used to do raw data
16processing in addition to the "normal" standard input and output device
17functions required of a console.
18
19The serial driver may be called as the consequence of a C Library call such as
20``printf`` or ``scanf`` or directly via the``read`` or ``write`` system calls.
21There are two main functioning modes:
22
23- console: formatted input/output, with special characters (end of line,
24  tabulations, etc.) recognition and processing,
25
26- raw: permits raw data processing.
27
28One may think that two serial drivers are needed to handle these two types of
29data, but Termios permits having only one driver.
30
31Termios
32=======
33
34Termios is a standard for terminal management, included in the POSIX 1003.1b
35standard.  As part of the POSIX and Open Group Single UNIX Specification, is
36commonly provided on UNIX implementations.  The Open Group has the termios
37portion of the POSIX standard online at
38http://opengroup.org/onlinepubs/007908775/xbd/termios.html.  The requirements
39for the ``<termios.h>`` file are also provided and are at
40http://opengroup.org/onlinepubs/007908775/xsh/termios.h.html.
41
42Having RTEMS support for Termios is beneficial because:
43
44- from the user's side because it provides standard primitive operations to
45  access the terminal and change configuration settings.  These operations are
46  the same under UNIX and RTEMS.
47
48- from the BSP developer's side because it frees the developer from dealing
49  with buffer states and mutual exclusions on them.  Early RTEMS console device
50  drivers also did their own special character processing.
51
52- it is part of an internationally recognized standard.
53
54- it makes porting code from other environments easier.
55
56Termios support includes:
57
58- raw and console handling,
59
60- blocking or non-blocking characters receive, with or without Timeout.
61
62At this time, RTEMS documentation does not include a thorough discussion of the
63Termios functionality.  For more information on Termios, type ``man termios``
64on a Unix box or point a web browser athttp://www.freebsd.org/cgi/man.cgi.
65
66Driver Functioning Modes
67========================
68
69There are generally three main functioning modes for an UART (Universal
70Asynchronous Receiver-Transmitter, i.e. the serial chip):
71
72- polled mode
73
74- interrupt driven mode
75
76- task driven mode
77
78In polled mode, the processor blocks on sending/receiving characters.  This
79mode is not the most efficient way to utilize the UART. But polled mode is
80usually necessary when one wants to print an error message in the event of a
81fatal error such as a fatal error in the BSP.  This is also the simplest mode
82to program.  Polled mode is generally preferred if the serial port is to be
83used primarily as a debug console.  In a simple polled driver, the software
84will continuously check the status of the UART when it is reading or writing to
85the UART.  Termios improves on this by delaying the caller for 1 clock tick
86between successive checks of the UART on a read operation.
87
88In interrupt driven mode, the processor does not block on sending/receiving
89characters.  Data is buffered between the interrupt service routine and
90application code.  Two buffers are used to insulate the application from the
91relative slowness of the serial device.  One of the buffers is used for
92incoming characters, while the other is used for outgoing characters.
93
94An interrupt is raised when a character is received by the UART.  The interrupt
95subroutine places the incoming character at the end of the input buffer.  When
96an application asks for input, the characters at the front of the buffer are
97returned.
98
99When the application prints to the serial device, the outgoing characters are
100placed at the end of the output buffer.  The driver will place one or more
101characters in the UART (the exact number depends on the UART) An interrupt will
102be raised when all the characters have been transmitted.  The interrupt service
103routine has to send the characters remaining in the output buffer the same way.
104When the transmitting side of the UART is idle, it is typically necessary to
105prime the transmitter before the first interrupt will occur.
106
107The task driven mode is similar to interrupt driven mode, but the actual data
108processing is done in dedicated tasks instead of interrupt routines.
109
110Serial Driver Functioning Overview
111==================================
112
113The following Figure shows how a Termios driven serial driver works: Figure not
114included in ASCII version
115
116The following list describes the basic flow.
117
118- the application programmer uses standard C library call (printf, scanf, read,
119  write, etc.),
120
121- C library (ctx.g. RedHat (formerly Cygnus) Newlib) calls the RTEMS system
122  call interface.  This code can be found in the:file:`cpukit/libcsupport/src`
123  directory.
124
125- Glue code calls the serial driver entry routines.
126
127Basics
128------
129
130The low-level driver API changed between RTEMS 4.10 and RTEMS 4.11.  The legacy
131callback API is still supported, but its use is discouraged.  The following
132functions are deprecated:
133
134- ``rtems_termios_open()`` - use ``rtems_termios_device_open()`` in combination
135  with ``rtems_termios_device_install()`` instead.
136
137- ``rtems_termios_close()`` - use ``rtems_termios_device_close()`` instead.
138
139This manual describes the new API.  A new console driver should consist of
140three parts.
141
142- The basic console driver functions using the Termios support.  Add this the
143  BSPs Makefile.am:
144
145.. code-block:: makefile
146
147      [...]
148      libbsp_a_SOURCES += ../../shared/console-termios.c
149      [...]
150
151- A general serial module specific low-level driver providing the handler table
152  for the Termios ``rtems_termios_device_install()`` function.  This low-level
153  driver could be used for more than one BSP.
154
155- A BSP specific initialization routine ``console_initialize()``, that calls
156  ``rtems_termios_device_install()`` providing a low-level driver context for
157  each installed device.
158
159You need to provide a device handler structure for the Termios device
160interface.  The functions are described later in this chapter.  The first open
161and set attributes handler return a boolean status to indicate success (true)
162or failure (false).  The polled read function returns an unsigned character in
163case one is available or minus one otherwise.
164
165If you want to use polled IO it should look like the following.  Termios must
166be told the addresses of the handler that are to be used for simple character
167IO, i.e. pointers to the ``my_driver_poll_read()`` and
168``my_driver_poll_write()`` functions described later in `Termios and Polled
169IO`_.
170
171.. code-block:: c
172
173    const rtems_termios_handler my_driver_handler_polled = {
174      .first_open = my_driver_first_open,
175      .last_close = my_driver_last_close,
176      .poll_read = my_driver_poll_read,
177      .write = my_driver_poll_write,
178      .set_attributes = my_driver_set_attributes,
179      .stop_remote_tx = NULL,
180      .start_remote_tx = NULL,
181      .mode = TERMIOS_POLLED
182    }
183
184For an interrupt driven implementation you need the following.  The driver
185functioning is quite different in this mode.  There is no device driver read
186handler to be passed to Termios.  Indeed a ``console_read()`` call returns the
187contents of Termios input buffer.  This buffer is filled in the driver
188interrupt subroutine, see also `Termios and Interrupt Driven IO`_.  The driver
189is responsible for providing a pointer to the``my_driver_interrupt_write()``
190function.
191
192.. code-block:: c
193
194    const rtems_termios_handler my_driver_handler_interrupt = {
195      .first_open = my_driver_first_open,
196      .last_close = my_driver_last_close,
197      .poll_read = NULL,
198      .write = my_driver_interrupt_write,
199      .set_attributes = my_driver_set_attributes,
200      .stopRemoteTx = NULL,
201      .stop_remote_tx = NULL,
202      .start_remote_tx = NULL,
203      .mode = TERMIOS_IRQ_DRIVEN
204    };
205
206You can also provide hander for remote transmission control.  This is not
207covered in this manual, so they are set to ``NULL`` in the above examples.
208
209The low-level driver should provide a data structure for its device context.
210The initialization routine must provide a context for each installed device via
211``rtems_termios_device_install()``.  For simplicity of the console
212initialization example the device name is also present.  Here is an example
213header file.
214
215.. code-block:: c
216
217    #ifndef MY_DRIVER_H
218    #define MY_DRIVER_H
219
220    #include <rtems/termiostypes.h>
221    #include <some-chip-header.h>
222
223    /* Low-level driver specific data structure \*/
224    typedef struct {
225      rtems_termios_device_context base;
226      const char \*device_name;
227      volatile module_register_block \*regs;
228      /* More stuff \*/
229    } my_driver_context;
230
231    extern const rtems_termios_handler my_driver_handler_polled;
232    extern const rtems_termios_handler my_driver_handler_interrupt;
233
234    #endif /* MY_DRIVER_H \*/
235
236Termios and Polled IO
237---------------------
238
239The following handler are provided by the low-level driver and invoked by
240Termios for simple character IO.
241
242The ``my_driver_poll_write()`` routine is responsible for writing ``n``
243characters from ``buf`` to the serial device specified by ``tty``.
244
245.. code-block:: c
246
247    static void my_driver_poll_write(
248      rtems_termios_device_context *base,
249      const char                   *buf,
250      size_t                        n
251    )
252    {
253      my_driver_context *ctx = (my_driver_context *) base;
254      size_t i;
255      /* Write */
256      for (i = 0; i < n; ++i) {
257        my_driver_write_char(ctx, buf[i]);
258      }
259    }
260
261The ``my_driver_poll_read`` routine is responsible for reading a single
262character from the serial device specified by ``tty``.  If no character is
263available, then the routine should return minus one.
264
265.. code-block:: c
266
267    static int my_driver_poll_read(rtems_termios_device_context *base)
268    {
269      my_driver_context *ctx = (my_driver_context *) base;
270      /* Check if a character is available */
271      if (my_driver_can_read_char(ctx)) {
272        /* Return the character */
273        return my_driver_read_char(ctx);
274      } else {
275        /* Return an error status */
276        return -1;
277      }
278    }
279
280Termios and Interrupt Driven IO
281-------------------------------
282
283The UART generally generates interrupts when it is ready to accept or to emit a
284number of characters.  In this mode, the interrupt subroutine is the core of
285the driver.
286
287The ``my_driver_interrupt_handler()`` is responsible for processing
288asynchronous interrupts from the UART.  There may be multiple interrupt
289handlers for a single UART.  Some UARTs can generate a unique interrupt vector
290for each interrupt source such as a character has been received or the
291transmitter is ready for another character.
292
293In the simplest case, the ``my_driver_interrupt_handler()`` will have to check
294the status of the UART and determine what caused the interrupt.  The following
295describes the operation of an ``my_driver_interrupt_handler`` which has to do
296this:
297
298.. code-block:: c
299
300    static void my_driver_interrupt_handler(
301      rtems_vector_number  vector,
302      void                *arg
303    )
304    {
305      rtems_termios_tty *tty = arg;
306      my_driver_context *ctx = rtems_termios_get_device_context(tty);
307      char buf[N];
308      size_t n;
309
310      /*
311       * Check if we have received something.  The function reads the
312       * received characters from the device and stores them in the
313       * buffer.  It returns the number of read characters.
314       */
315      n = my_driver_read_received_chars(ctx, buf, N);
316      if (n > 0) {
317        /* Hand the data over to the Termios infrastructure */
318        rtems_termios_enqueue_raw_characters(tty, buf, n);
319      }
320
321      /*
322       * Check if we have something transmitted.  The functions returns
323       * the number of transmitted characters since the last write to the
324       * device.
325       */
326      n = my_driver_transmitted_chars(ctx);
327      if (n > 0) {
328        /*
329         * Notify Termios that we have transmitted some characters.  It
330         * will call now the interrupt write function if more characters
331         * are ready for transmission.
332         */
333        rtems_termios_dequeue_characters(tty, n);
334      }
335    }
336
337The ``my_driver_interrupt_write()`` function is responsible for telling the
338device that the ``n`` characters at ``buf`` are to be transmitted.  It the
339value ``n`` is zero to indicate that no more characters are to send.  The
340driver can disable the transmit interrupts now.  This routine is invoked either
341from task context with disabled interrupts to start a new transmission process
342with exactly one character in case of an idle output state or from the
343interrupt handler to refill the transmitter.  If the routine is invoked to
344start the transmit process the output state will become busy and Termios starts
345to fill the output buffer.  If the transmit interrupt arises before Termios was
346able to fill the transmit buffer you will end up with one interrupt per
347character.
348
349.. code-block:: c
350
351    static void my_driver_interrupt_write(
352      rtems_termios_device_context  *base,
353      const char                    *buf,
354      size_t                         n
355    )
356    {
357      my_driver_context *ctx = (my_driver_context *) base;
358
359      /*
360       * Tell the device to transmit some characters from buf (less than
361       * or equal to n).  When the device is finished it should raise an
362       * interrupt.  The interrupt handler will notify Termios that these
363       * characters have been transmitted and this may trigger this write
364       * function again.  You may have to store the number of outstanding
365       * characters in the device data structure.
366       */
367      /*
368       * Termios will set n to zero to indicate that the transmitter is
369       * now inactive.  The output buffer is empty in this case.  The
370       * driver may disable the transmit interrupts now.
371       */
372    }
373
374Initialization
375--------------
376
377The BSP specific driver initialization is called once during the RTEMS
378initialization process.
379
380The ``console_initialize()`` function may look like this:
381
382.. code-block:: c
383
384    #include <my-driver.h>
385    #include <rtems/console.h>
386    #include <bsp.h>
387    #include <bsp/fatal.h>
388
389    static my_driver_context driver_context_table[M] = { /* Some values */ };
390
391    rtems_device_driver console_initialize(
392      rtems_device_major_number  major,
393      rtems_device_minor_number  minor,
394      void                      *arg
395    )
396    {
397      rtems_status_code sc;
398      #ifdef SOME_BSP_USE_INTERRUPTS
399        const rtems_termios_handler *handler = &my_driver_handler_interrupt;
400      #else
401        const rtems_termios_handler *handler = &my_driver_handler_polled;
402      #endif
403
404      /*
405       * Initialize the Termios infrastructure.  If Termios has already
406       * been initialized by another device driver, then this call will
407       * have no effect.
408       */
409      rtems_termios_initialize();
410
411      /* Initialize each device */
412      for (
413        minor = 0;
414        minor < RTEMS_ARRAY_SIZE(driver_context_table);
415        ++minor
416      ) {
417        my_driver_context *ctx = &driver_context_table[minor];
418
419        /*
420         * Install this device in the file system and Termios.  In order
421         * to use the console (i.e. being able to do printf, scanf etc.
422         * on stdin, stdout and stderr), one device must be registered as
423         * "/dev/console" (CONSOLE_DEVICE_NAME).
424         */
425        sc = rtems_termios_device_install(
426          ctx->device_name,
427          major,
428          minor,
429          handler,
430          NULL,
431          ctx
432        );
433        if (sc != RTEMS_SUCCESSFUL) {
434          bsp_fatal(SOME_BSP_FATAL_CONSOLE_DEVICE_INSTALL);
435        }
436      }
437
438      return RTEMS_SUCCESSFUL;
439    }
440
441Opening a serial device
442-----------------------
443
444The ``console_open()`` function provided by :file:`console-termios.c` is called
445whenever a serial device is opened.  The device registered as
446``"/dev/console"`` (``CONSOLE_DEVICE_NAME``) is opened automatically during
447RTEMS initialization.  For instance, if UART channel 2 is registered as
448``"/dev/tty1"``, the ``console_open()`` entry point will be called as the
449result of an ``fopen("/dev/tty1", mode)`` in the application.
450
451During the first open of the device Termios will call the
452``my_driver_first_open()`` handler.
453
454.. code-block:: c
455
456    static bool my_driver_first_open(
457      rtems_termios_tty             *tty,
458      rtems_termios_device_context  *base,
459      struct termios                *term,
460      rtems_libio_open_close_args_t *args
461    )
462    {
463      my_driver_context *ctx = (my_driver_context *) base;
464      rtems_status_code sc;
465      bool ok;
466
467      /*
468       * You may add some initialization code here.
469       */
470
471      /*
472       * Sets the initial baud rate.  This should be set to the value of
473       * the boot loader.  This function accepts only exact Termios baud
474       * values.
475       */
476      sc = rtems_termios_set_initial_baud(tty, MY_DRIVER_BAUD_RATE);
477      if (sc != RTEMS_SUCCESSFUL) {
478        /* Not a valid Termios baud */
479      }
480
481      /*
482       * Alternatively you can set the best baud.
483       */
484      rtems_termios_set_best_baud(term, MY_DRIVER_BAUD_RATE);
485
486      /*
487       * To propagate the initial Termios attributes to the device use
488       * this.
489      */
490      ok = my_driver_set_attributes(base, term);
491      if (!ok) {
492        /* This is bad */
493      }
494
495      /*
496       * Return true to indicate a successful set attributes, and false
497       * otherwise.
498       */
499      return true;
500    }
501
502Closing a Serial Device
503-----------------------
504
505The ``console_close()`` provided by :file:`console-termios.c` is invoked when
506the serial device is to be closed.  This entry point corresponds to the device
507driver close entry point.
508
509Termios will call the ``my_driver_last_close()`` handler if the last close
510happens on the device.
511
512.. code-block:: c
513
514    static void my_driver_last_close(
515      rtems_termios_tty             *tty,
516      rtems_termios_device_context  *base,
517      rtems_libio_open_close_args_t *args
518    )
519    {
520      my_driver_context *ctx = (my_driver_context *) base;
521
522      /*
523       * The driver may do some cleanup here.
524      */
525    }
526
527Reading Characters from a Serial Device
528---------------------------------------
529
530The ``console_read()`` provided by :file:`console-termios.c` is invoked when
531the serial device is to be read from.  This entry point corresponds to the
532device driver read entry point.
533
534Writing Characters to a Serial Device
535-------------------------------------
536
537The ``console_write()`` provided by :file:`console-termios.c` is invoked when
538the serial device is to be written to.  This entry point corresponds to the
539device driver write entry point.
540
541Changing Serial Line Parameters
542-------------------------------
543
544The ``console_control()`` provided by :file:`console-termios.c` is invoked when
545the line parameters for a particular serial device are to be changed.  This
546entry point corresponds to the device driver IO control entry point.
547
548The application writer is able to control the serial line configuration with
549Termios calls (such as the ``ioctl()`` command, see the Termios documentation
550for more details).  If the driver is to support dynamic configuration, then it
551must have the ``console_control()`` piece of code.  Basically ``ioctl()``
552commands call ``console_control()`` with the serial line configuration in a
553Termios defined data structure.
554
555The driver is responsible for reinitializing the device with the correct
556settings.  For this purpose Termios calls the ``my_driver_set_attributes()``
557handler.
558
559.. code-block:: c
560
561    static bool my_driver_set_attributes(
562      rtems_termios_device_context *base,
563      const struct termios         *term
564    )
565    {
566      my_driver_context *ctx = (my_driver_context *) base;
567
568      /*
569       * Inspect the termios data structure and configure the device
570       * appropriately.  The driver should only be concerned with the
571       * parts of the structure that specify hardware setting for the
572       * communications channel such as baud, character size, etc.
573       */
574      /*
575       * Return true to indicate a successful set attributes, and false
576       * otherwise.
577       */
578      return true;
579    }
Note: See TracBrowser for help on using the repository browser.