online compiler and debugger for c/c++

code. compile. run. debug. share.
Source Code    Language
/* DMA Proxy * * This module is designed to be a small example of a DMA device driver that is * a client to the DMA Engine using the AXI DMA driver. It serves as a proxy for * kernel space DMA control to a user space application. * * A zero copy scheme is provided by allowing user space to mmap a kernel allocated * memory region into user space, referred to as a proxy channel interface. The * ioctl function is provided to start a DMA transfer which then blocks until the * transfer is complete. No input arguments are being used in the ioctl function. * * There is an associated user space application, dma_proxy_test.c, and dma_proxy.h * that works with this device driver. * * The hardware design was tested with an AXI DMA without scatter gather and * with the transmit channel looped back to the receive channel. * * This driver is character driver which creates 2 devices that user space can * access for each DMA channel, /dev/dma_proxy_rx and /dev/dma_proxy_tx. * An internal test mode is provided to allow it to be self testing without the * need for a user space application and this mode is good for making bigger * changes to this driver. * * This driver is designed to be simple to help users get familiar with how to * use the DMA driver provided by Xilinx which uses the Linux DMA Engine. The * performance of this example is not expected to be high without more work. * * To use this driver a node must be added into the device tree. Add the * following node while adjusting the dmas property to match the name of * the AXI DMA node. * * dma_proxy { * compatible ="xlnx,dma_proxy"; * dmas = <&axi_dma_0 0 * &axi_dma_0 1>; * dma-names = "dma_proxy_tx", "dma_proxy_rx"; * } * */ #include <linux/dmaengine.h> #include <linux/module.h> #include <linux/version.h> #include <linux/kernel.h> #include <linux/dma-mapping.h> #include <linux/slab.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/fs.h> #include <linux/workqueue.h> #include <linux/platform_device.h> #include <linux/of_dma.h> #include "dma-proxy.h" #undef INTERNAL_TEST MODULE_LICENSE("GPL"); #define ENABLE_TX_CHANNEL 0 //1 #define ENABLE_TESTS ENABLE_TX_CHANNEL #define DRIVER_NAME "dma_proxy" #if ENABLE_TX_CHANNEL #define CHANNEL_COUNT 2 #define TX_CHANNEL 1 #else #define CHANNEL_COUNT 1 #endif #define RX_CHANNEL 0 #define ERROR -1 #define NOT_LAST_CHANNEL 0 #define LAST_CHANNEL 1 /* The following module parameter controls if the internal test runs when the module is inserted. */ static unsigned internal_test = 0; module_param(internal_test, int, S_IRUGO); /* The following data structure represents a single channel of DMA, transmit or receive in the case * when using AXI DMA. It contains all the data to be maintained for the channel. */ struct dma_proxy_channel { struct dma_proxy_channel_interface *interface_p; /* user to kernel space interface */ dma_addr_t interface_phys_addr; struct device *proxy_device_p; /* character device support */ struct device *dma_device_p; dev_t dev_node; struct cdev cdev; struct class *class_p; struct dma_chan *channel_p; /* dma support */ struct completion cmp; dma_cookie_t cookie; dma_addr_t dma_handle; u32 direction; /* DMA_MEM_TO_DEV or DMA_DEV_TO_MEM */ struct scatterlist sglist; }; /* Allocate the channels for this example statically rather than dynamically for simplicity. */ static struct dma_proxy_channel channels[CHANNEL_COUNT]; /* Handle a callback and indicate the DMA transfer is complete to another * thread of control */ static void sync_callback(void *completion) { /* Indicate the DMA transaction completed to allow the other * thread of control to finish processing */ complete(completion); } /* Prepare a DMA buffer to be used in a DMA transaction, submit it to the DMA engine * to ibe queued and return a cookie that can be used to track that status of the * transaction */ static void start_transfer(struct dma_proxy_channel *pchannel_p) { enum dma_ctrl_flags flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; struct dma_async_tx_descriptor *chan_desc; struct dma_proxy_channel_interface *interface_p = pchannel_p->interface_p; struct dma_device *dma_device = pchannel_p->channel_p->device; /* For now use a single entry in a scatter gather list just for future * flexibility for scatter gather. */ sg_init_table(&pchannel_p->sglist, 1); sg_dma_address(&pchannel_p->sglist) = pchannel_p->dma_handle; sg_dma_len(&pchannel_p->sglist) = interface_p->length; chan_desc = dma_device->device_prep_slave_sg(pchannel_p->channel_p, &pchannel_p->sglist, 1, pchannel_p->direction, flags, NULL); /* Make sure the operation was completed successfully */ if (!chan_desc) { printk(KERN_ERR "dmaengine_prep*() error\n"); } else { chan_desc->callback = sync_callback; chan_desc->callback_param = &pchannel_p->cmp; /* Initialize the completion for the transfer and before using it * then submit the transaction to the DMA engine so that it's queued * up to be processed later and get a cookie to track it's status */ init_completion(&pchannel_p->cmp); pchannel_p->cookie = dmaengine_submit(chan_desc); if (dma_submit_error(pchannel_p->cookie)) { printk("Submit error\n"); return; } /* Start the DMA transaction which was previously queued up in the DMA engine */ dma_async_issue_pending(pchannel_p->channel_p); } } /* Wait for a DMA transfer that was previously submitted to the DMA engine */ static void wait_for_transfer(struct dma_proxy_channel *pchannel_p) { unsigned long timeout = msecs_to_jiffies(3000); enum dma_status status; pchannel_p->interface_p->status = PROXY_BUSY; /* Wait for the transaction to complete, or timeout, or get an error */ timeout = wait_for_completion_timeout(&pchannel_p->cmp, timeout); status = dma_async_is_tx_complete(pchannel_p->channel_p, pchannel_p->cookie, NULL, NULL); if (timeout == 0) { pchannel_p->interface_p->status = PROXY_TIMEOUT; printk(KERN_ERR "DMA timed out\n"); } else if (status != DMA_COMPLETE) { pchannel_p->interface_p->status = PROXY_ERROR; printk(KERN_ERR "DMA returned completion callback status of: %s\n", status == DMA_ERROR ? "error" : "in progress"); } else pchannel_p->interface_p->status = PROXY_NO_ERROR; } /* Perform the DMA transfer for the channel, starting it then blocking to wait for completion. */ static void transfer(struct dma_proxy_channel *pchannel_p) { /* The physical address of the buffer in the interface is needed for the dma transfer * as the buffer may not be the first data in the interface */ pchannel_p->dma_handle = (dma_addr_t)(pchannel_p->interface_phys_addr + offsetof(struct dma_proxy_channel_interface, buffer)); start_transfer(pchannel_p); wait_for_transfer(pchannel_p); } #if ENABLE_TESTS //-- test functions /* The following functions are designed to test the driver from within the device * driver without any user space. */ static void tx_test(struct work_struct *unused) { channels[TX_CHANNEL].interface_p->length = TEST_SIZE; transfer(&channels[TX_CHANNEL]); } static void test(void) { int i; struct work_struct work; printk("Starting internal test\n"); /* Initialize the buffers for the test */ for (i = 0; i < TEST_SIZE; i++) { channels[TX_CHANNEL].interface_p->buffer[i] = i; channels[RX_CHANNEL].interface_p->buffer[i] = 0; } /* Since the transfer function is blocking the transmit channel is started from a worker * thread */ INIT_WORK(&work, tx_test); schedule_work(&work); /* Receive the data that was just sent and looped back */ channels[RX_CHANNEL].interface_p->length = TEST_SIZE; transfer(&channels[RX_CHANNEL]); /* Verify the receiver buffer matches the transmit buffer to * verify the transfer was good */ for (i = 0; i < TEST_SIZE; i++) if (channels[TX_CHANNEL].interface_p->buffer[i] != channels[RX_CHANNEL].interface_p->buffer[i]) { printk("buffers not equal, first index = %d\n", i); break; } printk("Internal test complete\n"); } #endif //-- test functions /* Map the memory for the channel interface into user space such that user space can * access it using coherent memory which will be non-cached for s/w coherent systems * such as Zynq 7K or the current default for Zynq MPSOC. MPSOC can be h/w coherent * when set up and then the memory will be cached. */ static int mmap(struct file *file_p, struct vm_area_struct *vma) { struct dma_proxy_channel *pchannel_p = (struct dma_proxy_channel *)file_p->private_data; return dma_mmap_coherent(pchannel_p->dma_device_p, vma, pchannel_p->interface_p, pchannel_p->interface_phys_addr, vma->vm_end - vma->vm_start); } /* Open the device file and set up the data pointer to the proxy channel data for the * proxy channel such that the ioctl function can access the data structure later. */ static int local_open(struct inode *ino, struct file *file) { file->private_data = container_of(ino->i_cdev, struct dma_proxy_channel, cdev); return 0; } /* Close the file and there's nothing to do for it */ static int release(struct inode *ino, struct file *file) { struct dma_proxy_channel *pchannel_p = (struct dma_proxy_channel *)file->private_data; struct dma_device *dma_device = pchannel_p->channel_p->device; /* Stop all the activity when the channel is closed assuming this * may help if the application is aborted without normal closure */ /*dma_device->device_terminate_all(pchannel_p->channel_p);*/ return 0; } /* Perform I/O control to start a DMA transfer. */ static long ioctl(struct file *file, unsigned int unused , unsigned long arg) { struct dma_proxy_channel *pchannel_p = (struct dma_proxy_channel *)file->private_data; /* Perform the DMA transfer on the specified channel blocking til it completes */ transfer(pchannel_p); return 0; } static struct file_operations dm_fops = { .owner = THIS_MODULE, .open = local_open, .release = release, .unlocked_ioctl = ioctl, .mmap = mmap }; /* Initialize the driver to be a character device such that is responds to * file operations. */ static int cdevice_init(struct dma_proxy_channel *pchannel_p, char *name) { int rc; //char device_name[32] = "dma_proxy"; // got warning due to strcat... char device_name[256] = "dma_proxy"; static struct class *local_class_p = NULL; /* Allocate a character device from the kernel for this driver. */ rc = alloc_chrdev_region(&pchannel_p->dev_node, 0, 1, "dma_proxy"); if (rc) { dev_err(pchannel_p->dma_device_p, "unable to get a char device number\n"); return rc; } /* Initialize the device data structure before registering the character * device with the kernel. */ cdev_init(&pchannel_p->cdev, &dm_fops); pchannel_p->cdev.owner = THIS_MODULE; rc = cdev_add(&pchannel_p->cdev, pchannel_p->dev_node, 1); if (rc) { dev_err(pchannel_p->dma_device_p, "unable to add char device\n"); goto init_error1; } /* Only one class in sysfs is to be created for multiple channels, * create the device in sysfs which will allow the device node * in /dev to be created */ if (!local_class_p) { local_class_p = class_create(THIS_MODULE, DRIVER_NAME); if (IS_ERR(pchannel_p->dma_device_p->class)) { dev_err(pchannel_p->dma_device_p, "unable to create class\n"); rc = ERROR; goto init_error2; } } pchannel_p->class_p = local_class_p; /* Create the device node in /dev so the device is accessible * as a character device */ strcat(device_name, name); pchannel_p->proxy_device_p = device_create(pchannel_p->class_p, NULL, pchannel_p->dev_node, NULL, name); if (IS_ERR(pchannel_p->proxy_device_p)) { dev_err(pchannel_p->dma_device_p, "unable to create the device\n"); goto init_error3; } return 0; init_error3: class_destroy(pchannel_p->class_p); init_error2: cdev_del(&pchannel_p->cdev); init_error1: unregister_chrdev_region(pchannel_p->dev_node, 1); return rc; } /* Exit the character device by freeing up the resources that it created and * disconnecting itself from the kernel. */ static void cdevice_exit(struct dma_proxy_channel *pchannel_p, int last_channel) { /* Take everything down in the reverse order * from how it was created for the char device */ if (pchannel_p->proxy_device_p) { device_destroy(pchannel_p->class_p, pchannel_p->dev_node); if (last_channel) class_destroy(pchannel_p->class_p); cdev_del(&pchannel_p->cdev); unregister_chrdev_region(pchannel_p->dev_node, 1); } } /* Create a DMA channel by getting a DMA channel from the DMA Engine and then setting * up the channel as a character device to allow user space control. */ static int create_channel(struct platform_device *pdev, struct dma_proxy_channel *pchannel_p, char *name, u32 direction) { int rc; /* Request the DMA channel from the DMA engine and then use the device from * the channel for the proxy channel also. */ //pchannel_p->channel_p = dma_request_slave_channel(&pdev->dev, name); //if (!pchannel_p->channel_p) struct dma_chan *ch = dma_request_chan(&pdev->dev, name); if (IS_ERR(ch)) { dev_err(pchannel_p->dma_device_p, "DMA channel request error: %li\n", PTR_ERR(ch) ); return ERROR; } pchannel_p->channel_p = ch; pchannel_p->dma_device_p = &pdev->dev; /* Initialize the character device for the dma proxy channel */ rc = cdevice_init(pchannel_p, name); if (rc) return rc; pchannel_p->direction = direction; /* Allocate DMA memory for the proxy channel interface. */ pchannel_p->interface_p = (struct dma_proxy_channel_interface *) dmam_alloc_coherent(pchannel_p->dma_device_p, sizeof(struct dma_proxy_channel_interface), &pchannel_p->interface_phys_addr, GFP_KERNEL); printk(KERN_INFO "Allocating uncached memory at virtual address 0x%p, physical address 0x%p\n", pchannel_p->interface_p, (void *)pchannel_p->interface_phys_addr); if (!pchannel_p->interface_p) { dev_err(pchannel_p->dma_device_p, "DMA allocation error\n"); return ERROR; } return 0; } /* Initialize the dma proxy device driver module. */ static int dma_proxy_probe(struct platform_device *pdev) { int rc; //printk(KERN_INFO "dma_proxy module initialized\n"); printk(KERN_INFO "dma_proxy module trying init...\n"); #if ENABLE_TX_CHANNEL /* Create the transmit and receive channels. */ rc = create_channel(pdev, &channels[TX_CHANNEL], "dma_proxy_tx", DMA_MEM_TO_DEV); if (rc) return rc; #endif //rc = create_channel(pdev, &channels[RX_CHANNEL], "dma_proxy_rx", DMA_DEV_TO_MEM); // DEBUG: did we name this w rong in the device tree, or does cat list dma-names without any comma in between or so? rc = create_channel(pdev, &channels[RX_CHANNEL], "dma_proxy_rx", DMA_DEV_TO_MEM); if (rc) return rc; #if ENABLE_TESTS if (internal_test) test(); #endif printk(KERN_INFO "dma_proxy module init ok\n"); return 0; } /* Exit the dma proxy device driver module. */ static int dma_proxy_remove(struct platform_device *pdev) { int i; printk(KERN_INFO "dma_proxy module exited\n"); /* Take care of the char device infrastructure for each * channel except for the last channel. Handle the last * channel seperately. */ for (i = 0; i < CHANNEL_COUNT - 1; i++) if (channels[i].proxy_device_p) cdevice_exit(&channels[i], NOT_LAST_CHANNEL); cdevice_exit(&channels[i], LAST_CHANNEL); /* Take care of the DMA channels and the any buffers allocated * for the DMA transfers. The DMA buffers are using managed * memory such that it's automatically done. */ for (i = 0; i < CHANNEL_COUNT; i++) if (channels[i].channel_p) { channels[i].channel_p->device->device_terminate_all(channels[i].channel_p); dma_release_channel(channels[i].channel_p); } return 0; } static const struct of_device_id dma_proxy_of_ids[] = { { .compatible = "xlnx,dma_proxy",}, {} }; static struct platform_driver dma_proxy_driver = { .driver = { .name = "dma_proxy_driver", .owner = THIS_MODULE, .of_match_table = dma_proxy_of_ids, }, .probe = dma_proxy_probe, .remove = dma_proxy_remove, }; static int __init dma_proxy_init(void) { return platform_driver_register(&dma_proxy_driver); } static void __exit dma_proxy_exit(void) { platform_driver_unregister(&dma_proxy_driver); } module_init(dma_proxy_init) module_exit(dma_proxy_exit) MODULE_AUTHOR("Xilinx, Inc."); MODULE_DESCRIPTION("DMA Proxy Prototype"); MODULE_LICENSE("GPL v2");

Compiling Program...

Command line arguments:
Standard Input: Interactive Console Text
×

                

                

Program is not being debugged. Click "Debug" button to start program in debug mode.

#FunctionFile:Line
VariableValue
RegisterValue
ExpressionValue