/* Get pci port io resources described by bar #pci_bar in uio resource n. */
static int
igbuio_pci_setup_ioport(struct pci_dev *dev, struct uio_info *info,
int n, int pci_bar, const char *name)
{
unsigned long addr, len;
if (sizeof(info->port) / sizeof(info->port[0]) <= n)
return -EINVAL;
addr = pci_resource_start(dev, pci_bar);
len = pci_resource_len(dev, pci_bar);
if (addr == 0 || len == 0)
return -EINVAL;
info->port[n].name = name;
info->port[n].start = addr;
info->port[n].size = len;
info->port[n].porttype = UIO_PORT_X86;
return 0;
}
static int
igbuio_setup_bars(struct pci_dev *dev, struct uio_info *info)
{
int i, iom, iop, ret;
unsigned long flags;
static const char *bar_names[PCI_STD_RESOURCE_END + 1] = {
"BAR0",
"BAR1",
"BAR2",
"BAR3",
"BAR4",
"BAR5",
};
iom = 0;
iop = 0;
//遍历PCI设备的6个BAR
for (i = 0; i < ARRAY_SIZE(bar_names); i++) {
//PCI BAR空间不等于0且起始地址不等于0,认为为有效BAR
if (pci_resource_len(dev, i) != 0 &&
pci_resource_start(dev, i) != 0) {
//拿到BAR的标识,如果为0x00000200则为内存空间
flags = pci_resource_flags(dev, i);
if (flags & IORESOURCE_MEM) {
//对内存空间的PCI BAR进行映射
ret = igbuio_pci_setup_iomem(dev, info, iom,
i, bar_names[i]);
if (ret != 0)
return ret;
iom++;
//IO空间不再讨论范围内
} else if (flags & IORESOURCE_IO) {
ret = igbuio_pci_setup_ioport(dev, info, iop,
i, bar_names[i]);
if (ret != 0)
return ret;
iop++;
}
}
}
return (iom != 0 || iop != 0) ? ret : -ENOENT;
}
//对内存BAR进行映射,以及填充数据结构
static int
igbuio_pci_setup_iomem(struct pci_dev *dev, struct uio_info *info,
int n, int pci_bar, const char *name)
{
unsigned long addr, len;
void *internal_addr;
if (n >= ARRAY_SIZE(info->mem))
return -EINVAL;
//拿到PCI BAR的起始地址
addr = pci_resource_start(dev, pci_bar);
//拿到PCI BAR的长度
len = pci_resource_len(dev, pci_bar);
if (addr == 0 || len == 0)
return -1;
//wc_activate为igb_uio.ko的参数,默认为0,会进入if条件
if (wc_activate == 0) {
//对PCI BAR进行ioremap,映射到内核空间,得到可以在内核空间映射后的PCI BAR地址,虽然没什么用,因为igb_uio完全不需要操作PCI设备,因此获得此地址意义不大
internal_addr = ioremap(addr, len);
if (internal_addr == NULL)
return -1;
} else {
internal_addr = NULL;
}
//填充数据结构
info->mem[n].name = name; //PCI BAR名,例如BAR0、BAR1
info->mem[n].addr = addr; //PCI BAR起始地址,物理地址
info->mem[n].internal_addr = internal_addr; //经过ioremap映射后的PCI BAR,可以供内核空间访问
info->mem[n].size = len; //PCI BAR长度
info->mem[n].memtype = UIO_MEM_PHYS; //PCI BAR类型,为内存BAR
return 0;
}