input子系统详解4——输入事件驱动层源码分析

以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。

一、前言

由input子系统简介可知,input子系统分为三层:

1、上层输入事件驱动层

涉及的文件有x210_kernel\drivers\input\evdev.cmousedev.c joydev.c文件,分别对应上层的各个不同的handler的源代码。但如图所示,一般集中采用 event handlers的方式,但这种方式不是排他性的,可以同时存在。该层负责将 struct input_event 呈送给应用层。

2、中层框架核心层

涉及的文件有x210_kernel\drivers\input\input.c文件。

3、下层具体硬件驱动层

涉及的文件包括x210_kernel\drivers\input目录中的各个文件夹,比如joystick文件、mouse文件夹、keyboard文件夹、touchscreen文件夹等。

下面将对上层输入事件驱动层进行分析。

二、输入事件驱动层分析

input子系统的输入事件驱动层其实是由各个handler构成的,各个handler之间是属于平行关系,不存在相互调用的现象。目前用的最多是event,今天就以这个handler为例分析他的源代码,以便对handler的实现有一定的了解。其中 drivers\input\evdev.c 就是event的源代码文件。

从evdev.c文件的末尾可以看到module_init、module_exit这些宏,这说明内核中将这部分实现为模块的方式,这很好理解,因为框架核心层都实现为模块的方式,而上层依赖于框架核心层才能够注册与工作的,所以上层也需要实现为模块的方式。

1、相关数据结构

(1)struct evdev结构体

struct evdev {int exist;int open;                                //  这个是用来作为设备被打开的计数int minor;                               //   handler 与 input设备匹配成功之后创建的设备对应的device的次设备号相对于基准次设备号的偏移量struct input_handle handle;   //   内置的一个  handle ,里面记录了匹配成功的input_dev 和 handlerwait_queue_head_t wait;struct evdev_client *grab;struct list_head client_list;       //   用来挂接与 evdev 匹配成功的evdev_client 的一个链表头spinlock_t client_lock; /* protects client_list */struct mutex mutex;             //  互斥锁struct device dev;                 //  这个是handler 与 input设备匹配成功之后创建的设备对应的device
};

(2)struct evdev_client结构体

struct evdev_client {struct input_event buffer[EVDEV_BUFFER_SIZE];    //  用来存放input_dev 事件的缓冲区int head;int tail;spinlock_t buffer_lock; /* protects access to buffer, head and tail */struct fasync_struct *fasync;struct evdev *evdev;              //   evdev 指针struct list_head node;            //  作为一个链表节点挂接到相应的 evdev->client_list 链表上struct wake_lock wake_lock;char name[28];            //  名字
};

(3)struct input_event结构体

struct input_event {struct timeval time;//事件发生的时间__u16 type;//事件的类型(键盘?触摸屏?)__u16 code;//事件的码值(按键a对应的编码)__s32 value;//事件的状态、操作值(比如是按下了还是弹起了;触摸点坐标等)
};

2、模块注册函数: evdev_init()

static struct input_handler evdev_handler = {.event		= evdev_event,.connect	= evdev_connect,.disconnect	= evdev_disconnect,.fops		= &evdev_fops,.minor		= EVDEV_MINOR_BASE, //次设备号起始编号是64.name		= "evdev",.id_table	= evdev_ids,
};static int __init evdev_init(void)
{return input_register_handler(&evdev_handler);
}

其中evdev_handler这个结构体变量,就是本次分析的handler对应的结构体变量。对这个结构体变量的填充操作里,最重要的有3个:evdev_event函数、evdev_connect函数和evdev_fops变量。

3、其他函数分析

(1)evdev_connect函数分析

