上一篇博文《input输入子系统框架分析》,尝试使用这种驱动模型来看一个按键驱动程序。
下面的程序是根据韦东山老师写的代码进行修改的,我的开发板是tq2440。
button.c文件:
#include <linux/module.h> #include <linux/version.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/sched.h> #include <linux/pm.h> #include <linux/sysctl.h> #include <linux/proc_fs.h> #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/input.h> #include <asm/gpio.h> #include <asm/io.h> #include <asm/arch/regs-gpio.h> struct pin_desc{ int irq; char *name; unsigned int pin; unsigned int key_val; }; struct pin_desc pins_desc[4] = { {IRQ_EINT1, "S1", S3C2410_GPF1, KEY_L}, {IRQ_EINT4, "S2", S3C2410_GPF4, KEY_S}, {IRQ_EINT2, "S3", S3C2410_GPG2, KEY_ENTER}, {IRQ_EINT0, "S4", S3C2410_GPG0, KEY_LEFTSHIFT}, }; static struct input_dev *buttons_dev; static struct pin_desc *irq_pd; static struct timer_list buttons_timer; /* 去抖动按键设计要点:在中断处理函数启动定时器 */ static irqreturn_t buttons_irq(int irq, void *dev_id) { /* 10ms后启动定时器 */ irq_pd = (struct pin_desc *)dev_id; mod_timer(&buttons_timer, jiffies+HZ/100); return IRQ_RETVAL(IRQ_HANDLED); } /* 定时器处理函数 */ static void buttons_timer_function(unsigned long data) { struct pin_desc * pindesc = irq_pd; unsigned int pinval; if (!pindesc) return; pinval = s3c2410_gpio_getpin(pindesc->pin); if (pinval) { /* 松开 : 最后一个参数: 0-松开, 1-按下 */ input_event(buttons_dev, EV_KEY, pindesc->key_val, 0);//向input核心层上报事件 input_sync(buttons_dev); //发送同步信号 } else { /* 按下 */ input_event(buttons_dev, EV_KEY, pindesc->key_val, 1); input_sync(buttons_dev); } } static int buttons_init(void) { int i; /* 1. 分配一个input_dev结构体 */ buttons_dev = input_allocate_device();; /* 2. 设置 */ /* 2.1 能产生哪类事件 */ set_bit(EV_KEY, buttons_dev->evbit); set_bit(EV_REP, buttons_dev->evbit); /* 2.2 能产生这类操作里的哪些事件: L,S,ENTER,LEFTSHIT */ set_bit(KEY_L, buttons_dev->keybit); set_bit(KEY_S, buttons_dev->keybit); set_bit(KEY_ENTER, buttons_dev->keybit); set_bit(KEY_LEFTSHIFT, buttons_dev->keybit); /* 3. 注册按键输入设备 */ input_register_device(buttons_dev); /* 4. 硬件相关的操作 */ init_timer(&buttons_timer);//初始化定时器timer_list结构体 buttons_timer.function = buttons_timer_function; //设置定时器响应函数 add_timer(&buttons_timer); //把定时器添加到内核定时器链表 for (i = 0; i < 4; i++) {//注册中断 request_irq(pins_desc[i].irq, buttons_irq, IRQT_BOTHEDGE, pins_desc[i].name, &pins_desc[i]); } return 0; } static void buttons_exit(void) { int i; for (i = 0; i < 4; i++) { free_irq(pins_desc[i].irq, &pins_desc[i]); } del_timer(&buttons_timer); input_unregister_device(buttons_dev); input_free_device(buttons_dev); } module_init(buttons_init); module_exit(buttons_exit); MODULE_LICENSE("GPL");
驱动程序内容比较少,结合注释也容易理解,不再另做解析。
编译好这个驱动模块之后,加载进内核,发现生成了/dev/event1
使用hexdump指令测试驱动程序:
(1)hexdump /dev/event1
(2)然后按下一次按键,会打印如下数据:
0000000 0bb2 0000 0e48 000c 0001 0026 0001 0000
0000010 0bb2 0000 0e54 000c 0000 0000 0000 0000
0000020 0bb2 0000 5815 000e 0001 0026 0000 0000
0000030 0bb2 0000 581f 000e 0000 0000 0000 0000
(地址) (秒) (微妙) (类) (code) (value)
小结:编写一个input驱动程序,需要我们完成哪些主要共作?
主要是完成设备驱动层的事情,事件处理层和核心层属于内核提供的稳定代码,正常只需要我们理解其中的工作原理,知道输入子系统的框架就好。
设备驱动层的工作在上一篇博文中第三点有小结。
时间: 2024-10-18 07:49:49