kernel(十)按键

一、定义keys平台设备

参考: Documentation/devicetree/bindings/gpio/gpio_keys.txt

drivers/input/keyboard/gpio_keys.c    查看 TQ210 原理图

kernel(十)按键_第1张图片

1.1、在 mach-Louis210.c 中添加头文件

1.2、定义 keys 的平台设备

/* gpio keys (add by JerryGou) */
static struct gpio_keys_button buttons[] = {
	[0] = {
		.code = KEY_UP,          按键对应的键码
		.gpio = S5PV210_GPH0(0), 按键对应的IO口
		.active_low = 1,         通过查看驱动代码,可得知表示是否按键按下是低电平,如是则设1
		.desc = "KEY_UP",        申请io口,申请中断时使用的名字
		.type = EV_KEY,          输入设备的事件类型,按键用EV_KEY
		.debounce_interval = 50, 防抖动用,间隔多久时间
	},
	[1] = {
		.code = KEY_DOWN,
		.gpio = S5PV210_GPH0(1),
		.active_low = 1,
		.desc = "KEY_DOWN",
		.type = EV_KEY,
		.debounce_interval = 50,
	},
	[2] = {
		.code = KEY_LEFT,
		.gpio = S5PV210_GPH0(2),
		.active_low = 1,
		.desc = "KEY_LEFT",
		.type = EV_KEY,
		.debounce_interval = 50,
	},
	[3] = {
		.code = KEY_RIGHT,
		.gpio = S5PV210_GPH0(3),
		.active_low = 1,
		.desc = "KEY_RIGHT",
		.type = EV_KEY,
		.debounce_interval = 50,
	},
	[4] = {
		.code = KEY_ENTER,
		.gpio = S5PV210_GPH0(4),
		.active_low = 1,
		.desc = "KEY_ENTER",
		.type = EV_KEY,
		.debounce_interval = 50,
	},
	[5] = {
		.code = KEY_BACK,
		.gpio = S5PV210_GPH0(5),
		.active_low = 1,
		.desc = "KEY_BACK",
		.type = EV_KEY,
		.debounce_interval = 50,
	},
	[6] = {
		.code = KEY_MENU,
		.gpio = S5PV210_GPH2(6),
		.active_low = 1,
		.desc = "KEY_MENU",
		.type = EV_KEY,
		.debounce_interval = 50,
	},
	[7] = {
		.code = KEY_POWER,
		.gpio = S5PV210_GPH2(7),
		.active_low = 1,
		.desc = "KEY_POWER",
		.type = EV_KEY,
		.debounce_interval = 50,
	},
};

static struct gpio_keys_platform_data Louis210_keys_pdata = {
	.buttons = buttons,
	.nbuttons = ARRAY_SIZE(buttons),
	.rep = 1,
};

static struct platform_device Louis210_keys = {
	.name = "gpio-keys",
	.dev = {
		.platform_data =  &Louis210_keys_pdata,
	},
	.id = -1,
};

kernel(十)按键_第2张图片

二、配置内核

Device Drivers --->
        Input device support --->
                [*] Keyboards --->
                        <*> GPIO Buttons

编译后/sys/bus/platform/drivers/目录下应有”gpio-keys”目录

要让按键支持控制台,需要配置/etc/inittab,在开一个 tty
tty2::askfirst:-/bin/sh
编译内核,运行测试
kernel(十)按键_第3张图片

成功注册 gpio_keys,设备文件为/dev/input/event1
现在按 Enter 键(key5), LCD 效果如下
kernel(十)按键_第4张图片

三、用户空间程序测试

key_app_test.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 

int fd = -1;
struct input_event ev;

void sig_handle(int sig)
{
	int rc = -1;
		
	rc = read(fd, &ev, sizeof(struct input_event));
	if (rc < 0)
	{
		perror("read");
		return;
	}
	if (EV_KEY == ev.type)
	{
		if (1 == ev.value)
			printf("key %d, value:%d\n", ev.code, ev.value);
		else
			printf("key %d, value:%d\n", ev.code, ev.value);
	}
}

int main(int argc, char *argv[])
{
	size_t rb;
	int version;
	char name[20];
	
	int oflags = 0;
	
	if (argc != 2)
	{
		fprintf(stderr, "Usage:%s /dev/eventx\n", argv[0]);
		exit(1);
	}

	if ((fd = open(argv[1], O_RDONLY)) < 0)
	{
		perror("open error");
		exit(1);
	}
	
#if 0	
	if (ioctl(fd, EVIOCGNAME(sizeof(name)-1), name) < 0)
	{
		perror("getname error");
		exit(1);
	}
	printf("name=%s\n", name);
	
	if (ioctl(fd, EVIOCGVERSION, &version) < 0)
	{
		perror("getversion error");
		exit(1);
	}
	printf("version=0x%x\n", version);

	while(1)
	{
		rb = read(fd, &ev, sizeof(struct input_event));
		
		if (rb < (int)sizeof(struct input_event))
		{
			perror("read error");
			exit(1);
		}

		if (EV_KEY == ev.type)
		{
			if (1 == ev.value)
				printf("key %d is pressed\n", ev.code);
			else
				printf("key %d is releassed\n", ev.code);
		}
	}
#else
	signal(SIGIO, sig_handle);
	fcntl(fd, F_SETOWN, getpid());
	oflags = fcntl(fd, F_GETFL);
	fcntl(fd, F_SETFL, oflags | FASYNC);
	while (1)
		sleep(1);
#endif
	close(fd);
	
	return 0;
}

测试效果

kernel(十)按键_第5张图片

四、keys 结构体分析

"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脚为低电平.

mypdev.c

#include 
#include 
#include 
#include 
#include 
#include 

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");

五、keys 平台驱动分析

驱动源码在”drivers/input/keyboard/gpio_keys.c”, 里面是一个平台驱动,我们只要写平台设备描述硬件的资源与此驱动匹配即可。

static struct platform_driver gpio_keys_device_driver = {
    .probe      = gpio_keys_probe,
    .remove     = gpio_keys_remove,
    .driver     = {
        .name   = "gpio-keys", 可匹配名为"gpio-keys"的平台设备
        .owner  = THIS_MODULE,
        .pm = &gpio_keys_pm_ops,
        .of_match_table = of_match_ptr(gpio_keys_of_match), 按这个成员来匹配平台设备也是可以的,要求设备的名字为"gpio-keys"
    }
};

通过阅读平台驱动的probe函数,可得知我们写的平台设备应提供具本哪些硬件信息.
static int __devinit gpio_keys_probe(struct platform_device *pdev)
{
    这里可得知我们写的平台设备的platform_data成员应当提供gpio_keys_platform_data类型数据
    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;
    ...

    对gpio_keys_drvdata对象的初始化
    输入设备对象的初始化
    ...
}

 

你可能感兴趣的:(kernel)