what are the factors can lead a DMA failure ?

Another thing… My 9056 requires that the DMA descriptors be in the
32-bit address space. It can do DMA in the 64-bit address space using the
dual address cycle, but the descriptors cannot be accessed by the DMA
controllers above the 4gb boundary. So, you need to make one WdfDmaEnabler
using the 32-bit profile to create the common buffer you use for the
descriptors and one using the 64-bit profile for the actual DMA
transactions.

  • Phil

On Fri, Feb 12, 2016 at 9:33 AM, Phil Selmer wrote:

> Are you using an actual PLX PCI9x5x? I use a custom board with a 9056 and
> it’s not very different from 9054 or 9656. If you can share your code
> where you are actually setting the DMA registers (DMAMODE, DMADPR, DMACSR)
> I can sanity check it. Also, have you made sure the interrupt is enabled
> in the INTCSR (bit 18 for channel 0 and bit 19 for channel 1)? You must
> enable it there in addition to DMAMODE bit 17.
>
>
> - Phil
>
> On Fri, Feb 12, 2016 at 8:56 AM, wrote:
>
>>


>>
>> There’s no “checking”… the interrupt occurs through an IDT entry. The
>> kernel dispatches the interrupt via a KINTERRUPT. The ISR is called.
>> That’s all that happens. The only difference between WDM and WDF is
>> there’s one added level of indirection in WDF (KINTERRUPT to interrupt
>> dispatcher to ISR in WDM versus KINTERRUPT to interrupt dispatcher to WDF
>> to ISR in WDF).
>>
>> If the DMA interrupt doesn’t occur, that means either that your ISR isn’t
>> connected to the interrupt (WdfInterruptCreate wasn’t done or done
>> correctly… something worth checking that only occurred to me right now)
>> or the hardware isn’t generating the interrupt. That is the entire
>> universe of the problem space.
>>
>> There’s no magic between your hardware and your ISR that Windows or WDM
>> or WDF mediates, as I explained above.
>>
>> I’m sorry, but seriously… that’s all there is to it.
>>
>> Peter
>> OSR
>> @OSRDrivers
>>
>>
>>
>> —
>> NTDEV is sponsored by OSR
>>
>> Visit the list online at: <
>> http://www.osronline.com/showlists.cfm?list=ntdev&gt;
>>
>> MONTHLY seminars on crash dump analysis, WDF, Windows internals and
>> software drivers!
>> Details at http:
>>
>> To unsubscribe, visit the List Server section of OSR Online at <
>> http://www.osronline.com/page.cfm?name=ListServer&gt;
>>
>
></http:>

My card is made of a Altera FPGA . So ,I eliminate a lot from the PLX9x5x.

The programming register is simple enough, and just as I had menstioned , it should worked well in the WinXP , because the traced the register’s value caught by the DriverStudio’s tool (The SoftIce can’t work any more .) is the same with description of the programming document , and the working procedure is rational.

I think the problem should be the DMA was not start totally.
All the WDF API called OK , so , all the WDF object should have be create well . Is there any other factors ?

> All the WDF API called OK , so , all the WDF object should have be create well

I would check the output of !wdfkd.wdflogdump just in case, it will tell you if there was a problem connecting the interrupt for example.

Driver.c

/*++

Module Name:

driver.c

Abstract:

This file contains the driver entry points and callbacks.

Environment:

Kernel-mode Driver Framework

–*/

#include “driver.h”
#include “driver.tmh”

#ifdef ALLOC_PRAGMA
#pragma alloc_text (INIT, DriverEntry)
#pragma alloc_text (PAGE, PCIEEvtDeviceAdd)
#pragma alloc_text (PAGE, PCIEEvtDriverContextCleanup)
#pragma alloc_text (PAGE, PCIEInitializeDeviceExtension)
#pragma alloc_text (PAGE, PCIEInitializeDMA)
#endif

NTSTATUS
DriverEntry(
In PDRIVER_OBJECT DriverObject,
In PUNICODE_STRING RegistryPath
)
/*++

Routine Description:
DriverEntry initializes the driver and is the first routine called by the
system after the driver is loaded. DriverEntry specifies the other entry
points in the function driver, such as EvtDevice and DriverUnload.

Parameters Description:

DriverObject - represents the instance of the function driver that is loaded
into memory. DriverEntry must initialize members of DriverObject before it
returns to the caller. DriverObject is allocated by the system before the
driver is loaded, and it is released by the system after the system unloads
the function driver from memory.

RegistryPath - represents the driver specific path in the Registry.
The function driver can use the path to store driver related data between
reboots. The path does not store hardware instance specific data.

Return Value:

STATUS_SUCCESS if successful,
STATUS_UNSUCCESSFUL otherwise.

–*/
{
WDF_DRIVER_CONFIG config;
NTSTATUS status;
WDF_OBJECT_ATTRIBUTES attributes;

//
// Initialize WPP Tracing
//
WPP_INIT_TRACING( DriverObject, RegistryPath );

TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, “%!FUNC! Entry”);

//
// Register a cleanup callback so that we can call WPP_CLEANUP when
// the framework driver object is deleted during driver unload.
//
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
attributes.EvtCleanupCallback = PCIEEvtDriverContextCleanup;

WDF_DRIVER_CONFIG_INIT(&config,
PCIEEvtDeviceAdd
);

status = WdfDriverCreate(DriverObject,
RegistryPath,
&attributes,
&config,
WDF_NO_HANDLE
);

if (!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, “WdfDriverCreate failed %!STATUS!”, status);
WPP_CLEANUP(DriverObject);
return status;
}

TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, “%!FUNC! Exit”);

return status;

}

NTSTATUS
PCIEEvtDeviceAdd(
In WDFDRIVER Driver,
Inout PWDFDEVICE_INIT DeviceInit
)
/*++
Routine Description:

EvtDeviceAdd is called by the framework in response to AddDevice
call from the PnP manager. We create and initialize a device object to
represent a new instance of the device.

Arguments:

Driver - Handle to a framework driver object created in DriverEntry

DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure.

Return Value:

NTSTATUS

–*/
{

NTSTATUS status;
WDF_OBJECT_ATTRIBUTES deviceAttributes;
PDEVICE_CONTEXT pdeviceContext;
WDFDEVICE device;

WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;

WDF_IO_QUEUE_CONFIG queueConfig ;

UNREFERENCED_PARAMETER(Driver);

PAGED_CODE();

TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, “%!FUNC! Entry”);

/**********************************************/

WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoDirect ) ;

//
// Zero out the PnpPowerCallbacks structure.
//
WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);

//
// Set Callbacks for any of the functions we are interested in.
// If no callback is set, Framework will take the default action
// by itself.
//
pnpPowerCallbacks.EvtDevicePrepareHardware = PCIEEvtDevicePrepareHardware; //?? I/O?MEM???DMA
pnpPowerCallbacks.EvtDeviceReleaseHardware = PCIEEvtDeviceReleaseHardware;

