11.2 AdapterControl Routine Requirements

At a minimum, an AdapterControl routine must do the following:

  1. Save the input MapRegisterBase value, which the driver must pass to IoFlushAdapterBuffers when each DMA transfer operation is complete, and any other context information that the driver needs to carry out one or more DMA transfer operations for the current IRP.

  2. Return the appropriate IO_ALLOCATION_ACTION value:

    • KeepObject if the device is a slave so the driver uses system DMA.

    • DeallocateObjectKeepRegisters if the device is a busmaster so the driver uses packet-based busmaster DMA.

Depending on the driver’s design, its AdapterControl routine also can do the following before it returns control:

  1. Determine the starting location for the transfer on its device.

  2. Calculate the size of the transfer possible, given any limitations of its device due to the starting location of the transfer.

    In general, it is the responsibility of the routine that calls IoAllocateAdapterChannel to determine whether a given transfer request must be split up into partial transfers due to any platform-specific limitations on the NumberOfMapRegisters available for each DMA transfer operation, as mentioned in Section 11.1.4.

  3. Set up any driver-maintained state about each tranfer request in the device (or controller) extension.

    For example, an AdapterControl routine might call KeSetTimer with the entry point for a CustomTimerDpc routine that times out DMA transfer operations for the driver. For more information about CustomTimerDpc routines, see Chapter 14.

  4. Call MmGetMdlVirtualAddress with the MDL at Irp->MdlAddress to get an index for the start of the transfer, suitable for passing to IoMapTransfer.

  5. Call IoMapTransfer to set up the system DMA controller or to obtain a physical-to-logical address mapping for a busmaster device.

  6. Program the driver’s device for a transfer operation by calling KeSynchronizeExecution with a SynchCritSection routine, described in Chapter 10.

If a given transfer request requires the driver to perform a sequence of partial-transfer operations to satisfy the current IRP, usually the driver’s DpcForIsr routine is responsible for reprogramming the device for subsequent transfer operations. An AdapterControl routine is called only once for each incoming transfer IRP.

The driver routine that completes the current transfer IRP, usually the DpcForIsr, also is responsible for releasing the system DMA controller or busmaster adapter by calling IoFreeAdapterChannel or IoFreeMapRegisters, respectively. This driver routine should make the appropriate call as soon as possible when its last partial-transfer operation is done so that NT drivers of slave DMA devices can allocate the system DMA controller or a busmaster driver can begin processing the next transfer IRP promptly.