SylixOS读写PCIe设备配置空间
在上一节中,我们知道注册控制器驱动后,SylixOS会遍历所有总线上的设备信息,这是通过读取设备的配置空间信息实现的。SylixOS中配置空间的读写都是通过调用PCI_DRV_FUNCS0 或者PCI_DRV_FUNCS12 数据结构中的回调函数实现的。PCI_DRV_FUNCS12 这个是x86平台使用的,所以我们这里只关注 PCI_DRV_FUNCS0 这个数据结构。
typedef struct pci_drv_funcs0 { INT (*cfgRead)(INT iBus, INT iSlot, INT iFunc, INT iOft, INT iLen, PVOID pvRet); INT (*cfgWrite)(INT iBus, INT iSlot, INT iFunc, INT iOft, INT iLen, UINT32 uiData); INT (*vpdRead)(INT iBus, INT iSlot, INT iFunc, INT iPos, UINT8 *pucBuf, INT iLen); INT (*irqGet)(INT iBus, INT iSlot, INT iFunc, INT iMsiEn, INT iLine, INT iPin, PVOID pvIrq); INT (*cfgSpcl)(INT iBus, UINT32 uiMsg); } PCI_DRV_FUNCS0;
我们简单回顾下PCIe知识,按照PCIe标准,一个控制器下最多可以有256个总线,每个总线下最多有32个设备,每个设备最多有8个功能,每个功能都有一个配置空间,普通功能配置空间大小为256字节,复杂的可能需要4KB大小。配置空间可以进行单字节、双字节、4字节访问。
有了以上信息后我们以读函数接口为例看看读写接口如何实现。
static int pci_cfg_read (int bus, int dev, int func, int offset, int len, void *data) { return 0; }
- bus:总线号。
- dev:设备号。
- func:功能号。
- offset:寄存器偏移。
- len:读取的字节数,可以为1、2、4。
- data:数据缓冲区。
每个入参的意义很好理解,不同平台需要根据这几个入参转换成平台相关的配置空间访问方法。比如对于飞腾2000/4平台来说,配置空间是通过普通的MMIO访问来实现的,配置空间MMIO起始地址为0x40000000,控制器驱动需要根据上述入参计算出配置空间寄存器对应的MMIO访问地址,然后通过普通的内存访问方法读写数据。
配置空间写接口和读接口类似,只是最后一个参数是上层传下来的具体要写的数值。
static int pci_cfg_write (int bus, int dev, int func, int offset, int len, unsigned int data) { return 0; }
驱动同样需要根据入参转换成平台相关的配置空间写方法。
在实际的驱动中,一般定义一个全局变量,并使用上述的读写接口来初始化。
static PCI_DRV_FUNCS0 pci_driver_funcs = { .cfgRead = pci_cfg_read, .cfgWrite = pci_cfg_write, };
附源码
pci_host_driver_demo2.c源码:
#define __SYLIXOS_KERNEL #define __SYLIXOS_PCI_DRV #include <SylixOS.h> #include <module.h> #include <system/device/pci/pciBus.h> #include <system/device/pci/pciMsi.h> #include <system/device/pci/pciLib.h> static PCI_CTRL_CB pci_host; static int pci_cfg_read (int bus, int dev, int func, int offset, int len, void *data) { return 0; } static int pci_cfg_write (int bus, int dev, int func, int offset, int len, unsigned int data) { return 0; } static PCI_DRV_FUNCS0 pci_driver_funcs = { .cfgRead = pci_cfg_read, .cfgWrite = pci_cfg_write, }; int pci_host_probe (void) { API_PciCtrlCreate(&pci_host); return 0; } int module_init (void) { pci_host_probe(); return 0; } void module_exit (void) { }
评论