[OK210開(kāi)發(fā)板體驗(yàn)]功能篇(3)Linux input子系統(tǒng)之Key按鍵驅(qū)動(dòng)

原創(chuàng) 2015-12-24 10:00:00 [OK210開(kāi)發(fā)板體驗(yàn)]功能篇(3)Linux input子系統(tǒng)之Key按鍵驅(qū)動(dòng)
前面進(jìn)行了OK210開(kāi)發(fā)板體驗(yàn)的入門(mén)篇介紹,算是初步入門(mén),分別包含:

[OK210開(kāi)發(fā)板體驗(yàn)]入門(mén)篇(1):開(kāi)箱驗(yàn)板
[OK210開(kāi)發(fā)板體驗(yàn)]入門(mén)篇(2):板載資源
[OK210開(kāi)發(fā)板體驗(yàn)]入門(mén)篇(3):開(kāi)發(fā)環(huán)境(軟件安裝,開(kāi)發(fā)環(huán)境,燒寫(xiě)系統(tǒng))
[OK210開(kāi)發(fā)板體驗(yàn)]入門(mén)篇(4):編程入門(mén)(NFS登錄,驅(qū)動(dòng)入門(mén)) 
[OK210開(kāi)發(fā)板體驗(yàn)]功能篇(1):字符驅(qū)動(dòng)之Led
[OK210開(kāi)發(fā)板體驗(yàn)]功能篇(2):字符驅(qū)動(dòng)之Key按鍵
前一篇介紹了字符驅(qū)動(dòng)之Key按鍵的控制,即用字符設(shè)備驅(qū)動(dòng)的方式實(shí)現(xiàn)了按鍵驅(qū)動(dòng),但是,這個(gè)驅(qū)動(dòng)程序只能供自己編寫(xiě)的應(yīng)用程序使用,其他應(yīng)用程序都無(wú)法接收這個(gè)驅(qū)動(dòng)的鍵值輸入,為了讓所有應(yīng)用程序都可以接收到按鍵驅(qū)動(dòng)解析的鍵值,Linux內(nèi)核定義了“輸入子系統(tǒng)”的概念,也就是說(shuō),只要我們按照這個(gè)模型進(jìn)行驅(qū)動(dòng)開(kāi)發(fā),并為其提供必須的接口函數(shù),那么,Linux內(nèi)核就可以正常來(lái)獲取我們的鍵盤(pán)值。輸入子系統(tǒng)的原理分析強(qiáng)烈推薦觀看韋東山老師的視頻講座,講的非常清楚。
今天是功能篇的第三篇:input子系統(tǒng)之Key按鍵,由于本節(jié)的硬件和上節(jié)的一節(jié),可以參見(jiàn)上一節(jié)的分析,本節(jié)主要分2部分:軟件基礎(chǔ),驅(qū)動(dòng)編程。
一、軟件基礎(chǔ)
1.1.input子系統(tǒng)概述
 輸入設(shè)備(如按鍵,鍵盤(pán),觸摸屏,鼠標(biāo)等)是典型的字符設(shè)備,其一般的工作機(jī)制是低層在按鍵,觸摸等動(dòng)作發(fā)生時(shí)產(chǎn)生一個(gè)中斷(或驅(qū)動(dòng)通過(guò)timer定時(shí)查詢(xún)),然后cpu通過(guò)SPI,I2C或者外部存儲(chǔ)器總線讀取鍵值,坐標(biāo)等數(shù)據(jù),放一個(gè)緩沖區(qū),字符設(shè)備驅(qū)動(dòng)管理該緩沖區(qū),而驅(qū)動(dòng)的read()接口讓用戶(hù)可以讀取鍵值,坐標(biāo)等數(shù)據(jù)。
 Linux中,輸入子系統(tǒng)是由輸入子系統(tǒng)設(shè)備驅(qū)動(dòng)層、輸入子系統(tǒng)核心層(Input Core)和輸入子系統(tǒng)事件處理層(Event Handler)組成。其中設(shè)備驅(qū)動(dòng)層提供對(duì)硬件各寄存器的讀寫(xiě)訪問(wèn)和將底層硬件對(duì)用戶(hù)輸入訪問(wèn)的響應(yīng)轉(zhuǎn)換為標(biāo)準(zhǔn)的輸入事件,再通過(guò)核心層提交給事件處理層;而核心層對(duì)下提供了設(shè)備驅(qū)動(dòng)層的編程接口,對(duì)上又提供了事件處理層的編程接口;而事件處理層就為我們用戶(hù)空間的應(yīng)用程序提供了統(tǒng)一訪問(wèn)設(shè)備的接口和驅(qū)動(dòng)層提交來(lái)的事件處理。所以這使得我們輸入設(shè)備的驅(qū)動(dòng)部分不在用關(guān)心對(duì)設(shè)備文件的操作,而是要關(guān)心對(duì)各硬件寄存器的操作和提交的輸入事件。                       
