how transfer from memory to device with dma in linux

148 views Asked by At

i found this code from internet for dma transfering from memory to memory this code work fine

#include <linux/module.h>
#include <linux/init.h>
#include <linux/completion.h>
#include <linux/slab.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>

void my_dma_transfer_completed(void *param)
{
    struct completion *cmp=(struct completion *)param;
    complete(cmp);
    printk("dma transfer completed callback\n");
}

static int __init dma_init (void)
{
    struct dma_device *dma_dev;
    dma_cap_mask_t mask;
    struct dma_chan *chan;
    struct dma_async_tx_descriptor *chan_desc;
    dma_cookie_t cookie;
    dma_addr_t src_addr,dst_addr;
    u8 *src_buf, *dst_buf;
    struct completion cmp;
    int status;
    
    printk(KERN_INFO "Loading module \n");
    
    dma_cap_zero(mask);
    dma_cap_set(DMA_MEMCPY,mask);
    chan=dma_request_channel(mask,NULL,NULL);
    if(!chan)
    {
        printk("chan request error\n");
        return ENODEV;
    }
    dma_dev = chan->device;
    
    src_buf=dma_alloc_coherent(chan->device->dev,1024,&src_addr,GFP_KERNEL);
    dst_buf=dma_alloc_coherent(chan->device->dev,1024,&dst_addr,GFP_KERNEL);

    memset(src_buf,0x12,1024);
    memset(dst_buf,0x00,1024);
    
    printk("Before %x\n",src_buf[0]);
    printk("Before %x\n",dst_buf[0]);

    chan_desc=dma_dev->device_prep_dma_memcpy(chan,dst_addr,src_addr,1024,DMA_MEM_TO_MEM);
    if(!chan_desc)
    {
        printk("chan desc request error\n");
        status=-1;
        goto free;
    }
    init_completion(&cmp);
    chan_desc->callback=my_dma_transfer_completed;
    chan_desc->callback_param=&cmp;
    cookie=dmaengine_submit(chan_desc);
    
    dma_async_issue_pending(chan);
    if(wait_for_completion_timeout(&cmp,msecs_to_jiffies(3000)) <= 0)
    {
        printk("timout\n");
        status=-1;

    }
    status=dma_async_is_tx_complete(chan,cookie,NULL,NULL); 
    if(status==DMA_SUCCESS)
    {
        printk("complete %d\n",status);
        status=0;
        printk("After %x\n",src_buf[0]);
        printk("After %x\n",dst_buf[0]);
    }
    else
    {
        printk("transfer error\n");
    }
    dmaengine_terminate_all(chan);
    
free:
    dma_free_coherent(chan->device->dev,1024,src_buf,src_addr);
    dma_free_coherent(chan->device->dev,1024,dst_buf,dst_addr);
    
    dma_release_channel(chan);
    return 0; 
} 


static void __exit dma_exit (void) 
{
    printk(KERN_INFO "Exitingg module \n");
}

module_init(dma_init);
module_exit(dma_exit);

MODULE_AUTHOR("Saeed Setareh");
MODULE_DESCRIPTION("DMA");
MODULE_LICENSE("GPL");

but i need code for transfer data from memory to device(spi,uart,...) or device to memory how to modify above code for transfer data from memory to device(spi,uart,...) or device to memory? the linux version is 3.4.113,Orange Pi One board

i use "Orange Pi One" board by linux 3.4.113 with "allwinner h3" soc I found the following code from the "allwinner h3" documentation in the dma section,this code transfer from memory to device this code compile successfully, but it not work and get error "transfer error"

#include <linux/module.h>
#include <linux/init.h>
#include <linux/completion.h>
#include <linux/slab.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/dma/sunxi-dma.h>


void my_dma_transfer_completed(void *param)
{
    struct completion *cmp=(struct completion *)param;
    complete(cmp);
    printk("dma transfer completed callback\n");
}

static int __init dma_init (void)
{
    struct dma_chan *chan;
    dma_cap_mask_t mask; 
    dma_cookie_t cookie;
    struct dma_slave_config config; 
    struct dma_async_tx_descriptor *tx = NULL; 
    void *src_buf;
    struct completion cmp;
    dma_addr_t src_dma;
    int status;

    printk(KERN_INFO "Loading module \n");

    dma_cap_zero(mask); 
    dma_cap_set(DMA_SLAVE, mask); 
    dma_cap_set(DMA_CYCLIC, mask);

    chan = dma_request_channel(mask, NULL, NULL); 
    if (!chan)
    {
        printk("Request channel error\n");
        return -EINVAL;
    }

    src_buf = kmalloc(1024*4, GFP_KERNEL); 
    if (!src_buf) 
    {
        printk("kmalloc error\n");
        dma_release_channel(chan); 
        return -EINVAL;
    }

    src_dma = dma_map_single(NULL, src_buf, 1024*4, DMA_TO_DEVICE);

    config.direction = DMA_MEM_TO_DEV; 
    config.src_addr = src_dma; 
    config.dst_addr = 0x01c;
    config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 
    config.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 
    config.src_maxburst = 1;
    config.dst_maxburst = 1;
    config.slave_id = sunxi_slave_id(DRQDST_AUDIO_CODEC, DRQSRC_SDRAM);

    dmaengine_slave_config(chan, &config);

    tx = dmaengine_prep_dma_cyclic(chan, src_dma, 1024*4, 1024, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
    if(!tx)
    {
        printk("chan desc request error\n");
        status=-1;
    }
    init_completion(&cmp);
    tx->callback = my_dma_transfer_completed; 
    tx->callback = NULL;

    cookie = dmaengine_submit(tx); 
    dma_async_issue_pending(chan);
    if(wait_for_completion_timeout(&cmp,msecs_to_jiffies(3000)) <= 0)
    {
        printk("timout\n");
        status=-1;

    }
    status=dma_async_is_tx_complete(chan,cookie,NULL,NULL); 
    if(status==DMA_SUCCESS)
    {
        printk("complete %d\n",status);
        status=0;
    }
    else
    {
        printk("transfer error\n");
    }
    dmaengine_terminate_all(chan);
    return 0; 
} 


static void __exit dma_exit (void) 
{
    printk(KERN_INFO "Exitingg module \n");
}

module_init(dma_init);
module_exit(dma_exit);

MODULE_AUTHOR("Saeed Setareh");
MODULE_DESCRIPTION("Low Level Driver");
MODULE_LICENSE("GPL");

please help me,thanks

1

There are 1 answers

0
datenwolf On

There's no device independent, one-size-fits-all DMA engine code for this, for the simple reason, that how DMA transfers are set up and managed are specific to each device. Some devices can operate only on a small DMA window. Others are capable of scatter/gather operations across the whole bus address space. Some devices have their DMA engine configured with absolute bus addresses, other devices use a combination of page size shift, page offset and page index. Some devices have their DMA descriptors stored locally, other devices will fetch DMA descriptors through an auxiliary gather operation from host memory, etc., etc.

The bottom line is, there's no way around familiarizing with the specific target device in question, how to configure its DMA engine on the hardware level, and then – if you intend to use the Linux DMA Engine – write glue code that will bring those together.

The code you're cited is an example for how to use the memory-to-memory DMA engine found on some CPUs. Basically this thing does the same as a memcpy, but without using CPU cycles for that, and operating entirely on bus addresses, instead of virtual address space.