当前位置: 首页 > news >正文

驱动开发,为什么需要映射?

核心概念:为什么需要映射?

PCI 设备通过 BAR(基地址寄存器)声明自己需要的内存或 I/O 空间。这些地址是物理地址,CPU 无法直接访问。必须先将这些物理地址映射到内核的虚拟地址空间,驱动程序才能通过指针访问硬件寄存器。

pci_iomap 和 pci_ioremap_bar 就是完成这个映射工作的函数。


pci_iomap

函数原型

#include <linux/pci.h>void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen);

参数详解

  1. struct pci_dev *dev

    • 指向要映射的PCI设备的结构体指针。

  2. int bar

    • 指定要映射的BAR的索引(0-5)。例如,bar=0表示映射BAR0。

  3. unsigned long maxlen

    • 要映射的最大长度(字节数)。

    • 如果设为0,会映射整个BAR区域。

    • 如果指定了长度,可以只映射BAR的一部分。

返回值

  • 成功:返回指向映射区域的 void __iomem * 类型的指针。

  • 失败:返回 NULL

特点与注意事项

  1. 不检查BAR类型pci_iomap 会映射无论是内存还是I/O空间的BAR。对于I/O端口的BAR,通常不应该使用内存映射方式访问。

  2. 需要手动判断BAR类型:调用者需要自己检查BAR是内存空间还是I/O空间:

    if (pci_resource_flags(pdev, bar) & IORESOURCE_IO) {// 这是I/O端口,应该用pci_iomap_range()或in/out指令
    } else {// 这是内存空间,可以用pci_iomap
    }
  3. 灵活性:可以通过 maxlen 参数控制映射的长度。

使用示例

struct pci_dev *pdev;
void __iomem *regs;/* 映射BAR1的全部区域 */
regs = pci_iomap(pdev, 1, 0);
if (!regs) {dev_err(&pdev->dev, "Failed to map BAR1\n");return -ENOMEM;
}/* 使用映射的地址访问硬件 */
writel(0x12345678, regs + REG_CONTROL);  // 写入控制寄存器
u32 status = readl(regs + REG_STATUS);   // 读取状态寄存器

pci_ioremap_bar

函数原型

#include <linux/pci.h>void __iomem *pci_ioremap_bar(struct pci_dev *pdev, int bar);

参数详解

  1. struct pci_dev *pdev

    • 指向要映射的PCI设备的结构体指针。

  2. int bar

    • 指定要映射的BAR的索引(0-5)。

返回值

  • 成功:返回指向映射区域的 void __iomem * 类型的指针。

  • 失败:返回 NULL

特点与优势

  1. 自动类型检查pci_ioremap_bar 会自动检查BAR的类型。如果BAR是I/O端口空间(IORESOURCE_IO),它不会进行映射并返回NULL。

  2. 映射整个BAR:总是映射整个BAR区域,不能指定部分映射。

  3. 更安全:由于自动类型检查,减少了错误映射I/O端口的风险。

  4. 便捷性:接口更简洁,不需要指定长度参数。

使用示例

struct pci_dev *pdev;
void __iomem *regs;/* 映射BAR0 - 自动检查是否为内存空间 */
regs = pci_ioremap_bar(pdev, 0);
if (!regs) {// 失败原因可能是:内存不足、BAR是I/O端口、BAR未启用等dev_err(&pdev->dev, "Failed to map BAR0\n");return -ENOMEM;
}/* 访问硬件寄存器 */
iowrite32(0x55AA, regs + OFFSET_CONFIG);
u32 data = ioread32(regs + OFFSET_DATA);

两个函数的对比

特性pci_iomappci_ioremap_bar
BAR类型检查❌ 不检查,可能错误映射I/O空间✅ 自动检查,只映射内存空间
映射范围可指定长度(0=全部)总是映射整个BAR
安全性较低,需要手动检查类型较高,内置安全检查
使用场景需要部分映射时;明确知道BAR类型时大多数情况下的首选
接口简洁性需要三个参数只需要两个参数

完整驱动代码示例

