专题一: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;
}