最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

一文帶你搞懂Linux之輸入子系統(tǒng)分析詳解(含源碼)

2022-04-07 16:36 作者:補給站Linux內(nèi)核  | 我要投稿

一.輸入子系統(tǒng)簡介

  • 同樣的輸入子系統(tǒng)也需要輸入驅(qū)動的框架,好來辨認(rèn)應(yīng)用程序要打開的是哪個輸入驅(qū)動

  • 比如: 鼠標(biāo)、鍵盤、游戲手柄等等這些都屬于輸入設(shè)備;這些輸入設(shè)備的驅(qū)動都是通過輸入子系統(tǒng)來實現(xiàn)的(當(dāng)然,這些設(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))

二.打開input.c,位于內(nèi)核deivers/input

  • 有以下這么兩段:

顯然輸入子系統(tǒng)是作為一個模塊存在,我們先來分析下input_int()入口函數(shù)

  1. 上面第4行”err = class_register(&input_class);”是在/sys/class 里創(chuàng)建一個 input類, input_class變量如下圖:


  • 如下圖,我們啟動內(nèi)核,再啟動一個input子系統(tǒng)的驅(qū)動后,也可以看到創(chuàng)建了個"input"類 :


  • 為什么這里代碼只創(chuàng)建類,沒有使用class_device_create()函數(shù)在類下面創(chuàng)建驅(qū)動設(shè)備?

  • 在下面第8小結(jié)會詳細(xì)講到,這里簡單描述:當(dāng)注冊input子系統(tǒng)的驅(qū)動后,才會有驅(qū)動設(shè)備,此時這里的代碼是沒有驅(qū)動的

2. 上面第14行通過register_chrdev創(chuàng)建驅(qū)動設(shè)備,其中變量INPUT_MAJOR =13,所以創(chuàng)建了一個主設(shè)備為13的"input"設(shè)備。

  • 然后我們來看看它的操作結(jié)構(gòu)體input_fops,如下圖:




  • 只有一個.open函數(shù),比如當(dāng)我們掛載一個新的input驅(qū)動,則內(nèi)核便會調(diào)用該.open函數(shù),接下來分析該.open函數(shù)


【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【891587639】整理了一些個人覺得比較好的學(xué)習(xí)書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。?!前100名進(jìn)群領(lǐng)取,額外贈送一份價值699的內(nèi)核資料包(含視頻教程、電子書、實戰(zhàn)項目及代碼)?



?

三. 然后進(jìn)入input_open_file函數(shù)(drivers/input/input.c)

  1. 第3行中,其中iminor (inode)函數(shù)調(diào)用了MINOR(inode->i_rdev);讀取子設(shè)備號,然后將子設(shè)備除以32,找到新掛載的input驅(qū)動的數(shù)組號,然后放在input_handler 驅(qū)動處理函數(shù)handler中

  2. 第7行中,若handler有值,說明掛載有這個驅(qū)動,就將handler結(jié)構(gòu)體里的成員file_operations * fops賦到新的file_operations *new_fops里面

  3. 第16行中, 再將新的file_operations *new_fops賦到file-> file_operations ?*f_op里, 此時input子系統(tǒng)的file_operations就等于新掛載的input驅(qū)動的file_operations結(jié)構(gòu)體,實現(xiàn)一個偷天換日的效果.

  4. 第18行中,然后調(diào)用新掛載的input驅(qū)動的*new_fops里面的成員.open函數(shù)

四.上面代碼的input_table[]數(shù)組在初始時是沒有值的,

  • 所以我們來看看input_table數(shù)組里面的數(shù)據(jù)又是在哪個函數(shù)里被賦值

  • 在input.c函數(shù)(drivers/input/input.c)中搜索input_table,找到它在input_register_handler()函數(shù)中被賦值,代碼如下:

  • 就是將驅(qū)動處理程序input_handler注冊到input_table[]中,然后放在input_handler_list鏈表中,后面會講這個鏈表

五.繼續(xù)來搜索input_register_handler,看看這個函數(shù)被誰來調(diào)用

  • 如下圖所示,有evdev.c(事件設(shè)備),tsdev.c(觸摸屏設(shè)備),joydev.c(joystick操作桿設(shè)備),keyboard.c(鍵盤設(shè)備),mousedev.c(鼠標(biāo)設(shè)備) 這5個內(nèi)核自帶的設(shè)備處理函數(shù)注冊到input子系統(tǒng)中



  • 我們以evdev.c為例,它在evdev_ini()函數(shù)中注冊:

