PCI(Peripheral Component Interconnect)总线标准是一种将系统外部设备连接起来的总线标准,速度可以达到133MB/s,它是PC中最重要的总线,其他总路线如ISA总线,USB总线等,都挂载在PCI总线上(通过桥接电路)。由Intel推出的一种局部总线,为32位数据地址总线,可以扩展为64位,支持突发读写,及多组外围设备。
在PCI系统中,Host/PCI称为北桥,连接主处理器总线到基础PCI局部总线;PCI-ISA桥称为南桥,连接基础PCI总线到ISA总线。其中南桥通常还含有中断控制器,IDE控制器,USB控制器和DMA控制器等设备。
图示 P412
PCI有三个相互独立的物理地址空间:设备存储器地址空间,I/O地址空间和配置空间。由于PCI支持设备即插即用,所以PCI设备不占用固定的内存地址空间或I/O地址空间,而是可以由操作系统决定其映射的基址。
PCI总线规范定义的配置Hha总长度为256个字节,配置信息按一定的顺序和大小依次存放。根据读取PCI配置空间,可以得到PCI设备的所有资源。[1]中讲述了多种读取PCI配置空间的方法,包括通过最基本的I/O端口操作进行读取,通过DDK提供的函数HalGetBusData,HalSetBusData在NT式驱动中进行读取,在WDM驱动中的读取方法,等。
一般程序所看到的内存指针都是虚拟内存,如果想操作物理内存,必须使用DDK提供的内核函数WRITE_REGISTER_XX,READ_REGISTER_XX系列函数。
MmAllocateContiguousMemory分配连续的物理地址,MmGetPhysicalAddress得到连续的物理内存地址。
代码
#pragma
PAGEDCODE
NTSTATUS InitMyPCI(IN PDEVICE_EXTENSION pdx,IN PCM_PARTIAL_RESOURCE_LIST list)
{
PDEVICE_OBJECT fdo = pdx -> fdo;
ULONG vector;
KIRQL irql;
KINTERRUPT_MODE mode;
KAFFINITY affinity;
BOOLEAN irqshare;
BOOLEAN gotinterrupt = FALSE;
PHYSICAL_ADDRESS portbase;
BOOLEAN gotport = FALSE;
PCM_PARTIAL_RESOURCE_DESCRIPTOR resource = & list -> PartialDescriptors[ 0 ];
ULONG nres = list -> Count;
BOOLEAN IsMem0 = TRUE;
for (ULONG i = 0 ; i < nres; ++ i, ++ resource)
{ // for each resource
switch (resource -> Type)
{ // switch on resource type
case CmResourceTypePort:
portbase = resource -> u.Port.Start;
pdx -> nports = resource -> u.Port.Length;
pdx -> mappedport = (resource -> Flags & CM_RESOURCE_PORT_IO) == 0 ;
gotport = TRUE;
break ;
case CmResourceTypeMemory:
if (IsMem0)
{
pdx -> MemBar0 = (PUCHAR)MmMapIoSpace(resource -> u.Memory.Start,
resource -> u.Memory.Length,
MmNonCached);
pdx -> nMem0 = resource -> u.Memory.Length;
IsMem0 = FALSE;
} else
{
pdx -> MemBar1 = (PUCHAR)MmMapIoSpace(resource -> u.Memory.Start,
resource -> u.Memory.Length,
MmNonCached);
pdx -> nMem1 = resource -> u.Memory.Length;
}
break ;
case CmResourceTypeInterrupt:
irql = (KIRQL) resource -> u.Interrupt.Level;
vector = resource -> u.Interrupt.Vector;
affinity = resource -> u.Interrupt.Affinity;
mode = (resource -> Flags == CM_RESOURCE_INTERRUPT_LATCHED)
? Latched : LevelSensitive;
irqshare = resource -> ShareDisposition == CmResourceShareShared;
gotinterrupt = TRUE;
break ;
default :
KdPrint(( " Unexpected I/O resource type %d\n " , resource -> Type));
break ;
} // switch on resource type
} // for each resource
if ( ! (TRUE && gotport && gotinterrupt ))
{
KdPrint(( " Didn't get expected I/O resources\n " ));
return STATUS_DEVICE_CONFIGURATION_ERROR;
}
if (pdx -> mappedport)
{ // map port address for RISC platform
pdx -> portbase = (PUCHAR) MmMapIoSpace(portbase, pdx -> nports, MmNonCached);
if ( ! pdx -> mappedport)
{
KdPrint(( " Unable to map port range %I64X, length %X\n " , portbase, pdx -> nports));
return STATUS_INSUFFICIENT_RESOURCES;
}
} // map port address for RISC platform
else
pdx -> portbase = (PUCHAR) portbase.QuadPart;
NTSTATUS status = IoConnectInterrupt( & pdx -> InterruptObject, (PKSERVICE_ROUTINE) OnInterrupt,
(PVOID) pdx, NULL, vector, irql, irql, LevelSensitive, TRUE, affinity, FALSE);
if ( ! NT_SUCCESS(status))
{
KdPrint(( " IoConnectInterrupt failed - %X\n " , status));
if (pdx -> portbase && pdx -> mappedport)
MmUnmapIoSpace(pdx -> portbase, pdx -> nports);
pdx -> portbase = NULL;
return status;
}
#define IMAGE_LENGTH (640*480)
// 申请一段连续物理地址来读取图像
PHYSICAL_ADDRESS maxAddress;
maxAddress.u.LowPart = 0xFFFFFFFF ;
maxAddress.u.HighPart = 0 ;
pdx -> MemForImage = MmAllocateContiguousMemory(IMAGE_LENGTH,maxAddress);
PHYSICAL_ADDRESS pycialAddressForImage = MmGetPhysicalAddress(pdx -> MemForImage);
WRITE_REGISTER_BUFFER_UCHAR((PUCHAR)pdx -> MemBar0 + 0x10000 ,
(PUCHAR) & pycialAddressForImage.u.LowPart, 4 );
return STATUS_SUCCESS;
}
NTSTATUS InitMyPCI(IN PDEVICE_EXTENSION pdx,IN PCM_PARTIAL_RESOURCE_LIST list)
{
PDEVICE_OBJECT fdo = pdx -> fdo;
ULONG vector;
KIRQL irql;
KINTERRUPT_MODE mode;
KAFFINITY affinity;
BOOLEAN irqshare;
BOOLEAN gotinterrupt = FALSE;
PHYSICAL_ADDRESS portbase;
BOOLEAN gotport = FALSE;
PCM_PARTIAL_RESOURCE_DESCRIPTOR resource = & list -> PartialDescriptors[ 0 ];
ULONG nres = list -> Count;
BOOLEAN IsMem0 = TRUE;
for (ULONG i = 0 ; i < nres; ++ i, ++ resource)
{ // for each resource
switch (resource -> Type)
{ // switch on resource type
case CmResourceTypePort:
portbase = resource -> u.Port.Start;
pdx -> nports = resource -> u.Port.Length;
pdx -> mappedport = (resource -> Flags & CM_RESOURCE_PORT_IO) == 0 ;
gotport = TRUE;
break ;
case CmResourceTypeMemory:
if (IsMem0)
{
pdx -> MemBar0 = (PUCHAR)MmMapIoSpace(resource -> u.Memory.Start,
resource -> u.Memory.Length,
MmNonCached);
pdx -> nMem0 = resource -> u.Memory.Length;
IsMem0 = FALSE;
} else
{
pdx -> MemBar1 = (PUCHAR)MmMapIoSpace(resource -> u.Memory.Start,
resource -> u.Memory.Length,
MmNonCached);
pdx -> nMem1 = resource -> u.Memory.Length;
}
break ;
case CmResourceTypeInterrupt:
irql = (KIRQL) resource -> u.Interrupt.Level;
vector = resource -> u.Interrupt.Vector;
affinity = resource -> u.Interrupt.Affinity;
mode = (resource -> Flags == CM_RESOURCE_INTERRUPT_LATCHED)
? Latched : LevelSensitive;
irqshare = resource -> ShareDisposition == CmResourceShareShared;
gotinterrupt = TRUE;
break ;
default :
KdPrint(( " Unexpected I/O resource type %d\n " , resource -> Type));
break ;
} // switch on resource type
} // for each resource
if ( ! (TRUE && gotport && gotinterrupt ))
{
KdPrint(( " Didn't get expected I/O resources\n " ));
return STATUS_DEVICE_CONFIGURATION_ERROR;
}
if (pdx -> mappedport)
{ // map port address for RISC platform
pdx -> portbase = (PUCHAR) MmMapIoSpace(portbase, pdx -> nports, MmNonCached);
if ( ! pdx -> mappedport)
{
KdPrint(( " Unable to map port range %I64X, length %X\n " , portbase, pdx -> nports));
return STATUS_INSUFFICIENT_RESOURCES;
}
} // map port address for RISC platform
else
pdx -> portbase = (PUCHAR) portbase.QuadPart;
NTSTATUS status = IoConnectInterrupt( & pdx -> InterruptObject, (PKSERVICE_ROUTINE) OnInterrupt,
(PVOID) pdx, NULL, vector, irql, irql, LevelSensitive, TRUE, affinity, FALSE);
if ( ! NT_SUCCESS(status))
{
KdPrint(( " IoConnectInterrupt failed - %X\n " , status));
if (pdx -> portbase && pdx -> mappedport)
MmUnmapIoSpace(pdx -> portbase, pdx -> nports);
pdx -> portbase = NULL;
return status;
}
#define IMAGE_LENGTH (640*480)
// 申请一段连续物理地址来读取图像
PHYSICAL_ADDRESS maxAddress;
maxAddress.u.LowPart = 0xFFFFFFFF ;
maxAddress.u.HighPart = 0 ;
pdx -> MemForImage = MmAllocateContiguousMemory(IMAGE_LENGTH,maxAddress);
PHYSICAL_ADDRESS pycialAddressForImage = MmGetPhysicalAddress(pdx -> MemForImage);
WRITE_REGISTER_BUFFER_UCHAR((PUCHAR)pdx -> MemBar0 + 0x10000 ,
(PUCHAR) & pycialAddressForImage.u.LowPart, 4 );
return STATUS_SUCCESS;
}
示例代码 P428
参考
[1] Windows 驱动开发技术详解,张帆