写此文章是为了记录移植4412按键驱动的过程,防止以后忘记,可进行查看
开发平台:DMATEK PAD-4412
内核:Linux3.2.0
系统:Android4.0
作者:lyp461340781
1、使用3个外部中断按键模拟Android中三个虚拟按键,这些按键都是低电平触发。三个按键分别是EINT10、EINT12、EINT13
2、编写驱动程序dma4412-keypad.c,代码如下:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/clk.h>
#include <linux/slab.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <mach/hardware.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <asm/gpio.h>
#include <linux/switch.h>
#include <mach/gpio.h>
#include <mach/regs-gpio.h>
#include <mach/regs-clock.h>
#include <asm/uaccess.h>
#include <mach/gpio-exynos4.h>
#include <plat/gpio-cfg.h>
#define KEY_EINT10_BACK 158 //158为Android系统中对应的值
#define KEY_EINT13_HOME 172//102
#define KEY_EINT12_MENU 127//139//59 //有三个对应值:59/139/229 127
#define MAX_BUTTON_CNT 3
static int g_keypad_keycode[] = {KEY_EINT10_BACK, KEY_EINT13_HOME, KEY_EINT12_MENU};
struct button_desc {
int gpio;
int key_flg;
char *name;
};
static struct button_desc buttons[MAX_BUTTON_CNT] = {
{ EXYNOS4_GPX1(2), 1, "KEY4" }, //EINT10 SW4
{ EXYNOS4_GPX1(5), 1, "KEY5" }, //EINT13 SW5
{ EXYNOS4_GPX1(4), 1, "KEY6" }, //EINT12 SW6
};
struct gpio_switch_data {
struct switch_dev sdev;
unsigned gpio;
const char *name_on;
const char *name_off;
const char *state_on;
const char *state_off;
int irq;
struct work_struct work;
};
static struct gpio_switch_data *switch_data;
static struct input_dev *g_input_dev;
static void __iomem *g_reg_keypad_base;
static void __iomem *g_reg_syscon_base;
static struct timer_list g_keypad_timer;
#define MY_KEY_DOWN 1
#define MY_KEY_UP 2
static irqreturn_t button_interrupt(int irq, void *dev_id)
{
g_keypad_timer.expires = jiffies + (HZ*10/1000);
mod_timer(&g_keypad_timer, g_keypad_timer.expires);
return IRQ_HANDLED;
}
static void keypad_timer_handler(unsigned long data)
{
int i;
bool keydown;
bool keyup;
unsigned tmp;
printk("keypad_timer_handler ########## \n");
//tmp = gpio_get_value(bdata->gpio);
for(i=0;i<MAX_BUTTON_CNT;i++)
{
tmp = gpio_get_value(buttons[i].gpio);
if(tmp != buttons[i].key_flg)
{
if(tmp) //通过IO口的高低电平来确定按键的按下与否
{
//按键值上报到用户空间,表示按键抬起
input_report_key(g_input_dev, g_keypad_keycode[i], 0);
printk("s3c-button back key up!\n");
printk("up_botton=%d\n",g_keypad_keycode[i]);
}
else
{
//表示按键按下
input_report_key(g_input_dev, g_keypad_keycode[i], 1);
printk("s3c-button back key down!!\n");
printk("down_botton=%d\n",g_keypad_keycode[i]);
}
buttons[i].key_flg= tmp;
//同步一个按键事件,表示一个按键事件结束
input_sync(g_input_dev);
}
}
}
static int __init keypad_probe(struct platform_device *pdev)
{
int irq;
int i;
int err = 0;
printk("keypad_probe ++++ \n");
for(i=0; i<MAX_BUTTON_CNT; i++)
{
gpio_request(buttons[i].gpio, "s3c-button");//申请一个按键IO
s3c_gpio_setpull(buttons[i].gpio, S3C_GPIO_PULL_UP);//设置IO为上拉
gpio_direction_input(buttons[i].gpio);//设置IO口为输入模式
}
init_timer(&g_keypad_timer);
g_keypad_timer.function = keypad_timer_handler;
//g_keypad_timer.expires = (HZ*10/1000);
g_keypad_timer.data = 0;
//add_timer(&g_keypad_timer);
//申请IO口中断
for (i = 0; i < ARRAY_SIZE(buttons); i++) {
if (!buttons[i].gpio)
continue;
//setup_timer(&buttons[i].timer, tiny4412_buttons_timer,
//(unsigned long)&buttons[i]);
//此处需将邋IO口设置双边沿触发IRQ_TYPE_EDGE_BOTH,这样keypad_timer_handler中的程序才可正常运行
irq = gpio_to_irq(buttons[i].gpio);
err = request_irq(irq, button_interrupt, IRQ_TYPE_EDGE_BOTH,
buttons[i].name, (void *)&buttons[i]);
if (err)
break;
}
if (err) {
i--;
for (; i >= 0; i--) {
if (!buttons[i].gpio)
continue;
irq = gpio_to_irq(buttons[i].gpio);
disable_irq(irq);
free_irq(irq, (void *)&buttons[i]);
del_timer_sync(&g_keypad_timer);
//del_timer_sync(&buttons[i].timer);
}
return -EINVAL;
}
//此处把必须进行input_register_device注册,不知道为什么,可能这样Android系统才能识别到此按键
g_input_dev = input_allocate_device();
if(NULL == g_input_dev)
{
printk("KeyPad: input_allocate_device fail !!!!! \n");
return -1;
}
set_bit(EV_KEY, g_input_dev->evbit);
for (i = 0; i < (sizeof(g_keypad_keycode)/sizeof(g_keypad_keycode[0])); i++)
{
set_bit(g_keypad_keycode[i] & KEY_MAX, g_input_dev->keybit);
}
g_input_dev->name = "dmatek keypad";
g_input_dev->phys = "dmatek-keypad";
g_input_dev->id.bustype = BUS_HOST;
g_input_dev->id.vendor = 0x0001;
g_input_dev->id.product = 0x0001;
g_input_dev->id.version = 0x0001;
g_input_dev->keycode = g_keypad_keycode;
if(0 != input_register_device(g_input_dev))
{
printk("Unable to register dmatek-keypad input device!!!\n");
return -1;
}
#endif
printk("keypad_probe ---- \n");
return 0;
}
static int keypad_remove(struct platform_device *pdev)
{
printk("keypad_remove ++++++ \r\n ");
return 0;
}
static int keypad_suspend(struct platform_device *dev, pm_message_t state)
{
// printk("keypad_suspend ++++++ \r\n ");
return 0;
}
static int keypad_resume(struct platform_device *dev)
{
return 0;
}
static struct platform_driver dma4412_keypad_driver = {
.probe = keypad_probe,
.remove = keypad_remove,
.suspend = keypad_suspend,
.resume = keypad_resume,
.driver = {
.owner = THIS_MODULE,
.name = "dma4412-keypad",
},
};
/*//定义一个平台设备,平台设备和平台驱动结构体中的名字必须一样,才能保证probe函数调用成功
static struct platform_device dma4412_keypad_driver = {
.name = "dma4412-keypad",
.id = -1,
};*/
static int __init dma4412_keypad_init(void)
{
int ret;
printk("dma4412_keypad_driver init ++++ \n");
ret = platform_driver_register(&dma4412_keypad_driver);
if (!ret)
pr_info(" platform_driver_register dam4412 Keypad Driver OK\n");
return ret;
}
static void __exit dma4412_keypad_exit(void)
{
platform_driver_unregister(&dma4412_keypad_driver);
}
module_init(dma4412_keypad_init);
module_exit(dma4412_keypad_exit);
MODULE_AUTHOR("lyp");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("KeyPad interface for DMATEK dma4412");
3、修改驱动同目录下的Kconfig和Makefile
Kconfig文件修改:在文件末添加如下内容
config KEYBOARD_DMA4412
tristate "Dma4412 Keypad support"
help
Say Y here to enable the keypad on evaluation board
based on DMA4412.
To compile this driver as a module, choose M here: the
module will be called dma4412_keypad.
Makefile文件修改:在文件末尾添加如下内容
obj-$(CONFIG_KEYBOARD_DMA4412)+= dma4412-keypad.o
4、在kernel\arch\arm\mach-exynos\mach-dma4412.c中添加对我们驱动的支持
#ifdef CONFIG_KEYBOARD_DMA4412
static struct resourcedma4412_keypad_resource[] = {
};
struct platform_devicedma4412_device_keyboard = {
.name= "dma4412-keypad",
.id = -1,
.num_resources= ARRAY_SIZE(dma4412_keypad_resource),
.resource = dma4412_keypad_resource,
};
#endif
//同时要添加下面的内容,与上面的一起添加
#ifdefCONFIG_KEYBOARD_DMA4412//与Makefile文件中一致
&dma4412_device_keyboard,
#endif
5、添加完成后修改内核配置,make menuconfig进入配置界面,进入Device Drivers->Input device support->Keyboards后会在末尾显示Dma4412 keypad support(new),选中后退出内核,编译后烧录到开发板进行测试。
6、驱动中定义的三个按键的键值对应Android系统中kl文件的键值
#define KEY_EINT10_BACK158//158为Android系统中对应的值
#define KEY_EINT13_HOME172//102
#define KEY_EINT12_MENU127//139//59//有三个对应值:59/139/229
上面定义的值可在开发板中进入system/usr/keylayout/目录查看kl文件,一般会调用qwerty.kl文件,如果系统没有设置调用哪个kl文件,那会默认调用Generic.kl(此说法网上看到的,我修改对应的键值后是正确的,但还有待进一步确认正确性)。
如果对应按键功能不正确,可在不同的kl文件中取对应的按键键值进行测试,便可验证调用的kl文件。(此办法为笨办法,有待确认如何更好查找)
以上为DMATEK 4412开发平台移植按键的源码及说明,如以后有需要,可按照此流程进行按键移植