linux驱动——input输入子系统(3)——evdev

2024-04-06 04:58

本文主要是介绍linux驱动——input输入子系统(3)——evdev,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


linux驱动——input输入子系统(1)—输入子系统核心层(Input Core)的地址链接


linux驱动——input输入子系统(2)——handler的地址链接


evdev输入事件驱动,为输入子系统提供了一个默认的事件处理方法。其接收来自底层驱动的大多数事件,并使用相应的逻辑对其进行处理。

1、evdev有关的代码都在Evdev.c (linux2.6.28\drivers\input)文件中,作为模块存在。

module_init(evdev_init);
module_exit(evdev_exit);

static void __exit evdev_exit(void)
{
input_unregister_handler(&evdev_handler);
}

static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);对这个函数应该很熟悉,我们上一篇才说过。

其中有:

static struct input_handler evdev_handler = {
.event = evdev_event,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.fops = &evdev_fops,
.minor = EVDEV_MINOR_BASE,

其中有:#define EVDEV_MINOR_BASE64

因为一个handler可以处理32个设备,所以evdev_handler所能处理的设备文件范围为(13,64)~(13,64+32),其中13是所有输入设备的主设备号。
.name = "evdev",
.id_table = evdev_ids,

其中:static const struct input_device_id evdev_ids[] = {
{ .driver_info = 1 },/* Matches all devices */
{ }, /* Terminating zero entry */
};

evdev_ids没有定义flags,也没有定义匹配属性值。这个evdev_ids的意思就是:evdev_handler可以匹配所有 input_dev设备,也就是所有的input_dev发出的事件,都可以由evdev_handler来处理。

};
}

2、如果input_dev和handler匹配成功后,会调用handler->connect()函数,现在就来分析evdev_connect函数,源码如下所示:

/*
 * Create new evdev device. Note that input core serializes calls
 * to connect and disconnect so we don't need to lock evdev_table here.
 */
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct evdev *evdev;
int minor;
int error;


for (minor = 0; minor < EVDEV_MINORS; minor++)
if (!evdev_table[minor])
break;
其中有定义:#define EVDEV_MINORS32

 static struct evdev *evdev_table[EVDEV_MINORS];

struct evdev {
int exist;
int open;
int minor;
char name[16];
struct input_handle handle;
wait_queue_head_t wait;
struct evdev_client *grab;
struct list_head client_list;
spinlock_t client_lock; /* protects client_list */
struct mutex mutex;
struct device dev;
};

表示evdev_handler所表示的32个设备,这个循环为了找到空的一项

if (minor == EVDEV_MINORS) {  没找到,则退出
printk(KERN_ERR "evdev: no more free evdev devices\n");
return -ENFILE;
}


evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
if (!evdev)
return -ENOMEM;


INIT_LIST_HEAD(&evdev->client_list);   初始化
spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex);
init_waitqueue_head(&evdev->wait);


snprintf(evdev->name, sizeof(evdev->name), "event%d", minor);
evdev->exist = 1;
evdev->minor = minor;


evdev->handle.dev = input_get_device(dev);  对evdev中handle的初始化,这些初始化的目的是使input_dev和input_handler联系起来。
evdev->handle.name = evdev->name;
evdev->handle.handler = handler;
evdev->handle.private = evdev;


strlcpy(evdev->dev.bus_id, evdev->name, sizeof(evdev->dev.bus_id));   初始化一个evdev->dev的设备
evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
evdev->dev.class = &input_class;
evdev->dev.parent = &dev->dev;
evdev->dev.release = evdev_free;
device_initialize(&evdev->dev);


error = input_register_handle(&evdev->handle);  注册一个input_handle结构体
if (error)
goto err_free_evdev;


error = evdev_install_chrdev(evdev);源码如下:

static int evdev_install_chrdev(struct evdev *evdev)
{
/*
* No need to do any locking here as calls to connect and
* disconnect are serialized by the input core
*/
evdev_table[evdev->minor] = evdev;
return 0;
}

if (error)
goto err_unregister_handle;


error = device_add(&evdev->dev);  和设备模型有关
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;
}

3、evdev设备的打开

对主设备号为INPUT_MAJOR的设备结点进行操作,会将操作集转换成handler的操作集。在evdev_handler中定义了一个fops集合,被赋值为evdev_fops的指针,evdev_fops结构的定义如下所示:

static const struct file_operations evdev_fops = {
.owner = THIS_MODULE,
.read = evdev_read,
.write = evdev_write,
.poll = evdev_poll,
.open = evdev_open,
.release = evdev_release,
.unlocked_ioctl= evdev_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = evdev_ioctl_compat,
#endif
.fasync = evdev_fasync,
.flush = evdev_flush
};

当用户程序调用open时,会调用evdev_open函数,源码如下:

