学习目的:
- 熟悉输入子系统下按键驱动程序编写
在分析输入子系统框架时,我们已经知道内核对不同类型的输入设备已经抽象出了不同的handler进行处理,device层实现纯硬件的操作,我们可以根据所实现驱动的功能对device层进行设计,主要是内容是当检测有效输入发送,调用input_event函数向handler层上报结果即可。device部分驱动程序设计主要分为以下几个步骤:
① 初始化硬件,注册input_dev设备
② 有效按键按下,向handler上报按键值
现在我们开始使用内核的输入子系统框架,将自己编写驱动程序融入其中,编写更通用的按键驱动程序。我们编写的驱动程序实现将开发板上的4个按键分别映射成键盘上不同键值,代表键盘上l、s、shift、enter值。
1、驱动入口函数
static int button_drv_init(void) { unsigned int i = 0; init_timer(&button_timer);-------------------------->① button_timer.expires = 0; button_timer.function = button_timer_handler; add_timer(&button_timer); /* 分配一个input_dev结构体 */ input = input_allocate_device();-------------------->② /* 产生哪一类事件 */ set_bit(EV_KEY, input->evbit); /* 产生该类事件的那些事件 */ set_bit(KEY_L, input->keybit); set_bit(KEY_S, input->keybit); set_bit(KEY_ENTER, input->keybit); set_bit(KEY_LEFTSHIFT, input->keybit); gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);------------->③ gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16); gpfdat = gpfcon + 1; gpgdat = gpgcon + 1; *gpfcon &= ~((0x3<<(0*2)) | (0x3<<(2*2))); *gpgcon &= ~((0x3<<(3*2)) | (0x3<<(11*2))); /* 注册中断处理函数 */ for(i = 0; i < BUTTON_NUMS; i++)---------------------->④ request_irq(btn_desc[i].irq_type, button_irq_handle, btn_desc[i].flags, btn_desc[i].name, &btn_desc[i]); /* 注册input_dev结构体 */ input_register_device(input);------------------------->⑤ return 0; }
① 初始化定时器,用于按键消抖获取稳定按键值使用
② 分配一个input_dev结构体,设置当前device能产生按键类事件,以及产生该类事件的那些按键值
③ 硬件相关操作:芯片相关gpio寄存器地址映射,设置连接按键的GPIO引脚为输入模式
④ 注册中断服务程序,设置中断触发方式以及中断发送时的中断服务函数
⑤ 注册input_dev结构体,找到内核中所有匹配的handler,并建立dev和handler直接连接
2、中断服务程序
static irqreturn_t button_irq_handle(int irq, void *dev_id) { pdesc = (struct button_desc *)dev_id; mod_timer(&button_timer, jiffies+HZ/100); return IRQ_RETVAL(IRQ_HANDLED); }
按键按下时,由于按键内部机械弹片抖动引起GIPO口电平跳变进入中断服务程序,此时读取按键值为非有效值。为按键消抖,设置消抖时间,消抖时间到时在定时器的超时处理函数中获取有效按键值
3、定时器超时处理程序
void button_timer_handler(unsigned long data)
{
unsigned char pin_val;
unsigned int key_sts;
if(pdesc != NULL)
{
pin_val = gpio_get_value(pdesc->pin);---------------------------->①
if(pin_val == 1)
{
/* 1-按下 */
key_sts = 1;
}
else
{
/* 0-松开 */
key_sts = 0;
}
input_event(input, EV_KEY, pdesc->key_val, key_sts);------------>②
input_sync(input);---------------------------------------------->③
}
}
① 读取触发中断的按键的GPIO的状态,确定按键是按下还是松开
② 调用input_event上报事件,input_event最终会调用handler中的event函数,event函数中将有效输入传送到handler中的相应存储区域,并唤醒因无数据而读取进入休眠的应用程序
③ 当提交输入设备产生的输入事件之后,需要调用此函数来通知输入子系统,以处理设备产生的完整事件
4、驱动程序测试
加载驱动程序,/dev目录下多出event1设备节点,该节点是device和内核中的handler匹配成功,调用handler中的connect函数input_class类下创建的设备。udev机制会使用这些信息在dev目录中创建相关节点
执行exec 0</dev/tty1命令,重定向shell程序的标准输入来自于/dev/tty1,这样按键产生的ls命令就能在中断显示了
完整驱动程序
#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/irq.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/io.h> #include <plat/gpio-fns.h> #include <mach/gpio-nrs.h> #include <linux/interrupt.h> #include <linux/wait.h> #include <linux/sched.h> #include <linux/device.h> #include <linux/gpio.h> #include <linux/poll.h> #include <linux/input.h> #define BUTTON_NUMS 4 #define IRQT_BOTHEDGE IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING struct button_desc { int pin; int irq_type; unsigned long flags; char *name; int key_val; }; static volatile unsigned long *gpfcon = NULL; static volatile unsigned long *gpgcon = NULL; static volatile unsigned long *gpfdat = NULL; static volatile unsigned long *gpgdat = NULL; static struct timer_list button_timer; static struct input_dev *input; struct button_desc *pdesc = NULL; static struct button_desc btn_desc[BUTTON_NUMS] = { {S3C2410_GPF(0), IRQ_EINT0, IRQT_BOTHEDGE, "S2", KEY_L}, {S3C2410_GPF(2), IRQ_EINT2, IRQT_BOTHEDGE, "S3", KEY_S}, {S3C2410_GPG(3), IRQ_EINT11, IRQT_BOTHEDGE, "S4", KEY_ENTER}, {S3C2410_GPG(11), IRQ_EINT19, IRQT_BOTHEDGE, "S5", KEY_LEFTSHIFT}, }; void button_timer_handler(unsigned long data) { unsigned char pin_val; unsigned int key_sts; if(pdesc != NULL) { pin_val = gpio_get_value(pdesc->pin); if(pin_val == 1) { /* 1-按下 */ key_sts = 1; } else { /* 0-松开 */ key_sts = 0; } input_event(input, EV_KEY, pdesc->key_val, key_sts); input_sync(input); } } static irqreturn_t button_irq_handle(int irq, void *dev_id) { pdesc = (struct button_desc *)dev_id; mod_timer(&button_timer, jiffies+HZ/100); return IRQ_RETVAL(IRQ_HANDLED); } static int button_drv_init(void) { unsigned int i = 0; init_timer(&button_timer); button_timer.expires = 0; button_timer.function = button_timer_handler; add_timer(&button_timer); /* 分配一个input_dev结构体 */ input = input_allocate_device(); /* 产生哪一类事件 */ set_bit(EV_KEY, input->evbit); /* 产生该类事件的那些事件 */ set_bit(KEY_L, input->keybit); set_bit(KEY_S, input->keybit); set_bit(KEY_ENTER, input->keybit); set_bit(KEY_LEFTSHIFT, input->keybit); gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16); gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16); gpfdat = gpfcon + 1; gpgdat = gpgcon + 1; *gpfcon &= ~((0x3<<(0*2)) | (0x3<<(2*2))); *gpgcon &= ~((0x3<<(3*2)) | (0x3<<(11*2))); /* 注册中断处理函数 */ for(i = 0; i < BUTTON_NUMS; i++) request_irq(btn_desc[i].irq_type, button_irq_handle, btn_desc[i].flags, btn_desc[i].name, &btn_desc[i]); /* 注册input_dev结构体 */ input_register_device(input); return 0; } static void button_drv_exit(void) { unsigned int i = 0; del_timer(&button_timer); for(i = 0; i < BUTTON_NUMS; i++) free_irq(btn_desc[i].irq_type, &btn_desc[i]); input_unregister_device(input); input_free_device(input); iounmap(gpfcon); iounmap(gpgcon); } module_init(button_drv_init); module_exit(button_drv_exit); MODULE_LICENSE("GPL");