// cat /proc/bus/input/devices 列出当前系统下注册的所有输入设备
/* 测试方法,将当前终端的标准输入重定向到驱动框架所产生的tty设备上
* exec 0</dev/tty1
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <mach/regs-gpio.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <mach/irqs.h>
#include <linux/input.h>
// cat /proc/bus/input/devices 列出当前系统下注册的所有输入设备
/* 测试方法,将当前终端的标准输入重定向到驱动框架所产生的tty设备上
* exec 0</dev/tty1
*/
//#define S3C_ADDR_BASE 0xF6000000
//#define S3C_ADDR(x) (S3C_ADDR_BASE + (x))
//#define S5P_VA_GPIO2 S3C_ADDR(0x02240000) //0x11840000
//GPX3CON 0x11000C60
#define GPX3CON (S5P_VA_GPIO2 + 0x0c60) //0x11840000+0x0c60=0x11840C60
#define GPX3DAT (S5P_VA_GPIO2 + 0x0c64)
struct ldm_info {
struct input_dev *dev;
};
static struct ldm_info ldm;
struct key_info {
const char *name;
size_t code; //键值
bool (*read_stat)(struct key_info*);//声明一个函数指针
unsigned long reg; //本按键所属的状态寄存器的虚拟地址
size_t ctrlbit; //记录各按键在各自所属的寄存器中的对应bit
int irqno; //中断号
};
//返回按键的状态:按住时为0,抬起时为1
static bool stat(struct key_info *pkey)
{
u32 data = readl(pkey->reg);//从内存映射的 I/O 空间读取数据,readl 从 I/O 读取 32 位数据 ( 4 字节 )
return data & pkey->ctrlbit;
}
static struct key_info key[] = {
{"KEY_L", KEY_L, stat, (unsigned long)GPX3DAT, 1 << 2, IRQ_EINT(26)},
{"KEY_S", KEY_S, stat, (unsigned long)GPX3DAT, 1 << 3, IRQ_EINT(27)},
{"KEY_ENTER", KEY_ENTER, stat, (unsigned long)GPX3DAT, 1 << 4, IRQ_EINT(28)},
{"KEY_BACKSPACE", KEY_BACKSPACE, stat, (unsigned long)GPX3DAT, 1 << 5, IRQ_EINT(29)},
};
static irqreturn_t key_handler(int irqno, void *arg)
{
struct key_info *pkey = (struct key_info *)arg;
//读出触发中断的当前按键的状态
if (pkey->read_stat(pkey)) { //抬起
//value=1表示按下,=0表示抬起,硬件状态应根据寄存器反应的电平状态来判断
input_report_key(ldm.dev, pkey->code, 0);
} else { //按下
input_report_key(ldm.dev, pkey->code, 1);
}
//哨兵event
input_sync(ldm.dev);
return IRQ_HANDLED;
}
static int __init ldm_init(void)
{
printk("%s %s\n", __FUNCTION__, __FILE__);
int ret = 0;
//1 创建输入设备对象
ldm.dev = input_allocate_device();
if (!ldm.dev) {
printk("input_allocate_device failed\n");
ret = -ENOMEM;
goto err_input_allocate_device;
}
//2 对象初始化
ldm.dev->name = "ldm_key";
//2.1 定义输入设备的输入类型
//有按键类型的操作
set_bit(EV_KEY, ldm.dev->evbit);
//支持连发
set_bit(EV_REP, ldm.dev->evbit);
//注册本设备所拥有的所有按键键值
ssize_t i = 0;
for (i = 0; i < ARRAY_SIZE(key); ++i) {
set_bit(key[i].code, ldm.dev->keybit);
}
//3 硬件初始化,中断申请,按下抬起都触发中断
for (i = 0; i < ARRAY_SIZE(key); ++i) {
ret = request_irq(key[i].irqno, key_handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, key[i].name, (void*)(key + i));
if (ret < 0) {
printk("request_irq %d faild\n", i);
goto err_request_irq;
}
}
//4 注册输入设备
ret = input_register_device(ldm.dev);
if (ret < 0) {
printk("input_register_device failed\n");
goto err_input_register_device;
}
return 0;
err_input_register_device:
err_request_irq:
for (i = i - 1; i >= 0; --i) {
free_irq(key[i].irqno, (void*)(key + i));
}
input_free_device(ldm.dev);
err_input_allocate_device:
return ret;
}
static void __exit ldm_exit(void)
{
printk(KERN_DEBUG "%s %s\n", __FUNCTION__, __FILE__);
input_unregister_device(ldm.dev);
ssize_t i = ARRAY_SIZE(key);//ARRAY_SIZE求设备结构体中设备的个数
for (i = i - 1; i >= 0; --i) {
free_irq(key[i].irqno, (void*)(key + i));
}
input_free_device(ldm.dev);
}
module_init(ldm_init);
module_exit(ldm_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xiangtan da xue chenhaipan");
MODULE_VERSION("2017.5.4");