TTY 3

来源:互联网 发布:java培训免费试听 编辑:程序博客网 时间:2024/05/29 12:01

 TTY 线路设置

当一个用户要改变一个 tty 设备的线路设置或者获取当前线路设置, 他调用一个许多的不同 termios 用户空间库函数或者直接对这个tty 设备的节点调用 ioctl. tty 核心转换这 2 种接口为许多不同的 tty 驱动函数回调和 ioctl 调用.

18.3.1. set_termios 函数

大部分 termios 用户空间函数被库转换为一个对驱动节点的 ioctl 调用. 大量的不同的 tty ioctl 调用接着被 tty核心转换为一个对 tty 驱动的单个 set_termios 函数调用. set_termios 调用需要决定哪个线路设置它被请求来改变,接着在 tty 设备中做这些改变. tty 驱动必须能够解码所有的在 termios 结构中的不同设置并且响应任何需要的改变.这是一个复杂的任务, 因为所有的线路设置以很多的方式被包装进 termios 结构.

一个 set_termios 函数应当做的第一件事情是决定任何事情是否真的需要改变. 这可使用下面的代码完成:

unsigned int cflag;
cflag = tty->termios->c_cflag;
/* check that they really want us to change something */
if (old_termios)
{
if ((cflag == old_termios->c_cflag) &&
(RELEVANT_IFLAG(tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) {
printk(KERN_DEBUG " - nothing to change.../n");
return;
}
}

RELEVANT_IFLAG 宏定义为:

#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))

而且用在屏蔽掉 cflags 变量的重要位. 接着这个和原来的值比较, 并且看是否它们不同. 如果不, 什么不改变, 因此我们返回.注意 old_termios 变量是第一个被检查来看是否它指向一个有效的结构, 在它被存取之前. 这是需要的, 因为有时这个变量被设为NULL. 试图存取一个 NULL 指针成员会导致内核崩溃.

为查看需要的字节大小, CSIZE 位掩码可用来从 cflag 变量区分出正确的位. 如果这个大小无法知道, 习惯上确实是 8 个数据位. 这个可如下实现:

/* get the byte size */
switch (cflag & CSIZE)
{

case CS5:
printk(KERN_DEBUG " - data bits = 5/n");
break;
case CS6:
printk(KERN_DEBUG " - data bits = 6/n");
break;
case CS7:
printk(KERN_DEBUG " - data bits = 7/n");
break;
default:
case CS8:
printk(KERN_DEBUG " - data bits = 8/n");
break;
}

为决定需要的奇偶值, PARENB 位掩码可对 cflag 变量检查来告知是否任何奇偶要被设置. 如果这样, PARODD 位掩码可用来决定是否奇偶应当是奇或者偶. 这个的一个实现是:

/* determine the parity */
if (cflag & PARENB)
if (cflag & PARODD)
printk(KERN_DEBUG " - parity = odd/n");
else
printk(KERN_DEBUG " - parity = even/n");
else
printk(KERN_DEBUG " - parity = none/n");

请求的停止位也可使用 CSTOPB 位掩码从 cflag 变量中来知道. 一个实现是:

/* figure out the stop bits requested */
if (cflag & CSTOPB)

printk(KERN_DEBUG " - stop bits = 2/n");
else

printk(KERN_DEBUG " - stop bits = 1/n");

有 2 个基本的流控类型: 硬件和软件. 为确定是否用户要求硬件流控, CRTSCTS 位掩码用来对 cflag 变量检查. 它的一个例子是:

/* figure out the hardware flow control settings */
if (cflag & CRTSCTS)

printk(KERN_DEBUG " - RTS/CTS is enabled/n");
else

printk(KERN_DEBUG " - RTS/CTS is disabled/n");

确定软件流控的不同模式和不同的起停字符是有些复杂:

/* determine software flow control */
/* if we are implementing XON/XOFF, set the start and

* stop character in the device */
if (I_IXOFF(tty) || I_IXON(tty))
{
unsigned char stop_char = STOP_CHAR(tty);
unsigned char start_char = START_CHAR(tty);

/* if we are implementing INBOUND XON/XOFF */
if (I_IXOFF(tty))
printk(KERN_DEBUG " - INBOUND XON/XOFF is enabled, "
"XON = %2x, XOFF = %2x", start_char, stop_char);
else
printk(KERN_DEBUG" - INBOUND XON/XOFF is disabled");

/* if we are implementing OUTBOUND XON/XOFF */
if (I_IXON(tty))
printk(KERN_DEBUG" - OUTBOUND XON/XOFF is enabled, "
"XON = %2x, XOFF = %2x", start_char, stop_char);
else
printk(KERN_DEBUG" - OUTBOUND XON/XOFF is disabled");
}

最后, 波特率需要确定. tty 核心提供了一个函数, tty_get_baud_rate, 来帮助做这个. 这个函数返回一个整型数指示请求的波特率给特定的 tty 设备.

/* get the baud rate wanted */
printk(KERN_DEBUG " - baud rate = %d", tty_get_baud_rate(tty));

现在 tty 驱动已经确定了所有的不同的线路设置, 它可以基于这些值正确设置硬件.

18.3.2. tiocmget 和 tiocmset

在 2.4 和老的内核, 常常有许多 tty ioctl 调用来获得和设置不同的控制线路设置. 这些被常量 TIOCMGET,TIOCMBIS, TIOCMBIC, 和 TIOCMSET 表示. TIOCMGET 用来获得内核的线路设置值, 并且对于 2.6 内核,这个 ioctl 调用已经被转换为一个 tty 驱动回调函数, 称为 tiocmget. 其他的 3 个 ioctls已经被简化并且现在用单个的 tty 驱动回调函数所代表, 称为 tiocmset.

tty 驱动中的 iocmget 函数被 tty 核心所调用, 当核心需要知道当前的特定 tty 设备的控制线的物理值.这常常用来获取一个串口的 DTR 和 RTS 线的值. 如果 tty 驱动不能直接读串口的 MSR 或者 MCR 寄存器,因为硬件不允许这样, 一个它们的拷贝应当在本地保持. 许多 USB-到-串口 驱动必须实现这类的"影子"变量. 这是这个函数能如何被实现,任何一个本地的这些值的拷贝被保存:

static int tiny_tiocmget(struct tty_struct *tty, struct file *file)
{
struct tiny_serial *tiny = tty->driver_ data;
unsigned int result = 0;
unsigned int msr = tiny->msr;
unsigned int mcr = tiny->mcr;
result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0) | /* DTR is set */
((mcr & MCR_RTS) ? TIOCM_RTS : 0) | /* RTS is set */
((mcr & MCR_LOOP) ? TIOCM_LOOP : 0) | /* LOOP 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 */
return result;
}

在 tty 驱动中的 tiocmset 函数被 tty 核心调用, 当核心要设置一个特定 tty 设备的控制线值. tty 核心告知tty 驱动设置什么值和清理什么, 通过传递它们用 2 个变量: set 和 clear. 这些变量包含一个应当改变的线路设置的位掩码. 一个ioctl 调用从不请求驱动既设置又清理一个特殊的位在同一时间, 因此先发生什么操作没有关系. 这是一个例子, 关于这个函数如何能够由一个tty 驱动实现:

static int tiny_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set , unsigned int clear) 
{
struct tiny_serial *tiny = tty->driver_data;
unsigned int mcr = tiny->mcr;

if (set & TIOCM_RTS)
mcr |= MCR_RTS;
if (set & TIOCM_DTR)
mcr |= MCR_RTS;

if (clear & TIOCM_RTS)
mcr &= ~MCR_RTS;
if (clear & TIOCM_DTR)
mcr &= ~MCR_RTS;

/* set the new MCR value in the device */
tiny->mcr = mcr;
return 0;

}
原创粉丝点击