专题二:AXI

编程入门 行业动态 更新时间:2024-10-07 16:17:28

<a href=https://www.elefans.com/category/jswz/34/1770001.html style=专题二:AXI"/>

专题二:AXI

专题二: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 devicesunsigned int minor_num;         // The minor number of the devicedev_t dev_num;                  // The device number of the devicechar *chrdev_name;              // The name of the character devicestruct device *device;          // Device structure for the char devicestruct class *dev_class;        // The device class for the chardevicestruct cdev chrdev;             // The character device structureint num_dma_tx_chans;           // The number of transmit DMA channelsint num_dma_rx_chans;           // The number of receive DMA channelsint num_vdma_tx_chans;          // The number of transmit VDMA channelsint num_vdma_rx_chans;          // The number of receive  VDMA channelsint num_chans;                  // The total number of DMA channelsint notify_signal;              // Signal used to notify transfer completionstruct platform_device *pdev;   // The platofrm device from the device treestruct axidma_cb_data *cb_data; // The callback data for each channelstruct axidma_chan *channels;   // All available channelsstruct list_head dmabuf_list;   // List of allocated DMA buffersstruct 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)

2.6platform_device结构体

struct platform_device {const char    * nameu32        idstruct device    devu32        num_resourcesstruct 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 usedint notify_signal;              // For async, signal to sendstruct task_struct *process;    // The process to send the signal tostruct 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 channelenum axidma_type type;          // The DMA type of the channelint channel_id;                 // The identifier for the deviceconst 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 engineAXIDMA_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)

在内核源码中,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 buffervoid *user_addr;            // User virtual address of the buffervoid *kern_addr;            // Kernel virtual address of the bufferdma_addr_t dma_addr;        // DMA bus address of the bufferstruct 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 systemint num_dma_tx_channels;        // DMA transmit channels availableint num_dma_rx_channels;        // DMA receive channels availableint num_vdma_tx_channels;       // VDMA transmit channels availableint 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 buffersize_t size;                    // The size of the external DMA buffervoid *user_addr;                // User virtual address of the buffer
};

2.14struct axidma_transaction结构体

struct axidma_transaction {bool wait;                      // Indicates if the call is blockingint channel_id;                 // The id of the DMA channel to usevoid *buf;                      // The buffer used for the transactionsize_t buf_len;                 // The length of the buffer
};

2.15struct axidma_inout_transaction结构体

struct axidma_inout_transaction {bool wait;                      // Indicates if the call is blockingint tx_channel_id;              // The id of the transmit DMA channelvoid *tx_buf;                   // The buffer containing the data to sendsize_t tx_buf_len;              // The length of the transmit bufferint rx_channel_id;              // The id of the receive DMA channelvoid *rx_buf;                   // The buffer to place the data insize_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 videoint num_frame_buffers;      // The number of frame buffers to use.void **frame_buffers;       // The frame buffer addresses to use for videosize_t width;               // The width of the image in pixelssize_t height;              // The height of the image in linessize_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 arraystruct scatterlist *sg_list;    // List of buffer descriptorsbool wait;                      // Indicates if we should waitdma_cookie_t cookie;            // The DMA cookie for the transferstruct completion comp;         // A completion to use for waitingenum axidma_dir dir;            // The direction of the transferenum axidma_type type;          // The type of the transfer (VDMA/DMA)int channel_id;                 // The ID of the channelint notify_signal;              // The signal to use for async transfersstruct task_struct *process;    // The process requesting the transferstruct 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 pixelsint height;             // Height of the image in linesint 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_linkunsigned 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[] = {{ patible = "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 DMAaxidma_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 interfacerc = 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                 1static 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 devicesaxidma_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 structuredev_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 treedev->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 structureselem_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 asyncelem_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 treerc = axidma_of_parse_dma_nodes(pdev, dev);if (rc < 0) {return rc;}// Exclusively request all of the channels in the device tree entryrc = 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 deviceaxidma_dev = dev;// Allocate a major and minor number region for the character devicerc = 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 devicedev->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 kernelcdev_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 allocationsINIT_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 exclusiveif (!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 filefile->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 structuredev = file->private_data;// Allocate a structure to store data about the DMA mappingdma_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 sizedma_alloc->size = vma->vm_end - vma->vm_start;dma_alloc->user_addr = (void *)vma->vm_start;
/*----------------------------------------------------------------------------* 2.调用of_dma_configure初始化
*----------------------------------------------------------------------------*/// Configure the DMA deviceof_dma_configure(dev->device, NULL);
/*----------------------------------------------------------------------------* 3.申请关闭cache
*----------------------------------------------------------------------------*/// Allocate the requested region a contiguous and uncached for DMAvma->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 userspacerc = 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 bufferslist_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 bufferdev = 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 structurelist_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 pointerarg_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 rangeif (_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 argumentif ((_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 filedev = file->private_data;
/*----------------------------------------------------------------------------* 2.开始解析命令
*----------------------------------------------------------------------------*/// Perform the specified commandswitch (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 userspaceaxidma_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 bufferssize = 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 spaceuser_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 bufferssize = 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 spaceuser_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 transferrc = 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 blockingint tx_channel_id;              // The id of the transmit DMA channelvoid *tx_buf;                   // The buffer containing the data to sendsize_t tx_buf_len;              // The length of the transmit bufferint rx_channel_id;              // The id of the receive DMA channelvoid *rx_buf;                   // The buffer to place the data insize_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 DMAtx_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: Temporarytx_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 transfersrc = 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 transferrc = 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;
}

更多推荐

专题二:AXI

本文发布于:2024-02-14 07:30:16,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1762483.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:专题   AXI

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!