input子系统
一、input子系統(tǒng)分析
1.輸入子系統(tǒng)簡介
同樣的輸入子系統(tǒng)也需要輸入驅(qū)動的框架,好來辨認應(yīng)用程序要打開的是哪個輸入驅(qū)動
比如: 鼠標、鍵盤、游戲手柄等等這些都屬于輸入設(shè)備;這些輸入設(shè)備的驅(qū)動都是通過輸入子系統(tǒng)來實現(xiàn)的(當然,這些設(shè)備也依賴于usb子系統(tǒng))
這些輸入設(shè)備都各有不同,那么輸入子系統(tǒng)也就只能實現(xiàn)他們的共性,差異性則由設(shè)備驅(qū)動來實現(xiàn)。差異性又體現(xiàn)在哪里?
最直觀的就表現(xiàn)在這些設(shè)備功能上的不同了。對于我們寫驅(qū)動的人來說在設(shè)備驅(qū)動中就只要使用輸入子系統(tǒng)提供的工具(也就是函數(shù))來完成這些“差異”就行了,其他的則是輸入子系統(tǒng)的工作。這個思想不僅存在于輸入子系統(tǒng),其他子系統(tǒng)也是一樣(比如:usb子系統(tǒng)、video子系統(tǒng)等)
所以我們先來分析下輸入子系統(tǒng)input.c的代碼,然后怎么來使用輸入子系統(tǒng)(在內(nèi)核中以input來形容輸入子系統(tǒng))
?
2.打開input.c,位于內(nèi)核deivers/input
有以下這么兩段:
subsys_initcall(input_init);
module_exit(input_exit);
顯然輸入子系統(tǒng)是作為一個模塊存在,我們先來分析下input_int()入口函數(shù)
static int __init input_init(void) {int err;input_init_abs_bypass();err = class_register(&input_class);if (err) {printk(KERN_ERR "input: unable to register input_dev class\n");return err;}err = input_proc_init();if (err)goto fail1;err = register_chrdev(INPUT_MAJOR, "input", &input_fops);if (err) {printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);goto fail2;}return 0;fail2: input_proc_exit();fail1: class_unregister(&input_class);return err; }(1)class_register(&input_class),在/sys/class 里創(chuàng)建一個 input類,這里代碼只創(chuàng)建類,沒有使用device_create()創(chuàng)建驅(qū)動設(shè)備
(2)register_chrdev創(chuàng)建驅(qū)動設(shè)備,其中變量INPUT_MAJOR =13,所以創(chuàng)建了一個主設(shè)備為13的"input"設(shè)備
然后我們來看看它的操作結(jié)構(gòu)體input_fops
static const struct file_operations input_fops = {.owner = THIS_MODULE,.open = input_open_file, };只有一個.open函數(shù),比如當我們掛載一個新的input驅(qū)動,則內(nèi)核便會調(diào)用該.open函數(shù),接下來分析該.open函數(shù)
?
3.然后進入input_open_file函數(shù)(drivers/input/input.c)
static int input_open_file(struct inode *inode, struct file *file) {struct input_handler *handler;const struct file_operations *old_fops, *new_fops = NULL;int err;err = mutex_lock_interruptible(&input_mutex);if (err)return err;/* No load-on-demand here? */handler = input_table[iminor(inode) >> 5];if (handler)new_fops = fops_get(handler->fops);mutex_unlock(&input_mutex);/** That's _really_ odd. Usually NULL ->open means "nothing special",* not "no device". Oh, well...*/if (!new_fops || !new_fops->open) {fops_put(new_fops);err = -ENODEV;goto out;}old_fops = file->f_op;file->f_op = new_fops;err = new_fops->open(inode, file);if (err) {fops_put(file->f_op);file->f_op = fops_get(old_fops);}fops_put(old_fops); out:return err; }(1)其中iminor (inode)函數(shù)調(diào)用了MINOR(inode->i_rdev);讀取子設(shè)備號,然后將子設(shè)備除以32,找到新掛載的input驅(qū)動的數(shù)組號,然后放在input_handler 驅(qū)動處理函數(shù)handler中?
(2)若handler有值,說明掛載有這個驅(qū)動,就將handler結(jié)構(gòu)體里的成員file_operations * fops賦到新的file_operations *old_fops里面
(3)再將新的file_operations *old_fops賦到file-> file_operations ?*f_op里, 此時input子系統(tǒng)的file_operations就等于新掛載的input驅(qū)動的file_operations結(jié)構(gòu)體,實現(xiàn)一個偷天換日的效果.
(4)然后調(diào)用新掛載的input驅(qū)動的*old_fops里面的成員.open函數(shù)
?
4.上面代碼的input_table[]數(shù)組在初始時是沒有值的,在input.c函數(shù)(drivers/input/input.c)中搜索input_table
找到它在input_register_handler()函數(shù)中被賦值,代碼如下:
int input_register_handler(struct input_handler *handler) {...input_table[handler->minor >> 5] = handler;...list_add_tail(&handler->node, &input_handler_list); }就是將驅(qū)動處理程序input_handler注冊到input_table[]中,然后放在input_handler_list鏈表中,后面會講這個鏈表
?
5.繼續(xù)搜索input_register_handler
發(fā)現(xiàn)這個函數(shù)被evdev.c(事件設(shè)備),joydev.c(joystick操作桿設(shè)備),keyboard.c(鍵盤設(shè)備),mousedev.c(鼠標設(shè)備)?等文件調(diào)用
我們以evdev.c為例
static int __init evdev_init(void) {return input_register_handler(&evdev_handler); }?
6.我們來看看這個evdev_handler變量是什么結(jié)構(gòu)體:
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, };就是我們之前看的input_handler驅(qū)動處理結(jié)構(gòu)體
(1)fops:文件操作結(jié)構(gòu)體,evdev_fops就是自己的寫的操作函數(shù),然后賦到.fops中
(2)minor:用來存放次設(shè)備號,其中EVDEV_MINOR_BASE=64, 然后調(diào)用input_register_handler(&evdev_handler)后,由于EVDEV_MINOR_BASE/32=2,所以存到input_table[2]中
?所以當open打開這個input設(shè)備,就會進入 input_open_file()函數(shù),執(zhí)行evdev_handler-> evdev_fops -> open函數(shù)
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, #ifdef CONFIG_COMPAT.compat_ioctl = evdev_ioctl_compat, #endif.fasync = evdev_fasync,.flush = evdev_flush };(3)id_table :?表示能支持哪些輸入設(shè)備,比如某個驅(qū)動設(shè)備的input_dev->id和某個input_handler->id_table相匹配,就會調(diào)用connect連接函數(shù)
(4)connect:連接函數(shù),將設(shè)備input_dev和某個input_handler建立連接,如下圖
?
?
7.我們先來看看上圖的input_register_device()函數(shù),如何創(chuàng)建驅(qū)動設(shè)備的
int input_register_device(struct input_dev *dev) {......list_add_tail(&dev->node, &input_dev_list);list_for_each_entry(handler, &input_handler_list, node)input_attach_handler(dev, handler);...... }(1)將要注冊的input_dev驅(qū)動設(shè)備放在input_dev_list鏈表中
(2)調(diào)用input_attach_handler()函數(shù),將每個input_handle的id_table進行判斷,若兩者支持便進行連接。
然后我們在回過頭來看注冊input_handler的input_register_handler()函數(shù),如下圖所示
int input_register_handler(struct input_handler *handler) {......list_add_tail(&handler->node, &input_handler_list);list_for_each_entry(dev, &input_dev_list, node)input_attach_handler(dev, handler);...... }所以,不管新添加input_dev還是input_handler,都會進入input_attach_handler()判斷兩者id是否有支持, 若兩者支持便進行連接。
我們來看看input_attach_handler()如何實現(xiàn)匹配兩者id的:
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler) {const struct input_device_id *id;int error;id = input_match_device(handler, dev);if (!id)return -ENODEV;error = handler->connect(handler, dev, id);if (error && error != -ENODEV)printk(KERN_ERR"input: failed to attach handler %s to device %s, ""error: %d\n",handler->name, kobject_name(&dev->dev.kobj), error);return error; }若兩者匹配成功,就會自動進入input_handler的connect函數(shù)建立連接
我們來看看input_match_device()是如何匹配的
static const struct input_device_id *input_match_device(const struct input_device_id *id, struct input_dev *dev) { int i; /*遍歷handler的id_table與device進行匹配*/ for (; id->flags || id->driver_info; id++) { /*根據(jù)flags的標志位,按需要匹配相應(yīng)的字段*/ if (id->flags & INPUT_DEVICE_ID_MATCH_BUS) if (id->bustype != dev->id.bustype)//總線類型不匹配 continue; if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)//生產(chǎn)廠商不匹配 if (id->vendor != dev->id.vendor) continue; if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)//產(chǎn)品不匹配 if (id->product != dev->id.product) continue; if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)//版本不匹配 if (id->version != dev->id.version) continue; MATCH_BIT(evbit, EV_MAX);//匹配所有的事件類型 /*匹配所有事件的子事件*/ MATCH_BIT(keybit, KEY_MAX); MATCH_BIT(relbit, REL_MAX); MATCH_BIT(absbit, ABS_MAX); MATCH_BIT(mscbit, MSC_MAX); MATCH_BIT(ledbit, LED_MAX); MATCH_BIT(sndbit, SND_MAX); MATCH_BIT(ffbit, FF_MAX); MATCH_BIT(swbit, SW_MAX); return id;//匹配成功,返回id } return NULL; }如果相應(yīng)的標志位或事件被設(shè)置了,則需要進行匹配。如果沒設(shè)置則不用匹配
以evdev為例,什么標志位和事件都沒有設(shè)置,也就意味著所有設(shè)備都能匹配上
static const struct input_device_id evdev_ids[] = {{ .driver_info = 1 }, /* Matches all devices */{ }, /* Terminating zero entry */ };?
8.我們還是以evdev.c(事件驅(qū)動) 的evdev_handler->connect函數(shù)來分析是怎樣建立連接的
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, };evdev_handler的.connect函數(shù)是evdev_connect(),代碼如下:
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,const struct input_device_id *id) {struct evdev *evdev;int minor;int error;for (minor = 0; minor < EVDEV_MINORS; minor++)if (!evdev_table[minor])break;if (minor == EVDEV_MINORS) {printk(KERN_ERR "evdev: no more free evdev devices\n");return -ENFILE;}evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);if (!evdev)return -ENOMEM;INIT_LIST_HEAD(&evdev->client_list);spin_lock_init(&evdev->client_lock);mutex_init(&evdev->mutex);init_waitqueue_head(&evdev->wait);dev_set_name(&evdev->dev, "event%d", minor);evdev->exist = 1;evdev->minor = minor;evdev->handle.dev = input_get_device(dev);evdev->handle.name = dev_name(&evdev->dev);evdev->handle.handler = handler;evdev->handle.private = evdev;evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);evdev->dev.class = &input_class;evdev->dev.parent = &dev->dev;evdev->dev.release = evdev_free;device_initialize(&evdev->dev);error = input_register_handle(&evdev->handle);if (error)goto err_free_evdev;error = evdev_install_chrdev(evdev);if (error)goto err_unregister_handle;error = device_add(&evdev->dev);if (error)goto err_cleanup_evdev;return 0;err_cleanup_evdev:evdev_cleanup(evdev);err_unregister_handle:input_unregister_handle(&evdev->handle);err_free_evdev:put_device(&evdev->dev);return error; }(1) 保存驅(qū)動設(shè)備名字,名為event%d, 比如(鍵盤驅(qū)動)event2
(2)保存驅(qū)動設(shè)備的主次設(shè)備號,其中主設(shè)備號INPUT_MAJOR=13,因為EVDEV_MINOR_BASE=64,所以此設(shè)備號=64+驅(qū)動程序本事子設(shè)備號, 比如(鍵盤驅(qū)動)event2: ?主次設(shè)備號就是13,66
(3)最終會進入input_register_handle()函數(shù)來注冊
int input_register_handle(struct input_handle *handle) {......if (handler->filter)list_add_rcu(&handle->d_node, &dev->h_list);elselist_add_tail_rcu(&handle->d_node, &dev->h_list);......list_add_tail_rcu(&handle->h_node, &handler->h_list);...... }(1)將handle->d_node放入到input_dev驅(qū)動設(shè)備的h_list鏈表中,即input_dev驅(qū)動設(shè)備的h_list鏈表就指向handle->d_node
(2) input_handler驅(qū)動處理結(jié)構(gòu)體的h_list也指向了handle->h_node
最終如下圖所示:
?
兩者的.h_list都指向了同一個handle結(jié)構(gòu)體,然后通過.h_list 來找到handle的成員.dev和handler,便能找到對方,便建立了連接
?
9.建立了連接后,又如何讀取evdev.c(事件驅(qū)動) 的evdev_handler->.fops->.read函數(shù)?
事件驅(qū)動的.read函數(shù)是evdev_read()函數(shù),我們來分析下:
static ssize_t evdev_read(struct file *file, char __user *buffer,size_t count, loff_t *ppos) {......if (count < input_event_size())return -EINVAL;if (client->head == client->tail && evdev->exist &&(file->f_flags & O_NONBLOCK))return -EAGAIN;retval = wait_event_interruptible(evdev->wait,client->head != client->tail || !evdev->exist);...... }如果是阻塞型并且沒有事件發(fā)生就等待
?
10.read函數(shù)進入休眠狀態(tài),事件發(fā)生后通過evdev_event函數(shù)喚醒
static void evdev_event(struct input_handle *handle,unsigned int type, unsigned int code, int value) {......wake_up_interruptible(&evdev->wait); }?
?
二、接口說明
核心層提供給設(shè)備驅(qū)動層的接口函數(shù)
input設(shè)備驅(qū)動框架留給設(shè)備驅(qū)動層的接口函數(shù)主要有3個:
? ? ? input_allocate_device。分配一塊input_dev結(jié)構(gòu)體類型大小的內(nèi)存
? ? ? input_set_capability。設(shè)置輸入設(shè)備可以上報哪些輸入事件
? ? ? input_register_device。向input核心層注冊設(shè)備
(1)input_allocate_device函數(shù)
1 struct input_dev *input_allocate_device(void)2 {3 struct input_dev *dev; // 定義一個 input_dev 指針4 5 dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL); // 申請分配內(nèi)存6 if (dev) {7 dev->dev.type = &input_dev_type; // 確定input設(shè)備的 設(shè)備類型 input_dev_type8 dev->dev.class = &input_class; // 確定input設(shè)備所屬的設(shè)備類 class9 device_initialize(&dev->dev); // input設(shè)備的初始化 10 mutex_init(&dev->mutex); // 互斥鎖初始化 11 spin_lock_init(&dev->event_lock); // 自旋鎖初始化 12 INIT_LIST_HEAD(&dev->h_list); // input_dev -> h_list 鏈表初始化 13 INIT_LIST_HEAD(&dev->node); // input_dev -> node 鏈表初始化 14 15 __module_get(THIS_MODULE); 16 } 17 18 return dev; 19 }?
(2)input_set_capability函數(shù):
函數(shù)原型:input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
參數(shù):dev就是設(shè)備的input_dev結(jié)構(gòu)體變量
? ? ? ? type表示設(shè)備可以上報的事件類型
? ? ? ? code表示上報這類事件中的那個事件
注意:input_set_capability函數(shù)一次只能設(shè)置一個具體事件,如果設(shè)備可以上報多個事件,則需要重復(fù)調(diào)用這個函數(shù)來進行設(shè)置,例如:
input_set_capability(dev, EV_KEY,?KEY_Q); ? ? ? ? // 至于函數(shù)內(nèi)部是怎么設(shè)置的,將會在后面進行分析。
input_set_capability(dev,?EV_KEY,?KEY_W);
input_set_capability(dev,?EV_KEY,?KEY_E);
具體的這些類下面有哪些具體的輸入事件,請看 drivers\input\input.h 這個文件。
?
(3)input_register_device函數(shù):
1 int input_register_device(struct input_dev *dev) // 注冊input輸入設(shè)備2 {3 static atomic_t input_no = ATOMIC_INIT(0);4 struct input_handler *handler; // 定義一個 input_handler 結(jié)構(gòu)體指針5 const char *path;6 int error;7 8 /* Every input device generates EV_SYN/SYN_REPORT events. */9 __set_bit(EV_SYN, dev->evbit); // 每一個input輸入設(shè)備都會發(fā)生這個事件 10 11 /* KEY_RESERVED is not supposed to be transmitted to userspace. */ 12 __clear_bit(KEY_RESERVED, dev->keybit); // 清除KEY_RESERVED 事件對應(yīng)的bit位,也就是不傳輸這種類型的事件 13 14 /* Make sure that bitmasks not mentioned in dev->evbit are clean. */ 15 input_cleanse_bitmasks(dev); // 確保input_dev中的用來記錄事件的變量中沒有提到的位掩碼是干凈的。 16 17 /* 18 * If delay and period are pre-set by the driver, then autorepeating 19 * is handled by the driver itself and we don't do it in input.c. 20 */ 21 init_timer(&dev->timer); 22 if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) { 23 dev->timer.data = (long) dev; 24 dev->timer.function = input_repeat_key; 25 dev->rep[REP_DELAY] = 250; 26 dev->rep[REP_PERIOD] = 33; 27 } 28 29 if (!dev->getkeycode) 30 dev->getkeycode = input_default_getkeycode; 31 32 if (!dev->setkeycode) 33 dev->setkeycode = input_default_setkeycode; 34 35 dev_set_name(&dev->dev, "input%ld", // 設(shè)置input設(shè)備對象的名字 input+數(shù)字 36 (unsigned long) atomic_inc_return(&input_no) - 1); 37 38 error = device_add(&dev->dev); // 添加設(shè)備 例如: /sys/devices/virtual/input/input0 39 if (error) 40 return error; 41 42 path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); // 獲取input設(shè)備對象所在的路徑 /sys/devices/virtual/input/input_xxx 43 printk(KERN_INFO "input: %s as %s\n", 44 dev->name ? dev->name : "Unspecified device", path ? path : "N/A"); 45 kfree(path); 46 47 error = mutex_lock_interruptible(&input_mutex); 48 if (error) { 49 device_del(&dev->dev); 50 return error; 51 } 52 53 list_add_tail(&dev->node, &input_dev_list); // 鏈表掛接: 將 input_dev->node 作為節(jié)點掛接到 input_dev_list 鏈表上 54 55 list_for_each_entry(handler, &input_handler_list, node) // 遍歷input_handler_list 鏈表上的所有handler 56 input_attach_handler(dev, handler); // 將handler與input設(shè)備進行匹配 57 58 input_wakeup_procfs_readers(); // 更新proc 文件系統(tǒng) 59 60 mutex_unlock(&input_mutex); 61 62 return 0; 63 }?
三、驅(qū)動代碼
#include <linux/input.h> #include <linux/module.h> #include <linux/init.h> #include <asm/irq.h> #include <asm/io.h> #include <mach/irqs.h> #include <linux/interrupt.h> #include <linux/gpio.h>#define BUTTON_IRQ IRQ_EINT2static struct input_dev *button_dev;static irqreturn_t button_interrupt(int irq, void *dummy) { int flag;s3c_gpio_cfgpin(S5PV210_GPH0(2), S3C_GPIO_SFN(0x0));flag = gpio_get_value(S5PV210_GPH0(2));s3c_gpio_cfgpin(S5PV210_GPH0(2), S3C_GPIO_SFN(0x0f));input_report_key(button_dev, KEY_LEFT, !flag);input_sync(button_dev);return IRQ_HANDLED; }static int __init button_init(void) { int error;error = gpio_request(S5PV210_GPH0(2), "GPH0_2");if(error){printk("key-s5pv210.c: request gpio GPH0(2) fail");return -EBUSY;}s3c_gpio_cfgpin(S5PV210_GPH0(2), S3C_GPIO_SFN(0x0f));if (request_irq(BUTTON_IRQ, button_interrupt, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "button", NULL)) { printk(KERN_ERR "key-s5pv210.c: Can't allocate irq %d\n", BUTTON_IRQ);error = -EBUSY; goto err_free_gpio; }button_dev = input_allocate_device();if (!button_dev){ printk(KERN_ERR "key-s5pv210.c: Not enough memory\n");error = -ENOMEM;goto err_free_irq; }button_dev->name = "keys";input_set_capability(button_dev, EV_KEY, KEY_LEFT);error = input_register_device(button_dev);if (error) { printk(KERN_ERR "key-s5pv210.c: Failed to register device\n");goto err_free_dev; }return 0;err_free_dev:input_free_device(button_dev);err_free_irq:free_irq(BUTTON_IRQ, NULL); err_free_gpio:gpio_free(S5PV210_GPH0(2));return error; }static void __exit button_exit(void) {input_unregister_device(button_dev);input_free_device(button_dev);free_irq(BUTTON_IRQ, NULL);gpio_free(S5PV210_GPH0(2)); }module_init(button_init); module_exit(button_exit);// MODULE_xxx這種宏作用是用來添加模塊描述信息 MODULE_LICENSE("GPL"); // 描述模塊的許可證 MODULE_AUTHOR("lsm"); // 描述模塊的作者 MODULE_DESCRIPTION("s5pv210 button driver"); // 描述模塊的介紹信息 MODULE_ALIAS("s5pv210_button"); // 描述模塊的別名信息?
四、應(yīng)用程序
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <linux/input.h> #include <string.h>#define X210_KEY "/dev/input/event2"int main(void) {int fd = -1, ret = -1;struct input_event ev;fd = open(X210_KEY, O_RDONLY);if (fd < 0){perror("open");return -1;}while (1){memset(&ev, 0, sizeof(struct input_event));ret = read(fd, &ev, sizeof(struct input_event));if (ret != sizeof(struct input_event)){perror("read");close(fd);return -1;}printf("-------------------------\n");printf("type: %hd\n", ev.type);printf("code: %hd\n", ev.code);printf("value: %d\n", ev.value);printf("\n");}close(fd);return 0; }?
總結(jié)
- 上一篇: BAT在AI领域投资收购大起底:当我们说
- 下一篇: 分析一天1000万北京地铁客流,我们发现