linux设备驱动模型之driver

在Linux内核的驱动模型中,​驱动(Driver)​是实现设备控制逻辑的核心组件,负责与硬件设备交互,管理数据传输、配置和资源分配。驱动通过总线模型与设备动态绑定,支持模块化加载和热插拔。以下是对驱动模型(struct device_driver)的深入解析:


1. 驱动的核心作用

  • 硬件控制:操作设备的寄存器、中断、DMA等硬件资源。
  • 协议实现:实现设备特定的通信协议(如I2C、USB协议)。
  • 资源管理:分配和释放内存、中断、DMA缓冲区等资源。
  • 用户接口:通过sysfs、设备文件(如/dev/)或网络接口暴露功能。

2. 驱动的数据结构:struct device_driver

驱动在内核中通过struct device_driver表示,包含驱动名称、所属总线、操作方法等:

struct device_driver {
    const char *name;                 // 驱动名称(如"e1000"、"my_i2c_drv")
    struct bus_type *bus;             // 所属总线(如&pci_bus_type)
    struct module *owner;             // 所属模块(THIS_MODULE)
    const struct of_device_id *of_match_table; // 设备树匹配表(用于嵌入式系统)
    const struct acpi_device_id *acpi_match_table; // ACPI匹配表

    // 生命周期回调函数
    int (*probe)(struct device *dev);         // 设备初始化
    int (*remove)(struct device *dev);        // 设备移除
    void (*shutdown)(struct device *dev);     // 系统关机时调用
    int (*suspend)(struct device *dev, pm_message_t state); // 电源挂起
    int (*resume)(struct device *dev);         // 电源恢复
};

3. 驱动的注册与管理

a. 驱动注册API
  • 通用驱动注册
    int driver_register(struct device_driver *drv);
  • 驱动注销
    void driver_unregister(struct device_driver *drv);
b. 总线特定驱动的注册

以PCI驱动为例,使用struct pci_driver封装:

struct pci_driver {
    struct device_driver driver;    // 内嵌的通用驱动结构
    const struct pci_device_id *id_table; // 支持的设备ID表
    int (*probe)(struct pci_dev *dev, const struct pci_device_id *id);
    void (*remove)(struct pci_dev *dev);
};

// 注册PCI驱动
int pci_register_driver(struct pci_driver *drv);

4. 驱动与设备的匹配机制

a. 匹配触发条件
  • 驱动注册时:遍历总线上的所有设备,尝试匹配。
  • 设备注册时:遍历总线上的所有驱动,尝试匹配。
b. 匹配规则
  1. 设备ID匹配:驱动定义支持的设备ID列表(如PCI的vendor/device ID)。
    static const struct pci_device_id my_pci_ids[] = {
        { PCI_DEVICE(0x8086, 0x100E) }, // Intel 82574L网卡
        {0}
    };
    MODULE_DEVICE_TABLE(pci, my_pci_ids);
  2. 设备树匹配:通过compatible属性匹配。
    static const struct of_device_id my_of_match[] = {
        { .compatible = "ti,tmp75" }, // 温度传感器
        {}
    };
  3. ACPI匹配:在支持ACPI的系统中通过硬件标识符匹配。

5. 驱动的生命周期:Probe与Remove

a. probe()函数
  • 作用:设备初始化入口,分配资源、注册中断、创建设备节点。
  • 示例:PCI驱动的probe函数:
    static int my_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) {
        // 1. 启用PCI设备
        pci_enable_device(pdev);
        // 2. 请求内存区域
        pci_request_regions(pdev, "my_driver");
        // 3. 映射BAR空间
        void __iomem *regs = pci_iomap(pdev, 0, 0);
        // 4. 注册中断处理函数
        request_irq(pdev->irq, my_interrupt_handler, IRQF_SHARED, "my_driver", pdev);
        // 5. 注册网络设备(如网卡驱动)
        struct net_device *netdev = alloc_etherdev(sizeof(struct my_priv));
        register_netdev(netdev);
        return 0;
    }
