DMA programming
DMA is a hardware mechanism that requires no CPU to participate in two-way data transfer between peripherals and system memory, using DMA to get the system CPU out of the actual I/O data transfer process, thus greatly improving the throughput rate of the system.
DMA mode of data transmission is controlled by the DMA controller, during transmission, the CPU can perform other tasks concurrently, when the DMA end, the DMA controller through the interrupt notification CPU data transfer has ended, The CPU then executes the corresponding interrupt service program for post-processing. DMA can be used as a way to transfer data between memory and peripherals, and in this way, the data does not need to be transferred by the CPU.
1. DMA and Cache consistency
Assuming that the DMA has no overlap between the destination address of the memory and the cached object, there is no problem between the DMA and the cache, but if the destination address of the DMA is overlapped with the memory address access cached by the cache, the data for the cache memory corresponding to the cache has been modified by the DMA operation. The CPU itself does not know that it still considers the data in the cache to be data in memory, and when it accesses the cache mapped memory later, it still uses stale cache data, which has the problem of data inconsistency between cache and memory.
The simplest way to resolve cache consistency problems due to DMA is to directly prohibit the cache function of memory in the DMA target address range
DMA is just one way for peripherals to interact with memory, and an area of memory used to interact with the peripheral data is called a DMA buffer.
Normal DMA operations can only be performed in memory below 16MB, so use the GFP_DMA flag when requesting a DMA buffer using Kmalloc () and __get_free_pages () and its similar functions, which guarantees that the obtained memory is located in the Dma_zone, is DMA capable.
Need to be aware of virtual addresses, physical addresses, and bus addresses when using DMA zones
DMA-based hardware uses the bus address instead of the physical address, which is the memory address seen from the device perspective, and the physical address is the memory address seen from the peripheral angle of the CPU MMU controller (virtual address seen from the CPU core). Although on a PC, the bus address is the physical address for both ISA and PCI, not every platform. DMA-related IOMMU, similar to the MMU, except that the IOMMU is for conversions between the peripheral bus address and the memory address
Consistent DMA Buffers
The DMA mapping consists of two tasks: allocating a DMA buffer to generate a device-accessible address for this buffer. Also, the DMA mapping page must consider the cache consistency issue, which provides the following functions for allocating a DMA-consistent memory area:
void* dma_alloc_coherent (struct device* dev,size_tsize,dma_addr_t* handle,gfp_y GFP);
The return value of this function is the virtual address of the requested DMA buffer, and in addition, the function returns the bus address of the DMA buffer via the parameter handle, the handle type is dma_addr_t, which represents the bus address
Dma_alloc_coherent () Request a DMA buffer for address mapping and to ensure cache consistency for this buffer
Void dma_free_coherent (struct device* dec,size_tsize,void* cpu_addr,dam_addr_t handle);
The kernel also provides a function pci_alloc_consitent () for the PCI device device requesting the DMA buffer, and the prototype is:
void* pci_alloc_consistent (struct pci_dev*pdev,size_t size,dma_addr_t* DAM_ADDRP);
Streaming DMA Buffers
Not all DMA buffers are driver requests, if the driver request, with a consistent DMA buffer natural most convenient, directly consider the cache consistency problem, but the buffer from the upper layer of the kernel, the upper layer is likely to use the normal Kmalloc () _get_free_pages ( ) and so on, it is time to use streaming DMA mapping. The general steps for using a streaming DMA buffer are as follows:
Perform Streaming DMA mapping
Performing DMA operations
Perform streaming DMA de-mapping
The kernel provides DMA-related functions
In some architectures, streaming mappings can also have multiple disjoint pages and multiple "scatter/aggregate" buffers. When you create a streaming map, you must tell the direction of the flow of the kernel data.
Dma_to_device
Device_to_dma
If the data is sent to the device, use Dma_to_device, and if the data is sent to the CPU, use DEVICE_TO_DMA.
Dma_bidirecttonal
This value is used if the data can be moved in both directions
Dma_none
The symbol is for debugging purposes only.
When only one buffer is to be transmitted, use the following function to map it:
dma_addr_t dma_map_single (struct device *dev, void *buffer, Size_tsize, enum dma_data_direction direction);
The return value is the bus address, which can be passed to the device, or null if an error is executed.
When the transfer is complete, use the following function to delete the mappings:
void Dma_unmap_single (struct device *dev, dma_addr_t dma_addr,size_t size, enum dma-data_direction direction);
The principle of using streaming DMA:
One is that the buffer can only be used for such a transmission, that is, its transmission direction matching and mapping when the given direction value;
The second is that once the buffer is mapped, it will belong to the device, not the processor. The driver cannot access its contents in any way until the buffer is unmapped. Only when the Dma_unmap_single function is called, the data in the processor cache is refreshed and the driver is able to access its contents securely.
Third, in the DMA for an active period, can not undo the mapping of the buffer, or it will seriously damage the stability of the system.
What if the buffer to be mapped is in a memory segment (high-end memory) that the device cannot access? Some architectures produce only one error, but some other system structures create a rebound buffer. A rebound buffer is a separate area in memory that can be accessed by the device. If you use the DMA_TO_DEVICE flag to map a buffer and you need to use a rebound buffer, the contents of the initial buffer are copied as part of the mapping operation. Obviously, after the copy, the initial buffer content changes are not visible to the device. The same DEVICE_TO_DMA bounce buffer is copied back to the original buffer by the Dma_unmap_single function, that is, until the copy operation is complete, the data from the device is available.
Sometimes the driver needs to access the contents of the streaming DMA buffer without having to go through the undo mapping, which provides the following call to the kernel:
void dma_sync_single_for_cpu (struct device *dev, dma_handle_tbus_addr, size_t size, enum dma_data_directction direction );
This function should be called before the processor accesses the streaming DMA buffer. Once the function is called, the processor "owns" the DMA buffer and can access it as needed. Then, before the device accesses the buffer, the following function should be called to return ownership to the device:
void Dma_sync_single_for_device (struct device *dev, dma_handle_tbus_addr, size_t size, enum dma_data_direction direction);
Again, the processor can no longer access the DMA buffer after calling the function.
Requesting and releasing DMA channels
As with interrupts, before using DMA, the device driver needs to request the DMA channel from the system first, requesting the DMA channel function:
int REQUEST_DMA (unsigned int dmanr,const char*device_id);
Device structure pointer can be used as the best parameter for incoming device_id
After using the DMA channel, you need to release
Void FREE_DMA (unsigned int dmanr);
DMA-related code flow in Linux device drivers:
1, REQUEST_DMA () and initialize the DMAC request DMA buffer (in the device driver module load or open () function)
2. DMA transfer (in function functions such as write () read () IOCTL ())
3, shut up can have the corresponding interrupt, DMA transmission after the interrupt processing (in the interrupt handler)
4. Release the DMA buffer FREE_DMA () (in the device driver module unload or Relese () function)
DMA programming under Linux Driver Development--linux