Linux keypad 设备树,matrix_keypad 矩阵按键驱动分析

Linux keypad 设备树,matrix_keypad 矩阵按键驱动分析matrix_keypad矩阵按键驱动分析//主要函数调用过程matrix_keypad_probematrix_keypad_parse_dt//根据设备树构造pdatapdata->num_row_gpios=nrow=of_gpio_named_count(np,”row-gpios”);pdata->num_col_gpios=ncol=of_gpio_…

大家好,又见面了,我是你们的朋友全栈君。

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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/131370.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(1)


相关推荐

  • httpclient4下载图片 java实现[通俗易懂]

    httpclient4下载图片 java实现[通俗易懂]有时候需要从网上抓取一下图片jpg、png等,也可以抓取zip等,这样就需要写程序才能达到想要的效果,下面是用httpclient4做一个工具类,非常的好用packagecom.wamei.tool;importjava.awt.image.BufferedImage;importjava.io.File;importjava.io.FileOutputStream;

  • 修改Matlab的背景颜色

    修改Matlab的背景颜色网上的教程基本是修改prf文件或者输入一串命令其实使用界面操作就可以改变背景颜色步骤点击主页—>预设(预设在布局右边)点击颜色,出现以下界面。在这里,默认是勾选“使用系统颜色”的。我们把这一项取消勾选!取消勾选以后,“文本”和“背景”就变成可点击的状态,点击“背景”—>“更多颜色”在RGB这一栏输入护眼色的参数,R:199,G:237,B:2…

  • 3分钟教你子网划分–(内含习题讲解)

    3分钟教你子网划分–(内含习题讲解)一.IPV41.IP地址IP地址分为IPV4和IPV6,但现在目前大家所常用的为IPV4。IPV4是由32位二进制数组成,分成四组,每组八位。例如:11000000111100000000000000000000为了便于配置通常表示成点分十进制例如:192.168.1.1IPV6由128位组成,一般用冒号分隔,十六进制表示2.IPV4地址组成IPV4是由两部分组成,即:网络部分(NETWORK)主机部分(HOST)例:192.168.1.132网络部分:192.168.1

  • tracert的工作原理?_ipconfig工作原理

    tracert的工作原理?_ipconfig工作原理Tracert利用ICMP数据报和IP数据报头部中的TTL值。TTL(TimeToLive)是一个IP数据报的生存时间,当每个IP数据报经过路由器的时候都回把TTL值减去1或者减去在路由器中停留的时间,但是大多数数据报在路由器中停留的时间都小于1秒种,因此实际上就是在TTL值减去了1。这样,TTL值就相当于一个路由器的计数器。当路由器接收到一个TTL为0或者1的IP…

  • Java课设–学生成绩管理系统一

    Java课设–学生成绩管理系统一写在前面这个项目是Java课程的课设,一共花了5天的时间去完成它,在这期间感谢一些博主的帮助,让我了解到了一些新的技术知识,所以打算写这一系列博客来介绍一整个课设项目,也为了帮助之后的人,如有错误,请联系我。为了更好的让读者了解到整个项目的设计流程,我将项目拆分成几个部分来就行解说,这一小节是一个总述,主要介绍课设的整个框架和最终效果,代码我会放到后面的github链接上,欢迎大家star。如果有一些参考没有加上联系,希望大家可以联系我,因为写的时候查的比较快,没有记录到博主的链接,敬请谅解!!!一、

  • STM32——软件SPI控制AD7705[通俗易懂]

    一、AD7705简介AD7705为差分输入的16位ADC,拥有两组差分输入通道。自带可编程增益,增益可在1到128调节。支持SPI接口。AD7705功能框图如图所示:AD7705的主要寄存器有通信寄存器(CommunicationRegister)、时钟寄存器(ClockRegister)、建立寄存器(SetupRegister)和数据寄存器(DataRegister)。在这些寄存器中,只有数据寄存器是16位的。通信寄存器负责寄存器寻址、读写控制…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号