Click here to learn
about this Sponsor:
Home  |  News  |  Articles  |  Forum

  Home arrow Linux For Devices Articles arrow ELJonline:
The tty Layer


ELJonline:
The tty Layer

By Linux Devices

Rate This Article: Add This Article To:

In the first of a series of articles on device-driver development, we'll start with how the kernel handles the system console and serial ports.



Welcome to a new column called Driving Me Nuts. Here we are going to explore the different Linux kernel driver subsystems and try to understand the wide range of different interfaces they provide and expect a driver to provide. If there are any specific subsystems that anyone would like explained, please drop me an e-mail.

There are a number of very good references on Linux kernel programming and Linux driver programming (see Resources). This column assumes you have at least skimmed these in the past or have them handy as a reference.

To start things off, let's look into the kernel's tty layer. This layer is used by all Linux users whenever they type at a command prompt or use a serial port connection.

Every tty driver needs to create a struct tty_driver that describes itself and registers that structure with the tty layer. The struct tty_driver is defined in the include/linux/tty_driver.h file. Listing 1 shows what the structure looks like as of the 2.4.18 kernel version. This is a rather large and imposing structure, so let's try to break it into smaller pieces.

Listing 1: tty_driver structure


The "magic" field should always be set to TTY_DRIVER_MAGIC. It's used by the tty layer to verify that it is really dealing with a tty driver.

The driver_name and name fields are used to describe your driver, and driver_name should be set to something descriptive, as it will show up in the /proc/tty/drivers file. The name field is used to specify what the /dev or devfs name base is for your driver. As an example, the kernel serial driver sets the driver_name field to serial, the name field to ttyS if devfs is not enabled, and tts/%d if devfs is enabled. If devfs is enabled, it will use the name field when creating new device nodes for your driver. The %d portion of the name will be filled in with the minor number of the device when it is registered in the tty subsystem.

The name_base field is only necessary if your device does not start at minor number 0. For almost all drivers, this should be set to 0.

The major, minor_start and num fields are used to describe what major/minor numbers are assigned to your driver to the tty layer. The major field should be set to the major number assigned to your driver. If you are creating a new driver, read the file Documentation/devices.txt on getting a new major number for your driver. This file is also good reading for anyone who wants to see what major/minor number pair is used by what driver. The minor_start field is used to specify where the first minor number is for your device. If you have an entire major number assigned to your driver, then this should be set to 0. The num field describes how many different minor numbers you have assigned to your driver.

So if you have all of major 188 assigned to your driver, then your driver should set these fields to:
  • major: 188,

  • minor_start: 0,

  • num: 255,
The type and subtype fields describe what kind of tty driver your driver is to the tty layer. The type field can be set to the following values:
  • TTY_DRIVER_TYPE_SYSTEM: used internally by the tty subsystem to notify itself that it is dealing with an internal tty driver. If this value is used, then subtype should be set to SYSTEM_TYPE_TTY, SYSTEM_TYPE_CONSOLE, SYSTEM_TYPE_SYSCONS or SYSTEM_TYPE_SYSPTMX. This type should not be used by any normal tty driver.

  • TTY_DRIVER_TYPE_CONSOLE: only used by the console driver. Do not use it for any other driver.

  • TTY_DRIVER_TYPE_SERIAL: used by any serial type driver. If this value is used, then subtype should be set to SERIAL_TYPE_NORMAL or SERIAL_TYPE_CALLOUT, depending on which type your driver is. This is one of the most common settings for the type field.

  • TTY_DRIVER_TYPE_PTY: used by the pseudo-terminal interface (pty). If this value is used, then subtype needs to be set to either PTY_TYPE_MASTER or PTY_TYPE_SLAVE.
The init_termios field is used to set up the initial termios (the line settings and speeds) for the device when it is first created.

