Linux驱动之input输入子系统

简介:

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代表标准错误 */
然后可以使用按键来输入

目录
相关文章
|
2月前
|
Linux 网络安全 虚拟化
适用于Linux的Windows子系统(WSL1)的安装与使用记录
并放到启动文件夹,就可以开机自动启动了。
56 0
|
4月前
|
Java Linux API
Linux设备驱动开发详解2
Linux设备驱动开发详解
49 6
|
4月前
|
消息中间件 算法 Unix
Linux设备驱动开发详解1
Linux设备驱动开发详解
52 5
|
4月前
|
Ubuntu Linux 虚拟化
安装Windows Linux 子系统的方法:适用于windows 11 版本
本文提供了在Windows 11系统上安装Linux子系统(WSL)的详细步骤,包括启用子系统和虚拟化功能、从Microsoft Store安装Linux发行版、设置WSL默认版本、安装WSL2补丁,以及完成Ubuntu的首次安装设置。
1042 2
|
4月前
|
Ubuntu NoSQL Linux
Linux内核和驱动
Linux内核和驱动
31 2
|
4月前
|
数据采集 Linux
Linux源码阅读笔记20-PCI设备驱动详解
Linux源码阅读笔记20-PCI设备驱动详解
|
5月前
|
存储 JSON Linux
|
5月前
|
Oracle 关系型数据库 Linux
讲解linux下的Qt如何编译oracle的驱动库libqsqloci.so
通过这一连串的步骤,可以专业且有效地在Linux下为Qt编译Oracle驱动库 `libqsqloci.so`,使得Qt应用能够通过OCI与Oracle数据库进行交互。这些步骤适用于具备一定Linux和Qt经验的开发者,并且能够为需要使用Qt开发数据库应用的专业人士提供指导。
159 1
讲解linux下的Qt如何编译oracle的驱动库libqsqloci.so
|
3月前
|
Linux API
Linux里的高精度时间计时器(HPET)驱动 【ChatGPT】
Linux里的高精度时间计时器(HPET)驱动 【ChatGPT】
|
4月前
|
Linux
【linux】【驱动】<specifier>-map-pass-thru讲解
【linux】【驱动】<specifier>-map-pass-thru讲解
23 0