//
// These two callbacks set up and tear down hardware state that must be
// done every time the device moves in and out of the D0-working state.
//
pnpPowerCallbacks.EvtDeviceD0Entry = PCIEEvtDeviceD0Entry;
pnpPowerCallbacks.EvtDeviceD0Exit = PCIEEvtDeviceD0Exit;

//
// Register the PnP Callbacks…
//
WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);

/**********************************************/
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_CONTEXT);

status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
if (!NT_SUCCESS(status))
{

return status;
}

//
// Get a pointer to the device context structure that we just associated
// with the device object. We define this structure in the device.h
// header file. DeviceGetContext is an inline function generated by
// using the WDF_DECLARE_CONTEXT_TYPE_WITH_NAME macro in device.h.
// This function will do the type checking and return the device context.
// If you pass a wrong object handle it will return NULL and assert if
// run under framework verifier mode.
//
pdeviceContext = PCIEGetDeviceContext(device);

pdeviceContext->Device = device;

//
// Initialize the context.
//
pdeviceContext->PrivateDeviceData = 0;

//
// Create a device interface so that applications can find and talk
// to us.
//
status = WdfDeviceCreateDeviceInterface( pdeviceContext->Device ,
&GUID_DEVINTERFACE_PCIE,
NULL // ReferenceString
);

if (!NT_SUCCESS(status))
{
//
// Initialize the I/O Package and any Queues
//
//status = PCIE2QueueInitialize(device);
return status;
}

/************************************************************************/
//
// Create a new IO Queue for IRP_MJ_READ requests in sequential mode.
//
WDF_IO_QUEUE_CONFIG_INIT(&queueConfig, WdfIoQueueDispatchSequential);

queueConfig.EvtIoRead = PCIEEvtIoRead;

//
__analysis_assume(queueConfig.EvtIoStop != 0);
status = WdfIoQueueCreate( pdeviceContext->Device,
&queueConfig,
WDF_NO_OBJECT_ATTRIBUTES,
&pdeviceContext->ReadQueue);
__analysis_assume(queueConfig.EvtIoStop == 0);

if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, “WdfIoQueueCreate failed: %!STATUS!”, status);
return status;
}

//
// Set the Read Queue forwarding for IRP_MJ_READ requests.
//
status = WdfDeviceConfigureRequestDispatching(pdeviceContext->Device,
pdeviceContext->ReadQueue,
WdfRequestTypeRead);

if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER,
“DeviceConfigureRequestDispatching failed: %!STATUS!”, status);

return status;
}
/************************************************************************/

/************************************************************************/
//
// Configure a default queue so that requests that are not
// configure-fowarded using WdfDeviceConfigureRequestDispatching to goto
// other queues get dispatched here.
//
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE( &queueConfig,
WdfIoQueueDispatchParallel ) ;

queueConfig.EvtIoDeviceControl = PCIEEvtIoDeviceControl;
queueConfig.EvtIoStop = PCIEEvtIoStop;

status = WdfIoQueueCreate( pdeviceContext->Device ,
&queueConfig,
WDF_NO_OBJECT_ATTRIBUTES,
&pdeviceContext->DeviceIoContolQueue
);
if ( !NT_SUCCESS(status) )
{

TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, “WdfIoQueueCreate failed %!STATUS!”, status);

return status;
}

/**********************************************************************/

/**********************************************************************/
//
// Initalize the Device Extension. ??? ?? ? DMA
//
status = PCIEInitializeDeviceExtension( pdeviceContext ) ;
if (!NT_SUCCESS(status))
{
return status;
}
/**********************************************************************/

TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, “%!FUNC! Exit”);

return status;

}

VOID
PCIEEvtDriverContextCleanup(
In WDFOBJECT DriverObject
)
/*++
Routine Description:

Free all the resources allocated in DriverEntry.

Arguments:

DriverObject - handle to a WDF Driver object.

Return Value:

VOID.

–*/
{
UNREFERENCED_PARAMETER(DriverObject);

PAGED_CODE ();

TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, “%!FUNC! Entry”);

//
// Stop WPP Tracing
//
WPP_CLEANUP( WdfDriverWdmGetDriverObject(DriverObject) );

}

NTSTATUS
PCIEInitializeDeviceExtension(
IN PDEVICE_CONTEXT DevExt
)
/*++
Routine Description:

This routine is called by EvtDeviceAdd. Here the device context is
initialized and all the software resources required by the device is
allocated.

Arguments:

DevExt Pointer to the Device Extension

Return Value:

NTSTATUS

–*/
{

NTSTATUS status;

PAGED_CODE();

status = STATUS_SUCCESS;

UNREFERENCED_PARAMETER( DevExt );

/************************************************************************/
DevExt->Interrupt_CONTEXT.Count = 0;
DevExt->Interrupt_CONTEXT.PrivateDeviceData = 0;
DevExt->Interrupt_CONTEXT.Event = NULL;

// Create a WDFINTERRUPT object.
//
status = PCIEInterruptCreate(DevExt);

if (!NT_SUCCESS(status))
{
return status;
}
/************************************************************************/

/************************************************************************/
status = PCIEInitializeDMA(DevExt);

if (!NT_SUCCESS(status))
{
return status;
}

/************************************************************************/

return status;

}

NTSTATUS
PCIEInitializeDMA(
IN PDEVICE_CONTEXT DevExt
)
/*++
Routine Description:

Initializes the DMA adapter.

Arguments:

DevExt Pointer to our DEVICE_EXTENSION

Return Value:

None

–*/
{

NTSTATUS status;

WDF_OBJECT_ATTRIBUTES attributes;

PAGED_CODE();

//
// Set Maximum Transfer Length (which must be less than the SRAM size).
//
//DevExt->MaximumTransferLength = PCI9656_MAXIMUM_TRANSFER_LENGTH;
DevExt->MaximumTransferLength = PCIE_MAXIMUM_TRANSFER_LENGTH ;

TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, “MaximumTransferLength %d”, DevExt->MaximumTransferLength);

// PLx PCI9656 DMA_TRANSFER_ELEMENTS must be 4-byte aligned
//
WdfDeviceSetAlignmentRequirement(DevExt->Device, FILE_LONG_ALIGNMENT );

//
// Create a new DMA Enabler instance.
// Use Scatter/Gather, 64-bit Addresses, Duplex-type profile.
//
{

/** Step 1 **/
WDF_DMA_ENABLER_CONFIG dmaConfig;

WDF_DMA_ENABLER_CONFIG_INIT( &dmaConfig,
WdfDmaProfilePacket,
DevExt->MaximumTransferLength);

TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER ,
" - The DMA Profile is WdfDmaProfileScatterGather64Duplex");

