Linux input子系統編程、分析與模板

2021-01-09 TechWeb

輸入設備都有共性:中斷驅動+字符IO,基於分層的思想,Linux內核將這些設備的公有的部分提取出來,基於cdev提供接口,設計了輸入子系統,所有使用輸入子系統構建的設備都使用主設備號13,同時輸入子系統也支持自動創建設備文件,這些文件採用阻塞的IO讀寫方式,被創建在"/dev/input/"下。如下圖所示。內核中的輸入子系統自底向上分為設備驅動層,輸入核心層,事件處理層。由於每種輸入的設備上報的事件都各有不同,所以為了應用層能夠很好識別上報的事件,內核中也為應用層封裝了標準的接口來描述一個事件,這些接口在"/include/upai/linux/input"中。

設備驅動層是具體硬體相關的實現,也是驅動開發中主要完成的部分, 輸入核心層主要提供一些API供設備驅動層調用,通過這些API設備驅動層上報的數據就可以傳遞到事件處理層, 事件處理層負責創建設備文件以及將上報的事件傳遞到用戶空間, 

input的使用

input對象描述了一個輸入設備,包括它可能上報的事件,這些事件使用位圖來描述,內核提供的相應的工具幫助我們構建一個input對象,大家可以參考內核文檔"Documentation/input/input-programming.txt",裡面對於input子系統的使用有詳細的描述。

//input設備對象  struct input_dev {          const char *name;          unsigned long evbit[BITS_TO_LONGS(EV_CNT)];          unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];          unsigned long relbit[BITS_TO_LONGS(REL_CNT)];          unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];          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 long key[BITS_TO_LONGS(KEY_CNT)];          unsigned long led[BITS_TO_LONGS(LED_CNT)];          unsigned long snd[BITS_TO_LONGS(SND_CNT)];          unsigned long sw[BITS_TO_LONGS(SW_CNT)];            struct input_handle __rcu *grab;            struct device dev;            struct list_head        h_list;          struct list_head        node;  }; 

struct input_dev

--122--> 這個name不是設備名,input子系統的設備名在子系統源碼中指定的,不是這。

--129--> 設備支持的輸入事件位圖,EV_KEY,EV_REL, etc

--130--> 對於按鍵事件,設備支持的輸入子事件位圖

--132--> 對於相對坐標事件,設備支持的相對坐標子事件位圖

--133--> 對於絕對坐標事件,設備支持的絕對坐標子事件位圖

--134--> 混雜設備的支持的子事件位圖

--180-->表示這是一個device。

--182-->h_list是用來連結相關handle的鍊表

--183-->node用來連結其他input_dev的鍊表

分配/釋放

//drivers/input/input.c //創建一個input對象  struct input_dev *input_allocate_device(void);//釋放一個input對象  void input_free_device(struct input_dev *dev); 

初始化

初始化一個input對象是使用input子系統編寫驅動的主要工作,內核在頭文件"include/uapi/linux/input.h"中規定了一些常見輸入設備的常見的輸入事件,這些宏和數組就是我們初始化input對象的工具。這些宏同時用在用戶空間的事件解析和驅動的事件註冊,可以看作是驅動和用戶空間的通信協議,所以理解其中的意義十分重要。在input子系統中,每一個事件的發生都使用事件(type)->子事件(code)->值(value)三級來描述,比如,按鍵事件->按鍵F1子事件->按鍵F1子事件觸發的值是高電平1。注意,事件和子事件和值是相輔相成的,只有註冊了事件EV_KEY,才可以註冊子事件BTN_0,也只有這樣做才是有意義的。

下面就是內核約定的事件類型,對應應用層的事件對象的type域

下面這些是按鍵子事件的類型,可以看到對PC鍵值的定義

除了對常用的事件進行描述,內核同樣提供了工具將這些事件正確的填充到input對象中描述事件的位圖中。

//第一種//這種方式非常適合同時註冊多個事件  button_dev->evbit[0] = BIT_MASK(EV_KEY);             button_dev->keybit[BIT_WORD(BTN_0|BTN_1)] = BIT_MASK(BTN_0|BTN_1);   

註冊/註銷

初始化好了一個input對象,接下來就需要將其註冊到內核

//註冊input對象到內核 int input_register_device(struct input_dev *dev); //從內核註銷一個input對象 void input_unregister_device(struct input_dev *dev); 

