#ifndef _GPIO_KEYS_H
#define _GPIO_KEYS_H
struct gpio_keys_button {
/* Configuration parameters */
int code; /* 输入事件的键值可以是EV_*或KEY_* */
int gpio; /* 对应的GPIO号 */
int active_low; /* 活动时是否为低,即按下的时候是否被拉低 */
char *desc; /* 一些注册信息 */
int type; /* 输入事件的类型,比如是拨码还是按键 */
int wakeup; /* 配置该输入信号作为系统唤醒源 */
int debounce_interval; /* 对于按键开关设置抖动时间,一般20ms */
};
struct gpio_keys_platform_data { /* gpio_keys 的平台私有数据,一般作为私有指针传给platform_device */
struct gpio_keys_button *buttons;
int nbuttons;
unsigned int rep:1; /* enable input subsystem auto repeat */
};
#endif
平台设备的注册:
在平台文件中包含gpio_keys所需的头文件
#include <linux/gpio_keys.h>
#include <linux/input.h>
#define GPIO_TO_KEY(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, \
.debounce_interval = 15, \
}
#define M2M_BAORD_USER_KEY GPIO_TO_PIN(3, 7)
static struct gpio_keys_button sbc_buttons[] = {
GPIO_TO_KEY(M2M_BAORD_USER_KEY, KEY_UP, 0, "user_bt1", 0),
};
static struct gpio_keys_platform_data sbc_buttons_data = {
.buttons = sbc_buttons,
.nbuttons = ARRAY_SIZE(sbc_buttons),
};
static struct platform_device sbc_buttons_device = {
.name = "gpio-keys", /* 如果使用内核驱动gpio-keys,则平台设备名必须为此,这是内核驱动模型所决定 */
.id = -1,
.num_resources = 0,
.dev = {
.platform_data = &sbc_buttons_data, /* 私有数据传递给驱动 */
}
};
上面定义了GPIO_TO_KEY宏来代替struct gpio_keys_button类型,方便定义多个按键。
最后需要在平台文件里显示注册gpio-keys到驱动设备链表里
platform_device_register(&sbc_buttons_device);
平台设备驱动:
内核里已经支持了标准的gpio_keys驱动,在drivers/input/keyboard目录下,对应刚才注册的gpip-keys设备,这里为gpio_keys.c
需要选择CONFIG_KEYBOARD_GPIO才会被编译
首先参考平台驱动结构体platform_driver
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,
#ifdef CONFIG_PM
.pm = &gpio_keys_pm_ops,
#endif
}
};
现在的cpu集成度很高,基本作为一个驱动开发者,特别是在天朝,其实并没有太多的技术含量,soc的驱动在芯片厂商提供的SDK包中
基本已经包含了,国内一般在官方板的基础上进行裁剪添加,而作为驱动工程所,剩下的工作只需要熟悉驱动框架模型,以及各种总线接口时序,
可能驱动只需要改动几行代码就可以了,而基于SOC的platform设备驱动也是接触最多的。
probe函数主要流程,初始化input设备结构,根据gpio号申请并置为输入,然后根据GPIO号申请中断,并注册中断函数,中端方式为共享中断,边沿触发。
将设备与input设备绑定并注册input到内核输入框架。
中断产生后。
/* 向上层报告 */
41 static void gpio_keys_report_event(struct work_struct *work)
42 {
43 struct gpio_button_data *bdata =
44 container_of(work, struct gpio_button_data, work);
45 struct gpio_keys_button *button = bdata->button;
46 struct input_dev *input = bdata->input;
47 unsigned int type = button->type ?: EV_KEY;
48 int state = (gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low;
49 input_event(input, type, button->code, !!state);
50 input_sync(input);
51 }
52 /* 若设置了抖动时间,定时器延时到后会调用进入函数 */
53 static void gpio_keys_timer(unsigned long _data)
54 {
55 struct gpio_button_data *data = (struct gpio_button_data *)_data;
56
57 schedule_work(&data->work);
58 }
59 /* 中断处理函数 */
60 static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
61 {
62 struct gpio_button_data *bdata = dev_id;
63 struct gpio_keys_button *button = bdata->button;
64
65 BUG_ON(irq != gpio_to_irq(button->gpio));
66
67 if (button->debounce_interval)
68 mod_timer(&bdata->timer,
69 jiffies + msecs_to_jiffies(button->debounce_interval));
70 else
71 schedule_work(&bdata->work);
72
73 return IRQ_HANDLED;
74 }
linux设备驱动之gpio_keys
最新推荐文章于 2021-05-26 20:49:04 发布