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

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

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


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

                           


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

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

            


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

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

void input_report_key(struct input_dev *dev,unsigned int code,int value);//報告鍵值                
void input_report_rel(struct input_dev *dev,unsigned int code,int value);//報告相對坐標(biāo)                
void input_report_abs(struct input_dev *dev,unsigned int code,int value);//報告絕對坐標(biāo)                
void input_sync(struct input_dev *dev);//報告同步事件               
在觸摸屏驅(qū)動設(shè)計中,一次坐標(biāo)及按下狀態(tài)的整個報告過程如下
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釋放與注銷設(shè)備
void input_free_device(struct input_dev *dev);                
void input_unregister_device(struct input_dev *);

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



1 驅(qū)動程序

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;/*對應(yīng)gpio*/

20.          unsigned int irq;/*對應(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; /*按鍵去抖定時器*/

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.                      /*因為上面申請失敗的那個沒有成功,所以也不要釋放*/

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開發(fā)板

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

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

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

推薦閱讀 換一批 換一批