static int evdev_connect(struct input_handler *handler, struct input_dev *dev,const struct input_device_id *id)
{struct evdev *evdev;                 //  定义一个 evdev 指针int minor;int error;for (minor = 0; minor < EVDEV_MINORS; minor++)  //  从evdev_table 数组中找到一个没有被使用的最小的数组项  最大值32if (!evdev_table[minor])break;if (minor == EVDEV_MINORS) {printk(KERN_ERR "evdev: no more free evdev devices\n");return -ENFILE;}evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);   //  给evdev 申请分配内存if (!evdev)return -ENOMEM;INIT_LIST_HEAD(&evdev->client_list);            //  初始化 evdev->client_list 链表spin_lock_init(&evdev->client_lock);              //  初始化自旋锁 evdev->client_lockmutex_init(&evdev->mutex);                          //  初始化互斥锁 evdev->mutexinit_waitqueue_head(&evdev->wait);dev_set_name(&evdev->dev, "event%d", minor);  // 设置input设备的名字evdev->exist = 1;evdev->minor = minor;                                     //  input设备的次设备号的偏移量evdev->handle.dev = input_get_device(dev);              //  将我们传进来的 input_dev 指针存放在 evdev->handle.dev 中evdev->handle.name = dev_name(&evdev->dev);     //  设置 evdev -> dev 对象的名字,并且把名字赋值给 evdev->handle.nameevdev->handle.handler = handler;          //  将我们传进来的 handler 指针存放在 handle.handler 中evdev->handle.private = evdev;             //  把evdev 作为handle 的私有数据evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);        //  设置 evdev->device 设备的设备号evdev->dev.class = &input_class;                                                  //  将 input_class 作为 evdev->device 的设备类evdev->dev.parent = &dev->dev;                                                // 将input_dev  -> device 作为evdev->device 的父设备evdev->dev.release = evdev_free;                   //  evdev -> device 设备的卸载函数device_initialize(&evdev->dev);                      //  设备初始化error = input_register_handle(&evdev->handle);       //  注册handleif (error)goto err_free_evdev;error = evdev_install_chrdev(evdev);       // 安装evdev   其实就是将evdev 结构体指针存放在evdev_table数组当中  下标就是evdev->minorif (error)goto err_unregister_handle;error = device_add(&evdev->dev);     //  添加设备到系统          /sys/devices/virtual/input/input0/event0        event0就是表示建立的设备文件if (error)goto err_cleanup_evdev;return 0;err_cleanup_evdev:evdev_cleanup(evdev);err_unregister_handle:input_unregister_handle(&evdev->handle);err_free_evdev:put_device(&evdev->dev);return error;
}

注意,/sys/devices/virtual/input/input0  这个设备是在注册input_dev时创建的,而input0/event0就是在handler和input_dev匹配成功之后创建的,也会在/dev/目录下创建设备节点。

(2)evdev_open函数分析

static int evdev_open(struct inode *inode, struct file *file)
{struct evdev *evdev;                       //  定义一个 evdev 结构体指针struct evdev_client *client;             //   定义一个evdev_client 指针int i = iminor(inode) - EVDEV_MINOR_BASE;   //  通过inode 获取 需要打开的设备对应的evdev_table 数组中的下标变量int error;if (i >= EVDEV_MINORS)return -ENODEV;error = mutex_lock_interruptible(&evdev_table_mutex);if (error)return error;evdev = evdev_table[i];             //  从evdev_table  数组中找到evdevif (evdev)get_device(&evdev->dev);mutex_unlock(&evdev_table_mutex);if (!evdev)return -ENODEV;client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);      //  给 client 申请分配内存if (!client) {error = -ENOMEM;goto err_put_evdev;}spin_lock_init(&client->buffer_lock);snprintf(client->name, sizeof(client->name), "%s-%d",dev_name(&evdev->dev), task_tgid_vnr(current));wake_lock_init(&client->wake_lock, WAKE_LOCK_SUSPEND, client->name);client->evdev = evdev;                          //  通过client->evdev 指针指向 evdevevdev_attach_client(evdev, client);   //  其实这个函数就是做了一个链表挂接:  client->node  挂接到 evdev->client_listerror = evdev_open_device(evdev); //  打开 evdev 设备   最终就会打开 input_dev -> open 函数if (error)goto err_free_client;file->private_data = client;              //   将evdev_client 作为file 的私有数据存在nonseekable_open(inode, file);return 0;err_free_client:evdev_detach_client(evdev, client);kfree(client);err_put_evdev:put_device(&evdev->dev);return error;
}

4、总结

(1)handler为何只能够处理对应的事件?

下层可以上报的事件在我们的内核中是定义好的,我们可以上报这些事。但是input子系统的上层输入事件驱动层的各个handler只能够处理某些特定的事件(event除外)。例如,joy handler只能处理摇杆类型的事件,key handler只能处理键盘。这是因为框架核心层会进行handler和device的匹配操作。如果上报的事件与多个handler都能够匹配成功,则绑定之后框架核心层会向输入事件驱动层中的多个handler都上报事件,再由handler上报给应用层。

(2)input设备注册的流程

具体硬件驱动层利用框架核心层提供的函数,向子系统注册输入设备

/******************************************************************************/

input_register_device

    device_add:  /sys/devices/virtual/input/input0

    链表挂接: input_dev->node    ------->  input_dev_list

    input_attach_handler                          //  进行input_dev和handler之间的匹配

        调用handler->connect进行连接

            构建evdev结构体,加入evdev_table数组

            input_register_handle

            device_add:  /sys/devices/virtual/input/input0/event0