/** Step 2 **/
status = WdfDmaEnablerCreate( DevExt->Device,
&dmaConfig,
WDF_NO_OBJECT_ATTRIBUTES,
&DevExt->DmaEnabler);
if (!NT_SUCCESS(status))
{

TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER ,
“WdfDmaEnablerCreate failed: %!STATUS!”, status);
return status;
}

}

/** Step 4 **/
// Since we are using sequential queue and processing one request
// at a time, we will create transaction objects upfront and reuse
// them to do DMA transfer. Transactions objects are parented to
// DMA enabler object by default. They will be deleted along with
// along with the DMA enabler object. So need to delete them
// explicitly.
//

//Read
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, TRANSACTION_CONTEXT);
// Create a new DmaTransaction.
status = WdfDmaTransactionCreate( DevExt->DmaEnabler,
//&attributes,
WDF_NO_OBJECT_ATTRIBUTES ,
&DevExt->ReadDmaTransaction);
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER ,
“WdfDmaTransactionCreate(read) failed: %!STATUS!”, status);
return status;
}

//Write
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, TRANSACTION_CONTEXT);
// Create a new DmaTransaction.
status = WdfDmaTransactionCreate( DevExt->DmaEnabler,
//&attributes,
WDF_NO_OBJECT_ATTRIBUTES ,
&DevExt->WriteDmaTransaction ) ;
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER ,
“WdfDmaTransactionCreate(write) failed: %!STATUS!”, status);
return status;
}

return status ;

}

Device.c

/*++

Module Name:

device.c - Device handling events for example driver.

Abstract:

This file contains the device entry points and callbacks.

Environment:

Kernel-mode Driver Framework

–*/

#include “driver.h”
#include “device.tmh”

#ifdef ALLOC_PRAGMA
//#pragma alloc_text (PAGE, PCIE2CreateDevice)
#pragma alloc_text (PAGE, PCIEEvtDevicePrepareHardware)
#pragma alloc_text (PAGE, PCIEEvtDeviceReleaseHardware)
#endif

PVOID LocalMmMapIoSpace(
In PHYSICAL_ADDRESS PhysicalAddress,
In SIZE_T NumberOfBytes
)
{

typedef
PVOID
(*PFN_MM_MAP_IO_SPACE_EX) (
In PHYSICAL_ADDRESS PhysicalAddress,
In SIZE_T NumberOfBytes,
In ULONG Protect
);

UNICODE_STRING name;
PFN_MM_MAP_IO_SPACE_EX pMmMapIoSpaceEx;

RtlInitUnicodeString(&name, L"MmMapIoSpaceEx");
pMmMapIoSpaceEx = (PFN_MM_MAP_IO_SPACE_EX)(ULONG_PTR)MmGetSystemRoutineAddress(&name);

if (pMmMapIoSpaceEx != NULL)
{
//
// Call WIN10 API if available
//
return pMmMapIoSpaceEx(PhysicalAddress,
NumberOfBytes,
PAGE_READWRITE | PAGE_NOCACHE);
}

//
// Supress warning that MmMapIoSpace allocates executable memory.
// This function is only used if the preferred API, MmMapIoSpaceEx
// is not present. MmMapIoSpaceEx is available starting in WIN10.
//
#pragma warning(suppress: 30029)
return MmMapIoSpace(PhysicalAddress, NumberOfBytes, MmNonCached);
}

NTSTATUS
PCIEEvtDevicePrepareHardware(
WDFDEVICE Device,
WDFCMRESLIST Resources,
WDFCMRESLIST ResourcesTranslated
)
/*++

Routine Description:

Performs whatever initialization is needed to setup the device, setting up
a DMA channel or mapping any I/O port resources. This will only be called
as a device starts or restarts, not every time the device moves into the D0
state. Consequently, most hardware initialization belongs elsewhere.

Arguments:

Device - A handle to the WDFDEVICE

Resources - The raw PnP resources associated with the device. Most of the
time, these aren’t useful for a PCI device.

ResourcesTranslated - The translated PnP resources associated with the
device. This is what is important to a PCI device.

Return Value:

NT status code - failure will result in the device stack being torn down

–*/
{

NTSTATUS status = STATUS_SUCCESS;
PDEVICE_CONTEXT pDevExt;

/********************************************************/

ULONG i;
CHAR *bar;

BOOLEAN foundSRAM1 = FALSE;
PHYSICAL_ADDRESS SRAM1Base1PA = { 0 };
ULONG SRAM1Length = 0;

BOOLEAN foundSRAM2 = FALSE;
PHYSICAL_ADDRESS SRAM2Base2PA = { 0 };
ULONG SRAM2Length = 0;

PCM_PARTIAL_RESOURCE_DESCRIPTOR desc;

UNREFERENCED_PARAMETER(Resources);
/********************************************************
UNREFERENCED_PARAMETER(Device);
UNREFERENCED_PARAMETER(ResourcesTranslated);
/********************************************************/

PAGED_CODE();

TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, “–> PLxEvtDevicePrepareHardware”);

pDevExt = PCIEGetDeviceContext(Device);

/***********************************************************/

// Parse the resource list and save the resource information.
//
for (i = 0; i < WdfCmResourceListGetCount(ResourcesTranslated); i++)
{

//call WdfCmResourceListGetDescriptor to get details about each individual resource.
desc = WdfCmResourceListGetDescriptor(ResourcesTranslated, i);

if (!desc)
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, “WdfResourceCmGetDescriptor failed”);
return STATUS_DEVICE_CONFIGURATION_ERROR;
}

switch (desc->Type)
{

case CmResourceTypeMemory:

bar = NULL;
if (desc->u.Memory.Length == PCIE_BAR1_SRAM_SIZE)
{

if ( SRAM1Length == 0 )
{
SRAM1Base1PA = desc->u.Memory.Start;
SRAM1Length = desc->u.Memory.Length;
foundSRAM1 = TRUE;
bar = “BAR1”;

break;
}
}

if (desc->u.Memory.Length == PCIE_BAR2_SRAM_SIZE)
{

if (SRAM2Length == 0)
{
SRAM2Base2PA = desc->u.Memory.Start;
SRAM2Length = desc->u.Memory.Length;
foundSRAM2 = TRUE;
bar = “BAR2”;
}

}

TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER,
" - Memory Resource [%I64X-%I64X] %s",
desc->u.Memory.Start.QuadPart,
desc->u.Memory.Start.QuadPart +
desc->u.Memory.Length,
(bar) ? bar : “”);
break;

case CmResourceTypePort:
break;

default:
//
// Ignore all other descriptors
//
break;
}

}

//
//
// Map in the SRAM Memory Space resource: BAR1
//
pDevExt->RegsBase = (PUCHAR)LocalMmMapIoSpace(SRAM1Base1PA, SRAM1Length);

if (!pDevExt->RegsBase)
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER,
" - Unable to map SRAM memory %08I64X, length %d",
SRAM1Base1PA.QuadPart, SRAM1Length);

return STATUS_INSUFFICIENT_RESOURCES;
}

pDevExt->RegsLength = SRAM1Length;

