欢迎光临
我们一直在努力

Linux系统驱动架构概述

Linux系统驱动架构概述

一、Linux驱动架构概述

Linux驱动采用分层架构和模块化设计,遵循"一切皆文件"的设计思想。

1.1 驱动类型分类

  • 字符设备驱动 (Character Device Driver)
  • 块设备驱动 (Block Device Driver)
  • 网络设备驱动 (Network Device Driver)
  • 杂项设备驱动 (Misc Device)
  • 平台设备驱动 (Platform Device)
  • 总线设备驱动 (PCI, USB, I2C, SPI等)
  • 二、驱动模型核心架构

    2.1 驱动模型三大组件

    // 内核驱动模型核心结构
    struct bus_type { // 总线
    const char *name;
    int (*match)(struct device *, struct device_driver *);
    int (*probe)(struct device *);
    int (*remove)(struct device *);
    };

    struct device { // 设备
    struct device *parent;
    struct kobject kobj;
    const char *init_name;
    struct device_driver *driver;
    struct bus_type *bus;
    void *platform_data;
    };

    struct device_driver { // 驱动
    const char *name;
    struct bus_type *bus;
    struct module *owner;
    int (*probe)(struct device *);
    int (*remove)(struct device *);
    };

    2.2 设备树(Device Tree)支持

    // 设备树示例
    /
    compatible = "mycompany,myboard";

    cpus {
    cpu@0 {
    compatible = "arm,cortex-a53";
    };
    };

    memory@80000000 {
    reg = <0x80000000 0x20000000>;
    };

    i2c@12340000 {
    compatible = "mycompany,i2c";
    reg = <0x12340000 0x1000>;
    #address-cells = <1>;
    #size-cells = <0>;

    eeprom@50 {
    compatible = "atmel,24c256";
    reg = <0x50>;
    };
    };
    };

    三、字符设备驱动架构

    3.1 字符设备核心结构

    // 字符设备驱动完整示例
    #include <linux/module.h>
    #include <linux/fs.h>
    #include <linux/cdev.h>
    #include <linux/device.h>

    #define DEVICE_NAME "mychardev"
    #define CLASS_NAME "mycharclass"
    #define DEVICE_COUNT 1

    static int major_number = 0;
    static struct class* char_class = NULL;
    static struct device* char_device = NULL;
    static struct cdev my_cdev;

    // 文件操作结构
    static struct file_operations fops = {
    .owner = THIS_MODULE,
    .open = mydev_open,
    .release = mydev_release,
    .read = mydev_read,
    .write = mydev_write,
    .unlocked_ioctl = mydev_ioctl,
    };

    // 初始化函数
    static int __init mydev_init(void)
    {
    dev_t devno;
    int ret;

    // 1. 分配主设备号
    ret = alloc_chrdev_region(&devno, 0, DEVICE_COUNT, DEVICE_NAME);
    major_number = MAJOR(devno);

    // 2. 初始化cdev
    cdev_init(&my_cdev, &fops);
    my_cdev.owner = THIS_MODULE;

    // 3. 添加cdev到系统
    ret = cdev_add(&my_cdev, devno, DEVICE_COUNT);

    // 4. 创建设备类
    char_class = class_create(THIS_MODULE, CLASS_NAME);

    // 5. 创建设备文件节点
    char_device = device_create(char_class, NULL, devno,
    NULL, DEVICE_NAME);
    return 0;
    }

    3.2 字符设备操作流程

    四、块设备驱动架构

    4.1 块设备核心结构

    // 块设备驱动结构
    struct block_device_operations {
    int (*open) (struct block_device *, fmode_t);
    void (*release) (struct gendisk *, fmode_t);
    int (*ioctl)(struct block_device *, fmode_t, unsigned, unsigned long);
    int (*getgeo)(struct block_device *, struct hd_geometry *);
    struct module *owner;
    };

    struct gendisk {
    int major; // 主设备号
    int first_minor; // 第一个次设备号
    int minors; // 次设备号数量
    char disk_name[DISK_NAME_LEN]; // 磁盘名
    struct block_device_operations *fops; // 操作集
    struct request_queue *queue; // 请求队列
    void *private_data; // 私有数据
    sector_t capacity; // 容量(扇区数)
    };

    4.2 请求队列处理

    // 块设备驱动示例
    static int myblk_init(void)
    {
    struct request_queue *queue;
    struct gendisk *gd;

    // 1. 分配gendisk结构
    gd = alloc_disk(MYDEV_MINORS);

    // 2. 初始化请求队列
    queue = blk_init_queue(mydev_request, &mydev_lock);

    // 3. 设置gendisk
    gd->major = MYDEV_MAJOR;
    gd->first_minor = 0;
    gd->fops = &mydev_fops;
    gd->queue = queue;
    gd->private_data = mydev_data;
    snprintf(gd->disk_name, 32, "myblk%d", 0);
    set_capacity(gd, mydev_size_sectors);

    // 4. 添加到系统
    add_disk(gd);
    return 0;
    }

    // 请求处理函数
    static void mydev_request(struct request_queue *q)
    {
    struct request *req;

    while ((req = blk_fetch_request(q)) != NULL) {
    // 处理每个请求
    if (req->cmd_type != REQ_TYPE_FS) {
    __blk_end_request_all(req, EIO);
    continue;
    }

    // 读写处理
    if (rq_data_dir(req) == READ)
    mydev_read(req);
    else
    mydev_write(req);
    }
    }

    五、网络设备驱动架构

    5.1 网络设备核心结构

    // 网络设备驱动结构
    struct net_device {
    char name[IFNAMSIZ]; // 设备名
    unsigned long mem_end; // 共享内存结束
    unsigned long mem_start; // 共享内存开始
    unsigned long base_addr; // I/O基地址
    unsigned int irq; // 中断号

    const struct net_device_ops *netdev_ops; // 网络设备操作
    const struct ethtool_ops *ethtool_ops; // ethtool操作

    unsigned int flags; // 设备标志
    int mtu; // 最大传输单元
    unsigned char *dev_addr; // MAC地址
    struct net_device_stats stats; // 统计信息
    void *priv; // 私有数据
    };

    // 网络设备操作集
    struct net_device_ops {
    int (*ndo_open)(struct net_device *dev);
    int (*ndo_stop)(struct net_device *dev);
    netdev_tx_t (*ndo_start_xmit)(struct sk_buff *skb,
    struct net_device *dev);
    void (*ndo_tx_timeout)(struct net_device *dev);
    struct rtnl_link_stats64* (*ndo_get_stats64)(...);
    int (*ndo_set_mac_address)(struct net_device *dev, void *addr);
    int (*ndo_do_ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd);
    };

    5.2 网络设备驱动示例

    // 网络设备驱动初始化
    static int mynet_init(void)
    {
    struct net_device *dev;
    int ret;

    // 1. 分配网络设备结构
    dev = alloc_netdev(sizeof(struct mynet_priv), "eth%d",
    NET_NAME_UNKNOWN, ether_setup);
    if (!dev)
    return ENOMEM;

    // 2. 设置MAC地址
    eth_hw_addr_random(dev);

    // 3. 设置操作函数
    dev->netdev_ops = &mynet_ops;
    dev->ethtool_ops = &mynet_ethtool_ops;

    // 4. 设置MTU等参数
    dev->mtu = 1500;
    dev->flags |= IFF_NOARP;

    // 5. 注册网络设备
    ret = register_netdev(dev);
    if (ret) {
    free_netdev(dev);
    return ret;
    }

    return 0;
    }

    // 数据包发送函数
    static netdev_tx_t mynet_start_xmit(struct sk_buff *skb,
    struct net_device *dev)
    {
    struct mynet_priv *priv = netdev_priv(dev);

    // 1. 锁定设备
    netif_stop_queue(dev);

    // 2. 复制数据到硬件缓冲区
    memcpy_toio(priv->tx_buffer, skb->data, skb->len);

    // 3. 启动传输
    outl(TX_START, priv->ioaddr + TX_REG);

    // 4. 统计
    dev->stats.tx_packets++;
    dev->stats.tx_bytes += skb->len;

    // 5. 释放skb
    dev_kfree_skb(skb);

    return NETDEV_TX_OK;
    }

    六、输入子系统驱动架构

    6.1 输入子系统核心结构

    // 输入子系统驱动示例
    #include <linux/input.h>

    struct input_dev *input_dev;

    // 初始化输入设备
    static int myinput_init(void)
    {
    int error;

    // 1. 分配输入设备
    input_dev = input_allocate_device();
    if (!input_dev)
    return ENOMEM;

    // 2. 设置设备类型
    input_dev->name = "My Input Device";
    input_dev->id.bustype = BUS_USB;
    input_dev->id.vendor = 0x1234;
    input_dev->id.product = 0x5678;
    input_dev->id.version = 0x0100;

    // 3. 设置支持的事件类型
    __set_bit(EV_KEY, input_dev->evbit); // 按键事件
    __set_bit(EV_REL, input_dev->evbit); // 相对事件
    __set_bit(EV_ABS, input_dev->evbit); // 绝对事件

    // 4. 设置支持的按键
    __set_bit(BTN_LEFT, input_dev->keybit); // 左键
    __set_bit(BTN_RIGHT, input_dev->keybit); // 右键
    __set_bit(BTN_MIDDLE, input_dev->keybit); // 中键

    // 5. 设置绝对坐标范围
    input_set_abs_params(input_dev, ABS_X, 0, 1023, 0, 0);
    input_set_abs_params(input_dev, ABS_Y, 0, 767, 0, 0);

    // 6. 注册设备
    error = input_register_device(input_dev);
    if (error) {
    input_free_device(input_dev);
    return error;
    }

    return 0;
    }

    // 上报事件
    static void report_input_event(int x, int y, int button)
    {
    // 上报绝对坐标
    input_report_abs(input_dev, ABS_X, x);
    input_report_abs(input_dev, ABS_Y, y);

    // 上报按键状态
    input_report_key(input_dev, BTN_LEFT, button & 0x01);
    input_report_key(input_dev, BTN_RIGHT, button & 0x02);

    // 同步事件
    input_sync(input_dev);
    }

    七、Platform总线驱动架构

    7.1 Platform设备/驱动模型

    // Platform设备定义
    static struct resource mydev_resources[] = {
    [0] = {
    .start = 0x10000000, // 物理地址
    .end = 0x1000FFFF,
    .flags = IORESOURCE_MEM,
    },
    [1] = {
    .start = IRQ_NUM, // 中断号
    .end = IRQ_NUM,
    .flags = IORESOURCE_IRQ,
    },
    };

    static struct platform_device mydev_device = {
    .name = "my-platform-device",
    .id = 1,
    .num_resources = ARRAY_SIZE(mydev_resources),
    .resource = mydev_resources,
    .dev = {
    .platform_data = &mydev_pdata, // 平台私有数据
    },
    };

    // Platform驱动定义
    static int mydev_probe(struct platform_device *pdev)
    {
    struct resource *res;
    void __iomem *base;
    int irq;

    // 1. 获取资源
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    base = ioremap(res->start, resource_size(res));

    irq = platform_get_irq(pdev, 0);

    // 2. 获取平台数据
    struct mydev_platform_data *pdata = dev_get_platdata(&pdev->dev);

    // 3. 初始化硬件
    // …

    return 0;
    }

    static int mydev_remove(struct platform_device *pdev)
    {
    // 清理资源
    return 0;
    }

    static struct platform_driver mydev_driver = {
    .probe = mydev_probe,
    .remove = mydev_remove,
    .driver = {
    .name = "my-platform-device",
    .owner = THIS_MODULE,
    .of_match_table = of_match_ptr(mydev_of_match),
    },
    };

    八、I2C/SPI总线驱动架构

    8.1 I2C驱动示例

    // I2C设备结构
    struct i2c_client {
    unsigned short flags; // 标志
    unsigned short addr; // 7位地址
    char name[I2C_NAME_SIZE]; // 设备名
    struct i2c_adapter *adapter; // 适配器
    struct device dev; // 设备结构
    int irq; // 中断号
    };

    // I2C驱动结构
    static struct i2c_driver my_i2c_driver = {
    .driver = {
    .name = "my_i2c_device",
    .owner = THIS_MODULE,
    .of_match_table = my_i2c_of_match,
    },
    .probe = my_i2c_probe,
    .remove = my_i2c_remove,
    .id_table = my_i2c_id,
    };

    // 从设备树获取I2C设备
    static const struct of_device_id my_i2c_of_match[] = {
    { .compatible = "vendor,my-i2c-device" },
    {},
    };
    MODULE_DEVICE_TABLE(of, my_i2c_of_match);

    // I2C操作函数
    static int my_i2c_read_reg(struct i2c_client *client, u8 reg, u8 *val)
    {
    return i2c_smbus_read_byte_data(client, reg);
    }

    static int my_i2c_write_reg(struct i2c_client *client, u8 reg, u8 val)
    {
    return i2c_smbus_write_byte_data(client, reg, val);
    }

    九、设备树与驱动匹配

    9.1 设备树节点示例

    设备树是描述硬件的结构化数据,以下是一个完整的设备树节点示例,包含了一个带自定义参数的I2C设备:

    // 设备树示例 – 包含I2C设备节点
    /
    compatible = "mycompany,myboard";

    i2c@12340000 {
    compatible = "mycompany,i2c";
    reg = <0x12340000 0x1000>;
    interrupts = <0 42 0>;
    #address-cells = <1>;
    #size-cells = <0>;

    // 带自定义参数的I2C设备
    mydevice@50 {
    compatible = "vendor,my-device";
    reg = <0x50>; // I2C设备地址
    interrupt-parent = <&gpio>;
    interrupts = <15 IRQ_TYPE_EDGE_FALLING>;
    clock-frequency = <100000>; // I2C时钟频率
    vendor,enable-gpio = <&gpio 16 GPIO_ACTIVE_HIGH>;
    vendor,led-polarity = <0>; // 自定义属性
    vendor,config-data = <0x01 0x02 0x03 0x04>; // 数组属性
    };
    };
    };

    9.2 驱动与设备树匹配机制

    驱动通过设备树匹配表与硬件设备建立关联:

    // 设备树匹配表定义
    static const struct of_device_id my_device_of_match[] = {
    { .compatible = "vendor,my-device", .data = &my_device_data },
    {}, // 终止条目
    };
    MODULE_DEVICE_TABLE(of, my_device_of_match);

    // 驱动结构体 – 关联设备树匹配表
    static struct i2c_driver my_device_driver = {
    .driver = {
    .name = "my-device",
    .owner = THIS_MODULE,
    .of_match_table = my_device_of_match, // 设备树匹配表
    },
    .probe = my_device_probe,
    .remove = my_device_remove,
    .id_table = my_device_id,
    };

    // 或者Platform驱动的设备树关联
    static struct platform_driver my_platform_driver = {
    .probe = my_platform_probe,
    .remove = my_platform_remove,
    .driver = {
    .name = "my-platform-device",
    .owner = THIS_MODULE,
    .of_match_table = my_platform_of_match, // 设备树匹配表
    },
    };

    9.3 Platform驱动probe函数获取设备树参数

    以下示例展示了如何在Platform驱动的probe函数中获取设备树参数:

    // Platform设备私有数据
    struct my_platform_dev {
    void __iomem *base;
    int irq;
    u32 clock_freq;
    bool led_polarity;
    };

    static int my_platform_probe(struct platform_device *pdev)
    {
    struct device_node *np = pdev->dev.of_node;
    struct my_platform_dev *dev;
    struct resource *res;
    u32 enable_pin;
    int ret;

    // 分配私有数据
    dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
    if (!dev) {
    return ENOMEM;
    }

    platform_set_drvdata(pdev, dev);

    // 获取内存资源
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    dev->base = devm_ioremap_resource(&pdev->dev, res);
    if (IS_ERR(dev->base)) {
    return PTR_ERR(dev->base);
    }

    // 获取中断资源
    dev->irq = platform_get_irq(pdev, 0);

    // 从设备树获取参数
    if (np) {
    // 获取整数参数 – 时钟频率
    ret = of_property_read_u32(np, "clock-frequency", &dev->clock_freq);
    if (ret) {
    dev_warn(&pdev->dev, "Using default clock frequency\\n");
    dev->clock_freq = 100000; // 默认值
    }

    // 获取GPIO引脚
    ret = of_property_read_u8(np, "vendor,enable-gpio", &enable_pin);
    if (ret) {
    dev_err(&pdev->dev, "Missing enable-gpio property\\n");
    return EINVAL;
    }

    // 获取布尔值参数
    dev->led_polarity = of_property_read_bool(np, "vendor,led-polarity");

    // 获取字符串参数
    const char *mode;
    ret = of_property_read_string(np, "vendor,mode", &mode);
    if (ret == 0) {
    dev_info(&pdev->dev, "Operation mode: %s\\n", mode);
    }

    // 获取数组参数
    u32 config_data[4];
    ret = of_property_read_u32_array(np, "vendor,config-data", config_data, 4);
    if (ret == 0) {
    dev_info(&pdev->dev, "Config data: %#x %#x %#x %#x\\n",
    config_data[0], config_data[1], config_data[2], config_data[3]);
    }
    }

    // 使用获取的参数初始化硬件
    // …

    dev_info(&pdev->dev, "Platform device probed successfully\\n");
    return 0;
    }

    9.4 I2C驱动probe函数获取设备树参数

    以下示例展示了如何在I2C驱动的probe函数中获取设备树参数:

    // I2C设备私有数据
    struct my_i2c_dev {
    struct i2c_client *client;
    int reset_gpio;
    bool wakeup_supported;
    };

    // 寄存器定义
    #define INTERRUPT_CONFIG_REG 0x0C

    static int my_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
    {
    struct device_node *np = client->dev.of_node;
    struct my_i2c_dev *dev;
    u8 int_config;
    int ret;

    // 分配私有数据
    dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL);
    if (!dev) {
    return ENOMEM;
    }

    dev->client = client;
    i2c_set_clientdata(client, dev);

    // 从设备树获取参数
    if (np) {
    // 获取中断配置
    ret = of_property_read_u8(np, "vendor,interrupt-config", &int_config);
    if (ret) {
    dev_warn(&client->dev, "Using default interrupt config\\n");
    int_config = 0x01; // 默认配置
    }

    // 检查是否支持唤醒功能
    dev->wakeup_supported = of_property_read_bool(np, "wakeup-source");
    if (dev->wakeup_supported) {
    device_init_wakeup(&client->dev, true);
    dev_info(&client->dev, "Wakeup functionality enabled\\n");
    }

    // 获取GPIO配置
    if (of_find_property(np, "vendor,reset-gpio", NULL)) {
    dev->reset_gpio = of_get_named_gpio(np, "vendor,reset-gpio", 0);
    if (gpio_is_valid(dev->reset_gpio)) {
    ret = devm_gpio_request_one(&client->dev, dev->reset_gpio,
    GPIOF_OUT_INIT_HIGH, "my_i2c_reset");
    if (ret) {
    dev_err(&client->dev, "Failed to request reset GPIO\\n");
    return ret;
    }
    }
    }
    }

    // 使用获取的参数初始化设备
    ret = i2c_smbus_write_byte_data(client, INTERRUPT_CONFIG_REG, int_config);
    if (ret < 0) {
    dev_err(&client->dev, "Failed to configure interrupt\\n");
    return ret;
    }

    // 注册中断
    if (client->irq > 0) {
    ret = devm_request_threaded_irq(&client->dev, client->irq,
    NULL, my_i2c_irq_thread,
    IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
    client->name, dev);
    if (ret) {
    dev_err(&client->dev, "Failed to request interrupt\\n");
    return ret;
    }
    }

    dev_info(&client->dev, "I2C device probed successfully\\n");
    return 0;
    }

    // I2C中断处理函数
    static irqreturn_t my_i2c_irq_thread(int irq, void *dev_id)
    {
    struct my_i2c_dev *dev = dev_id;
    // 中断处理逻辑
    return IRQ_HANDLED;
    }

    9.5 设备树匹配与参数获取的最佳实践

  • 使用默认值:当设备树中缺少某个属性时,提供合理的默认值
  • 错误处理:对设备树参数获取结果进行适当的错误处理
  • 日志记录:使用dev_info/dev_warn/dev_err等函数记录参数获取情况
  • GPIO管理:使用devm_gpio_request_one等资源管理函数管理GPIO
  • 唤醒功能:正确处理设备树中的wakeup-source属性
  • 数组属性:使用of_property_read_xxx_array函数处理数组类型的设备树属性
  • 十、驱动模型注册流程

    十一、驱动框架高级特性

    11.1 电源管理

    // 电源管理操作
    static const struct dev_pm_ops mydev_pm_ops = {
    .suspend = mydev_suspend,
    .resume = mydev_resume,
    .freeze = mydev_freeze,
    .thaw = mydev_thaw,
    .poweroff = mydev_poweroff,
    .restore = mydev_restore,
    .runtime_suspend = mydev_runtime_suspend,
    .runtime_resume = mydev_runtime_resume,
    .runtime_idle = mydev_runtime_idle,
    };

    11.2 DMA支持

    // DMA缓冲区分配
    static int mydev_dma_setup(struct mydev *dev)
    {
    // 分配一致性DMA映射
    dev->dma_buf = dma_alloc_coherent(&pdev->dev,
    DMA_BUF_SIZE,
    &dev->dma_handle,
    GFP_KERNEL);

    // 设置DMA掩码
    dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));

    // 创建DMA通道
    dev->dma_chan = dma_request_channel(mask, mydev_dma_filter, NULL);

    return 0;
    }

    11.3 中断处理

    // 中断处理示例
    static irqreturn_t mydev_interrupt(int irq, void *dev_id)
    {
    struct mydev *dev = dev_id;
    u32 status;

    // 读取中断状态
    status = readl(dev->base + INT_STATUS_REG);

    if (status & DATA_READY_INT) {
    // 处理数据
    mydev_process_data(dev);

    // 清除中断
    writel(DATA_READY_INT, dev->base + INT_CLEAR_REG);
    return IRQ_HANDLED;
    }

    return IRQ_NONE;
    }

    // 注册中断
    static int mydev_request_irq(struct mydev *dev)
    {
    int ret;

    ret = request_irq(dev->irq, mydev_interrupt,
    IRQF_SHARED | IRQF_TRIGGER_RISING,
    "mydev", dev);
    return ret;
    }

    十二、驱动调试与测试

    12.1 调试方法

    // 动态调试
    #define DEBUG
    #undef PDEBUG
    #ifdef DEBUG
    # ifdef __KERNEL__
    # define PDEBUG(fmt, args...) printk(KERN_DEBUG "mydev: " fmt, ## args)
    # else
    # define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)
    # endif
    #else
    # define PDEBUG(fmt, args...)
    #endif

    // 通过procfs调试
    static int mydev_proc_show(struct seq_file *m, void *v)
    {
    seq_printf(m, "Driver Status:\\n");
    seq_printf(m, " Version: %s\\n", DRIVER_VERSION);
    seq_printf(m, " Devices: %d\\n", device_count);
    return 0;
    }

    // 通过debugfs调试
    static int __init mydev_debugfs_init(void)
    {
    struct dentry *dir;

    dir = debugfs_create_dir("mydev", NULL);
    debugfs_create_u32("debug_level", 0644, dir, &debug_level);
    debugfs_create_file("registers", 0444, dir, NULL, &registers_fops);
    return 0;
    }

    十三、总结:Linux驱动架构特点

    这个架构设计使得Linux能够支持从嵌入式设备到服务器、从简单字符设备到复杂网络设备的广泛硬件平台

    赞(0)
    未经允许不得转载:171主机测评 » Linux系统驱动架构概述
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址