当前位置: 首页 > article >正文

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


http://www.kler.cn/a/388763.html

相关文章:

  • LabVIEW开发相机与显微镜自动对焦功能
  • [前端]NodeJS常见面试题目
  • 【图像压缩感知】论文阅读:Self-supervised Scalable Deep Compressed Sensing
  • 前端常用布局模板39套,纯CSS实现布局
  • 深入理解接口测试:实用指南与最佳实践5.0(一)
  • 【深度解析】CSS工程化全攻略(1)
  • 计算机毕业设计 | SpringBoot社区物业管理系统(附源码)
  • 使用Golang实现开发中常用的【实例设计模式】
  • Android下的系统调用 (syscall),内联汇编syscall
  • 开源项目OpenVoice的本地部署
  • Swift中的Combine
  • GISBox一站式解决GIS数据处理问题
  • 基于Zynq FPGA的雷龙SD NAND存储芯片性能测试
  • 直接插入排序法
  • 解方程(C语言)
  • JavaScript API部分知识点
  • 第三百一十九节 Java线程教程 - Java线程中断
  • element-ui-plus给头像avatar增加头像框
  • 红黑树的平衡之舞:数据结构中的优雅艺术
  • Linux进程信号(信号的产生)
  • 你对安装在自己网站上的wordpress插件了解吗?
  • 【C语言】浮点型数据存储 和 整型数据存储的区别
  • 基于深度学习的路面裂缝检测算法matlab仿真
  • C#-运算符重载
  • Redis运行时的10大重要指标
  • 1688拿货经验分享:亚马逊中小卖家如何选合作厂家