static int evdev_open(struct inode *inode, struct file *file)
{
struct evdev *evdev;
struct evdev_client *client;
int i = iminor(inode) - EVDEV_MINOR_BASE;  得到了在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];  得到struct evdev
if (evdev)
get_device(&evdev->dev); 增加引用计数
mutex_unlock(&evdev_table_mutex);


if (!evdev)
return -ENODEV;


client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL); 分配一个struct evdev_client结构体的空间

其中:

struct evdev_client {
struct input_event buffer[EVDEV_BUFFER_SIZE];
int head;
int tail;
spinlock_t buffer_lock; /* protects access to buffer, head and tail */
struct fasync_struct *fasync;
struct evdev *evdev;
struct list_head node;
};

if (!client) {
error = -ENOMEM;
goto err_put_evdev;
}


spin_lock_init(&client->buffer_lock); 
client->evdev = evdev;
evdev_attach_client(evdev, client);  将client挂到evdev->client_list上。


error =evdev_open_device(evdev);   打开输入设备

static int evdev_open_device(struct evdev *evdev)
{
int retval;


retval = mutex_lock_interruptible(&evdev->mutex);
if (retval)
return retval;


if (!evdev->exist)  判断设备的存在
retval = -ENODEV;
else if (!evdev->open++) {   如果是第一次打开,调用input_open_device打开evdev对应的handle
retval = input_open_device(&evdev->handle);
if (retval)
evdev->open--;
}


mutex_unlock(&evdev->mutex);
return retval;
}
if (error)
goto err_free_client;


file->private_data = client;  将 client赋给file的private_data
return 0;


 err_free_client:
evdev_detach_client(evdev, client);
kfree(client);
 err_put_evdev:
put_device(&evdev->dev);
return error;
}

这篇关于linux驱动——input输入子系统(3)——evdev的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/879049

相关文章

linux-基础知识3

打包和压缩 zip 安装zip软件包 yum -y install zip unzip 压缩打包命令: zip -q -r -d -u 压缩包文件名 目录和文件名列表 -q:不显示命令执行过程-r:递归处理,打包各级子目录和文件-u:把文件增加/替换到压缩包中-d:从压缩包中删除指定的文件 解压:unzip 压缩包名 打包文件 把压缩包从服务器下载到本地 把压缩包上传到服务器(zip

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

【测试】输入正确用户名和密码,点击登录没有响应的可能性原因

目录 一、前端问题 1. 界面交互问题 2. 输入数据校验问题 二、网络问题 1. 网络连接中断 2. 代理设置问题 三、后端问题 1. 服务器故障 2. 数据库问题 3. 权限问题: 四、其他问题 1. 缓存问题 2. 第三方服务问题 3. 配置问题 一、前端问题 1. 界面交互问题 登录按钮的点击事件未正确绑定,导致点击后无法触发登录操作。 页面可能存在

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta

【Linux 从基础到进阶】Ansible自动化运维工具使用

Ansible自动化运维工具使用 Ansible 是一款开源的自动化运维工具,采用无代理架构(agentless),基于 SSH 连接进行管理,具有简单易用、灵活强大、可扩展性高等特点。它广泛用于服务器管理、应用部署、配置管理等任务。本文将介绍 Ansible 的安装、基本使用方法及一些实际运维场景中的应用,旨在帮助运维人员快速上手并熟练运用 Ansible。 1. Ansible的核心概念

Linux服务器Java启动脚本

Linux服务器Java启动脚本 1、初版2、优化版本3、常用脚本仓库 本文章介绍了如何在Linux服务器上执行Java并启动jar包, 通常我们会使用nohup直接启动,但是还是需要手动停止然后再次启动, 那如何更优雅的在服务器上启动jar包呢,让我们一起探讨一下吧。 1、初版 第一个版本是常用的做法,直接使用nohup后台启动jar包, 并将日志输出到当前文件夹n

[Linux]:进程(下)

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:Linux学习 贝蒂的主页:Betty’s blog 1. 进程终止 1.1 进程退出的场景 进程退出只有以下三种情况: 代码运行完毕,结果正确。代码运行完毕,结果不正确。代码异常终止(进程崩溃)。 1.2 进程退出码 在编程中,我们通常认为main函数是代码的入口,但实际上它只是用户级

【Linux】应用层http协议

一、HTTP协议 1.1 简要介绍一下HTTP        我们在网络的应用层中可以自己定义协议,但是,已经有大佬定义了一些现成的,非常好用的应用层协议,供我们直接使用,HTTP(超文本传输协议)就是其中之一。        在互联网世界中,HTTP(超文本传输协议)是一个至关重要的协议,他定义了客户端(如浏览器)与服务器之间如何进行通信,以交换或者传输超文本(比如HTML文档)。