I have written somewhat working driver for mem2mem for buffer copy without device tree using only kernel dma api as below (without error handling),
Tested on beaglebone black rev C
# uname -r
5.4.242
#include <linux/module.h>
#include <linux/init.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
void *src, *dst;
struct dma_chan *pChan;
static int dmaTransfer(const void *pSrc, void *pDst, unsigned int len);
static int dmaTransfer(const void *pSrc, void *pDst, unsigned int len)
{
enum dma_status status = DMA_ERROR;
struct dmaengine_unmap_data *pUmData;
unsigned char align;
unsigned int alignLen;
struct page *pPage;
unsigned long pgOff;
struct dma_device *pDev;
struct dma_async_tx_descriptor *pTx = NULL;
dma_addr_t dmaSrcAddr, dmaDstAddr;
dma_cookie_t dmaCookie;
pr_info("dma4-module dmaTransfer: before dst: %d, src: %d", *(int *)dst, *(int *)src);
dma_cap_mask_t mask;
dma_cap_zero(mask);
dma_cap_set(DMA_MEMCPY, mask);
align = 0;
alignLen = len;
alignLen = (alignLen >> align) << align;
if (alignLen == 0) {
alignLen = 1 << align;
}
pChan = dma_request_channel(mask, NULL, NULL);
pr_info("dma4-module dmaTransfer: dma channel name : %s", dma_chan_name(pChan));
pDev = pChan->device;
pUmData = dmaengine_get_unmap_data(pDev->dev, 2, GFP_KERNEL);
pPage = virt_to_page(pSrc);
pgOff = offset_in_page(pSrc);
pUmData->addr[0] = dma_map_page(pDev->dev, pPage, pgOff, pUmData->len, DMA_TO_DEVICE);
dmaSrcAddr = pUmData->addr[0];
dma_mapping_error(pDev->dev, pUmData->addr[0]);
pPage = virt_to_page(pDst);
pgOff = offset_in_page(pDst);
pUmData->addr[1] = dma_map_page(pDev->dev, pPage, pgOff, pUmData->len, DMA_FROM_DEVICE);
dmaDstAddr = pUmData->addr[1];
dma_mapping_error(pDev->dev, pUmData->addr[1]);
pTx = pDev->device_prep_dma_memcpy(pChan, dmaDstAddr, dmaSrcAddr, alignLen, DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
dmaCookie = pTx->tx_submit(pTx);
dma_submit_error(dmaCookie);
status = dma_sync_wait(pChan, dmaCookie);
dmaengine_terminate_sync(pChan);
if (status != DMA_COMPLETE) {
switch (status) {
case DMA_IN_PROGRESS:
pr_info("dma4-module dmaTransfer: DMA in progres...");
break;
case DMA_PAUSED:
pr_info("dma4-module dmaTransfer: DMA paused...");
break;
case DMA_ERROR:
pr_info("dma4-module dmaTransfer: DMA error");
break;
default:
;
}
}
else {
pr_info("dma4-module dmaTransfer: DMA transfer complete...");
}
pr_info("dma4-module dmaTransfer: after dst: %d, src: %d", *(int *)dst, *(int *)src);
dmaengine_unmap_put(pUmData);
dmaengine_terminate_sync(pChan);
dma_release_channel(pChan);
return 0;
}
static int __init mod_init(void)
{
int *writer;
pr_info("dma4-module: in init\n");
src = kzalloc(16,GFP_KERNEL);
dst = kzalloc(16,GFP_KERNEL);
writer = (int *)src;
*writer = 65;
pr_info("dma4-module mod_init: before dst: %d, src: %d", *(int *)dst, *(int *)src);
dmaTransfer(src, dst, 16);
pr_info("dma4-module mod_init: after dst: %d, src: %d", *(int *)dst, *(int *)src);
pr_info("dma4-module mod_init: init done\n");
return 0;
}
static void __exit mod_exit(void)
{
pr_info("dma4-module mod_exit: mod_exit called\n");
kfree(src);
kfree(dst);
pr_info("dma4-module mod_exit: released channel");
}
module_init( mod_init );
module_exit( mod_exit );
MODULE_AUTHOR("Mrigendra.Chaubey@gmail.com");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("DMA engine api/memcpy test.");
Above module sometimes work, and sometimes it doesn’t work. Log for same,
[ 471.278017] dma4-module: in init
[ 471.281548] dma4-module: before dst: 0, src: 65
[ 471.281577] edma 49000000.edma: private_candidate: wrong capabilities
[ 471.286149] edma 49000000.edma: EER0 00000000
[ 471.286168] edma 49000000.edma: Got eDMA channel 20 for virt channel 0 (SW trigger)
[ 471.286180] dmaengine: __dma_request_channel: success (dma1chan0)
[ 471.286186] dma4-module: dma channel name : dma1chan0
[ 471.286233] edma 49000000.edma: vchan 8d3312c7: txd be193a83[9]: submitted
[ 471.291413] edma 49000000.edma: first transfer starting on channel 20
[ 471.291426] edma 49000000.edma: ESR0 00000000
[ 471.291448] edma 49000000.edma: EER0 00000000
[ 471.291461] edma 49000000.edma: Transfer completed on channel 20
[ 471.291487] dma4-module: DMA transfer complete...
[ 471.291505] edma 49000000.edma: EER0 00000000
[ 471.296242] edma 49000000.edma: EER0 00000000
[ 471.296254] edma 49000000.edma: Free eDMA channel 20 for virt channel 0
[ 471.296262] dma4-module: after dst: 0, src: 65 <<-- value is not updated in dst buffer
[ 471.296265] dma4-module: init done
rmmod’d module and ten again insmoded
[ 638.845004] dma4-module: in init
[ 638.848295] dma4-module: before dst: 0, src: 65
[ 638.848324] edma 49000000.edma: private_candidate: wrong capabilities
[ 638.853117] edma 49000000.edma: EER0 00000000
[ 638.853139] edma 49000000.edma: Got eDMA channel 20 for virt channel 0 (SW trigger)
[ 638.853152] dmaengine: __dma_request_channel: success (dma1chan0)
[ 638.853159] dma4-module: dma channel name : dma1chan0
[ 638.853202] edma 49000000.edma: vchan 8d3312c7: txd 2c0b7c5f[11]: submitted
[ 638.858297] edma 49000000.edma: first transfer starting on channel 20
[ 638.858309] edma 49000000.edma: ESR0 00000000
[ 638.858330] edma 49000000.edma: EER0 00000000
[ 638.858343] edma 49000000.edma: Transfer completed on channel 20
[ 638.858371] dma4-module: DMA transfer complete...
[ 638.858390] edma 49000000.edma: EER0 00000000
[ 638.863206] edma 49000000.edma: EER0 00000000
[ 638.863219] edma 49000000.edma: Free eDMA channel 20 for virt channel 0
[ 638.863228] dma4-module: after dst: 65, src: 65 <<-- correct value updated in dst buffer
[ 638.863232] dma4-module: init done
Any idea why sometimes I get correct buffer values (65) and sometimes don’t?
One more question how dma1chan0 becomes channel 20, is there a logic for it?