source: rtems-docs/bsp_howto/console.rst @ ed3794e

4.115
Last change on this file since ed3794e was d389819, checked in by Amar Takhar <amar@…>, on 01/18/16 at 05:37:40

Convert all Unicode to ASCII(128)

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