首页 > 技术文章 > input子系统详解2

zhaobinyouth 2017-01-08 15:58 原文

上一节大概了解了输入子系统的流程

这一节认真追踪一下代码

 

input.c:

input_init(void)函数

 1 static int __init input_init(void)
 2 {
 3     int err;
 4 
 5     err = class_register(&input_class);
 6     if (err) {
 7         printk(KERN_ERR "input: unable to register input_dev class\n");
 8         return err;
 9     }
10 
11     err = input_proc_init();
12     if (err)
13         goto fail1;
14 
15     err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
16     if (err) {
17         printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
18         goto fail2;
19     }
20 
21     return 0;
22 
23  fail2:    input_proc_exit();
24  fail1:    class_unregister(&input_class);
25     return err;
26 }
View Code

err = register_chrdev(INPUT_MAJOR, "input", &input_fops);     /* 注册字符设备(主设备号INPUT_MAJOR = 13) */

 

input_fops-->

static const struct file_operations input_fops = {
    .owner = THIS_MODULE,
    .open = input_open_file,
};

 

static int input_open_file(struct inode *inode, struct file *file)
{
	struct input_handler *handler = input_table[iminor(inode) >> 5];
	const struct file_operations *old_fops, *new_fops = NULL;
	int err;

	/* No load-on-demand here? */
	if (!handler || !(new_fops = fops_get(handler->fops)))
		return -ENODEV;

	/*
	 * That's _really_ odd. Usually NULL ->open means "nothing special",
	 * not "no device". Oh, well...
	 */
	if (!new_fops->open) {
		fops_put(new_fops);
		return -ENODEV;
	}
	old_fops = file->f_op;
	file->f_op = new_fops;

	err = new_fops->open(inode, file);

	if (err) {
		fops_put(file->f_op);
		file->f_op = fops_get(old_fops);
	}
	fops_put(old_fops);
	return err;
}

  

 知识点之一:struct input_handler *handler = input_table[iminor(inode) >> 5];
/* struct input_handler是用来定义一个handler的结构体,一个handler就对应一个struct input_handler结构体变量 */ 

----->input_table ----->
static struct input_handler *input_table[8];
既然是静态的就肯定在input.c中 ------>>>int input_register_handler(struct input_handler *handler)

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

    INIT_LIST_HEAD(&handler->h_list);

    if (handler->fops != NULL) {
        if (input_table[handler->minor >> 5])
            return -EBUSY;

        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_dev调用该函数并根据input_handler的id_table判断能不能支持这个inpt_dev了

    input_wakeup_procfs_readers();
    return 0;
}
  知识点之二:handler = input_table[iminor(inode) >> 5];    /* input_table是input子系统中用来管理handler的一个数组,里面存放的是handler的指针。通过次设备号找到本次应用层打开的输入设备对应的handler结构体 */

可知   input_table[]是由这个函数完成构造的    继续全局搜索该函数input_register_handler

可得到:

 

这些函数即分别对应上层的各个不同的handler的源代码。
evdev.c:

static int __init evdev_init(void)
{
    return input_register_handler(&evdev_handler);
}
input_register_handler(&evdev_handler);
在evdev_init函数中直接调用核心层提供的handler注册函数注册event这个handler,evdev_handler这个struct input_handler类型的变量就是对这个handler的一个描述,
evdev_handler:
static struct input_handler evdev_handler = {
    .event =    evdev_event,             /* 这个函数的作用就是实现将下层的事件包进行封装,然后存放在缓冲区中待read函数读取,并唤醒阻塞等待读取数据的进程 */
    .connect =    evdev_connect,         /* 匹配成功之后,就会调用这个函数进行连接 */
    .disconnect =    evdev_disconnect,   /* 断开连接 */
    .fops =        &evdev_fops,          /* 这个就是应用open/read/write...时对应的接口函数封装 file_operations */
    .minor =    EVDEV_MINOR_BASE,        /* 这个就是本handler下的设备的次设备号的起始设备号(基设备号) */
    .name =        "evdev",              /* handler的名字 */
    .id_table =    evdev_ids,            /* 一个handler描述自己支持的输入设备的特征的一个结构体,一个变量就描述了该halder支持的一类设备 */
};
   

