Linux的PCI子系统驱动框架简析

一、PCI子系统介绍

  1. PCI和PCIe在软件层面是可以兼容的,但是前者是并口的,并且速率较低
  2. PCI局部总线也是主从的,PCI设备均可当做主设备也可当做从设备,以下仅讨论从设备
  3. 树型结构+总线结构,CPU通过Host/PCI桥连接到0号PCI局部总线,0号PCI局部总线可以连接PCI设备,也可以连接PCI-PCI桥,连接到1号PCI局部总线
  4. PCI设备的三个地址空间:配置地址空间、内存地址空间、IO地址空间
  5. 配置地址空间的步骤:第一个时钟周期,FRAME拉低,AD总线上传输地址,C/BE发送命令,DELSEL表示从设备被选中;第二个时钟周期,AD总线上传输数据,C/BE发送字节有效指令;其中的IRDY和TRDY表示发起者和目标的准备信号,两者都拉低才表示有效,否则插入等待周期;最后一个周期,FRAME拉高,但是实际传输完成是在IRDY也拉高时。
  6. PCI设备中存储配置信息,初始化的时候通过配置读命令先读取配置信息,分配空间后,再使用配置写命令将分配地址的基址放入配置空间的基址寄存器中
  7. 两种配置方式:Type0选择总线上的设备,Type1将请求传递到另一个总线,Type1比Type0多了总线地址和设备地址,两者都存在功能地址和寄存器地址。
  8. 配置空间的一些寄存器,厂商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结构体中使用得到的虚拟地址进行读写操作。

四、后续补充。。。

posted @ 2025-08-06 16:11  gramming  阅读(286)  评论(0)    收藏  举报