本章主要介绍tty字符设备文件对应的操作接口,从而说明tty设备的数据打开、关闭、读、写等接口的实现等内容。
tyy file_operations定义
tty字符设备文件操作接口的定义如下,主要包括tty_fops、console_fops、hung_up_tty_fops,其中console_fops为控制台设备的文件操作接口,hung_up_tty_fops为tty设备挂起后的文件操作接口,而tty_fops则为非控制台设备的文件操作接口。
在之前几章的介绍中,我们已经说明过,在tty字符设备文件的打开操作后,完成tty_struct创‘建、tty_struct与tty_port、tty_struct与tty_ldisc等关联,下面我们简要分析下这几个函数。
tty_open接口
针对tty_open接口而言,主要实现如下几个功能:
- 若打开的tty设备为控制终端,则通过调用tty_open_current_tty湖区当前进程对应控制终端所对应的tty_struct指针;
- 若1中没有找到对应tty_struct,则根据字符设备号从tty_drivers链表中查找已注册的tty_driver,若该tty_driver与对应tty端口的tty_struct已完成绑定,则获取对应的tty_struct指针;
- 若以上两步均没有获取到tty端口对应的tty_struct,则说明该tty端口对应的tty_struct还没有创建,则调用tty_init_dev完成tty_struct的创建,并完成tty_struct与tty_driver的绑定、tty_struct与tty_port、tty_struct与ldisc、tty_struct与tty device的绑定操作, 并调用tty_ldisc_setup,进行线路规程的打开(如termios的设置,ldisc的使能、ldisc缓存的初始化等)等等
针对tty_open接口,主要涉及如下几个函数的调用:tty_open_current_tty、tty_driver_lookup_tty、tty_init_dev、initialize_tty_struct、tty_driver_install_tty等接口,而针对tty_init_dev接口而言,则主要实现tty_struct的创建以及各数据结构之间的关联等信息。
tty_init_dev
1. 创建一个tty_struct类型的变量,用于完成一个tty_struct、tty_driver的绑定等操作
2.调用initialize_tty_struct,初始化tty_struct类型变量,包括根据tty端口的id、tty端口对应的device
tty端口对应tty_driver、线路规程等设置该tty_struct对应的成员
3.调用tty_driver_install_tty,完成tty_driver与tty_struct的绑定,并初始化tty_struct的termios参数
4.调用tty_ldisc_setup,打开线路规程的open接口(如设置termios、关闭流控、使能ldisc)等功能
5.完成tty_struct与tty_port的关联
tty_read接口
当执行完成tty_open后,则完成了tty_driver、tty_port、tty device、tty_ldisc、tty_struct等数据结构的关联,则可以与tty端口进行读写操作了。下面说明下tty_read的操作流程。
如下图所示,针对应用程序读tty端口的操作,主要包括如下几个步骤:
- 应用程序read调用sys_read接口,由内核的vfs_read接口继续进行读操作;
- vfs_read则根据tty字符设备文件inode,调用tty_read进行读操作;
- 而tty_read则通过tty_struct获取其关联的tty_ldisc,调用tty_ldisc的read接口;
- 在tty_ldisc的read接口中,从tty->ldisc_data中获取已存储的数据,若存储的数据个数小于应用程序所需要读取的个数,则将该读进程加入到tty_struct->read_wait等待队列中,等待数据可读或者超时时间到期时,再次唤醒该读进程。
- 在tty_driver的驱动中,在其接收中断中,当接收到数据后,则将数据写入到tty_port的缓存中(调用tty_insert_flip_char执行写入操作),最后调用tty_flip_buffer_push接口,从而执行tty_port的buf对应的工作队列,从而调用其回调函数flush_to_ldisc,而在flush_to_ldisc中会调用tty_ldisc->ops->receive_buff接口,将数据写入到tty->ldisc_data的接收缓存中,并唤醒tty_struct->read_wait,唤醒该等待队列上所有阻塞的读进程。
tty_write接口
针对tty_write的操作流程,与tty_read类似,下面说明下tty_read的操作流程。
如下图所示,针对应用程序写tty端口的操作,主要包括如下几个步骤:
- 应用程序write调用sys_write接口,由内核的vfs_write接口继续进行读操作;
- vfs_write则根据tty字符设备文件inode,调用tty_write进行读操作;
- 而tty_write'则通过tty_struct获取其关联的tty_ldisc,调用tty_ldisc的write接口;
- 在tty_ldisc的write接口中,调用tty_driver->ops->write接口进行写操作,若当前需要写入的数据没有写完,则将该写进程加入到tty_struct->write_wait等待队列中,等待数据可写或者超时,再次唤醒该写进程。
- 在tty_driver的驱动中,在其发送中断中,若该tty端口可继续进行数据的写操作,则调用tty_wakeup,并唤醒tty_struct->write_wait,唤醒该等待队列上所有阻塞的写进程。
tty_poll接口
该接口主要用于select、epoll机制,主要也是借助tty_struct->read_wait、tty_struct->write_wait等待队列,完成tty_poll接口的实现,此处不再展开。
tty_close接口
- 若当前tty_struct上仅有一个文件描述符,则该tty_struct变量也可以释放掉,因此需要等待该文件描述符上所有已等待的读写队列成员,并设置tty_struct是可释放的;
- 调用tty_del_file,解除该文件描述符与tty_struct的关联;
该接口主要调用release_tty、tty_ldisc_release、tty_ldisc_kill、tty_del_file等接口,用于解除tty_struct、tty_driver、tty_ldisc、tty_port、tty device之间的关联。此处我们对release_tty进行简要说明
release_tty接口
其实现的功能如下:
- 调用tty_driver->ops->shutdown,执行tty端口的关闭操作(如关闭收发中断、停止接收与发送等功能);
- 调用tty_driver_remove_tty,去除tty_struct与tty_driver的关联
- 调用tty_kref_put,tty_struct的引用计数减一,当tty_struct的引用计数为0时,则调用queue_release_one_tty,释放该tty_struct的空间(包括调用tty_struct->cleanup释放tty端口的资源,并释放该tty_struct所占用的空间)
本章主要介绍tty字符设备接口的文件操作接口,其open接口主要完成tty_struct创建、tty_driver、tty_port、tty_ldisc、tty device的关联操作;而读写接口则主要借助等待队列、工作队列完成对tty端口的读写操作。下一章则根据tty__register_driver、tty_port_register_device创建一个虚拟的tty串口驱动,以便我们不借助开发板也可以进行tty子系统驱动的学习工作。