PCI有3种地址空间:I/O空间,内存地址空间和配置空间
CPU可以访问所有的地址空间。
I/O空间和内存地址空间由设备驱动程序使用。
而PCI配置空间由linux内核中的PCI初始代码使用,这些代码用于配置PCI设备,比如中断号,I/O或内存基地址。
1. PCI 的配置空间,及其软件表示
/include /linux /pci_regs.h文件定义了PCI配置空间寄存器。
1.1 一个PCI的配置空间至少有256字节,布局如下图所示:
1.2 Kernel通常使用 pci_device_id 来让PCI设备匹配上相应的PCI驱动:
与USB设备驱动中定义的usb_device_id一样,定义了pci_device_id结构体,并将它导出到用户空间。在include/ linux/ mod_devicetable.h中。
struct pci_device_id {
__u32 vendor, device; /* Vendor and device ID or PCI_ANY_ID*/
__u32 subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */
__u32 class, class_mask; /* (class,subclass,prog-if) triplet */
kernel_ulong_t driver_data; /* Data private to the driver */
};
参数如下:
--- Vendor ID : 占16bit, 也就是2个字节,用来标识硬件厂商
A central authority governs the registry of vendor IDs, and hardware manufactures must apply to this body when they would like a new vendor id.
--- Device ID : 占16bit,标识是哪类设备的ID,
--- subDevice ID : 各占16bit, 标识 PCI 子系统中的厂商号和设备号。
--- Class mask : 描述设备类型。
--- driver data : PCI 设备驱动私有数据,
1.3 内核提供了下面几个宏,来帮助生成 pci_device_id:
PCI_DEVICE(vend, dev); // 用来指定 设备厂商ID和设备ID
/**
* PCI_DEVICE - macro used to describe a specific pci device
* @vend: the 16 bit PCI Vendor ID
* @dev: the 16 bit PCI Device ID
*
* This macro is used to create a struct pci_device_id that matches a
* specific device. The subvendor and subdevice fields will be set to
* PCI_ANY_ID.
*/
#define PCI_DEVICE(vend,dev) \
.vendor = (vend), .device = (dev), \
.subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID
PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C561)
PCI_ANY_ID 定义如下:
#define PCI_ANY_ID (~0)
PCI_VDEVICE(vendor, device); //这里传输的是vendor name, 通过宏PCI_VENDOR_ID, 来查出对应的vendor ID, 这点与 PCI_DEVICE()不同。/**
* PCI_VDEVICE - macro used to describe a specific pci device in short form
* @vendor: the vendor name
* @device: the 16 bit PCI Device ID
*
* This macro is used to create a struct pci_device_id that matches a
* specific PCI device. The subvendor, and subdevice fields will be set
* to PCI_ANY_ID. The macro allows the next field to follow as the device
* private data.
*/
#define PCI_VDEVICE(vendor, device) \
PCI_VENDOR_ID_##vendor, (device), \
PCI_ANY_ID, PCI_ANY_ID, 0, 0
Although driver_data isn’t used by the PCI core, your driver can use it for its own purposes:
{ PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_6410),1 }
PCI_DEVICE_CLASS(dev_class, dev_class_mask); //用来指定 PCI 设备类型
/**
* PCI_DEVICE_CLASS - macro used to describe a specific pci device class
* @dev_class: the class, subclass, prog-if triple for this device
* @dev_class_mask: the class mask for this device
*
* This macro is used to create a struct pci_device_id that matches a
* specific PCI class. The vendor, device, subvendor, and subdevice
* fields will be set to PCI_ANY_ID.
*/
#define PCI_DEVICE_CLASS(dev_class,dev_class_mask) \
.class = (dev_class), .class_mask = (dev_class_mask), \
.vendor = PCI_ANY_ID, .device = PCI_ANY_ID, \
.subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID
PCI_DEVICE_CLASS(PCI_CLASS_WIRELESS_WHCI, ~0)
PCI驱动使用 宏MODULE_DEVICE_TABLE 来导出一系列 pci_device_id 结构,也就是PCI设备表到用户空间,
如下所示:
static struct pci_device_id netdrv_pci_tbl[] = {
{0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, NETDRV_CB },
{0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SMC1211TX },
/* {0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MPX5030 },*/
{0x1500, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DELTA8139 },
{0x4033, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ADDTRON8139 },
{0,}
};
MODULE_DEVICE_TABLE (pci, netdrv_pci_tbl);
宏 DEFINE_PCI_DEVICE_TABLE() ; ---用来便捷的定义一系列 pci_device_id
/**
* DEFINE_PCI_DEVICE_TABLE - macro used to describe a pci device table
* @_table: device table name
*
* This macro is used to create a struct pci_device_id array (a device table)
* in a generic manner.
*/
#define DEFINE_PCI_DEVICE_TABLE(_table) \
const struct pci_device_id _table[] __devinitconst
At build time, this information gets included into databases that user-space facilities like udev and modprobe can use to load appropriate drivers when matching devices are inserted
下图所示:
2.1 pci_driver 结构体
pci_driver 结构体用来定义PCI驱动。
struct pci_driver {
struct list_head node;
char *name;
const struct pci_device_id *id_table; /* must be non-NULL for probe to be called */
int (*probe) (struct pci_dev *dev, const struct pci_device_id *id); /* New device inserted */
void (*remove) (struct pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver) */
int (*suspend) (struct pci_dev *dev, pm_message_t state); /* Device suspended */
int (*suspend_late) (struct pci_dev *dev, pm_message_t state);
int (*resume_early) (struct pci_dev *dev);
int (*resume) (struct pci_dev *dev); /* Device woken up */
void (*shutdown) (struct pci_dev *dev);
struct pci_error_handlers *err_handler;
struct device_driver driver; //设备驱动,
struct pci_dynids dynids;
};
参数:
--- name : 驱动名,通常设置成模块名
--- *id_table : 指向 PCI驱动对应的一系列 pci_device_id 表的指针。
--- *probe : 当PCI core 发现一个设备与 *id_table 中的选项配对上时,该函数会被调用。
--- *remove : 当设备移除或驱动被卸载时,会调用该函数。
2.2 pci_register_driver(struct pci_driver *driver); --- 注册 PCI 驱动。
定义如下:
/*
* pci_register_driver must be a macro so that KBUILD_MODNAME can be expanded
*/
#define pci_register_driver(driver) \
__pci_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
/**
* __pci_register_driver - register a new pci driver
* @drv: the driver structure to register
* @owner: owner module of drv
* @mod_name: module name string
*
* Adds the driver structure to the list of registered drivers.
* Returns a negative value on error, otherwise 0.
* If no error occurred, the driver remains registered even if
* no device was claimed during registration.
*/
int __pci_register_driver(struct pci_driver *drv, struct module *owner,
const char *mod_name)
{
int error;
/* initialize common driver fields */
drv->driver.name = drv->name;
drv->driver.bus = &pci_bus_type; //指明是PCI bus 类型
drv->driver.owner = owner;
drv->driver.mod_name = mod_name;
spin_lock_init(&drv->dynids.lock);
INIT_LIST_HEAD(&drv->dynids.list);
/* register with core */
error = driver_register(&drv->driver); //注册设备驱动
if (error)
goto out;
error = pci_create_newid_file(drv);
if (error)
goto out_newid;
error = pci_create_removeid_file(drv);
if (error)
goto out_removeid;
out:
return error;
out_removeid:
pci_remove_newid_file(drv);
out_newid:
driver_unregister(&drv->driver);
goto out;
}
pci_create_newid_file(struct pci_driver *drv);
static int
pci_create_newid_file(struct pci_driver *drv)
{
int error = 0;
if (drv->probe != NULL)
error = driver_create_file(&drv->driver, &driver_attr_new_id);
return error;
}
pci_create_removeid_file(struct pci_driver *drv);
static int
pci_create_removeid_file(struct pci_driver *drv)
{
int error = 0;
if (drv->probe != NULL)
error = driver_create_file(&drv->driver,&driver_attr_remove_id);
return error;
}
static void pci_remove_newid_file(struct pci_driver *drv);
static void pci_remove_newid_file(struct pci_driver *drv)
{
driver_remove_file(&drv->driver, &driver_attr_new_id);
}
void pci_unregister_driver(struct pci_driver *drv); --- 卸载 pci_driver
/**
* pci_unregister_driver - unregister a pci driver
* @drv: the driver structure to unregister
*
* Deletes the driver structure from the list of registered PCI drivers,
* gives it a chance to clean up by calling its remove() function for
* each device it was responsible for, and marks those devices as
* driverless.
*/
void
pci_unregister_driver(struct pci_driver *drv)
{
pci_remove_removeid_file(drv);
pci_remove_newid_file(drv);
driver_unregister(&drv->driver);
pci_free_dynids(drv);
}
例子:
static struct pci_driver mydev_driver = {
.name = “mydev”,
.id_table = mydev_devices,
.probe = mydev_probe,
.remove = mydev_remove,
};
static int __init
mydev_init(void)
{
return pci_register_driver(&mydev_driver);
}
static void __exit
mydev_exit(void)
{
pci_unregister_driver(&driver);
}
2.3 使能/禁止 PCI设备:
int pci_enable_device(struct pci_dev *dev); ---
/**
* pci_enable_device - Initialize device before it's used by a driver.
* @dev: PCI device to be initialized
*
* Initialize device before it's used by a driver. Ask low-level code
* to enable I/O and memory. Wake up the device if it was suspended.
* Beware, this function can fail.
*
* Note we don't actually enable the device many times if we call
* this function repeatedly (we just increment the count).
*/
int pci_enable_device(struct pci_dev *dev)
{
return __pci_enable_device_flags(dev, IORESOURCE_MEM | IORESOURCE_IO);
}
void pci_disable_device(struct pci_dev *dev);
/**
* pci_disable_device - Disable PCI device after use
* @dev: PCI device to be disabled
*
* Signal to the system that the PCI device is not in use by the system
* anymore. This only involves disabling PCI bus-mastering, if active.
*
* Note we don't actually disable the device until all callers of
* pci_enable_device() have called pci_disable_device().
*/
void
pci_disable_device(struct pci_dev *dev)
{
struct pci_devres *dr;
dr = find_pci_dr(dev);
if (dr)
dr->enabled = 0;
if (atomic_sub_return(1, &dev->enable_cnt) != 0)
return;
do_pci_disable_device(dev);
dev->is_busmaster = 0;
}
pci probe() 中通常会用到,pci_enable_device(), 如下所示:
static int __devinit
mydev_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
int rc = pci_enable_device(pdev); //使能pci设备, Must enable the device before we can access any of its memory resources.
u8 irq;
if (rc) { /* handle error */
return rc;
}
rc = pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq); //Read the IRQ from the device’s configuration space,
//using one of the provided functions for reading/writing to/from the device configuration space.
if (rc) { /* handle error */
return rc;
}
rc = request_irq(irq, mydev_interrupt, IRQF_SHARED, “mydev”, NULL); //With IRQ known, we can proceed as usual. …
return 0;
}
1. PCI总线。
PCI总线体系结构中,PCI桥设备比较重要,它将父总线与子总线连接在一起。
PCI桥一种特殊的PCI设备,包括以下几种:
-----Host/PCI桥,用于连接CPU与PCI根总线,第一个根总线的编号为0。
-----PCI-to-PCI桥,用于连接PCI父总线与子总线。
-----PCI/ISA桥,用于连接老式的ISA总线。
PCI总线用pci_bus结构来描述。在kernel/ include/ linux / pci.h 中
struct pci_bus {
struct list_head node; /* node in list of buses */
struct pci_bus *parent; /* parent bus this bridge is on */
struct list_head children; /* list of child buses */
struct list_head devices; /* list of devices on this bus */
struct pci_dev *self; /* bridge device as seen by parent */
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 */
char name[48];
unsigned short bridge_ctl; /* manage NO_ISA/FBB/et al behaviors */
pci_bus_flags_t bus_flags; /* Inherited by child busses */
struct device *bridge;
struct class_device class_dev;
struct bin_attribute *legacy_io; /* legacy I/O for this bus */
struct bin_attribute *legacy_mem; /* legacy mem */
};
2. PCI设备
所有类型的PCI设备都可以用pci_dev结构描述。在在kernel/ include/ linux / pci.h 中
struct pci_dev {
struct list_head global_list; /* node in list of all PCI devices */
struct list_head bus_list; /* node in per-bus list */
struct pci_bus *bus; /* bus this device is on */
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 */
unsigned int devfn; /* encoded device & function index */
unsigned short vendor;
unsigned short device;
unsigned short subsystem_vendor;
unsigned short subsystem_device;
unsigned int class; /* 3 bytes: (base,sub,prog-if) */
u8 revision; /* PCI revision, low byte of class word */
u8 hdr_type; /* PCI header type (`multi' flag masked out) */
u8 rom_base_reg; /* which config register controls the ROM */
u8 pin; /* which interrupt pin this device uses */
struct pci_driver *driver; /* which driver has allocated this device */
u64 dma_mask; /* Mask of the bits of bus address this
device implements. Normally this is
0xffffffff. You only need to change
this if your device has broken DMA
or supports 64-bit transfers. */
pci_power_t current_state; /* Current operating state. In ACPI-speak,
this is D0-D3, D0 being fully functional,
and D3 being off. */
pci_channel_state_t error_state; /* current connectivity state */
struct device dev; /* Generic device interface */
/* device is compatible with these IDs */
unsigned short vendor_compatible[DEVICE_COUNT_COMPATIBLE];
unsigned short device_compatible[DEVICE_COUNT_COMPATIBLE];
int cfg_size; /* Size of configuration space */
/*
* Instead of touching interrupt line and base address registers
* directly, use the values stored here. They might be different!
*/
unsigned int irq;
struct resource resource[DEVICE_COUNT_RESOURCE]; /* I/O and memory regions + expansion ROMs */
/* These fields are used by common fixups */
unsigned int transparent:1; /* Transparent PCI bridge */
unsigned int multifunction:1;/* Part of multi-function device */
/* keep track of device state */
unsigned int is_busmaster:1; /* device is busmaster */
unsigned int no_msi:1; /* device may not use msi */
unsigned int no_d1d2:1; /* only allow d0 or d3 */
unsigned int block_ucfg_access:1; /* userspace config space access is blocked */
unsigned int broken_parity_status:1; /* Device generates false positive parity */
unsigned int msi_enabled:1;
unsigned int msix_enabled:1;
unsigned int is_managed:1;
atomic_t enable_cnt; /* pci_enable_device has been called */
u32 saved_config_space[16]; /* config space saved at suspend time */
struct hlist_head saved_cap_space;
struct bin_attribute *rom_attr; /* attribute descriptor for sysfs ROM entry */
int rom_attr_enabled; /* has display of the rom attribute been enabled? */
struct bin_attribute *res_attr[DEVICE_COUNT_RESOURCE]; /* sysfs file for resources */
#ifdef CONFIG_PCI_MSI
struct list_head msi_list;
#endif
};
3.PCI 配置空间的访问。
PCI有3种地址空间:I/O空间,内存地址空间和配置空间。
CPU可以访问所有的地址空间。
I/O空间和内存地址空间由设备驱动程序使用。
而PCI配置空间由linux内核中的PCI初始代码使用,这些代码用于配置PCI设备,比如中断号,I/O或内存基地址。
/include /linux /pci_regs.h文件定义了PCI配置空间寄存器。
pci_bus结构体中的pci_ops指针成员,指向PCI总线所使用的配置空间的访问操作。
struct pci_ops {
int (*read)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val);
int (*write)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val);
};
也在在
kernel/ include/ linux / pci.h中, 具体访问函数如下:
int pci_bus_read_config_byte (struct pci_bus *bus, unsigned int devfn, int where, u8 *val);
int pci_bus_read_config_word (struct pci_bus *bus, unsigned int devfn, int where, u16 *val);
int pci_bus_read_config_dword (struct pci_bus *bus, unsigned int devfn, int where, u32 *val);
int pci_bus_write_config_byte (struct pci_bus *bus, unsigned int devfn, int where, u8 val);
int pci_bus_write_config_word (struct pci_bus *bus, unsigned int devfn, int where, u16 val);
int pci_bus_write_config_dword (struct pci_bus *bus, unsigned int devfn, int where, u32 val);
static inline int pci_read_config_byte(struct pci_dev *dev, int where, u8 *val)
{
return pci_bus_read_config_byte (dev->bus, dev->devfn, where, val);
}
static inline int pci_read_config_word(struct pci_dev *dev, int where, u16 *val)
{
return pci_bus_read_config_word (dev->bus, dev->devfn, where, val);
}
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_byte(struct pci_dev *dev, int where, u8 val)
{
return pci_bus_write_config_byte (dev->bus, dev->devfn, where, val);
}
static inline int pci_write_config_word(struct pci_dev *dev, int where, u16 val)
{
return pci_bus_write_config_word (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);
}
4. PCI
设备驱动结构。