TTY 是 Teletypewriter 的缩写(Teleprinter、TeletypewriterTele-Type,缩写为 TTY),中文电传打字机简称电传,是远距离打印交换的编写形式;
在 linux 中 tty 表示各种终端,通常是和硬件相对应,tty 用来指任何的串口设备,如:输入设备键盘鼠标、输出设备显示器、虚拟的 pty 等;
一、TTY 简介
tty 驱动、uart 驱动存在密切联系,tty 设备包括 uart,uart 设备的工作依赖于 tty 设备,uart 是 tty 的上层,内核中有完整的 tty 驱动,uart 设备可以使用 tty 驱动进行封装;而内核中也有完整的 uart 驱动,两者相互独立又密切相关;本文主要讲解 tty 驱动相关的内容,以控制台驱动为例讲述 tty 驱动的应用,关于 uart 驱动方面内容,请参考下一文 uart 驱动;
tty 驱动程序的核心在标准字符设备驱动层之下;
linux 内核中的 tty 层次结构,包含:tty 核心、tty 线路规程、tty 驱动;
tty 核心(tty_core):整个 tty 设备的抽象,对用户提供统一的接口,用户空间通过设备文件与 tty_core 交互;tty_core 根据用户空间操作类型,选择将数据交给 line discipline 和 tty_driver;
tty 线路规程(line discipline):对传输数据的格式化,把从用户或硬件接收的数据格式化,这种格式化使用协议完成转换,如:蓝牙;处理之后,将数据交给 tty_driver;
tty 驱动(tty_driver):tty 设备对应的驱动,将字符转换成硬件可以理解的字符,将其传给硬件设备;并从硬件接收数据;
由于终端种类繁多,为了使其具有统一的上层接口函数, Linux 系统对终端设备进行了总结抽象,总结其共性,抽象出共用的上层接口函数,采用分层的设计思想,将 tty 设备分成三层: tty 核心层, tty 线路规程以及 tty 驱动层。

