- UID
- 1029342
- 性别
- 男
|
2 输入设备驱动的简单案例
在Linux内核文档的documentation/input下,有一个input-programming.txt文件,讲解了编写输入设备驱动程序的核心步骤。
提供的案例代码描述了一个button设备,产生的事件通过BUTTON_PORT引脚获取,当有按下/释放发生时,BUTTON_IRQ被触发,以下是驱动的源代码:
#include #include #include #include #include static struct input_dev *button_dev; static void button_interrupt(int irq, void*dummy, struct pt_regs *fp) { input_report_key(button_dev, BTN_1, inb(BUTTON_PORT) & 1); input_sync(button_dev); } 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(EV_KEY); button_dev->keybit[LONG(BTN_0)] = BIT(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);
编写基于输入子系统的设备驱动程序需要包含,因为它包含了输入子系统的接口和所有的宏定义,这些内容在编写输入设备驱动程序时需要用到。
button_init函数说明:
当模块加载(insmod)或内核引导过程中,button_init函数会被调用。首先做的工作是获取能够正确控制硬件设备的硬件资源(例如内存、IO内存、中断和DMA),在代码中BUTTON_IRQ作为BUTTON设备的中断资源,通过request_irq()函数被申请注册。当有按键按下/释放时,调用button_interrupt()中断处理函数获取按键值BUTTON_PORT(BUTTON设备的I/O资源)。
那么输入子系统怎么能够知道这个设备为输入设备呢?通过第8行为设备定义一个用于描述一个输入设备对象。
static struct input_dev *button_dev;
定义了button_dev之后,如何通知输入子系统有新的输入设备了呢?或者说如何把一个新的输入设备加入到输入子系统中呢?可以通过输入子系统核心层input.c中提供的函数分配一个输入设备,在代码的第25行。
button_dev= input_allocate_device();
有了输入设备的描述,当事件产生时,输入子系统怎么能够知道设备产生的事件类型呢?通过32和33行的代码。
button_dev->evbit[0]= BIT(EV_KEY); button_dev->keybit[LONG(BTN_0)]= BIT(BTN_0);
其中evbit和keybit成员分别代表设备产生的事件类型和上报的按键值。其中输入子系统的一些位操作NBITS、BIT、LONG经常被用到:
#defineNBITS(x) (((x)/BITS_PER_LONG)+1) //通过位x获取数组的长度 #defineBIT(x) (1UL<<((x)%BITS_PER_LONG)) //返回位x在数组中的位域 #defineLONG(x) ((x)/BITS_PER_LONG) //返回位x的索引
以上的工作做完之后,即可注册为输入设备了,代码的35行。
input_register_device(button_dev);
这个函数把button_dev输入设备挂入输入设备链表中,并且通知事件处理层调用connect函数完成设备和事件处理的绑定,当用户打开设备时,便能够调用到相应的事件处理接口获得硬件上报的数据了。input_register_device()函数是会睡眠的函数,因此不能够在中断上下文和持有自旋锁的代码中调用。
当我们把上面的工作做完之后,设备驱动中唯一值得关注的就是button_interrupt()中断处理函数了。当按键动作发生,button_interrupt()函数被调用,完成事件的上报由其中的两条语句完成。
input_report_key(button_dev, BTN_1, inb(BUTTON_PORT) & 1); input_sync(button_dev);
其中input_report_key上报了这是一个按键事件,且它的值为inb(BUTTON_PORT) & 1,由于案例代码只产生一个按键的值,因此input_sync()在这里不起关键作用。但如果是一个触摸屏,即有x坐标和y坐标,则需要通过input_sync()函数把x和y坐标完整地传递给输入子系统。
用于测试的应用层代码: |
|