matrix_keypad 矩陣按鍵驅動分析


matrix_keypad 矩陣按鍵驅動分析

//主要函數調用過程
matrix_keypad_probe
	matrix_keypad_parse_dt //根據設備樹構造 pdata
		pdata->num_row_gpios = nrow = of_gpio_named_count(np, "row-gpios");
		pdata->num_col_gpios = ncol = of_gpio_named_count(np, "col-gpios");
		of_get_property(np, "linux,no-autorepeat", NULL)

		of_get_property(np, "linux,wakeup", NULL)

		of_get_property(np, "gpio-activelow", NULL)

		of_property_read_u32(np, "debounce-delay-ms", &pdata->debounce_ms);
		of_property_read_u32(np, "col-scan-delay-us",&pdata->col_scan_delay_us);
		
		for (i = 0; i < pdata->num_row_gpios; i++)
			gpios[i] = of_get_named_gpio(np, "row-gpios", i);

		for (i = 0; i < pdata->num_col_gpios; i++)
			gpios[pdata->num_row_gpios + i] =  of_get_named_gpio(np, "col-gpios", i)	

	matrix_keypad_build_keymap
		matrix_keypad_parse_of_keymap
			 of_get_property(np, "linux,keymap", &proplen);
			 
			 matrix_keypad_map_key(input_dev, rows, cols, row_shift, key)
				unsigned int row = KEY_ROW(key);
				unsigned int col = KEY_COL(key);
				unsigned short code = KEY_VAL(key);
				
				keymap[MATRIX_SCAN_CODE(row, col, row_shift)] = code;
				__set_bit(code, input_dev->keybit);
	
	matrix_keypad_init_gpio
		gpio_request(pdata->col_gpios[i], "matrix_kbd_col")
		gpio_direction_output(pdata->col_gpios[i], !pdata->active_low);
		
		gpio_request(pdata->row_gpios[i], "matrix_kbd_row");
		gpio_direction_input(pdata->row_gpios[i]);
		
		request_any_context_irq
	
	input_register_device




//具體分析
//矩陣按鍵驅動源碼在”drivers/input/keyboard/matrix_keypad.c”中

static int matrix_keypad_probe(struct platform_device *pdev)
{
	const struct matrix_keypad_platform_data *pdata;
	struct matrix_keypad *keypad;
	struct input_dev *input_dev;
	int err;

	pdata = dev_get_platdata(&pdev->dev);  // 獲取設備的platform_data ;這個應該時傳統的 平臺設備匹配模型。
	if (!pdata) {
		//如果執行到這裏,說明不是使用傳統的平臺設備模型,而是使用 設備樹進行匹配的;
		// 那麼接下來的重點就是分析 matrix_keypad_parse_dt
		pdata = matrix_keypad_parse_dt(&pdev->dev); //根據設備樹的信息,構造 pdata
		if (IS_ERR(pdata)) {
			dev_err(&pdev->dev, "no platform data defined\n");
			return PTR_ERR(pdata);
		}
	} else if (!pdata->keymap_data) {
		dev_err(&pdev->dev, "no keymap data defined\n");
		return -EINVAL;
	}

	keypad = kzalloc(sizeof(struct matrix_keypad), GFP_KERNEL);
	input_dev = input_allocate_device();
	..

	keypad->input_dev = input_dev;
	keypad->pdata = pdata;
	keypad->row_shift = get_count_order(pdata->num_col_gpios);
	keypad->stopped = true;
	INIT_DELAYED_WORK(&keypad->work, matrix_keypad_scan);
	spin_lock_init(&keypad->lock);

	input_dev->name		= pdev->name;
	input_dev->id.bustype	= BUS_HOST;
	input_dev->dev.parent	= &pdev->dev;
	input_dev->open		= matrix_keypad_start;
	input_dev->close	= matrix_keypad_stop;

	err = matrix_keypad_build_keymap(pdata->keymap_data, NULL,
					 pdata->num_row_gpios,
					 pdata->num_col_gpios,
					 NULL, input_dev); //從 keymap_data 裏分解出行列鍵對應的鍵碼;或 從設備樹裏獲取 keymap
	..

	if (!pdata->no_autorepeat)
		__set_bit(EV_REP, input_dev->evbit); //按鍵的重複性時間
	input_set_capability(input_dev, EV_MSC, MSC_SCAN);
	input_set_drvdata(input_dev, keypad); //設置輸入設備的私有數據爲 keypad

	err = matrix_keypad_init_gpio(pdev, keypad);//註冊行線的中斷號
	..

	err = input_register_device(keypad->input_dev);//註冊輸入設備
	..

	device_init_wakeup(&pdev->dev, pdata->wakeup);
	platform_set_drvdata(pdev, keypad);

	return 0;

...
	return err;
}