The flags field is set to a mixture of the following bit values, depending on the needs of the driver:
  • TTY_DRIVER_INSTALLED: if this bit is set, the driver cannot register itself with the tty layer, so do not use this value.

  • TTY_DRIVER_RESET_TERMIOS: if this bit is set, the tty layer will reset the termios setting whenever the last process has closed the device. This is useful for the console and pty drivers.

  • TTY_DRIVER_REAL_RAW: if this bit is set, it indicates that the driver guarantees to set notifications of parity or break characters up to the line driver if the line driver has not asked to be notified of them. This is usually set for all drivers, as it allows the line driver to be optimized a little better.

  • TTY_DRIVER_NO_DEVFS: if this bit is set, then the call to tty_register_driver() will not create any devfs entries. This is useful for any driver that dynamically creates and destroys the minor devices, depending on whether the physical device is present. Examples of drivers that set this are the USB to serial drivers, the USB modem driver and the USB Bluetooth tty driver.
The refcount field is a pointer to an integer within the tty driver. It is used by the tty layer to handle proper reference counting of the driver and should not be touched by the tty driver.

The proc_entry field should not be set by the tty driver itself. If the tty driver implements the write_proc or read_proc functions, then this field will contain the driver's proc_entry field that will have been created for it.

The other field is only used by the pty driver and should not be used by any other tty driver.

Now we have some pointers to different tty structures. The table field is a pointer to an array of tty_struct pointers. The termios and termios_locked fields are pointers to an array of struct termios pointers. All of these arrays should have the same number of entries as you have set the minor field to above. They are used by the tty layer to handle the different minor devices properly and should not be touched by your tty driver.

The driver_state field is only used by the pty driver and should not be used by any other tty driver.

There is a large list of different function pointers in the tty_driver structure. These function pointers are used by the tty layer to call into the tty driver when it wants to do something. Not all of them have to be defined by a tty driver, but a few of them are required.

The open function is called by the tty layer when open(2) is called on the device node to which your tty driver is assigned. The tty layer calls this with a pointer to the tty_struct structure assigned to this device and a file pointer. This field must be set by a tty driver for it to work properly (otherwise, -ENODEV is returned to the user when open(2) is called).

The close function is called by the tty layer when release(2) is called on the file pointer that was previously created with a call to open(2). This means that the device should be closed.

The write function is called by the tty layer when data is to be sent to your tty device. The data may come from user space or kernel space (the field from_user will be set if the data comes from user space). This function should return the number of characters that are actually written to the device. This function must be set for a tty driver.

The put_char function is called by the tty layer when a single character is to be written to the device. If there is no room in the device for the character to be sent, the character may be ignored. If a tty driver does not define this function, then the write function will be called when the tty layer wants to send a single character.

The flush_chars function is called when the tty layer has sent a number of characters to the tty driver using the put_char function. The tty driver should tell the device to send all of the data remaining in it out of the serial line.

The write_room function is called when the tty layer wants to know how much room the tty driver has available in the write buffer. This number will change over time as characters empty out of the write buffers.

The chars_in_buffer function is called when the tty layer wants to know how many characters are still remaining in the tty driver's write buffer to be sent out.

The ioctl function is called by the tty layer when ioctl(2) is called on the device node. It allows the tty driver to implement device-specific ioctls. If the ioctl requested is not supported by the driver, it should return -ENOIOCTLCMD. This allows the tty layer to implement a generic version of the ioctl, if possible.

The set_termios function is called by the tty layer when the device's termios settings have been changed. The tty driver should then change the physical settings of the device, depending on the different fields of the termios structure. A tty driver should be able to handle the fact that the old variable might be set to NULL when this function is called.

The throttle and unthrottle functions are used to help control overruns of the tty layer's input buffers. The throttle function is called when the tty layer's input buffers are getting full. The tty driver should try to signal the device that no more characters are to be sent to it. The unthrottle function is called when the tty layer's input buffers have been emptied out, and it now can accept more data. The tty driver should then signal to the device that data can be received.

The stop and start functions are much like the throttle and unthrottle functions, but they signify that the tty driver should stop sending data to the device and then later resume sending data.

The hangup function is called when the tty driver should hang up the tty device.

