日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

driver: Linux设备模型之input子系统详解

發布時間:2025/4/16 linux 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 driver: Linux设备模型之input子系统详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本節從整體上講解了輸入子系統的框架結構。有助于讀者從整體上認識linux的輸入子系統。在陷入代碼分析的過程中,通過本節的知識能夠找準方向,明白原理。

本節重點:


  • ? ? ? ? ?輸入子系統的框架結構
  • ? ? ? ? ?各層對應內核中的文件位置
  • ? ? ? ? ?輸入子系統的事件處理機制
  • ? ? ? ? ?輸入子系統的驅動層基本操作流程
  • ? ? ? ? ?輸入子系統的驅動層常用函數


本節難點:

  • ? ? ? ? ?輸入子系統的事件處理機制
  • ? ? ? ? ?輸入子系統的驅動工作流程


1??? 初識linux輸入子系統

linux輸入子系統(linux input subsystem)從上到下由三層實現,分別為:輸入子系統事件處理層(EventHandler)、輸入子系統核心層(InputCore)和輸入子系統設備驅動層。

對于輸入子系統設備驅動層而言,主要實現對硬件設備的讀寫訪問,中斷設置,并把硬件產生的事件轉換為核心層定義的規范提交給事件處理層。

對于核心層而言,為設備驅動層提供了規范和接口。設備驅動層只要關心如何驅動硬件并獲得硬件數據(例如按下的按鍵數據),然后調用核心層提供的接口,核心層會自動把數據提交給事件處理層。

對于事件處理層而言,則是用戶編程的接口(設備節點),并處理驅動層提交的數據處理。

對于linux輸入子系統的框架結構如下圖1所示:


圖1? linux輸入子系統框架結構

?

由上圖所展現的內容就是linux輸入子系統的分層結構。

/dev/input目錄下顯示的是已經注冊在內核中的設備編程接口,用戶通過open這些設備文件來打開不同的輸入設備進行硬件操作。

事件處理層為不同硬件類型提供了用戶訪問及處理接口。例如當我們打開設備/dev/input/mice時,會調用到事件處理層的Mouse Handler來處理輸入事件,這也使得設備驅動層無需關心設備文件的操作,因為Mouse Handler已經有了對應事件處理的方法。

輸入子系統由內核代碼drivers/input/input.c構成,它的存在屏蔽了用戶到設備驅動的交互細節,為設備驅動層和事件處理層提供了相互通信的統一界面。

下圖2簡單描述了linux輸入子系統的事件處理機制:


圖2? linux輸入子系統事件處理機制

?

由上圖可知輸入子系統核心層提供的支持以及如何上報事件到input event drivers。

作為輸入設備的驅動開發者,需要做以下幾步:

????????????在驅動加載模塊中,設置你的input設備支持的事件類型,類型參見表1設置

????????????注冊中斷處理函數,例如鍵盤設備需要編寫按鍵的抬起、放下,觸摸屏設備需要編寫按下、抬起、絕對移動,鼠標設備需要編寫單擊、抬起、相對移動,并且需要在必要的時候提交硬件數據(鍵值/坐標/狀態等等)

????????????將輸入設備注冊到輸入子系統中

?

表1? Linux輸入子系統支持的數據類型

EV_SYN???? 0x00????同步事件

EV_KEY???? 0x01????按鍵事件

EV_REL???? 0x02????相對坐標(如:鼠標移動,報告相對最后一次位置的偏移)

EV_ABS???? 0x03????絕對坐標(如:觸摸屏或操作桿,報告絕對的坐標位置)

EV_MSC???? 0x04????其它

EV_SW????? 0x05????開關

EV_LED???? 0x11????按鍵/設備燈

EV_SND???? 0x12????聲音/警報

EV_REP???? 0x14????重復

EV_FF????? 0x15????力反饋

EV_PWR??? 0x16????電源

EV_FF_STATUS??? 0x17???力反饋狀態

EV_MAX??? 0x1f????事件類型最大個數和提供位掩碼支持

由表1可知,設備所能表示的事件種類,一個設備可以選擇一個或多個事件類型上報給輸入子系統。

Linux輸入子系統提供了設備驅動層上報輸入事件的函數,在include/linux/input.h中:

voidinput_report_key(struct input_dev *dev, unsigned int code, int value); ???? //上報按鍵事件

voidinput_report_rel(struct input_dev *dev, unsigned int code, int value); ????? //上報相對坐標事件

voidinput_report_abs(struct input_dev *dev, unsigned int code, int value);????????????? //上報絕對坐標事件

……

當提交輸入設備產生的輸入事件之后,需要調用下面的函數來通知輸入子系統,以處理設備產生的完整事件:


