输入子系统的input handler、input device、handle之间的关系

1.vdev.c中evdev_connect()是属于input handler层,是device和handler注册到核心层,且匹配成功后会调用evdev_connect()。一般在内核启动的时候,input handler是已经注册了的,也就是说我们evdev_handler是先有的。然后我们在device层写的input_dev注册进来后遍历input_handler_list,查找有没有一个合适的处理这,当然我们input_dev和input_handler是一个多对一的关系,也就是说我们input_handler是可以处理所有的input_dev的。这就是说我们注册了input_dev后一定能找到一个handler,找到后,就会调用connect方法,其中connect方法会帮你分配一个evdev对象,这个对象里面就有一个成员叫:input_Handle(注意不是handler),它的作用是记录input_dev和input_handler之间的关系的,有两个指针来记录,分别是:*dev和*handler,一旦调用connect,就会把input_dev和input_handler作为参数传入到connect中来,传进来后,*dev指针就指向input_dev,*handler指针就指向input_handler,记录后的作用是:通过input_handle找到input_dev和input_handler;实际上在input_dev中也会记录input_handle中的成员d_node,input_handler中会记录h_node,这样三者(input_handle,input_dev,input_handler)就能互相联系,互相关联了。这样任意一个调用者,可以通过三着中的任意一个查找到另外两个,例如:调用者从input_dev中如何知道d_node属于哪个handle,由于input_handle中的成员d_node是注册到了h_list中的,input_dev通过内核链表技术,container_of技术就可找到,这样找到input_handle后由于其中的*handler是指向input_handler的,这样就能找到处理者了,然后将数据交予处理。三者关系如下

2.connec调用之后还会做的是分配一个evdev对象,每个input_dev就会对应一个evdev,所以就是说构建了evdev的同时会创建设备节点:dev/event0,1,2,3…;这个evdev对象里面其中一个成员就是上面说的input_handle用于记录三者关系,而且input_handle还会指向evdev,这是一种内核代码设计的思想;另外,evdev里面还有个成员是cdev,就是字符设备对象的一个关键对象,是每个字符设备里面都会有的,一个input_dev对应一个cdev,cdev里面包括:主次设备号,fops等,其中fops对应的是evdev_fops,当我们的应用程序调用open,read等接口的时候就会找对应cdev中的evdev_fops,如下:

另外evdev_fops的kobj是记录了当前的dev的,便于操作。。。以上所有逻辑对应源码实现参考内核源码static int evdev_connect:

static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct evdev *evdev;
int minor;
int dev_no;
int error;
//找到一个没有被使用的次设备号, 从64开始,then 65,66 ...
minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);
if (minor < 0) {
error = minor;
pr_err("failed to reserve new minor: %d\n", error);
return error;
}
// 实例化 一个evdev对象
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
if (!evdev) {
error = -ENOMEM;
goto err_free_minor;
}
//初始化evdev对象
INIT_LIST_HEAD(&evdev->client_list);
spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex);
//等待队列是完成阻塞
init_waitqueue_head(&evdev->wait);
evdev->exist = true;

dev_no = minor;
/* Normalize device number if it falls into legacy range */
if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS)
//减去了EVDEV_MINOR_BASE:64
dev_no -= EVDEV_MINOR_BASE;
// 创建设备文件/dev/event0,1,2
dev_set_name(&evdev->dev, "event%d", dev_no);
//以上code和device_create是一样
//利用handle记录input device和input handler
evdev->handle.dev = input_get_device(dev);
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;
//实现三者handle,input handler,intpu dev联系起来,记录关系
evdev->handle.private = evdev;

evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);
evdev->dev.class = &input_class;
evdev->dev.parent = &dev->dev;
evdev->dev.release = evdev_free;
device_initialize(&evdev->dev);
//将生成对象(handle)关联到(input handler)和(input dev)
error = input_register_handle(&evdev->handle);
if (error)
goto err_free_evdev;

//初始化了cdev,完成了fops, 为用户提供文件io
cdev_init(&evdev->cdev, &evdev_fops);
evdev->cdev.kobj.parent = &evdev->dev.kobj;
error = cdev_add(&evdev->cdev, evdev->dev.devt, 1);
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);
 err_free_minor:
input_free_minor(minor);
return error;

}

========================end evdev_connect=============================

总结:

1.分配evdev,并初始化,记录input device和handler的关系

2.创建设备节点/dev/event0

3. 注册cdev,并实现fops

4.关系如下:

       1) 多个input device可以对应一个event handler 一个input device对应

        2)一个 evdev,对应于一个设备节点/dev/event0,1,2 

5. 所有的设备节点调用open,read,write文件io的时候实际是调用cdev中fops中各个接口,最终都调用了evdev_fops


猜你喜欢

转载自blog.csdn.net/u010802169/article/details/80465760