source: rtems/doc/bsp_howto/console.t @ 47eab0a

4.9
Last change on this file since 47eab0a was 47eab0a, checked in by Joel Sherrill <joel.sherrill@…>, on 12/11/08 at 15:50:02

2008-12-11 Sebastian Huber <sebastian.huber@…>

Joel Sherrrill <joel.sherrill@…>

  • bsp_howto/Makefile.am, bsp_howto/console.t: Sebastian improved documentation for termios device drivers.
  • bsp_howto/TERMIOSFlow.eps, bsp_howto/TERMIOSFlow.png: New files. Joel added Termios Flow figure from RTEMS Open Class material.
  • Property mode set to 100644
File size: 20.4 KB
Line 
1@c
2@c  COPYRIGHT (c) 1988-2008.
3@c  On-Line Applications Research Corporation (OAR).
4@c  All rights reserved.
5@c
6@c  $Id$
7@c
8
9@chapter Console Driver
10
11@section Introduction
12
13This chapter describes the operation of a console driver using
14the RTEMS POSIX Termios support.  Traditionally RTEMS has referred
15to all serial device drivers as console device drivers.  A
16console driver can be used to do raw data processing in addition
17to the "normal" standard input and output device functions required
18of a console.
19
20The serial driver may be called as the consequence of a C Library
21call such as @code{printf} or @code{scanf} or directly via the
22@code{read} or @code{write} system calls.
23There are two main functioning modes:
24
25@itemize @bullet
26
27@item console: formatted input/output, with special characters (end of
28line, tabulations, etc.) recognition and processing,
29
30@item raw: permits raw data processing.
31
32@end itemize
33
34One may think that two serial drivers are needed to handle these two types
35of data, but Termios permits having only one driver.
36
37@section Termios
38
39Termios is a standard for terminal management, included in the POSIX
401003.1b standard.  As part of the POSIX and Open Group Single UNIX
41Specification, is commonly provided on UNIX implementations.  The
42Open Group has the termios portion of the POSIX standard online
43at @uref{http://opengroup.org/onlinepubs/007908775/xbd/termios.html
44,http://opengroup.org/onlinepubs/007908775/xbd/termios.html}.
45The requirements for the @code{<termios.h>} file are also provided
46and are at @uref{http://opengroup.org/onlinepubs/007908775/xsh/termios.h.html,
47http://opengroup.org/onlinepubs/007908775/xsh/termios.h.html}.
48
49Having RTEMS support for Termios is beneficial because:
50
51@itemize @bullet
52
53@item from the user's side because it provides standard primitive operations
54to access the terminal and change configuration settings.  These operations
55are the same under Unix and Rtems.
56
57@item from the BSP developer's side because it frees the
58developer from dealing with buffer states and mutual exclusions on them.
59Early RTEMS console device drivers also did their own special
60character processing.
61
62@item it is part of an internationally recognized standard.
63
64@item it makes porting code from other environments easier.
65
66@end itemize
67
68Termios support includes:
69
70@itemize @bullet
71
72@item raw and console handling,
73
74@item blocking or non-blocking characters receive, with or without
75Timeout.
76
77@end itemize
78
79At this time, RTEMS documentation does not include a thorough discussion
80of the Termios functionality.  For more information on Termios,
81type @code{man termios} on a Unix box or point a web browser
82at
83@uref{http://www.freebsd.org/cgi/man.cgi}.
84
85@section Driver Functioning Modes
86
87There are generally two main functioning modes for an UART (Universal
88Asynchronous Receiver-Transmitter, i.e. the serial chip):
89
90@itemize @bullet
91
92@item polled mode
93@item interrupt driven mode
94
95@end itemize
96
97In polled mode, the processor blocks on sending/receiving characters.
98This mode is not the most efficient way to utilize the UART. But
99polled mode is usually necessary when one wants to print an
100error message in the event of a fatal error such as a fatal error
101in the BSP.  This is also the simplest mode to
102program.  Polled mode is generally preferred if the serial port is
103to be used primarily as a debug console.  In a simple polled driver,
104the software will continuously check the status of the UART when
105it is reading or writing to the UART.  Termios improves on this
106by delaying the caller for 1 clock tick between successive checks
107of the UART on a read operation.
108
109In interrupt driven mode, the processor does not block on sending/receiving
110characters.  Data is buffered between the interrupt service routine
111and application code.  Two buffers are used to insulate the application
112from the relative slowness of the serial device.  One of the buffers is
113used for incoming characters, while the other is used for outgoing characters.
114
115An interrupt is raised when a character is received by the UART.
116The interrupt subroutine places the incoming character at the end
117of the input buffer.  When an application asks for input,
118the characters at the front of the buffer are returned.
119
120When the application prints to the serial device, the outgoing characters
121are placed at the end of the output buffer.  The driver will place
122one or more characters in the UART (the exact number depends on the UART)
123An interrupt will be raised when all the characters have been transmitted.
124The interrupt service routine has to send the characters
125remaining in the output buffer the same way.   When the transmitting side
126of the UART is idle, it is typically necessary to prime the transmitter
127before the first interrupt will occur.
128
129@section Serial Driver Functioning Overview
130
131The following Figure shows how a Termios driven serial driver works:
132
133@ifset use-ascii
134@center Figure not included in ASCII version
135@end ifset
136
137@ifset use-tex
138@sp1
139@center{@image{TERMIOSFlow,,6in}}
140@end ifset
141
142@ifset use-html
143@html
144<P ALIGN="center"><IMG SRC="TERMIOSFlow.png"
145     WIDTH=800 HEIGHT=610 ALT="Termios Flow"></P>
146@end html
147@end ifset
148
149The most significant five bits are the object class.  The next
150three bits indicate the API to which the object class belongs.
151
152The following list describes the basic flow.
153
154@itemize @bullet
155
156@item the application programmer uses standard C library call (printf,
157scanf, read, write, etc.),
158
159@item C library (e.g. RedHat (formerly Cygnus) Newlib) calls
160the RTEMS system call interface.  This code can be found in the
161@code{cpukit/libcsupport/src} directory.
162
163@item Glue code calls the serial driver entry routines.
164
165@end itemize
166
167@subsection Basics
168
169You need to include the following header files in your Termios device driver
170source file:
171@example
172@group
173#include <unistd.h>
174#include <termios.h>
175
176#include <rtems.h>
177#include <rtems/libio.h>
178#include <rtems/console.h>
179@end group
180@end example
181
182You need to provide a data structure for the Termios driver interface.  The
183functions are described later in this chapter.  The functions should return
184zero on succes and minus one in case of an error.  Currently the return value
185will be not checked from the Termios infrastructure in most cases.  One notable
186exception is the polled read function, here is the return value important.
187
188If you want to use polled IO it should look like the following.  You may also
189have a look at @code{c/src/lib/libbsp/shared/console-polled.c} for a shared
190implementation of the basic framework.  Termios must be told the addresses of
191the functions that are to be used for simple character IO, i.e. pointers to the
192@code{my_driver_poll_read} and @code{my_driver_poll_write} functions described
193later in @ref{Console Driver Termios and Polled IO}.
194
195@example
196@group
197static const rtems_termios_callbacks my_driver_callbacks_polled = @{
198    .firstOpen = my_driver_first_open,
199    .lastClose = my_driver_last_close,
200    .pollRead = my_driver_poll_read,
201    .write = my_driver_poll_write,
202    .setAttributes = my_driver_set_attributes,
203    .stopRemoteTx = NULL,
204    .startRemoteTx = NULL,
205    .outputUsesInterrupts = TERMIOS_POLLED
206@};
207@end group
208@end example
209
210For an interrupt driven implementation you need the following.  The driver
211functioning is quite different in this mode.  There is no device driver read
212function to be passed to Termios.  Indeed a @code{console_read} call returns
213the contents of Termios input buffer.  This buffer is filled in the driver
214interrupt subroutine, see also
215@ref{Console Driver Termios and Interrupt Driven IO}.
216The driver is responsible for providing a pointer to the
217@code{my_driver_interrupt_write} function.
218
219@example
220@group
221static const rtems_termios_callbacks my_driver_callbacks_interrupt = @{
222    .firstOpen = my_driver_first_open,
223    .lastClose = my_driver_last_close,
224    .pollRead = NULL,
225    .write = my_driver_interrupt_write,
226    .setAttributes = my_driver_set_attributes,
227    .stopRemoteTx = NULL,
228    .startRemoteTx = NULL,
229    .outputUsesInterrupts = TERMIOS_IRQ_DRIVEN
230@};
231@end group
232@end example
233
234You can also provide callback functions for remote transmission control.  This
235is not covered in this manual, so thay are set to @code{NULL} in the above
236examples.
237
238Normally the device specific data structures are stored in a table which is
239indexed by the minor number.  You may need an entry for the Termios handler
240pointer in your data structure.  For simplicity of the console initialization
241example the device name is also present.
242
243@example
244@group
245/* Driver specific data structure */
246typedef struct @{
247    const char *device_name;
248    struct rtems_termios_tty *tty;
249@} my_driver_entry;
250
251/*
252 * This table contains the driver specific data.  It is later
253 * indexed by the minor number.
254 */
255static my_driver_entry my_driver_table [MY_DRIVER_DEVICE_NUMBER];
256@end group
257@end example
258
259@subsection Termios and Polled IO
260
261The following functions are provided by the driver and invoked by
262Termios for simple character IO.
263
264The @code{my_driver_poll_write} routine is responsible for writing @code{n}
265characters from @code{buf} to the serial device specified by @code{minor}.
266
267@example
268@group
269static int my_driver_poll_write(int minor, const char *buf, int n)
270@{
271    my_driver_entry *e = &my_driver_table [minor];
272    int i = 0;
273   
274    /*
275     * There is no need to check the minor number since it is derived
276     * from a file descriptor.  The upper layer takes care that it is
277     * in a valid range.
278     */
279   
280    /* Write */
281    for (i = 0; i < n; ++i) @{
282        my_driver_write_char(e, buf [i]);
283    @}
284   
285    return 0;
286@}
287@end group
288@end example
289
290The @code{my_driver_poll_read} routine is responsible for reading a single
291character from the serial device specified by @code{minor}.  If no character is
292available, then the routine should return minus one.
293
294@example
295@group
296static int my_driver_poll_read(int minor)
297@{
298    my_driver_entry *e = &my_driver_table [minor];
299   
300    /*
301     * There is no need to check the minor number since it is derived
302     * from a file descriptor.  The upper layer takes care that it is
303     * in a valid range.
304     */
305
306    /* Check if a character is available */
307    if (my_driver_can_read_char(e)) @{
308        /* Return the character */
309        return my_driver_read_char(e);
310    @} else @{
311        /* Return an error status */
312        return -1;
313    @}
314@}
315@end group
316@end example
317
318@subsection Termios and Interrupt Driven IO
319
320The UART generally generates interrupts when it is ready to accept or to emit a
321number of characters.  In this mode, the interrupt subroutine is the core of
322the driver.
323
324The @code{my_driver_interrupt_handler} is responsible for processing
325asynchronous interrupts from the UART.  There may be multiple interrupt
326handlers for a single UART.  Some UARTs can generate a unique interrupt vector
327for each interrupt source such as a character has been received or the
328transmitter is ready for another character.
329
330In the simplest case, the @code{my_driver_interrupt_handler} will have to check
331the status of the UART and determine what caused the interrupt.  The following
332describes the operation of an @code{my_driver_interrupt_handler} which has to
333do this:
334
335@example
336@group
337static void my_driver_interrupt_handler(
338    rtems_vector_number vector,
339    void *arg
340)
341@{
342    my_driver_entry *e = (my_driver_entry *) arg;
343    char buf [N];
344    int n = 0;
345
346    /*
347     * Check if we have received something.  The function reads the
348     * received characters from the device and stores them in the
349     * buffer.  It returns the number of read characters.
350     */
351    n = my_driver_read_received_chars(e, buf, N);
352    if (n > 0) @{
353        /* Hand the data over to the Termios infrastructure */
354        rtems_termios_enqueue_raw_characters(e->tty, buf, n);
355    @}
356
357    /*
358     * Check if we have something transmitted.  The functions returns
359     * the number of transmitted characters since the last write to the
360     * device.
361     */
362    n = my_driver_transmitted_chars(e);
363    if (n > 0) @{
364        /*
365         * Notify Termios that we have transmitted some characters.  It
366         * will call now the interrupt write function if more characters
367         * are ready for transmission.
368         */
369        rtems_termios_dequeue_characters(e->tty, n);
370    @}
371@}
372@end group
373@end example
374
375The @code{my_driver_interrupt_write} function is responsible for telling the
376device that the @code{n} characters at @code{buf} are to be transmitted.  The
377return value may be arbitrary since it is not checked from Termios.
378
379@example
380@group
381static int my_driver_interrupt_write(int minor, const char *buf, int n)
382@{
383    my_driver_entry *e = &my_driver_table [minor];
384   
385    /*
386     * There is no need to check the minor number since it is derived
387     * from a file descriptor.  The upper layer takes care that it is
388     * in a valid range.
389     */
390
391    /*
392     * Tell the device to transmit some characters from buf (less than
393     * or equal to n).  If the device is finished it should raise an
394     * interrupt.  The interrupt handler will notify Termios that these
395     * characters have been transmitted and this may trigger this write
396     * function again.  You may have to store the number of outstanding
397     * characters in the device data structure.
398     */
399
400    return 0;
401@}
402@end group
403@end example
404
405@subsection Initialization
406
407The driver initialization is called once during the RTEMS initialization
408process.
409
410The @code{console_initialize} function may look like this:
411
412@example
413@group
414rtems_device_driver console_initialize(
415    rtems_device_major_number major,
416    rtems_device_minor_number minor,
417    void *arg
418)
419@{
420    rtems_status_code sc = RTEMS_SUCCESSFUL;
421    rtems_device_minor_number i = 0;
422
423    /*
424     * Initialize the Termios infrastructure.  If Termios has already
425     * been initialized by another device driver, then this call will
426     * have no effect.
427     */
428    rtems_termios_initialize();
429   
430    /* Initialize each device */
431    for (i = 0; i < MY_DRIVER_DEVICE_NUMBER; ++i) @{
432        my_driver_entry *e = &my_driver_table [i];
433
434        /*
435         * Register this device in the file system.  In order to use the
436         * console (i.e. being able to do printf, scanf etc. on stdin,
437         * stdout and stderr), some device must be registered
438         * as "/dev/console" (CONSOLE_DEVICE_NAME).
439         */
440        sc = rtems_io_register_name (e->device_name, major, i);
441        RTEMS_CHECK_SC(sc, "Register IO device");
442
443        /*
444         * Initialize this device and install the interrupt handler if
445         * necessary.  You may also initialize the device in the first
446         * open call.
447         */
448    @}
449
450    return RTEMS_SUCCESSFUL;
451@}
452@end group
453@end example
454
455@subsection Opening a serial device
456
457The @code{console_open} function is called whenever a serial device is opened.
458The device registered as @code{"/dev/console"} (@code{CONSOLE_DEVICE_NAME}) is
459opened automatically during RTEMS initialization.  For instance, if UART
460channel 2 is registered as "/dev/tty1", the @code{console_open} entry point
461will be called as the result of an @code{fopen("/dev/tty1", mode)} in the
462application.
463
464The @code{console_open} function has to inform Termios of the low-level
465functions for serial line support.
466
467@example
468@group
469rtems_device_driver console_open(
470    rtems_device_major_number major,
471    rtems_device_minor_number minor,
472    void *arg
473)
474@{
475    struct rtems_termios_callbacks *callbacks =
476        &my_driver_callbacks_polled;
477
478    /*
479     * Check the minor number.  Termios does currently not check
480     * the return value of the first open call so the minor
481     * number must be checked here.
482     */
483    if (MY_DRIVER_IS_MINOR_INVALID(minor)) @{
484        return RTEMS_INVALID_NUMBER;
485    @}
486
487    /*
488     * Depending on the IO mode you need to pass a different set of
489     * callback functions to Termios.
490     */
491    if (MY_DRIVER_USES_INTERRUPTS(minor)) @{
492        callbacks = &my_driver_callbacks_interrupt;
493    @}
494
495    return rtems_termios_open(major, minor, arg, callbacks);
496@}
497@end group
498@end example
499
500During the first open of the device Termios will call @code{my_driver_first_open}.
501
502@example
503@group
504static int my_driver_first_open(int major, int minor, void *arg)
505@{
506    my_driver_entry *e = &my_driver_table [minor];
507    struct rtems_termios_tty *tty =
508        ((rtems_libio_open_close_args_t *) arg)->iop->data1;
509   
510    /* Check minor number */
511    if (MY_DRIVER_IS_MINOR_INVALID(minor)) @{
512        return -1;
513    @}
514
515    /* Connect the TTY data structure */
516    e->tty = tty;
517
518    /*
519     * You may add some initialization code here.
520     */
521
522    /*
523     * Sets the inital baud rate.  This should be set to the value of
524     * the boot loader.
525     */
526    return rtems_termios_set_initial_baud(e->tty, MY_DRIVER_BAUD_RATE);
527@}
528@end group
529@end example
530
531@subsection Closing a Serial Device
532
533The @code{console_close} is invoked when the serial device is to be closed.
534This entry point corresponds to the device driver close entry point.
535
536This routine is responsible for notifying Termios that the serial device was
537closed.  This is done with a call to @code{rtems_termios_close}.
538
539@example
540@group
541rtems_device_driver console_close(
542    rtems_device_major_number major,
543    rtems_device_minor_number minor,
544    void *arg
545)
546@{
547    return rtems_termios_close(arg);
548@}
549@end group
550@end example
551
552Termios will call the @code{my_driver_last_close} function if the last close
553happens on the device.
554@example
555@group
556static int my_driver_last_close(int major, int minor, void *arg)
557@{
558    my_driver_entry *e = &my_driver_table [minor];
559   
560    /*
561     * There is no need to check the minor number since it is derived
562     * from a file descriptor.  The upper layer takes care that it is
563     * in a valid range.
564     */
565
566    /* Disconnect the TTY data structure */
567    e->tty = NULL;
568
569    /*
570     * The driver may do some cleanup here.
571     */
572
573    return 0;
574@}
575@end group
576@end example
577
578@subsection Reading Characters from a Serial Device
579
580The @code{console_read} is invoked when the serial device is to be read from.
581This entry point corresponds to the device driver read entry point.
582
583This routine is responsible for returning the content of the Termios input
584buffer.  This is done by invoking the @code{rtems_termios_read} routine.
585
586@example
587@group
588rtems_device_driver console_read(
589    rtems_device_major_number major,
590    rtems_device_minor_number minor,
591    void *arg
592)
593@{
594    return rtems_termios_read(arg);
595@}
596@end group
597@end example
598
599@subsection Writing Characters to a Serial Device
600
601The @code{console_write} is invoked when the serial device is to be written to.
602This entry point corresponds to the device driver write entry point.
603
604This routine is responsible for adding the requested characters to the Termios
605output queue for this device.  This is done by calling the routine
606@code{rtems_termios_write} to add the characters at the end of the Termios
607output buffer.
608
609@example
610@group
611rtems_device_driver console_write(
612    rtems_device_major_number major,
613    rtems_device_minor_number minor,
614    void *arg
615)
616@{
617    return rtems_termios_write(arg);
618@}
619@end group
620@end example
621
622@subsection Changing Serial Line Parameters
623
624The @code{console_control} is invoked when the line parameters for a particular
625serial device are to be changed.  This entry point corresponds to the device
626driver io_control entry point.
627
628The application writer is able to control the serial line configuration with
629Termios calls (such as the @code{ioctl} command, see the Termios documentation
630for more details).  If the driver is to support dynamic configuration, then it
631must have the @code{console_control} piece of code.  Basically @code{ioctl}
632commands call @code{console_control} with the serial line configuration in a
633Termios defined data structure.
634
635@example
636@group
637rtems_device_driver console_control(
638    rtems_device_major_number major,
639    rtems_device_minor_number minor,
640    void *arg
641)
642@{
643    return rtems_termios_ioctl(arg);
644@}
645@end group
646@end example
647
648The driver is responsible for reinitializing the device with the correct
649settings.  For this purpuse Termios calls the @code{my_driver_set_attributes}
650function.
651
652@example
653@group
654static int my_driver_set_attributes(
655    int minor,
656    const struct termios *t
657)
658@{
659    my_driver_entry *e = &my_driver_table [minor];
660   
661    /*
662     * There is no need to check the minor number since it is derived
663     * from a file descriptor.  The upper layer takes care that it is
664     * in a valid range.
665     */
666
667    /*
668     * Inspect the termios data structure and configure the device
669     * appropriately.  The driver should only be concerned with the
670     * parts of the structure that specify hardware setting for the
671     * communications channel such as baud, character size, etc.
672     */
673
674    return 0;
675@}
676@end group
677@end example
Note: See TracBrowser for help on using the repository browser.