//根據設備樹的信息,構造 pdata
static struct matrix_keypad_platform_data *matrix_keypad_parse_dt(struct device *dev)
{
	struct matrix_keypad_platform_data *pdata;
	struct device_node *np = dev->of_node;
	unsigned int *gpios;
	int i, nrow, ncol;

	..

	//分配一塊內存給 pdata
	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
	...
	pdata->num_row_gpios = nrow = of_gpio_named_count(np, "row-gpios");//獲取GPIO引腳的個數
	pdata->num_col_gpios = ncol = of_gpio_named_count(np, "col-gpios");
	...

	if (of_get_property(np, "linux,no-autorepeat", NULL))
		pdata->no_autorepeat = true;
	if (of_get_property(np, "linux,wakeup", NULL))
		pdata->wakeup = true;
	if (of_get_property(np, "gpio-activelow", NULL))
		pdata->active_low = true;

	of_property_read_u32(np, "debounce-delay-ms", &pdata->debounce_ms);//按鍵的消抖延遲
	of_property_read_u32(np, "col-scan-delay-us",
						&pdata->col_scan_delay_us); //掃描延遲

	
	gpios = devm_kzalloc(dev,
			     sizeof(unsigned int) *
				(pdata->num_row_gpios + pdata->num_col_gpios),
			     GFP_KERNEL);
	...
	// 獲取GPIO引腳
	for (i = 0; i < pdata->num_row_gpios; i++)
		gpios[i] = of_get_named_gpio(np, "row-gpios", i);//獲取 屬性爲 "row-gpios" 的第 i 個數據

	for (i = 0; i < pdata->num_col_gpios; i++)
		gpios[pdata->num_row_gpios + i] =
			of_get_named_gpio(np, "col-gpios", i);

	pdata->row_gpios = gpios;
	pdata->col_gpios = &gpios[pdata->num_row_gpios];

	return pdata;
}


int matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data,
			       const char *keymap_name,
			       unsigned int rows, unsigned int cols,
			       unsigned short *keymap,
			       struct input_dev *input_dev)
{
	unsigned int row_shift = get_count_order(cols);
	size_t max_keys = rows << row_shift;
	int i;
	int error;

	...

	if (!keymap) {
		keymap = devm_kzalloc(input_dev->dev.parent,
				      max_keys * sizeof(*keymap),
				      GFP_KERNEL);
		...
		}
	}

	input_dev->keycode = keymap;
	input_dev->keycodesize = sizeof(*keymap);
	input_dev->keycodemax = max_keys;

	__set_bit(EV_KEY, input_dev->evbit);

	if (keymap_data) {
		for (i = 0; i < keymap_data->keymap_size; i++) {
			unsigned int key = keymap_data->keymap[i];

			if (!matrix_keypad_map_key(input_dev, rows, cols,
						   row_shift, key))
				return -EINVAL;
		}
	} else {
	//如果 keymap_data 爲NULL時,則從設備樹裏 獲取 ; 那麼重點就是解析設備樹裏的數據了
		error = matrix_keypad_parse_of_keymap(keymap_name, rows, cols,  input_dev);
		...
	}

	__clear_bit(KEY_RESERVED, input_dev->keybit);

	return 0;
}

//就是解析設備樹節點裏的 linux,keymap 屬性
static int matrix_keypad_parse_of_keymap(const char *propname,
					 unsigned int rows, unsigned int cols,
					 struct input_dev *input_dev)
{
	struct device *dev = input_dev->dev.parent;
	struct device_node *np = dev->of_node;
	unsigned int row_shift = get_count_order(cols);
	unsigned int max_keys = rows << row_shift;
	unsigned int proplen, i, size;
	const __be32 *prop;

	if (!np)
		return -ENOENT;

	if (!propname)
		propname = "linux,keymap";

	// 獲取節點屬性值裏的首地址
	prop = of_get_property(np, propname, &proplen);
	...

	size = proplen / sizeof(u32);
	...

	for (i = 0; i < size; i++) {
		unsigned int key = be32_to_cpup(prop + i);//獲取屬性值

		if (!matrix_keypad_map_key(input_dev, rows, cols, row_shift, key)) //設置 keymap
			return -EINVAL;
	}

	return 0;
}



static bool matrix_keypad_map_key(struct input_dev *input_dev,
				  unsigned int rows, unsigned int cols,
				  unsigned int row_shift, unsigned int key)
{
	unsigned short *keymap = input_dev->keycode;
	unsigned int row = KEY_ROW(key);
	unsigned int col = KEY_COL(key);
	unsigned short code = KEY_VAL(key);

	...

	keymap[MATRIX_SCAN_CODE(row, col, row_shift)] = code;
	__set_bit(code, input_dev->keybit);

	return true;
}