1.2. input子系統(tǒng)結(jié)構(gòu)圖
          


1.3.linux中輸入設(shè)備驅(qū)動(dòng)的分層

                           


1.4. 輸入子系統(tǒng)設(shè)備驅(qū)動(dòng)層實(shí)現(xiàn)原理

在Linux中,Input設(shè)備用input_dev結(jié)構(gòu)體描述,定義在input.h中。設(shè)備的驅(qū)動(dòng)只需按照如下步驟就可實(shí)現(xiàn)了。                
1).在驅(qū)動(dòng)模塊加載函數(shù)中設(shè)置Input設(shè)備支持input子系統(tǒng)的哪些事件;       
2).將Input設(shè)備注冊(cè)到input子系統(tǒng)中;                
3).在Input設(shè)備發(fā)生輸入操作時(shí)(如:鍵盤(pán)被按下/抬起、觸摸屏被觸摸/抬起/移動(dòng)、鼠標(biāo)被移動(dòng)/單擊/抬起時(shí)等),提交所發(fā)生的事件及對(duì)應(yīng)的鍵值/坐標(biāo)等狀態(tài)。
1.5.軟件設(shè)計(jì)流程                                  

            


1.6.與軟件設(shè)計(jì)有關(guān)的API函數(shù) 
1.6.1.分配一個(gè)輸入設(shè)備

struct input_dev *input_allocate_device*(void);                
1.6.2.注冊(cè)一個(gè)輸入設(shè)備
int input_register_device(struct input_dev *dev);
1.6.3.驅(qū)動(dòng)實(shí)現(xiàn)-事件支持
set_bit告訴inout子系統(tǒng)它支持哪些事件                
set_bit(EV_KEY,button_dev.evbit)                
struct input_dev中有兩個(gè)成員,一個(gè)是evbit;一個(gè)是keybit.分別用來(lái)表示設(shè)備所支持的事件類(lèi)型和按鍵類(lèi)型。             
1.6.3.1事件類(lèi)型
Linux中輸入設(shè)備的事件類(lèi)型有(這里只列出了常用的一些,更多請(qǐng)看linux/input.h中):EV_SYN 0x00 同步事件                
EV_KEY 0x01 按鍵事件                
EV_REL 0x02 相對(duì)坐標(biāo)                 
EV_ABS 0x03 絕對(duì)坐標(biāo)                
EV_MSC 0x04 其它                
EV_LED 0x11 LED                
EV_SND 0x12 聲音                
EV_REP 0x14 Repeat                
EV_FF 0x15 力反饋          
1.6.4.驅(qū)動(dòng)實(shí)現(xiàn)-報(bào)告事件
void input_event(struct input_dev *dev,unsigned int type,unsigned int code,int value);//報(bào)告指定type,code的輸入事件

void input_report_key(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)告相對(duì)坐標(biāo)                
void input_report_abs(struct input_dev *dev,unsigned int code,int value);//報(bào)告絕對(duì)坐標(biāo)                
void input_sync(struct input_dev *dev);//報(bào)告同步事件               
在觸摸屏驅(qū)動(dòng)設(shè)計(jì)中,一次坐標(biāo)及按下?tīng)顟B(tài)的整個(gè)報(bào)告過(guò)程如下
input_report_abs(input_dev,ABS_Y,y);//Y坐標(biāo)                
input_report_abs(input_dev,ABS_PRESSURE,pres);//壓力                
input_sync(struct input_dev *dev);//同步                
1.6.5釋放與注銷(xiāo)設(shè)備
void input_free_device(struct input_dev *dev);                
void input_unregister_device(struct input_dev *);

