驅動03.輸入子系統

来源:http://www.cnblogs.com/Lwd-linux/archive/2017/01/09/6263458.html
-Advertisement-
Play Games

之前我們做的按鍵驅動程式都是應用程式主動open設備/dev/buttons而現實情況不能來打開這個設備甚至不知道這個設備的存在。 解決方案:變成通用的驅動程式。接下來我們引入的輸入子系統可以完成該任務。 1.輸入子系統的簡介 1.1 引入輸入子系統的好處: (1)統一了物理形態各異的相似的輸入設備 ...


之前我們做的按鍵驅動程式都是應用程式主動open設備/dev/buttons而現實情況不能來打開這個設備甚至不知道這個設備的存在。

解決方案:變成通用的驅動程式。接下來我們引入的輸入子系統可以完成該任務。

1.輸入子系統的簡介

1.1 引入輸入子系統的好處:

(1)統一了物理形態各異的相似的輸入設備的處理功能。例如,各種滑鼠,不論PS/2、USB、還是藍牙,都被同樣處理。

(2)提供了用於分發輸入報告給用戶應用程式的簡單的事件(event)介面。你的驅動不必創建、管理/dev節點以及相關的訪問方法。因此它能夠很方便的調用輸入API以發送滑鼠移動、鍵盤按鍵,或觸摸事件給用戶空間。X windows這樣的應用程式能夠無縫地運行於輸入子系統提供的event介面之上。

(3)抽取出了輸入驅動的通用部分,簡化了驅動,並提供了一致性。例如,輸入子系統提供了一個底層驅動(成為serio)的集合,支持對串口和鍵盤控制器等硬體輸入的訪問。

1.2 分析輸入子系統實現的原理

  linux系統將輸入子系統分為三層結構,主要是input driver、input core、Input handler。在網上搜刮到的兩幅經典的圖片,有助於我們理解輸入子系統。

 

 

 

2 代碼分析

(1)/drivers/input/input.c
    input_init > err = register_chrdev(INPUT_MAJOR, "input", &input_fops)//註冊一個input_fops結構體

  static const struct file_operations input_fops = {
    .owner = THIS_MODULE,
    .open = input_open_file,
  };//結構體內只有一個open函數

(2)怎麼去讀取按鍵?

  上述的file_operations結構體內沒有read函數,我們需進一步探究。

  input_open_file
       struct input_handler *handler = input_table[iminor(inode) >> 5]
       new_fops = fops_get(handler->fops)                    //這個就是我們實際使用的file_operations結構體
       err = new_fops->open(inode, file)

  input_table這個數組由誰去構造?

  input_table[handler->minor >> 5] = handler

(3)註冊函數input_register_device和input_register_handler,向input.c註冊

  input_register_device
      list_add_tail(&dev->node, &input_dev_list)    //放入鏈表
      list_for_each_entry(handler, &input_handler_list, node)//對於每一個input_handler,都調用input_attach_handler
          input_attach_handler(dev, handler)//根據input_handler的id_table判斷是否支持這個input_dev

 

  input_register_handler
      input_table[handler->minor >> 5] = handler   //放入數組
      list_add_tail(&handler->node, &input_handler_list) //放入鏈表
      list_for_each_entry(dev, &input_dev_list, node)//對於每一個input_dev,都調用input_attach_handler
          input_attach_handler(dev, handler)//根據input_handler的id_table判斷是否支持這個input_dev

 

  input_attach_handler

   id = input_match_device(handler->id_table, dev);//如果dev和id匹配,則調用connect函數建立連接
     error = handler->connect(handler, dev, id);

(4)怎麼建立連接?怎麼讀按鍵?

以evdev.c為例

  evdev_connect

        evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);//分配一個結構體input_handle結構體


        evdev->handle.dev = dev;
        evdev->handle.name = evdev->name;
        evdev->handle.handler = handler;
        evdev->handle.private = evdev;//設置input_handle結構體handle的值

    

      error = input_register_handle(&evdev->handle);//註冊handle結構體
            input_handler->h_list = &input_handle

       input_dev    -> h_list = &input_handle

  evdev_read
      //無數據且非阻塞方式打開,則立刻返回
      if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK))
                    return -EAGAIN;
      wait_event_interruptible//休眠

誰來喚醒他?
  evdev_event
      wake_up_interruptible(&evdev->wait)
    
那evdev_event誰來調用?
  gpio_keys_isr
      input_event(input, type, button->code, !!state)
      input_sync(input)
    
input_event
    struct input_handle *handle
    list_for_each_entry(handle, &dev->h_list, d_node)
            if (handle->open)
                handle->handler->event(handle, type, code, value)//調用evdev_event

 

3.寫代碼

3.1 代碼框架

