axidma_tansfer.c分析

专题一:axidma_tansfer.c分析

1关键结构体与结构体关系说明

1.1struct dma_transfer

struct dma_transfer {
    int input_fd;           // 准备写入的文件描述符
    int input_channel;      // 用于写入数据的DMA通道
    int input_size;         // 准备写入的数据大小
    void *input_buf;        // 暂存写入数据的缓冲区
    int output_fd;          // 用于读出文件的文件描述符
    int output_channel;     // 用于发送数据的DMA通道
    int output_size;        // 发送数据的大小
    void *output_buf;       // 发送数据的临时缓冲区
};

1.2struct axidma_dev

typedef struct axidma_dev* axidma_dev_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
};

1.3typedef struct array

typedef struct array {
    int len;        ///< Length of the array
    int *data;      ///< Pointer to the memory buffer for the array
} array_t;

1.4typedef struct dma_channel

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

1.5enum axidma_dir & enum axidma_type

enum axidma_dir {
    AXIDMA_WRITE,                   ///< Transmits from memory to a device.
    AXIDMA_READ                     ///< Transmits from a device to memory.
};
enum axidma_type {
    AXIDMA_DMA,                     ///< Standard AXI DMA engine
    AXIDMA_VDMA                     ///< Specialized AXI video DMA enginge
};
typedef void (*axidma_cb_t)(int channel_id, void *data);

在这里插入图片描述

1.6struct stat

struct stat {  
               dev_t     st_dev;     /* ID of device containing file */  
               ino_t     st_ino;     /* inode number */  
               mode_t    st_mode;    /* protection */  
               nlink_t   st_nlink;   /* number of hard links */  
               uid_t     st_uid;     /* user ID of owner */  
               gid_t     st_gid;     /* group ID of owner */  
               dev_t     st_rdev;    /* device ID (if special file) */  
               off_t     st_size;    /* total size, in bytes */  
               blksize_t st_blksize; /* blocksize for file system I/O */  
               blkcnt_t  st_blocks;  /* number of 512B blocks allocated */  
               time_t    st_atime;   /* time of last access */  
               time_t    st_mtime;   /* time of last modification */  
               time_t    st_ctime;   /* time of last status change */  
           };  
参考:https://blog.csdn.net/feglass/article/details/46426085

1.7struct axidma_chan

在这里插入图片描述

// TODO: Channel really should not be here
struct axidma_chan {
    enum axidma_dir dir;            // The DMA direction of the channel
    enum axidma_type type;          // The DMA type of the channel
    int channel_id;                 // The identifier for the device
    const char *name;               // Name of the channel (ignore)
    struct dma_chan *chan;          // The DMA channel (ignore)
};

1.8struct axidma_num_channels

struct axidma_num_channels {
    int num_channels;               // Total DMA channels in the system
    int num_dma_tx_channels;        // DMA transmit channels available
    int num_dma_rx_channels;        // DMA receive channels available
    int num_vdma_tx_channels;       // VDMA transmit channels available
    int num_vdma_rx_channels;       // VDMA receive channels available
};

1.9struct axidma_channel_info

struct axidma_channel_info {
    struct axidma_chan *channels;   // Metadata about all available channels
};

1.10struct axidma_inout_transaction

struct axidma_inout_transaction {
    bool wait;                      // Indicates if the call is blocking
    int tx_channel_id;              // The id of the transmit DMA channel
    void *tx_buf;                   // The buffer containing the data to send
    size_t tx_buf_len;              // The length of the transmit buffer
    int rx_channel_id;              // The id of the receive DMA channel
    void *rx_buf;                   // The buffer to place the data in
    size_t rx_buf_len;              // The length of the receive buffer
};

2Main()函数分析

在这里插入图片描述

/*----------------------------------------------------------------------------
 * Main
 *----------------------------------------------------------------------------*/

int main(int argc, char **argv)
{
 /*----------------------------------------------------------------------------
 * 1.声明一系列结构体变量、指针变量等
 *----------------------------------------------------------------------------*/   
    int rc;
    char *input_path, *output_path;
    axidma_dev_t axidma_dev;
    struct stat input_stat;
    struct dma_transfer trans;
    const array_t *tx_chans, *rx_chans;

/*----------------------------------------------------------------------------
 * 2.对输入的参数进行解析,将输入的地址、DMAchannel相关信息分别放入对应的局部变量中
 *----------------------------------------------------------------------------*/
    memset(&trans, 0, sizeof(trans));
    if (parse_args(argc, argv, &input_path, &output_path, &trans.input_channel,
                   &trans.output_channel, &trans.output_size) < 0) {
        rc = 1;
        goto ret;
    }
/*----------------------------------------------------------------------------
 * 3.打开文件
 *----------------------------------------------------------------------------*/
    // Try opening the input and output images
    trans.input_fd = open(input_path, O_RDONLY);
    if (trans.input_fd < 0) {
        perror("Error opening input file");
        rc = 1;
        goto ret;
    }
    trans.output_fd = open(output_path, O_WRONLY|O_CREAT|O_TRUNC,
                     S_IWUSR|S_IRUSR|S_IRGRP|S_IWGRP|S_IROTH);
    if (trans.output_fd < 0) {
        perror("Error opening output file");
        rc = -1;
        goto close_input;
    }
/*----------------------------------------------------------------------------
 * 4.初始化AXIDMA设备
 *返回值为:axidma_dev_t axidma_dev;即,返回AXIDMA设备
 *----------------------------------------------------------------------------*/
    // Initialize the 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;
    }