二、驅(qū)動(dòng)編程
在編寫(xiě)驅(qū)動(dòng)的時(shí)候,首先要設(shè)置正確的硬件,為此首先定義了一個(gè)struct _ok210_key結(jié)構(gòu)體,用以表示單個(gè)按鍵的引腳號(hào),中斷號(hào)以及按鍵值;然后為這組按鍵(一共5個(gè))定義了一個(gè)結(jié)構(gòu)體struct ok210_kbd,以統(tǒng)一管理按鍵,最后使用ok210_init_kbd_data函數(shù),初始化各按鍵,這樣驅(qū)動(dòng)的編寫(xiě)已經(jīng)成功了一半。
在ok210_keys_probe函數(shù)中,通過(guò)調(diào)用__set_bit函數(shù),為各按鍵設(shè)置事件類(lèi)型和按鍵類(lèi)型,最后當(dāng)有按鍵按下時(shí),驅(qū)動(dòng)會(huì)調(diào)用ok210_kbd_handler回調(diào)函數(shù),并分別通過(guò)兩個(gè)函數(shù)(input_report_key和input_sync)將按鍵事件傳輸?shù)?a href="http://www.gxjmg.com/product/39.html" class="tag-link">應(yīng)用層。
值得一提的是,當(dāng)系統(tǒng)中input子系統(tǒng)多于一個(gè)時(shí),由于注冊(cè)的先后順序不一樣,每次生成在/dev/input目錄下生成的eventx指定的驅(qū)動(dòng)設(shè)備也不一樣,不過(guò)可以通過(guò)如下命令,來(lái)查看具體的eventx指代的設(shè)備。
cat /proc/bus/input/devices



1 驅(qū)動(dòng)程序

1.     
#include <linux/module.h>

2.     #include <linux/init.h>

3.     #include <linux/interrupt.h>

4.     #include <linux/platform_device.h>

5.     #include <linux/input.h>

6.     #include <linux/slab.h>

7.     #include <mach/regs-gpio.h>

8.     #include <linux/gpio.h>

9.     #include <linux/irq.h>

10.   

11.  #define OK210_KBD "ok210kbd"

12.  #define OK210_KBD_NUM (5)

13.  #define KBD_NONE (0xff)

14.   

15.  #define KBD_UP (0)

16.  #define KBD_DOWN (1)

17.   

18.  typedef struct _ok210_key{

19.          unsigned int gpio;/*對(duì)應(yīng)gpio*/

20.          unsigned int irq;/*對(duì)應(yīng)中斷*/

21.          int n_key;/*鍵值*/

22.  }ok210_key;

23.   

24.  struct ok210_kbd{

25.          ok210_key keys[OK210_KBD_NUM];

26.          struct timer_list key_timer; /*按鍵去抖定時(shí)器*/

27.          unsigned int key_status; /*按鍵狀態(tài)*/

28.          struct input_dev *input;

29.  };

30.   

31.  static void ok210_init_kbd_data(struct ok210_kbd *p_kbd)

32.  {

33.          printk("ok210_init_kbd_data p_kbd=%x\n", (unsigned int)p_kbd);

34.   

35.          p_kbd->keys[0].gpio = S5PV210_GPH0(3);

36.          p_kbd->keys[1].gpio = S5PV210_GPH0(4);

37.          p_kbd->keys[2].gpio = S5PV210_GPH0(5);

38.          p_kbd->keys[3].gpio = S5PV210_GPH0(6);

39.          p_kbd->keys[4].gpio = S5PV210_GPH0(7);

40.   

41.          p_kbd->keys[0].irq = IRQ_EINT3;

42.          p_kbd->keys[1].irq = IRQ_EINT4;

43.          p_kbd->keys[2].irq = IRQ_EINT5;

44.          p_kbd->keys[3].irq = IRQ_EINT6;

45.          p_kbd->keys[4].irq = IRQ_EINT7;

46.   

47.          p_kbd->keys[0].n_key = KEY_0;

48.          p_kbd->keys[1].n_key = KEY_1;

49.          p_kbd->keys[2].n_key = KEY_2;

50.          p_kbd->keys[3].n_key = KEY_3;

51.          p_kbd->keys[4].n_key = KEY_4;

52.   

53.  }

54.   

55.  struct ok210_kbd *p_ok210_kbd;

56.   

57.  struct ok210_kbd *get_kbd(void)

