4412按键驱动移植(按键对应Android系统的三个虚拟按键)

时间:2021-02-16 20:39:45

写此文章是为了记录移植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;
 }
 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;

 //进行输入设备注册 input_register_device
 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、修改驱动同目录下的KconfigMakefile

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 supportnew),选中后退出内核,编译后烧录到开发板进行测试。


6、驱动中定义的三个按键的键值对应Android系统中kl文件的键值


#define KEY_EINT10_BACK158//158Android系统中对应的值

#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开发平台移植按键的源码及说明,如以后有需要,可按照此流程进行按键移植