不少分析輸入子系統的文章已經講得很清楚了,這裏主要是記錄本身的學習過程。參考的幾篇文章:html
輸入子系統學習筆記之按鍵實例編程node
這裏已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];
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 }
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的環形緩衝區
至此對於輸入子系統的分析告一段落,之後有新的體會在補充。