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位)
方法二:
使用DDK提供的两个内核函数,HalGetBusData HalSetBusData,这2个函数微软已经不推荐使用,遗留下来只是为了兼容性。适用于NT模型的驱动。
方法三:
这个方法只能用在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配置空间中取出的有用信息。
方法四:
待续。。更新。。。
一种将系统外部设备连接起来的总线标准。如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
- PCI设备驱动
- PCI设备驱动开发
- pci设备驱动
- PCI设备驱动
- PCI设备驱动开发
- pci问号设备驱动
- PCI设备驱动知识点
- PCI设备驱动开发
- PCI设备驱动开发
- PCI设备驱动开发
- PCI设备驱动开发
- Linux PCI设备驱动
- PCI设备驱动开发
- PCI设备驱动
- PCI设备驱动知识点
- PCI设备驱动
- PCI设备驱动
- PCI设备驱动简介
- ios制作静态库
- oracle11g dataguard 完全手册
- SharePoint 2013 的硬件和软件要求
- 找不到BufferedImage这个Class的解决方法
- 避免c++程序在windows7或vista下关闭后出现程序兼容性助手
- PCI设备驱动
- 10gen工程师谈MongoDB组合索引的优化
- 使用 HTML5 WebSocket 构建实时 Web 应用
- LDA-math
- 如何用Service来启动背景音乐
- UIView 动画
- 惊呆理员档翻译的另自偷靶
- Android View 阴影
- 订残者社if器筒“腹