pDevExt->Regs = (PPCIE_INTERFACE_REGS)pDevExt->RegsBase;

/
/

TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER,
“<– PLxEvtDevicePrepareHardware, status %!STATUS!”, status);

return status;

}

NTSTATUS
PCIEEvtDeviceReleaseHardware(
IN WDFDEVICE Device,
IN WDFCMRESLIST ResourcesTranslated
)
/++

Routine Description:

Unmap the resources that were mapped in PLxEvtDevicePrepareHardware.
This will only be called when the device stopped for resource rebalance,
surprise-removed or query-removed.

Arguments:

Device - A handle to the WDFDEVICE

ResourcesTranslated - The translated PnP resources associated with the
device. This is what is important to a PCI device.

Return Value:

NT status code - failure will result in the device stack being torn down

/
{

NTSTATUS status = STATUS_SUCCESS;
PDEVICE_CONTEXT pDevExt;

UNREFERENCED_PARAMETER(Device);
UNREFERENCED_PARAMETER(ResourcesTranslated);

PAGED_CODE();

TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, “–> PLxEvtDeviceReleaseHardware”);

pDevExt = PCIEGetDeviceContext(Device);

if (pDevExt->RegsBase)
{
MmUnmapIoSpace(pDevExt->RegsBase, pDevExt->RegsLength);
pDevExt->RegsBase = NULL;
}

TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER,
“<– PLxEvtDeviceReleaseHardware”);

return status;
}

NTSTATUS
PCIEEvtDeviceD0Entry(
IN WDFDEVICE Device,
IN WDF_POWER_DEVICE_STATE PreviousState
)
/++

Routine Description:

This routine prepares the device for use. It is called whenever the device
enters the D0 state, which happens when the device is started, when it is
restarted, and when it has been powered off.

Note that interrupts will not be enabled at the time that this is called.
They will be enabled after this callback completes.

This function is not marked pageable because this function is in the
device power up path. When a function is marked pagable and the code
section is paged out, it will generate a page fault which could impact
the fast resume behavior because the client driver will have to wait
until the system drivers can service this page fault.

Arguments:

Device - The handle to the WDF device object

PreviousState - The state the device was in before this callback was invoked.

Return Value:

NTSTATUS

Success implies that the device can be used.

Failure will result in the device stack being torn down.

/
{
PDEVICE_CONTEXT devExt;
NTSTATUS status = STATUS_SUCCESS ;

UNREFERENCED_PARAMETER(PreviousState);

devExt = PCIEGetDeviceContext(Device);

/
status = PLxInitWrite(devExt);
if (NT_SUCCESS(status)) {

status = PLxInitRead(devExt);

}
/
return status;
}

NTSTATUS
PCIEEvtDeviceD0Exit(
IN WDFDEVICE Device,
IN WDF_POWER_DEVICE_STATE TargetState
)
/
++

Routine Description:

This routine undoes anything done in PLxEvtDeviceD0Entry. It is called
whenever the device leaves the D0 state, which happens when the device
is stopped, when it is removed, and when it is powered off.

The device is still in D0 when this callback is invoked, which means that
the driver can still touch hardware in this routine.

Note that interrupts have already been disabled by the time that this
callback is invoked.

Arguments:

Device - The handle to the WDF device object

TargetState - The state the device will go to when this callback completes.

Return Value:

Success implies that the device can be used. Failure will result in the
device stack being torn down.

/
{
PDEVICE_CONTEXT devExt;

PAGED_CODE();

devExt = PCIEGetDeviceContext(Device);

switch (TargetState) {
case WdfPowerDeviceD1:
case WdfPowerDeviceD2:
case WdfPowerDeviceD3:

//
// Fill in any code to save hardware state here.
//

//
// Fill in any code to put the device in a low-power state here.
//
break;

case WdfPowerDevicePrepareForHibernation:

//
// Fill in any code to save hardware state here. Do not put in any
// code to shut the device off. If this device cannot support being
// in the paging path (or being a parent or grandparent of a paging
// path device) then this whole case can be deleted.
//

break;

case WdfPowerDeviceD3Final:
default:

//
// Reset the hardware, as we’re shutting down for the last time.
//
//PLxShutdown(devExt);
break;
}

return STATUS_SUCCESS;

}

ISRDPC.c

/*++

Copyright (c) Microsoft Corporation. All rights reserved.

THIS CODE AND INFORMATION IS PROVIDED “AS IS” WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
PURPOSE.

Module Name:

IsrDpc.c

Abstract:

Contains routines related to interrupt and dpc handling.

Environment:

Kernel mode

–*/

#include “Driver.h”

#include “IsrDpc.tmh”

NTSTATUS
PCIEInterruptCreate(
IN PDEVICE_CONTEXT DevExt
)
/*++
Routine Description:

Configure and create the WDFINTERRUPT object.
This routine is called by EvtDeviceAdd callback.

Arguments:

DevExt Pointer to our DEVICE_EXTENSION

Return Value:

NTSTATUS code

–*/
{

NTSTATUS status = STATUS_SUCCESS ;
WDF_INTERRUPT_CONFIG InterruptConfig;

WDF_INTERRUPT_CONFIG_INIT( &InterruptConfig,
PCIEEvtInterruptIsr,
PCIEEvtInterruptDpc );

InterruptConfig.EvtInterruptEnable = PCIEEvtInterruptEnable;
InterruptConfig.EvtInterruptDisable = PCIEEvtInterruptDisable;

// JOHNR: Enable testing of the DpcForIsr Synchronization
InterruptConfig.AutomaticSerialization = TRUE;

//
// Unlike WDM, framework driver should create interrupt object in EvtDeviceAdd and
// let the framework do the resource parsing and registration of ISR with the kernel.
// Framework connects the interrupt after invoking the EvtDeviceD0Entry callback
// and disconnect before invoking EvtDeviceD0Exit. EvtInterruptEnable is called after
// the interrupt interrupt is connected and EvtInterruptDisable before the interrupt is
// disconnected.
//
status = WdfInterruptCreate( DevExt->Device,
&InterruptConfig,
WDF_NO_OBJECT_ATTRIBUTES,
&DevExt->Interrupt );

if( !NT_SUCCESS(status) )
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, “WdfInterruptCreate failed: %!STATUS!”, status);
}

return status;

}

