Common-buffer DMA? No translation required!

In common-buffer DMA, the driver and the device share the same area of memory for data transfer. If your driver uses common-buffer DMA, allocating that common buffer requires only a call to AllocateCommonBuffer. Your driver doesn't need to allocate contiguous memory or translate physical to virtual addresses. In fact, it shouldn't.

It might seem reasonable to allocate a buffer in contiguous memory and then translate the virtual addresses that your driver uses to access the buffer into physical addresses that your device can use. However, a straight virtual-to-physical translation doesn't account for differences in hardware architectures. A PCI bus with 32-bit addressing, for example, can't address physical memory locations above 4Gb, so this technique does not work on every Windows platform.

Instead, use AllocateCommonBuffer, which provides a simple and correct way to get the buffer along with hardware-independent addresses.

AllocateCommonBuffer allocates a region of memory specifically for use in DMA. It returns the base virtual address of the region for use by the driver and the base logical address for use by the device. The logical address space is independent of the underlying hardware, so logical addresses are guaranteed to work on every Windows platform. Internally, the HAL performs the address translations required on individual machines; the driver does not need to translate between logical addresses and physical addresses on behalf of its device.

Because DMA operations such as AllocateCommonBuffer depend on the underlying system and bus architecture, they cannot be implemented as single routines exported by Ntoskrnl.exe. Instead, these routines are implemented in the HAL and appropriate bus driver and accessed through the DMA adapter object that is returned by IoGetDmaAdapter. You call AllocateCommonBuffer through the pointer provided in the DMA_OPERATIONS structure that, in turn, is part of the DMA_ADAPTER structure returned by IoGetDmaAdapter.

The following code snippet shows how a driver can obtain this pointer.

// Call IoGetDmaAdapter to get adapter object.
DmaAdapterObject = IoGetDmaAdapter(PDO,
                                   &deviceDescription,
                                   &MapRegisters);        
// Save pointers to the commonly used DMA functions in 
// FdoData, which was previously initialized to point to 
// the device extension.
FdoData->AllocateCommonBuffer = 
   *DmaAdapterObject->DmaOperations->AllocateCommonBuffer; 
FdoData->FreeCommonBuffer = 
   *DmaAdapterObject->DmaOperations->FreeCommonBuffer;

The following example shows how the driver calls the routine through the saved pointer:

// Allocate a buffer for DMA and store the virtual and 
// logical addresses in FdoData.
FdoData->BaseVa = FdoData->AllocateCommonBuffer(
                  DmaAdapterObject,
                  FdoData->BufferLength,
                  &FdoData->BaseLogicalAddr,
                  FALSE
                );
if (FdoData->BaseVa == NULL)
        {
        DebugPrint(ERROR, DBG_INIT, 
          "Failed to allocate DMA buffer\n");
           status = STATUS_INSUFFICIENT_RESOURCES;
           break;
        }

The driver passes the adapter object, the requested buffer length, and the Boolean FALSE (which indicates that the allocated memory should not be cached). AllocateCommonBuffer returns the base logical address of the buffer as a parameter and the base virtual address of the buffer as the function return value. If allocation fails, the function returns NULL; the driver checks the returned value and sets an appropriate error status if necessary.

What should you do?

  • Call AllocateCommonBuffer to allocate memory and get virtual and logical addresses for common-buffer DMA.

  • Call FreeCommonBuffer to release memory and resources allocated by AllocateCommonBuffer.

  • Do not call MmAllocateContiguousMemory to allocate the buffer and MmGetPhysicalAddress to translate its virtual address to a physical address with which to program your DMA adapter. This approach will not work on all hardware, and your device and driver will not be cross-platform compatible. Serious errors can result.

  • Some device types have device-type specific DMA routines. If these are available for your device, use them.

  • Use Driver Verifier's DMA verification option (available on Windows XP and later releases), along with the debugger's !dma extension, to test your driver's DMA implementation.

 

 

Send comments about this topic to Microsoft