zoukankan      html  css  js  c++  java
  • 输入子系统

    注意:该输入子系统框架是基于linux3.4内核之前的版本来进行说明的,现在的linux内核版本4.4这一部分变动很大。

    输入子系统框架:

     

    drivers/input/input.c

    input_init  > err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
    static const struct file_operations input_fops = {
            .owner = THIS_MODULE,
            .open = input_open_file,  
            .llseek = noop_llseek,
      };
    问:input_fops没有读函数,怎样读按键呢?
    input_open_file
     handler = input_table[iminor(inode) >> 5];
       new_fops = fops_get(handler->fops);
       file->f_op = new_fops; 
    err = new_fops->open(inode, file)
    app: read  > .......>file->f_op->read
    input_table[]数组由谁构造?
    input_table数组是在
    input_register_handler函数中被构造的。那么input_register_handler函数又是被谁调用的?
    搜索一下,发现是由evdev.c,keyboard.c, mousedev.c等这几个文件调用,它们通过调用input_register_handler函数向上注册handler。
    注册input_handler
    input_register_handler
      input_table[handler->minor >> 5] = handler;

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

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

    注册输入设备:
    input_register_device
    //放入链表
    list_add_tail(&dev->node, &input_dev_list);
    //对于每一个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_dev还是input_handler,都调用input_attach_handler进行两两匹配。

    注册input_dev或Input_handler时,会两两比较input_dev和input_handler

    根据input_handler中的id_table判断input_handler能否支持这个input_dev,如果能支持,则调用input_handler中的connect函数建立连接。

    怎么建立连接?

    1、分配一个input_handle结构体,

    2、input_handle.dev = input_dev    //指向input_dev

       input_handle.handler = input_handler;// 指向input_handler

    3、注册

          input_register_handle

        input_handler->h_list =&input_handle

        input_dev->h_list        =&input_handle

     1 static const struct file_operations input_fops = {
     2     .owner = THIS_MODULE,
     3     .open = input_open_file,  //在该结构体中,只有一个input_open_file这个函数,这个函数只是起一个中转作用,在里面肯定还做了其他事情。
     4     .llseek = noop_llseek,
     5 };
     6 
     7 static int __init input_init(void)
     8 {
     9     int err;
    10 
    11     err = class_register(&input_class);
    12     if (err) {
    13         pr_err("unable to register input_dev class
    ");
    14         return err;
    15     }
    16 
    17     err = input_proc_init();
    18     if (err)
    19         goto fail1;
    20 
    21     err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
    22     if (err) {
    23         pr_err("unable to register char major %d", INPUT_MAJOR);
    24         goto fail2;
    25     }
    26 
    27     return 0;
     1 static int input_open_file(struct inode *inode, struct file *file)
     2 {
     3     struct input_handler *handler;
     4     const struct file_operations *old_fops, *new_fops = NULL;
     5     int err;
     6 
     7     err = mutex_lock_interruptible(&input_mutex);
     8     if (err)
     9         return err;
    10 
    11     /* No load-on-demand here? */
       /*次设备号右移5位,即相当于除以32,以这个数为下标,从input_table数组中取出一项,赋给handler.*/
    12 handler = input_table[iminor(inode) >> 5]; 13 if (handler)/*handler中有一个file_operation结构体,把handler中的file_operation结构体取出来赋给new_fops.*/ 14 new_fops = fops_get(handler->fops); 15 16 mutex_unlock(&input_mutex); 17 18 /* 19 * That's _really_ odd. Usually NULL ->open means "nothing special", 20 * not "no device". Oh, well... 21 */ 22 if (!new_fops || !new_fops->open) { 23 fops_put(new_fops); 24 err = -ENODEV; 25 goto out; 26 } 27 28 old_fops = file->f_op; 29 file->f_op = new_fops; /*打开的这个文件的f_op= 新的fops*/ 31 err = new_fops->open(inode, file);/*调用新的fops中的open函数,以后如果读的话,就是用到了handler里面的新的fops*/ 32 if (err) { 33 fops_put(file->f_op); 34 file->f_op = fops_get(old_fops); 35 } 36 fops_put(old_fops); 37 out: 38 return err;
     1 struct input_handler {
     3     void *private;
     5     void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
     6     bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
     7     bool (*match)(struct input_handler *handler, struct input_dev *dev);
     8     int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
     9     void (*disconnect)(struct input_handle *handle);
    10     void (*start)(struct input_handle *handle);
    12     const struct file_operations *fops;
    13     int minor;
    14     const char *name;
    15 
    16     const struct input_device_id *id_table;
    17 
    18     struct list_head    h_list;
    19     struct list_head    node;
    20 };

    input_table[]数组是在input_register_handler这个函数中构造的。

     1 int input_register_handler(struct input_handler *handler)
     2 {
     3     struct input_dev *dev;
     4     int retval;
     5 
     6     retval = mutex_lock_interruptible(&input_mutex);
     7     if (retval)
     8         return retval;
     9 
    10     INIT_LIST_HEAD(&handler->h_list);
    11 
    12     if (handler->fops != NULL) {
    13         if (input_table[handler->minor >> 5]) {
    14             retval = -EBUSY;
    15             goto out;
    16         }
    17         input_table[handler->minor >> 5] = handler;
    18     }
    19 
    20     list_add_tail(&handler->node, &input_handler_list);
    21 
    22     list_for_each_entry(dev, &input_dev_list, node)
    23         input_attach_handler(dev, handler);
    24 
    25     input_wakeup_procfs_readers();
    26 
    27  out:
    28     mutex_unlock(&input_mutex);
    29     return retval;

    input_register_handler这个函数又是被谁调用的呢?选取evdev.c进行分析。

     1 static struct input_handler evdev_handler = {
     2     .event        = evdev_event,
     3     .connect    = evdev_connect,
     4     .disconnect    = evdev_disconnect,
     5     .fops        = &evdev_fops,   /*这个fops以前是由我们自己构造,现在内核已经帮我们做好了。*/
     6     .minor        = EVDEV_MINOR_BASE,
     7     .name        = "evdev",
     8     .id_table    = evdev_ids,//表示该handler能够支持哪些输入设备。如果能够支持的话,就调用connect函数。
     9 };
    10 
    11 static int __init evdev_init(void)
    12 {
      /*input_register_handler会将evdev_handler向上注册,通过上面的分析可知,就是把这个evdev_handler放入Input_table数组
    中,以次设备号右移5位为下标。这样的话,在input_open_file中获得的handler就是evdev_handler这个结构体,new_fops就是evdev_handler
    结构体中的fops
    */ 13 return input_register_handler(&evdev_handler); 14 }
     1 static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
     2 {
     3     const struct input_device_id *id;
     4     int error;
     5 
     6     id = input_match_device(handler, dev);
     7     if (!id)
     8         return -ENODEV;
     9 
    10     error = handler->connect(handler, dev, id);
    11     if (error && error != -ENODEV)
    12         pr_err("failed to attach handler %s to device %s, error: %d
    ",
    13                handler->name, kobject_name(&dev->dev.kobj), error);
    14 
    15     return error;
    16 }
     1 static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
     2              const struct input_device_id *id)
     3 {
     4     struct evdev *evdev;
    17     evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);   分配
    18     if (!evdev)
    19         return -ENOMEM;
    20 
    21     INIT_LIST_HEAD(&evdev->client_list);
    22     spin_lock_init(&evdev->client_lock);
    23     mutex_init(&evdev->mutex);
    24     init_waitqueue_head(&evdev->wait);
    25 
    26     dev_set_name(&evdev->dev, "event%d", minor);
    27     evdev->exist = true;
    28     evdev->minor = minor;
    29     设置
    30     evdev->handle.dev = input_get_device(dev);   //指向input_dev结构体
    31     evdev->handle.name = dev_name(&evdev->dev);
    32     evdev->handle.handler = handler;  //指向input_handler结构体
    33     evdev->handle.private = evdev;
    注册 41 error = input_register_handle(&evdev->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;
    
        /*
         * Filters go to the head of the list, normal handlers
         * to the tail.
         */
        if (handler->filter)
            list_add_rcu(&handle->d_node, &dev->h_list);
        else
            list_add_tail_rcu(&handle->d_node, &dev->h_list);
    
        mutex_unlock(&dev->mutex);
    
        /*
         * 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_rcu(&handle->h_node, &handler->h_list);
    
        if (handler->start)
            handler->start(handle);
    
        return 0;
    }

    怎么读按键?

    app:read------------>evdev_read(还是以evdev.c为例)

     1 static const struct file_operations evdev_fops = {
     2     .owner        = THIS_MODULE,
     3     .read        = evdev_read,
     4     .write        = evdev_write,
     5     .poll        = evdev_poll,
     6     .open        = evdev_open,
     7     .release    = evdev_release,
     8     .unlocked_ioctl    = evdev_ioctl,
     9 #ifdef CONFIG_COMPAT
    10     .compat_ioctl    = evdev_ioctl_compat,
    11 #endif
    12     .fasync        = evdev_fasync,
    13     .flush        = evdev_flush,
    14     .llseek        = no_llseek,
    15 };
     1 static ssize_t evdev_read(struct file *file, char __user *buffer,
     2               size_t count, loff_t *ppos)
     3 {
     4     struct evdev_client *client = file->private_data;
     5     struct evdev *evdev = client->evdev;
     6     struct input_event event;
     7     int retval = 0;
     8 
     9     if (count < input_event_size())
    10         return -EINVAL;
    11 
    12     if (!(file->f_flags & O_NONBLOCK)) {    //以阻塞的方式打开,没有数据可读,则进行休眠。那在什么地方被唤醒呢?搜evdev->wait即可。
    13         retval = wait_event_interruptible(evdev->wait,
    14                 client->packet_head != client->tail ||
    15                 !evdev->exist);
    16         if (retval)
    17             return retval;
    18     }
    19 
    20     if (!evdev->exist)
    21         return -ENODEV;
    22 
    23     while (retval + input_event_size() <= count &&
    24            evdev_fetch_next_event(client, &event)) {
    25 
    26         if (input_event_to_user(buffer + retval, &event))
    27             return -EFAULT;
    28 
    29         retval += input_event_size();
    30     }
    31 
    32     if (retval == 0 && (file->f_flags & O_NONBLOCK))//以非阻塞的方式打开,没有数据可读,立即返回。
    33         return -EAGAIN;
    34 
    35     return retval;
    1 static void evdev_event(struct input_handle *handle,
    2             unsigned int type, unsigned int code, int value)
    3 {
    5         wake_up_interruptible(&evdev->wait);
    6 }

    evdev_event是被谁调用的呢?

    猜测:应该是硬件相关的代码,input_dev那层调用的

    在设备的中断服务程序里面,确定事件是什么,然后调用相应的input_handler的event处理函数。

    看一个例子:

    gpio_keys_irq_isr

       上报事件

      input_event(input, EV_KEY, button->code, 1);
      input_sync(input);

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

      input_handle_event(dev, type, code, value);

        input_pass_event(dev, type, code, value);    

            list_for_each_entry_rcu(handle, &dev->h_list, d_node) {
              if (!handle->open)
                continue;

              handler = handle->handler;

                  handler->event(handle, type, code, value)

              }

    如何写符合输入子系统框架的驱动程序?

    1、分配一个input_dev结构体;

    2、设置

    3、注册

    4、硬件相关的代码,比如在中断服务程序里上报事件

     以上就是输入子系统的框架及调用关系,不过这是基于较老的linux内核版本,在工作中使用的内核是linux4.4.对于Input.c里面的内容变动很大,具体还没有分析,以后会补上。

     

  • 相关阅读:
    cocos2dx3.1从零学习(二)菜单、场景切换、场景传值
    XCode5添加新建类模板(Cocos2dx Template Class for Scene or Layer)
    根据Uri获取图片绝对路径,解决Android4.4以上版本Uri转换
    如何学习 cocos2d-x ?
    Java数据类型中String、Integer、int相互间的转换
    Android各种效果集合
    重新生成IE02
    nvl与 is not null的区别等
    自定义view
    select into from 和 insert into select 的用法和区别(转)
  • 原文地址:https://www.cnblogs.com/-glb/p/11229100.html
Copyright © 2011-2022 走看看