PCI设备驱动

来源:互联网 发布:c语言中怎么开多次根号 编辑:程序博客网 时间:2024/05/01 07:38
PCI总线
一种将系统外部设备连接起来的总线标准。如ISA、USB总线都挂载在PCI总线上。
开发PCI设备驱动,需要获取PCI配置空间的各个数据。
基础PCI局部总线与主处理器相连接的Host/PCI称为北桥
基础PCI总线与中断控制器、IDE控制器、USB控制器、DMA控制器和ISA总线的 称为南桥。


PCI配置空间:
PCI三个相互独立的物理地址空间:设备存储器地址空间、I/O地址空间和配置空间。
系统加电时,BIOS检测PCI总线,确定所有连接的PCI上的设备以及它们的配置要求,进行系统配置。所有的PCI设备必须有配置空间,从而能够实现参数的自动配置,实现真正的即插即用。


配置空间总共256字节:
前64字节:配置头,主要用来识别设备,定义主机访问PCI的方式(IO访问或者存储器访问、中断信息)。
剩余192字节:本地配置空间:


访问PCI配置空间:
方法一:通过IO端口直接读取
访问2个重要的寄存器,CONFIG_ADDRESS寄存器和CONFIG_DATA寄存器。在PC中分别对应着端口CF8H和CFCH,并且是32位端口(ULONG)
CONFIG_ADDRESS结构如下:对应着3个信息进行定位:BusNumber、DeviceNumber、FunctionNumber。
另外配置空间为564个字节,被分解为64个4字节的寄存器,从0-63编号。


例如访问:BusNumber=0,DeviceNumber=1,FunctionNumber=2的PCI配置空间中的status和command。
首先查看status和command在pci的配置空间位于第1号寄存器,填写CONFIG_ADDRESS。然后查看CONFIG_DATA的内容(status在高16位,comand在低16位)


代码:

VOID DisPaly_PCI_ConfigSpace(ULONG uBus,ULONG uDev,ULONG uFunc){ULONG uPciConPort = 0xCF8;ULONG uPcIDataPort = 0xCFC;ULONG uAddr = 0;ULONG uData = 0;PCI_COMMON_CONFIG pciConfig;PCI_SLOT_NUMBER pciSlotNum;//Species the logical slot number of the device being configuredpciSlotNum.u.AsULONG = 0;//设置设备号pciSlotNum.u.bits.DeviceNumber = uDev;//设置功能号pciSlotNum.u.bits.FunctionNumber = uFunc;//得到物理地址uAddr = 0x80000000 | (uBus << 16 ) | (pciSlotNum.u.AsULONG<<8);/*256字节的PCI配置空间*/for (int i=0;i<0x100;i+=4){WRITE_PORT_ULONG((PULONG)uPciConPort,uAddr | i);uData = READ_PORT_ULONG((PULONG)uPcIDataPort);memcpy(((PUCHAR)&pciConfig)+i,&uData,4);}KdPrint(("bus:%d\tdev:%d\tfunc:%d\n",uBus,uDev,uFunc));KdPrint(("VendorID:%x\n",pciConfig.VendorID));KdPrint(("DeviceID:%x\n",pciConfig.DeviceID));KdPrint(("Command:%x\n",pciConfig.Command));KdPrint(("Status:%x\n",pciConfig.Status));KdPrint(("RevisionID:%x\n",pciConfig.RevisionID));KdPrint(("ProgIf:%x\n",pciConfig.ProgIf));KdPrint(("SubClass:%x\n",pciConfig.SubClass));KdPrint(("BaseClass:%x\n",pciConfig.BaseClass));KdPrint(("CacheLineSize:%x\n",pciConfig.CacheLineSize));KdPrint(("LatencyTimer:%x\n",pciConfig.LatencyTimer));KdPrint(("HeaderType:%x\n",pciConfig.HeaderType));KdPrint(("BIST:%x\n",pciConfig.BIST));for (int i=0;i<6;i++){KdPrint(("BaseAddresses[%d]:0X%08X\n",i,pciConfig.u.type0.BaseAddresses[i]));}KdPrint(("InterruptLine:%d\n",pciConfig.u.type0.InterruptLine));KdPrint(("InterruptPin:%d\n",pciConfig.u.type0.InterruptPin));}



方法二:
使用DDK提供的两个内核函数,HalGetBusData HalSetBusData,这2个函数微软已经不推荐使用,遗留下来只是为了兼容性。适用于NT模型的驱动。

#define  PCI_BUS_MAX0xFF#define  PCI_DEVICE_MAX0x1F#define  PCI_FUNC_MAX0x7VOID EnumPciConfig(){ULONG uBus = 0;ULONG uDev = 0;ULONG uFunc = 0;PCI_COMMON_CONFIG pciConfig;PCI_SLOT_NUMBER pciSlotNum;RtlZeroMemory(&pciConfig,sizeof(PCI_COMMON_CONFIG));RtlZeroMemory(&pciSlotNum,sizeof(PCI_SLOT_NUMBER));KdPrint(("EnumPciConfig enter...\n"));KdPrint(("Bus\tDevice\tFunc\tVendor\tDevice\tBaseCls\tSubCls\tIRQ\tPIN\n"));//枚举总线号for (uBus=0;uBus<PCI_BUS_MAX;uBus++){//枚举设备号for (uDev=0;uDev<PCI_DEVICE_MAX;uDev++){//枚举功能号for (uFunc=0;uFunc<PCI_FUNC_MAX;uFunc++){pciSlotNum.u.AsULONG = 0;pciSlotNum.u.bits.DeviceNumber = uDev;//设备号pciSlotNum.u.bits.FunctionNumber = uFunc;//功能号RtlZeroMemory(&pciConfig,sizeof(PCI_COMMON_CONFIG));ULONG uSize = HalGetBusData(PCIConfiguration,//总线类型uBus,//总线号pciSlotNum.u.AsULONG,&pciConfig,PCI_COMMON_HDR_LENGTH);if (uSize == PCI_COMMON_HDR_LENGTH){KdPrint(("%02X\t%02X\t%x\t%x\t%x\t%02X\t%02X\t%d\t%d\n",uBus,uDev,uFunc,pciConfig.VendorID,pciConfig.DeviceID,pciConfig.BaseClass,pciConfig.SubClass,pciConfig.u.type0.InterruptLine,pciConfig.u.type0.InterruptPin));}}}}KdPrint(("EnumPciConfig leave...\n"));}




方法三:
这个方法只能用在WDM模型的驱动程序中,缺点是不能完整的获取256字节的配置空间,WDM驱动会不同总线上的设备提供一个PDO,当驱动程序的FDO挂载到PDO上的时候,将IRP_MN_START_DEVICE传递给底层PDO去处理。PCI总线上的PDO会得到PCI配置空间,并从中得到中断号、设备物理内存、io端口信息等等。
IRP_MN_START_DEVICE处理完后,驱动会将结果保存到IRP的堆栈中,从堆栈中可以取出CM_FULL_RESOURCE_DESCRIPTOR,再取出CM_PARTIAL_RESOURCE_LIST  ,再取出CM_PARTIAL_RESOURCE_DESCRIPTOR  此结构就是PDO从PCI配置空间中取出的有用信息。


代码暂时略。。。


方法四:

在wdm模型的驱动中,创建IRP_MN_READ_CONFIG和IRP_MN_WRITE_CONFIG来获得完整的256字节的PCI的配置空间。此驱动必须是一个真实的设备,不能为虚拟设备。

代码暂时略。。。



待续。。更新。。。

0 0
原创粉丝点击