設備樹下的input子系統

input 就是輸入的意思,因此 input 子系統就是管理輸入的子系統,和 pinctrl 和 gpio 子系統一樣,都是 Linux 內核針對某一類設備而創建的框架。比如按鍵輸入、鍵盤、鼠標、觸摸屏等等這些都屬於輸入設備,不同的輸入設備所代表的含義不同,按鍵和鍵盤就是代表按鍵信息,鼠標和觸摸屏代表座標信息,因此在應用層的處理就不同,對於驅動編寫者而言不需要去關心應用層的事情,我們只需要按照要求上報這些輸入事件即可。爲此 input子系統分爲 input 驅動層、input 核心層、input 事件處理層,最終給用戶空間提供可訪問的設備節點,input 子系統框架如圖 58.1.1.1 所示:
在這裏插入圖片描述
圖 58.1.1.1 中左邊就是最底層的具體設備,比如按鍵、USB 鍵盤/鼠標等,中間部分屬於Linux 內核空間,分爲驅動層、核心層和事件層,最右邊的就是用戶空間,所有的輸入設備以文件的形式供用戶應用程序使用。可以看出 input 子系統用到了我們前面講解的驅動分層模型,我們編寫驅動程序的時候只需要關注中間的驅動層、核心層和事件層,這三個層的分工如下:
驅動層:輸入設備的具體驅動程序,比如按鍵驅動程序,向內核層報告輸入內容。
核心層:承上啓下,爲驅動層提供輸入設備註冊和操作接口。通知事件層對輸入事件進行處理。
事件層:主要和用戶空間進行交互。

input 驅動編寫流程

1767 struct class input_class = {
1768	 .name = "input",
1769     .devnode = input_devnode,
1770 };
......
2414 static int __init input_init(void)
2415 {
2416 	int err;
2418 	err = class_register(&input_class);
2419 	if (err) {
2420 		pr_err("unable to register input_dev class\n");
2421 		return err;
2422 	}
2423
2424 err = input_proc_init();
2425 if (err)
2426 goto fail1;
2427
2428 err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),
2429 INPUT_MAX_CHAR_DEVICES, "input");
2430 if (err) {
2431 	pr_err("unable to register char major %d",INPUT_MAJOR);
2432 goto fail2;
2433 }
2434
2435 return 0;
2436
2437 fail2: input_proc_exit();
2438 fail1: class_unregister(&input_class);
2439 return err;
2440 }

1、註冊 input_dev
在使用 input 子系統的時候我們只需要註冊一個 input 設備即可,input_dev 結構體表示 input
設備,此結構體定義在 include/linux/input.h 文件中,定義如下(有省略):

示例代碼 58.1.2.2 input_dev 結構體
121 struct input_dev {
122 const char *name;
123 const char *phys;
124 const char *uniq;
125 struct input_id id;
126
127 unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
128
129 unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; /* 事件類型的位圖 */
130 unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; /* 按鍵值的位圖 */
131 unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; /* 相對座標的位圖 */ 
132 unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; /* 絕對座標的位圖 */
133 unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)]; /* 雜項事件的位圖 */
134 unsigned long ledbit[BITS_TO_LONGS(LED_CNT)]; /*LED 相關的位圖 */
135 unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];/* sound 有關的位圖 */
136 unsigned long ffbit[BITS_TO_LONGS(FF_CNT)]; /* 壓力反饋的位圖 */
137 unsigned long swbit[BITS_TO_LONGS(SW_CNT)]; /*開關狀態的位圖 */
......
189 bool devres_managed;
190 };

第 129 行,evbit 表示輸入事件類型,可選的事件類型定義在 include/uapi/linux/input.h 文件
中,事件類型如下:

