C语言 如何在Linux中使用DMA从内存传输到设备

sxissh06  于 11个月前  发布在  Linux
关注(0)|答案(1)|浏览(96)

我发现这个代码从互联网上的DMA传输从内存到内存这个代码工作正常

#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");

字符串
但是我需要从内存到设备(spi,uart,...)或设备到内存传输数据的代码如何修改上述代码从内存到设备(spi,uart,...)或设备到内存传输数据?Linux版本是3. 4. 113,Orange Pi One board
我使用“橙子Pi One”板由Linux 3.4.113与“allwinner h3”我发现以下代码从“allwinner h3”文档中的dma部分,此代码从内存传输到设备此代码编译成功,但它不工作,并得到错误“传输错误”

#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");


请帮帮我,谢谢

xxls0lw8

xxls0lw81#

对于这一点,没有 * 设备独立 *,一刀切的DMA引擎代码,原因很简单,DMA传输的设置和管理是特定于每个设备的。有些设备只能在一个小的DMA窗口上操作。其他设备能够在整个总线地址空间上进行分散/聚集操作。有些设备的DMA引擎配置有绝对总线地址,一些设备将它们的DMA描述符存储在本地,其它设备将通过辅助收集操作从主机存储器获取DMA描述符,等等。
底线是,没有办法熟悉特定的目标设备,如何在硬件级别上配置其DMA引擎,然后-如果您打算使用Linux DMA引擎-编写粘合代码,将这些放在一起。
你引用的代码是一个如何使用某些CPU上的内存到内存DMA引擎的例子。基本上,这个东西和memcpy一样,但是没有使用CPU周期,并且完全在总线地址上操作,而不是虚拟地址空间。

相关问题