这个设备驱动适用于,每一个按键是链接到一个io口, 并且这个io口还有中断功能的linux
须要在linux内核配置里选上相关的配置。在内核源码目录下:数组
# make menuconfig Device Drivers ---> Input device support ---> [*] Keyboards ---> <*> GPIO Buttons
选择上后,再编内核,再使用新的内核镜像启动系统函数
使用新内核启动后,能够查看出设备驱动是否已选择上: 源码分析
/sys/bus/platform/drivers/目录下应有”gpio-keys”目录this
驱动源码在”drivers/input/keyboard/gpio_keys.c”, 里面是一个平台驱动,咱们只要写平台设备描述硬件的资源与此驱动匹配便可.编码
static struct platform_driver gpio_keys_device_driver = { .probe = gpio_keys_probe, .remove = __devexit_p(gpio_keys_remove), .driver = { .name = "gpio-keys", // 可匹配名为"gpio-keys"的平台设备 .owner = THIS_MODULE, .pm = &gpio_keys_pm_ops, .of_match_table = gpio_keys_of_match, //按这个成员来匹配平台设备也是能够的,要求设备的名字为"gpio-keys" } }; //经过阅读平台驱动的probe函数,可得知咱们写的平台设备应提供具本哪些硬件信息. static int __devinit gpio_keys_probe(struct platform_device *pdev) { const struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; //这里可得知咱们写的平台设备的platform_data成员应当提供gpio_keys_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; ... //对gpio_keys_drvdata对象的初始化 //输入设备对象的初始化 ... }
///////////////////// //经过probe函数,能够肯定咱们写平台设备时只需经过platform_data成员提供平台驱动所需的信息,无需再提供resource. //再肯定结构体gpio_keys_platform_data的每一个成员的做用便可,如不清楚具体用途,能够在驱动代码里经过查看对成员值的访问推出用途. //"include/linux/gpio_keys.h" //每一个struct gpio_key_button的对象表示一个按键的具体信息 struct gpio_keys_button { //此按键对应的键码 unsigned int code; /* input event code (KEY_*, SW_*) */ //此按键对应的一个io口 int gpio; /* -1 if this key does not support gpio */ //经过查看驱动代码,可得知表示是否按键按下是低电平,如是则设1. int active_low; //就是申请io口,申请中断时使用的名字 const char *desc; //输入设备的事件类型,按键用EV_KEY unsigned int type; /* input event type (EV_KEY, EV_SW, EV_ABS) */ //表示按键按下时是否唤醒系统, 这个须要io口硬件上有这功能 int wakeup; /* configure the button as a wake-up source */ //防抖动用,间隔多久时间 int debounce_interval; /* debounce ticks interval in msecs */ ... }; //gpio_keys_paltform_data对象表示一个输入设备, 一个输入设备可有多个按键 struct gpio_keys_platform_data { //多个按键须要用gpio_keys_button的变量数组才能够, buttons成员用于装数组首地址 struct gpio_keys_button *buttons; //在按键数组里的元素个数 int nbuttons; //轮询的按键的平台驱动所用 unsigned int poll_interval; /* polling interval in msecs - for polling driver only */ //键按住时,是否重复提交按键 unsigned int rep:1; /* enable input subsystem auto repeat */ //设备这边需在使用前所作的初始化工做,由设备驱动调用. 在输入设备产生的设备文件打开时触发调用 int (*enable)(struct device *dev); //设备这边需在结束工做前所作的工做, 由设备驱动调用.在输入设备产生的设备文件关闭时触发调用 void (*disable)(struct device *dev); const char *name; /* input device name */ };
现用一个按键链接再板上,SIG脚接到PA20. 当键按下时,SIG脚为高电平。键松开时,SIG脚为低电平.spa
如下信息能够添加到arch/arm/mach-board/mach-soc.c。线程
//mypdev.c #include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/gpio_keys.h> #include <linux/input.h> #include <mach/gpio.h> struct gpio_keys_button btns[] = { {KEY_L, GPIOA(20), 0, "mygpio-keys", EV_KEY, 0, 100}, }; struct gpio_keys_platform_data pdata = { .buttons = btns, .nbuttons = ARRAY_SIZE(btns), .rep = 1, .name = "mygpio-keys", }; struct platform_device mypdev = { .name = "gpio-keys", //与平台驱动的名字一致才会匹配上 .id = -1, .dev = { .platform_data = &pdata, }, }; module_driver(mypdev, platform_device_register, platform_device_unregister); MODULE_LICENSE("GPL");
使用方式比较简单,和普通的文件操做同样, 先打开设备文件, 再读文件获取键值便可:code
Linux中的按键检测经过循环读取设备文件/dev/input/event[x](其中x能够为0,1,2…)设备文件获取按键事件,不一样的平台设备文件可能会有差别, 若是不清楚对应的设备文件, 能够用下面的命令来查看:orm
打开设备代码以下:
/*1.key device*/ fd_key= open(KEY_DEVICE_FILE, O_RDONLY); if(fd_key< 0) { LOGE("can'topen key device file"); returnfd_key; }
按键事件通常由主线程循环获取按键事件,而后经过消息队列通知其余子线程,从而作出响应。
在Linux内核中,按键事件用input_event结构体描述,该结构体在头文件<linux/input.h>中定义,同时该文件还定义了有关按键事件的API函数接口、标准按键编码等。
input_event结构体定义以下:
struct input_event { struct timeval time; __u16 type; __u16 code; __s32 value; };
<linux/input.h>还定义了经常使用标准按键编码:
#define KEY_RESERVED 0 #define KEY_ESC 1 #define KEY_1 2 #define KEY_2 3 #define KEY_3 4 #define KEY_4 5 #define KEY_5 6 #define KEY_6 7
在用户态,咱们只须要循环读取设备文件/dev/input/eventx,就能够获得相应的键盘事件,代码以下:
#include <linux/input.h> #include <fcntl.h> #include <stdio.h> #include <stdint.h> #define KEY_EVENT_DEV1_NAME "/dev/input/event1" int sysKeyScan(void) { int l_ret = -1; int i = 0; int key_fd = 0; struct input_event key_event = {0}; key_fd = open(KEY_EVENT_DEV1_NAME, O_RDONLY); if(key_fd <= 0) { printf("---open /dev/input/event1 device error!---\n"); return l_ret; } while(1) { l_ret = lseek(key_fd, 0, SEEK_SET); l_ret = read(key_fd, &key_event, sizeof(key_event)); if(l_ret) { if(key_event.type == EV_KEY && (key_event.value == 0 || key_event.value == 1)) { printf("key %d %s\n", key_event.code, (key_event.value) ? "pressed" : "released"); if(key_event.code == KEY_ESC) { break; } } } } close(key_fd); return l_ret; } int main(int arg, char *arc[]) { printf("---This is a key event test!---\n"); sysKeyScan(); return 0; }
有时候,咱们的的Linux内核也能够把不一样的按键封装到不一样的event中,例如数字键键盘事件经过event1通知用户态,而功能键经过event0通知用户态。此时咱们可使用poll函数来同时监测多个等待事件,若事件未发生,进程睡眠,放弃CPU控制权,直到有键盘事件发生,poll将唤醒睡眠的进程,并执行相应的操做。代码以下:
#include <linux/input.h> #include <fcntl.h> #include <poll.h> #include <stdio.h> #include <stdint.h> #define KEY_EVENT_DEV0_NAME "/dev/input/event0" #define KEY_EVENT_DEV1_NAME "/dev/input/event1" int sysKeyScan(void) { int l_ret = -1; int i = 0; int key_fd[2] = {0}; struct pollfd key_fds[2] = {0}; struct input_event key_event = {0}; key_fd[0] = open(KEY_EVENT_DEV0_NAME, O_RDONLY); if(key_fd[0] <= 0) { printf("---open /dev/input/event0 device error!---\n"); return l_ret; } key_fd[1] = open(KEY_EVENT_DEV1_NAME, O_RDONLY); if(key_fd[1] <= 0) { printf("---open /dev/input/event1 device error!---\n"); return l_ret; } for(i = 0; i < 2; i++) { key_fds[i].fd = key_fd[i]; key_fds[i].events = POLLIN; } while(1) { l_ret = poll(key_fds, 2, -1); for(i = 0; i < 2; i++) { l_ret = lseek(key_fd[i], 0, SEEK_SET); l_ret = read(key_fd[i], &key_event, sizeof(key_event)); if(l_ret) { if(key_event.type == EV_KEY && (key_event.value == 0 || key_event.value == 1)) { printf("key value(%d) %s", key_event.code, key_event.value ? "press" : "release"); } } } } close(key_fd[0]); close(key_fd[1]); return l_ret; } int main(int argc, char *argv[]) { printf("---This is a key event test!---\n"); sysKeyScan(); return 0; }