一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

linux input輸入子系統(tǒng)分析《一》:初識(shí)input輸入子系統(tǒng)

 云將東游 2016-03-15

 轉(zhuǎn)自:http://blog.csdn.net/ielife/article/details/7798952

主要講述本人在學(xué)習(xí)Linux內(nèi)核input子系統(tǒng)的全部過(guò)程,如有分析不當(dāng),多謝指正。以下交流方式,文章歡迎轉(zhuǎn)載,保留聯(lián)系信息,以便交流。

郵箱:eabi010@gmail.com

主頁(yè):www.(愛(ài)嵌論壇——嵌入式技術(shù)學(xué)習(xí)交流)

博客:blog.csdn.net/ielife

1      開發(fā)環(huán)境

主  機(jī):ubuntu10.04

開發(fā)板:mini2440

內(nèi)  核:linux-2.6.22.6

編譯器:arm-linux-gcc(3.4.5)

2      linux輸入子系統(tǒng)

本節(jié)從整體上講解了輸入子系統(tǒng)的框架結(jié)構(gòu)。有助于讀者從整體上認(rèn)識(shí)linux的輸入子系統(tǒng)。在陷入代碼分析的過(guò)程中,通過(guò)本節(jié)的知識(shí)能夠找準(zhǔn)方向,明白原理。

本節(jié)重點(diǎn):

  •          輸入子系統(tǒng)的框架結(jié)構(gòu)
  •          各層對(duì)應(yīng)內(nèi)核中的文件位置
  •          輸入子系統(tǒng)的事件處理機(jī)制
  •          輸入子系統(tǒng)的驅(qū)動(dòng)層基本操作流程
  •          輸入子系統(tǒng)的驅(qū)動(dòng)層常用函數(shù)

本節(jié)難點(diǎn):

  •          輸入子系統(tǒng)的事件處理機(jī)制
  •          輸入子系統(tǒng)的驅(qū)動(dòng)工作流程

2.1    初識(shí)linux輸入子系統(tǒng)

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

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

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

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

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


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

 

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

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

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

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

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


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

 

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

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

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

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

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

 

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

EV_SYN     0x00    同步事件

EV_KEY     0x01    按鍵事件

EV_REL     0x02    相對(duì)坐標(biāo)(如:鼠標(biāo)移動(dòng),報(bào)告相對(duì)最后一次位置的偏移)

EV_ABS     0x03    絕對(duì)坐標(biāo)(如:觸摸屏或操作桿,報(bào)告絕對(duì)的坐標(biāo)位置)

EV_MSC     0x04    其它

EV_SW      0x05    開關(guān)

EV_LED     0x11    按鍵/設(shè)備燈

EV_SND     0x12    聲音/警報(bào)

EV_REP     0x14    重復(fù)

EV_FF      0x15    力反饋

EV_PWR    0x16    電源

EV_FF_STATUS    0x17   力反饋狀態(tài)

EV_MAX    0x1f    事件類型最大個(gè)數(shù)和提供位掩碼支持

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

Linux輸入子系統(tǒng)提供了設(shè)備驅(qū)動(dòng)層上報(bào)輸入事件的函數(shù),在include/linux/input.h中:

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

voidinput_report_rel(struct input_dev *dev, unsigned int code, int value);       //上報(bào)相對(duì)坐標(biāo)事件

voidinput_report_abs(struct input_dev *dev, unsigned int code, int value);              //上報(bào)絕對(duì)坐標(biāo)事件

……

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

  1. void input_sync(struct input_dev *dev);  