#include <linux/pci.h>
#include <linux/io.h>struct my_device {void __iomem *bar0_mem;void __iomem *bar2_mem;
};static int my_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{struct my_device *dev;int ret;/* 启用PCI设备 */ret = pci_enable_device(pdev);if (ret) return ret;/* 申请资源区域 */ret = pci_request_regions(pdev, "my_driver");if (ret) goto err_disable;/* 映射内存空间的BAR - 推荐使用pci_ioremap_bar */dev->bar0_mem = pci_ioremap_bar(pdev, 0);if (!dev->bar0_mem) {ret = -ENOMEM;goto err_release;}/* 如果需要部分映射,使用pci_iomap */dev->bar2_mem = pci_iomap(pdev, 2, 0x1000); // 只映射前4KBif (!dev->bar2_mem) {ret = -ENOMEM;goto err_unmap_bar0;}/* 设备初始化... */return 0;err_unmap_bar0:pci_iounmap(pdev, dev->bar0_mem);
err_release:pci_release_regions(pdev);
err_disable:pci_disable_device(pdev);return ret;
}static void my_remove(struct pci_dev *pdev)
{struct my_device *dev = pci_get_drvdata(pdev);/* 取消映射 */if (dev->bar2_mem)pci_iounmap(pdev, dev->bar2_mem);if (dev->bar0_mem)pci_iounmap(pdev, dev->bar0_mem);/* 释放资源 */pci_release_regions(pdev);pci_disable_device(pdev);
}

重要注意事项

  1. 必须先调用 pci_request_regions:在映射之前必须成功申请资源所有权。

  2. 使用正确的访问函数:映射后必须使用专门的I/O访问函数:

    // 对于内存映射I/O(MMIO)
    ioread8(), ioread16(), ioread32()
    iowrite8(), iowrite16(), iowrite32()// 或者传统函数(取决于架构)
    readb(), readw(), readl()
    writeb(), writew(), writel()
  3. 不要直接使用指针:绝对不能直接解引用 void __iomem * 指针:

    // 错误!
    u32 value = *regs;// 正确!
    u32 value = readl(regs);
  4. 配对使用 pci_iounmap:在驱动卸载时必须调用 pci_iounmap 来取消映射。

总结

  • pci_ioremap_bar 是首选:在大多数情况下,由于它的安全性和简洁性,应该优先使用。

  • pci_iomap 用于特殊情况:当需要部分映射或对BAR类型有明确控制时使用。

  • 遵循正确的调用顺序pci_enable_device → pci_request_regions → 映射函数。

  • 使用正确的I/O访问函数:映射后必须使用专门的读写函数访问硬件。

这两个函数是PCI驱动开发中访问硬件寄存器的基石,正确使用它们对于编写稳定可靠的驱动程序至关重要。

http://www.dtcms.com/a/398292.html

相关文章:

  • 网站栏目模版确定网站推广目标
  • AI产品经理项目实战:BERT语义分析识别重复信息
  • 亚远景-ISO 42001:为汽车AI安全设定新标杆
  • 电路方案分析(二十四)汽车高压互锁参考设计
  • 深圳网站快速备案手机app播放器
  • CSS精灵技术
  • 数据库导论#1
  • Web应用接入支付功能的准备工作和开发规范
  • 专业做logo的网站wordpress安装模板
  • 8 shiro的web整合
  • iOS 26 系统电耗分析实战指南 如何检测电池掉电、液体玻璃导致的能耗变化
  • 自动化平台自动化能力统一的建设
  • 做网站学的是代码吗网站备案流程教程
  • 【Unity 入门教程】二、核心概念
  • 【春秋云镜】CVE-2022-30887(文件上传/rce)
  • [iOS] YYModel 初步学习
  • 视频录屏软件 视频录屏软件 Bandicam (班迪录屏) 8.2.2.2531
  • 今天继续学习nginx服务部署与配置
  • flutter 编译报错java.util.zip.ZipException: zip END header not found
  • 网站建设精英京东商城网站域名
  • 《AI工具驱动的分布式任务调度系统从0到1实践解析》
  • C#练习——事件
  • 深拷贝浅拷贝的区别?如何实现⼀个深拷贝?
  • C primer plus (第六版)第十一章 编程练习第10题
  • AgentScope Studio 安装与测试
  • 长沙房产交易中心官网做seo网站空间
  • 金融培训网站源码淘宝基地网站怎么做
  • Spark核心Storage详解
  • 高系分二十:微服务系统分析与设计
  • 深度学习----ResNet(残差网络)-彻底改变深度神经网络的训练方式:通过残差学习来解决深层网络退化问题(附PyTorch实现)