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 ;
}