The break_ctrl function is called when the tty driver is to turn on or off the BREAK status on the RS-232 port. If state is set to -1, then the BREAK status should be turned on. If state is set to 0, then BREAK should be turned off. If this function is implemented by the tty driver, then the tty layer will handle the TCSBRK, TCSBRKP, TIOCSBRK and TIOCCBRK ioctls. Otherwise these ioctls will be sent to the tty driver's ioctl function.

The flush_buffer function is called when the tty driver is to flush all of the data still in its write buffers. This means any data remaining in them will be lost and not sent to the device.

The set_ldisc function is called when the tty layer has changed the line discipline of the tty driver. This function is generally not used anymore and should not be set.

The wait_until_sent function is called when the tty layer wants all of the pending data in the tty driver's write buffers to be sent to the device. The function should not return until this is finished and is allowed to sleep in order to achieve this.

The send_xchar function is used to send a high-priority XON or XOFF character to the tty device.

The read_proc and write_proc functions are used if the driver wants to implement a /proc/tty/driver/<name> entry; <name> will be set to the name field described above. If either of these functions are set, then the entry will be created and any read(2) or write(2) calls will be passed to the appropriate function.

And finally, the next and prev fields are used by the tty layer to chain all of the different tty drivers together and should not be touched by the tty driver.

No Read?

One thing that might stand out in the above list of functions is the lack of a read function to be implemented by the tty driver. The tty layer contains a buffer that it uses to send data to user space when read(2) is called on a tty device node. This buffer needs to be filled up by the tty driver whenever it receives any data from the device. Because tty data does not show up whenever a user asks for it, or wants it, this model is necessary. That way the tty layer buffers any received data and the individual tty driver does not have to worry about blocking until data shows up on the tty line.

The Tiny tty Driver

Now that we have gone over all of the different fields, which ones are actually necessary to get a basic tty driver up and running? Listing 2 is an example of the most minimal tty driver possible. Once the steps shown there are completed, create the tiny_open, tiny_close, tiny_write and tiny_write_room functions and you are finished with a tiny tty driver.

Listing 2. Minimal TTY Driver


See Listing 3 for an example implementation of a tiny tty driver. This tty driver creates a timer to place data into the tty layer every two seconds in order to emulate real hardware. It also properly handles locking the device structures when it is run on an SMP machine.

Listing 3: example implementation of a tiny tty driver


Flow of Data

When the tty driver's open function is called, the driver is expected to save some data within the tty_struct variable that is passed to it. This is so the tty driver will know which device is being referenced when the later close, write and other functions are called. If this is not done, the driver can key off of the MINOR(tty->device) function, which returns the minor number for the device.

If you look at the tiny_open function, the tiny_serial structure is saved within the tty driver. This allows the tiny_write, tiny_write_room and tiny_close functions to retrieve the tiny_serial structure and manipulate it properly.

The open and close functions of a tty driver can be called multiple times for the same device as different user programs connect to the device. This could allow one process to read data and another to write data. To handle everything correctly, you should keep a count of how many times the port has been opened or closed. When the port is opened for the first time you can do the necessary hardware initialization and memory allocation. When the port is closed for the last time you can do the proper hardware shutdown and free any allocated memory. See the tiny_open() and tiny_close() functions for examples of how the number of times the port is opened can be accounted for.

When data is received from the hardware, it needs to be placed into the tty device's flip buffer. This can be done with the following bit of code:

for (i = 0; i < data_size; ++i) {

if (tty->flip.count >= TTY_FLIPBUF_SIZE)

tty_flip_buffer_push(tty);

tty_insert_flip_char(tty, data[i], 0);

}

tty_flip_buffer_push(tty);


This example makes sure there are no buffer overflows in the tty flip buffer as the data is being added. For drivers that accept data at very high rates, the tty->low_latency flag should be set, which will cause the last call to tty_flip_buffer_push() to be executed immediately when called. In the example driver, the tty_timer function inserts one byte of data into the tty's flip buffer and then resubmits the timer to be called again. This emulates a slow stream of characters being received from a hardware device.