驅動層報告事件

在合適的時機(由於輸入最終是中斷表示的,所以通常在驅動的中斷處理函數中)驅動可以將註冊好的事件上報,且可以同時上報多個事件,下面是內核提供的API

//上報指定的事件+子事件+值 void input_event(    struct input_dev *dev,unsigned int type,unsigned int code,int value);//上報鍵值    void input_report_key(struct input_dev *dev,unsigned int code,int value);//上報絕對坐標    void input_report_abs(struct input_dev *dev,unsigned int code,int value);//報告同步事件    void input_report_rel(struct input_dev *dev,unsigned int code,int value);//同步所有的上報    void input_sync(struct input_dev *dev); 

上報事件有2點需要注意:

report函數們並不會真的上報,只是準備上報,sync才會真的將剛剛report的事件真的上報搭input核心 input核心會進行裁決再上報的事件處理層,所以對於按鍵事件,一定要先報1再報0(或者反過來),不能只report 1或0, 這樣核心會認為是一個事件被誤觸發了多次而只上報一次,雖然我們真的按下了多次。

應用層解析

事件處理層最終會將驅動sync一次時所有report的事件組織成一個struct input_value[]的形式上報到應用層,在應用層從相應的設備文件中獲取上報的事件的時候,需要注意:

收到數組元素的數量會比底層多一個空元素,類似於寫of_device_id[]時最後的空元素,這點應用層在解析的時候需要注意。 事件處理層並不會緩存收到的事件,如果有新的事件到來,即使舊的事件沒有被讀取,也會被覆蓋,所以應用程式需要及時讀取。

前文已經說過,"include/uapi/linux/input.h"中的宏是應用層和驅動層共用的通信協議,所以應用層在解析收到的struct input_value對象的時候,只需要"include <linux/input.h>"即可使用其中的宏。

/*  * The event structure itself  */  struct input_event {     struct timeval time;     __u16 type;     __u16 code;     __s32 value; };  

input分析

上文已經說過,input子系統使用三層結構來實現驅動事件到應用層的傳遞。具體的,這三個層次每一個層次都由一條結構體鍊表組成,在設備驅動層,核心結構體是input_dev;在input核心層,是input_handle;在事件處理層,是input_handler。內核通過鍊表和指針將三者結合到一起,最終實現了input_dev和input_handler的多對多的映射關係,這種關係可用下圖簡單描述。

模板

下面的這個模板首先使用input子系統上報按鍵事件,然後在應用層讀取。

input按鍵設備驅動

{            key@26{                       compatible = "xj4412,key";                       interrupt-parent = <&gpx1>;                       interrupts = <2 2>;            }; };   static struct input_dev *button_dev; static int button_irq; static int irqflags; static irqreturn_t button_interrupt(int irq, void *dummy){     input_report_key(button_dev, BTN_0, 0);     input_report_key(button_dev, BTN_0, 1);     input_sync(button_dev);    return IRQ_HANDLED; }  static int button_init(void){     request_irq(button_irq, button_interrupt,irqflags, "button", NULL)) ;          button_dev = input_allocate_device();     button_dev->name = "button";     button_dev->evbit[0] = BIT_MASK(EV_KEY);     button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);          input_register_device(button_dev);    return 0; } static int button_exit(void){     input_free_device(button_dev);     free_irq(button_irq, button_interrupt);    return 0;    } static int key_probe(struct platform_device *pdev){     struct resource *irq_res;     irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);     if(irq_res){         button_irq = irq_res->start;         irqflags = irq_res->flags & IRQF_TRIGGER_MASK;     }else{          return -EINVAL;          }    return button_init(); } static int key_remove(struct platform_device *dev){     return button_exit(); } struct of_device_id of_tbl[] = {     {.compatible = "xj4412,key",},     {}, }; MODULE_DEVICE_TABLE(of, of_tbl);struct platform_driver key_drv = {     .probe = key_probe,     .remove = key_remove,     .driver.name = "keydrv",     .driver.of_match_table = of_tbl, }; module_platform_driver_register(key_drv); MODULE_LICENSE("GPL");  

應用層獲取鍵值

