Linux 输入子系统分析(一)

Linux 输入子系统分析(一)
Linux 输入子系统分析(二)
分析一个内核提供的input_handler

Linux内核输入子系统

输入设备(如按键、键盘、触摸屏、鼠标等)是典型的字符设备,其一般的工作机理是底层在按键、触摸等动作发送时产生一个中断,然后CPU通过USB、I2C等读取键值、坐标等数据,并将它们放入一个缓冲区,字符设备驱动管理该缓冲区,而驱动的read接口让用户可以读取键值、坐标等数据。

显然,在这些工作中,只是中断、读键值/坐标值是与设备相关的,而输入事件的缓冲区管理以及字符设备驱动的file_operations接口则对输入设备是通用的。基于此,内核设计了输入子系统,由核心层处理公共的工作。Linux内核输入子系统的框架如下图所示。

在这里插入图片描述
linux输入子系统从上到下由三层实现,分别为:输入子系统事件处理层(Event handler)、输入子系统核心层(Input core)和输入子系统设备驱动层(Input driver)。

驱动层

对于输入子系统设备驱动层而言,主要实现对硬件设备的读写访问,中断设置,并把硬件产生的事件转换为核心层定义的规范提交给事件处理层。将底层的硬件输入转化为统一事件形式,想输入核心汇报。

输入子系统核心层

对于核心层而言,为设备驱动层、事件处理层提供了规范和接口。设备驱动层只要关心如何驱动硬件并获得硬件数据,然后调用核心层提供的接口,核心层会自动把数据提交给事件处理层,它起到承上启下的作用。

事件处理层

对于事件处理层而言,提供用户编程的接口(设备节点),并处理驱动层提交的数据处理。

主要数据结构

input_dev结构体,描述一个输入设备,定义如下:

struct input_dev {

	//设备name
	const char *name;

	......

	//用于与input_handler匹配
	struct input_id id;

	......
	
	/* evbit位图,表示设备支持的输入事件类型
     如:设备支持按键事件类型,则set_bit(EV_KEY, input_dev ->evbit);
   */
	unsigned long evbit[BITS_TO_LONGS(EV_CNT)];

	/* keybit位图,表示设备有哪些键值
     如,设备支持'L'这个键值,则通过set_bit(KEY_L, key_input_dev->keybit)设置键值
   */
	unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];

	//relbit、absbit等与keybit类似
	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 repeat_key;

	//用于实现长按的定时器(对于EV_KEY)
	struct timer_list timer;

	
	......

	/* 用于表示当前设备按键的状态的位图
     调用input_report_key函数上报按键时,会设置该状态位图
   */
	unsigned long key[BITS_TO_LONGS(KEY_CNT)];
	
	//led、snd等与key类似
	unsigned long led[BITS_TO_LONGS(LED_CNT)];
	unsigned long snd[BITS_TO_LONGS(SND_CNT)];
	unsigned long sw[BITS_TO_LONGS(SW_CNT)];

	//应用层第一次open设备时,会调用该函数
	int (*open)(struct input_dev *dev);

	//应用层close设备时,会调用该函数
	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);

	//如果设置了该项,表示设备上报的事件,全都由该input_handle来处理
	struct input_handle __rcu *grab;

	.......

	//链接与该设备相关的input_handle
	struct list_head	h_list;

	//用于链入全局链表input_dev_list的节点
	struct list_head	node;

	......

}

input_handler结构体,描述一个输入事件处理接口。怎么理解呢?输入设备会上传事件,输入核心层会把这个事件交给事件处理层来处理,最终交给的对象就是这个input_handler,input_handler里面有许多的成员函数,通过这些成员函数来处理事件。交给一个哪些input_handler处理呢?这里就涉及到匹配的问题,即input_handler与input_dev的匹配。内核为一些常用的输入设备提供了对应的input_handler,如keyboard.c、mousedev.c等。

input_handler结构体的定义如下:

struct input_handler {

	void *private;
	
	//设备上报的event,最终由该函数处理处理
	void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
	void (*events)(struct input_handle *handle,
		       const struct input_value *vals, unsigned int count);

	//用于过滤某些event
	bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
		
	//用于进一步匹配设备
	bool (*match)(struct input_handler *handler, struct input_dev *dev);

	//与设备匹配成功,调用connect函数
	int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
	
	void (*disconnect)(struct input_handle *handle);

	//注册handle时,会调用start
	void (*start)(struct input_handle *handle);

	......

	//id表,用于匹配input_dev
	const struct input_device_id *id_table;

	//链接与该input_handler相关的input_handle
	struct list_head	h_list;
	
	struct list_head	node;
};

input_handle结构体,内核设计这一数据结构,用于为匹配的input_handler与input_dev建立桥梁。通过这一结构体,input_handler或input_dev可以找到匹配的双方,结构体定义如下:

struct input_handle {

	void *private;

	int open;
	const char *name;

	struct input_dev *dev;
	struct input_handler *handler;

	//通过该节点,链入input_dev的h_list链表
	struct list_head	d_node;

	//通过该节点,链入input_handler的h_list链表
	struct list_head	h_node;
};

input_dev、input_handle与input_handler之间的关系如下图:
在这里插入图片描述
可以通过遍历input_dev的h_list链表找个与该input_dev匹配的input_handler。

在这里插入图片描述
当然,对于input_handler,也可以遍历其h_list链表,找到与之匹配的input_dev。

input_event结构体,描述一个事件,定义如下:

struct input_event {
	
	//时间戳
	struct timeval time;

	//输入事件类型
	__u16 type;

	__u16 code;

	__s32 value;
};

输入设备各种各样,对应的输入事件类型也有所不同,内核定义了几乎所有能涵盖的输入事件类型:

#define EV_SYN			0x00     //同步事件
#define EV_KEY			0x01     //按键事件
#define EV_REL			0x02     //相对坐标(如:鼠标移动,报告相对最后一次位置的偏移)
#define EV_ABS			0x03     //绝对坐标(如:触摸屏或操作杆,报告绝对的坐标位置)
#define EV_MSC			0x04     //其它
#define EV_SW			0x05     //开关
#define EV_LED			0x11     //按键/设备灯
#define EV_SND			0x12     //声音/警报
#define EV_REP			0x14     //重复
#define EV_FF			0x15     //力反馈
#define EV_PWR			0x16     //电源
#define EV_FF_STATUS	0x17     //力反馈状态
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值