input子系统——linux驱动学习笔记(三)

时间:2022-08-14 17:53:28

整个Input子系统的结构:
input子系统——linux驱动学习笔记(三)         
前面一片文章我们介绍的属于输入子系统核心层,该层主要负责对驱动层提供接口,比如之前介绍input_allocate_device()、
input_register_device(button_dev)、input_report_key(button_dev, BTN_0, 1)、input_sync(button_dev)等。
同时向上提供了事件处理层的的编程接口。
而handler层代表的是对输入事件具体的处理。

我们接下来要看的是evdev输入事件驱动,它为input子系统提供了一套默认的事件处理方法。
在/drivers/input/evdev.c 中,我们从初始化函数看起。

static struct input_handler evdev_handler = {
.event = evdev_event,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.fops = &evdev_fops,
.minor = EVDEV_MINOR_BASE,
.name = "evdev",
.id_table = evdev_ids,
};

static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);
}

接着来看input_register_handler(&evdev_handler)这个函数,注册一个handler事件处理器。

int input_register_handler(struct input_handler *handler)
{
struct input_dev *dev;
int retval;

retval = mutex_lock_interruptible(&input_mutex);
if (retval)
return retval;

INIT_LIST_HEAD(&handler->h_list);

if (handler->fops != NULL) {
if (input_table[handler->minor >> 5]) {
retval = -EBUSY;
goto out;
}
input_table[handler->minor >> 5] = handler;
}

list_add_tail(&handler->node, &input_handler_list);

list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);

input_wakeup_procfs_readers();

 out:
mutex_unlock(&input_mutex);
return retval;
}

struct input_handler {
void *private;
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
void (*disconnect)(struct input_handle *handle);
void (*start)(struct input_handle *handle);
const struct file_operations *fops;
int minor;
const char *name;
const struct input_device_id *id_table;
const struct input_device_id *blacklist;

struct list_headh_list;
struct list_headnode;
};

struct input_handler是事件处理结构,定义怎么处理事件。
其中最重要的就是:
even函数:具体处理输入事件的函数。
connect函数:用来连接dev与handler(之前的文章我们在注册input_dev的时候,在input_handler_list中寻找匹配的handler时就遇到过它)。


我们再来看一下input_dev、input_handler和input_handle三者的关系。
input子系统——linux驱动学习笔记(三)


接着看 input_register_handler(struct input_handler *handler):

NIT_LIST_HEAD(&handler->h_list) 首先初始化h_list链表。

input_table[handler->minor >> 5] = handler; 把要注册的handler的指针存到全局数组input_table的对应元素中。

list_add_tail(&handler->node, &input_handler_list);

list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
这几句我们应该相当眼熟了,因为在前面的注册input_dev的时候也是使用的这样的形式,将handler加入到input_handler_list当中,然后再遍历input_dev_list链表,寻找匹配的input_dev,如果匹配成功,调用handler的connect函数。


我们之前一直不清楚在connect的时候究竟做了什么事情,我们下面就来看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 error;

for (minor = 0; minor < EVDEV_MINORS; minor++)
if (!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);
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 = dev;
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.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);
if (error)
goto err_free_evdev;

error = evdev_install_chrdev(evdev);
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;
}


函数里有那么一段:
evdev->handle.dev = dev;
evdev->handle.name = evdev->name;
evdev->handle.handler = handler;
evdev->handle.private = evdev;
input_register_handle(&evdev->handle);
我们来看input_register_handle这个函数:

int input_register_handle(struct input_handle *handle)
{
struct input_handler *handler = handle->handler;
struct input_dev *dev = handle->dev;
int error;

/*
* We take dev->mutex here to prevent race with
* input_release_device().
*/
error = mutex_lock_interruptible(&dev->mutex);
if (error)
return error;
list_add_tail_rcu(&handle->d_node, &dev->h_list);
mutex_unlock(&dev->mutex);
synchronize_rcu();

/*
* Since we are supposed to be called from ->connect()
* which is mutually exclusive with ->disconnect()
* we can't be racing with input_unregister_handle()
* and so separate lock is not needed here.
*/
list_add_tail(&handle->h_node, &handler->h_list);

if (handler->start)
handler->start(handle);

return 0;
}
这该函数没什么难度,主要就是将handle分别加入到dev、handler的h_list当总中,然后调用handler的start函数,但是start函数未定义,所以不调用。

总结一下connect函数,主要就是分配一个evdev结构体,做一些初始化,并将handle结构体注册进去,再把evdev存放到全局数组中evdev_table中。


 




给前面的几篇做个总结,然后给出整个input子系统的框架:    




input子系统——linux驱动学习笔记(三)




从上图我们知道了三个层次是怎么联系在一起的。
open函数,从上图可以看出,在中间层init函数中,注册了一个字符设备,并把input_fops注册了进去,而input_fops里面只定义了一个open函数,这个open函数的用处就是把相应的handler提供的fops存放到file->fops里面 ,并跳转到相应handler的提供的 fops 里面的open函数。以后用户再打开这个设备文件,就直接进去的handler的fops函数集里面了。(输入设备的文件结点在/dev/input/eventX 下面。)


更多0