RK3399平台开发系列讲解(TTY子系统)4.48、TTY子系统详解

975 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

在这里插入图片描述

平台内核版本安卓版本
RK3399Linux4.4Android7.1

🚀返回专栏总目录

@[toc]

沉淀、分享、成长,让自己和他人都能有所收获!😄

📢TTY子系统详解

一、TTY相关概念


1.1、设备节点名称

/dev/ttyS0/dev/ttySAC0/dev/tty/dev/tty0/dev/tty1/dev/console

设备节点含义
/dev/ttyS0/dev/ttySAC0串口
/dev/tty1、/dev/tty2、/dev/tty3、……虚拟终端设备节点
/dev/tty0前台终端
/dev/tty程序自己的终端,可能是串口、也可能是虚拟终端
/dev/console控制台,由内核的cmdline参数确定

1.2、TTY/Terminal/Console/UART

术语含义
TTY来自teletype,最古老的输入输出设备,现在用来表示内核的一套驱动系统
Terminal终端,暗含远端之意,也是一个输入输出设备,可能是真实设备,也可能是虚拟设备
Console控制台,含控制之意,也是一种Terminal,权限更大,可以查看内核打印信息
UART串口,它的驱动程序包含在TTY驱动体系之内

1.3、/dev/console

可以通过内核的cmdline来指定, 比如: console=ttyS0 console=tty

console=ttyS0时:/dev/console就是ttyS0 console=tty时:/dev/console就是前台程序的虚拟终端 console=tty0时:/dev/console就是前台程序的虚拟终端 console=ttyN时:/dev/console就是/dev/ttyN console有多个取值时,使用最后一个取值来判断

二、TTY驱动


2.1、数据结构

  • 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端口设备的功能。

2.2、实现

tty driver的注册函数是tty_register_driver:

int tty_register_driver(struct tty_driver *driver)

该函数主要完成如下工作:

  • 根据tty_driver设置的主设备号、次设备号、tty端口个数,调用alloc_chrdev_region/register_chrdev_region进行字符设备号区间的申请。
  • 若该tty_driver动态创建字符设备,则在tty_register_driver中,仅创建次设备号范围为tty端口个数的字符设备。
  • 若该tty_driver静态创建字符设备,则在tty_register_driver中,针对每一个tty端口均创建一个对应的字符设备(次设备个数为1)。
  • 将该tty_driver添加至tty_drivers链表中。
  • 调用proc_tty_register_driver,在proc文件系统中,为该tty_driver创建对应的文件。

tty device的注册函数是tty_register_device:

struct device *tty_register_device(struct tty_driver *driver, unsigned index,
				   struct device *device)
  • 若该tty_driver没有设置TTY_DRIVER_DYNAMIC_ALLOC,则调用tty_cdev_add针对每一个tty端口均创建一个对应的字符设备(次设备个数为1)。
  • 申请struct device类型的内存空间,并将该devicetty_class关联,并调用device_register将该device注册至设备驱动子系统中,且该device中包含了字符设备的设备号,因此当将该设备注册至设备驱动子系统时,会通过netlink将设备注册的uevent信息发送给应用程序,而mdev或者udevd程序接收到该uevent信息后,则会根据注册信息,完成tty字符设备inode的创建,即可在/dev目录下创建相应的字符设备文件。

行规程的注册函数tty_register_ldisc

int tty_register_ldisc(struct tty_ldisc_ops *new_ldisc)

行规程的注册很简单就是将新的行规程结构体加入到一个全局数组中,如下:

static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS];

2.3、tty子系统中接口间的关联

tty子系统在系统中的位置如下:

  • vfs的系统调用接口,通过字符设备操作接口,最终调用tty字符设备文件节点的操作函数的调用(即tty_opentty_readtty_writetty_poll等接口)。
  • tty_opentty_readtty_writetty_poll等接口,则借助tty_ldisc数据结构类型的变量,调用tty_ldisc_ops中对应的接口。
  • tty_ldisc_ops中的接口,则借助tty_driver数据结构类型的变量,调用tty_operations数据结构中各成员变量对应的函数指针。
  • tty_operations中的函数指针中,则针对一部分设备驱动,则会调用tty_port中的ops的接口。 最后:TTY子系统是一个很复杂的子系统,这里也没有详细的分析里面具体的实现,而是主要将里面的数据结构列出来,在整体上对TTY子系统有一个认识。