aboutsummaryrefslogtreecommitdiffstats
path: root/sources/xdma_driver/xdma/cdev_bypass.c
diff options
context:
space:
mode:
authorAtaberk <olgunataberk@gmail.com>2022-09-25 17:22:03 +0200
committerAtaberk <olgunataberk@gmail.com>2022-09-25 17:22:03 +0200
commitdc0b3db1b4f1895a07e5fe280ee3790e87f97b9f (patch)
treeb47203aa281bdd959def4451c84d310cd9cf2e12 /sources/xdma_driver/xdma/cdev_bypass.c
downloaddram-bender-dc0b3db1b4f1895a07e5fe280ee3790e87f97b9f.tar.gz
Initial commit
Diffstat (limited to 'sources/xdma_driver/xdma/cdev_bypass.c')
-rw-r--r--sources/xdma_driver/xdma/cdev_bypass.c180
1 files changed, 180 insertions, 0 deletions
diff --git a/sources/xdma_driver/xdma/cdev_bypass.c b/sources/xdma_driver/xdma/cdev_bypass.c
new file mode 100644
index 0000000..4b84526
--- /dev/null
+++ b/sources/xdma_driver/xdma/cdev_bypass.c
@@ -0,0 +1,180 @@
+/*
+ * This file is part of the Xilinx DMA IP Core driver for Linux
+ *
+ * Copyright (c) 2016-present, Xilinx, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+#include "libxdma_api.h"
+#include "xdma_cdev.h"
+
+#define write_register(v,mem,off) iowrite32(v, mem)
+
+static int copy_desc_data(struct xdma_transfer *transfer, char __user *buf,
+ size_t *buf_offset, size_t buf_size)
+{
+ int i;
+ int copy_err;
+ int rc = 0;
+
+ BUG_ON(!buf);
+ BUG_ON(!buf_offset);
+
+ /* Fill user buffer with descriptor data */
+ for (i = 0; i < transfer->desc_num; i++) {
+ if (*buf_offset + sizeof(struct xdma_desc) <= buf_size) {
+ copy_err = copy_to_user(&buf[*buf_offset],
+ transfer->desc_virt + i,
+ sizeof(struct xdma_desc));
+
+ if (copy_err) {
+ dbg_sg("Copy to user buffer failed\n");
+ *buf_offset = buf_size;
+ rc = -EINVAL;
+ } else {
+ *buf_offset += sizeof(struct xdma_desc);
+ }
+ } else {
+ rc = -ENOMEM;
+ }
+ }
+
+ return rc;
+}
+
+static ssize_t char_bypass_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct xdma_dev *xdev;
+ struct xdma_engine *engine;
+ struct xdma_cdev *xcdev = (struct xdma_cdev *)file->private_data;
+ struct xdma_transfer *transfer;
+ struct list_head *idx;
+ size_t buf_offset = 0;
+ int rc = 0;
+
+ rc = xcdev_check(__func__, xcdev, 1);
+ if (rc < 0)
+ return rc;
+ xdev = xcdev->xdev;
+ engine = xcdev->engine;
+
+ dbg_sg("In char_bypass_read()\n");
+
+ if (count & 3) {
+ dbg_sg("Buffer size must be a multiple of 4 bytes\n");
+ return -EINVAL;
+ }
+
+ if (!buf) {
+ dbg_sg("Caught NULL pointer\n");
+ return -EINVAL;
+ }
+
+ if (xdev->bypass_bar_idx < 0) {
+ dbg_sg("Bypass BAR not present - unsupported operation\n");
+ return -ENODEV;
+ }
+
+ spin_lock(&engine->lock);
+
+ if (!list_empty(&engine->transfer_list)) {
+ list_for_each(idx, &engine->transfer_list) {
+ transfer = list_entry(idx, struct xdma_transfer, entry);
+
+ rc = copy_desc_data(transfer, buf, &buf_offset, count);
+ }
+ }
+
+ spin_unlock(&engine->lock);
+
+ if (rc < 0)
+ return rc;
+ else
+ return buf_offset;
+}
+
+static ssize_t char_bypass_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct xdma_dev *xdev;
+ struct xdma_engine *engine;
+ struct xdma_cdev *xcdev = (struct xdma_cdev *)file->private_data;
+
+ u32 desc_data;
+ u32 *bypass_addr;
+ size_t buf_offset = 0;
+ int rc = 0;
+ int copy_err;
+
+ rc = xcdev_check(__func__, xcdev, 1);
+ if (rc < 0)
+ return rc;
+ xdev = xcdev->xdev;
+ engine = xcdev->engine;
+
+ if (count & 3) {
+ dbg_sg("Buffer size must be a multiple of 4 bytes\n");
+ return -EINVAL;
+ }
+
+ if (!buf) {
+ dbg_sg("Caught NULL pointer\n");
+ return -EINVAL;
+ }
+
+ if (xdev->bypass_bar_idx < 0) {
+ dbg_sg("Bypass BAR not present - unsupported operation\n");
+ return -ENODEV;
+ }
+
+ dbg_sg("In char_bypass_write()\n");
+
+ spin_lock(&engine->lock);
+
+ /* Write descriptor data to the bypass BAR */
+ bypass_addr = (u32 *)xdev->bar[xdev->bypass_bar_idx];
+ bypass_addr += engine->bypass_offset;
+ while (buf_offset < count) {
+ copy_err = copy_from_user(&desc_data, &buf[buf_offset],
+ sizeof(u32));
+ if (!copy_err) {
+ write_register(desc_data, bypass_addr, bypass_addr - engine->bypass_offset);
+ buf_offset += sizeof(u32);
+ rc = buf_offset;
+ } else {
+ dbg_sg("Error reading data from userspace buffer\n");
+ rc = -EINVAL;
+ break;
+ }
+ }
+
+ spin_unlock(&engine->lock);
+
+
+ return rc;
+}
+
+
+/*
+ * character device file operations for bypass operation
+ */
+
+static const struct file_operations bypass_fops = {
+ .owner = THIS_MODULE,
+ .open = char_open,
+ .release = char_close,
+ .read = char_bypass_read,
+ .write = char_bypass_write,
+ .mmap = bridge_mmap,
+};
+
+void cdev_bypass_init(struct xdma_cdev *xcdev)
+{
+ cdev_init(&xcdev->cdev, &bypass_fops);
+}