The tty Layer, Part II
In the first part of this column (LJ, August 2002) we covered the basics of the tty layer and how to create a minimal tty driver. Now we move on and cover more of the tty layer, trying to explain some of the advanced portions.
Remember from Part I the big struct tty_driver structure that all tty drivers need to implement? Let's focus on a few of its functions that were not fully covered last time.
The ioctl function callback in the struct tty_driver is called by the tty layer when ioctl(2) is called on the device node. If your driver does not know how to handle the ioctl value passed to it, it should return -ENOIOCTLCMD to try to let the tty layer implement a generic version of the call, if possible. But what ioctl values will your driver be expected to handle?
The 2.4.19 kernel defines around 60 different possible tty ioctls. Your tty driver doesn't have to implement all of them, but it is a good idea to try to handle the following common ones.
TIOCMGET: called when the user wants to find the status of control lines of the serial port, such as the DTR or RTS lines. If you can directly read the MSR or MCR registers of your serial port, or if you keep a copy of them locally (like some USB to serial type devices need to do), here is how this ioctl can be implemented:
int tiny_ioctl (struct tty_struct *tty,
struct file *file,
unsigned int cmd, unsigned long arg)
{
struct tiny_private *tp = tty->private;
if (cmd == TIOCMGET) {
unsigned int result = 0;
unsigned int msr = tp->msr;
unsigned int mcr = tp->mcr;
result = ((mcr & MCR_DTR) ? TIOCM_DTR: 0)
/* DTR is set */
| ((mcr & MCR_RTS) ? TIOCM_RTS: 0)
/* RTS is set */
| ((msr & MSR_CTS) ? TIOCM_CTS: 0)
/* CTS is set */
| ((msr & MSR_CD) ? TIOCM_CAR: 0)
/* Carrier detect is set*/
| ((msr & MSR_RI) ? TIOCM_RI: 0)
/* Ring Indicator is set */
| ((msr & MSR_DSR) ? TIOCM_DSR: 0);
/* DSR is set */
if (copy_to_user((unsigned int *)arg,
&result,
sizeof(unsigned int)))
return -EFAULT;
return 0;
}
return -ENOIOCTLCMD;
}
TIOCMBIS, TIOCMBIC and TIOCMSET: used to set different modem control registers on your tty device. The TIOCMBIS call can turn on the RTS, DTR or loopback registers, while the TIOCMBIC call can turn them off. The TIOCMSET call turns all three values off, and then it sets only the specific values it wants. Here's an example of how this can be handled:
int tiny_ioctl (struct tty_struct *tty,
struct file *file,
unsigned int cmd,
unsigned long arg)
{
struct tiny_private *tp = tty->private;
if ((cmd == TIOCMBIS) ||
(cmd == TIOCMBIC) ||
(cmd == TIOCMSET)) {
unsigned int value;
unsigned int mcr = tp->mcr;
if (copy_from_user(&value,
(unsigned int *)arg,
sizeof(unsigned int)))
return -EFAULT;
switch (cmd) {
case TIOCMBIS:
if (value & TIOCM_RTS)
mcr |= MCR_RTS;
if (value & TIOCM_DTR)
mcr |= MCR_RTS;
if (value & TIOCM_LOOP)
mcr |= MCR_LOOPBACK;
break;
case TIOCMBIC:
if (value & TIOCM_RTS)
mcr &= ~MCR_RTS;
if (value & TIOCM_DTR)
mcr &= ~MCR_RTS;
if (value & TIOCM_LOOP)
mcr &= ~MCR_LOOPBACK;
break;
case TIOCMSET:
/* turn off the RTS and DTR and
* LOOPBACK, and then only turn on
* what was asked for */
mcr &= ~(MCR_RTS | MCR_DTR |
MCR_LOOPBACK);
mcr |= ((value & TIOCM_RTS) ?
MCR_RTS : 0);
mcr |= ((value & TIOCM_DTR) ?
MCR_DTR : 0);
mcr |= ((value & TIOCM_LOOP) ?
MCR_LOOPBACK : 0);
break;
}
/* set the new MCR value in the device */
tp->mcr = mcr;
return 0;
}
return -ENOIOCTLCMD;
}
Note, the loopback request (TIOCM_LOOP) is not present in the 2.2
kernel, but it is in the 2.4 and newer kernels.
If your tty driver can handle these four ioctls, it will work with the majority of existing user-space programs. However, there is always an odd program that asks for one of the other ioctls. So you may want to consider handling some of these other common ioctl functions as well.
TIOCSERGETLSR: called to retrieve the line status register (LSR) value of your tty device.
TIOCGSERIAL: called to get a bunch of serial line information from your device all at once. A pointer to a struct serial_struct is passed to this call, which your driver should fill up with the proper values. Some programs (like setserial and dip) call this function to make sure that the baud rate was set properly and to get general information on what type of device your tty is. Here's an example of how this can be implemented:
int tiny_ioctl (struct tty_struct *tty,
struct file *file,
unsigned int cmd,
unsigned long arg)
{
struct tiny_private *tp = tty->private;
if (cmd == TIOCGSERIAL) {
struct serial_struct tmp;
if (!arg)
return -EFAULT;
memset(&tmp, 0, sizeof(tmp));
tmp.type = tp->type;
tmp.line = tp->line;
tmp.port = tp->port;
tmp.irq = tp->irq;
tmp.flags = ASYNC_SKIP_TEST |
ASYNC_AUTO_IRQ;
tmp.xmit_fifo_size = tp->xmit_fifo_size;
tmp.baud_base = tp->baud_base;
tmp.close_delay = 5*HZ;
tmp.closing_wait = 30*HZ;
tmp.custom_divisor = tp->custom_divisor;
tmp.hub6 = tp->hub6;
tmp.io_type = tp->io_type;
if (copy_to_user(arg, &tmp, sizeof(struct
serial_struct)))
return -EFAULT;
return 0;
}
return -ENOIOCTLCMD;
}
TIOCSSERIAL: the opposite of TIOCGSERIAL; with this one the user can set the serial line status of your device all at once. A pointer to a struct serial_struct is passed to this call, full of the data to which your device should now be set. If your device does not implement this call, almost all programs still will work properly.
TIOCMIWAIT: an interesting call. If the user makes this ioctl call, they want to sleep within the kernel until something happens to the MSR register of the tty device. The “arg” parameter will contain the type of event for which the user is waiting. This ioctl is commonly used to wait for status line changes, signaling that more data is ready to be sent to the device.
Be careful in implementing the TIOCMIWAIT ioctl, however. Almost all of the existing kernel drivers that use it also use the interruptible_sleep_on() call, which is unsafe. (A lot of nasty race conditions are involved.) Instead, a wait_queue should be used in order to avoid these problems. Here's an example of a correct way to implement TIOCMIWAIT:
int tiny_ioctl (struct tty_struct *tty,
struct file *file,
unsigned int cmd,
unsigned long arg)
{
struct tiny_private *tp = tty->private;
if (cmd == TIOCMIWAIT) {
DECLARE_WAITQUEUE(wait, current);
struct async_icount cnow;
struct async_icount cprev;
cprev = tp->icount;
while (1) {
add_wait_queue(&tp->wait, &wait);
set_current_state(TASK_INTERRUPTIBLE);
schedule();
remove_wait_queue(&tp->wait, &wait);
/* see if a signal woke us up */
if (signal_pending(current))
return -ERESTARTSYS;
cnow = edge_port->icount;
if (cnow.rng == cprev.rng &&
cnow.dsr == cprev.dsr &&
cnow.dcd == cprev.dcd &&
cnow.cts == cprev.cts)
return -EIO;
/* no change => error */
if (((arg & TIOCM_RNG) &&
(cnow.rng != cprev.rng)) ||
((arg & TIOCM_DSR) &&
(cnow.dsr != cprev.dsr)) ||
((arg & TIOCM_CD) &&
(cnow.dcd != cprev.dcd)) ||
((arg & TIOCM_CTS) &&
(cnow.cts != cprev.cts)) ) {
return 0;
}
cprev = cnow;
}
}
return -ENOIOCTLCMD;
}
Somewhere in the portion of the code that recognizes that the MSR register changes, the line:
wake_up_interruptible(&tp->wait);must be called for this code to work properly.
TIOCGICOUNT: called when the user wants to know the number of serial line interrupts that have occurred. The kernel is passed a pointer to a struct serial_icounter_struct, which needs to be filled up. This call is often made in conjunction with the previous TIOCMIWAIT ioctl call. If you keep track of all of these interrupts while your driver is operating, the code to implement this call can be quite simple. See drivers/usb/serial/io_edgeport.c for an example.
Today’s modular x86 servers are compute-centric, designed as a least common denominator to support a wide range of IT workloads. Those generic, virtualized IT workloads have much different resource optimization requirements than hyperscale and cloud applications. They have resulted in a “one size fits all” enterprise IT architecture that is not optimized for a specific set of IT workloads, and especially not emerging hyperscale workloads, such as web applications, big data, and object storage. In this report, you will learn how shifting the focus from traditional compute-centric IT architectures to an innovative disaggregated fabric-based architecture can optimize and scale your data center.
Sponsored by AMD
Built-in forensics, incident response, and security with Red Hat Enterprise Linux 6
Every security policy provides guidance and requirements for ensuring adequate protection of information and data, as well as high-level technical and administrative security requirements for a system in a given environment. Traditionally, providing security for a system focuses on the confidentiality of the information on it. However, protecting the data integrity and system and data availability is just as important. For example, when processing United States intelligence information, there are three attributes that require protection: confidentiality, integrity, and availability.
Learn more about catching the bad guy in this free white paper.
Sponsored by DLT Solutions
| Making Linux and Android Get Along (It's Not as Hard as It Sounds) | May 16, 2013 |
| Drupal Is a Framework: Why Everyone Needs to Understand This | May 15, 2013 |
| Home, My Backup Data Center | May 13, 2013 |
| Non-Linux FOSS: Seashore | May 10, 2013 |
| Trying to Tame the Tablet | May 08, 2013 |
| Dart: a New Web Programming Experience | May 07, 2013 |
- RSS Feeds
- New Products
- Making Linux and Android Get Along (It's Not as Hard as It Sounds)
- A Topic for Discussion - Open Source Feature-Richness?
- Drupal Is a Framework: Why Everyone Needs to Understand This
- Home, My Backup Data Center
- New Products
- Paranoid Penguin - Building a Secure Squid Web Proxy, Part IV
- Trying to Tame the Tablet
- Developer Poll




5 min 58 sec ago
4 hours 19 min ago
6 hours 52 min ago
11 hours 31 min ago
13 hours 53 min ago
1 day 6 hours ago
1 day 9 hours ago
1 day 10 hours ago
1 day 11 hours ago
1 day 11 hours ago