输入子系统

注意:该输入子系统框架是基于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里面的内容变动很大,具体还没有分析,以后会补上。

 

原文地址:https://www.cnblogs.com/-glb/p/11229100.html