/*******************************************************************************/

(3)handler注册流程

/****************************************************************/

input_register_handler

    input_table[handler->minor >> 5] = handler

    链表挂接:  handler->node  ----->   input_handler_list

    input_attach_handler

        handler->connect                  // 调用handler的connect函数进行连接

/****************************************************************/

(4)事件如何传递到应用层

input子系统下层通过调用input_event函数向核心层上报数据。

input_event

    input_handle_event

        input_pass_event

            handler->event()            //  最终会调用到handler 中的event函数

                evdev_pass_event

                    client->buffer[client->head++] = *event;     //  会将input输入事件数据存放在evdev_client结构体中的缓冲去中

当应用层通过open打开event0这个设备节点时,最终会调用到input_init函数中注册的字符设备input时注册的file_operations->open() 函数

input_open_file

    handler = input_table[iminor(inode) >> 5]

    handler->fops->open()          

        evdev = evdev_table[i];

        evdev_open_device

            input_open_device

                input_dev->open()         //  最终就是执行input设备中的open函数

        file->private_data = evdev_client; 

所以当我们在应用层调用read函数时,最终会调用到handler->fops->read函数

evdev_read

    evdev_fetch_next_event

        *event = client->buffer[client->tail++]      //  将evdev_client->buffer中的数据取走

    input_event_to_user

        copy_to_user                  //  拷贝到用户空间

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/461147.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端翻译:Activating Browser Modes with Doctype

一、前言                         原本备份&#xff1a; http://www.cnblogs.com/fsjohnhuang/p/3830623.html 由于本人英语能力有限&#xff0c;译本内容难免有误&#xff0c;望各位指正&#xff01; 本译文不含附录部分&#xff0c;请知悉。 二、译…

java公钥加密私钥解密过程_GPG加密解密过程

GPG加密解密过程一、Linux系统下1.安装yum安装[rootPOC-ORACLE ~]# yum install gnupg下载安装包安装https://www.gnupg.org/download/index.en.html查看gpg帮助[rootPOC-ORACLE ~]# gpg --helpgpg (GnuPG) 2.0.14libgcrypt 1.4.5Copyright (C) 2009 Free Software Foundation,…

魔兽世界客户端数据研究(三)

终于决定&#xff0c;还是通过wow model viewer起手&#xff0c;研究一下WOW的数据类型&#xff0c;从另一个角度&#xff0c;体验一把这个唯一让我充过值的游戏。 这将是一系列随笔&#xff0c;即在读代码的时候&#xff0c;顺便记录&#xff0c;以理清思路和加深映象。 其中…

input子系统详解5——参考驱动模板编写按键驱动

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 一、input类设备驱动的开发 &#xff08;1&#xff09;输入事件驱动层和框架核心层不需要动&#xff0c;只需要编写具体硬件驱动层代码。 &#xff08;2&#xff09;具体硬件驱动层的编程接口与调用…

java很多魔法数判断_可别在代码中写那么多魔法值了,脑壳疼!

1. 前言重构老代码中遇到了不少类似下面这种写法&#xff1a;public void attend(String value) {if ("0".equals(value)) {//todo} else if ("1".equals(value)) {//todo} else {//todo}}脑壳疼&#xff01;从 Java 语法上无懈可击&#xff0c;但是从业务…

十分钟让你明白Objective-C的语法(和Java、C++的对比)

2019独角兽企业重金招聘Python工程师标准>>> 很多想开发iOS&#xff0c;或者正在开发iOS的程序员以前都做过Java或者C&#xff0c;当第一次看到Objective-C的代码时都会头疼&#xff0c;Objective-C的代码在语法上和Java, C有着很大的区别&#xff0c;有的同学会感觉…

稀疏多项式的运算

问题描述&#xff1a; 已知稀疏多项式Pn(X)c1x^e1c2x^e2....cmx^em,其中nem>em-1>....>e1>0; ci!0,m>1.试采用存储量同多项式项数m成正比的顺序存储结构&#xff0c;编写求Pn(x0)的算法&#xff08;x0为给定值&#xff09;&#xff0c;并分析你的算法的时间复杂…

I2C子系统详解1——I2C总线设备的驱动框架

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 参考博客 I2C总线驱动框架详解 linux内核I2C子系统详解 一、I2C总线的物理特征 这部分内容的简介可见博客&#xff1a;SPI、I2C、UART&#xff08;即串口&#xff09;三种串行总线详解。 &#x…