示例代碼 58.1.2.3 事件類型
#define EV_SYN 0x00 /* 同步事件 */
#define EV_KEY 0x01 /* 按鍵事件 */
#define EV_REL 0x02 /* 相對座標事件 */
#define EV_ABS 0x03 /* 絕對座標事件 */
#define EV_MSC 0x04 /* 雜項(其他)事件 */
#define EV_SW 0x05 /* 開關事件 */
#define EV_LED 0x11 /* LED */
#define EV_SND 0x12 /* sound(聲音) */
#define EV_REP 0x14 /* 重複事件 */
#define EV_FF 0x15 /* 壓力事件 */
#define EV_PWR 0x16 /* 電源事件 */
#define EV_FF_STATUS 0x17 /* 壓力狀態事件 */

比如本章我們要使用到按鍵,那麼就需要註冊 EV_KEY 事件,如果要使用連按功能的話還需要註冊 EV_REP 事件。繼續回到示例代碼 58.1.2.2 中,第 129 行~137 行的 evbit、keybit、relbit 等等都是存放不同事件對應的值。比如我們本章要使用按鍵事件,因此要用到 keybit,keybit 就是按鍵事件使用的位圖,Linux 內核定義了很多按鍵值,這些按鍵值定義在include/uapi/linux/input.h 文件中,按鍵值如下:
示例代碼 58.1.2.4 按鍵值

215 #define KEY_RESERVED 0
216 #define KEY_ESC 1
217 #define KEY_1 2
218 #define KEY_2 3
219 #define KEY_3 4
220 #define KEY_4 5
221 #define KEY_5 6
222 #define KEY_6 7
223 #define KEY_7 8
224 #define KEY_8 9
225 #define KEY_9 10
226 #define KEY_0 11
......
794 #define BTN_TRIGGER_HAPPY39 0x2e6
795 #define BTN_TRIGGER_HAPPY40 0x2e7

2、上報輸入事件
當我們向 Linux 內核註冊好 input_dev 以後還不能高枕無憂的使用 input 設備,input 設備都是具有輸入功能的,但是具體是什麼樣的輸入值 Linux 內核是不知道的,我們需要獲取到具體的輸入值,或者說是輸入事件,然後將輸入事件上報給 Linux 內核。比如按鍵,我們需要在按鍵中斷處理函數,或者消抖定時器中斷函數中將按鍵值上報給 Linux 內核,這樣 Linux 內核才能獲取到正確的輸入值。不同的事件,其上報事件的 API 函數不同,我們依次來看一下一些常用的事件上報 API 函數。
首先是 input_event 函數,此函數用於上報指定的事件以及對應的值,函數原型如下:

void input_event(struct input_dev *dev, 
 unsigned int type, 
 unsigned int code, 
 int value)

函數參數和返回值含義如下: dev:需要上報的 input_dev。 type: 上報的事件類型,比如 EV_KEY。
code:事件碼,也就是我們註冊的按鍵值,比如 KEY_0、KEY_1 等等。 value:事件值,比如 1 表示按鍵按下,0 表示按鍵鬆開

3、代碼示例

和按鍵中斷程序類似設備樹下的中斷,唯一不同的是在keyio_init函數裏申請完中斷後要申請輸入事件、註冊輸入設備

/* 申請 input_dev */
	keyinputdev.inputdev = input_allocate_device();
	keyinputdev.inputdev->name = KEYINPUT_NAME;
	keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) |BIT_MASK(EV_REP);
	input_set_capability(keyinputdev.inputdev, EV_KEY, KEY_0);

	/* 註冊輸入設備 */
	ret = input_register_device(keyinputdev.inputdev);
	if (ret) {
		printk("register input device failed!\r\n");
		return ret;
	}

timer_function定時器功能函數裏改爲上報按鍵事件

if(value == 0){ /* 按下按鍵 */
		/* 上報按鍵值 */
		//input_event(dev->inputdev, EV_KEY, keydesc->value, 1);
		input_report_key(dev->inputdev, keydesc->value, 1);/*1,按下*/
		input_sync(dev->inputdev);
	} else { /* 按鍵鬆開 */
		 //input_event(dev->inputdev, EV_KEY, keydesc->value, 0);
		input_report_key(dev->inputdev, keydesc->value, 0);
		input_sync(dev->inputdev);
	} 

更多文章請關注嵌入式機器人公衆號
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章