上篇中,我们探讨了PCIe设备是如何在Bios中被初始化的,Bios中各种准备工作做好后,就开始引导装载操作系统,系统的控制权移交给操作系统,操作系统中也要对系统中各PCI设备进行扫描以及初始化(和上文中提到的Bios对PCI设备的枚举和配置一样),事实上,早期的操作系统也是通过Bios调用的方式来获取系统PCI设备信息的,只不过现在变成亲自动手了。总体来说,操作系统对PCI的枚举以及初始化的过程和Bios的基本一致,在此我们就不在赘述了。我们把探讨的重点放在PCIe设备在驱动中如何启动,如何初始化等内容上。
我们知道所有驱动程序的入口点是DriverEntry函数,针对本次的PCIe设备,我们的DriverEntry函数设置如下:
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObj, PUNICODE_STRING RegistryPath)
{
DebugPrint("DriverEntry() Start\n");
/* 注册派遣函数 */
DriverObj->DriverUnload = DemoPciUnload;
DriverObj->DriverExtension->AddDevice = DemoPciAddDevice;
DriverObj->DriverStartIo = DemoPciStartIo;
DriverObj->MajorFunction[IRP_MJ_PNP] = DemoPciPnp;
DriverObj->MajorFunction[IRP_MJ_POWER] = DemoPciPower;
DriverObj->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = DemoPciSystemControl;
DriverObj->MajorFunction[IRP_MJ_CREATE] = DemoPciCreateClose;
DriverObj->MajorFunction[IRP_MJ_CLOSE] = DemoPciCreateClose;
DriverObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DemoPciDeviceControl;
DebugPrint("DriverEntry() End\n");
return STATUS_SUCCESS;
}
当PnP管理器检测到我们的PCIe设备时,它首先通过查阅相关的注册表来了解哪些驱动程序将管理我们的这个设备,并将驱动加载进来,然后调用驱动中的已经注册好的AddDevice函数(上述代码中注册的DemoPciAddDevice函数)。该函数将为我们的PCIe设备创建设备对象,并将该设备对象挂载在相应的设备栈上。
NTSTATUS DemoPciAddDevice(
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject
)
{
UCHAR i;
UNICODE_STRING devname;
ULONG xsize;
NTSTATUS status = STATUS_SUCCESS;
PTSTDPCI_DEVICE_EXT pDevExt = NULL;
PDEVICE_OBJECT functionalDeviceObject = NULL;
DebugPrint("DemoPciAddDevice() Start\n");
if( (DriverObject == NULL) || (PhysicalDeviceObject == NULL) )
return (STATUS_NO_SUCH_DEVICE);
/*设备名*/
RtlInitUnicodeString(&devname,TSTDPCI_DEVICE_NAME);
/*我们定义的扩展设备对象的大小*/
xsize=( (sizeof(DEMOPCI_DEVICE_EXT) + 7) & (~7) );
/*创建设备对象*/
status = IoCreateDevice( DriverObject,
xsize,
&devname,
FILE_DEVICE_DEMOPCI,
0, //FILE_DEVICE_SECURE_OPEN,
FALSE,
&functionalDeviceObject
);
if( !NT_SUCCESS(status) )
{
return (status);
}
if(functionalDeviceObject == NULL)
{
return (STATUS_UNSUCCESSFUL);
}
__try
{
/* 获取扩展设备对象 */
pDevExt = (PDEMOPCI_DEVICE_EXT)(functionalDeviceObject->DeviceExtension);
if(pDevExt != NULL)
{
/* 扩展设备对象初始化 */
pDevExt->FunctionalDeviceObject = functionalDeviceObject;
pDevExt->PhysicalDeviceObject = PhysicalDeviceObject;
pDevExt->OutstandingIO = 0;
pDevExt->Started = FALSE;
pDevExt->Removed = FALSE;
pDevExt->Removing = FALSE;
pDevExt->State = STATE_NEVER_STARTED;
/*内核事件初始化*/
KeInitializeEvent(&pDevExt->RemoveEvent, NotificationEvent, FALSE);
for(i=0; i<PCI_TYPE0_ADDRESSES; i++)
{
pDevExt->base[i].WhichMapped = TYPE_NONE;
}
RtlZeroMemory(&(pDevExt->CmnBuffSysPhyAddr), sizeof(pDevExt->CmnBuffSysPhyAddr));
pDevExt->CmnBuffSize = 0;
pDevExt->CmnBuffSysVirAddr = NULL;
pDevExt->CmnBuffMdl = NULL;
pDevExt->CmnBuffUserAddr = NULL;
pDevExt->pDmaIrp = NULL;
/*注册接口*/
status = IoRegisterDeviceInterface(PhysicalDeviceObject, (LPGUID)&DEMOPCI_GUID, NULL, &pDevExt->ifname);
}
else
{
status=STATUS_UNSUCCESSFUL;
}
if ( !NT_SUCCESS(status) )
{
/* 删除FDO */
if(pDevExt->FunctionalDeviceObject != NULL)
IoDeleteDevice(pDevExt->FunctionalDeviceObject);
__leave;
}
// Ask the I/O Manager to use describe user read/write buffers using MDLs
functionalDeviceObject->Flags |= DO_DIRECT_IO;
/*将该设备对象挂载在设备栈上*/
pDevExt->LowerDeviceObject = IoAttachDeviceToDeviceStack(
functionalDeviceObject,
PhysicalDeviceObject);
if(pDevExt->LowerDeviceObject == NULL)
{
status=STATUS_NO_SUCH_DEVICE;
}
functionalDeviceObject->AlignmentRequirement = FILE_QUAD_ALIGNMENT; //8
functionalDeviceObject->Flags &= (~DO_DEVICE_INITIALIZING);
}
__finally
{
// If that didn't work...
if ( !NT_SUCCESS(status) )
{
RtlFreeUnicodeString(&pDevExt->ifname);
if(pDevExt->LowerDeviceObject != NULL)
IoDetachDevice(pDevExt->LowerDeviceObject);
if(pDevExt->FunctionalDeviceObject != NULL)
IoDeleteDevice(pDevExt->FunctionalDeviceObject);
}
}
DebugPrint("DemoPciAddDevice() End\n");
return (status);
}
上述动作完成后,PnP管理器将向每一个设备(当然也包括我们的PCIe设备)发送一个带IRP_MN_START_DEVICE副功能请求码的PnP请求。驱动将执行DriverEntry中注册的派遣函数(DemoPciPnp)。下面我们来看看具体的代码做了哪些工作,此阶段我们只谈论IRP_MN_START_DEVICE请求的处理,其他请求的处理后面继续探讨。
NTSTATUS DemoPciPnp(PDEVICE_OBJECT DeviceObject, PIRP pIrp)
{
PIO_STACK_LOCATION pIrpStack;
PDEMOPCI_DEVICE_EXT pDevExt;
KEVENT eventWaitLowerDrivers;
NTSTATUS status = STATUS_SUCCESS;
DebugPrint("DemoPciPnp() Start\n");
pDevExt = (PDEMOPCI_DEVICE_EXT)(DeviceObject->DeviceExtension);
/* 设置使用标志位 */
if( DemoPciRequestIncrement(pDevExt) == 0 )
{
pIrp->IoStatus.Status = STATUS_DELETE_PENDING;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp,IO_NO_INCREMENT);
return (status);
}
/* 初始化事件 */
KeInitializeEvent(&eventWaitLowerDrivers, NotificationEvent, FALSE);
pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
switch( pIrpStack->MinorFunction )
{
case IRP_MN_START_DEVICE: /* 当设备被检测到时,PnP管理器将发送该IRP请求 */
DebugPrint("IRP_MN_START_DEVICE Start\n");
/* 将当前IRP中IRP stack location 中的相关堆栈参数复制到下一层驱动的IRP stack location */
IoCopyCurrentIrpStackLocationToNext( pIrp );
/*设置IRP完成例程*/
IoSetCompletionRoutine( pIrp,
DemoPciPnpComplete,
&eventWaitLowerDrivers,
TRUE,
TRUE,
TRUE );
/*将该IRP转发给下层驱动,该例中的下层驱动对应着AddDevice函数挂载本设备对象的PDO*/
status = IoCallDriver(pDevExt->LowerDeviceObject, pIrp);
if (status == STATUS_PENDING)
{
/*等待PDO完成该IRP请求*/
KeWaitForSingleObject( &eventWaitLowerDrivers,
Executive,
KernelMode,
FALSE,
NULL );
status = pIrp->IoStatus.Status;
}
if ( NT_SUCCESS (status) )
{
/*若设备成功响应该IRP,则开始启动设备*/
status = DemoPciStartDevice(pDevExt, pIrpStack);
if ( NT_SUCCESS(status) )
{
pDevExt->State = STATE_STARTED;
pDevExt->Started = TRUE;
}
IoSetDeviceInterfaceState(&pDevExt->ifname, TRUE);
}
/*IRP完成*/
pIrp->IoStatus.Status = status;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
DebugPrint("IRP_MN_START_DEVICE End\n");
break;
case IRP_MN_STOP_DEVICE:
.....
.....
break;
case IRP_MN_REMOVE_DEVICE:
.....
.....
break;
case IRP_MN_QUERY_STOP_DEVICE:
.....
.....
break;
case IRP_MN_QUERY_REMOVE_DEVICE:
.....
.....
break;
case IRP_MN_SURPRISE_REMOVAL:
.....
.....
break;
case IRP_MN_CANCEL_STOP_DEVICE:
.....
.....
break;
case IRP_MN_CANCEL_REMOVE_DEVICE:
.....
.....
break;
default:
.....
.....
break;
}
/* 释放使用标志位 */
DemoPciRequestDecrement(pDevExt);
DebugPrint("DemoPciPnp() End\n");
return(status);
}
上述代码中我们看到:若设备成功响应了带IRP_MN_START_DEVICE的请求之后,将执行DemoPciStartDevice函数,该函数中我们将针对我们的具体设备做具体的资源分配,中断注册等工作。下面我们来仔细看看代码的具体实现。
NTSTATUS DemoPciStartDevice(IN PDEMOPCI_DEVICE_EXT pDevExt, IN PIO_STACK_LOCATION pIrpStack)
{
int i;
ULONG index;
ULONG count;
PCM_RESOURCE_LIST pResourceList, pResourceListTranslated;
PCM_PARTIAL_RESOURCE_LIST prl, prlTranslated;
PCM_PARTIAL_RESOURCE_DESCRIPTOR prd, prdTranslated;
PCM_FULL_RESOURCE_DESCRIPTOR frd, frdTranslated;
ULONG NumCount;
NTSTATUS status = STATUS_SUCCESS;
DEVICE_DESCRIPTION DeviceDescription;
INTERFACE_TYPE BusType;
ULONG Junk;
PUCHAR pBaseMem0, pBaseMem2;
ULONG value = 0;
ULONG iCnt = 0;
DebugPrint("DemoPciStartDevice() Start\n");
/*获取设备已经分配的资源*/
pResourceListTranslated = pIrpStack->Parameters.StartDevice.AllocatedResourcesTranslated;
if( (pResourceListTranslated == NULL) || (pResourceListTranslated->Count == 0) )
{
return (STATUS_INSUFFICIENT_RESOURCES);
}
frdTranslated = &pResourceListTranslated->List[0];
prlTranslated = &frdTranslated->PartialResourceList;
prdTranslated = prlTranslated->PartialDescriptors;
NumCount = prlTranslated->Count;
count = 0;
for (index=0; index<NumCount ; prdTranslated++,index++ )
{
switch (prdTranslated->Type)
{
/*I/O端口资源*/
case CmResourceTypePort:
pDevExt->base[count].IoPortSize = prdTranslated->u.Port.Length;
pDevExt->base[count].IoPortMappedAddress = (PVOID)prdTranslated->u.Port.Start.LowPart;
pDevExt->base[count].WhichMapped = TYPE_IO;
count++;
break;
/*Memory资源*/
case CmResourceTypeMemory:
pDevExt->base[count].MemorySize = prdTranslated->u.Memory.Length;
pDevExt->base[count].MemoryRegisterAddress = prdTranslated->u.Memory.Start;
pDevExt->base[count].MemoryMappedAddress = MmMapIoSpace( prdTranslated->u.Memory.Start, prdTranslated->u.Memory.Length, MmNonCached);
pDevExt->base[count].WhichMapped = TYPE_MEM;
count++;
break;
/*中断资源*/
case CmResourceTypeInterrupt:
pDevExt->InterruptLevel = (KIRQL)prdTranslated->u.Interrupt.Level;
pDevExt->InterruptVector = prdTranslated->u.Interrupt.Vector;
pDevExt->InterruptAffinity = prdTranslated->u.Interrupt.Affinity;
pDevExt->InterruptMode = (prdTranslated->Flags == CM_RESOURCE_INTERRUPT_LATCHED) ? Latched : LevelSensitive;
pDevExt->InterruptShare = (prdTranslated->ShareDisposition == CmResourceShareShared);
pDevExt->InterruptSupport = 1;
DebugPrint("Interrupt Support\n");
break;
/*DMA资源*/
case CmResourceTypeDma:
break;
/*总线资源*/
case CmResourceTypeBusNumber:
break;
default:
break;
}
}
/* It is fails if there is no resource */
if ( count == 0 )
{
return (STATUS_UNSUCCESSFUL);
}
{
/*插槽编号*/
PCI_SLOT_NUMBER slot_number;
IoGetDeviceProperty( pDevExt->PhysicalDeviceObject, DevicePropertyAddress, sizeof(ULONG), &slot_number, &Junk);
pDevExt->SlotNumber = (slot_number.u.AsULONG >> 16);
}
{
/*总线编号*/
PCI_SLOT_NUMBER slot_number;
IoGetDeviceProperty( pDevExt->PhysicalDeviceObject, DevicePropertyBusNumber, sizeof(ULONG), &slot_number, &Junk);
pDevExt->BusNumber = (slot_number.u.AsULONG);
}
{
/*DMA控制器*/
// The device description is clear
RtlZeroMemory(&DeviceDescription, sizeof(DEVICE_DESCRIPTION));
// Get busType
IoGetDeviceProperty( pDevExt->PhysicalDeviceObject, DevicePropertyLegacyBusType, sizeof(BusType), &BusType, &Junk);
// initial value is set
DeviceDescription.Version = DEVICE_DESCRIPTION_VERSION;
DeviceDescription.Master = TRUE;
DeviceDescription.ScatterGather = FALSE;
DeviceDescription.Dma32BitAddresses = TRUE;
DeviceDescription.InterfaceType = BusType;
DeviceDescription.MaximumLength = MAX_TRANSFER_SIZE;
// Get DMA Adapter
pDevExt->DmaAdapter = IoGetDmaAdapter( pDevExt->PhysicalDeviceObject, &DeviceDescription, &pDevExt->DmaMapRegsGot);
if( pDevExt->DmaAdapter == NULL )
{
return((NTSTATUS)STATUS_UNSUCCESSFUL);
}
DebugPrint("DmaAdapter=%x MapRegs=%x\n",pDevExt->DmaAdapter,pDevExt->DmaMapRegsGot);
}
/*本例中我们只使用了两个BAR*/
pBaseMem0 = (PUCHAR)(pDevExt->base[0].MemoryMappedAddress);
pBaseMem2 = (PUCHAR)(pDevExt->base[2].MemoryMappedAddress);
/*本设备我们使用了4124芯片作为PCI芯片,访问前需设置以下两个寄存器*/
WRITE_REGISTER_ULONG( (PULONG)(pBaseMem2 + 0x808), 0x1F03C);
WRITE_REGISTER_ULONG( (PULONG)(pBaseMem2 + 0x800), 0x25000);
do
{
value = READ_REGISTER_ULONG( (PULONG)(pBaseMem2 + 0x808));
if ((value & 0xE0000000) == 0xE0000000)
{
break;
}
iCnt++;
}while (iCnt < 10000000);
DebugPrint("[0x808] = 0x%08X! \n", value);
if (iCnt == 10000000)
{
DebugPrint("*************** Clock unlock! \n");
}
if(pDevExt->InterruptSupport != 0)
{
/*设置DPC*/
IoInitializeDpcRequest(pDevExt->FunctionalDeviceObject, DpcForIsr);
/*注册中断处理程序*/
status = IoConnectInterrupt( &pDevExt->InterruptObject, //. Interrupt object pointer
(PKSERVICE_ROUTINE)InterruptHandler, //. Interrupt processing routine name
pDevExt, //. Context passed to interrupt handler
NULL, //. Spinlock
pDevExt->InterruptVector, //. Interrupt vector
pDevExt->InterruptLevel, //. Interrupt IRQL
pDevExt->InterruptLevel, //. Most significant IRQL
pDevExt->InterruptMode, //. Levelsensitive value or latched value
pDevExt->InterruptShare, //. Interrupt share
pDevExt->InterruptAffinity, //. Interrupt affinity
FALSE ); //. x86 case FALSE
if( !NT_SUCCESS(status) )
{
return(status);
}
}
DebugPrint("DemoPciStartDevice() End\n");
return (STATUS_SUCCESS);
}
通过以上步骤,我们的PCIe设备已经获取了系统分配的资源并成功启动。上层的软件就可以通过相应的接口函数来对该设备进行访问了!