evdev_handler变量就是本次分析的handler对应的结构体变量,变量中填充最重要的有3个:

evdev_event函数:

evdev_connect函数:

evdev_fops变量:这个变量是struct  fileoperations类型的结构体变量,将来应用层通过调用open函数时,这个结构体封装的函数会被核心层中注册字符设备时封装的open函数调用

 

说明:

input_handler中id_table 会和input_register_device中的struct input_dev类型的结构体进行匹配

 

 

 

 

全局搜索input_register_device可以知道有很多鼠标键盘等设备都会调用这个函数进行一个注册

 

int input_register_device(struct input_dev *dev)
{
    static atomic_t input_no = ATOMIC_INIT(0);
    struct input_handler *handler;
    const char *path;
    int error;

    set_bit(EV_SYN, dev->evbit);

    /*
     * If delay and period are pre-set by the driver, then autorepeating
     * is handled by the driver itself and we don't do it in input.c.
     */

    init_timer(&dev->timer);
    if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
        dev->timer.data = (long) dev;
        dev->timer.function = input_repeat_key;
        dev->rep[REP_DELAY] = 250;
        dev->rep[REP_PERIOD] = 33;
    }

    if (!dev->getkeycode)
        dev->getkeycode = input_default_getkeycode;

    if (!dev->setkeycode)
        dev->setkeycode = input_default_setkeycode;

    list_add_tail(&dev->node, &input_dev_list);//置入到链表
    snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id),
         "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);

    if (!dev->cdev.dev)
        dev->cdev.dev = dev->dev.parent;

    error = class_device_add(&dev->cdev);
    if (error)
        return error;

    path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL);
    printk(KERN_INFO "input: %s as %s\n",
        dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
    kfree(path);

    list_for_each_entry(handler, &input_handler_list, node)
        input_attach_handler(dev, handler);//input_handler_list链表中的每个都调用这个函数  
该函数根据input_handler的id_table判断能不能支持这个input_dev
input_wakeup_procfs_readers(); return 0; }

 

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
	const struct input_device_id *id;
	int error;

	if (handler->blacklist && input_match_device(handler->blacklist, dev))
		return -ENODEV;

	id = input_match_device(handler->id_table, dev);
	if (!id)
		return -ENODEV;

	error = handler->connect(handler, dev, id);
	if (error && error != -ENODEV)
		printk(KERN_ERR
			"input: failed to attach handler %s to device %s, "
			"error: %d\n",
			handler->name, kobject_name(&dev->cdev.kobj), error);

	return error;
}

  



input_register_handler
// 放入数组
input_table[handler->minor >> 5] = handler;

// 放入链表
list_add_tail(&handler->node, &input_handler_list);

// 对于每个input_dev,调用input_attach_handler
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler); // 根据input_handler的id_table判断能否支持这个input_dev


注册输入设备:
input_register_device
// 放入链表
list_add_tail(&dev->node, &input_dev_list);

// 对于每一个input_handler,都调用input_attach_handler
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler); // 根据input_handler的id_table判断能否支持这个input_dev


input_attach_handler
id = input_match_device(handler->id_table, dev);

error = handler->connect(handler, dev, id);


注册input_dev或input_handler时,会两两比较左边的input_dev和右边的input_handler,
根据input_handler的id_table判断这个input_handler能否支持这个input_dev,
如果能支持,则调用input_handler的connect函数建立"连接


怎么建立连接?

在 evdec.c中找这个例子怎么建立连接

static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
             const struct input_device_id *id)
{
    struct evdev *evdev;
    struct class_device *cdev;
    dev_t devt;
    int minor;
    int error;

    for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
    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);
    init_waitqueue_head(&evdev->wait);

    evdev->exist = 1;
    evdev->minor = minor;
    evdev->handle.dev = dev;
    evdev->handle.name = evdev->name;
    evdev->handle.handler = handler;
    evdev->handle.private = evdev;
    sprintf(evdev->name, "event%d", minor);

    evdev_table[minor] = evdev;

    devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),

    cdev = class_device_create(&input_class, &dev->cdev, devt,
                   dev->cdev.dev, evdev->name);
    if (IS_ERR(cdev)) {
        error = PTR_ERR(cdev);
        goto err_free_evdev;
    }

    /* temporary symlink to keep userspace happy */
    error = sysfs_create_link(&input_class.subsys.kobj,
                  &cdev->kobj, evdev->name);
    if (error)
        goto err_cdev_destroy;

    error = input_register_handle(&evdev->handle);
    if (error)
        goto err_remove_link;

    return 0;

 err_remove_link:
    sysfs_remove_link(&input_class.subsys.kobj, evdev->name);
 err_cdev_destroy:
    class_device_destroy(&input_class, devt);
 err_free_evdev:
    kfree(evdev);
    evdev_table[minor] = NULL;
    return error;
}
View Code

 


