Direct Memory Access

Direct Memory Access, or DMA, is the advanced topic that completes our overview of memory issues. 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.

To exploit the DMA capabilities of its hardware, the device driver needs to be able to correctly set up the DMA transfer and synchronize with the hardware. Unfortunately, because of its hardware nature, DMA is very system-dependent. Each architecture has its own techniques to manage DMA transfers, and the programming interface is different for each. The kernel can’t offer a unified interface, either, because a driver can’t abstract too much from the underlaying hardware mechanisms. In this chapter, I describe how DMA works with ISA devices and PCI peripherals, as these are currently the most common peripheral interface architectures.

However, I won’t go into much detail about ISA. The ISA implementation of DMA is unnecessarily complicated and not often used in modern peripherals. Nowadays, the ISA bus is used mainly for dumb peripheral interfaces, as hardware manufacturers who need DMA capability tend to use the PCI bus.

Overview of a DMA Data Transfer

Before introducing the programming details, let’s review how a DMA transfer takes place, considering only input transfers to simplify the discussion.

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 can be summarized as follows:

  • 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 it’s done.

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

Sometimes DMA is used asynchronously. This happens, for example, with those data acquisition devices that go on pushing data even if nobody is reading it. In this case, the driver should maintain a buffer so that a subsequent read call will return all the accumulated data to user space. The steps involved in this kind of transfer are slightly different:

  • 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 it’s done.

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

The processing steps in both cases above emphasize that efficient DMA handling relies on interrupt reporting. While it is possible to implement DMA with a polling driver, it wouldn’t make sense, as a polling driver would waste the performance benefits that DMA offers over the easier processor-driven I/O.

Another relevant item introduced here is the DMA buffer. To exploit Direct Memory Access, the device driver must be able to allocate a special buffer, suited to DMA. Note that most drivers allocate their buffer at initialization time and use it until shutdown--the word ``allocate'' in the lists above therefore means ``get hold of the previously allocated buffer.''

Allocating the DMA Buffer

The main problem with the DMA buffer is that when it is bigger than one page, it must occupy contiguous pages in physical memory, because the device transfers data using the ISA or PCI system bus, both of which carry physical addresses. It’s interesting to note that this constraint doesn’t apply to the Sbus (see Section 15.4 in Chapter 15), which uses virtual addresses on the peripheral bus.

Although DMA buffers can be allocated either at system boot or at run time, modules can only allocate their buffers at run time. Chapter 7 introduced these techniques: Section 7.4 talks about allocation at system boot while Section 7.1 and Section 7.2 describe allocation at run time. If you use kmalloc you must specify GFP_DMA priority, bitwise-ORed with either GFP_KERNEL or GFP_ATOMIC.

It is GFP_DMA that requires the memory space to be suitable for DMA transfers. The kernel guarantees that DMA-capable buffers have two features. First, the physical addresses must be consecutive when get_free_page returns more than one page (but this is always true, independent of GFP_DMA, because the kernel arranges free memory in clusters of consecutive pages). And second, when GFP_DMA is set, the kernel guarantees that only addresses lower than MAX_DMA_ADDRESS are returned. The macro MAX_DMA_ADDRESS is set to 16MB on the PC, to deal with the ISA constraints described later.

As far as PCI is concerned, there’s no MAX_DMA_ADDRESS limit, and a PCI device driver should avoid setting GFP_DMA when allocating its buffers.

Do-it-yourself allocation

We have seen how get_free_pages (and therefore kmalloc) can’t return more than 128KB (or, more generally, 32 pages) of consecutive memory space. But the request is prone to fail even when the allocated buffer is less than 128KB bytes, because system memory becomes fragmented over time.[34]

When the kernel cannot return the requested amount of memory, or when you need more than 128KB (a common requirement for PCI frame grabbers, for example), an alternative to returning -ENOMEM is to allocate memory at boot time or reserve the top of physical RAM for your buffer. I described allocation at boot time in Section 7.4 in Chapter 7, but it is not available to modules. Reserving the top of RAM is accomplished by passing a mem= argument to the kernel. For example, if you have 32 megs, the argument mem=31M keeps the kernel from using the top megabyte. Your module could later use the following code to gain access to such memory:

dmabuf = vremap( 0x1F00000 /* 31MB */, 0x100000 /* 1MB */);

