From dc0b3db1b4f1895a07e5fe280ee3790e87f97b9f Mon Sep 17 00:00:00 2001 From: Ataberk Date: Sun, 25 Sep 2022 17:22:03 +0200 Subject: Initial commit --- sources/xdma_driver/xdma/cdev_ctrl.c | 267 +++++++++++++++++++++++++++++++++++ 1 file changed, 267 insertions(+) create mode 100644 sources/xdma_driver/xdma/cdev_ctrl.c (limited to 'sources/xdma_driver/xdma/cdev_ctrl.c') diff --git a/sources/xdma_driver/xdma/cdev_ctrl.c b/sources/xdma_driver/xdma/cdev_ctrl.c new file mode 100644 index 0000000..6699f5c --- /dev/null +++ b/sources/xdma_driver/xdma/cdev_ctrl.c @@ -0,0 +1,267 @@ +/* + * 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ + +#include +#include "version.h" +#include "xdma_cdev.h" +#include "cdev_ctrl.h" + +/* + * character device file operations for control bus (through control bridge) + */ +static ssize_t char_ctrl_read(struct file *fp, char __user *buf, size_t count, + loff_t *pos) +{ + struct xdma_cdev *xcdev = (struct xdma_cdev *)fp->private_data; + struct xdma_dev *xdev; + void *reg; + u32 w; + int rv; + + rv = xcdev_check(__func__, xcdev, 0); + if (rv < 0) + return rv; + xdev = xcdev->xdev; + + /* only 32-bit aligned and 32-bit multiples */ + if (*pos & 3) + return -EPROTO; + /* first address is BAR base plus file position offset */ + reg = xdev->bar[xcdev->bar] + *pos; + //w = read_register(reg); + w = ioread32(reg); + dbg_sg("char_ctrl_read(@%p, count=%ld, pos=%d) value = 0x%08x\n", reg, + (long)count, (int)*pos, w); + rv = copy_to_user(buf, &w, 4); + if (rv) + dbg_sg("Copy to userspace failed but continuing\n"); + + *pos += 4; + return 4; +} + +static ssize_t char_ctrl_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct xdma_cdev *xcdev = (struct xdma_cdev *)file->private_data; + struct xdma_dev *xdev; + void *reg; + u32 w; + int rv; + + rv = xcdev_check(__func__, xcdev, 0); + if (rv < 0) + return rv; + xdev = xcdev->xdev; + + /* only 32-bit aligned and 32-bit multiples */ + if (*pos & 3) + return -EPROTO; + + /* first address is BAR base plus file position offset */ + reg = xdev->bar[xcdev->bar] + *pos; + rv = copy_from_user(&w, buf, 4); + if (rv) { + pr_info("copy from user failed %d/4, but continuing.\n", rv); + } + + dbg_sg("char_ctrl_write(0x%08x @%p, count=%ld, pos=%d)\n", w, reg, + (long)count, (int)*pos); + //write_register(w, reg); + iowrite32(w, reg); + *pos += 4; + return 4; +} + +static long version_ioctl(struct xdma_cdev *xcdev, void __user *arg) +{ + struct xdma_ioc_info obj; + struct xdma_dev *xdev = xcdev->xdev; + int rv; + + rv = copy_from_user((void *)&obj, arg, sizeof(struct xdma_ioc_info)); + if (rv) { + pr_info("copy from user failed %d/%ld.\n", + rv, sizeof(struct xdma_ioc_info)); + return -EFAULT; + } + memset(&obj, 0, sizeof(obj)); + obj.vendor = xdev->pdev->vendor; + obj.device = xdev->pdev->device; + obj.subsystem_vendor = xdev->pdev->subsystem_vendor; + obj.subsystem_device = xdev->pdev->subsystem_device; + obj.feature_id = xdev->feature_id; + obj.driver_version = DRV_MOD_VERSION_NUMBER; + obj.domain = 0; + obj.bus = PCI_BUS_NUM(xdev->pdev->devfn); + obj.dev = PCI_SLOT(xdev->pdev->devfn); + obj.func = PCI_FUNC(xdev->pdev->devfn); + if (copy_to_user(arg, &obj, sizeof(struct xdma_ioc_info))) + return -EFAULT; + return 0; +} + +long char_ctrl_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct xdma_cdev *xcdev = (struct xdma_cdev *)filp->private_data; + struct xdma_dev *xdev; + struct xdma_ioc_base ioctl_obj; + long result = 0; + int rv; + + rv = xcdev_check(__func__, xcdev, 0); + if (rv < 0) + return rv; + xdev = xcdev->xdev; + + pr_info("cmd 0x%x, xdev 0x%p, pdev 0x%p.\n", cmd, xdev, xdev->pdev); + + if (_IOC_TYPE(cmd) != XDMA_IOC_MAGIC) { + pr_err("cmd %u, bad magic 0x%x/0x%x.\n", + cmd, _IOC_TYPE(cmd), XDMA_IOC_MAGIC); + return -ENOTTY; + } + + #if LINUX_VERSION_CODE < KERNEL_VERSION(5,3,0) + if (_IOC_DIR(cmd) & _IOC_READ) + result = !access_ok(VERIFY_WRITE, (void __user *)arg, + _IOC_SIZE(cmd)); + else if (_IOC_DIR(cmd) & _IOC_WRITE) + result = !access_ok(VERIFY_READ, (void __user *)arg, + _IOC_SIZE(cmd)); + #else + if (_IOC_DIR(cmd) & _IOC_READ) + result = !access_ok((void __user *)arg, + _IOC_SIZE(cmd)); + else if (_IOC_DIR(cmd) & _IOC_WRITE) + result = !access_ok((void __user *)arg, + _IOC_SIZE(cmd)); + #endif + + if (result) { + pr_err("bad access %ld.\n", result); + return -EFAULT; + } + + switch (cmd) { + case XDMA_IOCINFO: + if (copy_from_user((void *)&ioctl_obj, (void *) arg, + sizeof(struct xdma_ioc_base))) { + pr_err("copy_from_user failed.\n"); + return -EFAULT; + } + + if (ioctl_obj.magic != XDMA_XCL_MAGIC) { + pr_err("magic 0x%x != XDMA_XCL_MAGIC (0x%x).\n", + ioctl_obj.magic, XDMA_XCL_MAGIC); + return -ENOTTY; + } + + return version_ioctl(xcdev, (void __user *)arg); + case XDMA_IOCOFFLINE: + if (!xdev) { + pr_info("cmd %u, xdev NULL.\n", cmd); + return -EINVAL; + } + xdma_device_offline(xdev->pdev, xdev); + break; + case XDMA_IOCONLINE: + if (!xdev) { + pr_info("cmd %u, xdev NULL.\n", cmd); + return -EINVAL; + } + xdma_device_online(xdev->pdev, xdev); + break; + default: + pr_err("UNKNOWN ioctl cmd 0x%x.\n", cmd); + return -ENOTTY; + } + return 0; +} + +/* maps the PCIe BAR into user space for memory-like access using mmap() */ +int bridge_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct xdma_dev *xdev; + struct xdma_cdev *xcdev = (struct xdma_cdev *)file->private_data; + unsigned long off; + unsigned long phys; + unsigned long vsize; + unsigned long psize; + int rv; + + rv = xcdev_check(__func__, xcdev, 0); + if (rv < 0) + return rv; + xdev = xcdev->xdev; + + off = vma->vm_pgoff << PAGE_SHIFT; + /* BAR physical address */ + phys = pci_resource_start(xdev->pdev, xcdev->bar) + off; + vsize = vma->vm_end - vma->vm_start; + /* complete resource */ + psize = pci_resource_end(xdev->pdev, xcdev->bar) - + pci_resource_start(xdev->pdev, xcdev->bar) + 1 - off; + + dbg_sg("mmap(): xcdev = 0x%08lx\n", (unsigned long)xcdev); + dbg_sg("mmap(): cdev->bar = %d\n", xcdev->bar); + dbg_sg("mmap(): xdev = 0x%p\n", xdev); + dbg_sg("mmap(): pci_dev = 0x%08lx\n", (unsigned long)xdev->pdev); + + dbg_sg("off = 0x%lx\n", off); + dbg_sg("start = 0x%llx\n", + (unsigned long long)pci_resource_start(xdev->pdev, + xcdev->bar)); + dbg_sg("phys = 0x%lx\n", phys); + + if (vsize > psize) + return -EINVAL; + /* + * pages must not be cached as this would result in cache line sized + * accesses to the end point + */ + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + /* + * prevent touching the pages (byte access) for swap-in, + * and prevent the pages from being swapped out + */ + vma->vm_flags |= VMEM_FLAGS; + /* make MMIO accessible to user space */ + rv = io_remap_pfn_range(vma, vma->vm_start, phys >> PAGE_SHIFT, + vsize, vma->vm_page_prot); + dbg_sg("vma=0x%p, vma->vm_start=0x%lx, phys=0x%lx, size=%lu = %d\n", + vma, vma->vm_start, phys >> PAGE_SHIFT, vsize, rv); + + if (rv) + return -EAGAIN; + return 0; +} + +/* + * character device file operations for control bus (through control bridge) + */ +static const struct file_operations ctrl_fops = { + .owner = THIS_MODULE, + .open = char_open, + .release = char_close, + .read = char_ctrl_read, + .write = char_ctrl_write, + .mmap = bridge_mmap, + .unlocked_ioctl = char_ctrl_ioctl, +}; + +void cdev_ctrl_init(struct xdma_cdev *xcdev) +{ + cdev_init(&xcdev->cdev, &ctrl_fops); +} -- cgit v1.2.3