本文是使用正点原子阿尔法IMX6ULL开发板作为硬件基础编写的用户应用程序和内核驱动程序。
实现了Linux内核中DMA引擎驱动模块的功能,用户程序测试通过。
程序的关键步骤:
1、一是使用内核驱动模块的/dev/sdma_test中的ioctl函数将希望传入数据的
源地址映射到了内核空间,
用户程序中对该地址中的内容所做的更改都会在启动DMA传输后传输到DMA
的目的地址中。可以从内核驱动模块的打印中看出数据正确,完成了一次DMA传输。
2、二是用mmap函数和/dev/mem文件将内核中DMA的目的地址映射到用户空间中,
内核空间中目的地址的数据可以通过映射到用户空间的地址打印出来。DMA的目的地址
是通过dma驱动函数提供的read函数读出来的。如此,DMA的源地址和目的地址在内核
驱动模块程序中和在用户程序中都不一样,但做了映射,可以通过改变用户程序中的
源地址中的数据改变在用户程序中的目的地址中的数据内容。可以通过打印显示出来。
源代码分为:
1、内核驱动模块代码
2、用户程序代码
1、内核驱动模块代码
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/mman.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <linux/fs.h>
#include <linux/version.h>
#include <linux/delay.h>
#include “linux/dmaengine.h”
#include <linux/device.h>
#include <linux/platform_data/dma-imx.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/ide.h>
static int gMajor; /* major number of device */
static struct class *dma_tm_class;
u32 *wbuf, *wbuf2, *wbuf3;
u32 *rbuf, *rbuf2, *rbuf3;
u32 *rxbuf,*txbuf;
struct dma_chan *dma_m2m_chan_rx,*dma_m2m_chan_tx;
struct completion dma_m2m_ok;
struct scatterlist sg_rx[1],sg_tx[1],sg[3], sg2[3];
#define DMA_BUF_SIZE (10246004)
dma_addr_t dma_src;
dma_addr_t dma_dst;
static bool dma_m2m_filter(struct dma_chan *chan, voidparam)
{
if (!imx_dma_is_general_purpose(chan))
{
// printk(“I #################\n”);
return false;
}
chan->private = param;
return true;
}
static void dma_m2m_callback(voiddata)
{
// printk(“in %s\n”,func);
complete(&dma_m2m_ok);
return ;
}
static int imx_dma_init(void)
{
dma_cap_mask_t dma_m2m_mask;
struct imx_dma_data m2m_dma_data = {0};
// struct dma_slave_config dma_m2m_config;
init_completion(&dma_m2m_ok);
dma_cap_zero(dma_m2m_mask);
dma_cap_set(DMA_SLAVE, dma_m2m_mask);
m2m_dma_data.peripheral_type = IMX_DMATYPE_MEMORY;
m2m_dma_data.priority = DMA_PRIO_HIGH;
/tx/
dma_m2m_chan_tx = dma_request_channel(dma_m2m_mask, dma_m2m_filter, &m2m_dma_data);
if (!dma_m2m_chan_tx) {
printk(“Error opening the SDMA memory to memory channel\n”);
return -EINVAL;
}
// dma_m2m_config.direction = DMA_MEM_TO_MEM;
// dma_m2m_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
// dma_m2m_config.dst_addr =*src;
// dmaengine_slave_config(dma_m2m_chan_tx, &dma_m2m_config);
txbuf=kzalloc(DMA_BUF_SIZE, GFP_DMA);
rxbuf=kzalloc(DMA_BUF_SIZE, GFP_DMA);
dma_src = dma_map_single(NULL,txbuf,DMA_BUF_SIZE, DMA_MEM_TO_MEM);
dma_dst = dma_map_single(NULL,rxbuf,DMA_BUF_SIZE, DMA_MEM_TO_MEM);
return 0;
}
static int start_tx_dma(void)
{
// int i;
long start, end;
struct timeval end_time;
struct dma_async_tx_descriptordesc;
struct dma_slave_config dma_m2m_config;
dma_m2m_config.direction = DMA_MEM_TO_MEM;
dma_m2m_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
// dma_m2m_config.dst_addr =src;
dmaengine_slave_config(dma_m2m_chan_tx, &dma_m2m_config);
dma_src = dma_map_single(NULL,txbuf,DMA_BUF_SIZE, DMA_MEM_TO_MEM);
dma_dst = dma_map_single(NULL,rxbuf,DMA_BUF_SIZE, DMA_MEM_TO_MEM);
printk(“dma_src_addr=0x%x\n”,dma_src);
printk(“dma_dst_addr=0x%x\n”,dma_dst);
desc = dma_m2m_chan_tx->device->device_prep_dma_memcpy(dma_m2m_chan_tx,dma_dst,dma_src,DMA_BUF_SIZE,1);
if (!desc) {
pr_err(“We cannot prepare for the tX slave dma!\n”);
return -EINVAL;
}
do_gettimeofday(&end_time);
start = end_time.tv_sec1000000 + end_time.tv_usec;
desc->callback = dma_m2m_callback;
// printk(KERN_ERR “%s:%d submitting for dma\n”,func,LINE);
dmaengine_submit(desc);
dma_async_issue_pending(dma_m2m_chan_tx);
wait_for_completion(&dma_m2m_ok);
do_gettimeofday(&end_time);
end = end_time.tv_sec1000000 + end_time.tv_usec;
printk(“end - start = %ld us\n”, end - start);
// for(i=0; i<DMA_BUF_SIZE; i++){
// printk(“0x%x”,dst[i]);
// }
return 0;
}
int sdma_open(struct inode * inode, struct file * filp)
{
imx_dma_init();
return 0;
}
int sdma_release(struct inode * inode, struct file * filp)
{
// dma_release_channel(dma_m2m_chan_rx);
// dma_m2m_chan_rx = NULL;
dma_release_channel(dma_m2m_chan_tx);
dma_m2m_chan_tx = NULL;
kfree(txbuf);
kfree(rxbuf);
/* kfree(wbuf);
kfree(wbuf2);
kfree(wbuf3);
kfree(rbuf);
kfree(rbuf2);
kfree(rbuf3);*/
return 0;
}
ssize_t sdma_read (struct file *filp, char __user * buf, size_t count, loff_t * offset)
{
u8 databuf[4];
int err = 0;
databuf[0] = dma_dst;
databuf[1] = dma_dst>>8;
databuf[2] = dma_dst>>16;
databuf[3] = dma_dst>>24;
err = copy_to_user(buf, databuf, sizeof(databuf));
if(err < 0) {
printk(“kernel read failed!\r\n”);
return -EFAULT;
}
return 0;
}
static int sdma_mmap(struct file *file, struct vm_area_struct *vma) {
//vma->vm_start是虚拟地址,dma_priv->dma_src >> PAGE_SHIFT是物理地址
if(remap_pfn_range(vma, vma->vm_start, dma_src >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot))
return -EAGAIN;
return 0;
}
static int sdma_write(struct file * filp, const char __user * buf, size_t count, loff_t * offset)
{
start_tx_dma();
return 0;
}
struct file_operations dma_fops = {
.open = sdma_open,
.release = sdma_release,
.read = sdma_read,
.unlocked_ioctl = sdma_write,
.mmap = sdma_mmap
};
int __init sdma_init_module(void)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26))
struct device *temp_class;
#else
struct class_devicetemp_class;
#endif
int error;
/register a character device */
error = register_chrdev(0, “sdma_test”, &dma_fops);
if (error < 0) {
printk(“SDMA test driver can’t get major number\n”);
return error;
}
gMajor = error;
printk(“SDMA test major number = %d\n”,gMajor);
dma_tm_class = class_create(THIS_MODULE, “sdma_test”);
if (IS_ERR(dma_tm_class)) {
printk(KERN_ERR “Error creating sdma test module class.\n”);
unregister_chrdev(gMajor, “sdma_test”);
return PTR_ERR(dma_tm_class);
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28))
temp_class = device_create(dma_tm_class, NULL,
MKDEV(gMajor, 0), NULL, “sdma_test”);
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26))
temp_class = device_create(dma_tm_class, NULL,
MKDEV(gMajor, 0), “sdma_test”);
#else
temp_class = class_device_create(dma_tm_class, NULL,
MKDEV(gMajor, 0), NULL,
“sdma_test”);
#endif
if (IS_ERR(temp_class)) {
printk(KERN_ERR “Error creating sdma test class device.\n”);
class_destroy(dma_tm_class);
unregister_chrdev(gMajor, “sdma_test”);
return -1;
}
printk(“SDMA test Driver Module loaded\n”);
return 0;
}
static void sdma_cleanup_module(void)
{
unregister_chrdev(gMajor, “sdma_test”);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26))
device_destroy(dma_tm_class, MKDEV(gMajor, 0));
#else
class_device_destroy(dma_tm_class, MKDEV(gMajor, 0));
#endif
class_destroy(dma_tm_class);
printk(“SDMA test Driver Module Unloaded\n”);
}
module_init(sdma_init_module);
module_exit(sdma_cleanup_module);
MODULE_AUTHOR(“Freescale Semiconductor”);
MODULE_DESCRIPTION(“SDMA test driver”);
MODULE_LICENSE(“GPL”);
2、用户测试程序代码:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
#include “stdlib.h”
// #include<stdio.h>
// #include<unistd.h>
// #include<sys/mman.h>
// #include<sys/types.h>
// #include<sys/stat.h>
// #include<fcntl.h>
#define SDMA_BUF_SIZE (10246004)
int main(void)
{
unsigned char * map_base;
int fd_mem;
char *virtaddr;
// char string_to_pass[128]={‘s’,‘t’};
char string_to_pass[128]=“string_to_pass+string_to_pass”;
unsigned long addr;
unsigned char content;
int ret,i;
u_int8_t databuf[4];
u_int32_t dst_phy_addr;
system(“insmod dma6.ko”);
int my_dev = open(“/dev/sdma_test”, O_RDWR);
if (my_dev < 0) {
perror(“Fail to open device file: /dev/sdma_test.”);
} else {
ret = read(my_dev, databuf, sizeof(databuf));
if(ret < 0) {
printf(“dst_phy_addr read failed!\r\n”);
}
dst_phy_addr=(u_int32_t)databuf[3]<<24|databuf[2]<<16|databuf[1]<<8|databuf[0];
printf(“dst_phy_addr=0x%x\n”,dst_phy_addr);
virtaddr = (char *)mmap(0, SDMA_BUF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, my_dev, 0);
// memset(string_to_pass,0x33,sizeof(string_to_pass));
strcpy(virtaddr, string_to_pass);
printf(“virtaddr=0x%x\n”,virtaddr);
ioctl(my_dev, NULL);
fd_mem = open(“/dev/mem”, O_RDWR|O_SYNC);
if (fd_mem == -1) {printf(" error!\n"); } map_base = mmap(NULL, SDMA_BUF_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd_mem, (u_int32_t)dst_phy_addr); if (map_base == 0) { printf("NULL pointer!\n"); } else{printf("address: 0x%x",map_base);printf("Successfull!\n"); }printf("\nstring_from_kernel_pass: %s\n",map_base);// for (i=0;i < 0xf; i++) // { // addr = (unsigned long)(map_base + i); // content =*(map_base+i); // printf("address: 0x%lx content 0x%x\t\t", addr, (unsigned int)content); // }close(fd_mem);close(my_dev);munmap(map_base, SDMA_BUF_SIZE); // system("rmmod dma6.ko");}return 0;
}