溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Linux input怎么使用

發(fā)布時(shí)間:2021-12-24 14:04:50 來(lái)源:億速云 閱讀:199 作者:iii 欄目:系統(tǒng)運(yùn)維

本篇內(nèi)容主要講解“Linux input怎么使用”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“Linux input怎么使用”吧!

輸入設(shè)備都有共性:中斷驅(qū)動(dòng)+字符IO,基于分層的思想,Linux內(nèi)核將這些設(shè)備的公有的部分提取出來(lái),基于cdev提供接口,設(shè)計(jì)了輸入子系統(tǒng),所有使用輸入子系統(tǒng)構(gòu)建的設(shè)備都使用主設(shè)備號(hào)13,同時(shí)輸入子系統(tǒng)也支持自動(dòng)創(chuàng)建設(shè)備文件,這些文件采用阻塞的IO讀寫方式,被創(chuàng)建在"/dev/input/"下。如下圖所示。內(nèi)核中的輸入子系統(tǒng)自底向上分為設(shè)備驅(qū)動(dòng)層,輸入核心層,事件處理層。由于每種輸入的設(shè)備上報(bào)的事件都各有不同,所以為了應(yīng)用層能夠很好識(shí)別上報(bào)的事件,內(nèi)核中也為應(yīng)用層封裝了標(biāo)準(zhǔn)的接口來(lái)描述一個(gè)事件,這些接口在"/include/upai/linux/input"中。

  • 設(shè)備驅(qū)動(dòng)層是具體硬件相關(guān)的實(shí)現(xiàn),也是驅(qū)動(dòng)開(kāi)發(fā)中主要完成的部分,

  • 輸入核心層主要提供一些API供設(shè)備驅(qū)動(dòng)層調(diào)用,通過(guò)這些API設(shè)備驅(qū)動(dòng)層上報(bào)的數(shù)據(jù)就可以傳遞到事件處理層,

  • 事件處理層負(fù)責(zé)創(chuàng)建設(shè)備文件以及將上報(bào)的事件傳遞到用戶空間, 

Linux input怎么使用

input的使用

input對(duì)象描述了一個(gè)輸入設(shè)備,包括它可能上報(bào)的事件,這些事件使用位圖來(lái)描述,內(nèi)核提供的相應(yīng)的工具幫助我們構(gòu)建一個(gè)input對(duì)象,大家可以參考內(nèi)核文檔"Documentation/input/input-programming.txt",里面對(duì)于input子系統(tǒng)的使用有詳細(xì)的描述。

//input設(shè)備對(duì)象  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--> 這個(gè)name不是設(shè)備名,input子系統(tǒng)的設(shè)備名在子系統(tǒng)源碼中指定的,不是這。

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

--130--> 對(duì)于按鍵事件,設(shè)備支持的輸入子事件位圖

--132--> 對(duì)于相對(duì)坐標(biāo)事件,設(shè)備支持的相對(duì)坐標(biāo)子事件位圖

--133--> 對(duì)于絕對(duì)坐標(biāo)事件,設(shè)備支持的絕對(duì)坐標(biāo)子事件位圖

--134--> 混雜設(shè)備的支持的子事件位圖

--180-->表示這是一個(gè)device。

--182-->h_list是用來(lái)鏈接相關(guān)handle的鏈表

--183-->node用來(lái)鏈接其他input_dev的鏈表

分配/釋放

//drivers/input/input.c //創(chuàng)建一個(gè)input對(duì)象  struct input_dev *input_allocate_device(void);//釋放一個(gè)input對(duì)象  void input_free_device(struct input_dev *dev);

初始化

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

下面就是內(nèi)核約定的事件類型,對(duì)應(yīng)應(yīng)用層的事件對(duì)象的type域

Linux input怎么使用

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

Linux input怎么使用

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

//***種//這種方式非常適合同時(shí)注冊(cè)多個(gè)事件  button_dev->evbit[0] = BIT_MASK(EV_KEY);             button_dev->keybit[BIT_WORD(BTN_0|BTN_1)] = BIT_MASK(BTN_0|BTN_1);

