专题二:AXI_DMA驱动分析

专题二:AXI_DMA驱动分析

1设备树

Petalinux构建的工程,设备树拥有重写特性,system-user.dtsi可以重写pl.dtsi中的内容

1.1pl.dtsi

/*
 * CAUTION: This file is automatically generated by Xilinx.
 * Version:  
 * Today is: Mon Apr  4 11:11:25 2022
 */


/ {
	amba_pl: amba_pl@0 {
		#address-cells = <2>;
		#size-cells = <2>;
		compatible = "simple-bus";
		ranges ;
		axi_dma_0: dma@80000000 {
			#dma-cells = <1>;
			clock-names = "s_axi_lite_aclk", "m_axi_mm2s_aclk", "m_axi_s2mm_aclk";
			clocks = <&zynqmp_clk 71>, <&zynqmp_clk 71>, <&zynqmp_clk 71>;
			compatible = "xlnx,axi-dma-7.1", "xlnx,axi-dma-1.00.a";
			interrupt-names = "mm2s_introut", "s2mm_introut";
			interrupt-parent = <&gic>;
			interrupts = <0 89 4 0 90 4>;
			reg = <0x0 0x80000000 0x0 0x10000>;
			xlnx,addrwidth = <0x20>;
			xlnx,sg-length-width = <0xe>;
			dma-channel@80000000 {
				compatible = "xlnx,axi-dma-mm2s-channel";
				dma-channels = <0x1>;
				interrupts = <0 89 4>;
				xlnx,datawidth = <0x20>;
				xlnx,device-id = <0x0>;
			};
			dma-channel@80000030 {
				compatible = "xlnx,axi-dma-s2mm-channel";
				dma-channels = <0x1>;
				interrupts = <0 90 4>;
				xlnx,datawidth = <0x20>;
				xlnx,device-id = <0x0>;
			};
		};
	};
};

1.2system-user.dtsi

/include/ "system-conf.dtsi"
/ {

};
/* SD */
&sdhci1 {
	disable-wp;
	no-1-8-v;
};
/* USB */
&dwc3_0 {
	status = "okay";
	dr_mode = "host";
};


&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@80000000 {
        xlnx,device-id = <0x0>;
    };
    dma-channel@80000030 {
        xlnx,device-id = <0x1>;
    };
};

2结构体

2.1struct axidma_device结构体

struct axidma_device {
    int num_devices;                // The number of devices
    unsigned int minor_num;         // The minor number of the device
    dev_t dev_num;                  // The device number of the device
    char *chrdev_name;              // The name of the character device
    struct device *device;          // Device structure for the char device
    struct class *dev_class;        // The device class for the chardevice
    struct cdev chrdev;             // The character device structure

    int num_dma_tx_chans;           // The number of transmit DMA channels
    int num_dma_rx_chans;           // The number of receive DMA channels
    int num_vdma_tx_chans;          // The number of transmit VDMA channels
    int num_vdma_rx_chans;          // The number of receive  VDMA channels
    int num_chans;                  // The total number of DMA channels
    int notify_signal;              // Signal used to notify transfer completion
    struct platform_device *pdev;   // The platofrm device from the device tree
    struct axidma_cb_data *cb_data; // The callback data for each channel
    struct axidma_chan *channels;   // All available channels
    struct list_head dmabuf_list;   // List of allocated DMA buffers
    struct list_head external_dmabufs;  // Buffers allocated in other drivers
};

2.2dev_t结构体

dev_t结构体

在内核中,dev_t结构体用来保存设备编号信息,在linux/type.h中定义,是一个32位的数,12位表示主设备号+20位的次设备号

int MAJOR(dev_t dev)//获得dev的主设备号
int MINOR(dev_t dev)//获得dev的次设备号
dev_t MKDEV(unsignde int major,unsigned int minor)//由主次设备号获得dev_t数据的宏。

2.3device结构体

(5条消息) 阅读Linux设备驱动模型源码之 device结构体成员详解_Qidi_Huang的博客-CSDN博客_device结构体

2.4class结构体

(5条消息) linux class结构体,linux中class_create和device_creat_肖牧之的博客-CSDN博客

2.5cdev结构体

struct cdev {
    struct kobject kobj;                    // 内嵌的kobject对象
    struct module *owner;                   // 所属模块
    const struct file_operations *ops;      // 文件操作结构体
    struct list_head list;                  //linux内核所维护的链表指针
    dev_t dev;                              //设备号
    unsigned int count;                     //设备数目
};

cdev结构体 - bluebluebluesky - 博客园 (cnblogs.com)

2.6platform_device结构体

struct platform_device {
    const char    * name
    u32        id
    struct device    dev
    u32        num_resources
    struct resource    * resource
}

device结构和platform_device结构-xiaohuima_dong-ChinaUnix博客

2.7struct axidma_cb_data结构体

// The data to pass to the DMA transfer completion callback function
struct axidma_cb_data {
    int channel_id;                 // The id of the channel used
    int notify_signal;              // For async, signal to send
    struct task_struct *process;    // The process to send the signal to
    struct completion *comp;        // For sync, the notification to kernel
};
2.7.1struct task_struct结构体

(5条消息) 浅析task_struct结构体_奄奄不息的博客-CSDN博客_task_struct

2.7.2struct completion结构体

(5条消息) 并发控制中的completion_O刺客的博客-CSDN博客_completion

2.8struct 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)
};
2.8.1enum axidma_dir枚举类
enum axidma_dir {
    AXIDMA_WRITE,                   ///< Transmits from memory to a device.
    AXIDMA_READ                     ///< Transmits from a device to memory.
};
2.8.2enum axidma_type枚举类
enum axidma_type {
    AXIDMA_DMA,                     ///< Standard AXI DMA engine
    AXIDMA_VDMA                     ///< Specialized AXI video DMA enginge
};
2.8.3struct dma_chan结构体

(5条消息) DMA 相关的一些结构体_lamdoc的博客-CSDN博客

/**
 * struct dma_chan - devices supply DMA channels, clients use them
 * @device: ptr to the dma device who supplies this channel, always !%NULL
 * @cookie: last cookie value returned to client
 * @chan_id: channel ID for sysfs
 * @dev: class device for sysfs
 * @device_node: used to add this to the device chan list
 * @local: per-cpu pointer to a struct dma_chan_percpu
 * @client-count: how many clients are using this channel
 * @table_count: number of appearances in the mem-to-mem allocation table
 * @private: private data for certain client-channel associations
 */
