基于Tiny4412的Linux按键输入子系统驱动的实现(一)

本文主要包含的章节:
    一、前期的准备工作
    二、Linux输入子系统的简单介绍
    三、基于输入子系统的按键驱动的实现


一、前期的准备工作
    1、基本的开发环境

          交叉开发环境  : Ubuntu12.04
          Linux内核版本  : Linux-3.0.86
          GUI系统  : Qtopia2.2.0
          开发板  : 友善之臂的Tiny4412(Cortex-A9)
     2、内核的配置
         由于默认的内核在初始化时已经将按键对应的GPIO口添加到平台设备当中,每当内核启动的时候这几个按键都会自动去请求中断,导致中断被占用。所以在编写自己的按键输入子系统驱动之前,要注释掉这个平台设备。
         这个平台设备对应的代码在内核中位置是:arch\arm\mach-exynos\mach-tiny4412.c,对应的行数为 :2968,代码如下所示:

static struct platform_device tiny4412_input_device = {
	.name	= GPIO_EVENT_DEV_NAME,
	.id		= 0,
	.dev	= {
		.platform_data = &tiny4412_input_data,
	},
};
      对这个文件进行修改,将 mach-tiny4412.c的3315行注释掉即可。


二、Linux输入子系统的简单介绍
        1、输入子系统

        常见的输入设备有按键、键盘、鼠标、触摸屏等,它们的工作原理虽然都各不相同,但是工作机制却是相似的。一般来讲都是通过触发中断,然后让CPU来读取键值、坐标值等。显然,在这些工作中,只有中断、读键值、坐标值等是设备相关的,而输入事件的缓冲区管理以及字符设备驱动的VFS(Virtual File System)接口对输入设备是通用的。所以,内核把这些输入事件相似的地方给抽象了出来,设计出了输入子系统。
        2、核心函数
       核心函数在内核的位置:drivers\input\input.c 和 include\linux\input.h
       2.1 分配、释放一个输入设备

struct input_dev *input_allocate_device(void)
void input_free_device(struct input_dev *dev)
       2.2 注册、注销输入设备

int input_register_device(struct input_dev *dev)
void input_unregister_device(struct input_dev *dev)
       2.3 报告输入事件的接口

void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
/* 报告键值 */
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
/* 报告相对坐标 */
static inline void input_report_rel(struct input_dev *dev, unsigned int code, int value)
/* 报告绝对坐标 */
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
/* 报告同步事件 */
static inline void input_sync(struct input_dev *dev)
        3、输入子系统驱动的一般编写步骤
           a、分配一个input_dev结构体
           b、设置input_dev结构体  :  支持哪类事件, 支持该类事件中的那些事件
           c、注册input_dev结构体
           d、硬件相关的操作  : 中断申请,定时器的设置等

三、基于输入子系统的按键驱动的实现
     3.1 Tiny4412开发板上的按键电路原理图

基于Tiny4412的Linux按键输入子系统驱动的实现(一)_第1张图片
      通过对原理图进行分析,可以看出当按键松开的时候IO口对应的是高电平,当按键按下的时候对应的是低电平。按键对应的GPIO是 :        
       * XEINT26----GPX3_2      

       * XEINT27----GPX3_3   

       * XEINT28----GPX3_4    

       * XEINT29----GPX3_5
       如下图所示:
基于Tiny4412的Linux按键输入子系统驱动的实现(一)_第2张图片

        3.2 按键输入子系统驱动的编写
        3.2.1 分配一个名为buttons_dev的input_dev结构体

buttons_dev = input_allocate_device();
if(!buttons_dev)
{
	printk("input_allocate_device error!\n");
	return -ENOMEM;
}
       3.2.2 设置buttons_dev结构体

/* 2.1、支持哪类事件 */
set_bit(EV_KEY, buttons_dev->evbit);
set_bit(EV_REP, buttons_dev->evbit);

/* 2.2、支持该类事件中的那些事件 */
for(i = 0; i < sizeof(buttons_desc)/sizeof(buttons_desc[0]); i++)
{
	set_bit(buttons_desc[i].key_code, buttons_dev->keybit);
}
       3.2.3 注册这个结构体

input_register_device(buttons_dev);
       3.2.4 硬件相关的操作

/* 4、硬件相关的操作 
*	  为每个按键申请一个中断,共用中断处理函数
*	  按键触发方式为双边沿触发
*/
for(i = 0; i < sizeof(buttons_desc)/sizeof(buttons_desc[0]); i++)
{
	irq = gpio_to_irq(buttons_desc[i].gpio);
	request_irq(irq, yl_buttons_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, buttons_desc[i].name, (void*)&buttons_desc[i]);	
}
       在硬件操作的核心是请求中断,中断的核心是 yl_buttons_irq 这个函数,这个函数的代码如下所示:

