輸入子系統(三)事件上報分析

不少分析輸入子系統的文章已經講得很清楚了,這裏主要是記錄本身的學習過程。參考的幾篇文章:html

輸入子系統學習筆記之按鍵實例編程node

輸入子系統學習筆記之源碼分析1_框架性分析linux

輸入子系統學習筆記之源碼分析2_數據結構分析編程

輸入子系統學習筆記之源碼分析3_流程分析數組

這裏已evdev.c爲例分析分析輸入子系統時間上報的流程。數據結構

evdev設備次設備號從64開始,最多支持32個evdev設備。框架

#define EVDEV_MINOR_BASE	64
#define EVDEV_MINORS		32

1. 重要的結構體async

 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)中使用。ide

每次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\n");
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\r\n");
 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\r\n");
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\r\n");
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\n");
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的環形緩衝區

 

至此對於輸入子系統的分析告一段落,之後有新的體會在補充。

相關文章
相關標籤/搜索