一、PCI子系统介绍
- PCI和PCIe在软件层面是可以兼容的,但是前者是并口的,并且速率较低
- PCI局部总线也是主从的,PCI设备均可当做主设备也可当做从设备,以下仅讨论从设备
- 树型结构+总线结构,CPU通过Host/PCI桥连接到0号PCI局部总线,0号PCI局部总线可以连接PCI设备,也可以连接PCI-PCI桥,连接到1号PCI局部总线
- PCI设备的三个地址空间:配置地址空间、内存地址空间、IO地址空间
- 配置地址空间的步骤:第一个时钟周期,FRAME拉低,AD总线上传输地址,C/BE发送命令,DELSEL表示从设备被选中;第二个时钟周期,AD总线上传输数据,C/BE发送字节有效指令;其中的IRDY和TRDY表示发起者和目标的准备信号,两者都拉低才表示有效,否则插入等待周期;最后一个周期,FRAME拉高,但是实际传输完成是在IRDY也拉高时。
- PCI设备中存储配置信息,初始化的时候通过配置读命令先读取配置信息,分配空间后,再使用配置写命令将分配地址的基址放入配置空间的基址寄存器中
- 两种配置方式:Type0选择总线上的设备,Type1将请求传递到另一个总线,Type1比Type0多了总线地址和设备地址,两者都存在功能地址和寄存器地址。
- 配置空间的一些寄存器,厂商ID、设备ID、外设类型、基址寄存器(配置空间有6个基址寄存器,每个都表示一个bar)
二、基本结构体介绍
// PCI设备
struct pci_dev{
unsigned short vendor; // 对应着配置空间的寄存器
unsigned short device;
unsigned short subsystem_vendor;
unsigned short subsystem_device;
unsigned int class;
...
};
// 驱动支持的设备列表
struct pci_device_id{
__u32 vendor, device;
__u32 subvendor, subdevice;
__u32 class, class_mask;
kernel_ulong_t driver_data;
};
// PCI驱动
struct pci_driver{
const char *name; // 名字
int (*probe)(struct pci_dev* dev, const struct pci_device_id *id);
void (*remove)(struct pci_dev* dev);
...
struct device_driver driver; // 父对象
};
三、驱动程序的流程
// PCI设备驱动
1. 使用module_pci_driver注册一个pci_driver
2. PCI总线和platform总线类似,id_table属性匹配时,调用probe函数,id_table中存储了设备信息
3. probe函数中,pci_enabale_deivce使能PCI设备、pci_resource_xx得到资源的起始地址、结束地址、标志、长度等信息,pci_request_regions申请内存资源和IO资源,ioremap/ioport_map映射地址空间到虚拟地址,pci_set_drvdata设置为driver_data,后续可以按照字符设备的注册流程,形成一个字符设备节点
4. remove函数中,释放资源,并最后pci_release_regions释放资源、pci_disable_device取消使能PCI设备
5. file_operations结构体中使用得到的虚拟地址进行读写操作。
四、后续补充。。。