注冊(cè)/注銷

初始化好了一個(gè)input對(duì)象,接下來(lái)就需要將其注冊(cè)到內(nèi)核

//注冊(cè)input對(duì)象到內(nèi)核 int input_register_device(struct input_dev *dev); //從內(nèi)核注銷一個(gè)input對(duì)象 void input_unregister_device(struct input_dev *dev);

驅(qū)動(dòng)層報(bào)告事件

在合適的時(shí)機(jī)(由于輸入最終是中斷表示的,所以通常在驅(qū)動(dòng)的中斷處理函數(shù)中)驅(qū)動(dòng)可以將注冊(cè)好的事件上報(bào),且可以同時(shí)上報(bào)多個(gè)事件,下面是內(nèi)核提供的API

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

上報(bào)事件有2點(diǎn)需要注意:

  1. report函數(shù)們并不會(huì)真的上報(bào),只是準(zhǔn)備上報(bào),sync才會(huì)真的將剛剛report的事件真的上報(bào)搭input核心

  2. input核心會(huì)進(jìn)行裁決再上報(bào)的事件處理層,所以對(duì)于按鍵事件,一定要先報(bào)1再報(bào)0(或者反過(guò)來(lái)),不能只report 1或0, 這樣核心會(huì)認(rèn)為是一個(gè)事件被誤觸發(fā)了多次而只上報(bào)一次,雖然我們真的按下了多次。

應(yīng)用層解析

事件處理層最終會(huì)將驅(qū)動(dòng)sync一次時(shí)所有report的事件組織成一個(gè)struct input_value[]的形式上報(bào)到應(yīng)用層,在應(yīng)用層從相應(yīng)的設(shè)備文件中獲取上報(bào)的事件的時(shí)候,需要注意:

  1. 收到數(shù)組元素的數(shù)量會(huì)比底層多一個(gè)空元素,類似于寫of_device_id[]時(shí)***的空元素,這點(diǎn)應(yīng)用層在解析的時(shí)候需要注意。

  2. 事件處理層并不會(huì)緩存收到的事件,如果有新的事件到來(lái),即使舊的事件沒(méi)有被讀取,也會(huì)被覆蓋,所以應(yīng)用程序需要及時(shí)讀取。

前文已經(jīng)說(shuō)過(guò),"include/uapi/linux/input.h"中的宏是應(yīng)用層和驅(qū)動(dòng)層共用的通信協(xié)議,所以應(yīng)用層在解析收到的struct input_value對(duì)象的時(shí)候,只需要"include <linux/input.h>"即可使用其中的宏。

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

input分析

上文已經(jīng)說(shuō)過(guò),input子系統(tǒng)使用三層結(jié)構(gòu)來(lái)實(shí)現(xiàn)驅(qū)動(dòng)事件到應(yīng)用層的傳遞。具體的,這三個(gè)層次每一個(gè)層次都由一條結(jié)構(gòu)體鏈表組成,在設(shè)備驅(qū)動(dòng)層,核心結(jié)構(gòu)體是input_dev;在input核心層,是input_handle;在事件處理層,是input_handler。內(nèi)核通過(guò)鏈表和指針將三者結(jié)合到一起,最終實(shí)現(xiàn)了input_dev和input_handler的多對(duì)多的映射關(guān)系,這種關(guān)系可用下圖簡(jiǎn)單描述。

Linux input怎么使用

模板

下面的這個(gè)模板首先使用input子系統(tǒng)上報(bào)按鍵事件,然后在應(yīng)用層讀取。

input按鍵設(shè)備驅(qū)動(dòng)

{            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");

應(yīng)用層獲取鍵值

#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?。?!,驅(qū)動(dòng)上傳了2個(gè)事件,第三個(gè)用來(lái)裝空元素      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?。?!,***一個(gè)是空         sleep(1);     }     return 0; }

到此,相信大家對(duì)“Linux input怎么使用”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI