The tty Layer
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 [available at ftp.linuxjournal.com/pub/lj/listings/issue100/5896.tgz] 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.
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:
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.
Fast/Flexible Linux OS Recovery
On Demand Now
In this live one-hour webinar, learn how to enhance your existing backup strategies for complete disaster recovery preparedness using Storix System Backup Administrator (SBAdmin), a highly flexible full-system recovery solution for UNIX and Linux systems.
Join Linux Journal's Shawn Powers and David Huffman, President/CEO, Storix, Inc.
Free to Linux Journal readers.Register Now!
- The Italian Army Switches to LibreOffice
- Download "Linux Management with Red Hat Satellite: Measuring Business Impact and ROI"
- Petros Koutoupis' RapidDisk
- Linux Mint 18
- Oracle vs. Google: Round 2
- The FBI and the Mozilla Foundation Lock Horns over Known Security Hole
- Varnish Software's Varnish Massive Storage Engine
- Privacy and the New Math
- Ben Rady's Serverless Single Page Apps (The Pragmatic Programmers)
Until recently, IBM’s Power Platform was looked upon as being the system that hosted IBM’s flavor of UNIX and proprietary operating system called IBM i. These servers often are found in medium-size businesses running ERP, CRM and financials for on-premise customers. By enabling the Power platform to run the Linux OS, IBM now has positioned Power to be the platform of choice for those already running Linux that are facing scalability issues, especially customers looking at analytics, big data or cloud computing.
￼Running Linux on IBM’s Power hardware offers some obvious benefits, including improved processing speed and memory bandwidth, inherent security, and simpler deployment and management. But if you look beyond the impressive architecture, you’ll also find an open ecosystem that has given rise to a strong, innovative community, as well as an inventory of system and network management applications that really help leverage the benefits offered by running Linux on Power.Get the Guide