struct dma_chan {
	struct dma_device *device;
	dma_cookie_t cookie;
 
	/* sysfs */
	int chan_id;
	struct dma_chan_dev *dev;
 
	struct list_head device_node;
	struct dma_chan_percpu __percpu *local;
	int client_count;
	int table_count;
	void *private;
};

2.9struct list_head结构体

Linux内核中的双向链表struct list_head - Cqlismy - 博客园 (cnblogs.com)

在内核源码中,list_head结构体的定义在文件source_code/include/linux/types.h文件中,结构体定义如下:

struct list_head {
    struct list_head *next, *prev;
};
通过这个结构体开始构建链表,然后插入、删除节点,遍历整个链表等,内核已经提供好了现成的调用接口。

2.10struct axidma_dma_allocation结构体

// A structure that represents a DMA buffer allocation
struct axidma_dma_allocation {
    size_t size;                // Size of the buffer
    void *user_addr;            // User virtual address of the buffer
    void *kern_addr;            // Kernel virtual address of the buffer
    dma_addr_t dma_addr;        // DMA bus address of the buffer
    struct list_head list;      // List node pointers for allocation list
};

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

2.12struct axidma_channel_info结构体

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

2.13struct axidma_register_buffer结构体

struct axidma_register_buffer {
    int fd;                         // Anonymous file descritpor for DMA buffer
    size_t size;                    // The size of the external DMA buffer
    void *user_addr;                // User virtual address of the buffer
};

2.14struct axidma_transaction结构体

struct axidma_transaction {
    bool wait;                      // Indicates if the call is blocking
    int channel_id;                 // The id of the DMA channel to use
    void *buf;                      // The buffer used for the transaction
    size_t buf_len;                 // The length of the buffer
};

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

2.16struct axidma_video_transaction结构体

struct axidma_video_transaction {
    int channel_id;             // The id of the DMA channel to transmit video
    int num_frame_buffers;      // The number of frame buffers to use.
    void **frame_buffers;       // The frame buffer addresses to use for video
    size_t width;               // The width of the image in pixels
    size_t height;              // The height of the image in lines
    size_t depth;               // The size of each pixel in bytes
};

2.17struct axidma_transfer结构体

// A convenient structure to pass between prep and start transfer functions
struct axidma_transfer {
    int sg_len;                     // The length of the BD array
    struct scatterlist *sg_list;    // List of buffer descriptors
    bool wait;                      // Indicates if we should wait
    dma_cookie_t cookie;            // The DMA cookie for the transfer
    struct completion comp;         // A completion to use for waiting
    enum axidma_dir dir;            // The direction of the transfer
    enum axidma_type type;          // The type of the transfer (VDMA/DMA)
    int channel_id;                 // The ID of the channel
    int notify_signal;              // The signal to use for async transfers
    struct task_struct *process;    // The process requesting the transfer
    struct axidma_cb_data *cb_data; // The callback data struct

    // VDMA specific fields (kept as union for extensability)
    union {
        struct {
            int width;              // Width of the image in pixels
            int height;             // Height of the image in lines
            int depth;              // Size of each pixel in bytes
        } vdma_tfr;
    };
};

2.18struct scatterlist结构体

(5条消息) struct scatterlist 使用_咸稀饭的博客-CSDN博客_scatterlist结构体声明

struct scatterlist {
     ...
     /* User input members */
    unsigned long   page_link;// pointer to a page, but the bit0 and bit1 have special info.[1]
    unsigned int    offset; // Offset of data buffer in page referred by @page_link
    unsigned int    length; //Length of data
    /* Output value */
    dma_addr_t  dma_address; // this address can be used by device to do DMA 
     ...
};

3分析

axi_dma.c模块完成内核驱动的初始化
/*----------------------------------------------------------------------------
 * Module Initialization and Exit
 *----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------
 * 初始化调用平台总线进行注册platform_driver_register(&axidma_driver);
 *----------------------------------------------------------------------------*/
static int __init axidma_init(void)
{
    return platform_driver_register(&axidma_driver);
}

static void __exit axidma_exit(void)
{
    return platform_driver_unregister(&axidma_driver);
}

module_init(axidma_init);
module_exit(axidma_exit);

MODULE_AUTHOR("Brandon Perez");
MODULE_AUTHOR("Jared Choi");

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Module to provide a userspace interface for transferring "
                   "data from the processor to the logic fabric via AXI DMA.");

axi_dma.c模块中的axidma_driver结构体
static const struct of_device_id axidma_compatible_of_ids[] = {
    { .compatible = "xlnx,axidma-chrdev" },
    {}
};

static struct platform_driver axidma_driver = {
    .driver = {
        .name = MODULE_NAME,
        .owner = THIS_MODULE,
        .of_match_table = axidma_compatible_of_ids,
    },
    .probe = axidma_probe,
    .remove = axidma_remove,
};
当匹配后,回调axi_dma.c模块中的axidma_probe函数

3.1axidma_probe()函数分析

/*----------------------------------------------------------------------------
 * Platform Device Functions
 *----------------------------------------------------------------------------*/