sqlite4java下载_使用sqlite4java的UnsatisfiedLinkError,没有sqlite4java-osx-amd64

我对这个问题有类似的问题&#xff1a;我正在运行一个使用sqlite的脚本,虽然我能够通过命令行成功运行sqlite3,但是当我尝试运行脚本时,我总是遇到这个错误&#xff1a;SEVERE: [sqlite] sqliteQueue[master.catalog]: error running job queuecom.almworks.sqlite4java.sqlite…

神经网络编程入门

本文主要内容包括&#xff1a; (1) 介绍神经网络基本原理&#xff0c; (2) AForge.NET实现前向神经网络的方法&#xff0c; (3) Matlab实现前向神经网络的方法 。 第0节、引例 本文以Fisher的Iris数据集作为神经网络程序的测试数据集。Iris数据集可以在http://en.wikipedia.or…

I2C子系统详解2——I2C核心层源码分析

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 一、前言 由I2C总线设备的驱动框架可知&#xff0c;I2C总线设备驱动框架涉及的文件如下&#xff1a; &#xff08;1&#xff09;I2C设备驱动层相关的文件 x210开发板的电容触摸屏gslX680采用I2C接口…

软考复习之路—组成原理

计算机系统的基础知识应该是作为一个编程人员必备的一门课程&#xff0c;仅仅有了解了计算机的组成&#xff0c;程序在计算 机中的存储状态&#xff0c;运算等基本内容&#xff0c;我们才干继续对计算机有更深层次的认识&#xff0c;更easy学习与上手。比方说要 想学习操作系统…

python内存管理机制错误_Python内存管理机制和垃圾回收机制的简单理解

一、内存管理机制1.由c开发出来的cpython2.include / objests3.需要下载python源码包4.Pyobject&#xff1a;floatPyVarObject&#xff1a;5.在python中所有东西创建对象的时候&#xff0c;内部都会存储一个数据// 维护双向链表struct _object *_ob_next;struct _object *_ob_p…

C#中几种数据库的大数据批量插入

C#语言中对SqlServer、Oracle、SQLite和MySql中的数据批量插入是支持的&#xff0c;不过Oracle需要使用Orace.DataAccess驱动。 IProvider里有一个用于实现批量插入的插件服务接口IBatcherProvider。批量插入的实现可以通过实现该接口来实现。 /// <summary>/// 提供数据…

求背包问题所有解(C++实现)

这是我学习数据结构时的一道上机作业&#xff0c;那时还没养成写注释的习惯&#xff0c;所以各位得受点苦了。 只是简易背包问题。 代码&#xff1a; 展开 1 // 背包问题所有解2 // 作者:王锦 3 // 邮箱:jinkswvip.qq.com4 5 #include "stdafx.h"6 #include <iost…

I2C子系统详解3——I2C总线驱动层代码分析

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 一、前言 由I2C总线设备的驱动框架可知&#xff0c;I2C总线设备的驱动框架涉及的文件如下&#xff1a; &#xff08;1&#xff09;I2C设备驱动层相关的文件 x210开发板的电容触摸屏gslX680采用I2C接…

java join 异常_Java:守护进程:thread.join()没有完成,当在一个线程中抛出异常时...

我写了一个Java守护进程(一个实现守护进程和Runnable的类)&#xff0c;现在我遇到了以下问题&#xff1a;在init()中&#xff0c;我创建了一个新线程 . Thread thread new Thread(this); 在start()中我启动新线程 . thread.start() . 在运行中我做了很多不同的事情......然后发…

硬链接与符号链接的比较?

今天就说说硬链接&#xff08;实体链接&#xff09;与符号链接&#xff08;类似Windows的快捷方式&#xff09;的不同?首先我们应知道&#xff1a;每个档案都会占用一个inode ,档案内容由 inode记录来指向;想要读取该档案&#xff0c;必须要经过目录记录的文件名来指向正确的i…

JAVA--自制斐波那契数列输出

累了&#xff0c;写点简单的。 1 public class hello {2 3 /**4 * param args5 */6 public static void main(String[] args) {7 int Fabnum 10;8 int sum 0;9 System.out.print("Serial:\t"); 10 for(int i 1…

I2C子系统详解4——I2C设备驱动层代码分析

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 一、前言 由I2C总线设备的驱动框架可知&#xff0c;I2C总线设备的驱动框架涉及的文件如下&#xff1a; &#xff08;1&#xff09;I2C设备驱动层相关的文件 x210开发板的电容触摸屏gslX680采用I2C接…