2.2    輸入設(shè)備驅(qū)動(dòng)的簡(jiǎn)單案例

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

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

  1. #include <linux/input.h>                                                                                                          
  2.  #include <linux/module.h>  
  3.  #include <linux/init.h>  
  4.   
  5.  #include <asm/irq.h>  
  6.  #include <asm/io.h>  
  7.   
  8.  static struct input_dev *button_dev;  
  9.   
  10.  static void button_interrupt(int irq, void*dummy, struct pt_regs *fp)  
  11.  {  
  12.         input_report_key(button_dev, BTN_1, inb(BUTTON_PORT) & 1);  
  13.         input_sync(button_dev);  
  14.  }        
  15.   
  16.  static int __init button_init(void)  
  17.  {  
  18.         int error;  
  19.           
  20.         if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button",NULL)) {  
  21.                  printk(KERN_ERR"button.c: Can't allocate irq %d\n", button_irq);  
  22.                  return -EBUSY;  
  23.         }        
  24.           
  25.          button_dev = input_allocate_device();  
  26.         if (!button_dev) {  
  27.                  printk(KERN_ERR"button.c: Not enough memory\n");  
  28.                  error = -ENOMEM;  
  29.                  goto err_free_irq;  
  30.         }  
  31.   
  32.         button_dev->evbit[0] = BIT(EV_KEY);  
  33.         button_dev->keybit[LONG(BTN_0)] = BIT(BTN_0);  
  34.   
  35.         error = input_register_device(button_dev);  
  36.         if (error) {  
  37.                  printk(KERN_ERR"button.c: Failed to register device\n");  
  38.                  goto err_free_dev;  
  39.         }  
  40.   
  41.         return 0;  
  42.   
  43.  err_free_dev:  
  44.         input_free_device(button_dev);  
  45.  err_free_irq:  
  46.         free_irq(BUTTON_IRQ, button_interrupt);  
  47.         return error;  
  48.  }  
  49.   
  50.  static void __exit button_exit(void)  
  51.  {  
  52.        input_unregister_device(button_dev);  
  53.         free_irq(BUTTON_IRQ, button_interrupt);  
  54. }  
  55.   
  56. module_init(button_init);  
  57. module_exit(button_exit);  

編寫基于輸入子系統(tǒng)的設(shè)備驅(qū)動(dòng)程序需要包含<linux/input.h>,因?yàn)樗溯斎胱酉到y(tǒng)的接口和所有的宏定義,這些內(nèi)容在編寫輸入設(shè)備驅(qū)動(dòng)程序時(shí)需要用到。

button_init函數(shù)說(shuō)明:

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

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

  1. static struct input_dev *button_dev;  

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

  1. button_dev= input_allocate_device();  

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

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

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

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

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

  1. input_register_device(button_dev);  

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

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

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

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

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    久久精品国产99精品最新| 色婷婷在线视频免费播放| 亚洲国产香蕉视频在线观看| 日本女优一区二区三区免费| 自拍偷女厕所拍偷区亚洲综合| 肥白女人日韩中文视频| 一区二区福利在线视频| 精品精品国产欧美在线| 偷拍偷窥女厕一区二区视频| 久久三级国外久久久三级| 视频一区中文字幕日韩| 久久精品久久久精品久久| 手机在线观看亚洲中文字幕| 女厕偷窥一区二区三区在线| 亚洲综合一区二区三区在线| 国产亚洲欧美另类久久久| 91午夜少妇极品福利| 亚洲高清亚洲欧美一区二区| 欧美中文字幕一区在线| 亚洲精品偷拍视频免费观看| 国产性情片一区二区三区| 国产又色又粗又黄又爽| 黄男女激情一区二区三区| 国产一级内片内射免费看| 欧美日韩一级aa大片| 福利新区一区二区人口| 欧美日韩亚洲国产av| 国产精品一区二区三区激情| 久久99青青精品免费观看| 国产色第一区不卡高清| 免费在线观看欧美喷水黄片| 亚洲中文字幕视频在线播放| 欧美日韩精品一区二区三区不卡| 久久精品亚洲精品国产欧美| 中文字幕亚洲精品乱码加勒比| 国产白丝粉嫩av在线免费观看| 久热99中文字幕视频在线| 色丁香一区二区黑人巨大| 欧美成人高清在线播放| 久久国产亚洲精品成人| 麻豆果冻传媒一二三区|