天天看點

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

繼續閱讀