evdev_handler浅析

本文深入剖析了Linux输入子系统的内部工作原理,重点介绍了输入事件处理流程,包括入口函数evdev_init的作用、事件处理器evdev_handler的实现细节,以及设备驱动层与核心层之间的交互过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/**
    入口函数。
    这是:事件处理层向核心层注册input_handler
    input_register_handler()函数的实现在以前博文中浅析过了。
*/
static int __init evdev_init(void)
{   
    //注册一个事件处理器
    return input_register_handler(&evdev_handler);
}

/**
    事件处理器,对输入事件的具体处理
*/
static struct input_handler evdev_handler = {
    /**
        设备驱动层调用input_event()向核心层上报的事件,最终会到达事件处理层 ,事件处理层的这个函数会被调用。事件流程以前的博文已浅析。
    */
    .event      = evdev_event,   
    .connect    = evdev_connect,
    .disconnect = evdev_disconnect,
    .fops       = &evdev_fops,
    .minor      = EVDEV_MINOR_BASE,
    .name       = "evdev",
    .id_table   = evdev_ids, //这个handler所能够支持的设备列表
};
/*
看evdev_connect这个函数。
这个函数什么时候会被调用呢?
是在input_dev和input_handler匹配成功后调用的。

具体的是在这个函数中:
*/
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
    /*
        用于匹配的代码,匹配不成功会出错返回
        先略过... 后面有分析
    */  
    .....
    //下面是匹配成功后调用的代码
    error = handler->connect(handler, dev, id); //调用input_handler结构体中定义的evdev_connect函数
    if(error)
    {
        ....
    }
    return error;
}
/*
那么input_attach_handler这个函数又是谁来调用的呢?
有俩个函数会调用这个函数

1 : 设备驱动层 向 核心层 注册input_dev的时候     int input_register_device(...)
2 : 事件处理层 向 核心层 注册input_handler的时候 input_register_handler(...)
    这两个函数以前的博文分析过了。
*/

