android 4.4 按键分析三 -- 键盘添加

来源:互联网 发布:金蝶软件好用吗 编辑:程序博客网 时间:2024/05/21 04:42

5.1         KeyPad

5.1.1         基本介绍

这部分简单介绍Keypad的基本知识。(图片上传不了,后续再补)

对于输入设备,

一般支持的API功能如下,

分配/释放一个输入设备:

struct input_dev *input_allocate_device(void);

void input_free_device(struct input_dev *dev);

注册/注销输入设备:

int __must_check input_register_device(struct input_dev *);

void input_unregister_device(struct input_dev *);

报告输入事件:    

void  input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);/* 报告指定type、code的输入事件 */

void  input_report_key(struct input_dev *dev, unsigned int code, int value);/* 报告键值 */

void input_report_rel(struct input_dev *dev, unsigned int code, int value);/* 报告相对坐标 */

void input_report_abs(struct input_dev *dev, unsigned int code, int value);/* 报告绝对坐标 */

void input_sync(struct input_dev *dev);/* 报告同步事件 */

 

Input驱动编写步骤

1.分配一个输入设备;

2.注册一个输入设备;

3.驱动支持什么事件;

Set_bit告诉inout子系统它支持哪些事件
Set_bit(EV_KEY,button_dev.evbit)
Struct input_dev中有两个成员,一个是evbit;一个是keybit.分别用来表示设备所支持的事件类型和按键类型。

4.驱动事件报告;

5.释放和注销设备;

 

 

 

 

5.1.2         编译相关

对于input子系统的编译处理,在\kernel\drivers的makefile里面我们可以看到,它会编译对应的input目录下的makefile,

 

对应键盘,在\kernel\drivers\input\keyboard目录下,定义了各种键盘实现,部分实例如下,

 

系统如何选择合适的驱动文件,在本目录下的makefile里面我们可以看到,如果系统配置了对应的键盘,则编译就添加对应文件,进行支持。

 

对于手机系统来讲,一般是支持GPIO键盘的,如MTK 6572平台的一款设计,硬件原理图设计如下,使用了系统的KROW0-KROW1,KCOL0-KCOL1,模拟出4个按键,提供诸如侧键等物理按键(本来芯片是支持9个按键的,KROW2和KCOL2被JTAG借用了)。

 

所以我们接着分析CONFIG_KEYBOARD_GPIO是如何开启来支持GPIO键盘的。在\kernel\arch\arm\configs目录下,定义了各种ARM核的相关配置,可以看到GPIO是在这里配置是否支持的。

 

至此,就知道了一个支持的键盘文件是如何参与编译的,并延伸的了解到,如果要新增加一个新的键盘文件,我们该如何处理。

 

5.1.3         代码分析

和Linux通用的驱动处理一样,GPIO的驱动编写也有固定的格式,简单介绍如下,

 

1)编写基本的驱动函数:

 static struct platform_driver gpio_keys_device_driver = {

.probe        = gpio_keys_probe,

.remove       = __devexit_p(gpio_keys_remove),

.driver       = {

      .name    = "gpio-keys",

      .owner   = THIS_MODULE,

      .pm  = &gpio_keys_pm_ops,

      .of_match_table = gpio_keys_of_match,

}

};

根据平台编写格式,给每个设备要编写类似上面的驱动节点,并完成节点里的函数实现。

 

2)向平台注册:

static int __init gpio_keys_init(void)

{

return platform_driver_register(&gpio_keys_device_driver);

}

 

static void __exit gpio_keys_exit(void)

{

platform_driver_unregister(&gpio_keys_device_driver);

}

 

late_initcall(gpio_keys_init);

module_exit(gpio_keys_exit);

在驱动编写中,一般在文件最后写上类似下面的代码,完成驱动模块的平台注册。

module_init(gpio_keys_init);

 module_exit(gpio_keys_exit);

当模块加载(insmod)或内核引导过程中,gpio_keys_init函数会被调用。首先做的工作是获取能够正确控制硬件设备的硬件资源(例如内存、IO内存、中断和DMA);在系统退出时,gpio_keys_exit会被调用。

 

 

对于gpio_keys_probe,它是一个关键的函数,用来检测按键,说明如下,

 static int __devinit gpio_keys_probe(struct platform_device *pdev)

