NT Device Driver for Busmaster device

Is there anyone out there who has any experience of writing NT Device
Drivers for Busmaster devices or could point me in the direction of some
appropriate font of knowledge?

I am trying to write a device driver that supports a device that can write
into NT host memory. To this end, I have written a driver that:

  1. Uses HalGetAdapter() to create an adapter object.
    Output: NumberOfMapRegisters = 1.
  2. Uses MmCreateMdl() to allocate and initialise an MDL. The driver passes
    the base and length of a local static buffer. The driver passes NULL as
    the input MDL in order to allocate and initialise an MDL from nonpaged
    pool.
    [I also tried the alternative route of IoAllocateMdl() followed by MmProbeAndLockPages, but this was no more successful.]
  3. Runs KeFlushIoBuffers() on this MDL with ReadOperation = FALSE and
    DmaOperation = TRUE.
  4. Uses IoAllocateAdapterChannel() (with required adjustment of IRQL) to
    register a suitable AdapterControl callback routine:
    Output: status = STATUS_SUCCESS
  5. The callback routine now runs and stores away the MapRegisterBase that
    was passed to it.
    Output: MapRegisterBase = 0x80D13A55
  6. The main thread now continues and uses MmGetMdlVirtualAddress() to
    obtain the VirtualAddress of the MDL that was created.
    Output: VirtualAddress = 0xF385BC48
  7. The driver then uses IoMapTransfer() to set up the map register and
    generate an address that the Busmaster device can use to write to NT host
    memory.
    Output: PhysicalAddress = 0x00211C48
  8. The driver passes this value to the Busmaster device which receives the
    message and uses this address to write back into host memory. The
    Busmaster device can confirm that the data it has written can be read
    back. But when my driver looks at the local static buffer, it does not
    find the data that was written. Apparently the Busmaster device has
    written it somewhere else.
  9. The driver finally runs IoFlushAdapterBuffers() with a NULL Adapter
    Object, but that does not make the data appear.
    Output: result = TRUE

Can anyone point out where I am going wrong or what step I have missed
out? Do the values that I am obtaining seem appropriate?

Thanks to anyone who can help.
Richard

xxxxx@cix.co.uk

> I am trying to write a device driver that supports a device that can write

into NT host memory. To this end, I have written a driver that:

  1. Uses HalGetAdapter() to create an adapter object.
    Output: NumberOfMapRegisters = 1.

Very strange. For PCI device, NumberOfMapRegisters is usually huge.

Can anyone point out where I am going wrong or what step I have missed
out? Do the values that I am obtaining seem appropriate?

If you want some permanent DMAble buffer - HalAllocateCommonBuffer is a
right thing.
IoMapTransfer is used to map the IRP’s MDLs to the DMA addresses to run the
device DMA directly over the buffer specified by the IO operation initiator.

Max

Richard – a few points:

  1. You should use IoGetDmaAdapter instead of HalGetAdapter. This is just
    friendly advice however, since HalGetDmaAdapter is an obsolete function…
    calling IoGetDmaAdapter will not fix your problem. There is nothing unusual
    about the numberofmapregisters being only 1 if map registers are actually
    being used – most of the time they are not. On a PC they are only used if
    you have a device that cannot address all of physical memory (i.e. a 24 bit
    isa card or a 32 bit PCI card on a machine with > 2^32 of RAM).

  2. Make sure that your AdapterControl callback routine returns
    DeallocateObjectKeepRegisters.

  3. Since the MapRegisterBase was NOT NULL, you are actually using map
    registers (you have a 24 bit ISA device, no?). The terminology doesn’t make
    a whole lot of sense on x86(NT originally ran on several architectures)
    since actual map registers are not used. Instead, the HAL double buffers
    dma when the device is not capable of reaching certain parts of physical
    memory (and calls the double-buffer a ‘map register’). For your driver,
    this means that the address your device talks to will NOT be the address
    pointed to by the mdl. This data will not show up until
    IoFlushAdapterBuffers is called.

  4. For IoFlushAdapterBuffers to work correctly, you need to pass an actual
    adapter object (I believe that this step will probably make your driver
    work).

  5. If your device is not a 24 bit isa device, you should set the
    Dma32BitAddresses flag in the DEVICE_DESCRIPTION structure when you call
    IoGetDmaAdapter & this problem will magically go away until you try your
    device on a machine with more than 4 gigs of ram.

  6. I’d advise using the new (windows)2000 dma apis that use indirect calls
    from the DMA_ADAPTER (unless, of course, you are writing for NT4) (e.g.
    DmaAdapter->DmaOperations->AllocateAdapterChannel(…))

