字元設備驅動之輸入子系統分析(二)

来源:https://www.cnblogs.com/Bright-Ho/archive/2023/07/12/17547939.html
-Advertisement-
Play Games

作者:Bright-Ho 聯繫方式:[email protected] input輸入子系統框架分析(純軟體方面): 上一節,我們簡單的描述的什麼是輸入子系統;什麼是字元設備;以及其作用;重點是我們講到分析輸入子系統必須結合硬體設備來分析;那麼這一節,我們主要講解輸入子系統的軟體框架;接下來,我們就進 ...


作者:Bright-Ho

聯繫方式:[email protected]


input輸入子系統框架分析(純軟體方面):

 

上一節,我們簡單的描述的什麼是輸入子系統什麼是字元設備以及其作用;重點是我們講到分析輸入子系統必須結合硬體設備來分析;那麼這一節,我們主要講解輸入子系統的軟體框架;接下來,我們就進入主題;


那麼在進入主題之前,我們先來瞭解一個簡單的字元設備驅動程式的框架;

1構造一個file_operations結構體;裡面實現了read,write,open等函數,用於應用層調用;也就是給應用層提供介面;

2通過 register_chrdev()函數來註冊驅動程式;所謂註冊,就是以主設備號為下標,把file_operations結構體放入一個內核數組;

3)通過udev機制,自動創建設備節點,通過下麵兩個函數來實現;

class_create(THIS_MODULE,"firstdrv");先創建一個類firstdrv,會在sys/class下生成;

class_device_create(firstdrv_class,NULL,MKDEV(major,0),NULL,"xyz");在類下麵創建一個xyz設備;

4)通過void first_drv_exit(void)函數,對所有資源進行卸載;

5)修飾驅動程式的入口函數;

  1. module_init(first_drv_init); /*載入驅動(insmod)的時候,會執行該巨集*/

  2. module_exit(first_drv_exit); /*卸載驅動(rmmod)的時候,會執行該巨集*/

註意,內核版本不一樣, class_create() class_device_create()這兩函數也不一樣,但實際效果是一樣的;


這上面5個步驟就是一個簡單的字元設備框架,不涉及字元設備的高級技巧;也不對應任何具體設備;就單單是一個簡單字元設備的框架;

上面這5部需要理解兩個問題:

  1. 從應用層的角度來看,應用層調用read,write,open等函數,是如何調用到驅動程式裡面的open,read,write等函數的?

  2. 就是所謂udev機制,需要知道怎麼使用內核提供的函數來自動創建設備節點;


上面這種字元設備驅動框架有一個很大缺點它提供給應用程式的介面,也就是設備節點,只有自己清楚,或者只有自己公司的人知道它所提供的介面,別人不知到!所以它不是一個通用的設備驅動程式;這是引入udev機制的原因


接下來,我們就要真正的講解輸入子系統了;


我一直認為內核就是最好的老師,所以學習輸入子系統,就得去分析內核源代碼;我採用的內核版本為2.6.22.6的版本,該版本有點老,有些東西和新版本內核不一樣,但是對於學習來說沒什麼太大影響;如果對新老版本都瞭解的話,那就更好了;那麼,在學習輸入子系統之前,需要牢記一個問題,加入輸入子系統的字元設備驅動的步驟和上面沒有加入輸入子系統的字元設備框架的5個步驟有什麼區別?這個問題,只有分析了內核自帶的輸入子系統源碼,你才能知道其中的區別;弄清楚了這個問題之後,你應該清楚,哪些事情是由內核幫我們實現的,哪些事情是需要我們自己完成的;


input輸入子系統的內核源碼位於:linux-2.6.22.6/drivers/input下;


首先明確一點,輸入子系統分為三層,核心層事件處理層設備硬體層


核心層(input.c):分析主要代碼!!!


(1)入口函數input_init()

static int __init input_init(void)/*入口*/

{

int err;

 

err = class_register(&input_class); /*sys/class/input 創建設備類*/

err = input_proc_init();/* proc文件系統相關的初始化*/

err = register_chrdev(INPUT_MAJOR, "input", &input_fops); /*註冊字元設備,majoy 13為索引,存放於把內核數組*/

...

}

input_fops結構體如下:


1279 static const struct file_operations input_fops = {

1280 .owner = THIS_MODULE,

1281 .open = input_open_file,

1282 };


