Linux输入子系统——按键驱动实例

<span style="font-size:18px;">#include<linux/init.h> 		
#include<linux/module.h>	
#include<linux/fs.h>		
#include<linux/device.h>   
#include<linux/slab.h>		
#include<linux/cdev.h>		
#include<linux/uaccess.h>	
#include<linux/miscdevice.h>
#include<asm/io.h>							// ioremap,ioread32等IO内存操作函数定义
#include<linux/irq.h>						// 中断相关
#include<linux/interrupt.h>
#include<asm/irq.h>
#include <linux/sched.h>					// 等待队列相关头文件
#include <linux/poll.h>						// poll
#include <linux/input.h>					// Input子系统头语言件

#define DEV_NAME 	"key_input"				// 设备名

#define GPFCON   0X56000050					// EINT 0 - 4, 即GPFCON控制引脚寄存器地址
#define GPFDAT	 0X56000054					// EINT 0 - 4,也即GPFDAT寄存器地址
#define GPFUDP	 0X56000058					// Pull-up/down control register for port F


// IO内存映射后的虚拟地址,对其进行读写
volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;
volatile unsigned long *gpfupd = NULL;

/*--------------- 按键描述 ----------------*/
struct key_des
{
	int irq;				// 按键中断号
	int number;				// 按键编号(对应实际键盘值)
	unsigned long flags;	// 中断触发标志,上升?下降?双边沿?
	char *name;				// 中断名称
};

/* Helper2416开发板上按键定义 */
static struct key_des key_irqs[] = 
{
	{IRQ_EINT0,0,IRQ_TYPE_EDGE_BOTH,"KEY1"},	// 双边沿触发
	{IRQ_EINT1,1,IRQ_TYPE_EDGE_BOTH,"KEY2"},
	{IRQ_EINT2,2,IRQ_TYPE_EDGE_BOTH,"KEY3"},
	{IRQ_EINT3,3,IRQ_TYPE_EDGE_BOTH,"KEY4"},
	{IRQ_EINT4,4,IRQ_TYPE_EDGE_BOTH,"KEY5"},
};

static struct key_des *key_event;				// key_event变量(用于中断源检测)

static struct input_dev *key_input_dev;			// 定义一个input设备(指针)	

/*---------定时器-----------------*/
static struct timer_list key_timer;	

/*-------- 定时器服务函数-------- */
void key_timer_func(unsigned long arg)
{	
	int number = key_event->number;				// 获得中断标号
	int code = -1;
	int value;
	
	// 读取IO引脚电平,获理相应中断值
	value  = ( ioread32(gpfdat) & (1 << number) )? 1:0 ;
	
	// 按键功能重定义
	switch(number)
	{
		case 0:{code = KEY_ENTER;break;}
		case 1:{code = KEY_RIGHT;break;}
		case 2:{code = KEY_LEFT; break;}
		case 3:{code = KEY_DOWN; break;}
		case 4:{code = KEY_UP;   break;}
	}
	// 上报用户键值信息
	/*input_event() - report new input event
	* @dev:   device that generated the event
	* @type:  type of the event
	* @code:  event code
	* @value: value of the event
	*/
	input_event(key_input_dev,EV_KEY,code,value);
	input_sync(key_input_dev);
}

// 中断服务程序
static irqreturn_t key_interrupt_func(int irq, void *dev_id)
{
	// 读取得到按键源
	key_event = (struct key_des *)dev_id;			
	
	// HZ就是一秒,除以100就是100ms
	mod_timer(&key_timer,jiffies + HZ/50);	
	
	return IRQ_RETVAL(IRQ_HANDLED);
}


//  input init
static int s3c2416_key_init(void)
{
	int i;
	int error;
	
	// 1. 分配一个结构体并对其成员进行初始化
	key_input_dev = input_allocate_device();
	if(key_input_dev == NULL)
	{
		printk(KERN_ERR "key_input.c: Not enough memory\n");
		return -ENOMEM;
	}
	
	// 2.  设置
	// 2.1 type of the event
	set_bit(EV_KEY,key_input_dev->evbit);		// 产生按键事件
	
	// 2.2 event code
	set_bit(KEY_UP,   key_input_dev->keybit);			    
	set_bit(KEY_DOWN, key_input_dev->keybit);			
	set_bit(KEY_LEFT, key_input_dev->keybit);			
	set_bit(KEY_REGHT,key_input_dev->keybit);			
	set_bit(KEY_ENTER,key_input_dev->keybit);			
	
	// 注册input device
	error = input_register_device(key_input_dev);
	if(error)
	{
		printk(KERN_ERR "key_input.c: input_register_device error\n");

	}
	/* 初始化定时器 */
	init_timer(&key_timer);					// 初始化定时器
	key_timer.function = &key_timer_func;	// 安装定时器服务函数
	add_timer(&key_timer);					// 添加定时器到内核定时器动态链表
	
	// 申请中断资源
	for(i = 0; i < sizeof(key_irqs)/sizeof(key_irqs[0]); i ++)
	{
		error = request_irq(key_irqs[i].irq, 		// 中断号
						  key_interrupt_func,		// 中断服务函数
						  key_irqs[i].flags,		// 中断标志
						  key_irqs[i].name, 		// 中断名称
						  (void *)&key_irqs[i]); 	// 传递给中服务函数的参数

	}
	
	// 3. 将物理寄存器地址映射到IO内存
	gpfcon = (volatile unsigned long *)ioremap(GPFCON, 4);
	gpfdat = (volatile unsigned long *)ioremap(GPFDAT, 4);
	gpfupd = (volatile unsigned long *)ioremap(GPFUDP, 4);
	
	// 执行到此,中断注册成功, 配置GPFUPD寄存器为上拉
	*gpfupd &= ~ ( (3 << 0) | (3 << 2) | (3 << 4) | (3 << 6) | (3 << 8) );
	*gpfupd |= ((2 << 0) | (2 << 2) | (2 << 4) | (2 << 6) | (2 << 8));
	
	return 0;	
}


static void s3c2416_key_exit(void)
{
	int i;
	
	// 释放申请的中断资源	
	for(i = 0; i < sizeof(key_irqs)/sizeof(key_irqs[0]); i ++)
	{
		free_irq(key_irqs[i].irq,(void *)&key_irqs[i]);
	}
	
	// 2. 释放映射的IO内存空间
	iounmap(gpfcon);
	iounmap(gpfdat);
	iounmap(gpfupd);
	
	// 删除定时器
	del_timer(&key_timer);
	
	// 取消注册 
	input_unregister_device(key_input_dev);
	
	// 释放input dev
	input_free_device(key_input_dev);	
}

module_init(s3c2416_key_init);
module_exit(s3c2416_key_exit);

MODULE_LICENSE("GPL");


</span>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值