14.5 TTY线路设置
14.5.1线路设置用户空间接口
用户可用下面的两种方式改变 tty 设备的线路设置或者获取当前线路设置。
1.调用用户空间的 termios 库函数
用户空间的应用程序需引用 termios.h 头文件,这个头文件包含终端设备的 I/O 接口,由 POSIX 定义的标准方法。对终端设备操作模式的描述由 termios 结构体完成,这个结构体包含 c_iflag、c_oflag、c_cflag、c_lflag 和 c_cc[]几个成员。
termios 的 c_cflag 主要包含如下位域信息:CSIZE(字长)、CSTOPB(两个停止位)、PARENB(奇偶校验位使能)、PARODD(奇校验位,当 PARENB 被使能时)、CREAD(字符接收使能,如果没有置位,仍然从端口接收字符,但这些字符都要被丢弃)、CRTSCTS(如果被置位,使能CTS 状态改变报告)、CLOCAL(如果没有置位,使能调制解调器状态改变报告)。
termios 的 c_iflag (输入模式标志)主要包含如下位域信息:INPCK(使能帧和奇偶校验错误检查)、BRKINT(break 将清除终端输入/输出队列,向该终端上前台的程序发出 SIGINT 信号)、PARMRK(奇偶
校验和帧错误被标记,在 INPCK 被设置且 IGNPAR 未被设置的情况下才有意义)、IGNPAR(忽略奇偶校验和帧错误)、IGNBRK(忽略 break)。
通过 tcgetattr()、tcsetattr()函数即可完成对终端设备的操作模式的获取和设置,这两个函数的原型如下:
int tcgetattr (int fd, struct termios *termios_p);
int tcsetattr (int fd, int optional_actions, struct termios *termios_p);
通过如下一组函数完成输入/输出波特率的获取和设置:
speed_t cfgetospeed (struct termios *termios_p); //获得输出波特率
speed_t cfgetispeed (struct termios *termios_p); //获得输入波特率
int cfsetospeed (struct termios *termios_p, speed_t speed); //设置输出波特率
int cfsetispeed (struct termios *termios_p, speed_t speed); //设置输入波特率
如下一组函数完成线路控制:
int tcdrain (int fd); //等待所有输出都被发送
int tcflush (int fd, int queue_selector); //flush 输入/输出缓冲区
int tcflow (int fd, int action); //对输入和输出流进行控制
int tcsendbreak (int fd, int duration); //发送 break
分析:
tcflush刷清(抛弃)输入缓冲区(终端驱动程序已接收到,但用户程序尚未读取)或输出缓冲区(用户程序已经写,但驱动尚未发送),queue_selector 参数可取 TCIFLUSH(刷清输入队列)、TCOFLUSH(刷清输出队列)或 TCIOFLUSH(刷清输入、输出队列)。
tcflow对输入输出进行流控制,action 参数可取 TCOOFF(输出被挂起)、TCOON(重新启动以前被挂起的输出)、TCIOFF(发送 1 个 STOP 字符,使终端设备暂停发送数据)、TCION(发送 1 个 START 字符,使终端恢复发送数据)。
tcsendbreak在一个指定的时间区间内发送连续的二进位数 0。若 duration 参数为 0,则此种发送延续 0.25~0.5 秒。POSIX.1 说明若 duration 非 0,则发送时间依赖于实现。
2.对 tty 设备节点进行 ioctl()调用
大部分 termios 库函数会被转化为对 tty 设备节点的 ioctl()调用,例如 tcgetattr()、tcsetattr()函数对应着 TCGETS、TCSETS IO 控制命令。
TIOCMGET(获得 MODEM 状态位)、TIOCMSET(设置 MODEM 状态位)、TIOCMBIC(清除指示 MODEM 位)、TIOCMBIS(设置指示 MODEM 位)这 4 个 I/O 控制命令用于获取和设置 MODEM 握手。
14.5.2 tty驱动set_termios函数
大部分 termios 用户空间函数被库转换为对驱动节点的 ioctl()调用,而 tty ioctl 中的大部分命令会被 tty 核心转换为对 tty 驱动的 set_termios()函数的调用。set_termios()函数需要根据用户对termios 的设置(termios 设置包括字长、奇偶校验位、停止位、波特率等)完成实际的硬件设置。
tty_operations 中的 set_termios()函数原型为:
void (*set_termios)(struct tty_struct *tty, struct ktermios * old);
新的设置被保存在 tty_struct 中,旧的设置被保存在 old 参数中,若新旧参数相同,则什么都不需要做,对于被改变的设置,需完成硬件上的设置。
14.5.3 tty 驱动的 tiocmget 和 tiocmset
对 TIOCMGET、TIOCMSET、TIOCMBIC 和 TIOCMBIS IO 控制命令的调用将被 tty 核心转换为对 tty 驱动 tiocmget()函数和 tiocmset()函数的调用,TIOCMGET 对应 tiocmget()函数,TIOCMSET、TIOCMBIC 和 TIOCMBIS 对应 tiocmset()函数,分别用于读取 Modem 控制的设置和进行Modem的设置。代码清单14.10所示为tiocmget()函数的范例,代码清单14.11所示为tiocmset()函数的范例。
代码清单 14.10 tty 驱动程序 tiocmget()函数范例
static int xxx_tiocmget(struct tty_struct *tty, struct file *file)
{
struct xxx_tty *info = tty->driver_ data;
unsigned int result = 0;
unsigned int msr = info->msr;
unsigned int mcr = info->mcr;
result = ((mcr &MCR_DTR) ? TIOCM_DTR : 0) | /* DTR 被设置 */
((mcr &MCR_RTS) ? TIOCM_RTS : 0) | /* RTS 被设置 */
((mcr &MCR_LOOP) ? TIOCM_LOOP : 0) | /* LOOP 被设置 */
((msr &MSR_CTS) ? TIOCM_CTS : 0) | /* CTS 被设置 */
((msr &MSR_CD) ? TIOCM_CAR : 0) | /* CD 被设置*/
((msr &MSR_RI) ? TIOCM_RI : 0) | /* 振铃指示被设置 */
((msr &MSR_DSR) ? TIOCM_DSR : 0); /* DSR 被设置 */
return result;
}
代码清单 14.11 tty 驱动程序 tiocmset()函数范例
static int xxx_tiocmset(struct tty_struct *tty, struct file *file, unsigned
int set, unsigned int clear)
{
struct xxx_tty *info = tty->driver_data;
unsigned int mcr = info->mcr;
if (set &TIOCM_RTS) /* 设置 RTS */
mcr |= MCR_RTS;
if (set &TIOCM_DTR) /* 设置 DTR */
mcr |= MCR_RTS;
if (clear &TIOCM_RTS) /* 清除 RTS */
mcr &= ~MCR_RTS;
if (clear &TIOCM_DTR) /* 清除 DTR */
mcr &= ~MCR_RTS;
/* 设置设备新的 MCR 值 */
tiny->mcr = mcr;
return 0;
}
备注:
tiocmget()函数会访问 MODEM 状态寄存器(MSR),而 tiocmset()函数会访问 MODEM 控制寄存器(MCR)。
14.5.4 tty 驱动 ioctl 函数
当用户在 tty 设备节点上进行 ioctl()调用时,tty_operations 中的 ioctl()函数会被 tty 核心调用。如果 tty 驱动不知道如何处理传递给它的 IOCTL 值,它返回–ENOIOCTLCMD,之后 tty 核心会执行一个通用的操作。
驱动中常见的需处理的 I/O 控制命令包括 TIOCSERGETLSR(获得这个 tty 设备的线路状态寄存器 LSR 的值)、TIOCGSERIAL(获得串口线信息)、TIOCMIWAIT(等待 MSR 改变)、TIOCGICOUNT(获得中断计数)等。代码清单 14.12 给出了 tty 驱动程序 ioctl()函数的范例。
代码清单 14.12 tty 驱动程序 ioctl()函数范例
static int xxx_ioctl(struct tty_struct *tty, struct file *filp, unsigned int
cmd, unsigned long arg)
{
struct xxx_tty *info = tty->driver_data;
...
/* 处理各种命令 */
switch (cmd){
case TIOCGSERIAL:
...
case TIOCSSERIAL:
...
case TIOCSERCONFIG:
...
case TIOCMIWAIT:
...
case TIOCGICOUNT:
...
case TIOCSERGETLSR:
...
}
...
}