Android系统移植之按键驱动篇


平台:MX53_QSB开发板

MX53_QSB开发板上一起有四个按键,分别为RESET,POWER,USER1,USER2。其中RESET为纯硬件复位按键,无须软件控制。POWER,USER1,USER2三个按键均需要程序控制。默认BSP包中将三个按钮全设置为上升和下降沿触发,当系统起来后,按下POWER键,进入睡眠状态,这时再按下POWER键唤醒时,系统系统被唤醒,但是一旦手松下,又触发了POWER键的中断,系统又睡下去了。在进入睡眠状态后,只有按USER1和USER2这两个键,才能正常唤醒。因此,这里有BUG需修复。

按键驱动有两个,一个为矩阵键盘驱动,路径为:

\drivers\input\keyboard\mxc_keyb.c

一个为GPIO接口的键盘驱动,路径为:

\drivers\input\keyboard\gpio_keys.c

前者用于多按键的情况,如果按键比较少,后者就可以了,一般情况下,Android系统只需几个按键就可以了,所以大多数情况下,都是使用的gpio_keys.c。下面我们将详细分析该驱动的工作流程。

在module_init函数中,在总线上注册名为gpio-keys的驱动,这时将夫在总线上查找是否存在同名的设备。系统初始化时,mx53_loco.c中,mxc_board_init函数已调用了按键初始化函数loco_add_device_buttons(),同时pdev的数据结构体中定义了按键的相关信息如下:

#define GPIO_BUTTON(gpio_num, ev_code, act_low,descr, wake)   \

{                                                                         \

         .gpio           = gpio_num,                                    \

         .type           = EV_KEY,                                     \

         .code          = ev_code,                             \

         .active_low = act_low,                              \

         .desc           = "btn " descr,                                 \

         .wakeup               = wake,                                           \

}

 

static struct gpio_keys_button loco_buttons[] = {

         GPIO_BUTTON(MX53_nONKEY,KEY_POWER, 1, "power", 0),

         GPIO_BUTTON(USER_UI1,KEY_BACK, 1, "back", 0),

         GPIO_BUTTON(USER_UI2,KEY_HOME, 1, "home", 0),

};

 

static struct gpio_keys_platform_dataloco_button_data = {

         .buttons      = loco_buttons,

         .nbuttons    = ARRAY_SIZE(loco_buttons),

};

可见,结构体定义了三个GPIO,分别为power,back以及home。注意GPIO_BUTTON函数中的实参,第一个为对应的GPIO,纯硬件特性,第二个为按键的键值,在linux/input.h中定义:

#defineKEY_POWER         116

#defineKEY_HOME           102

#defineKEY_BACK            158

第三个参数为1,表明按下去为1,抬起为0;第四个参数为按键名称描述,无关紧要;第5个参数为wakeup,看名称好像与休眠唤醒有关,实际测试修改为1后,没有发现有什么异常。这几个参数是后续添加新的按键,或者更改按键功能的关键。

再回到gpio-keys.c中,找到同名设备后,探测函数gpio_keys_probe得到执行,调用input_allocate_device函数创建一个input设备,再通过一个for循环,调用gpio_keys_setup_key和input_set_capability函数,设置上表中列出的三个IO口的中断函数以及按键功能。后面用到了sysfs_create_group函数创建了基于sys系统的文件属性组,具体可以在?sys/devices/platform/gpio-keys目录下找到gpio_keys_attr_group结构体中attrs组对应的gpio_keys_attrs结构体中的几个属性文件,gpio_keys_attrs结构体描述如下:

static struct attribute *gpio_keys_attrs[] = {

         &dev_attr_keys.attr,

         &dev_attr_switches.attr,

         &dev_attr_disabled_keys.attr,

         &dev_attr_disabled_switches.attr,

         NULL,

};

dev_attr_disabled_keys和dev_attr_disabled_switches在前面做了如下声明:

static DEVICE_ATTR(disabled_keys, S_IWUSR |S_IRUGO,

                      gpio_keys_show_disabled_keys,

                      gpio_keys_store_disabled_keys);

static DEVICE_ATTR(disabled_switches, S_IWUSR |S_IRUGO,

                      gpio_keys_show_disabled_switches,

                      gpio_keys_store_disabled_switches);

在android系统终端,我们可以进入该路径查看是否存在,以及他们的文件属性如下:


注意上面四个文件的读写属性。

可见,这里留有在系统中操作按键的后门,具体以后再分析。接下来,调用input_register_device函数向输入子系统注册input_dev,结束探测函数初始化。

探测函数的关键点在gpio_keys_setup_key函数中,相关代码如下:

static int __devinit gpio_keys_setup_key(structplatform_device *pdev,

                                                struct gpio_button_data *bdata,

                                                struct gpio_keys_button *button)

{

         char *desc= button->desc ? button->desc : "gpio_keys";//从loco_buttons数组中获得按键的描述名称

         structdevice *dev = &pdev->dev;

         unsignedlong irqflags;

         intirq, error;

 

         //传入参数: 过期时间,回调函数,上下文

         //当计时器过期时,回调函数gpio_keys_timer将得到运行

         setup_timer(&bdata->timer,gpio_keys_timer, (unsigned long)bdata);//初始化计时器

         INIT_WORK(&bdata->work,gpio_keys_work_func);

 

         error= gpio_request(button->gpio, desc);//请求使用GPIO

         if(error < 0)

         {

                   dev_err(dev,"failed to request GPIO %d, error %d\n",button->gpio, error);

                   gotofail2;

         }

 

         error= gpio_direction_input(button->gpio);//设置指定的GPIO为输入模式

         if(error < 0)

         {

                   dev_err(dev,"failed to configure direction for GPIO %d, error %d\n",

                            button->gpio,error);

                   gotofail3;

         }

 

         irq =gpio_to_irq(button->gpio);//获得GPIO对应的中断号

         if (irq< 0)

         {

                   error= irq;

                   dev_err(dev,"Unable to get irq number for GPIO %d, error %d\n",button->gpio,error);

                   gotofail3;

         }

 

         irqflags= IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;

         //irqflags= IRQF_TRIGGER_FALLING;//lqm changed.

         /*

          * If platform has specified that the buttoncan be disabled,

          * we don't want it to share the interruptline.

          */

         if(!button->can_disable)

                   irqflags|= IRQF_SHARED;

 

         error= request_irq(irq, gpio_keys_isr, irqflags, desc, bdata);//请求按键中断

     ……

}

该函数使用了带定时器延时的中断机制,用于按键去抖动。即中断的执行在定时器到来之后执行,这样能够有效的去除按键抖动。同时,中断使用工作队列的机制。

整个按键中断的工作调用有点复杂,下面逐步解析:

首先,上面函数中setup_timer函数初始化定时器,第二个实参为一个名为gpio_keys_timer的函数,一旦计时器过期,该函数将得到运行。setup_timer函数需和mod_timer函数配合使用,在按键中断的顶半部,即gpio_keys_isr函数,会判断debounce_interval是否为0,若为非0,则调用mod_timer函数延时debounce_interval ms,再触发定时器中断,即gpio_keys_timer函数得到执行。否则,直接调度工作队列,执行中断底半部。

回到gpio_keys_setup_key函数,在初始化完计时器后,再调用INIT_WORK函数初始化工作队列,初始化函数有一个实参函数gpio_keys_work_func,即一旦调度相应队列名,该函数将得到执行。

  • 1
  • 2
  • 3
  • 下一页

相关内容