/*----------------------------------------------------------------------------
 * 5.获取待写入的文件大小
 *----------------------------------------------------------------------------*/
    // Get the size of the input file
    if (fstat(trans.input_fd, &input_stat) < 0) {
        perror("Unable to get file statistics");
        rc = 1;
        goto destroy_axidma;
    }

    // If the output size was not specified by the user, set it to the default
    trans.input_size = input_stat.st_size;
    if (trans.output_size == -1) {
        trans.output_size = trans.input_size;
    }
/*----------------------------------------------------------------------------
 * 6.获取待tx缓冲区和rx缓冲区
 *    const array_t *axidma_get_dma_tx(axidma_dev_t dev)
 *    {
 *        return &dev->dma_tx_chans;
 *    }
 *
 *    const array_t *axidma_get_dma_rx(axidma_dev_t dev)
 *    {
 *        return &dev->dma_rx_chans;
  *   }
 *----------------------------------------------------------------------------*/
    // Get the tx and rx channels if they're not already specified
    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;
    }
/*----------------------------------------------------------------------------
 * 6.如果用户没有指定dma通道,则使用设备树中注册的dma通道,tx_channel为0,rx_channel为1
 *----------------------------------------------------------------------------*/
    /* If the user didn't specify the channels, we assume that the transmit and
     * receive channels are the lowest numbered ones. */
    if (trans.input_channel == -1 && trans.output_channel == -1) {
        trans.input_channel = tx_chans->data[0];
        trans.output_channel = rx_chans->data[0];
    }
    printf("AXI DMA File Transfer Info:\n");
    printf("\tTransmit Channel: %d\n", trans.input_channel);
    printf("\tReceive Channel: %d\n", trans.output_channel);
    printf("\tInput File Size: %.2f Mb\n", BYTE_TO_MB(trans.input_size));
    printf("\tOutput File Size: %.2f Mb\n\n", BYTE_TO_MB(trans.output_size));
/*----------------------------------------------------------------------------
 * 7.传输数据
 *----------------------------------------------------------------------------*/
    // Transfer the file over the AXI DMA
    rc = transfer_file(axidma_dev, &trans, output_path);
    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;
}

3axidma_init()函数分析

// The standard name for the AXI DMA device
#define AXIDMA_DEV_NAME     "axidma"

// The standard path to the AXI DMA device
#define AXIDMA_DEV_PATH     ("/dev/" AXIDMA_DEV_NAME)
/* Initializes the AXI DMA device, returning a new handle to the
 * axidma_device. */
struct axidma_dev *axidma_init()
{
/*----------------------------------------------------------------------------
 * 0.在头文件中定义了struct axidma_dev axidma_dev = {0};全局变量
 *	显然,当前axidma_dev没有初始化
 *----------------------------------------------------------------------------*/
    assert(!axidma_dev.initialized);
/*----------------------------------------------------------------------------
 * 1.打开AXIDMA设备
 *----------------------------------------------------------------------------*/
    // 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;
    }
/*----------------------------------------------------------------------------
 * 2.该函数完成axidma_dev结构体的填充
 *----------------------------------------------------------------------------*/
    // 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. */
/*----------------------------------------------------------------------------
 * 3.设置信号量用于说明DMA操作是否完成
 *----------------------------------------------------------------------------*/
    if (setup_dma_callback(&axidma_dev) < 0) {
        close(axidma_dev.fd);
        return NULL;
    }
/*----------------------------------------------------------------------------
 * 4.axidma_dev结构体的填充完成
 *----------------------------------------------------------------------------*/
    // Return the AXI DMA device to the user
    axidma_dev.initialized = true;
    return &axidma_dev;
}

4probe_channels()函数分析

/* 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)
{
/*----------------------------------------------------------------------------
 * 1.变量声明
 *----------------------------------------------------------------------------*/
    int rc;
    struct axidma_chan *channels;
    struct axidma_num_channels num_chan;
    struct axidma_channel_info channel_info;
/*----------------------------------------------------------------------------
 * 2.根据ioctl获取dma通道的相关信息,主要是通道的数量信息
 *----------------------------------------------------------------------------*/
    // 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;
    }