BOOLEAN
PCIEEvtInterruptIsr(
IN WDFINTERRUPT Interrupt,
IN ULONG MessageID
)
/*++
Routine Description:

Interrupt handler for this driver. Called at DIRQL level when the
device or another device sharing the same interrupt line asserts
the interrupt. The driver first checks the device to make sure whether
this interrupt is generated by its device and if so clear the interrupt
register to disable further generation of interrupts and queue a
DPC to do other I/O work related to interrupt - such as reading
the device memory, starting a DMA transaction, coping it to
the request buffer and completing the request, etc.

Arguments:

Interupt - Handle to WDFINTERRUPT Object for this device.
MessageID - MSI message ID (always 0 in this configuration)

Return Value:

TRUE – This device generated the interrupt.
FALSE – This device did not generated this interrupt.

–*/
{

PDEVICE_CONTEXT devExt;
BOOLEAN isRecognized = FALSE;
ULONG intCsr;

UNREFERENCED_PARAMETER(MessageID);

//TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER,
// “–> PLxInterruptHandler”);
devExt = PCIEGetDeviceContext(WdfInterruptGetDevice(Interrupt));

//
// Read the Interrupt CSR register (INTCSR)
//
intCsr = 0;
intCsr = READ_REGISTER_ULONG((PULONG)&devExt->Regs->DMA_INT_CSR);

//
// Is DMA Active?
//
if (intCsr == 0x3 )
{

TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, " Interrupt for DMA ");

intCsr = 0x0;
WRITE_REGISTER_ULONG((PULONG)&devExt->Regs->DMA_INT_CSR, intCsr);
intCsr = READ_REGISTER_ULONG((PULONG)&devExt->Regs->DMA_INT_CSR);

isRecognized = TRUE;
}

if( isRecognized )
{
//
// A read or a write or both is done. Queue a DPC.
//
WdfInterruptQueueDpcForIsr( devExt->Interrupt );
}

//TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER,
// “<– PLxInterruptHandler”);

return isRecognized;

}

Use_decl_annotations
VOID
PCIEEvtInterruptDpc(
WDFINTERRUPT Interrupt,
WDFOBJECT Device
)
/*++

Routine Description:

DPC callback for ISR. Please note that on a multiprocessor system,
you could have more than one DPCs running simulataneously on
multiple processors. So if you are accesing any global resources
make sure to synchrnonize the accesses with a spinlock.

Arguments:

Interupt - Handle to WDFINTERRUPT Object for this device.
Device - WDFDEVICE object passed to InterruptCreate

Return Value:

–*/
{
NTSTATUS status = STATUS_SUCCESS ;
WDFDMATRANSACTION dmaTransaction;
PDEVICE_CONTEXT devExt = NULL;
BOOLEAN writeInterrupt = FALSE;
BOOLEAN readInterrupt = FALSE;
ULONG IntCSR;

UNREFERENCED_PARAMETER(Device);

TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, “–> EvtInterruptDpc”);

devExt = PCIEGetDeviceContext(WdfInterruptGetDevice(Interrupt));

//deviceContext = DeviceGetContext(Device);

// Acquire this device’s InterruptSpinLock.
WdfInterruptAcquireLock(Interrupt);

IntCSR = READ_REGISTER_ULONG((PULONG)&devExt->Regs->DMA_INT_CSR);

if ( IntCSR & 0x1 )
{

// If Dma0 channel 0 (write) is interrupting and the
// Done bit is set in the Dma0 CSR,
// we’re interrupting because a WRITE is complete.
// Clear the done bit and channel interrupting bit from
// our copies…
//
writeInterrupt = TRUE;

}
else if (IntCSR & 0x1)
{

readInterrupt = TRUE;

}
else
{
return;
}

//
// Release our interrupt spinlock
//
WdfInterruptReleaseLock( Interrupt );

/************************************************************************

//
// Did a Write DMA complete?
//
if (writeInterrupt)
{

BOOLEAN transactionComplete;

//
// Get the current Write DmaTransaction.
//
dmaTransaction = devExt->WriteDmaTransaction;

//
// Indicate this DMA operation has completed:
// This may drive the transfer on the next packet if
// there is still data to be transfered in the request.
//
transactionComplete = WdfDmaTransactionDmaCompleted(dmaTransaction,
&status);

if (transactionComplete)
{
//
// Complete this DmaTransaction.
//
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER,
“Completing Write request in the DpcForIsr”);

PCIEWriteRequestComplete(dmaTransaction, status);

}
}
/************************************************************************/

//
// Did a Read DMA complete?
//
if ( readInterrupt )
{

BOOLEAN transactionComplete;
//PDMA_TRANSFER_ELEMENT dteVA;
size_t length;

//
// Get the current Read DmaTransaction.
//
dmaTransaction = devExt->ReadDmaTransaction;

//
// Only on Read-side –
// Use “DMA Clear-Count Mode” to get complemetary
// transferred byte count.
//
length = WdfDmaTransactionGetCurrentDmaTransferLength( dmaTransaction );

/*
dteVA = (PDMA_TRANSFER_ELEMENT) devExt->ReadCommonBufferBase;

while( dteVA->LastElement == FALSE )
{

length -= dteVA->TransferSize;

dteVA++;

}

length -= dteVA->TransferSize;
*/

//
// Indicate this DMA operation has completed:
// This may drive the transfer on the next packet if
// there is still data to be transfered in the request.
//
transactionComplete =
WdfDmaTransactionDmaCompletedWithLength( dmaTransaction,
length,
&status );

if (transactionComplete)
{
//
// Complete this DmaTransaction.
//
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER,
“Completing Read request in the DpcForIsr”);

PCIEReadRequestComplete(dmaTransaction, status);

if (devExt->Interrupt_CONTEXT.Event != NULL)
{
// KeSetEvent( devExt->Interrupt_CONTEXT.Event, 0, FALSE);
}

}

}

TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, “<– EvtInterruptDpc”);

return;

}

NTSTATUS
PCIEEvtInterruptEnable(
IN WDFINTERRUPT Interrupt,
IN WDFDEVICE Device
)
/*++

Routine Description:

Called by the framework at DIRQL immediately after registering the ISR with the kernel
by calling IoConnectInterrupt.

Return Value:

NTSTATUS
–*/
{

PDEVICE_CONTEXT devExt;

union
{
// INT_CSR bits;
ULONG ulong;
} intCSR;

TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER,
“PLxEvtInterruptEnable: Interrupt 0x%p, Device 0x%p\n”,
Interrupt, Device);

devExt = PCIEGetDeviceContext(WdfInterruptGetDevice(Interrupt));

//LED4 ON
WRITE_REGISTER_ULONG((PULONG)&devExt->Regs->LED_CTRL, 0x8);

//intCSR.ulong = READ_REGISTER_ULONG((PULONG)&devExt->Regs->DMA_INT_CSR);
intCSR.ulong = 0x2;
WRITE_REGISTER_ULONG((PULONG)&devExt->Regs->DMA_INT_CSR, intCSR.ulong );
intCSR.ulong = READ_REGISTER_ULONG((PULONG)&devExt->Regs->DMA_INT_CSR);

return STATUS_SUCCESS;

}

NTSTATUS
PCIEEvtInterruptDisable(
IN WDFINTERRUPT Interrupt,
IN WDFDEVICE Device
)
/*++

Routine Description:

Called by the framework at DIRQL before Deregistering the ISR with the kernel
by calling IoDisconnectInterrupt.

Return Value:

NTSTATUS
–*/
{

PDEVICE_CONTEXT devExt;

union {
// INT_CSR bits;
ULONG ulong;
} intCSR;

TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER,
“PLxEvtInterruptDisable: Interrupt 0x%p, Device 0x%p\n”,
Interrupt, Device);

