天天看點

input子系統一:輸入裝置驅動編寫input子系統一:輸入裝置驅動編寫

input子系統一:輸入裝置驅動編寫

在學習input子系統核心代碼之前,先來看看我們如何編寫一個符合input子系統架構的輸入裝置驅動,以最簡單的按鍵為例子。

1、驅動編寫步驟

1、配置設定一個struct input_dev結構體
2、設定裝置能産生哪類和哪些事件
3、注冊
4、硬體相關的操作(中斷、上報事件等)
           

2、接口函數

在編寫之前先來看看會用到的幾個關鍵的函數:

1、配置設定相關的函數

struct input_dev *input_allocate_device(void)
return:成功 傳回struct input_dev 失敗 NULL
NOTE:當注冊不成功時使用 input_free_device() 來釋放裝置,如果注冊成功了應該使用 input_unregister_device() 來登出裝置
---------------------------------------------------------------------
struct input_dev *devm_input_allocate_device(struct device *dev)
@dev:擁有正在建立的輸入裝置的裝置,如果你的裝置時一個i2c裝置 那這裡就是
	i2c_client->dev
return:成功 傳回struct input_dev 失敗 NULL
NOTE:如果調用這個函數來配置設定一個輸入裝置struct input_dev,就不再需要我們自己來主動釋放這個裝置,核心會在驅動解除安裝的時候自動釋放
           

2、設定相關的函數

void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
@dev:用前面說的函數申請的結構體struct input_dev
@type:能産生哪類事件,按鍵就是按鍵類事件 EV_KEY
@code:能産生這類事件中的哪些事件,比如鍵盤上的字母,空格,enter,滑鼠的左右鍵等
---------------------------------------------------------------------
void input_set_abs_params(struct input_dev *dev, unsigned int axis,
			  int min, int max, int fuzz, int flat)
@dev:用1中的函數申請的結構體struct input_dev
@axis:坐标
@min:坐标的最小值
@max:坐标的最大值
@fuzz:偏差
@flat:中心平滑位置(我也不知道是啥,一般填個0)
---------------------------------------------------------------------
__set_bit(nr, vaddr)
這是一個宏,如果不嫌麻煩想自己一個一個來配置就可以用這個宏,我們也可以看到上面兩個函數實際上也是調用的這個宏來設定相關的位,比如:鍵盤能産生按鍵類事件,并且能産生按鍵類事件中的enter鍵我們應該這麼寫:
__set_bit(EV_KEY, input_dev->evbit)
__set_bit(KEY_ENTER, input_dev->keybit)
           

3、注冊相關的函數

int input_register_device(struct input_dev *dev)
@dev:要注冊的輸入裝置
return:0 成功,其他失敗
           

4、上報事件的函數

void input_event(struct input_dev *dev,
		 unsigned int type, unsigned int code, int value)
@dev:輸入裝置
@type:哪一類事件
@code:這類事件中的哪個事件
@value:事件的值,比如松開0,按下1
---------------------------------------------------------------------
還有一些封裝好的特定的上報事件的函數,都是調用的input_event()函數,列舉一些:

static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
	input_event(dev, EV_KEY, code, !!value);
}

static inline void input_report_rel(struct input_dev *dev, unsigned int code, int value)
{
	input_event(dev, EV_REL, code, value);
}

static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
{
	input_event(dev, EV_ABS, code, value);
}
           

3、驅動的編寫

我們以按鍵驅動為例,來寫一個僞代碼,具體可以參考核心:\kernel\drivers\input\keyboard\gpio_keys.c

struct input_dev *glb_input;

static irqreturn_t my_keys_irq(int irq, void *devid)
{
	input_report_key(glb_input,KEY_L,1);
	input_sync(glb_input);  //事件上報完成要上報一個同步事件,這樣系統才會将這個事件上報給使用者空間
	return IRQ_HANDLED;
}
static int __init my_keys_init(void)
{
	int ret = 0;
	struct input_dev *input;
	/*配置設定一個struct input_dev*/
	input = devm_input_allocate_device(dev);
	if (!input) {
		dev_err(dev, "failed to allocate input device\n");
		return -ENOMEM;
	}
	glb_input = input;
	/*設定*/
	input_set_capability(input, EV_KEY, KEY_L);
	input_register_device(input);
	/*注冊*/
	ret = input_register_device(info->idev);
	if (ret ) {
		dev_err(chip->dev, "Can't register input device: %d\n", error);
		return error;
	}
	ret = request_threaded_irq(irq, NULL, my_keys_irq,
                               IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
                               "my_keys", input);
    return ret;
	
}
static void __exit my_keys_exit(void)
{
	
}
module_init(my_keys_init);
module_exit(my_keys_exit);
MODULE_LICENSE("GPL");
           

4、疑問

1、驅動調用input_register_device()來注冊輸入裝置,是如何注冊的?為什麼注冊之後會出現如event0,event1之類的東西?

2、在調用input_event()上報資料之後,資料是如何一步一步上報,又是如何被使用者讀取的?

繼續閱讀