58.  {

59.          return p_ok210_kbd;

60.  }

61.   

62.  void set_kbd(struct ok210_kbd *p_kbd)

63.  {

64.          p_ok210_kbd = p_kbd;

65.  }

66.   

67.  static irqreturn_t ok210_kbd_handler(int irq, void *p_date)

68.  {

69.          unsigned int n_key = 0;

70.          struct ok210_kbd *p_kbd = p_date;

71.          unsigned int key_state = 0;

72.          int i;

73.   

74.          for(i = 0; i < OK210_KBD_NUM; i++)

75.          {

76.                  if( irq == p_kbd->keys[i].irq )

77.                  {

78.                          key_state = gpio_get_value(p_kbd->keys[i].gpio);

79.                          n_key = p_kbd->keys[i].n_key;

80.                          break;

81.                  }

82.          }

83.   

84.          printk("ok210_kbd_handler n_key=%d, key_state=%d\n", n_key, key_state);

85.   

86.          input_report_key(p_kbd->input, n_key, !key_state);/*1表示按下*/

87.          input_sync(p_kbd->input);

88.   

89.          return IRQ_HANDLED;

90.  }

91.   

92.   

93.  static void kbd_free_irqs(void)

94.  {

95.          int i;

96.          struct ok210_kbd *p_kbd = get_kbd();

97.   

98.          for(i = 0; i < OK210_KBD_NUM; i++)

99.                  free_irq(p_kbd->keys[i].irq, p_kbd);

100.              , , }

101.               

102.              static int kbd_req_irqs(void)

103.              {

104.                      int n_ret;

105.                      int i;

106.                      struct ok210_kbd *p_kbd = get_kbd();

107.               

108.                      for(i = 0; i < OK210_KBD_NUM; i++)

109.                      {

110.                              n_ret = request_irq(p_kbd->keys[i].irq, ok210_kbd_handler, IRQ_TYPE_EDGE_BOTH, OK210_KBD, p_kbd);

111.                              if(n_ret)

112.                              {

113.                                      printk("%d: could not register interrupt\n", p_kbd->keys[i].irq);

114.                                      goto fail;

115.                              }

116.                      }

117.               

118.                      return n_ret;

119.               

120.              fail:

121.                      /*因?yàn)樯厦嫔暾?qǐng)失敗的那個(gè)沒(méi)有成功,所以也不要釋放*/

122.                      for(i--; i >= 0; i--)

123.                      {

124.                              disable_irq(p_kbd->keys[i].irq);

125.                              free_irq(p_kbd->keys[i].irq, p_kbd);

126.                      }

127.               

128.                      return n_ret;

129.              }

130.               

131.               

132.              static int __devinit ok210_keys_probe(struct platform_device *pdev)

133.              {

134.                      int err = -ENOMEM;

135.                      struct ok210_kbd *p_ok210_keys = NULL;

136.                      struct input_dev *input_dev = NULL;

137.               

138.                      printk("ok210_keys_probe entry!\n");

相關(guān)產(chǎn)品 >

  • OKMX6UL-C開(kāi)發(fā)板

    飛凌嵌入式專(zhuān)注imx6系列imx6ul開(kāi)發(fā)板、飛思卡爾imx6ul核心板等ARM嵌入式核心控制系統(tǒng)研發(fā)、設(shè)計(jì)和生產(chǎn),i.mx6UL系列產(chǎn)品現(xiàn)已暢銷(xiāo)全國(guó),作為恩智浦imx6ul,imx6ul開(kāi)發(fā)板,i.mx6提供者,飛凌嵌入式提供基于iMX6 iMX6UL解決方案定制。

    了解詳情
    OKMX6UL-C開(kāi)發(fā)板
  • OKMX6ULL-C開(kāi)發(fā)板

    40*29mm,雙網(wǎng)雙CAN,8路串口| i.MX6ULL開(kāi)發(fā)板是基于NXP i.MX6ULL設(shè)計(jì)開(kāi)發(fā)的的一款Linux開(kāi)發(fā)板 ,主頻800MHz,體積小,其核心板僅40*29mm,采用板對(duì)板連接器,適應(yīng)場(chǎng)景豐富。 了解詳情
    OKMX6ULL-C開(kāi)發(fā)板

推薦閱讀 換一批 換一批