1. 新建一个输入设备驱动程序
1.0 一个最简略的比如
本文由DroidPhone 翻译:http://blog.csdn.net/droidphone
Kernel版别:V3.4.10
以下是一个十分简略的输入设备驱动程序。该设备只需一个按键,它经过BUTTON_PORT这一i/o端口拜访,当按下或开释该按键,会发生BUTTON_IRQ中止,驱动程序看起来就像这样:
[cpp] view plain copy
#include
#include
#include
#include
#include
static struct input_dev *button_dev;
staTIc irqreturn_t button_interrupt(int irq, void *dummy)
{
input_report_key(button_dev, BTN_0, inb(BUTTON_PORT) & 1);
input_sync(button_dev);
return IRQ_HANDLED;
}
staTIc int __init button_init(void)
{
int error;
if (request_irq(BUTTON_IRQ, button_interrupt, 0, “button”, NULL)) {
printk(KERN_ERR “button.c: Can't allocate irq %d\n”, button_irq);
return -EBUSY;
}
button_dev = input_allocate_device();
if (!button_dev) {
printk(KERN_ERR “button.c: Not enough memory\n”);
error = -ENOMEM;
goto err_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: Failed to register device\n”);
goto err_free_dev;
}
return 0;
err_free_dev:
input_free_device(button_dev);
err_free_irq:
free_irq(BUTTON_IRQ, button_interrupt);
return error;
}
staTIc void __exit button_exit(void)
{
input_unregister_device(button_dev);
free_irq(BUTTON_IRQ, button_interrupt);
}
module_init(button_init);
module_exit(button_exit);
1.1 比如驱动的作业进程
首要它有必要包括头文件,它是input子体系的接口,它供给了一切必要的界说信息。
_init初始化函数,能够经过模块进行加载,也能够编译进内核中,它搜集设备需求的资源(也会查看设备是否存在)。
接着,它用input_allocate_device()分配了一个input device结构,设置它的bitfields,设备驱动程序经过这一办法把本身的信息告知input子体系的其他部分:它能发生或许承受什么工作。咱们的比如设备只能发生EV_KEY类型的工作,并且只能宣布BTN_0工作code。这样咱们只需求设置这两个bits,咱们也能够用
[cpp] view plain copy
set_bit(EV_KEY, button_dev.evbit);
set_bit(BTN_0, button_dev.keybit);
进行设置,可是上面比如代码中的办法能够一次设置更多的位。
然后,比如驱动用下面的函数注册input device结构:
[cpp] view plain copy
input_register_device(&button_dev);
这会把button_dev结构增加到input driver的大局链表中,调用device handler模块中的_connect函数来告知他一个新的设备呈现了。input_register_device()可能会休眠,所以他不能在中止或许持有一个spinlock的情况下被运用。
功用上,该驱动只运用了函数:
[cpp] view plain copy
button_interrupt()
在每次中止中经过查看按键的状况,并经过以下函数上报:
[cpp] view plain copy
input_report_key()
该函数会进入input子体系。中止服务程序不用查看它是否会给input子体系陈述value重复的工作(例如:按下,按下)。由于input_report_*函数自己会对此进行查看。
然后便是调用:
[cpp] view plain copy
input_sync()
该函数告知工作的接收者,咱们现已发送了一次完好的陈述信息。这对于咱们这个只需一个按键的设备如同不太重要,但有些情况下它是十分重要的,例如当鼠标移动后,你不期望X和Y值被分隔解说,由于那样会被解说为两次移动。
1.2 dev->open() and dev->close()
当驱动由于设备没有供给中止才能时,它需求不停地查询设备的状况,可是假设一向进行这个查询显得有点糟蹋。有时设备需求运用一些有价值的资源(例如中止)。这时,咱们能够运用open和close回调函数来完成动态地中止查询和开释中止和决议何时再次康复查询和获取中止。要完成这一功用,咱们的比如驱动需求增加以下代码:
[cpp] view plain copy
staTIc int button_open(struct input_dev *dev)
{
if (request_irq(BUTTON_IRQ, button_interrupt, 0, “button”, NULL)) {
printk(KERN_ERR “button.c: Can't allocate irq %d\n”, button_irq);
return -EBUSY;
}
return 0;
}
static void button_close(struct input_dev *dev)
{
free_irq(IRQ_AMIGA_VERTB, button_interrupt);
}
static int __init button_init(void)
{
…
button_dev->open = button_open;
button_dev->close = button_close;
…
}
需求留意的是,input中心会坚持设备的运用计数来确保dev->open()只需当第一个用户衔接该设备时才被调用,而dev->close()只需当最终一个用户断开和设备的衔接时才被调用。对两个回调的调用都是串行化的。
当调用成功时,open()回调回来0,回来非0则表明发生了过错。回来类型是void的close()回调有必要一向成功。
1.3 根本工作类型(types)
最简略的工作类型是EV_KEY,它用于键盘和按钮,它经过以下函数上报给input子体系:
[cpp] view plain copy
input_report_key(struct input_dev *dev, int code, int value)
linux/input.h界说了该类型可用的values和code (从0 到 KEY_MAX)。Value被解说为真假值,也便是任何非0值意味着键被按下,0则意味着键被松开。input子体系的代码只需当value的值和之前的值不一起才会生成一次工作。
除了EV_KEY外,还有别的两个根本的工作类型:EV_REL和EV_ABS 。它们用来供给设备的相对和肯定值。鼠标移动是一种相对值,鼠标陈述相对于上一个方位的相对差值,由于它作业在没有任何肯定坐标系体系中。肯定值工作对游戏操纵杆和数字化仪有用,这些设备作业在一个肯定坐标体系中。
设备上报EV_REL就像EV_KEY相同简略,只需设置相应的位然后调用以下函数即可:
[cpp] view plain copy
input_report_rel(struct input_dev *dev, int code, int value)
只需当非0的value时,工作才会被发生。
不过EV_ABS需求一点点特别的处理。在调用input_register_device之前,你需求在input_dev结构中为你的设备所支撑的轴填充的额定字段。假设咱们的按钮设备有ABS_X轴:
[cpp] view plain copy
button_dev.absmin[ABS_X] = 0;
button_dev.absmax[ABS_X] = 255;
button_dev.absfuzz[ABS_X] = 4;
button_dev.absflat[ABS_X] = 8;
或许,你只需求这样:
[cpp] view plain copy
input_set_abs_params(button_dev, ABS_X, 0, 255, 4, 8);
上述设置适合于一个游系操纵杆设备,它有一个X轴,最小值是0,最大值是255(这表明它有必要能供给的规模,偶然上报超出该规模也不会有问题,但它有必要要能到达最大和最小值)
,它的噪声规模是+-4,并且有一个巨细是8的中心点。
假设你不需求absfuzz和absflat,你能够把它们设置为0,这意味着它是肯定精确的并总是回来正中心方位(假设他有的话)。
1.4 BITS_TO_LONGS(), BIT_WORD(), BIT_MASK()
这3个宏来自bitops.h,有助于进行位域核算:
BITS_TO_LONGS(x) – 回来x位的位域数组需求多少个long类型来组成。
BIT_WORD(x) – 回来位域数组中第x位所对应的按long为单位的索引。
BIT_MASK(x) – 回来位x对应的long型的mask值。
1.5 The id* and name fields
dev->name字段应该要在驱动程序注册输入设备之前设置好。它是一个像'Generic button device'之类的对用户友爱的设备名字符串
id*字段包括了总线的ID(PCI,USB…),厂商ID和设备ID。总线IDs在input.h中界说。厂商和设备IDs在pci_ids.h,usb_ids.h和类似的头文件中界说。这些字段也应该在注册设备之前被设置好。
idtype字段能够被用作输入设备的专有信息。
这些id和name字段能够经过evdev接口传递到用户空间中。
1.6 keycode, keycodemax, keycodesize 字段
这3个字段用于需求键值映射的输入设备,keycode是一个用于把扫描码转换为输入体系键值的数组,keycodemax是这个数组的巨细,keycodesize则是该数组每个元素的巨细(以字节为单位)
用户空间能够经过相应的evdev接口,运用EVIOCGKEYCODE和EVIOCSKEYCODE ioctls来查询和修正当时的扫描码到键值的映射表。当设备填充了3个上述的字段,驱动程序能够依据kernel的默许完成来设置和查询键值映射表。
1.7 dev->getkeycode() and dev->setkeycode()
getkeycode()和setkeycode()回调答应驱动程序覆写由input中心代码供给的对keycode/keycodemax/keycodesize的默许映射机制,然后能够完成稀少的映射办法。
1.8 按键的autorepeat
… 很简略,它由input.c模块处理。硬件autorepeat没有被运用,由于许多设备不存在该功用,并且就算存在该功用,有时候也不正常(例如,Toshiba笔记本中的键盘)。要使能你的设备的autorepeat功用,只需设置dev->evbit中的EV_REP位即可,其它的工作都有输入子体系处理。
1.9 其它的工作types, 输出工作处理
现在停止,其它的工作types有:
EV_LED – 用作键盘的LEDs灯。
EV_SND – 用于键盘的蜂鸣器。
它们和键盘工作很类似,可是它们按另一个方向走动 – 从体系到输入设备驱动程序。假设你的驱动程序能够处理这些工作,有必要设置evbit中相应的位,并且要完成一个回调函数:
[cpp] view plain copy
button_dev->event = button_event;
int button_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
{
if (type == EV_SND && code == SND_BELL) {
outb(value, BUTTON_BELL);
return 0;
}
return -1;
}
这个回调能够在中止上下文或许BH上下文中被调用,所以它不能睡觉,不要处理太长的时刻。