When data is to be sent to the hardware, the write function is called. It is important that the write call checks the from_user flag to prevent it from accidentally trying to copy a user-space buffer directly into kernel space. The write function is allowed to return a short write. This means that the device was not able to send all of the data requested. It is up to the user-space program that is calling the write(2) function to check the return value properly to determine if all of the data was really sent. It is much easier for this check to be done in user space than it is for a kernel driver to sleep until all of the requested data is able to be sent out.

The tty Interface over Time

The tty layer has been very stable from the 2.0, 2.2 and 2.4 kernel versions, with only a very minor amount of functionality added over time. So a tty driver written for 2.0 will successfully work on 2.4 with almost no changes. Throughout the 2.5 kernel series, the tty layer has been marked for a rewrite, so this article may describe things that are no longer true. When in doubt, read the include/tty_driver.h file of the kernel version for which you wish to develop. Also, take a look at any of the tty drivers in the driver/char kernel directory for examples of how to implement some of the functions that are not covered here.

Conclusion

We have covered the basics of the tty layer, explaining all of the different fields in the tty_driver structure for the 2.4 kernel tree and pointing out which ones are necessary for a driver to implement. The tiny_tty.c driver (see Listing 3) is a good example of a very minimal tty driver that successfully works. Feel free to use this code as an example for your own tty drivers in the future.

Resources
  • Linux Core Kernel Commentary by Scott Maxwell. Coriolis Group, 1999.

  • Linux Device Drivers, 2nd Edition by Alessandro Rubini and Jonathan Corbet. O'Reilly & Associates, 2001. Also available online.

  • Understanding the Linux Kernel by Daniel P. Bovet and Marco Cesati. O'Reilly & Associates, 2000.

  • The Kernel Newbies Project provides a lot of documentation, the latest versions of the in-kernel documentation, good FAQs, lists of other recommended books, and a good mailing list and IRC channel for people of all skill levels.

  • "Linux 2.4 Kernel Internals" by Tigran Aivazian



Greg Kroah-Hartman is currently the Linux USB and PCI Hot Plug kernel maintainer. He works for IBM, doing various Linux kernel-related things.




Copyright © 2002 Specialized Systems Consultants, Inc., publishers of the monthly magazine Linux Journal. All rights reserved. Embedded Linux Journal Online is a cooperative project of Linux Journal and LinuxDevices.com.




The-tty-Layer/ class=sidenav>Discuss ELJonline:
The tty Layer
 
>>> The-tty-Layer/ class=sidenav>Be the FIRST to comment on this article!
 
 
 
>>> More Linux For Devices Articles Articles          >>> More By Linux Devices
 



FUEL Database on MontaVista Linux
Whether building a mobile handset, a car navigation system, a package tracking device, or a home entertainment console, developers need capable software systems, including an operating system, development tools, and supporting libraries, to gain maximum benefit from their hardware platform and to meet aggressive time-to-market goals.

Breaking New Ground: The Evolution of Linux Clustering
With a platform comprising a complete Linux distribution, enhanced for clustering, and tailored for HPC, Penguin Computing¿s Scyld Software provides the building blocks for organizations from enterprises to workgroups to deploy, manage, and maintain Linux clusters, regardless of their size.

Data Monitoring with NightStar LX
Unlike ordinary debuggers, NightStar LX doesn¿t leave you stranded in the dark. It¿s more than just a debugger, it¿s a whole suite of integrated diagnostic tools designed for time-critical Linux applications to reduce test time, increase productivity and lower costs. You can debug, monitor, analyze and tune with minimal intrusion, so you see real execution behavior. And that¿s positively illuminating.

Virtualizing Service Provider Networks with Vyatta
This paper highlights Vyatta's unique ability to virtualize networking functions using Vyatta's secure routing software in service provider environments.

