完整教程:Linux 内核驱动加载机制

    Linux 内核驱动加载机制一、概述Linux 内核采用分层的设备驱动模型,通过总线(Bus)、设备(Device)和驱动(Driver)三者的匹配机制来实现硬件的管理和控制。本文将详细介绍驱动加载流程、Platform 总线机制、以及 Input 和 IIO 子系统的工作原理。

    二、Linux 设备驱动模型基础2.1 核心概念Linux 设备驱动模型基于以下三个核心组件:

    ┌──────────┐ ┌──────────┐ ┌──────────┐

    │ Device │───────▶│ Bus │◀───────│ Driver │

    │ (设备) │ 注册 │ (总线) │ 注册 │ (驱动) │

    └──────────┘ └──────────┘ └──────────┘

    匹配机制

    (match/probe)

    关键数据结构:

    // 设备结构

    struct device {

    struct device *parent;

    struct device_private *p;

    struct kobject kobj;

    const char *init_name;

    const struct device_type *type;

    struct bus_type *bus; // 所属总线

    struct device_driver *driver; // 匹配的驱动

    void *platform_data;

    void *driver_data;

    // ...

    };

    // 驱动结构

    struct device_driver {

    const char *name;

    struct bus_type *bus;

    struct module *owner;

    const struct of_device_id *of_match_table; // 设备树匹配表

    int (*probe) (struct device *dev);

    int (*remove) (struct device *dev);

    // ...

    };

    // 总线结构

    struct bus_type {

    const char *name;

    struct device *dev_root;

    int (*match)(struct device *dev, struct device_driver *drv);

    int (*probe)(struct device *dev);

    int (*remove)(struct device *dev);

    // ...

    };

    2.2 驱动加载流程代码示例:

    // 驱动模块初始化

    static int __init my_driver_init(void)

    {

    printk(KERN_INFO "My driver init\n");

    return platform_driver_register(&my_platform_driver);

    }

    module_init(my_driver_init);

    // 驱动退出

    static void __exit my_driver_exit(void)

    {

    platform_driver_unregister(&my_platform_driver);

    printk(KERN_INFO "My driver exit\n");

    }

    module_exit(my_driver_exit);

    三、Platform 总线机制3.1 Platform 总线简介Platform 总线是一种虚拟总线,用于管理集成在 SoC 内部的设备(如 GPIO、I2C 控制器、SPI 控制器等),这些设备无法通过标准总线(如 PCI、USB)自动发现。

    特点:

    不依赖物理总线设备信息通过设备树(DTS)或平台数据传递自动匹配设备和驱动3.2 Platform 设备与驱动3.2.1 Platform 设备

    struct platform_device {

    const char *name; // 设备名称

    int id; // 设备 ID

    struct device dev; // 内嵌的 device 结构

    u32 num_resources; // 资源数量

    struct resource *resource; // 资源数组(内存、中断等)

    const struct platform_device_id *id_entry;

    // ...

    };

    // 资源定义

    struct resource {

    resource_size_t start; // 起始地址

    resource_size_t end; // 结束地址

    const char *name;

    unsigned long flags; // IORESOURCE_MEM, IORESOURCE_IRQ 等

    };

    3.2.2 Platform 驱动

    struct platform_driver {

    int (*probe)(struct platform_device *);

    int (*remove)(struct platform_device *);

    void (*shutdown)(struct platform_device *);

    int (*suspend)(struct platform_device *, pm_message_t state);

    int (*resume)(struct platform_device *);

    struct device_driver driver;

    const struct platform_device_id *id_table;

    };

    3.3 完整示例3.3.1 设备树定义 (DTS)

    // 设备树节点

    my_device: my_device@40010000 {

    compatible = "vendor,my-device";

    reg = <0x40010000 0x1000>;

    interrupts = <0 25 4>;

    clocks = <&clk_gate 10>;

    status = "okay";

    };

    3.3.2 驱动实现

    #include

    #include

    #include

    #include

    // 匹配表 - 通过 compatible 匹配

    static const struct of_device_id my_device_of_match[] = {

    { .compatible = "vendor,my-device", },

    { },

    };

    MODULE_DEVICE_TABLE(of, my_device_of_match);

    // probe 函数 - 设备匹配成功后调用

    static int my_device_probe(struct platform_device *pdev)

    {

    struct resource *res;

    void __iomem *base;

    int irq;

    printk(KERN_INFO "My device probe\n");

    // 获取内存资源

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

    if (!res) {

    dev_err(&pdev->dev, "Failed to get memory resource\n");

    return -ENODEV;

    }

    // 映射寄存器地址

    base = devm_ioremap_resource(&pdev->dev, res);

    if (IS_ERR(base))

    return PTR_ERR(base);

    // 获取中断号

    irq = platform_get_irq(pdev, 0);

    if (irq < 0) {

    dev_err(&pdev->dev, "Failed to get IRQ\n");

    return irq;

    }

    // 设备特定初始化

    // ...

    dev_info(&pdev->dev, "Device probed successfully, IRQ=%d\n", irq);

    return 0;

    }

    // remove 函数

    static int my_device_remove(struct platform_device *pdev)

    {

    printk(KERN_INFO "My device remove\n");

    // 清理资源

    return 0;

    }

    // Platform 驱动结构

    static struct platform_driver my_platform_driver = {

    .probe = my_device_probe,

    .remove = my_device_remove,

    .driver = {

    .name = "my-device",

    .of_match_table = my_device_of_match,

    },

    };

    // 模块初始化和退出

    module_platform_driver(my_platform_driver);

    MODULE_LICENSE("GPL");

    MODULE_AUTHOR("Your Name");

    MODULE_DESCRIPTION("My Platform Device Driver");

    3.4 Platform 总线匹配机制

    // platform 总线的 match 函数

    static int platform_match(struct device *dev, struct device_driver *drv)

    {

    struct platform_device *pdev = to_platform_device(dev);

    struct platform_driver *pdrv = to_platform_driver(drv);

    // 1. 优先通过设备树 compatible 匹配

    if (of_driver_match_device(dev, drv))

    return 1;

    // 2. 通过 ACPI 匹配

    if (acpi_driver_match_device(dev, drv))

    return 1;

    // 3. 通过 platform_device_id 匹配

    if (pdrv->id_table)

    return platform_match_id(pdrv->id_table, pdev) != NULL;

    // 4. 通过设备名称匹配

    return (strcmp(pdev->name, drv->name) == 0);

    }

    匹配优先级:

    设备树 compatible 属性ACPI 匹配platform_device_id 表设备名称四、Input 子系统4.1 Input 子系统架构

    ┌─────────────────────────────────────────────┐

    │ User Space (用户空间) │

    │ /dev/input/event0 /dev/input/mouse0 etc. │

    └─────────────────┬───────────────────────────┘

    ┌─────────────────▼───────────────────────────┐

    │ Event Handler (事件处理层) │

    │ evdev mousedev joydev tsdev │

    └─────────────────┬───────────────────────────┘

    ┌─────────────────▼───────────────────────────┐

    │ Input Core (输入核心层) │

    │ 事件分发、设备注册、事件过滤 │

    └─────────────────┬───────────────────────────┘

    ┌─────────────────▼───────────────────────────┐

    │ Device Driver (设备驱动层) │

    │ keyboard mouse touchscreen sensor etc. │

    └─────────────────────────────────────────────┘

    4.2 Input 设备注册4.2.1 核心数据结构

    struct input_dev {

    const char *name;

    const char *phys;

    const char *uniq;

    struct input_id id;

    unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; // 支持的事件类型

    unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; // 支持的按键

    unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; // 支持的相对坐标

    unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; // 支持的绝对坐标

    int (*open)(struct input_dev *dev);

    void (*close)(struct input_dev *dev);

    // ...

    };

    4.2.2 驱动实现示例(按键驱动)

    #include

    #include

    #include

    #include

    #include

    struct button_dev {

    struct input_dev *input;

    int gpio;

    int irq;

    int key_code;

    };

    // 中断处理函数

    static irqreturn_t button_irq_handler(int irq, void *dev_id)

    {

    struct button_dev *button = dev_id;

    int state;

    // 读取 GPIO 状态

    state = gpio_get_value(button->gpio);

    // 上报按键事件

    input_report_key(button->input, button->key_code, !state);

    input_sync(button->input); // 同步事件

    return IRQ_HANDLED;

    }

    static int button_probe(struct platform_device *pdev)

    {

    struct button_dev *button;

    struct device_node *np = pdev->dev.of_node;

    int ret;

    // 分配内存

    button = devm_kzalloc(&pdev->dev, sizeof(*button), GFP_KERNEL);

    if (!button)

    return -ENOMEM;

    // 分配 input 设备

    button->input = devm_input_allocate_device(&pdev->dev);

    if (!button->input)

    return -ENOMEM;

    // 从设备树获取 GPIO

    button->gpio = of_get_named_gpio(np, "gpios", 0);

    if (!gpio_is_valid(button->gpio))

    return -EINVAL;

    // 从设备树获取按键码

    of_property_read_u32(np, "linux,code", &button->key_code);

    // 配置 input 设备

    button->input->name = "GPIO Button";

    button->input->phys = "gpio-button/input0";

    button->input->id.bustype = BUS_HOST;

    // 设置支持的事件类型和按键

    set_bit(EV_KEY, button->input->evbit);

    set_bit(button->key_code, button->input->keybit);

    // 请求 GPIO

    ret = devm_gpio_request_one(&pdev->dev, button->gpio,

    GPIOF_IN, "button-gpio");

    if (ret)

    return ret;

    // 获取中断号

    button->irq = gpio_to_irq(button->gpio);

    // 请求中断

    ret = devm_request_irq(&pdev->dev, button->irq,

    button_irq_handler,

    IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,

    "button-irq", button);

    if (ret)

    return ret;

    // 注册 input 设备

    ret = input_register_device(button->input);

    if (ret)

    return ret;

    platform_set_drvdata(pdev, button);

    dev_info(&pdev->dev, "Button device registered\n");

    return 0;

    }

    static int button_remove(struct platform_device *pdev)

    {

    struct button_dev *button = platform_get_drvdata(pdev);

    input_unregister_device(button->input);

    return 0;

    }

    static const struct of_device_id button_of_match[] = {

    { .compatible = "gpio-button", },

    { },

    };

    MODULE_DEVICE_TABLE(of, button_of_match);

    static struct platform_driver button_driver = {

    .probe = button_probe,

    .remove = button_remove,

    .driver = {

    .name = "gpio-button",

    .of_match_table = button_of_match,

    },

    };

    module_platform_driver(button_driver);

    MODULE_LICENSE("GPL");

    MODULE_DESCRIPTION("GPIO Button Input Driver");

    4.3 Input 事件类型事件类型宏定义说明EV_SYN0x00同步事件EV_KEY0x01按键事件EV_REL0x02相对坐标(鼠标)EV_ABS0x03绝对坐标(触摸屏)EV_MSC0x04杂项事件EV_LED0x11LED 事件4.4 与 Platform 总线关联

    // 设备树定义

    gpio-button {

    compatible = "gpio-button";

    gpios = <&gpio4 5 GPIO_ACTIVE_LOW>;

    linux,code = ;

    status = "okay";

    };

    关联关系:

    Input 设备驱动通过 Platform 总线注册使用 Platform 框架的资源管理(devm_* 系列函数)通过设备树匹配并获取硬件配置五、IIO (Industrial I/O) 子系统5.1 IIO 子系统架构

    ┌─────────────────────────────────────────────┐

    │ User Space │

    │ /sys/bus/iio/devices/iio:device0/ │

    │ /dev/iio:device0 │

    └─────────────────┬───────────────────────────┘

    ┌─────────────────▼───────────────────────────┐

    │ IIO Core (IIO 核心层) │

    │ 设备注册、通道管理、触发器、缓冲区 │

    └─────────────────┬───────────────────────────┘

    ┌─────────────────▼───────────────────────────┐

    │ IIO Device Driver (IIO 驱动层) │

    │ ADC DAC 加速度计 陀螺仪 磁力计 温度计 │

    └─────────────────────────────────────────────┘

    5.2 IIO 核心概念5.2.1 IIO 设备和通道

    struct iio_dev {

    int modes; // 工作模式

    int currentmode;

    struct device dev;

    struct iio_buffer *buffer; // 数据缓冲区

    int scan_bytes;

    const unsigned long *available_scan_masks;

    const unsigned long *active_scan_mask;

    struct iio_trigger *trig; // 触发器

    int num_channels; // 通道数量

    const struct iio_chan_spec *channels; // 通道描述

    const char *name;

    const struct iio_info *info; // 操作函数

    // ...

    };

    // 通道描述

    struct iio_chan_spec {

    enum iio_chan_type type; // 通道类型: IIO_VOLTAGE, IIO_TEMP 等

    int channel; // 通道索引

    int channel2;

    unsigned long address;

    int scan_index;

    struct {

    char sign; // 's'=有符号, 'u'=无符号

    u8 realbits; // 有效位数

    u8 storagebits; // 存储位数

    u8 shift;

    enum iio_endian endianness;

    } scan_type;

    long info_mask_separate; // 单独的信息掩码

    long info_mask_shared_by_type; // 按类型共享的信息掩码

    // ...

    };

    5.2.2 IIO 信息结构

    struct iio_info {

    int (*read_raw)(struct iio_dev *indio_dev,

    struct iio_chan_spec const *chan,

    int *val, int *val2, long mask);

    int (*write_raw)(struct iio_dev *indio_dev,

    struct iio_chan_spec const *chan,

    int val, int val2, long mask);

    int (*read_event_config)(struct iio_dev *indio_dev,

    const struct iio_chan_spec *chan,

    enum iio_event_type type,

    enum iio_event_direction dir);

    // ...

    };

    5.3 IIO 驱动示例(ADC 驱动)

    #include

    #include

    #include

    #include

    #define ADC_CHANNELS 4

    struct my_adc {

    void __iomem *base;

    struct iio_dev *indio_dev;

    };

    // 读取原始数据

    static int my_adc_read_raw(struct iio_dev *indio_dev,

    struct iio_chan_spec const *chan,

    int *val, int *val2, long mask)

    {

    struct my_adc *adc = iio_priv(indio_dev);

    u32 raw_value;

    switch (mask) {

    case IIO_CHAN_INFO_RAW:

    // 读取 ADC 寄存器

    raw_value = readl(adc->base + chan->channel * 4);

    *val = raw_value & 0xFFF; // 12-bit ADC

    return IIO_VAL_INT;

    case IIO_CHAN_INFO_SCALE:

    // 返回缩放因子: 3.3V / 4096 = 0.0008056640625

    *val = 3300; // mV

    *val2 = 12; // 2^12

    return IIO_VAL_FRACTIONAL_LOG2;

    default:

    return -EINVAL;

    }

    }

    static const struct iio_info my_adc_info = {

    .read_raw = my_adc_read_raw,

    };

    // 定义通道

    #define MY_ADC_CHANNEL(idx) { \

    .type = IIO_VOLTAGE, \

    .indexed = 1, \

    .channel = idx, \

    .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \

    .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\

    .scan_index = idx, \

    .scan_type = { \

    .sign = 'u', \

    .realbits = 12, \

    .storagebits = 16, \

    .shift = 0, \

    }, \

    }

    static const struct iio_chan_spec my_adc_channels[] = {

    MY_ADC_CHANNEL(0),

    MY_ADC_CHANNEL(1),

    MY_ADC_CHANNEL(2),

    MY_ADC_CHANNEL(3),

    };

    static int my_adc_probe(struct platform_device *pdev)

    {

    struct iio_dev *indio_dev;

    struct my_adc *adc;

    struct resource *res;

    int ret;

    // 分配 IIO 设备

    indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));

    if (!indio_dev)

    return -ENOMEM;

    adc = iio_priv(indio_dev);

    // 获取寄存器地址

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

    adc->base = devm_ioremap_resource(&pdev->dev, res);

    if (IS_ERR(adc->base))

    return PTR_ERR(adc->base);

    // 配置 IIO 设备

    indio_dev->dev.parent = &pdev->dev;

    indio_dev->name = "my-adc";

    indio_dev->info = &my_adc_info;

    indio_dev->modes = INDIO_DIRECT_MODE;

    indio_dev->channels = my_adc_channels;

    indio_dev->num_channels = ARRAY_SIZE(my_adc_channels);

    // 注册 IIO 设备

    ret = devm_iio_device_register(&pdev->dev, indio_dev);

    if (ret) {

    dev_err(&pdev->dev, "Failed to register IIO device\n");

    return ret;

    }

    platform_set_drvdata(pdev, indio_dev);

    dev_info(&pdev->dev, "ADC device registered\n");

    return 0;

    }

    static const struct of_device_id my_adc_of_match[] = {

    { .compatible = "vendor,my-adc", },

    { },

    };

    MODULE_DEVICE_TABLE(of, my_adc_of_match);

    static struct platform_driver my_adc_driver = {

    .probe = my_adc_probe,

    .driver = {

    .name = "my-adc",

    .of_match_table = my_adc_of_match,

    },

    };

    module_platform_driver(my_adc_driver);

    MODULE_LICENSE("GPL");

    MODULE_DESCRIPTION("My ADC IIO Driver");

    5.4 IIO 用户空间接口5.4.1 Sysfs 接口

    # IIO 设备目录

    /sys/bus/iio/devices/iio:device0/

    ├── in_voltage0_raw # 通道 0 原始值

    ├── in_voltage1_raw # 通道 1 原始值

    ├── in_voltage_scale # 缩放因子

    ├── name # 设备名称

    ├── sampling_frequency # 采样频率

    └── buffer/

    ├── enable # 使能缓冲区

    └── length # 缓冲区长度

    # 读取 ADC 值

    cat /sys/bus/iio/devices/iio:device0/in_voltage0_raw

    # 输出: 2048

    cat /sys/bus/iio/devices/iio:device0/in_voltage_scale

    # 输出: 0.000805664

    # 实际电压 = raw * scale = 2048 * 0.000805664 ≈ 1.65V

    5.4.2 字符设备接口

    // 用户空间程序读取 IIO 数据

    #include

    #include

    #include

    #include

    int main() {

    int fd;

    uint16_t data[4]; // 4 个通道

    // 使能缓冲区

    system("echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_voltage0_en");

    system("echo 1 > /sys/bus/iio/devices/iio:device0/buffer/enable");

    // 打开字符设备

    fd = open("/dev/iio:device0", O_RDONLY);

    if (fd < 0) {

    perror("Failed to open device");

    return -1;

    }

    // 读取数据

    if (read(fd, data, sizeof(data)) > 0) {

    printf("ADC Value: %u\n", data[0]);

    }

    close(fd);

    return 0;

    }

    5.5 与 Platform 总线关联

    // 设备树定义

    adc: adc@40012000 {

    compatible = "vendor,my-adc";

    reg = <0x40012000 0x400>;

    clocks = <&rcc 0 28>;

    vref-supply = <®_vref>;

    status = "okay";

    };

    关联关系:

    IIO 驱动通过 Platform 总线框架注册使用 Platform 框架获取硬件资源(寄存器、时钟等)通过设备树传递硬件配置参数六、三者关系总结6.1 层次关系图

    ┌─────────────────────────────────────────────────┐

    │ User Space Application │

    │ /dev/input/event0 /sys/bus/iio/devices/... │

    └─────────────┬───────────────────────────────────┘

    ┌─────────────▼───────────────────────────────────┐

    │ Subsystems (子系统层) │

    │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │

    │ │ Input │ │ IIO │ │ Other │ │

    │ │ Subsystem│ │ Subsystem│ │Subsystems│ │

    │ └────┬─────┘ └────┬─────┘ └────┬─────┘ │

    │ │ │ │ │

    └───────┼─────────────┼─────────────┼─────────────┘

    │ │ │

    ┌───────▼─────────────▼─────────────▼─────────────┐

    │ Device Driver Model (设备驱动模型) │

    │ ┌──────────────────────────────────────┐ │

    │ │ Platform Bus Framework │ │

    │ │ (Platform 总线框架 - 核心基础) │ │

    │ └───┬──────────────────────────────┬───┘ │

    │ │ Device Match & Probe │ │

    └──────┼──────────────────────────────┼───────────┘

    │ │

    ┌──────▼──────┐ ┌────────▼──────┐

    │ Device │ │ Driver │

    │ (设备树) │ │ (驱动代码) │

    └─────────────┘ └───────────────┘

    6.2 关联性分析6.2.1 Platform 总线作为基础

    // Input 设备驱动基于 Platform

    static struct platform_driver input_button_driver = {

    .probe = button_probe,

    .driver = {

    .name = "gpio-button",

    .of_match_table = button_of_match,

    },

    };

    // IIO 设备驱动基于 Platform

    static struct platform_driver iio_adc_driver = {

    .probe = adc_probe,

    .driver = {

    .name = "my-adc",

    .of_match_table = adc_of_match,

    },

    };

    6.2.2 数据流向

    硬件中断/数据

    Platform Driver (底层驱动)

    Input/IIO Subsystem (子系统处理)

    Device Node (/dev/input/*, /sys/bus/iio/*)

    User Space Application

    6.3 对比表特性Platform 总线Input 子系统IIO 子系统定位基础设备框架人机交互设备工业传感器设备类型SoC 内部设备键盘、鼠标、触摸屏ADC、DAC、传感器用户接口sysfs/dev/input/*/sys/bus/iio/*数据特点-事件驱动连续采样/触发依赖关系独立基础框架依赖 Platform依赖 Platform注册函数platform_driver_registerinput_register_deviceiio_device_register七、实战案例:触摸屏驱动7.1 设备树定义

    touchscreen: touchscreen@48 {

    compatible = "edt,edt-ft5406";

    reg = <0x48>;

    interrupt-parent = <&gpio2>;

    interrupts = <5 IRQ_TYPE_EDGE_FALLING>;

    reset-gpios = <&gpio5 9 GPIO_ACTIVE_LOW>;

    touchscreen-size-x = <800>;

    touchscreen-size-y = <480>;

    status = "okay";

    };

    7.2 驱动实现要点

    // 1. Platform 层 - I2C 设备注册

    static int ts_probe(struct i2c_client *client,

    const struct i2c_device_id *id)

    {

    struct input_dev *input;

    int error;

    // 2. Input 层 - 分配 input 设备

    input = devm_input_allocate_device(&client->dev);

    // 3. 配置 input 属性

    input->name = "EDT-FT5406";

    set_bit(EV_ABS, input->evbit);

    set_bit(EV_KEY, input->evbit);

    set_bit(BTN_TOUCH, input->keybit);

    input_set_abs_params(input, ABS_X, 0, 800, 0, 0);

    input_set_abs_params(input, ABS_Y, 0, 480, 0, 0);

    // 4. 注册 input 设备

    error = input_register_device(input);

    // 5. 请求中断

    devm_request_threaded_irq(&client->dev, client->irq,

    NULL, ts_isr_handler,

    IRQF_TRIGGER_FALLING | IRQF_ONESHOT,

    client->name, tsdata);

    return 0;

    }

    // 中断处理 - 上报触摸事件

    static irqreturn_t ts_isr_handler(int irq, void *dev_id)

    {

    struct ts_data *tsdata = dev_id;

    // 读取触摸点坐标

    ts_read_touchdata(tsdata, &x, &y);

    // 上报绝对坐标事件

    input_report_abs(tsdata->input, ABS_X, x);

    input_report_abs(tsdata->input, ABS_Y, y);

    input_report_key(tsdata->input, BTN_TOUCH, 1);

    input_sync(tsdata->input);

    return IRQ_HANDLED;

    }

    八、调试技巧8.1 Platform 设备调试

    # 查看已注册的 platform 设备

    ls /sys/bus/platform/devices/

    # 查看设备树信息

    ls /proc/device-tree/

    # 查看驱动绑定情况

    cat /sys/bus/platform/drivers/my-driver/bind

    # 查看内核日志

    dmesg | grep platform

    8.2 Input 设备调试

    # 查看 input 设备列表

    cat /proc/bus/input/devices

    # 实时查看事件

    hexdump /dev/input/event0

    # 使用 evtest 工具

    evtest /dev/input/event0

    8.3 IIO 设备调试

    # 查看 IIO 设备

    ls /sys/bus/iio/devices/

    # 读取通道值

    cat /sys/bus/iio/devices/iio:device0/in_voltage0_raw

    # 查看设备信息

    cat /sys/bus/iio/devices/iio:device0/name

    # 使用 iio_info 工具

    iio_info

    I2C 子系统与 Input 子系统配合详解一、整体架构设计1.1 分层协作关系

    ┌─────────────────────────────────────────────────┐

    │ User Space (用户空间) │

    │ /dev/input/event0 evdev libinput │

    └─────────────────┬───────────────────────────────┘

    │ ioctl/read

    ┌─────────────────▼───────────────────────────────┐

    │ Input Event Handler Layer (事件处理层) │

    │ evdev.c mousedev.c joydev.c │

    └─────────────────┬───────────────────────────────┘

    │ input_event()

    ┌─────────────────▼───────────────────────────────┐

    │ Input Core Layer (输入核心层) │

    │ input.c 事件分发、过滤、同步 │

    └─────────────────┬───────────────────────────────┘

    │ input_report_*()

    ┌─────────────────▼───────────────────────────────┐

    │ Touch Driver Layer (触摸屏驱动层) │

    │ edt-ft5x06.c goodix.c 等 │

    └─────────────────┬───────────────────────────────┘

    │ i2c_transfer()

    ┌─────────────────▼───────────────────────────────┐

    │ I2C Bus Layer (I2C总线层) │

    │ i2c-core.c i2c-dev.c │

    └─────────────────┬───────────────────────────────┘

    │ 硬件操作

    ┌─────────────────▼───────────────────────────────┐

    │ I2C Controller Driver (I2C控制器驱动) │

    │ i2c-imx.c i2c-s3c2410.c 等 │

    └─────────────────┬───────────────────────────────┘

    [触摸屏芯片 IC]

    二、关键数据结构2.1 I2C 客户端结构

    struct i2c_client {

    unsigned short flags; // I2C_CLIENT_* 标志

    unsigned short addr; // 芯片地址(7位或10位)

    char name[I2C_NAME_SIZE]; // 设备名称

    struct i2c_adapter *adapter; // 所属适配器

    struct device dev; // 设备模型

    int irq; // 中断号

    struct list_head detected;

    // ...

    };

    2.2 触摸屏驱动私有数据

    struct edt_ft5x06_ts_data {

    struct i2c_client *client; // I2C 客户端

    struct input_dev *input; // Input 设备

    u16 num_x; // X 轴分辨率

    u16 num_y; // Y 轴分辨率

    struct gpio_desc *reset_gpio; // 复位 GPIO

    struct gpio_desc *wake_gpio; // 唤醒 GPIO

    struct regulator *vcc; // 电源

    struct touchscreen_properties prop; // 触摸屏属性

    int threshold; // 触摸阈值

    int gain; // 增益

    int offset; // 偏移

    enum edt_ver version; // 芯片版本

    // 多点触控支持

    struct edt_ft5x06_point points[MAX_SUPPORT_POINTS];

    };

    // 触摸点数据

    struct edt_ft5x06_point {

    u16 x;

    u16 y;

    u8 id;

    u8 event; // 按下/抬起/移动

    };

    三、详细工作流程3.1 驱动注册流程

    // 1. I2C 驱动定义

    static struct i2c_driver edt_ft5x06_ts_driver = {

    .driver = {

    .name = "edt_ft5x06",

    .of_match_table = edt_ft5x06_of_match, // 设备树匹配

    .pm = &edt_ft5x06_ts_pm_ops, // 电源管理

    },

    .id_table = edt_ft5x06_ts_id, // I2C ID 表

    .probe = edt_ft5x06_ts_probe, // 探测函数

    .remove = edt_ft5x06_ts_remove, // 移除函数

    };

    // 设备树匹配表

    static const struct of_device_id edt_ft5x06_of_match[] = {

    { .compatible = "edt,edt-ft5206", .data = &edt_ft5x06_data },

    { .compatible = "edt,edt-ft5406", .data = &edt_ft5x06_data },

    { .compatible = "focaltech,ft6236", .data = &edt_ft6236_data },

    { /* sentinel */ }

    };

    MODULE_DEVICE_TABLE(of, edt_ft5x06_of_match);

    // I2C 设备 ID 表

    static const struct i2c_device_id edt_ft5x06_ts_id[] = {

    { "edt-ft5x06", 0 },

    { }

    };

    MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id);

    // 注册 I2C 驱动

    module_i2c_driver(edt_ft5x06_ts_driver);

    3.2 Probe 函数详解

    static int edt_ft5x06_ts_probe(struct i2c_client *client,

    const struct i2c_device_id *id)

    {

    struct edt_ft5x06_ts_data *tsdata;

    struct input_dev *input;

    unsigned long irq_flags;

    int error;

    // ========== 第一步: 分配私有数据 ==========

    tsdata = devm_kzalloc(&client->dev, sizeof(*tsdata), GFP_KERNEL);

    if (!tsdata)

    return -ENOMEM;

    // ========== 第二步: 初始化 I2C 通信 ==========

    // 检查 I2C 功能性

    if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {

    dev_err(&client->dev, "I2C functionality not supported\n");

    return -ENODEV;

    }

    tsdata->client = client;

    i2c_set_clientdata(client, tsdata); // 保存私有数据

    // ========== 第三步: 硬件资源初始化 ==========

    // 1. 获取电源

    tsdata->vcc = devm_regulator_get(&client->dev, "vcc");

    if (IS_ERR(tsdata->vcc)) {

    error = PTR_ERR(tsdata->vcc);

    if (error != -EPROBE_DEFER)

    dev_err(&client->dev, "Failed to get vcc regulator: %d\n", error);

    return error;

    }

    // 2. 获取 GPIO

    tsdata->reset_gpio = devm_gpiod_get_optional(&client->dev,

    "reset", GPIOD_OUT_HIGH);

    if (IS_ERR(tsdata->reset_gpio))

    return PTR_ERR(tsdata->reset_gpio);

    tsdata->wake_gpio = devm_gpiod_get_optional(&client->dev,

    "wake", GPIOD_OUT_LOW);

    if (IS_ERR(tsdata->wake_gpio))

    return PTR_ERR(tsdata->wake_gpio);

    // 3. 上电时序

    error = regulator_enable(tsdata->vcc);

    if (error) {

    dev_err(&client->dev, "Failed to enable vcc: %d\n", error);

    return error;

    }

    msleep(10); // 等待电源稳定

    // 4. 复位芯片

    if (tsdata->reset_gpio) {

    gpiod_set_value_cansleep(tsdata->reset_gpio, 0); // 拉低

    msleep(5);

    gpiod_set_value_cansleep(tsdata->reset_gpio, 1); // 拉高

    msleep(300); // 等待芯片启动

    }

    // ========== 第四步: 识别芯片型号 ==========

    error = edt_ft5x06_ts_identify(client, tsdata);

    if (error) {

    dev_err(&client->dev, "Failed to identify chip\n");

    goto err_disable_regulator;

    }

    // ========== 第五步: 创建 Input 设备 ==========

    input = devm_input_allocate_device(&client->dev);

    if (!input) {

    dev_err(&client->dev, "Failed to allocate input device\n");

    error = -ENOMEM;

    goto err_disable_regulator;

    }

    tsdata->input = input;

    input->name = "EDT-FT5x06 Touchscreen";

    input->id.bustype = BUS_I2C; // 标识为 I2C 设备

    input->dev.parent = &client->dev;

    // ========== 第六步: 配置 Input 能力 ==========

    // 1. 设置支持的事件类型

    set_bit(EV_SYN, input->evbit); // 同步事件

    set_bit(EV_KEY, input->evbit); // 按键事件

    set_bit(EV_ABS, input->evbit); // 绝对坐标事件

    // 2. 设置触摸按键

    set_bit(BTN_TOUCH, input->keybit);

    // 3. 配置绝对坐标参数

    input_set_abs_params(input, ABS_X, 0, tsdata->num_x - 1, 0, 0);

    input_set_abs_params(input, ABS_Y, 0, tsdata->num_y - 1, 0, 0);

    // 4. 多点触控支持(MT协议)

    input_mt_init_slots(input, MAX_SUPPORT_POINTS, INPUT_MT_DIRECT);

    input_set_abs_params(input, ABS_MT_POSITION_X, 0, tsdata->num_x - 1, 0, 0);

    input_set_abs_params(input, ABS_MT_POSITION_Y, 0, tsdata->num_y - 1, 0, 0);

    // ========== 第七步: 触摸屏属性解析 ==========

    touchscreen_parse_properties(input, true, &tsdata->prop);

    // ========== 第八步: 配置芯片参数 ==========

    error = edt_ft5x06_ts_set_regs(tsdata);

    if (error)

    goto err_disable_regulator;

    // ========== 第九步: 注册 Input 设备 ==========

    error = input_register_device(input);

    if (error) {

    dev_err(&client->dev, "Failed to register input device: %d\n", error);

    goto err_disable_regulator;

    }

    // ========== 第十步: 注册中断处理 ==========

    irq_flags = irq_get_trigger_type(client->irq);

    if (irq_flags == IRQF_TRIGGER_NONE)

    irq_flags = IRQF_TRIGGER_FALLING; // 默认下降沿触发

    irq_flags |= IRQF_ONESHOT; // 单次触发模式

    error = devm_request_threaded_irq(&client->dev, client->irq,

    NULL,

    edt_ft5x06_ts_isr,

    irq_flags,

    client->name,

    tsdata);

    if (error) {

    dev_err(&client->dev, "Failed to request IRQ: %d\n", error);

    goto err_unregister_device;

    }

    // ========== 第十一步: 创建 sysfs 属性 ==========

    error = sysfs_create_group(&client->dev.kobj, &edt_ft5x06_attr_group);

    if (error)

    goto err_unregister_device;

    dev_info(&client->dev,

    "EDT FT5x06 initialized: IRQ %d, Reset pin %d, %dx%d\n",

    client->irq,

    tsdata->reset_gpio ? desc_to_gpio(tsdata->reset_gpio) : -1,

    tsdata->num_x, tsdata->num_y);

    return 0;

    err_unregister_device:

    input_unregister_device(input);

    err_disable_regulator:

    regulator_disable(tsdata->vcc);

    return error;

    }

    3.3 I2C 通信函数

    // 读取触摸数据

    static int edt_ft5x06_ts_readwrite(struct i2c_client *client,

    u16 wr_len, u8 *wr_buf,

    u16 rd_len, u8 *rd_buf)

    {

    struct i2c_msg msgs[2];

    int ret;

    // 写消息

    msgs[0].addr = client->addr;

    msgs[0].flags = 0;

    msgs[0].len = wr_len;

    msgs[0].buf = wr_buf;

    // 读消息

    msgs[1].addr = client->addr;

    msgs[1].flags = I2C_M_RD;

    msgs[1].len = rd_len;

    msgs[1].buf = rd_buf;

    ret = i2c_transfer(client->adapter, msgs, 2);

    if (ret != 2) {

    dev_err(&client->dev, "I2C transfer failed: %d\n", ret);

    return ret < 0 ? ret : -EIO;

    }

    return 0;

    }

    // 读取寄存器

    static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata,

    u8 addr)

    {

    u8 wrbuf[1], rdbuf[1];

    int error;

    wrbuf[0] = addr;

    error = edt_ft5x06_ts_readwrite(tsdata->client,

    1, wrbuf,

    1, rdbuf);

    if (error)

    return error;

    return rdbuf[0];

    }

    // 写入寄存器

    static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata,

    u8 addr, u8 value)

    {

    u8 wrbuf[2];

    wrbuf[0] = addr;

    wrbuf[1] = value;

    return i2c_master_send(tsdata->client, wrbuf, 2);

    }

    3.4 中断处理详解

    static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)

    {

    struct edt_ft5x06_ts_data *tsdata = dev_id;

    struct device *dev = &tsdata->client->dev;

    u8 rdbuf[63]; // 最大读取63字节

    int i, type, x, y, id;

    int offset, tplen, datalen, crclen;

    int error;

    // ========== 第一步: 从芯片读取触摸数据 ==========

    // FT5x06 寄存器布局:

    // 0x00: 设备模式

    // 0x01: 手势ID

    // 0x02: 触摸点数量 (TD_STATUS)

    // 0x03-0x08: 第1个触摸点数据

    // 0x09-0x0E: 第2个触摸点数据

    // ...

    memset(rdbuf, 0, sizeof(rdbuf));

    // 计算需要读取的字节数

    tplen = 6; // 每个触摸点6字节

    crclen = 1; // CRC校验1字节

    switch (tsdata->version) {

    case EDT_M06:

    datalen = tplen * MAX_SUPPORT_POINTS + crclen;

    offset = 5; // 数据起始偏移

    break;

    case EDT_M09:

    case EDT_M12:

    case GENERIC_FT:

    datalen = tplen * MAX_SUPPORT_POINTS;

    offset = 3; // TD_STATUS 寄存器偏移

    break;

    default:

    goto out;

    }

    // 读取触摸数据

    error = edt_ft5x06_ts_readwrite(tsdata->client,

    1, &offset,

    datalen, &rdbuf[offset]);

    if (error) {

    dev_err_ratelimited(dev, "Unable to fetch data: %d\n", error);

    goto out;

    }

    // ========== 第二步: 解析触摸点数量 ==========

    int num_points;

    switch (tsdata->version) {

    case EDT_M06:

    num_points = rdbuf[2] & 0x0F; // 低4位表示触摸点数

    break;

    case EDT_M09:

    case EDT_M12:

    case GENERIC_FT:

    num_points = rdbuf[2] & 0x0F;

    if (num_points > MAX_SUPPORT_POINTS) {

    dev_warn_ratelimited(dev, "Invalid number of points: %d\n", num_points);

    goto out;

    }

    break;

    default:

    goto out;

    }

    // ========== 第三步: 解析每个触摸点 ==========

    for (i = 0; i < num_points; i++) {

    u8 *buf = &rdbuf[offset + i * tplen];

    // 解析触摸事件类型

    // buf[0]: XH (高4位:事件类型, 低4位:X坐标高4位)

    // buf[1]: XL (X坐标低8位)

    // buf[2]: YH (高4位:触摸点ID, 低4位:Y坐标高4位)

    // buf[3]: YL (Y坐标低8位)

    // buf[4]: 压力值

    // buf[5]: 触摸面积

    type = (buf[0] >> 6) & 0x03;

    // 0: 按下 (Touch Down)

    // 1: 抬起 (Touch Up)

    // 2: 接触 (Touch Contact)

    // 3: 保留

    // 解析坐标

    x = ((buf[0] & 0x0F) << 8) | buf[1];

    y = ((buf[2] & 0x0F) << 8) | buf[3];

    // 解析触摸点ID

    id = (buf[2] >> 4) & 0x0F;

    // 坐标转换(考虑旋转、镜像等)

    touchscreen_report_pos(tsdata->input, &tsdata->prop, x, y, true);

    // ========== 第四步: 上报 MT 协议事件 ==========

    // MT 协议 B (Slot-based)

    input_mt_slot(tsdata->input, id); // 选择槽位

    if (type == 0x01) {

    // 抬起事件

    input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER, false);

    } else {

    // 按下/移动事件

    input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER, true);

    input_report_abs(tsdata->input, ABS_MT_POSITION_X, x);

    input_report_abs(tsdata->input, ABS_MT_POSITION_Y, y);

    }

    }

    // ========== 第五步: 上报传统单点事件(向后兼容) ==========

    input_mt_report_pointer_emulation(tsdata->input, true);

    // ========== 第六步: 同步事件 ==========

    input_sync(tsdata->input); // 发送 EV_SYN 事件

    out:

    return IRQ_HANDLED;

    }

    四、关键技术点4.1 多点触控协议

    // MT 协议 A (已废弃)

    input_report_abs(input, ABS_MT_POSITION_X, x);

    input_report_abs(input, ABS_MT_POSITION_Y, y);

    input_mt_sync(input); // 触摸点分隔

    input_sync(input); // 数据帧结束

    // MT 协议 B (推荐使用)

    input_mt_slot(input, slot); // 指定槽位

    input_mt_report_slot_state(input, MT_TOOL_FINGER, true);

    input_report_abs(input, ABS_MT_POSITION_X, x);

    input_report_abs(input, ABS_MT_POSITION_Y, y);

    input_sync(input);

    4.2 坐标变换

    // 处理旋转、镜像、交换轴

    void touchscreen_report_pos(struct input_dev *input,

    struct touchscreen_properties *prop,

    unsigned int x, unsigned int y,

    bool multitouch)

    {

    // 交换 X/Y 轴

    if (prop->swap_x_y)

    swap(x, y);

    // X 轴镜像

    if (prop->invert_x)

    x = prop->max_x - x;

    // Y 轴镜像

    if (prop->invert_y)

    y = prop->max_y - y;

    // 上报坐标

    if (multitouch) {

    input_report_abs(input, ABS_MT_POSITION_X, x);

    input_report_abs(input, ABS_MT_POSITION_Y, y);

    } else {

    input_report_abs(input, ABS_X, x);

    input_report_abs(input, ABS_Y, y);

    }

    }

    4.3 电源管理

    static int edt_ft5x06_ts_suspend(struct device *dev)

    {

    struct i2c_client *client = to_i2c_client(dev);

    struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);

    disable_irq(client->irq);

    // 进入睡眠模式

    edt_ft5x06_register_write(tsdata, 0xA5, 0x03);

    // 关闭电源

    if (tsdata->vcc)

    regulator_disable(tsdata->vcc);

    return 0;

    }

    static int edt_ft5x06_ts_resume(struct device *dev)

    {

    struct i2c_client *client = to_i2c_client(dev);

    struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);

    // 上电

    if (tsdata->vcc)

    regulator_enable(tsdata->vcc);

    msleep(10);

    // 复位芯片

    if (tsdata->reset_gpio) {

    gpiod_set_value_cansleep(tsdata->reset_gpio, 0);

    msleep(5);

    gpiod_set_value_cansleep(tsdata->reset_gpio, 1);

    msleep(300);

    }

    // 恢复寄存器配置

    edt_ft5x06_ts_set_regs(tsdata);

    enable_irq(client->irq);

    return 0;

    }

    static SIMPLE_DEV_PM_OPS(edt_ft5x06_ts_pm_ops,

    edt_ft5x06_ts_suspend,

    edt_ft5x06_ts_resume);

    五、数据流向图

    触摸屏芯片 (FT5406)

    ↓ [硬件中断]

    GPIO 中断控制器

    ↓ [IRQ]

    中断处理函数 (ts_isr_handler)

    ↓ [I2C读取]

    i2c_transfer() → I2C 控制器驱动

    ↓ [返回触摸数据]

    解析坐标、事件类型

    ↓ [调用 Input 函数]

    input_report_abs(ABS_MT_POSITION_X/Y)

    input_mt_slot() / input_mt_report_slot_state()

    ↓ [事件缓冲]

    input_sync() → Input Core

    ↓ [事件分发]

    evdev / mousedev / joydev

    ↓ [字符设备]

    /dev/input/event0

    ↓ [read/poll]

    用户空间应用 (Qt/Android/X11)

    参考资料:

    Linux Device Drivers (LDD3)Linux 内核文档: Documentation/driver-api/Input 子系统文档: Documentation/input/IIO 子系统文档: Documentation/iio/设备树规范: Devicetree Specification