static int axidma_probe(struct platform_device *pdev)
{
/*----------------------------------------------------------------------------
 * 1.变量初始化
 *----------------------------------------------------------------------------*/
    int rc;
    struct axidma_device *axidma_dev;

    // Allocate a AXI DMA device structure to hold metadata about the DMA
    axidma_dev = kmalloc(sizeof(*axidma_dev), GFP_KERNEL);
    if (axidma_dev == NULL) {
        axidma_err("Unable to allocate the AXI DMA device structure.\n");
        return -ENOMEM;
    }
    axidma_dev->pdev = pdev;
/*----------------------------------------------------------------------------
 * 2.AXIdma初始化
 *----------------------------------------------------------------------------*/
    // Initialize the DMA interface
    rc = axidma_dma_init(pdev, axidma_dev);
    if (rc < 0) {
        goto free_axidma_dev;
    }
/*----------------------------------------------------------------------------
 * 3.字符设备初始化
// Default name of the character of the device
#define CHRDEV_NAME                 AXIDMA_DEV_NAME
// Default minor number for the device
#define MINOR_NUMBER                0
// The default number of character devices for DMA
#define NUM_DEVICES                 1

static char *chrdev_name = CHRDEV_NAME;
module_param(chrdev_name, charp, S_IRUGO);

static int minor_num = MINOR_NUMBER;
module_param(minor_num, int, S_IRUGO);
*----------------------------------------------------------------------------*/
    // Assign the character device name, minor number, and number of devices
    axidma_dev->chrdev_name = chrdev_name;
    axidma_dev->minor_num = minor_num;
    axidma_dev->num_devices = NUM_DEVICES;

    // Initialize the character device for the module.
    rc = axidma_chrdev_init(axidma_dev);
    if (rc < 0) {
        goto destroy_dma_dev;
    }
/*----------------------------------------------------------------------------
 * 4.将axidma_dev结构存入平台设备pdev的私有数据区
*----------------------------------------------------------------------------*/
    // Set the private data in the device to the AXI DMA device structure
    dev_set_drvdata(&pdev->dev, axidma_dev);
    return 0;

destroy_dma_dev:
    axidma_dma_exit(axidma_dev);
free_axidma_dev:
    kfree(axidma_dev);
    return -ENOSYS;
}
3.1.2axidma_dma_init(pdev, axidma_dev)函数分析
int axidma_dma_init(struct platform_device *pdev, struct axidma_device *dev)
{
    int rc;
    size_t elem_size;
/*----------------------------------------------------------------------------
 * 1.解析设备树,获取总通道个数
 *	为每个通道的struct axidma_chan *channels结构体分配内存;
 *	为每个通道的struct axidma_cb_data *cb_data结构体分配内存;
 *----------------------------------------------------------------------------*/
    // Get the number of DMA channels listed in the device tree
    dev->num_chans = axidma_of_num_channels(pdev);
    if (dev->num_chans < 0) {
        return dev->num_chans;
    }

    // Allocate an array to store all the channel metdata structures
    elem_size = sizeof(dev->channels[0]);
    dev->channels = kmalloc(dev->num_chans * elem_size, GFP_KERNEL);
    if (dev->channels == NULL) {
        axidma_err("Unable to allocate memory for channel structures.\n");
        return -ENOMEM;
    }

    // Allocate an array to store all callback structures, for async
    elem_size = sizeof(dev->cb_data[0]);
    dev->cb_data = kmalloc(dev->num_chans * elem_size, GFP_KERNEL);
    if (dev->cb_data == NULL) {
        axidma_err("Unable to allocate memory for callback structures.\n");
        rc = -ENOMEM;
        goto free_channels;
    }
/*----------------------------------------------------------------------------
 * 2.解析设备树,获取各个通道的类型、方向,并请求dma_chan结构体
 *----------------------------------------------------------------------------*/
    // Parse the type and direction of each DMA channel from the device tree
    rc = axidma_of_parse_dma_nodes(pdev, dev);
    if (rc < 0) {
        return rc;
    }

    // Exclusively request all of the channels in the device tree entry
    rc = axidma_request_channels(pdev, dev);
    if (rc < 0) {
        goto free_callback_data;
    }

    axidma_info("DMA: Found %d transmit channels and %d receive channels.\n",
                dev->num_dma_tx_chans, dev->num_dma_rx_chans);
    axidma_info("VDMA: Found %d transmit channels and %d receive channels.\n",
                dev->num_vdma_tx_chans, dev->num_vdma_rx_chans);
    return 0;

free_callback_data:
    kfree(dev->cb_data);
free_channels:
    kfree(dev->channels);
    return rc;
}
3.1.2axidma_chrdev_init函数分析
/*----------------------------------------------------------------------------
 * Initialization and Cleanup
 *----------------------------------------------------------------------------*/

int axidma_chrdev_init(struct axidma_device *dev)
{
    int rc;
/*----------------------------------------------------------------------------
 * 1.创建字符设备
 *----------------------------------------------------------------------------*/
    // Store a global pointer to the axidma device
    axidma_dev = dev;

    // Allocate a major and minor number region for the character device
    rc = alloc_chrdev_region(&dev->dev_num, dev->minor_num, dev->num_devices,
                             dev->chrdev_name);
    if (rc < 0) {
        axidma_err("Unable to allocate character device region.\n");
        goto ret;
    }

    // Create a device class for our device
    dev->dev_class = class_create(THIS_MODULE, dev->chrdev_name);
    if (IS_ERR(dev->dev_class)) {
        axidma_err("Unable to create a device class.\n");
        rc = PTR_ERR(dev->dev_class);
        goto free_chrdev_region;
    }

    /* Create a device for our module. This will create a file on the
     * filesystem, under "/dev/dev->chrdev_name". */
    dev->device = device_create(dev->dev_class, NULL, dev->dev_num, NULL,
                                dev->chrdev_name);
    if (IS_ERR(dev->device)) {
        axidma_err("Unable to create a device.\n");
        rc = PTR_ERR(dev->device);
        goto class_cleanup;
    }
/*----------------------------------------------------------------------------
 * 2.注册字符设备和ops结构体
 *----------------------------------------------------------------------------*/
    // Register our character device with the kernel
    cdev_init(&dev->chrdev, &axidma_fops);
    rc = cdev_add(&dev->chrdev, dev->dev_num, dev->num_devices);
    if (rc < 0) {
        axidma_err("Unable to add a character device.\n");
        goto device_cleanup;
    }
/*----------------------------------------------------------------------------
 * 3.创建双向链表
 *----------------------------------------------------------------------------*/
    // Initialize the list for DMA mmap'ed allocations
    INIT_LIST_HEAD(&dev->dmabuf_list);
    INIT_LIST_HEAD(&dev->external_dmabufs);

    return 0;

device_cleanup:
    device_destroy(dev->dev_class, dev->dev_num);
class_cleanup:
    class_destroy(dev->dev_class);
free_chrdev_region:
    unregister_chrdev_region(dev->dev_num, dev->num_devices);
ret:
    return rc;
}

3.2axidma_fops结构

