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里面的内容变动很大,具体还没有分析,以后会补上。

     

  • 相关阅读:
    jquery toggle(listenerOdd, listenerEven)
    struts quick start
    hdu 1518 Square (dfs)
    hdu 2544 最短路 (最短路径)
    hdu 1754 I Hate It (线段树)
    hdu 1856 More is better (并查集)
    hdu 1358 Period (KMP)
    hdu 2616 Kill the monster (DFS)
    hdu 2579 Dating with girls(2) (bfs)
    zoj 2110 Tempter of the Bone (dfs)
  • 原文地址:https://www.cnblogs.com/-glb/p/11229100.html
Copyright © 2011-2022 走看看