输入子系统(三)事件上报分析
很多分析输入子系统的文章已经讲得很清楚了,这里主要是记录自己的学习过程。参考的几篇文章:
这里已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];
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\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 }
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");
加载内核模块后的调用流程如下,生成/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 }
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 }
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 }
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 }
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的环形缓冲区
至此对于输入子系统的分析告一段落,以后有新的体会在补充。