下面看event_connect函数的实现
static int event_connect(struct input_handler * handler,struct input_dev * dev,const struct input_device_id * id)
{
    struct evdev * evdev;
    int minor;
    int error;

    /**
        #define EVDEV_MINORS        32
        对evdev_table[32]数组遍历,目的是在数组中找出一个空缺项
        下边的代码会生成一个evdev,为evdev放入这个数组中做准备,

        下面讨论一下evdev_table[]这个数组
        它是再什么时候被构造的,什么时候被填充的,又是什么时候从里面取出元素的

        一指针数组 长度32 里面存放的元素都是 struct evdev *
        static struct evdev *evdev_table[EVDEV_MINORS];

        填充:
            evdev_connect(...)
            {
                evdev_install_chrdev(evdev)
                {
                    * No need to do any locking here as calls to connect and
                    * disconnect are serialized by the input core
                    //官方这里说:这里不需要使用互斥机制来保护临界区
                    //在evdev_remove_chrdev()函数中是需要避免竞态发生的。难道一下子不会匹配成功好几个?,
                    //为什么删除的时候却可能引起并发从而要防止竞太的发生呢?
                    //在调用event_connect的前面几个函数input_register_device、input_register_hander函数中操作链表也没有使用互斥保护机制的,
                    //请教:为什么?
                    evdev_table[evdev->minor] = evdev;
                    return 0;
                }
            }

        从evdev_table[]数组中删除evdev
        evdev_disconnect(struct input_handle *handle)
        {
            evdev_cleanup(evdev)
            {
                evdev_remove_chrdev(evdev)
                {
                    //获取互斥锁 --- 访问临界区 ----释放互斥锁
                    mutex_lock(&evdev_table_mutex);

                    evdev_table[evdev->minor] = NULL;

                    mutex_unlock(&evdev_table_mutex);
                }
            }
        }

    */
    for (int minor = 0; minor < EVDEV_MINORS; ++minor)
    {
        /**
            如果evdev_table[]有空缺项,就跳出循环
            那么minor就记录了这个空缺项在evdev_table[]中的索引值
        */
        if (!evdev_table[minor])  
        {
            break;
        }
    }
        /**
        对evdev_table[]是否满了的判断
    */
    if (minor == EVDEV_MINORS)
    {
        printk(KERN_ERR "evdev: no more free evdev devices\n");
        return -ENFILE;
    }

    /**
        为evdev分配内存空间
    */
    evdev = kzalloc(sizeof(struct devdev),GFP_KERNEL);
    if (!evdev)
    {
        return -ENOMEM;
    }
    /**
        初始化 链表、自旋锁、互斥锁、等待队列
        client_list : 所有的evdev都会放到这个连表上
    */
    INIT_LIST_HEAD(&evdev->client_list);
    spin_lock_init(&evdev->client_lock);
    mutex_init(&evdev->mutex);
    init_waitqueue_head(&evdev->wait);
    /**
        设置evdev的名字。evevt0、event1...event31。递增的形式
        在/dev/input/中出现
        看,minor在这里发挥作用了,
    */
    dev_set_name(&evdev->dev,"event%d",minor);
    evdev->exist = 1;  //?
    evdev->minor = minor;

    /**
        下面的代码是对input_handle进行初始化。
        input_handle:
                    用来连接input_dev和input_handler
    */
    //指向input_dev设备
    evdev->handle.dev = input_get_device(dev);
    evdev->handle.name = dev_name(&evdev->dev);
    //指向input_handler
    evdev->handle.handler = handler;

    /*
        将设置好的evdev放入到input_handle的私有数据中
        evdev_disconnect()函数中会使用到
    */
    evdev->handle.private = evdev;

    /**
        下面的代码对evdev->dev进行初始化
        会把它放入到linux设备驱动模型中,在放入之前先初始化一下。
    */
    //设备号。major: 13  minor : 64 + (0 ~ 31) 
    evdev->dev.devt = MKDEV(INPUT_MAJOR,EVDEV_MINOR_BASE + minor);
    /**
        设置设备所属的类。这样会在/sys/class/input/下面显示设备的目录

    */
    evdev->dev.class = &input_class;
    evdev->dev.parent = &dev->dev;
    /**
        设置释放函数,
        当evdev设备的引用计数为0的时候,这个函数会自动被调用。
    */
    evdev->dev.release = evdev_free;
    /**
        初始化了evdev->dev中的一些成员
        看看它的源码:
        device_initialize()
        {
            dev->kobj.kset = devices_kset;
            kobject_init(&dev->kobj, &device_ktype);
            INIT_LIST_HEAD(&dev->dma_pools);
            init_MUTEX(&dev->sem);
            spin_lock_init(&dev->devres_lock);
            INIT_LIST_HEAD(&dev->devres_head);
            device_init_wakeup(dev, 0);
            device_pm_init(dev);
            set_dev_node(dev, -1);
        }
    */
    device_initialize(&evdev->dev);

    /**
        注册一个新的input_handle
        就是把input_handle挂到input_dev维护的dev->h_list链表中、
                              input_handler维护的handler->h_list链表中
        看源码:
            int input_register_handle(struct input_handle *handle)
{
    //从input_handle中取出它所指向的input_handler
    struct input_handler *handler = handle->handler;
    //从input_handle中取出它所指向的input_dev
    struct input_dev *dev = handle->dev;
    int error;
    error = mutex_lock_interruptible(&dev->mutex);
    if (error){
        return error;   
    }
    if (handler->filter)
        list_add_rcu(&handle->d_node, &dev->h_list);
    else
        //将input_handle挂接到input_dev维护的dev->h_list链表中
        list_add_tail_rcu(&handle->d_node, &dev->h_list);

    mutex_unlock(&dev->mutex);  
    //将input_handle挂接到input_handler维护的handler->h_list链表中
    list_add_tail_rcu(&handle->h_node, &handler->h_list);
}   
    //如果input_handler提供了start函数,就调用它        
    if (handler->start){
        handler->start(handle);  
    }   
    */
    error = input_register_handle(&evdev->handle);
    if (error){
        goto err_free_evdev;
    }
    /**
        将设置好的evdev放入到evdev_table[]中
        源码:
        static int evdev_install_chrdev(struct evdev *evdev)
        {
            //依据次设备号为索引值将设置好的evdev放入到evdev_table[]中。还是那个问题 不明白为什么不保护起来.如果能并发就可能会引起竞态,既然有竞态可能发生,为什么临界区不用互斥机制保护起来呢?
            evdev_table[evdev->minor] = evdev;
            return 0;
        }
    */
    error = evdev_install_chrdev(evdev);
    if (error)
    {
        goto err_unregister_handle;
    }   
    /**
        任何设备的添加都会经过这个函数。
    */
    error = device_add(&evdev->dev);
    if (error)
    {
        goto err_cleanup_evdev; 
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值