一、TTY设备在*nix中,tty设备用来抽象串口类型的设备,它位于字符驱动之下,抽象了串口设备需要的特性、功能,抽象后的一个tty设备即可表示一个串行输入、输出接口(比如控制台口,串口、pty设备接口)。TTY的实现由两部分组成:
1.1 初始化tty的初始化包括几部分:
1.1.1 初始化控制台口在start_kernel会很快的调用tty中的console_init函数,它会完成:
一个使用console_initcall声明的函数至少需要条用register_console将其注册为控制台口,这样内核的输出才能被送到该设备。 1.1.2 创建tty_class在系统启动中,还会调用tty_class_init来创建一个tty class,它是tty设备的class。1.1.3 初始化tty设备在系统启动阶段完成的最后一件属于tty的初始化动作就是调用tty_init完成tty的初始化。该函数会完成/dev/tty/和/dev/console的初始化。对这两个设备,都分别会顺序完成:
1.2 与上层以及下层的接口tty设备是串口类设备的抽象,它以统一一致的方式来处理流向某个tty设备的数据以及来自某个tty设备的数据,并向用户空间提供了统一一致的用户接口,向底层即真实的设备驱动提供了统一一致的编程接口。1.2.1 与上层(实际上就是tty设备的使用者)的接口tty设备是字符设备,类似于其它的设备,在*niux中,它也被看做一个文件,因而也就有其相应的文件操作,它向其用户提供的接口就是通过文件操作提供的。tty核心提供了两个文件操作集:static const struct file_operations tty_fops和static const struct file_operations console_fops以及一个假的文件操作集static const struct file_operations hung_up_tty_fops,tty设备的文件操作指向其中一个。如果一个用户想要使用某个tty设备, 只要该tty对应的设备之后,就可以使用其中的文件操作来读写tty设备了。tty核心会把相应的操作转换为对设备的读写。 在文件操作中,打开文件操作是比较关键的,由于在打开后即可对打开的文件句柄进行操作,因而打开操作就至少需要找到对应的设备,找到对应的文件操作指针,在文件打开后,就可以直接对文件句柄进行操作了,因而我们简单分析下tty open的操作:
从上述打开过程可以看到,打开操作将tty和真实的物理设备已经需要使用的line discipline都关联了起来,因而之后对文件句柄进行操作的时候就可以找到对应的line discipline和设备驱动程序。 可以看到在打开一个tty设备后,tty的line discipline固定的就是tty_ldisc_N_TTY,如果想改变这一点,需要使用ioctl命令TIOCSETD来实现。实际上由于tty也被当做一个文件来操作,因而其对上层提供的接口也和普通文件相同,但是我们知道串口有很多参数设置,这都是通过ioctl实现的。 1.2.2 与下层(即真实的设备驱动之间)的接口Tty设备是对实际串口设备的抽象,实际的操作仍然要由相应的硬件来完成,为了达到这个目的,tty核心向下层提供了一些注册接口让真实设备驱动程序可以将其注册到tty上,以供tty使用。通过EXPORT_SYMBOL关键字即可找到tty对外提供的所有接口。我们这里只关注最关键的:1.2.2.1 tty_register_driver该函数式tty向下层提供的最主要的接口,它主要完成如下工作:
1.2.2.2 tty_register_device和tty_register_device_attr这两个函数向系统中注册一个新的tty设备,前一个是后一个的包装器函数。对于这两个函数有一个要求,该设备的驱动程序需要设置TTY_DRIVER_DYNAMIC_DEV标记,如果驱动程序没有设置该标记,则不应对该设备调用这两个函数。因为对于没有设置这个标记的驱动程序,当调用tty_register_driver时,该函数会调用tty_register_device完成tty设备的注册。 1.2.2.3 tty_insert_flip_char和tty_insert_flip_string这两个函数用于驱动将接受到的数据放入tty缓存。 1.2.2.4 tty_insert_flip_string_flags和tty_insert_flip_string_fixed_flag用于将标记放入tty缓存。
1.2.2.5 tty_prepare_flip_string和tty_prepare_flip_string_flags为接收的字符申请一片内存区域,它们会返回所申请的大小以及指向可用缓存起始位置的指针。它们用于驱动程序想自己将数据拷贝到tty缓存的场合,一般使用tty_insert_flip_char,tty_insert_flip_string。
1.2.2.6 tty_flip_buffer_push和tty_schedule_flip将数据从tty buffer转移到tty line discipline中。二者不同之处在于如果设置了low_latency则后者 不工作,而前者直接调用flush_to_ldisc来刷出数据到line discipline。如果没有设置low_latency,则二者都是将刷出数据到line discipline的工作添加到工作队列system_wq中,随后由工作者线程调用flush_to_ldisc。 需要注意的是由于flush_to_ldisc不能在中端上下文被调用,因此如果设置了low_latency,则不应该在中断上下文调用tty_flip_buffer_push。 1.2.2.7 tty_flush_to_ldisc它在没有设置low_latency时,会调用flush_work将数据从tty buffer刷到line discipline,flush_work会等待任务完成,因为它会阻塞,因而它不能在中端上下文被调用。该函数一般由读任务来调用,典型的在tty_ldisc_N_TTY的读以及poll中会调用到它。 1.2.2.8 tty_buffer_init为tty port初始化缓存数据结构 1.2.2.9 tty_buffer_request_room为tty申请缓存 1.3 文件读/写1.3.1 文件读Tty的读操作如下
Tty提供的缓存为每个输入的字符缓存了字符本身以及其对应的标记信息。 1.3.2 文件写Tty的写逻辑很简单:
二、Tty line discipline从tty核心的描述中也可以看出,tty discipline在文件读写中起了很大的作用,读写操作都需要经过它,它主要用来进行输入/输出数据的预处理,写入设备的数据要先经过它的处理才会被发送给真实的设备驱动,从设备接收的数据也会先经过它的处理才会进入到tty core的处理逻辑。它的实现本身非常简单,主要提供的几个对外的接口是:
|
|
来自: 唯时馆 > 《Android,Linux》