zynq operating system: Linux driver development AXIDMA

preface

   due to the speed limit of bram form, under the same urgent time conditions, we still changed back to the way of axidma to reduce the dimension and attack. For the speed of a few megabytes, there is a feeling of killing chickens with an ox knife. There is no way. The original knife is just a little worse. The ox knife is easy to use, but we have to improve our internal skill after all
DMA under bare metal is relatively simple. Refer to the operation of DMA on bare board before, but it can only be said to be miserable under Linux. Let's not talk about how to realize zero copy DMA transmission in user space. DMA transmission in Linux environment alone has been difficult. On the one hand, we don't know enough about Linux, on the other hand, Linux does not have good official support in relevant instructions.

   Xilinx provides an AXI-DMA IP core, which can be configured through AXI lite and ordered to read and store memory data directly from the AXI high performance bus (HP). It feels so simple when PS uses bare metal. It can call the library function to configure the start address, end address, transmission size and related of DMA, and there are even official routines for reference. But all this becomes ferocious on Linux. The complex operation mechanism based on DMA engine makes it less concise and clear to start using Linux system to operate AXI-DMA on zynq. Moreover, for people who have just come into contact with Linux for less than a month, wtf killed me.

   when the level is poor, in order to "avoid" the complicated operation of dma engine under Linux, someone thought about whether it is possible to map only the registers of the IP core axi-dma (mounted on the Axi Lite bus) to the memory through mmap, and then, as on the bare metal before, (isn't bram doing this, it can only be said that there are countless times fewer registers), To read and write this memory, you can directly configure the axi-dma register to complete the configuration operation of axi-dma (refer to this: 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 )
  however, it is also inevitable to copy from kernel space_ to_ Usr to copy data to user space, which is a very slow process when a large amount of data. Fortunately, there is an open source project xilinx_axidma realizes zero copy of AXI-DMA from user space and encapsulates it as a library,
(https://github.com/bperez77/xilinx_axidma/tree/master)
  an article mainly records how to use this library
   https://blog.csdn.net/sements/article/details/90230188
   the implementation form is similar. I didn't choose the form of SD card startup. I directly use the default file system from ddr

1 - preparatory work

  download Xilinx_ Axidma source file: https://github.com/bperez77/xilinx_axidma/tree/master , and take a good look at its README
   the compiled linux kernel is used to generate the model (or you can add xilinx_axidma by using the module mode of petalinux, which will be automatically compiled and generated when petalinux is generated. This form will be adopted below)

2 - establish the petalinux project

   set up a petalinux project, set the root file system to load from ddr (default), and use local linux, mainly to make it easier to modify the kernel later. (written in the multiple compilation articles of petalinux)

3 - configure Linux kernel

  it is necessary to ensure that DMA related items are enabled. Generally, if the vivado project contains the IP core of AXI-DMA, you will find that the basic related items have been turned on in the petalinux config - C kernel. At least 2018.2 version has been opened
   here is a tip introduced in the previous article. We choose to save in menuconfig and set a save name (such as your name   defconfig). Save it. Don't quit. Search the file name under your petalinux project file and copy it, Check whether the following items are selected y according to the requirements on github (the deletion line does not need to be checked. This library was written in 17 years, but now the linux code branch of xilinx has been used until 2018, and these related configuration items are no longer available)

CONFIG_CMA=y
CONFIG_DMA_CMA=y
CONFIG_XILINX_DMAENGINES=y
CONFIG_XILINX_AXIDMA=y
CONFIG_XILINX_AXIVDMA=y
CONFIG_DMA_SHARED_BUFFER=y
Remember, select save in menuconfig and name the file back config for petalinux to generate linux correctly
After DMA related settings are completed, we also need to configure CMA
The Size in Mega Bytes of device drivers - > generic driver options - > Default continuous memory area size is modified to 25

4 – Uboot is generated by petalinux

5 - modification of equipment tree

   first run to generate the PL related device tree (this is optional, just to facilitate the node name in pl.dtsi when modifying dtsi)
   $ petalinux-config -c device-tree
  there are two main points to modify the equipment tree: 1 Join axidma_chardev 2. Modify the device ID of each dma channel without duplication.
   I have two dma channels here (one is sent to FIFO and the other is received from FIFO). I modify their device ID to 0 and 1 respectively
  in project spec / meta user / recipes BSP / device tree / files / system user Adding 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>;
    };
};

  the reference override method of the device tree is used here to modify the device ID.

6 - compile loadable modules

  refer to the addition of modules in the compilation, such as Xilinx axidma modules,
petalinux-create -t modules --name xilinx-axidma-modules –enable,
Modify Makefile (also forget what the original version is, anyway, the focus is to check the device name and. o file)

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

  modification bb file

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.

   then compile the module, petalinux build - C Xilinx axidma modules. Maybe due to different versions, the number of parameters passed by a function is incorrect, which needs to be modified, and the timeout time. If you want to configure it as wireless blocking type, you can also modify the timeout time later

7 - compile application test demo

   first compile his own demo, and then modify it. The original transfer routine is used to carry two files locally
petalinux-create -t apps --name axidmaapp –enable
  modify Makefile first

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

  and bb file

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

  check carefully The files in the bb file must be included. The gpioapp two files were later used to mount gpio interrupts and can not be used. axidmaapp renamed the libaxidma two files because it needs to have a file with the same name or for some compilation reason
  overall build after compiling app
  you can test the demo by burning it into the board. If there is no problem with the PL test, you will successfully copy the contents of your newly created a document to b document
   the next step is our modification of other functions. It is definitely impossible to loop back locally in actual use. It is necessary to send and receive independently, encapsulate new functions, and uniformly map the sending and receiving space in the kernel when calling
Here is

Updated driver header file

/**
* @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_ */

Drive C file:

/**
 * @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
    The second parameter represents the size of the memory map
    The third parameter is a flag flag, prot_ READ | PROT_ The combination of write indicates that the mapped memory space is readable and writable
    Fourth parameter MAP_SHARED,
    The fifth parameter represents the file descriptor fd.
     mmap The return value of the function is equal to the actual address obtained after mapping
    */
    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;
}
/*----------------------------------------------------------------------------
 * file transfer
 *----------------------------------------------------------------------------*/


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

    // Allocate a buffer for the input file and read it into the buffer
    // trans->input_buf = axidma_malloc(dev, trans->input_size);
    
    memcpy(trans->input_buf,sbuffer,trans->input_size);
    // printf("sbuffer in is %d",sbuffer[1]);
    
 
    // Execute move
    // 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
    // Write data to output file
  
    // 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;
 
    // Execute move
    // 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;
}

   the reading and writing of the bram register in the middle can interact with the logic end. Because this module should not directly support interrupts and asynchronous notification does not seem to work, gpio interrupts are mounted and some simple things are added accordingly. The specific gpio interrupts are described in a separate article before, which can be referred to below
And then

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


    //Address mapping, enabling reading and writing DMA, specified separately
    axidma_config();
    XDma_Out32(map_base0+4,1);
    //  Parse input parameters
    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;
       }
//

    // Initialize AXIDMA device
    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");

    
    // If tx and rx channels have not been specified, the transceiver channel is obtained
    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 the user does not specify a channel, we assume that the sending and receiving channels are the lowest numbered channels. */
    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;
    // Allocate a buffer for the output file
    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;

}

Keywords: Linux Embedded system Operating System

Added by keefy on Tue, 08 Mar 2022 08:57:45 +0200