我們只需要完成input_dev那部分,其他的內核已經為我們寫好了。

(1)分配一個input_dev結構體

(2)設置

(3)註冊

(4)硬體相關的操作,eg:在中斷程式中上報事件,參考gpio-keys.c

3.2 驅動源代碼

  1 說明:輸入子系統(input subsystem)的驅動層的核心結構。  
  2 
  3 頭文件:include/linux/input.h
  4 
  5 成員說明:
  6 
  7 void *private;
  8 
  9        //不清楚。
 10 
 11 char *name;
 12 
 13        //設備名字,如鍵盤名字。
 14 
 15 char *phys;
 16 
 17        //設備文件節點名,如input/kbd0。
 18 
 19 char *uniq;
 20 
 21        //全球唯一的ID號。
 22 
 23 struct input_id id;
 24 
 25        //後文作詳細介紹。
 26 
 27 unsigned long evbit[NBITS(EV_MAX);]
 28 
 29        //該設備驅動所能支持的事件。
 30 
 31        //EV_SYN      同步事件
 32 
 33        //EV_KEY       鍵盤事件
 34 
 35        //EV_REL       相對坐標事件,用於滑鼠
 36 
 37        //EV_ABS       絕對坐標事件,用於搖桿
 38 
 39        //EV_MSC      其他事件
 40 
 41        //EV_LED       LED燈事件
 42 
 43        //EV_SND      聲音事件
 44 
 45        //EV_REP       重覆按鍵事件
 46 
 47        //EV_FF         受力事件
 48 
 49        //EV_PWR      電源事件
 50 
 51        //EV_FF_STATUS  受力狀態事件
 52 
 53 unsigned long keybit[NBITS(KEY_MAX)];
 54 
 55        //鍵值存放表
 56 
 57 unsigned long relbit[NBITS(REL_MAX)];
 58 
 59        //用於存放相對坐標值等
 60 
 61 unsigned long absbit[NBITS(ABS_MAX)];
 62 
 63        //用於存放絕對坐標值等
 64 
 65 unsigned long mscbit[NBITS(MSC_MAX)];
 66 
 67        //存放其他事件類型
 68 
 69 unsigned long ledbit[NBITS(LED_MAX)];
 70 
 71        //存放表示各種狀態的LED值
 72 
 73 unsigned long sndbit[NBITS(SND_MAX)];
 74 
 75        //存放各種事件的聲音
 76 
 77 unsigned long ffbit[NBITS(FF_MAX)];
 78 
 79        //存放受力設備的屬性
 80 
 81 int ff_effects_max;
 82 
 83        //顯然與受力效果有關,具體作用還不大清楚。
 84 
 85 unsigned int keycodemax;
 86 
 87 unsigned int keycodesize;
 88 
 89 void * keycode;
 90 
 91        //這三個不是很清楚,有點模糊理解。
 92 
 93 unsigned int repeat_key;
 94 
 95        //存放重覆按鍵時的鍵值
 96 
 97 struct timer_list timer;
 98 
 99        //定時器