<解析> 在入口函數中:

1. sys/class目錄下,創建設備類,用於生成設備節點;

class_register(&input_class);

2. 註冊字元設備驅動,以主設備號為下標,把input_fops 放入內核數組中,方便應用層通過主設備號索引;

register_chrdev(INPUT_MAJOR, "input", &input_fops);

註意:該函數執行後會在proc/devices裡面生成設備信息;

問題我們知道應用程式調用open,read,write函數來操作設備,最終會調用到驅動程式裡面所提供的open_drv,read_drv,write_drv函數,但是input_fops結構裡面只提供了open函數,那麼這是怎麼回事呢?所以接下來,繼續分析input_open_file函數;


2input_open_file函數分析:

static int input_open_file(struct inode *inode, struct file *file)

1245 {

1246 /*1input_table數組中以次設備號為下標,取出一項給handler*/

1247 struct input_handler *handler = input_table[iminor(inode) >> 5];

1248 const struct file_operations *old_fops, *new_fops = NULL;


1252 /*2handler裡面的fops賦給new_fops*/

1253 if (!handler || !(new_fops = fops_get(handler->fops)))

1264 /*3new_fops賦給file_f_op,實際上也就是handler裡面的fops*/

1265 old_fops = file->f_op;

1266 file->f_op = new_fops;

1267

1268 /*4調用new_fops裡面的open函數*/

1269 err = new_fops->open(inode, file);

...

}

 

<解析>在分析input_open_file函數之前,有一個非常重要的結構體需要瞭解!!!

struct input_handler *handler

1065 struct input_handler {

1066

1067 void *private;

1069 void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);

1070 int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);

1071 void (*disconnect)(struct input_handle *handle);

1072 void (*start)(struct input_handle *handle);

1074 const struct file_operations *fops;

1075 int minor;

1076 const char *name;

1078 const struct input_device_id *id_table;

1079 const struct input_device_id *blacklist;

1081 struct list_head h_list;

1082 struct list_head node;

1083 };


input_handler結構體在核心層引用,那麼誰來初始化該結構體呢?後面會分析到;


  1. input_table數組中,通過次設備號找到一項,賦給input_handler 指針;

    struct input_handler *handler = input_table[iminor(inode) >> 5];

  2. handler->fops賦給file_operations 指針;

    new_fops = fops_get(handler->fops)

  3. 調用new_fops裡面的open函數

    new_fops->open(inode, file)

  4. 如果read的話,最終會調用到 file->f_op中的read函數;

實際上,最終調用的open函數,是input_handler裡面的open函數;input_handler是從input_table數組中得到的;那麼歸根結底的問題就是input_table數組是由誰構造的


3input_table數組是由誰構造的?

搜索“input_table”欄位,可知:

static struct input_handler *input_table[8];

它是一個靜態結構體指針數組,說明只能在本文件使用;

最終在input_register_handler(struct input_handler *handler) 函數中找到該“input_table”數組,在這裡被賦值的;

所以接下來得分析input_register_handler()函數:

源碼如下:

1182 int input_register_handler(struct input_handler *handler)

1183 {

1184 struct input_dev *dev;

1185

1186 INIT_LIST_HEAD(&handler->h_list);

1187

1188 if (handler->fops != NULL) {

1189 if (input_table[handler->minor >> 5])

1190 return -EBUSY;

1191 /*1把傳進來的handler保存在input_table數組中*/

1192 input_table[handler->minor >> 5] = handler;

1193 }

1194

1195 /*2handler放入handler鏈表*/

1196 list_add_tail(&handler->node, &input_handler_list);

1197 /*3把設備鏈表的每項,與handler比較看是否匹配*/

1198 list_for_each_entry(dev, &input_dev_list, node)

1199 input_attach_handler(dev, handler);

1200

1201 input_wakeup_procfs_readers();

1202 return 0;

1203 }

