系统相关
首页 > 系统相关> > zynq操作系统: Linux驱动开发AXIDMA篇

zynq操作系统: Linux驱动开发AXIDMA篇

作者:互联网

前言

   由于bram形式的速率限制,在同样紧急的时间条件下,还是改回了axidma的方式来降维打击,对于几兆的速率,颇有种杀鸡用牛刀的感觉,没办法,原来的刀就是差一点,牛刀好用是好用但是终究得提升一点内功
裸机下的DMA相对是比较简单的,参考之前裸板对于DMA的操作,而对于LINUX下,只能说苦不堪言。先不谈如何实现用户空间的零拷贝DMA传输,光是Linux环境下的DMA传输就已经感觉比较棘手,一方面是对Linux了解不够深入,另一方面则是Linux在相关的使用说明方面的确没有比较好的官方支持。

   Xilinx提供了一个AXI-DMA的IP核,其可以通过AXI-Lite进行配置,命令其从AXI高性能总线(HP)上直接的对内存数据进行读取存储,这一切在PS使用裸机时感觉是那么的简单,调用库函数对DMA配置好起始、结束地址、传输大小及相关的即可甚至有官方例程可以参考。但是这一切到linux上则变得狰狞起来。复杂的基于DMA engine 的操作机制使得刚开始上手在zynq上使用linux系统操作AXI-DMA变得不那么简洁明了,而且对于刚接触Linux不到一个月的人来说,wtf杀了我得了。

   水平较差的时候为了“避免”繁杂的linux下dma engine的操作,参考了有人想到了是否可以只把AXI-DMA这个IP核的寄存器(挂载在AXI-Lite总线上)通过mmap的方式映射到内存中,然后像之前裸机上一样,( bram不就是这么做的吗 ,只能说配的寄存器少无数倍),对这块内存读写就直接配置AXI-DMA寄存器,完成了对AXI-DMA的配置操作(参考这个:https://forums.xilinx.com/t5/Embedded-Linux/AXI-DMA-with-Zynq-Running-Linux/m-p/522755?advanced=false&collapse_discussion=true&q=linux%20axi%20dma&search_type=thread)
   但是,其也不可避免的从内核空间通过copy_to_usr来拷贝数据到用户空间,在大批量的数据时,这是很缓慢的一个过程。幸好,有一个开源项目xilinx_axidma,实现了从用户空间使用AXI-DMA的零拷贝,并且将其封装为了库,
(https://github.com/bperez77/xilinx_axidma/tree/master)
   有篇文章主要就是记录如何使用这个库的
   https://blog.csdn.net/sements/article/details/90230188
   实现形式大同小异,我并没有选择SD卡启动的形式,直接从ddr起使用的默认的文件系统

1 - 准备工作

   下载xilinx_axidma源文件: https://github.com/bperez77/xilinx_axidma/tree/master,并好好看看它的README
   已编译过的linux kernel,用于生成model(或者也可以用petalinux的module方式自己将xilinx_axidma添加进去,在petalinux生成时会自动编译生成module下面将采取这种形式)

2 - 建立petalinux工程

   建立一个petalinux工程,设置根文件系统从ddr载入(默认),使用本地linux,主要也是为了更方便之后对内核的修改。(在petalinux多编译篇有写)
在这里插入图片描述

3 - 配置Linux内核

   这里面需要确保DMA相关项开启。一般如果vivado工程中含有AXI-DMA 的IP核,在petalinux-config -c kernel的时候会发现基本相关项都已经开启。起码2018.2版本是已经开启的
   这里推荐一个上篇文章介绍到的小技巧,我们在menuconfig中选保存,自己定一个保存名(例如自己的名字_defconfig),保存一下,不要退出,去你petalinux工程项目文件下搜索这个文件名,将其复制出来,按照github上的要求检查以下项目是否选y了(删除线的不需要检查,这个库是17年写的,但是现在xilinx的linux代码分支已经使用到2018,这些相关配置项已经不在了)

CONFIG_CMA=y
CONFIG_DMA_CMA=y
CONFIG_XILINX_DMAENGINES=y
CONFIG_XILINX_AXIDMA=y
CONFIG_XILINX_AXIVDMA=y

CONFIG_DMA_SHARED_BUFFER=y
记得,在menuconfig中再选保存,将文件名命名回.config,以供petalinux正确生成linux
DMA相关设置完毕后,我们还需要配置CMA
Device Drivers -> Generic Driver Options -> Default contiguous memory area size 的 Size in Mega Bytes修改为25

4 – Uboot采取petalinux生成的

5 - 设备树的修改

   先运行一下生成pl相关的设备树(这是可选项,只是为了方便修改dtsi时看看pl.dtsi里的节点名)
   $ petalinux-config -c device-tree
   修改设备树的主要有两个点:1.加入axidma_chardev 2.修改各个dma通道的device-id不重复。
   我这里有两个dma通道(一个发到FIFO,一个从FIFO接回来),我把他们的device-id分别修改为0和1
   在project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi中加入

/include/ "system-conf.dtsi"
/{
};
 
&amba_pl{
    axidma_chrdev: axidma_chrdev@0 {
            compatible = "xlnx,axidma-chrdev";
            dmas = <&axi_dma_0 0 &axi_dma_0 1>;
            dma-names = "tx_channel", "rx_channel";
    };
};
 
&axi_dma_0{
    dma-channel@40400000 {
        xlnx,device-id = <0x0>;
    };
    dma-channel@40400030 {
        xlnx,device-id = <0x1>;
    };
};

   这里使用的设备树的引用覆盖的方法来修改device-id。

6- 编译可加载的模块

  参考编译篇对模块的添加,如命名为xilinx-axidma-modules,
petalinux-create -t modules --name xilinx-axidma-modules –enable,
修改Makefile(也忘记原版是啥了,反正重点还是检查设备名和.o文件)

DRIVER_NAME = xilinx-axidma-modules
$(DRIVER_NAME)-objs = axi_dma.o axidma_chrdev.o axidma_dma.o axidma_of.o
obj-m := $(DRIVER_NAME).o

SRC := $(shell pwd)

all:
	$(MAKE) -C $(KERNEL_SRC) M=$(SRC)

modules_install:
	$(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules_install

clean:
	rm -f *.o *~ core .depend .*.cmd *.ko *.mod.c
	rm -f Module.markers Module.symvers modules.order
	rm -rf .tmp_versions Modules.symvers

  修改.bb文件

SUMMARY = "Recipe for  build an external xilinx-axidma Linux kernel module"
SECTION = "PETALINUX/modules"
LICENSE = "GPLv2"
LIC_FILES_CHKSUM = "file://COPYING;md5=12f884d2ae1ff87c09e5b7ccc2c4ca7e"

inherit module

SRC_URI = "file://Makefile \
	   file://axi_dma.c \
	   file://axidma.h \
	   file://axidma_chrdev.c \
	   file://axidma_dma.c \
	   file://axidma_of.c \
	   file://axidma_ioctl.h \
	   file://COPYING \
          "

S = "${WORKDIR}"

# The inherit of module.bbclass will automatically name module packages with
# "kernel-module-" prefix as required by the oe-core build environment.

  然后编译模块,petalinux-build -c xilinx-axidma-modules,可能因为版本不同会有一个函数传递参数的数量不对,需要修改,还有超时时间如果想配置成无线阻塞型也可以修改超时时间,后话了

7- 编译应用测试demo

  先编译一下他自己的demo吧,然后再加以修改,原来的transfer例程是用来本地回环搬运两个文件的
petalinux-create -t apps --name axidmaapp –enable
  依然先修改Makefile


APP = axidmaapp

# Add any other object files to this list below
APP_OBJS = axidmaapp.o util.o demo.o gpioapp.o

all: build

build: $(APP)

$(APP): $(APP_OBJS)
	$(CC) $(LDFLAGS) -o $@ $(APP_OBJS) $(LDLIBS) -lpthread

  和.bb文件

#
# This file is the axidmaapp recipe.
#

SUMMARY = "Simple axidmaapp application"
SECTION = "PETALINUX/apps"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"

SRC_URI = "file://axidmaapp.c \
        file://demo.c \
		file://util.c \
		file://util.h \
		file://conversion.h \
	    file://axidmaapp.h \
		file://axidma_ioctl.h \
		file://gpioapp.h \
		file://gpioapp.c \
	   file://Makefile \
		  "

S = "${WORKDIR}"

do_compile() {
	     oe_runmake
}

do_install() {
	     install -d ${D}${bindir}
	     install -m 0755 axidmaapp ${D}${bindir}
}

  注意查看.bb文件里的文件一定要包含在内,gpioapp两个文件是后来用于挂载gpio中断的可以不用,axidmaapp是因为需要有一个同名文件还是怎么的编译原因,将libaxidma的两个文件改名得到
  编译app后整体build
  烧进板子里就可以测试demo了,如果PL测没有问题的话 ,会很成功的将自己新建a文档的内容copy到b文档
  接下来就是我们对他功能的修改了,实际使用肯定不可能本地回环,需要将收发独立,封装新的函数,调用时统一在kernel里映射收发的空间
下面是

更新后的驱动头文件

/**
* @file axidmaapp.c
 * @date Saturday, March 20, 2021 at 10:24:11 AM EST
 * @author Brandon Perez (bmperez)
 * @author Jared Choi (jaewonch)
 * @author HanXin(update)
 *
 * This file defines the interface to the AXI DMA device through the AXI DMA
 * library.
 **/

#ifndef AXIDMAAPP_H_
#define AXIDMAAPP_H_

#include "axidma_ioctl.h"   // Video frame structure

/*----------------------------------------------------------------------------
 * Internal Definitions update by xin.han
 *----------------------------------------------------------------------------*/
#define XPAR_BRAM_0_BASEADDR   0x42000000
#define DMA_0_BASEADDR   0x40400000
unsigned char *map_base0;
unsigned char *map_base1;
/**
 * The struct representing an AXI DMA device.
 *
 * This is an opaque type to the end user, so it can only be used as a pointer
 * or handle.
 **/
struct axidma_dev;

/**
 * Type definition for an AXI DMA device.
 *
 * This is a pointer to an opaque struct, so the user cannot access any of the
 * internal fields.
 **/
typedef struct axidma_dev* axidma_dev_t;

/**
 * A structure that represents an integer array.
 *
 * This is used to give the channel id's to the user in a convenient fashion.
 **/
typedef struct array {
    int len;        ///< Length of the array
    int *data;      ///< Pointer to the memory buffer for the array
} array_t;

/**
 * Type definition for a AXI DMA callback function.
 *
 * The callback function is invoked on completion of an asynchronous transfer,
 * if requested by the user. The library will pass the channel id of the DMA
 * channel that has finished, and the generic data the user registered.
 **/
typedef void (*axidma_cb_t)(int channel_id, void *data);

/**
 * Initializes an AXI DMA device, returning a handle to the device.
 *
 * There is only one AXI DMA device, since it represents all of the available
 * channels. Thus, this function should only be invoked once, unless a call has
 * been made to #axidma_destroy. Otherwise, this function will abort.
 *
 * @return A handle to the AXI DMA device on success, NULL on failure.
 **/
struct axidma_dev *axidma_init();

/**
 * Tears down and destroys an AXI DMA device, deallocating its resources.
 *
 * @param[in] dev An #axidma_dev_t returned by #axidma_init.
 **/
void axidma_destroy(axidma_dev_t dev);

/**
 * Gets the available AXI DMA transmit channels, returning their channel ID's.
 *
 * In our terminology, the "transmit" direction is defined as from the processor
 * to the FPGA. This function is guaranteed to never fail.
 *
 * @param[in] dev An #axidma_dev_t returned by #axidma_init.
 * @return An array of channel ID's of the available AXI DMA transmit channels.
 **/
const array_t *axidma_get_dma_tx(axidma_dev_t dev);

/**
 * Gets the available AXI DMA transmit channels, returning their channel ID's.
 *
 * In our terminology, the "receive" direction is defined as from the FPGA to
 * the processor. This function is guaranteed to never fail.
 *
 * @param[in] dev An #axidma_dev_t returned by #axidma_init.
 * @return An array of channel ID's of the available AXI DMA receive channels.
 **/
const array_t *axidma_get_dma_rx(axidma_dev_t dev);

/**
 * Gets the available AXI VDMA transmit channels, returning their channel ID's.
 *
 * In our terminology, the "transmit" direction is defined as from the processor
 * to the FPGA. This function is guaranteed to never fail.
 *
 * @param[in] dev An #axidma_dev_t returned by #axidma_init.
 * @return An array of channel ID's of the available AXI VDMA transmit channels.
 **/
const array_t *axidma_get_vdma_tx(axidma_dev_t dev);

/**
 * Gets the available AXI VDMA receive channels, returning their channel ID's.
 *
 * In our terminology, the "receive" direction is defined as from the FPGA to
 * the processor. This function is guaranteed to never fail.
 *
 * @param[in] dev An #axidma_dev_t returned by #axidma_init.
 * @return An array of channel ID's of the available AXI VDMA receive channels.
 **/
const array_t *axidma_get_vdma_rx(axidma_dev_t dev);

/**
 * Allocates DMA buffer suitable for an AXI DMA/VDMA device of \p size bytes.
 *
 * This function allocates a DMA buffer that can be shared between the
 * processor and FPGA and is suitable for high bandwidth transfers. This means
 * that it is coherent between the FPGA and processor, and is contiguous in
 * physical memory.
 *
 * @param[in] dev An #axidma_dev_t returned by #axidma_init.
 * @param[in] size The size of the buffer in bytes.
 * @return The address of buffer on success, NULL on failure.
 **/
void *axidma_malloc(axidma_dev_t dev, size_t size);

/**
 * Frees a DMA buffer previously allocated by #axidma_malloc.
 *
 * This function will abort if \p addr is not an address previously returned by
 * #axidma_malloc, or if \p size does not match the value used when the buffer
 * was allocated.
 *
 * @param[in] dev An #axidma_dev_t returned by #axidma_init.
 * @param[in] addr Address of the buffer returned by #axidma_malloc.
 * @param[in] size Size of the buffer passed when it was allocated by
 *                 #axidma_malloc.
 **/
void axidma_free(axidma_dev_t dev, void *addr, size_t size);

/**
 * Registers a DMA buffer that was allocated externally, by another driver.
 *
 * An "external" DMA buffer is a DMA buffer that was allocated by another
 * driver. For example, you might want to perform DMA transfers on a frame
 * buffer allocated by a display rendering manager (DRM) driver. Registering
 * the DMA buffer allows for the AXI DMA device to access it and perform
 * transfers.
 *
 * @param[in] dev An #axidma_dev_t returned by #axidma_init.
 * @param[in] dmabuf_fd File descriptor corresponding to the buffer. This
 *                      corresponds to the file descriptor passed to the mmap
 *                      call that allocated the buffer.
 * @param[in] user_addr Address of the external buffer.
 * @param[in] size Size of the buffer in bytes.
 * @return 0 on success, a negative integer on failure.
 **/
int axidma_register_buffer(axidma_dev_t dev, int dmabuf_fd, void *user_addr,
                           size_t size);

/**
 * Unregisters an external DMA buffer that was previously registered by
 * #axidma_register_buffer.
 *
 * If \p user_addr is not has not been previously registered with a call to
 * #axidma_register_buffer, then this function will abort.
 *
 * @param[in] dev An #axidma_dev_t returned by #axidma_init.
 * @param[in] user_addr Address of the external buffer. This must have
 *                      previously been registered wtih a call to
 *                      #axidma_register_buffer.
 **/
void axidma_unregister_buffer(axidma_dev_t dev, void *user_addr);

/**
 * Registers a user callback function to be invoked upon completion of an
 * asynchronous transfer for the specified DMA channel.
 *
 * The callback will be invoked with a POSIX real-time signal, so it will
 * happen as soon as possible to the completion. The \p data will be passed to
 * the callback function. This function can never fail.
 *
 * @param[in] dev An #axidma_dev_t returned by #axidma_init.
 * @param[in] channel DMA channel to register the callback for.
 * @param[in] callback Callback function invoked when the asynchronous transfer
 *                     completes.
 * @param[in] data Generic user data that is passed to the callback function.
 **/
void axidma_set_callback(axidma_dev_t dev, int channel, axidma_cb_t callback,
                         void *data);

/**
 * Performs a single DMA transfer in the specified direction on the DMA channel.
 *
 * This function will perform a single DMA transfer using the specified buffer.
 * If wait is false, then this function will be non-blocking, and if the user
 * registered a callback function, it will be invoked upon completion of the
 * transfer.
 *
 * The addresses \p buf and \p buf+\p len must be within a buffer that was
 * previously allocated by #axidma_malloc or registered with
 * #axidma_register_buffer. This function will abort if the channel is invalid.
 *
 * @param[in] dev An #axidma_dev_t returned by #axidma_init.
 * @param[in] channel DMA channel the transfer is performed on.
 * @param[in] buf Address of the DMA buffer to transfer, previously allocated by
 *                #axidma_malloc or registered with #axidma_register_buffer.
 * @param[in] len Number of bytes that will be transfered.
 * @param[in] wait Indicates if the transfer should be synchronous or
 *                 asynchronous. If true, this function will block.
 * @return 0 upon success, a negative number on failure.
 **/
int axidma_oneway_transfer(axidma_dev_t dev, int channel, void *buf, size_t len,
        bool wait);

/**
 * Performs a two coupled DMA transfers, one in the receive direction, the other
 * in the transmit direction.
 *
 * This function will perform a receive and transmit DMA transfer using the
 * specified buffers. If wait is false, the user's callback function will be
 * invoked on the channels for which a callback was registered.
 *
 * This function will abort if either of the specified channels do not exist,
 * or if the channel does not support the direction requested.
 *
 * @param[in] dev An #axidma_dev_t returned by #axidma_init.
 * @param[in] tx_channel DMA channel the transmit transfer is performed on.
 * @param[in] tx_buf Address of the DMA buffer to transmit, previously allocated
 *                   by #axidma_malloc or registered with
 *                   #axidma_register_buffer.
 * @param[in] tx_len Number of bytes to transmit from \p tx_buf.
 * @param[in] tx_frame Information about the video frame for the transmit
 *                     channel. Should be set to NULL for non-VDMA transfers.
 * @param[in] rx_channel DMA channel the receive transfer is performed on.
 * @param[in] rx_buf Address of the DMA buffer to receive, previously allocated
 *                   by #axidma_malloc or registered with
 *                   #axidma_register_buffer.
 * @param[in] rx_len Number of bytes to receive into \p rx_buf.
 * @param[in] rx_frame Information about the video frame for the receive
 *                     channel. Should be set to NULL for non-VDMA transfers.
 * @param[in] wait Indicates if the transfer should be synchronous or
 *                 asynchronous. If true, this function will block.
 * @return 0 upon success, a negative number on failure.
 **/
int axidma_twoway_transfer(axidma_dev_t dev, int tx_channel, void *tx_buf,
        size_t tx_len, struct axidma_video_frame *tx_frame, int rx_channel,
        void *rx_buf, size_t rx_len, struct axidma_video_frame *rx_frame,
        bool wait);

/**
 * Starts a video DMA (VDMA) loop/continuous transfer on the given channel.
 *
 * A video loop transfer differs from a typical DMA transfer in that it is
 * cyclic, and ends only when requested by the user. A video loop transfer will
 * continuously transmit/receive the frame buffers, transmitting the first
 * buffer, then the second, etc., and then repeating from the beginning once the
 * last buffer is reached. This is suitable when continuously sending data to a
 * display, or continuous receiving data from a camera.
 *
 * This function supports an arbitrary number of frame buffers, allowing
 * for both double-buffering and triple-buffering. This function is
 * non-blocking, and returns immediately. The only way to stop the transfer is
 * via a call to #axidma_stop_transfer.
 *
 * @param[in] dev An #axidma_dev_t returned by #axidma_init.
 * @param[in] display_channel DMA channel the video transfer will take place
 *                            on. This must be a VDMA channel.
 * @param[in] width The number of pixels in a row of the frame buffer.
 * @param[in] height The number rows in the frame buffer.
 * @param[in] depth The number of bytes in a pixel.
 * @param[in] frame_buffers A list of frame buffer addresses.
 * @param[in] num_buffers The number of buffers in \p frame_buffers. This must
 *                        match the length of the list.
 * @return 0 upon success, a negative number on failure.
 **/
int axidma_video_transfer(axidma_dev_t dev, int display_channel, size_t width,
        size_t height, size_t depth, void **frame_buffers, int num_buffers);

/**
 * Stops the DMA transfer on specified DMA channel.
 *
 * This function stops transfers on either DMA or VDMA channels.
 *
 * This function will abort if the channel is invalid, or if the DMA channel
 * currently has no running transaction on it.
 *
 * @param[in] dev An #axidma_dev_t returned by #axidma_init.
 * @param[in] channel DMA channel to stop the transfer on.
 **/
void axidma_stop_transfer(axidma_dev_t dev, int channel);
/**
 The following update by xin.han
 A convenient structure to carry information around about the transfer
 **/
struct dma_transfer {
    int input_fd;           // The file descriptor for the input file
    int input_channel;      // The channel used to send the data
    int input_size;         // The amount of data to send
    void *input_buf;        // The buffer to hold the input data
    int output_fd;          // The file descriptor for the output file
    int output_channel;     // The channel used to receive the data
    int output_size;        // The amount of data to receive
    void *output_buf;       // The buffer to hold the output
};

//A read/write operation to memory
void XDma_Out32(unsigned int * Addr, unsigned int Value);
unsigned int * XDma_In32(unsigned int * Addr);
/*Memory mapping to AXIDMA&Bram 
To facilitate enablement and manipulation of registers
*/
int axidma_config();
/*A top-level DMA sending function
@param[in] dev An #axidma_dev_t returned by #axidma_init.
@param[in] trans transfer structure
@param[in] sbuffer The data to be sent
*/
int axidma0send(axidma_dev_t dev, struct dma_transfer *trans,
                         unsigned char *sbuffer);
/*A top-level DMA reading function
@param[in] dev An #axidma_dev_t returned by #axidma_init.
@param[in] trans transfer structure
@param[in] rbuffer The data to be received
return  Accept the size of the data
*/
int axidma0read(axidma_dev_t dev, struct dma_transfer *trans,
                         unsigned char *rbuffer);
#endif /* LIBAXIDMA_H_ */

驱动C文件:

/**
 * @file axidmaapp.c
 * @date Saturday, March 20, 2021 at 10:24:11 AM EST
 * @author Brandon Perez (bmperez)
 * @author Jared Choi (jaewonch)
 * @author HanXin(update)
 *
 * This is a simple library that wraps around the AXI DMA module,
 * allowing for the user to abstract away from the finer grained details.
 *
 * @bug No known bugs.
 **/

#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <assert.h>
#include <string.h>             // Memset and memcpy functions
#include <fcntl.h>              // Flags for open()
#include <sys/stat.h>           // Open() system call
#include <sys/types.h>          // Types for open()
#include <sys/mman.h>           // Mmap system call
#include <sys/ioctl.h>          // IOCTL system call
#include <unistd.h>             // Close() system call
#include <errno.h>              // Error codes
#include <signal.h>             // Signal handling functions

#include "axidmaapp.h"          // Local definitions
#include "axidma_ioctl.h"       // The IOCTL interface to AXI DMA


/*----------------------------------------------------------------------------
 * Internal definitions
 *----------------------------------------------------------------------------*/

// A structure that holds metadata about each channel
typedef struct dma_channel {
    enum axidma_dir dir;        ///< Direction of the channel
    enum axidma_type type;      ///< Type of the channel
    int channel_id;             ///< Integer id of the channel.
    axidma_cb_t callback;       ///< Callback function for channel completion
    void *user_data;            ///< User data to pass to the callback
} dma_channel_t;

// The structure that represents the AXI DMA device
struct axidma_dev {
    bool initialized;           ///< Indicates initialization for this struct.
    int fd;                     ///< File descriptor for the device
    array_t dma_tx_chans;       ///< Channel id's for the DMA transmit channels
    array_t dma_rx_chans;       ///< Channel id's for the DMA receive channels
    array_t vdma_tx_chans;      ///< Channel id's for the VDMA transmit channels
    array_t vdma_rx_chans;      ///< Channel id's for the VDMA receive channels
    int num_channels;           ///< The total number of DMA channels
    dma_channel_t *channels;    ///< All of the VDMA/DMA channels in the system
};

// The DMA device structure, and a boolean checking if it's already open
struct axidma_dev axidma_dev = {0};

/*----------------------------------------------------------------------------
 * Private Helper Functions
 *----------------------------------------------------------------------------*/

/* Categorizes the DMA channels by their type and direction, getting their ID's
 * and placing them into separate arrays. */
static int categorize_channels(axidma_dev_t dev,
        struct axidma_chan *channels, struct axidma_num_channels *num_chan)
{
    int i;
    struct axidma_chan *chan;
    dma_channel_t *dma_chan;

    // Allocate an array for all the channel metadata
    dev->channels = malloc(num_chan->num_channels * sizeof(dev->channels[0]));
    if  (dev->channels == NULL) {
        return -ENOMEM;
    }

    // Allocate arrays for the DMA channel ids
    dev->dma_tx_chans.data = malloc(num_chan->num_dma_tx_channels *
            sizeof(dev->dma_tx_chans.data[0]));
    if (dev->dma_tx_chans.data == NULL) {
        free(dev->channels);
        return -ENOMEM;
    }
    dev->dma_rx_chans.data = malloc(num_chan->num_dma_rx_channels *
            sizeof(dev->dma_rx_chans.data[0]));
    if (dev->dma_rx_chans.data == NULL) {
        free(dev->channels);
        free(dev->dma_tx_chans.data);
        return -ENOMEM;
    }

    // Allocate arrays for the VDMA channel ids
    dev->vdma_tx_chans.data = malloc(num_chan->num_vdma_tx_channels *
            sizeof(dev->vdma_tx_chans.data[0]));
    if (dev->vdma_tx_chans.data == NULL) {
        free(dev->channels);
        free(dev->dma_tx_chans.data);
        free(dev->dma_rx_chans.data);
        return -ENOMEM;
    }
    dev->vdma_rx_chans.data = malloc(num_chan->num_vdma_rx_channels *
            sizeof(dev->vdma_rx_chans.data[0]));
    if (dev->vdma_rx_chans.data == NULL) {
        free(dev->channels);
        free(dev->dma_tx_chans.data);
        free(dev->dma_rx_chans.data);
        free(dev->vdma_tx_chans.data);
        return -ENOMEM;
    }

    // Place the DMA channel ID's into the appropiate array
    dev->num_channels = num_chan->num_channels;
    for (i = 0; i < num_chan->num_channels; i++)
    {
        // Based on the current channels's type and direction, select the array
        array_t *array = NULL;
        chan = &channels[i];
        if (chan->dir == AXIDMA_WRITE && chan->type == AXIDMA_DMA) {
            array = &dev->dma_tx_chans;
        } else if (chan->dir == AXIDMA_READ && chan->type == AXIDMA_DMA) {
            array = &dev->dma_rx_chans;
        } else if (chan->dir == AXIDMA_WRITE && chan->type == AXIDMA_VDMA) {
            array = &dev->vdma_tx_chans;
        } else if (chan->dir == AXIDMA_READ && chan->type == AXIDMA_VDMA) {
            array = &dev->vdma_rx_chans;
        }
        assert(array != NULL);

        // Assign the ID for the channel into the appropiate array
        array->data[array->len] = chan->channel_id;
        array->len += 1;

        // Construct the DMA channel structure
        dma_chan = &dev->channels[i];
        dma_chan->dir = chan->dir;
        dma_chan->type = chan->type;
        dma_chan->channel_id = chan->channel_id;
        dma_chan->callback = NULL;
        dma_chan->user_data = NULL;
    }

    // Assign the length of the arrays

    return 0;
}

/* Probes the AXI DMA driver for all of the available channels. It places
 * returns an array of axidma_channel structures. */
static int probe_channels(axidma_dev_t dev)
{
    int rc;
    struct axidma_chan *channels;
    struct axidma_num_channels num_chan;
    struct axidma_channel_info channel_info;

    // Query the module for the total number of DMA channels
    rc = ioctl(dev->fd, AXIDMA_GET_NUM_DMA_CHANNELS, &num_chan);
    if (rc < 0) {
        perror("Unable to get the number of DMA channels");
        return rc;
    } else if (num_chan.num_channels == 0) {
        fprintf(stderr, "No DMA channels are present.\n");
        return -ENODEV;
    }

    // Allocate an array to hold the channel meta-data
    channels = malloc(num_chan.num_channels * sizeof(channels[0]));
    if (channels == NULL) {
        return -ENOMEM;
    }

    // Get the metdata about all the available channels
    channel_info.channels = channels;
    rc = ioctl(dev->fd, AXIDMA_GET_DMA_CHANNELS, &channel_info);
    if (rc < 0) {
        perror("Unable to get DMA channel information");
        free(channels);
        return rc;
    }

    // Extract the channel id's, and organize them by type
    rc = categorize_channels(dev, channels, &num_chan);
    free(channels);

    return rc;
}

static void axidma_callback(int signal, siginfo_t *siginfo, void *context)
{
    int channel_id;
    dma_channel_t *chan;

    assert(0 <= siginfo->si_int && siginfo->si_int < axidma_dev.num_channels);

    // Silence the compiler
    (void)signal;
    (void)context;

    // If the user defined a callback for a given channel, invoke it
    channel_id = siginfo->si_int;
    chan = &axidma_dev.channels[channel_id];
    if (chan->callback != NULL) {
        chan->callback(channel_id, chan->user_data);
    }

    return;
}

/* Sets up a signal handler for the lowest real-time signal to be delivered
 * whenever any asynchronous DMA transaction compeletes. */
// TODO: Should really check if real time signal is being used
static int setup_dma_callback(axidma_dev_t dev)
{
    int rc;
    struct sigaction sigact;

    // Register a signal handler for the real-time signal
    sigact.sa_sigaction = axidma_callback;
    sigemptyset(&sigact.sa_mask);
    sigact.sa_flags = SA_RESTART | SA_SIGINFO;
    rc = sigaction(SIGRTMIN, &sigact, NULL);
    if (rc < 0) {
        perror("Failed to register DMA callback");
        return rc;
    }

    // Tell the driver to deliver us SIGRTMIN upon DMA completion
    rc = ioctl(dev->fd, AXIDMA_SET_DMA_SIGNAL, SIGRTMIN);
    if (rc < 0) {
        perror("Failed to set the DMA callback signal");
        return rc;
    }

    return 0;
}

// Finds the DMA channel with the given id
static dma_channel_t *find_channel(axidma_dev_t dev, int channel_id)
{
    int i;
    dma_channel_t *dma_chan;

    for (i = 0; i < dev->num_channels; i++)
    {
        dma_chan = &dev->channels[i];
        if (dma_chan->channel_id == channel_id) {
            return dma_chan;
        }
    }

    return NULL;
}

// Converts the AXI DMA direction to the corresponding ioctl for the transfer
static unsigned long dir_to_ioctl(enum axidma_dir dir)
{
    switch (dir)
    {
        case AXIDMA_READ:
            return AXIDMA_DMA_READ;
        case AXIDMA_WRITE:
            return AXIDMA_DMA_WRITE;
    }

    assert(false);
    return 0;
}

/*----------------------------------------------------------------------------
 * Public Interface
 *----------------------------------------------------------------------------*/

/* Initializes the AXI DMA device, returning a new handle to the
 * axidma_device. */
struct axidma_dev *axidma_init()
{
    assert(!axidma_dev.initialized);

    // Open the AXI DMA device
    axidma_dev.fd = open(AXIDMA_DEV_PATH, O_RDWR|O_EXCL);
    if (axidma_dev.fd < 0) {
        perror("Error opening AXI DMA device");
        fprintf(stderr, "Expected the AXI DMA device at the path `%s`\n",
                AXIDMA_DEV_PATH);
        return NULL;
    }

    // Query the AXIDMA device for all of its channels
    if (probe_channels(&axidma_dev) < 0) {
        close(axidma_dev.fd);
        return NULL;
    }

    // TODO: Should really check that signal is not already taken
    /* Setup a real-time signal to indicate when transactions have completed,
     * and request the driver to send them to us. */
    if (setup_dma_callback(&axidma_dev) < 0) {
        close(axidma_dev.fd);
        return NULL;
    }

    // Return the AXI DMA device to the user
    axidma_dev.initialized = true;
    return &axidma_dev;
}

// Tears down the given AXI DMA device structure
void axidma_destroy(axidma_dev_t dev)
{
    // Free the arrays used for channel id's and channel metadata
    free(dev->vdma_rx_chans.data);
    free(dev->vdma_tx_chans.data);
    free(dev->dma_rx_chans.data);
    free(dev->dma_tx_chans.data);
    free(dev->channels);

    // Close the AXI DMA device
    if (close(dev->fd) < 0) {
        perror("Failed to close the AXI DMA device");
        assert(false);
    }

    // Free the device structure
    axidma_dev.initialized = false;
    return;
}

// Returns an array of all the available AXI DMA transmit channels
const array_t *axidma_get_dma_tx(axidma_dev_t dev)
{
    return &dev->dma_tx_chans;
}

// Returns an array of all the available AXI DMA receive channels
const array_t *axidma_get_dma_rx(axidma_dev_t dev)
{
    return &dev->dma_rx_chans;
}

// Returns an array of all the available AXI VDMA transmit channels
const array_t *axidma_get_vdma_tx(axidma_dev_t dev)
{
    return &dev->vdma_tx_chans;
}

// Returns an array of all the available AXI VDMA receive channels
const array_t *axidma_get_vdma_rx(axidma_dev_t dev)
{
    return &dev->vdma_rx_chans;
}

/* Allocates a region of memory suitable for use with the AXI DMA driver. Note
 * that this is a quite expensive operation, and should be done at initalization
 * time. */
void *axidma_malloc(axidma_dev_t dev, size_t size)
{
    void *addr;

    // Call the device's mmap method to allocate the memory region
    addr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, dev->fd, 0);
    if (addr == MAP_FAILED) {
        return NULL;
    }

    return addr;
}

/* This frees a region of memory that was allocated with a call to
 * axidma_malloc. The size passed in here must match the one used for that
 * call, or this function will throw an exception. */
void axidma_free(axidma_dev_t dev, void *addr, size_t size)
{
    // Silence the compiler
    (void)dev;

    if (munmap(addr, size) < 0) {
        perror("Failed to free the AXI DMA memory mapped region");
        assert(false);
    }

    return;
}

/* Sets up a callback function to be called whenever the transaction completes
 * on the given channel for asynchronous transfers. */
void axidma_set_callback(axidma_dev_t dev, int channel, axidma_cb_t callback,
                        void *data)
{
    dma_channel_t *chan;

    assert(find_channel(dev, channel) != NULL);

    chan = &dev->channels[channel];
    chan->callback = callback;
    chan->user_data = data;

    return;
}

/* Registers a DMA buffer allocated by another driver with the AXI DMA driver.
 * This allows it to be used in DMA transfers later on. The user must make sure
 * that the driver that allocated the buffer has exported it. The file
 * descriptor is the one that is returned by the other driver's export. */
int axidma_register_buffer(axidma_dev_t dev, int dmabuf_fd, void *user_addr,
                           size_t size)
{
    int rc;
    struct axidma_register_buffer register_buffer;

    // Setup the argument structure to the IOCTL
    register_buffer.fd = dmabuf_fd;
    register_buffer.size = size;
    register_buffer.user_addr = user_addr;

    // Perform the buffer registration with the driver
    rc = ioctl(dev->fd, AXIDMA_REGISTER_BUFFER, &register_buffer);
    if (rc < 0) {
        perror("Failed to register the external DMA buffer");
    }

    return rc;
}

/* Unregisters a DMA buffer preivously registered with the driver. This is
 * required to clean up the kernel data structures. */
void axidma_unregister_buffer(axidma_dev_t dev, void *user_addr)
{
    int rc;

    // Perform the deregistration with the driver
    rc = ioctl(dev->fd, AXIDMA_UNREGISTER_BUFFER, user_addr);
    if (rc < 0) {
        perror("Failed to unregister the external DMA buffer");
        assert(false);
    }

    return;
}

/* This performs a one-way transfer over AXI DMA, the direction being specified
 * by the user. The user determines if this is blocking or not with `wait. */
int axidma_oneway_transfer(axidma_dev_t dev, int channel, void *buf,
        size_t len, bool wait)
{
    int rc;
    struct axidma_transaction trans;
    unsigned long axidma_cmd;
    dma_channel_t *dma_chan;

    assert(find_channel(dev, channel) != NULL);

    // Setup the argument structure to the IOCTL
    dma_chan = find_channel(dev, channel);
    trans.wait = wait;
    trans.channel_id = channel;
    trans.buf = buf;
    trans.buf_len = len;
    axidma_cmd = dir_to_ioctl(dma_chan->dir);

    // Perform the given transfer
    rc = ioctl(dev->fd, axidma_cmd, &trans);
    if (rc < 0) {
        perror("Failed to perform the AXI DMA transfer");
        return rc;
    }

    return 0;
}

/* This performs a two-way transfer over AXI DMA, both sending data out and
 * receiving it back over DMA. The user determines if this call is blocking. */
int axidma_twoway_transfer(axidma_dev_t dev, int tx_channel, void *tx_buf,
        size_t tx_len, struct axidma_video_frame *tx_frame, int rx_channel,
        void *rx_buf, size_t rx_len, struct axidma_video_frame *rx_frame,
        bool wait)
{
    int rc;
    struct axidma_inout_transaction trans;

    assert(find_channel(dev, tx_channel) != NULL);
    assert(find_channel(dev, tx_channel)->dir == AXIDMA_WRITE);
    assert(find_channel(dev, rx_channel) != NULL);
    assert(find_channel(dev, rx_channel)->dir == AXIDMA_READ);

    // Setup the argument structure for the IOCTL
    trans.wait = wait;
    trans.tx_channel_id = tx_channel;
    trans.tx_buf = tx_buf;
    trans.tx_buf_len = tx_len;
    trans.rx_channel_id = rx_channel;
    trans.rx_buf = rx_buf;
    trans.rx_buf_len = rx_len;

    // Copy in the video frame if it is specified
    if (tx_frame == NULL) {
        memset(&trans.tx_frame, -1, sizeof(trans.tx_frame));
    } else {
        memcpy(&trans.tx_frame, tx_frame, sizeof(trans.tx_frame));
    }
    if (rx_frame == NULL) {
        memset(&trans.rx_frame, -1, sizeof(trans.rx_frame));
    } else {
        memcpy(&trans.rx_frame, rx_frame, sizeof(trans.rx_frame));
    }

    // Perform the read-write transfer
    rc = ioctl(dev->fd, AXIDMA_DMA_READWRITE, &trans);
    if (rc < 0) {
        perror("Failed to perform the AXI DMA read-write transfer");
    }

    return rc;
}

/* This function performs a video transfer over AXI DMA, setting up a VDMA
 * channel to either read from or write to given frame buffers on-demand
 * continuously. This call is always non-blocking. The transfer can only be
 * stopped with a call to axidma_stop_transfer. */
int axidma_video_transfer(axidma_dev_t dev, int display_channel, size_t width,
        size_t height, size_t depth, void **frame_buffers, int num_buffers)
{
    int rc;
    unsigned long axidma_cmd;
    struct axidma_video_transaction trans;
    dma_channel_t *dma_chan;

    assert(find_channel(dev, display_channel) != NULL);
    assert(find_channel(dev, display_channel)->type == AXIDMA_VDMA);

    // Setup the argument structure for the IOCTL
    dma_chan = find_channel(dev, display_channel);
    trans.channel_id = display_channel;
    trans.num_frame_buffers = num_buffers;
    trans.frame_buffers = frame_buffers;
    trans.frame.width = width;
    trans.frame.height = height;
    trans.frame.depth = depth;
    axidma_cmd = (dma_chan->dir == AXIDMA_READ) ? AXIDMA_DMA_VIDEO_READ :
                                                  AXIDMA_DMA_VIDEO_WRITE;
    // Perform the video transfer
    rc = ioctl(dev->fd, axidma_cmd, &trans);
    if (rc < 0) {
        perror("Failed to perform the AXI DMA video write transfer");
    }

    return rc;
}

/* This function stops all transfers on the given channel with the given
 * direction. This function is required to stop any video transfers, or any
 * non-blocking transfers. */
void axidma_stop_transfer(axidma_dev_t dev, int channel)
{
    struct axidma_chan chan;
    dma_channel_t *dma_chan;

    assert(find_channel(dev, channel) != NULL);

    // Setup the argument structure for the IOCTL
    dma_chan = find_channel(dev, channel);
    chan.channel_id = channel;
    chan.dir = dma_chan->dir;
    chan.type = dma_chan->type;

    // Stop all transfers on the given DMA channel
    if (ioctl(dev->fd, AXIDMA_STOP_DMA_CHANNEL, &chan) < 0) {
        perror("Failed to stop the DMA channel");
        assert(false);
    }

    return;
}
void XDma_Out32(unsigned int * Addr, unsigned int Value)
{
	volatile unsigned int *LocalAddr = (volatile unsigned int *)Addr;
	*LocalAddr = Value;
}
 unsigned int * XDma_In32(unsigned int * Addr)
{
	return *(volatile unsigned int *) Addr;
}
int axidma_config()
{

    int fd = open("/dev/mem", O_RDWR | O_SYNC);
    if (fd < 0) {
        printf("can not open /dev/mem \n");
        return (-1);
    }   
    printf("/dev/mem is open \n");
    /*
    mmap
    第二个参数表示内存映射的大小、
    第三个参数是一个 flag标志, PROT_READ | PROT_WRITE 的组合表示映射的内存空间是可读可写的、
    第四个参数MAP_SHARED、
    第五个参数表示文件描述符 fd。
     mmap 函数的返回值就等于映射之后得到的实际地址
    */
    map_base0 = mmap(NULL, 1024 * 4, PROT_READ | PROT_WRITE, MAP_SHARED, fd, XPAR_BRAM_0_BASEADDR);
    map_base1 = mmap(NULL, 1024 * 4, PROT_READ | PROT_WRITE, MAP_SHARED, fd, DMA_0_BASEADDR);
    
    if (map_base0 == 0 || map_base1 == 0) { 
        printf("NULL pointer\n");
    }   
    else {
        printf("mmap successful\n");
    }   
     close(fd);
     return 0;
}
/*----------------------------------------------------------------------------
 * 文件传输
 *----------------------------------------------------------------------------*/


int axidma0send(axidma_dev_t dev, struct dma_transfer *trans,
                         unsigned char *sbuffer)
{
    int rc;

    // 为输入文件分配一个缓冲区,并将其读入缓冲区
    // trans->input_buf = axidma_malloc(dev, trans->input_size);
    
    memcpy(trans->input_buf,sbuffer,trans->input_size);
    // printf("sbuffer in is %d",sbuffer[1]);
    
 
    // 执行搬移
    // rc = axidma_twoway_transfer(dev, trans->input_channel, trans->input_buf,
    //         trans->input_size, NULL, trans->output_channel, trans->output_buf,
    //         trans->output_size, NULL, true);
    rc = axidma_oneway_transfer(dev, trans->input_channel, trans->input_buf,
        trans->input_size, true);
        
    if (rc < 0) {
        fprintf(stderr, "DMA send transaction failed.\n");
        // goto free_output_buf;
    }
    XBram_Out32(map_base0+12,0x1);
    usleep(15);
    XBram_Out32(map_base0+12,0);//rx interrupt
    // 将数据写入输出文件
  
    // rc = robust_write(trans->output_fd, trans->output_buf, trans->output_size);

// free_output_buf:
//     axidma_free(dev, trans->output_buf, trans->output_size);
// free_input_buf:
//     axidma_free(dev, trans->input_buf, trans->input_size);
// ret:
    return rc;
}
int axidma0read(axidma_dev_t dev, struct dma_transfer *trans,
                         unsigned char *rbuffer)
{
    int rc;
    int Length;
 
    // 执行搬移
    // rc = axidma_twoway_transfer(dev, trans->input_channel, trans->input_buf,
    //         trans->input_size, NULL, trans->output_channel, trans->output_buf,
    //         trans->output_size, NULL, true);
    rc = axidma_oneway_transfer(dev, trans->output_channel, trans->output_buf,
        trans->output_size, true);
    if (rc < 0) {
        fprintf(stderr, "DMA read transaction failed.\n");
        // goto free_output_buf;
        axidma_free(dev, trans->output_buf, trans->output_size);
    }
    Length = XDma_In32(map_base1+0x58);
   
    // canshujiancha
    if(Length > 4096)
	    {
	     printf("gkhy_debug : Length is 0x%x  ,4096 error \n",Length);
	     return Length;
	    }
  
    // rc = robust_write(trans->output_fd, trans->output_buf, trans->output_size);
    memcpy(rbuffer,trans->output_buf,Length);
    XBram_Out32(map_base0+8,0x1);
    //   usleep(15);
    XBram_Out32(map_base0+8,0x0);
    // free_output_buf:
    // axidma_free(dev, trans->output_buf, trans->output_size);
    // free_input_buf:
    //     axidma_free(dev, trans->input_buf, trans->input_size);
    //   ret:
    return Length;
}

  中间对于bram寄存器的读写是和逻辑端做的使能交互,因为这个模块应该是不直接支持中断的,异步通知好像也不起作用,所以挂载了gpio中断,相应的添加了一些简单的东西,具体gpio中断的部分之前单独有一篇来讲,可以参考下
然后就是

测试demo:

/**
 * @file axidma_transfer.c
 * @date Sunday, April 1, 2021 at 12:23:43 PM EST
 * @author Brandon Perez (bmperez)
 * @author Jared Choi (jaewonch)
 * @author Xin Han (hicx)
 *
 * This program performs a simple AXI DMA transfer. It takes the input data,
 * loads it into memory, and then sends it out over the PL fabric. It then
 * receives the data back, and places it into the given output .
 *
 * By default it uses the lowest numbered channels for the transmit and receive,
 * unless overriden by the user. The amount of data transfered is automatically
 * determined from the file size. Unless specified, the output file size is
 * made to be 2 times the input size (to account for creating more data).
 *
 * This program also handles any additional channels that the pipeline
 * on the PL fabric might depend on. It starts up DMA transfers for these
 * pipeline stages, and discards their results.
 *
 * @bug No known bugs.
 **/

#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <assert.h>

#include <fcntl.h>    
#include <sys/mman.h>          // Flags for open()
#include <sys/stat.h>           // Open() system call
#include <sys/types.h>          // Types for open()
#include <unistd.h>             // Close() system call
#include <string.h>             // Memory setting and copying
#include <getopt.h>             // Option parsing
#include <errno.h>              // Error codes
#include <string.h>
#include <poll.h>
#include "util.h"               // Miscellaneous utilities
#include "conversion.h"         // Convert bytes to MiBs
#include "axidmaapp.h"          // Interface ot the AXI DMA library
#include <pthread.h>
#include "gpioapp.h"
static unsigned char rbuffer[4096] = {0};
static unsigned char sbuffer[4096] = {0};
static unsigned char tbuffer[4096] = {0};
#define MAXLENGTH 4096
extern gpio_fd;
extern gpio_fd1;
extern gpio_fd2;
extern gpio_fd3;
extern gpio_fd4;
extern gpio_fd5;
extern gpio_fd6;
extern gpio_fd7;
axidma_dev_t axidma_dev;
struct dma_transfer trans;
// Prints the usage for this program
static void print_usage(bool help)
{
    FILE* stream = (help) ? stdout : stderr;

    fprintf(stream, "Usage: axidma_transfer  "
            "[-t <DMA tx channel>] [-r <DMA rx channel>] [-s <Output file size>"
            " | -o <Output file size>].\n");
    if (!help) {
        return;
    }

    // fprintf(stream, "\t<input path>:\t\tThe path to file to send out over AXI "
    //         "DMA to the PL fabric. Can be a relative or absolute path.\n");
    // fprintf(stream, "\t<output path>:\t\tThe path to place the received data "
    //         "from the PL fabric into. Can be a relative or absolute path.\n");
    fprintf(stream, "\t-t <DMA tx channel>:\tThe device id of the DMA channel "
            "to use for transmitting the file. Default is to use the lowest "
            "numbered channel available.\n");
    fprintf(stream, "\t-r <DMA rx channel>:\tThe device id of the DMA channel "
            "to use for receiving the data from the PL fabric. Default is to "
            "use the lowest numbered channel available.\n");
    fprintf(stream, "\t-s <Output file size>:\tThe size of the output file in "
            "bytes. This is an integer value that must be at least the number "
            "of bytes received back. By default, this is the same as the size "
            "of the input file.\n");
    fprintf(stream, "\t-o <Output file size>:\tThe size of the output file in "
            "Mibs. This is a floating-point value that must be at least the "
            "number of bytes received back. By default, this is the same "
            "the size of the input file.\n");
    return;
}

/* Parses the command line arguments overriding the default transfer sizes,
 * and number of transfer to use for the benchmark if specified. */
static int parse_args(int argc, char **argv,  int *input_channel, int *output_channel, int *output_size)
{
    char option;
    int int_arg;
    double double_arg;
    bool o_specified, s_specified;
    int rc;

    // Set the default values for the arguments
    *input_channel = -1;
    *output_channel = -1;
    *output_size = -1;
    o_specified = false;
    s_specified = false;
    rc = 0;

    while ((option = getopt(argc, argv, "t:r:s:o:h")) != (char)-1)
    {
        switch (option)
        {
            // Parse the transmit channel device id
            case 't':
                rc = parse_int(option, optarg, &int_arg);
                if (rc < 0) {
                    print_usage(false);
                    return rc;
                }
                *input_channel = int_arg;
                break;

            // Parse the receive channel device id
            case 'r':
                rc = parse_int(option, optarg, &int_arg);
                if (rc < 0) {
                    print_usage(false);
                    return rc;
                }
                *output_channel = int_arg;
                break;

            // Parse the output file size (in bytes)
            case 's':
                rc = parse_int(option, optarg, &int_arg);
                if (rc < 0) {
                    print_usage(false);
                    return rc;
                }
                *output_size = int_arg;
                s_specified = true;
                break;

            // Parse the output file size (in MiBs)
            case 'o':
                rc = parse_double(option, optarg, &double_arg);
                if (rc < 0) {
                    print_usage(false);
                    return rc;
                }
                *output_size = MIB_TO_BYTE(double_arg);
                o_specified = true;
                break;

            case 'h':
                print_usage(true);
                exit(0);

            default:
                print_usage(false);
                return -EINVAL;
        }
    }

    // If one of -t or -r is specified, then both must be
    if ((*input_channel == -1) ^ (*output_channel == -1)) {
        fprintf(stderr, "Error: Either both -t and -r must be specified, or "
                "neither.\n");
        print_usage(false);
        return -EINVAL;
    }

    // Only one of -s and -o can be specified
    if (s_specified && o_specified) {
        fprintf(stderr, "Error: Only one of -s and -o can be specified.\n");
        print_usage(false);
        return -EINVAL;
    }

    // // Check that there are enough command line arguments
    // if (optind > argc-2) {
    //     fprintf(stderr, "Error: Too few command line arguments.\n");
    //     print_usage(false);
    //     return -EINVAL;
    // }

    // Check if there are too many command line arguments remaining
    if (optind < argc-2) {
        fprintf(stderr, "Error: Too many command line arguments.\n");
        print_usage(false);
        return -EINVAL;
    }

    // Parse out the input and output paths
    // *input_path = argv[optind];
    // *output_path = argv[optind+1];
    return 0;
}

//receive
void *rapidio_taks_rec(void *arg)
{
    
    // printf("r___________________________________________________________________");
    int ret = 0,i,err_num;
    unsigned int rec_len = 0;
    struct pollfd fds[1];
    char buff[10];
    static cnt = 0;
  

    fds[0].fd = gpio_fd1;
    fds[0].events  = POLLPRI;

    ret = read(gpio_fd1,buff,10);
    if( ret == -1 )
        MSG("read\n");

    while(1)
    {
      ret = poll(fds,1,-1);
      if( ret == -1 )
          MSG("poll\n");
      if( fds[0].revents & POLLPRI)
      {
        ret = lseek(gpio_fd1,0,SEEK_SET);
        if( ret == -1 )
            MSG("lseek\n");
        ret = read(gpio_fd1,buff,10);
        if( ret == -1 )
            MSG("read\n");

    //    printf("\n--------------------------------------------------------------------------------\n");
        rec_len = axidma0read(axidma_dev, &trans, rbuffer);
    //     XBram_Out32(map_base0+8,0x1);
    // //   usleep(15);
    //     XBram_Out32(map_base0+8,0x0);
        cnt++;
        if(rec_len > 4096)
	    {
	     printf("gkhy_debug : recv len error4096 \n");
	     continue;
	    }
        if(cnt%500 == 0)
	     {
	       printf("\nrec_len = 0x%x,cnt = %d\n",rec_len,cnt);
	     }
        // printf("\nrec_len = 0x%x,cnt=%d\n",rec_len,cnt);
        for(i=0;i<rec_len;i++)
	    {
		    if(rbuffer[i] != tbuffer[i])
		    {
			  printf("khy_debug :tbuffer[%d] : 0x%x,	rbuffer[%d] : 0x%x\n",i,tbuffer[i],i,rbuffer[i]);
			  err_num++;
		    }
	    }   
            if(err_num != 0)
            {
              printf("gkhy_debug:err_num = %d\n",err_num);
              err_num = 0;
            }
             if(cnt == 100000)
            {
              printf("gkhy_debug:cnt = %d\n",cnt);
              return 0;
            }
        // for(i = 0;i<(rec_len);i++)
        //   {
        //     if(i%16 == 0)
        //     {
        //         printf("\n");
        //     }
        //     printf("0x%02x ",rbuffer[i]);
        //   }
     
      }
    else
    printf("poll nothing--------------------------\n");
   }


   pthread_exit(0);
}

void *rapidio_taks_send(void *arg)
{
    int i;
    int cnt = 0;
    int rc,ret;
   
    // struct dma_transfer trans;

    while(1)
    {
      usleep(2000);
      
      axidma0send(axidma_dev, &trans, sbuffer);
    //   printf("send success");
      cnt++;
      if(cnt%500 == 0)
	     {
	       printf("send %d packet",cnt);
	     }
    //   printf("send %d packet",cnt);
    if(cnt == 100000)
        {
            printf("gkhy_debug:cnt = %d\n",cnt);
            return 0;
        }
      
     }
destroy_axidma:
    axidma_destroy(axidma_dev);
close_output:
    assert(close(trans.output_fd) == 0);
// close_input:
//     assert(close(trans.input_fd) == 0);
ret:
    return rc;

    pthread_exit(0);
 
}

/*----------------------------------------------------------------------------
 * Main
 *----------------------------------------------------------------------------*/
int main(int argc, char **argv)
{
    int rc;
    int i;
    int rec_len;
    char *input_path, *output_path;
    struct stat input_stat;
    const array_t *tx_chans, *rx_chans;
    int error;
    int ret;
   //Initialize the test buffer
    for(i = 0;i < 4096;i++)
       {
        tbuffer[i]=i;
       }                          
       
    GpioInit();
    pthread_t rapidio_sid;
    pthread_t rapidio_rid;


    //地址映射,使能读写DMA,单独规定
    axidma_config();
    XDma_Out32(map_base0+4,1);
    //  解析输入参数
    memset(&trans, 0, sizeof(trans));
    if (parse_args(argc, argv, &trans.input_channel,
                   &trans.output_channel, &trans.output_size) < 0) {
        rc = 1;
        goto ret;
    }
     for(i = 0;i < 4096;i++)
       {
        sbuffer[i]=i;
       }
//

    // 初始化AXIDMA设备
    axidma_dev = axidma_init();
    if (axidma_dev == NULL) {
        fprintf(stderr, "Error: Failed to initialize the AXI DMA device.\n");
        rc = 1;
        goto close_output;
    }
     printf("Succeed to initialize the AXI DMA device.\n");

    
    // 如果还没有指定tx和rx通道,则获取收发通道
    tx_chans = axidma_get_dma_tx(axidma_dev);
   
    if (tx_chans->len < 1) {
        fprintf(stderr, "Error: No transmit channels were found.\n");
        rc = -ENODEV;
        goto destroy_axidma;
    }
    rx_chans = axidma_get_dma_rx(axidma_dev);
    
    if (rx_chans->len < 1) {
        fprintf(stderr, "Error: No receive channels were found.\n");
        rc = -ENODEV;
        goto destroy_axidma;
    }

    /* 如果用户没有指定通道,我们假设发送和接收通道是编号最低的通道。 */
    if (trans.input_channel == -1 && trans.output_channel == -1) {
        trans.input_channel = tx_chans->data[0];
        trans.output_channel = rx_chans->data[0];
    }
    // trans.input_channel = 0;
    // trans.output_channel = 1;
    printf("AXI DMAt File Transfer Info:\n");
    printf("\tTransmit Channel: %d\n", trans.input_channel);
    printf("\tReceive Channel: %d\n", trans.output_channel);
    // printf("\tInput Data Size: %.4f MiB\n", BYTE_TO_MIB(trans.input_size));
    // printf("\tOutput Data Size: %.4f MiB\n\n", BYTE_TO_MIB(trans.output_size));
     

     
    trans.output_size = 5120;
    trans.input_size = 4096;
    // 为输出文件分配一个缓冲区
    trans.output_buf = axidma_malloc(axidma_dev, trans.output_size);
    // printf("output_size is 0x%d\n",trans->output_size);

    if (trans.output_buf == NULL) {
        rc = -ENOMEM;
        // goto free_output_buf;
        axidma_free(axidma_dev, trans.output_buf, trans.output_size);
    }
    trans.input_buf = axidma_malloc(axidma_dev, trans.input_size);
    if (trans.input_buf == NULL) {
        fprintf(stderr, "Failed to allocate the input buffer.\n");
        rc = -ENOMEM;
        axidma_free(axidma_dev, trans.input_buf, trans.input_size);
    }
    // rec_len = axidma0read(axidma_dev, &trans, rbuffer);
    // printf("\nlink test success\n");
    error=pthread_create(&rapidio_rid, NULL, &rapidio_taks_rec,NULL);
      if(error != 0)
      {
        printf("pthreadrx_create fail\n");
        return -1;
      }
    error=pthread_create(&rapidio_sid, NULL, &rapidio_taks_send,NULL);
      if(error != 0)
      {
        printf("pthreadtx_create fail\n");
        return -1;
      }
pthread_detach(rapidio_rid);
pthread_detach(rapidio_sid);

    while(1)
    {
        sleep(1);
    }
 //  rc = (rc < 0) ? -rc : 0;    
destroy_axidma:
    axidma_destroy(axidma_dev);
close_output:
    assert(close(trans.output_fd) == 0);
// close_input:
//     assert(close(trans.input_fd) == 0);
ret:
    return rc;

}

标签:DMA,int,axidma,dev,zynq,Linux,trans,AXIDMA,channel
来源: https://blog.csdn.net/qq_42330920/article/details/115618482