輸入子系統(tǒng)概述
?????? Linux內(nèi)核為了能夠處理各種不同類型的輸入設(shè)備荔棉,比如 觸摸屏 闹炉,鼠標 , 鍵盤 , 操縱桿 ,設(shè)計并實現(xiàn)了為驅(qū)動層程序的實現(xiàn)提供統(tǒng)一接口函數(shù)江耀;為上層應(yīng)用提供試圖統(tǒng)一的抽象層 , 即是Linux 輸入子系統(tǒng) 剩胁。
?????? 從上圖輸入子系統(tǒng)的框架圖,可以看出祥国,輸入子系統(tǒng)由Input driver(驅(qū)動層)昵观、Input core(輸入子系統(tǒng)核心)、Event handler(事件處理層)三部分組成舌稀。一個輸入事件啊犬,如鼠標移動、鍵盤按下等通過Input driver -> Input core -> Event handler -> userspace的順序到達用戶空間的應(yīng)用程序壁查。
-
Input driver :主要實現(xiàn)對硬件設(shè)備的讀寫訪問觉至,中斷設(shè)置,并把硬件產(chǎn)生的事件轉(zhuǎn)換為核心層定義的規(guī)范提交給事件處理層睡腿。
-
Input core :承上啟下语御。為設(shè)備驅(qū)動層提供了規(guī)范和接口;通知事件處理層對事件進行處理席怪;
-
Event handler :提供用戶編程的接口(設(shè)備節(jié)點)应闯,并處理驅(qū)動層提交的數(shù)據(jù)處理。
輸入子系統(tǒng)框架分析
?????? 輸入子系統(tǒng)是所有I/O設(shè)備驅(qū)動的中間層挂捻,為上層提供了一個統(tǒng)一的界面碉纺。例如,在終端系統(tǒng)中,我們不需要去管有多少個鍵盤骨田,多少個鼠標耿导。它只要從輸入子系統(tǒng)中去取對應(yīng)的事件(按鍵,鼠標移位等)就可以了态贤。
?????? 上面舱呻,我們從功能級別,描述了輸入子系統(tǒng)每一層抵卫,做了些什么狮荔。接下來,我們將從代碼級別的角度出發(fā)介粘,分析系統(tǒng)核心層、事件處理層晚树、設(shè)備驅(qū)動層姻采。
1.系統(tǒng)核心層(Input core)
- 申請主設(shè)備號;
- 提供input_register_device跟input_register_handler函數(shù)分別用于注冊device跟handler;
- 提供input_register_handle函數(shù)用于注冊一個事件處理,代表一個成功配對的input_dev和input_handler;
2.事件處理層(Event handler)
- 不涉及硬件方面的具體操作爵憎,handler層是純軟件層慨亲,包含不同的解決方案,如鍵盤宝鼓,鼠標刑棵,游戲手柄等;
- 對于不同的解決方案愚铡,都包含一個名為input_handler的結(jié)構(gòu)體蛉签,該結(jié)構(gòu)體內(nèi)含的主要成員如下:
成員 | 功能 |
---|---|
.id_table | 一個存放該handler所支持的設(shè)備id的表(其實內(nèi)部存放的是EV_xxx事件,用于判斷device是否支持該事件) |
.fops | 該handler的file_operation |
.connect | 連接該handler跟所支持device的函數(shù) |
.disconnect | 斷開該連接 |
.event | 事件處理函數(shù),讓device調(diào)用 |
h_list | 是一個鏈表沥寥,該鏈表保存著該handler到所支持的所有device的中間站:handle結(jié)構(gòu)體的指針 |
3.設(shè)備驅(qū)動層(Input driver)
- device是純硬件操作層碍舍,包含不同的硬件接口處理,如gpio等
- 對于每種不同的具體硬件操作邑雅,都對應(yīng)著不同的input_dev結(jié)構(gòu)體
- 該結(jié)構(gòu)體內(nèi)部也包含著一個h_list片橡,指向handle
4.兩條鏈表一個結(jié)構(gòu)
- 對于handler和device,分別用鏈表input_handler_list和input_device_list進行維護淮野,
當(dāng)handler或者device增加或減少的時候捧书,分別往這兩鏈表增加或刪除節(jié)點,這兩條都是全局鏈表骤星。 - input_handle 結(jié)構(gòu)體代表一個成功配對的input_dev和input_handler经瓷。input_hande 沒有一個全局的鏈表,它注冊的時候?qū)⒆约悍謩e掛在了input_device_list和 input_handler_list的h_list上了妈踊;同時了嚎,input_handle的成員
.*dev
,關(guān)聯(lián)到input_dev結(jié)構(gòu),.*handler
關(guān)聯(lián)到input_handler結(jié)構(gòu) 歪泳。從此萝勤,建立好了三者的鐵三角關(guān)系,通過任何一方呐伞,都可以找到彼此敌卓。
?????? 總結(jié)一下,輸入子系統(tǒng)作為一個模塊存在伶氢;向上,為用戶層提供調(diào)用接口趟径;向下,為驅(qū)動層程序提供統(tǒng)一的注冊接口。這樣,就能夠使輸入設(shè)備的事件通過輸入子系統(tǒng)發(fā)送給用戶層應(yīng)用程序,用戶層應(yīng)用程序也可以通過輸入子系統(tǒng)通知驅(qū)動程序完成某項功能癣防。(Linux中在用戶空間將所有的設(shè)備都當(dāng)初文件來處理蜗巧,由于在一般的驅(qū)動程序中都有提供fops接口,以及在/dev下生成相應(yīng)的設(shè)備文件nod蕾盯,這些操作在輸入子系統(tǒng)中由事件處理層完成)
輸入子系統(tǒng)分析
Input core 作為輸入子系統(tǒng)的核心幕屹,我們以他為入口,進行分析级遭。內(nèi)核所有的輸入子系統(tǒng)核心代碼在driver/input
下望拖;
vim driver/input/input.c (核心層)
找到入口函數(shù):
subsys_initcall(input_init);
input_init
:分析:
static int __init input_init(void)
{
int err;
err = class_register(&input_class); //在/sys/class下創(chuàng)建邏輯(input)類
if (err) {
pr_err("unable to register input_dev class\n");
return err;
}
err = input_proc_init();//在/proc下面建立相關(guān)的文件
if (err)
goto fail1;
/*申請一個字符設(shè)備,主設(shè)備號13*/
err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),
INPUT_MAX_CHAR_DEVICES, "input");
if (err) {
pr_err("unable to register char major %d", INPUT_MAJOR);
goto fail2;
}
return 0;
fail2: input_proc_exit();
fail1: class_unregister(&input_class);
return err;
}
在入口函數(shù)里面創(chuàng)建了一個input_class類,其實就在/sys/class下創(chuàng)建了一個目錄input挫鸽。另外在/proc創(chuàng)建了入口項,這樣就可以/proc目錄看到input的信息,然后就注冊設(shè)備,可以看出輸入子系統(tǒng)的主設(shè)備號是13,在這里并沒有生成設(shè)備文件.只是在/dev/目錄下創(chuàng)建了input目錄,以后所有注冊進系統(tǒng)的輸入設(shè)備文件都放在這個目錄下说敏。
到這里,輸入子系統(tǒng)的核心初始化也就完成了丢郊,完成了盔沫?你可能會有這樣的疑問:
①為什么這里代碼只創(chuàng)建邏輯(input)類,沒有使用class_device_create()函數(shù)在類下面注冊驅(qū)動設(shè)備蚂夕?
②為什么這里的代碼只是申請了一個主設(shè)備號INPUT_MAJOR的字符設(shè)備迅诬,沒有進行設(shè)備的注冊?
核心層作為一個中轉(zhuǎn)層存在婿牍,不涉及具體硬件設(shè)備的注冊侈贷,倒是更符合他存在的邏輯。那么猜測下等脂,設(shè)備的注冊到底會在Input driver 還是Event hanlder呢俏蛮?往下分析...
輸入核心為驅(qū)動層提供統(tǒng)一的接口,涉及的結(jié)構(gòu)和方法如下:
實現(xiàn)設(shè)備驅(qū)動核心工作是:向系統(tǒng)報告按鍵上遥、觸摸屏等輸入事件(event搏屑,通過input_event結(jié)構(gòu)描述),不再需要關(guān)心文件操作接口粉楚。驅(qū)動報告事件經(jīng)過inputCore和Eventhandler到達用戶空間辣恋。
-
input_dev結(jié)構(gòu)
struct input_dev {
const char *name; //提供給用戶的輸入設(shè)備的名稱
const char *phys; //提供給編程者的設(shè)備節(jié)點的名稱
const char *uniq; //指定唯一的ID號亮垫,就像MAC地址一樣
struct input_id id; //輸入設(shè)備標識ID,用于和事件處理層進行匹配
unsigned long evbit[NBITS(EV_MAX)]; // 記錄設(shè)備支持的事件類型
unsigned long keybit[NBITS(KEY_MAX)]; // 記錄設(shè)備支持的按鍵類型
unsigned long relbit[NBITS(REL_MAX)]; // 表示能產(chǎn)生哪些相對位移事件, x,y,滾輪
unsigned long absbit[NBITS(ABS_MAX)]; // 表示能產(chǎn)生哪些絕對位移事件, x,y
unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
unsigned int hint_events_per_packet;
unsigned int keycodemax;
unsigned int keycodesize;
void *keycode;
...
}
功能 | 接口 |
---|---|
分配輸入設(shè)備函數(shù) | struct input_dev *input_allocate_device(void) |
注冊輸入設(shè)備函數(shù) | int input_register_device(struct input_dev *dev) |
注銷輸入設(shè)備函數(shù) | void input_unregister_device(struct input_dev *dev) |
事件支持(初始化) | set_bit() 告訴input輸入子系統(tǒng)支持哪些事件伟骨,哪些按鍵饮潦,例如: set_bit(EV_KEY,button_dev.evbit) (其中button_dev是struct input_dev類型) struct input_dev中有兩個成員為: evbit: 事件類型(EV_RST,EV_REL,EV_MSC,EV_KEY,EV_ABS,EV_REP等) keybit: 按鍵類型(當(dāng)事件類型為EV_KEY時包括BTN_LEFT,BTN_0,BTN_1,BTN_MIDDLE等) |
報告事件 | void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) 在發(fā)生輸入事件時,向子系統(tǒng)報告事件携狭。 參數(shù)說明: input_dev *dev :要上報哪個input_dev驅(qū)動設(shè)備的事件继蜡; type : 要上報哪類事件, 比如按鍵事件,則填入: EV_KEY; code: 對應(yīng)的事件里支持的哪個變量逛腿,比如按下按鍵L則填入: KEY_L稀并; value:對應(yīng)的變量里的數(shù)值,比如松開按鍵則填入1,松開按鍵則填入0; |
報告結(jié)束 | input_sync() 同步用于告訴input core子系統(tǒng)報告結(jié)束 |
所以单默,對于Input driver的工作主要還是分配碘举、設(shè)置、注冊一個結(jié)構(gòu)體雕凹。input_register_device()
用于注冊一個輸入設(shè)備殴俱。那么注冊過程是怎樣的呢?這是一個重點枚抵,在下面的代碼中進行注釋分析:
int input_register_device(struct input_dev *dev)
{
struct input_devres *devres = NULL;
/* 輸入事件的處理接口指針,用于和設(shè)備的事件類型進行匹配 */
struct input_handler *handler;
unsigned int packet_size;
const char *path;
int error;
if (dev->devres_managed) {
devres = devres_alloc(devm_input_device_unregister,
sizeof(struct input_devres), GFP_KERNEL);
if (!devres)
return -ENOMEM;
devres->input = dev;
}
/* 默認所有的輸入設(shè)備都支持EV_SYN同步事件 */
/* Every input device generates EV_SYN/SYN_REPORT events. */
__set_bit(EV_SYN, dev->evbit);
/* KEY_RESERVED is not supposed to be transmitted to userspace. */
__clear_bit(KEY_RESERVED, dev->keybit);
/* Make sure that bitmasks not mentioned in dev->evbit are clean. */
input_cleanse_bitmasks(dev);
packet_size = input_estimate_events_per_packet(dev);
if (dev->hint_events_per_packet < packet_size)
dev->hint_events_per_packet = packet_size;
dev->max_vals = dev->hint_events_per_packet + 2;
dev->vals = kcalloc(dev->max_vals, sizeof(*dev->vals), GFP_KERNEL);
if (!dev->vals) {
error = -ENOMEM;
goto err_devres_free;
}
/*
* If delay and period are pre-set by the driver, then autorepeating
* is handled by the driver itself and we don't do it in input.c.
*/
if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
dev->timer.data = (long) dev;
dev->timer.function = input_repeat_key;
dev->rep[REP_DELAY] = 250;
dev->rep[REP_PERIOD] = 33;
}
/*沒有定義設(shè)備的getkeycode函數(shù)明场,則使用默認的獲取鍵值函數(shù)*/
if (!dev->getkeycode)
dev->getkeycode = input_default_getkeycode;
/*沒有定義設(shè)備的setkeycode函數(shù)汽摹,則使用默認的設(shè)定鍵值函數(shù)*/
if (!dev->setkeycode)
dev->setkeycode = input_default_setkeycode;
/*添加設(shè)備*/
error = device_add(&dev->dev);
if (error)
goto err_free_vals;
/* 獲取并打印設(shè)備的絕對路徑名稱 */
path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
pr_info("%s as %s\n",
dev->name ? dev->name : "Unspecified device",
path ? path : "N/A");
kfree(path);
error = mutex_lock_interruptible(&input_mutex);
if (error)
goto err_device_del;
/* `重要`:把設(shè)備掛到全局的input子系統(tǒng)設(shè)備鏈表input_dev_list上 */
list_add_tail(&dev->node, &input_dev_list);
/* 核心重點,input設(shè)備在增加到input_dev_list鏈表上之后苦锨,會查找
* input_handler_list事件處理鏈表上的handler進行匹配逼泣,這里的匹配
* 方式與設(shè)備模型的device和driver匹配過程很相似,所有的input devicel
* 都掛在input_dev_list上舟舒,所有類型的事件都掛在input_handler_list
* 上拉庶,進行“匹配相親”*/
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);/*遍歷input_handler_list,試圖與每一個handler進行匹配*/
input_wakeup_procfs_readers();
mutex_unlock(&input_mutex);
if (dev->devres_managed) {
dev_dbg(dev->dev.parent, "%s: registering %s with devres.\n",
__func__, dev_name(&dev->dev));
devres_add(dev->dev.parent, devres);
}
return 0;
err_device_del:
device_del(&dev->dev);
err_free_vals:
kfree(dev->vals);
dev->vals = NULL;
err_devres_free:
devres_free(devres);
return error;
}
上面的代碼主要的功能有以下幾個功能秃励,也是設(shè)備驅(qū)動注冊為輸入設(shè)備委托內(nèi)核做的事情:
- 添加設(shè)備氓润;
- 把輸入設(shè)備掛到輸入設(shè)備鏈表
input_dev_list
中慰于; - 遍歷
input_handler_list
鏈表,查找并匹配輸入設(shè)備對應(yīng)的事件處理層,如果匹配上了觉既,就調(diào)用handler
的connnect
函數(shù)進行連接。設(shè)備就是在此時注冊的阳仔,下面分析handler就清晰了掸冤。
(input_attach_handler放到分析handler時再做講解,更容易理解食呻。)
輸入核心為事件管理層提供主要接口:
事件處理層文件主要是用來支持輸入設(shè)備并與用戶空間交互流炕,這部分代碼一般不需要我們自己去編寫澎现,因為Linux內(nèi)核已經(jīng)自帶有一些事件處理器,可以支持大部分輸入設(shè)備每辟,比如Evdev.c剑辫、mousedev.c、joydev.c等影兽。
功能 | 接口 |
---|---|
注冊一個事件處理器 | input_register_handler |
向內(nèi)核注冊一個handle結(jié)構(gòu) | input_register_handle |
對于Event handler揭斧,就是根據(jù)事件注冊一個handler,將handler掛到鏈表input_handler_list下峻堰,然后遍歷input_dev_list鏈表,查找并匹配輸入設(shè)備對應(yīng)的事件處理層讹开,如果匹配上了,就調(diào)用connect
函數(shù)進行連接捐名,并創(chuàng)建input_handle結(jié)構(gòu)旦万。
下面以Evdev為例,來分析事件處理層镶蹋。
vim drivers/input/evdev.c
同樣找到入口函數(shù):
module_init(evdev_init);
evdev_init
分析:
static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);
}
直接調(diào)用input_register_handler 注冊一個input_handler結(jié)構(gòu)體成艘,這下回到了輸入核心層提供的接口input_register_handler上,接著往下看:
int input_register_handler(struct input_handler *handler)
{
struct input_dev *dev;
int error;
error = mutex_lock_interruptible(&input_mutex);
if (error)
return error;
INIT_LIST_HEAD(&handler->h_list);
/* `重要`:把設(shè)備處理器掛到全局的input子系統(tǒng)設(shè)備鏈表input_handler_list上 */
list_add_tail(&handler->node, &input_handler_list);
/*遍歷input_dev_list贺归,試圖與每一個input_dev進行匹配*/
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
input_wakeup_procfs_readers();
mutex_unlock(&input_mutex);
return 0;
}
這個input_register_handler的注冊過程淆两,你可以能看起來覺得挺熟悉;沒錯拂酣,這個注冊過程和input_register_device極其相似秋冰;下面就重點分析匹配連接過程中,事件處理層到底做了些什么婶熬。
input_attach_handler
匹配過程如下:
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id;
int error;
/* 利用handler->id_table和dev進行匹配*/
id = input_match_device(handler, dev);
if (!id)
return -ENODEV;
/*匹配成功剑勾,則調(diào)用handler->connect函數(shù)進行連接*/
error = handler->connect(handler, dev, id);
if (error && error != -ENODEV)
pr_err("failed to attach handler %s to device %s, error: %d\n",
handler->name, kobject_name(&dev->dev.kobj), error);
return error;
}
對于connect函數(shù),每種事件處理器的實現(xiàn)都有差異赵颅,但原理都相同虽另。他主要注冊input_handle結(jié)構(gòu),然后將input_device和input_handler進行關(guān)聯(lián)饺谬。
以evdev的connect的evdev_connect函數(shù)捂刺,代碼注釋分析下這個過程:
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct evdev *evdev;
int minor;
int dev_no;
int error;
/*申請一個新的次設(shè)備號*/
minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);
/* 這說明內(nèi)核已經(jīng)沒辦法再分配這種類型的設(shè)備了 */
if (minor < 0) {
error = minor;
pr_err("failed to reserve new minor: %d\n", error);
return error;
}
/* 開始給evdev事件層驅(qū)動分配空間了 */
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
if (!evdev) {
error = -ENOMEM;
goto err_free_minor;
}
/* 初始化client_list列表和evdev_wait隊列 */
INIT_LIST_HEAD(&evdev->client_list);
spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex);
init_waitqueue_head(&evdev->wait);
evdev->exist = true;
dev_no = minor;
/* Normalize device number if it falls into legacy range */
if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS)
dev_no -= EVDEV_MINOR_BASE;
/*設(shè)置設(shè)備節(jié)點名稱,/dev/eventX 就是在此時設(shè)置*/
dev_set_name(&evdev->dev, "event%d", dev_no);
/* 初始化evdev結(jié)構(gòu)體商蕴,其中handle為輸入設(shè)備和事件處理的關(guān)聯(lián)接口 */
evdev->handle.dev = input_get_device(dev);
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;
evdev->handle.private = evdev;
/*設(shè)置設(shè)備號叠萍,應(yīng)用層就是通過設(shè)備號,找到該設(shè)備的*/
evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);
evdev->dev.class = &input_class;
evdev->dev.parent = &dev->dev;
evdev->dev.release = evdev_free;
device_initialize(&evdev->dev);
/* input_dev設(shè)備驅(qū)動和handler事件處理層的關(guān)聯(lián)绪商,就在這時由handle完成 */
error = input_register_handle(&evdev->handle);
if (error)
goto err_free_evdev;
cdev_init(&evdev->cdev, &evdev_fops);
evdev->cdev.kobj.parent = &evdev->dev.kobj;
error = cdev_add(&evdev->cdev, evdev->dev.devt, 1);
if (error)
goto err_unregister_handle;
/*將設(shè)備加入到Linux設(shè)備模型苛谷,它的內(nèi)部將找到它的bus,然后讓它的bus
給它找到它的driver格郁,在驅(qū)動或者總線的probe函數(shù)中腹殿,一般會在/dev/目錄
先創(chuàng)建相應(yīng)的設(shè)備節(jié)點独悴,這樣應(yīng)用程序就可以通過該設(shè)備節(jié)點來使用設(shè)備了
,/dev/eventX 設(shè)備節(jié)點就是在此時生成
*/
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);
err_free_minor:
input_free_minor(minor);
return error;
}
到這里锣尉,輸入子系統(tǒng)的分析就結(jié)束了刻炒;如果還不是很了解,那么我們換個角度自沧,應(yīng)用層的調(diào)用如何作用到具體的實際硬件來分析坟奥,你就會清晰了。
從應(yīng)用層的角度出發(fā)看input子系統(tǒng)
首先思考個問題拇厢,在應(yīng)用層調(diào)用read函數(shù)爱谁,是如何讀取到實際硬件的按鍵值的?
由上面分析可知孝偎,當(dāng)調(diào)用open函數(shù)讀取鍵值時访敌,將調(diào)用到evdev_read
:
static ssize_t evdev_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev;
struct input_event event;
size_t read = 0;
int error;
if (count != 0 && count < input_event_size())
return -EINVAL;
for (;;) {
if (!evdev->exist || client->revoked)
return -ENODEV;
/*如果client的環(huán)形緩沖區(qū)中沒有數(shù)據(jù)并且是非阻塞的,那么返回-EAGAIN衣盾,也就是try again*/
if (client->packet_head == client->tail &&
(file->f_flags & O_NONBLOCK))
return -EAGAIN;
/*
* count == 0 is special - no IO is done but we check
* for error conditions (see above).
*/
if (count == 0)
break;
/*調(diào)用evdev_fetch_next_event寺旺,如果獲得了數(shù)據(jù)則取出來*/
while (read + input_event_size() <= count &&
evdev_fetch_next_event(client, &event)) {
/*input_event_to_user調(diào)用copy_to_user傳入用戶程序中,這樣讀取完成*/
if (input_event_to_user(buffer + read, &event))
return -EFAULT;
read += input_event_size();
}
if (read)
break;
/*如果沒有數(shù)據(jù)势决,并且是阻塞的阻塑,則在等待隊列上等待*/
if (!(file->f_flags & O_NONBLOCK)) {
error = wait_event_interruptible(evdev->wait,
client->packet_head != client->tail ||
!evdev->exist || client->revoked);
if (error)
return error;
}
}
return read;
}
如果read函數(shù)進入了休眠狀態(tài),又是誰來喚醒果复?搞明白了這個問題叮姑,也就知道了read的數(shù)據(jù)是從哪來的了。
搜索這個evdev->wait這個等待隊列變量,找到evdev_event函數(shù)里喚醒:
static void evdev_event(struct input_handle *handle,
unsigned int type, unsigned int code, int value)
{
struct input_value vals[] = { { type, code, value } };
evdev_events(handle, vals, 1);
---> evdev_pass_values(client, vals, count, ev_time);
---> wake_up_interruptible(&evdev->wait);
}
其中evdev_event()是evdev.c(事件處理層) 的evdev_handler->event
成員,如下圖所示:
猜測下,是誰調(diào)用evdev_event()這個
evdev_handler->event
事件驅(qū)動函數(shù)据悔,應(yīng)該就是之前分析的input_dev設(shè)備層調(diào)用的。
input.c中試搜下
handler->event
或 handler.event
,回溯下函數(shù)調(diào)用:handler->event(handle, v->type, v->code, v->value)
? ???---> input_to_handler
???????? ---> input_pass_values
??? ??????? ---> input_handle_event
???????????????? ---> input_event
顯然耘沼,就是input_dev通過輸入核心為驅(qū)動層提供統(tǒng)一的接口极颓,
input_event
,來向事件處理層上報數(shù)據(jù)并喚醒群嗤。
參考:
https://www.cnblogs.com/lcw/p/3506110.html
input子系統(tǒng)整體流程全面分析
linux中class_create和class_regster說明
input_proc_init 參考
register_chrdev_region 參考
使用register_chrdev_region()系列來注冊字符設(shè)備
https://www.cnblogs.com/lcw/p/3294356.html
https://www.cnblogs.com/lcw/p/3293302.html
https://www.cnblogs.com/lcw/p/3506110.html
https://blog.csdn.net/qq_695538007/article/details/40456875