笔记整理—linux驱动开发部分(10)input子系统与相关框架

关于输入类设备的系统有touch、按键、鼠标等,在系统中,命令行也是输入类系统。但是GUI的引入,不同输入类设备数量不断提升,带来麻烦,所以出现了struct input_event。

复制代码
struct input_event {
	struct timeval time;//内核用于描述时间点的时间结构体
	__u16 type;//什么类型的事件(如案件类)
	__u16 code;//什么按键(如按键1)
	__s32 value;//值(按下)
};//去描述一次输入类事件

input子系统分为四个部分:应用层、input_event(事件,是驱动层到应用层)+input_core(核心,就是框架)+硬件驱动。

中断事件去唤醒input子系统,从驱动->input_core->input_event->应用层,向应用层返回一个input_event。

事件驱动里的GUI框架:QT(信号与槽),VC等。等待事件发生,执行下一步过程,等待过程中,处于平衡状态。应用层的信号与槽可以理解为嵌入式系统中的中断与中断处理程序。

应用层的使用方法:/device设备文件;/sys属性文件。

但是input子系统等的为/dev/input/xxx(event n n=0、1、2......)。使用cat去确认event对应的设备,但是cat去read一个input设备时,若无输入则会阻塞,直到有输入信息出现。

复制代码
int fd = -1, ret = -1;
struct input_event ev;

fd = open(DEVICE_KEY, O_RDONLY);
if (fd < 0)
{
	perror("open");
	return -1;
}
	
while (1)
	{
	//读取一个event事件包
	memset(&ev, 0, sizeof(struct input_event));
	ret = read(fd, &ev, sizeof(struct input_event));
	if (ret != sizeof(struct input_event))
	{
		perror("read");
		close(fd);
		return -1;
	}
		
		// 解析event包,才知道发生了什么样的输入事件
		printf("%s.\n", (unsigned char *)&ev);	
	}
	
	//关闭设备
close(fd);

input子系统框架:

首先,确认一个三层思想:最上层的输入事件驱动层;中间的输入核心层;最下层的输入设备驱动层。输入事件驱动层evdev.c mousedev.c 其被剥离与下面两层。输入核心层是input.c输入核心层解析,而这两层是内核相关层,维护归属于内核开发者,。最下层是输入设备驱动类,有各种文件夹,里面有各种设备驱动,归属于驱动开发者进行维护。

因为输入事件驱动层存在四种平行层,所以应剥离于输入核心层,不同设备以适应不同的特性(Keyboard Hander、Mouse Hander;Joystick Hander;Event Hander)。最后的Event Hande模型达到了最大的兼容,可以兼容上面3个模型。

一个事件支持一对多模型发送到应用层。

在开发驱动过程中,只需要去写/改最下层,上中两层是内核开发人员进行维护的,中间的一层只是为驱动写了一些接口,模型已经定义完成,开发者核心工作将是在驱动的调优方向。

输入核心层以一个模块编译到内核中input_input();class_register()注册了input类/sys/class/input;input_proc_init()是procfs初始化;register_chrdev()注册字符类设备。

设备驱动层接口函数(在中间层实现)

复制代码
input_allocate_device();//申请dev,初步初始化input_dev
input_set_capability();//设置输入事件能力(接收上面事件)可多次调用
input_register_device();//注册dev
顺序执行上述代码

一个鼠标最少应调用input_set_capabitity 4次声明能力为BTN_LEFT;BTN_RIGHT;REL_X;REL_Y这四个宏定义。

__set_bit(EV_SYN,dev_evbit)在register_device中调用,使dev有发送同步包的能力。

复制代码
init_timer();//内核定时器
list_add_tail();//添加链表完成注册
list_for_each_entry();//遍历dev与handler方法匹配
input_attach_handler();//
    handler->comect();//最终实现device与handler挂接

input_match_device()中进行关于总线、厂商等的对比,最终实现handler匹配,其位置在handler->connect()之前。

事件驱动层接口函数:

复制代码
int input_register_handler(struct input_handler *handler);
int input_register_handle(struct input_handle *handle);


