Writing a Common Buffer Slave DMA Driver

< BACK  NEXT >
[oR]

In common buffer slave DMA, the device transfers data to or from a contiguous buffer in nonpaged pool, using a system DMA channel. Although originally intended for devices that use the system DMA controller's autoinitialize mode, common buffers can also improve throughput for some types of ISA-based slave devices.

Allocating a Common Buffer

Memory for a common buffer must be physically contiguous and visible in the DMA logical space of a specific device. To guarantee that both these conditions are met, the function AllocateCommonBuffer, a method of the Adapter object, is used. It is described in Table 12.8.

The CacheEnabled argument for this function is normally set to FALSE. Using noncached memory for the common buffer eliminates the need to call KeFlushIoBuffers. On some platforms, this can significantly improve the performance of both the driver and the system.

Table 12.8. Function Prototype for AllocateCommonBuffer
PVOID AllocateCommonBuffer IRQL == PASSIVE_LEVEL
Parameter Description
IN PDMA_ADAPTER pDmaAdapter Points to the DMA_ADAPTER structure returned by IoGetDmaAdapter
IN ULONG Length Requested size of buffer in bytes
OUT PPHYSICAL_ADDRESS LogicalAdress Address of the common buffer in the DMA controller's logical space
IN BOOLEAN CacheEnabled TRUE - memory is cacheable
FALSE - memory is not cached
Return value Non-NULL - VA of common buffer
FALSE - error

Besides allocating the common buffer, AllocateCommonBuffer also allocates map registers (if required) and sets up a translation for the device, loading map registers as necessary. Thus, the buffer is available for immediate and continuous use. The buffer remains usable until FreeCommonBuffer is explicitly invoked, typically in the handler routine for IRP_MN_ STOP_DEVICE.

Using Common Buffer Slave DMA to Maintain Throughput

Common buffer slave DMA is useful if the driver can't afford to set up mapping registers for each data transfer that it performs. The overhead of setting up mapping registers using MapTransfer can be significant, especially for ISA buses. There is always the possibility that MapTransfer will be forced to copy the transfer buffer into the lowest 16 MB of RAM clearly an expensive proposition. Since common buffers are guaranteed to be accessible by their associated DMA devices, there is never a need to move data from one place to another.

For example, drivers of some ISA-based tape drives need to maintain very high throughput if they want to keep the tape streaming. They may not be able to do this if the buffer copy must occur during a call to MapTransfer. To prevent this, the driver can use a ring of common buffers for the actual DMA operation. Other, less time-critical portions of the driver move data between these common buffers and the actual user buffers.

Consider the operation of the driver for a hypothetical ISA output device. To maintain a high DMA data rate, it uses a series of common buffers that are shared between the driver's Dispatch and DpcForIsr routines. The Dispatch routine copies user-output data into an available common buffer and attaches the buffer to a queue of pending DMA requests. Once a DMA is in progress, the DpcForIsr removes buffers from the queue and processes them as fast as it can. Figure 12.6 shows the organization of this driver, and the various driver routines are described in the sections that follow.

Figure 12.6. Using common buffers to improve I/O throughput.
graphics/12fig06.gif
AddDevice ROUTINE

Besides creating the device object, this routine should set the DO_ BUFFERED_IO bit in the Flags field. Even though DMA is used for the actual device transfer, the user buffers are initially copied into system space.

IRP_MN_START_DEVICE HANDLER

Besides the usual responsibilities of initializing the physical device, the handler must now perform the following:

  1. Two queues should be initialized in the device extension. One holds a list of free common buffers. The other is for work requests in progress.

  2. Two spin locks should be created to guard each queue. The spin lock for the work list also protects a flag in the device extension called DmaInProgress.

  3. IoGetDmaAdapter is used to find the adapter object associated with the device. Using the count of mapping registers returned by this function is helpful to determine the size of the common buffers.

  4. Individual common buffers should be allocated, using AllocateCommonBuffer once for each buffer. Initially they should be placed in the free list of the device extension.

  5. Finally, the Start handler initializes a semaphore object and sets its initial count to the number of common buffers it has just created.

DISPATCH ROUTINE

The Dispatch routine of this driver is somewhat uncommon. The Dispatch routine is responsible for queuing and starting each request. This is what the Dispatch routine does to process an output request:

  1. It calls KeWaitForSingleObject to wait for the Semaphore object associated with the driver's list of free buffers. The thread issuing the call will suspend until there is at least one buffer in the queue. (Chapter 14 explains the details of Semaphore use.)

  2. The Dispatch routine removes an available common buffer from the free list and uses RtlMoveMemory to fill it with data from the user's buffer.

  3. It prevents the I/O Manager from completing the request by calling IoMarkIrpPending.

  4. Next, it acquires the spin lock associated with the queue of active requests. As a side-effect, acquiring the spin lock raises IRQL up to DISPATCH_LEVEL. After it owns the spin lock, the Dispatch routine adds the new request to the list of buffers to be output.

  5. Still holding the spin lock, the Dispatch routine checks an internal DmaInProgress flag to see if other parts of the driver are already performing an output operation. If the flag is TRUE, it simply releases the spin lock. If the flag is FALSE, the Dispatch routine sets it to TRUE and starts the device. It then releases the spin lock.

  6. Finally, it returns a value of STATUS_PENDING.

At this point, the work request for this buffer has been either started or queued. The next phase of the transfer occurs within the Start I/O routine.

START I/O ROUTINE

If the device is idle, the Start I/O function is called to start it. It performs the following tasks:

  1. It removes the first request from the work queue and saves its address in the Device Extension as the current request.

  2. It programs the device's DMA registers to point to the dequeued buffer.

  3. The device is started and DMA transfer begins.

INTERRUPT SERVICE ROUTINE

As with packet-based DMA, the ISR in a common buffer driver for a slave device merely saves hardware status in the Device Extension. It then calls IoRequestDpc to request the DpcForIsr routine.

DpcForIsr ROUTINE

In this driver, the DpcForIsr routine sets up each additional work request after the first.

  1. It calls FlushAdapterBuffers to flush any data from the system DMA controller's hardware cache.

  2. It attempts to dequeue the next I/O request from the work queue. If there is another request, the driver makes it the new current request and restarts the device. Otherwise, it clears the DmaInProgress flag in the Device Extension.

  3. Next, it moves the just-completed work buffer back in the free queue. KeReleaseSemaphore is used to signal an increase in the number of available free buffers.

  4. Finally, the IRP is marked as complete.

Each completed DMA operation causes another interrupt that brings the driver back through the DpcForIsr routine. This loop continues until all the requests in the work queue have been processed.

IRP_MN_STOP_DEVICE HANDLER

When the device is stopped, the driver should ensure that the device will no longer attempt use of the common buffer. Once the device is quiescent, the Stophandler calls FreeCommonBuffer to release the memory associated with the ring of buffers.

< BACK  NEXT >


The Windows 2000 Device Driver Book(c) A Guide for Programmers
The Windows 2000 Device Driver Book: A Guide for Programmers (2nd Edition)
ISBN: 0130204315
EAN: 2147483647
Year: 2000
Pages: 156

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net