source: rtems/doc/bsp_howto/console.t @ f4b6dc0

4.9
Last change on this file since f4b6dc0 was f4b6dc0, checked in by Joel Sherrill <joel.sherrill@…>, on 12/12/08 at 14:03:28

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

  • bsp_howto/console.t: Remove size constraints on image. Fix a sentence.
  • Property mode set to 100644
File size: 20.3 KB
RevLine 
[f96e8ee]1@c
[47eab0a]2@c  COPYRIGHT (c) 1988-2008.
[f96e8ee]3@c  On-Line Applications Research Corporation (OAR).
4@c  All rights reserved.
5@c
6@c  $Id$
7@c
8
[abfbfa7]9@chapter Console Driver
[07b3693f]10
[abfbfa7]11@section Introduction
[07b3693f]12
[bc950e87]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:
[07b3693f]24
25@itemize @bullet
26
27@item console: formatted input/output, with special characters (end of
[bc950e87]28line, tabulations, etc.) recognition and processing,
[07b3693f]29
30@item raw: permits raw data processing.
31
32@end itemize
33
[183369d]34One may think that two serial drivers are needed to handle these two types
[07b3693f]35of data, but Termios permits having only one driver.
36
[abfbfa7]37@section Termios
[07b3693f]38
[47eab0a]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:
[07b3693f]50
51@itemize @bullet
52
[bc950e87]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.
[07b3693f]56
[bc950e87]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.
[07b3693f]61
[47eab0a]62@item it is part of an internationally recognized standard.
63
64@item it makes porting code from other environments easier.
65
[07b3693f]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
[bc950e87]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
[b406ad20]83@uref{http://www.freebsd.org/cgi/man.cgi}.
[07b3693f]84
[abfbfa7]85@section Driver Functioning Modes
[07b3693f]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
[bc950e87]92@item polled mode
93@item interrupt driven mode
[07b3693f]94
95@end itemize
96
[bc950e87]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
[183369d]100error message in the event of a fatal error such as a fatal error
[bc950e87]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
[abfbfa7]129@section Serial Driver Functioning Overview
[07b3693f]130
[bc950e87]131The following Figure shows how a Termios driven serial driver works:
132
[47eab0a]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
[f4b6dc0]144<P ALIGN="center"><IMG SRC="TERMIOSFlow.png" ALT="Termios Flow"></P>
[47eab0a]145@end html
146@end ifset
147
[bc950e87]148The following list describes the basic flow.
[07b3693f]149
150@itemize @bullet
151
152@item the application programmer uses standard C library call (printf,
153scanf, read, write, etc.),
154
[47eab0a]155@item C library (e.g. RedHat (formerly Cygnus) Newlib) calls
156the RTEMS system call interface.  This code can be found in the
157@code{cpukit/libcsupport/src} directory.
[07b3693f]158
[bc950e87]159@item Glue code calls the serial driver entry routines.
[07b3693f]160
161@end itemize
162
[47eab0a]163@subsection Basics
[07b3693f]164
[47eab0a]165You need to include the following header files in your Termios device driver
166source file:
167@example
168@group
169#include <unistd.h>
170#include <termios.h>
[07b3693f]171
[47eab0a]172#include <rtems.h>
173#include <rtems/libio.h>
174#include <rtems/console.h>
175@end group
176@end example
177
178You need to provide a data structure for the Termios driver interface.  The
179functions are described later in this chapter.  The functions should return
180zero on succes and minus one in case of an error.  Currently the return value
181will be not checked from the Termios infrastructure in most cases.  One notable
182exception is the polled read function, here is the return value important.
[07b3693f]183
[47eab0a]184If you want to use polled IO it should look like the following.  You may also
185have a look at @code{c/src/lib/libbsp/shared/console-polled.c} for a shared
186implementation of the basic framework.  Termios must be told the addresses of
187the functions that are to be used for simple character IO, i.e. pointers to the
188@code{my_driver_poll_read} and @code{my_driver_poll_write} functions described
189later in @ref{Console Driver Termios and Polled IO}.
[07b3693f]190
191@example
[bc950e87]192@group
[47eab0a]193static const rtems_termios_callbacks my_driver_callbacks_polled = @{
194    .firstOpen = my_driver_first_open,
195    .lastClose = my_driver_last_close,
196    .pollRead = my_driver_poll_read,
197    .write = my_driver_poll_write,
198    .setAttributes = my_driver_set_attributes,
199    .stopRemoteTx = NULL,
200    .startRemoteTx = NULL,
201    .outputUsesInterrupts = TERMIOS_POLLED
202@};
[bc950e87]203@end group
[07b3693f]204@end example
205
[47eab0a]206For an interrupt driven implementation you need the following.  The driver
207functioning is quite different in this mode.  There is no device driver read
208function to be passed to Termios.  Indeed a @code{console_read} call returns
209the contents of Termios input buffer.  This buffer is filled in the driver
210interrupt subroutine, see also
211@ref{Console Driver Termios and Interrupt Driven IO}.
212The driver is responsible for providing a pointer to the
213@code{my_driver_interrupt_write} function.
[bc950e87]214
215@example
216@group
[47eab0a]217static const rtems_termios_callbacks my_driver_callbacks_interrupt = @{
218    .firstOpen = my_driver_first_open,
219    .lastClose = my_driver_last_close,
220    .pollRead = NULL,
221    .write = my_driver_interrupt_write,
222    .setAttributes = my_driver_set_attributes,
223    .stopRemoteTx = NULL,
224    .startRemoteTx = NULL,
225    .outputUsesInterrupts = TERMIOS_IRQ_DRIVEN
226@};
[bc950e87]227@end group
228@end example
[07b3693f]229
[47eab0a]230You can also provide callback functions for remote transmission control.  This
231is not covered in this manual, so thay are set to @code{NULL} in the above
232examples.
[07b3693f]233
[47eab0a]234Normally the device specific data structures are stored in a table which is
235indexed by the minor number.  You may need an entry for the Termios handler
236pointer in your data structure.  For simplicity of the console initialization
237example the device name is also present.
[bc950e87]238
[47eab0a]239@example
240@group
241/* Driver specific data structure */
242typedef struct @{
243    const char *device_name;
244    struct rtems_termios_tty *tty;
245@} my_driver_entry;
246
247/*
248 * This table contains the driver specific data.  It is later
249 * indexed by the minor number.
250 */
251static my_driver_entry my_driver_table [MY_DRIVER_DEVICE_NUMBER];
252@end group
253@end example
[bc950e87]254
[47eab0a]255@subsection Termios and Polled IO
256
257The following functions are provided by the driver and invoked by
258Termios for simple character IO.
[bc950e87]259
[47eab0a]260The @code{my_driver_poll_write} routine is responsible for writing @code{n}
261characters from @code{buf} to the serial device specified by @code{minor}.
[07b3693f]262
263@example
[bc950e87]264@group
[47eab0a]265static int my_driver_poll_write(int minor, const char *buf, int n)
[bc950e87]266@{
[47eab0a]267    my_driver_entry *e = &my_driver_table [minor];
268    int i = 0;
269   
270    /*
271     * There is no need to check the minor number since it is derived
272     * from a file descriptor.  The upper layer takes care that it is
273     * in a valid range.
274     */
275   
276    /* Write */
277    for (i = 0; i < n; ++i) @{
278        my_driver_write_char(e, buf [i]);
279    @}
280   
281    return 0;
[bc950e87]282@}
283@end group
[07b3693f]284@end example
285
[47eab0a]286The @code{my_driver_poll_read} routine is responsible for reading a single
287character from the serial device specified by @code{minor}.  If no character is
288available, then the routine should return minus one.
[07b3693f]289
290@example
[47eab0a]291@group
292static int my_driver_poll_read(int minor)
[bc950e87]293@{
[47eab0a]294    my_driver_entry *e = &my_driver_table [minor];
295   
296    /*
297     * There is no need to check the minor number since it is derived
298     * from a file descriptor.  The upper layer takes care that it is
299     * in a valid range.
300     */
301
302    /* Check if a character is available */
303    if (my_driver_can_read_char(e)) @{
304        /* Return the character */
305        return my_driver_read_char(e);
306    @} else @{
307        /* Return an error status */
308        return -1;
309    @}
[bc950e87]310@}
[47eab0a]311@end group
[07b3693f]312@end example
313
[47eab0a]314@subsection Termios and Interrupt Driven IO
[07b3693f]315
[47eab0a]316The UART generally generates interrupts when it is ready to accept or to emit a
317number of characters.  In this mode, the interrupt subroutine is the core of
318the driver.
[07b3693f]319
[47eab0a]320The @code{my_driver_interrupt_handler} is responsible for processing
321asynchronous interrupts from the UART.  There may be multiple interrupt
322handlers for a single UART.  Some UARTs can generate a unique interrupt vector
323for each interrupt source such as a character has been received or the
324transmitter is ready for another character.
[07b3693f]325
[47eab0a]326In the simplest case, the @code{my_driver_interrupt_handler} will have to check
327the status of the UART and determine what caused the interrupt.  The following
328describes the operation of an @code{my_driver_interrupt_handler} which has to
329do this:
[07b3693f]330
[47eab0a]331@example
332@group
333static void my_driver_interrupt_handler(
334    rtems_vector_number vector,
335    void *arg
336)
337@{
338    my_driver_entry *e = (my_driver_entry *) arg;
339    char buf [N];
340    int n = 0;
341
342    /*
343     * Check if we have received something.  The function reads the
344     * received characters from the device and stores them in the
345     * buffer.  It returns the number of read characters.
346     */
347    n = my_driver_read_received_chars(e, buf, N);
348    if (n > 0) @{
349        /* Hand the data over to the Termios infrastructure */
350        rtems_termios_enqueue_raw_characters(e->tty, buf, n);
351    @}
352
353    /*
354     * Check if we have something transmitted.  The functions returns
355     * the number of transmitted characters since the last write to the
356     * device.
357     */
358    n = my_driver_transmitted_chars(e);
359    if (n > 0) @{
360        /*
361         * Notify Termios that we have transmitted some characters.  It
362         * will call now the interrupt write function if more characters
363         * are ready for transmission.
364         */
365        rtems_termios_dequeue_characters(e->tty, n);
366    @}
367@}
368@end group
369@end example
[07b3693f]370
[47eab0a]371The @code{my_driver_interrupt_write} function is responsible for telling the
372device that the @code{n} characters at @code{buf} are to be transmitted.  The
373return value may be arbitrary since it is not checked from Termios.
[07b3693f]374
375@example
[47eab0a]376@group
377static int my_driver_interrupt_write(int minor, const char *buf, int n)
378@{
379    my_driver_entry *e = &my_driver_table [minor];
380   
381    /*
382     * There is no need to check the minor number since it is derived
383     * from a file descriptor.  The upper layer takes care that it is
384     * in a valid range.
385     */
386
387    /*
388     * Tell the device to transmit some characters from buf (less than
389     * or equal to n).  If the device is finished it should raise an
390     * interrupt.  The interrupt handler will notify Termios that these
391     * characters have been transmitted and this may trigger this write
392     * function again.  You may have to store the number of outstanding
393     * characters in the device data structure.
394     */
395
396    return 0;
397@}
398@end group
[07b3693f]399@end example
400
[47eab0a]401@subsection Initialization
402
403The driver initialization is called once during the RTEMS initialization
404process.
[07b3693f]405
[47eab0a]406The @code{console_initialize} function may look like this:
[07b3693f]407
408@example
[47eab0a]409@group
410rtems_device_driver console_initialize(
411    rtems_device_major_number major,
412    rtems_device_minor_number minor,
413    void *arg
414)
415@{
416    rtems_status_code sc = RTEMS_SUCCESSFUL;
417    rtems_device_minor_number i = 0;
418
419    /*
420     * Initialize the Termios infrastructure.  If Termios has already
421     * been initialized by another device driver, then this call will
422     * have no effect.
423     */
424    rtems_termios_initialize();
425   
426    /* Initialize each device */
427    for (i = 0; i < MY_DRIVER_DEVICE_NUMBER; ++i) @{
428        my_driver_entry *e = &my_driver_table [i];
429
430        /*
431         * Register this device in the file system.  In order to use the
432         * console (i.e. being able to do printf, scanf etc. on stdin,
433         * stdout and stderr), some device must be registered
434         * as "/dev/console" (CONSOLE_DEVICE_NAME).
435         */
436        sc = rtems_io_register_name (e->device_name, major, i);
437        RTEMS_CHECK_SC(sc, "Register IO device");
438
439        /*
440         * Initialize this device and install the interrupt handler if
441         * necessary.  You may also initialize the device in the first
442         * open call.
443         */
444    @}
445
446    return RTEMS_SUCCESSFUL;
447@}
448@end group
[07b3693f]449@end example
450
[3351d41]451@subsection Opening a serial device
[07b3693f]452
[47eab0a]453The @code{console_open} function is called whenever a serial device is opened.
454The device registered as @code{"/dev/console"} (@code{CONSOLE_DEVICE_NAME}) is
455opened automatically during RTEMS initialization.  For instance, if UART
456channel 2 is registered as "/dev/tty1", the @code{console_open} entry point
457will be called as the result of an @code{fopen("/dev/tty1", mode)} in the
[07b3693f]458application.
459
[bc950e87]460The @code{console_open} function has to inform Termios of the low-level
[47eab0a]461functions for serial line support.
[07b3693f]462
[47eab0a]463@example
464@group
465rtems_device_driver console_open(
466    rtems_device_major_number major,
467    rtems_device_minor_number minor,
468    void *arg
469)
470@{
471    struct rtems_termios_callbacks *callbacks =
472        &my_driver_callbacks_polled;
473
474    /*
475     * Check the minor number.  Termios does currently not check
476     * the return value of the first open call so the minor
477     * number must be checked here.
478     */
479    if (MY_DRIVER_IS_MINOR_INVALID(minor)) @{
480        return RTEMS_INVALID_NUMBER;
481    @}
482
483    /*
484     * Depending on the IO mode you need to pass a different set of
485     * callback functions to Termios.
486     */
487    if (MY_DRIVER_USES_INTERRUPTS(minor)) @{
488        callbacks = &my_driver_callbacks_interrupt;
489    @}
490
491    return rtems_termios_open(major, minor, arg, callbacks);
492@}
493@end group
494@end example
[07b3693f]495
[47eab0a]496During the first open of the device Termios will call @code{my_driver_first_open}.
[07b3693f]497
[47eab0a]498@example
499@group
500static int my_driver_first_open(int major, int minor, void *arg)
501@{
502    my_driver_entry *e = &my_driver_table [minor];
503    struct rtems_termios_tty *tty =
504        ((rtems_libio_open_close_args_t *) arg)->iop->data1;
505   
506    /* Check minor number */
507    if (MY_DRIVER_IS_MINOR_INVALID(minor)) @{
508        return -1;
509    @}
510
511    /* Connect the TTY data structure */
512    e->tty = tty;
513
514    /*
515     * You may add some initialization code here.
516     */
517
518    /*
519     * Sets the inital baud rate.  This should be set to the value of
520     * the boot loader.
521     */
522    return rtems_termios_set_initial_baud(e->tty, MY_DRIVER_BAUD_RATE);
523@}
524@end group
525@end example
[07b3693f]526
[47eab0a]527@subsection Closing a Serial Device
[07b3693f]528
[47eab0a]529The @code{console_close} is invoked when the serial device is to be closed.
530This entry point corresponds to the device driver close entry point.
[07b3693f]531
[47eab0a]532This routine is responsible for notifying Termios that the serial device was
533closed.  This is done with a call to @code{rtems_termios_close}.
[bc950e87]534
[47eab0a]535@example
536@group
537rtems_device_driver console_close(
538    rtems_device_major_number major,
539    rtems_device_minor_number minor,
540    void *arg
541)
542@{
543    return rtems_termios_close(arg);
544@}
545@end group
546@end example
[07b3693f]547
[47eab0a]548Termios will call the @code{my_driver_last_close} function if the last close
549happens on the device.
550@example
551@group
552static int my_driver_last_close(int major, int minor, void *arg)
553@{
554    my_driver_entry *e = &my_driver_table [minor];
555   
556    /*
557     * There is no need to check the minor number since it is derived
558     * from a file descriptor.  The upper layer takes care that it is
559     * in a valid range.
560     */
561
562    /* Disconnect the TTY data structure */
563    e->tty = NULL;
564
565    /*
566     * The driver may do some cleanup here.
567     */
568
569    return 0;
570@}
571@end group
572@end example
[07b3693f]573
[47eab0a]574@subsection Reading Characters from a Serial Device
[07b3693f]575
[47eab0a]576The @code{console_read} is invoked when the serial device is to be read from.
577This entry point corresponds to the device driver read entry point.
[07b3693f]578
[47eab0a]579This routine is responsible for returning the content of the Termios input
580buffer.  This is done by invoking the @code{rtems_termios_read} routine.
[07b3693f]581
[47eab0a]582@example
583@group
584rtems_device_driver console_read(
585    rtems_device_major_number major,
586    rtems_device_minor_number minor,
587    void *arg
588)
589@{
590    return rtems_termios_read(arg);
591@}
592@end group
593@end example
[07b3693f]594
[47eab0a]595@subsection Writing Characters to a Serial Device
[07b3693f]596
[47eab0a]597The @code{console_write} is invoked when the serial device is to be written to.
598This entry point corresponds to the device driver write entry point.
[07b3693f]599
[47eab0a]600This routine is responsible for adding the requested characters to the Termios
601output queue for this device.  This is done by calling the routine
602@code{rtems_termios_write} to add the characters at the end of the Termios
603output buffer.
[07b3693f]604
[47eab0a]605@example
606@group
607rtems_device_driver console_write(
608    rtems_device_major_number major,
609    rtems_device_minor_number minor,
610    void *arg
611)
612@{
613    return rtems_termios_write(arg);
614@}
615@end group
616@end example
[07b3693f]617
[47eab0a]618@subsection Changing Serial Line Parameters
[07b3693f]619
[47eab0a]620The @code{console_control} is invoked when the line parameters for a particular
621serial device are to be changed.  This entry point corresponds to the device
622driver io_control entry point.
[07b3693f]623
[47eab0a]624The application writer is able to control the serial line configuration with
625Termios calls (such as the @code{ioctl} command, see the Termios documentation
626for more details).  If the driver is to support dynamic configuration, then it
627must have the @code{console_control} piece of code.  Basically @code{ioctl}
628commands call @code{console_control} with the serial line configuration in a
629Termios defined data structure.
[07b3693f]630
[47eab0a]631@example
632@group
633rtems_device_driver console_control(
634    rtems_device_major_number major,
635    rtems_device_minor_number minor,
636    void *arg
637)
638@{
639    return rtems_termios_ioctl(arg);
640@}
641@end group
642@end example
[07b3693f]643
[47eab0a]644The driver is responsible for reinitializing the device with the correct
645settings.  For this purpuse Termios calls the @code{my_driver_set_attributes}
646function.
[07b3693f]647
[47eab0a]648@example
649@group
650static int my_driver_set_attributes(
651    int minor,
652    const struct termios *t
653)
654@{
655    my_driver_entry *e = &my_driver_table [minor];
656   
657    /*
658     * There is no need to check the minor number since it is derived
659     * from a file descriptor.  The upper layer takes care that it is
660     * in a valid range.
661     */
662
663    /*
664     * Inspect the termios data structure and configure the device
665     * appropriately.  The driver should only be concerned with the
666     * parts of the structure that specify hardware setting for the
667     * communications channel such as baud, character size, etc.
668     */
669
670    return 0;
671@}
672@end group
673@end example
Note: See TracBrowser for help on using the repository browser.