一、何为输入子系统,有什么作用
1.input子系统
linux内核中已经实现了一个子系统,它在内核驱动的实现是:
- 注册了主设备(13已经被它占用了),分配了1024个次设备号
- 它注册了class类,实现了里面的fops功能
- 但是就是没有注册device也就是节点(一个也没有)这就是留给驱动工程师要做的。(注册的device在/dev/input下自己判断是哪一个)
struct class input_class = {
.name = "input",
.devnode = input_devnode,
};
.....
.....
err = class_register(&input_class);
err = input_proc_init();
err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),INPUT_MAX_CHAR_DEVICES, "input");//INPUT_MAX_CHAR_DEVICES=1024
2.功能
它内核实现了各种fops,只需要驱动层发送指定的信号,然后应用层可以直接read struct input_event结构体,然后用户就可以在应用空间用switch判断是哪种输入,又对应哪个,是什么值等信息,然后进行处理。
这大大减轻了驱动工程师的工作,复杂的各种信号分情况处理就变得很简单了。
二、编写流程
//注册
struct input_dev *inputdev;
//1.申请一个input_dev
inputdev = input_allocate_device();
//2.自己初始化
inputdev->name = "inputdev0";
inputdev->evbit[0] = BIT_MASK(EV_KEY);//设置为按键类输入
input_set_capability(inputdev, EV_KEY, KEY_0);//设置按键类中的key0
//3.注册inputdev
input_register_device(inputdev);
//发送数据
input_event(inputdev, EV_KEY, KEY_0, value);
//同步停止发送
input_sync(inputdev);
//注销与释放
/* 释放 input_dev */
input_unregister_device(inputdev);//注销
input_free_device(inputdev);//释放
三、程序
1.driver
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/poll.h>
#include <linux/init.h>
#include <linux/major.h>
#include <linux/of.h>
#include <linux/timer.h>
#include <linux/input.h>
//不借助硬件
//1.使用 input_allocate_device 函数申请一个 input_dev。
//2.初始化 input_dev 的事件类型以及事件值。
//3.使用 input_register_device 函数向 Linux 系统注册前面初始化好的 input_dev。
struct input_dev *inputdev;
static int timeperiod;
//定义一个内核定时器变量
struct timer_list timer;
int value=0;
void timer_function(struct timer_list *t)
{
printk("timer_function runing!\n");
//按照一定时间重启定时器
mod_timer(&timer, jiffies+msecs_to_jiffies(timeperiod));//msecs_to_jiffies将ms换成节拍
//发送KEY_0=1的事件给对应字设备
input_event(inputdev, EV_KEY, KEY_0, value);
//同步停止发送
input_sync(inputdev);
value = !value;
}
void Timer_init(void)
{
//初始化定时器变量
timer_setup(&timer,timer_function,0);//4.14内核之后
//设置定时器初始时间
timeperiod = 5000;//单位ms
//mod_timer与add_timer都可以激活定时器,对应del_timer
}
static __init int my_input_init(void)
{
//1.申请一个input_dev
inputdev = input_allocate_device();
//2.自己初始化
inputdev->name = "inputdev0";
inputdev->evbit[0] = BIT_MASK(EV_KEY);//设置为按键类输入
input_set_capability(inputdev, EV_KEY, KEY_0);//设置按键类中的key0
//3.注册inputdev
input_register_device(inputdev);
//初始化定时器,每5s发送一次input信号给对应设备节点
Timer_init();
//开启定时器
mod_timer(&timer, jiffies+msecs_to_jiffies(timeperiod));
return 0;
}
static __exit void my_input_exit(void)
{
del_timer_sync(&timer);//注销定时器
/* 释放 input_dev */
input_unregister_device(inputdev);//注销
input_free_device(inputdev);//释放
}
module_init(my_input_init);
module_exit(my_input_exit);
MODULE_LICENSE("GPL");
2.test.c
#include <linux/input.h>
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "sys/ioctl.h"
#include "fcntl.h"
static struct input_event inputevent;
int main(int argc,char **argv)
{
int err;
int fd;
if(argc!=2)
{
printf("./test /dev/xx\n");
return -1;
}
fd = open(argv[1],O_RDWR);
if(fd<0)
{
printf("open error\n");
return -1;
}
while(1)
{
err = read(fd, &inputevent, sizeof(inputevent));
if(err>0)
{
switch (inputevent.type)
{
case EV_KEY:
if(inputevent.code==KEY_0)
{
printf("KEY_0 value is %d\n",inputevent.value);
}
break;
/* 其他类型的事件,自行处理 */
case EV_REL:
break;
case EV_ABS:
break;
case EV_MSC:
break;
case EV_SW:
break;
}
}
}
close(fd);
}
程序讲解
加载模块后,定时器开始,5s发送一次key_0信号,轮流数0/1(1表示按下)
然后在应用打印出发送的key_0还是1
有趣现象
在key_0是1时,命令行不断打印 “00000000000000000000000000000000”
我怀疑时这个input子系统的发送信号是针对所有input下的节点的,他们都能接收到这个信号,所以有关键盘的应用程序调用获取到这个"欺骗信号"时以为时key_0按下了就不停打印0。