I hope that this has been helpful.
-jordan

Hey Richard,

I guess the first question is: Which operating system? NT V4 or Win2K?
The DMA interface has changed slightly between the two. The calls you list
are NT V4 function calls, so I’ll assume that you wanna do this on NT V4.
If you’re trying to do DMA on Win2K, ignore the rest of this message.

  1. Uses HalGetAdapter() to create an adapter object.
    Output: NumberOfMapRegisters = 1.

The number of map registers returned is determined according to the maximum
transfer size you indicate in your Device Description data structure, and
is influenced by whether you can do 32 bit DMA and whether or not your
device is capable of scatter/gather support.

  1. Uses MmCreateMdl() to allocate and initialise an MDL. The driver passes
    the base and length of a local static buffer. The driver passes NULL as
    the input MDL in order to allocate and initialise an MDL from nonpaged
    pool.
    [I also tried the alternative route of IoAllocateMdl() followed by MmProbeAndLockPages, but this was no more successful.]

As somebody (Max?) posted, if the idea is to simply slam some data into
some allocated location in main memory, you’re better off using
HalAllocateCommonBuffer(). This returns you a Kernel Virtual Address, as
well as a “physical address” (well, it’s really a device bus logical
address by why quibble) that’s appropriate for use with DMA on the system.
Just call put the “physical address” into your device, set the byte count
and direction, and set the GO bit. You’re done.

If you want to use an MDL, it has to POINT to a buffer that’s being
described. If the buffer you’re trying to describe is in non-paged pool,
you can use MmBuildMdlForNonpagedPool().

  1. Runs KeFlushIoBuffers() on this MDL with ReadOperation = FALSE and
    DmaOperation = TRUE.

My favorite function. Have you see the definition of this in NTDDK.H?? In
any case, calling it here is both correct and appropriate.

  1. Uses IoAllocateAdapterChannel() (with required adjustment of IRQL) to
    register a suitable AdapterControl callback routine:
    Output: status = STATUS_SUCCESS
  2. The callback routine now runs and stores away the MapRegisterBase that
    was passed to it.
    Output: MapRegisterBase = 0x80D13A55

Which means to me you indicated that your device does NOT support
scatter/gather… or that you don’t support DMAing 32 bit addresses.

  1. The main thread now continues and uses MmGetMdlVirtualAddress() to
    obtain the VirtualAddress of the MDL that was created.
    Output: VirtualAddress = 0xF385BC48
  2. The driver then uses IoMapTransfer() to set up the map register and
    generate an address that the Busmaster device can use to write to NT host
    memory.
    Output: PhysicalAddress = 0x00211C48
  3. The driver passes this value to the Busmaster device which receives the
    message and uses this address to write back into host memory. The
    Busmaster device can confirm that the data it has written can be read
    back. But when my driver looks at the local static buffer, it does not
    find the data that was written. Apparently the Busmaster device has
    written it somewhere else.

Ahh! That’s cuz it’s not IN your buffer yet… see below.

  1. The driver finally runs IoFlushAdapterBuffers() with a NULL Adapter
    Object, but that does not make the data appear.
    Output: result = TRUE

Well, this step is the step that should make the data appear in your
buffer. I’m assuming you’re passing the MapRegisterBase, as you received
it in your callback. I’m not sure about the NULL pointer to the Adapter…
That shouldn’t matter, but it wouldn’t hurt to specify it, either.

Can anyone point out where I am going wrong or what step I have missed
out? Do the values that I am obtaining seem appropriate?

At first glance, yes.

HTH,

PeterGV