Linux keypad 设备树,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
Linux keypad 设备树,matrix_keypad 矩阵按键驱动分析相关推荐
- Linux keypad 设备树,GitHub - bilibilifmk/rpi_mt: 树莓派移动终端 全键盘及2.4寸tft材质屏幕...
rpi_mt 树莓派移动终端 全键盘及2.4寸tft材质屏幕 这是一个还在开发中的项目 我自己打包的系统镜像 dd命令镜像:https://pan.baidu.com/s/1et3HwhCRhgPBm ...
- Linux keypad 设备树,SC7731客户配置文档.pdf
SC7731客户配置文档 SC7731 客户配置文档 2014-07-09 目录 02 Add your texts here SC7731 0101 03 SC7731 软件架构简介 02 Pinm ...
- 【正点原子Linux连载】第四十四章 设备树下的LED驱动实验 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0
1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...
- Linux 设备树下的 platform 驱动实验基于正点原子IMX6ULL开发板
1 设备树下的 platform 驱动简介 platform 驱动框架分为总线.设备和驱动,其中总线不需要我们这些驱动程序员去管理,这个是 Linux 内核提供的,我们在编写驱动的时候只要关注于设备和 ...
- Linux 设备树下的 platform 驱动示例
1.简介 基于总线.设备和驱动这样的驱动框架,Linux 内核提出来 platform 这个虚拟总线,相应的也有 platform 设备和 platform 驱动. Linux 总线设备和驱动模式 2 ...
- linux uart寄存器 代替 printk,Linux驱动学习之设备树(设备树下的LED驱动实验),...
Linux驱动学习之设备树(设备树下的LED驱动实验), 概念 Linux内核从3.x开始引入设备树的概念,用于实现驱动代码与设备信息相分离.相当于从驱动代码分离出来的配置文件,比如串口的波特率通过设 ...
- 【正点原子MP157连载】第二十四章 设备树下的LED驱动实验-摘自【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7
1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频 ...
- linux设备和驱动匹配的方法,Linux使用设备树的i2c驱动与设备匹配方式
Linux使用设备树的i2c驱动与设备匹配有3种方式: of_driver_match_device acpi_driver_match_device i2c_match_id 源码: static ...
- 韦东山 IMX6ULL和正点原子_「正点原子Linux连载」第四十四章设备树下的LED驱动实验...
1)实验平台:正点原子Linux开发板 2)摘自<正点原子I.MX6U嵌入式Linux驱动开发指南> 关注官方微信号公众号,获取更多资料:正点原子 上一章我们详细的讲解了设备树语法以及在驱 ...
最新文章
- CSS3 Transitions 你可能不知道的知识点
- 在/sys目录下建立一个文件,并且实现信息的读取和存储
- 【技术分享】如何解锁高通骁龙660上的安卓引导加载程序
- 物理化学 相平衡
- 局部变量-不同函数内的同名局部变量
- python 编辑数学公式_用python编写数学公式
- 2014年考研英语二作文PartB图表题
- su切换到oracle后怎么退出,linux下启动oralce和关闭oracle以及数据库实例化
- 我php第一个页面,您的第一个 PHP 页面
- Hdu - 1210 - Eddy's 洗牌问题
- java 绘制动态小球_JavaScript动画实例:动感小球
- Android粒子爆炸特效[可用于任意控件]
- 批量建模:一元线性回归
- 在Exchange server 2007中管理pop3和IMAP4协议访问
- 商务网站建设与维护【16】
- LeetCode 1071(最大公约)
- 13位时间戳(单位为毫秒)转换为10位字符串(单位为秒)
- C++中为什么str=new char[strlen(s)+1];中要加1
- CodeLite可以媲美Code::Blocks
- Ucloud直播技术细节