Linux输入子系统框架原理解析
input输入子系统框架
linux输入子系统(linuxinputsubsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler)、输入子系统核心层(InputCore)和输入子系统设备驱动层。
一个输入事件,如鼠标移动,键盘按键按下,joystick的移动等等通过inputdriver->Inputcore->Eventhandler->userspace到达用户空间传给应用程序。
【注意】keyboard.c不会在/dev/input下产生节点,而是作为ttyn终端(不包括串口终端)的输入。
驱动层
对于输入子系统设备驱动层而言,主要实现对硬件设备的读写访问,中断设置,并把硬件产生的事件转换为核心层定义的规范提交给事件处理层。将底层的硬件输入转化为统一事件形式,想输入核心(InputCore)汇报。
输入子系统核心层
对于核心层而言,为设备驱动层提供了规范和接口。设备驱动层只要关心如何驱动硬件并获得硬件数据(例如按下的按键数据),然后调用核心层提供的接口,核心层会自动把数据提交给事件处理层。它承上启下为驱动层提供输入设备注册与操作接口,如:input_register_device;通知事件处理层对事件进行处理;在/Proc下产生相应的设备信息。
事件处理层
对于事件处理层而言,则是用户编程的接口(设备节点),并处理驱动层提交的数据处理。主要是和用户空间交互(Linux中在用户空间将所有的设备都当作文件来处理,由于在一般的驱动程序中都有提供fops接口,以及在/dev下生成相应的设备文件nod,这些操作在输入子系统中由事件处理层完成)。
/dev/input目录下显示的是已经注册在内核中的设备编程接口,用户通过open这些设备文件来打开不同的输入设备进行硬件操作。
事件处理层为不同硬件类型提供了用户访问及处理接口。例如当我们打开设备/dev/input/mice时,会调用到事件处理层的MouseHandler来处理输入事件,这也使得设备驱动层无需关心设备文件的操作,因为MouseHandler已经有了对应事件处理的方法。
输入子系统由内核代码drivers/input/input.c构成,它的存在屏蔽了用户到设备驱动的交互细节,为设备驱动层和事件处理层提供了相互通信的统一界面。
由上图可知输入子系统核心层提供的支持以及如何上报事件到inputeventdrivers。
作为输入设备的驱动开发者,需要做以下几步:
- 在驱动加载模块中,设置你的input设备支持的事件类型
- 注册中断处理函数,例如键盘设备需要编写按键的抬起、放下,触摸屏设备需要编写按下、抬起、绝对移动,鼠标设备需要编写单击、抬起、相对移动,并且需要在必要的时候提交硬件数据(键值/坐标/状态等等)
- 将输入设备注册到输入子系统中
///////////////////////////////////////////////////////////////////分割线/////////////////////////////////////////////////////////////////////////////////
输入核心提供了底层输入设备驱动程序所需的API,如分配/释放一个输入设备:
structinput_dev*input_allocate_device(void);
voidinput_free_device(structinput_dev*dev);
/** *input_allocate_device-allocatememoryfornewinputdevice * *Returnspreparedstructinput_devorNULL. * *NOTE:Useinput_free_device()tofreedevicesthathavenotbeen *registered;input_unregister_device()shouldbeusedforalready *registereddevices. */ structinput_dev*input_allocate_device(void) { structinput_dev*dev; /*分配一个input_dev结构体,并初始化为0*/ dev=kzalloc(sizeof(structinput_dev),GFP_KERNEL); if(dev){ dev->dev.type=&input_dev_type;/*初始化设备的类型*/ dev->dev.class=&input_class;/*设置为输入设备类*/ device_initialize(&dev->dev);/*初始化device结构*/ mutex_init(&dev->mutex);/*初始化互斥锁*/ spin_lock_init(&dev->event_lock);/*初始化事件自旋锁*/ INIT_LIST_HEAD(&dev->h_list);/*初始化链表*/ INIT_LIST_HEAD(&dev->node);/*初始化链表*/ __module_get(THIS_MODULE);/*模块引用技术加1*/ } returndev; }
注册/注销输入设备用的接口如下:
int__must_checkinput_register_device(structinput_dev*);
voidinput_unregister_device(structinput_dev*);
/** *input_register_device-registerdevicewithinputcore *@dev:devicetoberegistered * *Thisfunctionregistersdevicewithinputcore.Thedevicemustbe *allocatedwithinput_allocate_device()andallit'scapabilities *setupbeforeregistering. *Iffunctionfailsthedevicemustbefreedwithinput_free_device(). *Oncedevicehasbeensuccessfullyregistereditcanbeunregistered *withinput_unregister_device();input_free_device()shouldnotbe *calledinthiscase. */ intinput_register_device(structinput_dev*dev) { //定义一些函数中将用到的局部变量 staticatomic_tinput_no=ATOMIC_INIT(0); structinput_handler*handler; constchar*path; interror; //设置input_dev所支持的事件类型,由evbit成员来表示。具体类型在后面归纳。 /*EveryinputdevicegeneratesEV_SYN/SYN_REPORTevents.*/ __set_bit(EV_SYN,dev->evbit); /*KEY_RESERVEDisnotsupposedtobetransmittedtouserspace.*/ __clear_bit(KEY_RESERVED,dev->keybit); /*Makesurethatbitmasksnotmentionedindev->evbitareclean.*/ input_cleanse_bitmasks(dev); //初始化timer定时器,用来处理重复点击按键。(去抖) /* *Ifdelayandperiodarepre-setbythedriver,thenautorepeating *ishandledbythedriveritselfandwedon'tdoitininput.c. */ init_timer(&dev->timer); //如果rep[REP_DELAY]和[REP_PERIOD]没有设值,则赋默认值。为了去抖。 if(!dev->rep[REP_DELAY]&&!dev->rep[REP_PERIOD]){ dev->timer.data=(long)dev; dev->timer.function=input_repeat_key; dev->rep[REP_DELAY]=250; dev->rep[REP_PERIOD]=33; } //检查下列两个函数是否被定义,没有被定义则赋默认值。 if(!dev->getkeycode) dev->getkeycode=input_default_getkeycode;//得到指定位置键值 if(!dev->setkeycode) dev->setkeycode=input_default_setkeycode;//设置指定位置键值 //设置input_dev中device的名字为inputN //将如input0input1input2出现在sysfs文件系统中 dev_set_name(&dev->dev,"input%ld", (unsignedlong)atomic_inc_return(&input_no)-1); //将input->dev包含的device结构注册到Linux设备模型中。 error=device_add(&dev->dev); if(error) returnerror; //打印设备的路径并输出调试信息 path=kobject_get_path(&dev->dev.kobj,GFP_KERNEL); printk(KERN_INFO"input:%sas%s\n", dev->name?dev->name:"Unspecifieddevice",path?path:"N/A"); kfree(path); error=mutex_lock_interruptible(&input_mutex); if(error){ device_del(&dev->dev); returnerror; } //将input_dev加入input_dev_list链表中(这个链表中包含有所有input设备) list_add_tail(&dev->node,&input_dev_list); list_for_each_entry(handler,&input_handler_list,node) //调用input_attatch_handler()函数匹配handler和input_dev。 //这个函数很重要,在后面单独分析。 input_attach_handler(dev,handler); input_wakeup_procfs_readers(); mutex_unlock(&input_mutex); return0; }
而对于所有的输入事件,内核都用统一的数据结构来描述,这个数据结构是input_event
/*
*Theeventstructureitself
*/
structinput_event{
structtimevaltime;//<输入事件发生的时间
__u16type;//<输入事件的类型
__u16code;//<在输入事件类型下的编码
__s32value;//
输入事件的类型--input_event.type
/*
*Eventtypes
*/
#defineEV_SYN0x00//<同步事件
#defineEV_KEY0x01//<按键事件
#defineEV_REL0x02//<相对坐标(如:鼠标移动,报告相对最后一次位置的偏移)
#defineEV_ABS0x03//<绝对坐标(如:触摸屏或操作杆,报告绝对的坐标位置)
#defineEV_MSC0x04//<其它
#defineEV_SW0x05//<开关
#defineEV_LED0x11//<按键/设备灯
#defineEV_SND0x12//<声音/警报
#defineEV_REP0x14//<重复
#defineEV_FF0x15//<力反馈
#defineEV_PWR0x16//<电源
#defineEV_FF_STATUS0x17//<力反馈状态
#defineEV_MAX0x1f//<事件类型最大个数和提供位掩码支持
#defineEV_CNT(EV_MAX+1)
Linux输入子系统提供了设备驱动层上报输入事件的函数
报告输入事件用的接口如下:
/*报告指定type、code的输入事件*/
voidinput_event(structinput_dev*dev,unsignedinttype,unsignedintcode,intvalue);
/*报告键值*/
staticinlinevoidinput_report_key(structinput_dev*dev,unsignedintcode,intvalue)
{
input_event(dev,EV_KEY,code,!!value);
}
/*报告相对坐标*/
staticinlinevoidinput_report_rel(structinput_dev*dev,unsignedintcode,intvalue)
{
input_event(dev,EV_REL,code,value);
}
/*报告绝对坐标*/
staticinlinevoidinput_report_abs(structinput_dev*dev,unsignedintcode,intvalue)
{
input_event(dev,EV_ABS,code,value);
}
...
当提交输入设备产生的输入事件之后,需要调用下面的函数来通知输入子系统,以处理设备产生的完整事件:
voidinput_sync(structinput_dev*dev);
【例子】驱动实现——报告结束input_sync()同步用于告诉inputcore子系统报告结束,触摸屏设备驱动中,一次点击的整个报告过程如下:
input_reprot_abs(input_dev,ABS_X,x);//x坐标
input_reprot_abs(input_dev,ABS_Y,y);//y坐标
input_reprot_abs(input_dev,ABS_PRESSURE,1);
input_sync(input_dev);//同步结束
【例子】按键中断程序
//按键初始化
staticint__initbutton_init(void)
{//申请中断
if(request_irq(BUTTON_IRQ,button_interrupt,0,”button”,NUll))
return–EBUSY;
set_bit(EV_KEY,button_dev.evbit);//支持EV_KEY事件
set_bit(BTN_0,button_dev.keybit);//支持设备两个键
set_bit(BTN_1,button_dev.keybit);//
input_register_device(&button_dev);//注册input设备
}
/*在按键中断中报告事件*/
Staticvoidbutton_interrupt(intirq,void*dummy,structpt_regs*fp)
{
input_report_key(&button_dev,BTN_0,inb(BUTTON_PORT0));//读取寄存器BUTTON_PORT0的值
input_report_key(&button_dev,BTN_1,inb(BUTTON_PORT1));
input_sync(&button_dev);
}
【小结】input子系统仍然是字符设备驱动程序,但是代码量减少很多,input子系统只需要完成两个工作:初始化和事件报告(这里在linux中是通过中断来实现的)。
EventHandler层解析
Input输入子系统数据结构关系图
input_handler结构体
structinput_handle;
/**
*structinput_handler-implementsoneofinterfacesforinputdevices
*@private:driver-specificdata
*@event:eventhandler.Thismethodisbeingcalledbyinputcorewith
*interruptsdisabledanddev->event_lockspinlockheldandso
*itmaynotsleep
*@filter:similarto@event;separatesnormaleventhandlersfrom
*"filters".
*@match:calledaftercomparingdevice'sidwithhandler'sid_table
*toperformfine-grainedmatchingbetweendeviceandhandler
*@connect:calledwhenattachingahandlertoaninputdevice
*@disconnect:disconnectsahandlerfrominputdevice
*@start:startshandlerforgivenhandle.Thisfunctioniscalledby
*inputcorerightafterconnect()methodandalsowhenaprocess
*that"grabbed"adevicereleasesit
*@fops:fileoperationsthisdriverimplements
*@minor:beginningofrangeof32minorsfordevicesthisdriver
*canprovide
*@name:nameofthehandler,tobeshownin/proc/bus/input/handlers
*@id_table:pointertoatableofinput_device_idsthisdrivercan
*handle
*@h_list:listofinputhandlesassociatedwiththehandler
*@node:forplacingthedriverontoinput_handler_list
*
*Inputhandlersattachtoinputdevicesandcreateinputhandles.There
*arelikelyseveralhandlersattachedtoanygiveninputdeviceatthe
*sametime.Allofthemwillgettheircopyofinputeventgeneratedby
*thedevice.
*
*Theverysamestructureisusedtoimplementinputfilters.Inputcore
*allowsfilterstorunfirstandwillnotpasseventtoregularhandlers
*ifanyofthefiltersindicatethattheeventshouldbefiltered(by
*returning%truefromtheirfilter()method).
*
*Notethatinputcoreserializescallstoconnect()anddisconnect()
*methods.
*/
structinput_handler{
void*private;
void(*event)(structinput_handle*handle,unsignedinttype,unsignedintcode,intvalue);
bool(*filter)(structinput_handle*handle,unsignedinttype,unsignedintcode,intvalue);
bool(*match)(structinput_handler*handler,structinput_dev*dev);
int(*connect)(structinput_handler*handler,structinput_dev*dev,conststructinput_device_id*id);
void(*disconnect)(structinput_handle*handle);
void(*start)(structinput_handle*handle);
conststructfile_operations*fops;
intminor;
constchar*name;
conststructinput_device_id*id_table;
structlist_headh_list;
structlist_headnode;
};
【例子】以evdev.c中的evdev_handler为例:
staticstructinput_handlerevdev_handler={
.event=evdev_event,//<向系统报告input事件,系统通过read方法读取
.connect=evdev_connect,//<和input_dev匹配后调用connect构建
.disconnect=evdev_disconnect,
.fops=&evdev_fops,//
输入设备驱动的简单案例
documentation/input/input-programming.txt文件,讲解了编写输入设备驱动程序的核心步骤。
Programminginputdrivers
~~~~~~~~~~~~~~~~~~~~~~~~~
1.Creatinganinputdevicedriver
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1.0Thesimplestexample
~~~~~~~~~~~~~~~~~~~~~~~~
Herecomesaverysimpleexampleofaninputdevicedriver.Thedevicehas
justonebuttonandthebuttonisaccessibleati/oportBUTTON_PORT.When
pressedorreleasedaBUTTON_IRQhappens.Thedrivercouldlooklike:
#include
#include
#include
#include
#include
staticstructinput_dev*button_dev;
staticirqreturn_tbutton_interrupt(intirq,void*dummy)
{
input_report_key(button_dev,BTN_0,inb(BUTTON_PORT)&1);
input_sync(button_dev);
returnIRQ_HANDLED;
}
staticint__initbutton_init(void)
{
interror;
if(request_irq(BUTTON_IRQ,button_interrupt,0,"button",NULL)){
printk(KERN_ERR"button.c:Can'tallocateirq%d\n",button_irq);
return-EBUSY;
}
button_dev=input_allocate_device();
if(!button_dev){
printk(KERN_ERR"button.c:Notenoughmemory\n");
error=-ENOMEM;
gotoerr_free_irq;
}
button_dev->evbit[0]=BIT_MASK(EV_KEY);
button_dev->keybit[BIT_WORD(BTN_0)]=BIT_MASK(BTN_0);
error=input_register_device(button_dev);
if(error){
printk(KERN_ERR"button.c:Failedtoregisterdevice\n");
gotoerr_free_dev;
}
return0;
err_free_dev:
input_free_device(button_dev);
err_free_irq:
free_irq(BUTTON_IRQ,button_interrupt);
returnerror;
}
staticvoid__exitbutton_exit(void)
{
input_unregister_device(button_dev);
free_irq(BUTTON_IRQ,button_interrupt);
}
module_init(button_init);
module_exit(button_exit);
1.1Whattheexampledoes
~~~~~~~~~~~~~~~~~~~~~~~~~
Firstithastoincludethefile,whichinterfacestothe
inputsubsystem.Thisprovidesallthedefinitionsneeded.
Inthe_initfunction,whichiscalledeitheruponmoduleloadorwhen
bootingthekernel,itgrabstherequiredresources(itshouldalsocheck
forthepresenceofthedevice).
Thenitallocatesanewinputdevicestructurewithinput_allocate_device()
andsetsupinputbitfields.Thiswaythedevicedrivertellstheother
partsoftheinputsystemswhatitis-whateventscanbegeneratedor
acceptedbythisinputdevice.OurexampledevicecanonlygenerateEV_KEY
typeevents,andfromthoseonlyBTN_0eventcode.Thusweonlysetthese
twobits.Wecouldhaveused
set_bit(EV_KEY,button_dev.evbit);
set_bit(BTN_0,button_dev.keybit);
aswell,butwithmorethansinglebitsthefirstapproachtendstobe
shorter.
Thentheexampledriverregisterstheinputdevicestructurebycalling
input_register_device(&button_dev);
Thisaddsthebutton_devstructuretolinkedlistsoftheinputdriverand
callsdevicehandlermodules_connectfunctionstotellthemanewinput
devicehasappeared.input_register_device()maysleepandthereforemust
notbecalledfromaninterruptorwithaspinlockheld.
Whileinuse,theonlyusedfunctionofthedriveris
button_interrupt()
whichuponeveryinterruptfromthebuttonchecksitsstateandreportsit
viathe
input_report_key()
calltotheinputsystem.Thereisnoneedtocheckwhethertheinterrupt
routineisn'treportingtwosamevalueevents(press,pressforexample)to
theinputsystem,becausetheinput_report_*functionscheckthat
themselves.
Thenthereisthe
input_sync()
calltotellthosewhoreceivetheeventsthatwe'vesentacompletereport.
Thisdoesn'tseemimportantintheonebuttoncase,butisquiteimportant
forforexamplemousemovement,whereyoudon'twanttheXandYvalues
tobeinterpretedseparately,becausethat'dresultinadifferentmovement.
1.2dev->open()anddev->close()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Incasethedriverhastorepeatedlypollthedevice,becauseitdoesn't
haveaninterruptcomingfromitandthepollingistooexpensivetobedone
allthetime,orifthedeviceusesavaluableresource(eg.interrupt),it
canusetheopenandclosecallbacktoknowwhenitcanstoppollingor
releasetheinterruptandwhenitmustresumepollingorgrabtheinterrupt
again.Todothat,wewouldaddthistoourexampledriver:
staticintbutton_open(structinput_dev*dev)
{
if(request_irq(BUTTON_IRQ,button_interrupt,0,"button",NULL)){
printk(KERN_ERR"button.c:Can'tallocateirq%d\n",button_irq);
return-EBUSY;
}
return0;
}
staticvoidbutton_close(structinput_dev*dev)
{
free_irq(IRQ_AMIGA_VERTB,button_interrupt);
}
staticint__initbutton_init(void)
{
...
button_dev->open=button_open;
button_dev->close=button_close;
...
}
Notethatinputcorekeepstrackofnumberofusersforthedeviceand
makessurethatdev->open()iscalledonlywhenthefirstuserconnects
tothedeviceandthatdev->close()iscalledwhentheverylastuser
disconnects.Callstobothcallbacksareserialized.
Theopen()callbackshouldreturna0incaseofsuccessoranynonzerovalue
incaseoffailure.Theclose()callback(whichisvoid)mustalwayssucceed.
1.3Basiceventtypes
~~~~~~~~~~~~~~~~~~~~~
ThemostsimpleeventtypeisEV_KEY,whichisusedforkeysandbuttons.
It'sreportedtotheinputsystemvia:
input_report_key(structinput_dev*dev,intcode,intvalue)
Seelinux/input.hfortheallowablevaluesofcode(from0toKEY_MAX).
Valueisinterpretedasatruthvalue,ieanynonzerovaluemeanskey
pressed,zerovaluemeanskeyreleased.Theinputcodegenerateseventsonly
incasethevalueisdifferentfrombefore.
InadditiontoEV_KEY,therearetwomorebasiceventtypes:EV_RELand
EV_ABS.Theyareusedforrelativeandabsolutevaluessuppliedbythe
device.ArelativevaluemaybeforexampleamousemovementintheXaxis.
Themousereportsitasarelativedifferencefromthelastposition,
becauseitdoesn'thaveanyabsolutecoordinatesystemtoworkin.Absolute
eventsarenamelyforjoysticksanddigitizers-devicesthatdoworkinan
absolutecoordinatesystems.
HavingthedevicereportEV_RELbuttonsisassimpleaswithEV_KEY,simply
setthecorrespondingbitsandcallthe
input_report_rel(structinput_dev*dev,intcode,intvalue)
function.Eventsaregeneratedonlyfornonzerovalue.
HoweverEV_ABSrequiresalittlespecialcare.Beforecalling
input_register_device,youhavetofilladditionalfieldsintheinput_dev
structforeachabsoluteaxisyourdevicehas.Ifourbuttondevicehadalso
theABS_Xaxis:
button_dev.absmin[ABS_X]=0;
button_dev.absmax[ABS_X]=255;
button_dev.absfuzz[ABS_X]=4;
button_dev.absflat[ABS_X]=8;
Or,youcanjustsay:
input_set_abs_params(button_dev,ABS_X,0,255,4,8);
ThissettingwouldbeappropriateforajoystickXaxis,withtheminimumof
0,maximumof255(whichthejoystick*must*beabletoreach,noproblemif
itsometimesreportsmore,butitmustbeabletoalwaysreachtheminand
maxvalues),withnoiseinthedataupto+-4,andwithacenterflat
positionofsize8.
Ifyoudon'tneedabsfuzzandabsflat,youcansetthemtozero,whichmean
thatthethingispreciseandalwaysreturnstoexactlythecenterposition
(ifithasany).
1.4BITS_TO_LONGS(),BIT_WORD(),BIT_MASK()
~~~~~~~~~~~~~~~~~~~~~~~~~~
Thesethreemacrosfrombitops.hhelpsomebitfieldcomputations:
BITS_TO_LONGS(x)-returnsthelengthofabitfieldarrayinlongsfor
xbits
BIT_WORD(x)-returnstheindexinthearrayinlongsforbitx
BIT_MASK(x)-returnstheindexinalongforbitx
1.5Theid*andnamefields
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Thedev->nameshouldbesetbeforeregisteringtheinputdevicebytheinput
devicedriver.It'sastringlike'Genericbuttondevice'containinga
userfriendlynameofthedevice.
Theid*fieldscontainthebusID(PCI,USB,...),vendorIDanddeviceID
ofthedevice.ThebusIDsaredefinedininput.h.Thevendoranddeviceids
aredefinedinpci_ids.h,usb_ids.handsimilarincludefiles.Thesefields
shouldbesetbytheinputdevicedriverbeforeregisteringit.
Theidtypefieldcanbeusedforspecificinformationfortheinputdevice
driver.
Theidandnamefieldscanbepassedtouserlandviatheevdevinterface.
1.6Thekeycode,keycodemax,keycodesizefields
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Thesethreefieldsshouldbeusedbyinputdevicesthathavedensekeymaps.
Thekeycodeisanarrayusedtomapfromscancodestoinputsystemkeycodes.
Thekeycodemaxshouldcontainthesizeofthearrayandkeycodesizethe
sizeofeachentryinit(inbytes).
Userspacecanqueryandaltercurrentscancodetokeycodemappingsusing
EVIOCGKEYCODEandEVIOCSKEYCODEioctlsoncorrespondingevdevinterface.
Whenadevicehasall3aforementionedfieldsfilledin,thedrivermay
relyonkernel'sdefaultimplementationofsettingandqueryingkeycode
mappings.
1.7dev->getkeycode()anddev->setkeycode()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
getkeycode()andsetkeycode()callbacksallowdriverstooverridedefault
keycode/keycodesize/keycodemaxmappingmechanismprovidedbyinputcore
andimplementsparsekeycodemaps.
1.8Keyautorepeat
~~~~~~~~~~~~~~~~~~
...issimple.Itishandledbytheinput.cmodule.Hardwareautorepeatis
notused,becauseit'snotpresentinmanydevicesandevenwhereitis
present,itisbrokensometimes(atkeyboards:Toshibanotebooks).Toenable
autorepeatforyourdevice,justsetEV_REPindev->evbit.Allwillbe
handledbytheinputsystem.
1.9Othereventtypes,handlingoutputevents
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Theothereventtypesuptonoware:
EV_LED-usedforthekeyboardLEDs.
EV_SND-usedforkeyboardbeeps.
Theyareverysimilartoforexamplekeyevents,buttheygointheother
direction-fromthesystemtotheinputdevicedriver.Ifyourinputdevice
drivercanhandletheseevents,ithastosettherespectivebitsinevbit,
*and*alsothecallbackroutine:
button_dev->event=button_event;
intbutton_event(structinput_dev*dev,unsignedinttype,unsignedintcode,intvalue);
{
if(type==EV_SND&&code==SND_BELL){
outb(value,BUTTON_BELL);
return0;
}
return-1;
}
ThiscallbackroutinecanbecalledfromaninterruptoraBH(althoughthat
isn'tarule),andthusmustnotsleep,andmustnottaketoolongtofinish.
input-programming.txt
该例子提供的案例代码描述了一个button设备,产生的事件通过BUTTON_PORT引脚获取,当有按下/释放发生时,BUTTON_IRQ被触发,以下是驱动的源代码:
#include
#include
#include
#include
#include
staticstructinput_dev*button_dev;/*输入设备结构体*/
/*中断处理函数*/
staticirqreturn_tbutton_interrupt(intirq,void*dummy)
{
/*向输入子系统报告产生按键事件*/
input_report_key(button_dev,BTN_0,inb(BUTTON_PORT)&1);
/*通知接收者,一个报告发送完毕*/
input_sync(button_dev);
returnIRQ_HANDLED;
}
/*加载函数*/
staticint__initbutton_init(void)
{
interror;
/*申请中断处理函数*///返回0表示成功,返回-INVAL表示无效
if(request_irq(BUTTON_IRQ,button_interrupt,0,"button",NULL)){
/*申请失败,则打印出错信息*/
printk(KERN_ERR"button.c:Can'tallocateirq%d\n",button_irq);
return-EBUSY;
}
/*分配一个设备结构体*/
//将在sys/class/input/input-n下面创建设备属性文件
button_dev=input_allocate_device();
if(!button_dev){/*判断分配是否成功*/
printk(KERN_ERR"button.c:Notenoughmemory\n");
error=-ENOMEM;
gotoerr_free_irq;
}
button_dev->evbit[0]=BIT_MASK(EV_KEY);/*设置按键信息*/
button_dev->keybit[BIT_WORD(BTN_0)]=BIT_MASK(BTN_0);
error=input_register_device(button_dev);/*注册一个输入设备*/
if(error){
printk(KERN_ERR"button.c:Failedtoregisterdevice\n");
gotoerr_free_dev;
}
return0;
/*以下是错误处理*/
err_free_dev:
input_free_device(button_dev);
err_free_irq:
free_irq(BUTTON_IRQ,button_interrupt);
returnerror;
}
/*卸载函数*/
staticvoid__exitbutton_exit(void)
{
input_unregister_device(button_dev);/*注销按键设备*/
free_irq(BUTTON_IRQ,button_interrupt);/*释放按键占用的中断线*/
}
module_init(button_init);
module_exit(button_exit);
从这个简单的例子中可以看到。
- 在初始化函数button_init()中注册了一个中断处理函数,然后调用input_allocate_device()函数分配了一个input_dev结构体,并调用input_register_device()对其进行注册。
- 在中断处理函数button_interrupt()中,实例将接收到的按键信息上报给input子系统,从而通过input子系统,向用户态程序提供按键输入信息。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。