关于pcie dma传输的问题

小弟最近在做pcie dma传输的程序,现在碰到一个很奇怪的问题。程序要实现对dma传输
12m的数据。程序轮训传输12m数据,但程序传输了几次12m数据后,dma传输函数会返回ApiDmaInProgress dma。
程序:

int Dma(PLX_DEVICE_OBJECT *pDevice, char *pUserBuffer, int UserBufferLen, int wr_addr)
{

U8                DmaChannel;
U16               ChannelInput;
PLX_STATUS        rc;
PLX_DMA_PROP      DmaProp;
PLX_DMA_PARAMS    DmaParams;
PLX_PHYSICAL_MEM  PciBuffer;
int               count = 0;
int               len = 0;

int cpld_idea = 0;
int data_value = 0;
int dma_count = 0; 
int retry_times = 0;
int ret = 0;
char            *buffer = NULL; 
int             j = 0; 
int             offset = 0; 


ChannelInput = 0; 
DmaChannel = (U8)ChannelInput;

buffer = (char *)malloc(4096); 
if(buffer == NULL) 
{
    Cons_printf("xbuffer malloc memory err\n"); 
    return ERR_MALLOC_MEM_ERR; 
}

count = UserBufferLen / 4096; 
offset = UserBufferLen % 4096;
// Get DMA buffer parameters
//ret = Read_Cpld_Idea(pDevice, &cpld_idea);
rc = PlxPci_DeviceReset(pDevice);
if (rc != ApiSuccess)
{
    Cons_printf("*ERROR* - API failed to reset\n");
    ret = ERR_OPEN_DMA_FAIL;
}
//cpld_idea = 0;
//ret = Read_Cpld_Idea(pDevice, &cpld_idea);
rc = PlxPci_CommonBufferProperties(pDevice,&PciBuffer);
if (rc != ApiSuccess)
{
    Cons_printf("*ERROR* - API failed\n");
    ret = ERR_OPEN_DMA_FAIL;
}
// Clear DMA structure
memset(&DmaProp, 0, sizeof(PLX_DMA_PROP));
// Initialize the DMA channel
DmaProp.LocalBusWidth = 3;   // 32-bit
DmaProp.ReadyInput    = 1;
DmaProp.ConstAddrLocal = 1; 

rc =PlxPci_DmaChannelOpen(pDevice,DmaChannel,&DmaProp);
if (rc != ApiSuccess)
{
    Cons_printf("*ERROR* - API failed\n");
    ret = ERR_OPEN_DMA_FAIL;
}

// Clear DMA data
for(j = 0; j < count + 1; j++) 
{
    Delay_Us(50); 
    if(j == count)
    {
        len = offset;
    }
    else
    {
        len = 4096; 
    }

    memset(buffer, 0, 4096);
    memcpy(buffer, pUserBuffer + j * 4096, 4096); 

    memset(&DmaParams, 0, sizeof(PLX_DMA_PARAMS));
    // Clear DMA data
    memset(&DmaParams, 0, sizeof(PLX_DMA_PARAMS));

    DmaParams.UserVa    = (PLX_UINT_PTR)buffer;
    DmaParams.LocalAddr = wr_addr;
    DmaParams.ByteCount = len;
    DmaParams.Direction = PLX_DMA_PCI_TO_LOC;
    // Specify a timeout to let API perform wait
    //ASCIIToHex(buffer, len); 
    rc =PlxPci_DmaTransferUserBuffer(pDevice,DmaChannel,&DmaParams,0);         

    while(1)
    {
        do
        {
            rc = PlxPci_DmaStatus(pDevice, 0); 
            retry_times++;
        }
        while (rc == ApiDmaInProgress && retry_times <= 1000000); 

        switch (rc)
        {
            case ApiDmaDone:
                //Cons_printf("Ok (DMA ApiDmaDone)\n");
                ret = 0;
                break;

            case ApiDmaInProgress:
                Cons_printf("*ERROR* -DMA ApiDmaInProgress\n");
                ret = ERR_DMA_ALWAYS_INPROGESS;
                goto FAIL;

            default:
                Cons_printf("*ERROR* - API failed\n");
                ret = ERR_DMA_OTHER_ERR;
                goto FAIL ;
        }
        break; 
    } 
    ret = Read_Dma_Count(pDevice, &dma_count);
    if(ret)
    { 
        Cons_printf("Read_Dma_Count\n");
        goto FAIL;      
    }
    //cpld_idea = 0;
    //ret = Read_Cpld_Idea(pDevice, &cpld_idea);

    ret = Read_Dma_To_Fpga_Data(pDevice, &data_value);
    if(ret)
    { 
        Cons_printf("Read_Dma_Count\n");
        goto FAIL;      
    }

}

if(UserBufferLen != dma_count)
{ 
    Cons_printf("UserBufferLen = %d, count = %d\n", UserBufferLen, dma_count);
    ret = ERR_DMA_TRANSLATE_ERR;
    goto FAIL;          
}

FAIL:
// Close DMA Channel
//Cons_printf(" Close DMA Channel.............. ");
rc =PlxPci_DmaChannelClose(pDevice,DmaChannel);
if (rc != ApiSuccess)
{
Cons_printf("*ERROR* - API failed\n");
PlxPci_DeviceReset(pDevice);
ret = ERR_CLOSE_DMA_FAIL;
}
PlxPci_DmaChannelClose(pDevice,DmaChannel);
free(buffer);

return ret;

}

