这一篇文章主要介绍PCI设备初始化(一)中涉及的主要函数
主要数据结构
pci_bus
struct pci_bus {
struct list_head node; /* node in list of buses */ // 同级PCI总线链表
struct pci_bus *parent; /* parent bus this bridge is on */ // 上级PCI总线
struct list_head children; /* list of child buses */ // 下级PCI总线链表
struct list_head devices; /* list of devices on this bus */ // PCI总线上设备链表
struct pci_dev *self; /* bridge device as seen by parent */ // 所挂PCI桥
struct list_head slots; /* list of slots on this bus */
struct resource *resource[PCI_BUS_NUM_RESOURCES]; // 总线地址空间
/* address space routed to this bus */
struct pci_ops *ops; /* configuration access functions */
void *sysdata; /* hook for sys-specific extension */
struct proc_dir_entry *procdir; /* directory entry in /proc/bus/pci */
unsigned char number; /* bus number */ // 总线号
unsigned char primary; /* number of primary bridge */
unsigned char secondary; /* number of secondary bridge */
unsigned char subordinate; /* max number of subordinate buses */
...
};
pci_dev
struct pci_dev {
struct list_head bus_list; /* node in per-bus list */ // 所挂PCI总线上设备链表
struct pci_bus *bus; /* bus this device is on */ // 所挂PCI总线
struct pci_bus *subordinate; /* bus this device bridges to */
void *sysdata; /* hook for sys-specific extension */
struct proc_dir_entry *procent; /* device entry in /proc/bus/pci */
struct pci_slot *slot; /* Physical slot this device is in */
unsigned int devfn; /* encoded device & function index */ // 设备号+功能号(逻辑设备号)
unsigned short vendor; // 厂商号
unsigned short device; // 设备号
unsigned short subsystem_vendor; // 子系统厂商号
unsigned short subsystem_device; // 子系统设备号
...
// resource数组大小为12,每个resource对应PCI设备的一段存储空间
struct resource resource[DEVICE_COUNT_RESOURCE]; /* I/O and memory regions + expansion ROMs */
...
};
resource
struct resource {
resource_size_t start;
resource_size_t end;
const char *name;
unsigned long flags;
struct resource *parent, *sibling, *child;
};
主要函数
pci_direct_probe()
申请IO地址空间0xCF8~0xCFF
int __init pci_direct_probe(void)
{
struct resource *region, *region2;
if ((pci_probe & PCI_PROBE_CONF1) == 0)
goto type2;
// 在IO地址空间中从0xCF8开始申请8个字节,名称为"PCI conf1"
region = request_region(0xCF8, 8, "PCI conf1");
if (!region)
goto type2;
if (pci_check_type1()) {
raw_pci_ops = &pci_direct_conf1; // 使用pci_direct_conf1函数表
port_cf9_safe = true;
return 1;
}
release_resource(region);
type2:
if ((pci_probe & PCI_PROBE_CONF2) == 0)
return 0;
region = request_region(0xCF8, 4, "PCI conf2");
if (!region)
return 0;
region2 = request_region(0xC000, 0x1000, "PCI conf2");
if (!region2)
goto fail2;
if (pci_check_type2()) {
raw_pci_ops = &pci_direct_conf2;
port_cf9_safe = true;
return 2;
}
release_resource(region2);
fail2:
release_resource(region);
return 0;
}
pci_sanity_check()
探测Host-PCI桥
static int __init pci_sanity_check(struct pci_raw_ops *o)
{
u32 x = 0;
int year, devfn;
if (pci_probe & PCI_NO_CHECKS)
return 1;
/* Assume Type 1 works for newer systems.
This handles machines that don't have anything on PCI Bus 0. */
dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL);
if (year >= 2001)
return 1;
for (devfn = 0; devfn < 0x100; devfn++) { // 遍历逻辑设备号(设备号+功能号),从0到255
/* static int pci_conf1_read(unsigned int seg,
unsigned int bus,
unsigned int devfn,
int reg,
int len,
u32 *value) */
// 将总线号为0、逻辑设备号为devfn的逻辑设备的PCI_CLASS_DEVICE(2字节)读入x
if (o->read(0, 0, devfn, PCI_CLASS_DEVICE, 2, &x))
continue;
// 若类型是Host-PCI桥
if (x == PCI_CLASS_BRIDGE_HOST || x == PCI_CLASS_DISPLAY_VGA)
return 1;
// 将PCI_VENDOR_ID(2字节)读入x
if (o->read(0, 0, devfn, PCI_VENDOR_ID, 2, &x))
continue;
// 若厂商是intel或compaq
if (x == PCI_VENDOR_ID_INTEL || x == PCI_VENDOR_ID_COMPAQ)
return 1;
}
DBG(KERN_WARNING "PCI: Sanity check failed\n");
return 0;
}
pci_scan_child_bus()
深度优先搜索总线树上的所有设备
unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus)
{
unsigned int devfn, pass, max = bus->secondary;
struct pci_dev *dev;
pr_debug("PCI: Scanning bus %04x:%02x\n", pci_domain_nr(bus), bus->number);
/* Go find them, Rover! */
for (devfn = 0; devfn < 0x100; devfn += 8) // 遍历设备号
pci_scan_slot(bus, devfn);
/* Reserve buses for SR-IOV capability. */
max += pci_iov_bus_range(bus);
/*
* After performing arch-dependent fixup of the bus, look behind
* all PCI-to-PCI bridges on this bus.
*/
if (!bus->is_added) {
pr_debug("PCI: Fixups for bus %04x:%02x\n",
pci_domain_nr(bus), bus->number);
pcibios_fixup_bus(bus);
if (pci_is_root_bus(bus))
bus->is_added = 1;
}
for (pass=0; pass < 2; pass++)
list_for_each_entry(dev, &bus->devices, bus_list) {
if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
max = pci_scan_bridge(bus, dev, max, pass); // 扫描PCI桥,在pci_scan_bridge()中递归调用pci_scan_child_bus()
}
/*
* We've scanned the bus and so we know all about what's on
* the other side of any bridges that may be on this bus plus
* any devices.
*
* Return how far we've got finding sub-buses.
*/
pr_debug("PCI: Bus scan for %04x:%02x returning with max=%02x\n",
pci_domain_nr(bus), bus->number, max);
return max;
}
__pci_read_base()
读取PCI设备的BAR中的起始地址和长度,写入resource
int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
struct resource *res, unsigned int pos)
{
u32 l, sz, mask;
mask = type ? ~PCI_ROM_ADDRESS_ENABLE : ~0;
res->name = pci_name(dev);
pci_read_config_dword(dev, pos, &l); // 读BAR到l(起始地址)
pci_write_config_dword(dev, pos, mask); // 写BAR全1
pci_read_config_dword(dev, pos, &sz); // 读BAR到sz(长度)
pci_write_config_dword(dev, pos, l); // 写BAR l(恢复)
/*
* All bits set in sz means the device isn't working properly.
* If the BAR isn't implemented, all bits must be 0. If it's a
* memory BAR or a ROM, bit 0 must be clear; if it's an io BAR, bit
* 1 must be clear.
*/
if (!sz || sz == 0xffffffff)
goto fail;
/*
* I don't know how l can have all bits set. Copied from old code.
* Maybe it fixes a bug on some ancient platform.
*/
if (l == 0xffffffff)
l = 0;
if (type == pci_bar_unknown) {
type = decode_bar(res, l);
res->flags |= pci_calc_resource_flags(l) | IORESOURCE_SIZEALIGN;
if (type == pci_bar_io) {
l &= PCI_BASE_ADDRESS_IO_MASK;
mask = PCI_BASE_ADDRESS_IO_MASK & IO_SPACE_LIMIT;
} else {
l &= PCI_BASE_ADDRESS_MEM_MASK;
mask = (u32)PCI_BASE_ADDRESS_MEM_MASK;
}
} else {
res->flags |= (l & IORESOURCE_ROM_ENABLE);
l &= PCI_ROM_ADDRESS_MASK;
mask = (u32)PCI_ROM_ADDRESS_MASK;
}
if (type == pci_bar_mem64) {
u64 l64 = l;
u64 sz64 = sz;
u64 mask64 = mask | (u64)~0 << 32;
pci_read_config_dword(dev, pos + 4, &l);
pci_write_config_dword(dev, pos + 4, ~0);
pci_read_config_dword(dev, pos + 4, &sz);
pci_write_config_dword(dev, pos + 4, l);
l64 |= ((u64)l << 32);
sz64 |= ((u64)sz << 32);
sz64 = pci_size(l64, sz64, mask64);
if (!sz64)
goto fail;
if ((sizeof(resource_size_t) < 8) && (sz64 > 0x100000000ULL)) {
dev_err(&dev->dev, "can't handle 64-bit BAR\n");
goto fail;
} else if ((sizeof(resource_size_t) < 8) && l) {
/* Address above 32-bit boundary; disable the BAR */
pci_write_config_dword(dev, pos, 0);
pci_write_config_dword(dev, pos + 4, 0);
res->start = 0;
res->end = sz64;
} else {
res->start = l64;
res->end = l64 + sz64;
dev_printk(KERN_DEBUG, &dev->dev,
"reg %x %s: %pR\n", pos,
(res->flags & IORESOURCE_PREFETCH) ?
"64bit mmio pref" : "64bit mmio",
res);
}
res->flags |= IORESOURCE_MEM_64;
} else {
sz = pci_size(l, sz, mask);
if (!sz)
goto fail;
res->start = l; // BAR中的起始地址
res->end = l + sz; // BAR中的结束地址
dev_printk(KERN_DEBUG, &dev->dev, "reg %x %s: %pR\n", pos,
(res->flags & IORESOURCE_IO) ? "io port" :
((res->flags & IORESOURCE_PREFETCH) ?
"32bit mmio pref" : "32bit mmio"),
res);
}
out:
return (type == pci_bar_mem64) ? 1 : 0;
fail:
res->flags = 0;
goto out;
}
pci_read_bridge_bases()
读取PCI桥的过滤窗口的起始地址和长度,写入resource
void __devinit pci_read_bridge_bases(struct pci_bus *child)
{
struct pci_dev *dev = child->self;
u8 io_base_lo, io_limit_lo;
u16 mem_base_lo, mem_limit_lo;
unsigned long base, limit;
struct resource *res;
int i;
if (pci_is_root_bus(child)) /* It's a host bus, nothing to read */
return;
if (dev->transparent) {
dev_info(&dev->dev, "transparent bridge\n");
for(i = 3; i < PCI_BUS_NUM_RESOURCES; i++)
child->resource[i] = child->parent->resource[i - 3];
}
// 读取IO地址窗口(起始地址和长度)
res = child->resource[0];
pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo);
pci_read_config_byte(dev, PCI_IO_LIMIT, &io_limit_lo);
base = (io_base_lo & PCI_IO_RANGE_MASK) << 8;
limit = (io_limit_lo & PCI_IO_RANGE_MASK) << 8;
if ((io_base_lo & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) {
u16 io_base_hi, io_limit_hi;
pci_read_config_word(dev, PCI_IO_BASE_UPPER16, &io_base_hi);
pci_read_config_word(dev, PCI_IO_LIMIT_UPPER16, &io_limit_hi);
base |= (io_base_hi << 16);
limit |= (io_limit_hi << 16);
}
if (base <= limit) {
res->flags = (io_base_lo & PCI_IO_RANGE_TYPE_MASK) | IORESOURCE_IO;
if (!res->start)
res->start = base;
if (!res->end)
res->end = limit + 0xfff;
dev_printk(KERN_DEBUG, &dev->dev, "bridge io port: %pR\n", res);
}
// 读取存储器地址窗口(起始地址和长度)
res = child->resource[1];
pci_read_config_word(dev, PCI_MEMORY_BASE, &mem_base_lo);
pci_read_config_word(dev, PCI_MEMORY_LIMIT, &mem_limit_lo);
base = (mem_base_lo & PCI_MEMORY_RANGE_MASK) << 16;
limit = (mem_limit_lo & PCI_MEMORY_RANGE_MASK) << 16;
if (base <= limit) {
res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM;
res->start = base;
res->end = limit + 0xfffff;
dev_printk(KERN_DEBUG, &dev->dev, "bridge 32bit mmio: %pR\n",
res);
}
// 读取"可预取"存储器地址窗口(起始地址和长度)
res = child->resource[2];
pci_read_config_word(dev, PCI_PREF_MEMORY_BASE, &mem_base_lo);
pci_read_config_word(dev, PCI_PREF_MEMORY_LIMIT, &mem_limit_lo);
base = (mem_base_lo & PCI_PREF_RANGE_MASK) << 16;
limit = (mem_limit_lo & PCI_PREF_RANGE_MASK) << 16;
if ((mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) {
u32 mem_base_hi, mem_limit_hi;
pci_read_config_dword(dev, PCI_PREF_BASE_UPPER32, &mem_base_hi);
pci_read_config_dword(dev, PCI_PREF_LIMIT_UPPER32, &mem_limit_hi);
/*
* Some bridges set the base > limit by default, and some
* (broken) BIOSes do not initialize them. If we find
* this, just assume they are not being used.
*/
if (mem_base_hi <= mem_limit_hi) {
#if BITS_PER_LONG == 64
base |= ((long) mem_base_hi) << 32;
limit |= ((long) mem_limit_hi) << 32;
#else
if (mem_base_hi || mem_limit_hi) {
dev_err(&dev->dev, "can't handle 64-bit "
"address space for bridge\n");
return;
}
#endif
}
}
if (base <= limit) {
res->flags = (mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) |
IORESOURCE_MEM | IORESOURCE_PREFETCH;
if (res->flags & PCI_PREF_RANGE_TYPE_64)
res->flags |= IORESOURCE_MEM_64;
res->start = base;
res->end = limit + 0xfffff;
dev_printk(KERN_DEBUG, &dev->dev, "bridge %sbit mmio pref: %pR\n",
(res->flags & PCI_PREF_RANGE_TYPE_64) ? "64" : "32",
res);
}
}
alloc_resource()
若申请子区间失败,设置区间的起始地址为0
static inline void __devinit alloc_resource(struct pci_dev *dev, int idx)
{
struct resource *pr, *r = &dev->resource[idx];
pr_debug("PCI: Allocating %s: Resource %d: %016llx..%016llx [%x]\n",
pci_name(dev), idx,
(unsigned long long)r->start,
(unsigned long long)r->end,
(unsigned int)r->flags);
pr = pci_find_parent_resource(dev, r);
if (!pr || (pr->flags & IORESOURCE_UNSET) ||
request_resource(pr, r) < 0) { // 若没找到满足需要的父区间、IORESOURCE_UNSET、申请子区间失败
printk(KERN_WARNING "PCI: Cannot allocate resource region %d"
" of device %s, will remap\n", idx, pci_name(dev));
if (pr)
pr_debug("PCI: parent is %p: %016llx-%016llx [%x]\n",
pr,
(unsigned long long)pr->start,
(unsigned long long)pr->end,
(unsigned int)pr->flags);
/* We'll assign a new address later */
r->flags |= IORESOURCE_UNSET;
r->end -= r->start;
r->start = 0; // 设置区间的起始地址为0
}
}
find_resource()
根据resource的长度分配总线地址,写入resource(真正分配总线地址的函数)
static int find_resource(struct resource *root, struct resource *new,
resource_size_t size, resource_size_t min,
resource_size_t max, resource_size_t align,
void (*alignf)(void *, struct resource *,
resource_size_t, resource_size_t),
void *alignf_data)
{
struct resource *this = root->child; // this指向子区间链表
new->start = root->start; // 设置new->start为root->start
/*
* Skip past an allocated resource that starts at 0, since the assignment
* of this->start - 1 to new->end below would cause an underflow.
*/
if (this && this->start == 0) { // 若第一个子区间的起始地址为0
new->start = this->end + 1; // 设置new->start为第一个子区间的结束地址+1
this = this->sibling; // this向右移动
}
for(;;) {
if (this) // 若this非空,设置new->end为this指向子区间的起始地址-1
new->end = this->start - 1;
else // 若this为空,设置new->end为root->end
new->end = root->end;
if (new->start < min)
new->start = min;
if (new->end > max)
new->end = max;
new->start = ALIGN(new->start, align); // 地址对齐
if (alignf)
alignf(alignf_data, new, size, align);
if (new->start < new->end && new->end - new->start >= size - 1) { // 找到足够大的子区间
new->end = new->start + size - 1;
return 0;
}
if (!this) // this为空跳出循环
break;
new->start = this->end + 1;
this = this->sibling; // this向右移动
}
return -EBUSY;
}
pci_update_resource()
将分配的总线地址写入BAR
void pci_update_resource(struct pci_dev *dev, int resno)
{
struct pci_bus_region region;
u32 new, check, mask;
int reg;
enum pci_bar_type type;
struct resource *res = dev->resource + resno; // 得到dev->resource[resno]
/*
* Ignore resources for unimplemented BARs and unused resource slots
* for 64 bit BARs.
*/
if (!res->flags)
return;
/*
* Ignore non-moveable resources. This might be legacy resources for
* which no functional BAR register exists or another important
* system resource we shouldn't move around.
*/
if (res->flags & IORESOURCE_PCI_FIXED)
return;
pcibios_resource_to_bus(dev, ®ion, res); // 根据res得到region
dev_dbg(&dev->dev, "BAR %d: got res %pR bus [%#llx-%#llx] "
"flags %#lx\n", resno, res,
(unsigned long long)region.start,
(unsigned long long)region.end,
(unsigned long)res->flags);
new = region.start | (res->flags & PCI_REGION_FLAG_MASK); // 得到region中的总线地址
if (res->flags & IORESOURCE_IO)
mask = (u32)PCI_BASE_ADDRESS_IO_MASK;
else
mask = (u32)PCI_BASE_ADDRESS_MEM_MASK;
reg = pci_resource_bar(dev, resno, &type);
if (!reg)
return;
if (type != pci_bar_unknown) {
if (!(res->flags & IORESOURCE_ROM_ENABLE))
return;
new |= PCI_ROM_ADDRESS_ENABLE;
}
pci_write_config_dword(dev, reg, new); // 将总线地址写入BAR
pci_read_config_dword(dev, reg, &check);
if ((new ^ check) & mask) {
dev_err(&dev->dev, "BAR %d: error updating (%#08x != %#08x)\n",
resno, new, check);
}
if ((new & (PCI_BASE_ADDRESS_SPACE|PCI_BASE_ADDRESS_MEM_TYPE_MASK)) ==
(PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64)) {
new = region.start >> 16 >> 16;
pci_write_config_dword(dev, reg + 4, new);
pci_read_config_dword(dev, reg + 4, &check);
if (check != new) {
dev_err(&dev->dev, "BAR %d: error updating "
"(high %#08x != %#08x)\n", resno, new, check);
}
}
res->flags &= ~IORESOURCE_UNSET;
dev_dbg(&dev->dev, "BAR %d: moved to bus [%#llx-%#llx] flags %#lx\n",
resno, (unsigned long long)region.start,
(unsigned long long)region.end, res->flags);
}
配置空间读写函数
在linux-2.6.32.27/include/linux/pci.h中定义了pci_read_config_dword()和pci_write_config_dword(),它们分别调用pci_bus_read_config_dword()和pci_bus_write_config_dword()
static inline int pci_read_config_dword(struct pci_dev *dev, int where,
u32 *val)
{
return pci_bus_read_config_dword(dev->bus, dev->devfn, where, val);
}
static inline int pci_write_config_dword(struct pci_dev *dev, int where,
u32 val)
{
return pci_bus_write_config_dword(dev->bus, dev->devfn, where, val);
}
在linux-2.6.32.27/drivers/pci/access.c中通过宏定义了pci_bus_read_config_dword()和pci_bus_write_config_dword()
PCI_OP_READ(dword, u32, 4)
PCI_OP_WRITE(dword, u32, 4)
#define PCI_OP_READ(size,type,len) \
int pci_bus_read_config_##size \
(struct pci_bus *bus, unsigned int devfn, int pos, type *value) \
{ \
int res; \
unsigned long flags; \
u32 data = 0; \
if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \
spin_lock_irqsave(&pci_lock, flags); \
res = bus->ops->read(bus, devfn, pos, len, &data); \
*value = (type)data; \
spin_unlock_irqrestore(&pci_lock, flags); \
return res; \
}
#define PCI_OP_WRITE(size,type,len) \
int pci_bus_write_config_##size \
(struct pci_bus *bus, unsigned int devfn, int pos, type value) \
{ \
int res; \
unsigned long flags; \
if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \
spin_lock_irqsave(&pci_lock, flags); \
res = bus->ops->write(bus, devfn, pos, len, value); \
spin_unlock_irqrestore(&pci_lock, flags); \
return res; \
}
在linux-2.6.32.27/arch/x86/pci/common.c中定义了pci_root_ops,同时通过pci_scan_bus_parented()传入pci_root_ops
pci_bus_read_config_dword()和pci_bus_write_config_dword()分别调用pci_read()和pci_write(),pci_read()和pci_write()分别调用raw_pci_ops的read和write,raw_pci_ops在pci_direct_probe()中被初始化为pci_direct_conf1,即pci_read()和pci_write()分别调用pci_conf1_read()和pci_conf1_write()
pci_root_ops
pci_read()和pci_write()
struct pci_ops pci_root_ops = {
.read = pci_read,
.write = pci_write,
};
static int pci_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *value)
{
return raw_pci_read(pci_domain_nr(bus), bus->number,
devfn, where, size, value);
}
static int pci_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 value)
{
return raw_pci_write(pci_domain_nr(bus), bus->number,
devfn, where, size, value);
}
int raw_pci_read(unsigned int domain, unsigned int bus, unsigned int devfn,
int reg, int len, u32 *val)
{
if (domain == 0 && reg < 256 && raw_pci_ops)
return raw_pci_ops->read(domain, bus, devfn, reg, len, val);
if (raw_pci_ext_ops)
return raw_pci_ext_ops->read(domain, bus, devfn, reg, len, val);
return -EINVAL;
}
int raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn,
int reg, int len, u32 val)
{
if (domain == 0 && reg < 256 && raw_pci_ops)
return raw_pci_ops->write(domain, bus, devfn, reg, len, val);
if (raw_pci_ext_ops)
return raw_pci_ext_ops->write(domain, bus, devfn, reg, len, val);
return -EINVAL;
}
pci_direct_conf1
pci_conf1_read()和pci_conf1_write()
struct pci_raw_ops pci_direct_conf1 = {
.read = pci_conf1_read,
.write = pci_conf1_write,
};
static int pci_conf1_read(unsigned int seg, unsigned int bus,
unsigned int devfn, int reg, int len, u32 *value)
{
unsigned long flags;
if ((bus > 255) || (devfn > 255) || (reg > 4095)) {
*value = -1;
return -EINVAL;
}
spin_lock_irqsave(&pci_config_lock, flags);
// 通过outl写32位io端口
outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8);
switch (len) {
case 1:
*value = inb(0xCFC + (reg & 3));
break;
case 2:
*value = inw(0xCFC + (reg & 2));
break;
case 4:
// 通过inl读32位io端口
*value = inl(0xCFC);
break;
}
spin_unlock_irqrestore(&pci_config_lock, flags);
return 0;
}
static int pci_conf1_write(unsigned int seg, unsigned int bus,
unsigned int devfn, int reg, int len, u32 value)
{
unsigned long flags;
if ((bus > 255) || (devfn > 255) || (reg > 4095))
return -EINVAL;
spin_lock_irqsave(&pci_config_lock, flags);
// 通过outl写32位io端口
outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8);
switch (len) {
case 1:
outb((u8)value, 0xCFC + (reg & 3));
break;
case 2:
outw((u16)value, 0xCFC + (reg & 2));
break;
case 4:
// 通过outl写32位io端口
outl((u32)value, 0xCFC);
break;
}
spin_unlock_irqrestore(&pci_config_lock, flags);
return 0;
}
// 生成综合地址
#define PCI_CONF1_ADDRESS(bus, devfn, reg) \
(0x80000000 | ((reg & 0xF00) << 16) | (bus << 16) \
| (devfn << 8) | (reg & 0xFC))
ioport/iomem_resource资源树
__request_region()
__request_region递归查询ioport/iomem_resource资源树,将resource插入合适的位置
struct resource * __request_region(struct resource *parent,
resource_size_t start, resource_size_t n,
const char *name, int flags)
{
struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL);
if (!res)
return NULL;
res->name = name;
res->start = start;
res->end = start + n - 1;
res->flags = IORESOURCE_BUSY;
res->flags |= flags;
write_lock(&resource_lock);
for (;;) {
struct resource *conflict;
conflict = __request_resource(parent, res); // 调用__request_resource()
if (!conflict)
break;
if (conflict != parent) {
parent = conflict;
if (!(conflict->flags & IORESOURCE_BUSY)) // IORESOURCE_BUSY表示驱动已经标记这个resource为busy
continue; // 递归调用__request_resource()
}
/* Uhhuh, that didn't work out.. */
kfree(res);
res = NULL;
break;
}
write_unlock(&resource_lock);
return res;
}
__request_resource()
将当前区间插入子区间链表,返回值有三种:
- NULL,表示成功插入子区间链表
- 父区间,表示父区间不满足要求
- 子区间,表示父区间满足要求,但当前区间和子区间有交集
返回子区间后,__request_region会递归调用__request_resource()
static struct resource * __request_resource(struct resource *root, struct resource *new)
{
resource_size_t start = new->start;
resource_size_t end = new->end;
struct resource *tmp, **p;
if (end < start)
return root;
if (start < root->start)
return root;
if (end > root->end)
return root;
p = &root->child; // *p指向子区间链表
for (;;) {
tmp = *p; // tmp依次遍历链表
if (!tmp || tmp->start > end) {
// 将new插到tmp前面,return NULL
new->sibling = tmp;
*p = new;
new->parent = root;
return NULL;
}
p = &tmp->sibling; // *p向右移动
if (tmp->end < start)
continue;
// 若tmp ->start <= end和tmp->end >= start(即两者有交集),return tmp
return tmp;
}
}