/* 按键中断处理程序 */
static irqreturn_t yl_buttons_irq(int irq, void *devid)
{
	struct yl_buttons_desc *buttons_desc = (struct yl_buttons_desc *)devid;
	int pinval = gpio_get_value(buttons_desc->gpio);

	if(pinval == 1)	/* 判断按键是按下还是松开 */
	{
		/* 松开 */
		input_event(buttons_dev, EV_KEY, buttons_desc->key_code, 0);
		input_sync(buttons_dev);
	}
	else
	{
		/* 按下 */
		input_event(buttons_dev, EV_KEY, buttons_desc->key_code, 1);
		input_sync(buttons_dev);
	}
	
	return IRQ_HANDLED;
}
        当发生按键中断时将触发按键中断程序,根据获得的对应按键的值来确定按键是按下还是松开,然后分别进行事件上报和事件同步。


附录:本节实现的按键输入子系统的完整代码如下所示。
/* 包含的头文件 */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 

/* 输入子系统需要的头文件 */
#include 

/**	按键映射:
  *		XEINT26		----	GPX3_2
  *		XEINT27		----	GPX3_3
  *		XEINT28		----	GPX3_4
  *		XEINT29		----	GPX3_5
  */

/* 定义一个结构体用来对输入按键进行描述 */
struct yl_buttons_desc{
	int  gpio;		// 表示对于的按键的引脚
	char *name;		// 表示对应的按键请求中断时的中断名
	int  key_code;	// 表示按键在输入子系统中对应的键值
};

/* 定义一个描述按键的数组 */
static struct yl_buttons_desc buttons_desc[] = {
		{EXYNOS4_GPX3(2), "yl_buttons_L", 		  KEY_L},
		{EXYNOS4_GPX3(3), "yl_buttons_S",		  KEY_S},
		{EXYNOS4_GPX3(4), "yl_buttons_ENTER", 	  KEY_ENTER},
		{EXYNOS4_GPX3(5), "yl_buttons_LEFTSHIFT", KEY_LEFTSHIFT},
};

/* 定义一个输入子系统的结构体指针变量 */
static struct input_dev *buttons_dev;

/* 按键中断处理程序 */
static irqreturn_t yl_buttons_irq(int irq, void *devid)
{
	struct yl_buttons_desc *buttons_desc = (struct yl_buttons_desc *)devid;
	int pinval = gpio_get_value(buttons_desc->gpio);

	if(pinval == 1)	/* 判断按键是按下还是松开 */
	{
		/* 松开 */
		input_event(buttons_dev, EV_KEY, buttons_desc->key_code, 0);
		input_sync(buttons_dev);
	}
	else
	{
		/* 按下 */
		input_event(buttons_dev, EV_KEY, buttons_desc->key_code, 1);
		input_sync(buttons_dev);
	}
	
	return IRQ_HANDLED;
}

/* 入口函数 */
static int __init yl_buttons_init(void)
{
	int irq;
	int i;

	/* 1、分配一个input_dev结构体 */
	buttons_dev = input_allocate_device();
	if(!buttons_dev)
	{
		printk("input_allocate_device error!\n");
		return -ENOMEM;
	}

	/* 2、设置input_dev结构体 */
	/* 2.1、支持哪类事件 */
	set_bit(EV_KEY, buttons_dev->evbit);
	set_bit(EV_REP, buttons_dev->evbit);

	/* 2.2、支持该类事件中的那些事件 */
	for(i = 0; i < sizeof(buttons_desc)/sizeof(buttons_desc[0]); i++)
	{
		set_bit(buttons_desc[i].key_code, buttons_dev->keybit);
	}

	/* 3、注册input_dev结构体 */
	input_register_device(buttons_dev);

	/* 4、硬件相关的操作 
	 *	  为每个按键申请一个中断,共用中断处理函数
	 *	  按键触发方式为双边沿触发
	 */
	for(i = 0; i < sizeof(buttons_desc)/sizeof(buttons_desc[0]); i++)
	{
		irq = gpio_to_irq(buttons_desc[i].gpio);
		request_irq(irq, yl_buttons_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, buttons_desc[i].name, (void*)&buttons_desc[i]);	
	}
	
	return 0;
}

/* 出口函数 */
static void __exit yl_buttons_exit(void)
{
	int irq;
	int i;

	/* 释放申请的按键中断 */
	for(i = 0; i < sizeof(buttons_desc)/sizeof(buttons_desc[0]); i++)
	{
		irq = gpio_to_irq(buttons_desc[i].gpio);
		free_irq(irq, (void*)&buttons_desc[i]);	
	}

	input_unregister_device(buttons_dev);
	input_free_device(buttons_dev);
}

module_init(yl_buttons_init);
module_exit(yl_buttons_exit);

MODULE_LICENSE("GPL");








你可能感兴趣的:(基于Tiny4412的Linux按键输入子系统驱动的实现(一))