{

const struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;

struct gpio_keys_drvdata *ddata;

struct device *dev = &pdev->dev;

struct gpio_keys_platform_data alt_pdata;

struct input_dev *input;

int i, error;

int wakeup = 0;

 

if (!pdata) {

      error = gpio_keys_get_devtree_pdata(dev, &alt_pdata);

      if (error)

          return error;

      pdata = &alt_pdata;

}

 

ddata = kzalloc(sizeof(struct gpio_keys_drvdata) +

          pdata->nbuttons * sizeof(struct gpio_button_data),

          GFP_KERNEL);

input = input_allocate_device(); //申请一个input设备

if (!ddata || !input) {

      dev_err(dev, "failed to allocate state\n");

      error = -ENOMEM;

      goto fail1;

}

 

ddata->input = input;

ddata->n_buttons = pdata->nbuttons;

ddata->enable = pdata->enable;

ddata->disable = pdata->disable;

mutex_init(&ddata->disable_lock);

 

platform_set_drvdata(pdev, ddata);

input_set_drvdata(input, ddata);

 

input->name = pdata->name ? : pdev->name;

input->phys = "gpio-keys/input0";

input->dev.parent = &pdev->dev;

input->open = gpio_keys_open;

input->close = gpio_keys_close;

 

input->id.bustype = BUS_HOST;

input->id.vendor = 0x0001;

input->id.product = 0x0001;

input->id.version = 0x0100;

//描述input设备节点属性,最后添加eventHup里的节点列表,可以通过log了解到实际相关设置如下,表明这个GPIO键盘设备的节点名字为/dev/input/event4,在节点列表中id为2,并使用的Keylayout和KeyChars设置如下

New device: id=2, fd=121, path='/dev/input/event4', name='mtk-tpd-kpd', classes=0x1, configuration='', keyLayout='/system/usr/keylayout/Generic.kl', keyCharacterMap='/system/usr/keychars/Generic.kcm', builtinKeyboard=false, usingSuspendBlockIoctl=true, usingClockIoctl=true

 


/* Enable auto repeat feature of Linux input subsystem */

if (pdata->rep)

      __set_bit(EV_REP, input->evbit);

 

for (i = 0; i < pdata->nbuttons; i++) {

      const struct gpio_keys_button *button = &pdata->buttons[i];

      struct gpio_button_data *bdata = &ddata->data[i];

 

      error = gpio_keys_setup_key(pdev, input, bdata, button);

      if (error)

          goto fail2;

 

      if (button->wakeup)

          wakeup = 1;

}

 

error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group);

if (error) {

      dev_err(dev, "Unable to export keys/switches, error: %d\n",

          error);

      goto fail2;

}

 

error = input_register_device(input);//设备注册,并和对应的handler处理函数挂钩

if (error) {

      dev_err(dev, "Unable to register input device, error: %d\n",

          error);

      goto fail3;

}

 

/* get current state of buttons that are connected to GPIOs */

for (i = 0; i < pdata->nbuttons; i++) {

      struct gpio_button_data *bdata = &ddata->data[i];

      if (gpio_is_valid(bdata->button->gpio))

          gpio_keys_gpio_report_event(bdata);//这里是中断引起的事件报告,通过gpio_keys_gpio_report_event,传递给input_event,再到input_handle_event,经过check,再通过input_pass_event,获取到对应的handler,用handler->event将事件传递给用户空间。

}

input_sync(input);

 

device_init_wakeup(&pdev->dev, wakeup);

 

return 0;

 

 fail3:

sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group);

 fail2:

while (--i >= 0)

      gpio_remove_key(&ddata->data[i]);

 

platform_set_drvdata(pdev, NULL);

 fail1:

input_free_device(input);

kfree(ddata);

/* If we have no platform_data, we allocated buttons dynamically. */

if (!pdev->dev.platform_data)

      kfree(pdata->buttons);

 

return error;

}

 

 

 

 

实际上Keypad驱动的实现依附于Input输入子系统,其完整流程比较复杂,这里只简单分析一下流程,主要是描述键盘按键是如何从驱动传递到界面的。

0 0
原创粉丝点击