[cpp]?view plaincopy
  • void?input_sync(struct?input_dev?*dev);??

  • 2??? 輸入設備驅動的簡單案例

    在Linux內核文檔的documentation/input下,有一個input-programming.txt文件,講解了編寫輸入設備驅動程序的核心步驟。

    提供的案例代碼描述了一個button設備,產生的事件通過BUTTON_PORT引腳獲取,當有按下/釋放發生時,BUTTON_IRQ被觸發,以下是驅動的源代碼:


    [cpp]?view plaincopy
  • #include???
  • ?#include???
  • ?#include???
  • ??
  • ?#include???
  • ?#include???
  • ??
  • ?static?struct?input_dev?*button_dev;??
  • ??
  • ?static?void?button_interrupt(int?irq,?void*dummy,?struct?pt_regs?*fp)??
  • ?{??
  • ????????input_report_key(button_dev,?BTN_1,?inb(BUTTON_PORT)?&?1);??
  • ????????input_sync(button_dev);??
  • ?}????????
  • ??
  • ?static?int?__init?button_init(void)??
  • ?{??
  • ????????int?error;??
  • ??????????
  • ????????if?(request_irq(BUTTON_IRQ,?button_interrupt,?0,?"button",NULL))?{??
  • ?????????????????printk(KERN_ERR"button.c:?Can't?allocate?irq?%d\n",?button_irq);??
  • ?????????????????return?-EBUSY;??
  • ????????}????????
  • ??????????
  • ?????????button_dev?=?input_allocate_device();??
  • ????????if?(!button_dev)?{??
  • ?????????????????printk(KERN_ERR"button.c:?Not?enough?memory\n");??
  • ?????????????????error?=?-ENOMEM;??
  • ?????????????????goto?err_free_irq;??
  • ????????}??
  • ??
  • ????????button_dev->evbit[0]?=?BIT(EV_KEY);??
  • ????????button_dev->keybit[LONG(BTN_0)]?=?BIT(BTN_0);??
  • ??
  • ????????error?=?input_register_device(button_dev);??
  • ????????if?(error)?{??
  • ?????????????????printk(KERN_ERR"button.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,?button_interrupt);??
  • ????????return?error;??
  • ?}??
  • ??
  • ?static?void?__exit?button_exit(void)??
  • ?{??
  • ???????input_unregister_device(button_dev);??
  • ????????free_irq(BUTTON_IRQ,?button_interrupt);??
  • }??
  • ??
  • module_init(button_init);??
  • module_exit(button_exit);??

  • 編寫基于輸入子系統的設備驅動程序需要包含,因為它包含了輸入子系統的接口和所有的宏定義,這些內容在編寫輸入設備驅動程序時需要用到。

    button_init函數說明:

    當模塊加載(insmod)或內核引導過程中,button_init函數會被調用。首先做的工作是獲取能夠正確控制硬件設備的硬件資源(例如內存、IO內存、中斷和DMA),在代碼中BUTTON_IRQ作為BUTTON設備的中斷資源,通過request_irq()函數被申請注冊。當有按鍵按下/釋放時,調用button_interrupt()中斷處理函數獲取按鍵值BUTTON_PORT(BUTTON設備的I/O資源)。

    那么輸入子系統怎么能夠知道這個設備為輸入設備呢?通過第8行為設備定義一個用于描述一個輸入設備對象。


    [cpp]?view plaincopy
  • static?struct?input_dev?*button_dev;??

  • 定義了button_dev之后,如何通知輸入子系統有新的輸入設備了呢?或者說如何把一個新的輸入設備加入到輸入子系統中呢?可以通過輸入子系統核心層input.c中提供的函數分配一個輸入設備,在代碼的第25行。


    [cpp]?view plaincopy
  • button_dev=?input_allocate_device();??

  • 有了輸入設備的描述,當事件產生時,輸入子系統怎么能夠知道設備產生的事件類型呢?通過32和33行的代碼。


    [cpp]?view plaincopy
  • button_dev->evbit[0]=?BIT(EV_KEY);??
  • button_dev->keybit[LONG(BTN_0)]=?BIT(BTN_0);??

  • 其中evbit和keybit成員分別代表設備產生的事件類型和上報的按鍵值。其中輸入子系統的一些位操作NBITS、BIT、LONG經常被用到:


    [cpp]?view plaincopy
  • #defineNBITS(x)?(((x)/BITS_PER_LONG)+1)?????????????????//通過位x獲取數組的長度??
  • #defineBIT(x)???????(1UL<<((x)%BITS_PER_LONG))???????//返回位x在數組中的位域??
  • #defineLONG(x)?((x)/BITS_PER_LONG)????????????????????????//返回位x的索引??

  • 以上的工作做完之后,即可注冊為輸入設備了,代碼的35行。


    [cpp]?view plaincopy
  • input_register_device(button_dev);??

  • 這個函數把button_dev輸入設備掛入輸入設備鏈表中,并且通知事件處理層調用connect函數完成設備和事件處理的綁定,當用戶打開設備時,便能夠調用到相應的事件處理接口獲得硬件上報的數據了。input_register_device()函數是會睡眠的函數,因此不能夠在中斷上下文和持有自旋鎖的代碼中調用。

    當我們把上面的工作做完之后,設備驅動中唯一值得關注的就是button_interrupt()中斷處理函數了。當按鍵動作發生,button_interrupt()函數被調用,完成事件的上報由其中的兩條語句完成。


    [cpp]?view plaincopy
  • input_report_key(button_dev,?BTN_1,?inb(BUTTON_PORT)?&?1);??
  • input_sync(button_dev);??

  • 其中input_report_key上報了這是一個按鍵事件,且它的值為inb(BUTTON_PORT) & 1,由于案例代碼只產生一個按鍵的值,因此input_sync()在這里不起關鍵作用。但如果是一個觸摸屏,即有x坐標和y坐標,則需要通過input_sync()函數把x和y坐標完整地傳遞給輸入子系統。

    總結

    以上是生活随笔為你收集整理的driver: Linux设备模型之input子系统详解的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。