热门内容

公众号"MAKE1"

获取行业最新资讯

请扫码添加

专业客服企业微信

Keypad驱动--简介

简介

Keypad驱动--简介

5. Driver

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子系统的编译处理,在kerneldrivers的makefile里面我们可以看到,它会编译对应的input目录下的makefile,

对应键盘,在kerneldriversinputkeyboard目录下,定义了各种键盘实现,部分实例如下,

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

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

所以我们接着分析CONFIG_KEYBOARD_GPIO是如何开启来支持GPIO键盘的。在kernelarcharmconfigs目录下,定义了各种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 staten");

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: %dn",

error);

goto fail2;

}

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

if (error) {

dev_err(dev, "Unable to register input device, error: %dn",

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;

}

2
 条评论
相关内容推荐