// The file operations for the AXI DMA device
static const struct file_operations axidma_fops = {
    .owner = THIS_MODULE,
    .open = axidma_open,
    .release = axidma_release,
    .mmap = axidma_mmap,
    .unlocked_ioctl = axidma_ioctl,
};
3.2.1axidma_open()函数
static int axidma_open(struct inode *inode, struct file *file)
{
    // Only the root user can open this device, and it must be exclusive
    if (!capable(CAP_SYS_ADMIN)) {
        axidma_err("Only root can open this device.");
        return -EACCES;
    } else if (!(file->f_flags & O_EXCL)) {
        axidma_err("O_EXCL must be specified for open()\n");
        return -EINVAL;
    }
/*----------------------------------------------------------------------------
 * 1.将axidma_dev结构存入文件结构体file的私有数据区
*----------------------------------------------------------------------------*/
    // Place the axidma structure in the private data of the file
    file->private_data = (void *)axidma_dev;
    return 0;
}

3.2.2axidma_release()函数
static int axidma_release(struct inode *inode, struct file *file)
{
    file->private_data = NULL;
    return 0;
}
3.2.3axidma_mmap()函数
static int axidma_mmap(struct file *file, struct vm_area_struct *vma)
{
/*----------------------------------------------------------------------------
 * 1.变量初始化
 *	获取file结构体中设置的私有数据
 *	获取系统分配的vma起始地址和种植地址
*----------------------------------------------------------------------------*/
    int rc;
    struct axidma_device *dev;
    struct axidma_dma_allocation *dma_alloc;

    // Get the axidma device structure
    dev = file->private_data;

    // Allocate a structure to store data about the DMA mapping
    dma_alloc = kmalloc(sizeof(*dma_alloc), GFP_KERNEL);
    if (dma_alloc == NULL) {
        axidma_err("Unable to allocate VMA data structure.");
        rc = -ENOMEM;
        goto ret;
    }

    // Set the user virtual address and the size
    dma_alloc->size = vma->vm_end - vma->vm_start;
    dma_alloc->user_addr = (void *)vma->vm_start;
/*----------------------------------------------------------------------------
 * 2.调用of_dma_configure初始化
*----------------------------------------------------------------------------*/
    // Configure the DMA device
    of_dma_configure(dev->device, NULL);
/*----------------------------------------------------------------------------
 * 3.申请关闭cache
*----------------------------------------------------------------------------*/
    // Allocate the requested region a contiguous and uncached for DMA
    vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
/*----------------------------------------------------------------------------
 * 4.申请连续内存
*----------------------------------------------------------------------------*/
    dma_alloc->kern_addr = dma_alloc_coherent(dev->device, dma_alloc->size,
                                              &dma_alloc->dma_addr, GFP_KERNEL);
    if (dma_alloc->kern_addr == NULL) {
        axidma_err("Unable to allocate contiguous DMA memory region of size "
                   "%zu.\n", dma_alloc->size);
        axidma_err("Please make sure that you specified cma=<size> on the "
                   "kernel command line, and the size is large enough.\n");
        rc = -ENOMEM;
        goto free_vma_data;
    }
/*----------------------------------------------------------------------------
 * 5.Map the region into userspace
*----------------------------------------------------------------------------*/
    // Map the region into userspace
    rc = dma_mmap_coherent(dev->device, vma, dma_alloc->kern_addr,
                           dma_alloc->dma_addr, dma_alloc->size);
    if (rc < 0) {
        axidma_err("Unable to remap address %p to userspace address %p, size "
                   "%zu.\n", dma_alloc->kern_addr, dma_alloc->user_addr,
                   dma_alloc->size);
        goto free_dma_region;
    }
/*----------------------------------------------------------------------------
 * 6.Override the VMA close with our call, so that we can free the DMA region
     * when the memory region is closed. Pass in the data to do so
*----------------------------------------------------------------------------*/
    vma->vm_ops = &axidma_vm_ops;
    vma->vm_private_data = dma_alloc;

    // Add the allocation to the driver's list of DMA buffers
    list_add(&dma_alloc->list, &dev->dmabuf_list);
    return 0;

free_dma_region:
    dma_free_coherent(dev->device, dma_alloc->size, dma_alloc->kern_addr,
                      dma_alloc->dma_addr);
free_vma_data:
    kfree(dma_alloc);
ret:
    return rc;
}

3.2.3.1axidma_vm_ops结构重写
static void axidma_vma_close(struct vm_area_struct *vma)
{
    struct axidma_device *dev;
    struct axidma_dma_allocation *dma_alloc;

    // Get the AXI DMA allocation data and free the DMA buffer
    dev = axidma_dev;
    dma_alloc = vma->vm_private_data;
    dma_free_coherent(dev->device, dma_alloc->size, dma_alloc->kern_addr,
                      dma_alloc->dma_addr);

    // Remove the allocation from the list, and free the structure
    list_del(&dma_alloc->list);
    kfree(dma_alloc);

    return;
}

// The VMA operations for the AXI DMA device
static const struct vm_operations_struct axidma_vm_ops = {
    .close = axidma_vma_close,
};
3.2.4axidma_ioctl()函数
static long axidma_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    long rc;
    size_t size;
    void *__user arg_ptr;
    struct axidma_device *dev;
    struct axidma_num_channels num_chans;
    struct axidma_channel_info usr_chans, kern_chans;
    struct axidma_register_buffer ext_buf;
    struct axidma_transaction trans;
    struct axidma_inout_transaction inout_trans;
    struct axidma_video_transaction video_trans, *__user user_video_trans;
    struct axidma_chan chan_info;
/*----------------------------------------------------------------------------
 * 1.(void __user *)arg 指的是arg值是一个用户空间的地址,不能直接进行拷贝等,要使用例如copy_from_user,copy_to_user等函数。
*----------------------------------------------------------------------------*/
    // Coerce the arguement as a userspace pointer
    arg_ptr = (void __user *)arg;
/*----------------------------------------------------------------------------
 * 2.验证命令的合法性
 
 // The magic number used to distinguish IOCTL's for our device
#define AXIDMA_IOCTL_MAGIC              'W'

// The number of IOCTL's implemented, used for verification
#define AXIDMA_NUM_IOCTLS               10
*----------------------------------------------------------------------------*/
    // Verify that this IOCTL is intended for our device, and is in range
    if (_IOC_TYPE(cmd) != AXIDMA_IOCTL_MAGIC) {
        axidma_err("IOCTL command magic number does not match.\n");
        return -ENOTTY;
    } else if (_IOC_NR(cmd) >= AXIDMA_NUM_IOCTLS) {
        axidma_err("IOCTL command is out of range for this device.\n");
        return -ENOTTY;
    }

    // Verify the input argument
    if ((_IOC_DIR(cmd) & _IOC_READ)) {
        if (!axidma_access_ok(arg_ptr, _IOC_SIZE(cmd), false)) {
            return -EFAULT;
        }
    } else if (_IOC_DIR(cmd) & _IOC_WRITE) {
        if (!axidma_access_ok(arg_ptr, _IOC_SIZE(cmd), true)) {
            return -EFAULT;
        }
    }

    // Get the axidma device from the file
    dev = file->private_data;
