1. Linux设备模型:驱动开发的"户籍系统"
老张那句"没上户口"的比喻实在太贴切了。在Linux内核中,设备模型就是所有硬件的"户籍管理系统"。我调试的车载CAN设备之所以在休眠唤醒后丢失,本质上是因为它没有在系统中注册自己的"身份信息",导致电源管理子系统无法追踪和管理它的状态。
1.1 设备模型的三要素
设备模型的核心结构体构成了一个清晰的层级关系:
c复制struct bus_type { // 相当于"街道办"
const char *name;
int (*match)(struct device *dev, struct device_driver *drv);
struct kset drivers, devices;
};
struct device_driver { // 相当于"户口本"
const char *name;
struct bus_type *bus;
int (*probe)(struct device *dev);
};
struct device { // 相当于"居民身份证"
struct kobject kobj;
struct device_driver *driver;
struct bus_type *bus;
};
这三者的关系就像现实生活中的行政管理体系:
- 总线(bus_type)是行政区域(如街道)
- 驱动(device_driver)是户口登记信息
- 设备(device)是具体的居民
1.2 为什么现代驱动必须"上户口"
在早期的Linux 2.4内核中,驱动开发就像在"三不管"地带:
- 电源管理各搞各的
- 设备拓扑关系混乱
- 热插拔支持参差不齐
- /dev目录充斥着僵尸设备节点
引入统一设备模型后,带来了三大革命性改进:
- 生命周期管理:从设备注册到注销的全过程可追踪
- 资源整合:统一处理电源管理、热插拔等共性需求
- 用户空间接口:通过sysfs提供标准化的设备访问方式
在车载系统中,这些特性尤为重要。一个典型的ECU(电子控制单元)可能包含:
- 多个CAN总线设备
- 各类传感器
- 执行器控制单元
- 车载娱乐系统组件
没有统一的设备模型,这些组件在休眠唤醒时的状态管理将是一场噩梦。
2. 驱动暴露实战:从"黑户"到"合法公民"
2.1 基础注册流程
让我们从一个典型的"黑户"驱动开始改造:
c复制// 反面教材:隐形驱动
static int my_probe(struct platform_device *pdev)
{
printk("Device ready\n"); // 仅靠printk调试是远远不够的
return 0;
}
static struct platform_driver my_driver = {
.driver = {
.name = "my_device",
},
.probe = my_probe,
};
这个驱动虽然能工作,但在系统中几乎不可见。改造第一步是完善基本注册信息:
c复制static int my_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct my_private *priv;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
platform_set_drvdata(pdev, priv);
// 初始化硬件
priv->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->regs))
return PTR_ERR(priv->regs);
// 注册中断
priv->irq = platform_get_irq(pdev, 0);
if (priv->irq < 0)
return priv->irq;
return devm_request_irq(dev, priv->irq, my_interrupt,
IRQF_SHARED, dev_name(dev), priv);
}
static int my_remove(struct platform_device *pdev)
{
// devm_* 系列函数会自动清理,这里只需处理非托管资源
return 0;
}
static const struct dev_pm_ops my_pm_ops = {
.suspend = my_suspend