devExt = PCIEGetDeviceContext(WdfInterruptGetDevice(Interrupt));

//intCSR.ulong = READ_REGISTER_ULONG((PULONG)&devExt->Regs->DMA_INT_CSR);

intCSR.ulong = 0x0;
WRITE_REGISTER_ULONG((PULONG)&devExt->Regs->DMA_INT_CSR, intCSR.ulong );
intCSR.ulong = READ_REGISTER_ULONG((PULONG)&devExt->Regs->DMA_INT_CSR);

return STATUS_SUCCESS;

}

read.c

/*++

Copyright (c) Microsoft Corporation. All rights reserved.

THIS CODE AND INFORMATION IS PROVIDED “AS IS” WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
PURPOSE.

Module Name:

Read.c

Abstract:

Environment:

Kernel mode

–*/

#include “Driver.h”

#include “Read.tmh”

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
VOID
PCIEEvtIoRead(
IN WDFQUEUE Queue,
IN WDFREQUEST Request,
IN size_t Length
)
/*++

Routine Description:

Called by the framework as soon as it receives a read request.
If the device is not ready, fail the request.
Otherwise get scatter-gather list for this request and send the
packet to the hardware for DMA.

Arguments:

Queue - Default queue handle
Request - Handle to the write request
Lenght - Length of the data buffer associated with the request.
The default property of the queue is to not dispatch
zero lenght read & write requests to the driver and
complete is with status success. So we will never get
a zero length request.

Return Value:

–*/
{

NTSTATUS status = STATUS_UNSUCCESSFUL;
PDEVICE_CONTEXT pdevExt;

TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, “–> PLxEvtIoRead: Request %p”, Request);

//
// Get the DevExt from the Queue handle
//
pdevExt = PCIEGetDeviceContext(WdfIoQueueGetDevice(Queue));

do
{
// Validate the Length parameter.
//
if (Length > PCIE_SRAM_SIZE )
{
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
else
{
pdevExt->ReadDMALength = (ULONG)Length ;
}

// Initialize this new DmaTransaction.
status = WdfDmaTransactionInitializeUsingRequest(
pdevExt->ReadDmaTransaction,
Request,
PCIEEvtProgramReadDma,
WdfDmaDirectionReadFromDevice );

if(!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER,
"WdfDmaTransactionInitializeUsingRequest "
“failed: %!STATUS!”, status);
break;
}

#if 0 // FYI
//
// Modify the MaximumLength for this DmaTransaction only.
//
// Note: The new length must be less than or equal to that set when
// the DmaEnabler was created.
//
{
ULONG length = devExt->MaximumTransferLength / 2;

//TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER,
// “Setting a new MaxLen %d\n”, length);

WdfDmaTransactionSetMaximumLength( devExt->ReadDmaTransaction,
length );
}
#endif

//
// Execute this DmaTransaction.
//
status = WdfDmaTransactionExecute(pdevExt->ReadDmaTransaction,
WDF_NO_CONTEXT);

if(!NT_SUCCESS(status))
{
//
// Couldn’t execute this DmaTransaction, so fail Request.
//
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER,
“WdfDmaTransactionExecute failed: %!STATUS!”, status);
break;
}

//
// Indicate that Dma transaction has been started successfully.
// The request will be complete by the Dpc routine when the DMA
// transaction completes.
//
status = STATUS_SUCCESS;

} while ((void)0, 0);

//
// If there are errors, then clean up and complete the Request.
//
if (!NT_SUCCESS(status ))
{

WdfDmaTransactionRelease( pdevExt->ReadDmaTransaction);

WdfRequestComplete(Request, status);

}

TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER,
“<– PLxEvtIoRead: status %!STATUS!”, status);

return;
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
BOOLEAN
PCIEEvtProgramReadDma(
IN WDFDMATRANSACTION Transaction,
IN WDFDEVICE Device,
IN WDFCONTEXT Context,
IN WDF_DMA_DIRECTION Direction,
IN PSCATTER_GATHER_LIST SgList
)
/*++

Routine Description:

The framework calls a driver’s EvtProgramDma event callback function
when the driver calls WdfDmaTransactionExecute and the system has
enough map registers to do the transfer. The callback function must
program the hardware to start the transfer. A single transaction
initiated by calling WdfDmaTransactionExecute may result in multiple
calls to this function if the buffer is too large and there aren’t
enough map registers to do the whole transfer.

Arguments:

Return Value:

–*/
{

PDEVICE_CONTEXT pdevExt ;
size_t offset;
DMA_TRANSFER_ELEMENT dteVA;
//ULONG_PTR dteLA;
BOOLEAN errors;
ULONG i;

ULONG LED_State ;
ULONG tempx;
//NTSTATUS status;

UNREFERENCED_PARAMETER( Context );
UNREFERENCED_PARAMETER( Direction );

TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, “–> PLxEvtProgramReadDma”);

//
// Initialize locals
//
pdevExt = PCIEGetDeviceContext(Device);
errors = FALSE ;
dteVA.PciAddressLow = 0;
dteVA.PciAddressHigh = 0;
dteVA.TransferSize = 0;
dteVA.LastElement = 0;
dteVA.LocalAddress = 0;

// Get the number of bytes as the offset to the beginning of this
// Dma operations transfer location in the buffer.
//
offset = WdfDmaTransactionGetBytesTransferred(Transaction);

/*******************************************************************
// Setup the pointer to the next DMA_TRANSFER_ELEMENT
// for both virtual and physical address references.
//
dteVA = (PDMA_TRANSFER_ELEMENT)pdevExt->ReadCommonBufferBase;
dteLA = (pdevExt->ReadCommonBufferBaseLA.LowPart + sizeof(DMA_TRANSFER_ELEMENT));
/*******************************************************************/

// Translate the System’s SCATTER_GATHER_LIST elements
// into the device’s DMA_TRANSFER_ELEMENT elements.
//
for (i=0; i < SgList->NumberOfElements; i++)
{

// Construct this DTE.
//
// NOTE: The LocalAddress is the offset into the SRAM from
// where this Read will start.
//
dteVA.PciAddressLow = SgList->Elements[i].Address.LowPart;
dteVA.PciAddressHigh = SgList->Elements[i].Address.HighPart;
dteVA.TransferSize = SgList->Elements[i].Length;

dteVA.LocalAddress = (ULONG) offset;

// Increment the DmaTransaction length by this element length
//
offset += SgList->Elements[i].Length;

// If at end of SgList, then set LastElement bit in final NTE.
//
if (i == SgList->NumberOfElements - 1)
{

//dteVA->DescPtr.LastElement = TRUE;
dteVA.LastElement = TRUE;

break;
}

}