/*----------------------------------------------------------------------------
 * 2.开始解析命令
*----------------------------------------------------------------------------*/
    // Perform the specified command
    switch (cmd) {
        case AXIDMA_GET_NUM_DMA_CHANNELS:
            axidma_get_num_channels(dev, &num_chans);
            if (copy_to_user(arg_ptr, &num_chans, sizeof(num_chans)) != 0) {
                axidma_err("Unable to copy channel info to userspace for "
                           "AXIDMA_GET_NUM_DMA_CHANNELS.\n");
                return -EFAULT;
            }
            rc = 0;
            break;

        case AXIDMA_GET_DMA_CHANNELS:
            if (copy_from_user(&usr_chans, arg_ptr, sizeof(usr_chans)) != 0) {
                axidma_err("Unable to copy channel buffer address from "
                           "userspace for AXIDMA_GET_DMA_CHANNELS.\n");
                return -EFAULT;
            }

            // Copy the channels array to userspace
            axidma_get_num_channels(dev, &num_chans);
            axidma_get_channel_info(dev, &kern_chans);
            size = num_chans.num_channels * sizeof(kern_chans.channels[0]);
            if (copy_to_user(usr_chans.channels, kern_chans.channels, size)) {
                axidma_err("Unable to copy channel ids to userspace for "
                           "AXIDMA_GET_DMA_CHANNELS.\n");
                return -EFAULT;
            }

            rc = 0;
            break;

        case AXIDMA_SET_DMA_SIGNAL:
            rc = axidma_set_signal(dev, arg);
            break;

        case AXIDMA_REGISTER_BUFFER:
            if (copy_from_user(&ext_buf, arg_ptr, sizeof(ext_buf)) != 0) {
                axidma_err("Unable to copy external buffer info from userspace "
                           "for AXIDMA_REGISTER_BUFFER.\n");
                return -EFAULT;
            }
            rc = axidma_get_external(dev, &ext_buf);
            break;

        case AXIDMA_DMA_READ:
            if (copy_from_user(&trans, arg_ptr, sizeof(trans)) != 0) {
                axidma_err("Unable to copy transfer info from userspace for "
                           "AXIDMA_DMA_READ.\n");
                return -EFAULT;
            }
            rc = axidma_read_transfer(dev, &trans);
            break;

        case AXIDMA_DMA_WRITE:
            if (copy_from_user(&trans, arg_ptr, sizeof(trans)) != 0) {
                axidma_err("Unable to copy transfer info from userspace for "
                           "AXIDMA_DMA_WRITE.\n");
                return -EFAULT;
            }
            rc = axidma_write_transfer(dev, &trans);
            break;

        case AXIDMA_DMA_READWRITE:
            if (copy_from_user(&inout_trans, arg_ptr,
                               sizeof(inout_trans)) != 0) {
                axidma_err("Unable to copy transfer info from userspace for "
                           "AXIDMA_DMA_READWRITE.\n");
                return -EFAULT;
            }
            rc = axidma_rw_transfer(dev, &inout_trans);
            break;

        case AXIDMA_DMA_VIDEO_READ:
            if (copy_from_user(&video_trans, arg_ptr,
                               sizeof(video_trans)) != 0) {
                axidma_err("Unable to copy transfer info from userspace for "
                           "AXIDMA_DMA_VIDEO_READ.\n");
                return -EFAULT;
            }

            // Allocate a kernel-space array for the frame buffers
            size = video_trans.num_frame_buffers *
                   sizeof(video_trans.frame_buffers[0]);
            video_trans.frame_buffers = kmalloc(size, GFP_KERNEL);
            if (video_trans.frame_buffers == NULL) {
                axidma_err("Unable to allocate array for the frame buffers.\n");
                return -ENOMEM;
            }

            // Copy the frame buffer array from user space to kernel space
            user_video_trans = (struct axidma_video_transaction *__user)arg_ptr;
            if (!copy_from_user(video_trans.frame_buffers,
                        user_video_trans->frame_buffers, size) != 0) {
                axidma_err("Unable to copy the frame buffer array from "
                        "userspace for AXIDMA_VIDEO_READ.\n");
                kfree(video_trans.frame_buffers);
                return -EFAULT;
            }

            rc = axidma_video_transfer(dev, &video_trans, AXIDMA_READ);
            kfree(video_trans.frame_buffers);
            break;

        case AXIDMA_DMA_VIDEO_WRITE:
            if (copy_from_user(&video_trans, arg_ptr,
                               sizeof(video_trans)) != 0) {
                axidma_err("Unable to copy transfer info from userspace for "
                           "AXIDMA_VIDEO_WRITE.\n");
                return -EFAULT;
            }

            // Allocate a kernel-space array for the frame buffers
            size = video_trans.num_frame_buffers *
                   sizeof(video_trans.frame_buffers[0]);
            video_trans.frame_buffers = kmalloc(size, GFP_KERNEL);
            if (video_trans.frame_buffers == NULL) {
                axidma_err("Unable to allocate array for the frame buffers.\n");
                return -ENOMEM;
            }

            // Copy the frame buffer array from user space to kernel space
            user_video_trans = (struct axidma_video_transaction *__user)arg_ptr;
            if (!copy_from_user(video_trans.frame_buffers,
                        user_video_trans->frame_buffers, size) != 0) {
                axidma_err("Unable to copy the frame buffer array from "
                        "userspace for AXIDMA_VIDEO_READ.\n");
                kfree(video_trans.frame_buffers);
                return -EFAULT;
            }

            rc = axidma_video_transfer(dev, &video_trans, AXIDMA_WRITE);
            kfree(video_trans.frame_buffers);
            break;

        case AXIDMA_STOP_DMA_CHANNEL:
            if (copy_from_user(&chan_info, arg_ptr, sizeof(chan_info)) != 0) {
                axidma_err("Unable to channel info from userspace for "
                           "AXIDMA_STOP_DMA_CHANNEL.\n");
            }
            rc = axidma_stop_channel(dev, &chan_info);
            break;

        case AXIDMA_UNREGISTER_BUFFER:
            rc = axidma_put_external(dev, (void *)arg);
            break;

        // Invalid command (already handled in preamble)
        default:
            return -ENOTTY;
    }

    return rc;
}
3.2.4.1命令定义
/**
 * Returns the number of available DMA channels in the system.
 *
 * This writes to the structure given as input by the user, telling the numbers for all DMA channels in the system.
 *
 * Outputs:
 *  - num_channels - The total number of DMA channels in the system.
 *  - num_dma_tx_channels - The number of transmit AXI DMA channels
 *  - num_dma_rx_channels - The number of receive AXI DMA channels
 *  - num_vdma_tx_channels - The number of transmit AXI VDMA channels
 *  - num_vdma_rx_channels - The number of receive AXI VDMA channels
 **/
