遥控器设备驱动程序源代码分析
1.源代码位置
\kernel\drivers\input\remotectl
2.源代码分析
2.1 程序头部
/*
* Driver for keys onGPIO lines capable of generating interrupts.
*
* Copyright 2005Phil Blundell
*
* This program isfree software; you can redistribute it and/or modify
* it under the termsof the GNU General Public License version 2 as
* published by theFree Software Foundation.
*/
/*
NEC遥控器命令格式:
8位地址和8位命令长度
为提高可靠性每次传输两遍地址(用户码)和命令(按键值)
通过脉冲串之间的时间间隔来实现信号的调制
38Khz载波
每位的周期为1.12ms或者2.25ms. 分别表示0和1.
9+4.5ms表示起始位.
起始码+用户码+命令码
*/
#include <linux/module.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 <linux/workqueue.h>
#include <linux/adc.h>
#include <asm/gpio.h>
#include <mach/remotectl.h>
#include <mach/iomux.h>
#include <linux/wakelock.h>
#include <linux/suspend.h>
#if 1
#define remotectl_dbg(bdata, format, arg...) \
dev_printk(KERN_INFO, &bdata->input->dev , format , ## arg)
#else
#define remotectl_dbg(bdata, format, arg...)
#endif
extern suspend_state_t get_suspend_state(void);
#ifdef CONFIG_RK30_KEYBOARD_LED_CTL
extern int rk29_keyboard_led_init(void);
extern int rk29_keyboard_led_suspend(void);
extern int rk29_keyboard_led_resume(void);
#endif
struct rkxx_remotectl_suspend_data{
int suspend_flag;
int cnt;
longscanTime[50];
};
struct rkxx_remote_key_table{
int scanCode;
int keyCode;
};
struct rkxx_remotectl_button {
int usercode;
int nbuttons;
structrkxx_remote_key_table *key_table;
};
2.2 遥控器驱动数据块
/* 遥控器驱动的核心结构体
里面包含用户码, 遥控器的扫描码等信息*/
struct rkxx_remotectl_drvdata {
int state;
int nbuttons;
int result;
unsigned longpre_time;
unsigned longcur_time;
long period;
int scanData;/*被扫描到的位值. 命令码为2字节*/
int count;
int keybdNum;
int keycode;
int press;/*当remotectl_do_something检测到有按键被按下时, 置为1*/
int pre_press;/*恒为0*/
struct input_dev*input;
struct timer_listtimer;
structtasklet_struct remote_tasklet;
struct wake_lockremotectl_wake_lock;
structrkxx_remotectl_suspend_data remotectl_suspend_data;
};
2.3 功能键值
//特殊功能键值定义
//193 //photo
//194 //video
//195 //music
//196 //IE
//197 //
//198
//199
//200
//183 //rorate_left
//184 //rorate_right
//185 //zoom out
//186 //zoom in
static struct rkxx_remote_key_tableremote_key_table_meiyu_202[] = {
{0xB0,KEY_REPLY},//ok = DPAD CENTER
{0xA2, KEY_BACK},
{0xD0, KEY_UP},
{0x70, KEY_DOWN},
{0x08, KEY_LEFT},
{0x88,KEY_RIGHT},
{0x42,KEY_HOME}, //home
{0xA8,KEY_VOLUMEUP},
{0x38,KEY_VOLUMEDOWN},
{0xE2,KEY_SEARCH}, //search
{0xB2,KEY_POWER}, //power off
{0xC2, KEY_MUTE}, //mute
{0xC8, KEY_MENU},
//media ctrl
{0x78, 0x190}, //play pause
{0xF8, 0x191}, //pre
{0x02, 0x192}, //next
//pic
{0xB8, 183}, //rorate left
{0x58, 184}, //rorate right
{0x68, 185}, //zoom out
{0x98, 186}, //zoom in
//mouse switch
{0xf0,388},
//display switch
{0x82, 0x175},
};
static struct rkxx_remote_key_table remote_key_table_df[] ={
{0xf8,KEY_REPLY},
{0xc0, KEY_BACK},
{0xf0, KEY_UP},
{0xd8, KEY_DOWN},
{0xd0, KEY_LEFT},
{0xe8,KEY_RIGHT},
{0x90,KEY_VOLUMEDOWN},
{0x60,KEY_VOLUMEUP},
{0x80,KEY_HOME}, //home
{0xe0, 183}, //rorate left
{0x10, 184}, //rorate right
{0x20, 185}, //zoom out
{0xa0, 186}, //zoom in
{0x70,KEY_MUTE}, //mute
{0x50,KEY_POWER}, //power off
{0x40,KEY_SEARCH}, //search
};
static struct rkxx_remote_key_table remote_key_table_1fe[]= {
{0x00, KEY_POWER},
{0x80, KEY_MUTE},
{0x20,KEY_1},
{0xc0,KEY_2},
{0x40,KEY_3},
{0xa0,KEY_4},
{0x60,KEY_5},
{0xe0,KEY_6},
{0x50,KEY_7},
{0x90,KEY_8},
{0x10,KEY_9},
{0xd0,KEY_PLAYPAUSE},
{0x30,KEY_0},
{0xb0,KEY_DELETE},
{0x08,KEY_MODE},
{0xf0,KEY_SEARCH},
{0x70,KEY_F1},
{0x88,KEY_VOLUMEUP},
{0xe8,KEY_VOLUMEDOWN},
{0x48,KEY_UP},
{0x18,KEY_DOWN},
{0x68,KEY_LEFT},
{0x28,KEY_RIGHT},
{0xa8,KEY_REPLY},
{0xd8,KEY_MENU},
{0x58,KEY_BACK},
{0xb8,KEY_HOME},
{0x38,KEY_F2},
{0x78,KEY_PREVIOUS},
{0xf8,KEY_NEXT},
{0xc8, 185}, //zoom out
{0x98, 186}, //zoom in
};
extern suspend_state_t get_suspend_state(void);
/* 三种类型的遥控器, 分别对应不同的用户码
用户码是用于区分设备的厂家的,
避免不同厂家设备遥控键值混淆*/
static struct rkxx_remotectl_button remotectl_button[] =
{
{
.usercode =0x202,
.nbuttons= 22,
.key_table =&remote_key_table_meiyu_202[0],
},
{
.usercode =0xdf,
.nbuttons= 16,
.key_table =&remote_key_table_df[0],
},
{
.usercode =0x1fe,
.nbuttons =32,
.key_table =&remote_key_table_1fe[0],
},
};
2.4 遥控器类型键值查询函数
/* 从遥控参数查找对应的遥控器类型*/
static int remotectl_keybdNum_lookup(structrkxx_remotectl_drvdata *ddata)
{
int i;
for (i = 0; i< sizeof(remotectl_button)/sizeof(struct rkxx_remotectl_button); i++){
if(remotectl_button[i].usercode == (ddata->scanData&0xFFFF)){
ddata->keybdNum = i;
return 1;
}
}
return 0;
}
/* 从遥控参数查找遥控键值*/
static int remotectl_keycode_lookup(structrkxx_remotectl_drvdata *ddata)
{
int i;
unsigned charkeyData = ((ddata->scanData >> 8) & 0xff);
for (i = 0; i< remotectl_button[ddata->keybdNum].nbuttons; i++){
if(remotectl_button[ddata->keybdNum].key_table[i].scanCode == keyData){
ddata->keycode = remotectl_button[ddata->keybdNum].key_table[i].keyCode;
return 1;
}
}
return 0;
}
2.5 遥控器驱动流程处理函数
/*遥控器的主要通信处理函数*/
static void remotectl_do_something(unsigned long data)
{
structrkxx_remotectl_drvdata *ddata = (struct rkxx_remotectl_drvdata *)data;
switch(ddata->state)
{
caseRMC_IDLE:
{
;
}
break;
/*如果当前状态是起始状态 */
caseRMC_PRELOAD:
{
/*起始码NEC为9+4.5ms*/
if((TIME_PRE_MIN < ddata->period) && (ddata->period <TIME_PRE_MAX)){
ddata->scanData = 0;
ddata->count = 0;
ddata->state = RMC_USERCODE;/*设置为准备接收用户码状态*/
}else{
ddata->state = RMC_PRELOAD;
}
ddata->pre_time = ddata->cur_time;
//mod_timer(&ddata->timer,jiffies + msecs_to_jiffies(130));
}
break;
/*如果当前状态是用户码状态
这里处理的数据为接收用户码*/
caseRMC_USERCODE:
{
ddata->scanData <<= 1;
ddata->count ++;
/*当前的时间间隔是否表明当前位为1?*/
if((TIME_BIT1_MIN < ddata->period) && (ddata->period <TIME_BIT1_MAX)){
ddata->scanData |= 0x01;
}
/*如果16位用户码接收完成
进一步根据用户码判断遥控器类型*/
if(ddata->count == 0x10){//16 bit user code
//printk("u=0x%x\n",((ddata->scanData)&0xFFFF));
if(remotectl_keybdNum_lookup(ddata)){/* 查找遥控器类型 */
ddata->state = RMC_GETDATA;/*遥控器类型已找到, 准备接收命令码*/
ddata->scanData = 0;
ddata->count = 0;
}else{ /*user code error 用户码没有找到, 则其后的命令也没有意义了, 回到接收起始码状态. */
ddata->state = RMC_PRELOAD;
}
}
}
break;
/*如果当前状态是接收命令码状态*/
caseRMC_GETDATA:
{
ddata->count ++;
ddata->scanData <<= 1;
/*当前的时间间隔是否表明当前位为1?*/
if((TIME_BIT1_MIN < ddata->period) && (ddata->period <TIME_BIT1_MAX)){
ddata->scanData |= 0x01;
}
/*如果16位命令码接收完成*/
if(ddata->count == 0x10){
//printk(KERN_ERR "d=%x\n",(ddata->scanData&0xFFFF));
/*命令码的高8位与低8位是反码关系, 用于验证命令码是否正确*/
if((ddata->scanData&0x0ff) == ((~ddata->scanData >>8)&0x0ff)){
#ifdefCONFIG_RK30_KEYBOARD_LED_CTL
rk29_keyboard_led_suspend();
#endif
/*查找键值*/
if (remotectl_keycode_lookup(ddata)){
ddata->press = 1;
/*区分是否待机状态, 对遥控器键值作出不同的响应
input_event之后就不管了, 交给input.c去进行逻辑处理*/
if (get_suspend_state()==0){
input_event(ddata->input, EV_KEY, ddata->keycode, 1);
input_sync(ddata->input);
}else if ((get_suspend_state())&&(ddata->keycode==KEY_POWER)){
input_event(ddata->input, EV_KEY, KEY_WAKEUP, 1);
input_sync(ddata->input);
}
//input_event(ddata->input, EV_KEY, ddata->keycode,ddata->press);
//input_sync(ddata->input);
ddata->state = RMC_SEQUENCE;
}else{
ddata->state = RMC_PRELOAD;
}
}else{
ddata->state = RMC_PRELOAD;
}
}
}
break;
caseRMC_SEQUENCE:{
//printk("S\n");
if((TIME_RPT_MIN < ddata->period) && (ddata->period <TIME_RPT_MAX)){
;
}else if((TIME_SEQ_MIN < ddata->period) && (ddata->period <TIME_SEQ_MAX)){
if (ddata->press == 1){
ddata->press = 3;
}elseif (ddata->press & 0x2){
ddata->press = 2;
//input_event(ddata->input, EV_KEY, ddata->keycode, 2);
//input_sync(ddata->input);
}
//mod_timer(&ddata->timer,jiffies + msecs_to_jiffies(130));
//ddata->state = RMC_PRELOAD;
}
}
break;
default:
break;
}
return;
}
#ifdef CONFIG_PM
void remotectl_wakeup(unsigned long _data)
{
structrkxx_remotectl_drvdata *ddata = (structrkxx_remotectl_drvdata*)_data;
long *time;
int i;
time =ddata->remotectl_suspend_data.scanTime;
/*如果是待机状态*/
if(get_suspend_state()){
static intcnt;
ddata->remotectl_suspend_data.suspend_flag = 0;
ddata->count = 0;
ddata->state = RMC_USERCODE;
ddata->scanData = 0;
/*如果是用户码/命令码, 则得到起始位置*/
for(i=0;i<ddata->remotectl_suspend_data.cnt;i++){
if(((TIME_BIT1_MIN<time[i])&&(TIME_BIT1_MAX>time[i]))||((TIME_BIT0_MIN<time[i])&&(TIME_BIT0_MAX>time[i]))){
cnt =i;
break;;
}
}
/*用户码/命令码之后的32个位都要处理*/
for(;i<cnt+32;i++){
ddata->scanData <<= 1;
ddata->count ++;
/*如果位值是1*/
if((TIME_BIT1_MIN < time[i]) && (time[i] < TIME_BIT1_MAX)){
ddata->scanData |= 0x01;
}
/*得到一个完整的用户码/命令码*/
if(ddata->count == 0x10){//16 bit user code
/*如果是用户码, 则查找遥控器类型*/
if(ddata->state == RMC_USERCODE){
// printk(KERN_ERR"d=%x\n",(ddata->scanData&0xFFFF));
if (remotectl_keybdNum_lookup(ddata)){
ddata->scanData = 0;
ddata->count = 0;
ddata->state = RMC_GETDATA;
}else{
ddata->state = RMC_PRELOAD;
}
}elseif (ddata->state == RMC_GETDATA){
/*如果是命令码, 则先反码校验数据是否正确, 然后查找遥控器键值*/
/*如果是待机键, 则唤醒*/
if ((ddata->scanData&0x0ff) == ((~ddata->scanData >>8)&0x0ff)){
// printk(KERN_ERR "d=%x\n",(ddata->scanData&0xFFFF));
if (remotectl_keycode_lookup(ddata)){
if (ddata->keycode==KEY_POWER){
input_event(ddata->input, EV_KEY, KEY_WAKEUP, 1);
input_sync(ddata->input);
input_event(ddata->input,EV_KEY, KEY_WAKEUP, 0);
input_sync(ddata->input);
}
/*回到接收起始码状态*/
ddata->state = RMC_PRELOAD;
}else{
ddata->state = RMC_PRELOAD;
}
}else{
ddata->state = RMC_PRELOAD;
}
}else{
ddata->state = RMC_PRELOAD;
}
}
}
}
memset(ddata->remotectl_suspend_data.scanTime,0,50*sizeof(long));
ddata->remotectl_suspend_data.cnt= 0;
ddata->state =RMC_PRELOAD;
}
#endif
2.6 按键弹起定时器
/*这个定时器似乎有问题啊,
因为前面检测到按键之后, 就将键值给了input_event了
这里的remotectl_timer再给一次, 相当于重复的键值了!!!!*/
/*这个函数应该是用来处理重复按键的, 但是这种处理方式有问题
或者应该判断ddata->press不为1的情况, 即在处理SEQUENCE的情况.
press的取值为1, 3, 2顺序.
但是input_event的最后一个参数值是0, 似乎是del_timer. */
/*
XX在报遥控器有重复按键的问题, 是否这里造成的?????
研究一下input_event的最后一个参数1, 和0的区别.
===
1是发送键值, 0是del_timer, 什么意思???移除定时器. 那remotectl_timer不就没有意义了???
*/
/*明白了, 原来传0是表示释放的意思. mod_timer就是表示多久之后自动释放.
remotectl_timer也是用于处理按键释放事件的. */
/*这是自动连击计时器吗??????????????????????????????????????????????????????????????????*/
static void remotectl_timer(unsigned long _data)
{
structrkxx_remotectl_drvdata *ddata = (structrkxx_remotectl_drvdata*)_data;
//printk("to\n");
#ifdefCONFIG_RK30_KEYBOARD_LED_CTL
rk29_keyboard_led_resume();
#endif
/*当有遥控器按键被按下时, press将被置为1*/
if(ddata->press != ddata->pre_press) {
ddata->pre_press = ddata->press = 0;
/*正常状态下, 发送遥控码*/
if(get_suspend_state()==0){
//input_event(ddata->input, EV_KEY, ddata->keycode, 1);
//input_sync(ddata->input);
input_event(ddata->input, EV_KEY, ddata->keycode, 0);
input_sync(ddata->input);
/*如果是待机状态, 且遥控码是电源键, 则开机*/
}else if((get_suspend_state())&&(ddata->keycode==KEY_POWER)){
//input_event(ddata->input, EV_KEY, KEY_WAKEUP, 1);
//input_sync(ddata->input);
input_event(ddata->input, EV_KEY, KEY_WAKEUP, 0);
input_sync(ddata->input);
}
}
/*PM是Power Management的意思? 电源管理? 如果是, 此处将是电源定时开机*/
#ifdef CONFIG_PM
remotectl_wakeup(_data);
#endif
ddata->state =RMC_PRELOAD;
}
2.7 遥控中断处理函数
/*一个中断就是一个位.
遥控器的电平每发生一次完整的变化, 产生一个中断
在这个中断里只负责判断这个电平中断所占用的时间
在remotectl_do_something中会根据这个时间判断位的值,
进而得到遥控器的键值.
*/
static irqreturn_t remotectl_isr(int irq, void *dev_id)
{
structrkxx_remotectl_drvdata *ddata = (structrkxx_remotectl_drvdata*)dev_id;
structtimeval ts;
ddata->pre_time = ddata->cur_time;
do_gettimeofday(&ts);
ddata->cur_time = ts.tv_usec;
if(ddata->cur_time && ddata->pre_time)
ddata->period = ddata->cur_time - ddata->pre_time;
tasklet_hi_schedule(&ddata->remote_tasklet);
/*在起始码时激活定时器. 自动连击计时器???*/
if((ddata->state==RMC_PRELOAD)||(ddata->state==RMC_SEQUENCE))
mod_timer(&ddata->timer,jiffies + msecs_to_jiffies(130));
#ifdef CONFIG_PM
//wake_lock_timeout(&ddata->remotectl_wake_lock, HZ);
/*待机状态下的遥控按键, 全部保存到数组中, 最多保存50个
每一个中断保存一次, 计数器增加一个. */
if((get_suspend_state())&&(ddata->remotectl_suspend_data.cnt<50))
ddata->remotectl_suspend_data.scanTime[ddata->remotectl_suspend_data.cnt++]= ddata->period;
#endif
returnIRQ_HANDLED;
}
2.8 遥控设备驱动初始化函数
/*初始化遥控器驱动程序*/
static int __devinit remotectl_probe(struct platform_device*pdev)
{
structRKxx_remotectl_platform_data *pdata = pdev->dev.platform_data;
structrkxx_remotectl_drvdata *ddata;
struct input_dev*input;
int i, j;
int irq;
int error = 0;
printk("++++++++remotectl_probe\n");
if(!pdata)
return-EINVAL;
#ifdefCONFIG_RK30_KEYBOARD_LED_CTL
rk29_keyboard_led_init();
#endif
ddata =kzalloc(sizeof(struct rkxx_remotectl_drvdata),GFP_KERNEL);
memset(ddata,0,sizeof(struct rkxx_remotectl_drvdata));
ddata->state =RMC_PRELOAD;
input =input_allocate_device();
if (!ddata ||!input) {
error =-ENOMEM;
goto fail0;
}
platform_set_drvdata(pdev, ddata);
input->name =pdev->name;
input->phys ="gpio-keys/input0";
input->dev.parent = &pdev->dev;
input->id.bustype = BUS_HOST;
input->id.vendor= 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
/* Enable auto repeatfeature of Linux input subsystem */
if (pdata->rep)
__set_bit(EV_REP,input->evbit);
ddata->nbuttons =pdata->nbuttons;
ddata->input =input;
wake_lock_init(&ddata->remotectl_wake_lock, WAKE_LOCK_SUSPEND,"rk29_remote");
if(pdata->set_iomux){
pdata->set_iomux();
}
error =gpio_request(pdata->gpio, "remotectl");
if (error < 0) {
printk("gpio-keys:failed to request GPIO %d,"
" error%d\n", pdata->gpio, error);
//goto fail1;
}
error =gpio_direction_input(pdata->gpio);
if (error < 0) {
pr_err("gpio-keys:failed to configure input"
"direction for GPIO %d, error %d\n",
pdata->gpio,error);
gpio_free(pdata->gpio);
//goto fail1;
}
irq =gpio_to_irq(pdata->gpio);
if (irq < 0) {
error = irq;
pr_err("gpio-keys:Unable to get irq number for GPIO %d, error %d\n",
pdata->gpio,error);
gpio_free(pdata->gpio);
goto fail1;
}
error =request_irq(irq, remotectl_isr, IRQF_TRIGGER_FALLING, "remotectl", ddata);
if (error) {
pr_err("gpio-remotectl:Unable to claim irq %d; error %d\n", irq, error);
gpio_free(pdata->gpio);
goto fail1;
}
setup_timer(&ddata->timer,remotectl_timer, (unsigned long)ddata);
tasklet_init(&ddata->remote_tasklet, remotectl_do_something,(unsigned long)ddata);
for(j=0;j<sizeof(remotectl_button)/sizeof(struct rkxx_remotectl_button);j++){
printk("remotectl probej=0x%x\n",j);
for (i = 0; i <remotectl_button[j].nbuttons; i++) {
unsigned inttype = EV_KEY;
input_set_capability(input,type, remotectl_button[j].key_table[i].keyCode);
}
}
error =input_register_device(input);
if (error) {
pr_err("gpio-keys:Unable to register input device, error: %d\n", error);
goto fail2;
}
input_set_capability(input, EV_KEY, KEY_WAKEUP);
device_init_wakeup(&pdev->dev,1);
return 0;
fail2:
pr_err("gpio-remotectl input_allocate_device fail\n");
input_free_device(input);
kfree(ddata);
fail1:
pr_err("gpio-remotectl gpio irq request fail\n");
free_irq(gpio_to_irq(pdata->gpio), ddata);
del_timer_sync(&ddata->timer);
tasklet_kill(&ddata->remote_tasklet);
gpio_free(pdata->gpio);
fail0:
pr_err("gpio-remotectl input_register_device fail\n");
platform_set_drvdata(pdev, NULL);
return error;
}
static int __devexit remotectl_remove(structplatform_device *pdev)
{
structRKxx_remotectl_platform_data *pdata = pdev->dev.platform_data;
struct rkxx_remotectl_drvdata*ddata = platform_get_drvdata(pdev);
struct input_dev*input = ddata->input;
int irq;
device_init_wakeup(&pdev->dev,0);
irq =gpio_to_irq(pdata->gpio);
free_irq(irq,ddata);
tasklet_kill(&ddata->remote_tasklet);
gpio_free(pdata->gpio);
input_unregister_device(input);
return 0;
}
#ifdef CONFIG_PM
static int remotectl_suspend(struct device *dev)
{
structplatform_device *pdev = to_platform_device(dev);
structRKxx_remotectl_platform_data *pdata = pdev->dev.platform_data;
structrkxx_remotectl_drvdata *ddata = platform_get_drvdata(pdev);
//ddata->remotectl_suspend_data.suspend_flag = 1;
ddata->remotectl_suspend_data.cnt = 0;
if(device_may_wakeup(&pdev->dev)) {
if(pdata->wakeup) {
int irq = gpio_to_irq(pdata->gpio);
enable_irq_wake(irq);
}
}
return 0;
}
static int remotectl_resume(struct device *dev)
{
structplatform_device *pdev = to_platform_device(dev);
structRKxx_remotectl_platform_data *pdata = pdev->dev.platform_data;
if (device_may_wakeup(&pdev->dev)) {
if(pdata->wakeup) {
int irq =gpio_to_irq(pdata->gpio);
disable_irq_wake(irq);
}
}
return 0;
}
static const struct dev_pm_ops remotectl_pm_ops = {
.suspend = remotectl_suspend,
.resume = remotectl_resume,
};
#endif
static struct platform_driver remotectl_device_driver = {
.probe = remotectl_probe,
.remove = __devexit_p(remotectl_remove),
.driver = {
.name = "rkxx-remotectl",
.owner = THIS_MODULE,
#ifdef CONFIG_PM
.pm =&remotectl_pm_ops,
#endif
},
};
static int remotectl_init(void)
{
printk(KERN_INFO"++++++++remotectl_init\n");
returnplatform_driver_register(&remotectl_device_driver);
}
static void remotectl_exit(void)
{
platform_driver_unregister(&remotectl_device_driver);
printk(KERN_INFO"++++++++remotectl_init\n");
}
module_init(remotectl_init);
module_exit(remotectl_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("rockchip");
MODULE_DESCRIPTION("Keyboard driver for CPUGPIOs");
MODULE_ALIAS("platform:gpio-keys1");
3.遗留问题
3.1自动连击计时器原理.
Kernel中有事件驱动程序设备驱动程序,这是两类不同的驱动程序。
事件驱动程序处理所有输入设备的具有共性的流程,设备驱动程序则针对硬件、中断、寄存器等进行相应的处理。一个面向上层,一个面向硬件。
连击计时是由事件驱动程序自动处理的.
就是说, 如果在指定时间内, 比如250毫秒内, 如果事件驱动程序没有收到input_event(, , 0)消息, 则事件驱动程序自动生成连击事件.
如果在250毫秒内, 设备驱动程序(RKXX_remotectl.c)发送了input_event(, , 0)消息, 则事件驱动将调用input_stop_autorepeat结束自动连击生成.
自动连击生成函数是由事件驱动程序创建的, 函数名称是input_repeat_key. 与设备驱动程序无关.
出错可能与设备驱动程序(RKXX_remotectl.c)没有及时发出input_event(, , 0)消息有关系.