Xilinx ZYNQMP DMA Client 代码

本文详细介绍了XilinxZYNQMP平台上的DMAClient驱动代码,涉及GPIO控制、DMA数据传输、DMA通道管理以及用户空间接口,展示了如何使用该驱动进行DMA数据从DMA控制器到应用程序空间的传输。
摘要由CSDN通过智能技术生成

Xilinx ZYNQMP DMA Client 代码

驱动代码:

#include "linux/device.h"
#include "linux/gfp.h"
#include "linux/gpio.h"
#include "linux/gpio/consumer.h"
#include "linux/kernel.h"
#include "linux/ktime.h"
#include "linux/printk.h"
#include "linux/scatterlist.h"
#include "linux/stddef.h"
#include <asm/uaccess.h>
#include <linux/dma/xilinx_dma.h>
#include <linux/cdev.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/ktime.h>

#include "dma_user.h"
#define MODULE_NAME "DMA_Client"

/*
 *  module name:DMA_Client
 *  Auhor:Swing
 *  Data:2023/12/20
 *  Describe:
 *      This is a driver module for fetching data from the DMA controller; 
 *      user space directly transfers the data from the PS side to the 
 *    application space via DMA using address mapping.
 *
 *  Update: 
 *  A0 Date:2023/12/20
*/

//gpio registered, io num is 511 ,  this reset
//gpio registered, io num is 510 ,  this start
#define RESET_GPIO      511
#define START_GPIO      510
#define DMA_START_TRANS     1
#define DMA_STOP_TRANS      0
// #define BD_CNT          1
#define MAX_TRANS_BYTE      4194304 //(1024 * 1024 *4)    //4M字节
#define DMA_SET_TRANS_GPIO(x,v)   gpio_set_value(x->start_gpio,v)
#define DMA_RST_TRANS_GPIO(x)   gpio_set_value(x->reset_gpio,DMA_STOP_TRANS); \
                                gpio_set_value(x->reset_gpio,DMA_START_TRANS)
#define DMA_COMPILE_FLAG    (DMA_CTRL_ACK | DMA_PREP_INTERRUPT)
// #define DMA_START_TRANS_GPIO(x) x?gpio_set_value(gpio_data->start_gpio,DMA_START_TRANS):gpio_set_value(gpio_data->start_gpio,DMA_STOP_TRANS)
struct xilinx_dma_dev
{
	struct platform_device *pdev;
	struct mutex mutex;
	int value;
    /* DMA stuff */
	struct dma_chan *rx_chan;
	dma_cookie_t rx_cookie;
	struct completion rx_cmp;
	unsigned long rx_tmo;
	// int bd_cnt;
	struct scatterlist rx_sg;
    struct dma_async_tx_descriptor *rxd;
	/*DMA address of buffer */
	dma_addr_t dma_dsts;
	u8 *dsts;
	int dma_len_bytes;
};

struct xilinx_dma_gpio{
    unsigned int reset_gpio;    
    unsigned int start_gpio;    
};
struct xilinx_dma_client{
    struct xilinx_dma_dev *dma_dev_p;
    struct xilinx_dma_gpio *dma_gpio_p;

	dev_t dev_node;
	struct cdev cdev;
	struct class *class_p;
};


/*
    描述:用于初始化GPIO
    传入一个空struct xilinx_dma_gpio 类型即可
*/
static int dma_gpio_init(struct xilinx_dma_gpio *gpio_data){
    int ret;
    gpio_data->reset_gpio = RESET_GPIO;
    gpio_data->start_gpio = START_GPIO;
    
    // gpio_data->reset_descs = gpio_descs(gpio_data->reset_gpio);
    // gpio_data->start_descs = gpio_descs(gpio_data->start_gpio);
    ret = gpio_request(gpio_data->reset_gpio,"DMA_Reset");
    if(ret == -EPROBE_DEFER){
		printk("gpio request faild");
        return ret;
	}
    ret = gpio_request(gpio_data->start_gpio,"DMA_Start");
    if(ret == -EPROBE_DEFER){
		printk("gpio request faild");
        gpio_free(gpio_data->reset_gpio);
        return ret;
	}
    gpio_direction_output(gpio_data->reset_gpio, 1);
    gpio_direction_output(gpio_data->start_gpio, DMA_STOP_TRANS);

    //以下为复位操作
    gpio_set_value(gpio_data->reset_gpio,0);
    //usleep(1000); //1ms
    gpio_set_value(gpio_data->reset_gpio,1);
    //DMA数据源复位完毕
    gpio_set_value(gpio_data->start_gpio,DMA_STOP_TRANS);

    //把GPIO导出在在class
    gpio_export(gpio_data->reset_gpio, true);
    gpio_export(gpio_data->start_gpio, true);
    ret = 0;
    return ret;
}