input_register_handler();
    INIT_LIST_HEAD(&handler->h_list);
    list_add_tail(&handler->node, &input_handler_list);
        input_table[8];//指针数组,指向input_handler(表示最多允许注册8种handler)大多数用event_handler

一个硬件匹配两个handler会匹配2个设备号(次)与各自设备文件相绑定:

复制代码
handler->minor>>5    =>minor/32=input_table[下标]。

注册handler时去dev_list()在找匹配对象。注册dev时去handler_list()中找匹配对象。

input_register_handle用于处理dev与handler关系。

事件驱动层框架 evdev.c/mouse.c是一种handler。

复制代码
evdev_init()
    input_register_handler()核心层已经实现,handler去调用

input_handler结构体:

复制代码
struct input_handler {

	void *private;//指向一个结构体

	void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);//硬件信息加工
	bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
	bool (*match)(struct input_handler *handler, struct input_dev *dev);//支持自有match
	int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);//匹配上连接
	void (*disconnect)(struct input_handle *handle);//断开连接
	void (*start)(struct input_handle *handle);

	const struct file_operations *fops;//对应应用层使用方法
    //一些设备信息
	int minor;
	const char *name;

	const struct input_device_id *id_table;//handler支持设备特征,用于match匹配

    //handler与dev链表
	struct list_head	h_list;
	struct list_head	node;
};

.read方法:①获取信息;②信息校验(结构体大小、client等);③input_event_to_user将event发送到用户层,wait_evebt_interruptible等待event信息(应用层等待事件实现)在.event中唤醒。

.connect方法,在匹配上后调用(match):①minor校验;②内存开辟;③数据填充;④MKDEV填充次设备号;⑤device_initialize()+⑦device_add完成device_register;⑥input_register_handle注册handle放链表中。

.event方法,封装硬件层信息为struct发送到user:①获取驱动信息(时间部分-内核时间);②evdev_pass_event()发送到那个handler(支持多个handler发送)是一种通知方式(放buffer)用wake_lock_timeout设置唤醒时钟,kill_fasync()发异步通知(谁关注发谁)异步通知+多路IO复用。

在X210中,官方实现的按键发送值与规范方法不同,不是很规范。

复制代码
.probe=s3c_button_probe

platform+input总线实现。driver+dev=>probe=驱动;input+驱动=>发包应用层。在x210按键消息可见Button_x210.c。

复制代码
GPIO_SFN(n)//模式
BITS_TO_LONGS(X)//几个32为的long能放下x个bit数据,有余数就向上兼容
set_bit();//向位图设置相应位

handler与dev匹配通过input->id.bustype ;input->id.vendor;input->id.product;input->id.version,进行匹配。

.probe①申请GPIO;②设置GPIO;③申请input空间;④填空input;⑤注册input;⑥启动定时器(等待一定时间完成消抖或轮询)。

/proc/interrupts记录了内核注册的中断。

相关推荐
2302_799525743 分钟前
【Hadoop】Hadoop集群安装中出现的问题
linux·hadoop
刘一说9 分钟前
Linux调试命令速查:Java/微服务必备
java·linux·微服务
枫の准大一20 分钟前
【Linux游记】基础指令篇
linux
lingzhilab20 分钟前
零知开源——基于STM32F103RBT6的智能风扇控制系统设计与实现
stm32·单片机·嵌入式硬件
汇能感知24 分钟前
光谱相机的探测器类型
经验分享·笔记·科技
ypf520831 分钟前
OrbStack 配置国内镜像加速
linux
Hello.Reader36 分钟前
一文通关 Proto3完整语法与工程实践
java·linux·数据库·proto3
Hello.Reader42 分钟前
一文吃透 Protobuf “Editions” 模式从概念、语法到迁移与实战
linux·服务器·网络·protobuf·editions
jzzy_hony1 小时前
移植Qt4.8.7到ARM40-A5
qt·ubuntu·arm·终端
陌上花开缓缓归以1 小时前
linux ubi文件系统
linux