小弟最近在做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;
}
根据您提供的代码和描述,可能是由于 DMA 传输过程中出现了错误导致函数返回 ApiDmaInProgress 错误码。以下是一些可能的解决方法:
下面是一个简单的 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);
}