Petalinux构建的工程,设备树拥有重写特性,system-user.dtsi可以重写pl.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>;
};
};
};
};
/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>;
};
};
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
};
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数据的宏。
(5条消息) 阅读Linux设备驱动模型源码之 device结构体成员详解_Qidi_Huang的博客-CSDN博客_device结构体
(5条消息) linux class结构体,linux中class_create和device_creat_肖牧之的博客-CSDN博客
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)
struct platform_device {
const char * name
u32 id
struct device dev
u32 num_resources
struct resource * resource
}
device结构和platform_device结构-xiaohuima_dong-ChinaUnix博客
// 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
};
(5条消息) 浅析task_struct结构体_奄奄不息的博客-CSDN博客_task_struct
(5条消息) 并发控制中的completion_O刺客的博客-CSDN博客_completion
// 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)
};
enum axidma_dir {
AXIDMA_WRITE, ///< Transmits from memory to a device.
AXIDMA_READ ///< Transmits from a device to memory.
};
enum axidma_type {
AXIDMA_DMA, ///< Standard AXI DMA engine
AXIDMA_VDMA ///< Specialized AXI video DMA enginge
};
(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;
};
Linux内核中的双向链表struct list_head - Cqlismy - 博客园 (cnblogs.com)
在内核源码中,list_head结构体的定义在文件source_code/include/linux/types.h文件中,结构体定义如下:
struct list_head {
struct list_head *next, *prev;
};
通过这个结构体开始构建链表,然后插入、删除节点,遍历整个链表等,内核已经提供好了现成的调用接口。
// 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
};
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
};
struct axidma_channel_info {
struct axidma_chan *channels; // Metadata about all available channels
};
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
};
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
};
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
};
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
};
// 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;
};
};
(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
...
};
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函数
/*----------------------------------------------------------------------------
* 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;
}
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;
}
/*----------------------------------------------------------------------------
* 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;
}
// 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,
};
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;
}
static int axidma_release(struct inode *inode, struct file *file)
{
file->private_data = NULL;
return 0;
}
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= 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;
}
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,
};
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;
}
/**
* 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)
/*----------------------------------------------------------------------------
* 在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");
}
参数说明:
/**
* 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)
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;
实现方式:
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;
/* 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;
}