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线程之线程的创建、属性、回收、退出、取消方式

《Linux线程之线程的创建、属性、回收、退出、取消方式》文章总结了线程管理核心知识:线程号唯一、创建方式、属性设置(如分离状态与栈大小)、回收机制(join/detach)、退出方法(返回/pthr... 目录1. 线程号2. 线程的创建3. 线程属性4. 线程的回收5. 线程的退出6. 线程的取消7.

Linux下进程的CPU配置与线程绑定过程

《Linux下进程的CPU配置与线程绑定过程》本文介绍Linux系统中基于进程和线程的CPU配置方法,通过taskset命令和pthread库调整亲和力,将进程/线程绑定到特定CPU核心以优化资源分配... 目录1 基于进程的CPU配置1.1 对CPU亲和力的配置1.2 绑定进程到指定CPU核上运行2 基于

golang程序打包成脚本部署到Linux系统方式

《golang程序打包成脚本部署到Linux系统方式》Golang程序通过本地编译(设置GOOS为linux生成无后缀二进制文件),上传至Linux服务器后赋权执行,使用nohup命令实现后台运行,完... 目录本地编译golang程序上传Golang二进制文件到linux服务器总结本地编译Golang程序

Linux下删除乱码文件和目录的实现方式

《Linux下删除乱码文件和目录的实现方式》:本文主要介绍Linux下删除乱码文件和目录的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux下删除乱码文件和目录方法1方法2总结Linux下删除乱码文件和目录方法1使用ls -i命令找到文件或目录

Linux在线解压jar包的实现方式

《Linux在线解压jar包的实现方式》:本文主要介绍Linux在线解压jar包的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux在线解压jar包解压 jar包的步骤总结Linux在线解压jar包在 Centos 中解压 jar 包可以使用 u

linux解压缩 xxx.jar文件进行内部操作过程

《linux解压缩xxx.jar文件进行内部操作过程》:本文主要介绍linux解压缩xxx.jar文件进行内部操作,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、解压文件二、压缩文件总结一、解压文件1、把 xxx.jar 文件放在服务器上,并进入当前目录#

Linux系统性能检测命令详解

《Linux系统性能检测命令详解》本文介绍了Linux系统常用的监控命令(如top、vmstat、iostat、htop等)及其参数功能,涵盖进程状态、内存使用、磁盘I/O、系统负载等多维度资源监控,... 目录toppsuptimevmstatIOStatiotopslabtophtopdstatnmon

Java通过驱动包(jar包)连接MySQL数据库的步骤总结及验证方式

《Java通过驱动包(jar包)连接MySQL数据库的步骤总结及验证方式》本文详细介绍如何使用Java通过JDBC连接MySQL数据库,包括下载驱动、配置Eclipse环境、检测数据库连接等关键步骤,... 目录一、下载驱动包二、放jar包三、检测数据库连接JavaJava 如何使用 JDBC 连接 mys

在Linux中改变echo输出颜色的实现方法

《在Linux中改变echo输出颜色的实现方法》在Linux系统的命令行环境下,为了使输出信息更加清晰、突出,便于用户快速识别和区分不同类型的信息,常常需要改变echo命令的输出颜色,所以本文给大家介... 目python录在linux中改变echo输出颜色的方法技术背景实现步骤使用ANSI转义码使用tpu

linux hostname设置全过程

《linuxhostname设置全过程》:本文主要介绍linuxhostname设置全过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录查询hostname设置步骤其它相关点hostid/etc/hostsEDChina编程A工具license破解注意事项总结以RHE