#define AXIDMA_GET_NUM_DMA_CHANNELS     _IOW(AXIDMA_IOCTL_MAGIC, 0, \
                                             struct axidma_num_channels)

/**
 * Returns information on all DMA channels in the system.
 *
 * This function writes to the array specified in the pointer given to the
 * struct structures representing all data about a given DMA channel (device id,
 * direction, etc.). The array must be able to hold at least the number of
 * elements returned by AXIDMA_GET_NUM_DMA_CHANNELS.
 *
 * The important piece of information returned in the id for the channels.
 * This, along with the direction and type, uniquely identifies a DMA channel
 * in the system, and this is how you refer to a channel in later calls.
 *
 * Inputs:
 *  - channels - A pointer to a region of memory that can hold at least
 *               num_channels * sizeof(struct axidma_chan) bytes.
 *
 * Outputs:
 *  - channels - An array of structures of the following format:
 *  - An array of structures with the following fields:
 *       - dir - The direction of the channel (either read or write).
 *       - type - The type of the channel (either normal DMA or video DMA).
 *       - channel_id - The integer id for the channel.
 *       - chan - This field has no meaning and can be safely ignored.
 **/
#define AXIDMA_GET_DMA_CHANNELS         _IOR(AXIDMA_IOCTL_MAGIC, 1, \
                                             struct axidma_channel_info)

/**
 * Register the given signal to be sent when DMA transactions complete.
 *
 * This function sets up an asynchronous signal to be delivered to the invoking
 * process any DMA subsystem completes a transaction. If the user dispatches
 * an asynchronous transaction, and wants to know when it completes, they must
 * register a signal to be delivered.
 *
 * The signal must be one of the POSIX real time signals. So, it must be
 * between the signals SIGRTMIN and SIGRTMAX. The kernel will deliver the
 * channel id back to the userspace signal handler.
 *
 * This can be used to have a user callback function, effectively emulating an
 * interrupt in userspace. The user must register their signal handler for
 * the specified signal for this to happen.
 *
 * Inputs:
 *  - signal - The signal to send upon transaction completion.
 **/
#define AXIDMA_SET_DMA_SIGNAL           _IO(AXIDMA_IOCTL_MAGIC, 2)

/**
 * Registers the external DMA buffer with the driver, making it available to be
 * used in DMA transfers.
 *
 * Sometimes, it may be useful to use a DMA buffer that was allocated by another
 * driver. The best example of this is if you want to interact with a
 * frame buffer allocated by a DRM driver.
 *
 * However, the driver cannot simply access this DMA buffer as is. The user must
 * register the buffer with the driver, so that it can get the information from
 * the driver that originally allocated it.
 *
 * This uses the kernel's DMA buffer sharing API. Thus, the user must first tell
 * the other driver to export the DMA buffer for sharing, typically done through
 * an IOCTL. This will return a file descriptor, which the user must pass into
 * this function, along with the virtual address in userspace.
 *
 * Inputs:
 *  - fd - File descriptor corresponding to the DMA buffer share.
 *  - size - The size of the DMA buffer in bytes.
 *  - user_addr - The user virtual address of the buffer.
 **/
#define AXIDMA_REGISTER_BUFFER          _IOR(AXIDMA_IOCTL_MAGIC, 3, \
                                             struct axidma_register_buffer)

/**
 * Receives the data from the logic fabric into the processing system.
 *
 * This function receives data from a device on the PL fabric through
 * AXI DMA into memory. The device id should be an id that is returned by the
 * get dma channels ioctl. The user can specify if the call should wait for the
 * transfer to complete, or if it should return immediately.
 *
 * The specified buffer must be within an address range that was allocated by a
 * call to mmap with the AXI DMA device. Also, the buffer must be able to hold
 * at least `buf_len` bytes.
 *
 * Inputs:
 *  - wait - Indicates if the call should be blocking or non-blocking
 *  - channel_id - The id for the channel you want receive data over.
 *  - buf - The address of the buffer you want to receive the data in.
 *  - buf_len - The number of bytes to receive.
 **/
#define AXIDMA_DMA_READ                 _IOR(AXIDMA_IOCTL_MAGIC, 4, \
                                             struct axidma_transaction)

/**
 * Sends the given data from the processing system to the logic fabric.
 *
 * This function sends data from memory to a device on the PL fabric through
 * AXI DMA. The device id should be an id that is returned by the get dma
 * channels ioctl. The user can specify if the call should wait for the transfer
 * to complete, or if it should return immediately.
 *
 * The specified buffer must be within an address range that was allocated by a
 * call to mmap with the AXI DMA device. Also, the buffer must be able to hold
 * at least `buf_len` bytes.
 *
 * Inputs:
 *  - wait - Indicates if the call should be blocking or non-blocking
 *  - channel_id - The id for the channel you want to send data over.
 *  - buf - The address of the data you want to send.
 *  - buf_len - The number of bytes to send.
 **/
#define AXIDMA_DMA_WRITE                _IOR(AXIDMA_IOCTL_MAGIC, 5, \
                                             struct axidma_transaction)