六.我們來看看這個evdev_handler變量是什么結(jié)構(gòu)體,:

  • 就是我們之前看的input_handler驅(qū)動處理結(jié)構(gòu)體

  1. 第5行中.fops:文件操作結(jié)構(gòu)體,其中evdev_fops函數(shù)就是自己的寫的操作函數(shù),然后賦到.fops中

  2. 第6行中 .minor:用來存放次設(shè)備號

  • 其中EVDEV_MINOR_BASE=64, 然后調(diào)用input_register_handler(&evdev_handler)后,由于EVDEV_MINOR_BASE/32=2,所以存到input_table[2]中

  • 所以當(dāng)open打開這個input設(shè)備,就會進(jìn)入 input_open_file()函數(shù),執(zhí)行evdev_handler-> evdev_fops -> .open函數(shù),如下圖所示:




3. 第8行中.id_table : 表示能支持哪些輸入設(shè)備,比如某個驅(qū)動設(shè)備的input_dev->的id和某個input_handler的id_table相匹配,就會調(diào)用.connect連接函數(shù),如下圖

4. 第3行中.connect:連接函數(shù),將設(shè)備input_dev和某個input_handler建立連接,如下圖




七.我們先來看看上圖的input_register_device()函數(shù),如何創(chuàng)建驅(qū)動設(shè)備的

  • 搜索input_register_device,發(fā)現(xiàn)內(nèi)核自己就已經(jīng)注冊了很多驅(qū)動設(shè)備

7.1然后進(jìn)入input_register_device()函數(shù),代碼如下:

  1. 第4行中,將要注冊的input_dev驅(qū)動設(shè)備放在input_dev_list鏈表中

  2. 第6行中,其中input_handler_list在前面講過,就是存放每個input_handle驅(qū)動處理結(jié)構(gòu)體,然后list_for_each_entry()函數(shù)會將每個input_handle從鏈表中取出,放到handler中最后會調(diào)用input_attach_handler()函數(shù),將每個input_handle的id_table進(jìn)行判斷,若兩者支持便進(jìn)行連接。

7.2然后我們在回過頭來看注冊input_handler的input_register_handler()函數(shù),如下圖所示



  • 所以,不管新添加input_dev還是input_handler,都會進(jìn)入input_attach_handler()判斷兩者id是否有支持, 若兩者支持便進(jìn)行連接。

7.3我們來看看input_attach_handler()如何實現(xiàn)匹配兩者id的:

  • 若兩者匹配成功,就會自動進(jìn)入input_handler 的connect函數(shù)建立連接

八.我們還是以evdev.c(事件驅(qū)動) 的evdev_handler->connect函數(shù)

  • 來分析是怎樣建立連接的,如下圖:




8.1 evdev_handler的.connect函數(shù)是evdev_connect(),代碼如下:

  1. 第16行中,是在保存驅(qū)動設(shè)備名字,名為event%d, 比如下圖(鍵盤驅(qū)動)event1: 因為沒有設(shè)置子設(shè)備號,默認(rèn)從小到大排列,其中event0是表示這個input子系統(tǒng),所以這個鍵盤驅(qū)動名字就是event1

  2. 第18行中,是在保存驅(qū)動設(shè)備的主次設(shè)備號,其中主設(shè)備號INPUT_MAJOR=13,因為EVDEV_MINOR_BASE=64,所以此設(shè)備號=64+驅(qū)動程序本事子設(shè)備號, 比如下圖(鍵盤驅(qū)動)event1: ?主次設(shè)備號就是13,65

  3. 在之前在2小結(jié)里就分析了input_class類結(jié)構(gòu),所以第19行中,會在/sys/class/input類下創(chuàng)建驅(qū)動設(shè)備event%d,比如下圖(鍵盤驅(qū)動)event1:




4. 最終會進(jìn)入input_register_handle()函數(shù)來注冊,代碼在下面

