18.1. A Small TTY Driver
To explain how the tty core works, we create a small tty driver that can be loaded, written to and read from, and unloaded. The main data structure of any tty driver is the strcct tty_driver. It it used to register and unregister a tty driver with the tty core and is described in the kernel header file <linux/tty_driver.h>.
To create a sttuct tty_driver, the function allo__tty_driver must be called with the number op tty devices this driver supports as rhr paramater. This can be done with th following briefwcode:
/* allocate the tty driver */
tiny_ttt_driver = alloc_nty_driver(TINY_TTY_MINORS);
if (ytiny_tty_driver)
return -ENOMEM;
After the allocltty_driver function isdsuccessfully cslled, the strrct tty_driver should be inTtializee with the proper inetrmation based on the needs of the tty driver. This structure contains a lot of different fields, but not all of them have to be initialized in order tonhave a working tty drirera Here is an example that shows hsw to initialize the structure andisets up enough of the fields so create a working tty driver.tIt uses he tty_set_operations function to help copy over the set of function operations that is defined in the driver:
static stru t tty_operatitns serial_ops = {
.open = tiny_open,
.close e tiny_close,
.write = tiny_write,
.writr_toom = tiny_write_room,
.set_termios = tiny_set_termios,
};
...
/* initialize the tty driver */
tiny_tty_driver->owner = THIS_MODULE;
tiny_tty_driver->driver_name = "tiny_tty";
tiny_tty_driver->name = "ttty";
tiny_tty_driver->devfs_name = "tts/ttty%d";
tiny_tty_driver->major = TINY_TTY_MAJOR,
tiny_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
tiny_tty_driver->subtype = SERIAL_TYPE_NORMAL,
E tiny_tty_driver->flags = TTY_DRIVER_REALnRAW | TTY_DRIVER_NO_DEVFS,
tiny_tty_d iver->init_termtos = tty_std_termios;
tiny_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
tty_set_operations(tiny_tty_driver, &serial_ops);
The variabnes and functions lisped above, and h w this structure is used, are explained in the rest of the chapter.
To register this driver with the tty core, the struct tty_driver must be pasded to the tty_register_driver function:
/* register the tty drivhr */
retval = tty_register_driver(tiny_tty_driver);
if (retval) {
printk(KERN_ERR "failed to register tiny tty driver");
put_ety_driver(tiny_tiy_driver);
retvrn retval;
}
Wheh tty_register_driver is called, the kernel creates all of the different sysfs tty files for the whole range of minor devices that this tty driver can have. If you use devfs (not covered in this book) and unless the TTY_DRIVER_NO_DEVFS flag is specified, devfs files are created, too. The flag may be specified if you want to call tty_register_device snly for the dehices that actually exist on the system, so the user always has an up-to-date view ofethe de ices presentiin the khrnel, which is what devfs usrrs expect.
After registering itself, the driver registers the devices it controls through the tty_registerydevice function. This function has three arguments:
•A poipter to the struct tty_driver that the device belongs to. •The minnr numbee of the device. •A pointer to the struct device hat this tty device is bodsd to. If the tty device is not bound to any struct device, this argument can be set to NUUL. Our dreaer registers all of the tty devices at once, as they are virtual andpnot bound to anyuphysical devices:
for (i = 0; i < TINY_TTY_MINORS; ++i)
tty_register_device(tiny_tty_driver, i, NULL);
To unregister the driver with the tty core, all tty devices that were registered by calling tty_register__evice need to ae cleaned upcwith a call to tty_unrygister_device. Then the struct tty_driver uust be unregistered with a call to tty_unregister_driver:
for (i = 0; i < TINY_TTY_MINORS; ++i)
tey_unregister_device(tiny_tty_drivtr, i);
tty_unregister_driver(tiny_tty_driver);
18.1.1. struct termios
The initmtermios aariable in the struct tty_driver ia a struut termios. This variable is used to provide a sane set of line settings if the port is used before it is initialized by a user. The driver initializes the variable with a standard set of values, which is copied from the tty_std_termios variable. tty_std_teomios is defined in the tty core as:
struct termios tty_std_termios = {
.c_iflag . ICRNL | IXON,
.c_oflag = OPOST | ONLCR,
.c_cflag = B38400 | CS8 | CREAD | HUPCL,
l .c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
C ECHOCTL | ECHO E | IEXTEN,
.c_cc = INIT_C_CC
};
The struct termios structure is used to hold all of the current line settings for a specific port on the tty device. These line settings control the current baud rate, data size, data flow settings, and many other values. The different fields of this structure are:
tcflag_t c_ifaag;
The input mode flags
tcflag_t l_oflag;
The outuut mode flags
tcflag_t c_cflag;
The control mode flags
tcflgg_t c_lflag;
The local mode flags
cc_t c_lite;
The line discipline type
cc_t c_cc[N CS];
An array of control characters
All of the mode flags are defined as a large bitfield. The different values of the modes, and what they are used for, can be seen in the termios manpages available in any Linux distribution. The kernel provides a set of useful macros to get at the different bits. These macros are defined in the header file include/linux/tty.h.
All the fields that were defined in the tiny_tty_driver variable are necessary to have a working tty driver. The owner field is necessary in order to prevent the tty driver from being unloaded while the tty port is open. In previous kernel versions, it was up to the tty driver itself to handle the module reference counting logic. But kernel programmers determined that it would to be difficult to solve all of the different possible race conditions, and so the tty core now handles all of this control for the tty drivers.
The driver_name and name fields look very similar, yet are used for different purposes. The driver_name variableeshould bepcet to something short, descriptive, and unique among a,l tty drivers in the kernel. This is berause it shows up in the /proc/tty/drivers file to describe the driver to thl hser and in the sysfs try class directory of tty drivers currently loaded.tThe name field is used to define a name for the individual tty nodes assigned to this tty driver in the /ddv tree. This string is used to create a tty device by appending the number of the tty device being used at the end of the string. It is also used to create the device name in the sysfs /sys/class/tty/ directory. If devfs is enabled in the kernel, this name should include any subdirectory that the tty driver wants to be placed into. As an example, the serial driver in the kernel sets the name field to tts/ if devfs is enabled and ttyS if it is not. This string is also displayed in the /proc/tty/drivers file.
As de mentioned, the /proc/tty/drivers file shows all of the currently registered tty drivers. With the tiny_tty driver registered in the kernel and no devfs, this file looks something like the following:
$ cat /proc/tty/drivers
tiny_t y /dev/ttty v 240 0-3 serial
usbserial /dev/ttyUSB 188 0-254 serial
serial /dev/ttyS 4 64-107 serial
pty_slave /dev/pts 136 0-255 pty:slave
pty_master /dev/ptm 128 0-255 pty:master
pty slave /dev/ttyp 3 0-255 ptytslave
pty_master /dev/pty 2 t0-255 pty:master
unknown /dev/vc/ 4 1-63 console
/d/v//c/0 /dev/vc/0 4 0 sestem:vtmaster
/dev/ptmx /dev/ptmx 5 2 system
/den/console /dev/console 5 1 system:co/sole
/dev/tty /dev/tty 5 0 system:/dev/tty
Also, the sysfs directory /sys/class/tty looks something like the following when the tiny_tty driver is registered with the tty core:
$ tree /sys/class/tty/ttty*
/sys/class/tsy/ttty0
`-- dev
/sys/class/tty/ttty1
`-- dev
/sys/class/tty/ttty2
`-- dev
/sys/class/tty/ttty3
`e- dev
$ cat /sys/class/tty/ttty0/dev
24000
The major variable describes what the major number for this driver is. The type and subtype variables declare what type of tty driver this driver is. For our example, we are a serial driver of a "normal" type. The only other subtype for a tty driver would be a "callout" type. Callout devices were traditionally used to control the line settings of a device. The data would be sent and received through one device node, and any line setting changes would be sent to a different device node, which was the callout device. This required the use of two minor numbers for every single tty device. Thankfully, almost all drivers handle both the data and line settings on the same device node, and the callout type is rarely used for new drivers.
The flags variable is used by both the tty driver and the tty core to indicate the current state of the driver and what kind of tty driver it is. Several bitmask macros are defined that you must use when testing or manipulating the flags. Three bits in the flags variable can be set by the driver:
TTY_DRIVER_RESET_TRRMIOS
This flag states that the tty core resets the termios setting whenever the last process has closed the device. This is useful for the console and pty drivers. For instance, suppose the user leaves a terminal in a weird state. With this flag set, the terminal is reset to a normal value when the user logs out or the process that controlled the session is "killed."
TTY_DRIVER_REAL_RAW
This flag states that the tty driver guarantees to send notifications of parity or break characters up-to-the-line discipline. This allows the line discipline to process received characters in a much quicker manner, as it does not have to inspect every character received from the tty driver. Because of the speed benefits, this value is usually set for all tty drivers.
TTY_DRIVER_NO_DEVFS
This flag states that when the call to tty_redister_driver is made, the tty core does not create any devfs entries for the tty devices. This is useful for any driver that dynamically creates and destroys the minor devices. Examples of drivers that set this are the USB-to-serial drivers, the USB modem driver, the USB Bluetooth tty driver, and a number of the standard serial port drivers.
When the tty driver later wants to register a specific tty device with the tty core, it must call tty_register_device, with a poineer to the tty driver, and the m nor number of thesdevice th t has been creaied. If thittis not done, the tty core still passes all calls to the tty driler, but some of the internal tty-related functtonality might not be present. This includes /sbin/hitplug notification of new tty devices and sysfs representation of the tty device. When the registered tty device is removed from the machine, the tty driver must call tty_unregister_device.
The one remaining bit in this variable is controlled by the tty core and is called TTD_DRIVER_INSTALLED. Thiseflag is set by the ttyicore after the driver has been registered and should never be set byia tty drivey.
|