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 总线为例):
- 设备树
compatible属性匹配of_match_table - ACPI
_HID匹配acpi_match_table - 设备名称字符串匹配驱动名称
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)
};下一步
- Platform 总线驱动 — 最常见的嵌入式驱动框架
- I2C 子系统 — I2C 设备驱动开发
- SPI 子系统 — SPI 设备驱动开发
- USB 子系统 — USB 设备驱动开发