PCI相关知识总结(1)

57 篇文章 4 订阅

1、pci概述
PCI(Peripheral Component Interconnect)是一种由英特尔(Intel)公司1991年推出的用于定义局部总线的标准。每个PCI设备有许多地址配置的寄存器,初始化时要通过这些寄存器来配置该设备的总线地址,一旦完成配置以后,CPU就可以访问该设备的各项资源了。PCI标准规定每个设备的配置寄存器组最多可以有256个连续的字节空间,开头64个字节叫头部,分为0型(PCI设备)和1型(PCI桥)头部,头部开头16个字节是设备的类型、型号和厂商等。这些头部寄存器除了地址配置的作用,还能使CPU能够探测到相应设备的存在,这样就不需要用户告诉系统都有哪些设备了,而是改由CPU通过一个号称枚举的过程自动扫描探测所有挂接在PCI总线上的设备。

设备的配置寄存器组采用相同的地址,由所在总线的PCI桥在访问时附加上其他条件区分,对于I386处理器,有两个32位寄存器,0XCF8为地址寄存器,0XCFC为数据寄存器。地址寄存器写入的内容包括总线号,设备号,功能号。逻辑地址(XX:YY.Z),XX表示PCI总线号,最多256个总线。YY表示PCI设备号,最多32个设备。Z表示PCI设备功能号,最多8个功能。

参考链接:https://blog.csdn.net/zz2862625432/article/details/104926925
2、pci_dev结构体
每种类的PCI设备都可以用结构类型pci_dev来描述。更为准确地说,应该是每一个PCI功能,即PCI逻辑设备都唯一地对应有一个pci_dev设备描述符。该数据结构的部分定义如下(include/linux/pci.h):

struct pci_dev {
/* 总线设备链表元素bus_list:每一个pci_dev结构除了链接到全局设备链表中外,还会通过这个成员连接到
其所属PCI总线的设备链表中。每一条PCI总线都维护一条它自己的设备链表视图,以便描述所有连接在该
PCI总线上的设备,其表头由PCI总线的pci_bus结构中的 devices成员所描述t*/
struct list_head bus_list;
/* 总线指针bus:指向这个PCI设备所在的PCI总线的pci_bus结构。因此,对于桥设备而言,bus指针将指向
桥设备的主总线(primary bus),也即指向桥设备所在的PCI总线*/
struct pci_bus *bus;
/* 指针subordinate:指向这个PCI设备所桥接的下级总线。这个指针成员仅对桥设备才有意义,而对于一般
的非桥PCI设备而言,该指针成员总是为NULL*/
struct pci_bus *subordinate;
/* 无类型指针sysdata:指向一片特定于系统的扩展数据*/
void *sysdata;
/* 指针procent:指向该PCI设备在/proc文件系统中对应的目录项*/
struct proc_dir_entry *procent;
/* devfn:这个PCI设备的设备功能号,也成为PCI逻辑设备号(0-255)。其中bit[7:3]是物理设备号(取值
范围0-31),bit[2:0]是功能号(取值范围0-7)。 */
unsigned int devfn;
/* vendor:这是一个16无符号整数,表示PCI设备的厂商ID*/
unsigned short vendor;
/*device:这是一个16无符号整数,表示PCI设备的设备ID */
unsigned short device;
/* subsystem_vendor:这是一个16无符号整数,表示PCI设备的子系统厂商ID*/
unsigned short subsystem_vendor;
/* subsystem_device:这是一个16无符号整数,表示PCI设备的子系统设备ID。*/
unsigned short subsystem_device;
/* class:32位的无符号整数,表示该PCI设备的类别,其中,bit[7:0]为编程接口,bit[15:8]为子类
别代码,bit [23:16]为基类别代码,bit[31:24]无意义。显然,class成员的低3字节刚好对应与PCI配
置空间中的类代码*/
unsigned int class;
/* hdr_type:8位符号整数,表示PCI配置空间头部的类型。其中,bit[7]=1表示这是一个多功能设备,
bit[7]=0表示这是一个单功能设备。Bit[6:0]则表示PCI配置空间头部的布局类型,值00h表示这是一
个一般PCI设备的配置空间头部,值01h表示这是一个PCI-to-PCI桥的配置空间头部,值02h表示CardBus桥
的配置空间头部*/
u8 hdr_type;
/* rom_base_reg:8位无符号整数,表示PCI配置空间中的ROM基地址寄存器在PCI配置空间中的位置。
ROM基地址寄存器在不同类型的PCI配置空间头部的位置是不一样的,对于type 0的配置空间布局,ROM基
地址寄存器的起始位置是30h,而对于PCI-to-PCI桥所用的type 1配置空间布局,ROM基地址寄存器的起始
位置是38h*/
u8 rom_base_reg;
/* 指针driver:指向这个PCI设备所对应的驱动程序定义的pci_driver结构。每一个pci设备驱动程序都必须定
义它自己的pci_driver结构来描述它自己。*/
struct pci_driver *driver;
/*dma_mask:用于DMA的总线地址掩码,一般来说,这个成员的值是0xffffffff。数据类型dma_addr_t定义在
include/asm/types.h中,在x86平台上,dma_addr_t类型就是u32类型*/
u64 dma_mask;
/* 当前操作状态 */
pci_power_t  current_state;
/* 通用的设备接口*/
 struct device dev;
/* 无符号的整数irq:表示这个PCI设备通过哪根IRQ输入线产生中断,一般为0-15之间的某个值 */
unsigned int irq;
/*表示该设备可能用到的资源,包括:I/O断口区域、设备内存地址区域以及扩展ROM地址区域。*/
struct resource resource[DEVICE_COUNT_RESOURCE];
/* 配置空间的大小 */
int cfg_size;
/* 透明 PCI 桥 */
unsigned int transparent:1;
/* 多功能设备*/
unsigned int multifunction:1;
/* 设备是主设备*/
unsigned int is_busmaster:1;
/* 设备不使用msi*/
unsigned int no_msi:1;
/* 配置空间访问形式用块的形式 */
unsigned int block_ucfg_access:1;
/* 在挂起时保存配置空间*/
u32 saved_config_space[16];
/* sysfs ROM入口的属性描述*/
struct bin_attribute *rom_attr;
/* 能显示rom 属性*/
int rom_attr_enabled;
/* 资源的sysfs文件*/
struct bin_attribute *res_attr[DEVICE_COUNT_RESOURCE];
};