/**
 * Performs a two-way transfer between the logic fabric and processing system.
 *
 * This function both sends data to the PL and receives data from the PL fabric.
 * It is intended for DMA transfers that are tightly coupled together
 * (e.g. converting an image to grayscale on the PL fabric). The device id's for
 * both channels should be ones that are returned by the get dma ioctl. The user
 * can specify if the call should block. If it blocks, it will wait until the
 * receive transaction completes.
 *
 * The specified buffers must be within an address range that was allocated by a
 * call to mmap with the AXI DMA device. Also, each buffer must be able to hold
 * at least the number of bytes that are being transfered.
 *
 * Inputs:
 *  - wait - Indicates if the call should be blocking or non-blocking
 *  - tx_channel_id - The id for the channel you want transmit data on.
 *  - tx_buf - The address of the data you want to send.
 *  - tx_buf_len - The number of bytes you want to send.
 *  - rx_buf - The address of the buffer you want to receive data in.
 *  - rx_buf_len - The number of bytes you want to receive.
 **/
#define AXIDMA_DMA_READWRITE            _IOR(AXIDMA_IOCTL_MAGIC, 6, \
                                             struct axidma_inout_transaction)

/**
 * Performs frame-buffer based transfers from a camera on the fabric.
 *
 * This function performs a video transfer from the logic fabric. It receives
 * the given buffers from logic fabric (intended for a camera pipeline). When it
 * reaches the end of the buffers, it loops back and receives the data again in
 * the first buffer. This is used for frame-buffer based cameras.
 *
 * All of the frame buffers must be within an address range that was allocated
 * by a call to mmap with the AXI DMA device. Also, each buffer must
 * be able to hold a frame of (width * height * depth) bytes. The input array of
 * buffers must be a memory location that holds `num_frame_buffers` addresses.
 *
 * This call is always non-blocking as the VDMA engine will run forever. In
 * order to end the transaction, you must make a call to the stop dma channel
 * ioctl.
 *
 * Inputs:
 *  - channel_id - The id for the channel you want to send data over.
 *  - num_frame_buffers - The number of frame buffers you're using.
 *  - frame_buffers - An array of the frame buffer addresses.
 *  - width - The width of the frame (image) in pixels.
 *  - height - The height of the frame in lines.
 *  - depth - The size of each pixel in the frame in bytes.
 **/
#define AXIDMA_DMA_VIDEO_READ           _IOR(AXIDMA_IOCTL_MAGIC, 7, \
                                             struct axidma_video_transaction)

/**
 * Performs frame-buffer based transfers to a display on the logic fabric.
 *
 * This function performs a video transfer to the logic fabric. It sends
 * the given buffers to logic fabric (intended for a display pipeline). When it
 * reaches the end of the buffers, it loops back and re-sends the first buffer.
 * This is used for frame-buffer based graphics.
 *
 * All of the frame buffers must be within an address range that was allocated
 * by a call to mmap with the AXI DMA device. Also, each buffer must
 * be able to hold a frame of (width * height * depth) bytes. The input array of
 * buffers must be a memory location that holds `num_frame_buffers` addresses.
 *
 * This call is always non-blocking as the VDMA engine will run forever. In
 * order to end the transaction, you must make a call to the stop dma channel
 * ioctl.
 *
 * Inputs:
 *  - channel_id - The id for the channel you want to send data over.
 *  - num_frame_buffers - The number of frame buffers you're using.
 *  - frame_buffers - An array of the frame buffer addresses.
 *  - width - The width of the frame (image) in pixels.
 *  - height - The height of the frame in lines.
 *  - depth - The size of each pixel in the frame in bytes.
 **/
#define AXIDMA_DMA_VIDEO_WRITE          _IOR(AXIDMA_IOCTL_MAGIC, 8, \
                                             struct axidma_video_transaction)

/**
 * Stops all transactions on the given DMA channel.
 *
 * This function flushes all in-progress transactions, and discards all pending
 * transactions on the given DMA channel. The specified id should be one that
 * was returned by the get dma channels ioctl.
 *
 * Inputs:
 *  - dir - The direction of the channel (either read or write).
 *  - type - The type of the channel (either normal DMA or video DMA).
 *  - channel_id - The integer id for the channel.
 *  - chan - This field is unused an can be safely left uninitialized.
 */
#define AXIDMA_STOP_DMA_CHANNEL         _IOR(AXIDMA_IOCTL_MAGIC, 9, \
                                             struct axidma_chan)

/**
 * Unregisters and external DMA buffer previously registered through an
 * AXIDMA_REGISTER_BUFFER IOCTL
 *
 * All external buffers that are registered by the user must be freed in order
 * to ensure that all kernel data structures are properly cleaned up. This
 * removes the external DMA buffer from the driver, so it can no longer be
 * used in DMA transfers after this call.
 *
 * Inputs:
 *  - user_addr - The user virtual address of the external DMA buffer.
 **/
#define AXIDMA_UNREGISTER_BUFFER        _IO(AXIDMA_IOCTL_MAGIC, 10)

4分析示例

4.1示例
/*----------------------------------------------------------------------------
 * 在Axi_transfer中存在如下用法: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");
    }

4.2命令参数说明
参数说明:
/**
 * Performs a two-way transfer between the logic fabric and processing system.
 *
 * This function both sends data to the PL and receives data from the PL fabric.
 * It is intended for DMA transfers that are tightly coupled together
 * (e.g. converting an image to grayscale on the PL fabric). The device id's for
 * both channels should be ones that are returned by the get dma ioctl. The user
 * can specify if the call should block. If it blocks, it will wait until the
 * receive transaction completes.
 *
 * The specified buffers must be within an address range that was allocated by a
 * call to mmap with the AXI DMA device. Also, each buffer must be able to hold
 * at least the number of bytes that are being transfered.
 *
 * Inputs:
 *  - wait - Indicates if the call should be blocking or non-blocking
 *  - tx_channel_id - The id for the channel you want transmit data on.
 *  - tx_buf - The address of the data you want to send.
 *  - tx_buf_len - The number of bytes you want to send.
 *  - rx_buf - The address of the buffer you want to receive data in.
 *  - rx_buf_len - The number of bytes you want to receive.
 **/
#define AXIDMA_DMA_READWRITE            _IOR(AXIDMA_IOCTL_MAGIC, 6, \
                                             struct axidma_inout_transaction)
4.3结构体说明
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
}inout_trans;
4.4命令解析说明
实现方式:     
		case AXIDMA_DMA_READWRITE:
            if (copy_from_user(&inout_trans, arg_ptr,
                               sizeof(inout_trans)) != 0) {
                axidma_err("Unable to copy transfer info from userspace for "
                           "AXIDMA_DMA_READWRITE.\n");
                return -EFAULT;
            }
            rc = axidma_rw_transfer(dev, &inout_trans);
            break;
