广州西樵网站制作,离我最近的广告牌制作,浙江人事考试网,安阳seo往期内容 本专栏往期内容#xff1a; input子系统的框架和重要数据结构详解-CSDN博客input device和input handler的注册以及匹配过程解析-CSDN博客input device和input handler的注册以及匹配过程解析-CSDN博客 I2C子系统专栏#xff1a; 专栏地址#xff1a;IIC子系统_憧憬…往期内容 本专栏往期内容 input子系统的框架和重要数据结构详解-CSDN博客input device和input handler的注册以及匹配过程解析-CSDN博客input device和input handler的注册以及匹配过程解析-CSDN博客 I2C子系统专栏 专栏地址IIC子系统_憧憬一下的博客-CSDN博客具体芯片的IIC控制器驱动程序分析i2c-imx.c-CSDN博客 – 末篇有往期内容观看顺序 总线和设备树专栏 专栏地址总线和设备树_憧憬一下的博客-CSDN博客设备树与 Linux 内核设备驱动模型的整合-CSDN博客 – 末篇有往期内容观看顺序 前言 和之前的驱动程序有点差别IIC专栏中编写的控制器驱动框架编写一个通用的i2c控制器驱动框架-CSDN博客在driver中变成注册input_devfile_operations字符驱程序的创建在input_handler层实现(原本是在platform_driver中实现的file_operation、设备类的注册)实现了内核驱动程序的上层、中转层、下层的分离
下层驱动中只需要去编写好设备的驱动程序在程序中分配、设置、注册input_dev发生中断时只需要上报中断事件即可其余的中转层和上层的驱动程序内核已经做好了。 这个在之前对内核提供的源码示例进行讲解的时候也很清晰了详见本专栏前3章内容。 1. 怎么编写input_dev驱动
这里参考内核提供的gpio_keys.c为例子input_dev上层
\Linux-4.9.88\drivers\input\keyboard\gpio_keys.cgpio_keys.c
1.1 分配、设置、注册input_dev
这一部分主要是probe完成 在gpio_keys.c中添加了自己理解的一点注释如下
static int gpio_keys_probe(struct platform_device *pdev)
{struct device *dev pdev-dev; // 获取设备结构体const struct gpio_keys_platform_data *pdata dev_get_platdata(dev); // 从平台设备获取平台数据struct gpio_keys_drvdata *ddata; // 驱动私有数据结构struct input_dev *input; // 输入设备结构体size_t size; // 计算需要分配的内存大小int i, error; // 循环计数器和错误码int wakeup 0; // 标志位指示是否支持唤醒功能// 如果 pdata 为 NULL则从设备树获取平台数据if (!pdata) {pdata gpio_keys_get_devtree_pdata(dev);if (IS_ERR(pdata))return PTR_ERR(pdata); // 返回错误码}// 计算 gpio_keys_drvdata 和按钮数据结构的大小size sizeof(struct gpio_keys_drvdata) pdata-nbuttons * sizeof(struct gpio_button_data);// 分配驱动私有数据内存ddata devm_kzalloc(dev, size, GFP_KERNEL);if (!ddata) {dev_err(dev, failed to allocate state\n); // 分配失败输出错误信息return -ENOMEM; // 返回内存不足错误码}// 分配输入设备input devm_input_allocate_device(dev);if (!input) {dev_err(dev, failed to allocate input device\n); // 分配失败输出错误信息return -ENOMEM; // 返回内存不足错误码}ddata-pdata pdata; // 保存平台数据指针ddata-input input; // 保存输入设备指针mutex_init(ddata-disable_lock); // 初始化互斥锁// 将驱动数据指针与平台设备相关联platform_set_drvdata(pdev, ddata);input_set_drvdata(input, ddata); // 将驱动数据与输入设备关联// 设置输入设备名称和物理路径input-name pdata-name ? : pdev-name; // 如果 pdata 中有名称则使用否则使用平台设备名称input-phys gpio-keys/input0; // 设置物理路径input-dev.parent pdev-dev; // 设置设备的父设备input-open gpio_keys_open; // 设置打开设备的函数input-close gpio_keys_close; // 设置关闭设备的函数// 设置输入设备的 IDinput-id.bustype BUS_HOST; // 设置总线类型input-id.vendor 0x0001; // 设置厂商 IDinput-id.product 0x0001; // 设置产品 IDinput-id.version 0x0100; // 设置版本号// 启用 Linux 输入子系统的自动重复功能if (pdata-rep)__set_bit(EV_REP, input-evbit); // 设置 EV_REP 事件位// 遍历每个按钮并设置for (i 0; i pdata-nbuttons; i) {const struct gpio_keys_button *button pdata-buttons[i]; // 获取当前按钮信息struct gpio_button_data *bdata ddata-data[i]; // 获取按钮数据error gpio_keys_setup_key(pdev, input, bdata, button); // 设置按键里面包括设置了中断函数if (error)return error; // 返回错误码if (button-wakeup) // 如果按钮支持唤醒功能wakeup 1; // 设置唤醒标志}// 创建 sysfs 组用于导出按键和开关error sysfs_create_group(pdev-dev.kobj, gpio_keys_attr_group);if (error) {dev_err(dev, Unable to export keys/switches, error: %d\n, error); // 输出错误信息return error; // 返回错误码}// 注册输入设备error input_register_device(input);if (error) {dev_err(dev, Unable to register input device, error: %d\n, error); // 输出错误信息goto err_remove_group; // 错误处理移除 sysfs 组}// 初始化唤醒设备功能device_init_wakeup(pdev-dev, wakeup);return 0; // 返回成功err_remove_group:// 在错误情况下移除 sysfs 组sysfs_remove_group(pdev-dev.kobj, gpio_keys_attr_group);return error; // 返回错误码
}可以看出来分配、设置、注册input_dev然后去注册中断函数用于调用中断的input_event函数上报中断事件尝试写一个
static struct input_dev *g_input_dev;
static int g_irq;
static irqreturn_t input_dev_demo_isr(int irq, void *dev_id)
{/* read data *//* report data */input_event(g_input_dev, EV_KEY, XX, 0);//通过 input_event() 上报事件input_sync() 用于同步报告的输入事件。input_sync(g_input_dev);return IRQ_HANDLED;
}static int input_dev_demo_probe(struct platform_device *pdev)
{struct device *dev pdev-dev;int error;struct resource *irq;/* 从设备树中获取硬件信息 *//* 分配、设置并注册 input_dev */g_input_dev devm_input_allocate_device(dev);// 设置输入设备的基本信息g_input_dev-name input_dev_demo;g_input_dev-phys input_dev_demo;g_input_dev-dev.parent dev;g_input_dev-id.bustype BUS_HOST;g_input_dev-id.vendor 0x0001;g_input_dev-id.product 0x0001;g_input_dev-id.version 0x0100;/* 设置 1: 支持的事件类型 */__set_bit(EV_KEY, g_input_dev-evbit); // 键盘或按钮事件__set_bit(EV_ABS, g_input_dev-evbit); // 绝对坐标事件/* 设置 2: 支持的具体事件 */__set_bit(BTN_TOUCH, g_input_dev-keybit); // 触摸按钮事件__set_bit(ABS_MT_SLOT, g_input_dev-absbit); // 多点触摸槽位__set_bit(ABS_MT_POSITION_X, g_input_dev-absbit); // 触摸屏 X 轴坐标__set_bit(ABS_MT_POSITION_Y, g_input_dev-absbit); // 触摸屏 Y 轴坐标/* 设置 3: 事件参数 */input_set_abs_params(g_input_dev, ABS_MT_POSITION_X, 0, 0xffff, 0, 0); // X 坐标范围input_set_abs_params(g_input_dev, ABS_MT_POSITION_Y, 0, 0xffff, 0, 0); // Y 坐标范围// 注册输入设备error input_register_device(g_input_dev);/* 硬件操作: 获取中断资源并注册中断 */irq platform_get_resource(pdev, IORESOURCE_IRQ, 0);g_irq irq-start;request_irq(irq-start, input_dev_demo_isr, IRQF_TRIGGER_RISING, input_dev_demo_irq, NULL);return 0;
}1.2 硬件相关操作
申请中断在中断服务程序里 读取硬件获得数据上报数据
void input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value);static inline void input_sync(struct input_dev *dev); // 实质也是 input_eventinput子系统中读取流程解析-CSDN博客 具体内容在该章节的 event 处已经讲解过。
2. 代码 #include linux/module.h
#include linux/init.h
#include linux/fs.h
#include linux/interrupt.h
#include linux/irq.h
#include linux/sched.h
#include linux/pm.h
#include linux/slab.h
#include linux/sysctl.h
#include linux/proc_fs.h
#include linux/delay.h
#include linux/platform_device.h
#include linux/input.h
#include linux/gpio_keys.h
#include linux/workqueue.h
#include linux/gpio.h
#include linux/gpio/consumer.h
#include linux/of.h
#include linux/of_irq.h
#include linux/spinlock.h/* 定义指向输入设备结构体的指针 */
static struct input_dev *input_device_demo;
/* 用于保存输入设备的中断号 */
static int irq_num;/* 中断服务程序处理输入设备的事件 */
static irqreturn_t input_device_demo_isr(int irq, void *dev_id)
{/* 可在此处添加数据读取和事件处理逻辑 *//* 向输入子系统报告按键事件 */input_event(input_device_demo, EV_KEY, KEY_TOUCH, 0);input_sync(input_device_demo);return IRQ_HANDLED;
}/* 分配、配置和注册平台驱动 */
static int input_device_demo_probe(struct platform_device *pdev)
{struct device *dev pdev-dev;int error;struct resource *irq;/* 从设备树获取硬件信息 *//* 分配并初始化输入设备结构体 */input_device_demo devm_input_allocate_device(dev);if (!input_device_demo)return -ENOMEM;/* 设置设备名称和物理位置 */input_device_demo-name input_device_demo;input_device_demo-phys input_device_demo;input_device_demo-dev.parent dev;/* 设置设备的总线类型和ID */input_device_demo-id.bustype BUS_HOST;input_device_demo-id.vendor 0x0001;input_device_demo-id.product 0x0001;input_device_demo-id.version 0x0100;/* 设置输入设备支持的事件类型 */__set_bit(EV_KEY, input_device_demo-evbit); // 支持按键事件__set_bit(EV_ABS, input_device_demo-evbit); // 支持绝对位置事件/* 设置输入设备支持的具体事件 */__set_bit(BTN_TOUCH, input_device_demo-keybit); // 支持触摸按键事件__set_bit(ABS_MT_SLOT, input_device_demo-absbit); // 多点触控槽位事件__set_bit(ABS_MT_POSITION_X, input_device_demo-absbit); // X轴位置事件__set_bit(ABS_MT_POSITION_Y, input_device_demo-absbit); // Y轴位置事件/* 设置具体事件的参数范围例如X和Y坐标的最小值、最大值等 */input_set_abs_params(input_device_demo, ABS_MT_POSITION_X, 0, 0xffff, 0, 0);input_set_abs_params(input_device_demo, ABS_MT_POSITION_Y, 0, 0xffff, 0, 0);/* 注册输入设备 */error input_register_device(input_device_demo);if (error)return error;/* 硬件操作从设备树获取中断资源并注册中断 */irq platform_get_resource(pdev, IORESOURCE_IRQ, 0);irq_num irq-start;error request_irq(irq_num, input_device_demo_isr, IRQF_TRIGGER_RISING, input_device_demo_irq, NULL);if (error)input_unregister_device(input_device_demo);return error;
}/* 驱动移除函数释放中断资源并注销输入设备 */
static int input_device_demo_remove(struct platform_device *pdev)
{free_irq(irq_num, NULL);input_unregister_device(input_device_demo);return 0;
}/* 设备树匹配表用于匹配设备树中描述的设备 */
static const struct of_device_id input_device_demo_of_match[] {{ .compatible input,input_device_demo, },{ },
};/* 定义平台驱动结构体指定probe和remove函数 */
static struct platform_driver input_device_demo_driver {.probe input_device_demo_probe,.remove input_device_demo_remove,.driver {.name input_device_demo,.of_match_table input_device_demo_of_match,}
};/* 模块初始化函数注册平台驱动 */
static int __init input_device_demo_init(void)
{return platform_driver_register(input_device_demo_driver);
}/* 模块退出函数注销平台驱动 */
static void __exit input_device_demo_exit(void)
{platform_driver_unregister(input_device_demo_driver);
}module_init(input_device_demo_init);
module_exit(input_device_demo_exit);MODULE_LICENSE(GPL);在以前的驱动程序模板中platform_driver中不仅注册了file_operation等也注册了中断处理函数并且中断处理函数中是直接对中断事件进行处理
但是在内核驱动分层中platform_input_dev中断处理函数只上报中断事件给input.c交由Input.c去调用input_dev对应的input_handler中的函数来处理中断(filter、events、event函数)同时input_handler层提供了app调用的接口函数如file_operation中的read