笔记整理—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记录了内核注册的中断。

相关推荐
酷酷学!!!4 分钟前
Linux基础指令(汇总)
linux·运维·服务器
枫叶丹411 分钟前
【在Linux世界中追寻伟大的One Piece】手写序列化与反序列化
linux·运维·网络
code_snow42 分钟前
STM32--JLINK使用、下载问题记录
stm32·单片机·嵌入式硬件
夜流冰1 小时前
工程师 - 智能家居方案介绍
笔记
韦德斯1 小时前
嵌入式Linux的RTC读写操作应用
linux·运维·c语言·arm开发·实时音视频
程序员JerrySUN1 小时前
熟悉的 Docker,陌生的 Podman
linux·docker·容器·系统架构·podman
a_安徒生1 小时前
window系统改为Linux系统
linux·windows·centos·系统安全
C++忠实粉丝1 小时前
计算机网络socket编程(2)_UDP网络编程实现网络字典
linux·网络·c++·网络协议·计算机网络·udp
哎呦喂-ll1 小时前
Linux进阶:常用操作
linux·运维·服务器
m0_644697331 小时前
DNS域名解析服务器
linux·运维·服务器