/*
 列線作爲輸出,行線作爲中斷輸入
*/
static int matrix_keypad_init_gpio(struct platform_device *pdev,  struct matrix_keypad *keypad)
{
	const struct matrix_keypad_platform_data *pdata = keypad->pdata;
	int i, err;

	/* initialized strobe lines as outputs, activated */
	for (i = 0; i < pdata->num_col_gpios; i++) {
		err = gpio_request(pdata->col_gpios[i], "matrix_kbd_col"); //請求IO 
		...
		gpio_direction_output(pdata->col_gpios[i], !pdata->active_low);//設置爲輸出
	}

	for (i = 0; i < pdata->num_row_gpios; i++) {
		err = gpio_request(pdata->row_gpios[i], "matrix_kbd_row");//請求io
		...
		gpio_direction_input(pdata->row_gpios[i]);//設置爲輸入
	}

	if (pdata->clustered_irq > 0) {
		err = request_any_context_irq(pdata->clustered_irq,
				matrix_keypad_interrupt,
				pdata->clustered_irq_flags,
				"matrix-keypad", keypad);
		...
	} else {
		for (i = 0; i < pdata->num_row_gpios; i++) {
			err = request_any_context_irq(
					gpio_to_irq(pdata->row_gpios[i]),
					matrix_keypad_interrupt,
					IRQF_TRIGGER_RISING |
					IRQF_TRIGGER_FALLING,
					"matrix-keypad", keypad);
			...
		}
	}

	/* initialized as disabled - enabled by input->open */
	disable_row_irqs(keypad);
	return 0;

...

	return err;
}


通過probe函數,可以確定我們寫平臺設備時只需通過platform_data成員提供平臺驅動所需的信息,無需再提供resource.

再確定結構體matrix_keypad_platform_data的每個成員的作用即可,如不清楚具體用途,可以在驅動代碼裏通過查看對成員值的訪問反推出用途.

在"include/linux/input/matrix_keypad.h"中有

#define KEY(row, col, val)  ((((row) & (MATRIX_MAX_ROWS - 1)) << 24) |\
                 (((col) & (MATRIX_MAX_COLS - 1)) << 16) |\
                 ((val) & 0xffff))
.....
#define KEY_ROW(k)      (((k) >> 24) & 0xff)
#define KEY_COL(k)      (((k) >> 16) & 0xff)
#define KEY_VAL(k)      ((k) & 0xffff)
.....
.....
#define MATRIX_SCAN_CODE(row, col, row_shift)   (((row) << (row_shift)) + (col))


......
......
struct matrix_keymap_data {
    const uint32_t *keymap; //裝載按鍵對應的鍵碼數組, 注意每個鍵碼需要使用宏KEY來寫。也就是一個32位數據裏,行,列,鍵碼各佔用8, 8, 16位.
    unsigned int    keymap_size; //鍵碼數組的元素個數
};
......
......
struct matrix_keypad_platform_data {
    const struct matrix_keymap_data *keymap_data; //鍵碼數據對象的首地址

    const unsigned int *row_gpios; //行線用的IO口
    const unsigned int *col_gpios; //列線用的IO口

    unsigned int    num_row_gpios; //多少個行線
    unsigned int    num_col_gpios; //多少個列線

    unsigned int    col_scan_delay_us; //掃描列線時間隔時間

    unsigned int    debounce_ms; //防抖動的間隔時間

    unsigned int    clustered_irq; //行線是否共用一箇中斷, 設0則每個行線的中斷是獨立的
    unsigned int    clustered_irq_flags;

    bool        active_low; //鍵按下時,行線是否爲低電平
    bool        wakeup;
    bool        no_autorepeat; //按鍵按下時是否重複提交按鍵, 設1就是不重複,設0重複
};


Linux中輸入設備的事件類型有:
EV_SYN 0x00 同步事件
EV_KEY 0x01 按鍵事件,如KEY_VOLUMEDOWN
EV_REL 0x02 相對座標,   如shubiao上報的座標
EV_ABS 0x03 絕對座標,如觸摸屏上報的座標
EV_MSC 0x04 其它
EV_LED 0x11 LED
EV_SND 0x12 聲音
EV_REP 0x14 Repeat
EV_FF  0x15 力反饋 


IMX6UL上添加支持矩陣按鍵(裏面有設備樹的配置信息):
https://blog.csdn.net/qq_39346729/article/details/103293553


 

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