说明:之前关于platform总线按键驱动的分析,几天返回去看时发现排版有很多问题,现在用Markdown重新弄一下,方便阅读。下面是原文:
驱动做了三个多星期了,从helloworld到LED再到PlatformLED,现在终于到了按键驱动,对于驱动的理解深刻了不少,从完全看不懂到现在能够独立分析,进步很大,今天完成了按键驱动,做一个总结性的分享,给后面的自己看!加油!
按键驱动:
与之前不同,这次将按键驱动按照platform总线的理解,设备链表和驱动链表,做成了两个模块,一个kbd_driver.c 一个kbd_device.c 侧重点在理解按键的消抖和中断
中断:我个人理解为你正在看电影,有人叫你出去,你就得暂停出去处理,然后才能回来继续看电影,这就是一个生活中的中断。在开发板上,你也可以带入的理解。
消抖:当你按下按键时,会有微小的抖动,硬件很难消除,我们通过延时来达到消抖的目的。
上代码分析代码:
kbd_driver.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <mach/hardware.h>
#include <asm/gpio.h>
#include <asm/irq.h>
#include <linux/slab.h>
#include <mach/regs-gpio.h>
#include "kbd_driver.h"
/* 1HZ=100*jiffies 1*jiffies=10ms => 1HZ=100*10ms = 1s 这是在设置时钟/
#define CANCEL_DITHERING_DELAY (HZ/50) /* Remove button push down dithering timer delay 20ms 延时 */
typedef struct s3c_kbd_s
{
struct timer_list *timers; /* every key get a cancel dithering timer 消抖时间*/
struct input_dev *input_dev;
s3c_kbd_platform_data_t *pdata;
} s3c_kbd_t; /*--- end of struct s3c_kbd_s ---*/
s3c_kbd_t *s3c_kbd = NULL;
static irqreturn_t s3c_kbd_intterupt(int irq, void *dev_id) //见139行,一旦发生中断后,将中断号传过来
{
int i;
int found = 0;
struct platform_device *pdev = dev_id;
s3c_kbd_t *s3c_kbd = NULL;
s3c_kbd = platform_get_drvdata(pdev);
for(i=0; i<s3c_kbd->pdata->nkeys; i++) //寻找中断号
{
if(irq == s3c_kbd->pdata->keys[i].nIRQ)
{
found = 1;
break;
}
}
if(!found) /* An ERROR interrupt */
return IRQ_NONE;
mod_timer(&s3c_kbd->timers[i], jiffies+CANCEL_DITHERING_DELAY); //消抖定时器,延时 jiffies是当前时间由内核维护
//中断的处理涉及到上半部和下半部,上半部进行响应然后离开,下半部例如定时器会在这里执行,提高效率。
return IRQ_HANDLED;
}
static void cancel_dithering_timer_handler(unsigned long data)//消抖定时器处理方式:当62行延时结束后就来到这里调用这个函数,看看是否按键按下,这样就利用timer(定时器)来消抖
{
int which =(int)data;
unsigned int pinval;
pinval = s3c2410_gpio_getpin(s3c_kbd->pdata->keys[which].gpio); //获取按键引脚电平
if( pinval )
{
//printk("s3c_kbd key[%d] code[%d] released\n", which, s3c_kbd->pdata->keys[which].code);
input_event(s3c_kbd->input_dev, EV_KEY, s3c_kbd->pdata->keys[which].code, 0);
}
else
{
//printk("s3c_kbd key[%d] code[%d] pressed\n", which, s3c_kbd->pdata->keys[which].code);
input_event(s3c_kbd->input_dev, EV_KEY, s3c_kbd->pdata->keys[which].code, 1);
}
input_sync(s3c_kbd->input_dev);
}
static int s3c_kbd_probe(struct platform_device *pdev) //probe()函数传参调用platform_device相应的设备信息,在总线上device和driver name匹配时调用
{
int i = 0;
int rv = -ENOMEM;/*
在内核当中ENOMEM这个宏是这样定义的,表示内存不足
#define ENOMEM 0x05
Description:
ENOMEM - no memory can be allocated by a function in the library. Note that malloc, calloc, and realloc do not set errno to ENOMEM on failure, but other l