{

LED_State = READ_REGISTER_ULONG((PULONG)&pdevExt->Regs->LED_CTRL);
//LED ON
LED_State = 0;
WRITE_REGISTER_ULONG((PULONG)&pdevExt->Regs->LED_CTRL, LED_State);
LED_State = 0x6;
WRITE_REGISTER_ULONG((PULONG)&pdevExt->Regs->LED_CTRL, LED_State);
LED_State = 0;
WRITE_REGISTER_ULONG((PULONG)&pdevExt->Regs->LED_CTRL, LED_State);
LED_State = 0;
LED_State = READ_REGISTER_ULONG((PULONG)&pdevExt->Regs->LED_CTRL);
tempx = 0;

}

// Start the DMA operation.
// Acquire this device’s InterruptSpinLock.
//
WdfInterruptAcquireLock( pdevExt->Interrupt );

// DMA 1 Descriptor Pointer Register - (DMADPR1)
// Write the base LOGICAL address of the DMA_TRANSFER_ELEMENT list.
{
//size.ulong = pdevExt->ReadDMALength ;
tempx = dteVA.TransferSize;
WRITE_REGISTER_ULONG((PULONG)&pdevExt->Regs->DMA_SIZE, tempx );
}

{
//ptr.ulong = pdevExt->ReadCommonBufferBaseLA.LowPart;
tempx = dteVA.PciAddressLow;
WRITE_REGISTER_ULONG((PULONG)&pdevExt->Regs->DMA_ADDR_PHY, tempx );
}

TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, " PLxEvtProgramReadDma: Start a Read DMA operation");

// Interrupt CSR Register - (INTCSR)
// Enable PCI Ints and DMA Channel 1 Ints.
//
{
tempx = 0x2;
WRITE_REGISTER_ULONG((PULONG)&pdevExt->Regs->DMA_INT_CSR, tempx );
}

tempx = READ_REGISTER_ULONG((PULONG)&pdevExt->Regs->DMA_ADDR_PHY);
tempx = READ_REGISTER_ULONG((PULONG)&pdevExt->Regs->DMA_SIZE);
tempx = READ_REGISTER_ULONG((PULONG)&pdevExt->Regs->DMA_INT_CSR);

// DMA 1 CSR Register - (DMACSR1)
// Start the DMA operation: Set Enable and Start bits.
//
{

//dmaCSR.uchar = READ_REGISTER_UCHAR((PUCHAR)&pdevExt->Regs->DMA_CTRL);
tempx = PCIE_CARD_TO_MEN;
WRITE_REGISTER_ULONG((PULONG)&pdevExt->Regs->DMA_CTRL, tempx );

}

// Release our interrupt spinlock
//
WdfInterruptReleaseLock(pdevExt->Interrupt);

/*
//
length2 = WdfDmaTransactionGetCurrentDmaTransferLength(pdevExt->ReadDmaTransaction);

// Indicate this DMA operation has completed:
// This may drive the transfer on the next packet if
// there is still data to be transfered in the request.
//
errors =
WdfDmaTransactionDmaCompletedWithLength(pdevExt->ReadDmaTransaction,
length2,
&status);
*/

// NOTE: This shows how to process errors which occur in the
// PFN_WDF_PROGRAM_DMA function in general.
// Basically the DmaTransaction must be deleted and
// the Request must be completed.
//
//errors = TRUE;
if ( errors )
{

NTSTATUS status;

//
// Must abort the transaction before deleting.
//
(VOID) WdfDmaTransactionDmaCompletedFinal(Transaction, 0, &status);
ASSERT(NT_SUCCESS(status));

PCIEReadRequestComplete( Transaction, STATUS_INVALID_DEVICE_STATE );

TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, “<– PLxEvtProgramReadDma: errors ****”);

return FALSE;

}

TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, “<– PLxEvtProgramReadDma”);

return TRUE ;

}

VOID
PCIEReadRequestComplete(
IN WDFDMATRANSACTION DmaTransaction,
IN NTSTATUS Status
)
/*++

Routine Description:

Arguments:

Return Value:

–*/
{
WDFREQUEST request;
size_t bytesTransferred;

//
// Get the associated request from the transaction.
//
request = WdfDmaTransactionGetRequest(DmaTransaction);

ASSERT(request);

//
// Get the final bytes transferred count.
//
bytesTransferred = WdfDmaTransactionGetBytesTransferred( DmaTransaction );

TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER ,
"PLxReadRequestComplete: Request %p, Status %!STATUS!, "
“bytes transferred %d\n”,
request, Status, (int) bytesTransferred );

WdfDmaTransactionRelease(DmaTransaction);

//
// Complete this Request.
//
WdfRequestCompleteWithInformation( request, Status, bytesTransferred);

}

Are you sure your device is powered on when you’re doing the DMA? How are you sure of that?

-p

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@gmail.com
Sent: Friday, February 12, 2016 12:26 AM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] what are the factors can lead a DMA failure ?

The demo which made by the DriverStudio runs well in WinXP on the same PC .
So , the harrdware platform is verified.


NTDEV is sponsored by OSR

Visit the list online at: https:

MONTHLY seminars on crash dump analysis, WDF, Windows internals and software drivers!
Details at https:

To unsubscribe, visit the List Server section of OSR Online at https:</https:></https:></https:>

Of course ,it is a PCIE card , plugged in the motherboard .

I can control the LED on the card .

It should not be the interrupt’s problem. I have made a driver based on the Xilinx’s BMD, I discared the interrput handler, using polling directly.

xxxxx@gmail.com wrote:

I have tried all the FILE_xxxx_ALIGNMENT options, None can arouse the ISR .

You shouldn’t have to try them all. Your documentation should have a
line that says something like “the DMA engine in our device requires
32-byte alignment”. You have to KNOW this.

The operation of the hardware’s register is excatly the same as the demo in WinXP by the DriverStudio .

I doubt that. The facts are simple. If the hardware is set up
identically, then the DMA operation has to work. The hardware doesn’t
care what operating system or driver technology you use.

Are you saying that you never see a DMA completion bit, or are you
saying the DMA runs but you don’t see the results in memory?


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

It said that during the DMA , the physical address increase 4 after every double word , so I choose the FILE_LONG_ALIGNMENT option.

The flag of a DMA completion is the bit0 of the 0x4 offset reg mapped by Bar1 . When the reg set to 0x2, the interrupt allowed , when it turns to 0x3, the interrupt is coming ( this can be seen in the XP ).

After I set the DMA start ,
length2 = WdfDmaTransactionGetCurrentDmaTransferLength(pdevExt->ReadDmaTransaction);
length2 is just the same as the size I want to read.
So , the DMA does not run in all probability .

WdfDmaTrasactionGetCurrentDmaTransferLength() tells you how many bytes the current DMA transfer contains. You take that value and program it into your hardware. It is an INPUT to the DMA operation.

