天天看点

S5PV210(TQ210)学习笔记——输入子系统驱动

前面的文章简单的讲述了字符设备驱动程序的编写,用字符设备驱动的方式实现了按键驱动,但是,出了我们的自己编写的针对我们的这个驱动程序的应用程序之外,其他应用程序都无法接收我们这个驱动的键值输入,为了让所有应用程序都可以接收我们的按键驱动解析的键值,Linux内核定义了“输入子系统”的概念,也就是说,只要我们按照这个模型进行驱动开发,并为其提供必须的接口函数,那么,Linux内核就可以正常来获取我们的键盘值了。

输入子系统的原理分析强烈推荐观看韦东山老师的视频讲座,讲的非常清楚,我这里是按照输入子系统的方式实现的按键驱动,下面是源码,放在这里做个备份:

#include <linux/types.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>

static struct input_dev *buttons_dev;
static struct timer_list timer;
struct button_desc* button_desc = NULL;

struct button_desc{
	char* name;
	unsigned int pin;
	unsigned int irq;
	unsigned int val;
};

static struct button_desc buttons_desc[8] = {
	[0] = {
		.name = "S1",
		.pin = S5PV210_GPH0(0),
		.irq = IRQ_EINT(0),
		.val = KEY_L,
	},

	[1] = {
		.name = "S2",
		.pin = S5PV210_GPH0(1),
		.irq = IRQ_EINT(1),
		.val = KEY_S,
	},

	[2] = {
		.name = "S3",
		.pin = S5PV210_GPH0(2),
		.irq = IRQ_EINT(2),
		.val = KEY_C,
	},

	[3] = {
		.name = "S4",
		.pin = S5PV210_GPH0(3),
		.irq = IRQ_EINT(3),
		.val = KEY_ENTER,
	},

	[4] = {
		.name = "S5",
		.pin = S5PV210_GPH0(4),
		.irq = IRQ_EINT(4),
		.val = KEY_LEFTCTRL,
	},

	[5] = {
		.name = "S6",
		.pin = S5PV210_GPH0(5),
		.irq = IRQ_EINT(5),
		.val = KEY_MINUS,
	},

	[6] = {
		.name = "S7",
		.pin = S5PV210_GPH2(6),
		.irq = IRQ_EINT(22),
		.val = KEY_CAPSLOCK,
	},

	[7] = {
		.name = "S8",
		.pin = S5PV210_GPH2(7),
		.irq = IRQ_EINT(23),
		.val = KEY_SPACE,
	},
};

static void timer_function(unsigned long data){
	if(button_desc == NULL)
		return;

	if(gpio_get_value(button_desc->pin)){
		input_event(buttons_dev, EV_KEY, button_desc->val, 0);
	}
	else{
		input_event(buttons_dev, EV_KEY, button_desc->val, 1);
	}
	input_sync(buttons_dev);
}

static irqreturn_t irq_handler(int irq, void *devid){
	button_desc = (struct button_desc*)devid;
	mod_timer(&timer, jiffies + HZ/100);
	return IRQ_RETVAL(IRQ_HANDLED);
}

static int buttons_init(void){
	int i;
	
	buttons_dev = input_allocate_device();
	if(buttons_dev == NULL){
		printk(KERN_ERR "Error: allocate input device failed!\n");
		return -ENOMEM;
	}

	__set_bit(EV_KEY, buttons_dev->evbit);
	__set_bit(EV_REP, buttons_dev->evbit);

	__set_bit(KEY_L,        buttons_dev->keybit);
	__set_bit(KEY_S,        buttons_dev->keybit);
	__set_bit(KEY_C,        buttons_dev->keybit);
	__set_bit(KEY_SPACE,    buttons_dev->keybit);
	__set_bit(KEY_MINUS,    buttons_dev->keybit);
	__set_bit(KEY_ENTER,    buttons_dev->keybit);
	__set_bit(KEY_LEFTCTRL, buttons_dev->keybit);
	__set_bit(KEY_CAPSLOCK, buttons_dev->keybit);

	printk("1\n");
	if(input_register_device(buttons_dev)){
		goto error_1;
	}

	printk("2\n");
	init_timer(&timer);
	timer.function = timer_function;
	add_timer(&timer);

	printk("3\n");
	for(i = 0; i != 8; ++i){
		if(request_irq(buttons_desc[i].irq, irq_handler, 
			IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, buttons_desc[i].name, &buttons_desc[i])){
			goto error_2;
		}
	}
	printk("4\n");
	
	return 0;

error_2:
	for(--i; i >= 0; --i){
		free_irq(buttons_desc[i].irq, &buttons_desc[i]);
	}
	input_unregister_device(buttons_dev);

error_1:
	input_free_device(buttons_dev);

	return -EBUSY;
}

static void buttons_exit(void){
	int i;
	for(i = 0; i != 8; ++i){
		free_irq(buttons_desc[i].irq, &buttons_desc[i]);
	}

	input_unregister_device(buttons_dev);
	input_free_device(buttons_dev);
}

module_init(buttons_init);
module_exit(buttons_exit);
MODULE_LICENSE("GPL");
           

很显然,基于输入子系统的按键驱动比起直接编写的字符驱动设备要简单的多,不过按键消抖还是要加的,上面的代码加上了按键消抖。如果在开发过程中遇到了什么问题,还是欢迎留言讨论。

本文链接:http://blog.csdn.net/girlkoo/article/details/8736243

本文作者:girlkoo

继续阅读