一、pci_iomap
/*
* pci_iomap 是一个用于映射 PCI 设备的 BAR(Base Address Register,基地址寄存器)的函数。
* 此函数返回指向内存映射 IO 的指针,用于直接访问 PCI 设备的内存或 I/O 空间。
*
* 参数:
* dev - 指向pci_dev结构的指针,表示PCI设备。
* bar - 表示要映射的基地址寄存器的索引(0-5)。
* maxlen - 要映射的最大长度;如果为0,则映射整个BAR空间。
*
* 返回值:
* 成功时,函数返回指向映射区域的指针。
* 如果映射失败,返回NULL。
*
* 注意: 当不再需要访问映射的内存时,应调用pci_iounmap来释放映射的资源。
*/
void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen)
{
return pci_iomap_range(dev, bar, 0, maxlen);
}
EXPORT_SYMBOL(pci_iomap); // 将 pci_iomap 函数导出,使得其它模块也能够调用该函数
这个函数 pci_iomap
是一个辅助函数,实际上它调用了 pci_iomap_range
函数,但是将 offset 设置为0,目的是简化对整个BAR的映射,而不是基于某个特定的起始偏移量。`pci_iomap` 在许多PCI驱动程序中被用来初始化设备操作所需的地址映射。
函数定义:lib\pci_iomap.c
二、pci_iomap_range
/**
* pci_iomap_range - 为PCI BAR创建一个虚拟映射
* @dev: 拥有BAR的PCI设备
* @bar: BAR编号
* @offset: 从BAR中给定的偏移量开始映射内存
* @maxlen: 要映射的最大内存长度
*
* 使用该函数可以获得指向设备BAR的__iomem地址。
* 可以使用ioread*()和iowrite*()来进行访问。这些函数会隐藏
* 是MMIO还是PIO地址空间的细节,并且会按照预期的方式正确地工作。
*
* @maxlen 指定了要映射的最大长度。如果您想访问从偏移量到结束的完整BAR,
* 在这里传递%0。
*/
void __iomem *pci_iomap_range(struct pci_dev *dev,
int bar,
unsigned long offset,
unsigned long maxlen)
{
//获取BAR资源的起始地址
resource_size_t start = pci_resource_start(dev, bar);
//获取BAR资源的总长度
resource_size_t len = pci_resource_len(dev, bar);
//获取BAR资源的标志
unsigned long flags = pci_resource_flags(dev, bar);
//如果资源长度不足或起始地址为0,则返回NULL
if (len <= offset || !start)
return NULL;
//减去偏移量,获得新的长度
len -= offset;
//根据偏移量更新起始地址
start += offset;
//如果有指定最大长度,并且新的长度大于它,则使用最大长度
if (maxlen && len > maxlen)
len = maxlen;
//如果是I/O资源,使用__pci_ioport_map进行映射
if (flags & IORESOURCE_IO)
return __pci_ioport_map(dev, start, len);
//如果是内存资源,使用ioremap进行映射
if (flags & IORESOURCE_MEM)
return ioremap(start, len);
//如果不是I/O资源也不是内存资源,返回NULL
return NULL;
}
//导出pci_iomap_range符号,使其在内核其他模块中可用
EXPORT_SYMBOL(pci_iomap_range);
上述代码是Linux内核中的一个函数注释,它是用于将PCI设备的某个BAR(基址地址寄存器)区域映射到内核虚拟地址空间,以便于内核或驱动程序可以直接通过这个虚拟地址对硬件设备进行访问。这个机制是PCI驱动程序常用的方法之一。
函数定义:lib\pci_iomap.c
三、pci_iomap和pci_iomap_range
/**
* pci_iomap_range - 为PCI设备的BAR创建虚拟映射
* @dev: 拥有BAR的PCI设备
* @bar: BAR编号
* @offset: 从BAR的给定偏移量处开始映射内存
* @maxlen: 需要映射的内存的最大长度
*
* 使用此函数可以获取到指向设备BAR的__iomem地址。
* 您可以使用ioread*() 和 iowrite*() 来访问其地址。
* 这些函数会隐藏这是一个MMIO地址还是PIO地址空间的细节,
* 并以您期望的正确方式进行操作。
*
* @maxlen 指定映射的最大长度。如果您想要从偏移量到末尾访问整个BAR,
* 请在这里传递%0(即传递0表示映射整个BAR的长度)。
*/
void __iomem *pci_iomap_range(struct pci_dev *dev,
int bar,
unsigned long offset,
unsigned long maxlen)
{
// ... 实现细节略 ...
}
EXPORT_SYMBOL(pci_iomap_range);
/**
* pci_iomap - 为PCI设备的BAR创建虚拟映射
* @dev: 拥有BAR的PCI设备
* @bar: BAR编号
* @maxlen: 需要映射的内存的长度
*
* 使用此函数可以获取到指向设备BAR的__iomem地址。
* 您可以使用ioread*() 和 iowrite*() 来访问这个地址。
* 这些函数会隐藏这是一个MMIO地址还是PIO地址空间的细节,
* 并以您期望的正确方式进行操作。
*
* @maxlen 指定映射的最大长度。如果您想要访问整个BAR而不首先检查其长度,
* 请在这里传递%0(即传递0表示映射整个BAR的长度)。
*/
void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen)
{
// ... 实现细节略 ...
}
EXPORT_SYMBOL(pci_iomap);
这两个函数都是用来建立PCI设备的BAR(Base Address Register,基址寄存器)到进程的虚拟地址空间的映射。通过这个映射,程序可以通过标准的I/O访问函数来读写设备的硬件寄存器。
pci_iomap_range允许指定一个起始偏移量和最大映射长度,可以用来映射BAR内部一段特定的内存区域。
pci_iomap是一个简化的版本,它默认从BAR的起始处映射整个区域,或者是指定长度的内存。(当maxlen为0时映射整个BAR)。
EXPORT_SYMBOL宏用于导出符号,使得这些函数可以在内核模块之间共享。
ChatGPT
在Linux内核中,`pci_iomap`是一个用于将PCI设备的内存或I/O端口映射到内核虚拟地址空间的内核函数。这使得内核代码可以通过正常的指针操作来访问PCI设备的资源。
函数的原型通常如下所示:
void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen);
参数解释:
- dev:指向`pci_dev`结构的指针,它表示要映射的PCI设备。
- bar:要映射的PCI设备的Base Address Register (BAR) 索引,其值从0开始。
- maxlen:映射区域的最大长度。如果该值为0,则表示映射从BAR开始的整个区域。
返回值:
该函数返回一个`void __iomem *类型的指针,它指向请求的映射区域的起始地址,如果映射失败,则返回NULL`。
使用`pci_iomap`函数可以直接访问PCI设备的内存,这通常在设备驱动程序初始化时完成。例如,驱动程序会调用`pci_iomap`来获取指向设备寄存器的指针,之后就可以通过这个指针来读写寄存器。
在使用完毕映射的内存后,应该调用`pci_iounmap()`函数来解除映射。
例如:
#define MY_DEVICE_BAR 0
struct pci_dev *pdev; // 假设已经通过某种方式获得
void __iomem *iomem;
// 映射BAR 0
iomem = pci_iomap(pdev, MY_DEVICE_BAR, 0);
if (!iomem) {
// 处理错误情况
}
// 使用iomem指针访问PCI设备内存
writeb(value, iomem + offset); // 写入字节
// 或者
writel(value, iomem + offset); // 写入长字
// 解除映射
pci_iounmap(pdev, iomem);
请记住,在写驱动程序时,应确保遵循操作系统的内存管理和设备操作准则,以确保系统稳定性和安全性。