2020-09-01

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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值