#include <linux/input.h> struct input_event {    struct timeval time;     unsigned short type;     unsigned short code;    int value; }; int main(int argc, char * const argv[]){     int fd = 0;     struct input_event event[3] = {0};      //3!!!,驅動上傳了2個事件,第三個用來裝空元素      int ret = 0;     fd = open(argv[1],O_RDONLY);     while(1){         ret = read(fd,&event,sizeof(event));         printf("ret:%d,val0:%d,val1:%d,val12:%d",ret,event[0].value,event[1].value,event[2].value);          //2!!!,最後一個是空         sleep(1);     }     return 0; } 

點讚 0

相關焦點

  • 「正點原子Linux連載」第五十八章Linux INPUT子系統實驗
    第五十八章Linux INPUT子系統實按鍵、滑鼠、鍵盤、觸控螢幕等都屬於輸入(input)設備,Linux內核為此專門做了一個叫做input子系統的框架來處理輸入事件。輸入設備本質上還是字符設備,只是在此基礎上套上了input框架,用戶只需要負責上報輸入事件,比如按鍵值、坐標等信息,input核心層負責處理這些事件。
  • Android Input 子系統初探
    Android系統基於Linux內核實現,內核作為整個作業系統的核心,對下,它負責整個硬體的驅動、實現對硬體器件的控制管理;對上,它提供各種系統所需的核心功能。Android系統支持的輸入設備較多,如按鍵、觸控螢幕、手柄等,面對種類繁雜的輸入設備,內核通過抽象化的方式來使得各輸入設備的的核心處理流程統一化,細節處理流程差異化(通過不同類型的回調實現),這就是Input子系統所要完成的內容,總結來說,它在內核中主要作用為:規範化Input Device的定義方式及其數據的上報格式;規範化Input
  • LINUX IIO子系統專欄分析之一 IIO子系統概述
    從本章開始,我們進行IIO子系統專欄的分析文檔,本次IIO子系統專欄分析文檔大概包含如下幾章:一、 IIO子系統概述二、IIO子系統相關數據結構分析三、iio trigger 介紹四、iio event介紹五、iio buffer介紹六、iio device的註冊與註銷介紹七、iio trigger
  • Linux regmap子系統分析之二 從數據結構分析系統實現
    上一章我們簡要分析了regmap子系統,本章我們將從regmap子系統的數據結構介紹regmap子系統的實現。 一、數據結構間的關聯及說明 針對regmap子系統,我們首先要知道regmap子系統要解決的痛點是什麼?
  • Linux V4l2子系統專欄之一 子系統概述
    從本章開始,我們開始分析V4L2子系統(Video for Linux two),主要用於音視頻設備的框架。V4l2主要用於驅動視頻輸出設備(video outpt interface)、Video overlay interface、Video output overlay device、VBI interface、Radio interface等。
  • LINUX IIO子系統分析之三 IIO trigger分析
    上一章我們分析了IIO子系統的數據結構定義,本章我們主要介紹IIO TRIGGER的設計實現,主要內容如下:一、數據結構簡述 二、設計實現說明 三、提供接口說明一、數據結構簡述中斷子系統的內容可參考我之前寫的中斷子系統專欄,我在中斷子系統專欄也實現了一個虛擬的irq chip,實現的原理和此處trigger實現的虛擬irq chip的原理是一樣的)。
  • 企業日誌分析之Linux系統message收集展示
    2.linux系統日誌收集數據總量 主要是展示當前總共有多少臺主機上傳了message日誌數據。 主要是展示收集的日誌信息裡前5個程序名;我這5個都是docker伺服器,所以docker日誌有很多。
  • Linux regulator子系統分析之一 總體概述
    Linux regulator 子系統主要用於管理電壓、電流電源設備的電壓、電流輸入使能、動態調整電壓、電流等,屬於電源管理的一部分。從本章開始我們分析regulator子系統。主要分為如下幾部分:一、regulator子系統總體概述二、regulator相關數據結構分析三、regulator相關註冊、註銷函數分析四、regulator設備驅動實現本章主要對regulator子系統進行簡要說明。
  • Linux regmap子系統分析之一 系統概述
    本系統專欄主要涉及如下幾個子章節:一、regmap子系統概述二、regmap子系統數據結構分析三、regmap子系統接口說明四、regmap bus實例說明(以i2c為例)本章我們主要對regmap子系統做一個簡單的說明。
  • LINUX CommonClock Framework子系統分析之一 系統概述
    本專欄主要介紹linux的ccf子系統,主要用於系統clock的管理等操作。本專欄我們大概分為如下幾章進行學習:一、CCF子系統概述二、CCF子系統數據結構分析及關聯說明三、CCF子系統的clk註冊與註銷接口實現分析四、虛擬的clk設備驅動實現 本章我們主要進行CCF子系統的概述。
  • 打造適用於 Linux 的 Windows 子系統——替代虛擬機的好幫手
    前言曾經為了在Linux環境中編程學習,但又不想放棄熟悉的Windows界面,為此安裝過雙系統,也安裝過虛擬機,然而又苦於雙系統中切換系統的不便,苦於如今微軟在Windows10系統中,推出了適用於 Linux 的 Windows 子系統,說簡單點,就是把Linux發行版當作桌面應用安裝在Windows10中。
  • 基於Linux系統的觸控螢幕驅動方案
    輸入子系統由設備驅動層、核心層和事件處理層構成。設備驅動層提供對硬體各寄存器的讀寫訪問和將底層硬體對用戶輸入訪問的響應轉換為標準的輸入事件,通過核心層提交給事件處理層;核心層對設備驅動層提供編程接口,對事件處理層的也提供編程接口;事件處理層為用戶空間的應用程式提供了統一訪問設備的接口和驅動層提交來的事件處理。
  • 簡單分析「Windows將被微軟放棄淪為Linux子系統」
    昨天看一個消息:有觀點認為Windows將被微軟放棄而淪為Linux子系統 – Windows – cnBeta.COMhttps://www.cnbeta.com/articles/tech/1034593.htmWindows
  • win10安裝linux子系統(wsl)
    win10安裝linux子系統(wsl)1、打開Microsoft Store方式一:在電腦左下角打開方式二:在電腦左下角的搜索裡2、允許windows運行linux子系統打開控制面板->程序和功能->啟動或關閉windows功能勾上「適用於Linux的Windows子系統」,點擊確定,然後按提示重啟電腦。
  • Linux regulator子系統分析之三 regulator註冊、註銷接口分析
    在上一章我們介紹了regulator子系統相關的數據結構間的關聯以及每一個數據結構的定義。針對數據結構間的關聯則需要regulator子系統提供的接口實現。而本章我們就說明這些接口。本章的章節如下:一、regulator device的註冊與註銷二、regulator的註冊與註銷(regulator device的使用者)三、regulator子系統提供的接口一、regulator device的註冊與註銷針對regulator device的註冊與註銷函數主要涉及regulator_register、regulator_unregister
  • Python編程3:與計算機對話之鍵盤輸入input()
    Python為我們提供了input( )函數。技能1:輸入語句input( )input()函數可以接收任意輸入,並將所有輸入默認為字符串處理,並返回字符串類型。input()直接使用input(),系統執行這句後會暫停,並等待你的輸入(圖1),紅色箭頭指向的位置就是讓你鍵入內容。
  • Linux系統推薦學習的程式語言以及經驗
    First ;首先作為一個初學者,必須扎紮實實的掌握一門基礎的編程,計算機語言想通相似,想要學好編程,必須熟練地掌它握一門基礎計語言,這裡推薦學習C和C++;C++作為一門最難的語言,能掌握它,其他語言自然不在話下。
  • Android系統級深入開發——input驅動程序
    3、Event設備在用戶空間大多使用read、ioctl、poll等文件系統的接口進行操作,read用於讀取輸入信息,ioctl用於獲取和設置信息,poll調用可以進行用戶空間的阻塞,當內核有按鍵中斷時,通過在中斷中喚醒poll的內核實現,這樣在用戶空間poll調用也可以返回。 Event設備在文件系統中的設備節點為:/dev/input/eventX。
  • 嵌入式Linux學習方法 適合初學者設計學習計劃
    簡單說,從arm基礎知識到裸機編程,從uCOSII到linux,從linux基礎的命令到shell編程,從u-boot到文件系統,最後到現在的linux驅動程序。從簡單到複雜,從基礎到高級基本是按照這個來的。
  • Linux regmap子系統分析之三 regmap bus實例分析
    在前面一章我們分析了regmap子系統的數據結構,基本上熟悉了數據結構的關聯,也就大概理解了regmap子系統的實現流程,本章我們簡要介紹下regmap子系統中接口的調用過程,然後介紹下regmap bus的實現及其提供的regmap的創建及註銷接口。