Sie sind auf Seite 1von 12

DEVICE_DESCRIPTION dd;

RtlZeroMemory(&dd, sizeof(dd));
dd.Version = DEVICE_DESCRIPTION_VERSION;
dd.Master = TRUE;
dd.InterfaceType = InterfaceTypeUndefined;
dd.MaximumLength = MAXTRANSFER;
dd.Dma32BitAddresses = TRUE;

pdx->AdapterObject = IoGetDmaAdapter(pdx->Pdo, &dd,


&pdx->nMapRegisters);

DEVICE_DESCRIPTION 结构如下页:

VOID StopDevice(...)
{

if (pdx->AdapterObject)
(*pdx->AdapterObject->DmaOperations->PutDmaAdapter)
(pdx->AdapterObject);
pdx->AdapterObject = NULL;

}
Field Name Description Relevant
to Device
Version Version number of structure—initialize All
to DEVICE_DESCRIPTION_VERSION
Master Bus-master device—set based on your All
knowledge of device
ScatterGather Device supports scatter/gather list—set All
based on your knowledge of device
DemandMode Use system DMA controller’s demand mode Slave
—set based on your knowledge of device
AutoInitialize Use system DMA controller’s Slave
autoinitialize mode—set based on your
knowledge of device
Dma32BitAddresses Can use 32-bit physical addresses All
IgnoreCount Controller doesn’t maintain an accurate Slave
transfer count—set based on your
knowledge of device
Reserved1 Reserved—must be FALSE
Dma64BitAddresses Can use 64-bit physical addresses All
DoNotUse2 Reserved—must be 0
DmaChannel DMA channel number—initialize from - Slave
Channel attribute of resource descriptor
InterfaceType Bus type—initialize to InterfaceType- All
Undefined
DmaWidth Width of transfers—set based on your Slave
knowledge of device to Width8Bits,
Width16Bits, or Width32Bits
DmaSpeed Speed of transfers—set based on your Slave
knowledge of device to Compatible, TypeA,
TypeB, TypeC, or TypeF
MaximumLength Maximum length of a single transfer—set All
based on your knowledge of device (and
round up to a multiple of PAGE_SIZE)
DmaPort Microchannel-type bus port number—- Slave
initialize from Port attribute of
resource descriptor
typedef struct _DEVICE_EXTENSION {

PADAPTER_OBJECT AdapterObject; // device's adapter object


ULONG nMapRegisters; // max # map registers
ULONG nMapRegistersAllocated; // # allocated for this xfer
ULONG numxfer; // # bytes transferred so far
ULONG xfer; // # bytes to transfer during this stage
ULONG nbytes; // # bytes remaining to transfer
PVOID vaddr; // virtual address for current stage
PVOID regbase; // map register base for this stage

} DEVICE_EXTENSION, *PDEVICE_EXTENSION;

VOID StartIo(PDEVICE_OBJECT fdo, PIRP Irp)


{
PDEVICE_EXTENSION pdx =
(PDEVICE_EXTENSION) fdo->DeviceExtension;

PMDL mdl = Irp->MdlAddress;


pdx->numxfer = 0;
pdx->xfer = pdx->nbytes = MmGetMdlByteCount(mdl);
pdx->vaddr = MmGetMdlVirtualAddress(mdl);

ULONG nregs = ADDRESS_AND_SIZE_TO_SPAN_PAGES(pdx->vaddr,


pdx->nbytes);
if (nregs > pdx->nMapRegisters)
{
nregs = pdx->nMapRegisters;
pdx->xfer = nregs * PAGE_SIZE - MmGetMdlByteOffset(mdl);
}
pdx->nMapRegistersAllocated = nregs;

NTSTATUS status = (*pdx->AdapterObject->DmaOperations


->AllocateAdapterChannel)(pdx->AdapterObject, fdo, nregs,
(PDRIVER_CONTROL) AdapterControl, pdx);
if (!NT_SUCCESS(status))
{
CompleteRequest(Irp, status, 0);
StartNextPacket(&pdx->dqReadWrite, fdo);
}
}

IO_ALLOCATION_ACTION AdapterControl(PDEVICE_OBJECT fdo,


PIRP junk, PVOID regbase, PDEVICE_EXTENSION pdx)
{
PIRP Irp = GetCurrentIrp(&pdx->dqReadWrite);//这里如何取得 IRP 要看
排队机制

PMDL mdl = Irp->MdlAddress;


PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);

BOOLEAN isread = stack->MajorFunction == IRP_MJ_READ;

pdx->regbase = regbase;

KeFlushIoBuffers(mdl, isread, TRUE);

