基于Linux内核输入子系统的驱动研究
2012-03-17刘少平
刘少平
(陕西烽火电子股份有限公司 陕西 宝鸡 721006)
嵌入式技术在工业和日常生活中变得越来越普及,Linux作为是目前最流行的操作系统之一,在桌面系统、服务器领域都有大量用户,在嵌入式领域也备受青睐。尽管Linux内核完全由C语言和汇编语言写成,但是却频繁的用到了面向对象的设计思想。具体到驱动方面,就是为同类的设备设计一个框架,而框架的核心层则实现了该设备通用的一些功能。
Linux系统提供了输入子系统,按键、键盘、触摸屏、鼠标等设备都可以利用它的接口函数来实现设备驱动。基于输入子系统的优越性,这种设备程序接口得到了很好的应用,但目前为止介绍输入子系统的相关资料却较缺乏。文中基于Linux内核输入子系统对设备驱动流程进行了研究,并以触摸屏为例,详细分析了驱动实现过程,通过程序移植,触摸屏校准之后可以正常使用。入设备的抽象,是内核中字符设备驱动接口的封装。输入子系统由设备驱动层、核心层和事件处理层构成,如图1所示。
图1 输入子系统Fig.1 Input subsystem
1 Linux下的设备驱动基础
Linux系统主要将设备分成3种类型:字符设备、块设备和网络接口。分别对应字符模块 (charmodule)、块模块(block module)和网络模块(network module)[1]。 比如:常用的按键、键盘、触摸屏、鼠标等都是典型的字符设备。对于面向对象的程序设计,为了极大提高代码的可重用能力,引入了输入子系统。
Linux输入子系统[2-3]是对物理形态各异的功能相似的输
设备驱动层(Input Device Drivers)提供对硬件各寄存器的读写访问和将底层硬件对用户输入访问的响应转换为标准的输入事件,通过核心层提交给事件处理层 (Input Event Drivers);核心层(Input Core)对设备驱动层提供编程接口,对事件处理层的也提供编程接口;事件处理层为用户空间的应用程序提供了统一访问设备的接口和驱动层提交来的事件处理。
以字符设备为例,一般的工作原理是底层在这些输入设备动作发生时产生一个中断(或通过timer定时查询),然后CPU通过SPI、I2C或者外部存储器总线读取键值、坐标等数据,放入一个缓冲区,即字符设备驱动管理缓冲区,而驱动的read()接口让用户可以读取键值,坐标等数据。
显然,在这些工作中,只有中断、读键值与坐标值是与设备相关的,而输入事件的缓冲区管理以及字符设备驱动的文件管理接口对于输入设备是通用的。因此,内核中的输入子系统就用来处理公共的工作。所有的输入事件,内核使用统一的数据结构input_event来描述,如下:
在input_dev结构体中,一个字段是evbit,它表示响应的事件类型。
基于输入子系统的设备驱动层驱动的实现过程如下:
1)在驱动模块加载函数中设置输入设备支持输入子系统的事件,Linux内核用input_dev代表一个输入设备。
2)设备驱动通过set_bit()告诉子系统它支持哪些事件,如 :set_bit (EV_KEY,input_dev.evbit)。 通 过 内 核 提 供 的input_register_device()函数向输入子系统注册输入设备。
3)在键被按下/抬起,触摸屏被触摸/抬起/移动,鼠标被移动/单击/抬起时,输入设备通过 input_report_xxx()报告发生的事件及对应的键值、坐标等状态。
2 基于触摸屏的驱动实现
驱动模块初始化函数中,除了对驱动字符设备注册外,还要进行中断申请等多项工作。在设备驱动中,将实现open( )、release( )、read( )、fasync( )和 poll( )等函数。 以触摸屏[4]为例,常规的触摸屏驱动设计[5-6]需要处理更多的事务,还要向应用层实现设备的读取入口函数等等。而利用输入子系统,通过对input_dev实例的evbit[0]的设置来支持同步(EN_SYN)、按键(EN_KEY)和绝对坐标(EV_ABS)事件。 触摸屏使用输入子系统提供的通用输入事件驱动程序evdev,将事件信息打包成Input_event类型进行报告。其实现流程大致为:
1)定义一个输入设备并进行初始化需要用到的结构体和参数。设置input_dev中的device的名字,名字以input0、input1、input2、input3的 形 式 出 现 在 sysfs文 件 系 统 中 :dev_set_name (&dev->dev,”input%d”, (unsigned long)atomic_inc_return(&input_no)-1)。
2)告知输入子系统,使用device_add()函数将input_dev包含的device结构注册到Linux设备模型中,并可以在 sysfs文件系统中表现出来:device_add(&dev->dev);并将input_dev加入input_dev_list链表中,input_dev_list链表中包含了系统中所有input_dev设备,如下:
list_add_tail(&dev->node,&input_dev_list);
3)在触摸屏设备驱动的模块加载函数中,完成申请设备号、申请中断、设置触摸屏控制引脚等多项工作:
在触摸屏设备驱动中,一次坐标及按下状态的整个报告过程如下:
4) 中断申请:request_irq(DEV_IRQ, dev_interrupt,0,“dev”,NULL))。触摸屏驱动中会产生两类中断,一类是触点中断(INT-TC),一类是X/Y位置转换中断(INT-ADC)。在前一类中断发生后,若之前处于PEN_UP状态,则应该启动X/Y位置转换。另外,将抬起中断也放在INT-TC处理程序中,调用函数完成等待队列和信号的释放。当X/Y位置转换中断发生后,应读取X、Y的坐标值,填入缓冲区。
5)触摸屏设备驱动的读函数实现缓冲区中信息向用户空间的复制。当缓冲区有内容时,直接复制;否则,如果用户阻塞访问触摸屏,则进程在等待队列上睡眠,如果没有阻塞,立即返回-EAGAIN。
6)在模块的卸载函数中,要完成释放设备号、释放中断等工作。注销输入设备函数为:
void input_unregister_device(struct input_dev*dev)。
最后,驱动应向应用层提供接口函数。当应用程序读取和关闭该触摸屏的设备节点时,将其调用,工作流程如图2所示。通过试验,使用该流程驱动的触摸屏校准之后可以正常使用。
3 结束语
Linux输入子系统是对分散的、不同类别的输入设备进行统一处理的内核驱动模型。具有高效、无Bug和可重用等优点。随着信息技术的迅速发展和芯片制造工艺的不断进步,从军事电子设备,现代武器到工业过程控制,从网络通信,办公自动化到消费电子领域,基于Linux系统的产品将与人们的生活密不可分,并且也将有更多的输入事件得到Linux输入子系统的支持。
图2 流程图Fig.2 Flow chart
[1]Johnson MK,Troan EW.Linux应用程序开发[M].2版.武延军,郭松柳,译.北京:电子工业出版社,2005.
[2]Hards B.Using the Input Subsystem,PartⅠ[EB/OL].(2003-02).http://www.linuxjournal.com/article/6396.
[3]Hards B.Using the Input Subsystem, PartⅡ[EB/OL].(2003-02).http://www.linuxjournal.com/article/6429.
[4]孙天泽,袁文菊,张海峰.嵌入式设计及Linux驱动开发指南[M].北京:电子工业出版社,2005.
[5]刘淼.嵌入式系统接口设计与Linux驱动程序开发 [M].北京:北京航空航天大学出版社,2006.
[6]杨卫功,丁忠林.嵌入式Linux系统中触摸屏驱动的研究[J].微计算机信息,2007,23(1-2):103-105.
YANG Wei-gong,DING Zhong-lin.The embedded Linux system touch screen driver[J].Micro Computer Information,2007,23(1-2):103-105.