从图中可以看出用户程序通过 open、 read 等函数调用核心层,核心层可通过线路规程与 tty 驱动通信。如果数据不需要处理加工,核心层也可直接与 tty 驱动层进行数据的传输。 tty 驱动程序将数据格式化为硬件理解的形式,然后才将数据发送到硬件设备。在用户程序进行终端设备操作时,要调用核心层操作函数 file_operations,实现了所以终端设备的 API 函数,是核心层的主要数据结构。
在链路层主要通过 tty_regiser_driver() 函数注册线路规程,是链路层的核心函数。终端设备的主要工作是实现 tty_driver 结构体成员的填充,实现其成员函数。
tty 核心和线路规程由内核提供,驱动工程师只需要完成 tty 驱动部分代码就可以使用 tty 了;
多数时候 tty 核心和 tty 驱动之间的数据传输会经历 tty 线路规程的转换,但是 tty 驱动和 tty 核心之间也可以直接传输数据;tty 核心根据操作类型选择和线路规程和 tty 驱动交互;如:设置硬件的 ioctl 直接交给 tty_driver 处理;而 read/write 操作交给线路规程处理;
发送数据流程:tty 核心从用户空间获取到将要发送给 tty 设备的数据,tty 核心将数据传递给 tty 线路规程驱动,然后数据被传递到 tty 驱动,tty 驱动将数据转换为可以发送给硬件的格式;
接收数据流程:tty 硬件将接收到的数据上交给 tty 驱动,进入 tty 线路规程驱动,再进入 tty 核心,被传递给用户空间;
tty 驱动程序有三种类型:控制台、串口和 pty;控制台和 pty 驱动程序已经在内核中稳定使用,使用 tty 核心与用户和系统交互的新驱动都可以看做是串口驱动程序;
tty 驱动代码位于
drivers/tty目录;tty 核心是所有 tty 类型驱动的顶层架构,向应用层提供了统一的接口;用户态的
open、close、read、write系统调用首先到达 tty 核心;tty 核心在
tty_io.c文件中由内核实现,定义了 tty 设备需要的核心数据结构,和一些需要操作;
二、结构体
tty_driver该数据结构即为tty控制器的抽象,该数据结构包含该tty控制器的访问接口(包括读、写等接口)。tty_port针对每一个tty端口设备(也可称为tty端口),抽象为tty_port数据结构,该结构体包含了存储从tty端口中接收的数据以及每一个数据对应的flag信息,并包含对应的接口(包括数据载波监听是否启动判断、tty端口激活及关闭、dtr/rts启动、tty port释放等接口)。tty_ldisc借助一个tty端口传输不同格式的协议数据,即可实现不同的协议传输,即针对一个tty端口可实现多个不同的协议,因此tty驱动模块针对协议数据传输抽象出行规程,现内核中实现了tty行规程、irda行规程、SLIP行规程、PPP行规程等。tty_struct而针对linux tty子系统,为了将tty子模块与上层文件系统关联,实现tty设备文件,又抽象了tty_struct数据结构,该数据结构包含一个tty端口、tty端口对应的行规程、该tty端口所依附的tty控制器以及打开该tty端口的文件变量,即通过该数据结构,即可完成应用程序读写tty端口设备的功能。
serial8250_init 使用的数据结构很多,按照自顶向下的顺序介绍,即从更一般的 TTY 层到更具体的 8250 层。
介绍这些数据结构时,以 serial8250_init 执行时设置的各个成员变量的值为例进行说明。
tty_porttty_structtty_driver
1、tty_port
每个设备保存自己的 port 信息,用 struct tty_port 实现。
struct tty_port {
struct tty_bufhead buf; /* Locked internally */
struct tty_struct *tty; /* Back pointer */
struct tty_struct *itty; /* internal back ptr */
// static const struct tty_port_operations uart_port_ops ;
const struct tty_port_operations *ops; /* Port operations */
// static const struct tty_port_client_operations default_client_ops;
const struct tty_port_client_operations *client_ops; /* Port client operations */
spinlock_t lock; /* Lock protecting tty field */
int blocked_open; /* Waiting to open */
int count; /* Usage count */
// 打开 tty 设备文件时将进程添加到该等待队列
wait_queue_head_t open_wait; /* Open waiters */
wait_queue_head_t delta_msr_wait; /* Modem status change */
unsigned long flags; /* User TTY flags ASYNC_ */
unsigned long iflags; /* Internal flags TTY_PORT_ */
unsigned char console:1, /* port is a console */
low_latency:1; /* optional: tune for latency */
struct mutex mutex; /* Locking */
struct mutex buf_mutex; /* Buffer alloc lock */
unsigned char *xmit_buf; /* Optional buffer */
unsigned int close_delay; /* Close port delay */
unsigned int closing_wait; /* Delay for output */
int drain_delay; /* Set to zero if no pure time
based drain is needed else
set to size of fifo */
struct kref kref; /* Ref counter */
void *client_data;
};2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
2、tty_struct
和 TTY 相关的所有状态在 TTY 打开期间会一直有效; termios 在 tty 关闭后也保留 —— 比如波特率。这些信息都保存在 tty_struct 。
struct tty_struct {
int magic;
struct kref kref;
struct device *dev;
// 对应的 tty_driver
struct tty_driver *driver;
// static const struct tty_operations uart_ops;
const struct tty_operations *ops;
int index;
/* Protects ldisc changes: Lock tty not pty */
struct ld_semaphore ldisc_sem;
struct tty_ldisc *ldisc;
struct mutex atomic_write_lock;
struct mutex legacy_mutex;
struct mutex throttle_mutex;
struct rw_semaphore termios_rwsem;
struct mutex winsize_mutex;
spinlock_t ctrl_lock;
spinlock_t flow_lock;
/* Termios values are protected by the termios rwsem */
struct ktermios termios, termios_locked;
struct termiox *termiox; /* May be NULL for unsupported */
char name[64];
struct pid *pgrp; /* Protected by ctrl lock */
struct pid *session;
unsigned long flags;
int count;
struct winsize winsize; /* winsize_mutex */
unsigned long stopped:1, /* flow_lock */
flow_stopped:1,
unused:BITS_PER_LONG - 2;
int hw_stopped;
unsigned long ctrl_status:8, /* ctrl_lock */
packet:1,
unused_ctrl:BITS_PER_LONG - 9;
unsigned int receive_room; /* Bytes free for queue */
int flow_change;
// 与当前 tty 相连的 tty_struct ,比如 pty 设备
struct tty_struct *link;
struct fasync_struct *fasync;
// 对端口执行写操作的等待队列
wait_queue_head_t write_wait;
// 对端口执行读操作的等待队列
wait_queue_head_t read_wait;
struct work_struct hangup_work;
// tty 设备使用的 line discipline 数据
void *disc_data;
void *driver_data;
spinlock_t files_lock; /* protects tty_files list */
struct list_head tty_files;
#define N_TTY_BUF_SIZE 4096
int closing;
unsigned char *write_buf;
int write_cnt;
/* If the tty has a pending do_SAK, queue it here - akpm */
struct work_struct SAK_work;
struct tty_port *port;
} __randomize_layout;2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
3、tty_driver
TTY 驱动由 tty_driver 描述。
struct tty_driver {
int magic; /* magic number for this structure */
struct kref kref; /* Reference management */
// 驱动绑定到的字符设备数组
struct cdev **cdevs;
struct module *owner;
const char *driver_name;
const char *name;
int name_base; /* offset of printed name */
int major; /* major device number */
int minor_start; /* start of minor device number */
// 分配的设备文件的数量
unsigned int num; /* number of devices allocated */
short type; /* type of tty driver */
short subtype; /* subtype of tty driver */
struct ktermios init_termios; /* Initial termios */
unsigned long flags; /* tty driver flags */
struct proc_dir_entry *proc_entry; /* /proc fs entry */
// pty 设备有两个 tty driver
struct tty_driver *other; /* only used for the PTY driver */
/*
* Pointer to the tty data structures
*/
// ttys , ports , termios 是每个字符设备的数据
struct tty_struct **ttys;
struct tty_port **ports;
struct ktermios **termios;
void *driver_state;
/*
* Driver methods
*/
// static const struct tty_operations uart_ops;
const struct tty_operations *ops;
// 用于插入到 tty_drivers 表
struct list_head tty_drivers;
} __randomize_layout;2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
8250 串口设备有专用的数据结构 uart_8250_port 。
struct uart_8250_port {
// 对应的 uart_port
struct uart_port port;
// 没有 irq 功能的设备通过计时器收发数据
struct timer_list timer; /* "no irq" timer */
struct list_head list; /* ports on this IRQ */
u32 capabilities; /* port capabilities */
unsigned short bugs; /* port bugs */
bool fifo_bug; /* min RX trigger if enabled */
unsigned int tx_loadsz; /* transmit fifo load size */
unsigned char acr;
unsigned char fcr;
unsigned char ier;
unsigned char lcr;
unsigned char mcr;
unsigned char mcr_mask; /* mask of user bits */
unsigned char mcr_force; /* mask of forced bits */
unsigned char cur_iotype; /* Running I/O type */
unsigned int rpm_tx_active;
unsigned char canary; /* non-zero during system sleep
* if no_console_suspend
*/
unsigned char probe;
#define UART_PROBE_RSA (1 << 0)
/*
* Some bits in registers are cleared on a read, so they must
* be saved whenever the register is read but the bits will not
* be immediately processed.
*/
#define LSR_SAVE_FLAGS UART_LSR_BRK_ERROR_BITS
unsigned char lsr_saved_flags;
#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA
unsigned char msr_saved_flags;
// dma 模式下的 dma 信息
struct uart_8250_dma *dma;
//static const struct uart_8250_ops univ8250_driver_ops;
const struct uart_8250_ops *ops;
/* 8250 specific callbacks */
int (*dl_read)(struct uart_8250_port *);
void (*dl_write)(struct uart_8250_port *, int);
struct uart_8250_em485 *em485;
};2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
通用的串口设备通过 uart_port 表示。
struct uart_port {
spinlock_t lock; /* port lock */
unsigned long iobase; /* in/out[bwl] */
unsigned char __iomem *membase; /* read/write[bwl] */
unsigned int irq; /* irq number */
unsigned long irqflags; /* irq flags */
unsigned int uartclk; /* base uart clock */
unsigned int fifosize; /* tx fifo size */
unsigned char x_char; /* xon/xoff char */
unsigned char regshift; /* reg offset shift */
unsigned char iotype; /* io access style */
struct uart_state *state; /* pointer to parent state */
// uart 设备收到数据的统计信息
struct uart_icount icount; /* statistics */
// uart 设备绑定到的 console
struct console *cons; /* struct console, if any */
/* flags must be updated while holding port mutex */
upf_t flags;
unsigned int type; /* port type */
// static const struct uart_ops serial8250_pops;
const struct uart_ops *ops;
unsigned int custom_divisor;
unsigned int line; /* port index */
unsigned int minor;
resource_size_t mapbase; /* for ioremap */
resource_size_t mapsize;
struct device *dev; /* parent device */
const char *name; /* port name */
void *private_data; /* generic platform data pointer */
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
每个 uart 设备都会绑定到一个 uart_driver 。
struct uart_driver {
struct module *owner;
const char *driver_name;
const char *dev_name;
int major;
int minor;
int nr;
struct console *cons;
/*
* these are private; the low level driver should not
* touch these; they should be initialised to NULL
*/
struct uart_state *state;
struct tty_driver *tty_driver;
};2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
每个 uart 设备打开后,会有状态信息与之绑定,在设备打开的阶段一直有效,通过 uart_state 描述。
struct uart_state {
struct tty_port port;
enum uart_pm_state pm_state;
// uart 设备的发送缓冲区
struct circ_buf xmit;
atomic_t refcount;
wait_queue_head_t remove_wait;
struct uart_port *uart_port;
};2
3
4
5
6
7
8
9
10
11
12
13