溫馨提示×

溫馨提示×

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

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

Linux設(shè)備模型之input子系統(tǒng)的示例分析

發(fā)布時間:2021-07-14 14:11:00 來源:億速云 閱讀:198 作者:小新 欄目:系統(tǒng)運維

這篇文章主要介紹Linux設(shè)備模型之input子系統(tǒng)的示例分析,文中介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們一定要看完!

本節(jié)重點:

  •          輸入子系統(tǒng)的框架結(jié)構(gòu)

  •          各層對應(yīng)內(nèi)核中的文件位置

  •          輸入子系統(tǒng)的事件處理機(jī)制

  •          輸入子系統(tǒng)的驅(qū)動層基本操作流程

  •          輸入子系統(tǒng)的驅(qū)動層常用函數(shù)

本節(jié)難點:

輸入子系統(tǒng)的事件處理機(jī)制

輸入子系統(tǒng)的驅(qū)動工作流程

1 初識linux輸入子系統(tǒng)

linux輸入子系統(tǒng)(linux input subsystem)從上到下由三層實現(xiàn),分別為:輸入子系統(tǒng)事件處理層(EventHandler)、輸入子系統(tǒng)核心層(InputCore)和輸入子系統(tǒng)設(shè)備驅(qū)動層。

對于輸入子系統(tǒng)設(shè)備驅(qū)動層而言,主要實現(xiàn)對硬件設(shè)備的讀寫訪問,中斷設(shè)置,并把硬件產(chǎn)生的事件轉(zhuǎn)換為核心層定義的規(guī)范提交給事件處理層。

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

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

對于linux輸入子系統(tǒng)的框架結(jié)構(gòu)如下圖1所示:

Linux設(shè)備模型之input子系統(tǒng)的示例分析

圖1 linux輸入子系統(tǒng)框架結(jié)構(gòu)

由上圖所展現(xiàn)的內(nèi)容就是linux輸入子系統(tǒng)的分層結(jié)構(gòu)。

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

事件處理層為不同硬件類型提供了用戶訪問及處理接口。例如當(dāng)我們打開設(shè)備/dev/input/mice時,會調(diào)用到事件處理層的Mouse Handler來處理輸入事件,這也使得設(shè)備驅(qū)動層無需關(guān)心設(shè)備文件的操作,因為Mouse Handler已經(jīng)有了對應(yīng)事件處理的方法。

輸入子系統(tǒng)由內(nèi)核代碼drivers/input/input.c構(gòu)成,它的存在屏蔽了用戶到設(shè)備驅(qū)動的交互細(xì)節(jié),為設(shè)備驅(qū)動層和事件處理層提供了相互通信的統(tǒng)一界面。

下圖2簡單描述了linux輸入子系統(tǒng)的事件處理機(jī)制:

Linux設(shè)備模型之input子系統(tǒng)的示例分析

圖2 linux輸入子系統(tǒng)事件處理機(jī)制

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

作為輸入設(shè)備的驅(qū)動開發(fā)者,需要做以下幾步:

1、在驅(qū)動加載模塊中,設(shè)置你的input設(shè)備支持的事件類型,類型參見表1設(shè)置 

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

3、 將輸入設(shè)備注冊到輸入子系統(tǒng)中

表1 Linux輸入子系統(tǒng)支持的數(shù)據(jù)類型

EV_SYN     0x00    同步事件
EV_KEY     0x01    按鍵事件
EV_REL     0x02    相對坐標(biāo)(如:鼠標(biāo)移動,報告相對最后一次位置的偏移)
EV_ABS     0x03    絕對坐標(biāo)(如:觸摸屏或操作桿,報告絕對的坐標(biāo)位置)
EV_MSC     0x04    其它
EV_SW      0x05    開關(guān)
EV_LED     0x11    按鍵/設(shè)備燈
EV_SND     0x12    聲音/警報
EV_REP     0x14    重復(fù)
EV_FF      0x15    力反饋
EV_PWR    0x16    電源
EV_FF_STATUS    0x17   力反饋狀態(tài)
EV_MAX    0x1f    事件類型最大個數(shù)和提供位掩碼支持

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

Linux輸入子系統(tǒng)提供了設(shè)備驅(qū)動層上報輸入事件的函數(shù),在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);       //上報相對坐標(biāo)事件
voidinput_report_abs(struct input_dev *dev, unsigned int code, int value);              //上報絕對坐標(biāo)事件

當(dāng)提交輸入設(shè)備產(chǎn)生的輸入事件之后,需要調(diào)用下面的函數(shù)來通知輸入子系統(tǒng),以處理設(shè)備產(chǎn)生的完整事件:

void input_sync(struct input_dev *dev);

2 輸入設(shè)備驅(qū)動的簡單案例

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

提供的案例代碼描述了一個button設(shè)備,產(chǎn)生的事件通過BUTTON_PORT引腳獲取,當(dāng)有按下/釋放發(fā)生時,BUTTON_IRQ被觸發(fā),以下是驅(qū)動的源代碼:

#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);

編寫基于輸入子系統(tǒng)的設(shè)備驅(qū)動程序需要包含,因為它包含了輸入子系統(tǒng)的接口和所有的宏定義,這些內(nèi)容在編寫輸入設(shè)備驅(qū)動程序時需要用到。

button_init函數(shù)說明:

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

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

static struct input_dev *button_dev;

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

button_dev= input_allocate_device();

有了輸入設(shè)備的描述,當(dāng)事件產(chǎn)生時,輸入子系統(tǒng)怎么能夠知道設(shè)備產(chǎn)生的事件類型呢?通過32和33行的代碼。

button_dev->evbit[0]= BIT(EV_KEY);  
button_dev->keybit[LONG(BTN_0)]= BIT(BTN_0);

其中evbit和keybit成員分別代表設(shè)備產(chǎn)生的事件類型和上報的按鍵值。其中輸入子系統(tǒng)的一些位操作NBITS、BIT、LONG經(jīng)常被用到:

#defineNBITS(x) (((x)/BITS_PER_LONG)+1)                 //通過位x獲取數(shù)組的長度  
#defineBIT(x)       (1UL<<((x)%BITS_PER_LONG))       //返回位x在數(shù)組中的位域  
#defineLONG(x) ((x)/BITS_PER_LONG)                        //返回位x的索引

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

input_register_device(button_dev);

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

當(dāng)我們把上面的工作做完之后,設(shè)備驅(qū)動中唯一值得關(guān)注的就是button_interrupt()中斷處理函數(shù)了。當(dāng)按鍵動作發(fā)生,button_interrupt()函數(shù)被調(diào)用,完成事件的上報由其中的兩條語句完成。

input_report_key(button_dev, BTN_1, inb(BUTTON_PORT) & 1);  
input_sync(button_dev);

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

以上是“Linux設(shè)備模型之input子系統(tǒng)的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

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

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

AI