linux串口设备操作(串口子系统框架的总体说明)
linux串口设备操作(串口子系统框架的总体说明)该数据结构是对uart端口的抽象 Uart_driver的定义如下,其中dev_name即为串口对应字符设备文件名的前缀,而major、minor则为串口对应主设备号以及次设备号起始位置(若不设置主设备号,则动态申请,一般不设置这两个变量);state为该串口控制器下串口的资源信息(如存储待发送数据的环形缓冲区等),tty_driver则为该串口对应的tty_driver类型的成员变量,该变量由串口子系统实现,因此无需驱动开发者初始化该变量(这也是uart子系统抽象后的一个好处,开发串口驱动时不再需要关注tty_driver的实现) 该数据结构可以理解为串口的资源信息,如申请了一个环形缓冲区成员,用于存储待发送的数据;而port则为tty端口变量;uart_port则指向uart子系统定义的串口抽象数据结构。
在前面几个章节,我们学习了tty子系统,并借助tty子系统完成了一个虚拟串口控制器驱动,并增加了两个虚拟串口。而在串口子系统中,为了让串口的开发更加简单,串口子系统又进行了一层框架搭建,构建了串口子系统,本章我们就介绍串口子系统框架。
老规矩,在了解一个子系统之前,我们先熟悉其数据结构及数据结构间的关联,这可有助于我们快速理解该子系统的大致结构,所要实现的功能有哪些等等。。。
相关数据结构说明针对uart子系统,其又抽象了uart_driver、uart_port、uart_state、uart_ops几个数据结构,其中uart_driver也是表示一个串口控制器驱动的抽象,内部包含了tty_driver类型的成员变量,同时也包含了该串口控制器所支持的所有串口对应uart_state(可以理解为一个uart port相关的资源的数据结构);而uart_port是针对一个串口的抽象,其内部包含一个tty_port类型的成员变量,而uart_state可以理解该串口的资源信息,其完成了与uart_driver、uart_port、tty_struct的关联,其功能也类似于tty_struct,而uart_ops则定义了一个串口支持的操作接口。下面我们分别说明每一个数据结构的内容。
struct uart_driver
Uart_driver的定义如下,其中dev_name即为串口对应字符设备文件名的前缀,而major、minor则为串口对应主设备号以及次设备号起始位置(若不设置主设备号,则动态申请,一般不设置这两个变量);state为该串口控制器下串口的资源信息(如存储待发送数据的环形缓冲区等),tty_driver则为该串口对应的tty_driver类型的成员变量,该变量由串口子系统实现,因此无需驱动开发者初始化该变量(这也是uart子系统抽象后的一个好处,开发串口驱动时不再需要关注tty_driver的实现)
struct uart_state
该数据结构可以理解为串口的资源信息,如申请了一个环形缓冲区成员,用于存储待发送的数据;而port则为tty端口变量;uart_port则指向uart子系统定义的串口抽象数据结构。
struct uart_port
该数据结构是对uart端口的抽象
1.包含读写该端口寄存器相关的i/o及内存地址;
2.包含读写串口、设置串口termios的接口serial_in、seriall_out、set_termios
3.包含中断处理接口handle_irq(针对该接口,虽然在uart_port中定义但串口子系统的代码并没有对其做进一步使用,估计当时想把每一个串口中断处理也纳入uart_port中)
4.包含该串口端口的中断号、中断标签
5.state指向该uart_port对应的资源
6.ops指向该uart端口的操作接口(针对uart端口则需要实现ops中各函数指针)
struct uart_ops
该数据结构表示一个串口的操作接口,主要包含如下几个方向:
- tx_empty表示发送队列是否为空,也即该串口的存储发送数据的环形缓冲区是否为空;
- stop_tx表示停止发送操作(可实现为关闭tx中断);
- start_tx表示启动一次发送操作(如调用tty_driver->write执行发送操作时,完成发送数据拷贝后,则调用该start_tx,启动一次数据发送操作,该接口一般是使能发送中断);
- stop_rx表示停止接收操作(一般是去使能接收中断),在uart_close接口中,会调用stop_rx、stop_tx停止收发操作;
- throttle、unthrottle主要用于流控;
- enable_ms主要等待操作完成;
- startup主要用于启动串口的功能(如注册中断等)
- set_termios用于设置该串口的termios信息,包括传输字节宽度、波特率等等,
- 而request_port申请该串口相关的io和内存资源以及端口的配置,针对端口的配置接口,config_port可以配置uart_port的type、flag等信息。目前针对io和内存的资源申请与释放,目前驱动并不是严格按照这两个接口定义,有一部分是在串口对应的probe接口中实现io和内存资源的申请并在remove接口中释放,这也就是设计人员提供了丰富而又全面的接口,但使用者却没有一一使用。
数据结构间的关联
下面是这几个数据结构与tty_port、tty_driver的关联,针对uart子系统只要我们掌握了如下关联图,基本上针对uart子系统的概念就比较熟悉了。
- uart_driver主要是对tty_driver的包含(就像设备驱动模型中struct device_driver与struct xxx_device_driver的关系,uart_driver与tty_driver可简单理解为父类与子类之间的关系);
- uart_state可以理解为uart_port的资源信息,此处我有点不解,个人感觉可以将uart_state和uart_port集合在一起,没必要整两个数据结构;
- uart_port是对一个串口的抽象,其主要包含了一个串口的操作接口(struct uart_ops类型的成员变量,而使用uart子系统提供的接口完成串口驱动的开发时,一定要实现struct uart_ops,这是实现串口数据收发的关键);
- 串口子系统中已经定义struct tty_operation中各函数指针对应的函数,如uart_open、uart_write、uart_close等等,且uart_register_driver函数完成了tty_driver的申请及初始化,并注册至tty子系统中,因此这大大节省了驱动开发者的时间,只需调用uart_register_driver即可完成tty_driver的注册。
- 在调用uart_add_one_port时,即完成uart_state与uart_port的相互关联。
- 而在uart_open时,则完成tty_struct与uart_state的关联(tty_struct->driver_data=uart_state)
其实,针对uart子系统架构的实现,在我们上一章借助tty_register_driver创建虚拟串口后,对它的实现也可以快速理解,如我们在上一节中创建了数据结构virtual_tty_port,代表我们的虚拟串口,在该数据结构中定义了一个fifo用于存储待发送的数据,包含了tty_port。而在uart子系统中定义了uart_state、uart_port(uart子系统抽象了两个数据结构)
而uart子系统针对串口的特性,抽象出来串口统一的操作接口(uart_open、uart_write、uart_close等等),并完成了统一的tty_driver、tty_port的申请、初始化、注册,而驱动开发者只需要实现uart_port->ops中各函数指针的设置即可,相对来说进一步简化了串口的开发流程。
以上即是本章的主要内容,下一章我们说明串口子系统的注册、注销以及读写操作流程,最后一章再借助uart子系统实现一个虚拟串口驱动。