static int dma_gpio_free(struct xilinx_dma_gpio *gpio_data){
    gpio_set_value(gpio_data->reset_gpio, DMA_STOP_TRANS);
    gpio_set_value(gpio_data->start_gpio, DMA_STOP_TRANS);
    gpio_free(gpio_data->reset_gpio);
    gpio_free(gpio_data->start_gpio);
    return 0;
}
static void dma_slave_rx_callback(void *completion)
{
	complete(completion);
}

static int dma_start_trans(struct xilinx_dma_client *dma_device_client)
{
    struct xilinx_dma_client *client = dma_device_client;
    struct xilinx_dma_dev *dma_dev = client->dma_dev_p;
    struct xilinx_dma_gpio *dma_gpio = client->dma_gpio_p;
    struct platform_device *pdev = client->dma_dev_p->pdev;
    int rx_tmo = 0 ,status = 0;
    dma_async_issue_pending(dma_dev->rx_chan);
    DMA_SET_TRANS_GPIO(dma_gpio,DMA_START_TRANS);
    rx_tmo = wait_for_completion_timeout(&dma_dev->rx_cmp,
	           dma_dev->rx_tmo);
    // dma_stop_trans_gpio(dma_gpio);
    DMA_SET_TRANS_GPIO(dma_gpio,DMA_STOP_TRANS);
    status = dma_async_is_tx_complete(dma_dev->rx_chan,
                                        dma_dev->rx_cookie, NULL, NULL);
    if(rx_tmo == 0){
        if(status == DMA_IN_PROGRESS)
            dev_info(&pdev->dev,"dma trans time out,curren,curren status is IN_PROGRESS");
        else if(status == DMA_PAUSED)
            dev_info(&pdev->dev,"dma trans time out,curren,curren status is PAUSED");
        else if(status == DMA_ERROR)
            dev_info(&pdev->dev,"dma trans time out,curren,curren status is ERROR");
        return -status;
    }

    dev_info(&pdev->dev,"dma trans is COMPILE");
    return 0;
}


/*********************禁止屎山代码!!!!*********/
static int dma_driver_init(struct xilinx_dma_client *dma_device_client,unsigned int size)
{
    struct xilinx_dma_client *client = dma_device_client;
    struct xilinx_dma_dev *dma_dev = client->dma_dev_p;
    // struct xilinx_dma_gpio *dma_gpio = client->dma_gpio_p;
    struct platform_device *pdev = client->dma_dev_p->pdev;
    struct scatterlist *rx_sg = &dma_dev->rx_sg;
    struct dma_device *rx_dev = dma_dev->rx_chan->device;
    dma_dev->dma_len_bytes = size;
    /**********************分配内存*******************/
    dma_dev->dsts = dma_alloc_coherent(&pdev->dev, dma_dev->dma_len_bytes, &dma_dev->dma_dsts, GFP_KERNEL);
    if(!dma_dev->dsts || !dma_dev->dma_dsts){
        dev_info(&pdev->dev,"dma alloc fail");
        return -1;
    }
    /***********************上传DMA传输的地址*******************/
    sg_init_one(rx_sg, dma_dev->dsts, dma_dev->dma_len_bytes);
    sg_dma_address(rx_sg) = dma_dev->dma_dsts;
    sg_dma_len(rx_sg) = dma_dev->dma_len_bytes;
    dma_dev->rxd = rx_dev->device_prep_slave_sg(dma_dev->rx_chan, rx_sg, 1,
                                        DMA_DEV_TO_MEM, DMA_COMPILE_FLAG, NULL);
    /***********************设置DMA的描述符并提交*******************/
    if (!dma_dev->rxd)
    {
        dev_err(&dma_dev->pdev->dev, "rxd is NULL\n");
        dma_free_coherent(&pdev->dev,dma_dev->dma_len_bytes,dma_dev->dsts,dma_dev->dma_dsts);
    }
    init_completion(&dma_dev->rx_cmp);

    dma_dev->rxd->callback = dma_slave_rx_callback;
    dma_dev->rxd->callback_param = &dma_dev->rx_cmp;
    dma_dev->rx_cookie = dma_dev->rxd->tx_submit(dma_dev->rxd);
    if (dma_submit_error(dma_dev->rx_cookie))
    {
        dev_err(&dma_dev->pdev->dev, "dma submit error\n");
    }
    /**********************设置超时时间************************/
    dma_dev->rx_tmo = msecs_to_jiffies(10000); 
    return 0;
}

static int dma_open(struct inode *node, struct file *file)
{
    struct xilinx_dma_client *client;
    file->private_data = container_of(node->i_cdev,struct xilinx_dma_client,cdev);
    client = (struct xilinx_dma_client *)(file->private_data);
    return 0;
}