input_table[]數組是通過input_register_handler()函數的參數,來賦值的;也就是說誰調用該函數,就是誰傳的這個input_handler結構體;

  1. input_register_handler()函數把這個結構體,存放於input_table數組;

  2. 並且把這個input_handler結構體放入handler鏈表

  3. 把設備鏈表的每項取出來,與handler比較看是否匹配;

    list_for_each_entry(dev, &input_dev_list, node)

    input_attach_handler(dev, handler);

    input_attach_handler()函數源碼如下:

    static int input_attach_handler(struct inpuft_dev *dev, struct input_handler *handler)

    {

    const struct input_device_id *id;

    id = input_match_device(handler->id_table, dev);

    error = handler->connect(handler, dev, id);

    ...

    return error;

    }

    匹配設備和handlerid,匹配成功則調用handler->connect(handler, dev, id);

分析到這裡,關鍵是看誰調用input_register_handler()函數;


 

遍歷所有內核源代碼:

input_register_handler()函數;evdev.c(事件設備)tsdev.c(觸摸屏設備)keyborad.c(鍵盤設備),以及mousedev.c(滑鼠設備)等;都調用了該註冊函數;

實際上我們這裡就進入了所謂的“事件處理層”了!!!

所以說目前對於事件處理層來說,核心層做瞭如下幾件事情:

    1. 提供了用於事件處理層的註冊介面,也就是input_register_handler()函數;

    2. 提供了 input_match_device()匹配函數,通過id號來匹配設備,一旦匹配成功,則調用input_handler->connect函數;

      註意:這個connect函數是由“事件處理層”實現的;

    3. 把事件處理層所提供的input_handler->fops通過register_chrdev()函數,放入內核數組,為應用層提供設備介面;


上面我們一直講到input_handler這個結構體,它是在核心層裡面聲明,定義的,至於在哪裡初始化的?就是“事件處理層”的事情了,“事件處理層”中會初始化這個結構體,並提交給核心層;


所以接下來,下一節我們具體的分析一個事件設備;

 


 



 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文基於 Vert.x 官網 https://vertx.io/ 內容,帶領大家學習響應式編程里比較有名的工具包 Vert.x 。文章內容取自官網由博主簡化總結,希望幫助大家理解響應式編程。 - Vert.x 簡介 - Vert.x 特性 - 響應式模式概述 > 推薦博主開源的 H5 商城項目**w ...
  • poi-tl是一個基於Apache POI的Word模板引擎,也是一個免費開源的Java類庫。同類型的FreeMarker或Velocity基於文本模板和數據生成新的html頁面或配置文件。而poi tl是一個基於Word模板和數據生成新文檔的Word模板引擎。Word模板具有豐富的樣式。Poi-t... ...
  • # 背景 在多線程編程中,線程同步是一個關鍵的概念,它確保了多個線程對共用資源的安全訪問。Java中的synchronized關鍵字是一種常用的線程同步機制,它不僅提供了互斥訪問的功能,還具備鎖升級的特性。本文將深入探討synchronized的鎖升級原理和實現方式。 在jdk1.5(包含)版本之前 ...
  • 一 .準備一個空的Maven項目。 二. 配置pom文件,引入相關依賴。 <!--版本建議換成提示的更安全的版本--> <!-- mybatis插件 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifa ...
  • **本文深入探索了Django中的Cookie和Session,解析瞭如何應對HTTP協議的無狀態性問題,說明其基礎概念,分析工作原理,並討論何時應選擇使用Cookie或Session。文章進階部分,提出高效管理Cookie和Session,以及如何利用它們進行用戶身份驗證。** ## HTTP協議 ...
  • # 一、前言 最近在做一個項目,有個比較耗時的操作是啟用線程進行非同步操作,當時在啟用的線程時,突然發現子線程無法獲取父線程中的HttpServletRequest請求對象,因為是第一次遇到這種問題,所以記錄一下解決方案。 # 二、問題模擬 在這裡,我們簡單模擬一下出現的問題。我們首先編寫一個簡單的h ...
  • 在C#中調用StringBoot介面,POST請求,案例代碼: public string HttpPost() { //把用戶傳過來的數據轉成“UTF-8”的位元組流Encoding encoding = Encoding.UTF8;//創建一個HTTP請求HttpWebRequest reques ...
  • fdisk 命令 創建分區 實現擴容 Linux fdisk命令簡介 Linux fdisk 是一個創建和維護分區表的程式,它相容 DOS 類型的分區表、BSD 或者 SUN 類型的磁碟列表。 菜單操作說明 m :顯示菜單和幫助信息 a :活動分區標記/引導分區 d :刪除分區 l :顯示分區類型 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...