High Availability Messaging Solution Using AXIGEN, Heartbeat and DRBD
This white paper discusses a high-availability messaging solution relying on the AXIGEN Mail Server, Heartbeat and DRBD. Solution architecture and implementation, as well as benefits of using AXIGEN for this setup are all presented in detail.

Understanding the Financial Benefits of Open Source
Will open source pay off? Open source is becoming standard within enterprises, often because of cost savings. Find out how much of a financial impact it can have on your organization. Get this methodology and calculator now, compliments of JBoss.

Embedded Hardware and OS Technology Empower PC-Based Platforms
The modern embedded computer is the jack of all trades appearing in many forms.

Data Management for Real-Time Distributed Systems
This paper provides an overview of the network-centric computing model, data distribution services, and distributed data management. It then describes how the SkyBoard integration and synchronization service, coupled with an implementation of the OMG¿s Data Distribution Service (DDS) standard, can be used to create an efficient data distribution, storage, and retrieval system.

7 Advantages of D2D Backup
For decades, tape has been the backup medium of choice. But, now, disk-to-disk (D2D) backup is gaining in favor. Learn why you should make the move in this whitepaper.

Got a HOT tip?   please tell us!
Free weekly newsletter
Enter your email...
Click for a profile of each sponsor:
SUPER-PLATINUM SPONSOR
MOBLIN NEWS & LINKS
Moblin Official Blog
A Framework for Innovation
Compliance Means ...
MiND Over Matter
FEATURED VIDEO

Doug Fisher and Jim Zemlin
PLATINUM SPONSORS
GOLD SPONSORS
(Become a sponsor)

ADVERTISEMENT
(Advertise here)

Check out the latest Linux powered...

Mobile phones!

MIDs, UMPCs
& tablets

Mobile devices

Other cool
gadgets

Resource Library

• Unix, Linux Uptime and Reliability Increase: Patch Management Woes Plague Windows Yankee Group survey finds IBM AIX Unix is highest in ...
• Scalable, Fault-Tolerant NAS for Oracle - The Next Generation For several years NAS has been evolving as a storage ...
• Managing Software Intellectual Property in an Open Source World This whitepaper draws on the experiences of the Black Duck ...
• Open Source Security Myths Dispelled Is it risky to trust mission-critical infrastructure to open source ...
• Bringing IT Operations Management to Open Source & Beyond Download this IDC analyst report to learn how open source ...


BREAKING NEWS

• NAS system houses 2.5-inch drives for up to 6TB
• Atom SBC boasts special low-power mode
• Android leaps to rugged handheld, and more phones
• Simulator runs Android apps on Ubuntu
• Fanless industrial PC taps Atom
• Router platform runs OpenWRT Linux
• Feature-packed UMPC survives four-foot drops
• UMPC pioneer gives up the ghost
• Biodegradable, solar-powered netbook runs Linux
• Hypervisor rev'd for higher reliability
• Eurotech spins Atom development kits
• Home media server to demo on Intel Atom platform
• Atom boards feature fanless DC operation
• Low-cost pluggable NAS adds Linux support
• Taiwan open source conference sets agenda


Most popular stories -- past 90 days:
• Linux boots in 2.97 seconds
• Tiniest Linux system, yet?
• Linux powers "cloud" gaming console
• Report: T-Mobile sells out first 1.5 million G1s
• Open set-top box ships
• E17 adapted to Linux devices, demo'd on Treo650
• Android debuts
• First ALP Linux smartphone?
• Cortex-A8 gaming handheld runs Linux
• Ubuntu announces ARM port


DesktopLinux headlines:
• Simulator runs Android apps on Ubuntu
• Hypervisor rev'd for higher reliability
• Pluggable NAS now supports Linux desktops
• Moblin v2 beta targets netbooks
• Linux-ready netbook touted as "Student rugged"
• USB display technology heading for Linux
• Ubuntu One takes baby step to the cloud
• Game over for Linux netbooks?
• Linux Foundation relaunches Linux web site
• Dell spins lower-cost netbook


Also visit our sister site:


Sign up for LinuxForDevices.com's...

news feed


Or, follow us on Twitter...