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;
}