Linux輸入子系統(一) _驅動編碼

来源:http://www.cnblogs.com/xiaojiang1025/archive/2017/02/19/6414746.html
-Advertisement-
Play Games

輸入設備都有共性:中斷驅動+字元IO ,基於分層的思想,Linux內核將這些設備的公有的部分提取出來,基於cdev提供介面,設計了輸入子系統,所有使用輸入子系統構建的設備都使用 主設備號13 ,同時輸入子系統也 支持自動創建設備文件 ,這些文件採用阻塞的IO讀寫方式,被創建在 "/dev/input ...


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

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

input的使用

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

//input設備對象
121 struct input_dev {
122         const char *name;
129         unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
130         unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
131         unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
132         unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
133         unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
134         unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
135         unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
136         unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
137         unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
155 
162         unsigned long key[BITS_TO_LONGS(KEY_CNT)];
163         unsigned long led[BITS_TO_LONGS(LED_CNT)];
164         unsigned long snd[BITS_TO_LONGS(SND_CNT)];
165         unsigned long sw[BITS_TO_LONGS(SW_CNT)];
166 
172         struct input_handle __rcu *grab;
179 
180         struct device dev;
181 
182         struct list_head        h_list;
183         struct list_head        node;
190 };

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); 
//第二種
//通常用於只註冊一個事件
set_bit(EV_KEY,button_dev.evbit);
set_bit(BTN_0,button_dev.keybit);

註冊/註銷

初始化好了一個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點需要註意:

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

應用層解析

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

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

前文已經說過,"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按鍵設備驅動

/{
           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\n",ret,event[0].value,event[1].value,event[2].value);          //2!!!,最後一個是空
        sleep(1);
    }
    return 0;
}




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

-Advertisement-
Play Games
更多相關文章
  • 一、this.xx 和 xx 是兩回事 受後端語言影響,總把this.xx 和xx 當中一回事,認為在function中,xx 就是this.xx,其實完全兩回事; this.xx 是沿著this 原型鏈找變數,xx是沿著作用域鏈找變數 var func = function(){ console. ...
  • 本文將從頭開始編寫實際的代碼來完成一個angular2的demo。 題外話是其實angular2官網的快速開始項目已經很酷炫了,但其側重快速二字,只夠拿來練習玩耍,倒是github上確實已經有了一些不錯的angular2-starter。 1. 安裝必要的node環境與npm 當然TS環境也是必須的 ...
  • 微信公眾號開發,主要是移動端網頁的頁面開發,在這裡推薦3個移動端UI框架:WeUI、SUI和Mint UI。 ...
  • 1. 個人開發者賬號的申請 2. DUNS的申請 3. 個人開發者賬號轉公司賬號 ...
  • Navicatfor MySQL:快捷鍵整理, 使用快捷鍵,提升工作效率! ...
  • 1、 " Oracle 資料庫管理工具概述 " 2、 " SQL Plus 實用命令參考 " 2.1、 "連接/斷開命令" 2.2、 "執行 SQL 語句" 2.3、 "執行 PL/SQL 語句" 2.4、 "文件操作命令" 2.5、 "修改用戶密碼" 2.6、 "執行存儲過程" 2.7、 "其它命 ...
  • 本文屬於一篇內部規範文檔,整理的初衷是為了規範、統一集團的Windows伺服器(僅僅SQL Server資料庫伺服器)防火牆設置,僅僅供內部其它同事設置Windows防火牆時作為參考的文檔資料。如有不足,敬請指正。後續將不斷完善、整理該文檔。文檔裡面部分內容直接摘抄自MSDN,敬請知曉! 文檔類型 ... ...
  • 終於開始看Spark源碼了,先從最常用的spark shell腳本開始吧。不要覺得一個啟動腳本有什麼東東,其實裡面還是有很多知識點的。另外,從啟動腳本入手,是尋找代碼入口最簡單的方法,很多開源框架,其實都可以通過這種方式來尋找源碼入口。 先來介紹一下Spark shell是什麼? Spark she ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...