Only your hardware knows how many of those bytes it has transferred. Only your driver knows how to get that information from your device. No WDM or WDF API will tell you how many bytes your device has moved. You figure that out on your own.

WdfDmaTransactionGetCurrentDmaTransferLength() will return the same value every time you call it until you end the current transfer and start a new one. Then it will tell you the length of the next transfer.

-p

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@gmail.com
Sent: Friday, February 12, 2016 10:52 AM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] what are the factors can lead a DMA failure ?

It said that during the DMA , the physical address increase 4 after every double word , so I choose the FILE_LONG_ALIGNMENT option.

The flag of a DMA completion is the bit0 of the 0x4 offset reg mapped by Bar1 . When the reg set to 0x2, the interrupt allowed , when it turns to 0x3, the interrupt is coming ( this can be seen in the XP ).

After I set the DMA start ,
length2 = WdfDmaTransactionGetCurrentDmaTransferLength(pdevExt->ReadDmaTransaction);
length2 is just the same as the size I want to read.
So , the DMA does not run in all probability .


NTDEV is sponsored by OSR

Visit the list online at: https:

MONTHLY seminars on crash dump analysis, WDF, Windows internals and software drivers!
Details at https:

To unsubscribe, visit the List Server section of OSR Online at https:</https:></https:></https:>

A PCI-e card plugged into the mother board can be powered down. If you have a KMDF driver for the device and have enabled idle detection, it will be powered down for you automatically.

But the fact that you can control the LED on your card while you’re checking all of this DMA stuff probably means it’s powered up.

Another thing you might check is the DMA bus mastering enable flag in the PCI header. I’m not sure if those are still relevant for PCI-E but perhaps yours was being enabled by the old driver and you aren’t enabling it now.

-p

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@gmail.com
Sent: Friday, February 12, 2016 10:03 AM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] what are the factors can lead a DMA failure ?

Of course ,it is a PCIE card , plugged in the motherboard .

I can control the LED on the card .

It should not be the interrupt’s problem. I have made a driver based on the Xilinx’s BMD, I discared the interrput handler, using polling directly.


NTDEV is sponsored by OSR

Visit the list online at: https:

MONTHLY seminars on crash dump analysis, WDF, Windows internals and software drivers!
Details at https:

To unsubscribe, visit the List Server section of OSR Online at https:</https:></https:></https:>

Typically, a driver calls WdfDmaTransactionGetCurrentDmaTransferLength for devices that report residual DMA transfer lengths (that is, byte counts of data that was not transferred). By subtracting the residual transfer length from the value that WdfDmaTransactionGetCurrentDmaTransferLength returned, the driver can determine the actual transfer length. The driver then calls WdfDmaTransactionDmaCompletedWithLength to let the framework know the number of bytes that the device actually transferred.

So , WdfDmaTransactionGetCurrentDmaTransferLength can be used to check whether the DMA is finished or not .

So, the DMA does not carry out.

WdfDmaTransactionGetCurrentDmaTransferLength returns the length of the memory chunk you mapped for the current DMA transfer. This could be before you program the hardware to perform the DMA, or after the DMA was performed but before you tell WDF that you completed the transfer with one of WdfDmaTransactionDmaCompleted/WithLength/Final.

If your device reports residual lengths after it performed a transfer, then before you call DmaCompletedWithLength, you can calculate the actual number of bytes that were actually DMA’d with (WdfDmaTransactionGetCurrentDmaTransferLength - ResidualLength).

So WdfDmaTransactionGetCurrentDmaTransferLength itself does not tell you whether DMA actually happened or not, your hardware needs to tell you that.

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@gmail.com
Sent: Friday, February 12, 2016 11:30 AM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] what are the factors can lead a DMA failure ?

Typically, a driver calls WdfDmaTransactionGetCurrentDmaTransferLength for devices that report residual DMA transfer lengths (that is, byte counts of data that was not transferred). By subtracting the residual transfer length from the value that WdfDmaTransactionGetCurrentDmaTransferLength returned, the driver can determine the actual transfer length. The driver then calls WdfDmaTransactionDmaCompletedWithLength to let the framework know the number of bytes that the device actually transferred.

So , WdfDmaTransactionGetCurrentDmaTransferLength can be used to check whether the DMA is finished or not .

So, the DMA does not carry out.


NTDEV is sponsored by OSR

Visit the list online at: http:

MONTHLY seminars on crash dump analysis, WDF, Windows internals and software drivers!
Details at http:

To unsubscribe, visit the List Server section of OSR Online at http:</http:></http:></http:>

xxxxx@gmail.com wrote:

It said that during the DMA , the physical address increase 4 after every double word , so I choose the FILE_LONG_ALIGNMENT option.

That’s not what the alignment option means. Many FPGA DMA engines do
not implement all of the low-order address bits. For example, they
might not handle a transfer that begins at address 8765FFFF. They might
require that it be a multiple of 32 bytes, as in 8765FFE0.

The flag of a DMA completion is the bit0 of the 0x4 offset reg mapped by Bar1 . When the reg set to 0x2, the interrupt allowed , when it turns to 0x3, the interrupt is coming ( this can be seen in the XP ).

And do you see this bit get set?


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

This is a address that sglist->Elements[0].Address.LowPart give once : 0x7e0b5e38 , any problems?

I never see the bit be set in this WDF driver.

What’s the hell

If I complete the DMA in the EvtProgramDma Event Processing Callback routine. Then I shutdown the PC , it can’t be closed itself , liking be halted . So ,maybe the DMA operation is occuping the PICE bus ,leading this result.

xxxxx@gmail.com wrote:

This is a address that sglist->Elements[0].Address.LowPart give once : 0x7e0b5e38 , any problems?

I never see the bit be set in this WDF driver.

Are you talking about the low-order 12 bits? Unless you specified
page-alignment in your DMA adapter setup, you will always see those bits
be non-zero in the very first scatter/gather entry, because the buffer
will not actually start at the beginning of a page. The succeeding
elements should all start at a page boundary.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

xxxxx@gmail.com wrote:

If I complete the DMA in the EvtProgramDma Event Processing Callback routine. Then I shutdown the PC , it can’t be closed itself , liking be halted . So ,maybe the DMA operation is occuping the PICE bus ,leading this result.

You really do seem to be confused about the distinction between the
hardware DMA process, and the software management of that process. What
do you mean by “complete the DMA”? If you tell the kernel that your DMA
operation is complete, it’s going to feel free to release the bounce
buffers that it allocated below 4GB. If your hardware is still actively
transferring, then it is now writing into random memory that doesn’t
belong to you.

Remember please that neither KMDF nor the Windows kernel can affect your
hardware DMA engine in ANY WAY AT ALL. They can’t start transfers, and
they can’t stop transfers. Control of the DMA engine is entirely up to
your driver. KMDF will hand you the information you need, but it’s up
to you to configure the engine and start the transfer.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.