/*----------------------------------------------------------------------------
 * 3.根据DMA通道的数量信息获取,在堆中为每个通道分配空间,本次申请了一个dma_tx通道和一个dma_rx通道
 *----------------------------------------------------------------------------*/
    // Allocate an array to hold the channel meta-data
    channels = malloc(num_chan.num_channels * sizeof(channels[0]));
    if (channels == NULL) {
        return -ENOMEM;
    }
/*----------------------------------------------------------------------------
 * 4.利用Ioctl,向设备树中定义的DMA通道的特性传递给channel_info结构体,包括通道类型、方向
 *以及申请的dma_chan结构体
 *----------------------------------------------------------------------------*/
    // 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;
    }
/*----------------------------------------------------------------------------
 * 5.填充axidma_dev_t dev中的dma_tx_chans、dma_rx_chans
 *----------------------------------------------------------------------------*/
    // Extract the channel id's, and organize them by type
    rc = categorize_channels(dev, channels, &num_chan);
    free(channels);

    return rc;
}

5categorize_channels()函数分析

/* 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)
{
/*----------------------------------------------------------------------------
 * 1.变量声明
 *----------------------------------------------------------------------------*/
    int i;
    struct axidma_chan *chan;
    dma_channel_t *dma_chan;
/*----------------------------------------------------------------------------
 * 2.为axidma_dev_t dev中的channels分配空间
 *----------------------------------------------------------------------------*/
    // 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;
    }
/*----------------------------------------------------------------------------
 * 3.为axidma_dev_t dev中的dma_tx.data、dma_rx.data的地址
 *----------------------------------------------------------------------------*/
    // 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;
    }
/*----------------------------------------------------------------------------
 * 4.为axidma_dev_t dev中的vdma_tx、vdma_rx缓冲区的地址
 *----------------------------------------------------------------------------*/
    // 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;
    }
/*----------------------------------------------------------------------------
 * 5.为axidma_dev_t dev中的dma_tx_chans、dma_rx_chans等设置len变量、data变量
 *----------------------------------------------------------------------------*/
    // 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;
}

6.setup_dma_callback()函数分析

/* 似乎时设置信号,当DMA完成后,会由内核发送信号量*/
// 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;
}

7transfer_file()函数分析

/*----------------------------------------------------------------------------
 * DMA File Transfer Functions
 *----------------------------------------------------------------------------*/

static int transfer_file(axidma_dev_t dev, struct dma_transfer *trans,
                         char *output_path)
{
    int rc;
/*----------------------------------------------------------------------------
 * 1.利用重写对的axidma_malloc函数,连续分配接收缓冲区 trans->input_buf和发送缓冲区trans->output_buf
 *----------------------------------------------------------------------------*/
    // Allocate a buffer for the input file, and read it into the buffer
    trans->input_buf = axidma_malloc(dev, trans->input_size);
    if (trans->input_buf == NULL) {
        fprintf(stderr, "Failed to allocate the input buffer.\n");
        rc = -ENOMEM;
        goto ret;
    }
    rc = robust_read(trans->input_fd, trans->input_buf, trans->input_size);
    if (rc < 0) {
        perror("Unable to read in input buffer.\n");
        axidma_free(dev, trans->input_buf, trans->input_size);
        return rc;
    }

    // Allocate a buffer for the output file
    trans->output_buf = axidma_malloc(dev, trans->output_size);
    if (trans->output_buf == NULL) {
        rc = -ENOMEM;
        goto free_input_buf;
    }
/*----------------------------------------------------------------------------
 * 2.执行DMA操作
 *----------------------------------------------------------------------------*/
    // Perform the transfer
    // Perform the main transaction
    rc = axidma_twoway_transfer(dev, trans->input_channel, trans->input_buf,
            trans->input_size, trans->output_channel, trans->output_buf,
            trans->output_size, true);
    if (rc < 0) {
        fprintf(stderr, "DMA read write transaction failed.\n");
        goto free_output_buf;
    }

    // Write the data to the output file
    printf("Writing output data to `%s`.\n", output_path);
    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;
}

8axidma_malloc()函数分析

/* 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;
/*----------------------------------------------------------------------------
 * 1.该函数调用驱动中的mmap函数
 *----------------------------------------------------------------------------*/
    // 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;
}

9.axidma_twoway_transfer()函数分析

/* 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, int rx_channel, void *rx_buf, size_t rx_len, 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);
/*----------------------------------------------------------------------------
 * 1.设置DMA搬移参数
 *----------------------------------------------------------------------------*/
    // 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;
/*----------------------------------------------------------------------------
 * 2.调用DMA驱动中的ioctl完成数据搬移
 *----------------------------------------------------------------------------*/
    // 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;
}
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值