触摸屏(典型 I2C + Input 子系统设备)从设备树解析到触摸事件上报

触摸屏(典型 I2C + Input 子系统设备)从设备树解析到触摸事件上报

以下是架构图,对触摸屏(典型I2C + Input子系统设备)从设备树解析到触摸事件上报的全流程详细拆解,包含文字讲解和配套流程图:

注:代码程序可以参考driver\input\touchscreen\gt9xx.c,驱动程序非常复杂是因为要兼容很多厂家的设备甚至升级固件之类的操作,通用的大致流程如下:

一、核心角色与职责

组件 作用
设备树 描述硬件拓扑(I2C总线、触摸屏设备地址、中断号等)
i2c_client 代表I2C总线上的触摸屏设备,保存设备地址、总线适配器等信息
i2c_driver 触摸屏驱动核心,负责匹配i2c_client、初始化硬件、创建Input子系统设备
input_dev 向上抽象为输入设备(如触摸屏),提供事件上报接口(按键、绝对坐标等)
input_handler 输入事件处理器(如evdev),负责接收input_dev事件并暴露给用户空间

二、全流程分步讲解(配流程图)

1. 设备树解析与 i2c_client 创建

流程
设备树 设备树解析 i2c总线驱动 i2c_client创建 i2c_client注册到i2c总线

细节

  • 设备树中定义触摸屏节点(示例片段):

    dts 复制代码
    i2c@12340000 { // I2C控制器节点
        touchscreen@38 { // 触摸屏子节点,地址0x38
            compatible = "vendor,touchscreen"; 
            reg = <0x38>; // I2C设备地址
            interrupts = <IRQ_TYPE_EDGE_FALLING 25>; // 中断号
        };
    };
  • 内核启动时,I2C总线驱动 解析设备树,为每个I2C设备创建 i2c_client,填充设备地址、兼容字符串、中断等信息,注册到 i2c_bus_type 总线。

2. i2c_driver 注册与匹配

流程
compatible匹配 i2c_driver编写 i2c_driver注册 i2c总线匹配 i2c_driver.probe调用

细节

  • 驱动代码中定义 i2c_driver

    c 复制代码
    static const struct of_device_id touchscreen_of_match[] = {
        { .compatible = "vendor,touchscreen" }, // 匹配设备树compatible
        { },
    };
    
    static struct i2c_driver touchscreen_i2c_driver = {
        .probe    = touchscreen_probe, // 匹配成功后执行的函数
        .driver = {
            .name = "touchscreen",
            .of_match_table = touchscreen_of_match,
        },
    };
    
    module_i2c_driver(touchscreen_i2c_driver); // 注册到I2C总线
  • 匹配逻辑i2c_bus_type 遍历总线上的 i2c_client 和已注册 i2c_driver,通过 compatible 字符串匹配。匹配成功后,调用 i2c_driver.probe 函数。

3. input_dev 创建与注册(在 probe 中完成)

流程
probe函数启动 硬件初始化 input_dev分配 input_dev设置 input_dev注册

细节

  • probe 函数核心逻辑:

    c 复制代码
    static int touchscreen_probe(struct i2c_client *client, 
                                 const struct i2c_device_id *id) {
        struct input_dev *input;
        int ret;
    
        // 1. 硬件初始化(如配置I2C寄存器、申请中断)
        ret = i2c_smbus_write_byte_data(client, 0x00, 0x01); // 示例写寄存器
        if (ret < 0) return ret;
    
        // 2. 分配input_dev
        input = devm_input_allocate_device(&client->dev);
        if (!input) return -ENOMEM;
    
        // 3. 设置input_dev属性
        input->name = "touchscreen";
        input->phys = "i2c-touchscreen/input0";
        input->id.bustype = BUS_I2C;
    
        // 4. 声明支持的事件类型(如绝对坐标、按键)
        __set_bit(EV_ABS, input->evbit); 
        __set_bit(ABS_X, input->absbit);
        __set_bit(ABS_Y, input->absbit);
        __set_bit(EV_KEY, input->evbit);
        __set_bit(BTN_TOUCH, input->keybit);
    
        // 5. 注册input_dev到Input子系统
        ret = input_register_device(input);
        if (ret < 0) return ret;
    
        // 6. 保存input_dev到私有数据(供中断处理用)
        i2c_set_clientdata(client, input);
    
        // 7. 申请中断(触摸事件触发时调用中断处理函数)
        return devm_request_irq(&client->dev, client->irq, 
                                touchscreen_irq_handler, 
                                IRQF_TRIGGER_FALLING, 
                                "touchscreen", 
                                input);
    }
4. input_handler 自动匹配与关联

流程
事件类型匹配 input_dev注册 Input子系统匹配 input_handler关联

