zoukankan      html  css  js  c++  java
  • 输入子系统(三)事件上报分析

    很多分析输入子系统的文章已经讲得很清楚了,这里主要是记录自己的学习过程。参考的几篇文章:

    输入子系统学习笔记之按键实例编程

    输入子系统学习笔记之源码分析1_框架性分析

    输入子系统学习笔记之源码分析2_数据结构分析

    输入子系统学习笔记之源码分析3_流程分析

    这里已evdev.c为例分析分析输入子系统时间上报的流程。

    evdev设备次设备号从64开始,最多支持32个evdev设备。

    #define EVDEV_MINOR_BASE	64
    #define EVDEV_MINORS		32
    

    1. 重要的结构体

     1 struct evdev {
     2     int open;
     3     int minor;
     4     struct input_handle handle;
     5     wait_queue_head_t wait;
     6     struct evdev_client __rcu *grab;
     7     struct list_head client_list;
     8     spinlock_t client_lock; /* protects client_list */
     9     struct mutex mutex;
    10     struct device dev;
    11     bool exist;
    12 };
    13 
    14 struct evdev_client {
    15     unsigned int head;
    16     unsigned int tail;
    17     unsigned int packet_head; /* [future] position of the first element of next packet */
    18     spinlock_t buffer_lock; /* protects access to buffer, head and tail */
    19     struct fasync_struct *fasync;
    20     struct evdev *evdev;
    21     struct list_head node;
    22     unsigned int bufsize;
    23     struct input_event buffer[];
    24 };
    25 
    26 static struct evdev *evdev_table[EVDEV_MINORS];
    struct evdev

    1.1 evdev结构体在evdev_connect(struct input_handler *handler, struct input_dev *dev,const struct input_device_id *id)中使用。

    每次evdev_connect()创建一个设备,都会分配一个evdev结构体,并根据次设备号填充evdev_table[]数组。也就是说,每一个设备都有一个evdev结构体与之对应,通过次设备号实现对应关系。具体参看代码。

     1 /*
     2  * Create new evdev device. Note that input core serializes calls
     3  * to connect and disconnect so we don't need to lock evdev_table here.
     4  */
     5 static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
     6              const struct input_device_id *id)
     7 {
     8     struct evdev *evdev;
     9     int minor;
    10     int error;
    11 
    12     for (minor = 0; minor < EVDEV_MINORS; minor++)
    13         if (!evdev_table[minor])
    14             break;
    15 
    16     if (minor == EVDEV_MINORS) {
    17         pr_err("no more free evdev devices
    ");
    18         return -ENFILE;
    19     }
    20 
    21     evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
    22     if (!evdev)
    23         return -ENOMEM;
    24 
    25     INIT_LIST_HEAD(&evdev->client_list);
    26     spin_lock_init(&evdev->client_lock);
    27     mutex_init(&evdev->mutex);
    28     init_waitqueue_head(&evdev->wait);
    29 
    30     dev_set_name(&evdev->dev, "event%d", minor);
    31     evdev->exist = true;
    32     evdev->minor = minor;
    33 
    34     evdev->handle.dev = input_get_device(dev);
    35     evdev->handle.name = dev_name(&evdev->dev);
    36     evdev->handle.handler = handler;
    37     evdev->handle.private = evdev;
    38 
    39     evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
    40     evdev->dev.class = &input_class;
    41     evdev->dev.parent = &dev->dev;
    42     evdev->dev.release = evdev_free;
    43     device_initialize(&evdev->dev);
    44 
    45     error = input_register_handle(&evdev->handle);
    46     if (error)
    47         goto err_free_evdev;
    48 
    49     error = evdev_install_chrdev(evdev);
    50     if (error)
    51         goto err_unregister_handle;
    52 
    53     error = device_add(&evdev->dev);
    54     if (error)
    55         goto err_cleanup_evdev;
    56 
    57     return 0;
    58 
    59  err_cleanup_evdev:
    60     evdev_cleanup(evdev);
    61  err_unregister_handle:
    62     input_unregister_handle(&evdev->handle);
    63  err_free_evdev:
    64     put_device(&evdev->dev);
    65     return error;
    66 }
    evdev_connect()

    1.2 evdev_client结构体在evdev_open(struct inode *inode, struct file *file)中使用,每打开一个设备,先通过次设备号寻找到对应的evdev结构体,

    再在evdev结构体上关联一个evdev_client结构体。evdev_open(),evdev_write()都要用到evdev_client结构体,evdev_client结构体中定义了一个环形缓冲区用于存放上报事件的input_event结构体。

    1.3 input_event结构体是Linux抽象出来的用于存储时间的结构体。

    1 struct input_event {
    2     struct timeval time;
    3     __u16 type;
    4     __u16 code;
    5     __s32 value;
    6 };

    2. 按键驱动输入子系统范例

    以下是一个输入子系统的按键驱动范例代码,可以在OK6410开发板上运行。

    符合输入子系统框架的驱动的四步:a. 分配一个input_dev结构体;b. 设置;c. input_register_device()注册;d. 硬件相关的代码

      1 #include <linux/module.h> //
      2 #include <linux/init.h>//
      3 #include <linux/miscdevice.h>
      4 #include <linux/fs.h>//
      5 #include <linux/device.h>
      6 #include <linux/interrupt.h>
      7 #include <linux/wait.h>
      8 #include <linux/poll.h>
      9 #include <linux/sched.h>
     10 #include <asm/uaccess.h>//
     11 #include <asm/io.h>//
     12 #include <linux/workqueue.h>
     13 #include <linux/timer.h>
     14 #include <linux/input.h>
     15 #include <linux/irq.h>
     16 #include <linux/slab.h>
     17 #include <linux/sysctl.h>
     18 #include <linux/proc_fs.h>
     19 #include <linux/delay.h>
     20 #include <linux/gpio_keys.h>
     21 #include <linux/workqueue.h>
     22 #include <linux/gpio.h>
     23 
     24 volatile unsigned long * gpncon = NULL;
     25 volatile unsigned long * gpndat = NULL;
     26 
     27 static struct timer_list  my_timer;
     28 
     29 struct pin_desc{
     30     int irq;
     31     char * name;
     32     unsigned int pin;
     33     unsigned int key_val;
     34 };
     35 
     36 struct input_dev *input;
     37 /*
     38 °´¼üÖµ°´ÏÂʱ0x00,0x01,0x02,0x03,0x04
     39 ËÉ¿ªÊ± 0x80,0x81,0x82,0x83,0x84
     40 */
     41  
     42 struct pin_desc pins_desc[4] = {
     43 
     44     {IRQ_EINT(0),   "S0",    S3C64XX_GPN(0),     KEY_L                   },
     45     {IRQ_EINT(1),   "S1",    S3C64XX_GPN(1),     KEY_S                   },
     46     {IRQ_EINT(2),   "S2",    S3C64XX_GPN(2),     KEY_ENTER            },
     47     {IRQ_EINT(3),   "S3",    S3C64XX_GPN(3),     KEY_LEFTSHIFT      },
     48 };
     49 
     50 static irqreturn_t key_irq(int irq, void *dev_id)
     51 {
     52 
     53     my_timer.data =(unsigned long)dev_id;
     54     mod_timer(&my_timer,jiffies+HZ/100);    
     55     return 0;
     56 }
     57 
     58 static void buttons_timer_func(unsigned long data)
     59 {
     60     int pinval;
     61     struct pin_desc * pindes = (struct pin_desc *)data;    
     62     
     63     printk(KERN_WARNING "this is my_work1_func
    ");
     64     if(!pindes)
     65         return;
     66     
     67     pinval = gpio_get_value(pindes->pin);
     68     if (pinval)
     69     {
     70         /* ËÉ¿ªµÄ */
     71         input_event(input, EV_KEY, pindes->key_val, 0);
     72     }
     73     else
     74     {
     75         /* °´Ï嵀 */
     76         input_event(input, EV_KEY, pindes->key_val, 1);
     77     }
     78     
     79     input_sync(input);
     80 
     81 }
     82 
     83 static void key_hw_init(void)
     84 {
     85     unsigned int data;
     86     gpncon = (volatile unsigned long *)ioremap(0x7F008830,4);    
     87     gpndat = gpncon + 1;    
     88 
     89     data=readl(gpncon);
     90     data &= ~0xFF;
     91     data |= 0xAA;
     92     writel(data,gpncon);
     93 }
     94 
     95 static int gq_key_init(void)
     96 {
     97     int i;
     98     /*Çý¶¯Èë¿Úº¯ÊýÖе÷ÓÃregister_chrdevº¯Êý*/
     99     printk(KERN_WARNING "gq_key_init
    ");
    100     // 1 allocate input_dev struct
    101     input = input_allocate_device();
    102 
    103     // 2 set
    104     // 2.1 set which event to generate
    105     __set_bit(EV_KEY, input->evbit);
    106     // 2.2 set which keys to generate
    107     __set_bit(KEY_L,input->keybit);
    108     __set_bit(KEY_S,input->keybit);
    109     __set_bit(KEY_ENTER,input->keybit);
    110     __set_bit(KEY_LEFTSHIFT,input->keybit);
    111 
    112     // 3 register
    113     input_register_device(input);
    114 
    115     // 4 hardware 
    116     key_hw_init();
    117     init_timer(&my_timer);
    118     my_timer.function = buttons_timer_func;
    119     add_timer(&my_timer);
    120 
    121     for(i=0;i<4;i++){
    122 
    123         request_irq(pins_desc[i].irq, key_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, pins_desc[i].name,&pins_desc[i]);
    124     }
    125     
    126     return 0;
    127 }
    128 
    129 static void  gq_key_exit(void)
    130 {
    131 
    132     int i;
    133     printk(KERN_WARNING "gq_key_exit
    ");
    134     
    135 
    136     for(i=0;i<4;i++){
    137 
    138         free_irq(pins_desc[i].irq,&pins_desc[i]);
    139     }    
    140     del_timer(&my_timer);
    141     input_unregister_device(input);    
    142     input_free_device(input);
    143     iounmap(gpncon );
    144 }
    145 
    146 module_init(gq_key_init);
    147 module_exit(gq_key_exit);
    148 MODULE_LICENSE("GPL");
    gq_key_input_system_module

    加载内核模块后的调用流程如下,生成/dev/event1设备节点。主设备号13,次设备号65。

    static int gq_key_init(void)
      -->input_register_device(input);
        -->input_attach_handler(dev, handler);
          -->handler->connect(handler, dev, id);//就是evdev_connect()
            -->创建/dev/event1

    测试:#hexdump /dev/event1,按键按下在松开后打印信息如下。

             /* 秒 */ /* us */ /*type code */ /* val */

    0000000 6dbf 386d a589 0005 0001 0026 0001 0000

    0000010 6dbf 386d a593 0005 0000 0000 0000 0000

    0000020 6dc2 386d a14e 0007 0001 0026 0000 0000

    0000030 6dc2 386d a154 0007 0000 0000 0000 0000

     hexdump /dev/event1相当于先打开设备open("/dev/event1"),在执行read("/dev/event1")。

    3. 打开设备流程分析

    3.1根据主设备号,找到对应的fops,应该执行input_open_file()函数。

     1 static const struct file_operations input_fops = {
     2     .owner = THIS_MODULE,
     3     .open = 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;
    28 
    29  fail2:    input_proc_exit();
    30  fail1:    class_unregister(&input_class);
    31     return err;
    32 }
    input_init()
     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? */
    12     handler = input_table[iminor(inode) >> 5];
    13     if (handler)
    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;
    30 
    31     err = new_fops->open(inode, file);
    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;
    39 }
    input_open_file()

    static int input_open_file(struct inode *inode, struct file *file)

      -->struct input_handler *handler = input_table[iminor(inode) >> 5];//根据打开文件的次设备号获得一个input_handler结构体

      -->struct file_operations *new_fops = fops_get(handler->fops);//重定位file->f_op到handler指定的fops

      -->file->f_op = new_fops;//重定位后,read,write都是调用handler->fops指定的函数

      -->new_fops->open(inode, file);//执行handler->fops->open(inode,file)

    3.2 handler-fops->open(inode,file)

    根据次设备号,找到的input_table[65>>5]=input_table[2]在evdev.c中赋值为struct input_handler evdev_handler。

    static struct input_handler evdev_handler = {
    .event    = evdev_event,
    .connect    = evdev_connect,
    .disconnect    = evdev_disconnect,
    .fops    = &evdev_fops,
    .minor    = EVDEV_MINOR_BASE,
    .name    = "evdev",
    .id_table    = evdev_ids,
    };
    static const struct file_operations evdev_fops = {
        .owner        = THIS_MODULE,
        .read        = evdev_read,
        .write        = evdev_write,
        .poll        = evdev_poll,
        .open        = evdev_open,
        .release    = evdev_release,
        .unlocked_ioctl    = evdev_ioctl,
        .fasync        = evdev_fasync,
        .flush        = evdev_flush,
        .llseek        = no_llseek,
    };

    继续调用evdev_open(),执行流程如下:

    static int evdev_open(struct inode *inode, struct file *file)
      -->int i = iminor(inode) - EVDEV_MINOR_BASE; 
      -->struct evdev *evdev = evdev_table[i];//根据次设备号找到connect时指定的evdev_table[i]
      -->client->bufsize = evdev_compute_buffer_size(evdev->handle.dev);//设置evdev_client结构体中缓冲区的大小
      -->client->evdev = evdev;//关联evdev与evdev_client
      -->evdev_attach_client(evdev, client);//关联evdev与evdev_client
      -->evdev_open_device(evdev);
        -->input_open_device(&evdev->handle);
          -- if (!dev->users++ && dev->open) dev->open(dev); //如果相应的input_device中定义了open函数则执行

      -->file->private_data = client;//保存evdev_client结构体指针

     1 static int evdev_open(struct inode *inode, struct file *file)
     2 {
     3     struct evdev *evdev;
     4     struct evdev_client *client;
     5     int i = iminor(inode) - EVDEV_MINOR_BASE;
     6     unsigned int bufsize;
     7     int error;
     8 
     9     if (i >= EVDEV_MINORS)
    10         return -ENODEV;
    11 
    12     error = mutex_lock_interruptible(&evdev_table_mutex);
    13     if (error)
    14         return error;
    15     evdev = evdev_table[i];
    16     if (evdev)
    17         get_device(&evdev->dev);
    18     mutex_unlock(&evdev_table_mutex);
    19 
    20     if (!evdev)
    21         return -ENODEV;
    22 
    23     bufsize = evdev_compute_buffer_size(evdev->handle.dev);
    24 
    25     client = kzalloc(sizeof(struct evdev_client) +
    26                 bufsize * sizeof(struct input_event),
    27              GFP_KERNEL);
    28     if (!client) {
    29         error = -ENOMEM;
    30         goto err_put_evdev;
    31     }
    32 
    33     client->bufsize = bufsize;
    34     spin_lock_init(&client->buffer_lock);
    35     client->evdev = evdev;
    36     evdev_attach_client(evdev, client);
    37 
    38     error = evdev_open_device(evdev);
    39     if (error)
    40         goto err_free_client;
    41 
    42     file->private_data = client;
    43     nonseekable_open(inode, file);
    44 
    45     return 0;
    46 
    47  err_free_client:
    48     evdev_detach_client(evdev, client);
    49     kfree(client);
    50  err_put_evdev:
    51     put_device(&evdev->dev);
    52     return error;
    53 }
    evdev_open()

    4. 读取设备流程分析

    static ssize_t evdev_read(struct file *file, char __user *buffer,size_t count, loff_t *ppos)
      -->struct evdev_client *client = file->private_data;//从文件中获取evdev_client结构体
      -->struct evdev *evdev = client->evdev;//根据evdev_client找到evdev结构体
      -->retval = wait_event_interruptible(evdev->wait,client->packet_head != client->tail || !evdev->exist);//没有数据休眠
      -->evdev_fetch_next_event(client, &event)//退出休眠后从环形缓冲区读取数据,一个input_event结构体
        -->have_event = client->head != client->tail;
        -->*event = client->buffer[client->tail++];
        -->client->tail &= client->bufsize - 1;
      -->input_event_to_user(buffer + retval, &event)//从内核读数据到用户空间
        -->copy_to_user(buffer, event, sizeof(struct input_event))

     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;
     8     
     9     if (count < input_event_size())
    10         return -EINVAL;
    11 
    12     if (client->packet_head == client->tail && evdev->exist &&
    13         (file->f_flags & O_NONBLOCK))
    14         return -EAGAIN;
    15     retval = wait_event_interruptible(evdev->wait,
    16         client->packet_head != client->tail || !evdev->exist);
    17 
    18     if (retval)
    19         return retval;
    20 
    21     if (!evdev->exist)
    22         return -ENODEV;
    23 
    24     while (retval + input_event_size() <= count &&
    25            evdev_fetch_next_event(client, &event)) {
    26 
    27         if (input_event_to_user(buffer + retval, &event))
    28             return -EFAULT;
    29 
    30         retval += input_event_size();
    31     }
    32 
    33     return retval;
    34 }
    evdev_read()

    5. 上报事件流程分析

    input_device通知input核心层有input_event产生,核心层再调用input_handler层的input_handler->event处理函数。本例中在按键中断中上报事件,代码很简单。

     1 static void buttons_timer_func(unsigned long data)
     2 {
     3     int pinval;
     4     struct pin_desc * pindes = (struct pin_desc *)data;    
     5     
     6     if(!pindes)
     7         return;
     8     
     9     pinval = gpio_get_value(pindes->pin);
    10     if (pinval)
    11     {
    12         input_event(input, EV_KEY, pindes->key_val, 0);
    13     }
    14     else
    15     {
    16         input_event(input, EV_KEY, pindes->key_val, 1);
    17     }    
    18     input_sync(input);
    19 
    20 }

    //          input_dev type       code                  value
    input_event(input, EV_KEY, pindes->key_val, 1);
      -->input_handle_event(dev, type, code, value);
        -->disposition = INPUT_PASS_TO_HANDLERS;
        -->input_pass_event(dev, type, code, value);
          -->struct input_handle *handle = rcu_dereference(dev->grab);//找到input_dev对应的handle结构体
          -->handle->handler->event(handle, type, code, value);//调用handle->handler->event

    static void evdev_event(struct input_handle *handle,unsigned int type, unsigned int code, int value)
      -->struct evdev *evdev = handle->private;//找到handle关联的evdev结构体
      -->struct evdev_client *client = rcu_dereference(evdev->grab);//找到evdev关联的evdev_client结构体
      -->do_gettimeofday(&event.time); event.type = type; event.code = code; event.value = value;//填充input_event结构体
      -->evdev_pass_event(client, &event);//将input_event填充到evdev_client的环形缓冲区中
        -->client->buffer[client->head++] = *event;
        -->client->head &= client->bufsize - 1;
      --> if (type == EV_SYN && code == SYN_REPORT) wake_up_interruptible(&evdev->wait);//input_sync(input)唤醒中断

    input_sync()函数调用的也是input_event()函数。

    static inline void input_sync(struct input_dev *dev)
    {
        input_event(dev, EV_SYN, SYN_REPORT, 0);
    }

    6. 写入设备流程分析

    static ssize_t evdev_write(struct file *file, const char __user *buffer,size_t count, loff_t *ppos)
      -->input_event_from_user(buffer + retval, &event);//读取用户空间数据到input_event结构体
      -->input_inject_event(&evdev->handle,event.type, event.code, event.value);
        -->input_handle_event(dev, type, code, value);//将input_event数据填充到evdev_client的环形缓冲区

    至此对于输入子系统的分析告一段落,以后有新的体会在补充。

  • 相关阅读:
    test
    封装和构造方法
    面向对象
    数组的排序
    UDP编程(八)
    多的是面向对象你不知道的事
    面向对象组合的使用
    类成员的进一步阐述
    面向对象初始
    吾日三省吾身
  • 原文地址:https://www.cnblogs.com/yangjiguang/p/6046191.html
Copyright © 2011-2022 走看看