static long dma_ioctl(struct file *file, unsigned int cmd, unsigned long udata)
{
    struct xilinx_dma_client *client = (struct xilinx_dma_client *)(file->private_data);
    // struct xilinx_dma_dev *dma_dev = client->dma_dev_p;
    struct xilinx_dma_gpio *dma_gpio = client->dma_gpio_p;
    struct platform_device *pdev = client->dma_dev_p->pdev;
    struct xilinx_dma_info dma_info;
    ktime_t trans_start_time,trans_end_time;
    int ret;
    int user_data;

    switch(cmd)
    {
        case ZHA_DMA_DMA_INIT:
            ret = copy_from_user(&user_data,(int *)udata,sizeof(user_data));
            if(ret){
                dev_info(&pdev->dev,"copy from user fail");
                return -ret;
            }
            if(user_data >= MAX_TRANS_BYTE){
                dev_info(&pdev->dev,"set max size:%d,current set size is:%d,",MAX_TRANS_BYTE,MAX_TRANS_BYTE);
                user_data = MAX_TRANS_BYTE;
            }
            if(dma_driver_init(client,user_data)){
                dev_info(&pdev->dev,"dma driver init fail");
                return -1;
            }
            break;
        case ZHD_DMA_START:
            if((void *)udata == NULL)
                ret = dma_start_trans(client);
            else
            {
                trans_start_time = ktime_get();
                ret = dma_start_trans(client);
                trans_end_time = ktime_get();
                dma_info.use_time = trans_end_time - trans_start_time;
                dma_info.trans_status = ret;
                if(copy_to_user((struct xilinx_dma_info *)udata,
                &dma_info,sizeof(struct xilinx_dma_info))){
                    dev_info(&pdev->dev,"copy from user fail");
                    return -1;
                }
            }
            return ret == 0 ? 0 : -1;
            break;
        case ZHD_DMA_RESET_PL:
            DMA_RST_TRANS_GPIO(dma_gpio);
            break;

    }
    return 0;
}
static int dma_mmap(struct file *file, struct vm_area_struct *vm_area)
{
    struct xilinx_dma_client *client = (struct xilinx_dma_client *)(file->private_data);
    return dma_mmap_coherent(&client->dma_dev_p->pdev->dev, vm_area,
                    client->dma_dev_p->dsts, client->dma_dev_p->dma_dsts,
                    vm_area->vm_end - vm_area->vm_start);
    
}
static int dma_release(struct inode *node, struct file *file)
{
    struct xilinx_dma_client *client = (struct xilinx_dma_client *)(file->private_data);
    struct xilinx_dma_dev *dma_dev = client->dma_dev_p;
    struct xilinx_dma_gpio *dma_gpio = client->dma_gpio_p;
    struct platform_device *pdev = client->dma_dev_p->pdev;
    dma_free_coherent(&pdev->dev, dma_dev->dma_len_bytes, dma_dev->dsts, dma_dev->dma_dsts);
    // dma_stop_trans_gpio(dma_gpio);
    DMA_SET_TRANS_GPIO(dma_gpio, DMA_STOP_TRANS);
    return 0;
}

static struct file_operations dma_operations = 
{
    .open = dma_open,
    .compat_ioctl = dma_ioctl,
    .unlocked_ioctl = dma_ioctl,
    .mmap = dma_mmap,
    .release = dma_release,
};