4.5axidma_rw_transfer()函数分析
/* Transfers data from the given source buffer out to the AXI DMA device, and
 * places the data received into the receive buffer. */
int axidma_rw_transfer(struct axidma_device *dev,
                       struct axidma_inout_transaction *trans)
{
/*----------------------------------------------------------------------------
 * 1.初始化相关变量
 *----------------------------------------------------------------------------*/
    int rc;
    struct axidma_chan *tx_chan, *rx_chan;
    struct scatterlist tx_sg_list, rx_sg_list;
    struct axidma_transfer tx_tfr, rx_tfr;

    // Get the transmit and receive channels with the given ids.
    tx_chan = axidma_get_chan(dev, trans->tx_channel_id);
    if (tx_chan == NULL || tx_chan->dir != AXIDMA_WRITE) {
        axidma_err("Invalid device id %d for DMA transmit channel.\n",
                   trans->tx_channel_id);
        return -ENODEV;
    }

    rx_chan = axidma_get_chan(dev, trans->rx_channel_id);
    if (rx_chan == NULL || rx_chan->dir != AXIDMA_READ) {
        axidma_err("Invalid device id %d for DMA receive channel.\n",
                   trans->rx_channel_id);
        return -ENODEV;
    }

    // Setup the scatter-gather list for the transfers (only one entry)
    sg_init_table(&tx_sg_list, 1);
    rc = axidma_init_sg_entry(dev, &tx_sg_list, 0, trans->tx_buf,
                              trans->tx_buf_len);
    if (rc < 0) {
        return rc;
    }
    sg_init_table(&rx_sg_list, 1);
    rc = axidma_init_sg_entry(dev, &rx_sg_list, 0, trans->rx_buf,
                              trans->rx_buf_len);
    if (rc < 0) {
        return rc;
    }

    // Setup receive and trasmit transfer structures for DMA
    tx_tfr.sg_list = &tx_sg_list,
    tx_tfr.sg_len = 1,
    tx_tfr.dir = tx_chan->dir,
    tx_tfr.type = tx_chan->type,
    tx_tfr.wait = false,
    tx_tfr.channel_id = trans->tx_channel_id,
    tx_tfr.notify_signal = dev->notify_signal,
    tx_tfr.process = get_current(),
    tx_tfr.cb_data = &dev->cb_data[trans->tx_channel_id];
    // FIXME: FIXME: FIXME: Temporary
    tx_tfr.vdma_tfr.height = 1080;
    tx_tfr.vdma_tfr.width = 1920;
    tx_tfr.vdma_tfr.depth = 4;

    rx_tfr.sg_list = &rx_sg_list,
    rx_tfr.sg_len = 1,
    rx_tfr.dir = rx_chan->dir,
    rx_tfr.type = rx_chan->type,
    rx_tfr.wait = trans->wait,
    rx_tfr.channel_id = trans->rx_channel_id,
    rx_tfr.notify_signal = dev->notify_signal,
    rx_tfr.process = get_current(),
    rx_tfr.cb_data = &dev->cb_data[trans->rx_channel_id];
    rx_tfr.vdma_tfr.height = 1080;
    rx_tfr.vdma_tfr.width = 1920;
    rx_tfr.vdma_tfr.depth = 4;
/*----------------------------------------------------------------------------
 * 2.待分析.......
 *----------------------------------------------------------------------------*/
    // Prep both the receive and transmit transfers
    rc = axidma_prep_transfer(tx_chan, &tx_tfr);
    if (rc < 0) {
        return rc;
    }
    rc = axidma_prep_transfer(rx_chan, &rx_tfr);
    if (rc < 0) {
        return rc;
    }

    // Submit both transfers to the DMA engine, and wait on the receive transfer
    rc = axidma_start_transfer(tx_chan, &tx_tfr);
    if (rc < 0) {
        return rc;
    }
    rc = axidma_start_transfer(rx_chan, &rx_tfr);
    if (rc < 0) {
        return rc;
    }

    return 0;
}
  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
AXI DMA是Xilinx公司提供的一种高性能DMA控制器IP,用于实现高速数据传输。在使用AXI DMA时,需要在Linux系统中加载相应的驱动程序,才能对其进行操作。以下是AXI DMA驱动程序的基本步骤: 1. 获取DMA的设备号:在Linux系统中,通过设备文件/dev下的名称来标识每一个硬件设备。在使用AXI DMA时,需要获取其设备号,可以通过以下命令获取: ``` cat /proc/devices | grep xdma ``` 其中xdmaAXI DMA的设备名,命令会返回类似于“246 xdma”这样的字符串,其中246就是该设备的主设备号。 2. 创建设备节点:在Linux系统中,每一个设备都需要创建一个对应的设备节点,以便用户空间程序可以通过该节点来访问设备。可以使用以下命令创建设备节点: ``` mknod /dev/xdma0 c 246 0 ``` 其中xdma0是设备节点的名称,246是设备的主设备号,0是设备的次设备号。 3. 打开设备:可以使用标准的文件操作函数open()来打开设备节点,并获取一个文件描述符。例如: ``` int fd = open("/dev/xdma0", O_RDWR); ``` 4. 配置AXI DMA:在使用AXI DMA进行数据传输之前,需要对其进行一些配置,例如设置传输方向、数据长度、中断使能等。可以通过ioctl()函数调用设备驱动程序提供的一些接口来进行配置。例如: ``` struct dma_config cfg; cfg.direction = DMA_MEM_TO_DEV; cfg.length = 1024; cfg.interrupt_enable = 1; ioctl(fd, XDMA_IOCTL_CONFIG, &cfg); ``` 其中XDMA_IOCTL_CONFIG是设备驱动程序提供的一个控制命令,用于配置AXI DMA的相关参数。 5. 启动传输:配置完成后,可以通过write()函数向设备节点发送数据,或者通过read()函数从设备节点读取数据。例如: ``` char buf[1024]; write(fd, buf, 1024); ``` 在传输完成后,可以通过设备驱动程序提供的一些接口来获取传输状态或者等待传输完成。例如: ``` int status; while (1) { ioctl(fd, XDMA_IOCTL_WAIT_TX_DONE, &status); if (status == XDMA_STAT_SUCCESS) break; } ``` 其中XDMA_IOCTL_WAIT_TX_DONE是设备驱动程序提供的一个控制命令,用于等待传输完成,并获取传输状态。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值