Peter,
thanks for unfastening my seatbelt – I am again debugging the initial problem (system freezes on second load of the driver – but outside of the driver code). So my assumption is I am leaving the device or system in bad state after the 1st driver load/unload iteration.
I am not touching any device hardware (MMIO) to the BARs; I have disabled all MMIO related calls and any further driver functionality (mostly SGDMA).
To answer your question(s):
So… the question comes down to:
a) Why are you calling GetBusData?
Basically to find out on which bus/device/function we are (as, at the time, PCIe hardware needed to be debugged).
b) Why are you calling GetBusData at the point that you’re calling it?
I think I copied this code from a 2008-time reference driver but don’t ask me which one – so far for tracability. (Does that count…?)
It is possible that calling the GetBusData from the bus interface isn’t happy to be called at EvtDriverDeviceAdd.
Maybe, how can I find out about its unhappiness, and how it wants to be treated otherwise?
I will dump the EvtDeviceAdd() for you to grumble at:
/**
* Perform device initialization when a device is found by the PnP manager
*/
NTSTATUS EvtDeviceAdd(IN WDFDRIVER Driver, IN PWDFDEVICE_INIT DeviceInit)
{
NTSTATUS Status = STATUS_SUCCESS;
WDF_PNPPOWER_EVENT_CALLBACKS PnpPowerCallbacks;
WDF_POWER_POLICY_EVENT_CALLBACKS PowerPolicyCallbacks;
WDF_OBJECT_ATTRIBUTES FdoAttributes;
WDFDEVICE Device;
WDF_INTERRUPT_CONFIG InterruptConfig;
WDF_IO_QUEUE_CONFIG QueueConfig;
WDF_OBJECT_ATTRIBUTES Attributes;
WDF_FILEOBJECT_CONFIG FileObjectConfig;
PDEVICE_CONTEXT DeviceContextData = NULL;
WDF_DMA_ENABLER_CONFIG DmaConfig;
ULONG BytesRead = 0;
uint32 Index;
PAGED_CODE();
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, DRIVERNAME " EvtDeviceAdd(Driver=0x%p)\n", Driver);
#if 1
/* we prefer Direct I/O
*
* Direct I/O only works with deferred buffer retrieval
* No guarantee that Direct I/O is actually used
* Direct I/O is only used for buffers that are full pages
* Buffered I/O is used for other parts of the transfer
*/
WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoDirect);
// Set call-backs for any of the functions we are interested in.
// If no call-back is set, the framework will take the default action by itself.
WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&PnpPowerCallbacks);
PnpPowerCallbacks.EvtDevicePrepareHardware = EvtDevicePrepareHardware;
PnpPowerCallbacks.EvtDeviceReleaseHardware = EvtDeviceReleaseHardware;
WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &PnpPowerCallbacks);
WDF_POWER_POLICY_EVENT_CALLBACKS_INIT(&PowerPolicyCallbacks);
WdfDeviceInitSetPowerPolicyEventCallbacks(DeviceInit, &PowerPolicyCallbacks);
// ContextCleanup will be called by the framework when it deletes the device.
// So you can defer freeing any resources allocated to Cleanup callback in the
// eventEvtDeviceAdd returns any error after the device is created.
FdoAttributes.EvtCleanupCallback = EvtDeviceContextCleanup;
// Register file object call-backs
WDF_OBJECT_ATTRIBUTES_INIT(&Attributes);
Attributes.SynchronizationScope = WdfSynchronizationScopeNone;
WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&Attributes, FILE_CONTEXT);
WDF_FILEOBJECT_CONFIG_INIT(&FileObjectConfig, EvtDeviceFileCreate, EvtFileClose, EvtFileCleanup);
WdfDeviceInitSetFileObjectConfig(DeviceInit, &FileObjectConfig, &Attributes);
// Specify the context type and size for the device we are about to create.
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&FdoAttributes, DEVICE_CONTEXT);
Status = WdfDeviceCreate(&DeviceInit, &FdoAttributes, &Device);
if (!NT_SUCCESS(Status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, DRIVERNAME " WdfDeviceCreate failed %d\n", Status);
return Status;
}
// Device creation is complete.
// Get the DeviceExtension and initialize it.
/* get the driver state context from the device */
DeviceContextData = GetDeviceContextData(Device);
/* reference from driver context to device */
DeviceContextData->mWdfDevice = Device;
#if 0
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP,
DRIVERNAME " PhysicalDevice=0x%p, Device=0x%p, Lower=0x%p, DevExt=0x%p\n",
WdfDeviceWdmGetPhysicalDevice(Device),
WdfDeviceWdmGetDeviceObject(Device),
WdfDeviceWdmGetAttachedDevice(Device),
DeviceContextData);
#endif
for (Index = 0; Index < 6; Index++) {
DeviceContextData->mBarPhysicalAddress[Index].QuadPart = 0;
DeviceContextData->mBarKernelVirtualAddress[Index] = NULL;
DeviceContextData->mBarLength[Index] = 0;
DeviceContextData->mBarIsMemory[Index] = FALSE;
}
DeviceContextData->mInterruptCount = 0;
DeviceContextData->mMyInterruptCount = 0;
/* the events device by default is only interested in user interrupts */
DeviceContextData->mEventsPendingMask = 0x0000FFFFUL;
#if 0
Status = WdfFdoQueryForInterface(DeviceContextData->mWdfDevice,
&GUID_BUS_INTERFACE_STANDARD,
(PINTERFACE) &DeviceContextData->mBusInterface,
sizeof(BUS_INTERFACE_STANDARD),
1, // Version
NULL); //InterfaceSpecificData
if (!NT_SUCCESS(Status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, DRIVERNAME " WdfFdoQueryForInterface failed 0x%X\n", Status);
return Status;
}
#endif
/* configure interrupt handler for device interrupts */
WDF_INTERRUPT_CONFIG_INIT(&InterruptConfig, EvtInterruptIsr, EvtInterruptDpc);
/* set callbacks for enabling/disabling interrupts */
InterruptConfig.EvtInterruptEnable = EvtInterruptEnable;
InterruptConfig.EvtInterruptDisable = EvtInterruptDisable;
/* register interrupt handler */
Status = WdfInterruptCreate(DeviceContextData->mWdfDevice, &InterruptConfig, WDF_NO_OBJECT_ATTRIBUTES, &DeviceContextData->mWdfInterrupt);
if (!NT_SUCCESS (Status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, DRIVERNAME " WdfInterruptCreate failed: 0x%X\n", Status);
return Status;
}
#if 0
Status = IoGetDeviceProperty(WdfDeviceWdmGetPhysicalDevice(Device),
DevicePropertyBusNumber,
sizeof(ULONG),
&DeviceContextData->mPciBusNr,
&BytesRead);
if (!NT_SUCCESS(Status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, DRIVERNAME " IoGetDeviceProperty (DevicePropertyBusNumber) failed\n");
return Status;
}
Status = IoGetDeviceProperty(WdfDeviceWdmGetPhysicalDevice(Device),
DevicePropertyAddress,
sizeof(ULONG),
&DeviceContextData->mPciFunctionNr,
&BytesRead);
if (!NT_SUCCESS(Status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, DRIVERNAME " - IoGetDeviceProperty (DevicePropertyAddress) failed\n");
return Status;
}
#endif
#if 0
// For PCI, the DevicePropertyAddress has device number in the high word and the function number in the low word.
DeviceContextData->mPciDeviceNr = (DeviceContextData->mPciFunctionNr >> 16) & 0x0000FFFF;
DeviceContextData->mPciFunctionNr &= 0x0000FFFF;
BytesRead = 0;
BytesRead = DeviceContextData->mBusInterface.GetBusData(DeviceContextData->mBusInterface.Context,
PCI_WHICHSPACE_CONFIG, //READ
&DeviceContextData->mPciCommonHeader,
FIELD_OFFSET(PCI_COMMON_CONFIG, VendorID),
PCI_COMMON_HDR_LENGTH);
if (BytesRead != PCI_COMMON_HDR_LENGTH) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, DRIVERNAME " GetBusData failed [got %d instead of %d bytes]\n", BytesRead, PCI_COMMON_HDR_LENGTH);
return STATUS_INVALID_DEVICE_REQUEST;
}
#endif
// Tell the Framework that this device will need an interface so that applications can interact with it.
Status = WdfDeviceCreateDeviceInterface(Device, (LPGUID) &GUID_DEVINTERFACE_LANCERO, NULL);
if (!NT_SUCCESS (Status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, DRIVERNAME " WdfDeviceCreateDeviceInterface failed with status %d\n", Status);
return Status;
}
/* accept multiple I/O request to run in parallel, they are sequentialized later */
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&QueueConfig, WdfIoQueueDispatchParallel);
/* callback handler for control requests */
QueueConfig.EvtIoDeviceControl = EvtIoDeviceControl;
/* callback handler for read requests */
QueueConfig.EvtIoRead = EvtIoRead;
/* callback handler for write requests */
QueueConfig.EvtIoWrite = EvtIoWrite;
/* create the default queue upon all I/O requests arrive */
Status = WdfIoQueueCreate(Device, &QueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &DeviceContextData->mBothQueue);
if (!NT_SUCCESS (Status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, DRIVERNAME " WdfIoQueueCreate failed with status %d\n", Status);
return Status;
}
WDF_IO_QUEUE_CONFIG_INIT(&QueueConfig, WdfIoQueueDispatchSequential);
/* callback handler for event read requests */
QueueConfig.EvtIoRead = EvtIoReadEvents;
Status = WdfIoQueueCreate(Device, &QueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &DeviceContextData->mEventsReadQueue);
if (!NT_SUCCESS (Status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, DRIVERNAME " WdfIoQueueCreate failed with status %d\n", Status);
return Status;
}
/* this queue should not forward requests until we ask it to */
WdfIoQueueStop(DeviceContextData->mEventsReadQueue, NULL, NULL);
/* at least 8 bytes alignment */
WdfDeviceSetAlignmentRequirement(Device, 8 - 1);
WDF_DMA_ENABLER_CONFIG_INIT(&DmaConfig, WdfDmaProfileScatterGather64Duplex, MAX_TRANSFER_SIZE);
/* WDF DMA Enabler */
Status = WdfDmaEnablerCreate(Device, &DmaConfig, WDF_NO_OBJECT_ATTRIBUTES, &DeviceContextData->mDmaEnabler);
if (!NT_SUCCESS (Status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, DRIVERNAME " WdfDmaEnablerCreate() failed with status %d\n", Status);
return Status;
}
#if 0
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, DRIVERNAME " Added device at Bus %u Device %u Function %u\n",
DeviceContextData->mPciBusNr, DeviceContextData->mPciDeviceNr, DeviceContextData->mPciFunctionNr);
#endif
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, DRIVERNAME " EvtDeviceAdd() returns status %d\n", Status);
#endif
return Status;
}