Linux输入子系统框架
輸入子系統(tǒng)
自己寫(xiě)的驅(qū)動(dòng)程序,自己可以調(diào)用,我們自己寫(xiě)驅(qū)動(dòng)的流程一般是,建立fops結(jié)構(gòu),使用register_chrdev在初始化函數(shù)中進(jìn)行注冊(cè),在應(yīng)用中使用open函數(shù)打開(kāi)該設(shè)備。這種驅(qū)動(dòng)不標(biāo)準(zhǔn)只能在公司內(nèi)部,別人知道驅(qū)動(dòng)用法的情況下才能使用,當(dāng)我們使用QT等標(biāo)準(zhǔn)程序時(shí),這類(lèi)標(biāo)準(zhǔn)程序不能打開(kāi)像我們這樣的野驅(qū)動(dòng),我們應(yīng)該讓我們的驅(qū)動(dòng)程序融入“標(biāo)準(zhǔn)”中去,linux提供的輸入子系統(tǒng)
輸入子系統(tǒng)分析
在input.c \drivers\input.c核心層可以分析輸入子系統(tǒng)源碼,首先看他的初始化函數(shù)
static int __init input_init(void) {int err;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; }在初始化函數(shù)中我們可以看出這只是執(zhí)行了一個(gè)普通的字符設(shè)備注冊(cè)過(guò)程,創(chuàng)建了一個(gè)input類(lèi),在該類(lèi)下并沒(méi)有創(chuàng)建具體設(shè)備,其余沒(méi)有什么特別。在字符設(shè)備注冊(cè)時(shí)的操作函數(shù)集合中只有一個(gè)open函數(shù),直觀(guān)上看一個(gè)open函數(shù)并不能執(zhí)行read等操作。那我們分析一下這個(gè)open函數(shù)究竟做了些什么:
static const struct file_operations input_fops = {.owner = THIS_MODULE,.open = input_open_file, };那我們分析一下這個(gè)open函數(shù)做了些什么
static int input_open_file(struct inode *inode, struct file *file) {struct input_handler *handler = input_table[iminor(inode) >> 5];const struct file_operations *old_fops, *new_fops = NULL;int err;/* No load-on-demand here? */if (!handler || !(new_fops = fops_get(handler->fops))) return -ENODEV;/** That's _really_ odd. Usually NULL ->open means "nothing special",* not "no device". Oh, well...*/if (!new_fops->open) {fops_put(new_fops); return -ENODEV;}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); return err; }(1)其中iminor(inode)函數(shù)調(diào)用了MINOR(inode->i_rdev);讀取子設(shè)備號(hào),然后將子設(shè)備除以32,找到新掛載的input驅(qū)動(dòng)的數(shù)組號(hào),然后放在input_handler 驅(qū)動(dòng)處理函數(shù)handler中,輸入子系統(tǒng)支持的設(shè)備大類(lèi)就那么幾項(xiàng),每項(xiàng)支持最多32個(gè)設(shè)備,除32意味著可將在這32區(qū)段的設(shè)備都能準(zhǔn)確定位到自己對(duì)應(yīng)大類(lèi)上,這些設(shè)備都可以使用對(duì)應(yīng)大類(lèi)的公共fops ,(例如:輸入子系統(tǒng)的事件設(shè)備evdev,次設(shè)備號(hào)起始位置為64,之后32個(gè)設(shè)備都屬于evdev設(shè)備,input_table[iminor(inode) >> 5],次設(shè)備號(hào)64~95的設(shè)備都對(duì)應(yīng)input_table[2],這個(gè)數(shù)組位置指向的是evdev的handler,這個(gè)區(qū)段內(nèi)設(shè)備的fops,在這個(gè)函數(shù)中都會(huì)指向evdev設(shè)備共用的fops,這樣以來(lái)不用驅(qū)動(dòng)編寫(xiě)者自己編寫(xiě)fops,直接使用該設(shè)備類(lèi)型下別人寫(xiě)出的fops即可)
(2)若handler有值,說(shuō)明掛載有這個(gè)驅(qū)動(dòng),就將handler結(jié)構(gòu)體里的成員file_operations * fops賦到新的file_operations *new_fops里面
(3)再將新的file_operations *new_fops賦到file-> file_operations *f_op里, 此時(shí)input子系統(tǒng)的file_operations就等于新掛載的input驅(qū)動(dòng)的file_operations結(jié)構(gòu)體,實(shí)現(xiàn)一個(gè)偷天換日的效果.
(4)然后調(diào)用新掛載的input驅(qū)動(dòng)的*old_fops里面的成員.open函數(shù),打開(kāi)驅(qū)動(dòng)open函數(shù)
這里需要說(shuō)明:除以32的意義,linux輸入子系統(tǒng),作為將輸入設(shè)備標(biāo)準(zhǔn)化處理的一種方式,使別人使用這類(lèi)驅(qū)動(dòng)時(shí)不必關(guān)心驅(qū)動(dòng)細(xì)節(jié)即可使用,輸入設(shè)備分為好多類(lèi)型,鍵盤(pán)類(lèi)、鼠標(biāo)類(lèi)、觸摸屏類(lèi)等等,linux將輸入子系統(tǒng)設(shè)備主設(shè)備定為13,次設(shè)備號(hào)以32為間隔細(xì)分了幾大類(lèi),例如事件設(shè)備evdev,屬于次設(shè)備號(hào)為64起始向后32個(gè)成員都屬于事件設(shè)備分段,64-95這些設(shè)備都屬于事件設(shè)備,這些設(shè)備次設(shè)備號(hào)除以32結(jié)果都是2,在調(diào)用驅(qū)動(dòng)操作時(shí)他們都可以使用事件設(shè)備提供的fops,從而不用驅(qū)動(dòng)編寫(xiě)者自己編寫(xiě)fops,大大提高了驅(qū)動(dòng)易用性
分析:
struct input_handler *handler = input_table[iminor(inode) >> 5];將傳入的節(jié)點(diǎn)的次設(shè)備號(hào)除32放入input_table數(shù)組中,并將其賦值給handle,創(chuàng)建文件操作結(jié)構(gòu)體new_fops,并將其用剛剛傳入節(jié)點(diǎn)的fops初始化,實(shí)現(xiàn)復(fù)制,這里input_table首次出現(xiàn),那么它由誰(shuí)構(gòu)造,怎么初始化的呢:全局搜索一下
全局搜索input_register_handler被誰(shuí)調(diào)用:搜索結(jié)果如下:
---- input_register_handler Matches (10 in 9 files) ---- evbug_init in evbug.c (F:\SourceInsightProj\JZ2440_2.6\linux-2.6.22.6\drivers\input) : return input_register_handler(&evbug_handler); evdev_init in evdev.c (F:\SourceInsightProj\JZ2440_2.6\linux-2.6.22.6\drivers\input) : return input_register_handler(&evdev_handler); input.c (F:\SourceInsightProj\JZ2440_2.6\linux-2.6.22.6\drivers\input) line 1182 : int input_register_handler(struct input_handler *handler) input.c (F:\SourceInsightProj\JZ2440_2.6\linux-2.6.22.6\drivers\input) line 1203 : EXPORT_SYMBOL(input_register_handler); input.h (F:\SourceInsightProj\JZ2440_2.6\linux-2.6.22.6\include\linux) line 1130 : int input_register_handler(struct input_handler *); joydev_init in joydev.c (F:\SourceInsightProj\JZ2440_2.6\linux-2.6.22.6\drivers\input) : return input_register_handler(&joydev_handler); kbd_init in keyboard.c (F:\SourceInsightProj\JZ2440_2.6\linux-2.6.22.6\drivers\char) : error = input_register_handler(&kbd_handler); mousedev_init in mousedev.c (F:\SourceInsightProj\JZ2440_2.6\linux-2.6.22.6\drivers\input) : error = input_register_handler(&mousedev_handler); rfkill_handler_init in rfkill-input.c (F:\SourceInsightProj\JZ2440_2.6\linux-2.6.22.6\net\rfkill) : return input_register_handler(&rfkill_handler); tsdev_init in tsdev.c (F:\SourceInsightProj\JZ2440_2.6\linux-2.6.22.6\drivers\input) : return input_register_handler(&tsdev_handler);我們可以看到在evdev.c joydev.c mousedev.c等等都通過(guò)input_register_handler向核心層注冊(cè)自己的結(jié)構(gòu),分析其中一個(gè)evdev.c,在其入口函數(shù)中:
static int __init evdev_init(void) {return input_register_handler(&evdev_handler); }看看evdev_handler這個(gè)結(jié)構(gòu)體是怎樣定義的:
static struct input_handler evdev_handler = {.event = evdev_event,//.connect:連接函數(shù),將設(shè)備input_dev和某個(gè)input_handler建立連接.connect = evdev_connect,.disconnect = evdev_disconnect,//.fops:文件操作結(jié)構(gòu)體,其中evdev_fops函數(shù)就是自己的寫(xiě)的操作函數(shù),然后賦到.fops中.fops = &evdev_fops,//.minor:用來(lái)存放次設(shè)備號(hào)/*其中EVDEV_MINOR_BASE=64, 然后調(diào)用input_register_handler(&evdev_handler)后,由于EVDEV_MINOR_BASE/32=2,所以存到input_table[2]中,所以當(dāng)open打開(kāi)這個(gè)input設(shè)備,就會(huì)進(jìn)入 input_open_file()函數(shù),執(zhí)行evdev_handler-> evdev_fops -> .open函數(shù)*/.minor = EVDEV_MINOR_BASE,.name = "evdev",/*.id_table : 表示能支持哪些輸入設(shè)備,比如某個(gè)驅(qū)動(dòng)設(shè)備的input_dev->的id和某個(gè)input_handler的id_table相匹配,就會(huì)調(diào)用.connect連接函數(shù)*/.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, #ifdef CONFIG_COMPAT.compat_ioctl = evdev_ioctl_compat, #endif.fasync = evdev_fasync,.flush = evdev_flush,.llseek = no_llseek, };input_register_device()函數(shù),如何創(chuàng)建驅(qū)動(dòng)設(shè)備的
int input_register_device(struct input_dev *dev) //*dev:要注冊(cè)的驅(qū)動(dòng)設(shè)備 {... ...list_add_tail(&dev->node, &input_dev_list); //(1)放入鏈表中... ...list_for_each_entry(handler, &input_handler_list, node) //(2)input_attach_handler(dev, handler); ... ... }(1)將要注冊(cè)的input_dev驅(qū)動(dòng)設(shè)備放在input_dev_list鏈表中
(2)其中input_handler_list在前面講過(guò),就是存放每個(gè)input_handle驅(qū)動(dòng)處理結(jié)構(gòu)體,然后list_for_each_entry()函數(shù)會(huì)將每個(gè)input_handle從鏈表中取出,放到handler中,最后會(huì)調(diào)用input_attach_handler()函數(shù),將每個(gè)input_handle的id_table進(jìn)行判斷,若兩者支持便進(jìn)行連接。
然后我們?cè)诨剡^(guò)頭來(lái)看注冊(cè)input_handler的input_register_handler()函數(shù)
int input_register_handler(struct input_handler *handler) {struct input_dev *dev;int retval;retval = mutex_lock_interruptible(&input_mutex);if (retval)return retval;INIT_LIST_HEAD(&handler->h_list);if (handler->fops != NULL) {if (input_table[handler->minor >> 5]) {retval = -EBUSY;goto out;}input_table[handler->minor >> 5] = handler;}list_add_tail(&handler->node, &input_handler_list);list_for_each_entry(dev, &input_dev_list, node)//在設(shè)備列表中找出設(shè)備結(jié)構(gòu)體input_attach_handler(dev, handler);//設(shè)備結(jié)構(gòu)體中的id與handler中的id進(jìn)行匹配input_wakeup_procfs_readers();out:mutex_unlock(&input_mutex);return retval; }所以,不管新添加input_dev還是input_handler,都會(huì)進(jìn)入input_attach_handler()判斷兩者id是否有支持, 若兩者支持便進(jìn)行連接
我們來(lái)看看input_attach_handler()如何實(shí)現(xiàn)匹配兩者id的:
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler) { ... ... id = input_match_device(handler->id_table, dev); //匹配兩者if (!id) //若不匹配,return退出 return -ENODEV; error = handler->connect(handler, dev, id); //調(diào)用input_handler ->connect函數(shù)建立連接 ... ... }若兩者匹配成功,就會(huì)自動(dòng)進(jìn)入input_handler 的connect函數(shù)建立連接
我們還是以evdev.c(事件驅(qū)動(dòng)) 的evdev_handler->connect函數(shù)來(lái)分析是怎樣建立連接的
evdev_handler的.connect函數(shù)是evdev_connect(),代碼如下:
static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id) { ... ... for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++); //查找驅(qū)動(dòng)設(shè)備的子設(shè)備號(hào)if (minor == EVDEV_MINORS) { // EVDEV_MINORS=32,所以該事件下的驅(qū)動(dòng)設(shè)備最多存32個(gè),printk(KERN_ERR "evdev: no more free evdev devices\n");return -ENFILE; //沒(méi)找到驅(qū)動(dòng)設(shè)備}... ...evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); //分配一個(gè)input_handle全局結(jié)構(gòu)體(沒(méi)有r)... ...evdev->handle.dev = dev; //指向參數(shù)input_dev驅(qū)動(dòng)設(shè)備 evdev->handle.name = evdev->name; evdev->handle.handler = handler; //指向參數(shù) input_handler驅(qū)動(dòng)處理結(jié)構(gòu)體 evdev->handle.private = evdev; sprintf(evdev->name, "event%d", minor); //(1)保存驅(qū)動(dòng)設(shè)備名字, event%d ... ... devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor), //(2) 將主設(shè)備號(hào)和次設(shè)備號(hào)轉(zhuǎn)換成dev_t類(lèi)型 cdev = class_device_create(&input_class, &dev->cdev, devt,dev->cdev.dev, evdev->name); // (3)在input類(lèi)下創(chuàng)建驅(qū)動(dòng)設(shè)備... ... error = input_register_handle(&evdev->handle); //(4)注冊(cè)這個(gè)input_handle結(jié)構(gòu)體... ... }(1) 是在保存驅(qū)動(dòng)設(shè)備名字,名為event%d, 因?yàn)闆](méi)有設(shè)置子設(shè)備號(hào),默認(rèn)從小到大排列,其中event0是表示這個(gè)input子系統(tǒng),所以這個(gè)鍵盤(pán)驅(qū)動(dòng)名字就是event1
(2)是在保存驅(qū)動(dòng)設(shè)備的主次設(shè)備號(hào),其中主設(shè)備號(hào)INPUT_MAJOR=13,因?yàn)镋VDEV_MINOR_BASE=64,所以此設(shè)備號(hào)=64+驅(qū)動(dòng)程序本事子設(shè)備號(hào)
(3)在之前在2小結(jié)里就分析了input_class類(lèi)結(jié)構(gòu),會(huì)在/sys/class/input類(lèi)下創(chuàng)建驅(qū)動(dòng)設(shè)備event%d
(4)最終會(huì)進(jìn)入input_register_handle()函數(shù)來(lái)注冊(cè),代碼在下面
(1) 因?yàn)閔andle->dev指向input_dev驅(qū)動(dòng)設(shè)備,所以就是將handle->d_node放入到input_dev驅(qū)動(dòng)設(shè)備的h_list鏈表中,即input_dev驅(qū)動(dòng)設(shè)備的h_list鏈表就指向handle->d_node
(2) 同樣, input_handler驅(qū)動(dòng)處理結(jié)構(gòu)體的h_list也指向了handle->h_node,兩者的.h_list都指向了同一個(gè)handle結(jié)構(gòu)體,然后通過(guò).h_list 來(lái)找到handle的成員.dev和handler,便能找到對(duì)方,便建立了連接
建立了連接后,又如何讀取evdev.c(事件驅(qū)動(dòng)) 的evdev_handler->.fops->.read函數(shù)
事件驅(qū)動(dòng)的.read函數(shù)是evdev_read()函數(shù),我們來(lái)分析下
static ssize_t evdev_read(struct file *file, char __user * buffer, size_t count, loff_t *ppos) {... ... /*判斷應(yīng)用層要讀取的數(shù)據(jù)是否正確*/ if (count < evdev_event_size()) return -EINVAL;/*在非阻塞操作情況下,若client->head == client->tail|| evdev->exist時(shí)(沒(méi)有數(shù)據(jù)),則return返回*/if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK)) return -EAGAIN;/*若client->head == client->tail|| evdev->exist時(shí)(沒(méi)有數(shù)據(jù)),等待中斷進(jìn)入睡眠狀態(tài) */retval = wait_event_interruptible(evdev->wait,client->head != client->tail || !evdev->exist);... ... //上傳數(shù)據(jù) }若read函數(shù)進(jìn)入了休眠狀態(tài),又是誰(shuí)來(lái)喚醒
我們搜索這個(gè)evdev->wait這個(gè)等待隊(duì)列變量,找到evdev_event函數(shù)里喚醒:
static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) { ... ...wake_up_interruptible(&evdev->wait); //有事件觸發(fā),便喚醒等待中斷 }其中evdev_event()是evdev.c(事件驅(qū)動(dòng))的evdev_handler->.event成員,當(dāng)有事件發(fā)生了,比如對(duì)于按鍵驅(qū)動(dòng),當(dāng)有按鍵按下時(shí),就會(huì)進(jìn)入.event函數(shù)中處理事件
分析下,是誰(shuí)調(diào)用evdev_event()這個(gè).event事件驅(qū)動(dòng)函數(shù)
應(yīng)該就是之前分析的input_dev那層調(diào)用的,我們來(lái)看看內(nèi)核 gpio_keys_isr()函數(shù)代碼例子就知道了(driver/input/keyboard/gpio_key.c)
static irqreturn_t gpio_keys_isr(int irq, void *dev_id) {/*獲取按鍵值,賦到state里*/... ... /*上報(bào)事件*/ input_event(input, type, button->code, !!state); input_sync(input); //同步信號(hào)通知,表示事件發(fā)送完畢 }顯然就是通過(guò)input_event()來(lái)調(diào)用.event事件函數(shù),我們來(lái)看看:
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { struct input_handle *handle; ... .../* 通過(guò)input_dev ->h_list鏈表找到input_handle驅(qū)動(dòng)處理結(jié)構(gòu)體*/ list_for_each_entry(handle, &dev->h_list, d_node) if (handle->open) //如果input_handle之前open 過(guò),那么這個(gè)就是我們的驅(qū)動(dòng)處理結(jié)構(gòu)體handle->handler->event(handle, type, code, value); //調(diào)用evdev_event()的.event事件函數(shù) }若之前驅(qū)動(dòng)input_dev和處理input_handler已經(jīng)通過(guò)input_handler的.connect函數(shù)建立起了連接,那么就調(diào)用evdev_event()的.event事件函數(shù)
本節(jié)總結(jié)分析:
1.注冊(cè)輸入子系統(tǒng),進(jìn)入put_init():
1)創(chuàng)建主設(shè)備號(hào)為13的”input“字符設(shè)備
err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
2.open打開(kāi)驅(qū)動(dòng),進(jìn)入input_open_file():
1)更新設(shè)備的file_oprations
file->f_op=fops_get(handler->fops);
2)執(zhí)行file_oprations->open函數(shù)
err = new_fops->open(inode, file);
3.注冊(cè)input_handler,進(jìn)入input_register_handler():
1)添加到input_table[]處理數(shù)組中
input_table[handler->minor >> 5] = handler;
2)添加到input_handler_list鏈表中
list_add_tail(&handler->node, &input_handler_list);
3)判斷input_dev的id,是否有支持這個(gè)驅(qū)動(dòng)的設(shè)備 list_for_each_entry(dev, &input_dev_list, node) //遍歷查找input_dev_list鏈表里所有input_dev
input_attach_handler(dev, handler); //判斷兩者id,若兩者支持便進(jìn)行連接。
4.注冊(cè)input_dev,進(jìn)入input_register_device():
1)放在input_dev_list鏈表中
list_add_tail(&dev->node, &input_dev_list);
2)判斷input_handler的id,是否有支持這個(gè)設(shè)備的驅(qū)動(dòng)
list_for_each_entry(handler, &input_handler_list, node) //遍歷查找input_handler_list鏈表里所有input_handler
input_attach_handler(dev, handler); //判斷兩者id,若兩者支持便進(jìn)行連接。
5.判斷input_handler和input_dev的id,進(jìn)入input_attach_handler():
1)匹配兩者id,
input_match_device(handler->id_table, dev); //匹配input_handler和dev的id,不成功退出函數(shù)
2)匹配成功調(diào)用input_handler ->connect
handler->connect(handler, dev, id); //建立連接
6.建立input_handler和input_dev的連接,進(jìn)入input_handler->connect():
1)創(chuàng)建全局結(jié)構(gòu)體,通過(guò)input_handle結(jié)構(gòu)體連接雙方
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); //創(chuàng)建兩者連接的input_handle全局結(jié)構(gòu)體
list_add_tail(&handle->d_node, &handle->dev->h_list); //連接input_dev->h_list
list_add_tail(&handle->h_node, &handler->h_list); // 連接input_handle->h_list
7.有事件發(fā)生時(shí),比如按鍵中斷,在中斷函數(shù)中需要進(jìn)入input_event()上報(bào)事件:
1)找到驅(qū)動(dòng)處理結(jié)構(gòu)體,然后執(zhí)行input_handler->event()
list_for_each_entry(handle, &dev->h_list, d_node) // 通過(guò)input_dev ->h_list鏈表找到input_handle驅(qū)動(dòng)處理結(jié)構(gòu)體
if (handle->open) //如果input_handle之前open 過(guò),那么這個(gè)就是我們的驅(qū)動(dòng)處理結(jié)構(gòu)體(有可能一個(gè)驅(qū)動(dòng)設(shè)備在不同情況下有不同的驅(qū)動(dòng)處理方式)
handle->handler->event(handle, type, code, value); //調(diào)用evdev_event()的.event事件函數(shù)
參考鏈接:http://www.cnblogs.com/lifexy/p/7542989.html
http://www.cnblogs.com/lifexy/p/7553861.html
總結(jié)
以上是生活随笔為你收集整理的Linux输入子系统框架的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: RK3399 Debian 制作xxx.
- 下一篇: 官宣!CSDN“2019 优秀 AI、I