https://www.baidu.com/link?url=sXHs5umtUj2HCbAFdeRgO91QxRRoJRFuSIBt15VKGOMiESpzXs3gssXw0rLwVMjGlPPvK1ZqRwJhFnBEBVqBdjKf53gk2I6Imoc8ZUXd8kq&wd=&eqid=ea32bb47000288480000000558b597a6

根据您提供的代码和描述,可能是由于 DMA 传输过程中出现了错误导致函数返回 ApiDmaInProgress 错误码。以下是一些可能的解决方法:

  1. 确认 DMA 传输参数是否正确设置。例如,检查 DMA 缓冲区地址、传输数据长度等参数是否符合规范。可以使用调试工具观察这些参数的值,并与硬件手册进行对比。
  1. 确认 DMA 通道是否正确配置。例如,检查 DMA 模式、总线宽度等参数是否正确设置。可以使用调试工具观察这些参数的值,并与硬件手册进行对比。
  1. 在 DMA 传输过程中添加适当的延时以避免硬件错误。例如,在每次传输完成后添加 10 毫秒的延迟,可以减少硬件错误的概率。

下面是一个简单的 DMA 传输示例,其中使用的是 Linux 内核自带的 DMA API。该示例可以在 DMA 通道上进行循环传输,并检查传输结果是否正确。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>

#define BUF_SIZE (12 * 1024 * 1024)
#define DMA_CHAN 0

int main()
{
    int fd, ret;
    char *buf;
    dma_addr_t phy_addr;
    struct dma_chan *chan;
    struct dma_device *dev;
    struct dma_async_tx_descriptor *desc;
    struct scatterlist sg;
    int i;

    // 分配缓冲区
    buf = (char *)malloc(BUF_SIZE);
    if (!buf) {
        perror("Failed to allocate memory");
        return -1;
    }

    // 获取 DMA 设备
    fd = open("/dev/dma", O_RDWR);
    if (fd < 0) {
        perror("Failed to open DMA device");
        free(buf);
        return -1;
    }
    dev = dma_get_device_from_fd(fd);

    // 获取 DMA 通道
    chan = dma_request_channel(dev, "dma");
    if (!chan) {
        perror("Failed to request DMA channel");
        dma_release_device(dev);
        free(buf);
        close(fd);
        return -1;
    }

    // 映射 DMA 缓冲区
    phy_addr = dma_map_single(dev->dev, buf, BUF_SIZE, DMA_TO_DEVICE);
    if (dma_mapping_error(dev->dev, phy_addr)) {
        perror("Failed to map DMA buffer");
        dma_release_channel(chan);
        dma_release_device(dev);
        free(buf);
        close(fd);
        return -1;
    }

    // 循环进行 DMA 传输
    for (i = 0; i < 10000; i++) {
        memset(buf, i % 256, BUF_SIZE);

        sg_init_one(&sg, buf, BUF_SIZE);
        desc = dmaengine_prep_slave_single(chan, phy_addr, BUF_SIZE, DMA_MEM_TO_DEV, 0);
        if (!desc) {
            perror("Failed to prepare DMA transfer");
            dma_unmap_single(dev->dev, phy_addr, BUF_SIZE, DMA_TO_DEVICE);
            dma_release_channel(chan);
            dma_release_device(dev);
            free(buf);
            close(fd);
            return -1;
        }
        dmaengine_sg_init(desc, &sg, 1);
        ret = dma_submit_error(dmaengine_submit(desc));
        if (ret) {
            perror("Failed to submit DMA transfer");
            dma_unmap_single(dev->dev, phy_addr, BUF_SIZE, DMA_TO_DEVICE);
            dma_release_channel(chan);
            dma_release_device(dev);
            free(buf);
            close(fd);
            return -1;
        }
        dma_async_issue_pending(chan);

        ret = dma_sync_wait(desc, dma_async_tx_callback, NULL);
        if (ret) {
            perror("DMA transfer failed");
            dma_unmap_single(dev->dev, phy_addr, BUF_SIZE, DMA_TO_DEVICE);
            dma_release_channel(chan);
            dma_release_device(dev);
            free(buf);
            close(fd);
            return -1;
        }

        printf("DMA transfer %d OK\n", i);
    }

    // 取消映射 DMA 缓冲区
    dma_unmap_single(dev->dev, phy_addr, BUF_SIZE, DMA_TO_DEVICE);
}