static int dma_probe(struct platform_device *pdev)
{
    struct xilinx_dma_dev *dma_dev;
    struct xilinx_dma_gpio *gpio_data_probe;
    struct xilinx_dma_client *dma_client;
    int ret;
    /*******************************申请使用的内存***********************************/
    dma_dev = devm_kzalloc(&pdev->dev, sizeof(struct xilinx_dma_dev), GFP_KERNEL);
    if(dma_dev == NULL){
        dev_info(&pdev->dev,"dma device malloc fail");
        return -1;
    }
    gpio_data_probe = devm_kzalloc(&pdev->dev, sizeof(struct xilinx_dma_gpio), GFP_KERNEL);
    if(gpio_data_probe == NULL){
        dev_info(&pdev->dev,"dma gpio malloc fail");
        return -1;
    }
    dma_client = devm_kzalloc(&pdev->dev, sizeof(struct xilinx_dma_client), GFP_KERNEL);
    if(dma_client == NULL){
        dev_info(&pdev->dev,"dma gpio malloc fail");
        return -1;
    }

    if(dma_gpio_init(gpio_data_probe)){
        dev_info(&pdev->dev,"dma gpio init fail");
        return -1;
    }
    
    dma_client->dma_dev_p = dma_dev;
    dma_client->dma_gpio_p = gpio_data_probe;
    dma_dev->pdev = pdev;

    /*******************************创建设备节点***********************************/
    ret = alloc_chrdev_region(&dma_client->dev_node, 0, 1, MODULE_NAME);
    if(ret)
    {
        dev_info(&pdev->dev,"register chrdev fail");
        return -1;
    }
    cdev_init(&dma_client->cdev, &dma_operations);
	dma_client->cdev.owner = THIS_MODULE;
	ret = cdev_add(&dma_client->cdev, dma_client->dev_node, 1);
    if(ret){
        unregister_chrdev_region(dma_client->dev_node,1); //创建失败,释放掉前面的设备号
        dev_info(&pdev->dev,"create cdev fail");
        return -1;
    }
    dma_client->class_p = class_create(THIS_MODULE,MODULE_NAME);
    if(IS_ERR(dma_client->class_p))
    {
        dev_info(&pdev->dev,"create class fail");
        cdev_del(&dma_client->cdev);//创建失败,释放掉前面的注册的用户操作接口
        unregister_chrdev_region(dma_client->dev_node,1);//创建失败,释放掉前面的设备号
        return -1;
    }
    device_create(dma_client->class_p,NULL,dma_client->dev_node,dma_client,MODULE_NAME);
    platform_set_drvdata(pdev, dma_client);
    /******************************DMA通道申请以及初始化*********************************/
    dma_dev->rx_chan = dma_request_slave_channel(&pdev->dev, "dma_proxy_rx_only");
    if(dma_dev->rx_chan == NULL)
        printk("dma request slave channel fail\n");

    return 0;
}


static int dma_remove(struct platform_device *pdev)
{
    struct xilinx_dma_client *client;
    struct xilinx_dma_gpio *dma_gpio;
    struct xilinx_dma_dev *dma_dev;
    client = (struct xilinx_dma_client *)(platform_get_drvdata(pdev));
    dma_gpio = client->dma_gpio_p;
    dma_dev = client->dma_dev_p;
    dma_gpio_free(dma_gpio);
    dma_release_channel(dma_dev->rx_chan);

    device_destroy(client->class_p, client->dev_node);
    class_destroy(client->class_p);
    cdev_del(&client->cdev);
    unregister_chrdev_region(client->dev_node,1);
    return 0;
}

static const struct of_device_id dma_of_ids[] =
{
	{.compatible = "xlnx,dma_proxy",},
};


static struct platform_driver dma_of_driver =
{
	.driver = {
		.name = MODULE_NAME,
		.owner = THIS_MODULE,
		.of_match_table = dma_of_ids,
	},
	.probe = dma_probe,
	.remove = dma_remove,
};

module_platform_driver(dma_of_driver);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("ZYNQ DMA Driver");
MODULE_AUTHOR("Swing");
MODULE_VERSION("A0");

xilinx应用测试demo代码

#include <stdio.h>
#include <error.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <pthread.h>
#include <time.h>
#include <errno.h>
#include "dma_user.h"
#include <sys/ioctl.h>
#define DEVICE_PWD	"/dev/DMA_Client"

int main()
{
    int fd,ret;
    int size = 1024 * 1024 *4;
    void *point = NULL;
    struct xilinx_dma_info trans_info;
    double speed_result;
    fd = open(DEVICE_PWD,O_RDWR | O_NOCTTY);
    if(fd < 0){
        printf("open fail,reason:%s\n",strerror(errno));
        return -1;
    }
    //void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
    ret = ioctl(fd,ZHD_DMA_RESET_PL);
    ret = ioctl(fd,ZHA_DMA_DMA_INIT,&size);
    printf("cmd ZHA_DMA_DMA_INIT :return:%d,reason:%s\n",ret,strerror(errno));

    point = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    point += 1;
    ret = ioctl(fd,ZHD_DMA_START,&trans_info);
    printf("cmd ZHD_DMA_START return:%d,reason:%s\n",ret,strerror(errno));
    for(int i = 0;i < 100;i++){//一个数据占用4字节
        int data = 0;
        data = (((unsigned char *)point)[i*4 + 0] << 24) |
                (((unsigned char *)point)[i*4 + 1] << 16) |
                (((unsigned char *)point)[i*4 + 2] << 8) |
                (((unsigned char *)point)[i*4 + 3] << 0);
        printf("%p:%d\n",point + 4*i,data);
    }
    speed_result = (double)size / (double)trans_info.use_time * 1000000000;
    speed_result = speed_result / (1024*1024);
    printf("Trans size:%d Byte,trans status:%s\ntrans use time:%lld us,trans speed:%.02lf MB/s\n",
        size,trans_info.trans_status ? "trans fail" : "trans success",
        trans_info.use_time,speed_result);
    close(fd);
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值