Skip to content

Linux 设备驱动模型总览

设计目标

Linux 设备驱动模型(Driver Model)在 2.6 内核引入,解决了早期内核中设备管理混乱的问题:

  • 统一的设备发现与注册:无论是 PCI、USB、I2C 还是 Platform 设备,都通过同一套框架管理
  • 电源管理:统一的 suspend/resume 回调链
  • 热插拔支持:设备插入/拔出时自动加载/卸载驱动
  • sysfs 导出:设备拓扑结构在 /sys 下可见,用户空间可查询

核心数据结构

kobject — 最基础的对象

c
struct kobject {
    const char          *name;       /* sysfs 目录名 */
    struct list_head    entry;
    struct kobject      *parent;     /* 父对象(构成树形结构) */
    struct kset         *kset;
    const struct kobj_type *ktype;
    struct kernfs_node  *sd;         /* sysfs 节点 */
    struct kref         kref;        /* 引用计数 */
};

kobject 通常嵌入在更大的结构体中,通过 container_of() 获取外层结构:

c
struct my_device {
    int id;
    struct kobject kobj;   /* 嵌入 kobject */
};

/* 从 kobject 指针获取 my_device */
struct my_device *dev = container_of(kobj, struct my_device, kobj);

bus_type — 总线

c
struct bus_type {
    const char *name;                /* 总线名称,如 "i2c", "spi", "platform" */
    int (*match)(struct device *dev, struct device_driver *drv);  /* 匹配函数 */
    int (*probe)(struct device *dev);
    int (*remove)(struct device *dev);
    int (*suspend)(struct device *dev, pm_message_t state);
    int (*resume)(struct device *dev);
    const struct dev_pm_ops *pm;
};

device — 设备

c
struct device {
    struct kobject kobj;
    struct device *parent;
    struct bus_type *bus;            /* 所属总线 */
    struct device_driver *driver;    /* 绑定的驱动 */
    void *platform_data;             /* 平台相关数据(旧式) */
    void *driver_data;               /* 驱动私有数据 */
    struct device_node *of_node;     /* 设备树节点 */
    const struct device_type *type;
    struct dev_pm_info power;
};

device_driver — 驱动

c
struct device_driver {
    const char *name;
    struct bus_type *bus;
    int (*probe)(struct device *dev);
    int (*remove)(struct device *dev);
    void (*shutdown)(struct device *dev);
    const struct dev_pm_ops *pm;
    const struct of_device_id *of_match_table;  /* 设备树匹配表 */
    const struct acpi_device_id *acpi_match_table;
};

设备与驱动的匹配流程

注册设备 (device_register)          注册驱动 (driver_register)
         │                                    │
         ▼                                    ▼
    bus->match()  ◄──────────────────────────►

    匹配成功


    driver->probe(dev)

    ├── 返回 0:绑定成功,dev->driver = drv
    └── 返回负值:绑定失败,尝试下一个驱动

匹配优先级(以 Platform 总线为例):

  1. 设备树 compatible 属性匹配 of_match_table
  2. ACPI _HID 匹配 acpi_match_table
  3. 设备名称字符串匹配驱动名称

sysfs 文件系统

设备模型在 /sys 下构建了完整的设备拓扑:

/sys/
├── bus/                    # 按总线类型组织
│   ├── i2c/
│   │   ├── devices/        # 符号链接到 /sys/devices/
│   │   └── drivers/        # 该总线上的驱动
│   └── platform/
├── class/                  # 按设备类型组织(如 /sys/class/net/)
├── devices/                # 真实设备树(按物理拓扑)
│   └── platform/
│       └── my-device/
│           ├── driver/     # 符号链接到绑定的驱动
│           └── power/
└── module/                 # 已加载模块

自定义 sysfs 属性

c
/* 定义属性(读写) */
static ssize_t status_show(struct device *dev,
                           struct device_attribute *attr, char *buf)
{
    struct my_priv *priv = dev_get_drvdata(dev);
    return sysfs_emit(buf, "%d\n", priv->status);
}

static ssize_t status_store(struct device *dev,
                            struct device_attribute *attr,
                            const char *buf, size_t count)
{
    struct my_priv *priv = dev_get_drvdata(dev);
    int val;
    if (kstrtoint(buf, 10, &val))
        return -EINVAL;
    priv->status = val;
    return count;
}

static DEVICE_ATTR_RW(status);   /* 生成 dev_attr_status */

/* 在 probe 中注册 */
device_create_file(dev, &dev_attr_status);

/* 在 remove 中注销 */
device_remove_file(dev, &dev_attr_status);

驱动私有数据

驱动通常需要为每个设备维护私有状态,标准做法:

c
struct my_priv {
    struct device *dev;
    void __iomem *base;
    int irq;
    spinlock_t lock;
    /* ... */
};

static int my_probe(struct platform_device *pdev)
{
    struct my_priv *priv;

    priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;

    priv->dev = &pdev->dev;
    platform_set_drvdata(pdev, priv);   /* 存储私有数据 */
    /* ... */
    return 0;
}

static int my_remove(struct platform_device *pdev)
{
    struct my_priv *priv = platform_get_drvdata(pdev);  /* 取回私有数据 */
    /* 清理资源 */
    return 0;
}

devm_* 资源管理

devm_(device-managed)系列函数在设备解绑时自动释放资源,避免 remove() 中遗漏清理:

c
/* 内存分配 */
void *ptr = devm_kzalloc(dev, size, GFP_KERNEL);

/* I/O 内存映射 */
void __iomem *base = devm_ioremap_resource(dev, res);

/* 中断注册 */
devm_request_irq(dev, irq, handler, flags, name, dev_id);

/* GPIO */
struct gpio_desc *gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);

/* 时钟 */
struct clk *clk = devm_clk_get(dev, "apb_pclk");

/* regulator */
struct regulator *vdd = devm_regulator_get(dev, "vdd");

使用 devm_* 后,remove() 函数通常只需要几行代码,大幅减少资源泄漏风险。

电源管理回调

c
static int my_suspend(struct device *dev)
{
    struct my_priv *priv = dev_get_drvdata(dev);
    /* 保存寄存器状态,关闭时钟 */
    clk_disable_unprepare(priv->clk);
    return 0;
}

static int my_resume(struct device *dev)
{
    struct my_priv *priv = dev_get_drvdata(dev);
    clk_prepare_enable(priv->clk);
    /* 恢复寄存器状态 */
    return 0;
}

static const struct dev_pm_ops my_pm_ops = {
    SET_SYSTEM_SLEEP_PM_OPS(my_suspend, my_resume)
};

下一步

褚成志的笔记