嵌入式Linux按钮驱动程序开发摘要:文章主要探讨了Linux驱动程序的基本概念以及字符设备、块设备和网路设备的特性,通过鼠标驱动实例阐述了怎样设计和编撰模块化的驱动程序,并解释按键驱动程序的关键代码,最后归纳了开发嵌入式Linux设备驱动程序的核心思想。关键词:设备驱动;Linux;内核;模块;鼠标中图分类号:TP311.1文献标示码:A序言嵌入式Linux以其可应用于多种硬件平台、内核高效稳定、源码开放、软件丰富、网络通讯和文件管理机制建立等优良特点,成为嵌入式系统领域中的一个研究热点。在嵌入式Linux系统中,内核提供保护机制,用户空间的进程通常不能直接访问硬件,进行嵌入式系统的开发,很大的工作量是为各类设备编撰驱动程序。按键设备在嵌入式系统中应用的特别广泛,剖析驱动程序的原理和编撰相应的按键驱动程序在嵌入式开发中显的尤其重要。1设备驱动程序概述1.1设备驱动程序的概念Linux设备驱动程序是为特定的硬件提供给用户程序的一组标准化插口,它隐藏了设备工作的细节[1]。在应用程序看来,硬件设备只是一个设备文件,应用程序可以像操作普通文件一样对硬件设备进行操作。1.2设备驱动程序的类型Linux系统的设备分为字符设备(chardevice)、块设备(lockdevice)和网路设备(networkdevice)三种。
字符设备是指存取时没有缓存的设备,因而在对字符设备发出读/写恳求时,实际的硬件I/O通常就紧接着发生了。字符设备是Linux设备中最简单的一种,应用程序可以用与存取文件相同的系统调换拿来打开、读写及关掉它。典型的字符设备包括键盘、键盘、串行口等。块设备的读写都有缓存来支持,但是块设备必须才能随机存取(randomaccess),是指这些在输入输出时数据处理以块为单位的设备,采用了缓冲技术,支持数据的随机读写,系统可以通过它们的设备做特殊文件访问,并且更常见的是通过文件系统访问。典型的块设备包括硬碟和光碟等。网路设备在Linux里做了专门的处理,Linux的网路系统主要是基于BSDUNIX的socket机制。在系统和驱动程序之间定义有专门的数据结构进行数据的传递,系统支持对发送数据和接收数据的缓存,提供流量控制机制和多合同的支持[2]。典型的网路设备是网卡。2模块化驱动程序设计及流程2.1模块化设计思想模块是指整个系统中一些相对独立的程序单元每位程序单元完成和实现一个相对独立的软件功能Linux的内核是一个整体式内核,假如新添加一个硬件,就须要重新编译内核;假如除去一个硬件linux button驱动,这么这个硬件早已编译进内核的驱动程序就是浪费。
Linux内核用模块来解决这个问题,模块是内核的一部份,并且都是设备驱动程序,但它们并没有被编译到内核中,而是被分别编译并链接成一组目标文件。这种文件能被载入正在运行的内核,或从正在运行的内核中卸载,必要时内核能恳求内核守护进程kerneld对模块进行加载或卸载[3]。按照须要动态载入模块可以保证内核达到最小,但是具有很大的灵活性。内核模块一部份保存在Kernel中,另一部份在Modules包中。2.2几个关键的模块函数(1)init_module()函数init_module()在模块调入内核时被调用,它在内核中用insmod命令注册一定的功能函数(如图1中的功能1、功能2、功能3)。在注册以后,假如有程序访问内核模块的某个功能,如功能1,内核将查表获得功能1在module中的位置,之后调用功能1的函数,同样功能2和功能3也是这样调用的。[insmod][rmmod]图1Linux模块调用结(2)cleanup_module()函数cleanup_module()在模块从内核中卸载时被调用,用rmmod命令把原先注册的功能函数卸载[4]。cleanup_module()函数必须把init_module()函数在内核中注册的功能函数完全卸载,否则,在此模块上次调入时,将会由于有重名的函数而造成调入失败。
其中init_module()函数在运行insmod命令后由系统调用linux运维博客,完成驱动模块的初始化工作。cleanup_module()函数在运行rmmod命令后由系统调用,完成驱动模块卸载时的清理工作。在2.3版本之后的Linux内核中,提供了一种新的方式来命名这两个函数。比如,可以定义my_init()函数来替代init_module()函数,定义my_cleanup()函数来替代cleanup_module()函数,之后在源代码文件末尾使用下边的句子:module_init(my_init);module_exit(my_cleanup);注意:此时在源代码文件中必须包含“#include”语句。这样做的用处是每位模块都可以有自己的初始化和卸载函数的函数名,多个模块在调试时不会有函数重名问题。2.3设备驱动程序开发的步骤(1)定义主、次设备号linux button驱动,也可以动态获取;(2)实现驱动初始化和清理函数,假若驱动程序采用模块形式,则要实现模块初始化和清理函数;(3)设计所要实现的文件操作,定义file_operations结构;(4)实现所需的文件操作调用,如read,write等;(5)实现中断服务函数,并用request_irq向内核注册;(6)将驱动编译到内核或编译成模块,用insmod命令加载;(7)生成设备节点文件。
与普通文件相比,设备文件的操作要复杂得多,不可能简单地通过read、writellseek等命令来实现。所有其它类型的操作都可以通过VFS的ioctl命令调用来执行,因此只须要在驱动程序中实现ioctl,并在其中添加相应的case选项即可[5]。3HDHD72797279A按键驱动的编撰3.1HD7279A按键简介HD7279是一片具有串行插口,可联接多达64键的按键矩阵的字符驱动芯片,单片即可完成按键插口的全部功能,但是有片选讯号,可便捷地实现少于64键的按键插口,广泛的应用于仪表仪器,工业控制器,控制面板等许多场合。3.2HD7279A按键驱动程序按键设备驱动程序的structfile_operation结构申明如下所示,在此依据实际须要,做了适当的精简,只定义了与用户的鼠标应用程序里对设备文件操作的函数相对应的驱动函数。用户可依照自己的须要作相应的定义,使用该方式来提升驱动程序的可移植性。staticstructfile_operationskbd7279_fops={open:kbd7279_open,//打开设备文件ioctl:kbd7279_ioctl,//设备文件其他操作release:kbd7279_close,//关掉设备文件read:kbd7279_read,//读取设备文件}在嵌入式Linux系统中,设备驱动程序就是一个函数和数据结构的集合,所提供的功能是由一个文件操作结构来向系统说明,即file_operations结构。
每位进程对设备的操作,就会按照MAJOR、MINOR设备号,转换成对file_operations结构的访问,当操作系统对设备进行操作时,会调用驱动程序注册的file_operations构中的成员,这个结构中的成员几乎全部是函数表针,所以实质上就是函数跳转表,依据所接收的恳求,进行对应的操作。staticintkbd7279_open(structinode*inode,structfile*file){kdb7279_event(kdb7279_stop);//关掉内核对按键的操作MOD_INC_USE_COUNT;//使模块的使用次数加1return0;}在用户程序打开设备时,鼠标字符设备驱动程序实现的函数kbd7279_open把鼠标控制单元形参给文件描述符的私有数据。MOD_INC_USE_COUNT是内核提供的一个宏,它使模块的使用次数加1,目的是避免当模块被使用的同时又被卸载,只有当模块使用计数为0时,才会被卸载。staticintkbd7279_relaese(structinode*inode,structfile*file){MOD_DEC_USE_COUNT;//使模块的使用次数减1kdb7279_event(kdb7279_start);//恢复内核对按键的操作return0;}当用户关掉设备时,先使模块的使用计数减1linux使用教程,再还原内核对鼠标的控制。
staticintkbd7279_read(structfile*fp,char*buf,size_tcount){KEY_LOCK(unit);//上锁put_user(kbd_buf,buf);//把处于内核空间记录按钮值的数据复制到用户空间buf中kbd_buf=0xFF;//把记录按钮值的暂存单元清空KEY_UNLOCK(unit);//换锁return0;}读取鼠标状态时,先锁定按键控制单元,避免另一进程同一时刻改变鼠标状态。之后从控制单元获得状态值,并拷贝到用户缓冲区。操作完成后解锁,并返回读取字节数。其中copy_to_user函数实现将内核空间的数据copy到用户空间里。staticintkbd7279_ioctl(structinode*inode,structfile*file,unsignedintcmd,unsignedlongarg){switch(cmd){casekbd7279_GETKEY:returnkbd7279_getkey();//获取键盘值break;default:printk("UnkownKeyboardCommandID.n");break;}return0;}函数主要实现把用户空间所保存的键盘值送到数码管上显示。
注册函数_initkbd7279_Init(void)中,驱动程序通过调用register_chrdev(KEYBOARD_MAJOR,"kbd7279",&kbd7279_fops)注册设备,调用request_irq(33,kbd7279_ISR,0,"kbd7279","88")申请中断号。void_exitkbd7279_exit(void)//关掉鼠标设备{unregister_chrdev(KEYBOARD_MAJOR,"kbd7279");//注销设备free_irq(33,"88");//释放中断号send_byte(cmd_reset);//关掉鼠标}在这个函数中,释放了设备所占用的中断号并通过设备注册号关掉了鼠标设备。4结束语对Linux的驱动原理和鼠标驱动的具体实现方式进行了详尽的介绍,并加以实现,该驱动采用了高度的结构化、模块化、层次化的插口方式,可以便捷的移植到Linux下其它系列的按键驱动,也对在嵌入式Linux下开发其它的驱动具有指导意义。参考文献:[1]Rubini.A.Linux设备驱动程序[M].上海:中国电力出版社,2002.[2]周立功.ARM微控制器基础与实战[M].上海:上海民航航人学院出版社,2003.[3]李善平,刘文峰,李程远.Linux内核2.4版源代码剖析大全[M].上海:机械工业出版社,2002.[4]胥静.嵌入式系统设计与开发实例解读—基于ARM的应用[M].上海:上海航天民航学院出版社,2005.[5][美]JohnLombardo著.吴雨浓译.EmbededLinux[M].上海:中国电力出版社,2003.4功能1功能2功能3Module功能1功能2功能3注册功能函数删掉功能函数驱动函数调用LinuxKernel