1. 分配一个input_handle结构体

struct evdev *evdev;

evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);

2.
input_handle.dev = input_dev; // 指向左边的输入设备input_dev
input_handle.handler = input_handler; // 指向右边的input_handler

3. 注册这个handle:

error = input_register_handle(&evdev->handle);

  

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

	list_add_tail(&handle->d_node, &handle->dev->h_list);
	list_add_tail(&handle->h_node, &handler->h_list);

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

	return 0;
}

  连接的时候构建一个input_handle结构体其中刚有个成员.dev  这个成员指向输入设备即input_device 

    还有一个.handler成员   他指向input_handler

 

input_handler有个h_list  指向了input_handle;   对于input_handler来讲就可以由h_list 找到input_handle然后找到dev然后找到相应的设备了
inpu_dev      有个  h_list 指向了input_handle;   对输入设备inpu_dev 来讲就可以由h_list 找到input_handle 然后input_handle 发现到inpu_dev 所对应的处理input_handler

 

 


evdev_connect
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); // 分配一个input_handle

// 设置
evdev->handle.dev = dev; // 指向左边的input_dev
evdev->handle.name = evdev->name;
evdev->handle.handler = handler; // 指向右边的input_handler
evdev->handle.private = evdev;

// 注册
error = input_register_handle(&evdev->handle);

 

 


 

怎么读按键?
app: read
--------------------------
.......
evdev_read
// 无数据并且是非阻塞方式打开,则立刻返回
if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK))
return -EAGAIN;

// 否则休眠
retval = wait_event_interruptible(evdev->wait,
client->head != client->tail || !evdev->exist);

谁来唤醒?不好找,那就找在哪里休眠?在距代码中是在这evdev->wait里休眠  那就在这里唤醒  全局搜索evdev->wait
evdev_event时间处理函数函数:
wake_up_interruptible(&evdev->wait);

evdev_event来唤醒,那么evdev_event被谁调用?

猜:应该是硬件相关的代码,input_dev那层调用的
在设备的中断服务程序里,确定事件是什么,然后调用相应的input_handler的event处理函数

gpio_keys_isr
// 上报事件
input_event(input, type, button->code, !!state);
input_sync(input);

input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
struct input_handle *handle;

list_for_each_entry(handle, &dev->h_list, d_node)
if (handle->open)
handle->handler->event(handle, type, code, value);  evdev_event被调用

 

evdev_event函数分析:

当设备驱动层发生一个输入事件的时候,设备驱动层就会去读取设备硬件,获取事件信息,然后上报给核心层,核心层索引到与当前设备匹配成功的所有上层handler,然后再上报给

这些handler,上层就会调用event函数将事件包封装放入缓冲区,然后唤醒进程。  我对这个函数的内部细节并不了解,只能知道大概加上自己的一些猜想.

 

 

其实下层可以上报的事件都在我们的内核中是定义好的,我们都可以上报这些事,但是input子系统的上层输入事件驱动层的各个handler只能够处理某一些事件(event除外),

例如joy handler只能处理摇杆类型的事件,key handler只能处理键盘,内部实现的原理就是会在核心层做handler和device匹配的过程。如果我们的上报的事件与多个handler都能够匹配成功,那么绑定之后核心层会向这多个handler都上报事件,再由handler上报给应用层。

同一个input设备可以对应多个次设备号,因为对于一个输入设备来说,他在进行匹配的时候可能会与多个handler匹配成功,匹配成功就会在连接过程中创建设备文件,而不同的handler创建的设备文件的次设备号是不一样的,所以就是导致一个设备对应多个次设备号。

 

推荐阅读