1.概述
1.1 什么叫做输入子系统
简单来说,输入子系统就是统一各种各样的输入设备的系统;
常见的输入设备比如: 键盘、触摸屏、按键、鼠标。
1.2 为什么要引入输入子系统
每个人写驱动代码都有自己的风格和习惯,导致代码会有一定的差异,举个简单的例子:
同样的人写一个驱动,当按键按下时,有人给应用层返回1,有些人就会返回0;
输入子系统就是为了统一这个输入。当按键按下去之后 ,驱动层不直接给应用层返回一个信息,而是告诉输入子系统,由输入子系统做了统一之后再告诉应用层。
2.相关API
2.1 向内核注册一个输入子系统
函数的头文件: <linux/input.h>
函数的原型:int input_register_device(struct input_dev *dev)
函数的参数:struct input_dev *dev:输入子系统的核心的结构体
函数的返回值:
成功返回 0
失败返回 非零
2.2 输入子系统的核心结构体的注册
这个结构体不允许自己定义,需要通过内核注册;
函数的头文件:<linux/input.h>
函数的原型:struct input_dev *input_allocate_device(void)
函数的参数:无
函数的返回值:
成功返回 输入子系统的核心结构体的指针
失败返回 NULL
下边是输入子系统的核心结构体,可以看到里面包含的内容非常多,所以需要向内核注册。
struct input_dev {
const char *name; //设备名(sys目录下的设备)
const char *phys; //sys目录下的设备路径 不需要用户填充
const char *uniq; //设备的唯一识别码
struct input_id id; //用于匹配事件处理层handler(事件处理者)
unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
//如下两个元素需重点了解
unsigned long evbit[BITS_TO_LONGS(EV_CNT)];//用于记录支持的事件类型的位图
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];//记录支持的按键值的位图(实际上就是存放所支持的具体哪些按键)
unsigned long relbit[BITS_TO_LONGS(REL_CNT)];//记录支持的相对坐标的位图(实际是支持哪些相对事件)
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];//记录支持的绝对坐标的位图(实际是支持哪些绝对事件)
unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
unsigned int hint_events_per_packet;
unsigned int keycodemax; //支持的按键值的个数
unsigned int keycodesize; //每个键值的字节数
void *keycode; //存放按键值的数组首地址
int (*setkeycode)(struct input_dev *dev,
const struct input_keymap_entry *ke,
unsigned int *old_keycode); //修改键值的函数 可选
int (*getkeycode)(struct input_dev *dev,
struct input_keymap_entry *ke); //获取扫描码的键值
struct ff_device *ff;
unsigned int repeat_key; //最近一次键值,用于连击
struct timer_list timer; //自动连击计时器
int sync; //最后一次同步后没有新的事件置1
int abs[REP_CNT]; //当前各个坐标的值
int rep[REP_CNT]; //自动连击的参数
struct input_mt_slot *mt;
int mtsize;
int slot;
int trkid;
struct input_absinfo *absinfo;
unsigned long key[BITS_TO_LONGS(KEY_CNT)];//反映当前按键状态的位图
unsigned long led[BITS_TO_LONGS(LED_CNT)]; //反映当前 led 状态的位图
unsigned long snd[BITS_TO_LONGS(SND_CNT)];//反映当前 beep 状态的位图
unsigned long sw[BITS_TO_LONGS(SW_CNT)];
int (*open)(struct input_dev *dev);//打开函数,可以不实现,如果有就调用
void (*close)(struct input_dev *dev);//关闭函数,可以不实现,如果有就调用。
int (*flush)(struct input_dev *dev, struct file *file);//断开连接时清空上报上去数据
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value); //回调函数,可选
struct input_handle __rcu *grab;
spinlock_t event_lock;
struct mutex mutex;
unsigned int users;
bool going_away;
bool sync;
struct device dev;
struct list_head h_list;//handle 链表
struct list_head node;//input_dev 链表
};
evbit:支持的事件的类型
set_bit(EV_KEY,evbit); //表示设置事件为按键事件
set_bit(EV_REP,evbit); //表示设置的事件为重复事件
输入子系统支持的事件的宏定义:
#define EV_SYN 0x00 //同步事件
#define EV_KEY 0x01 //按键事件 使用的比较多
#define EV_REL 0x02 //相对事件
#define EV_ABS 33 //绝对事件
#define EV_MSC 0x04 //其他事件
#define EV_SW 0x05 //开关事件
#define EV_LED 0x11 //按键LED灯事件
#define EV_SND 0x12 //按键声音事件
#define EV_REP 0x14 //重复事件 比较常用的
#define EV_FF 0x15 //力反馈事件
#define EV_PWR 0x16 /*电源事件*/
#define EV_FF_STATUS 0x17 /*受压状态*/
keybit:输入子系统支持的按键
设置按键支持第一个按键:set_bit(KEY_1,keybit);
2.3 输入子系统事件的上报
函数的头文件:<linux/input.h>
函数的原型:
void input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)
函数的参数:
struct input_dev *dev:输入子系统的核心结构体
unsigned int type:上报的事件的类型(EV_KEY 按键事件)
unsigned int code, :按键 也就是是哪个按键(KEY_1 KEY_2)
int value:按键的值
假如是按键事件:
0:表示按键松开
1:表示按键按下
函数的返回值:无
++++++++++++++++++++++++++++++++++++++++++++++++++++++++
例如上报一个按键事件:
函数的头文件:<linux/input.h>
函数的原型:void input_report_key(struct input_dev *dev, unsigned int code, int value)
函数的参数:
struct input_dev *dev:输入子系统的核心结构体
unsigned int code:哪个按键
int value:
0表示按键松开
1表示按键按下
函数的返回:无
++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2.4 报告同步事件
函数的头文件: <linux/input.h>
函数的原型:void input_sync(struct input_dev *dev);
函数的参数:struct input_dev *dev:输入子系统的核心的结构体
函数的返回值:无
3.下边是练习的一个例子,仅供参考
内核层:
//内核层代码:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/gpio.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/timer.h>struct input_dev *myinput=NULL;
int my_irq; //中断号
struct timer_list mytime;//定时器1回调函数
void my_fun(unsigned long data)
{int val=0;val=gpio_get_value(EXYNOS4_GPX3(2));if(val==0){//printk("按键按下了,事件上报1");input_report_key(myinput,KEY_1,1);}else if(val==1){//printk("按键松开了,事件上报0");input_report_key(myinput,KEY_1,0);}//报告同步事件input_sync(myinput);//printk("我被调用了\n");
}irqreturn_t func(int num, void *arg)
{ //定时器1mod_timer(&mytime, jiffies+msecs_to_jiffies(10)); //延时10msreturn 0;
}static int __init test_init(void)
{int ret,ret1;//定时器1mytime.expires=jiffies+msecs_to_jiffies(20); //20msmytime.function=my_fun;init_timer(&mytime); //初始化定时器add_timer(&mytime); //向内核注册一个定时器 并运行//1:中断号转换my_irq=gpio_to_irq(EXYNOS4_GPX3(2)); //key1//2:使能中断号enable_irq(my_irq);//3:注册中断ret1=request_irq(my_irq,func,IRQ_TYPE_EDGE_BOTH, "mykey", NULL);//向内核注册一个输入子系统myinput=input_allocate_device();//输入子系统的核心结构体的注册myinput->name="key";set_bit(EV_KEY,myinput->evbit);//表示设置事件为按键事件set_bit(EV_REP,myinput->evbit);//表示设置的事件为重复事件set_bit(KEY_1,myinput->keybit);//设置按键支持第一个按键ret=input_register_device(myinput);return 0;
}static void __exit test_exit(void)
{//取消注册input_unregister_device(myinput);
}module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");
应用层:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>struct input_event mydata;
int main()
{int fd;fd=open("/dev/input/event3",O_RDWR);while(1){read(fd,&mydata,sizeof(mydata));if(mydata.code == 2){if(mydata.value == 1){printf("应用层:key1按下了\n");}else if(mydata.value == 0){printf("应用层:key1松开了\n");}}}return 0;
}