📄 dmasampledevice.cpp
字号:
// DMASampleDevice.cpp
// Implementation of DMASampleDevice device class
//
// Generated by DriverWizard version DriverStudio 2.6.0 (Build 336)
// Requires Compuware's DriverWorks classes
//
#pragma warning(disable:4065) // Allow switch statement with no cases
#include <vdw.h>
#include "..\DMASampleDeviceinterface.h"
#include "DMASample.h"
#include "DMASampleDevice.h"
#pragma hdrstop("DMASample.pch")
KTrace t("DMASample");
GUID DMASampleDevice_Guid = DMASampleDevice_CLASS_GUID;
DMASampleDevice::DMASampleDevice(PDEVICE_OBJECT Pdo, ULONG Unit) :
KPnpDevice(Pdo, &DMASampleDevice_Guid)
{
// Check constructor status
if ( ! NT_SUCCESS(m_ConstructorStatus) )
{
return;
}
// Remember our unit number
m_Unit = Unit;
// Initialize the lower device
m_Lower.Initialize(this, Pdo);
// Inform the base class of the lower edge device object
SetLowerDevice(&m_Lower);
// Initialize the PnP Policy settings to the "standard" policy
SetPnpPolicy();
}
DMASampleDevice::~DMASampleDevice()
{
}
NTSTATUS DMASampleDevice::DefaultPnp(KIrp I)
{
I.ForceReuseOfCurrentStackLocationInCalldown();
return m_Lower.PnpCall(this, I);
}
NTSTATUS DMASampleDevice::DefaultPower(KIrp I)
{
I.IndicatePowerIrpProcessed();
I.CopyParametersDown();
return m_Lower.PnpPowerCall(this, I);
}
NTSTATUS DMASampleDevice::SystemControl(KIrp I)
{
I.ForceReuseOfCurrentStackLocationInCalldown();
return m_Lower.PnpCall(this, I);
}
VOID DMASampleDevice::Invalidate()
{
// It is not necessary to release the system resource for the DMA adapter
// object, since NT provides no mechanism for this.
m_Buffer.Invalidate();
}
NTSTATUS DMASampleDevice::OnStartDevice(KIrp I)
{
NTSTATUS status = STATUS_SUCCESS;
I.Information() = 0;
// The default Pnp policy has already cleared the IRP with the lower device
// Initialize the physical device object.
// Get the list of raw resources from the IRP
PCM_RESOURCE_LIST pResListRaw = I.AllocatedResources();
// Get the list of translated resources from the IRP
PCM_RESOURCE_LIST pResListTranslated = I.TranslatedResources();
// TODO: Check to ensure that the following parameters are correct for your hardware
//
#define MAX_DMA_LENGTH 0x100000 // 0x100000 is 1 MB
// Initialize the device descriptor for the DMA object using the assigned resource
DEVICE_DESCRIPTION dd;
RtlZeroMemory(&dd, sizeof(dd));
dd.Version = DEVICE_DESCRIPTION_VERSION;
dd.Master = TRUE;
dd.ScatterGather = FALSE;
dd.DemandMode = TRUE;
dd.AutoInitialize = FALSE;
dd.Dma32BitAddresses = TRUE;
dd.IgnoreCount = FALSE;
dd.DmaChannel = 0;
dd.InterfaceType = PCIBus;
dd.DmaWidth = Width32Bits; // PCI default width
dd.DmaSpeed = Compatible;
dd.MaximumLength = MAX_DMA_LENGTH;
// Initialize the DMA adapter object
m_Dma.Initialize(&dd, m_Lower.TopOfStack());
m_Buffer.Initialize(&m_Dma,1024);
return status;
}
NTSTATUS DMASampleDevice::OnStopDevice(KIrp I)
{
// Device stopped, release the system resources.
Invalidate();
return STATUS_SUCCESS;
}
NTSTATUS DMASampleDevice::OnRemoveDevice(KIrp I)
{
// Device removed, release the system resources.
Invalidate();
return STATUS_SUCCESS;
}
VOID DMASampleDevice::CancelQueuedIrp(KIrp I)
{
KDeviceQueue dq(DeviceQueue());
// Test if the IRP is the current IRP.
if ( (PIRP)I == CurrentIrp() )
{
CurrentIrp() = NULL;
CancelSpinLock::Release(I.CancelIrql());
I.Information() = 0;
I.Status() = STATUS_CANCELLED;
PnpNextIrp(I);
}
// See if the IRP can be removed from the device queue.
else if (dq.RemoveSpecificEntry(I))
{
CancelSpinLock::Release(I.CancelIrql());
I.Information() = 0;
I.PnpComplete(this, STATUS_CANCELLED);
}
else
{
CancelSpinLock::Release(I.CancelIrql());
}
}
VOID DMASampleDevice::StartIo(KIrp I)
{
if ( !I.TestAndSetCancelRoutine(
LinkTo(CancelQueuedIrp),
NULL,
CurrentIrp()) )
{
return;
}
switch (I.MajorFunction())
{
case IRP_MJ_READ:
SerialRead(I);
break;
case IRP_MJ_WRITE:
SerialWrite(I);
break;
default:
ASSERT(FALSE);
PnpNextIrp(I);
break;
}
}
NTSTATUS DMASampleDevice::Create(KIrp I)
{
NTSTATUS status;
status = I.PnpComplete(this, STATUS_SUCCESS, IO_NO_INCREMENT);
return status;
}
NTSTATUS DMASampleDevice::Close(KIrp I)
{
NTSTATUS status;
status = I.PnpComplete(this, STATUS_SUCCESS, IO_NO_INCREMENT);
return status;
}
NTSTATUS DMASampleDevice::CleanUp(KIrp I)
{
KDeviceQueue dq(DeviceQueue());
dq.PnpCleanUp(this, I.FileObject());
return I.PnpComplete(this, STATUS_SUCCESS);
}
void DMASampleDevice::SerialRead(KIrp I)
{
NTSTATUS status = STATUS_SUCCESS;
m_CurrentTransfer = new(NonPagedPool) KDmaTransfer(this, &m_Dma);
if ( m_CurrentTransfer == NULL )
{
status = STATUS_INSUFFICIENT_RESOURCES;
DbgPrint("unable to allocate transfer object: %x\n", status);
I.Information() = 0;
I.Status() = status;
PnpNextIrp(I);
}
status = m_CurrentTransfer->Initiate(
this,
&m_Dma,
I.Mdl(),
(I.MajorFunction() == IRP_MJ_READ) ? FromDeviceToMemory : FromMemoryToDevice,
LinkTo(OnDmaReady),
&m_Buffer
); //运行Initiate后,系统将首次调用OnDmaReady
// If the transfer cannot be initiated, complete it with an error status.
if ( ! NT_SUCCESS(status) )
{
DbgPrint("unable to initiate transfer: %x\n", status);
delete m_CurrentTransfer;
m_CurrentTransfer = NULL;
I.Information() = 0;
I.Status() = status;
PnpNextIrp(I);
}
}
NTSTATUS DMASampleDevice::Read(KIrp I)
{
if (I.ReadSize() == 0)
{
I.Information() = 0;
return I.PnpComplete(this, STATUS_SUCCESS);
}
return QueueIrp(I, LinkTo(CancelQueuedIrp));
}
void DMASampleDevice::SerialWrite(KIrp I)
{
NTSTATUS status = STATUS_SUCCESS;
// Declare a memory object
KMemory Mem(I.Mdl());
// Use the memory object to create a pointer to the caller's buffer
PVOID pSBuffer = Mem.MapToSystemSpace();
//应用程序输出缓冲区在内核模式中的地址
PVOID pDBuffer = m_Buffer.VirtualAddress();//公用缓冲区的内核模式地址
ULONG dwTotalSize = I.WriteSize(CURRENT); //所写数据的字节长度
ULONG dwBytesSent = 0;
if (dwTotalSize > 1024) dwBytesSent = 1024;
else dwBytesSent = dwTotalSize;
RtlCopyMemory(pDBuffer, pSBuffer, dwBytesSent);
//将应用程序写缓冲区数据拷贝至公用缓冲区,模仿DMA操作
I.Information() = dwBytesSent;
I.Status() = status;
PnpNextIrp(I);
}
NTSTATUS DMASampleDevice::Write(KIrp I)
{
if (I.WriteSize() == 0)
{
I.Information() = 0;
return I.PnpComplete(this, STATUS_SUCCESS);
}
return QueueIrp(I, LinkTo(CancelQueuedIrp));
}
VOID DMASampleDevice::IsrProxyTimer(PVOID Arg1, PVOID Arg2)
{
m_CurrentTransfer->Continue(UseTransferSize);
//当DMA传输完成后,将产生中断,运行Continue,系统将再次调用OnDmaReady
}
VOID DMASampleDevice::StartDMA(ULONG PAddress,ULONG NBytes)
{
// Normally at this point we would use the transfer descriptors to set up
// the hardware for the next DMA. Once the transfer was setup, it would be
// initiated and once the transfer was complete, the interrupt would be
// signaled. We simply set up a timer to emmulate this process.
//由于并不存在实际的DMA操作,产生不了DMA中断,只好启动一个1秒的定时器,
//来模拟DMA中断
LONGLONG DueTime = -10000000; // 100ns units (1sec)
m_IsrProxyTimer.Set(
DueTime,
LinkTo(IsrProxyTimer),
this
);
}
VOID DMASampleDevice::OnDmaReady(KDmaTransfer* pXfer, KIrp I)
{
t << "Entering OnDmaReady\n";
// All KDmaTransfer callbacks must first check to see if there are any bytes
// left to transfer.
if (pXfer->BytesRemaining() == 0)
{ // 一次DMA传输完成
// If there are no bytes left to transfer, the callback must call
// Terminate(). Then it completes the IRP with STATUS_SUCCESS.
t << "terminating transfer\n";
pXfer->Terminate();
I.Information() = I.ReadSize(CURRENT);
I.Status() = STATUS_SUCCESS;
PnpNextIrp(I);
m_CurrentTransfer = NULL;
delete pXfer;
return;
}
// We must get the descriptor for the physical memory location for
// the DMA transfer.
PTRANSFER_DESCRIPTOR ptd;
while (pXfer->SequenceTransferDescriptors(&ptd)) {
// program the h/w using ppTD
t << "Physical address 0x" << ptd->td_PhysAddr.LowPart << ". Length is 0x"
<< ptd->td_Length << "." << EOL;
}
// If this is the first time through, then start the DMA going.
// We only want to do this ONCE for a given Read transfer. That
// way, our data will be collected smoothly, without interruptions
// or dropouts.
if ((ULONG) pXfer->BytesRemaining() == I.ReadSize())
StartDMA(ptd->td_PhysAddr.LowPart,ptd->td_Length);
//首次调用OnDmaReady,应启动DMA传输
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -