Linux驱动之input输入子系统

时间:2022-08-14 17:53:34

input输入子系统在实际项目中用的也比较多,按键,触摸屏,鼠标,键盘等,用来实现内核层和应用层数据之间的传递,这里得说明不只有input,还有copy_to_user等,利用input的好处是我们用自己上传数据到应用程序, 我们直接上报这个事件发生了,input自带的机制会实现上传的功能。还有很多开源的工具也是基于input输入来制作的,像tslib触摸检测程序和提取数据。


tips:不要启动QT程序,否则会出错,用cat /dev/tty1 来测试。

驱动程序:

/* 参考drivers\input\keyboard\gpio_keys.c */

#include <linux/module.h>

#include <linux/version.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <asm/gpio.h>
#include <asm/io.h>
#include <linux/kernel.h>/*内核有关的*/
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <asm/io.h>
#include <asm/uaccess.h>  //copy_to_user
#include <mach/regs-gpio.h>/*寄存器设置*/
#include <mach/hardware.h>//s3c2410_gpio_getpin等的定义
#include <mach/irqs.h> //IRQ_EINT0等的定义
#include <asm/system.h>

struct pin_desc{     /* 定义一个结构体类型 */
int irq;
char *name;
unsigned int pin;
unsigned int key_val;
};

struct pin_desc pins_desc[4] = {      /* 定义这种类型的结构体数组并赋值 */
{IRQ_EINT0,  "S0", S3C2410_GPF0,   KEY_L},
{IRQ_EINT2,  "S2", S3C2410_GPF2,   KEY_S},
{IRQ_EINT3,  "S3", S3C2410_GPF3,   KEY_ENTER},
{IRQ_EINT4,  "S4", S3C2410_GPF4,   KEY_LEFTSHIFT},
};
static struct input_dev *buttons_dev;     /* 定义input_dev类型的结构体指针 */
static struct pin_desc *irq_pd;
static struct timer_list buttons_timer;
static irqreturn_t buttons_irq(int irq, void *dev_id)   /* 定时器,这里用来消除抖动 */
{
/* 10ms后启动定时器 */
irq_pd = (struct pin_desc *)dev_id;
mod_timer(&buttons_timer, jiffies+HZ/100);
return IRQ_RETVAL(IRQ_HANDLED);
}
static void buttons_timer_function(unsigned long data)
{
struct pin_desc * pindesc = irq_pd;
unsigned int pinval;
if (!pindesc)
return;
pinval = s3c2410_gpio_getpin(pindesc->pin);
if (pinval)
{
/* 松开 : 最后一个参数: 0-松开, 1-按下 */
input_event(buttons_dev, EV_KEY, pindesc->key_val, 0);
input_sync(buttons_dev);

}
else
{
/* 按下 */
input_event(buttons_dev, EV_KEY, pindesc->key_val, 1);
input_sync(buttons_dev);

}
}
static int buttons_init(void)   /* 初始化函数,硬件初始化,注册中断,分配内存 */
{
int i;

/* 1. 分配一个input_dev结构体 */
buttons_dev = input_allocate_device();  /*  */
/* 2. 设置 */
/* 2.1 能产生哪类事件 */
set_bit(EV_KEY, buttons_dev->evbit);
//set_bit(EV_REP, buttons_dev->evbit);
/* 2.2 能产生这类操作里的哪些事件: L,S,ENTER,LEFTSHIT */
set_bit(KEY_L, buttons_dev->keybit);
set_bit(KEY_S, buttons_dev->keybit);
set_bit(KEY_ENTER, buttons_dev->keybit);
set_bit(KEY_LEFTSHIFT, buttons_dev->keybit);
/* 3. 注册 */
input_register_device(buttons_dev);

/* 4. 硬件相关的操作 */
init_timer(&buttons_timer);
buttons_timer.function = buttons_timer_function;
add_timer(&buttons_timer);
for (i = 0; i < 4; i++)
{
request_irq(pins_desc[i].irq, buttons_irq, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, pins_desc[i].name, &pins_desc[i]);
}
return 0;
}
static void buttons_exit(void)   /* 退出函数,取消中断的注册,释放内存 */
{
int i;
for (i = 0; i < 4; i++)
{
free_irq(pins_desc[i].irq, &pins_desc[i]);
}
del_timer(&buttons_timer);
input_unregister_device(buttons_dev);   /* 取消注册的结构体 */
input_free_device(buttons_dev);           /* 取消分配的结构体 */

}

module_init(buttons_init);
module_exit(buttons_exit);
MODULE_LICENSE("GPL");


注册input_dev或input_handler时,会两两比较左边的input_dev和右边的input_handler,
根据input_handler的id_table判断这个input_handler能否支持这个input_dev,
如果能支持,则调用input_handler的connect函数建立"连接"


上面调用input上报事件最终都将调用到input_sync最终也将调用input_envent,该代码还有一个问题,细心的朋友应该看到了,怎么没有看到休眠和唤醒之类的代码呢?这些稳定的部分内核里面已经自带了,在input_event里面来实现唤醒的,这些内核在稳定部分已经实现了,我们只需读到数据直接上报就行了,具体应用程序怎么取读是应用层的事情。


unsigned long evbit[NBITS(EV_MAX)];   // 表示能产生哪类事件
unsigned long keybit[NBITS(KEY_MAX)]; // 表示能产生哪些按键
unsigned long relbit[NBITS(REL_MAX)]; // 表示能产生哪些相对位移事件, x,y,滚轮
unsigned long absbit[NBITS(ABS_MAX)]; // 表示能产生哪些绝对位移事件, x,y


#define EV_SYN 0x00      /* 同步类事件 */
#define EV_KEY 0x01/* 按键类事件 */
#define EV_REL 0x02/* 相对位移类事件 */
#define EV_ABS 0x03/* 绝对位移类事件 */


set_bit(EV_KEY, buttons_dev->evbit);                    /* 能产生按键类事件 */
set_bit(EV_REP, buttons_dev->evbit); /* 能产生重复类事件 */


set_bit(KEY_L, buttons_dev->keybit);            /*能产生(KEY_L这些事件*/
set_bit(KEY_S, buttons_dev->keybit);  /*能产生KEY_S这些事件*/
set_bit(KEY_ENTER, buttons_dev->keybit);     /*能产生KEY_ENTER这些事件*/
set_bit(KEY_LEFTSHIFT, buttons_dev->keybit);    /*能产生KEY_LEFTSHIFT这些事件*/

input_sync(buttons_dev);            /* 上报同步类事件 */

测试方法如下:

1. 
hexdump /dev/event1  (open(/dev/event1), read(), )
           秒        微秒    类  code    value
0000000 0bb2 0000 0e48 000c 0001 0026 0001 0000
0000010 0bb2 0000 0e54 000c 0000 0000 0000 0000
0000020 0bb2 0000 5815 000e 0001 0026 0000 0000
0000030 0bb2 0000 581f 000e 0000 0000 0000 0000


2. 如果没有启动QT:
cat /dev/tty1
按:s2,s3,s4
就可以得到ls


或者:
exec 0</dev/tty1                 /* 把标准输入文件改为tty1,0代表标准输入,1代表标准输出,2代表标准错误 */
然后可以使用按键来输入