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,一经查实,立即删除!

相关文章

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,…

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

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

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

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

神经网络编程入门

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

软考复习之路—组成原理

计算机系统的基础知识应该是作为一个编程人员必备的一门课程&#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++实现)

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

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…

9、C语言 —— 指针的用处

为什么80%的码农都做不了架构师&#xff1f;>>> 1、用函数实现两个数的交换 ‍‍在没用函数之前&#xff0c;可以这样实现‍‍#include <stdio.h>int main() {int a 3;int b 7;int c;printf("交换前&#xff0c;a%d&#xff0c;b%d\n", a, b); …

java动态代理二cglib

2019独角兽企业重金招聘Python工程师标准>>> java动态代理 转载于:https://my.oschina.net/u/1430510/blog/290215

中断的上下半部

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 前言 因为输入类设备的输入都是异步事件&#xff0c;因此一般使用中断来处理和响应。 中断处理程序处于中断上下文中&#xff0c;不能和用户空间数据交互&#xff08;不能使用copy_to(from)_usr函数…

图片播放器小项目(详解)

以下内容源于朱有鹏《物联网大讲堂》课程的学习整理&#xff0c;如有侵权&#xff0c;请告知删除。一、开始动手写代码 1、Makefile介绍 &#xff08;1&#xff09;这是一个通用的项目管理的Makefile体系&#xff0c;自己写的&#xff08;有子文件夹组织的&#xff09;项目可以…

Telnet远程访问思科交换机、路由器

一、实验目的Telnet远程访问思科交换机、路由器二、实验拓扑三、实验步骤1、PC1远程管理S11&#xff09;配置交换机的管理IPS1(config)#int vlan 1S1(config-if)#ip add 192.168.1.100 255.255.255.0S1(config-if)#no shu2&#xff09;开启S1的telnet远程管理服务S1(config)#li…

[置顶]       cocos2d-x 手游源码站

尊重开发者的劳动成果&#xff0c;转载的时候请务必注明出处&#xff1a;http://blog.csdn.net/haomengzhu/article/details/37829061 1、魔幻方块 链接&#xff1a;魔幻方块源码关键词&#xff1a;魔幻方块源码 源代码 Cocos2d-x2.0 游戏源码 益智 休闲 游戏 游戏类型&#xf…

Android SDK开发包国内下载地址

原帖地址&#xff1a;http://www.cnblogs.com/bjzhanghao/archive/2012/11/14/2769409.html 不知道是因为最近kaihui还是怎么的&#xff0c;打开android sdk官方网站特别的慢&#xff0c;想下载最新版本的platform几乎变成不可能完成的任务&#xff0c;不知道为什么Google不像…

SharePoint 2013 Workflow - Advanced Workflow Debugging with Fiddler

来自&#xff1a;Andrew Connell [MVP SharePoint] | 时间&#xff1a;2012-07-18 19:26:30 原文链接&#xff1a; http://www.andrewconnell.com/blog/archive/2012/07/18/sharepoint-2013-workflow-advanced-workflow-debugging-with-fiddler.aspx In previous posts Iv…

java sheet 打印区域设定,如何使用Java设置电子表格的打印区域。(How to set the print area of a spreadsheet using Java.)...

如何使用Java设置电子表格的打印区域。(How to set the print area of a spreadsheet using Java.)问题描述 (Problem Description)如何使用Java设置电子表格的打印区域。解决方案 (Solution)以下是使用Java设置电子表格打印区域的程序。import java.io.File;import java.io.Fi…

RedHat6.2 x86手动配置LNMP环境

为什么80%的码农都做不了架构师&#xff1f;>>> 因为公司要求用RedHat配&#xff0c;顺便让我练习一下Linux里面的操作什么的。 折腾来折腾去终于搞好了&#xff0c;其实也没那么难嘛。但是也要记录一下。 首先&#xff0c;是在服务器里面用VMware搭建的RedHat6.2 …

《c语言深度剖析》读书笔记

一、注意点 1、 2、 3、 4、 5、 6、 7、 8、 9、 10、 11、 二、问题 1、 2、 3、 4、 5、 6、 7、

androidpn的一次亲密接触(二)

简单看了一下源码的实现&#xff0c;这里贴一点个人觉得比叫重要的代码。 XmppManager.java 构造方法&#xff1a;Java代码public XmppManager(NotificationService notificationService) 在这里主要是从共享引用中取得xmpp服务器地址和端口号、用户名和密码。 内部类Java代码…