pcie总线向下兼容pci总线,文中所述pci默认等于pcie
pcie拓扑主要由总线、桥和设备卡组成,桥将父总线与子总线连接在一起形成树型结构。桥主要分为一下三类:
pcie由三种地址空间,分别是:PCI配置空间
、PCI/IO空间
和PCI内存地址空间
PCI配置空间
可以理解为pcie设备卡的配置寄存器,这个配置寄存器有一定的规范,pci协议中大小为256字节,pcie协议中可以扩展到4K。其中前64字节是固化在设备中的,不可读写,其他部分可以读写。访问配置寄存器的方法是借助北桥,北桥将0xCF8 ~ 0xCFF的地址用作pci卡配置用。其中向0xCF8 ~ 0xCFC写入要访问的PCI的卡id,id由pic总线编号+设备编号+功能编号+寄存器号组成,具体格式如下图。上一步选中卡和寄存器之后读写0xCFC ~ 0xCFF,北桥会发送配置事务完成寄存器的配置。
PCI内存地址空间
可以理解为pci设备的数据寄存器,设备设计的时候定好的。这段空间记录在了PCI配置空间
的前64k信息里面。操作系统只需要吧这段地址映射到内存就可以访问了。
PCI/IO空间
可以理解为pcie设备的可寻址空间,32为的pci总线可寻址空间为4GB,所以PCI内存地址空间
不可以大于4GB
其实配置寄存器分为三种:
PCI Bridge 的配置空间相比较 PCI Agent 的配置空间,多了 Bus Number 寄存器
读写pci设备的配置寄存器
pci_read_config_byte/word/dword(struct pci_dev *pdev, int offset, int *value);
pci_write_config_byte/word/dword(struct pci_dev *pdev, int offset, int *value);
获取配置寄存器单中的base addr起始地址,bar为第几个地址,一共六个
pci_resource_start(dev, bar) /*Bar值的范围为0-5*/
获取配置寄存器单中的base addr结束地址
pci_resource_end(dev, bar) /*Bar值的范围为0-5*/
获取配置寄存器单中的base addr状态,状态还是比较多的,详见ioport.h。
pci_resource_flags(dev, bar) /*Bar值的范围为0-5*/
获取配置寄存器单中的base addr地址长度,也等于pci_resource_end((dev), (bar))-pci_resource_start((dev), (bar)) + 1)
pci_resource_len(dev,bar) /*Bar值的范围为0-5*/
申请或者释放资源
int pci_request_regions(struct pci_dev *pdev, const char *res_name);
void pci_release_regions(struct pci_dev *pdev);
使能或禁用设备
int pci_enable_device(struct pci_dev *pdev);
int pci_disable_device(struct pci_dev *pdev);
设置为主设备
void pci_set_master(struct pci_dev *pdev);
查找设备
struct pci_dev *pci_find_slot (unsigned int bus,unsigned int devfn);
设置电源状态
int pci_set_power_state(struct pci_dev *pdev, pci_power_t state);
查找功能
int pci_find_capability(struct pci_dev *pdev, int cap);
启用禁用禁止写入
int pci_set_mwi(struct pci_dev *pdev);
void pci_clear_mwi(struct pci_dev *pdev);
pci设备的框架跟bus差不多,也都万变不离其踪,大致是下面的步骤:
1.驱动的注册:
2.驱动适配:
3.pci设备的读写
pci_dev
结构体基本对应配置寄存器里面的内容
struct pci_dev {
struct list_head global_list;
struct list_head bus_list;
struct pci_bus *bus;
struct pci_bus *subordinate;
void *sysdata;
struct proc_dir_entry *procent;
unsigned int devfn;
unsigned short vendor;
unsigned short device;
unsigned short subsystem_vendor;
unsigned short subsystem_device;
unsigned int class;
u8 hdr_type;
u8 rom_base_reg;
struct pci_driver *driver;
void *driver_data;
u64 dma_mask;
u32 current_state;
unsigned short vendor_compatible[DEVICE_COUNT_COMPATIBLE];
unsigned short device_compatible[DEVICE_COUNT_COMPATIBLE];
unsigned int irq;
struct resource resource[DEVICE_COUNT_RESOURCE];
struct resource dma_resource[DEVICE_COUNT_DMA];
struct resource irq_resource[DEVICE_COUNT_IRQ];
char name[80];
char slot_name[8];
int active;
int ro;
unsigned short regs;
int (*prepare)(struct pci_dev *dev);
int (*activate)(struct pci_dev *dev);
int (*deactivate)(struct pci_dev *dev);
};
整体演示
#include
#include
#include
#include
#include
#include
#include
#include
MODULE_LICENSE("GPL");
/* 对特定PCI设备进行描述的数据结构 */
struct hello_card {
unsigned int magic;
/* 使用链表保存所有同类的PCI设备 */
struct hello_card *next;
}
static int hello_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
switch(cmd) {
case hello_RDATA:
/* 从I/O端口读取4字节的数据 */
val = inl(card->iobae + 0x10);
/* 将读取的数据传输到用户空间 */
}
return 0;
}
static int hello_open(struct inode *inode, struct file *file)
{
/* 申请中断,注册中断处理程序 */
request_irq(card->irq, &hello_interrupt, SA_SHIRQ,
card_names[pci_id->driver_data], card)) {
/* 检查读写模式 */
if(file->f_mode & FMODE_READ) {
/* ... */
}
if(file->f_mode & FMODE_WRITE) {
/* ... */
}
/* 申请对设备的控制权 */
down(&card->open_sem);
while(card->open_mode & file->f_mode) {
if (file->f_flags & O_NONBLOCK) {
/* NONBLOCK模式,返回-EBUSY */
up(&card->open_sem);
return -EBUSY;
} else {
/* 等待调度,获得控制权 */
card->open_mode |= f_mode & (FMODE_READ | FMODE_WRITE);
up(&card->open_sem);
/* 设备打开计数增1 */
MOD_INC_USE_COUNT;
/* ... */
}
}
}
static void hello_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct hello_card *card = (struct hello_card *)dev_id;
u32 status;
spin_lock(&card->lock);
/* 识别中断 */
status = inl(card->iobase + GLOB_STA);
if(!(status & INT_MASK))
{
spin_unlock(&card->lock);
return; /* not for us */
}
/* 告诉设备已经收到中断 */
outl(status & INT_MASK, card->iobase + GLOB_STA);
spin_unlock(&card->lock);
/* 其它进一步的处理,如更新DMA缓冲区指针等 */
}
static int hello_release(struct inode *inode, struct file *file)
{
/* 释放对设备的控制权 */
card->open_mode &= (FMODE_READ | FMODE_WRITE);
/* 唤醒其它等待获取控制权的进程 */
wake_up(&card->open_wait);
up(&card->open_sem);
/* 释放中断 */
free_irq(card->irq, card);
/* 设备打开计数增1 */
MOD_DEC_USE_COUNT;
}
static struct file_operations hello_fops = {
owner: THIS_MODULE,
read: hello_read,
write: hello_write,
ioctl: hello_ioctl,
open: hello_open,
release: hello_release,
};
static int __init hello_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id)
{
struct hello_card *card;
/* 启动PCI设备 */
if (pci_enable_device(pci_dev))
return -EIO;
/* 设备DMA标识 */
if (pci_set_dma_mask(pci_dev, hello_DMA_MASK)) {
return -ENODEV;
}
/* 在内核空间中动态申请内存 */
if ((card = kmalloc(sizeof(struct hello_card), GFP_KERNEL)) == NULL) {
printk(KERN_ERR "pci_hello: out of memory\n");
return -ENOMEM;
}
memset(card, 0, sizeof(*card));
/* 读取PCI配置信息 */
card->iobase = pci_resource_start(pci_dev, 1);
card->pci_dev = pci_dev;
card->pci_id = pci_id->device;
card->irq = pci_dev->irq;
card->next = devs;
card->magic = hello_CARD_MAGIC;
/* 设置成总线主DMA模式 */
pci_set_master(pci_dev);
/* 申请I/O资源 */
request_region(card->iobase, 64, card_names[pci_id->driver_data]);
return 0;
}
static struct pci_device_id hello_pci_tbl [] __initdata = {
{PCI_VENDOR_ID_hello, PCI_DEVICE_ID_hello,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, hello},
{0,}
};
static struct pci_driver hello_pci_driver = {
name: hello_MODULE_NAME,
id_table: hello_pci_tbl,
probe: hello_probe,
remove: hello_remove,
};
static int __init hello_init_module (void)
{
//检查系统是否支持PCI总线
if (!pci_present())
return -ENODEV;
if (!pci_register_driver(&hello_pci_driver)) {
pci_unregister_driver(&hello_pci_driver);
return -ENODEV;
}
return 0;
}
static void __exit hello_cleanup_module (void)
{
pci_unregister_driver(&hello_pci_driver);
}
module_init(hello_init_module);
module_exit(hello_cleanup_module);