05、TTY子系统详解
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_port
- tty_struct
- tty_driver
2.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
2.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
2.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
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
通用的串口设备通过 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
每个 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
每个 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