细节

  • Input子系统中,input_handler(如通用的 evdev 处理器)会遍历所有 input_dev,根据 input_dev 声明的事件类型(evbit/keybit 等)自动匹配。
  • 典型场景:evdev 会匹配所有 input_dev,为每个设备创建 /dev/input/eventX 节点,用户空间通过读取这些节点获取事件。
5. 触摸事件上报(中断触发)

流程
触摸屏幕 硬件中断触发 中断处理函数 读取I2C数据 input_event上报 input_handler转发 用户空间读取

细节

  • 中断触发 :触摸屏幕时,硬件产生中断,触发 touchscreen_irq_handler

  • 中断处理函数

    c 复制代码
    static irqreturn_t touchscreen_irq_handler(int irq, void *dev_id) {
        struct input_dev *input = dev_id;
        struct i2c_client *client = input_get_drvdata(input);
        u16 x, y;
        bool pressed;
    
        // 1. 从I2C设备读取触摸坐标、压力等数据
        i2c_smbus_read_i2c_block_data(client, 0x10, 4, buffer); // 示例读数据
        x = (buffer[0] << 8) | buffer[1];
        y = (buffer[2] << 8) | buffer[3];
        pressed = (buffer[0] & 0x80) ? 1 : 0; // 假设最高位表示按压
    
        // 2. 上报按键事件(按下/松开)
        input_report_key(input, BTN_TOUCH, pressed);
    
        // 3. 上报绝对坐标事件
        input_report_abs(input, ABS_X, x);
        input_report_abs(input, ABS_Y, y);
    
        // 4. 同步事件(通知用户空间数据已就绪)
        input_sync(input);
    
        return IRQ_HANDLED;
    }
  • 用户空间读取 :通过 libinput 或直接读 /dev/input/eventX,解析事件结构体(struct input_event)获取坐标、按键状态。

三、完整流程图(合并版)

flowchart TB subgraph 设备树解析 A[设备树定义触摸屏节点] --> B[i2c总线驱动解析] B --> C[i2c_client创建并注册到i2c_bus_type] end subgraph 驱动注册与匹配 D[i2c_driver定义(含compatible)] --> E[i2c_driver注册到i2c_bus_type] E --> F{i2c_bus_type匹配} F -->|compatible一致| G[调用i2c_driver.probe] end subgraph Input子系统初始化 G --> H[硬件初始化(I2C、中断)] H --> I[input_dev分配+配置(事件类型)] I --> J[input_dev注册到Input子系统] J --> K{Input子系统匹配} K -->|事件类型匹配| L[input_handler(如evdev)关联] end subgraph 触摸事件上报 M[触摸屏幕触发硬件中断] --> N[中断处理函数调用] N --> O[读取I2C触摸数据(坐标、压力)] O --> P[input_event上报(按键、坐标)] P --> Q[input_sync同步事件] Q --> R[input_handler转发到/dev/input/eventX] R --> S[用户空间读取事件] end

四、关键总结

  1. 分层解耦:I2C总线负责硬件通信,Input子系统负责输入事件抽象,驱动只需关注"硬件数据读取"和"事件上报"。
  2. 自动匹配 :通过 compatible(I2C层)和 事件类型(Input层)实现驱动与设备、处理器的自动关联。
  3. 事件流 :触摸动作→硬件中断→驱动读数据→input_event 上报→input_handler 转发→用户空间消费。

理解这套流程后,无论是调试触摸屏驱动、扩展Input设备(如按键、传感器),还是优化事件响应,都能更清晰地定位问题~

相关推荐
迷途之人不知返13 分钟前
shell相关知识与Linux权限
linux
SPC的存折16 分钟前
3、主从复制实现同步数据过滤
linux·运维·服务器
SPC的存折18 分钟前
openEuler 24.03 MariaDB Galera 集群部署指南(cz)
linux·运维·服务器·数据库·mysql
芯智工坊28 分钟前
第19章 Mosquitto完整项目实战
网络·人工智能·mqtt·开源
SPC的存折32 分钟前
MySQL 8.0 分库分表
linux·运维·服务器·数据库·mysql
才知道的1 小时前
stm32F407学习DAY.27 ADC
stm32·嵌入式硬件·学习
cyber_两只龙宝1 小时前
【Oracle】Oracle之DQL中WHERE限制条件查询
linux·运维·数据库·云原生·oracle
senijusene1 小时前
i.MX6ULL 裸机 ECSPI 驱动开发详解:
arm开发·驱动开发·嵌入式硬件
22信通小白1 小时前
USRP初学者使用手册(基础配置及bug记录)——Linux+Clion(单台X310收发)
linux·运维·c++·5g·bug·信息与通信
网络安全许木1 小时前
自学渗透测试第14天(信息收集进阶与指纹识别)
linux·网络安全·渗透测试