100 
101 struct pm_dev *pm_dev;
102 
103        //考慮到有些設備可能有電源管理
104 
105 struct pt_regs *regs;
106 
107        //不清楚
108 
109 int state;
110 
111        //顯然是表示一個狀態,但不清楚具體是誰的狀態
112 
113 int sync;
114 
115        //具體用於什麼也不大清楚
116 
117 int abs[ABS_MAX + 1];
118 
119        //顯然是與絕對坐標有關的,但具體的作用不清楚。
120 
121 int rep[REP_MAX + 1];
122 
123        //存放重覆按鍵時的延時,系統依靠這個延時時間來判斷重覆按鍵
124 
125        //rep[0]表示開始要重覆按鍵時的延時時間,即第1個鍵與第2個鍵(開始重覆按鍵)之間的延時
126 
127        //rep[1]此後重覆按鍵之前的延時時間,直到按鍵抬起
128 
129        //通俗解釋就是,假如我按了一個“a”,並且一直按著,那麼在顯示出來的第一個a與第二個a之間的時間延時為rep[0],而此後的相鄰兩個a之間的延時為rep[1]
130 
131   
132 
133 unsigned long key[NBITS(KEY_MAX)];
134 
135 unsigned long led[NBITS(LED_MAX)];
136 
137 unsigned long snd[NBITS(SND_MAX)];
138 
139        //不知道有什麼用
140 
141 int absmax[ABS_MAX + 1];
142 
143 int absmin[ABS_MAX + 1];
144 
145 int absfuzz[ABS_MAX + 1];
146 
147 int absflat[ABS_MAX + 1];
148 
149        //顯然與絕對坐標值有關,但不知道具體作用
150 
151  
152 
153 int (*open)(struct input_dev *dev);
154 
155 void (*close)(struct input_dev *dev);
156 
157 int (*accept)(struct input_dev *dev, struct file *file);
158 
159 int (*flush)(struct input_dev *dev, struct file *file);
160 
161 int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
162 
163 int (*upload_effect)(struct input_dev *dev, struct ff_effect *effect);
164 
165 int (*erase_effect)(struct input_dev *dev, int effect_id);
166 
167        //底層與硬體相關的一組操作,若有具體定義,則會在input core層被調用,具體看input.c。
168 
169  
170 
171 struct input_handle *grab;
172 
173        //該結構會在後文做具體介紹,這個指針用於占用輸入設備用,如鍵盤
174 
175 struct list_head h_list;
176 
177 struct list_head node;
178 
179        //h_list鏈表用於與input_handler相聯繫
180 
181        //node鏈表:設備向輸入子系統(input subsystem)註冊後,會將該鏈表添加到系統維護的一個鏈表中去,從而系統可以管理這個設備
input_dev結構體
  1 /*
  2  *輸入子系統
  3  *參考gpio-keys.c
  4 */
  5 #include <linux/module.h>
  6 #include <linux/version.h>
  7 
  8 #include <linux/init.h>
  9 #include <linux/fs.h>
 10 #include <linux/interrupt.h>
 11 #include <linux/irq.h>
 12 #include <linux/sched.h>
 13 #include <linux/pm.h>
 14 #include <linux/sysctl.h>
 15 #include <linux/proc_fs.h>
 16 #include <linux/delay.h>
 17 #include <linux/platform_device.h>
 18 #include <linux/input.h>
 19 #include <linux/irq.h>
 20 
 21 
 22 #include <asm/gpio.h>
 23 #include <asm/io.h>
 24 #include <asm/arch/regs-gpio.h>
 25 
 26 
 27 struct pin_desc{
 28     int irq;
 29     char *name;
 30     unsigned int pin;
 31     unsigned int key_val;
 32 };
 33 
 34 struct pin_desc pins_desc[4] = {
 35     {IRQ_EINT0,  "S2", S3C2410_GPF0,   KEY_L},
 36     {IRQ_EINT2,  "S3", S3C2410_GPF2,   KEY_S},
 37     {IRQ_EINT11, "S4", S3C2410_GPG3,   KEY_ENTER},
 38     {IRQ_EINT19, "S5",  S3C2410_GPG11, KEY_LEFTSHIFT},
 39 };
 40 
 41 static struct input_dev *g_ptButtonsInput;
 42 static struct timer_list g_tButtonsTimer;
 43 static struct pin_desc *g_ptPindescTmp;
 44 
 45 
 46 static irqreturn_t buttons_irq(int irq, void *dev_id)
 47 {
 48     /* 10ms後啟動定時器 */
 49     g_ptPindescTmp = (struct pin_desc *)dev_id;
 50     mod_timer(&g_tButtonsTimer, jiffies+HZ/100);
 51     return IRQ_RETVAL(IRQ_HANDLED);
 52 }
 53 
 54 static void ButtonsTimerFunc(unsigned long data)
 55 {
 56     struct pin_desc * tPindesc = g_ptPindescTmp;
 57     unsigned int uiPinval;
 58 
 59     if(!tPindesc)
 60         return;
 61     
 62     uiPinval = s3c2410_gpio_getpin(tPindesc->pin);
 63 
 64     if (uiPinval)
 65     {
 66         /* 鬆開 */
 67         input_event(g_ptButtonsInput, EV_KEY, tPindesc->key_val, 0);
 68         input_sync(g_ptButtonsInput);
 69     }
 70     else
 71     {
 72         /* 按下 */
 73         input_event(g_ptButtonsInput, EV_KEY, tPindesc->key_val, 1);
 74         input_sync(g_ptButtonsInput);
 75 
 76     }
 77 }
 78 
 79 static int buttons_init(void)
 80 {
 81     int iError;
 82     int i;
 83     /*1.分配一個input_dev結構體*/
 84     g_ptButtonsInput = input_allocate_device();
 85     if (!g_ptButtonsInput)
 86         return -ENOMEM;
 87     /*2.設置*/
 88 
 89     /*2.1事件類型*/
 90     set_bit(EV_KEY, g_ptButtonsInput->evbit);
 91     set_bit(EV_REP, g_ptButtonsInput->evbit);
 92 
 93     
 94     /*2.2哪些事件*/
 95     set_bit(KEY_L, g_ptButtonsInput->keybit);
 96     set_bit(KEY_S, g_ptButtonsInput->keybit);
 97     set_bit(KEY_ENTER, g_ptButtonsInput->keybit);
 98     set_bit(KEY_LEFTSHIFT, g_ptButtonsInput->keybit);
 99 
100 
101     
102     /*3.註冊*/
103     iError = input_register_device(g_ptButtonsInput);
104     if (iError) {
105         printk("Unable to register buttons input device\n");
106     }
107 
108     /*4.硬體相關的操作*/
109     init_timer(&g_tButtonsTimer);
110     g_tButtonsTimer.function = &ButtonsTimerFunc;    /* timer handler */
111     add_timer(&g_tButtonsTimer);
112 
113     for(i = 0; i < 4; i++)
114     {
115         request_irq(pins_desc[i].irq,  buttons_irq, IRQT_BOTHEDGE, pins_desc[i].name, &pins_desc[i]);
116     }
117     return 0;
118 
119 }
120 
121 static void buttons_exit(void)
122 {
123     int i;
124 
125     for(i = 0;i<4;i++)
126     {
127         free_irq(pins_desc[i].irq, &pins_desc[i]);    
128     }
129 
130     del_timer(&g_tButtonsTimer);
131     input_unregister_device(g_ptButtonsInput);
132     input_free_device(g_ptButtonsInput);
133 }
134 
135 module_init(buttons_init);
136 module_exit(buttons_exit);
137 
138 MODULE_LICENSE("GPL");
buttons_input
//input驅動的測試方法
1.ls /dev/event* -l 查看現有的/dev/event*設備
2.insmod buttons_input.ko 安裝驅動
3.ls /dev/event* -l 查看buttons_input對應的設備
4.cat /dev/tty1,然後在按鍵,“l”“s”“ENTER”便會出現ls
5.如果啟動了QT,可以點開記事本,按相應的按鍵“l”“s”“ENTER”便會在記事本上出現ls
6.也可通過執行exec 0</dev/tty1   //標準輸入改為tty1,然後重覆上述操作即可。
測試方法

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 我們平時在做web開發運行web伺服器或運行某個應用時會報錯,提示該應用的埠號已被占用,我們可以用以下的方法解決。 解決方法一:重新為應用配置埠。 解決方法二:找到占用埠的應用並關閉該應用釋放占用的埠: 1、win+r運行cmd或在開菜單的運行中運行 2、運行命令 netstat -aon| ...
  • 系統window8.1 1、安裝IIS組件:點開始菜單—選擇控制面板——程式——打開或關閉WINDOWS功能——展開Internet信息服務,勾選FTP伺服器(包括FTP服務和FTP擴展性),點確定。 由於我的電腦已將安裝了IIS服務所以找不到了,不知道你電腦有沒有安裝可以在 控制面板\所有控制面板 ...
  • 運行級別 說明 0 系統關機狀態 1 單用戶工作狀態,用於root對系統進行維護,此時不予許其他用戶使用主機。(類似於windows 的安全模式) 2 多用戶狀態(沒有NFS) 3 多用戶狀態(有NFS),主機做為伺服器常在該模式下工作 4 系統未定義 5 多用戶狀態,並且在系統啟動後運行xwind ...
  • 我這裡要講的並不是IPC中的消息隊列,我要講的是在進程內部實現自定義的消息隊列,讓各個線程的消息來推動整個進程的運動。進程間的消息隊列用於進程與進程之間的通信,而我將要實現的進程內的消息隊列是用於有序妥當處理來自於各個線程請求,避免一窩蜂的請求而導致消息的異常丟失。想想socket編程里的liste ...
  • Linux系統下給非root用戶添加sudo許可權 有時,在linux系統中非root用戶運行sudo命令,會提示類似信息: xxx is not in the sudoers file. This incident will be reported. 這裡,xxx是當前用戶名,該用戶無法執行sudo ...
  • 鏈接: top:命令提供了實時的對系統處理器的狀態監視.它將顯示系統中CPU最“敏感”的任務列表. 該命令可以按CPU使用.記憶體使用和執行時間對任務進行排序; 而且該命令的很多特性都可以通過互動式命令或者在個人定製文件中進行設定. top - 01:06:48 up 1:22, 1 user, lo ...
  • 1 平臺匯流排的簡介 平臺匯流排是一種虛擬的匯流排,相應的設備則為platform_device,而驅動則為platform_driver。匯流排將設備和驅動綁定,在系統每註冊一個設備的時候,會尋找與之匹配的驅動;相反的,在系統每註冊一個驅動的時候,會尋找與之匹配的設備,而匹配由匯流排完成。 我們可以把一個驅 ...
  • 作者:楓雪庭 出處:http://www.cnblogs.com/FengXueTing-px/ 歡迎轉載 前言 雖然Emacs已經可以完成大部分的編輯操作,但有時候為了方便也會用到vim。所以記錄了vim的簡單操作,只要求到達上手即可。 本文簡單記錄了,vim編輯器模式之間的轉換和複製粘貼操作。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...