<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>
Linux输入子系统——按键驱动实例
最新推荐文章于 2023-02-14 17:55:35 发布