diff options
| author | Ataberk <olgunataberk@gmail.com> | 2022-09-25 17:22:03 +0200 |
|---|---|---|
| committer | Ataberk <olgunataberk@gmail.com> | 2022-09-25 17:22:03 +0200 |
| commit | dc0b3db1b4f1895a07e5fe280ee3790e87f97b9f (patch) | |
| tree | b47203aa281bdd959def4451c84d310cd9cf2e12 /sources/xdma_driver/xdma/cdev_bypass.c | |
| download | dram-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.c | 180 |
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); +} |
