zoukankan      html  css  js  c++  java
  • input子系统详解2

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

    这一节认真追踪一下代码

    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
    ");
     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
    ",
            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
    ",
    			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
    ");
            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创建的设备文件的次设备号是不一样的,所以就是导致一个设备对应多个次设备号。

  • 相关阅读:
    11.11 ntsysv:管理开机服务
    11.13 ethtool:查询网卡参数
    11.14 mii-tool:管理网络接口的状态
    11.15 dmidecode:查询系统硬件信息
    11.16-18 lsci、ipcs、ipcrm:清除ipc相关信息
    Devops 导论
    * SPOJ PGCD Primes in GCD Table (需要自己推线性筛函数,好题)
    SPOJ
    HDU 1695 莫比乌斯反演
    HDU 1800 hash 找出现最多次数的字符串的次数
  • 原文地址:https://www.cnblogs.com/zhaobinyouth/p/6257871.html
Copyright © 2011-2022 走看看