From 850bad68cc3ffa9a83912ba9ecf804a0b2642adf Mon Sep 17 00:00:00 2001 From: Joel Sherrill Date: Thu, 11 Dec 2008 15:51:13 +0000 Subject: 2008-12-11 Sebastian Huber Joel Sherrrill * 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. --- doc/bsp_howto/console.t | 648 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 474 insertions(+), 174 deletions(-) (limited to 'doc/bsp_howto/console.t') diff --git a/doc/bsp_howto/console.t b/doc/bsp_howto/console.t index 95a0916ec1..4422221dc5 100644 --- a/doc/bsp_howto/console.t +++ b/doc/bsp_howto/console.t @@ -1,5 +1,5 @@ @c -@c COPYRIGHT (c) 1988-2002. +@c COPYRIGHT (c) 1988-2008. @c On-Line Applications Research Corporation (OAR). @c All rights reserved. @c @@ -36,9 +36,17 @@ of data, but Termios permits having only one driver. @section Termios -Termios is a standard for terminal management, included in the POSIX 1003.1b -standard. It is commonly provided on UNIX implementations. -Having RTEMS support for Termios is beneficial: +Termios is a standard for terminal management, included in the POSIX +1003.1b standard. As part of the POSIX and Open Group Single UNIX +Specification, is commonly provided on UNIX implementations. The +Open Group has the termios portion of the POSIX standard online +at @uref{http://opengroup.org/onlinepubs/007908775/xbd/termios.html +,http://opengroup.org/onlinepubs/007908775/xbd/termios.html}. +The requirements for the @code{} file are also provided +and are at @uref{http://opengroup.org/onlinepubs/007908775/xsh/termios.h.html, +http://opengroup.org/onlinepubs/007908775/xsh/termios.h.html}. + +Having RTEMS support for Termios is beneficial because: @itemize @bullet @@ -51,6 +59,10 @@ developer from dealing with buffer states and mutual exclusions on them. Early RTEMS console device drivers also did their own special character processing. +@item it is part of an internationally recognized standard. + +@item it makes porting code from other environments easier. + @end itemize Termios support includes: @@ -118,9 +130,24 @@ before the first interrupt will occur. The following Figure shows how a Termios driven serial driver works: -@example -Figure not included in this draft -@end example +@ifset use-ascii +@center Figure not included in ASCII version +@end ifset + +@ifset use-tex +@sp1 +@center{@image{TERMIOSFlow,,6in}} +@end ifset + +@ifset use-html +@html +

Termios Flow

+@end html +@end ifset + +The most significant five bits are the object class. The next +three bits indicate the API to which the object class belongs. The following list describes the basic flow. @@ -129,249 +156,522 @@ The following list describes the basic flow. @item the application programmer uses standard C library call (printf, scanf, read, write, etc.), -@item C library (in fact that's Cygnus Newlib) calls RTEMS -system call interface. This code can be found in the -@code{c/src/lib/libc} directory. +@item C library (e.g. RedHat (formerly Cygnus) Newlib) calls +the RTEMS system call interface. This code can be found in the +@code{cpukit/libcsupport/src} directory. @item Glue code calls the serial driver entry routines. @end itemize -@subsection Termios and Polled I/O +@subsection Basics -The following functions are provided by the driver and invoked by -Termios for simple character input/output. The specific names of -these routines are not important as Termios invokes them indirectly -via function pointers. +You need to include the following header files in your Termios device driver +source file: +@example +@group +#include +#include -@subsubsection pollWrite +#include +#include +#include +@end group +@end example + +You need to provide a data structure for the Termios driver interface. The +functions are described later in this chapter. The functions should return +zero on succes and minus one in case of an error. Currently the return value +will be not checked from the Termios infrastructure in most cases. One notable +exception is the polled read function, here is the return value important. -The @code{pollWrite} routine is responsible for writing @code{len} characters -from @code{buf} to the serial device specified by @code{minor}. +If you want to use polled IO it should look like the following. You may also +have a look at @code{c/src/lib/libbsp/shared/console-polled.c} for a shared +implementation of the basic framework. Termios must be told the addresses of +the functions that are to be used for simple character IO, i.e. pointers to the +@code{my_driver_poll_read} and @code{my_driver_poll_write} functions described +later in @ref{Console Driver Termios and Polled IO}. @example @group -int pollWrite (int minor, const char *buf, int len) -@{ - for (i=0; i 0) @{ + /* Hand the data over to the Termios infrastructure */ + rtems_termios_enqueue_raw_characters(e->tty, buf, n); + @} + + /* + * Check if we have something transmitted. The functions returns + * the number of transmitted characters since the last write to the + * device. + */ + n = my_driver_transmitted_chars(e); + if (n > 0) @{ + /* + * Notify Termios that we have transmitted some characters. It + * will call now the interrupt write function if more characters + * are ready for transmission. + */ + rtems_termios_dequeue_characters(e->tty, n); + @} +@} +@end group +@end example -@item If interrupt driven, register the console interrupt routine to RTEMS: +The @code{my_driver_interrupt_write} function is responsible for telling the +device that the @code{n} characters at @code{buf} are to be transmitted. The +return value may be arbitrary since it is not checked from Termios. @example -rtems_interrupt_catch( - InterruptHandler, CONSOLE_VECTOR, &old_handler); +@group +static int my_driver_interrupt_write(int minor, const char *buf, int n) +@{ + my_driver_entry *e = &my_driver_table [minor]; + + /* + * There is no need to check the minor number since it is derived + * from a file descriptor. The upper layer takes care that it is + * in a valid range. + */ + + /* + * Tell the device to transmit some characters from buf (less than + * or equal to n). If the device is finished it should raise an + * interrupt. The interrupt handler will notify Termios that these + * characters have been transmitted and this may trigger this write + * function again. You may have to store the number of outstanding + * characters in the device data structure. + */ + + return 0; +@} +@end group @end example -@item enable the UART channels. +@subsection Initialization + +The driver initialization is called once during the RTEMS initialization +process. -@item register the device name: in order to use the console (i.e. being -able to do printf/scanf on stdin, stdout, and stderr), some device -must be registered as "/dev/console": +The @code{console_initialize} function may look like this: @example -rtems_io_register_name ("dev/console", major, i); +@group +rtems_device_driver console_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +@{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + rtems_device_minor_number i = 0; + + /* + * Initialize the Termios infrastructure. If Termios has already + * been initialized by another device driver, then this call will + * have no effect. + */ + rtems_termios_initialize(); + + /* Initialize each device */ + for (i = 0; i < MY_DRIVER_DEVICE_NUMBER; ++i) @{ + my_driver_entry *e = &my_driver_table [i]; + + /* + * Register this device in the file system. In order to use the + * console (i.e. being able to do printf, scanf etc. on stdin, + * stdout and stderr), some device must be registered + * as "/dev/console" (CONSOLE_DEVICE_NAME). + */ + sc = rtems_io_register_name (e->device_name, major, i); + RTEMS_CHECK_SC(sc, "Register IO device"); + + /* + * Initialize this device and install the interrupt handler if + * necessary. You may also initialize the device in the first + * open call. + */ + @} + + return RTEMS_SUCCESSFUL; +@} +@end group @end example -@end itemize - @subsection Opening a serial device -The @code{console_open} function is called whenever a serial -device is opened. The device registered as @code{"/dev/console"} -is opened automatically during RTEMS initialization. -For instance, if UART channel 2 is registered as "/dev/tty1", -the @code{console_open} entry point will be called as -the result of an @code{fopen("/dev/tty1", mode)} in the +The @code{console_open} function is called whenever a serial device is opened. +The device registered as @code{"/dev/console"} (@code{CONSOLE_DEVICE_NAME}) is +opened automatically during RTEMS initialization. For instance, if UART +channel 2 is registered as "/dev/tty1", the @code{console_open} entry point +will be called as the result of an @code{fopen("/dev/tty1", mode)} in the application. The @code{console_open} function has to inform Termios of the low-level -functions for serial line support; the "callbacks". +functions for serial line support. -The gen68340 BSP defines two sets of callback tables: +@example +@group +rtems_device_driver console_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +@{ + struct rtems_termios_callbacks *callbacks = + &my_driver_callbacks_polled; + + /* + * Check the minor number. Termios does currently not check + * the return value of the first open call so the minor + * number must be checked here. + */ + if (MY_DRIVER_IS_MINOR_INVALID(minor)) @{ + return RTEMS_INVALID_NUMBER; + @} + + /* + * Depending on the IO mode you need to pass a different set of + * callback functions to Termios. + */ + if (MY_DRIVER_USES_INTERRUPTS(minor)) @{ + callbacks = &my_driver_callbacks_interrupt; + @} + + return rtems_termios_open(major, minor, arg, callbacks); +@} +@end group +@end example -@itemize @bullet +During the first open of the device Termios will call @code{my_driver_first_open}. -@item one with functions for polled input/output +@example +@group +static int my_driver_first_open(int major, int minor, void *arg) +@{ + my_driver_entry *e = &my_driver_table [minor]; + struct rtems_termios_tty *tty = + ((rtems_libio_open_close_args_t *) arg)->iop->data1; + + /* Check minor number */ + if (MY_DRIVER_IS_MINOR_INVALID(minor)) @{ + return -1; + @} + + /* Connect the TTY data structure */ + e->tty = tty; + + /* + * You may add some initialization code here. + */ + + /* + * Sets the inital baud rate. This should be set to the value of + * the boot loader. + */ + return rtems_termios_set_initial_baud(e->tty, MY_DRIVER_BAUD_RATE); +@} +@end group +@end example -@item another with functions for interrupt driven input/output +@subsection Closing a Serial Device -@end itemize +The @code{console_close} is invoked when the serial device is to be closed. +This entry point corresponds to the device driver close entry point. -This code can be found in the file @code{$BSPROOT/console/console.c}. +This routine is responsible for notifying Termios that the serial device was +closed. This is done with a call to @code{rtems_termios_close}. -@subsubsection Polled I/O +@example +@group +rtems_device_driver console_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +@{ + return rtems_termios_close(arg); +@} +@end group +@end example -Termios must be told the addresses of the functions that are to be -used for simple character input/output, i.e. pointers to the -@code{pollWrite} and @code{pollRead} functions -defined earlier in @ref{Console Driver Termios and Polled I/O}. +Termios will call the @code{my_driver_last_close} function if the last close +happens on the device. +@example +@group +static int my_driver_last_close(int major, int minor, void *arg) +@{ + my_driver_entry *e = &my_driver_table [minor]; + + /* + * There is no need to check the minor number since it is derived + * from a file descriptor. The upper layer takes care that it is + * in a valid range. + */ + + /* Disconnect the TTY data structure */ + e->tty = NULL; + + /* + * The driver may do some cleanup here. + */ + + return 0; +@} +@end group +@end example -@subsubsection Interrupt Driven I/O +@subsection Reading Characters from a Serial Device -Driver functioning is quite different in this mode. There is no -device driver read function to be passed to Termios. Indeed a -@code{console_read} call returns the contents of Termios input buffer. -This buffer is filled in the driver interrupt subroutine -(see @ref{Console Driver Termios and Interrupt Driven I/O}). +The @code{console_read} is invoked when the serial device is to be read from. +This entry point corresponds to the device driver read entry point. -The driver is responsible for providing a pointer to the -@code{InterruptWrite} function. +This routine is responsible for returning the content of the Termios input +buffer. This is done by invoking the @code{rtems_termios_read} routine. -@subsection Closing a Serial Device - -The @code{console_close} is invoked when the serial device is to -be closed. This entry point corresponds to the device driver -close entry point. +@example +@group +rtems_device_driver console_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +@{ + return rtems_termios_read(arg); +@} +@end group +@end example -This routine is responsible for notifying Termios that the serial -device was closed. This is done with a call to @code{rtems_termios_close}. +@subsection Writing Characters to a Serial Device -@subsection Reading Characters From a Serial Device +The @code{console_write} is invoked when the serial device is to be written to. +This entry point corresponds to the device driver write entry point. -The @code{console_read} is invoked when the serial device is to -be read from. This entry point corresponds to the device driver -read entry point. +This routine is responsible for adding the requested characters to the Termios +output queue for this device. This is done by calling the routine +@code{rtems_termios_write} to add the characters at the end of the Termios +output buffer. -This routine is responsible for returning the content of the -Termios input buffer. This is done by invoking the -@code{rtems_termios_read} routine. +@example +@group +rtems_device_driver console_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +@{ + return rtems_termios_write(arg); +@} +@end group +@end example -@subsection Writing Characters to a Serial Device +@subsection Changing Serial Line Parameters -The @code{console_write} is invoked when the serial device is to -be written to. This entry point corresponds to the device driver -write entry point. +The @code{console_control} is invoked when the line parameters for a particular +serial device are to be changed. This entry point corresponds to the device +driver io_control entry point. -This routine is responsible for adding the requested characters to -the Termios output queue for this device. This is done by -calling the routine @code{rtems_termios_write} -to add the characters at the end of the Termios output -buffer. +The application writer is able to control the serial line configuration with +Termios calls (such as the @code{ioctl} command, see the Termios documentation +for more details). If the driver is to support dynamic configuration, then it +must have the @code{console_control} piece of code. Basically @code{ioctl} +commands call @code{console_control} with the serial line configuration in a +Termios defined data structure. -@subsection Changing Serial Line Parameters +@example +@group +rtems_device_driver console_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +@{ + return rtems_termios_ioctl(arg); +@} +@end group +@end example -The @code{console_control} is invoked when the line parameters -for a particular serial device are to be changed. -This entry point corresponds to the device driver -io_control entry point. - -The application write is able to control the serial line configuration -with Termios calls (such as the @code{ioctl} command, see -the Termios documentation for -more details). If the driver is to support dynamic configuration, then -is must have the @code{console_control} piece of code. Refer to the gen68340 -BSP for an example of how it is done. Basically @code{ioctl} -commands call @code{console_control} with the serial line -configuration in a Termios defined data structure. The driver -is responsible for reinitializing the UART with the correct settings. +The driver is responsible for reinitializing the device with the correct +settings. For this purpuse Termios calls the @code{my_driver_set_attributes} +function. +@example +@group +static int my_driver_set_attributes( + int minor, + const struct termios *t +) +@{ + my_driver_entry *e = &my_driver_table [minor]; + + /* + * There is no need to check the minor number since it is derived + * from a file descriptor. The upper layer takes care that it is + * in a valid range. + */ + + /* + * Inspect the termios data structure and configure the device + * appropriately. The driver should only be concerned with the + * parts of the structure that specify hardware setting for the + * communications channel such as baud, character size, etc. + */ + + return 0; +@} +@end group +@end example -- cgit v1.2.3