PHYSICAL_ADDRESS address =
(*pdx->AdapterObject->DmaOperations->MapTransfer)
(pdx->AdapterObject, mdl, regbase, pdx->vaddr, pdx->xfer,
!isread);

……

return DeallocateObjectKeepRegisters;
}
VOID DpcForIsr(PKDPC Dpc, PDEVICE_OBJECT fdo,
PIRP junk, PDEVICE_EXTENSION pdx)
{

PIRP Irp = GetCurrentIrp(&pdx->dqReadWrite);


PMDL mdl = Irp->MdlAddress;
BOOLEAN isread = IoGetCurrentIrpStackLocation(Irp)
->MajorFunction == IRP_MJ_READ;

(*pdx->AdapterObject->DmaOperations->FlushAdapterBuffers)
(pdx->AdapterObject, mdl, pdx->regbase, pdx->vaddr,
pdx->xfer, !isread);

pdx->nbytes -= pdx->xfer;
pdx->numxfer += pdx->xfer;
NTSTATUS status = STATUS_SUCCESS;
……判断是否读写正确的完成了

if (pdx->nbytes && NT_SUCCESS(status))


{

pdx->vaddr = (PVOID) ((PUCHAR) pdx->vaddr + pdx->xfer);


pdx->xfer = pdx->nbytes;

ULONG nregs = ADDRESS_AND_SIZE_TO_SPAN_PAGES(pdx->vaddr,


pdx->nbytes);
if (nregs > pdx->nMapRegistersAllocated)
{
nregs = pdx->nMapRegistersAllocated;
pdx->xfer = nregs * PAGE_SIZE;
}
PHYSICAL_ADDRESS address =
(*pdx->AdapterObject->DmaOperations->MapTransfer)
(pdx->AdapterObject, mdl, pdx->regbase, pdx->vaddr,
pdx->xfer, !isread);
//这里是硬件特有的开始 DMA 传输的操作
……

}
//如果出错了或者传输完成了
else
{
ULONG numxfer = pdx->numxfer;

(*pdx->AdapterObject->DmaOperations->FreeMapRegisters)
(pdx->AdapterObject, pdx->regbase,
pdx->nMapRegistersAllocated);

StartNextPacket(&pdx->dqReadWrite, fdo);//开始下一次传输,这于依
赖于排队机制
CompleteRequest(Irp, status, numxfer);
}
}
//两个数据结构
typedef struct _SCATTER_GATHER_ELEMENT {
PHYSICAL_ADDRESS Address;
ULONG Length;
ULONG_PTR Reserved;
} SCATTER_GATHER_ELEMENT, *PSCATTER_GATHER_ELEMENT;

typedef struct _SCATTER_GATHER_LIST {


ULONG NumberOfElements;
ULONG_PTR Reserved;
SCATTER_GATHER_ELEMENT Elements[];
} SCATTER_GATHER_LIST, *PSCATTER_GATHER_LIST;

//AddDevice 中申请资源
pdx->sglist = (PSCATTER_GATHER_LIST)
ExAllocatePool(NonPagedPool, sizeof(SCATTER_GATHER_LIST) +
MAXSG * sizeof(SCATTER_GATHER_ELEMENT));
//adapterControl 中如下;
IO_ALLOCATION_ACTION AdapterControl(PDEVICE_OBJECT fdo,
PIRP junk, PVOID regbase, PDEVICE_EXTENSION pdx)
{

PIRP Irp = GetCurrentIrp(&pdx->dqReadWrite);


PMDL mdl = Irp->MdlAddress;
BOOLEAN isread = IoGetCurrentIrpStackLocation(Irp)
->MajorFunction == IRP_MJ_READ;
pdx->regbase = regbase;
KeFlushIoBuffers(mdl, isread, TRUE);
PSCATTER_GATHER_LIST sglist = pdx->sglist;

ULONG xfer = pdx->xfer;


PVOID vaddr = pdx->vaddr;
pdx->xfer = 0;
ULONG isg = 0;
//主要的不同在这里
while (xfer && isg < MAXSG)
{
ULONG elen = xfer;

sglist->Elements[isg].Address =
(*pdx->AdapterObject->DmaOperations->MapTransfer)
(pdx->AdapterObject, mdl, regbase, pdx->vaddr,
&elen, !isread);
sglist->Elements[isg].Length = elen;

xfer -= elen;
pdx->xfer += elen;
vaddr = (PVOID) ((PUCHAR) vaddr + elen);

++isg;
}
sglist->NumberOfElements = isg;

//这里开始设置特定的硬件,进行传输

return DeallocateObjectKeepRegisters;
}
VOID StartIo(PDEVICE_OBJECT fdo, PIRP Irp)
{
PDEVICE_EXTENSION pdx =
(PDEVICE_EXTENSION) fdo->DeviceExtension;
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
NTSTATUS status;
PMDL mdl = Irp->MdlAddress;
ULONG nbytes = MmGetMdlByteCount(mdl);
PVOID vaddr = MmGetMdlVirtualAddress(mdl);
BOOLEAN isread = stack->MajorFunction == IRP_MJ_READ;
pdx->numxfer = 0;
pdx->nbytes = nbytes;
status =
(*pdx->AdapterObject->DmaOperations->GetScatterGatherList)
(pdx->AdapterObject, fdo, mdl, vaddr, nbytes,
(PDRIVER_LIST_CONTROL) DmaExecutionRoutine, pdx, !isread);
if (!NT_SUCCESS(status))
{
CompleteRequest(Irp, status, 0);
StartNextPacket(&pdx->dqReadWrite, fdo);
}
}