My own implementation of allocating DMA buffers is available as an allocator.c module (with an accompanying header). You can find a version in the sample files within src/misc-modules; and the latest version is always available from my own ftp site, ftp://ftp.systemy.it/pub/develop. You could also look for the bigphysarea kernel patch, which is meant to accomplish the same goal as my allocator.

Bus Addresses

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

As a matter of fact, the situation is slightly more complicated than that. DMA-based hardware uses bus, rather than physical, addresses. While ISA and PCI addresses are the same as physical addresses on the PC, this is not true for every platform. Sometimes the interface bus is connected through bridge circuitry that maps I/O addresses to different physical 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 * address);
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.

If you look at code that relies on the allocator engine described earlier, you’ll find a sample use of these functions. The code relies also on vremap because of the context:

/* the allocator returns a physical address */
dev->dmabuffer = allocator_allocate_dma(kilobytes, GFP_KERNEL);
/* map it in the virtual address space */
dev->dmavbuffer = vremap(dev->dmabuffer, kilobytes*1024);

/* ... */

/* pass the address to the device */
writel(virt_to_bus(dev->dmavbuffer,
                   dev->registers + DEV_DMA_ADDRESS);

Although they are not related to DMA, the kernel exports two additional functions that perform address conversion and are worth knowing about:

unsigned long virt_to_phys(volatile void * address);
void * phys_to_virt(unsigned long address);

These functions convert virtual addresses to physical addresses; they are needed when the program code needs to talk to a Memory Management Unit (MMU) or other hardware connected to the address lines of the processor. On the PC platform the two pairs of functions accomplish the same task; keeping them as separate functions is nonetheless important, both for code clarity and portability.

DMA for ISA devices

The ISA bus allows for two kinds of DMA transfers: ``native'' DMA uses standard DMA-controller circuitry on the mainboard to drive the signal lines on the ISA bus; ISA-busmaster DMA, on the other hand, is completely controlled by the peripheral device. The latter type of DMA is rarely used and doesn’t deserve discussion here because it is similar to DMA for PCI devices, at least from the driver’s point of view. An example of an ISA busmaster is the 1542 SCSI controller, whose driver is drivers/scsi/aha1542.c in the kernel sources.

As far as ``native'' DMA is concerned, there are three entities involved in a DMA data transfer on the ISA bus:

The 8237 DMA controller (DMAC)

The controller holds information about the DMA transfer, such as the direction, the memory address, and the size of the transfer. It also contains a counter that tracks the status of ongoing transfers. When the controller receives a DMA request signal, it gains control of the bus and drives the signal lines so the device can read or write its data.

The peripheral device

The device must activate the DMA request signal when it’s ready to transfer data. The actual transfer is managed by the DMAC; the hardware device sequentially reads or writes data onto the bus when the controller strobes the device. The device usually raises an interrupt when the transfer is over.

The device driver

The driver has little to do: it provides the DMA controller with the direction, RAM address, and size of the transfer. It also talks to its peripheral to prepare it for transferring the data and responds to the interrupt when the DMA is over.

The original DMA controller used in the PC could manage four ``channels.'' Each channel is associated with one set of DMA registers, so that four devices can store their DMA information in the controller at the same time. Newer PCs contain the equivalent of two DMAC devices:[35] the second controller (the master) is connected to the system processor, and the first (the slave) is connected to channel 0 of the second controller.[36]

The channels are numbered from 0 to 7; channel 4 is not available to ISA peripherals because it is used internally to ``cascade'' the slave controller onto the master. The available channels are thus 0-3 on the slave (the 8-bit channels) and 5-7 on the master (the 16-bit channels[37]). The size of any DMA transfer, as stored in the controller, is a 16-bit number representing the number of bus cycles. The maximum transfer size is therefore 64KB for the slave controller and 128KB for the master.

Since the DMA controller is a system-wide resource, the kernel helps deal with it. It 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.

Registering DMA usage

You should be used to kernel registries--we’ve already seen them for I/O ports and interrupt lines. The DMA channel registry is similar to the others. After <asm/dma.h> has been included, the following functions can be used to obtain and release ownership of a DMA channel:

int request_dma(unsigned int channel, const char *name); 
void free_dma(unsigned int channel);

The channel argument is a number between 0 and 7 or, more precisely, a positive number less than MAX_DMA_CHANNELS. On the PC, MAX_DMA_CHANNELS is defined as 8, to match the hardware. The name argument is a string identifying the device. The specified name appears in the file /proc/dma, which can be read by user programs.

The return value from request_dma is 0 for success and -EINVAL or -EBUSY if there was an error. The former means that the requested channel is out of range, and the latter means that another device is holding the channel.

I recommend that you take the same care with DMA channels as with I/O ports and interrupt lines; requesting the channel at open time is much better than requesting it from init_module. Delaying the request allows some sharing between drivers; for example, your sound card and your analog I/O interface can share the DMA channel, as long as they are not used at the same time.

I also suggest that you request the DMA channel after you’ve requested the interrupt line and that you release it before the interrupt. This is the conventional order for requesting the two resources; following the convention avoids possible deadlocks. Note that every device using DMA needs an IRQ line as well, otherwise it couldn’t signal the completion of data transfer.

In a typical case, the code for open looks like the following, which refers to a hypothetical dad module (DMA Acquisition Device). The dad device as shown uses a fast interrupt handler without support for shared IRQ lines.

int dad_open (struct inode *inode, struct file *filp)
{
    struct dad_device *my_device; 

    /* ... */
    if ( (error = request_irq(my_device.irq, dad_interrupt,
                              SA_INTERRUPT, "dad", NULL)) )
        return error; /* or implement blocking open */

    if ( (error = request_dma(my_device.dma, "dad")) ) {
        free_irq(my_device.irq, NULL);
        return error; /* or implement blocking open */
    }
    /* ... */
    return 0;
}

The close implementation that matches the open just shown looks like this:

void dad_close (struct inode *inode, struct file *filp)
{
    struct dad_device *my_device;

    /* ... */
    free_dma(my_device.dma);
    free_irq(my_device.irq, NULL);
    /* ... */
}

As far as /proc/dma is concerned, here’s how the file looks on a system with the sound card installed:

merlino% cat /proc/dma
 1: Sound Blaster8
 4: cascade

It’s interesting to note that the default sound driver gets the DMA channel at system boot and never releases it. The cascade entry shown is a placeholder, indicating that channel 4 is not available to drivers, as explained previously.

Talking to the DMA controller

After registration, the main part of the driver’s job consists of configuring the DMA controller for proper operation. This task is not trivial, but fortunately the kernel exports all the functions needed by the typical driver.

The driver needs to configure the DMA controller either when read or write is called, or when preparing for asynchronous transfers. This latter task is performed either at open time or in response to an ioctl command, depending on the driver and the policy it implements. The code shown here is the code that is typically called by the read or write device methods.

This subsection provides a quick overview of the internals of the DMA controller so you will understand the code introduced here. If you want to learn more, I’d urge you to read <asm/dma.h> and some hardware manuals describing the PC architecture. In particular, I don’t deal with the issue of 8-bit vs. 16-bit data transfers. If you are writing device drivers for ISA device boards, you should find the relevant information in the hardware manuals for the devices.

The information that must be loaded into the controller is made up of three items: the RAM address, the number of atomic items that must be transferred (in bytes or words), and the direction of the transfer. To this end, the following functions are exported by <asm/dma.h>:

void set_dma_mode(unsigned int channel, char mode);

Indicates whether the channel must read from the device (DMA_MODE_READ) or write to it (DMA_MODE_WRITE). A third mode exists, DMA_ MODE_CASCADE, which is used to release control of the bus. Cascading is the way the first controller is connected to the top of the second, but it can also be used by true ISA bus-master devices. I won’t discuss bus-mastering here.

void set_dma_addr(unsigned int channel, unsigned int addr);

Assigns the address of the DMA buffer. The function stores the 24 least-significant bits of addr in the controller. The addr argument must be a bus address (see Section 13.3.3).

void set_dma_count(unsigned int channel, ,                   unsigned int count);

Assigns the number of bytes to transfer. The count argument represents bytes for 16-bit channels as well; in this case, the number must be even.

In addition to these functions, there are a number of housekeeping facilities that must be used when dealing with DMA devices:

void disable_dma(unsigned int channel);

A DMA channel can be disabled within the controller. The channel should be disabled before the DMAC is configured, to prevent improper operation (the controller is programmed via 8-bit data transfers, and thus none of the previous functions is executed atomically).

void enable_dma(unsigned int channel);

This function tells the controller that the DMA channel contains valid data.

int get_dma_residue(unsigned int channel);

The driver sometimes needs to know if a DMA transfer has been completed. This function returns the number of bytes that are still to be transferred. The return value is 0 after a successful transfer and is unpredictable (but not 0) while the controller is working. The unpredictability reflects the fact that the residue is a 16-bit value, which is obtained by two 8-bit input operations.

void clear_dma_ff(unsigned int channel)

The function clears the DMA flip-flop. The flip-flop is used to control access to 16-bit registers. The registers are accessed by two consecutive 8-bit operations, and the flip-flop is used to select the least-significant byte (when it is clear) or the most-significant byte (when it is set). The flip-flop automatically toggles when 8 bits have been transferred; the programmer must clear the flip-flop once before accessing the DMA registers.

Using these functions, a driver can implement a function like the following to prepare for a DMA transfer:

int dad_dma_prepare(int channel, int mode, unsigned int buf,
                        unsigned int count)
{
    unsigned long flags;

    save_flags(flags);
    cli();
    disable_dma(channel);
    clear_dma_ff(channel);
    set_dma_mode(channel, mode);
    set_dma_addr(channel, virt_to_bus(buf));
    set_dma_count(channel, count);
    enable_dma(channel);
    restore_flags(flags);

    return 0;
}

A function like the next one, then, is used to check for successful completion of DMA:

int dad_dma_isdone(int channel)
{
    return (get_dma_residue(channel) == 0);
}

The only thing that remains to be done is to configure the device board. This device-specific task usually consists of reading or writing a few I/O ports. Devices differ in significant ways. For example, some devices expect the programmer to tell the hardware how big the DMA buffer is, and sometimes the driver has to read a value that is hardwired into the device. For configuring the board, the hardware manual is your only friend.

DMA and PCI Devices

The PCI implementation of DMA is much easier to handle than the ISA version.

PCI supports multiple bus-masters, and DMA reduces to bus-mastering. The device that needs to read or write main memory simply requests control of the bus and then directly controls the electrical signals. The PCI implementation is more elaborate at the hardware level and easier to manage in the device driver.

Programming a DMA transfer with PCI consists of the following steps:

Allocating a buffer

The DMA buffer must be physically contiguous in memory, but there’s no 16MB addressability limit. A call to get_free_pages is sufficient; there’s no need to specify GFP_DMA in the priority. If you really need it, you can resort to the (deprecated) aggressive allocation technique described ealier in Section 13.3.2.

Talking to the device

The expansion device must be told about the DMA buffer. This usually means writing the address and size of the buffer to a few device registers. Sometimes the DMA size is dictated by the hardware device, but the issue is device-dependent. The address passed to PCI devices must be a bus address.

As you can see, there’s no general code used to program DMA for a PCI device. A typical implementation looks like the following one, but every device is different, and the amount of configurable information varies greatly between devices.

int dad_dma_prepare_pci(int mode, unsigned long buf,
                        unsigned int count)
{
    unsigned long addr = virt_to_bus(buf);
        

    writeb(DAD_CMD_DISABLEDMA, DAD_COMMAND);
    writeb(mode, DAD_COMMAND); /* either DAD_CMD_RD or DAD_CMD_WR */
    writel(addr, DAD_DMA_BUFFER);
    writel(count>>2, DAD_DMA_COUNT); /* each transfer is 32 bits */
    writeb(DAD_CMD_ENABLEDMA, DAD_COMMAND);

    return 0;
}


[34] The word fragmentation is usually applied to disks, to express the idea that files are not stored consecutively on the magnetic medium. The same concept applies to memory, where each virtual address space gets scattered throughout physical RAM, and it becomes difficult to retrieve consecutive free pages when a DMA buffer is requested.

[35] These circuits are now part of the motherboard’s chipset, but a few years ago they were two separate 8237 chips.

[36] The original PCs had only one controller; the second was added in 286-based platforms. However, the second controller is connected as the master because it handles 16-bit transfers, while the first transfers only 8 bits at a time and is there for backward-compatibility.

[37] Two bytes are transferred at each bus I/O cycle.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.164.151