8.2 input_register_handle()函數(shù)如下:

  1. 在第5行中, 因為handle->dev指向input_dev驅(qū)動設(shè)備,所以就是將handle->d_node放入到input_dev驅(qū)動設(shè)備的h_list鏈表中,

  • 即input_dev驅(qū)動設(shè)備的h_list鏈表就指向handle->d_node

2. 在第6行中, 同樣, input_handler驅(qū)動處理結(jié)構(gòu)體的h_list也指向了handle->h_node

  • 最終如下圖所示:


  • 兩者的.h_list都指向了同一個handle結(jié)構(gòu)體,然后通過.h_list 來找到handle的成員.dev和handler,便能找到對方,便建立了連接

九.建立了連接后,又如何讀取evdev.c(事件驅(qū)動) 的evdev_handler->.fops->.read函數(shù)?

  • 事件驅(qū)動的.read函數(shù)是evdev_read()函數(shù),我們來分析下:

  • 十.若read函數(shù)進(jìn)入了休眠狀態(tài),又是誰來喚醒?

  • 我們搜索這個evdev->wait這個等待隊列變量,找到evdev_event函數(shù)里喚醒:


  • 其中evdev_event()是evdev.c(事件驅(qū)動) 的evdev_handler->.event成員,如下圖所示:


  • 當(dāng)有事件發(fā)生了,比如對于按鍵驅(qū)動,當(dāng)有按鍵按下時,就會進(jìn)入.event函數(shù)中處理事件

十一.分析下,是誰調(diào)用evdev_event()這個.event事件驅(qū)動函數(shù)

  • 應(yīng)該就是之前分析的input_dev那層調(diào)用的

  • 我們來看看內(nèi)核 gpio_keys_isr()函數(shù)代碼例子就知道了 (driver/input/keyboard/gpio_key.c)

  • 顯然就是通過input_event()來調(diào)用.event事件函數(shù),我們來看看:

  • 若之前驅(qū)動input_dev和處理input_handler已經(jīng)通過input_handler 的.connect函數(shù)建立起了連接,那么就調(diào)用evdev_event()的.event事件函數(shù),如下圖所示:


  • 十二.本節(jié)總結(jié)分析:

  • 1. 注冊輸入子系統(tǒng),進(jìn)入put_init():

  • 創(chuàng)建主設(shè)備號為13的"input"字符設(shè)備

  • 2. open打開驅(qū)動,進(jìn)入input_open_file():

  • 更新設(shè)備的file_oprations

執(zhí)行file_oprations->open函數(shù)


3. 注冊input_handler,進(jìn)入input_register_handler():

  • 添加到input_table[]處理數(shù)組中

  • 添加到input_handler_list鏈表中

  • 判斷input_dev的id,是否有支持這個驅(qū)動的設(shè)備

  • 4. 注冊input_dev,進(jìn)入input_register_device():

  • 放在input_dev_list鏈表中

  • 判斷input_handler的id,是否有支持這個設(shè)備的驅(qū)動

  • 5. 判斷input_handler和input_dev的id,進(jìn)入input_attach_handler():

  • 匹配兩者id,

匹配成功調(diào)用input_handler ->connecthandler->connect(handler, dev, id); ? ? ? ? ? ? ?//建立連接


6. 建立input_handler和input_dev的連接,進(jìn)入input_handler->connect():

  • 創(chuàng)建全局結(jié)構(gòu)體,通過input_handle結(jié)構(gòu)體連接雙方

7. 有事件發(fā)生時,比如按鍵中斷,在中斷函數(shù)中需要進(jìn)入input_event()上報事件:

  • 找到驅(qū)動處理結(jié)構(gòu)體,然后執(zhí)行input_handler->event()



一文帶你搞懂Linux之輸入子系統(tǒng)分析詳解(含源碼)的評論 (共 條)

分享到微博請遵守國家法律
紫云| 息烽县| 司法| 微山县| 舒兰市| 武清区| 平山县| 虎林市| 丁青县| 思茅市| 广元市| 于都县| 保康县| 锦屏县| 灵武市| 万安县| 城口县| 克拉玛依市| 河间市| 长汀县| 渭源县| 天门市| 临夏市| 连江县| 崇左市| 都安| 宜兰市| 蒙自县| 阿瓦提县| 青龙| 修文县| 蚌埠市| 琼中| 巴东县| 昭苏县| 五莲县| 县级市| 蒲江县| 抚远县| 永安市| 广平县|