一、字符设备完成注册
我们知道,在serial核心层提供了2个重要接口
uart_register_driver
uart_add_one_port
上者通过调用tty核心的接口,完成了tty_driver的动态分配和注册,然而此时并没有看到创建字符设备,
通过对uart_add_one_port的分析,最终调用了tty核心的
tty_register_device_attr接口
tty核心层的tty_cdev_add接口最终完成了字符设备的注册工作,
这里比较关注的是tty_fops
二、字符设备的操作集
上面我们看到,注册字符设备,使用了tty_fops作为操作集
看看tty_fops的定义,tty_fops定义在tty核心层。
tty设备归根结底是一个字符设备。
三、字符设备的打开
1、找到对应的tty_driver和tty_struct
直接看tty_fops的open。
首先获取设备号
按设备号匹配获取tty_driver(在全局数组tty_drivers中)
tty_open_current_tty是查找默认注册的/dev/tty和/dev/console,直接返回空
tty_lookup_driver依靠遍历tty_drivers数组,比较设备号最终找到tty_driver
tty_driver_lookup_tty通过index下标将会找到tty_struct
tty_struct是tty_driver维护的一个数组,在分配tty_drivers的时候分配的lines个tty_struct数组。
注意:这里分配的ttys和ports都是一个指针数组,保存的是指针,
cdevs是保存的字符设备。
注意:kcalloc会对申请空间清0
所以第一次打开获取到的tty应该是NULL,下面走else新建tty_struct并初始化
第二次打开获取到不为空,直接跳过
接着,,如果tty_struct有操作集ops且有open,执行,,如果没有,报错
那么这个操作集在哪里赋值的呢???
这个操作集是第一次打开,,新建tty_struct的时候用tty_driver的ops赋值的
tty_driver的ops是注册的时候用uart层定义的ops赋值的。。
至此tty层的open过程结束了,进入到serial层的open。。
2、新建tty_struct并初始化
tty层执行open第一次没有找到tty_struct,需要调用tty_init_dev创建并初始化tty_init_dev。
看看tty_init_dev做了什么,从字面意思上理解是初始化tty_struct,
应该是要先分配一个tty_struct,然后地址放到ttys对应位置,
那么就好理解了,之前获取tty_struct的时候,如果能获取到,代表这个串口已经被打开过了。
分配一个tty_struct
然后调用initialize_tty_struct对tty_struct初始化,包括使用tty_driver的一些资源对其初始化,
重要的几项
比如
tty->ops = driver->ops;
初始化线路规程,,注意,这里有了线路规程的概念了,
tty_ldisc_init(tty);
调用tty_ldisc_get将会新建一个tty_ldisc,并且使用N_TTY操作集,去初始化tty_struct的线路规程,,这里我们先记住。。
这里将新建的tty_struct地址保存在tty_driver的对应位置
这里是对termios做初始化,这里显然tty_driver里面是没有termios的,最终使用的
tty->termios = tty->driver->init_termios;
tty的port就用的tty_driver对应line的port,,
而tty_driver的port是在注册uart port的时候,赋值的
最后激活tty_struct的线路规程,,暂且这么理解吧(其实就是找到线路规程的open,执行一下)
这里o_tty即tty->link好像并没有初始化。
tty_ldisc_open就是找了tty_ldisc的ops的open,有就执行
前面我们看到
3、线路规程的激活
看看线路规程是怎样维护的,从新建tty_struct初始化线路规程开始看。
最终注意看,是从全局数组tty_ldiscs中用disc(N_TTY)为下标返回一个操作集,tty_ldiscs是一个操作集集合。
总共有这些类型的线路规程
4、线路规程的注册