b. remove()函数
  • 作用:释放资源,注销设备。
  • 示例
    static void my_pci_remove(struct pci_dev *pdev) {
        // 1. 释放中断
        free_irq(pdev->irq, pdev);
        // 2. 解除内存映射
        pci_iounmap(pdev, regs);
        // 3. 释放PCI资源
        pci_release_regions(pdev);
        // 4. 禁用设备
        pci_disable_device(pdev);
    }

6. 实际案例:Platform驱动

a. Platform驱动结构
#include <linux/platform_device.h>

static int my_platform_probe(struct platform_device *pdev) {
    struct resource *res;
    // 获取内存资源
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    void __iomem *regs = devm_ioremap_resource(&pdev->dev, res);
    // 获取中断资源
    int irq = platform_get_irq(pdev, 0);
    request_irq(irq, my_irq_handler, 0, "my_device", NULL);
    // 创建设备节点
    struct device *dev = &pdev->dev;
    devm_device_create(dev, NULL, dev, NULL, "mydev");
    return 0;
}

static struct platform_driver my_driver = {
    .driver = {
        .name = "my_platform_drv",
        .of_match_table = my_of_match, // 设备树匹配表
    },
    .probe = my_platform_probe,
    .remove = my_platform_remove,
};

module_platform_driver(my_driver); // 自动注册驱动
b. 设备树节点
my_device@10000000 {
    compatible = "vendor,my_device";
    reg = <0x10000000 0x1000>;
    interrupts = <0 42 4>;
};

7. 调试与常见问题

a. 检查驱动是否绑定设备
# 查看sysfs中的驱动绑定状态
ls /sys/bus/pci/drivers/e1000/
cat /sys/bus/platform/devices/my_device/driver
b. 内核日志分析
  • 驱动加载日志
    dmesg | grep "my_driver"
  • Probe失败原因
    • 资源冲突(如内存地址已被占用)。
    • 中断申请失败(request_irq返回错误码)。
c. 动态调试
  • 启用调试输出
    dev_dbg(&pdev->dev, "Probe started for device at 0x%lx\n", (long)res->start);
  • 使用ftrace跟踪函数调用
    echo function > /sys/kernel/tracing/current_tracer
    echo my_pci_probe > /sys/kernel/tracing/set_ftrace_filter
    cat /sys/kernel/tracing/trace

8. 驱动的电源管理

驱动需实现suspend()resume()回调,以支持系统休眠唤醒:

static int my_suspend(struct device *dev) {
    // 保存设备状态,关闭硬件电源
    return 0;
}

static int my_resume(struct device *dev) {
    // 恢复设备状态,重新初始化硬件
    return 0;
}

static const struct dev_pm_ops my_pm_ops = {
    .suspend = my_suspend,
    .resume = my_resume,
};

struct device_driver my_driver = {
    .name = "my_drv",
    .pm = &my_pm_ops,
};

9. 用户空间接口

a. sysfs属性

驱动可通过sysfs暴露配置或状态信息:

static ssize_t my_attr_show(struct device *dev, struct device_attribute *attr, char *buf) {
    return sprintf(buf, "Value: %d\n", my_priv->value);
}

static DEVICE_ATTR(my_value, 0444, my_attr_show, NULL);

static int my_probe(struct platform_device *pdev) {
    device_create_file(&pdev->dev, &dev_attr_my_value);
}
b. 创建设备文件

字符驱动通过cdevdevtmpfs生成/dev节点:

dev_t dev_id;
alloc_chrdev_region(&dev_id, 0, 1, "my_char_dev");
cdev_init(&my_cdev, &my_fops);
cdev_add(&my_cdev, dev_id, 1);
device_create(my_class, NULL, dev_id, NULL, "mydev");

10. 总结

Linux内核中的驱动模型通过标准化接口实现了硬件的抽象与控制,是设备驱动开发的核心。理解驱动的注册、匹配、资源管理和用户接口,是开发高效稳定驱动的关键。通过结合总线模型、设备树和sysfs调试工具,开发者可以快速定位问题并优化驱动性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浩瀚之水_csdn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值