//当条件满足时,调用这个例程
VOID DmaExecutionRoutine(PDEVICE_OBJECT fdo, PIRP junk,
PSCATTER_GATHER_LIST sglist, PDEVICE_EXTENSION pdx)
{
PIRP Irp = GetCurrentIrp(&pdx->dqReadWrite);
//保存以便这个地址被 PutScatterGatherList 用于释放列表。
pdx->sglist = sglist;
//这里开始设置特定的硬件,开始 dma 传输。如果列表中的元素个数超过设备一
次能处理的能力,需要分阶段执行整个传输。如果能相当快速地编程一个阶段,
建议在 ISR 中加入初始化下一阶段的逻辑。
}

当传输完成时,我们调用适配器对象的 PutScatterGatherList 函数释放列表和适配器:

VOID DpcForIsr(PKDPC Dpc, PDEVICE_OBJECT fdo, PIRP junk,


PVOID Context)
{

(*pdx->AdapterObject->DmaOperations->PutScatterGatherList)
(pdx->AdapterObject, pdx->sglist, !isread);

//使用系统 DMA 通道
NTSTATUS StartDevice(...)
{
ULONG dmachannel; // system DMA channel #
ULONG dmaport; // MCA bus port number

for (ULONG i = 0; i < nres; ++i, ++resource)


{
switch (resource->Type)
{
case CmResourceTypeDma:

dmachannel = resource->u.Dma.Channel;
dmaport = resource->u.Dma.Port;
break;
}
}

DEVICE_DESCRIPTION dd;
RtlZeroMemory(&dd, sizeof(dd));
dd.Version = DEVICE_DESCRIPTION_VERSION;
dd.InterfaceType = InterfaceTypeUndefined;
dd.MaximumLength = MAXTRANSFER;

dd.DmaChannel = dmachannel;
dd.DmaPort = dmaport;
dd.DemandMode = ??;
dd.AutoInitialize = ??;
dd.IgnoreCount = ??;
dd.DmaWidth = ??;
dd.DmaSpeed = ??;
pdx->AdapterObject = IoGetDmaAdapter(...);
}

IO_ALLOCATION_ACTION AdapterControl(...)
{

return KeepObject;//
}

返回值 KeepObject 指出希望保留映射寄存器和 DMA 通道的控制。第二,因为


我们在 AdapterControl 返回时没有释放适配器对象,所以我们必须在 DPC 例程
中做,调用 FreeAdapterChannel 替代 FreeMapRegisters :

VOID DpcForIsr(...)
{

(*pdx->AdapterObject->DmaOperations->FreeAdapterChannel)
(pdx->AdapterObject);

}
//使用公用缓冲区
typedef struct _DEVICE_EXTENSION {

PVOID vaCommonBuffer;
PHYSICAL_ADDRESS paCommonBuffer;

} DEVICE_EXTENSION, *PDEVICE_EXTENSION;

dd.Dma32BitAddresses = ??;
dd.Dma64BitAddresses = ??;
pdx->AdapterObject = IoGetDmaAdapter(...);
pdx->vaCommonBuffer =
(*pdx->AdapterObject->DmaOperations->AllocateCommonBuffer)
(pdx->AdapterObject, <length>, &pdx->paCommonBuffer, FALSE);

在调用 AllocateCommonBuffer 时,第二个参数是要分配缓冲区的字节长度。


第四个参数是布尔值,它表明是否需要使分配的内存进入 CPU 高速缓冲。

AllocateCommonBuffer 返回一个虚拟地址。它就是在驱动程序中用于访问已分
配缓冲区的地址。AllocateCommonBuffer 还通过其第三个参数设置了
PHYSICAL_ADDRESS 为设备使用的逻辑地址。

Das könnte Ihnen auch gefallen