有串口、usb转串口、调制解调器(传统的WinModem类设备)等。Linux-tty驱动程序的核心紧挨在标准字符设备驱动层之下,并体统了一系列的功能,作为接口被终端类型设备使用。内核负责控制通过tty设备的数据流,并且格式化这些数据。为了控制数据流,有许多不同的线路规程(line discipline)可以虚拟地“插入”任何的tty设备上,这由不同的tty线路规程驱动程序实现。tty线路规程的作用是使用特殊的方法,把从用户或者硬件那里接受的数据格式化。这种格式化通常使用一些协议来完成转换,比如PPP或者是蓝牙Bluetooth。
用户空间主要是通过设备文件同tty_core交互。tty_core根据用空间操作的类型再选择跟line discipline和tty_driver交互。例如设置硬件的ioctl指令就直接交给tty_driver处理。Read和write操作就会交给 line discipline处理。Line discipline是线路规程的意思。正如它的名字一样,它表示的是这条终端”线程”的输入与输出规范设置.主要用来进行输入/输出数据的预处理。处理之后。就会将数据交给tty_driver。Tty_driver就是终端对应的驱动了。它将字符转换成终端可以理解的字串,再将其传给终端设备。
值得注意的是,这个架构没有为tty_driver提供read操作。也就是说tty_core 和line discipline都没有办法从tty_driver里直接读终端信息。这是因为tty_driver对应的hardware并不一定是输入数据和输出 数据的共同负载者。例如控制终端,输出设备是显示器。输入设备是键盘。基于这样的原理。在line discipline中有一个输入缓存区。并提供了一个名叫receive_buf()的接口函数。对应的终端设备只要调用line discipine的receiver_buf函数,将数据写入到输入缓存区就可以了。
如果一个设备同时是输入设备又是输出设备。那在设备的中断处理中调用receive_buf()将数据写入即可。
#include <linux/tty_driver.h> struct tty_driver { int magic; /*幻数,通常被设置为TTY_DRIVER_MAGIC。在alloc_tty_driver函数中被初始化*/ struct kref kref; /* Reference management */ struct cdev cdev; struct module *owner;/*驱动模块的所有者*/ const char *driver_name;/*驱动程序的名称,在/proc/tty和sysfs中使用*/ const char *name;/*驱动节点的名字*/ int name_base; /*为穿件设备名字而使用的开始编号*/ int major; /*驱动程序的主设备号*/ int minor_start; /*驱动程序使用的最小次设备号*/ int minor_num; /* number of *possible* devices */ int num; /*可以分配给驱动程序次设备号的个数*/ short type; /* type of tty driver */ short subtype; /* subtype of tty driver */ struct ktermios init_termios; /*当被创建时,含有初始值的termios结构*/ int flags; /*驱动程序标志位*/ struct proc_dir_entry *proc_entry; /*驱动程序的/proc入口结构体*/ struct tty_driver *other; /*指向tty从属设备驱动程序的指针*/ /* * Pointer to the tty data structures */ struct tty_struct **ttys; struct ktermios **termios; struct ktermios **termios_locked; void *driver_state; /* * Driver methods */ const struct tty_operations *ops; struct list_head tty_drivers; };
#include <linux/tty_driver.h> struct tty_operations { struct tty_struct * (*lookup)(struct tty_driver *driver, struct inode *inode, int idx); int (*install)(struct tty_driver *driver, struct tty_struct *tty); void (*remove)(struct tty_driver *driver, struct tty_struct *tty); int (*open)(struct tty_struct * tty, struct file * filp);/*open函数*/ void (*close)(struct tty_struct * tty, struct file * filp);/*close函数*/ void (*shutdown)(struct tty_struct *tty); void (*cleanup)(struct tty_struct *tty); int (*write)(struct tty_struct * tty, const unsigned char *buf, int count);/*write函数*/ int (*put_char)(struct tty_struct *tty, unsigned char ch);/*单字符写入函数*/ void (*flush_chars)(struct tty_struct *tty); int (*write_room)(struct tty_struct *tty);/*检测缓冲区的剩余空间*/ int (*chars_in_buffer)(struct tty_struct *tty);/*检测包含数据的缓冲区数量*/ int (*ioctl)(struct tty_struct *tty, unsigned int cmd, unsigned long arg);/*当设备节点的调用ioctl(2)时,该函数被tty核心调用*/ long (*compat_ioctl)(struct tty_struct *tty, unsigned int cmd, unsigned long arg); void (*set_termios)(struct tty_struct *tty, struct ktermios * old);/*改变设备的termios设置*/ void (*throttle)(struct tty_struct * tty);/*当tty核心的输入缓冲区满的时候,调用该函数。 tty驱动程序将试图通知设备,不要再发送更多的字符。*/ void (*unthrottle)(struct tty_struct * tty);/*当tty核心的输入缓冲区被清空是,调用该函数,使能其接受更多的数据*/ void (*stop)(struct tty_struct *tty);/*tty驱动程序将停止向设备发送数据*/ void (*start)(struct tty_struct *tty);/*恢复数据的传送*/ void (*hangup)(struct tty_struct *tty);/*当tty驱动程序挂起tty设备时,调用该函数。此时对任何特定硬件的操作应当被挂起*/ int (*break_ctl)(struct tty_struct *tty, int state);/*中断连接控制函数*/ void (*flush_buffer)(struct tty_struct *tty);/*刷新缓冲区,并丢失里面的数据*/ void (*set_ldisc)(struct tty_struct *tty);/*设置线路规程的函数*/ void (*wait_until_sent)(struct tty_struct *tty, int timeout);/*向硬件发送数据*/ void (*send_xchar)(struct tty_struct *tty, char ch);/*发送X类型的字符函数。要发送的字符放在ch变量中*/ int (*tiocmget)(struct tty_struct *tty);/*获得特定tty设备当前的线路设置*/ int (*tiocmset)(struct tty_struct *tty, unsigned int set, unsigned int clear);/*为特定的tty设备设置当前线路*/ int (*resize)(struct tty_struct *tty, struct winsize *ws); int (*set_termiox)(struct tty_struct *tty, struct termiox *tnew); int (*get_icount)(struct tty_struct *tty, struct serial_icounter_struct *icount); #ifdef CONFIG_CONSOLE_POLL int (*poll_init)(struct tty_driver *driver, int line, char *options); int (*poll_get_char)(struct tty_driver *driver, int line); void (*poll_put_char)(struct tty_driver *driver, int line, char ch); #endif const struct file_operations *proc_fops; };