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

发布于:2025-06-24 ⋅ 阅读:(19) ⋅ 点赞:(0)

触摸屏(典型 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总线

细节

  • 设备树中定义触摸屏节点(示例片段):
    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
    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 函数核心逻辑:
    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
  • 中断处理函数
    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设备(如按键、传感器),还是优化事件响应,都能更清晰地定位问题~


网站公告

今日签到

点亮在社区的每一天
去签到