重点关注:
/* 总线指针bus:指向这个PCI设备所在的PCI总线的pci_bus结构。因此,对于桥设备而言,bus指针将指向
桥设备的主总线(primary bus),也即指向桥设备所在的PCI总线*/
struct pci_bus *bus;

/* vendor:这是一个16无符号整数,表示PCI设备的厂商ID*/
unsigned short vendor;

/*device:这是一个16无符号整数,表示PCI设备的设备ID */
unsigned short device;

参考链接:https://blog.csdn.net/u011471873/article/details/83863567
3、pci_driver结构体
描述一个pci设备,每个pci驱动必须创建一个pci_driver实例。

 1 /* 
 2     描述一个pci设备,每个pci驱动必须创建一个pci_driver实例 
 3 */
 4	   struct pci_driver {
 5     struct list_head node;
 6     /* 驱动程序名,内核中所有pci驱动程序名都是唯一的 */
 7     const char *name;
 8     /* pci设备配置信息数组 */
 9     const struct pci_device_id *id_table;    /* must be non-NULL for probe to be called */
10     /* 设备插入内核时调用 */
11     int  (*probe)  (struct pci_dev *dev, const struct pci_device_id *id);    /* New device inserted */
12     /* 设备从内核移除时调用 */
13     void (*remove) (struct pci_dev *dev);    /* Device removed (NULL if not a hot-plug capable driver) */
14     /* 设备被挂起时调用 */
15     int  (*suspend) (struct pci_dev *dev, pm_message_t state);    /* Device suspended */
16     int  (*suspend_late) (struct pci_dev *dev, pm_message_t state);
17     int  (*resume_early) (struct pci_dev *dev);
18     /* 设备被恢复时调用 */
19     int  (*resume) (struct pci_dev *dev);                    /* Device woken up */
20     /* 设备关闭时调用 */
21     void (*shutdown) (struct pci_dev *dev);
22     int (*sriov_configure) (struct pci_dev *dev, int num_vfs); /* PF pdev */
23     /* 错误处理 */
24     const struct pci_error_handlers *err_handler;
25     struct device_driver    driver;
26     struct pci_dynids dynids;
27 };

重点关注:
/* 驱动程序名,内核中所有pci驱动程序名都是唯一的 */
const char *name;

/* pci设备配置信息数组 */
const struct pci_device_id id_table; / must be non-NULL for probe to be called */
该结构可以有很多项,每一项表明使用该结构的驱动支持的设备。注意:需要以一个空的项结尾,也就是:{0,}

/* 设备插入内核时调用 */
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) */

参考链接:https://www.cnblogs.com/wanpengcoder/p/7526032.html
4、pci_device_id结构体
该结构体用于用户设置pci设备信息。

/* pci设备配置寄存器 */
 2 struct pci_device_id {
 3     /* 厂商id,设备id */
 4     __u32 vendor, device;        /* Vendor and device ID or PCI_ANY_ID*/
 5     /* 子系统厂商id,子系统设备id */
 6     __u32 subvendor, subdevice;    /* Subsystem ID's or PCI_ANY_ID */
 7     /* 类,类掩码,使驱动程序可以指定支持某一种pci设备 */
 8     __u32 class, class_mask;    /* (class,subclass,prog-if) triplet */
 9     /* 保存于设备相关的私有信息 */
10     kernel_ulong_t driver_data;    /* Data private to the driver */
11 };

参考链接:https://www.cnblogs.com/wanpengcoder/p/7526005.html

5、
整体流程:
1、 pci_set_drvdata(pdev, rm);
设置驱动私有数据
2、 pci_enable_device(pdev)
同时初始化memory和IO类型的BAR
3、 pci_request_regions(pdev, DEVICE_NAME)
对PCI区进行标记 ,标记该区域已经分配出去
4、 pci_set_dma_mask(pdev, DMA_BIT_MASK(48));
辅助函数用于检查总线是否可以接收给定大小的总线地址(mask),如果可以,则通知总线层给定的外围设备将使用该大小的总线地址。
5、pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(48));
设置 DMA 地址掩码位,因为 PCI 设备时有寻址限制的,所以需要标识那些位的地址可以使用
6、 pci_set_master(pdev);
设定设备工作在总线主设备模式

7、
pci_iomap(pdev, bar,mmio_length)

将获得内存地址转换成虚拟地址

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值