第14章 Linux终端设备驱动之TTY线路设置

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:
         ...
         }
         ...
 }


猜你喜欢

转载自blog.csdn.net/xiezhi123456/article/details/80692775