在上一章我们早已说明了uart驱动的开发流程,本章我们就不再介绍uart相关的插口实现,仅通过实现一个虚拟的并口控制器程序,用以说明虚拟并口的开发流程。
本次开发的虚拟并口提供的功能如下:
提供两个并口实例并口名称的前缀为vttyU为了验证并口收发,提供了loopback机制,即应用程序向虚拟并口写入数据后,数据再回环至应用程序;在/sys目录下提供数据写入属性文件,可向虚拟并口中写入数据,用以模拟并口接收数据的功能
本次开发的代码涉及的模块包括:
创建两个platformdevice,分别对应两个虚拟并口的platformdevice;创建一个platformdriverlinux版qq,在platformdriver的probe插口中,完成虚拟并口的注册,主要是完成uart_add_one_port插口完成虚拟并口的注册;在platformdriver的remove插口中,完成虚拟并口的注销;在并口驱动的初始化函数中,调用uart_register_driver,uartdriver的注册。
数据结构说明
在虚拟并口驱动中,定义了数据结构virtual_uart_port,该数据结构中包含了uart_port。并定义了tx_enable_flag、rx_enable_flag,分别用于控制并口收发,因是虚拟并口所以使用这两个变量进行表示,若是真是的并口控制器,则不须要这两个变量,而只须要在uart_ops->startup、uart_ops->stop_rx、uart_ops->stop_tx中关掉中断即可,这两个变量由载流子锁write_lock进行保护。
uart_driver定义及注册
定义virtual_uart_driver,本并口控制器驱动支持的并口个数为MAX_VIRTUAL_UART(6个)、而dev_name则为该虚拟并口对应字符设备文件名称的前缀,本次定义前缀为“vttyU”,本并口不支持控制台。调用uart_register_driver即完成本并口控制器驱动的注册与注销
Platformdevice定义及注册
本次我们主要完成了并口的注册,因而我们定义并注册了两个platformdevice,而传递的参数为并口的index的。插口定义如下所示,platformdevice的name为“virtual_uart_port_dev”,按照该名称可完成与platformdriver的匹配及侦测功能(因我们在ubuntu16.04下完成的验证linux操作系统教程,没有设备树概念,因而就定义了这两个platformdevice,若支持设备树,则无须我们自动定义这两个platformdevice,只须要更改设备树文件即可)。
Platformdriver的定义及注册
我们的platformdriver的定义如下,支持probe、remove插口,probe插口主要完成uartport的注册、remove插口主要完成uartport注销,该platformdriver的name为“virtual_uart_port_dev”,通过该名称可进行platformdevice与platformdriver的匹配监测,同时我们也定义了of_device_id,若内核支持设备树,则在设备树中的compatible中也设置“jerry_chg,virtual-uart”,即可完成platformdevice与platformdriver的匹配监测。
virtual_uart_port_platform_probe插口的定义如下,主要实现的功能如下:
为uart_port申请显存,并设置uart_port的ops、fifosize、type、line等值,另外还可为该uart_port创建uart_port相关的私有属性文件(sysfs下),而在linux3.10的内核中uart_port中并没有定义attr_group变量,致使uart_add_one_port插口只能在tty_port对应device中创建uart核心定义的属性文件,而不能创建uartport私有的属性文件,而在旁边的内核中特意降低了attr_grouplinux 定时器程序,用于创建uartport私有的属性文件,这个成员变量降低的很好,本次虚拟并口就利用该变量创建了属性文件uart_receive_buff,用于模拟并口接收数据。初始化一个工作队列及其反弹函数virtual_uart_flush_to_port,该插口主要是模拟并口发送中断的功能,在真实的并口控制器中,则是申请并口中断,在并口中断的处理函数中进行数据的发送,而我们这个工作队列则是模拟并口中断函数的功能;调用uart_add_one_port,完成并口的添加。
Uartport操作插口定义
我们为虚拟并口定义的操作插口如下
其中tx_empty插口用于测试发送缓存是否为空(uart_port的环型缓冲区);stop_tx用于停止发送操作(在真实并口控制器驱动中linux 定时器程序,则关掉发送中断即可);start_tx用于启动发送操作(在真实并口控制器驱动中,则开启发送中断,之后则触发发送中断,从而在发送中断处理插口中执行数据的发送操作,而在我们的虚拟并口中,则调用schedule_work,启动工作队列,调用工作队列的反弹函数进行数据发送);throttle、unthrottle则是流控操作,在真实并口中则设置相应定时器即可(本驱动未实现该功能);stop_rx用于停止接收操作(在真实并口控制器驱动中,则关掉接收中断即可)startup插口主要是启动并口功能(在真实并口控制器驱动中,则申请中断,并使能接收中断;而发送中断的使能在start_tx中实现,而在我们虚拟并口驱动中,则是使能收发数据的flag);set_termios则主要是设置termios相关参数(包括字节长度、波特率等参数的设置);
而set_mctrl插口则一定要定义,虽然是一个空函数也要定义。
模拟并口接收功能实现
由于我们是虚拟并口,而且又要模拟并口接收功能,为此我们在ttyport对应的device中,定义了模拟并口接收数据的属性文件,定义如下:
当用户向该属性文件(uart_receive_buff)中写数据时,则在该属性文件的store插口中,将写入的数据发送到tty_port的接收缓存中,并通过调用tty_flip_buffer_push插口,将tty_port接收缓存中的数据通过线路规程的receive_buff插口将数据刷新到tty_struct的接收缓存中,并wakeup读等待队列中sleep的读线程(这一系统的操作流程可参考我之前写的几篇文档),正式数据发送的该并口上的读线程中。
我们可以通过如下脚本向uart_receive_buff写数据,因而模拟并口接收数据
测试验证在应用程序中打开/dev/vttyU1,进行数据接收;向/sys/class/tty/vttyU1/uart_receive_buff写数据,则上述1中的进程即会接收到数据,测试截图如下
至此我们完成了虚拟并口驱动代码的实现以及验证工作,我们也完成了tty子系统、uart子系统构架内部实现流程的剖析,也完成了对应虚拟控制器驱动的实现及验证,下一次我们开始进行input子系统的剖析(关于本驱动的源码,后续我们会把链接发下来)。