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,
pdx->nMem1 = resource->u.Memory.Length;
}
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;
default:
KdPrint(("Unexpected I/O resource type %d\n", resource->Type));
} // 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 驅動開發技術詳解,張帆