Linux Device Drivers

Linux Device Drivers


Linux
Modules
Character drivers
IO & Memory
Linux Kernel
Process Management
Process Address space

Linux Scheduler
Memory Management
Interrupts
Signals
System Calls
Kernel Synchronization
Linux Inter Process Communications




Serial Ports
Parallel Ports
Introduction to Hardware
Linux Timers
DMA in Linux
Linux Threads

Linux Thread Synchronization
Linux Multi Threading
Debugging in Linux
GDB GNU Debugger
KDB Kernel Debugger
KGDB Kernel GNU Debugger
Example Ethernet Driver




DMA

DMA

 

This section covers following topics:

 

Direct Memory Access (DMA) I/O operations, which provide peripherals with direct access to system memory.

 

Direct Memory Access and Bus Mastering

 

DMA is the hardware mechanism that allows peripheral components to transfer their I/O data directly to and from main memory without the need for the system processor to be involved in the transfer.

Use of this mechanism can greatly increase throughput to and from a device, because a great deal of computational overhead is eliminated.

 

Overview of a DMA data transfer

 

Data transfer can be triggered in two ways: either the software asks for data (via a function such as read) or the hardware asynchronously pushes data to the system.

 

In the first case, the steps involved are:

     When a process calls read, the driver method allocates a DMA buffer and instructs the hardware to transfer its data. The process is put to sleep.

     The hardware writes data to the DMA buffer and raises an interrupt when its done.

     The interrupt handler gets the input data, acknowledges the interrupt and awakens the process, which is now able to read data.

 

The second case comes about when DMA is used asynchronously:

     The hardware raises an interrupt to announce that new data has arrived.

     The interrupt handler allocates a buffer and tells the hardware where to transfer its data.

     The peripheral device writes the data to the buffer and raises another interrupt when its done.

     The handler dispatches the new data, wakes any relevant process, and takes care of housekeeping.

 

Allocating the DMA Buffer

 

The DMA buffers can be allocated either at system boot or at runtime; modules can only allocate their buffers at runtime.

memory should be allocated from the DMA zone by adding the GFP_DMA flag to the kmalloc or get_free_pages call.

 

Bus Addresses

 

A device driver using DMA has to talk to hardware connected to the interface bus, which uses physical addresses, whereas program code uses virtual addresses.

 

The Linux kernel provides a portable solution by exporting the following functions, defined in <asm/io.h>:

 

unsigned long virt_to_bus(volatile void * addresses);

void * bus_to_virt(unsigned long address);

 

The virt_to_bus conversion must be used when the driver needs to send address information to an I/O device (such as an expansion board or the DMA controller),

 

while bus_to_virt must be used when address information is received from hardware connected to the bus.

 

DMA on the PCI Bus

 

The 2.4 kernel includes a flexible mechanism that supports PCI DMA also known as bus mastering. It handles the details of buffer allocation and can deal with setting up the bus hardware for multipage transfers on hardware that supports them.

The functions require a struct pci_dev structure for the device. The drivers should include <linux/pci.h>.

 

DMA mappings

 

A DMA mapping is a combination of allocating a DMA buffer and generating an address for that buffer that is accessible by the device.

 

Consistent DMA Mapping

 

These exist for the life of the driver. A consistently mapped buffer must be simultaneously available to both the CPU and the peripheral.

The following function handles both the allocation and the mapping of the buffer:

void *pci_alloc_consistent (structpci_dev*pdev, size_tsize,

dma_addr_t*bus_addr);

 

Streaming DMA Mappings

 

These are set up for a single operation. When you have a single buffer to transfer map it with pci_map_single:

dma_addr_t pci_map_single(structpci_dev *pdev, void *buffer,

size_t size, int direction);

 

The return value is the bus address that you can pass to the device,

or NULL if something goes wrong.

 

Scatter-gather mappings

 

Some smart devices can accept a scatterlist of array pointers and lengths and transfer them all in one DMA operation. To map a scatter/gather DMA operation, driver should set the addresses and length fields in a struct scatter list entry for each buffer to be transferred. Then call:

 

int pci_map_sg(structpci_dev*pdev, struct scatterlist *list,

int nents, int direction);

 

The return value will be the number of DMA buffers to transfer.

 

DMA for ISA Devices

 

The ISA bus DMA uses standard DMA controller circuitry on the motherboard to drive the signal lines on the ISA bus. There are three entities involved in a DMA data transfer on the ISA bus: the 8237 DMA controller, the peripheral device and the device driver.

Kernel uses a DMA registry to provide a request and free mechanism for the DMA channels and a set of functions to configure channel information in the DMA controller.

The following functions can be used to obtain and release ownership of a DMA channel:

 

int request_dma(unsigndintchannel, const char *name);

void free_dma(unsigned intchannel);

 

After registration, the main part of the drivers job consists of configuring the DMA controller for proper operation.

 

The kernel exports all the functions needed by the typical driver, e.g.

set_dma_mode,

set_dma_addr,

set_dma_count,

enable_dma, etc.